aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorvitalyisaev <vitalyisaev@ydb.tech>2023-11-30 13:26:22 +0300
committervitalyisaev <vitalyisaev@ydb.tech>2023-11-30 15:44:45 +0300
commit0a98fece5a9b54f16afeb3a94b3eb3105e9c3962 (patch)
tree291d72dbd7e9865399f668c84d11ed86fb190bbf /contrib
parentcb2c8d75065e5b3c47094067cb4aa407d4813298 (diff)
downloadydb-0a98fece5a9b54f16afeb3a94b3eb3105e9c3962.tar.gz
YQ Connector:Use docker-compose in integrational tests
Diffstat (limited to 'contrib')
-rw-r--r--contrib/deprecated/galloc/basictypes.h33
-rw-r--r--contrib/deprecated/galloc/commandlineflags.h111
-rw-r--r--contrib/deprecated/galloc/galloc.cpp55
-rw-r--r--contrib/deprecated/galloc/hack.cpp20
-rw-r--r--contrib/deprecated/galloc/hack.h21
-rw-r--r--contrib/deprecated/galloc/internal_logging.cc67
-rw-r--r--contrib/deprecated/galloc/internal_logging.h101
-rw-r--r--contrib/deprecated/galloc/internal_spinlock.h47
-rw-r--r--contrib/deprecated/galloc/malloc_extension.cc129
-rw-r--r--contrib/deprecated/galloc/malloc_extension.h154
-rw-r--r--contrib/deprecated/galloc/malloc_hook.h13
-rw-r--r--contrib/deprecated/galloc/pagemap.h250
-rw-r--r--contrib/deprecated/galloc/spinlock.h21
-rw-r--r--contrib/deprecated/galloc/stacktrace.h9
-rw-r--r--contrib/deprecated/galloc/system-alloc.cc296
-rw-r--r--contrib/deprecated/galloc/system-alloc.h46
-rw-r--r--contrib/deprecated/galloc/tcmalloc.cc2661
-rw-r--r--contrib/deprecated/galloc/ya.make22
-rw-r--r--contrib/java/antlr/antlr4/antlr.jarbin0 -> 3547867 bytes
-rw-r--r--contrib/libs/antlr4_cpp_runtime/CHANGES.txt582
-rw-r--r--contrib/libs/antlr4_cpp_runtime/CONTRIBUTING.md22
-rw-r--r--contrib/libs/antlr4_cpp_runtime/LICENSE.txt28
-rw-r--r--contrib/libs/antlr4_cpp_runtime/README-cpp.md72
-rw-r--r--contrib/libs/antlr4_cpp_runtime/README.md84
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorListener.cpp10
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorListener.h167
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorStrategy.cpp10
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorStrategy.h121
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ANTLRFileStream.cpp23
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ANTLRFileStream.h30
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.cpp180
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.h79
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/BailErrorStrategy.cpp61
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/BailErrorStrategy.h59
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/BaseErrorListener.cpp25
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/BaseErrorListener.h36
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.cpp414
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.h200
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/CharStream.cpp11
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/CharStream.h37
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/CommonToken.cpp193
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/CommonToken.h158
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/CommonTokenFactory.cpp39
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/CommonTokenFactory.h74
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/CommonTokenStream.cpp78
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/CommonTokenStream.h79
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ConsoleErrorListener.cpp15
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ConsoleErrorListener.h35
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/DefaultErrorStrategy.cpp336
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/DefaultErrorStrategy.h466
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/DiagnosticErrorListener.cpp84
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/DiagnosticErrorListener.h80
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Exceptions.cpp64
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Exceptions.h99
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/FailedPredicateException.cpp52
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/FailedPredicateException.h32
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/FlatHashMap.h57
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/FlatHashSet.h57
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/InputMismatchException.cpp18
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/InputMismatchException.h24
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/IntStream.cpp12
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/IntStream.h218
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/InterpreterRuleContext.cpp19
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/InterpreterRuleContext.h45
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Lexer.cpp294
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Lexer.h196
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/LexerInterpreter.cpp60
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/LexerInterpreter.h46
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/LexerNoViableAltException.cpp36
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/LexerNoViableAltException.h31
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ListTokenSource.cpp92
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ListTokenSource.h88
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/NoViableAltException.cpp46
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/NoViableAltException.h42
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Parser.cpp670
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Parser.h461
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ParserInterpreter.cpp294
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ParserInterpreter.h173
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ParserRuleContext.cpp138
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ParserRuleContext.h147
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ProxyErrorListener.cpp53
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/ProxyErrorListener.h38
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/RecognitionException.cpp65
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/RecognitionException.h98
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Recognizer.cpp157
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Recognizer.h160
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/RuleContext.cpp144
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/RuleContext.h141
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/RuleContextWithAltNum.cpp27
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/RuleContextWithAltNum.h32
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/RuntimeMetaData.cpp54
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/RuntimeMetaData.h155
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Token.cpp9
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Token.h92
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/TokenFactory.h30
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/TokenSource.cpp9
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/TokenSource.h85
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/TokenStream.cpp11
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/TokenStream.h137
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/TokenStreamRewriter.cpp425
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/TokenStreamRewriter.h295
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/UnbufferedCharStream.cpp208
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/UnbufferedCharStream.h117
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/UnbufferedTokenStream.cpp270
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/UnbufferedTokenStream.h115
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Version.h42
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Vocabulary.cpp64
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/Vocabulary.h177
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/WritableToken.cpp9
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/WritableToken.h23
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/antlr4-common.h101
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/antlr4-runtime.h168
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATN.cpp159
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATN.h133
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfig.cpp106
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfig.h157
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfigSet.cpp232
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfigSet.h157
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializationOptions.cpp39
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializationOptions.h48
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializer.cpp628
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializer.h32
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNSimulator.cpp33
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNSimulator.h71
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNState.cpp56
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNState.h139
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNStateType.cpp33
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNStateType.h36
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ATNType.h20
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ActionTransition.cpp29
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ActionTransition.h35
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/AmbiguityInfo.cpp16
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/AmbiguityInfo.h68
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ArrayPredictionContext.cpp109
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ArrayPredictionContext.h51
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/AtomTransition.cpp27
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/AtomTransition.h33
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/BasicBlockStartState.h24
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/BasicState.h23
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/BlockEndState.h26
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/BlockStartState.h30
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ContextSensitivityInfo.cpp14
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ContextSensitivityInfo.h47
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/DecisionEventInfo.cpp14
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/DecisionEventInfo.h70
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/DecisionInfo.cpp25
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/DecisionInfo.h227
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/DecisionState.cpp12
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/DecisionState.h34
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/EpsilonTransition.cpp31
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/EpsilonTransition.h42
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ErrorInfo.cpp15
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ErrorInfo.h43
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LL1Analyzer.cpp189
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LL1Analyzer.h76
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNConfig.cpp67
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNConfig.h44
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNSimulator.cpp617
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNSimulator.h199
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerAction.cpp15
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerAction.h100
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionExecutor.cpp111
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionExecutor.h128
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionType.h57
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerChannelAction.cpp43
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerChannelAction.h59
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerCustomAction.cpp45
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerCustomAction.h75
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerIndexedCustomAction.cpp57
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerIndexedCustomAction.h76
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerModeAction.cpp43
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerModeAction.h57
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerMoreAction.cpp36
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerMoreAction.h53
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerPopModeAction.cpp36
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerPopModeAction.h53
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerPushModeAction.cpp43
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerPushModeAction.h57
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerSkipAction.cpp36
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerSkipAction.h51
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerTypeAction.cpp43
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LexerTypeAction.h51
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LookaheadEventInfo.cpp16
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LookaheadEventInfo.h42
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/LoopEndState.h26
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/NotSetTransition.cpp22
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/NotSetTransition.h27
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/OrderedATNConfigSet.cpp16
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/OrderedATNConfigSet.h25
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ParseInfo.cpp102
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ParseInfo.h102
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulator.cpp1387
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulator.h911
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulatorOptions.h50
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PlusBlockStartState.h29
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PlusLoopbackState.h25
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PrecedencePredicateTransition.cpp23
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PrecedencePredicateTransition.h35
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredicateEvalInfo.cpp17
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredicateEvalInfo.h62
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredicateTransition.cpp24
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredicateTransition.h50
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContext.cpp579
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContext.h225
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextCache.cpp56
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextCache.h63
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCache.cpp167
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCache.h101
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCacheOptions.h71
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextType.h21
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionMode.cpp202
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/PredictionMode.h436
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ProfilingATNSimulator.cpp179
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/ProfilingATNSimulator.h60
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/RangeTransition.cpp26
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/RangeTransition.h31
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/RuleStartState.h26
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/RuleStopState.h27
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/RuleTransition.cpp33
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/RuleTransition.h42
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContext.cpp418
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContext.h237
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContextType.h23
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/SerializedATNView.h101
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/SetTransition.cpp28
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/SetTransition.h38
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/SingletonPredictionContext.cpp86
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/SingletonPredictionContext.h43
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/StarBlockStartState.h24
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopEntryState.h37
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopbackState.cpp19
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopbackState.h25
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/TokensStartState.h24
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/Transition.cpp36
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/Transition.h65
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/TransitionType.cpp27
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/TransitionType.h33
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/WildcardTransition.cpp21
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/atn/WildcardTransition.h27
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/dfa/DFA.cpp115
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/dfa/DFA.h96
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/dfa/DFASerializer.cpp60
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/dfa/DFASerializer.h32
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/dfa/DFAState.cpp59
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/dfa/DFAState.h154
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/dfa/LexerDFASerializer.cpp17
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/dfa/LexerDFASerializer.h22
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/internal/Synchronization.cpp100
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/internal/Synchronization.h154
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/InterpreterDataReader.cpp124
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/InterpreterDataReader.h33
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/Interval.cpp61
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/Interval.h84
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/IntervalSet.cpp501
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/IntervalSet.h188
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/MurmurHash.cpp120
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/MurmurHash.h102
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/Predicate.cpp4
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/misc/Predicate.h21
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Any.cpp8
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Any.h16
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Arrays.cpp43
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Arrays.h149
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/BitSet.h76
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/CPPUtils.cpp207
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/CPPUtils.h65
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Casts.h34
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Declarations.h161
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/StringUtils.cpp38
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/StringUtils.h16
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Unicode.h28
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Utf8.cpp242
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/support/Utf8.h54
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/AbstractParseTreeVisitor.h129
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNode.h24
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNodeImpl.cpp54
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNodeImpl.h43
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/IterativeParseTreeWalker.cpp66
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/IterativeParseTreeWalker.h53
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTree.cpp12
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTree.h111
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeListener.cpp9
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeListener.h39
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeProperty.h50
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeType.h22
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeVisitor.cpp9
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeVisitor.h57
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeWalker.cpp48
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeWalker.h55
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNode.h40
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNodeImpl.cpp54
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNodeImpl.h32
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/Trees.cpp241
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/Trees.h78
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/Chunk.cpp9
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/Chunk.h44
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreeMatch.cpp69
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreeMatch.h132
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePattern.cpp64
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePattern.h105
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePatternMatcher.cpp370
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePatternMatcher.h185
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/RuleTagToken.cpp77
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/RuleTagToken.h117
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TagChunk.cpp39
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TagChunk.h86
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TextChunk.cpp28
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TextChunk.h51
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TokenTagToken.cpp36
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TokenTagToken.h80
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPath.cpp154
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPath.h86
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathElement.cpp31
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathElement.h40
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexer.cpp182
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexer.h47
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexerErrorListener.cpp13
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexerErrorListener.h22
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleAnywhereElement.cpp20
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleAnywhereElement.h27
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleElement.cpp30
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleElement.h26
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenAnywhereElement.cpp20
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenAnywhereElement.h25
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenElement.cpp33
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenElement.h26
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardAnywhereElement.cpp23
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardAnywhereElement.h23
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardElement.cpp24
-rw-r--r--contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardElement.h23
-rw-r--r--contrib/libs/antlr4_cpp_runtime/ya.make164
-rw-r--r--contrib/libs/blake2/COPYING117
-rw-r--r--contrib/libs/blake2/README.md14
-rw-r--r--contrib/libs/blake2/include/blake2.h1
-rw-r--r--contrib/libs/blake2/src/blake2-config.h71
-rw-r--r--contrib/libs/blake2/src/blake2-dispatch.c577
-rw-r--r--contrib/libs/blake2/src/blake2-impl.h160
-rw-r--r--contrib/libs/blake2/src/blake2.h182
-rw-r--r--contrib/libs/blake2/src/blake2b-load-sse2.h68
-rw-r--r--contrib/libs/blake2/src/blake2b-load-sse41.h402
-rw-r--r--contrib/libs/blake2/src/blake2b-ref.c386
-rw-r--r--contrib/libs/blake2/src/blake2b-round.h160
-rw-r--r--contrib/libs/blake2/src/blake2b.c443
-rw-r--r--contrib/libs/blake2/src/blake2bp.c274
-rw-r--r--contrib/libs/blake2/src/blake2s-load-sse2.h59
-rw-r--r--contrib/libs/blake2/src/blake2s-load-sse41.h229
-rw-r--r--contrib/libs/blake2/src/blake2s-load-xop.h189
-rw-r--r--contrib/libs/blake2/src/blake2s-ref.c375
-rw-r--r--contrib/libs/blake2/src/blake2s-round.h91
-rw-r--r--contrib/libs/blake2/src/blake2s.c422
-rw-r--r--contrib/libs/blake2/src/blake2sp.c274
-rw-r--r--contrib/libs/blake2/src/config.h157
-rw-r--r--contrib/libs/blake2/ya.make49
-rw-r--r--contrib/libs/libarchive/CONTRIBUTING.md98
-rw-r--r--contrib/libs/libarchive/COPYING65
-rw-r--r--contrib/libs/libarchive/INSTALL35
-rw-r--r--contrib/libs/libarchive/NEWS757
-rw-r--r--contrib/libs/libarchive/README.md244
-rw-r--r--contrib/libs/libarchive/SECURITY.md19
-rw-r--r--contrib/libs/libarchive/config-linux.h1402
-rw-r--r--contrib/libs/libarchive/config-osx.h42
-rw-r--r--contrib/libs/libarchive/config-win.h121
-rw-r--r--contrib/libs/libarchive/config.h9
-rw-r--r--contrib/libs/libarchive/libarchive/archive.h1212
-rw-r--r--contrib/libs/libarchive/libarchive/archive_acl.c2097
-rw-r--r--contrib/libs/libarchive/libarchive/archive_acl_private.h83
-rw-r--r--contrib/libs/libarchive/libarchive/archive_check_magic.c175
-rw-r--r--contrib/libs/libarchive/libarchive/archive_cmdline.c227
-rw-r--r--contrib/libs/libarchive/libarchive/archive_cmdline_private.h47
-rw-r--r--contrib/libs/libarchive/libarchive/archive_cryptor.c534
-rw-r--r--contrib/libs/libarchive/libarchive/archive_cryptor_private.h188
-rw-r--r--contrib/libs/libarchive/libarchive/archive_digest.c1565
-rw-r--r--contrib/libs/libarchive/libarchive/archive_digest_private.h426
-rw-r--r--contrib/libs/libarchive/libarchive/archive_endian.h195
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry.c2149
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry.h723
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_copy_bhfi.c75
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_copy_stat.c83
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_link_resolver.c447
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_locale.h92
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_private.h200
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_sparse.c156
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_stat.c118
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_strmode.c87
-rw-r--r--contrib/libs/libarchive/libarchive/archive_entry_xattr.c156
-rw-r--r--contrib/libs/libarchive/libarchive/archive_getdate.c1104
-rw-r--r--contrib/libs/libarchive/libarchive/archive_getdate.h39
-rw-r--r--contrib/libs/libarchive/libarchive/archive_hmac.c339
-rw-r--r--contrib/libs/libarchive/libarchive/archive_hmac_private.h119
-rw-r--r--contrib/libs/libarchive/libarchive/archive_match.c1875
-rw-r--r--contrib/libs/libarchive/libarchive/archive_openssl_evp_private.h54
-rw-r--r--contrib/libs/libarchive/libarchive/archive_openssl_hmac_private.h54
-rw-r--r--contrib/libs/libarchive/libarchive/archive_options.c218
-rw-r--r--contrib/libs/libarchive/libarchive/archive_options_private.h51
-rw-r--r--contrib/libs/libarchive/libarchive/archive_pack_dev.c337
-rw-r--r--contrib/libs/libarchive/libarchive/archive_pack_dev.h49
-rw-r--r--contrib/libs/libarchive/libarchive/archive_pathmatch.c463
-rw-r--r--contrib/libs/libarchive/libarchive/archive_pathmatch.h52
-rw-r--r--contrib/libs/libarchive/libarchive/archive_platform.h233
-rw-r--r--contrib/libs/libarchive/libarchive/archive_platform_acl.h55
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd7.c1168
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd7_private.h119
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd8.c1287
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd8_private.h148
-rw-r--r--contrib/libs/libarchive/libarchive/archive_ppmd_private.h151
-rw-r--r--contrib/libs/libarchive/libarchive/archive_private.h180
-rw-r--r--contrib/libs/libarchive/libarchive/archive_random.c301
-rw-r--r--contrib/libs/libarchive/libarchive/archive_random_private.h36
-rw-r--r--contrib/libs/libarchive/libarchive/archive_rb.c709
-rw-r--r--contrib/libs/libarchive/libarchive/archive_rb.h113
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read.c1756
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_add_passphrase.c190
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_append_filter.c204
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_data_into_fd.c144
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_entry_from_file.c1086
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_posix.c2760
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_private.h98
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_set_standard_lookup.c313
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_disk_windows.c2547
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_extract.c60
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_extract2.c155
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_open_fd.c211
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_open_file.c180
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_open_filename.c586
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_open_memory.c186
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_private.h267
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_set_format.c117
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_set_options.c133
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_all.c85
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_by_code.c83
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_bzip2.c365
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_compress.c452
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_grzip.c112
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_gzip.c536
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_lrzip.c122
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_lz4.c742
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_lzop.c499
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_none.c52
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_program.c496
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_rpm.c287
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_uu.c731
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_xz.c793
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_filter_zstd.c297
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_7zip.c4152
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_all.c89
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_ar.c638
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_by_code.c92
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_cab.c3228
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_cpio.c1104
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_empty.c96
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_iso9660.c3279
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_lha.c2919
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_mtree.c2156
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_rar.c3788
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_rar5.c4251
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_raw.c192
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_tar.c2946
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_warc.c848
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_xar.c3332
-rw-r--r--contrib/libs/libarchive/libarchive/archive_read_support_format_zip.c4270
-rw-r--r--contrib/libs/libarchive/libarchive/archive_string.c4244
-rw-r--r--contrib/libs/libarchive/libarchive/archive_string.h243
-rw-r--r--contrib/libs/libarchive/libarchive/archive_string_composition.h2292
-rw-r--r--contrib/libs/libarchive/libarchive/archive_string_sprintf.c192
-rw-r--r--contrib/libs/libarchive/libarchive/archive_util.c704
-rw-r--r--contrib/libs/libarchive/libarchive/archive_version_details.c151
-rw-r--r--contrib/libs/libarchive/libarchive/archive_virtual.c163
-rw-r--r--contrib/libs/libarchive/libarchive/archive_windows.c938
-rw-r--r--contrib/libs/libarchive/libarchive/archive_windows.h315
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write.c859
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter.c72
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_b64encode.c304
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_by_name.c77
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_bzip2.c401
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_compress.c447
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_grzip.c135
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_gzip.c442
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_lrzip.c197
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_lz4.c700
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_lzop.c478
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_none.c43
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_program.c391
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_uuencode.c295
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_xz.c545
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_add_filter_zstd.c503
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_disk_posix.c4759
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_disk_private.h45
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_disk_set_standard_lookup.c263
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_disk_windows.c2911
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_open_fd.c146
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_open_file.c111
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_open_filename.c270
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_open_memory.c115
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_private.h166
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format.c125
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_7zip.c2347
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_ar.c570
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_by_name.c94
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_cpio.c11
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_binary.c610
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_newc.c457
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_odc.c500
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_filter_by_ext.c142
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_gnutar.c755
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_iso9660.c8161
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_mtree.c2217
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_pax.c2078
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_private.h42
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_raw.c125
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_shar.c641
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_ustar.c758
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_v7tar.c638
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_warc.c444
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_xar.c3255
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_format_zip.c1693
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_options.c130
-rw-r--r--contrib/libs/libarchive/libarchive/archive_write_set_passphrase.c95
-rw-r--r--contrib/libs/libarchive/libarchive/archive_xxhash.h48
-rw-r--r--contrib/libs/libarchive/libarchive/filter_fork.h46
-rw-r--r--contrib/libs/libarchive/libarchive/filter_fork_posix.c240
-rw-r--r--contrib/libs/libarchive/libarchive/filter_fork_windows.c245
-rw-r--r--contrib/libs/libarchive/libarchive/xxhash.c529
-rw-r--r--contrib/libs/libarchive/ya.make183
-rw-r--r--contrib/libs/libmagic/AUTHORS1
-rw-r--r--contrib/libs/libmagic/COPYING29
-rw-r--r--contrib/libs/libmagic/ChangeLog2129
-rw-r--r--contrib/libs/libmagic/INSTALL365
-rw-r--r--contrib/libs/libmagic/NEWS1
-rw-r--r--contrib/libs/libmagic/README.DEVELOPER49
-rw-r--r--contrib/libs/libmagic/README.md156
-rw-r--r--contrib/libs/libmagic/config-linux.h519
-rw-r--r--contrib/libs/libmagic/config-osx.h9
-rw-r--r--contrib/libs/libmagic/config.h7
-rw-r--r--contrib/libs/libmagic/file/0/ya.make12
-rw-r--r--contrib/libs/libmagic/include/magic.h1
-rw-r--r--contrib/libs/libmagic/magic/Magdir/acorn102
-rw-r--r--contrib/libs/libmagic/magic/Magdir/adi13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/adventure122
-rw-r--r--contrib/libs/libmagic/magic/Magdir/aes29
-rw-r--r--contrib/libs/libmagic/magic/Magdir/algol6835
-rw-r--r--contrib/libs/libmagic/magic/Magdir/allegro9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/alliant18
-rw-r--r--contrib/libs/libmagic/magic/Magdir/amanda12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/amigaos218
-rw-r--r--contrib/libs/libmagic/magic/Magdir/android259
-rw-r--r--contrib/libs/libmagic/magic/Magdir/animation1206
-rw-r--r--contrib/libs/libmagic/magic/Magdir/aout46
-rwxr-xr-xcontrib/libs/libmagic/magic/Magdir/apache28
-rw-r--r--contrib/libs/libmagic/magic/Magdir/apl7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/apple773
-rw-r--r--contrib/libs/libmagic/magic/Magdir/application7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/applix13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/apt52
-rw-r--r--contrib/libs/libmagic/magic/Magdir/archive2607
-rw-r--r--contrib/libs/libmagic/magic/Magdir/aria38
-rw-r--r--contrib/libs/libmagic/magic/Magdir/arm50
-rw-r--r--contrib/libs/libmagic/magic/Magdir/asf132
-rw-r--r--contrib/libs/libmagic/magic/Magdir/assembler18
-rw-r--r--contrib/libs/libmagic/magic/Magdir/asterix18
-rw-r--r--contrib/libs/libmagic/magic/Magdir/att3b41
-rw-r--r--contrib/libs/libmagic/magic/Magdir/audio1291
-rw-r--r--contrib/libs/libmagic/magic/Magdir/avm33
-rw-r--r--contrib/libs/libmagic/magic/Magdir/basis18
-rw-r--r--contrib/libs/libmagic/magic/Magdir/beetle7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ber65
-rw-r--r--contrib/libs/libmagic/magic/Magdir/bflt14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/bhl10
-rw-r--r--contrib/libs/libmagic/magic/Magdir/bioinformatics178
-rw-r--r--contrib/libs/libmagic/magic/Magdir/biosig154
-rw-r--r--contrib/libs/libmagic/magic/Magdir/blackberry8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/blcr25
-rw-r--r--contrib/libs/libmagic/magic/Magdir/blender50
-rw-r--r--contrib/libs/libmagic/magic/Magdir/blit24
-rw-r--r--contrib/libs/libmagic/magic/Magdir/bm10
-rw-r--r--contrib/libs/libmagic/magic/Magdir/bout11
-rw-r--r--contrib/libs/libmagic/magic/Magdir/bsdi33
-rw-r--r--contrib/libs/libmagic/magic/Magdir/bsi10
-rw-r--r--contrib/libs/libmagic/magic/Magdir/btsnoop13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/burp7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/bytecode41
-rw-r--r--contrib/libs/libmagic/magic/Magdir/c-lang110
-rw-r--r--contrib/libs/libmagic/magic/Magdir/c64549
-rw-r--r--contrib/libs/libmagic/magic/Magdir/cad437
-rw-r--r--contrib/libs/libmagic/magic/Magdir/cafebabe107
-rw-r--r--contrib/libs/libmagic/magic/Magdir/cbor21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ccf14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/cddb12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/chord15
-rw-r--r--contrib/libs/libmagic/magic/Magdir/cisco12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/citrus12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/clarion27
-rw-r--r--contrib/libs/libmagic/magic/Magdir/claris48
-rw-r--r--contrib/libs/libmagic/magic/Magdir/clipper65
-rw-r--r--contrib/libs/libmagic/magic/Magdir/clojure30
-rw-r--r--contrib/libs/libmagic/magic/Magdir/coff98
-rw-r--r--contrib/libs/libmagic/magic/Magdir/commands201
-rw-r--r--contrib/libs/libmagic/magic/Magdir/communications22
-rw-r--r--contrib/libs/libmagic/magic/Magdir/compress461
-rw-r--r--contrib/libs/libmagic/magic/Magdir/console1226
-rw-r--r--contrib/libs/libmagic/magic/Magdir/convex69
-rw-r--r--contrib/libs/libmagic/magic/Magdir/coverage91
-rw-r--r--contrib/libs/libmagic/magic/Magdir/cracklib14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/crypto49
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ctags6
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ctf23
-rw-r--r--contrib/libs/libmagic/magic/Magdir/cubemap8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/cups56
-rw-r--r--contrib/libs/libmagic/magic/Magdir/dact11
-rw-r--r--contrib/libs/libmagic/magic/Magdir/database886
-rw-r--r--contrib/libs/libmagic/magic/Magdir/dataone47
-rw-r--r--contrib/libs/libmagic/magic/Magdir/dbpf15
-rw-r--r--contrib/libs/libmagic/magic/Magdir/der146
-rw-r--r--contrib/libs/libmagic/magic/Magdir/diamond12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/dif33
-rw-r--r--contrib/libs/libmagic/magic/Magdir/diff41
-rw-r--r--contrib/libs/libmagic/magic/Magdir/digital59
-rw-r--r--contrib/libs/libmagic/magic/Magdir/dolby69
-rw-r--r--contrib/libs/libmagic/magic/Magdir/dump96
-rw-r--r--contrib/libs/libmagic/magic/Magdir/dwarfs45
-rw-r--r--contrib/libs/libmagic/magic/Magdir/dyadic61
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ebml8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/edid11
-rw-r--r--contrib/libs/libmagic/magic/Magdir/editors43
-rw-r--r--contrib/libs/libmagic/magic/Magdir/efi15
-rw-r--r--contrib/libs/libmagic/magic/Magdir/elf379
-rw-r--r--contrib/libs/libmagic/magic/Magdir/encore22
-rw-r--r--contrib/libs/libmagic/magic/Magdir/epoc62
-rw-r--r--contrib/libs/libmagic/magic/Magdir/erlang21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/espressif57
-rw-r--r--contrib/libs/libmagic/magic/Magdir/esri28
-rw-r--r--contrib/libs/libmagic/magic/Magdir/etf33
-rw-r--r--contrib/libs/libmagic/magic/Magdir/fcs9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/filesystems2694
-rw-r--r--contrib/libs/libmagic/magic/Magdir/finger16
-rw-r--r--contrib/libs/libmagic/magic/Magdir/firmware133
-rw-r--r--contrib/libs/libmagic/magic/Magdir/flash62
-rw-r--r--contrib/libs/libmagic/magic/Magdir/flif36
-rw-r--r--contrib/libs/libmagic/magic/Magdir/fonts449
-rw-r--r--contrib/libs/libmagic/magic/Magdir/forth82
-rw-r--r--contrib/libs/libmagic/magic/Magdir/fortran9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/frame62
-rw-r--r--contrib/libs/libmagic/magic/Magdir/freebsd164
-rw-r--r--contrib/libs/libmagic/magic/Magdir/fsav128
-rw-r--r--contrib/libs/libmagic/magic/Magdir/fusecompress12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/games696
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gcc17
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gconv10
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gentoo85
-rw-r--r--contrib/libs/libmagic/magic/Magdir/geo166
-rw-r--r--contrib/libs/libmagic/magic/Magdir/geos20
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gimp77
-rw-r--r--contrib/libs/libmagic/magic/Magdir/git13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/glibc21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gnome59
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gnu173
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gnumeric8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gpt240
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gpu28
-rw-r--r--contrib/libs/libmagic/magic/Magdir/grace21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/graphviz12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/gringotts48
-rw-r--r--contrib/libs/libmagic/magic/Magdir/hardware12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/hitachi-sh30
-rw-r--r--contrib/libs/libmagic/magic/Magdir/hp433
-rw-r--r--contrib/libs/libmagic/magic/Magdir/human68k26
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ibm37052
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ibm600035
-rw-r--r--contrib/libs/libmagic/magic/Magdir/icc214
-rw-r--r--contrib/libs/libmagic/magic/Magdir/iff80
-rw-r--r--contrib/libs/libmagic/magic/Magdir/images4219
-rw-r--r--contrib/libs/libmagic/magic/Magdir/inform9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/intel310
-rw-r--r--contrib/libs/libmagic/magic/Magdir/interleaf9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/island10
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ispell63
-rw-r--r--contrib/libs/libmagic/magic/Magdir/isz15
-rw-r--r--contrib/libs/libmagic/magic/Magdir/java52
-rw-r--r--contrib/libs/libmagic/magic/Magdir/javascript171
-rw-r--r--contrib/libs/libmagic/magic/Magdir/jpeg252
-rw-r--r--contrib/libs/libmagic/magic/Magdir/karma9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/kde11
-rw-r--r--contrib/libs/libmagic/magic/Magdir/keepass20
-rw-r--r--contrib/libs/libmagic/magic/Magdir/kerberos45
-rw-r--r--contrib/libs/libmagic/magic/Magdir/kicad85
-rw-r--r--contrib/libs/libmagic/magic/Magdir/kml34
-rw-r--r--contrib/libs/libmagic/magic/Magdir/lammps64
-rw-r--r--contrib/libs/libmagic/magic/Magdir/lecter6
-rw-r--r--contrib/libs/libmagic/magic/Magdir/lex12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/lif50
-rw-r--r--contrib/libs/libmagic/magic/Magdir/linux627
-rw-r--r--contrib/libs/libmagic/magic/Magdir/lisp78
-rw-r--r--contrib/libs/libmagic/magic/Magdir/llvm22
-rw-r--r--contrib/libs/libmagic/magic/Magdir/locoscript12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/lua31
-rw-r--r--contrib/libs/libmagic/magic/Magdir/luks126
-rw-r--r--contrib/libs/libmagic/magic/Magdir/m411
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mach303
-rw-r--r--contrib/libs/libmagic/magic/Magdir/macintosh505
-rw-r--r--contrib/libs/libmagic/magic/Magdir/macos7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/magic71
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mail.news132
-rw-r--r--contrib/libs/libmagic/magic/Magdir/make21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/map413
-rw-r--r--contrib/libs/libmagic/magic/Magdir/maple109
-rw-r--r--contrib/libs/libmagic/magic/Magdir/marc2130
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mathcad8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mathematica192
-rw-r--r--contrib/libs/libmagic/magic/Magdir/matroska17
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mcrypt52
-rw-r--r--contrib/libs/libmagic/magic/Magdir/measure44
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mercurial13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/metastore8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/meteorological53
-rw-r--r--contrib/libs/libmagic/magic/Magdir/microfocus21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mime9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mips120
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mirage8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/misctools140
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mkid11
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mlssa8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mmdf6
-rw-r--r--contrib/libs/libmagic/magic/Magdir/modem92
-rw-r--r--contrib/libs/libmagic/magic/Magdir/modulefile9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/motorola71
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mozilla37
-rw-r--r--contrib/libs/libmagic/magic/Magdir/msdos2304
-rw-r--r--contrib/libs/libmagic/magic/Magdir/msooxml68
-rw-r--r--contrib/libs/libmagic/magic/Magdir/msvc222
-rw-r--r--contrib/libs/libmagic/magic/Magdir/msx309
-rw-r--r--contrib/libs/libmagic/magic/Magdir/mup24
-rw-r--r--contrib/libs/libmagic/magic/Magdir/music17
-rw-r--r--contrib/libs/libmagic/magic/Magdir/nasa7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/natinst24
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ncr49
-rw-r--r--contrib/libs/libmagic/magic/Magdir/netbsd251
-rw-r--r--contrib/libs/libmagic/magic/Magdir/netscape26
-rw-r--r--contrib/libs/libmagic/magic/Magdir/netware11
-rw-r--r--contrib/libs/libmagic/magic/Magdir/news13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/nifty202
-rw-r--r--contrib/libs/libmagic/magic/Magdir/nim-lang29
-rw-r--r--contrib/libs/libmagic/magic/Magdir/nitpicker14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/numpy9
-rw-r--r--contrib/libs/libmagic/magic/Magdir/oasis12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ocaml14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/octave6
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ole2compounddocs760
-rw-r--r--contrib/libs/libmagic/magic/Magdir/olf98
-rw-r--r--contrib/libs/libmagic/magic/Magdir/openfst17
-rw-r--r--contrib/libs/libmagic/magic/Magdir/opentimestamps16
-rw-r--r--contrib/libs/libmagic/magic/Magdir/oric16
-rw-r--r--contrib/libs/libmagic/magic/Magdir/os2186
-rw-r--r--contrib/libs/libmagic/magic/Magdir/os40039
-rw-r--r--contrib/libs/libmagic/magic/Magdir/os980
-rw-r--r--contrib/libs/libmagic/magic/Magdir/osf110
-rw-r--r--contrib/libs/libmagic/magic/Magdir/palm156
-rw-r--r--contrib/libs/libmagic/magic/Magdir/parix13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/parrot22
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pascal39
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pbf11
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pbm8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pc8824
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pc9877
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pci_ids116
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pcjr8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pdf51
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pdp42
-rw-r--r--contrib/libs/libmagic/magic/Magdir/perl100
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pgf52
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pgp581
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pgp-binary-keys388
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pkgadd7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/plan925
-rw-r--r--contrib/libs/libmagic/magic/Magdir/playdate57
-rw-r--r--contrib/libs/libmagic/magic/Magdir/plus518
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pmem46
-rw-r--r--contrib/libs/libmagic/magic/Magdir/polyml23
-rw-r--r--contrib/libs/libmagic/magic/Magdir/printer278
-rw-r--r--contrib/libs/libmagic/magic/Magdir/project10
-rw-r--r--contrib/libs/libmagic/magic/Magdir/psdbms14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/psl14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pulsar13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/puzzle17
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pwsafe14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/pyramid12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/python305
-rw-r--r--contrib/libs/libmagic/magic/Magdir/qt30
-rw-r--r--contrib/libs/libmagic/magic/Magdir/revision66
-rw-r--r--contrib/libs/libmagic/magic/Magdir/riff841
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ringdove45
-rw-r--r--contrib/libs/libmagic/magic/Magdir/rpi52
-rw-r--r--contrib/libs/libmagic/magic/Magdir/rpm45
-rw-r--r--contrib/libs/libmagic/magic/Magdir/rpmsg7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/rst13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/rtf94
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ruby55
-rw-r--r--contrib/libs/libmagic/magic/Magdir/rust21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sc7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sccs24
-rw-r--r--contrib/libs/libmagic/magic/Magdir/scientific144
-rw-r--r--contrib/libs/libmagic/magic/Magdir/securitycerts6
-rw-r--r--contrib/libs/libmagic/magic/Magdir/selinux24
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sendmail37
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sequent42
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sereal35
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sgi144
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sgml163
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sharc23
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sinclair40
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sisu18
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sketch6
-rw-r--r--contrib/libs/libmagic/magic/Magdir/smalltalk25
-rw-r--r--contrib/libs/libmagic/magic/Magdir/smile34
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sniffer482
-rw-r--r--contrib/libs/libmagic/magic/Magdir/softquad40
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sosi40
-rw-r--r--contrib/libs/libmagic/magic/Magdir/spec21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/spectrum184
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sql288
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ssh42
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ssl20
-rw-r--r--contrib/libs/libmagic/magic/Magdir/statistics45
-rw-r--r--contrib/libs/libmagic/magic/Magdir/subtitle38
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sun141
-rw-r--r--contrib/libs/libmagic/magic/Magdir/svf5
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sylk36
-rw-r--r--contrib/libs/libmagic/magic/Magdir/symbos42
-rw-r--r--contrib/libs/libmagic/magic/Magdir/sysex429
-rw-r--r--contrib/libs/libmagic/magic/Magdir/tcl29
-rw-r--r--contrib/libs/libmagic/magic/Magdir/teapot6
-rw-r--r--contrib/libs/libmagic/magic/Magdir/terminfo63
-rw-r--r--contrib/libs/libmagic/magic/Magdir/tex141
-rw-r--r--contrib/libs/libmagic/magic/Magdir/tgif7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/ti-8x239
-rw-r--r--contrib/libs/libmagic/magic/Magdir/timezone42
-rw-r--r--contrib/libs/libmagic/magic/Magdir/tplink95
-rw-r--r--contrib/libs/libmagic/magic/Magdir/troff44
-rw-r--r--contrib/libs/libmagic/magic/Magdir/tuxedo8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/typeset8
-rw-r--r--contrib/libs/libmagic/magic/Magdir/uf272
-rw-r--r--contrib/libs/libmagic/magic/Magdir/unicode15
-rw-r--r--contrib/libs/libmagic/magic/Magdir/unisig12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/unknown34
-rw-r--r--contrib/libs/libmagic/magic/Magdir/usd21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/uterus16
-rw-r--r--contrib/libs/libmagic/magic/Magdir/uuencode28
-rw-r--r--contrib/libs/libmagic/magic/Magdir/vacuum-cleaner54
-rw-r--r--contrib/libs/libmagic/magic/Magdir/varied.out46
-rw-r--r--contrib/libs/libmagic/magic/Magdir/varied.script21
-rw-r--r--contrib/libs/libmagic/magic/Magdir/vax32
-rw-r--r--contrib/libs/libmagic/magic/Magdir/vicar17
-rw-r--r--contrib/libs/libmagic/magic/Magdir/virtual307
-rw-r--r--contrib/libs/libmagic/magic/Magdir/virtutech12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/visx32
-rw-r--r--contrib/libs/libmagic/magic/Magdir/vms30
-rw-r--r--contrib/libs/libmagic/magic/Magdir/vmware6
-rw-r--r--contrib/libs/libmagic/magic/Magdir/vorbis155
-rw-r--r--contrib/libs/libmagic/magic/Magdir/vxl14
-rw-r--r--contrib/libs/libmagic/magic/Magdir/warc16
-rw-r--r--contrib/libs/libmagic/magic/Magdir/weak16
-rw-r--r--contrib/libs/libmagic/magic/Magdir/web18
-rw-r--r--contrib/libs/libmagic/magic/Magdir/webassembly17
-rw-r--r--contrib/libs/libmagic/magic/Magdir/windows1822
-rw-r--r--contrib/libs/libmagic/magic/Magdir/wireless7
-rw-r--r--contrib/libs/libmagic/magic/Magdir/wordprocessors630
-rw-r--r--contrib/libs/libmagic/magic/Magdir/wsdl23
-rw-r--r--contrib/libs/libmagic/magic/Magdir/x6800025
-rw-r--r--contrib/libs/libmagic/magic/Magdir/xdelta13
-rw-r--r--contrib/libs/libmagic/magic/Magdir/xenix106
-rw-r--r--contrib/libs/libmagic/magic/Magdir/xilinx58
-rw-r--r--contrib/libs/libmagic/magic/Magdir/xo6537
-rw-r--r--contrib/libs/libmagic/magic/Magdir/xwindows43
-rw-r--r--contrib/libs/libmagic/magic/Magdir/yara17
-rw-r--r--contrib/libs/libmagic/magic/Magdir/zfs96
-rw-r--r--contrib/libs/libmagic/magic/Magdir/zilog12
-rw-r--r--contrib/libs/libmagic/magic/Magdir/zip126
-rw-r--r--contrib/libs/libmagic/magic/Magdir/zyxel17
-rw-r--r--contrib/libs/libmagic/magic/ya.make44
-rw-r--r--contrib/libs/libmagic/src/apprentice.c3743
-rw-r--r--contrib/libs/libmagic/src/apptype.c169
-rw-r--r--contrib/libs/libmagic/src/ascmagic.c389
-rw-r--r--contrib/libs/libmagic/src/buffer.c95
-rw-r--r--contrib/libs/libmagic/src/cdf.c1676
-rw-r--r--contrib/libs/libmagic/src/cdf.h353
-rw-r--r--contrib/libs/libmagic/src/cdf_time.c203
-rw-r--r--contrib/libs/libmagic/src/compress.c1227
-rw-r--r--contrib/libs/libmagic/src/der.c458
-rw-r--r--contrib/libs/libmagic/src/der.h28
-rw-r--r--contrib/libs/libmagic/src/elfclass.h82
-rw-r--r--contrib/libs/libmagic/src/encoding.c658
-rw-r--r--contrib/libs/libmagic/src/file.c859
-rw-r--r--contrib/libs/libmagic/src/file.h729
-rw-r--r--contrib/libs/libmagic/src/file/ya.make34
-rw-r--r--contrib/libs/libmagic/src/file_opts.h89
-rw-r--r--contrib/libs/libmagic/src/fmtcheck.c254
-rw-r--r--contrib/libs/libmagic/src/fsmagic.c435
-rw-r--r--contrib/libs/libmagic/src/funcs.c932
-rw-r--r--contrib/libs/libmagic/src/is_csv.c198
-rw-r--r--contrib/libs/libmagic/src/is_json.c500
-rw-r--r--contrib/libs/libmagic/src/is_simh.c209
-rw-r--r--contrib/libs/libmagic/src/is_tar.c179
-rw-r--r--contrib/libs/libmagic/src/magic.c686
-rw-r--r--contrib/libs/libmagic/src/magic.h166
-rw-r--r--contrib/libs/libmagic/src/print.c368
-rw-r--r--contrib/libs/libmagic/src/readcdf.c682
-rw-r--r--contrib/libs/libmagic/src/readelf.c1899
-rw-r--r--contrib/libs/libmagic/src/readelf.h545
-rw-r--r--contrib/libs/libmagic/src/res.cpp7
-rw-r--r--contrib/libs/libmagic/src/seccomp.c290
-rw-r--r--contrib/libs/libmagic/src/softmagic.c2522
-rw-r--r--contrib/libs/libmagic/src/tar.h73
-rw-r--r--contrib/libs/libmagic/src/ya.make60
-rw-r--r--contrib/libs/libmagic/ya.make28
-rw-r--r--contrib/libs/pcre2/AUTHORS36
-rw-r--r--contrib/libs/pcre2/COPYING5
-rw-r--r--contrib/libs/pcre2/ChangeLog2827
-rw-r--r--contrib/libs/pcre2/INSTALL368
-rw-r--r--contrib/libs/pcre2/NEWS436
-rw-r--r--contrib/libs/pcre2/README924
-rw-r--r--contrib/libs/pcre2/README.md56
-rw-r--r--contrib/libs/pcre2/pcre2.h993
-rw-r--r--contrib/libs/pcre2/src/pcre2_auto_possess.c1365
-rw-r--r--contrib/libs/pcre2/src/pcre2_chartables.c202
-rw-r--r--contrib/libs/pcre2/src/pcre2_compile.c10631
-rw-r--r--contrib/libs/pcre2/src/pcre2_config.c252
-rw-r--r--contrib/libs/pcre2/src/pcre2_config.h446
-rw-r--r--contrib/libs/pcre2/src/pcre2_context.c494
-rw-r--r--contrib/libs/pcre2/src/pcre2_convert.c1181
-rw-r--r--contrib/libs/pcre2/src/pcre2_dfa_match.c4066
-rw-r--r--contrib/libs/pcre2/src/pcre2_error.c341
-rw-r--r--contrib/libs/pcre2/src/pcre2_extuni.c148
-rw-r--r--contrib/libs/pcre2/src/pcre2_find_bracket.c219
-rw-r--r--contrib/libs/pcre2/src/pcre2_internal.h2047
-rw-r--r--contrib/libs/pcre2/src/pcre2_intmodedep.h934
-rw-r--r--contrib/libs/pcre2/src/pcre2_jit_compile.c14507
-rw-r--r--contrib/libs/pcre2/src/pcre2_jit_match.c186
-rw-r--r--contrib/libs/pcre2/src/pcre2_jit_misc.c234
-rw-r--r--contrib/libs/pcre2/src/pcre2_jit_simd_inc.h1858
-rw-r--r--contrib/libs/pcre2/src/pcre2_maketables.c163
-rw-r--r--contrib/libs/pcre2/src/pcre2_match.c7544
-rw-r--r--contrib/libs/pcre2/src/pcre2_match_data.c173
-rw-r--r--contrib/libs/pcre2/src/pcre2_newline.c243
-rw-r--r--contrib/libs/pcre2/src/pcre2_ord2utf.c120
-rw-r--r--contrib/libs/pcre2/src/pcre2_pattern_info.c432
-rw-r--r--contrib/libs/pcre2/src/pcre2_script_run.c344
-rw-r--r--contrib/libs/pcre2/src/pcre2_serialize.c286
-rw-r--r--contrib/libs/pcre2/src/pcre2_string_utils.c237
-rw-r--r--contrib/libs/pcre2/src/pcre2_study.c1825
-rw-r--r--contrib/libs/pcre2/src/pcre2_substitute.c1009
-rw-r--r--contrib/libs/pcre2/src/pcre2_substring.c547
-rw-r--r--contrib/libs/pcre2/src/pcre2_tables.c234
-rw-r--r--contrib/libs/pcre2/src/pcre2_ucd.c5396
-rw-r--r--contrib/libs/pcre2/src/pcre2_ucp.h394
-rw-r--r--contrib/libs/pcre2/src/pcre2_ucptables.c1524
-rw-r--r--contrib/libs/pcre2/src/pcre2_valid_utf.c398
-rw-r--r--contrib/libs/pcre2/src/pcre2_xclass.c289
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitConfig.h162
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitConfigInternal.h851
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitExecAllocator.c411
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitLir.c3136
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitLir.h1823
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeARM_32.c3700
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeARM_64.c2417
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeARM_T2_32.c3150
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeMIPS_32.c314
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeMIPS_64.c319
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeMIPS_common.c3720
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativePPC_32.c340
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativePPC_64.c579
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativePPC_common.c2851
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeRISCV_32.c73
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeRISCV_64.c183
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeRISCV_common.c2762
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeS390X.c3747
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeX86_32.c1298
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeX86_64.c1092
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitNativeX86_common.c3422
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitProtExecAllocator.c474
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitUtils.c344
-rw-r--r--contrib/libs/pcre2/src/sljit/sljitWXExecAllocator.c204
-rw-r--r--contrib/libs/pcre2/ya.make70
-rw-r--r--contrib/libs/python/Include/opcode.h7
-rw-r--r--contrib/python/coverage/plugins/coveragerc.txt29
-rw-r--r--contrib/python/coverage/plugins/ya.make19
-rw-r--r--contrib/python/coverage/plugins/yarcadia/plugin.py114
-rw-r--r--contrib/python/coverage/py2/.dist-info/METADATA190
-rw-r--r--contrib/python/coverage/py2/.dist-info/entry_points.txt5
-rw-r--r--contrib/python/coverage/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/coverage/py2/LICENSE.txt177
-rw-r--r--contrib/python/coverage/py2/NOTICE.txt14
-rw-r--r--contrib/python/coverage/py2/README.rst151
-rw-r--r--contrib/python/coverage/py2/coverage/__init__.py36
-rw-r--r--contrib/python/coverage/py2/coverage/__main__.py8
-rw-r--r--contrib/python/coverage/py2/coverage/annotate.py108
-rw-r--r--contrib/python/coverage/py2/coverage/backward.py267
-rw-r--r--contrib/python/coverage/py2/coverage/bytecode.py19
-rw-r--r--contrib/python/coverage/py2/coverage/cmdline.py910
-rw-r--r--contrib/python/coverage/py2/coverage/collector.py455
-rw-r--r--contrib/python/coverage/py2/coverage/config.py605
-rw-r--r--contrib/python/coverage/py2/coverage/context.py91
-rw-r--r--contrib/python/coverage/py2/coverage/control.py1162
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/datastack.c50
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/datastack.h45
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/filedisp.c85
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/filedisp.h26
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/module.c108
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/stats.h31
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/tracer.c1149
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/tracer.h75
-rw-r--r--contrib/python/coverage/py2/coverage/ctracer/util.h67
-rw-r--r--contrib/python/coverage/py2/coverage/data.py125
-rw-r--r--contrib/python/coverage/py2/coverage/debug.py406
-rw-r--r--contrib/python/coverage/py2/coverage/disposition.py37
-rw-r--r--contrib/python/coverage/py2/coverage/env.py130
-rw-r--r--contrib/python/coverage/py2/coverage/execfile.py362
-rw-r--r--contrib/python/coverage/py2/coverage/files.py441
-rw-r--r--contrib/python/coverage/py2/coverage/fullcoverage/encodings.py60
-rw-r--r--contrib/python/coverage/py2/coverage/html.py539
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/coverage_html.js616
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/favicon_32.pngbin0 -> 1732 bytes
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/index.html119
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js9
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.hotkeys.js99
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.isonscreen.js53
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.min.js4
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/jquery.tablesorter.min.js2
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/keybd_closed.pngbin0 -> 112 bytes
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/keybd_open.pngbin0 -> 112 bytes
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/pyfile.html113
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/style.css291
-rw-r--r--contrib/python/coverage/py2/coverage/htmlfiles/style.scss660
-rw-r--r--contrib/python/coverage/py2/coverage/inorout.py513
-rw-r--r--contrib/python/coverage/py2/coverage/jsonreport.py103
-rw-r--r--contrib/python/coverage/py2/coverage/misc.py361
-rw-r--r--contrib/python/coverage/py2/coverage/multiproc.py117
-rw-r--r--contrib/python/coverage/py2/coverage/numbits.py163
-rw-r--r--contrib/python/coverage/py2/coverage/parser.py1276
-rw-r--r--contrib/python/coverage/py2/coverage/phystokens.py297
-rw-r--r--contrib/python/coverage/py2/coverage/plugin.py533
-rw-r--r--contrib/python/coverage/py2/coverage/plugin_support.py281
-rw-r--r--contrib/python/coverage/py2/coverage/python.py261
-rw-r--r--contrib/python/coverage/py2/coverage/pytracer.py274
-rw-r--r--contrib/python/coverage/py2/coverage/report.py86
-rw-r--r--contrib/python/coverage/py2/coverage/results.py343
-rw-r--r--contrib/python/coverage/py2/coverage/sqldata.py1123
-rw-r--r--contrib/python/coverage/py2/coverage/summary.py152
-rw-r--r--contrib/python/coverage/py2/coverage/templite.py302
-rw-r--r--contrib/python/coverage/py2/coverage/tomlconfig.py168
-rw-r--r--contrib/python/coverage/py2/coverage/version.py33
-rw-r--r--contrib/python/coverage/py2/coverage/xmlreport.py234
-rw-r--r--contrib/python/coverage/py2/ya.make98
-rw-r--r--contrib/python/coverage/py3/.dist-info/METADATA190
-rw-r--r--contrib/python/coverage/py3/.dist-info/entry_points.txt5
-rw-r--r--contrib/python/coverage/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/coverage/py3/LICENSE.txt177
-rw-r--r--contrib/python/coverage/py3/NOTICE.txt14
-rw-r--r--contrib/python/coverage/py3/README.rst151
-rw-r--r--contrib/python/coverage/py3/coverage/__init__.py36
-rw-r--r--contrib/python/coverage/py3/coverage/__main__.py8
-rw-r--r--contrib/python/coverage/py3/coverage/annotate.py108
-rw-r--r--contrib/python/coverage/py3/coverage/backward.py267
-rw-r--r--contrib/python/coverage/py3/coverage/bytecode.py19
-rw-r--r--contrib/python/coverage/py3/coverage/cmdline.py910
-rw-r--r--contrib/python/coverage/py3/coverage/collector.py455
-rw-r--r--contrib/python/coverage/py3/coverage/config.py605
-rw-r--r--contrib/python/coverage/py3/coverage/context.py91
-rw-r--r--contrib/python/coverage/py3/coverage/control.py1162
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/datastack.c50
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/datastack.h45
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/filedisp.c85
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/filedisp.h26
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/module.c108
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/stats.h31
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/tracer.c1149
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/tracer.h75
-rw-r--r--contrib/python/coverage/py3/coverage/ctracer/util.h103
-rw-r--r--contrib/python/coverage/py3/coverage/data.py125
-rw-r--r--contrib/python/coverage/py3/coverage/debug.py406
-rw-r--r--contrib/python/coverage/py3/coverage/disposition.py37
-rw-r--r--contrib/python/coverage/py3/coverage/env.py130
-rw-r--r--contrib/python/coverage/py3/coverage/execfile.py362
-rw-r--r--contrib/python/coverage/py3/coverage/files.py441
-rw-r--r--contrib/python/coverage/py3/coverage/fullcoverage/encodings.py60
-rw-r--r--contrib/python/coverage/py3/coverage/html.py539
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/coverage_html.js616
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/favicon_32.pngbin0 -> 1732 bytes
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/index.html119
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js9
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.hotkeys.js99
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.isonscreen.js53
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.min.js4
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/jquery.tablesorter.min.js2
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/keybd_closed.pngbin0 -> 112 bytes
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/keybd_open.pngbin0 -> 112 bytes
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/pyfile.html113
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/style.css291
-rw-r--r--contrib/python/coverage/py3/coverage/htmlfiles/style.scss660
-rw-r--r--contrib/python/coverage/py3/coverage/inorout.py513
-rw-r--r--contrib/python/coverage/py3/coverage/jsonreport.py103
-rw-r--r--contrib/python/coverage/py3/coverage/misc.py361
-rw-r--r--contrib/python/coverage/py3/coverage/multiproc.py117
-rw-r--r--contrib/python/coverage/py3/coverage/numbits.py163
-rw-r--r--contrib/python/coverage/py3/coverage/parser.py1276
-rw-r--r--contrib/python/coverage/py3/coverage/phystokens.py297
-rw-r--r--contrib/python/coverage/py3/coverage/plugin.py533
-rw-r--r--contrib/python/coverage/py3/coverage/plugin_support.py281
-rw-r--r--contrib/python/coverage/py3/coverage/python.py261
-rw-r--r--contrib/python/coverage/py3/coverage/pytracer.py274
-rw-r--r--contrib/python/coverage/py3/coverage/report.py86
-rw-r--r--contrib/python/coverage/py3/coverage/results.py343
-rw-r--r--contrib/python/coverage/py3/coverage/sqldata.py1123
-rw-r--r--contrib/python/coverage/py3/coverage/summary.py152
-rw-r--r--contrib/python/coverage/py3/coverage/templite.py302
-rw-r--r--contrib/python/coverage/py3/coverage/tomlconfig.py168
-rw-r--r--contrib/python/coverage/py3/coverage/version.py33
-rw-r--r--contrib/python/coverage/py3/coverage/xmlreport.py234
-rw-r--r--contrib/python/coverage/py3/ya.make98
-rw-r--r--contrib/python/coverage/ya.make19
-rw-r--r--contrib/python/diff-match-patch/py2/.dist-info/METADATA112
-rw-r--r--contrib/python/diff-match-patch/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/diff-match-patch/py2/AUTHORS10
-rw-r--r--contrib/python/diff-match-patch/py2/LICENSE202
-rw-r--r--contrib/python/diff-match-patch/py2/README.md84
-rw-r--r--contrib/python/diff-match-patch/py2/diff_match_patch/__init__.py9
-rw-r--r--contrib/python/diff-match-patch/py2/diff_match_patch/diff_match_patch_py2.py2037
-rw-r--r--contrib/python/diff-match-patch/py2/ya.make27
-rw-r--r--contrib/python/diff-match-patch/py3/.dist-info/METADATA108
-rw-r--r--contrib/python/diff-match-patch/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/diff-match-patch/py3/AUTHORS10
-rw-r--r--contrib/python/diff-match-patch/py3/LICENSE202
-rw-r--r--contrib/python/diff-match-patch/py3/README.md84
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/__init__.py10
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/__version__.py7
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/diff_match_patch.py2019
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest1.txt230
-rw-r--r--contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest2.txt188
-rw-r--r--contrib/python/diff-match-patch/py3/ya.make28
-rw-r--r--contrib/python/diff-match-patch/ya.make18
-rw-r--r--contrib/python/distro/py2/.dist-info/METADATA169
-rw-r--r--contrib/python/distro/py2/.dist-info/entry_points.txt3
-rw-r--r--contrib/python/distro/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/distro/py2/LICENSE202
-rw-r--r--contrib/python/distro/py2/README.md135
-rw-r--r--contrib/python/distro/py2/distro.py1386
-rw-r--r--contrib/python/distro/py2/ya.make31
-rw-r--r--contrib/python/distro/py3/.dist-info/METADATA183
-rw-r--r--contrib/python/distro/py3/.dist-info/entry_points.txt2
-rw-r--r--contrib/python/distro/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/distro/py3/LICENSE202
-rw-r--r--contrib/python/distro/py3/README.md152
-rw-r--r--contrib/python/distro/py3/distro/__init__.py54
-rw-r--r--contrib/python/distro/py3/distro/__main__.py4
-rw-r--r--contrib/python/distro/py3/distro/distro.py1399
-rw-r--r--contrib/python/distro/py3/distro/py.typed0
-rw-r--r--contrib/python/distro/py3/ya.make34
-rw-r--r--contrib/python/distro/ya.make18
-rw-r--r--contrib/python/humanfriendly/py2/LICENSE.txt20
-rw-r--r--contrib/python/humanfriendly/py2/README.rst170
-rw-r--r--contrib/python/humanfriendly/py3/.dist-info/METADATA216
-rw-r--r--contrib/python/humanfriendly/py3/.dist-info/entry_points.txt3
-rw-r--r--contrib/python/humanfriendly/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/humanfriendly/py3/LICENSE.txt20
-rw-r--r--contrib/python/humanfriendly/py3/README.rst170
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/__init__.py838
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/case.py157
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/cli.py291
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/compat.py146
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/decorators.py43
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/deprecation.py251
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/prompts.py376
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/sphinx.py315
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/tables.py341
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/terminal/__init__.py776
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/terminal/html.py423
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/terminal/spinners.py310
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/testing.py669
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/text.py449
-rw-r--r--contrib/python/humanfriendly/py3/humanfriendly/usage.py351
-rw-r--r--contrib/python/humanfriendly/py3/ya.make41
-rw-r--r--contrib/python/humanfriendly/ya.make18
-rw-r--r--contrib/python/marisa-trie/agent.pxd22
-rw-r--r--contrib/python/marisa-trie/base.pxd63
-rw-r--r--contrib/python/marisa-trie/iostream.pxd7
-rw-r--r--contrib/python/marisa-trie/key.pxd22
-rw-r--r--contrib/python/marisa-trie/keyset.pxd30
-rw-r--r--contrib/python/marisa-trie/marisa/agent.cc51
-rw-r--r--contrib/python/marisa-trie/marisa/agent.h75
-rw-r--r--contrib/python/marisa-trie/marisa/base.h196
-rw-r--r--contrib/python/marisa-trie/marisa/exception.h84
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/algorithm.h27
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/algorithm/sort.h197
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/intrin.h116
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io.h19
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/mapper.cc163
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/mapper.h68
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/reader.cc147
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/reader.h67
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/writer.cc148
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/io/writer.h66
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie.h17
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/cache.h82
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/config.h156
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/entry.h83
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/header.h62
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/history.h66
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/key.h227
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.cc877
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.h135
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/range.h116
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/state.h118
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/tail.cc218
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/trie/tail.h73
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector.h19
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.cc825
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.h180
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/flat-vector.h206
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/pop-count.h111
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/rank-index.h83
-rw-r--r--contrib/python/marisa-trie/marisa/grimoire/vector/vector.h257
-rw-r--r--contrib/python/marisa-trie/marisa/iostream.h19
-rw-r--r--contrib/python/marisa-trie/marisa/key.h86
-rw-r--r--contrib/python/marisa-trie/marisa/keyset.cc181
-rw-r--r--contrib/python/marisa-trie/marisa/keyset.h81
-rw-r--r--contrib/python/marisa-trie/marisa/query.h72
-rw-r--r--contrib/python/marisa-trie/marisa/scoped-array.h49
-rw-r--r--contrib/python/marisa-trie/marisa/scoped-ptr.h53
-rw-r--r--contrib/python/marisa-trie/marisa/stdio.h16
-rw-r--r--contrib/python/marisa-trie/marisa/trie.cc249
-rw-r--r--contrib/python/marisa-trie/marisa/trie.h65
-rw-r--r--contrib/python/marisa-trie/marisa_trie.pyx763
-rw-r--r--contrib/python/marisa-trie/query.pxd20
-rw-r--r--contrib/python/marisa-trie/std_iostream.pxd18
-rw-r--r--contrib/python/marisa-trie/trie.pxd41
-rw-r--r--contrib/python/marisa-trie/ya.make33
-rw-r--r--contrib/python/path.py/py2/LICENSE7
-rw-r--r--contrib/python/path.py/py2/README.rst134
-rw-r--r--contrib/python/path.py/py3/.dist-info/METADATA36
-rw-r--r--contrib/python/path.py/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/path.py/py3/LICENSE7
-rw-r--r--contrib/python/path.py/py3/README.rst1
-rw-r--r--contrib/python/path.py/py3/ya.make21
-rw-r--r--contrib/python/path.py/ya.make18
-rw-r--r--contrib/python/path/.dist-info/METADATA201
-rw-r--r--contrib/python/path/.dist-info/top_level.txt1
-rw-r--r--contrib/python/path/LICENSE17
-rw-r--r--contrib/python/path/README.rst163
-rw-r--r--contrib/python/path/path/__init__.py1665
-rw-r--r--contrib/python/path/path/classes.py27
-rw-r--r--contrib/python/path/path/masks.py159
-rw-r--r--contrib/python/path/path/matchers.py59
-rw-r--r--contrib/python/path/path/py.typed0
-rw-r--r--contrib/python/path/ya.make30
-rw-r--r--contrib/python/portalocker/py2/.dist-info/METADATA136
-rw-r--r--contrib/python/portalocker/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/portalocker/py2/LICENSE48
-rw-r--r--contrib/python/portalocker/py2/README.rst106
-rw-r--r--contrib/python/portalocker/py2/portalocker/__about__.py7
-rw-r--r--contrib/python/portalocker/py2/portalocker/__init__.py67
-rw-r--r--contrib/python/portalocker/py2/portalocker/constants.py39
-rw-r--r--contrib/python/portalocker/py2/portalocker/exceptions.py19
-rw-r--r--contrib/python/portalocker/py2/portalocker/portalocker.py148
-rw-r--r--contrib/python/portalocker/py2/portalocker/utils.py256
-rw-r--r--contrib/python/portalocker/py2/ya.make31
-rw-r--r--contrib/python/portalocker/py3/.dist-info/METADATA255
-rw-r--r--contrib/python/portalocker/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/portalocker/py3/LICENSE11
-rw-r--r--contrib/python/portalocker/py3/README.rst193
-rw-r--r--contrib/python/portalocker/py3/portalocker/__about__.py6
-rw-r--r--contrib/python/portalocker/py3/portalocker/__init__.py76
-rw-r--r--contrib/python/portalocker/py3/portalocker/__main__.py98
-rw-r--r--contrib/python/portalocker/py3/portalocker/constants.py58
-rw-r--r--contrib/python/portalocker/py3/portalocker/exceptions.py27
-rw-r--r--contrib/python/portalocker/py3/portalocker/portalocker.py117
-rw-r--r--contrib/python/portalocker/py3/portalocker/py.typed0
-rw-r--r--contrib/python/portalocker/py3/portalocker/redis.py236
-rw-r--r--contrib/python/portalocker/py3/portalocker/utils.py563
-rw-r--r--contrib/python/portalocker/py3/ya.make38
-rw-r--r--contrib/python/portalocker/ya.make18
-rw-r--r--contrib/python/pygtrie/py2/LICENSE202
-rw-r--r--contrib/python/pygtrie/py2/README.rst66
-rw-r--r--contrib/python/pygtrie/py3/.dist-info/METADATA220
-rw-r--r--contrib/python/pygtrie/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/pygtrie/py3/LICENSE202
-rw-r--r--contrib/python/pygtrie/py3/README.rst66
-rw-r--r--contrib/python/pygtrie/py3/pygtrie.py1939
-rw-r--r--contrib/python/pygtrie/py3/ya.make26
-rw-r--r--contrib/python/pygtrie/ya.make18
-rw-r--r--contrib/python/pylev/py2/.dist-info/METADATA106
-rw-r--r--contrib/python/pylev/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/pylev/py2/LICENSE25
-rw-r--r--contrib/python/pylev/py2/README.rst87
-rw-r--r--contrib/python/pylev/py2/pylev/__init__.py44
-rw-r--r--contrib/python/pylev/py2/pylev/classic.py35
-rw-r--r--contrib/python/pylev/py2/pylev/damerau.py80
-rw-r--r--contrib/python/pylev/py2/pylev/recursive.py56
-rw-r--r--contrib/python/pylev/py2/pylev/wf.py107
-rw-r--r--contrib/python/pylev/py2/ya.make26
-rw-r--r--contrib/python/pylev/py3/.dist-info/METADATA106
-rw-r--r--contrib/python/pylev/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/pylev/py3/LICENSE25
-rw-r--r--contrib/python/pylev/py3/README.rst87
-rw-r--r--contrib/python/pylev/py3/pylev/__init__.py44
-rw-r--r--contrib/python/pylev/py3/pylev/classic.py35
-rw-r--r--contrib/python/pylev/py3/pylev/damerau.py80
-rw-r--r--contrib/python/pylev/py3/pylev/recursive.py56
-rw-r--r--contrib/python/pylev/py3/pylev/wf.py107
-rw-r--r--contrib/python/pylev/py3/ya.make26
-rw-r--r--contrib/python/pylev/ya.make18
-rw-r--r--contrib/python/pyre2/py2/AUTHORS12
-rw-r--r--contrib/python/pyre2/py2/LICENSE9
-rw-r--r--contrib/python/pyre2/py2/README.rst250
-rw-r--r--contrib/python/pyre2/py2/tests/test_charliterals.txt47
-rw-r--r--contrib/python/pyre2/py2/tests/test_count.txt40
-rw-r--r--contrib/python/pyre2/py2/tests/test_emptygroups.txt36
-rw-r--r--contrib/python/pyre2/py2/tests/test_findall.txt42
-rw-r--r--contrib/python/pyre2/py2/tests/test_finditer.txt28
-rw-r--r--contrib/python/pyre2/py2/tests/test_match_expand.txt29
-rw-r--r--contrib/python/pyre2/py2/tests/test_mmap.txt18
-rw-r--r--contrib/python/pyre2/py2/tests/test_namedgroups.txt56
-rw-r--r--contrib/python/pyre2/py2/tests/test_pattern.txt12
-rw-r--r--contrib/python/pyre2/py2/tests/test_search.txt29
-rw-r--r--contrib/python/pyre2/py2/tests/test_split.txt17
-rw-r--r--contrib/python/pyre2/py2/tests/test_sub.txt31
-rw-r--r--contrib/python/pyre2/py2/tests/test_unicode.txt71
-rw-r--r--contrib/python/pyre2/py3/.dist-info/METADATA275
-rw-r--r--contrib/python/pyre2/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/pyre2/py3/AUTHORS12
-rw-r--r--contrib/python/pyre2/py3/LICENSE9
-rw-r--r--contrib/python/pyre2/py3/README.rst250
-rw-r--r--contrib/python/pyre2/py3/src/_re2macros.h13
-rw-r--r--contrib/python/pyre2/py3/src/compile.pxi234
-rw-r--r--contrib/python/pyre2/py3/src/includes.pxi109
-rw-r--r--contrib/python/pyre2/py3/src/match.pxi280
-rw-r--r--contrib/python/pyre2/py3/src/pattern.pxi650
-rw-r--r--contrib/python/pyre2/py3/src/re2.pyx458
-rw-r--r--contrib/python/pyre2/py3/tests/test_charliterals.txt47
-rw-r--r--contrib/python/pyre2/py3/tests/test_count.txt40
-rw-r--r--contrib/python/pyre2/py3/tests/test_emptygroups.txt36
-rw-r--r--contrib/python/pyre2/py3/tests/test_findall.txt42
-rw-r--r--contrib/python/pyre2/py3/tests/test_finditer.txt28
-rw-r--r--contrib/python/pyre2/py3/tests/test_match_expand.txt29
-rw-r--r--contrib/python/pyre2/py3/tests/test_mmap.txt18
-rw-r--r--contrib/python/pyre2/py3/tests/test_namedgroups.txt56
-rw-r--r--contrib/python/pyre2/py3/tests/test_pattern.txt12
-rw-r--r--contrib/python/pyre2/py3/tests/test_search.txt29
-rw-r--r--contrib/python/pyre2/py3/tests/test_split.txt17
-rw-r--r--contrib/python/pyre2/py3/tests/test_sub.txt31
-rw-r--r--contrib/python/pyre2/py3/tests/test_unicode.txt71
-rw-r--r--contrib/python/pyre2/py3/ya.make39
-rw-r--r--contrib/python/pyre2/ya.make18
-rw-r--r--contrib/python/python-libarchive/py2/libarchive/__init__.py800
-rw-r--r--contrib/python/python-libarchive/py2/libarchive/_libarchive.swg339
-rw-r--r--contrib/python/python-libarchive/py2/libarchive/tar.py135
-rw-r--r--contrib/python/python-libarchive/py2/libarchive/zip.py151
-rw-r--r--contrib/python/python-libarchive/py2/ya.make28
-rw-r--r--contrib/python/python-libarchive/py3/libarchive/__init__.py800
-rw-r--r--contrib/python/python-libarchive/py3/libarchive/_libarchive.swg339
-rw-r--r--contrib/python/python-libarchive/py3/libarchive/tar.py135
-rw-r--r--contrib/python/python-libarchive/py3/libarchive/zip.py151
-rw-r--r--contrib/python/python-libarchive/py3/ya.make28
-rw-r--r--contrib/python/python-libarchive/ya.make18
-rw-r--r--contrib/python/python-magic/py2/LICENSE58
-rw-r--r--contrib/python/python-magic/py2/README.md144
-rw-r--r--contrib/python/python-magic/py3/.dist-info/METADATA171
-rw-r--r--contrib/python/python-magic/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/python-magic/py3/LICENSE58
-rw-r--r--contrib/python/python-magic/py3/README.md144
-rw-r--r--contrib/python/python-magic/py3/magic/__init__.py469
-rw-r--r--contrib/python/python-magic/py3/magic/compat.py287
-rw-r--r--contrib/python/python-magic/py3/magic/loader.py50
-rw-r--r--contrib/python/python-magic/py3/magic/py.typed0
-rw-r--r--contrib/python/python-magic/py3/ya.make31
-rw-r--r--contrib/python/python-magic/ya.make18
-rw-r--r--contrib/python/termcolor/py2/.dist-info/METADATA135
-rw-r--r--contrib/python/termcolor/py2/.dist-info/top_level.txt1
-rw-r--r--contrib/python/termcolor/py2/COPYING.txt20
-rw-r--r--contrib/python/termcolor/py2/README.rst72
-rw-r--r--contrib/python/termcolor/py2/termcolor.py217
-rw-r--r--contrib/python/termcolor/py2/ya.make22
-rw-r--r--contrib/python/termcolor/py3/.dist-info/METADATA126
-rw-r--r--contrib/python/termcolor/py3/.dist-info/top_level.txt1
-rw-r--r--contrib/python/termcolor/py3/COPYING.txt19
-rw-r--r--contrib/python/termcolor/py3/README.md91
-rw-r--r--contrib/python/termcolor/py3/termcolor/__init__.py13
-rw-r--r--contrib/python/termcolor/py3/termcolor/__main__.py68
-rw-r--r--contrib/python/termcolor/py3/termcolor/py.typed0
-rw-r--r--contrib/python/termcolor/py3/termcolor/termcolor.py201
-rw-r--r--contrib/python/termcolor/py3/ya.make25
-rw-r--r--contrib/python/termcolor/ya.make18
-rw-r--r--contrib/python/toolz/py2/.dist-info/METADATA159
-rw-r--r--contrib/python/toolz/py2/.dist-info/top_level.txt2
-rw-r--r--contrib/python/toolz/py2/LICENSE.txt28
-rw-r--r--contrib/python/toolz/py2/README.rst132
-rw-r--r--contrib/python/toolz/py2/tlz/__init__.py9
-rw-r--r--contrib/python/toolz/py2/tlz/_build_tlz.py100
-rw-r--r--contrib/python/toolz/py2/toolz/__init__.py22
-rw-r--r--contrib/python/toolz/py2/toolz/_signatures.py832
-rw-r--r--contrib/python/toolz/py2/toolz/compatibility.py34
-rw-r--r--contrib/python/toolz/py2/toolz/curried/__init__.py103
-rw-r--r--contrib/python/toolz/py2/toolz/curried/exceptions.py18
-rw-r--r--contrib/python/toolz/py2/toolz/curried/operator.py23
-rw-r--r--contrib/python/toolz/py2/toolz/dicttoolz.py337
-rw-r--r--contrib/python/toolz/py2/toolz/functoolz.py1152
-rw-r--r--contrib/python/toolz/py2/toolz/itertoolz.py1056
-rw-r--r--contrib/python/toolz/py2/toolz/recipes.py47
-rw-r--r--contrib/python/toolz/py2/toolz/sandbox/__init__.py2
-rw-r--r--contrib/python/toolz/py2/toolz/sandbox/core.py133
-rw-r--r--contrib/python/toolz/py2/toolz/sandbox/parallel.py76
-rw-r--r--contrib/python/toolz/py2/toolz/utils.py9
-rw-r--r--contrib/python/toolz/py2/ya.make41
-rw-r--r--contrib/python/toolz/py3/.dist-info/METADATA159
-rw-r--r--contrib/python/toolz/py3/.dist-info/top_level.txt2
-rw-r--r--contrib/python/toolz/py3/AUTHORS.md33
-rw-r--r--contrib/python/toolz/py3/LICENSE.txt28
-rw-r--r--contrib/python/toolz/py3/README.rst132
-rw-r--r--contrib/python/toolz/py3/tlz/__init__.py9
-rw-r--r--contrib/python/toolz/py3/tlz/_build_tlz.py92
-rw-r--r--contrib/python/toolz/py3/toolz/__init__.py26
-rw-r--r--contrib/python/toolz/py3/toolz/_signatures.py785
-rw-r--r--contrib/python/toolz/py3/toolz/_version.py21
-rw-r--r--contrib/python/toolz/py3/toolz/compatibility.py30
-rw-r--r--contrib/python/toolz/py3/toolz/curried/__init__.py103
-rw-r--r--contrib/python/toolz/py3/toolz/curried/exceptions.py18
-rw-r--r--contrib/python/toolz/py3/toolz/curried/operator.py22
-rw-r--r--contrib/python/toolz/py3/toolz/dicttoolz.py339
-rw-r--r--contrib/python/toolz/py3/toolz/functoolz.py1048
-rw-r--r--contrib/python/toolz/py3/toolz/itertoolz.py1057
-rw-r--r--contrib/python/toolz/py3/toolz/recipes.py46
-rw-r--r--contrib/python/toolz/py3/toolz/sandbox/__init__.py2
-rw-r--r--contrib/python/toolz/py3/toolz/sandbox/core.py133
-rw-r--r--contrib/python/toolz/py3/toolz/sandbox/parallel.py75
-rw-r--r--contrib/python/toolz/py3/toolz/utils.py9
-rw-r--r--contrib/python/toolz/py3/ya.make42
-rw-r--r--contrib/python/toolz/ya.make18
-rw-r--r--contrib/python/ujson/py2/lib/ultrajson.h333
-rw-r--r--contrib/python/ujson/py2/lib/ultrajsondec.c907
-rw-r--r--contrib/python/ujson/py2/lib/ultrajsonenc.c1029
-rw-r--r--contrib/python/ujson/py2/python/JSONtoObj.c252
-rw-r--r--contrib/python/ujson/py2/python/objToJSON.c1168
-rw-r--r--contrib/python/ujson/py2/python/py_defines.h53
-rw-r--r--contrib/python/ujson/py2/python/ujson.c113
-rw-r--r--contrib/python/ujson/py2/python/version.h39
-rw-r--r--contrib/python/ujson/py2/ya.make30
-rw-r--r--contrib/python/ujson/py3/lib/ultrajson.h333
-rw-r--r--contrib/python/ujson/py3/lib/ultrajsondec.c907
-rw-r--r--contrib/python/ujson/py3/lib/ultrajsonenc.c1029
-rw-r--r--contrib/python/ujson/py3/python/JSONtoObj.c252
-rw-r--r--contrib/python/ujson/py3/python/objToJSON.c1168
-rw-r--r--contrib/python/ujson/py3/python/py_defines.h53
-rw-r--r--contrib/python/ujson/py3/python/ujson.c113
-rw-r--r--contrib/python/ujson/py3/python/version.h39
-rw-r--r--contrib/python/ujson/py3/ya.make30
-rw-r--r--contrib/python/ujson/ya.make18
-rw-r--r--contrib/tools/ragel5/common/buffer.h55
-rw-r--r--contrib/tools/ragel5/common/common.cpp296
-rw-r--r--contrib/tools/ragel5/common/common.h308
-rw-r--r--contrib/tools/ragel5/common/config.h39
-rw-r--r--contrib/tools/ragel5/common/pcheck.h51
-rw-r--r--contrib/tools/ragel5/common/version.h2
-rw-r--r--contrib/tools/ragel5/common/ya.make20
-rw-r--r--contrib/tools/ragel5/ragel/fsmap.cpp840
-rw-r--r--contrib/tools/ragel5/ragel/fsmattach.cpp425
-rw-r--r--contrib/tools/ragel5/ragel/fsmbase.cpp598
-rw-r--r--contrib/tools/ragel5/ragel/fsmgraph.cpp1426
-rw-r--r--contrib/tools/ragel5/ragel/fsmgraph.h1482
-rw-r--r--contrib/tools/ragel5/ragel/fsmmin.cpp732
-rw-r--r--contrib/tools/ragel5/ragel/fsmstate.cpp463
-rw-r--r--contrib/tools/ragel5/ragel/main.cpp355
-rw-r--r--contrib/tools/ragel5/ragel/parsedata.cpp1505
-rw-r--r--contrib/tools/ragel5/ragel/parsedata.h401
-rw-r--r--contrib/tools/ragel5/ragel/parsetree.cpp2089
-rw-r--r--contrib/tools/ragel5/ragel/parsetree.h755
-rw-r--r--contrib/tools/ragel5/ragel/ragel.h74
-rw-r--r--contrib/tools/ragel5/ragel/rlparse.cpp6088
-rw-r--r--contrib/tools/ragel5/ragel/rlparse.h184
-rw-r--r--contrib/tools/ragel5/ragel/rlscan.cpp4876
-rw-r--r--contrib/tools/ragel5/ragel/rlscan.h161
-rw-r--r--contrib/tools/ragel5/ragel/xmlcodegen.cpp713
-rw-r--r--contrib/tools/ragel5/ragel/xmlcodegen.h137
-rw-r--r--contrib/tools/ragel5/ragel/ya.make26
-rw-r--r--contrib/tools/ragel5/redfsm/gendata.cpp717
-rw-r--r--contrib/tools/ragel5/redfsm/gendata.h167
-rw-r--r--contrib/tools/ragel5/redfsm/phash.h10
-rw-r--r--contrib/tools/ragel5/redfsm/redfsm.cpp559
-rw-r--r--contrib/tools/ragel5/redfsm/redfsm.h534
-rw-r--r--contrib/tools/ragel5/redfsm/xmlparse.cpp3549
-rw-r--r--contrib/tools/ragel5/redfsm/xmlparse.h228
-rw-r--r--contrib/tools/ragel5/redfsm/xmlscan.cpp925
-rw-r--r--contrib/tools/ragel5/redfsm/xmltags.cpp244
-rw-r--r--contrib/tools/ragel5/redfsm/ya.make25
-rw-r--r--contrib/tools/ragel5/rlgen-cd/fflatcodegen.cpp351
-rw-r--r--contrib/tools/ragel5/rlgen-cd/fflatcodegen.h76
-rw-r--r--contrib/tools/ragel5/rlgen-cd/fgotocodegen.cpp262
-rw-r--r--contrib/tools/ragel5/rlgen-cd/fgotocodegen.h76
-rw-r--r--contrib/tools/ragel5/rlgen-cd/flatcodegen.cpp766
-rw-r--r--contrib/tools/ragel5/rlgen-cd/flatcodegen.h108
-rw-r--r--contrib/tools/ragel5/rlgen-cd/fsmcodegen.cpp749
-rw-r--r--contrib/tools/ragel5/rlgen-cd/fsmcodegen.h218
-rw-r--r--contrib/tools/ragel5/rlgen-cd/ftabcodegen.cpp405
-rw-r--r--contrib/tools/ragel5/rlgen-cd/ftabcodegen.h78
-rw-r--r--contrib/tools/ragel5/rlgen-cd/gotocodegen.cpp742
-rw-r--r--contrib/tools/ragel5/rlgen-cd/gotocodegen.h111
-rw-r--r--contrib/tools/ragel5/rlgen-cd/ipgotocodegen.cpp414
-rw-r--r--contrib/tools/ragel5/rlgen-cd/ipgotocodegen.h97
-rw-r--r--contrib/tools/ragel5/rlgen-cd/main.cpp394
-rw-r--r--contrib/tools/ragel5/rlgen-cd/rlgen-cd.h60
-rw-r--r--contrib/tools/ragel5/rlgen-cd/splitcodegen.cpp521
-rw-r--r--contrib/tools/ragel5/rlgen-cd/splitcodegen.h71
-rw-r--r--contrib/tools/ragel5/rlgen-cd/tabcodegen.cpp988
-rw-r--r--contrib/tools/ragel5/rlgen-cd/tabcodegen.h115
-rw-r--r--contrib/tools/ragel5/rlgen-cd/ya.make25
-rw-r--r--contrib/tools/swig/CHANGES27992
-rw-r--r--contrib/tools/swig/CHANGES.current79
-rw-r--r--contrib/tools/swig/COPYRIGHT113
-rw-r--r--contrib/tools/swig/INSTALL226
-rw-r--r--contrib/tools/swig/LICENSE22
-rw-r--r--contrib/tools/swig/LICENSE-GPL674
-rw-r--r--contrib/tools/swig/LICENSE-UNIVERSITIES95
-rw-r--r--contrib/tools/swig/Lib/exception.i332
-rw-r--r--contrib/tools/swig/Lib/go/exception.i7
-rw-r--r--contrib/tools/swig/Lib/go/go.swg744
-rw-r--r--contrib/tools/swig/Lib/go/gokw.swg33
-rw-r--r--contrib/tools/swig/Lib/go/goruntime.swg217
-rw-r--r--contrib/tools/swig/Lib/go/gostring.swg29
-rw-r--r--contrib/tools/swig/Lib/go/std_common.i4
-rw-r--r--contrib/tools/swig/Lib/go/std_except.i31
-rw-r--r--contrib/tools/swig/Lib/go/std_string.i162
-rw-r--r--contrib/tools/swig/Lib/go/std_vector.i92
-rw-r--r--contrib/tools/swig/Lib/go/typemaps.i298
-rw-r--r--contrib/tools/swig/Lib/java/enumtypesafe.swg117
-rw-r--r--contrib/tools/swig/Lib/java/java.swg1458
-rw-r--r--contrib/tools/swig/Lib/java/javahead.swg91
-rw-r--r--contrib/tools/swig/Lib/java/javakw.swg70
-rw-r--r--contrib/tools/swig/Lib/java/std_common.i5
-rw-r--r--contrib/tools/swig/Lib/java/std_except.i32
-rw-r--r--contrib/tools/swig/Lib/java/std_string.i121
-rw-r--r--contrib/tools/swig/Lib/java/std_vector.i185
-rw-r--r--contrib/tools/swig/Lib/java/typemaps.i529
-rw-r--r--contrib/tools/swig/Lib/perl5/exception.i5
-rw-r--r--contrib/tools/swig/Lib/perl5/extra-install.list2
-rw-r--r--contrib/tools/swig/Lib/perl5/perl5.swg42
-rw-r--r--contrib/tools/swig/Lib/perl5/perlfragments.swg23
-rw-r--r--contrib/tools/swig/Lib/perl5/perlinit.swg78
-rw-r--r--contrib/tools/swig/Lib/perl5/perlkw.swg251
-rw-r--r--contrib/tools/swig/Lib/perl5/perlmacros.swg2
-rw-r--r--contrib/tools/swig/Lib/perl5/perlopers.swg54
-rw-r--r--contrib/tools/swig/Lib/perl5/perlprimtypes.swg364
-rw-r--r--contrib/tools/swig/Lib/perl5/perlruntime.swg8
-rw-r--r--contrib/tools/swig/Lib/perl5/perlstrings.swg59
-rw-r--r--contrib/tools/swig/Lib/perl5/perltypemaps.swg104
-rw-r--r--contrib/tools/swig/Lib/perl5/perluserdir.swg2
-rw-r--r--contrib/tools/swig/Lib/perl5/reference.i261
-rw-r--r--contrib/tools/swig/Lib/perl5/std_common.i28
-rw-r--r--contrib/tools/swig/Lib/perl5/std_except.i1
-rw-r--r--contrib/tools/swig/Lib/perl5/std_string.i2
-rw-r--r--contrib/tools/swig/Lib/perl5/std_vector.i592
-rw-r--r--contrib/tools/swig/Lib/perl5/typemaps.i371
-rw-r--r--contrib/tools/swig/Lib/python/README103
-rw-r--r--contrib/tools/swig/Lib/python/builtin.swg764
-rw-r--r--contrib/tools/swig/Lib/python/exception.i6
-rw-r--r--contrib/tools/swig/Lib/python/pyapi.swg30
-rw-r--r--contrib/tools/swig/Lib/python/pybackward.swg45
-rw-r--r--contrib/tools/swig/Lib/python/pyclasses.swg157
-rw-r--r--contrib/tools/swig/Lib/python/pycontainer.swg1082
-rw-r--r--contrib/tools/swig/Lib/python/pydocs.swg45
-rw-r--r--contrib/tools/swig/Lib/python/pyerrors.swg107
-rw-r--r--contrib/tools/swig/Lib/python/pyfragments.swg23
-rw-r--r--contrib/tools/swig/Lib/python/pyhead.swg75
-rw-r--r--contrib/tools/swig/Lib/python/pyinit.swg325
-rw-r--r--contrib/tools/swig/Lib/python/pyiterators.swg458
-rw-r--r--contrib/tools/swig/Lib/python/pymacros.swg4
-rw-r--r--contrib/tools/swig/Lib/python/pyopers.swg264
-rw-r--r--contrib/tools/swig/Lib/python/pyprimtypes.swg353
-rw-r--r--contrib/tools/swig/Lib/python/pyrun.swg1913
-rw-r--r--contrib/tools/swig/Lib/python/pyruntime.swg49
-rw-r--r--contrib/tools/swig/Lib/python/pystdcommon.swg265
-rw-r--r--contrib/tools/swig/Lib/python/pystrings.swg139
-rw-r--r--contrib/tools/swig/Lib/python/python.swg59
-rw-r--r--contrib/tools/swig/Lib/python/pythonkw.swg140
-rw-r--r--contrib/tools/swig/Lib/python/pythreads.swg68
-rw-r--r--contrib/tools/swig/Lib/python/pytypemaps.swg105
-rw-r--r--contrib/tools/swig/Lib/python/pyuserdir.swg242
-rw-r--r--contrib/tools/swig/Lib/python/pywstrings.swg85
-rw-r--r--contrib/tools/swig/Lib/python/std_alloc.i1
-rw-r--r--contrib/tools/swig/Lib/python/std_char_traits.i1
-rw-r--r--contrib/tools/swig/Lib/python/std_common.i74
-rw-r--r--contrib/tools/swig/Lib/python/std_container.i2
-rw-r--r--contrib/tools/swig/Lib/python/std_except.i1
-rw-r--r--contrib/tools/swig/Lib/python/std_string.i1
-rw-r--r--contrib/tools/swig/Lib/python/std_vector.i34
-rw-r--r--contrib/tools/swig/Lib/python/typemaps.i148
-rw-r--r--contrib/tools/swig/Lib/python/wchar.i21
-rw-r--r--contrib/tools/swig/Lib/python/ya.make22
-rw-r--r--contrib/tools/swig/Lib/std/README22
-rw-r--r--contrib/tools/swig/Lib/std/std_alloc.i77
-rw-r--r--contrib/tools/swig/Lib/std/std_basic_string.i276
-rw-r--r--contrib/tools/swig/Lib/std/std_char_traits.i140
-rw-r--r--contrib/tools/swig/Lib/std/std_common.i250
-rw-r--r--contrib/tools/swig/Lib/std/std_container.i169
-rw-r--r--contrib/tools/swig/Lib/std/std_except.i73
-rw-r--r--contrib/tools/swig/Lib/std/std_string.i13
-rw-r--r--contrib/tools/swig/Lib/std/std_vector.i225
-rw-r--r--contrib/tools/swig/Lib/swig.swg729
-rw-r--r--contrib/tools/swig/Lib/swigerrors.swg15
-rw-r--r--contrib/tools/swig/Lib/swigfragments.swg90
-rw-r--r--contrib/tools/swig/Lib/swiginit.swg233
-rw-r--r--contrib/tools/swig/Lib/swiglabels.swg123
-rw-r--r--contrib/tools/swig/Lib/swigrun.swg581
-rw-r--r--contrib/tools/swig/Lib/swigwarn.swg300
-rw-r--r--contrib/tools/swig/Lib/swigwarnings.swg131
-rw-r--r--contrib/tools/swig/Lib/typemaps/README54
-rw-r--r--contrib/tools/swig/Lib/typemaps/enumint.swg39
-rw-r--r--contrib/tools/swig/Lib/typemaps/exception.swg87
-rw-r--r--contrib/tools/swig/Lib/typemaps/fragments.swg231
-rw-r--r--contrib/tools/swig/Lib/typemaps/inoutlist.swg296
-rw-r--r--contrib/tools/swig/Lib/typemaps/misctypes.swg21
-rw-r--r--contrib/tools/swig/Lib/typemaps/primtypes.swg367
-rw-r--r--contrib/tools/swig/Lib/typemaps/ptrtypes.swg208
-rw-r--r--contrib/tools/swig/Lib/typemaps/std_except.swg37
-rw-r--r--contrib/tools/swig/Lib/typemaps/std_string.swg25
-rw-r--r--contrib/tools/swig/Lib/typemaps/std_strings.swg78
-rw-r--r--contrib/tools/swig/Lib/typemaps/string.swg36
-rw-r--r--contrib/tools/swig/Lib/typemaps/strings.swg658
-rw-r--r--contrib/tools/swig/Lib/typemaps/swigmacros.swg228
-rw-r--r--contrib/tools/swig/Lib/typemaps/swigobject.swg37
-rw-r--r--contrib/tools/swig/Lib/typemaps/swigtype.swg710
-rw-r--r--contrib/tools/swig/Lib/typemaps/swigtypemaps.swg168
-rw-r--r--contrib/tools/swig/Lib/typemaps/typemaps.swg157
-rw-r--r--contrib/tools/swig/Lib/typemaps/valtypes.swg215
-rw-r--r--contrib/tools/swig/Lib/typemaps/void.swg84
-rw-r--r--contrib/tools/swig/Lib/wchar.i11
-rw-r--r--contrib/tools/swig/README139
-rw-r--r--contrib/tools/swig/RELEASENOTES494
-rw-r--r--contrib/tools/swig/Source/CParse/cparse.h85
-rw-r--r--contrib/tools/swig/Source/CParse/cscanner.c1088
-rw-r--r--contrib/tools/swig/Source/CParse/parser.y7499
-rw-r--r--contrib/tools/swig/Source/CParse/templ.c976
-rw-r--r--contrib/tools/swig/Source/CParse/util.c126
-rw-r--r--contrib/tools/swig/Source/DOH/README124
-rw-r--r--contrib/tools/swig/Source/DOH/base.c937
-rw-r--r--contrib/tools/swig/Source/DOH/doh.h507
-rw-r--r--contrib/tools/swig/Source/DOH/dohint.h131
-rw-r--r--contrib/tools/swig/Source/DOH/file.c342
-rw-r--r--contrib/tools/swig/Source/DOH/fio.c599
-rw-r--r--contrib/tools/swig/Source/DOH/hash.c583
-rw-r--r--contrib/tools/swig/Source/DOH/list.c371
-rw-r--r--contrib/tools/swig/Source/DOH/memory.c292
-rw-r--r--contrib/tools/swig/Source/DOH/string.c1286
-rw-r--r--contrib/tools/swig/Source/DOH/void.c96
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxycommands.h174
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxyentity.cxx69
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxyentity.h45
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxyparser.cxx1494
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxyparser.h377
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxytranslator.cxx69
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxytranslator.h90
-rw-r--r--contrib/tools/swig/Source/Doxygen/javadoc.cxx849
-rw-r--r--contrib/tools/swig/Source/Doxygen/javadoc.h169
-rw-r--r--contrib/tools/swig/Source/Doxygen/pydoc.cxx993
-rw-r--r--contrib/tools/swig/Source/Doxygen/pydoc.h208
-rw-r--r--contrib/tools/swig/Source/Include/swigconfig.h105
-rw-r--r--contrib/tools/swig/Source/Include/swigwarn.h327
-rw-r--r--contrib/tools/swig/Source/Modules/README9
-rw-r--r--contrib/tools/swig/Source/Modules/allocate.cxx971
-rw-r--r--contrib/tools/swig/Source/Modules/contract.cxx358
-rw-r--r--contrib/tools/swig/Source/Modules/csharp.cxx4616
-rw-r--r--contrib/tools/swig/Source/Modules/d.cxx4649
-rw-r--r--contrib/tools/swig/Source/Modules/directors.cxx270
-rw-r--r--contrib/tools/swig/Source/Modules/emit.cxx556
-rw-r--r--contrib/tools/swig/Source/Modules/go.cxx5708
-rw-r--r--contrib/tools/swig/Source/Modules/guile.cxx1662
-rw-r--r--contrib/tools/swig/Source/Modules/interface.cxx210
-rw-r--r--contrib/tools/swig/Source/Modules/java.cxx4979
-rw-r--r--contrib/tools/swig/Source/Modules/javascript.cxx2490
-rw-r--r--contrib/tools/swig/Source/Modules/lang.cxx3902
-rw-r--r--contrib/tools/swig/Source/Modules/lua.cxx2240
-rw-r--r--contrib/tools/swig/Source/Modules/main.cxx1407
-rw-r--r--contrib/tools/swig/Source/Modules/mzscheme.cxx802
-rw-r--r--contrib/tools/swig/Source/Modules/nested.cxx453
-rw-r--r--contrib/tools/swig/Source/Modules/ocaml.cxx1848
-rw-r--r--contrib/tools/swig/Source/Modules/octave.cxx1572
-rw-r--r--contrib/tools/swig/Source/Modules/overload.cxx866
-rw-r--r--contrib/tools/swig/Source/Modules/perl5.cxx2502
-rw-r--r--contrib/tools/swig/Source/Modules/php.cxx2702
-rw-r--r--contrib/tools/swig/Source/Modules/python.cxx5768
-rw-r--r--contrib/tools/swig/Source/Modules/r.cxx2889
-rw-r--r--contrib/tools/swig/Source/Modules/ruby.cxx3470
-rw-r--r--contrib/tools/swig/Source/Modules/scilab.cxx1167
-rw-r--r--contrib/tools/swig/Source/Modules/swigmain.cxx265
-rw-r--r--contrib/tools/swig/Source/Modules/swigmod.h463
-rw-r--r--contrib/tools/swig/Source/Modules/tcl8.cxx1291
-rw-r--r--contrib/tools/swig/Source/Modules/typepass.cxx1322
-rw-r--r--contrib/tools/swig/Source/Modules/utils.cxx218
-rw-r--r--contrib/tools/swig/Source/Modules/xml.cxx326
-rw-r--r--contrib/tools/swig/Source/Preprocessor/cpp.c2111
-rw-r--r--contrib/tools/swig/Source/Preprocessor/expr.c496
-rw-r--r--contrib/tools/swig/Source/Preprocessor/preprocessor.h40
-rw-r--r--contrib/tools/swig/Source/README15
-rw-r--r--contrib/tools/swig/Source/Swig/cwrap.c1661
-rw-r--r--contrib/tools/swig/Source/Swig/deprecate.c107
-rw-r--r--contrib/tools/swig/Source/Swig/error.c351
-rw-r--r--contrib/tools/swig/Source/Swig/extend.c141
-rw-r--r--contrib/tools/swig/Source/Swig/fragment.c188
-rw-r--r--contrib/tools/swig/Source/Swig/getopt.c104
-rw-r--r--contrib/tools/swig/Source/Swig/include.c377
-rw-r--r--contrib/tools/swig/Source/Swig/misc.c1569
-rw-r--r--contrib/tools/swig/Source/Swig/naming.c1771
-rw-r--r--contrib/tools/swig/Source/Swig/parms.c272
-rw-r--r--contrib/tools/swig/Source/Swig/scanner.c1898
-rw-r--r--contrib/tools/swig/Source/Swig/stype.c1422
-rw-r--r--contrib/tools/swig/Source/Swig/swig.h451
-rw-r--r--contrib/tools/swig/Source/Swig/swigfile.h41
-rw-r--r--contrib/tools/swig/Source/Swig/swigopt.h18
-rw-r--r--contrib/tools/swig/Source/Swig/swigparm.h35
-rw-r--r--contrib/tools/swig/Source/Swig/swigscan.h119
-rw-r--r--contrib/tools/swig/Source/Swig/swigtree.h54
-rw-r--r--contrib/tools/swig/Source/Swig/swigwrap.h31
-rw-r--r--contrib/tools/swig/Source/Swig/symbol.c2128
-rw-r--r--contrib/tools/swig/Source/Swig/tree.c426
-rw-r--r--contrib/tools/swig/Source/Swig/typemap.c2210
-rw-r--r--contrib/tools/swig/Source/Swig/typeobj.c1367
-rw-r--r--contrib/tools/swig/Source/Swig/typesys.c2309
-rw-r--r--contrib/tools/swig/Source/Swig/wrapfunc.c520
-rw-r--r--contrib/tools/swig/swig_lib.cpp28
-rw-r--r--contrib/tools/swig/ya.make120
1724 files changed, 623544 insertions, 0 deletions
diff --git a/contrib/deprecated/galloc/basictypes.h b/contrib/deprecated/galloc/basictypes.h
new file mode 100644
index 0000000000..985ba38531
--- /dev/null
+++ b/contrib/deprecated/galloc/basictypes.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <inttypes.h>
+
+typedef signed char schar;
+
+typedef int8_t int8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef int64_t int64;
+
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+
+const uint16 kuint16max = ( (uint16) 0xFFFF);
+const uint32 kuint32max = ( (uint32) 0xFFFFFFFF);
+const uint64 kuint64max = ( (((uint64) kuint32max) << 32) | kuint32max );
+
+const int8 kint8max = ( ( int8) 0x7F);
+const int16 kint16max = ( ( int16) 0x7FFF);
+const int32 kint32max = ( ( int32) 0x7FFFFFFF);
+const int64 kint64max = ( ((( int64) kint32max) << 32) | kuint32max );
+
+const int8 kint8min = ( ( int8) 0x80);
+const int16 kint16min = ( ( int16) 0x8000);
+const int32 kint32min = ( ( int32) 0x80000000);
+const int64 kint64min = ( ((( int64) kint32min) << 32) | 0 );
+
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
diff --git a/contrib/deprecated/galloc/commandlineflags.h b/contrib/deprecated/galloc/commandlineflags.h
new file mode 100644
index 0000000000..3666eaaf86
--- /dev/null
+++ b/contrib/deprecated/galloc/commandlineflags.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// This file is a compatibility layer that defines Google's version of
+// command line flags that are used for configuration.
+//
+// We put flags into their own namespace. It is purposefully
+// named in an opaque way that people should have trouble typing
+// directly. The idea is that DEFINE puts the flag in the weird
+// namespace, and DECLARE imports the flag from there into the
+// current namespace. The net result is to force people to use
+// DECLARE to get access to a flag, rather than saying
+// extern bool FLAGS_logtostderr;
+// or some such instead. We want this so we can put extra
+// functionality (like sanity-checking) in DECLARE if we want,
+// and make sure it is picked up everywhere.
+//
+// We also put the type of the variable in the namespace, so that
+// people can't DECLARE_int32 something that they DEFINE_bool'd
+// elsewhere.
+#ifndef BASE_COMMANDLINEFLAGS_H__
+#define BASE_COMMANDLINEFLAGS_H__
+
+#include <string>
+
+#include "basictypes.h"
+
+#define DECLARE_VARIABLE(type, name) \
+ namespace FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead { \
+ extern type FLAGS_##name; \
+ } \
+ using FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead::FLAGS_##name
+
+#define DEFINE_VARIABLE(type, name, value, meaning) \
+ namespace FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead { \
+ type FLAGS_##name(value); \
+ char FLAGS_no##name; \
+ } \
+ using FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead::FLAGS_##name
+
+// bool specialization
+#define DECLARE_bool(name) \
+ DECLARE_VARIABLE(bool, name)
+#define DEFINE_bool(name, value, meaning) \
+ DEFINE_VARIABLE(bool, name, value, meaning)
+
+// int32 specialization
+#define DECLARE_int32(name) \
+ DECLARE_VARIABLE(int32, name)
+#define DEFINE_int32(name, value, meaning) \
+ DEFINE_VARIABLE(int32, name, value, meaning)
+
+// int64 specialization
+#define DECLARE_int64(name) \
+ DECLARE_VARIABLE(int64, name)
+#define DEFINE_int64(name, value, meaning) \
+ DEFINE_VARIABLE(int64, name, value, meaning)
+
+#define DECLARE_uint64(name) \
+ DECLARE_VARIABLE(uint64, name)
+#define DEFINE_uint64(name, value, meaning) \
+ DEFINE_VARIABLE(uint64, name, value, meaning)
+
+// double specialization
+#define DECLARE_double(name) \
+ DECLARE_VARIABLE(double, name)
+#define DEFINE_double(name, value, meaning) \
+ DEFINE_VARIABLE(double, name, value, meaning)
+
+// Special case for string, because we have to specify the namespace
+// std::string, which doesn't play nicely with our FLAG__namespace hackery.
+#define DECLARE_string(name) \
+ namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead { \
+ extern std::string FLAGS_##name; \
+ } \
+ using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name
+#define DEFINE_string(name, value, meaning) \
+ namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead { \
+ std::string FLAGS_##name(value); \
+ char FLAGS_no##name; \
+ } \
+ using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name
+
+#endif // BASE_COMMANDLINEFLAGS_H__
diff --git a/contrib/deprecated/galloc/galloc.cpp b/contrib/deprecated/galloc/galloc.cpp
new file mode 100644
index 0000000000..4d7fc14f49
--- /dev/null
+++ b/contrib/deprecated/galloc/galloc.cpp
@@ -0,0 +1,55 @@
+#include <util/system/defaults.h>
+
+#ifndef USE_GOOGLE_ALLOCATOR
+ #define USE_GOOGLE_ALLOCATOR 1
+#endif
+
+#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined (__ICL) && !defined (__COMO__)
+ #define USE_VISUALCC
+#elif defined(__INTEL_COMPILER)
+ #define USE_INTELCC
+#elif defined(__GNUC__)
+ #define USE_GNUCC
+#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
+ #define USE_SUNCC
+#else
+ //#error your compiler does not supported
+#endif
+
+#if defined(USE_INTELCC)
+ #pragma warning(disable 177)
+ #pragma warning(disable 869)
+ #pragma warning(disable 810)
+ #pragma warning(disable 967)
+ #pragma warning(disable 1599)
+ #pragma warning(disable 1469)
+#endif
+
+#if defined(_linux_) || defined(_freebsd_)
+ #define GOOGLE_ALLOCATOR_IS_USABLE
+#endif
+
+#if defined(GOOGLE_ALLOCATOR_IS_USABLE) && USE_GOOGLE_ALLOCATOR
+ #undef NDEBUG
+ #define NDEBUG
+
+ #define HAVE_INTTYPES_H 1
+ #define HAVE_MMAP 1
+ #define HAVE_MUNMAP 1
+ #define HAVE_PTHREAD 1
+ #define HAVE_SBRK 1
+ #define HAVE_UNWIND_H 1
+
+ #if defined(USE_INTELCC) || defined(USE_GNUCC)
+ #undef HAVE___ATTRIBUTE__
+ #define HAVE___ATTRIBUTE__
+ #endif
+
+ #define PRIuS PRISZT
+ #define LLU PRIu64
+
+ #include "malloc_extension.cc"
+ #include "internal_logging.cc"
+ #include "system-alloc.cc"
+ #include "tcmalloc.cc"
+#endif
diff --git a/contrib/deprecated/galloc/hack.cpp b/contrib/deprecated/galloc/hack.cpp
new file mode 100644
index 0000000000..3ba36dacea
--- /dev/null
+++ b/contrib/deprecated/galloc/hack.cpp
@@ -0,0 +1,20 @@
+#include "hack.h"
+
+#include <library/cpp/deprecated/atomic/atomic.h>
+#include <util/system/spin_wait.h>
+
+#include "spinlock.h"
+
+void SPIN_L(spinlock_t* l) {
+ if (!AtomicTryLock(l)) {
+ TSpinWait sw;
+
+ while (!AtomicTryAndTryLock(l)) {
+ sw.Sleep();
+ }
+ }
+}
+
+void SPIN_U(spinlock_t* l) {
+ AtomicUnlock(l);
+}
diff --git a/contrib/deprecated/galloc/hack.h b/contrib/deprecated/galloc/hack.h
new file mode 100644
index 0000000000..3b172a2da2
--- /dev/null
+++ b/contrib/deprecated/galloc/hack.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <sys/types.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define AcquireAdaptiveLockSlow AllocAcquireAdaptiveLockSlow
+#define SchedYield AllocSchedYield
+#define ThreadYield AllocThreadYield
+#define NSystemInfo NAllocSystemInfo
+
+#ifdef _MSC_VER
+# define __restrict__ __restrict
+# define JEMALLOC_EXPORT
+#endif
+
+#if defined(__cplusplus)
+};
+#endif
diff --git a/contrib/deprecated/galloc/internal_logging.cc b/contrib/deprecated/galloc/internal_logging.cc
new file mode 100644
index 0000000000..00e5928fe2
--- /dev/null
+++ b/contrib/deprecated/galloc/internal_logging.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// Sanjay Ghemawat <opensource@google.com>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "internal_logging.h"
+
+int TCMallocDebug::level;
+
+void TCMalloc_MESSAGE(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ char buf[800];
+ vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+ write(STDERR_FILENO, buf, strlen(buf));
+}
+
+void TCMalloc_Printer::printf(const char* format, ...) {
+ if (left_ > 0) {
+ va_list ap;
+ va_start(ap, format);
+ const int r = vsnprintf(buf_, left_, format, ap);
+ va_end(ap);
+ if (r < 0) {
+ // Perhaps an old glibc that returns -1 on truncation?
+ left_ = 0;
+ } else if (r > left_) {
+ // Truncation
+ left_ = 0;
+ } else {
+ left_ -= r;
+ buf_ += r;
+ }
+ }
+}
diff --git a/contrib/deprecated/galloc/internal_logging.h b/contrib/deprecated/galloc/internal_logging.h
new file mode 100644
index 0000000000..6a4e365723
--- /dev/null
+++ b/contrib/deprecated/galloc/internal_logging.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// Author: Sanjay Ghemawat <opensource@google.com>
+//
+// Internal logging and related utility routines.
+
+#ifndef TCMALLOC_INTERNAL_LOGGING_H__
+#define TCMALLOC_INTERNAL_LOGGING_H__
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+//-------------------------------------------------------------------
+// Utility routines
+//-------------------------------------------------------------------
+
+struct TCMallocDebug {
+ static int level;
+
+ enum { kNone, kInfo, kVerbose };
+};
+
+// Safe debugging routine: we write directly to the stderr file
+// descriptor and avoid FILE buffering because that may invoke
+// malloc()
+extern void TCMalloc_MESSAGE(const char* format, ...)
+#ifdef HAVE___ATTRIBUTE__
+ __attribute__ ((__format__ (__printf__, 1, 2)))
+#endif
+;
+
+// Short form for convenience
+#define MESSAGE TCMalloc_MESSAGE
+
+// Like assert(), but executed even in NDEBUG mode
+#undef CHECK_CONDITION
+#define CHECK_CONDITION(cond) \
+do { \
+ if (!(cond)) { \
+ MESSAGE("%s:%d: assertion failed: %s\n", __FILE__, __LINE__, #cond); \
+ abort(); \
+ } \
+} while (0)
+
+// Our own version of assert() so we can avoid hanging by trying to do
+// all kinds of goofy printing while holding the malloc lock.
+#ifndef NDEBUG
+#define ASSERT(cond) CHECK_CONDITION(cond)
+#else
+#define ASSERT(cond) ((void) 0)
+#endif
+
+// Print into buffer
+class TCMalloc_Printer {
+ private:
+ char* buf_; // Where should we write next
+ int left_; // Space left in buffer (including space for \0)
+
+ public:
+ // REQUIRES: "length > 0"
+ TCMalloc_Printer(char* buf, int length) : buf_(buf), left_(length) {
+ buf[0] = '\0';
+ }
+
+ void printf(const char* format, ...)
+#ifdef HAVE___ATTRIBUTE__
+ __attribute__ ((__format__ (__printf__, 2, 3)))
+#endif
+;
+};
+
+#endif // TCMALLOC_INTERNAL_LOGGING_H__
diff --git a/contrib/deprecated/galloc/internal_spinlock.h b/contrib/deprecated/galloc/internal_spinlock.h
new file mode 100644
index 0000000000..e7086f2da2
--- /dev/null
+++ b/contrib/deprecated/galloc/internal_spinlock.h
@@ -0,0 +1,47 @@
+#pragma once
+
+extern "C" {
+ #include "hack.h"
+ #include "spinlock.h"
+}
+
+#define SPINLOCK_INITIALIZER { _SPINLOCK_INITIALIZER }
+
+struct TCMalloc_SpinLock {
+ volatile spinlock_t private_lockword_;
+
+ inline void Init() noexcept {
+ private_lockword_ = _SPINLOCK_INITIALIZER;
+ }
+
+ inline void Finalize() noexcept {
+ }
+
+ inline void Lock() noexcept {
+ _SPINLOCK(&private_lockword_);
+ }
+
+ inline void Unlock() noexcept {
+ _SPINUNLOCK(&private_lockword_);
+ }
+};
+
+class TCMalloc_SpinLockHolder {
+ private:
+ TCMalloc_SpinLock* lock_;
+
+ public:
+ inline explicit TCMalloc_SpinLockHolder(TCMalloc_SpinLock* l)
+ : lock_(l)
+ {
+ l->Lock();
+ }
+
+ inline ~TCMalloc_SpinLockHolder() {
+ lock_->Unlock();
+ }
+};
+
+// Short-hands for convenient use by tcmalloc.cc
+typedef TCMalloc_SpinLock SpinLock;
+typedef TCMalloc_SpinLockHolder SpinLockHolder;
diff --git a/contrib/deprecated/galloc/malloc_extension.cc b/contrib/deprecated/galloc/malloc_extension.cc
new file mode 100644
index 0000000000..4b20b72bdc
--- /dev/null
+++ b/contrib/deprecated/galloc/malloc_extension.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// Author: Sanjay Ghemawat <opensource@google.com>
+
+#include <assert.h>
+#include <string.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <new>
+#include <string>
+
+#include "malloc_extension.h"
+
+// Note: this routine is meant to be called before threads are spawned.
+void MallocExtension::Initialize() {
+ static bool initialize_called = false;
+
+ if (initialize_called) return;
+ initialize_called = true;
+
+ // GNU libc++ versions 3.3 and 3.4 obey the environment variables
+ // GLIBCPP_FORCE_NEW and GLIBCXX_FORCE_NEW respectively. Setting
+ // one of these variables forces the STL default allocator to call
+ // new() or delete() for each allocation or deletion. Otherwise
+ // the STL allocator tries to avoid the high cost of doing
+ // allocations by pooling memory internally. However, tcmalloc
+ // does allocations really fast, especially for the types of small
+ // items one sees in STL, so it's better off just using us.
+ // TODO: control whether we do this via an environment variable?
+ setenv("GLIBCPP_FORCE_NEW", "1", false /* no overwrite*/);
+ setenv("GLIBCXX_FORCE_NEW", "1", false /* no overwrite*/);
+
+ // Now we need to make the setenv 'stick', which it may not do since
+ // the env is flakey before main() is called. But luckily stl only
+ // looks at this env var the first time it tries to do an alloc, and
+ // caches what it finds. So we just cause an stl alloc here.
+ std::string dummy("I need to be allocated");
+ dummy += "!"; // so the definition of dummy isn't optimized out
+}
+
+// Default implementation -- does nothing
+MallocExtension::~MallocExtension() { }
+bool MallocExtension::VerifyAllMemory() { return true; }
+bool MallocExtension::VerifyNewMemory(void* /*p*/) { return true; }
+bool MallocExtension::VerifyArrayNewMemory(void* /*p*/) { return true; }
+bool MallocExtension::VerifyMallocMemory(void* /*p*/) { return true; }
+
+bool MallocExtension::GetNumericProperty(const char* /*property*/, size_t* /*value*/) {
+ return false;
+}
+
+bool MallocExtension::SetNumericProperty(const char* /*property*/, size_t /*value*/) {
+ return false;
+}
+
+void MallocExtension::GetStats(char* buffer, int length) {
+ assert(length > 0);
+ (void)length;
+ buffer[0] = '\0';
+}
+
+bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total,
+ int histogram[kMallocHistogramSize]) {
+ *blocks = 0;
+ *total = 0;
+ memset(histogram, 0, sizeof(histogram));
+ return true;
+}
+
+void** MallocExtension::ReadStackTraces() {
+ return NULL;
+}
+
+void** MallocExtension::ReadHeapGrowthStackTraces() {
+ return NULL;
+}
+
+// The current malloc extension object. We also keep a pointer to
+// the default implementation so that the heap-leak checker does not
+// complain about a memory leak.
+
+static pthread_once_t module_init = PTHREAD_ONCE_INIT;
+static MallocExtension* default_instance = NULL;
+static MallocExtension* current_instance = NULL;
+
+static void InitModule() {
+ default_instance = new MallocExtension;
+ current_instance = default_instance;
+}
+
+MallocExtension* MallocExtension::instance() {
+ pthread_once(&module_init, InitModule);
+ return current_instance;
+}
+
+void MallocExtension::Register(MallocExtension* implementation) {
+ pthread_once(&module_init, InitModule);
+ current_instance = implementation;
+}
diff --git a/contrib/deprecated/galloc/malloc_extension.h b/contrib/deprecated/galloc/malloc_extension.h
new file mode 100644
index 0000000000..3ed8a91e35
--- /dev/null
+++ b/contrib/deprecated/galloc/malloc_extension.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// Author: Sanjay Ghemawat <opensource@google.com>
+//
+// Extra interfaces exported by some malloc implementations. These
+// interfaces are accessed through a virtual base class so an
+// application can link against a malloc that does not implement these
+// interfaces, and it will get default versions that do nothing.
+
+#ifndef _GOOGLE_MALLOC_EXTENSION_H__
+#define _GOOGLE_MALLOC_EXTENSION_H__
+
+#include <stddef.h>
+
+static const int kMallocHistogramSize = 64;
+
+// The default implementations of the following routines do nothing.
+class MallocExtension {
+ public:
+ virtual ~MallocExtension();
+
+ // Call this very early in the program execution -- say, in a global
+ // constructor -- to set up parameters and state needed by all
+ // instrumented malloc implemenatations. One example: this routine
+ // sets environemnt variables to tell STL to use libc's malloc()
+ // instead of doing its own memory management. This is safe to call
+ // multiple times, as long as each time is before threads start up.
+ static void Initialize();
+
+ // See "verify_memory.h" to see what these routines do
+ virtual bool VerifyAllMemory();
+ virtual bool VerifyNewMemory(void* p);
+ virtual bool VerifyArrayNewMemory(void* p);
+ virtual bool VerifyMallocMemory(void* p);
+ virtual bool MallocMemoryStats(int* blocks, size_t* total,
+ int histogram[kMallocHistogramSize]);
+
+ // Get a human readable description of the current state of the malloc
+ // data structures. The state is stored as a null-terminated string
+ // in a prefix of "buffer[0,buffer_length-1]".
+ // REQUIRES: buffer_length > 0.
+ virtual void GetStats(char* buffer, int buffer_length);
+
+ // -------------------------------------------------------------------
+ // Control operations for getting and setting malloc implementation
+ // specific parameters. Some currently useful properties:
+ //
+ // generic
+ // -------
+ // "generic.current_allocated_bytes"
+ // Number of bytes currently allocated by application
+ // This property is not writable.
+ //
+ // "generic.heap_size"
+ // Number of bytes in the heap ==
+ // current_allocated_bytes +
+ // fragmentation +
+ // freed memory regions
+ // This property is not writable.
+ //
+ // tcmalloc
+ // --------
+ // "tcmalloc.max_total_thread_cache_bytes"
+ // Upper limit on total number of bytes stored across all
+ // per-thread caches. Default: 16MB.
+ //
+ // "tcmalloc.current_total_thread_cache_bytes"
+ // Number of bytes used across all thread caches.
+ // This property is not writable.
+ //
+ // "tcmalloc.slack_bytes"
+ // Number of bytes allocated from system, but not currently
+ // in use by malloced objects. I.e., bytes available for
+ // allocation without needing more bytes from system.
+ // This property is not writable.
+ //
+ // TODO: Add more properties as necessary
+ // -------------------------------------------------------------------
+
+ // Get the named "property"'s value. Returns true if the property
+ // is known. Returns false if the property is not a valid property
+ // name for the current malloc implementation.
+ // REQUIRES: property != NULL; value != NULL
+ virtual bool GetNumericProperty(const char* property, size_t* value);
+
+ // Set the named "property"'s value. Returns true if the property
+ // is known and writable. Returns false if the property is not a
+ // valid property name for the current malloc implementation, or
+ // is not writable.
+ // REQUIRES: property != NULL
+ virtual bool SetNumericProperty(const char* property, size_t value);
+
+ // The current malloc implementation. Always non-NULL.
+ static MallocExtension* instance();
+
+ // Change the malloc implementation. Typically called by the
+ // malloc implementation during initialization.
+ static void Register(MallocExtension* implementation);
+
+ protected:
+ // Get a list of stack traces of sampled allocation points.
+ // Returns a pointer to a "new[]-ed" result array.
+ //
+ // The state is stored as a sequence of adjacent entries
+ // in the returned array. Each entry has the following form:
+ // uintptr_t count; // Number of objects with following trace
+ // uintptr_t size; // Size of object
+ // uintptr_t depth; // Number of PC values in stack trace
+ // void* stack[depth]; // PC values that form the stack trace
+ //
+ // The list of entries is terminated by a "count" of 0.
+ //
+ // It is the responsibility of the caller to "delete[]" the returned array.
+ //
+ // May return NULL to indicate no results.
+ //
+ // This is an internal extension. Callers should use the more
+ // convenient "GetHeapSample(string*)" method defined above.
+ virtual void** ReadStackTraces();
+
+ // Like ReadStackTraces(), but returns stack traces that caused growth
+ // in the address space size.
+ virtual void** ReadHeapGrowthStackTraces();
+};
+
+#endif // _GOOGLE_MALLOC_EXTENSION_H__
diff --git a/contrib/deprecated/galloc/malloc_hook.h b/contrib/deprecated/galloc/malloc_hook.h
new file mode 100644
index 0000000000..1e109180b6
--- /dev/null
+++ b/contrib/deprecated/galloc/malloc_hook.h
@@ -0,0 +1,13 @@
+#pragma once
+
+class MallocHook {
+ public:
+ typedef void (*NewHook)(void* ptr, size_t size);
+ typedef void (*DeleteHook)(void* ptr);
+
+ inline static void InvokeNewHook(void*, size_t) {
+ }
+
+ inline static void InvokeDeleteHook(void*) {
+ }
+};
diff --git a/contrib/deprecated/galloc/pagemap.h b/contrib/deprecated/galloc/pagemap.h
new file mode 100644
index 0000000000..7be23ff90c
--- /dev/null
+++ b/contrib/deprecated/galloc/pagemap.h
@@ -0,0 +1,250 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// Author: Sanjay Ghemawat <opensource@google.com>
+//
+// A data structure used by the caching malloc. It maps from page# to
+// a pointer that contains info about that page. We use two
+// representations: one for 32-bit addresses, and another for 64 bit
+// addresses. Both representations provide the same interface. The
+// first representation is implemented as a flat array, the seconds as
+// a three-level radix tree that strips away approximately 1/3rd of
+// the bits every time.
+//
+// The BITS parameter should be the number of bits required to hold
+// a page number. E.g., with 32 bit pointers and 4K pages (i.e.,
+// page offset fits in lower 12 bits), BITS == 20.
+
+#ifndef TCMALLOC_PAGEMAP_H__
+#define TCMALLOC_PAGEMAP_H__
+
+#if defined HAVE_STDINT_H
+#include <stdint.h>
+#elif defined HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <sys/types.h>
+#endif
+#include "internal_logging.h"
+
+// Single-level array
+template <int BITS>
+class TCMalloc_PageMap1 {
+ private:
+ void** array_;
+
+ public:
+ typedef uintptr_t Number;
+
+ explicit TCMalloc_PageMap1(void* (*allocator)(size_t)) {
+ array_ = reinterpret_cast<void**>((*allocator)(sizeof(void*) << BITS));
+ memset(array_, 0, sizeof(void*) << BITS);
+ }
+
+ // Ensure that the map contains initialized entries "x .. x+n-1".
+ // Returns true if successful, false if we could not allocate memory.
+ bool Ensure(Number x, size_t n) {
+ // Nothing to do since flat array was allocate at start
+ return true;
+ }
+
+ void PreallocateMoreMemory() {}
+
+ // REQUIRES "k" is in range "[0,2^BITS-1]".
+ // REQUIRES "k" has been ensured before.
+ //
+ // Return the current value for KEY. Returns "Value()" if not
+ // yet set.
+ void* get(Number k) const {
+ return array_[k];
+ }
+
+ // REQUIRES "k" is in range "[0,2^BITS-1]".
+ // REQUIRES "k" has been ensured before.
+ //
+ // Sets the value for KEY.
+ void set(Number k, void* v) {
+ array_[k] = v;
+ }
+};
+
+// Two-level radix tree
+template <int BITS>
+class TCMalloc_PageMap2 {
+ private:
+ // Put 32 entries in the root and (2^BITS)/32 entries in each leaf.
+ static const int ROOT_BITS = 5;
+ static const int ROOT_LENGTH = 1 << ROOT_BITS;
+
+ static const int LEAF_BITS = BITS - ROOT_BITS;
+ static const int LEAF_LENGTH = 1 << LEAF_BITS;
+
+ // Leaf node
+ struct Leaf {
+ void* values[LEAF_LENGTH];
+ };
+
+ Leaf* root_[ROOT_LENGTH]; // Pointers to 32 child nodes
+ void* (*allocator_)(size_t); // Memory allocator
+
+ public:
+ typedef uintptr_t Number;
+
+ explicit TCMalloc_PageMap2(void* (*allocator)(size_t)) {
+ allocator_ = allocator;
+ memset(root_, 0, sizeof(root_));
+ }
+
+ void* get(Number k) const {
+ ASSERT(k >> BITS == 0);
+ const Number i1 = k >> LEAF_BITS;
+ const Number i2 = k & (LEAF_LENGTH-1);
+ return root_[i1]->values[i2];
+ }
+
+ void set(Number k, void* v) {
+ ASSERT(k >> BITS == 0);
+ const Number i1 = k >> LEAF_BITS;
+ const Number i2 = k & (LEAF_LENGTH-1);
+ root_[i1]->values[i2] = v;
+ }
+
+ bool Ensure(Number start, size_t n) {
+ for (Number key = start; key <= start + n - 1; ) {
+ const Number i1 = key >> LEAF_BITS;
+
+ // Make 2nd level node if necessary
+ if (root_[i1] == NULL) {
+ Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf)));
+ if (leaf == NULL) return false;
+ memset(leaf, 0, sizeof(*leaf));
+ root_[i1] = leaf;
+ }
+
+ // Advance key past whatever is covered by this leaf node
+ key = ((key >> LEAF_BITS) + 1) << LEAF_BITS;
+ }
+ return true;
+ }
+
+ void PreallocateMoreMemory() {
+ // Allocate enough to keep track of all possible pages
+ Ensure(0, 1 << BITS);
+ }
+};
+
+// Three-level radix tree
+template <int BITS>
+class TCMalloc_PageMap3 {
+ private:
+ // How many bits should we consume at each interior level
+ static const int INTERIOR_BITS = (BITS + 2) / 3; // Round-up
+ static const int INTERIOR_LENGTH = 1 << INTERIOR_BITS;
+
+ // How many bits should we consume at leaf level
+ static const int LEAF_BITS = BITS - 2*INTERIOR_BITS;
+ static const int LEAF_LENGTH = 1 << LEAF_BITS;
+
+ // Interior node
+ struct Node {
+ Node* ptrs[INTERIOR_LENGTH];
+ };
+
+ // Leaf node
+ struct Leaf {
+ void* values[LEAF_LENGTH];
+ };
+
+ Node* root_; // Root of radix tree
+ void* (*allocator_)(size_t); // Memory allocator
+
+ Node* NewNode() {
+ Node* result = reinterpret_cast<Node*>((*allocator_)(sizeof(Node)));
+ if (result != NULL) {
+ memset(result, 0, sizeof(*result));
+ }
+ return result;
+ }
+
+ public:
+ typedef uintptr_t Number;
+
+ explicit TCMalloc_PageMap3(void* (*allocator)(size_t)) {
+ allocator_ = allocator;
+ root_ = NewNode();
+ }
+
+ void* get(Number k) const {
+ ASSERT(k >> BITS == 0);
+ const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS);
+ const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1);
+ const Number i3 = k & (LEAF_LENGTH-1);
+ return reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3];
+ }
+
+ void set(Number k, void* v) {
+ ASSERT(k >> BITS == 0);
+ const Number i1 = k >> (LEAF_BITS + INTERIOR_BITS);
+ const Number i2 = (k >> LEAF_BITS) & (INTERIOR_LENGTH-1);
+ const Number i3 = k & (LEAF_LENGTH-1);
+ reinterpret_cast<Leaf*>(root_->ptrs[i1]->ptrs[i2])->values[i3] = v;
+ }
+
+ bool Ensure(Number start, size_t n) {
+ for (Number key = start; key <= start + n - 1; ) {
+ const Number i1 = key >> (LEAF_BITS + INTERIOR_BITS);
+ const Number i2 = (key >> LEAF_BITS) & (INTERIOR_LENGTH-1);
+
+ // Make 2nd level node if necessary
+ if (root_->ptrs[i1] == NULL) {
+ Node* n = NewNode();
+ if (n == NULL) return false;
+ root_->ptrs[i1] = n;
+ }
+
+ // Make leaf node if necessary
+ if (root_->ptrs[i1]->ptrs[i2] == NULL) {
+ Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf)));
+ if (leaf == NULL) return false;
+ memset(leaf, 0, sizeof(*leaf));
+ root_->ptrs[i1]->ptrs[i2] = reinterpret_cast<Node*>(leaf);
+ }
+
+ // Advance key past whatever is covered by this leaf node
+ key = ((key >> LEAF_BITS) + 1) << LEAF_BITS;
+ }
+ return true;
+ }
+
+ void PreallocateMoreMemory() {
+ }
+};
+
+#endif // TCMALLOC_PAGEMAP_H__
diff --git a/contrib/deprecated/galloc/spinlock.h b/contrib/deprecated/galloc/spinlock.h
new file mode 100644
index 0000000000..93fcf10e12
--- /dev/null
+++ b/contrib/deprecated/galloc/spinlock.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <util/system/defaults.h>
+
+typedef volatile intptr_t spinlock_t;
+
+#define SPIN_L AllocAcquireAdaptiveLock
+#define SPIN_U AllocReleaseAdaptiveLock
+
+#define _SPINLOCK_INITIALIZER 0
+#define _SPINUNLOCK(_lck) SPIN_U(_lck)
+#define _SPINLOCK(_lck) SPIN_L(_lck)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ void SPIN_L(spinlock_t* lock);
+ void SPIN_U(spinlock_t* lock);
+#if defined(__cplusplus)
+};
+#endif
diff --git a/contrib/deprecated/galloc/stacktrace.h b/contrib/deprecated/galloc/stacktrace.h
new file mode 100644
index 0000000000..ac393ca600
--- /dev/null
+++ b/contrib/deprecated/galloc/stacktrace.h
@@ -0,0 +1,9 @@
+#pragma once
+
+static inline int GetStackTrace(void** /*result*/, int /*max_depth*/, int /*skip_count*/) noexcept {
+ return 0;
+}
+
+static inline bool GetStackExtent(void* /*sp*/, void** /*stack_top*/, void** /*stack_bottom*/ ) noexcept {
+ return false;
+}
diff --git a/contrib/deprecated/galloc/system-alloc.cc b/contrib/deprecated/galloc/system-alloc.cc
new file mode 100644
index 0000000000..6fea9dfa24
--- /dev/null
+++ b/contrib/deprecated/galloc/system-alloc.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// Author: Sanjay Ghemawat
+
+#if defined HAVE_STDINT_H
+#include <stdint.h>
+#elif defined HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <sys/types.h>
+#endif
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "system-alloc.h"
+#include "internal_spinlock.h"
+#include "internal_logging.h"
+#include "commandlineflags.h"
+
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+ #define MAP_ANONYMOUS MAP_ANON
+#endif
+
+// Structure for discovering alignment
+union MemoryAligner {
+ void* p;
+ double d;
+ size_t s;
+};
+
+static SpinLock spinlock = SPINLOCK_INITIALIZER;
+
+// Page size is initialized on demand
+static size_t pagesize = 0;
+
+// Configuration parameters.
+//
+// if use_devmem is true, either use_sbrk or use_mmap must also be true.
+// For 2.2 kernels, it looks like the sbrk address space (500MBish) and
+// the mmap address space (1300MBish) are disjoint, so we need both allocators
+// to get as much virtual memory as possible.
+static bool use_devmem = true;
+static bool use_sbrk = true;
+static bool use_mmap = true;
+
+// Flags to keep us from retrying allocators that failed.
+static bool devmem_failure = false;
+static bool sbrk_failure = false;
+static bool mmap_failure = false;
+
+DEFINE_int32(malloc_devmem_start, 0,
+ "Physical memory starting location in MB for /dev/mem allocation."
+ " Setting this to 0 disables /dev/mem allocation");
+DEFINE_int32(malloc_devmem_limit, 0,
+ "Physical memory limit location in MB for /dev/mem allocation."
+ " Setting this to 0 means no limit.");
+
+#ifdef HAVE_SBRK
+
+static void* TrySbrk(size_t size, size_t alignment) {
+ // sbrk will release memory if passed a negative number, so we do
+ // a strict check here
+ if (static_cast<ptrdiff_t>(size + alignment) < 0) return NULL;
+
+ size = ((size + alignment - 1) / alignment) * alignment;
+ void* result = sbrk(size);
+ if (result == reinterpret_cast<void*>(-1)) {
+ sbrk_failure = true;
+ return NULL;
+ }
+
+ // Is it aligned?
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
+ if ((ptr & (alignment-1)) == 0) return result;
+
+ // Try to get more memory for alignment
+ size_t extra = alignment - (ptr & (alignment-1));
+ void* r2 = sbrk(extra);
+ if (reinterpret_cast<uintptr_t>(r2) == (ptr + size)) {
+ // Contiguous with previous result
+ return reinterpret_cast<void*>(ptr + extra);
+ }
+
+ // Give up and ask for "size + alignment - 1" bytes so
+ // that we can find an aligned region within it.
+ result = sbrk(size + alignment - 1);
+ if (result == reinterpret_cast<void*>(-1)) {
+ sbrk_failure = true;
+ return NULL;
+ }
+ ptr = reinterpret_cast<uintptr_t>(result);
+ if ((ptr & (alignment-1)) != 0) {
+ ptr += alignment - (ptr & (alignment-1));
+ }
+ return reinterpret_cast<void*>(ptr);
+}
+
+#endif /* HAVE_SBRK */
+
+#ifdef HAVE_MMAP
+
+static void* TryMmap(size_t size, size_t alignment) {
+ // Enforce page alignment
+ if (pagesize == 0) pagesize = getpagesize();
+ if (alignment < pagesize) alignment = pagesize;
+ size = ((size + alignment - 1) / alignment) * alignment;
+
+ // Ask for extra memory if alignment > pagesize
+ size_t extra = 0;
+ if (alignment > pagesize) {
+ extra = alignment - pagesize;
+ }
+
+ // Note: size + extra does not overflow since:
+ // size + alignment < (1<<NBITS).
+ // and extra <= alignment
+ // therefore size + extra < (1<<NBITS)
+ void* result = mmap(NULL, size + extra,
+ PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS,
+ -1, 0);
+ if (result == reinterpret_cast<void*>(MAP_FAILED)) {
+ mmap_failure = true;
+ return NULL;
+ }
+
+ // Adjust the return memory so it is aligned
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
+ size_t adjust = 0;
+ if ((ptr & (alignment - 1)) != 0) {
+ adjust = alignment - (ptr & (alignment - 1));
+ }
+
+ // Return the unused memory to the system
+ if (adjust > 0) {
+ munmap(reinterpret_cast<void*>(ptr), adjust);
+ }
+ if (adjust < extra) {
+ munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
+ }
+
+ ptr += adjust;
+ return reinterpret_cast<void*>(ptr);
+}
+
+#endif /* HAVE_MMAP */
+
+static void* TryDevMem(size_t size, size_t alignment) {
+ static bool initialized = false;
+ static off_t physmem_base; // next physical memory address to allocate
+ static off_t physmem_limit; // maximum physical address allowed
+ static int physmem_fd; // file descriptor for /dev/mem
+
+ // Check if we should use /dev/mem allocation. Note that it may take
+ // a while to get this flag initialized, so meanwhile we fall back to
+ // the next allocator. (It looks like 7MB gets allocated before
+ // this flag gets initialized -khr.)
+ if (FLAGS_malloc_devmem_start == 0) {
+ // NOTE: not a devmem_failure - we'd like TCMalloc_SystemAlloc to
+ // try us again next time.
+ return NULL;
+ }
+
+ if (!initialized) {
+ physmem_fd = open("/dev/mem", O_RDWR);
+ if (physmem_fd < 0) {
+ devmem_failure = true;
+ return NULL;
+ }
+ physmem_base = FLAGS_malloc_devmem_start*1024LL*1024LL;
+ physmem_limit = FLAGS_malloc_devmem_limit*1024LL*1024LL;
+ initialized = true;
+ }
+
+ // Enforce page alignment
+ if (pagesize == 0) pagesize = getpagesize();
+ if (alignment < pagesize) alignment = pagesize;
+ size = ((size + alignment - 1) / alignment) * alignment;
+
+ // Ask for extra memory if alignment > pagesize
+ size_t extra = 0;
+ if (alignment > pagesize) {
+ extra = alignment - pagesize;
+ }
+
+ // check to see if we have any memory left
+ if (physmem_limit != 0 &&
+ (off_t(size + extra) > (physmem_limit - physmem_base))) {
+ devmem_failure = true;
+ return NULL;
+ }
+
+ // Note: size + extra does not overflow since:
+ // size + alignment < (1<<NBITS).
+ // and extra <= alignment
+ // therefore size + extra < (1<<NBITS)
+ void *result = mmap(0, size + extra, PROT_WRITE|PROT_READ,
+ MAP_SHARED, physmem_fd, physmem_base);
+ if (result == reinterpret_cast<void*>(MAP_FAILED)) {
+ devmem_failure = true;
+ return NULL;
+ }
+ uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
+
+ // Adjust the return memory so it is aligned
+ size_t adjust = 0;
+ if ((ptr & (alignment - 1)) != 0) {
+ adjust = alignment - (ptr & (alignment - 1));
+ }
+
+ // Return the unused virtual memory to the system
+ if (adjust > 0) {
+ munmap(reinterpret_cast<void*>(ptr), adjust);
+ }
+ if (adjust < extra) {
+ munmap(reinterpret_cast<void*>(ptr + adjust + size), extra - adjust);
+ }
+
+ ptr += adjust;
+ physmem_base += adjust + size;
+
+ return reinterpret_cast<void*>(ptr);
+}
+
+void* TCMalloc_SystemAlloc(size_t size, size_t alignment) {
+ // Discard requests that overflow
+ if (size + alignment < size) return NULL;
+
+ if (TCMallocDebug::level >= TCMallocDebug::kVerbose) {
+ MESSAGE("TCMalloc_SystemAlloc(%" PRIuS ", %" PRIuS")\n",
+ size, alignment);
+ }
+ SpinLockHolder lock_holder(&spinlock);
+
+ // Enforce minimum alignment
+ if (alignment < sizeof(MemoryAligner)) alignment = sizeof(MemoryAligner);
+
+ // Try twice, once avoiding allocators that failed before, and once
+ // more trying all allocators even if they failed before.
+ for (int i = 0; i < 2; i++) {
+ if (use_devmem && !devmem_failure) {
+ void* result = TryDevMem(size, alignment);
+ if (result != NULL) return result;
+ }
+
+#ifdef HAVE_SBRK
+ if (use_sbrk && !sbrk_failure) {
+ void* result = TrySbrk(size, alignment);
+ if (result != NULL) return result;
+ }
+#endif
+
+#ifdef HAVE_MMAP
+ if (use_mmap && !mmap_failure) {
+ void* result = TryMmap(size, alignment);
+ if (result != NULL) return result;
+ }
+#endif
+
+ // nothing worked - reset failure flags and try again
+ devmem_failure = false;
+ sbrk_failure = false;
+ mmap_failure = false;
+ }
+ return NULL;
+}
diff --git a/contrib/deprecated/galloc/system-alloc.h b/contrib/deprecated/galloc/system-alloc.h
new file mode 100644
index 0000000000..c72d5e00be
--- /dev/null
+++ b/contrib/deprecated/galloc/system-alloc.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// Author: Sanjay Ghemawat
+//
+// Routine that uses sbrk/mmap to allocate memory from the system.
+// Useful for implementing malloc.
+
+#ifndef TCMALLOC_SYSTEM_ALLOC_H__
+#define TCMALLOC_SYSTEM_ALLOC_H__
+
+// REQUIRES: "alignment" is a power of two or "0" to indicate default alignment
+//
+// Allocate and return "N" bytes of zeroed memory. The returned
+// pointer is a multiple of "alignment" if non-zero. Returns NULL
+// when out of memory.
+extern void* TCMalloc_SystemAlloc(size_t bytes, size_t alignment = 0);
+
+#endif /* TCMALLOC_SYSTEM_ALLOC_H__ */
diff --git a/contrib/deprecated/galloc/tcmalloc.cc b/contrib/deprecated/galloc/tcmalloc.cc
new file mode 100644
index 0000000000..a980effa8b
--- /dev/null
+++ b/contrib/deprecated/galloc/tcmalloc.cc
@@ -0,0 +1,2661 @@
+// Copyright (c) 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+// ---
+// Author: Sanjay Ghemawat <opensource@google.com>
+//
+// A malloc that uses a per-thread cache to satisfy small malloc requests.
+// (The time for malloc/free of a small object drops from 300 ns to 50 ns.)
+//
+// See doc/tcmalloc.html for a high-level
+// description of how this malloc works.
+//
+// SYNCHRONIZATION
+// 1. The thread-specific lists are accessed without acquiring any locks.
+// This is safe because each such list is only accessed by one thread.
+// 2. We have a lock per central free-list, and hold it while manipulating
+// the central free list for a particular size.
+// 3. The central page allocator is protected by "pageheap_lock".
+// 4. The pagemap (which maps from page-number to descriptor),
+// can be read without holding any locks, and written while holding
+// the "pageheap_lock".
+//
+// This multi-threaded access to the pagemap is safe for fairly
+// subtle reasons. We basically assume that when an object X is
+// allocated by thread A and deallocated by thread B, there must
+// have been appropriate synchronization in the handoff of object
+// X from thread A to thread B.
+//
+// TODO: Bias reclamation to larger addresses
+// TODO: implement mallinfo/mallopt
+// TODO: Better testing
+// TODO: Return memory to system
+//
+// 9/28/2003 (new page-level allocator replaces ptmalloc2):
+// * malloc/free of small objects goes from ~300 ns to ~50 ns.
+// * allocation of a reasonably complicated struct
+// goes from about 1100 ns to about 300 ns.
+
+#include <new>
+
+#include <stdio.h>
+#include <stddef.h>
+
+#if defined(_linux_)
+ #include <malloc.h>
+#endif
+
+#if defined(_darwin_)
+ #include <malloc/malloc.h>
+#endif
+
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "commandlineflags.h"
+#include "malloc_hook.h"
+#include "malloc_extension.h"
+#include "stacktrace.h"
+#include "internal_logging.h"
+#include "internal_spinlock.h"
+#include "pagemap.h"
+#include "system-alloc.h"
+
+#include <util/system/tls.h>
+
+#if defined(Y_HAVE_FAST_POD_TLS)
+Y_POD_STATIC_THREAD(void*) my_heap((void*)0);
+#endif
+
+static inline void SetHeap(pthread_key_t key, const void* pointer) {
+ pthread_setspecific(key, pointer);
+
+#if defined(Y_HAVE_FAST_POD_TLS)
+ my_heap = (void*)pointer;
+#endif
+}
+
+static inline void* GetHeap(pthread_key_t key) {
+#if defined(Y_HAVE_FAST_POD_TLS)
+ return my_heap;
+#else
+ return pthread_getspecific(key);
+#endif
+}
+
+//-------------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------------
+
+// Not all possible combinations of the following parameters make
+// sense. In particular, if kMaxSize increases, you may have to
+// increase kNumClasses as well.
+static const size_t kPageShift = 12;
+static const size_t kPageSize = 1 << kPageShift;
+static const size_t kMaxSize = 8u * kPageSize;
+static const size_t kAlignShift = 4;
+static const size_t kAlignment = 1 << kAlignShift;
+static const size_t kNumClasses = 170;
+
+// Allocates a big block of memory for the pagemap once we reach more than
+// 128MB
+static const size_t kPageMapBigAllocationThreshold = 128 << 20;
+
+// Minimum number of pages to fetch from system at a time. Must be
+// significantly bigger than kBlockSize to amortize system-call
+// overhead, and also to reduce external fragementation. Also, we
+// should keep this value big because various incarnations of Linux
+// have small limits on the number of mmap() regions per
+// address-space.
+static const size_t kMinSystemAlloc = 1 << (20 - kPageShift);
+
+// Number of objects to move between a per-thread list and a central
+// list in one shot. We want this to be not too small so we can
+// amortize the lock overhead for accessing the central list. Making
+// it too big may temporarily cause unnecessary memory wastage in the
+// per-thread free list until the scavenger cleans up the list.
+static int num_objects_to_move[kNumClasses];
+
+// Maximum length we allow a per-thread free-list to have before we
+// move objects from it into the corresponding central free-list. We
+// want this big to avoid locking the central free-list too often. It
+// should not hurt to make this list somewhat big because the
+// scavenging code will shrink it down when its contents are not in use.
+static const int kMaxFreeListLength = 256;
+
+// Lower and upper bounds on the per-thread cache sizes
+static const size_t kMinThreadCacheSize = kMaxSize * 2;
+static const size_t kMaxThreadCacheSize = 2 << 20;
+
+// Default bound on the total amount of thread caches
+static const size_t kDefaultOverallThreadCacheSize = 16 << 20;
+
+// For all span-lengths < kMaxPages we keep an exact-size list.
+// REQUIRED: kMaxPages >= kMinSystemAlloc;
+static const size_t kMaxPages = kMinSystemAlloc;
+
+/* The smallest prime > 2^n */
+static int primes_list[] = {
+ // Small values might cause high rates of sampling
+ // and hence commented out.
+ // 2, 5, 11, 17, 37, 67, 131, 257,
+ // 521, 1031, 2053, 4099, 8209, 16411,
+ 32771, 65537, 131101, 262147, 524309, 1048583,
+ 2097169, 4194319, 8388617, 16777259, 33554467 };
+
+// Twice the approximate gap between sampling actions.
+// I.e., we take one sample approximately once every
+// tcmalloc_sample_parameter/2
+// bytes of allocation, i.e., ~ once every 128KB.
+// Must be a prime number.
+DEFINE_int64(tcmalloc_sample_parameter, 262147,
+ "Twice the approximate gap between sampling actions."
+ " Must be a prime number. Otherwise will be rounded up to a "
+ " larger prime number");
+static size_t sample_period = 262147;
+// Protects sample_period above
+static SpinLock sample_period_lock = SPINLOCK_INITIALIZER;
+
+//-------------------------------------------------------------------
+// Mapping from size to size_class and vice versa
+//-------------------------------------------------------------------
+
+// A pair of arrays we use for implementing the mapping from a size to
+// its size class. Indexed by "floor(lg(size))".
+static const int kSizeBits = 8 * sizeof(size_t);
+static unsigned char size_base[kSizeBits];
+static unsigned char size_shift[kSizeBits];
+
+// Mapping from size class to size
+static size_t class_to_size[kNumClasses];
+
+// Mapping from size class to number of pages to allocate at a time
+static size_t class_to_pages[kNumClasses];
+
+
+
+// TransferCache is used to cache transfers of num_objects_to_move[size_class]
+// back and forth between thread caches and the central cache for a given size
+// class.
+struct TCEntry {
+ void *head; // Head of chain of objects.
+ void *tail; // Tail of chain of objects.
+};
+// A central cache freelist can have anywhere from 0 to kNumTransferEntries
+// slots to put link list chains into. To keep memory usage bounded the total
+// number of TCEntries across size classes is fixed. Currently each size
+// class is initially given one TCEntry which also means that the maximum any
+// one class can have is kNumClasses.
+static const int kNumTransferEntries = kNumClasses;
+
+// Return floor(log2(n)) for n > 0.
+#if (defined __i386__ || defined __x86_64__) && defined __GNUC__
+static inline int LgFloor(size_t n) {
+ // "ro" for the input spec means the input can come from either a
+ // register ("r") or offsetable memory ("o").
+ size_t result;
+ __asm__("bsr %1, %0"
+ : "=r" (result) // Output spec
+ : "ro" (n) // Input spec
+ : "cc" // Clobbers condition-codes
+ );
+ return result;
+}
+#else
+// Note: the following only works for "n"s that fit in 32-bits, but
+// that is fine since we only use it for small sizes.
+static inline int LgFloor(size_t n) {
+ int log = 0;
+ for (int i = 4; i >= 0; --i) {
+ int shift = (1 << i);
+ size_t x = n >> shift;
+ if (x != 0) {
+ n = x;
+ log += shift;
+ }
+ }
+ ASSERT(n == 1);
+ return log;
+}
+#endif
+
+
+// Some very basic linked list functions for dealing with using void * as
+// storage.
+
+static inline void *SLL_Next(void *t) {
+ return *(reinterpret_cast<void**>(t));
+}
+
+static inline void SLL_SetNext(void *t, void *n) {
+ *(reinterpret_cast<void**>(t)) = n;
+}
+
+static inline void SLL_Push(void **list, void *element) {
+ SLL_SetNext(element, *list);
+ *list = element;
+}
+
+static inline void *SLL_Pop(void **list) {
+ void *result = *list;
+ *list = SLL_Next(*list);
+ return result;
+}
+
+
+// Remove N elements from a linked list to which head points. head will be
+// modified to point to the new head. start and end will point to the first
+// and last nodes of the range. Note that end will point to NULL after this
+// function is called.
+static inline void SLL_PopRange(void **head, int N, void **start, void **end) {
+ if (N == 0) {
+ *start = NULL;
+ *end = NULL;
+ return;
+ }
+
+ void *tmp = *head;
+ for (int i = 1; i < N; ++i) {
+ tmp = SLL_Next(tmp);
+ }
+
+ *start = *head;
+ *end = tmp;
+ *head = SLL_Next(tmp);
+ // Unlink range from list.
+ SLL_SetNext(tmp, NULL);
+}
+
+static inline void SLL_PushRange(void **head, void *start, void *end) {
+ if (!start) return;
+ SLL_SetNext(end, *head);
+ *head = start;
+}
+
+static inline size_t SLL_Size(void *head) {
+ int count = 0;
+ while (head) {
+ count++;
+ head = SLL_Next(head);
+ }
+ return count;
+}
+
+// Setup helper functions.
+
+static inline size_t SizeClass(size_t size) {
+ if (size == 0) size = 1;
+ const size_t lg = LgFloor(size);
+ const size_t align = size_shift[lg];
+ return static_cast<size_t>(size_base[lg]) + ((size-1) >> align);
+}
+
+// Get the byte-size for a specified class
+static inline size_t ByteSizeForClass(size_t cl) {
+ return class_to_size[cl];
+}
+
+
+static int NumMoveSize(size_t size) {
+ if (size == 0) return 0;
+ // Use approx 64k transfers between thread and central caches.
+ int num = static_cast<int>(64.0 * 1024.0 / size);
+ if (num < 2) num = 2;
+ // Clamp well below kMaxFreeListLength to avoid ping pong between central
+ // and thread caches.
+ if (num > static_cast<int>(0.8 * kMaxFreeListLength))
+ num = static_cast<int>(0.8 * kMaxFreeListLength);
+
+ // Also, avoid bringing in too many objects into small object free
+ // lists. There are lots of such lists, and if we allow each one to
+ // fetch too many at a time, we end up having to scavenge too often
+ // (especially when there are lots of threads and each thread gets a
+ // small allowance for its thread cache).
+ //
+ // TODO: Make thread cache free list sizes dynamic so that we do not
+ // have to equally divide a fixed resource amongst lots of threads.
+ if (num > 32) num = 32;
+
+ return num;
+}
+
+// Initialize the mapping arrays
+static void InitSizeClasses() {
+ // Special initialization for small sizes
+ for (unsigned int lg = 0; lg < kAlignShift; lg++) {
+ size_base[lg] = 1;
+ size_shift[lg] = kAlignShift;
+ }
+
+ int next_class = 1;
+ int alignshift = kAlignShift;
+ int last_lg = -1;
+ for (size_t size = kAlignment; size <= kMaxSize; size += (1 << alignshift)) {
+ int lg = LgFloor(size);
+ if (lg > last_lg) {
+ // Increase alignment every so often.
+ //
+ // Since we double the alignment every time size doubles and
+ // size >= 128, this means that space wasted due to alignment is
+ // at most 16/128 i.e., 12.5%. Plus we cap the alignment at 256
+ // bytes, so the space wasted as a percentage starts falling for
+ // sizes > 2K.
+ if ((lg >= 7) && (alignshift < 8)) {
+ alignshift++;
+ }
+ size_base[lg] = next_class - ((size-1) >> alignshift);
+ size_shift[lg] = alignshift;
+ }
+
+ class_to_size[next_class] = size;
+ last_lg = lg;
+
+ next_class++;
+ }
+ if ((size_t)next_class >= kNumClasses) {
+ MESSAGE("used up too many size classes: %d\n", next_class);
+ abort();
+ }
+
+ // Initialize the number of pages we should allocate to split into
+ // small objects for a given class.
+ for (size_t cl = 1; cl < (size_t)next_class; cl++) {
+ // Allocate enough pages so leftover is less than 1/8 of total.
+ // This bounds wasted space to at most 12.5%.
+ size_t psize = kPageSize;
+ const size_t s = class_to_size[cl];
+ while ((psize % s) > (psize >> 3)) {
+ psize += kPageSize;
+ }
+ class_to_pages[cl] = psize >> kPageShift;
+ }
+
+ // Double-check sizes just to be safe
+ for (size_t size = 0; size <= kMaxSize; size++) {
+ const unsigned int sc = SizeClass(size);
+ if (sc == 0) {
+ MESSAGE("Bad size class %u for %" PRIuS "\n", sc, size);
+ abort();
+ }
+ if (sc > 1 && size <= class_to_size[sc-1]) {
+ MESSAGE("Allocating unnecessarily large class %u for %" PRIuS
+ "\n", sc, size);
+ abort();
+ }
+ if (sc >= kNumClasses) {
+ MESSAGE("Bad size class %u for %" PRIuS "\n", sc, size);
+ abort();
+ }
+ const size_t s = class_to_size[sc];
+ if (size > s) {
+ MESSAGE("Bad size %" PRIuS " for %" PRIuS " (sc = %u)\n", s, size, sc);
+ abort();
+ }
+ if (s == 0) {
+ MESSAGE("Bad size %" PRIuS " for %" PRIuS " (sc = %u)\n", s, size, sc);
+ abort();
+ }
+ }
+
+ // Initialize the num_objects_to_move array.
+ for (size_t cl = 1; cl < kNumClasses; ++cl) {
+ num_objects_to_move[cl] = NumMoveSize(ByteSizeForClass(cl));
+ }
+}
+
+// -------------------------------------------------------------------------
+// Simple allocator for objects of a specified type. External locking
+// is required before accessing one of these objects.
+// -------------------------------------------------------------------------
+
+// Metadata allocator -- keeps stats about how many bytes allocated
+static uint64_t metadata_system_bytes = 0;
+static void* MetaDataAlloc(size_t bytes) {
+ void* result = TCMalloc_SystemAlloc(bytes);
+ if (result != NULL) {
+ metadata_system_bytes += bytes;
+ }
+ return result;
+}
+
+template <class T>
+class PageHeapAllocator {
+ private:
+ // How much to allocate from system at a time
+ static const int kAllocIncrement = 128 << 10;
+
+ // Aligned size of T
+ static const size_t kAlignedSize
+ = (((sizeof(T) + kAlignment - 1) / kAlignment) * kAlignment);
+
+ // Free area from which to carve new objects
+ char* free_area_;
+ size_t free_avail_;
+
+ // Free list of already carved objects
+ void* free_list_;
+
+ // Number of allocated but unfreed objects
+ int inuse_;
+
+ public:
+ void Init() {
+ ASSERT(kAlignedSize <= kAllocIncrement);
+ inuse_ = 0;
+ free_area_ = NULL;
+ free_avail_ = 0;
+ free_list_ = NULL;
+ // Reserve some space at the beginning to avoid fragmentation.
+ Delete(New());
+ }
+
+ T* New() {
+ // Consult free list
+ void* result;
+ if (free_list_ != NULL) {
+ result = free_list_;
+ free_list_ = *(reinterpret_cast<void**>(result));
+ } else {
+ if (free_avail_ < kAlignedSize) {
+ // Need more room
+ free_area_ = reinterpret_cast<char*>(MetaDataAlloc(kAllocIncrement));
+ if (free_area_ == NULL) abort();
+ free_avail_ = kAllocIncrement;
+ }
+ result = free_area_;
+ free_area_ += kAlignedSize;
+ free_avail_ -= kAlignedSize;
+ }
+ inuse_++;
+ return reinterpret_cast<T*>(result);
+ }
+
+ void Delete(T* p) {
+ *(reinterpret_cast<void**>(p)) = free_list_;
+ free_list_ = p;
+ inuse_--;
+ }
+
+ int inuse() const { return inuse_; }
+};
+
+// -------------------------------------------------------------------------
+// Span - a contiguous run of pages
+// -------------------------------------------------------------------------
+
+// Type that can hold a page number
+typedef uintptr_t PageID;
+
+// Type that can hold the length of a run of pages
+typedef uintptr_t Length;
+
+// Convert byte size into pages
+static inline Length pages(size_t bytes) {
+ return ((bytes + kPageSize - 1) >> kPageShift);
+}
+
+// Convert a user size into the number of bytes that will actually be
+// allocated
+static size_t AllocationSize(size_t bytes) {
+ if (bytes > kMaxSize) {
+ // Large object: we allocate an integral number of pages
+ return pages(bytes) << kPageShift;
+ } else {
+ // Small object: find the size class to which it belongs
+ return ByteSizeForClass(SizeClass(bytes));
+ }
+}
+
+// Information kept for a span (a contiguous run of pages).
+struct Span {
+ PageID start; // Starting page number
+ Length length; // Number of pages in span
+ Span* next; // Used when in link list
+ Span* prev; // Used when in link list
+ void* objects; // Linked list of free objects
+ unsigned int free : 1; // Is the span free
+ unsigned int sample : 1; // Sampled object?
+ unsigned int sizeclass : 8; // Size-class for small objects (or 0)
+ unsigned int refcount : 11; // Number of non-free objects
+
+#undef SPAN_HISTORY
+#ifdef SPAN_HISTORY
+ // For debugging, we can keep a log events per span
+ int nexthistory;
+ char history[64];
+ int value[64];
+#endif
+};
+
+#ifdef SPAN_HISTORY
+void Event(Span* span, char op, int v = 0) {
+ span->history[span->nexthistory] = op;
+ span->value[span->nexthistory] = v;
+ span->nexthistory++;
+ if (span->nexthistory == sizeof(span->history)) span->nexthistory = 0;
+}
+#else
+#define Event(s,o,v) ((void) 0)
+#endif
+
+// Allocator/deallocator for spans
+static PageHeapAllocator<Span> span_allocator;
+static Span* NewSpan(PageID p, Length len) {
+ Span* result = span_allocator.New();
+ memset(result, 0, sizeof(*result));
+ result->start = p;
+ result->length = len;
+#ifdef SPAN_HISTORY
+ result->nexthistory = 0;
+#endif
+ return result;
+}
+
+static void DeleteSpan(Span* span) {
+#ifndef NDEBUG
+ // In debug mode, trash the contents of deleted Spans
+ memset(span, 0x3f, sizeof(*span));
+#endif
+ span_allocator.Delete(span);
+}
+
+// -------------------------------------------------------------------------
+// Doubly linked list of spans.
+// -------------------------------------------------------------------------
+
+static void DLL_Init(Span* list) {
+ list->next = list;
+ list->prev = list;
+}
+
+static void DLL_Remove(Span* span) {
+ span->prev->next = span->next;
+ span->next->prev = span->prev;
+ span->prev = NULL;
+ span->next = NULL;
+}
+
+static inline bool DLL_IsEmpty(const Span* list) {
+ return list->next == list;
+}
+
+static unsigned int DLL_Length(const Span* list) {
+ unsigned int result = 0;
+ for (Span* s = list->next; s != list; s = s->next) {
+ result++;
+ }
+ return result;
+}
+
+#if 0 /* Not needed at the moment -- causes compiler warnings if not used */
+static void DLL_Print(const char* label, const Span* list) {
+ MESSAGE("%-10s %p:", label, list);
+ for (const Span* s = list->next; s != list; s = s->next) {
+ MESSAGE(" <%p,%u,%u>", s, s->start, s->length);
+ }
+ MESSAGE("\n");
+}
+#endif
+
+static void DLL_Prepend(Span* list, Span* span) {
+ ASSERT(span->next == NULL);
+ ASSERT(span->prev == NULL);
+ span->next = list->next;
+ span->prev = list;
+ list->next->prev = span;
+ list->next = span;
+}
+
+static void DLL_InsertOrdered(Span* list, Span* span) {
+ ASSERT(span->next == NULL);
+ ASSERT(span->prev == NULL);
+ // Look for appropriate place to insert
+ Span* x = list;
+ while ((x->next != list) && (x->next->start < span->start)) {
+ x = x->next;
+ }
+ span->next = x->next;
+ span->prev = x;
+ x->next->prev = span;
+ x->next = span;
+}
+
+// -------------------------------------------------------------------------
+// Stack traces kept for sampled allocations
+// The following state is protected by pageheap_lock_.
+// -------------------------------------------------------------------------
+
+static const int kMaxStackDepth = 31;
+struct StackTrace {
+ uintptr_t size; // Size of object
+ int depth; // Number of PC values stored in array below
+ void* stack[kMaxStackDepth];
+};
+static PageHeapAllocator<StackTrace> stacktrace_allocator;
+static Span sampled_objects;
+
+// Linked list of stack traces recorded every time we allocated memory
+// from the system. Useful for finding allocation sites that cause
+// increase in the footprint of the system. The linked list pointer
+// is stored in trace->stack[kMaxStackDepth-1].
+static StackTrace* growth_stacks = NULL;
+
+// -------------------------------------------------------------------------
+// Map from page-id to per-page data
+// -------------------------------------------------------------------------
+
+// We use PageMap2<> for 32-bit and PageMap3<> for 64-bit machines.
+
+// Selector class -- general selector uses 3-level map
+template <int BITS> class MapSelector {
+ public:
+ typedef TCMalloc_PageMap3<BITS-kPageShift> Type;
+};
+
+// A two-level map for 32-bit machines
+template <> class MapSelector<32> {
+ public:
+ typedef TCMalloc_PageMap2<32-kPageShift> Type;
+};
+
+// -------------------------------------------------------------------------
+// Page-level allocator
+// * Eager coalescing
+//
+// Heap for page-level allocation. We allow allocating and freeing a
+// contiguous runs of pages (called a "span").
+// -------------------------------------------------------------------------
+
+class TCMalloc_PageHeap {
+ public:
+ TCMalloc_PageHeap();
+
+ // Allocate a run of "n" pages. Returns zero if out of memory.
+ // Caller should not pass "n == 0" -- instead, n should have
+ // been rounded up already.
+ Span* New(Length n);
+
+ // Delete the span "[p, p+n-1]".
+ // REQUIRES: span was returned by earlier call to New() and
+ // has not yet been deleted.
+ void Delete(Span* span);
+
+ // Mark an allocated span as being used for small objects of the
+ // specified size-class.
+ // REQUIRES: span was returned by an earlier call to New()
+ // and has not yet been deleted.
+ void RegisterSizeClass(Span* span, size_t sc);
+
+ // Split an allocated span into two spans: one of length "n" pages
+ // followed by another span of length "span->length - n" pages.
+ // Modifies "*span" to point to the first span of length "n" pages.
+ // Returns a pointer to the second span.
+ //
+ // REQUIRES: "0 < n < span->length"
+ // REQUIRES: !span->free
+ // REQUIRES: span->sizeclass == 0
+ Span* Split(Span* span, Length n);
+
+ // Return the descriptor for the specified page.
+ inline Span* GetDescriptor(PageID p) const {
+ return reinterpret_cast<Span*>(pagemap_.get(p));
+ }
+
+ // Dump state to stderr
+ void Dump(TCMalloc_Printer* out);
+
+ // Return number of bytes allocated from system
+ inline uint64_t SystemBytes() const { return system_bytes_; }
+
+ // Return number of free bytes in heap
+ uint64_t FreeBytes() const {
+ return (static_cast<uint64_t>(free_pages_) << kPageShift);
+ }
+
+ bool Check();
+ bool CheckList(Span* list, Length min_pages, Length max_pages);
+
+ private:
+ // Pick the appropriate map type based on pointer size
+ typedef MapSelector<8*sizeof(uintptr_t)>::Type PageMap;
+ PageMap pagemap_;
+
+ // List of free spans of length >= kMaxPages
+ Span large_;
+
+ // Array mapping from span length to a doubly linked list of free spans
+ Span free_[kMaxPages];
+
+ // Number of pages kept in free lists
+ uintptr_t free_pages_;
+
+ // Bytes allocated from system
+ uint64_t system_bytes_;
+
+ bool GrowHeap(Length n);
+
+ // REQUIRES span->length >= n
+ // Remove span from its free list, and move any leftover part of
+ // span into appropriate free lists. Also update "span" to have
+ // length exactly "n" and mark it as non-free so it can be returned
+ // to the client.
+ void Carve(Span* span, Length n);
+
+ void RecordSpan(Span* span) {
+ pagemap_.set(span->start, span);
+ if (span->length > 1) {
+ pagemap_.set(span->start + span->length - 1, span);
+ }
+ }
+};
+
+TCMalloc_PageHeap::TCMalloc_PageHeap() : pagemap_(MetaDataAlloc),
+ free_pages_(0),
+ system_bytes_(0) {
+ DLL_Init(&large_);
+ for (unsigned int i = 0; i < kMaxPages; i++) {
+ DLL_Init(&free_[i]);
+ }
+}
+
+Span* TCMalloc_PageHeap::New(Length n) {
+ ASSERT(Check());
+
+ // n==0 occurs iff pages() overflowed when we added kPageSize-1 to n
+ if (n == 0) return NULL;
+
+ // Find first size >= n that has a non-empty list
+ for (Length s = n; s < kMaxPages; s++) {
+ if (!DLL_IsEmpty(&free_[s])) {
+ Span* result = free_[s].next;
+ Carve(result, n);
+ ASSERT(Check());
+ free_pages_ -= n;
+ return result;
+ }
+ }
+
+ // Look in large list. If we first do not find something, we try to
+ // grow the heap and try again.
+ for (int i = 0; i < 2; i++) {
+ // find the best span (closest to n in size)
+ Span *best = NULL;
+ for (Span* span = large_.next; span != &large_; span = span->next) {
+ if (span->length >= n &&
+ (best == NULL || span->length < best->length)) {
+ best = span;
+ }
+ }
+ if (best != NULL) {
+ Carve(best, n);
+ ASSERT(Check());
+ free_pages_ -= n;
+ return best;
+ }
+ if (i == 0) {
+ // Nothing suitable in large list. Grow the heap and look again.
+ if (!GrowHeap(n)) {
+ ASSERT(Check());
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+Span* TCMalloc_PageHeap::Split(Span* span, Length n) {
+ ASSERT(0 < n);
+ ASSERT(n < span->length);
+ ASSERT(!span->free);
+ ASSERT(span->sizeclass == 0);
+ Event(span, 'T', n);
+
+ const int extra = span->length - n;
+ Span* leftover = NewSpan(span->start + n, extra);
+ Event(leftover, 'U', extra);
+ RecordSpan(leftover);
+ pagemap_.set(span->start + n - 1, span); // Update map from pageid to span
+ span->length = n;
+
+ return leftover;
+}
+
+void TCMalloc_PageHeap::Carve(Span* span, Length n) {
+ ASSERT(n > 0);
+ DLL_Remove(span);
+ span->free = 0;
+ Event(span, 'A', n);
+
+ const int extra = span->length - n;
+ ASSERT(extra >= 0);
+ if (extra > 0) {
+ Span* leftover = NewSpan(span->start + n, extra);
+ leftover->free = 1;
+ Event(leftover, 'S', extra);
+ RecordSpan(leftover);
+ if ((unsigned int)extra < kMaxPages) {
+ DLL_Prepend(&free_[extra], leftover);
+ } else {
+ DLL_InsertOrdered(&large_, leftover);
+ }
+ span->length = n;
+ pagemap_.set(span->start + n - 1, span);
+ }
+}
+
+void TCMalloc_PageHeap::Delete(Span* span) {
+ ASSERT(Check());
+ ASSERT(!span->free);
+ ASSERT(span->length > 0);
+ ASSERT(GetDescriptor(span->start) == span);
+ ASSERT(GetDescriptor(span->start + span->length - 1) == span);
+ span->sizeclass = 0;
+ span->sample = 0;
+
+ // Coalesce -- we guarantee that "p" != 0, so no bounds checking
+ // necessary. We do not bother resetting the stale pagemap
+ // entries for the pieces we are merging together because we only
+ // care about the pagemap entries for the boundaries.
+ const PageID p = span->start;
+ const Length n = span->length;
+ Span* prev = GetDescriptor(p-1);
+ if (prev != NULL && prev->free) {
+ // Merge preceding span into this span
+ ASSERT(prev->start + prev->length == p);
+ const Length len = prev->length;
+ DLL_Remove(prev);
+ DeleteSpan(prev);
+ span->start -= len;
+ span->length += len;
+ pagemap_.set(span->start, span);
+ Event(span, 'L', len);
+ }
+ Span* next = GetDescriptor(p+n);
+ if (next != NULL && next->free) {
+ // Merge next span into this span
+ ASSERT(next->start == p+n);
+ const Length len = next->length;
+ DLL_Remove(next);
+ DeleteSpan(next);
+ span->length += len;
+ pagemap_.set(span->start + span->length - 1, span);
+ Event(span, 'R', len);
+ }
+
+ Event(span, 'D', span->length);
+ span->free = 1;
+ if (span->length < kMaxPages) {
+ DLL_Prepend(&free_[span->length], span);
+ } else {
+ DLL_InsertOrdered(&large_, span);
+ }
+ free_pages_ += n;
+
+ ASSERT(Check());
+}
+
+void TCMalloc_PageHeap::RegisterSizeClass(Span* span, size_t sc) {
+ // Associate span object with all interior pages as well
+ ASSERT(!span->free);
+ ASSERT(GetDescriptor(span->start) == span);
+ ASSERT(GetDescriptor(span->start+span->length-1) == span);
+ Event(span, 'C', sc);
+ span->sizeclass = sc;
+ for (Length i = 1; i < span->length-1; i++) {
+ pagemap_.set(span->start+i, span);
+ }
+}
+
+void TCMalloc_PageHeap::Dump(TCMalloc_Printer* out) {
+ int nonempty_sizes = 0;
+ for (unsigned int s = 0; s < kMaxPages; s++) {
+ if (!DLL_IsEmpty(&free_[s])) nonempty_sizes++;
+ }
+ out->printf("------------------------------------------------\n");
+ out->printf("PageHeap: %d sizes; %6.1f MB free\n", nonempty_sizes,
+ (static_cast<double>(free_pages_) * kPageSize) / 1048576.0);
+ out->printf("------------------------------------------------\n");
+ uint64_t cumulative = 0;
+ for (unsigned int s = 0; s < kMaxPages; s++) {
+ if (!DLL_IsEmpty(&free_[s])) {
+ const unsigned int list_length = DLL_Length(&free_[s]);
+ uint64_t s_pages = s * list_length;
+ cumulative += s_pages;
+ out->printf("%6u pages * %6u spans ~ %6.1f MB; %6.1f MB cum\n",
+ s, list_length,
+ (s_pages << kPageShift) / 1048576.0,
+ (cumulative << kPageShift) / 1048576.0);
+ }
+ }
+
+ uint64_t large_pages = 0;
+ unsigned int large_spans = 0;
+ for (Span* s = large_.next; s != &large_; s = s->next) {
+ out->printf(" [ %6" PRIuS " pages ]\n", s->length);
+ large_pages += s->length;
+ large_spans++;
+ }
+ cumulative += large_pages;
+ out->printf(">255 large * %6u spans ~ %6.1f MB; %6.1f MB cum\n",
+ large_spans,
+ (large_pages << kPageShift) / 1048576.0,
+ (cumulative << kPageShift) / 1048576.0);
+}
+
+static void RecordGrowth(size_t growth) {
+ StackTrace* t = stacktrace_allocator.New();
+ t->depth = GetStackTrace(t->stack, kMaxStackDepth-1, 3);
+ t->size = growth;
+ t->stack[kMaxStackDepth-1] = reinterpret_cast<void*>(growth_stacks);
+ growth_stacks = t;
+}
+
+bool TCMalloc_PageHeap::GrowHeap(Length n) {
+ ASSERT(kMaxPages >= kMinSystemAlloc);
+ Length ask = (n>kMinSystemAlloc) ? n : static_cast<Length>(kMinSystemAlloc);
+ void* ptr = TCMalloc_SystemAlloc(ask << kPageShift, kPageSize);
+ if (ptr == NULL) {
+ if (n < ask) {
+ // Try growing just "n" pages
+ ask = n;
+ ptr = TCMalloc_SystemAlloc(ask << kPageShift, kPageSize);
+ }
+ if (ptr == NULL) return false;
+ }
+ RecordGrowth(ask << kPageShift);
+
+ uint64_t old_system_bytes = system_bytes_;
+ system_bytes_ += (ask << kPageShift);
+ const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
+ ASSERT(p > 0);
+
+ // If we have already a lot of pages allocated, just pre allocate a bunch of
+ // memory for the page map. This prevents fragmentation by pagemap metadata
+ // when a program keeps allocating and freeing large blocks.
+
+ if (old_system_bytes < kPageMapBigAllocationThreshold
+ && system_bytes_ >= kPageMapBigAllocationThreshold) {
+ pagemap_.PreallocateMoreMemory();
+ }
+
+ // Make sure pagemap_ has entries for all of the new pages.
+ // Plus ensure one before and one after so coalescing code
+ // does not need bounds-checking.
+ if (pagemap_.Ensure(p-1, ask+2)) {
+ // Pretend the new area is allocated and then Delete() it to
+ // cause any necessary coalescing to occur.
+ //
+ // We do not adjust free_pages_ here since Delete() will do it for us.
+ Span* span = NewSpan(p, ask);
+ RecordSpan(span);
+ Delete(span);
+ ASSERT(Check());
+ return true;
+ } else {
+ // We could not allocate memory within "pagemap_"
+ // TODO: Once we can return memory to the system, return the new span
+ return false;
+ }
+}
+
+bool TCMalloc_PageHeap::Check() {
+ ASSERT(free_[0].next == &free_[0]);
+ CheckList(&large_, kMaxPages, 1000000000);
+ for (Length s = 1; s < kMaxPages; s++) {
+ CheckList(&free_[s], s, s);
+ }
+ return true;
+}
+
+bool TCMalloc_PageHeap::CheckList(Span* list, Length min_pages, Length max_pages) {
+ for (Span* s = list->next; s != list; s = s->next) {
+ CHECK_CONDITION(s->free);
+ CHECK_CONDITION(s->length >= min_pages);
+ CHECK_CONDITION(s->length <= max_pages);
+ CHECK_CONDITION(GetDescriptor(s->start) == s);
+ CHECK_CONDITION(GetDescriptor(s->start+s->length-1) == s);
+ }
+ return true;
+}
+
+//-------------------------------------------------------------------
+// Free list
+//-------------------------------------------------------------------
+
+class TCMalloc_ThreadCache_FreeList {
+ private:
+ void* list_; // Linked list of nodes
+ uint16_t length_; // Current length
+ uint16_t lowater_; // Low water mark for list length
+
+ public:
+ void Init() {
+ list_ = NULL;
+ length_ = 0;
+ lowater_ = 0;
+ }
+
+ // Return current length of list
+ int length() const {
+ return length_;
+ }
+
+ // Is list empty?
+ bool empty() const {
+ return list_ == NULL;
+ }
+
+ // Low-water mark management
+ int lowwatermark() const { return lowater_; }
+ void clear_lowwatermark() { lowater_ = length_; }
+
+ void Push(void* ptr) {
+ SLL_Push(&list_, ptr);
+ length_++;
+ }
+
+ void* Pop() {
+ ASSERT(list_ != NULL);
+ length_--;
+ if (length_ < lowater_) lowater_ = length_;
+ return SLL_Pop(&list_);
+ }
+
+ void PushRange(int N, void *start, void *end) {
+ SLL_PushRange(&list_, start, end);
+ length_ += N;
+ }
+
+ void PopRange(int N, void **start, void **end) {
+ SLL_PopRange(&list_, N, start, end);
+ ASSERT(length_ >= N);
+ length_ -= N;
+ if (length_ < lowater_) lowater_ = length_;
+ }
+};
+
+//-------------------------------------------------------------------
+// Data kept per thread
+//-------------------------------------------------------------------
+
+class TCMalloc_ThreadCache {
+ private:
+ typedef TCMalloc_ThreadCache_FreeList FreeList;
+
+ size_t size_; // Combined size of data
+ pthread_t tid_; // Which thread owns it
+ bool in_setspecific_; // In call to pthread_setspecific?
+ FreeList list_[kNumClasses]; // Array indexed by size-class
+
+ // We sample allocations, biased by the size of the allocation
+ uint32_t rnd_; // Cheap random number generator
+ size_t bytes_until_sample_; // Bytes until we sample next
+
+ public:
+ // All ThreadCache objects are kept in a linked list (for stats collection)
+ TCMalloc_ThreadCache* next_;
+ TCMalloc_ThreadCache* prev_;
+
+ void Init(pthread_t tid);
+ void Cleanup();
+
+ // Accessors (mostly just for printing stats)
+ int freelist_length(size_t cl) const { return list_[cl].length(); }
+
+ // Total byte size in cache
+ size_t Size() const { return size_; }
+
+ void* Allocate(size_t size);
+ void Deallocate(void* ptr, size_t size_class);
+
+ void FetchFromCentralCache(size_t cl);
+ void ReleaseToCentralCache(size_t cl, int N);
+ void Scavenge();
+ void Print() const;
+
+ // Record allocation of "k" bytes. Return true iff allocation
+ // should be sampled
+ bool SampleAllocation(size_t k);
+
+ // Pick next sampling point
+ void PickNextSample();
+
+ static void InitModule();
+ static void InitTSD();
+ static TCMalloc_ThreadCache* GetCache();
+ static TCMalloc_ThreadCache* GetCacheIfPresent();
+ static void* CreateCacheIfNecessary();
+ static void DeleteCache(void* ptr);
+ static void RecomputeThreadCacheSize();
+};
+
+//-------------------------------------------------------------------
+// Data kept per size-class in central cache
+//-------------------------------------------------------------------
+
+class TCMalloc_Central_FreeList {
+ public:
+ void Init(size_t cl);
+
+ // These methods all do internal locking.
+
+ // Insert the specified range into the central freelist. N is the number of
+ // elements in the range.
+ void InsertRange(void *start, void *end, int N);
+
+ // Returns the actual number of fetched elements into N.
+ void RemoveRange(void **start, void **end, int *N);
+
+ // Returns the number of free objects in cache.
+ int length() {
+ SpinLockHolder h(&lock_);
+ return counter_;
+ }
+
+ // Returns the number of free objects in the transfer cache.
+ int tc_length() {
+ SpinLockHolder h(&lock_);
+ return used_slots_ * num_objects_to_move[size_class_];
+ }
+
+ private:
+ // REQUIRES: lock_ is held
+ // Remove object from cache and return.
+ // Return NULL if no free entries in cache.
+ void* FetchFromSpans();
+
+ // REQUIRES: lock_ is held
+ // Remove object from cache and return. Fetches
+ // from pageheap if cache is empty. Only returns
+ // NULL on allocation failure.
+ void* FetchFromSpansSafe();
+
+ // REQUIRES: lock_ is held
+ // Release a linked list of objects to spans.
+ // May temporarily release lock_.
+ void ReleaseListToSpans(void *start);
+
+ // REQUIRES: lock_ is held
+ // Release an object to spans.
+ // May temporarily release lock_.
+ void ReleaseToSpans(void* object);
+
+ // REQUIRES: lock_ is held
+ // Populate cache by fetching from the page heap.
+ // May temporarily release lock_.
+ void Populate();
+
+ // REQUIRES: lock is held.
+ // Tries to make room for a TCEntry. If the cache is full it will try to
+ // expand it at the cost of some other cache size. Return false if there is
+ // no space.
+ bool MakeCacheSpace();
+
+ // REQUIRES: lock_ for locked_size_class is held.
+ // Picks a "random" size class to steal TCEntry slot from. In reality it
+ // just iterates over the sizeclasses but does so without taking a lock.
+ // Returns true on success.
+ // May temporarily lock a "random" size class.
+ static bool EvictRandomSizeClass(size_t locked_size_class, bool force);
+
+ // REQUIRES: lock_ is *not* held.
+ // Tries to shrink the Cache. If force is true it will relase objects to
+ // spans if it allows it to shrink the cache. Return false if it failed to
+ // shrink the cache. Decrements cache_size_ on succeess.
+ // May temporarily take lock_. If it takes lock_, the locked_size_class
+ // lock is released to the thread from holding two size class locks
+ // concurrently which could lead to a deadlock.
+ bool ShrinkCache(int locked_size_class, bool force);
+
+ // This lock protects all the data members. cached_entries and cache_size_
+ // may be looked at without holding the lock.
+ SpinLock lock_;
+
+ // We keep linked lists of empty and non-empty spans.
+ size_t size_class_; // My size class
+ Span empty_; // Dummy header for list of empty spans
+ Span nonempty_; // Dummy header for list of non-empty spans
+ size_t counter_; // Number of free objects in cache entry
+
+ // Here we reserve space for TCEntry cache slots. Since one size class can
+ // end up getting all the TCEntries quota in the system we just preallocate
+ // sufficient number of entries here.
+ TCEntry tc_slots_[kNumTransferEntries];
+
+ // Number of currently used cached entries in tc_slots_. This variable is
+ // updated under a lock but can be read without one.
+ int32_t used_slots_;
+ // The current number of slots for this size class. This is an
+ // adaptive value that is increased if there is lots of traffic
+ // on a given size class.
+ int32_t cache_size_;
+};
+
+// Pad each CentralCache object to multiple of 64 bytes
+class TCMalloc_Central_FreeListPadded : public TCMalloc_Central_FreeList {
+ private:
+ char pad_[(64 - (sizeof(TCMalloc_Central_FreeList) % 64)) % 64];
+};
+
+//-------------------------------------------------------------------
+// Global variables
+//-------------------------------------------------------------------
+
+// Central cache -- a collection of free-lists, one per size-class.
+// We have a separate lock per free-list to reduce contention.
+static TCMalloc_Central_FreeListPadded central_cache[kNumClasses];
+
+// Page-level allocator
+static SpinLock pageheap_lock = SPINLOCK_INITIALIZER;
+static char pageheap_memory[sizeof(TCMalloc_PageHeap)];
+static bool phinited = false;
+
+// Avoid extra level of indirection by making "pageheap" be just an alias
+// of pageheap_memory.
+#define pageheap ((TCMalloc_PageHeap*) pageheap_memory)
+
+// Thread-specific key. Initialization here is somewhat tricky
+// because some Linux startup code invokes malloc() before it
+// is in a good enough state to handle pthread_keycreate().
+// Therefore, we use TSD keys only after tsd_inited is set to true.
+// Until then, we use a slow path to get the heap object.
+static bool tsd_inited = false;
+static pthread_key_t heap_key;
+
+// Allocator for thread heaps
+static PageHeapAllocator<TCMalloc_ThreadCache> threadheap_allocator;
+
+// Linked list of heap objects. Protected by pageheap_lock.
+static TCMalloc_ThreadCache* thread_heaps = NULL;
+static int thread_heap_count = 0;
+
+// Overall thread cache size. Protected by pageheap_lock.
+static size_t overall_thread_cache_size = kDefaultOverallThreadCacheSize;
+
+// Global per-thread cache size. Writes are protected by
+// pageheap_lock. Reads are done without any locking, which should be
+// fine as long as size_t can be written atomically and we don't place
+// invariants between this variable and other pieces of state.
+static volatile size_t per_thread_cache_size = kMaxThreadCacheSize;
+
+//-------------------------------------------------------------------
+// Central cache implementation
+//-------------------------------------------------------------------
+
+void TCMalloc_Central_FreeList::Init(size_t cl) {
+ lock_.Init();
+ size_class_ = cl;
+ DLL_Init(&empty_);
+ DLL_Init(&nonempty_);
+ counter_ = 0;
+
+ cache_size_ = 1;
+ used_slots_ = 0;
+ ASSERT(cache_size_ <= kNumTransferEntries);
+}
+
+void TCMalloc_Central_FreeList::ReleaseListToSpans(void* start) {
+ while (start) {
+ void *next = SLL_Next(start);
+ ReleaseToSpans(start);
+ start = next;
+ }
+}
+
+void TCMalloc_Central_FreeList::ReleaseToSpans(void* object) {
+ const PageID p = reinterpret_cast<uintptr_t>(object) >> kPageShift;
+ Span* span = pageheap->GetDescriptor(p);
+ ASSERT(span != NULL);
+ ASSERT(span->refcount > 0);
+
+ // If span is empty, move it to non-empty list
+ if (span->objects == NULL) {
+ DLL_Remove(span);
+ DLL_Prepend(&nonempty_, span);
+ Event(span, 'N', 0);
+ }
+
+ // The following check is expensive, so it is disabled by default
+ if (false) {
+ // Check that object does not occur in list
+ int got = 0;
+ for (void* p = span->objects; p != NULL; p = *((void**) p)) {
+ ASSERT(p != object);
+ got++;
+ }
+ ASSERT(got + span->refcount ==
+ (span->length<<kPageShift)/ByteSizeForClass(span->sizeclass));
+ }
+
+ counter_++;
+ span->refcount--;
+ if (span->refcount == 0) {
+ Event(span, '#', 0);
+ counter_ -= (span->length<<kPageShift) / ByteSizeForClass(span->sizeclass);
+ DLL_Remove(span);
+
+ // Release central list lock while operating on pageheap
+ lock_.Unlock();
+ {
+ SpinLockHolder h(&pageheap_lock);
+ pageheap->Delete(span);
+ }
+ lock_.Lock();
+ } else {
+ *(reinterpret_cast<void**>(object)) = span->objects;
+ span->objects = object;
+ }
+}
+
+bool TCMalloc_Central_FreeList::EvictRandomSizeClass(
+ size_t locked_size_class, bool force) {
+ static unsigned int race_counter = 0;
+ unsigned int t = race_counter++; // Updated without a lock, but who cares.
+ if (t >= kNumClasses) {
+ while (t >= kNumClasses) {
+ t -= kNumClasses;
+ }
+ race_counter = t;
+ }
+ ASSERT(t >= 0);
+ ASSERT(t < kNumClasses);
+ if (t == locked_size_class) return false;
+ return central_cache[t].ShrinkCache(locked_size_class, force);
+}
+
+bool TCMalloc_Central_FreeList::MakeCacheSpace() {
+ // Is there room in the cache?
+ if (used_slots_ < cache_size_) return true;
+ // Check if we can expand this cache?
+ if (cache_size_ == kNumTransferEntries) return false;
+ // Ok, we'll try to grab an entry from some other size class.
+ if (EvictRandomSizeClass(size_class_, false) ||
+ EvictRandomSizeClass(size_class_, true)) {
+ // Succeeded in evicting, we're going to make our cache larger.
+ cache_size_++;
+ return true;
+ }
+ return false;
+}
+
+
+namespace {
+class LockInverter {
+ private:
+ TCMalloc_SpinLock *held_, *temp_;
+ public:
+ inline explicit LockInverter(TCMalloc_SpinLock* held, TCMalloc_SpinLock *temp)
+ : held_(held), temp_(temp) { held_->Unlock(); temp_->Lock(); }
+ inline ~LockInverter() { temp_->Unlock(); held_->Lock(); }
+};
+}
+
+bool TCMalloc_Central_FreeList::ShrinkCache(int locked_size_class, bool force) {
+ // Start with a quick check without taking a lock.
+ if (cache_size_ == 0) return false;
+ // We don't evict from a full cache unless we are 'forcing'.
+ if (force == false && used_slots_ == cache_size_) return false;
+
+ // Grab lock, but first release the other lock held by this thread. We use
+ // the lock inverter to ensure that we never hold two size class locks
+ // concurrently. That can create a deadlock because there is no well
+ // defined nesting order.
+ LockInverter li(&central_cache[locked_size_class].lock_, &lock_);
+ ASSERT(used_slots_ <= cache_size_);
+ ASSERT(0 <= cache_size_);
+ if (cache_size_ == 0) return false;
+ if (used_slots_ == cache_size_) {
+ if (force == false) return false;
+ // ReleaseListToSpans releases the lock, so we have to make all the
+ // updates to the central list before calling it.
+ cache_size_--;
+ used_slots_--;
+ ReleaseListToSpans(tc_slots_[used_slots_].head);
+ return true;
+ }
+ cache_size_--;
+ return true;
+}
+
+void TCMalloc_Central_FreeList::InsertRange(void *start, void *end, int N) {
+ SpinLockHolder h(&lock_);
+ if (N == num_objects_to_move[size_class_] &&
+ MakeCacheSpace()) {
+ int slot = used_slots_++;
+ ASSERT(slot >=0);
+ ASSERT(slot < kNumTransferEntries);
+ TCEntry *entry = &tc_slots_[slot];
+ entry->head = start;
+ entry->tail = end;
+ return;
+ }
+ ReleaseListToSpans(start);
+}
+
+void TCMalloc_Central_FreeList::RemoveRange(void **start, void **end, int *N) {
+ int num = *N;
+ ASSERT(num > 0);
+
+ SpinLockHolder h(&lock_);
+ if (num == num_objects_to_move[size_class_] && used_slots_ > 0) {
+ int slot = --used_slots_;
+ ASSERT(slot >= 0);
+ TCEntry *entry = &tc_slots_[slot];
+ *start = entry->head;
+ *end = entry->tail;
+ return;
+ }
+
+ // TODO: Prefetch multiple TCEntries?
+ void *tail = FetchFromSpansSafe();
+ if (!tail) {
+ // We are completely out of memory.
+ *start = *end = NULL;
+ *N = 0;
+ return;
+ }
+
+ SLL_SetNext(tail, NULL);
+ void *head = tail;
+ int count = 1;
+ while (count < num) {
+ void *t = FetchFromSpans();
+ if (!t) break;
+ SLL_Push(&head, t);
+ count++;
+ }
+ *start = head;
+ *end = tail;
+ *N = count;
+}
+
+
+void* TCMalloc_Central_FreeList::FetchFromSpansSafe() {
+ void *t = FetchFromSpans();
+ if (!t) {
+ Populate();
+ t = FetchFromSpans();
+ }
+ return t;
+}
+
+void* TCMalloc_Central_FreeList::FetchFromSpans() {
+ if (DLL_IsEmpty(&nonempty_)) return NULL;
+ Span* span = nonempty_.next;
+
+ ASSERT(span->objects != NULL);
+ span->refcount++;
+ void* result = span->objects;
+ span->objects = *(reinterpret_cast<void**>(result));
+ if (span->objects == NULL) {
+ // Move to empty list
+ DLL_Remove(span);
+ DLL_Prepend(&empty_, span);
+ Event(span, 'E', 0);
+ }
+ counter_--;
+ return result;
+}
+
+// Fetch memory from the system and add to the central cache freelist.
+void TCMalloc_Central_FreeList::Populate() {
+ // Release central list lock while operating on pageheap
+ lock_.Unlock();
+ const size_t npages = class_to_pages[size_class_];
+
+ Span* span;
+ {
+ SpinLockHolder h(&pageheap_lock);
+ span = pageheap->New(npages);
+ if (span) pageheap->RegisterSizeClass(span, size_class_);
+ }
+ if (span == NULL) {
+ MESSAGE("allocation failed: %d\n", errno);
+ lock_.Lock();
+ return;
+ }
+
+ // Split the block into pieces and add to the free-list
+ // TODO: coloring of objects to avoid cache conflicts?
+ void** tail = &span->objects;
+ char* ptr = reinterpret_cast<char*>(span->start << kPageShift);
+ char* limit = ptr + (npages << kPageShift);
+ const size_t size = ByteSizeForClass(size_class_);
+ int num = 0;
+ while (ptr + size <= limit) {
+ *tail = ptr;
+ tail = reinterpret_cast<void**>(ptr);
+ ptr += size;
+ num++;
+ }
+ ASSERT(ptr <= limit);
+ *tail = NULL;
+ span->refcount = 0; // No sub-object in use yet
+
+ // Add span to list of non-empty spans
+ lock_.Lock();
+ DLL_Prepend(&nonempty_, span);
+ counter_ += num;
+}
+
+//-------------------------------------------------------------------
+// TCMalloc_ThreadCache implementation
+//-------------------------------------------------------------------
+
+inline bool TCMalloc_ThreadCache::SampleAllocation(size_t k) {
+ if (bytes_until_sample_ < k) {
+ PickNextSample();
+ return true;
+ } else {
+ bytes_until_sample_ -= k;
+ return false;
+ }
+}
+
+void TCMalloc_ThreadCache::Init(pthread_t tid) {
+ size_ = 0;
+ next_ = NULL;
+ prev_ = NULL;
+ tid_ = tid;
+ in_setspecific_ = false;
+ for (size_t cl = 0; cl < kNumClasses; ++cl) {
+ list_[cl].Init();
+ }
+
+ // Initialize RNG -- run it for a bit to get to good values
+ rnd_ = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this));
+ for (int i = 0; i < 100; i++) {
+ PickNextSample();
+ }
+}
+
+void TCMalloc_ThreadCache::Cleanup() {
+ // Put unused memory back into central cache
+ for (unsigned int cl = 0; cl < kNumClasses; ++cl) {
+ if (list_[cl].length() > 0) {
+ ReleaseToCentralCache(cl, list_[cl].length());
+ }
+ }
+}
+
+inline void* TCMalloc_ThreadCache::Allocate(size_t size) {
+ ASSERT(size <= kMaxSize);
+ const size_t cl = SizeClass(size);
+ FreeList* list = &list_[cl];
+ if (list->empty()) {
+ FetchFromCentralCache(cl);
+ if (list->empty()) return NULL;
+ }
+ size_ -= ByteSizeForClass(cl);
+ return list->Pop();
+}
+
+inline void TCMalloc_ThreadCache::Deallocate(void* ptr, size_t cl) {
+ size_ += ByteSizeForClass(cl);
+ FreeList* list = &list_[cl];
+ list->Push(ptr);
+ // If enough data is free, put back into central cache
+ if (list->length() > kMaxFreeListLength) {
+ ReleaseToCentralCache(cl, num_objects_to_move[cl]);
+ }
+ if (size_ >= per_thread_cache_size) Scavenge();
+}
+
+// Remove some objects of class "cl" from central cache and add to thread heap
+void TCMalloc_ThreadCache::FetchFromCentralCache(size_t cl) {
+ int fetch_count = num_objects_to_move[cl];
+ void *start, *end;
+ central_cache[cl].RemoveRange(&start, &end, &fetch_count);
+ list_[cl].PushRange(fetch_count, start, end);
+ size_ += ByteSizeForClass(cl) * fetch_count;
+}
+
+// Remove some objects of class "cl" from thread heap and add to central cache
+void TCMalloc_ThreadCache::ReleaseToCentralCache(size_t cl, int N) {
+ ASSERT(N > 0);
+ FreeList* src = &list_[cl];
+ if (N > src->length()) N = src->length();
+ size_ -= N*ByteSizeForClass(cl);
+
+ // We return prepackaged chains of the correct size to the central cache.
+ // TODO: Use the same format internally in the thread caches?
+ int batch_size = num_objects_to_move[cl];
+ while (N > batch_size) {
+ void *tail, *head;
+ src->PopRange(batch_size, &head, &tail);
+ central_cache[cl].InsertRange(head, tail, batch_size);
+ N -= batch_size;
+ }
+ void *tail, *head;
+ src->PopRange(N, &head, &tail);
+ central_cache[cl].InsertRange(head, tail, N);
+}
+
+// Release idle memory to the central cache
+void TCMalloc_ThreadCache::Scavenge() {
+ // If the low-water mark for the free list is L, it means we would
+ // not have had to allocate anything from the central cache even if
+ // we had reduced the free list size by L. We aim to get closer to
+ // that situation by dropping L/2 nodes from the free list. This
+ // may not release much memory, but if so we will call scavenge again
+ // pretty soon and the low-water marks will be high on that call.
+ //int64 start = CycleClock::Now();
+
+ for (unsigned int cl = 0; cl < kNumClasses; cl++) {
+ FreeList* list = &list_[cl];
+ const int lowmark = list->lowwatermark();
+ if (lowmark > 0) {
+ const int drop = (lowmark > 1) ? lowmark/2 : 1;
+ ReleaseToCentralCache(cl, drop);
+ }
+ list->clear_lowwatermark();
+ }
+
+ //int64 finish = CycleClock::Now();
+ //CycleTimer ct;
+ //MESSAGE("GC: %.0f ns\n", ct.CyclesToUsec(finish-start)*1000.0);
+}
+
+inline TCMalloc_ThreadCache* TCMalloc_ThreadCache::GetCache() {
+ void* ptr = NULL;
+ if (!tsd_inited) {
+ InitModule();
+ } else {
+ ptr = GetHeap(heap_key);
+ }
+ if (ptr == NULL) ptr = CreateCacheIfNecessary();
+ return reinterpret_cast<TCMalloc_ThreadCache*>(ptr);
+}
+
+// In deletion paths, we do not try to create a thread-cache. This is
+// because we may be in the thread destruction code and may have
+// already cleaned up the cache for this thread.
+inline TCMalloc_ThreadCache* TCMalloc_ThreadCache::GetCacheIfPresent() {
+ if (!tsd_inited) return NULL;
+ return reinterpret_cast<TCMalloc_ThreadCache*>
+ (GetHeap(heap_key));
+}
+
+void TCMalloc_ThreadCache::PickNextSample() {
+ // Make next "random" number
+ // x^32+x^22+x^2+x^1+1 is a primitive polynomial for random numbers
+ static const uint32_t kPoly = (1 << 22) | (1 << 2) | (1 << 1) | (1 << 0);
+ uint32_t r = rnd_;
+ rnd_ = (r << 1) ^ ((static_cast<int32_t>(r) >> 31) & kPoly);
+
+ // Next point is "rnd_ % (sample_period)". I.e., average
+ // increment is "sample_period/2".
+ const int flag_value = FLAGS_tcmalloc_sample_parameter;
+ static int last_flag_value = -1;
+
+ if (flag_value != last_flag_value) {
+ SpinLockHolder h(&sample_period_lock);
+ unsigned int i;
+ for (i = 0; i < (sizeof(primes_list)/sizeof(primes_list[0]) - 1); i++) {
+ if (primes_list[i] >= flag_value) {
+ break;
+ }
+ }
+ sample_period = primes_list[i];
+ last_flag_value = flag_value;
+ }
+ bytes_until_sample_ = rnd_ % sample_period;
+}
+
+void TCMalloc_ThreadCache::InitModule() {
+ // There is a slight potential race here because of double-checked
+ // locking idiom. However, as long as the program does a small
+ // allocation before switching to multi-threaded mode, we will be
+ // fine. We increase the chances of doing such a small allocation
+ // by doing one in the constructor of the module_enter_exit_hook
+ // object declared below.
+ SpinLockHolder h(&pageheap_lock);
+ if (!phinited) {
+ InitSizeClasses();
+ threadheap_allocator.Init();
+ span_allocator.Init();
+ span_allocator.New(); // Reduce cache conflicts
+ span_allocator.New(); // Reduce cache conflicts
+ stacktrace_allocator.Init();
+ DLL_Init(&sampled_objects);
+ for (unsigned int i = 0; i < kNumClasses; ++i) {
+ central_cache[i].Init(i);
+ }
+ new ((void*)pageheap_memory) TCMalloc_PageHeap;
+ phinited = 1;
+ }
+}
+
+void TCMalloc_ThreadCache::InitTSD() {
+ ASSERT(!tsd_inited);
+ pthread_key_create(&heap_key, DeleteCache);
+ tsd_inited = true;
+
+ // We may have used a fake pthread_t for the main thread. Fix it.
+ pthread_t zero;
+ memset(&zero, 0, sizeof(zero));
+ SpinLockHolder h(&pageheap_lock);
+ for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) {
+ if (h->tid_ == zero) {
+ h->tid_ = pthread_self();
+ }
+ }
+}
+
+void* TCMalloc_ThreadCache::CreateCacheIfNecessary() {
+ // Initialize per-thread data if necessary
+ TCMalloc_ThreadCache* heap = NULL;
+ {
+ SpinLockHolder h(&pageheap_lock);
+
+ // Early on in glibc's life, we cannot even call pthread_self()
+ pthread_t me;
+ if (!tsd_inited) {
+ memset(&me, 0, sizeof(me));
+ } else {
+ me = pthread_self();
+ }
+
+ // This may be a recursive malloc call from pthread_setspecific()
+ // In that case, the heap for this thread has already been created
+ // and added to the linked list. So we search for that first.
+ for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) {
+ if (h->tid_ == me) {
+ heap = h;
+ break;
+ }
+ }
+
+ if (heap == NULL) {
+ // Create the heap and add it to the linked list
+ heap = threadheap_allocator.New();
+ heap->Init(me);
+ heap->next_ = thread_heaps;
+ heap->prev_ = NULL;
+ if (thread_heaps != NULL) thread_heaps->prev_ = heap;
+ thread_heaps = heap;
+ thread_heap_count++;
+ RecomputeThreadCacheSize();
+ }
+ }
+
+ // We call pthread_setspecific() outside the lock because it may
+ // call malloc() recursively. We check for the recursive call using
+ // the "in_setspecific_" flag so that we can avoid calling
+ // pthread_setspecific() if we are already inside pthread_setspecific().
+ if (!heap->in_setspecific_ && tsd_inited) {
+ heap->in_setspecific_ = true;
+ SetHeap(heap_key, heap);
+ heap->in_setspecific_ = false;
+ }
+ return heap;
+}
+
+void TCMalloc_ThreadCache::DeleteCache(void* ptr) {
+#if defined(Y_HAVE_FAST_POD_TLS)
+ my_heap = 0;
+#endif
+
+ // Remove all memory from heap
+ TCMalloc_ThreadCache* heap;
+ heap = reinterpret_cast<TCMalloc_ThreadCache*>(ptr);
+ heap->Cleanup();
+
+ // Remove from linked list
+ SpinLockHolder h(&pageheap_lock);
+ if (heap->next_ != NULL) heap->next_->prev_ = heap->prev_;
+ if (heap->prev_ != NULL) heap->prev_->next_ = heap->next_;
+ if (thread_heaps == heap) thread_heaps = heap->next_;
+ thread_heap_count--;
+ RecomputeThreadCacheSize();
+
+ threadheap_allocator.Delete(heap);
+}
+
+void TCMalloc_ThreadCache::RecomputeThreadCacheSize() {
+ // Divide available space across threads
+ int n = thread_heap_count > 0 ? thread_heap_count : 1;
+ size_t space = overall_thread_cache_size / n;
+
+ // Limit to allowed range
+ if (space < kMinThreadCacheSize) space = kMinThreadCacheSize;
+ if (space > kMaxThreadCacheSize) space = kMaxThreadCacheSize;
+
+ per_thread_cache_size = space;
+}
+
+void TCMalloc_ThreadCache::Print() const {
+ for (unsigned int cl = 0; cl < kNumClasses; ++cl) {
+ MESSAGE(" %5" PRIuS " : %4d len; %4d lo\n",
+ ByteSizeForClass(cl),
+ list_[cl].length(),
+ list_[cl].lowwatermark());
+ }
+}
+
+// Extract interesting stats
+struct TCMallocStats {
+ uint64_t system_bytes; // Bytes alloced from system
+ uint64_t thread_bytes; // Bytes in thread caches
+ uint64_t central_bytes; // Bytes in central cache
+ uint64_t transfer_bytes; // Bytes in central transfer cache
+ uint64_t pageheap_bytes; // Bytes in page heap
+ uint64_t metadata_bytes; // Bytes alloced for metadata
+};
+
+// Get stats into "r". Also get per-size-class counts if class_count != NULL
+static void ExtractStats(TCMallocStats* r, uint64_t* class_count) {
+ r->central_bytes = 0;
+ r->transfer_bytes = 0;
+ for (unsigned int cl = 0; cl < kNumClasses; ++cl) {
+ const int length = central_cache[cl].length();
+ const int tc_length = central_cache[cl].tc_length();
+ r->central_bytes += static_cast<uint64_t>(ByteSizeForClass(cl)) * length;
+ r->transfer_bytes +=
+ static_cast<uint64_t>(ByteSizeForClass(cl)) * tc_length;
+ if (class_count) class_count[cl] = length + tc_length;
+ }
+
+ // Add stats from per-thread heaps
+ r->thread_bytes = 0;
+ { // scope
+ SpinLockHolder h(&pageheap_lock);
+ for (TCMalloc_ThreadCache* h = thread_heaps; h != NULL; h = h->next_) {
+ r->thread_bytes += h->Size();
+ if (class_count) {
+ for (unsigned int cl = 0; cl < kNumClasses; ++cl) {
+ class_count[cl] += h->freelist_length(cl);
+ }
+ }
+ }
+ }
+
+ { //scope
+ SpinLockHolder h(&pageheap_lock);
+ r->system_bytes = pageheap->SystemBytes();
+ r->metadata_bytes = metadata_system_bytes;
+ r->pageheap_bytes = pageheap->FreeBytes();
+ }
+}
+
+// WRITE stats to "out"
+static void DumpStats(TCMalloc_Printer* out, int level) {
+ TCMallocStats stats;
+ uint64_t class_count[kNumClasses];
+ ExtractStats(&stats, (level >= 2 ? class_count : NULL));
+
+ if (level >= 2) {
+ out->printf("------------------------------------------------\n");
+ uint64_t cumulative = 0;
+ for (unsigned int cl = 0; cl < kNumClasses; ++cl) {
+ if (class_count[cl] > 0) {
+ uint64_t class_bytes = class_count[cl] * ByteSizeForClass(cl);
+ cumulative += class_bytes;
+ out->printf("class %3u [ %8" PRIuS " bytes ] : "
+ "%8" LLU " objs; %5.1f MB; %5.1f cum MB\n",
+ cl, ByteSizeForClass(cl),
+ class_count[cl],
+ class_bytes / 1048576.0,
+ cumulative / 1048576.0);
+ }
+ }
+
+ SpinLockHolder h(&pageheap_lock);
+ pageheap->Dump(out);
+ }
+
+ const uint64_t bytes_in_use = stats.system_bytes
+ - stats.pageheap_bytes
+ - stats.central_bytes
+ - stats.transfer_bytes
+ - stats.thread_bytes;
+
+ out->printf("------------------------------------------------\n"
+ "MALLOC: %12" LLU " Heap size\n"
+ "MALLOC: %12" LLU " Bytes in use by application\n"
+ "MALLOC: %12" LLU " Bytes free in page heap\n"
+ "MALLOC: %12" LLU " Bytes free in central cache\n"
+ "MALLOC: %12" LLU " Bytes free in transfer cache\n"
+ "MALLOC: %12" LLU " Bytes free in thread caches\n"
+ "MALLOC: %12" LLU " Spans in use\n"
+ "MALLOC: %12" LLU " Thread heaps in use\n"
+ "MALLOC: %12" LLU " Metadata allocated\n"
+ "------------------------------------------------\n",
+ stats.system_bytes,
+ bytes_in_use,
+ stats.pageheap_bytes,
+ stats.central_bytes,
+ stats.transfer_bytes,
+ stats.thread_bytes,
+ uint64_t(span_allocator.inuse()),
+ uint64_t(threadheap_allocator.inuse()),
+ stats.metadata_bytes);
+}
+
+static void PrintStats(int level) {
+ const int kBufferSize = 16 << 10;
+ char* buffer = new char[kBufferSize];
+ TCMalloc_Printer printer(buffer, kBufferSize);
+ DumpStats(&printer, level);
+ write(STDERR_FILENO, buffer, strlen(buffer));
+ delete[] buffer;
+}
+
+static void** DumpStackTraces() {
+ // Count how much space we need
+ int needed_slots = 0;
+ {
+ SpinLockHolder h(&pageheap_lock);
+ for (Span* s = sampled_objects.next; s != &sampled_objects; s = s->next) {
+ StackTrace* stack = reinterpret_cast<StackTrace*>(s->objects);
+ needed_slots += 3 + stack->depth;
+ }
+ needed_slots += 100; // Slop in case sample grows
+ needed_slots += needed_slots/8; // An extra 12.5% slop
+ }
+
+ void** result = new void*[needed_slots];
+ if (result == NULL) {
+ MESSAGE("tcmalloc: could not allocate %d slots for stack traces\n",
+ needed_slots);
+ return NULL;
+ }
+
+ SpinLockHolder h(&pageheap_lock);
+ int used_slots = 0;
+ for (Span* s = sampled_objects.next; s != &sampled_objects; s = s->next) {
+ ASSERT(used_slots < needed_slots); // Need to leave room for terminator
+ StackTrace* stack = reinterpret_cast<StackTrace*>(s->objects);
+ if (used_slots + 3 + stack->depth >= needed_slots) {
+ // No more room
+ break;
+ }
+
+ result[used_slots+0] = reinterpret_cast<void*>(1);
+ result[used_slots+1] = reinterpret_cast<void*>(stack->size);
+ result[used_slots+2] = reinterpret_cast<void*>(stack->depth);
+ for (int d = 0; d < stack->depth; d++) {
+ result[used_slots+3+d] = stack->stack[d];
+ }
+ used_slots += 3 + stack->depth;
+ }
+ result[used_slots] = reinterpret_cast<void*>(0);
+ return result;
+}
+
+static void** DumpHeapGrowthStackTraces() {
+ // Count how much space we need
+ int needed_slots = 0;
+ {
+ SpinLockHolder h(&pageheap_lock);
+ for (StackTrace* t = growth_stacks;
+ t != NULL;
+ t = reinterpret_cast<StackTrace*>(t->stack[kMaxStackDepth-1])) {
+ needed_slots += 3 + t->depth;
+ }
+ needed_slots += 100; // Slop in case list grows
+ needed_slots += needed_slots/8; // An extra 12.5% slop
+ }
+
+ void** result = new void*[needed_slots];
+ if (result == NULL) {
+ MESSAGE("tcmalloc: could not allocate %d slots for stack traces\n",
+ needed_slots);
+ return NULL;
+ }
+
+ SpinLockHolder h(&pageheap_lock);
+ int used_slots = 0;
+ for (StackTrace* t = growth_stacks;
+ t != NULL;
+ t = reinterpret_cast<StackTrace*>(t->stack[kMaxStackDepth-1])) {
+ ASSERT(used_slots < needed_slots); // Need to leave room for terminator
+ if (used_slots + 3 + t->depth >= needed_slots) {
+ // No more room
+ break;
+ }
+
+ result[used_slots+0] = reinterpret_cast<void*>(1);
+ result[used_slots+1] = reinterpret_cast<void*>(t->size);
+ result[used_slots+2] = reinterpret_cast<void*>(t->depth);
+ for (int d = 0; d < t->depth; d++) {
+ result[used_slots+3+d] = t->stack[d];
+ }
+ used_slots += 3 + t->depth;
+ }
+ result[used_slots] = reinterpret_cast<void*>(0);
+ return result;
+}
+
+// TCMalloc's support for extra malloc interfaces
+class TCMallocImplementation : public MallocExtension {
+ public:
+ virtual void GetStats(char* buffer, int buffer_length) {
+ ASSERT(buffer_length > 0);
+ TCMalloc_Printer printer(buffer, buffer_length);
+
+ // Print level one stats unless lots of space is available
+ if (buffer_length < 10000) {
+ DumpStats(&printer, 1);
+ } else {
+ DumpStats(&printer, 2);
+ }
+ }
+
+ virtual void** ReadStackTraces() {
+ return DumpStackTraces();
+ }
+
+ virtual void** ReadHeapGrowthStackTraces() {
+ return DumpHeapGrowthStackTraces();
+ }
+
+ virtual bool GetNumericProperty(const char* name, size_t* value) {
+ ASSERT(name != NULL);
+
+ if (strcmp(name, "generic.current_allocated_bytes") == 0) {
+ TCMallocStats stats;
+ ExtractStats(&stats, NULL);
+ *value = stats.system_bytes
+ - stats.thread_bytes
+ - stats.central_bytes
+ - stats.pageheap_bytes;
+ return true;
+ }
+
+ if (strcmp(name, "generic.heap_size") == 0) {
+ TCMallocStats stats;
+ ExtractStats(&stats, NULL);
+ *value = stats.system_bytes;
+ return true;
+ }
+
+ if (strcmp(name, "tcmalloc.slack_bytes") == 0) {
+ // We assume that bytes in the page heap are not fragmented too
+ // badly, and are therefore available for allocation.
+ SpinLockHolder l(&pageheap_lock);
+ *value = pageheap->FreeBytes();
+ return true;
+ }
+
+ if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) {
+ SpinLockHolder l(&pageheap_lock);
+ *value = overall_thread_cache_size;
+ return true;
+ }
+
+ if (strcmp(name, "tcmalloc.current_total_thread_cache_bytes") == 0) {
+ TCMallocStats stats;
+ ExtractStats(&stats, NULL);
+ *value = stats.thread_bytes;
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual bool SetNumericProperty(const char* name, size_t value) {
+ ASSERT(name != NULL);
+
+ if (strcmp(name, "tcmalloc.max_total_thread_cache_bytes") == 0) {
+ // Clip the value to a reasonable range
+ if (value < kMinThreadCacheSize) value = kMinThreadCacheSize;
+ if (value > (1<<30)) value = (1<<30); // Limit to 1GB
+
+ SpinLockHolder l(&pageheap_lock);
+ overall_thread_cache_size = static_cast<size_t>(value);
+ TCMalloc_ThreadCache::RecomputeThreadCacheSize();
+ return true;
+ }
+
+ return false;
+ }
+};
+
+//-------------------------------------------------------------------
+// Helpers for the exported routines below
+//-------------------------------------------------------------------
+
+static Span* DoSampledAllocation(size_t size) {
+ SpinLockHolder h(&pageheap_lock);
+
+ // Allocate span
+ Span* span = pageheap->New(pages(size == 0 ? 1 : size));
+ if (span == NULL) {
+ return NULL;
+ }
+
+ // Allocate stack trace
+ StackTrace* stack = stacktrace_allocator.New();
+ if (stack == NULL) {
+ // Sampling failed because of lack of memory
+ return span;
+ }
+
+ // Fill stack trace and record properly
+ stack->depth = GetStackTrace(stack->stack, kMaxStackDepth, 1);
+ stack->size = size;
+ span->sample = 1;
+ span->objects = stack;
+ DLL_Prepend(&sampled_objects, span);
+
+ return span;
+}
+
+static inline void* do_malloc(size_t size) {
+ void* ret = NULL;
+
+ if (TCMallocDebug::level >= TCMallocDebug::kVerbose) {
+ MESSAGE("In tcmalloc do_malloc(%" PRIuS")\n", size);
+ }
+ // The following call forces module initialization
+ TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCache();
+ if ((FLAGS_tcmalloc_sample_parameter > 0) && heap->SampleAllocation(size)) {
+ Span* span = DoSampledAllocation(size);
+ if (span != NULL) {
+ ret = reinterpret_cast<void*>(span->start << kPageShift);
+ }
+ } else if (size > kMaxSize) {
+ // Use page-level allocator
+ SpinLockHolder h(&pageheap_lock);
+ Span* span = pageheap->New(pages(size));
+ if (span != NULL) {
+ ret = reinterpret_cast<void*>(span->start << kPageShift);
+ }
+ } else {
+ ret = heap->Allocate(size);
+ }
+ if (ret == NULL) errno = ENOMEM;
+ return ret;
+}
+
+static inline void do_free(void* ptr) {
+ if (TCMallocDebug::level >= TCMallocDebug::kVerbose)
+ MESSAGE("In tcmalloc do_free(%p)\n", ptr);
+ if (ptr == NULL) return;
+ ASSERT(pageheap != NULL); // Should not call free() before malloc()
+ const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
+ Span* span = pageheap->GetDescriptor(p);
+
+ ASSERT(span != NULL);
+ ASSERT(!span->free);
+ const size_t cl = span->sizeclass;
+ if (cl != 0) {
+ ASSERT(!span->sample);
+ TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCacheIfPresent();
+ if (heap != NULL) {
+ heap->Deallocate(ptr, cl);
+ } else {
+ // Delete directly into central cache
+ SLL_SetNext(ptr, NULL);
+ central_cache[cl].InsertRange(ptr, ptr, 1);
+ }
+ } else {
+ SpinLockHolder h(&pageheap_lock);
+ ASSERT(reinterpret_cast<uintptr_t>(ptr) % kPageSize == 0);
+ ASSERT(span->start == p);
+ if (span->sample) {
+ DLL_Remove(span);
+ stacktrace_allocator.Delete(reinterpret_cast<StackTrace*>(span->objects));
+ span->objects = NULL;
+ }
+ pageheap->Delete(span);
+ }
+}
+
+// For use by exported routines below that want specific alignments
+//
+// Note: this code can be slow, and can significantly fragment memory.
+// The expectation is that memalign/posix_memalign/valloc/pvalloc will
+// not be invoked very often. This requirement simplifies our
+// implementation and allows us to tune for expected allocation
+// patterns.
+static void* do_memalign(size_t align, size_t size) {
+ ASSERT((align & (align - 1)) == 0);
+ ASSERT(align > 0);
+ if (size + align < size) return NULL; // Overflow
+
+ if (pageheap == NULL) TCMalloc_ThreadCache::InitModule();
+
+ // Allocate at least one byte to avoid boundary conditions below
+ if (size == 0) size = 1;
+
+ if (size <= kMaxSize && align < kPageSize) {
+ // Search through acceptable size classes looking for one with
+ // enough alignment. This depends on the fact that
+ // InitSizeClasses() currently produces several size classes that
+ // are aligned at powers of two. We will waste time and space if
+ // we miss in the size class array, but that is deemed acceptable
+ // since memalign() should be used rarely.
+ unsigned int cl = SizeClass(size);
+ while (cl < kNumClasses && ((class_to_size[cl] & (align - 1)) != 0)) {
+ cl++;
+ }
+ if (cl < kNumClasses) {
+ TCMalloc_ThreadCache* heap = TCMalloc_ThreadCache::GetCache();
+ return heap->Allocate(class_to_size[cl]);
+ }
+ }
+
+ // We will allocate directly from the page heap
+ SpinLockHolder h(&pageheap_lock);
+
+ if (align <= kPageSize) {
+ // Any page-level allocation will be fine
+ // TODO: We could put the rest of this page in the appropriate
+ // TODO: cache but it does not seem worth it.
+ Span* span = pageheap->New(pages(size));
+ if (span == NULL) return NULL;
+ return reinterpret_cast<void*>(span->start << kPageShift);
+ }
+
+ // Allocate extra pages and carve off an aligned portion
+ const int alloc = pages(size + align);
+ Span* span = pageheap->New(alloc);
+ if (span == NULL) return NULL;
+
+ // Skip starting portion so that we end up aligned
+ int skip = 0;
+ while ((((span->start+skip) << kPageShift) & (align - 1)) != 0) {
+ skip++;
+ }
+ ASSERT(skip < alloc);
+ if (skip > 0) {
+ Span* rest = pageheap->Split(span, skip);
+ pageheap->Delete(span);
+ span = rest;
+ }
+
+ // Skip trailing portion that we do not need to return
+ const unsigned int needed = pages(size);
+ ASSERT(span->length >= needed);
+ if (span->length > needed) {
+ Span* trailer = pageheap->Split(span, needed);
+ pageheap->Delete(trailer);
+ }
+ return reinterpret_cast<void*>(span->start << kPageShift);
+}
+
+
+
+// The constructor allocates an object to ensure that initialization
+// runs before main(), and therefore we do not have a chance to become
+// multi-threaded before initialization. We also create the TSD key
+// here. Presumably by the time this constructor runs, glibc is in
+// good enough shape to handle pthread_key_create().
+//
+// The constructor also takes the opportunity to tell STL to use
+// tcmalloc. We want to do this early, before construct time, so
+// all user STL allocations go through tcmalloc (which works really
+// well for STL).
+//
+// The destructor prints stats when the program exits.
+
+class TCMallocGuard {
+ public:
+ TCMallocGuard() {
+ char *envval;
+ if ((envval = getenv("TCMALLOC_DEBUG"))) {
+ TCMallocDebug::level = atoi(envval);
+ MESSAGE("Set tcmalloc debugging level to %d\n", TCMallocDebug::level);
+ }
+ do_free(do_malloc(1));
+ TCMalloc_ThreadCache::InitTSD();
+ do_free(do_malloc(1));
+ MallocExtension::Register(new TCMallocImplementation);
+ }
+
+ ~TCMallocGuard() {
+ const char* env = getenv("MALLOCSTATS");
+ if (env != NULL) {
+ int level = atoi(env);
+ if (level < 1) level = 1;
+ PrintStats(level);
+ }
+ }
+};
+
+static TCMallocGuard module_enter_exit_hook;
+
+
+//-------------------------------------------------------------------
+// Exported routines
+//-------------------------------------------------------------------
+
+// CAVEAT: The code structure below ensures that MallocHook methods are always
+// called from the stack frame of the invoked allocation function.
+// heap-checker.cc depends on this to start a stack trace from
+// the call to the (de)allocation function.
+
+extern "C" void* malloc(size_t size) {
+ void* result = do_malloc(size);
+ MallocHook::InvokeNewHook(result, size);
+ return result;
+}
+
+extern "C" void free(void* ptr) {
+ MallocHook::InvokeDeleteHook(ptr);
+ do_free(ptr);
+}
+
+extern "C" void* calloc(size_t n, size_t elem_size) {
+ // Overflow check
+ const size_t size = n * elem_size;
+ if (elem_size != 0 && size / elem_size != n) return NULL;
+
+ void* result = do_malloc(size);
+ if (result != NULL) {
+ memset(result, 0, size);
+ }
+ MallocHook::InvokeNewHook(result, size);
+ return result;
+}
+
+extern "C" void cfree(void* ptr) {
+ MallocHook::InvokeDeleteHook(ptr);
+ do_free(ptr);
+}
+
+extern "C" void* realloc(void* old_ptr, size_t new_size) {
+ if (old_ptr == NULL) {
+ void* result = do_malloc(new_size);
+ MallocHook::InvokeNewHook(result, new_size);
+ return result;
+ }
+ if (new_size == 0) {
+ MallocHook::InvokeDeleteHook(old_ptr);
+ do_free(old_ptr);
+ return NULL;
+ }
+
+ // Get the size of the old entry
+ const PageID p = reinterpret_cast<uintptr_t>(old_ptr) >> kPageShift;
+ Span* span = pageheap->GetDescriptor(p);
+ size_t old_size;
+ if (span->sizeclass != 0) {
+ old_size = ByteSizeForClass(span->sizeclass);
+ } else {
+ old_size = span->length << kPageShift;
+ }
+
+ // Reallocate if the new size is larger than the old size,
+ // or if the new size is significantly smaller than the old size.
+ if ((new_size > old_size) || (AllocationSize(new_size) < old_size)) {
+ // Need to reallocate
+ void* new_ptr = do_malloc(new_size);
+ if (new_ptr == NULL) {
+ return NULL;
+ }
+ MallocHook::InvokeNewHook(new_ptr, new_size);
+ memcpy(new_ptr, old_ptr, ((old_size < new_size) ? old_size : new_size));
+ MallocHook::InvokeDeleteHook(old_ptr);
+ do_free(old_ptr);
+ return new_ptr;
+ } else {
+ return old_ptr;
+ }
+}
+
+/*
+ * TODO
+ */
+#if defined(USE_INTELCC) || defined(_darwin_) || defined(_freebsd_) || defined(_STLPORT_VERSION)
+ #define OP_THROWNOTHING noexcept
+ #define OP_THROWBADALLOC
+#else
+ #define OP_THROWNOTHING
+ #define OP_THROWBADALLOC
+#endif
+
+static SpinLock set_new_handler_lock = SPINLOCK_INITIALIZER;
+
+static inline void* cpp_alloc(size_t size, bool nothrow) {
+ for (;;) {
+ void* p = do_malloc(size);
+#ifdef PREANSINEW
+ MallocHook::InvokeNewHook(p, size);
+ return p;
+#else
+ if (p == NULL) { // allocation failed
+ // Get the current new handler. NB: this function is not
+ // thread-safe. We make a feeble stab at making it so here, but
+ // this lock only protects against tcmalloc interfering with
+ // itself, not with other libraries calling set_new_handler.
+ std::new_handler nh;
+ {
+ SpinLockHolder h(&set_new_handler_lock);
+ nh = std::set_new_handler(0);
+ (void) std::set_new_handler(nh);
+ }
+ // If no new_handler is established, the allocation failed.
+ if (!nh) {
+ if (nothrow) return 0;
+ throw std::bad_alloc();
+ }
+ // Otherwise, try the new_handler. If it returns, retry the
+ // allocation. If it throws std::bad_alloc, fail the allocation.
+ // if it throws something else, don't interfere.
+ try {
+ (*nh)();
+ } catch (const std::bad_alloc&) {
+ if (!nothrow) throw;
+ MallocHook::InvokeNewHook(p, size);
+ return p;
+ }
+ } else { // allocation success
+ MallocHook::InvokeNewHook(p, size);
+ return p;
+ }
+#endif
+ }
+}
+
+#if !defined(YMAKE)
+void* operator new(size_t size) OP_THROWBADALLOC {
+ return cpp_alloc(size, false);
+}
+
+void* operator new(size_t size, const std::nothrow_t&) OP_THROWNOTHING {
+ return cpp_alloc(size, true);
+}
+
+void operator delete(void* p) OP_THROWNOTHING {
+ MallocHook::InvokeDeleteHook(p);
+ do_free(p);
+}
+
+void operator delete(void* p, const std::nothrow_t&) OP_THROWNOTHING {
+ MallocHook::InvokeDeleteHook(p);
+ do_free(p);
+}
+
+void* operator new[](size_t size) OP_THROWBADALLOC {
+ return cpp_alloc(size, false);
+}
+
+void* operator new[](size_t size, const std::nothrow_t&) OP_THROWNOTHING {
+ return cpp_alloc(size, true);
+}
+
+void operator delete[](void* p) OP_THROWNOTHING {
+ MallocHook::InvokeDeleteHook(p);
+ do_free(p);
+}
+
+void operator delete[](void* p, const std::nothrow_t&) OP_THROWNOTHING {
+ MallocHook::InvokeDeleteHook(p);
+ do_free(p);
+}
+#endif
+
+extern "C" void* memalign(size_t align, size_t size) {
+ void* result = do_memalign(align, size);
+ MallocHook::InvokeNewHook(result, size);
+ return result;
+}
+
+extern "C" int posix_memalign(void** result_ptr, size_t align, size_t size) {
+ if (((align % sizeof(void*)) != 0) ||
+ ((align & (align - 1)) != 0) ||
+ (align == 0)) {
+ return EINVAL;
+ }
+
+ void* result = do_memalign(align, size);
+ MallocHook::InvokeNewHook(result, size);
+ if (result == NULL) {
+ return ENOMEM;
+ } else {
+ *result_ptr = result;
+ return 0;
+ }
+}
+
+extern "C" void* valloc(size_t size) {
+ // Allocate page-aligned object of length >= size bytes
+ if (pagesize == 0) pagesize = getpagesize();
+ void* result = do_memalign(pagesize, size);
+ MallocHook::InvokeNewHook(result, size);
+ return result;
+}
+
+extern "C" void* pvalloc(size_t size) {
+ // Round up size to a multiple of pagesize
+ if (pagesize == 0) pagesize = getpagesize();
+ size = (size + pagesize - 1) & ~(pagesize - 1);
+ void* result = do_memalign(pagesize, size);
+ MallocHook::InvokeNewHook(result, size);
+ return result;
+}
+
+extern "C" void malloc_stats(void) {
+ PrintStats(1);
+}
+
+extern "C" int mallopt(int /*cmd*/, int /*value*/) {
+ return 1; // Indicates error
+}
+
+#if defined(__GLIBC__)
+extern "C" struct mallinfo mallinfo(void) {
+ TCMallocStats stats;
+ ExtractStats(&stats, NULL);
+
+ // Just some of the fields are filled in.
+ struct mallinfo info;
+ memset(&info, 0, sizeof(info));
+
+ // Unfortunately, the struct contains "int" field, so some of the
+ // size values will be truncated.
+ info.arena = static_cast<int>(stats.system_bytes);
+ info.fsmblks = static_cast<int>(stats.thread_bytes
+ + stats.central_bytes
+ + stats.transfer_bytes);
+ info.fordblks = static_cast<int>(stats.pageheap_bytes);
+ info.uordblks = static_cast<int>(stats.system_bytes
+ - stats.thread_bytes
+ - stats.central_bytes
+ - stats.transfer_bytes
+ - stats.pageheap_bytes);
+
+ return info;
+}
+#endif
+
+#if defined(_darwin_)
+extern "C" struct mstats mstats()
+{
+ TCMallocStats stats;
+ ExtractStats( &stats, NULL );
+
+ struct mstats info;
+ memset( &info, 0, sizeof( info ) );
+
+ return info;
+}
+#endif
+
+//-------------------------------------------------------------------
+// Some library routines on RedHat 9 allocate memory using malloc()
+// and free it using __libc_free() (or vice-versa). Since we provide
+// our own implementations of malloc/free, we need to make sure that
+// the __libc_XXX variants also point to the same implementations.
+//-------------------------------------------------------------------
+
+extern "C" {
+#if (defined(HAVE___ATTRIBUTE__) && !defined(_darwin_)) && 0
+ // Potentially faster variants that use the gcc alias extension
+#define ALIAS(x) __attribute__ ((weak, alias (x)))
+ void* __libc_malloc(size_t size) ALIAS("malloc");
+ void __libc_free(void* ptr) ALIAS("free");
+ void* __libc_realloc(void* ptr, size_t size) ALIAS("realloc");
+ void* __libc_calloc(size_t n, size_t size) ALIAS("calloc");
+ void __libc_cfree(void* ptr) ALIAS("cfree");
+ void* __libc_memalign(size_t align, size_t s) ALIAS("memalign");
+ void* __libc_valloc(size_t size) ALIAS("valloc");
+ void* __libc_pvalloc(size_t size) ALIAS("pvalloc");
+ int __posix_memalign(void** r, size_t a, size_t s) ALIAS("posix_memalign");
+#undef ALIAS
+#else
+ // Portable wrappers
+ void* __libc_malloc(size_t size) { return malloc(size); }
+ void __libc_free(void* ptr) { free(ptr); }
+ void* __libc_realloc(void* ptr, size_t size) { return realloc(ptr, size); }
+ void* __libc_calloc(size_t n, size_t size) { return calloc(n, size); }
+ void __libc_cfree(void* ptr) { cfree(ptr); }
+ void* __libc_memalign(size_t align, size_t s) { return memalign(align, s); }
+ void* __libc_valloc(size_t size) { return valloc(size); }
+ void* __libc_pvalloc(size_t size) { return pvalloc(size); }
+ int __posix_memalign(void** r, size_t a, size_t s) {
+ return posix_memalign(r, a, s);
+ }
+#endif
+}
+
+// Override __libc_memalign in libc on linux boxes specially.
+// They have a bug in libc that causes them to (very rarely) allocate
+// with __libc_memalign() yet deallocate with free() and the
+// definitions above don't catch it.
+// This function is an exception to the rule of calling MallocHook method
+// from the stack frame of the allocation function;
+// heap-checker handles this special case explicitly.
+static void *MemalignOverride(size_t align, size_t size, const void* /*caller*/) {
+ void* result = do_memalign(align, size);
+ MallocHook::InvokeNewHook(result, size);
+ return result;
+}
+
+#if !defined(__MALLOC_HOOK_VOLATILE)
+ #define __MALLOC_HOOK_VOLATILE
+#endif
+
+void *(* __MALLOC_HOOK_VOLATILE __memalign_hook)(size_t, size_t, const void *) = MemalignOverride;
diff --git a/contrib/deprecated/galloc/ya.make b/contrib/deprecated/galloc/ya.make
new file mode 100644
index 0000000000..8b823b1d0e
--- /dev/null
+++ b/contrib/deprecated/galloc/ya.make
@@ -0,0 +1,22 @@
+LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+NO_UTIL()
+
+NO_COMPILER_WARNINGS()
+
+IF (OS_DARWIN)
+ PEERDIR(
+ contrib/libs/gperftools
+ )
+ELSE()
+ SRCS(
+ galloc.cpp
+ hack.cpp
+ )
+ENDIF()
+
+END()
diff --git a/contrib/java/antlr/antlr4/antlr.jar b/contrib/java/antlr/antlr4/antlr.jar
new file mode 100644
index 0000000000..bb96df9510
--- /dev/null
+++ b/contrib/java/antlr/antlr4/antlr.jar
Binary files differ
diff --git a/contrib/libs/antlr4_cpp_runtime/CHANGES.txt b/contrib/libs/antlr4_cpp_runtime/CHANGES.txt
new file mode 100644
index 0000000000..b2eef10540
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/CHANGES.txt
@@ -0,0 +1,582 @@
+****************************************************************************
+As of ANTLR 4.2.1, March 25 2014, we are no longer updating this file. Instead,
+we are using the github release mechanism. For example, here is
+4.2.1 release notes:
+
+https://github.com/antlr/antlr4/releases/tag/4.2.1
+****************************************************************************
+
+ANTLR v4 Honey Badger
+
+January 15, 2014
+
+* Unit tests for lexer actions from yesterday.
+* Refactored TreeView so we can refresh tree externally w/o creating new one.
+ Needed for intellij plugin.
+
+January 14, 2014
+
+* Updated serialized ATN representation of lexer actions, allowing the lexer
+ interpreter to execute the majority of lexer commands (#408)
+
+January 12, 2014
+
+* Support executing precedence predicates during the SLL phase of
+ adaptivePredict (#401). The result is a massive performance boost for grammars
+ containing direct left-recursion (improvements of 5% to 1000+% have been
+ observed, depending on the grammar and input).
+
+December 29, 2013
+
+* Internal change: Tool.loadGrammar() -> parseGrammar(). Tool.load()->parse()
+
+* Added Tool.loadGrammar(fileName) that completely parses, extracts implicit lexer,
+ and processes into Grammar object. Does not geneate code. Use
+ Grammar.getImplicitLexer() to get the lexer created during processing of
+ combined grammar.
+
+* Added Grammar.load(fileName) that creates Tool object for you. loadGrammar()
+ lets you create your own Tool for setting error handlers etc...
+
+ final Grammar g = Grammar.load("/tmp/MyGrammar.g4");
+
+December 19, 2013
+
+* Sam:
+ Improved documentation for tree patterns classes
+ Refactored parts of the tree patterns API to simplify classes and improve encapsulation
+ Move ATN serializer to runtime
+ Use ATNDeserializer methods instead of ATNSimulator methods which are now deprecated
+
+* parrt: fix null pointer bug with rule "a : a;"
+
+November 24, 2013
+
+* Ter adds tree pattern matching. Preferred interface:
+
+ ParseTree t = parser.expr();
+ ParseTreePattern p = parser.compileParseTreePattern("<ID>+0", MyParser.RULE_expr);
+ ParseTreeMatch m = p.match(t);
+ String id = m.get("ID");
+
+ or
+
+ String xpath = "//blockStatement/*";
+ String treePattern = "int <Identifier> = <expression>;";
+ ParseTreePattern p =
+ parser.compileParseTreePattern(treePattern,
+ JavaParser.RULE_localVariableDeclarationStatement);
+ List<ParseTreeMatch> matches = p.findAll(tree, xpath);
+
+November 20, 2013
+
+* Sam added method stuff like expr() that calls expr(0). Makes it possible
+ to call expr rule from TestRig (grun).
+
+November 14, 2013
+
+* Added Sam's ParserInterpreter implementation that uses ATN after
+ deserialization.
+
+ LexerGrammar lg = new LexerGrammar(
+ "lexer grammar L;\n" +
+ "A : 'a' ;\n" +
+ "B : 'b' ;\n" +
+ "C : 'c' ;\n");
+ Grammar g = new Grammar(
+ "parser grammar T;\n" +
+ "s : (A{;}|B)* C ;\n",
+ lg);
+
+ LexerInterpreter lexEngine = lg.createLexerInterpreter(new ANTLRInputStream(input));
+ CommonTokenStream tokens = new CommonTokenStream(lexEngine);
+ ParserInterpreter parser = g.createParserInterpreter(tokens);
+ ParseTree t = parser.parse(g.rules.get(startRule).index);
+
+November 13, 2013
+
+* move getChildren() from Tree into Trees (to avoid breaking change)
+* Notation:
+ /prog/func, -> all funcs under prog at root
+ /prog/*, -> all children of prog at root
+ /*/func, -> all func kids of any root node
+ prog, -> prog must be root node
+ /prog, -> prog must be root node
+ /*, -> any root
+ *, -> any root
+ //ID, -> any ID in tree
+ //expr/primary/ID, -> any ID child of a primary under any expr
+ //body//ID, -> any ID under a body
+ //'return', -> any 'return' literal in tree
+ //primary/*, -> all kids of any primary
+ //func/*/stat, -> all stat nodes grandkids of any func node
+ /prog/func/'def', -> all def literal kids of func kid of prog
+ //stat/';', -> all ';' under any stat node
+ //expr/primary/!ID, -> anything but ID under primary under any expr node
+ //expr/!primary, -> anything but primary under any expr node
+ //!*, -> nothing anywhere
+ /!*, -> nothing at root
+
+September 16, 2013
+
+* Updated build.xml to support v4 grammars in v4 itself; compiles XPathLexer.g4
+* Add to XPath:
+ Collection<ParseTree> findAll(String xpath);
+
+September 11, 2013
+
+* Add ! operator to XPath
+* Use ANTLR v4 XPathLexer.g4 not regex
+* Copy lots of find node stuff from v3 GrammarAST to Trees class in runtime.
+
+September 10, 2013
+
+* Adding in XPath stuff.
+
+August 31, 2013
+
+* Lots of little fixes thanks to Coverity Scan
+
+August 7, 2013
+
+* [BREAKING CHANGE] Altered left-recursion elimination to be simpler. Now,
+ we use the following patterns:
+
+ * Binary expressions are expressions which contain a recursive invocation of
+ the rule as the first and last element of the alternative.
+
+ * Suffix expressions contain a recursive invocation of the rule as the first
+ element of the alternative, but not as the last element.
+
+ * Prefix expressions contain a recursive invocation of the rule as the last
+ element of the alternative, but not as the first element.
+
+There is no such thing as a "ternary" expression--they are just binary
+expressions in disguise.
+
+The right associativity specifiers no longer on the individual tokens because
+it's done on alternative basis anyway. The option is now on the individual
+alternative; e.g.,
+
+ e : e '*' e
+ | e '+' e
+ |<assoc=right> e '?' e ':' e
+ |<assoc=right> e '=' e
+ | INT
+ ;
+
+If your language uses a right-associative ternary operator, you will need
+to update your grammar to include <assoc=right> on the alternative operator.
+
+This also fixes #245 and fixes #268:
+
+https://github.com/antlr/antlr4/issues/245
+https://github.com/antlr/antlr4/issues/268
+
+To smooth the transition, <assoc=right> is still allowed on token references
+but it is ignored.
+
+June 30, 2013 -- 4.1 release
+
+June 24, 2013
+
+* Resize ANTLRInputStream.data after reading a file with fewer characters than
+ bytes
+* Fix ATN created for non-greedy optional block with multiple alternatives
+* Support Unicode escape sequences with indirection in JavaUnicodeInputStream
+ (fixes #287)
+* Remove the ParserRuleContext.altNum field (fixes #288)
+* PredictionContext no longer implements Iterable<SingletonPredictionContext>
+* PredictionContext no longer implements Comparable<PredictionContext>
+* Add the EPSILON_CLOSURE error and EPSILON_OPTIONAL warning
+* Optimized usage of closureBusy set (fixes #282)
+
+June 9, 2013
+
+* Add regression test for #239 (already passes)
+
+June 8, 2013
+
+* Support list labels on a set of tokens (fixes #270)
+* Fix associativity of XOR in Java LR grammar (fixes #280)
+
+June 1, 2013
+
+* DiagnosticErrorListener includes rule names for each decision in its reports
+* Document ANTLRErrorListener and DiagnosticErrorListener (fixes #265)
+* Support '\uFFFF' (fixes #267)
+* Optimize serialized ATN
+
+May 26, 2013
+
+* Report errors that occur while lexing a grammar (fixes #262)
+* Improved error message for unterminated string literals (fixes #243)
+
+May 24, 2013
+
+* Significantly improve performance of JavaUnicodeInputStream.LA(1)
+
+May 20, 2013
+
+* Generate Javadoc for generated visitor and listener interfaces and classes
+* Fix unit tests
+
+May 18, 2013
+
+* Group terminals in Java grammars so ATN can collapse sets
+* Improved Java 7 support in Java grammars (numeric literals)
+* Updated error listener interfaces
+* Support detailed statistics in TestPerformance
+
+May 17, 2013
+
+* Add JavaUnicodeInputStream to handle Unicode escapes in Java code
+* Proper Unicode identifier handling in Java grammars
+* Report file names with lexer errors in TestPerformance
+
+May 14, 2013
+
+* Use a called rule stack to prevent stack overflow in LL1Analyzer
+* Use 0-based indexing for several arrays in the tool
+* Code simplification, assertions, documentation
+
+May 13, 2013
+
+* Unit test updates to ensure exceptions are not hidden
+
+May 12, 2013
+
+* Updates to TestPerformance
+
+May 5, 2013
+
+* Updated several classes to use MurmurHash 3 hashing
+
+May 1, 2013
+
+* Added parse tree JTree to TreeViewer (Bart Kiers)
+
+April 30, 2013
+
+* Updated TestPerformance to support parallelization across passes
+
+April 24, 2013
+
+* Remove unused stub class ParserATNPathFinder
+* Remove ParserInterpreter.predictATN
+* Remove DFA.getATNStatesAlongPath
+* Encapsulate implementation methods in LexerATNSimulator and ParserATNSimulator
+* Updated documentation
+* Simplify creation of new DFA edges
+* Fix handling of previously cached error edges
+* Fix DFA created during forced-SLL parsing (PredictionMode.SLL)
+* Extract methods ParserATNSimulator.getExistingTargetState and
+ ParserATNSimulator.computeTargetState.
+
+April 22, 2013
+
+* Lazy initialization of ParserATNSimulator.mergeCache
+* Improved hash code for DFAState
+* Improved hash code with caching for ATNConfigSet
+* Add new configuration parameters to TestPerformance
+* Update Java LR and Java Std to support Java 7 syntax
+
+April 21, 2013
+
+* Add new configuration parameters to TestPerformance
+
+April 18, 2013
+
+* Must check rule transition follow states before eliminating states in
+ the ATN (fixes #224)
+* Simplify ParserATNSimulator and improve performance by combining execDFA and
+ execATN and using DFA edges even after edge computation is required
+
+April 15, 2013
+
+* Fix code in TestPerformance that clears the DFA
+
+April 12, 2013
+
+* Improved initialization and concurrency control in DFA updates
+* Fix EOF handling in edge case (fixes #218)
+
+April 4, 2013
+
+* Improved testing of error reporting
+* Fix NPE revealed by updated testing method
+* Strict handling of redefined rules - prevents code generation (fixes #210)
+* Updated documentation in Tool
+
+March 27, 2013
+
+* Avoid creating empty action methods in lexer (fixes #202)
+* Split serialized ATN when it exceeds Java's 65535 byte limit (fixes #76)
+* Fix incorrect reports of label type conflicts across separated labeled outer
+ alternatives (fixes #195)
+* Update Maven plugin site documentation
+
+March 26, 2013
+
+* Fix bugs with the closureBusy set in ParserATNSimulator.closure
+* Fix handling of empty options{} block (fixes #194)
+* Add error 149 INVALID_LEXER_COMMAND (fixes #190)
+* Add error 150 MISSING_LEXER_COMMAND_ARGUMENT
+* Add error 151 UNWANTED_LEXER_COMMAND_ARGUMENT
+* Updated documentation in the Parser and RecognitionException classes
+* Refactored and extensively documented the ANTLRErrorStrategy interface and
+ DefaultErrorStrategy default implementation
+* Track the number of syntax errors in Parser.notifyErrorListeners instead of in
+ the error strategy
+* Move primary implementation of getExpectedTokens to ATN, fixes #191
+* Updated ATN documentation
+* Use UUID instead of incremented integer for serialized ATN versioning
+
+March 7, 2013
+
+* Added export to PNG feature to the parse tree viewer
+
+March 6, 2013
+
+* Allow direct calls to left-recursive rules (fixes #161)
+* Change error type 146 (EPSILON_TOKEN) to a warning (fixes #180)
+* Specify locale for all format operations (fixes #158)
+* Fix generation of invalid Unicode escape sequences in Java code (fixes #164)
+* Do not require escape for $ in action when not followed by an ID start char
+ (fixes #176)
+
+February 23, 2013
+
+* Refactoring Target-related classes to improve support for additional language
+ targets
+
+February 22, 2013
+
+* Do not allow raw newline characters in literals
+* Pair and Triple are immutable; Triple is not a Pair
+
+February 5, 2013
+
+* Fix IntervalSet.add when multiple merges are required (fixes #153)
+
+January 29, 2013
+
+* don't call process() if args aren't specified (Dave Parfitt)
+
+January 21, 2013 -- Release 4.0
+
+* Updated PredictionContext Javadocs
+* Updated Maven site documentation
+* Minor tweaks in Java.stg
+
+January 15, 2013
+
+* Tweak error messages
+* (Tool) Make TokenVocabParser fields `protected final`
+* Fix generated escape sequences for literals containing backslashes
+
+January 14, 2013
+
+* Relax parser in favor of errors during semantic analysis
+* Add error 145: lexer mode must contain at least one non-fragment rule
+* Add error 146: non-fragment lexer rule can match the empty string
+
+January 11, 2013
+
+* Updated error 72, 76; added 73-74 and 136-143: detailed errors about name
+ conflicts
+* Report exact location for parameter/retval/local name conflicts
+* Add error 144: multi-character literals are not allowed in lexer sets
+* Error 134 now only applies to rule references in lexer sets
+* Updated error messages (cleanup)
+* Reduce size of _serializedATN by adding 2 to each element: new representation
+ avoids embedded values 0 and 0xFFFF which are common and have multi-byte
+ representations in Java's modified UTF-8
+
+January 10, 2013
+
+* Add error 135: cannot assign a value to list label: $label
+ (fixes antlr/antlr4#128)
+
+January 2, 2013
+
+* Fix EOF handling (antlr/antlr4#110)
+* Remove TREE_PARSER reference
+* Additional validation checks in ATN deserialization
+* Fix potential NPE in parser predicate evaluation
+* Fix termination condition detection in full-context parsing
+
+January 1, 2013
+
+* Updated documentation
+* Minor code cleanup
+* Added the `-XdbgSTWait` command line option for the Tool
+* Removed method override since bug was fixed in V3 runtime
+
+December 31, 2012
+
+* I altered Target.getTargetStringLiteralFromANTLRStringLiteral() so that
+ it converts \uXXXX in an ANTLR string to \\uXXXX, thus, avoiding Java's
+ conversion to a single character before compilation.
+
+December 16, 2012
+
+* Encapsulate some fields in ANTLRMessage
+* Remove ErrorType.INVALID
+* Update error/warning messages, show all v3 compatibility messages
+
+December 12, 2012
+
+* Use arrays instead of HashSet to save memory in SemanticContext.AND/OR
+* Use arrays instead of HashSet to save memory in cached DFA
+* Reduce single-operand SemanticContext.and/or operations
+
+December 11, 2012
+
+* Add -long-messages option; only show exceptions with errors when set
+* "warning treated as error" is a one-off error
+* Listen for issues reported by StringTemplate, report them as warnings
+* Fix template issues
+* GrammarASTWithOptions.getOptions never returns null
+* Use EnumSet instead of HashSet
+* Use new STGroup.GROUP_FILE_EXTENSION value
+
+December 2, 2012
+
+* Remove -Xverbose-dfa option
+* Create the ParseTreeVisitor interface for all visitors, rename previous base
+ visitor class to AbstractParseTreeVisitor
+
+December 1, 2012
+
+* escape [\n\r\t] in lexical error messages; e.g,:
+ line 2:3 token recognition error at: '\t'
+ line 2:4 token recognition error at: '\n'
+
+* added error for bad sets in lexer; e.g.:
+ lexer set element A is invalid (either rule ref or literal with > 1 char)
+ some tests in TestSets appeared to allow ~('a'|B) but it was randomly working.
+ ('a'|B) works, though doesn't collapse to a set.
+
+* label+='foo' wasn't generating good code. It was generating token type as
+ variable name. Now, I gen "s<ttype>" for implicit labels on string literals.
+
+* tokens now have token and char source to draw from.
+
+* remove -Xsave-lexer option; log file as implicit lexer AST.
+
+November 30, 2012
+
+* Maven updates (cleanup, unification, and specify Java 6 bootstrap classpath)
+
+November 28, 2012
+
+* Maven updates (uber-jar, manifest details)
+
+November 27, 2012
+
+* Maven updates (prepare for deploying to Sonatype OSS)
+* Use efficient bitset tests instead of long chains of operator ==
+
+November 26, 2012
+
+* Maven updates (include sources and javadocs, fix warnings)
+* Don't generate action methods for lexer rules not containing an action
+* Generated action and sempred methods are private
+* Remove unused / problematic methods:
+** (unused) TerminalNodeImpl.isErrorNode
+** (unused) RuleContext.conflictsWith, RuleContext.suffix.
+** (problematic) RuleContext.hashCode, RuleContext.equals.
+
+November 23, 2012
+
+* Updated Maven build (added master POM, cleaned up module POMs)
+
+November 22, 2012
+
+* make sure left-recur rule translation uses token stream from correct imported file.
+* actions like @after in imported rules caused inf loop.
+* This misidentified scope lexer/parser: @lexer::members { } @parser::members { }
+
+November 18, 2012
+
+* fixed: undefined rule refs caused exception
+* cleanup, rm dead etypes, add check for ids that cause code gen issues
+* added notion of one-off error
+* added check for v3 backward incompatibilities:
+** tree grammars
+** labels in lexer rules
+** tokens {A;B;} syntax
+** tokens {A='C';} syntax
+** {...}?=> gate semantic predicates
+** (...)=> syntactic predicates
+* Detect EOF in lexer rule
+
+November 17, 2012
+
+* .tokens files goes in output dir like parser file.
+* added check: action in lexer rules must be last element of outermost alt
+* properly check for grammar/filename difference
+* if labels, don't allow set collapse for
+ a : A # X | B ;
+* wasn't checking soon enough for rule redef; now it sets a dead flag in
+ AST so no more walking dup.
+ error(51): T.g:7:0: rule s redefinition (ignoring); previous at line 3
+
+November 11, 2012
+
+* Change version to 4.0b4 (btw, forgot to push 4.0b3 in build.properties when
+ I made git tag 4.0b3...ooops).
+
+November 4, 2012
+
+* Kill box in tree dialog box makes dialog dispose of itself
+
+October 29, 2012
+
+* Sam fixes nongreedy more.
+* -Werror added.
+* Sam made speed improvement re preds in lexer.
+
+October 20, 2012
+
+* Merged Sam's fix for nongreedy lexer/parser. lots of unit tests. A fix in
+ prediction ctx merge. https://github.com/parrt/antlr4/pull/99
+
+October 14, 2012
+
+* Rebuild how ANTLR detects SLL conflict and failover to full LL. LL is
+ a bit slower but correct now. Added ability to ask for exact ambiguity
+ detection.
+
+October 8, 2012
+
+* Fixed a bug where labeling the alternatives of the start rule caused
+ a null pointer exception.
+
+October 1, 2012 -- 4.0b2 release
+
+September 30, 2012
+
+* Fixed the unbuffered streams, which actually buffered everything
+ up by mistake. tweaked a few comments.
+
+* Added a getter to IntStream for the token factory
+
+* Added -depend cmd-line option.
+
+September 29, 2012
+
+* no nongreedy or wildcard in parser.
+
+September 28, 2012
+
+* empty "tokens {}" is ok now.
+
+September 22, 2012
+
+* Rule exception handlers weren't passed to the generated code
+* $ruleattribute.foo weren't handled properly
+* Added -package option
+
+September 18, 2012 -- 4.0b1 release
diff --git a/contrib/libs/antlr4_cpp_runtime/CONTRIBUTING.md b/contrib/libs/antlr4_cpp_runtime/CONTRIBUTING.md
new file mode 100644
index 0000000000..0a2317bab3
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/CONTRIBUTING.md
@@ -0,0 +1,22 @@
+# Contributing to ANTLR 4
+
+1. [Fork](https://help.github.com/articles/fork-a-repo) the [antlr/antlr4 repo](https://github.com/antlr/antlr4), which will give you both key branches, `master` and `dev`
+2. Make sure to `git checkout dev` in your fork so that you are working from the latest development branch
+3. Create and work from a branch derived from `dev` such as `git checkout -b your-branch-name`
+4. Install and configure [EditorConfig](http://editorconfig.org/) so your text editor or IDE uses the ANTLR 4 coding style
+5. [Build ANTLR 4](doc/building-antlr.md)
+6. [Run the ANTLR project unit tests](doc/antlr-project-testing.md)
+7. Create a [pull request](https://help.github.com/articles/using-pull-requests/) with your changes and make sure you're comparing your `dev`-derived branch in your fork to the `dev` branch from the `antlr/antlr4` repo:
+
+<img src="doc/images/PR-on-dev.png" width="600">
+
+**Note:** Each commit requires a "signature", which is simple as using `-s` (not
+`-S`) to the git commit command:
+
+```
+git commit -s -m 'This is my commit message'
+```
+
+Github's pull request process enforces the sig and gives instructions on how to
+fix any commits that lack the sig. See [Github DCO app](https://github.com/apps/dco)
+for more info.
diff --git a/contrib/libs/antlr4_cpp_runtime/LICENSE.txt b/contrib/libs/antlr4_cpp_runtime/LICENSE.txt
new file mode 100644
index 0000000000..5d27694155
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/LICENSE.txt
@@ -0,0 +1,28 @@
+Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
+
+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. Neither name of copyright holders nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``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 REGENTS OR
+CONTRIBUTORS 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/contrib/libs/antlr4_cpp_runtime/README-cpp.md b/contrib/libs/antlr4_cpp_runtime/README-cpp.md
new file mode 100644
index 0000000000..622289ba77
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/README-cpp.md
@@ -0,0 +1,72 @@
+# C++ target for ANTLR 4
+
+This folder contains the C++ runtime support for ANTLR. See [the canonical antlr4 repository](https://github.com/antlr/antlr4) for in depth detail about how to use ANTLR 4.
+
+## Authors and major contributors
+
+ANTLR 4 is the result of substantial effort of the following people:
+
+* [Terence Parr](http://www.cs.usfca.edu/~parrt/), parrt@cs.usfca.edu
+ ANTLR project lead and supreme dictator for life
+ [University of San Francisco](http://www.usfca.edu/)
+* [Sam Harwell](http://tunnelvisionlabs.com/)
+ Tool co-author, Java and C# target)
+
+The C++ target has been the work of the following people:
+
+* Dan McLaughlin, dan.mclaughlin@gmail.com (initial port, got code to compile)
+* David Sisson, dsisson@google.com (initial port, made the runtime C++ tests runnable)
+* [Mike Lischke](http://www.soft-gems.net), mike@lischke-online.de (brought the initial port to a working library, made most runtime tests passing)
+
+## Other contributors
+
+* Marcin Szalowicz, mszalowicz@mailplus.pl (cmake build setup)
+* Tim O'Callaghan, timo@linux.com (additional superbuild cmake pattern script)
+
+## Project Status
+
+* Building on macOS, Windows, Android and Linux
+* No errors and warnings
+* Library linking
+* Some unit tests in the macOS project, for important base classes with almost 100% code coverage.
+* All memory allocations checked
+* Simple command line demo application working on all supported platforms.
+* All runtime tests pass.
+
+### Build + Usage Notes
+
+The minimum C++ version to compile the ANTLR C++ runtime with is C++11. The supplied projects can built the runtime either as static or dynamic library, as both 32bit and 64bit arch. The macOS project contains a target for iOS and can also be built using cmake (instead of XCode).
+
+Include the antlr4-runtime.h umbrella header in your target application to get everything needed to use the library.
+
+If you are compiling with cmake, the minimum version required is cmake 2.8.
+By default, the libraries produced by the CMake build target C++11. If you want to target a different C++ standard, you can explicitly pass the standard - e.g. `-DCMAKE_CXX_STANDARD=17`.
+
+#### Compiling on Windows with Visual Studio using he Visual Studio projects
+Simply open the VS project from the runtime folder (VS 2019+) and build it.
+
+#### Compiling on Windows using cmake with Visual Studio VS2019 and later
+Use the "Open Folder" Feature from the File->Open->Folder menu to open the runtime/Cpp directory.
+It will automatically use the CMake description to open up a Visual Studio Solution.
+
+#### Compiling on macOS
+Either open the included XCode project and build that or use the cmake compilation as described for linux.
+
+#### Compiling on Android
+Try run cmake -DCMAKE_ANDROID_NDK=/folder/of/android_ndkr17_and_above -DCMAKE_SYSTEM_NAME=Android -DCMAKE_ANDROID_API=14 -DCMAKE_ANDROID_ARCH_ABI=x86 -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_BUILD_TYPE=Release /folder/antlr4_src_dir -G Ninja.
+
+#### Compiling on Linux
+- cd \<antlr4-dir\>/runtime/Cpp (this is where this readme is located)
+- mkdir build && mkdir run && cd build
+- cmake .. -DANTLR_JAR_LOCATION=full/path/to/antlr4-4.5.4-SNAPSHOT.jar -DWITH_DEMO=True
+- make
+- DESTDIR=\<antlr4-dir\>/runtime/Cpp/run make install
+
+If you don't want to build the demo then replace the "cmake .. -DANTLR_JAR_LOCATION<...>" command in the above recipe with "cmake .." without any further parameters.
+There is another cmake script available in the subfolder cmake/ for those who prefer the superbuild cmake pattern.
+
+#### CMake Package support
+If the CMake variable 'ANTLR4_INSTALL' is set, CMake Packages will be build and installed during the install step.
+They expose two packages: antlr4_runtime and antlr4_generator which can be referenced to ease up the use of the
+ANTLR Generator and runtime.
+Use and Sample can be found [here](cmake/Antlr4Package.md)
diff --git a/contrib/libs/antlr4_cpp_runtime/README.md b/contrib/libs/antlr4_cpp_runtime/README.md
new file mode 100644
index 0000000000..5566fa224d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/README.md
@@ -0,0 +1,84 @@
+# ANTLR v4
+
+[![Java 7+](https://img.shields.io/badge/java-7+-4c7e9f.svg)](http://java.oracle.com)
+[![License](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/antlr/antlr4/master/LICENSE.txt)
+
+
+**ANTLR** (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files. It's widely used to build languages, tools, and frameworks. From a grammar, ANTLR generates a parser that can build parse trees and also generates a listener interface (or visitor) that makes it easy to respond to the recognition of phrases of interest.
+
+**Dev branch build status**
+
+[![MacOSX, Windows, Linux](https://github.com/antlr/antlr4/actions/workflows/hosted.yml/badge.svg)](https://github.com/antlr/antlr4/actions/workflows/hosted.yml) (github actions)
+
+<!--
+* [![Windows](https://github.com/antlr/antlr4/actions/workflows/windows.yml/badge.svg?branch=dev)](https://github.com/antlr/antlr4/actions/workflows/windows.yml) (github actions)
+
+* [![Circle CI Build Status (Linux)](https://img.shields.io/circleci/build/gh/antlr/antlr4/master?label=Linux)](https://app.circleci.com/pipelines/github/antlr/antlr4) (CircleCI)
+
+[![AppVeyor CI Build Status (Windows)](https://img.shields.io/appveyor/build/parrt/antlr4?label=Windows)](https://ci.appveyor.com/project/parrt/antlr4)
+[![Travis-CI Build Status (Swift-Linux)](https://img.shields.io/travis/antlr/antlr4.svg?label=Linux-Swift&branch=master)](https://travis-ci.com/github/antlr/antlr4)
+-->
+
+## Repo branch structure
+
+The default branch for this repo is [`master`](https://github.com/antlr/antlr4/tree/master), which is the latest stable release and has tags for the various releases; e.g., see release tag [4.9.3](https://github.com/antlr/antlr4/tree/4.9.3). Branch [`dev`](https://github.com/antlr/antlr4/tree/dev) is where development occurs between releases and all pull requests should be derived from that branch. The `dev` branch is merged back into `master` to cut a release and the release state is tagged (e.g., with `4.10-rc1` or `4.10`.) Visually our process looks roughly like this:
+
+<img src="doc/images/new-antlr-branches.png" width="500">
+
+Targets such as Go that pull directly from the repository can use the default `master` branch but can also pull from the active `dev` branch:
+
+```bash
+$ go get github.com/antlr/antlr4/runtime/Go/antlr@dev
+```
+
+## Authors and major contributors
+
+* [Terence Parr](http://www.cs.usfca.edu/~parrt/), parrt@cs.usfca.edu
+ANTLR project lead and supreme dictator for life
+[University of San Francisco](http://www.usfca.edu/)
+* [Sam Harwell](http://tunnelvisionlabs.com/) (Tool co-author, Java and original C# target)
+* [Eric Vergnaud](https://github.com/ericvergnaud) (Javascript, Python2, Python3 targets and maintenance of C# target)
+* [Peter Boyer](https://github.com/pboyer) (Go target)
+* [Mike Lischke](http://www.soft-gems.net/) (C++ completed target)
+* Dan McLaughlin (C++ initial target)
+* David Sisson (C++ initial target and test)
+* [Janyou](https://github.com/janyou) (Swift target)
+* [Ewan Mellor](https://github.com/ewanmellor), [Hanzhou Shi](https://github.com/hanjoes) (Swift target merging)
+* [Ben Hamilton](https://github.com/bhamiltoncx) (Full Unicode support in serialized ATN and all languages' runtimes for code points > U+FFFF)
+* [Marcos Passos](https://github.com/marcospassos) (PHP target)
+* [Lingyu Li](https://github.com/lingyv-li) (Dart target)
+* [Ivan Kochurkin](https://github.com/KvanTTT) has made major contributions to overall quality, error handling, and Target performance.
+* [Justin King](https://github.com/jcking) has done a huge amount of work across multiple targets, but especially for C++.
+* [Ken Domino](https://github.com/kaby76) has a knack for finding bugs/issues and analysis; also a major contributor on the [grammars-v4 repo](https://github.com/antlr/grammars-v4).
+* [Jim Idle](https://github.com/jimidle) has contributed to previous versions of ANTLR and recently jumped back in to solve a major problem with the Go target.
+
+
+## Useful information
+
+* [Release notes](https://github.com/antlr/antlr4/releases)
+* [Getting started with v4](https://github.com/antlr/antlr4/blob/master/doc/getting-started.md)
+* [Official site](http://www.antlr.org/)
+* [Documentation](https://github.com/antlr/antlr4/blob/master/doc/index.md)
+* [FAQ](https://github.com/antlr/antlr4/blob/master/doc/faq/index.md)
+* [ANTLR code generation targets](https://github.com/antlr/antlr4/blob/master/doc/targets.md)<br>(Currently: Java, C#, Python2|3, JavaScript, Go, C++, Swift, Dart, PHP)
+* [Java API](http://www.antlr.org/api/Java/index.html)
+* [ANTLR v3](http://www.antlr3.org/)
+* [v3 to v4 Migration, differences](https://github.com/antlr/antlr4/blob/master/doc/faq/general.md)
+
+You might also find the following pages useful, particularly if you want to mess around with the various target languages.
+
+* [How to build ANTLR itself](https://github.com/antlr/antlr4/blob/master/doc/building-antlr.md)
+* [How we create and deploy an ANTLR release](https://github.com/antlr/antlr4/blob/master/doc/releasing-antlr.md)
+
+## The Definitive ANTLR 4 Reference
+
+Programmers run into parsing problems all the time. Whether it’s a data format like JSON, a network protocol like SMTP, a server configuration file for Apache, a PostScript/PDF file, or a simple spreadsheet macro language—ANTLR v4 and this book will demystify the process. ANTLR v4 has been rewritten from scratch to make it easier than ever to build parsers and the language applications built on top. This completely rewritten new edition of the bestselling Definitive ANTLR Reference shows you how to take advantage of these new features.
+
+You can buy the book [The Definitive ANTLR 4 Reference](http://amzn.com/1934356999) at amazon or an [electronic version at the publisher's site](https://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference).
+
+You will find the [Book source code](http://pragprog.com/titles/tpantlr2/source_code) useful.
+
+## Additional grammars
+[This repository](https://github.com/antlr/grammars-v4) is a collection of grammars without actions where the
+root directory name is the all-lowercase name of the language parsed
+by the grammar. For example, java, cpp, csharp, c, etc...
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorListener.cpp b/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorListener.cpp
new file mode 100644
index 0000000000..6ceadb87f9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorListener.cpp
@@ -0,0 +1,10 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "ANTLRErrorListener.h"
+
+antlr4::ANTLRErrorListener::~ANTLRErrorListener()
+{
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorListener.h b/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorListener.h
new file mode 100644
index 0000000000..6dc66237e4
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorListener.h
@@ -0,0 +1,167 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "RecognitionException.h"
+
+namespace antlrcpp {
+ class BitSet;
+}
+
+namespace antlr4 {
+
+ /// How to emit recognition errors (an interface in Java).
+ class ANTLR4CPP_PUBLIC ANTLRErrorListener {
+ public:
+ virtual ~ANTLRErrorListener();
+
+ /// <summary>
+ /// Upon syntax error, notify any interested parties. This is not how to
+ /// recover from errors or compute error messages. <seealso cref="ANTLRErrorStrategy"/>
+ /// specifies how to recover from syntax errors and how to compute error
+ /// messages. This listener's job is simply to emit a computed message,
+ /// though it has enough information to create its own message in many cases.
+ /// <p/>
+ /// The <seealso cref="RecognitionException"/> is non-null for all syntax errors except
+ /// when we discover mismatched token errors that we can recover from
+ /// in-line, without returning from the surrounding rule (via the single
+ /// token insertion and deletion mechanism).
+ /// </summary>
+ /// <param name="recognizer">
+ /// What parser got the error. From this
+ /// object, you can access the context as well
+ /// as the input stream. </param>
+ /// <param name="offendingSymbol">
+ /// The offending token in the input token
+ /// stream, unless recognizer is a lexer (then it's null). If
+ /// no viable alternative error, {@code e} has token at which we
+ /// started production for the decision. </param>
+ /// <param name="line">
+ /// The line number in the input where the error occurred. </param>
+ /// <param name="charPositionInLine">
+ /// The character position within that line where the error occurred. </param>
+ /// <param name="msg">
+ /// The message to emit. </param>
+ /// <param name="e">
+ /// The exception generated by the parser that led to
+ /// the reporting of an error. It is null in the case where
+ /// the parser was able to recover in line without exiting the
+ /// surrounding rule. </param>
+ virtual void syntaxError(Recognizer *recognizer, Token *offendingSymbol, size_t line,
+ size_t charPositionInLine, const std::string &msg, std::exception_ptr e) = 0;
+
+ /**
+ * This method is called by the parser when a full-context prediction
+ * results in an ambiguity.
+ *
+ * <p>Each full-context prediction which does not result in a syntax error
+ * will call either {@link #reportContextSensitivity} or
+ * {@link #reportAmbiguity}.</p>
+ *
+ * <p>When {@code ambigAlts} is not null, it contains the set of potentially
+ * viable alternatives identified by the prediction algorithm. When
+ * {@code ambigAlts} is null, use {@link ATNConfigSet#getAlts} to obtain the
+ * represented alternatives from the {@code configs} argument.</p>
+ *
+ * <p>When {@code exact} is {@code true}, <em>all</em> of the potentially
+ * viable alternatives are truly viable, i.e. this is reporting an exact
+ * ambiguity. When {@code exact} is {@code false}, <em>at least two</em> of
+ * the potentially viable alternatives are viable for the current input, but
+ * the prediction algorithm terminated as soon as it determined that at
+ * least the <em>minimum</em> potentially viable alternative is truly
+ * viable.</p>
+ *
+ * <p>When the {@link PredictionMode#LL_EXACT_AMBIG_DETECTION} prediction
+ * mode is used, the parser is required to identify exact ambiguities so
+ * {@code exact} will always be {@code true}.</p>
+ *
+ * <p>This method is not used by lexers.</p>
+ *
+ * @param recognizer the parser instance
+ * @param dfa the DFA for the current decision
+ * @param startIndex the input index where the decision started
+ * @param stopIndex the input input where the ambiguity was identified
+ * @param exact {@code true} if the ambiguity is exactly known, otherwise
+ * {@code false}. This is always {@code true} when
+ * {@link PredictionMode#LL_EXACT_AMBIG_DETECTION} is used.
+ * @param ambigAlts the potentially ambiguous alternatives, or {@code null}
+ * to indicate that the potentially ambiguous alternatives are the complete
+ * set of represented alternatives in {@code configs}
+ * @param configs the ATN configuration set where the ambiguity was
+ * identified
+ */
+ virtual void reportAmbiguity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact,
+ const antlrcpp::BitSet &ambigAlts, atn::ATNConfigSet *configs) = 0;
+
+ /**
+ * This method is called when an SLL conflict occurs and the parser is about
+ * to use the full context information to make an LL decision.
+ *
+ * <p>If one or more configurations in {@code configs} contains a semantic
+ * predicate, the predicates are evaluated before this method is called. The
+ * subset of alternatives which are still viable after predicates are
+ * evaluated is reported in {@code conflictingAlts}.</p>
+ *
+ * <p>This method is not used by lexers.</p>
+ *
+ * @param recognizer the parser instance
+ * @param dfa the DFA for the current decision
+ * @param startIndex the input index where the decision started
+ * @param stopIndex the input index where the SLL conflict occurred
+ * @param conflictingAlts The specific conflicting alternatives. If this is
+ * {@code null}, the conflicting alternatives are all alternatives
+ * represented in {@code configs}. At the moment, conflictingAlts is non-null
+ * (for the reference implementation, but Sam's optimized version can see this
+ * as null).
+ * @param configs the ATN configuration set where the SLL conflict was
+ * detected
+ */
+ virtual void reportAttemptingFullContext(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ const antlrcpp::BitSet &conflictingAlts, atn::ATNConfigSet *configs) = 0;
+
+ /**
+ * This method is called by the parser when a full-context prediction has a
+ * unique result.
+ *
+ * <p>Each full-context prediction which does not result in a syntax error
+ * will call either {@link #reportContextSensitivity} or
+ * {@link #reportAmbiguity}.</p>
+ *
+ * <p>For prediction implementations that only evaluate full-context
+ * predictions when an SLL conflict is found (including the default
+ * {@link ParserATNSimulator} implementation), this method reports cases
+ * where SLL conflicts were resolved to unique full-context predictions,
+ * i.e. the decision was context-sensitive. This report does not necessarily
+ * indicate a problem, and it may appear even in completely unambiguous
+ * grammars.</p>
+ *
+ * <p>{@code configs} may have more than one represented alternative if the
+ * full-context prediction algorithm does not evaluate predicates before
+ * beginning the full-context prediction. In all cases, the final prediction
+ * is passed as the {@code prediction} argument.</p>
+ *
+ * <p>Note that the definition of "context sensitivity" in this method
+ * differs from the concept in {@link DecisionInfo#contextSensitivities}.
+ * This method reports all instances where an SLL conflict occurred but LL
+ * parsing produced a unique result, whether or not that unique result
+ * matches the minimum alternative in the SLL conflicting set.</p>
+ *
+ * <p>This method is not used by lexers.</p>
+ *
+ * @param recognizer the parser instance
+ * @param dfa the DFA for the current decision
+ * @param startIndex the input index where the decision started
+ * @param stopIndex the input index where the context sensitivity was
+ * finally determined
+ * @param prediction the unambiguous result of the full-context prediction
+ * @param configs the ATN configuration set where the unambiguous prediction
+ * was determined
+ */
+ virtual void reportContextSensitivity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ size_t prediction, atn::ATNConfigSet *configs) = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorStrategy.cpp b/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorStrategy.cpp
new file mode 100644
index 0000000000..1655a5731d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorStrategy.cpp
@@ -0,0 +1,10 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "ANTLRErrorStrategy.h"
+
+antlr4::ANTLRErrorStrategy::~ANTLRErrorStrategy()
+{
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorStrategy.h b/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorStrategy.h
new file mode 100644
index 0000000000..a3eecd14c4
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ANTLRErrorStrategy.h
@@ -0,0 +1,121 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Token.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// The interface for defining strategies to deal with syntax errors encountered
+ /// during a parse by ANTLR-generated parsers. We distinguish between three
+ /// different kinds of errors:
+ ///
+ /// <ul>
+ /// <li>The parser could not figure out which path to take in the ATN (none of
+ /// the available alternatives could possibly match)</li>
+ /// <li>The current input does not match what we were looking for</li>
+ /// <li>A predicate evaluated to false</li>
+ /// </ul>
+ ///
+ /// Implementations of this interface report syntax errors by calling
+ /// <seealso cref="Parser#notifyErrorListeners"/>.
+ /// <p/>
+ /// TODO: what to do about lexers
+ /// </summary>
+ class ANTLR4CPP_PUBLIC ANTLRErrorStrategy {
+ public:
+
+ /// <summary>
+ /// Reset the error handler state for the specified {@code recognizer}. </summary>
+ /// <param name="recognizer"> the parser instance </param>
+ virtual ~ANTLRErrorStrategy();
+
+ virtual void reset(Parser *recognizer) = 0;
+
+ /**
+ * This method is called when an unexpected symbol is encountered during an
+ * inline match operation, such as {@link Parser#match}. If the error
+ * strategy successfully recovers from the match failure, this method
+ * returns the {@link Token} instance which should be treated as the
+ * successful result of the match.
+ *
+ * <p>This method handles the consumption of any tokens - the caller should
+ * <b>not</b> call {@link Parser#consume} after a successful recovery.</p>
+ *
+ * <p>Note that the calling code will not report an error if this method
+ * returns successfully. The error strategy implementation is responsible
+ * for calling {@link Parser#notifyErrorListeners} as appropriate.</p>
+ *
+ * @param recognizer the parser instance
+ * @throws RecognitionException if the error strategy was not able to
+ * recover from the unexpected input symbol
+ */
+ virtual Token* recoverInline(Parser *recognizer) = 0;
+
+ /// <summary>
+ /// This method is called to recover from exception {@code e}. This method is
+ /// called after <seealso cref="#reportError"/> by the default exception handler
+ /// generated for a rule method.
+ /// </summary>
+ /// <seealso cref= #reportError
+ /// </seealso>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <param name="e"> the recognition exception to recover from </param>
+ /// <exception cref="RecognitionException"> if the error strategy could not recover from
+ /// the recognition exception </exception>
+ virtual void recover(Parser *recognizer, std::exception_ptr e) = 0;
+
+ /// <summary>
+ /// This method provides the error handler with an opportunity to handle
+ /// syntactic or semantic errors in the input stream before they result in a
+ /// <seealso cref="RecognitionException"/>.
+ /// <p/>
+ /// The generated code currently contains calls to <seealso cref="#sync"/> after
+ /// entering the decision state of a closure block ({@code (...)*} or
+ /// {@code (...)+}).
+ /// <p/>
+ /// For an implementation based on Jim Idle's "magic sync" mechanism, see
+ /// <seealso cref="DefaultErrorStrategy#sync"/>.
+ /// </summary>
+ /// <seealso cref= DefaultErrorStrategy#sync
+ /// </seealso>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <exception cref="RecognitionException"> if an error is detected by the error
+ /// strategy but cannot be automatically recovered at the current state in
+ /// the parsing process </exception>
+ virtual void sync(Parser *recognizer) = 0;
+
+ /// <summary>
+ /// Tests whether or not {@code recognizer} is in the process of recovering
+ /// from an error. In error recovery mode, <seealso cref="Parser#consume"/> adds
+ /// symbols to the parse tree by calling
+ /// {@link Parser#createErrorNode(ParserRuleContext, Token)} then
+ /// {@link ParserRuleContext#addErrorNode(ErrorNode)} instead of
+ /// {@link Parser#createTerminalNode(ParserRuleContext, Token)}.
+ /// </summary>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <returns> {@code true} if the parser is currently recovering from a parse
+ /// error, otherwise {@code false} </returns>
+ virtual bool inErrorRecoveryMode(Parser *recognizer) = 0;
+
+ /// <summary>
+ /// This method is called by when the parser successfully matches an input
+ /// symbol.
+ /// </summary>
+ /// <param name="recognizer"> the parser instance </param>
+ virtual void reportMatch(Parser *recognizer) = 0;
+
+ /// <summary>
+ /// Report any kind of <seealso cref="RecognitionException"/>. This method is called by
+ /// the default exception handler generated for a rule method.
+ /// </summary>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <param name="e"> the recognition exception to report </param>
+ virtual void reportError(Parser *recognizer, const RecognitionException &e) = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ANTLRFileStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/ANTLRFileStream.cpp
new file mode 100644
index 0000000000..674817ac0e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ANTLRFileStream.cpp
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "ANTLRFileStream.h"
+
+using namespace antlr4;
+
+void ANTLRFileStream::loadFromFile(const std::string &fileName) {
+ _fileName = fileName;
+ if (_fileName.empty()) {
+ return;
+ }
+
+ std::ifstream stream(fileName, std::ios::binary);
+
+ ANTLRInputStream::load(stream);
+}
+
+std::string ANTLRFileStream::getSourceName() const {
+ return _fileName;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ANTLRFileStream.h b/contrib/libs/antlr4_cpp_runtime/src/ANTLRFileStream.h
new file mode 100644
index 0000000000..6c7d619a00
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ANTLRFileStream.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "ANTLRInputStream.h"
+
+namespace antlr4 {
+
+ /// This is an ANTLRInputStream that is loaded from a file all at once
+ /// when you construct the object (or call load()).
+ // TODO: this class needs testing.
+ class ANTLR4CPP_PUBLIC ANTLRFileStream : public ANTLRInputStream {
+ public:
+ ANTLRFileStream() = default;
+ ANTLRFileStream(const std::string &) = delete;
+ ANTLRFileStream(const char *data, size_t length) = delete;
+ ANTLRFileStream(std::istream &stream) = delete;
+
+ // Assumes a file name encoded in UTF-8 and file content in the same encoding (with or w/o BOM).
+ virtual void loadFromFile(const std::string &fileName);
+ virtual std::string getSourceName() const override;
+
+ private:
+ std::string _fileName; // UTF-8 encoded file name.
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.cpp
new file mode 100644
index 0000000000..b6470af9b7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.cpp
@@ -0,0 +1,180 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include <string.h>
+
+#include "Exceptions.h"
+#include "misc/Interval.h"
+#include "IntStream.h"
+
+#include "support/Utf8.h"
+#include "support/CPPUtils.h"
+
+#include "ANTLRInputStream.h"
+
+using namespace antlr4;
+using namespace antlrcpp;
+
+using misc::Interval;
+
+ANTLRInputStream::ANTLRInputStream() {
+ InitializeInstanceFields();
+}
+
+ANTLRInputStream::ANTLRInputStream(std::string_view input): ANTLRInputStream() {
+ load(input.data(), input.length());
+}
+
+ANTLRInputStream::ANTLRInputStream(const char *data, size_t length) {
+ load(data, length);
+}
+
+ANTLRInputStream::ANTLRInputStream(std::istream &stream): ANTLRInputStream() {
+ load(stream);
+}
+
+void ANTLRInputStream::load(const std::string &input, bool lenient) {
+ load(input.data(), input.size(), lenient);
+}
+
+void ANTLRInputStream::load(const char *data, size_t length, bool lenient) {
+ // Remove the UTF-8 BOM if present.
+ const char *bom = "\xef\xbb\xbf";
+ if (length >= 3 && strncmp(data, bom, 3) == 0) {
+ data += 3;
+ length -= 3;
+ }
+ if (lenient) {
+ _data = Utf8::lenientDecode(std::string_view(data, length));
+ } else {
+ auto maybe_utf32 = Utf8::strictDecode(std::string_view(data, length));
+ if (!maybe_utf32.has_value()) {
+ throw IllegalArgumentException("UTF-8 string contains an illegal byte sequence");
+ }
+ _data = std::move(maybe_utf32).value();
+ }
+ p = 0;
+}
+
+void ANTLRInputStream::load(std::istream &stream, bool lenient) {
+ if (!stream.good() || stream.eof()) // No fail, bad or EOF.
+ return;
+
+ _data.clear();
+
+ std::string s((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
+ load(s.data(), s.length(), lenient);
+}
+
+void ANTLRInputStream::reset() {
+ p = 0;
+}
+
+void ANTLRInputStream::consume() {
+ if (p >= _data.size()) {
+ assert(LA(1) == IntStream::EOF);
+ throw IllegalStateException("cannot consume EOF");
+ }
+
+ if (p < _data.size()) {
+ p++;
+ }
+}
+
+size_t ANTLRInputStream::LA(ssize_t i) {
+ if (i == 0) {
+ return 0; // undefined
+ }
+
+ ssize_t position = static_cast<ssize_t>(p);
+ if (i < 0) {
+ i++; // e.g., translate LA(-1) to use offset i=0; then _data[p+0-1]
+ if ((position + i - 1) < 0) {
+ return IntStream::EOF; // invalid; no char before first char
+ }
+ }
+
+ if ((position + i - 1) >= static_cast<ssize_t>(_data.size())) {
+ return IntStream::EOF;
+ }
+
+ return _data[static_cast<size_t>((position + i - 1))];
+}
+
+size_t ANTLRInputStream::LT(ssize_t i) {
+ return LA(i);
+}
+
+size_t ANTLRInputStream::index() {
+ return p;
+}
+
+size_t ANTLRInputStream::size() {
+ return _data.size();
+}
+
+// Mark/release do nothing. We have entire buffer.
+ssize_t ANTLRInputStream::mark() {
+ return -1;
+}
+
+void ANTLRInputStream::release(ssize_t /* marker */) {
+}
+
+void ANTLRInputStream::seek(size_t index) {
+ if (index <= p) {
+ p = index; // just jump; don't update stream state (line, ...)
+ return;
+ }
+ // seek forward, consume until p hits index or n (whichever comes first)
+ index = std::min(index, _data.size());
+ while (p < index) {
+ consume();
+ }
+}
+
+std::string ANTLRInputStream::getText(const Interval &interval) {
+ if (interval.a < 0 || interval.b < 0) {
+ return "";
+ }
+
+ size_t start = static_cast<size_t>(interval.a);
+ size_t stop = static_cast<size_t>(interval.b);
+
+
+ if (stop >= _data.size()) {
+ stop = _data.size() - 1;
+ }
+
+ size_t count = stop - start + 1;
+ if (start >= _data.size()) {
+ return "";
+ }
+
+ auto maybeUtf8 = Utf8::strictEncode(std::u32string_view(_data).substr(start, count));
+ if (!maybeUtf8.has_value()) {
+ throw IllegalArgumentException("Input stream contains invalid Unicode code points");
+ }
+ return std::move(maybeUtf8).value();
+}
+
+std::string ANTLRInputStream::getSourceName() const {
+ if (name.empty()) {
+ return IntStream::UNKNOWN_SOURCE_NAME;
+ }
+ return name;
+}
+
+std::string ANTLRInputStream::toString() const {
+ auto maybeUtf8 = Utf8::strictEncode(_data);
+ if (!maybeUtf8.has_value()) {
+ throw IllegalArgumentException("Input stream contains invalid Unicode code points");
+ }
+ return std::move(maybeUtf8).value();
+}
+
+void ANTLRInputStream::InitializeInstanceFields() {
+ p = 0;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.h b/contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.h
new file mode 100644
index 0000000000..413eadefa4
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ANTLRInputStream.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <string_view>
+
+#include "CharStream.h"
+
+namespace antlr4 {
+
+ // Vacuum all input from a stream and then treat it
+ // like a string. Can also pass in a string or char[] to use.
+ // Input is expected to be encoded in UTF-8 and converted to UTF-32 internally.
+ class ANTLR4CPP_PUBLIC ANTLRInputStream : public CharStream {
+ protected:
+ /// The data being scanned.
+ // UTF-32
+ std::u32string _data;
+
+ /// 0..n-1 index into string of next char </summary>
+ size_t p;
+
+ public:
+ /// What is name or source of this char stream?
+ std::string name;
+
+ ANTLRInputStream();
+
+ ANTLRInputStream(std::string_view input);
+
+ ANTLRInputStream(const char *data, size_t length);
+ ANTLRInputStream(std::istream &stream);
+
+ virtual void load(const std::string &input, bool lenient);
+ virtual void load(const char *data, size_t length, bool lenient);
+ virtual void load(std::istream &stream, bool lenient);
+
+ virtual void load(const std::string &input) { load(input, false); }
+ virtual void load(const char *data, size_t length) { load(data, length, false); }
+ virtual void load(std::istream &stream) { load(stream, false); }
+
+ /// Reset the stream so that it's in the same state it was
+ /// when the object was created *except* the data array is not
+ /// touched.
+ virtual void reset();
+ virtual void consume() override;
+ virtual size_t LA(ssize_t i) override;
+ virtual size_t LT(ssize_t i);
+
+ /// <summary>
+ /// Return the current input symbol index 0..n where n indicates the
+ /// last symbol has been read. The index is the index of char to
+ /// be returned from LA(1).
+ /// </summary>
+ virtual size_t index() override;
+ virtual size_t size() override;
+
+ /// <summary>
+ /// mark/release do nothing; we have entire buffer </summary>
+ virtual ssize_t mark() override;
+ virtual void release(ssize_t marker) override;
+
+ /// <summary>
+ /// consume() ahead until p==index; can't just set p=index as we must
+ /// update line and charPositionInLine. If we seek backwards, just set p
+ /// </summary>
+ virtual void seek(size_t index) override;
+ virtual std::string getText(const misc::Interval &interval) override;
+ virtual std::string getSourceName() const override;
+ virtual std::string toString() const override;
+
+ private:
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/BailErrorStrategy.cpp b/contrib/libs/antlr4_cpp_runtime/src/BailErrorStrategy.cpp
new file mode 100644
index 0000000000..781a13b547
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/BailErrorStrategy.cpp
@@ -0,0 +1,61 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Exceptions.h"
+#include "ParserRuleContext.h"
+#include "InputMismatchException.h"
+#include "Parser.h"
+
+#include "BailErrorStrategy.h"
+
+using namespace antlr4;
+
+void BailErrorStrategy::recover(Parser *recognizer, std::exception_ptr e) {
+ ParserRuleContext *context = recognizer->getContext();
+ do {
+ context->exception = e;
+ if (context->parent == nullptr)
+ break;
+ context = static_cast<ParserRuleContext *>(context->parent);
+ } while (true);
+
+ try {
+ std::rethrow_exception(e); // Throw the exception to be able to catch and rethrow nested.
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023026
+ } catch (RecognitionException &inner) {
+ throw ParseCancellationException(inner.what());
+#else
+ } catch (RecognitionException & /*inner*/) {
+ std::throw_with_nested(ParseCancellationException());
+#endif
+ }
+}
+
+Token* BailErrorStrategy::recoverInline(Parser *recognizer) {
+ InputMismatchException e(recognizer);
+ std::exception_ptr exception = std::make_exception_ptr(e);
+
+ ParserRuleContext *context = recognizer->getContext();
+ do {
+ context->exception = exception;
+ if (context->parent == nullptr)
+ break;
+ context = static_cast<ParserRuleContext *>(context->parent);
+ } while (true);
+
+ try {
+ throw e;
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023026
+ } catch (InputMismatchException &inner) {
+ throw ParseCancellationException(inner.what());
+#else
+ } catch (InputMismatchException & /*inner*/) {
+ std::throw_with_nested(ParseCancellationException());
+#endif
+ }
+}
+
+void BailErrorStrategy::sync(Parser * /*recognizer*/) {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/BailErrorStrategy.h b/contrib/libs/antlr4_cpp_runtime/src/BailErrorStrategy.h
new file mode 100644
index 0000000000..598f993022
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/BailErrorStrategy.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "DefaultErrorStrategy.h"
+
+namespace antlr4 {
+
+ /**
+ * This implementation of {@link ANTLRErrorStrategy} responds to syntax errors
+ * by immediately canceling the parse operation with a
+ * {@link ParseCancellationException}. The implementation ensures that the
+ * {@link ParserRuleContext#exception} field is set for all parse tree nodes
+ * that were not completed prior to encountering the error.
+ *
+ * <p>
+ * This error strategy is useful in the following scenarios.</p>
+ *
+ * <ul>
+ * <li><strong>Two-stage parsing:</strong> This error strategy allows the first
+ * stage of two-stage parsing to immediately terminate if an error is
+ * encountered, and immediately fall back to the second stage. In addition to
+ * avoiding wasted work by attempting to recover from errors here, the empty
+ * implementation of {@link BailErrorStrategy#sync} improves the performance of
+ * the first stage.</li>
+ * <li><strong>Silent validation:</strong> When syntax errors are not being
+ * reported or logged, and the parse result is simply ignored if errors occur,
+ * the {@link BailErrorStrategy} avoids wasting work on recovering from errors
+ * when the result will be ignored either way.</li>
+ * </ul>
+ *
+ * <p>
+ * {@code myparser.setErrorHandler(new BailErrorStrategy());}</p>
+ *
+ * @see Parser#setErrorHandler(ANTLRErrorStrategy)
+ */
+ class ANTLR4CPP_PUBLIC BailErrorStrategy : public DefaultErrorStrategy {
+ /// <summary>
+ /// Instead of recovering from exception {@code e}, re-throw it wrapped
+ /// in a <seealso cref="ParseCancellationException"/> so it is not caught by the
+ /// rule function catches. Use <seealso cref="Exception#getCause()"/> to get the
+ /// original <seealso cref="RecognitionException"/>.
+ /// </summary>
+ public:
+ virtual void recover(Parser *recognizer, std::exception_ptr e) override;
+
+ /// Make sure we don't attempt to recover inline; if the parser
+ /// successfully recovers, it won't throw an exception.
+ virtual Token* recoverInline(Parser *recognizer) override;
+
+ /// <summary>
+ /// Make sure we don't attempt to recover from problems in subrules. </summary>
+ virtual void sync(Parser *recognizer) override;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/BaseErrorListener.cpp b/contrib/libs/antlr4_cpp_runtime/src/BaseErrorListener.cpp
new file mode 100644
index 0000000000..cdcca8bc5c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/BaseErrorListener.cpp
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "BaseErrorListener.h"
+#include "RecognitionException.h"
+
+using namespace antlr4;
+
+void BaseErrorListener::syntaxError(Recognizer * /*recognizer*/, Token * /*offendingSymbol*/, size_t /*line*/,
+ size_t /*charPositionInLine*/, const std::string &/*msg*/, std::exception_ptr /*e*/) {
+}
+
+void BaseErrorListener::reportAmbiguity(Parser * /*recognizer*/, const dfa::DFA &/*dfa*/, size_t /*startIndex*/,
+ size_t /*stopIndex*/, bool /*exact*/, const antlrcpp::BitSet &/*ambigAlts*/, atn::ATNConfigSet * /*configs*/) {
+}
+
+void BaseErrorListener::reportAttemptingFullContext(Parser * /*recognizer*/, const dfa::DFA &/*dfa*/, size_t /*startIndex*/,
+ size_t /*stopIndex*/, const antlrcpp::BitSet &/*conflictingAlts*/, atn::ATNConfigSet * /*configs*/) {
+}
+
+void BaseErrorListener::reportContextSensitivity(Parser * /*recognizer*/, const dfa::DFA &/*dfa*/, size_t /*startIndex*/,
+ size_t /*stopIndex*/, size_t /*prediction*/, atn::ATNConfigSet * /*configs*/) {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/BaseErrorListener.h b/contrib/libs/antlr4_cpp_runtime/src/BaseErrorListener.h
new file mode 100644
index 0000000000..317785aa64
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/BaseErrorListener.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "ANTLRErrorListener.h"
+
+namespace antlrcpp {
+ class BitSet;
+}
+
+namespace antlr4 {
+
+ /**
+ * Provides an empty default implementation of {@link ANTLRErrorListener}. The
+ * default implementation of each method does nothing, but can be overridden as
+ * necessary.
+ */
+ class ANTLR4CPP_PUBLIC BaseErrorListener : public ANTLRErrorListener {
+
+ virtual void syntaxError(Recognizer *recognizer, Token * offendingSymbol, size_t line, size_t charPositionInLine,
+ const std::string &msg, std::exception_ptr e) override;
+
+ virtual void reportAmbiguity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact,
+ const antlrcpp::BitSet &ambigAlts, atn::ATNConfigSet *configs) override;
+
+ virtual void reportAttemptingFullContext(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ const antlrcpp::BitSet &conflictingAlts, atn::ATNConfigSet *configs) override;
+
+ virtual void reportContextSensitivity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ size_t prediction, atn::ATNConfigSet *configs) override;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.cpp
new file mode 100644
index 0000000000..4eaff2c852
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.cpp
@@ -0,0 +1,414 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "WritableToken.h"
+#include "Lexer.h"
+#include "RuleContext.h"
+#include "misc/Interval.h"
+#include "Exceptions.h"
+#include "support/CPPUtils.h"
+
+#include "BufferedTokenStream.h"
+
+using namespace antlr4;
+using namespace antlrcpp;
+
+BufferedTokenStream::BufferedTokenStream(TokenSource *tokenSource) : _tokenSource(tokenSource){
+ InitializeInstanceFields();
+}
+
+TokenSource* BufferedTokenStream::getTokenSource() const {
+ return _tokenSource;
+}
+
+size_t BufferedTokenStream::index() {
+ return _p;
+}
+
+ssize_t BufferedTokenStream::mark() {
+ return 0;
+}
+
+void BufferedTokenStream::release(ssize_t /*marker*/) {
+ // no resources to release
+}
+
+void BufferedTokenStream::reset() {
+ seek(0);
+}
+
+void BufferedTokenStream::seek(size_t index) {
+ lazyInit();
+ _p = adjustSeekIndex(index);
+}
+
+size_t BufferedTokenStream::size() {
+ return _tokens.size();
+}
+
+void BufferedTokenStream::consume() {
+ bool skipEofCheck = false;
+ if (!_needSetup) {
+ if (_fetchedEOF) {
+ // the last token in tokens is EOF. skip check if p indexes any
+ // fetched token except the last.
+ skipEofCheck = _p < _tokens.size() - 1;
+ } else {
+ // no EOF token in tokens. skip check if p indexes a fetched token.
+ skipEofCheck = _p < _tokens.size();
+ }
+ } else {
+ // not yet initialized
+ skipEofCheck = false;
+ }
+
+ if (!skipEofCheck && LA(1) == Token::EOF) {
+ throw IllegalStateException("cannot consume EOF");
+ }
+
+ if (sync(_p + 1)) {
+ _p = adjustSeekIndex(_p + 1);
+ }
+}
+
+bool BufferedTokenStream::sync(size_t i) {
+ if (i + 1 < _tokens.size())
+ return true;
+ size_t n = i - _tokens.size() + 1; // how many more elements we need?
+
+ if (n > 0) {
+ size_t fetched = fetch(n);
+ return fetched >= n;
+ }
+
+ return true;
+}
+
+size_t BufferedTokenStream::fetch(size_t n) {
+ if (_fetchedEOF) {
+ return 0;
+ }
+
+ size_t i = 0;
+ while (i < n) {
+ std::unique_ptr<Token> t(_tokenSource->nextToken());
+
+ if (is<WritableToken *>(t.get())) {
+ (static_cast<WritableToken *>(t.get()))->setTokenIndex(_tokens.size());
+ }
+
+ _tokens.push_back(std::move(t));
+ ++i;
+
+ if (_tokens.back()->getType() == Token::EOF) {
+ _fetchedEOF = true;
+ break;
+ }
+ }
+
+ return i;
+}
+
+Token* BufferedTokenStream::get(size_t i) const {
+ if (i >= _tokens.size()) {
+ throw IndexOutOfBoundsException(std::string("token index ") +
+ std::to_string(i) +
+ std::string(" out of range 0..") +
+ std::to_string(_tokens.size() - 1));
+ }
+ return _tokens[i].get();
+}
+
+std::vector<Token *> BufferedTokenStream::get(size_t start, size_t stop) {
+ std::vector<Token *> subset;
+
+ lazyInit();
+
+ if (_tokens.empty()) {
+ return subset;
+ }
+
+ if (stop >= _tokens.size()) {
+ stop = _tokens.size() - 1;
+ }
+ for (size_t i = start; i <= stop; i++) {
+ Token *t = _tokens[i].get();
+ if (t->getType() == Token::EOF) {
+ break;
+ }
+ subset.push_back(t);
+ }
+ return subset;
+}
+
+size_t BufferedTokenStream::LA(ssize_t i) {
+ return LT(i)->getType();
+}
+
+Token* BufferedTokenStream::LB(size_t k) {
+ if (k > _p) {
+ return nullptr;
+ }
+ return _tokens[_p - k].get();
+}
+
+Token* BufferedTokenStream::LT(ssize_t k) {
+ lazyInit();
+ if (k == 0) {
+ return nullptr;
+ }
+ if (k < 0) {
+ return LB(-k);
+ }
+
+ size_t i = _p + k - 1;
+ sync(i);
+ if (i >= _tokens.size()) { // return EOF token
+ // EOF must be last token
+ return _tokens.back().get();
+ }
+
+ return _tokens[i].get();
+}
+
+ssize_t BufferedTokenStream::adjustSeekIndex(size_t i) {
+ return i;
+}
+
+void BufferedTokenStream::lazyInit() {
+ if (_needSetup) {
+ setup();
+ }
+}
+
+void BufferedTokenStream::setup() {
+ _needSetup = false;
+ sync(0);
+ _p = adjustSeekIndex(0);
+}
+
+void BufferedTokenStream::setTokenSource(TokenSource *tokenSource) {
+ _tokenSource = tokenSource;
+ _tokens.clear();
+ _fetchedEOF = false;
+ _needSetup = true;
+}
+
+std::vector<Token *> BufferedTokenStream::getTokens() {
+ std::vector<Token *> result;
+ for (auto &t : _tokens)
+ result.push_back(t.get());
+ return result;
+}
+
+std::vector<Token *> BufferedTokenStream::getTokens(size_t start, size_t stop) {
+ return getTokens(start, stop, std::vector<size_t>());
+}
+
+std::vector<Token *> BufferedTokenStream::getTokens(size_t start, size_t stop, const std::vector<size_t> &types) {
+ lazyInit();
+ if (stop >= _tokens.size() || start >= _tokens.size()) {
+ throw IndexOutOfBoundsException(std::string("start ") +
+ std::to_string(start) +
+ std::string(" or stop ") +
+ std::to_string(stop) +
+ std::string(" not in 0..") +
+ std::to_string(_tokens.size() - 1));
+ }
+
+ std::vector<Token *> filteredTokens;
+
+ if (start > stop) {
+ return filteredTokens;
+ }
+
+ for (size_t i = start; i <= stop; i++) {
+ Token *tok = _tokens[i].get();
+
+ if (types.empty() || std::find(types.begin(), types.end(), tok->getType()) != types.end()) {
+ filteredTokens.push_back(tok);
+ }
+ }
+ return filteredTokens;
+}
+
+std::vector<Token *> BufferedTokenStream::getTokens(size_t start, size_t stop, size_t ttype) {
+ std::vector<size_t> s;
+ s.push_back(ttype);
+ return getTokens(start, stop, s);
+}
+
+ssize_t BufferedTokenStream::nextTokenOnChannel(size_t i, size_t channel) {
+ sync(i);
+ if (i >= size()) {
+ return size() - 1;
+ }
+
+ Token *token = _tokens[i].get();
+ while (token->getChannel() != channel) {
+ if (token->getType() == Token::EOF) {
+ return i;
+ }
+ i++;
+ sync(i);
+ token = _tokens[i].get();
+ }
+ return i;
+}
+
+ssize_t BufferedTokenStream::previousTokenOnChannel(size_t i, size_t channel) {
+ sync(i);
+ if (i >= size()) {
+ // the EOF token is on every channel
+ return size() - 1;
+ }
+
+ while (true) {
+ Token *token = _tokens[i].get();
+ if (token->getType() == Token::EOF || token->getChannel() == channel) {
+ return i;
+ }
+
+ if (i == 0)
+ return -1;
+ i--;
+ }
+ return i;
+}
+
+std::vector<Token *> BufferedTokenStream::getHiddenTokensToRight(size_t tokenIndex, ssize_t channel) {
+ lazyInit();
+ if (tokenIndex >= _tokens.size()) {
+ throw IndexOutOfBoundsException(std::to_string(tokenIndex) + " not in 0.." + std::to_string(_tokens.size() - 1));
+ }
+
+ ssize_t nextOnChannel = nextTokenOnChannel(tokenIndex + 1, Lexer::DEFAULT_TOKEN_CHANNEL);
+ size_t to;
+ size_t from = tokenIndex + 1;
+ // if none onchannel to right, nextOnChannel=-1 so set to = last token
+ if (nextOnChannel == -1) {
+ to = static_cast<ssize_t>(size() - 1);
+ } else {
+ to = nextOnChannel;
+ }
+
+ return filterForChannel(from, to, channel);
+}
+
+std::vector<Token *> BufferedTokenStream::getHiddenTokensToRight(size_t tokenIndex) {
+ return getHiddenTokensToRight(tokenIndex, -1);
+}
+
+std::vector<Token *> BufferedTokenStream::getHiddenTokensToLeft(size_t tokenIndex, ssize_t channel) {
+ lazyInit();
+ if (tokenIndex >= _tokens.size()) {
+ throw IndexOutOfBoundsException(std::to_string(tokenIndex) + " not in 0.." + std::to_string(_tokens.size() - 1));
+ }
+
+ if (tokenIndex == 0) {
+ // Obviously no tokens can appear before the first token.
+ return { };
+ }
+
+ ssize_t prevOnChannel = previousTokenOnChannel(tokenIndex - 1, Lexer::DEFAULT_TOKEN_CHANNEL);
+ if (prevOnChannel == static_cast<ssize_t>(tokenIndex - 1)) {
+ return { };
+ }
+ // if none onchannel to left, prevOnChannel=-1 then from=0
+ size_t from = static_cast<size_t>(prevOnChannel + 1);
+ size_t to = tokenIndex - 1;
+
+ return filterForChannel(from, to, channel);
+}
+
+std::vector<Token *> BufferedTokenStream::getHiddenTokensToLeft(size_t tokenIndex) {
+ return getHiddenTokensToLeft(tokenIndex, -1);
+}
+
+std::vector<Token *> BufferedTokenStream::filterForChannel(size_t from, size_t to, ssize_t channel) {
+ std::vector<Token *> hidden;
+ for (size_t i = from; i <= to; i++) {
+ Token *t = _tokens[i].get();
+ if (channel == -1) {
+ if (t->getChannel() != Lexer::DEFAULT_TOKEN_CHANNEL) {
+ hidden.push_back(t);
+ }
+ } else {
+ if (t->getChannel() == static_cast<size_t>(channel)) {
+ hidden.push_back(t);
+ }
+ }
+ }
+
+ return hidden;
+}
+
+bool BufferedTokenStream::isInitialized() const {
+ return !_needSetup;
+}
+
+/**
+ * Get the text of all tokens in this buffer.
+ */
+std::string BufferedTokenStream::getSourceName() const
+{
+ return _tokenSource->getSourceName();
+}
+
+std::string BufferedTokenStream::getText() {
+ fill();
+ return getText(misc::Interval(0U, size() - 1));
+}
+
+std::string BufferedTokenStream::getText(const misc::Interval &interval) {
+ lazyInit();
+ size_t start = interval.a;
+ size_t stop = interval.b;
+ if (start == INVALID_INDEX || stop == INVALID_INDEX) {
+ return "";
+ }
+ sync(stop);
+ if (stop >= _tokens.size()) {
+ stop = _tokens.size() - 1;
+ }
+
+ std::stringstream ss;
+ for (size_t i = start; i <= stop; i++) {
+ Token *t = _tokens[i].get();
+ if (t->getType() == Token::EOF) {
+ break;
+ }
+ ss << t->getText();
+ }
+ return ss.str();
+}
+
+std::string BufferedTokenStream::getText(RuleContext *ctx) {
+ return getText(ctx->getSourceInterval());
+}
+
+std::string BufferedTokenStream::getText(Token *start, Token *stop) {
+ if (start != nullptr && stop != nullptr) {
+ return getText(misc::Interval(start->getTokenIndex(), stop->getTokenIndex()));
+ }
+
+ return "";
+}
+
+void BufferedTokenStream::fill() {
+ lazyInit();
+ const size_t blockSize = 1000;
+ while (true) {
+ size_t fetched = fetch(blockSize);
+ if (fetched < blockSize) {
+ return;
+ }
+ }
+}
+
+void BufferedTokenStream::InitializeInstanceFields() {
+ _needSetup = true;
+ _fetchedEOF = false;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.h b/contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.h
new file mode 100644
index 0000000000..2161471241
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/BufferedTokenStream.h
@@ -0,0 +1,200 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "TokenStream.h"
+
+namespace antlr4 {
+
+ /**
+ * This implementation of {@link TokenStream} loads tokens from a
+ * {@link TokenSource} on-demand, and places the tokens in a buffer to provide
+ * access to any previous token by index.
+ *
+ * <p>
+ * This token stream ignores the value of {@link Token#getChannel}. If your
+ * parser requires the token stream filter tokens to only those on a particular
+ * channel, such as {@link Token#DEFAULT_CHANNEL} or
+ * {@link Token#HIDDEN_CHANNEL}, use a filtering token stream such a
+ * {@link CommonTokenStream}.</p>
+ */
+ class ANTLR4CPP_PUBLIC BufferedTokenStream : public TokenStream {
+ public:
+ BufferedTokenStream(TokenSource *tokenSource);
+ BufferedTokenStream(const BufferedTokenStream& other) = delete;
+
+ BufferedTokenStream& operator = (const BufferedTokenStream& other) = delete;
+
+ virtual TokenSource* getTokenSource() const override;
+ virtual size_t index() override;
+ virtual ssize_t mark() override;
+
+ virtual void release(ssize_t marker) override;
+ virtual void reset();
+ virtual void seek(size_t index) override;
+
+ virtual size_t size() override;
+ virtual void consume() override;
+
+ virtual Token* get(size_t i) const override;
+
+ /// Get all tokens from start..stop inclusively.
+ virtual std::vector<Token *> get(size_t start, size_t stop);
+
+ virtual size_t LA(ssize_t i) override;
+ virtual Token* LT(ssize_t k) override;
+
+ /// Reset this token stream by setting its token source.
+ virtual void setTokenSource(TokenSource *tokenSource);
+ virtual std::vector<Token *> getTokens();
+ virtual std::vector<Token *> getTokens(size_t start, size_t stop);
+
+ /// <summary>
+ /// Given a start and stop index, return a List of all tokens in
+ /// the token type BitSet. Return null if no tokens were found. This
+ /// method looks at both on and off channel tokens.
+ /// </summary>
+ virtual std::vector<Token *> getTokens(size_t start, size_t stop, const std::vector<size_t> &types);
+ virtual std::vector<Token *> getTokens(size_t start, size_t stop, size_t ttype);
+
+ /// Collect all tokens on specified channel to the right of
+ /// the current token up until we see a token on DEFAULT_TOKEN_CHANNEL or
+ /// EOF. If channel is -1, find any non default channel token.
+ virtual std::vector<Token *> getHiddenTokensToRight(size_t tokenIndex, ssize_t channel);
+
+ /// <summary>
+ /// Collect all hidden tokens (any off-default channel) to the right of
+ /// the current token up until we see a token on DEFAULT_TOKEN_CHANNEL
+ /// or EOF.
+ /// </summary>
+ virtual std::vector<Token *> getHiddenTokensToRight(size_t tokenIndex);
+
+ /// <summary>
+ /// Collect all tokens on specified channel to the left of
+ /// the current token up until we see a token on DEFAULT_TOKEN_CHANNEL.
+ /// If channel is -1, find any non default channel token.
+ /// </summary>
+ virtual std::vector<Token *> getHiddenTokensToLeft(size_t tokenIndex, ssize_t channel);
+
+ /// <summary>
+ /// Collect all hidden tokens (any off-default channel) to the left of
+ /// the current token up until we see a token on DEFAULT_TOKEN_CHANNEL.
+ /// </summary>
+ virtual std::vector<Token *> getHiddenTokensToLeft(size_t tokenIndex);
+
+ virtual std::string getSourceName() const override;
+ virtual std::string getText() override;
+ virtual std::string getText(const misc::Interval &interval) override;
+ virtual std::string getText(RuleContext *ctx) override;
+ virtual std::string getText(Token *start, Token *stop) override;
+
+ /// Get all tokens from lexer until EOF.
+ virtual void fill();
+
+ protected:
+ /**
+ * The {@link TokenSource} from which tokens for this stream are fetched.
+ */
+ TokenSource *_tokenSource;
+
+ /**
+ * A collection of all tokens fetched from the token source. The list is
+ * considered a complete view of the input once {@link #fetchedEOF} is set
+ * to {@code true}.
+ */
+ std::vector<std::unique_ptr<Token>> _tokens;
+
+ /**
+ * The index into {@link #tokens} of the current token (next token to
+ * {@link #consume}). {@link #tokens}{@code [}{@link #p}{@code ]} should be
+ * {@link #LT LT(1)}.
+ *
+ * <p>This field is set to -1 when the stream is first constructed or when
+ * {@link #setTokenSource} is called, indicating that the first token has
+ * not yet been fetched from the token source. For additional information,
+ * see the documentation of {@link IntStream} for a description of
+ * Initializing Methods.</p>
+ */
+ // ml: since -1 requires to make this member signed for just this single aspect we use a member _needSetup instead.
+ // Use bool isInitialized() to find out if this stream has started reading.
+ size_t _p;
+
+ /**
+ * Indicates whether the {@link Token#EOF} token has been fetched from
+ * {@link #tokenSource} and added to {@link #tokens}. This field improves
+ * performance for the following cases:
+ *
+ * <ul>
+ * <li>{@link #consume}: The lookahead check in {@link #consume} to prevent
+ * consuming the EOF symbol is optimized by checking the values of
+ * {@link #fetchedEOF} and {@link #p} instead of calling {@link #LA}.</li>
+ * <li>{@link #fetch}: The check to prevent adding multiple EOF symbols into
+ * {@link #tokens} is trivial with this field.</li>
+ * <ul>
+ */
+ bool _fetchedEOF;
+
+ /// <summary>
+ /// Make sure index {@code i} in tokens has a token.
+ /// </summary>
+ /// <returns> {@code true} if a token is located at index {@code i}, otherwise
+ /// {@code false}. </returns>
+ /// <seealso cref= #get(int i) </seealso>
+ virtual bool sync(size_t i);
+
+ /// <summary>
+ /// Add {@code n} elements to buffer.
+ /// </summary>
+ /// <returns> The actual number of elements added to the buffer. </returns>
+ virtual size_t fetch(size_t n);
+
+ virtual Token* LB(size_t k);
+
+ /// Allowed derived classes to modify the behavior of operations which change
+ /// the current stream position by adjusting the target token index of a seek
+ /// operation. The default implementation simply returns {@code i}. If an
+ /// exception is thrown in this method, the current stream index should not be
+ /// changed.
+ /// <p/>
+ /// For example, <seealso cref="CommonTokenStream"/> overrides this method to ensure that
+ /// the seek target is always an on-channel token.
+ ///
+ /// <param name="i"> The target token index. </param>
+ /// <returns> The adjusted target token index. </returns>
+ virtual ssize_t adjustSeekIndex(size_t i);
+ void lazyInit();
+ virtual void setup();
+
+ /**
+ * Given a starting index, return the index of the next token on channel.
+ * Return {@code i} if {@code tokens[i]} is on channel. Return the index of
+ * the EOF token if there are no tokens on channel between {@code i} and
+ * EOF.
+ */
+ virtual ssize_t nextTokenOnChannel(size_t i, size_t channel);
+
+ /**
+ * Given a starting index, return the index of the previous token on
+ * channel. Return {@code i} if {@code tokens[i]} is on channel. Return -1
+ * if there are no tokens on channel between {@code i} and 0.
+ *
+ * <p>
+ * If {@code i} specifies an index at or after the EOF token, the EOF token
+ * index is returned. This is due to the fact that the EOF token is treated
+ * as though it were on every channel.</p>
+ */
+ virtual ssize_t previousTokenOnChannel(size_t i, size_t channel);
+
+ virtual std::vector<Token *> filterForChannel(size_t from, size_t to, ssize_t channel);
+
+ bool isInitialized() const;
+
+ private:
+ bool _needSetup;
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/CharStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/CharStream.cpp
new file mode 100644
index 0000000000..b05874c8bf
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/CharStream.cpp
@@ -0,0 +1,11 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "CharStream.h"
+
+using namespace antlr4;
+
+CharStream::~CharStream() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/CharStream.h b/contrib/libs/antlr4_cpp_runtime/src/CharStream.h
new file mode 100644
index 0000000000..a9952dbbac
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/CharStream.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "IntStream.h"
+#include "misc/Interval.h"
+
+namespace antlr4 {
+
+ /// A source of characters for an ANTLR lexer.
+ class ANTLR4CPP_PUBLIC CharStream : public IntStream {
+ public:
+ virtual ~CharStream();
+
+ /// This method returns the text for a range of characters within this input
+ /// stream. This method is guaranteed to not throw an exception if the
+ /// specified interval lies entirely within a marked range. For more
+ /// information about marked ranges, see IntStream::mark.
+ ///
+ /// <param name="interval"> an interval within the stream </param>
+ /// <returns> the text of the specified interval
+ /// </returns>
+ /// <exception cref="NullPointerException"> if {@code interval} is {@code null} </exception>
+ /// <exception cref="IllegalArgumentException"> if {@code interval.a < 0}, or if
+ /// {@code interval.b < interval.a - 1}, or if {@code interval.b} lies at or
+ /// past the end of the stream </exception>
+ /// <exception cref="UnsupportedOperationException"> if the stream does not support
+ /// getting the text of the specified interval </exception>
+ virtual std::string getText(const misc::Interval &interval) = 0;
+
+ virtual std::string toString() const = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/CommonToken.cpp b/contrib/libs/antlr4_cpp_runtime/src/CommonToken.cpp
new file mode 100644
index 0000000000..6e9f06a249
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/CommonToken.cpp
@@ -0,0 +1,193 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "TokenSource.h"
+#include "CharStream.h"
+#include "Recognizer.h"
+#include "Vocabulary.h"
+
+#include "misc/Interval.h"
+
+#include "support/CPPUtils.h"
+#include "support/StringUtils.h"
+
+#include "CommonToken.h"
+
+using namespace antlr4;
+using namespace antlr4::misc;
+
+using namespace antlrcpp;
+
+const std::pair<TokenSource*, CharStream*> CommonToken::EMPTY_SOURCE;
+
+CommonToken::CommonToken(size_t type) {
+ InitializeInstanceFields();
+ _type = type;
+}
+
+CommonToken::CommonToken(std::pair<TokenSource*, CharStream*> source, size_t type, size_t channel, size_t start, size_t stop) {
+ InitializeInstanceFields();
+ _source = source;
+ _type = type;
+ _channel = channel;
+ _start = start;
+ _stop = stop;
+ if (_source.first != nullptr) {
+ _line = static_cast<int>(source.first->getLine());
+ _charPositionInLine = source.first->getCharPositionInLine();
+ }
+}
+
+CommonToken::CommonToken(size_t type, const std::string &text) {
+ InitializeInstanceFields();
+ _type = type;
+ _channel = DEFAULT_CHANNEL;
+ _text = text;
+ _source = EMPTY_SOURCE;
+}
+
+CommonToken::CommonToken(Token *oldToken) {
+ InitializeInstanceFields();
+ _type = oldToken->getType();
+ _line = oldToken->getLine();
+ _index = oldToken->getTokenIndex();
+ _charPositionInLine = oldToken->getCharPositionInLine();
+ _channel = oldToken->getChannel();
+ _start = oldToken->getStartIndex();
+ _stop = oldToken->getStopIndex();
+
+ if (is<CommonToken *>(oldToken)) {
+ _text = (static_cast<CommonToken *>(oldToken))->_text;
+ _source = (static_cast<CommonToken *>(oldToken))->_source;
+ } else {
+ _text = oldToken->getText();
+ _source = { oldToken->getTokenSource(), oldToken->getInputStream() };
+ }
+}
+
+size_t CommonToken::getType() const {
+ return _type;
+}
+
+void CommonToken::setLine(size_t line) {
+ _line = line;
+}
+
+std::string CommonToken::getText() const {
+ if (!_text.empty()) {
+ return _text;
+ }
+
+ CharStream *input = getInputStream();
+ if (input == nullptr) {
+ return "";
+ }
+ size_t n = input->size();
+ if (_start < n && _stop < n) {
+ return input->getText(misc::Interval(_start, _stop));
+ } else {
+ return "<EOF>";
+ }
+}
+
+void CommonToken::setText(const std::string &text) {
+ _text = text;
+}
+
+size_t CommonToken::getLine() const {
+ return _line;
+}
+
+size_t CommonToken::getCharPositionInLine() const {
+ return _charPositionInLine;
+}
+
+void CommonToken::setCharPositionInLine(size_t charPositionInLine) {
+ _charPositionInLine = charPositionInLine;
+}
+
+size_t CommonToken::getChannel() const {
+ return _channel;
+}
+
+void CommonToken::setChannel(size_t channel) {
+ _channel = channel;
+}
+
+void CommonToken::setType(size_t type) {
+ _type = type;
+}
+
+size_t CommonToken::getStartIndex() const {
+ return _start;
+}
+
+void CommonToken::setStartIndex(size_t start) {
+ _start = start;
+}
+
+size_t CommonToken::getStopIndex() const {
+ return _stop;
+}
+
+void CommonToken::setStopIndex(size_t stop) {
+ _stop = stop;
+}
+
+size_t CommonToken::getTokenIndex() const {
+ return _index;
+}
+
+void CommonToken::setTokenIndex(size_t index) {
+ _index = index;
+}
+
+antlr4::TokenSource *CommonToken::getTokenSource() const {
+ return _source.first;
+}
+
+antlr4::CharStream *CommonToken::getInputStream() const {
+ return _source.second;
+}
+
+std::string CommonToken::toString() const {
+ return toString(nullptr);
+}
+
+std::string CommonToken::toString(Recognizer *r) const {
+ std::stringstream ss;
+
+ std::string channelStr;
+ if (_channel > 0) {
+ channelStr = ",channel=" + std::to_string(_channel);
+ }
+ std::string txt = getText();
+ if (!txt.empty()) {
+ txt = antlrcpp::escapeWhitespace(txt);
+ } else {
+ txt = "<no text>";
+ }
+
+ std::string typeString = std::to_string(symbolToNumeric(_type));
+ if (r != nullptr)
+ typeString = r->getVocabulary().getDisplayName(_type);
+
+ ss << "[@" << symbolToNumeric(getTokenIndex()) << "," << symbolToNumeric(_start) << ":" << symbolToNumeric(_stop)
+ << "='" << txt << "',<" << typeString << ">" << channelStr << "," << _line << ":"
+ << getCharPositionInLine() << "]";
+
+ return ss.str();
+}
+
+void CommonToken::InitializeInstanceFields() {
+ _type = 0;
+ _line = 0;
+ _charPositionInLine = INVALID_INDEX;
+ _channel = DEFAULT_CHANNEL;
+ _index = INVALID_INDEX;
+ _start = 0;
+ _stop = 0;
+ _source = EMPTY_SOURCE;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/CommonToken.h b/contrib/libs/antlr4_cpp_runtime/src/CommonToken.h
new file mode 100644
index 0000000000..3fbc2ae4f5
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/CommonToken.h
@@ -0,0 +1,158 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "WritableToken.h"
+
+namespace antlr4 {
+
+ class ANTLR4CPP_PUBLIC CommonToken : public WritableToken {
+ protected:
+ /**
+ * An empty {@link Pair} which is used as the default value of
+ * {@link #source} for tokens that do not have a source.
+ */
+ static const std::pair<TokenSource *, CharStream *> EMPTY_SOURCE;
+
+ /**
+ * This is the backing field for {@link #getType} and {@link #setType}.
+ */
+ size_t _type;
+
+ /**
+ * This is the backing field for {@link #getLine} and {@link #setLine}.
+ */
+ size_t _line;
+
+ /**
+ * This is the backing field for {@link #getCharPositionInLine} and
+ * {@link #setCharPositionInLine}.
+ */
+ size_t _charPositionInLine; // set to invalid position
+
+ /**
+ * This is the backing field for {@link #getChannel} and
+ * {@link #setChannel}.
+ */
+ size_t _channel;
+
+ /**
+ * This is the backing field for {@link #getTokenSource} and
+ * {@link #getInputStream}.
+ *
+ * <p>
+ * These properties share a field to reduce the memory footprint of
+ * {@link CommonToken}. Tokens created by a {@link CommonTokenFactory} from
+ * the same source and input stream share a reference to the same
+ * {@link Pair} containing these values.</p>
+ */
+
+ std::pair<TokenSource *, CharStream *> _source; // ml: pure references, usually from statically allocated classes.
+
+ /**
+ * This is the backing field for {@link #getText} when the token text is
+ * explicitly set in the constructor or via {@link #setText}.
+ *
+ * @see #getText()
+ */
+ std::string _text;
+
+ /**
+ * This is the backing field for {@link #getTokenIndex} and
+ * {@link #setTokenIndex}.
+ */
+ size_t _index;
+
+ /**
+ * This is the backing field for {@link #getStartIndex} and
+ * {@link #setStartIndex}.
+ */
+ size_t _start;
+
+ /**
+ * This is the backing field for {@link #getStopIndex} and
+ * {@link #setStopIndex}.
+ */
+ size_t _stop;
+
+ public:
+ /**
+ * Constructs a new {@link CommonToken} with the specified token type.
+ *
+ * @param type The token type.
+ */
+ CommonToken(size_t type);
+ CommonToken(std::pair<TokenSource*, CharStream*> source, size_t type, size_t channel, size_t start, size_t stop);
+
+ /**
+ * Constructs a new {@link CommonToken} with the specified token type and
+ * text.
+ *
+ * @param type The token type.
+ * @param text The text of the token.
+ */
+ CommonToken(size_t type, const std::string &text);
+
+ /**
+ * Constructs a new {@link CommonToken} as a copy of another {@link Token}.
+ *
+ * <p>
+ * If {@code oldToken} is also a {@link CommonToken} instance, the newly
+ * constructed token will share a reference to the {@link #text} field and
+ * the {@link Pair} stored in {@link #source}. Otherwise, {@link #text} will
+ * be assigned the result of calling {@link #getText}, and {@link #source}
+ * will be constructed from the result of {@link Token#getTokenSource} and
+ * {@link Token#getInputStream}.</p>
+ *
+ * @param oldToken The token to copy.
+ */
+ CommonToken(Token *oldToken);
+
+ virtual size_t getType() const override;
+
+ /**
+ * Explicitly set the text for this token. If {code text} is not
+ * {@code null}, then {@link #getText} will return this value rather than
+ * extracting the text from the input.
+ *
+ * @param text The explicit text of the token, or {@code null} if the text
+ * should be obtained from the input along with the start and stop indexes
+ * of the token.
+ */
+ virtual void setText(const std::string &text) override;
+ virtual std::string getText() const override;
+
+ virtual void setLine(size_t line) override;
+ virtual size_t getLine() const override;
+
+ virtual size_t getCharPositionInLine() const override;
+ virtual void setCharPositionInLine(size_t charPositionInLine) override;
+
+ virtual size_t getChannel() const override;
+ virtual void setChannel(size_t channel) override;
+
+ virtual void setType(size_t type) override;
+
+ virtual size_t getStartIndex() const override;
+ virtual void setStartIndex(size_t start);
+
+ virtual size_t getStopIndex() const override;
+ virtual void setStopIndex(size_t stop);
+
+ virtual size_t getTokenIndex() const override;
+ virtual void setTokenIndex(size_t index) override;
+
+ virtual TokenSource *getTokenSource() const override;
+ virtual CharStream *getInputStream() const override;
+
+ virtual std::string toString() const override;
+
+ virtual std::string toString(Recognizer *r) const;
+ private:
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/CommonTokenFactory.cpp b/contrib/libs/antlr4_cpp_runtime/src/CommonTokenFactory.cpp
new file mode 100644
index 0000000000..23d8f7003a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/CommonTokenFactory.cpp
@@ -0,0 +1,39 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/Interval.h"
+#include "CommonToken.h"
+#include "CharStream.h"
+
+#include "CommonTokenFactory.h"
+
+using namespace antlr4;
+
+const std::unique_ptr<TokenFactory<CommonToken>> CommonTokenFactory::DEFAULT(new CommonTokenFactory);
+
+CommonTokenFactory::CommonTokenFactory(bool copyText_) : copyText(copyText_) {
+}
+
+CommonTokenFactory::CommonTokenFactory() : CommonTokenFactory(false) {
+}
+
+std::unique_ptr<CommonToken> CommonTokenFactory::create(std::pair<TokenSource*, CharStream*> source, size_t type,
+ const std::string &text, size_t channel, size_t start, size_t stop, size_t line, size_t charPositionInLine) {
+
+ std::unique_ptr<CommonToken> t(new CommonToken(source, type, channel, start, stop));
+ t->setLine(line);
+ t->setCharPositionInLine(charPositionInLine);
+ if (text != "") {
+ t->setText(text);
+ } else if (copyText && source.second != nullptr) {
+ t->setText(source.second->getText(misc::Interval(start, stop)));
+ }
+
+ return t;
+}
+
+std::unique_ptr<CommonToken> CommonTokenFactory::create(size_t type, const std::string &text) {
+ return std::unique_ptr<CommonToken>(new CommonToken(type, text));
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/CommonTokenFactory.h b/contrib/libs/antlr4_cpp_runtime/src/CommonTokenFactory.h
new file mode 100644
index 0000000000..0ae1a0353c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/CommonTokenFactory.h
@@ -0,0 +1,74 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "TokenFactory.h"
+
+namespace antlr4 {
+
+ /**
+ * This default implementation of {@link TokenFactory} creates
+ * {@link CommonToken} objects.
+ */
+ class ANTLR4CPP_PUBLIC CommonTokenFactory : public TokenFactory<CommonToken> {
+ public:
+ /**
+ * The default {@link CommonTokenFactory} instance.
+ *
+ * <p>
+ * This token factory does not explicitly copy token text when constructing
+ * tokens.</p>
+ */
+ static const std::unique_ptr<TokenFactory<CommonToken>> DEFAULT;
+
+ protected:
+ /**
+ * Indicates whether {@link CommonToken#setText} should be called after
+ * constructing tokens to explicitly set the text. This is useful for cases
+ * where the input stream might not be able to provide arbitrary substrings
+ * of text from the input after the lexer creates a token (e.g. the
+ * implementation of {@link CharStream#getText} in
+ * {@link UnbufferedCharStream} throws an
+ * {@link UnsupportedOperationException}). Explicitly setting the token text
+ * allows {@link Token#getText} to be called at any time regardless of the
+ * input stream implementation.
+ *
+ * <p>
+ * The default value is {@code false} to avoid the performance and memory
+ * overhead of copying text for every token unless explicitly requested.</p>
+ */
+ const bool copyText;
+
+ public:
+ /**
+ * Constructs a {@link CommonTokenFactory} with the specified value for
+ * {@link #copyText}.
+ *
+ * <p>
+ * When {@code copyText} is {@code false}, the {@link #DEFAULT} instance
+ * should be used instead of constructing a new instance.</p>
+ *
+ * @param copyText The value for {@link #copyText}.
+ */
+ CommonTokenFactory(bool copyText);
+
+ /**
+ * Constructs a {@link CommonTokenFactory} with {@link #copyText} set to
+ * {@code false}.
+ *
+ * <p>
+ * The {@link #DEFAULT} instance should be used instead of calling this
+ * directly.</p>
+ */
+ CommonTokenFactory();
+
+ virtual std::unique_ptr<CommonToken> create(std::pair<TokenSource*, CharStream*> source, size_t type,
+ const std::string &text, size_t channel, size_t start, size_t stop, size_t line, size_t charPositionInLine) override;
+
+ virtual std::unique_ptr<CommonToken> create(size_t type, const std::string &text) override;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/CommonTokenStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/CommonTokenStream.cpp
new file mode 100644
index 0000000000..02a2e55af3
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/CommonTokenStream.cpp
@@ -0,0 +1,78 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Token.h"
+
+#include "CommonTokenStream.h"
+
+using namespace antlr4;
+
+CommonTokenStream::CommonTokenStream(TokenSource *tokenSource) : CommonTokenStream(tokenSource, Token::DEFAULT_CHANNEL) {
+}
+
+CommonTokenStream::CommonTokenStream(TokenSource *tokenSource, size_t channel_)
+: BufferedTokenStream(tokenSource), channel(channel_) {
+}
+
+ssize_t CommonTokenStream::adjustSeekIndex(size_t i) {
+ return nextTokenOnChannel(i, channel);
+}
+
+Token* CommonTokenStream::LB(size_t k) {
+ if (k == 0 || k > _p) {
+ return nullptr;
+ }
+
+ ssize_t i = static_cast<ssize_t>(_p);
+ size_t n = 1;
+ // find k good tokens looking backwards
+ while (n <= k) {
+ // skip off-channel tokens
+ i = previousTokenOnChannel(i - 1, channel);
+ n++;
+ }
+ if (i < 0) {
+ return nullptr;
+ }
+
+ return _tokens[i].get();
+}
+
+Token* CommonTokenStream::LT(ssize_t k) {
+ lazyInit();
+ if (k == 0) {
+ return nullptr;
+ }
+ if (k < 0) {
+ return LB(static_cast<size_t>(-k));
+ }
+ size_t i = _p;
+ ssize_t n = 1; // we know tokens[p] is a good one
+ // find k good tokens
+ while (n < k) {
+ // skip off-channel tokens, but make sure to not look past EOF
+ if (sync(i + 1)) {
+ i = nextTokenOnChannel(i + 1, channel);
+ }
+ n++;
+ }
+
+ return _tokens[i].get();
+}
+
+int CommonTokenStream::getNumberOfOnChannelTokens() {
+ int n = 0;
+ fill();
+ for (size_t i = 0; i < _tokens.size(); i++) {
+ Token *t = _tokens[i].get();
+ if (t->getChannel() == channel) {
+ n++;
+ }
+ if (t->getType() == Token::EOF) {
+ break;
+ }
+ }
+ return n;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/CommonTokenStream.h b/contrib/libs/antlr4_cpp_runtime/src/CommonTokenStream.h
new file mode 100644
index 0000000000..fde72c7386
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/CommonTokenStream.h
@@ -0,0 +1,79 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "BufferedTokenStream.h"
+
+namespace antlr4 {
+
+ /**
+ * This class extends {@link BufferedTokenStream} with functionality to filter
+ * token streams to tokens on a particular channel (tokens where
+ * {@link Token#getChannel} returns a particular value).
+ *
+ * <p>
+ * This token stream provides access to all tokens by index or when calling
+ * methods like {@link #getText}. The channel filtering is only used for code
+ * accessing tokens via the lookahead methods {@link #LA}, {@link #LT}, and
+ * {@link #LB}.</p>
+ *
+ * <p>
+ * By default, tokens are placed on the default channel
+ * ({@link Token#DEFAULT_CHANNEL}), but may be reassigned by using the
+ * {@code ->channel(HIDDEN)} lexer command, or by using an embedded action to
+ * call {@link Lexer#setChannel}.
+ * </p>
+ *
+ * <p>
+ * Note: lexer rules which use the {@code ->skip} lexer command or call
+ * {@link Lexer#skip} do not produce tokens at all, so input text matched by
+ * such a rule will not be available as part of the token stream, regardless of
+ * channel.</p>
+ */
+ class ANTLR4CPP_PUBLIC CommonTokenStream : public BufferedTokenStream {
+ public:
+ /**
+ * Constructs a new {@link CommonTokenStream} using the specified token
+ * source and the default token channel ({@link Token#DEFAULT_CHANNEL}).
+ *
+ * @param tokenSource The token source.
+ */
+ CommonTokenStream(TokenSource *tokenSource);
+
+ /**
+ * Constructs a new {@link CommonTokenStream} using the specified token
+ * source and filtering tokens to the specified channel. Only tokens whose
+ * {@link Token#getChannel} matches {@code channel} or have the
+ * {@link Token#getType} equal to {@link Token#EOF} will be returned by the
+ * token stream lookahead methods.
+ *
+ * @param tokenSource The token source.
+ * @param channel The channel to use for filtering tokens.
+ */
+ CommonTokenStream(TokenSource *tokenSource, size_t channel);
+
+ virtual Token* LT(ssize_t k) override;
+
+ /// Count EOF just once.
+ virtual int getNumberOfOnChannelTokens();
+
+ protected:
+ /**
+ * Specifies the channel to use for filtering tokens.
+ *
+ * <p>
+ * The default value is {@link Token#DEFAULT_CHANNEL}, which matches the
+ * default channel assigned to tokens created by the lexer.</p>
+ */
+ size_t channel;
+
+ virtual ssize_t adjustSeekIndex(size_t i) override;
+
+ virtual Token* LB(size_t k) override;
+
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ConsoleErrorListener.cpp b/contrib/libs/antlr4_cpp_runtime/src/ConsoleErrorListener.cpp
new file mode 100644
index 0000000000..c16f949cd2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ConsoleErrorListener.cpp
@@ -0,0 +1,15 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "ConsoleErrorListener.h"
+
+using namespace antlr4;
+
+ConsoleErrorListener ConsoleErrorListener::INSTANCE;
+
+void ConsoleErrorListener::syntaxError(Recognizer * /*recognizer*/, Token * /*offendingSymbol*/,
+ size_t line, size_t charPositionInLine, const std::string &msg, std::exception_ptr /*e*/) {
+ std::cerr << "line " << line << ":" << charPositionInLine << " " << msg << std::endl;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ConsoleErrorListener.h b/contrib/libs/antlr4_cpp_runtime/src/ConsoleErrorListener.h
new file mode 100644
index 0000000000..f1d1188667
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ConsoleErrorListener.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "BaseErrorListener.h"
+
+namespace antlr4 {
+
+ class ANTLR4CPP_PUBLIC ConsoleErrorListener : public BaseErrorListener {
+ public:
+ /**
+ * Provides a default instance of {@link ConsoleErrorListener}.
+ */
+ static ConsoleErrorListener INSTANCE;
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>
+ * This implementation prints messages to {@link System#err} containing the
+ * values of {@code line}, {@code charPositionInLine}, and {@code msg} using
+ * the following format.</p>
+ *
+ * <pre>
+ * line <em>line</em>:<em>charPositionInLine</em> <em>msg</em>
+ * </pre>
+ */
+ virtual void syntaxError(Recognizer *recognizer, Token * offendingSymbol, size_t line, size_t charPositionInLine,
+ const std::string &msg, std::exception_ptr e) override;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/DefaultErrorStrategy.cpp b/contrib/libs/antlr4_cpp_runtime/src/DefaultErrorStrategy.cpp
new file mode 100644
index 0000000000..e5a7327859
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/DefaultErrorStrategy.cpp
@@ -0,0 +1,336 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "NoViableAltException.h"
+#include "misc/IntervalSet.h"
+#include "atn/ParserATNSimulator.h"
+#include "InputMismatchException.h"
+#include "FailedPredicateException.h"
+#include "ParserRuleContext.h"
+#include "atn/RuleTransition.h"
+#include "atn/ATN.h"
+#include "atn/ATNState.h"
+#include "support/StringUtils.h"
+#include "support/Casts.h"
+#include "Parser.h"
+#include "CommonToken.h"
+#include "Vocabulary.h"
+
+#include "DefaultErrorStrategy.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+using namespace antlrcpp;
+
+DefaultErrorStrategy::DefaultErrorStrategy() {
+ InitializeInstanceFields();
+}
+
+DefaultErrorStrategy::~DefaultErrorStrategy() {
+}
+
+void DefaultErrorStrategy::reset(Parser *recognizer) {
+ _errorSymbols.clear();
+ endErrorCondition(recognizer);
+}
+
+void DefaultErrorStrategy::beginErrorCondition(Parser * /*recognizer*/) {
+ errorRecoveryMode = true;
+}
+
+bool DefaultErrorStrategy::inErrorRecoveryMode(Parser * /*recognizer*/) {
+ return errorRecoveryMode;
+}
+
+void DefaultErrorStrategy::endErrorCondition(Parser * /*recognizer*/) {
+ errorRecoveryMode = false;
+ lastErrorIndex = -1;
+}
+
+void DefaultErrorStrategy::reportMatch(Parser *recognizer) {
+ endErrorCondition(recognizer);
+}
+
+void DefaultErrorStrategy::reportError(Parser *recognizer, const RecognitionException &e) {
+ // If we've already reported an error and have not matched a token
+ // yet successfully, don't report any errors.
+ if (inErrorRecoveryMode(recognizer)) {
+ return; // don't report spurious errors
+ }
+
+ beginErrorCondition(recognizer);
+ if (is<const NoViableAltException *>(&e)) {
+ reportNoViableAlternative(recognizer, static_cast<const NoViableAltException &>(e));
+ } else if (is<const InputMismatchException *>(&e)) {
+ reportInputMismatch(recognizer, static_cast<const InputMismatchException &>(e));
+ } else if (is<const FailedPredicateException *>(&e)) {
+ reportFailedPredicate(recognizer, static_cast<const FailedPredicateException &>(e));
+ } else if (is<const RecognitionException *>(&e)) {
+ recognizer->notifyErrorListeners(e.getOffendingToken(), e.what(), std::current_exception());
+ }
+}
+
+void DefaultErrorStrategy::recover(Parser *recognizer, std::exception_ptr /*e*/) {
+ if (lastErrorIndex == static_cast<int>(recognizer->getInputStream()->index()) &&
+ lastErrorStates.contains(recognizer->getState())) {
+
+ // uh oh, another error at same token index and previously-visited
+ // state in ATN; must be a case where LT(1) is in the recovery
+ // token set so nothing got consumed. Consume a single token
+ // at least to prevent an infinite loop; this is a failsafe.
+ recognizer->consume();
+ }
+ lastErrorIndex = static_cast<int>(recognizer->getInputStream()->index());
+ lastErrorStates.add(recognizer->getState());
+ misc::IntervalSet followSet = getErrorRecoverySet(recognizer);
+ consumeUntil(recognizer, followSet);
+}
+
+void DefaultErrorStrategy::sync(Parser *recognizer) {
+ atn::ATNState *s = recognizer->getInterpreter<atn::ATNSimulator>()->atn.states[recognizer->getState()];
+
+ // If already recovering, don't try to sync
+ if (inErrorRecoveryMode(recognizer)) {
+ return;
+ }
+
+ TokenStream *tokens = recognizer->getTokenStream();
+ size_t la = tokens->LA(1);
+
+ // try cheaper subset first; might get lucky. seems to shave a wee bit off
+ auto nextTokens = recognizer->getATN().nextTokens(s);
+ if (nextTokens.contains(Token::EPSILON) || nextTokens.contains(la)) {
+ return;
+ }
+
+ switch (s->getStateType()) {
+ case atn::ATNStateType::BLOCK_START:
+ case atn::ATNStateType::STAR_BLOCK_START:
+ case atn::ATNStateType::PLUS_BLOCK_START:
+ case atn::ATNStateType::STAR_LOOP_ENTRY:
+ // report error and recover if possible
+ if (singleTokenDeletion(recognizer) != nullptr) {
+ return;
+ }
+
+ throw InputMismatchException(recognizer);
+
+ case atn::ATNStateType::PLUS_LOOP_BACK:
+ case atn::ATNStateType::STAR_LOOP_BACK: {
+ reportUnwantedToken(recognizer);
+ misc::IntervalSet expecting = recognizer->getExpectedTokens();
+ misc::IntervalSet whatFollowsLoopIterationOrRule = expecting.Or(getErrorRecoverySet(recognizer));
+ consumeUntil(recognizer, whatFollowsLoopIterationOrRule);
+ }
+ break;
+
+ default:
+ // do nothing if we can't identify the exact kind of ATN state
+ break;
+ }
+}
+
+void DefaultErrorStrategy::reportNoViableAlternative(Parser *recognizer, const NoViableAltException &e) {
+ TokenStream *tokens = recognizer->getTokenStream();
+ std::string input;
+ if (tokens != nullptr) {
+ if (e.getStartToken()->getType() == Token::EOF) {
+ input = "<EOF>";
+ } else {
+ input = tokens->getText(e.getStartToken(), e.getOffendingToken());
+ }
+ } else {
+ input = "<unknown input>";
+ }
+ std::string msg = "no viable alternative at input " + escapeWSAndQuote(input);
+ recognizer->notifyErrorListeners(e.getOffendingToken(), msg, std::make_exception_ptr(e));
+}
+
+void DefaultErrorStrategy::reportInputMismatch(Parser *recognizer, const InputMismatchException &e) {
+ std::string msg = "mismatched input " + getTokenErrorDisplay(e.getOffendingToken()) +
+ " expecting " + e.getExpectedTokens().toString(recognizer->getVocabulary());
+ recognizer->notifyErrorListeners(e.getOffendingToken(), msg, std::make_exception_ptr(e));
+}
+
+void DefaultErrorStrategy::reportFailedPredicate(Parser *recognizer, const FailedPredicateException &e) {
+ const std::string& ruleName = recognizer->getRuleNames()[recognizer->getContext()->getRuleIndex()];
+ std::string msg = "rule " + ruleName + " " + e.what();
+ recognizer->notifyErrorListeners(e.getOffendingToken(), msg, std::make_exception_ptr(e));
+}
+
+void DefaultErrorStrategy::reportUnwantedToken(Parser *recognizer) {
+ if (inErrorRecoveryMode(recognizer)) {
+ return;
+ }
+
+ beginErrorCondition(recognizer);
+
+ Token *t = recognizer->getCurrentToken();
+ std::string tokenName = getTokenErrorDisplay(t);
+ misc::IntervalSet expecting = getExpectedTokens(recognizer);
+
+ std::string msg = "extraneous input " + tokenName + " expecting " + expecting.toString(recognizer->getVocabulary());
+ recognizer->notifyErrorListeners(t, msg, nullptr);
+}
+
+void DefaultErrorStrategy::reportMissingToken(Parser *recognizer) {
+ if (inErrorRecoveryMode(recognizer)) {
+ return;
+ }
+
+ beginErrorCondition(recognizer);
+
+ Token *t = recognizer->getCurrentToken();
+ misc::IntervalSet expecting = getExpectedTokens(recognizer);
+ std::string expectedText = expecting.toString(recognizer->getVocabulary());
+ std::string msg = "missing " + expectedText + " at " + getTokenErrorDisplay(t);
+
+ recognizer->notifyErrorListeners(t, msg, nullptr);
+}
+
+Token* DefaultErrorStrategy::recoverInline(Parser *recognizer) {
+ // Single token deletion.
+ Token *matchedSymbol = singleTokenDeletion(recognizer);
+ if (matchedSymbol) {
+ // We have deleted the extra token.
+ // Now, move past ttype token as if all were ok.
+ recognizer->consume();
+ return matchedSymbol;
+ }
+
+ // Single token insertion.
+ if (singleTokenInsertion(recognizer)) {
+ return getMissingSymbol(recognizer);
+ }
+
+ // Even that didn't work; must throw the exception.
+ throw InputMismatchException(recognizer);
+}
+
+bool DefaultErrorStrategy::singleTokenInsertion(Parser *recognizer) {
+ ssize_t currentSymbolType = recognizer->getInputStream()->LA(1);
+
+ // if current token is consistent with what could come after current
+ // ATN state, then we know we're missing a token; error recovery
+ // is free to conjure up and insert the missing token
+ atn::ATNState *currentState = recognizer->getInterpreter<atn::ATNSimulator>()->atn.states[recognizer->getState()];
+ atn::ATNState *next = currentState->transitions[0]->target;
+ const atn::ATN &atn = recognizer->getInterpreter<atn::ATNSimulator>()->atn;
+ misc::IntervalSet expectingAtLL2 = atn.nextTokens(next, recognizer->getContext());
+ if (expectingAtLL2.contains(currentSymbolType)) {
+ reportMissingToken(recognizer);
+ return true;
+ }
+ return false;
+}
+
+Token* DefaultErrorStrategy::singleTokenDeletion(Parser *recognizer) {
+ size_t nextTokenType = recognizer->getInputStream()->LA(2);
+ misc::IntervalSet expecting = getExpectedTokens(recognizer);
+ if (expecting.contains(nextTokenType)) {
+ reportUnwantedToken(recognizer);
+ recognizer->consume(); // simply delete extra token
+ // we want to return the token we're actually matching
+ Token *matchedSymbol = recognizer->getCurrentToken();
+ reportMatch(recognizer); // we know current token is correct
+ return matchedSymbol;
+ }
+ return nullptr;
+}
+
+Token* DefaultErrorStrategy::getMissingSymbol(Parser *recognizer) {
+ Token *currentSymbol = recognizer->getCurrentToken();
+ misc::IntervalSet expecting = getExpectedTokens(recognizer);
+ size_t expectedTokenType = expecting.getMinElement(); // get any element
+ std::string tokenText;
+ if (expectedTokenType == Token::EOF) {
+ tokenText = "<missing EOF>";
+ } else {
+ tokenText = "<missing " + recognizer->getVocabulary().getDisplayName(expectedTokenType) + ">";
+ }
+ Token *current = currentSymbol;
+ Token *lookback = recognizer->getTokenStream()->LT(-1);
+ if (current->getType() == Token::EOF && lookback != nullptr) {
+ current = lookback;
+ }
+
+ _errorSymbols.push_back(recognizer->getTokenFactory()->create(
+ { current->getTokenSource(), current->getTokenSource()->getInputStream() },
+ expectedTokenType, tokenText, Token::DEFAULT_CHANNEL, INVALID_INDEX, INVALID_INDEX,
+ current->getLine(), current->getCharPositionInLine()));
+
+ return _errorSymbols.back().get();
+}
+
+misc::IntervalSet DefaultErrorStrategy::getExpectedTokens(Parser *recognizer) {
+ return recognizer->getExpectedTokens();
+}
+
+std::string DefaultErrorStrategy::getTokenErrorDisplay(Token *t) {
+ if (t == nullptr) {
+ return "<no Token>";
+ }
+ std::string s = getSymbolText(t);
+ if (s == "") {
+ if (getSymbolType(t) == Token::EOF) {
+ s = "<EOF>";
+ } else {
+ s = "<" + std::to_string(getSymbolType(t)) + ">";
+ }
+ }
+ return escapeWSAndQuote(s);
+}
+
+std::string DefaultErrorStrategy::getSymbolText(Token *symbol) {
+ return symbol->getText();
+}
+
+size_t DefaultErrorStrategy::getSymbolType(Token *symbol) {
+ return symbol->getType();
+}
+
+std::string DefaultErrorStrategy::escapeWSAndQuote(const std::string &s) const {
+ std::string result;
+ result.reserve(s.size() + 2);
+ result.push_back('\'');
+ antlrcpp::escapeWhitespace(result, s);
+ result.push_back('\'');
+ result.shrink_to_fit();
+ return result;
+}
+
+misc::IntervalSet DefaultErrorStrategy::getErrorRecoverySet(Parser *recognizer) {
+ const atn::ATN &atn = recognizer->getInterpreter<atn::ATNSimulator>()->atn;
+ RuleContext *ctx = recognizer->getContext();
+ misc::IntervalSet recoverSet;
+ while (ctx->invokingState != ATNState::INVALID_STATE_NUMBER) {
+ // compute what follows who invoked us
+ atn::ATNState *invokingState = atn.states[ctx->invokingState];
+ const atn::RuleTransition *rt = downCast<const atn::RuleTransition*>(invokingState->transitions[0].get());
+ misc::IntervalSet follow = atn.nextTokens(rt->followState);
+ recoverSet.addAll(follow);
+
+ if (ctx->parent == nullptr)
+ break;
+ ctx = static_cast<RuleContext *>(ctx->parent);
+ }
+ recoverSet.remove(Token::EPSILON);
+
+ return recoverSet;
+}
+
+void DefaultErrorStrategy::consumeUntil(Parser *recognizer, const misc::IntervalSet &set) {
+ size_t ttype = recognizer->getInputStream()->LA(1);
+ while (ttype != Token::EOF && !set.contains(ttype)) {
+ recognizer->consume();
+ ttype = recognizer->getInputStream()->LA(1);
+ }
+}
+
+void DefaultErrorStrategy::InitializeInstanceFields() {
+ errorRecoveryMode = false;
+ lastErrorIndex = -1;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/DefaultErrorStrategy.h b/contrib/libs/antlr4_cpp_runtime/src/DefaultErrorStrategy.h
new file mode 100644
index 0000000000..7b914468cf
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/DefaultErrorStrategy.h
@@ -0,0 +1,466 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "ANTLRErrorStrategy.h"
+#include "misc/IntervalSet.h"
+
+namespace antlr4 {
+
+ /**
+ * This is the default implementation of {@link ANTLRErrorStrategy} used for
+ * error reporting and recovery in ANTLR parsers.
+ */
+ class ANTLR4CPP_PUBLIC DefaultErrorStrategy : public ANTLRErrorStrategy {
+ public:
+ DefaultErrorStrategy();
+ DefaultErrorStrategy(DefaultErrorStrategy const& other) = delete;
+ virtual ~DefaultErrorStrategy();
+
+ DefaultErrorStrategy& operator = (DefaultErrorStrategy const& other) = delete;
+
+ protected:
+ /**
+ * Indicates whether the error strategy is currently "recovering from an
+ * error". This is used to suppress reporting multiple error messages while
+ * attempting to recover from a detected syntax error.
+ *
+ * @see #inErrorRecoveryMode
+ */
+ bool errorRecoveryMode;
+
+ /** The index into the input stream where the last error occurred.
+ * This is used to prevent infinite loops where an error is found
+ * but no token is consumed during recovery...another error is found,
+ * ad nauseum. This is a failsafe mechanism to guarantee that at least
+ * one token/tree node is consumed for two errors.
+ */
+ int lastErrorIndex;
+
+ misc::IntervalSet lastErrorStates;
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// <p/>
+ /// The default implementation simply calls <seealso cref="#endErrorCondition"/> to
+ /// ensure that the handler is not in error recovery mode.
+ /// </summary>
+ public:
+ virtual void reset(Parser *recognizer) override;
+
+ /// <summary>
+ /// This method is called to enter error recovery mode when a recognition
+ /// exception is reported.
+ /// </summary>
+ /// <param name="recognizer"> the parser instance </param>
+ protected:
+ virtual void beginErrorCondition(Parser *recognizer);
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// </summary>
+ public:
+ virtual bool inErrorRecoveryMode(Parser *recognizer) override;
+
+ /// <summary>
+ /// This method is called to leave error recovery mode after recovering from
+ /// a recognition exception.
+ /// </summary>
+ /// <param name="recognizer"> </param>
+ protected:
+ virtual void endErrorCondition(Parser *recognizer);
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// <p/>
+ /// The default implementation simply calls <seealso cref="#endErrorCondition"/>.
+ /// </summary>
+ public:
+ virtual void reportMatch(Parser *recognizer) override;
+
+ /// {@inheritDoc}
+ /// <p/>
+ /// The default implementation returns immediately if the handler is already
+ /// in error recovery mode. Otherwise, it calls <seealso cref="#beginErrorCondition"/>
+ /// and dispatches the reporting task based on the runtime type of {@code e}
+ /// according to the following table.
+ ///
+ /// <ul>
+ /// <li><seealso cref="NoViableAltException"/>: Dispatches the call to
+ /// <seealso cref="#reportNoViableAlternative"/></li>
+ /// <li><seealso cref="InputMismatchException"/>: Dispatches the call to
+ /// <seealso cref="#reportInputMismatch"/></li>
+ /// <li><seealso cref="FailedPredicateException"/>: Dispatches the call to
+ /// <seealso cref="#reportFailedPredicate"/></li>
+ /// <li>All other types: calls <seealso cref="Parser#notifyErrorListeners"/> to report
+ /// the exception</li>
+ /// </ul>
+ virtual void reportError(Parser *recognizer, const RecognitionException &e) override;
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// <p/>
+ /// The default implementation resynchronizes the parser by consuming tokens
+ /// until we find one in the resynchronization set--loosely the set of tokens
+ /// that can follow the current rule.
+ /// </summary>
+ virtual void recover(Parser *recognizer, std::exception_ptr e) override;
+
+ /**
+ * The default implementation of {@link ANTLRErrorStrategy#sync} makes sure
+ * that the current lookahead symbol is consistent with what were expecting
+ * at this point in the ATN. You can call this anytime but ANTLR only
+ * generates code to check before subrules/loops and each iteration.
+ *
+ * <p>Implements Jim Idle's magic sync mechanism in closures and optional
+ * subrules. E.g.,</p>
+ *
+ * <pre>
+ * a : sync ( stuff sync )* ;
+ * sync : {consume to what can follow sync} ;
+ * </pre>
+ *
+ * At the start of a sub rule upon error, {@link #sync} performs single
+ * token deletion, if possible. If it can't do that, it bails on the current
+ * rule and uses the default error recovery, which consumes until the
+ * resynchronization set of the current rule.
+ *
+ * <p>If the sub rule is optional ({@code (...)?}, {@code (...)*}, or block
+ * with an empty alternative), then the expected set includes what follows
+ * the subrule.</p>
+ *
+ * <p>During loop iteration, it consumes until it sees a token that can start a
+ * sub rule or what follows loop. Yes, that is pretty aggressive. We opt to
+ * stay in the loop as long as possible.</p>
+ *
+ * <p><strong>ORIGINS</strong></p>
+ *
+ * <p>Previous versions of ANTLR did a poor job of their recovery within loops.
+ * A single mismatch token or missing token would force the parser to bail
+ * out of the entire rules surrounding the loop. So, for rule</p>
+ *
+ * <pre>
+ * classDef : 'class' ID '{' member* '}'
+ * </pre>
+ *
+ * input with an extra token between members would force the parser to
+ * consume until it found the next class definition rather than the next
+ * member definition of the current class.
+ *
+ * <p>This functionality cost a little bit of effort because the parser has to
+ * compare token set at the start of the loop and at each iteration. If for
+ * some reason speed is suffering for you, you can turn off this
+ * functionality by simply overriding this method as a blank { }.</p>
+ */
+ virtual void sync(Parser *recognizer) override;
+
+ /// <summary>
+ /// This is called by <seealso cref="#reportError"/> when the exception is a
+ /// <seealso cref="NoViableAltException"/>.
+ /// </summary>
+ /// <seealso cref= #reportError
+ /// </seealso>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <param name="e"> the recognition exception </param>
+ protected:
+ virtual void reportNoViableAlternative(Parser *recognizer, const NoViableAltException &e);
+
+ /// <summary>
+ /// This is called by <seealso cref="#reportError"/> when the exception is an
+ /// <seealso cref="InputMismatchException"/>.
+ /// </summary>
+ /// <seealso cref= #reportError
+ /// </seealso>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <param name="e"> the recognition exception </param>
+ virtual void reportInputMismatch(Parser *recognizer, const InputMismatchException &e);
+
+ /// <summary>
+ /// This is called by <seealso cref="#reportError"/> when the exception is a
+ /// <seealso cref="FailedPredicateException"/>.
+ /// </summary>
+ /// <seealso cref= #reportError
+ /// </seealso>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <param name="e"> the recognition exception </param>
+ virtual void reportFailedPredicate(Parser *recognizer, const FailedPredicateException &e);
+
+ /**
+ * This method is called to report a syntax error which requires the removal
+ * of a token from the input stream. At the time this method is called, the
+ * erroneous symbol is current {@code LT(1)} symbol and has not yet been
+ * removed from the input stream. When this method returns,
+ * {@code recognizer} is in error recovery mode.
+ *
+ * <p>This method is called when {@link #singleTokenDeletion} identifies
+ * single-token deletion as a viable recovery strategy for a mismatched
+ * input error.</p>
+ *
+ * <p>The default implementation simply returns if the handler is already in
+ * error recovery mode. Otherwise, it calls {@link #beginErrorCondition} to
+ * enter error recovery mode, followed by calling
+ * {@link Parser#notifyErrorListeners}.</p>
+ *
+ * @param recognizer the parser instance
+ */
+ virtual void reportUnwantedToken(Parser *recognizer);
+
+ /**
+ * This method is called to report a syntax error which requires the
+ * insertion of a missing token into the input stream. At the time this
+ * method is called, the missing token has not yet been inserted. When this
+ * method returns, {@code recognizer} is in error recovery mode.
+ *
+ * <p>This method is called when {@link #singleTokenInsertion} identifies
+ * single-token insertion as a viable recovery strategy for a mismatched
+ * input error.</p>
+ *
+ * <p>The default implementation simply returns if the handler is already in
+ * error recovery mode. Otherwise, it calls {@link #beginErrorCondition} to
+ * enter error recovery mode, followed by calling
+ * {@link Parser#notifyErrorListeners}.</p>
+ *
+ * @param recognizer the parser instance
+ */
+ virtual void reportMissingToken(Parser *recognizer);
+
+ public:
+ /**
+ * {@inheritDoc}
+ *
+ * <p>The default implementation attempts to recover from the mismatched input
+ * by using single token insertion and deletion as described below. If the
+ * recovery attempt fails, this method throws an
+ * {@link InputMismatchException}.</p>
+ *
+ * <p><strong>EXTRA TOKEN</strong> (single token deletion)</p>
+ *
+ * <p>{@code LA(1)} is not what we are looking for. If {@code LA(2)} has the
+ * right token, however, then assume {@code LA(1)} is some extra spurious
+ * token and delete it. Then consume and return the next token (which was
+ * the {@code LA(2)} token) as the successful result of the match operation.</p>
+ *
+ * <p>This recovery strategy is implemented by {@link #singleTokenDeletion}.</p>
+ *
+ * <p><strong>MISSING TOKEN</strong> (single token insertion)</p>
+ *
+ * <p>If current token (at {@code LA(1)}) is consistent with what could come
+ * after the expected {@code LA(1)} token, then assume the token is missing
+ * and use the parser's {@link TokenFactory} to create it on the fly. The
+ * "insertion" is performed by returning the created token as the successful
+ * result of the match operation.</p>
+ *
+ * <p>This recovery strategy is implemented by {@link #singleTokenInsertion}.</p>
+ *
+ * <p><strong>EXAMPLE</strong></p>
+ *
+ * <p>For example, Input {@code i=(3;} is clearly missing the {@code ')'}. When
+ * the parser returns from the nested call to {@code expr}, it will have
+ * call chain:</p>
+ *
+ * <pre>
+ * stat &rarr; expr &rarr; atom
+ * </pre>
+ *
+ * and it will be trying to match the {@code ')'} at this point in the
+ * derivation:
+ *
+ * <pre>
+ * =&gt; ID '=' '(' INT ')' ('+' atom)* ';'
+ * ^
+ * </pre>
+ *
+ * The attempt to match {@code ')'} will fail when it sees {@code ';'} and
+ * call {@link #recoverInline}. To recover, it sees that {@code LA(1)==';'}
+ * is in the set of tokens that can follow the {@code ')'} token reference
+ * in rule {@code atom}. It can assume that you forgot the {@code ')'}.
+ */
+ virtual Token* recoverInline(Parser *recognizer) override;
+
+ /// <summary>
+ /// This method implements the single-token insertion inline error recovery
+ /// strategy. It is called by <seealso cref="#recoverInline"/> if the single-token
+ /// deletion strategy fails to recover from the mismatched input. If this
+ /// method returns {@code true}, {@code recognizer} will be in error recovery
+ /// mode.
+ /// <p/>
+ /// This method determines whether or not single-token insertion is viable by
+ /// checking if the {@code LA(1)} input symbol could be successfully matched
+ /// if it were instead the {@code LA(2)} symbol. If this method returns
+ /// {@code true}, the caller is responsible for creating and inserting a
+ /// token with the correct type to produce this behavior.
+ /// </summary>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <returns> {@code true} if single-token insertion is a viable recovery
+ /// strategy for the current mismatched input, otherwise {@code false} </returns>
+ protected:
+ virtual bool singleTokenInsertion(Parser *recognizer);
+
+ /// <summary>
+ /// This method implements the single-token deletion inline error recovery
+ /// strategy. It is called by <seealso cref="#recoverInline"/> to attempt to recover
+ /// from mismatched input. If this method returns null, the parser and error
+ /// handler state will not have changed. If this method returns non-null,
+ /// {@code recognizer} will <em>not</em> be in error recovery mode since the
+ /// returned token was a successful match.
+ /// <p/>
+ /// If the single-token deletion is successful, this method calls
+ /// <seealso cref="#reportUnwantedToken"/> to report the error, followed by
+ /// <seealso cref="Parser#consume"/> to actually "delete" the extraneous token. Then,
+ /// before returning <seealso cref="#reportMatch"/> is called to signal a successful
+ /// match.
+ /// </summary>
+ /// <param name="recognizer"> the parser instance </param>
+ /// <returns> the successfully matched <seealso cref="Token"/> instance if single-token
+ /// deletion successfully recovers from the mismatched input, otherwise
+ /// {@code null} </returns>
+ virtual Token* singleTokenDeletion(Parser *recognizer);
+
+ /// <summary>
+ /// Conjure up a missing token during error recovery.
+ ///
+ /// The recognizer attempts to recover from single missing
+ /// symbols. But, actions might refer to that missing symbol.
+ /// For example, x=ID {f($x);}. The action clearly assumes
+ /// that there has been an identifier matched previously and that
+ /// $x points at that token. If that token is missing, but
+ /// the next token in the stream is what we want we assume that
+ /// this token is missing and we keep going. Because we
+ /// have to return some token to replace the missing token,
+ /// we have to conjure one up. This method gives the user control
+ /// over the tokens returned for missing tokens. Mostly,
+ /// you will want to create something special for identifier
+ /// tokens. For literals such as '{' and ',', the default
+ /// action in the parser or tree parser works. It simply creates
+ /// a CommonToken of the appropriate type. The text will be the token.
+ /// If you change what tokens must be created by the lexer,
+ /// override this method to create the appropriate tokens.
+ /// </summary>
+ virtual Token* getMissingSymbol(Parser *recognizer);
+
+ virtual misc::IntervalSet getExpectedTokens(Parser *recognizer);
+
+ /// <summary>
+ /// How should a token be displayed in an error message? The default
+ /// is to display just the text, but during development you might
+ /// want to have a lot of information spit out. Override in that case
+ /// to use t.toString() (which, for CommonToken, dumps everything about
+ /// the token). This is better than forcing you to override a method in
+ /// your token objects because you don't have to go modify your lexer
+ /// so that it creates a new class.
+ /// </summary>
+ virtual std::string getTokenErrorDisplay(Token *t);
+
+ virtual std::string getSymbolText(Token *symbol);
+
+ virtual size_t getSymbolType(Token *symbol);
+
+ virtual std::string escapeWSAndQuote(const std::string &s) const;
+
+ /* Compute the error recovery set for the current rule. During
+ * rule invocation, the parser pushes the set of tokens that can
+ * follow that rule reference on the stack; this amounts to
+ * computing FIRST of what follows the rule reference in the
+ * enclosing rule. See LinearApproximator.FIRST().
+ * This local follow set only includes tokens
+ * from within the rule; i.e., the FIRST computation done by
+ * ANTLR stops at the end of a rule.
+ *
+ * EXAMPLE
+ *
+ * When you find a "no viable alt exception", the input is not
+ * consistent with any of the alternatives for rule r. The best
+ * thing to do is to consume tokens until you see something that
+ * can legally follow a call to r *or* any rule that called r.
+ * You don't want the exact set of viable next tokens because the
+ * input might just be missing a token--you might consume the
+ * rest of the input looking for one of the missing tokens.
+ *
+ * Consider grammar:
+ *
+ * a : '[' b ']'
+ * | '(' b ')'
+ * ;
+ * b : c '^' INT ;
+ * c : ID
+ * | INT
+ * ;
+ *
+ * At each rule invocation, the set of tokens that could follow
+ * that rule is pushed on a stack. Here are the various
+ * context-sensitive follow sets:
+ *
+ * FOLLOW(b1_in_a) = FIRST(']') = ']'
+ * FOLLOW(b2_in_a) = FIRST(')') = ')'
+ * FOLLOW(c_in_b) = FIRST('^') = '^'
+ *
+ * Upon erroneous input "[]", the call chain is
+ *
+ * a -> b -> c
+ *
+ * and, hence, the follow context stack is:
+ *
+ * depth follow set start of rule execution
+ * 0 <EOF> a (from main())
+ * 1 ']' b
+ * 2 '^' c
+ *
+ * Notice that ')' is not included, because b would have to have
+ * been called from a different context in rule a for ')' to be
+ * included.
+ *
+ * For error recovery, we cannot consider FOLLOW(c)
+ * (context-sensitive or otherwise). We need the combined set of
+ * all context-sensitive FOLLOW sets--the set of all tokens that
+ * could follow any reference in the call chain. We need to
+ * resync to one of those tokens. Note that FOLLOW(c)='^' and if
+ * we resync'd to that token, we'd consume until EOF. We need to
+ * sync to context-sensitive FOLLOWs for a, b, and c: {']','^'}.
+ * In this case, for input "[]", LA(1) is ']' and in the set, so we would
+ * not consume anything. After printing an error, rule c would
+ * return normally. Rule b would not find the required '^' though.
+ * At this point, it gets a mismatched token error and throws an
+ * exception (since LA(1) is not in the viable following token
+ * set). The rule exception handler tries to recover, but finds
+ * the same recovery set and doesn't consume anything. Rule b
+ * exits normally returning to rule a. Now it finds the ']' (and
+ * with the successful match exits errorRecovery mode).
+ *
+ * So, you can see that the parser walks up the call chain looking
+ * for the token that was a member of the recovery set.
+ *
+ * Errors are not generated in errorRecovery mode.
+ *
+ * ANTLR's error recovery mechanism is based upon original ideas:
+ *
+ * "Algorithms + Data Structures = Programs" by Niklaus Wirth
+ *
+ * and
+ *
+ * "A note on error recovery in recursive descent parsers":
+ * http://portal.acm.org/citation.cfm?id=947902.947905
+ *
+ * Later, Josef Grosch had some good ideas:
+ *
+ * "Efficient and Comfortable Error Recovery in Recursive Descent
+ * Parsers":
+ * ftp://www.cocolab.com/products/cocktail/doca4.ps/ell.ps.zip
+ *
+ * Like Grosch I implement context-sensitive FOLLOW sets that are combined
+ * at run-time upon error to avoid overhead during parsing.
+ */
+ virtual misc::IntervalSet getErrorRecoverySet(Parser *recognizer);
+
+ /// <summary>
+ /// Consume tokens until one matches the given token set. </summary>
+ virtual void consumeUntil(Parser *recognizer, const misc::IntervalSet &set);
+
+ private:
+ std::vector<std::unique_ptr<Token>> _errorSymbols; // Temporarily created token.
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/DiagnosticErrorListener.cpp b/contrib/libs/antlr4_cpp_runtime/src/DiagnosticErrorListener.cpp
new file mode 100644
index 0000000000..ef6f64372d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/DiagnosticErrorListener.cpp
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/PredictionContext.h"
+#include "atn/ATNConfig.h"
+#include "atn/ATNConfigSet.h"
+#include "Parser.h"
+#include "misc/Interval.h"
+#include "dfa/DFA.h"
+
+#include "DiagnosticErrorListener.h"
+
+using namespace antlr4;
+
+DiagnosticErrorListener::DiagnosticErrorListener() : DiagnosticErrorListener(true) {
+}
+
+DiagnosticErrorListener::DiagnosticErrorListener(bool exactOnly_) : exactOnly(exactOnly_) {
+}
+
+void DiagnosticErrorListener::reportAmbiguity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ bool exact, const antlrcpp::BitSet &ambigAlts, atn::ATNConfigSet *configs) {
+ if (exactOnly && !exact) {
+ return;
+ }
+
+ std::string decision = getDecisionDescription(recognizer, dfa);
+ antlrcpp::BitSet conflictingAlts = getConflictingAlts(ambigAlts, configs);
+ std::string text = recognizer->getTokenStream()->getText(misc::Interval(startIndex, stopIndex));
+ std::string message = "reportAmbiguity d=" + decision + ": ambigAlts=" + conflictingAlts.toString() +
+ ", input='" + text + "'";
+
+ recognizer->notifyErrorListeners(message);
+}
+
+void DiagnosticErrorListener::reportAttemptingFullContext(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex,
+ size_t stopIndex, const antlrcpp::BitSet &/*conflictingAlts*/, atn::ATNConfigSet * /*configs*/) {
+ std::string decision = getDecisionDescription(recognizer, dfa);
+ std::string text = recognizer->getTokenStream()->getText(misc::Interval(startIndex, stopIndex));
+ std::string message = "reportAttemptingFullContext d=" + decision + ", input='" + text + "'";
+ recognizer->notifyErrorListeners(message);
+}
+
+void DiagnosticErrorListener::reportContextSensitivity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex,
+ size_t stopIndex, size_t /*prediction*/, atn::ATNConfigSet * /*configs*/) {
+ std::string decision = getDecisionDescription(recognizer, dfa);
+ std::string text = recognizer->getTokenStream()->getText(misc::Interval(startIndex, stopIndex));
+ std::string message = "reportContextSensitivity d=" + decision + ", input='" + text + "'";
+ recognizer->notifyErrorListeners(message);
+}
+
+std::string DiagnosticErrorListener::getDecisionDescription(Parser *recognizer, const dfa::DFA &dfa) {
+ size_t decision = dfa.decision;
+ size_t ruleIndex = (reinterpret_cast<atn::ATNState*>(dfa.atnStartState))->ruleIndex;
+
+ const std::vector<std::string>& ruleNames = recognizer->getRuleNames();
+ if (ruleIndex == INVALID_INDEX || ruleIndex >= ruleNames.size()) {
+ return std::to_string(decision);
+ }
+
+ std::string ruleName = ruleNames[ruleIndex];
+ if (ruleName == "" || ruleName.empty()) {
+ return std::to_string(decision);
+ }
+
+ return std::to_string(decision) + " (" + ruleName + ")";
+}
+
+antlrcpp::BitSet DiagnosticErrorListener::getConflictingAlts(const antlrcpp::BitSet &reportedAlts,
+ atn::ATNConfigSet *configs) {
+ if (reportedAlts.count() > 0) { // Not exactly like the original Java code, but this listener is only used
+ // in the TestRig (where it never provides a good alt set), so it's probably ok so.
+ return reportedAlts;
+ }
+
+ antlrcpp::BitSet result;
+ for (auto &config : configs->configs) {
+ result.set(config->alt);
+ }
+
+ return result;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/DiagnosticErrorListener.h b/contrib/libs/antlr4_cpp_runtime/src/DiagnosticErrorListener.h
new file mode 100644
index 0000000000..ed6d749429
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/DiagnosticErrorListener.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "BaseErrorListener.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// This implementation of <seealso cref="ANTLRErrorListener"/> can be used to identify
+ /// certain potential correctness and performance problems in grammars. "Reports"
+ /// are made by calling <seealso cref="Parser#notifyErrorListeners"/> with the appropriate
+ /// message.
+ ///
+ /// <ul>
+ /// <li><b>Ambiguities</b>: These are cases where more than one path through the
+ /// grammar can match the input.</li>
+ /// <li><b>Weak context sensitivity</b>: These are cases where full-context
+ /// prediction resolved an SLL conflict to a unique alternative which equaled the
+ /// minimum alternative of the SLL conflict.</li>
+ /// <li><b>Strong (forced) context sensitivity</b>: These are cases where the
+ /// full-context prediction resolved an SLL conflict to a unique alternative,
+ /// <em>and</em> the minimum alternative of the SLL conflict was found to not be
+ /// a truly viable alternative. Two-stage parsing cannot be used for inputs where
+ /// this situation occurs.</li>
+ /// </ul>
+ ///
+ /// @author Sam Harwell
+ /// </summary>
+ class ANTLR4CPP_PUBLIC DiagnosticErrorListener : public BaseErrorListener {
+ /// <summary>
+ /// When {@code true}, only exactly known ambiguities are reported.
+ /// </summary>
+ protected:
+ const bool exactOnly;
+
+ /// <summary>
+ /// Initializes a new instance of <seealso cref="DiagnosticErrorListener"/> which only
+ /// reports exact ambiguities.
+ /// </summary>
+ public:
+ DiagnosticErrorListener();
+
+ /// <summary>
+ /// Initializes a new instance of <seealso cref="DiagnosticErrorListener"/>, specifying
+ /// whether all ambiguities or only exact ambiguities are reported.
+ /// </summary>
+ /// <param name="exactOnly"> {@code true} to report only exact ambiguities, otherwise
+ /// {@code false} to report all ambiguities. </param>
+ DiagnosticErrorListener(bool exactOnly);
+
+ virtual void reportAmbiguity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact,
+ const antlrcpp::BitSet &ambigAlts, atn::ATNConfigSet *configs) override;
+
+ virtual void reportAttemptingFullContext(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ const antlrcpp::BitSet &conflictingAlts, atn::ATNConfigSet *configs) override;
+
+ virtual void reportContextSensitivity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ size_t prediction, atn::ATNConfigSet *configs) override;
+
+ protected:
+ virtual std::string getDecisionDescription(Parser *recognizer, const dfa::DFA &dfa);
+
+ /// <summary>
+ /// Computes the set of conflicting or ambiguous alternatives from a
+ /// configuration set, if that information was not already provided by the
+ /// parser.
+ /// </summary>
+ /// <param name="reportedAlts"> The set of conflicting or ambiguous alternatives, as
+ /// reported by the parser. </param>
+ /// <param name="configs"> The conflicting or ambiguous configuration set. </param>
+ /// <returns> Returns {@code reportedAlts} if it is not {@code null}, otherwise
+ /// returns the set of alternatives represented in {@code configs}. </returns>
+ virtual antlrcpp::BitSet getConflictingAlts(const antlrcpp::BitSet &reportedAlts, atn::ATNConfigSet *configs);
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Exceptions.cpp b/contrib/libs/antlr4_cpp_runtime/src/Exceptions.cpp
new file mode 100644
index 0000000000..24aea29b0c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Exceptions.cpp
@@ -0,0 +1,64 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Exceptions.h"
+
+using namespace antlr4;
+
+RuntimeException::RuntimeException(const std::string &msg) : std::exception(), _message(msg) {
+}
+
+const char* RuntimeException::what() const noexcept {
+ return _message.c_str();
+}
+
+//------------------ IOException ---------------------------------------------------------------------------------------
+
+IOException::IOException(const std::string &msg) : std::exception(), _message(msg) {
+}
+
+const char* IOException::what() const noexcept {
+ return _message.c_str();
+}
+
+//------------------ IllegalStateException -----------------------------------------------------------------------------
+
+IllegalStateException::~IllegalStateException() {
+}
+
+//------------------ IllegalArgumentException --------------------------------------------------------------------------
+
+IllegalArgumentException::~IllegalArgumentException() {
+}
+
+//------------------ NullPointerException ------------------------------------------------------------------------------
+
+NullPointerException::~NullPointerException() {
+}
+
+//------------------ IndexOutOfBoundsException -------------------------------------------------------------------------
+
+IndexOutOfBoundsException::~IndexOutOfBoundsException() {
+}
+
+//------------------ UnsupportedOperationException ---------------------------------------------------------------------
+
+UnsupportedOperationException::~UnsupportedOperationException() {
+}
+
+//------------------ EmptyStackException -------------------------------------------------------------------------------
+
+EmptyStackException::~EmptyStackException() {
+}
+
+//------------------ CancellationException -----------------------------------------------------------------------------
+
+CancellationException::~CancellationException() {
+}
+
+//------------------ ParseCancellationException ------------------------------------------------------------------------
+
+ParseCancellationException::~ParseCancellationException() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Exceptions.h b/contrib/libs/antlr4_cpp_runtime/src/Exceptions.h
new file mode 100644
index 0000000000..35d72b52ee
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Exceptions.h
@@ -0,0 +1,99 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+
+ // An exception hierarchy modelled loosely after java.lang.* exceptions.
+ class ANTLR4CPP_PUBLIC RuntimeException : public std::exception {
+ private:
+ std::string _message;
+ public:
+ RuntimeException(const std::string &msg = "");
+
+ virtual const char* what() const noexcept override;
+ };
+
+ class ANTLR4CPP_PUBLIC IllegalStateException : public RuntimeException {
+ public:
+ IllegalStateException(const std::string &msg = "") : RuntimeException(msg) {}
+ IllegalStateException(IllegalStateException const&) = default;
+ ~IllegalStateException();
+ IllegalStateException& operator=(IllegalStateException const&) = default;
+ };
+
+ class ANTLR4CPP_PUBLIC IllegalArgumentException : public RuntimeException {
+ public:
+ IllegalArgumentException(IllegalArgumentException const&) = default;
+ IllegalArgumentException(const std::string &msg = "") : RuntimeException(msg) {}
+ ~IllegalArgumentException();
+ IllegalArgumentException& operator=(IllegalArgumentException const&) = default;
+ };
+
+ class ANTLR4CPP_PUBLIC NullPointerException : public RuntimeException {
+ public:
+ NullPointerException(const std::string &msg = "") : RuntimeException(msg) {}
+ NullPointerException(NullPointerException const&) = default;
+ ~NullPointerException();
+ NullPointerException& operator=(NullPointerException const&) = default;
+ };
+
+ class ANTLR4CPP_PUBLIC IndexOutOfBoundsException : public RuntimeException {
+ public:
+ IndexOutOfBoundsException(const std::string &msg = "") : RuntimeException(msg) {}
+ IndexOutOfBoundsException(IndexOutOfBoundsException const&) = default;
+ ~IndexOutOfBoundsException();
+ IndexOutOfBoundsException& operator=(IndexOutOfBoundsException const&) = default;
+ };
+
+ class ANTLR4CPP_PUBLIC UnsupportedOperationException : public RuntimeException {
+ public:
+ UnsupportedOperationException(const std::string &msg = "") : RuntimeException(msg) {}
+ UnsupportedOperationException(UnsupportedOperationException const&) = default;
+ ~UnsupportedOperationException();
+ UnsupportedOperationException& operator=(UnsupportedOperationException const&) = default;
+
+ };
+
+ class ANTLR4CPP_PUBLIC EmptyStackException : public RuntimeException {
+ public:
+ EmptyStackException(const std::string &msg = "") : RuntimeException(msg) {}
+ EmptyStackException(EmptyStackException const&) = default;
+ ~EmptyStackException();
+ EmptyStackException& operator=(EmptyStackException const&) = default;
+ };
+
+ // IOException is not a runtime exception (in the java hierarchy).
+ // Hence we have to duplicate the RuntimeException implementation.
+ class ANTLR4CPP_PUBLIC IOException : public std::exception {
+ private:
+ std::string _message;
+
+ public:
+ IOException(const std::string &msg = "");
+
+ virtual const char* what() const noexcept override;
+ };
+
+ class ANTLR4CPP_PUBLIC CancellationException : public IllegalStateException {
+ public:
+ CancellationException(const std::string &msg = "") : IllegalStateException(msg) {}
+ CancellationException(CancellationException const&) = default;
+ ~CancellationException();
+ CancellationException& operator=(CancellationException const&) = default;
+ };
+
+ class ANTLR4CPP_PUBLIC ParseCancellationException : public CancellationException {
+ public:
+ ParseCancellationException(const std::string &msg = "") : CancellationException(msg) {}
+ ParseCancellationException(ParseCancellationException const&) = default;
+ ~ParseCancellationException();
+ ParseCancellationException& operator=(ParseCancellationException const&) = default;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/FailedPredicateException.cpp b/contrib/libs/antlr4_cpp_runtime/src/FailedPredicateException.cpp
new file mode 100644
index 0000000000..ca2537b300
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/FailedPredicateException.cpp
@@ -0,0 +1,52 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ParserATNSimulator.h"
+#include "Parser.h"
+#include "atn/PredicateTransition.h"
+#include "atn/ATN.h"
+#include "atn/ATNState.h"
+#include "support/Casts.h"
+#include "support/CPPUtils.h"
+
+#include "FailedPredicateException.h"
+
+using namespace antlr4;
+using namespace antlrcpp;
+
+FailedPredicateException::FailedPredicateException(Parser *recognizer) : FailedPredicateException(recognizer, "", "") {
+}
+
+FailedPredicateException::FailedPredicateException(Parser *recognizer, const std::string &predicate): FailedPredicateException(recognizer, predicate, "") {
+}
+
+FailedPredicateException::FailedPredicateException(Parser *recognizer, const std::string &predicate, const std::string &message)
+ : RecognitionException(!message.empty() ? message : "failed predicate: " + predicate + "?", recognizer,
+ recognizer->getInputStream(), recognizer->getContext(), recognizer->getCurrentToken()) {
+
+ atn::ATNState *s = recognizer->getInterpreter<atn::ATNSimulator>()->atn.states[recognizer->getState()];
+ const atn::Transition *transition = s->transitions[0].get();
+ if (transition->getTransitionType() == atn::TransitionType::PREDICATE) {
+ _ruleIndex = downCast<const atn::PredicateTransition&>(*transition).getRuleIndex();
+ _predicateIndex = downCast<const atn::PredicateTransition&>(*transition).getPredIndex();
+ } else {
+ _ruleIndex = 0;
+ _predicateIndex = 0;
+ }
+
+ _predicate = predicate;
+}
+
+size_t FailedPredicateException::getRuleIndex() {
+ return _ruleIndex;
+}
+
+size_t FailedPredicateException::getPredIndex() {
+ return _predicateIndex;
+}
+
+std::string FailedPredicateException::getPredicate() {
+ return _predicate;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/FailedPredicateException.h b/contrib/libs/antlr4_cpp_runtime/src/FailedPredicateException.h
new file mode 100644
index 0000000000..89bec0fd0b
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/FailedPredicateException.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "RecognitionException.h"
+
+namespace antlr4 {
+
+ /// A semantic predicate failed during validation. Validation of predicates
+ /// occurs when normally parsing the alternative just like matching a token.
+ /// Disambiguating predicate evaluation occurs when we test a predicate during
+ /// prediction.
+ class ANTLR4CPP_PUBLIC FailedPredicateException : public RecognitionException {
+ public:
+ explicit FailedPredicateException(Parser *recognizer);
+ FailedPredicateException(Parser *recognizer, const std::string &predicate);
+ FailedPredicateException(Parser *recognizer, const std::string &predicate, const std::string &message);
+
+ virtual size_t getRuleIndex();
+ virtual size_t getPredIndex();
+ virtual std::string getPredicate();
+
+ private:
+ size_t _ruleIndex;
+ size_t _predicateIndex;
+ std::string _predicate;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/FlatHashMap.h b/contrib/libs/antlr4_cpp_runtime/src/FlatHashMap.h
new file mode 100644
index 0000000000..ad5ffa2432
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/FlatHashMap.h
@@ -0,0 +1,57 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#pragma once
+
+#include "antlr4-common.h"
+
+#if ANTLR4CPP_USING_ABSEIL
+#error #include "absl/container/flat_hash_map.h"
+#else
+#include <unordered_map>
+#endif
+
+// By default ANTLRv4 uses containers provided by the C++ standard library. In most deployments this
+// is fine, however in some using custom containers may be preferred. This header allows that by
+// optionally supporting some alternative implementations and allowing for more easier patching of
+// other alternatives.
+
+namespace antlr4 {
+
+#if ANTLR4CPP_USING_ABSEIL
+ template <typename Key, typename Value,
+ typename Hash = typename absl::flat_hash_map<Key, Value>::hasher,
+ typename Equal = typename absl::flat_hash_map<Key, Value>::key_equal,
+ typename Allocator = typename absl::flat_hash_map<Key, Value>::allocator_type>
+ using FlatHashMap = absl::flat_hash_map<Key, Value, Hash, Equal, Allocator>;
+#else
+ template <typename Key, typename Value,
+ typename Hash = typename std::unordered_map<Key, Value>::hasher,
+ typename Equal = typename std::unordered_map<Key, Value>::key_equal,
+ typename Allocator = typename std::unordered_map<Key, Value>::allocator_type>
+ using FlatHashMap = std::unordered_map<Key, Value, Hash, Equal, Allocator>;
+#endif
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/FlatHashSet.h b/contrib/libs/antlr4_cpp_runtime/src/FlatHashSet.h
new file mode 100644
index 0000000000..5396c2bd5d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/FlatHashSet.h
@@ -0,0 +1,57 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#pragma once
+
+#include "antlr4-common.h"
+
+#if ANTLR4CPP_USING_ABSEIL
+#error #include "absl/container/flat_hash_set.h"
+#else
+#include <unordered_set>
+#endif
+
+// By default ANTLRv4 uses containers provided by the C++ standard library. In most deployments this
+// is fine, however in some using custom containers may be preferred. This header allows that by
+// optionally supporting some alternative implementations and allowing for more easier patching of
+// other alternatives.
+
+namespace antlr4 {
+
+#if ANTLR4CPP_USING_ABSEIL
+ template <typename Key,
+ typename Hash = typename absl::flat_hash_set<Key>::hasher,
+ typename Equal = typename absl::flat_hash_set<Key>::key_equal,
+ typename Allocator = typename absl::flat_hash_set<Key>::allocator_type>
+ using FlatHashSet = absl::flat_hash_set<Key, Hash, Equal, Allocator>;
+#else
+ template <typename Key,
+ typename Hash = typename std::unordered_set<Key>::hasher,
+ typename Equal = typename std::unordered_set<Key>::key_equal,
+ typename Allocator = typename std::unordered_set<Key>::allocator_type>
+ using FlatHashSet = std::unordered_set<Key, Hash, Equal, Allocator>;
+#endif
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/InputMismatchException.cpp b/contrib/libs/antlr4_cpp_runtime/src/InputMismatchException.cpp
new file mode 100644
index 0000000000..4f4947985d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/InputMismatchException.cpp
@@ -0,0 +1,18 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Parser.h"
+
+#include "InputMismatchException.h"
+
+using namespace antlr4;
+
+InputMismatchException::InputMismatchException(Parser *recognizer)
+ : RecognitionException(recognizer, recognizer->getInputStream(), recognizer->getContext(),
+ recognizer->getCurrentToken()) {
+}
+
+InputMismatchException::~InputMismatchException() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/InputMismatchException.h b/contrib/libs/antlr4_cpp_runtime/src/InputMismatchException.h
new file mode 100644
index 0000000000..8b75420968
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/InputMismatchException.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "RecognitionException.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// This signifies any kind of mismatched input exceptions such as
+ /// when the current input does not match the expected token.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC InputMismatchException : public RecognitionException {
+ public:
+ InputMismatchException(Parser *recognizer);
+ InputMismatchException(InputMismatchException const&) = default;
+ ~InputMismatchException();
+ InputMismatchException& operator=(InputMismatchException const&) = default;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/IntStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/IntStream.cpp
new file mode 100644
index 0000000000..37a90a7cd9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/IntStream.cpp
@@ -0,0 +1,12 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "IntStream.h"
+
+using namespace antlr4;
+
+const std::string IntStream::UNKNOWN_SOURCE_NAME = "<unknown>";
+
+IntStream::~IntStream() = default;
diff --git a/contrib/libs/antlr4_cpp_runtime/src/IntStream.h b/contrib/libs/antlr4_cpp_runtime/src/IntStream.h
new file mode 100644
index 0000000000..40a0f2a9e8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/IntStream.h
@@ -0,0 +1,218 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// A simple stream of symbols whose values are represented as integers. This
+ /// interface provides <em>marked ranges</em> with support for a minimum level
+ /// of buffering necessary to implement arbitrary lookahead during prediction.
+ /// For more information on marked ranges, see <seealso cref="#mark"/>.
+ /// <p/>
+ /// <strong>Initializing Methods:</strong> Some methods in this interface have
+ /// unspecified behavior if no call to an initializing method has occurred after
+ /// the stream was constructed. The following is a list of initializing methods:
+ ///
+ /// <ul>
+ /// <li><seealso cref="#LA"/></li>
+ /// <li><seealso cref="#consume"/></li>
+ /// <li><seealso cref="#size"/></li>
+ /// </ul>
+ /// </summary>
+ class ANTLR4CPP_PUBLIC IntStream {
+ public:
+ static constexpr size_t EOF = std::numeric_limits<size_t>::max();
+
+ /// The value returned by <seealso cref="#LA LA()"/> when the end of the stream is
+ /// reached.
+ /// No explicit EOF definition. We got EOF on all platforms.
+ //static const size_t _EOF = std::ios::eofbit;
+
+ /// <summary>
+ /// The value returned by <seealso cref="#getSourceName"/> when the actual name of the
+ /// underlying source is not known.
+ /// </summary>
+ static const std::string UNKNOWN_SOURCE_NAME;
+
+ virtual ~IntStream();
+
+ /// <summary>
+ /// Consumes the current symbol in the stream. This method has the following
+ /// effects:
+ ///
+ /// <ul>
+ /// <li><strong>Forward movement:</strong> The value of <seealso cref="#index index()"/>
+ /// before calling this method is less than the value of {@code index()}
+ /// after calling this method.</li>
+ /// <li><strong>Ordered lookahead:</strong> The value of {@code LA(1)} before
+ /// calling this method becomes the value of {@code LA(-1)} after calling
+ /// this method.</li>
+ /// </ul>
+ ///
+ /// Note that calling this method does not guarantee that {@code index()} is
+ /// incremented by exactly 1, as that would preclude the ability to implement
+ /// filtering streams (e.g. <seealso cref="CommonTokenStream"/> which distinguishes
+ /// between "on-channel" and "off-channel" tokens).
+ /// </summary>
+ /// <exception cref="IllegalStateException"> if an attempt is made to consume the the
+ /// end of the stream (i.e. if {@code LA(1)==}<seealso cref="#EOF EOF"/> before calling
+ /// {@code consume}). </exception>
+ virtual void consume() = 0;
+
+ /// <summary>
+ /// Gets the value of the symbol at offset {@code i} from the current
+ /// position. When {@code i==1}, this method returns the value of the current
+ /// symbol in the stream (which is the next symbol to be consumed). When
+ /// {@code i==-1}, this method returns the value of the previously read
+ /// symbol in the stream. It is not valid to call this method with
+ /// {@code i==0}, but the specific behavior is unspecified because this
+ /// method is frequently called from performance-critical code.
+ /// <p/>
+ /// This method is guaranteed to succeed if any of the following are true:
+ ///
+ /// <ul>
+ /// <li>{@code i>0}</li>
+ /// <li>{@code i==-1} and <seealso cref="#index index()"/> returns a value greater
+ /// than the value of {@code index()} after the stream was constructed
+ /// and {@code LA(1)} was called in that order. Specifying the current
+ /// {@code index()} relative to the index after the stream was created
+ /// allows for filtering implementations that do not return every symbol
+ /// from the underlying source. Specifying the call to {@code LA(1)}
+ /// allows for lazily initialized streams.</li>
+ /// <li>{@code LA(i)} refers to a symbol consumed within a marked region
+ /// that has not yet been released.</li>
+ /// </ul>
+ ///
+ /// If {@code i} represents a position at or beyond the end of the stream,
+ /// this method returns <seealso cref="#EOF"/>.
+ /// <p/>
+ /// The return value is unspecified if {@code i<0} and fewer than {@code -i}
+ /// calls to <seealso cref="#consume consume()"/> have occurred from the beginning of
+ /// the stream before calling this method.
+ /// </summary>
+ /// <exception cref="UnsupportedOperationException"> if the stream does not support
+ /// retrieving the value of the specified symbol </exception>
+ virtual size_t LA(ssize_t i) = 0;
+
+ /// <summary>
+ /// A mark provides a guarantee that <seealso cref="#seek seek()"/> operations will be
+ /// valid over a "marked range" extending from the index where {@code mark()}
+ /// was called to the current <seealso cref="#index index()"/>. This allows the use of
+ /// streaming input sources by specifying the minimum buffering requirements
+ /// to support arbitrary lookahead during prediction.
+ /// <p/>
+ /// The returned mark is an opaque handle (type {@code int}) which is passed
+ /// to <seealso cref="#release release()"/> when the guarantees provided by the marked
+ /// range are no longer necessary. When calls to
+ /// {@code mark()}/{@code release()} are nested, the marks must be released
+ /// in reverse order of which they were obtained. Since marked regions are
+ /// used during performance-critical sections of prediction, the specific
+ /// behavior of invalid usage is unspecified (i.e. a mark is not released, or
+ /// a mark is released twice, or marks are not released in reverse order from
+ /// which they were created).
+ /// <p/>
+ /// The behavior of this method is unspecified if no call to an
+ /// <seealso cref="IntStream initializing method"/> has occurred after this stream was
+ /// constructed.
+ /// <p/>
+ /// This method does not change the current position in the input stream.
+ /// <p/>
+ /// The following example shows the use of <seealso cref="#mark mark()"/>,
+ /// <seealso cref="#release release(mark)"/>, <seealso cref="#index index()"/>, and
+ /// <seealso cref="#seek seek(index)"/> as part of an operation to safely work within a
+ /// marked region, then restore the stream position to its original value and
+ /// release the mark.
+ /// <pre>
+ /// IntStream stream = ...;
+ /// int index = -1;
+ /// int mark = stream.mark();
+ /// try {
+ /// index = stream.index();
+ /// // perform work here...
+ /// } finally {
+ /// if (index != -1) {
+ /// stream.seek(index);
+ /// }
+ /// stream.release(mark);
+ /// }
+ /// </pre>
+ /// </summary>
+ /// <returns> An opaque marker which should be passed to
+ /// <seealso cref="#release release()"/> when the marked range is no longer required. </returns>
+ virtual ssize_t mark() = 0;
+
+ /// <summary>
+ /// This method releases a marked range created by a call to
+ /// <seealso cref="#mark mark()"/>. Calls to {@code release()} must appear in the
+ /// reverse order of the corresponding calls to {@code mark()}. If a mark is
+ /// released twice, or if marks are not released in reverse order of the
+ /// corresponding calls to {@code mark()}, the behavior is unspecified.
+ /// <p/>
+ /// For more information and an example, see <seealso cref="#mark"/>.
+ /// </summary>
+ /// <param name="marker"> A marker returned by a call to {@code mark()}. </param>
+ /// <seealso cref= #mark </seealso>
+ virtual void release(ssize_t marker) = 0;
+
+ /// <summary>
+ /// Return the index into the stream of the input symbol referred to by
+ /// {@code LA(1)}.
+ /// <p/>
+ /// The behavior of this method is unspecified if no call to an
+ /// <seealso cref="IntStream initializing method"/> has occurred after this stream was
+ /// constructed.
+ /// </summary>
+ virtual size_t index() = 0;
+
+ /// <summary>
+ /// Set the input cursor to the position indicated by {@code index}. If the
+ /// specified index lies past the end of the stream, the operation behaves as
+ /// though {@code index} was the index of the EOF symbol. After this method
+ /// returns without throwing an exception, the at least one of the following
+ /// will be true.
+ ///
+ /// <ul>
+ /// <li><seealso cref="#index index()"/> will return the index of the first symbol
+ /// appearing at or after the specified {@code index}. Specifically,
+ /// implementations which filter their sources should automatically
+ /// adjust {@code index} forward the minimum amount required for the
+ /// operation to target a non-ignored symbol.</li>
+ /// <li>{@code LA(1)} returns <seealso cref="#EOF"/></li>
+ /// </ul>
+ ///
+ /// This operation is guaranteed to not throw an exception if {@code index}
+ /// lies within a marked region. For more information on marked regions, see
+ /// <seealso cref="#mark"/>. The behavior of this method is unspecified if no call to
+ /// an <seealso cref="IntStream initializing method"/> has occurred after this stream
+ /// was constructed.
+ /// </summary>
+ /// <param name="index"> The absolute index to seek to.
+ /// </param>
+ /// <exception cref="IllegalArgumentException"> if {@code index} is less than 0 </exception>
+ /// <exception cref="UnsupportedOperationException"> if the stream does not support
+ /// seeking to the specified index </exception>
+ virtual void seek(size_t index) = 0;
+
+ /// <summary>
+ /// Returns the total number of symbols in the stream, including a single EOF
+ /// symbol.
+ /// </summary>
+ /// <exception cref="UnsupportedOperationException"> if the size of the stream is
+ /// unknown. </exception>
+ virtual size_t size() = 0;
+
+ /// <summary>
+ /// Gets the name of the underlying symbol source. This method returns a
+ /// non-null, non-empty string. If such a name is not known, this method
+ /// returns <seealso cref="#UNKNOWN_SOURCE_NAME"/>.
+ /// </summary>
+ virtual std::string getSourceName() const = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/InterpreterRuleContext.cpp b/contrib/libs/antlr4_cpp_runtime/src/InterpreterRuleContext.cpp
new file mode 100644
index 0000000000..f2812ba910
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/InterpreterRuleContext.cpp
@@ -0,0 +1,19 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "InterpreterRuleContext.h"
+
+using namespace antlr4;
+
+InterpreterRuleContext::InterpreterRuleContext() : ParserRuleContext() {
+}
+
+InterpreterRuleContext::InterpreterRuleContext(ParserRuleContext *parent, size_t invokingStateNumber, size_t ruleIndex)
+ : ParserRuleContext(parent, invokingStateNumber), _ruleIndex(ruleIndex) {
+}
+
+size_t InterpreterRuleContext::getRuleIndex() const {
+ return _ruleIndex;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/InterpreterRuleContext.h b/contrib/libs/antlr4_cpp_runtime/src/InterpreterRuleContext.h
new file mode 100644
index 0000000000..a34d06b1f1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/InterpreterRuleContext.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "ParserRuleContext.h"
+
+namespace antlr4 {
+
+ /**
+ * This class extends {@link ParserRuleContext} by allowing the value of
+ * {@link #getRuleIndex} to be explicitly set for the context.
+ *
+ * <p>
+ * {@link ParserRuleContext} does not include field storage for the rule index
+ * since the context classes created by the code generator override the
+ * {@link #getRuleIndex} method to return the correct value for that context.
+ * Since the parser interpreter does not use the context classes generated for a
+ * parser, this class (with slightly more memory overhead per node) is used to
+ * provide equivalent functionality.</p>
+ */
+ class ANTLR4CPP_PUBLIC InterpreterRuleContext : public ParserRuleContext {
+ public:
+ InterpreterRuleContext();
+
+ /**
+ * Constructs a new {@link InterpreterRuleContext} with the specified
+ * parent, invoking state, and rule index.
+ *
+ * @param parent The parent context.
+ * @param invokingStateNumber The invoking state number.
+ * @param ruleIndex The rule index for the current context.
+ */
+ InterpreterRuleContext(ParserRuleContext *parent, size_t invokingStateNumber, size_t ruleIndex);
+
+ virtual size_t getRuleIndex() const override;
+
+ protected:
+ /** This is the backing field for {@link #getRuleIndex}. */
+ const size_t _ruleIndex = INVALID_INDEX;
+};
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Lexer.cpp b/contrib/libs/antlr4_cpp_runtime/src/Lexer.cpp
new file mode 100644
index 0000000000..b0385c56ba
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Lexer.cpp
@@ -0,0 +1,294 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/LexerATNSimulator.h"
+#include "Exceptions.h"
+#include "misc/Interval.h"
+#include "CommonTokenFactory.h"
+#include "LexerNoViableAltException.h"
+#include "ANTLRErrorListener.h"
+#include "support/CPPUtils.h"
+#include "CommonToken.h"
+
+#include "Lexer.h"
+
+#define DEBUG_LEXER 0
+
+using namespace antlrcpp;
+using namespace antlr4;
+
+Lexer::Lexer() : Recognizer() {
+ InitializeInstanceFields();
+ _input = nullptr;
+}
+
+Lexer::Lexer(CharStream *input) : Recognizer(), _input(input) {
+ InitializeInstanceFields();
+}
+
+void Lexer::reset() {
+ // wack Lexer state variables
+ _input->seek(0); // rewind the input
+
+ _syntaxErrors = 0;
+ token.reset();
+ type = Token::INVALID_TYPE;
+ channel = Token::DEFAULT_CHANNEL;
+ tokenStartCharIndex = INVALID_INDEX;
+ tokenStartCharPositionInLine = 0;
+ tokenStartLine = 0;
+ type = 0;
+ _text = "";
+
+ hitEOF = false;
+ mode = Lexer::DEFAULT_MODE;
+ modeStack.clear();
+
+ getInterpreter<atn::LexerATNSimulator>()->reset();
+}
+
+std::unique_ptr<Token> Lexer::nextToken() {
+ // Mark start location in char stream so unbuffered streams are
+ // guaranteed at least have text of current token
+ ssize_t tokenStartMarker = _input->mark();
+
+ auto onExit = finally([this, tokenStartMarker]{
+ // make sure we release marker after match or
+ // unbuffered char stream will keep buffering
+ _input->release(tokenStartMarker);
+ });
+
+ while (true) {
+ outerContinue:
+ if (hitEOF) {
+ emitEOF();
+ return std::move(token);
+ }
+
+ token.reset();
+ channel = Token::DEFAULT_CHANNEL;
+ tokenStartCharIndex = _input->index();
+ tokenStartCharPositionInLine = getInterpreter<atn::LexerATNSimulator>()->getCharPositionInLine();
+ tokenStartLine = getInterpreter<atn::LexerATNSimulator>()->getLine();
+ _text = "";
+ do {
+ type = Token::INVALID_TYPE;
+ size_t ttype;
+ try {
+ ttype = getInterpreter<atn::LexerATNSimulator>()->match(_input, mode);
+ } catch (LexerNoViableAltException &e) {
+ notifyListeners(e); // report error
+ recover(e);
+ ttype = SKIP;
+ }
+ if (_input->LA(1) == EOF) {
+ hitEOF = true;
+ }
+ if (type == Token::INVALID_TYPE) {
+ type = ttype;
+ }
+ if (type == SKIP) {
+ goto outerContinue;
+ }
+ } while (type == MORE);
+ if (token == nullptr) {
+ emit();
+ }
+ return std::move(token);
+ }
+}
+
+void Lexer::skip() {
+ type = SKIP;
+}
+
+void Lexer::more() {
+ type = MORE;
+}
+
+void Lexer::setMode(size_t m) {
+ mode = m;
+}
+
+void Lexer::pushMode(size_t m) {
+#if DEBUG_LEXER == 1
+ std::cout << "pushMode " << m << std::endl;
+#endif
+
+ modeStack.push_back(mode);
+ setMode(m);
+}
+
+size_t Lexer::popMode() {
+ if (modeStack.empty()) {
+ throw EmptyStackException();
+ }
+#if DEBUG_LEXER == 1
+ std::cout << std::string("popMode back to ") << modeStack.back() << std::endl;
+#endif
+
+ setMode(modeStack.back());
+ modeStack.pop_back();
+ return mode;
+}
+
+
+TokenFactory<CommonToken>* Lexer::getTokenFactory() {
+ return _factory;
+}
+
+void Lexer::setInputStream(IntStream *input) {
+ reset();
+ _input = dynamic_cast<CharStream*>(input);
+}
+
+std::string Lexer::getSourceName() {
+ return _input->getSourceName();
+}
+
+CharStream* Lexer::getInputStream() {
+ return _input;
+}
+
+void Lexer::emit(std::unique_ptr<Token> newToken) {
+ token = std::move(newToken);
+}
+
+Token* Lexer::emit() {
+ emit(_factory->create({ this, _input }, type, _text, channel,
+ tokenStartCharIndex, getCharIndex() - 1, tokenStartLine, tokenStartCharPositionInLine));
+ return token.get();
+}
+
+Token* Lexer::emitEOF() {
+ size_t cpos = getCharPositionInLine();
+ size_t line = getLine();
+ emit(_factory->create({ this, _input }, EOF, "", Token::DEFAULT_CHANNEL, _input->index(), _input->index() - 1, line, cpos));
+ return token.get();
+}
+
+size_t Lexer::getLine() const {
+ return getInterpreter<atn::LexerATNSimulator>()->getLine();
+}
+
+size_t Lexer::getCharPositionInLine() {
+ return getInterpreter<atn::LexerATNSimulator>()->getCharPositionInLine();
+}
+
+void Lexer::setLine(size_t line) {
+ getInterpreter<atn::LexerATNSimulator>()->setLine(line);
+}
+
+void Lexer::setCharPositionInLine(size_t charPositionInLine) {
+ getInterpreter<atn::LexerATNSimulator>()->setCharPositionInLine(charPositionInLine);
+}
+
+size_t Lexer::getCharIndex() {
+ return _input->index();
+}
+
+std::string Lexer::getText() {
+ if (!_text.empty()) {
+ return _text;
+ }
+ return getInterpreter<atn::LexerATNSimulator>()->getText(_input);
+}
+
+void Lexer::setText(const std::string &text) {
+ _text = text;
+}
+
+std::unique_ptr<Token> Lexer::getToken() {
+ return std::move(token);
+}
+
+void Lexer::setToken(std::unique_ptr<Token> newToken) {
+ token = std::move(newToken);
+}
+
+void Lexer::setType(size_t ttype) {
+ type = ttype;
+}
+
+size_t Lexer::getType() {
+ return type;
+}
+
+void Lexer::setChannel(size_t newChannel) {
+ channel = newChannel;
+}
+
+size_t Lexer::getChannel() {
+ return channel;
+}
+
+std::vector<std::unique_ptr<Token>> Lexer::getAllTokens() {
+ std::vector<std::unique_ptr<Token>> tokens;
+ std::unique_ptr<Token> t = nextToken();
+ while (t->getType() != EOF) {
+ tokens.push_back(std::move(t));
+ t = nextToken();
+ }
+ return tokens;
+}
+
+void Lexer::recover(const LexerNoViableAltException &/*e*/) {
+ if (_input->LA(1) != EOF) {
+ // skip a char and try again
+ getInterpreter<atn::LexerATNSimulator>()->consume(_input);
+ }
+}
+
+void Lexer::notifyListeners(const LexerNoViableAltException & /*e*/) {
+ ++_syntaxErrors;
+ std::string text = _input->getText(misc::Interval(tokenStartCharIndex, _input->index()));
+ std::string msg = std::string("token recognition error at: '") + getErrorDisplay(text) + std::string("'");
+
+ ProxyErrorListener &listener = getErrorListenerDispatch();
+ listener.syntaxError(this, nullptr, tokenStartLine, tokenStartCharPositionInLine, msg, std::current_exception());
+}
+
+std::string Lexer::getErrorDisplay(const std::string &s) {
+ std::stringstream ss;
+ for (auto c : s) {
+ switch (c) {
+ case '\n':
+ ss << "\\n";
+ break;
+ case '\t':
+ ss << "\\t";
+ break;
+ case '\r':
+ ss << "\\r";
+ break;
+ default:
+ ss << c;
+ break;
+ }
+ }
+ return ss.str();
+}
+
+void Lexer::recover(RecognitionException * /*re*/) {
+ // TODO: Do we lose character or line position information?
+ _input->consume();
+}
+
+size_t Lexer::getNumberOfSyntaxErrors() {
+ return _syntaxErrors;
+}
+
+void Lexer::InitializeInstanceFields() {
+ _syntaxErrors = 0;
+ token = nullptr;
+ _factory = CommonTokenFactory::DEFAULT.get();
+ tokenStartCharIndex = INVALID_INDEX;
+ tokenStartLine = 0;
+ tokenStartCharPositionInLine = 0;
+ hitEOF = false;
+ channel = 0;
+ type = 0;
+ mode = Lexer::DEFAULT_MODE;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Lexer.h b/contrib/libs/antlr4_cpp_runtime/src/Lexer.h
new file mode 100644
index 0000000000..77033ad9e6
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Lexer.h
@@ -0,0 +1,196 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Recognizer.h"
+#include "TokenSource.h"
+#include "CharStream.h"
+#include "Token.h"
+
+namespace antlr4 {
+
+ /// A lexer is recognizer that draws input symbols from a character stream.
+ /// lexer grammars result in a subclass of this object. A Lexer object
+ /// uses simplified match() and error recovery mechanisms in the interest
+ /// of speed.
+ class ANTLR4CPP_PUBLIC Lexer : public Recognizer, public TokenSource {
+ public:
+ static constexpr size_t DEFAULT_MODE = 0;
+ static constexpr size_t MORE = std::numeric_limits<size_t>::max() - 1;
+ static constexpr size_t SKIP = std::numeric_limits<size_t>::max() - 2;
+
+ static constexpr size_t DEFAULT_TOKEN_CHANNEL = Token::DEFAULT_CHANNEL;
+ static constexpr size_t HIDDEN = Token::HIDDEN_CHANNEL;
+ static constexpr size_t MIN_CHAR_VALUE = 0;
+ static constexpr size_t MAX_CHAR_VALUE = 0x10FFFF;
+
+ CharStream *_input; // Pure reference, usually from statically allocated instance.
+
+ protected:
+ /// How to create token objects.
+ TokenFactory<CommonToken> *_factory;
+
+ public:
+ /// The goal of all lexer rules/methods is to create a token object.
+ /// This is an instance variable as multiple rules may collaborate to
+ /// create a single token. nextToken will return this object after
+ /// matching lexer rule(s). If you subclass to allow multiple token
+ /// emissions, then set this to the last token to be matched or
+ /// something nonnull so that the auto token emit mechanism will not
+ /// emit another token.
+
+ // Life cycle of a token is this:
+ // Created by emit() (via the token factory) or by action code, holding ownership of it.
+ // Ownership is handed over to the token stream when calling nextToken().
+ std::unique_ptr<Token> token;
+
+ /// <summary>
+ /// What character index in the stream did the current token start at?
+ /// Needed, for example, to get the text for current token. Set at
+ /// the start of nextToken.
+ /// </summary>
+ size_t tokenStartCharIndex;
+
+ /// <summary>
+ /// The line on which the first character of the token resides </summary>
+ size_t tokenStartLine;
+
+ /// The character position of first character within the line.
+ size_t tokenStartCharPositionInLine;
+
+ /// Once we see EOF on char stream, next token will be EOF.
+ /// If you have DONE : EOF ; then you see DONE EOF.
+ bool hitEOF;
+
+ /// The channel number for the current token.
+ size_t channel;
+
+ /// The token type for the current token.
+ size_t type;
+
+ // Use the vector as a stack.
+ std::vector<size_t> modeStack;
+ size_t mode;
+
+ Lexer();
+ Lexer(CharStream *input);
+ virtual ~Lexer() {}
+
+ virtual void reset();
+
+ /// Return a token from this source; i.e., match a token on the char stream.
+ virtual std::unique_ptr<Token> nextToken() override;
+
+ /// Instruct the lexer to skip creating a token for current lexer rule
+ /// and look for another token. nextToken() knows to keep looking when
+ /// a lexer rule finishes with token set to SKIP_TOKEN. Recall that
+ /// if token == null at end of any token rule, it creates one for you
+ /// and emits it.
+ virtual void skip();
+ virtual void more();
+ virtual void setMode(size_t m);
+ virtual void pushMode(size_t m);
+ virtual size_t popMode();
+
+ template<typename T1>
+ void setTokenFactory(TokenFactory<T1> *factory) {
+ this->_factory = factory;
+ }
+
+ virtual TokenFactory<CommonToken>* getTokenFactory() override;
+
+ /// Set the char stream and reset the lexer
+ virtual void setInputStream(IntStream *input) override;
+
+ virtual std::string getSourceName() override;
+
+ virtual CharStream* getInputStream() override;
+
+ /// By default does not support multiple emits per nextToken invocation
+ /// for efficiency reasons. Subclasses can override this method, nextToken,
+ /// and getToken (to push tokens into a list and pull from that list
+ /// rather than a single variable as this implementation does).
+ virtual void emit(std::unique_ptr<Token> newToken);
+
+ /// The standard method called to automatically emit a token at the
+ /// outermost lexical rule. The token object should point into the
+ /// char buffer start..stop. If there is a text override in 'text',
+ /// use that to set the token's text. Override this method to emit
+ /// custom Token objects or provide a new factory.
+ virtual Token* emit();
+
+ virtual Token* emitEOF();
+
+ virtual size_t getLine() const override;
+
+ virtual size_t getCharPositionInLine() override;
+
+ virtual void setLine(size_t line);
+
+ virtual void setCharPositionInLine(size_t charPositionInLine);
+
+ /// What is the index of the current character of lookahead?
+ virtual size_t getCharIndex();
+
+ /// Return the text matched so far for the current token or any
+ /// text override.
+ virtual std::string getText();
+
+ /// Set the complete text of this token; it wipes any previous
+ /// changes to the text.
+ virtual void setText(const std::string &text);
+
+ /// Override if emitting multiple tokens.
+ virtual std::unique_ptr<Token> getToken();
+
+ virtual void setToken(std::unique_ptr<Token> newToken);
+
+ virtual void setType(size_t ttype);
+
+ virtual size_t getType();
+
+ virtual void setChannel(size_t newChannel);
+
+ virtual size_t getChannel();
+
+ virtual const std::vector<std::string>& getChannelNames() const = 0;
+
+ virtual const std::vector<std::string>& getModeNames() const = 0;
+
+ /// Return a list of all Token objects in input char stream.
+ /// Forces load of all tokens. Does not include EOF token.
+ virtual std::vector<std::unique_ptr<Token>> getAllTokens();
+
+ virtual void recover(const LexerNoViableAltException &e);
+
+ virtual void notifyListeners(const LexerNoViableAltException &e);
+
+ virtual std::string getErrorDisplay(const std::string &s);
+
+ /// Lexers can normally match any char in it's vocabulary after matching
+ /// a token, so do the easy thing and just kill a character and hope
+ /// it all works out. You can instead use the rule invocation stack
+ /// to do sophisticated error recovery if you are in a fragment rule.
+ virtual void recover(RecognitionException *re);
+
+ /// <summary>
+ /// Gets the number of syntax errors reported during parsing. This value is
+ /// incremented each time <seealso cref="#notifyErrorListeners"/> is called.
+ /// </summary>
+ /// <seealso cref= #notifyListeners </seealso>
+ virtual size_t getNumberOfSyntaxErrors();
+
+ protected:
+ /// You can set the text for the current token to override what is in
+ /// the input char buffer (via setText()).
+ std::string _text;
+
+ private:
+ size_t _syntaxErrors;
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/LexerInterpreter.cpp b/contrib/libs/antlr4_cpp_runtime/src/LexerInterpreter.cpp
new file mode 100644
index 0000000000..38acd09ddd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/LexerInterpreter.cpp
@@ -0,0 +1,60 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATNType.h"
+#include "atn/LexerATNSimulator.h"
+#include "dfa/DFA.h"
+#include "Exceptions.h"
+#include "Vocabulary.h"
+
+#include "LexerInterpreter.h"
+
+using namespace antlr4;
+
+LexerInterpreter::LexerInterpreter(const std::string &grammarFileName, const dfa::Vocabulary &vocabulary,
+ const std::vector<std::string> &ruleNames, const std::vector<std::string> &channelNames, const std::vector<std::string> &modeNames,
+ const atn::ATN &atn, CharStream *input)
+ : Lexer(input), _grammarFileName(grammarFileName), _atn(atn), _ruleNames(ruleNames),
+ _channelNames(channelNames), _modeNames(modeNames),
+ _vocabulary(vocabulary) {
+
+ if (_atn.grammarType != atn::ATNType::LEXER) {
+ throw IllegalArgumentException("The ATN must be a lexer ATN.");
+ }
+
+ for (size_t i = 0; i < atn.getNumberOfDecisions(); ++i) {
+ _decisionToDFA.push_back(dfa::DFA(_atn.getDecisionState(i), i));
+ }
+ _interpreter = new atn::LexerATNSimulator(this, _atn, _decisionToDFA, _sharedContextCache); /* mem-check: deleted in d-tor */
+}
+
+LexerInterpreter::~LexerInterpreter()
+{
+ delete _interpreter;
+}
+
+const atn::ATN& LexerInterpreter::getATN() const {
+ return _atn;
+}
+
+std::string LexerInterpreter::getGrammarFileName() const {
+ return _grammarFileName;
+}
+
+const std::vector<std::string>& LexerInterpreter::getRuleNames() const {
+ return _ruleNames;
+}
+
+const std::vector<std::string>& LexerInterpreter::getChannelNames() const {
+ return _channelNames;
+}
+
+const std::vector<std::string>& LexerInterpreter::getModeNames() const {
+ return _modeNames;
+}
+
+const dfa::Vocabulary& LexerInterpreter::getVocabulary() const {
+ return _vocabulary;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/LexerInterpreter.h b/contrib/libs/antlr4_cpp_runtime/src/LexerInterpreter.h
new file mode 100644
index 0000000000..3787c1d0d5
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/LexerInterpreter.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Lexer.h"
+#include "atn/PredictionContext.h"
+#include "atn/PredictionContextCache.h"
+#include "Vocabulary.h"
+
+namespace antlr4 {
+
+ class ANTLR4CPP_PUBLIC LexerInterpreter : public Lexer {
+ public:
+ LexerInterpreter(const std::string &grammarFileName, const dfa::Vocabulary &vocabulary,
+ const std::vector<std::string> &ruleNames, const std::vector<std::string> &channelNames,
+ const std::vector<std::string> &modeNames, const atn::ATN &atn, CharStream *input);
+
+ ~LexerInterpreter();
+
+ virtual const atn::ATN& getATN() const override;
+ virtual std::string getGrammarFileName() const override;
+ virtual const std::vector<std::string>& getRuleNames() const override;
+ virtual const std::vector<std::string>& getChannelNames() const override;
+ virtual const std::vector<std::string>& getModeNames() const override;
+
+ virtual const dfa::Vocabulary& getVocabulary() const override;
+
+ protected:
+ const std::string _grammarFileName;
+ const atn::ATN &_atn;
+
+ const std::vector<std::string> &_ruleNames;
+ const std::vector<std::string> &_channelNames;
+ const std::vector<std::string> &_modeNames;
+ std::vector<dfa::DFA> _decisionToDFA;
+
+ atn::PredictionContextCache _sharedContextCache;
+
+ private:
+ dfa::Vocabulary _vocabulary;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/LexerNoViableAltException.cpp b/contrib/libs/antlr4_cpp_runtime/src/LexerNoViableAltException.cpp
new file mode 100644
index 0000000000..3304b82b40
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/LexerNoViableAltException.cpp
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/Interval.h"
+#include "support/CPPUtils.h"
+#include "CharStream.h"
+#include "Lexer.h"
+
+#include "LexerNoViableAltException.h"
+
+using namespace antlr4;
+
+LexerNoViableAltException::LexerNoViableAltException(Lexer *lexer, CharStream *input, size_t startIndex,
+ atn::ATNConfigSet *deadEndConfigs)
+ : RecognitionException(lexer, input, nullptr, nullptr), _startIndex(startIndex), _deadEndConfigs(deadEndConfigs) {
+}
+
+size_t LexerNoViableAltException::getStartIndex() {
+ return _startIndex;
+}
+
+atn::ATNConfigSet* LexerNoViableAltException::getDeadEndConfigs() {
+ return _deadEndConfigs;
+}
+
+std::string LexerNoViableAltException::toString() {
+ std::string symbol;
+ if (_startIndex < getInputStream()->size()) {
+ symbol = static_cast<CharStream *>(getInputStream())->getText(misc::Interval(_startIndex, _startIndex));
+ symbol = antlrcpp::escapeWhitespace(symbol, false);
+ }
+ std::string format = "LexerNoViableAltException('" + symbol + "')";
+ return format;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/LexerNoViableAltException.h b/contrib/libs/antlr4_cpp_runtime/src/LexerNoViableAltException.h
new file mode 100644
index 0000000000..52eada7cfa
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/LexerNoViableAltException.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "RecognitionException.h"
+#include "atn/ATNConfigSet.h"
+
+namespace antlr4 {
+
+ class ANTLR4CPP_PUBLIC LexerNoViableAltException : public RecognitionException {
+ public:
+ LexerNoViableAltException(Lexer *lexer, CharStream *input, size_t startIndex,
+ atn::ATNConfigSet *deadEndConfigs);
+
+ virtual size_t getStartIndex();
+ virtual atn::ATNConfigSet* getDeadEndConfigs();
+ virtual std::string toString();
+
+ private:
+ /// Matching attempted at what input index?
+ const size_t _startIndex;
+
+ /// Which configurations did we try at input.index() that couldn't match input.LA(1)?
+ atn::ATNConfigSet *_deadEndConfigs;
+
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ListTokenSource.cpp b/contrib/libs/antlr4_cpp_runtime/src/ListTokenSource.cpp
new file mode 100644
index 0000000000..45372808e5
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ListTokenSource.cpp
@@ -0,0 +1,92 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Token.h"
+#include "CommonToken.h"
+#include "CharStream.h"
+
+#include "ListTokenSource.h"
+
+using namespace antlr4;
+
+ListTokenSource::ListTokenSource(std::vector<std::unique_ptr<Token>> tokens_) : ListTokenSource(std::move(tokens_), "") {
+}
+
+ListTokenSource::ListTokenSource(std::vector<std::unique_ptr<Token>> tokens_, const std::string &sourceName_)
+ : tokens(std::move(tokens_)), sourceName(sourceName_) {
+ InitializeInstanceFields();
+ if (tokens.empty()) {
+ throw "tokens cannot be null";
+ }
+
+ // Check if there is an eof token and create one if not.
+ if (tokens.back()->getType() != Token::EOF) {
+ Token *lastToken = tokens.back().get();
+ size_t start = INVALID_INDEX;
+ size_t previousStop = lastToken->getStopIndex();
+ if (previousStop != INVALID_INDEX) {
+ start = previousStop + 1;
+ }
+
+ size_t stop = std::max(INVALID_INDEX, start - 1);
+ tokens.emplace_back((_factory->create({ this, getInputStream() }, Token::EOF, "EOF",
+ Token::DEFAULT_CHANNEL, start, stop, static_cast<int>(lastToken->getLine()), lastToken->getCharPositionInLine())));
+ }
+}
+
+size_t ListTokenSource::getCharPositionInLine() {
+ if (i < tokens.size()) {
+ return tokens[i]->getCharPositionInLine();
+ }
+ return 0;
+}
+
+std::unique_ptr<Token> ListTokenSource::nextToken() {
+ if (i < tokens.size()) {
+ return std::move(tokens[i++]);
+ }
+ return nullptr;
+}
+
+size_t ListTokenSource::getLine() const {
+ if (i < tokens.size()) {
+ return tokens[i]->getLine();
+ }
+
+ return 1;
+}
+
+CharStream *ListTokenSource::getInputStream() {
+ if (i < tokens.size()) {
+ return tokens[i]->getInputStream();
+ } else if (!tokens.empty()) {
+ return tokens.back()->getInputStream();
+ }
+
+ // no input stream information is available
+ return nullptr;
+}
+
+std::string ListTokenSource::getSourceName() {
+ if (sourceName != "") {
+ return sourceName;
+ }
+
+ CharStream *inputStream = getInputStream();
+ if (inputStream != nullptr) {
+ return inputStream->getSourceName();
+ }
+
+ return "List";
+}
+
+TokenFactory<CommonToken>* ListTokenSource::getTokenFactory() {
+ return _factory;
+}
+
+void ListTokenSource::InitializeInstanceFields() {
+ i = 0;
+ _factory = CommonTokenFactory::DEFAULT.get();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ListTokenSource.h b/contrib/libs/antlr4_cpp_runtime/src/ListTokenSource.h
new file mode 100644
index 0000000000..542b05cb5a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ListTokenSource.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "TokenSource.h"
+#include "CommonTokenFactory.h"
+
+namespace antlr4 {
+
+ /// Provides an implementation of <seealso cref="TokenSource"/> as a wrapper around a list
+ /// of <seealso cref="Token"/> objects.
+ ///
+ /// If the final token in the list is an <seealso cref="Token#EOF"/> token, it will be used
+ /// as the EOF token for every call to <seealso cref="#nextToken"/> after the end of the
+ /// list is reached. Otherwise, an EOF token will be created.
+ class ANTLR4CPP_PUBLIC ListTokenSource : public TokenSource {
+ protected:
+ // This list will be emptied token by token as we call nextToken().
+ // Token streams can be used to buffer tokens for a while.
+ std::vector<std::unique_ptr<Token>> tokens;
+
+ private:
+ /// <summary>
+ /// The name of the input source. If this value is {@code null}, a call to
+ /// <seealso cref="#getSourceName"/> should return the source name used to create the
+ /// the next token in <seealso cref="#tokens"/> (or the previous token if the end of
+ /// the input has been reached).
+ /// </summary>
+ const std::string sourceName;
+
+ protected:
+ /// The index into <seealso cref="#tokens"/> of token to return by the next call to
+ /// <seealso cref="#nextToken"/>. The end of the input is indicated by this value
+ /// being greater than or equal to the number of items in <seealso cref="#tokens"/>.
+ size_t i;
+
+ private:
+ /// This is the backing field for <seealso cref="#getTokenFactory"/> and
+ /// <seealso cref="setTokenFactory"/>.
+ TokenFactory<CommonToken> *_factory = CommonTokenFactory::DEFAULT.get();
+
+ public:
+ /// Constructs a new <seealso cref="ListTokenSource"/> instance from the specified
+ /// collection of <seealso cref="Token"/> objects.
+ ///
+ /// <param name="tokens"> The collection of <seealso cref="Token"/> objects to provide as a
+ /// <seealso cref="TokenSource"/>. </param>
+ /// <exception cref="NullPointerException"> if {@code tokens} is {@code null} </exception>
+ ListTokenSource(std::vector<std::unique_ptr<Token>> tokens);
+ ListTokenSource(const ListTokenSource& other) = delete;
+
+ ListTokenSource& operator = (const ListTokenSource& other) = delete;
+
+ /// <summary>
+ /// Constructs a new <seealso cref="ListTokenSource"/> instance from the specified
+ /// collection of <seealso cref="Token"/> objects and source name.
+ /// </summary>
+ /// <param name="tokens"> The collection of <seealso cref="Token"/> objects to provide as a
+ /// <seealso cref="TokenSource"/>. </param>
+ /// <param name="sourceName"> The name of the <seealso cref="TokenSource"/>. If this value is
+ /// {@code null}, <seealso cref="#getSourceName"/> will attempt to infer the name from
+ /// the next <seealso cref="Token"/> (or the previous token if the end of the input has
+ /// been reached).
+ /// </param>
+ /// <exception cref="NullPointerException"> if {@code tokens} is {@code null} </exception>
+ ListTokenSource(std::vector<std::unique_ptr<Token>> tokens_, const std::string &sourceName_);
+
+ virtual size_t getCharPositionInLine() override;
+ virtual std::unique_ptr<Token> nextToken() override;
+ virtual size_t getLine() const override;
+ virtual CharStream* getInputStream() override;
+ virtual std::string getSourceName() override;
+
+ template<typename T1>
+ void setTokenFactory(TokenFactory<T1> *factory) {
+ this->_factory = factory;
+ }
+
+ virtual TokenFactory<CommonToken>* getTokenFactory() override;
+
+ private:
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/NoViableAltException.cpp b/contrib/libs/antlr4_cpp_runtime/src/NoViableAltException.cpp
new file mode 100644
index 0000000000..273c208c74
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/NoViableAltException.cpp
@@ -0,0 +1,46 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Parser.h"
+
+#include "NoViableAltException.h"
+
+using namespace antlr4;
+
+namespace {
+
+// Create a normal shared pointer if the configurations are to be deleted. If not, then
+// the shared pointer is created with a deleter that does nothing.
+Ref<atn::ATNConfigSet> buildConfigsRef(atn::ATNConfigSet *configs, bool deleteConfigs) {
+ if (deleteConfigs) {
+ return Ref<atn::ATNConfigSet>(configs);
+ } else {
+ return Ref<atn::ATNConfigSet>(configs, [](atn::ATNConfigSet *){});
+ }
+}
+
+}
+
+NoViableAltException::NoViableAltException(Parser *recognizer)
+ : NoViableAltException(recognizer, recognizer->getTokenStream(), recognizer->getCurrentToken(),
+ recognizer->getCurrentToken(), nullptr, recognizer->getContext(), false) {
+}
+
+NoViableAltException::NoViableAltException(Parser *recognizer, TokenStream *input,Token *startToken,
+ Token *offendingToken, atn::ATNConfigSet *deadEndConfigs, ParserRuleContext *ctx, bool deleteConfigs)
+ : RecognitionException("No viable alternative", recognizer, input, ctx, offendingToken),
+ _deadEndConfigs(buildConfigsRef(deadEndConfigs, deleteConfigs)), _startToken(startToken) {
+}
+
+NoViableAltException::~NoViableAltException() {
+}
+
+Token* NoViableAltException::getStartToken() const {
+ return _startToken;
+}
+
+atn::ATNConfigSet* NoViableAltException::getDeadEndConfigs() const {
+ return _deadEndConfigs.get();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/NoViableAltException.h b/contrib/libs/antlr4_cpp_runtime/src/NoViableAltException.h
new file mode 100644
index 0000000000..b15039d0cb
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/NoViableAltException.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "RecognitionException.h"
+#include "Token.h"
+#include "atn/ATNConfigSet.h"
+
+namespace antlr4 {
+
+ /// Indicates that the parser could not decide which of two or more paths
+ /// to take based upon the remaining input. It tracks the starting token
+ /// of the offending input and also knows where the parser was
+ /// in the various paths when the error. Reported by reportNoViableAlternative()
+ class ANTLR4CPP_PUBLIC NoViableAltException : public RecognitionException {
+ public:
+ NoViableAltException(Parser *recognizer); // LL(1) error
+ NoViableAltException(Parser *recognizer, TokenStream *input,Token *startToken,
+ Token *offendingToken, atn::ATNConfigSet *deadEndConfigs, ParserRuleContext *ctx, bool deleteConfigs);
+ ~NoViableAltException();
+
+ virtual Token* getStartToken() const;
+ virtual atn::ATNConfigSet* getDeadEndConfigs() const;
+
+ private:
+ /// Which configurations did we try at input.index() that couldn't match input.LT(1)?
+ /// Shared pointer that conditionally deletes the configurations (based on flag
+ /// passed during construction)
+ Ref<atn::ATNConfigSet> _deadEndConfigs;
+
+ /// The token object at the start index; the input stream might
+ /// not be buffering tokens so get a reference to it. (At the
+ /// time the error occurred, of course the stream needs to keep a
+ /// buffer all of the tokens but later we might not have access to those.)
+ Token *_startToken;
+
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Parser.cpp b/contrib/libs/antlr4_cpp_runtime/src/Parser.cpp
new file mode 100644
index 0000000000..337bcba17a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Parser.cpp
@@ -0,0 +1,670 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATNDeserializationOptions.h"
+#include "tree/pattern/ParseTreePatternMatcher.h"
+#include "dfa/DFA.h"
+#include "ParserRuleContext.h"
+#include "tree/TerminalNode.h"
+#include "tree/ErrorNodeImpl.h"
+#include "Lexer.h"
+#include "atn/ParserATNSimulator.h"
+#include "misc/IntervalSet.h"
+#include "atn/RuleStartState.h"
+#include "DefaultErrorStrategy.h"
+#include "atn/ATNDeserializer.h"
+#include "atn/RuleTransition.h"
+#include "atn/ATN.h"
+#include "Exceptions.h"
+#include "ANTLRErrorListener.h"
+#include "tree/pattern/ParseTreePattern.h"
+#include "internal/Synchronization.h"
+
+#include "atn/ProfilingATNSimulator.h"
+#include "atn/ParseInfo.h"
+
+#include "Parser.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::internal;
+using namespace antlrcpp;
+
+namespace {
+
+struct BypassAltsAtnCache final {
+ std::shared_mutex mutex;
+ /// This field maps from the serialized ATN string to the deserialized <seealso cref="ATN"/> with
+ /// bypass alternatives.
+ ///
+ /// <seealso cref= ATNDeserializationOptions#isGenerateRuleBypassTransitions() </seealso>
+ std::map<std::vector<int32_t>, std::unique_ptr<const atn::ATN>, std::less<>> map;
+};
+
+BypassAltsAtnCache* getBypassAltsAtnCache() {
+ static BypassAltsAtnCache* const instance = new BypassAltsAtnCache();
+ return instance;
+}
+
+}
+
+Parser::TraceListener::TraceListener(Parser *outerInstance_) : outerInstance(outerInstance_) {
+}
+
+Parser::TraceListener::~TraceListener() {
+}
+
+void Parser::TraceListener::enterEveryRule(ParserRuleContext *ctx) {
+ std::cout << "enter " << outerInstance->getRuleNames()[ctx->getRuleIndex()]
+ << ", LT(1)=" << outerInstance->_input->LT(1)->getText() << std::endl;
+}
+
+void Parser::TraceListener::visitTerminal(tree::TerminalNode *node) {
+ std::cout << "consume " << node->getSymbol() << " rule "
+ << outerInstance->getRuleNames()[outerInstance->getContext()->getRuleIndex()] << std::endl;
+}
+
+void Parser::TraceListener::visitErrorNode(tree::ErrorNode * /*node*/) {
+}
+
+void Parser::TraceListener::exitEveryRule(ParserRuleContext *ctx) {
+ std::cout << "exit " << outerInstance->getRuleNames()[ctx->getRuleIndex()]
+ << ", LT(1)=" << outerInstance->_input->LT(1)->getText() << std::endl;
+}
+
+Parser::TrimToSizeListener Parser::TrimToSizeListener::INSTANCE;
+
+Parser::TrimToSizeListener::~TrimToSizeListener() {
+}
+
+void Parser::TrimToSizeListener::enterEveryRule(ParserRuleContext * /*ctx*/) {
+}
+
+void Parser::TrimToSizeListener::visitTerminal(tree::TerminalNode * /*node*/) {
+}
+
+void Parser::TrimToSizeListener::visitErrorNode(tree::ErrorNode * /*node*/) {
+}
+
+void Parser::TrimToSizeListener::exitEveryRule(ParserRuleContext * ctx) {
+ ctx->children.shrink_to_fit();
+}
+
+Parser::Parser(TokenStream *input) {
+ InitializeInstanceFields();
+ setInputStream(input);
+}
+
+Parser::~Parser() {
+ _tracker.reset();
+ delete _tracer;
+}
+
+void Parser::reset() {
+ if (getInputStream() != nullptr) {
+ getInputStream()->seek(0);
+ }
+ _errHandler->reset(this); // Watch out, this is not shared_ptr.reset().
+
+ _matchedEOF = false;
+ _syntaxErrors = 0;
+ setTrace(false);
+ _precedenceStack.clear();
+ _precedenceStack.push_back(0);
+ _ctx = nullptr;
+ _tracker.reset();
+
+ atn::ATNSimulator *interpreter = getInterpreter<atn::ParserATNSimulator>();
+ if (interpreter != nullptr) {
+ interpreter->reset();
+ }
+}
+
+Token* Parser::match(size_t ttype) {
+ Token *t = getCurrentToken();
+ if (t->getType() == ttype) {
+ if (ttype == EOF) {
+ _matchedEOF = true;
+ }
+ _errHandler->reportMatch(this);
+ consume();
+ } else {
+ t = _errHandler->recoverInline(this);
+ if (_buildParseTrees && t->getTokenIndex() == INVALID_INDEX) {
+ // we must have conjured up a new token during single token insertion
+ // if it's not the current symbol
+ _ctx->addChild(createErrorNode(t));
+ }
+ }
+ return t;
+}
+
+Token* Parser::matchWildcard() {
+ Token *t = getCurrentToken();
+ if (t->getType() > 0) {
+ _errHandler->reportMatch(this);
+ consume();
+ } else {
+ t = _errHandler->recoverInline(this);
+ if (_buildParseTrees && t->getTokenIndex() == INVALID_INDEX) {
+ // we must have conjured up a new token during single token insertion
+ // if it's not the current symbol
+ _ctx->addChild(createErrorNode(t));
+ }
+ }
+
+ return t;
+}
+
+void Parser::setBuildParseTree(bool buildParseTrees) {
+ this->_buildParseTrees = buildParseTrees;
+}
+
+bool Parser::getBuildParseTree() {
+ return _buildParseTrees;
+}
+
+void Parser::setTrimParseTree(bool trimParseTrees) {
+ if (trimParseTrees) {
+ if (getTrimParseTree()) {
+ return;
+ }
+ addParseListener(&TrimToSizeListener::INSTANCE);
+ } else {
+ removeParseListener(&TrimToSizeListener::INSTANCE);
+ }
+}
+
+bool Parser::getTrimParseTree() {
+ return std::find(getParseListeners().begin(), getParseListeners().end(), &TrimToSizeListener::INSTANCE) != getParseListeners().end();
+}
+
+std::vector<tree::ParseTreeListener *> Parser::getParseListeners() {
+ return _parseListeners;
+}
+
+void Parser::addParseListener(tree::ParseTreeListener *listener) {
+ if (!listener) {
+ throw NullPointerException("listener");
+ }
+
+ this->_parseListeners.push_back(listener);
+}
+
+void Parser::removeParseListener(tree::ParseTreeListener *listener) {
+ if (!_parseListeners.empty()) {
+ auto it = std::find(_parseListeners.begin(), _parseListeners.end(), listener);
+ if (it != _parseListeners.end()) {
+ _parseListeners.erase(it);
+ }
+ }
+}
+
+void Parser::removeParseListeners() {
+ _parseListeners.clear();
+}
+
+void Parser::triggerEnterRuleEvent() {
+ for (auto *listener : _parseListeners) {
+ listener->enterEveryRule(_ctx);
+ _ctx->enterRule(listener);
+ }
+}
+
+void Parser::triggerExitRuleEvent() {
+ // reverse order walk of listeners
+ for (auto it = _parseListeners.rbegin(); it != _parseListeners.rend(); ++it) {
+ _ctx->exitRule(*it);
+ (*it)->exitEveryRule(_ctx);
+ }
+}
+
+size_t Parser::getNumberOfSyntaxErrors() {
+ return _syntaxErrors;
+}
+
+TokenFactory<CommonToken>* Parser::getTokenFactory() {
+ return _input->getTokenSource()->getTokenFactory();
+}
+
+const atn::ATN& Parser::getATNWithBypassAlts() {
+ auto serializedAtn = getSerializedATN();
+ if (serializedAtn.empty()) {
+ throw UnsupportedOperationException("The current parser does not support an ATN with bypass alternatives.");
+ }
+ // XXX: using the entire serialized ATN as key into the map is a big resource waste.
+ // How large can that thing become?
+ auto *cache = getBypassAltsAtnCache();
+ {
+ std::shared_lock<std::shared_mutex> lock(cache->mutex);
+ auto existing = cache->map.find(serializedAtn);
+ if (existing != cache->map.end()) {
+ return *existing->second;
+ }
+ }
+
+ std::unique_lock<std::shared_mutex> lock(cache->mutex);
+ auto existing = cache->map.find(serializedAtn);
+ if (existing != cache->map.end()) {
+ return *existing->second;
+ }
+ atn::ATNDeserializationOptions deserializationOptions;
+ deserializationOptions.setGenerateRuleBypassTransitions(true);
+ atn::ATNDeserializer deserializer(deserializationOptions);
+ auto atn = deserializer.deserialize(serializedAtn);
+ return *cache->map.insert(std::make_pair(std::vector<int32_t>(serializedAtn.begin(), serializedAtn.end()), std::move(atn))).first->second;
+}
+
+tree::pattern::ParseTreePattern Parser::compileParseTreePattern(const std::string &pattern, int patternRuleIndex) {
+ if (getTokenStream() != nullptr) {
+ TokenSource *tokenSource = getTokenStream()->getTokenSource();
+ if (is<Lexer*>(tokenSource)) {
+ Lexer *lexer = dynamic_cast<Lexer *>(tokenSource);
+ return compileParseTreePattern(pattern, patternRuleIndex, lexer);
+ }
+ }
+ throw UnsupportedOperationException("Parser can't discover a lexer to use");
+}
+
+tree::pattern::ParseTreePattern Parser::compileParseTreePattern(const std::string &pattern, int patternRuleIndex,
+ Lexer *lexer) {
+ tree::pattern::ParseTreePatternMatcher m(lexer, this);
+ return m.compile(pattern, patternRuleIndex);
+}
+
+Ref<ANTLRErrorStrategy> Parser::getErrorHandler() {
+ return _errHandler;
+}
+
+void Parser::setErrorHandler(Ref<ANTLRErrorStrategy> const& handler) {
+ _errHandler = handler;
+}
+
+IntStream* Parser::getInputStream() {
+ return getTokenStream();
+}
+
+void Parser::setInputStream(IntStream *input) {
+ setTokenStream(static_cast<TokenStream*>(input));
+}
+
+TokenStream* Parser::getTokenStream() {
+ return _input;
+}
+
+void Parser::setTokenStream(TokenStream *input) {
+ _input = nullptr; // Just a reference we don't own.
+ reset();
+ _input = input;
+}
+
+Token* Parser::getCurrentToken() {
+ return _input->LT(1);
+}
+
+void Parser::notifyErrorListeners(const std::string &msg) {
+ notifyErrorListeners(getCurrentToken(), msg, nullptr);
+}
+
+void Parser::notifyErrorListeners(Token *offendingToken, const std::string &msg, std::exception_ptr e) {
+ _syntaxErrors++;
+ size_t line = offendingToken->getLine();
+ size_t charPositionInLine = offendingToken->getCharPositionInLine();
+
+ ProxyErrorListener &listener = getErrorListenerDispatch();
+ listener.syntaxError(this, offendingToken, line, charPositionInLine, msg, e);
+}
+
+Token* Parser::consume() {
+ Token *o = getCurrentToken();
+ if (o->getType() != EOF) {
+ getInputStream()->consume();
+ }
+
+ bool hasListener = _parseListeners.size() > 0 && !_parseListeners.empty();
+ if (_buildParseTrees || hasListener) {
+ if (_errHandler->inErrorRecoveryMode(this)) {
+ tree::ErrorNode *node = createErrorNode(o);
+ _ctx->addChild(node);
+ if (_parseListeners.size() > 0) {
+ for (auto *listener : _parseListeners) {
+ listener->visitErrorNode(node);
+ }
+ }
+ } else {
+ tree::TerminalNode *node = _ctx->addChild(createTerminalNode(o));
+ if (_parseListeners.size() > 0) {
+ for (auto *listener : _parseListeners) {
+ listener->visitTerminal(node);
+ }
+ }
+ }
+ }
+ return o;
+}
+
+void Parser::addContextToParseTree() {
+ // Add current context to parent if we have a parent.
+ if (_ctx->parent == nullptr)
+ return;
+
+ downCast<ParserRuleContext*>(_ctx->parent)->addChild(_ctx);
+}
+
+void Parser::enterRule(ParserRuleContext *localctx, size_t state, size_t /*ruleIndex*/) {
+ setState(state);
+ _ctx = localctx;
+ _ctx->start = _input->LT(1);
+ if (_buildParseTrees) {
+ addContextToParseTree();
+ }
+ if (_parseListeners.size() > 0) {
+ triggerEnterRuleEvent();
+ }
+}
+
+void Parser::exitRule() {
+ if (_matchedEOF) {
+ // if we have matched EOF, it cannot consume past EOF so we use LT(1) here
+ _ctx->stop = _input->LT(1); // LT(1) will be end of file
+ } else {
+ _ctx->stop = _input->LT(-1); // stop node is what we just matched
+ }
+
+ // trigger event on ctx, before it reverts to parent
+ if (_parseListeners.size() > 0) {
+ triggerExitRuleEvent();
+ }
+ setState(_ctx->invokingState);
+ _ctx = downCast<ParserRuleContext*>(_ctx->parent);
+}
+
+void Parser::enterOuterAlt(ParserRuleContext *localctx, size_t altNum) {
+ localctx->setAltNumber(altNum);
+
+ // if we have new localctx, make sure we replace existing ctx
+ // that is previous child of parse tree
+ if (_buildParseTrees && _ctx != localctx) {
+ if (_ctx->parent != nullptr) {
+ ParserRuleContext *parent = downCast<ParserRuleContext*>(_ctx->parent);
+ parent->removeLastChild();
+ parent->addChild(localctx);
+ }
+ }
+ _ctx = localctx;
+}
+
+int Parser::getPrecedence() const {
+ if (_precedenceStack.empty()) {
+ return -1;
+ }
+
+ return _precedenceStack.back();
+}
+
+void Parser::enterRecursionRule(ParserRuleContext *localctx, size_t ruleIndex) {
+ enterRecursionRule(localctx, getATN().ruleToStartState[ruleIndex]->stateNumber, ruleIndex, 0);
+}
+
+void Parser::enterRecursionRule(ParserRuleContext *localctx, size_t state, size_t /*ruleIndex*/, int precedence) {
+ setState(state);
+ _precedenceStack.push_back(precedence);
+ _ctx = localctx;
+ _ctx->start = _input->LT(1);
+ if (!_parseListeners.empty()) {
+ triggerEnterRuleEvent(); // simulates rule entry for left-recursive rules
+ }
+}
+
+void Parser::pushNewRecursionContext(ParserRuleContext *localctx, size_t state, size_t /*ruleIndex*/) {
+ ParserRuleContext *previous = _ctx;
+ previous->parent = localctx;
+ previous->invokingState = state;
+ previous->stop = _input->LT(-1);
+
+ _ctx = localctx;
+ _ctx->start = previous->start;
+ if (_buildParseTrees) {
+ _ctx->addChild(previous);
+ }
+
+ if (_parseListeners.size() > 0) {
+ triggerEnterRuleEvent(); // simulates rule entry for left-recursive rules
+ }
+}
+
+void Parser::unrollRecursionContexts(ParserRuleContext *parentctx) {
+ _precedenceStack.pop_back();
+ _ctx->stop = _input->LT(-1);
+ ParserRuleContext *retctx = _ctx; // save current ctx (return value)
+
+ // unroll so ctx is as it was before call to recursive method
+ if (_parseListeners.size() > 0) {
+ while (_ctx != parentctx) {
+ triggerExitRuleEvent();
+ _ctx = downCast<ParserRuleContext*>(_ctx->parent);
+ }
+ } else {
+ _ctx = parentctx;
+ }
+
+ // hook into tree
+ retctx->parent = parentctx;
+
+ if (_buildParseTrees && parentctx != nullptr) {
+ // add return ctx into invoking rule's tree
+ parentctx->addChild(retctx);
+ }
+}
+
+ParserRuleContext* Parser::getInvokingContext(size_t ruleIndex) {
+ ParserRuleContext *p = _ctx;
+ while (p) {
+ if (p->getRuleIndex() == ruleIndex) {
+ return p;
+ }
+ if (p->parent == nullptr)
+ break;
+ p = downCast<ParserRuleContext*>(p->parent);
+ }
+ return nullptr;
+}
+
+ParserRuleContext* Parser::getContext() {
+ return _ctx;
+}
+
+void Parser::setContext(ParserRuleContext *ctx) {
+ _ctx = ctx;
+}
+
+bool Parser::precpred(RuleContext * /*localctx*/, int precedence) {
+ return precedence >= _precedenceStack.back();
+}
+
+bool Parser::inContext(const std::string &/*context*/) {
+ // TODO: useful in parser?
+ return false;
+}
+
+bool Parser::isExpectedToken(size_t symbol) {
+ const atn::ATN &atn = getInterpreter<atn::ParserATNSimulator>()->atn;
+ ParserRuleContext *ctx = _ctx;
+ atn::ATNState *s = atn.states[getState()];
+ misc::IntervalSet following = atn.nextTokens(s);
+
+ if (following.contains(symbol)) {
+ return true;
+ }
+
+ if (!following.contains(Token::EPSILON)) {
+ return false;
+ }
+
+ while (ctx && ctx->invokingState != ATNState::INVALID_STATE_NUMBER && following.contains(Token::EPSILON)) {
+ atn::ATNState *invokingState = atn.states[ctx->invokingState];
+ const atn::RuleTransition *rt = static_cast<const atn::RuleTransition*>(invokingState->transitions[0].get());
+ following = atn.nextTokens(rt->followState);
+ if (following.contains(symbol)) {
+ return true;
+ }
+
+ ctx = downCast<ParserRuleContext*>(ctx->parent);
+ }
+
+ if (following.contains(Token::EPSILON) && symbol == EOF) {
+ return true;
+ }
+
+ return false;
+}
+
+bool Parser::isMatchedEOF() const {
+ return _matchedEOF;
+}
+
+misc::IntervalSet Parser::getExpectedTokens() {
+ return getATN().getExpectedTokens(getState(), getContext());
+}
+
+misc::IntervalSet Parser::getExpectedTokensWithinCurrentRule() {
+ const atn::ATN &atn = getInterpreter<atn::ParserATNSimulator>()->atn;
+ atn::ATNState *s = atn.states[getState()];
+ return atn.nextTokens(s);
+}
+
+size_t Parser::getRuleIndex(const std::string &ruleName) {
+ const std::map<std::string, size_t> &m = getRuleIndexMap();
+ auto iterator = m.find(ruleName);
+ if (iterator == m.end()) {
+ return INVALID_INDEX;
+ }
+ return iterator->second;
+}
+
+ParserRuleContext* Parser::getRuleContext() {
+ return _ctx;
+}
+
+std::vector<std::string> Parser::getRuleInvocationStack() {
+ return getRuleInvocationStack(_ctx);
+}
+
+std::vector<std::string> Parser::getRuleInvocationStack(RuleContext *p) {
+ std::vector<std::string> const& ruleNames = getRuleNames();
+ std::vector<std::string> stack;
+ RuleContext *run = p;
+ while (run != nullptr) {
+ // compute what follows who invoked us
+ size_t ruleIndex = run->getRuleIndex();
+ if (ruleIndex == INVALID_INDEX ) {
+ stack.push_back("n/a");
+ } else {
+ stack.push_back(ruleNames[ruleIndex]);
+ }
+ if (!RuleContext::is(run->parent)) {
+ break;
+ }
+ run = downCast<RuleContext*>(run->parent);
+ }
+ return stack;
+}
+
+std::vector<std::string> Parser::getDFAStrings() {
+ atn::ParserATNSimulator *simulator = getInterpreter<atn::ParserATNSimulator>();
+ if (!simulator->decisionToDFA.empty()) {
+ UniqueLock<Mutex> lck(_mutex);
+
+ std::vector<std::string> s;
+ for (size_t d = 0; d < simulator->decisionToDFA.size(); d++) {
+ dfa::DFA &dfa = simulator->decisionToDFA[d];
+ s.push_back(dfa.toString(getVocabulary()));
+ }
+ return s;
+ }
+ return std::vector<std::string>();
+}
+
+void Parser::dumpDFA() {
+ atn::ParserATNSimulator *simulator = getInterpreter<atn::ParserATNSimulator>();
+ if (!simulator->decisionToDFA.empty()) {
+ UniqueLock<Mutex> lck(_mutex);
+ bool seenOne = false;
+ for (size_t d = 0; d < simulator->decisionToDFA.size(); d++) {
+ dfa::DFA &dfa = simulator->decisionToDFA[d];
+ if (!dfa.states.empty()) {
+ if (seenOne) {
+ std::cout << std::endl;
+ }
+ std::cout << "Decision " << dfa.decision << ":" << std::endl;
+ std::cout << dfa.toString(getVocabulary());
+ seenOne = true;
+ }
+ }
+ }
+}
+
+std::string Parser::getSourceName() {
+ return _input->getSourceName();
+}
+
+atn::ParseInfo Parser::getParseInfo() const {
+ atn::ParserATNSimulator *simulator = getInterpreter<atn::ParserATNSimulator>();
+ return atn::ParseInfo(dynamic_cast<atn::ProfilingATNSimulator*>(simulator));
+}
+
+void Parser::setProfile(bool profile) {
+ atn::ParserATNSimulator *interp = getInterpreter<atn::ParserATNSimulator>();
+ atn::PredictionMode saveMode = interp != nullptr ? interp->getPredictionMode() : atn::PredictionMode::LL;
+ if (profile) {
+ if (!is<atn::ProfilingATNSimulator *>(interp)) {
+ setInterpreter(new atn::ProfilingATNSimulator(this)); /* mem-check: replacing existing interpreter which gets deleted. */
+ }
+ } else if (is<atn::ProfilingATNSimulator *>(interp)) {
+ /* mem-check: replacing existing interpreter which gets deleted. */
+ atn::ParserATNSimulator *sim = new atn::ParserATNSimulator(this, getATN(), interp->decisionToDFA, interp->getSharedContextCache());
+ setInterpreter(sim);
+ }
+ getInterpreter<atn::ParserATNSimulator>()->setPredictionMode(saveMode);
+}
+
+void Parser::setTrace(bool trace) {
+ if (!trace) {
+ if (_tracer)
+ removeParseListener(_tracer);
+ delete _tracer;
+ _tracer = nullptr;
+ } else {
+ if (_tracer)
+ removeParseListener(_tracer); // Just in case this is triggered multiple times.
+ _tracer = new TraceListener(this);
+ addParseListener(_tracer);
+ }
+}
+
+bool Parser::isTrace() const {
+ return _tracer != nullptr;
+}
+
+tree::TerminalNode *Parser::createTerminalNode(Token *t) {
+ return _tracker.createInstance<tree::TerminalNodeImpl>(t);
+}
+
+tree::ErrorNode *Parser::createErrorNode(Token *t) {
+ return _tracker.createInstance<tree::ErrorNodeImpl>(t);
+}
+
+void Parser::InitializeInstanceFields() {
+ _errHandler = std::make_shared<DefaultErrorStrategy>();
+ _precedenceStack.clear();
+ _precedenceStack.push_back(0);
+ _buildParseTrees = true;
+ _syntaxErrors = 0;
+ _matchedEOF = false;
+ _input = nullptr;
+ _tracer = nullptr;
+ _ctx = nullptr;
+}
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Parser.h b/contrib/libs/antlr4_cpp_runtime/src/Parser.h
new file mode 100644
index 0000000000..f490b00c38
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Parser.h
@@ -0,0 +1,461 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Recognizer.h"
+#include "tree/ParseTreeListener.h"
+#include "tree/ParseTree.h"
+#include "TokenStream.h"
+#include "TokenSource.h"
+#include "misc/Interval.h"
+
+namespace antlr4 {
+
+ /// This is all the parsing support code essentially; most of it is error recovery stuff.
+ class ANTLR4CPP_PUBLIC Parser : public Recognizer {
+ public:
+
+ class TraceListener : public tree::ParseTreeListener {
+ public:
+ TraceListener(Parser *outerInstance);
+ virtual ~TraceListener();
+
+ virtual void enterEveryRule(ParserRuleContext *ctx) override;
+ virtual void visitTerminal(tree::TerminalNode *node) override;
+ virtual void visitErrorNode(tree::ErrorNode *node) override;
+ virtual void exitEveryRule(ParserRuleContext *ctx) override;
+
+ private:
+ Parser *const outerInstance;
+ };
+
+ class TrimToSizeListener : public tree::ParseTreeListener {
+ public:
+ static TrimToSizeListener INSTANCE;
+
+ virtual ~TrimToSizeListener();
+
+ virtual void enterEveryRule(ParserRuleContext *ctx) override;
+ virtual void visitTerminal(tree::TerminalNode *node) override;
+ virtual void visitErrorNode(tree::ErrorNode *node) override;
+ virtual void exitEveryRule(ParserRuleContext *ctx) override;
+ };
+
+ Parser(TokenStream *input);
+ virtual ~Parser();
+
+ /// reset the parser's state
+ virtual void reset();
+
+ /// <summary>
+ /// Match current input symbol against {@code ttype}. If the symbol type
+ /// matches, <seealso cref="ANTLRErrorStrategy#reportMatch"/> and <seealso cref="#consume"/> are
+ /// called to complete the match process.
+ ///
+ /// If the symbol type does not match,
+ /// <seealso cref="ANTLRErrorStrategy#recoverInline"/> is called on the current error
+ /// strategy to attempt recovery. If <seealso cref="#getBuildParseTree"/> is
+ /// {@code true} and the token index of the symbol returned by
+ /// <seealso cref="ANTLRErrorStrategy#recoverInline"/> is -1, the symbol is added to
+ /// the parse tree by calling {@link #createErrorNode(ParserRuleContext, Token)} then
+ /// {@link ParserRuleContext#addErrorNode(ErrorNode)}.
+ /// </summary>
+ /// <param name="ttype"> the token type to match </param>
+ /// <returns> the matched symbol </returns>
+ /// <exception cref="RecognitionException"> if the current input symbol did not match
+ /// {@code ttype} and the error strategy could not recover from the
+ /// mismatched symbol </exception>
+ virtual Token* match(size_t ttype);
+
+ /// <summary>
+ /// Match current input symbol as a wildcard. If the symbol type matches
+ /// (i.e. has a value greater than 0), <seealso cref="ANTLRErrorStrategy#reportMatch"/>
+ /// and <seealso cref="#consume"/> are called to complete the match process.
+ /// <p/>
+ /// If the symbol type does not match,
+ /// <seealso cref="ANTLRErrorStrategy#recoverInline"/> is called on the current error
+ /// strategy to attempt recovery. If <seealso cref="#getBuildParseTree"/> is
+ /// {@code true} and the token index of the symbol returned by
+ /// <seealso cref="ANTLRErrorStrategy#recoverInline"/> is -1, the symbol is added to
+ /// the parse tree by calling <seealso cref="ParserRuleContext#addErrorNode"/>.
+ /// </summary>
+ /// <returns> the matched symbol </returns>
+ /// <exception cref="RecognitionException"> if the current input symbol did not match
+ /// a wildcard and the error strategy could not recover from the mismatched
+ /// symbol </exception>
+ virtual Token* matchWildcard();
+
+ /// <summary>
+ /// Track the <seealso cref="ParserRuleContext"/> objects during the parse and hook
+ /// them up using the <seealso cref="ParserRuleContext#children"/> list so that it
+ /// forms a parse tree. The <seealso cref="ParserRuleContext"/> returned from the start
+ /// rule represents the root of the parse tree.
+ /// <p/>
+ /// Note that if we are not building parse trees, rule contexts only point
+ /// upwards. When a rule exits, it returns the context but that gets garbage
+ /// collected if nobody holds a reference. It points upwards but nobody
+ /// points at it.
+ /// <p/>
+ /// When we build parse trees, we are adding all of these contexts to
+ /// <seealso cref="ParserRuleContext#children"/> list. Contexts are then not candidates
+ /// for garbage collection.
+ /// </summary>
+ virtual void setBuildParseTree(bool buildParseTrees);
+
+ /// <summary>
+ /// Gets whether or not a complete parse tree will be constructed while
+ /// parsing. This property is {@code true} for a newly constructed parser.
+ /// </summary>
+ /// <returns> {@code true} if a complete parse tree will be constructed while
+ /// parsing, otherwise {@code false} </returns>
+ virtual bool getBuildParseTree();
+
+ /// <summary>
+ /// Trim the internal lists of the parse tree during parsing to conserve memory.
+ /// This property is set to {@code false} by default for a newly constructed parser.
+ /// </summary>
+ /// <param name="trimParseTrees"> {@code true} to trim the capacity of the <seealso cref="ParserRuleContext#children"/>
+ /// list to its size after a rule is parsed. </param>
+ virtual void setTrimParseTree(bool trimParseTrees);
+
+ /// <returns> {@code true} if the <seealso cref="ParserRuleContext#children"/> list is trimmed
+ /// using the default <seealso cref="Parser.TrimToSizeListener"/> during the parse process. </returns>
+ virtual bool getTrimParseTree();
+
+ virtual std::vector<tree::ParseTreeListener *> getParseListeners();
+
+ /// <summary>
+ /// Registers {@code listener} to receive events during the parsing process.
+ /// <p/>
+ /// To support output-preserving grammar transformations (including but not
+ /// limited to left-recursion removal, automated left-factoring, and
+ /// optimized code generation), calls to listener methods during the parse
+ /// may differ substantially from calls made by
+ /// <seealso cref="ParseTreeWalker#DEFAULT"/> used after the parse is complete. In
+ /// particular, rule entry and exit events may occur in a different order
+ /// during the parse than after the parser. In addition, calls to certain
+ /// rule entry methods may be omitted.
+ /// <p/>
+ /// With the following specific exceptions, calls to listener events are
+ /// <em>deterministic</em>, i.e. for identical input the calls to listener
+ /// methods will be the same.
+ ///
+ /// <ul>
+ /// <li>Alterations to the grammar used to generate code may change the
+ /// behavior of the listener calls.</li>
+ /// <li>Alterations to the command line options passed to ANTLR 4 when
+ /// generating the parser may change the behavior of the listener calls.</li>
+ /// <li>Changing the version of the ANTLR Tool used to generate the parser
+ /// may change the behavior of the listener calls.</li>
+ /// </ul>
+ /// </summary>
+ /// <param name="listener"> the listener to add
+ /// </param>
+ /// <exception cref="NullPointerException"> if {@code} listener is {@code null} </exception>
+ virtual void addParseListener(tree::ParseTreeListener *listener);
+
+ /// <summary>
+ /// Remove {@code listener} from the list of parse listeners.
+ /// <p/>
+ /// If {@code listener} is {@code null} or has not been added as a parse
+ /// listener, this method does nothing.
+ /// </summary>
+ /// <seealso cref= #addParseListener
+ /// </seealso>
+ /// <param name="listener"> the listener to remove </param>
+ virtual void removeParseListener(tree::ParseTreeListener *listener);
+
+ /// <summary>
+ /// Remove all parse listeners.
+ /// </summary>
+ /// <seealso cref= #addParseListener </seealso>
+ virtual void removeParseListeners();
+
+ /// <summary>
+ /// Notify any parse listeners of an enter rule event.
+ /// </summary>
+ /// <seealso cref= #addParseListener </seealso>
+ virtual void triggerEnterRuleEvent();
+
+ /// <summary>
+ /// Notify any parse listeners of an exit rule event.
+ /// </summary>
+ /// <seealso cref= #addParseListener </seealso>
+ virtual void triggerExitRuleEvent();
+
+ /// <summary>
+ /// Gets the number of syntax errors reported during parsing. This value is
+ /// incremented each time <seealso cref="#notifyErrorListeners"/> is called.
+ /// </summary>
+ /// <seealso cref= #notifyErrorListeners </seealso>
+ virtual size_t getNumberOfSyntaxErrors();
+
+ virtual TokenFactory<CommonToken>* getTokenFactory() override;
+
+ /// <summary>
+ /// Tell our token source and error strategy about a new way to create tokens. </summary>
+ template<typename T1>
+ void setTokenFactory(TokenFactory<T1> *factory) {
+ _input->getTokenSource()->setTokenFactory(factory);
+ }
+
+ /// The ATN with bypass alternatives is expensive to create so we create it
+ /// lazily. The ATN is owned by us.
+ virtual const atn::ATN& getATNWithBypassAlts();
+
+ /// <summary>
+ /// The preferred method of getting a tree pattern. For example, here's a
+ /// sample use:
+ ///
+ /// <pre>
+ /// ParseTree t = parser.expr();
+ /// ParseTreePattern p = parser.compileParseTreePattern("<ID>+0", MyParser.RULE_expr);
+ /// ParseTreeMatch m = p.match(t);
+ /// String id = m.get("ID");
+ /// </pre>
+ /// </summary>
+ virtual tree::pattern::ParseTreePattern compileParseTreePattern(const std::string &pattern, int patternRuleIndex);
+
+ /// <summary>
+ /// The same as <seealso cref="#compileParseTreePattern(String, int)"/> but specify a
+ /// <seealso cref="Lexer"/> rather than trying to deduce it from this parser.
+ /// </summary>
+ virtual tree::pattern::ParseTreePattern compileParseTreePattern(const std::string &pattern, int patternRuleIndex,
+ Lexer *lexer);
+
+ virtual Ref<ANTLRErrorStrategy> getErrorHandler();
+ virtual void setErrorHandler(Ref<ANTLRErrorStrategy> const& handler);
+
+ virtual IntStream* getInputStream() override;
+ void setInputStream(IntStream *input) override;
+
+ virtual TokenStream* getTokenStream();
+
+ /// Set the token stream and reset the parser.
+ virtual void setTokenStream(TokenStream *input);
+
+ /// <summary>
+ /// Match needs to return the current input symbol, which gets put
+ /// into the label for the associated token ref; e.g., x=ID.
+ /// </summary>
+ virtual Token* getCurrentToken();
+
+ void notifyErrorListeners(const std::string &msg);
+
+ virtual void notifyErrorListeners(Token *offendingToken, const std::string &msg, std::exception_ptr e);
+
+ /// Consume and return the <seealso cref="#getCurrentToken current symbol"/>.
+ /// <p/>
+ /// E.g., given the following input with {@code A} being the current
+ /// lookahead symbol, this function moves the cursor to {@code B} and returns
+ /// {@code A}.
+ ///
+ /// <pre>
+ /// A B
+ /// ^
+ /// </pre>
+ ///
+ /// If the parser is not in error recovery mode, the consumed symbol is added
+ /// to the parse tree using <seealso cref="ParserRuleContext#addChild(TerminalNode)"/>, and
+ /// <seealso cref="ParseTreeListener#visitTerminal"/> is called on any parse listeners.
+ /// If the parser <em>is</em> in error recovery mode, the consumed symbol is
+ /// added to the parse tree using {@link #createErrorNode(ParserRuleContext, Token)} then
+ /// {@link ParserRuleContext#addErrorNode(ErrorNode)} and
+ /// <seealso cref="ParseTreeListener#visitErrorNode"/> is called on any parse
+ /// listeners.
+ virtual Token* consume();
+
+ /// Always called by generated parsers upon entry to a rule. Access field
+ /// <seealso cref="#_ctx"/> get the current context.
+ virtual void enterRule(ParserRuleContext *localctx, size_t state, size_t ruleIndex);
+
+ void exitRule();
+
+ virtual void enterOuterAlt(ParserRuleContext *localctx, size_t altNum);
+
+ /**
+ * Get the precedence level for the top-most precedence rule.
+ *
+ * @return The precedence level for the top-most precedence rule, or -1 if
+ * the parser context is not nested within a precedence rule.
+ */
+ int getPrecedence() const;
+
+ /// @deprecated Use
+ /// <seealso cref="#enterRecursionRule(ParserRuleContext, int, int, int)"/> instead.
+ virtual void enterRecursionRule(ParserRuleContext *localctx, size_t ruleIndex);
+ virtual void enterRecursionRule(ParserRuleContext *localctx, size_t state, size_t ruleIndex, int precedence);
+
+ /** Like {@link #enterRule} but for recursive rules.
+ * Make the current context the child of the incoming localctx.
+ */
+ virtual void pushNewRecursionContext(ParserRuleContext *localctx, size_t state, size_t ruleIndex);
+ virtual void unrollRecursionContexts(ParserRuleContext *parentctx);
+ virtual ParserRuleContext* getInvokingContext(size_t ruleIndex);
+ virtual ParserRuleContext* getContext();
+ virtual void setContext(ParserRuleContext *ctx);
+ virtual bool precpred(RuleContext *localctx, int precedence) override;
+ virtual bool inContext(const std::string &context);
+
+ /// <summary>
+ /// Checks whether or not {@code symbol} can follow the current state in the
+ /// ATN. The behavior of this method is equivalent to the following, but is
+ /// implemented such that the complete context-sensitive follow set does not
+ /// need to be explicitly constructed.
+ ///
+ /// <pre>
+ /// return getExpectedTokens().contains(symbol);
+ /// </pre>
+ /// </summary>
+ /// <param name="symbol"> the symbol type to check </param>
+ /// <returns> {@code true} if {@code symbol} can follow the current state in
+ /// the ATN, otherwise {@code false}. </returns>
+ virtual bool isExpectedToken(size_t symbol);
+
+ bool isMatchedEOF() const;
+
+ /// <summary>
+ /// Computes the set of input symbols which could follow the current parser
+ /// state and context, as given by <seealso cref="#getState"/> and <seealso cref="#getContext"/>,
+ /// respectively.
+ /// </summary>
+ /// <seealso cref= ATN#getExpectedTokens(int, RuleContext) </seealso>
+ virtual misc::IntervalSet getExpectedTokens();
+
+ virtual misc::IntervalSet getExpectedTokensWithinCurrentRule();
+
+ /// Get a rule's index (i.e., {@code RULE_ruleName} field) or INVALID_INDEX if not found.
+ virtual size_t getRuleIndex(const std::string &ruleName);
+
+ virtual ParserRuleContext* getRuleContext();
+
+ /// <summary>
+ /// Return List&lt;String&gt; of the rule names in your parser instance
+ /// leading up to a call to the current rule. You could override if
+ /// you want more details such as the file/line info of where
+ /// in the ATN a rule is invoked.
+ ///
+ /// This is very useful for error messages.
+ /// </summary>
+ virtual std::vector<std::string> getRuleInvocationStack();
+
+ virtual std::vector<std::string> getRuleInvocationStack(RuleContext *p);
+
+ /// <summary>
+ /// For debugging and other purposes. </summary>
+ virtual std::vector<std::string> getDFAStrings();
+
+ /// <summary>
+ /// For debugging and other purposes. </summary>
+ virtual void dumpDFA();
+
+ virtual std::string getSourceName();
+
+ atn::ParseInfo getParseInfo() const;
+
+ /**
+ * @since 4.3
+ */
+ void setProfile(bool profile);
+
+ /// <summary>
+ /// During a parse is sometimes useful to listen in on the rule entry and exit
+ /// events as well as token matches. This is for quick and dirty debugging.
+ /// </summary>
+ virtual void setTrace(bool trace);
+
+ /**
+ * Gets whether a {@link TraceListener} is registered as a parse listener
+ * for the parser.
+ *
+ * @see #setTrace(boolean)
+ */
+ bool isTrace() const;
+
+ tree::ParseTreeTracker& getTreeTracker() { return _tracker; }
+
+ /** How to create a token leaf node associated with a parent.
+ * Typically, the terminal node to create is not a function of the parent
+ * but this method must still set the parent pointer of the terminal node
+ * returned. I would prefer having {@link ParserRuleContext#addAnyChild(ParseTree)}
+ * set the parent pointer, but the parent pointer is implementation dependent
+ * and currently there is no setParent() in {@link TerminalNode} (and can't
+ * add method in Java 1.7 without breaking backward compatibility).
+ *
+ * @since 4.7
+ */
+ tree::TerminalNode *createTerminalNode(Token *t);
+
+ /** How to create an error node, given a token, associated with a parent.
+ * Typically, the error node to create is not a function of the parent
+ * but this method must still set the parent pointer of the terminal node
+ * returned. I would prefer having {@link ParserRuleContext#addAnyChild(ParseTree)}
+ * set the parent pointer, but the parent pointer is implementation dependent
+ * and currently there is no setParent() in {@link ErrorNode} (and can't
+ * add method in Java 1.7 without breaking backward compatibility).
+ *
+ * @since 4.7
+ */
+ tree::ErrorNode *createErrorNode(Token *t);
+
+ protected:
+ /// The ParserRuleContext object for the currently executing rule.
+ /// This is always non-null during the parsing process.
+ // ml: this is one of the contexts tracked in _allocatedContexts.
+ ParserRuleContext *_ctx;
+
+ /// The error handling strategy for the parser. The default is DefaultErrorStrategy.
+ /// See also getErrorHandler.
+ Ref<ANTLRErrorStrategy> _errHandler;
+
+ /// <summary>
+ /// The input stream.
+ /// </summary>
+ /// <seealso cref= #getInputStream </seealso>
+ /// <seealso cref= #setInputStream </seealso>
+ TokenStream *_input;
+
+ std::vector<int> _precedenceStack;
+
+ /// <summary>
+ /// Specifies whether or not the parser should construct a parse tree during
+ /// the parsing process. The default value is {@code true}.
+ /// </summary>
+ /// <seealso cref= #getBuildParseTree </seealso>
+ /// <seealso cref= #setBuildParseTree </seealso>
+ bool _buildParseTrees;
+
+ /// The list of <seealso cref="ParseTreeListener"/> listeners registered to receive
+ /// events during the parse.
+ /// <seealso cref= #addParseListener </seealso>
+ std::vector<tree::ParseTreeListener *> _parseListeners;
+
+ /// <summary>
+ /// The number of syntax errors reported during parsing. This value is
+ /// incremented each time <seealso cref="#notifyErrorListeners"/> is called.
+ /// </summary>
+ size_t _syntaxErrors;
+
+ /** Indicates parser has match()ed EOF token. See {@link #exitRule()}. */
+ bool _matchedEOF;
+
+ virtual void addContextToParseTree();
+
+ // All rule contexts created during a parse run. This is cleared when calling reset().
+ tree::ParseTreeTracker _tracker;
+
+ private:
+ /// When setTrace(true) is called, a reference to the
+ /// TraceListener is stored here so it can be easily removed in a
+ /// later call to setTrace(false). The listener itself is
+ /// implemented as a parser listener so this field is not directly used by
+ /// other parser methods.
+ TraceListener *_tracer;
+
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ParserInterpreter.cpp b/contrib/libs/antlr4_cpp_runtime/src/ParserInterpreter.cpp
new file mode 100644
index 0000000000..e1c54a0eb1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ParserInterpreter.cpp
@@ -0,0 +1,294 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "dfa/DFA.h"
+#include "atn/RuleStartState.h"
+#include "InterpreterRuleContext.h"
+#include "atn/ParserATNSimulator.h"
+#include "ANTLRErrorStrategy.h"
+#include "atn/LoopEndState.h"
+#include "FailedPredicateException.h"
+#include "atn/StarLoopEntryState.h"
+#include "atn/AtomTransition.h"
+#include "atn/RuleTransition.h"
+#include "atn/PredicateTransition.h"
+#include "atn/PrecedencePredicateTransition.h"
+#include "atn/ActionTransition.h"
+#include "atn/ATN.h"
+#include "atn/RuleStopState.h"
+#include "Lexer.h"
+#include "Token.h"
+#include "Vocabulary.h"
+#include "InputMismatchException.h"
+#include "CommonToken.h"
+#include "tree/ErrorNode.h"
+
+#include "support/CPPUtils.h"
+#include "support/Casts.h"
+
+#include "ParserInterpreter.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+
+using namespace antlrcpp;
+
+ParserInterpreter::ParserInterpreter(const std::string &grammarFileName, const dfa::Vocabulary &vocabulary,
+ const std::vector<std::string> &ruleNames, const atn::ATN &atn, TokenStream *input)
+ : Parser(input), _grammarFileName(grammarFileName), _atn(atn), _ruleNames(ruleNames), _vocabulary(vocabulary) {
+
+ // init decision DFA
+ for (size_t i = 0; i < atn.getNumberOfDecisions(); ++i) {
+ atn::DecisionState *decisionState = atn.getDecisionState(i);
+ _decisionToDFA.push_back(dfa::DFA(decisionState, i));
+ }
+
+ // get atn simulator that knows how to do predictions
+ _interpreter = new atn::ParserATNSimulator(this, atn, _decisionToDFA, _sharedContextCache); /* mem-check: deleted in d-tor */
+}
+
+ParserInterpreter::~ParserInterpreter() {
+ delete _interpreter;
+}
+
+void ParserInterpreter::reset() {
+ Parser::reset();
+ _overrideDecisionReached = false;
+ _overrideDecisionRoot = nullptr;
+}
+
+const atn::ATN& ParserInterpreter::getATN() const {
+ return _atn;
+}
+
+const dfa::Vocabulary& ParserInterpreter::getVocabulary() const {
+ return _vocabulary;
+}
+
+const std::vector<std::string>& ParserInterpreter::getRuleNames() const {
+ return _ruleNames;
+}
+
+std::string ParserInterpreter::getGrammarFileName() const {
+ return _grammarFileName;
+}
+
+ParserRuleContext* ParserInterpreter::parse(size_t startRuleIndex) {
+ atn::RuleStartState *startRuleStartState = _atn.ruleToStartState[startRuleIndex];
+
+ _rootContext = createInterpreterRuleContext(nullptr, atn::ATNState::INVALID_STATE_NUMBER, startRuleIndex);
+
+ if (startRuleStartState->isLeftRecursiveRule) {
+ enterRecursionRule(_rootContext, startRuleStartState->stateNumber, startRuleIndex, 0);
+ } else {
+ enterRule(_rootContext, startRuleStartState->stateNumber, startRuleIndex);
+ }
+
+ while (true) {
+ atn::ATNState *p = getATNState();
+ switch (p->getStateType()) {
+ case atn::ATNStateType::RULE_STOP :
+ // pop; return from rule
+ if (_ctx->isEmpty()) {
+ if (startRuleStartState->isLeftRecursiveRule) {
+ ParserRuleContext *result = _ctx;
+ auto parentContext = _parentContextStack.top();
+ _parentContextStack.pop();
+ unrollRecursionContexts(parentContext.first);
+ return result;
+ } else {
+ exitRule();
+ return _rootContext;
+ }
+ }
+
+ visitRuleStopState(p);
+ break;
+
+ default :
+ try {
+ visitState(p);
+ }
+ catch (RecognitionException &e) {
+ setState(_atn.ruleToStopState[p->ruleIndex]->stateNumber);
+ getErrorHandler()->reportError(this, e);
+ getContext()->exception = std::current_exception();
+ recover(e);
+ }
+
+ break;
+ }
+ }
+}
+
+void ParserInterpreter::enterRecursionRule(ParserRuleContext *localctx, size_t state, size_t ruleIndex, int precedence) {
+ _parentContextStack.push({ _ctx, localctx->invokingState });
+ Parser::enterRecursionRule(localctx, state, ruleIndex, precedence);
+}
+
+void ParserInterpreter::addDecisionOverride(int decision, int tokenIndex, int forcedAlt) {
+ _overrideDecision = decision;
+ _overrideDecisionInputIndex = tokenIndex;
+ _overrideDecisionAlt = forcedAlt;
+}
+
+Ref<InterpreterRuleContext> ParserInterpreter::getOverrideDecisionRoot() const {
+ return _overrideDecisionRoot;
+}
+
+InterpreterRuleContext* ParserInterpreter::getRootContext() {
+ return _rootContext;
+}
+
+atn::ATNState* ParserInterpreter::getATNState() {
+ return _atn.states[getState()];
+}
+
+void ParserInterpreter::visitState(atn::ATNState *p) {
+ size_t predictedAlt = 1;
+ if (DecisionState::is(p)) {
+ predictedAlt = visitDecisionState(downCast<DecisionState*>(p));
+ }
+
+ const atn::Transition *transition = p->transitions[predictedAlt - 1].get();
+ switch (transition->getTransitionType()) {
+ case atn::TransitionType::EPSILON:
+ if (p->getStateType() == ATNStateType::STAR_LOOP_ENTRY &&
+ (downCast<StarLoopEntryState *>(p))->isPrecedenceDecision &&
+ !LoopEndState::is(transition->target)) {
+ // We are at the start of a left recursive rule's (...)* loop
+ // and we're not taking the exit branch of loop.
+ InterpreterRuleContext *localctx = createInterpreterRuleContext(_parentContextStack.top().first,
+ _parentContextStack.top().second, static_cast<int>(_ctx->getRuleIndex()));
+ pushNewRecursionContext(localctx, _atn.ruleToStartState[p->ruleIndex]->stateNumber, static_cast<int>(_ctx->getRuleIndex()));
+ }
+ break;
+
+ case atn::TransitionType::ATOM:
+ match(static_cast<int>(static_cast<const atn::AtomTransition*>(transition)->_label));
+ break;
+
+ case atn::TransitionType::RANGE:
+ case atn::TransitionType::SET:
+ case atn::TransitionType::NOT_SET:
+ if (!transition->matches(static_cast<int>(_input->LA(1)), Token::MIN_USER_TOKEN_TYPE, Lexer::MAX_CHAR_VALUE)) {
+ recoverInline();
+ }
+ matchWildcard();
+ break;
+
+ case atn::TransitionType::WILDCARD:
+ matchWildcard();
+ break;
+
+ case atn::TransitionType::RULE:
+ {
+ atn::RuleStartState *ruleStartState = static_cast<atn::RuleStartState*>(transition->target);
+ size_t ruleIndex = ruleStartState->ruleIndex;
+ InterpreterRuleContext *newctx = createInterpreterRuleContext(_ctx, p->stateNumber, ruleIndex);
+ if (ruleStartState->isLeftRecursiveRule) {
+ enterRecursionRule(newctx, ruleStartState->stateNumber, ruleIndex, static_cast<const atn::RuleTransition*>(transition)->precedence);
+ } else {
+ enterRule(newctx, transition->target->stateNumber, ruleIndex);
+ }
+ }
+ break;
+
+ case atn::TransitionType::PREDICATE:
+ {
+ const atn::PredicateTransition *predicateTransition = static_cast<const atn::PredicateTransition*>(transition);
+ if (!sempred(_ctx, predicateTransition->getRuleIndex(), predicateTransition->getPredIndex())) {
+ throw FailedPredicateException(this);
+ }
+ }
+ break;
+
+ case atn::TransitionType::ACTION:
+ {
+ const atn::ActionTransition *actionTransition = static_cast<const atn::ActionTransition*>(transition);
+ action(_ctx, actionTransition->ruleIndex, actionTransition->actionIndex);
+ }
+ break;
+
+ case atn::TransitionType::PRECEDENCE:
+ {
+ if (!precpred(_ctx, static_cast<const atn::PrecedencePredicateTransition*>(transition)->getPrecedence())) {
+ throw FailedPredicateException(this, "precpred(_ctx, " + std::to_string(static_cast<const atn::PrecedencePredicateTransition*>(transition)->getPrecedence()) + ")");
+ }
+ }
+ break;
+
+ default:
+ throw UnsupportedOperationException("Unrecognized ATN transition type.");
+ }
+
+ setState(transition->target->stateNumber);
+}
+
+size_t ParserInterpreter::visitDecisionState(DecisionState *p) {
+ size_t predictedAlt = 1;
+ if (p->transitions.size() > 1) {
+ getErrorHandler()->sync(this);
+ int decision = p->decision;
+ if (decision == _overrideDecision && _input->index() == _overrideDecisionInputIndex && !_overrideDecisionReached) {
+ predictedAlt = _overrideDecisionAlt;
+ _overrideDecisionReached = true;
+ } else {
+ predictedAlt = getInterpreter<ParserATNSimulator>()->adaptivePredict(_input, decision, _ctx);
+ }
+ }
+ return predictedAlt;
+}
+
+InterpreterRuleContext* ParserInterpreter::createInterpreterRuleContext(ParserRuleContext *parent,
+ size_t invokingStateNumber, size_t ruleIndex) {
+ return _tracker.createInstance<InterpreterRuleContext>(parent, invokingStateNumber, ruleIndex);
+}
+
+void ParserInterpreter::visitRuleStopState(atn::ATNState *p) {
+ atn::RuleStartState *ruleStartState = _atn.ruleToStartState[p->ruleIndex];
+ if (ruleStartState->isLeftRecursiveRule) {
+ std::pair<ParserRuleContext *, size_t> parentContext = _parentContextStack.top();
+ _parentContextStack.pop();
+
+ unrollRecursionContexts(parentContext.first);
+ setState(parentContext.second);
+ } else {
+ exitRule();
+ }
+
+ const atn::RuleTransition *ruleTransition = static_cast<const atn::RuleTransition*>(_atn.states[getState()]->transitions[0].get());
+ setState(ruleTransition->followState->stateNumber);
+}
+
+void ParserInterpreter::recover(RecognitionException &e) {
+ size_t i = _input->index();
+ getErrorHandler()->recover(this, std::make_exception_ptr(e));
+
+ if (_input->index() == i) {
+ // no input consumed, better add an error node
+ if (is<InputMismatchException *>(&e)) {
+ InputMismatchException &ime = static_cast<InputMismatchException&>(e);
+ Token *tok = e.getOffendingToken();
+ size_t expectedTokenType = ime.getExpectedTokens().getMinElement(); // get any element
+ _errorToken = getTokenFactory()->create({ tok->getTokenSource(), tok->getTokenSource()->getInputStream() },
+ expectedTokenType, tok->getText(), Token::DEFAULT_CHANNEL, INVALID_INDEX, INVALID_INDEX, // invalid start/stop
+ tok->getLine(), tok->getCharPositionInLine());
+ _ctx->addChild(createErrorNode(_errorToken.get()));
+ }
+ else { // NoViableAlt
+ Token *tok = e.getOffendingToken();
+ _errorToken = getTokenFactory()->create({ tok->getTokenSource(), tok->getTokenSource()->getInputStream() },
+ Token::INVALID_TYPE, tok->getText(), Token::DEFAULT_CHANNEL, INVALID_INDEX, INVALID_INDEX, // invalid start/stop
+ tok->getLine(), tok->getCharPositionInLine());
+ _ctx->addChild(createErrorNode(_errorToken.get()));
+ }
+ }
+}
+
+Token* ParserInterpreter::recoverInline() {
+ return _errHandler->recoverInline(this);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ParserInterpreter.h b/contrib/libs/antlr4_cpp_runtime/src/ParserInterpreter.h
new file mode 100644
index 0000000000..6d4a679e5b
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ParserInterpreter.h
@@ -0,0 +1,173 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Parser.h"
+#include "atn/ATN.h"
+#include "support/BitSet.h"
+#include "atn/PredictionContext.h"
+#include "atn/PredictionContextCache.h"
+#include "Vocabulary.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// A parser simulator that mimics what ANTLR's generated
+ /// parser code does. A ParserATNSimulator is used to make
+ /// predictions via adaptivePredict but this class moves a pointer through the
+ /// ATN to simulate parsing. ParserATNSimulator just
+ /// makes us efficient rather than having to backtrack, for example.
+ ///
+ /// This properly creates parse trees even for left recursive rules.
+ ///
+ /// We rely on the left recursive rule invocation and special predicate
+ /// transitions to make left recursive rules work.
+ ///
+ /// See TestParserInterpreter for examples.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC ParserInterpreter : public Parser {
+ public:
+ ParserInterpreter(const std::string &grammarFileName, const dfa::Vocabulary &vocabulary,
+ const std::vector<std::string> &ruleNames, const atn::ATN &atn, TokenStream *input);
+ ~ParserInterpreter();
+
+ virtual void reset() override;
+
+ virtual const atn::ATN& getATN() const override;
+
+ virtual const dfa::Vocabulary& getVocabulary() const override;
+
+ virtual const std::vector<std::string>& getRuleNames() const override;
+ virtual std::string getGrammarFileName() const override;
+
+ /// Begin parsing at startRuleIndex
+ virtual ParserRuleContext* parse(size_t startRuleIndex);
+
+ virtual void enterRecursionRule(ParserRuleContext *localctx, size_t state, size_t ruleIndex, int precedence) override;
+
+
+ /** Override this parser interpreters normal decision-making process
+ * at a particular decision and input token index. Instead of
+ * allowing the adaptive prediction mechanism to choose the
+ * first alternative within a block that leads to a successful parse,
+ * force it to take the alternative, 1..n for n alternatives.
+ *
+ * As an implementation limitation right now, you can only specify one
+ * override. This is sufficient to allow construction of different
+ * parse trees for ambiguous input. It means re-parsing the entire input
+ * in general because you're never sure where an ambiguous sequence would
+ * live in the various parse trees. For example, in one interpretation,
+ * an ambiguous input sequence would be matched completely in expression
+ * but in another it could match all the way back to the root.
+ *
+ * s : e '!'? ;
+ * e : ID
+ * | ID '!'
+ * ;
+ *
+ * Here, x! can be matched as (s (e ID) !) or (s (e ID !)). In the first
+ * case, the ambiguous sequence is fully contained only by the root.
+ * In the second case, the ambiguous sequences fully contained within just
+ * e, as in: (e ID !).
+ *
+ * Rather than trying to optimize this and make
+ * some intelligent decisions for optimization purposes, I settled on
+ * just re-parsing the whole input and then using
+ * {link Trees#getRootOfSubtreeEnclosingRegion} to find the minimal
+ * subtree that contains the ambiguous sequence. I originally tried to
+ * record the call stack at the point the parser detected and ambiguity but
+ * left recursive rules create a parse tree stack that does not reflect
+ * the actual call stack. That impedance mismatch was enough to make
+ * it it challenging to restart the parser at a deeply nested rule
+ * invocation.
+ *
+ * Only parser interpreters can override decisions so as to avoid inserting
+ * override checking code in the critical ALL(*) prediction execution path.
+ *
+ * @since 4.5.1
+ */
+ void addDecisionOverride(int decision, int tokenIndex, int forcedAlt);
+
+ Ref<InterpreterRuleContext> getOverrideDecisionRoot() const;
+
+ /** Return the root of the parse, which can be useful if the parser
+ * bails out. You still can access the top node. Note that,
+ * because of the way left recursive rules add children, it's possible
+ * that the root will not have any children if the start rule immediately
+ * called and left recursive rule that fails.
+ *
+ * @since 4.5.1
+ */
+ InterpreterRuleContext* getRootContext();
+
+ protected:
+ const std::string _grammarFileName;
+ const atn::ATN &_atn;
+
+ std::vector<std::string> _ruleNames;
+
+ std::vector<dfa::DFA> _decisionToDFA; // not shared like it is for generated parsers
+ atn::PredictionContextCache _sharedContextCache;
+
+ /** This stack corresponds to the _parentctx, _parentState pair of locals
+ * that would exist on call stack frames with a recursive descent parser;
+ * in the generated function for a left-recursive rule you'd see:
+ *
+ * private EContext e(int _p) throws RecognitionException {
+ * ParserRuleContext _parentctx = _ctx; // Pair.a
+ * int _parentState = getState(); // Pair.b
+ * ...
+ * }
+ *
+ * Those values are used to create new recursive rule invocation contexts
+ * associated with left operand of an alt like "expr '*' expr".
+ */
+ std::stack<std::pair<ParserRuleContext *, size_t>> _parentContextStack;
+
+ /** We need a map from (decision,inputIndex)->forced alt for computing ambiguous
+ * parse trees. For now, we allow exactly one override.
+ */
+ int _overrideDecision = -1;
+ size_t _overrideDecisionInputIndex = INVALID_INDEX;
+ size_t _overrideDecisionAlt = INVALID_INDEX;
+ bool _overrideDecisionReached = false; // latch and only override once; error might trigger infinite loop
+
+ /** What is the current context when we override a decision? This tells
+ * us what the root of the parse tree is when using override
+ * for an ambiguity/lookahead check.
+ */
+ Ref<InterpreterRuleContext> _overrideDecisionRoot;
+ InterpreterRuleContext* _rootContext;
+
+ virtual atn::ATNState *getATNState();
+ virtual void visitState(atn::ATNState *p);
+
+ /** Method visitDecisionState() is called when the interpreter reaches
+ * a decision state (instance of DecisionState). It gives an opportunity
+ * for subclasses to track interesting things.
+ */
+ size_t visitDecisionState(atn::DecisionState *p);
+
+ /** Provide simple "factory" for InterpreterRuleContext's.
+ * @since 4.5.1
+ */
+ InterpreterRuleContext* createInterpreterRuleContext(ParserRuleContext *parent, size_t invokingStateNumber, size_t ruleIndex);
+
+ virtual void visitRuleStopState(atn::ATNState *p);
+
+ /** Rely on the error handler for this parser but, if no tokens are consumed
+ * to recover, add an error node. Otherwise, nothing is seen in the parse
+ * tree.
+ */
+ void recover(RecognitionException &e);
+ Token* recoverInline();
+
+ private:
+ const dfa::Vocabulary &_vocabulary;
+ std::unique_ptr<Token> _errorToken;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ParserRuleContext.cpp b/contrib/libs/antlr4_cpp_runtime/src/ParserRuleContext.cpp
new file mode 100644
index 0000000000..7eb3e6577f
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ParserRuleContext.cpp
@@ -0,0 +1,138 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/TerminalNode.h"
+#include "tree/ErrorNode.h"
+#include "misc/Interval.h"
+#include "Parser.h"
+#include "Token.h"
+
+#include "support/Casts.h"
+#include "support/CPPUtils.h"
+
+#include "ParserRuleContext.h"
+
+using namespace antlr4;
+using namespace antlr4::tree;
+
+using namespace antlrcpp;
+
+ParserRuleContext ParserRuleContext::EMPTY;
+
+ParserRuleContext::ParserRuleContext()
+ : start(nullptr), stop(nullptr) {
+}
+
+ParserRuleContext::ParserRuleContext(ParserRuleContext *parent, size_t invokingStateNumber)
+: RuleContext(parent, invokingStateNumber), start(nullptr), stop(nullptr) {
+}
+
+void ParserRuleContext::copyFrom(ParserRuleContext *ctx) {
+ // from RuleContext
+ this->parent = ctx->parent;
+ this->invokingState = ctx->invokingState;
+
+ this->start = ctx->start;
+ this->stop = ctx->stop;
+
+ // copy any error nodes to alt label node
+ if (!ctx->children.empty()) {
+ for (auto *child : ctx->children) {
+ if (ErrorNode::is(child)) {
+ downCast<ErrorNode*>(child)->setParent(this);
+ children.push_back(child);
+ }
+ }
+
+ // Remove the just reparented error nodes from the source context.
+ ctx->children.erase(std::remove_if(ctx->children.begin(), ctx->children.end(), [this](tree::ParseTree *e) -> bool {
+ return std::find(children.begin(), children.end(), e) != children.end();
+ }), ctx->children.end());
+ }
+}
+
+void ParserRuleContext::enterRule(tree::ParseTreeListener * /*listener*/) {
+}
+
+void ParserRuleContext::exitRule(tree::ParseTreeListener * /*listener*/) {
+}
+
+tree::TerminalNode* ParserRuleContext::addChild(tree::TerminalNode *t) {
+ t->setParent(this);
+ children.push_back(t);
+ return t;
+}
+
+RuleContext* ParserRuleContext::addChild(RuleContext *ruleInvocation) {
+ children.push_back(ruleInvocation);
+ return ruleInvocation;
+}
+
+void ParserRuleContext::removeLastChild() {
+ if (!children.empty()) {
+ children.pop_back();
+ }
+}
+
+tree::TerminalNode* ParserRuleContext::getToken(size_t ttype, size_t i) const {
+ if (i >= children.size()) {
+ return nullptr;
+ }
+ size_t j = 0; // what token with ttype have we found?
+ for (auto *child : children) {
+ if (TerminalNode::is(child)) {
+ tree::TerminalNode *typedChild = downCast<tree::TerminalNode*>(child);
+ Token *symbol = typedChild->getSymbol();
+ if (symbol->getType() == ttype) {
+ if (j++ == i) {
+ return typedChild;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+std::vector<tree::TerminalNode *> ParserRuleContext::getTokens(size_t ttype) const {
+ std::vector<tree::TerminalNode*> tokens;
+ for (auto *child : children) {
+ if (TerminalNode::is(child)) {
+ tree::TerminalNode *typedChild = downCast<tree::TerminalNode*>(child);
+ Token *symbol = typedChild->getSymbol();
+ if (symbol->getType() == ttype) {
+ tokens.push_back(typedChild);
+ }
+ }
+ }
+ return tokens;
+}
+
+misc::Interval ParserRuleContext::getSourceInterval() {
+ if (start == nullptr) {
+ return misc::Interval::INVALID;
+ }
+
+ if (stop == nullptr || stop->getTokenIndex() < start->getTokenIndex()) {
+ return misc::Interval(start->getTokenIndex(), start->getTokenIndex() - 1); // empty
+ }
+ return misc::Interval(start->getTokenIndex(), stop->getTokenIndex());
+}
+
+Token* ParserRuleContext::getStart() const {
+ return start;
+}
+
+Token* ParserRuleContext::getStop() const {
+ return stop;
+}
+
+std::string ParserRuleContext::toInfoString(Parser *recognizer) {
+ std::vector<std::string> rules = recognizer->getRuleInvocationStack(this);
+ std::reverse(rules.begin(), rules.end());
+ std::string rulesStr = antlrcpp::arrayToString(rules);
+ return "ParserRuleContext" + rulesStr + "{start=" + std::to_string(start->getTokenIndex()) + ", stop=" +
+ std::to_string(stop->getTokenIndex()) + '}';
+}
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ParserRuleContext.h b/contrib/libs/antlr4_cpp_runtime/src/ParserRuleContext.h
new file mode 100644
index 0000000000..63a8466e59
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ParserRuleContext.h
@@ -0,0 +1,147 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "RuleContext.h"
+#include "support/CPPUtils.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// A rule invocation record for parsing.
+ ///
+ /// Contains all of the information about the current rule not stored in the
+ /// RuleContext. It handles parse tree children list, Any ATN state
+ /// tracing, and the default values available for rule invocatons:
+ /// start, stop, rule index, current alt number.
+ ///
+ /// Subclasses made for each rule and grammar track the parameters,
+ /// return values, locals, and labels specific to that rule. These
+ /// are the objects that are returned from rules.
+ ///
+ /// Note text is not an actual field of a rule return value; it is computed
+ /// from start and stop using the input stream's toString() method. I
+ /// could add a ctor to this so that we can pass in and store the input
+ /// stream, but I'm not sure we want to do that. It would seem to be undefined
+ /// to get the .text property anyway if the rule matches tokens from multiple
+ /// input streams.
+ ///
+ /// I do not use getters for fields of objects that are used simply to
+ /// group values such as this aggregate. The getters/setters are there to
+ /// satisfy the superclass interface.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC ParserRuleContext : public RuleContext {
+ public:
+ static ParserRuleContext EMPTY;
+
+ /// <summary>
+ /// For debugging/tracing purposes, we want to track all of the nodes in
+ /// the ATN traversed by the parser for a particular rule.
+ /// This list indicates the sequence of ATN nodes used to match
+ /// the elements of the children list. This list does not include
+ /// ATN nodes and other rules used to match rule invocations. It
+ /// traces the rule invocation node itself but nothing inside that
+ /// other rule's ATN submachine.
+ ///
+ /// There is NOT a one-to-one correspondence between the children and
+ /// states list. There are typically many nodes in the ATN traversed
+ /// for each element in the children list. For example, for a rule
+ /// invocation there is the invoking state and the following state.
+ ///
+ /// The parser setState() method updates field s and adds it to this list
+ /// if we are debugging/tracing.
+ ///
+ /// This does not trace states visited during prediction.
+ /// </summary>
+ // public List<Integer> states;
+
+ Token *start;
+ Token *stop;
+
+ /// The exception that forced this rule to return. If the rule successfully
+ /// completed, this is "null exception pointer".
+ std::exception_ptr exception;
+
+ ParserRuleContext();
+ ParserRuleContext(ParserRuleContext *parent, size_t invokingStateNumber);
+
+ /** COPY a ctx (I'm deliberately not using copy constructor) to avoid
+ * confusion with creating node with parent. Does not copy children
+ * (except error leaves).
+ */
+ virtual void copyFrom(ParserRuleContext *ctx);
+
+
+ // Double dispatch methods for listeners
+
+ virtual void enterRule(tree::ParseTreeListener *listener);
+ virtual void exitRule(tree::ParseTreeListener *listener);
+
+ /** Add a token leaf node child and force its parent to be this node. */
+ tree::TerminalNode* addChild(tree::TerminalNode *t);
+ RuleContext* addChild(RuleContext *ruleInvocation);
+
+ /// Used by enterOuterAlt to toss out a RuleContext previously added as
+ /// we entered a rule. If we have # label, we will need to remove
+ /// generic ruleContext object.
+ void removeLastChild();
+
+ tree::TerminalNode* getToken(size_t ttype, std::size_t i) const;
+
+ std::vector<tree::TerminalNode*> getTokens(size_t ttype) const;
+
+ template<typename T>
+ T* getRuleContext(size_t i) const {
+ static_assert(std::is_base_of_v<RuleContext, T>, "T must be derived from RuleContext");
+ size_t j = 0; // what element have we found with ctxType?
+ for (auto *child : children) {
+ if (RuleContext::is(child)) {
+ if (auto *typedChild = dynamic_cast<T*>(child); typedChild != nullptr) {
+ if (j++ == i) {
+ return typedChild;
+ }
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ template<typename T>
+ std::vector<T*> getRuleContexts() const {
+ static_assert(std::is_base_of_v<RuleContext, T>, "T must be derived from RuleContext");
+ std::vector<T*> contexts;
+ for (auto *child : children) {
+ if (RuleContext::is(child)) {
+ if (auto *typedChild = dynamic_cast<T*>(child); typedChild != nullptr) {
+ contexts.push_back(typedChild);
+ }
+ }
+ }
+ return contexts;
+ }
+
+ virtual misc::Interval getSourceInterval() override;
+
+ /**
+ * Get the initial token in this context.
+ * Note that the range from start to stop is inclusive, so for rules that do not consume anything
+ * (for example, zero length or error productions) this token may exceed stop.
+ */
+ Token* getStart() const;
+
+ /**
+ * Get the final token in this context.
+ * Note that the range from start to stop is inclusive, so for rules that do not consume anything
+ * (for example, zero length or error productions) this token may precede start.
+ */
+ Token* getStop() const;
+
+ /// <summary>
+ /// Used for rule context info debugging during parse-time, not so much for ATN debugging </summary>
+ virtual std::string toInfoString(Parser *recognizer);
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ProxyErrorListener.cpp b/contrib/libs/antlr4_cpp_runtime/src/ProxyErrorListener.cpp
new file mode 100644
index 0000000000..34bfd73e26
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ProxyErrorListener.cpp
@@ -0,0 +1,53 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "ProxyErrorListener.h"
+
+using namespace antlr4;
+
+void ProxyErrorListener::addErrorListener(ANTLRErrorListener *listener) {
+ if (listener == nullptr) {
+ throw "listener cannot be null.";
+ }
+
+ _delegates.insert(listener);
+}
+
+void ProxyErrorListener::removeErrorListener(ANTLRErrorListener *listener) {
+ _delegates.erase(listener);
+}
+
+void ProxyErrorListener::removeErrorListeners() {
+ _delegates.clear();
+}
+
+void ProxyErrorListener::syntaxError(Recognizer *recognizer, Token *offendingSymbol, size_t line,
+ size_t charPositionInLine, const std::string &msg, std::exception_ptr e) {
+
+ for (auto *listener : _delegates) {
+ listener->syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e);
+ }
+}
+
+void ProxyErrorListener::reportAmbiguity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ bool exact, const antlrcpp::BitSet &ambigAlts, atn::ATNConfigSet *configs) {
+ for (auto *listener : _delegates) {
+ listener->reportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs);
+ }
+}
+
+void ProxyErrorListener::reportAttemptingFullContext(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex,
+ size_t stopIndex, const antlrcpp::BitSet &conflictingAlts, atn::ATNConfigSet *configs) {
+ for (auto *listener : _delegates) {
+ listener->reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs);
+ }
+}
+
+void ProxyErrorListener::reportContextSensitivity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ size_t prediction, atn::ATNConfigSet *configs) {
+ for (auto *listener : _delegates) {
+ listener->reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs);
+ }
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/ProxyErrorListener.h b/contrib/libs/antlr4_cpp_runtime/src/ProxyErrorListener.h
new file mode 100644
index 0000000000..04630ce12c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/ProxyErrorListener.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "ANTLRErrorListener.h"
+#include "Exceptions.h"
+
+namespace antlr4 {
+
+ /// This implementation of ANTLRErrorListener dispatches all calls to a
+ /// collection of delegate listeners. This reduces the effort required to support multiple
+ /// listeners.
+ class ANTLR4CPP_PUBLIC ProxyErrorListener : public ANTLRErrorListener {
+ private:
+ std::set<ANTLRErrorListener *> _delegates; // Not owned.
+
+ public:
+ void addErrorListener(ANTLRErrorListener *listener);
+ void removeErrorListener(ANTLRErrorListener *listener);
+ void removeErrorListeners();
+
+ void syntaxError(Recognizer *recognizer, Token *offendingSymbol, size_t line, size_t charPositionInLine,
+ const std::string &msg, std::exception_ptr e) override;
+
+ virtual void reportAmbiguity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact,
+ const antlrcpp::BitSet &ambigAlts, atn::ATNConfigSet *configs) override;
+
+ virtual void reportAttemptingFullContext(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ const antlrcpp::BitSet &conflictingAlts, atn::ATNConfigSet *configs) override;
+
+ virtual void reportContextSensitivity(Parser *recognizer, const dfa::DFA &dfa, size_t startIndex, size_t stopIndex,
+ size_t prediction, atn::ATNConfigSet *configs) override;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/RecognitionException.cpp b/contrib/libs/antlr4_cpp_runtime/src/RecognitionException.cpp
new file mode 100644
index 0000000000..5b37f9d2f0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/RecognitionException.cpp
@@ -0,0 +1,65 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATN.h"
+#include "Recognizer.h"
+#include "ParserRuleContext.h"
+#include "misc/IntervalSet.h"
+
+#include "RecognitionException.h"
+
+using namespace antlr4;
+
+RecognitionException::RecognitionException(Recognizer *recognizer, IntStream *input, ParserRuleContext *ctx,
+ Token *offendingToken)
+ : RecognitionException("", recognizer, input, ctx, offendingToken) {
+}
+
+RecognitionException::RecognitionException(const std::string &message, Recognizer *recognizer, IntStream *input,
+ ParserRuleContext *ctx, Token *offendingToken)
+ : RuntimeException(message), _recognizer(recognizer), _input(input), _ctx(ctx), _offendingToken(offendingToken) {
+ InitializeInstanceFields();
+ if (recognizer != nullptr) {
+ _offendingState = recognizer->getState();
+ }
+}
+
+RecognitionException::~RecognitionException() {
+}
+
+size_t RecognitionException::getOffendingState() const {
+ return _offendingState;
+}
+
+void RecognitionException::setOffendingState(size_t offendingState) {
+ _offendingState = offendingState;
+}
+
+misc::IntervalSet RecognitionException::getExpectedTokens() const {
+ if (_recognizer) {
+ return _recognizer->getATN().getExpectedTokens(_offendingState, _ctx);
+ }
+ return misc::IntervalSet::EMPTY_SET;
+}
+
+RuleContext* RecognitionException::getCtx() const {
+ return _ctx;
+}
+
+IntStream* RecognitionException::getInputStream() const {
+ return _input;
+}
+
+Token* RecognitionException::getOffendingToken() const {
+ return _offendingToken;
+}
+
+Recognizer* RecognitionException::getRecognizer() const {
+ return _recognizer;
+}
+
+void RecognitionException::InitializeInstanceFields() {
+ _offendingState = INVALID_INDEX;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/RecognitionException.h b/contrib/libs/antlr4_cpp_runtime/src/RecognitionException.h
new file mode 100644
index 0000000000..9397ab20c8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/RecognitionException.h
@@ -0,0 +1,98 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Exceptions.h"
+
+namespace antlr4 {
+
+ /// The root of the ANTLR exception hierarchy. In general, ANTLR tracks just
+ /// 3 kinds of errors: prediction errors, failed predicate errors, and
+ /// mismatched input errors. In each case, the parser knows where it is
+ /// in the input, where it is in the ATN, the rule invocation stack,
+ /// and what kind of problem occurred.
+ class ANTLR4CPP_PUBLIC RecognitionException : public RuntimeException {
+ private:
+ /// The Recognizer where this exception originated.
+ Recognizer *_recognizer;
+ IntStream *_input;
+ ParserRuleContext *_ctx;
+
+ /// The current Token when an error occurred. Since not all streams
+ /// support accessing symbols by index, we have to track the Token
+ /// instance itself.
+ Token *_offendingToken;
+
+ size_t _offendingState;
+
+ public:
+ RecognitionException(Recognizer *recognizer, IntStream *input, ParserRuleContext *ctx,
+ Token *offendingToken = nullptr);
+ RecognitionException(const std::string &message, Recognizer *recognizer, IntStream *input,
+ ParserRuleContext *ctx, Token *offendingToken = nullptr);
+ RecognitionException(RecognitionException const&) = default;
+ ~RecognitionException();
+ RecognitionException& operator=(RecognitionException const&) = default;
+
+ /// Get the ATN state number the parser was in at the time the error
+ /// occurred. For NoViableAltException and
+ /// LexerNoViableAltException exceptions, this is the
+ /// DecisionState number. For others, it is the state whose outgoing
+ /// edge we couldn't match.
+ ///
+ /// If the state number is not known, this method returns -1.
+ virtual size_t getOffendingState() const;
+
+ protected:
+ void setOffendingState(size_t offendingState);
+
+ /// Gets the set of input symbols which could potentially follow the
+ /// previously matched symbol at the time this exception was thrown.
+ ///
+ /// If the set of expected tokens is not known and could not be computed,
+ /// this method returns an empty set.
+ ///
+ /// @returns The set of token types that could potentially follow the current
+ /// state in the ATN, or an empty set if the information is not available.
+ public:
+ virtual misc::IntervalSet getExpectedTokens() const;
+
+ /// <summary>
+ /// Gets the <seealso cref="RuleContext"/> at the time this exception was thrown.
+ /// <p/>
+ /// If the context is not available, this method returns {@code null}.
+ /// </summary>
+ /// <returns> The <seealso cref="RuleContext"/> at the time this exception was thrown.
+ /// If the context is not available, this method returns {@code null}. </returns>
+ virtual RuleContext* getCtx() const;
+
+ /// <summary>
+ /// Gets the input stream which is the symbol source for the recognizer where
+ /// this exception was thrown.
+ /// <p/>
+ /// If the input stream is not available, this method returns {@code null}.
+ /// </summary>
+ /// <returns> The input stream which is the symbol source for the recognizer
+ /// where this exception was thrown, or {@code null} if the stream is not
+ /// available. </returns>
+ virtual IntStream* getInputStream() const;
+
+ virtual Token* getOffendingToken() const;
+
+ /// <summary>
+ /// Gets the <seealso cref="Recognizer"/> where this exception occurred.
+ /// <p/>
+ /// If the recognizer is not available, this method returns {@code null}.
+ /// </summary>
+ /// <returns> The recognizer where this exception occurred, or {@code null} if
+ /// the recognizer is not available. </returns>
+ virtual Recognizer* getRecognizer() const;
+
+ private:
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Recognizer.cpp b/contrib/libs/antlr4_cpp_runtime/src/Recognizer.cpp
new file mode 100644
index 0000000000..c8a183324c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Recognizer.cpp
@@ -0,0 +1,157 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "ConsoleErrorListener.h"
+#include "RecognitionException.h"
+#include "support/CPPUtils.h"
+#include "Token.h"
+#include "atn/ATN.h"
+#include "atn/ATNSimulator.h"
+#include "support/CPPUtils.h"
+#include "support/StringUtils.h"
+
+#include "Vocabulary.h"
+
+#include "Recognizer.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::internal;
+
+std::map<const dfa::Vocabulary*, std::map<std::string_view, size_t>> Recognizer::_tokenTypeMapCache;
+std::map<std::vector<std::string>, std::map<std::string, size_t>> Recognizer::_ruleIndexMapCache;
+
+Recognizer::Recognizer() {
+ InitializeInstanceFields();
+ _proxListener.addErrorListener(&ConsoleErrorListener::INSTANCE);
+}
+
+Recognizer::~Recognizer() {
+}
+
+std::map<std::string_view, size_t> Recognizer::getTokenTypeMap() {
+ const dfa::Vocabulary& vocabulary = getVocabulary();
+
+ UniqueLock<Mutex> lck(_mutex);
+ std::map<std::string_view, size_t> result;
+ auto iterator = _tokenTypeMapCache.find(&vocabulary);
+ if (iterator != _tokenTypeMapCache.end()) {
+ result = iterator->second;
+ } else {
+ for (size_t i = 0; i <= getATN().maxTokenType; ++i) {
+ std::string_view literalName = vocabulary.getLiteralName(i);
+ if (!literalName.empty()) {
+ result[literalName] = i;
+ }
+
+ std::string_view symbolicName = vocabulary.getSymbolicName(i);
+ if (!symbolicName.empty()) {
+ result[symbolicName] = i;
+ }
+ }
+ result["EOF"] = EOF;
+ _tokenTypeMapCache[&vocabulary] = result;
+ }
+
+ return result;
+}
+
+std::map<std::string, size_t> Recognizer::getRuleIndexMap() {
+ const std::vector<std::string>& ruleNames = getRuleNames();
+ if (ruleNames.empty()) {
+ throw "The current recognizer does not provide a list of rule names.";
+ }
+
+ UniqueLock<Mutex> lck(_mutex);
+ std::map<std::string, size_t> result;
+ auto iterator = _ruleIndexMapCache.find(ruleNames);
+ if (iterator != _ruleIndexMapCache.end()) {
+ result = iterator->second;
+ } else {
+ result = antlrcpp::toMap(ruleNames);
+ _ruleIndexMapCache[ruleNames] = result;
+ }
+ return result;
+}
+
+size_t Recognizer::getTokenType(std::string_view tokenName) {
+ const std::map<std::string_view, size_t> &map = getTokenTypeMap();
+ auto iterator = map.find(tokenName);
+ if (iterator == map.end())
+ return Token::INVALID_TYPE;
+
+ return iterator->second;
+}
+
+void Recognizer::setInterpreter(atn::ATNSimulator *interpreter) {
+ // Usually the interpreter is set by the descendant (lexer or parser (simulator), but can also be exchanged
+ // by the profiling ATN simulator.
+ delete _interpreter;
+ _interpreter = interpreter;
+}
+
+std::string Recognizer::getErrorHeader(RecognitionException *e) {
+ // We're having issues with cross header dependencies, these two classes will need to be
+ // rewritten to remove that.
+ size_t line = e->getOffendingToken()->getLine();
+ size_t charPositionInLine = e->getOffendingToken()->getCharPositionInLine();
+ return std::string("line ") + std::to_string(line) + ":" + std::to_string(charPositionInLine);
+
+}
+
+std::string Recognizer::getTokenErrorDisplay(Token *t) {
+ if (t == nullptr) {
+ return "<no Token>";
+ }
+ std::string s = t->getText();
+ if (s == "") {
+ if (t->getType() == EOF) {
+ s = "<EOF>";
+ } else {
+ s = std::string("<") + std::to_string(t->getType()) + std::string(">");
+ }
+ }
+
+ std::string result;
+ result.reserve(s.size() + 2);
+ result.push_back('\'');
+ antlrcpp::escapeWhitespace(result, s);
+ result.push_back('\'');
+ result.shrink_to_fit();
+ return result;
+}
+
+void Recognizer::addErrorListener(ANTLRErrorListener *listener) {
+ _proxListener.addErrorListener(listener);
+}
+
+void Recognizer::removeErrorListener(ANTLRErrorListener *listener) {
+ _proxListener.removeErrorListener(listener);
+}
+
+void Recognizer::removeErrorListeners() {
+ _proxListener.removeErrorListeners();
+}
+
+ProxyErrorListener& Recognizer::getErrorListenerDispatch() {
+ return _proxListener;
+}
+
+bool Recognizer::sempred(RuleContext * /*localctx*/, size_t /*ruleIndex*/, size_t /*actionIndex*/) {
+ return true;
+}
+
+bool Recognizer::precpred(RuleContext * /*localctx*/, int /*precedence*/) {
+ return true;
+}
+
+void Recognizer::action(RuleContext * /*localctx*/, size_t /*ruleIndex*/, size_t /*actionIndex*/) {
+}
+
+void Recognizer::InitializeInstanceFields() {
+ _stateNumber = ATNState::INVALID_STATE_NUMBER;
+ _interpreter = nullptr;
+}
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Recognizer.h b/contrib/libs/antlr4_cpp_runtime/src/Recognizer.h
new file mode 100644
index 0000000000..0226a612e1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Recognizer.h
@@ -0,0 +1,160 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "ProxyErrorListener.h"
+#include "support/Casts.h"
+#include "atn/SerializedATNView.h"
+#include "internal/Synchronization.h"
+
+namespace antlr4 {
+
+ class ANTLR4CPP_PUBLIC Recognizer {
+ public:
+ static constexpr size_t EOF = std::numeric_limits<size_t>::max();
+
+ Recognizer();
+ Recognizer(Recognizer const&) = delete;
+ virtual ~Recognizer();
+
+ Recognizer& operator=(Recognizer const&) = delete;
+
+ virtual std::vector<std::string> const& getRuleNames() const = 0;
+
+ /**
+ * Get the vocabulary used by the recognizer.
+ *
+ * @return A {@link Vocabulary} instance providing information about the
+ * vocabulary used by the grammar.
+ */
+ virtual dfa::Vocabulary const& getVocabulary() const = 0;
+
+ /// <summary>
+ /// Get a map from token names to token types.
+ /// <p/>
+ /// Used for XPath and tree pattern compilation.
+ /// </summary>
+ virtual std::map<std::string_view, size_t> getTokenTypeMap();
+
+ /// <summary>
+ /// Get a map from rule names to rule indexes.
+ /// <p/>
+ /// Used for XPath and tree pattern compilation.
+ /// </summary>
+ virtual std::map<std::string, size_t> getRuleIndexMap();
+
+ virtual size_t getTokenType(std::string_view tokenName);
+
+ /// <summary>
+ /// If this recognizer was generated, it will have a serialized ATN
+ /// representation of the grammar.
+ /// <p/>
+ /// For interpreters, we don't know their serialized ATN despite having
+ /// created the interpreter from it.
+ /// </summary>
+ virtual atn::SerializedATNView getSerializedATN() const {
+ throw "there is no serialized ATN";
+ }
+
+ /// <summary>
+ /// For debugging and other purposes, might want the grammar name.
+ /// Have ANTLR generate an implementation for this method.
+ /// </summary>
+ virtual std::string getGrammarFileName() const = 0;
+
+ /// Get the ATN interpreter (in fact one of it's descendants) used by the recognizer for prediction.
+ /// @returns The ATN interpreter used by the recognizer for prediction.
+ template <class T>
+ T* getInterpreter() const {
+ return antlrcpp::downCast<T *>(_interpreter);
+ }
+
+ /**
+ * Set the ATN interpreter used by the recognizer for prediction.
+ *
+ * @param interpreter The ATN interpreter used by the recognizer for
+ * prediction.
+ */
+ void setInterpreter(atn::ATNSimulator *interpreter);
+
+ /// What is the error header, normally line/character position information?
+ virtual std::string getErrorHeader(RecognitionException *e);
+
+ /** How should a token be displayed in an error message? The default
+ * is to display just the text, but during development you might
+ * want to have a lot of information spit out. Override in that case
+ * to use t.toString() (which, for CommonToken, dumps everything about
+ * the token). This is better than forcing you to override a method in
+ * your token objects because you don't have to go modify your lexer
+ * so that it creates a new Java type.
+ *
+ * @deprecated This method is not called by the ANTLR 4 Runtime. Specific
+ * implementations of {@link ANTLRErrorStrategy} may provide a similar
+ * feature when necessary. For example, see
+ * {@link DefaultErrorStrategy#getTokenErrorDisplay}.
+ */
+ virtual std::string getTokenErrorDisplay(Token *t);
+
+ /// <exception cref="NullPointerException"> if {@code listener} is {@code null}. </exception>
+ virtual void addErrorListener(ANTLRErrorListener *listener);
+
+ virtual void removeErrorListener(ANTLRErrorListener *listener);
+
+ virtual void removeErrorListeners();
+
+ virtual ProxyErrorListener& getErrorListenerDispatch();
+
+ // subclass needs to override these if there are sempreds or actions
+ // that the ATN interp needs to execute
+ virtual bool sempred(RuleContext *localctx, size_t ruleIndex, size_t actionIndex);
+
+ virtual bool precpred(RuleContext *localctx, int precedence);
+
+ virtual void action(RuleContext *localctx, size_t ruleIndex, size_t actionIndex);
+
+ size_t getState() const { return _stateNumber; }
+
+ // Get the ATN used by the recognizer for prediction.
+ virtual const atn::ATN& getATN() const = 0;
+
+ /// <summary>
+ /// Indicate that the recognizer has changed internal state that is
+ /// consistent with the ATN state passed in. This way we always know
+ /// where we are in the ATN as the parser goes along. The rule
+ /// context objects form a stack that lets us see the stack of
+ /// invoking rules. Combine this and we have complete ATN
+ /// configuration information.
+ /// </summary>
+ void setState(size_t atnState) { _stateNumber = atnState; }
+
+ virtual IntStream* getInputStream() = 0;
+
+ virtual void setInputStream(IntStream *input) = 0;
+
+ virtual TokenFactory<CommonToken>* getTokenFactory() = 0;
+
+ template<typename T1>
+ void setTokenFactory(TokenFactory<T1> *input);
+
+ protected:
+ atn::ATNSimulator *_interpreter; // Set and deleted in descendants (or the profiler).
+
+ // Mutex to manage synchronized access for multithreading.
+ internal::Mutex _mutex;
+
+ private:
+ static std::map<const dfa::Vocabulary*, std::map<std::string_view, size_t>> _tokenTypeMapCache;
+ static std::map<std::vector<std::string>, std::map<std::string, size_t>> _ruleIndexMapCache;
+
+ ProxyErrorListener _proxListener; // Manages a collection of listeners.
+
+ size_t _stateNumber;
+
+ void InitializeInstanceFields();
+
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/RuleContext.cpp b/contrib/libs/antlr4_cpp_runtime/src/RuleContext.cpp
new file mode 100644
index 0000000000..6d67f9a29a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/RuleContext.cpp
@@ -0,0 +1,144 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/Trees.h"
+#include "misc/Interval.h"
+#include "Parser.h"
+#include "atn/ATN.h"
+#include "atn/ATNState.h"
+#include "tree/ParseTreeVisitor.h"
+
+#include "RuleContext.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::tree;
+
+RuleContext::RuleContext() : ParseTree(ParseTreeType::RULE) {
+ InitializeInstanceFields();
+}
+
+RuleContext::RuleContext(RuleContext *parent_, size_t invokingState_) : ParseTree(ParseTreeType::RULE) {
+ InitializeInstanceFields();
+ this->parent = parent_;
+ this->invokingState = invokingState_;
+}
+
+int RuleContext::depth() {
+ int n = 1;
+ RuleContext *p = this;
+ while (true) {
+ if (p->parent == nullptr)
+ break;
+ p = static_cast<RuleContext *>(p->parent);
+ n++;
+ }
+ return n;
+}
+
+bool RuleContext::isEmpty() {
+ return invokingState == ATNState::INVALID_STATE_NUMBER;
+}
+
+misc::Interval RuleContext::getSourceInterval() {
+ return misc::Interval::INVALID;
+}
+
+std::string RuleContext::getText() {
+ if (children.empty()) {
+ return "";
+ }
+
+ std::stringstream ss;
+ for (size_t i = 0; i < children.size(); i++) {
+ ParseTree *tree = children[i];
+ if (tree != nullptr)
+ ss << tree->getText();
+ }
+
+ return ss.str();
+}
+
+size_t RuleContext::getRuleIndex() const {
+ return INVALID_INDEX;
+}
+
+size_t RuleContext::getAltNumber() const {
+ return atn::ATN::INVALID_ALT_NUMBER;
+}
+
+void RuleContext::setAltNumber(size_t /*altNumber*/) {
+}
+
+std::any RuleContext::accept(tree::ParseTreeVisitor *visitor) {
+ return visitor->visitChildren(this);
+}
+
+std::string RuleContext::toStringTree(Parser *recog, bool pretty) {
+ return tree::Trees::toStringTree(this, recog, pretty);
+}
+
+std::string RuleContext::toStringTree(std::vector<std::string> &ruleNames, bool pretty) {
+ return tree::Trees::toStringTree(this, ruleNames, pretty);
+}
+
+std::string RuleContext::toStringTree(bool pretty) {
+ return toStringTree(nullptr, pretty);
+}
+
+
+std::string RuleContext::toString(const std::vector<std::string> &ruleNames) {
+ return toString(ruleNames, nullptr);
+}
+
+
+std::string RuleContext::toString(const std::vector<std::string> &ruleNames, RuleContext *stop) {
+ std::stringstream ss;
+
+ RuleContext *currentParent = this;
+ ss << "[";
+ while (currentParent != stop) {
+ if (ruleNames.empty()) {
+ if (!currentParent->isEmpty()) {
+ ss << currentParent->invokingState;
+ }
+ } else {
+ size_t ruleIndex = currentParent->getRuleIndex();
+
+ std::string ruleName = (ruleIndex < ruleNames.size()) ? ruleNames[ruleIndex] : std::to_string(ruleIndex);
+ ss << ruleName;
+ }
+
+ if (currentParent->parent == nullptr) // No parent anymore.
+ break;
+ currentParent = static_cast<RuleContext *>(currentParent->parent);
+ if (!ruleNames.empty() || !currentParent->isEmpty()) {
+ ss << " ";
+ }
+ }
+
+ ss << "]";
+
+ return ss.str();
+}
+
+std::string RuleContext::toString() {
+ return toString(nullptr);
+}
+
+std::string RuleContext::toString(Recognizer *recog) {
+ return toString(recog, &ParserRuleContext::EMPTY);
+}
+
+std::string RuleContext::toString(Recognizer *recog, RuleContext *stop) {
+ if (recog == nullptr)
+ return toString(std::vector<std::string>(), stop); // Don't use an initializer {} here or we end up calling ourselve recursivly.
+ return toString(recog->getRuleNames(), stop);
+}
+
+void RuleContext::InitializeInstanceFields() {
+ invokingState = INVALID_INDEX;
+}
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/RuleContext.h b/contrib/libs/antlr4_cpp_runtime/src/RuleContext.h
new file mode 100644
index 0000000000..a0effa2a02
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/RuleContext.h
@@ -0,0 +1,141 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "tree/ParseTree.h"
+
+namespace antlr4 {
+
+ /** A rule context is a record of a single rule invocation.
+ *
+ * We form a stack of these context objects using the parent
+ * pointer. A parent pointer of null indicates that the current
+ * context is the bottom of the stack. The ParserRuleContext subclass
+ * as a children list so that we can turn this data structure into a
+ * tree.
+ *
+ * The root node always has a null pointer and invokingState of -1.
+ *
+ * Upon entry to parsing, the first invoked rule function creates a
+ * context object (asubclass specialized for that rule such as
+ * SContext) and makes it the root of a parse tree, recorded by field
+ * Parser._ctx.
+ *
+ * public final SContext s() throws RecognitionException {
+ * SContext _localctx = new SContext(_ctx, getState()); <-- create new node
+ * enterRule(_localctx, 0, RULE_s); <-- push it
+ * ...
+ * exitRule(); <-- pop back to _localctx
+ * return _localctx;
+ * }
+ *
+ * A subsequent rule invocation of r from the start rule s pushes a
+ * new context object for r whose parent points at s and use invoking
+ * state is the state with r emanating as edge label.
+ *
+ * The invokingState fields from a context object to the root
+ * together form a stack of rule indication states where the root
+ * (bottom of the stack) has a -1 sentinel value. If we invoke start
+ * symbol s then call r1, which calls r2, the would look like
+ * this:
+ *
+ * SContext[-1] <- root node (bottom of the stack)
+ * R1Context[p] <- p in rule s called r1
+ * R2Context[q] <- q in rule r1 called r2
+ *
+ * So the top of the stack, _ctx, represents a call to the current
+ * rule and it holds the return address from another rule that invoke
+ * to this rule. To invoke a rule, we must always have a current context.
+ *
+ * The parent contexts are useful for computing lookahead sets and
+ * getting error information.
+ *
+ * These objects are used during parsing and prediction.
+ * For the special case of parsers, we use the subclass
+ * ParserRuleContext.
+ *
+ * @see ParserRuleContext
+ */
+ class ANTLR4CPP_PUBLIC RuleContext : public tree::ParseTree {
+ public:
+ static bool is(const tree::ParseTree &parseTree) { return parseTree.getTreeType() == tree::ParseTreeType::RULE; }
+
+ static bool is(const tree::ParseTree *parseTree) { return parseTree != nullptr && is(*parseTree); }
+
+ /// What state invoked the rule associated with this context?
+ /// The "return address" is the followState of invokingState
+ /// If parent is null, this should be -1 and this context object represents the start rule.
+ size_t invokingState;
+
+ RuleContext();
+ RuleContext(RuleContext *parent, size_t invokingState);
+
+ virtual int depth();
+
+ /// A context is empty if there is no invoking state; meaning nobody called current context.
+ virtual bool isEmpty();
+
+ // satisfy the ParseTree / SyntaxTree interface
+
+ virtual misc::Interval getSourceInterval() override;
+
+ virtual std::string getText() override;
+
+ virtual size_t getRuleIndex() const;
+
+ /** For rule associated with this parse tree internal node, return
+ * the outer alternative number used to match the input. Default
+ * implementation does not compute nor store this alt num. Create
+ * a subclass of ParserRuleContext with backing field and set
+ * option contextSuperClass.
+ * to set it.
+ *
+ * @since 4.5.3
+ */
+ virtual size_t getAltNumber() const;
+
+ /** Set the outer alternative number for this context node. Default
+ * implementation does nothing to avoid backing field overhead for
+ * trees that don't need it. Create
+ * a subclass of ParserRuleContext with backing field and set
+ * option contextSuperClass.
+ *
+ * @since 4.5.3
+ */
+ virtual void setAltNumber(size_t altNumber);
+
+ virtual std::any accept(tree::ParseTreeVisitor *visitor) override;
+
+ /// <summary>
+ /// Print out a whole tree, not just a node, in LISP format
+ /// (root child1 .. childN). Print just a node if this is a leaf.
+ /// We have to know the recognizer so we can get rule names.
+ /// </summary>
+ virtual std::string toStringTree(Parser *recog, bool pretty = false) override;
+
+ /// <summary>
+ /// Print out a whole tree, not just a node, in LISP format
+ /// (root child1 .. childN). Print just a node if this is a leaf.
+ /// </summary>
+ virtual std::string toStringTree(std::vector<std::string> &ruleNames, bool pretty = false);
+
+ virtual std::string toStringTree(bool pretty = false) override;
+ virtual std::string toString() override;
+ std::string toString(Recognizer *recog);
+ std::string toString(const std::vector<std::string> &ruleNames);
+
+ // recog null unless ParserRuleContext, in which case we use subclass toString(...)
+ std::string toString(Recognizer *recog, RuleContext *stop);
+
+ virtual std::string toString(const std::vector<std::string> &ruleNames, RuleContext *stop);
+
+ bool operator == (const RuleContext &other) { return this == &other; } // Simple address comparison.
+
+ private:
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/RuleContextWithAltNum.cpp b/contrib/libs/antlr4_cpp_runtime/src/RuleContextWithAltNum.cpp
new file mode 100644
index 0000000000..250859fdc0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/RuleContextWithAltNum.cpp
@@ -0,0 +1,27 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATN.h"
+
+#include "RuleContextWithAltNum.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+RuleContextWithAltNum::RuleContextWithAltNum() : ParserRuleContext() {
+ altNum = ATN::INVALID_ALT_NUMBER;
+}
+
+RuleContextWithAltNum::RuleContextWithAltNum(ParserRuleContext *parent, int invokingStateNumber)
+ : ParserRuleContext(parent, invokingStateNumber) {
+}
+
+size_t RuleContextWithAltNum::getAltNumber() const {
+ return altNum;
+}
+
+void RuleContextWithAltNum::setAltNumber(size_t number) {
+ altNum = number;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/RuleContextWithAltNum.h b/contrib/libs/antlr4_cpp_runtime/src/RuleContextWithAltNum.h
new file mode 100644
index 0000000000..995d9aa7b1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/RuleContextWithAltNum.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "ParserRuleContext.h"
+
+namespace antlr4 {
+
+ /// A handy class for use with
+ ///
+ /// options {contextSuperClass=org.antlr.v4.runtime.RuleContextWithAltNum;}
+ ///
+ /// that provides a backing field / impl for the outer alternative number
+ /// matched for an internal parse tree node.
+ ///
+ /// I'm only putting into Java runtime as I'm certain I'm the only one that
+ /// will really every use this.
+ class ANTLR4CPP_PUBLIC RuleContextWithAltNum : public ParserRuleContext {
+ public:
+ size_t altNum = 0;
+
+ RuleContextWithAltNum();
+ RuleContextWithAltNum(ParserRuleContext *parent, int invokingStateNumber);
+
+ virtual size_t getAltNumber() const override;
+ virtual void setAltNumber(size_t altNum) override;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/RuntimeMetaData.cpp b/contrib/libs/antlr4_cpp_runtime/src/RuntimeMetaData.cpp
new file mode 100644
index 0000000000..cf30d68587
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/RuntimeMetaData.cpp
@@ -0,0 +1,54 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "RuntimeMetaData.h"
+#include "Version.h"
+
+using namespace antlr4;
+
+const std::string RuntimeMetaData::VERSION = ANTLRCPP_VERSION_STRING;
+
+std::string RuntimeMetaData::getRuntimeVersion() {
+ return VERSION;
+}
+
+void RuntimeMetaData::checkVersion(const std::string &generatingToolVersion, const std::string &compileTimeVersion) {
+ std::string runtimeVersion = VERSION;
+ bool runtimeConflictsWithGeneratingTool = false;
+ bool runtimeConflictsWithCompileTimeTool = false;
+
+ if (generatingToolVersion != "") {
+ runtimeConflictsWithGeneratingTool = runtimeVersion != generatingToolVersion
+ && getMajorMinorVersion(runtimeVersion) != getMajorMinorVersion(generatingToolVersion);
+ }
+
+ runtimeConflictsWithCompileTimeTool = runtimeVersion != compileTimeVersion
+ && getMajorMinorVersion(runtimeVersion) != getMajorMinorVersion(compileTimeVersion);
+
+ if (runtimeConflictsWithGeneratingTool) {
+ std::cerr << "ANTLR Tool version " << generatingToolVersion << " used for code generation does not match "
+ "the current runtime version " << runtimeVersion << std::endl;
+ }
+ if (runtimeConflictsWithCompileTimeTool) {
+ std::cerr << "ANTLR Runtime version " << compileTimeVersion << " used for parser compilation does not match "
+ "the current runtime version " << runtimeVersion << std::endl;
+ }
+}
+
+std::string RuntimeMetaData::getMajorMinorVersion(const std::string &version) {
+ size_t firstDot = version.find('.');
+ size_t secondDot = firstDot != std::string::npos ? version.find('.', firstDot + 1) : std::string::npos;
+ size_t firstDash = version.find('-');
+ size_t referenceLength = version.size();
+ if (secondDot != std::string::npos) {
+ referenceLength = std::min(referenceLength, secondDot);
+ }
+
+ if (firstDash != std::string::npos) {
+ referenceLength = std::min(referenceLength, firstDash);
+ }
+
+ return version.substr(0, referenceLength);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/RuntimeMetaData.h b/contrib/libs/antlr4_cpp_runtime/src/RuntimeMetaData.h
new file mode 100644
index 0000000000..f178cfe9e8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/RuntimeMetaData.h
@@ -0,0 +1,155 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// This class provides access to the current version of the ANTLR 4 runtime
+ /// library as compile-time and runtime constants, along with methods for
+ /// checking for matching version numbers and notifying listeners in the case
+ /// where a version mismatch is detected.
+ ///
+ /// <para>
+ /// The runtime version information is provided by <seealso cref="#VERSION"/> and
+ /// <seealso cref="#getRuntimeVersion()"/>. Detailed information about these values is
+ /// provided in the documentation for each member.</para>
+ ///
+ /// <para>
+ /// The runtime version check is implemented by <seealso cref="#checkVersion"/>. Detailed
+ /// information about incorporating this call into user code, as well as its use
+ /// in generated code, is provided in the documentation for the method.</para>
+ ///
+ /// <para>
+ /// Version strings x.y and x.y.z are considered "compatible" and no error
+ /// would be generated. Likewise, version strings x.y-SNAPSHOT and x.y.z are
+ /// considered "compatible" because the major and minor components x.y
+ /// are the same in each.</para>
+ ///
+ /// <para>
+ /// To trap any error messages issued by this code, use System.setErr()
+ /// in your main() startup code.
+ /// </para>
+ ///
+ /// @since 4.3
+ /// </summary>
+ class ANTLR4CPP_PUBLIC RuntimeMetaData {
+ public:
+ /// A compile-time constant containing the current version of the ANTLR 4
+ /// runtime library.
+ ///
+ /// <para>
+ /// This compile-time constant value allows generated parsers and other
+ /// libraries to include a literal reference to the version of the ANTLR 4
+ /// runtime library the code was compiled against. At each release, we
+ /// change this value.</para>
+ ///
+ /// <para>Version numbers are assumed to have the form
+ ///
+ /// <em>major</em>.<em>minor</em>.<em>patch</em>.<em>revision</em>-<em>suffix</em>,
+ ///
+ /// with the individual components defined as follows.</para>
+ ///
+ /// <ul>
+ /// <li><em>major</em> is a required non-negative integer, and is equal to
+ /// {@code 4} for ANTLR 4.</li>
+ /// <li><em>minor</em> is a required non-negative integer.</li>
+ /// <li><em>patch</em> is an optional non-negative integer. When
+ /// <em>patch</em> is omitted, the {@code .} (dot) appearing before it is
+ /// also omitted.</li>
+ /// <li><em>revision</em> is an optional non-negative integer, and may only
+ /// be included when <em>patch</em> is also included. When <em>revision</em>
+ /// is omitted, the {@code .} (dot) appearing before it is also omitted.</li>
+ /// <li><em>suffix</em> is an optional string. When <em>suffix</em> is
+ /// omitted, the {@code -} (hyphen-minus) appearing before it is also
+ /// omitted.</li>
+ /// </ul>
+ static const std::string VERSION;
+
+ /// <summary>
+ /// Gets the currently executing version of the ANTLR 4 runtime library.
+ ///
+ /// <para>
+ /// This method provides runtime access to the <seealso cref="#VERSION"/> field, as
+ /// opposed to directly referencing the field as a compile-time constant.</para>
+ /// </summary>
+ /// <returns> The currently executing version of the ANTLR 4 library </returns>
+
+ static std::string getRuntimeVersion();
+
+ /// <summary>
+ /// This method provides the ability to detect mismatches between the version
+ /// of ANTLR 4 used to generate a parser, the version of the ANTLR runtime a
+ /// parser was compiled against, and the version of the ANTLR runtime which
+ /// is currently executing.
+ ///
+ /// <para>
+ /// The version check is designed to detect the following two specific
+ /// scenarios.</para>
+ ///
+ /// <ul>
+ /// <li>The ANTLR Tool version used for code generation does not match the
+ /// currently executing runtime version.</li>
+ /// <li>The ANTLR Runtime version referenced at the time a parser was
+ /// compiled does not match the currently executing runtime version.</li>
+ /// </ul>
+ ///
+ /// <para>
+ /// Starting with ANTLR 4.3, the code generator emits a call to this method
+ /// using two constants in each generated lexer and parser: a hard-coded
+ /// constant indicating the version of the tool used to generate the parser
+ /// and a reference to the compile-time constant <seealso cref="#VERSION"/>. At
+ /// runtime, this method is called during the initialization of the generated
+ /// parser to detect mismatched versions, and notify the registered listeners
+ /// prior to creating instances of the parser.</para>
+ ///
+ /// <para>
+ /// This method does not perform any detection or filtering of semantic
+ /// changes between tool and runtime versions. It simply checks for a
+ /// version match and emits an error to stderr if a difference
+ /// is detected.</para>
+ ///
+ /// <para>
+ /// Note that some breaking changes between releases could result in other
+ /// types of runtime exceptions, such as a <seealso cref="LinkageError"/>, prior to
+ /// calling this method. In these cases, the underlying version mismatch will
+ /// not be reported here. This method is primarily intended to
+ /// notify users of potential semantic changes between releases that do not
+ /// result in binary compatibility problems which would be detected by the
+ /// class loader. As with semantic changes, changes that break binary
+ /// compatibility between releases are mentioned in the release notes
+ /// accompanying the affected release.</para>
+ ///
+ /// <para>
+ /// <strong>Additional note for target developers:</strong> The version check
+ /// implemented by this class is designed to address specific compatibility
+ /// concerns that may arise during the execution of Java applications. Other
+ /// targets should consider the implementation of this method in the context
+ /// of that target's known execution environment, which may or may not
+ /// resemble the design provided for the Java target.</para>
+ /// </summary>
+ /// <param name="generatingToolVersion"> The version of the tool used to generate a parser.
+ /// This value may be null when called from user code that was not generated
+ /// by, and does not reference, the ANTLR 4 Tool itself. </param>
+ /// <param name="compileTimeVersion"> The version of the runtime the parser was
+ /// compiled against. This should always be passed using a direct reference
+ /// to <seealso cref="#VERSION"/>. </param>
+ static void checkVersion(const std::string &generatingToolVersion, const std::string &compileTimeVersion);
+
+ /// <summary>
+ /// Gets the major and minor version numbers from a version string. For
+ /// details about the syntax of the input {@code version}.
+ /// E.g., from x.y.z return x.y.
+ /// </summary>
+ /// <param name="version"> The complete version string. </param>
+ /// <returns> A string of the form <em>major</em>.<em>minor</em> containing
+ /// only the major and minor components of the version string. </returns>
+ static std::string getMajorMinorVersion(const std::string &version);
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Token.cpp b/contrib/libs/antlr4_cpp_runtime/src/Token.cpp
new file mode 100644
index 0000000000..31266b42d1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Token.cpp
@@ -0,0 +1,9 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Token.h"
+
+antlr4::Token::~Token() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Token.h b/contrib/libs/antlr4_cpp_runtime/src/Token.h
new file mode 100644
index 0000000000..832db740b3
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Token.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "IntStream.h"
+
+namespace antlr4 {
+
+ /// A token has properties: text, type, line, character position in the line
+ /// (so we can ignore tabs), token channel, index, and source from which
+ /// we obtained this token.
+ class ANTLR4CPP_PUBLIC Token {
+ public:
+ static constexpr size_t INVALID_TYPE = 0;
+
+ /// During lookahead operations, this "token" signifies we hit rule end ATN state
+ /// and did not follow it despite needing to.
+ static constexpr size_t EPSILON = std::numeric_limits<size_t>::max() - 1;
+ static constexpr size_t MIN_USER_TOKEN_TYPE = 1;
+ static constexpr size_t EOF = IntStream::EOF;
+
+ virtual ~Token();
+
+ /// All tokens go to the parser (unless skip() is called in that rule)
+ /// on a particular "channel". The parser tunes to a particular channel
+ /// so that whitespace etc... can go to the parser on a "hidden" channel.
+ static constexpr size_t DEFAULT_CHANNEL = 0;
+
+ /// Anything on different channel than DEFAULT_CHANNEL is not parsed
+ /// by parser.
+ static constexpr size_t HIDDEN_CHANNEL = 1;
+
+ /**
+ * This is the minimum constant value which can be assigned to a
+ * user-defined token channel.
+ *
+ * <p>
+ * The non-negative numbers less than {@link #MIN_USER_CHANNEL_VALUE} are
+ * assigned to the predefined channels {@link #DEFAULT_CHANNEL} and
+ * {@link #HIDDEN_CHANNEL}.</p>
+ *
+ * @see Token#getChannel()
+ */
+ static constexpr size_t MIN_USER_CHANNEL_VALUE = 2;
+
+ /// Get the text of the token.
+ virtual std::string getText() const = 0;
+
+ /// Get the token type of the token
+ virtual size_t getType() const = 0;
+
+ /// The line number on which the 1st character of this token was matched, line=1..n
+ virtual size_t getLine() const = 0;
+
+ /// The index of the first character of this token relative to the
+ /// beginning of the line at which it occurs, 0..n-1
+ virtual size_t getCharPositionInLine() const = 0;
+
+ /// Return the channel this token. Each token can arrive at the parser
+ /// on a different channel, but the parser only "tunes" to a single channel.
+ /// The parser ignores everything not on DEFAULT_CHANNEL.
+ virtual size_t getChannel() const = 0;
+
+ /// An index from 0..n-1 of the token object in the input stream.
+ /// This must be valid in order to print token streams and
+ /// use TokenRewriteStream.
+ ///
+ /// Return INVALID_INDEX to indicate that this token was conjured up since
+ /// it doesn't have a valid index.
+ virtual size_t getTokenIndex() const = 0;
+
+ /// The starting character index of the token
+ /// This method is optional; return INVALID_INDEX if not implemented.
+ virtual size_t getStartIndex() const = 0;
+
+ /// The last character index of the token.
+ /// This method is optional; return INVALID_INDEX if not implemented.
+ virtual size_t getStopIndex() const = 0;
+
+ /// Gets the <seealso cref="TokenSource"/> which created this token.
+ virtual TokenSource *getTokenSource() const = 0;
+
+ /// Gets the <seealso cref="CharStream"/> from which this token was derived.
+ virtual CharStream *getInputStream() const = 0;
+
+ virtual std::string toString() const = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/TokenFactory.h b/contrib/libs/antlr4_cpp_runtime/src/TokenFactory.h
new file mode 100644
index 0000000000..4eef044329
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/TokenFactory.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+
+ /// The default mechanism for creating tokens. It's used by default in Lexer and
+ /// the error handling strategy (to create missing tokens). Notifying the parser
+ /// of a new factory means that it notifies it's token source and error strategy.
+ template<typename Symbol>
+ class ANTLR4CPP_PUBLIC TokenFactory {
+ public:
+ virtual ~TokenFactory() {}
+
+ /// This is the method used to create tokens in the lexer and in the
+ /// error handling strategy. If text!=null, than the start and stop positions
+ /// are wiped to -1 in the text override is set in the CommonToken.
+ virtual std::unique_ptr<Symbol> create(std::pair<TokenSource *, CharStream *> source, size_t type, const std::string &text,
+ size_t channel, size_t start, size_t stop, size_t line, size_t charPositionInLine) = 0;
+
+ /// Generically useful
+ virtual std::unique_ptr<Symbol> create(size_t type, const std::string &text) = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/TokenSource.cpp b/contrib/libs/antlr4_cpp_runtime/src/TokenSource.cpp
new file mode 100644
index 0000000000..6b9d7af2f7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/TokenSource.cpp
@@ -0,0 +1,9 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "TokenSource.h"
+
+antlr4::TokenSource::~TokenSource() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/TokenSource.h b/contrib/libs/antlr4_cpp_runtime/src/TokenSource.h
new file mode 100644
index 0000000000..f05c27efac
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/TokenSource.h
@@ -0,0 +1,85 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "TokenFactory.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// A source of tokens must provide a sequence of tokens via <seealso cref="#nextToken()"/>
+ /// and also must reveal it's source of characters; <seealso cref="CommonToken"/>'s text is
+ /// computed from a <seealso cref="CharStream"/>; it only store indices into the char
+ /// stream.
+ /// <p/>
+ /// Errors from the lexer are never passed to the parser. Either you want to keep
+ /// going or you do not upon token recognition error. If you do not want to
+ /// continue lexing then you do not want to continue parsing. Just throw an
+ /// exception not under <seealso cref="RecognitionException"/> and Java will naturally toss
+ /// you all the way out of the recognizers. If you want to continue lexing then
+ /// you should not throw an exception to the parser--it has already requested a
+ /// token. Keep lexing until you get a valid one. Just report errors and keep
+ /// going, looking for a valid token.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC TokenSource {
+ public:
+ virtual ~TokenSource();
+
+ /// Return a <seealso cref="Token"/> object from your input stream (usually a
+ /// <seealso cref="CharStream"/>). Do not fail/return upon lexing error; keep chewing
+ /// on the characters until you get a good one; errors are not passed through
+ /// to the parser.
+ virtual std::unique_ptr<Token> nextToken() = 0;
+
+ /// <summary>
+ /// Get the line number for the current position in the input stream. The
+ /// first line in the input is line 1.
+ /// </summary>
+ /// <returns> The line number for the current position in the input stream, or
+ /// 0 if the current token source does not track line numbers. </returns>
+ virtual size_t getLine() const = 0;
+
+ /// <summary>
+ /// Get the index into the current line for the current position in the input
+ /// stream. The first character on a line has position 0.
+ /// </summary>
+ /// <returns> The line number for the current position in the input stream, or
+ /// (sze_t)-1 if the current token source does not track character positions. </returns>
+ virtual size_t getCharPositionInLine() = 0;
+
+ /// <summary>
+ /// Get the <seealso cref="CharStream"/> from which this token source is currently
+ /// providing tokens.
+ /// </summary>
+ /// <returns> The <seealso cref="CharStream"/> associated with the current position in
+ /// the input, or {@code null} if no input stream is available for the token
+ /// source. </returns>
+ virtual CharStream* getInputStream() = 0;
+
+ /// <summary>
+ /// Gets the name of the underlying input source. This method returns a
+ /// non-null, non-empty string. If such a name is not known, this method
+ /// returns <seealso cref="IntStream#UNKNOWN_SOURCE_NAME"/>.
+ /// </summary>
+ virtual std::string getSourceName() = 0;
+
+ /// <summary>
+ /// Set the <seealso cref="TokenFactory"/> this token source should use for creating
+ /// <seealso cref="Token"/> objects from the input.
+ /// </summary>
+ /// <param name="factory"> The <seealso cref="TokenFactory"/> to use for creating tokens. </param>
+ template<typename T1>
+ void setTokenFactory(TokenFactory<T1> * /*factory*/) {}
+
+ /// <summary>
+ /// Gets the <seealso cref="TokenFactory"/> this token source is currently using for
+ /// creating <seealso cref="Token"/> objects from the input.
+ /// </summary>
+ /// <returns> The <seealso cref="TokenFactory"/> currently used by this token source. </returns>
+ virtual TokenFactory<CommonToken>* getTokenFactory() = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/TokenStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/TokenStream.cpp
new file mode 100644
index 0000000000..fbb1ab788a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/TokenStream.cpp
@@ -0,0 +1,11 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "TokenStream.h"
+
+using namespace antlr4;
+
+TokenStream::~TokenStream() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/TokenStream.h b/contrib/libs/antlr4_cpp_runtime/src/TokenStream.h
new file mode 100644
index 0000000000..15b4f367a6
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/TokenStream.h
@@ -0,0 +1,137 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "IntStream.h"
+
+namespace antlr4 {
+
+ /// <summary>
+ /// An <seealso cref="IntStream"/> whose symbols are <seealso cref="Token"/> instances.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC TokenStream : public IntStream {
+ /// <summary>
+ /// Get the <seealso cref="Token"/> instance associated with the value returned by
+ /// <seealso cref="#LA LA(k)"/>. This method has the same pre- and post-conditions as
+ /// <seealso cref="IntStream#LA"/>. In addition, when the preconditions of this method
+ /// are met, the return value is non-null and the value of
+ /// {@code LT(k).getType()==LA(k)}.
+ /// </summary>
+ /// <seealso cref= IntStream#LA </seealso>
+ public:
+ virtual ~TokenStream();
+
+ virtual Token* LT(ssize_t k) = 0;
+
+ /// <summary>
+ /// Gets the <seealso cref="Token"/> at the specified {@code index} in the stream. When
+ /// the preconditions of this method are met, the return value is non-null.
+ /// <p/>
+ /// The preconditions for this method are the same as the preconditions of
+ /// <seealso cref="IntStream#seek"/>. If the behavior of {@code seek(index)} is
+ /// unspecified for the current state and given {@code index}, then the
+ /// behavior of this method is also unspecified.
+ /// <p/>
+ /// The symbol referred to by {@code index} differs from {@code seek()} only
+ /// in the case of filtering streams where {@code index} lies before the end
+ /// of the stream. Unlike {@code seek()}, this method does not adjust
+ /// {@code index} to point to a non-ignored symbol.
+ /// </summary>
+ /// <exception cref="IllegalArgumentException"> if {code index} is less than 0 </exception>
+ /// <exception cref="UnsupportedOperationException"> if the stream does not support
+ /// retrieving the token at the specified index </exception>
+ virtual Token* get(size_t index) const = 0;
+
+ /// Gets the underlying TokenSource which provides tokens for this stream.
+ virtual TokenSource* getTokenSource() const = 0;
+
+ /// <summary>
+ /// Return the text of all tokens within the specified {@code interval}. This
+ /// method behaves like the following code (including potential exceptions
+ /// for violating preconditions of <seealso cref="#get"/>, but may be optimized by the
+ /// specific implementation.
+ ///
+ /// <pre>
+ /// TokenStream stream = ...;
+ /// String text = "";
+ /// for (int i = interval.a; i <= interval.b; i++) {
+ /// text += stream.get(i).getText();
+ /// }
+ /// </pre>
+ /// </summary>
+ /// <param name="interval"> The interval of tokens within this stream to get text
+ /// for. </param>
+ /// <returns> The text of all tokens within the specified interval in this
+ /// stream.
+ /// </returns>
+ /// <exception cref="NullPointerException"> if {@code interval} is {@code null} </exception>
+ virtual std::string getText(const misc::Interval &interval) = 0;
+
+ /// <summary>
+ /// Return the text of all tokens in the stream. This method behaves like the
+ /// following code, including potential exceptions from the calls to
+ /// <seealso cref="IntStream#size"/> and <seealso cref="#getText(Interval)"/>, but may be
+ /// optimized by the specific implementation.
+ ///
+ /// <pre>
+ /// TokenStream stream = ...;
+ /// String text = stream.getText(new Interval(0, stream.size()));
+ /// </pre>
+ /// </summary>
+ /// <returns> The text of all tokens in the stream. </returns>
+ virtual std::string getText() = 0;
+
+ /// <summary>
+ /// Return the text of all tokens in the source interval of the specified
+ /// context. This method behaves like the following code, including potential
+ /// exceptions from the call to <seealso cref="#getText(Interval)"/>, but may be
+ /// optimized by the specific implementation.
+ /// </p>
+ /// If {@code ctx.getSourceInterval()} does not return a valid interval of
+ /// tokens provided by this stream, the behavior is unspecified.
+ ///
+ /// <pre>
+ /// TokenStream stream = ...;
+ /// String text = stream.getText(ctx.getSourceInterval());
+ /// </pre>
+ /// </summary>
+ /// <param name="ctx"> The context providing the source interval of tokens to get
+ /// text for. </param>
+ /// <returns> The text of all tokens within the source interval of {@code ctx}. </returns>
+ virtual std::string getText(RuleContext *ctx) = 0;
+
+ /// <summary>
+ /// Return the text of all tokens in this stream between {@code start} and
+ /// {@code stop} (inclusive).
+ /// <p/>
+ /// If the specified {@code start} or {@code stop} token was not provided by
+ /// this stream, or if the {@code stop} occurred before the {@code start}
+ /// token, the behavior is unspecified.
+ /// <p/>
+ /// For streams which ensure that the <seealso cref="Token#getTokenIndex"/> method is
+ /// accurate for all of its provided tokens, this method behaves like the
+ /// following code. Other streams may implement this method in other ways
+ /// provided the behavior is consistent with this at a high level.
+ ///
+ /// <pre>
+ /// TokenStream stream = ...;
+ /// String text = "";
+ /// for (int i = start.getTokenIndex(); i <= stop.getTokenIndex(); i++) {
+ /// text += stream.get(i).getText();
+ /// }
+ /// </pre>
+ /// </summary>
+ /// <param name="start"> The first token in the interval to get text for. </param>
+ /// <param name="stop"> The last token in the interval to get text for (inclusive). </param>
+ /// <returns> The text of all tokens lying between the specified {@code start}
+ /// and {@code stop} tokens.
+ /// </returns>
+ /// <exception cref="UnsupportedOperationException"> if this stream does not support
+ /// this method for the specified tokens </exception>
+ virtual std::string getText(Token *start, Token *stop) = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/TokenStreamRewriter.cpp b/contrib/libs/antlr4_cpp_runtime/src/TokenStreamRewriter.cpp
new file mode 100644
index 0000000000..9050eb5c91
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/TokenStreamRewriter.cpp
@@ -0,0 +1,425 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Exceptions.h"
+#include "misc/Interval.h"
+#include "Token.h"
+#include "TokenStream.h"
+
+#include "TokenStreamRewriter.h"
+
+using namespace antlr4;
+
+using antlr4::misc::Interval;
+
+TokenStreamRewriter::RewriteOperation::RewriteOperation(TokenStreamRewriter *outerInstance_, size_t index_)
+ : outerInstance(outerInstance_) {
+
+ InitializeInstanceFields();
+ this->index = index_;
+}
+
+TokenStreamRewriter::RewriteOperation::RewriteOperation(TokenStreamRewriter *outerInstance_, size_t index_,
+ const std::string& text_) : outerInstance(outerInstance_) {
+
+ InitializeInstanceFields();
+ this->index = index_;
+ this->text = text_;
+}
+
+TokenStreamRewriter::RewriteOperation::~RewriteOperation()
+{
+}
+
+size_t TokenStreamRewriter::RewriteOperation::execute(std::string * /*buf*/) {
+ return index;
+}
+
+std::string TokenStreamRewriter::RewriteOperation::toString() {
+ std::string opName = "TokenStreamRewriter";
+ size_t dollarIndex = opName.find('$');
+ opName = opName.substr(dollarIndex + 1, opName.length() - (dollarIndex + 1));
+ return "<" + opName + "@" + outerInstance->tokens->get(dollarIndex)->getText() + ":\"" + text + "\">";
+}
+
+void TokenStreamRewriter::RewriteOperation::InitializeInstanceFields() {
+ instructionIndex = 0;
+ index = 0;
+}
+
+TokenStreamRewriter::InsertBeforeOp::InsertBeforeOp(TokenStreamRewriter *outerInstance_, size_t index_, const std::string& text_)
+: RewriteOperation(outerInstance_, index_, text_), outerInstance(outerInstance_) {
+}
+
+size_t TokenStreamRewriter::InsertBeforeOp::execute(std::string *buf) {
+ buf->append(text);
+ if (outerInstance->tokens->get(index)->getType() != Token::EOF) {
+ buf->append(outerInstance->tokens->get(index)->getText());
+ }
+ return index + 1;
+}
+
+TokenStreamRewriter::ReplaceOp::ReplaceOp(TokenStreamRewriter *outerInstance_, size_t from, size_t to, const std::string& text)
+: RewriteOperation(outerInstance_, from, text), outerInstance(outerInstance_) {
+
+ InitializeInstanceFields();
+ lastIndex = to;
+}
+
+size_t TokenStreamRewriter::ReplaceOp::execute(std::string *buf) {
+ buf->append(text);
+ return lastIndex + 1;
+}
+
+std::string TokenStreamRewriter::ReplaceOp::toString() {
+ if (text.empty()) {
+ return "<DeleteOp@" + outerInstance->tokens->get(index)->getText() + ".." + outerInstance->tokens->get(lastIndex)->getText() + ">";
+ }
+ return "<ReplaceOp@" + outerInstance->tokens->get(index)->getText() + ".." + outerInstance->tokens->get(lastIndex)->getText() + ":\"" + text + "\">";
+}
+
+void TokenStreamRewriter::ReplaceOp::InitializeInstanceFields() {
+ lastIndex = 0;
+}
+
+//------------------ TokenStreamRewriter -------------------------------------------------------------------------------
+
+const std::string TokenStreamRewriter::DEFAULT_PROGRAM_NAME = "default";
+
+TokenStreamRewriter::TokenStreamRewriter(TokenStream *tokens_) : tokens(tokens_) {
+ _programs[DEFAULT_PROGRAM_NAME].reserve(PROGRAM_INIT_SIZE);
+}
+
+TokenStreamRewriter::~TokenStreamRewriter() {
+ for (const auto &program : _programs) {
+ for (auto *operation : program.second) {
+ delete operation;
+ }
+ }
+}
+
+TokenStream *TokenStreamRewriter::getTokenStream() {
+ return tokens;
+}
+
+void TokenStreamRewriter::rollback(size_t instructionIndex) {
+ rollback(DEFAULT_PROGRAM_NAME, instructionIndex);
+}
+
+void TokenStreamRewriter::rollback(const std::string &programName, size_t instructionIndex) {
+ std::vector<RewriteOperation*> is = _programs[programName];
+ if (is.size() > 0) {
+ _programs.insert({ programName, std::vector<RewriteOperation*>(is.begin() + MIN_TOKEN_INDEX, is.begin() + instructionIndex) });
+ }
+}
+
+void TokenStreamRewriter::deleteProgram() {
+ deleteProgram(DEFAULT_PROGRAM_NAME);
+}
+
+void TokenStreamRewriter::deleteProgram(const std::string &programName) {
+ rollback(programName, MIN_TOKEN_INDEX);
+}
+
+void TokenStreamRewriter::insertAfter(Token *t, const std::string& text) {
+ insertAfter(DEFAULT_PROGRAM_NAME, t, text);
+}
+
+void TokenStreamRewriter::insertAfter(size_t index, const std::string& text) {
+ insertAfter(DEFAULT_PROGRAM_NAME, index, text);
+}
+
+void TokenStreamRewriter::insertAfter(const std::string &programName, Token *t, const std::string& text) {
+ insertAfter(programName, t->getTokenIndex(), text);
+}
+
+void TokenStreamRewriter::insertAfter(const std::string &programName, size_t index, const std::string& text) {
+ // to insert after, just insert before next index (even if past end)
+ insertBefore(programName, index + 1, text);
+}
+
+void TokenStreamRewriter::insertBefore(Token *t, const std::string& text) {
+ insertBefore(DEFAULT_PROGRAM_NAME, t, text);
+}
+
+void TokenStreamRewriter::insertBefore(size_t index, const std::string& text) {
+ insertBefore(DEFAULT_PROGRAM_NAME, index, text);
+}
+
+void TokenStreamRewriter::insertBefore(const std::string &programName, Token *t, const std::string& text) {
+ insertBefore(programName, t->getTokenIndex(), text);
+}
+
+void TokenStreamRewriter::insertBefore(const std::string &programName, size_t index, const std::string& text) {
+ RewriteOperation *op = new InsertBeforeOp(this, index, text); /* mem-check: deleted in d-tor */
+ std::vector<RewriteOperation*> &rewrites = getProgram(programName);
+ op->instructionIndex = rewrites.size();
+ rewrites.push_back(op);
+}
+
+void TokenStreamRewriter::replace(size_t index, const std::string& text) {
+ replace(DEFAULT_PROGRAM_NAME, index, index, text);
+}
+
+void TokenStreamRewriter::replace(size_t from, size_t to, const std::string& text) {
+ replace(DEFAULT_PROGRAM_NAME, from, to, text);
+}
+
+void TokenStreamRewriter::replace(Token *indexT, const std::string& text) {
+ replace(DEFAULT_PROGRAM_NAME, indexT, indexT, text);
+}
+
+void TokenStreamRewriter::replace(Token *from, Token *to, const std::string& text) {
+ replace(DEFAULT_PROGRAM_NAME, from, to, text);
+}
+
+void TokenStreamRewriter::replace(const std::string &programName, size_t from, size_t to, const std::string& text) {
+ if (from > to || to >= tokens->size()) {
+ throw IllegalArgumentException("replace: range invalid: " + std::to_string(from) + ".." + std::to_string(to) +
+ "(size = " + std::to_string(tokens->size()) + ")");
+ }
+ RewriteOperation *op = new ReplaceOp(this, from, to, text); /* mem-check: deleted in d-tor */
+ std::vector<RewriteOperation*> &rewrites = getProgram(programName);
+ op->instructionIndex = rewrites.size();
+ rewrites.push_back(op);
+}
+
+void TokenStreamRewriter::replace(const std::string &programName, Token *from, Token *to, const std::string& text) {
+ replace(programName, from->getTokenIndex(), to->getTokenIndex(), text);
+}
+
+void TokenStreamRewriter::Delete(size_t index) {
+ Delete(DEFAULT_PROGRAM_NAME, index, index);
+}
+
+void TokenStreamRewriter::Delete(size_t from, size_t to) {
+ Delete(DEFAULT_PROGRAM_NAME, from, to);
+}
+
+void TokenStreamRewriter::Delete(Token *indexT) {
+ Delete(DEFAULT_PROGRAM_NAME, indexT, indexT);
+}
+
+void TokenStreamRewriter::Delete(Token *from, Token *to) {
+ Delete(DEFAULT_PROGRAM_NAME, from, to);
+}
+
+void TokenStreamRewriter::Delete(const std::string &programName, size_t from, size_t to) {
+ std::string nullString;
+ replace(programName, from, to, nullString);
+}
+
+void TokenStreamRewriter::Delete(const std::string &programName, Token *from, Token *to) {
+ std::string nullString;
+ replace(programName, from, to, nullString);
+}
+
+size_t TokenStreamRewriter::getLastRewriteTokenIndex() {
+ return getLastRewriteTokenIndex(DEFAULT_PROGRAM_NAME);
+}
+
+size_t TokenStreamRewriter::getLastRewriteTokenIndex(const std::string &programName) {
+ if (_lastRewriteTokenIndexes.find(programName) == _lastRewriteTokenIndexes.end()) {
+ return INVALID_INDEX;
+ }
+ return _lastRewriteTokenIndexes[programName];
+}
+
+void TokenStreamRewriter::setLastRewriteTokenIndex(const std::string &programName, size_t i) {
+ _lastRewriteTokenIndexes.insert({ programName, i });
+}
+
+std::vector<TokenStreamRewriter::RewriteOperation*>& TokenStreamRewriter::getProgram(const std::string &name) {
+ auto iterator = _programs.find(name);
+ if (iterator == _programs.end()) {
+ return initializeProgram(name);
+ }
+ return iterator->second;
+}
+
+std::vector<TokenStreamRewriter::RewriteOperation*>& TokenStreamRewriter::initializeProgram(const std::string &name) {
+ _programs[name].reserve(PROGRAM_INIT_SIZE);
+ return _programs[name];
+}
+
+std::string TokenStreamRewriter::getText() {
+ return getText(DEFAULT_PROGRAM_NAME, Interval(0UL, tokens->size() - 1));
+}
+
+std::string TokenStreamRewriter::getText(std::string programName) {
+ return getText(programName, Interval(0UL, tokens->size() - 1));
+}
+
+std::string TokenStreamRewriter::getText(const Interval &interval) {
+ return getText(DEFAULT_PROGRAM_NAME, interval);
+}
+
+std::string TokenStreamRewriter::getText(const std::string &programName, const Interval &interval) {
+ std::vector<TokenStreamRewriter::RewriteOperation*> &rewrites = _programs[programName];
+ size_t start = interval.a;
+ size_t stop = interval.b;
+
+ // ensure start/end are in range
+ if (stop > tokens->size() - 1) {
+ stop = tokens->size() - 1;
+ }
+ if (start == INVALID_INDEX) {
+ start = 0;
+ }
+
+ if (rewrites.empty() || rewrites.empty()) {
+ return tokens->getText(interval); // no instructions to execute
+ }
+ std::string buf;
+
+ // First, optimize instruction stream
+ std::unordered_map<size_t, TokenStreamRewriter::RewriteOperation*> indexToOp = reduceToSingleOperationPerIndex(rewrites);
+
+ // Walk buffer, executing instructions and emitting tokens
+ size_t i = start;
+ while (i <= stop && i < tokens->size()) {
+ RewriteOperation *op = indexToOp[i];
+ indexToOp.erase(i); // remove so any left have index size-1
+ Token *t = tokens->get(i);
+ if (op == nullptr) {
+ // no operation at that index, just dump token
+ if (t->getType() != Token::EOF) {
+ buf.append(t->getText());
+ }
+ i++; // move to next token
+ }
+ else {
+ i = op->execute(&buf); // execute operation and skip
+ }
+ }
+
+ // include stuff after end if it's last index in buffer
+ // So, if they did an insertAfter(lastValidIndex, "foo"), include
+ // foo if end==lastValidIndex.
+ if (stop == tokens->size() - 1) {
+ // Scan any remaining operations after last token
+ // should be included (they will be inserts).
+ for (auto op : indexToOp) {
+ if (op.second->index >= tokens->size() - 1) {
+ buf.append(op.second->text);
+ }
+ }
+ }
+ return buf;
+}
+
+std::unordered_map<size_t, TokenStreamRewriter::RewriteOperation*> TokenStreamRewriter::reduceToSingleOperationPerIndex(
+ std::vector<TokenStreamRewriter::RewriteOperation*> &rewrites) {
+
+
+ // WALK REPLACES
+ for (size_t i = 0; i < rewrites.size(); ++i) {
+ TokenStreamRewriter::RewriteOperation *op = rewrites[i];
+ ReplaceOp *rop = dynamic_cast<ReplaceOp *>(op);
+ if (rop == nullptr)
+ continue;
+
+ // Wipe prior inserts within range
+ std::vector<InsertBeforeOp *> inserts = getKindOfOps<InsertBeforeOp>(rewrites, i);
+ for (auto *iop : inserts) {
+ if (iop->index == rop->index) {
+ // E.g., insert before 2, delete 2..2; update replace
+ // text to include insert before, kill insert
+ delete rewrites[iop->instructionIndex];
+ rewrites[iop->instructionIndex] = nullptr;
+ rop->text = iop->text + (!rop->text.empty() ? rop->text : "");
+ }
+ else if (iop->index > rop->index && iop->index <= rop->lastIndex) {
+ // delete insert as it's a no-op.
+ delete rewrites[iop->instructionIndex];
+ rewrites[iop->instructionIndex] = nullptr;
+ }
+ }
+ // Drop any prior replaces contained within
+ std::vector<ReplaceOp*> prevReplaces = getKindOfOps<ReplaceOp>(rewrites, i);
+ for (auto *prevRop : prevReplaces) {
+ if (prevRop->index >= rop->index && prevRop->lastIndex <= rop->lastIndex) {
+ // delete replace as it's a no-op.
+ delete rewrites[prevRop->instructionIndex];
+ rewrites[prevRop->instructionIndex] = nullptr;
+ continue;
+ }
+ // throw exception unless disjoint or identical
+ bool disjoint = prevRop->lastIndex < rop->index || prevRop->index > rop->lastIndex;
+ // Delete special case of replace (text==null):
+ // D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right)
+ if (prevRop->text.empty() && rop->text.empty() && !disjoint) {
+ delete rewrites[prevRop->instructionIndex];
+ rewrites[prevRop->instructionIndex] = nullptr; // kill first delete
+ rop->index = std::min(prevRop->index, rop->index);
+ rop->lastIndex = std::max(prevRop->lastIndex, rop->lastIndex);
+ std::cout << "new rop " << rop << std::endl;
+ }
+ else if (!disjoint) {
+ throw IllegalArgumentException("replace op boundaries of " + rop->toString() +
+ " overlap with previous " + prevRop->toString());
+ }
+ }
+ }
+
+ // WALK INSERTS
+ for (size_t i = 0; i < rewrites.size(); i++) {
+ InsertBeforeOp *iop = dynamic_cast<InsertBeforeOp *>(rewrites[i]);
+ if (iop == nullptr)
+ continue;
+
+ // combine current insert with prior if any at same index
+
+ std::vector<InsertBeforeOp *> prevInserts = getKindOfOps<InsertBeforeOp>(rewrites, i);
+ for (auto *prevIop : prevInserts) {
+ if (prevIop->index == iop->index) { // combine objects
+ // convert to strings...we're in process of toString'ing
+ // whole token buffer so no lazy eval issue with any templates
+ iop->text = catOpText(&iop->text, &prevIop->text);
+ // delete redundant prior insert
+ delete rewrites[prevIop->instructionIndex];
+ rewrites[prevIop->instructionIndex] = nullptr;
+ }
+ }
+ // look for replaces where iop.index is in range; error
+ std::vector<ReplaceOp*> prevReplaces = getKindOfOps<ReplaceOp>(rewrites, i);
+ for (auto *rop : prevReplaces) {
+ if (iop->index == rop->index) {
+ rop->text = catOpText(&iop->text, &rop->text);
+ delete rewrites[i];
+ rewrites[i] = nullptr; // delete current insert
+ continue;
+ }
+ if (iop->index >= rop->index && iop->index <= rop->lastIndex) {
+ throw IllegalArgumentException("insert op " + iop->toString() + " within boundaries of previous " + rop->toString());
+ }
+ }
+ }
+
+ std::unordered_map<size_t, TokenStreamRewriter::RewriteOperation*> m;
+ for (TokenStreamRewriter::RewriteOperation *op : rewrites) {
+ if (op == nullptr) { // ignore deleted ops
+ continue;
+ }
+ if (m.count(op->index) > 0) {
+ throw RuntimeException("should only be one op per index");
+ }
+ m[op->index] = op;
+ }
+
+ return m;
+}
+
+std::string TokenStreamRewriter::catOpText(std::string *a, std::string *b) {
+ std::string x = "";
+ std::string y = "";
+ if (a != nullptr) {
+ x = *a;
+ }
+ if (b != nullptr) {
+ y = *b;
+ }
+ return x + y;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/TokenStreamRewriter.h b/contrib/libs/antlr4_cpp_runtime/src/TokenStreamRewriter.h
new file mode 100644
index 0000000000..929056a3f9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/TokenStreamRewriter.h
@@ -0,0 +1,295 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+
+ /**
+ * Useful for rewriting out a buffered input token stream after doing some
+ * augmentation or other manipulations on it.
+ *
+ * <p>
+ * You can insert stuff, replace, and delete chunks. Note that the operations
+ * are done lazily--only if you convert the buffer to a {@link String} with
+ * {@link TokenStream#getText()}. This is very efficient because you are not
+ * moving data around all the time. As the buffer of tokens is converted to
+ * strings, the {@link #getText()} method(s) scan the input token stream and
+ * check to see if there is an operation at the current index. If so, the
+ * operation is done and then normal {@link String} rendering continues on the
+ * buffer. This is like having multiple Turing machine instruction streams
+ * (programs) operating on a single input tape. :)</p>
+ *
+ * <p>
+ * This rewriter makes no modifications to the token stream. It does not ask the
+ * stream to fill itself up nor does it advance the input cursor. The token
+ * stream {@link TokenStream#index()} will return the same value before and
+ * after any {@link #getText()} call.</p>
+ *
+ * <p>
+ * The rewriter only works on tokens that you have in the buffer and ignores the
+ * current input cursor. If you are buffering tokens on-demand, calling
+ * {@link #getText()} halfway through the input will only do rewrites for those
+ * tokens in the first half of the file.</p>
+ *
+ * <p>
+ * Since the operations are done lazily at {@link #getText}-time, operations do
+ * not screw up the token index values. That is, an insert operation at token
+ * index {@code i} does not change the index values for tokens
+ * {@code i}+1..n-1.</p>
+ *
+ * <p>
+ * Because operations never actually alter the buffer, you may always get the
+ * original token stream back without undoing anything. Since the instructions
+ * are queued up, you can easily simulate transactions and roll back any changes
+ * if there is an error just by removing instructions. For example,</p>
+ *
+ * <pre>
+ * CharStream input = new ANTLRFileStream("input");
+ * TLexer lex = new TLexer(input);
+ * CommonTokenStream tokens = new CommonTokenStream(lex);
+ * T parser = new T(tokens);
+ * TokenStreamRewriter rewriter = new TokenStreamRewriter(tokens);
+ * parser.startRule();
+ * </pre>
+ *
+ * <p>
+ * Then in the rules, you can execute (assuming rewriter is visible):</p>
+ *
+ * <pre>
+ * Token t,u;
+ * ...
+ * rewriter.insertAfter(t, "text to put after t");}
+ * rewriter.insertAfter(u, "text after u");}
+ * System.out.println(rewriter.getText());
+ * </pre>
+ *
+ * <p>
+ * You can also have multiple "instruction streams" and get multiple rewrites
+ * from a single pass over the input. Just name the instruction streams and use
+ * that name again when printing the buffer. This could be useful for generating
+ * a C file and also its header file--all from the same buffer:</p>
+ *
+ * <pre>
+ * rewriter.insertAfter("pass1", t, "text to put after t");}
+ * rewriter.insertAfter("pass2", u, "text after u");}
+ * System.out.println(rewriter.getText("pass1"));
+ * System.out.println(rewriter.getText("pass2"));
+ * </pre>
+ *
+ * <p>
+ * If you don't use named rewrite streams, a "default" stream is used as the
+ * first example shows.</p>
+ */
+ class ANTLR4CPP_PUBLIC TokenStreamRewriter {
+ public:
+ static const std::string DEFAULT_PROGRAM_NAME;
+ static constexpr size_t PROGRAM_INIT_SIZE = 100;
+ static constexpr size_t MIN_TOKEN_INDEX = 0;
+
+ TokenStreamRewriter(TokenStream *tokens);
+ virtual ~TokenStreamRewriter();
+
+ TokenStream *getTokenStream();
+
+ virtual void rollback(size_t instructionIndex);
+
+ /// Rollback the instruction stream for a program so that
+ /// the indicated instruction (via instructionIndex) is no
+ /// longer in the stream. UNTESTED!
+ virtual void rollback(const std::string &programName, size_t instructionIndex);
+
+ virtual void deleteProgram();
+
+ /// Reset the program so that no instructions exist.
+ virtual void deleteProgram(const std::string &programName);
+ virtual void insertAfter(Token *t, const std::string& text);
+ virtual void insertAfter(size_t index, const std::string& text);
+ virtual void insertAfter(const std::string &programName, Token *t, const std::string& text);
+ virtual void insertAfter(const std::string &programName, size_t index, const std::string& text);
+
+ virtual void insertBefore(Token *t, const std::string& text);
+ virtual void insertBefore(size_t index, const std::string& text);
+ virtual void insertBefore(const std::string &programName, Token *t, const std::string& text);
+ virtual void insertBefore(const std::string &programName, size_t index, const std::string& text);
+
+ virtual void replace(size_t index, const std::string& text);
+ virtual void replace(size_t from, size_t to, const std::string& text);
+ virtual void replace(Token *indexT, const std::string& text);
+ virtual void replace(Token *from, Token *to, const std::string& text);
+ virtual void replace(const std::string &programName, size_t from, size_t to, const std::string& text);
+ virtual void replace(const std::string &programName, Token *from, Token *to, const std::string& text);
+
+ virtual void Delete(size_t index);
+ virtual void Delete(size_t from, size_t to);
+ virtual void Delete(Token *indexT);
+ virtual void Delete(Token *from, Token *to);
+ virtual void Delete(const std::string &programName, size_t from, size_t to);
+ virtual void Delete(const std::string &programName, Token *from, Token *to);
+
+ virtual size_t getLastRewriteTokenIndex();
+
+ /// Return the text from the original tokens altered per the
+ /// instructions given to this rewriter.
+ virtual std::string getText();
+
+ /** Return the text from the original tokens altered per the
+ * instructions given to this rewriter in programName.
+ */
+ std::string getText(std::string programName);
+
+ /// Return the text associated with the tokens in the interval from the
+ /// original token stream but with the alterations given to this rewriter.
+ /// The interval refers to the indexes in the original token stream.
+ /// We do not alter the token stream in any way, so the indexes
+ /// and intervals are still consistent. Includes any operations done
+ /// to the first and last token in the interval. So, if you did an
+ /// insertBefore on the first token, you would get that insertion.
+ /// The same is true if you do an insertAfter the stop token.
+ virtual std::string getText(const misc::Interval &interval);
+
+ virtual std::string getText(const std::string &programName, const misc::Interval &interval);
+
+ protected:
+ class RewriteOperation {
+ public:
+ /// What index into rewrites List are we?
+ size_t index;
+ std::string text;
+
+ /// Token buffer index.
+ size_t instructionIndex;
+
+ RewriteOperation(TokenStreamRewriter *outerInstance, size_t index);
+ RewriteOperation(TokenStreamRewriter *outerInstance, size_t index, const std::string& text);
+ virtual ~RewriteOperation();
+
+ /// Execute the rewrite operation by possibly adding to the buffer.
+ /// Return the index of the next token to operate on.
+
+ virtual size_t execute(std::string *buf);
+ virtual std::string toString();
+
+ private:
+ TokenStreamRewriter *const outerInstance;
+ void InitializeInstanceFields();
+ };
+
+ class InsertBeforeOp : public RewriteOperation {
+ private:
+ TokenStreamRewriter *const outerInstance;
+
+ public:
+ InsertBeforeOp(TokenStreamRewriter *outerInstance, size_t index, const std::string& text);
+
+ virtual size_t execute(std::string *buf) override;
+ };
+
+ class ReplaceOp : public RewriteOperation {
+ private:
+ TokenStreamRewriter *const outerInstance;
+
+ public:
+ size_t lastIndex;
+
+ ReplaceOp(TokenStreamRewriter *outerInstance, size_t from, size_t to, const std::string& text);
+ virtual size_t execute(std::string *buf) override;
+ virtual std::string toString() override;
+
+ private:
+ void InitializeInstanceFields();
+ };
+
+ /// Our source stream
+ TokenStream *const tokens;
+
+ /// You may have multiple, named streams of rewrite operations.
+ /// I'm calling these things "programs."
+ /// Maps String (name) -> rewrite (List)
+ std::map<std::string, std::vector<RewriteOperation*>> _programs;
+
+ /// <summary>
+ /// Map String (program name) -> Integer index </summary>
+ std::map<std::string, size_t> _lastRewriteTokenIndexes;
+ virtual size_t getLastRewriteTokenIndex(const std::string &programName);
+ virtual void setLastRewriteTokenIndex(const std::string &programName, size_t i);
+ virtual std::vector<RewriteOperation*>& getProgram(const std::string &name);
+
+ /// <summary>
+ /// We need to combine operations and report invalid operations (like
+ /// overlapping replaces that are not completed nested). Inserts to
+ /// same index need to be combined etc... Here are the cases:
+ ///
+ /// I.i.u I.j.v leave alone, nonoverlapping
+ /// I.i.u I.i.v combine: Iivu
+ ///
+ /// R.i-j.u R.x-y.v | i-j in x-y delete first R
+ /// R.i-j.u R.i-j.v delete first R
+ /// R.i-j.u R.x-y.v | x-y in i-j ERROR
+ /// R.i-j.u R.x-y.v | boundaries overlap ERROR
+ ///
+ /// Delete special case of replace (text==null):
+ /// D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right)
+ ///
+ /// I.i.u R.x-y.v | i in (x+1)-y delete I (since insert before
+ /// we're not deleting i)
+ /// I.i.u R.x-y.v | i not in (x+1)-y leave alone, nonoverlapping
+ /// R.x-y.v I.i.u | i in x-y ERROR
+ /// R.x-y.v I.x.u R.x-y.uv (combine, delete I)
+ /// R.x-y.v I.i.u | i not in x-y leave alone, nonoverlapping
+ ///
+ /// I.i.u = insert u before op @ index i
+ /// R.x-y.u = replace x-y indexed tokens with u
+ ///
+ /// First we need to examine replaces. For any replace op:
+ ///
+ /// 1. wipe out any insertions before op within that range.
+ /// 2. Drop any replace op before that is contained completely within
+ /// that range.
+ /// 3. Throw exception upon boundary overlap with any previous replace.
+ ///
+ /// Then we can deal with inserts:
+ ///
+ /// 1. for any inserts to same index, combine even if not adjacent.
+ /// 2. for any prior replace with same left boundary, combine this
+ /// insert with replace and delete this replace.
+ /// 3. throw exception if index in same range as previous replace
+ ///
+ /// Don't actually delete; make op null in list. Easier to walk list.
+ /// Later we can throw as we add to index -> op map.
+ ///
+ /// Note that I.2 R.2-2 will wipe out I.2 even though, technically, the
+ /// inserted stuff would be before the replace range. But, if you
+ /// add tokens in front of a method body '{' and then delete the method
+ /// body, I think the stuff before the '{' you added should disappear too.
+ ///
+ /// Return a map from token index to operation.
+ /// </summary>
+ virtual std::unordered_map<size_t, RewriteOperation*> reduceToSingleOperationPerIndex(std::vector<RewriteOperation*> &rewrites);
+
+ virtual std::string catOpText(std::string *a, std::string *b);
+
+ /// Get all operations before an index of a particular kind.
+ template <typename T>
+ std::vector<T *> getKindOfOps(std::vector<RewriteOperation *> rewrites, size_t before) {
+ std::vector<T *> ops;
+ for (size_t i = 0; i < before && i < rewrites.size(); i++) {
+ T *op = dynamic_cast<T *>(rewrites[i]);
+ if (op == nullptr) { // ignore deleted or non matching entries
+ continue;
+ }
+ ops.push_back(op);
+ }
+ return ops;
+ }
+
+ private:
+ std::vector<RewriteOperation *>& initializeProgram(const std::string &name);
+
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/UnbufferedCharStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/UnbufferedCharStream.cpp
new file mode 100644
index 0000000000..bbfb8848fd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/UnbufferedCharStream.cpp
@@ -0,0 +1,208 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/Interval.h"
+#include "Exceptions.h"
+#include "support/Utf8.h"
+
+#include "UnbufferedCharStream.h"
+
+using namespace antlrcpp;
+using namespace antlr4;
+using namespace antlr4::misc;
+
+UnbufferedCharStream::UnbufferedCharStream(std::wistream &input)
+ : _p(0), _numMarkers(0), _lastChar(0), _lastCharBufferStart(0), _currentCharIndex(0), _input(input) {
+ // The vector's size is what used to be n in Java code.
+ fill(1); // prime
+}
+
+void UnbufferedCharStream::consume() {
+ if (LA(1) == EOF) {
+ throw IllegalStateException("cannot consume EOF");
+ }
+
+ // buf always has at least data[p==0] in this method due to ctor
+ _lastChar = _data[_p]; // track last char for LA(-1)
+
+ if (_p == _data.size() - 1 && _numMarkers == 0) {
+ size_t capacity = _data.capacity();
+ _data.clear();
+ _data.reserve(capacity);
+
+ _p = 0;
+ _lastCharBufferStart = _lastChar;
+ } else {
+ _p++;
+ }
+
+ _currentCharIndex++;
+ sync(1);
+}
+
+void UnbufferedCharStream::sync(size_t want) {
+ if (_p + want <= _data.size()) // Already enough data loaded?
+ return;
+
+ fill(_p + want - _data.size());
+}
+
+size_t UnbufferedCharStream::fill(size_t n) {
+ for (size_t i = 0; i < n; i++) {
+ if (_data.size() > 0 && _data.back() == 0xFFFF) {
+ return i;
+ }
+
+ try {
+ char32_t c = nextChar();
+ add(c);
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023026
+ } catch (IOException &ioe) {
+ // throw_with_nested is not available before VS 2015.
+ throw ioe;
+#else
+ } catch (IOException & /*ioe*/) {
+ std::throw_with_nested(RuntimeException());
+#endif
+ }
+ }
+
+ return n;
+}
+
+char32_t UnbufferedCharStream::nextChar() {
+ return _input.get();
+}
+
+void UnbufferedCharStream::add(char32_t c) {
+ _data += c;
+}
+
+size_t UnbufferedCharStream::LA(ssize_t i) {
+ if (i == -1) { // special case
+ return _lastChar;
+ }
+
+ // We can look back only as many chars as we have buffered.
+ ssize_t index = static_cast<ssize_t>(_p) + i - 1;
+ if (index < 0) {
+ throw IndexOutOfBoundsException();
+ }
+
+ if (i > 0) {
+ sync(static_cast<size_t>(i)); // No need to sync if we look back.
+ }
+ if (static_cast<size_t>(index) >= _data.size()) {
+ return EOF;
+ }
+
+ if (_data[static_cast<size_t>(index)] == std::char_traits<wchar_t>::eof()) {
+ return EOF;
+ }
+
+ return _data[static_cast<size_t>(index)];
+}
+
+ssize_t UnbufferedCharStream::mark() {
+ if (_numMarkers == 0) {
+ _lastCharBufferStart = _lastChar;
+ }
+
+ ssize_t mark = -static_cast<ssize_t>(_numMarkers) - 1;
+ _numMarkers++;
+ return mark;
+}
+
+void UnbufferedCharStream::release(ssize_t marker) {
+ ssize_t expectedMark = -static_cast<ssize_t>(_numMarkers);
+ if (marker != expectedMark) {
+ throw IllegalStateException("release() called with an invalid marker.");
+ }
+
+ _numMarkers--;
+ if (_numMarkers == 0 && _p > 0) {
+ _data.erase(0, _p);
+ _p = 0;
+ _lastCharBufferStart = _lastChar;
+ }
+}
+
+size_t UnbufferedCharStream::index() {
+ return _currentCharIndex;
+}
+
+void UnbufferedCharStream::seek(size_t index) {
+ if (index == _currentCharIndex) {
+ return;
+ }
+
+ if (index > _currentCharIndex) {
+ sync(index - _currentCharIndex);
+ index = std::min(index, getBufferStartIndex() + _data.size() - 1);
+ }
+
+ // index == to bufferStartIndex should set p to 0
+ ssize_t i = static_cast<ssize_t>(index) - static_cast<ssize_t>(getBufferStartIndex());
+ if (i < 0) {
+ throw IllegalArgumentException(std::string("cannot seek to negative index ") + std::to_string(index));
+ } else if (i >= static_cast<ssize_t>(_data.size())) {
+ throw UnsupportedOperationException("Seek to index outside buffer: " + std::to_string(index) +
+ " not in " + std::to_string(getBufferStartIndex()) + ".." +
+ std::to_string(getBufferStartIndex() + _data.size()));
+ }
+
+ _p = static_cast<size_t>(i);
+ _currentCharIndex = index;
+ if (_p == 0) {
+ _lastChar = _lastCharBufferStart;
+ } else {
+ _lastChar = _data[_p - 1];
+ }
+}
+
+size_t UnbufferedCharStream::size() {
+ throw UnsupportedOperationException("Unbuffered stream cannot know its size");
+}
+
+std::string UnbufferedCharStream::getSourceName() const {
+ if (name.empty()) {
+ return UNKNOWN_SOURCE_NAME;
+ }
+
+ return name;
+}
+
+std::string UnbufferedCharStream::getText(const misc::Interval &interval) {
+ if (interval.a < 0 || interval.b < interval.a - 1) {
+ throw IllegalArgumentException("invalid interval");
+ }
+
+ size_t bufferStartIndex = getBufferStartIndex();
+ if (!_data.empty() && _data.back() == 0xFFFF) {
+ if (interval.a + interval.length() > bufferStartIndex + _data.size()) {
+ throw IllegalArgumentException("the interval extends past the end of the stream");
+ }
+ }
+
+ if (interval.a < static_cast<ssize_t>(bufferStartIndex) || interval.b >= ssize_t(bufferStartIndex + _data.size())) {
+ throw UnsupportedOperationException("interval " + interval.toString() + " outside buffer: " +
+ std::to_string(bufferStartIndex) + ".." + std::to_string(bufferStartIndex + _data.size() - 1));
+ }
+ // convert from absolute to local index
+ size_t i = interval.a - bufferStartIndex;
+ auto maybeUtf8 = Utf8::strictEncode(std::u32string_view(_data).substr(i, interval.length()));
+ if (!maybeUtf8.has_value()) {
+ throw IllegalArgumentException("Unbuffered stream contains invalid Unicode code points");
+ }
+ return std::move(maybeUtf8).value();
+}
+
+std::string UnbufferedCharStream::toString() const {
+ throw UnsupportedOperationException("Unbuffered stream cannot be materialized to a string");
+}
+
+size_t UnbufferedCharStream::getBufferStartIndex() const {
+ return _currentCharIndex - _p;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/UnbufferedCharStream.h b/contrib/libs/antlr4_cpp_runtime/src/UnbufferedCharStream.h
new file mode 100644
index 0000000000..5b05834f85
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/UnbufferedCharStream.h
@@ -0,0 +1,117 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "CharStream.h"
+
+namespace antlr4 {
+
+ /// Do not buffer up the entire char stream. It does keep a small buffer
+ /// for efficiency and also buffers while a mark exists (set by the
+ /// lookahead prediction in parser). "Unbuffered" here refers to fact
+ /// that it doesn't buffer all data, not that's it's on demand loading of char.
+ class ANTLR4CPP_PUBLIC UnbufferedCharStream : public CharStream {
+ public:
+ /// The name or source of this char stream.
+ std::string name;
+
+ explicit UnbufferedCharStream(std::wistream &input);
+
+ void consume() override;
+ size_t LA(ssize_t i) override;
+
+ /// <summary>
+ /// Return a marker that we can release later.
+ /// <p/>
+ /// The specific marker value used for this class allows for some level of
+ /// protection against misuse where {@code seek()} is called on a mark or
+ /// {@code release()} is called in the wrong order.
+ /// </summary>
+ ssize_t mark() override;
+
+ /// <summary>
+ /// Decrement number of markers, resetting buffer if we hit 0. </summary>
+ /// <param name="marker"> </param>
+ void release(ssize_t marker) override;
+ size_t index() override;
+
+ /// <summary>
+ /// Seek to absolute character index, which might not be in the current
+ /// sliding window. Move {@code p} to {@code index-bufferStartIndex}.
+ /// </summary>
+ void seek(size_t index) override;
+ size_t size() override;
+ std::string getSourceName() const override;
+ std::string getText(const misc::Interval &interval) override;
+
+ std::string toString() const override;
+
+ protected:
+ /// A moving window buffer of the data being scanned. While there's a marker,
+ /// we keep adding to buffer. Otherwise, <seealso cref="#consume consume()"/> resets so
+ /// we start filling at index 0 again.
+ // UTF-32 encoded.
+ std::u32string _data;
+ typedef char32_t storage_type;
+
+ /// <summary>
+ /// 0..n-1 index into <seealso cref="#data data"/> of next character.
+ /// <p/>
+ /// The {@code LA(1)} character is {@code data[p]}. If {@code p == n}, we are
+ /// out of buffered characters.
+ /// </summary>
+ size_t _p;
+
+ /// <summary>
+ /// Count up with <seealso cref="#mark mark()"/> and down with
+ /// <seealso cref="#release release()"/>. When we {@code release()} the last mark,
+ /// {@code numMarkers} reaches 0 and we reset the buffer. Copy
+ /// {@code data[p]..data[n-1]} to {@code data[0]..data[(n-1)-p]}.
+ /// </summary>
+ size_t _numMarkers;
+
+ /// This is the {@code LA(-1)} character for the current position.
+ size_t _lastChar; // UTF-32
+
+ /// <summary>
+ /// When {@code numMarkers > 0}, this is the {@code LA(-1)} character for the
+ /// first character in <seealso cref="#data data"/>. Otherwise, this is unspecified.
+ /// </summary>
+ size_t _lastCharBufferStart; // UTF-32
+
+ /// <summary>
+ /// Absolute character index. It's the index of the character about to be
+ /// read via {@code LA(1)}. Goes from 0 to the number of characters in the
+ /// entire stream, although the stream size is unknown before the end is
+ /// reached.
+ /// </summary>
+ size_t _currentCharIndex;
+
+ std::wistream &_input;
+
+ /// <summary>
+ /// Make sure we have 'want' elements from current position <seealso cref="#p p"/>.
+ /// Last valid {@code p} index is {@code data.length-1}. {@code p+need-1} is
+ /// the char index 'need' elements ahead. If we need 1 element,
+ /// {@code (p+1-1)==p} must be less than {@code data.length}.
+ /// </summary>
+ virtual void sync(size_t want);
+
+ /// <summary>
+ /// Add {@code n} characters to the buffer. Returns the number of characters
+ /// actually added to the buffer. If the return value is less than {@code n},
+ /// then EOF was reached before {@code n} characters could be added.
+ /// </summary>
+ virtual size_t fill(size_t n);
+
+ /// Override to provide different source of characters than
+ /// <seealso cref="#input input"/>.
+ virtual char32_t nextChar();
+ virtual void add(char32_t c);
+ size_t getBufferStartIndex() const;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/UnbufferedTokenStream.cpp b/contrib/libs/antlr4_cpp_runtime/src/UnbufferedTokenStream.cpp
new file mode 100644
index 0000000000..16ff49e332
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/UnbufferedTokenStream.cpp
@@ -0,0 +1,270 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Token.h"
+#include "Exceptions.h"
+#include "assert.h"
+#include "TokenSource.h"
+#include "support/Arrays.h"
+#include "misc/Interval.h"
+#include "RuleContext.h"
+#include "WritableToken.h"
+
+#include "UnbufferedTokenStream.h"
+
+using namespace antlr4;
+
+UnbufferedTokenStream::UnbufferedTokenStream(TokenSource *tokenSource) : UnbufferedTokenStream(tokenSource, 256) {
+}
+
+UnbufferedTokenStream::UnbufferedTokenStream(TokenSource *tokenSource, int /*bufferSize*/)
+ : _tokenSource(tokenSource), _lastToken(nullptr), _lastTokenBufferStart(nullptr)
+{
+ InitializeInstanceFields();
+ fill(1); // prime the pump
+}
+
+UnbufferedTokenStream::~UnbufferedTokenStream() {
+}
+
+Token* UnbufferedTokenStream::get(size_t i) const
+{ // get absolute index
+ size_t bufferStartIndex = getBufferStartIndex();
+ if (i < bufferStartIndex || i >= bufferStartIndex + _tokens.size()) {
+ throw IndexOutOfBoundsException(std::string("get(") + std::to_string(i) + std::string(") outside buffer: ")
+ + std::to_string(bufferStartIndex) + std::string("..") + std::to_string(bufferStartIndex + _tokens.size()));
+ }
+ return _tokens[i - bufferStartIndex].get();
+}
+
+Token* UnbufferedTokenStream::LT(ssize_t i)
+{
+ if (i == -1) {
+ return _lastToken;
+ }
+
+ sync(i);
+ ssize_t index = static_cast<ssize_t>(_p) + i - 1;
+ if (index < 0) {
+ throw IndexOutOfBoundsException(std::string("LT(") + std::to_string(i) + std::string(") gives negative index"));
+ }
+
+ if (index >= static_cast<ssize_t>(_tokens.size())) {
+ assert(_tokens.size() > 0 && _tokens.back()->getType() == EOF);
+ return _tokens.back().get();
+ }
+
+ return _tokens[static_cast<size_t>(index)].get();
+}
+
+size_t UnbufferedTokenStream::LA(ssize_t i)
+{
+ return LT(i)->getType();
+}
+
+TokenSource* UnbufferedTokenStream::getTokenSource() const
+{
+ return _tokenSource;
+}
+
+std::string UnbufferedTokenStream::getText()
+{
+ return "";
+}
+
+std::string UnbufferedTokenStream::getText(RuleContext* ctx)
+{
+ return getText(ctx->getSourceInterval());
+}
+
+std::string UnbufferedTokenStream::getText(Token *start, Token *stop)
+{
+ return getText(misc::Interval(start->getTokenIndex(), stop->getTokenIndex()));
+}
+
+void UnbufferedTokenStream::consume()
+{
+ if (LA(1) == EOF) {
+ throw IllegalStateException("cannot consume EOF");
+ }
+
+ // buf always has at least tokens[p==0] in this method due to ctor
+ _lastToken = _tokens[_p].get(); // track last token for LT(-1)
+
+ // if we're at last token and no markers, opportunity to flush buffer
+ if (_p == _tokens.size() - 1 && _numMarkers == 0) {
+ _tokens.clear();
+ _p = 0;
+ _lastTokenBufferStart = _lastToken;
+ } else {
+ ++_p;
+ }
+
+ ++_currentTokenIndex;
+ sync(1);
+}
+
+/// <summary>
+/// Make sure we have 'need' elements from current position <seealso cref="#p p"/>. Last valid
+/// {@code p} index is {@code tokens.length-1}. {@code p+need-1} is the tokens index 'need' elements
+/// ahead. If we need 1 element, {@code (p+1-1)==p} must be less than {@code tokens.length}.
+/// </summary>
+void UnbufferedTokenStream::sync(ssize_t want)
+{
+ ssize_t need = (static_cast<ssize_t>(_p) + want - 1) - static_cast<ssize_t>(_tokens.size()) + 1; // how many more elements we need?
+ if (need > 0) {
+ fill(static_cast<size_t>(need));
+ }
+}
+
+/// <summary>
+/// Add {@code n} elements to the buffer. Returns the number of tokens
+/// actually added to the buffer. If the return value is less than {@code n},
+/// then EOF was reached before {@code n} tokens could be added.
+/// </summary>
+size_t UnbufferedTokenStream::fill(size_t n)
+{
+ for (size_t i = 0; i < n; i++) {
+ if (_tokens.size() > 0 && _tokens.back()->getType() == EOF) {
+ return i;
+ }
+
+ add(_tokenSource->nextToken());
+ }
+
+ return n;
+}
+
+void UnbufferedTokenStream::add(std::unique_ptr<Token> t)
+{
+ WritableToken *writable = dynamic_cast<WritableToken *>(t.get());
+ if (writable != nullptr) {
+ writable->setTokenIndex(int(getBufferStartIndex() + _tokens.size()));
+ }
+
+ _tokens.push_back(std::move(t));
+}
+
+/// <summary>
+/// Return a marker that we can release later.
+/// <p/>
+/// The specific marker value used for this class allows for some level of
+/// protection against misuse where {@code seek()} is called on a mark or
+/// {@code release()} is called in the wrong order.
+/// </summary>
+ssize_t UnbufferedTokenStream::mark()
+{
+ if (_numMarkers == 0) {
+ _lastTokenBufferStart = _lastToken;
+ }
+
+ int mark = -_numMarkers - 1;
+ _numMarkers++;
+ return mark;
+}
+
+void UnbufferedTokenStream::release(ssize_t marker)
+{
+ ssize_t expectedMark = -_numMarkers;
+ if (marker != expectedMark) {
+ throw IllegalStateException("release() called with an invalid marker.");
+ }
+
+ _numMarkers--;
+ if (_numMarkers == 0) { // can we release buffer?
+ if (_p > 0) {
+ // Copy tokens[p]..tokens[n-1] to tokens[0]..tokens[(n-1)-p], reset ptrs
+ // p is last valid token; move nothing if p==n as we have no valid char
+ _tokens.erase(_tokens.begin(), _tokens.begin() + static_cast<ssize_t>(_p));
+ _p = 0;
+ }
+
+ _lastTokenBufferStart = _lastToken;
+ }
+}
+
+size_t UnbufferedTokenStream::index()
+{
+ return _currentTokenIndex;
+}
+
+void UnbufferedTokenStream::seek(size_t index)
+{ // seek to absolute index
+ if (index == _currentTokenIndex) {
+ return;
+ }
+
+ if (index > _currentTokenIndex) {
+ sync(ssize_t(index - _currentTokenIndex));
+ index = std::min(index, getBufferStartIndex() + _tokens.size() - 1);
+ }
+
+ size_t bufferStartIndex = getBufferStartIndex();
+ if (bufferStartIndex > index) {
+ throw IllegalArgumentException(std::string("cannot seek to negative index ") + std::to_string(index));
+ }
+
+ size_t i = index - bufferStartIndex;
+ if (i >= _tokens.size()) {
+ throw UnsupportedOperationException(std::string("seek to index outside buffer: ") + std::to_string(index) +
+ " not in " + std::to_string(bufferStartIndex) + ".." + std::to_string(bufferStartIndex + _tokens.size()));
+ }
+
+ _p = i;
+ _currentTokenIndex = index;
+ if (_p == 0) {
+ _lastToken = _lastTokenBufferStart;
+ } else {
+ _lastToken = _tokens[_p - 1].get();
+ }
+}
+
+size_t UnbufferedTokenStream::size()
+{
+ throw UnsupportedOperationException("Unbuffered stream cannot know its size");
+}
+
+std::string UnbufferedTokenStream::getSourceName() const
+{
+ return _tokenSource->getSourceName();
+}
+
+std::string UnbufferedTokenStream::getText(const misc::Interval &interval)
+{
+ size_t bufferStartIndex = getBufferStartIndex();
+ size_t bufferStopIndex = bufferStartIndex + _tokens.size() - 1;
+
+ size_t start = interval.a;
+ size_t stop = interval.b;
+ if (start < bufferStartIndex || stop > bufferStopIndex) {
+ throw UnsupportedOperationException(std::string("interval ") + interval.toString() +
+ " not in token buffer window: " + std::to_string(bufferStartIndex) + ".." + std::to_string(bufferStopIndex));
+ }
+
+ size_t a = start - bufferStartIndex;
+ size_t b = stop - bufferStartIndex;
+
+ std::stringstream ss;
+ for (size_t i = a; i <= b; i++) {
+ Token *t = _tokens[i].get();
+ if (i > 0)
+ ss << ", ";
+ ss << t->getText();
+ }
+
+ return ss.str();
+}
+
+size_t UnbufferedTokenStream::getBufferStartIndex() const
+{
+ return _currentTokenIndex - _p;
+}
+
+void UnbufferedTokenStream::InitializeInstanceFields()
+{
+ _p = 0;
+ _numMarkers = 0;
+ _currentTokenIndex = 0;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/UnbufferedTokenStream.h b/contrib/libs/antlr4_cpp_runtime/src/UnbufferedTokenStream.h
new file mode 100644
index 0000000000..0c67ec8610
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/UnbufferedTokenStream.h
@@ -0,0 +1,115 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "TokenStream.h"
+
+namespace antlr4 {
+
+ class ANTLR4CPP_PUBLIC UnbufferedTokenStream : public TokenStream {
+ public:
+ UnbufferedTokenStream(TokenSource *tokenSource);
+ UnbufferedTokenStream(TokenSource *tokenSource, int bufferSize);
+ UnbufferedTokenStream(const UnbufferedTokenStream& other) = delete;
+ virtual ~UnbufferedTokenStream();
+
+ UnbufferedTokenStream& operator = (const UnbufferedTokenStream& other) = delete;
+
+ virtual Token* get(size_t i) const override;
+ virtual Token* LT(ssize_t i) override;
+ virtual size_t LA(ssize_t i) override;
+
+ virtual TokenSource* getTokenSource() const override;
+
+ virtual std::string getText(const misc::Interval &interval) override;
+ virtual std::string getText() override;
+ virtual std::string getText(RuleContext *ctx) override;
+ virtual std::string getText(Token *start, Token *stop) override;
+
+ virtual void consume() override;
+
+ /// <summary>
+ /// Return a marker that we can release later.
+ /// <p/>
+ /// The specific marker value used for this class allows for some level of
+ /// protection against misuse where {@code seek()} is called on a mark or
+ /// {@code release()} is called in the wrong order.
+ /// </summary>
+ virtual ssize_t mark() override;
+ virtual void release(ssize_t marker) override;
+ virtual size_t index() override;
+ virtual void seek(size_t index) override;
+ virtual size_t size() override;
+ virtual std::string getSourceName() const override;
+
+ protected:
+ /// Make sure we have 'need' elements from current position p. Last valid
+ /// p index is tokens.length - 1. p + need - 1 is the tokens index 'need' elements
+ /// ahead. If we need 1 element, (p+1-1)==p must be less than tokens.length.
+ TokenSource *_tokenSource;
+
+ /// <summary>
+ /// A moving window buffer of the data being scanned. While there's a marker,
+ /// we keep adding to buffer. Otherwise, <seealso cref="#consume consume()"/> resets so
+ /// we start filling at index 0 again.
+ /// </summary>
+
+ std::vector<std::unique_ptr<Token>> _tokens;
+
+ /// <summary>
+ /// 0..n-1 index into <seealso cref="#tokens tokens"/> of next token.
+ /// <p/>
+ /// The {@code LT(1)} token is {@code tokens[p]}. If {@code p == n}, we are
+ /// out of buffered tokens.
+ /// </summary>
+ size_t _p;
+
+ /// <summary>
+ /// Count up with <seealso cref="#mark mark()"/> and down with
+ /// <seealso cref="#release release()"/>. When we {@code release()} the last mark,
+ /// {@code numMarkers} reaches 0 and we reset the buffer. Copy
+ /// {@code tokens[p]..tokens[n-1]} to {@code tokens[0]..tokens[(n-1)-p]}.
+ /// </summary>
+ int _numMarkers;
+
+ /// <summary>
+ /// This is the {@code LT(-1)} token for the current position.
+ /// </summary>
+ Token *_lastToken;
+
+ /// <summary>
+ /// When {@code numMarkers > 0}, this is the {@code LT(-1)} token for the
+ /// first token in <seealso cref="#tokens"/>. Otherwise, this is {@code null}.
+ /// </summary>
+ Token *_lastTokenBufferStart;
+
+ /// <summary>
+ /// Absolute token index. It's the index of the token about to be read via
+ /// {@code LT(1)}. Goes from 0 to the number of tokens in the entire stream,
+ /// although the stream size is unknown before the end is reached.
+ /// <p/>
+ /// This value is used to set the token indexes if the stream provides tokens
+ /// that implement <seealso cref="WritableToken"/>.
+ /// </summary>
+ size_t _currentTokenIndex;
+
+ virtual void sync(ssize_t want);
+
+ /// <summary>
+ /// Add {@code n} elements to the buffer. Returns the number of tokens
+ /// actually added to the buffer. If the return value is less than {@code n},
+ /// then EOF was reached before {@code n} tokens could be added.
+ /// </summary>
+ virtual size_t fill(size_t n);
+ virtual void add(std::unique_ptr<Token> t);
+
+ size_t getBufferStartIndex() const;
+
+ private:
+ void InitializeInstanceFields();
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Version.h b/contrib/libs/antlr4_cpp_runtime/src/Version.h
new file mode 100644
index 0000000000..43f00ea65c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Version.h
@@ -0,0 +1,42 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#pragma once
+
+#include "antlr4-common.h"
+
+#define ANTLRCPP_VERSION_MAJOR 4
+#define ANTLRCPP_VERSION_MINOR 11
+#define ANTLRCPP_VERSION_PATCH 1
+
+#define ANTLRCPP_MAKE_VERSION(major, minor, patch) ((major) * 100000 + (minor) * 1000 + (patch))
+
+#define ANTLRCPP_VERSION \
+ ANTLRCPP_MAKE_VERSION(ANTLR4CPP_VERSION_MAJOR, ANTLR4CPP_VERSION_MINOR, ANTLR4CPP_VERSION_PATCH)
+
+#define ANTLRCPP_VERSION_STRING \
+ ANTLR4CPP_STRINGIFY(ANTLR4CPP_VERSION_MAJOR) "." \
+ ANTLR4CPP_STRINGIFY(ANTLR4CPP_VERSION_MINOR) "." \
+ ANTLR4CPP_STRINGIFY(ANTLR4CPP_VERSION_PATCH)
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Vocabulary.cpp b/contrib/libs/antlr4_cpp_runtime/src/Vocabulary.cpp
new file mode 100644
index 0000000000..0f783d5d79
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Vocabulary.cpp
@@ -0,0 +1,64 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Token.h"
+
+#include "Vocabulary.h"
+
+using namespace antlr4::dfa;
+
+const Vocabulary Vocabulary::EMPTY_VOCABULARY;
+
+Vocabulary::Vocabulary(std::vector<std::string> literalNames, std::vector<std::string> symbolicNames)
+: Vocabulary(std::move(literalNames), std::move(symbolicNames), {}) {
+}
+
+Vocabulary::Vocabulary(std::vector<std::string> literalNames,
+ std::vector<std::string> symbolicNames, std::vector<std::string> displayNames)
+ : _literalNames(std::move(literalNames)), _symbolicNames(std::move(symbolicNames)), _displayNames(std::move(displayNames)),
+ _maxTokenType(std::max(_displayNames.size(), std::max(_literalNames.size(), _symbolicNames.size())) - 1) {
+ // See note here on -1 part: https://github.com/antlr/antlr4/pull/1146
+}
+
+std::string_view Vocabulary::getLiteralName(size_t tokenType) const {
+ if (tokenType < _literalNames.size()) {
+ return _literalNames[tokenType];
+ }
+
+ return "";
+}
+
+std::string_view Vocabulary::getSymbolicName(size_t tokenType) const {
+ if (tokenType == Token::EOF) {
+ return "EOF";
+ }
+
+ if (tokenType < _symbolicNames.size()) {
+ return _symbolicNames[tokenType];
+ }
+
+ return "";
+}
+
+std::string Vocabulary::getDisplayName(size_t tokenType) const {
+ if (tokenType < _displayNames.size()) {
+ std::string_view displayName = _displayNames[tokenType];
+ if (!displayName.empty()) {
+ return std::string(displayName);
+ }
+ }
+
+ std::string_view literalName = getLiteralName(tokenType);
+ if (!literalName.empty()) {
+ return std::string(literalName);
+ }
+
+ std::string_view symbolicName = getSymbolicName(tokenType);
+ if (!symbolicName.empty()) {
+ return std::string(symbolicName);
+ }
+
+ return std::to_string(tokenType);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/Vocabulary.h b/contrib/libs/antlr4_cpp_runtime/src/Vocabulary.h
new file mode 100644
index 0000000000..af5b243880
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/Vocabulary.h
@@ -0,0 +1,177 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace dfa {
+
+ /// This class provides a default implementation of the <seealso cref="Vocabulary"/>
+ /// interface.
+ class ANTLR4CPP_PUBLIC Vocabulary final {
+ public:
+ /// Gets an empty <seealso cref="Vocabulary"/> instance.
+ ///
+ /// <para>
+ /// No literal or symbol names are assigned to token types, so
+ /// <seealso cref="#getDisplayName(int)"/> returns the numeric value for all tokens
+ /// except <seealso cref="Token#EOF"/>.</para>
+ [[deprecated("Use the default constructor of Vocabulary instead.")]] static const Vocabulary EMPTY_VOCABULARY;
+
+ Vocabulary() {}
+
+ Vocabulary(const Vocabulary&) = default;
+
+ /// <summary>
+ /// Constructs a new instance of <seealso cref="Vocabulary"/> from the specified
+ /// literal and symbolic token names.
+ /// </summary>
+ /// <param name="literalNames"> The literal names assigned to tokens, or {@code null}
+ /// if no literal names are assigned. </param>
+ /// <param name="symbolicNames"> The symbolic names assigned to tokens, or
+ /// {@code null} if no symbolic names are assigned.
+ /// </param>
+ /// <seealso cref= #getLiteralName(int) </seealso>
+ /// <seealso cref= #getSymbolicName(int) </seealso>
+ Vocabulary(std::vector<std::string> literalNames, std::vector<std::string> symbolicNames);
+
+ /// <summary>
+ /// Constructs a new instance of <seealso cref="Vocabulary"/> from the specified
+ /// literal, symbolic, and display token names.
+ /// </summary>
+ /// <param name="literalNames"> The literal names assigned to tokens, or {@code null}
+ /// if no literal names are assigned. </param>
+ /// <param name="symbolicNames"> The symbolic names assigned to tokens, or
+ /// {@code null} if no symbolic names are assigned. </param>
+ /// <param name="displayNames"> The display names assigned to tokens, or {@code null}
+ /// to use the values in {@code literalNames} and {@code symbolicNames} as
+ /// the source of display names, as described in
+ /// <seealso cref="#getDisplayName(int)"/>.
+ /// </param>
+ /// <seealso cref= #getLiteralName(int) </seealso>
+ /// <seealso cref= #getSymbolicName(int) </seealso>
+ /// <seealso cref= #getDisplayName(int) </seealso>
+ Vocabulary(std::vector<std::string> literalNames, std::vector<std::string> symbolicNames,
+ std::vector<std::string> displayNames);
+
+ /// <summary>
+ /// Returns the highest token type value. It can be used to iterate from
+ /// zero to that number, inclusively, thus querying all stored entries. </summary>
+ /// <returns> the highest token type value </returns>
+ constexpr size_t getMaxTokenType() const { return _maxTokenType; }
+
+ /// <summary>
+ /// Gets the string literal associated with a token type. The string returned
+ /// by this method, when not {@code null}, can be used unaltered in a parser
+ /// grammar to represent this token type.
+ ///
+ /// <para>The following table shows examples of lexer rules and the literal
+ /// names assigned to the corresponding token types.</para>
+ ///
+ /// <table>
+ /// <tr>
+ /// <th>Rule</th>
+ /// <th>Literal Name</th>
+ /// <th>Java String Literal</th>
+ /// </tr>
+ /// <tr>
+ /// <td>{@code THIS : 'this';}</td>
+ /// <td>{@code 'this'}</td>
+ /// <td>{@code "'this'"}</td>
+ /// </tr>
+ /// <tr>
+ /// <td>{@code SQUOTE : '\'';}</td>
+ /// <td>{@code '\''}</td>
+ /// <td>{@code "'\\''"}</td>
+ /// </tr>
+ /// <tr>
+ /// <td>{@code ID : [A-Z]+;}</td>
+ /// <td>n/a</td>
+ /// <td>{@code null}</td>
+ /// </tr>
+ /// </table>
+ /// </summary>
+ /// <param name="tokenType"> The token type.
+ /// </param>
+ /// <returns> The string literal associated with the specified token type, or
+ /// {@code null} if no string literal is associated with the type. </returns>
+ std::string_view getLiteralName(size_t tokenType) const;
+
+ /// <summary>
+ /// Gets the symbolic name associated with a token type. The string returned
+ /// by this method, when not {@code null}, can be used unaltered in a parser
+ /// grammar to represent this token type.
+ ///
+ /// <para>This method supports token types defined by any of the following
+ /// methods:</para>
+ ///
+ /// <ul>
+ /// <li>Tokens created by lexer rules.</li>
+ /// <li>Tokens defined in a <code>tokens{}</code> block in a lexer or parser
+ /// grammar.</li>
+ /// <li>The implicitly defined {@code EOF} token, which has the token type
+ /// <seealso cref="Token#EOF"/>.</li>
+ /// </ul>
+ ///
+ /// <para>The following table shows examples of lexer rules and the literal
+ /// names assigned to the corresponding token types.</para>
+ ///
+ /// <table>
+ /// <tr>
+ /// <th>Rule</th>
+ /// <th>Symbolic Name</th>
+ /// </tr>
+ /// <tr>
+ /// <td>{@code THIS : 'this';}</td>
+ /// <td>{@code THIS}</td>
+ /// </tr>
+ /// <tr>
+ /// <td>{@code SQUOTE : '\'';}</td>
+ /// <td>{@code SQUOTE}</td>
+ /// </tr>
+ /// <tr>
+ /// <td>{@code ID : [A-Z]+;}</td>
+ /// <td>{@code ID}</td>
+ /// </tr>
+ /// </table>
+ /// </summary>
+ /// <param name="tokenType"> The token type.
+ /// </param>
+ /// <returns> The symbolic name associated with the specified token type, or
+ /// {@code null} if no symbolic name is associated with the type. </returns>
+ std::string_view getSymbolicName(size_t tokenType) const;
+
+ /// <summary>
+ /// Gets the display name of a token type.
+ ///
+ /// <para>ANTLR provides a default implementation of this method, but
+ /// applications are free to override the behavior in any manner which makes
+ /// sense for the application. The default implementation returns the first
+ /// result from the following list which produces a non-{@code null}
+ /// result.</para>
+ ///
+ /// <ol>
+ /// <li>The result of <seealso cref="#getLiteralName"/></li>
+ /// <li>The result of <seealso cref="#getSymbolicName"/></li>
+ /// <li>The result of <seealso cref="Integer#toString"/></li>
+ /// </ol>
+ /// </summary>
+ /// <param name="tokenType"> The token type.
+ /// </param>
+ /// <returns> The display name of the token type, for use in error reporting or
+ /// other user-visible messages which reference specific token types. </returns>
+ std::string getDisplayName(size_t tokenType) const;
+
+ private:
+ std::vector<std::string> const _literalNames;
+ std::vector<std::string> const _symbolicNames;
+ std::vector<std::string> const _displayNames;
+ const size_t _maxTokenType = 0;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/WritableToken.cpp b/contrib/libs/antlr4_cpp_runtime/src/WritableToken.cpp
new file mode 100644
index 0000000000..a30cd96f19
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/WritableToken.cpp
@@ -0,0 +1,9 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "WritableToken.h"
+
+antlr4::WritableToken::~WritableToken() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/WritableToken.h b/contrib/libs/antlr4_cpp_runtime/src/WritableToken.h
new file mode 100644
index 0000000000..28856f25b9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/WritableToken.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Token.h"
+
+namespace antlr4 {
+
+ class ANTLR4CPP_PUBLIC WritableToken : public Token {
+ public:
+ virtual ~WritableToken();
+ virtual void setText(const std::string &text) = 0;
+ virtual void setType(size_t ttype) = 0;
+ virtual void setLine(size_t line) = 0;
+ virtual void setCharPositionInLine(size_t pos) = 0;
+ virtual void setChannel(size_t channel) = 0;
+ virtual void setTokenIndex(size_t index) = 0;
+ };
+
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/antlr4-common.h b/contrib/libs/antlr4_cpp_runtime/src/antlr4-common.h
new file mode 100644
index 0000000000..d7f9a65fa1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/antlr4-common.h
@@ -0,0 +1,101 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <any>
+#include <atomic>
+#include <bitset>
+#include <cassert>
+#include <climits>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <stack>
+#include <string>
+#include <string_view>
+#include <typeinfo>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+// Defines for the Guid class and other platform dependent stuff.
+#ifdef _WIN32
+ #ifdef _MSC_VER
+ #pragma warning (disable: 4250) // Class inherits by dominance.
+ #pragma warning (disable: 4512) // assignment operator could not be generated
+
+ #if _MSC_VER < 1900
+ // Before VS 2015 code like "while (true)" will create a (useless) warning in level 4.
+ #pragma warning (disable: 4127) // conditional expression is constant
+ #endif
+ #endif
+
+ #ifdef _WIN64
+ typedef __int64 ssize_t;
+ #else
+ typedef __int32 ssize_t;
+ #endif
+
+ #ifdef ANTLR4CPP_EXPORTS
+ #define ANTLR4CPP_PUBLIC __declspec(dllexport)
+ #else
+ #ifdef ANTLR4CPP_STATIC
+ #define ANTLR4CPP_PUBLIC
+ #else
+ #define ANTLR4CPP_PUBLIC __declspec(dllimport)
+ #endif
+ #endif
+
+#elif defined(__APPLE__)
+ #if __GNUC__ >= 4
+ #define ANTLR4CPP_PUBLIC __attribute__ ((visibility ("default")))
+ #else
+ #define ANTLR4CPP_PUBLIC
+ #endif
+#else
+ #if __GNUC__ >= 6
+ #define ANTLR4CPP_PUBLIC __attribute__ ((visibility ("default")))
+ #else
+ #define ANTLR4CPP_PUBLIC
+ #endif
+#endif
+
+#ifdef __has_builtin
+#define ANTLR4CPP_HAVE_BUILTIN(x) __has_builtin(x)
+#else
+#define ANTLR4CPP_HAVE_BUILTIN(x) 0
+#endif
+
+#define ANTLR4CPP_INTERNAL_STRINGIFY(x) #x
+#define ANTLR4CPP_STRINGIFY(x) ANTLR4CPP_INTERNAL_STRINGIFY(x)
+
+// We use everything from the C++ standard library by default.
+#ifndef ANTLR4CPP_USING_ABSEIL
+#define ANTLR4CPP_USING_ABSEIL 0
+#endif
+
+#include "support/Declarations.h"
+
+// We have to undefine this symbol as ANTLR will use this name for own members and even
+// generated functions. Because EOF is a global macro we cannot use e.g. a namespace scope to disambiguate.
+#ifdef EOF
+#undef EOF
+#endif
+
+#define INVALID_INDEX std::numeric_limits<size_t>::max()
+template<class T> using Ref = std::shared_ptr<T>;
diff --git a/contrib/libs/antlr4_cpp_runtime/src/antlr4-runtime.h b/contrib/libs/antlr4_cpp_runtime/src/antlr4-runtime.h
new file mode 100644
index 0000000000..50b85aa4fc
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/antlr4-runtime.h
@@ -0,0 +1,168 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+// This is the umbrella header for all ANTLR4 C++ runtime headers.
+
+#include "antlr4-common.h"
+
+#include "ANTLRErrorListener.h"
+#include "ANTLRErrorStrategy.h"
+#include "ANTLRFileStream.h"
+#include "ANTLRInputStream.h"
+#include "BailErrorStrategy.h"
+#include "BaseErrorListener.h"
+#include "BufferedTokenStream.h"
+#include "CharStream.h"
+#include "CommonToken.h"
+#include "CommonTokenFactory.h"
+#include "CommonTokenStream.h"
+#include "ConsoleErrorListener.h"
+#include "DefaultErrorStrategy.h"
+#include "DiagnosticErrorListener.h"
+#include "Exceptions.h"
+#include "FailedPredicateException.h"
+#include "InputMismatchException.h"
+#include "IntStream.h"
+#include "InterpreterRuleContext.h"
+#include "Lexer.h"
+#include "LexerInterpreter.h"
+#include "LexerNoViableAltException.h"
+#include "ListTokenSource.h"
+#include "NoViableAltException.h"
+#include "Parser.h"
+#include "ParserInterpreter.h"
+#include "ParserRuleContext.h"
+#include "ProxyErrorListener.h"
+#include "RecognitionException.h"
+#include "Recognizer.h"
+#include "RuleContext.h"
+#include "RuleContextWithAltNum.h"
+#include "RuntimeMetaData.h"
+#include "Token.h"
+#include "TokenFactory.h"
+#include "TokenSource.h"
+#include "TokenStream.h"
+#include "TokenStreamRewriter.h"
+#include "UnbufferedCharStream.h"
+#include "UnbufferedTokenStream.h"
+#include "Version.h"
+#include "Vocabulary.h"
+#include "Vocabulary.h"
+#include "WritableToken.h"
+#include "atn/ATN.h"
+#include "atn/ATNConfig.h"
+#include "atn/ATNConfigSet.h"
+#include "atn/ATNDeserializationOptions.h"
+#include "atn/ATNDeserializer.h"
+#include "atn/ATNSimulator.h"
+#include "atn/ATNState.h"
+#include "atn/ATNType.h"
+#include "atn/ActionTransition.h"
+#include "atn/AmbiguityInfo.h"
+#include "atn/ArrayPredictionContext.h"
+#include "atn/AtomTransition.h"
+#include "atn/BasicBlockStartState.h"
+#include "atn/BasicState.h"
+#include "atn/BlockEndState.h"
+#include "atn/BlockStartState.h"
+#include "atn/ContextSensitivityInfo.h"
+#include "atn/DecisionEventInfo.h"
+#include "atn/DecisionInfo.h"
+#include "atn/DecisionState.h"
+#include "atn/EpsilonTransition.h"
+#include "atn/ErrorInfo.h"
+#include "atn/LL1Analyzer.h"
+#include "atn/LexerATNConfig.h"
+#include "atn/LexerATNSimulator.h"
+#include "atn/LexerAction.h"
+#include "atn/LexerActionExecutor.h"
+#include "atn/LexerActionType.h"
+#include "atn/LexerChannelAction.h"
+#include "atn/LexerCustomAction.h"
+#include "atn/LexerIndexedCustomAction.h"
+#include "atn/LexerModeAction.h"
+#include "atn/LexerMoreAction.h"
+#include "atn/LexerPopModeAction.h"
+#include "atn/LexerPushModeAction.h"
+#include "atn/LexerSkipAction.h"
+#include "atn/LexerTypeAction.h"
+#include "atn/LookaheadEventInfo.h"
+#include "atn/LoopEndState.h"
+#include "atn/NotSetTransition.h"
+#include "atn/OrderedATNConfigSet.h"
+#include "atn/ParseInfo.h"
+#include "atn/ParserATNSimulator.h"
+#include "atn/ParserATNSimulatorOptions.h"
+#include "atn/PlusBlockStartState.h"
+#include "atn/PlusLoopbackState.h"
+#include "atn/PrecedencePredicateTransition.h"
+#include "atn/PredicateEvalInfo.h"
+#include "atn/PredicateTransition.h"
+#include "atn/PredictionContext.h"
+#include "atn/PredictionContextCache.h"
+#include "atn/PredictionContextMergeCache.h"
+#include "atn/PredictionContextMergeCacheOptions.h"
+#include "atn/PredictionMode.h"
+#include "atn/ProfilingATNSimulator.h"
+#include "atn/RangeTransition.h"
+#include "atn/RuleStartState.h"
+#include "atn/RuleStopState.h"
+#include "atn/RuleTransition.h"
+#include "atn/SemanticContext.h"
+#include "atn/SerializedATNView.h"
+#include "atn/SetTransition.h"
+#include "atn/SingletonPredictionContext.h"
+#include "atn/StarBlockStartState.h"
+#include "atn/StarLoopEntryState.h"
+#include "atn/StarLoopbackState.h"
+#include "atn/TokensStartState.h"
+#include "atn/Transition.h"
+#include "atn/WildcardTransition.h"
+#include "dfa/DFA.h"
+#include "dfa/DFASerializer.h"
+#include "dfa/DFAState.h"
+#include "dfa/LexerDFASerializer.h"
+#include "misc/InterpreterDataReader.h"
+#include "misc/Interval.h"
+#include "misc/IntervalSet.h"
+#include "misc/MurmurHash.h"
+#include "misc/Predicate.h"
+#include "support/Any.h"
+#include "support/Arrays.h"
+#include "support/BitSet.h"
+#include "support/Casts.h"
+#include "support/CPPUtils.h"
+#include "tree/AbstractParseTreeVisitor.h"
+#include "tree/ErrorNode.h"
+#include "tree/ErrorNodeImpl.h"
+#include "tree/ParseTree.h"
+#include "tree/ParseTreeListener.h"
+#include "tree/ParseTreeProperty.h"
+#include "tree/ParseTreeVisitor.h"
+#include "tree/ParseTreeWalker.h"
+#include "tree/TerminalNode.h"
+#include "tree/TerminalNodeImpl.h"
+#include "tree/Trees.h"
+#include "tree/pattern/Chunk.h"
+#include "tree/pattern/ParseTreeMatch.h"
+#include "tree/pattern/ParseTreePattern.h"
+#include "tree/pattern/ParseTreePatternMatcher.h"
+#include "tree/pattern/RuleTagToken.h"
+#include "tree/pattern/TagChunk.h"
+#include "tree/pattern/TextChunk.h"
+#include "tree/pattern/TokenTagToken.h"
+#include "tree/xpath/XPath.h"
+#include "tree/xpath/XPathElement.h"
+#include "tree/xpath/XPathLexer.h"
+#include "tree/xpath/XPathLexerErrorListener.h"
+#include "tree/xpath/XPathRuleAnywhereElement.h"
+#include "tree/xpath/XPathRuleElement.h"
+#include "tree/xpath/XPathTokenAnywhereElement.h"
+#include "tree/xpath/XPathTokenElement.h"
+#include "tree/xpath/XPathWildcardAnywhereElement.h"
+#include "tree/xpath/XPathWildcardElement.h"
+#include "internal/Synchronization.h"
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATN.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ATN.cpp
new file mode 100644
index 0000000000..339515cc9c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATN.cpp
@@ -0,0 +1,159 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/LL1Analyzer.h"
+#include "Token.h"
+#include "atn/RuleTransition.h"
+#include "misc/IntervalSet.h"
+#include "RuleContext.h"
+#include "atn/DecisionState.h"
+#include "Recognizer.h"
+#include "atn/ATNType.h"
+#include "Exceptions.h"
+#include "support/CPPUtils.h"
+
+#include "atn/ATN.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::internal;
+using namespace antlrcpp;
+
+ATN::ATN() : ATN(ATNType::LEXER, 0) {}
+
+ATN::ATN(ATNType grammarType_, size_t maxTokenType_) : grammarType(grammarType_), maxTokenType(maxTokenType_) {}
+
+ATN::~ATN() {
+ for (ATNState *state : states) {
+ delete state;
+ }
+}
+
+misc::IntervalSet ATN::nextTokens(ATNState *s, RuleContext *ctx) const {
+ LL1Analyzer analyzer(*this);
+ return analyzer.LOOK(s, ctx);
+
+}
+
+misc::IntervalSet const& ATN::nextTokens(ATNState *s) const {
+ if (!s->_nextTokenUpdated) {
+ UniqueLock<Mutex> lock(_mutex);
+ if (!s->_nextTokenUpdated) {
+ s->_nextTokenWithinRule = nextTokens(s, nullptr);
+ s->_nextTokenUpdated = true;
+ }
+ }
+ return s->_nextTokenWithinRule;
+}
+
+void ATN::addState(ATNState *state) {
+ if (state != nullptr) {
+ //state->atn = this;
+ state->stateNumber = static_cast<int>(states.size());
+ }
+
+ states.push_back(state);
+}
+
+void ATN::removeState(ATNState *state) {
+ delete states.at(state->stateNumber);// just free mem, don't shift states in list
+ states.at(state->stateNumber) = nullptr;
+}
+
+int ATN::defineDecisionState(DecisionState *s) {
+ decisionToState.push_back(s);
+ s->decision = static_cast<int>(decisionToState.size() - 1);
+ return s->decision;
+}
+
+DecisionState *ATN::getDecisionState(size_t decision) const {
+ if (!decisionToState.empty()) {
+ return decisionToState[decision];
+ }
+ return nullptr;
+}
+
+size_t ATN::getNumberOfDecisions() const {
+ return decisionToState.size();
+}
+
+misc::IntervalSet ATN::getExpectedTokens(size_t stateNumber, RuleContext *context) const {
+ if (stateNumber == ATNState::INVALID_STATE_NUMBER || stateNumber >= states.size()) {
+ throw IllegalArgumentException("Invalid state number.");
+ }
+
+ RuleContext *ctx = context;
+ ATNState *s = states.at(stateNumber);
+ misc::IntervalSet following = nextTokens(s);
+ if (!following.contains(Token::EPSILON)) {
+ return following;
+ }
+
+ misc::IntervalSet expected;
+ expected.addAll(following);
+ expected.remove(Token::EPSILON);
+ while (ctx && ctx->invokingState != ATNState::INVALID_STATE_NUMBER && following.contains(Token::EPSILON)) {
+ ATNState *invokingState = states.at(ctx->invokingState);
+ const RuleTransition *rt = static_cast<const RuleTransition*>(invokingState->transitions[0].get());
+ following = nextTokens(rt->followState);
+ expected.addAll(following);
+ expected.remove(Token::EPSILON);
+
+ if (ctx->parent == nullptr) {
+ break;
+ }
+ ctx = static_cast<RuleContext *>(ctx->parent);
+ }
+
+ if (following.contains(Token::EPSILON)) {
+ expected.add(Token::EOF);
+ }
+
+ return expected;
+}
+
+std::string ATN::toString() const {
+ std::stringstream ss;
+ std::string type;
+ switch (grammarType) {
+ case ATNType::LEXER:
+ type = "LEXER ";
+ break;
+
+ case ATNType::PARSER:
+ type = "PARSER ";
+ break;
+
+ default:
+ break;
+ }
+ ss << "(" << type << "ATN " << std::hex << this << std::dec << ") maxTokenType: " << maxTokenType << std::endl;
+ ss << "states (" << states.size() << ") {" << std::endl;
+
+ size_t index = 0;
+ for (auto *state : states) {
+ if (state == nullptr) {
+ ss << " " << index++ << ": nul" << std::endl;
+ } else {
+ std::string text = state->toString();
+ ss << " " << index++ << ": " << indent(text, " ", false) << std::endl;
+ }
+ }
+
+ index = 0;
+ for (auto *state : decisionToState) {
+ if (state == nullptr) {
+ ss << " " << index++ << ": nul" << std::endl;
+ } else {
+ std::string text = state->toString();
+ ss << " " << index++ << ": " << indent(text, " ", false) << std::endl;
+ }
+ }
+
+ ss << "}";
+
+ return ss.str();
+}
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATN.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATN.h
new file mode 100644
index 0000000000..f12476358a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATN.h
@@ -0,0 +1,133 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "RuleContext.h"
+#include "internal/Synchronization.h"
+
+// GCC generates a warning when forward-declaring ATN if ATN has already been
+// declared due to the attributes added by ANTLR4CPP_PUBLIC.
+// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39159
+// Add constant that can be checked so forward-declarations can be omitted.
+#define ANTLR4CPP_ATN_DECLARED
+
+namespace antlr4 {
+namespace atn {
+
+ class LexerATNSimulator;
+ class ParserATNSimulator;
+
+ class ANTLR4CPP_PUBLIC ATN {
+ public:
+ static constexpr size_t INVALID_ALT_NUMBER = 0;
+
+ /// Used for runtime deserialization of ATNs from strings.
+ ATN();
+
+ ATN(ATNType grammarType, size_t maxTokenType);
+
+ ATN(const ATN&) = delete;
+
+ ATN(ATN&&) = delete;
+
+ ~ATN();
+
+ ATN& operator=(const ATN&) = delete;
+
+ ATN& operator=(ATN&&) = delete;
+
+ std::vector<ATNState *> states;
+
+ /// Each subrule/rule is a decision point and we must track them so we
+ /// can go back later and build DFA predictors for them. This includes
+ /// all the rules, subrules, optional blocks, ()+, ()* etc...
+ std::vector<DecisionState *> decisionToState;
+
+ /// Maps from rule index to starting state number.
+ std::vector<RuleStartState *> ruleToStartState;
+
+ /// Maps from rule index to stop state number.
+ std::vector<RuleStopState *> ruleToStopState;
+
+ /// The type of the ATN.
+ ATNType grammarType;
+
+ /// The maximum value for any symbol recognized by a transition in the ATN.
+ size_t maxTokenType;
+
+ /// <summary>
+ /// For lexer ATNs, this maps the rule index to the resulting token type.
+ /// For parser ATNs, this maps the rule index to the generated bypass token
+ /// type if the
+ /// <seealso cref="ATNDeserializationOptions#isGenerateRuleBypassTransitions"/>
+ /// deserialization option was specified; otherwise, this is {@code null}.
+ /// </summary>
+ std::vector<size_t> ruleToTokenType;
+
+ /// For lexer ATNs, this is an array of {@link LexerAction} objects which may
+ /// be referenced by action transitions in the ATN.
+ std::vector<Ref<const LexerAction>> lexerActions;
+
+ std::vector<TokensStartState *> modeToStartState;
+
+ /// <summary>
+ /// Compute the set of valid tokens that can occur starting in state {@code s}.
+ /// If {@code ctx} is null, the set of tokens will not include what can follow
+ /// the rule surrounding {@code s}. In other words, the set will be
+ /// restricted to tokens reachable staying within {@code s}'s rule.
+ /// </summary>
+ misc::IntervalSet nextTokens(ATNState *s, RuleContext *ctx) const;
+
+ /// <summary>
+ /// Compute the set of valid tokens that can occur starting in {@code s} and
+ /// staying in same rule. <seealso cref="Token#EPSILON"/> is in set if we reach end of
+ /// rule.
+ /// </summary>
+ misc::IntervalSet const& nextTokens(ATNState *s) const;
+
+ void addState(ATNState *state);
+
+ void removeState(ATNState *state);
+
+ int defineDecisionState(DecisionState *s);
+
+ DecisionState *getDecisionState(size_t decision) const;
+
+ size_t getNumberOfDecisions() const;
+
+ /// <summary>
+ /// Computes the set of input symbols which could follow ATN state number
+ /// {@code stateNumber} in the specified full {@code context}. This method
+ /// considers the complete parser context, but does not evaluate semantic
+ /// predicates (i.e. all predicates encountered during the calculation are
+ /// assumed true). If a path in the ATN exists from the starting state to the
+ /// <seealso cref="RuleStopState"/> of the outermost context without matching any
+ /// symbols, <seealso cref="Token#EOF"/> is added to the returned set.
+ /// <p/>
+ /// If {@code context} is {@code null}, it is treated as
+ /// <seealso cref="ParserRuleContext#EMPTY"/>.
+ /// </summary>
+ /// <param name="stateNumber"> the ATN state number </param>
+ /// <param name="context"> the full parse context </param>
+ /// <returns> The set of potentially valid input symbols which could follow the
+ /// specified state in the specified context. </returns>
+ /// <exception cref="IllegalArgumentException"> if the ATN does not contain a state with
+ /// number {@code stateNumber} </exception>
+ misc::IntervalSet getExpectedTokens(size_t stateNumber, RuleContext *context) const;
+
+ std::string toString() const;
+
+ private:
+ friend class LexerATNSimulator;
+ friend class ParserATNSimulator;
+
+ mutable internal::Mutex _mutex;
+ mutable internal::SharedMutex _stateMutex;
+ mutable internal::SharedMutex _edgeMutex;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfig.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfig.cpp
new file mode 100644
index 0000000000..be4d5bfa8c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfig.cpp
@@ -0,0 +1,106 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "atn/PredictionContext.h"
+#include "SemanticContext.h"
+
+#include "atn/ATNConfig.h"
+
+using namespace antlr4::atn;
+
+namespace {
+
+/**
+ * This field stores the bit mask for implementing the
+ * {@link #isPrecedenceFilterSuppressed} property as a bit within the
+ * existing {@link #reachesIntoOuterContext} field.
+ */
+inline constexpr size_t SUPPRESS_PRECEDENCE_FILTER = 0x40000000;
+
+}
+
+ATNConfig::ATNConfig(ATNState *state, size_t alt, Ref<const PredictionContext> context)
+ : ATNConfig(state, alt, std::move(context), 0, SemanticContext::Empty::Instance) {}
+
+ATNConfig::ATNConfig(ATNState *state, size_t alt, Ref<const PredictionContext> context, Ref<const SemanticContext> semanticContext)
+ : ATNConfig(state, alt, std::move(context), 0, std::move(semanticContext)) {}
+
+ATNConfig::ATNConfig(ATNConfig const& other, Ref<const SemanticContext> semanticContext)
+ : ATNConfig(other.state, other.alt, other.context, other.reachesIntoOuterContext, std::move(semanticContext)) {}
+
+ATNConfig::ATNConfig(ATNConfig const& other, ATNState *state)
+ : ATNConfig(state, other.alt, other.context, other.reachesIntoOuterContext, other.semanticContext) {}
+
+ATNConfig::ATNConfig(ATNConfig const& other, ATNState *state, Ref<const SemanticContext> semanticContext)
+ : ATNConfig(state, other.alt, other.context, other.reachesIntoOuterContext, std::move(semanticContext)) {}
+
+ATNConfig::ATNConfig(ATNConfig const& other, ATNState *state, Ref<const PredictionContext> context)
+ : ATNConfig(state, other.alt, std::move(context), other.reachesIntoOuterContext, other.semanticContext) {}
+
+ATNConfig::ATNConfig(ATNConfig const& other, ATNState *state, Ref<const PredictionContext> context, Ref<const SemanticContext> semanticContext)
+ : ATNConfig(state, other.alt, std::move(context), other.reachesIntoOuterContext, std::move(semanticContext)) {}
+
+ATNConfig::ATNConfig(ATNState *state, size_t alt, Ref<const PredictionContext> context, size_t reachesIntoOuterContext, Ref<const SemanticContext> semanticContext)
+ : state(state), alt(alt), context(std::move(context)), reachesIntoOuterContext(reachesIntoOuterContext), semanticContext(std::move(semanticContext)) {}
+
+size_t ATNConfig::hashCode() const {
+ size_t hashCode = misc::MurmurHash::initialize(7);
+ hashCode = misc::MurmurHash::update(hashCode, state->stateNumber);
+ hashCode = misc::MurmurHash::update(hashCode, alt);
+ hashCode = misc::MurmurHash::update(hashCode, context);
+ hashCode = misc::MurmurHash::update(hashCode, semanticContext);
+ hashCode = misc::MurmurHash::finish(hashCode, 4);
+ return hashCode;
+}
+
+size_t ATNConfig::getOuterContextDepth() const {
+ return reachesIntoOuterContext & ~SUPPRESS_PRECEDENCE_FILTER;
+}
+
+bool ATNConfig::isPrecedenceFilterSuppressed() const {
+ return (reachesIntoOuterContext & SUPPRESS_PRECEDENCE_FILTER) != 0;
+}
+
+void ATNConfig::setPrecedenceFilterSuppressed(bool value) {
+ if (value) {
+ reachesIntoOuterContext |= SUPPRESS_PRECEDENCE_FILTER;
+ } else {
+ reachesIntoOuterContext &= ~SUPPRESS_PRECEDENCE_FILTER;
+ }
+}
+
+bool ATNConfig::operator==(const ATNConfig &other) const {
+ return state->stateNumber == other.state->stateNumber && alt == other.alt &&
+ ((context == other.context) || (*context == *other.context)) &&
+ *semanticContext == *other.semanticContext &&
+ isPrecedenceFilterSuppressed() == other.isPrecedenceFilterSuppressed();
+}
+
+std::string ATNConfig::toString() const {
+ return toString(true);
+}
+
+std::string ATNConfig::toString(bool showAlt) const {
+ std::stringstream ss;
+ ss << "(";
+
+ ss << state->toString();
+ if (showAlt) {
+ ss << "," << alt;
+ }
+ if (context) {
+ ss << ",[" << context->toString() << "]";
+ }
+ if (semanticContext != nullptr && semanticContext != SemanticContext::Empty::Instance) {
+ ss << ",[" << semanticContext->toString() << "]";
+ }
+ if (getOuterContextDepth() > 0) {
+ ss << ",up=" << getOuterContextDepth();
+ }
+ ss << ")";
+
+ return ss.str();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfig.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfig.h
new file mode 100644
index 0000000000..1d2e7ae163
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfig.h
@@ -0,0 +1,157 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cassert>
+
+#include "antlr4-common.h"
+#include "atn/SemanticContext.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// A tuple: (ATN state, predicted alt, syntactic, semantic context).
+ /// The syntactic context is a graph-structured stack node whose
+ /// path(s) to the root is the rule invocation(s)
+ /// chain used to arrive at the state. The semantic context is
+ /// the tree of semantic predicates encountered before reaching
+ /// an ATN state.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC ATNConfig {
+ public:
+ struct Hasher
+ {
+ size_t operator()(Ref<ATNConfig> const& k) const {
+ return k->hashCode();
+ }
+
+ size_t operator()(ATNConfig const& k) const {
+ return k.hashCode();
+ }
+ };
+
+ struct Comparer {
+ bool operator()(Ref<ATNConfig> const& lhs, Ref<ATNConfig> const& rhs) const {
+ return (lhs == rhs) || (*lhs == *rhs);
+ }
+
+ bool operator()(ATNConfig const& lhs, ATNConfig const& rhs) const {
+ return (&lhs == &rhs) || (lhs == rhs);
+ }
+ };
+
+ using Set = std::unordered_set<Ref<ATNConfig>, Hasher, Comparer>;
+
+ /// The ATN state associated with this configuration.
+ ATNState *state = nullptr;
+
+ /// What alt (or lexer rule) is predicted by this configuration.
+ const size_t alt = 0;
+
+ /// The stack of invoking states leading to the rule/states associated
+ /// with this config. We track only those contexts pushed during
+ /// execution of the ATN simulator.
+ ///
+ /// Can be shared between multiple ANTConfig instances.
+ Ref<const PredictionContext> context;
+
+ /**
+ * We cannot execute predicates dependent upon local context unless
+ * we know for sure we are in the correct context. Because there is
+ * no way to do this efficiently, we simply cannot evaluate
+ * dependent predicates unless we are in the rule that initially
+ * invokes the ATN simulator.
+ *
+ * <p>
+ * closure() tracks the depth of how far we dip into the outer context:
+ * depth > 0. Note that it may not be totally accurate depth since I
+ * don't ever decrement. TODO: make it a boolean then</p>
+ *
+ * <p>
+ * For memory efficiency, the {@link #isPrecedenceFilterSuppressed} method
+ * is also backed by this field. Since the field is publicly accessible, the
+ * highest bit which would not cause the value to become negative is used to
+ * store this field. This choice minimizes the risk that code which only
+ * compares this value to 0 would be affected by the new purpose of the
+ * flag. It also ensures the performance of the existing {@link ATNConfig}
+ * constructors as well as certain operations like
+ * {@link ATNConfigSet#add(ATNConfig, DoubleKeyMap)} method are
+ * <em>completely</em> unaffected by the change.</p>
+ */
+ size_t reachesIntoOuterContext = 0;
+
+ /// Can be shared between multiple ATNConfig instances.
+ Ref<const SemanticContext> semanticContext;
+
+ ATNConfig(ATNState *state, size_t alt, Ref<const PredictionContext> context);
+ ATNConfig(ATNState *state, size_t alt, Ref<const PredictionContext> context, Ref<const SemanticContext> semanticContext);
+
+ ATNConfig(ATNConfig const& other, Ref<const SemanticContext> semanticContext);
+ ATNConfig(ATNConfig const& other, ATNState *state);
+ ATNConfig(ATNConfig const& other, ATNState *state, Ref<const SemanticContext> semanticContext);
+ ATNConfig(ATNConfig const& other, ATNState *state, Ref<const PredictionContext> context);
+ ATNConfig(ATNConfig const& other, ATNState *state, Ref<const PredictionContext> context, Ref<const SemanticContext> semanticContext);
+
+ ATNConfig(ATNConfig const&) = default;
+
+ ATNConfig(ATNConfig&&) = default;
+
+ virtual ~ATNConfig() = default;
+
+ virtual size_t hashCode() const;
+
+ /**
+ * This method gets the value of the {@link #reachesIntoOuterContext} field
+ * as it existed prior to the introduction of the
+ * {@link #isPrecedenceFilterSuppressed} method.
+ */
+ size_t getOuterContextDepth() const;
+ bool isPrecedenceFilterSuppressed() const;
+ void setPrecedenceFilterSuppressed(bool value);
+
+ /// An ATN configuration is equal to another if both have
+ /// the same state, they predict the same alternative, and
+ /// syntactic/semantic contexts are the same.
+ bool operator==(const ATNConfig &other) const;
+ bool operator!=(const ATNConfig &other) const;
+
+ virtual std::string toString() const;
+ std::string toString(bool showAlt) const;
+
+ private:
+ ATNConfig(ATNState *state, size_t alt, Ref<const PredictionContext> context, size_t reachesIntoOuterContext, Ref<const SemanticContext> semanticContext);
+ };
+
+} // namespace atn
+} // namespace antlr4
+
+
+// Hash function for ATNConfig.
+
+namespace std {
+ using antlr4::atn::ATNConfig;
+
+ template <> struct hash<ATNConfig>
+ {
+ size_t operator() (const ATNConfig &x) const
+ {
+ return x.hashCode();
+ }
+ };
+
+ template <> struct hash<std::vector<Ref<ATNConfig>>>
+ {
+ size_t operator() (const std::vector<Ref<ATNConfig>> &vector) const
+ {
+ std::size_t seed = 0;
+ for (const auto &config : vector) {
+ seed ^= config->hashCode() + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ }
+ return seed;
+ }
+ };
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfigSet.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfigSet.cpp
new file mode 100644
index 0000000000..4ebdf8882b
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfigSet.cpp
@@ -0,0 +1,232 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/PredictionContext.h"
+#include "atn/ATNConfig.h"
+#include "atn/ATNSimulator.h"
+#include "Exceptions.h"
+#include "atn/SemanticContext.h"
+#include "support/Arrays.h"
+
+#include "atn/ATNConfigSet.h"
+
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+namespace {
+
+}
+
+ATNConfigSet::ATNConfigSet() : ATNConfigSet(true) {}
+
+ATNConfigSet::ATNConfigSet(const ATNConfigSet &other)
+ : fullCtx(other.fullCtx), _configLookup(other._configLookup.bucket_count(), ATNConfigHasher{this}, ATNConfigComparer{this}) {
+ addAll(other);
+ uniqueAlt = other.uniqueAlt;
+ conflictingAlts = other.conflictingAlts;
+ hasSemanticContext = other.hasSemanticContext;
+ dipsIntoOuterContext = other.dipsIntoOuterContext;
+}
+
+ATNConfigSet::ATNConfigSet(bool fullCtx)
+ : fullCtx(fullCtx), _configLookup(0, ATNConfigHasher{this}, ATNConfigComparer{this}) {}
+
+bool ATNConfigSet::add(const Ref<ATNConfig> &config) {
+ return add(config, nullptr);
+}
+
+bool ATNConfigSet::add(const Ref<ATNConfig> &config, PredictionContextMergeCache *mergeCache) {
+ assert(config);
+
+ if (_readonly) {
+ throw IllegalStateException("This set is readonly");
+ }
+ if (config->semanticContext != SemanticContext::Empty::Instance) {
+ hasSemanticContext = true;
+ }
+ if (config->getOuterContextDepth() > 0) {
+ dipsIntoOuterContext = true;
+ }
+
+ auto existing = _configLookup.find(config.get());
+ if (existing == _configLookup.end()) {
+ _configLookup.insert(config.get());
+ _cachedHashCode = 0;
+ configs.push_back(config); // track order here
+
+ return true;
+ }
+
+ // a previous (s,i,pi,_), merge with it and save result
+ bool rootIsWildcard = !fullCtx;
+ Ref<const PredictionContext> merged = PredictionContext::merge((*existing)->context, config->context, rootIsWildcard, mergeCache);
+ // no need to check for existing.context, config.context in cache
+ // since only way to create new graphs is "call rule" and here. We
+ // cache at both places.
+ (*existing)->reachesIntoOuterContext = std::max((*existing)->reachesIntoOuterContext, config->reachesIntoOuterContext);
+
+ // make sure to preserve the precedence filter suppression during the merge
+ if (config->isPrecedenceFilterSuppressed()) {
+ (*existing)->setPrecedenceFilterSuppressed(true);
+ }
+
+ (*existing)->context = std::move(merged); // replace context; no need to alt mapping
+
+ return true;
+}
+
+bool ATNConfigSet::addAll(const ATNConfigSet &other) {
+ for (const auto &c : other.configs) {
+ add(c);
+ }
+ return false;
+}
+
+std::vector<ATNState*> ATNConfigSet::getStates() const {
+ std::vector<ATNState*> states;
+ states.reserve(configs.size());
+ for (const auto &c : configs) {
+ states.push_back(c->state);
+ }
+ return states;
+}
+
+/**
+ * Gets the complete set of represented alternatives for the configuration
+ * set.
+ *
+ * @return the set of represented alternatives in this configuration set
+ *
+ * @since 4.3
+ */
+
+BitSet ATNConfigSet::getAlts() const {
+ BitSet alts;
+ for (const auto &config : configs) {
+ alts.set(config->alt);
+ }
+ return alts;
+}
+
+std::vector<Ref<const SemanticContext>> ATNConfigSet::getPredicates() const {
+ std::vector<Ref<const SemanticContext>> preds;
+ preds.reserve(configs.size());
+ for (const auto &c : configs) {
+ if (c->semanticContext != SemanticContext::Empty::Instance) {
+ preds.push_back(c->semanticContext);
+ }
+ }
+ return preds;
+}
+
+const Ref<ATNConfig>& ATNConfigSet::get(size_t i) const {
+ return configs[i];
+}
+
+void ATNConfigSet::optimizeConfigs(ATNSimulator *interpreter) {
+ assert(interpreter);
+
+ if (_readonly) {
+ throw IllegalStateException("This set is readonly");
+ }
+ if (_configLookup.empty())
+ return;
+
+ for (const auto &config : configs) {
+ config->context = interpreter->getCachedContext(config->context);
+ }
+}
+
+bool ATNConfigSet::equals(const ATNConfigSet &other) const {
+ if (&other == this) {
+ return true;
+ }
+
+ if (configs.size() != other.configs.size())
+ return false;
+
+ if (fullCtx != other.fullCtx || uniqueAlt != other.uniqueAlt ||
+ conflictingAlts != other.conflictingAlts || hasSemanticContext != other.hasSemanticContext ||
+ dipsIntoOuterContext != other.dipsIntoOuterContext) // includes stack context
+ return false;
+
+ return Arrays::equals(configs, other.configs);
+}
+
+size_t ATNConfigSet::hashCode() const {
+ size_t cachedHashCode = _cachedHashCode.load(std::memory_order_relaxed);
+ if (!isReadonly() || cachedHashCode == 0) {
+ cachedHashCode = 1;
+ for (const auto &i : configs) {
+ cachedHashCode = 31 * cachedHashCode + i->hashCode(); // Same as Java's list hashCode impl.
+ }
+ _cachedHashCode.store(cachedHashCode, std::memory_order_relaxed);
+ }
+ return cachedHashCode;
+}
+
+size_t ATNConfigSet::size() const {
+ return configs.size();
+}
+
+bool ATNConfigSet::isEmpty() const {
+ return configs.empty();
+}
+
+void ATNConfigSet::clear() {
+ if (_readonly) {
+ throw IllegalStateException("This set is readonly");
+ }
+ configs.clear();
+ _cachedHashCode = 0;
+ _configLookup.clear();
+}
+
+bool ATNConfigSet::isReadonly() const {
+ return _readonly;
+}
+
+void ATNConfigSet::setReadonly(bool readonly) {
+ _readonly = readonly;
+ LookupContainer(0, ATNConfigHasher{this}, ATNConfigComparer{this}).swap(_configLookup);
+}
+
+std::string ATNConfigSet::toString() const {
+ std::stringstream ss;
+ ss << "[";
+ for (size_t i = 0; i < configs.size(); i++) {
+ ss << configs[i]->toString();
+ }
+ ss << "]";
+
+ if (hasSemanticContext) {
+ ss << ",hasSemanticContext = " << hasSemanticContext;
+ }
+ if (uniqueAlt != ATN::INVALID_ALT_NUMBER) {
+ ss << ",uniqueAlt = " << uniqueAlt;
+ }
+
+ if (conflictingAlts.size() > 0) {
+ ss << ",conflictingAlts = ";
+ ss << conflictingAlts.toString();
+ }
+
+ if (dipsIntoOuterContext) {
+ ss << ", dipsIntoOuterContext";
+ }
+ return ss.str();
+}
+
+size_t ATNConfigSet::hashCode(const ATNConfig &other) const {
+ size_t hashCode = 7;
+ hashCode = 31 * hashCode + other.state->stateNumber;
+ hashCode = 31 * hashCode + other.alt;
+ hashCode = 31 * hashCode + other.semanticContext->hashCode();
+ return hashCode;
+}
+
+bool ATNConfigSet::equals(const ATNConfig &lhs, const ATNConfig &rhs) const {
+ return lhs.state->stateNumber == rhs.state->stateNumber && lhs.alt == rhs.alt && *lhs.semanticContext == *rhs.semanticContext;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfigSet.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfigSet.h
new file mode 100644
index 0000000000..d147f183a0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNConfigSet.h
@@ -0,0 +1,157 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cassert>
+
+#include "support/BitSet.h"
+#include "atn/PredictionContext.h"
+#include "atn/ATNConfig.h"
+#include "FlatHashSet.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// Specialized set that can track info about the set, with support for combining similar configurations using a
+ /// graph-structured stack.
+ class ANTLR4CPP_PUBLIC ATNConfigSet {
+ public:
+ /// Track the elements as they are added to the set; supports get(i)
+ std::vector<Ref<ATNConfig>> configs;
+
+ // TODO: these fields make me pretty uncomfortable but nice to pack up info together, saves recomputation
+ // TODO: can we track conflicts as they are added to save scanning configs later?
+ size_t uniqueAlt = 0;
+
+ /** Currently this is only used when we detect SLL conflict; this does
+ * not necessarily represent the ambiguous alternatives. In fact,
+ * I should also point out that this seems to include predicated alternatives
+ * that have predicates that evaluate to false. Computed in computeTargetState().
+ */
+ antlrcpp::BitSet conflictingAlts;
+
+ // Used in parser and lexer. In lexer, it indicates we hit a pred
+ // while computing a closure operation. Don't make a DFA state from this.
+ bool hasSemanticContext = false;
+ bool dipsIntoOuterContext = false;
+
+ /// Indicates that this configuration set is part of a full context
+ /// LL prediction. It will be used to determine how to merge $. With SLL
+ /// it's a wildcard whereas it is not for LL context merge.
+ const bool fullCtx = true;
+
+ ATNConfigSet();
+
+ ATNConfigSet(const ATNConfigSet &other);
+
+ ATNConfigSet(ATNConfigSet&&) = delete;
+
+ explicit ATNConfigSet(bool fullCtx);
+
+ virtual ~ATNConfigSet() = default;
+
+ bool add(const Ref<ATNConfig> &config);
+
+ /// <summary>
+ /// Adding a new config means merging contexts with existing configs for
+ /// {@code (s, i, pi, _)}, where {@code s} is the
+ /// <seealso cref="ATNConfig#state"/>, {@code i} is the <seealso cref="ATNConfig#alt"/>, and
+ /// {@code pi} is the <seealso cref="ATNConfig#semanticContext"/>. We use
+ /// {@code (s,i,pi)} as key.
+ /// <p/>
+ /// This method updates <seealso cref="#dipsIntoOuterContext"/> and
+ /// <seealso cref="#hasSemanticContext"/> when necessary.
+ /// </summary>
+ bool add(const Ref<ATNConfig> &config, PredictionContextMergeCache *mergeCache);
+
+ bool addAll(const ATNConfigSet &other);
+
+ std::vector<ATNState*> getStates() const;
+
+ /**
+ * Gets the complete set of represented alternatives for the configuration
+ * set.
+ *
+ * @return the set of represented alternatives in this configuration set
+ *
+ * @since 4.3
+ */
+ antlrcpp::BitSet getAlts() const;
+ std::vector<Ref<const SemanticContext>> getPredicates() const;
+
+ const Ref<ATNConfig>& get(size_t i) const;
+
+ void optimizeConfigs(ATNSimulator *interpreter);
+
+ size_t size() const;
+ bool isEmpty() const;
+ void clear();
+ bool isReadonly() const;
+ void setReadonly(bool readonly);
+
+ virtual size_t hashCode() const;
+
+ virtual bool equals(const ATNConfigSet &other) const;
+
+ virtual std::string toString() const;
+
+ private:
+ struct ATNConfigHasher final {
+ const ATNConfigSet* atnConfigSet;
+
+ size_t operator()(const ATNConfig *other) const {
+ assert(other != nullptr);
+ return atnConfigSet->hashCode(*other);
+ }
+ };
+
+ struct ATNConfigComparer final {
+ const ATNConfigSet* atnConfigSet;
+
+ bool operator()(const ATNConfig *lhs, const ATNConfig *rhs) const {
+ assert(lhs != nullptr);
+ assert(rhs != nullptr);
+ return atnConfigSet->equals(*lhs, *rhs);
+ }
+ };
+
+ mutable std::atomic<size_t> _cachedHashCode = 0;
+
+ /// Indicates that the set of configurations is read-only. Do not
+ /// allow any code to manipulate the set; DFA states will point at
+ /// the sets and they must not change. This does not protect the other
+ /// fields; in particular, conflictingAlts is set after
+ /// we've made this readonly.
+ bool _readonly = false;
+
+ virtual size_t hashCode(const ATNConfig &atnConfig) const;
+
+ virtual bool equals(const ATNConfig &lhs, const ATNConfig &rhs) const;
+
+ using LookupContainer = FlatHashSet<ATNConfig*, ATNConfigHasher, ATNConfigComparer>;
+
+ /// All configs but hashed by (s, i, _, pi) not including context. Wiped out
+ /// when we go readonly as this set becomes a DFA state.
+ LookupContainer _configLookup;
+ };
+
+ inline bool operator==(const ATNConfigSet &lhs, const ATNConfigSet &rhs) { return lhs.equals(rhs); }
+
+ inline bool operator!=(const ATNConfigSet &lhs, const ATNConfigSet &rhs) { return !operator==(lhs, rhs); }
+
+} // namespace atn
+} // namespace antlr4
+
+namespace std {
+
+template <>
+struct hash<::antlr4::atn::ATNConfigSet> {
+ size_t operator()(const ::antlr4::atn::ATNConfigSet &atnConfigSet) const {
+ return atnConfigSet.hashCode();
+ }
+};
+
+} // namespace std
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializationOptions.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializationOptions.cpp
new file mode 100644
index 0000000000..e0a7cb2b27
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializationOptions.cpp
@@ -0,0 +1,39 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATNDeserializationOptions.h"
+#include "Exceptions.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+ATNDeserializationOptions::ATNDeserializationOptions(ATNDeserializationOptions *options)
+ : _readOnly(false), _verifyATN(options->_verifyATN),
+ _generateRuleBypassTransitions(options->_generateRuleBypassTransitions) {}
+
+const ATNDeserializationOptions& ATNDeserializationOptions::getDefaultOptions() {
+ static const ATNDeserializationOptions* const defaultOptions = new ATNDeserializationOptions();
+ return *defaultOptions;
+}
+
+void ATNDeserializationOptions::makeReadOnly() {
+ _readOnly = true;
+}
+
+void ATNDeserializationOptions::setVerifyATN(bool verify) {
+ throwIfReadOnly();
+ _verifyATN = verify;
+}
+
+void ATNDeserializationOptions::setGenerateRuleBypassTransitions(bool generate) {
+ throwIfReadOnly();
+ _generateRuleBypassTransitions = generate;
+}
+
+void ATNDeserializationOptions::throwIfReadOnly() const {
+ if (isReadOnly()) {
+ throw IllegalStateException("ATNDeserializationOptions is read only.");
+ }
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializationOptions.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializationOptions.h
new file mode 100644
index 0000000000..595f918649
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializationOptions.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+class ANTLR4CPP_PUBLIC ATNDeserializationOptions final {
+public:
+ ATNDeserializationOptions()
+ : _readOnly(false), _verifyATN(true), _generateRuleBypassTransitions(false) {}
+
+ // TODO: Is this useful? If so we should mark it as explicit, otherwise remove it.
+ ATNDeserializationOptions(ATNDeserializationOptions *options);
+
+ ATNDeserializationOptions(const ATNDeserializationOptions&) = default;
+
+ ATNDeserializationOptions& operator=(const ATNDeserializationOptions&) = default;
+
+ static const ATNDeserializationOptions& getDefaultOptions();
+
+ bool isReadOnly() const { return _readOnly; }
+
+ void makeReadOnly();
+
+ bool isVerifyATN() const { return _verifyATN; }
+
+ void setVerifyATN(bool verify);
+
+ bool isGenerateRuleBypassTransitions() const { return _generateRuleBypassTransitions; }
+
+ void setGenerateRuleBypassTransitions(bool generate);
+
+private:
+ void throwIfReadOnly() const;
+
+ bool _readOnly;
+ bool _verifyATN;
+ bool _generateRuleBypassTransitions;
+};
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializer.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializer.cpp
new file mode 100644
index 0000000000..2da3c32357
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializer.cpp
@@ -0,0 +1,628 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATNDeserializationOptions.h"
+
+#include "atn/ATNType.h"
+#include "atn/ATNState.h"
+#include "atn/ATN.h"
+
+#include "atn/LoopEndState.h"
+#include "atn/DecisionState.h"
+#include "atn/RuleStartState.h"
+#include "atn/RuleStopState.h"
+#include "atn/TokensStartState.h"
+#include "atn/RuleTransition.h"
+#include "atn/EpsilonTransition.h"
+#include "atn/PlusLoopbackState.h"
+#include "atn/PlusBlockStartState.h"
+#include "atn/StarLoopbackState.h"
+#include "atn/BasicBlockStartState.h"
+#include "atn/BasicState.h"
+#include "atn/BlockEndState.h"
+#include "atn/StarLoopEntryState.h"
+
+#include "atn/AtomTransition.h"
+#include "atn/StarBlockStartState.h"
+#include "atn/RangeTransition.h"
+#include "atn/PredicateTransition.h"
+#include "atn/PrecedencePredicateTransition.h"
+#include "atn/ActionTransition.h"
+#include "atn/SetTransition.h"
+#include "atn/NotSetTransition.h"
+#include "atn/WildcardTransition.h"
+#include "atn/TransitionType.h"
+#include "Token.h"
+
+#include "misc/IntervalSet.h"
+#include "Exceptions.h"
+#include "support/CPPUtils.h"
+#include "support/Casts.h"
+
+#include "atn/LexerCustomAction.h"
+#include "atn/LexerChannelAction.h"
+#include "atn/LexerModeAction.h"
+#include "atn/LexerMoreAction.h"
+#include "atn/LexerPopModeAction.h"
+#include "atn/LexerPushModeAction.h"
+#include "atn/LexerSkipAction.h"
+#include "atn/LexerTypeAction.h"
+
+#include "atn/ATNDeserializer.h"
+
+#include <cassert>
+#include <string>
+#include <vector>
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+namespace {
+
+ void checkCondition(bool condition, std::string_view message) {
+ if (!condition) {
+ throw IllegalStateException(std::string(message));
+ }
+ }
+
+ void checkCondition(bool condition) {
+ checkCondition(condition, "");
+ }
+
+ /**
+ * Analyze the {@link StarLoopEntryState} states in the specified ATN to set
+ * the {@link StarLoopEntryState#isPrecedenceDecision} field to the
+ * correct value.
+ *
+ * @param atn The ATN.
+ */
+ void markPrecedenceDecisions(const ATN &atn) {
+ for (ATNState *state : atn.states) {
+ if (!StarLoopEntryState::is(state)) {
+ continue;
+ }
+
+ /* We analyze the ATN to determine if this ATN decision state is the
+ * decision for the closure block that determines whether a
+ * precedence rule should continue or complete.
+ */
+ if (atn.ruleToStartState[state->ruleIndex]->isLeftRecursiveRule) {
+ ATNState *maybeLoopEndState = state->transitions[state->transitions.size() - 1]->target;
+ if (LoopEndState::is(maybeLoopEndState)) {
+ if (maybeLoopEndState->epsilonOnlyTransitions && RuleStopState::is(maybeLoopEndState->transitions[0]->target)) {
+ downCast<StarLoopEntryState*>(state)->isPrecedenceDecision = true;
+ }
+ }
+ }
+ }
+ }
+
+ Ref<const LexerAction> lexerActionFactory(LexerActionType type, int data1, int data2) {
+ switch (type) {
+ case LexerActionType::CHANNEL:
+ return std::make_shared<LexerChannelAction>(data1);
+
+ case LexerActionType::CUSTOM:
+ return std::make_shared<LexerCustomAction>(data1, data2);
+
+ case LexerActionType::MODE:
+ return std::make_shared< LexerModeAction>(data1);
+
+ case LexerActionType::MORE:
+ return LexerMoreAction::getInstance();
+
+ case LexerActionType::POP_MODE:
+ return LexerPopModeAction::getInstance();
+
+ case LexerActionType::PUSH_MODE:
+ return std::make_shared<LexerPushModeAction>(data1);
+
+ case LexerActionType::SKIP:
+ return LexerSkipAction::getInstance();
+
+ case LexerActionType::TYPE:
+ return std::make_shared<LexerTypeAction>(data1);
+
+ default:
+ throw IllegalArgumentException("The specified lexer action type " + std::to_string(static_cast<size_t>(type)) +
+ " is not valid.");
+ }
+ }
+
+ ConstTransitionPtr edgeFactory(const ATN &atn, TransitionType type, size_t trg, size_t arg1, size_t arg2,
+ size_t arg3, const std::vector<misc::IntervalSet> &sets) {
+ ATNState *target = atn.states[trg];
+ switch (type) {
+ case TransitionType::EPSILON:
+ return std::make_unique<EpsilonTransition>(target);
+ case TransitionType::RANGE:
+ if (arg3 != 0) {
+ return std::make_unique<RangeTransition>(target, Token::EOF, arg2);
+ } else {
+ return std::make_unique<RangeTransition>(target, arg1, arg2);
+ }
+ case TransitionType::RULE:
+ return std::make_unique<RuleTransition>(downCast<RuleStartState*>(atn.states[arg1]), arg2, (int)arg3, target);
+ case TransitionType::PREDICATE:
+ return std::make_unique<PredicateTransition>(target, arg1, arg2, arg3 != 0);
+ case TransitionType::PRECEDENCE:
+ return std::make_unique<PrecedencePredicateTransition>(target, (int)arg1);
+ case TransitionType::ATOM:
+ if (arg3 != 0) {
+ return std::make_unique<AtomTransition>(target, Token::EOF);
+ } else {
+ return std::make_unique<AtomTransition>(target, arg1);
+ }
+ case TransitionType::ACTION:
+ return std::make_unique<ActionTransition>(target, arg1, arg2, arg3 != 0);
+ case TransitionType::SET:
+ return std::make_unique<SetTransition>(target, sets[arg1]);
+ case TransitionType::NOT_SET:
+ return std::make_unique<NotSetTransition>(target, sets[arg1]);
+ case TransitionType::WILDCARD:
+ return std::make_unique<WildcardTransition>(target);
+ }
+
+ throw IllegalArgumentException("The specified transition type is not valid.");
+ }
+
+ /* mem check: all created instances are freed in the d-tor of the ATN. */
+ ATNState* stateFactory(ATNStateType type, size_t ruleIndex) {
+ ATNState *s;
+ switch (type) {
+ case ATNStateType::INVALID:
+ return nullptr;
+ case ATNStateType::BASIC :
+ s = new BasicState();
+ break;
+ case ATNStateType::RULE_START :
+ s = new RuleStartState();
+ break;
+ case ATNStateType::BLOCK_START :
+ s = new BasicBlockStartState();
+ break;
+ case ATNStateType::PLUS_BLOCK_START :
+ s = new PlusBlockStartState();
+ break;
+ case ATNStateType::STAR_BLOCK_START :
+ s = new StarBlockStartState();
+ break;
+ case ATNStateType::TOKEN_START :
+ s = new TokensStartState();
+ break;
+ case ATNStateType::RULE_STOP :
+ s = new RuleStopState();
+ break;
+ case ATNStateType::BLOCK_END :
+ s = new BlockEndState();
+ break;
+ case ATNStateType::STAR_LOOP_BACK :
+ s = new StarLoopbackState();
+ break;
+ case ATNStateType::STAR_LOOP_ENTRY :
+ s = new StarLoopEntryState();
+ break;
+ case ATNStateType::PLUS_LOOP_BACK :
+ s = new PlusLoopbackState();
+ break;
+ case ATNStateType::LOOP_END :
+ s = new LoopEndState();
+ break;
+ default :
+ std::string message = "The specified state type " + std::to_string(static_cast<size_t>(type)) + " is not valid.";
+ throw IllegalArgumentException(message);
+ }
+ assert(s->getStateType() == type);
+ s->ruleIndex = ruleIndex;
+ return s;
+ }
+
+ ssize_t readUnicodeInt32(SerializedATNView data, int& p) {
+ return static_cast<ssize_t>(data[p++]);
+ }
+
+ void deserializeSets(
+ SerializedATNView data,
+ int& p,
+ std::vector<misc::IntervalSet>& sets) {
+ size_t nsets = data[p++];
+ sets.reserve(sets.size() + nsets);
+ for (size_t i = 0; i < nsets; i++) {
+ size_t nintervals = data[p++];
+ misc::IntervalSet set;
+
+ bool containsEof = data[p++] != 0;
+ if (containsEof) {
+ set.add(-1);
+ }
+
+ for (size_t j = 0; j < nintervals; j++) {
+ auto a = readUnicodeInt32(data, p);
+ auto b = readUnicodeInt32(data, p);
+ set.add(a, b);
+ }
+ sets.push_back(set);
+ }
+ }
+
+}
+
+ATNDeserializer::ATNDeserializer() : ATNDeserializer(ATNDeserializationOptions::getDefaultOptions()) {}
+
+ATNDeserializer::ATNDeserializer(ATNDeserializationOptions deserializationOptions) : _deserializationOptions(std::move(deserializationOptions)) {}
+
+std::unique_ptr<ATN> ATNDeserializer::deserialize(SerializedATNView data) const {
+ int p = 0;
+ int version = data[p++];
+ if (version != SERIALIZED_VERSION) {
+ std::string reason = "Could not deserialize ATN with version" + std::to_string(version) + "(expected " + std::to_string(SERIALIZED_VERSION) + ").";
+
+ throw UnsupportedOperationException(reason);
+ }
+
+ ATNType grammarType = (ATNType)data[p++];
+ size_t maxTokenType = data[p++];
+ auto atn = std::make_unique<ATN>(grammarType, maxTokenType);
+
+ //
+ // STATES
+ //
+ {
+ std::vector<std::pair<LoopEndState*, size_t>> loopBackStateNumbers;
+ std::vector<std::pair<BlockStartState*, size_t>> endStateNumbers;
+ size_t nstates = data[p++];
+ atn->states.reserve(nstates);
+ loopBackStateNumbers.reserve(nstates); // Reserve worst case size, its short lived.
+ endStateNumbers.reserve(nstates); // Reserve worst case size, its short lived.
+ for (size_t i = 0; i < nstates; i++) {
+ ATNStateType stype = static_cast<ATNStateType>(data[p++]);
+ // ignore bad type of states
+ if (stype == ATNStateType::INVALID) {
+ atn->addState(nullptr);
+ continue;
+ }
+
+ size_t ruleIndex = data[p++];
+ ATNState *s = stateFactory(stype, ruleIndex);
+ if (stype == ATNStateType::LOOP_END) { // special case
+ int loopBackStateNumber = data[p++];
+ loopBackStateNumbers.push_back({ downCast<LoopEndState*>(s), loopBackStateNumber });
+ } else if (BlockStartState::is(s)) {
+ int endStateNumber = data[p++];
+ endStateNumbers.push_back({ downCast<BlockStartState*>(s), endStateNumber });
+ }
+ atn->addState(s);
+ }
+
+ // delay the assignment of loop back and end states until we know all the state instances have been initialized
+ for (auto &pair : loopBackStateNumbers) {
+ pair.first->loopBackState = atn->states[pair.second];
+ }
+
+ for (auto &pair : endStateNumbers) {
+ pair.first->endState = downCast<BlockEndState*>(atn->states[pair.second]);
+ }
+ }
+
+ size_t numNonGreedyStates = data[p++];
+ for (size_t i = 0; i < numNonGreedyStates; i++) {
+ size_t stateNumber = data[p++];
+ // The serialized ATN must be specifying the right states, so that the
+ // cast below is correct.
+ downCast<DecisionState*>(atn->states[stateNumber])->nonGreedy = true;
+ }
+
+ size_t numPrecedenceStates = data[p++];
+ for (size_t i = 0; i < numPrecedenceStates; i++) {
+ size_t stateNumber = data[p++];
+ downCast<RuleStartState*>(atn->states[stateNumber])->isLeftRecursiveRule = true;
+ }
+
+ //
+ // RULES
+ //
+ size_t nrules = data[p++];
+ atn->ruleToStartState.reserve(nrules);
+ for (size_t i = 0; i < nrules; i++) {
+ size_t s = data[p++];
+ // Also here, the serialized atn must ensure to point to the correct class type.
+ RuleStartState *startState = downCast<RuleStartState*>(atn->states[s]);
+ atn->ruleToStartState.push_back(startState);
+ if (atn->grammarType == ATNType::LEXER) {
+ size_t tokenType = data[p++];
+ atn->ruleToTokenType.push_back(tokenType);
+ }
+ }
+
+ atn->ruleToStopState.resize(nrules);
+ for (ATNState *state : atn->states) {
+ if (!RuleStopState::is(state)) {
+ continue;
+ }
+
+ RuleStopState *stopState = downCast<RuleStopState*>(state);
+ atn->ruleToStopState[state->ruleIndex] = stopState;
+ atn->ruleToStartState[state->ruleIndex]->stopState = stopState;
+ }
+
+ //
+ // MODES
+ //
+ size_t nmodes = data[p++];
+ atn->modeToStartState.reserve(nmodes);
+ for (size_t i = 0; i < nmodes; i++) {
+ size_t s = data[p++];
+ atn->modeToStartState.push_back(downCast<TokensStartState*>(atn->states[s]));
+ }
+
+ //
+ // SETS
+ //
+ {
+ std::vector<misc::IntervalSet> sets;
+
+ deserializeSets(data, p, sets);
+ sets.shrink_to_fit();
+
+ //
+ // EDGES
+ //
+ int nedges = data[p++];
+ for (int i = 0; i < nedges; i++) {
+ size_t src = data[p];
+ size_t trg = data[p + 1];
+ TransitionType ttype = static_cast<TransitionType>(data[p + 2]);
+ size_t arg1 = data[p + 3];
+ size_t arg2 = data[p + 4];
+ size_t arg3 = data[p + 5];
+ ConstTransitionPtr trans = edgeFactory(*atn, ttype, trg, arg1, arg2, arg3, sets);
+ ATNState *srcState = atn->states[src];
+ srcState->addTransition(std::move(trans));
+ p += 6;
+ }
+ }
+ // edges for rule stop states can be derived, so they aren't serialized
+ for (ATNState *state : atn->states) {
+ for (size_t i = 0; i < state->transitions.size(); i++) {
+ const Transition *t = state->transitions[i].get();
+ if (!RuleTransition::is(t)) {
+ continue;
+ }
+
+ const RuleTransition *ruleTransition = downCast<const RuleTransition*>(t);
+ size_t outermostPrecedenceReturn = INVALID_INDEX;
+ if (atn->ruleToStartState[ruleTransition->target->ruleIndex]->isLeftRecursiveRule) {
+ if (ruleTransition->precedence == 0) {
+ outermostPrecedenceReturn = ruleTransition->target->ruleIndex;
+ }
+ }
+
+ ConstTransitionPtr returnTransition = std::make_unique<EpsilonTransition>(ruleTransition->followState, outermostPrecedenceReturn);
+ atn->ruleToStopState[ruleTransition->target->ruleIndex]->addTransition(std::move(returnTransition));
+ }
+ }
+
+ for (ATNState *state : atn->states) {
+ if (BlockStartState::is(state)) {
+ BlockStartState *startState = downCast<BlockStartState*>(state);
+
+ // we need to know the end state to set its start state
+ if (startState->endState == nullptr) {
+ throw IllegalStateException();
+ }
+
+ // block end states can only be associated to a single block start state
+ if (startState->endState->startState != nullptr) {
+ throw IllegalStateException();
+ }
+
+ startState->endState->startState = downCast<BlockStartState*>(state);
+ }
+
+ if (PlusLoopbackState::is(state)) {
+ PlusLoopbackState *loopbackState = downCast<PlusLoopbackState*>(state);
+ for (size_t i = 0; i < loopbackState->transitions.size(); i++) {
+ ATNState *target = loopbackState->transitions[i]->target;
+ if (PlusBlockStartState::is(target)) {
+ (downCast<PlusBlockStartState*>(target))->loopBackState = loopbackState;
+ }
+ }
+ } else if (StarLoopbackState::is(state)) {
+ StarLoopbackState *loopbackState = downCast<StarLoopbackState*>(state);
+ for (size_t i = 0; i < loopbackState->transitions.size(); i++) {
+ ATNState *target = loopbackState->transitions[i]->target;
+ if (StarLoopEntryState::is(target)) {
+ downCast<StarLoopEntryState*>(target)->loopBackState = loopbackState;
+ }
+ }
+ }
+ }
+
+ //
+ // DECISIONS
+ //
+ size_t ndecisions = data[p++];
+ atn->decisionToState.reserve(ndecisions);
+ for (size_t i = 0; i < ndecisions; i++) {
+ size_t s = data[p++];
+ DecisionState *decState = downCast<DecisionState*>(atn->states[s]);
+ if (decState == nullptr)
+ throw IllegalStateException();
+
+ atn->decisionToState.push_back(decState);
+ decState->decision = static_cast<int>(i);
+ }
+
+ //
+ // LEXER ACTIONS
+ //
+ if (atn->grammarType == ATNType::LEXER) {
+ atn->lexerActions.resize(data[p++]);
+ for (size_t i = 0; i < atn->lexerActions.size(); i++) {
+ LexerActionType actionType = static_cast<LexerActionType>(data[p++]);
+ int data1 = data[p++];
+ int data2 = data[p++];
+ atn->lexerActions[i] = lexerActionFactory(actionType, data1, data2);
+ }
+ }
+
+ markPrecedenceDecisions(*atn);
+
+ if (_deserializationOptions.isVerifyATN()) {
+ verifyATN(*atn);
+ }
+
+ if (_deserializationOptions.isGenerateRuleBypassTransitions() && atn->grammarType == ATNType::PARSER) {
+ atn->ruleToTokenType.resize(atn->ruleToStartState.size());
+ for (size_t i = 0; i < atn->ruleToStartState.size(); i++) {
+ atn->ruleToTokenType[i] = static_cast<int>(atn->maxTokenType + i + 1);
+ }
+
+ for (std::vector<RuleStartState*>::size_type i = 0; i < atn->ruleToStartState.size(); i++) {
+ BasicBlockStartState *bypassStart = new BasicBlockStartState(); /* mem check: freed in ATN d-tor */
+ bypassStart->ruleIndex = static_cast<int>(i);
+ atn->addState(bypassStart);
+
+ BlockEndState *bypassStop = new BlockEndState(); /* mem check: freed in ATN d-tor */
+ bypassStop->ruleIndex = static_cast<int>(i);
+ atn->addState(bypassStop);
+
+ bypassStart->endState = bypassStop;
+ atn->defineDecisionState(bypassStart);
+
+ bypassStop->startState = bypassStart;
+
+ ATNState *endState;
+ const Transition *excludeTransition = nullptr;
+ if (atn->ruleToStartState[i]->isLeftRecursiveRule) {
+ // wrap from the beginning of the rule to the StarLoopEntryState
+ endState = nullptr;
+ for (ATNState *state : atn->states) {
+ if (state->ruleIndex != i) {
+ continue;
+ }
+
+ if (!StarLoopEntryState::is(state)) {
+ continue;
+ }
+
+ ATNState *maybeLoopEndState = state->transitions[state->transitions.size() - 1]->target;
+ if (!LoopEndState::is(maybeLoopEndState)) {
+ continue;
+ }
+
+ if (maybeLoopEndState->epsilonOnlyTransitions && RuleStopState::is(maybeLoopEndState->transitions[0]->target)) {
+ endState = state;
+ break;
+ }
+ }
+
+ if (endState == nullptr) {
+ throw UnsupportedOperationException("Couldn't identify final state of the precedence rule prefix section.");
+
+ }
+
+ excludeTransition = (static_cast<StarLoopEntryState*>(endState))->loopBackState->transitions[0].get();
+ } else {
+ endState = atn->ruleToStopState[i];
+ }
+
+ // all non-excluded transitions that currently target end state need to target blockEnd instead
+ for (ATNState *state : atn->states) {
+ for (auto &transition : state->transitions) {
+ if (transition.get() == excludeTransition) {
+ continue;
+ }
+
+ if (transition->target == endState) {
+ const_cast<Transition*>(transition.get())->target = bypassStop;
+ }
+ }
+ }
+
+ // all transitions leaving the rule start state need to leave blockStart instead
+ while (atn->ruleToStartState[i]->transitions.size() > 0) {
+ ConstTransitionPtr transition = atn->ruleToStartState[i]->removeTransition(atn->ruleToStartState[i]->transitions.size() - 1);
+ bypassStart->addTransition(std::move(transition));
+ }
+
+ // link the new states
+ atn->ruleToStartState[i]->addTransition(std::make_unique<EpsilonTransition>(bypassStart));
+ bypassStop->addTransition(std::make_unique<EpsilonTransition>(endState));
+
+ ATNState *matchState = new BasicState(); /* mem check: freed in ATN d-tor */
+ atn->addState(matchState);
+ matchState->addTransition(std::make_unique<AtomTransition>(bypassStop, atn->ruleToTokenType[i]));
+ bypassStart->addTransition(std::make_unique<EpsilonTransition>(matchState));
+ }
+
+ if (_deserializationOptions.isVerifyATN()) {
+ // reverify after modification
+ verifyATN(*atn);
+ }
+ }
+
+ return atn;
+}
+
+void ATNDeserializer::verifyATN(const ATN &atn) const {
+ // verify assumptions
+ for (ATNState *state : atn.states) {
+ if (state == nullptr) {
+ continue;
+ }
+
+ checkCondition(state->epsilonOnlyTransitions || state->transitions.size() <= 1);
+
+ if (PlusBlockStartState::is(state)) {
+ checkCondition((downCast<PlusBlockStartState*>(state))->loopBackState != nullptr);
+ }
+
+ if (StarLoopEntryState::is(state)) {
+ StarLoopEntryState *starLoopEntryState = downCast<StarLoopEntryState*>(state);
+ checkCondition(starLoopEntryState->loopBackState != nullptr);
+ checkCondition(starLoopEntryState->transitions.size() == 2);
+
+ if (StarBlockStartState::is(starLoopEntryState->transitions[0]->target)) {
+ checkCondition(downCast<LoopEndState*>(starLoopEntryState->transitions[1]->target) != nullptr);
+ checkCondition(!starLoopEntryState->nonGreedy);
+ } else if (LoopEndState::is(starLoopEntryState->transitions[0]->target)) {
+ checkCondition(StarBlockStartState::is(starLoopEntryState->transitions[1]->target));
+ checkCondition(starLoopEntryState->nonGreedy);
+ } else {
+ throw IllegalStateException();
+ }
+ }
+
+ if (StarLoopbackState::is(state)) {
+ checkCondition(state->transitions.size() == 1);
+ checkCondition(StarLoopEntryState::is(state->transitions[0]->target));
+ }
+
+ if (LoopEndState::is(state)) {
+ checkCondition((downCast<LoopEndState*>(state))->loopBackState != nullptr);
+ }
+
+ if (RuleStartState::is(state)) {
+ checkCondition((downCast<RuleStartState*>(state))->stopState != nullptr);
+ }
+
+ if (BlockStartState::is(state)) {
+ checkCondition((downCast<BlockStartState*>(state))->endState != nullptr);
+ }
+
+ if (BlockEndState::is(state)) {
+ checkCondition((downCast<BlockEndState*>(state))->startState != nullptr);
+ }
+
+ if (DecisionState::is(state)) {
+ DecisionState *decisionState = downCast<DecisionState*>(state);
+ checkCondition(decisionState->transitions.size() <= 1 || decisionState->decision >= 0);
+ } else {
+ checkCondition(state->transitions.size() <= 1 || RuleStopState::is(state));
+ }
+ }
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializer.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializer.h
new file mode 100644
index 0000000000..3cd56b9cdf
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNDeserializer.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNDeserializationOptions.h"
+#include "atn/SerializedATNView.h"
+#include "atn/LexerAction.h"
+#include "atn/Transition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC ATNDeserializer final {
+ public:
+ static constexpr size_t SERIALIZED_VERSION = 4;
+
+ ATNDeserializer();
+
+ explicit ATNDeserializer(ATNDeserializationOptions deserializationOptions);
+
+ std::unique_ptr<ATN> deserialize(SerializedATNView input) const;
+ void verifyATN(const ATN &atn) const;
+
+ private:
+ const ATNDeserializationOptions _deserializationOptions;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNSimulator.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNSimulator.cpp
new file mode 100644
index 0000000000..04e1af992e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNSimulator.cpp
@@ -0,0 +1,33 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATNSimulator.h"
+
+#include "atn/ATNConfigSet.h"
+#include "atn/ATNDeserializer.h"
+#include "atn/ATNType.h"
+#include "dfa/DFAState.h"
+
+using namespace antlr4;
+using namespace antlr4::dfa;
+using namespace antlr4::atn;
+
+const Ref<DFAState> ATNSimulator::ERROR = std::make_shared<DFAState>(std::numeric_limits<int>::max());
+
+ATNSimulator::ATNSimulator(const ATN &atn, PredictionContextCache &sharedContextCache)
+ : atn(atn), _sharedContextCache(sharedContextCache) {}
+
+void ATNSimulator::clearDFA() {
+ throw UnsupportedOperationException("This ATN simulator does not support clearing the DFA.");
+}
+
+PredictionContextCache& ATNSimulator::getSharedContextCache() const {
+ return _sharedContextCache;
+}
+
+Ref<const PredictionContext> ATNSimulator::getCachedContext(const Ref<const PredictionContext> &context) {
+ // This function must only be called with an active state lock, as we are going to change a shared structure.
+ return PredictionContext::getCachedContext(context, getSharedContextCache());
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNSimulator.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNSimulator.h
new file mode 100644
index 0000000000..b14939e219
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNSimulator.h
@@ -0,0 +1,71 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATN.h"
+#include "atn/PredictionContext.h"
+#include "atn/PredictionContextCache.h"
+#include "misc/IntervalSet.h"
+#include "support/CPPUtils.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC ATNSimulator {
+ public:
+ /// Must distinguish between missing edge and edge we know leads nowhere.
+ static const Ref<dfa::DFAState> ERROR;
+ const ATN &atn;
+
+ ATNSimulator(const ATN &atn, PredictionContextCache &sharedContextCache);
+
+ virtual ~ATNSimulator() = default;
+
+ virtual void reset() = 0;
+
+ /**
+ * Clear the DFA cache used by the current instance. Since the DFA cache may
+ * be shared by multiple ATN simulators, this method may affect the
+ * performance (but not accuracy) of other parsers which are being used
+ * concurrently.
+ *
+ * @throws UnsupportedOperationException if the current instance does not
+ * support clearing the DFA.
+ *
+ * @since 4.3
+ */
+ virtual void clearDFA();
+
+ PredictionContextCache& getSharedContextCache() const;
+ Ref<const PredictionContext> getCachedContext(const Ref<const PredictionContext> &context);
+
+ protected:
+ /// <summary>
+ /// The context cache maps all PredictionContext objects that are equals()
+ /// to a single cached copy. This cache is shared across all contexts
+ /// in all ATNConfigs in all DFA states. We rebuild each ATNConfigSet
+ /// to use only cached nodes/graphs in addDFAState(). We don't want to
+ /// fill this during closure() since there are lots of contexts that
+ /// pop up but are not used ever again. It also greatly slows down closure().
+ /// <p/>
+ /// This cache makes a huge difference in memory and a little bit in speed.
+ /// For the Java grammar on java.*, it dropped the memory requirements
+ /// at the end from 25M to 16M. We don't store any of the full context
+ /// graphs in the DFA because they are limited to local context only,
+ /// but apparently there's a lot of repetition there as well. We optimize
+ /// the config contexts before storing the config set in the DFA states
+ /// by literally rebuilding them with cached subgraphs only.
+ /// <p/>
+ /// I tried a cache for use during closure operations, that was
+ /// whacked after each adaptivePredict(). It cost a little bit
+ /// more time I think and doesn't save on the overall footprint
+ /// so it's not worth the complexity.
+ /// </summary>
+ PredictionContextCache &_sharedContextCache;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNState.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNState.cpp
new file mode 100644
index 0000000000..29911901be
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNState.cpp
@@ -0,0 +1,56 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATN.h"
+#include "atn/Transition.h"
+#include "misc/IntervalSet.h"
+#include "support/CPPUtils.h"
+
+#include "atn/ATNState.h"
+
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+size_t ATNState::hashCode() const {
+ return stateNumber;
+}
+
+bool ATNState::equals(const ATNState &other) const {
+ return stateNumber == other.stateNumber;
+}
+
+bool ATNState::isNonGreedyExitState() const {
+ return false;
+}
+
+std::string ATNState::toString() const {
+ return std::to_string(stateNumber);
+}
+
+void ATNState::addTransition(ConstTransitionPtr e) {
+ addTransition(transitions.size(), std::move(e));
+}
+
+void ATNState::addTransition(size_t index, ConstTransitionPtr e) {
+ for (const auto &transition : transitions)
+ if (transition->target->stateNumber == e->target->stateNumber) {
+ return;
+ }
+
+ if (transitions.empty()) {
+ epsilonOnlyTransitions = e->isEpsilon();
+ } else if (epsilonOnlyTransitions != e->isEpsilon()) {
+ std::cerr << "ATN state %d has both epsilon and non-epsilon transitions.\n" << stateNumber;
+ epsilonOnlyTransitions = false;
+ }
+
+ transitions.insert(transitions.begin() + index, std::move(e));
+}
+
+ConstTransitionPtr ATNState::removeTransition(size_t index) {
+ ConstTransitionPtr result = std::move(transitions[index]);
+ transitions.erase(transitions.begin() + index);
+ return result;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNState.h
new file mode 100644
index 0000000000..7613f40eee
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNState.h
@@ -0,0 +1,139 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "misc/IntervalSet.h"
+#include "atn/Transition.h"
+#include "atn/ATNStateType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// The following images show the relation of states and
+ /// <seealso cref="ATNState#transitions"/> for various grammar constructs.
+ ///
+ /// <ul>
+ ///
+ /// <li>Solid edges marked with an &#0949; indicate a required
+ /// <seealso cref="EpsilonTransition"/>.</li>
+ ///
+ /// <li>Dashed edges indicate locations where any transition derived from
+ /// <seealso cref="Transition"/> might appear.</li>
+ ///
+ /// <li>Dashed nodes are place holders for either a sequence of linked
+ /// <seealso cref="BasicState"/> states or the inclusion of a block representing a nested
+ /// construct in one of the forms below.</li>
+ ///
+ /// <li>Nodes showing multiple outgoing alternatives with a {@code ...} support
+ /// any number of alternatives (one or more). Nodes without the {@code ...} only
+ /// support the exact number of alternatives shown in the diagram.</li>
+ ///
+ /// </ul>
+ ///
+ /// <h2>Basic Blocks</h2>
+ ///
+ /// <h3>Rule</h3>
+ ///
+ /// <embed src="images/Rule.svg" type="image/svg+xml"/>
+ ///
+ /// <h3>Block of 1 or more alternatives</h3>
+ ///
+ /// <embed src="images/Block.svg" type="image/svg+xml"/>
+ ///
+ /// <h2>Greedy Loops</h2>
+ ///
+ /// <h3>Greedy Closure: {@code (...)*}</h3>
+ ///
+ /// <embed src="images/ClosureGreedy.svg" type="image/svg+xml"/>
+ ///
+ /// <h3>Greedy Positive Closure: {@code (...)+}</h3>
+ ///
+ /// <embed src="images/PositiveClosureGreedy.svg" type="image/svg+xml"/>
+ ///
+ /// <h3>Greedy Optional: {@code (...)?}</h3>
+ ///
+ /// <embed src="images/OptionalGreedy.svg" type="image/svg+xml"/>
+ ///
+ /// <h2>Non-Greedy Loops</h2>
+ ///
+ /// <h3>Non-Greedy Closure: {@code (...)*?}</h3>
+ ///
+ /// <embed src="images/ClosureNonGreedy.svg" type="image/svg+xml"/>
+ ///
+ /// <h3>Non-Greedy Positive Closure: {@code (...)+?}</h3>
+ ///
+ /// <embed src="images/PositiveClosureNonGreedy.svg" type="image/svg+xml"/>
+ ///
+ /// <h3>Non-Greedy Optional: {@code (...)??}</h3>
+ ///
+ /// <embed src="images/OptionalNonGreedy.svg" type="image/svg+xml"/>
+ /// </summary>
+
+// GCC generates a warning here if ATN has already been declared due to the
+// attributes added by ANTLR4CPP_PUBLIC.
+// See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39159
+// Only forward-declare if it hasn't already been declared.
+#ifndef ANTLR4CPP_ATN_DECLARED
+ class ANTLR4CPP_PUBLIC ATN;
+#endif
+
+ class ANTLR4CPP_PUBLIC ATNState {
+ public:
+ static constexpr size_t INITIAL_NUM_TRANSITIONS = 4;
+ static constexpr size_t INVALID_STATE_NUMBER = std::numeric_limits<size_t>::max();
+
+ size_t stateNumber = INVALID_STATE_NUMBER;
+ size_t ruleIndex = 0; // at runtime, we don't have Rule objects
+ bool epsilonOnlyTransitions = false;
+
+ /// Track the transitions emanating from this ATN state.
+ std::vector<ConstTransitionPtr> transitions;
+
+ ATNState() = delete;
+
+ ATNState(ATNState const&) = delete;
+
+ ATNState(ATNState&&) = delete;
+
+ virtual ~ATNState() = default;
+
+ ATNState& operator=(ATNState const&) = delete;
+
+ ATNState& operator=(ATNState&&) = delete;
+
+ void addTransition(ConstTransitionPtr e);
+ void addTransition(size_t index, ConstTransitionPtr e);
+ ConstTransitionPtr removeTransition(size_t index);
+
+ virtual size_t hashCode() const;
+ virtual bool equals(const ATNState &other) const;
+
+ virtual bool isNonGreedyExitState() const;
+ virtual std::string toString() const;
+
+ ATNStateType getStateType() const { return _stateType; }
+
+ protected:
+ explicit ATNState(ATNStateType stateType) : _stateType(stateType) {}
+
+ private:
+ /// Used to cache lookahead during parsing, not used during construction.
+
+ misc::IntervalSet _nextTokenWithinRule;
+ std::atomic<bool> _nextTokenUpdated { false };
+
+ const ATNStateType _stateType;
+
+ friend class ATN;
+ };
+
+ inline bool operator==(const ATNState &lhs, const ATNState &rhs) { return lhs.equals(rhs); }
+
+ inline bool operator!=(const ATNState &lhs, const ATNState &rhs) { return !operator==(lhs, rhs); }
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNStateType.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNStateType.cpp
new file mode 100644
index 0000000000..577e2af87c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNStateType.cpp
@@ -0,0 +1,33 @@
+#include "atn/ATNStateType.h"
+
+std::string antlr4::atn::atnStateTypeName(ATNStateType atnStateType) {
+ switch (atnStateType) {
+ case ATNStateType::INVALID:
+ return "INVALID";
+ case ATNStateType::BASIC:
+ return "BASIC";
+ case ATNStateType::RULE_START:
+ return "RULE_START";
+ case ATNStateType::BLOCK_START:
+ return "BLOCK_START";
+ case ATNStateType::PLUS_BLOCK_START:
+ return "PLUS_BLOCK_START";
+ case ATNStateType::STAR_BLOCK_START:
+ return "STAR_BLOCK_START";
+ case ATNStateType::TOKEN_START:
+ return "TOKEN_START";
+ case ATNStateType::RULE_STOP:
+ return "RULE_STOP";
+ case ATNStateType::BLOCK_END:
+ return "BLOCK_END";
+ case ATNStateType::STAR_LOOP_BACK:
+ return "STAR_LOOP_BACK";
+ case ATNStateType::STAR_LOOP_ENTRY:
+ return "STAR_LOOP_ENTRY";
+ case ATNStateType::PLUS_LOOP_BACK:
+ return "PLUS_LOOP_BACK";
+ case ATNStateType::LOOP_END:
+ return "LOOP_END";
+ }
+ return "UNKNOWN";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNStateType.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNStateType.h
new file mode 100644
index 0000000000..e19b2cce92
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNStateType.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <string>
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ // Constants for ATNState serialization.
+ enum class ATNStateType : size_t {
+ INVALID = 0,
+ BASIC = 1,
+ RULE_START = 2,
+ BLOCK_START = 3,
+ PLUS_BLOCK_START = 4,
+ STAR_BLOCK_START = 5,
+ TOKEN_START = 6,
+ RULE_STOP = 7,
+ BLOCK_END = 8,
+ STAR_LOOP_BACK = 9,
+ STAR_LOOP_ENTRY = 10,
+ PLUS_LOOP_BACK = 11,
+ LOOP_END = 12,
+ };
+
+ ANTLR4CPP_PUBLIC std::string atnStateTypeName(ATNStateType atnStateType);
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ATNType.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNType.h
new file mode 100644
index 0000000000..3530ef6051
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ATNType.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// Represents the type of recognizer an ATN applies to.
+ enum class ATNType {
+ LEXER = 0,
+ PARSER = 1,
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ActionTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ActionTransition.cpp
new file mode 100644
index 0000000000..1886b7e169
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ActionTransition.cpp
@@ -0,0 +1,29 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ActionTransition.h"
+
+using namespace antlr4::atn;
+
+ActionTransition::ActionTransition(ATNState *target, size_t ruleIndex)
+ : Transition(TransitionType::ACTION, target), ruleIndex(ruleIndex), actionIndex(INVALID_INDEX), isCtxDependent(false) {
+}
+
+ActionTransition::ActionTransition(ATNState *target, size_t ruleIndex, size_t actionIndex, bool isCtxDependent)
+ : Transition(TransitionType::ACTION, target), ruleIndex(ruleIndex), actionIndex(actionIndex), isCtxDependent(isCtxDependent) {
+}
+
+bool ActionTransition::isEpsilon() const {
+ return true; // we are to be ignored by analysis 'cept for predicates
+}
+
+bool ActionTransition::matches(size_t /*symbol*/, size_t /*minVocabSymbol*/, size_t /*maxVocabSymbol*/) const {
+ return false;
+}
+
+std::string ActionTransition::toString() const {
+ return " ACTION " + Transition::toString() + " { ruleIndex: " + std::to_string(ruleIndex) + ", actionIndex: " +
+ std::to_string(actionIndex) + ", isCtxDependent: " + std::to_string(isCtxDependent) + " }";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ActionTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ActionTransition.h
new file mode 100644
index 0000000000..1700297a78
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ActionTransition.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC ActionTransition final : public Transition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::ACTION; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ const size_t ruleIndex;
+ const size_t actionIndex;
+ const bool isCtxDependent; // e.g., $i ref in action
+
+ ActionTransition(ATNState *target, size_t ruleIndex);
+
+ ActionTransition(ATNState *target, size_t ruleIndex, size_t actionIndex, bool isCtxDependent);
+
+ virtual bool isEpsilon() const override;
+
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+
+ virtual std::string toString() const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/AmbiguityInfo.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/AmbiguityInfo.cpp
new file mode 100644
index 0000000000..72ce922633
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/AmbiguityInfo.cpp
@@ -0,0 +1,16 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/AmbiguityInfo.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+AmbiguityInfo::AmbiguityInfo(size_t decision, ATNConfigSet *configs, const antlrcpp::BitSet &ambigAlts,
+ TokenStream *input, size_t startIndex, size_t stopIndex, bool fullCtx)
+ : DecisionEventInfo(decision, configs, input, startIndex, stopIndex, fullCtx) {
+
+ this->ambigAlts = ambigAlts;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/AmbiguityInfo.h b/contrib/libs/antlr4_cpp_runtime/src/atn/AmbiguityInfo.h
new file mode 100644
index 0000000000..db594a1f48
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/AmbiguityInfo.h
@@ -0,0 +1,68 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionEventInfo.h"
+#include "support/BitSet.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// This class represents profiling event information for an ambiguity.
+ /// Ambiguities are decisions where a particular input resulted in an SLL
+ /// conflict, followed by LL prediction also reaching a conflict state
+ /// (indicating a true ambiguity in the grammar).
+ ///
+ /// <para>
+ /// This event may be reported during SLL prediction in cases where the
+ /// conflicting SLL configuration set provides sufficient information to
+ /// determine that the SLL conflict is truly an ambiguity. For example, if none
+ /// of the ATN configurations in the conflicting SLL configuration set have
+ /// traversed a global follow transition (i.e.
+ /// <seealso cref="ATNConfig#reachesIntoOuterContext"/> is 0 for all configurations), then
+ /// the result of SLL prediction for that input is known to be equivalent to the
+ /// result of LL prediction for that input.</para>
+ ///
+ /// <para>
+ /// In some cases, the minimum represented alternative in the conflicting LL
+ /// configuration set is not equal to the minimum represented alternative in the
+ /// conflicting SLL configuration set. Grammars and inputs which result in this
+ /// scenario are unable to use <seealso cref="PredictionMode#SLL"/>, which in turn means
+ /// they cannot use the two-stage parsing strategy to improve parsing performance
+ /// for that input.</para>
+ /// </summary>
+ /// <seealso cref= ParserATNSimulator#reportAmbiguity </seealso>
+ /// <seealso cref= ANTLRErrorListener#reportAmbiguity
+ ///
+ /// @since 4.3 </seealso>
+ class ANTLR4CPP_PUBLIC AmbiguityInfo : public DecisionEventInfo {
+ public:
+ /// The set of alternative numbers for this decision event that lead to a valid parse.
+ antlrcpp::BitSet ambigAlts;
+
+ /// <summary>
+ /// Constructs a new instance of the <seealso cref="AmbiguityInfo"/> class with the
+ /// specified detailed ambiguity information.
+ /// </summary>
+ /// <param name="decision"> The decision number </param>
+ /// <param name="configs"> The final configuration set identifying the ambiguous
+ /// alternatives for the current input </param>
+ /// <param name="ambigAlts"> The set of alternatives in the decision that lead to a valid parse.
+ /// The predicted alt is the min(ambigAlts) </param>
+ /// <param name="input"> The input token stream </param>
+ /// <param name="startIndex"> The start index for the current prediction </param>
+ /// <param name="stopIndex"> The index at which the ambiguity was identified during
+ /// prediction </param>
+ /// <param name="fullCtx"> {@code true} if the ambiguity was identified during LL
+ /// prediction; otherwise, {@code false} if the ambiguity was identified
+ /// during SLL prediction </param>
+ AmbiguityInfo(size_t decision, ATNConfigSet *configs, const antlrcpp::BitSet &ambigAlts, TokenStream *input,
+ size_t startIndex, size_t stopIndex, bool fullCtx);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ArrayPredictionContext.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ArrayPredictionContext.cpp
new file mode 100644
index 0000000000..e9478001b4
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ArrayPredictionContext.cpp
@@ -0,0 +1,109 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ArrayPredictionContext.h"
+
+#include <cstring>
+
+#include "atn/SingletonPredictionContext.h"
+#include "misc/MurmurHash.h"
+#include "support/Casts.h"
+
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+using namespace antlrcpp;
+
+namespace {
+
+ bool cachedHashCodeEqual(size_t lhs, size_t rhs) {
+ return lhs == rhs || lhs == 0 || rhs == 0;
+ }
+
+ bool predictionContextEqual(const Ref<const PredictionContext> &lhs, const Ref<const PredictionContext> &rhs) {
+ return *lhs == *rhs;
+ }
+
+}
+
+ArrayPredictionContext::ArrayPredictionContext(const SingletonPredictionContext &predictionContext)
+ : ArrayPredictionContext({ predictionContext.parent }, { predictionContext.returnState }) {}
+
+ArrayPredictionContext::ArrayPredictionContext(std::vector<Ref<const PredictionContext>> parents,
+ std::vector<size_t> returnStates)
+ : PredictionContext(PredictionContextType::ARRAY), parents(std::move(parents)), returnStates(std::move(returnStates)) {
+ assert(this->parents.size() > 0);
+ assert(this->returnStates.size() > 0);
+ assert(this->parents.size() == this->returnStates.size());
+}
+
+bool ArrayPredictionContext::isEmpty() const {
+ // Since EMPTY_RETURN_STATE can only appear in the last position, we don't need to verify that size == 1.
+ return returnStates[0] == EMPTY_RETURN_STATE;
+}
+
+size_t ArrayPredictionContext::size() const {
+ return returnStates.size();
+}
+
+const Ref<const PredictionContext>& ArrayPredictionContext::getParent(size_t index) const {
+ return parents[index];
+}
+
+size_t ArrayPredictionContext::getReturnState(size_t index) const {
+ return returnStates[index];
+}
+
+size_t ArrayPredictionContext::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getContextType()));
+ for (const auto &parent : parents) {
+ hash = MurmurHash::update(hash, parent);
+ }
+ for (const auto &returnState : returnStates) {
+ hash = MurmurHash::update(hash, returnState);
+ }
+ return MurmurHash::finish(hash, 1 + parents.size() + returnStates.size());
+}
+
+bool ArrayPredictionContext::equals(const PredictionContext &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ if (getContextType() != other.getContextType()) {
+ return false;
+ }
+ const auto &array = downCast<const ArrayPredictionContext&>(other);
+ return returnStates.size() == array.returnStates.size() &&
+ parents.size() == array.parents.size() &&
+ cachedHashCodeEqual(cachedHashCode(), array.cachedHashCode()) &&
+ std::memcmp(returnStates.data(), array.returnStates.data(), returnStates.size() * sizeof(decltype(returnStates)::value_type)) == 0 &&
+ std::equal(parents.begin(), parents.end(), array.parents.begin(), predictionContextEqual);
+}
+
+std::string ArrayPredictionContext::toString() const {
+ if (isEmpty()) {
+ return "[]";
+ }
+
+ std::stringstream ss;
+ ss << "[";
+ for (size_t i = 0; i < returnStates.size(); i++) {
+ if (i > 0) {
+ ss << ", ";
+ }
+ if (returnStates[i] == EMPTY_RETURN_STATE) {
+ ss << "$";
+ continue;
+ }
+ ss << returnStates[i];
+ if (parents[i] != nullptr) {
+ ss << " " << parents[i]->toString();
+ } else {
+ ss << "nul";
+ }
+ }
+ ss << "]";
+ return ss.str();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ArrayPredictionContext.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ArrayPredictionContext.h
new file mode 100644
index 0000000000..f43db98a01
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ArrayPredictionContext.h
@@ -0,0 +1,51 @@
+
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/PredictionContext.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class SingletonPredictionContext;
+
+ class ANTLR4CPP_PUBLIC ArrayPredictionContext final : public PredictionContext {
+ public:
+ static bool is(const PredictionContext &predictionContext) { return predictionContext.getContextType() == PredictionContextType::ARRAY; }
+
+ static bool is(const PredictionContext *predictionContext) { return predictionContext != nullptr && is(*predictionContext); }
+
+ /// Parent can be empty only if full ctx mode and we make an array
+ /// from EMPTY and non-empty. We merge EMPTY by using null parent and
+ /// returnState == EMPTY_RETURN_STATE.
+ // Also here: we use a strong reference to our parents to avoid having them freed prematurely.
+ // See also SinglePredictionContext.
+ std::vector<Ref<const PredictionContext>> parents;
+
+ /// Sorted for merge, no duplicates; if present, EMPTY_RETURN_STATE is always last.
+ std::vector<size_t> returnStates;
+
+ explicit ArrayPredictionContext(const SingletonPredictionContext &predictionContext);
+
+ ArrayPredictionContext(std::vector<Ref<const PredictionContext>> parents, std::vector<size_t> returnStates);
+
+ ArrayPredictionContext(ArrayPredictionContext&&) = default;
+
+ bool isEmpty() const override;
+ size_t size() const override;
+ const Ref<const PredictionContext>& getParent(size_t index) const override;
+ size_t getReturnState(size_t index) const override;
+ bool equals(const PredictionContext &other) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/AtomTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/AtomTransition.cpp
new file mode 100644
index 0000000000..74153bf5cd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/AtomTransition.cpp
@@ -0,0 +1,27 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/IntervalSet.h"
+#include "atn/Transition.h"
+
+#include "atn/AtomTransition.h"
+
+using namespace antlr4::misc;
+using namespace antlr4::atn;
+
+AtomTransition::AtomTransition(ATNState *target, size_t label) : Transition(TransitionType::ATOM, target), _label(label) {
+}
+
+IntervalSet AtomTransition::label() const {
+ return IntervalSet::of((int)_label);
+}
+
+bool AtomTransition::matches(size_t symbol, size_t /*minVocabSymbol*/, size_t /*maxVocabSymbol*/) const {
+ return _label == symbol;
+}
+
+std::string AtomTransition::toString() const {
+ return "ATOM " + Transition::toString() + " { label: " + std::to_string(_label) + " }";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/AtomTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/AtomTransition.h
new file mode 100644
index 0000000000..db62a7feab
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/AtomTransition.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// TODO: make all transitions sets? no, should remove set edges.
+ class ANTLR4CPP_PUBLIC AtomTransition final : public Transition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::ATOM; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ /// The token type or character value; or, signifies special label.
+ /// TODO: rename this to label
+ const size_t _label;
+
+ AtomTransition(ATNState *target, size_t label);
+
+ virtual misc::IntervalSet label() const override;
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+
+ virtual std::string toString() const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/BasicBlockStartState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/BasicBlockStartState.h
new file mode 100644
index 0000000000..1c462ec0eb
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/BasicBlockStartState.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+#include "atn/BlockStartState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC BasicBlockStartState final : public BlockStartState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::BLOCK_START; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ BasicBlockStartState() : BlockStartState(ATNStateType::BLOCK_START) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/BasicState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/BasicState.h
new file mode 100644
index 0000000000..7f8a9ef0dd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/BasicState.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC BasicState final : public ATNState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::BASIC; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ BasicState() : ATNState(ATNStateType::BASIC) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/BlockEndState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/BlockEndState.h
new file mode 100644
index 0000000000..11ef5499ba
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/BlockEndState.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// Terminal node of a simple {@code (a|b|c)} block.
+ class ANTLR4CPP_PUBLIC BlockEndState final : public ATNState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::BLOCK_END; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ BlockStartState *startState = nullptr;
+
+ BlockEndState() : ATNState(ATNStateType::BLOCK_END) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/BlockStartState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/BlockStartState.h
new file mode 100644
index 0000000000..3475115894
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/BlockStartState.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// The start of a regular {@code (...)} block.
+ class ANTLR4CPP_PUBLIC BlockStartState : public DecisionState {
+ public:
+ static bool is(const ATNState &atnState) {
+ const auto stateType = atnState.getStateType();
+ return stateType >= ATNStateType::BLOCK_START && stateType <= ATNStateType::STAR_BLOCK_START;
+ }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ BlockEndState *endState = nullptr;
+
+ protected:
+ using DecisionState::DecisionState;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ContextSensitivityInfo.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ContextSensitivityInfo.cpp
new file mode 100644
index 0000000000..12442a9bc0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ContextSensitivityInfo.cpp
@@ -0,0 +1,14 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ContextSensitivityInfo.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+ContextSensitivityInfo::ContextSensitivityInfo(size_t decision, ATNConfigSet *configs, TokenStream *input,
+ size_t startIndex, size_t stopIndex)
+ : DecisionEventInfo(decision, configs, input, startIndex, stopIndex, true) {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ContextSensitivityInfo.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ContextSensitivityInfo.h
new file mode 100644
index 0000000000..430ce3b6e8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ContextSensitivityInfo.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionEventInfo.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// This class represents profiling event information for a context sensitivity.
+ /// Context sensitivities are decisions where a particular input resulted in an
+ /// SLL conflict, but LL prediction produced a single unique alternative.
+ ///
+ /// <para>
+ /// In some cases, the unique alternative identified by LL prediction is not
+ /// equal to the minimum represented alternative in the conflicting SLL
+ /// configuration set. Grammars and inputs which result in this scenario are
+ /// unable to use <seealso cref="PredictionMode#SLL"/>, which in turn means they cannot use
+ /// the two-stage parsing strategy to improve parsing performance for that
+ /// input.</para>
+ /// </summary>
+ /// <seealso cref= ParserATNSimulator#reportContextSensitivity </seealso>
+ /// <seealso cref= ANTLRErrorListener#reportContextSensitivity
+ ///
+ /// @since 4.3 </seealso>
+ class ANTLR4CPP_PUBLIC ContextSensitivityInfo : public DecisionEventInfo {
+ public:
+ /// <summary>
+ /// Constructs a new instance of the <seealso cref="ContextSensitivityInfo"/> class
+ /// with the specified detailed context sensitivity information.
+ /// </summary>
+ /// <param name="decision"> The decision number </param>
+ /// <param name="configs"> The final configuration set containing the unique
+ /// alternative identified by full-context prediction </param>
+ /// <param name="input"> The input token stream </param>
+ /// <param name="startIndex"> The start index for the current prediction </param>
+ /// <param name="stopIndex"> The index at which the context sensitivity was
+ /// identified during full-context prediction </param>
+ ContextSensitivityInfo(size_t decision, ATNConfigSet *configs, TokenStream *input, size_t startIndex, size_t stopIndex);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionEventInfo.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionEventInfo.cpp
new file mode 100644
index 0000000000..bca6c778c0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionEventInfo.cpp
@@ -0,0 +1,14 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/DecisionEventInfo.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+DecisionEventInfo::DecisionEventInfo(size_t decision, ATNConfigSet *configs, TokenStream *input, size_t startIndex,
+ size_t stopIndex, bool fullCtx)
+ : decision(decision), configs(configs), input(input), startIndex(startIndex), stopIndex(stopIndex), fullCtx(fullCtx) {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionEventInfo.h b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionEventInfo.h
new file mode 100644
index 0000000000..af7f5f4b17
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionEventInfo.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// This is the base class for gathering detailed information about prediction
+ /// events which occur during parsing.
+ ///
+ /// Note that we could record the parser call stack at the time this event
+ /// occurred but in the presence of left recursive rules, the stack is kind of
+ /// meaningless. It's better to look at the individual configurations for their
+ /// individual stacks. Of course that is a <seealso cref="PredictionContext"/> object
+ /// not a parse tree node and so it does not have information about the extent
+ /// (start...stop) of the various subtrees. Examining the stack tops of all
+ /// configurations provide the return states for the rule invocations.
+ /// From there you can get the enclosing rule.
+ ///
+ /// @since 4.3
+ /// </summary>
+ class ANTLR4CPP_PUBLIC DecisionEventInfo {
+ public:
+ /// <summary>
+ /// The invoked decision number which this event is related to.
+ /// </summary>
+ /// <seealso cref= ATN#decisionToState </seealso>
+ const size_t decision;
+
+ /// <summary>
+ /// The configuration set containing additional information relevant to the
+ /// prediction state when the current event occurred, or {@code null} if no
+ /// additional information is relevant or available.
+ /// </summary>
+ const ATNConfigSet *configs;
+
+ /// <summary>
+ /// The input token stream which is being parsed.
+ /// </summary>
+ const TokenStream *input;
+
+ /// <summary>
+ /// The token index in the input stream at which the current prediction was
+ /// originally invoked.
+ /// </summary>
+ const size_t startIndex;
+
+ /// <summary>
+ /// The token index in the input stream at which the current event occurred.
+ /// </summary>
+ const size_t stopIndex;
+
+ /// <summary>
+ /// {@code true} if the current event occurred during LL prediction;
+ /// otherwise, {@code false} if the input occurred during SLL prediction.
+ /// </summary>
+ const bool fullCtx;
+
+ DecisionEventInfo(size_t decision, ATNConfigSet *configs, TokenStream *input, size_t startIndex,
+ size_t stopIndex, bool fullCtx);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionInfo.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionInfo.cpp
new file mode 100644
index 0000000000..ee9b1aac34
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionInfo.cpp
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ErrorInfo.h"
+#include "atn/LookaheadEventInfo.h"
+
+#include "atn/DecisionInfo.h"
+
+using namespace antlr4::atn;
+
+DecisionInfo::DecisionInfo(size_t decision) : decision(decision) {
+}
+
+std::string DecisionInfo::toString() const {
+ std::stringstream ss;
+
+ ss << "{decision=" << decision << ", contextSensitivities=" << contextSensitivities.size() << ", errors=";
+ ss << errors.size() << ", ambiguities=" << ambiguities.size() << ", SLL_lookahead=" << SLL_TotalLook;
+ ss << ", SLL_ATNTransitions=" << SLL_ATNTransitions << ", SLL_DFATransitions=" << SLL_DFATransitions;
+ ss << ", LL_Fallback=" << LL_Fallback << ", LL_lookahead=" << LL_TotalLook << ", LL_ATNTransitions=" << LL_ATNTransitions << '}';
+
+ return ss.str();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionInfo.h b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionInfo.h
new file mode 100644
index 0000000000..2b43ad8be9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionInfo.h
@@ -0,0 +1,227 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ContextSensitivityInfo.h"
+#include "atn/AmbiguityInfo.h"
+#include "atn/PredicateEvalInfo.h"
+#include "atn/ErrorInfo.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class LookaheadEventInfo;
+
+ /// <summary>
+ /// This class contains profiling gathered for a particular decision.
+ ///
+ /// <para>
+ /// Parsing performance in ANTLR 4 is heavily influenced by both static factors
+ /// (e.g. the form of the rules in the grammar) and dynamic factors (e.g. the
+ /// choice of input and the state of the DFA cache at the time profiling
+ /// operations are started). For best results, gather and use aggregate
+ /// statistics from a large sample of inputs representing the inputs expected in
+ /// production before using the results to make changes in the grammar.</para>
+ ///
+ /// @since 4.3
+ /// </summary>
+ class ANTLR4CPP_PUBLIC DecisionInfo {
+ public:
+ /// <summary>
+ /// The decision number, which is an index into <seealso cref="ATN#decisionToState"/>.
+ /// </summary>
+ const size_t decision;
+
+ /// <summary>
+ /// The total number of times <seealso cref="ParserATNSimulator#adaptivePredict"/> was
+ /// invoked for this decision.
+ /// </summary>
+ long long invocations = 0;
+
+ /// <summary>
+ /// The total time spent in <seealso cref="ParserATNSimulator#adaptivePredict"/> for
+ /// this decision, in nanoseconds.
+ ///
+ /// <para>
+ /// The value of this field contains the sum of differential results obtained
+ /// by <seealso cref="System#nanoTime()"/>, and is not adjusted to compensate for JIT
+ /// and/or garbage collection overhead. For best accuracy, use a modern JVM
+ /// implementation that provides precise results from
+ /// <seealso cref="System#nanoTime()"/>, and perform profiling in a separate process
+ /// which is warmed up by parsing the input prior to profiling. If desired,
+ /// call <seealso cref="ATNSimulator#clearDFA"/> to reset the DFA cache to its initial
+ /// state before starting the profiling measurement pass.</para>
+ /// </summary>
+ long long timeInPrediction = 0;
+
+ /// <summary>
+ /// The sum of the lookahead required for SLL prediction for this decision.
+ /// Note that SLL prediction is used before LL prediction for performance
+ /// reasons even when <seealso cref="PredictionMode#LL"/> or
+ /// <seealso cref="PredictionMode#LL_EXACT_AMBIG_DETECTION"/> is used.
+ /// </summary>
+ long long SLL_TotalLook = 0;
+
+ /// <summary>
+ /// Gets the minimum lookahead required for any single SLL prediction to
+ /// complete for this decision, by reaching a unique prediction, reaching an
+ /// SLL conflict state, or encountering a syntax error.
+ /// </summary>
+ long long SLL_MinLook = 0;
+
+ /// <summary>
+ /// Gets the maximum lookahead required for any single SLL prediction to
+ /// complete for this decision, by reaching a unique prediction, reaching an
+ /// SLL conflict state, or encountering a syntax error.
+ /// </summary>
+ long long SLL_MaxLook = 0;
+
+ /// Gets the <seealso cref="LookaheadEventInfo"/> associated with the event where the
+ /// <seealso cref="#SLL_MaxLook"/> value was set.
+ Ref<LookaheadEventInfo> SLL_MaxLookEvent;
+
+ /// <summary>
+ /// The sum of the lookahead required for LL prediction for this decision.
+ /// Note that LL prediction is only used when SLL prediction reaches a
+ /// conflict state.
+ /// </summary>
+ long long LL_TotalLook = 0;
+
+ /// <summary>
+ /// Gets the minimum lookahead required for any single LL prediction to
+ /// complete for this decision. An LL prediction completes when the algorithm
+ /// reaches a unique prediction, a conflict state (for
+ /// <seealso cref="PredictionMode#LL"/>, an ambiguity state (for
+ /// <seealso cref="PredictionMode#LL_EXACT_AMBIG_DETECTION"/>, or a syntax error.
+ /// </summary>
+ long long LL_MinLook = 0;
+
+ /// <summary>
+ /// Gets the maximum lookahead required for any single LL prediction to
+ /// complete for this decision. An LL prediction completes when the algorithm
+ /// reaches a unique prediction, a conflict state (for
+ /// <seealso cref="PredictionMode#LL"/>, an ambiguity state (for
+ /// <seealso cref="PredictionMode#LL_EXACT_AMBIG_DETECTION"/>, or a syntax error.
+ /// </summary>
+ long long LL_MaxLook = 0;
+
+ /// <summary>
+ /// Gets the <seealso cref="LookaheadEventInfo"/> associated with the event where the
+ /// <seealso cref="#LL_MaxLook"/> value was set.
+ /// </summary>
+ Ref<LookaheadEventInfo> LL_MaxLookEvent;
+
+ /// <summary>
+ /// A collection of <seealso cref="ContextSensitivityInfo"/> instances describing the
+ /// context sensitivities encountered during LL prediction for this decision.
+ /// </summary>
+ /// <seealso cref= ContextSensitivityInfo </seealso>
+ std::vector<ContextSensitivityInfo> contextSensitivities;
+
+ /// <summary>
+ /// A collection of <seealso cref="ErrorInfo"/> instances describing the parse errors
+ /// identified during calls to <seealso cref="ParserATNSimulator#adaptivePredict"/> for
+ /// this decision.
+ /// </summary>
+ /// <seealso cref= ErrorInfo </seealso>
+ std::vector<ErrorInfo> errors;
+
+ /// <summary>
+ /// A collection of <seealso cref="AmbiguityInfo"/> instances describing the
+ /// ambiguities encountered during LL prediction for this decision.
+ /// </summary>
+ /// <seealso cref= AmbiguityInfo </seealso>
+ std::vector<AmbiguityInfo> ambiguities;
+
+ /// <summary>
+ /// A collection of <seealso cref="PredicateEvalInfo"/> instances describing the
+ /// results of evaluating individual predicates during prediction for this
+ /// decision.
+ /// </summary>
+ /// <seealso cref= PredicateEvalInfo </seealso>
+ std::vector<PredicateEvalInfo> predicateEvals;
+
+ /// <summary>
+ /// The total number of ATN transitions required during SLL prediction for
+ /// this decision. An ATN transition is determined by the number of times the
+ /// DFA does not contain an edge that is required for prediction, resulting
+ /// in on-the-fly computation of that edge.
+ ///
+ /// <para>
+ /// If DFA caching of SLL transitions is employed by the implementation, ATN
+ /// computation may cache the computed edge for efficient lookup during
+ /// future parsing of this decision. Otherwise, the SLL parsing algorithm
+ /// will use ATN transitions exclusively.</para>
+ /// </summary>
+ /// <seealso cref= #SLL_ATNTransitions </seealso>
+ /// <seealso cref= ParserATNSimulator#computeTargetState </seealso>
+ /// <seealso cref= LexerATNSimulator#computeTargetState </seealso>
+ long long SLL_ATNTransitions = 0;
+
+ /// <summary>
+ /// The total number of DFA transitions required during SLL prediction for
+ /// this decision.
+ ///
+ /// <para>If the ATN simulator implementation does not use DFA caching for SLL
+ /// transitions, this value will be 0.</para>
+ /// </summary>
+ /// <seealso cref= ParserATNSimulator#getExistingTargetState </seealso>
+ /// <seealso cref= LexerATNSimulator#getExistingTargetState </seealso>
+ long long SLL_DFATransitions = 0;
+
+ /// <summary>
+ /// Gets the total number of times SLL prediction completed in a conflict
+ /// state, resulting in fallback to LL prediction.
+ ///
+ /// <para>Note that this value is not related to whether or not
+ /// <seealso cref="PredictionMode#SLL"/> may be used successfully with a particular
+ /// grammar. If the ambiguity resolution algorithm applied to the SLL
+ /// conflicts for this decision produce the same result as LL prediction for
+ /// this decision, <seealso cref="PredictionMode#SLL"/> would produce the same overall
+ /// parsing result as <seealso cref="PredictionMode#LL"/>.</para>
+ /// </summary>
+ long long LL_Fallback = 0;
+
+ /// <summary>
+ /// The total number of ATN transitions required during LL prediction for
+ /// this decision. An ATN transition is determined by the number of times the
+ /// DFA does not contain an edge that is required for prediction, resulting
+ /// in on-the-fly computation of that edge.
+ ///
+ /// <para>
+ /// If DFA caching of LL transitions is employed by the implementation, ATN
+ /// computation may cache the computed edge for efficient lookup during
+ /// future parsing of this decision. Otherwise, the LL parsing algorithm will
+ /// use ATN transitions exclusively.</para>
+ /// </summary>
+ /// <seealso cref= #LL_DFATransitions </seealso>
+ /// <seealso cref= ParserATNSimulator#computeTargetState </seealso>
+ /// <seealso cref= LexerATNSimulator#computeTargetState </seealso>
+ long long LL_ATNTransitions = 0;
+
+ /// <summary>
+ /// The total number of DFA transitions required during LL prediction for
+ /// this decision.
+ ///
+ /// <para>If the ATN simulator implementation does not use DFA caching for LL
+ /// transitions, this value will be 0.</para>
+ /// </summary>
+ /// <seealso cref= ParserATNSimulator#getExistingTargetState </seealso>
+ /// <seealso cref= LexerATNSimulator#getExistingTargetState </seealso>
+ long long LL_DFATransitions = 0;
+
+ /// <summary>
+ /// Constructs a new instance of the <seealso cref="DecisionInfo"/> class to contain
+ /// statistics for a particular decision.
+ /// </summary>
+ /// <param name="decision"> The decision number </param>
+ explicit DecisionInfo(size_t decision);
+
+ std::string toString() const;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionState.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionState.cpp
new file mode 100644
index 0000000000..72adb210f5
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionState.cpp
@@ -0,0 +1,12 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/DecisionState.h"
+
+using namespace antlr4::atn;
+
+std::string DecisionState::toString() const {
+ return "DECISION " + ATNState::toString();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionState.h
new file mode 100644
index 0000000000..b7341ac6c9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/DecisionState.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC DecisionState : public ATNState {
+ public:
+ static bool is(const ATNState &atnState) {
+ const auto stateType = atnState.getStateType();
+ return (stateType >= ATNStateType::BLOCK_START && stateType <= ATNStateType::TOKEN_START) ||
+ stateType == ATNStateType::PLUS_LOOP_BACK ||
+ stateType == ATNStateType::STAR_LOOP_ENTRY;
+ }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ int decision = -1;
+ bool nonGreedy = false;
+
+ virtual std::string toString() const override;
+
+ protected:
+ using ATNState::ATNState;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/EpsilonTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/EpsilonTransition.cpp
new file mode 100644
index 0000000000..503fb1630e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/EpsilonTransition.cpp
@@ -0,0 +1,31 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/EpsilonTransition.h"
+
+using namespace antlr4::atn;
+
+EpsilonTransition::EpsilonTransition(ATNState *target) : EpsilonTransition(target, INVALID_INDEX) {
+}
+
+EpsilonTransition::EpsilonTransition(ATNState *target, size_t outermostPrecedenceReturn)
+ : Transition(TransitionType::EPSILON, target), _outermostPrecedenceReturn(outermostPrecedenceReturn) {
+}
+
+size_t EpsilonTransition::outermostPrecedenceReturn() const {
+ return _outermostPrecedenceReturn;
+}
+
+bool EpsilonTransition::isEpsilon() const {
+ return true;
+}
+
+bool EpsilonTransition::matches(size_t /*symbol*/, size_t /*minVocabSymbol*/, size_t /*maxVocabSymbol*/) const {
+ return false;
+}
+
+std::string EpsilonTransition::toString() const {
+ return "EPSILON " + Transition::toString() + " {}";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/EpsilonTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/EpsilonTransition.h
new file mode 100644
index 0000000000..21bc812822
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/EpsilonTransition.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC EpsilonTransition final : public Transition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::EPSILON; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ explicit EpsilonTransition(ATNState *target);
+ EpsilonTransition(ATNState *target, size_t outermostPrecedenceReturn);
+
+ /**
+ * @return the rule index of a precedence rule for which this transition is
+ * returning from, where the precedence value is 0; otherwise, INVALID_INDEX.
+ *
+ * @see ATNConfig#isPrecedenceFilterSuppressed()
+ * @see ParserATNSimulator#applyPrecedenceFilter(ATNConfigSet)
+ * @since 4.4.1
+ */
+ size_t outermostPrecedenceReturn() const;
+
+ virtual bool isEpsilon() const override;
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+
+ virtual std::string toString() const override;
+
+ private:
+ const size_t _outermostPrecedenceReturn; // A rule index.
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ErrorInfo.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ErrorInfo.cpp
new file mode 100644
index 0000000000..efe8507124
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ErrorInfo.cpp
@@ -0,0 +1,15 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATNConfigSet.h"
+
+#include "atn/ErrorInfo.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+ErrorInfo::ErrorInfo(size_t decision, ATNConfigSet *configs, TokenStream *input, size_t startIndex, size_t stopIndex, bool fullCtx)
+ : DecisionEventInfo(decision, configs, input, startIndex, stopIndex, fullCtx) {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ErrorInfo.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ErrorInfo.h
new file mode 100644
index 0000000000..d34642a195
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ErrorInfo.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionEventInfo.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// This class represents profiling event information for a syntax error
+ /// identified during prediction. Syntax errors occur when the prediction
+ /// algorithm is unable to identify an alternative which would lead to a
+ /// successful parse.
+ /// </summary>
+ /// <seealso cref= Parser#notifyErrorListeners(Token, String, RecognitionException) </seealso>
+ /// <seealso cref= ANTLRErrorListener#syntaxError
+ ///
+ /// @since 4.3 </seealso>
+ class ANTLR4CPP_PUBLIC ErrorInfo : public DecisionEventInfo {
+ public:
+ /// <summary>
+ /// Constructs a new instance of the <seealso cref="ErrorInfo"/> class with the
+ /// specified detailed syntax error information.
+ /// </summary>
+ /// <param name="decision"> The decision number </param>
+ /// <param name="configs"> The final configuration set reached during prediction
+ /// prior to reaching the <seealso cref="ATNSimulator#ERROR"/> state </param>
+ /// <param name="input"> The input token stream </param>
+ /// <param name="startIndex"> The start index for the current prediction </param>
+ /// <param name="stopIndex"> The index at which the syntax error was identified </param>
+ /// <param name="fullCtx"> {@code true} if the syntax error was identified during LL
+ /// prediction; otherwise, {@code false} if the syntax error was identified
+ /// during SLL prediction </param>
+ ErrorInfo(size_t decision, ATNConfigSet *configs, TokenStream *input, size_t startIndex, size_t stopIndex,
+ bool fullCtx);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LL1Analyzer.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LL1Analyzer.cpp
new file mode 100644
index 0000000000..1d43697584
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LL1Analyzer.cpp
@@ -0,0 +1,189 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/RuleStopState.h"
+#include "atn/Transition.h"
+#include "atn/RuleTransition.h"
+#include "atn/SingletonPredictionContext.h"
+#include "atn/WildcardTransition.h"
+#include "atn/NotSetTransition.h"
+#include "misc/IntervalSet.h"
+#include "atn/ATNConfig.h"
+
+#include "support/CPPUtils.h"
+
+#include "atn/LL1Analyzer.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+namespace {
+
+ struct ATNConfigHasher final {
+ size_t operator()(const ATNConfig& atn_config) const {
+ return atn_config.hashCode();
+ }
+ };
+
+ struct ATNConfigComparer final {
+ bool operator()(const ATNConfig& lhs, const ATNConfig& rhs) const {
+ return lhs == rhs;
+ }
+ };
+
+ class LL1AnalyzerImpl final {
+ public:
+ LL1AnalyzerImpl(const ATN& atn, misc::IntervalSet& look, bool seeThruPreds, bool addEOF) : _atn(atn), _look(look), _seeThruPreds(seeThruPreds), _addEOF(addEOF) {}
+
+ /// <summary>
+ /// Compute set of tokens that can follow {@code s} in the ATN in the
+ /// specified {@code ctx}.
+ /// <p/>
+ /// If {@code ctx} is {@code null} and {@code stopState} or the end of the
+ /// rule containing {@code s} is reached, <seealso cref="Token#EPSILON"/> is added to
+ /// the result set. If {@code ctx} is not {@code null} and {@code addEOF} is
+ /// {@code true} and {@code stopState} or the end of the outermost rule is
+ /// reached, <seealso cref="Token#EOF"/> is added to the result set.
+ /// </summary>
+ /// <param name="s"> the ATN state. </param>
+ /// <param name="stopState"> the ATN state to stop at. This can be a
+ /// <seealso cref="BlockEndState"/> to detect epsilon paths through a closure. </param>
+ /// <param name="ctx"> The outer context, or {@code null} if the outer context should
+ /// not be used. </param>
+ /// <param name="look"> The result lookahead set. </param>
+ /// <param name="lookBusy"> A set used for preventing epsilon closures in the ATN
+ /// from causing a stack overflow. Outside code should pass
+ /// {@code new HashSet<ATNConfig>} for this argument. </param>
+ /// <param name="calledRuleStack"> A set used for preventing left recursion in the
+ /// ATN from causing a stack overflow. Outside code should pass
+ /// {@code new BitSet()} for this argument. </param>
+ /// <param name="seeThruPreds"> {@code true} to true semantic predicates as
+ /// implicitly {@code true} and "see through them", otherwise {@code false}
+ /// to treat semantic predicates as opaque and add <seealso cref="#HIT_PRED"/> to the
+ /// result if one is encountered. </param>
+ /// <param name="addEOF"> Add <seealso cref="Token#EOF"/> to the result if the end of the
+ /// outermost context is reached. This parameter has no effect if {@code ctx}
+ /// is {@code null}. </param>
+ void LOOK(ATNState *s, ATNState *stopState, Ref<const PredictionContext> const& ctx) {
+ if (!_lookBusy.insert(ATNConfig(s, 0, ctx)).second) {
+ return;
+ }
+
+ // ml: s can never be null, hence no need to check if stopState is != null.
+ if (s == stopState) {
+ if (ctx == nullptr) {
+ _look.add(Token::EPSILON);
+ return;
+ } else if (ctx->isEmpty() && _addEOF) {
+ _look.add(Token::EOF);
+ return;
+ }
+ }
+
+ if (s->getStateType() == ATNStateType::RULE_STOP) {
+ if (ctx == nullptr) {
+ _look.add(Token::EPSILON);
+ return;
+ } else if (ctx->isEmpty() && _addEOF) {
+ _look.add(Token::EOF);
+ return;
+ }
+
+ if (ctx != PredictionContext::EMPTY) {
+ bool removed = _calledRuleStack.test(s->ruleIndex);
+ _calledRuleStack[s->ruleIndex] = false;
+ // run thru all possible stack tops in ctx
+ for (size_t i = 0; i < ctx->size(); i++) {
+ ATNState *returnState = _atn.states[ctx->getReturnState(i)];
+ LOOK(returnState, stopState, ctx->getParent(i));
+ }
+ if (removed) {
+ _calledRuleStack.set(s->ruleIndex);
+ }
+ return;
+ }
+ }
+
+ size_t n = s->transitions.size();
+ for (size_t i = 0; i < n; i++) {
+ const Transition *t = s->transitions[i].get();
+ const auto tType = t->getTransitionType();
+
+ if (tType == TransitionType::RULE) {
+ if (_calledRuleStack[(static_cast<const RuleTransition*>(t))->target->ruleIndex]) {
+ continue;
+ }
+
+ Ref<const PredictionContext> newContext = SingletonPredictionContext::create(ctx, (static_cast<const RuleTransition*>(t))->followState->stateNumber);
+
+ _calledRuleStack.set((static_cast<const RuleTransition*>(t))->target->ruleIndex);
+ LOOK(t->target, stopState, newContext);
+ _calledRuleStack[(static_cast<const RuleTransition*>(t))->target->ruleIndex] = false;
+
+ } else if (tType == TransitionType::PREDICATE || tType == TransitionType::PRECEDENCE) {
+ if (_seeThruPreds) {
+ LOOK(t->target, stopState, ctx);
+ } else {
+ _look.add(LL1Analyzer::HIT_PRED);
+ }
+ } else if (t->isEpsilon()) {
+ LOOK(t->target, stopState, ctx);
+ } else if (tType == TransitionType::WILDCARD) {
+ _look.addAll(misc::IntervalSet::of(Token::MIN_USER_TOKEN_TYPE, static_cast<ssize_t>(_atn.maxTokenType)));
+ } else {
+ misc::IntervalSet set = t->label();
+ if (!set.isEmpty()) {
+ if (tType == TransitionType::NOT_SET) {
+ set = set.complement(misc::IntervalSet::of(Token::MIN_USER_TOKEN_TYPE, static_cast<ssize_t>(_atn.maxTokenType)));
+ }
+ _look.addAll(set);
+ }
+ }
+ }
+ }
+
+ private:
+ const ATN& _atn;
+ misc::IntervalSet& _look;
+ antlrcpp::BitSet _calledRuleStack;
+ std::unordered_set<ATNConfig, ATNConfigHasher, ATNConfigComparer> _lookBusy;
+ bool _seeThruPreds;
+ bool _addEOF;
+ };
+
+}
+
+std::vector<misc::IntervalSet> LL1Analyzer::getDecisionLookahead(ATNState *s) const {
+ std::vector<misc::IntervalSet> look;
+
+ if (s == nullptr) {
+ return look;
+ }
+
+ look.resize(s->transitions.size()); // Fills all interval sets with defaults.
+ for (size_t alt = 0; alt < s->transitions.size(); alt++) {
+ LL1AnalyzerImpl impl(_atn, look[alt], false, false);
+ impl.LOOK(s->transitions[alt]->target, nullptr, PredictionContext::EMPTY);
+ // Wipe out lookahead for this alternative if we found nothing
+ // or we had a predicate when we !seeThruPreds
+ if (look[alt].size() == 0 || look[alt].contains(LL1Analyzer::HIT_PRED)) {
+ look[alt].clear();
+ }
+ }
+ return look;
+}
+
+misc::IntervalSet LL1Analyzer::LOOK(ATNState *s, RuleContext *ctx) const {
+ return LOOK(s, nullptr, ctx);
+}
+
+misc::IntervalSet LL1Analyzer::LOOK(ATNState *s, ATNState *stopState, RuleContext *ctx) const {
+ Ref<const PredictionContext> lookContext = ctx != nullptr ? PredictionContext::fromRuleContext(_atn, ctx) : nullptr;
+ misc::IntervalSet r;
+ LL1AnalyzerImpl impl(_atn, r, true, true);
+ impl.LOOK(s, stopState, lookContext);
+ return r;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LL1Analyzer.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LL1Analyzer.h
new file mode 100644
index 0000000000..7d47c7610f
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LL1Analyzer.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Token.h"
+#include "atn/ATNConfig.h"
+#include "atn/PredictionContext.h"
+#include "support/BitSet.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC LL1Analyzer final {
+ public:
+ /// Special value added to the lookahead sets to indicate that we hit
+ /// a predicate during analysis if {@code seeThruPreds==false}.
+ static constexpr size_t HIT_PRED = Token::INVALID_TYPE;
+
+ explicit LL1Analyzer(const atn::ATN &atn) : _atn(atn) {}
+
+ /// <summary>
+ /// Calculates the SLL(1) expected lookahead set for each outgoing transition
+ /// of an <seealso cref="ATNState"/>. The returned array has one element for each
+ /// outgoing transition in {@code s}. If the closure from transition
+ /// <em>i</em> leads to a semantic predicate before matching a symbol, the
+ /// element at index <em>i</em> of the result will be {@code null}.
+ /// </summary>
+ /// <param name="s"> the ATN state </param>
+ /// <returns> the expected symbols for each outgoing transition of {@code s}. </returns>
+ std::vector<misc::IntervalSet> getDecisionLookahead(ATNState *s) const;
+
+ /// <summary>
+ /// Compute set of tokens that can follow {@code s} in the ATN in the
+ /// specified {@code ctx}.
+ /// <p/>
+ /// If {@code ctx} is {@code null} and the end of the rule containing
+ /// {@code s} is reached, <seealso cref="Token#EPSILON"/> is added to the result set.
+ /// If {@code ctx} is not {@code null} and the end of the outermost rule is
+ /// reached, <seealso cref="Token#EOF"/> is added to the result set.
+ /// </summary>
+ /// <param name="s"> the ATN state </param>
+ /// <param name="ctx"> the complete parser context, or {@code null} if the context
+ /// should be ignored
+ /// </param>
+ /// <returns> The set of tokens that can follow {@code s} in the ATN in the
+ /// specified {@code ctx}. </returns>
+ misc::IntervalSet LOOK(ATNState *s, RuleContext *ctx) const;
+
+ /// <summary>
+ /// Compute set of tokens that can follow {@code s} in the ATN in the
+ /// specified {@code ctx}.
+ /// <p/>
+ /// If {@code ctx} is {@code null} and the end of the rule containing
+ /// {@code s} is reached, <seealso cref="Token#EPSILON"/> is added to the result set.
+ /// If {@code ctx} is not {@code null} and the end of the outermost rule is
+ /// reached, <seealso cref="Token#EOF"/> is added to the result set.
+ /// </summary>
+ /// <param name="s"> the ATN state </param>
+ /// <param name="stopState"> the ATN state to stop at. This can be a
+ /// <seealso cref="BlockEndState"/> to detect epsilon paths through a closure. </param>
+ /// <param name="ctx"> the complete parser context, or {@code null} if the context
+ /// should be ignored
+ /// </param>
+ /// <returns> The set of tokens that can follow {@code s} in the ATN in the
+ /// specified {@code ctx}. </returns>
+ misc::IntervalSet LOOK(ATNState *s, ATNState *stopState, RuleContext *ctx) const;
+
+ private:
+ const atn::ATN &_atn;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNConfig.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNConfig.cpp
new file mode 100644
index 0000000000..e70cfac2ca
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNConfig.cpp
@@ -0,0 +1,67 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "atn/DecisionState.h"
+#include "atn/PredictionContext.h"
+#include "SemanticContext.h"
+#include "atn/LexerActionExecutor.h"
+
+#include "support/CPPUtils.h"
+#include "support/Casts.h"
+
+#include "atn/LexerATNConfig.h"
+
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+LexerATNConfig::LexerATNConfig(ATNState *state, int alt, Ref<const PredictionContext> context)
+ : ATNConfig(state, alt, std::move(context)) {}
+
+LexerATNConfig::LexerATNConfig(ATNState *state, int alt, Ref<const PredictionContext> context, Ref<const LexerActionExecutor> lexerActionExecutor)
+ : ATNConfig(state, alt, std::move(context)), _lexerActionExecutor(std::move(lexerActionExecutor)) {}
+
+LexerATNConfig::LexerATNConfig(LexerATNConfig const& other, ATNState *state)
+ : ATNConfig(other, state), _lexerActionExecutor(other._lexerActionExecutor), _passedThroughNonGreedyDecision(checkNonGreedyDecision(other, state)) {}
+
+LexerATNConfig::LexerATNConfig(LexerATNConfig const& other, ATNState *state, Ref<const LexerActionExecutor> lexerActionExecutor)
+ : ATNConfig(other, state), _lexerActionExecutor(std::move(lexerActionExecutor)), _passedThroughNonGreedyDecision(checkNonGreedyDecision(other, state)) {}
+
+LexerATNConfig::LexerATNConfig(LexerATNConfig const& other, ATNState *state, Ref<const PredictionContext> context)
+ : ATNConfig(other, state, std::move(context)), _lexerActionExecutor(other._lexerActionExecutor), _passedThroughNonGreedyDecision(checkNonGreedyDecision(other, state)) {}
+
+size_t LexerATNConfig::hashCode() const {
+ size_t hashCode = misc::MurmurHash::initialize(7);
+ hashCode = misc::MurmurHash::update(hashCode, state->stateNumber);
+ hashCode = misc::MurmurHash::update(hashCode, alt);
+ hashCode = misc::MurmurHash::update(hashCode, context);
+ hashCode = misc::MurmurHash::update(hashCode, semanticContext);
+ hashCode = misc::MurmurHash::update(hashCode, _passedThroughNonGreedyDecision ? 1 : 0);
+ hashCode = misc::MurmurHash::update(hashCode, _lexerActionExecutor);
+ hashCode = misc::MurmurHash::finish(hashCode, 6);
+ return hashCode;
+}
+
+bool LexerATNConfig::operator==(const LexerATNConfig& other) const
+{
+ if (this == &other)
+ return true;
+
+ if (_passedThroughNonGreedyDecision != other._passedThroughNonGreedyDecision)
+ return false;
+
+ if (_lexerActionExecutor == nullptr)
+ return other._lexerActionExecutor == nullptr;
+ if (*_lexerActionExecutor != *(other._lexerActionExecutor)) {
+ return false;
+ }
+
+ return ATNConfig::operator==(other);
+}
+
+bool LexerATNConfig::checkNonGreedyDecision(LexerATNConfig const& source, ATNState *target) {
+ return source._passedThroughNonGreedyDecision ||
+ (DecisionState::is(target) && downCast<DecisionState*>(target)->nonGreedy);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNConfig.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNConfig.h
new file mode 100644
index 0000000000..7d1d6b40e2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNConfig.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNConfig.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC LexerATNConfig final : public ATNConfig {
+ public:
+ LexerATNConfig(ATNState *state, int alt, Ref<const PredictionContext> context);
+ LexerATNConfig(ATNState *state, int alt, Ref<const PredictionContext> context, Ref<const LexerActionExecutor> lexerActionExecutor);
+
+ LexerATNConfig(LexerATNConfig const& other, ATNState *state);
+ LexerATNConfig(LexerATNConfig const& other, ATNState *state, Ref<const LexerActionExecutor> lexerActionExecutor);
+ LexerATNConfig(LexerATNConfig const& other, ATNState *state, Ref<const PredictionContext> context);
+
+ /**
+ * Gets the {@link LexerActionExecutor} capable of executing the embedded
+ * action(s) for the current configuration.
+ */
+ const Ref<const LexerActionExecutor>& getLexerActionExecutor() const { return _lexerActionExecutor; }
+ bool hasPassedThroughNonGreedyDecision() const { return _passedThroughNonGreedyDecision; }
+
+ virtual size_t hashCode() const override;
+
+ bool operator==(const LexerATNConfig& other) const;
+
+ private:
+ /**
+ * This is the backing field for {@link #getLexerActionExecutor}.
+ */
+ const Ref<const LexerActionExecutor> _lexerActionExecutor;
+ const bool _passedThroughNonGreedyDecision = false;
+
+ static bool checkNonGreedyDecision(LexerATNConfig const& source, ATNState *target);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNSimulator.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNSimulator.cpp
new file mode 100644
index 0000000000..ef1b1cf2f1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNSimulator.cpp
@@ -0,0 +1,617 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "IntStream.h"
+#include "atn/OrderedATNConfigSet.h"
+#include "Token.h"
+#include "LexerNoViableAltException.h"
+#include "atn/RuleStopState.h"
+#include "atn/RuleTransition.h"
+#include "atn/SingletonPredictionContext.h"
+#include "atn/PredicateTransition.h"
+#include "atn/ActionTransition.h"
+#include "atn/TokensStartState.h"
+#include "misc/Interval.h"
+#include "dfa/DFA.h"
+#include "Lexer.h"
+#include "internal/Synchronization.h"
+
+#include "dfa/DFAState.h"
+#include "atn/LexerATNConfig.h"
+#include "atn/LexerActionExecutor.h"
+
+#include "atn/LexerATNSimulator.h"
+
+#define DEBUG_ATN 0
+#define DEBUG_DFA 0
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::internal;
+using namespace antlrcpp;
+
+void LexerATNSimulator::SimState::reset() {
+ *this = SimState();
+}
+
+LexerATNSimulator::LexerATNSimulator(const ATN &atn, std::vector<dfa::DFA> &decisionToDFA,
+ PredictionContextCache &sharedContextCache)
+ : LexerATNSimulator(nullptr, atn, decisionToDFA, sharedContextCache) {
+}
+
+LexerATNSimulator::LexerATNSimulator(Lexer *recog, const ATN &atn, std::vector<dfa::DFA> &decisionToDFA,
+ PredictionContextCache &sharedContextCache)
+ : ATNSimulator(atn, sharedContextCache), _recog(recog), _decisionToDFA(decisionToDFA) {
+ InitializeInstanceFields();
+}
+
+void LexerATNSimulator::copyState(LexerATNSimulator *simulator) {
+ _charPositionInLine = simulator->_charPositionInLine;
+ _line = simulator->_line;
+ _mode = simulator->_mode;
+ _startIndex = simulator->_startIndex;
+}
+
+size_t LexerATNSimulator::match(CharStream *input, size_t mode) {
+ _mode = mode;
+ ssize_t mark = input->mark();
+
+ auto onExit = finally([input, mark] {
+ input->release(mark);
+ });
+
+ _startIndex = input->index();
+ _prevAccept.reset();
+ const dfa::DFA &dfa = _decisionToDFA[mode];
+ dfa::DFAState* s0;
+ {
+ SharedLock<SharedMutex> stateLock(atn._stateMutex);
+ s0 = dfa.s0;
+ }
+ if (s0 == nullptr) {
+ return matchATN(input);
+ } else {
+ return execATN(input, s0);
+ }
+}
+
+void LexerATNSimulator::reset() {
+ _prevAccept.reset();
+ _startIndex = 0;
+ _line = 1;
+ _charPositionInLine = 0;
+ _mode = Lexer::DEFAULT_MODE;
+}
+
+void LexerATNSimulator::clearDFA() {
+ size_t size = _decisionToDFA.size();
+ _decisionToDFA.clear();
+ for (size_t d = 0; d < size; ++d) {
+ _decisionToDFA.emplace_back(atn.getDecisionState(d), d);
+ }
+}
+
+size_t LexerATNSimulator::matchATN(CharStream *input) {
+ ATNState *startState = atn.modeToStartState[_mode];
+
+ std::unique_ptr<ATNConfigSet> s0_closure = computeStartState(input, startState);
+
+ bool suppressEdge = s0_closure->hasSemanticContext;
+ s0_closure->hasSemanticContext = false;
+
+ dfa::DFAState *next = addDFAState(s0_closure.release(), suppressEdge);
+
+ size_t predict = execATN(input, next);
+
+ return predict;
+}
+
+size_t LexerATNSimulator::execATN(CharStream *input, dfa::DFAState *ds0) {
+ if (ds0->isAcceptState) {
+ // allow zero-length tokens
+ // ml: in Java code this method uses 3 params. The first is a member var of the class anyway (_prevAccept), so why pass it here?
+ captureSimState(input, ds0);
+ }
+
+ size_t t = input->LA(1);
+ dfa::DFAState *s = ds0; // s is current/from DFA state
+
+ while (true) { // while more work
+ // As we move src->trg, src->trg, we keep track of the previous trg to
+ // avoid looking up the DFA state again, which is expensive.
+ // If the previous target was already part of the DFA, we might
+ // be able to avoid doing a reach operation upon t. If s!=null,
+ // it means that semantic predicates didn't prevent us from
+ // creating a DFA state. Once we know s!=null, we check to see if
+ // the DFA state has an edge already for t. If so, we can just reuse
+ // it's configuration set; there's no point in re-computing it.
+ // This is kind of like doing DFA simulation within the ATN
+ // simulation because DFA simulation is really just a way to avoid
+ // computing reach/closure sets. Technically, once we know that
+ // we have a previously added DFA state, we could jump over to
+ // the DFA simulator. But, that would mean popping back and forth
+ // a lot and making things more complicated algorithmically.
+ // This optimization makes a lot of sense for loops within DFA.
+ // A character will take us back to an existing DFA state
+ // that already has lots of edges out of it. e.g., .* in comments.
+ dfa::DFAState *target = getExistingTargetState(s, t);
+ if (target == nullptr) {
+ target = computeTargetState(input, s, t);
+ }
+
+ if (target == ERROR.get()) {
+ break;
+ }
+
+ // If this is a consumable input element, make sure to consume before
+ // capturing the accept state so the input index, line, and char
+ // position accurately reflect the state of the interpreter at the
+ // end of the token.
+ if (t != Token::EOF) {
+ consume(input);
+ }
+
+ if (target->isAcceptState) {
+ captureSimState(input, target);
+ if (t == Token::EOF) {
+ break;
+ }
+ }
+
+ t = input->LA(1);
+ s = target; // flip; current DFA target becomes new src/from state
+ }
+
+ return failOrAccept(input, s->configs.get(), t);
+}
+
+dfa::DFAState *LexerATNSimulator::getExistingTargetState(dfa::DFAState *s, size_t t) {
+ dfa::DFAState* retval = nullptr;
+ SharedLock<SharedMutex> edgeLock(atn._edgeMutex);
+ if (t <= MAX_DFA_EDGE) {
+ auto iterator = s->edges.find(t - MIN_DFA_EDGE);
+#if DEBUG_ATN == 1
+ if (iterator != s->edges.end()) {
+ std::cout << std::string("reuse state ") << s->stateNumber << std::string(" edge to ") << iterator->second->stateNumber << std::endl;
+ }
+#endif
+
+ if (iterator != s->edges.end())
+ retval = iterator->second;
+ }
+ return retval;
+}
+
+dfa::DFAState *LexerATNSimulator::computeTargetState(CharStream *input, dfa::DFAState *s, size_t t) {
+ OrderedATNConfigSet *reach = new OrderedATNConfigSet(); /* mem-check: deleted on error or managed by new DFA state. */
+
+ // if we don't find an existing DFA state
+ // Fill reach starting from closure, following t transitions
+ getReachableConfigSet(input, s->configs.get(), reach, t);
+
+ if (reach->isEmpty()) { // we got nowhere on t from s
+ if (!reach->hasSemanticContext) {
+ // we got nowhere on t, don't throw out this knowledge; it'd
+ // cause a failover from DFA later.
+ addDFAEdge(s, t, ERROR.get());
+ }
+ delete reach;
+
+ // stop when we can't match any more char
+ return ERROR.get();
+ }
+
+ // Add an edge from s to target DFA found/created for reach
+ return addDFAEdge(s, t, reach);
+}
+
+size_t LexerATNSimulator::failOrAccept(CharStream *input, ATNConfigSet *reach, size_t t) {
+ if (_prevAccept.dfaState != nullptr) {
+ accept(input, _prevAccept.dfaState->lexerActionExecutor, _startIndex, _prevAccept.index, _prevAccept.line, _prevAccept.charPos);
+ return _prevAccept.dfaState->prediction;
+ } else {
+ // if no accept and EOF is first char, return EOF
+ if (t == Token::EOF && input->index() == _startIndex) {
+ return Token::EOF;
+ }
+
+ throw LexerNoViableAltException(_recog, input, _startIndex, reach);
+ }
+}
+
+void LexerATNSimulator::getReachableConfigSet(CharStream *input, ATNConfigSet *closure_, ATNConfigSet *reach, size_t t) {
+ // this is used to skip processing for configs which have a lower priority
+ // than a config that already reached an accept state for the same rule
+ size_t skipAlt = ATN::INVALID_ALT_NUMBER;
+
+ for (const auto &c : closure_->configs) {
+ bool currentAltReachedAcceptState = c->alt == skipAlt;
+ if (currentAltReachedAcceptState && (std::static_pointer_cast<LexerATNConfig>(c))->hasPassedThroughNonGreedyDecision()) {
+ continue;
+ }
+
+#if DEBUG_ATN == 1
+ std::cout << "testing " << getTokenName((int)t) << " at " << c->toString(true) << std::endl;
+#endif
+
+ size_t n = c->state->transitions.size();
+ for (size_t ti = 0; ti < n; ti++) { // for each transition
+ const Transition *trans = c->state->transitions[ti].get();
+ ATNState *target = getReachableTarget(trans, (int)t);
+ if (target != nullptr) {
+ auto lexerActionExecutor = downCast<const LexerATNConfig&>(*c).getLexerActionExecutor();
+ if (lexerActionExecutor != nullptr) {
+ lexerActionExecutor = lexerActionExecutor->fixOffsetBeforeMatch((int)input->index() - (int)_startIndex);
+ }
+
+ bool treatEofAsEpsilon = t == Token::EOF;
+ Ref<LexerATNConfig> config = std::make_shared<LexerATNConfig>(downCast<const LexerATNConfig&>(*c),
+ target, std::move(lexerActionExecutor));
+
+ if (closure(input, config, reach, currentAltReachedAcceptState, true, treatEofAsEpsilon)) {
+ // any remaining configs for this alt have a lower priority than
+ // the one that just reached an accept state.
+ skipAlt = c->alt;
+ break;
+ }
+ }
+ }
+ }
+}
+
+void LexerATNSimulator::accept(CharStream *input, const Ref<const LexerActionExecutor> &lexerActionExecutor, size_t /*startIndex*/,
+ size_t index, size_t line, size_t charPos) {
+#if DEBUG_ATN == 1
+ std::cout << "ACTION ";
+ std::cout << toString(lexerActionExecutor) << std::endl;
+#endif
+
+ // seek to after last char in token
+ input->seek(index);
+ _line = line;
+ _charPositionInLine = (int)charPos;
+
+ if (lexerActionExecutor != nullptr && _recog != nullptr) {
+ lexerActionExecutor->execute(_recog, input, _startIndex);
+ }
+}
+
+atn::ATNState *LexerATNSimulator::getReachableTarget(const Transition *trans, size_t t) {
+ if (trans->matches(t, Lexer::MIN_CHAR_VALUE, Lexer::MAX_CHAR_VALUE)) {
+ return trans->target;
+ }
+
+ return nullptr;
+}
+
+std::unique_ptr<ATNConfigSet> LexerATNSimulator::computeStartState(CharStream *input, ATNState *p) {
+ Ref<const PredictionContext> initialContext = PredictionContext::EMPTY; // ml: the purpose of this assignment is unclear
+ std::unique_ptr<ATNConfigSet> configs(new OrderedATNConfigSet());
+ for (size_t i = 0; i < p->transitions.size(); i++) {
+ ATNState *target = p->transitions[i]->target;
+ Ref<LexerATNConfig> c = std::make_shared<LexerATNConfig>(target, (int)(i + 1), initialContext);
+ closure(input, c, configs.get(), false, false, false);
+ }
+
+ return configs;
+}
+
+bool LexerATNSimulator::closure(CharStream *input, const Ref<LexerATNConfig> &config, ATNConfigSet *configs,
+ bool currentAltReachedAcceptState, bool speculative, bool treatEofAsEpsilon) {
+#if DEBUG_ATN == 1
+ std::cout << "closure(" << config->toString(true) << ")" << std::endl;
+#endif
+
+ if (config->state != nullptr && config->state->getStateType() == ATNStateType::RULE_STOP) {
+#if DEBUG_ATN == 1
+ if (_recog != nullptr) {
+ std::cout << "closure at " << _recog->getRuleNames()[config->state->ruleIndex] << " rule stop " << config << std::endl;
+ } else {
+ std::cout << "closure at rule stop " << config << std::endl;
+ }
+#endif
+
+ if (config->context == nullptr || config->context->hasEmptyPath()) {
+ if (config->context == nullptr || config->context->isEmpty()) {
+ configs->add(config);
+ return true;
+ } else {
+ configs->add(std::make_shared<LexerATNConfig>(*config, config->state, PredictionContext::EMPTY));
+ currentAltReachedAcceptState = true;
+ }
+ }
+
+ if (config->context != nullptr && !config->context->isEmpty()) {
+ for (size_t i = 0; i < config->context->size(); i++) {
+ if (config->context->getReturnState(i) != PredictionContext::EMPTY_RETURN_STATE) {
+ Ref<const PredictionContext> newContext = config->context->getParent(i); // "pop" return state
+ ATNState *returnState = atn.states[config->context->getReturnState(i)];
+ Ref<LexerATNConfig> c = std::make_shared<LexerATNConfig>(*config, returnState, newContext);
+ currentAltReachedAcceptState = closure(input, c, configs, currentAltReachedAcceptState, speculative, treatEofAsEpsilon);
+ }
+ }
+ }
+
+ return currentAltReachedAcceptState;
+ }
+
+ // optimization
+ if (!config->state->epsilonOnlyTransitions) {
+ if (!currentAltReachedAcceptState || !config->hasPassedThroughNonGreedyDecision()) {
+ configs->add(config);
+ }
+ }
+
+ ATNState *p = config->state;
+ for (size_t i = 0; i < p->transitions.size(); i++) {
+ const Transition *t = p->transitions[i].get();
+ Ref<LexerATNConfig> c = getEpsilonTarget(input, config, t, configs, speculative, treatEofAsEpsilon);
+ if (c != nullptr) {
+ currentAltReachedAcceptState = closure(input, c, configs, currentAltReachedAcceptState, speculative, treatEofAsEpsilon);
+ }
+ }
+
+ return currentAltReachedAcceptState;
+}
+
+Ref<LexerATNConfig> LexerATNSimulator::getEpsilonTarget(CharStream *input, const Ref<LexerATNConfig> &config, const Transition *t,
+ ATNConfigSet *configs, bool speculative, bool treatEofAsEpsilon) {
+
+ Ref<LexerATNConfig> c = nullptr;
+ switch (t->getTransitionType()) {
+ case TransitionType::RULE: {
+ const RuleTransition *ruleTransition = static_cast<const RuleTransition*>(t);
+ Ref<const PredictionContext> newContext = SingletonPredictionContext::create(config->context, ruleTransition->followState->stateNumber);
+ c = std::make_shared<LexerATNConfig>(*config, t->target, newContext);
+ break;
+ }
+
+ case TransitionType::PRECEDENCE:
+ throw UnsupportedOperationException("Precedence predicates are not supported in lexers.");
+
+ case TransitionType::PREDICATE: {
+ /* Track traversing semantic predicates. If we traverse,
+ we cannot add a DFA state for this "reach" computation
+ because the DFA would not test the predicate again in the
+ future. Rather than creating collections of semantic predicates
+ like v3 and testing them on prediction, v4 will test them on the
+ fly all the time using the ATN not the DFA. This is slower but
+ semantically it's not used that often. One of the key elements to
+ this predicate mechanism is not adding DFA states that see
+ predicates immediately afterwards in the ATN. For example,
+
+ a : ID {p1}? | ID {p2}? ;
+
+ should create the start state for rule 'a' (to save start state
+ competition), but should not create target of ID state. The
+ collection of ATN states the following ID references includes
+ states reached by traversing predicates. Since this is when we
+ test them, we cannot cash the DFA state target of ID.
+ */
+ const PredicateTransition *pt = static_cast<const PredicateTransition*>(t);
+
+#if DEBUG_ATN == 1
+ std::cout << "EVAL rule " << pt->getRuleIndex() << ":" << pt->getPredIndex() << std::endl;
+#endif
+
+ configs->hasSemanticContext = true;
+ if (evaluatePredicate(input, pt->getRuleIndex(), pt->getPredIndex(), speculative)) {
+ c = std::make_shared<LexerATNConfig>(*config, t->target);
+ }
+ break;
+ }
+
+ case TransitionType::ACTION:
+ if (config->context == nullptr|| config->context->hasEmptyPath()) {
+ // execute actions anywhere in the start rule for a token.
+ //
+ // TODO: if the entry rule is invoked recursively, some
+ // actions may be executed during the recursive call. The
+ // problem can appear when hasEmptyPath() is true but
+ // isEmpty() is false. In this case, the config needs to be
+ // split into two contexts - one with just the empty path
+ // and another with everything but the empty path.
+ // Unfortunately, the current algorithm does not allow
+ // getEpsilonTarget to return two configurations, so
+ // additional modifications are needed before we can support
+ // the split operation.
+ auto lexerActionExecutor = LexerActionExecutor::append(config->getLexerActionExecutor(),
+ atn.lexerActions[static_cast<const ActionTransition *>(t)->actionIndex]);
+ c = std::make_shared<LexerATNConfig>(*config, t->target, std::move(lexerActionExecutor));
+ break;
+ }
+ else {
+ // ignore actions in referenced rules
+ c = std::make_shared<LexerATNConfig>(*config, t->target);
+ break;
+ }
+
+ case TransitionType::EPSILON:
+ c = std::make_shared<LexerATNConfig>(*config, t->target);
+ break;
+
+ case TransitionType::ATOM:
+ case TransitionType::RANGE:
+ case TransitionType::SET:
+ if (treatEofAsEpsilon) {
+ if (t->matches(Token::EOF, Lexer::MIN_CHAR_VALUE, Lexer::MAX_CHAR_VALUE)) {
+ c = std::make_shared<LexerATNConfig>(*config, t->target);
+ break;
+ }
+ }
+
+ break;
+
+ default: // To silence the compiler. Other transition types are not used here.
+ break;
+ }
+
+ return c;
+}
+
+bool LexerATNSimulator::evaluatePredicate(CharStream *input, size_t ruleIndex, size_t predIndex, bool speculative) {
+ // assume true if no recognizer was provided
+ if (_recog == nullptr) {
+ return true;
+ }
+
+ if (!speculative) {
+ return _recog->sempred(nullptr, ruleIndex, predIndex);
+ }
+
+ size_t savedCharPositionInLine = _charPositionInLine;
+ size_t savedLine = _line;
+ size_t index = input->index();
+ ssize_t marker = input->mark();
+
+ auto onExit = finally([this, input, savedCharPositionInLine, savedLine, index, marker] {
+ _charPositionInLine = savedCharPositionInLine;
+ _line = savedLine;
+ input->seek(index);
+ input->release(marker);
+ });
+
+ consume(input);
+ return _recog->sempred(nullptr, ruleIndex, predIndex);
+}
+
+void LexerATNSimulator::captureSimState(CharStream *input, dfa::DFAState *dfaState) {
+ _prevAccept.index = input->index();
+ _prevAccept.line = _line;
+ _prevAccept.charPos = _charPositionInLine;
+ _prevAccept.dfaState = dfaState;
+}
+
+dfa::DFAState *LexerATNSimulator::addDFAEdge(dfa::DFAState *from, size_t t, ATNConfigSet *q) {
+ /* leading to this call, ATNConfigSet.hasSemanticContext is used as a
+ * marker indicating dynamic predicate evaluation makes this edge
+ * dependent on the specific input sequence, so the static edge in the
+ * DFA should be omitted. The target DFAState is still created since
+ * execATN has the ability to resynchronize with the DFA state cache
+ * following the predicate evaluation step.
+ *
+ * TJP notes: next time through the DFA, we see a pred again and eval.
+ * If that gets us to a previously created (but dangling) DFA
+ * state, we can continue in pure DFA mode from there.
+ */
+ bool suppressEdge = q->hasSemanticContext;
+ q->hasSemanticContext = false;
+
+ dfa::DFAState *to = addDFAState(q);
+
+ if (suppressEdge) {
+ return to;
+ }
+
+ addDFAEdge(from, t, to);
+ return to;
+}
+
+void LexerATNSimulator::addDFAEdge(dfa::DFAState *p, size_t t, dfa::DFAState *q) {
+ if (/*t < MIN_DFA_EDGE ||*/ t > MAX_DFA_EDGE) { // MIN_DFA_EDGE is 0
+ // Only track edges within the DFA bounds
+ return;
+ }
+
+ UniqueLock<SharedMutex> edgeLock(atn._edgeMutex);
+ p->edges[t - MIN_DFA_EDGE] = q; // connect
+}
+
+dfa::DFAState *LexerATNSimulator::addDFAState(ATNConfigSet *configs) {
+ return addDFAState(configs, true);
+}
+
+dfa::DFAState *LexerATNSimulator::addDFAState(ATNConfigSet *configs, bool suppressEdge) {
+ /* the lexer evaluates predicates on-the-fly; by this point configs
+ * should not contain any configurations with unevaluated predicates.
+ */
+ assert(!configs->hasSemanticContext);
+
+ dfa::DFAState *proposed = new dfa::DFAState(std::unique_ptr<ATNConfigSet>(configs)); /* mem-check: managed by the DFA or deleted below */
+ Ref<ATNConfig> firstConfigWithRuleStopState = nullptr;
+ for (const auto &c : configs->configs) {
+ if (RuleStopState::is(c->state)) {
+ firstConfigWithRuleStopState = c;
+ break;
+ }
+ }
+
+ if (firstConfigWithRuleStopState != nullptr) {
+ proposed->isAcceptState = true;
+ proposed->lexerActionExecutor = downCast<const LexerATNConfig&>(*firstConfigWithRuleStopState).getLexerActionExecutor();
+ proposed->prediction = atn.ruleToTokenType[firstConfigWithRuleStopState->state->ruleIndex];
+ }
+
+ dfa::DFA &dfa = _decisionToDFA[_mode];
+
+ {
+ UniqueLock<SharedMutex> stateLock(atn._stateMutex);
+ auto [existing, inserted] = dfa.states.insert(proposed);
+ if (!inserted) {
+ delete proposed;
+ proposed = *existing;
+ } else {
+ // Previously we did a lookup, then set fields, then inserted. It was `dfa.states.size()`,
+ // since we already inserted we need to subtract one.
+ proposed->stateNumber = static_cast<int>(dfa.states.size() - 1);
+ proposed->configs->setReadonly(true);
+ }
+ if (!suppressEdge) {
+ dfa.s0 = proposed;
+ }
+ }
+
+ return proposed;
+}
+
+dfa::DFA& LexerATNSimulator::getDFA(size_t mode) {
+ return _decisionToDFA[mode];
+}
+
+std::string LexerATNSimulator::getText(CharStream *input) {
+ // index is first lookahead char, don't include.
+ return input->getText(misc::Interval(_startIndex, input->index() - 1));
+}
+
+size_t LexerATNSimulator::getLine() const {
+ return _line;
+}
+
+void LexerATNSimulator::setLine(size_t line) {
+ _line = line;
+}
+
+size_t LexerATNSimulator::getCharPositionInLine() {
+ return _charPositionInLine;
+}
+
+void LexerATNSimulator::setCharPositionInLine(size_t charPositionInLine) {
+ _charPositionInLine = charPositionInLine;
+}
+
+void LexerATNSimulator::consume(CharStream *input) {
+ size_t curChar = input->LA(1);
+ if (curChar == '\n') {
+ _line++;
+ _charPositionInLine = 0;
+ } else {
+ _charPositionInLine++;
+ }
+ input->consume();
+}
+
+std::string LexerATNSimulator::getTokenName(size_t t) {
+ if (t == Token::EOF) {
+ return "EOF";
+ }
+ return std::string("'") + static_cast<char>(t) + std::string("'");
+}
+
+void LexerATNSimulator::InitializeInstanceFields() {
+ _startIndex = 0;
+ _line = 1;
+ _charPositionInLine = 0;
+ _mode = antlr4::Lexer::DEFAULT_MODE;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNSimulator.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNSimulator.h
new file mode 100644
index 0000000000..304430b04d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerATNSimulator.h
@@ -0,0 +1,199 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <atomic>
+
+#include "atn/ATNSimulator.h"
+#include "atn/LexerATNConfig.h"
+#include "atn/ATNConfigSet.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// "dup" of ParserInterpreter
+ class ANTLR4CPP_PUBLIC LexerATNSimulator : public ATNSimulator {
+ protected:
+ struct ANTLR4CPP_PUBLIC SimState final {
+ size_t index = INVALID_INDEX;
+ size_t line = 0;
+ size_t charPos = INVALID_INDEX;
+ dfa::DFAState *dfaState = nullptr;
+
+ void reset();
+ };
+
+ public:
+ static constexpr size_t MIN_DFA_EDGE = 0;
+ static constexpr size_t MAX_DFA_EDGE = 127; // forces unicode to stay in ATN
+
+ protected:
+ /// <summary>
+ /// When we hit an accept state in either the DFA or the ATN, we
+ /// have to notify the character stream to start buffering characters
+ /// via <seealso cref="IntStream#mark"/> and record the current state. The current sim state
+ /// includes the current index into the input, the current line,
+ /// and current character position in that line. Note that the Lexer is
+ /// tracking the starting line and characterization of the token. These
+ /// variables track the "state" of the simulator when it hits an accept state.
+ /// <p/>
+ /// We track these variables separately for the DFA and ATN simulation
+ /// because the DFA simulation often has to fail over to the ATN
+ /// simulation. If the ATN simulation fails, we need the DFA to fall
+ /// back to its previously accepted state, if any. If the ATN succeeds,
+ /// then the ATN does the accept and the DFA simulator that invoked it
+ /// can simply return the predicted token type.
+ /// </summary>
+ Lexer *const _recog;
+
+ /// The current token's starting index into the character stream.
+ /// Shared across DFA to ATN simulation in case the ATN fails and the
+ /// DFA did not have a previous accept state. In this case, we use the
+ /// ATN-generated exception object.
+ size_t _startIndex;
+
+ /// line number 1..n within the input.
+ size_t _line;
+
+ /// The index of the character relative to the beginning of the line 0..n-1.
+ size_t _charPositionInLine;
+
+ public:
+ std::vector<dfa::DFA> &_decisionToDFA;
+
+ protected:
+ size_t _mode;
+
+ /// Used during DFA/ATN exec to record the most recent accept configuration info.
+ SimState _prevAccept;
+
+ public:
+ LexerATNSimulator(const ATN &atn, std::vector<dfa::DFA> &decisionToDFA, PredictionContextCache &sharedContextCache);
+ LexerATNSimulator(Lexer *recog, const ATN &atn, std::vector<dfa::DFA> &decisionToDFA, PredictionContextCache &sharedContextCache);
+ virtual ~LexerATNSimulator() = default;
+
+ virtual void copyState(LexerATNSimulator *simulator);
+ virtual size_t match(CharStream *input, size_t mode);
+ virtual void reset() override;
+
+ virtual void clearDFA() override;
+
+ protected:
+ virtual size_t matchATN(CharStream *input);
+ virtual size_t execATN(CharStream *input, dfa::DFAState *ds0);
+
+ /// <summary>
+ /// Get an existing target state for an edge in the DFA. If the target state
+ /// for the edge has not yet been computed or is otherwise not available,
+ /// this method returns {@code null}.
+ /// </summary>
+ /// <param name="s"> The current DFA state </param>
+ /// <param name="t"> The next input symbol </param>
+ /// <returns> The existing target DFA state for the given input symbol
+ /// {@code t}, or {@code null} if the target state for this edge is not
+ /// already cached </returns>
+ virtual dfa::DFAState *getExistingTargetState(dfa::DFAState *s, size_t t);
+
+ /// <summary>
+ /// Compute a target state for an edge in the DFA, and attempt to add the
+ /// computed state and corresponding edge to the DFA.
+ /// </summary>
+ /// <param name="input"> The input stream </param>
+ /// <param name="s"> The current DFA state </param>
+ /// <param name="t"> The next input symbol
+ /// </param>
+ /// <returns> The computed target DFA state for the given input symbol
+ /// {@code t}. If {@code t} does not lead to a valid DFA state, this method
+ /// returns <seealso cref="#ERROR"/>. </returns>
+ virtual dfa::DFAState *computeTargetState(CharStream *input, dfa::DFAState *s, size_t t);
+
+ virtual size_t failOrAccept(CharStream *input, ATNConfigSet *reach, size_t t);
+
+ /// <summary>
+ /// Given a starting configuration set, figure out all ATN configurations
+ /// we can reach upon input {@code t}. Parameter {@code reach} is a return
+ /// parameter.
+ /// </summary>
+ void getReachableConfigSet(CharStream *input, ATNConfigSet *closure_, // closure_ as we have a closure() already
+ ATNConfigSet *reach, size_t t);
+
+ virtual void accept(CharStream *input, const Ref<const LexerActionExecutor> &lexerActionExecutor, size_t startIndex, size_t index,
+ size_t line, size_t charPos);
+
+ virtual ATNState *getReachableTarget(const Transition *trans, size_t t);
+
+ virtual std::unique_ptr<ATNConfigSet> computeStartState(CharStream *input, ATNState *p);
+
+ /// <summary>
+ /// Since the alternatives within any lexer decision are ordered by
+ /// preference, this method stops pursuing the closure as soon as an accept
+ /// state is reached. After the first accept state is reached by depth-first
+ /// search from {@code config}, all other (potentially reachable) states for
+ /// this rule would have a lower priority.
+ /// </summary>
+ /// <returns> {@code true} if an accept state is reached, otherwise
+ /// {@code false}. </returns>
+ virtual bool closure(CharStream *input, const Ref<LexerATNConfig> &config, ATNConfigSet *configs,
+ bool currentAltReachedAcceptState, bool speculative, bool treatEofAsEpsilon);
+
+ // side-effect: can alter configs.hasSemanticContext
+ virtual Ref<LexerATNConfig> getEpsilonTarget(CharStream *input, const Ref<LexerATNConfig> &config, const Transition *t,
+ ATNConfigSet *configs, bool speculative, bool treatEofAsEpsilon);
+
+ /// <summary>
+ /// Evaluate a predicate specified in the lexer.
+ /// <p/>
+ /// If {@code speculative} is {@code true}, this method was called before
+ /// <seealso cref="#consume"/> for the matched character. This method should call
+ /// <seealso cref="#consume"/> before evaluating the predicate to ensure position
+ /// sensitive values, including <seealso cref="Lexer#getText"/>, <seealso cref="Lexer#getLine"/>,
+ /// and <seealso cref="Lexer#getCharPositionInLine"/>, properly reflect the current
+ /// lexer state. This method should restore {@code input} and the simulator
+ /// to the original state before returning (i.e. undo the actions made by the
+ /// call to <seealso cref="#consume"/>.
+ /// </summary>
+ /// <param name="input"> The input stream. </param>
+ /// <param name="ruleIndex"> The rule containing the predicate. </param>
+ /// <param name="predIndex"> The index of the predicate within the rule. </param>
+ /// <param name="speculative"> {@code true} if the current index in {@code input} is
+ /// one character before the predicate's location.
+ /// </param>
+ /// <returns> {@code true} if the specified predicate evaluates to
+ /// {@code true}. </returns>
+ virtual bool evaluatePredicate(CharStream *input, size_t ruleIndex, size_t predIndex, bool speculative);
+
+ virtual void captureSimState(CharStream *input, dfa::DFAState *dfaState);
+ virtual dfa::DFAState* addDFAEdge(dfa::DFAState *from, size_t t, ATNConfigSet *q);
+ virtual void addDFAEdge(dfa::DFAState *p, size_t t, dfa::DFAState *q);
+
+ /// <summary>
+ /// Add a new DFA state if there isn't one with this set of
+ /// configurations already. This method also detects the first
+ /// configuration containing an ATN rule stop state. Later, when
+ /// traversing the DFA, we will know which rule to accept.
+ /// </summary>
+ virtual dfa::DFAState *addDFAState(ATNConfigSet *configs);
+
+ virtual dfa::DFAState *addDFAState(ATNConfigSet *configs, bool suppressEdge);
+
+ public:
+ dfa::DFA& getDFA(size_t mode);
+
+ /// Get the text matched so far for the current token.
+ virtual std::string getText(CharStream *input);
+ virtual size_t getLine() const;
+ virtual void setLine(size_t line);
+ virtual size_t getCharPositionInLine();
+ virtual void setCharPositionInLine(size_t charPositionInLine);
+ virtual void consume(CharStream *input);
+ virtual std::string getTokenName(size_t t);
+
+ private:
+ void InitializeInstanceFields();
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerAction.cpp
new file mode 100644
index 0000000000..a9d9a6771b
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerAction.cpp
@@ -0,0 +1,15 @@
+#include "LexerAction.h"
+
+using namespace antlr4::atn;
+
+size_t LexerAction::hashCode() const {
+ auto hash = cachedHashCode();
+ if (hash == 0) {
+ hash = hashCodeImpl();
+ if (hash == 0) {
+ hash = std::numeric_limits<size_t>::max();
+ }
+ _hashCode.store(hash, std::memory_order_relaxed);
+ }
+ return hash;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerAction.h
new file mode 100644
index 0000000000..5c30a89608
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerAction.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerActionType.h"
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// Represents a single action which can be executed following the successful
+ /// match of a lexer rule. Lexer actions are used for both embedded action syntax
+ /// and ANTLR 4's new lexer command syntax.
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerAction {
+ public:
+ virtual ~LexerAction() = default;
+
+ /// <summary>
+ /// Gets the serialization type of the lexer action.
+ /// </summary>
+ /// <returns> The serialization type of the lexer action. </returns>
+ ///
+ /// IMPORTANT: Unlike Java, this returns LexerActionType::INDEXED_CUSTOM for instances of
+ /// LexerIndexedCustomAction. If you need the wrapped action type, use
+ /// LexerIndexedCustomAction::getAction()->getActionType().
+ LexerActionType getActionType() const { return _actionType; }
+
+ /// <summary>
+ /// Gets whether the lexer action is position-dependent. Position-dependent
+ /// actions may have different semantics depending on the <seealso cref="CharStream"/>
+ /// index at the time the action is executed.
+ ///
+ /// <para>Many lexer commands, including {@code type}, {@code skip}, and
+ /// {@code more}, do not check the input index during their execution.
+ /// Actions like this are position-independent, and may be stored more
+ /// efficiently as part of the <seealso cref="LexerATNConfig#lexerActionExecutor"/>.</para>
+ /// </summary>
+ /// <returns> {@code true} if the lexer action semantics can be affected by the
+ /// position of the input <seealso cref="CharStream"/> at the time it is executed;
+ /// otherwise, {@code false}. </returns>
+ bool isPositionDependent() const { return _positionDependent; }
+
+ /// <summary>
+ /// Execute the lexer action in the context of the specified <seealso cref="Lexer"/>.
+ ///
+ /// <para>For position-dependent actions, the input stream must already be
+ /// positioned correctly prior to calling this method.</para>
+ /// </summary>
+ /// <param name="lexer"> The lexer instance. </param>
+ virtual void execute(Lexer *lexer) const = 0;
+
+ size_t hashCode() const;
+
+ virtual bool equals(const LexerAction &other) const = 0;
+
+ virtual std::string toString() const = 0;
+
+ protected:
+ LexerAction(LexerActionType actionType, bool positionDependent)
+ : _actionType(actionType), _hashCode(0), _positionDependent(positionDependent) {}
+
+ virtual size_t hashCodeImpl() const = 0;
+
+ size_t cachedHashCode() const { return _hashCode.load(std::memory_order_relaxed); }
+
+ private:
+ const LexerActionType _actionType;
+ mutable std::atomic<size_t> _hashCode;
+ const bool _positionDependent;
+ };
+
+ inline bool operator==(const LexerAction &lhs, const LexerAction &rhs) {
+ return lhs.equals(rhs);
+ }
+
+ inline bool operator!=(const LexerAction &lhs, const LexerAction &rhs) {
+ return !operator==(lhs, rhs);
+ }
+
+} // namespace atn
+} // namespace antlr4
+
+namespace std {
+
+ template <>
+ struct hash<::antlr4::atn::LexerAction> {
+ size_t operator()(const ::antlr4::atn::LexerAction &lexerAction) const {
+ return lexerAction.hashCode();
+ }
+ };
+
+} // namespace std
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionExecutor.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionExecutor.cpp
new file mode 100644
index 0000000000..490351b892
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionExecutor.cpp
@@ -0,0 +1,111 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "atn/LexerIndexedCustomAction.h"
+#include "support/CPPUtils.h"
+#include "support/Arrays.h"
+#include "support/Casts.h"
+
+#include "atn/LexerActionExecutor.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+using namespace antlrcpp;
+
+namespace {
+
+ bool cachedHashCodeEqual(size_t lhs, size_t rhs) {
+ return lhs == rhs || lhs == 0 || rhs == 0;
+ }
+
+ bool lexerActionEqual(const Ref<const LexerAction> &lhs, const Ref<const LexerAction> &rhs) {
+ return *lhs == *rhs;
+ }
+
+}
+
+LexerActionExecutor::LexerActionExecutor(std::vector<Ref<const LexerAction>> lexerActions)
+ : _lexerActions(std::move(lexerActions)), _hashCode(0) {}
+
+Ref<const LexerActionExecutor> LexerActionExecutor::append(const Ref<const LexerActionExecutor> &lexerActionExecutor,
+ Ref<const LexerAction> lexerAction) {
+ if (lexerActionExecutor == nullptr) {
+ return std::make_shared<LexerActionExecutor>(std::vector<Ref<const LexerAction>>{ std::move(lexerAction) });
+ }
+ std::vector<Ref<const LexerAction>> lexerActions;
+ lexerActions.reserve(lexerActionExecutor->_lexerActions.size() + 1);
+ lexerActions.insert(lexerActions.begin(), lexerActionExecutor->_lexerActions.begin(), lexerActionExecutor->_lexerActions.end());
+ lexerActions.push_back(std::move(lexerAction));
+ return std::make_shared<LexerActionExecutor>(std::move(lexerActions));
+}
+
+Ref<const LexerActionExecutor> LexerActionExecutor::fixOffsetBeforeMatch(int offset) const {
+ std::vector<Ref<const LexerAction>> updatedLexerActions;
+ for (size_t i = 0; i < _lexerActions.size(); i++) {
+ if (_lexerActions[i]->isPositionDependent() && !LexerIndexedCustomAction::is(*_lexerActions[i])) {
+ if (updatedLexerActions.empty()) {
+ updatedLexerActions = _lexerActions; // Make a copy.
+ }
+ updatedLexerActions[i] = std::make_shared<LexerIndexedCustomAction>(offset, _lexerActions[i]);
+ }
+ }
+ if (updatedLexerActions.empty()) {
+ return shared_from_this();
+ }
+ return std::make_shared<LexerActionExecutor>(std::move(updatedLexerActions));
+}
+
+const std::vector<Ref<const LexerAction>>& LexerActionExecutor::getLexerActions() const {
+ return _lexerActions;
+}
+
+void LexerActionExecutor::execute(Lexer *lexer, CharStream *input, size_t startIndex) const {
+ bool requiresSeek = false;
+ size_t stopIndex = input->index();
+
+ auto onExit = finally([requiresSeek, input, stopIndex]() {
+ if (requiresSeek) {
+ input->seek(stopIndex);
+ }
+ });
+ for (const auto &lexerAction : _lexerActions) {
+ if (LexerIndexedCustomAction::is(*lexerAction)) {
+ int offset = downCast<const LexerIndexedCustomAction&>(*lexerAction).getOffset();
+ input->seek(startIndex + offset);
+ requiresSeek = (startIndex + offset) != stopIndex;
+ } else if (lexerAction->isPositionDependent()) {
+ input->seek(stopIndex);
+ requiresSeek = false;
+ }
+ lexerAction->execute(lexer);
+ }
+}
+
+size_t LexerActionExecutor::hashCode() const {
+ auto hash = _hashCode.load(std::memory_order_relaxed);
+ if (hash == 0) {
+ hash = MurmurHash::initialize();
+ for (const auto &lexerAction : _lexerActions) {
+ hash = MurmurHash::update(hash, lexerAction);
+ }
+ hash = MurmurHash::finish(hash, _lexerActions.size());
+ if (hash == 0) {
+ hash = std::numeric_limits<size_t>::max();
+ }
+ _hashCode.store(hash, std::memory_order_relaxed);
+ }
+ return hash;
+}
+
+bool LexerActionExecutor::equals(const LexerActionExecutor &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ return cachedHashCodeEqual(_hashCode.load(std::memory_order_relaxed), other._hashCode.load(std::memory_order_relaxed)) &&
+ _lexerActions.size() == other._lexerActions.size() &&
+ std::equal(_lexerActions.begin(), _lexerActions.end(), other._lexerActions.begin(), lexerActionEqual);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionExecutor.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionExecutor.h
new file mode 100644
index 0000000000..28bb1e28ec
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionExecutor.h
@@ -0,0 +1,128 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "CharStream.h"
+#include "atn/LexerAction.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// Represents an executor for a sequence of lexer actions which traversed during
+ /// the matching operation of a lexer rule (token).
+ ///
+ /// <para>The executor tracks position information for position-dependent lexer actions
+ /// efficiently, ensuring that actions appearing only at the end of the rule do
+ /// not cause bloating of the <seealso cref="DFA"/> created for the lexer.</para>
+ class ANTLR4CPP_PUBLIC LexerActionExecutor final : public std::enable_shared_from_this<LexerActionExecutor> {
+ public:
+ /// <summary>
+ /// Constructs an executor for a sequence of <seealso cref="LexerAction"/> actions. </summary>
+ /// <param name="lexerActions"> The lexer actions to execute. </param>
+ explicit LexerActionExecutor(std::vector<Ref<const LexerAction>> lexerActions);
+
+ /// <summary>
+ /// Creates a <seealso cref="LexerActionExecutor"/> which executes the actions for
+ /// the input {@code lexerActionExecutor} followed by a specified
+ /// {@code lexerAction}.
+ /// </summary>
+ /// <param name="lexerActionExecutor"> The executor for actions already traversed by
+ /// the lexer while matching a token within a particular
+ /// <seealso cref="LexerATNConfig"/>. If this is {@code null}, the method behaves as
+ /// though it were an empty executor. </param>
+ /// <param name="lexerAction"> The lexer action to execute after the actions
+ /// specified in {@code lexerActionExecutor}.
+ /// </param>
+ /// <returns> A <seealso cref="LexerActionExecutor"/> for executing the combine actions
+ /// of {@code lexerActionExecutor} and {@code lexerAction}. </returns>
+ static Ref<const LexerActionExecutor> append(const Ref<const LexerActionExecutor> &lexerActionExecutor,
+ Ref<const LexerAction> lexerAction);
+
+ /// <summary>
+ /// Creates a <seealso cref="LexerActionExecutor"/> which encodes the current offset
+ /// for position-dependent lexer actions.
+ ///
+ /// <para>Normally, when the executor encounters lexer actions where
+ /// <seealso cref="LexerAction#isPositionDependent"/> returns {@code true}, it calls
+ /// <seealso cref="IntStream#seek"/> on the input <seealso cref="CharStream"/> to set the input
+ /// position to the <em>end</em> of the current token. This behavior provides
+ /// for efficient DFA representation of lexer actions which appear at the end
+ /// of a lexer rule, even when the lexer rule matches a variable number of
+ /// characters.</para>
+ ///
+ /// <para>Prior to traversing a match transition in the ATN, the current offset
+ /// from the token start index is assigned to all position-dependent lexer
+ /// actions which have not already been assigned a fixed offset. By storing
+ /// the offsets relative to the token start index, the DFA representation of
+ /// lexer actions which appear in the middle of tokens remains efficient due
+ /// to sharing among tokens of the same length, regardless of their absolute
+ /// position in the input stream.</para>
+ ///
+ /// <para>If the current executor already has offsets assigned to all
+ /// position-dependent lexer actions, the method returns {@code this}.</para>
+ /// </summary>
+ /// <param name="offset"> The current offset to assign to all position-dependent
+ /// lexer actions which do not already have offsets assigned.
+ /// </param>
+ /// <returns> A <seealso cref="LexerActionExecutor"/> which stores input stream offsets
+ /// for all position-dependent lexer actions. </returns>
+ Ref<const LexerActionExecutor> fixOffsetBeforeMatch(int offset) const;
+
+ /// <summary>
+ /// Gets the lexer actions to be executed by this executor. </summary>
+ /// <returns> The lexer actions to be executed by this executor. </returns>
+ const std::vector<Ref<const LexerAction>>& getLexerActions() const;
+
+ /// <summary>
+ /// Execute the actions encapsulated by this executor within the context of a
+ /// particular <seealso cref="Lexer"/>.
+ ///
+ /// <para>This method calls <seealso cref="IntStream#seek"/> to set the position of the
+ /// {@code input} <seealso cref="CharStream"/> prior to calling
+ /// <seealso cref="LexerAction#execute"/> on a position-dependent action. Before the
+ /// method returns, the input position will be restored to the same position
+ /// it was in when the method was invoked.</para>
+ /// </summary>
+ /// <param name="lexer"> The lexer instance. </param>
+ /// <param name="input"> The input stream which is the source for the current token.
+ /// When this method is called, the current <seealso cref="IntStream#index"/> for
+ /// {@code input} should be the start of the following token, i.e. 1
+ /// character past the end of the current token. </param>
+ /// <param name="startIndex"> The token start index. This value may be passed to
+ /// <seealso cref="IntStream#seek"/> to set the {@code input} position to the beginning
+ /// of the token. </param>
+ void execute(Lexer *lexer, CharStream *input, size_t startIndex) const;
+
+ size_t hashCode() const;
+
+ bool equals(const LexerActionExecutor &other) const;
+
+ private:
+ const std::vector<Ref<const LexerAction>> _lexerActions;
+ mutable std::atomic<size_t> _hashCode;
+ };
+
+ inline bool operator==(const LexerActionExecutor &lhs, const LexerActionExecutor &rhs) {
+ return lhs.equals(rhs);
+ }
+
+ inline bool operator!=(const LexerActionExecutor &lhs, const LexerActionExecutor &rhs) {
+ return !operator==(lhs, rhs);
+ }
+
+} // namespace atn
+} // namespace antlr4
+
+namespace std {
+
+ template <>
+ struct hash<::antlr4::atn::LexerActionExecutor> {
+ size_t operator()(const ::antlr4::atn::LexerActionExecutor &lexerActionExecutor) const {
+ return lexerActionExecutor.hashCode();
+ }
+ };
+
+} // namespace std
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionType.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionType.h
new file mode 100644
index 0000000000..aab4033415
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerActionType.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// Represents the serialization type of a <seealso cref="LexerAction"/>.
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ enum class LexerActionType : size_t {
+ /// <summary>
+ /// The type of a <seealso cref="LexerChannelAction"/> action.
+ /// </summary>
+ CHANNEL = 0,
+ /// <summary>
+ /// The type of a <seealso cref="LexerCustomAction"/> action.
+ /// </summary>
+ CUSTOM,
+ /// <summary>
+ /// The type of a <seealso cref="LexerModeAction"/> action.
+ /// </summary>
+ MODE,
+ /// <summary>
+ /// The type of a <seealso cref="LexerMoreAction"/> action.
+ /// </summary>
+ MORE,
+ /// <summary>
+ /// The type of a <seealso cref="LexerPopModeAction"/> action.
+ /// </summary>
+ POP_MODE,
+ /// <summary>
+ /// The type of a <seealso cref="LexerPushModeAction"/> action.
+ /// </summary>
+ PUSH_MODE,
+ /// <summary>
+ /// The type of a <seealso cref="LexerSkipAction"/> action.
+ /// </summary>
+ SKIP,
+ /// <summary>
+ /// The type of a <seealso cref="LexerTypeAction"/> action.
+ /// </summary>
+ TYPE,
+
+ INDEXED_CUSTOM,
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerChannelAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerChannelAction.cpp
new file mode 100644
index 0000000000..b6cda6cff0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerChannelAction.cpp
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+#include "support/Casts.h"
+
+#include "atn/LexerChannelAction.h"
+
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+using namespace antlrcpp;
+
+LexerChannelAction::LexerChannelAction(int channel)
+ : LexerAction(LexerActionType::CHANNEL, false), _channel(channel) {}
+
+void LexerChannelAction::execute(Lexer *lexer) const {
+ lexer->setChannel(getChannel());
+}
+
+size_t LexerChannelAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ hash = MurmurHash::update(hash, getChannel());
+ return MurmurHash::finish(hash, 2);
+}
+
+bool LexerChannelAction::equals(const LexerAction &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ if (getActionType() != other.getActionType()) {
+ return false;
+ }
+ const auto &lexerAction = downCast<const LexerChannelAction&>(other);
+ return getChannel() == lexerAction.getChannel();
+}
+
+std::string LexerChannelAction::toString() const {
+ return "channel(" + std::to_string(getChannel()) + ")";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerChannelAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerChannelAction.h
new file mode 100644
index 0000000000..1a5c53efef
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerChannelAction.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerAction.h"
+#include "atn/LexerActionType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ using antlr4::Lexer;
+
+ /// <summary>
+ /// Implements the {@code channel} lexer action by calling
+ /// <seealso cref="Lexer#setChannel"/> with the assigned channel.
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerChannelAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::CHANNEL; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// <summary>
+ /// Constructs a new {@code channel} action with the specified channel value. </summary>
+ /// <param name="channel"> The channel value to pass to <seealso cref="Lexer#setChannel"/>. </param>
+ explicit LexerChannelAction(int channel);
+
+ /// <summary>
+ /// Gets the channel to use for the <seealso cref="Token"/> created by the lexer.
+ /// </summary>
+ /// <returns> The channel to use for the <seealso cref="Token"/> created by the lexer. </returns>
+ int getChannel() const { return _channel; }
+
+ /// <summary>
+ /// {@inheritDoc}
+ ///
+ /// <para>This action is implemented by calling <seealso cref="Lexer#setChannel"/> with the
+ /// value provided by <seealso cref="#getChannel"/>.</para>
+ /// </summary>
+ void execute(Lexer *lexer) const override;
+
+ bool equals(const LexerAction &other) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ const int _channel;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerCustomAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerCustomAction.cpp
new file mode 100644
index 0000000000..b6edd89ea1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerCustomAction.cpp
@@ -0,0 +1,45 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+#include "support/Casts.h"
+
+#include "atn/LexerCustomAction.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+using namespace antlrcpp;
+
+LexerCustomAction::LexerCustomAction(size_t ruleIndex, size_t actionIndex)
+ : LexerAction(LexerActionType::CUSTOM, true), _ruleIndex(ruleIndex), _actionIndex(actionIndex) {}
+
+void LexerCustomAction::execute(Lexer *lexer) const {
+ lexer->action(nullptr, getRuleIndex(), getActionIndex());
+}
+
+size_t LexerCustomAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ hash = MurmurHash::update(hash, getRuleIndex());
+ hash = MurmurHash::update(hash, getActionIndex());
+ return MurmurHash::finish(hash, 3);
+}
+
+bool LexerCustomAction::equals(const LexerAction &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ if (getActionType() != other.getActionType()) {
+ return false;
+ }
+ const auto &lexerAction = downCast<const LexerCustomAction&>(other);
+ return getRuleIndex() == lexerAction.getRuleIndex() && getActionIndex() == lexerAction.getActionIndex();
+}
+
+std::string LexerCustomAction::toString() const {
+ return "custom(" + std::to_string(getRuleIndex()) + ", " + std::to_string(getActionIndex()) + ")";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerCustomAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerCustomAction.h
new file mode 100644
index 0000000000..7973271c62
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerCustomAction.h
@@ -0,0 +1,75 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerAction.h"
+#include "atn/LexerActionType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// Executes a custom lexer action by calling <seealso cref="Recognizer#action"/> with the
+ /// rule and action indexes assigned to the custom action. The implementation of
+ /// a custom action is added to the generated code for the lexer in an override
+ /// of <seealso cref="Recognizer#action"/> when the grammar is compiled.
+ ///
+ /// <para>This class may represent embedded actions created with the <code>{...}</code>
+ /// syntax in ANTLR 4, as well as actions created for lexer commands where the
+ /// command argument could not be evaluated when the grammar was compiled.</para>
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerCustomAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::CUSTOM; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// <summary>
+ /// Constructs a custom lexer action with the specified rule and action
+ /// indexes.
+ /// </summary>
+ /// <param name="ruleIndex"> The rule index to use for calls to
+ /// <seealso cref="Recognizer#action"/>. </param>
+ /// <param name="actionIndex"> The action index to use for calls to
+ /// <seealso cref="Recognizer#action"/>. </param>
+ LexerCustomAction(size_t ruleIndex, size_t actionIndex);
+
+ /// <summary>
+ /// Gets the rule index to use for calls to <seealso cref="Recognizer#action"/>.
+ /// </summary>
+ /// <returns> The rule index for the custom action. </returns>
+ size_t getRuleIndex() const { return _ruleIndex; }
+
+ /// <summary>
+ /// Gets the action index to use for calls to <seealso cref="Recognizer#action"/>.
+ /// </summary>
+ /// <returns> The action index for the custom action. </returns>
+ size_t getActionIndex() const { return _actionIndex; }
+
+ /// <summary>
+ /// {@inheritDoc}
+ ///
+ /// <para>Custom actions are implemented by calling <seealso cref="Lexer#action"/> with the
+ /// appropriate rule and action indexes.</para>
+ /// </summary>
+ void execute(Lexer *lexer) const override;
+
+ bool equals(const LexerAction &other) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ const size_t _ruleIndex;
+ const size_t _actionIndex;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerIndexedCustomAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerIndexedCustomAction.cpp
new file mode 100644
index 0000000000..114863702c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerIndexedCustomAction.cpp
@@ -0,0 +1,57 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+#include "support/CPPUtils.h"
+#include "support/Casts.h"
+
+#include "atn/LexerIndexedCustomAction.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+using namespace antlrcpp;
+
+namespace {
+
+ bool cachedHashCodeEqual(size_t lhs, size_t rhs) {
+ return lhs == rhs || lhs == 0 || rhs == 0;
+ }
+
+}
+
+LexerIndexedCustomAction::LexerIndexedCustomAction(int offset, Ref<const LexerAction> action)
+ : LexerAction(LexerActionType::INDEXED_CUSTOM, true), _action(std::move(action)), _offset(offset) {}
+
+void LexerIndexedCustomAction::execute(Lexer *lexer) const {
+ // assume the input stream position was properly set by the calling code
+ getAction()->execute(lexer);
+}
+
+size_t LexerIndexedCustomAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ hash = MurmurHash::update(hash, getOffset());
+ hash = MurmurHash::update(hash, getAction());
+ return MurmurHash::finish(hash, 3);
+}
+
+bool LexerIndexedCustomAction::equals(const LexerAction &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ if (getActionType() != other.getActionType()) {
+ return false;
+ }
+ const auto &lexerAction = downCast<const LexerIndexedCustomAction&>(other);
+ return getOffset() == lexerAction.getOffset() &&
+ cachedHashCodeEqual(cachedHashCode(), lexerAction.cachedHashCode()) &&
+ *getAction() == *lexerAction.getAction();
+}
+
+std::string LexerIndexedCustomAction::toString() const {
+ return "indexedCustom(" + std::to_string(getOffset()) + ", " + getAction()->toString() + ")";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerIndexedCustomAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerIndexedCustomAction.h
new file mode 100644
index 0000000000..5693bac62b
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerIndexedCustomAction.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "RuleContext.h"
+#include "atn/LexerAction.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// This implementation of <seealso cref="LexerAction"/> is used for tracking input offsets
+ /// for position-dependent actions within a <seealso cref="LexerActionExecutor"/>.
+ ///
+ /// <para>This action is not serialized as part of the ATN, and is only required for
+ /// position-dependent lexer actions which appear at a location other than the
+ /// end of a rule. For more information about DFA optimizations employed for
+ /// lexer actions, see <seealso cref="LexerActionExecutor#append"/> and
+ /// <seealso cref="LexerActionExecutor#fixOffsetBeforeMatch"/>.</para>
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerIndexedCustomAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::INDEXED_CUSTOM; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// <summary>
+ /// Constructs a new indexed custom action by associating a character offset
+ /// with a <seealso cref="LexerAction"/>.
+ ///
+ /// <para>Note: This class is only required for lexer actions for which
+ /// <seealso cref="LexerAction#isPositionDependent"/> returns {@code true}.</para>
+ /// </summary>
+ /// <param name="offset"> The offset into the input <seealso cref="CharStream"/>, relative to
+ /// the token start index, at which the specified lexer action should be
+ /// executed. </param>
+ /// <param name="action"> The lexer action to execute at a particular offset in the
+ /// input <seealso cref="CharStream"/>. </param>
+ LexerIndexedCustomAction(int offset, Ref<const LexerAction> action);
+
+ /// <summary>
+ /// Gets the location in the input <seealso cref="CharStream"/> at which the lexer
+ /// action should be executed. The value is interpreted as an offset relative
+ /// to the token start index.
+ /// </summary>
+ /// <returns> The location in the input <seealso cref="CharStream"/> at which the lexer
+ /// action should be executed. </returns>
+ int getOffset() const { return _offset; }
+
+ /// <summary>
+ /// Gets the lexer action to execute.
+ /// </summary>
+ /// <returns> A <seealso cref="LexerAction"/> object which executes the lexer action. </returns>
+ const Ref<const LexerAction>& getAction() const { return _action; }
+
+ void execute(Lexer *lexer) const override;
+ bool equals(const LexerAction &other) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ const Ref<const LexerAction> _action;
+ const int _offset;
+ };
+
+} // namespace atn
+} // namespace antlr4
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerModeAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerModeAction.cpp
new file mode 100644
index 0000000000..a4ca3b3d79
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerModeAction.cpp
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+#include "support/Casts.h"
+
+#include "atn/LexerModeAction.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+using namespace antlrcpp;
+
+LexerModeAction::LexerModeAction(int mode) : LexerAction(LexerActionType::MODE, false), _mode(mode) {}
+
+void LexerModeAction::execute(Lexer *lexer) const {
+ lexer->setMode(getMode());
+}
+
+size_t LexerModeAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ hash = MurmurHash::update(hash, getMode());
+ return MurmurHash::finish(hash, 2);
+}
+
+bool LexerModeAction::equals(const LexerAction &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ if (getActionType() != other.getActionType()) {
+ return false;
+ }
+ const auto &lexerAction = downCast<const LexerModeAction&>(other);
+ return getMode() == lexerAction.getMode();
+}
+
+std::string LexerModeAction::toString() const {
+ return "mode(" + std::to_string(getMode()) + ")";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerModeAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerModeAction.h
new file mode 100644
index 0000000000..6fa61a2e67
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerModeAction.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerAction.h"
+#include "atn/LexerActionType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// Implements the {@code mode} lexer action by calling <seealso cref="Lexer#mode"/> with
+ /// the assigned mode.
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerModeAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::MODE; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// <summary>
+ /// Constructs a new {@code mode} action with the specified mode value. </summary>
+ /// <param name="mode"> The mode value to pass to <seealso cref="Lexer#mode"/>. </param>
+ explicit LexerModeAction(int mode);
+
+ /// <summary>
+ /// Get the lexer mode this action should transition the lexer to.
+ /// </summary>
+ /// <returns> The lexer mode for this {@code mode} command. </returns>
+ int getMode() const { return _mode; }
+
+ /// <summary>
+ /// {@inheritDoc}
+ ///
+ /// <para>This action is implemented by calling <seealso cref="Lexer#mode"/> with the
+ /// value provided by <seealso cref="#getMode"/>.</para>
+ /// </summary>
+ void execute(Lexer *lexer) const override;
+
+ bool equals(const LexerAction &obj) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ const int _mode;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerMoreAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerMoreAction.cpp
new file mode 100644
index 0000000000..30df87b7b6
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerMoreAction.cpp
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+
+#include "atn/LexerMoreAction.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+
+const Ref<const LexerMoreAction>& LexerMoreAction::getInstance() {
+ static const Ref<const LexerMoreAction> instance(new LexerMoreAction());
+ return instance;
+}
+
+void LexerMoreAction::execute(Lexer *lexer) const {
+ lexer->more();
+}
+
+size_t LexerMoreAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ return MurmurHash::finish(hash, 1);
+}
+
+bool LexerMoreAction::equals(const LexerAction &other) const {
+ return this == std::addressof(other);
+}
+
+std::string LexerMoreAction::toString() const {
+ return "more";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerMoreAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerMoreAction.h
new file mode 100644
index 0000000000..fc4b8fcbfc
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerMoreAction.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerAction.h"
+#include "atn/LexerActionType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// Implements the {@code more} lexer action by calling <seealso cref="Lexer#more"/>.
+ ///
+ /// <para>The {@code more} command does not have any parameters, so this action is
+ /// implemented as a singleton instance exposed by <seealso cref="#INSTANCE"/>.</para>
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerMoreAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::MORE; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// <summary>
+ /// Provides a singleton instance of this parameterless lexer action.
+ /// </summary>
+ static const Ref<const LexerMoreAction>& getInstance();
+
+ /// <summary>
+ /// {@inheritDoc}
+ ///
+ /// <para>This action is implemented by calling <seealso cref="Lexer#more"/>.</para>
+ /// </summary>
+ void execute(Lexer *lexer) const override;
+
+ bool equals(const LexerAction &obj) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ /// Constructs the singleton instance of the lexer {@code more} command.
+ LexerMoreAction() : LexerAction(LexerActionType::MORE, false) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPopModeAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPopModeAction.cpp
new file mode 100644
index 0000000000..5192049348
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPopModeAction.cpp
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+
+#include "atn/LexerPopModeAction.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+
+const Ref<const LexerPopModeAction>& LexerPopModeAction::getInstance() {
+ static const Ref<const LexerPopModeAction> instance(new LexerPopModeAction());
+ return instance;
+}
+
+void LexerPopModeAction::execute(Lexer *lexer) const {
+ lexer->popMode();
+}
+
+size_t LexerPopModeAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ return MurmurHash::finish(hash, 1);
+}
+
+bool LexerPopModeAction::equals(const LexerAction &other) const {
+ return this == std::addressof(other);
+}
+
+std::string LexerPopModeAction::toString() const {
+ return "popMode";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPopModeAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPopModeAction.h
new file mode 100644
index 0000000000..8d712cad8c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPopModeAction.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerAction.h"
+#include "atn/LexerActionType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// Implements the {@code popMode} lexer action by calling <seealso cref="Lexer#popMode"/>.
+ ///
+ /// <para>The {@code popMode} command does not have any parameters, so this action is
+ /// implemented as a singleton instance exposed by <seealso cref="#INSTANCE"/>.</para>
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerPopModeAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::POP_MODE; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// <summary>
+ /// Provides a singleton instance of this parameterless lexer action.
+ /// </summary>
+ static const Ref<const LexerPopModeAction>& getInstance();
+
+ /// <summary>
+ /// {@inheritDoc}
+ ///
+ /// <para>This action is implemented by calling <seealso cref="Lexer#popMode"/>.</para>
+ /// </summary>
+ void execute(Lexer *lexer) const override;
+
+ bool equals(const LexerAction &other) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ /// Constructs the singleton instance of the lexer {@code popMode} command.
+ LexerPopModeAction() : LexerAction(LexerActionType::POP_MODE, false) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPushModeAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPushModeAction.cpp
new file mode 100644
index 0000000000..3ebd21fab2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPushModeAction.cpp
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+#include "support/Casts.h"
+
+#include "atn/LexerPushModeAction.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+using namespace antlrcpp;
+
+LexerPushModeAction::LexerPushModeAction(int mode) : LexerAction(LexerActionType::PUSH_MODE, false), _mode(mode) {}
+
+void LexerPushModeAction::execute(Lexer *lexer) const {
+ lexer->pushMode(getMode());
+}
+
+size_t LexerPushModeAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ hash = MurmurHash::update(hash, getMode());
+ return MurmurHash::finish(hash, 2);
+}
+
+bool LexerPushModeAction::equals(const LexerAction &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ if (getActionType() != other.getActionType()) {
+ return false;
+ }
+ const auto &lexerAction = downCast<const LexerPushModeAction&>(other);
+ return getMode() == lexerAction.getMode();
+}
+
+std::string LexerPushModeAction::toString() const {
+ return "pushMode(" + std::to_string(getMode()) + ")";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPushModeAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPushModeAction.h
new file mode 100644
index 0000000000..32b706b583
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerPushModeAction.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerAction.h"
+#include "atn/LexerActionType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// Implements the {@code pushMode} lexer action by calling
+ /// <seealso cref="Lexer#pushMode"/> with the assigned mode.
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerPushModeAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::PUSH_MODE; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// <summary>
+ /// Constructs a new {@code pushMode} action with the specified mode value. </summary>
+ /// <param name="mode"> The mode value to pass to <seealso cref="Lexer#pushMode"/>. </param>
+ explicit LexerPushModeAction(int mode);
+
+ /// <summary>
+ /// Get the lexer mode this action should transition the lexer to.
+ /// </summary>
+ /// <returns> The lexer mode for this {@code pushMode} command. </returns>
+ int getMode() const { return _mode; }
+
+ /// <summary>
+ /// {@inheritDoc}
+ ///
+ /// <para>This action is implemented by calling <seealso cref="Lexer#pushMode"/> with the
+ /// value provided by <seealso cref="#getMode"/>.</para>
+ /// </summary>
+ void execute(Lexer *lexer) const override;
+
+ bool equals(const LexerAction &obj) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ const int _mode;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerSkipAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerSkipAction.cpp
new file mode 100644
index 0000000000..72f9de3e1f
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerSkipAction.cpp
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+
+#include "atn/LexerSkipAction.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+
+const Ref<const LexerSkipAction>& LexerSkipAction::getInstance() {
+ static const Ref<const LexerSkipAction> instance(new LexerSkipAction());
+ return instance;
+}
+
+void LexerSkipAction::execute(Lexer *lexer) const {
+ lexer->skip();
+}
+
+size_t LexerSkipAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ return MurmurHash::finish(hash, 1);
+}
+
+bool LexerSkipAction::equals(const LexerAction &other) const {
+ return this == std::addressof(other);
+}
+
+std::string LexerSkipAction::toString() const {
+ return "skip";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerSkipAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerSkipAction.h
new file mode 100644
index 0000000000..afdf4702f2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerSkipAction.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerAction.h"
+#include "atn/LexerActionType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// Implements the {@code skip} lexer action by calling <seealso cref="Lexer#skip"/>.
+ ///
+ /// <para>The {@code skip} command does not have any parameters, so this action is
+ /// implemented as a singleton instance exposed by <seealso cref="#INSTANCE"/>.</para>
+ ///
+ /// @author Sam Harwell
+ /// @since 4.2
+ /// </summary>
+ class ANTLR4CPP_PUBLIC LexerSkipAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::SKIP; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// Provides a singleton instance of this parameterless lexer action.
+ static const Ref<const LexerSkipAction>& getInstance();
+
+ /// <summary>
+ /// {@inheritDoc}
+ ///
+ /// <para>This action is implemented by calling <seealso cref="Lexer#skip"/>.</para>
+ /// </summary>
+ void execute(Lexer *lexer) const override;
+
+ bool equals(const LexerAction &obj) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ /// Constructs the singleton instance of the lexer {@code skip} command.
+ LexerSkipAction() : LexerAction(LexerActionType::SKIP, false) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerTypeAction.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerTypeAction.cpp
new file mode 100644
index 0000000000..55ccf358ba
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerTypeAction.cpp
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+#include "support/Casts.h"
+
+#include "atn/LexerTypeAction.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+using namespace antlrcpp;
+
+LexerTypeAction::LexerTypeAction(int type) : LexerAction(LexerActionType::TYPE, false), _type(type) {}
+
+void LexerTypeAction::execute(Lexer *lexer) const {
+ lexer->setType(getType());
+}
+
+size_t LexerTypeAction::hashCodeImpl() const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, static_cast<size_t>(getActionType()));
+ hash = MurmurHash::update(hash, getType());
+ return MurmurHash::finish(hash, 2);
+}
+
+bool LexerTypeAction::equals(const LexerAction &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ if (getActionType() != other.getActionType()) {
+ return false;
+ }
+ const auto &lexerAction = downCast<const LexerTypeAction&>(other);
+ return getType() == lexerAction.getType();
+}
+
+std::string LexerTypeAction::toString() const {
+ return "type(" + std::to_string(getType()) + ")";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LexerTypeAction.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerTypeAction.h
new file mode 100644
index 0000000000..1cd7d71fd3
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LexerTypeAction.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/LexerActionType.h"
+#include "atn/LexerAction.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// Implements the {@code type} lexer action by calling <seealso cref="Lexer#setType"/>
+ /// with the assigned type.
+ class ANTLR4CPP_PUBLIC LexerTypeAction final : public LexerAction {
+ public:
+ static bool is(const LexerAction &lexerAction) { return lexerAction.getActionType() == LexerActionType::TYPE; }
+
+ static bool is(const LexerAction *lexerAction) { return lexerAction != nullptr && is(*lexerAction); }
+
+ /// <summary>
+ /// Constructs a new {@code type} action with the specified token type value. </summary>
+ /// <param name="type"> The type to assign to the token using <seealso cref="Lexer#setType"/>. </param>
+ explicit LexerTypeAction(int type);
+
+ /// <summary>
+ /// Gets the type to assign to a token created by the lexer. </summary>
+ /// <returns> The type to assign to a token created by the lexer. </returns>
+ int getType() const { return _type; }
+
+ /// <summary>
+ /// {@inheritDoc}
+ ///
+ /// <para>This action is implemented by calling <seealso cref="Lexer#setType"/> with the
+ /// value provided by <seealso cref="#getType"/>.</para>
+ /// </summary>
+ void execute(Lexer *lexer) const override;
+
+ bool equals(const LexerAction &obj) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+
+ private:
+ const int _type;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LookaheadEventInfo.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/LookaheadEventInfo.cpp
new file mode 100644
index 0000000000..aa3f9124c7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LookaheadEventInfo.cpp
@@ -0,0 +1,16 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/LookaheadEventInfo.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+LookaheadEventInfo::LookaheadEventInfo(size_t decision, ATNConfigSet *configs, size_t predictedAlt,
+ TokenStream *input, size_t startIndex, size_t stopIndex, bool fullCtx)
+ : DecisionEventInfo(decision, configs, input, startIndex, stopIndex, fullCtx) {
+
+ this->predictedAlt = predictedAlt;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LookaheadEventInfo.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LookaheadEventInfo.h
new file mode 100644
index 0000000000..f5fc24fde2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LookaheadEventInfo.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionEventInfo.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// This class represents profiling event information for tracking the lookahead
+ /// depth required in order to make a prediction.
+ class ANTLR4CPP_PUBLIC LookaheadEventInfo : public DecisionEventInfo {
+ public:
+ /// The alternative chosen by adaptivePredict(), not necessarily
+ /// the outermost alt shown for a rule; left-recursive rules have
+ /// user-level alts that differ from the rewritten rule with a (...) block
+ /// and a (..)* loop.
+ size_t predictedAlt = 0;
+
+ /// <summary>
+ /// Constructs a new instance of the <seealso cref="LookaheadEventInfo"/> class with
+ /// the specified detailed lookahead information.
+ /// </summary>
+ /// <param name="decision"> The decision number </param>
+ /// <param name="configs"> The final configuration set containing the necessary
+ /// information to determine the result of a prediction, or {@code null} if
+ /// the final configuration set is not available </param>
+ /// <param name="input"> The input token stream </param>
+ /// <param name="startIndex"> The start index for the current prediction </param>
+ /// <param name="stopIndex"> The index at which the prediction was finally made </param>
+ /// <param name="fullCtx"> {@code true} if the current lookahead is part of an LL
+ /// prediction; otherwise, {@code false} if the current lookahead is part of
+ /// an SLL prediction </param>
+ LookaheadEventInfo(size_t decision, ATNConfigSet *configs, size_t predictedAlt, TokenStream *input, size_t startIndex,
+ size_t stopIndex, bool fullCtx);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/LoopEndState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/LoopEndState.h
new file mode 100644
index 0000000000..2616b1c4b8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/LoopEndState.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// Mark the end of a * or + loop.
+ class ANTLR4CPP_PUBLIC LoopEndState final : public ATNState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::LOOP_END; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ ATNState *loopBackState = nullptr;
+
+ LoopEndState() : ATNState(ATNStateType::LOOP_END) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/NotSetTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/NotSetTransition.cpp
new file mode 100644
index 0000000000..ba796d7188
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/NotSetTransition.cpp
@@ -0,0 +1,22 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/NotSetTransition.h"
+#include "atn/ATNState.h"
+#include "misc/IntervalSet.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+NotSetTransition::NotSetTransition(ATNState *target, misc::IntervalSet set) : SetTransition(TransitionType::NOT_SET, target, std::move(set)) {}
+
+bool NotSetTransition::matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const {
+ return symbol >= minVocabSymbol && symbol <= maxVocabSymbol
+ && !SetTransition::matches(symbol, minVocabSymbol, maxVocabSymbol);
+}
+
+std::string NotSetTransition::toString() const {
+ return "NOT_SET " + Transition::toString() + " { " + SetTransition::toString() + " }";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/NotSetTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/NotSetTransition.h
new file mode 100644
index 0000000000..ef937a60fe
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/NotSetTransition.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/SetTransition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC NotSetTransition final : public SetTransition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::NOT_SET; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ NotSetTransition(ATNState *target, misc::IntervalSet set);
+
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+
+ virtual std::string toString() const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/OrderedATNConfigSet.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/OrderedATNConfigSet.cpp
new file mode 100644
index 0000000000..48655424d8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/OrderedATNConfigSet.cpp
@@ -0,0 +1,16 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/OrderedATNConfigSet.h"
+
+using namespace antlr4::atn;
+
+size_t OrderedATNConfigSet::hashCode(const ATNConfig &atnConfig) const {
+ return atnConfig.hashCode();
+}
+
+bool OrderedATNConfigSet::equals(const ATNConfig &lhs, const ATNConfig &rhs) const {
+ return lhs == rhs;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/OrderedATNConfigSet.h b/contrib/libs/antlr4_cpp_runtime/src/atn/OrderedATNConfigSet.h
new file mode 100644
index 0000000000..18bf6bcb21
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/OrderedATNConfigSet.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNConfigSet.h"
+#include "atn/ATNConfig.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC OrderedATNConfigSet final : public ATNConfigSet {
+ public:
+ OrderedATNConfigSet() = default;
+
+ private:
+ size_t hashCode(const ATNConfig &atnConfig) const override;
+
+ bool equals(const ATNConfig &lhs, const ATNConfig &rhs) const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ParseInfo.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ParseInfo.cpp
new file mode 100644
index 0000000000..95a89ac855
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ParseInfo.cpp
@@ -0,0 +1,102 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ProfilingATNSimulator.h"
+#include "dfa/DFA.h"
+
+#include "atn/ParseInfo.h"
+
+using namespace antlr4::atn;
+
+ParseInfo::ParseInfo(ProfilingATNSimulator *atnSimulator) : _atnSimulator(atnSimulator) {
+}
+
+ParseInfo::~ParseInfo() {
+}
+
+std::vector<DecisionInfo> ParseInfo::getDecisionInfo() {
+ return _atnSimulator->getDecisionInfo();
+}
+
+std::vector<size_t> ParseInfo::getLLDecisions() {
+ std::vector<DecisionInfo> decisions = _atnSimulator->getDecisionInfo();
+ std::vector<size_t> LL;
+ for (size_t i = 0; i < decisions.size(); ++i) {
+ long long fallBack = decisions[i].LL_Fallback;
+ if (fallBack > 0) {
+ LL.push_back(i);
+ }
+ }
+ return LL;
+}
+
+long long ParseInfo::getTotalTimeInPrediction() {
+ std::vector<DecisionInfo> decisions = _atnSimulator->getDecisionInfo();
+ long long t = 0;
+ for (size_t i = 0; i < decisions.size(); ++i) {
+ t += decisions[i].timeInPrediction;
+ }
+ return t;
+}
+
+long long ParseInfo::getTotalSLLLookaheadOps() {
+ std::vector<DecisionInfo> decisions = _atnSimulator->getDecisionInfo();
+ long long k = 0;
+ for (size_t i = 0; i < decisions.size(); ++i) {
+ k += decisions[i].SLL_TotalLook;
+ }
+ return k;
+}
+
+long long ParseInfo::getTotalLLLookaheadOps() {
+ std::vector<DecisionInfo> decisions = _atnSimulator->getDecisionInfo();
+ long long k = 0;
+ for (size_t i = 0; i < decisions.size(); i++) {
+ k += decisions[i].LL_TotalLook;
+ }
+ return k;
+}
+
+long long ParseInfo::getTotalSLLATNLookaheadOps() {
+ std::vector<DecisionInfo> decisions = _atnSimulator->getDecisionInfo();
+ long long k = 0;
+ for (size_t i = 0; i < decisions.size(); ++i) {
+ k += decisions[i].SLL_ATNTransitions;
+ }
+ return k;
+}
+
+long long ParseInfo::getTotalLLATNLookaheadOps() {
+ std::vector<DecisionInfo> decisions = _atnSimulator->getDecisionInfo();
+ long long k = 0;
+ for (size_t i = 0; i < decisions.size(); ++i) {
+ k += decisions[i].LL_ATNTransitions;
+ }
+ return k;
+}
+
+long long ParseInfo::getTotalATNLookaheadOps() {
+ std::vector<DecisionInfo> decisions = _atnSimulator->getDecisionInfo();
+ long long k = 0;
+ for (size_t i = 0; i < decisions.size(); ++i) {
+ k += decisions[i].SLL_ATNTransitions;
+ k += decisions[i].LL_ATNTransitions;
+ }
+ return k;
+}
+
+size_t ParseInfo::getDFASize() {
+ size_t n = 0;
+ std::vector<dfa::DFA> &decisionToDFA = _atnSimulator->decisionToDFA;
+ for (size_t i = 0; i < decisionToDFA.size(); ++i) {
+ n += getDFASize(i);
+ }
+ return n;
+}
+
+size_t ParseInfo::getDFASize(size_t decision) {
+ dfa::DFA &decisionToDFA = _atnSimulator->decisionToDFA[decision];
+ return decisionToDFA.states.size();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ParseInfo.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ParseInfo.h
new file mode 100644
index 0000000000..7ced7de433
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ParseInfo.h
@@ -0,0 +1,102 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionInfo.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ProfilingATNSimulator;
+
+ /// This class provides access to specific and aggregate statistics gathered
+ /// during profiling of a parser.
+ class ANTLR4CPP_PUBLIC ParseInfo {
+ public:
+ ParseInfo(ProfilingATNSimulator *atnSimulator);
+ ParseInfo(ParseInfo const&) = default;
+ virtual ~ParseInfo();
+
+ ParseInfo& operator=(ParseInfo const&) = default;
+
+ /// <summary>
+ /// Gets an array of <seealso cref="DecisionInfo"/> instances containing the profiling
+ /// information gathered for each decision in the ATN.
+ /// </summary>
+ /// <returns> An array of <seealso cref="DecisionInfo"/> instances, indexed by decision
+ /// number. </returns>
+ virtual std::vector<DecisionInfo> getDecisionInfo();
+
+ /// <summary>
+ /// Gets the decision numbers for decisions that required one or more
+ /// full-context predictions during parsing. These are decisions for which
+ /// <seealso cref="DecisionInfo#LL_Fallback"/> is non-zero.
+ /// </summary>
+ /// <returns> A list of decision numbers which required one or more
+ /// full-context predictions during parsing. </returns>
+ virtual std::vector<size_t> getLLDecisions();
+
+ /// <summary>
+ /// Gets the total time spent during prediction across all decisions made
+ /// during parsing. This value is the sum of
+ /// <seealso cref="DecisionInfo#timeInPrediction"/> for all decisions.
+ /// </summary>
+ virtual long long getTotalTimeInPrediction();
+
+ /// <summary>
+ /// Gets the total number of SLL lookahead operations across all decisions
+ /// made during parsing. This value is the sum of
+ /// <seealso cref="DecisionInfo#SLL_TotalLook"/> for all decisions.
+ /// </summary>
+ virtual long long getTotalSLLLookaheadOps();
+
+ /// <summary>
+ /// Gets the total number of LL lookahead operations across all decisions
+ /// made during parsing. This value is the sum of
+ /// <seealso cref="DecisionInfo#LL_TotalLook"/> for all decisions.
+ /// </summary>
+ virtual long long getTotalLLLookaheadOps();
+
+ /// <summary>
+ /// Gets the total number of ATN lookahead operations for SLL prediction
+ /// across all decisions made during parsing.
+ /// </summary>
+ virtual long long getTotalSLLATNLookaheadOps();
+
+ /// <summary>
+ /// Gets the total number of ATN lookahead operations for LL prediction
+ /// across all decisions made during parsing.
+ /// </summary>
+ virtual long long getTotalLLATNLookaheadOps();
+
+ /// <summary>
+ /// Gets the total number of ATN lookahead operations for SLL and LL
+ /// prediction across all decisions made during parsing.
+ ///
+ /// <para>
+ /// This value is the sum of <seealso cref="#getTotalSLLATNLookaheadOps"/> and
+ /// <seealso cref="#getTotalLLATNLookaheadOps"/>.</para>
+ /// </summary>
+ virtual long long getTotalATNLookaheadOps();
+
+ /// <summary>
+ /// Gets the total number of DFA states stored in the DFA cache for all
+ /// decisions in the ATN.
+ /// </summary>
+ virtual size_t getDFASize();
+
+ /// <summary>
+ /// Gets the total number of DFA states stored in the DFA cache for a
+ /// particular decision.
+ /// </summary>
+ virtual size_t getDFASize(size_t decision);
+
+ protected:
+ const ProfilingATNSimulator *_atnSimulator; // non-owning, we are created by this simulator.
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulator.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulator.cpp
new file mode 100644
index 0000000000..ad1da03570
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulator.cpp
@@ -0,0 +1,1387 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "dfa/DFA.h"
+#include "NoViableAltException.h"
+#include "atn/DecisionState.h"
+#include "ParserRuleContext.h"
+#include "misc/IntervalSet.h"
+#include "Parser.h"
+#include "CommonTokenStream.h"
+#include "atn/NotSetTransition.h"
+#include "atn/AtomTransition.h"
+#include "atn/RuleTransition.h"
+#include "atn/PredicateTransition.h"
+#include "atn/PrecedencePredicateTransition.h"
+#include "atn/SingletonPredictionContext.h"
+#include "atn/ActionTransition.h"
+#include "atn/EpsilonTransition.h"
+#include "atn/RuleStopState.h"
+#include "atn/ATNConfigSet.h"
+#include "atn/ATNConfig.h"
+#include "internal/Synchronization.h"
+
+#include "atn/StarLoopEntryState.h"
+#include "atn/BlockStartState.h"
+#include "atn/BlockEndState.h"
+
+#include "misc/Interval.h"
+#include "ANTLRErrorListener.h"
+
+#include "Vocabulary.h"
+#include "support/Arrays.h"
+#include "support/Casts.h"
+
+#include "atn/ParserATNSimulator.h"
+
+#define DEBUG_ATN 0
+#define DEBUG_LIST_ATN_DECISIONS 0
+#define DEBUG_DFA 0
+#define RETRY_DEBUG 0
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::internal;
+using namespace antlrcpp;
+
+const bool ParserATNSimulator::TURN_OFF_LR_LOOP_ENTRY_BRANCH_OPT = ParserATNSimulator::getLrLoopSetting();
+
+ParserATNSimulator::ParserATNSimulator(const ATN &atn, std::vector<dfa::DFA> &decisionToDFA,
+ PredictionContextCache &sharedContextCache)
+: ParserATNSimulator(nullptr, atn, decisionToDFA, sharedContextCache) {
+}
+
+ParserATNSimulator::ParserATNSimulator(Parser *parser, const ATN &atn, std::vector<dfa::DFA> &decisionToDFA,
+ PredictionContextCache &sharedContextCache)
+: ParserATNSimulator(parser, atn, decisionToDFA, sharedContextCache, ParserATNSimulatorOptions()) {}
+
+ParserATNSimulator::ParserATNSimulator(Parser *parser, const ATN &atn, std::vector<dfa::DFA> &decisionToDFA,
+ PredictionContextCache &sharedContextCache,
+ const ParserATNSimulatorOptions &options)
+: ATNSimulator(atn, sharedContextCache), decisionToDFA(decisionToDFA), parser(parser),
+ mergeCache(options.getPredictionContextMergeCacheOptions()) {
+ InitializeInstanceFields();
+}
+
+void ParserATNSimulator::reset() {
+}
+
+void ParserATNSimulator::clearDFA() {
+ int size = (int)decisionToDFA.size();
+ decisionToDFA.clear();
+ for (int d = 0; d < size; ++d) {
+ decisionToDFA.push_back(dfa::DFA(atn.getDecisionState(d), d));
+ }
+}
+
+size_t ParserATNSimulator::adaptivePredict(TokenStream *input, size_t decision, ParserRuleContext *outerContext) {
+
+#if DEBUG_ATN == 1 || DEBUG_LIST_ATN_DECISIONS == 1
+ std::cout << "adaptivePredict decision " << decision << " exec LA(1)==" << getLookaheadName(input) << " line "
+ << input->LT(1)->getLine() << ":" << input->LT(1)->getCharPositionInLine() << std::endl;
+#endif
+
+ _input = input;
+ _startIndex = input->index();
+ _outerContext = outerContext;
+ dfa::DFA &dfa = decisionToDFA[decision];
+ _dfa = &dfa;
+
+ ssize_t m = input->mark();
+ size_t index = _startIndex;
+
+ // Now we are certain to have a specific decision's DFA
+ // But, do we still need an initial state?
+ auto onExit = finally([this, input, index, m] {
+ if (mergeCache.getOptions().getClearEveryN() != 0) {
+ if (++_mergeCacheCounter == mergeCache.getOptions().getClearEveryN()) {
+ mergeCache.clear();
+ _mergeCacheCounter = 0;
+ }
+ }
+ _dfa = nullptr;
+ input->seek(index);
+ input->release(m);
+ });
+
+ dfa::DFAState *s0;
+ {
+ SharedLock<SharedMutex> stateLock(atn._stateMutex);
+ if (dfa.isPrecedenceDfa()) {
+ // the start state for a precedence DFA depends on the current
+ // parser precedence, and is provided by a DFA method.
+ SharedLock<SharedMutex> edgeLock(atn._edgeMutex);
+ s0 = dfa.getPrecedenceStartState(parser->getPrecedence());
+ } else {
+ // the start state for a "regular" DFA is just s0
+ s0 = dfa.s0;
+ }
+ }
+
+ if (s0 == nullptr) {
+ auto s0_closure = computeStartState(dfa.atnStartState, &ParserRuleContext::EMPTY, false);
+ std::unique_ptr<dfa::DFAState> newState;
+ std::unique_ptr<dfa::DFAState> oldState;
+ UniqueLock<SharedMutex> stateLock(atn._stateMutex);
+ dfa::DFAState* ds0 = dfa.s0;
+ if (dfa.isPrecedenceDfa()) {
+ /* If this is a precedence DFA, we use applyPrecedenceFilter
+ * to convert the computed start state to a precedence start
+ * state. We then use DFA.setPrecedenceStartState to set the
+ * appropriate start state for the precedence level rather
+ * than simply setting DFA.s0.
+ */
+ ds0->configs = std::move(s0_closure); // not used for prediction but useful to know start configs anyway
+ newState = std::make_unique<dfa::DFAState>(applyPrecedenceFilter(ds0->configs.get()));
+ s0 = addDFAState(dfa, newState.get());
+ UniqueLock<SharedMutex> edgeLock(atn._edgeMutex);
+ dfa.setPrecedenceStartState(parser->getPrecedence(), s0);
+ } else {
+ newState = std::make_unique<dfa::DFAState>(std::move(s0_closure));
+ s0 = addDFAState(dfa, newState.get());
+ if (ds0 != s0) {
+ oldState.reset(ds0);
+ dfa.s0 = s0;
+ }
+ }
+ if (s0 == newState.get()) {
+ newState.release();
+ }
+ }
+
+ // We can start with an existing DFA.
+ size_t alt = execATN(dfa, s0, input, index, outerContext != nullptr ? outerContext : &ParserRuleContext::EMPTY);
+
+ return alt;
+}
+
+size_t ParserATNSimulator::execATN(dfa::DFA &dfa, dfa::DFAState *s0, TokenStream *input, size_t startIndex,
+ ParserRuleContext *outerContext) {
+
+#if DEBUG_ATN == 1 || DEBUG_LIST_ATN_DECISIONS == 1
+ std::cout << "execATN decision " << dfa.decision << " exec LA(1)==" << getLookaheadName(input) <<
+ " line " << input->LT(1)->getLine() << ":" << input->LT(1)->getCharPositionInLine() << std::endl;
+#endif
+
+ dfa::DFAState *previousD = s0;
+
+#if DEBUG_ATN == 1
+ std::cout << "s0 = " << s0 << std::endl;
+#endif
+
+ size_t t = input->LA(1);
+
+ while (true) { // while more work
+ dfa::DFAState *D = getExistingTargetState(previousD, t);
+ if (D == nullptr) {
+ D = computeTargetState(dfa, previousD, t);
+ }
+
+ if (D == ERROR.get()) {
+ // if any configs in previous dipped into outer context, that
+ // means that input up to t actually finished entry rule
+ // at least for SLL decision. Full LL doesn't dip into outer
+ // so don't need special case.
+ // We will get an error no matter what so delay until after
+ // decision; better error message. Also, no reachable target
+ // ATN states in SLL implies LL will also get nowhere.
+ // If conflict in states that dip out, choose min since we
+ // will get error no matter what.
+ NoViableAltException e = noViableAlt(input, outerContext, previousD->configs.get(), startIndex, false);
+ input->seek(startIndex);
+ size_t alt = getSynValidOrSemInvalidAltThatFinishedDecisionEntryRule(previousD->configs.get(), outerContext);
+ if (alt != ATN::INVALID_ALT_NUMBER) {
+ return alt;
+ }
+
+ throw e;
+ }
+
+ if (D->requiresFullContext && _mode != PredictionMode::SLL) {
+ // IF PREDS, MIGHT RESOLVE TO SINGLE ALT => SLL (or syntax error)
+ BitSet conflictingAlts;
+ if (D->predicates.size() != 0) {
+#if DEBUG_ATN == 1
+ std::cout << "DFA state has preds in DFA sim LL failover" << std::endl;
+#endif
+
+ size_t conflictIndex = input->index();
+ if (conflictIndex != startIndex) {
+ input->seek(startIndex);
+ }
+
+ conflictingAlts = evalSemanticContext(D->predicates, outerContext, true);
+ if (conflictingAlts.count() == 1) {
+#if DEBUG_ATN == 1
+ std::cout << "Full LL avoided" << std::endl;
+#endif
+
+ return conflictingAlts.nextSetBit(0);
+ }
+
+ if (conflictIndex != startIndex) {
+ // restore the index so reporting the fallback to full
+ // context occurs with the index at the correct spot
+ input->seek(conflictIndex);
+ }
+ }
+
+#if DEBUG_DFA == 1
+ std::cout << "ctx sensitive state " << outerContext << " in " << D << std::endl;
+#endif
+
+ bool fullCtx = true;
+ std::unique_ptr<ATNConfigSet> s0_closure = computeStartState(dfa.atnStartState, outerContext, fullCtx);
+ reportAttemptingFullContext(dfa, conflictingAlts, D->configs.get(), startIndex, input->index());
+ size_t alt = execATNWithFullContext(dfa, D, s0_closure.get(), input, startIndex, outerContext);
+ return alt;
+ }
+
+ if (D->isAcceptState) {
+ if (D->predicates.empty()) {
+ return D->prediction;
+ }
+
+ size_t stopIndex = input->index();
+ input->seek(startIndex);
+ BitSet alts = evalSemanticContext(D->predicates, outerContext, true);
+ switch (alts.count()) {
+ case 0:
+ throw noViableAlt(input, outerContext, D->configs.get(), startIndex, false);
+
+ case 1:
+ return alts.nextSetBit(0);
+
+ default:
+ // report ambiguity after predicate evaluation to make sure the correct
+ // set of ambig alts is reported.
+ reportAmbiguity(dfa, D, startIndex, stopIndex, false, alts, D->configs.get());
+ return alts.nextSetBit(0);
+ }
+ }
+
+ previousD = D;
+
+ if (t != Token::EOF) {
+ input->consume();
+ t = input->LA(1);
+ }
+ }
+}
+
+dfa::DFAState *ParserATNSimulator::getExistingTargetState(dfa::DFAState *previousD, size_t t) {
+ dfa::DFAState* retval;
+ SharedLock<SharedMutex> edgeLock(atn._edgeMutex);
+ auto iterator = previousD->edges.find(t);
+ retval = (iterator == previousD->edges.end()) ? nullptr : iterator->second;
+ return retval;
+}
+
+dfa::DFAState *ParserATNSimulator::computeTargetState(dfa::DFA &dfa, dfa::DFAState *previousD, size_t t) {
+ std::unique_ptr<ATNConfigSet> reach = computeReachSet(previousD->configs.get(), t, false);
+ if (reach == nullptr) {
+ addDFAEdge(dfa, previousD, t, ERROR.get());
+ return ERROR.get();
+ }
+
+ // create new target state; we'll add to DFA after it's complete
+ dfa::DFAState *D = new dfa::DFAState(std::move(reach)); /* mem-check: managed by the DFA or deleted below, "reach" is no longer valid now. */
+ size_t predictedAlt = getUniqueAlt(D->configs.get());
+
+ if (predictedAlt != ATN::INVALID_ALT_NUMBER) {
+ // NO CONFLICT, UNIQUELY PREDICTED ALT
+ D->isAcceptState = true;
+ D->configs->uniqueAlt = predictedAlt;
+ D->prediction = predictedAlt;
+ } else if (PredictionModeClass::hasSLLConflictTerminatingPrediction(_mode, D->configs.get())) {
+ // MORE THAN ONE VIABLE ALTERNATIVE
+ D->configs->conflictingAlts = getConflictingAlts(D->configs.get());
+ D->requiresFullContext = true;
+ // in SLL-only mode, we will stop at this state and return the minimum alt
+ D->isAcceptState = true;
+ D->prediction = D->configs->conflictingAlts.nextSetBit(0);
+ }
+
+ if (D->isAcceptState && D->configs->hasSemanticContext) {
+ predicateDFAState(D, atn.getDecisionState(dfa.decision));
+ if (D->predicates.size() != 0) {
+ D->prediction = ATN::INVALID_ALT_NUMBER;
+ }
+ }
+
+ // all adds to dfa are done after we've created full D state
+ dfa::DFAState *state = addDFAEdge(dfa, previousD, t, D);
+ if (state != D) {
+ delete D; // If the new state exists already we don't need it and use the existing one instead.
+ }
+ return state;
+}
+
+void ParserATNSimulator::predicateDFAState(dfa::DFAState *dfaState, DecisionState *decisionState) {
+ // We need to test all predicates, even in DFA states that
+ // uniquely predict alternative.
+ size_t nalts = decisionState->transitions.size();
+
+ // Update DFA so reach becomes accept state with (predicate,alt)
+ // pairs if preds found for conflicting alts
+ BitSet altsToCollectPredsFrom = getConflictingAltsOrUniqueAlt(dfaState->configs.get());
+ std::vector<Ref<const SemanticContext>> altToPred = getPredsForAmbigAlts(altsToCollectPredsFrom, dfaState->configs.get(), nalts);
+ if (!altToPred.empty()) {
+ dfaState->predicates = getPredicatePredictions(altsToCollectPredsFrom, altToPred);
+ dfaState->prediction = ATN::INVALID_ALT_NUMBER; // make sure we use preds
+ } else {
+ // There are preds in configs but they might go away
+ // when OR'd together like {p}? || NONE == NONE. If neither
+ // alt has preds, resolve to min alt
+ dfaState->prediction = altsToCollectPredsFrom.nextSetBit(0);
+ }
+}
+
+size_t ParserATNSimulator::execATNWithFullContext(dfa::DFA &dfa, dfa::DFAState *D, ATNConfigSet *s0,
+ TokenStream *input, size_t startIndex, ParserRuleContext *outerContext) {
+
+ bool fullCtx = true;
+ bool foundExactAmbig = false;
+
+ std::unique_ptr<ATNConfigSet> reach;
+ ATNConfigSet *previous = s0;
+ input->seek(startIndex);
+ size_t t = input->LA(1);
+ size_t predictedAlt;
+
+ while (true) {
+ reach = computeReachSet(previous, t, fullCtx);
+ if (reach == nullptr) {
+ // if any configs in previous dipped into outer context, that
+ // means that input up to t actually finished entry rule
+ // at least for LL decision. Full LL doesn't dip into outer
+ // so don't need special case.
+ // We will get an error no matter what so delay until after
+ // decision; better error message. Also, no reachable target
+ // ATN states in SLL implies LL will also get nowhere.
+ // If conflict in states that dip out, choose min since we
+ // will get error no matter what.
+ NoViableAltException e = noViableAlt(input, outerContext, previous, startIndex, previous != s0);
+ input->seek(startIndex);
+ size_t alt = getSynValidOrSemInvalidAltThatFinishedDecisionEntryRule(previous, outerContext);
+ if (alt != ATN::INVALID_ALT_NUMBER) {
+ return alt;
+ }
+ throw e;
+ }
+ if (previous != s0) // Don't delete the start set.
+ delete previous;
+ previous = nullptr;
+
+ std::vector<BitSet> altSubSets = PredictionModeClass::getConflictingAltSubsets(reach.get());
+ reach->uniqueAlt = getUniqueAlt(reach.get());
+ // unique prediction?
+ if (reach->uniqueAlt != ATN::INVALID_ALT_NUMBER) {
+ predictedAlt = reach->uniqueAlt;
+ break;
+ }
+ if (_mode != PredictionMode::LL_EXACT_AMBIG_DETECTION) {
+ predictedAlt = PredictionModeClass::resolvesToJustOneViableAlt(altSubSets);
+ if (predictedAlt != ATN::INVALID_ALT_NUMBER) {
+ break;
+ }
+ } else {
+ // In exact ambiguity mode, we never try to terminate early.
+ // Just keeps scarfing until we know what the conflict is
+ if (PredictionModeClass::allSubsetsConflict(altSubSets) && PredictionModeClass::allSubsetsEqual(altSubSets)) {
+ foundExactAmbig = true;
+ predictedAlt = PredictionModeClass::getSingleViableAlt(altSubSets);
+ break;
+ }
+ // else there are multiple non-conflicting subsets or
+ // we're not sure what the ambiguity is yet.
+ // So, keep going.
+ }
+ previous = reach.release();
+
+ if (t != Token::EOF) {
+ input->consume();
+ t = input->LA(1);
+ }
+ }
+
+ if (previous != s0) // Don't delete the start set
+ delete previous;
+
+ // If the configuration set uniquely predicts an alternative,
+ // without conflict, then we know that it's a full LL decision
+ // not SLL.
+ if (reach->uniqueAlt != ATN::INVALID_ALT_NUMBER) {
+ reportContextSensitivity(dfa, predictedAlt, reach.get(), startIndex, input->index());
+ return predictedAlt;
+ }
+
+ // We do not check predicates here because we have checked them
+ // on-the-fly when doing full context prediction.
+
+ /*
+ In non-exact ambiguity detection mode, we might actually be able to
+ detect an exact ambiguity, but I'm not going to spend the cycles
+ needed to check. We only emit ambiguity warnings in exact ambiguity
+ mode.
+
+ For example, we might know that we have conflicting configurations.
+ But, that does not mean that there is no way forward without a
+ conflict. It's possible to have nonconflicting alt subsets as in:
+
+ LL altSubSets=[{1, 2}, {1, 2}, {1}, {1, 2}]
+
+ from
+
+ [(17,1,[5 $]), (13,1,[5 10 $]), (21,1,[5 10 $]), (11,1,[$]),
+ (13,2,[5 10 $]), (21,2,[5 10 $]), (11,2,[$])]
+
+ In this case, (17,1,[5 $]) indicates there is some next sequence that
+ would resolve this without conflict to alternative 1. Any other viable
+ next sequence, however, is associated with a conflict. We stop
+ looking for input because no amount of further lookahead will alter
+ the fact that we should predict alternative 1. We just can't say for
+ sure that there is an ambiguity without looking further.
+ */
+ reportAmbiguity(dfa, D, startIndex, input->index(), foundExactAmbig, reach->getAlts(), reach.get());
+
+ return predictedAlt;
+}
+
+std::unique_ptr<ATNConfigSet> ParserATNSimulator::computeReachSet(ATNConfigSet *closure_, size_t t, bool fullCtx) {
+
+ std::unique_ptr<ATNConfigSet> intermediate(new ATNConfigSet(fullCtx));
+
+ /* Configurations already in a rule stop state indicate reaching the end
+ * of the decision rule (local context) or end of the start rule (full
+ * context). Once reached, these configurations are never updated by a
+ * closure operation, so they are handled separately for the performance
+ * advantage of having a smaller intermediate set when calling closure.
+ *
+ * For full-context reach operations, separate handling is required to
+ * ensure that the alternative matching the longest overall sequence is
+ * chosen when multiple such configurations can match the input.
+ */
+ std::vector<Ref<ATNConfig>> skippedStopStates;
+
+ // First figure out where we can reach on input t
+ for (const auto &c : closure_->configs) {
+ if (RuleStopState::is(c->state)) {
+ assert(c->context->isEmpty());
+
+ if (fullCtx || t == Token::EOF) {
+ skippedStopStates.push_back(c);
+ }
+
+ continue;
+ }
+
+ size_t n = c->state->transitions.size();
+ for (size_t ti = 0; ti < n; ti++) { // for each transition
+ const Transition *trans = c->state->transitions[ti].get();
+ ATNState *target = getReachableTarget(trans, (int)t);
+ if (target != nullptr) {
+ intermediate->add(std::make_shared<ATNConfig>(*c, target), &mergeCache);
+ }
+ }
+ }
+
+ // Now figure out where the reach operation can take us...
+ std::unique_ptr<ATNConfigSet> reach;
+
+ /* This block optimizes the reach operation for intermediate sets which
+ * trivially indicate a termination state for the overall
+ * adaptivePredict operation.
+ *
+ * The conditions assume that intermediate
+ * contains all configurations relevant to the reach set, but this
+ * condition is not true when one or more configurations have been
+ * withheld in skippedStopStates, or when the current symbol is EOF.
+ */
+ if (skippedStopStates.empty() && t != Token::EOF) {
+ if (intermediate->size() == 1) {
+ // Don't pursue the closure if there is just one state.
+ // It can only have one alternative; just add to result
+ // Also don't pursue the closure if there is unique alternative
+ // among the configurations.
+ reach = std::move(intermediate);
+ } else if (getUniqueAlt(intermediate.get()) != ATN::INVALID_ALT_NUMBER) {
+ // Also don't pursue the closure if there is unique alternative
+ // among the configurations.
+ reach = std::move(intermediate);
+ }
+ }
+
+ /* If the reach set could not be trivially determined, perform a closure
+ * operation on the intermediate set to compute its initial value.
+ */
+ if (reach == nullptr) {
+ reach.reset(new ATNConfigSet(fullCtx));
+ ATNConfig::Set closureBusy;
+
+ bool treatEofAsEpsilon = t == Token::EOF;
+ for (const auto &c : intermediate->configs) {
+ closure(c, reach.get(), closureBusy, false, fullCtx, treatEofAsEpsilon);
+ }
+ }
+
+ if (t == IntStream::EOF) {
+ /* After consuming EOF no additional input is possible, so we are
+ * only interested in configurations which reached the end of the
+ * decision rule (local context) or end of the start rule (full
+ * context). Update reach to contain only these configurations. This
+ * handles both explicit EOF transitions in the grammar and implicit
+ * EOF transitions following the end of the decision or start rule.
+ *
+ * When reach==intermediate, no closure operation was performed. In
+ * this case, removeAllConfigsNotInRuleStopState needs to check for
+ * reachable rule stop states as well as configurations already in
+ * a rule stop state.
+ *
+ * This is handled before the configurations in skippedStopStates,
+ * because any configurations potentially added from that list are
+ * already guaranteed to meet this condition whether or not it's
+ * required.
+ */
+ ATNConfigSet *temp = removeAllConfigsNotInRuleStopState(reach.get(), *reach == *intermediate);
+ if (temp != reach.get())
+ reach.reset(temp); // We got a new set, so use that.
+ }
+
+ /* If skippedStopStates is not null, then it contains at least one
+ * configuration. For full-context reach operations, these
+ * configurations reached the end of the start rule, in which case we
+ * only add them back to reach if no configuration during the current
+ * closure operation reached such a state. This ensures adaptivePredict
+ * chooses an alternative matching the longest overall sequence when
+ * multiple alternatives are viable.
+ */
+ if (skippedStopStates.size() > 0 && (!fullCtx || !PredictionModeClass::hasConfigInRuleStopState(reach.get()))) {
+ assert(!skippedStopStates.empty());
+
+ for (const auto &c : skippedStopStates) {
+ reach->add(c, &mergeCache);
+ }
+ }
+
+ if (reach->isEmpty()) {
+ return nullptr;
+ }
+ return reach;
+}
+
+ATNConfigSet* ParserATNSimulator::removeAllConfigsNotInRuleStopState(ATNConfigSet *configs,
+ bool lookToEndOfRule) {
+ if (PredictionModeClass::allConfigsInRuleStopStates(configs)) {
+ return configs;
+ }
+
+ ATNConfigSet *result = new ATNConfigSet(configs->fullCtx); /* mem-check: released by caller */
+
+ for (const auto &config : configs->configs) {
+ if (config->state != nullptr && config->state->getStateType() == ATNStateType::RULE_STOP) {
+ result->add(config, &mergeCache);
+ continue;
+ }
+
+ if (lookToEndOfRule && config->state->epsilonOnlyTransitions) {
+ misc::IntervalSet nextTokens = atn.nextTokens(config->state);
+ if (nextTokens.contains(Token::EPSILON)) {
+ ATNState *endOfRuleState = atn.ruleToStopState[config->state->ruleIndex];
+ result->add(std::make_shared<ATNConfig>(*config, endOfRuleState), &mergeCache);
+ }
+ }
+ }
+
+ return result;
+}
+
+std::unique_ptr<ATNConfigSet> ParserATNSimulator::computeStartState(ATNState *p, RuleContext *ctx, bool fullCtx) {
+ // always at least the implicit call to start rule
+ Ref<const PredictionContext> initialContext = PredictionContext::fromRuleContext(atn, ctx);
+ std::unique_ptr<ATNConfigSet> configs(new ATNConfigSet(fullCtx));
+
+ for (size_t i = 0; i < p->transitions.size(); i++) {
+ ATNState *target = p->transitions[i]->target;
+ Ref<ATNConfig> c = std::make_shared<ATNConfig>(target, (int)i + 1, initialContext);
+ ATNConfig::Set closureBusy;
+ closure(c, configs.get(), closureBusy, true, fullCtx, false);
+ }
+
+ return configs;
+}
+
+std::unique_ptr<ATNConfigSet> ParserATNSimulator::applyPrecedenceFilter(ATNConfigSet *configs) {
+ std::map<size_t, Ref<const PredictionContext>> statesFromAlt1;
+ std::unique_ptr<ATNConfigSet> configSet(new ATNConfigSet(configs->fullCtx));
+ for (const auto &config : configs->configs) {
+ // handle alt 1 first
+ if (config->alt != 1) {
+ continue;
+ }
+
+ Ref<const SemanticContext> updatedContext = config->semanticContext->evalPrecedence(parser, _outerContext);
+ if (updatedContext == nullptr) {
+ // the configuration was eliminated
+ continue;
+ }
+
+ statesFromAlt1[config->state->stateNumber] = config->context;
+ if (updatedContext != config->semanticContext) {
+ configSet->add(std::make_shared<ATNConfig>(*config, updatedContext), &mergeCache);
+ }
+ else {
+ configSet->add(config, &mergeCache);
+ }
+ }
+
+ for (const auto &config : configs->configs) {
+ if (config->alt == 1) {
+ // already handled
+ continue;
+ }
+
+ if (!config->isPrecedenceFilterSuppressed()) {
+ /* In the future, this elimination step could be updated to also
+ * filter the prediction context for alternatives predicting alt>1
+ * (basically a graph subtraction algorithm).
+ */
+ auto iterator = statesFromAlt1.find(config->state->stateNumber);
+ if (iterator != statesFromAlt1.end() && *iterator->second == *config->context) {
+ // eliminated
+ continue;
+ }
+ }
+
+ configSet->add(config, &mergeCache);
+ }
+
+ return configSet;
+}
+
+atn::ATNState* ParserATNSimulator::getReachableTarget(const Transition *trans, size_t ttype) {
+ if (trans->matches(ttype, 0, atn.maxTokenType)) {
+ return trans->target;
+ }
+
+ return nullptr;
+}
+
+// Note that caller must memory manage the returned value from this function
+std::vector<Ref<const SemanticContext>> ParserATNSimulator::getPredsForAmbigAlts(const BitSet &ambigAlts,
+ ATNConfigSet *configs, size_t nalts) {
+ // REACH=[1|1|[]|0:0, 1|2|[]|0:1]
+ /* altToPred starts as an array of all null contexts. The entry at index i
+ * corresponds to alternative i. altToPred[i] may have one of three values:
+ * 1. null: no ATNConfig c is found such that c.alt==i
+ * 2. SemanticContext.NONE: At least one ATNConfig c exists such that
+ * c.alt==i and c.semanticContext==SemanticContext.NONE. In other words,
+ * alt i has at least one un-predicated config.
+ * 3. Non-NONE Semantic Context: There exists at least one, and for all
+ * ATNConfig c such that c.alt==i, c.semanticContext!=SemanticContext.NONE.
+ *
+ * From this, it is clear that NONE||anything==NONE.
+ */
+ std::vector<Ref<const SemanticContext>> altToPred(nalts + 1);
+
+ for (const auto &c : configs->configs) {
+ if (ambigAlts.test(c->alt)) {
+ altToPred[c->alt] = SemanticContext::Or(altToPred[c->alt], c->semanticContext);
+ }
+ }
+
+ size_t nPredAlts = 0;
+ for (size_t i = 1; i <= nalts; i++) {
+ if (altToPred[i] == nullptr) {
+ altToPred[i] = SemanticContext::Empty::Instance;
+ } else if (altToPred[i] != SemanticContext::Empty::Instance) {
+ nPredAlts++;
+ }
+ }
+
+ // nonambig alts are null in altToPred
+ if (nPredAlts == 0) {
+ altToPred.clear();
+ }
+#if DEBUG_ATN == 1
+ std::cout << "getPredsForAmbigAlts result " << Arrays::toString(altToPred) << std::endl;
+#endif
+
+ return altToPred;
+}
+
+std::vector<dfa::DFAState::PredPrediction> ParserATNSimulator::getPredicatePredictions(const antlrcpp::BitSet &ambigAlts,
+ const std::vector<Ref<const SemanticContext>> &altToPred) {
+ bool containsPredicate = std::find_if(altToPred.begin(), altToPred.end(), [](const Ref<const SemanticContext> &context) {
+ return context != SemanticContext::Empty::Instance;
+ }) != altToPred.end();
+ std::vector<dfa::DFAState::PredPrediction> pairs;
+ if (containsPredicate) {
+ for (size_t i = 1; i < altToPred.size(); i++) {
+ const auto &pred = altToPred[i];
+ assert(pred != nullptr); // unpredicted is indicated by SemanticContext.NONE
+ if (ambigAlts.test(i)) {
+ pairs.emplace_back(pred, static_cast<int>(i));
+ }
+ }
+ }
+ return pairs;
+}
+
+size_t ParserATNSimulator::getSynValidOrSemInvalidAltThatFinishedDecisionEntryRule(ATNConfigSet *configs,
+ ParserRuleContext *outerContext)
+{
+ std::pair<ATNConfigSet *, ATNConfigSet *> sets = splitAccordingToSemanticValidity(configs, outerContext);
+ std::unique_ptr<ATNConfigSet> semValidConfigs(sets.first);
+ std::unique_ptr<ATNConfigSet> semInvalidConfigs(sets.second);
+ size_t alt = getAltThatFinishedDecisionEntryRule(semValidConfigs.get());
+ if (alt != ATN::INVALID_ALT_NUMBER) { // semantically/syntactically viable path exists
+ return alt;
+ }
+ // Is there a syntactically valid path with a failed pred?
+ if (!semInvalidConfigs->configs.empty()) {
+ alt = getAltThatFinishedDecisionEntryRule(semInvalidConfigs.get());
+ if (alt != ATN::INVALID_ALT_NUMBER) { // syntactically viable path exists
+ return alt;
+ }
+ }
+ return ATN::INVALID_ALT_NUMBER;
+}
+
+size_t ParserATNSimulator::getAltThatFinishedDecisionEntryRule(ATNConfigSet *configs) {
+ misc::IntervalSet alts;
+ for (const auto &c : configs->configs) {
+ if (c->getOuterContextDepth() > 0 || (c->state != nullptr && c->state->getStateType() == ATNStateType::RULE_STOP && c->context->hasEmptyPath())) {
+ alts.add(c->alt);
+ }
+ }
+ if (alts.size() == 0) {
+ return ATN::INVALID_ALT_NUMBER;
+ }
+ return alts.getMinElement();
+}
+
+std::pair<ATNConfigSet *, ATNConfigSet *> ParserATNSimulator::splitAccordingToSemanticValidity(ATNConfigSet *configs,
+ ParserRuleContext *outerContext) {
+
+ // mem-check: both pointers must be freed by the caller.
+ ATNConfigSet *succeeded(new ATNConfigSet(configs->fullCtx));
+ ATNConfigSet *failed(new ATNConfigSet(configs->fullCtx));
+ for (const auto &c : configs->configs) {
+ if (c->semanticContext != SemanticContext::Empty::Instance) {
+ bool predicateEvaluationResult = evalSemanticContext(c->semanticContext, outerContext, c->alt, configs->fullCtx);
+ if (predicateEvaluationResult) {
+ succeeded->add(c);
+ } else {
+ failed->add(c);
+ }
+ } else {
+ succeeded->add(c);
+ }
+ }
+ return { succeeded, failed };
+}
+
+BitSet ParserATNSimulator::evalSemanticContext(const std::vector<dfa::DFAState::PredPrediction> &predPredictions,
+ ParserRuleContext *outerContext, bool complete) {
+ BitSet predictions;
+ for (const auto &prediction : predPredictions) {
+ if (prediction.pred == SemanticContext::Empty::Instance) {
+ predictions.set(prediction.alt);
+ if (!complete) {
+ break;
+ }
+ continue;
+ }
+
+ bool fullCtx = false; // in dfa
+ bool predicateEvaluationResult = evalSemanticContext(prediction.pred, outerContext, prediction.alt, fullCtx);
+#if DEBUG_ATN == 1 || DEBUG_DFA == 1
+ std::cout << "eval pred " << prediction.toString() << " = " << predicateEvaluationResult << std::endl;
+#endif
+
+ if (predicateEvaluationResult) {
+#if DEBUG_ATN == 1 || DEBUG_DFA == 1
+ std::cout << "PREDICT " << prediction.alt << std::endl;
+#endif
+
+ predictions.set(prediction.alt);
+ if (!complete) {
+ break;
+ }
+ }
+ }
+
+ return predictions;
+}
+
+bool ParserATNSimulator::evalSemanticContext(Ref<const SemanticContext> const& pred, ParserRuleContext *parserCallStack,
+ size_t /*alt*/, bool /*fullCtx*/) {
+ return pred->eval(parser, parserCallStack);
+}
+
+void ParserATNSimulator::closure(Ref<ATNConfig> const& config, ATNConfigSet *configs, ATNConfig::Set &closureBusy,
+ bool collectPredicates, bool fullCtx, bool treatEofAsEpsilon) {
+ const int initialDepth = 0;
+ closureCheckingStopState(config, configs, closureBusy, collectPredicates, fullCtx, initialDepth, treatEofAsEpsilon);
+
+ assert(!fullCtx || !configs->dipsIntoOuterContext);
+}
+
+void ParserATNSimulator::closureCheckingStopState(Ref<ATNConfig> const& config, ATNConfigSet *configs,
+ ATNConfig::Set &closureBusy, bool collectPredicates, bool fullCtx, int depth, bool treatEofAsEpsilon) {
+
+#if DEBUG_ATN == 1
+ std::cout << "closure(" << config->toString(true) << ")" << std::endl;
+#endif
+
+ if (config->state != nullptr && config->state->getStateType() == ATNStateType::RULE_STOP) {
+ // We hit rule end. If we have context info, use it
+ // run thru all possible stack tops in ctx
+ if (!config->context->isEmpty()) {
+ for (size_t i = 0; i < config->context->size(); i++) {
+ if (config->context->getReturnState(i) == PredictionContext::EMPTY_RETURN_STATE) {
+ if (fullCtx) {
+ configs->add(std::make_shared<ATNConfig>(*config, config->state, PredictionContext::EMPTY), &mergeCache);
+ continue;
+ } else {
+ // we have no context info, just chase follow links (if greedy)
+#if DEBUG_ATN == 1
+ std::cout << "FALLING off rule " << getRuleName(config->state->ruleIndex) << std::endl;
+#endif
+ closure_(config, configs, closureBusy, collectPredicates, fullCtx, depth, treatEofAsEpsilon);
+ }
+ continue;
+ }
+ ATNState *returnState = atn.states[config->context->getReturnState(i)];
+ Ref<const PredictionContext> newContext = config->context->getParent(i); // "pop" return state
+ Ref<ATNConfig> c = std::make_shared<ATNConfig>(returnState, config->alt, newContext, config->semanticContext);
+ // While we have context to pop back from, we may have
+ // gotten that context AFTER having falling off a rule.
+ // Make sure we track that we are now out of context.
+ //
+ // This assignment also propagates the
+ // isPrecedenceFilterSuppressed() value to the new
+ // configuration.
+ c->reachesIntoOuterContext = config->reachesIntoOuterContext;
+ assert(depth > INT_MIN);
+
+ closureCheckingStopState(c, configs, closureBusy, collectPredicates, fullCtx, depth - 1, treatEofAsEpsilon);
+ }
+ return;
+ } else if (fullCtx) {
+ // reached end of start rule
+ configs->add(config, &mergeCache);
+ return;
+ } else {
+ // else if we have no context info, just chase follow links (if greedy)
+ }
+ }
+
+ closure_(config, configs, closureBusy, collectPredicates, fullCtx, depth, treatEofAsEpsilon);
+}
+
+void ParserATNSimulator::closure_(Ref<ATNConfig> const& config, ATNConfigSet *configs, ATNConfig::Set &closureBusy,
+ bool collectPredicates, bool fullCtx, int depth, bool treatEofAsEpsilon) {
+ ATNState *p = config->state;
+ // optimization
+ if (!p->epsilonOnlyTransitions) {
+ // make sure to not return here, because EOF transitions can act as
+ // both epsilon transitions and non-epsilon transitions.
+ configs->add(config, &mergeCache);
+ }
+
+ for (size_t i = 0; i < p->transitions.size(); i++) {
+ if (i == 0 && canDropLoopEntryEdgeInLeftRecursiveRule(config.get()))
+ continue;
+
+ const Transition *t = p->transitions[i].get();
+ bool continueCollecting = !(t != nullptr && t->getTransitionType() == TransitionType::ACTION) && collectPredicates;
+ Ref<ATNConfig> c = getEpsilonTarget(config, t, continueCollecting, depth == 0, fullCtx, treatEofAsEpsilon);
+ if (c != nullptr) {
+ int newDepth = depth;
+ if (config->state != nullptr && config->state->getStateType() == ATNStateType::RULE_STOP) {
+ assert(!fullCtx);
+
+ // target fell off end of rule; mark resulting c as having dipped into outer context
+ // We can't get here if incoming config was rule stop and we had context
+ // track how far we dip into outer context. Might
+ // come in handy and we avoid evaluating context dependent
+ // preds if this is > 0.
+
+ if (closureBusy.count(c) > 0) {
+ // avoid infinite recursion for right-recursive rules
+ continue;
+ }
+ closureBusy.insert(c);
+
+ if (_dfa != nullptr && _dfa->isPrecedenceDfa()) {
+ size_t outermostPrecedenceReturn = downCast<const EpsilonTransition *>(t)->outermostPrecedenceReturn();
+ if (outermostPrecedenceReturn == _dfa->atnStartState->ruleIndex) {
+ c->setPrecedenceFilterSuppressed(true);
+ }
+ }
+
+ c->reachesIntoOuterContext++;
+
+ if (!t->isEpsilon()) {
+ // avoid infinite recursion for EOF* and EOF+
+ if (closureBusy.count(c) == 0) {
+ closureBusy.insert(c);
+ } else {
+ continue;
+ }
+ }
+
+ configs->dipsIntoOuterContext = true; // TODO: can remove? only care when we add to set per middle of this method
+ assert(newDepth > INT_MIN);
+
+ newDepth--;
+#if DEBUG_DFA == 1
+ std::cout << "dips into outer ctx: " << c << std::endl;
+#endif
+
+ } else if (!t->isEpsilon()) {
+ // avoid infinite recursion for EOF* and EOF+
+ if (closureBusy.count(c) == 0) {
+ closureBusy.insert(c);
+ } else {
+ continue;
+ }
+ }
+
+ if (t != nullptr && t->getTransitionType() == TransitionType::RULE) {
+ // latch when newDepth goes negative - once we step out of the entry context we can't return
+ if (newDepth >= 0) {
+ newDepth++;
+ }
+ }
+
+ closureCheckingStopState(c, configs, closureBusy, continueCollecting, fullCtx, newDepth, treatEofAsEpsilon);
+ }
+ }
+}
+
+bool ParserATNSimulator::canDropLoopEntryEdgeInLeftRecursiveRule(ATNConfig *config) const {
+ if (TURN_OFF_LR_LOOP_ENTRY_BRANCH_OPT)
+ return false;
+
+ ATNState *p = config->state;
+
+ // First check to see if we are in StarLoopEntryState generated during
+ // left-recursion elimination. For efficiency, also check if
+ // the context has an empty stack case. If so, it would mean
+ // global FOLLOW so we can't perform optimization
+ if (p->getStateType() != ATNStateType::STAR_LOOP_ENTRY ||
+ !((StarLoopEntryState *)p)->isPrecedenceDecision || // Are we the special loop entry/exit state?
+ config->context->isEmpty() || // If SLL wildcard
+ config->context->hasEmptyPath())
+ {
+ return false;
+ }
+
+ // Require all return states to return back to the same rule
+ // that p is in.
+ size_t numCtxs = config->context->size();
+ for (size_t i = 0; i < numCtxs; i++) { // for each stack context
+ ATNState *returnState = atn.states[config->context->getReturnState(i)];
+ if (returnState->ruleIndex != p->ruleIndex)
+ return false;
+ }
+
+ BlockStartState *decisionStartState = (BlockStartState *)p->transitions[0]->target;
+ size_t blockEndStateNum = decisionStartState->endState->stateNumber;
+ BlockEndState *blockEndState = (BlockEndState *)atn.states[blockEndStateNum];
+
+ // Verify that the top of each stack context leads to loop entry/exit
+ // state through epsilon edges and w/o leaving rule.
+ for (size_t i = 0; i < numCtxs; i++) { // for each stack context
+ size_t returnStateNumber = config->context->getReturnState(i);
+ ATNState *returnState = atn.states[returnStateNumber];
+ // All states must have single outgoing epsilon edge.
+ if (returnState->transitions.size() != 1 || !returnState->transitions[0]->isEpsilon())
+ {
+ return false;
+ }
+
+ // Look for prefix op case like 'not expr', (' type ')' expr
+ ATNState *returnStateTarget = returnState->transitions[0]->target;
+ if (returnState->getStateType() == ATNStateType::BLOCK_END && returnStateTarget == p) {
+ continue;
+ }
+
+ // Look for 'expr op expr' or case where expr's return state is block end
+ // of (...)* internal block; the block end points to loop back
+ // which points to p but we don't need to check that
+ if (returnState == blockEndState) {
+ continue;
+ }
+
+ // Look for ternary expr ? expr : expr. The return state points at block end,
+ // which points at loop entry state
+ if (returnStateTarget == blockEndState) {
+ continue;
+ }
+
+ // Look for complex prefix 'between expr and expr' case where 2nd expr's
+ // return state points at block end state of (...)* internal block
+ if (returnStateTarget->getStateType() == ATNStateType::BLOCK_END &&
+ returnStateTarget->transitions.size() == 1 &&
+ returnStateTarget->transitions[0]->isEpsilon() &&
+ returnStateTarget->transitions[0]->target == p)
+ {
+ continue;
+ }
+
+ // Anything else ain't conforming.
+ return false;
+ }
+
+ return true;
+}
+
+std::string ParserATNSimulator::getRuleName(size_t index) {
+ if (parser != nullptr) {
+ return parser->getRuleNames()[index];
+ }
+ return "<rule " + std::to_string(index) + ">";
+}
+
+Ref<ATNConfig> ParserATNSimulator::getEpsilonTarget(Ref<ATNConfig> const& config, const Transition *t, bool collectPredicates,
+ bool inContext, bool fullCtx, bool treatEofAsEpsilon) {
+ switch (t->getTransitionType()) {
+ case TransitionType::RULE:
+ return ruleTransition(config, static_cast<const RuleTransition*>(t));
+
+ case TransitionType::PRECEDENCE:
+ return precedenceTransition(config, static_cast<const PrecedencePredicateTransition*>(t), collectPredicates, inContext, fullCtx);
+
+ case TransitionType::PREDICATE:
+ return predTransition(config, static_cast<const PredicateTransition*>(t), collectPredicates, inContext, fullCtx);
+
+ case TransitionType::ACTION:
+ return actionTransition(config, static_cast<const ActionTransition*>(t));
+
+ case TransitionType::EPSILON:
+ return std::make_shared<ATNConfig>(*config, t->target);
+
+ case TransitionType::ATOM:
+ case TransitionType::RANGE:
+ case TransitionType::SET:
+ // EOF transitions act like epsilon transitions after the first EOF
+ // transition is traversed
+ if (treatEofAsEpsilon) {
+ if (t->matches(Token::EOF, 0, 1)) {
+ return std::make_shared<ATNConfig>(*config, t->target);
+ }
+ }
+
+ return nullptr;
+
+ default:
+ return nullptr;
+ }
+}
+
+Ref<ATNConfig> ParserATNSimulator::actionTransition(Ref<ATNConfig> const& config, const ActionTransition *t) {
+#if DEBUG_DFA == 1
+ std::cout << "ACTION edge " << t->ruleIndex << ":" << t->actionIndex << std::endl;
+#endif
+
+ return std::make_shared<ATNConfig>(*config, t->target);
+}
+
+Ref<ATNConfig> ParserATNSimulator::precedenceTransition(Ref<ATNConfig> const& config, const PrecedencePredicateTransition *pt,
+ bool collectPredicates, bool inContext, bool fullCtx) {
+#if DEBUG_DFA == 1
+ std::cout << "PRED (collectPredicates=" << collectPredicates << ") " << pt->getPrecedence() << ">=_p" << ", ctx dependent=true" << std::endl;
+ if (parser != nullptr) {
+ std::cout << "context surrounding pred is " << Arrays::listToString(parser->getRuleInvocationStack(), ", ") << std::endl;
+ }
+#endif
+
+ Ref<ATNConfig> c;
+ if (collectPredicates && inContext) {
+ const auto &predicate = pt->getPredicate();
+
+ if (fullCtx) {
+ // In full context mode, we can evaluate predicates on-the-fly
+ // during closure, which dramatically reduces the size of
+ // the config sets. It also obviates the need to test predicates
+ // later during conflict resolution.
+ size_t currentPosition = _input->index();
+ _input->seek(_startIndex);
+ bool predSucceeds = evalSemanticContext(predicate, _outerContext, config->alt, fullCtx);
+ _input->seek(currentPosition);
+ if (predSucceeds) {
+ c = std::make_shared<ATNConfig>(*config, pt->target); // no pred context
+ }
+ } else {
+ Ref<const SemanticContext> newSemCtx = SemanticContext::And(config->semanticContext, predicate);
+ c = std::make_shared<ATNConfig>(*config, pt->target, std::move(newSemCtx));
+ }
+ } else {
+ c = std::make_shared<ATNConfig>(*config, pt->target);
+ }
+
+#if DEBUG_DFA == 1
+ std::cout << "config from pred transition=" << c << std::endl;
+#endif
+
+ return c;
+}
+
+Ref<ATNConfig> ParserATNSimulator::predTransition(Ref<ATNConfig> const& config, const PredicateTransition *pt,
+ bool collectPredicates, bool inContext, bool fullCtx) {
+#if DEBUG_DFA == 1
+ std::cout << "PRED (collectPredicates=" << collectPredicates << ") " << pt->getRuleIndex() << ":" << pt->getPredIndex() << ", ctx dependent=" << pt->isCtxDependent() << std::endl;
+ if (parser != nullptr) {
+ std::cout << "context surrounding pred is " << Arrays::listToString(parser->getRuleInvocationStack(), ", ") << std::endl;
+ }
+#endif
+
+ Ref<ATNConfig> c = nullptr;
+ if (collectPredicates && (!pt->isCtxDependent() || (pt->isCtxDependent() && inContext))) {
+ const auto &predicate = pt->getPredicate();
+ if (fullCtx) {
+ // In full context mode, we can evaluate predicates on-the-fly
+ // during closure, which dramatically reduces the size of
+ // the config sets. It also obviates the need to test predicates
+ // later during conflict resolution.
+ size_t currentPosition = _input->index();
+ _input->seek(_startIndex);
+ bool predSucceeds = evalSemanticContext(predicate, _outerContext, config->alt, fullCtx);
+ _input->seek(currentPosition);
+ if (predSucceeds) {
+ c = std::make_shared<ATNConfig>(*config, pt->target); // no pred context
+ }
+ } else {
+ Ref<const SemanticContext> newSemCtx = SemanticContext::And(config->semanticContext, predicate);
+ c = std::make_shared<ATNConfig>(*config, pt->target, std::move(newSemCtx));
+ }
+ } else {
+ c = std::make_shared<ATNConfig>(*config, pt->target);
+ }
+
+#if DEBUG_DFA == 1
+ std::cout << "config from pred transition=" << c << std::endl;
+#endif
+
+ return c;
+}
+
+Ref<ATNConfig> ParserATNSimulator::ruleTransition(Ref<ATNConfig> const& config, const RuleTransition *t) {
+#if DEBUG_DFA == 1
+ std::cout << "CALL rule " << getRuleName(t->target->ruleIndex) << ", ctx=" << config->context << std::endl;
+#endif
+
+ atn::ATNState *returnState = t->followState;
+ Ref<const PredictionContext> newContext = SingletonPredictionContext::create(config->context, returnState->stateNumber);
+ return std::make_shared<ATNConfig>(*config, t->target, newContext);
+}
+
+BitSet ParserATNSimulator::getConflictingAlts(ATNConfigSet *configs) {
+ std::vector<BitSet> altsets = PredictionModeClass::getConflictingAltSubsets(configs);
+ return PredictionModeClass::getAlts(altsets);
+}
+
+BitSet ParserATNSimulator::getConflictingAltsOrUniqueAlt(ATNConfigSet *configs) {
+ BitSet conflictingAlts;
+ if (configs->uniqueAlt != ATN::INVALID_ALT_NUMBER) {
+ conflictingAlts.set(configs->uniqueAlt);
+ } else {
+ conflictingAlts = configs->conflictingAlts;
+ }
+ return conflictingAlts;
+}
+
+std::string ParserATNSimulator::getTokenName(size_t t) {
+ if (t == Token::EOF) {
+ return "EOF";
+ }
+
+ const dfa::Vocabulary &vocabulary = parser != nullptr ? parser->getVocabulary() : dfa::Vocabulary();
+ std::string displayName = vocabulary.getDisplayName(t);
+ if (displayName == std::to_string(t)) {
+ return displayName;
+ }
+
+ return displayName + "<" + std::to_string(t) + ">";
+}
+
+std::string ParserATNSimulator::getLookaheadName(TokenStream *input) {
+ return getTokenName(input->LA(1));
+}
+
+void ParserATNSimulator::dumpDeadEndConfigs(NoViableAltException &nvae) {
+ std::cerr << "dead end configs: ";
+ for (const auto &c : nvae.getDeadEndConfigs()->configs) {
+ std::string trans = "no edges";
+ if (c->state->transitions.size() > 0) {
+ const Transition *t = c->state->transitions[0].get();
+ if (t != nullptr && t->getTransitionType() == TransitionType::ATOM) {
+ const AtomTransition *at = static_cast<const AtomTransition*>(t);
+ trans = "Atom " + getTokenName(at->_label);
+ } else if (t != nullptr && t->getTransitionType() == TransitionType::SET) {
+ const SetTransition *st = static_cast<const SetTransition*>(t);
+ trans = "Set ";
+ trans += st->set.toString();
+ } else if (t != nullptr && t->getTransitionType() == TransitionType::NOT_SET) {
+ const SetTransition *st = static_cast<const NotSetTransition*>(t);
+ trans = "~Set ";
+ trans += st->set.toString();
+ }
+ }
+ std::cerr << c->toString(true) + ":" + trans;
+ }
+}
+
+NoViableAltException ParserATNSimulator::noViableAlt(TokenStream *input, ParserRuleContext *outerContext,
+ ATNConfigSet *configs, size_t startIndex, bool deleteConfigs) {
+ return NoViableAltException(parser, input, input->get(startIndex), input->LT(1), configs, outerContext, deleteConfigs);
+}
+
+size_t ParserATNSimulator::getUniqueAlt(ATNConfigSet *configs) {
+ size_t alt = ATN::INVALID_ALT_NUMBER;
+ for (const auto &c : configs->configs) {
+ if (alt == ATN::INVALID_ALT_NUMBER) {
+ alt = c->alt; // found first alt
+ } else if (c->alt != alt) {
+ return ATN::INVALID_ALT_NUMBER;
+ }
+ }
+ return alt;
+}
+
+dfa::DFAState *ParserATNSimulator::addDFAEdge(dfa::DFA &dfa, dfa::DFAState *from, ssize_t t, dfa::DFAState *to) {
+#if DEBUG_DFA == 1
+ std::cout << "EDGE " << from << " -> " << to << " upon " << getTokenName(t) << std::endl;
+#endif
+
+ if (to == nullptr) {
+ return nullptr;
+ }
+
+ {
+ UniqueLock<SharedMutex> stateLock(atn._stateMutex);
+ to = addDFAState(dfa, to); // used existing if possible not incoming
+ }
+ if (from == nullptr || t > (int)atn.maxTokenType) {
+ return to;
+ }
+
+ {
+ UniqueLock<SharedMutex> edgeLock(atn._edgeMutex);
+ from->edges[t] = to; // connect
+ }
+
+#if DEBUG_DFA == 1
+ std::string dfaText;
+ if (parser != nullptr) {
+ dfaText = dfa.toString(parser->getVocabulary());
+ } else {
+ dfaText = dfa.toString(dfa::Vocabulary());
+ }
+ std::cout << "DFA=\n" << dfaText << std::endl;
+#endif
+
+ return to;
+}
+
+dfa::DFAState *ParserATNSimulator::addDFAState(dfa::DFA &dfa, dfa::DFAState *D) {
+ if (D == ERROR.get()) {
+ return D;
+ }
+
+ // Optimizing the configs below should not alter the hash code. Thus we can just do an insert
+ // which will only succeed if an equivalent DFAState does not already exist.
+ auto [existing, inserted] = dfa.states.insert(D);
+ if (!inserted) {
+ return *existing;
+ }
+
+ // Previously we did a lookup, then set fields, then inserted. It was `dfa.states.size()`, since
+ // we already inserted we need to subtract one.
+ D->stateNumber = static_cast<int>(dfa.states.size() - 1);
+ if (!D->configs->isReadonly()) {
+ D->configs->optimizeConfigs(this);
+ D->configs->setReadonly(true);
+ }
+
+#if DEBUG_DFA == 1
+ std::cout << "adding new DFA state: " << D << std::endl;
+#endif
+
+ return D;
+}
+
+void ParserATNSimulator::reportAttemptingFullContext(dfa::DFA &dfa, const antlrcpp::BitSet &conflictingAlts,
+ ATNConfigSet *configs, size_t startIndex, size_t stopIndex) {
+#if DEBUG_DFA == 1 || RETRY_DEBUG == 1
+ misc::Interval interval = misc::Interval((int)startIndex, (int)stopIndex);
+ std::cout << "reportAttemptingFullContext decision=" << dfa.decision << ":" << configs << ", input=" << parser->getTokenStream()->getText(interval) << std::endl;
+#endif
+
+ if (parser != nullptr) {
+ parser->getErrorListenerDispatch().reportAttemptingFullContext(parser, dfa, startIndex, stopIndex, conflictingAlts, configs);
+ }
+}
+
+void ParserATNSimulator::reportContextSensitivity(dfa::DFA &dfa, size_t prediction, ATNConfigSet *configs,
+ size_t startIndex, size_t stopIndex) {
+#if DEBUG_DFA == 1 || RETRY_DEBUG == 1
+ misc::Interval interval = misc::Interval(startIndex, stopIndex);
+ std::cout << "reportContextSensitivity decision=" << dfa.decision << ":" << configs << ", input=" << parser->getTokenStream()->getText(interval) << std::endl;
+#endif
+
+ if (parser != nullptr) {
+ parser->getErrorListenerDispatch().reportContextSensitivity(parser, dfa, startIndex, stopIndex, prediction, configs);
+ }
+}
+
+void ParserATNSimulator::reportAmbiguity(dfa::DFA &dfa, dfa::DFAState * /*D*/, size_t startIndex, size_t stopIndex,
+ bool exact, const antlrcpp::BitSet &ambigAlts, ATNConfigSet *configs) {
+#if DEBUG_DFA == 1 || RETRY_DEBUG == 1
+ misc::Interval interval = misc::Interval((int)startIndex, (int)stopIndex);
+ std::cout << "reportAmbiguity " << ambigAlts << ":" << configs << ", input=" << parser->getTokenStream()->getText(interval) << std::endl;
+#endif
+
+ if (parser != nullptr) {
+ parser->getErrorListenerDispatch().reportAmbiguity(parser, dfa, startIndex, stopIndex, exact, ambigAlts, configs);
+ }
+}
+
+void ParserATNSimulator::setPredictionMode(PredictionMode newMode) {
+ _mode = newMode;
+}
+
+atn::PredictionMode ParserATNSimulator::getPredictionMode() {
+ return _mode;
+}
+
+Parser* ParserATNSimulator::getParser() {
+ return parser;
+}
+
+#ifdef _MSC_VER
+#pragma warning (disable:4996) // 'getenv': This function or variable may be unsafe. Consider using _dupenv_s instead.
+#endif
+
+bool ParserATNSimulator::getLrLoopSetting() {
+ char *var = std::getenv("TURN_OFF_LR_LOOP_ENTRY_BRANCH_OPT");
+ if (var == nullptr)
+ return false;
+ std::string value(var);
+ return value == "true" || value == "1";
+}
+
+#ifdef _MSC_VER
+#pragma warning (default:4996)
+#endif
+
+void ParserATNSimulator::InitializeInstanceFields() {
+ _mode = PredictionMode::LL;
+ _startIndex = 0;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulator.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulator.h
new file mode 100644
index 0000000000..28fd059dd2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulator.h
@@ -0,0 +1,911 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "PredictionMode.h"
+#include "dfa/DFAState.h"
+#include "atn/ATNSimulator.h"
+#include "atn/PredictionContext.h"
+#include "atn/PredictionContextMergeCache.h"
+#include "atn/ParserATNSimulatorOptions.h"
+#include "SemanticContext.h"
+#include "atn/ATNConfig.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /**
+ * The embodiment of the adaptive LL(*), ALL(*), parsing strategy.
+ *
+ * <p>
+ * The basic complexity of the adaptive strategy makes it harder to understand.
+ * We begin with ATN simulation to build paths in a DFA. Subsequent prediction
+ * requests go through the DFA first. If they reach a state without an edge for
+ * the current symbol, the algorithm fails over to the ATN simulation to
+ * complete the DFA path for the current input (until it finds a conflict state
+ * or uniquely predicting state).</p>
+ *
+ * <p>
+ * All of that is done without using the outer context because we want to create
+ * a DFA that is not dependent upon the rule invocation stack when we do a
+ * prediction. One DFA works in all contexts. We avoid using context not
+ * necessarily because it's slower, although it can be, but because of the DFA
+ * caching problem. The closure routine only considers the rule invocation stack
+ * created during prediction beginning in the decision rule. For example, if
+ * prediction occurs without invoking another rule's ATN, there are no context
+ * stacks in the configurations. When lack of context leads to a conflict, we
+ * don't know if it's an ambiguity or a weakness in the strong LL(*) parsing
+ * strategy (versus full LL(*)).</p>
+ *
+ * <p>
+ * When SLL yields a configuration set with conflict, we rewind the input and
+ * retry the ATN simulation, this time using full outer context without adding
+ * to the DFA. Configuration context stacks will be the full invocation stacks
+ * from the start rule. If we get a conflict using full context, then we can
+ * definitively say we have a true ambiguity for that input sequence. If we
+ * don't get a conflict, it implies that the decision is sensitive to the outer
+ * context. (It is not context-sensitive in the sense of context-sensitive
+ * grammars.)</p>
+ *
+ * <p>
+ * The next time we reach this DFA state with an SLL conflict, through DFA
+ * simulation, we will again retry the ATN simulation using full context mode.
+ * This is slow because we can't save the results and have to "interpret" the
+ * ATN each time we get that input.</p>
+ *
+ * <p>
+ * <strong>CACHING FULL CONTEXT PREDICTIONS</strong></p>
+ *
+ * <p>
+ * We could cache results from full context to predicted alternative easily and
+ * that saves a lot of time but doesn't work in presence of predicates. The set
+ * of visible predicates from the ATN start state changes depending on the
+ * context, because closure can fall off the end of a rule. I tried to cache
+ * tuples (stack context, semantic context, predicted alt) but it was slower
+ * than interpreting and much more complicated. Also required a huge amount of
+ * memory. The goal is not to create the world's fastest parser anyway. I'd like
+ * to keep this algorithm simple. By launching multiple threads, we can improve
+ * the speed of parsing across a large number of files.</p>
+ *
+ * <p>
+ * There is no strict ordering between the amount of input used by SLL vs LL,
+ * which makes it really hard to build a cache for full context. Let's say that
+ * we have input A B C that leads to an SLL conflict with full context X. That
+ * implies that using X we might only use A B but we could also use A B C D to
+ * resolve conflict. Input A B C D could predict alternative 1 in one position
+ * in the input and A B C E could predict alternative 2 in another position in
+ * input. The conflicting SLL configurations could still be non-unique in the
+ * full context prediction, which would lead us to requiring more input than the
+ * original A B C. To make a prediction cache work, we have to track the exact
+ * input used during the previous prediction. That amounts to a cache that maps
+ * X to a specific DFA for that context.</p>
+ *
+ * <p>
+ * Something should be done for left-recursive expression predictions. They are
+ * likely LL(1) + pred eval. Easier to do the whole SLL unless error and retry
+ * with full LL thing Sam does.</p>
+ *
+ * <p>
+ * <strong>AVOIDING FULL CONTEXT PREDICTION</strong></p>
+ *
+ * <p>
+ * We avoid doing full context retry when the outer context is empty, we did not
+ * dip into the outer context by falling off the end of the decision state rule,
+ * or when we force SLL mode.</p>
+ *
+ * <p>
+ * As an example of the not dip into outer context case, consider as super
+ * constructor calls versus function calls. One grammar might look like
+ * this:</p>
+ *
+ * <pre>
+ * ctorBody
+ * : '{' superCall? stat* '}'
+ * ;
+ * </pre>
+ *
+ * <p>
+ * Or, you might see something like</p>
+ *
+ * <pre>
+ * stat
+ * : superCall ';'
+ * | expression ';'
+ * | ...
+ * ;
+ * </pre>
+ *
+ * <p>
+ * In both cases I believe that no closure operations will dip into the outer
+ * context. In the first case ctorBody in the worst case will stop at the '}'.
+ * In the 2nd case it should stop at the ';'. Both cases should stay within the
+ * entry rule and not dip into the outer context.</p>
+ *
+ * <p>
+ * <strong>PREDICATES</strong></p>
+ *
+ * <p>
+ * Predicates are always evaluated if present in either SLL or LL both. SLL and
+ * LL simulation deals with predicates differently. SLL collects predicates as
+ * it performs closure operations like ANTLR v3 did. It delays predicate
+ * evaluation until it reaches and accept state. This allows us to cache the SLL
+ * ATN simulation whereas, if we had evaluated predicates on-the-fly during
+ * closure, the DFA state configuration sets would be different and we couldn't
+ * build up a suitable DFA.</p>
+ *
+ * <p>
+ * When building a DFA accept state during ATN simulation, we evaluate any
+ * predicates and return the sole semantically valid alternative. If there is
+ * more than 1 alternative, we report an ambiguity. If there are 0 alternatives,
+ * we throw an exception. Alternatives without predicates act like they have
+ * true predicates. The simple way to think about it is to strip away all
+ * alternatives with false predicates and choose the minimum alternative that
+ * remains.</p>
+ *
+ * <p>
+ * When we start in the DFA and reach an accept state that's predicated, we test
+ * those and return the minimum semantically viable alternative. If no
+ * alternatives are viable, we throw an exception.</p>
+ *
+ * <p>
+ * During full LL ATN simulation, closure always evaluates predicates and
+ * on-the-fly. This is crucial to reducing the configuration set size during
+ * closure. It hits a landmine when parsing with the Java grammar, for example,
+ * without this on-the-fly evaluation.</p>
+ *
+ * <p>
+ * <strong>SHARING DFA</strong></p>
+ *
+ * <p>
+ * All instances of the same parser share the same decision DFAs through a
+ * static field. Each instance gets its own ATN simulator but they share the
+ * same {@link #decisionToDFA} field. They also share a
+ * {@link PredictionContextCache} object that makes sure that all
+ * {@link PredictionContext} objects are shared among the DFA states. This makes
+ * a big size difference.</p>
+ *
+ * <p>
+ * <strong>THREAD SAFETY</strong></p>
+ *
+ * <p>
+ * The {@link ParserATNSimulator} locks on the {@link #decisionToDFA} field when
+ * it adds a new DFA object to that array. {@link #addDFAEdge}
+ * locks on the DFA for the current decision when setting the
+ * {@link DFAState#edges} field. {@link #addDFAState} locks on
+ * the DFA for the current decision when looking up a DFA state to see if it
+ * already exists. We must make sure that all requests to add DFA states that
+ * are equivalent result in the same shared DFA object. This is because lots of
+ * threads will be trying to update the DFA at once. The
+ * {@link #addDFAState} method also locks inside the DFA lock
+ * but this time on the shared context cache when it rebuilds the
+ * configurations' {@link PredictionContext} objects using cached
+ * subgraphs/nodes. No other locking occurs, even during DFA simulation. This is
+ * safe as long as we can guarantee that all threads referencing
+ * {@code s.edge[t]} get the same physical target {@link DFAState}, or
+ * {@code null}. Once into the DFA, the DFA simulation does not reference the
+ * {@link DFA#states} map. It follows the {@link DFAState#edges} field to new
+ * targets. The DFA simulator will either find {@link DFAState#edges} to be
+ * {@code null}, to be non-{@code null} and {@code dfa.edges[t]} null, or
+ * {@code dfa.edges[t]} to be non-null. The
+ * {@link #addDFAEdge} method could be racing to set the field
+ * but in either case the DFA simulator works; if {@code null}, and requests ATN
+ * simulation. It could also race trying to get {@code dfa.edges[t]}, but either
+ * way it will work because it's not doing a test and set operation.</p>
+ *
+ * <p>
+ * <strong>Starting with SLL then failing to combined SLL/LL (Two-Stage
+ * Parsing)</strong></p>
+ *
+ * <p>
+ * Sam pointed out that if SLL does not give a syntax error, then there is no
+ * point in doing full LL, which is slower. We only have to try LL if we get a
+ * syntax error. For maximum speed, Sam starts the parser set to pure SLL
+ * mode with the {@link BailErrorStrategy}:</p>
+ *
+ * <pre>
+ * parser.{@link Parser#getInterpreter() getInterpreter()}.{@link #setPredictionMode setPredictionMode}{@code (}{@link PredictionMode#SLL}{@code )};
+ * parser.{@link Parser#setErrorHandler setErrorHandler}(new {@link BailErrorStrategy}());
+ * </pre>
+ *
+ * <p>
+ * If it does not get a syntax error, then we're done. If it does get a syntax
+ * error, we need to retry with the combined SLL/LL strategy.</p>
+ *
+ * <p>
+ * The reason this works is as follows. If there are no SLL conflicts, then the
+ * grammar is SLL (at least for that input set). If there is an SLL conflict,
+ * the full LL analysis must yield a set of viable alternatives which is a
+ * subset of the alternatives reported by SLL. If the LL set is a singleton,
+ * then the grammar is LL but not SLL. If the LL set is the same size as the SLL
+ * set, the decision is SLL. If the LL set has size &gt; 1, then that decision
+ * is truly ambiguous on the current input. If the LL set is smaller, then the
+ * SLL conflict resolution might choose an alternative that the full LL would
+ * rule out as a possibility based upon better context information. If that's
+ * the case, then the SLL parse will definitely get an error because the full LL
+ * analysis says it's not viable. If SLL conflict resolution chooses an
+ * alternative within the LL set, them both SLL and LL would choose the same
+ * alternative because they both choose the minimum of multiple conflicting
+ * alternatives.</p>
+ *
+ * <p>
+ * Let's say we have a set of SLL conflicting alternatives {@code {1, 2, 3}} and
+ * a smaller LL set called <em>s</em>. If <em>s</em> is {@code {2, 3}}, then SLL
+ * parsing will get an error because SLL will pursue alternative 1. If
+ * <em>s</em> is {@code {1, 2}} or {@code {1, 3}} then both SLL and LL will
+ * choose the same alternative because alternative one is the minimum of either
+ * set. If <em>s</em> is {@code {2}} or {@code {3}} then SLL will get a syntax
+ * error. If <em>s</em> is {@code {1}} then SLL will succeed.</p>
+ *
+ * <p>
+ * Of course, if the input is invalid, then we will get an error for sure in
+ * both SLL and LL parsing. Erroneous input will therefore require 2 passes over
+ * the input.</p>
+ */
+ class ANTLR4CPP_PUBLIC ParserATNSimulator : public ATNSimulator {
+ public:
+ /// Testing only!
+ ParserATNSimulator(const ATN &atn, std::vector<dfa::DFA> &decisionToDFA,
+ PredictionContextCache &sharedContextCache);
+
+ ParserATNSimulator(Parser *parser, const ATN &atn, std::vector<dfa::DFA> &decisionToDFA,
+ PredictionContextCache &sharedContextCache);
+
+ ParserATNSimulator(Parser *parser, const ATN &atn, std::vector<dfa::DFA> &decisionToDFA,
+ PredictionContextCache &sharedContextCache,
+ const ParserATNSimulatorOptions &options);
+
+ virtual void reset() override;
+ virtual void clearDFA() override;
+ virtual size_t adaptivePredict(TokenStream *input, size_t decision, ParserRuleContext *outerContext);
+
+ static const bool TURN_OFF_LR_LOOP_ENTRY_BRANCH_OPT;
+
+ std::vector<dfa::DFA> &decisionToDFA;
+
+ /** Implements first-edge (loop entry) elimination as an optimization
+ * during closure operations. See antlr/antlr4#1398.
+ *
+ * The optimization is to avoid adding the loop entry config when
+ * the exit path can only lead back to the same
+ * StarLoopEntryState after popping context at the rule end state
+ * (traversing only epsilon edges, so we're still in closure, in
+ * this same rule).
+ *
+ * We need to detect any state that can reach loop entry on
+ * epsilon w/o exiting rule. We don't have to look at FOLLOW
+ * links, just ensure that all stack tops for config refer to key
+ * states in LR rule.
+ *
+ * To verify we are in the right situation we must first check
+ * closure is at a StarLoopEntryState generated during LR removal.
+ * Then we check that each stack top of context is a return state
+ * from one of these cases:
+ *
+ * 1. 'not' expr, '(' type ')' expr. The return state points at loop entry state
+ * 2. expr op expr. The return state is the block end of internal block of (...)*
+ * 3. 'between' expr 'and' expr. The return state of 2nd expr reference.
+ * That state points at block end of internal block of (...)*.
+ * 4. expr '?' expr ':' expr. The return state points at block end,
+ * which points at loop entry state.
+ *
+ * If any is true for each stack top, then closure does not add a
+ * config to the current config set for edge[0], the loop entry branch.
+ *
+ * Conditions fail if any context for the current config is:
+ *
+ * a. empty (we'd fall out of expr to do a global FOLLOW which could
+ * even be to some weird spot in expr) or,
+ * b. lies outside of expr or,
+ * c. lies within expr but at a state not the BlockEndState
+ * generated during LR removal
+ *
+ * Do we need to evaluate predicates ever in closure for this case?
+ *
+ * No. Predicates, including precedence predicates, are only
+ * evaluated when computing a DFA start state. I.e., only before
+ * the lookahead (but not parser) consumes a token.
+ *
+ * There are no epsilon edges allowed in LR rule alt blocks or in
+ * the "primary" part (ID here). If closure is in
+ * StarLoopEntryState any lookahead operation will have consumed a
+ * token as there are no epsilon-paths that lead to
+ * StarLoopEntryState. We do not have to evaluate predicates
+ * therefore if we are in the generated StarLoopEntryState of a LR
+ * rule. Note that when making a prediction starting at that
+ * decision point, decision d=2, compute-start-state performs
+ * closure starting at edges[0], edges[1] emanating from
+ * StarLoopEntryState. That means it is not performing closure on
+ * StarLoopEntryState during compute-start-state.
+ *
+ * How do we know this always gives same prediction answer?
+ *
+ * Without predicates, loop entry and exit paths are ambiguous
+ * upon remaining input +b (in, say, a+b). Either paths lead to
+ * valid parses. Closure can lead to consuming + immediately or by
+ * falling out of this call to expr back into expr and loop back
+ * again to StarLoopEntryState to match +b. In this special case,
+ * we choose the more efficient path, which is to take the bypass
+ * path.
+ *
+ * The lookahead language has not changed because closure chooses
+ * one path over the other. Both paths lead to consuming the same
+ * remaining input during a lookahead operation. If the next token
+ * is an operator, lookahead will enter the choice block with
+ * operators. If it is not, lookahead will exit expr. Same as if
+ * closure had chosen to enter the choice block immediately.
+ *
+ * Closure is examining one config (some loopentrystate, some alt,
+ * context) which means it is considering exactly one alt. Closure
+ * always copies the same alt to any derived configs.
+ *
+ * How do we know this optimization doesn't mess up precedence in
+ * our parse trees?
+ *
+ * Looking through expr from left edge of stat only has to confirm
+ * that an input, say, a+b+c; begins with any valid interpretation
+ * of an expression. The precedence actually doesn't matter when
+ * making a decision in stat seeing through expr. It is only when
+ * parsing rule expr that we must use the precedence to get the
+ * right interpretation and, hence, parse tree.
+ */
+ bool canDropLoopEntryEdgeInLeftRecursiveRule(ATNConfig *config) const;
+ virtual std::string getRuleName(size_t index);
+
+ virtual Ref<ATNConfig> precedenceTransition(Ref<ATNConfig> const& config, const PrecedencePredicateTransition *pt,
+ bool collectPredicates, bool inContext, bool fullCtx);
+
+ void setPredictionMode(PredictionMode newMode);
+ PredictionMode getPredictionMode();
+
+ Parser* getParser();
+
+ virtual std::string getTokenName(size_t t);
+
+ virtual std::string getLookaheadName(TokenStream *input);
+
+ /// <summary>
+ /// Used for debugging in adaptivePredict around execATN but I cut
+ /// it out for clarity now that alg. works well. We can leave this
+ /// "dead" code for a bit.
+ /// </summary>
+ virtual void dumpDeadEndConfigs(NoViableAltException &nvae);
+
+ protected:
+ Parser *const parser;
+
+ /// <summary>
+ /// Each prediction operation uses a cache for merge of prediction contexts.
+ /// Don't keep around as it wastes huge amounts of memory. The merge cache
+ /// isn't synchronized but we're ok since two threads shouldn't reuse same
+ /// parser/atnsim object because it can only handle one input at a time.
+ /// This maps graphs a and b to merged result c. (a,b)->c. We can avoid
+ /// the merge if we ever see a and b again. Note that (b,a)->c should
+ /// also be examined during cache lookup.
+ /// </summary>
+ PredictionContextMergeCache mergeCache;
+ size_t _mergeCacheCounter = 0;
+
+ // LAME globals to avoid parameters!!!!! I need these down deep in predTransition
+ TokenStream *_input;
+ size_t _startIndex;
+ ParserRuleContext *_outerContext;
+ dfa::DFA *_dfa; // Reference into the decisionToDFA vector.
+
+ /// <summary>
+ /// Performs ATN simulation to compute a predicted alternative based
+ /// upon the remaining input, but also updates the DFA cache to avoid
+ /// having to traverse the ATN again for the same input sequence.
+ ///
+ /// There are some key conditions we're looking for after computing a new
+ /// set of ATN configs (proposed DFA state):
+ /// if the set is empty, there is no viable alternative for current symbol
+ /// does the state uniquely predict an alternative?
+ /// does the state have a conflict that would prevent us from
+ /// putting it on the work list?
+ ///
+ /// We also have some key operations to do:
+ /// add an edge from previous DFA state to potentially new DFA state, D,
+ /// upon current symbol but only if adding to work list, which means in all
+ /// cases except no viable alternative (and possibly non-greedy decisions?)
+ /// collecting predicates and adding semantic context to DFA accept states
+ /// adding rule context to context-sensitive DFA accept states
+ /// consuming an input symbol
+ /// reporting a conflict
+ /// reporting an ambiguity
+ /// reporting a context sensitivity
+ /// reporting insufficient predicates
+ ///
+ /// cover these cases:
+ /// dead end
+ /// single alt
+ /// single alt + preds
+ /// conflict
+ /// conflict + preds
+ /// </summary>
+ virtual size_t execATN(dfa::DFA &dfa, dfa::DFAState *s0, TokenStream *input, size_t startIndex,
+ ParserRuleContext *outerContext);
+
+ /// <summary>
+ /// Get an existing target state for an edge in the DFA. If the target state
+ /// for the edge has not yet been computed or is otherwise not available,
+ /// this method returns {@code null}.
+ /// </summary>
+ /// <param name="previousD"> The current DFA state </param>
+ /// <param name="t"> The next input symbol </param>
+ /// <returns> The existing target DFA state for the given input symbol
+ /// {@code t}, or {@code null} if the target state for this edge is not
+ /// already cached </returns>
+ virtual dfa::DFAState* getExistingTargetState(dfa::DFAState *previousD, size_t t);
+
+ /// <summary>
+ /// Compute a target state for an edge in the DFA, and attempt to add the
+ /// computed state and corresponding edge to the DFA.
+ /// </summary>
+ /// <param name="dfa"> The DFA </param>
+ /// <param name="previousD"> The current DFA state </param>
+ /// <param name="t"> The next input symbol
+ /// </param>
+ /// <returns> The computed target DFA state for the given input symbol
+ /// {@code t}. If {@code t} does not lead to a valid DFA state, this method
+ /// returns <seealso cref="#ERROR"/>. </returns>
+ virtual dfa::DFAState *computeTargetState(dfa::DFA &dfa, dfa::DFAState *previousD, size_t t);
+
+ virtual void predicateDFAState(dfa::DFAState *dfaState, DecisionState *decisionState);
+
+ // comes back with reach.uniqueAlt set to a valid alt
+ virtual size_t execATNWithFullContext(dfa::DFA &dfa, dfa::DFAState *D, ATNConfigSet *s0,
+ TokenStream *input, size_t startIndex, ParserRuleContext *outerContext); // how far we got before failing over
+
+ virtual std::unique_ptr<ATNConfigSet> computeReachSet(ATNConfigSet *closure, size_t t, bool fullCtx);
+
+ /// <summary>
+ /// Return a configuration set containing only the configurations from
+ /// {@code configs} which are in a <seealso cref="RuleStopState"/>. If all
+ /// configurations in {@code configs} are already in a rule stop state, this
+ /// method simply returns {@code configs}.
+ /// <p/>
+ /// When {@code lookToEndOfRule} is true, this method uses
+ /// <seealso cref="ATN#nextTokens"/> for each configuration in {@code configs} which is
+ /// not already in a rule stop state to see if a rule stop state is reachable
+ /// from the configuration via epsilon-only transitions.
+ /// </summary>
+ /// <param name="configs"> the configuration set to update </param>
+ /// <param name="lookToEndOfRule"> when true, this method checks for rule stop states
+ /// reachable by epsilon-only transitions from each configuration in
+ /// {@code configs}.
+ /// </param>
+ /// <returns> {@code configs} if all configurations in {@code configs} are in a
+ /// rule stop state, otherwise return a new configuration set containing only
+ /// the configurations from {@code configs} which are in a rule stop state </returns>
+ virtual ATNConfigSet* removeAllConfigsNotInRuleStopState(ATNConfigSet *configs, bool lookToEndOfRule);
+
+ virtual std::unique_ptr<ATNConfigSet> computeStartState(ATNState *p, RuleContext *ctx, bool fullCtx);
+
+ /* parrt internal source braindump that doesn't mess up
+ * external API spec.
+
+ applyPrecedenceFilter is an optimization to avoid highly
+ nonlinear prediction of expressions and other left recursive
+ rules. The precedence predicates such as {3>=prec}? Are highly
+ context-sensitive in that they can only be properly evaluated
+ in the context of the proper prec argument. Without pruning,
+ these predicates are normal predicates evaluated when we reach
+ conflict state (or unique prediction). As we cannot evaluate
+ these predicates out of context, the resulting conflict leads
+ to full LL evaluation and nonlinear prediction which shows up
+ very clearly with fairly large expressions.
+
+ Example grammar:
+
+ e : e '*' e
+ | e '+' e
+ | INT
+ ;
+
+ We convert that to the following:
+
+ e[int prec]
+ : INT
+ ( {3>=prec}? '*' e[4]
+ | {2>=prec}? '+' e[3]
+ )*
+ ;
+
+ The (..)* loop has a decision for the inner block as well as
+ an enter or exit decision, which is what concerns us here. At
+ the 1st + of input 1+2+3, the loop entry sees both predicates
+ and the loop exit also sees both predicates by falling off the
+ edge of e. This is because we have no stack information with
+ SLL and find the follow of e, which will hit the return states
+ inside the loop after e[4] and e[3], which brings it back to
+ the enter or exit decision. In this case, we know that we
+ cannot evaluate those predicates because we have fallen off
+ the edge of the stack and will in general not know which prec
+ parameter is the right one to use in the predicate.
+
+ Because we have special information, that these are precedence
+ predicates, we can resolve them without failing over to full
+ LL despite their context sensitive nature. We make an
+ assumption that prec[-1] <= prec[0], meaning that the current
+ precedence level is greater than or equal to the precedence
+ level of recursive invocations above us in the stack. For
+ example, if predicate {3>=prec}? is true of the current prec,
+ then one option is to enter the loop to match it now. The
+ other option is to exit the loop and the left recursive rule
+ to match the current operator in rule invocation further up
+ the stack. But, we know that all of those prec are lower or
+ the same value and so we can decide to enter the loop instead
+ of matching it later. That means we can strip out the other
+ configuration for the exit branch.
+
+ So imagine we have (14,1,$,{2>=prec}?) and then
+ (14,2,$-dipsIntoOuterContext,{2>=prec}?). The optimization
+ allows us to collapse these two configurations. We know that
+ if {2>=prec}? is true for the current prec parameter, it will
+ also be true for any prec from an invoking e call, indicated
+ by dipsIntoOuterContext. As the predicates are both true, we
+ have the option to evaluate them early in the decision start
+ state. We do this by stripping both predicates and choosing to
+ enter the loop as it is consistent with the notion of operator
+ precedence. It's also how the full LL conflict resolution
+ would work.
+
+ The solution requires a different DFA start state for each
+ precedence level.
+
+ The basic filter mechanism is to remove configurations of the
+ form (p, 2, pi) if (p, 1, pi) exists for the same p and pi. In
+ other words, for the same ATN state and predicate context,
+ remove any configuration associated with an exit branch if
+ there is a configuration associated with the enter branch.
+
+ It's also the case that the filter evaluates precedence
+ predicates and resolves conflicts according to precedence
+ levels. For example, for input 1+2+3 at the first +, we see
+ prediction filtering
+
+ [(11,1,[$],{3>=prec}?), (14,1,[$],{2>=prec}?), (5,2,[$],up=1),
+ (11,2,[$],up=1), (14,2,[$],up=1)],hasSemanticContext=true,dipsIntoOuterContext
+
+ to
+
+ [(11,1,[$]), (14,1,[$]), (5,2,[$],up=1)],dipsIntoOuterContext
+
+ This filters because {3>=prec}? evals to true and collapses
+ (11,1,[$],{3>=prec}?) and (11,2,[$],up=1) since early conflict
+ resolution based upon rules of operator precedence fits with
+ our usual match first alt upon conflict.
+
+ We noticed a problem where a recursive call resets precedence
+ to 0. Sam's fix: each config has flag indicating if it has
+ returned from an expr[0] call. then just don't filter any
+ config with that flag set. flag is carried along in
+ closure(). so to avoid adding field, set bit just under sign
+ bit of dipsIntoOuterContext (SUPPRESS_PRECEDENCE_FILTER).
+ With the change you filter "unless (p, 2, pi) was reached
+ after leaving the rule stop state of the LR rule containing
+ state p, corresponding to a rule invocation with precedence
+ level 0"
+ */
+
+ /**
+ * This method transforms the start state computed by
+ * {@link #computeStartState} to the special start state used by a
+ * precedence DFA for a particular precedence value. The transformation
+ * process applies the following changes to the start state's configuration
+ * set.
+ *
+ * <ol>
+ * <li>Evaluate the precedence predicates for each configuration using
+ * {@link SemanticContext#evalPrecedence}.</li>
+ * <li>When {@link ATNConfig#isPrecedenceFilterSuppressed} is {@code false},
+ * remove all configurations which predict an alternative greater than 1,
+ * for which another configuration that predicts alternative 1 is in the
+ * same ATN state with the same prediction context. This transformation is
+ * valid for the following reasons:
+ * <ul>
+ * <li>The closure block cannot contain any epsilon transitions which bypass
+ * the body of the closure, so all states reachable via alternative 1 are
+ * part of the precedence alternatives of the transformed left-recursive
+ * rule.</li>
+ * <li>The "primary" portion of a left recursive rule cannot contain an
+ * epsilon transition, so the only way an alternative other than 1 can exist
+ * in a state that is also reachable via alternative 1 is by nesting calls
+ * to the left-recursive rule, with the outer calls not being at the
+ * preferred precedence level. The
+ * {@link ATNConfig#isPrecedenceFilterSuppressed} property marks ATN
+ * configurations which do not meet this condition, and therefore are not
+ * eligible for elimination during the filtering process.</li>
+ * </ul>
+ * </li>
+ * </ol>
+ *
+ * <p>
+ * The prediction context must be considered by this filter to address
+ * situations like the following.
+ * </p>
+ * <code>
+ * <pre>
+ * grammar TA;
+ * prog: statement* EOF;
+ * statement: letterA | statement letterA 'b' ;
+ * letterA: 'a';
+ * </pre>
+ * </code>
+ * <p>
+ * If the above grammar, the ATN state immediately before the token
+ * reference {@code 'a'} in {@code letterA} is reachable from the left edge
+ * of both the primary and closure blocks of the left-recursive rule
+ * {@code statement}. The prediction context associated with each of these
+ * configurations distinguishes between them, and prevents the alternative
+ * which stepped out to {@code prog} (and then back in to {@code statement}
+ * from being eliminated by the filter.
+ * </p>
+ *
+ * @param configs The configuration set computed by
+ * {@link #computeStartState} as the start state for the DFA.
+ * @return The transformed configuration set representing the start state
+ * for a precedence DFA at a particular precedence level (determined by
+ * calling {@link Parser#getPrecedence}).
+ */
+ std::unique_ptr<ATNConfigSet> applyPrecedenceFilter(ATNConfigSet *configs);
+
+ virtual ATNState *getReachableTarget(const Transition *trans, size_t ttype);
+
+ virtual std::vector<Ref<const SemanticContext>> getPredsForAmbigAlts(const antlrcpp::BitSet &ambigAlts,
+ ATNConfigSet *configs, size_t nalts);
+
+ std::vector<dfa::DFAState::PredPrediction> getPredicatePredictions(const antlrcpp::BitSet &ambigAlts,
+ const std::vector<Ref<const SemanticContext>> &altToPred);
+
+ /**
+ * This method is used to improve the localization of error messages by
+ * choosing an alternative rather than throwing a
+ * {@link NoViableAltException} in particular prediction scenarios where the
+ * {@link #ERROR} state was reached during ATN simulation.
+ *
+ * <p>
+ * The default implementation of this method uses the following
+ * algorithm to identify an ATN configuration which successfully parsed the
+ * decision entry rule. Choosing such an alternative ensures that the
+ * {@link ParserRuleContext} returned by the calling rule will be complete
+ * and valid, and the syntax error will be reported later at a more
+ * localized location.</p>
+ *
+ * <ul>
+ * <li>If a syntactically valid path or paths reach the end of the decision rule and
+ * they are semantically valid if predicated, return the min associated alt.</li>
+ * <li>Else, if a semantically invalid but syntactically valid path exist
+ * or paths exist, return the minimum associated alt.
+ * </li>
+ * <li>Otherwise, return {@link ATN#INVALID_ALT_NUMBER}.</li>
+ * </ul>
+ *
+ * <p>
+ * In some scenarios, the algorithm described above could predict an
+ * alternative which will result in a {@link FailedPredicateException} in
+ * the parser. Specifically, this could occur if the <em>only</em> configuration
+ * capable of successfully parsing to the end of the decision rule is
+ * blocked by a semantic predicate. By choosing this alternative within
+ * {@link #adaptivePredict} instead of throwing a
+ * {@link NoViableAltException}, the resulting
+ * {@link FailedPredicateException} in the parser will identify the specific
+ * predicate which is preventing the parser from successfully parsing the
+ * decision rule, which helps developers identify and correct logic errors
+ * in semantic predicates.
+ * </p>
+ *
+ * @param configs The ATN configurations which were valid immediately before
+ * the {@link #ERROR} state was reached
+ * @param outerContext The is the \gamma_0 initial parser context from the paper
+ * or the parser stack at the instant before prediction commences.
+ *
+ * @return The value to return from {@link #adaptivePredict}, or
+ * {@link ATN#INVALID_ALT_NUMBER} if a suitable alternative was not
+ * identified and {@link #adaptivePredict} should report an error instead.
+ */
+ size_t getSynValidOrSemInvalidAltThatFinishedDecisionEntryRule(ATNConfigSet *configs,
+ ParserRuleContext *outerContext);
+
+ virtual size_t getAltThatFinishedDecisionEntryRule(ATNConfigSet *configs);
+
+ /** Walk the list of configurations and split them according to
+ * those that have preds evaluating to true/false. If no pred, assume
+ * true pred and include in succeeded set. Returns Pair of sets.
+ *
+ * Create a new set so as not to alter the incoming parameter.
+ *
+ * Assumption: the input stream has been restored to the starting point
+ * prediction, which is where predicates need to evaluate.
+ */
+ std::pair<ATNConfigSet *, ATNConfigSet *> splitAccordingToSemanticValidity(ATNConfigSet *configs,
+ ParserRuleContext *outerContext);
+
+ /// <summary>
+ /// Look through a list of predicate/alt pairs, returning alts for the
+ /// pairs that win. A {@code NONE} predicate indicates an alt containing an
+ /// unpredicated config which behaves as "always true." If !complete
+ /// then we stop at the first predicate that evaluates to true. This
+ /// includes pairs with null predicates.
+ /// </summary>
+ antlrcpp::BitSet evalSemanticContext(const std::vector<dfa::DFAState::PredPrediction> &predPredictions,
+ ParserRuleContext *outerContext, bool complete);
+
+ /**
+ * Evaluate a semantic context within a specific parser context.
+ *
+ * <p>
+ * This method might not be called for every semantic context evaluated
+ * during the prediction process. In particular, we currently do not
+ * evaluate the following but it may change in the future:</p>
+ *
+ * <ul>
+ * <li>Precedence predicates (represented by
+ * {@link SemanticContext.PrecedencePredicate}) are not currently evaluated
+ * through this method.</li>
+ * <li>Operator predicates (represented by {@link SemanticContext.AND} and
+ * {@link SemanticContext.OR}) are evaluated as a single semantic
+ * context, rather than evaluating the operands individually.
+ * Implementations which require evaluation results from individual
+ * predicates should override this method to explicitly handle evaluation of
+ * the operands within operator predicates.</li>
+ * </ul>
+ *
+ * @param pred The semantic context to evaluate
+ * @param parserCallStack The parser context in which to evaluate the
+ * semantic context
+ * @param alt The alternative which is guarded by {@code pred}
+ * @param fullCtx {@code true} if the evaluation is occurring during LL
+ * prediction; otherwise, {@code false} if the evaluation is occurring
+ * during SLL prediction
+ *
+ * @since 4.3
+ */
+ virtual bool evalSemanticContext(Ref<const SemanticContext> const& pred, ParserRuleContext *parserCallStack,
+ size_t alt, bool fullCtx);
+
+ /* TODO: If we are doing predicates, there is no point in pursuing
+ closure operations if we reach a DFA state that uniquely predicts
+ alternative. We will not be caching that DFA state and it is a
+ waste to pursue the closure. Might have to advance when we do
+ ambig detection thought :(
+ */
+ virtual void closure(Ref<ATNConfig> const& config, ATNConfigSet *configs, ATNConfig::Set &closureBusy,
+ bool collectPredicates, bool fullCtx, bool treatEofAsEpsilon);
+
+ virtual void closureCheckingStopState(Ref<ATNConfig> const& config, ATNConfigSet *configs, ATNConfig::Set &closureBusy,
+ bool collectPredicates, bool fullCtx, int depth, bool treatEofAsEpsilon);
+
+ /// Do the actual work of walking epsilon edges.
+ virtual void closure_(Ref<ATNConfig> const& config, ATNConfigSet *configs, ATNConfig::Set &closureBusy,
+ bool collectPredicates, bool fullCtx, int depth, bool treatEofAsEpsilon);
+
+ virtual Ref<ATNConfig> getEpsilonTarget(Ref<ATNConfig> const& config, const Transition *t, bool collectPredicates,
+ bool inContext, bool fullCtx, bool treatEofAsEpsilon);
+ virtual Ref<ATNConfig> actionTransition(Ref<ATNConfig> const& config, const ActionTransition *t);
+
+ virtual Ref<ATNConfig> predTransition(Ref<ATNConfig> const& config, const PredicateTransition *pt, bool collectPredicates,
+ bool inContext, bool fullCtx);
+
+ virtual Ref<ATNConfig> ruleTransition(Ref<ATNConfig> const& config, const RuleTransition *t);
+
+ /**
+ * Gets a {@link BitSet} containing the alternatives in {@code configs}
+ * which are part of one or more conflicting alternative subsets.
+ *
+ * @param configs The {@link ATNConfigSet} to analyze.
+ * @return The alternatives in {@code configs} which are part of one or more
+ * conflicting alternative subsets. If {@code configs} does not contain any
+ * conflicting subsets, this method returns an empty {@link BitSet}.
+ */
+ virtual antlrcpp::BitSet getConflictingAlts(ATNConfigSet *configs);
+
+ /// <summary>
+ /// Sam pointed out a problem with the previous definition, v3, of
+ /// ambiguous states. If we have another state associated with conflicting
+ /// alternatives, we should keep going. For example, the following grammar
+ ///
+ /// s : (ID | ID ID?) ';' ;
+ ///
+ /// When the ATN simulation reaches the state before ';', it has a DFA
+ /// state that looks like: [12|1|[], 6|2|[], 12|2|[]]. Naturally
+ /// 12|1|[] and 12|2|[] conflict, but we cannot stop processing this node
+ /// because alternative to has another way to continue, via [6|2|[]].
+ /// The key is that we have a single state that has config's only associated
+ /// with a single alternative, 2, and crucially the state transitions
+ /// among the configurations are all non-epsilon transitions. That means
+ /// we don't consider any conflicts that include alternative 2. So, we
+ /// ignore the conflict between alts 1 and 2. We ignore a set of
+ /// conflicting alts when there is an intersection with an alternative
+ /// associated with a single alt state in the state->config-list map.
+ ///
+ /// It's also the case that we might have two conflicting configurations but
+ /// also a 3rd nonconflicting configuration for a different alternative:
+ /// [1|1|[], 1|2|[], 8|3|[]]. This can come about from grammar:
+ ///
+ /// a : A | A | A B ;
+ ///
+ /// After matching input A, we reach the stop state for rule A, state 1.
+ /// State 8 is the state right before B. Clearly alternatives 1 and 2
+ /// conflict and no amount of further lookahead will separate the two.
+ /// However, alternative 3 will be able to continue and so we do not
+ /// stop working on this state. In the previous example, we're concerned
+ /// with states associated with the conflicting alternatives. Here alt
+ /// 3 is not associated with the conflicting configs, but since we can continue
+ /// looking for input reasonably, I don't declare the state done. We
+ /// ignore a set of conflicting alts when we have an alternative
+ /// that we still need to pursue.
+ /// </summary>
+
+ virtual antlrcpp::BitSet getConflictingAltsOrUniqueAlt(ATNConfigSet *configs);
+
+ virtual NoViableAltException noViableAlt(TokenStream *input, ParserRuleContext *outerContext,
+ ATNConfigSet *configs, size_t startIndex, bool deleteConfigs);
+
+ static size_t getUniqueAlt(ATNConfigSet *configs);
+
+ /// <summary>
+ /// Add an edge to the DFA, if possible. This method calls
+ /// <seealso cref="#addDFAState"/> to ensure the {@code to} state is present in the
+ /// DFA. If {@code from} is {@code null}, or if {@code t} is outside the
+ /// range of edges that can be represented in the DFA tables, this method
+ /// returns without adding the edge to the DFA.
+ /// <p/>
+ /// If {@code to} is {@code null}, this method returns {@code null}.
+ /// Otherwise, this method returns the <seealso cref="DFAState"/> returned by calling
+ /// <seealso cref="#addDFAState"/> for the {@code to} state.
+ /// </summary>
+ /// <param name="dfa"> The DFA </param>
+ /// <param name="from"> The source state for the edge </param>
+ /// <param name="t"> The input symbol </param>
+ /// <param name="to"> The target state for the edge
+ /// </param>
+ /// <returns> If {@code to} is {@code null}, this method returns {@code null};
+ /// otherwise this method returns the result of calling <seealso cref="#addDFAState"/>
+ /// on {@code to} </returns>
+ virtual dfa::DFAState *addDFAEdge(dfa::DFA &dfa, dfa::DFAState *from, ssize_t t, dfa::DFAState *to);
+
+ /// <summary>
+ /// Add state {@code D} to the DFA if it is not already present, and return
+ /// the actual instance stored in the DFA. If a state equivalent to {@code D}
+ /// is already in the DFA, the existing state is returned. Otherwise this
+ /// method returns {@code D} after adding it to the DFA.
+ /// <p/>
+ /// If {@code D} is <seealso cref="#ERROR"/>, this method returns <seealso cref="#ERROR"/> and
+ /// does not change the DFA.
+ /// </summary>
+ /// <param name="dfa"> The dfa </param>
+ /// <param name="D"> The DFA state to add </param>
+ /// <returns> The state stored in the DFA. This will be either the existing
+ /// state if {@code D} is already in the DFA, or {@code D} itself if the
+ /// state was not already present. </returns>
+ virtual dfa::DFAState *addDFAState(dfa::DFA &dfa, dfa::DFAState *D);
+
+ virtual void reportAttemptingFullContext(dfa::DFA &dfa, const antlrcpp::BitSet &conflictingAlts,
+ ATNConfigSet *configs, size_t startIndex, size_t stopIndex);
+
+ virtual void reportContextSensitivity(dfa::DFA &dfa, size_t prediction, ATNConfigSet *configs,
+ size_t startIndex, size_t stopIndex);
+
+ /// If context sensitive parsing, we know it's ambiguity not conflict.
+ virtual void reportAmbiguity(dfa::DFA &dfa,
+ dfa::DFAState *D, // the DFA state from execATN() that had SLL conflicts
+ size_t startIndex, size_t stopIndex,
+ bool exact,
+ const antlrcpp::BitSet &ambigAlts,
+ ATNConfigSet *configs); // configs that LL not SLL considered conflicting
+
+ private:
+ // SLL, LL, or LL + exact ambig detection?
+ PredictionMode _mode;
+
+ static bool getLrLoopSetting();
+ void InitializeInstanceFields();
+ };
+
+} // namespace atn
+} // namespace antlr4
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulatorOptions.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulatorOptions.h
new file mode 100644
index 0000000000..ea31226d25
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ParserATNSimulatorOptions.h
@@ -0,0 +1,50 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#pragma once
+
+#include "atn/PredictionContextMergeCacheOptions.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC ParserATNSimulatorOptions final {
+ public:
+ ParserATNSimulatorOptions& setPredictionContextMergeCacheOptions(
+ PredictionContextMergeCacheOptions predictionContextMergeCacheOptions) {
+ _predictionContextMergeCacheOptions = std::move(predictionContextMergeCacheOptions);
+ return *this;
+ }
+
+ const PredictionContextMergeCacheOptions& getPredictionContextMergeCacheOptions() const {
+ return _predictionContextMergeCacheOptions;
+ }
+
+ private:
+ PredictionContextMergeCacheOptions _predictionContextMergeCacheOptions;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PlusBlockStartState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PlusBlockStartState.h
new file mode 100644
index 0000000000..b6103dc4d0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PlusBlockStartState.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/BlockStartState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// Start of {@code (A|B|...)+} loop. Technically a decision state, but
+ /// we don't use for code generation; somebody might need it, so I'm defining
+ /// it for completeness. In reality, the <seealso cref="PlusLoopbackState"/> node is the
+ /// real decision-making note for {@code A+}.
+ class ANTLR4CPP_PUBLIC PlusBlockStartState final : public BlockStartState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::PLUS_BLOCK_START; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ PlusLoopbackState *loopBackState = nullptr;
+
+ PlusBlockStartState() : BlockStartState(ATNStateType::PLUS_BLOCK_START) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PlusLoopbackState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PlusLoopbackState.h
new file mode 100644
index 0000000000..07f25aa0c9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PlusLoopbackState.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// Decision state for {@code A+} and {@code (A|B)+}. It has two transitions:
+ /// one to the loop back to start of the block and one to exit.
+ class ANTLR4CPP_PUBLIC PlusLoopbackState final : public DecisionState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::PLUS_LOOP_BACK; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ PlusLoopbackState() : DecisionState(ATNStateType::PLUS_LOOP_BACK) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PrecedencePredicateTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/PrecedencePredicateTransition.cpp
new file mode 100644
index 0000000000..b8685e9516
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PrecedencePredicateTransition.cpp
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/PrecedencePredicateTransition.h"
+
+using namespace antlr4::atn;
+
+PrecedencePredicateTransition::PrecedencePredicateTransition(ATNState *target, int precedence)
+ : Transition(TransitionType::PRECEDENCE, target), _predicate(std::make_shared<SemanticContext::PrecedencePredicate>(precedence)) {}
+
+bool PrecedencePredicateTransition::isEpsilon() const {
+ return true;
+}
+
+bool PrecedencePredicateTransition::matches(size_t /*symbol*/, size_t /*minVocabSymbol*/, size_t /*maxVocabSymbol*/) const {
+ return false;
+}
+
+std::string PrecedencePredicateTransition::toString() const {
+ return "PRECEDENCE " + Transition::toString() + " { precedence: " + std::to_string(getPrecedence()) + " }";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PrecedencePredicateTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PrecedencePredicateTransition.h
new file mode 100644
index 0000000000..3db79a9b73
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PrecedencePredicateTransition.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+#include "atn/SemanticContext.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC PrecedencePredicateTransition final : public Transition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::PRECEDENCE; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ PrecedencePredicateTransition(ATNState *target, int precedence);
+
+ int getPrecedence() const { return _predicate->precedence; }
+
+ bool isEpsilon() const override;
+ bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+ std::string toString() const override;
+
+ const Ref<const SemanticContext::PrecedencePredicate>& getPredicate() const { return _predicate; }
+
+ private:
+ const std::shared_ptr<const SemanticContext::PrecedencePredicate> _predicate;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateEvalInfo.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateEvalInfo.cpp
new file mode 100644
index 0000000000..73ee2a2b97
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateEvalInfo.cpp
@@ -0,0 +1,17 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "SemanticContext.h"
+
+#include "atn/PredicateEvalInfo.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+PredicateEvalInfo::PredicateEvalInfo(size_t decision, TokenStream *input, size_t startIndex, size_t stopIndex,
+ Ref<const SemanticContext> semctx, bool evalResult, size_t predictedAlt, bool fullCtx)
+ : DecisionEventInfo(decision, nullptr, input, startIndex, stopIndex, fullCtx),
+ semctx(std::move(semctx)), predictedAlt(predictedAlt), evalResult(evalResult) {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateEvalInfo.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateEvalInfo.h
new file mode 100644
index 0000000000..f343f541cb
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateEvalInfo.h
@@ -0,0 +1,62 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionEventInfo.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// This class represents profiling event information for semantic predicate
+ /// evaluations which occur during prediction.
+ /// </summary>
+ /// <seealso cref= ParserATNSimulator#evalSemanticContext
+ ///
+ /// @since 4.3 </seealso>
+ class ANTLR4CPP_PUBLIC PredicateEvalInfo : public DecisionEventInfo {
+ public:
+ /// The semantic context which was evaluated.
+ const Ref<const SemanticContext> semctx;
+
+ /// <summary>
+ /// The alternative number for the decision which is guarded by the semantic
+ /// context <seealso cref="#semctx"/>. Note that other ATN
+ /// configurations may predict the same alternative which are guarded by
+ /// other semantic contexts and/or <seealso cref="SemanticContext#NONE"/>.
+ /// </summary>
+ const size_t predictedAlt;
+
+ /// The result of evaluating the semantic context <seealso cref="#semctx"/>.
+ const bool evalResult;
+
+ /// <summary>
+ /// Constructs a new instance of the <seealso cref="PredicateEvalInfo"/> class with the
+ /// specified detailed predicate evaluation information.
+ /// </summary>
+ /// <param name="decision"> The decision number </param>
+ /// <param name="input"> The input token stream </param>
+ /// <param name="startIndex"> The start index for the current prediction </param>
+ /// <param name="stopIndex"> The index at which the predicate evaluation was
+ /// triggered. Note that the input stream may be reset to other positions for
+ /// the actual evaluation of individual predicates. </param>
+ /// <param name="semctx"> The semantic context which was evaluated </param>
+ /// <param name="evalResult"> The results of evaluating the semantic context </param>
+ /// <param name="predictedAlt"> The alternative number for the decision which is
+ /// guarded by the semantic context {@code semctx}. See <seealso cref="#predictedAlt"/>
+ /// for more information. </param>
+ /// <param name="fullCtx"> {@code true} if the semantic context was
+ /// evaluated during LL prediction; otherwise, {@code false} if the semantic
+ /// context was evaluated during SLL prediction
+ /// </param>
+ /// <seealso cref= ParserATNSimulator#evalSemanticContext(SemanticContext, ParserRuleContext, int, boolean) </seealso>
+ /// <seealso cref= SemanticContext#eval(Recognizer, RuleContext) </seealso>
+ PredicateEvalInfo(size_t decision, TokenStream *input, size_t startIndex, size_t stopIndex,
+ Ref<const SemanticContext> semctx, bool evalResult, size_t predictedAlt, bool fullCtx);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateTransition.cpp
new file mode 100644
index 0000000000..d76dbd203a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateTransition.cpp
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/PredicateTransition.h"
+
+using namespace antlr4::atn;
+
+PredicateTransition::PredicateTransition(ATNState *target, size_t ruleIndex, size_t predIndex, bool isCtxDependent)
+ : Transition(TransitionType::PREDICATE, target), _predicate(std::make_shared<SemanticContext::Predicate>(ruleIndex, predIndex, isCtxDependent)) {}
+
+bool PredicateTransition::isEpsilon() const {
+ return true;
+}
+
+bool PredicateTransition::matches(size_t /*symbol*/, size_t /*minVocabSymbol*/, size_t /*maxVocabSymbol*/) const {
+ return false;
+}
+
+std::string PredicateTransition::toString() const {
+ return "PREDICATE " + Transition::toString() + " { ruleIndex: " + std::to_string(getRuleIndex()) +
+ ", predIndex: " + std::to_string(getPredIndex()) + ", isCtxDependent: " + std::to_string(isCtxDependent()) + " }";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateTransition.h
new file mode 100644
index 0000000000..e889b1c198
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredicateTransition.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+#include "atn/SemanticContext.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// TODO: this is old comment:
+ /// A tree of semantic predicates from the grammar AST if label==SEMPRED.
+ /// In the ATN, labels will always be exactly one predicate, but the DFA
+ /// may have to combine a bunch of them as it collects predicates from
+ /// multiple ATN configurations into a single DFA state.
+ class ANTLR4CPP_PUBLIC PredicateTransition final : public Transition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::PREDICATE; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ PredicateTransition(ATNState *target, size_t ruleIndex, size_t predIndex, bool isCtxDependent);
+
+ size_t getRuleIndex() const {
+ return _predicate->ruleIndex;
+ }
+
+ size_t getPredIndex() const {
+ return _predicate->predIndex;
+ }
+
+ bool isCtxDependent() const {
+ return _predicate->isCtxDependent;
+ }
+
+ bool isEpsilon() const override;
+ bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+ std::string toString() const override;
+
+ const Ref<const SemanticContext::Predicate>& getPredicate() const { return _predicate; }
+
+ private:
+ const std::shared_ptr<const SemanticContext::Predicate> _predicate;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContext.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContext.cpp
new file mode 100644
index 0000000000..704408f04d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContext.cpp
@@ -0,0 +1,579 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/SingletonPredictionContext.h"
+#include "misc/MurmurHash.h"
+#include "atn/ArrayPredictionContext.h"
+#include "atn/PredictionContextCache.h"
+#include "atn/PredictionContextMergeCache.h"
+#include "RuleContext.h"
+#include "ParserRuleContext.h"
+#include "atn/RuleTransition.h"
+#include "support/Arrays.h"
+#include "support/CPPUtils.h"
+#include "support/Casts.h"
+
+#include "atn/PredictionContext.h"
+
+using namespace antlr4;
+using namespace antlr4::misc;
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+namespace {
+
+ void combineCommonParents(std::vector<Ref<const PredictionContext>> &parents) {
+ std::unordered_set<Ref<const PredictionContext>> uniqueParents;
+ uniqueParents.reserve(parents.size());
+ for (const auto &parent : parents) {
+ uniqueParents.insert(parent);
+ }
+ for (auto &parent : parents) {
+ parent = *uniqueParents.find(parent);
+ }
+ }
+
+ Ref<const PredictionContext> getCachedContextImpl(const Ref<const PredictionContext> &context,
+ PredictionContextCache &contextCache,
+ std::unordered_map<Ref<const PredictionContext>,
+ Ref<const PredictionContext>> &visited) {
+ if (context->isEmpty()) {
+ return context;
+ }
+
+ {
+ auto iterator = visited.find(context);
+ if (iterator != visited.end()) {
+ return iterator->second; // Not necessarly the same as context.
+ }
+ }
+
+ auto cached = contextCache.get(context);
+ if (cached) {
+ visited[context] = cached;
+ return cached;
+ }
+
+ bool changed = false;
+
+ std::vector<Ref<const PredictionContext>> parents(context->size());
+ for (size_t i = 0; i < parents.size(); i++) {
+ auto parent = getCachedContextImpl(context->getParent(i), contextCache, visited);
+ if (changed || parent != context->getParent(i)) {
+ if (!changed) {
+ parents.clear();
+ for (size_t j = 0; j < context->size(); j++) {
+ parents.push_back(context->getParent(j));
+ }
+
+ changed = true;
+ }
+
+ parents[i] = std::move(parent);
+ }
+ }
+
+ if (!changed) {
+ visited[context] = context;
+ contextCache.put(context);
+ return context;
+ }
+
+ Ref<const PredictionContext> updated;
+ if (parents.empty()) {
+ updated = PredictionContext::EMPTY;
+ } else if (parents.size() == 1) {
+ updated = SingletonPredictionContext::create(std::move(parents[0]), context->getReturnState(0));
+ contextCache.put(updated);
+ } else {
+ updated = std::make_shared<ArrayPredictionContext>(std::move(parents), downCast<const ArrayPredictionContext*>(context.get())->returnStates);
+ contextCache.put(updated);
+ }
+
+ visited[updated] = updated;
+ visited[context] = updated;
+
+ return updated;
+ }
+
+ void getAllContextNodesImpl(const Ref<const PredictionContext> &context,
+ std::vector<Ref<const PredictionContext>> &nodes,
+ std::unordered_set<const PredictionContext*> &visited) {
+
+ if (visited.find(context.get()) != visited.end()) {
+ return; // Already done.
+ }
+
+ visited.insert(context.get());
+ nodes.push_back(context);
+
+ for (size_t i = 0; i < context->size(); i++) {
+ getAllContextNodesImpl(context->getParent(i), nodes, visited);
+ }
+ }
+
+ size_t insertOrAssignNodeId(std::unordered_map<const PredictionContext*, size_t> &nodeIds, size_t &nodeId, const PredictionContext *node) {
+ auto existing = nodeIds.find(node);
+ if (existing != nodeIds.end()) {
+ return existing->second;
+ }
+ return nodeIds.insert({node, nodeId++}).first->second;
+ }
+
+}
+
+const Ref<const PredictionContext> PredictionContext::EMPTY = std::make_shared<SingletonPredictionContext>(nullptr, PredictionContext::EMPTY_RETURN_STATE);
+
+//----------------- PredictionContext ----------------------------------------------------------------------------------
+
+PredictionContext::PredictionContext(PredictionContextType contextType) : _contextType(contextType), _hashCode(0) {}
+
+PredictionContext::PredictionContext(PredictionContext&& other) : _contextType(other._contextType), _hashCode(other._hashCode.exchange(0, std::memory_order_relaxed)) {}
+
+Ref<const PredictionContext> PredictionContext::fromRuleContext(const ATN &atn, RuleContext *outerContext) {
+ if (outerContext == nullptr) {
+ return PredictionContext::EMPTY;
+ }
+
+ // if we are in RuleContext of start rule, s, then PredictionContext
+ // is EMPTY. Nobody called us. (if we are empty, return empty)
+ if (outerContext->parent == nullptr || outerContext == &ParserRuleContext::EMPTY) {
+ return PredictionContext::EMPTY;
+ }
+
+ // If we have a parent, convert it to a PredictionContext graph
+ auto parent = PredictionContext::fromRuleContext(atn, RuleContext::is(outerContext->parent) ? downCast<RuleContext*>(outerContext->parent) : nullptr);
+ const auto *transition = downCast<const RuleTransition*>(atn.states[outerContext->invokingState]->transitions[0].get());
+ return SingletonPredictionContext::create(std::move(parent), transition->followState->stateNumber);
+}
+
+bool PredictionContext::hasEmptyPath() const {
+ // since EMPTY_RETURN_STATE can only appear in the last position, we check last one
+ return getReturnState(size() - 1) == EMPTY_RETURN_STATE;
+}
+
+size_t PredictionContext::hashCode() const {
+ auto hash = cachedHashCode();
+ if (hash == 0) {
+ hash = hashCodeImpl();
+ if (hash == 0) {
+ hash = std::numeric_limits<size_t>::max();
+ }
+ _hashCode.store(hash, std::memory_order_relaxed);
+ }
+ return hash;
+}
+
+Ref<const PredictionContext> PredictionContext::merge(Ref<const PredictionContext> a, Ref<const PredictionContext> b,
+ bool rootIsWildcard, PredictionContextMergeCache *mergeCache) {
+ assert(a && b);
+
+ // share same graph if both same
+ if (a == b || *a == *b) {
+ return a;
+ }
+
+ const auto aType = a->getContextType();
+ const auto bType = b->getContextType();
+
+ if (aType == PredictionContextType::SINGLETON && bType == PredictionContextType::SINGLETON) {
+ return mergeSingletons(std::static_pointer_cast<const SingletonPredictionContext>(std::move(a)),
+ std::static_pointer_cast<const SingletonPredictionContext>(std::move(b)), rootIsWildcard, mergeCache);
+ }
+
+ // At least one of a or b is array.
+ // If one is $ and rootIsWildcard, return $ as * wildcard.
+ if (rootIsWildcard) {
+ if (a == PredictionContext::EMPTY) {
+ return a;
+ }
+ if (b == PredictionContext::EMPTY) {
+ return b;
+ }
+ }
+
+ // convert singleton so both are arrays to normalize
+ Ref<const ArrayPredictionContext> left;
+ if (aType == PredictionContextType::SINGLETON) {
+ left = std::make_shared<ArrayPredictionContext>(downCast<const SingletonPredictionContext&>(*a));
+ } else {
+ left = std::static_pointer_cast<const ArrayPredictionContext>(std::move(a));
+ }
+ Ref<const ArrayPredictionContext> right;
+ if (bType == PredictionContextType::SINGLETON) {
+ right = std::make_shared<ArrayPredictionContext>(downCast<const SingletonPredictionContext&>(*b));
+ } else {
+ right = std::static_pointer_cast<const ArrayPredictionContext>(std::move(b));
+ }
+ return mergeArrays(std::move(left), std::move(right), rootIsWildcard, mergeCache);
+}
+
+Ref<const PredictionContext> PredictionContext::mergeSingletons(Ref<const SingletonPredictionContext> a, Ref<const SingletonPredictionContext> b,
+ bool rootIsWildcard, PredictionContextMergeCache *mergeCache) {
+
+ if (mergeCache) {
+ auto existing = mergeCache->get(a, b);
+ if (existing) {
+ return existing;
+ }
+ existing = mergeCache->get(b, a);
+ if (existing) {
+ return existing;
+ }
+ }
+
+ auto rootMerge = mergeRoot(a, b, rootIsWildcard);
+ if (rootMerge) {
+ if (mergeCache) {
+ return mergeCache->put(a, b, std::move(rootMerge));
+ }
+ return rootMerge;
+ }
+
+ const auto& parentA = a->parent;
+ const auto& parentB = b->parent;
+ if (a->returnState == b->returnState) { // a == b
+ auto parent = merge(parentA, parentB, rootIsWildcard, mergeCache);
+
+ // If parent is same as existing a or b parent or reduced to a parent, return it.
+ if (parent == parentA) { // ax + bx = ax, if a=b
+ return a;
+ }
+ if (parent == parentB) { // ax + bx = bx, if a=b
+ return b;
+ }
+
+ // else: ax + ay = a'[x,y]
+ // merge parents x and y, giving array node with x,y then remainders
+ // of those graphs. dup a, a' points at merged array
+ // new joined parent so create new singleton pointing to it, a'
+ auto c = SingletonPredictionContext::create(std::move(parent), a->returnState);
+ if (mergeCache) {
+ return mergeCache->put(a, b, std::move(c));
+ }
+ return c;
+ }
+ // a != b payloads differ
+ // see if we can collapse parents due to $+x parents if local ctx
+ Ref<const PredictionContext> singleParent;
+ if (a == b || (*parentA == *parentB)) { // ax + bx = [a,b]x
+ singleParent = parentA;
+ }
+ if (singleParent) { // parents are same, sort payloads and use same parent
+ std::vector<size_t> payloads = { a->returnState, b->returnState };
+ if (a->returnState > b->returnState) {
+ payloads[0] = b->returnState;
+ payloads[1] = a->returnState;
+ }
+ std::vector<Ref<const PredictionContext>> parents = { singleParent, singleParent };
+ auto c = std::make_shared<ArrayPredictionContext>(std::move(parents), std::move(payloads));
+ if (mergeCache) {
+ return mergeCache->put(a, b, std::move(c));
+ }
+ return c;
+ }
+
+ // parents differ and can't merge them. Just pack together
+ // into array; can't merge.
+ // ax + by = [ax,by]
+ if (a->returnState > b->returnState) { // sort by payload
+ std::vector<size_t> payloads = { b->returnState, a->returnState };
+ std::vector<Ref<const PredictionContext>> parents = { b->parent, a->parent };
+ auto c = std::make_shared<ArrayPredictionContext>(std::move(parents), std::move(payloads));
+ if (mergeCache) {
+ return mergeCache->put(a, b, std::move(c));
+ }
+ return c;
+ }
+ std::vector<size_t> payloads = {a->returnState, b->returnState};
+ std::vector<Ref<const PredictionContext>> parents = { a->parent, b->parent };
+ auto c = std::make_shared<ArrayPredictionContext>(std::move(parents), std::move(payloads));
+ if (mergeCache) {
+ return mergeCache->put(a, b, std::move(c));
+ }
+ return c;
+}
+
+Ref<const PredictionContext> PredictionContext::mergeRoot(Ref<const SingletonPredictionContext> a, Ref<const SingletonPredictionContext> b,
+ bool rootIsWildcard) {
+ if (rootIsWildcard) {
+ if (a == EMPTY) { // * + b = *
+ return EMPTY;
+ }
+ if (b == EMPTY) { // a + * = *
+ return EMPTY;
+ }
+ } else {
+ if (a == EMPTY && b == EMPTY) { // $ + $ = $
+ return EMPTY;
+ }
+ if (a == EMPTY) { // $ + x = [$,x]
+ std::vector<size_t> payloads = { b->returnState, EMPTY_RETURN_STATE };
+ std::vector<Ref<const PredictionContext>> parents = { b->parent, nullptr };
+ return std::make_shared<ArrayPredictionContext>(std::move(parents), std::move(payloads));
+ }
+ if (b == EMPTY) { // x + $ = [$,x] ($ is always first if present)
+ std::vector<size_t> payloads = { a->returnState, EMPTY_RETURN_STATE };
+ std::vector<Ref<const PredictionContext>> parents = { a->parent, nullptr };
+ return std::make_shared<ArrayPredictionContext>(std::move(parents), std::move(payloads));
+ }
+ }
+ return nullptr;
+}
+
+Ref<const PredictionContext> PredictionContext::mergeArrays(Ref<const ArrayPredictionContext> a, Ref<const ArrayPredictionContext> b,
+ bool rootIsWildcard, PredictionContextMergeCache *mergeCache) {
+
+ if (mergeCache) {
+ auto existing = mergeCache->get(a, b);
+ if (existing) {
+ return existing;
+ }
+ existing = mergeCache->get(b, a);
+ if (existing) {
+ return existing;
+ }
+ }
+
+ // merge sorted payloads a + b => M
+ size_t i = 0; // walks a
+ size_t j = 0; // walks b
+ size_t k = 0; // walks target M array
+
+ std::vector<size_t> mergedReturnStates(a->returnStates.size() + b->returnStates.size());
+ std::vector<Ref<const PredictionContext>> mergedParents(a->returnStates.size() + b->returnStates.size());
+
+ // walk and merge to yield mergedParents, mergedReturnStates
+ while (i < a->returnStates.size() && j < b->returnStates.size()) {
+ const auto& parentA = a->parents[i];
+ const auto& parentB = b->parents[j];
+ if (a->returnStates[i] == b->returnStates[j]) {
+ // same payload (stack tops are equal), must yield merged singleton
+ size_t payload = a->returnStates[i];
+ // $+$ = $
+ bool both$ = payload == EMPTY_RETURN_STATE && !parentA && !parentB;
+ bool ax_ax = (parentA && parentB) && *parentA == *parentB; // ax+ax -> ax
+ if (both$ || ax_ax) {
+ mergedParents[k] = parentA; // choose left
+ mergedReturnStates[k] = payload;
+ } else { // ax+ay -> a'[x,y]
+ mergedParents[k] = merge(parentA, parentB, rootIsWildcard, mergeCache);
+ mergedReturnStates[k] = payload;
+ }
+ i++; // hop over left one as usual
+ j++; // but also skip one in right side since we merge
+ } else if (a->returnStates[i] < b->returnStates[j]) { // copy a[i] to M
+ mergedParents[k] = parentA;
+ mergedReturnStates[k] = a->returnStates[i];
+ i++;
+ } else { // b > a, copy b[j] to M
+ mergedParents[k] = parentB;
+ mergedReturnStates[k] = b->returnStates[j];
+ j++;
+ }
+ k++;
+ }
+
+ // copy over any payloads remaining in either array
+ if (i < a->returnStates.size()) {
+ for (auto p = i; p < a->returnStates.size(); p++) {
+ mergedParents[k] = a->parents[p];
+ mergedReturnStates[k] = a->returnStates[p];
+ k++;
+ }
+ } else {
+ for (auto p = j; p < b->returnStates.size(); p++) {
+ mergedParents[k] = b->parents[p];
+ mergedReturnStates[k] = b->returnStates[p];
+ k++;
+ }
+ }
+
+ // trim merged if we combined a few that had same stack tops
+ if (k < mergedParents.size()) { // write index < last position; trim
+ if (k == 1) { // for just one merged element, return singleton top
+ auto c = SingletonPredictionContext::create(std::move(mergedParents[0]), mergedReturnStates[0]);
+ if (mergeCache) {
+ return mergeCache->put(a, b, std::move(c));
+ }
+ return c;
+ }
+ mergedParents.resize(k);
+ mergedReturnStates.resize(k);
+ }
+
+ ArrayPredictionContext m(std::move(mergedParents), std::move(mergedReturnStates));
+
+ // if we created same array as a or b, return that instead
+ // TODO: track whether this is possible above during merge sort for speed
+ if (m == *a) {
+ if (mergeCache) {
+ return mergeCache->put(a, b, a);
+ }
+ return a;
+ }
+ if (m == *b) {
+ if (mergeCache) {
+ return mergeCache->put(a, b, b);
+ }
+ return b;
+ }
+
+ combineCommonParents(m.parents);
+ auto c = std::make_shared<ArrayPredictionContext>(std::move(m));
+ if (mergeCache) {
+ return mergeCache->put(a, b, std::move(c));
+ }
+ return c;
+}
+
+std::string PredictionContext::toDOTString(const Ref<const PredictionContext> &context) {
+ if (context == nullptr) {
+ return "";
+ }
+
+ std::stringstream ss;
+ ss << "digraph G {\n" << "rankdir=LR;\n";
+
+ std::vector<Ref<const PredictionContext>> nodes = getAllContextNodes(context);
+ std::unordered_map<const PredictionContext*, size_t> nodeIds;
+ size_t nodeId = 0;
+
+ for (const auto &current : nodes) {
+ if (current->getContextType() == PredictionContextType::SINGLETON) {
+ std::string s = std::to_string(insertOrAssignNodeId(nodeIds, nodeId, current.get()));
+ ss << " s" << s;
+ std::string returnState = std::to_string(current->getReturnState(0));
+ if (current == PredictionContext::EMPTY) {
+ returnState = "$";
+ }
+ ss << " [label=\"" << returnState << "\"];\n";
+ continue;
+ }
+ Ref<const ArrayPredictionContext> arr = std::static_pointer_cast<const ArrayPredictionContext>(current);
+ ss << " s" << insertOrAssignNodeId(nodeIds, nodeId, arr.get()) << " [shape=box, label=\"" << "[";
+ bool first = true;
+ for (auto inv : arr->returnStates) {
+ if (!first) {
+ ss << ", ";
+ }
+ if (inv == EMPTY_RETURN_STATE) {
+ ss << "$";
+ } else {
+ ss << inv;
+ }
+ first = false;
+ }
+ ss << "]";
+ ss << "\"];\n";
+ }
+
+ for (const auto &current : nodes) {
+ if (current == EMPTY) {
+ continue;
+ }
+ for (size_t i = 0; i < current->size(); i++) {
+ if (!current->getParent(i)) {
+ continue;
+ }
+ ss << " s" << insertOrAssignNodeId(nodeIds, nodeId, current.get()) << "->" << "s" << insertOrAssignNodeId(nodeIds, nodeId, current->getParent(i).get());
+ if (current->size() > 1) {
+ ss << " [label=\"parent[" << i << "]\"];\n";
+ } else {
+ ss << ";\n";
+ }
+ }
+ }
+
+ ss << "}\n";
+ return ss.str();
+}
+
+// The "visited" map is just a temporary structure to control the retrieval process (which is recursive).
+Ref<const PredictionContext> PredictionContext::getCachedContext(const Ref<const PredictionContext> &context,
+ PredictionContextCache &contextCache) {
+ std::unordered_map<Ref<const PredictionContext>, Ref<const PredictionContext>> visited;
+ return getCachedContextImpl(context, contextCache, visited);
+}
+
+std::vector<Ref<const PredictionContext>> PredictionContext::getAllContextNodes(const Ref<const PredictionContext> &context) {
+ std::vector<Ref<const PredictionContext>> nodes;
+ std::unordered_set<const PredictionContext*> visited;
+ getAllContextNodesImpl(context, nodes, visited);
+ return nodes;
+}
+
+std::vector<std::string> PredictionContext::toStrings(Recognizer *recognizer, int currentState) const {
+ return toStrings(recognizer, EMPTY, currentState);
+}
+
+std::vector<std::string> PredictionContext::toStrings(Recognizer *recognizer, const Ref<const PredictionContext> &stop, int currentState) const {
+
+ std::vector<std::string> result;
+
+ for (size_t perm = 0; ; perm++) {
+ size_t offset = 0;
+ bool last = true;
+ const PredictionContext *p = this;
+ size_t stateNumber = currentState;
+
+ std::stringstream ss;
+ ss << "[";
+ bool outerContinue = false;
+ while (!p->isEmpty() && p != stop.get()) {
+ size_t index = 0;
+ if (p->size() > 0) {
+ size_t bits = 1;
+ while ((1ULL << bits) < p->size()) {
+ bits++;
+ }
+
+ size_t mask = (1 << bits) - 1;
+ index = (perm >> offset) & mask;
+ last &= index >= p->size() - 1;
+ if (index >= p->size()) {
+ outerContinue = true;
+ break;
+ }
+ offset += bits;
+ }
+
+ if (recognizer != nullptr) {
+ if (ss.tellp() > 1) {
+ // first char is '[', if more than that this isn't the first rule
+ ss << ' ';
+ }
+
+ const ATN &atn = recognizer->getATN();
+ ATNState *s = atn.states[stateNumber];
+ std::string ruleName = recognizer->getRuleNames()[s->ruleIndex];
+ ss << ruleName;
+ } else if (p->getReturnState(index) != EMPTY_RETURN_STATE) {
+ if (!p->isEmpty()) {
+ if (ss.tellp() > 1) {
+ // first char is '[', if more than that this isn't the first rule
+ ss << ' ';
+ }
+
+ ss << p->getReturnState(index);
+ }
+ }
+ stateNumber = p->getReturnState(index);
+ p = p->getParent(index).get();
+ }
+
+ if (outerContinue)
+ continue;
+
+ ss << "]";
+ result.push_back(ss.str());
+
+ if (last) {
+ break;
+ }
+ }
+
+ return result;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContext.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContext.h
new file mode 100644
index 0000000000..967355af17
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContext.h
@@ -0,0 +1,225 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <atomic>
+
+#include "Recognizer.h"
+#include "atn/ATN.h"
+#include "atn/ATNState.h"
+#include "atn/PredictionContextType.h"
+
+namespace antlr4 {
+
+ class RuleContext;
+
+namespace atn {
+
+ class ATN;
+ class ArrayPredictionContext;
+ class SingletonPredictionContext;
+ class PredictionContextCache;
+ class PredictionContextMergeCache;
+
+ class ANTLR4CPP_PUBLIC PredictionContext {
+ public:
+ /// Represents $ in local context prediction, which means wildcard.
+ /// *+x = *.
+ static const Ref<const PredictionContext> EMPTY;
+
+ /// Represents $ in an array in full context mode, when $
+ /// doesn't mean wildcard: $ + x = [$,x]. Here,
+ /// $ = EMPTY_RETURN_STATE.
+ // ml: originally Integer.MAX_VALUE, which would be -1 for us, but this is already used in places where
+ // -1 is converted to unsigned, so we use a different value here. Any value does the job provided it doesn't
+ // conflict with real return states.
+ static constexpr size_t EMPTY_RETURN_STATE = std::numeric_limits<size_t>::max() - 9;
+
+ // dispatch
+ static Ref<const PredictionContext> merge(Ref<const PredictionContext> a,
+ Ref<const PredictionContext> b,
+ bool rootIsWildcard,
+ PredictionContextMergeCache *mergeCache);
+
+ /// <summary>
+ /// Merge two <seealso cref="SingletonPredictionContext"/> instances.
+ ///
+ /// <p/>
+ ///
+ /// Stack tops equal, parents merge is same; return left graph.<br/>
+ /// <embed src="images/SingletonMerge_SameRootSamePar.svg" type="image/svg+xml"/>
+ ///
+ /// <p/>
+ ///
+ /// Same stack top, parents differ; merge parents giving array node, then
+ /// remainders of those graphs. A new root node is created to point to the
+ /// merged parents.<br/>
+ /// <embed src="images/SingletonMerge_SameRootDiffPar.svg" type="image/svg+xml"/>
+ ///
+ /// <p/>
+ ///
+ /// Different stack tops pointing to same parent. Make array node for the
+ /// root where both element in the root point to the same (original)
+ /// parent.<br/>
+ /// <embed src="images/SingletonMerge_DiffRootSamePar.svg" type="image/svg+xml"/>
+ ///
+ /// <p/>
+ ///
+ /// Different stack tops pointing to different parents. Make array node for
+ /// the root where each element points to the corresponding original
+ /// parent.<br/>
+ /// <embed src="images/SingletonMerge_DiffRootDiffPar.svg" type="image/svg+xml"/>
+ /// </summary>
+ /// <param name="a"> the first <seealso cref="SingletonPredictionContext"/> </param>
+ /// <param name="b"> the second <seealso cref="SingletonPredictionContext"/> </param>
+ /// <param name="rootIsWildcard"> {@code true} if this is a local-context merge,
+ /// otherwise false to indicate a full-context merge </param>
+ /// <param name="mergeCache"> </param>
+ static Ref<const PredictionContext> mergeSingletons(Ref<const SingletonPredictionContext> a,
+ Ref<const SingletonPredictionContext> b,
+ bool rootIsWildcard,
+ PredictionContextMergeCache *mergeCache);
+
+ /**
+ * Handle case where at least one of {@code a} or {@code b} is
+ * {@link #EMPTY}. In the following diagrams, the symbol {@code $} is used
+ * to represent {@link #EMPTY}.
+ *
+ * <h2>Local-Context Merges</h2>
+ *
+ * <p>These local-context merge operations are used when {@code rootIsWildcard}
+ * is true.</p>
+ *
+ * <p>{@link #EMPTY} is superset of any graph; return {@link #EMPTY}.<br>
+ * <embed src="images/LocalMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
+ *
+ * <p>{@link #EMPTY} and anything is {@code #EMPTY}, so merged parent is
+ * {@code #EMPTY}; return left graph.<br>
+ * <embed src="images/LocalMerge_EmptyParent.svg" type="image/svg+xml"/></p>
+ *
+ * <p>Special case of last merge if local context.<br>
+ * <embed src="images/LocalMerge_DiffRoots.svg" type="image/svg+xml"/></p>
+ *
+ * <h2>Full-Context Merges</h2>
+ *
+ * <p>These full-context merge operations are used when {@code rootIsWildcard}
+ * is false.</p>
+ *
+ * <p><embed src="images/FullMerge_EmptyRoots.svg" type="image/svg+xml"/></p>
+ *
+ * <p>Must keep all contexts; {@link #EMPTY} in array is a special value (and
+ * null parent).<br>
+ * <embed src="images/FullMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
+ *
+ * <p><embed src="images/FullMerge_SameRoot.svg" type="image/svg+xml"/></p>
+ *
+ * @param a the first {@link SingletonPredictionContext}
+ * @param b the second {@link SingletonPredictionContext}
+ * @param rootIsWildcard {@code true} if this is a local-context merge,
+ * otherwise false to indicate a full-context merge
+ */
+ static Ref<const PredictionContext> mergeRoot(Ref<const SingletonPredictionContext> a,
+ Ref<const SingletonPredictionContext> b,
+ bool rootIsWildcard);
+
+ /**
+ * Merge two {@link ArrayPredictionContext} instances.
+ *
+ * <p>Different tops, different parents.<br>
+ * <embed src="images/ArrayMerge_DiffTopDiffPar.svg" type="image/svg+xml"/></p>
+ *
+ * <p>Shared top, same parents.<br>
+ * <embed src="images/ArrayMerge_ShareTopSamePar.svg" type="image/svg+xml"/></p>
+ *
+ * <p>Shared top, different parents.<br>
+ * <embed src="images/ArrayMerge_ShareTopDiffPar.svg" type="image/svg+xml"/></p>
+ *
+ * <p>Shared top, all shared parents.<br>
+ * <embed src="images/ArrayMerge_ShareTopSharePar.svg" type="image/svg+xml"/></p>
+ *
+ * <p>Equal tops, merge parents and reduce top to
+ * {@link SingletonPredictionContext}.<br>
+ * <embed src="images/ArrayMerge_EqualTop.svg" type="image/svg+xml"/></p>
+ */
+ static Ref<const PredictionContext> mergeArrays(Ref<const ArrayPredictionContext> a,
+ Ref<const ArrayPredictionContext> b,
+ bool rootIsWildcard,
+ PredictionContextMergeCache *mergeCache);
+
+ static std::string toDOTString(const Ref<const PredictionContext> &context);
+
+ static Ref<const PredictionContext> getCachedContext(const Ref<const PredictionContext> &context,
+ PredictionContextCache &contextCache);
+
+ static std::vector<Ref<const PredictionContext>> getAllContextNodes(const Ref<const PredictionContext> &context);
+
+ /// Convert a RuleContext tree to a PredictionContext graph.
+ /// Return EMPTY if outerContext is empty.
+ static Ref<const PredictionContext> fromRuleContext(const ATN &atn, RuleContext *outerContext);
+
+ PredictionContext(const PredictionContext&) = delete;
+
+ virtual ~PredictionContext() = default;
+
+ PredictionContext& operator=(const PredictionContext&) = delete;
+ PredictionContext& operator=(PredictionContext&&) = delete;
+
+ PredictionContextType getContextType() const { return _contextType; }
+
+ virtual size_t size() const = 0;
+ virtual const Ref<const PredictionContext>& getParent(size_t index) const = 0;
+ virtual size_t getReturnState(size_t index) const = 0;
+
+ /// This means only the EMPTY (wildcard? not sure) context is in set.
+ virtual bool isEmpty() const = 0;
+ bool hasEmptyPath() const;
+
+ size_t hashCode() const;
+
+ virtual bool equals(const PredictionContext &other) const = 0;
+
+ virtual std::string toString() const = 0;
+
+ std::vector<std::string> toStrings(Recognizer *recognizer, int currentState) const;
+ std::vector<std::string> toStrings(Recognizer *recognizer,
+ const Ref<const PredictionContext> &stop,
+ int currentState) const;
+
+ protected:
+ explicit PredictionContext(PredictionContextType contextType);
+
+ PredictionContext(PredictionContext&& other);
+
+ virtual size_t hashCodeImpl() const = 0;
+
+ size_t cachedHashCode() const { return _hashCode.load(std::memory_order_relaxed); }
+
+ private:
+ const PredictionContextType _contextType;
+ mutable std::atomic<size_t> _hashCode;
+ };
+
+ inline bool operator==(const PredictionContext &lhs, const PredictionContext &rhs) {
+ return lhs.equals(rhs);
+ }
+
+ inline bool operator!=(const PredictionContext &lhs, const PredictionContext &rhs) {
+ return !operator==(lhs, rhs);
+ }
+
+} // namespace atn
+} // namespace antlr4
+
+namespace std {
+
+ template <>
+ struct hash<::antlr4::atn::PredictionContext> {
+ size_t operator()(const ::antlr4::atn::PredictionContext &predictionContext) const {
+ return predictionContext.hashCode();
+ }
+ };
+
+} // namespace std
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextCache.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextCache.cpp
new file mode 100644
index 0000000000..031a35cbf7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextCache.cpp
@@ -0,0 +1,56 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#include "atn/PredictionContextCache.h"
+
+using namespace antlr4::atn;
+
+void PredictionContextCache::put(const Ref<const PredictionContext> &value) {
+ assert(value);
+
+ _data.insert(value);
+}
+
+Ref<const PredictionContext> PredictionContextCache::get(
+ const Ref<const PredictionContext> &value) const {
+ assert(value);
+
+ auto iterator = _data.find(value);
+ if (iterator == _data.end()) {
+ return nullptr;
+ }
+ return *iterator;
+}
+
+size_t PredictionContextCache::PredictionContextHasher::operator()(
+ const Ref<const PredictionContext> &predictionContext) const {
+ return predictionContext->hashCode();
+}
+
+bool PredictionContextCache::PredictionContextComparer::operator()(
+ const Ref<const PredictionContext> &lhs,
+ const Ref<const PredictionContext> &rhs) const {
+ return *lhs == *rhs;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextCache.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextCache.h
new file mode 100644
index 0000000000..78c8210d97
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextCache.h
@@ -0,0 +1,63 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#pragma once
+
+#include "atn/PredictionContext.h"
+#include "FlatHashSet.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC PredictionContextCache final {
+ public:
+ PredictionContextCache() = default;
+
+ PredictionContextCache(const PredictionContextCache&) = delete;
+ PredictionContextCache(PredictionContextCache&&) = delete;
+
+ PredictionContextCache& operator=(const PredictionContextCache&) = delete;
+ PredictionContextCache& operator=(PredictionContextCache&&) = delete;
+
+ void put(const Ref<const PredictionContext> &value);
+
+ Ref<const PredictionContext> get(const Ref<const PredictionContext> &value) const;
+
+ private:
+ struct ANTLR4CPP_PUBLIC PredictionContextHasher final {
+ size_t operator()(const Ref<const PredictionContext> &predictionContext) const;
+ };
+
+ struct ANTLR4CPP_PUBLIC PredictionContextComparer final {
+ bool operator()(const Ref<const PredictionContext> &lhs,
+ const Ref<const PredictionContext> &rhs) const;
+ };
+
+ FlatHashSet<Ref<const PredictionContext>,
+ PredictionContextHasher, PredictionContextComparer> _data;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCache.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCache.cpp
new file mode 100644
index 0000000000..7160b59998
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCache.cpp
@@ -0,0 +1,167 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#include "atn/PredictionContextMergeCache.h"
+
+#include "misc/MurmurHash.h"
+
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+
+PredictionContextMergeCache::PredictionContextMergeCache(
+ const PredictionContextMergeCacheOptions &options) : _options(options) {}
+
+Ref<const PredictionContext> PredictionContextMergeCache::put(
+ const Ref<const PredictionContext> &key1,
+ const Ref<const PredictionContext> &key2,
+ Ref<const PredictionContext> value) {
+ assert(key1);
+ assert(key2);
+
+ if (getOptions().getMaxSize() == 0) {
+ // Cache is effectively disabled.
+ return value;
+ }
+
+ auto [existing, inserted] = _entries.try_emplace(std::make_pair(key1.get(), key2.get()));
+ if (inserted) {
+ try {
+ existing->second.reset(new Entry());
+ } catch (...) {
+ _entries.erase(existing);
+ throw;
+ }
+ existing->second->key = std::make_pair(key1, key2);
+ existing->second->value = std::move(value);
+ pushToFront(existing->second.get());
+ } else {
+ if (existing->second->value != value) {
+ existing->second->value = std::move(value);
+ }
+ moveToFront(existing->second.get());
+ }
+ compact(existing->second.get());
+ return existing->second->value;
+}
+
+Ref<const PredictionContext> PredictionContextMergeCache::get(
+ const Ref<const PredictionContext> &key1,
+ const Ref<const PredictionContext> &key2) const {
+ assert(key1);
+ assert(key2);
+
+ if (getOptions().getMaxSize() == 0) {
+ // Cache is effectively disabled.
+ return nullptr;
+ }
+
+ auto iterator = _entries.find(std::make_pair(key1.get(), key2.get()));
+ if (iterator == _entries.end()) {
+ return nullptr;
+ }
+ moveToFront(iterator->second.get());
+ return iterator->second->value;
+}
+
+void PredictionContextMergeCache::clear() {
+ Container().swap(_entries);
+ _head = _tail = nullptr;
+ _size = 0;
+}
+
+void PredictionContextMergeCache::moveToFront(Entry *entry) const {
+ if (entry->prev == nullptr) {
+ assert(entry == _head);
+ return;
+ }
+ entry->prev->next = entry->next;
+ if (entry->next != nullptr) {
+ entry->next->prev = entry->prev;
+ } else {
+ assert(entry == _tail);
+ _tail = entry->prev;
+ }
+ entry->prev = nullptr;
+ entry->next = _head;
+ _head->prev = entry;
+ _head = entry;
+ assert(entry->prev == nullptr);
+}
+
+void PredictionContextMergeCache::pushToFront(Entry *entry) {
+ ++_size;
+ entry->prev = nullptr;
+ entry->next = _head;
+ if (_head != nullptr) {
+ _head->prev = entry;
+ _head = entry;
+ } else {
+ assert(entry->next == nullptr);
+ _head = entry;
+ _tail = entry;
+ }
+ assert(entry->prev == nullptr);
+}
+
+void PredictionContextMergeCache::remove(Entry *entry) {
+ if (entry->prev != nullptr) {
+ entry->prev->next = entry->next;
+ } else {
+ assert(entry == _head);
+ _head = entry->next;
+ }
+ if (entry->next != nullptr) {
+ entry->next->prev = entry->prev;
+ } else {
+ assert(entry == _tail);
+ _tail = entry->prev;
+ }
+ --_size;
+ _entries.erase(std::make_pair(entry->key.first.get(), entry->key.second.get()));
+}
+
+void PredictionContextMergeCache::compact(const Entry *preserve) {
+ Entry *entry = _tail;
+ while (entry != nullptr && _size > getOptions().getMaxSize()) {
+ Entry *next = entry->prev;
+ if (entry != preserve) {
+ remove(entry);
+ }
+ entry = next;
+ }
+}
+
+size_t PredictionContextMergeCache::PredictionContextHasher::operator()(
+ const PredictionContextPair &value) const {
+ size_t hash = MurmurHash::initialize();
+ hash = MurmurHash::update(hash, value.first->hashCode());
+ hash = MurmurHash::update(hash, value.second->hashCode());
+ return MurmurHash::finish(hash, 2);
+}
+
+bool PredictionContextMergeCache::PredictionContextComparer::operator()(
+ const PredictionContextPair &lhs, const PredictionContextPair &rhs) const {
+ return *lhs.first == *rhs.first && *lhs.second == *rhs.second;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCache.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCache.h
new file mode 100644
index 0000000000..efaeaef578
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCache.h
@@ -0,0 +1,101 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#pragma once
+
+#include <utility>
+
+#include "atn/PredictionContext.h"
+#include "atn/PredictionContextMergeCacheOptions.h"
+#include "FlatHashMap.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC PredictionContextMergeCache final {
+ public:
+ PredictionContextMergeCache()
+ : PredictionContextMergeCache(PredictionContextMergeCacheOptions()) {}
+
+ explicit PredictionContextMergeCache(const PredictionContextMergeCacheOptions &options);
+
+ PredictionContextMergeCache(const PredictionContextMergeCache&) = delete;
+ PredictionContextMergeCache(PredictionContextMergeCache&&) = delete;
+
+ PredictionContextMergeCache& operator=(const PredictionContextMergeCache&) = delete;
+ PredictionContextMergeCache& operator=(PredictionContextMergeCache&&) = delete;
+
+ Ref<const PredictionContext> put(const Ref<const PredictionContext> &key1,
+ const Ref<const PredictionContext> &key2,
+ Ref<const PredictionContext> value);
+
+ Ref<const PredictionContext> get(const Ref<const PredictionContext> &key1,
+ const Ref<const PredictionContext> &key2) const;
+
+ const PredictionContextMergeCacheOptions& getOptions() const { return _options; }
+
+ void clear();
+
+ private:
+ using PredictionContextPair = std::pair<const PredictionContext*, const PredictionContext*>;
+
+ struct ANTLR4CPP_PUBLIC PredictionContextHasher final {
+ size_t operator()(const PredictionContextPair &value) const;
+ };
+
+ struct ANTLR4CPP_PUBLIC PredictionContextComparer final {
+ bool operator()(const PredictionContextPair &lhs, const PredictionContextPair &rhs) const;
+ };
+
+ struct ANTLR4CPP_PUBLIC Entry final {
+ std::pair<Ref<const PredictionContext>, Ref<const PredictionContext>> key;
+ Ref<const PredictionContext> value;
+ Entry *prev = nullptr;
+ Entry *next = nullptr;
+ };
+
+ void moveToFront(Entry *entry) const;
+
+ void pushToFront(Entry *entry);
+
+ void remove(Entry *entry);
+
+ void compact(const Entry *preserve);
+
+ using Container = FlatHashMap<PredictionContextPair, std::unique_ptr<Entry>,
+ PredictionContextHasher, PredictionContextComparer>;
+
+ const PredictionContextMergeCacheOptions _options;
+
+ Container _entries;
+
+ mutable Entry *_head = nullptr;
+ mutable Entry *_tail = nullptr;
+
+ size_t _size = 0;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCacheOptions.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCacheOptions.h
new file mode 100644
index 0000000000..7331cc17e0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextMergeCacheOptions.h
@@ -0,0 +1,71 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC PredictionContextMergeCacheOptions final {
+ public:
+ PredictionContextMergeCacheOptions() = default;
+
+ size_t getMaxSize() const { return _maxSize; }
+
+ bool hasMaxSize() const { return getMaxSize() != std::numeric_limits<size_t>::max(); }
+
+ PredictionContextMergeCacheOptions& setMaxSize(size_t maxSize) {
+ _maxSize = maxSize;
+ return *this;
+ }
+
+ size_t getClearEveryN() const {
+ return _clearEveryN;
+ }
+
+ bool hasClearEveryN() const { return getClearEveryN() != 0; }
+
+ PredictionContextMergeCacheOptions& setClearEveryN(uint64_t clearEveryN) {
+ _clearEveryN = clearEveryN;
+ return *this;
+ }
+
+ PredictionContextMergeCacheOptions& neverClear() {
+ return setClearEveryN(0);
+ }
+
+ private:
+ size_t _maxSize = std::numeric_limits<size_t>::max();
+ uint64_t _clearEveryN = 1;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextType.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextType.h
new file mode 100644
index 0000000000..c8c4473e13
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionContextType.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ enum class PredictionContextType : size_t {
+ SINGLETON = 1,
+ ARRAY = 2,
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionMode.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionMode.cpp
new file mode 100644
index 0000000000..9db0b8bdb9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionMode.cpp
@@ -0,0 +1,202 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/RuleStopState.h"
+#include "atn/ATNConfigSet.h"
+#include "atn/ATNConfig.h"
+#include "misc/MurmurHash.h"
+#include "SemanticContext.h"
+
+#include "PredictionMode.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+struct AltAndContextConfigHasher
+{
+ /**
+ * The hash code is only a function of the {@link ATNState#stateNumber}
+ * and {@link ATNConfig#context}.
+ */
+ size_t operator () (ATNConfig *o) const {
+ size_t hashCode = misc::MurmurHash::initialize(7);
+ hashCode = misc::MurmurHash::update(hashCode, o->state->stateNumber);
+ hashCode = misc::MurmurHash::update(hashCode, o->context);
+ return misc::MurmurHash::finish(hashCode, 2);
+ }
+};
+
+struct AltAndContextConfigComparer {
+ bool operator()(ATNConfig *a, ATNConfig *b) const
+ {
+ if (a == b) {
+ return true;
+ }
+ return a->state->stateNumber == b->state->stateNumber && *a->context == *b->context;
+ }
+};
+
+bool PredictionModeClass::hasSLLConflictTerminatingPrediction(PredictionMode mode, ATNConfigSet *configs) {
+ /* Configs in rule stop states indicate reaching the end of the decision
+ * rule (local context) or end of start rule (full context). If all
+ * configs meet this condition, then none of the configurations is able
+ * to match additional input so we terminate prediction.
+ */
+ if (allConfigsInRuleStopStates(configs)) {
+ return true;
+ }
+
+ bool heuristic;
+
+ // Pure SLL mode parsing or SLL+LL if:
+ // Don't bother with combining configs from different semantic
+ // contexts if we can fail over to full LL; costs more time
+ // since we'll often fail over anyway.
+ if (mode == PredictionMode::SLL || !configs->hasSemanticContext) {
+ std::vector<antlrcpp::BitSet> altsets = getConflictingAltSubsets(configs);
+ heuristic = hasConflictingAltSet(altsets) && !hasStateAssociatedWithOneAlt(configs);
+ } else {
+ // dup configs, tossing out semantic predicates
+ ATNConfigSet dup(true);
+ for (auto &config : configs->configs) {
+ Ref<ATNConfig> c = std::make_shared<ATNConfig>(*config, SemanticContext::Empty::Instance);
+ dup.add(c);
+ }
+ std::vector<antlrcpp::BitSet> altsets = getConflictingAltSubsets(&dup);
+ heuristic = hasConflictingAltSet(altsets) && !hasStateAssociatedWithOneAlt(&dup);
+ }
+
+ return heuristic;
+}
+
+bool PredictionModeClass::hasConfigInRuleStopState(ATNConfigSet *configs) {
+ for (const auto &config : configs->configs) {
+ if (RuleStopState::is(config->state)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PredictionModeClass::allConfigsInRuleStopStates(ATNConfigSet *configs) {
+ for (const auto &config : configs->configs) {
+ if (!RuleStopState::is(config->state)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+size_t PredictionModeClass::resolvesToJustOneViableAlt(const std::vector<antlrcpp::BitSet>& altsets) {
+ return getSingleViableAlt(altsets);
+}
+
+bool PredictionModeClass::allSubsetsConflict(const std::vector<antlrcpp::BitSet>& altsets) {
+ return !hasNonConflictingAltSet(altsets);
+}
+
+bool PredictionModeClass::hasNonConflictingAltSet(const std::vector<antlrcpp::BitSet>& altsets) {
+ for (antlrcpp::BitSet alts : altsets) {
+ if (alts.count() == 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PredictionModeClass::hasConflictingAltSet(const std::vector<antlrcpp::BitSet>& altsets) {
+ for (antlrcpp::BitSet alts : altsets) {
+ if (alts.count() > 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PredictionModeClass::allSubsetsEqual(const std::vector<antlrcpp::BitSet>& altsets) {
+ if (altsets.empty()) {
+ return true;
+ }
+
+ const antlrcpp::BitSet& first = *altsets.begin();
+ for (const antlrcpp::BitSet& alts : altsets) {
+ if (alts != first) {
+ return false;
+ }
+ }
+ return true;
+}
+
+size_t PredictionModeClass::getUniqueAlt(const std::vector<antlrcpp::BitSet>& altsets) {
+ antlrcpp::BitSet all = getAlts(altsets);
+ if (all.count() == 1) {
+ return all.nextSetBit(0);
+ }
+ return ATN::INVALID_ALT_NUMBER;
+}
+
+antlrcpp::BitSet PredictionModeClass::getAlts(const std::vector<antlrcpp::BitSet>& altsets) {
+ antlrcpp::BitSet all;
+ for (const auto &alts : altsets) {
+ all |= alts;
+ }
+
+ return all;
+}
+
+antlrcpp::BitSet PredictionModeClass::getAlts(ATNConfigSet *configs) {
+ antlrcpp::BitSet alts;
+ for (const auto &config : configs->configs) {
+ alts.set(config->alt);
+ }
+ return alts;
+}
+
+std::vector<antlrcpp::BitSet> PredictionModeClass::getConflictingAltSubsets(ATNConfigSet *configs) {
+ std::unordered_map<ATNConfig*, antlrcpp::BitSet, AltAndContextConfigHasher, AltAndContextConfigComparer> configToAlts;
+ for (auto &config : configs->configs) {
+ configToAlts[config.get()].set(config->alt);
+ }
+ std::vector<antlrcpp::BitSet> values;
+ values.reserve(configToAlts.size());
+ for (const auto &pair : configToAlts) {
+ values.push_back(pair.second);
+ }
+ return values;
+}
+
+std::unordered_map<ATNState*, antlrcpp::BitSet> PredictionModeClass::getStateToAltMap(ATNConfigSet *configs) {
+ std::unordered_map<ATNState*, antlrcpp::BitSet> m;
+ for (const auto &c : configs->configs) {
+ m[c->state].set(c->alt);
+ }
+ return m;
+}
+
+bool PredictionModeClass::hasStateAssociatedWithOneAlt(ATNConfigSet *configs) {
+ auto x = getStateToAltMap(configs);
+ for (const auto &pair : x){
+ if (pair.second.count() == 1) return true;
+ }
+ return false;
+}
+
+size_t PredictionModeClass::getSingleViableAlt(const std::vector<antlrcpp::BitSet>& altsets) {
+ antlrcpp::BitSet viableAlts;
+ for (const auto &alts : altsets) {
+ size_t minAlt = alts.nextSetBit(0);
+
+ viableAlts.set(minAlt);
+ if (viableAlts.count() > 1) // more than 1 viable alt
+ {
+ return ATN::INVALID_ALT_NUMBER;
+ }
+ }
+
+ return viableAlts.nextSetBit(0);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionMode.h b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionMode.h
new file mode 100644
index 0000000000..4868ea2ff2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/PredictionMode.h
@@ -0,0 +1,436 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "support/BitSet.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /**
+ * This enumeration defines the prediction modes available in ANTLR 4 along with
+ * utility methods for analyzing configuration sets for conflicts and/or
+ * ambiguities.
+ */
+ enum class PredictionMode {
+ /**
+ * The SLL(*) prediction mode. This prediction mode ignores the current
+ * parser context when making predictions. This is the fastest prediction
+ * mode, and provides correct results for many grammars. This prediction
+ * mode is more powerful than the prediction mode provided by ANTLR 3, but
+ * may result in syntax errors for grammar and input combinations which are
+ * not SLL.
+ *
+ * <p>
+ * When using this prediction mode, the parser will either return a correct
+ * parse tree (i.e. the same parse tree that would be returned with the
+ * {@link #LL} prediction mode), or it will report a syntax error. If a
+ * syntax error is encountered when using the {@link #SLL} prediction mode,
+ * it may be due to either an actual syntax error in the input or indicate
+ * that the particular combination of grammar and input requires the more
+ * powerful {@link #LL} prediction abilities to complete successfully.</p>
+ *
+ * <p>
+ * This prediction mode does not provide any guarantees for prediction
+ * behavior for syntactically-incorrect inputs.</p>
+ */
+ SLL,
+
+ /**
+ * The LL(*) prediction mode. This prediction mode allows the current parser
+ * context to be used for resolving SLL conflicts that occur during
+ * prediction. This is the fastest prediction mode that guarantees correct
+ * parse results for all combinations of grammars with syntactically correct
+ * inputs.
+ *
+ * <p>
+ * When using this prediction mode, the parser will make correct decisions
+ * for all syntactically-correct grammar and input combinations. However, in
+ * cases where the grammar is truly ambiguous this prediction mode might not
+ * report a precise answer for <em>exactly which</em> alternatives are
+ * ambiguous.</p>
+ *
+ * <p>
+ * This prediction mode does not provide any guarantees for prediction
+ * behavior for syntactically-incorrect inputs.</p>
+ */
+ LL,
+
+ /**
+ * The LL(*) prediction mode with exact ambiguity detection. In addition to
+ * the correctness guarantees provided by the {@link #LL} prediction mode,
+ * this prediction mode instructs the prediction algorithm to determine the
+ * complete and exact set of ambiguous alternatives for every ambiguous
+ * decision encountered while parsing.
+ *
+ * <p>
+ * This prediction mode may be used for diagnosing ambiguities during
+ * grammar development. Due to the performance overhead of calculating sets
+ * of ambiguous alternatives, this prediction mode should be avoided when
+ * the exact results are not necessary.</p>
+ *
+ * <p>
+ * This prediction mode does not provide any guarantees for prediction
+ * behavior for syntactically-incorrect inputs.</p>
+ */
+ LL_EXACT_AMBIG_DETECTION
+ };
+
+ class ANTLR4CPP_PUBLIC PredictionModeClass {
+ public:
+ /**
+ * Computes the SLL prediction termination condition.
+ *
+ * <p>
+ * This method computes the SLL prediction termination condition for both of
+ * the following cases.</p>
+ *
+ * <ul>
+ * <li>The usual SLL+LL fallback upon SLL conflict</li>
+ * <li>Pure SLL without LL fallback</li>
+ * </ul>
+ *
+ * <p><strong>COMBINED SLL+LL PARSING</strong></p>
+ *
+ * <p>When LL-fallback is enabled upon SLL conflict, correct predictions are
+ * ensured regardless of how the termination condition is computed by this
+ * method. Due to the substantially higher cost of LL prediction, the
+ * prediction should only fall back to LL when the additional lookahead
+ * cannot lead to a unique SLL prediction.</p>
+ *
+ * <p>Assuming combined SLL+LL parsing, an SLL configuration set with only
+ * conflicting subsets should fall back to full LL, even if the
+ * configuration sets don't resolve to the same alternative (e.g.
+ * {@code {1,2}} and {@code {3,4}}. If there is at least one non-conflicting
+ * configuration, SLL could continue with the hopes that more lookahead will
+ * resolve via one of those non-conflicting configurations.</p>
+ *
+ * <p>Here's the prediction termination rule them: SLL (for SLL+LL parsing)
+ * stops when it sees only conflicting configuration subsets. In contrast,
+ * full LL keeps going when there is uncertainty.</p>
+ *
+ * <p><strong>HEURISTIC</strong></p>
+ *
+ * <p>As a heuristic, we stop prediction when we see any conflicting subset
+ * unless we see a state that only has one alternative associated with it.
+ * The single-alt-state thing lets prediction continue upon rules like
+ * (otherwise, it would admit defeat too soon):</p>
+ *
+ * <p>{@code [12|1|[], 6|2|[], 12|2|[]]. s : (ID | ID ID?) ';' ;}</p>
+ *
+ * <p>When the ATN simulation reaches the state before {@code ';'}, it has a
+ * DFA state that looks like: {@code [12|1|[], 6|2|[], 12|2|[]]}. Naturally
+ * {@code 12|1|[]} and {@code 12|2|[]} conflict, but we cannot stop
+ * processing this node because alternative to has another way to continue,
+ * via {@code [6|2|[]]}.</p>
+ *
+ * <p>It also let's us continue for this rule:</p>
+ *
+ * <p>{@code [1|1|[], 1|2|[], 8|3|[]] a : A | A | A B ;}</p>
+ *
+ * <p>After matching input A, we reach the stop state for rule A, state 1.
+ * State 8 is the state right before B. Clearly alternatives 1 and 2
+ * conflict and no amount of further lookahead will separate the two.
+ * However, alternative 3 will be able to continue and so we do not stop
+ * working on this state. In the previous example, we're concerned with
+ * states associated with the conflicting alternatives. Here alt 3 is not
+ * associated with the conflicting configs, but since we can continue
+ * looking for input reasonably, don't declare the state done.</p>
+ *
+ * <p><strong>PURE SLL PARSING</strong></p>
+ *
+ * <p>To handle pure SLL parsing, all we have to do is make sure that we
+ * combine stack contexts for configurations that differ only by semantic
+ * predicate. From there, we can do the usual SLL termination heuristic.</p>
+ *
+ * <p><strong>PREDICATES IN SLL+LL PARSING</strong></p>
+ *
+ * <p>SLL decisions don't evaluate predicates until after they reach DFA stop
+ * states because they need to create the DFA cache that works in all
+ * semantic situations. In contrast, full LL evaluates predicates collected
+ * during start state computation so it can ignore predicates thereafter.
+ * This means that SLL termination detection can totally ignore semantic
+ * predicates.</p>
+ *
+ * <p>Implementation-wise, {@link ATNConfigSet} combines stack contexts but not
+ * semantic predicate contexts so we might see two configurations like the
+ * following.</p>
+ *
+ * <p>{@code (s, 1, x, {}), (s, 1, x', {p})}</p>
+ *
+ * <p>Before testing these configurations against others, we have to merge
+ * {@code x} and {@code x'} (without modifying the existing configurations).
+ * For example, we test {@code (x+x')==x''} when looking for conflicts in
+ * the following configurations.</p>
+ *
+ * <p>{@code (s, 1, x, {}), (s, 1, x', {p}), (s, 2, x'', {})}</p>
+ *
+ * <p>If the configuration set has predicates (as indicated by
+ * {@link ATNConfigSet#hasSemanticContext}), this algorithm makes a copy of
+ * the configurations to strip out all of the predicates so that a standard
+ * {@link ATNConfigSet} will merge everything ignoring predicates.</p>
+ */
+ static bool hasSLLConflictTerminatingPrediction(PredictionMode mode, ATNConfigSet *configs);
+
+ /// <summary>
+ /// Checks if any configuration in {@code configs} is in a
+ /// <seealso cref="RuleStopState"/>. Configurations meeting this condition have
+ /// reached
+ /// the end of the decision rule (local context) or end of start rule (full
+ /// context).
+ /// </summary>
+ /// <param name="configs"> the configuration set to test </param>
+ /// <returns> {@code true} if any configuration in {@code configs} is in a
+ /// <seealso cref="RuleStopState"/>, otherwise {@code false} </returns>
+ static bool hasConfigInRuleStopState(ATNConfigSet *configs);
+
+ /// <summary>
+ /// Checks if all configurations in {@code configs} are in a
+ /// <seealso cref="RuleStopState"/>. Configurations meeting this condition have
+ /// reached
+ /// the end of the decision rule (local context) or end of start rule (full
+ /// context).
+ /// </summary>
+ /// <param name="configs"> the configuration set to test </param>
+ /// <returns> {@code true} if all configurations in {@code configs} are in a
+ /// <seealso cref="RuleStopState"/>, otherwise {@code false} </returns>
+ static bool allConfigsInRuleStopStates(ATNConfigSet *configs);
+
+ /**
+ * Full LL prediction termination.
+ *
+ * <p>Can we stop looking ahead during ATN simulation or is there some
+ * uncertainty as to which alternative we will ultimately pick, after
+ * consuming more input? Even if there are partial conflicts, we might know
+ * that everything is going to resolve to the same minimum alternative. That
+ * means we can stop since no more lookahead will change that fact. On the
+ * other hand, there might be multiple conflicts that resolve to different
+ * minimums. That means we need more look ahead to decide which of those
+ * alternatives we should predict.</p>
+ *
+ * <p>The basic idea is to split the set of configurations {@code C}, into
+ * conflicting subsets {@code (s, _, ctx, _)} and singleton subsets with
+ * non-conflicting configurations. Two configurations conflict if they have
+ * identical {@link ATNConfig#state} and {@link ATNConfig#context} values
+ * but different {@link ATNConfig#alt} value, e.g. {@code (s, i, ctx, _)}
+ * and {@code (s, j, ctx, _)} for {@code i!=j}.</p>
+ *
+ * <p>Reduce these configuration subsets to the set of possible alternatives.
+ * You can compute the alternative subsets in one pass as follows:</p>
+ *
+ * <p>{@code A_s,ctx = {i | (s, i, ctx, _)}} for each configuration in
+ * {@code C} holding {@code s} and {@code ctx} fixed.</p>
+ *
+ * <p>Or in pseudo-code, for each configuration {@code c} in {@code C}:</p>
+ *
+ * <pre>
+ * map[c] U= c.{@link ATNConfig#alt alt} # map hash/equals uses s and x, not
+ * alt and not pred
+ * </pre>
+ *
+ * <p>The values in {@code map} are the set of {@code A_s,ctx} sets.</p>
+ *
+ * <p>If {@code |A_s,ctx|=1} then there is no conflict associated with
+ * {@code s} and {@code ctx}.</p>
+ *
+ * <p>Reduce the subsets to singletons by choosing a minimum of each subset. If
+ * the union of these alternative subsets is a singleton, then no amount of
+ * more lookahead will help us. We will always pick that alternative. If,
+ * however, there is more than one alternative, then we are uncertain which
+ * alternative to predict and must continue looking for resolution. We may
+ * or may not discover an ambiguity in the future, even if there are no
+ * conflicting subsets this round.</p>
+ *
+ * <p>The biggest sin is to terminate early because it means we've made a
+ * decision but were uncertain as to the eventual outcome. We haven't used
+ * enough lookahead. On the other hand, announcing a conflict too late is no
+ * big deal; you will still have the conflict. It's just inefficient. It
+ * might even look until the end of file.</p>
+ *
+ * <p>No special consideration for semantic predicates is required because
+ * predicates are evaluated on-the-fly for full LL prediction, ensuring that
+ * no configuration contains a semantic context during the termination
+ * check.</p>
+ *
+ * <p><strong>CONFLICTING CONFIGS</strong></p>
+ *
+ * <p>Two configurations {@code (s, i, x)} and {@code (s, j, x')}, conflict
+ * when {@code i!=j} but {@code x=x'}. Because we merge all
+ * {@code (s, i, _)} configurations together, that means that there are at
+ * most {@code n} configurations associated with state {@code s} for
+ * {@code n} possible alternatives in the decision. The merged stacks
+ * complicate the comparison of configuration contexts {@code x} and
+ * {@code x'}. Sam checks to see if one is a subset of the other by calling
+ * merge and checking to see if the merged result is either {@code x} or
+ * {@code x'}. If the {@code x} associated with lowest alternative {@code i}
+ * is the superset, then {@code i} is the only possible prediction since the
+ * others resolve to {@code min(i)} as well. However, if {@code x} is
+ * associated with {@code j>i} then at least one stack configuration for
+ * {@code j} is not in conflict with alternative {@code i}. The algorithm
+ * should keep going, looking for more lookahead due to the uncertainty.</p>
+ *
+ * <p>For simplicity, I'm doing a equality check between {@code x} and
+ * {@code x'} that lets the algorithm continue to consume lookahead longer
+ * than necessary. The reason I like the equality is of course the
+ * simplicity but also because that is the test you need to detect the
+ * alternatives that are actually in conflict.</p>
+ *
+ * <p><strong>CONTINUE/STOP RULE</strong></p>
+ *
+ * <p>Continue if union of resolved alternative sets from non-conflicting and
+ * conflicting alternative subsets has more than one alternative. We are
+ * uncertain about which alternative to predict.</p>
+ *
+ * <p>The complete set of alternatives, {@code [i for (_,i,_)]}, tells us which
+ * alternatives are still in the running for the amount of input we've
+ * consumed at this point. The conflicting sets let us to strip away
+ * configurations that won't lead to more states because we resolve
+ * conflicts to the configuration with a minimum alternate for the
+ * conflicting set.</p>
+ *
+ * <p><strong>CASES</strong></p>
+ *
+ * <ul>
+ *
+ * <li>no conflicts and more than 1 alternative in set =&gt; continue</li>
+ *
+ * <li> {@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s, 3, z)},
+ * {@code (s', 1, y)}, {@code (s', 2, y)} yields non-conflicting set
+ * {@code {3}} U conflicting sets {@code min({1,2})} U {@code min({1,2})} =
+ * {@code {1,3}} =&gt; continue
+ * </li>
+ *
+ * <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 1, y)},
+ * {@code (s', 2, y)}, {@code (s'', 1, z)} yields non-conflicting set
+ * {@code {1}} U conflicting sets {@code min({1,2})} U {@code min({1,2})} =
+ * {@code {1}} =&gt; stop and predict 1</li>
+ *
+ * <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 1, y)},
+ * {@code (s', 2, y)} yields conflicting, reduced sets {@code {1}} U
+ * {@code {1}} = {@code {1}} =&gt; stop and predict 1, can announce
+ * ambiguity {@code {1,2}}</li>
+ *
+ * <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 2, y)},
+ * {@code (s', 3, y)} yields conflicting, reduced sets {@code {1}} U
+ * {@code {2}} = {@code {1,2}} =&gt; continue</li>
+ *
+ * <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 3, y)},
+ * {@code (s', 4, y)} yields conflicting, reduced sets {@code {1}} U
+ * {@code {3}} = {@code {1,3}} =&gt; continue</li>
+ *
+ * </ul>
+ *
+ * <p><strong>EXACT AMBIGUITY DETECTION</strong></p>
+ *
+ * <p>If all states report the same conflicting set of alternatives, then we
+ * know we have the exact ambiguity set.</p>
+ *
+ * <p><code>|A_<em>i</em>|&gt;1</code> and
+ * <code>A_<em>i</em> = A_<em>j</em></code> for all <em>i</em>, <em>j</em>.</p>
+ *
+ * <p>In other words, we continue examining lookahead until all {@code A_i}
+ * have more than one alternative and all {@code A_i} are the same. If
+ * {@code A={{1,2}, {1,3}}}, then regular LL prediction would terminate
+ * because the resolved set is {@code {1}}. To determine what the real
+ * ambiguity is, we have to know whether the ambiguity is between one and
+ * two or one and three so we keep going. We can only stop prediction when
+ * we need exact ambiguity detection when the sets look like
+ * {@code A={{1,2}}} or {@code {{1,2},{1,2}}}, etc...</p>
+ */
+ static size_t resolvesToJustOneViableAlt(const std::vector<antlrcpp::BitSet> &altsets);
+
+ /// <summary>
+ /// Determines if every alternative subset in {@code altsets} contains more
+ /// than one alternative.
+ /// </summary>
+ /// <param name="altsets"> a collection of alternative subsets </param>
+ /// <returns> {@code true} if every <seealso cref="BitSet"/> in {@code altsets}
+ /// has
+ /// <seealso cref="BitSet#cardinality cardinality"/> &gt; 1, otherwise {@code
+ /// false} </returns>
+ static bool allSubsetsConflict(const std::vector<antlrcpp::BitSet> &altsets);
+
+ /// <summary>
+ /// Determines if any single alternative subset in {@code altsets} contains
+ /// exactly one alternative.
+ /// </summary>
+ /// <param name="altsets"> a collection of alternative subsets </param>
+ /// <returns> {@code true} if {@code altsets} contains a <seealso
+ /// cref="BitSet"/> with
+ /// <seealso cref="BitSet#cardinality cardinality"/> 1, otherwise {@code false}
+ /// </returns>
+ static bool hasNonConflictingAltSet(const std::vector<antlrcpp::BitSet> &altsets);
+
+ /// <summary>
+ /// Determines if any single alternative subset in {@code altsets} contains
+ /// more than one alternative.
+ /// </summary>
+ /// <param name="altsets"> a collection of alternative subsets </param>
+ /// <returns> {@code true} if {@code altsets} contains a <seealso
+ /// cref="BitSet"/> with
+ /// <seealso cref="BitSet#cardinality cardinality"/> &gt; 1, otherwise {@code
+ /// false} </returns>
+ static bool hasConflictingAltSet(const std::vector<antlrcpp::BitSet> &altsets);
+
+ /// <summary>
+ /// Determines if every alternative subset in {@code altsets} is equivalent.
+ /// </summary>
+ /// <param name="altsets"> a collection of alternative subsets </param>
+ /// <returns> {@code true} if every member of {@code altsets} is equal to the
+ /// others, otherwise {@code false} </returns>
+ static bool allSubsetsEqual(const std::vector<antlrcpp::BitSet> &altsets);
+
+ /// <summary>
+ /// Returns the unique alternative predicted by all alternative subsets in
+ /// {@code altsets}. If no such alternative exists, this method returns
+ /// <seealso cref="ATN#INVALID_ALT_NUMBER"/>.
+ /// </summary>
+ /// <param name="altsets"> a collection of alternative subsets </param>
+ static size_t getUniqueAlt(const std::vector<antlrcpp::BitSet> &altsets);
+
+ /// <summary>
+ /// Gets the complete set of represented alternatives for a collection of
+ /// alternative subsets. This method returns the union of each <seealso
+ /// cref="BitSet"/>
+ /// in {@code altsets}.
+ /// </summary>
+ /// <param name="altsets"> a collection of alternative subsets </param>
+ /// <returns> the set of represented alternatives in {@code altsets} </returns>
+ static antlrcpp::BitSet getAlts(const std::vector<antlrcpp::BitSet> &altsets);
+
+ /** Get union of all alts from configs. @since 4.5.1 */
+ static antlrcpp::BitSet getAlts(ATNConfigSet *configs);
+
+ /// <summary>
+ /// This function gets the conflicting alt subsets from a configuration set.
+ /// For each configuration {@code c} in {@code configs}:
+ ///
+ /// <pre>
+ /// map[c] U= c.<seealso cref="ATNConfig#alt alt"/> # map hash/equals uses s and
+ /// x, not
+ /// alt and not pred
+ /// </pre>
+ /// </summary>
+ static std::vector<antlrcpp::BitSet> getConflictingAltSubsets(ATNConfigSet *configs);
+
+ /// <summary>
+ /// Get a map from state to alt subset from a configuration set. For each
+ /// configuration {@code c} in {@code configs}:
+ ///
+ /// <pre>
+ /// map[c.<seealso cref="ATNConfig#state state"/>] U= c.<seealso
+ /// cref="ATNConfig#alt alt"/>
+ /// </pre>
+ /// </summary>
+ static std::unordered_map<ATNState*, antlrcpp::BitSet> getStateToAltMap(ATNConfigSet *configs);
+
+ static bool hasStateAssociatedWithOneAlt(ATNConfigSet *configs);
+
+ static size_t getSingleViableAlt(const std::vector<antlrcpp::BitSet> &altsets);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ProfilingATNSimulator.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/ProfilingATNSimulator.cpp
new file mode 100644
index 0000000000..9fd86d67d4
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ProfilingATNSimulator.cpp
@@ -0,0 +1,179 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/PredicateEvalInfo.h"
+#include "atn/LookaheadEventInfo.h"
+#include "Parser.h"
+#include "atn/ATNConfigSet.h"
+#include "support/CPPUtils.h"
+
+#include "atn/ProfilingATNSimulator.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlr4::dfa;
+using namespace antlrcpp;
+
+using namespace std::chrono;
+
+ProfilingATNSimulator::ProfilingATNSimulator(Parser *parser)
+ : ParserATNSimulator(parser, parser->getInterpreter<ParserATNSimulator>()->atn,
+ parser->getInterpreter<ParserATNSimulator>()->decisionToDFA,
+ parser->getInterpreter<ParserATNSimulator>()->getSharedContextCache()) {
+ for (size_t i = 0; i < atn.decisionToState.size(); i++) {
+ _decisions.push_back(DecisionInfo(i));
+ }
+}
+
+size_t ProfilingATNSimulator::adaptivePredict(TokenStream *input, size_t decision, ParserRuleContext *outerContext) {
+ auto onExit = finally([this](){
+ _currentDecision = 0; // Originally -1, but that makes no sense (index into a vector and init value is also 0).
+ });
+
+ _sllStopIndex = -1;
+ _llStopIndex = -1;
+ _currentDecision = decision;
+ high_resolution_clock::time_point start = high_resolution_clock::now();
+ size_t alt = ParserATNSimulator::adaptivePredict(input, decision, outerContext);
+ high_resolution_clock::time_point stop = high_resolution_clock::now();
+ _decisions[decision].timeInPrediction += duration_cast<nanoseconds>(stop - start).count();
+ _decisions[decision].invocations++;
+
+ long long SLL_k = _sllStopIndex - _startIndex + 1;
+ _decisions[decision].SLL_TotalLook += SLL_k;
+ _decisions[decision].SLL_MinLook = _decisions[decision].SLL_MinLook == 0 ? SLL_k : std::min(_decisions[decision].SLL_MinLook, SLL_k);
+ if (SLL_k > _decisions[decision].SLL_MaxLook) {
+ _decisions[decision].SLL_MaxLook = SLL_k;
+ _decisions[decision].SLL_MaxLookEvent = std::make_shared<LookaheadEventInfo>(decision, nullptr, alt, input, _startIndex, _sllStopIndex, false);
+ }
+
+ if (_llStopIndex >= 0) {
+ long long LL_k = _llStopIndex - _startIndex + 1;
+ _decisions[decision].LL_TotalLook += LL_k;
+ _decisions[decision].LL_MinLook = _decisions[decision].LL_MinLook == 0 ? LL_k : std::min(_decisions[decision].LL_MinLook, LL_k);
+ if (LL_k > _decisions[decision].LL_MaxLook) {
+ _decisions[decision].LL_MaxLook = LL_k;
+ _decisions[decision].LL_MaxLookEvent = std::make_shared<LookaheadEventInfo>(decision, nullptr, alt, input, _startIndex, _llStopIndex, true);
+ }
+ }
+
+ return alt;
+}
+
+DFAState* ProfilingATNSimulator::getExistingTargetState(DFAState *previousD, size_t t) {
+ // this method is called after each time the input position advances
+ // during SLL prediction
+ _sllStopIndex = (int)_input->index();
+
+ DFAState *existingTargetState = ParserATNSimulator::getExistingTargetState(previousD, t);
+ if (existingTargetState != nullptr) {
+ _decisions[_currentDecision].SLL_DFATransitions++; // count only if we transition over a DFA state
+ if (existingTargetState == ERROR.get()) {
+ _decisions[_currentDecision].errors.push_back(
+ ErrorInfo(_currentDecision, previousD->configs.get(), _input, _startIndex, _sllStopIndex, false)
+ );
+ }
+ }
+
+ _currentState = existingTargetState;
+ return existingTargetState;
+}
+
+DFAState* ProfilingATNSimulator::computeTargetState(DFA &dfa, DFAState *previousD, size_t t) {
+ DFAState *state = ParserATNSimulator::computeTargetState(dfa, previousD, t);
+ _currentState = state;
+ return state;
+}
+
+std::unique_ptr<ATNConfigSet> ProfilingATNSimulator::computeReachSet(ATNConfigSet *closure, size_t t, bool fullCtx) {
+ if (fullCtx) {
+ // this method is called after each time the input position advances
+ // during full context prediction
+ _llStopIndex = (int)_input->index();
+ }
+
+ std::unique_ptr<ATNConfigSet> reachConfigs = ParserATNSimulator::computeReachSet(closure, t, fullCtx);
+ if (fullCtx) {
+ _decisions[_currentDecision].LL_ATNTransitions++; // count computation even if error
+ if (reachConfigs != nullptr) {
+ } else { // no reach on current lookahead symbol. ERROR.
+ // TODO: does not handle delayed errors per getSynValidOrSemInvalidAltThatFinishedDecisionEntryRule()
+ _decisions[_currentDecision].errors.push_back(ErrorInfo(_currentDecision, closure, _input, _startIndex, _llStopIndex, true));
+ }
+ } else {
+ ++_decisions[_currentDecision].SLL_ATNTransitions;
+ if (reachConfigs != nullptr) {
+ } else { // no reach on current lookahead symbol. ERROR.
+ _decisions[_currentDecision].errors.push_back(ErrorInfo(_currentDecision, closure, _input, _startIndex, _sllStopIndex, false));
+ }
+ }
+ return reachConfigs;
+}
+
+bool ProfilingATNSimulator::evalSemanticContext(Ref<const SemanticContext> const& pred, ParserRuleContext *parserCallStack,
+ size_t alt, bool fullCtx) {
+ bool result = ParserATNSimulator::evalSemanticContext(pred, parserCallStack, alt, fullCtx);
+ if (!(std::dynamic_pointer_cast<const SemanticContext::PrecedencePredicate>(pred) != nullptr)) {
+ bool fullContext = _llStopIndex >= 0;
+ int stopIndex = fullContext ? _llStopIndex : _sllStopIndex;
+ _decisions[_currentDecision].predicateEvals.push_back(
+ PredicateEvalInfo(_currentDecision, _input, _startIndex, stopIndex, pred, result, alt, fullCtx));
+ }
+
+ return result;
+}
+
+void ProfilingATNSimulator::reportAttemptingFullContext(DFA &dfa, const BitSet &conflictingAlts, ATNConfigSet *configs,
+ size_t startIndex, size_t stopIndex) {
+ if (conflictingAlts.count() > 0) {
+ conflictingAltResolvedBySLL = conflictingAlts.nextSetBit(0);
+ } else {
+ conflictingAltResolvedBySLL = configs->getAlts().nextSetBit(0);
+ }
+ _decisions[_currentDecision].LL_Fallback++;
+ ParserATNSimulator::reportAttemptingFullContext(dfa, conflictingAlts, configs, startIndex, stopIndex);
+}
+
+void ProfilingATNSimulator::reportContextSensitivity(DFA &dfa, size_t prediction, ATNConfigSet *configs,
+ size_t startIndex, size_t stopIndex) {
+ if (prediction != conflictingAltResolvedBySLL) {
+ _decisions[_currentDecision].contextSensitivities.push_back(
+ ContextSensitivityInfo(_currentDecision, configs, _input, startIndex, stopIndex)
+ );
+ }
+ ParserATNSimulator::reportContextSensitivity(dfa, prediction, configs, startIndex, stopIndex);
+}
+
+void ProfilingATNSimulator::reportAmbiguity(DFA &dfa, DFAState *D, size_t startIndex, size_t stopIndex, bool exact,
+ const BitSet &ambigAlts, ATNConfigSet *configs) {
+ size_t prediction;
+ if (ambigAlts.count() > 0) {
+ prediction = ambigAlts.nextSetBit(0);
+ } else {
+ prediction = configs->getAlts().nextSetBit(0);
+ }
+ if (configs->fullCtx && prediction != conflictingAltResolvedBySLL) {
+ // Even though this is an ambiguity we are reporting, we can
+ // still detect some context sensitivities. Both SLL and LL
+ // are showing a conflict, hence an ambiguity, but if they resolve
+ // to different minimum alternatives we have also identified a
+ // context sensitivity.
+ _decisions[_currentDecision].contextSensitivities.push_back(
+ ContextSensitivityInfo(_currentDecision, configs, _input, startIndex, stopIndex)
+ );
+ }
+ _decisions[_currentDecision].ambiguities.push_back(
+ AmbiguityInfo(_currentDecision, configs, ambigAlts, _input, startIndex, stopIndex, configs->fullCtx)
+ );
+ ParserATNSimulator::reportAmbiguity(dfa, D, startIndex, stopIndex, exact, ambigAlts, configs);
+}
+
+std::vector<DecisionInfo> ProfilingATNSimulator::getDecisionInfo() const {
+ return _decisions;
+}
+
+DFAState* ProfilingATNSimulator::getCurrentState() const {
+ return _currentState;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/ProfilingATNSimulator.h b/contrib/libs/antlr4_cpp_runtime/src/atn/ProfilingATNSimulator.h
new file mode 100644
index 0000000000..551efb8556
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/ProfilingATNSimulator.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ParserATNSimulator.h"
+#include "atn/DecisionInfo.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC ProfilingATNSimulator : public ParserATNSimulator {
+ public:
+ explicit ProfilingATNSimulator(Parser *parser);
+
+ virtual size_t adaptivePredict(TokenStream *input, size_t decision, ParserRuleContext *outerContext) override;
+
+ virtual std::vector<DecisionInfo> getDecisionInfo() const;
+ virtual dfa::DFAState* getCurrentState() const;
+
+ protected:
+ std::vector<DecisionInfo> _decisions;
+
+ int _sllStopIndex = 0;
+ int _llStopIndex = 0;
+
+ size_t _currentDecision = 0;
+ dfa::DFAState *_currentState;
+
+ /// <summary>
+ /// At the point of LL failover, we record how SLL would resolve the conflict so that
+ /// we can determine whether or not a decision / input pair is context-sensitive.
+ /// If LL gives a different result than SLL's predicted alternative, we have a
+ /// context sensitivity for sure. The converse is not necessarily true, however.
+ /// It's possible that after conflict resolution chooses minimum alternatives,
+ /// SLL could get the same answer as LL. Regardless of whether or not the result indicates
+ /// an ambiguity, it is not treated as a context sensitivity because LL prediction
+ /// was not required in order to produce a correct prediction for this decision and input sequence.
+ /// It may in fact still be a context sensitivity but we don't know by looking at the
+ /// minimum alternatives for the current input.
+ /// </summary>
+ size_t conflictingAltResolvedBySLL = 0;
+
+ virtual dfa::DFAState* getExistingTargetState(dfa::DFAState *previousD, size_t t) override;
+ virtual dfa::DFAState* computeTargetState(dfa::DFA &dfa, dfa::DFAState *previousD, size_t t) override;
+ virtual std::unique_ptr<ATNConfigSet> computeReachSet(ATNConfigSet *closure, size_t t, bool fullCtx) override;
+ virtual bool evalSemanticContext(Ref<const SemanticContext> const& pred, ParserRuleContext *parserCallStack,
+ size_t alt, bool fullCtx) override;
+ virtual void reportAttemptingFullContext(dfa::DFA &dfa, const antlrcpp::BitSet &conflictingAlts, ATNConfigSet *configs,
+ size_t startIndex, size_t stopIndex) override;
+ virtual void reportContextSensitivity(dfa::DFA &dfa, size_t prediction, ATNConfigSet *configs,
+ size_t startIndex, size_t stopIndex) override;
+ virtual void reportAmbiguity(dfa::DFA &dfa, dfa::DFAState *D, size_t startIndex, size_t stopIndex, bool exact,
+ const antlrcpp::BitSet &ambigAlts, ATNConfigSet *configs) override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/RangeTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/RangeTransition.cpp
new file mode 100644
index 0000000000..342e550de9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/RangeTransition.cpp
@@ -0,0 +1,26 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/IntervalSet.h"
+
+#include "atn/RangeTransition.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+RangeTransition::RangeTransition(ATNState *target, size_t from, size_t to) : Transition(TransitionType::RANGE, target), from(from), to(to) {
+}
+
+misc::IntervalSet RangeTransition::label() const {
+ return misc::IntervalSet::of((int)from, (int)to);
+}
+
+bool RangeTransition::matches(size_t symbol, size_t /*minVocabSymbol*/, size_t /*maxVocabSymbol*/) const {
+ return symbol >= from && symbol <= to;
+}
+
+std::string RangeTransition::toString() const {
+ return "RANGE " + Transition::toString() + " { from: " + std::to_string(from) + ", to: " + std::to_string(to) + " }";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/RangeTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/RangeTransition.h
new file mode 100644
index 0000000000..b75c60e247
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/RangeTransition.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC RangeTransition final : public Transition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::RANGE; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ const size_t from;
+ const size_t to;
+
+ RangeTransition(ATNState *target, size_t from, size_t to);
+
+ virtual misc::IntervalSet label() const override;
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+
+ virtual std::string toString() const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/RuleStartState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/RuleStartState.h
new file mode 100644
index 0000000000..549491514b
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/RuleStartState.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC RuleStartState final : public ATNState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::RULE_START; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ RuleStopState *stopState = nullptr;
+ bool isLeftRecursiveRule = false;
+
+ RuleStartState() : ATNState(ATNStateType::RULE_START) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/RuleStopState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/RuleStopState.h
new file mode 100644
index 0000000000..7792a1265c
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/RuleStopState.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// The last node in the ATN for a rule, unless that rule is the start symbol.
+ /// In that case, there is one transition to EOF. Later, we might encode
+ /// references to all calls to this rule to compute FOLLOW sets for
+ /// error handling.
+ class ANTLR4CPP_PUBLIC RuleStopState final : public ATNState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::RULE_STOP; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ RuleStopState() : ATNState(ATNStateType::RULE_STOP) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/RuleTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/RuleTransition.cpp
new file mode 100644
index 0000000000..ba50dd03dd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/RuleTransition.cpp
@@ -0,0 +1,33 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/RuleStartState.h"
+#include "atn/RuleTransition.h"
+
+using namespace antlr4::atn;
+
+RuleTransition::RuleTransition(RuleStartState *ruleStart, size_t ruleIndex, ATNState *followState)
+ : RuleTransition(ruleStart, ruleIndex, 0, followState) {
+}
+
+RuleTransition::RuleTransition(RuleStartState *ruleStart, size_t ruleIndex, int precedence, ATNState *followState)
+ : Transition(TransitionType::RULE, ruleStart), ruleIndex(ruleIndex), precedence(precedence) {
+ this->followState = followState;
+}
+
+bool RuleTransition::isEpsilon() const {
+ return true;
+}
+
+bool RuleTransition::matches(size_t /*symbol*/, size_t /*minVocabSymbol*/, size_t /*maxVocabSymbol*/) const {
+ return false;
+}
+
+std::string RuleTransition::toString() const {
+ std::stringstream ss;
+ ss << "RULE " << Transition::toString() << " { ruleIndex: " << ruleIndex << ", precedence: " << precedence <<
+ ", followState: " << std::hex << followState << " }";
+ return ss.str();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/RuleTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/RuleTransition.h
new file mode 100644
index 0000000000..396ef700f2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/RuleTransition.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC RuleTransition final : public Transition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::RULE; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ /// Ptr to the rule definition object for this rule ref.
+ const size_t ruleIndex; // no Rule object at runtime
+
+ const int precedence;
+
+ /// What node to begin computations following ref to rule.
+ ATNState *followState;
+
+ /// @deprecated Use
+ /// <seealso cref="#RuleTransition(RuleStartState, size_t, int, ATNState)"/> instead.
+ RuleTransition(RuleStartState *ruleStart, size_t ruleIndex, ATNState *followState);
+
+ RuleTransition(RuleStartState *ruleStart, size_t ruleIndex, int precedence, ATNState *followState);
+ RuleTransition(RuleTransition const&) = delete;
+ RuleTransition& operator=(RuleTransition const&) = delete;
+
+ virtual bool isEpsilon() const override;
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+
+ virtual std::string toString() const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContext.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContext.cpp
new file mode 100644
index 0000000000..7d7fe068df
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContext.cpp
@@ -0,0 +1,418 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include <functional>
+#include <unordered_set>
+
+#include "misc/MurmurHash.h"
+#include "support/Casts.h"
+#include "support/CPPUtils.h"
+#include "support/Arrays.h"
+
+#include "SemanticContext.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+namespace {
+
+ struct SemanticContextHasher final {
+ size_t operator()(const SemanticContext *semanticContext) const {
+ return semanticContext->hashCode();
+ }
+ };
+
+ struct SemanticContextComparer final {
+ bool operator()(const SemanticContext *lhs, const SemanticContext *rhs) const {
+ return *lhs == *rhs;
+ }
+ };
+
+ template <typename Comparer>
+ void insertSemanticContext(const Ref<const SemanticContext> &semanticContext,
+ std::unordered_set<const SemanticContext*, SemanticContextHasher, SemanticContextComparer> &operandSet,
+ std::vector<Ref<const SemanticContext>> &operandList,
+ Ref<const SemanticContext::PrecedencePredicate> &precedencePredicate,
+ Comparer comparer) {
+ if (semanticContext != nullptr) {
+ if (semanticContext->getContextType() == SemanticContextType::PRECEDENCE) {
+ if (precedencePredicate == nullptr || comparer(downCast<const SemanticContext::PrecedencePredicate*>(semanticContext.get())->precedence, precedencePredicate->precedence)) {
+ precedencePredicate = std::static_pointer_cast<const SemanticContext::PrecedencePredicate>(semanticContext);
+ }
+ } else {
+ auto [existing, inserted] = operandSet.insert(semanticContext.get());
+ if (inserted) {
+ operandList.push_back(semanticContext);
+ }
+ }
+ }
+ }
+
+ template <typename Comparer>
+ void insertSemanticContext(Ref<const SemanticContext> &&semanticContext,
+ std::unordered_set<const SemanticContext*, SemanticContextHasher, SemanticContextComparer> &operandSet,
+ std::vector<Ref<const SemanticContext>> &operandList,
+ Ref<const SemanticContext::PrecedencePredicate> &precedencePredicate,
+ Comparer comparer) {
+ if (semanticContext != nullptr) {
+ if (semanticContext->getContextType() == SemanticContextType::PRECEDENCE) {
+ if (precedencePredicate == nullptr || comparer(downCast<const SemanticContext::PrecedencePredicate*>(semanticContext.get())->precedence, precedencePredicate->precedence)) {
+ precedencePredicate = std::static_pointer_cast<const SemanticContext::PrecedencePredicate>(std::move(semanticContext));
+ }
+ } else {
+ auto [existing, inserted] = operandSet.insert(semanticContext.get());
+ if (inserted) {
+ operandList.push_back(std::move(semanticContext));
+ }
+ }
+ }
+ }
+
+ size_t predictOperandCapacity(const Ref<const SemanticContext> &x) {
+ switch (x->getContextType()) {
+ case SemanticContextType::AND:
+ return downCast<const SemanticContext::AND&>(*x).getOperands().size();
+ case SemanticContextType::OR:
+ return downCast<const SemanticContext::OR&>(*x).getOperands().size();
+ default:
+ return 1;
+ }
+ }
+
+ size_t predictOperandCapacity(const Ref<const SemanticContext> &a, const Ref<const SemanticContext> &b) {
+ return predictOperandCapacity(a) + predictOperandCapacity(b);
+ }
+
+}
+
+//------------------ Predicate -----------------------------------------------------------------------------------------
+
+SemanticContext::Predicate::Predicate(size_t ruleIndex, size_t predIndex, bool isCtxDependent)
+ : SemanticContext(SemanticContextType::PREDICATE), ruleIndex(ruleIndex), predIndex(predIndex), isCtxDependent(isCtxDependent) {}
+
+bool SemanticContext::Predicate::eval(Recognizer *parser, RuleContext *parserCallStack) const {
+ RuleContext *localctx = nullptr;
+ if (isCtxDependent) {
+ localctx = parserCallStack;
+ }
+ return parser->sempred(localctx, ruleIndex, predIndex);
+}
+
+size_t SemanticContext::Predicate::hashCode() const {
+ size_t hashCode = misc::MurmurHash::initialize();
+ hashCode = misc::MurmurHash::update(hashCode, static_cast<size_t>(getContextType()));
+ hashCode = misc::MurmurHash::update(hashCode, ruleIndex);
+ hashCode = misc::MurmurHash::update(hashCode, predIndex);
+ hashCode = misc::MurmurHash::update(hashCode, isCtxDependent ? 1 : 0);
+ hashCode = misc::MurmurHash::finish(hashCode, 4);
+ return hashCode;
+}
+
+bool SemanticContext::Predicate::equals(const SemanticContext &other) const {
+ if (this == &other) {
+ return true;
+ }
+ if (getContextType() != other.getContextType()) {
+ return false;
+ }
+ const Predicate &p = downCast<const Predicate&>(other);
+ return ruleIndex == p.ruleIndex && predIndex == p.predIndex && isCtxDependent == p.isCtxDependent;
+}
+
+std::string SemanticContext::Predicate::toString() const {
+ return std::string("{") + std::to_string(ruleIndex) + std::string(":") + std::to_string(predIndex) + std::string("}?");
+}
+
+//------------------ PrecedencePredicate -------------------------------------------------------------------------------
+
+SemanticContext::PrecedencePredicate::PrecedencePredicate(int precedence) : SemanticContext(SemanticContextType::PRECEDENCE), precedence(precedence) {}
+
+bool SemanticContext::PrecedencePredicate::eval(Recognizer *parser, RuleContext *parserCallStack) const {
+ return parser->precpred(parserCallStack, precedence);
+}
+
+Ref<const SemanticContext> SemanticContext::PrecedencePredicate::evalPrecedence(Recognizer *parser,
+ RuleContext *parserCallStack) const {
+ if (parser->precpred(parserCallStack, precedence)) {
+ return SemanticContext::Empty::Instance;
+ }
+ return nullptr;
+}
+
+size_t SemanticContext::PrecedencePredicate::hashCode() const {
+ size_t hashCode = misc::MurmurHash::initialize();
+ hashCode = misc::MurmurHash::update(hashCode, static_cast<size_t>(getContextType()));
+ hashCode = misc::MurmurHash::update(hashCode, static_cast<size_t>(precedence));
+ return misc::MurmurHash::finish(hashCode, 2);
+}
+
+bool SemanticContext::PrecedencePredicate::equals(const SemanticContext &other) const {
+ if (this == &other) {
+ return true;
+ }
+ if (getContextType() != other.getContextType()) {
+ return false;
+ }
+ const PrecedencePredicate &predicate = downCast<const PrecedencePredicate&>(other);
+ return precedence == predicate.precedence;
+}
+
+std::string SemanticContext::PrecedencePredicate::toString() const {
+ return "{" + std::to_string(precedence) + ">=prec}?";
+}
+
+//------------------ AND -----------------------------------------------------------------------------------------------
+
+SemanticContext::AND::AND(Ref<const SemanticContext> a, Ref<const SemanticContext> b) : Operator(SemanticContextType::AND) {
+ std::unordered_set<const SemanticContext*, SemanticContextHasher, SemanticContextComparer> operands;
+ Ref<const SemanticContext::PrecedencePredicate> precedencePredicate;
+
+ _opnds.reserve(predictOperandCapacity(a, b) + 1);
+
+ if (a->getContextType() == SemanticContextType::AND) {
+ for (const auto &operand : downCast<const AND*>(a.get())->getOperands()) {
+ insertSemanticContext(operand, operands, _opnds, precedencePredicate, std::less<int>{});
+ }
+ } else {
+ insertSemanticContext(std::move(a), operands, _opnds, precedencePredicate, std::less<int>{});
+ }
+
+ if (b->getContextType() == SemanticContextType::AND) {
+ for (const auto &operand : downCast<const AND*>(b.get())->getOperands()) {
+ insertSemanticContext(operand, operands, _opnds, precedencePredicate, std::less<int>{});
+ }
+ } else {
+ insertSemanticContext(std::move(b), operands, _opnds, precedencePredicate, std::less<int>{});
+ }
+
+ if (precedencePredicate != nullptr) {
+ // interested in the transition with the lowest precedence
+ auto [existing, inserted] = operands.insert(precedencePredicate.get());
+ if (inserted) {
+ _opnds.push_back(std::move(precedencePredicate));
+ }
+ }
+}
+
+const std::vector<Ref<const SemanticContext>>& SemanticContext::AND::getOperands() const {
+ return _opnds;
+}
+
+bool SemanticContext::AND::equals(const SemanticContext &other) const {
+ if (this == &other) {
+ return true;
+ }
+ if (getContextType() != other.getContextType()) {
+ return false;
+ }
+ const AND &context = downCast<const AND&>(other);
+ return Arrays::equals(getOperands(), context.getOperands());
+}
+
+size_t SemanticContext::AND::hashCode() const {
+ size_t hash = misc::MurmurHash::initialize();
+ hash = misc::MurmurHash::update(hash, static_cast<size_t>(getContextType()));
+ return misc::MurmurHash::hashCode(getOperands(), hash);
+}
+
+bool SemanticContext::AND::eval(Recognizer *parser, RuleContext *parserCallStack) const {
+ for (const auto &opnd : getOperands()) {
+ if (!opnd->eval(parser, parserCallStack)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+Ref<const SemanticContext> SemanticContext::AND::evalPrecedence(Recognizer *parser, RuleContext *parserCallStack) const {
+ bool differs = false;
+ std::vector<Ref<const SemanticContext>> operands;
+ for (const auto &context : getOperands()) {
+ auto evaluated = context->evalPrecedence(parser, parserCallStack);
+ differs |= (evaluated != context);
+ if (evaluated == nullptr) {
+ // The AND context is false if any element is false.
+ return nullptr;
+ }
+ if (evaluated != Empty::Instance) {
+ // Reduce the result by skipping true elements.
+ operands.push_back(std::move(evaluated));
+ }
+ }
+
+ if (!differs) {
+ return shared_from_this();
+ }
+
+ if (operands.empty()) {
+ // All elements were true, so the AND context is true.
+ return Empty::Instance;
+ }
+
+ Ref<const SemanticContext> result = std::move(operands[0]);
+ for (size_t i = 1; i < operands.size(); ++i) {
+ result = SemanticContext::And(std::move(result), std::move(operands[i]));
+ }
+
+ return result;
+}
+
+std::string SemanticContext::AND::toString() const {
+ std::string tmp;
+ for (const auto &var : getOperands()) {
+ tmp += var->toString() + " && ";
+ }
+ return tmp;
+}
+
+//------------------ OR ------------------------------------------------------------------------------------------------
+
+SemanticContext::OR::OR(Ref<const SemanticContext> a, Ref<const SemanticContext> b) : Operator(SemanticContextType::OR) {
+ std::unordered_set<const SemanticContext*, SemanticContextHasher, SemanticContextComparer> operands;
+ Ref<const SemanticContext::PrecedencePredicate> precedencePredicate;
+
+ _opnds.reserve(predictOperandCapacity(a, b) + 1);
+
+ if (a->getContextType() == SemanticContextType::OR) {
+ for (const auto &operand : downCast<const OR*>(a.get())->getOperands()) {
+ insertSemanticContext(operand, operands, _opnds, precedencePredicate, std::greater<int>{});
+ }
+ } else {
+ insertSemanticContext(std::move(a), operands, _opnds, precedencePredicate, std::greater<int>{});
+ }
+
+ if (b->getContextType() == SemanticContextType::OR) {
+ for (const auto &operand : downCast<const OR*>(b.get())->getOperands()) {
+ insertSemanticContext(operand, operands, _opnds, precedencePredicate, std::greater<int>{});
+ }
+ } else {
+ insertSemanticContext(std::move(b), operands, _opnds, precedencePredicate, std::greater<int>{});
+ }
+
+ if (precedencePredicate != nullptr) {
+ // interested in the transition with the highest precedence
+ auto [existing, inserted] = operands.insert(precedencePredicate.get());
+ if (inserted) {
+ _opnds.push_back(std::move(precedencePredicate));
+ }
+ }
+}
+
+const std::vector<Ref<const SemanticContext>>& SemanticContext::OR::getOperands() const {
+ return _opnds;
+}
+
+bool SemanticContext::OR::equals(const SemanticContext &other) const {
+ if (this == &other) {
+ return true;
+ }
+ if (getContextType() != other.getContextType()) {
+ return false;
+ }
+ const OR &context = downCast<const OR&>(other);
+ return Arrays::equals(getOperands(), context.getOperands());
+}
+
+size_t SemanticContext::OR::hashCode() const {
+ size_t hash = misc::MurmurHash::initialize();
+ hash = misc::MurmurHash::update(hash, static_cast<size_t>(getContextType()));
+ return misc::MurmurHash::hashCode(getOperands(), hash);
+}
+
+bool SemanticContext::OR::eval(Recognizer *parser, RuleContext *parserCallStack) const {
+ for (const auto &opnd : getOperands()) {
+ if (opnd->eval(parser, parserCallStack)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+Ref<const SemanticContext> SemanticContext::OR::evalPrecedence(Recognizer *parser, RuleContext *parserCallStack) const {
+ bool differs = false;
+ std::vector<Ref<const SemanticContext>> operands;
+ for (const auto &context : getOperands()) {
+ auto evaluated = context->evalPrecedence(parser, parserCallStack);
+ differs |= (evaluated != context);
+ if (evaluated == Empty::Instance) {
+ // The OR context is true if any element is true.
+ return Empty::Instance;
+ }
+ if (evaluated != nullptr) {
+ // Reduce the result by skipping false elements.
+ operands.push_back(std::move(evaluated));
+ }
+ }
+
+ if (!differs) {
+ return shared_from_this();
+ }
+
+ if (operands.empty()) {
+ // All elements were false, so the OR context is false.
+ return nullptr;
+ }
+
+ Ref<const SemanticContext> result = std::move(operands[0]);
+ for (size_t i = 1; i < operands.size(); ++i) {
+ result = SemanticContext::Or(std::move(result), std::move(operands[i]));
+ }
+
+ return result;
+}
+
+std::string SemanticContext::OR::toString() const {
+ std::string tmp;
+ for(const auto &var : getOperands()) {
+ tmp += var->toString() + " || ";
+ }
+ return tmp;
+}
+
+//------------------ SemanticContext -----------------------------------------------------------------------------------
+
+const Ref<const SemanticContext> SemanticContext::Empty::Instance = std::make_shared<Predicate>(INVALID_INDEX, INVALID_INDEX, false);
+
+Ref<const SemanticContext> SemanticContext::evalPrecedence(Recognizer * /*parser*/, RuleContext * /*parserCallStack*/) const {
+ return shared_from_this();
+}
+
+Ref<const SemanticContext> SemanticContext::And(Ref<const SemanticContext> a, Ref<const SemanticContext> b) {
+ if (!a || a == Empty::Instance) {
+ return b;
+ }
+
+ if (!b || b == Empty::Instance) {
+ return a;
+ }
+
+ Ref<AND> result = std::make_shared<AND>(std::move(a), std::move(b));
+ if (result->getOperands().size() == 1) {
+ return result->getOperands()[0];
+ }
+
+ return result;
+}
+
+Ref<const SemanticContext> SemanticContext::Or(Ref<const SemanticContext> a, Ref<const SemanticContext> b) {
+ if (!a) {
+ return b;
+ }
+ if (!b) {
+ return a;
+ }
+
+ if (a == Empty::Instance || b == Empty::Instance) {
+ return Empty::Instance;
+ }
+
+ Ref<OR> result = std::make_shared<OR>(std::move(a), std::move(b));
+ if (result->getOperands().size() == 1) {
+ return result->getOperands()[0];
+ }
+
+ return result;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContext.h b/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContext.h
new file mode 100644
index 0000000000..8116fc0b56
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContext.h
@@ -0,0 +1,237 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Recognizer.h"
+#include "support/CPPUtils.h"
+#include "atn/SemanticContextType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// A tree structure used to record the semantic context in which
+ /// an ATN configuration is valid. It's either a single predicate,
+ /// a conjunction "p1 && p2", or a sum of products "p1||p2".
+ ///
+ /// I have scoped the AND, OR, and Predicate subclasses of
+ /// SemanticContext within the scope of this outer class.
+ class ANTLR4CPP_PUBLIC SemanticContext : public std::enable_shared_from_this<SemanticContext> {
+ public:
+ virtual ~SemanticContext() = default;
+
+ SemanticContextType getContextType() const { return _contextType; }
+
+ /// <summary>
+ /// For context independent predicates, we evaluate them without a local
+ /// context (i.e., null context). That way, we can evaluate them without
+ /// having to create proper rule-specific context during prediction (as
+ /// opposed to the parser, which creates them naturally). In a practical
+ /// sense, this avoids a cast exception from RuleContext to myruleContext.
+ /// <p/>
+ /// For context dependent predicates, we must pass in a local context so that
+ /// references such as $arg evaluate properly as _localctx.arg. We only
+ /// capture context dependent predicates in the context in which we begin
+ /// prediction, so we passed in the outer context here in case of context
+ /// dependent predicate evaluation.
+ /// </summary>
+ virtual bool eval(Recognizer *parser, RuleContext *parserCallStack) const = 0;
+
+ /**
+ * Evaluate the precedence predicates for the context and reduce the result.
+ *
+ * @param parser The parser instance.
+ * @param parserCallStack
+ * @return The simplified semantic context after precedence predicates are
+ * evaluated, which will be one of the following values.
+ * <ul>
+ * <li>{@link #NONE}: if the predicate simplifies to {@code true} after
+ * precedence predicates are evaluated.</li>
+ * <li>{@code null}: if the predicate simplifies to {@code false} after
+ * precedence predicates are evaluated.</li>
+ * <li>{@code this}: if the semantic context is not changed as a result of
+ * precedence predicate evaluation.</li>
+ * <li>A non-{@code null} {@link SemanticContext}: the new simplified
+ * semantic context after precedence predicates are evaluated.</li>
+ * </ul>
+ */
+ virtual Ref<const SemanticContext> evalPrecedence(Recognizer *parser, RuleContext *parserCallStack) const;
+
+ virtual size_t hashCode() const = 0;
+
+ virtual bool equals(const SemanticContext &other) const = 0;
+
+ virtual std::string toString() const = 0;
+
+ static Ref<const SemanticContext> And(Ref<const SemanticContext> a, Ref<const SemanticContext> b);
+
+ /// See also: ParserATNSimulator::getPredsForAmbigAlts.
+ static Ref<const SemanticContext> Or(Ref<const SemanticContext> a, Ref<const SemanticContext> b);
+
+ class Empty;
+ class Predicate;
+ class PrecedencePredicate;
+ class Operator;
+ class AND;
+ class OR;
+
+ protected:
+ explicit SemanticContext(SemanticContextType contextType) : _contextType(contextType) {}
+
+ private:
+ const SemanticContextType _contextType;
+ };
+
+ inline bool operator==(const SemanticContext &lhs, const SemanticContext &rhs) {
+ return lhs.equals(rhs);
+ }
+
+ inline bool operator!=(const SemanticContext &lhs, const SemanticContext &rhs) {
+ return !operator==(lhs, rhs);
+ }
+
+ class ANTLR4CPP_PUBLIC SemanticContext::Empty : public SemanticContext{
+ public:
+ /**
+ * The default {@link SemanticContext}, which is semantically equivalent to
+ * a predicate of the form {@code {true}?}.
+ */
+ static const Ref<const SemanticContext> Instance;
+ };
+
+ class ANTLR4CPP_PUBLIC SemanticContext::Predicate final : public SemanticContext {
+ public:
+ static bool is(const SemanticContext &semanticContext) { return semanticContext.getContextType() == SemanticContextType::PREDICATE; }
+
+ static bool is(const SemanticContext *semanticContext) { return semanticContext != nullptr && is(*semanticContext); }
+
+ const size_t ruleIndex;
+ const size_t predIndex;
+ const bool isCtxDependent; // e.g., $i ref in pred
+
+ Predicate(size_t ruleIndex, size_t predIndex, bool isCtxDependent);
+
+ bool eval(Recognizer *parser, RuleContext *parserCallStack) const override;
+ size_t hashCode() const override;
+ bool equals(const SemanticContext &other) const override;
+ std::string toString() const override;
+ };
+
+ class ANTLR4CPP_PUBLIC SemanticContext::PrecedencePredicate final : public SemanticContext {
+ public:
+ static bool is(const SemanticContext &semanticContext) { return semanticContext.getContextType() == SemanticContextType::PRECEDENCE; }
+
+ static bool is(const SemanticContext *semanticContext) { return semanticContext != nullptr && is(*semanticContext); }
+
+ const int precedence;
+
+ explicit PrecedencePredicate(int precedence);
+
+ bool eval(Recognizer *parser, RuleContext *parserCallStack) const override;
+ Ref<const SemanticContext> evalPrecedence(Recognizer *parser, RuleContext *parserCallStack) const override;
+ size_t hashCode() const override;
+ bool equals(const SemanticContext &other) const override;
+ std::string toString() const override;
+ };
+
+ /**
+ * This is the base class for semantic context "operators", which operate on
+ * a collection of semantic context "operands".
+ *
+ * @since 4.3
+ */
+ class ANTLR4CPP_PUBLIC SemanticContext::Operator : public SemanticContext {
+ public:
+ static bool is(const SemanticContext &semanticContext) {
+ const auto contextType = semanticContext.getContextType();
+ return contextType == SemanticContextType::AND || contextType == SemanticContextType::OR;
+ }
+
+ static bool is(const SemanticContext *semanticContext) { return semanticContext != nullptr && is(*semanticContext); }
+
+ /**
+ * Gets the operands for the semantic context operator.
+ *
+ * @return a collection of {@link SemanticContext} operands for the
+ * operator.
+ *
+ * @since 4.3
+ */
+
+ virtual const std::vector<Ref<const SemanticContext>>& getOperands() const = 0;
+
+ protected:
+ using SemanticContext::SemanticContext;
+ };
+
+ /**
+ * A semantic context which is true whenever none of the contained contexts
+ * is false.
+ */
+ class ANTLR4CPP_PUBLIC SemanticContext::AND final : public SemanticContext::Operator {
+ public:
+ static bool is(const SemanticContext &semanticContext) { return semanticContext.getContextType() == SemanticContextType::AND; }
+
+ static bool is(const SemanticContext *semanticContext) { return semanticContext != nullptr && is(*semanticContext); }
+
+ AND(Ref<const SemanticContext> a, Ref<const SemanticContext> b) ;
+
+ const std::vector<Ref<const SemanticContext>>& getOperands() const override;
+
+ /**
+ * The evaluation of predicates by this context is short-circuiting, but
+ * unordered.</p>
+ */
+ bool eval(Recognizer *parser, RuleContext *parserCallStack) const override;
+ Ref<const SemanticContext> evalPrecedence(Recognizer *parser, RuleContext *parserCallStack) const override;
+ size_t hashCode() const override;
+ bool equals(const SemanticContext &other) const override;
+ std::string toString() const override;
+
+ private:
+ std::vector<Ref<const SemanticContext>> _opnds;
+ };
+
+ /**
+ * A semantic context which is true whenever at least one of the contained
+ * contexts is true.
+ */
+ class ANTLR4CPP_PUBLIC SemanticContext::OR final : public SemanticContext::Operator {
+ public:
+ static bool is(const SemanticContext &semanticContext) { return semanticContext.getContextType() == SemanticContextType::OR; }
+
+ static bool is(const SemanticContext *semanticContext) { return semanticContext != nullptr && is(*semanticContext); }
+
+ OR(Ref<const SemanticContext> a, Ref<const SemanticContext> b);
+
+ const std::vector<Ref<const SemanticContext>>& getOperands() const override;
+
+ /**
+ * The evaluation of predicates by this context is short-circuiting, but
+ * unordered.
+ */
+ bool eval(Recognizer *parser, RuleContext *parserCallStack) const override;
+ Ref<const SemanticContext> evalPrecedence(Recognizer *parser, RuleContext *parserCallStack) const override;
+ size_t hashCode() const override;
+ bool equals(const SemanticContext &other) const override;
+ std::string toString() const override;
+
+ private:
+ std::vector<Ref<const SemanticContext>> _opnds;
+ };
+
+} // namespace atn
+} // namespace antlr4
+
+namespace std {
+
+ template <>
+ struct hash<::antlr4::atn::SemanticContext> {
+ size_t operator()(const ::antlr4::atn::SemanticContext &semanticContext) const {
+ return semanticContext.hashCode();
+ }
+ };
+
+} // namespace std
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContextType.h b/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContextType.h
new file mode 100644
index 0000000000..bca6e421d2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/SemanticContextType.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ enum class SemanticContextType : size_t {
+ PREDICATE = 1,
+ PRECEDENCE = 2,
+ AND = 3,
+ OR = 4,
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/SerializedATNView.h b/contrib/libs/antlr4_cpp_runtime/src/atn/SerializedATNView.h
new file mode 100644
index 0000000000..a723589bc3
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/SerializedATNView.h
@@ -0,0 +1,101 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <vector>
+
+#include "antlr4-common.h"
+#include "misc/MurmurHash.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC SerializedATNView final {
+ public:
+ using value_type = int32_t;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+ using reference = int32_t&;
+ using const_reference = const int32_t&;
+ using pointer = int32_t*;
+ using const_pointer = const int32_t*;
+ using iterator = const_pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ SerializedATNView() = default;
+
+ SerializedATNView(const_pointer data, size_type size) : _data(data), _size(size) {}
+
+ SerializedATNView(const std::vector<int32_t> &serializedATN) : _data(serializedATN.data()), _size(serializedATN.size()) {}
+
+ SerializedATNView(const SerializedATNView&) = default;
+
+ SerializedATNView& operator=(const SerializedATNView&) = default;
+
+ const_iterator begin() const { return data(); }
+
+ const_iterator cbegin() const { return data(); }
+
+ const_iterator end() const { return data() + size(); }
+
+ const_iterator cend() const { return data() + size(); }
+
+ const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
+
+ const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
+
+ const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
+
+ const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
+
+ bool empty() const { return size() == 0; }
+
+ const_pointer data() const { return _data; }
+
+ size_type size() const { return _size; }
+
+ size_type size_bytes() const { return size() * sizeof(value_type); }
+
+ const_reference operator[](size_type index) const { return _data[index]; }
+
+ private:
+ const_pointer _data = nullptr;
+ size_type _size = 0;
+ };
+
+ inline bool operator==(const SerializedATNView &lhs, const SerializedATNView &rhs) {
+ return (lhs.data() == rhs.data() && lhs.size() == rhs.size()) ||
+ (lhs.size() == rhs.size() && std::memcmp(lhs.data(), rhs.data(), lhs.size_bytes()) == 0);
+ }
+
+ inline bool operator!=(const SerializedATNView &lhs, const SerializedATNView &rhs) {
+ return !operator==(lhs, rhs);
+ }
+
+ inline bool operator<(const SerializedATNView &lhs, const SerializedATNView &rhs) {
+ int diff = std::memcmp(lhs.data(), rhs.data(), std::min(lhs.size_bytes(), rhs.size_bytes()));
+ return diff < 0 || (diff == 0 && lhs.size() < rhs.size());
+ }
+
+} // namespace atn
+} // namespace antlr4
+
+namespace std {
+
+ template <>
+ struct hash<::antlr4::atn::SerializedATNView> {
+ size_t operator()(const ::antlr4::atn::SerializedATNView &serializedATNView) const {
+ return ::antlr4::misc::MurmurHash::hashCode(serializedATNView.data(), serializedATNView.size());
+ }
+ };
+
+} // namespace std
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/SetTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/SetTransition.cpp
new file mode 100644
index 0000000000..95ec514edb
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/SetTransition.cpp
@@ -0,0 +1,28 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Token.h"
+#include "misc/IntervalSet.h"
+
+#include "atn/SetTransition.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+
+SetTransition::SetTransition(TransitionType transitionType, ATNState *target, misc::IntervalSet aSet)
+ : Transition(transitionType, target), set(aSet.isEmpty() ? misc::IntervalSet::of(Token::INVALID_TYPE) : std::move(aSet)) {
+}
+
+misc::IntervalSet SetTransition::label() const {
+ return set;
+}
+
+bool SetTransition::matches(size_t symbol, size_t /*minVocabSymbol*/, size_t /*maxVocabSymbol*/) const {
+ return set.contains(symbol);
+}
+
+std::string SetTransition::toString() const {
+ return "SET " + Transition::toString() + " { set: " + set.toString() + "}";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/SetTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/SetTransition.h
new file mode 100644
index 0000000000..3a3343ec25
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/SetTransition.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// A transition containing a set of values. </summary>
+ class ANTLR4CPP_PUBLIC SetTransition : public Transition {
+ public:
+ static bool is(const Transition &transition) {
+ const auto transitionType = transition.getTransitionType();
+ return transitionType == TransitionType::SET || transitionType == TransitionType::NOT_SET;
+ }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ const misc::IntervalSet set;
+
+ SetTransition(ATNState *target, misc::IntervalSet set) : SetTransition(TransitionType::SET, target, std::move(set)) {}
+
+ virtual misc::IntervalSet label() const override;
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+
+ virtual std::string toString() const override;
+
+ protected:
+ SetTransition(TransitionType transitionType, ATNState *target, misc::IntervalSet set);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/SingletonPredictionContext.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/SingletonPredictionContext.cpp
new file mode 100644
index 0000000000..66a91936e9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/SingletonPredictionContext.cpp
@@ -0,0 +1,86 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/SingletonPredictionContext.h"
+
+#include "support/Casts.h"
+#include "misc/MurmurHash.h"
+
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+namespace {
+
+ bool cachedHashCodeEqual(size_t lhs, size_t rhs) {
+ return lhs == rhs || lhs == 0 || rhs == 0;
+ }
+
+}
+
+SingletonPredictionContext::SingletonPredictionContext(Ref<const PredictionContext> parent, size_t returnState)
+ : PredictionContext(PredictionContextType::SINGLETON), parent(std::move(parent)), returnState(returnState) {
+ assert(returnState != ATNState::INVALID_STATE_NUMBER);
+}
+
+Ref<const SingletonPredictionContext> SingletonPredictionContext::create(Ref<const PredictionContext> parent, size_t returnState) {
+ if (returnState == EMPTY_RETURN_STATE && parent == nullptr) {
+ // someone can pass in the bits of an array ctx that mean $
+ return std::dynamic_pointer_cast<const SingletonPredictionContext>(EMPTY);
+ }
+ return std::make_shared<SingletonPredictionContext>(std::move(parent), returnState);
+}
+
+bool SingletonPredictionContext::isEmpty() const {
+ return parent == nullptr && returnState == EMPTY_RETURN_STATE;
+}
+
+size_t SingletonPredictionContext::size() const {
+ return 1;
+}
+
+const Ref<const PredictionContext>& SingletonPredictionContext::getParent(size_t index) const {
+ assert(index == 0);
+ static_cast<void>(index);
+ return parent;
+}
+
+size_t SingletonPredictionContext::getReturnState(size_t index) const {
+ assert(index == 0);
+ static_cast<void>(index);
+ return returnState;
+}
+
+size_t SingletonPredictionContext::hashCodeImpl() const {
+ size_t hash = misc::MurmurHash::initialize();
+ hash = misc::MurmurHash::update(hash, static_cast<size_t>(getContextType()));
+ hash = misc::MurmurHash::update(hash, parent);
+ hash = misc::MurmurHash::update(hash, returnState);
+ return misc::MurmurHash::finish(hash, 3);
+}
+
+bool SingletonPredictionContext::equals(const PredictionContext &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ if (getContextType() != other.getContextType()) {
+ return false;
+ }
+ const auto &singleton = downCast<const SingletonPredictionContext&>(other);
+ return returnState == singleton.returnState &&
+ cachedHashCodeEqual(cachedHashCode(), singleton.cachedHashCode()) &&
+ (parent == singleton.parent || (parent != nullptr && singleton.parent != nullptr && *parent == *singleton.parent));
+}
+
+std::string SingletonPredictionContext::toString() const {
+ //std::string up = !parent.expired() ? parent.lock()->toString() : "";
+ std::string up = parent != nullptr ? parent->toString() : "";
+ if (up.length() == 0) {
+ if (returnState == EMPTY_RETURN_STATE) {
+ return "$";
+ }
+ return std::to_string(returnState);
+ }
+ return std::to_string(returnState) + " " + up;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/SingletonPredictionContext.h b/contrib/libs/antlr4_cpp_runtime/src/atn/SingletonPredictionContext.h
new file mode 100644
index 0000000000..1784c4f045
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/SingletonPredictionContext.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/PredictionContext.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC SingletonPredictionContext final : public PredictionContext {
+ public:
+ static bool is(const PredictionContext &predictionContext) { return predictionContext.getContextType() == PredictionContextType::SINGLETON; }
+
+ static bool is(const PredictionContext *predictionContext) { return predictionContext != nullptr && is(*predictionContext); }
+
+ static Ref<const SingletonPredictionContext> create(Ref<const PredictionContext> parent, size_t returnState);
+
+ // Usually a parent is linked via a weak ptr. Not so here as we have kinda reverse reference chain.
+ // There are no child contexts stored here and often the parent context is left dangling when it's
+ // owning ATNState is released. In order to avoid having this context released as well (leaving all other contexts
+ // which got this one as parent with a null reference) we use a shared_ptr here instead, to keep those left alone
+ // parent contexts alive.
+ const Ref<const PredictionContext> parent;
+ const size_t returnState;
+
+ SingletonPredictionContext(Ref<const PredictionContext> parent, size_t returnState);
+
+ bool isEmpty() const override;
+ size_t size() const override;
+ const Ref<const PredictionContext>& getParent(size_t index) const override;
+ size_t getReturnState(size_t index) const override;
+ bool equals(const PredictionContext &other) const override;
+ std::string toString() const override;
+
+ protected:
+ size_t hashCodeImpl() const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/StarBlockStartState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/StarBlockStartState.h
new file mode 100644
index 0000000000..17fd43fde8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/StarBlockStartState.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/BlockStartState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// The block that begins a closure loop.
+ class ANTLR4CPP_PUBLIC StarBlockStartState final : public BlockStartState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::STAR_BLOCK_START; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ StarBlockStartState() : BlockStartState(ATNStateType::STAR_BLOCK_START) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopEntryState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopEntryState.h
new file mode 100644
index 0000000000..a62eb812b1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopEntryState.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC StarLoopEntryState final : public DecisionState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::STAR_LOOP_ENTRY; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ /**
+ * Indicates whether this state can benefit from a precedence DFA during SLL
+ * decision making.
+ *
+ * <p>This is a computed property that is calculated during ATN deserialization
+ * and stored for use in {@link ParserATNSimulator} and
+ * {@link ParserInterpreter}.</p>
+ *
+ * @see DFA#isPrecedenceDfa()
+ */
+ bool isPrecedenceDecision = false;
+
+ StarLoopbackState *loopBackState = nullptr;
+
+ StarLoopEntryState() : DecisionState(ATNStateType::STAR_LOOP_ENTRY) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopbackState.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopbackState.cpp
new file mode 100644
index 0000000000..6dddbc0d4e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopbackState.cpp
@@ -0,0 +1,19 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/StarLoopEntryState.h"
+#include "atn/Transition.h"
+#include "support/Casts.h"
+
+#include "atn/StarLoopbackState.h"
+
+using namespace antlr4::atn;
+
+StarLoopEntryState *StarLoopbackState::getLoopEntryState() const {
+ if (transitions[0]->target != nullptr && transitions[0]->target->getStateType() == ATNStateType::STAR_LOOP_ENTRY) {
+ return antlrcpp::downCast<StarLoopEntryState*>(transitions[0]->target);
+ }
+ return nullptr;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopbackState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopbackState.h
new file mode 100644
index 0000000000..04ef9db095
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/StarLoopbackState.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/ATNState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC StarLoopbackState final : public ATNState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::STAR_LOOP_BACK; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ StarLoopbackState() : ATNState(ATNStateType::STAR_LOOP_BACK) {}
+
+ StarLoopEntryState *getLoopEntryState() const;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/TokensStartState.h b/contrib/libs/antlr4_cpp_runtime/src/atn/TokensStartState.h
new file mode 100644
index 0000000000..8e41636283
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/TokensStartState.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/DecisionState.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// The Tokens rule start state linking to each lexer rule start state.
+ class ANTLR4CPP_PUBLIC TokensStartState final : public DecisionState {
+ public:
+ static bool is(const ATNState &atnState) { return atnState.getStateType() == ATNStateType::TOKEN_START; }
+
+ static bool is(const ATNState *atnState) { return atnState != nullptr && is(*atnState); }
+
+ TokensStartState() : DecisionState(ATNStateType::TOKEN_START) {}
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/Transition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/Transition.cpp
new file mode 100644
index 0000000000..b918cddfcf
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/Transition.cpp
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Exceptions.h"
+#include "support/Arrays.h"
+
+#include "atn/Transition.h"
+
+using namespace antlr4;
+using namespace antlr4::atn;
+using namespace antlrcpp;
+
+Transition::Transition(TransitionType transitionType, ATNState *target) : _transitionType(transitionType) {
+ if (target == nullptr) {
+ throw NullPointerException("target cannot be null.");
+ }
+
+ this->target = target;
+}
+
+bool Transition::isEpsilon() const {
+ return false;
+}
+
+misc::IntervalSet Transition::label() const {
+ return misc::IntervalSet::EMPTY_SET;
+}
+
+std::string Transition::toString() const {
+ std::stringstream ss;
+ ss << "(Transition " << std::hex << this << ", target: " << std::hex << target << ')';
+
+ return ss.str();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/Transition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/Transition.h
new file mode 100644
index 0000000000..4c88d698ae
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/Transition.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "misc/IntervalSet.h"
+#include "atn/TransitionType.h"
+
+namespace antlr4 {
+namespace atn {
+
+ /// <summary>
+ /// An ATN transition between any two ATN states. Subclasses define
+ /// atom, set, epsilon, action, predicate, rule transitions.
+ /// <p/>
+ /// This is a one way link. It emanates from a state (usually via a list of
+ /// transitions) and has a target state.
+ /// <p/>
+ /// Since we never have to change the ATN transitions once we construct it,
+ /// we can fix these transitions as specific classes. The DFA transitions
+ /// on the other hand need to update the labels as it adds transitions to
+ /// the states. We'll use the term Edge for the DFA to distinguish them from
+ /// ATN transitions.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC Transition {
+ public:
+ /// The target of this transition.
+ // ml: this is a reference into the ATN.
+ ATNState *target;
+
+ virtual ~Transition() = default;
+
+ TransitionType getTransitionType() const { return _transitionType; }
+
+ /**
+ * Determines if the transition is an "epsilon" transition.
+ *
+ * <p>The default implementation returns {@code false}.</p>
+ *
+ * @return {@code true} if traversing this transition in the ATN does not
+ * consume an input symbol; otherwise, {@code false} if traversing this
+ * transition consumes (matches) an input symbol.
+ */
+ virtual bool isEpsilon() const;
+ virtual misc::IntervalSet label() const;
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const = 0;
+
+ virtual std::string toString() const;
+
+ Transition(Transition const&) = delete;
+ Transition& operator=(Transition const&) = delete;
+
+ protected:
+ Transition(TransitionType transitionType, ATNState *target);
+
+ private:
+ const TransitionType _transitionType;
+ };
+
+ using ConstTransitionPtr = std::unique_ptr<const Transition>;
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/TransitionType.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/TransitionType.cpp
new file mode 100644
index 0000000000..78769b2ada
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/TransitionType.cpp
@@ -0,0 +1,27 @@
+#include "atn/TransitionType.h"
+
+std::string antlr4::atn::transitionTypeName(TransitionType transitionType) {
+ switch (transitionType) {
+ case TransitionType::EPSILON:
+ return "EPSILON";
+ case TransitionType::RANGE:
+ return "RANGE";
+ case TransitionType::RULE:
+ return "RULE";
+ case TransitionType::PREDICATE:
+ return "PREDICATE";
+ case TransitionType::ATOM:
+ return "ATOM";
+ case TransitionType::ACTION:
+ return "ACTION";
+ case TransitionType::SET:
+ return "SET";
+ case TransitionType::NOT_SET:
+ return "NOT_SET";
+ case TransitionType::WILDCARD:
+ return "WILDCARD";
+ case TransitionType::PRECEDENCE:
+ return "PRECEDENCE";
+ }
+ return "UNKNOWN";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/TransitionType.h b/contrib/libs/antlr4_cpp_runtime/src/atn/TransitionType.h
new file mode 100644
index 0000000000..d5d5f3bd97
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/TransitionType.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <string>
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace atn {
+
+ // Constants for transition serialization.
+ enum class TransitionType : size_t {
+ EPSILON = 1,
+ RANGE = 2,
+ RULE = 3,
+ PREDICATE = 4, // e.g., {isType(input.LT(1))}?
+ ATOM = 5,
+ ACTION = 6,
+ SET = 7, // ~(A|B) or ~atom, wildcard, which convert to next 2
+ NOT_SET = 8,
+ WILDCARD = 9,
+ PRECEDENCE = 10,
+ };
+
+ ANTLR4CPP_PUBLIC std::string transitionTypeName(TransitionType transitionType);
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/WildcardTransition.cpp b/contrib/libs/antlr4_cpp_runtime/src/atn/WildcardTransition.cpp
new file mode 100644
index 0000000000..03ec00d399
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/WildcardTransition.cpp
@@ -0,0 +1,21 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATNState.h"
+
+#include "atn/WildcardTransition.h"
+
+using namespace antlr4::atn;
+
+WildcardTransition::WildcardTransition(ATNState *target) : Transition(TransitionType::WILDCARD, target) {
+}
+
+bool WildcardTransition::matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const {
+ return symbol >= minVocabSymbol && symbol <= maxVocabSymbol;
+}
+
+std::string WildcardTransition::toString() const {
+ return "WILDCARD " + Transition::toString() + " {}";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/atn/WildcardTransition.h b/contrib/libs/antlr4_cpp_runtime/src/atn/WildcardTransition.h
new file mode 100644
index 0000000000..d8d663f1fd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/atn/WildcardTransition.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "atn/Transition.h"
+
+namespace antlr4 {
+namespace atn {
+
+ class ANTLR4CPP_PUBLIC WildcardTransition final : public Transition {
+ public:
+ static bool is(const Transition &transition) { return transition.getTransitionType() == TransitionType::WILDCARD; }
+
+ static bool is(const Transition *transition) { return transition != nullptr && is(*transition); }
+
+ explicit WildcardTransition(ATNState *target);
+
+ virtual bool matches(size_t symbol, size_t minVocabSymbol, size_t maxVocabSymbol) const override;
+
+ virtual std::string toString() const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/dfa/DFA.cpp b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFA.cpp
new file mode 100644
index 0000000000..4cc0ab7cc1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFA.cpp
@@ -0,0 +1,115 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "dfa/DFASerializer.h"
+#include "dfa/LexerDFASerializer.h"
+#include "support/CPPUtils.h"
+#include "atn/StarLoopEntryState.h"
+#include "atn/ATNConfigSet.h"
+#include "support/Casts.h"
+
+#include "dfa/DFA.h"
+
+using namespace antlr4;
+using namespace antlr4::dfa;
+using namespace antlrcpp;
+
+DFA::DFA(atn::DecisionState *atnStartState) : DFA(atnStartState, 0) {
+}
+
+DFA::DFA(atn::DecisionState *atnStartState, size_t decision)
+ : atnStartState(atnStartState), s0(nullptr), decision(decision) {
+
+ _precedenceDfa = false;
+ if (atn::StarLoopEntryState::is(atnStartState)) {
+ if (downCast<atn::StarLoopEntryState*>(atnStartState)->isPrecedenceDecision) {
+ _precedenceDfa = true;
+ s0 = new DFAState(std::unique_ptr<atn::ATNConfigSet>(new atn::ATNConfigSet()));
+ s0->isAcceptState = false;
+ s0->requiresFullContext = false;
+ }
+ }
+}
+
+DFA::DFA(DFA &&other) : atnStartState(other.atnStartState), s0(other.s0), decision(other.decision) {
+ // Source states are implicitly cleared by the move.
+ states = std::move(other.states);
+
+ other.atnStartState = nullptr;
+ other.decision = 0;
+ other.s0 = nullptr;
+ _precedenceDfa = other._precedenceDfa;
+ other._precedenceDfa = false;
+}
+
+DFA::~DFA() {
+ bool s0InList = (s0 == nullptr);
+ for (auto *state : states) {
+ if (state == s0)
+ s0InList = true;
+ delete state;
+ }
+
+ if (!s0InList) {
+ delete s0;
+ }
+}
+
+bool DFA::isPrecedenceDfa() const {
+ return _precedenceDfa;
+}
+
+DFAState* DFA::getPrecedenceStartState(int precedence) const {
+ assert(_precedenceDfa); // Only precedence DFAs may contain a precedence start state.
+
+ auto iterator = s0->edges.find(precedence);
+ if (iterator == s0->edges.end())
+ return nullptr;
+
+ return iterator->second;
+}
+
+void DFA::setPrecedenceStartState(int precedence, DFAState *startState) {
+ if (!isPrecedenceDfa()) {
+ throw IllegalStateException("Only precedence DFAs may contain a precedence start state.");
+ }
+
+ if (precedence < 0) {
+ return;
+ }
+
+ s0->edges[precedence] = startState;
+}
+
+std::vector<DFAState *> DFA::getStates() const {
+ std::vector<DFAState *> result;
+ for (auto *state : states)
+ result.push_back(state);
+
+ std::sort(result.begin(), result.end(), [](DFAState *o1, DFAState *o2) -> bool {
+ return o1->stateNumber < o2->stateNumber;
+ });
+
+ return result;
+}
+
+std::string DFA::toString(const Vocabulary &vocabulary) const {
+ if (s0 == nullptr) {
+ return "";
+ }
+
+ DFASerializer serializer(this, vocabulary);
+ return serializer.toString();
+}
+
+std::string DFA::toLexerString() const {
+ if (s0 == nullptr) {
+ return "";
+ }
+ LexerDFASerializer serializer(this);
+
+ return serializer.toString();
+}
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/dfa/DFA.h b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFA.h
new file mode 100644
index 0000000000..360eda8ba7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFA.h
@@ -0,0 +1,96 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "dfa/DFAState.h"
+
+namespace antlr4 {
+namespace dfa {
+
+ class ANTLR4CPP_PUBLIC DFA final {
+ private:
+ struct DFAStateHasher final {
+ size_t operator()(const DFAState *dfaState) const {
+ return dfaState->hashCode();
+ }
+ };
+
+ struct DFAStateComparer final {
+ bool operator()(const DFAState *lhs, const DFAState *rhs) const {
+ return lhs == rhs || *lhs == *rhs;
+ }
+ };
+
+ public:
+ /// A set of all DFA states. Use a map so we can get old state back.
+ /// Set only allows you to see if it's there.
+
+ /// From which ATN state did we create this DFA?
+ atn::DecisionState *atnStartState;
+ std::unordered_set<DFAState*, DFAStateHasher, DFAStateComparer> states; // States are owned by this class.
+ DFAState *s0;
+ size_t decision;
+
+ explicit DFA(atn::DecisionState *atnStartState);
+ DFA(atn::DecisionState *atnStartState, size_t decision);
+ DFA(const DFA &other) = delete;
+ DFA(DFA &&other);
+ ~DFA();
+
+ /**
+ * Gets whether this DFA is a precedence DFA. Precedence DFAs use a special
+ * start state {@link #s0} which is not stored in {@link #states}. The
+ * {@link DFAState#edges} array for this start state contains outgoing edges
+ * supplying individual start states corresponding to specific precedence
+ * values.
+ *
+ * @return {@code true} if this is a precedence DFA; otherwise,
+ * {@code false}.
+ * @see Parser#getPrecedence()
+ */
+ bool isPrecedenceDfa() const;
+
+ /**
+ * Get the start state for a specific precedence value.
+ *
+ * @param precedence The current precedence.
+ * @return The start state corresponding to the specified precedence, or
+ * {@code null} if no start state exists for the specified precedence.
+ *
+ * @throws IllegalStateException if this is not a precedence DFA.
+ * @see #isPrecedenceDfa()
+ */
+ DFAState* getPrecedenceStartState(int precedence) const;
+
+ /**
+ * Set the start state for a specific precedence value.
+ *
+ * @param precedence The current precedence.
+ * @param startState The start state corresponding to the specified
+ * precedence.
+ *
+ * @throws IllegalStateException if this is not a precedence DFA.
+ * @see #isPrecedenceDfa()
+ */
+ void setPrecedenceStartState(int precedence, DFAState *startState);
+
+ /// Return a list of all states in this DFA, ordered by state number.
+ std::vector<DFAState *> getStates() const;
+
+ std::string toString(const Vocabulary &vocabulary) const;
+
+ std::string toLexerString() const;
+
+ private:
+ /**
+ * {@code true} if this DFA is for a precedence decision; otherwise,
+ * {@code false}. This is the backing field for {@link #isPrecedenceDfa}.
+ */
+ bool _precedenceDfa;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/dfa/DFASerializer.cpp b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFASerializer.cpp
new file mode 100644
index 0000000000..64d01769de
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFASerializer.cpp
@@ -0,0 +1,60 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "dfa/DFA.h"
+#include "Vocabulary.h"
+
+#include "dfa/DFASerializer.h"
+
+using namespace antlr4::dfa;
+
+DFASerializer::DFASerializer(const DFA *dfa, const Vocabulary &vocabulary) : _dfa(dfa), _vocabulary(vocabulary) {
+}
+
+std::string DFASerializer::toString() const {
+ if (_dfa->s0 == nullptr) {
+ return "";
+ }
+
+ std::stringstream ss;
+ std::vector<DFAState *> states = _dfa->getStates();
+ for (auto *s : states) {
+ for (size_t i = 0; i < s->edges.size(); i++) {
+ DFAState *t = s->edges[i];
+ if (t != nullptr && t->stateNumber != INT32_MAX) {
+ ss << getStateString(s);
+ std::string label = getEdgeLabel(i);
+ ss << "-" << label << "->" << getStateString(t) << "\n";
+ }
+ }
+ }
+
+ return ss.str();
+}
+
+std::string DFASerializer::getEdgeLabel(size_t i) const {
+ return _vocabulary.getDisplayName(i); // ml: no longer needed -1 as we use a map for edges, without offset.
+}
+
+std::string DFASerializer::getStateString(DFAState *s) const {
+ size_t n = s->stateNumber;
+
+ const std::string baseStateStr = std::string(s->isAcceptState ? ":" : "") + "s" + std::to_string(n) +
+ (s->requiresFullContext ? "^" : "");
+
+ if (s->isAcceptState) {
+ if (!s->predicates.empty()) {
+ std::string buf;
+ for (size_t i = 0; i < s->predicates.size(); i++) {
+ buf.append(s->predicates[i].toString());
+ }
+ return baseStateStr + "=>" + buf;
+ } else {
+ return baseStateStr + "=>" + std::to_string(s->prediction);
+ }
+ } else {
+ return baseStateStr;
+ }
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/dfa/DFASerializer.h b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFASerializer.h
new file mode 100644
index 0000000000..b541714078
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFASerializer.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Vocabulary.h"
+
+namespace antlr4 {
+namespace dfa {
+
+ /// A DFA walker that knows how to dump them to serialized strings.
+ class ANTLR4CPP_PUBLIC DFASerializer {
+ public:
+ DFASerializer(const DFA *dfa, const Vocabulary &vocabulary);
+
+ virtual ~DFASerializer() = default;
+
+ std::string toString() const;
+
+ protected:
+ virtual std::string getEdgeLabel(size_t i) const;
+ std::string getStateString(DFAState *s) const;
+
+ private:
+ const DFA *_dfa;
+ const Vocabulary &_vocabulary;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/dfa/DFAState.cpp b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFAState.cpp
new file mode 100644
index 0000000000..e591b204c7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFAState.cpp
@@ -0,0 +1,59 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATNConfigSet.h"
+#include "atn/SemanticContext.h"
+#include "atn/ATNConfig.h"
+#include "misc/MurmurHash.h"
+
+#include "dfa/DFAState.h"
+
+using namespace antlr4::dfa;
+using namespace antlr4::atn;
+
+std::string DFAState::PredPrediction::toString() const {
+ return std::string("(") + pred->toString() + ", " + std::to_string(alt) + ")";
+}
+
+std::set<size_t> DFAState::getAltSet() const {
+ std::set<size_t> alts;
+ if (configs != nullptr) {
+ for (size_t i = 0; i < configs->size(); i++) {
+ alts.insert(configs->get(i)->alt);
+ }
+ }
+ return alts;
+}
+
+size_t DFAState::hashCode() const {
+ return configs != nullptr ? configs->hashCode() : 0;
+}
+
+bool DFAState::equals(const DFAState &other) const {
+ if (this == std::addressof(other)) {
+ return true;
+ }
+ return configs == other.configs ||
+ (configs != nullptr && other.configs != nullptr && *configs == *other.configs);
+}
+
+std::string DFAState::toString() const {
+ std::stringstream ss;
+ ss << stateNumber;
+ if (configs) {
+ ss << ":" << configs->toString();
+ }
+ if (isAcceptState) {
+ ss << " => ";
+ if (!predicates.empty()) {
+ for (size_t i = 0; i < predicates.size(); i++) {
+ ss << predicates[i].toString();
+ }
+ } else {
+ ss << prediction;
+ }
+ }
+ return ss.str();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/dfa/DFAState.h b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFAState.h
new file mode 100644
index 0000000000..f555cc45cf
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/dfa/DFAState.h
@@ -0,0 +1,154 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+#include "atn/ATNConfigSet.h"
+#include "FlatHashMap.h"
+
+namespace antlr4 {
+namespace dfa {
+
+ /// <summary>
+ /// A DFA state represents a set of possible ATN configurations.
+ /// As Aho, Sethi, Ullman p. 117 says "The DFA uses its state
+ /// to keep track of all possible states the ATN can be in after
+ /// reading each input symbol. That is to say, after reading
+ /// input a1a2..an, the DFA is in a state that represents the
+ /// subset T of the states of the ATN that are reachable from the
+ /// ATN's start state along some path labeled a1a2..an."
+ /// In conventional NFA->DFA conversion, therefore, the subset T
+ /// would be a bitset representing the set of states the
+ /// ATN could be in. We need to track the alt predicted by each
+ /// state as well, however. More importantly, we need to maintain
+ /// a stack of states, tracking the closure operations as they
+ /// jump from rule to rule, emulating rule invocations (method calls).
+ /// I have to add a stack to simulate the proper lookahead sequences for
+ /// the underlying LL grammar from which the ATN was derived.
+ /// <p/>
+ /// I use a set of ATNConfig objects not simple states. An ATNConfig
+ /// is both a state (ala normal conversion) and a RuleContext describing
+ /// the chain of rules (if any) followed to arrive at that state.
+ /// <p/>
+ /// A DFA state may have multiple references to a particular state,
+ /// but with different ATN contexts (with same or different alts)
+ /// meaning that state was reached via a different set of rule invocations.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC DFAState final {
+ public:
+ struct ANTLR4CPP_PUBLIC PredPrediction final {
+ public:
+ Ref<const atn::SemanticContext> pred; // never null; at least SemanticContext.NONE
+ int alt;
+
+ PredPrediction() = delete;
+
+ PredPrediction(const PredPrediction&) = default;
+ PredPrediction(PredPrediction&&) = default;
+
+ PredPrediction(Ref<const atn::SemanticContext> pred, int alt) : pred(std::move(pred)), alt(alt) {}
+
+ PredPrediction& operator=(const PredPrediction&) = default;
+ PredPrediction& operator=(PredPrediction&&) = default;
+
+ std::string toString() const;
+ };
+
+ std::unique_ptr<atn::ATNConfigSet> configs;
+
+ /// {@code edges[symbol]} points to target of symbol. Shift up by 1 so (-1)
+ /// <seealso cref="Token#EOF"/> maps to {@code edges[0]}.
+ // ml: this is a sparse list, so we use a map instead of a vector.
+ // Watch out: we no longer have the -1 offset, as it isn't needed anymore.
+ FlatHashMap<size_t, DFAState*> edges;
+
+ /// if accept state, what ttype do we match or alt do we predict?
+ /// This is set to <seealso cref="ATN#INVALID_ALT_NUMBER"/> when <seealso cref="#predicates"/>{@code !=null} or
+ /// <seealso cref="#requiresFullContext"/>.
+ size_t prediction = 0;
+
+ Ref<const atn::LexerActionExecutor> lexerActionExecutor;
+
+ /// <summary>
+ /// During SLL parsing, this is a list of predicates associated with the
+ /// ATN configurations of the DFA state. When we have predicates,
+ /// <seealso cref="#requiresFullContext"/> is {@code false} since full context prediction evaluates predicates
+ /// on-the-fly. If this is not null, then <seealso cref="#prediction"/> is
+ /// <seealso cref="ATN#INVALID_ALT_NUMBER"/>.
+ /// <p/>
+ /// We only use these for non-<seealso cref="#requiresFullContext"/> but conflicting states. That
+ /// means we know from the context (it's $ or we don't dip into outer
+ /// context) that it's an ambiguity not a conflict.
+ /// <p/>
+ /// This list is computed by <seealso cref="ParserATNSimulator#predicateDFAState"/>.
+ /// </summary>
+ std::vector<PredPrediction> predicates;
+
+ int stateNumber = -1;
+
+ bool isAcceptState = false;
+
+ /// <summary>
+ /// Indicates that this state was created during SLL prediction that
+ /// discovered a conflict between the configurations in the state. Future
+ /// <seealso cref="ParserATNSimulator#execATN"/> invocations immediately jumped doing
+ /// full context prediction if this field is true.
+ /// </summary>
+ bool requiresFullContext = false;
+
+ /// Map a predicate to a predicted alternative.
+ DFAState() = default;
+
+ explicit DFAState(int stateNumber) : stateNumber(stateNumber) {}
+
+ explicit DFAState(std::unique_ptr<atn::ATNConfigSet> configs) : configs(std::move(configs)) {}
+
+ /// <summary>
+ /// Get the set of all alts mentioned by all ATN configurations in this
+ /// DFA state.
+ /// </summary>
+ std::set<size_t> getAltSet() const;
+
+ size_t hashCode() const;
+
+ /// Two DFAState instances are equal if their ATN configuration sets
+ /// are the same. This method is used to see if a state already exists.
+ ///
+ /// Because the number of alternatives and number of ATN configurations are
+ /// finite, there is a finite number of DFA states that can be processed.
+ /// This is necessary to show that the algorithm terminates.
+ ///
+ /// Cannot test the DFA state numbers here because in
+ /// ParserATNSimulator#addDFAState we need to know if any other state
+ /// exists that has this exact set of ATN configurations. The
+ /// stateNumber is irrelevant.
+ bool equals(const DFAState &other) const;
+
+ std::string toString() const;
+ };
+
+ inline bool operator==(const DFAState &lhs, const DFAState &rhs) {
+ return lhs.equals(rhs);
+ }
+
+ inline bool operator!=(const DFAState &lhs, const DFAState &rhs) {
+ return !operator==(lhs, rhs);
+ }
+
+} // namespace dfa
+} // namespace antlr4
+
+namespace std {
+
+ template <>
+ struct hash<::antlr4::dfa::DFAState> {
+ size_t operator()(const ::antlr4::dfa::DFAState &dfaState) const {
+ return dfaState.hashCode();
+ }
+ };
+
+} // namespace std
diff --git a/contrib/libs/antlr4_cpp_runtime/src/dfa/LexerDFASerializer.cpp b/contrib/libs/antlr4_cpp_runtime/src/dfa/LexerDFASerializer.cpp
new file mode 100644
index 0000000000..20ed734743
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/dfa/LexerDFASerializer.cpp
@@ -0,0 +1,17 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Vocabulary.h"
+
+#include "dfa/LexerDFASerializer.h"
+
+using namespace antlr4::dfa;
+
+LexerDFASerializer::LexerDFASerializer(const DFA *dfa) : DFASerializer(dfa, Vocabulary()) {
+}
+
+std::string LexerDFASerializer::getEdgeLabel(size_t i) const {
+ return std::string("'") + static_cast<char>(i) + "'";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/dfa/LexerDFASerializer.h b/contrib/libs/antlr4_cpp_runtime/src/dfa/LexerDFASerializer.h
new file mode 100644
index 0000000000..eed7f4f0c5
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/dfa/LexerDFASerializer.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "dfa/DFASerializer.h"
+
+namespace antlr4 {
+namespace dfa {
+
+ class ANTLR4CPP_PUBLIC LexerDFASerializer final : public DFASerializer {
+ public:
+ explicit LexerDFASerializer(const DFA *dfa);
+
+ protected:
+ std::string getEdgeLabel(size_t i) const override;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/internal/Synchronization.cpp b/contrib/libs/antlr4_cpp_runtime/src/internal/Synchronization.cpp
new file mode 100644
index 0000000000..dd30ef971b
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/internal/Synchronization.cpp
@@ -0,0 +1,100 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#include "internal/Synchronization.h"
+
+using namespace antlr4::internal;
+
+void Mutex::lock() {
+#if ANTLR4CPP_USING_ABSEIL
+ _impl.Lock();
+#else
+ _impl.lock();
+#endif
+}
+
+bool Mutex::try_lock() {
+#if ANTLR4CPP_USING_ABSEIL
+ return _impl.TryLock();
+#else
+ return _impl.try_lock();
+#endif
+}
+
+void Mutex::unlock() {
+#if ANTLR4CPP_USING_ABSEIL
+ _impl.Unlock();
+#else
+ _impl.unlock();
+#endif
+}
+
+void SharedMutex::lock() {
+#if ANTLR4CPP_USING_ABSEIL
+ _impl.WriterLock();
+#else
+ _impl.lock();
+#endif
+}
+
+bool SharedMutex::try_lock() {
+#if ANTLR4CPP_USING_ABSEIL
+ return _impl.WriterTryLock();
+#else
+ return _impl.try_lock();
+#endif
+}
+
+void SharedMutex::unlock() {
+#if ANTLR4CPP_USING_ABSEIL
+ _impl.WriterUnlock();
+#else
+ _impl.unlock();
+#endif
+}
+
+void SharedMutex::lock_shared() {
+#if ANTLR4CPP_USING_ABSEIL
+ _impl.ReaderLock();
+#else
+ _impl.lock_shared();
+#endif
+}
+
+bool SharedMutex::try_lock_shared() {
+#if ANTLR4CPP_USING_ABSEIL
+ return _impl.ReaderTryLock();
+#else
+ return _impl.try_lock_shared();
+#endif
+}
+
+void SharedMutex::unlock_shared() {
+#if ANTLR4CPP_USING_ABSEIL
+ _impl.ReaderUnlock();
+#else
+ _impl.unlock_shared();
+#endif
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/internal/Synchronization.h b/contrib/libs/antlr4_cpp_runtime/src/internal/Synchronization.h
new file mode 100644
index 0000000000..0f1ff9587d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/internal/Synchronization.h
@@ -0,0 +1,154 @@
+// Copyright 2012-2022 The ANTLR Project
+//
+// 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. Neither the name of the copyright holder nor the names of its contributors may be used to
+// endorse or promote products derived from this software without specific prior written
+// permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR
+// CONTRIBUTORS 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.
+
+#pragma once
+
+#include "antlr4-common.h"
+
+#include <mutex>
+#include <shared_mutex>
+#include <utility>
+
+#if ANTLR4CPP_USING_ABSEIL
+#error #include "absl/base/call_once.h"
+#error #include "absl/base/thread_annotations.h"
+#error #include "absl/synchronization/mutex.h"
+#define ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS ABSL_NO_THREAD_SAFETY_ANALYSIS
+#else
+#define ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS
+#endif
+
+// By default ANTLRv4 uses synchronization primitives provided by the C++ standard library. In most
+// deployments this is fine, however in some using custom synchronization primitives may be
+// preferred. This header allows that by optionally supporting some alternative implementations and
+// allowing for more easier patching of other alternatives.
+
+namespace antlr4::internal {
+
+ // Must be compatible with C++ standard library Mutex requirement.
+ class ANTLR4CPP_PUBLIC Mutex final {
+ public:
+ Mutex() = default;
+
+ // No copying or moving, we are as strict as possible to support other implementations.
+ Mutex(const Mutex&) = delete;
+ Mutex(Mutex&&) = delete;
+
+ // No copying or moving, we are as strict as possible to support other implementations.
+ Mutex& operator=(const Mutex&) = delete;
+ Mutex& operator=(Mutex&&) = delete;
+
+ void lock() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ bool try_lock() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ void unlock() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ private:
+#if ANTLR4CPP_USING_ABSEIL
+ absl::Mutex _impl;
+#else
+ std::mutex _impl;
+#endif
+ };
+
+ template <typename Mutex>
+ using UniqueLock = std::unique_lock<Mutex>;
+
+ // Must be compatible with C++ standard library SharedMutex requirement.
+ class ANTLR4CPP_PUBLIC SharedMutex final {
+ public:
+ SharedMutex() = default;
+
+ // No copying or moving, we are as strict as possible to support other implementations.
+ SharedMutex(const SharedMutex&) = delete;
+ SharedMutex(SharedMutex&&) = delete;
+
+ // No copying or moving, we are as strict as possible to support other implementations.
+ SharedMutex& operator=(const SharedMutex&) = delete;
+ SharedMutex& operator=(SharedMutex&&) = delete;
+
+ void lock() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ bool try_lock() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ void unlock() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ void lock_shared() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ bool try_lock_shared() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ void unlock_shared() ANTLR4CPP_NO_THREAD_SAFTEY_ANALYSIS;
+
+ private:
+#if ANTLR4CPP_USING_ABSEIL
+ absl::Mutex _impl;
+#else
+ std::shared_mutex _impl;
+#endif
+ };
+
+ template <typename Mutex>
+ using SharedLock = std::shared_lock<Mutex>;
+
+ class OnceFlag;
+
+ template <typename Callable, typename... Args>
+ void call_once(OnceFlag &onceFlag, Callable &&callable, Args&&... args);
+
+ // Must be compatible with std::once_flag.
+ class ANTLR4CPP_PUBLIC OnceFlag final {
+ public:
+ constexpr OnceFlag() = default;
+
+ // No copying or moving, we are as strict as possible to support other implementations.
+ OnceFlag(const OnceFlag&) = delete;
+ OnceFlag(OnceFlag&&) = delete;
+
+ // No copying or moving, we are as strict as possible to support other implementations.
+ OnceFlag& operator=(const OnceFlag&) = delete;
+ OnceFlag& operator=(OnceFlag&&) = delete;
+
+ private:
+ template <typename Callable, typename... Args>
+ friend void call_once(OnceFlag &onceFlag, Callable &&callable, Args&&... args);
+
+#if ANTLR4CPP_USING_ABSEIL
+ absl::once_flag _impl;
+#else
+ std::once_flag _impl;
+#endif
+ };
+
+ template <typename Callable, typename... Args>
+ void call_once(OnceFlag &onceFlag, Callable &&callable, Args&&... args) {
+#if ANTLR4CPP_USING_ABSEIL
+ absl::call_once(onceFlag._impl, std::forward<Callable>(callable), std::forward<Args>(args)...);
+#else
+ std::call_once(onceFlag._impl, std::forward<Callable>(callable), std::forward<Args>(args)...);
+#endif
+ }
+
+} // namespace antlr4::internal
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/InterpreterDataReader.cpp b/contrib/libs/antlr4_cpp_runtime/src/misc/InterpreterDataReader.cpp
new file mode 100644
index 0000000000..1a236eccfb
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/InterpreterDataReader.cpp
@@ -0,0 +1,124 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "atn/ATN.h"
+#include "atn/ATNDeserializer.h"
+#include "Vocabulary.h"
+
+#include "misc/InterpreterDataReader.h"
+
+using namespace antlr4::dfa;
+using namespace antlr4::atn;
+using namespace antlr4::misc;
+
+InterpreterData::InterpreterData(std::vector<std::string> const& literalNames, std::vector<std::string> const& symbolicNames)
+: vocabulary(literalNames, symbolicNames) {
+}
+
+InterpreterData InterpreterDataReader::parseFile(std::string const& fileName) {
+ // The structure of the data file is very simple. Everything is line based with empty lines
+ // separating the different parts. For lexers the layout is:
+ // token literal names:
+ // ...
+ //
+ // token symbolic names:
+ // ...
+ //
+ // rule names:
+ // ...
+ //
+ // channel names:
+ // ...
+ //
+ // mode names:
+ // ...
+ //
+ // atn:
+ // <a single line with comma separated int values> enclosed in a pair of squared brackets.
+ //
+ // Data for a parser does not contain channel and mode names.
+
+ std::ifstream input(fileName);
+ if (!input.good())
+ return {};
+
+ std::vector<std::string> literalNames;
+ std::vector<std::string> symbolicNames;
+
+ std::string line;
+
+ std::getline(input, line, '\n');
+ assert(line == "token literal names:");
+ while (true) {
+ std::getline(input, line, '\n');
+ if (line.empty())
+ break;
+
+ literalNames.push_back(line == "null" ? "" : line);
+ };
+
+ std::getline(input, line, '\n');
+ assert(line == "token symbolic names:");
+ while (true) {
+ std::getline(input, line, '\n');
+ if (line.empty())
+ break;
+
+ symbolicNames.push_back(line == "null" ? "" : line);
+ };
+ InterpreterData result(literalNames, symbolicNames);
+
+ std::getline(input, line, '\n');
+ assert(line == "rule names:");
+ while (true) {
+ std::getline(input, line, '\n');
+ if (line.empty())
+ break;
+
+ result.ruleNames.push_back(line);
+ };
+
+ std::getline(input, line, '\n');
+ if (line == "channel names:") {
+ while (true) {
+ std::getline(input, line, '\n');
+ if (line.empty())
+ break;
+
+ result.channels.push_back(line);
+ };
+
+ std::getline(input, line, '\n');
+ assert(line == "mode names:");
+ while (true) {
+ std::getline(input, line, '\n');
+ if (line.empty())
+ break;
+
+ result.modes.push_back(line);
+ };
+ }
+
+ std::vector<int32_t> serializedATN;
+
+ std::getline(input, line, '\n');
+ assert(line == "atn:");
+ std::getline(input, line, '\n');
+ std::stringstream tokenizer(line);
+ std::string value;
+ while (tokenizer.good()) {
+ std::getline(tokenizer, value, ',');
+ unsigned long number;
+ if (value[0] == '[')
+ number = std::strtoul(&value[1], nullptr, 10);
+ else
+ number = std::strtoul(value.c_str(), nullptr, 10);
+ serializedATN.push_back(static_cast<int32_t>(number));
+ }
+
+ ATNDeserializer deserializer;
+ result.atn = deserializer.deserialize(serializedATN);
+ return result;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/InterpreterDataReader.h b/contrib/libs/antlr4_cpp_runtime/src/misc/InterpreterDataReader.h
new file mode 100644
index 0000000000..4b83dd129d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/InterpreterDataReader.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+#include "atn/ATN.h"
+#include "Vocabulary.h"
+
+namespace antlr4 {
+namespace misc {
+
+ struct InterpreterData {
+ std::unique_ptr<atn::ATN> atn;
+ dfa::Vocabulary vocabulary;
+ std::vector<std::string> ruleNames;
+ std::vector<std::string> channels; // Only valid for lexer grammars.
+ std::vector<std::string> modes; // ditto
+
+ InterpreterData() {}; // For invalid content.
+ InterpreterData(std::vector<std::string> const& literalNames, std::vector<std::string> const& symbolicNames);
+ };
+
+ // A class to read plain text interpreter data produced by ANTLR.
+ class ANTLR4CPP_PUBLIC InterpreterDataReader {
+ public:
+ static InterpreterData parseFile(std::string const& fileName);
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/Interval.cpp b/contrib/libs/antlr4_cpp_runtime/src/misc/Interval.cpp
new file mode 100644
index 0000000000..f0d0bfb491
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/Interval.cpp
@@ -0,0 +1,61 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/Interval.h"
+
+using namespace antlr4::misc;
+
+const Interval Interval::INVALID;
+
+size_t Interval::hashCode() const {
+ size_t hash = 23;
+ hash = hash * 31 + static_cast<size_t>(a);
+ hash = hash * 31 + static_cast<size_t>(b);
+ return hash;
+}
+
+bool Interval::startsBeforeDisjoint(const Interval &other) const {
+ return a < other.a && b < other.a;
+}
+
+bool Interval::startsBeforeNonDisjoint(const Interval &other) const {
+ return a <= other.a && b >= other.a;
+}
+
+bool Interval::startsAfter(const Interval &other) const {
+ return a > other.a;
+}
+
+bool Interval::startsAfterDisjoint(const Interval &other) const {
+ return a > other.b;
+}
+
+bool Interval::startsAfterNonDisjoint(const Interval &other) const {
+ return a > other.a && a <= other.b; // b >= other.b implied
+}
+
+bool Interval::disjoint(const Interval &other) const {
+ return startsBeforeDisjoint(other) || startsAfterDisjoint(other);
+}
+
+bool Interval::adjacent(const Interval &other) const {
+ return a == other.b + 1 || b == other.a - 1;
+}
+
+bool Interval::properlyContains(const Interval &other) const {
+ return other.a >= a && other.b <= b;
+}
+
+Interval Interval::Union(const Interval &other) const {
+ return Interval(std::min(a, other.a), std::max(b, other.b));
+}
+
+Interval Interval::intersection(const Interval &other) const {
+ return Interval(std::max(a, other.a), std::min(b, other.b));
+}
+
+std::string Interval::toString() const {
+ return std::to_string(a) + ".." + std::to_string(b);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/Interval.h b/contrib/libs/antlr4_cpp_runtime/src/misc/Interval.h
new file mode 100644
index 0000000000..32abf629a8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/Interval.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace misc {
+
+ // Helpers to convert certain unsigned symbols (e.g. Token::EOF) to their original numeric value (e.g. -1)
+ // and vice versa. This is needed mostly for intervals to keep their original order and for toString()
+ // methods to print the original numeric value (e.g. for tests).
+ constexpr size_t numericToSymbol(ssize_t v) { return static_cast<size_t>(v); }
+ constexpr ssize_t symbolToNumeric(size_t v) { return static_cast<ssize_t>(v); }
+
+ /// An immutable inclusive interval a..b
+ class ANTLR4CPP_PUBLIC Interval final {
+ public:
+ static const Interval INVALID;
+
+ // Must stay signed to guarantee the correct sort order.
+ ssize_t a;
+ ssize_t b;
+
+ constexpr Interval() : Interval(static_cast<ssize_t>(-1), static_cast<ssize_t>(-2)) {}
+
+ constexpr explicit Interval(size_t a_, size_t b_) : Interval(symbolToNumeric(a_), symbolToNumeric(b_)) {}
+
+ constexpr Interval(ssize_t a_, ssize_t b_) : a(a_), b(b_) {}
+
+ /// return number of elements between a and b inclusively. x..x is length 1.
+ /// if b < a, then length is 0. 9..10 has length 2.
+ constexpr size_t length() const { return b >= a ? static_cast<size_t>(b - a + 1) : 0; }
+
+ constexpr bool operator==(const Interval &other) const { return a == other.a && b == other.b; }
+
+ size_t hashCode() const;
+
+ /// <summary>
+ /// Does this start completely before other? Disjoint </summary>
+ bool startsBeforeDisjoint(const Interval &other) const;
+
+ /// <summary>
+ /// Does this start at or before other? Nondisjoint </summary>
+ bool startsBeforeNonDisjoint(const Interval &other) const;
+
+ /// <summary>
+ /// Does this.a start after other.b? May or may not be disjoint </summary>
+ bool startsAfter(const Interval &other) const;
+
+ /// <summary>
+ /// Does this start completely after other? Disjoint </summary>
+ bool startsAfterDisjoint(const Interval &other) const;
+
+ /// <summary>
+ /// Does this start after other? NonDisjoint </summary>
+ bool startsAfterNonDisjoint(const Interval &other) const;
+
+ /// <summary>
+ /// Are both ranges disjoint? I.e., no overlap? </summary>
+ bool disjoint(const Interval &other) const;
+
+ /// <summary>
+ /// Are two intervals adjacent such as 0..41 and 42..42? </summary>
+ bool adjacent(const Interval &other) const;
+
+ bool properlyContains(const Interval &other) const;
+
+ /// <summary>
+ /// Return the interval computed from combining this and other </summary>
+ Interval Union(const Interval &other) const;
+
+ /// <summary>
+ /// Return the interval in common between this and o </summary>
+ Interval intersection(const Interval &other) const;
+
+ std::string toString() const;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/IntervalSet.cpp b/contrib/libs/antlr4_cpp_runtime/src/misc/IntervalSet.cpp
new file mode 100644
index 0000000000..d230bf45f6
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/IntervalSet.cpp
@@ -0,0 +1,501 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/MurmurHash.h"
+#include "Lexer.h"
+#include "Exceptions.h"
+#include "Vocabulary.h"
+
+#include "misc/IntervalSet.h"
+
+using namespace antlr4;
+using namespace antlr4::misc;
+
+IntervalSet const IntervalSet::COMPLETE_CHAR_SET =
+ IntervalSet::of(Lexer::MIN_CHAR_VALUE, Lexer::MAX_CHAR_VALUE);
+
+IntervalSet const IntervalSet::EMPTY_SET;
+
+IntervalSet::IntervalSet() : _intervals() {
+}
+
+IntervalSet::IntervalSet(const IntervalSet &set) : IntervalSet() {
+ _intervals = set._intervals;
+}
+
+IntervalSet::IntervalSet(IntervalSet&& set) : IntervalSet(std::move(set._intervals)) {
+}
+
+IntervalSet::IntervalSet(std::vector<Interval>&& intervals) : _intervals(std::move(intervals)) {
+}
+
+IntervalSet& IntervalSet::operator=(const IntervalSet& other) {
+ _intervals = other._intervals;
+ return *this;
+}
+
+IntervalSet& IntervalSet::operator=(IntervalSet&& other) {
+ _intervals = move(other._intervals);
+ return *this;
+}
+
+IntervalSet IntervalSet::of(ssize_t a) {
+ return IntervalSet({ Interval(a, a) });
+}
+
+IntervalSet IntervalSet::of(ssize_t a, ssize_t b) {
+ return IntervalSet({ Interval(a, b) });
+}
+
+void IntervalSet::clear() {
+ _intervals.clear();
+}
+
+void IntervalSet::add(ssize_t el) {
+ add(el, el);
+}
+
+void IntervalSet::add(ssize_t a, ssize_t b) {
+ add(Interval(a, b));
+}
+
+void IntervalSet::add(const Interval &addition) {
+ if (addition.b < addition.a) {
+ return;
+ }
+
+ // find position in list
+ for (auto iterator = _intervals.begin(); iterator != _intervals.end(); ++iterator) {
+ Interval r = *iterator;
+ if (addition == r) {
+ return;
+ }
+
+ if (addition.adjacent(r) || !addition.disjoint(r)) {
+ // next to each other, make a single larger interval
+ Interval bigger = addition.Union(r);
+ *iterator = bigger;
+
+ // make sure we didn't just create an interval that
+ // should be merged with next interval in list
+ while (iterator + 1 != _intervals.end()) {
+ Interval next = *++iterator;
+ if (!bigger.adjacent(next) && bigger.disjoint(next)) {
+ break;
+ }
+
+ // if we bump up against or overlap next, merge
+ iterator = _intervals.erase(iterator);// remove this one
+ --iterator; // move backwards to what we just set
+ *iterator = bigger.Union(next); // set to 3 merged ones
+ // ml: no need to advance iterator, we do that in the next round anyway. ++iterator; // first call to next after previous duplicates the result
+ }
+ return;
+ }
+
+ if (addition.startsBeforeDisjoint(r)) {
+ // insert before r
+ //--iterator;
+ _intervals.insert(iterator, addition);
+ return;
+ }
+
+ // if disjoint and after r, a future iteration will handle it
+ }
+
+ // ok, must be after last interval (and disjoint from last interval)
+ // just add it
+ _intervals.push_back(addition);
+}
+
+IntervalSet IntervalSet::Or(const std::vector<IntervalSet> &sets) {
+ IntervalSet result;
+ for (const auto &s : sets) {
+ result.addAll(s);
+ }
+ return result;
+}
+
+IntervalSet& IntervalSet::addAll(const IntervalSet &set) {
+ // walk set and add each interval
+ for (auto const& interval : set._intervals) {
+ add(interval);
+ }
+ return *this;
+}
+
+IntervalSet IntervalSet::complement(ssize_t minElement, ssize_t maxElement) const {
+ return complement(IntervalSet::of(minElement, maxElement));
+}
+
+IntervalSet IntervalSet::complement(const IntervalSet &vocabulary) const {
+ return vocabulary.subtract(*this);
+}
+
+IntervalSet IntervalSet::subtract(const IntervalSet &other) const {
+ return subtract(*this, other);
+}
+
+IntervalSet IntervalSet::subtract(const IntervalSet &left, const IntervalSet &right) {
+ if (left.isEmpty()) {
+ return IntervalSet();
+ }
+
+ if (right.isEmpty()) {
+ // right set has no elements; just return the copy of the current set
+ return left;
+ }
+
+ IntervalSet result(left);
+ size_t resultI = 0;
+ size_t rightI = 0;
+ while (resultI < result._intervals.size() && rightI < right._intervals.size()) {
+ Interval &resultInterval = result._intervals[resultI];
+ const Interval &rightInterval = right._intervals[rightI];
+
+ // operation: (resultInterval - rightInterval) and update indexes
+
+ if (rightInterval.b < resultInterval.a) {
+ rightI++;
+ continue;
+ }
+
+ if (rightInterval.a > resultInterval.b) {
+ resultI++;
+ continue;
+ }
+
+ Interval beforeCurrent;
+ Interval afterCurrent;
+ if (rightInterval.a > resultInterval.a) {
+ beforeCurrent = Interval(resultInterval.a, rightInterval.a - 1);
+ }
+
+ if (rightInterval.b < resultInterval.b) {
+ afterCurrent = Interval(rightInterval.b + 1, resultInterval.b);
+ }
+
+ if (beforeCurrent.a > -1) { // -1 is the default value
+ if (afterCurrent.a > -1) {
+ // split the current interval into two
+ result._intervals[resultI] = beforeCurrent;
+ result._intervals.insert(result._intervals.begin() + resultI + 1, afterCurrent);
+ resultI++;
+ rightI++;
+ } else {
+ // replace the current interval
+ result._intervals[resultI] = beforeCurrent;
+ resultI++;
+ }
+ } else {
+ if (afterCurrent.a > -1) {
+ // replace the current interval
+ result._intervals[resultI] = afterCurrent;
+ rightI++;
+ } else {
+ // remove the current interval (thus no need to increment resultI)
+ result._intervals.erase(result._intervals.begin() + resultI);
+ }
+ }
+ }
+
+ // If rightI reached right.intervals.size(), no more intervals to subtract from result.
+ // If resultI reached result.intervals.size(), we would be subtracting from an empty set.
+ // Either way, we are done.
+ return result;
+}
+
+IntervalSet IntervalSet::Or(const IntervalSet &a) const {
+ IntervalSet result;
+ result.addAll(*this);
+ result.addAll(a);
+ return result;
+}
+
+IntervalSet IntervalSet::And(const IntervalSet &other) const {
+ IntervalSet intersection;
+ size_t i = 0;
+ size_t j = 0;
+
+ // iterate down both interval lists looking for nondisjoint intervals
+ while (i < _intervals.size() && j < other._intervals.size()) {
+ Interval mine = _intervals[i];
+ Interval theirs = other._intervals[j];
+
+ if (mine.startsBeforeDisjoint(theirs)) {
+ // move this iterator looking for interval that might overlap
+ i++;
+ } else if (theirs.startsBeforeDisjoint(mine)) {
+ // move other iterator looking for interval that might overlap
+ j++;
+ } else if (mine.properlyContains(theirs)) {
+ // overlap, add intersection, get next theirs
+ intersection.add(mine.intersection(theirs));
+ j++;
+ } else if (theirs.properlyContains(mine)) {
+ // overlap, add intersection, get next mine
+ intersection.add(mine.intersection(theirs));
+ i++;
+ } else if (!mine.disjoint(theirs)) {
+ // overlap, add intersection
+ intersection.add(mine.intersection(theirs));
+
+ // Move the iterator of lower range [a..b], but not
+ // the upper range as it may contain elements that will collide
+ // with the next iterator. So, if mine=[0..115] and
+ // theirs=[115..200], then intersection is 115 and move mine
+ // but not theirs as theirs may collide with the next range
+ // in thisIter.
+ // move both iterators to next ranges
+ if (mine.startsAfterNonDisjoint(theirs)) {
+ j++;
+ } else if (theirs.startsAfterNonDisjoint(mine)) {
+ i++;
+ }
+ }
+ }
+
+ return intersection;
+}
+
+
+bool IntervalSet::contains(ssize_t el) const {
+ if (_intervals.empty() || el < _intervals.front().a || el > _intervals.back().b) {
+ return false;
+ }
+
+ return std::binary_search(_intervals.begin(), _intervals.end(), Interval(el, el), [](const Interval &lhs, const Interval &rhs) {
+ return lhs.b < rhs.a;
+ });
+}
+
+bool IntervalSet::isEmpty() const {
+ return _intervals.empty();
+}
+
+ssize_t IntervalSet::getSingleElement() const {
+ if (_intervals.size() == 1) {
+ if (_intervals[0].a == _intervals[0].b) {
+ return _intervals[0].a;
+ }
+ }
+
+ return Token::INVALID_TYPE; // XXX: this value is 0, but 0 is a valid interval range, how can that work?
+}
+
+ssize_t IntervalSet::getMaxElement() const {
+ if (_intervals.empty()) {
+ return Token::INVALID_TYPE;
+ }
+
+ return _intervals.back().b;
+}
+
+ssize_t IntervalSet::getMinElement() const {
+ if (_intervals.empty()) {
+ return Token::INVALID_TYPE;
+ }
+
+ return _intervals.front().a;
+}
+
+std::vector<Interval> const& IntervalSet::getIntervals() const {
+ return _intervals;
+}
+
+size_t IntervalSet::hashCode() const {
+ size_t hash = MurmurHash::initialize();
+ for (const auto &interval : _intervals) {
+ hash = MurmurHash::update(hash, interval.a);
+ hash = MurmurHash::update(hash, interval.b);
+ }
+
+ return MurmurHash::finish(hash, _intervals.size() * 2);
+}
+
+bool IntervalSet::operator == (const IntervalSet &other) const {
+ if (_intervals.empty() && other._intervals.empty())
+ return true;
+
+ if (_intervals.size() != other._intervals.size())
+ return false;
+
+ return std::equal(_intervals.begin(), _intervals.end(), other._intervals.begin());
+}
+
+std::string IntervalSet::toString() const {
+ return toString(false);
+}
+
+std::string IntervalSet::toString(bool elemAreChar) const {
+ if (_intervals.empty()) {
+ return "{}";
+ }
+
+ std::stringstream ss;
+ size_t effectiveSize = size();
+ if (effectiveSize > 1) {
+ ss << "{";
+ }
+
+ bool firstEntry = true;
+ for (const auto &interval : _intervals) {
+ if (!firstEntry)
+ ss << ", ";
+ firstEntry = false;
+
+ ssize_t a = interval.a;
+ ssize_t b = interval.b;
+ if (a == b) {
+ if (a == -1) {
+ ss << "<EOF>";
+ } else if (elemAreChar) {
+ ss << "'" << static_cast<char>(a) << "'";
+ } else {
+ ss << a;
+ }
+ } else {
+ if (elemAreChar) {
+ ss << "'" << static_cast<char>(a) << "'..'" << static_cast<char>(b) << "'";
+ } else {
+ ss << a << ".." << b;
+ }
+ }
+ }
+ if (effectiveSize > 1) {
+ ss << "}";
+ }
+
+ return ss.str();
+}
+
+std::string IntervalSet::toString(const dfa::Vocabulary &vocabulary) const {
+ if (_intervals.empty()) {
+ return "{}";
+ }
+
+ std::stringstream ss;
+ size_t effectiveSize = size();
+ if (effectiveSize > 1) {
+ ss << "{";
+ }
+
+ bool firstEntry = true;
+ for (const auto &interval : _intervals) {
+ if (!firstEntry)
+ ss << ", ";
+ firstEntry = false;
+
+ ssize_t a = interval.a;
+ ssize_t b = interval.b;
+ if (a == b) {
+ ss << elementName(vocabulary, a);
+ } else {
+ for (ssize_t i = a; i <= b; i++) {
+ if (i > a) {
+ ss << ", ";
+ }
+ ss << elementName(vocabulary, i);
+ }
+ }
+ }
+ if (effectiveSize > 1) {
+ ss << "}";
+ }
+
+ return ss.str();
+}
+
+std::string IntervalSet::elementName(const dfa::Vocabulary &vocabulary, ssize_t a) const {
+ if (a == -1) {
+ return "<EOF>";
+ } else if (a == -2) {
+ return "<EPSILON>";
+ } else {
+ return vocabulary.getDisplayName(a);
+ }
+}
+
+size_t IntervalSet::size() const {
+ size_t result = 0;
+ for (const auto &interval : _intervals) {
+ result += size_t(interval.b - interval.a + 1);
+ }
+ return result;
+}
+
+std::vector<ssize_t> IntervalSet::toList() const {
+ std::vector<ssize_t> result;
+ for (const auto &interval : _intervals) {
+ ssize_t a = interval.a;
+ ssize_t b = interval.b;
+ for (ssize_t v = a; v <= b; v++) {
+ result.push_back(v);
+ }
+ }
+ return result;
+}
+
+std::set<ssize_t> IntervalSet::toSet() const {
+ std::set<ssize_t> result;
+ for (const auto &interval : _intervals) {
+ ssize_t a = interval.a;
+ ssize_t b = interval.b;
+ for (ssize_t v = a; v <= b; v++) {
+ result.insert(v);
+ }
+ }
+ return result;
+}
+
+ssize_t IntervalSet::get(size_t i) const {
+ size_t index = 0;
+ for (const auto &interval : _intervals) {
+ ssize_t a = interval.a;
+ ssize_t b = interval.b;
+ for (ssize_t v = a; v <= b; v++) {
+ if (index == i) {
+ return v;
+ }
+ index++;
+ }
+ }
+ return -1;
+}
+
+void IntervalSet::remove(ssize_t el) {
+ for (size_t i = 0; i < _intervals.size(); ++i) {
+ Interval &interval = _intervals[i];
+ ssize_t a = interval.a;
+ ssize_t b = interval.b;
+ if (el < a) {
+ break; // list is sorted and el is before this interval; not here
+ }
+
+ // if whole interval x..x, rm
+ if (el == a && el == b) {
+ _intervals.erase(_intervals.begin() + (long)i);
+ break;
+ }
+ // if on left edge x..b, adjust left
+ if (el == a) {
+ interval.a++;
+ break;
+ }
+ // if on right edge a..x, adjust right
+ if (el == b) {
+ interval.b--;
+ break;
+ }
+ // if in middle a..x..b, split interval
+ if (el > a && el < b) { // found in this interval
+ ssize_t oldb = interval.b;
+ interval.b = el - 1; // [a..x-1]
+ add(el + 1, oldb); // add [x+1..b]
+
+ break; // ml: not in the Java code but I believe we also should stop searching here, as we found x.
+ }
+ }
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/IntervalSet.h b/contrib/libs/antlr4_cpp_runtime/src/misc/IntervalSet.h
new file mode 100644
index 0000000000..49565dc691
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/IntervalSet.h
@@ -0,0 +1,188 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "misc/Interval.h"
+#include "Exceptions.h"
+
+namespace antlr4 {
+namespace misc {
+
+ /**
+ * This class implements the {@link IntSet} backed by a sorted array of
+ * non-overlapping intervals. It is particularly efficient for representing
+ * large collections of numbers, where the majority of elements appear as part
+ * of a sequential range of numbers that are all part of the set. For example,
+ * the set { 1, 2, 3, 4, 7, 8 } may be represented as { [1, 4], [7, 8] }.
+ *
+ * <p>
+ * This class is able to represent sets containing any combination of values in
+ * the range {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}
+ * (inclusive).</p>
+ */
+ class ANTLR4CPP_PUBLIC IntervalSet final {
+ public:
+ static IntervalSet const COMPLETE_CHAR_SET;
+ static IntervalSet const EMPTY_SET;
+
+ private:
+ /// The list of sorted, disjoint intervals.
+ std::vector<Interval> _intervals;
+
+ explicit IntervalSet(std::vector<Interval>&& intervals);
+
+ public:
+ IntervalSet();
+ IntervalSet(IntervalSet const& set);
+ IntervalSet(IntervalSet&& set);
+
+ template<typename T1, typename... T_NEXT>
+ IntervalSet(int, T1 t1, T_NEXT&&... next) : IntervalSet() {
+ // The first int argument is an ignored count for compatibility
+ // with the previous varargs based interface.
+ addItems(t1, std::forward<T_NEXT>(next)...);
+ }
+
+ IntervalSet& operator=(IntervalSet const& set);
+ IntervalSet& operator=(IntervalSet&& set);
+
+ /// Create a set with a single element, el.
+ static IntervalSet of(ssize_t a);
+
+ /// Create a set with all ints within range [a..b] (inclusive)
+ static IntervalSet of(ssize_t a, ssize_t b);
+
+ void clear();
+
+ /// Add a single element to the set. An isolated element is stored
+ /// as a range el..el.
+ void add(ssize_t el);
+
+ /// Add interval; i.e., add all integers from a to b to set.
+ /// If b<a, do nothing.
+ /// Keep list in sorted order (by left range value).
+ /// If overlap, combine ranges. For example,
+ /// If this is {1..5, 10..20}, adding 6..7 yields
+ /// {1..5, 6..7, 10..20}. Adding 4..8 yields {1..8, 10..20}.
+ void add(ssize_t a, ssize_t b);
+
+ /// combine all sets in the array returned the or'd value
+ static IntervalSet Or(const std::vector<IntervalSet> &sets);
+
+ // Copy on write so we can cache a..a intervals and sets of that.
+ void add(const Interval &addition);
+ IntervalSet& addAll(const IntervalSet &set);
+
+ template<typename T1, typename... T_NEXT>
+ void addItems(T1 t1, T_NEXT&&... next) {
+ add(t1);
+ addItems(std::forward<T_NEXT>(next)...);
+ }
+
+ IntervalSet complement(ssize_t minElement, ssize_t maxElement) const;
+
+ /// Given the set of possible values (rather than, say UNICODE or MAXINT),
+ /// return a new set containing all elements in vocabulary, but not in
+ /// this. The computation is (vocabulary - this).
+ ///
+ /// 'this' is assumed to be either a subset or equal to vocabulary.
+ IntervalSet complement(const IntervalSet &vocabulary) const;
+
+ /// Compute this-other via this&~other.
+ /// Return a new set containing all elements in this but not in other.
+ /// other is assumed to be a subset of this;
+ /// anything that is in other but not in this will be ignored.
+ IntervalSet subtract(const IntervalSet &other) const;
+
+ /**
+ * Compute the set difference between two interval sets. The specific
+ * operation is {@code left - right}. If either of the input sets is
+ * {@code null}, it is treated as though it was an empty set.
+ */
+ static IntervalSet subtract(const IntervalSet &left, const IntervalSet &right);
+
+ IntervalSet Or(const IntervalSet &a) const;
+
+ /// Return a new set with the intersection of this set with other. Because
+ /// the intervals are sorted, we can use an iterator for each list and
+ /// just walk them together. This is roughly O(min(n,m)) for interval
+ /// list lengths n and m.
+ IntervalSet And(const IntervalSet &other) const;
+
+ /// Is el in any range of this set?
+ bool contains(ssize_t el) const;
+
+ /// return true if this set has no members
+ bool isEmpty() const;
+
+ /// If this set is a single integer, return it otherwise Token.INVALID_TYPE.
+ ssize_t getSingleElement() const;
+
+ /**
+ * Returns the maximum value contained in the set.
+ *
+ * @return the maximum value contained in the set. If the set is empty, this
+ * method returns {@link Token#INVALID_TYPE}.
+ */
+ ssize_t getMaxElement() const;
+
+ /**
+ * Returns the minimum value contained in the set.
+ *
+ * @return the minimum value contained in the set. If the set is empty, this
+ * method returns {@link Token#INVALID_TYPE}.
+ */
+ ssize_t getMinElement() const;
+
+ /// <summary>
+ /// Return a list of Interval objects. </summary>
+ std::vector<Interval> const& getIntervals() const;
+
+ size_t hashCode() const;
+
+ /// Are two IntervalSets equal? Because all intervals are sorted
+ /// and disjoint, equals is a simple linear walk over both lists
+ /// to make sure they are the same.
+ bool operator == (const IntervalSet &other) const;
+ std::string toString() const;
+ std::string toString(bool elemAreChar) const;
+
+ std::string toString(const dfa::Vocabulary &vocabulary) const;
+
+ protected:
+ std::string elementName(const dfa::Vocabulary &vocabulary, ssize_t a) const;
+
+ public:
+ size_t size() const;
+ std::vector<ssize_t> toList() const;
+ std::set<ssize_t> toSet() const;
+
+ /// Get the ith element of ordered set. Used only by RandomPhrase so
+ /// don't bother to implement if you're not doing that for a new
+ /// ANTLR code gen target.
+ ssize_t get(size_t i) const;
+ void remove(ssize_t el);
+
+ private:
+ void addItems() { /* No-op */ }
+ };
+
+} // namespace atn
+} // namespace antlr4
+
+// Hash function for IntervalSet.
+
+namespace std {
+ using antlr4::misc::IntervalSet;
+
+ template <> struct hash<IntervalSet>
+ {
+ size_t operator() (const IntervalSet &x) const
+ {
+ return x.hashCode();
+ }
+ };
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/MurmurHash.cpp b/contrib/libs/antlr4_cpp_runtime/src/misc/MurmurHash.cpp
new file mode 100644
index 0000000000..09072c9f7e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/MurmurHash.cpp
@@ -0,0 +1,120 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+
+#include "misc/MurmurHash.h"
+
+using namespace antlr4::misc;
+
+// A variation of the MurmurHash3 implementation (https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp)
+// Here we unrolled the loop used there into individual calls to update(), as we usually hash object fields
+// instead of entire buffers.
+
+// Platform-specific functions and macros
+
+// Microsoft Visual Studio
+
+#if defined(_MSC_VER)
+
+#include <stdlib.h>
+
+#define ROTL32(x,y) _rotl(x,y)
+#define ROTL64(x,y) _rotl64(x,y)
+
+#elif ANTLR4CPP_HAVE_BUILTIN(__builtin_rotateleft32) && ANTLR4CPP_HAVE_BUILTIN(__builtin_rotateleft64)
+
+#define ROTL32(x, y) __builtin_rotateleft32(x, y)
+#define ROTL64(x, y) __builtin_rotateleft64(x, y)
+
+#else // defined(_MSC_VER)
+
+// Other compilers
+
+namespace {
+
+constexpr uint32_t ROTL32(uint32_t x, int r) {
+ return (x << r) | (x >> (32 - r));
+}
+constexpr uint64_t ROTL64(uint64_t x, int r) {
+ return (x << r) | (x >> (64 - r));
+}
+
+}
+
+#endif // !defined(_MSC_VER)
+
+#if SIZE_MAX == UINT64_MAX
+
+size_t MurmurHash::update(size_t hash, size_t value) {
+ size_t k1 = value;
+ k1 *= UINT64_C(0x87c37b91114253d5);
+ k1 = ROTL64(k1, 31);
+ k1 *= UINT64_C(0x4cf5ad432745937f);
+
+ hash ^= k1;
+ hash = ROTL64(hash, 27);
+ hash = hash * 5 + UINT64_C(0x52dce729);
+
+ return hash;
+}
+
+size_t MurmurHash::finish(size_t hash, size_t entryCount) {
+ hash ^= entryCount * 8;
+ hash ^= hash >> 33;
+ hash *= UINT64_C(0xff51afd7ed558ccd);
+ hash ^= hash >> 33;
+ hash *= UINT64_C(0xc4ceb9fe1a85ec53);
+ hash ^= hash >> 33;
+ return hash;
+}
+
+#elif SIZE_MAX == UINT32_MAX
+
+size_t MurmurHash::update(size_t hash, size_t value) {
+ size_t k1 = value;
+ k1 *= UINT32_C(0xCC9E2D51);
+ k1 = ROTL32(k1, 15);
+ k1 *= UINT32_C(0x1B873593);
+
+ hash ^= k1;
+ hash = ROTL32(hash, 13);
+ hash = hash * 5 + UINT32_C(0xE6546B64);
+
+ return hash;
+}
+
+size_t MurmurHash::finish(size_t hash, size_t entryCount) {
+ hash ^= entryCount * 4;
+ hash ^= hash >> 16;
+ hash *= UINT32_C(0x85EBCA6B);
+ hash ^= hash >> 13;
+ hash *= UINT32_C(0xC2B2AE35);
+ hash ^= hash >> 16;
+ return hash;
+}
+
+#else
+#error "Expected sizeof(size_t) to be 4 or 8."
+#endif
+
+size_t MurmurHash::update(size_t hash, const void *data, size_t size) {
+ size_t value;
+ const uint8_t *bytes = static_cast<const uint8_t*>(data);
+ while (size >= sizeof(size_t)) {
+ std::memcpy(&value, bytes, sizeof(size_t));
+ hash = update(hash, value);
+ bytes += sizeof(size_t);
+ size -= sizeof(size_t);
+ }
+ if (size != 0) {
+ value = 0;
+ std::memcpy(&value, bytes, size);
+ hash = update(hash, value);
+ }
+ return hash;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/MurmurHash.h b/contrib/libs/antlr4_cpp_runtime/src/misc/MurmurHash.h
new file mode 100644
index 0000000000..cde7ac7906
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/MurmurHash.h
@@ -0,0 +1,102 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <type_traits>
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace misc {
+
+ class ANTLR4CPP_PUBLIC MurmurHash final {
+ private:
+ static constexpr size_t DEFAULT_SEED = 0;
+
+ /// Initialize the hash using the default seed value.
+ /// Returns the intermediate hash value.
+ public:
+ static size_t initialize() { return initialize(DEFAULT_SEED); }
+
+ /// Initialize the hash using the specified seed.
+ static size_t initialize(size_t seed) { return seed; }
+
+ /// Update the intermediate hash value for the next input {@code value}.
+ /// <param name="hash"> the intermediate hash value </param>
+ /// <param name="value"> the value to add to the current hash </param>
+ /// Returns the updated intermediate hash value.
+ static size_t update(size_t hash, size_t value);
+
+ /**
+ * Update the intermediate hash value for the next input {@code value}.
+ *
+ * @param hash the intermediate hash value
+ * @param value the value to add to the current hash
+ * @return the updated intermediate hash value
+ */
+ template <class T>
+ static size_t update(size_t hash, Ref<T> const& value) {
+ return update(hash, value != nullptr ? value->hashCode() : 0);
+ }
+
+ template <class T>
+ static size_t update(size_t hash, T *value) {
+ return update(hash, value != nullptr ? value->hashCode() : 0);
+ }
+
+ static size_t update(size_t hash, const void *data, size_t size);
+
+ template <typename T>
+ static size_t update(size_t hash, const T *data, size_t size) {
+ return update(hash, static_cast<const void*>(data), size * sizeof(std::remove_reference_t<T>));
+ }
+
+ /// <summary>
+ /// Apply the final computation steps to the intermediate value {@code hash}
+ /// to form the final result of the MurmurHash 3 hash function.
+ /// </summary>
+ /// <param name="hash"> the intermediate hash value </param>
+ /// <param name="entryCount"> the number of calls to update() before calling finish() </param>
+ /// <returns> the final hash result </returns>
+ static size_t finish(size_t hash, size_t entryCount);
+
+ /// Utility function to compute the hash code of an array using the MurmurHash3 algorithm.
+ ///
+ /// @param <T> the array element type </param>
+ /// <param name="data"> the array data </param>
+ /// <param name="seed"> the seed for the MurmurHash algorithm </param>
+ /// <returns> the hash code of the data </returns>
+ template<typename T> // where T is C array type
+ static size_t hashCode(const std::vector<Ref<T>> &data, size_t seed = DEFAULT_SEED) {
+ size_t hash = initialize(seed);
+ for (auto &entry : data) {
+ hash = update(hash, entry);
+ }
+ return finish(hash, data.size());
+ }
+
+ static size_t hashCode(const void *data, size_t size, size_t seed = DEFAULT_SEED) {
+ size_t hash = initialize(seed);
+ hash = update(hash, data, size);
+ return finish(hash, size);
+ }
+
+ template <typename T>
+ static size_t hashCode(const T *data, size_t size, size_t seed = DEFAULT_SEED) {
+ return hashCode(static_cast<const void*>(data), size * sizeof(std::remove_reference_t<T>), seed);
+ }
+
+ private:
+ MurmurHash() = delete;
+
+ MurmurHash(const MurmurHash&) = delete;
+
+ MurmurHash& operator=(const MurmurHash&) = delete;
+ };
+
+} // namespace atn
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/Predicate.cpp b/contrib/libs/antlr4_cpp_runtime/src/misc/Predicate.cpp
new file mode 100644
index 0000000000..c35f1921c4
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/Predicate.cpp
@@ -0,0 +1,4 @@
+#include "misc/Predicate.h"
+
+antlr4::misc::Predicate::~Predicate() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/misc/Predicate.h b/contrib/libs/antlr4_cpp_runtime/src/misc/Predicate.h
new file mode 100644
index 0000000000..1032d53fed
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/misc/Predicate.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace misc {
+
+ class ANTLR4CPP_PUBLIC Predicate {
+ public:
+ virtual ~Predicate();
+
+ virtual bool test(tree::ParseTree *t) = 0;
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Any.cpp b/contrib/libs/antlr4_cpp_runtime/src/support/Any.cpp
new file mode 100644
index 0000000000..a1ed50d456
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Any.cpp
@@ -0,0 +1,8 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Any.h"
+
+using namespace antlrcpp;
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Any.h b/contrib/libs/antlr4_cpp_runtime/src/support/Any.h
new file mode 100644
index 0000000000..fa5df58946
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Any.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+// A standard C++ class loosely modeled after boost::Any.
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlrcpp {
+
+ using Any = std::any;
+
+} // namespace antlrcpp
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Arrays.cpp b/contrib/libs/antlr4_cpp_runtime/src/support/Arrays.cpp
new file mode 100644
index 0000000000..b3c4f94f2f
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Arrays.cpp
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ParseTree.h"
+#include "Exceptions.h"
+
+#include "support/Arrays.h"
+
+using namespace antlrcpp;
+
+std::string Arrays::listToString(const std::vector<std::string> &list, const std::string &separator)
+{
+ std::stringstream ss;
+ bool firstEntry = true;
+
+ ss << '[';
+ for (const auto &entry : list) {
+ ss << entry;
+ if (firstEntry) {
+ ss << separator;
+ firstEntry = false;
+ }
+ }
+
+ ss << ']';
+ return ss.str();
+}
+
+template <>
+std::string Arrays::toString(const std::vector<antlr4::tree::ParseTree*> &source) {
+ std::string result = "[";
+ bool firstEntry = true;
+ for (auto *value : source) {
+ result += value->toStringTree();
+ if (firstEntry) {
+ result += ", ";
+ firstEntry = false;
+ }
+ }
+ return result + "]";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Arrays.h b/contrib/libs/antlr4_cpp_runtime/src/support/Arrays.h
new file mode 100644
index 0000000000..04b852d986
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Arrays.h
@@ -0,0 +1,149 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlrcpp {
+
+ class ANTLR4CPP_PUBLIC Arrays {
+ public:
+
+ static std::string listToString(const std::vector<std::string> &list, const std::string &separator);
+
+ template <typename T>
+ static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i)
+ if (!(a[i] == b[i]))
+ return false;
+
+ return true;
+ }
+
+ template <typename T>
+ static bool equals(const std::vector<T *> &a, const std::vector<T *> &b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (!a[i] && !b[i])
+ continue;
+ if (!a[i] || !b[i])
+ return false;
+ if (a[i] == b[i])
+ continue;
+
+ if (!(*a[i] == *b[i]))
+ return false;
+ }
+
+ return true;
+ }
+
+ template <typename T>
+ static bool equals(const std::vector<Ref<T>> &a, const std::vector<Ref<T>> &b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (!a[i] && !b[i])
+ continue;
+ if (!a[i] || !b[i])
+ return false;
+ if (a[i] == b[i])
+ continue;
+
+ if (!(*a[i] == *b[i]))
+ return false;
+ }
+
+ return true;
+ }
+
+ template <typename T>
+ static bool equals(const std::vector<std::unique_ptr<T>> &a, const std::vector<std::unique_ptr<T>> &b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (!a[i] && !b[i])
+ continue;
+ if (!a[i] || !b[i])
+ return false;
+ if (a[i] == b[i])
+ continue;
+
+ if (!(*a[i] == *b[i]))
+ return false;
+ }
+
+ return true;
+ }
+
+ template <typename T>
+ static std::string toString(const std::vector<T> &source) {
+ std::string result = "[";
+ bool firstEntry = true;
+ for (auto &value : source) {
+ result += value.toString();
+ if (firstEntry) {
+ result += ", ";
+ firstEntry = false;
+ }
+ }
+ return result + "]";
+ }
+
+ template <typename T>
+ static std::string toString(const std::vector<Ref<T>> &source) {
+ std::string result = "[";
+ bool firstEntry = true;
+ for (auto &value : source) {
+ result += value->toString();
+ if (firstEntry) {
+ result += ", ";
+ firstEntry = false;
+ }
+ }
+ return result + "]";
+ }
+
+ template <typename T>
+ static std::string toString(const std::vector<std::unique_ptr<T>> &source) {
+ std::string result = "[";
+ bool firstEntry = true;
+ for (auto &value : source) {
+ result += value->toString();
+ if (firstEntry) {
+ result += ", ";
+ firstEntry = false;
+ }
+ }
+ return result + "]";
+ }
+
+ template <typename T>
+ static std::string toString(const std::vector<T *> &source) {
+ std::string result = "[";
+ bool firstEntry = true;
+ for (auto value : source) {
+ result += value->toString();
+ if (firstEntry) {
+ result += ", ";
+ firstEntry = false;
+ }
+ }
+ return result + "]";
+ }
+
+ };
+
+ template <>
+ std::string Arrays::toString(const std::vector<antlr4::tree::ParseTree *> &source);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/BitSet.h b/contrib/libs/antlr4_cpp_runtime/src/support/BitSet.h
new file mode 100644
index 0000000000..bb30364be0
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/BitSet.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlrcpp {
+
+ class ANTLR4CPP_PUBLIC BitSet : public std::bitset<2048> {
+ public:
+ size_t nextSetBit(size_t pos) const {
+ for (size_t i = pos; i < size(); i++){
+ if (test(i)) {
+ return i;
+ }
+ }
+
+ return INVALID_INDEX;
+ }
+
+ // Prints a list of every index for which the bitset contains a bit in true.
+ friend std::wostream& operator << (std::wostream& os, const BitSet& obj)
+ {
+ os << "{";
+ size_t total = obj.count();
+ for (size_t i = 0; i < obj.size(); i++){
+ if (obj.test(i)){
+ os << i;
+ --total;
+ if (total > 1){
+ os << ", ";
+ }
+ }
+ }
+
+ os << "}";
+ return os;
+ }
+
+ static std::string subStringRepresentation(const std::vector<BitSet>::iterator &begin,
+ const std::vector<BitSet>::iterator &end) {
+ std::string result;
+ std::vector<BitSet>::iterator vectorIterator;
+
+ for (vectorIterator = begin; vectorIterator != end; vectorIterator++) {
+ result += vectorIterator->toString();
+ }
+ // Grab the end
+ result += end->toString();
+
+ return result;
+ }
+
+ std::string toString() const {
+ std::stringstream stream;
+ stream << "{";
+ bool valueAdded = false;
+ for (size_t i = 0; i < size(); ++i){
+ if (test(i)){
+ if (valueAdded) {
+ stream << ", ";
+ }
+ stream << i;
+ valueAdded = true;
+ }
+ }
+
+ stream << "}";
+ return stream.str();
+ }
+
+ };
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/CPPUtils.cpp b/contrib/libs/antlr4_cpp_runtime/src/support/CPPUtils.cpp
new file mode 100644
index 0000000000..95321b3dc1
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/CPPUtils.cpp
@@ -0,0 +1,207 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "support/CPPUtils.h"
+
+namespace antlrcpp {
+
+ std::string join(const std::vector<std::string> &strings, const std::string &separator) {
+ std::string str;
+ bool firstItem = true;
+ for (const std::string &s : strings) {
+ if (!firstItem) {
+ str.append(separator);
+ }
+ firstItem = false;
+ str.append(s);
+ }
+ return str;
+ }
+
+ std::map<std::string, size_t> toMap(const std::vector<std::string> &keys) {
+ std::map<std::string, size_t> result;
+ for (size_t i = 0; i < keys.size(); ++i) {
+ result.insert({ keys[i], i });
+ }
+ return result;
+ }
+
+ std::string escapeWhitespace(std::string str, bool escapeSpaces) {
+ std::string result;
+ for (auto c : str) {
+ switch (c) {
+ case '\n':
+ result += "\\n";
+ break;
+
+ case '\r':
+ result += "\\r";
+ break;
+
+ case '\t':
+ result += "\\t";
+ break;
+
+ case ' ':
+ if (escapeSpaces) {
+ result += "\u00B7";
+ break;
+ }
+ result += c;
+ break;
+
+ default:
+ result += c;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ std::string toHexString(const int t) {
+ std::stringstream stream;
+ stream << std::uppercase << std::hex << t;
+ return stream.str();
+ }
+
+ std::string arrayToString(const std::vector<std::string> &data) {
+ std::string answer;
+ size_t toReserve = 0;
+ for (const auto &sub : data) {
+ toReserve += sub.size();
+ }
+ answer.reserve(toReserve);
+ for (const auto &sub: data) {
+ answer.append(sub);
+ }
+ return answer;
+ }
+
+ std::string replaceString(const std::string &s, const std::string &from, const std::string &to) {
+ std::string::size_type p;
+ std::string ss, res;
+
+ ss = s;
+ p = ss.find(from);
+ while (p != std::string::npos) {
+ if (p > 0)
+ res.append(ss.substr(0, p)).append(to);
+ else
+ res.append(to);
+ ss = ss.substr(p + from.size());
+ p = ss.find(from);
+ }
+ res.append(ss);
+
+ return res;
+ }
+
+ std::vector<std::string> split(const std::string &s, const std::string &sep, int count) {
+ std::vector<std::string> parts;
+ std::string ss = s;
+
+ std::string::size_type p;
+
+ if (s.empty())
+ return parts;
+
+ if (count == 0)
+ count= -1;
+
+ p = ss.find(sep);
+ while (!ss.empty() && p != std::string::npos && (count < 0 || count > 0)) {
+ parts.push_back(ss.substr(0, p));
+ ss = ss.substr(p+sep.size());
+
+ --count;
+ p = ss.find(sep);
+ }
+ parts.push_back(ss);
+
+ return parts;
+ }
+
+ //--------------------------------------------------------------------------------------------------
+
+ // Debugging helper. Adds indentation to all lines in the given string.
+ std::string indent(const std::string &s, const std::string &indentation, bool includingFirst) {
+ std::vector<std::string> parts = split(s, "\n", -1);
+ for (size_t i = 0; i < parts.size(); ++i) {
+ if (i == 0 && !includingFirst)
+ continue;
+ parts[i].insert(0, indentation);
+ }
+
+ return join(parts, "\n");
+ }
+
+ //--------------------------------------------------------------------------------------------------
+
+ // Recursively get the error from a, possibly nested, exception.
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023026
+ // No nested exceptions before VS 2015.
+ template <typename T>
+ std::exception_ptr get_nested(const T &/*e*/) {
+ try {
+ return nullptr;
+ }
+ catch (const std::bad_cast &) {
+ return nullptr;
+ }
+ }
+#else
+ template <typename T>
+ std::exception_ptr get_nested(const T &e) {
+ try {
+ auto nested = dynamic_cast<const std::nested_exception&>(e);
+ return nested.nested_ptr();
+ }
+ catch (const std::bad_cast &) {
+ return nullptr;
+ }
+ }
+#endif
+
+ std::string what(std::exception_ptr eptr) {
+ if (!eptr) {
+ throw std::bad_exception();
+ }
+
+ std::string result;
+ std::size_t nestCount = 0;
+
+ next: {
+ try {
+ std::exception_ptr yeptr;
+ std::swap(eptr, yeptr);
+ std::rethrow_exception(yeptr);
+ }
+ catch (const std::exception &e) {
+ result += e.what();
+ eptr = get_nested(e);
+ }
+ catch (const std::string &e) {
+ result += e;
+ }
+ catch (const char *e) {
+ result += e;
+ }
+ catch (...) {
+ result += "cannot be determined";
+ }
+
+ if (eptr) {
+ result += " (";
+ ++nestCount;
+ goto next;
+ }
+ }
+
+ result += std::string(nestCount, ')');
+ return result;
+ }
+
+} // namespace antlrcpp
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/CPPUtils.h b/contrib/libs/antlr4_cpp_runtime/src/support/CPPUtils.h
new file mode 100644
index 0000000000..2eb1a36037
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/CPPUtils.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlrcpp {
+
+ ANTLR4CPP_PUBLIC std::string join(const std::vector<std::string> &strings, const std::string &separator);
+ ANTLR4CPP_PUBLIC std::map<std::string, size_t> toMap(const std::vector<std::string> &keys);
+ ANTLR4CPP_PUBLIC std::string escapeWhitespace(std::string str, bool escapeSpaces);
+ ANTLR4CPP_PUBLIC std::string toHexString(const int t);
+ ANTLR4CPP_PUBLIC std::string arrayToString(const std::vector<std::string> &data);
+ ANTLR4CPP_PUBLIC std::string replaceString(const std::string &s, const std::string &from, const std::string &to);
+ ANTLR4CPP_PUBLIC std::vector<std::string> split(const std::string &s, const std::string &sep, int count);
+ ANTLR4CPP_PUBLIC std::string indent(const std::string &s, const std::string &indentation, bool includingFirst = true);
+
+ // Using RAII + a lambda to implement a "finally" replacement.
+ template <typename OnEnd>
+ struct FinalAction {
+ FinalAction(OnEnd f) : _cleanUp { std::move(f) } {}
+ FinalAction(FinalAction &&other) :
+ _cleanUp(std::move(other._cleanUp)), _enabled(other._enabled) {
+ other._enabled = false; // Don't trigger the lambda after ownership has moved.
+ }
+ ~FinalAction() { if (_enabled) _cleanUp(); }
+
+ void disable() { _enabled = false; }
+ private:
+ OnEnd _cleanUp;
+ bool _enabled {true};
+ };
+
+ template <typename OnEnd>
+ FinalAction<OnEnd> finally(OnEnd f) {
+ return FinalAction<OnEnd>(std::move(f));
+ }
+
+ // Convenience functions to avoid lengthy dynamic_cast() != nullptr checks in many places.
+ template <typename T1, typename T2>
+ inline bool is(T2 *obj) { // For pointer types.
+ return dynamic_cast<typename std::add_const<T1>::type>(obj) != nullptr;
+ }
+
+ template <typename T1, typename T2>
+ inline bool is(Ref<T2> const& obj) { // For shared pointers.
+ return dynamic_cast<T1 *>(obj.get()) != nullptr;
+ }
+
+ template <typename T>
+ std::string toString(const T &o) {
+ std::stringstream ss;
+ // typeid gives the mangled class name, but that's all what's possible
+ // in a portable way.
+ ss << typeid(o).name() << "@" << std::hex << reinterpret_cast<uintptr_t>(&o);
+ return ss.str();
+ }
+
+ // Get the error text from an exception pointer or the current exception.
+ ANTLR4CPP_PUBLIC std::string what(std::exception_ptr eptr = std::current_exception());
+
+} // namespace antlrcpp
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Casts.h b/contrib/libs/antlr4_cpp_runtime/src/support/Casts.h
new file mode 100644
index 0000000000..2ded955dcd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Casts.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2012-2021 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cassert>
+#include <memory>
+#include <type_traits>
+
+namespace antlrcpp {
+
+ template <typename To, typename From>
+ To downCast(From* from) {
+ static_assert(std::is_pointer_v<To>, "Target type not a pointer.");
+ static_assert(std::is_base_of_v<From, std::remove_pointer_t<To>>, "Target type not derived from source type.");
+ #if !defined(__GNUC__) || defined(__GXX_RTTI)
+ assert(from == nullptr || dynamic_cast<To>(from) != nullptr);
+ #endif
+ return static_cast<To>(from);
+ }
+
+ template <typename To, typename From>
+ To downCast(From& from) {
+ static_assert(std::is_lvalue_reference_v<To>, "Target type not a lvalue reference.");
+ static_assert(std::is_base_of_v<From, std::remove_reference_t<To>>, "Target type not derived from source type.");
+ #if !defined(__GNUC__) || defined(__GXX_RTTI)
+ assert(dynamic_cast<std::add_pointer_t<std::remove_reference_t<To>>>(std::addressof(from)) != nullptr);
+ #endif
+ return static_cast<To>(from);
+ }
+
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Declarations.h b/contrib/libs/antlr4_cpp_runtime/src/support/Declarations.h
new file mode 100644
index 0000000000..8e960676cf
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Declarations.h
@@ -0,0 +1,161 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+namespace antlr4 {
+ class ANTLRErrorListener;
+ class ANTLRErrorStrategy;
+ class ANTLRFileStream;
+ class ANTLRInputStream;
+ class BailErrorStrategy;
+ class BaseErrorListener;
+ class BufferedTokenStream;
+ class CharStream;
+ class CommonToken;
+ class CommonTokenFactory;
+ class CommonTokenStream;
+ class ConsoleErrorListener;
+ class DefaultErrorStrategy;
+ class DiagnosticErrorListener;
+ class EmptyStackException;
+ class FailedPredicateException;
+ class IllegalArgumentException;
+ class IllegalStateException;
+ class InputMismatchException;
+ class IntStream;
+ class InterpreterRuleContext;
+ class Lexer;
+ class LexerInterpreter;
+ class LexerNoViableAltException;
+ class ListTokenSource;
+ class NoSuchElementException;
+ class NoViableAltException;
+ class NullPointerException;
+ class ParseCancellationException;
+ class Parser;
+ class ParserInterpreter;
+ class ParserRuleContext;
+ class ProxyErrorListener;
+ class RecognitionException;
+ class Recognizer;
+ class RuleContext;
+ class Token;
+ template<typename Symbol> class TokenFactory;
+ class TokenSource;
+ class TokenStream;
+ class TokenStreamRewriter;
+ class UnbufferedCharStream;
+ class UnbufferedTokenStream;
+ class WritableToken;
+
+ namespace misc {
+ class InterpreterDataReader;
+ class Interval;
+ class IntervalSet;
+ class MurmurHash;
+ class Utils;
+ class Predicate;
+ }
+ namespace atn {
+ class ATN;
+ class ATNConfig;
+ class ATNConfigSet;
+ class ATNDeserializationOptions;
+ class ATNDeserializer;
+ class ATNSerializer;
+ class ATNSimulator;
+ class ATNState;
+ enum class ATNType;
+ class ActionTransition;
+ class ArrayPredictionContext;
+ class AtomTransition;
+ class BasicBlockStartState;
+ class BasicState;
+ class BlockEndState;
+ class BlockStartState;
+ class DecisionState;
+ class EpsilonTransition;
+ class LL1Analyzer;
+ class LexerAction;
+ class LexerActionExecutor;
+ class LexerATNConfig;
+ class LexerATNSimulator;
+ class LexerMoreAction;
+ class LexerPopModeAction;
+ class LexerSkipAction;
+ class LookaheadEventInfo;
+ class LoopEndState;
+ class NotSetTransition;
+ class OrderedATNConfigSet;
+ class ParseInfo;
+ class ParserATNSimulator;
+ class PlusBlockStartState;
+ class PlusLoopbackState;
+ class PrecedencePredicateTransition;
+ class PredicateTransition;
+ class PredictionContext;
+ enum class PredictionMode;
+ class PredictionModeClass;
+ class RangeTransition;
+ class RuleStartState;
+ class RuleStopState;
+ class RuleTransition;
+ class SemanticContext;
+ class SetTransition;
+ class SingletonPredictionContext;
+ class StarBlockStartState;
+ class StarLoopEntryState;
+ class StarLoopbackState;
+ class TokensStartState;
+ class Transition;
+ class WildcardTransition;
+ }
+ namespace dfa {
+ class DFA;
+ class DFASerializer;
+ class DFAState;
+ class LexerDFASerializer;
+ class Vocabulary;
+ }
+ namespace tree {
+ class AbstractParseTreeVisitor;
+ class ErrorNode;
+ class ErrorNodeImpl;
+ class ParseTree;
+ class ParseTreeListener;
+ template<typename T> class ParseTreeProperty;
+ class ParseTreeVisitor;
+ class ParseTreeWalker;
+ class SyntaxTree;
+ class TerminalNode;
+ class TerminalNodeImpl;
+ class Tree;
+ class Trees;
+
+ namespace pattern {
+ class Chunk;
+ class ParseTreeMatch;
+ class ParseTreePattern;
+ class ParseTreePatternMatcher;
+ class RuleTagToken;
+ class TagChunk;
+ class TextChunk;
+ class TokenTagToken;
+ }
+
+ namespace xpath {
+ class XPath;
+ class XPathElement;
+ class XPathLexerErrorListener;
+ class XPathRuleAnywhereElement;
+ class XPathRuleElement;
+ class XPathTokenAnywhereElement;
+ class XPathTokenElement;
+ class XPathWildcardAnywhereElement;
+ class XPathWildcardElement;
+ }
+ }
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/StringUtils.cpp b/contrib/libs/antlr4_cpp_runtime/src/support/StringUtils.cpp
new file mode 100644
index 0000000000..9ee274c8de
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/StringUtils.cpp
@@ -0,0 +1,38 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "support/StringUtils.h"
+
+namespace antlrcpp {
+
+ std::string escapeWhitespace(std::string_view in) {
+ std::string out;
+ escapeWhitespace(out, in);
+ out.shrink_to_fit();
+ return out;
+ }
+
+ std::string& escapeWhitespace(std::string& out, std::string_view in) {
+ out.reserve(in.size()); // Best case, no escaping.
+ for (const auto &c : in) {
+ switch (c) {
+ case '\t':
+ out.append("\\t");
+ break;
+ case '\r':
+ out.append("\\r");
+ break;
+ case '\n':
+ out.append("\\n");
+ break;
+ default:
+ out.push_back(c);
+ break;
+ }
+ }
+ return out;
+ }
+
+} // namespace antrlcpp
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/StringUtils.h b/contrib/libs/antlr4_cpp_runtime/src/support/StringUtils.h
new file mode 100644
index 0000000000..aee0d46d6e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/StringUtils.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlrcpp {
+
+ ANTLR4CPP_PUBLIC std::string escapeWhitespace(std::string_view in);
+
+ ANTLR4CPP_PUBLIC std::string& escapeWhitespace(std::string& out, std::string_view in);
+
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Unicode.h b/contrib/libs/antlr4_cpp_runtime/src/support/Unicode.h
new file mode 100644
index 0000000000..f0f84375ad
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Unicode.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2021 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlrcpp {
+
+ class ANTLR4CPP_PUBLIC Unicode final {
+ public:
+ static constexpr char32_t REPLACEMENT_CHARACTER = 0xfffd;
+
+ static constexpr bool isValid(char32_t codePoint) {
+ return codePoint < 0xd800 || (codePoint > 0xdfff && codePoint <= 0x10ffff);
+ }
+
+ private:
+ Unicode() = delete;
+ Unicode(const Unicode&) = delete;
+ Unicode(Unicode&&) = delete;
+ Unicode& operator=(const Unicode&) = delete;
+ Unicode& operator=(Unicode&&) = delete;
+ };
+
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Utf8.cpp b/contrib/libs/antlr4_cpp_runtime/src/support/Utf8.cpp
new file mode 100644
index 0000000000..294e9f1b21
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Utf8.cpp
@@ -0,0 +1,242 @@
+/* Copyright (c) 2021 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include <cassert>
+#include <cstdint>
+
+#include "support/Utf8.h"
+#include "support/Unicode.h"
+
+// The below implementation is based off of https://github.com/google/cel-cpp/internal/utf8.cc,
+// which is itself based off of https://go.googlesource.com/go/+/refs/heads/master/src/unicode/utf8/utf8.go.
+// If for some reason you feel the need to copy this implementation, please retain a comment
+// referencing the two source files and giving credit, as well as maintaining any and all
+// obligations required by the BSD 3-clause license that governs this file.
+
+namespace antlrcpp {
+
+namespace {
+
+#undef SELF
+ constexpr uint8_t SELF = 0x80;
+
+#undef LOW
+ constexpr uint8_t LOW = 0x80;
+#undef HIGH
+ constexpr uint8_t HIGH = 0xbf;
+
+#undef MASKX
+ constexpr uint8_t MASKX = 0x3f;
+#undef MASK2
+ constexpr uint8_t MASK2 = 0x1f;
+#undef MASK3
+ constexpr uint8_t MASK3 = 0xf;
+#undef MASK4
+ constexpr uint8_t MASK4 = 0x7;
+
+#undef TX
+ constexpr uint8_t TX = 0x80;
+#undef T2
+ constexpr uint8_t T2 = 0xc0;
+#undef T3
+ constexpr uint8_t T3 = 0xe0;
+#undef T4
+ constexpr uint8_t T4 = 0xf0;
+
+#undef XX
+ constexpr uint8_t XX = 0xf1;
+#undef AS
+ constexpr uint8_t AS = 0xf0;
+#undef S1
+ constexpr uint8_t S1 = 0x02;
+#undef S2
+ constexpr uint8_t S2 = 0x13;
+#undef S3
+ constexpr uint8_t S3 = 0x03;
+#undef S4
+ constexpr uint8_t S4 = 0x23;
+#undef S5
+ constexpr uint8_t S5 = 0x34;
+#undef S6
+ constexpr uint8_t S6 = 0x04;
+#undef S7
+ constexpr uint8_t S7 = 0x44;
+
+ // NOLINTBEGIN
+ // clang-format off
+#undef LEADING
+ constexpr uint8_t LEADING[256] = {
+ // 1 2 3 4 5 6 7 8 9 A B C D E F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x00-0x0F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x10-0x1F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x20-0x2F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x30-0x3F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x40-0x4F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x50-0x5F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x60-0x6F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x70-0x7F
+ // 1 2 3 4 5 6 7 8 9 A B C D E F
+ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0x80-0x8F
+ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0x90-0x9F
+ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0xA0-0xAF
+ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0xB0-0xBF
+ XX, XX, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, // 0xC0-0xCF
+ S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, // 0xD0-0xDF
+ S2, S3, S3, S3, S3, S3, S3, S3, S3, S3, S3, S3, S3, S4, S3, S3, // 0xE0-0xEF
+ S5, S6, S6, S6, S7, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0xF0-0xFF
+ };
+ // clang-format on
+ // NOLINTEND
+
+#undef ACCEPT
+ constexpr std::pair<uint8_t, uint8_t> ACCEPT[16] = {
+ {LOW, HIGH}, {0xa0, HIGH}, {LOW, 0x9f}, {0x90, HIGH},
+ {LOW, 0x8f}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0},
+ {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0},
+ {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0},
+ };
+
+} // namespace
+
+ std::pair<char32_t, size_t> Utf8::decode(std::string_view input) {
+ assert(!input.empty());
+ const auto b = static_cast<uint8_t>(input.front());
+ input.remove_prefix(1);
+ if (b < SELF) {
+ return {static_cast<char32_t>(b), 1};
+ }
+ const auto leading = LEADING[b];
+ if (leading == XX) {
+ return {Unicode::REPLACEMENT_CHARACTER, 1};
+ }
+ auto size = static_cast<size_t>(leading & 7) - 1;
+ if (size > input.size()) {
+ return {Unicode::REPLACEMENT_CHARACTER, 1};
+ }
+ const auto& accept = ACCEPT[leading >> 4];
+ const auto b1 = static_cast<uint8_t>(input.front());
+ input.remove_prefix(1);
+ if (b1 < accept.first || b1 > accept.second) {
+ return {Unicode::REPLACEMENT_CHARACTER, 1};
+ }
+ if (size <= 1) {
+ return {(static_cast<char32_t>(b & MASK2) << 6) |
+ static_cast<char32_t>(b1 & MASKX),
+ 2};
+ }
+ const auto b2 = static_cast<uint8_t>(input.front());
+ input.remove_prefix(1);
+ if (b2 < LOW || b2 > HIGH) {
+ return {Unicode::REPLACEMENT_CHARACTER, 1};
+ }
+ if (size <= 2) {
+ return {(static_cast<char32_t>(b & MASK3) << 12) |
+ (static_cast<char32_t>(b1 & MASKX) << 6) |
+ static_cast<char32_t>(b2 & MASKX),
+ 3};
+ }
+ const auto b3 = static_cast<uint8_t>(input.front());
+ input.remove_prefix(1);
+ if (b3 < LOW || b3 > HIGH) {
+ return {Unicode::REPLACEMENT_CHARACTER, 1};
+ }
+ return {(static_cast<char32_t>(b & MASK4) << 18) |
+ (static_cast<char32_t>(b1 & MASKX) << 12) |
+ (static_cast<char32_t>(b2 & MASKX) << 6) |
+ static_cast<char32_t>(b3 & MASKX),
+ 4};
+ }
+
+ std::optional<std::u32string> Utf8::strictDecode(std::string_view input) {
+ std::u32string output;
+ char32_t codePoint;
+ size_t codeUnits;
+ output.reserve(input.size()); // Worst case is each byte is a single Unicode code point.
+ for (size_t index = 0; index < input.size(); index += codeUnits) {
+ std::tie(codePoint, codeUnits) = Utf8::decode(input.substr(index));
+ if (codePoint == Unicode::REPLACEMENT_CHARACTER && codeUnits == 1) {
+ // Condition is only met when an illegal byte sequence is encountered. See Utf8::decode.
+ return std::nullopt;
+ }
+ output.push_back(codePoint);
+ }
+ output.shrink_to_fit();
+ return output;
+ }
+
+ std::u32string Utf8::lenientDecode(std::string_view input) {
+ std::u32string output;
+ char32_t codePoint;
+ size_t codeUnits;
+ output.reserve(input.size()); // Worst case is each byte is a single Unicode code point.
+ for (size_t index = 0; index < input.size(); index += codeUnits) {
+ std::tie(codePoint, codeUnits) = Utf8::decode(input.substr(index));
+ output.push_back(codePoint);
+ }
+ output.shrink_to_fit();
+ return output;
+ }
+
+ std::string& Utf8::encode(std::string* buffer, char32_t codePoint) {
+ assert(buffer != nullptr);
+ if (!Unicode::isValid(codePoint)) {
+ codePoint = Unicode::REPLACEMENT_CHARACTER;
+ }
+ if (codePoint <= 0x7f) {
+ buffer->push_back(static_cast<char>(static_cast<uint8_t>(codePoint)));
+ } else if (codePoint <= 0x7ff) {
+ buffer->push_back(
+ static_cast<char>(T2 | static_cast<uint8_t>(codePoint >> 6)));
+ buffer->push_back(
+ static_cast<char>(TX | (static_cast<uint8_t>(codePoint) & MASKX)));
+ } else if (codePoint <= 0xffff) {
+ buffer->push_back(
+ static_cast<char>(T3 | static_cast<uint8_t>(codePoint >> 12)));
+ buffer->push_back(static_cast<char>(
+ TX | (static_cast<uint8_t>(codePoint >> 6) & MASKX)));
+ buffer->push_back(
+ static_cast<char>(TX | (static_cast<uint8_t>(codePoint) & MASKX)));
+ } else {
+ buffer->push_back(
+ static_cast<char>(T4 | static_cast<uint8_t>(codePoint >> 18)));
+ buffer->push_back(static_cast<char>(
+ TX | (static_cast<uint8_t>(codePoint >> 12) & MASKX)));
+ buffer->push_back(static_cast<char>(
+ TX | (static_cast<uint8_t>(codePoint >> 6) & MASKX)));
+ buffer->push_back(
+ static_cast<char>(TX | (static_cast<uint8_t>(codePoint) & MASKX)));
+ }
+ return *buffer;
+ }
+
+ std::optional<std::string> Utf8::strictEncode(std::u32string_view input) {
+ std::string output;
+ output.reserve(input.size() * 4); // Worst case is each Unicode code point encodes to 4 bytes.
+ for (size_t index = 0; index < input.size(); index++) {
+ char32_t codePoint = input[index];
+ if (!Unicode::isValid(codePoint)) {
+ return std::nullopt;
+ }
+ Utf8::encode(&output, codePoint);
+ }
+ output.shrink_to_fit();
+ return output;
+ }
+
+ std::string Utf8::lenientEncode(std::u32string_view input) {
+ std::string output;
+ output.reserve(input.size() * 4); // Worst case is each Unicode code point encodes to 4 bytes.
+ for (size_t index = 0; index < input.size(); index++) {
+ char32_t codePoint = input[index];
+ if (!Unicode::isValid(codePoint)) {
+ codePoint = Unicode::REPLACEMENT_CHARACTER;
+ }
+ Utf8::encode(&output, codePoint);
+ }
+ output.shrink_to_fit();
+ return output;
+ }
+
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/support/Utf8.h b/contrib/libs/antlr4_cpp_runtime/src/support/Utf8.h
new file mode 100644
index 0000000000..e4828441cd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/support/Utf8.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2021 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include <string_view>
+#include <tuple>
+
+#include "antlr4-common.h"
+
+namespace antlrcpp {
+
+ class ANTLR4CPP_PUBLIC Utf8 final {
+ public:
+ // Decodes the next code point, returning the decoded code point and the number
+ // of code units (a.k.a. bytes) consumed. In the event that an invalid code unit
+ // sequence is returned the replacement character, U+FFFD, is returned with a
+ // code unit count of 1. As U+FFFD requires 3 code units when encoded, this can
+ // be used to differentiate valid input from malformed input.
+ static std::pair<char32_t, size_t> decode(std::string_view input);
+
+ // Decodes the given UTF-8 encoded input into a string of code points.
+ static std::optional<std::u32string> strictDecode(std::string_view input);
+
+ // Decodes the given UTF-8 encoded input into a string of code points. Unlike strictDecode(),
+ // each byte in an illegal byte sequence is replaced with the Unicode replacement character,
+ // U+FFFD.
+ static std::u32string lenientDecode(std::string_view input);
+
+ // Encodes the given code point and appends it to the buffer. If the code point
+ // is an unpaired surrogate or outside of the valid Unicode range it is replaced
+ // with the replacement character, U+FFFD.
+ static std::string& encode(std::string *buffer, char32_t codePoint);
+
+ // Encodes the given Unicode code point string as UTF-8.
+ static std::optional<std::string> strictEncode(std::u32string_view input);
+
+ // Encodes the given Unicode code point string as UTF-8. Unlike strictEncode(),
+ // each invalid Unicode code point is replaced with the Unicode replacement character, U+FFFD.
+ static std::string lenientEncode(std::u32string_view input);
+
+ private:
+ Utf8() = delete;
+ Utf8(const Utf8&) = delete;
+ Utf8(Utf8&&) = delete;
+ Utf8& operator=(const Utf8&) = delete;
+ Utf8& operator=(Utf8&&) = delete;
+ };
+
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/AbstractParseTreeVisitor.h b/contrib/libs/antlr4_cpp_runtime/src/tree/AbstractParseTreeVisitor.h
new file mode 100644
index 0000000000..25505278f2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/AbstractParseTreeVisitor.h
@@ -0,0 +1,129 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "tree/ParseTree.h"
+#include "tree/ParseTreeVisitor.h"
+
+namespace antlr4 {
+namespace tree {
+
+ class ANTLR4CPP_PUBLIC AbstractParseTreeVisitor : public ParseTreeVisitor {
+ public:
+ /// The default implementation calls <seealso cref="ParseTree#accept"/> on the
+ /// specified tree.
+ virtual std::any visit(ParseTree *tree) override {
+ return tree->accept(this);
+ }
+
+ /**
+ * <p>The default implementation initializes the aggregate result to
+ * {@link #defaultResult defaultResult()}. Before visiting each child, it
+ * calls {@link #shouldVisitNextChild shouldVisitNextChild}; if the result
+ * is {@code false} no more children are visited and the current aggregate
+ * result is returned. After visiting a child, the aggregate result is
+ * updated by calling {@link #aggregateResult aggregateResult} with the
+ * previous aggregate result and the result of visiting the child.</p>
+ *
+ * <p>The default implementation is not safe for use in visitors that modify
+ * the tree structure. Visitors that modify the tree should override this
+ * method to behave properly in respect to the specific algorithm in use.</p>
+ */
+ virtual std::any visitChildren(ParseTree *node) override {
+ std::any result = defaultResult();
+ size_t n = node->children.size();
+ for (size_t i = 0; i < n; i++) {
+ if (!shouldVisitNextChild(node, result)) {
+ break;
+ }
+
+ std::any childResult = node->children[i]->accept(this);
+ result = aggregateResult(std::move(result), std::move(childResult));
+ }
+
+ return result;
+ }
+
+ /// The default implementation returns the result of
+ /// <seealso cref="#defaultResult defaultResult"/>.
+ virtual std::any visitTerminal(TerminalNode * /*node*/) override {
+ return defaultResult();
+ }
+
+ /// The default implementation returns the result of
+ /// <seealso cref="#defaultResult defaultResult"/>.
+ virtual std::any visitErrorNode(ErrorNode * /*node*/) override {
+ return defaultResult();
+ }
+
+ protected:
+ /// <summary>
+ /// Gets the default value returned by visitor methods. This value is
+ /// returned by the default implementations of
+ /// <seealso cref="#visitTerminal visitTerminal"/>, <seealso cref="#visitErrorNode visitErrorNode"/>.
+ /// The default implementation of <seealso cref="#visitChildren visitChildren"/>
+ /// initializes its aggregate result to this value.
+ /// <p/>
+ /// The base implementation returns {@code std::any()}.
+ /// </summary>
+ /// <returns> The default value returned by visitor methods. </returns>
+ virtual std::any defaultResult() {
+ return std::any();
+ }
+
+ /// <summary>
+ /// Aggregates the results of visiting multiple children of a node. After
+ /// either all children are visited or <seealso cref="#shouldVisitNextChild"/> returns
+ /// {@code false}, the aggregate value is returned as the result of
+ /// <seealso cref="#visitChildren"/>.
+ /// <p/>
+ /// The default implementation returns {@code nextResult}, meaning
+ /// <seealso cref="#visitChildren"/> will return the result of the last child visited
+ /// (or return the initial value if the node has no children).
+ /// </summary>
+ /// <param name="aggregate"> The previous aggregate value. In the default
+ /// implementation, the aggregate value is initialized to
+ /// <seealso cref="#defaultResult"/>, which is passed as the {@code aggregate} argument
+ /// to this method after the first child node is visited. </param>
+ /// <param name="nextResult"> The result of the immediately preceeding call to visit
+ /// a child node.
+ /// </param>
+ /// <returns> The updated aggregate result. </returns>
+ virtual std::any aggregateResult(std::any /*aggregate*/, std::any nextResult) {
+ return nextResult;
+ }
+
+ /// <summary>
+ /// This method is called after visiting each child in
+ /// <seealso cref="#visitChildren"/>. This method is first called before the first
+ /// child is visited; at that point {@code currentResult} will be the initial
+ /// value (in the default implementation, the initial value is returned by a
+ /// call to <seealso cref="#defaultResult"/>. This method is not called after the last
+ /// child is visited.
+ /// <p/>
+ /// The default implementation always returns {@code true}, indicating that
+ /// {@code visitChildren} should only return after all children are visited.
+ /// One reason to override this method is to provide a "short circuit"
+ /// evaluation option for situations where the result of visiting a single
+ /// child has the potential to determine the result of the visit operation as
+ /// a whole.
+ /// </summary>
+ /// <param name="node"> The <seealso cref="ParseTree"/> whose children are currently being
+ /// visited. </param>
+ /// <param name="currentResult"> The current aggregate result of the children visited
+ /// to the current point.
+ /// </param>
+ /// <returns> {@code true} to continue visiting children. Otherwise return
+ /// {@code false} to stop visiting children and immediately return the
+ /// current aggregate result from <seealso cref="#visitChildren"/>. </returns>
+ virtual bool shouldVisitNextChild(ParseTree * /*node*/, const std::any &/*currentResult*/) {
+ return true;
+ }
+
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNode.h b/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNode.h
new file mode 100644
index 0000000000..319ce39e0d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNode.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "tree/TerminalNode.h"
+
+namespace antlr4 {
+namespace tree {
+
+ class ANTLR4CPP_PUBLIC ErrorNode : public TerminalNode {
+ public:
+ static bool is(const tree::ParseTree &parseTree) { return parseTree.getTreeType() == tree::ParseTreeType::ERROR; }
+
+ static bool is(const tree::ParseTree *parseTree) { return parseTree != nullptr && is(*parseTree); }
+
+ protected:
+ using TerminalNode::TerminalNode;
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNodeImpl.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNodeImpl.cpp
new file mode 100644
index 0000000000..142791dd96
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNodeImpl.cpp
@@ -0,0 +1,54 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/Interval.h"
+#include "Token.h"
+#include "RuleContext.h"
+#include "tree/ParseTreeVisitor.h"
+
+#include "tree/ErrorNodeImpl.h"
+
+using namespace antlr4;
+using namespace antlr4::tree;
+
+Token* ErrorNodeImpl::getSymbol() const {
+ return symbol;
+}
+
+void ErrorNodeImpl::setParent(RuleContext *parent_) {
+ this->parent = parent_;
+}
+
+misc::Interval ErrorNodeImpl::getSourceInterval() {
+ if (symbol == nullptr) {
+ return misc::Interval::INVALID;
+ }
+
+ size_t tokenIndex = symbol->getTokenIndex();
+ return misc::Interval(tokenIndex, tokenIndex);
+}
+
+std::any ErrorNodeImpl::accept(ParseTreeVisitor *visitor) {
+ return visitor->visitErrorNode(this);
+}
+
+std::string ErrorNodeImpl::getText() {
+ return symbol->getText();
+}
+
+std::string ErrorNodeImpl::toStringTree(Parser * /*parser*/, bool /*pretty*/) {
+ return toString();
+}
+
+std::string ErrorNodeImpl::toString() {
+ if (symbol->getType() == Token::EOF) {
+ return "<EOF>";
+ }
+ return symbol->getText();
+}
+
+std::string ErrorNodeImpl::toStringTree(bool /*pretty*/) {
+ return toString();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNodeImpl.h b/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNodeImpl.h
new file mode 100644
index 0000000000..8bafb62552
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ErrorNodeImpl.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "tree/ErrorNode.h"
+#include "tree/TerminalNodeImpl.h"
+#include "misc/Interval.h"
+
+#include "support/Any.h"
+
+namespace antlr4 {
+namespace tree {
+
+ /// <summary>
+ /// Represents a token that was consumed during resynchronization
+ /// rather than during a valid match operation. For example,
+ /// we will create this kind of a node during single token insertion
+ /// and deletion as well as during "consume until error recovery set"
+ /// upon no viable alternative exceptions.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC ErrorNodeImpl : public ErrorNode {
+ public:
+ Token *symbol;
+
+ explicit ErrorNodeImpl(Token *symbol) : ErrorNode(ParseTreeType::ERROR), symbol(symbol) {}
+
+ virtual Token* getSymbol() const override;
+ virtual void setParent(RuleContext *parent) override;
+ virtual misc::Interval getSourceInterval() override;
+
+ virtual std::any accept(ParseTreeVisitor *visitor) override;
+
+ virtual std::string getText() override;
+ virtual std::string toStringTree(Parser *parser, bool pretty = false) override;
+ virtual std::string toString() override;
+ virtual std::string toStringTree(bool pretty = false) override;
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/IterativeParseTreeWalker.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/IterativeParseTreeWalker.cpp
new file mode 100644
index 0000000000..83e6339518
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/IterativeParseTreeWalker.cpp
@@ -0,0 +1,66 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "support/CPPUtils.h"
+#include "support/Casts.h"
+
+#include "tree/ParseTreeListener.h"
+#include "tree/ParseTree.h"
+#include "tree/ErrorNode.h"
+
+#include "IterativeParseTreeWalker.h"
+
+using namespace antlr4::tree;
+using namespace antlrcpp;
+
+void IterativeParseTreeWalker::walk(ParseTreeListener *listener, ParseTree *t) const {
+ std::vector<std::pair<ParseTree*, size_t>> stack;
+ ParseTree *currentNode = t;
+ size_t currentIndex = 0;
+
+ while (currentNode != nullptr) {
+ // pre-order visit
+ if (ErrorNode::is(*currentNode)) {
+ listener->visitErrorNode(downCast<ErrorNode*>(currentNode));
+ } else if (TerminalNode::is(*currentNode)) {
+ listener->visitTerminal(downCast<TerminalNode*>(currentNode));
+ } else {
+ enterRule(listener, currentNode);
+ }
+
+ // Move down to first child, if it exists.
+ if (!currentNode->children.empty()) {
+ stack.push_back(std::make_pair(currentNode, currentIndex));
+ currentIndex = 0;
+ currentNode = currentNode->children[0];
+ continue;
+ }
+
+ // No child nodes, so walk tree.
+ do {
+ // post-order visit
+ if (!TerminalNode::is(*currentNode)) {
+ exitRule(listener, currentNode);
+ }
+
+ // No parent, so no siblings.
+ if (stack.empty()) {
+ currentNode = nullptr;
+ currentIndex = 0;
+ break;
+ }
+
+ // Move to next sibling if possible.
+ if (stack.back().first->children.size() > ++currentIndex) {
+ currentNode = stack.back().first->children[currentIndex];
+ break;
+ }
+
+ // No next sibling, so move up.
+ std::tie(currentNode, currentIndex) = stack.back();
+ stack.pop_back();
+ } while (currentNode != nullptr);
+ }
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/IterativeParseTreeWalker.h b/contrib/libs/antlr4_cpp_runtime/src/tree/IterativeParseTreeWalker.h
new file mode 100644
index 0000000000..8957d87e44
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/IterativeParseTreeWalker.h
@@ -0,0 +1,53 @@
+/*
+ * [The "BSD license"]
+ * Copyright (c) 2012 Terence Parr
+ * Copyright (c) 2012 Sam Harwell
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+#include "tree/ParseTreeWalker.h"
+
+namespace antlr4 {
+namespace tree {
+
+ class ParseTreeListener;
+
+ /**
+ * An iterative (read: non-recursive) pre-order and post-order tree walker that
+ * doesn't use the thread stack but heap-based stacks. Makes it possible to
+ * process deeply nested parse trees.
+ */
+ class ANTLR4CPP_PUBLIC IterativeParseTreeWalker : public ParseTreeWalker {
+ public:
+ virtual void walk(ParseTreeListener *listener, ParseTree *t) const override;
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTree.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTree.cpp
new file mode 100644
index 0000000000..8756398d88
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTree.cpp
@@ -0,0 +1,12 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ParseTree.h"
+
+using namespace antlr4::tree;
+
+bool ParseTree::operator == (const ParseTree &other) const {
+ return &other == this;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTree.h b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTree.h
new file mode 100644
index 0000000000..cf8027b8fd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTree.h
@@ -0,0 +1,111 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "support/Any.h"
+#include "tree/ParseTreeType.h"
+
+namespace antlr4 {
+namespace tree {
+
+ /// An interface to access the tree of <seealso cref="RuleContext"/> objects created
+ /// during a parse that makes the data structure look like a simple parse tree.
+ /// This node represents both internal nodes, rule invocations,
+ /// and leaf nodes, token matches.
+ ///
+ /// The payload is either a <seealso cref="Token"/> or a <seealso cref="RuleContext"/> object.
+ // ml: This class unites 4 Java classes: RuleNode, ParseTree, SyntaxTree and Tree.
+ class ANTLR4CPP_PUBLIC ParseTree {
+ public:
+ ParseTree(ParseTree const&) = delete;
+
+ virtual ~ParseTree() = default;
+
+ ParseTree& operator=(ParseTree const&) = delete;
+
+ /// The parent of this node. If the return value is null, then this
+ /// node is the root of the tree.
+ ParseTree *parent = nullptr;
+
+ /// If we are debugging or building a parse tree for a visitor,
+ /// we need to track all of the tokens and rule invocations associated
+ /// with this rule's context. This is empty for parsing w/o tree constr.
+ /// operation because we don't the need to track the details about
+ /// how we parse this rule.
+ // ml: memory is not managed here, but by the owning class. This is just for the structure.
+ std::vector<ParseTree *> children;
+
+ /// Print out a whole tree, not just a node, in LISP format
+ /// {@code (root child1 .. childN)}. Print just a node if this is a leaf.
+ virtual std::string toStringTree(bool pretty = false) = 0;
+ virtual std::string toString() = 0;
+
+ /// Specialize toStringTree so that it can print out more information
+ /// based upon the parser.
+ virtual std::string toStringTree(Parser *parser, bool pretty = false) = 0;
+
+ virtual bool operator == (const ParseTree &other) const;
+
+ /// The <seealso cref="ParseTreeVisitor"/> needs a double dispatch method.
+ // ml: This has been changed to use Any instead of a template parameter, to avoid the need of a virtual template function.
+ virtual std::any accept(ParseTreeVisitor *visitor) = 0;
+
+ /// Return the combined text of all leaf nodes. Does not get any
+ /// off-channel tokens (if any) so won't return whitespace and
+ /// comments if they are sent to parser on hidden channel.
+ virtual std::string getText() = 0;
+
+ /**
+ * Return an {@link Interval} indicating the index in the
+ * {@link TokenStream} of the first and last token associated with this
+ * subtree. If this node is a leaf, then the interval represents a single
+ * token and has interval i..i for token index i.
+ *
+ * <p>An interval of i..i-1 indicates an empty interval at position
+ * i in the input stream, where 0 &lt;= i &lt;= the size of the input
+ * token stream. Currently, the code base can only have i=0..n-1 but
+ * in concept one could have an empty interval after EOF. </p>
+ *
+ * <p>If source interval is unknown, this returns {@link Interval#INVALID}.</p>
+ *
+ * <p>As a weird special case, the source interval for rules matched after
+ * EOF is unspecified.</p>
+ */
+ virtual misc::Interval getSourceInterval() = 0;
+
+ ParseTreeType getTreeType() const { return _treeType; }
+
+ protected:
+ explicit ParseTree(ParseTreeType treeType) : _treeType(treeType) {}
+
+ private:
+ const ParseTreeType _treeType;
+ };
+
+ // A class to help managing ParseTree instances without the need of a shared_ptr.
+ class ANTLR4CPP_PUBLIC ParseTreeTracker {
+ public:
+ template<typename T, typename ... Args>
+ T* createInstance(Args&& ... args) {
+ static_assert(std::is_base_of<ParseTree, T>::value, "Argument must be a parse tree type");
+ T* result = new T(args...);
+ _allocated.push_back(result);
+ return result;
+ }
+
+ void reset() {
+ for (auto * entry : _allocated)
+ delete entry;
+ _allocated.clear();
+ }
+
+ private:
+ std::vector<ParseTree *> _allocated;
+ };
+
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeListener.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeListener.cpp
new file mode 100644
index 0000000000..ce12297586
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeListener.cpp
@@ -0,0 +1,9 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "ParseTreeListener.h"
+
+antlr4::tree::ParseTreeListener::~ParseTreeListener() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeListener.h b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeListener.h
new file mode 100644
index 0000000000..60c7d8861a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeListener.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+
+ /** This interface describes the minimal core of methods triggered
+ * by {@link ParseTreeWalker}. E.g.,
+ *
+ * ParseTreeWalker walker = new ParseTreeWalker();
+ * walker.walk(myParseTreeListener, myParseTree); <-- triggers events in your listener
+ *
+ * If you want to trigger events in multiple listeners during a single
+ * tree walk, you can use the ParseTreeDispatcher object available at
+ *
+ * https://github.com/antlr/antlr4/issues/841
+ */
+ class ANTLR4CPP_PUBLIC ParseTreeListener {
+ public:
+ virtual ~ParseTreeListener();
+
+ virtual void visitTerminal(TerminalNode *node) = 0;
+ virtual void visitErrorNode(ErrorNode *node) = 0;
+ virtual void enterEveryRule(ParserRuleContext *ctx) = 0;
+ virtual void exitEveryRule(ParserRuleContext *ctx) = 0;
+
+ bool operator == (const ParseTreeListener &other) {
+ return this == &other;
+ }
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeProperty.h b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeProperty.h
new file mode 100644
index 0000000000..efd5e73bf8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeProperty.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+
+ /// <summary>
+ /// Associate a property with a parse tree node. Useful with parse tree listeners
+ /// that need to associate values with particular tree nodes, kind of like
+ /// specifying a return value for the listener event method that visited a
+ /// particular node. Example:
+ ///
+ /// <pre>
+ /// ParseTreeProperty&lt;Integer&gt; values = new ParseTreeProperty&lt;Integer&gt;();
+ /// values.put(tree, 36);
+ /// int x = values.get(tree);
+ /// values.removeFrom(tree);
+ /// </pre>
+ ///
+ /// You would make one decl (values here) in the listener and use lots of times
+ /// in your event methods.
+ /// </summary>
+ template<typename V>
+ class ANTLR4CPP_PUBLIC ParseTreeProperty {
+ public:
+ virtual ~ParseTreeProperty() {}
+ virtual V get(ParseTree *node) {
+ return _annotations[node];
+ }
+ virtual void put(ParseTree *node, V value) {
+ _annotations[node] = value;
+ }
+ virtual V removeFrom(ParseTree *node) {
+ auto value = _annotations[node];
+ _annotations.erase(node);
+ return value;
+ }
+
+ protected:
+ std::map<ParseTree*, V> _annotations;
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeType.h b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeType.h
new file mode 100644
index 0000000000..17e0512b00
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeType.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+
+ enum class ParseTreeType : size_t {
+ TERMINAL = 1,
+ ERROR = 2,
+ RULE = 3,
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeVisitor.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeVisitor.cpp
new file mode 100644
index 0000000000..a329919c13
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeVisitor.cpp
@@ -0,0 +1,9 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "ParseTreeVisitor.h"
+
+antlr4::tree::ParseTreeVisitor::~ParseTreeVisitor() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeVisitor.h b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeVisitor.h
new file mode 100644
index 0000000000..02d9dc9b95
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeVisitor.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "support/Any.h"
+
+namespace antlr4 {
+namespace tree {
+
+ /// <summary>
+ /// This interface defines the basic notion of a parse tree visitor. Generated
+ /// visitors implement this interface and the {@code XVisitor} interface for
+ /// grammar {@code X}.
+ /// </summary>
+ /// @param <T> The return type of the visit operation. Use <seealso cref="Void"/> for
+ /// operations with no return type. </param>
+ // ml: no template parameter here, to avoid the need for virtual template functions. Instead we have our Any class.
+ class ANTLR4CPP_PUBLIC ParseTreeVisitor {
+ public:
+ virtual ~ParseTreeVisitor();
+
+ /// <summary>
+ /// Visit a parse tree, and return a user-defined result of the operation.
+ /// </summary>
+ /// <param name="tree"> The <seealso cref="ParseTree"/> to visit. </param>
+ /// <returns> The result of visiting the parse tree. </returns>
+ virtual std::any visit(ParseTree *tree) = 0;
+
+ /// <summary>
+ /// Visit the children of a node, and return a user-defined result of the
+ /// operation.
+ /// </summary>
+ /// <param name="node"> The <seealso cref="ParseTree"/> whose children should be visited. </param>
+ /// <returns> The result of visiting the children of the node. </returns>
+ virtual std::any visitChildren(ParseTree *node) = 0;
+
+ /// <summary>
+ /// Visit a terminal node, and return a user-defined result of the operation.
+ /// </summary>
+ /// <param name="node"> The <seealso cref="TerminalNode"/> to visit. </param>
+ /// <returns> The result of visiting the node. </returns>
+ virtual std::any visitTerminal(TerminalNode *node) = 0;
+
+ /// <summary>
+ /// Visit an error node, and return a user-defined result of the operation.
+ /// </summary>
+ /// <param name="node"> The <seealso cref="ErrorNode"/> to visit. </param>
+ /// <returns> The result of visiting the node. </returns>
+ virtual std::any visitErrorNode(ErrorNode *node) = 0;
+
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeWalker.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeWalker.cpp
new file mode 100644
index 0000000000..3da4bec5c5
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeWalker.cpp
@@ -0,0 +1,48 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ErrorNode.h"
+#include "ParserRuleContext.h"
+#include "tree/ParseTreeListener.h"
+#include "support/CPPUtils.h"
+#include "support/Casts.h"
+
+#include "tree/IterativeParseTreeWalker.h"
+#include "tree/ParseTreeWalker.h"
+
+using namespace antlr4::tree;
+using namespace antlrcpp;
+
+static IterativeParseTreeWalker defaultWalker;
+ParseTreeWalker &ParseTreeWalker::DEFAULT = defaultWalker;
+
+void ParseTreeWalker::walk(ParseTreeListener *listener, ParseTree *t) const {
+ if (ErrorNode::is(*t)) {
+ listener->visitErrorNode(downCast<ErrorNode*>(t));
+ return;
+ }
+ if (TerminalNode::is(*t)) {
+ listener->visitTerminal(downCast<TerminalNode*>(t));
+ return;
+ }
+
+ enterRule(listener, t);
+ for (auto &child : t->children) {
+ walk(listener, child);
+ }
+ exitRule(listener, t);
+}
+
+void ParseTreeWalker::enterRule(ParseTreeListener *listener, ParseTree *r) const {
+ auto *ctx = downCast<ParserRuleContext*>(r);
+ listener->enterEveryRule(ctx);
+ ctx->enterRule(listener);
+}
+
+void ParseTreeWalker::exitRule(ParseTreeListener *listener, ParseTree *r) const {
+ auto *ctx = downCast<ParserRuleContext*>(r);
+ ctx->exitRule(listener);
+ listener->exitEveryRule(ctx);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeWalker.h b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeWalker.h
new file mode 100644
index 0000000000..718cbbd1e4
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/ParseTreeWalker.h
@@ -0,0 +1,55 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+
+ class ANTLR4CPP_PUBLIC ParseTreeWalker {
+ public:
+ static ParseTreeWalker &DEFAULT;
+
+ virtual ~ParseTreeWalker() = default;
+
+ /**
+ * <summary>
+ * Performs a walk on the given parse tree starting at the root and going down recursively
+ * with depth-first search. On each node, <seealso cref="ParseTreeWalker#enterRule"/> is called before
+ * recursively walking down into child nodes, then
+ * <seealso cref="ParseTreeWalker#exitRule"/> is called after the recursive call to wind up.
+ * </summary>
+ * <param name='listener'> The listener used by the walker to process grammar rules </param>
+ * <param name='t'> The parse tree to be walked on </param>
+ */
+ virtual void walk(ParseTreeListener *listener, ParseTree *t) const;
+
+ protected:
+
+ /**
+ * <summary>
+ * Enters a grammar rule by first triggering the generic event <seealso cref="ParseTreeListener#enterEveryRule"/>
+ * then by triggering the event specific to the given parse tree node
+ * </summary>
+ * <param name='listener'> The listener responding to the trigger events </param>
+ * <param name='r'> The grammar rule containing the rule context </param>
+ */
+ virtual void enterRule(ParseTreeListener *listener, ParseTree *r) const;
+
+ /**
+ * <summary>
+ * Exits a grammar rule by first triggering the event specific to the given parse tree node
+ * then by triggering the generic event <seealso cref="ParseTreeListener#exitEveryRule"/>
+ * </summary>
+ * <param name='listener'> The listener responding to the trigger events </param>
+ * <param name='r'> The grammar rule containing the rule context </param>
+ */
+ virtual void exitRule(ParseTreeListener *listener, ParseTree *r) const;
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNode.h b/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNode.h
new file mode 100644
index 0000000000..9f7466edc5
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNode.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "tree/ParseTree.h"
+
+namespace antlr4 {
+namespace tree {
+
+ class ANTLR4CPP_PUBLIC TerminalNode : public ParseTree {
+ public:
+ static bool is(const tree::ParseTree &parseTree) {
+ const auto treeType = parseTree.getTreeType();
+ return treeType == ParseTreeType::TERMINAL || treeType == ParseTreeType::ERROR;
+ }
+
+ static bool is(const tree::ParseTree *parseTree) { return parseTree != nullptr && is(*parseTree); }
+
+ virtual Token* getSymbol() const = 0;
+
+ /** Set the parent for this leaf node.
+ *
+ * Technically, this is not backward compatible as it changes
+ * the interface but no one was able to create custom
+ * TerminalNodes anyway so I'm adding as it improves internal
+ * code quality.
+ *
+ * @since 4.7
+ */
+ virtual void setParent(RuleContext *parent) = 0;
+
+ protected:
+ using ParseTree::ParseTree;
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNodeImpl.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNodeImpl.cpp
new file mode 100644
index 0000000000..8eeb299fee
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNodeImpl.cpp
@@ -0,0 +1,54 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "misc/Interval.h"
+#include "Token.h"
+#include "RuleContext.h"
+#include "tree/ParseTreeVisitor.h"
+
+#include "tree/TerminalNodeImpl.h"
+
+using namespace antlr4;
+using namespace antlr4::tree;
+
+Token* TerminalNodeImpl::getSymbol() const {
+ return symbol;
+}
+
+void TerminalNodeImpl::setParent(RuleContext *parent_) {
+ this->parent = parent_;
+}
+
+misc::Interval TerminalNodeImpl::getSourceInterval() {
+ if (symbol == nullptr) {
+ return misc::Interval::INVALID;
+ }
+
+ size_t tokenIndex = symbol->getTokenIndex();
+ return misc::Interval(tokenIndex, tokenIndex);
+}
+
+std::any TerminalNodeImpl::accept(ParseTreeVisitor *visitor) {
+ return visitor->visitTerminal(this);
+}
+
+std::string TerminalNodeImpl::getText() {
+ return symbol->getText();
+}
+
+std::string TerminalNodeImpl::toStringTree(Parser * /*parser*/, bool /*pretty*/) {
+ return toString();
+}
+
+std::string TerminalNodeImpl::toString() {
+ if (symbol->getType() == Token::EOF) {
+ return "<EOF>";
+ }
+ return symbol->getText();
+}
+
+std::string TerminalNodeImpl::toStringTree(bool /*pretty*/) {
+ return toString();
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNodeImpl.h b/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNodeImpl.h
new file mode 100644
index 0000000000..1f8adacc6a
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/TerminalNodeImpl.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "tree/TerminalNode.h"
+
+namespace antlr4 {
+namespace tree {
+
+ class ANTLR4CPP_PUBLIC TerminalNodeImpl : public TerminalNode {
+ public:
+ Token *symbol;
+
+ explicit TerminalNodeImpl(Token *symbol) : TerminalNode(ParseTreeType::TERMINAL), symbol(symbol) {}
+
+ virtual Token* getSymbol() const override;
+ virtual void setParent(RuleContext *parent) override;
+ virtual misc::Interval getSourceInterval() override;
+
+ virtual std::any accept(ParseTreeVisitor *visitor) override;
+
+ virtual std::string getText() override;
+ virtual std::string toStringTree(Parser *parser, bool pretty = false) override;
+ virtual std::string toString() override;
+ virtual std::string toStringTree(bool pretty = false) override;
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/Trees.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/Trees.cpp
new file mode 100644
index 0000000000..f4065949b2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/Trees.cpp
@@ -0,0 +1,241 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ErrorNode.h"
+#include "Parser.h"
+#include "ParserRuleContext.h"
+#include "support/CPPUtils.h"
+#include "tree/TerminalNodeImpl.h"
+#include "atn/ATN.h"
+#include "misc/Interval.h"
+#include "Token.h"
+#include "CommonToken.h"
+#include "misc/Predicate.h"
+
+#include "tree/Trees.h"
+
+using namespace antlr4;
+using namespace antlr4::misc;
+using namespace antlr4::tree;
+
+using namespace antlrcpp;
+
+Trees::Trees() {
+}
+
+std::string Trees::toStringTree(ParseTree *t, bool pretty) {
+ return toStringTree(t, nullptr, pretty);
+}
+
+std::string Trees::toStringTree(ParseTree *t, Parser *recog, bool pretty) {
+ if (recog == nullptr)
+ return toStringTree(t, std::vector<std::string>(), pretty);
+ return toStringTree(t, recog->getRuleNames(), pretty);
+}
+
+std::string Trees::toStringTree(ParseTree *t, const std::vector<std::string> &ruleNames, bool pretty) {
+ std::string temp = antlrcpp::escapeWhitespace(Trees::getNodeText(t, ruleNames), false);
+ if (t->children.empty()) {
+ return temp;
+ }
+
+ std::stringstream ss;
+ ss << "(" << temp << ' ';
+
+ // Implement the recursive walk as iteration to avoid trouble with deep nesting.
+ std::stack<size_t> stack;
+ size_t childIndex = 0;
+ ParseTree *run = t;
+ size_t indentationLevel = 1;
+ while (childIndex < run->children.size()) {
+ if (childIndex > 0) {
+ ss << ' ';
+ }
+ ParseTree *child = run->children[childIndex];
+ temp = antlrcpp::escapeWhitespace(Trees::getNodeText(child, ruleNames), false);
+ if (!child->children.empty()) {
+ // Go deeper one level.
+ stack.push(childIndex);
+ run = child;
+ childIndex = 0;
+ if (pretty) {
+ ++indentationLevel;
+ ss << std::endl;
+ for (size_t i = 0; i < indentationLevel; ++i) {
+ ss << " ";
+ }
+ }
+ ss << "(" << temp << " ";
+ } else {
+ ss << temp;
+ while (++childIndex == run->children.size()) {
+ if (stack.size() > 0) {
+ // Reached the end of the current level. See if we can step up from here.
+ childIndex = stack.top();
+ stack.pop();
+ run = run->parent;
+ if (pretty) {
+ --indentationLevel;
+ }
+ ss << ")";
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ ss << ")";
+ return ss.str();
+}
+
+std::string Trees::getNodeText(ParseTree *t, Parser *recog) {
+ return getNodeText(t, recog->getRuleNames());
+}
+
+std::string Trees::getNodeText(ParseTree *t, const std::vector<std::string> &ruleNames) {
+ if (ruleNames.size() > 0) {
+ if (is<RuleContext *>(t)) {
+ size_t ruleIndex = dynamic_cast<RuleContext *>(t)->getRuleIndex();
+ std::string ruleName = ruleNames[ruleIndex];
+ size_t altNumber = dynamic_cast<RuleContext *>(t)->getAltNumber();
+ if (altNumber != atn::ATN::INVALID_ALT_NUMBER) {
+ return ruleName + ":" + std::to_string(altNumber);
+ }
+ return ruleName;
+ } else if (is<ErrorNode *>(t)) {
+ return t->toString();
+ } else if (is<TerminalNode *>(t)) {
+ Token *symbol = dynamic_cast<TerminalNode *>(t)->getSymbol();
+ if (symbol != nullptr) {
+ std::string s = symbol->getText();
+ return s;
+ }
+ }
+ }
+ // no recog for rule names
+ if (is<RuleContext *>(t)) {
+ return dynamic_cast<RuleContext *>(t)->getText();
+ }
+
+ if (is<TerminalNodeImpl *>(t)) {
+ return dynamic_cast<TerminalNodeImpl *>(t)->getSymbol()->getText();
+ }
+
+ return "";
+}
+
+std::vector<ParseTree *> Trees::getAncestors(ParseTree *t) {
+ std::vector<ParseTree *> ancestors;
+ ParseTree *parent = t->parent;
+ while (parent != nullptr) {
+ ancestors.insert(ancestors.begin(), parent); // insert at start
+ parent = parent->parent;
+ }
+ return ancestors;
+}
+
+template<typename T>
+static void _findAllNodes(ParseTree *t, size_t index, bool findTokens, std::vector<T> &nodes) {
+ // check this node (the root) first
+ if (findTokens && is<TerminalNode *>(t)) {
+ TerminalNode *tnode = dynamic_cast<TerminalNode *>(t);
+ if (tnode->getSymbol()->getType() == index) {
+ nodes.push_back(t);
+ }
+ } else if (!findTokens && is<ParserRuleContext *>(t)) {
+ ParserRuleContext *ctx = dynamic_cast<ParserRuleContext *>(t);
+ if (ctx->getRuleIndex() == index) {
+ nodes.push_back(t);
+ }
+ }
+ // check children
+ for (size_t i = 0; i < t->children.size(); i++) {
+ _findAllNodes(t->children[i], index, findTokens, nodes);
+ }
+}
+
+bool Trees::isAncestorOf(ParseTree *t, ParseTree *u) {
+ if (t == nullptr || u == nullptr || t->parent == nullptr) {
+ return false;
+ }
+
+ ParseTree *p = u->parent;
+ while (p != nullptr) {
+ if (t == p) {
+ return true;
+ }
+ p = p->parent;
+ }
+ return false;
+}
+
+std::vector<ParseTree *> Trees::findAllTokenNodes(ParseTree *t, size_t ttype) {
+ return findAllNodes(t, ttype, true);
+}
+
+std::vector<ParseTree *> Trees::findAllRuleNodes(ParseTree *t, size_t ruleIndex) {
+ return findAllNodes(t, ruleIndex, false);
+}
+
+std::vector<ParseTree *> Trees::findAllNodes(ParseTree *t, size_t index, bool findTokens) {
+ std::vector<ParseTree *> nodes;
+ _findAllNodes<ParseTree *>(t, index, findTokens, nodes);
+ return nodes;
+}
+
+std::vector<ParseTree *> Trees::getDescendants(ParseTree *t) {
+ std::vector<ParseTree *> nodes;
+ nodes.push_back(t);
+ std::size_t n = t->children.size();
+ for (size_t i = 0 ; i < n ; i++) {
+ auto descentants = getDescendants(t->children[i]);
+ for (auto *entry: descentants) {
+ nodes.push_back(entry);
+ }
+ }
+ return nodes;
+}
+
+std::vector<ParseTree *> Trees::descendants(ParseTree *t) {
+ return getDescendants(t);
+}
+
+ParserRuleContext* Trees::getRootOfSubtreeEnclosingRegion(ParseTree *t, size_t startTokenIndex, size_t stopTokenIndex) {
+ size_t n = t->children.size();
+ for (size_t i = 0; i < n; i++) {
+ ParserRuleContext *r = getRootOfSubtreeEnclosingRegion(t->children[i], startTokenIndex, stopTokenIndex);
+ if (r != nullptr) {
+ return r;
+ }
+ }
+
+ if (is<ParserRuleContext *>(t)) {
+ ParserRuleContext *r = dynamic_cast<ParserRuleContext *>(t);
+ if (startTokenIndex >= r->getStart()->getTokenIndex() && // is range fully contained in t?
+ (r->getStop() == nullptr || stopTokenIndex <= r->getStop()->getTokenIndex())) {
+ // note: r.getStop()==null likely implies that we bailed out of parser and there's nothing to the right
+ return r;
+ }
+ }
+ return nullptr;
+}
+
+ParseTree * Trees::findNodeSuchThat(ParseTree *t, Ref<Predicate> const& pred) {
+ if (pred->test(t)) {
+ return t;
+ }
+
+ size_t n = t->children.size();
+ for (size_t i = 0 ; i < n ; ++i) {
+ ParseTree *u = findNodeSuchThat(t->children[i], pred);
+ if (u != nullptr) {
+ return u;
+ }
+ }
+
+ return nullptr;
+}
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/Trees.h b/contrib/libs/antlr4_cpp_runtime/src/tree/Trees.h
new file mode 100644
index 0000000000..f779158d01
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/Trees.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "tree/TerminalNode.h"
+#include "ParserRuleContext.h"
+#include "Recognizer.h"
+
+namespace antlr4 {
+namespace tree {
+
+ /// A set of utility routines useful for all kinds of ANTLR trees.
+ class ANTLR4CPP_PUBLIC Trees {
+ public:
+ /// Print out a whole tree in LISP form. getNodeText is used on the
+ /// node payloads to get the text for the nodes. Detect
+ /// parse trees and extract data appropriately.
+ static std::string toStringTree(ParseTree *t, bool pretty = false);
+
+ /// Print out a whole tree in LISP form. getNodeText is used on the
+ /// node payloads to get the text for the nodes. Detect
+ /// parse trees and extract data appropriately.
+ static std::string toStringTree(ParseTree *t, Parser *recog, bool pretty = false);
+
+ /// Print out a whole tree in LISP form. getNodeText is used on the
+ /// node payloads to get the text for the nodes. Detect
+ /// parse trees and extract data appropriately.
+ static std::string toStringTree(ParseTree *t, const std::vector<std::string> &ruleNames, bool pretty = false);
+ static std::string getNodeText(ParseTree *t, Parser *recog);
+ static std::string getNodeText(ParseTree *t, const std::vector<std::string> &ruleNames);
+
+ /// Return a list of all ancestors of this node. The first node of
+ /// list is the root and the last is the parent of this node.
+ static std::vector<ParseTree *> getAncestors(ParseTree *t);
+
+ /** Return true if t is u's parent or a node on path to root from u.
+ * Use == not equals().
+ *
+ * @since 4.5.1
+ */
+ static bool isAncestorOf(ParseTree *t, ParseTree *u);
+ static std::vector<ParseTree *> findAllTokenNodes(ParseTree *t, size_t ttype);
+ static std::vector<ParseTree *> findAllRuleNodes(ParseTree *t, size_t ruleIndex);
+ static std::vector<ParseTree *> findAllNodes(ParseTree *t, size_t index, bool findTokens);
+
+ /** Get all descendents; includes t itself.
+ *
+ * @since 4.5.1
+ */
+ static std::vector<ParseTree *> getDescendants(ParseTree *t);
+
+ /** @deprecated */
+ static std::vector<ParseTree *> descendants(ParseTree *t);
+
+ /** Find smallest subtree of t enclosing range startTokenIndex..stopTokenIndex
+ * inclusively using postorder traversal. Recursive depth-first-search.
+ *
+ * @since 4.5.1
+ */
+ static ParserRuleContext* getRootOfSubtreeEnclosingRegion(ParseTree *t,
+ size_t startTokenIndex, // inclusive
+ size_t stopTokenIndex); // inclusive
+
+ /** Return first node satisfying the pred
+ *
+ * @since 4.5.1
+ */
+ static ParseTree* findNodeSuchThat(ParseTree *t, Ref<misc::Predicate> const& pred);
+
+ private:
+ Trees();
+ };
+
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/Chunk.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/Chunk.cpp
new file mode 100644
index 0000000000..5320f910b9
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/Chunk.cpp
@@ -0,0 +1,9 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/pattern/Chunk.h"
+
+antlr4::tree::pattern::Chunk::~Chunk() {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/Chunk.h b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/Chunk.h
new file mode 100644
index 0000000000..61079a8ca8
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/Chunk.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+namespace pattern {
+
+ /// <summary>
+ /// A chunk is either a token tag, a rule tag, or a span of literal text within a
+ /// tree pattern.
+ /// <p/>
+ /// The method <seealso cref="ParseTreePatternMatcher#split(String)"/> returns a list of
+ /// chunks in preparation for creating a token stream by
+ /// <seealso cref="ParseTreePatternMatcher#tokenize(String)"/>. From there, we get a parse
+ /// tree from with <seealso cref="ParseTreePatternMatcher#compile(String, int)"/>. These
+ /// chunks are converted to <seealso cref="RuleTagToken"/>, <seealso cref="TokenTagToken"/>, or the
+ /// regular tokens of the text surrounding the tags.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC Chunk {
+ public:
+ Chunk() = default;
+ Chunk(Chunk const&) = default;
+ virtual ~Chunk();
+
+ Chunk& operator=(Chunk const&) = default;
+
+ /// This method returns a text representation of the tag chunk. Labeled tags
+ /// are returned in the form {@code label:tag}, and unlabeled tags are
+ /// returned as just the tag name.
+ virtual std::string toString() {
+ std::string str;
+ return str;
+ }
+ };
+
+} // namespace pattern
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreeMatch.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreeMatch.cpp
new file mode 100644
index 0000000000..41896d6df7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreeMatch.cpp
@@ -0,0 +1,69 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Exceptions.h"
+
+#include "tree/pattern/ParseTreeMatch.h"
+
+using namespace antlr4::tree;
+using namespace antlr4::tree::pattern;
+
+ParseTreeMatch::ParseTreeMatch(ParseTree *tree, const ParseTreePattern &pattern,
+ const std::map<std::string, std::vector<ParseTree *>> &labels,
+ ParseTree *mismatchedNode)
+ : _tree(tree), _pattern(pattern), _labels(labels), _mismatchedNode(mismatchedNode) {
+ if (tree == nullptr) {
+ throw IllegalArgumentException("tree cannot be nul");
+ }
+}
+
+ParseTreeMatch::~ParseTreeMatch() {
+}
+
+ParseTree* ParseTreeMatch::get(const std::string &label) {
+ auto iterator = _labels.find(label);
+ if (iterator == _labels.end() || iterator->second.empty()) {
+ return nullptr;
+ }
+
+ return iterator->second.back(); // return last if multiple
+}
+
+std::vector<ParseTree *> ParseTreeMatch::getAll(const std::string &label) {
+ auto iterator = _labels.find(label);
+ if (iterator == _labels.end()) {
+ return {};
+ }
+
+ return iterator->second;
+}
+
+std::map<std::string, std::vector<ParseTree *>>& ParseTreeMatch::getLabels() {
+ return _labels;
+}
+
+ParseTree *ParseTreeMatch::getMismatchedNode() {
+ return _mismatchedNode;
+}
+
+bool ParseTreeMatch::succeeded() {
+ return _mismatchedNode == nullptr;
+}
+
+const ParseTreePattern& ParseTreeMatch::getPattern() {
+ return _pattern;
+}
+
+ParseTree * ParseTreeMatch::getTree() {
+ return _tree;
+}
+
+std::string ParseTreeMatch::toString() {
+ if (succeeded()) {
+ return "Match succeeded; found " + std::to_string(_labels.size()) + " labels";
+ } else {
+ return "Match failed; found " + std::to_string(_labels.size()) + " labels";
+ }
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreeMatch.h b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreeMatch.h
new file mode 100644
index 0000000000..eefde46c83
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreeMatch.h
@@ -0,0 +1,132 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+namespace pattern {
+
+ /// Represents the result of matching a ParseTree against a tree pattern.
+ class ANTLR4CPP_PUBLIC ParseTreeMatch {
+ private:
+ /// This is the backing field for getTree().
+ ParseTree *_tree;
+
+ /// This is the backing field for getPattern().
+ const ParseTreePattern &_pattern;
+
+ /// This is the backing field for getLabels().
+ std::map<std::string, std::vector<ParseTree *>> _labels;
+
+ /// This is the backing field for getMismatchedNode().
+ ParseTree *_mismatchedNode;
+
+ public:
+ /// <summary>
+ /// Constructs a new instance of <seealso cref="ParseTreeMatch"/> from the specified
+ /// parse tree and pattern.
+ /// </summary>
+ /// <param name="tree"> The parse tree to match against the pattern. </param>
+ /// <param name="pattern"> The parse tree pattern. </param>
+ /// <param name="labels"> A mapping from label names to collections of
+ /// <seealso cref="ParseTree"/> objects located by the tree pattern matching process. </param>
+ /// <param name="mismatchedNode"> The first node which failed to match the tree
+ /// pattern during the matching process.
+ /// </param>
+ /// <exception cref="IllegalArgumentException"> if {@code tree} is {@code null} </exception>
+ /// <exception cref="IllegalArgumentException"> if {@code pattern} is {@code null} </exception>
+ /// <exception cref="IllegalArgumentException"> if {@code labels} is {@code null} </exception>
+ ParseTreeMatch(ParseTree *tree, ParseTreePattern const& pattern,
+ const std::map<std::string, std::vector<ParseTree *>> &labels, ParseTree *mismatchedNode);
+ ParseTreeMatch(ParseTreeMatch const&) = default;
+ virtual ~ParseTreeMatch();
+
+ /// <summary>
+ /// Get the last node associated with a specific {@code label}.
+ /// <p/>
+ /// For example, for pattern {@code <id:ID>}, {@code get("id")} returns the
+ /// node matched for that {@code ID}. If more than one node
+ /// matched the specified label, only the last is returned. If there is
+ /// no node associated with the label, this returns {@code null}.
+ /// <p/>
+ /// Pattern tags like {@code <ID>} and {@code <expr>} without labels are
+ /// considered to be labeled with {@code ID} and {@code expr}, respectively.
+ /// </summary>
+ /// <param name="labe"> The label to check.
+ /// </param>
+ /// <returns> The last <seealso cref="ParseTree"/> to match a tag with the specified
+ /// label, or {@code null} if no parse tree matched a tag with the label. </returns>
+ virtual ParseTree* get(const std::string &label);
+
+ /// <summary>
+ /// Return all nodes matching a rule or token tag with the specified label.
+ /// <p/>
+ /// If the {@code label} is the name of a parser rule or token in the
+ /// grammar, the resulting list will contain both the parse trees matching
+ /// rule or tags explicitly labeled with the label and the complete set of
+ /// parse trees matching the labeled and unlabeled tags in the pattern for
+ /// the parser rule or token. For example, if {@code label} is {@code "foo"},
+ /// the result will contain <em>all</em> of the following.
+ ///
+ /// <ul>
+ /// <li>Parse tree nodes matching tags of the form {@code <foo:anyRuleName>} and
+ /// {@code <foo:AnyTokenName>}.</li>
+ /// <li>Parse tree nodes matching tags of the form {@code <anyLabel:foo>}.</li>
+ /// <li>Parse tree nodes matching tags of the form {@code <foo>}.</li>
+ /// </ul>
+ /// </summary>
+ /// <param name="labe"> The label.
+ /// </param>
+ /// <returns> A collection of all <seealso cref="ParseTree"/> nodes matching tags with
+ /// the specified {@code label}. If no nodes matched the label, an empty list
+ /// is returned. </returns>
+ virtual std::vector<ParseTree *> getAll(const std::string &label);
+
+ /// <summary>
+ /// Return a mapping from label &rarr; [list of nodes].
+ /// <p/>
+ /// The map includes special entries corresponding to the names of rules and
+ /// tokens referenced in tags in the original pattern. For additional
+ /// information, see the description of <seealso cref="#getAll(String)"/>.
+ /// </summary>
+ /// <returns> A mapping from labels to parse tree nodes. If the parse tree
+ /// pattern did not contain any rule or token tags, this map will be empty. </returns>
+ virtual std::map<std::string, std::vector<ParseTree *>>& getLabels();
+
+ /// <summary>
+ /// Get the node at which we first detected a mismatch.
+ /// </summary>
+ /// <returns> the node at which we first detected a mismatch, or {@code null}
+ /// if the match was successful. </returns>
+ virtual ParseTree* getMismatchedNode();
+
+ /// <summary>
+ /// Gets a value indicating whether the match operation succeeded.
+ /// </summary>
+ /// <returns> {@code true} if the match operation succeeded; otherwise,
+ /// {@code false}. </returns>
+ virtual bool succeeded();
+
+ /// <summary>
+ /// Get the tree pattern we are matching against.
+ /// </summary>
+ /// <returns> The tree pattern we are matching against. </returns>
+ virtual const ParseTreePattern& getPattern();
+
+ /// <summary>
+ /// Get the parse tree we are trying to match to a pattern.
+ /// </summary>
+ /// <returns> The <seealso cref="ParseTree"/> we are trying to match to a pattern. </returns>
+ virtual ParseTree* getTree();
+
+ virtual std::string toString();
+ };
+
+} // namespace pattern
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePattern.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePattern.cpp
new file mode 100644
index 0000000000..ca7f8f20d6
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePattern.cpp
@@ -0,0 +1,64 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ParseTree.h"
+#include "tree/pattern/ParseTreePatternMatcher.h"
+#include "tree/pattern/ParseTreeMatch.h"
+
+#include "tree/xpath/XPath.h"
+#include "tree/xpath/XPathElement.h"
+
+#include "tree/pattern/ParseTreePattern.h"
+
+using namespace antlr4::tree;
+using namespace antlr4::tree::pattern;
+
+using namespace antlrcpp;
+
+ParseTreePattern::ParseTreePattern(ParseTreePatternMatcher *matcher, const std::string &pattern, int patternRuleIndex_,
+ ParseTree *patternTree)
+ : patternRuleIndex(patternRuleIndex_), _pattern(pattern), _patternTree(patternTree), _matcher(matcher) {
+}
+
+ParseTreePattern::~ParseTreePattern() {
+}
+
+ParseTreeMatch ParseTreePattern::match(ParseTree *tree) {
+ return _matcher->match(tree, *this);
+}
+
+bool ParseTreePattern::matches(ParseTree *tree) {
+ return _matcher->match(tree, *this).succeeded();
+}
+
+std::vector<ParseTreeMatch> ParseTreePattern::findAll(ParseTree *tree, const std::string &xpath) {
+ xpath::XPath finder(_matcher->getParser(), xpath);
+ std::vector<ParseTree *> subtrees = finder.evaluate(tree);
+ std::vector<ParseTreeMatch> matches;
+ for (auto *t : subtrees) {
+ ParseTreeMatch aMatch = match(t);
+ if (aMatch.succeeded()) {
+ matches.push_back(aMatch);
+ }
+ }
+ return matches;
+}
+
+
+ParseTreePatternMatcher *ParseTreePattern::getMatcher() const {
+ return _matcher;
+}
+
+std::string ParseTreePattern::getPattern() const {
+ return _pattern;
+}
+
+int ParseTreePattern::getPatternRuleIndex() const {
+ return patternRuleIndex;
+}
+
+ParseTree* ParseTreePattern::getPatternTree() const {
+ return _patternTree;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePattern.h b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePattern.h
new file mode 100644
index 0000000000..d5b86ff473
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePattern.h
@@ -0,0 +1,105 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+namespace pattern {
+
+ /// <summary>
+ /// A pattern like {@code <ID> = <expr>;} converted to a <seealso cref="ParseTree"/> by
+ /// <seealso cref="ParseTreePatternMatcher#compile(String, int)"/>.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC ParseTreePattern {
+ public:
+ /// <summary>
+ /// Construct a new instance of the <seealso cref="ParseTreePattern"/> class.
+ /// </summary>
+ /// <param name="matcher"> The <seealso cref="ParseTreePatternMatcher"/> which created this
+ /// tree pattern. </param>
+ /// <param name="pattern"> The tree pattern in concrete syntax form. </param>
+ /// <param name="patternRuleIndex"> The parser rule which serves as the root of the
+ /// tree pattern. </param>
+ /// <param name="patternTree"> The tree pattern in <seealso cref="ParseTree"/> form. </param>
+ ParseTreePattern(ParseTreePatternMatcher *matcher, const std::string &pattern, int patternRuleIndex,
+ ParseTree *patternTree);
+ ParseTreePattern(ParseTreePattern const&) = default;
+ virtual ~ParseTreePattern();
+
+ /// <summary>
+ /// Match a specific parse tree against this tree pattern.
+ /// </summary>
+ /// <param name="tree"> The parse tree to match against this tree pattern. </param>
+ /// <returns> A <seealso cref="ParseTreeMatch"/> object describing the result of the
+ /// match operation. The <seealso cref="ParseTreeMatch#succeeded()"/> method can be
+ /// used to determine whether or not the match was successful. </returns>
+ virtual ParseTreeMatch match(ParseTree *tree);
+
+ /// <summary>
+ /// Determine whether or not a parse tree matches this tree pattern.
+ /// </summary>
+ /// <param name="tree"> The parse tree to match against this tree pattern. </param>
+ /// <returns> {@code true} if {@code tree} is a match for the current tree
+ /// pattern; otherwise, {@code false}. </returns>
+ virtual bool matches(ParseTree *tree);
+
+ /// Find all nodes using XPath and then try to match those subtrees against
+ /// this tree pattern.
+ /// @param tree The ParseTree to match against this pattern.
+ /// @param xpath An expression matching the nodes
+ ///
+ /// @returns A collection of ParseTreeMatch objects describing the
+ /// successful matches. Unsuccessful matches are omitted from the result,
+ /// regardless of the reason for the failure.
+ virtual std::vector<ParseTreeMatch> findAll(ParseTree *tree, const std::string &xpath);
+
+ /// <summary>
+ /// Get the <seealso cref="ParseTreePatternMatcher"/> which created this tree pattern.
+ /// </summary>
+ /// <returns> The <seealso cref="ParseTreePatternMatcher"/> which created this tree
+ /// pattern. </returns>
+ virtual ParseTreePatternMatcher *getMatcher() const;
+
+ /// <summary>
+ /// Get the tree pattern in concrete syntax form.
+ /// </summary>
+ /// <returns> The tree pattern in concrete syntax form. </returns>
+ virtual std::string getPattern() const;
+
+ /// <summary>
+ /// Get the parser rule which serves as the outermost rule for the tree
+ /// pattern.
+ /// </summary>
+ /// <returns> The parser rule which serves as the outermost rule for the tree
+ /// pattern. </returns>
+ virtual int getPatternRuleIndex() const;
+
+ /// <summary>
+ /// Get the tree pattern as a <seealso cref="ParseTree"/>. The rule and token tags from
+ /// the pattern are present in the parse tree as terminal nodes with a symbol
+ /// of type <seealso cref="RuleTagToken"/> or <seealso cref="TokenTagToken"/>.
+ /// </summary>
+ /// <returns> The tree pattern as a <seealso cref="ParseTree"/>. </returns>
+ virtual ParseTree* getPatternTree() const;
+
+ private:
+ const int patternRuleIndex;
+
+ /// This is the backing field for <seealso cref="#getPattern()"/>.
+ const std::string _pattern;
+
+ /// This is the backing field for <seealso cref="#getPatternTree()"/>.
+ ParseTree *_patternTree;
+
+ /// This is the backing field for <seealso cref="#getMatcher()"/>.
+ ParseTreePatternMatcher *const _matcher;
+ };
+
+} // namespace pattern
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePatternMatcher.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePatternMatcher.cpp
new file mode 100644
index 0000000000..4c28658954
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePatternMatcher.cpp
@@ -0,0 +1,370 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/pattern/ParseTreePattern.h"
+#include "tree/pattern/ParseTreeMatch.h"
+#include "tree/TerminalNode.h"
+#include "CommonTokenStream.h"
+#include "ParserInterpreter.h"
+#include "tree/pattern/TokenTagToken.h"
+#include "ParserRuleContext.h"
+#include "tree/pattern/RuleTagToken.h"
+#include "tree/pattern/TagChunk.h"
+#include "atn/ATN.h"
+#include "Lexer.h"
+#include "BailErrorStrategy.h"
+
+#include "ListTokenSource.h"
+#include "tree/pattern/TextChunk.h"
+#include "ANTLRInputStream.h"
+#include "support/Arrays.h"
+#include "Exceptions.h"
+#include "support/CPPUtils.h"
+
+#include "tree/pattern/ParseTreePatternMatcher.h"
+
+using namespace antlr4;
+using namespace antlr4::tree;
+using namespace antlr4::tree::pattern;
+using namespace antlrcpp;
+
+ParseTreePatternMatcher::CannotInvokeStartRule::CannotInvokeStartRule(const RuntimeException &e) : RuntimeException(e.what()) {
+}
+
+ParseTreePatternMatcher::CannotInvokeStartRule::~CannotInvokeStartRule() {
+}
+
+ParseTreePatternMatcher::StartRuleDoesNotConsumeFullPattern::~StartRuleDoesNotConsumeFullPattern() {
+}
+
+ParseTreePatternMatcher::ParseTreePatternMatcher(Lexer *lexer, Parser *parser) : _lexer(lexer), _parser(parser) {
+ InitializeInstanceFields();
+}
+
+ParseTreePatternMatcher::~ParseTreePatternMatcher() {
+}
+
+void ParseTreePatternMatcher::setDelimiters(const std::string &start, const std::string &stop, const std::string &escapeLeft) {
+ if (start.empty()) {
+ throw IllegalArgumentException("start cannot be null or empty");
+ }
+
+ if (stop.empty()) {
+ throw IllegalArgumentException("stop cannot be null or empty");
+ }
+
+ _start = start;
+ _stop = stop;
+ _escape = escapeLeft;
+}
+
+bool ParseTreePatternMatcher::matches(ParseTree *tree, const std::string &pattern, int patternRuleIndex) {
+ ParseTreePattern p = compile(pattern, patternRuleIndex);
+ return matches(tree, p);
+}
+
+bool ParseTreePatternMatcher::matches(ParseTree *tree, const ParseTreePattern &pattern) {
+ std::map<std::string, std::vector<ParseTree *>> labels;
+ ParseTree *mismatchedNode = matchImpl(tree, pattern.getPatternTree(), labels);
+ return mismatchedNode == nullptr;
+}
+
+ParseTreeMatch ParseTreePatternMatcher::match(ParseTree *tree, const std::string &pattern, int patternRuleIndex) {
+ ParseTreePattern p = compile(pattern, patternRuleIndex);
+ return match(tree, p);
+}
+
+ParseTreeMatch ParseTreePatternMatcher::match(ParseTree *tree, const ParseTreePattern &pattern) {
+ std::map<std::string, std::vector<ParseTree *>> labels;
+ tree::ParseTree *mismatchedNode = matchImpl(tree, pattern.getPatternTree(), labels);
+ return ParseTreeMatch(tree, pattern, labels, mismatchedNode);
+}
+
+ParseTreePattern ParseTreePatternMatcher::compile(const std::string &pattern, int patternRuleIndex) {
+ ListTokenSource tokenSrc(tokenize(pattern));
+ CommonTokenStream tokens(&tokenSrc);
+
+ ParserInterpreter parserInterp(_parser->getGrammarFileName(), _parser->getVocabulary(),
+ _parser->getRuleNames(), _parser->getATNWithBypassAlts(), &tokens);
+
+ ParserRuleContext *tree = nullptr;
+ try {
+ parserInterp.setErrorHandler(std::make_shared<BailErrorStrategy>());
+ tree = parserInterp.parse(patternRuleIndex);
+ } catch (ParseCancellationException &e) {
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023026
+ // rethrow_if_nested is not available before VS 2015.
+ throw e;
+#else
+ std::rethrow_if_nested(e); // Unwrap the nested exception.
+#endif
+ } catch (RecognitionException &re) {
+ throw re;
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023026
+ } catch (std::exception &e) {
+ // throw_with_nested is not available before VS 2015.
+ throw e;
+#else
+ } catch (std::exception & /*e*/) {
+ std::throw_with_nested(RuntimeException("Cannot invoke start rule")); // Wrap any other exception.
+#endif
+ }
+
+ // Make sure tree pattern compilation checks for a complete parse
+ if (tokens.LA(1) != Token::EOF) {
+ throw StartRuleDoesNotConsumeFullPattern();
+ }
+
+ return ParseTreePattern(this, pattern, patternRuleIndex, tree);
+}
+
+Lexer* ParseTreePatternMatcher::getLexer() {
+ return _lexer;
+}
+
+Parser* ParseTreePatternMatcher::getParser() {
+ return _parser;
+}
+
+ParseTree* ParseTreePatternMatcher::matchImpl(ParseTree *tree, ParseTree *patternTree,
+ std::map<std::string, std::vector<ParseTree *>> &labels) {
+ if (tree == nullptr) {
+ throw IllegalArgumentException("tree cannot be nul");
+ }
+
+ if (patternTree == nullptr) {
+ throw IllegalArgumentException("patternTree cannot be nul");
+ }
+
+ // x and <ID>, x and y, or x and x; or could be mismatched types
+ if (is<TerminalNode *>(tree) && is<TerminalNode *>(patternTree)) {
+ TerminalNode *t1 = dynamic_cast<TerminalNode *>(tree);
+ TerminalNode *t2 = dynamic_cast<TerminalNode *>(patternTree);
+
+ ParseTree *mismatchedNode = nullptr;
+ // both are tokens and they have same type
+ if (t1->getSymbol()->getType() == t2->getSymbol()->getType()) {
+ if (is<TokenTagToken *>(t2->getSymbol())) { // x and <ID>
+ TokenTagToken *tokenTagToken = dynamic_cast<TokenTagToken *>(t2->getSymbol());
+
+ // track label->list-of-nodes for both token name and label (if any)
+ labels[tokenTagToken->getTokenName()].push_back(tree);
+ if (tokenTagToken->getLabel() != "") {
+ labels[tokenTagToken->getLabel()].push_back(tree);
+ }
+ } else if (t1->getText() == t2->getText()) {
+ // x and x
+ } else {
+ // x and y
+ if (mismatchedNode == nullptr) {
+ mismatchedNode = t1;
+ }
+ }
+ } else {
+ if (mismatchedNode == nullptr) {
+ mismatchedNode = t1;
+ }
+ }
+
+ return mismatchedNode;
+ }
+
+ if (is<ParserRuleContext *>(tree) && is<ParserRuleContext *>(patternTree)) {
+ ParserRuleContext *r1 = dynamic_cast<ParserRuleContext *>(tree);
+ ParserRuleContext *r2 = dynamic_cast<ParserRuleContext *>(patternTree);
+ ParseTree *mismatchedNode = nullptr;
+
+ // (expr ...) and <expr>
+ RuleTagToken *ruleTagToken = getRuleTagToken(r2);
+ if (ruleTagToken != nullptr) {
+ //ParseTreeMatch *m = nullptr; // unused?
+ if (r1->getRuleIndex() == r2->getRuleIndex()) {
+ // track label->list-of-nodes for both rule name and label (if any)
+ labels[ruleTagToken->getRuleName()].push_back(tree);
+ if (ruleTagToken->getLabel() != "") {
+ labels[ruleTagToken->getLabel()].push_back(tree);
+ }
+ } else {
+ if (!mismatchedNode) {
+ mismatchedNode = r1;
+ }
+ }
+
+ return mismatchedNode;
+ }
+
+ // (expr ...) and (expr ...)
+ if (r1->children.size() != r2->children.size()) {
+ if (mismatchedNode == nullptr) {
+ mismatchedNode = r1;
+ }
+
+ return mismatchedNode;
+ }
+
+ std::size_t n = r1->children.size();
+ for (size_t i = 0; i < n; i++) {
+ ParseTree *childMatch = matchImpl(r1->children[i], patternTree->children[i], labels);
+ if (childMatch) {
+ return childMatch;
+ }
+ }
+
+ return mismatchedNode;
+ }
+
+ // if nodes aren't both tokens or both rule nodes, can't match
+ return tree;
+}
+
+RuleTagToken* ParseTreePatternMatcher::getRuleTagToken(ParseTree *t) {
+ if (t->children.size() == 1 && is<TerminalNode *>(t->children[0])) {
+ TerminalNode *c = dynamic_cast<TerminalNode *>(t->children[0]);
+ if (is<RuleTagToken *>(c->getSymbol())) {
+ return dynamic_cast<RuleTagToken *>(c->getSymbol());
+ }
+ }
+ return nullptr;
+}
+
+std::vector<std::unique_ptr<Token>> ParseTreePatternMatcher::tokenize(const std::string &pattern) {
+ // split pattern into chunks: sea (raw input) and islands (<ID>, <expr>)
+ std::vector<Chunk> chunks = split(pattern);
+
+ // create token stream from text and tags
+ std::vector<std::unique_ptr<Token>> tokens;
+ for (auto chunk : chunks) {
+ if (is<TagChunk *>(&chunk)) {
+ TagChunk &tagChunk = (TagChunk&)chunk;
+ // add special rule token or conjure up new token from name
+ if (isupper(tagChunk.getTag()[0])) {
+ size_t ttype = _parser->getTokenType(tagChunk.getTag());
+ if (ttype == Token::INVALID_TYPE) {
+ throw IllegalArgumentException("Unknown token " + tagChunk.getTag() + " in pattern: " + pattern);
+ }
+ tokens.emplace_back(new TokenTagToken(tagChunk.getTag(), (int)ttype, tagChunk.getLabel()));
+ } else if (islower(tagChunk.getTag()[0])) {
+ size_t ruleIndex = _parser->getRuleIndex(tagChunk.getTag());
+ if (ruleIndex == INVALID_INDEX) {
+ throw IllegalArgumentException("Unknown rule " + tagChunk.getTag() + " in pattern: " + pattern);
+ }
+ size_t ruleImaginaryTokenType = _parser->getATNWithBypassAlts().ruleToTokenType[ruleIndex];
+ tokens.emplace_back(new RuleTagToken(tagChunk.getTag(), ruleImaginaryTokenType, tagChunk.getLabel()));
+ } else {
+ throw IllegalArgumentException("invalid tag: " + tagChunk.getTag() + " in pattern: " + pattern);
+ }
+ } else {
+ TextChunk &textChunk = (TextChunk&)chunk;
+ ANTLRInputStream input(textChunk.getText());
+ _lexer->setInputStream(&input);
+ std::unique_ptr<Token> t(_lexer->nextToken());
+ while (t->getType() != Token::EOF) {
+ tokens.push_back(std::move(t));
+ t = _lexer->nextToken();
+ }
+ _lexer->setInputStream(nullptr);
+ }
+ }
+
+ return tokens;
+}
+
+std::vector<Chunk> ParseTreePatternMatcher::split(const std::string &pattern) {
+ size_t p = 0;
+ size_t n = pattern.length();
+ std::vector<Chunk> chunks;
+
+ // find all start and stop indexes first, then collect
+ std::vector<size_t> starts;
+ std::vector<size_t> stops;
+ while (p < n) {
+ if (p == pattern.find(_escape + _start,p)) {
+ p += _escape.length() + _start.length();
+ } else if (p == pattern.find(_escape + _stop,p)) {
+ p += _escape.length() + _stop.length();
+ } else if (p == pattern.find(_start,p)) {
+ starts.push_back(p);
+ p += _start.length();
+ } else if (p == pattern.find(_stop,p)) {
+ stops.push_back(p);
+ p += _stop.length();
+ } else {
+ p++;
+ }
+ }
+
+ if (starts.size() > stops.size()) {
+ throw IllegalArgumentException("unterminated tag in pattern: " + pattern);
+ }
+
+ if (starts.size() < stops.size()) {
+ throw IllegalArgumentException("missing start tag in pattern: " + pattern);
+ }
+
+ size_t ntags = starts.size();
+ for (size_t i = 0; i < ntags; i++) {
+ if (starts[i] >= stops[i]) {
+ throw IllegalArgumentException("tag delimiters out of order in pattern: " + pattern);
+ }
+ }
+
+ // collect into chunks now
+ if (ntags == 0) {
+ std::string text = pattern.substr(0, n);
+ chunks.push_back(TextChunk(text));
+ }
+
+ if (ntags > 0 && starts[0] > 0) { // copy text up to first tag into chunks
+ std::string text = pattern.substr(0, starts[0]);
+ chunks.push_back(TextChunk(text));
+ }
+
+ for (size_t i = 0; i < ntags; i++) {
+ // copy inside of <tag>
+ std::string tag = pattern.substr(starts[i] + _start.length(), stops[i] - (starts[i] + _start.length()));
+ std::string ruleOrToken = tag;
+ std::string label = "";
+ size_t colon = tag.find(':');
+ if (colon != std::string::npos) {
+ label = tag.substr(0,colon);
+ ruleOrToken = tag.substr(colon + 1, tag.length() - (colon + 1));
+ }
+ chunks.push_back(TagChunk(label, ruleOrToken));
+ if (i + 1 < ntags) {
+ // copy from end of <tag> to start of next
+ std::string text = pattern.substr(stops[i] + _stop.length(), starts[i + 1] - (stops[i] + _stop.length()));
+ chunks.push_back(TextChunk(text));
+ }
+ }
+
+ if (ntags > 0) {
+ size_t afterLastTag = stops[ntags - 1] + _stop.length();
+ if (afterLastTag < n) { // copy text from end of last tag to end
+ std::string text = pattern.substr(afterLastTag, n - afterLastTag);
+ chunks.push_back(TextChunk(text));
+ }
+ }
+
+ // strip out all backslashes from text chunks but not tags
+ for (size_t i = 0; i < chunks.size(); i++) {
+ Chunk &c = chunks[i];
+ if (is<TextChunk *>(&c)) {
+ TextChunk &tc = (TextChunk&)c;
+ std::string unescaped = tc.getText();
+ unescaped.erase(std::remove(unescaped.begin(), unescaped.end(), '\\'), unescaped.end());
+ if (unescaped.length() < tc.getText().length()) {
+ chunks[i] = TextChunk(unescaped);
+ }
+ }
+ }
+
+ return chunks;
+}
+
+void ParseTreePatternMatcher::InitializeInstanceFields() {
+ _start = "<";
+ _stop = ">";
+ _escape = "\\";
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePatternMatcher.h b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePatternMatcher.h
new file mode 100644
index 0000000000..8641fc9a00
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/ParseTreePatternMatcher.h
@@ -0,0 +1,185 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Exceptions.h"
+
+namespace antlr4 {
+namespace tree {
+namespace pattern {
+
+ /// <summary>
+ /// A tree pattern matching mechanism for ANTLR <seealso cref="ParseTree"/>s.
+ /// <p/>
+ /// Patterns are strings of source input text with special tags representing
+ /// token or rule references such as:
+ /// <p/>
+ /// {@code <ID> = <expr>;}
+ /// <p/>
+ /// Given a pattern start rule such as {@code statement}, this object constructs
+ /// a <seealso cref="ParseTree"/> with placeholders for the {@code ID} and {@code expr}
+ /// subtree. Then the <seealso cref="#match"/> routines can compare an actual
+ /// <seealso cref="ParseTree"/> from a parse with this pattern. Tag {@code <ID>} matches
+ /// any {@code ID} token and tag {@code <expr>} references the result of the
+ /// {@code expr} rule (generally an instance of {@code ExprContext}.
+ /// <p/>
+ /// Pattern {@code x = 0;} is a similar pattern that matches the same pattern
+ /// except that it requires the identifier to be {@code x} and the expression to
+ /// be {@code 0}.
+ /// <p/>
+ /// The <seealso cref="#matches"/> routines return {@code true} or {@code false} based
+ /// upon a match for the tree rooted at the parameter sent in. The
+ /// <seealso cref="#match"/> routines return a <seealso cref="ParseTreeMatch"/> object that
+ /// contains the parse tree, the parse tree pattern, and a map from tag name to
+ /// matched nodes (more below). A subtree that fails to match, returns with
+ /// <seealso cref="ParseTreeMatch#mismatchedNode"/> set to the first tree node that did not
+ /// match.
+ /// <p/>
+ /// For efficiency, you can compile a tree pattern in string form to a
+ /// <seealso cref="ParseTreePattern"/> object.
+ /// <p/>
+ /// See {@code TestParseTreeMatcher} for lots of examples.
+ /// <seealso cref="ParseTreePattern"/> has two static helper methods:
+ /// <seealso cref="ParseTreePattern#findAll"/> and <seealso cref="ParseTreePattern#match"/> that
+ /// are easy to use but not super efficient because they create new
+ /// <seealso cref="ParseTreePatternMatcher"/> objects each time and have to compile the
+ /// pattern in string form before using it.
+ /// <p/>
+ /// The lexer and parser that you pass into the <seealso cref="ParseTreePatternMatcher"/>
+ /// constructor are used to parse the pattern in string form. The lexer converts
+ /// the {@code <ID> = <expr>;} into a sequence of four tokens (assuming lexer
+ /// throws out whitespace or puts it on a hidden channel). Be aware that the
+ /// input stream is reset for the lexer (but not the parser; a
+ /// <seealso cref="ParserInterpreter"/> is created to parse the input.). Any user-defined
+ /// fields you have put into the lexer might get changed when this mechanism asks
+ /// it to scan the pattern string.
+ /// <p/>
+ /// Normally a parser does not accept token {@code <expr>} as a valid
+ /// {@code expr} but, from the parser passed in, we create a special version of
+ /// the underlying grammar representation (an <seealso cref="ATN"/>) that allows imaginary
+ /// tokens representing rules ({@code <expr>}) to match entire rules. We call
+ /// these <em>bypass alternatives</em>.
+ /// <p/>
+ /// Delimiters are {@code <} and {@code >}, with {@code \} as the escape string
+ /// by default, but you can set them to whatever you want using
+ /// <seealso cref="#setDelimiters"/>. You must escape both start and stop strings
+ /// {@code \<} and {@code \>}.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC ParseTreePatternMatcher {
+ public:
+ class CannotInvokeStartRule : public RuntimeException {
+ public:
+ CannotInvokeStartRule(const RuntimeException &e);
+ ~CannotInvokeStartRule();
+ };
+
+ // Fixes https://github.com/antlr/antlr4/issues/413
+ // "Tree pattern compilation doesn't check for a complete parse"
+ class StartRuleDoesNotConsumeFullPattern : public RuntimeException {
+ public:
+ StartRuleDoesNotConsumeFullPattern() = default;
+ StartRuleDoesNotConsumeFullPattern(StartRuleDoesNotConsumeFullPattern const&) = default;
+ ~StartRuleDoesNotConsumeFullPattern();
+
+ StartRuleDoesNotConsumeFullPattern& operator=(StartRuleDoesNotConsumeFullPattern const&) = default;
+ };
+
+ /// Constructs a <seealso cref="ParseTreePatternMatcher"/> or from a <seealso cref="Lexer"/> and
+ /// <seealso cref="Parser"/> object. The lexer input stream is altered for tokenizing
+ /// the tree patterns. The parser is used as a convenient mechanism to get
+ /// the grammar name, plus token, rule names.
+ ParseTreePatternMatcher(Lexer *lexer, Parser *parser);
+ virtual ~ParseTreePatternMatcher();
+
+ /// <summary>
+ /// Set the delimiters used for marking rule and token tags within concrete
+ /// syntax used by the tree pattern parser.
+ /// </summary>
+ /// <param name="start"> The start delimiter. </param>
+ /// <param name="stop"> The stop delimiter. </param>
+ /// <param name="escapeLeft"> The escape sequence to use for escaping a start or stop delimiter.
+ /// </param>
+ /// <exception cref="IllegalArgumentException"> if {@code start} is {@code null} or empty. </exception>
+ /// <exception cref="IllegalArgumentException"> if {@code stop} is {@code null} or empty. </exception>
+ virtual void setDelimiters(const std::string &start, const std::string &stop, const std::string &escapeLeft);
+
+ /// <summary>
+ /// Does {@code pattern} matched as rule {@code patternRuleIndex} match {@code tree}? </summary>
+ virtual bool matches(ParseTree *tree, const std::string &pattern, int patternRuleIndex);
+
+ /// <summary>
+ /// Does {@code pattern} matched as rule patternRuleIndex match tree? Pass in a
+ /// compiled pattern instead of a string representation of a tree pattern.
+ /// </summary>
+ virtual bool matches(ParseTree *tree, const ParseTreePattern &pattern);
+
+ /// <summary>
+ /// Compare {@code pattern} matched as rule {@code patternRuleIndex} against
+ /// {@code tree} and return a <seealso cref="ParseTreeMatch"/> object that contains the
+ /// matched elements, or the node at which the match failed.
+ /// </summary>
+ virtual ParseTreeMatch match(ParseTree *tree, const std::string &pattern, int patternRuleIndex);
+
+ /// <summary>
+ /// Compare {@code pattern} matched against {@code tree} and return a
+ /// <seealso cref="ParseTreeMatch"/> object that contains the matched elements, or the
+ /// node at which the match failed. Pass in a compiled pattern instead of a
+ /// string representation of a tree pattern.
+ /// </summary>
+ virtual ParseTreeMatch match(ParseTree *tree, const ParseTreePattern &pattern);
+
+ /// <summary>
+ /// For repeated use of a tree pattern, compile it to a
+ /// <seealso cref="ParseTreePattern"/> using this method.
+ /// </summary>
+ virtual ParseTreePattern compile(const std::string &pattern, int patternRuleIndex);
+
+ /// <summary>
+ /// Used to convert the tree pattern string into a series of tokens. The
+ /// input stream is reset.
+ /// </summary>
+ virtual Lexer* getLexer();
+
+ /// <summary>
+ /// Used to collect to the grammar file name, token names, rule names for
+ /// used to parse the pattern into a parse tree.
+ /// </summary>
+ virtual Parser* getParser();
+
+ // ---- SUPPORT CODE ----
+
+ virtual std::vector<std::unique_ptr<Token>> tokenize(const std::string &pattern);
+
+ /// Split "<ID> = <e:expr>;" into 4 chunks for tokenizing by tokenize().
+ virtual std::vector<Chunk> split(const std::string &pattern);
+
+ protected:
+ std::string _start;
+ std::string _stop;
+ std::string _escape; // e.g., \< and \> must escape BOTH!
+
+ /// Recursively walk {@code tree} against {@code patternTree}, filling
+ /// {@code match.}<seealso cref="ParseTreeMatch#labels labels"/>.
+ ///
+ /// <returns> the first node encountered in {@code tree} which does not match
+ /// a corresponding node in {@code patternTree}, or {@code null} if the match
+ /// was successful. The specific node returned depends on the matching
+ /// algorithm used by the implementation, and may be overridden. </returns>
+ virtual ParseTree* matchImpl(ParseTree *tree, ParseTree *patternTree, std::map<std::string, std::vector<ParseTree *>> &labels);
+
+ /// Is t <expr> subtree?
+ virtual RuleTagToken* getRuleTagToken(ParseTree *t);
+
+ private:
+ Lexer *_lexer;
+ Parser *_parser;
+
+ void InitializeInstanceFields();
+ };
+
+} // namespace pattern
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/RuleTagToken.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/RuleTagToken.cpp
new file mode 100644
index 0000000000..6f3fb73446
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/RuleTagToken.cpp
@@ -0,0 +1,77 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Exceptions.h"
+
+#include "tree/pattern/RuleTagToken.h"
+
+using namespace antlr4::tree::pattern;
+
+RuleTagToken::RuleTagToken(const std::string &/*ruleName*/, int _bypassTokenType) : bypassTokenType(_bypassTokenType) {
+}
+
+RuleTagToken::RuleTagToken(const std::string &ruleName, size_t bypassTokenType, const std::string &label)
+ : ruleName(ruleName), bypassTokenType(bypassTokenType), label(label) {
+ if (ruleName.empty()) {
+ throw IllegalArgumentException("ruleName cannot be null or empty.");
+ }
+
+}
+
+std::string RuleTagToken::getRuleName() const {
+ return ruleName;
+}
+
+std::string RuleTagToken::getLabel() const {
+ return label;
+}
+
+size_t RuleTagToken::getChannel() const {
+ return DEFAULT_CHANNEL;
+}
+
+std::string RuleTagToken::getText() const {
+ if (label != "") {
+ return std::string("<") + label + std::string(":") + ruleName + std::string(">");
+ }
+
+ return std::string("<") + ruleName + std::string(">");
+}
+
+size_t RuleTagToken::getType() const {
+ return bypassTokenType;
+}
+
+size_t RuleTagToken::getLine() const {
+ return 0;
+}
+
+size_t RuleTagToken::getCharPositionInLine() const {
+ return INVALID_INDEX;
+}
+
+size_t RuleTagToken::getTokenIndex() const {
+ return INVALID_INDEX;
+}
+
+size_t RuleTagToken::getStartIndex() const {
+ return INVALID_INDEX;
+}
+
+size_t RuleTagToken::getStopIndex() const {
+ return INVALID_INDEX;
+}
+
+antlr4::TokenSource *RuleTagToken::getTokenSource() const {
+ return nullptr;
+}
+
+antlr4::CharStream *RuleTagToken::getInputStream() const {
+ return nullptr;
+}
+
+std::string RuleTagToken::toString() const {
+ return ruleName + ":" + std::to_string(bypassTokenType);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/RuleTagToken.h b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/RuleTagToken.h
new file mode 100644
index 0000000000..cb0e50399e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/RuleTagToken.h
@@ -0,0 +1,117 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Token.h"
+
+namespace antlr4 {
+namespace tree {
+namespace pattern {
+
+ /// <summary>
+ /// A <seealso cref="Token"/> object representing an entire subtree matched by a parser
+ /// rule; e.g., {@code <expr>}. These tokens are created for <seealso cref="TagChunk"/>
+ /// chunks where the tag corresponds to a parser rule.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC RuleTagToken : public Token {
+ /// <summary>
+ /// This is the backing field for <seealso cref="#getRuleName"/>.
+ /// </summary>
+ private:
+ const std::string ruleName;
+
+ /// The token type for the current token. This is the token type assigned to
+ /// the bypass alternative for the rule during ATN deserialization.
+ const size_t bypassTokenType;
+
+ /// This is the backing field for <seealso cref="#getLabe"/>.
+ const std::string label;
+
+ public:
+ /// <summary>
+ /// Constructs a new instance of <seealso cref="RuleTagToken"/> with the specified rule
+ /// name and bypass token type and no label.
+ /// </summary>
+ /// <param name="ruleName"> The name of the parser rule this rule tag matches. </param>
+ /// <param name="bypassTokenType"> The bypass token type assigned to the parser rule.
+ /// </param>
+ /// <exception cref="IllegalArgumentException"> if {@code ruleName} is {@code null}
+ /// or empty. </exception>
+ RuleTagToken(const std::string &ruleName, int bypassTokenType); //this(ruleName, bypassTokenType, nullptr);
+
+ /// <summary>
+ /// Constructs a new instance of <seealso cref="RuleTagToken"/> with the specified rule
+ /// name, bypass token type, and label.
+ /// </summary>
+ /// <param name="ruleName"> The name of the parser rule this rule tag matches. </param>
+ /// <param name="bypassTokenType"> The bypass token type assigned to the parser rule. </param>
+ /// <param name="label"> The label associated with the rule tag, or {@code null} if
+ /// the rule tag is unlabeled.
+ /// </param>
+ /// <exception cref="IllegalArgumentException"> if {@code ruleName} is {@code null}
+ /// or empty. </exception>
+ RuleTagToken(const std::string &ruleName, size_t bypassTokenType, const std::string &label);
+
+ /// <summary>
+ /// Gets the name of the rule associated with this rule tag.
+ /// </summary>
+ /// <returns> The name of the parser rule associated with this rule tag. </returns>
+ std::string getRuleName() const;
+
+ /// <summary>
+ /// Gets the label associated with the rule tag.
+ /// </summary>
+ /// <returns> The name of the label associated with the rule tag, or
+ /// {@code null} if this is an unlabeled rule tag. </returns>
+ std::string getLabel() const;
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// <p/>
+ /// Rule tag tokens are always placed on the <seealso cref="#DEFAULT_CHANNE"/>.
+ /// </summary>
+ virtual size_t getChannel() const override;
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// <p/>
+ /// This method returns the rule tag formatted with {@code <} and {@code >}
+ /// delimiters.
+ /// </summary>
+ virtual std::string getText() const override;
+
+ /// Rule tag tokens have types assigned according to the rule bypass
+ /// transitions created during ATN deserialization.
+ virtual size_t getType() const override;
+
+ /// The implementation for <seealso cref="RuleTagToken"/> always returns 0.
+ virtual size_t getLine() const override;
+
+ /// The implementation for <seealso cref="RuleTagToken"/> always returns INVALID_INDEX.
+ virtual size_t getCharPositionInLine() const override;
+
+ /// The implementation for <seealso cref="RuleTagToken"/> always returns INVALID_INDEX.
+ virtual size_t getTokenIndex() const override;
+
+ /// The implementation for <seealso cref="RuleTagToken"/> always returns INVALID_INDEX.
+ virtual size_t getStartIndex() const override;
+
+ /// The implementation for <seealso cref="RuleTagToken"/> always returns INVALID_INDEX.
+ virtual size_t getStopIndex() const override;
+
+ /// The implementation for <seealso cref="RuleTagToken"/> always returns {@code null}.
+ virtual TokenSource *getTokenSource() const override;
+
+ /// The implementation for <seealso cref="RuleTagToken"/> always returns {@code null}.
+ virtual CharStream *getInputStream() const override;
+
+ /// The implementation for <seealso cref="RuleTagToken"/> returns a string of the form {@code ruleName:bypassTokenType}.
+ virtual std::string toString() const override;
+ };
+
+} // namespace pattern
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TagChunk.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TagChunk.cpp
new file mode 100644
index 0000000000..63e97aeaa2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TagChunk.cpp
@@ -0,0 +1,39 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Exceptions.h"
+
+#include "tree/pattern/TagChunk.h"
+
+using namespace antlr4::tree::pattern;
+
+TagChunk::TagChunk(const std::string &tag) : TagChunk("", tag) {
+}
+
+TagChunk::TagChunk(const std::string &label, const std::string &tag) : _tag(tag), _label(label) {
+ if (tag.empty()) {
+ throw IllegalArgumentException("tag cannot be null or empty");
+ }
+
+}
+
+TagChunk::~TagChunk() {
+}
+
+std::string TagChunk::getTag() {
+ return _tag;
+}
+
+std::string TagChunk::getLabel() {
+ return _label;
+}
+
+std::string TagChunk::toString() {
+ if (!_label.empty()) {
+ return _label + ":" + _tag;
+ }
+
+ return _tag;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TagChunk.h b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TagChunk.h
new file mode 100644
index 0000000000..1cdae78995
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TagChunk.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Chunk.h"
+
+namespace antlr4 {
+namespace tree {
+namespace pattern {
+
+ /// <summary>
+ /// Represents a placeholder tag in a tree pattern. A tag can have any of the
+ /// following forms.
+ ///
+ /// <ul>
+ /// <li>{@code expr}: An unlabeled placeholder for a parser rule {@code expr}.</li>
+ /// <li>{@code ID}: An unlabeled placeholder for a token of type {@code ID}.</li>
+ /// <li>{@code e:expr}: A labeled placeholder for a parser rule {@code expr}.</li>
+ /// <li>{@code id:ID}: A labeled placeholder for a token of type {@code ID}.</li>
+ /// </ul>
+ ///
+ /// This class does not perform any validation on the tag or label names aside
+ /// from ensuring that the tag is a non-null, non-empty string.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC TagChunk : public Chunk {
+ public:
+ /// <summary>
+ /// Construct a new instance of <seealso cref="TagChunk"/> using the specified tag and
+ /// no label.
+ /// </summary>
+ /// <param name="tag"> The tag, which should be the name of a parser rule or token
+ /// type.
+ /// </param>
+ /// <exception cref="IllegalArgumentException"> if {@code tag} is {@code null} or
+ /// empty. </exception>
+ TagChunk(const std::string &tag);
+ virtual ~TagChunk();
+
+ /// <summary>
+ /// Construct a new instance of <seealso cref="TagChunk"/> using the specified label
+ /// and tag.
+ /// </summary>
+ /// <param name="label"> The label for the tag. If this is {@code null}, the
+ /// <seealso cref="TagChunk"/> represents an unlabeled tag. </param>
+ /// <param name="tag"> The tag, which should be the name of a parser rule or token
+ /// type.
+ /// </param>
+ /// <exception cref="IllegalArgumentException"> if {@code tag} is {@code null} or
+ /// empty. </exception>
+ TagChunk(const std::string &label, const std::string &tag);
+
+ /// <summary>
+ /// Get the tag for this chunk.
+ /// </summary>
+ /// <returns> The tag for the chunk. </returns>
+ std::string getTag();
+
+ /// <summary>
+ /// Get the label, if any, assigned to this chunk.
+ /// </summary>
+ /// <returns> The label assigned to this chunk, or {@code null} if no label is
+ /// assigned to the chunk. </returns>
+ std::string getLabel();
+
+ /// <summary>
+ /// This method returns a text representation of the tag chunk. Labeled tags
+ /// are returned in the form {@code label:tag}, and unlabeled tags are
+ /// returned as just the tag name.
+ /// </summary>
+ virtual std::string toString() override;
+
+ private:
+ /// This is the backing field for <seealso cref="#getTag"/>.
+ const std::string _tag;
+ /// <summary>
+ /// This is the backing field for <seealso cref="#getLabe"/>.
+ /// </summary>
+ const std::string _label;
+ };
+
+} // namespace pattern
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TextChunk.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TextChunk.cpp
new file mode 100644
index 0000000000..8e2e6689d7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TextChunk.cpp
@@ -0,0 +1,28 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "Exceptions.h"
+
+#include "tree/pattern/TextChunk.h"
+
+using namespace antlr4::tree::pattern;
+
+TextChunk::TextChunk(const std::string &text) : text(text) {
+ if (text == "") {
+ throw IllegalArgumentException("text cannot be nul");
+ }
+
+}
+
+TextChunk::~TextChunk() {
+}
+
+std::string TextChunk::getText() {
+ return text;
+}
+
+std::string TextChunk::toString() {
+ return std::string("'") + text + std::string("'");
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TextChunk.h b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TextChunk.h
new file mode 100644
index 0000000000..bb7fc7f966
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TextChunk.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "Chunk.h"
+
+namespace antlr4 {
+namespace tree {
+namespace pattern {
+
+ /// <summary>
+ /// Represents a span of raw text (concrete syntax) between tags in a tree
+ /// pattern string.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC TextChunk : public Chunk {
+ private:
+ /// <summary>
+ /// This is the backing field for <seealso cref="#getText"/>.
+ /// </summary>
+ const std::string text;
+
+ /// <summary>
+ /// Constructs a new instance of <seealso cref="TextChunk"/> with the specified text.
+ /// </summary>
+ /// <param name="text"> The text of this chunk. </param>
+ /// <exception cref="IllegalArgumentException"> if {@code text} is {@code null}. </exception>
+ public:
+ TextChunk(const std::string &text);
+ virtual ~TextChunk();
+
+ /// <summary>
+ /// Gets the raw text of this chunk.
+ /// </summary>
+ /// <returns> The text of the chunk. </returns>
+ std::string getText();
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// <p/>
+ /// The implementation for <seealso cref="TextChunk"/> returns the result of
+ /// <seealso cref="#getText()"/> in single quotes.
+ /// </summary>
+ virtual std::string toString() override;
+ };
+
+} // namespace pattern
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TokenTagToken.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TokenTagToken.cpp
new file mode 100644
index 0000000000..f5153c8357
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TokenTagToken.cpp
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/pattern/TokenTagToken.h"
+
+using namespace antlr4::tree::pattern;
+
+TokenTagToken::TokenTagToken(const std::string &/*tokenName*/, int type)
+ : CommonToken(type), tokenName(""), label("") {
+}
+
+TokenTagToken::TokenTagToken(const std::string &tokenName, int type, const std::string &label)
+ : CommonToken(type), tokenName(tokenName), label(label) {
+}
+
+std::string TokenTagToken::getTokenName() const {
+ return tokenName;
+}
+
+std::string TokenTagToken::getLabel() const {
+ return label;
+}
+
+std::string TokenTagToken::getText() const {
+ if (!label.empty()) {
+ return "<" + label + ":" + tokenName + ">";
+ }
+
+ return "<" + tokenName + ">";
+}
+
+std::string TokenTagToken::toString() const {
+ return tokenName + ":" + std::to_string(_type);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TokenTagToken.h b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TokenTagToken.h
new file mode 100644
index 0000000000..da9e11cd36
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/pattern/TokenTagToken.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "CommonToken.h"
+
+namespace antlr4 {
+namespace tree {
+namespace pattern {
+
+ /// <summary>
+ /// A <seealso cref="Token"/> object representing a token of a particular type; e.g.,
+ /// {@code <ID>}. These tokens are created for <seealso cref="TagChunk"/> chunks where the
+ /// tag corresponds to a lexer rule or token type.
+ /// </summary>
+ class ANTLR4CPP_PUBLIC TokenTagToken : public CommonToken {
+ /// <summary>
+ /// This is the backing field for <seealso cref="#getTokenName"/>.
+ /// </summary>
+ private:
+ const std::string tokenName;
+ /// <summary>
+ /// This is the backing field for <seealso cref="#getLabe"/>.
+ /// </summary>
+ const std::string label;
+
+ /// <summary>
+ /// Constructs a new instance of <seealso cref="TokenTagToken"/> for an unlabeled tag
+ /// with the specified token name and type.
+ /// </summary>
+ /// <param name="tokenName"> The token name. </param>
+ /// <param name="type"> The token type. </param>
+ public:
+ TokenTagToken(const std::string &tokenName, int type); //this(tokenName, type, nullptr);
+
+ /// <summary>
+ /// Constructs a new instance of <seealso cref="TokenTagToken"/> with the specified
+ /// token name, type, and label.
+ /// </summary>
+ /// <param name="tokenName"> The token name. </param>
+ /// <param name="type"> The token type. </param>
+ /// <param name="label"> The label associated with the token tag, or {@code null} if
+ /// the token tag is unlabeled. </param>
+ TokenTagToken(const std::string &tokenName, int type, const std::string &label);
+
+ /// <summary>
+ /// Gets the token name. </summary>
+ /// <returns> The token name. </returns>
+ std::string getTokenName() const;
+
+ /// <summary>
+ /// Gets the label associated with the rule tag.
+ /// </summary>
+ /// <returns> The name of the label associated with the rule tag, or
+ /// {@code null} if this is an unlabeled rule tag. </returns>
+ std::string getLabel() const;
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// <p/>
+ /// The implementation for <seealso cref="TokenTagToken"/> returns the token tag
+ /// formatted with {@code <} and {@code >} delimiters.
+ /// </summary>
+ virtual std::string getText() const override;
+
+ /// <summary>
+ /// {@inheritDoc}
+ /// <p/>
+ /// The implementation for <seealso cref="TokenTagToken"/> returns a string of the form
+ /// {@code tokenName:type}.
+ /// </summary>
+ virtual std::string toString() const override;
+ };
+
+} // namespace pattern
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPath.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPath.cpp
new file mode 100644
index 0000000000..c0398962ec
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPath.cpp
@@ -0,0 +1,154 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "XPathLexer.h"
+#include "XPathLexerErrorListener.h"
+#include "XPathElement.h"
+#include "XPathWildcardAnywhereElement.h"
+#include "XPathWildcardElement.h"
+#include "XPathTokenAnywhereElement.h"
+#include "XPathTokenElement.h"
+#include "XPathRuleAnywhereElement.h"
+#include "XPathRuleElement.h"
+
+#include "XPath.h"
+
+using namespace antlr4;
+using namespace antlr4::tree;
+using namespace antlr4::tree::xpath;
+
+const std::string XPath::WILDCARD = "*";
+const std::string XPath::NOT = "!";
+
+XPath::XPath(Parser *parser, const std::string &path) {
+ _parser = parser;
+ _path = path;
+}
+
+std::vector<std::unique_ptr<XPathElement>> XPath::split(const std::string &path) {
+ ANTLRInputStream in(path);
+ XPathLexer lexer(&in);
+ lexer.removeErrorListeners();
+ XPathLexerErrorListener listener;
+ lexer.addErrorListener(&listener);
+ CommonTokenStream tokenStream(&lexer);
+ try {
+ tokenStream.fill();
+ } catch (LexerNoViableAltException &) {
+ size_t pos = lexer.getCharPositionInLine();
+ std::string msg = "Invalid tokens or characters at index " + std::to_string(pos) + " in path '" + path + "'";
+ throw IllegalArgumentException(msg);
+ }
+
+ std::vector<Token *> tokens = tokenStream.getTokens();
+ std::vector<std::unique_ptr<XPathElement>> elements;
+ size_t n = tokens.size();
+ size_t i = 0;
+ bool done = false;
+ while (!done && i < n) {
+ Token *el = tokens[i];
+ Token *next = nullptr;
+ switch (el->getType()) {
+ case XPathLexer::ROOT:
+ case XPathLexer::ANYWHERE: {
+ bool anywhere = el->getType() == XPathLexer::ANYWHERE;
+ i++;
+ next = tokens[i];
+ bool invert = next->getType() == XPathLexer::BANG;
+ if (invert) {
+ i++;
+ next = tokens[i];
+ }
+ std::unique_ptr<XPathElement> pathElement = getXPathElement(next, anywhere);
+ pathElement->setInvert(invert);
+ elements.push_back(std::move(pathElement));
+ i++;
+ break;
+
+ }
+ case XPathLexer::TOKEN_REF:
+ case XPathLexer::RULE_REF:
+ case XPathLexer::WILDCARD:
+ elements.push_back(getXPathElement(el, false));
+ i++;
+ break;
+
+ case Token::EOF:
+ done = true;
+ break;
+
+ default :
+ throw IllegalArgumentException("Unknown path element " + el->toString());
+ }
+ }
+
+ return elements;
+}
+
+std::unique_ptr<XPathElement> XPath::getXPathElement(Token *wordToken, bool anywhere) {
+ if (wordToken->getType() == Token::EOF) {
+ throw IllegalArgumentException("Missing path element at end of path");
+ }
+
+ std::string word = wordToken->getText();
+ size_t ttype = _parser->getTokenType(word);
+ ssize_t ruleIndex = _parser->getRuleIndex(word);
+ switch (wordToken->getType()) {
+ case XPathLexer::WILDCARD :
+ if (anywhere)
+ return std::unique_ptr<XPathWildcardAnywhereElement>(new XPathWildcardAnywhereElement());
+ return std::unique_ptr<XPathWildcardElement>(new XPathWildcardElement());
+
+ case XPathLexer::TOKEN_REF:
+ case XPathLexer::STRING :
+ if (ttype == Token::INVALID_TYPE) {
+ throw IllegalArgumentException(word + " at index " + std::to_string(wordToken->getStartIndex()) + " isn't a valid token name");
+ }
+ if (anywhere)
+ return std::unique_ptr<XPathTokenAnywhereElement>(new XPathTokenAnywhereElement(word, (int)ttype));
+ return std::unique_ptr<XPathTokenElement>(new XPathTokenElement(word, (int)ttype));
+
+ default :
+ if (ruleIndex == -1) {
+ throw IllegalArgumentException(word + " at index " + std::to_string(wordToken->getStartIndex()) + " isn't a valid rule name");
+ }
+ if (anywhere)
+ return std::unique_ptr<XPathRuleAnywhereElement>(new XPathRuleAnywhereElement(word, (int)ruleIndex));
+ return std::unique_ptr<XPathRuleElement>(new XPathRuleElement(word, (int)ruleIndex));
+ }
+}
+
+static ParserRuleContext dummyRoot;
+
+std::vector<ParseTree *> XPath::findAll(ParseTree *tree, std::string const& xpath, Parser *parser) {
+ XPath p(parser, xpath);
+ return p.evaluate(tree);
+}
+
+std::vector<ParseTree *> XPath::evaluate(ParseTree *t) {
+ dummyRoot.children = { t }; // don't set t's parent.
+
+ std::vector<ParseTree *> work = { &dummyRoot };
+
+ size_t i = 0;
+ std::vector<std::unique_ptr<XPathElement>> elements = split(_path);
+
+ while (i < elements.size()) {
+ std::vector<ParseTree *> next;
+ for (auto *node : work) {
+ if (!node->children.empty()) {
+ // only try to match next element if it has children
+ // e.g., //func/*/stat might have a token node for which
+ // we can't go looking for stat nodes.
+ auto matching = elements[i]->evaluate(node);
+ next.insert(next.end(), matching.begin(), matching.end());
+ }
+ }
+ i++;
+ work = next;
+ }
+
+ return work;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPath.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPath.h
new file mode 100644
index 0000000000..e38d482d58
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPath.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+namespace xpath {
+
+ /// Represent a subset of XPath XML path syntax for use in identifying nodes in
+ /// parse trees.
+ ///
+ /// <para>
+ /// Split path into words and separators {@code /} and {@code //} via ANTLR
+ /// itself then walk path elements from left to right. At each separator-word
+ /// pair, find set of nodes. Next stage uses those as work list.</para>
+ ///
+ /// <para>
+ /// The basic interface is
+ /// <seealso cref="XPath#findAll ParseTree.findAll"/>{@code (tree, pathString, parser)}.
+ /// But that is just shorthand for:</para>
+ ///
+ /// <pre>
+ /// <seealso cref="XPath"/> p = new <seealso cref="XPath#XPath XPath"/>(parser, pathString);
+ /// return p.<seealso cref="#evaluate evaluate"/>(tree);
+ /// </pre>
+ ///
+ /// <para>
+ /// See {@code org.antlr.v4.test.TestXPath} for descriptions. In short, this
+ /// allows operators:</para>
+ ///
+ /// <dl>
+ /// <dt>/</dt> <dd>root</dd>
+ /// <dt>//</dt> <dd>anywhere</dd>
+ /// <dt>!</dt> <dd>invert; this must appear directly after root or anywhere
+ /// operator</dd>
+ /// </dl>
+ ///
+ /// <para>
+ /// and path elements:</para>
+ ///
+ /// <dl>
+ /// <dt>ID</dt> <dd>token name</dd>
+ /// <dt>'string'</dt> <dd>any string literal token from the grammar</dd>
+ /// <dt>expr</dt> <dd>rule name</dd>
+ /// <dt>*</dt> <dd>wildcard matching any node</dd>
+ /// </dl>
+ ///
+ /// <para>
+ /// Whitespace is not allowed.</para>
+
+ class ANTLR4CPP_PUBLIC XPath {
+ public:
+ static const std::string WILDCARD; // word not operator/separator
+ static const std::string NOT; // word for invert operator
+
+ XPath(Parser *parser, const std::string &path);
+ virtual ~XPath() {}
+
+ // TODO: check for invalid token/rule names, bad syntax
+ virtual std::vector<std::unique_ptr<XPathElement>> split(const std::string &path);
+
+ static std::vector<ParseTree *> findAll(ParseTree *tree, std::string const& xpath, Parser *parser);
+
+ /// Return a list of all nodes starting at {@code t} as root that satisfy the
+ /// path. The root {@code /} is relative to the node passed to
+ /// <seealso cref="#evaluate"/>.
+ virtual std::vector<ParseTree *> evaluate(ParseTree *t);
+
+ protected:
+ std::string _path;
+ Parser *_parser;
+
+ /// Convert word like {@code *} or {@code ID} or {@code expr} to a path
+ /// element. {@code anywhere} is {@code true} if {@code //} precedes the
+ /// word.
+ virtual std::unique_ptr<XPathElement> getXPathElement(Token *wordToken, bool anywhere);
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathElement.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathElement.cpp
new file mode 100644
index 0000000000..64b122df13
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathElement.cpp
@@ -0,0 +1,31 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "support/CPPUtils.h"
+
+#include "XPathElement.h"
+
+using namespace antlr4::tree;
+using namespace antlr4::tree::xpath;
+
+XPathElement::XPathElement(const std::string &nodeName) {
+ _nodeName = nodeName;
+}
+
+XPathElement::~XPathElement() {
+}
+
+std::vector<ParseTree *> XPathElement::evaluate(ParseTree * /*t*/) {
+ return {};
+}
+
+std::string XPathElement::toString() const {
+ std::string inv = _invert ? "!" : "";
+ return antlrcpp::toString(*this) + "[" + inv + _nodeName + "]";
+}
+
+void XPathElement::setInvert(bool value) {
+ _invert = value;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathElement.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathElement.h
new file mode 100644
index 0000000000..f339117d7f
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathElement.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "antlr4-common.h"
+
+namespace antlr4 {
+namespace tree {
+ class ParseTree;
+
+namespace xpath {
+
+ class ANTLR4CPP_PUBLIC XPathElement {
+ public:
+ /// Construct element like {@code /ID} or {@code ID} or {@code /*} etc...
+ /// op is null if just node
+ XPathElement(const std::string &nodeName);
+ XPathElement(XPathElement const&) = default;
+ virtual ~XPathElement();
+
+ XPathElement& operator=(XPathElement const&) = default;
+
+ /// Given tree rooted at {@code t} return all nodes matched by this path
+ /// element.
+ virtual std::vector<ParseTree *> evaluate(ParseTree *t);
+ virtual std::string toString() const;
+
+ void setInvert(bool value);
+
+ protected:
+ std::string _nodeName;
+ bool _invert = false;
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexer.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexer.cpp
new file mode 100644
index 0000000000..506d2e1179
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexer.cpp
@@ -0,0 +1,182 @@
+
+// Generated from XPathLexer.g4 by ANTLR 4.9.3
+
+
+#include "XPathLexer.h"
+
+
+using namespace antlr4;
+
+namespace {
+
+struct XPathLexerStaticData final {
+ XPathLexerStaticData(std::vector<std::string> ruleNames,
+ std::vector<std::string> channelNames,
+ std::vector<std::string> modeNames,
+ std::vector<std::string> literalNames,
+ std::vector<std::string> symbolicNames)
+ : ruleNames(std::move(ruleNames)), channelNames(std::move(channelNames)),
+ modeNames(std::move(modeNames)), literalNames(std::move(literalNames)),
+ symbolicNames(std::move(symbolicNames)),
+ vocabulary(this->literalNames, this->symbolicNames) {}
+
+ XPathLexerStaticData(const XPathLexerStaticData&) = delete;
+ XPathLexerStaticData(XPathLexerStaticData&&) = delete;
+ XPathLexerStaticData& operator=(const XPathLexerStaticData&) = delete;
+ XPathLexerStaticData& operator=(XPathLexerStaticData&&) = delete;
+
+ std::vector<antlr4::dfa::DFA> decisionToDFA;
+ antlr4::atn::PredictionContextCache sharedContextCache;
+ const std::vector<std::string> ruleNames;
+ const std::vector<std::string> channelNames;
+ const std::vector<std::string> modeNames;
+ const std::vector<std::string> literalNames;
+ const std::vector<std::string> symbolicNames;
+ const antlr4::dfa::Vocabulary vocabulary;
+ antlr4::atn::SerializedATNView serializedATN;
+ std::unique_ptr<antlr4::atn::ATN> atn;
+};
+
+::antlr4::internal::OnceFlag xpathLexerOnceFlag;
+XPathLexerStaticData *xpathLexerStaticData = nullptr;
+
+void xpathLexerInitialize() {
+ assert(xpathLexerStaticData == nullptr);
+ auto staticData = std::make_unique<XPathLexerStaticData>(
+ std::vector<std::string>{
+ "ANYWHERE", "ROOT", "WILDCARD", "BANG", "ID", "NameChar", "NameStartChar",
+ "STRING"
+ },
+ std::vector<std::string>{
+ "DEFAULT_TOKEN_CHANNEL", "HIDDEN"
+ },
+ std::vector<std::string>{
+ "DEFAULT_MODE"
+ },
+ std::vector<std::string>{
+ "", "", "", "'//'", "'/'", "'*'", "'!'"
+ },
+ std::vector<std::string>{
+ "", "TOKEN_REF", "RULE_REF", "ANYWHERE", "ROOT", "WILDCARD", "BANG", "ID",
+ "STRING"
+ }
+ );
+ static const int32_t serializedATNSegment[] = {
+ 0x4, 0x0, 0x8, 0x32, 0x6, -1, 0x2, 0x0, 0x7, 0x0, 0x2, 0x1, 0x7,
+ 0x1, 0x2, 0x2, 0x7, 0x2, 0x2, 0x3, 0x7, 0x3, 0x2, 0x4, 0x7, 0x4,
+ 0x2, 0x5, 0x7, 0x5, 0x2, 0x6, 0x7, 0x6, 0x2, 0x7, 0x7, 0x7, 0x1,
+ 0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x1, 0x2,
+ 0x1, 0x3, 0x1, 0x3, 0x1, 0x4, 0x1, 0x4, 0x5, 0x4, 0x1d, 0x8, 0x4,
+ 0xa, 0x4, 0xc, 0x4, 0x20, 0x9, 0x4, 0x1, 0x4, 0x1, 0x4, 0x1, 0x5,
+ 0x1, 0x5, 0x3, 0x5, 0x26, 0x8, 0x5, 0x1, 0x6, 0x1, 0x6, 0x1, 0x7,
+ 0x1, 0x7, 0x5, 0x7, 0x2c, 0x8, 0x7, 0xa, 0x7, 0xc, 0x7, 0x2f, 0x9,
+ 0x7, 0x1, 0x7, 0x1, 0x7, 0x1, 0x2d, 0x0, 0x8, 0x1, 0x3, 0x3, 0x4,
+ 0x5, 0x5, 0x7, 0x6, 0x9, 0x7, 0xb, 0x0, 0xd, 0x0, 0xf, 0x8, 0x1,
+ 0x0, 0x2, 0x5, 0x0, 0x30, 0x39, 0x5f, 0x5f, 0xb7, 0xb7, 0x300, 0x36f,
+ 0x203f, 0x2040, 0xd, 0x0, 0x41, 0x5a, 0x61, 0x7a, 0xc0, 0xd6, 0xd8,
+ 0xf6, 0xf8, 0x2ff, 0x370, 0x37d, 0x37f, 0x1fff, 0x200c, 0x200d, 0x2070,
+ 0x218f, 0x2c00, 0x2fef, 0x3001, 0xd7ff, 0xf900, 0xfdcf, 0xfdf0, -1,
+ 0x0, 0x32, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1, 0x0, 0x0,
+ 0x0, 0x0, 0x5, 0x1, 0x0, 0x0, 0x0, 0x0, 0x7, 0x1, 0x0, 0x0, 0x0,
+ 0x0, 0x9, 0x1, 0x0, 0x0, 0x0, 0x0, 0xf, 0x1, 0x0, 0x0, 0x0, 0x1,
+ 0x11, 0x1, 0x0, 0x0, 0x0, 0x3, 0x14, 0x1, 0x0, 0x0, 0x0, 0x5, 0x16,
+ 0x1, 0x0, 0x0, 0x0, 0x7, 0x18, 0x1, 0x0, 0x0, 0x0, 0x9, 0x1a, 0x1,
+ 0x0, 0x0, 0x0, 0xb, 0x25, 0x1, 0x0, 0x0, 0x0, 0xd, 0x27, 0x1, 0x0,
+ 0x0, 0x0, 0xf, 0x29, 0x1, 0x0, 0x0, 0x0, 0x11, 0x12, 0x5, 0x2f, 0x0,
+ 0x0, 0x12, 0x13, 0x5, 0x2f, 0x0, 0x0, 0x13, 0x2, 0x1, 0x0, 0x0, 0x0,
+ 0x14, 0x15, 0x5, 0x2f, 0x0, 0x0, 0x15, 0x4, 0x1, 0x0, 0x0, 0x0, 0x16,
+ 0x17, 0x5, 0x2a, 0x0, 0x0, 0x17, 0x6, 0x1, 0x0, 0x0, 0x0, 0x18, 0x19,
+ 0x5, 0x21, 0x0, 0x0, 0x19, 0x8, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x1e, 0x3,
+ 0xd, 0x6, 0x0, 0x1b, 0x1d, 0x3, 0xb, 0x5, 0x0, 0x1c, 0x1b, 0x1, 0x0,
+ 0x0, 0x0, 0x1d, 0x20, 0x1, 0x0, 0x0, 0x0, 0x1e, 0x1c, 0x1, 0x0, 0x0,
+ 0x0, 0x1e, 0x1f, 0x1, 0x0, 0x0, 0x0, 0x1f, 0x21, 0x1, 0x0, 0x0, 0x0,
+ 0x20, 0x1e, 0x1, 0x0, 0x0, 0x0, 0x21, 0x22, 0x6, 0x4, 0x0, 0x0, 0x22,
+ 0xa, 0x1, 0x0, 0x0, 0x0, 0x23, 0x26, 0x3, 0xd, 0x6, 0x0, 0x24, 0x26,
+ 0x7, 0x0, 0x0, 0x0, 0x25, 0x23, 0x1, 0x0, 0x0, 0x0, 0x25, 0x24, 0x1,
+ 0x0, 0x0, 0x0, 0x26, 0xc, 0x1, 0x0, 0x0, 0x0, 0x27, 0x28, 0x7, 0x1,
+ 0x0, 0x0, 0x28, 0xe, 0x1, 0x0, 0x0, 0x0, 0x29, 0x2d, 0x5, 0x27, 0x0,
+ 0x0, 0x2a, 0x2c, 0x9, 0x0, 0x0, 0x0, 0x2b, 0x2a, 0x1, 0x0, 0x0, 0x0,
+ 0x2c, 0x2f, 0x1, 0x0, 0x0, 0x0, 0x2d, 0x2e, 0x1, 0x0, 0x0, 0x0, 0x2d,
+ 0x2b, 0x1, 0x0, 0x0, 0x0, 0x2e, 0x30, 0x1, 0x0, 0x0, 0x0, 0x2f, 0x2d,
+ 0x1, 0x0, 0x0, 0x0, 0x30, 0x31, 0x5, 0x27, 0x0, 0x0, 0x31, 0x10,
+ 0x1, 0x0, 0x0, 0x0, 0x4, 0x0, 0x1e, 0x25, 0x2d, 0x1, 0x1, 0x4, 0x0,
+ };
+
+ staticData->serializedATN = antlr4::atn::SerializedATNView(serializedATNSegment, sizeof(serializedATNSegment) / sizeof(serializedATNSegment[0]));
+
+ atn::ATNDeserializer deserializer;
+ staticData->atn = deserializer.deserialize(staticData->serializedATN);
+
+ size_t count = staticData->atn->getNumberOfDecisions();
+ staticData->decisionToDFA.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ staticData->decisionToDFA.emplace_back(staticData->atn->getDecisionState(i), i);
+ }
+ xpathLexerStaticData = staticData.release();
+}
+
+}
+
+XPathLexer::XPathLexer(CharStream *input) : Lexer(input) {
+ XPathLexer::initialize();
+ _interpreter = new atn::LexerATNSimulator(this, *xpathLexerStaticData->atn, xpathLexerStaticData->decisionToDFA, xpathLexerStaticData->sharedContextCache);
+}
+
+XPathLexer::~XPathLexer() {
+ delete _interpreter;
+}
+
+std::string XPathLexer::getGrammarFileName() const {
+ return "XPathLexer.g4";
+}
+
+const std::vector<std::string>& XPathLexer::getRuleNames() const {
+ return xpathLexerStaticData->ruleNames;
+}
+
+const std::vector<std::string>& XPathLexer::getChannelNames() const {
+ return xpathLexerStaticData->channelNames;
+}
+
+const std::vector<std::string>& XPathLexer::getModeNames() const {
+ return xpathLexerStaticData->modeNames;
+}
+
+const dfa::Vocabulary& XPathLexer::getVocabulary() const {
+ return xpathLexerStaticData->vocabulary;
+}
+
+antlr4::atn::SerializedATNView XPathLexer::getSerializedATN() const {
+ return xpathLexerStaticData->serializedATN;
+}
+
+const atn::ATN& XPathLexer::getATN() const {
+ return *xpathLexerStaticData->atn;
+}
+
+void XPathLexer::action(RuleContext *context, size_t ruleIndex, size_t actionIndex) {
+ switch (ruleIndex) {
+ case 4: IDAction(antlrcpp::downCast<antlr4::RuleContext *>(context), actionIndex); break;
+
+ default:
+ break;
+ }
+}
+
+void XPathLexer::IDAction(antlr4::RuleContext *context, size_t actionIndex) {
+ switch (actionIndex) {
+ case 0:
+ if (isupper(getText()[0]))
+ setType(TOKEN_REF);
+ else
+ setType(RULE_REF);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void XPathLexer::initialize() {
+ ::antlr4::internal::call_once(xpathLexerOnceFlag, xpathLexerInitialize);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexer.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexer.h
new file mode 100644
index 0000000000..6926d2161e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexer.h
@@ -0,0 +1,47 @@
+
+// Generated from XPathLexer.g4 by ANTLR 4.9.3
+
+#pragma once
+
+
+#include "antlr4-runtime.h"
+
+
+class XPathLexer : public antlr4::Lexer {
+public:
+ enum {
+ TOKEN_REF = 1, RULE_REF = 2, ANYWHERE = 3, ROOT = 4, WILDCARD = 5, BANG = 6,
+ ID = 7, STRING = 8
+ };
+
+ explicit XPathLexer(antlr4::CharStream *input);
+
+ ~XPathLexer() override;
+
+ virtual std::string getGrammarFileName() const override;
+
+ virtual const std::vector<std::string>& getRuleNames() const override;
+
+ virtual const std::vector<std::string>& getChannelNames() const override;
+
+ virtual const std::vector<std::string>& getModeNames() const override;
+
+ virtual const antlr4::dfa::Vocabulary& getVocabulary() const override;
+
+ virtual antlr4::atn::SerializedATNView getSerializedATN() const override;
+
+ virtual const antlr4::atn::ATN& getATN() const override;
+
+ virtual void action(antlr4::RuleContext *context, size_t ruleIndex, size_t actionIndex) override;
+
+ // By default the static state used to implement the lexer is lazily initialized during the first
+ // call to the constructor. You can call this function if you wish to initialize the static state
+ // ahead of time.
+ static void initialize();
+private:
+ // Individual action functions triggered by action() above.
+ void IDAction(antlr4::RuleContext *context, size_t actionIndex);
+
+ // Individual semantic predicate functions triggered by sempred() above.
+};
+
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexerErrorListener.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexerErrorListener.cpp
new file mode 100644
index 0000000000..2804c8ee3d
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexerErrorListener.cpp
@@ -0,0 +1,13 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "XPathLexerErrorListener.h"
+
+using namespace antlr4;
+using namespace antlr4::tree::xpath;
+
+void XPathLexerErrorListener::syntaxError(Recognizer * /*recognizer*/, Token * /*offendingSymbol*/,
+ size_t /*line*/, size_t /*charPositionInLine*/, const std::string &/*msg*/, std::exception_ptr /*e*/) {
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexerErrorListener.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexerErrorListener.h
new file mode 100644
index 0000000000..c0c3eaaca7
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathLexerErrorListener.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "BaseErrorListener.h"
+
+namespace antlr4 {
+namespace tree {
+namespace xpath {
+
+ class ANTLR4CPP_PUBLIC XPathLexerErrorListener : public BaseErrorListener {
+ public:
+ virtual void syntaxError(Recognizer *recognizer, Token *offendingSymbol, size_t line,
+ size_t charPositionInLine, const std::string &msg, std::exception_ptr e) override;
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleAnywhereElement.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleAnywhereElement.cpp
new file mode 100644
index 0000000000..9ca910df2e
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleAnywhereElement.cpp
@@ -0,0 +1,20 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ParseTree.h"
+#include "tree/Trees.h"
+
+#include "tree/xpath/XPathRuleAnywhereElement.h"
+
+using namespace antlr4::tree;
+using namespace antlr4::tree::xpath;
+
+XPathRuleAnywhereElement::XPathRuleAnywhereElement(const std::string &ruleName, int ruleIndex) : XPathElement(ruleName) {
+ _ruleIndex = ruleIndex;
+}
+
+std::vector<ParseTree *> XPathRuleAnywhereElement::evaluate(ParseTree *t) {
+ return Trees::findAllRuleNodes(t, _ruleIndex);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleAnywhereElement.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleAnywhereElement.h
new file mode 100644
index 0000000000..2ceb75ceed
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleAnywhereElement.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "XPathElement.h"
+
+namespace antlr4 {
+namespace tree {
+namespace xpath {
+
+ /// Either {@code ID} at start of path or {@code ...//ID} in middle of path.
+ class ANTLR4CPP_PUBLIC XPathRuleAnywhereElement : public XPathElement {
+ public:
+ XPathRuleAnywhereElement(const std::string &ruleName, int ruleIndex);
+
+ virtual std::vector<ParseTree *> evaluate(ParseTree *t) override;
+
+ protected:
+ int _ruleIndex = 0;
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleElement.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleElement.cpp
new file mode 100644
index 0000000000..1d145fb575
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleElement.cpp
@@ -0,0 +1,30 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ParseTree.h"
+#include "tree/Trees.h"
+
+#include "XPathRuleElement.h"
+
+using namespace antlr4::tree;
+using namespace antlr4::tree::xpath;
+
+XPathRuleElement::XPathRuleElement(const std::string &ruleName, size_t ruleIndex) : XPathElement(ruleName) {
+ _ruleIndex = ruleIndex;
+}
+
+std::vector<ParseTree *> XPathRuleElement::evaluate(ParseTree *t) {
+ // return all children of t that match nodeName
+ std::vector<ParseTree *> nodes;
+ for (auto *c : t->children) {
+ if (antlrcpp::is<ParserRuleContext *>(c)) {
+ ParserRuleContext *ctx = dynamic_cast<ParserRuleContext *>(c);
+ if ((ctx->getRuleIndex() == _ruleIndex && !_invert) || (ctx->getRuleIndex() != _ruleIndex && _invert)) {
+ nodes.push_back(ctx);
+ }
+ }
+ }
+ return nodes;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleElement.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleElement.h
new file mode 100644
index 0000000000..b57276f033
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathRuleElement.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "XPathElement.h"
+
+namespace antlr4 {
+namespace tree {
+namespace xpath {
+
+ class ANTLR4CPP_PUBLIC XPathRuleElement : public XPathElement {
+ public:
+ XPathRuleElement(const std::string &ruleName, size_t ruleIndex);
+
+ virtual std::vector<ParseTree *> evaluate(ParseTree *t) override;
+
+ protected:
+ size_t _ruleIndex = 0;
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenAnywhereElement.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenAnywhereElement.cpp
new file mode 100644
index 0000000000..c557c9d675
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenAnywhereElement.cpp
@@ -0,0 +1,20 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ParseTree.h"
+#include "tree/Trees.h"
+
+#include "XPathTokenAnywhereElement.h"
+
+using namespace antlr4::tree;
+using namespace antlr4::tree::xpath;
+
+XPathTokenAnywhereElement::XPathTokenAnywhereElement(const std::string &tokenName, int tokenType) : XPathElement(tokenName) {
+ this->tokenType = tokenType;
+}
+
+std::vector<ParseTree *> XPathTokenAnywhereElement::evaluate(ParseTree *t) {
+ return Trees::findAllTokenNodes(t, tokenType);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenAnywhereElement.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenAnywhereElement.h
new file mode 100644
index 0000000000..2045d91b32
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenAnywhereElement.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "XPathElement.h"
+
+namespace antlr4 {
+namespace tree {
+namespace xpath {
+
+ class ANTLR4CPP_PUBLIC XPathTokenAnywhereElement : public XPathElement {
+ protected:
+ int tokenType = 0;
+ public:
+ XPathTokenAnywhereElement(const std::string &tokenName, int tokenType);
+
+ virtual std::vector<ParseTree *> evaluate(ParseTree *t) override;
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenElement.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenElement.cpp
new file mode 100644
index 0000000000..d52fc26afd
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenElement.cpp
@@ -0,0 +1,33 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "tree/ParseTree.h"
+#include "tree/Trees.h"
+#include "support/CPPUtils.h"
+#include "Token.h"
+
+#include "XPathTokenElement.h"
+
+using namespace antlr4;
+using namespace antlr4::tree;
+using namespace antlr4::tree::xpath;
+
+XPathTokenElement::XPathTokenElement(const std::string &tokenName, size_t tokenType) : XPathElement(tokenName) {
+ _tokenType = tokenType;
+}
+
+std::vector<ParseTree *> XPathTokenElement::evaluate(ParseTree *t) {
+ // return all children of t that match nodeName
+ std::vector<ParseTree *> nodes;
+ for (auto *c : t->children) {
+ if (antlrcpp::is<TerminalNode *>(c)) {
+ TerminalNode *tnode = dynamic_cast<TerminalNode *>(c);
+ if ((tnode->getSymbol()->getType() == _tokenType && !_invert) || (tnode->getSymbol()->getType() != _tokenType && _invert)) {
+ nodes.push_back(tnode);
+ }
+ }
+ }
+ return nodes;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenElement.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenElement.h
new file mode 100644
index 0000000000..7221530ce6
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathTokenElement.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "XPathElement.h"
+
+namespace antlr4 {
+namespace tree {
+namespace xpath {
+
+ class ANTLR4CPP_PUBLIC XPathTokenElement : public XPathElement {
+ public:
+ XPathTokenElement(const std::string &tokenName, size_t tokenType);
+
+ virtual std::vector<ParseTree *> evaluate(ParseTree *t) override;
+
+ protected:
+ size_t _tokenType = 0;
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardAnywhereElement.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardAnywhereElement.cpp
new file mode 100644
index 0000000000..4ff424f056
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardAnywhereElement.cpp
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "XPath.h"
+#include "tree/ParseTree.h"
+#include "tree/Trees.h"
+
+#include "XPathWildcardAnywhereElement.h"
+
+using namespace antlr4::tree;
+using namespace antlr4::tree::xpath;
+
+XPathWildcardAnywhereElement::XPathWildcardAnywhereElement() : XPathElement(XPath::WILDCARD) {
+}
+
+std::vector<ParseTree *> XPathWildcardAnywhereElement::evaluate(ParseTree *t) {
+ if (_invert) {
+ return {}; // !* is weird but valid (empty)
+ }
+ return Trees::getDescendants(t);
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardAnywhereElement.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardAnywhereElement.h
new file mode 100644
index 0000000000..dc5d1e5a29
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardAnywhereElement.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "XPathElement.h"
+
+namespace antlr4 {
+namespace tree {
+namespace xpath {
+
+ class ANTLR4CPP_PUBLIC XPathWildcardAnywhereElement : public XPathElement {
+ public:
+ XPathWildcardAnywhereElement();
+
+ virtual std::vector<ParseTree *> evaluate(ParseTree *t) override;
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardElement.cpp b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardElement.cpp
new file mode 100644
index 0000000000..aabda5a9be
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardElement.cpp
@@ -0,0 +1,24 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#include "XPath.h"
+#include "tree/ParseTree.h"
+#include "tree/Trees.h"
+
+#include "XPathWildcardElement.h"
+
+using namespace antlr4::tree;
+using namespace antlr4::tree::xpath;
+
+XPathWildcardElement::XPathWildcardElement() : XPathElement(XPath::WILDCARD) {
+}
+
+std::vector<ParseTree *> XPathWildcardElement::evaluate(ParseTree *t) {
+ if (_invert) {
+ return {}; // !* is weird but valid (empty)
+ }
+
+ return t->children;
+}
diff --git a/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardElement.h b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardElement.h
new file mode 100644
index 0000000000..accb461de2
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/src/tree/xpath/XPathWildcardElement.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+ * Use of this file is governed by the BSD 3-clause license that
+ * can be found in the LICENSE.txt file in the project root.
+ */
+
+#pragma once
+
+#include "XPathElement.h"
+
+namespace antlr4 {
+namespace tree {
+namespace xpath {
+
+ class ANTLR4CPP_PUBLIC XPathWildcardElement : public XPathElement {
+ public:
+ XPathWildcardElement();
+
+ virtual std::vector<ParseTree *> evaluate(ParseTree *t) override;
+ };
+
+} // namespace xpath
+} // namespace tree
+} // namespace antlr4
diff --git a/contrib/libs/antlr4_cpp_runtime/ya.make b/contrib/libs/antlr4_cpp_runtime/ya.make
new file mode 100644
index 0000000000..6a0db5139b
--- /dev/null
+++ b/contrib/libs/antlr4_cpp_runtime/ya.make
@@ -0,0 +1,164 @@
+# Generated by devtools/yamaker from nixpkgs 22.05.
+
+LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(4.11.1)
+
+ORIGINAL_SOURCE(https://github.com/antlr/antlr4/archive/4.11.1.tar.gz)
+
+ADDINCL(
+ GLOBAL contrib/libs/antlr4_cpp_runtime/src
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+CFLAGS(
+ GLOBAL -DANTLR4CPP_STATIC
+)
+
+SRCS(
+ src/ANTLRErrorListener.cpp
+ src/ANTLRErrorStrategy.cpp
+ src/ANTLRFileStream.cpp
+ src/ANTLRInputStream.cpp
+ src/BailErrorStrategy.cpp
+ src/BaseErrorListener.cpp
+ src/BufferedTokenStream.cpp
+ src/CharStream.cpp
+ src/CommonToken.cpp
+ src/CommonTokenFactory.cpp
+ src/CommonTokenStream.cpp
+ src/ConsoleErrorListener.cpp
+ src/DefaultErrorStrategy.cpp
+ src/DiagnosticErrorListener.cpp
+ src/Exceptions.cpp
+ src/FailedPredicateException.cpp
+ src/InputMismatchException.cpp
+ src/IntStream.cpp
+ src/InterpreterRuleContext.cpp
+ src/Lexer.cpp
+ src/LexerInterpreter.cpp
+ src/LexerNoViableAltException.cpp
+ src/ListTokenSource.cpp
+ src/NoViableAltException.cpp
+ src/Parser.cpp
+ src/ParserInterpreter.cpp
+ src/ParserRuleContext.cpp
+ src/ProxyErrorListener.cpp
+ src/RecognitionException.cpp
+ src/Recognizer.cpp
+ src/RuleContext.cpp
+ src/RuleContextWithAltNum.cpp
+ src/RuntimeMetaData.cpp
+ src/Token.cpp
+ src/TokenSource.cpp
+ src/TokenStream.cpp
+ src/TokenStreamRewriter.cpp
+ src/UnbufferedCharStream.cpp
+ src/UnbufferedTokenStream.cpp
+ src/Vocabulary.cpp
+ src/WritableToken.cpp
+ src/atn/ATN.cpp
+ src/atn/ATNConfig.cpp
+ src/atn/ATNConfigSet.cpp
+ src/atn/ATNDeserializationOptions.cpp
+ src/atn/ATNDeserializer.cpp
+ src/atn/ATNSimulator.cpp
+ src/atn/ATNState.cpp
+ src/atn/ATNStateType.cpp
+ src/atn/ActionTransition.cpp
+ src/atn/AmbiguityInfo.cpp
+ src/atn/ArrayPredictionContext.cpp
+ src/atn/AtomTransition.cpp
+ src/atn/ContextSensitivityInfo.cpp
+ src/atn/DecisionEventInfo.cpp
+ src/atn/DecisionInfo.cpp
+ src/atn/DecisionState.cpp
+ src/atn/EpsilonTransition.cpp
+ src/atn/ErrorInfo.cpp
+ src/atn/LL1Analyzer.cpp
+ src/atn/LexerATNConfig.cpp
+ src/atn/LexerATNSimulator.cpp
+ src/atn/LexerAction.cpp
+ src/atn/LexerActionExecutor.cpp
+ src/atn/LexerChannelAction.cpp
+ src/atn/LexerCustomAction.cpp
+ src/atn/LexerIndexedCustomAction.cpp
+ src/atn/LexerModeAction.cpp
+ src/atn/LexerMoreAction.cpp
+ src/atn/LexerPopModeAction.cpp
+ src/atn/LexerPushModeAction.cpp
+ src/atn/LexerSkipAction.cpp
+ src/atn/LexerTypeAction.cpp
+ src/atn/LookaheadEventInfo.cpp
+ src/atn/NotSetTransition.cpp
+ src/atn/OrderedATNConfigSet.cpp
+ src/atn/ParseInfo.cpp
+ src/atn/ParserATNSimulator.cpp
+ src/atn/PrecedencePredicateTransition.cpp
+ src/atn/PredicateEvalInfo.cpp
+ src/atn/PredicateTransition.cpp
+ src/atn/PredictionContext.cpp
+ src/atn/PredictionContextCache.cpp
+ src/atn/PredictionContextMergeCache.cpp
+ src/atn/PredictionMode.cpp
+ src/atn/ProfilingATNSimulator.cpp
+ src/atn/RangeTransition.cpp
+ src/atn/RuleTransition.cpp
+ src/atn/SemanticContext.cpp
+ src/atn/SetTransition.cpp
+ src/atn/SingletonPredictionContext.cpp
+ src/atn/StarLoopbackState.cpp
+ src/atn/Transition.cpp
+ src/atn/TransitionType.cpp
+ src/atn/WildcardTransition.cpp
+ src/dfa/DFA.cpp
+ src/dfa/DFASerializer.cpp
+ src/dfa/DFAState.cpp
+ src/dfa/LexerDFASerializer.cpp
+ src/internal/Synchronization.cpp
+ src/misc/InterpreterDataReader.cpp
+ src/misc/Interval.cpp
+ src/misc/IntervalSet.cpp
+ src/misc/MurmurHash.cpp
+ src/misc/Predicate.cpp
+ src/support/Any.cpp
+ src/support/Arrays.cpp
+ src/support/CPPUtils.cpp
+ src/support/StringUtils.cpp
+ src/support/Utf8.cpp
+ src/tree/ErrorNodeImpl.cpp
+ src/tree/IterativeParseTreeWalker.cpp
+ src/tree/ParseTree.cpp
+ src/tree/ParseTreeListener.cpp
+ src/tree/ParseTreeVisitor.cpp
+ src/tree/ParseTreeWalker.cpp
+ src/tree/TerminalNodeImpl.cpp
+ src/tree/Trees.cpp
+ src/tree/pattern/Chunk.cpp
+ src/tree/pattern/ParseTreeMatch.cpp
+ src/tree/pattern/ParseTreePattern.cpp
+ src/tree/pattern/ParseTreePatternMatcher.cpp
+ src/tree/pattern/RuleTagToken.cpp
+ src/tree/pattern/TagChunk.cpp
+ src/tree/pattern/TextChunk.cpp
+ src/tree/pattern/TokenTagToken.cpp
+ src/tree/xpath/XPath.cpp
+ src/tree/xpath/XPathElement.cpp
+ src/tree/xpath/XPathLexer.cpp
+ src/tree/xpath/XPathLexerErrorListener.cpp
+ src/tree/xpath/XPathRuleAnywhereElement.cpp
+ src/tree/xpath/XPathRuleElement.cpp
+ src/tree/xpath/XPathTokenAnywhereElement.cpp
+ src/tree/xpath/XPathTokenElement.cpp
+ src/tree/xpath/XPathWildcardAnywhereElement.cpp
+ src/tree/xpath/XPathWildcardElement.cpp
+)
+
+END()
diff --git a/contrib/libs/blake2/COPYING b/contrib/libs/blake2/COPYING
new file mode 100644
index 0000000000..2c4afabdb6
--- /dev/null
+++ b/contrib/libs/blake2/COPYING
@@ -0,0 +1,117 @@
+CC0 1.0 Universal
+
+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.
+
+For more information, please see
+<http://creativecommons.org/publicdomain/zero/1.0/>
+
diff --git a/contrib/libs/blake2/README.md b/contrib/libs/blake2/README.md
new file mode 100644
index 0000000000..17faa8f4d5
--- /dev/null
+++ b/contrib/libs/blake2/README.md
@@ -0,0 +1,14 @@
+# libb2
+
+C library providing BLAKE2b, BLAKE2s, BLAKE2bp, BLAKE2sp
+
+Installation:
+
+```
+$ ./autogen.sh
+$ ./configure
+$ make
+$ sudo make install
+```
+
+Contact: contact@blake2.net
diff --git a/contrib/libs/blake2/include/blake2.h b/contrib/libs/blake2/include/blake2.h
new file mode 100644
index 0000000000..731ebdc993
--- /dev/null
+++ b/contrib/libs/blake2/include/blake2.h
@@ -0,0 +1 @@
+#include "../src/blake2.h" /* inclink generated by yamaker */
diff --git a/contrib/libs/blake2/src/blake2-config.h b/contrib/libs/blake2/src/blake2-config.h
new file mode 100644
index 0000000000..c09cb4bcf0
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2-config.h
@@ -0,0 +1,71 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2_CONFIG_H__
+#define __BLAKE2_CONFIG_H__
+
+#if defined(__SSE2__)
+#define HAVE_SSE2
+#endif
+
+#if defined(__SSSE3__)
+#define HAVE_SSSE3
+#endif
+
+#if defined(__SSE4_1__)
+#define HAVE_SSE4_1
+#endif
+
+#if defined(__AVX__)
+#define HAVE_AVX
+#endif
+
+#if defined(__XOP__)
+#define HAVE_XOP
+#endif
+
+
+#ifdef HAVE_AVX2
+#ifndef HAVE_AVX
+#define HAVE_AVX
+#endif
+#endif
+
+#ifdef HAVE_XOP
+#ifndef HAVE_AVX
+#define HAVE_AVX
+#endif
+#endif
+
+#ifdef HAVE_AVX
+#ifndef HAVE_SSE4_1
+#define HAVE_SSE4_1
+#endif
+#endif
+
+#ifdef HAVE_SSE4_1
+#ifndef HAVE_SSSE3
+#define HAVE_SSSE3
+#endif
+#endif
+
+#ifdef HAVE_SSSE3
+#define HAVE_SSE2
+#endif
+
+#if !defined(HAVE_SSE2)
+#error "This code requires at least SSE2."
+#endif
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2-dispatch.c b/contrib/libs/blake2/src/blake2-dispatch.c
new file mode 100644
index 0000000000..96bb3408bb
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2-dispatch.c
@@ -0,0 +1,577 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#include <stdio.h>
+#if defined(WIN32)
+#include <windows.h>
+#endif
+#include "blake2.h"
+
+#if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64)
+#define HAVE_X86
+#endif
+
+typedef enum
+{
+ NONE = 0,
+#if defined(HAVE_X86)
+ SSE2 = 1,
+ SSSE3 = 2,
+ SSE41 = 3,
+ AVX = 4,
+ XOP = 5,
+ /* AVX2 = 6, */
+#endif
+} cpu_feature_t;
+
+static const char feature_names[][8] =
+{
+ "none",
+#if defined(HAVE_X86)
+ "sse2",
+ "ssse3",
+ "sse41",
+ "avx",
+ "xop",
+ /* "avx2" */
+#endif
+};
+
+#if defined(HAVE_X86)
+
+#if defined(__GNUC__)
+static inline void cpuid( uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx )
+{
+ __asm__ __volatile__(
+#if defined(__i386__) /* This is needed for -fPIC to work on i386 */
+ "movl %%ebx, %%esi\n\t"
+#endif
+ "cpuid\n\t"
+#if defined(__i386__)
+ "xchgl %%ebx, %%esi\n\t"
+ : "=a"( *eax ), "=S"( *ebx ), "=c"( *ecx ), "=d"( *edx ) : "a"( *eax ) );
+#else
+ : "=a"( *eax ), "=b"( *ebx ), "=c"( *ecx ), "=d"( *edx ) : "a"( *eax ) );
+#endif
+}
+
+static inline uint64_t xgetbv(uint32_t xcr)
+{
+ uint32_t a, d;
+ __asm__ __volatile__(
+ "xgetbv"
+ : "=a"(a),"=d"(d)
+ : "c"(xcr)
+ );
+ return ((uint64_t)d << 32) | a;
+}
+
+#elif defined(_MSC_VER)
+#include <intrin.h>
+static inline void cpuid( uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx )
+{
+ int regs[4];
+ __cpuid( regs, *eax );
+ *eax = regs[0];
+ *ebx = regs[1];
+ *ecx = regs[2];
+ *edx = regs[3];
+}
+#else
+#error "Don't know how to call cpuid on this compiler!"
+#endif
+
+#endif /* HAVE_X86 */
+
+static inline cpu_feature_t get_cpu_features( void )
+{
+#if defined(HAVE_X86)
+ static volatile int initialized = 0;
+ static cpu_feature_t feature = NONE; // Safe default
+ uint32_t eax, ecx, edx, ebx;
+
+ if( initialized )
+ return feature;
+
+ eax = 1;
+ cpuid( &eax, &ebx, &ecx, &edx );
+
+ if( 1 & ( edx >> 26 ) )
+ feature = SSE2;
+
+ if( 1 & ( ecx >> 9 ) )
+ feature = SSSE3;
+
+ if( 1 & ( ecx >> 19 ) )
+ feature = SSE41;
+
+#if defined(WIN32) /* Work around the fact that Windows <7 does NOT support AVX... */
+ if( IsProcessorFeaturePresent(17) ) /* Some environments don't know about PF_XSAVE_ENABLED */
+#endif
+ {
+ /* check for AVX and OSXSAVE bits */
+ if( 1 & ( ecx >> 28 ) & (ecx >> 27) ) {
+#if !defined(WIN32) /* Already checked for this in WIN32 */
+ if( (xgetbv(0) & 6) == 6 ) /* XCR0 */
+#endif
+ feature = AVX;
+ }
+
+
+ eax = 0x80000001;
+ cpuid( &eax, &ebx, &ecx, &edx );
+
+ if( 1 & ( ecx >> 11 ) )
+ feature = XOP;
+ }
+
+ /* For future architectures */
+ /*
+ eax = 7; ecx = 0;
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ if(1&(ebx >> 5))
+ feature = AVX2;
+ */
+ /* fprintf( stderr, "Using %s engine\n", feature_names[feature] ); */
+ initialized = 1;
+ return feature;
+#else
+ return NONE;
+#endif
+}
+
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2b_init_ref( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_ref( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_ref( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_ref( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_ref( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_ref( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+#if defined(HAVE_X86)
+
+ int blake2b_init_sse2( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_sse2( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_sse2( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_sse2( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_sse2( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_sse2( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2b_init_ssse3( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_ssse3( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_ssse3( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_ssse3( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_ssse3( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_ssse3( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2b_init_sse41( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_sse41( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_sse41( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_sse41( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_sse41( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_sse41( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2b_init_avx( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_avx( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_avx( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_avx( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_avx( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_avx( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2b_init_xop( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_xop( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_xop( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_xop( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_xop( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_xop( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+#endif /* HAVE_X86 */
+
+ int blake2s_init_ref( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_ref( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_ref( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_ref( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_ref( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_ref( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+#if defined(HAVE_X86)
+
+ int blake2s_init_sse2( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_sse2( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_sse2( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_sse2( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_sse2( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_sse2( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_ssse3( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_ssse3( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_ssse3( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_ssse3( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_ssse3( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_ssse3( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_sse41( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_sse41( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_sse41( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_sse41( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_sse41( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_sse41( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_avx( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_avx( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_avx( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_avx( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_avx( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_avx( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_xop( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_xop( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_xop( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_xop( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_xop( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_xop( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+#endif /* HAVE_X86 */
+
+#if defined(__cplusplus)
+}
+#endif
+
+typedef int ( *blake2b_init_fn )( blake2b_state *, size_t );
+typedef int ( *blake2b_init_key_fn )( blake2b_state *, size_t, const void *, size_t );
+typedef int ( *blake2b_init_param_fn )( blake2b_state *, const blake2b_param * );
+typedef int ( *blake2b_update_fn )( blake2b_state *, const uint8_t *, size_t );
+typedef int ( *blake2b_final_fn )( blake2b_state *, uint8_t *, size_t );
+typedef int ( *blake2b_fn )( uint8_t *, const void *, const void *, size_t, size_t, size_t );
+
+typedef int ( *blake2s_init_fn )( blake2s_state *, size_t );
+typedef int ( *blake2s_init_key_fn )( blake2s_state *, size_t, const void *, size_t );
+typedef int ( *blake2s_init_param_fn )( blake2s_state *, const blake2s_param * );
+typedef int ( *blake2s_update_fn )( blake2s_state *, const uint8_t *, size_t );
+typedef int ( *blake2s_final_fn )( blake2s_state *, uint8_t *, size_t );
+typedef int ( *blake2s_fn )( uint8_t *, const void *, const void *, size_t, size_t, size_t );
+
+static const blake2b_init_fn blake2b_init_table[] =
+{
+ blake2b_init_ref,
+#if defined(HAVE_X86)
+ blake2b_init_sse2,
+ blake2b_init_ssse3,
+ blake2b_init_sse41,
+ blake2b_init_avx,
+ blake2b_init_xop
+#endif
+};
+
+static const blake2b_init_key_fn blake2b_init_key_table[] =
+{
+ blake2b_init_key_ref,
+#if defined(HAVE_X86)
+ blake2b_init_key_sse2,
+ blake2b_init_key_ssse3,
+ blake2b_init_key_sse41,
+ blake2b_init_key_avx,
+ blake2b_init_key_xop
+#endif
+};
+
+static const blake2b_init_param_fn blake2b_init_param_table[] =
+{
+ blake2b_init_param_ref,
+#if defined(HAVE_X86)
+ blake2b_init_param_sse2,
+ blake2b_init_param_ssse3,
+ blake2b_init_param_sse41,
+ blake2b_init_param_avx,
+ blake2b_init_param_xop
+#endif
+};
+
+static const blake2b_update_fn blake2b_update_table[] =
+{
+ blake2b_update_ref,
+#if defined(HAVE_X86)
+ blake2b_update_sse2,
+ blake2b_update_ssse3,
+ blake2b_update_sse41,
+ blake2b_update_avx,
+ blake2b_update_xop
+#endif
+};
+
+static const blake2b_final_fn blake2b_final_table[] =
+{
+ blake2b_final_ref,
+#if defined(HAVE_X86)
+ blake2b_final_sse2,
+ blake2b_final_ssse3,
+ blake2b_final_sse41,
+ blake2b_final_avx,
+ blake2b_final_xop
+#endif
+};
+
+static const blake2b_fn blake2b_table[] =
+{
+ blake2b_ref,
+#if defined(HAVE_X86)
+ blake2b_sse2,
+ blake2b_ssse3,
+ blake2b_sse41,
+ blake2b_avx,
+ blake2b_xop
+#endif
+};
+
+static const blake2s_init_fn blake2s_init_table[] =
+{
+ blake2s_init_ref,
+#if defined(HAVE_X86)
+ blake2s_init_sse2,
+ blake2s_init_ssse3,
+ blake2s_init_sse41,
+ blake2s_init_avx,
+ blake2s_init_xop
+#endif
+};
+
+static const blake2s_init_key_fn blake2s_init_key_table[] =
+{
+ blake2s_init_key_ref,
+#if defined(HAVE_X86)
+ blake2s_init_key_sse2,
+ blake2s_init_key_ssse3,
+ blake2s_init_key_sse41,
+ blake2s_init_key_avx,
+ blake2s_init_key_xop
+#endif
+};
+
+static const blake2s_init_param_fn blake2s_init_param_table[] =
+{
+ blake2s_init_param_ref,
+#if defined(HAVE_X86)
+ blake2s_init_param_sse2,
+ blake2s_init_param_ssse3,
+ blake2s_init_param_sse41,
+ blake2s_init_param_avx,
+ blake2s_init_param_xop
+#endif
+};
+
+static const blake2s_update_fn blake2s_update_table[] =
+{
+ blake2s_update_ref,
+#if defined(HAVE_X86)
+ blake2s_update_sse2,
+ blake2s_update_ssse3,
+ blake2s_update_sse41,
+ blake2s_update_avx,
+ blake2s_update_xop
+#endif
+};
+
+static const blake2s_final_fn blake2s_final_table[] =
+{
+ blake2s_final_ref,
+#if defined(HAVE_X86)
+ blake2s_final_sse2,
+ blake2s_final_ssse3,
+ blake2s_final_sse41,
+ blake2s_final_avx,
+ blake2s_final_xop
+#endif
+};
+
+static const blake2s_fn blake2s_table[] =
+{
+ blake2s_ref,
+#if defined(HAVE_X86)
+ blake2s_sse2,
+ blake2s_ssse3,
+ blake2s_sse41,
+ blake2s_avx,
+ blake2s_xop
+#endif
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2b_init_dispatch( blake2b_state *S, size_t outlen );
+ int blake2b_init_key_dispatch( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param_dispatch( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update_dispatch( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final_dispatch( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b_dispatch( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ int blake2s_init_dispatch( blake2s_state *S, size_t outlen );
+ int blake2s_init_key_dispatch( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param_dispatch( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update_dispatch( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final_dispatch( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s_dispatch( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+static blake2b_init_fn blake2b_init_ptr = blake2b_init_dispatch;
+static blake2b_init_key_fn blake2b_init_key_ptr = blake2b_init_key_dispatch;
+static blake2b_init_param_fn blake2b_init_param_ptr = blake2b_init_param_dispatch;
+static blake2b_update_fn blake2b_update_ptr = blake2b_update_dispatch;
+static blake2b_final_fn blake2b_final_ptr = blake2b_final_dispatch;
+static blake2b_fn blake2b_ptr = blake2b_dispatch;
+
+static blake2s_init_fn blake2s_init_ptr = blake2s_init_dispatch;
+static blake2s_init_key_fn blake2s_init_key_ptr = blake2s_init_key_dispatch;
+static blake2s_init_param_fn blake2s_init_param_ptr = blake2s_init_param_dispatch;
+static blake2s_update_fn blake2s_update_ptr = blake2s_update_dispatch;
+static blake2s_final_fn blake2s_final_ptr = blake2s_final_dispatch;
+static blake2s_fn blake2s_ptr = blake2s_dispatch;
+
+int blake2b_init_dispatch( blake2b_state *S, size_t outlen )
+{
+ blake2b_init_ptr = blake2b_init_table[get_cpu_features()];
+ return blake2b_init_ptr( S, outlen );
+}
+
+int blake2b_init_key_dispatch( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ blake2b_init_key_ptr = blake2b_init_key_table[get_cpu_features()];
+ return blake2b_init_key_ptr( S, outlen, key, keylen );
+}
+
+int blake2b_init_param_dispatch( blake2b_state *S, const blake2b_param *P )
+{
+ blake2b_init_param_ptr = blake2b_init_param_table[get_cpu_features()];
+ return blake2b_init_param_ptr( S, P );
+}
+
+int blake2b_update_dispatch( blake2b_state *S, const uint8_t *in, size_t inlen )
+{
+ blake2b_update_ptr = blake2b_update_table[get_cpu_features()];
+ return blake2b_update_ptr( S, in, inlen );
+}
+
+int blake2b_final_dispatch( blake2b_state *S, uint8_t *out, size_t outlen )
+{
+ blake2b_final_ptr = blake2b_final_table[get_cpu_features()];
+ return blake2b_final_ptr( S, out, outlen );
+}
+
+int blake2b_dispatch( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2b_ptr = blake2b_table[get_cpu_features()];
+ return blake2b_ptr( out, in, key, outlen, inlen, keylen );
+}
+
+BLAKE2_API int blake2b_init( blake2b_state *S, size_t outlen )
+{
+ return blake2b_init_ptr( S, outlen );
+}
+
+BLAKE2_API int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ return blake2b_init_key_ptr( S, outlen, key, keylen );
+}
+
+BLAKE2_API int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+{
+ return blake2b_init_param_ptr( S, P );
+}
+
+BLAKE2_API int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen )
+{
+ return blake2b_update_ptr( S, in, inlen );
+}
+
+BLAKE2_API int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen )
+{
+ return blake2b_final_ptr( S, out, outlen );
+}
+
+BLAKE2_API int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ return blake2b_ptr( out, in, key, outlen, inlen, keylen );
+}
+
+int blake2s_init_dispatch( blake2s_state *S, size_t outlen )
+{
+ blake2s_init_ptr = blake2s_init_table[get_cpu_features()];
+ return blake2s_init_ptr( S, outlen );
+}
+
+int blake2s_init_key_dispatch( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ blake2s_init_key_ptr = blake2s_init_key_table[get_cpu_features()];
+ return blake2s_init_key_ptr( S, outlen, key, keylen );
+}
+
+int blake2s_init_param_dispatch( blake2s_state *S, const blake2s_param *P )
+{
+ blake2s_init_param_ptr = blake2s_init_param_table[get_cpu_features()];
+ return blake2s_init_param_ptr( S, P );
+}
+
+int blake2s_update_dispatch( blake2s_state *S, const uint8_t *in, size_t inlen )
+{
+ blake2s_update_ptr = blake2s_update_table[get_cpu_features()];
+ return blake2s_update_ptr( S, in, inlen );
+}
+
+int blake2s_final_dispatch( blake2s_state *S, uint8_t *out, size_t outlen )
+{
+ blake2s_final_ptr = blake2s_final_table[get_cpu_features()];
+ return blake2s_final_ptr( S, out, outlen );
+}
+
+int blake2s_dispatch( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2s_ptr = blake2s_table[get_cpu_features()];
+ return blake2s_ptr( out, in, key, outlen, inlen, keylen );
+}
+
+BLAKE2_API int blake2s_init( blake2s_state *S, size_t outlen )
+{
+ return blake2s_init_ptr( S, outlen );
+}
+
+BLAKE2_API int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ return blake2s_init_key_ptr( S, outlen, key, keylen );
+}
+
+BLAKE2_API int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+ return blake2s_init_param_ptr( S, P );
+}
+
+BLAKE2_API int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen )
+{
+ return blake2s_update_ptr( S, in, inlen );
+}
+
+BLAKE2_API int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen )
+{
+ return blake2s_final_ptr( S, out, outlen );
+}
+
+BLAKE2_API int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ return blake2s_ptr( out, in, key, outlen, inlen, keylen );
+}
+
diff --git a/contrib/libs/blake2/src/blake2-impl.h b/contrib/libs/blake2/src/blake2-impl.h
new file mode 100644
index 0000000000..801f1ae7b7
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2-impl.h
@@ -0,0 +1,160 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2_IMPL_H__
+#define __BLAKE2_IMPL_H__
+
+#if defined(_WIN32) || defined(WIN32)
+#include <windows.h>
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include "config.h"
+
+#define BLAKE2_IMPL_CAT(x,y) x ## y
+#define BLAKE2_IMPL_EVAL(x,y) BLAKE2_IMPL_CAT(x,y)
+#define BLAKE2_IMPL_NAME(fun) BLAKE2_IMPL_EVAL(fun, SUFFIX)
+
+static inline uint32_t load32( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint32_t w;
+ memcpy( &w, src, sizeof( w ) );
+ return w;
+#else
+ const uint8_t *p = ( uint8_t * )src;
+ uint32_t w = *p++;
+ w |= ( uint32_t )( *p++ ) << 8;
+ w |= ( uint32_t )( *p++ ) << 16;
+ w |= ( uint32_t )( *p++ ) << 24;
+ return w;
+#endif
+}
+
+static inline uint64_t load64( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint64_t w;
+ memcpy( &w, src, sizeof( w ) );
+ return w;
+#else
+ const uint8_t *p = ( uint8_t * )src;
+ uint64_t w = *p++;
+ w |= ( uint64_t )( *p++ ) << 8;
+ w |= ( uint64_t )( *p++ ) << 16;
+ w |= ( uint64_t )( *p++ ) << 24;
+ w |= ( uint64_t )( *p++ ) << 32;
+ w |= ( uint64_t )( *p++ ) << 40;
+ w |= ( uint64_t )( *p++ ) << 48;
+ w |= ( uint64_t )( *p++ ) << 56;
+ return w;
+#endif
+}
+
+static inline void store32( void *dst, uint32_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy( dst, &w, sizeof( w ) );
+#else
+ uint8_t *p = ( uint8_t * )dst;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w;
+#endif
+}
+
+static inline void store64( void *dst, uint64_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy( dst, &w, sizeof( w ) );
+#else
+ uint8_t *p = ( uint8_t * )dst;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w;
+#endif
+}
+
+static inline uint64_t load48( const void *src )
+{
+ const uint8_t *p = ( const uint8_t * )src;
+ uint64_t w = *p++;
+ w |= ( uint64_t )( *p++ ) << 8;
+ w |= ( uint64_t )( *p++ ) << 16;
+ w |= ( uint64_t )( *p++ ) << 24;
+ w |= ( uint64_t )( *p++ ) << 32;
+ w |= ( uint64_t )( *p++ ) << 40;
+ return w;
+}
+
+static inline void store48( void *dst, uint64_t w )
+{
+ uint8_t *p = ( uint8_t * )dst;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w; w >>= 8;
+ *p++ = ( uint8_t )w;
+}
+
+static inline uint32_t rotl32( const uint32_t w, const unsigned c )
+{
+ return ( w << c ) | ( w >> ( 32 - c ) );
+}
+
+static inline uint64_t rotl64( const uint64_t w, const unsigned c )
+{
+ return ( w << c ) | ( w >> ( 64 - c ) );
+}
+
+static inline uint32_t rotr32( const uint32_t w, const unsigned c )
+{
+ return ( w >> c ) | ( w << ( 32 - c ) );
+}
+
+static inline uint64_t rotr64( const uint64_t w, const unsigned c )
+{
+ return ( w >> c ) | ( w << ( 64 - c ) );
+}
+
+/* prevents compiler optimizing out memset() */
+static inline void secure_zero_memory(void *v, size_t n)
+{
+#if defined(_WIN32) || defined(WIN32)
+ SecureZeroMemory(v, n);
+#else
+// prioritize first the general C11 call
+#if defined(HAVE_MEMSET_S)
+ memset_s(v, n, 0, n);
+#elif defined(HAVE_EXPLICIT_BZERO)
+ explicit_bzero(v, n);
+#elif defined(HAVE_EXPLICIT_MEMSET)
+ explicit_memset(v, 0, n);
+#else
+ memset(v, 0, n);
+ __asm__ __volatile__("" :: "r"(v) : "memory");
+#endif
+#endif
+}
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2.h b/contrib/libs/blake2/src/blake2.h
new file mode 100644
index 0000000000..5ca17f6107
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2.h
@@ -0,0 +1,182 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2_H__
+#define __BLAKE2_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+ #define BLAKE2_DLL_IMPORT __declspec(dllimport)
+ #define BLAKE2_DLL_EXPORT __declspec(dllexport)
+ #define BLAKE2_DLL_PRIVATE
+#elif __GNUC__ >= 4
+ #define BLAKE2_DLL_IMPORT __attribute__ ((visibility ("default")))
+ #define BLAKE2_DLL_EXPORT __attribute__ ((visibility ("default")))
+ #define BLAKE2_DLL_PRIVATE __attribute__ ((visibility ("hidden")))
+#else
+ #define BLAKE2_DLL_IMPORT
+ #define BLAKE2_DLL_EXPORT
+ #define BLAKE2_DLL_PRIVATE
+#endif
+
+#if defined(BLAKE2_DLL)
+ #if defined(BLAKE2_DLL_EXPORTS) // defined if we are building the DLL
+ #define BLAKE2_API BLAKE2_DLL_EXPORT
+ #else
+ #define BLAKE2_API BLAKE2_DLL_IMPORT
+ #endif
+ #define BLAKE2_PRIVATE BLAKE2_DLL_PRIVATE // must only be used by hidden logic
+#else
+ #define BLAKE2_API
+ #define BLAKE2_PRIVATE
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#elif defined(_MSC_VER) && !defined(inline)
+#define inline __inline
+#endif
+
+ enum blake2s_constant
+ {
+ BLAKE2S_BLOCKBYTES = 64,
+ BLAKE2S_OUTBYTES = 32,
+ BLAKE2S_KEYBYTES = 32,
+ BLAKE2S_SALTBYTES = 8,
+ BLAKE2S_PERSONALBYTES = 8
+ };
+
+ enum blake2b_constant
+ {
+ BLAKE2B_BLOCKBYTES = 128,
+ BLAKE2B_OUTBYTES = 64,
+ BLAKE2B_KEYBYTES = 64,
+ BLAKE2B_SALTBYTES = 16,
+ BLAKE2B_PERSONALBYTES = 16
+ };
+
+#pragma pack(push, 1)
+ typedef struct __blake2s_param
+ {
+ uint8_t digest_length; // 1
+ uint8_t key_length; // 2
+ uint8_t fanout; // 3
+ uint8_t depth; // 4
+ uint32_t leaf_length; // 8
+ uint8_t node_offset[6];// 14
+ uint8_t node_depth; // 15
+ uint8_t inner_length; // 16
+ // uint8_t reserved[0];
+ uint8_t salt[BLAKE2S_SALTBYTES]; // 24
+ uint8_t personal[BLAKE2S_PERSONALBYTES]; // 32
+ } blake2s_param;
+
+ typedef struct __blake2s_state
+ {
+ uint32_t h[8];
+ uint32_t t[2];
+ uint32_t f[2];
+ uint8_t buf[2 * BLAKE2S_BLOCKBYTES];
+ uint32_t buflen;
+ uint8_t outlen;
+ uint8_t last_node;
+ } blake2s_state;
+
+ typedef struct __blake2b_param
+ {
+ uint8_t digest_length; // 1
+ uint8_t key_length; // 2
+ uint8_t fanout; // 3
+ uint8_t depth; // 4
+ uint32_t leaf_length; // 8
+ uint64_t node_offset; // 16
+ uint8_t node_depth; // 17
+ uint8_t inner_length; // 18
+ uint8_t reserved[14]; // 32
+ uint8_t salt[BLAKE2B_SALTBYTES]; // 48
+ uint8_t personal[BLAKE2B_PERSONALBYTES]; // 64
+ } blake2b_param;
+
+ typedef struct __blake2b_state
+ {
+ uint64_t h[8];
+ uint64_t t[2];
+ uint64_t f[2];
+ uint8_t buf[2 * BLAKE2B_BLOCKBYTES];
+ uint32_t buflen;
+ uint8_t outlen;
+ uint8_t last_node;
+ } blake2b_state;
+
+ typedef struct __blake2sp_state
+ {
+ blake2s_state S[8][1];
+ blake2s_state R[1];
+ uint8_t buf[8 * BLAKE2S_BLOCKBYTES];
+ uint32_t buflen;
+ uint8_t outlen;
+ } blake2sp_state;
+
+ typedef struct __blake2bp_state
+ {
+ blake2b_state S[4][1];
+ blake2b_state R[1];
+ uint8_t buf[4 * BLAKE2B_BLOCKBYTES];
+ uint32_t buflen;
+ uint8_t outlen;
+ } blake2bp_state;
+#pragma pack(pop)
+
+ // Streaming API
+ BLAKE2_API int blake2s_init( blake2s_state *S, size_t outlen );
+ BLAKE2_API int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ BLAKE2_API int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+ BLAKE2_API int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen );
+ BLAKE2_API int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen );
+
+ BLAKE2_API int blake2b_init( blake2b_state *S, size_t outlen );
+ BLAKE2_API int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ BLAKE2_API int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+ BLAKE2_API int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen );
+ BLAKE2_API int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen );
+
+ BLAKE2_API int blake2sp_init( blake2sp_state *S, size_t outlen );
+ BLAKE2_API int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
+ BLAKE2_API int blake2sp_update( blake2sp_state *S, const uint8_t *in, size_t inlen );
+ BLAKE2_API int blake2sp_final( blake2sp_state *S, uint8_t *out, size_t outlen );
+
+ BLAKE2_API int blake2bp_init( blake2bp_state *S, size_t outlen );
+ BLAKE2_API int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
+ BLAKE2_API int blake2bp_update( blake2bp_state *S, const uint8_t *in, size_t inlen );
+ BLAKE2_API int blake2bp_final( blake2bp_state *S, uint8_t *out, size_t outlen );
+
+ // Simple API
+ BLAKE2_API int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+ BLAKE2_API int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ BLAKE2_API int blake2sp( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+ BLAKE2_API int blake2bp( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+
+ static inline int blake2( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+ {
+ return blake2b( out, in, key, outlen, inlen, keylen );
+ }
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2b-load-sse2.h b/contrib/libs/blake2/src/blake2b-load-sse2.h
new file mode 100644
index 0000000000..1ba153c87d
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b-load-sse2.h
@@ -0,0 +1,68 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_LOAD_SSE2_H__
+#define __BLAKE2B_LOAD_SSE2_H__
+
+#define LOAD_MSG_0_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4)
+#define LOAD_MSG_0_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5)
+#define LOAD_MSG_0_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_0_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13)
+#define LOAD_MSG_1_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9)
+#define LOAD_MSG_1_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15)
+#define LOAD_MSG_1_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11)
+#define LOAD_MSG_1_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7)
+#define LOAD_MSG_2_1(b0, b1) b0 = _mm_set_epi64x(m12, m11); b1 = _mm_set_epi64x(m15, m5)
+#define LOAD_MSG_2_2(b0, b1) b0 = _mm_set_epi64x(m0, m8); b1 = _mm_set_epi64x(m13, m2)
+#define LOAD_MSG_2_3(b0, b1) b0 = _mm_set_epi64x(m3, m10); b1 = _mm_set_epi64x(m9, m7)
+#define LOAD_MSG_2_4(b0, b1) b0 = _mm_set_epi64x(m6, m14); b1 = _mm_set_epi64x(m4, m1)
+#define LOAD_MSG_3_1(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m13)
+#define LOAD_MSG_3_2(b0, b1) b0 = _mm_set_epi64x(m1, m9); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_3_3(b0, b1) b0 = _mm_set_epi64x(m5, m2); b1 = _mm_set_epi64x(m15, m4)
+#define LOAD_MSG_3_4(b0, b1) b0 = _mm_set_epi64x(m10, m6); b1 = _mm_set_epi64x(m8, m0)
+#define LOAD_MSG_4_1(b0, b1) b0 = _mm_set_epi64x(m5, m9); b1 = _mm_set_epi64x(m10, m2)
+#define LOAD_MSG_4_2(b0, b1) b0 = _mm_set_epi64x(m7, m0); b1 = _mm_set_epi64x(m15, m4)
+#define LOAD_MSG_4_3(b0, b1) b0 = _mm_set_epi64x(m11, m14); b1 = _mm_set_epi64x(m3, m6)
+#define LOAD_MSG_4_4(b0, b1) b0 = _mm_set_epi64x(m12, m1); b1 = _mm_set_epi64x(m13, m8)
+#define LOAD_MSG_5_1(b0, b1) b0 = _mm_set_epi64x(m6, m2); b1 = _mm_set_epi64x(m8, m0)
+#define LOAD_MSG_5_2(b0, b1) b0 = _mm_set_epi64x(m10, m12); b1 = _mm_set_epi64x(m3, m11)
+#define LOAD_MSG_5_3(b0, b1) b0 = _mm_set_epi64x(m7, m4); b1 = _mm_set_epi64x(m1, m15)
+#define LOAD_MSG_5_4(b0, b1) b0 = _mm_set_epi64x(m5, m13); b1 = _mm_set_epi64x(m9, m14)
+#define LOAD_MSG_6_1(b0, b1) b0 = _mm_set_epi64x(m1, m12); b1 = _mm_set_epi64x(m4, m14)
+#define LOAD_MSG_6_2(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m10, m13)
+#define LOAD_MSG_6_3(b0, b1) b0 = _mm_set_epi64x(m6, m0); b1 = _mm_set_epi64x(m8, m9)
+#define LOAD_MSG_6_4(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m2)
+#define LOAD_MSG_7_1(b0, b1) b0 = _mm_set_epi64x(m7, m13); b1 = _mm_set_epi64x(m3, m12)
+#define LOAD_MSG_7_2(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m9, m1)
+#define LOAD_MSG_7_3(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m2, m8)
+#define LOAD_MSG_7_4(b0, b1) b0 = _mm_set_epi64x(m4, m0); b1 = _mm_set_epi64x(m10, m6)
+#define LOAD_MSG_8_1(b0, b1) b0 = _mm_set_epi64x(m14, m6); b1 = _mm_set_epi64x(m0, m11)
+#define LOAD_MSG_8_2(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m8, m3)
+#define LOAD_MSG_8_3(b0, b1) b0 = _mm_set_epi64x(m13, m12); b1 = _mm_set_epi64x(m10, m1)
+#define LOAD_MSG_8_4(b0, b1) b0 = _mm_set_epi64x(m7, m2); b1 = _mm_set_epi64x(m5, m4)
+#define LOAD_MSG_9_1(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m1, m7)
+#define LOAD_MSG_9_2(b0, b1) b0 = _mm_set_epi64x(m4, m2); b1 = _mm_set_epi64x(m5, m6)
+#define LOAD_MSG_9_3(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m13, m3)
+#define LOAD_MSG_9_4(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m0, m12)
+#define LOAD_MSG_10_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4)
+#define LOAD_MSG_10_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5)
+#define LOAD_MSG_10_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12)
+#define LOAD_MSG_10_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13)
+#define LOAD_MSG_11_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9)
+#define LOAD_MSG_11_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15)
+#define LOAD_MSG_11_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11)
+#define LOAD_MSG_11_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7)
+
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2b-load-sse41.h b/contrib/libs/blake2/src/blake2b-load-sse41.h
new file mode 100644
index 0000000000..f6c1bc8393
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b-load-sse41.h
@@ -0,0 +1,402 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_LOAD_SSE41_H__
+#define __BLAKE2B_LOAD_SSE41_H__
+
+#define LOAD_MSG_0_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m1); \
+b1 = _mm_unpacklo_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_0_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m0, m1); \
+b1 = _mm_unpackhi_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_0_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m5); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_0_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m5); \
+b1 = _mm_unpackhi_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_1_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m2); \
+b1 = _mm_unpackhi_epi64(m4, m6); \
+} while(0)
+
+
+#define LOAD_MSG_1_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_alignr_epi8(m3, m7, 8); \
+} while(0)
+
+
+#define LOAD_MSG_1_3(b0, b1) \
+do \
+{ \
+b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \
+b1 = _mm_unpackhi_epi64(m5, m2); \
+} while(0)
+
+
+#define LOAD_MSG_1_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m1); \
+b1 = _mm_unpackhi_epi64(m3, m1); \
+} while(0)
+
+
+#define LOAD_MSG_2_1(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m6, m5, 8); \
+b1 = _mm_unpackhi_epi64(m2, m7); \
+} while(0)
+
+
+#define LOAD_MSG_2_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m0); \
+b1 = _mm_blend_epi16(m1, m6, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_2_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m5, m1, 0xF0); \
+b1 = _mm_unpackhi_epi64(m3, m4); \
+} while(0)
+
+
+#define LOAD_MSG_2_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m3); \
+b1 = _mm_alignr_epi8(m2, m0, 8); \
+} while(0)
+
+
+#define LOAD_MSG_3_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m3, m1); \
+b1 = _mm_unpackhi_epi64(m6, m5); \
+} while(0)
+
+
+#define LOAD_MSG_3_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m0); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_3_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m1, m2, 0xF0); \
+b1 = _mm_blend_epi16(m2, m7, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_3_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m3, m5); \
+b1 = _mm_unpacklo_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_4_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m2); \
+b1 = _mm_unpacklo_epi64(m1, m5); \
+} while(0)
+
+
+#define LOAD_MSG_4_2(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m0, m3, 0xF0); \
+b1 = _mm_blend_epi16(m2, m7, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_4_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m7, m5, 0xF0); \
+b1 = _mm_blend_epi16(m3, m1, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_4_4(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m6, m0, 8); \
+b1 = _mm_blend_epi16(m4, m6, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_5_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m1, m3); \
+b1 = _mm_unpacklo_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_5_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m5); \
+b1 = _mm_unpackhi_epi64(m5, m1); \
+} while(0)
+
+
+#define LOAD_MSG_5_3(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m2, m3, 0xF0); \
+b1 = _mm_unpackhi_epi64(m7, m0); \
+} while(0)
+
+
+#define LOAD_MSG_5_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m6, m2); \
+b1 = _mm_blend_epi16(m7, m4, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_6_1(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m6, m0, 0xF0); \
+b1 = _mm_unpacklo_epi64(m7, m2); \
+} while(0)
+
+
+#define LOAD_MSG_6_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m2, m7); \
+b1 = _mm_alignr_epi8(m5, m6, 8); \
+} while(0)
+
+
+#define LOAD_MSG_6_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m3); \
+b1 = _mm_shuffle_epi32(m4, _MM_SHUFFLE(1,0,3,2)); \
+} while(0)
+
+
+#define LOAD_MSG_6_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m3, m1); \
+b1 = _mm_blend_epi16(m1, m5, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_7_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m6, m3); \
+b1 = _mm_blend_epi16(m6, m1, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_7_2(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m7, m5, 8); \
+b1 = _mm_unpackhi_epi64(m0, m4); \
+} while(0)
+
+
+#define LOAD_MSG_7_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m2, m7); \
+b1 = _mm_unpacklo_epi64(m4, m1); \
+} while(0)
+
+
+#define LOAD_MSG_7_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m2); \
+b1 = _mm_unpacklo_epi64(m3, m5); \
+} while(0)
+
+
+#define LOAD_MSG_8_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m3, m7); \
+b1 = _mm_alignr_epi8(m0, m5, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m7, m4); \
+b1 = _mm_alignr_epi8(m4, m1, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_3(b0, b1) \
+do \
+{ \
+b0 = m6; \
+b1 = _mm_alignr_epi8(m5, m0, 8); \
+} while(0)
+
+
+#define LOAD_MSG_8_4(b0, b1) \
+do \
+{ \
+b0 = _mm_blend_epi16(m1, m3, 0xF0); \
+b1 = m2; \
+} while(0)
+
+
+#define LOAD_MSG_9_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_unpackhi_epi64(m3, m0); \
+} while(0)
+
+
+#define LOAD_MSG_9_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m1, m2); \
+b1 = _mm_blend_epi16(m3, m2, 0xF0); \
+} while(0)
+
+
+#define LOAD_MSG_9_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m7, m4); \
+b1 = _mm_unpackhi_epi64(m1, m6); \
+} while(0)
+
+
+#define LOAD_MSG_9_4(b0, b1) \
+do \
+{ \
+b0 = _mm_alignr_epi8(m7, m5, 8); \
+b1 = _mm_unpacklo_epi64(m6, m0); \
+} while(0)
+
+
+#define LOAD_MSG_10_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m0, m1); \
+b1 = _mm_unpacklo_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_10_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m0, m1); \
+b1 = _mm_unpackhi_epi64(m2, m3); \
+} while(0)
+
+
+#define LOAD_MSG_10_3(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m4, m5); \
+b1 = _mm_unpacklo_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_10_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpackhi_epi64(m4, m5); \
+b1 = _mm_unpackhi_epi64(m6, m7); \
+} while(0)
+
+
+#define LOAD_MSG_11_1(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m7, m2); \
+b1 = _mm_unpackhi_epi64(m4, m6); \
+} while(0)
+
+
+#define LOAD_MSG_11_2(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m5, m4); \
+b1 = _mm_alignr_epi8(m3, m7, 8); \
+} while(0)
+
+
+#define LOAD_MSG_11_3(b0, b1) \
+do \
+{ \
+b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \
+b1 = _mm_unpackhi_epi64(m5, m2); \
+} while(0)
+
+
+#define LOAD_MSG_11_4(b0, b1) \
+do \
+{ \
+b0 = _mm_unpacklo_epi64(m6, m1); \
+b1 = _mm_unpackhi_epi64(m3, m1); \
+} while(0)
+
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2b-ref.c b/contrib/libs/blake2/src/blake2b-ref.c
new file mode 100644
index 0000000000..4d40764d0e
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b-ref.c
@@ -0,0 +1,386 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+static const uint64_t blake2b_IV[8] =
+{
+ 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+};
+
+
+static inline int blake2b_set_lastnode( blake2b_state *S )
+{
+ S->f[1] = ~0ULL;
+ return 0;
+}
+
+static inline int blake2b_clear_lastnode( blake2b_state *S )
+{
+ S->f[1] = 0ULL;
+ return 0;
+}
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2b_set_lastblock( blake2b_state *S )
+{
+ if( S->last_node ) blake2b_set_lastnode( S );
+
+ S->f[0] = ~0ULL;
+ return 0;
+}
+
+static inline int blake2b_clear_lastblock( blake2b_state *S )
+{
+ if( S->last_node ) blake2b_clear_lastnode( S );
+
+ S->f[0] = 0ULL;
+ return 0;
+}
+
+static inline int blake2b_increment_counter( blake2b_state *S, const uint64_t inc )
+{
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+ return 0;
+}
+
+
+
+// Parameter-related functions
+static inline int blake2b_param_set_digest_length( blake2b_param *P, const uint8_t digest_length )
+{
+ P->digest_length = digest_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_fanout( blake2b_param *P, const uint8_t fanout )
+{
+ P->fanout = fanout;
+ return 0;
+}
+
+static inline int blake2b_param_set_max_depth( blake2b_param *P, const uint8_t depth )
+{
+ P->depth = depth;
+ return 0;
+}
+
+static inline int blake2b_param_set_leaf_length( blake2b_param *P, const uint32_t leaf_length )
+{
+ store32( &P->leaf_length, leaf_length );
+ return 0;
+}
+
+static inline int blake2b_param_set_node_offset( blake2b_param *P, const uint64_t node_offset )
+{
+ store64( &P->node_offset, node_offset );
+ return 0;
+}
+
+static inline int blake2b_param_set_node_depth( blake2b_param *P, const uint8_t node_depth )
+{
+ P->node_depth = node_depth;
+ return 0;
+}
+
+static inline int blake2b_param_set_inner_length( blake2b_param *P, const uint8_t inner_length )
+{
+ P->inner_length = inner_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_salt( blake2b_param *P, const uint8_t salt[BLAKE2B_SALTBYTES] )
+{
+ memcpy( P->salt, salt, BLAKE2B_SALTBYTES );
+ return 0;
+}
+
+static inline int blake2b_param_set_personal( blake2b_param *P, const uint8_t personal[BLAKE2B_PERSONALBYTES] )
+{
+ memcpy( P->personal, personal, BLAKE2B_PERSONALBYTES );
+ return 0;
+}
+
+static inline int blake2b_init0( blake2b_state *S )
+{
+ memset( S, 0, sizeof( blake2b_state ) );
+
+ for( int i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];
+
+ return 0;
+}
+
+#define blake2b_init BLAKE2_IMPL_NAME(blake2b_init)
+#define blake2b_init_param BLAKE2_IMPL_NAME(blake2b_init_param)
+#define blake2b_init_key BLAKE2_IMPL_NAME(blake2b_init_key)
+#define blake2b_update BLAKE2_IMPL_NAME(blake2b_update)
+#define blake2b_final BLAKE2_IMPL_NAME(blake2b_final)
+#define blake2b BLAKE2_IMPL_NAME(blake2b)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2b_init( blake2b_state *S, size_t outlen );
+ int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+ int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+/* init xors IV with input parameter block */
+int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+{
+ blake2b_init0( S );
+ uint8_t *p = ( uint8_t * )( P );
+
+ /* IV XOR ParamBlock */
+ for( size_t i = 0; i < 8; ++i )
+ S->h[i] ^= load64( p + sizeof( S->h[i] ) * i );
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+
+int blake2b_init( blake2b_state *S, size_t outlen )
+{
+ blake2b_param P[1];
+
+ if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+ P->digest_length = ( uint8_t ) outlen;
+ P->key_length = 0;
+ P->fanout = 1;
+ P->depth = 1;
+ store32( &P->leaf_length, 0 );
+ store64( &P->node_offset, 0 );
+ P->node_depth = 0;
+ P->inner_length = 0;
+ memset( P->reserved, 0, sizeof( P->reserved ) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ return blake2b_init_param( S, P );
+}
+
+
+int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ blake2b_param P[1];
+
+ if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+ if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ P->digest_length = ( uint8_t ) outlen;
+ P->key_length = ( uint8_t ) keylen;
+ P->fanout = 1;
+ P->depth = 1;
+ store32( &P->leaf_length, 0 );
+ store64( &P->node_offset, 0 );
+ P->node_depth = 0;
+ P->inner_length = 0;
+ memset( P->reserved, 0, sizeof( P->reserved ) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+
+ if( blake2b_init_param( S, P ) < 0 ) return -1;
+
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset( block, 0, BLAKE2B_BLOCKBYTES );
+ memcpy( block, key, keylen );
+ blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
+ secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+static int blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
+{
+ uint64_t m[16];
+ uint64_t v[16];
+ size_t i;
+
+ for( i = 0; i < 16; ++i )
+ m[i] = load64( block + i * sizeof( m[i] ) );
+
+ for( i = 0; i < 8; ++i )
+ v[i] = S->h[i];
+
+ v[ 8] = blake2b_IV[0];
+ v[ 9] = blake2b_IV[1];
+ v[10] = blake2b_IV[2];
+ v[11] = blake2b_IV[3];
+ v[12] = S->t[0] ^ blake2b_IV[4];
+ v[13] = S->t[1] ^ blake2b_IV[5];
+ v[14] = S->f[0] ^ blake2b_IV[6];
+ v[15] = S->f[1] ^ blake2b_IV[7];
+#define G(r,i,a,b,c,d) \
+ do { \
+ a = a + b + m[blake2b_sigma[r][2*i+0]]; \
+ d = rotr64(d ^ a, 32); \
+ c = c + d; \
+ b = rotr64(b ^ c, 24); \
+ a = a + b + m[blake2b_sigma[r][2*i+1]]; \
+ d = rotr64(d ^ a, 16); \
+ c = c + d; \
+ b = rotr64(b ^ c, 63); \
+ } while(0)
+#define ROUND(r) \
+ do { \
+ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+ G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+ G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+ G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+ G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+ } while(0)
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+ ROUND( 10 );
+ ROUND( 11 );
+
+ for( i = 0; i < 8; ++i )
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+
+#undef G
+#undef ROUND
+ return 0;
+}
+
+
+int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ uint32_t left = S->buflen;
+ uint32_t fill = 2 * BLAKE2B_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+ blake2b_compress( S, S->buf ); // Compress
+ memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2B_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else // inlen <= fill
+ {
+ memcpy( S->buf + left, in, inlen );
+ S->buflen += ( uint32_t ) inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+
+ return 0;
+}
+
+int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t buffer[BLAKE2B_OUTBYTES];
+ size_t i;
+
+ if(S->outlen != outlen) return -1;
+
+ if( S->buflen > BLAKE2B_BLOCKBYTES )
+ {
+ blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+ blake2b_compress( S, S->buf );
+ S->buflen -= BLAKE2B_BLOCKBYTES;
+ memmove( S->buf, S->buf + BLAKE2B_BLOCKBYTES, S->buflen );
+ }
+
+ blake2b_increment_counter( S, S->buflen );
+ blake2b_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2b_compress( S, S->buf );
+
+ for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+ store64( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+ memcpy( out, buffer, outlen );
+ return 0;
+}
+
+int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2b_state S[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if( NULL == key && keylen > 0 ) return -1;
+
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ if( keylen > 0 )
+ {
+ if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+ }
+ else
+ {
+ if( blake2b_init( S, outlen ) < 0 ) return -1;
+ }
+
+ if( blake2b_update( S, ( uint8_t * )in, inlen ) < 0 ) return -1;
+ return blake2b_final( S, out, outlen );
+}
+
+
diff --git a/contrib/libs/blake2/src/blake2b-round.h b/contrib/libs/blake2/src/blake2b-round.h
new file mode 100644
index 0000000000..cebc22550d
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b-round.h
@@ -0,0 +1,160 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2B_ROUND_H__
+#define __BLAKE2B_ROUND_H__
+
+#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
+#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
+
+#define LOADU(p) _mm_loadu_si128( (__m128i *)(p) )
+#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r)
+
+#define TOF(reg) _mm_castsi128_ps((reg))
+#define TOI(reg) _mm_castps_si128((reg))
+
+#define LIKELY(x) __builtin_expect((x),1)
+
+
+/* Microarchitecture-specific macros */
+#ifndef HAVE_XOP
+#ifdef HAVE_SSSE3
+#define _mm_roti_epi64(x, c) \
+ (-(c) == 32) ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2,3,0,1)) \
+ : (-(c) == 24) ? _mm_shuffle_epi8((x), r24) \
+ : (-(c) == 16) ? _mm_shuffle_epi8((x), r16) \
+ : (-(c) == 63) ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_add_epi64((x), (x))) \
+ : _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_slli_epi64((x), 64-(-(c))))
+#else
+#define _mm_roti_epi64(r, c) _mm_xor_si128(_mm_srli_epi64( (r), -(c) ),_mm_slli_epi64( (r), 64-(-(c)) ))
+#endif
+#else
+/* ... */
+#endif
+
+
+
+#define G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \
+ row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \
+ row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \
+ \
+ row4l = _mm_xor_si128(row4l, row1l); \
+ row4h = _mm_xor_si128(row4h, row1h); \
+ \
+ row4l = _mm_roti_epi64(row4l, -32); \
+ row4h = _mm_roti_epi64(row4h, -32); \
+ \
+ row3l = _mm_add_epi64(row3l, row4l); \
+ row3h = _mm_add_epi64(row3h, row4h); \
+ \
+ row2l = _mm_xor_si128(row2l, row3l); \
+ row2h = _mm_xor_si128(row2h, row3h); \
+ \
+ row2l = _mm_roti_epi64(row2l, -24); \
+ row2h = _mm_roti_epi64(row2h, -24); \
+
+#define G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \
+ row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \
+ row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \
+ \
+ row4l = _mm_xor_si128(row4l, row1l); \
+ row4h = _mm_xor_si128(row4h, row1h); \
+ \
+ row4l = _mm_roti_epi64(row4l, -16); \
+ row4h = _mm_roti_epi64(row4h, -16); \
+ \
+ row3l = _mm_add_epi64(row3l, row4l); \
+ row3h = _mm_add_epi64(row3h, row4h); \
+ \
+ row2l = _mm_xor_si128(row2l, row3l); \
+ row2h = _mm_xor_si128(row2h, row3h); \
+ \
+ row2l = _mm_roti_epi64(row2l, -63); \
+ row2h = _mm_roti_epi64(row2h, -63); \
+
+#if defined(HAVE_SSSE3)
+#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+ t0 = _mm_alignr_epi8(row2h, row2l, 8); \
+ t1 = _mm_alignr_epi8(row2l, row2h, 8); \
+ row2l = t0; \
+ row2h = t1; \
+ \
+ t0 = row3l; \
+ row3l = row3h; \
+ row3h = t0; \
+ \
+ t0 = _mm_alignr_epi8(row4h, row4l, 8); \
+ t1 = _mm_alignr_epi8(row4l, row4h, 8); \
+ row4l = t1; \
+ row4h = t0;
+
+#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+ t0 = _mm_alignr_epi8(row2l, row2h, 8); \
+ t1 = _mm_alignr_epi8(row2h, row2l, 8); \
+ row2l = t0; \
+ row2h = t1; \
+ \
+ t0 = row3l; \
+ row3l = row3h; \
+ row3h = t0; \
+ \
+ t0 = _mm_alignr_epi8(row4l, row4h, 8); \
+ t1 = _mm_alignr_epi8(row4h, row4l, 8); \
+ row4l = t1; \
+ row4h = t0;
+#else
+
+#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+ t0 = row4l;\
+ t1 = row2l;\
+ row4l = row3l;\
+ row3l = row3h;\
+ row3h = row4l;\
+ row4l = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t0, t0)); \
+ row4h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row4h, row4h)); \
+ row2l = _mm_unpackhi_epi64(row2l, _mm_unpacklo_epi64(row2h, row2h)); \
+ row2h = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(t1, t1))
+
+#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \
+ t0 = row3l;\
+ row3l = row3h;\
+ row3h = t0;\
+ t0 = row2l;\
+ t1 = row4l;\
+ row2l = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(row2l, row2l)); \
+ row2h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row2h, row2h)); \
+ row4l = _mm_unpackhi_epi64(row4l, _mm_unpacklo_epi64(row4h, row4h)); \
+ row4h = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t1, t1))
+
+#endif
+
+#if defined(HAVE_SSE4_1)
+#include "blake2b-load-sse41.h"
+#else
+#include "blake2b-load-sse2.h"
+#endif
+
+#define ROUND(r) \
+ LOAD_MSG_ ##r ##_1(b0, b1); \
+ G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+ LOAD_MSG_ ##r ##_2(b0, b1); \
+ G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+ DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h); \
+ LOAD_MSG_ ##r ##_3(b0, b1); \
+ G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+ LOAD_MSG_ ##r ##_4(b0, b1); \
+ G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \
+ UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h);
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2b.c b/contrib/libs/blake2/src/blake2b.c
new file mode 100644
index 0000000000..6ac57f24c0
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2b.c
@@ -0,0 +1,443 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#include "blake2-config.h"
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+
+#if defined(HAVE_SSE2)
+#include <emmintrin.h>
+// MSVC only defines _mm_set_epi64x for x86_64...
+#if defined(_MSC_VER) && !defined(_M_X64) && !defined(__clang__)
+static inline __m128i _mm_set_epi64x( const uint64_t u1, const uint64_t u0 )
+{
+ return _mm_set_epi32( u1 >> 32, u1, u0 >> 32, u0 );
+}
+#endif
+#endif
+
+#if defined(HAVE_SSSE3)
+#include <tmmintrin.h>
+#endif
+#if defined(HAVE_SSE4_1)
+#include <smmintrin.h>
+#endif
+#if defined(HAVE_AVX)
+#include <immintrin.h>
+#endif
+#if defined(HAVE_XOP) && !defined(_MSC_VER)
+#include <x86intrin.h>
+#endif
+
+
+
+#include "blake2b-round.h"
+
+static const uint64_t blake2b_IV[8] =
+{
+ 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+};
+
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2b_set_lastnode( blake2b_state *S )
+{
+ S->f[1] = ~0ULL;
+ return 0;
+}
+
+static inline int blake2b_clear_lastnode( blake2b_state *S )
+{
+ S->f[1] = 0ULL;
+ return 0;
+}
+
+static inline int blake2b_set_lastblock( blake2b_state *S )
+{
+ if( S->last_node ) blake2b_set_lastnode( S );
+
+ S->f[0] = ~0ULL;
+ return 0;
+}
+
+static inline int blake2b_clear_lastblock( blake2b_state *S )
+{
+ if( S->last_node ) blake2b_clear_lastnode( S );
+
+ S->f[0] = 0ULL;
+ return 0;
+}
+
+
+static inline int blake2b_increment_counter( blake2b_state *S, const uint64_t inc )
+{
+#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__))
+ // ADD/ADC chain
+ __uint128_t t = ( ( __uint128_t )S->t[1] << 64 ) | S->t[0];
+ t += inc;
+ S->t[0] = ( uint64_t )( t >> 0 );
+ S->t[1] = ( uint64_t )( t >> 64 );
+#else
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+#endif
+ return 0;
+}
+
+
+// Parameter-related functions
+static inline int blake2b_param_set_digest_length( blake2b_param *P, const uint8_t digest_length )
+{
+ P->digest_length = digest_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_fanout( blake2b_param *P, const uint8_t fanout )
+{
+ P->fanout = fanout;
+ return 0;
+}
+
+static inline int blake2b_param_set_max_depth( blake2b_param *P, const uint8_t depth )
+{
+ P->depth = depth;
+ return 0;
+}
+
+static inline int blake2b_param_set_leaf_length( blake2b_param *P, const uint32_t leaf_length )
+{
+ P->leaf_length = leaf_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_node_offset( blake2b_param *P, const uint64_t node_offset )
+{
+ P->node_offset = node_offset;
+ return 0;
+}
+
+static inline int blake2b_param_set_node_depth( blake2b_param *P, const uint8_t node_depth )
+{
+ P->node_depth = node_depth;
+ return 0;
+}
+
+static inline int blake2b_param_set_inner_length( blake2b_param *P, const uint8_t inner_length )
+{
+ P->inner_length = inner_length;
+ return 0;
+}
+
+static inline int blake2b_param_set_salt( blake2b_param *P, const uint8_t salt[BLAKE2B_SALTBYTES] )
+{
+ memcpy( P->salt, salt, BLAKE2B_SALTBYTES );
+ return 0;
+}
+
+static inline int blake2b_param_set_personal( blake2b_param *P, const uint8_t personal[BLAKE2B_PERSONALBYTES] )
+{
+ memcpy( P->personal, personal, BLAKE2B_PERSONALBYTES );
+ return 0;
+}
+
+static inline int blake2b_init0( blake2b_state *S )
+{
+ memset( S, 0, sizeof( blake2b_state ) );
+
+ for( int i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];
+
+ return 0;
+}
+
+
+
+#define blake2b_init BLAKE2_IMPL_NAME(blake2b_init)
+#define blake2b_init_param BLAKE2_IMPL_NAME(blake2b_init_param)
+#define blake2b_init_key BLAKE2_IMPL_NAME(blake2b_init_key)
+#define blake2b_update BLAKE2_IMPL_NAME(blake2b_update)
+#define blake2b_final BLAKE2_IMPL_NAME(blake2b_final)
+#define blake2b BLAKE2_IMPL_NAME(blake2b)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2b_init( blake2b_state *S, size_t outlen );
+ int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+ int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen );
+ int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen );
+ int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+/* init xors IV with input parameter block */
+int blake2b_init_param( blake2b_state *S, const blake2b_param *P )
+{
+ uint8_t *p, *h, *v;
+ //blake2b_init0( S );
+ v = ( uint8_t * )( blake2b_IV );
+ h = ( uint8_t * )( S->h );
+ p = ( uint8_t * )( P );
+ /* IV XOR ParamBlock */
+ memset( S, 0, sizeof( blake2b_state ) );
+
+ for( int i = 0; i < BLAKE2B_OUTBYTES; ++i ) h[i] = v[i] ^ p[i];
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+/* Some sort of default parameter block initialization, for sequential blake2b */
+
+int blake2b_init( blake2b_state *S, size_t outlen )
+{
+ if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+ const blake2b_param P =
+ {
+ ( uint8_t ) outlen,
+ 0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ {0},
+ {0},
+ {0}
+ };
+ return blake2b_init_param( S, &P );
+}
+
+int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;
+
+ if ( ( !keylen ) || keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ const blake2b_param P =
+ {
+ ( uint8_t ) outlen,
+ ( uint8_t ) keylen,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ {0},
+ {0},
+ {0}
+ };
+
+ if( blake2b_init_param( S, &P ) < 0 )
+ return 0;
+
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset( block, 0, BLAKE2B_BLOCKBYTES );
+ memcpy( block, key, keylen );
+ blake2b_update( S, block, BLAKE2B_BLOCKBYTES );
+ secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+static inline int blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
+{
+ __m128i row1l, row1h;
+ __m128i row2l, row2h;
+ __m128i row3l, row3h;
+ __m128i row4l, row4h;
+ __m128i b0, b1;
+ __m128i t0, t1;
+#if defined(HAVE_SSSE3) && !defined(HAVE_XOP)
+ const __m128i r16 = _mm_setr_epi8( 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9 );
+ const __m128i r24 = _mm_setr_epi8( 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10 );
+#endif
+#if defined(HAVE_SSE4_1)
+ const __m128i m0 = LOADU( block + 00 );
+ const __m128i m1 = LOADU( block + 16 );
+ const __m128i m2 = LOADU( block + 32 );
+ const __m128i m3 = LOADU( block + 48 );
+ const __m128i m4 = LOADU( block + 64 );
+ const __m128i m5 = LOADU( block + 80 );
+ const __m128i m6 = LOADU( block + 96 );
+ const __m128i m7 = LOADU( block + 112 );
+#else
+ const uint64_t m0 = ( ( uint64_t * )block )[ 0];
+ const uint64_t m1 = ( ( uint64_t * )block )[ 1];
+ const uint64_t m2 = ( ( uint64_t * )block )[ 2];
+ const uint64_t m3 = ( ( uint64_t * )block )[ 3];
+ const uint64_t m4 = ( ( uint64_t * )block )[ 4];
+ const uint64_t m5 = ( ( uint64_t * )block )[ 5];
+ const uint64_t m6 = ( ( uint64_t * )block )[ 6];
+ const uint64_t m7 = ( ( uint64_t * )block )[ 7];
+ const uint64_t m8 = ( ( uint64_t * )block )[ 8];
+ const uint64_t m9 = ( ( uint64_t * )block )[ 9];
+ const uint64_t m10 = ( ( uint64_t * )block )[10];
+ const uint64_t m11 = ( ( uint64_t * )block )[11];
+ const uint64_t m12 = ( ( uint64_t * )block )[12];
+ const uint64_t m13 = ( ( uint64_t * )block )[13];
+ const uint64_t m14 = ( ( uint64_t * )block )[14];
+ const uint64_t m15 = ( ( uint64_t * )block )[15];
+#endif
+ row1l = LOADU( &S->h[0] );
+ row1h = LOADU( &S->h[2] );
+ row2l = LOADU( &S->h[4] );
+ row2h = LOADU( &S->h[6] );
+ row3l = LOADU( &blake2b_IV[0] );
+ row3h = LOADU( &blake2b_IV[2] );
+ row4l = _mm_xor_si128( LOADU( &blake2b_IV[4] ), LOADU( &S->t[0] ) );
+ row4h = _mm_xor_si128( LOADU( &blake2b_IV[6] ), LOADU( &S->f[0] ) );
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+ ROUND( 10 );
+ ROUND( 11 );
+ row1l = _mm_xor_si128( row3l, row1l );
+ row1h = _mm_xor_si128( row3h, row1h );
+ STOREU( &S->h[0], _mm_xor_si128( LOADU( &S->h[0] ), row1l ) );
+ STOREU( &S->h[2], _mm_xor_si128( LOADU( &S->h[2] ), row1h ) );
+ row2l = _mm_xor_si128( row4l, row2l );
+ row2h = _mm_xor_si128( row4h, row2h );
+ STOREU( &S->h[4], _mm_xor_si128( LOADU( &S->h[4] ), row2l ) );
+ STOREU( &S->h[6], _mm_xor_si128( LOADU( &S->h[6] ), row2h ) );
+ return 0;
+}
+
+
+int blake2b_update( blake2b_state *S, const uint8_t *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ uint32_t left = S->buflen;
+ uint32_t fill = 2 * BLAKE2B_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+ blake2b_compress( S, S->buf ); // Compress
+ memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2B_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else // inlen <= fill
+ {
+ memcpy( S->buf + left, in, inlen );
+ S->buflen += ( uint32_t ) inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+
+ return 0;
+}
+
+
+int blake2b_final( blake2b_state *S, uint8_t *out, size_t outlen )
+{
+ if(S->outlen != outlen) return -1;
+
+ if( S->buflen > BLAKE2B_BLOCKBYTES )
+ {
+ blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );
+ blake2b_compress( S, S->buf );
+ S->buflen -= BLAKE2B_BLOCKBYTES;
+ memmove( S->buf, S->buf + BLAKE2B_BLOCKBYTES, S->buflen );
+ }
+
+ blake2b_increment_counter( S, S->buflen );
+ blake2b_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2b_compress( S, S->buf );
+ memcpy( out, &S->h[0], outlen );
+ return 0;
+}
+
+
+int blake2b( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2b_state S[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if( NULL == key && keylen > 0 ) return -1;
+
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ if( keylen )
+ {
+ if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+ }
+ else
+ {
+ if( blake2b_init( S, outlen ) < 0 ) return -1;
+ }
+
+ if( blake2b_update( S, ( uint8_t * )in, inlen ) < 0) return -1;
+ return blake2b_final( S, out, outlen );
+}
+
+#if defined(SUPERCOP)
+int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
+{
+ return blake2b( out, in, NULL, BLAKE2B_OUTBYTES, inlen, 0 );
+}
+#endif
diff --git a/contrib/libs/blake2/src/blake2bp.c b/contrib/libs/blake2/src/blake2bp.c
new file mode 100644
index 0000000000..4522161171
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2bp.c
@@ -0,0 +1,274 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#define PARALLELISM_DEGREE 4
+
+static int blake2bp_init_leaf( blake2b_state *S, uint8_t outlen, uint8_t keylen, uint64_t offset )
+{
+ blake2b_param P[1];
+ P->digest_length = outlen;
+ P->key_length = keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ store32(&P->leaf_length, 0);
+ store64(&P->node_offset, offset);
+ P->node_depth = 0;
+ P->inner_length = BLAKE2B_OUTBYTES;
+ memset( P->reserved, 0, sizeof( P->reserved ) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ blake2b_init_param( S, P );
+ S->outlen = P->inner_length;
+ return 0;
+}
+
+static int blake2bp_init_root( blake2b_state *S, uint8_t outlen, uint8_t keylen )
+{
+ blake2b_param P[1];
+ P->digest_length = outlen;
+ P->key_length = keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ store32(&P->leaf_length, 0);
+ store64(&P->node_offset, 0);
+ P->node_depth = 1;
+ P->inner_length = BLAKE2B_OUTBYTES;
+ memset( P->reserved, 0, sizeof( P->reserved ) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ blake2b_init_param( S, P );
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+int blake2bp_init( blake2bp_state *S, size_t outlen )
+{
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ if( blake2bp_init_root( S->R, ( uint8_t ) outlen, 0 ) < 0 )
+ return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2bp_init_leaf( S->S[i], ( uint8_t ) outlen, 0, i ) < 0 ) return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ S->outlen = ( uint8_t ) outlen;
+ return 0;
+}
+
+int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ if( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ if( blake2bp_init_root( S->R, ( uint8_t ) outlen, ( uint8_t ) keylen ) < 0 )
+ return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2bp_init_leaf( S->S[i], ( uint8_t ) outlen, ( uint8_t ) keylen, i ) < 0 )
+ return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ S->outlen = ( uint8_t ) outlen;
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset( block, 0, BLAKE2B_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES );
+
+ secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+
+int blake2bp_update( blake2bp_state *S, const uint8_t *in, size_t inlen )
+{
+ size_t left = S->buflen;
+ size_t fill = sizeof( S->buf ) - left;
+
+ if( left && inlen >= fill )
+ {
+ memcpy( S->buf + left, in, fill );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES );
+
+ in += fill;
+ inlen -= fill;
+ left = 0;
+ }
+
+#if defined(_OPENMP)
+ omp_set_num_threads(PARALLELISM_DEGREE);
+ #pragma omp parallel shared(S)
+#else
+ for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t id__ = ( size_t ) omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const uint8_t *in__ = ( const uint8_t * )in;
+ in__ += id__ * BLAKE2B_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
+ {
+ blake2b_update( S->S[id__], in__, BLAKE2B_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+ }
+ }
+
+ in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES );
+ inlen %= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+
+ if( inlen > 0 )
+ memcpy( S->buf + left, in, inlen );
+
+ S->buflen = ( uint32_t ) left + ( uint32_t ) inlen;
+ return 0;
+}
+
+
+
+int blake2bp_final( blake2bp_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
+
+ if(S->outlen != outlen) return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ {
+ if( S->buflen > i * BLAKE2B_BLOCKBYTES )
+ {
+ size_t left = S->buflen - i * BLAKE2B_BLOCKBYTES;
+
+ if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES;
+
+ blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left );
+ }
+
+ blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES );
+ }
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES );
+
+ return blake2b_final( S->R, out, outlen );
+}
+
+int blake2bp( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES];
+ blake2b_state S[PARALLELISM_DEGREE][1];
+ blake2b_state FS[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if ( NULL == key && keylen > 0) return -1;
+
+ if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2B_KEYBYTES ) return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2bp_init_leaf( S[i], ( uint8_t ) outlen, ( uint8_t ) keylen, i ) < 0 )
+ return -1;
+
+ S[PARALLELISM_DEGREE - 1]->last_node = 1; // mark last node
+
+ if( keylen > 0 )
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES];
+ memset( block, 0, BLAKE2B_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES );
+
+ secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */
+ }
+
+#if defined(_OPENMP)
+ omp_set_num_threads(PARALLELISM_DEGREE);
+ #pragma omp parallel shared(S,hash)
+#else
+ for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t id__ = ( size_t ) omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const uint8_t *in__ = ( const uint8_t * )in;
+ in__ += id__ * BLAKE2B_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES )
+ {
+ blake2b_update( S[id__], in__, BLAKE2B_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES;
+ }
+
+ if( inlen__ > id__ * BLAKE2B_BLOCKBYTES )
+ {
+ const size_t left = inlen__ - id__ * BLAKE2B_BLOCKBYTES;
+ const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES;
+ blake2b_update( S[id__], in__, len );
+ }
+
+ blake2b_final( S[id__], hash[id__], BLAKE2B_OUTBYTES );
+ }
+
+ if( blake2bp_init_root( FS, ( uint8_t ) outlen, ( uint8_t ) keylen ) < 0 )
+ return -1;
+
+ FS->last_node = 1; // Mark as last node
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES );
+
+ return blake2b_final( FS, out, outlen );
+}
+
+
+
diff --git a/contrib/libs/blake2/src/blake2s-load-sse2.h b/contrib/libs/blake2/src/blake2s-load-sse2.h
new file mode 100644
index 0000000000..b24483cf93
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-load-sse2.h
@@ -0,0 +1,59 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_SSE2_H__
+#define __BLAKE2S_LOAD_SSE2_H__
+
+#define LOAD_MSG_0_1(buf) buf = _mm_set_epi32(m6,m4,m2,m0)
+#define LOAD_MSG_0_2(buf) buf = _mm_set_epi32(m7,m5,m3,m1)
+#define LOAD_MSG_0_3(buf) buf = _mm_set_epi32(m14,m12,m10,m8)
+#define LOAD_MSG_0_4(buf) buf = _mm_set_epi32(m15,m13,m11,m9)
+#define LOAD_MSG_1_1(buf) buf = _mm_set_epi32(m13,m9,m4,m14)
+#define LOAD_MSG_1_2(buf) buf = _mm_set_epi32(m6,m15,m8,m10)
+#define LOAD_MSG_1_3(buf) buf = _mm_set_epi32(m5,m11,m0,m1)
+#define LOAD_MSG_1_4(buf) buf = _mm_set_epi32(m3,m7,m2,m12)
+#define LOAD_MSG_2_1(buf) buf = _mm_set_epi32(m15,m5,m12,m11)
+#define LOAD_MSG_2_2(buf) buf = _mm_set_epi32(m13,m2,m0,m8)
+#define LOAD_MSG_2_3(buf) buf = _mm_set_epi32(m9,m7,m3,m10)
+#define LOAD_MSG_2_4(buf) buf = _mm_set_epi32(m4,m1,m6,m14)
+#define LOAD_MSG_3_1(buf) buf = _mm_set_epi32(m11,m13,m3,m7)
+#define LOAD_MSG_3_2(buf) buf = _mm_set_epi32(m14,m12,m1,m9)
+#define LOAD_MSG_3_3(buf) buf = _mm_set_epi32(m15,m4,m5,m2)
+#define LOAD_MSG_3_4(buf) buf = _mm_set_epi32(m8,m0,m10,m6)
+#define LOAD_MSG_4_1(buf) buf = _mm_set_epi32(m10,m2,m5,m9)
+#define LOAD_MSG_4_2(buf) buf = _mm_set_epi32(m15,m4,m7,m0)
+#define LOAD_MSG_4_3(buf) buf = _mm_set_epi32(m3,m6,m11,m14)
+#define LOAD_MSG_4_4(buf) buf = _mm_set_epi32(m13,m8,m12,m1)
+#define LOAD_MSG_5_1(buf) buf = _mm_set_epi32(m8,m0,m6,m2)
+#define LOAD_MSG_5_2(buf) buf = _mm_set_epi32(m3,m11,m10,m12)
+#define LOAD_MSG_5_3(buf) buf = _mm_set_epi32(m1,m15,m7,m4)
+#define LOAD_MSG_5_4(buf) buf = _mm_set_epi32(m9,m14,m5,m13)
+#define LOAD_MSG_6_1(buf) buf = _mm_set_epi32(m4,m14,m1,m12)
+#define LOAD_MSG_6_2(buf) buf = _mm_set_epi32(m10,m13,m15,m5)
+#define LOAD_MSG_6_3(buf) buf = _mm_set_epi32(m8,m9,m6,m0)
+#define LOAD_MSG_6_4(buf) buf = _mm_set_epi32(m11,m2,m3,m7)
+#define LOAD_MSG_7_1(buf) buf = _mm_set_epi32(m3,m12,m7,m13)
+#define LOAD_MSG_7_2(buf) buf = _mm_set_epi32(m9,m1,m14,m11)
+#define LOAD_MSG_7_3(buf) buf = _mm_set_epi32(m2,m8,m15,m5)
+#define LOAD_MSG_7_4(buf) buf = _mm_set_epi32(m10,m6,m4,m0)
+#define LOAD_MSG_8_1(buf) buf = _mm_set_epi32(m0,m11,m14,m6)
+#define LOAD_MSG_8_2(buf) buf = _mm_set_epi32(m8,m3,m9,m15)
+#define LOAD_MSG_8_3(buf) buf = _mm_set_epi32(m10,m1,m13,m12)
+#define LOAD_MSG_8_4(buf) buf = _mm_set_epi32(m5,m4,m7,m2)
+#define LOAD_MSG_9_1(buf) buf = _mm_set_epi32(m1,m7,m8,m10)
+#define LOAD_MSG_9_2(buf) buf = _mm_set_epi32(m5,m6,m4,m2)
+#define LOAD_MSG_9_3(buf) buf = _mm_set_epi32(m13,m3,m9,m15)
+#define LOAD_MSG_9_4(buf) buf = _mm_set_epi32(m0,m12,m14,m11)
+
+
+#endif
diff --git a/contrib/libs/blake2/src/blake2s-load-sse41.h b/contrib/libs/blake2/src/blake2s-load-sse41.h
new file mode 100644
index 0000000000..3ac12eb6f5
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-load-sse41.h
@@ -0,0 +1,229 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_SSE41_H__
+#define __BLAKE2S_LOAD_SSE41_H__
+
+#define LOAD_MSG_0_1(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(2,0,2,0)));
+
+#define LOAD_MSG_0_2(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(3,1,3,1)));
+
+#define LOAD_MSG_0_3(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m2), TOF(m3), _MM_SHUFFLE(2,0,2,0)));
+
+#define LOAD_MSG_0_4(buf) \
+buf = TOI(_mm_shuffle_ps(TOF(m2), TOF(m3), _MM_SHUFFLE(3,1,3,1)));
+
+#define LOAD_MSG_1_1(buf) \
+t0 = _mm_blend_epi16(m1, m2, 0x0C); \
+t1 = _mm_slli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3));
+
+#define LOAD_MSG_1_2(buf) \
+t0 = _mm_shuffle_epi32(m2,_MM_SHUFFLE(0,0,2,0)); \
+t1 = _mm_blend_epi16(m1,m3,0xC0); \
+t2 = _mm_blend_epi16(t0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_1_3(buf) \
+t0 = _mm_slli_si128(m1, 4); \
+t1 = _mm_blend_epi16(m2, t0, 0x30); \
+t2 = _mm_blend_epi16(m0, t1, 0xF0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_1_4(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_slli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0, t1, 0x0C); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1));
+
+#define LOAD_MSG_2_1(buf) \
+t0 = _mm_unpackhi_epi32(m2,m3); \
+t1 = _mm_blend_epi16(m3,m1,0x0C); \
+t2 = _mm_blend_epi16(t0, t1, 0x0F); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2));
+
+#define LOAD_MSG_2_2(buf) \
+t0 = _mm_unpacklo_epi32(m2,m0); \
+t1 = _mm_blend_epi16(t0, m0, 0xF0); \
+t2 = _mm_slli_si128(m3, 8); \
+buf = _mm_blend_epi16(t1, t2, 0xC0);
+
+#define LOAD_MSG_2_3(buf) \
+t0 = _mm_blend_epi16(m0, m2, 0x3C); \
+t1 = _mm_srli_si128(m1, 12); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_2_4(buf) \
+t0 = _mm_slli_si128(m3, 4); \
+t1 = _mm_blend_epi16(m0, m1, 0x33); \
+t2 = _mm_blend_epi16(t1, t0, 0xC0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(0,1,2,3));
+
+#define LOAD_MSG_3_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_unpackhi_epi32(t0, m2); \
+t2 = _mm_blend_epi16(t1, m3, 0x0C); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2));
+
+#define LOAD_MSG_3_2(buf) \
+t0 = _mm_slli_si128(m2, 8); \
+t1 = _mm_blend_epi16(m3,m0,0x0C); \
+t2 = _mm_blend_epi16(t1, t0, 0xC0); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3));
+
+#define LOAD_MSG_3_3(buf) \
+t0 = _mm_blend_epi16(m0,m1,0x0F); \
+t1 = _mm_blend_epi16(t0, m3, 0xC0); \
+buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(3,0,1,2));
+
+#define LOAD_MSG_3_4(buf) \
+t0 = _mm_unpacklo_epi32(m0,m2); \
+t1 = _mm_unpackhi_epi32(m1,m2); \
+buf = _mm_unpacklo_epi64(t1,t0);
+
+#define LOAD_MSG_4_1(buf) \
+t0 = _mm_unpacklo_epi64(m1,m2); \
+t1 = _mm_unpackhi_epi64(m0,m2); \
+t2 = _mm_blend_epi16(t0,t1,0x33); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3));
+
+#define LOAD_MSG_4_2(buf) \
+t0 = _mm_unpackhi_epi64(m1,m3); \
+t1 = _mm_unpacklo_epi64(m0,m1); \
+buf = _mm_blend_epi16(t0,t1,0x33);
+
+#define LOAD_MSG_4_3(buf) \
+t0 = _mm_unpackhi_epi64(m3,m1); \
+t1 = _mm_unpackhi_epi64(m2,m0); \
+buf = _mm_blend_epi16(t1,t0,0x33);
+
+#define LOAD_MSG_4_4(buf) \
+t0 = _mm_blend_epi16(m0,m2,0x03); \
+t1 = _mm_slli_si128(t0, 8); \
+t2 = _mm_blend_epi16(t1,m3,0x0F); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,2,0,3));
+
+#define LOAD_MSG_5_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_unpacklo_epi32(m0,m2); \
+buf = _mm_unpacklo_epi64(t0,t1);
+
+#define LOAD_MSG_5_2(buf) \
+t0 = _mm_srli_si128(m2, 4); \
+t1 = _mm_blend_epi16(m0,m3,0x03); \
+buf = _mm_blend_epi16(t1,t0,0x3C);
+
+#define LOAD_MSG_5_3(buf) \
+t0 = _mm_blend_epi16(m1,m0,0x0C); \
+t1 = _mm_srli_si128(m3, 4); \
+t2 = _mm_blend_epi16(t0,t1,0x30); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,2,3,0));
+
+#define LOAD_MSG_5_4(buf) \
+t0 = _mm_unpacklo_epi64(m1,m2); \
+t1= _mm_shuffle_epi32(m3, _MM_SHUFFLE(0,2,0,1)); \
+buf = _mm_blend_epi16(t0,t1,0x33);
+
+#define LOAD_MSG_6_1(buf) \
+t0 = _mm_slli_si128(m1, 12); \
+t1 = _mm_blend_epi16(m0,m3,0x33); \
+buf = _mm_blend_epi16(t1,t0,0xC0);
+
+#define LOAD_MSG_6_2(buf) \
+t0 = _mm_blend_epi16(m3,m2,0x30); \
+t1 = _mm_srli_si128(m1, 4); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,3,0));
+
+#define LOAD_MSG_6_3(buf) \
+t0 = _mm_unpacklo_epi64(m0,m2); \
+t1 = _mm_srli_si128(m1, 4); \
+buf = _mm_shuffle_epi32(_mm_blend_epi16(t0,t1,0x0C), _MM_SHUFFLE(2,3,1,0));
+
+#define LOAD_MSG_6_4(buf) \
+t0 = _mm_unpackhi_epi32(m1,m2); \
+t1 = _mm_unpackhi_epi64(m0,t0); \
+buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(3,0,1,2));
+
+#define LOAD_MSG_7_1(buf) \
+t0 = _mm_unpackhi_epi32(m0,m1); \
+t1 = _mm_blend_epi16(t0,m3,0x0F); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(2,0,3,1));
+
+#define LOAD_MSG_7_2(buf) \
+t0 = _mm_blend_epi16(m2,m3,0x30); \
+t1 = _mm_srli_si128(m0,4); \
+t2 = _mm_blend_epi16(t0,t1,0x03); \
+buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,0,2,3));
+
+#define LOAD_MSG_7_3(buf) \
+t0 = _mm_unpackhi_epi64(m0,m3); \
+t1 = _mm_unpacklo_epi64(m1,m2); \
+t2 = _mm_blend_epi16(t0,t1,0x3C); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(0,2,3,1));
+
+#define LOAD_MSG_7_4(buf) \
+t0 = _mm_unpacklo_epi32(m0,m1); \
+t1 = _mm_unpackhi_epi32(m1,m2); \
+buf = _mm_unpacklo_epi64(t0,t1);
+
+#define LOAD_MSG_8_1(buf) \
+t0 = _mm_unpackhi_epi32(m1,m3); \
+t1 = _mm_unpacklo_epi64(t0,m0); \
+t2 = _mm_blend_epi16(t1,m2,0xC0); \
+buf = _mm_shufflehi_epi16(t2,_MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_8_2(buf) \
+t0 = _mm_unpackhi_epi32(m0,m3); \
+t1 = _mm_blend_epi16(m2,t0,0xF0); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(0,2,1,3));
+
+#define LOAD_MSG_8_3(buf) \
+t0 = _mm_blend_epi16(m2,m0,0x0C); \
+t1 = _mm_slli_si128(t0,4); \
+buf = _mm_blend_epi16(t1,m3,0x0F);
+
+#define LOAD_MSG_8_4(buf) \
+t0 = _mm_blend_epi16(m1,m0,0x30); \
+buf = _mm_shuffle_epi32(t0,_MM_SHUFFLE(1,0,3,2));
+
+#define LOAD_MSG_9_1(buf) \
+t0 = _mm_blend_epi16(m0,m2,0x03); \
+t1 = _mm_blend_epi16(m1,m2,0x30); \
+t2 = _mm_blend_epi16(t1,t0,0x0F); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(1,3,0,2));
+
+#define LOAD_MSG_9_2(buf) \
+t0 = _mm_slli_si128(m0,4); \
+t1 = _mm_blend_epi16(m1,t0,0xC0); \
+buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(1,2,0,3));
+
+#define LOAD_MSG_9_3(buf) \
+t0 = _mm_unpackhi_epi32(m0,m3); \
+t1 = _mm_unpacklo_epi32(m2,m3); \
+t2 = _mm_unpackhi_epi64(t0,t1); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(3,0,2,1));
+
+#define LOAD_MSG_9_4(buf) \
+t0 = _mm_blend_epi16(m3,m2,0xC0); \
+t1 = _mm_unpacklo_epi32(m0,m3); \
+t2 = _mm_blend_epi16(t0,t1,0x0F); \
+buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(0,1,2,3));
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2s-load-xop.h b/contrib/libs/blake2/src/blake2s-load-xop.h
new file mode 100644
index 0000000000..ac591a77d1
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-load-xop.h
@@ -0,0 +1,189 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_LOAD_XOP_H__
+#define __BLAKE2S_LOAD_XOP_H__
+
+#define TOB(x) ((x)*4*0x01010101 + 0x03020100) // ..or not TOB
+
+/* Basic VPPERM emulation, for testing purposes */
+/*static __m128i _mm_perm_epi8(const __m128i src1, const __m128i src2, const __m128i sel)
+{
+ const __m128i sixteen = _mm_set1_epi8(16);
+ const __m128i t0 = _mm_shuffle_epi8(src1, sel);
+ const __m128i s1 = _mm_shuffle_epi8(src2, _mm_sub_epi8(sel, sixteen));
+ const __m128i mask = _mm_or_si128(_mm_cmpeq_epi8(sel, sixteen),
+ _mm_cmpgt_epi8(sel, sixteen)); // (>=16) = 0xff : 00
+ return _mm_blendv_epi8(t0, s1, mask);
+}*/
+
+#define LOAD_MSG_0_1(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(6),TOB(4),TOB(2),TOB(0)) );
+
+#define LOAD_MSG_0_2(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(7),TOB(5),TOB(3),TOB(1)) );
+
+#define LOAD_MSG_0_3(buf) \
+buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(6),TOB(4),TOB(2),TOB(0)) );
+
+#define LOAD_MSG_0_4(buf) \
+buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(7),TOB(5),TOB(3),TOB(1)) );
+
+#define LOAD_MSG_1_1(buf) \
+t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(5),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_1_2(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(2),TOB(0),TOB(4),TOB(6)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_1_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(0),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_1_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(7),TOB(2),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_2_1(buf) \
+t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(1),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(4),TOB(0)) );
+
+#define LOAD_MSG_2_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(2),TOB(0),TOB(4)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_2_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(7),TOB(3),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_2_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(1),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_3_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(3),TOB(7)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(1),TOB(5)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(6),TOB(4),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(5),TOB(2)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_3_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(4),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_4_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(6),TOB(2),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_4_2(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_4_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(6),TOB(0),TOB(0)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(6)) );
+
+#define LOAD_MSG_4_4(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(4),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(4),TOB(0)) );
+
+#define LOAD_MSG_5_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(2)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(4),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_5_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_5_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(0),TOB(7),TOB(4)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_5_4(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(5),TOB(0),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(6),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_6_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(0),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(6),TOB(1),TOB(4)) );
+
+#define LOAD_MSG_6_2(buf) \
+t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(6),TOB(0),TOB(0),TOB(1)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(7),TOB(0)) );
+
+#define LOAD_MSG_6_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(4),TOB(5),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_6_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(3),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_7_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(0),TOB(7),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(5)) );
+
+#define LOAD_MSG_7_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(5),TOB(1),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_7_3(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(2),TOB(0),TOB(0),TOB(5)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(7),TOB(0)) );
+
+#define LOAD_MSG_7_4(buf) \
+t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(6),TOB(4),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(6),TOB(2),TOB(1),TOB(0)) );
+
+#define LOAD_MSG_8_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \
+t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) );
+
+#define LOAD_MSG_8_2(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(4),TOB(3),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(7)) );
+
+#define LOAD_MSG_8_3(buf) \
+t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(6),TOB(1),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(5),TOB(4)) ); \
+
+#define LOAD_MSG_8_4(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(4),TOB(7),TOB(2)) );
+
+#define LOAD_MSG_9_1(buf) \
+t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(7),TOB(0),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(4),TOB(6)) );
+
+#define LOAD_MSG_9_2(buf) \
+buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(6),TOB(4),TOB(2)) );
+
+#define LOAD_MSG_9_3(buf) \
+t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(3),TOB(5),TOB(0)) ); \
+buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(7)) );
+
+#define LOAD_MSG_9_4(buf) \
+t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(7)) ); \
+buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(4),TOB(6),TOB(0)) );
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2s-ref.c b/contrib/libs/blake2/src/blake2s-ref.c
new file mode 100644
index 0000000000..db7390f925
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-ref.c
@@ -0,0 +1,375 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+static const uint32_t blake2s_IV[8] =
+{
+ 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+ 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+};
+
+static inline int blake2s_set_lastnode( blake2s_state *S )
+{
+ S->f[1] = ~0U;
+ return 0;
+}
+
+static inline int blake2s_clear_lastnode( blake2s_state *S )
+{
+ S->f[1] = 0U;
+ return 0;
+}
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2s_set_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_set_lastnode( S );
+
+ S->f[0] = ~0U;
+ return 0;
+}
+
+static inline int blake2s_clear_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_clear_lastnode( S );
+
+ S->f[0] = 0U;
+ return 0;
+}
+
+static inline int blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
+{
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+ return 0;
+}
+
+// Parameter-related functions
+static inline int blake2s_param_set_digest_length( blake2s_param *P, const uint8_t digest_length )
+{
+ P->digest_length = digest_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_fanout( blake2s_param *P, const uint8_t fanout )
+{
+ P->fanout = fanout;
+ return 0;
+}
+
+static inline int blake2s_param_set_max_depth( blake2s_param *P, const uint8_t depth )
+{
+ P->depth = depth;
+ return 0;
+}
+
+static inline int blake2s_param_set_leaf_length( blake2s_param *P, const uint32_t leaf_length )
+{
+ store32( &P->leaf_length, leaf_length );
+ return 0;
+}
+
+static inline int blake2s_param_set_node_offset( blake2s_param *P, const uint64_t node_offset )
+{
+ store48( P->node_offset, node_offset );
+ return 0;
+}
+
+static inline int blake2s_param_set_node_depth( blake2s_param *P, const uint8_t node_depth )
+{
+ P->node_depth = node_depth;
+ return 0;
+}
+
+static inline int blake2s_param_set_inner_length( blake2s_param *P, const uint8_t inner_length )
+{
+ P->inner_length = inner_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_salt( blake2s_param *P, const uint8_t salt[BLAKE2S_SALTBYTES] )
+{
+ memcpy( P->salt, salt, BLAKE2S_SALTBYTES );
+ return 0;
+}
+
+static inline int blake2s_param_set_personal( blake2s_param *P, const uint8_t personal[BLAKE2S_PERSONALBYTES] )
+{
+ memcpy( P->personal, personal, BLAKE2S_PERSONALBYTES );
+ return 0;
+}
+
+static inline int blake2s_init0( blake2s_state *S )
+{
+ memset( S, 0, sizeof( blake2s_state ) );
+
+ for( int i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
+
+ return 0;
+}
+
+#define blake2s_init BLAKE2_IMPL_NAME(blake2s_init)
+#define blake2s_init_param BLAKE2_IMPL_NAME(blake2s_init_param)
+#define blake2s_init_key BLAKE2_IMPL_NAME(blake2s_init_key)
+#define blake2s_update BLAKE2_IMPL_NAME(blake2s_update)
+#define blake2s_final BLAKE2_IMPL_NAME(blake2s_final)
+#define blake2s BLAKE2_IMPL_NAME(blake2s)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2s_init( blake2s_state *S, size_t outlen );
+ int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+ int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+/* init2 xors IV with input parameter block */
+int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+ blake2s_init0( S );
+ uint32_t *p = ( uint32_t * )( P );
+
+ /* IV XOR ParamBlock */
+ for( size_t i = 0; i < 8; ++i )
+ S->h[i] ^= load32( &p[i] );
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+// Sequential blake2s initialization
+int blake2s_init( blake2s_state *S, size_t outlen )
+{
+ blake2s_param P[1];
+
+ /* Move interval verification here? */
+ if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+ P->digest_length = ( uint8_t) outlen;
+ P->key_length = 0;
+ P->fanout = 1;
+ P->depth = 1;
+ store32( &P->leaf_length, 0 );
+ store48( &P->node_offset, 0 );
+ P->node_depth = 0;
+ P->inner_length = 0;
+ // memset(P->reserved, 0, sizeof(P->reserved) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ return blake2s_init_param( S, P );
+}
+
+int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ blake2s_param P[1];
+
+ if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+ if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ P->digest_length = ( uint8_t ) outlen;
+ P->key_length = ( uint8_t ) keylen;
+ P->fanout = 1;
+ P->depth = 1;
+ store32( &P->leaf_length, 0 );
+ store48( &P->node_offset, 0 );
+ P->node_depth = 0;
+ P->inner_length = 0;
+ // memset(P->reserved, 0, sizeof(P->reserved) );
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+
+ if( blake2s_init_param( S, P ) < 0 ) return -1;
+
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+ blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
+ secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+static int blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] )
+{
+ uint32_t m[16];
+ uint32_t v[16];
+
+ for( size_t i = 0; i < 16; ++i )
+ m[i] = load32( block + i * sizeof( m[i] ) );
+
+ for( size_t i = 0; i < 8; ++i )
+ v[i] = S->h[i];
+
+ v[ 8] = blake2s_IV[0];
+ v[ 9] = blake2s_IV[1];
+ v[10] = blake2s_IV[2];
+ v[11] = blake2s_IV[3];
+ v[12] = S->t[0] ^ blake2s_IV[4];
+ v[13] = S->t[1] ^ blake2s_IV[5];
+ v[14] = S->f[0] ^ blake2s_IV[6];
+ v[15] = S->f[1] ^ blake2s_IV[7];
+#define G(r,i,a,b,c,d) \
+ do { \
+ a = a + b + m[blake2s_sigma[r][2*i+0]]; \
+ d = rotr32(d ^ a, 16); \
+ c = c + d; \
+ b = rotr32(b ^ c, 12); \
+ a = a + b + m[blake2s_sigma[r][2*i+1]]; \
+ d = rotr32(d ^ a, 8); \
+ c = c + d; \
+ b = rotr32(b ^ c, 7); \
+ } while(0)
+#define ROUND(r) \
+ do { \
+ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+ G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+ G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+ G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+ G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+ } while(0)
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+
+ for( size_t i = 0; i < 8; ++i )
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+
+#undef G
+#undef ROUND
+ return 0;
+}
+
+
+int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ uint32_t left = S->buflen;
+ uint32_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf ); // Compress
+ memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else // inlen <= fill
+ {
+ memcpy( S->buf + left, in, inlen );
+ S->buflen += ( uint32_t ) inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+
+ return 0;
+}
+
+int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t buffer[BLAKE2S_OUTBYTES];
+ size_t i;
+
+ if(S->outlen != outlen) return -1;
+
+ if( S->buflen > BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf );
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ memmove( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
+ }
+
+ blake2s_increment_counter( S, ( uint32_t )S->buflen );
+ blake2s_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2s_compress( S, S->buf );
+
+ for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+ store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+ memcpy( out, buffer, outlen );
+ return 0;
+}
+
+int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2s_state S[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if ( NULL == key && keylen > 0 ) return -1;
+
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ if( keylen > 0 )
+ {
+ if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+ }
+ else
+ {
+ if( blake2s_init( S, outlen ) < 0 ) return -1;
+ }
+
+ if( blake2s_update( S, ( uint8_t * )in, inlen ) < 0) return -1;
+ return blake2s_final( S, out, outlen );
+}
+
diff --git a/contrib/libs/blake2/src/blake2s-round.h b/contrib/libs/blake2/src/blake2s-round.h
new file mode 100644
index 0000000000..1e2f2b7f59
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s-round.h
@@ -0,0 +1,91 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+#pragma once
+#ifndef __BLAKE2S_ROUND_H__
+#define __BLAKE2S_ROUND_H__
+
+#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
+#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
+
+#define LOADU(p) _mm_loadu_si128( (__m128i *)(p) )
+#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r)
+
+#define TOF(reg) _mm_castsi128_ps((reg))
+#define TOI(reg) _mm_castps_si128((reg))
+
+#define LIKELY(x) __builtin_expect((x),1)
+
+
+/* Microarchitecture-specific macros */
+#ifndef HAVE_XOP
+#ifdef HAVE_SSSE3
+#define _mm_roti_epi32(r, c) ( \
+ (8==-(c)) ? _mm_shuffle_epi8(r,r8) \
+ : (16==-(c)) ? _mm_shuffle_epi8(r,r16) \
+ : _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) )) )
+#else
+#define _mm_roti_epi32(r, c) _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) ))
+#endif
+#else
+/* ... */
+#endif
+
+
+#define G1(row1,row2,row3,row4,buf) \
+ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+ row4 = _mm_xor_si128( row4, row1 ); \
+ row4 = _mm_roti_epi32(row4, -16); \
+ row3 = _mm_add_epi32( row3, row4 ); \
+ row2 = _mm_xor_si128( row2, row3 ); \
+ row2 = _mm_roti_epi32(row2, -12);
+
+#define G2(row1,row2,row3,row4,buf) \
+ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+ row4 = _mm_xor_si128( row4, row1 ); \
+ row4 = _mm_roti_epi32(row4, -8); \
+ row3 = _mm_add_epi32( row3, row4 ); \
+ row2 = _mm_xor_si128( row2, row3 ); \
+ row2 = _mm_roti_epi32(row2, -7);
+
+#define DIAGONALIZE(row1,row2,row3,row4) \
+ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
+ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
+
+#define UNDIAGONALIZE(row1,row2,row3,row4) \
+ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
+ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
+
+#if defined(HAVE_XOP)
+#include "blake2s-load-xop.h"
+#elif defined(HAVE_SSE4_1)
+#include "blake2s-load-sse41.h"
+#else
+#include "blake2s-load-sse2.h"
+#endif
+
+#define ROUND(r) \
+ LOAD_MSG_ ##r ##_1(buf1); \
+ G1(row1,row2,row3,row4,buf1); \
+ LOAD_MSG_ ##r ##_2(buf2); \
+ G2(row1,row2,row3,row4,buf2); \
+ DIAGONALIZE(row1,row2,row3,row4); \
+ LOAD_MSG_ ##r ##_3(buf3); \
+ G1(row1,row2,row3,row4,buf3); \
+ LOAD_MSG_ ##r ##_4(buf4); \
+ G2(row1,row2,row3,row4,buf4); \
+ UNDIAGONALIZE(row1,row2,row3,row4); \
+
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2s.c b/contrib/libs/blake2/src/blake2s.c
new file mode 100644
index 0000000000..db97974895
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2s.c
@@ -0,0 +1,422 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#include "blake2-config.h"
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+
+#if defined(HAVE_SSE2)
+#include <emmintrin.h>
+// MSVC only defines _mm_set_epi64x for x86_64...
+#if defined(_MSC_VER) && !defined(_M_X64) && !defined(__clang__)
+static inline __m128i _mm_set_epi64x( const uint64_t u1, const uint64_t u0 )
+{
+ return _mm_set_epi32( u1 >> 32, u1, u0 >> 32, u0 );
+}
+#endif
+#endif
+
+
+#if defined(HAVE_SSSE3)
+#include <tmmintrin.h>
+#endif
+#if defined(HAVE_SSE4_1)
+#include <smmintrin.h>
+#endif
+#if defined(HAVE_AVX)
+#include <immintrin.h>
+#endif
+#if defined(HAVE_XOP) && !defined(_MSC_VER)
+#include <x86intrin.h>
+#endif
+
+#include "blake2s-round.h"
+
+static const uint32_t blake2s_IV[8] =
+{
+ 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+ 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+};
+
+
+/* Some helper functions, not necessarily useful */
+static inline int blake2s_set_lastnode( blake2s_state *S )
+{
+ S->f[1] = ~0U;
+ return 0;
+}
+
+static inline int blake2s_clear_lastnode( blake2s_state *S )
+{
+ S->f[1] = 0U;
+ return 0;
+}
+
+static inline int blake2s_set_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_set_lastnode( S );
+
+ S->f[0] = ~0U;
+ return 0;
+}
+
+static inline int blake2s_clear_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_clear_lastnode( S );
+
+ S->f[0] = 0U;
+ return 0;
+}
+
+static inline int blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
+{
+ uint64_t t = ( ( uint64_t )S->t[1] << 32 ) | S->t[0];
+ t += inc;
+ S->t[0] = ( uint32_t )( t >> 0 );
+ S->t[1] = ( uint32_t )( t >> 32 );
+ return 0;
+}
+
+
+// Parameter-related functions
+static inline int blake2s_param_set_digest_length( blake2s_param *P, const uint8_t digest_length )
+{
+ P->digest_length = digest_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_fanout( blake2s_param *P, const uint8_t fanout )
+{
+ P->fanout = fanout;
+ return 0;
+}
+
+static inline int blake2s_param_set_max_depth( blake2s_param *P, const uint8_t depth )
+{
+ P->depth = depth;
+ return 0;
+}
+
+static inline int blake2s_param_set_leaf_length( blake2s_param *P, const uint32_t leaf_length )
+{
+ P->leaf_length = leaf_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_node_offset( blake2s_param *P, const uint64_t node_offset )
+{
+ store48( P->node_offset, node_offset );
+ return 0;
+}
+
+static inline int blake2s_param_set_node_depth( blake2s_param *P, const uint8_t node_depth )
+{
+ P->node_depth = node_depth;
+ return 0;
+}
+
+static inline int blake2s_param_set_inner_length( blake2s_param *P, const uint8_t inner_length )
+{
+ P->inner_length = inner_length;
+ return 0;
+}
+
+static inline int blake2s_param_set_salt( blake2s_param *P, const uint8_t salt[BLAKE2S_SALTBYTES] )
+{
+ memcpy( P->salt, salt, BLAKE2S_SALTBYTES );
+ return 0;
+}
+
+static inline int blake2s_param_set_personal( blake2s_param *P, const uint8_t personal[BLAKE2S_PERSONALBYTES] )
+{
+ memcpy( P->personal, personal, BLAKE2S_PERSONALBYTES );
+ return 0;
+}
+
+static inline int blake2s_init0( blake2s_state *S )
+{
+ memset( S, 0, sizeof( blake2s_state ) );
+
+ for( int i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
+
+ return 0;
+}
+
+#define blake2s_init BLAKE2_IMPL_NAME(blake2s_init)
+#define blake2s_init_param BLAKE2_IMPL_NAME(blake2s_init_param)
+#define blake2s_init_key BLAKE2_IMPL_NAME(blake2s_init_key)
+#define blake2s_update BLAKE2_IMPL_NAME(blake2s_update)
+#define blake2s_final BLAKE2_IMPL_NAME(blake2s_final)
+#define blake2s BLAKE2_IMPL_NAME(blake2s)
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ int blake2s_init( blake2s_state *S, size_t outlen );
+ int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+ int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen );
+ int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen );
+ int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen );
+#if defined(__cplusplus)
+}
+#endif
+
+
+/* init2 xors IV with input parameter block */
+int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+ uint8_t *p, *h, *v;
+ //blake2s_init0( S );
+ v = ( uint8_t * )( blake2s_IV );
+ h = ( uint8_t * )( S->h );
+ p = ( uint8_t * )( P );
+ /* IV XOR ParamBlock */
+ memset( S, 0, sizeof( blake2s_state ) );
+
+ for( int i = 0; i < BLAKE2S_OUTBYTES; ++i ) h[i] = v[i] ^ p[i];
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+/* Some sort of default parameter block initialization, for sequential blake2s */
+int blake2s_init( blake2s_state *S, size_t outlen )
+{
+ if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+ const blake2s_param P =
+ {
+ outlen,
+ 0,
+ 1,
+ 1,
+ 0,
+ {0},
+ 0,
+ 0,
+ {0},
+ {0}
+ };
+ return blake2s_init_param( S, &P );
+}
+
+
+int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+ if ( ( !key ) || ( !keylen ) || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ const blake2s_param P =
+ {
+ outlen,
+ keylen,
+ 1,
+ 1,
+ 0,
+ {0},
+ 0,
+ 0,
+ {0},
+ {0}
+ };
+
+ if( blake2s_init_param( S, &P ) < 0 )
+ return -1;
+
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+ blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
+ secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+
+static inline int blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] )
+{
+ __m128i row1, row2, row3, row4;
+ __m128i buf1, buf2, buf3, buf4;
+#if defined(HAVE_SSE4_1)
+ __m128i t0, t1;
+#if !defined(HAVE_XOP)
+ __m128i t2;
+#endif
+#endif
+ __m128i ff0, ff1;
+#if defined(HAVE_SSSE3) && !defined(HAVE_XOP)
+ const __m128i r8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
+ const __m128i r16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
+#endif
+#if defined(HAVE_SSE4_1)
+ const __m128i m0 = LOADU( block + 00 );
+ const __m128i m1 = LOADU( block + 16 );
+ const __m128i m2 = LOADU( block + 32 );
+ const __m128i m3 = LOADU( block + 48 );
+#else
+ const uint32_t m0 = ( ( uint32_t * )block )[ 0];
+ const uint32_t m1 = ( ( uint32_t * )block )[ 1];
+ const uint32_t m2 = ( ( uint32_t * )block )[ 2];
+ const uint32_t m3 = ( ( uint32_t * )block )[ 3];
+ const uint32_t m4 = ( ( uint32_t * )block )[ 4];
+ const uint32_t m5 = ( ( uint32_t * )block )[ 5];
+ const uint32_t m6 = ( ( uint32_t * )block )[ 6];
+ const uint32_t m7 = ( ( uint32_t * )block )[ 7];
+ const uint32_t m8 = ( ( uint32_t * )block )[ 8];
+ const uint32_t m9 = ( ( uint32_t * )block )[ 9];
+ const uint32_t m10 = ( ( uint32_t * )block )[10];
+ const uint32_t m11 = ( ( uint32_t * )block )[11];
+ const uint32_t m12 = ( ( uint32_t * )block )[12];
+ const uint32_t m13 = ( ( uint32_t * )block )[13];
+ const uint32_t m14 = ( ( uint32_t * )block )[14];
+ const uint32_t m15 = ( ( uint32_t * )block )[15];
+#endif
+ row1 = ff0 = LOADU( &S->h[0] );
+ row2 = ff1 = LOADU( &S->h[4] );
+ row3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
+ row4 = _mm_xor_si128( _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ), LOADU( &S->t[0] ) );
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+ STOREU( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row1, row3 ) ) );
+ STOREU( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row2, row4 ) ) );
+ return 0;
+}
+
+
+int blake2s_update( blake2s_state *S, const uint8_t *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ size_t left = S->buflen;
+ size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf ); // Compress
+ memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else /* inlen <= fill */
+ {
+ memcpy( S->buf + left, in, inlen );
+ S->buflen += inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen -= inlen;
+ }
+ }
+
+ return 0;
+}
+
+
+int blake2s_final( blake2s_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t buffer[BLAKE2S_OUTBYTES];
+
+ if(outlen != S->outlen ) return -1;
+
+ if( S->buflen > BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf );
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ memmove( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
+ }
+
+ blake2s_increment_counter( S, ( uint32_t )S->buflen );
+ blake2s_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2s_compress( S, S->buf );
+
+ for( int i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+ store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+ memcpy( out, buffer, outlen );
+ return 0;
+}
+
+int blake2s( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ blake2s_state S[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if ( NULL == key && keylen > 0) return -1;
+
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ if( keylen > 0 )
+ {
+ if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+ }
+ else
+ {
+ if( blake2s_init( S, outlen ) < 0 ) return -1;
+ }
+
+ if( blake2s_update( S, ( uint8_t * )in, inlen ) < 0) return -1;
+ return blake2s_final( S, out, outlen );
+}
+
+#if defined(SUPERCOP)
+int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
+{
+ return blake2s( out, in, NULL, BLAKE2S_OUTBYTES, (size_t)inlen, 0 );
+}
+#endif
+
diff --git a/contrib/libs/blake2/src/blake2sp.c b/contrib/libs/blake2/src/blake2sp.c
new file mode 100644
index 0000000000..2f32bf3a22
--- /dev/null
+++ b/contrib/libs/blake2/src/blake2sp.c
@@ -0,0 +1,274 @@
+/*
+ BLAKE2 reference source code package - optimized C implementations
+
+ Written in 2012 by Samuel Neves <sneves@dei.uc.pt>
+
+ 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/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+#include "blake2.h"
+#include "blake2-impl.h"
+
+#define PARALLELISM_DEGREE 8
+
+static int blake2sp_init_leaf( blake2s_state *S, uint8_t outlen, uint8_t keylen, uint64_t offset )
+{
+ blake2s_param P[1];
+ P->digest_length = outlen;
+ P->key_length = keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ P->leaf_length = 0;
+ store48( P->node_offset, offset );
+ P->node_depth = 0;
+ P->inner_length = BLAKE2S_OUTBYTES;
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ blake2s_init_param( S, P );
+ S->outlen = P->inner_length;
+ return 0;
+}
+
+static int blake2sp_init_root( blake2s_state *S, uint8_t outlen, uint8_t keylen )
+{
+ blake2s_param P[1];
+ P->digest_length = outlen;
+ P->key_length = keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ P->leaf_length = 0;
+ store48( P->node_offset, 0ULL );
+ P->node_depth = 1;
+ P->inner_length = BLAKE2S_OUTBYTES;
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ blake2s_init_param( S, P );
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+int blake2sp_init( blake2sp_state *S, size_t outlen )
+{
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ if( blake2sp_init_root( S->R, ( uint8_t ) outlen, 0 ) < 0 )
+ return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S->S[i], ( uint8_t ) outlen, 0, i ) < 0 ) return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ S->outlen = ( uint8_t ) outlen;
+ return 0;
+}
+
+int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ if( blake2sp_init_root( S->R, ( uint8_t ) outlen, ( uint8_t ) keylen ) < 0 )
+ return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S->S[i], ( uint8_t ) outlen, ( uint8_t ) keylen, i ) < 0 )
+ return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ S->outlen = ( uint8_t ) outlen;
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
+
+ secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+
+int blake2sp_update( blake2sp_state *S, const uint8_t *in, size_t inlen )
+{
+ size_t left = S->buflen;
+ size_t fill = sizeof( S->buf ) - left;
+
+ if( left && inlen >= fill )
+ {
+ memcpy( S->buf + left, in, fill );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
+
+ in += fill;
+ inlen -= fill;
+ left = 0;
+ }
+
+#if defined(_OPENMP)
+ omp_set_num_threads(PARALLELISM_DEGREE);
+ #pragma omp parallel shared(S)
+#else
+ for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t id__ = ( size_t ) omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const uint8_t *in__ = ( const uint8_t * )in;
+ in__ += id__ * BLAKE2S_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_update( S->S[id__], in__, BLAKE2S_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ }
+ }
+
+ in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
+ inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+
+ if( inlen > 0 )
+ memcpy( S->buf + left, in, inlen );
+
+ S->buflen = ( uint32_t ) left + ( uint32_t ) inlen;
+ return 0;
+}
+
+
+int blake2sp_final( blake2sp_state *S, uint8_t *out, size_t outlen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+
+ if(S->outlen != outlen) return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ {
+ if( S->buflen > i * BLAKE2S_BLOCKBYTES )
+ {
+ size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
+
+ if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
+
+ blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
+ }
+
+ blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
+ }
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
+
+ blake2s_final( S->R, out, outlen );
+ return 0;
+}
+
+
+int blake2sp( uint8_t *out, const void *in, const void *key, size_t outlen, size_t inlen, size_t keylen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+ blake2s_state S[PARALLELISM_DEGREE][1];
+ blake2s_state FS[1];
+
+ /* Verify parameters */
+ if ( NULL == in && inlen > 0 ) return -1;
+
+ if ( NULL == out ) return -1;
+
+ if ( NULL == key && keylen > 0 ) return -1;
+
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S[i], ( uint8_t ) outlen, ( uint8_t ) keylen, i ) < 0 )
+ return -1;
+
+ S[PARALLELISM_DEGREE - 1]->last_node = 1; // mark last node
+
+ if( keylen > 0 )
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
+
+ secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+ }
+
+#if defined(_OPENMP)
+ omp_set_num_threads(PARALLELISM_DEGREE);
+ #pragma omp parallel shared(S,hash)
+#else
+
+ for( size_t id__ = 0; id__ < PARALLELISM_DEGREE; ++id__ )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t id__ = ( size_t ) omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const uint8_t *in__ = ( const uint8_t * )in;
+ in__ += id__ * BLAKE2S_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_update( S[id__], in__, BLAKE2S_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ }
+
+ if( inlen__ > id__ * BLAKE2S_BLOCKBYTES )
+ {
+ const size_t left = inlen__ - id__ * BLAKE2S_BLOCKBYTES;
+ const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
+ blake2s_update( S[id__], in__, len );
+ }
+
+ blake2s_final( S[id__], hash[id__], BLAKE2S_OUTBYTES );
+ }
+
+ if( blake2sp_init_root( FS, ( uint8_t ) outlen, ( uint8_t ) keylen ) < 0 )
+ return -1;
+
+ FS->last_node = 1;
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
+
+ return blake2s_final( FS, out, outlen );
+}
+
+
+
+
diff --git a/contrib/libs/blake2/src/config.h b/contrib/libs/blake2/src/config.h
new file mode 100644
index 0000000000..fd863ba01a
--- /dev/null
+++ b/contrib/libs/blake2/src/config.h
@@ -0,0 +1,157 @@
+/* src/config.h. Generated from config.h.in by configure. */
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Support Altivec instructions */
+/* #undef HAVE_ALTIVEC */
+
+/* Support AVX (Advanced Vector Extensions) instructions */
+/* #undef HAVE_AVX */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `explicit_bzero' function. */
+#define HAVE_EXPLICIT_BZERO 1
+
+/* Define to 1 if you have the `explicit_memset' function. */
+/* #undef HAVE_EXPLICIT_MEMSET */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `memset_s' function. */
+/* #undef HAVE_MEMSET_S */
+
+/* Support mmx instructions */
+/* #undef HAVE_MMX */
+
+/* Support SSE (Streaming SIMD Extensions) instructions */
+/* #undef HAVE_SSE */
+
+/* Support SSE2 (Streaming SIMD Extensions 2) instructions */
+/* #undef HAVE_SSE2 */
+
+/* Support SSE3 (Streaming SIMD Extensions 3) instructions */
+/* #undef HAVE_SSE3 */
+
+/* Support SSSE4.1 (Streaming SIMD Extensions 4.1) instructions */
+/* #undef HAVE_SSE4_1 */
+
+/* Support SSSE4.2 (Streaming SIMD Extensions 4.2) instructions */
+/* #undef HAVE_SSE4_2 */
+
+/* Support SSSE3 (Supplemental Streaming SIMD Extensions 3) instructions */
+/* #undef HAVE_SSSE3 */
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* machine is little-endian */
+#define NATIVE_LITTLE_ENDIAN 1
+
+/* Name of package */
+#define PACKAGE "libb2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "contact@blake2.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libb2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libb2 0.98.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libb2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "https://blake2.net"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.98.1"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "0.98.1"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
diff --git a/contrib/libs/blake2/ya.make b/contrib/libs/blake2/ya.make
new file mode 100644
index 0000000000..82158e6ab3
--- /dev/null
+++ b/contrib/libs/blake2/ya.make
@@ -0,0 +1,49 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+LICENSE(CC0-1.0)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(0.98.1)
+
+ORIGINAL_SOURCE(https://github.com/BLAKE2/libb2/archive/v0.98.1.tar.gz)
+
+ADDINCL(
+ contrib/libs/blake2/src
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DHAVE_CONFIG_H
+ -DSUFFIX=
+)
+
+SRCS(
+ src/blake2-dispatch.c
+ src/blake2bp.c
+ src/blake2sp.c
+)
+
+SRC(src/blake2b-ref.c -DSUFFIX=_ref)
+
+SRC(src/blake2s-ref.c -DSUFFIX=_ref)
+
+IF (ARCH_X86_64)
+ SRC_C_AVX(src/blake2b.c -DSUFFIX=_avx)
+ SRC_C_SSE2(src/blake2b.c -DSUFFIX=_sse2)
+ SRC_C_SSE41(src/blake2b.c -DSUFFIX=_sse41)
+ SRC_C_SSSE3(src/blake2b.c -DSUFFIX=_ssse3)
+ SRC_C_XOP(src/blake2b.c -DSUFFIX=_xop)
+ SRC_C_AVX(src/blake2s.c -DSUFFIX=_avx)
+ SRC_C_SSE2(src/blake2s.c -DSUFFIX=_sse2)
+ SRC_C_SSE41(src/blake2s.c -DSUFFIX=_sse41)
+ SRC_C_SSSE3(src/blake2s.c -DSUFFIX=_ssse3)
+ SRC_C_XOP(src/blake2s.c -DSUFFIX=_xop)
+ENDIF()
+
+END()
diff --git a/contrib/libs/libarchive/CONTRIBUTING.md b/contrib/libs/libarchive/CONTRIBUTING.md
new file mode 100644
index 0000000000..9ccc45c3d1
--- /dev/null
+++ b/contrib/libs/libarchive/CONTRIBUTING.md
@@ -0,0 +1,98 @@
+Thank you for helping us improve libarchive.
+The following guidelines will help ensure your contribution gets prompt attention.
+
+# Bugs and other Issues
+
+If you encounter any problems with libarchive,
+[please file an issue on our issue tracker](https://github.com/libarchive/libarchive/issues).
+
+All bug reports should include the following information. You can copy the text below directly into the issue tracker to get started:
+
+```
+Basic Information
+ Version of libarchive:
+ How you obtained it: (build from source, pre-packaged binary, etc)
+ Operating system and version:
+ What compiler and/or IDE you are using (include version):
+
+If you are using a pre-packaged binary
+ Exact package name and version:
+ Repository you obtained it from:
+
+Description of the problem you are seeing:
+ What did you do?
+ What did you expect to happen?
+ What actually happened?
+ What log files or error messages were produced?
+
+How the libarchive developers can reproduce your problem:
+ What other software was involved?
+ What other files were involved?
+ How can we obtain any of the above?
+```
+
+Depending on the specific type of issue, other information will be helpful:
+
+## Test Failures
+
+If you see any test failures, please include the information above and also:
+
+* Names of the tests that failed.
+
+* Look for the .log files in the /tmp/libarchive_test_*date-and-time* directories. (On Mac OS, look in $TMPDIR which is different than /tmp.)
+
+Please paste the .log files you will find there directly into your report.
+
+
+## Problems using libarchive in a program
+
+If you are trying to write a program using libarchive, please include the information above and also:
+
+* It will help us if we can actually run the program. This is easiest if you can provide source to a short program that illustrates your problem.
+
+* If you have a sufficiently short program that shows the problem, you can either paste it into the report or [put it into a gist](https://gist.github.com).
+
+
+## Libarchive produced incorrect output
+
+Please tell us what program you ran, any command-line arguments you provided, and details of the input files (`ls -l` output is helpful here). If the problem involved a command-line program, please copy the full terminal text into the report, including the command line and any error messages.
+
+Please try to make the output file available to us. Unless it is very large, you can upload it into a fresh github repository and provide a link in your issue report.
+
+
+## Libarchive could not read a particular input file
+
+Note: If you can provide a **very small** input file that reproduces the problem, we can add that to our test suite. This will ensure that the bug does not reappear in the future.
+
+A link to the relevant file is usually sufficient.
+
+If you cannot provide the input file or a link to the file, please let us know if there is some other way to obtain it.
+
+
+## Documentation improvements
+
+We are always interested in improving the libarchive documentation. Please tell us about any errors you find, including:
+
+* Typos or errors in the manpages provided with libarchive source.
+
+* Mistakes in the [libarchive Wiki](https://github.com/libarchive/libarchive/wiki)
+
+* Problems with the PDF or Wiki files that are automatically generated from the manpages.
+
+
+# Code Submissions
+
+We welcome all code submissions. But of course, some code submissions are easier for us to respond to than others. The best code submissions:
+
+* Address a single issue. There have been many cases where a simple fix to an obvious problem did not get handled for months because the patch that was provided also included an unrelated change affecting an especially complex area of the code.
+
+* Follow existing libarchive code style and conventions. Libarchive generally follows [BSD KNF](https://www.freebsd.org/cgi/man.cgi?query=style&sektion=9) for formatting code.
+
+* Do not make unnecessary changes to existing whitespace, capitalization, or spelling.
+
+* Include detailed instructions for reproducing the problem you're fixing. We do try to verify that a submission actually fixes a real problem. If we can't reproduce the problem, it will take us longer to evaluate the fix. For this reason, we encourage you to file an issue report first with details on reproducing the problem, then refer to that issue in your pull request.
+
+* Includes a test case. The libarchive Wiki has [detailed documentation for adding new test cases](https://github.com/libarchive/libarchive/wiki/LibarchiveAddingTest).
+
+* Are provided via Github pull requests. We welcome patches in almost any format, but github's pull request management makes it significantly easier for us to evaluate and test changes.
+
diff --git a/contrib/libs/libarchive/COPYING b/contrib/libs/libarchive/COPYING
new file mode 100644
index 0000000000..1b9723574a
--- /dev/null
+++ b/contrib/libs/libarchive/COPYING
@@ -0,0 +1,65 @@
+The libarchive distribution as a whole is Copyright by Tim Kientzle
+and is subject to the copyright notice reproduced at the bottom of
+this file.
+
+Each individual file in this distribution should have a clear
+copyright/licensing statement at the beginning of the file. If any do
+not, please let me know and I will rectify it. The following is
+intended to summarize the copyright status of the individual files;
+the actual statements in the files are controlling.
+
+* Except as listed below, all C sources (including .c and .h files)
+ and documentation files are subject to the copyright notice reproduced
+ at the bottom of this file.
+
+* The following source files are also subject in whole or in part to
+ a 3-clause UC Regents copyright; please read the individual source
+ files for details:
+ libarchive/archive_read_support_filter_compress.c
+ libarchive/archive_write_add_filter_compress.c
+ libarchive/mtree.5
+
+* The following source files are in the public domain:
+ libarchive/archive_getdate.c
+
+* The following source files are triple-licensed with the ability to choose
+ from CC0 1.0 Universal, OpenSSL or Apache 2.0 licenses:
+ libarchive/archive_blake2.h
+ libarchive/archive_blake2_impl.h
+ libarchive/archive_blake2s_ref.c
+ libarchive/archive_blake2sp_ref.c
+
+* The build files---including Makefiles, configure scripts,
+ and auxiliary scripts used as part of the compile process---have
+ widely varying licensing terms. Please check individual files before
+ distributing them to see if those restrictions apply to you.
+
+I intend for all new source code to use the license below and hope over
+time to replace code with other licenses with new implementations that
+do use the license below. The varying licensing of the build scripts
+seems to be an unavoidable mess.
+
+
+Copyright (c) 2003-2018 <author(s)>
+All rights reserved.
+
+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
+ in this position and unchanged.
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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/contrib/libs/libarchive/INSTALL b/contrib/libs/libarchive/INSTALL
new file mode 100644
index 0000000000..2fafbd5046
--- /dev/null
+++ b/contrib/libs/libarchive/INSTALL
@@ -0,0 +1,35 @@
+More complete build documentation is available on the libarchive
+Wiki: https://github.com/libarchive/libarchive/wiki
+
+On most Unix-like systems, you should be able to install libarchive,
+bsdtar, and bsdcpio using the following common steps:
+ ./configure
+ make
+ make install
+
+If you need to customize the target directories or otherwise adjust
+the build setting, use
+ ./configure --help
+to list the configure options.
+
+If you are developing libarchive and need to update the
+configure script and other build files:
+ /bin/sh build/autogen.sh
+
+To create a distribution, please use the 'distcheck' target:
+ /bin/sh build/autogen.sh && ./configure && make distcheck
+
+On Unix-like and non-Unix-like systems, use the "cmake" utility (available from
+http://cmake.org/) to generate suitable build files for your platform.
+Cmake requires the name of the directory containing CmakeLists.txt and
+the "generator" to use for your build environment. For example, to
+build with Xcode on Mac OS, you can use the following command:
+ cmake -G "Xcode" ~/libarchive-download-dir/
+The result will be appropriate makefiles, solution files, or project
+files that can be used with the corresponding development tool.
+The default on Unix-like systems is to generate Makefiles, so you
+can also use cmake instead of the configure script:
+ cmake ~/libarchive-download-dir/
+ make
+ make install
+See the libarchive Wiki or the cmake site for further documentation.
diff --git a/contrib/libs/libarchive/NEWS b/contrib/libs/libarchive/NEWS
new file mode 100644
index 0000000000..d3b0eb9ff7
--- /dev/null
+++ b/contrib/libs/libarchive/NEWS
@@ -0,0 +1,757 @@
+Sep 12, 2023: libarchive 3.7.2 released
+
+Jul 29, 2023: libarchive 3.7.1 released
+
+Jul 18, 2023: libarchive 3.7.0 released
+
+Jul 14, 2023: bsdunzip port from FreeBSD
+
+Dec 07, 2022: libarchive 3.6.2 released
+
+Apr 08, 2022: libarchive 3.6.1 released
+
+Feb 09, 2022: libarchive 3.6.0 released
+
+Feb 08, 2022: libarchive 3.5.3 released
+
+Aug 22, 2021: libarchive 3.5.2 released
+
+Dec 26, 2020: libarchive 3.5.1 released
+
+Dec 01, 2020: libarchive 3.5.0 released
+
+Oct 14, 2020: Support for system extended attributes
+
+May 20, 2020: libarchive 3.4.3 released
+
+Apr 30, 2020: Support for pzstd compressed files
+
+Apr 16, 2020: Support for RHT.security.selinux tar extended attribute
+
+Feb 11, 2020: libarchive 3.4.2 released
+
+Jan 23, 2020: Important fixes for writing XAR archives
+
+Jan 20, 2020: New tar option: --safe-writes (atomical file extraction)
+
+Jan 03, 2020: Support mbed TLS (PolarSSL) as optional crypto provider
+
+Dec 30, 2019: libarchive 3.4.1 released
+
+Dec 11, 2019: New pax write option "xattrhdr"
+
+Nov 17, 2019: Unicode filename support for reading lha/lzh archives
+
+Jun 11, 2019: libarchive 3.4.0 released
+
+May 18, 2019: Fixes for reading Android APK and JAR archives
+
+Apr 16, 2019: Support for non-recursive list and extract
+
+Apr 14, 2019: New tar option: --exclude-vcs
+
+Mar 27, 2019: Support for file and directory symlinks on Windows
+
+Mar 12, 2019: Important fixes for storing file attributes and flags
+
+Jan 20, 2019: Support for xz, lzma, ppmd8 and bzip2 decompression in ZIP files
+
+Oct 06, 2018: RAR 5.0 reader
+
+Sep 03, 2018: libarchive 3.3.3 released
+
+Jul 19, 2018: Avoid super-linear slowdown on malformed mtree files
+
+Jan 27, 2018: Many fixes for building with Visual Studio
+
+Oct 19, 2017: NO_OVERWRITE doesn't change existing directory attributes
+
+Aug 12, 2017: New support for Zstandard read and write filters
+
+Jul 09, 2017: libarchive 3.3.2 released
+
+Mar 16, 2017: NFSv4 ACL support for Linux (librichacl)
+
+Feb 26, 2017: libarchive 3.3.1 released
+ Security & Feature release
+
+Feb 19, 2017: libarchive 3.3.0 released
+ Security & Feature release
+
+Jan 29, 2017: Limited NFSv4 ACL support for Mac OS (Darwin)
+
+Jan 10, 2017: POSIX.1e and NFSv4 ACL support for Solaris and derivates
+
+Dec 27, 2016: NFSv4 ACL read and write support for pax
+ Deprecated functions: archive_entry_acl_text(), archive_entry_acl_text_w()
+
+Nov, 2016: libarchive is now being tested by the OSS-Fuzz project
+
+Oct 26, 2016: Remove liblzmadec support
+
+Oct 23, 2016: libarchive 3.2.2 released
+ Security release
+
+Jun 20, 2016: libarchive 3.2.1 released
+ This fixes a handful of security and other critical issues with 3.2.0
+
+May 01, 2016: libarchive 3.2.0 released
+
+Apr 09, 2016: libarchive 3.1.901a released
+ Another test release in preparation for 3.2.0
+
+Feb 13, 2016: libarchive 3.1.900a released
+ This is a test release in preparation for 3.2.0
+
+Oct 21, 2015: Preliminary port to OSF
+
+Apr 11, 2015: libarchive's issue tracker is now hosted at GitHub.
+ https://github.com/libarchive/libarchive/issues
+
+Early 2015: Many fixes to crash and overflow bugs thanks to Hanno Boeck
+
+Oct 13, 2014: Zip encryption and decryption support
+
+Aug 13, 2014: Add support for lz4 compression.
+
+Jun 10, 2014: Add warc format support
+
+May 3, 2014: Add experimental Zip streaming extension
+
+Apr 6, 2014: Add bsdcat command-line tool
+
+Jan 12, 2014: Add Zip64 support
+
+Dec 1, 2013: Rewrite Zip write logic
+
+Jul 1, 2013: Add ability to detect encrypted entries for many formats
+ (This does not add the ability to *decrypt* those entries, however)
+
+Feb 23, 2013: "raw" write support added
+
+Feb 09, 2013: libarchive 3.1.2 released
+
+Jan 28, 2013: libarchive's new website moved to http://www.libarchive.org.
+
+Jan 13, 2013: libarchive 3.1.1 released
+
+Jan 13, 2013: libarchive 3.1.0 released
+
+Dec 07, 2012: Implement functions to manually set the format and filters used.
+
+Nov 11, 2012: Add support for __MACOSX directory in Zip archives, which resource
+ forks are stored in.
+
+Oct 20, 2012: Add support for writing v7 tar format.
+
+Oct 09, 2012: Add support for grzip compression.
+
+Oct 07, 2012: Introduce b64encode filter.
+Oct 07, 2012: Introduce uuencode filter.
+
+Oct 06, 2012: Add support for lzop.
+
+Sep 27, 2012: Implement function used to seek within data blocks.
+ (Currently only supported for uncompressed RAR archives).
+
+Apr 22, 2012: Add basic archive read and write filter support for lrzip.
+
+Mar 27, 2012: libarchive 3.0.4 released
+
+Feb 05, 2012: libarchive development now hosted at GitHub.
+ http://libarchive.github.com/
+Feb 05, 2012: libarchive's issue tracker remains at Google Code.
+ http://code.google.com/p/libarchive/issues/list
+Feb 05, 2012: libarchive's mailing lists remain at Google Groups.
+
+Dec 24, 2011: libarchive 3.0.2 released
+Dec 23, 2011: Various fixes merged from FreeBSD
+Dec 23, 2011: Symlink support in Zip reader and writer
+Dec 23, 2011: Robustness fixes to 7Zip reader
+
+Nov 27, 2011: libarchive 3.0.1b released
+
+Nov 26, 2011: 7Zip reader
+Nov 26, 2011: Small fixes to ISO and Zip to improve robustness with corrupted input
+Nov 24, 2011: Improve streaming Zip reader's support for uncompressed entries
+Nov 20, 2011: New seeking Zip reader supports SFX Zip archives
+Nov 20, 2011: Build fixes on Windows
+
+Nov 13, 2011: libarchive 3.0.0a released
+
+Nov 06, 2011: Update shared-library version calculations for libarchive 3.x
+Sep 04, 2011: Fix tar -s; follow GNU tar for controlling hardlink/symlink substitutions
+Aug 18, 2011: Fix reading ISO images built by NetBSD's mkisofs
+Aug 15, 2011: Old archive_read_support_compression_XXX functions are deprecated and
+ will disappear in libarchive 4.0.
+Jun 26, 2011: RAR reader
+Jun 16, 2011: Add tar:compat-2x option to emulate broken libarchive 2.x
+ handling of pax UTF-8 headers
+Apr 25, 2011: Refactor read_open() into a collection of single-item setters;
+ support the old interfaces as wrappers
+Apr 12, 2011: Split disk writer into separate POSIX and Windows implementations
+Apr 10, 2011: Improvements to character translations on Windows.
+Mar 30, 2011: More work to return errors instead of calling abort()
+Mar 23, 2011: Add charset option to many writers to control MBCS filenames
+Mar 17, 2011: Overhauled support for per-format extension options
+Mar 17, 2011: Track character set used for mbcs strings, support
+ translating to/from user-specified locale
+Mar 09, 2011: Recognize mtree files without requiring a signature
+Mar 06, 2011: Use iconv to convert to/from Unicode instead of making bad
+ assumptions about the C90 character set translation functions
+Feb 17, 2011: Fixes for AIX, TRU64, and other platforms
+Dec 22, 2010: CAB reader
+Dec 20, 2010: LHA/LZH reader
+Jul 03, 2010: minitar example demonstrates archive_read_disk directory traversal
+Jun 29, 2010: Many improvements to ISO reader compatibility
+Jun 26, 2010: Use larger buffers when copy files into an archive
+Jun 18, 2010: Reimplement Mac OS extensions in libarchive
+Jun 09, 2010: archive_read_disk now supports traversals
+May 28, 2010: XAR writer
+May 16, 2010: Fix ^T handling; don't exit on interrupted reads and writes
+May 09, 2010: Improved detection of platform-specific crypto support
+May 04, 2010: lzip read and write filters
+May 01, 2010: New options: tar --gid --gname --uid --uname
+Apr 28, 2010: Use Red-black tree for ISO reader/writer to improve performance
+Apr 17, 2010: Minimal writer for legacy GNU tar format
+Mar 12, 2010: Don't dereference symlinks on Linux when reading ACLs.
+Mar 06, 2010: Fix build when an older libarchive is already installed
+Feb 28, 2010: Relax handling of state failures; misuse by clients now generally
+ results in a sticky ARCHIVE_FATAL rather than a visit to abort()
+Feb 25, 2010: ISO writer
+Feb 21, 2010: Split many man pages into smaller chunks.
+Feb 21, 2010: Performance: Cheat on block sizes when reading archives from disk.
+Feb 21, 2010: Use int64_t instead of off_t, dev_t, ino_t, uid_t, and gid_t
+Feb 20, 2010: Document new ACL functions.
+Feb 19, 2010: Support multiple write filters
+Feb 07, 2010: Remove some legacy libarchive 1.x APIs
+Feb 04, 2010: Read afio headers
+Feb 02, 2010: Archive sparse files compatibly with GNU tar
+Feb 01, 2010: Integrate Apple extensions for Mac OS extended attributes into bsdtar
+Jan 31, 2010: Support cpio -V
+
+Feb 04, 2010: libarchive 2.8.0 released
+Jan 17, 2010: Fix error handling for 'echo nonexistent | cpio -o'
+Jan 17, 2010: Don't use futimes() on Cygwin
+
+Jan 02, 2010: libarchive 2.7.902a released (test release for 2.8)
+Jan 02, 2010: Fix tar/test/test_windows on MinGW
+Jan 02, 2010: Fix memory leaks in libarchive tests
+Jan 01, 2010: Fix memory leak when filter startup fails
+
+Dec 27, 2009: libarchive 2.7.901a released (test release for 2.8)
+
+Aug 04, 2009: libarchive 2.7.1 released
+Jul 20, 2009: Suppress bogus warning about unxz
+Jul 19, 2009: Support Cygwin 1.7
+Jun 11, 2009: Support lzma/xz files compressed with larger buffer sizes.
+May 24, 2009: Handle gzip files signed with OpenBSD "gzsig" program.
+May 07, 2009: Avoid false failures when reading from pipe.
+
+Apr 16, 2009: libarchive 2.7.0 released
+
+Apr 10, 2009: libarchive 2.6.992a released
+Apr 09, 2009: Fix SIGPIPE issue building with MSVC.
+Apr 09, 2009: Fix several minor memory leaks in libarchive and libarchive_test
+
+Apr 08, 2009: libarchive 2.6.991a released
+Apr 07, 2009: Additional tests added to bsdcpio_test
+
+Apr 01, 2009: libarchive 2.6.990a released
+Apr 01, 2009: Use command-line gunzip, bunzip2, unxz, unlzma for
+ decompression if the library is built without suitable
+ libraries. The setup functions return ARCHIVE_WARN
+ in this case so clients can adapt if necessary.
+Apr 01, 2009: Use getpw*_r and getgr*_r functions for thread-safety.
+Mar 24, 2009: Add archive_read_next_header2(), which is up to 25%
+ more efficient for some clients; from Brian Harring.
+Mar 22, 2009: PDF versions of manpages are now included in the distribution.
+Mar, 2009: Major work to improve Cygwin build by Charles Wilson.
+Feb/Mar, 2009: Major work on cmake build support, mostly by Michihiro NAKAJIMA.
+Feb/Mar, 2009: Major work on Visual Studio support by Michihiro NAKAJIMA.
+ All tests now pass.
+Feb 25, 2009: Fix Debian Bug #516577
+Feb 21, 2009: Yacc is no longer needed to build; date parser rewritten in C.
+Jan/Feb, 2009: Mtree work by Michihiro.
+Feb, 2009: Joliet support by Andreas Henriksson.
+Jan/Feb, 2009: New options framework by Michihiro.
+Feb, 2009: High-res timestamps on Tru64, AIX, and GNU Hurd, by Björn Jacke.
+Jan 18, 2009: Extended attributes work on FreeBSD and Linux now with pax format.
+Jan 07, 2009: New archive_read_disk_entry_from_file() knows about ACLs,
+ extended attributes, etc so that bsdtar and bsdcpio don't require
+ such system-specific knowledge.
+Jan 03, 2009: Read filter system extensively refactored. In particular,
+ read filter pipelines are now built out automatically and individual
+ filters should be much easier to implement. Documentation on the
+ Googlecode Wiki explains how to implement new filters.
+Dec 28, 2008: Many Windows/Visual Studio fixes from Michihiro NAKAJIMA.
+
+Dec 28, 2008: Main libarchive development moved from FreeBSD Perforce
+ server to Google Code. This should make it easier for more
+ people to participate in libarchive development.
+
+Dec 28, 2008: libarchive 2.6.0 released
+Dec 25, 2008: libarchive 2.5.905a released
+Dec 10, 2008: libarchive 2.5.904a released
+Dec 04, 2008: libarchive 2.5.903a released
+Nov 09, 2008: libarchive 2.5.902a released
+Nov 08, 2008: libarchive 2.5.901a released
+Nov 08, 2008: Start of pre-release testing for libarchive 2.6
+
+Nov 07, 2008: Read filter refactor: The decompression routines just
+ consume and produce arbitrarily-sized blocks. The reblocking
+ from read_support_compression_none() has been pulled into the
+ read core. Also, the decompression bid now makes multiple
+ passes and stacks read filters.
+Oct 21, 2008: bsdcpio: New command-line parser.
+Oct 19, 2008: Internal read_ahead change: short reads are now an error
+Oct 06, 2008: bsdtar: option parser no longer uses getopt_long(),
+ gives consistent option parsing on all platforms.
+Sep 19, 2008: Jaakko Heinonen: shar utility built on libarchive
+Sep 17, 2008: Pedro Giffuni: birthtime support
+Sep 17, 2008: Miklos Vajna: lzma reader and test. Note: I still have
+ some concerns about the auto-detection (LZMA file format
+ doesn't support auto-detection well), so this is not yet
+ enabled under archive_read_support_compression_all(). For
+ now, you must call archive_read_support_compression_lzma() if
+ you want LZMA read support.
+Sep 11, 2008: Ivailo Petrov: Many fixes to Windows build, new solution files
+Jul 26, 2008: archive_entry now tracks which values have not been set.
+ This helps zip extraction (file size is often "unknown") and
+ time restores (tar usually doesn't know atime).
+Jul 26, 2008: Joerg Sonnenberger: Performance improvements to shar writer
+Jul 25, 2008: Joerg Sonnenberger: mtree write support
+
+Jul 02, 2008: libarchive 2.5.5 released
+
+Jul 02, 2008: libarchive 2.5.5b released
+Jul 01, 2008: bsdcpio is being used by enough people, we can call it 1.0.0 now
+Jun 20, 2008: bsdcpio: If a -l link fails with EXDEV, copy the file instead
+Jun 19, 2008: bsdcpio: additional long options for better GNU cpio compat
+Jun 15, 2008: Many small portability and bugfixes since 2.5.4b.
+
+May 25, 2008: libarchive 2.5.4b released
+May 21, 2008: Joerg Sonnenberger: fix bsdtar hardlink handling for newc format
+
+May 21, 2008: More progress on Windows building. Thanks to "Scott"
+ for the Windows makefiles, thanks to Kees Zeelenberg for
+ code contributions.
+
+May 21, 2008: Fix a number of non-exploitable integer and buffer overflows,
+ thanks to David Remahl at Apple for pointing these out.
+
+May 21, 2008: Colin Percival: SIGINFO or SIGUSR1 to bsdtar prints progress info
+
+May 16, 2008: bsdtar's test harness no longer depends on file ordering.
+ This was causing spurious test failures on a lot of systems.
+ Thanks to Bernhard R. Link for the diagnosis.
+
+May 14, 2008: Joerg Sonnenberger: -s substitution support for bsdtar
+
+May 13, 2008: Joerg Sonnenberger: Many mtree improvements
+
+May 11, 2008: Joerg Sonnenberger: fix hardlink extraction when
+ hardlinks have different permissions from original file
+
+April 30, 2008: Primary libarchive work has been moved into the FreeBSD
+ project's Perforce repository: http://perforce.freebsd.org/
+ The libarchive project can be browsed at
+ //depot/user/kientzle/libarchive-portable
+ Direct link: http://preview.tinyurl.com/46mdgr
+
+May 04, 2008: libarchive 2.5.3b released
+ * libarchive: Several fixes to link resolver to address bsdcpio crashes
+ * bsdcpio: -p hardlink handling fixes
+ * tar/pax: Ensure ustar dirnames end in '/'; be more careful about
+ measuring filenames when deciding what pathname fields to use
+ * libarchive: Mark which entry strings are set; be accurate about
+ distinguishing empty strings ("") from unset ones (NULL)
+ * tar: Don't crash reading entries with empty filenames
+ * libarchive_test, bsdtar_test, bsdcpio_test: Better defaults:
+ run all tests, delete temp dirs, summarize repeated failures
+ * -no-undefined to libtool for Cygwin
+ * libarchive_test: Skip large file tests on systems with 32-bit off_t
+ * iso9660: Don't bother trying to find the body of an empty file;
+ this works around strange behavior from some ISO9660 writers
+ * tar: allow -r -T to be used together
+ * tar: allow --format with -r or -u
+ * libarchive: Don't build archive.h
+
+May 04, 2008: Simplified building: archive.h is no longer constructed
+ This may require additional #if conditionals on some platforms.
+
+Mar 30, 2008: libarchive 2.5.1b released
+
+Mar 15, 2008: libarchive 2.5.0b released
+Mar 15, 2008: bsdcpio now seems to correctly write hardlinks into newc,
+ ustar, and old cpio archives. Just a little more testing before
+ bsdcpio 1.0 becomes a reality.
+Mar 15, 2008: I think the new linkify() interface is finally handling
+ all known hardlink strategies.
+Mar 15, 2008: Mtree read fixes from Joerg Sonnenberger.
+Mar 15, 2008: Many new bsdtar and bsdcpio options from Joerg Sonnenberger.
+Mar 15, 2008: test harnesses no longer require uudecode; they
+ now have built-in decoding logic that decodes the reference
+ files as they are needed.
+
+Mar 14, 2008: libarchive 2.4.14 released; identical to 2.4.13 except for
+ a point fix for gname/uname mixup in pax format that was introduced
+ with the UTF-8 fixes.
+
+Feb 26, 2008: libarchive 2.4.13 released
+Feb 25, 2008: Handle path, linkname, gname, or uname that can't be converted
+ to/from UTF-8. Implement "hdrcharset" attribute from SUS-2008.
+Feb 25, 2008: Fix name clash on NetBSD.
+Feb 18, 2008: Fix writing empty 'ar' archives, per Kai Wang
+Feb 18, 2008: [bsdtar] Permit appending on block devices.
+Feb 09, 2008: New "linkify" resolver to help with newc hardlink writing;
+ bsdcpio still needs to be converted to use this.
+Feb 02, 2008: Windows compatibility fixes from Ivailo Petrov, Kees Zeelenberg
+Jan 30, 2008: Ignore hardlink size for non-POSIX tar archives.
+
+Jan 22, 2008: libarchive 2.4.12 released
+Jan 22, 2008: Fix bad padding when writing symlinks to newc cpio archives.
+Jan 22, 2008: Verify bsdcpio_test by getting it to work against GNU cpio 2.9.
+ bsdcpio_test complains about missing options (-y and -z), format
+ of informational messages (--version, --help), and a minor formatting
+ issue in odc format output. After this update, bsdcpio_test uncovered
+ several more cosmetic issues in bsdcpio, all now fixed.
+Jan 22, 2008: Experimental support for self-extracting Zip archives.
+Jan 22, 2008: Extend hardlink restore strategy to work correctly with
+ hardlinks extracted from newc cpio files. (Which store the body
+ only with the last occurrence of a link.)
+
+Dec 30, 2007: libarchive 2.4.11 released
+Dec 30, 2007: Fixed a compile error in bsdcpio on some systems.
+
+Dec 29, 2007: libarchive 2.4.10 released
+Dec 29, 2007: bsdcpio 0.9.0 is ready for wider use.
+Dec 29, 2007: Completed initial test harness for bsdcpio.
+
+Dec 22, 2007: libarchive 2.4.9 released
+Dec 22, 2007: Implement the remaining options for bsdcpio: -a, -q, -L, -f,
+ pattern selection for -i and -it.
+
+Dec 13, 2007: libarchive 2.4.8 released
+Dec 13, 2007: gzip and bzip2 compression now handle zero-byte writes correctly,
+ Thanks to Damien Golding for bringing this to my attention.
+
+Dec 12, 2007: libarchive 2.4.7 released
+
+Dec 10, 2007: libarchive 2.4.6 released
+Dec 09, 2007: tar/test/test_copy.c verifies "tar -c | tar -x" copy pipeline
+Dec 07, 2007: Fix a couple of minor memory leaks.
+
+Dec 04, 2007: libarchive 2.4.5 released
+Dec 04, 2007: Fix cpio/test/test_write_odc by setting the umask first.
+
+Dec 03, 2007: libarchive 2.4.4 released
+Dec 03, 2007: New configure options --disable-xattr and --disable-acl,
+ thanks to Samuli Suominen.
+
+Dec 03, 2007: libarchive 2.4.3 released
+Dec 03, 2007: Thanks to Lapo Luchini for sending me a ZIP file that
+ libarchive couldn't handle. Fixed a bug in handling of
+ "length at end" flags in ZIP files.
+Dec 03, 2007: Fixed bsdcpio -help, bsdtar -help tests.
+Dec 02, 2007: First cut at real bsdtar test harness.
+
+Dec 02, 2007: libarchive 2.4.2 released
+
+Dec 02, 2007: libarchive 2.4.1 released
+Dec 02, 2007: Minor fixes, rough cut of mdoc-to-man conversion for
+ man pages.
+
+Oct 30, 2007: libarchive 2.4.0 released
+Oct 30, 2007: Minor compile fix thanks to Joerg Schilling.
+Oct 30, 2007: Only run the format auction once at the beginning of the
+ archive. This is simpler and supports better error recovery.
+Oct 29, 2007: Test support for very large entries in tar archives:
+ libarchive_test now exercises entries from 2GB up to 1TB.
+
+Oct 27, 2007: libarchive 2.3.5 released
+Oct 27, 2007: Correct some unnecessary internal data copying in the
+ "compression none" reader and writer; this reduces user time
+ by up to 2/3 in some tests. (Thanks to Jan Psota for
+ publishing his performance test results to GNU tar's bug-tar
+ mailing list; those results pointed me towards this problem.)
+Oct 27, 2007: Fix for skipping archive entries that are exactly
+ a multiple of 4G on 32-bit platforms.
+Oct 25, 2007: Fix for reading very large (>8G) tar archives; this was
+ broken when I put in support for new GNU tar sparse formats.
+Oct 20, 2007: Initial work on new pattern-matching code for cpio; I
+ hope this eventually replaces the code currently in bsdtar.
+
+Oct 08, 2007: libarchive 2.3.4 released
+Oct 05, 2007: Continuing work on bsdcpio test suite.
+Oct 05, 2007: New cpio.5 manpage, updates to "History" of bsdcpio.1 and
+ bsdtar.1 manpages.
+Oct 05, 2007: Fix zip reader to immediately return EOF if you try
+ to read body of non-regular file. In particular, this fixes
+ bsdtar extraction of zip archives.
+
+Sep 30, 2007: libarchive 2.3.3 released
+Sep 26, 2007: Rework Makefile.am so that the enable/disable options
+ actually do the right things.
+Sep 26, 2007: cpio-odc and cpio-newc archives no longer write bodies
+ for non-regular files.
+Sep 26, 2007: Test harness for bsdcpio is in place, needs more tests written.
+ This is much nicer than the ragtag collection of test scripts
+ that bsdtar has.
+
+Sep 20, 2007: libarchive 2.3.2 released
+Sep 20, 2007: libarchive 2.3.1 broke bsdtar because the archive_write_data()
+ fix was implemented incorrectly.
+
+Sep 16, 2007: libarchive 2.3.1 released
+Sep 16, 2007: Many fixes to bsdcpio 0.3: handle hardlinks with -p, recognize
+ block size on writing, fix a couple of segfaults.
+Sep 16, 2007: Fixed return value from archive_write_data() when used
+ with archive_write_disk() to match the documentation and other
+ instances of this same function.
+Sep 15, 2007: Add archive_entry_link_resolver, archive_entry_strmode
+
+Sep 11, 2007: libarchive 2.2.8 released
+Sep 09, 2007: bsdcpio 0.2 supports most (not yet all) of the old POSIX spec.
+
+Sep 01, 2007: libarchive 2.2.7 released
+Aug 31, 2007: Support for reading mtree files, including an mtree.5 manpage
+ (A little experimental still.)
+Aug 18, 2007: Read gtar 1.17 --posix --sparse entries.
+Aug 13, 2007: Refined suid/sgid restore handling; it is no longer
+ an error if suid/sgid bits are dropped when you request
+ perm restore but don't request owner restore.
+Aug 06, 2007: Use --enable-bsdcpio if you want to try bsdcpio
+
+Aug 05, 2007: libarchive 2.2.6 released
+Aug 05, 2007: New configure option --disable-bsdtar, thanks to Joerg
+ Sonnenberger.
+Aug 05, 2007: Several bug fixes from FreeBSD CVS repo.
+
+Jul 13, 2007: libarchive 2.2.5 released
+
+Jul 12, 2007: libarchive 2.2.4 released
+Jul 12, 2007: Thanks to Colin Percival's help in diagnosing and
+ fixing several critical security bugs. Details available at
+ http://security.freebsd.org/advisories/FreeBSD-SA-07:05.libarchive.asc
+
+May 26, 2007: libarchive 2.2.3 released
+May 26, 2007: Fix memory leaks in ZIP reader and shar writer, add some
+ missing system headers to archive_entry.h, dead code cleanup
+ from Colin Percival, more tests for gzip/bzip2, fix an
+ EOF anomaly in bzip2 decompression.
+
+May 12, 2007: libarchive 2.2.2 released
+May 12, 2007: Fix archive_write_disk permission restore by cloning
+ entry passed into write_header so that permission info is
+ still available at finish_entry time. (archive_read_extract()
+ worked okay because it held onto the passed-in entry, but
+ direct consumers of archive_write_disk would break). This
+ required fixing archive_entry_clone(), which now works and has
+ a reasonably complete test case.
+May 10, 2007: Skeletal cpio implementation.
+
+May 06, 2007: libarchive 2.2.1 released
+May 06, 2007: Flesh out a lot more of test_entry.c so as to catch
+ problems such as the device node breakage before releasing <sigh>.
+May 05, 2007: Fix a bad bug introduced in 2.1.9 that broke device
+ node entries in tar archives.
+May 03, 2007: Move 'struct stat' out of archive_entry core as well.
+ This removes some portability headaches and fixes a bunch
+ of corner cases that arise when manipulating archives on
+ dissimilar systems.
+
+Apr 30, 2007: libarchive 2.1.10 released
+Apr 31, 2007: Minor code cleanup.
+
+Apr 24, 2007: libarchive 2.1.9 released
+Apr 24, 2007: Fix some recently-introduced problems with libraries
+ (Just let automake handle it and it all works much better.)
+ Finish isolating major()/minor()/makedev() in archive_entry.c.
+
+Apr 23, 2007: libarchive 2.1.8 released
+Apr 23, 2007: Minor fixes found from building on MacOS X
+
+Apr 22, 2007: libarchive 2.1.7 released
+Apr 22, 2007: Eliminated all uses of 'struct stat' from the
+ format readers/writers. This should improve portability;
+ 'struct stat' is now only used in archive_entry and in
+ code that actually touches the disk.
+
+Apr 17, 2007: libarchive 2.1.6 released
+ Libarchive now compiles and passes all tests on Interix.
+
+Apr 16, 2007: libarchive 2.1.5 released
+
+Apr 15, 2007: libarchive 2.1b2 released
+Apr 15, 2007: New libarchive_internals.3 documentation of internal APIs.
+ Not complete, but should prove helpful.
+Apr 15, 2007: Experimental "read_compress_program" and "write_compress_program"
+ for using libarchive with external compression. Not yet
+ well tested, and likely has portability issues. Feedback
+ appreciated.
+
+Apr 14, 2007: libarchive 2.0.31 released
+Apr 14, 2007: More fixes for Interix, more 'ar' work
+
+Apr 14, 2007: libarchive 2.0.30 released
+Apr 13, 2007: libarchive now enforces trailing '/' on dirs
+ written to tar archives
+
+Apr 11, 2007: libarchive 2.0.29 released
+Apr 11, 2007: Make it easier to statically configure for different platforms.
+Apr 11, 2007: Updated config.guess, config.sub, libtool
+
+Apr 06, 2007: libarchive 2.0.28 released
+Apr 06, 2007: 'ar' format read/write support thanks to Kai Wang.
+
+Apr 01, 2007: libarchive 2.0.27 released
+Mar 31, 2007: Several minor fixes from Colin Percival and Joerg Sonnenberger.
+
+Mar 12, 2007: libarchive 2.0.25 released
+Mar 12, 2007: Fix broken --unlink flag.
+
+Mar 11, 2007: libarchive 2.0.24 released
+Mar 10, 2007: Correct an ACL blunder that causes any ACL with an entry
+ that refers to a non-existent user or group to not be restored correctly.
+ The fix both makes the parser more tolerant (so that archives created
+ with the buggy ACLs can be read now) and corrects the ACL formatter.
+Mar 10, 2007: More work on test portability to Linux.
+
+Mar 10, 2007: libarchive 2.0.22 released
+Mar 10, 2007: Header cleanups; added linux/fs.h, removed
+ some unnecessary headers, added #include guards in bsdtar.
+ If you see any obvious compile failures from this, let me know.
+Mar 10, 2007: Work on bsdtar test scripts: not yet robust enough
+ to enable as part of "make check", but getting better.
+Mar 10, 2007: libarchive now returns ARCHIVE_FAILED when
+ a header write fails in a way that only affects this item.
+ Less bad than ARCHIVE_FATAL, but worse than ARCHIVE_WARN.
+
+Mar 07, 2007: libarchive 2.0.21 released
+Mar 07, 2007: Add some ACL tests (only for the system-independent
+ portion of the ACL support for now).
+Mar 07, 2007: tar's ability to read ACLs off disk got
+ turned off for FreeBSD; re-enable it. (ACL restores and
+ libarchive support for storing/reading ACLs from pax
+ archives was unaffected.)
+
+Mar 02, 2007: libarchive 2.0.20 released
+Mar 2, 2007: It's not perfect, but it's pretty good.
+ Libarchive 2.0 is officially out of beta.
+
+Feb 28, 2007: libarchive 2.0b17 released
+Feb 27, 2007: Make the GID restore checks more robust by checking
+ whether the current user has too few or too many privileges.
+
+Feb 26, 2007: libarchive 2.0b15 released
+Feb 26, 2007: Don't lose symlinks when extracting from ISOs.
+ Thanks to Diego "Flameeyes" Pettenò for telling me about the
+ broken testcase on Gentoo that (finally!) led me to the cause
+ of this long-standing bug.
+
+Feb 26, 2007: libarchive 2.0b14 released
+Feb 26, 2007: Fix a broken test on platforms that lack lchmod().
+
+Feb 25, 2007: libarchive 2.0b13 released
+Feb 25, 2007: Empty archives were being written as empty files,
+ without a proper end-of-archive marker. Fixed.
+
+Feb 23, 2007: libarchive 2.0b12 released
+Feb 22, 2007: Basic security checks added: _EXTRACT_SECURE_NODOTDOT
+ and _EXTRACT_SECURE_SYMLINK. These checks used to be in bsdtar,
+ but they belong down in libarchive where they can be used by
+ other tools and where they can be better optimized.
+
+Feb 11, 2007: libarchive 2.0b11 released
+Feb 10, 2007: Fixed a bunch of errors in libarchive's handling
+ of EXTRACT_PERM and EXTRACT_OWNER, especially relating
+ to SUID and SGID bits.
+
+Jan 31, 2007: libarchive 2.0b9 released
+Jan 31, 2007: Added read support for "empty" archives as a
+ distinct archive format. Bsdtar uses this to handle, e.g.,
+ "touch foo.tar; tar -rf foo.tar"
+
+Jan 22, 2007: libarchive 2.0b6 released
+Jan 22, 2007: archive_write_disk API is now in place. It provides
+ a finer-grained interface than archive_read_extract. In particular,
+ you can use it to create objects on disk without having an archive
+ around (just feed it archive_entry objects describing what you
+ want to create), you can override the uname/gname-to-uid/gid lookups
+ (minitar uses this to avoid getpwXXX() and getgrXXX() bloat).
+
+Jan 09, 2007: libarchive 2.0a3 released
+Jan 9, 2007: archive_extract is now much better; it handles the
+ most common cases with a minimal number of system calls.
+ Some features still need a lot of testing, especially corner
+ cases involving objects that already exist on disk. I expect
+ the next round of API overhaul will simplify building test cases.
+Jan 9, 2007: a number of fixes thanks to Colin Percival, especially
+ corrections to the skip() framework and handling of large files.
+Jan 9, 2007: Fixes for large ISOs. The code should correctly handle
+ very large ISOs with entries up to 4G. Thanks to Robert Sciuk
+ for pointing out these issues.
+
+Sep 05, 2006: libarchive 1.3.1 released
+Sep 5, 2006: Bump version to 1.3 for new I/O wrappers.
+Sep 4, 2006: New memory and FILE read/write wrappers.
+Sep 4, 2006: libarchive test harness is now minimally functional;
+ it's located a few minor bugs in error-handling logic
+
+Aug 17, 2006: libarchive 1.2.54 released
+Aug 17, 2006: Outline ABI changes for libarchive 2.0; these
+ are protected behind #ifdef's until I think I've found everything
+ that needs to change.
+Aug 17, 2006: Fix error-handling in archive_read/write_close()
+ They weren't returning any errors before.
+Aug 17, 2006: Fix recursive-add logic to not trigger if it's not set
+ Fixes a bug adding files when writing archive to pipe or when
+ using archive_write_open() directly.
+Jul 2006: New "skip" handling improves performance extracting
+ single files from large uncompressed archives.
+
+Mar 21, 2006: 1.2.52 released
+Mar 21, 2006: Fix -p on platforms that don't have platform-specific
+ extended attribute code.
+Mar 20, 2006: Add NEWS file; fill in some older history from other
+ files. I'll try to keep this file up-to-date from now on.
+
+OLDER NEWS SUMMARIES
+
+Mar 19, 2006: libarchive 1.2.51 released
+Mar 18, 2006: Many fixes to extended attribute support, including a redesign
+ of the storage format to simplify debugging.
+Mar 12, 2006: Remove 'tp' support; it was a fun idea, but not worth
+ spending much time on.
+Mar 11, 2006: Incorporated Jaakko Heinonen's still-experimental support
+ for extended attributes (Currently Linux-only.).
+Mar 11, 2006: Reorganized distribution package: There is now one tar.gz
+ file that builds both libarchive and bsdtar.
+Feb 13, 2006: Minor bug fixes: correctly read cpio device entries, write
+ Pax attribute entry names.
+Nov 7, 2005: Experimental 'tp' format support in libarchive. Feedback
+ appreciated; this is not enabled by archive_read_support_format_all()
+ yet as I'm not quite content with the format detection heuristics.
+Nov 7, 2005: Some more portability improvements thanks to Darin Broady,
+ minor bugfixes.
+Oct 12, 2005: Use GNU libtool to build shared libraries on many systems.
+Aug 9, 2005: Correctly detect that MacOS X does not have POSIX ACLs.
+Apr 17, 2005: Kees Zeelenberg has ported libarchive and bsdtar to Windows:
+ http://gnuwin32.sourceforge.net/
+Apr 11, 2005: Extended Zip/Zip64 support thanks to Dan Nelson. -L/-h
+ fix from Jaakko Heinonen.
+Mar 12, 2005: archive_read_extract can now handle very long
+ pathnames (I've tested with pathnames up to 1MB).
+Mar 12, 2005: Marcus Geiger has written an article about libarchive
+ http://xsnil.antbear.org/2005/02/05/archive-mit-libarchive-verarbeiten/
+ including examples of using it from Objective-C. His MoinX
+ http://moinx.antbear.org/ desktop Wiki uses
+ libarchive for archiving and restoring Wiki pages.
+Jan 22, 2005: Preliminary ZIP extraction support,
+ new directory-walking code for bsdtar.
+Jan 16, 2005: ISO9660 extraction code added; manpage corrections.
+May 22, 2004: Many gtar-compatible long options have been added; almost
+ all FreeBSD ports extract correctly with bsdtar.
+May 18, 2004: bsdtar can read Solaris, HP-UX, Unixware, star, gtar,
+ and pdtar archives.
diff --git a/contrib/libs/libarchive/README.md b/contrib/libs/libarchive/README.md
new file mode 100644
index 0000000000..727ed49856
--- /dev/null
+++ b/contrib/libs/libarchive/README.md
@@ -0,0 +1,244 @@
+# Welcome to libarchive!
+
+The libarchive project develops a portable, efficient C library that
+can read and write streaming archives in a variety of formats. It
+also includes implementations of the common `tar`, `cpio`, and `zcat`
+command-line tools that use the libarchive library.
+
+## Questions? Issues?
+
+* https://www.libarchive.org is the home for ongoing
+ libarchive development, including documentation,
+ and links to the libarchive mailing lists.
+* To report an issue, use the issue tracker at
+ https://github.com/libarchive/libarchive/issues
+* To submit an enhancement to libarchive, please
+ submit a pull request via GitHub: https://github.com/libarchive/libarchive/pulls
+
+## Contents of the Distribution
+
+This distribution bundle includes the following major components:
+
+* **libarchive**: a library for reading and writing streaming archives
+* **tar**: the 'bsdtar' program is a full-featured 'tar' implementation built on libarchive
+* **cpio**: the 'bsdcpio' program is a different interface to essentially the same functionality
+* **cat**: the 'bsdcat' program is a simple replacement tool for zcat, bzcat, xzcat, and such
+* **unzip**: the 'bsdunzip' program is a simple replacement tool for Info-ZIP's unzip
+* **examples**: Some small example programs that you may find useful.
+* **examples/minitar**: a compact sample demonstrating use of libarchive.
+* **contrib**: Various items sent to me by third parties; please contact the authors with any questions.
+
+The top-level directory contains the following information files:
+
+* **NEWS** - highlights of recent changes
+* **COPYING** - what you can do with this
+* **INSTALL** - installation instructions
+* **README** - this file
+* **CMakeLists.txt** - input for "cmake" build tool, see INSTALL
+* **configure** - configuration script, see INSTALL for details. If your copy of the source lacks a `configure` script, you can try to construct it by running the script in `build/autogen.sh` (or use `cmake`).
+
+The following files in the top-level directory are used by the 'configure' script:
+
+* `Makefile.am`, `aclocal.m4`, `configure.ac` - used to build this distribution, only needed by maintainers
+* `Makefile.in`, `config.h.in` - templates used by configure script
+
+## Documentation
+
+In addition to the informational articles and documentation
+in the online [libarchive Wiki](https://github.com/libarchive/libarchive/wiki),
+the distribution also includes a number of manual pages:
+
+ * bsdtar.1 explains the use of the bsdtar program
+ * bsdcpio.1 explains the use of the bsdcpio program
+ * bsdcat.1 explains the use of the bsdcat program
+ * libarchive.3 gives an overview of the library as a whole
+ * archive_read.3, archive_write.3, archive_write_disk.3, and
+ archive_read_disk.3 provide detailed calling sequences for the read
+ and write APIs
+ * archive_entry.3 details the "struct archive_entry" utility class
+ * archive_internals.3 provides some insight into libarchive's
+ internal structure and operation.
+ * libarchive-formats.5 documents the file formats supported by the library
+ * cpio.5, mtree.5, and tar.5 provide detailed information about these
+ popular archive formats, including hard-to-find details about
+ modern cpio and tar variants.
+
+The manual pages above are provided in the 'doc' directory in
+a number of different formats.
+
+You should also read the copious comments in `archive.h` and the
+source code for the sample programs for more details. Please let us
+know about any errors or omissions you find.
+
+## Supported Formats
+
+Currently, the library automatically detects and reads the following formats:
+
+ * Old V7 tar archives
+ * POSIX ustar
+ * GNU tar format (including GNU long filenames, long link names, and sparse files)
+ * Solaris 9 extended tar format (including ACLs)
+ * POSIX pax interchange format
+ * POSIX octet-oriented cpio
+ * SVR4 ASCII cpio
+ * Binary cpio (big-endian or little-endian)
+ * PWB binary cpio
+ * ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions)
+ * ZIP archives (with uncompressed or "deflate" compressed entries, including support for encrypted Zip archives)
+ * ZIPX archives (with support for bzip2, ppmd8, lzma and xz compressed entries)
+ * GNU and BSD 'ar' archives
+ * 'mtree' format
+ * 7-Zip archives (including archives that use zstandard compression)
+ * Microsoft CAB format
+ * LHA and LZH archives
+ * RAR and RAR 5.0 archives (with some limitations due to RAR's proprietary status)
+ * XAR archives
+
+The library also detects and handles any of the following before evaluating the archive:
+
+ * uuencoded files
+ * files with RPM wrapper
+ * gzip compression
+ * bzip2 compression
+ * compress/LZW compression
+ * lzma, lzip, and xz compression
+ * lz4 compression
+ * lzop compression
+ * zstandard compression
+
+The library can create archives in any of the following formats:
+
+ * POSIX ustar
+ * POSIX pax interchange format
+ * "restricted" pax format, which will create ustar archives except for
+ entries that require pax extensions (for long filenames, ACLs, etc).
+ * Old GNU tar format
+ * Old V7 tar format
+ * POSIX octet-oriented cpio
+ * SVR4 "newc" cpio
+ * Binary cpio (little-endian)
+ * PWB binary cpio
+ * shar archives
+ * ZIP archives (with uncompressed or "deflate" compressed entries)
+ * GNU and BSD 'ar' archives
+ * 'mtree' format
+ * ISO9660 format
+ * 7-Zip archives
+ * XAR archives
+
+When creating archives, the result can be filtered with any of the following:
+
+ * uuencode
+ * gzip compression
+ * bzip2 compression
+ * compress/LZW compression
+ * lzma, lzip, and xz compression
+ * lz4 compression
+ * lzop compression
+ * zstandard compression
+
+## Notes about the Library Design
+
+The following notes address many of the most common
+questions we are asked about libarchive:
+
+* This is a heavily stream-oriented system. That means that
+ it is optimized to read or write the archive in a single
+ pass from beginning to end. For example, this allows
+ libarchive to process archives too large to store on disk
+ by processing them on-the-fly as they are read from or
+ written to a network or tape drive. This also makes
+ libarchive useful for tools that need to produce
+ archives on-the-fly (such as webservers that provide
+ archived contents of a users account).
+
+* In-place modification and random access to the contents
+ of an archive are not directly supported. For some formats,
+ this is not an issue: For example, tar.gz archives are not
+ designed for random access. In some other cases, libarchive
+ can re-open an archive and scan it from the beginning quickly
+ enough to provide the needed abilities even without true
+ random access. Of course, some applications do require true
+ random access; those applications should consider alternatives
+ to libarchive.
+
+* The library is designed to be extended with new compression and
+ archive formats. The only requirement is that the format be
+ readable or writable as a stream and that each archive entry be
+ independent. There are articles on the libarchive Wiki explaining
+ how to extend libarchive.
+
+* On read, compression and format are always detected automatically.
+
+* The same API is used for all formats; it should be very
+ easy for software using libarchive to transparently handle
+ any of libarchive's archiving formats.
+
+* Libarchive's automatic support for decompression can be used
+ without archiving by explicitly selecting the "raw" and "empty"
+ formats.
+
+* I've attempted to minimize static link pollution. If you don't
+ explicitly invoke a particular feature (such as support for a
+ particular compression or format), it won't get pulled in to
+ statically-linked programs. In particular, if you don't explicitly
+ enable a particular compression or decompression support, you won't
+ need to link against the corresponding compression or decompression
+ libraries. This also reduces the size of statically-linked
+ binaries in environments where that matters.
+
+* The library is generally _thread safe_ depending on the platform:
+ it does not define any global variables of its own. However, some
+ platforms do not provide fully thread-safe versions of key C library
+ functions. On those platforms, libarchive will use the non-thread-safe
+ functions. Patches to improve this are of great interest to us.
+
+* The function `archive_write_disk_header()` is _not_ thread safe on
+ POSIX machines and could lead to security issue resulting in world
+ writeable directories. Thus it must be mutexed by the calling code.
+ This is due to calling `umask(oldumask = umask(0))`, which sets the
+ umask for the whole process to 0 for a short time frame.
+ In case other thread calls the same function in parallel, it might
+ get interrupted by it and cause the executable to use umask=0 for the
+ remaining execution.
+ This will then lead to implicitely created directories to have 777
+ permissions without sticky bit.
+
+* In particular, libarchive's modules to read or write a directory
+ tree do use `chdir()` to optimize the directory traversals. This
+ can cause problems for programs that expect to do disk access from
+ multiple threads. Of course, those modules are completely
+ optional and you can use the rest of libarchive without them.
+
+* The library is _not_ thread aware, however. It does no locking
+ or thread management of any kind. If you create a libarchive
+ object and need to access it from multiple threads, you will
+ need to provide your own locking.
+
+* On read, the library accepts whatever blocks you hand it.
+ Your read callback is free to pass the library a byte at a time
+ or mmap the entire archive and give it to the library at once.
+ On write, the library always produces correctly-blocked output.
+
+* The object-style approach allows you to have multiple archive streams
+ open at once. bsdtar uses this in its "@archive" extension.
+
+* The archive itself is read/written using callback functions.
+ You can read an archive directly from an in-memory buffer or
+ write it to a socket, if you wish. There are some utility
+ functions to provide easy-to-use "open file," etc, capabilities.
+
+* The read/write APIs are designed to allow individual entries
+ to be read or written to any data source: You can create
+ a block of data in memory and add it to a tar archive without
+ first writing a temporary file. You can also read an entry from
+ an archive and write the data directly to a socket. If you want
+ to read/write entries to disk, there are convenience functions to
+ make this especially easy.
+
+* Note: The "pax interchange format" is a POSIX standard extended tar
+ format that should be used when the older _ustar_ format is not
+ appropriate. It has many advantages over other tar formats
+ (including the legacy GNU tar format) and is widely supported by
+ current tar implementations.
+
diff --git a/contrib/libs/libarchive/SECURITY.md b/contrib/libs/libarchive/SECURITY.md
new file mode 100644
index 0000000000..6ca188b603
--- /dev/null
+++ b/contrib/libs/libarchive/SECURITY.md
@@ -0,0 +1,19 @@
+# Security Policy
+
+If you have discovered a security vulnerability in this project, please report it
+privately. **Do not disclose it as a public issue.** This gives us time to work with you
+to fix the issue before public exposure, reducing the chance that the exploit will be
+used before a patch is released.
+
+You may submit the report in the following ways:
+
+- send an email to security@libarchive.de; and/or
+- send us a [private vulnerability report](https://github.com/libarchive/libarchive/security/advisories/new)
+
+Please provide the following information in your report:
+
+- A description of the vulnerability and its impact
+- How to reproduce the issue
+
+This project is maintained by volunteers on a reasonable-effort basis. As such, we ask
+that you give me 90 days to work on a fix before public exposure.
diff --git a/contrib/libs/libarchive/config-linux.h b/contrib/libs/libarchive/config-linux.h
new file mode 100644
index 0000000000..bc817ab25a
--- /dev/null
+++ b/contrib/libs/libarchive/config-linux.h
@@ -0,0 +1,1402 @@
+/* config.h. Generated from build/cmake/config.h.in by cmake configure */
+#define __LIBARCHIVE_CONFIG_H_INCLUDED 1
+
+/*
+ * Ensure we have C99-style int64_t, etc, all defined.
+ */
+
+/* First, we need to know if the system has already defined them. */
+#define HAVE_INT16_T
+#define HAVE_INT32_T
+#define HAVE_INT64_T
+#define HAVE_INTMAX_T
+
+#define HAVE_UINT8_T
+#define HAVE_UINT16_T
+#define HAVE_UINT32_T
+#define HAVE_UINT64_T
+#define HAVE_UINTMAX_T
+
+/* We might have the types we want under other spellings. */
+/* #undef HAVE___INT64 */
+/* #undef HAVE_U_INT64_T */
+/* #undef HAVE_UNSIGNED___INT64 */
+
+/* The sizes of various standard integer types. */
+#define SIZEOF_SHORT 2
+#define SIZEOF_INT 4
+#define SIZEOF_LONG 8
+#define SIZEOF_LONG_LONG 8
+#define SIZEOF_UNSIGNED_SHORT 2
+#define SIZEOF_UNSIGNED 4
+#define SIZEOF_UNSIGNED_LONG 8
+#define SIZEOF_UNSIGNED_LONG_LONG 8
+
+/*
+ * If we lack int64_t, define it to the first of __int64, int, long, and long long
+ * that exists and is the right size.
+ */
+#if !defined(HAVE_INT64_T) && defined(HAVE___INT64)
+typedef __int64 int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZEOF_INT == 8
+typedef int int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZEOF_LONG == 8
+typedef long int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZEOF_LONG_LONG == 8
+typedef long long int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T)
+#error No 64-bit integer type was found.
+#endif
+
+/*
+ * Similarly for int32_t
+ */
+#if !defined(HAVE_INT32_T) && SIZEOF_INT == 4
+typedef int int32_t;
+#define HAVE_INT32_T
+#endif
+
+#if !defined(HAVE_INT32_T) && SIZEOF_LONG == 4
+typedef long int32_t;
+#define HAVE_INT32_T
+#endif
+
+#if !defined(HAVE_INT32_T)
+#error No 32-bit integer type was found.
+#endif
+
+/*
+ * Similarly for int16_t
+ */
+#if !defined(HAVE_INT16_T) && SIZEOF_INT == 2
+typedef int int16_t;
+#define HAVE_INT16_T
+#endif
+
+#if !defined(HAVE_INT16_T) && SIZEOF_SHORT == 2
+typedef short int16_t;
+#define HAVE_INT16_T
+#endif
+
+#if !defined(HAVE_INT16_T)
+#error No 16-bit integer type was found.
+#endif
+
+/*
+ * Similarly for uint64_t
+ */
+#if !defined(HAVE_UINT64_T) && defined(HAVE_UNSIGNED___INT64)
+typedef unsigned __int64 uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZEOF_UNSIGNED == 8
+typedef unsigned uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZEOF_UNSIGNED_LONG == 8
+typedef unsigned long uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZEOF_UNSIGNED_LONG_LONG == 8
+typedef unsigned long long uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T)
+#error No 64-bit unsigned integer type was found.
+#endif
+
+
+/*
+ * Similarly for uint32_t
+ */
+#if !defined(HAVE_UINT32_T) && SIZEOF_UNSIGNED == 4
+typedef unsigned uint32_t;
+#define HAVE_UINT32_T
+#endif
+
+#if !defined(HAVE_UINT32_T) && SIZEOF_UNSIGNED_LONG == 4
+typedef unsigned long uint32_t;
+#define HAVE_UINT32_T
+#endif
+
+#if !defined(HAVE_UINT32_T)
+#error No 32-bit unsigned integer type was found.
+#endif
+
+/*
+ * Similarly for uint16_t
+ */
+#if !defined(HAVE_UINT16_T) && SIZEOF_UNSIGNED == 2
+typedef unsigned uint16_t;
+#define HAVE_UINT16_T
+#endif
+
+#if !defined(HAVE_UINT16_T) && SIZEOF_UNSIGNED_SHORT == 2
+typedef unsigned short uint16_t;
+#define HAVE_UINT16_T
+#endif
+
+#if !defined(HAVE_UINT16_T)
+#error No 16-bit unsigned integer type was found.
+#endif
+
+/*
+ * Similarly for uint8_t
+ */
+#if !defined(HAVE_UINT8_T)
+typedef unsigned char uint8_t;
+#define HAVE_UINT8_T
+#endif
+
+#if !defined(HAVE_UINT8_T)
+#error No 8-bit unsigned integer type was found.
+#endif
+
+/* Define intmax_t and uintmax_t if they are not already defined. */
+#if !defined(HAVE_INTMAX_T)
+typedef int64_t intmax_t;
+#endif
+
+#if !defined(HAVE_UINTMAX_T)
+typedef uint64_t uintmax_t;
+#endif
+
+/* Define ZLIB_WINAPI if zlib was built on Visual Studio. */
+/* #undef ZLIB_WINAPI */
+
+/* Darwin ACL support */
+/* #undef ARCHIVE_ACL_DARWIN */
+
+/* FreeBSD ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD */
+
+/* FreeBSD NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */
+
+/* Linux POSIX.1e ACL support via libacl */
+/* #undef ARCHIVE_ACL_LIBACL */
+
+/* Linux NFSv4 ACL support via librichacl */
+/* #undef ARCHIVE_ACL_LIBRICHACL */
+
+/* Solaris ACL support */
+/* #undef ARCHIVE_ACL_SUNOS */
+
+/* Solaris NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_SUNOS_NFS4 */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBC */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBSYSTEM */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_MD5_OPENSSL 1
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_WIN */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_RMD160_OPENSSL 1
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBSYSTEM */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_SHA1_OPENSSL 1
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_WIN */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBSYSTEM */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_SHA256_OPENSSL 1
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_WIN */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBSYSTEM */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_SHA384_OPENSSL 1
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_WIN */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBSYSTEM */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
+#define ARCHIVE_CRYPTO_SHA512_OPENSSL 1
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */
+
+/* AIX xattr support */
+/* #undef ARCHIVE_XATTR_AIX */
+
+/* Darwin xattr support */
+/* #undef ARCHIVE_XATTR_DARWIN */
+
+/* FreeBSD xattr support */
+/* #undef ARCHIVE_XATTR_FREEBSD */
+
+/* Linux xattr support */
+/* #undef ARCHIVE_XATTR_LINUX */
+
+/* Version number of bsdcpio */
+#define BSDCPIO_VERSION_STRING "3.7.2"
+
+/* Version number of bsdtar */
+#define BSDTAR_VERSION_STRING "3.7.2"
+
+/* Version number of bsdcat */
+#define BSDCAT_VERSION_STRING "3.7.2"
+
+/* Version number of bsdunzip */
+#define BSDUNZIP_VERSION_STRING "3.7.2"
+
+/* Define to 1 if you have the `acl_create_entry' function. */
+/* #undef HAVE_ACL_CREATE_ENTRY */
+
+/* Define to 1 if you have the `acl_get_fd_np' function. */
+/* #undef HAVE_ACL_GET_FD_NP */
+
+/* Define to 1 if you have the `acl_get_link' function. */
+/* #undef HAVE_ACL_GET_LINK */
+
+/* Define to 1 if you have the `acl_get_link_np' function. */
+/* #undef HAVE_ACL_GET_LINK_NP */
+
+/* Define to 1 if you have the `acl_get_perm' function. */
+/* #undef HAVE_ACL_GET_PERM */
+
+/* Define to 1 if you have the `acl_get_perm_np' function. */
+/* #undef HAVE_ACL_GET_PERM_NP */
+
+/* Define to 1 if you have the `acl_init' function. */
+/* #undef HAVE_ACL_INIT */
+
+/* Define to 1 if you have the <acl/libacl.h> header file. */
+/* #undef HAVE_ACL_LIBACL_H */
+
+/* Define to 1 if the system has the type `acl_permset_t'. */
+/* #undef HAVE_ACL_PERMSET_T */
+
+/* Define to 1 if you have the `acl_set_fd' function. */
+/* #undef HAVE_ACL_SET_FD */
+
+/* Define to 1 if you have the `acl_set_fd_np' function. */
+/* #undef HAVE_ACL_SET_FD_NP */
+
+/* Define to 1 if you have the `acl_set_file' function. */
+/* #undef HAVE_ACL_SET_FILE */
+
+/* Define to 1 if you have the `arc4random_buf' function. */
+/* #undef HAVE_ARC4RANDOM_BUF */
+
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+/* #undef HAVE_ATTR_XATTR_H */
+
+/* Define to 1 if you have the <bcrypt.h> header file. */
+/* #undef HAVE_BCRYPT_H */
+
+/* Define to 1 if you have the <bsdxml.h> header file. */
+/* #undef HAVE_BSDXML_H */
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+#define HAVE_BZLIB_H 1
+
+/* Define to 1 if you have the `chflags' function. */
+/* #undef HAVE_CHFLAGS */
+
+/* Define to 1 if you have the `chown' function. */
+#define HAVE_CHOWN 1
+
+/* Define to 1 if you have the `chroot' function. */
+#define HAVE_CHROOT 1
+
+/* Define to 1 if you have the <copyfile.h> header file. */
+/* #undef HAVE_COPYFILE_H */
+
+/* Define to 1 if you have the `ctime_r' function. */
+#define HAVE_CTIME_R 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the `cygwin_conv_path' function. */
+/* #undef HAVE_CYGWIN_CONV_PATH */
+
+/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACE_GETACL */
+
+/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACE_GETACLCNT */
+
+/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACE_SETACL */
+
+/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_ACL_SYNCHRONIZE */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_ACL_TYPE_EXTENDED */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACL_TYPE_NFS4 */
+
+/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_ACL_USER */
+
+/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_INT32_MAX 1
+
+/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you
+ don't. */
+#define HAVE_DECL_INT32_MIN 1
+
+/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_INT64_MAX 1
+
+/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you
+ don't. */
+#define HAVE_DECL_INT64_MIN 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_INTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you
+ don't. */
+#define HAVE_DECL_INTMAX_MIN 1
+
+/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.
+ */
+/* #undef HAVE_DECL_SETACL */
+
+/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_SIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_SSIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRERROR_R 1
+
+/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_UINT32_MAX 1
+
+/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_UINT64_MAX 1
+
+/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you
+ don't. */
+#define HAVE_DECL_UINTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_XATTR_NOFOLLOW */
+
+/* Define to 1 if you have the <direct.h> header file. */
+/* #undef HAVE_DIRECT_H */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the `dirfd' function. */
+#define HAVE_DIRFD 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define to 1 if nl_langinfo supports D_MD_ORDER */
+/* #undef HAVE_D_MD_ORDER */
+
+/* A possible errno value for invalid file format errors */
+/* #undef HAVE_EFTYPE */
+
+/* A possible errno value for invalid file format errors */
+#define HAVE_EILSEQ 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <expat.h> header file. */
+/* #undef HAVE_EXPAT_H */
+
+/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
+/* #undef HAVE_EXT2FS_EXT2_FS_H */
+
+/* Define to 1 if you have the `extattr_get_file' function. */
+/* #undef HAVE_EXTATTR_GET_FILE */
+
+/* Define to 1 if you have the `extattr_list_file' function. */
+/* #undef HAVE_EXTATTR_LIST_FILE */
+
+/* Define to 1 if you have the `extattr_set_fd' function. */
+/* #undef HAVE_EXTATTR_SET_FD */
+
+/* Define to 1 if you have the `extattr_set_file' function. */
+/* #undef HAVE_EXTATTR_SET_FILE */
+
+/* Define to 1 if EXTATTR_NAMESPACE_USER is defined in sys/extattr.h. */
+/* #undef HAVE_DECL_EXTATTR_NAMESPACE_USER */
+
+/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.
+ */
+/* #undef HAVE_DECL_GETACL */
+
+/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_GETACLCNT */
+
+/* Define to 1 if you have the `fchdir' function. */
+#define HAVE_FCHDIR 1
+
+/* Define to 1 if you have the `fchflags' function. */
+/* #undef HAVE_FCHFLAGS */
+
+/* Define to 1 if you have the `fchmod' function. */
+#define HAVE_FCHMOD 1
+
+/* Define to 1 if you have the `fchown' function. */
+#define HAVE_FCHOWN 1
+
+/* Define to 1 if you have the `fcntl' function. */
+#define HAVE_FCNTL 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fdopendir' function. */
+#define HAVE_FDOPENDIR 1
+
+/* Define to 1 if you have the `fgetea' function. */
+/* #undef HAVE_FGETEA */
+
+/* Define to 1 if you have the `fgetxattr' function. */
+/* #undef HAVE_FGETXATTR */
+
+/* Define to 1 if you have the `flistea' function. */
+/* #undef HAVE_FLISTEA */
+
+/* Define to 1 if you have the `flistxattr' function. */
+/* #undef HAVE_FLISTXATTR */
+
+/* Define to 1 if you have the `fnmatch' function. */
+#define HAVE_FNMATCH 1
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#define HAVE_FNMATCH_H 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `fsetea' function. */
+/* #undef HAVE_FSETEA */
+
+/* Define to 1 if you have the `fsetxattr' function. */
+/* #undef HAVE_FSETXATTR */
+
+/* Define to 1 if you have the `fstat' function. */
+#define HAVE_FSTAT 1
+
+/* Define to 1 if you have the `fstatat' function. */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the `fstatfs' function. */
+#define HAVE_FSTATFS 1
+
+/* Define to 1 if you have the `fstatvfs' function. */
+/* #undef HAVE_FSTATVFS */
+
+/* Define to 1 if you have the `ftruncate' function. */
+#define HAVE_FTRUNCATE 1
+
+/* Define to 1 if you have the `futimens' function. */
+#define HAVE_FUTIMENS 1
+
+/* Define to 1 if you have the `futimes' function. */
+#define HAVE_FUTIMES 1
+
+/* Define to 1 if you have the `futimesat' function. */
+#define HAVE_FUTIMESAT 1
+
+/* Define to 1 if you have the `getea' function. */
+/* #undef HAVE_GETEA */
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getgrgid_r' function. */
+#define HAVE_GETGRGID_R 1
+
+/* Define to 1 if you have the `getgrnam_r' function. */
+#define HAVE_GETGRNAM_R 1
+
+/* Define to 1 if you have the `getline' function. */
+#define HAVE_GETLINE 1
+
+/* Define to 1 if you have the `getpid' function. */
+#define HAVE_GETPID 1
+
+/* Define to 1 if you have the `getpwnam_r' function. */
+#define HAVE_GETPWNAM_R 1
+
+/* Define to 1 if you have the `getpwuid_r' function. */
+#define HAVE_GETPWUID_R 1
+
+/* Define to 1 if you have the `getvfsbyname' function. */
+/* #undef HAVE_GETVFSBYNAME */
+
+/* Define to 1 if you have the `getxattr' function. */
+/* #undef HAVE_GETXATTR */
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define to 1 if you have the `iconv' function. */
+/* #undef HAVE_ICONV */
+
+/* Define to 1 if you have the <iconv.h> header file. */
+/* #undef HAVE_ICONV_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <io.h> header file. */
+/* #undef HAVE_IO_H */
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define to 1 if you have the `lchflags' function. */
+/* #undef HAVE_LCHFLAGS */
+
+/* Define to 1 if you have the `lchmod' function. */
+#define HAVE_LCHMOD 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the `lgetea' function. */
+/* #undef HAVE_LGETEA */
+
+/* Define to 1 if you have the `lgetxattr' function. */
+/* #undef HAVE_LGETXATTR */
+
+/* Define to 1 if you have the `acl' library (-lacl). */
+/* #undef HAVE_LIBACL */
+
+/* Define to 1 if you have the `attr' library (-lattr). */
+/* #undef HAVE_LIBATTR */
+
+/* Define to 1 if you have the `bsdxml' library (-lbsdxml). */
+/* #undef HAVE_LIBBSDXML */
+
+/* Define to 1 if you have the `bz2' library (-lbz2). */
+#define HAVE_LIBBZ2 1
+
+/* Define to 1 if you have the `b2' library (-lb2). */
+#define HAVE_LIBB2 1
+
+/* Define to 1 if you have the <blake2.h> header file. */
+#define HAVE_BLAKE2_H 1
+
+/* Define to 1 if you have the `charset' library (-lcharset). */
+/* #undef HAVE_LIBCHARSET */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+#define HAVE_LIBCRYPTO 1
+
+/* Define to 1 if you have the `expat' library (-lexpat). */
+/* #undef HAVE_LIBEXPAT */
+
+/* Define to 1 if you have the `gcc' library (-lgcc). */
+/* #undef HAVE_LIBGCC */
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+#define HAVE_LIBLZ4 1
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+/* #undef HAVE_LIBLZMA */
+
+/* Define to 1 if you have the `lzmadec' library (-llzmadec). */
+/* #undef HAVE_LIBLZMADEC */
+
+/* Define to 1 if you have the `lzo2' library (-llzo2). */
+/* #undef HAVE_LIBLZO2 */
+
+/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */
+/* #undef HAVE_LIBMBEDCRYPTO */
+
+/* Define to 1 if you have the `nettle' library (-lnettle). */
+/* #undef HAVE_LIBNETTLE */
+
+/* Define to 1 if you have the `pcre' library (-lpcre). */
+/* #undef HAVE_LIBPCRE */
+
+/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */
+/* #undef HAVE_LIBPCREPOSIX */
+
+/* Define to 1 if you have the `xml2' library (-lxml2). */
+/* #undef HAVE_LIBXML2 */
+
+/* Define to 1 if you have the <libxml/xmlreader.h> header file. */
+/* #undef HAVE_LIBXML_XMLREADER_H */
+
+/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */
+/* #undef HAVE_LIBXML_XMLWRITER_H */
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+#define HAVE_LIBZSTD 1
+
+/* Define to 1 if you have the `zstd' library (-lzstd) with compression
+ support. */
+#define HAVE_LIBZSTD_COMPRESSOR 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the `link' function. */
+#define HAVE_LINK 1
+
+/* Define to 1 if you have the `linkat' function. */
+#define HAVE_LINKAT 1
+
+/* Define to 1 if you have the <linux/fiemap.h> header file. */
+#define HAVE_LINUX_FIEMAP_H 1
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+#define HAVE_LINUX_FS_H 1
+
+/* Define to 1 if you have the <linux/magic.h> header file. */
+#define HAVE_LINUX_MAGIC_H 1
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#define HAVE_LINUX_TYPES_H 1
+
+/* Define to 1 if you have the `listea' function. */
+/* #undef HAVE_LISTEA */
+
+/* Define to 1 if you have the `listxattr' function. */
+/* #undef HAVE_LISTXATTR */
+
+/* Define to 1 if you have the `llistea' function. */
+/* #undef HAVE_LLISTEA */
+
+/* Define to 1 if you have the `llistxattr' function. */
+/* #undef HAVE_LLISTXATTR */
+
+/* Define to 1 if you have the <localcharset.h> header file. */
+/* #undef HAVE_LOCALCHARSET_H */
+
+/* Define to 1 if you have the `locale_charset' function. */
+/* #undef HAVE_LOCALE_CHARSET */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if the system has the type `long long int'. */
+/* #undef HAVE_LONG_LONG_INT */
+
+/* Define to 1 if you have the `lsetea' function. */
+/* #undef HAVE_LSETEA */
+
+/* Define to 1 if you have the `lsetxattr' function. */
+/* #undef HAVE_LSETXATTR */
+
+/* Define to 1 if you have the `lstat' function. */
+#define HAVE_LSTAT 1
+
+/* Define to 1 if `lstat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the `lutimes' function. */
+#define HAVE_LUTIMES 1
+
+/* Define to 1 if you have the <lz4hc.h> header file. */
+#define HAVE_LZ4HC_H 1
+
+/* Define to 1 if you have the <lz4.h> header file. */
+#define HAVE_LZ4_H 1
+
+/* Define to 1 if you have the <lzmadec.h> header file. */
+/* #undef HAVE_LZMADEC_H */
+
+/* Define to 1 if you have the <lzma.h> header file. */
+/* #undef HAVE_LZMA_H */
+
+/* Define to 1 if you have a working `lzma_stream_encoder_mt' function. */
+/* #undef HAVE_LZMA_STREAM_ENCODER_MT */
+
+/* Define to 1 if you have the <lzo/lzo1x.h> header file. */
+/* #undef HAVE_LZO_LZO1X_H */
+
+/* Define to 1 if you have the <lzo/lzoconf.h> header file. */
+/* #undef HAVE_LZO_LZOCONF_H */
+
+/* Define to 1 if you have the <mbedtls/aes.h> header file. */
+/* #undef HAVE_MBEDTLS_AES_H */
+
+/* Define to 1 if you have the <mbedtls/md.h> header file. */
+/* #undef HAVE_MBEDTLS_MD_H */
+
+/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */
+/* #undef HAVE_MBEDTLS_PKCS5_H */
+
+/* Define to 1 if you have the `mbrtowc' function. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the <membership.h> header file. */
+/* #undef HAVE_MEMBERSHIP_H */
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mkdir' function. */
+#define HAVE_MKDIR 1
+
+/* Define to 1 if you have the `mkfifo' function. */
+#define HAVE_MKFIFO 1
+
+/* Define to 1 if you have the `mknod' function. */
+#define HAVE_MKNOD 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <nettle/aes.h> header file. */
+/* #undef HAVE_NETTLE_AES_H */
+
+/* Define to 1 if you have the <nettle/hmac.h> header file. */
+/* #undef HAVE_NETTLE_HMAC_H */
+
+/* Define to 1 if you have the <nettle/md5.h> header file. */
+/* #undef HAVE_NETTLE_MD5_H */
+
+/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */
+/* #undef HAVE_NETTLE_PBKDF2_H */
+
+/* Define to 1 if you have the <nettle/ripemd160.h> header file. */
+/* #undef HAVE_NETTLE_RIPEMD160_H */
+
+/* Define to 1 if you have the <nettle/sha.h> header file. */
+/* #undef HAVE_NETTLE_SHA_H */
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#define HAVE_NL_LANGINFO 1
+
+/* Define to 1 if you have the `openat' function. */
+#define HAVE_OPENAT 1
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+#define HAVE_OPENSSL_EVP_H 1
+
+/* Define to 1 if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define to 1 if you have the <pcreposix.h> header file. */
+/* #undef HAVE_PCREPOSIX_H */
+
+/* Define to 1 if you have the `pipe' function. */
+#define HAVE_PIPE 1
+
+/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */
+#define HAVE_PKCS5_PBKDF2_HMAC_SHA1 1
+
+/* Define to 1 if you have the `poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Define to 1 if you have the `posix_spawnp' function. */
+#define HAVE_POSIX_SPAWNP 0
+
+/* Define to 1 if you have the <process.h> header file. */
+/* #undef HAVE_PROCESS_H */
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#define HAVE_PTHREAD_H 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the `readdir_r' function. */
+#define HAVE_READDIR_R 1
+
+/* Define to 1 if you have the `readlink' function. */
+#define HAVE_READLINK 1
+
+/* Define to 1 if you have the `readlinkat' function. */
+#define HAVE_READLINKAT 1
+
+/* Define to 1 if you have the `readpassphrase' function. */
+/* #undef HAVE_READPASSPHRASE */
+
+/* Define to 1 if you have the <readpassphrase.h> header file. */
+/* #undef HAVE_READPASSPHRASE_H */
+
+/* Define to 1 if you have the <regex.h> header file. */
+#define HAVE_REGEX_H 1
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <spawn.h> header file. */
+#define HAVE_SPAWN_H 1
+
+/* Define to 1 if you have the `statfs' function. */
+#define HAVE_STATFS 1
+
+/* Define to 1 if you have the `statvfs' function. */
+/* #undef HAVE_STATVFS */
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+/* #undef HAVE_STAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strnlen' function. */
+#define HAVE_STRNLEN 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strerror_r' function. */
+#define HAVE_STRERROR_R 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strrchr' function. */
+#define HAVE_STRRCHR 1
+
+/* Define to 1 if the system has the type `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS */
+
+/* Define to 1 if `f_iosize' is a member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_IOSIZE */
+
+/* Define to 1 if `f_namemax' is a member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */
+
+/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */
+/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */
+
+/* Define to 1 if `st_birthtime' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIME */
+
+/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC */
+
+/* Define to 1 if `st_blksize' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+
+/* Define to 1 if `st_flags' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_FLAGS */
+
+/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC */
+
+/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */
+
+/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+
+/* Define to 1 if `st_umtime' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_UMTIME */
+
+/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+
+/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */
+/* #undef HAVE_STRUCT_TM___TM_GMTOFF */
+
+/* Define to 1 if you have `struct vfsconf'. */
+/* #undef HAVE_STRUCT_VFSCONF */
+
+/* Define to 1 if you have `struct xvfsconf'. */
+/* #undef HAVE_STRUCT_XVFSCONF */
+
+/* Define to 1 if you have the `symlink' function. */
+#define HAVE_SYMLINK 1
+
+/* Define to 1 if you have the <sys/acl.h> header file. */
+/* #undef HAVE_SYS_ACL_H */
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/ea.h> header file. */
+/* #undef HAVE_SYS_EA_H */
+
+/* Define to 1 if you have the <sys/extattr.h> header file. */
+/* #undef HAVE_SYS_EXTATTR_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+/* #undef HAVE_SYS_MOUNT_H */
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#define HAVE_SYS_QUEUE_H 1
+
+/* Define to 1 if you have the <sys/richacl.h> header file. */
+/* #undef HAVE_SYS_RICHACL_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/statfs.h> header file. */
+#define HAVE_SYS_STATFS_H 1
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#define HAVE_SYS_STATVFS_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+#define HAVE_SYS_SYSMACROS_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+/* #undef HAVE_SYS_UTIME_H */
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+#define HAVE_SYS_VFS_H 1
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#define HAVE_SYS_XATTR_H 1
+
+/* Define to 1 if you have the `timegm' function. */
+#define HAVE_TIMEGM 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the `tzset' function. */
+#define HAVE_TZSET 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlinkat' function. */
+#define HAVE_UNLINKAT 1
+
+/* Define to 1 if you have the `unsetenv' function. */
+#define HAVE_UNSETENV 1
+
+/* Define to 1 if the system has the type `unsigned long long'. */
+/* #undef HAVE_UNSIGNED_LONG_LONG */
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+/* #undef HAVE_UNSIGNED_LONG_LONG_INT */
+
+/* Define to 1 if you have the `utime' function. */
+#define HAVE_UTIME 1
+
+/* Define to 1 if you have the `utimensat' function. */
+#define HAVE_UTIMENSAT 1
+
+/* Define to 1 if you have the `utimes' function. */
+#define HAVE_UTIMES 1
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if the system has the type `wchar_t'. */
+#define HAVE_WCHAR_T 1
+
+/* Define to 1 if you have the `wcrtomb' function. */
+#define HAVE_WCRTOMB 1
+
+/* Define to 1 if you have the `wcscmp' function. */
+#define HAVE_WCSCMP 1
+
+/* Define to 1 if you have the `wcscpy' function. */
+#define HAVE_WCSCPY 1
+
+/* Define to 1 if you have the `wcslen' function. */
+#define HAVE_WCSLEN 1
+
+/* Define to 1 if you have the `wctomb' function. */
+#define HAVE_WCTOMB 1
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#define HAVE_WCTYPE_H 1
+
+/* Define to 1 if you have the <wincrypt.h> header file. */
+/* #undef HAVE_WINCRYPT_H */
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <winioctl.h> header file. */
+/* #undef HAVE_WINIOCTL_H */
+
+/* Define to 1 if you have _CrtSetReportMode in <crtdbg.h> */
+/* #undef HAVE__CrtSetReportMode */
+
+/* Define to 1 if you have the `wmemcmp' function. */
+#define HAVE_WMEMCMP 1
+
+/* Define to 1 if you have the `wmemcpy' function. */
+#define HAVE_WMEMCPY 1
+
+/* Define to 1 if you have the `wmemmove' function. */
+#define HAVE_WMEMMOVE 1
+
+/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */
+/* #undef HAVE_WORKING_EXT2_IOC_GETFLAGS */
+
+/* Define to 1 if you have a working FS_IOC_GETFLAGS */
+#define HAVE_WORKING_FS_IOC_GETFLAGS 1
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#define HAVE_ZLIB_H 1
+
+/* Define to 1 if you have the <zstd.h> header file. */
+#define HAVE_ZSTD_H 1
+
+/* Define to 1 if you have the `ctime_s' function. */
+/* #undef HAVE_CTIME_S */
+
+/* Define to 1 if you have the `_fseeki64' function. */
+/* #undef HAVE__FSEEKI64 */
+
+/* Define to 1 if you have the `_get_timezone' function. */
+/* #undef HAVE__GET_TIMEZONE */
+
+/* Define to 1 if you have the `gmtime_s' function. */
+/* #undef HAVE_GMTIME_S */
+
+/* Define to 1 if you have the `localtime_s' function. */
+/* #undef HAVE_LOCALTIME_S */
+
+/* Define to 1 if you have the `_mkgmtime' function. */
+/* #undef HAVE__MKGMTIME */
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST
+
+/* Version number of libarchive as a single integer */
+#define LIBARCHIVE_VERSION_NUMBER "3007002"
+
+/* Version number of libarchive */
+#define LIBARCHIVE_VERSION_STRING "3.7.2"
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+ slash. */
+/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+ */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+ <sysmacros.h>. */
+#define MAJOR_IN_SYSMACROS 1
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* The size of `wchar_t', as computed by sizeof. */
+#define SIZEOF_WCHAR_T 4
+
+/* Define to 1 if strerror_r returns char *. */
+/* #undef STRERROR_R_CHAR_P */
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+/* #undef TIME_WITH_SYS_TIME */
+
+/*
+ * Some platform requires a macro to use extension functions.
+ */
+#define SAFE_TO_DEFINE_EXTENSIONS 1
+#ifdef SAFE_TO_DEFINE_EXTENSIONS
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+#endif /* SAFE_TO_DEFINE_EXTENSIONS */
+
+/* Version number of package */
+#define VERSION "3.7.2"
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to control Windows SDK version */
+#ifndef NTDDI_VERSION
+/* #undef NTDDI_VERSION */
+#endif // NTDDI_VERSION
+
+#ifndef _WIN32_WINNT
+/* #undef _WIN32_WINNT */
+#endif // _WIN32_WINNT
+
+#ifndef WINVER
+/* #undef WINVER */
+#endif // WINVER
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define to `unsigned long' if <sys/types.h> does not define. */
+/* #undef id_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `long long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef pid_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef ssize_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef intptr_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef uintptr_t */
+
+#define HAVE_STRUCT_STATFS
diff --git a/contrib/libs/libarchive/config-osx.h b/contrib/libs/libarchive/config-osx.h
new file mode 100644
index 0000000000..be6d950a9e
--- /dev/null
+++ b/contrib/libs/libarchive/config-osx.h
@@ -0,0 +1,42 @@
+#include "config-linux.h"
+
+#define ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1
+#define ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1
+#define ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1
+#define ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1
+#define ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1
+
+#define HAVE_ARC4RANDOM_BUF 1
+#define HAVE_CHFLAGS 1
+#define HAVE_COPYFILE_H 1
+#define HAVE_D_MD_ORDER 1
+#define HAVE_EFTYPE 1
+#define HAVE_FCHFLAGS 1
+#define HAVE_GETVFSBYNAME 1
+#define HAVE_LCHFLAGS 1
+#define HAVE_READPASSPHRASE 1
+#define HAVE_READPASSPHRASE_H 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_VFSCONF 1
+
+#undef HAVE_FUTIMENS
+#undef HAVE_FUTIMESAT
+#undef HAVE_LINUX_FIEMAP_H
+#undef HAVE_LINUX_FS_H
+#undef HAVE_LINUX_MAGIC_H
+#undef HAVE_LINUX_TYPES_H
+#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#undef HAVE_SYS_STATFS_H
+#undef HAVE_SYS_SYSMACROS_H
+#undef HAVE_SYS_VFS_H
+#undef HAVE_SYS_XATTR_H
+#undef HAVE_UTIMENSAT
+#undef MAJOR_IN_SYSMACROS
+
+#undef HAVE_POSIX_SPAWNP
+#define HAVE_POSIX_SPAWNP 1
+
+#undef HAVE_STRUCT_STATFS
diff --git a/contrib/libs/libarchive/config-win.h b/contrib/libs/libarchive/config-win.h
new file mode 100644
index 0000000000..794a9c46a6
--- /dev/null
+++ b/contrib/libs/libarchive/config-win.h
@@ -0,0 +1,121 @@
+#include "config-linux.h"
+
+#define HAVE___INT64
+#define HAVE_UNSIGNED___INT64
+
+#undef SIZEOF_LONG
+#define SIZEOF_LONG 4
+
+#undef SIZEOF_UNSIGNED_LONG
+#define SIZEOF_UNSIGNED_LONG 4
+
+#define ARCHIVE_CRYPTO_MD5_WIN 1
+#define ARCHIVE_CRYPTO_SHA1_WIN 1
+#define ARCHIVE_CRYPTO_SHA256_WIN 1
+#define ARCHIVE_CRYPTO_SHA384_WIN 1
+#define ARCHIVE_CRYPTO_SHA512_WIN 1
+
+#define HAVE_BCRYPT_H 1
+#define HAVE_DIRECT_H 1
+#define HAVE__GET_TIMEZONE 1
+#define HAVE_IO_H 1
+#define HAVE_PROCESS_H 1
+#define HAVE_SYS_UTIME_H 1
+#define HAVE_WINCRYPT_H 1
+
+#undef HAVE_CHOWN
+#undef HAVE_CHROOT
+#undef HAVE_CTIME_R
+#undef HAVE_DECL_SSIZE_MAX
+#undef HAVE_DECL_STRERROR_R
+#undef HAVE_DIRENT_H
+#undef HAVE_DIRFD
+#undef HAVE_DLFCN_H
+#undef HAVE_FCHDIR
+#undef HAVE_FCHMOD
+#undef HAVE_FCHOWN
+#undef HAVE_FCNTL
+#undef HAVE_FDOPENDIR
+#undef HAVE_FORK
+#undef HAVE_FSEEK
+#undef HAVE_FSTATAT
+#undef HAVE_FSTATFS
+#undef HAVE_FSTATVFS
+#undef HAVE_FTRUNCATE
+#undef HAVE_FUTIMENS
+#undef HAVE_FUTIMES
+#undef HAVE_FUTIMESAT
+#undef HAVE_GETEUID
+#undef HAVE_GETGRGID_R
+#undef HAVE_GETGRNAM_R
+#undef HAVE_GETPWNAM_R
+#undef HAVE_GETPWUID_R
+#undef HAVE_GMTIME_R
+#undef HAVE_GRP_H
+#undef HAVE_LANGINFO_H
+#undef HAVE_LCHMOD
+#undef HAVE_LCHOW
+#undef HAVE_LINK
+#undef HAVE_LINUX_FIEMAP_H
+#undef HAVE_LINUX_FS_H
+#undef HAVE_LINUX_MAGIC_H
+#undef HAVE_LINUX_TYPES_H
+#undef HAVE_LOCALTIME_R
+#undef HAVE_LSTAT
+#undef HAVE_LUTIMES
+#undef HAVE_MKFIFO
+#undef HAVE_MKNOD
+#undef HAVE_MKSTEMP
+#undef HAVE_NL_LANGINFO
+#undef HAVE_OPENAT
+#undef HAVE_PATHS_H
+#undef HAVE_PIPE
+#undef HAVE_POLL
+#undef HAVE_POLL_H
+#undef HAVE_POSIX_SPAWN
+#undef HAVE_PTHREAD_H
+#undef HAVE_PWD_H
+#undef HAVE_READDIR_R
+#undef HAVE_READLINK
+#undef HAVE_READLINKAT
+#undef HAVE_REGEX_H
+#undef HAVE_SELECT
+#undef HAVE_SETENV
+#undef HAVE_SIGACTION
+#undef HAVE_SPAWN_H
+#undef HAVE_STATFS
+#undef HAVE_STATVFS
+#undef HAVE_STRERROR_R
+#undef HAVE_STRINGS_H
+#undef HAVE_STRUCT_STAT_ST_BLKSIZE
+#undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#undef HAVE_STRUCT_TM_TM_GMTOFF
+#undef HAVE_SYMLINK
+#undef HAVE_SYS_CDEFS_H
+#undef HAVE_SYS_IOCTL_H
+#undef HAVE_SYS_PARAM_H
+#undef HAVE_SYS_POLL_H
+#undef HAVE_SYS_SELECT_H
+#undef HAVE_SYS_STATFS_H
+#undef HAVE_SYS_STATVFS_H
+#undef HAVE_SYS_SYSMACROS_H
+#undef HAVE_SYS_TIME_H
+#undef HAVE_SYS_UTSNAME_H
+#undef HAVE_SYS_VFS_H
+#undef HAVE_SYS_VFS_H
+#undef HAVE_SYS_WAIT_H
+#undef HAVE_SYS_XATTR_H
+#undef HAVE_TIMEGM
+#undef HAVE_UNISTD_H
+#undef HAVE_UNSETENV
+#undef HAVE_UTIMENSAT
+#undef HAVE_UTIMES
+#undef HAVE_UTIME_H
+#undef MAJOR_IN_SYSMACROS
+
+typedef __int64 ssize_t;
+typedef int pid_t;
+typedef short uid_t;
+typedef short gid_t;
+typedef short id_t;
+typedef unsigned short mode_t;
diff --git a/contrib/libs/libarchive/config.h b/contrib/libs/libarchive/config.h
new file mode 100644
index 0000000000..1614a4f2b6
--- /dev/null
+++ b/contrib/libs/libarchive/config.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#if defined(__APPLE__)
+# include "config-osx.h"
+#elif defined(_MSC_VER)
+# include "config-win.h"
+#else
+# include "config-linux.h"
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive.h b/contrib/libs/libarchive/libarchive/archive.h
new file mode 100644
index 0000000000..b207f72238
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive.h
@@ -0,0 +1,1212 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: src/lib/libarchive/archive.h.in,v 1.50 2008/05/26 17:00:22 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_H_INCLUDED
+#define ARCHIVE_H_INCLUDED
+
+/*
+ * The version number is expressed as a single integer that makes it
+ * easy to compare versions at build time: for version a.b.c, the
+ * version number is printf("%d%03d%03d",a,b,c). For example, if you
+ * know your application requires version 2.12.108 or later, you can
+ * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
+ */
+/* Note: Compiler will complain if this does not match archive_entry.h! */
+#define ARCHIVE_VERSION_NUMBER 3007002
+
+#include <sys/stat.h>
+#include <stddef.h> /* for wchar_t */
+#include <stdio.h> /* For FILE * */
+#include <time.h> /* For time_t */
+
+/*
+ * Note: archive.h is for use outside of libarchive; the configuration
+ * headers (config.h, archive_platform.h, etc.) are purely internal.
+ * Do NOT use HAVE_XXX configuration macros to control the behavior of
+ * this header! If you must conditionalize, use predefined compiler and/or
+ * platform macros.
+ */
+#if defined(__BORLANDC__) && __BORLANDC__ >= 0x560
+# include <stdint.h>
+#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) && !defined(__CLANG_INTTYPES_H)
+# include <inttypes.h>
+#endif
+
+/* Get appropriate definitions of 64-bit integer */
+#if !defined(__LA_INT64_T_DEFINED)
+/* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_INT64_T la_int64_t
+# endif
+#define __LA_INT64_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+typedef __int64 la_int64_t;
+# else
+# include <unistd.h> /* ssize_t */
+# if defined(_SCO_DS) || defined(__osf__)
+typedef long long la_int64_t;
+# else
+typedef int64_t la_int64_t;
+# endif
+# endif
+#endif
+
+/* The la_ssize_t should match the type used in 'struct stat' */
+#if !defined(__LA_SSIZE_T_DEFINED)
+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_SSIZE_T la_ssize_t
+# endif
+#define __LA_SSIZE_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
+typedef ssize_t la_ssize_t;
+# elif defined(_WIN64)
+typedef __int64 la_ssize_t;
+# else
+typedef long la_ssize_t;
+# endif
+# else
+# include <unistd.h> /* ssize_t */
+typedef ssize_t la_ssize_t;
+# endif
+#endif
+
+/* Large file support for Android */
+#if defined(__LIBARCHIVE_BUILD) && defined(__ANDROID__)
+#error #include "android_lf.h"
+#endif
+
+/*
+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a
+ * .lib. The default here assumes you're building a DLL. Only
+ * libarchive source should ever define __LIBARCHIVE_BUILD.
+ */
+#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
+# ifdef __LIBARCHIVE_BUILD
+# ifdef __GNUC__
+# define __LA_DECL __attribute__((dllexport)) extern
+# else
+# define __LA_DECL __declspec(dllexport)
+# endif
+# else
+# ifdef __GNUC__
+# define __LA_DECL
+# else
+# define __LA_DECL __declspec(dllimport)
+# endif
+# endif
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+# define __LA_DECL __attribute__((visibility("default")))
+#else
+/* Static libraries or non-Windows needs no special declaration. */
+# define __LA_DECL
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__)
+#define __LA_PRINTF(fmtarg, firstvararg) \
+ __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+#else
+#define __LA_PRINTF(fmtarg, firstvararg) /* nothing */
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1
+# define __LA_DEPRECATED __attribute__((deprecated))
+#else
+# define __LA_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The version number is provided as both a macro and a function.
+ * The macro identifies the installed header; the function identifies
+ * the library version (which may not be the same if you're using a
+ * dynamically-linked version of the library). Of course, if the
+ * header and library are very different, you should expect some
+ * strangeness. Don't do that.
+ */
+__LA_DECL int archive_version_number(void);
+
+/*
+ * Textual name/version of the library, useful for version displays.
+ */
+#define ARCHIVE_VERSION_ONLY_STRING "3.7.2"
+#define ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
+__LA_DECL const char * archive_version_string(void);
+
+/*
+ * Detailed textual name/version of the library and its dependencies.
+ * This has the form:
+ * "libarchive x.y.z zlib/a.b.c liblzma/d.e.f ... etc ..."
+ * the list of libraries described here will vary depending on how
+ * libarchive was compiled.
+ */
+__LA_DECL const char * archive_version_details(void);
+
+/*
+ * Returns NULL if libarchive was compiled without the associated library.
+ * Otherwise, returns the version number that libarchive was compiled
+ * against.
+ */
+__LA_DECL const char * archive_zlib_version(void);
+__LA_DECL const char * archive_liblzma_version(void);
+__LA_DECL const char * archive_bzlib_version(void);
+__LA_DECL const char * archive_liblz4_version(void);
+__LA_DECL const char * archive_libzstd_version(void);
+
+/* Declare our basic types. */
+struct archive;
+struct archive_entry;
+
+/*
+ * Error codes: Use archive_errno() and archive_error_string()
+ * to retrieve details. Unless specified otherwise, all functions
+ * that return 'int' use these codes.
+ */
+#define ARCHIVE_EOF 1 /* Found end of archive. */
+#define ARCHIVE_OK 0 /* Operation was successful. */
+#define ARCHIVE_RETRY (-10) /* Retry might succeed. */
+#define ARCHIVE_WARN (-20) /* Partial success. */
+/* For example, if write_header "fails", then you can't push data. */
+#define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */
+/* But if write_header is "fatal," then this archive is dead and useless. */
+#define ARCHIVE_FATAL (-30) /* No more operations are possible. */
+
+/*
+ * As far as possible, archive_errno returns standard platform errno codes.
+ * Of course, the details vary by platform, so the actual definitions
+ * here are stored in "archive_platform.h". The symbols are listed here
+ * for reference; as a rule, clients should not need to know the exact
+ * platform-dependent error code.
+ */
+/* Unrecognized or invalid file format. */
+/* #define ARCHIVE_ERRNO_FILE_FORMAT */
+/* Illegal usage of the library. */
+/* #define ARCHIVE_ERRNO_PROGRAMMER_ERROR */
+/* Unknown or unclassified error. */
+/* #define ARCHIVE_ERRNO_MISC */
+
+/*
+ * Callbacks are invoked to automatically read/skip/write/open/close the
+ * archive. You can provide your own for complex tasks (like breaking
+ * archives across multiple tapes) or use standard ones built into the
+ * library.
+ */
+
+/* Returns pointer and size of next block of data from archive. */
+typedef la_ssize_t archive_read_callback(struct archive *,
+ void *_client_data, const void **_buffer);
+
+/* Skips at most request bytes from archive and returns the skipped amount.
+ * This may skip fewer bytes than requested; it may even skip zero bytes.
+ * If you do skip fewer bytes than requested, libarchive will invoke your
+ * read callback and discard data as necessary to make up the full skip.
+ */
+typedef la_int64_t archive_skip_callback(struct archive *,
+ void *_client_data, la_int64_t request);
+
+/* Seeks to specified location in the file and returns the position.
+ * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h.
+ * Return ARCHIVE_FATAL if the seek fails for any reason.
+ */
+typedef la_int64_t archive_seek_callback(struct archive *,
+ void *_client_data, la_int64_t offset, int whence);
+
+/* Returns size actually written, zero on EOF, -1 on error. */
+typedef la_ssize_t archive_write_callback(struct archive *,
+ void *_client_data,
+ const void *_buffer, size_t _length);
+
+typedef int archive_open_callback(struct archive *, void *_client_data);
+
+typedef int archive_close_callback(struct archive *, void *_client_data);
+
+typedef int archive_free_callback(struct archive *, void *_client_data);
+
+/* Switches from one client data object to the next/prev client data object.
+ * This is useful for reading from different data blocks such as a set of files
+ * that make up one large file.
+ */
+typedef int archive_switch_callback(struct archive *, void *_client_data1,
+ void *_client_data2);
+
+/*
+ * Returns a passphrase used for encryption or decryption, NULL on nothing
+ * to do and give it up.
+ */
+typedef const char *archive_passphrase_callback(struct archive *,
+ void *_client_data);
+
+/*
+ * Codes to identify various stream filters.
+ */
+#define ARCHIVE_FILTER_NONE 0
+#define ARCHIVE_FILTER_GZIP 1
+#define ARCHIVE_FILTER_BZIP2 2
+#define ARCHIVE_FILTER_COMPRESS 3
+#define ARCHIVE_FILTER_PROGRAM 4
+#define ARCHIVE_FILTER_LZMA 5
+#define ARCHIVE_FILTER_XZ 6
+#define ARCHIVE_FILTER_UU 7
+#define ARCHIVE_FILTER_RPM 8
+#define ARCHIVE_FILTER_LZIP 9
+#define ARCHIVE_FILTER_LRZIP 10
+#define ARCHIVE_FILTER_LZOP 11
+#define ARCHIVE_FILTER_GRZIP 12
+#define ARCHIVE_FILTER_LZ4 13
+#define ARCHIVE_FILTER_ZSTD 14
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+#define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE
+#define ARCHIVE_COMPRESSION_GZIP ARCHIVE_FILTER_GZIP
+#define ARCHIVE_COMPRESSION_BZIP2 ARCHIVE_FILTER_BZIP2
+#define ARCHIVE_COMPRESSION_COMPRESS ARCHIVE_FILTER_COMPRESS
+#define ARCHIVE_COMPRESSION_PROGRAM ARCHIVE_FILTER_PROGRAM
+#define ARCHIVE_COMPRESSION_LZMA ARCHIVE_FILTER_LZMA
+#define ARCHIVE_COMPRESSION_XZ ARCHIVE_FILTER_XZ
+#define ARCHIVE_COMPRESSION_UU ARCHIVE_FILTER_UU
+#define ARCHIVE_COMPRESSION_RPM ARCHIVE_FILTER_RPM
+#define ARCHIVE_COMPRESSION_LZIP ARCHIVE_FILTER_LZIP
+#define ARCHIVE_COMPRESSION_LRZIP ARCHIVE_FILTER_LRZIP
+#endif
+
+/*
+ * Codes returned by archive_format.
+ *
+ * Top 16 bits identifies the format family (e.g., "tar"); lower
+ * 16 bits indicate the variant. This is updated by read_next_header.
+ * Note that the lower 16 bits will often vary from entry to entry.
+ * In some cases, this variation occurs as libarchive learns more about
+ * the archive (for example, later entries might utilize extensions that
+ * weren't necessary earlier in the archive; in this case, libarchive
+ * will change the format code to indicate the extended format that
+ * was used). In other cases, it's because different tools have
+ * modified the archive and so different parts of the archive
+ * actually have slightly different formats. (Both tar and cpio store
+ * format codes in each entry, so it is quite possible for each
+ * entry to be in a different format.)
+ */
+#define ARCHIVE_FORMAT_BASE_MASK 0xff0000
+#define ARCHIVE_FORMAT_CPIO 0x10000
+#define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1)
+#define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2)
+#define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3)
+#define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4)
+#define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5)
+#define ARCHIVE_FORMAT_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6)
+#define ARCHIVE_FORMAT_CPIO_PWB (ARCHIVE_FORMAT_CPIO | 7)
+#define ARCHIVE_FORMAT_SHAR 0x20000
+#define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1)
+#define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2)
+#define ARCHIVE_FORMAT_TAR 0x30000
+#define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1)
+#define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2)
+#define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3)
+#define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4)
+#define ARCHIVE_FORMAT_ISO9660 0x40000
+#define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1)
+#define ARCHIVE_FORMAT_ZIP 0x50000
+#define ARCHIVE_FORMAT_EMPTY 0x60000
+#define ARCHIVE_FORMAT_AR 0x70000
+#define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1)
+#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2)
+#define ARCHIVE_FORMAT_MTREE 0x80000
+#define ARCHIVE_FORMAT_RAW 0x90000
+#define ARCHIVE_FORMAT_XAR 0xA0000
+#define ARCHIVE_FORMAT_LHA 0xB0000
+#define ARCHIVE_FORMAT_CAB 0xC0000
+#define ARCHIVE_FORMAT_RAR 0xD0000
+#define ARCHIVE_FORMAT_7ZIP 0xE0000
+#define ARCHIVE_FORMAT_WARC 0xF0000
+#define ARCHIVE_FORMAT_RAR_V5 0x100000
+
+/*
+ * Codes returned by archive_read_format_capabilities().
+ *
+ * This list can be extended with values between 0 and 0xffff.
+ * The original purpose of this list was to let different archive
+ * format readers expose their general capabilities in terms of
+ * encryption.
+ */
+#define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */
+#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0) /* reader can detect encrypted data */
+#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1) /* reader can detect encryptable metadata (pathname, mtime, etc.) */
+
+/*
+ * Codes returned by archive_read_has_encrypted_entries().
+ *
+ * In case the archive does not support encryption detection at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader
+ * for some other reason (e.g. not enough bytes read) cannot say if
+ * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW
+ * is returned.
+ */
+#define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2
+#define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1
+
+/*-
+ * Basic outline for reading an archive:
+ * 1) Ask archive_read_new for an archive reader object.
+ * 2) Update any global properties as appropriate.
+ * In particular, you'll certainly want to call appropriate
+ * archive_read_support_XXX functions.
+ * 3) Call archive_read_open_XXX to open the archive
+ * 4) Repeatedly call archive_read_next_header to get information about
+ * successive archive entries. Call archive_read_data to extract
+ * data for entries of interest.
+ * 5) Call archive_read_free to end processing.
+ */
+__LA_DECL struct archive *archive_read_new(void);
+
+/*
+ * The archive_read_support_XXX calls enable auto-detect for this
+ * archive handle. They also link in the necessary support code.
+ * For example, if you don't want bzlib linked in, don't invoke
+ * support_compression_bzip2(). The "all" functions provide the
+ * obvious shorthand.
+ */
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+__LA_DECL int archive_read_support_compression_all(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_bzip2(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_compress(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_gzip(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_lzip(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_lzma(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_none(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_program(struct archive *,
+ const char *command) __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_program_signature
+ (struct archive *, const char *,
+ const void * /* match */, size_t) __LA_DEPRECATED;
+
+__LA_DECL int archive_read_support_compression_rpm(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_uu(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_xz(struct archive *)
+ __LA_DEPRECATED;
+#endif
+
+__LA_DECL int archive_read_support_filter_all(struct archive *);
+__LA_DECL int archive_read_support_filter_by_code(struct archive *, int);
+__LA_DECL int archive_read_support_filter_bzip2(struct archive *);
+__LA_DECL int archive_read_support_filter_compress(struct archive *);
+__LA_DECL int archive_read_support_filter_gzip(struct archive *);
+__LA_DECL int archive_read_support_filter_grzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lrzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lz4(struct archive *);
+__LA_DECL int archive_read_support_filter_lzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lzma(struct archive *);
+__LA_DECL int archive_read_support_filter_lzop(struct archive *);
+__LA_DECL int archive_read_support_filter_none(struct archive *);
+__LA_DECL int archive_read_support_filter_program(struct archive *,
+ const char *command);
+__LA_DECL int archive_read_support_filter_program_signature
+ (struct archive *, const char * /* cmd */,
+ const void * /* match */, size_t);
+__LA_DECL int archive_read_support_filter_rpm(struct archive *);
+__LA_DECL int archive_read_support_filter_uu(struct archive *);
+__LA_DECL int archive_read_support_filter_xz(struct archive *);
+__LA_DECL int archive_read_support_filter_zstd(struct archive *);
+
+__LA_DECL int archive_read_support_format_7zip(struct archive *);
+__LA_DECL int archive_read_support_format_all(struct archive *);
+__LA_DECL int archive_read_support_format_ar(struct archive *);
+__LA_DECL int archive_read_support_format_by_code(struct archive *, int);
+__LA_DECL int archive_read_support_format_cab(struct archive *);
+__LA_DECL int archive_read_support_format_cpio(struct archive *);
+__LA_DECL int archive_read_support_format_empty(struct archive *);
+__LA_DECL int archive_read_support_format_gnutar(struct archive *);
+__LA_DECL int archive_read_support_format_iso9660(struct archive *);
+__LA_DECL int archive_read_support_format_lha(struct archive *);
+__LA_DECL int archive_read_support_format_mtree(struct archive *);
+__LA_DECL int archive_read_support_format_rar(struct archive *);
+__LA_DECL int archive_read_support_format_rar5(struct archive *);
+__LA_DECL int archive_read_support_format_raw(struct archive *);
+__LA_DECL int archive_read_support_format_tar(struct archive *);
+__LA_DECL int archive_read_support_format_warc(struct archive *);
+__LA_DECL int archive_read_support_format_xar(struct archive *);
+/* archive_read_support_format_zip() enables both streamable and seekable
+ * zip readers. */
+__LA_DECL int archive_read_support_format_zip(struct archive *);
+/* Reads Zip archives as stream from beginning to end. Doesn't
+ * correctly handle SFX ZIP files or ZIP archives that have been modified
+ * in-place. */
+__LA_DECL int archive_read_support_format_zip_streamable(struct archive *);
+/* Reads starting from central directory; requires seekable input. */
+__LA_DECL int archive_read_support_format_zip_seekable(struct archive *);
+
+/* Functions to manually set the format and filters to be used. This is
+ * useful to bypass the bidding process when the format and filters to use
+ * is known in advance.
+ */
+__LA_DECL int archive_read_set_format(struct archive *, int);
+__LA_DECL int archive_read_append_filter(struct archive *, int);
+__LA_DECL int archive_read_append_filter_program(struct archive *,
+ const char *);
+__LA_DECL int archive_read_append_filter_program_signature
+ (struct archive *, const char *, const void * /* match */, size_t);
+
+/* Set various callbacks. */
+__LA_DECL int archive_read_set_open_callback(struct archive *,
+ archive_open_callback *);
+__LA_DECL int archive_read_set_read_callback(struct archive *,
+ archive_read_callback *);
+__LA_DECL int archive_read_set_seek_callback(struct archive *,
+ archive_seek_callback *);
+__LA_DECL int archive_read_set_skip_callback(struct archive *,
+ archive_skip_callback *);
+__LA_DECL int archive_read_set_close_callback(struct archive *,
+ archive_close_callback *);
+/* Callback used to switch between one data object to the next */
+__LA_DECL int archive_read_set_switch_callback(struct archive *,
+ archive_switch_callback *);
+
+/* This sets the first data object. */
+__LA_DECL int archive_read_set_callback_data(struct archive *, void *);
+/* This sets data object at specified index */
+__LA_DECL int archive_read_set_callback_data2(struct archive *, void *,
+ unsigned int);
+/* This adds a data object at the specified index. */
+__LA_DECL int archive_read_add_callback_data(struct archive *, void *,
+ unsigned int);
+/* This appends a data object to the end of list */
+__LA_DECL int archive_read_append_callback_data(struct archive *, void *);
+/* This prepends a data object to the beginning of list */
+__LA_DECL int archive_read_prepend_callback_data(struct archive *, void *);
+
+/* Opening freezes the callbacks. */
+__LA_DECL int archive_read_open1(struct archive *);
+
+/* Convenience wrappers around the above. */
+__LA_DECL int archive_read_open(struct archive *, void *_client_data,
+ archive_open_callback *, archive_read_callback *,
+ archive_close_callback *);
+__LA_DECL int archive_read_open2(struct archive *, void *_client_data,
+ archive_open_callback *, archive_read_callback *,
+ archive_skip_callback *, archive_close_callback *);
+
+/*
+ * A variety of shortcuts that invoke archive_read_open() with
+ * canned callbacks suitable for common situations. The ones that
+ * accept a block size handle tape blocking correctly.
+ */
+/* Use this if you know the filename. Note: NULL indicates stdin. */
+__LA_DECL int archive_read_open_filename(struct archive *,
+ const char *_filename, size_t _block_size);
+/* Use this for reading multivolume files by filenames.
+ * NOTE: Must be NULL terminated. Sorting is NOT done. */
+__LA_DECL int archive_read_open_filenames(struct archive *,
+ const char **_filenames, size_t _block_size);
+__LA_DECL int archive_read_open_filename_w(struct archive *,
+ const wchar_t *_filename, size_t _block_size);
+/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
+__LA_DECL int archive_read_open_file(struct archive *,
+ const char *_filename, size_t _block_size) __LA_DEPRECATED;
+/* Read an archive that's stored in memory. */
+__LA_DECL int archive_read_open_memory(struct archive *,
+ const void * buff, size_t size);
+/* A more involved version that is only used for internal testing. */
+__LA_DECL int archive_read_open_memory2(struct archive *a, const void *buff,
+ size_t size, size_t read_size);
+/* Read an archive that's already open, using the file descriptor. */
+__LA_DECL int archive_read_open_fd(struct archive *, int _fd,
+ size_t _block_size);
+/* Read an archive that's already open, using a FILE *. */
+/* Note: DO NOT use this with tape drives. */
+__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);
+
+/* Parses and returns next entry header. */
+__LA_DECL int archive_read_next_header(struct archive *,
+ struct archive_entry **);
+
+/* Parses and returns next entry header using the archive_entry passed in */
+__LA_DECL int archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+
+/*
+ * Retrieve the byte offset in UNCOMPRESSED data where last-read
+ * header started.
+ */
+__LA_DECL la_int64_t archive_read_header_position(struct archive *);
+
+/*
+ * Returns 1 if the archive contains at least one encrypted entry.
+ * If the archive format not support encryption at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
+ * If for any other reason (e.g. not enough data read so far)
+ * we cannot say whether there are encrypted entries, then
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
+ * In general, this function will return values below zero when the
+ * reader is uncertain or totally incapable of encryption support.
+ * When this function returns 0 you can be sure that the reader
+ * supports encryption detection but no encrypted entries have
+ * been found yet.
+ *
+ * NOTE: If the metadata/header of an archive is also encrypted, you
+ * cannot rely on the number of encrypted entries. That is why this
+ * function does not return the number of encrypted entries but#
+ * just shows that there are some.
+ */
+__LA_DECL int archive_read_has_encrypted_entries(struct archive *);
+
+/*
+ * Returns a bitmask of capabilities that are supported by the archive format reader.
+ * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.
+ */
+__LA_DECL int archive_read_format_capabilities(struct archive *);
+
+/* Read data from the body of an entry. Similar to read(2). */
+__LA_DECL la_ssize_t archive_read_data(struct archive *,
+ void *, size_t);
+
+/* Seek within the body of an entry. Similar to lseek(2). */
+__LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int);
+
+/*
+ * A zero-copy version of archive_read_data that also exposes the file offset
+ * of each returned block. Note that the client has no way to specify
+ * the desired size of the block. The API does guarantee that offsets will
+ * be strictly increasing and that returned blocks will not overlap.
+ */
+__LA_DECL int archive_read_data_block(struct archive *a,
+ const void **buff, size_t *size, la_int64_t *offset);
+
+/*-
+ * Some convenience functions that are built on archive_read_data:
+ * 'skip': skips entire entry
+ * 'into_buffer': writes data into memory buffer that you provide
+ * 'into_fd': writes data to specified filedes
+ */
+__LA_DECL int archive_read_data_skip(struct archive *);
+__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);
+
+/*
+ * Set read options.
+ */
+/* Apply option to the format only. */
+__LA_DECL int archive_read_set_format_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option to the filter only. */
+__LA_DECL int archive_read_set_filter_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option to both the format and the filter. */
+__LA_DECL int archive_read_set_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option string to both the format and the filter. */
+__LA_DECL int archive_read_set_options(struct archive *_a,
+ const char *opts);
+
+/*
+ * Add a decryption passphrase.
+ */
+__LA_DECL int archive_read_add_passphrase(struct archive *, const char *);
+__LA_DECL int archive_read_set_passphrase_callback(struct archive *,
+ void *client_data, archive_passphrase_callback *);
+
+
+/*-
+ * Convenience function to recreate the current entry (whose header
+ * has just been read) on disk.
+ *
+ * This does quite a bit more than just copy data to disk. It also:
+ * - Creates intermediate directories as required.
+ * - Manages directory permissions: non-writable directories will
+ * be initially created with write permission enabled; when the
+ * archive is closed, dir permissions are edited to the values specified
+ * in the archive.
+ * - Checks hardlinks: hardlinks will not be extracted unless the
+ * linked-to file was also extracted within the same session. (TODO)
+ */
+
+/* The "flags" argument selects optional behavior, 'OR' the flags you want. */
+
+/* Default: Do not try to set owner/group. */
+#define ARCHIVE_EXTRACT_OWNER (0x0001)
+/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
+#define ARCHIVE_EXTRACT_PERM (0x0002)
+/* Default: Do not restore mtime/atime. */
+#define ARCHIVE_EXTRACT_TIME (0x0004)
+/* Default: Replace existing files. */
+#define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008)
+/* Default: Try create first, unlink only if create fails with EEXIST. */
+#define ARCHIVE_EXTRACT_UNLINK (0x0010)
+/* Default: Do not restore ACLs. */
+#define ARCHIVE_EXTRACT_ACL (0x0020)
+/* Default: Do not restore fflags. */
+#define ARCHIVE_EXTRACT_FFLAGS (0x0040)
+/* Default: Do not restore xattrs. */
+#define ARCHIVE_EXTRACT_XATTR (0x0080)
+/* Default: Do not try to guard against extracts redirected by symlinks. */
+/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */
+#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100)
+/* Default: Do not reject entries with '..' as path elements. */
+#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200)
+/* Default: Create parent directories as needed. */
+#define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400)
+/* Default: Overwrite files, even if one on disk is newer. */
+#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800)
+/* Detect blocks of 0 and write holes instead. */
+#define ARCHIVE_EXTRACT_SPARSE (0x1000)
+/* Default: Do not restore Mac extended metadata. */
+/* This has no effect except on Mac OS. */
+#define ARCHIVE_EXTRACT_MAC_METADATA (0x2000)
+/* Default: Use HFS+ compression if it was compressed. */
+/* This has no effect except on Mac OS v10.6 or later. */
+#define ARCHIVE_EXTRACT_NO_HFS_COMPRESSION (0x4000)
+/* Default: Do not use HFS+ compression if it was not compressed. */
+/* This has no effect except on Mac OS v10.6 or later. */
+#define ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED (0x8000)
+/* Default: Do not reject entries with absolute paths */
+#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)
+/* Default: Do not clear no-change flags when unlinking object */
+#define ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS (0x20000)
+/* Default: Do not extract atomically (using rename) */
+#define ARCHIVE_EXTRACT_SAFE_WRITES (0x40000)
+
+__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
+ int flags);
+__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *,
+ struct archive * /* dest */);
+__LA_DECL void archive_read_extract_set_progress_callback(struct archive *,
+ void (*_progress_func)(void *), void *_user_data);
+
+/* Record the dev/ino of a file that will not be written. This is
+ * generally set to the dev/ino of the archive being read. */
+__LA_DECL void archive_read_extract_set_skip_file(struct archive *,
+ la_int64_t, la_int64_t);
+
+/* Close the file and release most resources. */
+__LA_DECL int archive_read_close(struct archive *);
+/* Release all resources and destroy the object. */
+/* Note that archive_read_free will call archive_read_close for you. */
+__LA_DECL int archive_read_free(struct archive *);
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Synonym for archive_read_free() for backwards compatibility. */
+__LA_DECL int archive_read_finish(struct archive *) __LA_DEPRECATED;
+#endif
+
+/*-
+ * To create an archive:
+ * 1) Ask archive_write_new for an archive writer object.
+ * 2) Set any global properties. In particular, you should set
+ * the compression and format to use.
+ * 3) Call archive_write_open to open the file (most people
+ * will use archive_write_open_file or archive_write_open_fd,
+ * which provide convenient canned I/O callbacks for you).
+ * 4) For each entry:
+ * - construct an appropriate struct archive_entry structure
+ * - archive_write_header to write the header
+ * - archive_write_data to write the entry data
+ * 5) archive_write_close to close the output
+ * 6) archive_write_free to cleanup the writer and release resources
+ */
+__LA_DECL struct archive *archive_write_new(void);
+__LA_DECL int archive_write_set_bytes_per_block(struct archive *,
+ int bytes_per_block);
+__LA_DECL int archive_write_get_bytes_per_block(struct archive *);
+/* XXX This is badly misnamed; suggestions appreciated. XXX */
+__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *,
+ int bytes_in_last_block);
+__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *);
+
+/* The dev/ino of a file that won't be archived. This is used
+ * to avoid recursively adding an archive to itself. */
+__LA_DECL int archive_write_set_skip_file(struct archive *,
+ la_int64_t, la_int64_t);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+__LA_DECL int archive_write_set_compression_bzip2(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_compress(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_gzip(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_lzip(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_lzma(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_none(struct archive *)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_program(struct archive *,
+ const char *cmd) __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_xz(struct archive *)
+ __LA_DEPRECATED;
+#endif
+
+/* A convenience function to set the filter based on the code. */
+__LA_DECL int archive_write_add_filter(struct archive *, int filter_code);
+__LA_DECL int archive_write_add_filter_by_name(struct archive *,
+ const char *name);
+__LA_DECL int archive_write_add_filter_b64encode(struct archive *);
+__LA_DECL int archive_write_add_filter_bzip2(struct archive *);
+__LA_DECL int archive_write_add_filter_compress(struct archive *);
+__LA_DECL int archive_write_add_filter_grzip(struct archive *);
+__LA_DECL int archive_write_add_filter_gzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lrzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lz4(struct archive *);
+__LA_DECL int archive_write_add_filter_lzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lzma(struct archive *);
+__LA_DECL int archive_write_add_filter_lzop(struct archive *);
+__LA_DECL int archive_write_add_filter_none(struct archive *);
+__LA_DECL int archive_write_add_filter_program(struct archive *,
+ const char *cmd);
+__LA_DECL int archive_write_add_filter_uuencode(struct archive *);
+__LA_DECL int archive_write_add_filter_xz(struct archive *);
+__LA_DECL int archive_write_add_filter_zstd(struct archive *);
+
+
+/* A convenience function to set the format based on the code or name. */
+__LA_DECL int archive_write_set_format(struct archive *, int format_code);
+__LA_DECL int archive_write_set_format_by_name(struct archive *,
+ const char *name);
+/* To minimize link pollution, use one or more of the following. */
+__LA_DECL int archive_write_set_format_7zip(struct archive *);
+__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);
+__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);
+__LA_DECL int archive_write_set_format_cpio(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_bin(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_odc(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_pwb(struct archive *);
+__LA_DECL int archive_write_set_format_gnutar(struct archive *);
+__LA_DECL int archive_write_set_format_iso9660(struct archive *);
+__LA_DECL int archive_write_set_format_mtree(struct archive *);
+__LA_DECL int archive_write_set_format_mtree_classic(struct archive *);
+/* TODO: int archive_write_set_format_old_tar(struct archive *); */
+__LA_DECL int archive_write_set_format_pax(struct archive *);
+__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);
+__LA_DECL int archive_write_set_format_raw(struct archive *);
+__LA_DECL int archive_write_set_format_shar(struct archive *);
+__LA_DECL int archive_write_set_format_shar_dump(struct archive *);
+__LA_DECL int archive_write_set_format_ustar(struct archive *);
+__LA_DECL int archive_write_set_format_v7tar(struct archive *);
+__LA_DECL int archive_write_set_format_warc(struct archive *);
+__LA_DECL int archive_write_set_format_xar(struct archive *);
+__LA_DECL int archive_write_set_format_zip(struct archive *);
+__LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename);
+__LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext);
+__LA_DECL int archive_write_zip_set_compression_deflate(struct archive *);
+__LA_DECL int archive_write_zip_set_compression_store(struct archive *);
+/* Deprecated; use archive_write_open2 instead */
+__LA_DECL int archive_write_open(struct archive *, void *,
+ archive_open_callback *, archive_write_callback *,
+ archive_close_callback *);
+__LA_DECL int archive_write_open2(struct archive *, void *,
+ archive_open_callback *, archive_write_callback *,
+ archive_close_callback *, archive_free_callback *);
+__LA_DECL int archive_write_open_fd(struct archive *, int _fd);
+__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
+__LA_DECL int archive_write_open_filename_w(struct archive *,
+ const wchar_t *_file);
+/* A deprecated synonym for archive_write_open_filename() */
+__LA_DECL int archive_write_open_file(struct archive *, const char *_file)
+ __LA_DEPRECATED;
+__LA_DECL int archive_write_open_FILE(struct archive *, FILE *);
+/* _buffSize is the size of the buffer, _used refers to a variable that
+ * will be updated after each write into the buffer. */
+__LA_DECL int archive_write_open_memory(struct archive *,
+ void *_buffer, size_t _buffSize, size_t *_used);
+
+/*
+ * Note that the library will truncate writes beyond the size provided
+ * to archive_write_header or pad if the provided data is short.
+ */
+__LA_DECL int archive_write_header(struct archive *,
+ struct archive_entry *);
+__LA_DECL la_ssize_t archive_write_data(struct archive *,
+ const void *, size_t);
+
+/* This interface is currently only available for archive_write_disk handles. */
+__LA_DECL la_ssize_t archive_write_data_block(struct archive *,
+ const void *, size_t, la_int64_t);
+
+__LA_DECL int archive_write_finish_entry(struct archive *);
+__LA_DECL int archive_write_close(struct archive *);
+/* Marks the archive as FATAL so that a subsequent free() operation
+ * won't try to close() cleanly. Provides a fast abort capability
+ * when the client discovers that things have gone wrong. */
+__LA_DECL int archive_write_fail(struct archive *);
+/* This can fail if the archive wasn't already closed, in which case
+ * archive_write_free() will implicitly call archive_write_close(). */
+__LA_DECL int archive_write_free(struct archive *);
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Synonym for archive_write_free() for backwards compatibility. */
+__LA_DECL int archive_write_finish(struct archive *) __LA_DEPRECATED;
+#endif
+
+/*
+ * Set write options.
+ */
+/* Apply option to the format only. */
+__LA_DECL int archive_write_set_format_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option to the filter only. */
+__LA_DECL int archive_write_set_filter_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option to both the format and the filter. */
+__LA_DECL int archive_write_set_option(struct archive *_a,
+ const char *m, const char *o,
+ const char *v);
+/* Apply option string to both the format and the filter. */
+__LA_DECL int archive_write_set_options(struct archive *_a,
+ const char *opts);
+
+/*
+ * Set a encryption passphrase.
+ */
+__LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p);
+__LA_DECL int archive_write_set_passphrase_callback(struct archive *,
+ void *client_data, archive_passphrase_callback *);
+
+/*-
+ * ARCHIVE_WRITE_DISK API
+ *
+ * To create objects on disk:
+ * 1) Ask archive_write_disk_new for a new archive_write_disk object.
+ * 2) Set any global properties. In particular, you probably
+ * want to set the options.
+ * 3) For each entry:
+ * - construct an appropriate struct archive_entry structure
+ * - archive_write_header to create the file/dir/etc on disk
+ * - archive_write_data to write the entry data
+ * 4) archive_write_free to cleanup the writer and release resources
+ *
+ * In particular, you can use this in conjunction with archive_read()
+ * to pull entries out of an archive and create them on disk.
+ */
+__LA_DECL struct archive *archive_write_disk_new(void);
+/* This file will not be overwritten. */
+__LA_DECL int archive_write_disk_set_skip_file(struct archive *,
+ la_int64_t, la_int64_t);
+/* Set flags to control how the next item gets created.
+ * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */
+__LA_DECL int archive_write_disk_set_options(struct archive *,
+ int flags);
+/*
+ * The lookup functions are given uname/uid (or gname/gid) pairs and
+ * return a uid (gid) suitable for this system. These are used for
+ * restoring ownership and for setting ACLs. The default functions
+ * are naive, they just return the uid/gid. These are small, so reasonable
+ * for applications that don't need to preserve ownership; they
+ * are probably also appropriate for applications that are doing
+ * same-system backup and restore.
+ */
+/*
+ * The "standard" lookup functions use common system calls to lookup
+ * the uname/gname, falling back to the uid/gid if the names can't be
+ * found. They cache lookups and are reasonably fast, but can be very
+ * large, so they are not used unless you ask for them. In
+ * particular, these match the specifications of POSIX "pax" and old
+ * POSIX "tar".
+ */
+__LA_DECL int archive_write_disk_set_standard_lookup(struct archive *);
+/*
+ * If neither the default (naive) nor the standard (big) functions suit
+ * your needs, you can write your own and register them. Be sure to
+ * include a cleanup function if you have allocated private data.
+ */
+__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,
+ void * /* private_data */,
+ la_int64_t (*)(void *, const char *, la_int64_t),
+ void (* /* cleanup */)(void *));
+__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,
+ void * /* private_data */,
+ la_int64_t (*)(void *, const char *, la_int64_t),
+ void (* /* cleanup */)(void *));
+__LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t);
+__LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t);
+
+/*
+ * ARCHIVE_READ_DISK API
+ *
+ * This is still evolving and somewhat experimental.
+ */
+__LA_DECL struct archive *archive_read_disk_new(void);
+/* The names for symlink modes here correspond to an old BSD
+ * command-line argument convention: -L, -P, -H */
+/* Follow all symlinks. */
+__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *);
+/* Follow no symlinks. */
+__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *);
+/* Follow symlink initially, then not. */
+__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *);
+/* TODO: Handle Linux stat32/stat64 ugliness. <sigh> */
+__LA_DECL int archive_read_disk_entry_from_file(struct archive *,
+ struct archive_entry *, int /* fd */, const struct stat *);
+/* Look up gname for gid or uname for uid. */
+/* Default implementations are very, very stupid. */
+__LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t);
+__LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t);
+/* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the
+ * results for performance. */
+__LA_DECL int archive_read_disk_set_standard_lookup(struct archive *);
+/* You can install your own lookups if you like. */
+__LA_DECL int archive_read_disk_set_gname_lookup(struct archive *,
+ void * /* private_data */,
+ const char *(* /* lookup_fn */)(void *, la_int64_t),
+ void (* /* cleanup_fn */)(void *));
+__LA_DECL int archive_read_disk_set_uname_lookup(struct archive *,
+ void * /* private_data */,
+ const char *(* /* lookup_fn */)(void *, la_int64_t),
+ void (* /* cleanup_fn */)(void *));
+/* Start traversal. */
+__LA_DECL int archive_read_disk_open(struct archive *, const char *);
+__LA_DECL int archive_read_disk_open_w(struct archive *, const wchar_t *);
+/*
+ * Request that current entry be visited. If you invoke it on every
+ * directory, you'll get a physical traversal. This is ignored if the
+ * current entry isn't a directory or a link to a directory. So, if
+ * you invoke this on every returned path, you'll get a full logical
+ * traversal.
+ */
+__LA_DECL int archive_read_disk_descend(struct archive *);
+__LA_DECL int archive_read_disk_can_descend(struct archive *);
+__LA_DECL int archive_read_disk_current_filesystem(struct archive *);
+__LA_DECL int archive_read_disk_current_filesystem_is_synthetic(struct archive *);
+__LA_DECL int archive_read_disk_current_filesystem_is_remote(struct archive *);
+/* Request that the access time of the entry visited by traversal be restored. */
+__LA_DECL int archive_read_disk_set_atime_restored(struct archive *);
+/*
+ * Set behavior. The "flags" argument selects optional behavior.
+ */
+/* Request that the access time of the entry visited by traversal be restored.
+ * This is the same as archive_read_disk_set_atime_restored. */
+#define ARCHIVE_READDISK_RESTORE_ATIME (0x0001)
+/* Default: Do not skip an entry which has nodump flags. */
+#define ARCHIVE_READDISK_HONOR_NODUMP (0x0002)
+/* Default: Skip a mac resource fork file whose prefix is "._" because of
+ * using copyfile. */
+#define ARCHIVE_READDISK_MAC_COPYFILE (0x0004)
+/* Default: Traverse mount points. */
+#define ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS (0x0008)
+/* Default: Xattrs are read from disk. */
+#define ARCHIVE_READDISK_NO_XATTR (0x0010)
+/* Default: ACLs are read from disk. */
+#define ARCHIVE_READDISK_NO_ACL (0x0020)
+/* Default: File flags are read from disk. */
+#define ARCHIVE_READDISK_NO_FFLAGS (0x0040)
+/* Default: Sparse file information is read from disk. */
+#define ARCHIVE_READDISK_NO_SPARSE (0x0080)
+
+__LA_DECL int archive_read_disk_set_behavior(struct archive *,
+ int flags);
+
+/*
+ * Set archive_match object that will be used in archive_read_disk to
+ * know whether an entry should be skipped. The callback function
+ * _excluded_func will be invoked when an entry is skipped by the result
+ * of archive_match.
+ */
+__LA_DECL int archive_read_disk_set_matching(struct archive *,
+ struct archive *_matching, void (*_excluded_func)
+ (struct archive *, void *, struct archive_entry *),
+ void *_client_data);
+__LA_DECL int archive_read_disk_set_metadata_filter_callback(struct archive *,
+ int (*_metadata_filter_func)(struct archive *, void *,
+ struct archive_entry *), void *_client_data);
+
+/* Simplified cleanup interface;
+ * This calls archive_read_free() or archive_write_free() as needed. */
+__LA_DECL int archive_free(struct archive *);
+
+/*
+ * Accessor functions to read/set various information in
+ * the struct archive object:
+ */
+
+/* Number of filters in the current filter pipeline. */
+/* Filter #0 is the one closest to the format, -1 is a synonym for the
+ * last filter, which is always the pseudo-filter that wraps the
+ * client callbacks. */
+__LA_DECL int archive_filter_count(struct archive *);
+__LA_DECL la_int64_t archive_filter_bytes(struct archive *, int);
+__LA_DECL int archive_filter_code(struct archive *, int);
+__LA_DECL const char * archive_filter_name(struct archive *, int);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* These don't properly handle multiple filters, so are deprecated and
+ * will eventually be removed. */
+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */
+__LA_DECL la_int64_t archive_position_compressed(struct archive *)
+ __LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */
+__LA_DECL la_int64_t archive_position_uncompressed(struct archive *)
+ __LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */
+__LA_DECL const char *archive_compression_name(struct archive *)
+ __LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */
+__LA_DECL int archive_compression(struct archive *)
+ __LA_DEPRECATED;
+#endif
+
+__LA_DECL int archive_errno(struct archive *);
+__LA_DECL const char *archive_error_string(struct archive *);
+__LA_DECL const char *archive_format_name(struct archive *);
+__LA_DECL int archive_format(struct archive *);
+__LA_DECL void archive_clear_error(struct archive *);
+__LA_DECL void archive_set_error(struct archive *, int _err,
+ const char *fmt, ...) __LA_PRINTF(3, 4);
+__LA_DECL void archive_copy_error(struct archive *dest,
+ struct archive *src);
+__LA_DECL int archive_file_count(struct archive *);
+
+/*
+ * ARCHIVE_MATCH API
+ */
+__LA_DECL struct archive *archive_match_new(void);
+__LA_DECL int archive_match_free(struct archive *);
+
+/*
+ * Test if archive_entry is excluded.
+ * This is a convenience function. This is the same as calling all
+ * archive_match_path_excluded, archive_match_time_excluded
+ * and archive_match_owner_excluded.
+ */
+__LA_DECL int archive_match_excluded(struct archive *,
+ struct archive_entry *);
+
+/*
+ * Test if pathname is excluded. The conditions are set by following functions.
+ */
+__LA_DECL int archive_match_path_excluded(struct archive *,
+ struct archive_entry *);
+/* Control recursive inclusion of directory content when directory is included. Default on. */
+__LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int);
+/* Add exclusion pathname pattern. */
+__LA_DECL int archive_match_exclude_pattern(struct archive *, const char *);
+__LA_DECL int archive_match_exclude_pattern_w(struct archive *,
+ const wchar_t *);
+/* Add exclusion pathname pattern from file. */
+__LA_DECL int archive_match_exclude_pattern_from_file(struct archive *,
+ const char *, int _nullSeparator);
+__LA_DECL int archive_match_exclude_pattern_from_file_w(struct archive *,
+ const wchar_t *, int _nullSeparator);
+/* Add inclusion pathname pattern. */
+__LA_DECL int archive_match_include_pattern(struct archive *, const char *);
+__LA_DECL int archive_match_include_pattern_w(struct archive *,
+ const wchar_t *);
+/* Add inclusion pathname pattern from file. */
+__LA_DECL int archive_match_include_pattern_from_file(struct archive *,
+ const char *, int _nullSeparator);
+__LA_DECL int archive_match_include_pattern_from_file_w(struct archive *,
+ const wchar_t *, int _nullSeparator);
+/*
+ * How to get statistic information for inclusion patterns.
+ */
+/* Return the amount number of unmatched inclusion patterns. */
+__LA_DECL int archive_match_path_unmatched_inclusions(struct archive *);
+/* Return the pattern of unmatched inclusion with ARCHIVE_OK.
+ * Return ARCHIVE_EOF if there is no inclusion pattern. */
+__LA_DECL int archive_match_path_unmatched_inclusions_next(
+ struct archive *, const char **);
+__LA_DECL int archive_match_path_unmatched_inclusions_next_w(
+ struct archive *, const wchar_t **);
+
+/*
+ * Test if a file is excluded by its time stamp.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int archive_match_time_excluded(struct archive *,
+ struct archive_entry *);
+
+/*
+ * Flags to tell a matching type of time stamps. These are used for
+ * following functions.
+ */
+/* Time flag: mtime to be tested. */
+#define ARCHIVE_MATCH_MTIME (0x0100)
+/* Time flag: ctime to be tested. */
+#define ARCHIVE_MATCH_CTIME (0x0200)
+/* Comparison flag: Match the time if it is newer than. */
+#define ARCHIVE_MATCH_NEWER (0x0001)
+/* Comparison flag: Match the time if it is older than. */
+#define ARCHIVE_MATCH_OLDER (0x0002)
+/* Comparison flag: Match the time if it is equal to. */
+#define ARCHIVE_MATCH_EQUAL (0x0010)
+/* Set inclusion time. */
+__LA_DECL int archive_match_include_time(struct archive *, int _flag,
+ time_t _sec, long _nsec);
+/* Set inclusion time by a date string. */
+__LA_DECL int archive_match_include_date(struct archive *, int _flag,
+ const char *_datestr);
+__LA_DECL int archive_match_include_date_w(struct archive *, int _flag,
+ const wchar_t *_datestr);
+/* Set inclusion time by a particular file. */
+__LA_DECL int archive_match_include_file_time(struct archive *,
+ int _flag, const char *_pathname);
+__LA_DECL int archive_match_include_file_time_w(struct archive *,
+ int _flag, const wchar_t *_pathname);
+/* Add exclusion entry. */
+__LA_DECL int archive_match_exclude_entry(struct archive *,
+ int _flag, struct archive_entry *);
+
+/*
+ * Test if a file is excluded by its uid ,gid, uname or gname.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int archive_match_owner_excluded(struct archive *,
+ struct archive_entry *);
+/* Add inclusion uid, gid, uname and gname. */
+__LA_DECL int archive_match_include_uid(struct archive *, la_int64_t);
+__LA_DECL int archive_match_include_gid(struct archive *, la_int64_t);
+__LA_DECL int archive_match_include_uname(struct archive *, const char *);
+__LA_DECL int archive_match_include_uname_w(struct archive *,
+ const wchar_t *);
+__LA_DECL int archive_match_include_gname(struct archive *, const char *);
+__LA_DECL int archive_match_include_gname_w(struct archive *,
+ const wchar_t *);
+
+/* Utility functions */
+/* Convenience function to sort a NULL terminated list of strings */
+__LA_DECL int archive_utility_string_sort(char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* These are meaningless outside of this header. */
+#undef __LA_DECL
+
+#endif /* !ARCHIVE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_acl.c b/contrib/libs/libarchive/libarchive/archive_acl.c
new file mode 100644
index 0000000000..ead7e36e49
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_acl.c
@@ -0,0 +1,2097 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+#undef max
+#define max(a, b) ((a)>(b)?(a):(b))
+
+#ifndef HAVE_WMEMCMP
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+static int acl_special(struct archive_acl *acl,
+ int type, int permset, int tag);
+static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
+ int type, int permset, int tag, int id);
+static int archive_acl_add_entry_len_l(struct archive_acl *acl,
+ int type, int permset, int tag, int id, const char *name,
+ size_t len, struct archive_string_conv *sc);
+static int archive_acl_text_want_type(struct archive_acl *acl, int flags);
+static ssize_t archive_acl_text_len(struct archive_acl *acl, int want_type,
+ int flags, int wide, struct archive *a,
+ struct archive_string_conv *sc);
+static int isint_w(const wchar_t *start, const wchar_t *end, int *result);
+static int ismode_w(const wchar_t *start, const wchar_t *end, int *result);
+static int is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
+ int *result);
+static int is_nfs4_perms_w(const wchar_t *start, const wchar_t *end,
+ int *result);
+static void next_field_w(const wchar_t **wp, const wchar_t **start,
+ const wchar_t **end, wchar_t *sep);
+static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+ int tag, int flags, const wchar_t *wname, int perm, int id);
+static void append_id_w(wchar_t **wp, int id);
+static int isint(const char *start, const char *end, int *result);
+static int ismode(const char *start, const char *end, int *result);
+static int is_nfs4_flags(const char *start, const char *end,
+ int *result);
+static int is_nfs4_perms(const char *start, const char *end,
+ int *result);
+static void next_field(const char **p, const char **start,
+ const char **end, char *sep);
+static void append_entry(char **p, const char *prefix, int type,
+ int tag, int flags, const char *name, int perm, int id);
+static void append_id(char **p, int id);
+
+static const struct {
+ const int perm;
+ const char c;
+ const wchar_t wc;
+} nfsv4_acl_perm_map[] = {
+ { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
+ L'r' },
+ { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
+ L'w' },
+ { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
+ { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
+ 'p', L'p' },
+ { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
+ { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
+ { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
+ { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
+ { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
+ { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
+ { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
+ { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
+ { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
+ { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
+};
+
+static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
+ sizeof(nfsv4_acl_perm_map[0]));
+
+static const struct {
+ const int perm;
+ const char c;
+ const wchar_t wc;
+} nfsv4_acl_flag_map[] = {
+ { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
+ { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
+};
+
+static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
+ sizeof(nfsv4_acl_flag_map[0]));
+
+void
+archive_acl_clear(struct archive_acl *acl)
+{
+ struct archive_acl_entry *ap;
+
+ while (acl->acl_head != NULL) {
+ ap = acl->acl_head->next;
+ archive_mstring_clean(&acl->acl_head->name);
+ free(acl->acl_head);
+ acl->acl_head = ap;
+ }
+ free(acl->acl_text_w);
+ acl->acl_text_w = NULL;
+ free(acl->acl_text);
+ acl->acl_text = NULL;
+ acl->acl_p = NULL;
+ acl->acl_types = 0;
+ acl->acl_state = 0; /* Not counting. */
+}
+
+void
+archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
+{
+ struct archive_acl_entry *ap, *ap2;
+
+ archive_acl_clear(dest);
+
+ dest->mode = src->mode;
+ ap = src->acl_head;
+ while (ap != NULL) {
+ ap2 = acl_new_entry(dest,
+ ap->type, ap->permset, ap->tag, ap->id);
+ if (ap2 != NULL)
+ archive_mstring_copy(&ap2->name, &ap->name);
+ ap = ap->next;
+ }
+}
+
+int
+archive_acl_add_entry(struct archive_acl *acl,
+ int type, int permset, int tag, int id, const char *name)
+{
+ struct archive_acl_entry *ap;
+
+ if (acl_special(acl, type, permset, tag) == 0)
+ return ARCHIVE_OK;
+ ap = acl_new_entry(acl, type, permset, tag, id);
+ if (ap == NULL) {
+ /* XXX Error XXX */
+ return ARCHIVE_FAILED;
+ }
+ if (name != NULL && *name != '\0')
+ archive_mstring_copy_mbs(&ap->name, name);
+ else
+ archive_mstring_clean(&ap->name);
+ return ARCHIVE_OK;
+}
+
+int
+archive_acl_add_entry_w_len(struct archive_acl *acl,
+ int type, int permset, int tag, int id, const wchar_t *name, size_t len)
+{
+ struct archive_acl_entry *ap;
+
+ if (acl_special(acl, type, permset, tag) == 0)
+ return ARCHIVE_OK;
+ ap = acl_new_entry(acl, type, permset, tag, id);
+ if (ap == NULL) {
+ /* XXX Error XXX */
+ return ARCHIVE_FAILED;
+ }
+ if (name != NULL && *name != L'\0' && len > 0)
+ archive_mstring_copy_wcs_len(&ap->name, name, len);
+ else
+ archive_mstring_clean(&ap->name);
+ return ARCHIVE_OK;
+}
+
+static int
+archive_acl_add_entry_len_l(struct archive_acl *acl,
+ int type, int permset, int tag, int id, const char *name, size_t len,
+ struct archive_string_conv *sc)
+{
+ struct archive_acl_entry *ap;
+ int r;
+
+ if (acl_special(acl, type, permset, tag) == 0)
+ return ARCHIVE_OK;
+ ap = acl_new_entry(acl, type, permset, tag, id);
+ if (ap == NULL) {
+ /* XXX Error XXX */
+ return ARCHIVE_FAILED;
+ }
+ if (name != NULL && *name != '\0' && len > 0) {
+ r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
+ } else {
+ r = 0;
+ archive_mstring_clean(&ap->name);
+ }
+ if (r == 0)
+ return (ARCHIVE_OK);
+ else if (errno == ENOMEM)
+ return (ARCHIVE_FATAL);
+ else
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * If this ACL entry is part of the standard POSIX permissions set,
+ * store the permissions in the stat structure and return zero.
+ */
+static int
+acl_special(struct archive_acl *acl, int type, int permset, int tag)
+{
+ if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+ && ((permset & ~007) == 0)) {
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ acl->mode &= ~0700;
+ acl->mode |= (permset & 7) << 6;
+ return (0);
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ acl->mode &= ~0070;
+ acl->mode |= (permset & 7) << 3;
+ return (0);
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ acl->mode &= ~0007;
+ acl->mode |= permset & 7;
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Allocate and populate a new ACL entry with everything but the
+ * name.
+ */
+static struct archive_acl_entry *
+acl_new_entry(struct archive_acl *acl,
+ int type, int permset, int tag, int id)
+{
+ struct archive_acl_entry *ap, *aq;
+
+ /* Type argument must be a valid NFS4 or POSIX.1e type.
+ * The type must agree with anything already set and
+ * the permset must be compatible. */
+ if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ return (NULL);
+ }
+ if (permset &
+ ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
+ | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
+ return (NULL);
+ }
+ } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+ if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+ return (NULL);
+ }
+ if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
+ return (NULL);
+ }
+ } else {
+ return (NULL);
+ }
+
+ /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_USER:
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ /* Tags valid in both NFS4 and POSIX.1e */
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ /* Tags valid only in POSIX.1e. */
+ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+ return (NULL);
+ }
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ /* Tags valid only in NFS4. */
+ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ return (NULL);
+ }
+ break;
+ default:
+ /* No other values are valid. */
+ return (NULL);
+ }
+
+ free(acl->acl_text_w);
+ acl->acl_text_w = NULL;
+ free(acl->acl_text);
+ acl->acl_text = NULL;
+
+ /*
+ * If there's a matching entry already in the list, overwrite it.
+ * NFSv4 entries may be repeated and are not overwritten.
+ *
+ * TODO: compare names of no id is provided (needs more rework)
+ */
+ ap = acl->acl_head;
+ aq = NULL;
+ while (ap != NULL) {
+ if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
+ ap->type == type && ap->tag == tag && ap->id == id) {
+ if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
+ tag != ARCHIVE_ENTRY_ACL_GROUP)) {
+ ap->permset = permset;
+ return (ap);
+ }
+ }
+ aq = ap;
+ ap = ap->next;
+ }
+
+ /* Add a new entry to the end of the list. */
+ ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
+ if (ap == NULL)
+ return (NULL);
+ if (aq == NULL)
+ acl->acl_head = ap;
+ else
+ aq->next = ap;
+ ap->type = type;
+ ap->tag = tag;
+ ap->id = id;
+ ap->permset = permset;
+ acl->acl_types |= type;
+ return (ap);
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+int
+archive_acl_count(struct archive_acl *acl, int want_type)
+{
+ int count;
+ struct archive_acl_entry *ap;
+
+ count = 0;
+ ap = acl->acl_head;
+ while (ap != NULL) {
+ if ((ap->type & want_type) != 0)
+ count++;
+ ap = ap->next;
+ }
+
+ if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
+ count += 3;
+ return (count);
+}
+
+/*
+ * Return a bitmask of stored ACL types in an ACL list
+ */
+int
+archive_acl_types(struct archive_acl *acl)
+{
+ return (acl->acl_types);
+}
+
+/*
+ * Prepare for reading entries from the ACL data. Returns a count
+ * of entries matching "want_type", or zero if there are no
+ * non-extended ACL entries of that type.
+ */
+int
+archive_acl_reset(struct archive_acl *acl, int want_type)
+{
+ int count, cutoff;
+
+ count = archive_acl_count(acl, want_type);
+
+ /*
+ * If the only entries are the three standard ones,
+ * then don't return any ACL data. (In this case,
+ * client can just use chmod(2) to set permissions.)
+ */
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+ cutoff = 3;
+ else
+ cutoff = 0;
+
+ if (count > cutoff)
+ acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ else
+ acl->acl_state = 0;
+ acl->acl_p = acl->acl_head;
+ return (count);
+}
+
+
+/*
+ * Return the next ACL entry in the list. Fake entries for the
+ * standard permissions and include them in the returned list.
+ */
+int
+archive_acl_next(struct archive *a, struct archive_acl *acl, int want_type,
+ int *type, int *permset, int *tag, int *id, const char **name)
+{
+ *name = NULL;
+ *id = -1;
+
+ /*
+ * The acl_state is either zero (no entries available), -1
+ * (reading from list), or an entry type (retrieve that type
+ * from ae_stat.aest_mode).
+ */
+ if (acl->acl_state == 0)
+ return (ARCHIVE_WARN);
+
+ /* The first three access entries are special. */
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ switch (acl->acl_state) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ *permset = (acl->mode >> 6) & 7;
+ *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ return (ARCHIVE_OK);
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ *permset = (acl->mode >> 3) & 7;
+ *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
+ return (ARCHIVE_OK);
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ *permset = acl->mode & 7;
+ *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ *tag = ARCHIVE_ENTRY_ACL_OTHER;
+ acl->acl_state = -1;
+ acl->acl_p = acl->acl_head;
+ return (ARCHIVE_OK);
+ default:
+ break;
+ }
+ }
+
+ while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
+ acl->acl_p = acl->acl_p->next;
+ if (acl->acl_p == NULL) {
+ acl->acl_state = 0;
+ *type = 0;
+ *permset = 0;
+ *tag = 0;
+ *id = -1;
+ *name = NULL;
+ return (ARCHIVE_EOF); /* End of ACL entries. */
+ }
+ *type = acl->acl_p->type;
+ *permset = acl->acl_p->permset;
+ *tag = acl->acl_p->tag;
+ *id = acl->acl_p->id;
+ if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
+ if (errno == ENOMEM)
+ return (ARCHIVE_FATAL);
+ *name = NULL;
+ }
+ acl->acl_p = acl->acl_p->next;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Determine what type of ACL do we want
+ */
+static int
+archive_acl_text_want_type(struct archive_acl *acl, int flags)
+{
+ int want_type;
+
+ /* Check if ACL is NFSv4 */
+ if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ /* NFSv4 should never mix with POSIX.1e */
+ if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+ return (0);
+ else
+ return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+ }
+
+ /* Now deal with POSIX.1e ACLs */
+
+ want_type = 0;
+ if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+ want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+ want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+
+ /* By default we want both access and default ACLs */
+ if (want_type == 0)
+ return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
+
+ return (want_type);
+}
+
+/*
+ * Calculate ACL text string length
+ */
+static ssize_t
+archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
+ int wide, struct archive *a, struct archive_string_conv *sc) {
+ struct archive_acl_entry *ap;
+ const char *name;
+ const wchar_t *wname;
+ int count, idlen, tmp, r;
+ ssize_t length;
+ size_t len;
+
+ count = 0;
+ length = 0;
+ for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+ if ((ap->type & want_type) == 0)
+ continue;
+ /*
+ * Filemode-mapping ACL entries are stored exclusively in
+ * ap->mode so they should not be in the list
+ */
+ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+ && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+ continue;
+ count++;
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
+ && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+ length += 8; /* "default:" */
+ switch (ap->tag) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ length += 6; /* "owner@" */
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_USER:
+ case ARCHIVE_ENTRY_ACL_MASK:
+ length += 4; /* "user", "mask" */
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ length += 6; /* "group@" */
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ length += 5; /* "group", "other" */
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ length += 9; /* "everyone@" */
+ break;
+ }
+ length += 1; /* colon after tag */
+ if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+ ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ if (wide) {
+ r = archive_mstring_get_wcs(a, &ap->name,
+ &wname);
+ if (r == 0 && wname != NULL)
+ length += wcslen(wname);
+ else if (r < 0 && errno == ENOMEM)
+ return (0);
+ else
+ length += sizeof(uid_t) * 3 + 1;
+ } else {
+ r = archive_mstring_get_mbs_l(a, &ap->name, &name,
+ &len, sc);
+ if (r != 0)
+ return (0);
+ if (len > 0 && name != NULL)
+ length += len;
+ else
+ length += sizeof(uid_t) * 3 + 1;
+ }
+ length += 1; /* colon after user or group name */
+ } else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
+ length += 1; /* 2nd colon empty user,group or other */
+
+ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
+ && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+ && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
+ || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
+ /* Solaris has no colon after other: and mask: */
+ length = length - 1;
+ }
+
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ /* rwxpdDaARWcCos:fdinSFI:deny */
+ length += 27;
+ if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
+ length += 1; /* allow, alarm, audit */
+ } else
+ length += 3; /* rwx */
+
+ if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+ ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
+ (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
+ length += 1; /* colon */
+ /* ID digit count */
+ idlen = 1;
+ tmp = ap->id;
+ while (tmp > 9) {
+ tmp = tmp / 10;
+ idlen++;
+ }
+ length += idlen;
+ }
+ length ++; /* entry separator */
+ }
+
+ /* Add filemode-mapping access entries to the length */
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
+ /* "user::rwx\ngroup::rwx\nother:rwx\n" */
+ length += 31;
+ } else {
+ /* "user::rwx\ngroup::rwx\nother::rwx\n" */
+ length += 32;
+ }
+ } else if (count == 0)
+ return (0);
+
+ /* The terminating character is included in count */
+ return (length);
+}
+
+/*
+ * Generate a wide text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+wchar_t *
+archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
+ struct archive *a)
+{
+ int count;
+ ssize_t length;
+ size_t len;
+ const wchar_t *wname;
+ const wchar_t *prefix;
+ wchar_t separator;
+ struct archive_acl_entry *ap;
+ int id, r, want_type;
+ wchar_t *wp, *ws;
+
+ want_type = archive_acl_text_want_type(acl, flags);
+
+ /* Both NFSv4 and POSIX.1 types found */
+ if (want_type == 0)
+ return (NULL);
+
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+ flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+ length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
+
+ if (length == 0)
+ return (NULL);
+
+ if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+ separator = L',';
+ else
+ separator = L'\n';
+
+ /* Now, allocate the string and actually populate it. */
+ wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
+ if (wp == NULL) {
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+ }
+ count = 0;
+
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
+ acl->mode & 0700, -1);
+ *wp++ = separator;
+ append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
+ acl->mode & 0070, -1);
+ *wp++ = separator;
+ append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
+ acl->mode & 0007, -1);
+ count += 3;
+ }
+
+ for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+ if ((ap->type & want_type) == 0)
+ continue;
+ /*
+ * Filemode-mapping ACL entries are stored exclusively in
+ * ap->mode so they should not be in the list
+ */
+ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+ && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+ continue;
+ if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+ (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+ prefix = L"default:";
+ else
+ prefix = NULL;
+ r = archive_mstring_get_wcs(a, &ap->name, &wname);
+ if (r == 0) {
+ if (count > 0)
+ *wp++ = separator;
+ if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+ id = ap->id;
+ else
+ id = -1;
+ append_entry_w(&wp, prefix, ap->type, ap->tag, flags,
+ wname, ap->permset, id);
+ count++;
+ } else if (r < 0 && errno == ENOMEM) {
+ free(ws);
+ return (NULL);
+ }
+ }
+
+ /* Add terminating character */
+ *wp++ = L'\0';
+
+ len = wcslen(ws);
+
+ if ((ssize_t)len > (length - 1))
+ __archive_errx(1, "Buffer overrun");
+
+ if (text_len != NULL)
+ *text_len = len;
+
+ return (ws);
+}
+
+static void
+append_id_w(wchar_t **wp, int id)
+{
+ if (id < 0)
+ id = 0;
+ if (id > 9)
+ append_id_w(wp, id / 10);
+ *(*wp)++ = L"0123456789"[id % 10];
+}
+
+static void
+append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+ int tag, int flags, const wchar_t *wname, int perm, int id)
+{
+ int i;
+
+ if (prefix != NULL) {
+ wcscpy(*wp, prefix);
+ *wp += wcslen(*wp);
+ }
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ wname = NULL;
+ id = -1;
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ wcscpy(*wp, L"owner@");
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_USER:
+ wcscpy(*wp, L"user");
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ wname = NULL;
+ id = -1;
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ wcscpy(*wp, L"group@");
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ wcscpy(*wp, L"group");
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ wcscpy(*wp, L"mask");
+ wname = NULL;
+ id = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ wcscpy(*wp, L"other");
+ wname = NULL;
+ id = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ wcscpy(*wp, L"everyone@");
+ wname = NULL;
+ id = -1;
+ break;
+ }
+ *wp += wcslen(*wp);
+ *(*wp)++ = L':';
+ if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+ tag == ARCHIVE_ENTRY_ACL_USER ||
+ tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ if (wname != NULL) {
+ wcscpy(*wp, wname);
+ *wp += wcslen(*wp);
+ } else if (tag == ARCHIVE_ENTRY_ACL_USER
+ || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ append_id_w(wp, id);
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
+ id = -1;
+ }
+ /* Solaris style has no second colon after other and mask */
+ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+ || (tag != ARCHIVE_ENTRY_ACL_OTHER
+ && tag != ARCHIVE_ENTRY_ACL_MASK))
+ *(*wp)++ = L':';
+ }
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+ /* POSIX.1e ACL perms */
+ *(*wp)++ = (perm & 0444) ? L'r' : L'-';
+ *(*wp)++ = (perm & 0222) ? L'w' : L'-';
+ *(*wp)++ = (perm & 0111) ? L'x' : L'-';
+ } else {
+ /* NFSv4 ACL perms */
+ for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
+ if (perm & nfsv4_acl_perm_map[i].perm)
+ *(*wp)++ = nfsv4_acl_perm_map[i].wc;
+ else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+ *(*wp)++ = L'-';
+ }
+ *(*wp)++ = L':';
+ for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
+ if (perm & nfsv4_acl_flag_map[i].perm)
+ *(*wp)++ = nfsv4_acl_flag_map[i].wc;
+ else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+ *(*wp)++ = L'-';
+ }
+ *(*wp)++ = L':';
+ switch (type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+ wcscpy(*wp, L"allow");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+ wcscpy(*wp, L"deny");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+ wcscpy(*wp, L"audit");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+ wcscpy(*wp, L"alarm");
+ break;
+ default:
+ break;
+ }
+ *wp += wcslen(*wp);
+ }
+ if (id != -1) {
+ *(*wp)++ = L':';
+ append_id_w(wp, id);
+ }
+}
+
+/*
+ * Generate a text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+char *
+archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
+ struct archive_string_conv *sc)
+{
+ int count;
+ ssize_t length;
+ size_t len;
+ const char *name;
+ const char *prefix;
+ char separator;
+ struct archive_acl_entry *ap;
+ int id, r, want_type;
+ char *p, *s;
+
+ want_type = archive_acl_text_want_type(acl, flags);
+
+ /* Both NFSv4 and POSIX.1 types found */
+ if (want_type == 0)
+ return (NULL);
+
+ if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+ flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+ length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
+
+ if (length == 0)
+ return (NULL);
+
+ if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+ separator = ',';
+ else
+ separator = '\n';
+
+ /* Now, allocate the string and actually populate it. */
+ p = s = (char *)malloc(length * sizeof(char));
+ if (p == NULL) {
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+ }
+ count = 0;
+
+ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
+ acl->mode & 0700, -1);
+ *p++ = separator;
+ append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
+ acl->mode & 0070, -1);
+ *p++ = separator;
+ append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+ ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
+ acl->mode & 0007, -1);
+ count += 3;
+ }
+
+ for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+ if ((ap->type & want_type) == 0)
+ continue;
+ /*
+ * Filemode-mapping ACL entries are stored exclusively in
+ * ap->mode so they should not be in the list
+ */
+ if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+ && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+ || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+ continue;
+ if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+ (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+ prefix = "default:";
+ else
+ prefix = NULL;
+ r = archive_mstring_get_mbs_l(
+ NULL, &ap->name, &name, &len, sc);
+ if (r != 0) {
+ free(s);
+ return (NULL);
+ }
+ if (count > 0)
+ *p++ = separator;
+ if (name == NULL ||
+ (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
+ id = ap->id;
+ } else {
+ id = -1;
+ }
+ append_entry(&p, prefix, ap->type, ap->tag, flags, name,
+ ap->permset, id);
+ count++;
+ }
+
+ /* Add terminating character */
+ *p++ = '\0';
+
+ len = strlen(s);
+
+ if ((ssize_t)len > (length - 1))
+ __archive_errx(1, "Buffer overrun");
+
+ if (text_len != NULL)
+ *text_len = len;
+
+ return (s);
+}
+
+static void
+append_id(char **p, int id)
+{
+ if (id < 0)
+ id = 0;
+ if (id > 9)
+ append_id(p, id / 10);
+ *(*p)++ = "0123456789"[id % 10];
+}
+
+static void
+append_entry(char **p, const char *prefix, int type,
+ int tag, int flags, const char *name, int perm, int id)
+{
+ int i;
+
+ if (prefix != NULL) {
+ strcpy(*p, prefix);
+ *p += strlen(*p);
+ }
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ name = NULL;
+ id = -1;
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ strcpy(*p, "owner@");
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_USER:
+ strcpy(*p, "user");
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ name = NULL;
+ id = -1;
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ strcpy(*p, "group@");
+ break;
+ }
+ /* FALLTHROUGH */
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ strcpy(*p, "group");
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ strcpy(*p, "mask");
+ name = NULL;
+ id = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ strcpy(*p, "other");
+ name = NULL;
+ id = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ strcpy(*p, "everyone@");
+ name = NULL;
+ id = -1;
+ break;
+ }
+ *p += strlen(*p);
+ *(*p)++ = ':';
+ if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+ tag == ARCHIVE_ENTRY_ACL_USER ||
+ tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ if (name != NULL) {
+ strcpy(*p, name);
+ *p += strlen(*p);
+ } else if (tag == ARCHIVE_ENTRY_ACL_USER
+ || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ append_id(p, id);
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
+ id = -1;
+ }
+ /* Solaris style has no second colon after other and mask */
+ if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+ || (tag != ARCHIVE_ENTRY_ACL_OTHER
+ && tag != ARCHIVE_ENTRY_ACL_MASK))
+ *(*p)++ = ':';
+ }
+ if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+ /* POSIX.1e ACL perms */
+ *(*p)++ = (perm & 0444) ? 'r' : '-';
+ *(*p)++ = (perm & 0222) ? 'w' : '-';
+ *(*p)++ = (perm & 0111) ? 'x' : '-';
+ } else {
+ /* NFSv4 ACL perms */
+ for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
+ if (perm & nfsv4_acl_perm_map[i].perm)
+ *(*p)++ = nfsv4_acl_perm_map[i].c;
+ else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+ *(*p)++ = '-';
+ }
+ *(*p)++ = ':';
+ for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
+ if (perm & nfsv4_acl_flag_map[i].perm)
+ *(*p)++ = nfsv4_acl_flag_map[i].c;
+ else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+ *(*p)++ = '-';
+ }
+ *(*p)++ = ':';
+ switch (type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+ strcpy(*p, "allow");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+ strcpy(*p, "deny");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+ strcpy(*p, "audit");
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+ strcpy(*p, "alarm");
+ break;
+ }
+ *p += strlen(*p);
+ }
+ if (id != -1) {
+ *(*p)++ = ':';
+ append_id(p, id);
+ }
+}
+
+/*
+ * Parse a wide ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
+ */
+int
+archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
+ int want_type)
+{
+ struct {
+ const wchar_t *start;
+ const wchar_t *end;
+ } field[6], name;
+
+ const wchar_t *s, *st;
+
+ int numfields, fields, n, r, sol, ret;
+ int type, types, tag, permset, id;
+ size_t len;
+ wchar_t sep;
+
+ ret = ARCHIVE_OK;
+ types = 0;
+
+ switch (want_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+ want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ __LA_FALLTHROUGH;
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ numfields = 5;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+ numfields = 6;
+ break;
+ default:
+ return (ARCHIVE_FATAL);
+ }
+
+ while (text != NULL && *text != L'\0') {
+ /*
+ * Parse the fields out of the next entry,
+ * advance 'text' to start of next entry.
+ */
+ fields = 0;
+ do {
+ const wchar_t *start, *end;
+ next_field_w(&text, &start, &end, &sep);
+ if (fields < numfields) {
+ field[fields].start = start;
+ field[fields].end = end;
+ }
+ ++fields;
+ } while (sep == L':');
+
+ /* Set remaining fields to blank. */
+ for (n = fields; n < numfields; ++n)
+ field[n].start = field[n].end = NULL;
+
+ if (field[0].start != NULL && *(field[0].start) == L'#') {
+ /* Comment, skip entry */
+ continue;
+ }
+
+ n = 0;
+ sol = 0;
+ id = -1;
+ permset = 0;
+ name.start = name.end = NULL;
+
+ if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ /* POSIX.1e ACLs */
+ /*
+ * Default keyword "default:user::rwx"
+ * if found, we have one more field
+ *
+ * We also support old Solaris extension:
+ * "defaultuser::rwx" is the default ACL corresponding
+ * to "user::rwx", etc. valid only for first field
+ */
+ s = field[0].start;
+ len = field[0].end - field[0].start;
+ if (*s == L'd' && (len == 1 || (len >= 7
+ && wmemcmp((s + 1), L"efault", 6) == 0))) {
+ type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+ if (len > 7)
+ field[0].start += 7;
+ else
+ n = 1;
+ } else
+ type = want_type;
+
+ /* Check for a numeric ID in field n+1 or n+3. */
+ isint_w(field[n + 1].start, field[n + 1].end, &id);
+ /* Field n+3 is optional. */
+ if (id == -1 && fields > n+3)
+ isint_w(field[n + 3].start, field[n + 3].end,
+ &id);
+
+ tag = 0;
+ s = field[n].start;
+ st = field[n].start + 1;
+ len = field[n].end - field[n].start;
+
+ switch (*s) {
+ case L'u':
+ if (len == 1 || (len == 4
+ && wmemcmp(st, L"ser", 3) == 0))
+ tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ break;
+ case L'g':
+ if (len == 1 || (len == 5
+ && wmemcmp(st, L"roup", 4) == 0))
+ tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case L'o':
+ if (len == 1 || (len == 5
+ && wmemcmp(st, L"ther", 4) == 0))
+ tag = ARCHIVE_ENTRY_ACL_OTHER;
+ break;
+ case L'm':
+ if (len == 1 || (len == 4
+ && wmemcmp(st, L"ask", 3) == 0))
+ tag = ARCHIVE_ENTRY_ACL_MASK;
+ break;
+ default:
+ break;
+ }
+
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ case ARCHIVE_ENTRY_ACL_MASK:
+ if (fields == (n + 2)
+ && field[n + 1].start < field[n + 1].end
+ && ismode_w(field[n + 1].start,
+ field[n + 1].end, &permset)) {
+ /* This is Solaris-style "other:rwx" */
+ sol = 1;
+ } else if (fields == (n + 3) &&
+ field[n + 1].start < field[n + 1].end) {
+ /* Invalid mask or other field */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ break;
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ if (id != -1 ||
+ field[n + 1].start < field[n + 1].end) {
+ name = field[n + 1];
+ if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+ tag = ARCHIVE_ENTRY_ACL_USER;
+ else
+ tag = ARCHIVE_ENTRY_ACL_GROUP;
+ }
+ break;
+ default:
+ /* Invalid tag, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
+ /*
+ * Without "default:" we expect mode in field 2
+ * Exception: Solaris other and mask fields
+ */
+ if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
+ field[n + 2 - sol].end, &permset)) {
+ /* Invalid mode, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ } else {
+ /* NFS4 ACLs */
+ s = field[0].start;
+ len = field[0].end - field[0].start;
+ tag = 0;
+
+ switch (len) {
+ case 4:
+ if (wmemcmp(s, L"user", 4) == 0)
+ tag = ARCHIVE_ENTRY_ACL_USER;
+ break;
+ case 5:
+ if (wmemcmp(s, L"group", 5) == 0)
+ tag = ARCHIVE_ENTRY_ACL_GROUP;
+ break;
+ case 6:
+ if (wmemcmp(s, L"owner@", 6) == 0)
+ tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ else if (wmemcmp(s, L"group@", len) == 0)
+ tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case 9:
+ if (wmemcmp(s, L"everyone@", 9) == 0)
+ tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+ default:
+ break;
+ }
+
+ if (tag == 0) {
+ /* Invalid tag, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+ tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ n = 1;
+ name = field[1];
+ isint_w(name.start, name.end, &id);
+ } else
+ n = 0;
+
+ if (!is_nfs4_perms_w(field[1 + n].start,
+ field[1 + n].end, &permset)) {
+ /* Invalid NFSv4 perms, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ if (!is_nfs4_flags_w(field[2 + n].start,
+ field[2 + n].end, &permset)) {
+ /* Invalid NFSv4 flags, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ s = field[3 + n].start;
+ len = field[3 + n].end - field[3 + n].start;
+ type = 0;
+ if (len == 4) {
+ if (wmemcmp(s, L"deny", 4) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+ } else if (len == 5) {
+ if (wmemcmp(s, L"allow", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+ else if (wmemcmp(s, L"audit", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+ else if (wmemcmp(s, L"alarm", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+ }
+ if (type == 0) {
+ /* Invalid entry type, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ isint_w(field[4 + n].start, field[4 + n].end, &id);
+ }
+
+ /* Add entry to the internal list. */
+ r = archive_acl_add_entry_w_len(acl, type, permset,
+ tag, id, name.start, name.end - name.start);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r != ARCHIVE_OK)
+ ret = ARCHIVE_WARN;
+ types |= type;
+ }
+
+ /* Reset ACL */
+ archive_acl_reset(acl, types);
+
+ return (ret);
+}
+
+/*
+ * Parse a string to a positive decimal integer. Returns true if
+ * the string is non-empty and consists only of decimal digits,
+ * false otherwise.
+ */
+static int
+isint_w(const wchar_t *start, const wchar_t *end, int *result)
+{
+ int n = 0;
+ if (start >= end)
+ return (0);
+ while (start < end) {
+ if (*start < L'0' || *start > L'9')
+ return (0);
+ if (n > (INT_MAX / 10) ||
+ (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
+ n = INT_MAX;
+ } else {
+ n *= 10;
+ n += *start - L'0';
+ }
+ start++;
+ }
+ *result = n;
+ return (1);
+}
+
+/*
+ * Parse a string as a mode field. Returns true if
+ * the string is non-empty and consists only of mode characters,
+ * false otherwise.
+ */
+static int
+ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+ const wchar_t *p;
+
+ if (start >= end)
+ return (0);
+ p = start;
+ *permset = 0;
+ while (p < end) {
+ switch (*p++) {
+ case L'r': case L'R':
+ *permset |= ARCHIVE_ENTRY_ACL_READ;
+ break;
+ case L'w': case L'W':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE;
+ break;
+ case L'x': case L'X':
+ *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+ break;
+ case L'-':
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+ const wchar_t *p = start;
+
+ while (p < end) {
+ switch (*p++) {
+ case L'r':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+ break;
+ case L'w':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+ break;
+ case L'x':
+ *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+ break;
+ case L'p':
+ *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+ break;
+ case L'D':
+ *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+ break;
+ case L'd':
+ *permset |= ARCHIVE_ENTRY_ACL_DELETE;
+ break;
+ case L'a':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+ break;
+ case L'A':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+ break;
+ case L'R':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+ break;
+ case L'W':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+ break;
+ case L'c':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+ break;
+ case L'C':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+ break;
+ case L'o':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+ break;
+ case L's':
+ *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+ break;
+ case L'-':
+ break;
+ default:
+ return(0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+ const wchar_t *p = start;
+
+ while (p < end) {
+ switch(*p++) {
+ case L'f':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+ break;
+ case L'd':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+ break;
+ case L'i':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+ break;
+ case L'n':
+ *permset |=
+ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+ break;
+ case L'S':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+ break;
+ case L'F':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+ break;
+ case L'I':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+ break;
+ case L'-':
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
+ * to point to just after the separator. *start points to the first
+ * character of the matched text and *end just after the last
+ * character of the matched identifier. In particular *end - *start
+ * is the length of the field body, not including leading or trailing
+ * whitespace.
+ */
+static void
+next_field_w(const wchar_t **wp, const wchar_t **start,
+ const wchar_t **end, wchar_t *sep)
+{
+ /* Skip leading whitespace to find start of field. */
+ while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
+ (*wp)++;
+ }
+ *start = *wp;
+
+ /* Scan for the separator. */
+ while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
+ **wp != L'\n' && **wp != L'#') {
+ (*wp)++;
+ }
+ *sep = **wp;
+
+ /* Locate end of field, trim trailing whitespace if necessary */
+ if (*wp == *start) {
+ *end = *wp;
+ } else {
+ *end = *wp - 1;
+ while (**end == L' ' || **end == L'\t' || **end == L'\n') {
+ (*end)--;
+ }
+ (*end)++;
+ }
+
+ /* Handle in-field comments */
+ if (*sep == L'#') {
+ while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
+ (*wp)++;
+ }
+ *sep = **wp;
+ }
+
+ /* Adjust scanner location. */
+ if (**wp != L'\0')
+ (*wp)++;
+}
+
+/*
+ * Parse an ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
+ */
+int
+archive_acl_from_text_l(struct archive_acl *acl, const char *text,
+ int want_type, struct archive_string_conv *sc)
+{
+ struct {
+ const char *start;
+ const char *end;
+ } field[6], name;
+
+ const char *s, *st;
+ int numfields, fields, n, r, sol, ret;
+ int type, types, tag, permset, id;
+ size_t len;
+ char sep;
+
+ switch (want_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+ want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ __LA_FALLTHROUGH;
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ numfields = 5;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+ numfields = 6;
+ break;
+ default:
+ return (ARCHIVE_FATAL);
+ }
+
+ ret = ARCHIVE_OK;
+ types = 0;
+
+ while (text != NULL && *text != '\0') {
+ /*
+ * Parse the fields out of the next entry,
+ * advance 'text' to start of next entry.
+ */
+ fields = 0;
+ do {
+ const char *start, *end;
+ next_field(&text, &start, &end, &sep);
+ if (fields < numfields) {
+ field[fields].start = start;
+ field[fields].end = end;
+ }
+ ++fields;
+ } while (sep == ':');
+
+ /* Set remaining fields to blank. */
+ for (n = fields; n < numfields; ++n)
+ field[n].start = field[n].end = NULL;
+
+ if (field[0].start != NULL && *(field[0].start) == '#') {
+ /* Comment, skip entry */
+ continue;
+ }
+
+ n = 0;
+ sol = 0;
+ id = -1;
+ permset = 0;
+ name.start = name.end = NULL;
+
+ if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ /* POSIX.1e ACLs */
+ /*
+ * Default keyword "default:user::rwx"
+ * if found, we have one more field
+ *
+ * We also support old Solaris extension:
+ * "defaultuser::rwx" is the default ACL corresponding
+ * to "user::rwx", etc. valid only for first field
+ */
+ s = field[0].start;
+ len = field[0].end - field[0].start;
+ if (*s == 'd' && (len == 1 || (len >= 7
+ && memcmp((s + 1), "efault", 6) == 0))) {
+ type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+ if (len > 7)
+ field[0].start += 7;
+ else
+ n = 1;
+ } else
+ type = want_type;
+
+ /* Check for a numeric ID in field n+1 or n+3. */
+ isint(field[n + 1].start, field[n + 1].end, &id);
+ /* Field n+3 is optional. */
+ if (id == -1 && fields > (n + 3))
+ isint(field[n + 3].start, field[n + 3].end,
+ &id);
+
+ tag = 0;
+ s = field[n].start;
+ st = field[n].start + 1;
+ len = field[n].end - field[n].start;
+
+ if (len == 0) {
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
+ switch (*s) {
+ case 'u':
+ if (len == 1 || (len == 4
+ && memcmp(st, "ser", 3) == 0))
+ tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ break;
+ case 'g':
+ if (len == 1 || (len == 5
+ && memcmp(st, "roup", 4) == 0))
+ tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case 'o':
+ if (len == 1 || (len == 5
+ && memcmp(st, "ther", 4) == 0))
+ tag = ARCHIVE_ENTRY_ACL_OTHER;
+ break;
+ case 'm':
+ if (len == 1 || (len == 4
+ && memcmp(st, "ask", 3) == 0))
+ tag = ARCHIVE_ENTRY_ACL_MASK;
+ break;
+ default:
+ break;
+ }
+
+ switch (tag) {
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ case ARCHIVE_ENTRY_ACL_MASK:
+ if (fields == (n + 2)
+ && field[n + 1].start < field[n + 1].end
+ && ismode(field[n + 1].start,
+ field[n + 1].end, &permset)) {
+ /* This is Solaris-style "other:rwx" */
+ sol = 1;
+ } else if (fields == (n + 3) &&
+ field[n + 1].start < field[n + 1].end) {
+ /* Invalid mask or other field */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ break;
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ if (id != -1 ||
+ field[n + 1].start < field[n + 1].end) {
+ name = field[n + 1];
+ if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+ tag = ARCHIVE_ENTRY_ACL_USER;
+ else
+ tag = ARCHIVE_ENTRY_ACL_GROUP;
+ }
+ break;
+ default:
+ /* Invalid tag, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
+ /*
+ * Without "default:" we expect mode in field 3
+ * Exception: Solaris other and mask fields
+ */
+ if (permset == 0 && !ismode(field[n + 2 - sol].start,
+ field[n + 2 - sol].end, &permset)) {
+ /* Invalid mode, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ } else {
+ /* NFS4 ACLs */
+ s = field[0].start;
+ len = field[0].end - field[0].start;
+ tag = 0;
+
+ switch (len) {
+ case 4:
+ if (memcmp(s, "user", 4) == 0)
+ tag = ARCHIVE_ENTRY_ACL_USER;
+ break;
+ case 5:
+ if (memcmp(s, "group", 5) == 0)
+ tag = ARCHIVE_ENTRY_ACL_GROUP;
+ break;
+ case 6:
+ if (memcmp(s, "owner@", 6) == 0)
+ tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ else if (memcmp(s, "group@", 6) == 0)
+ tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case 9:
+ if (memcmp(s, "everyone@", 9) == 0)
+ tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+ break;
+ default:
+ break;
+ }
+
+ if (tag == 0) {
+ /* Invalid tag, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ } else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+ tag == ARCHIVE_ENTRY_ACL_GROUP) {
+ n = 1;
+ name = field[1];
+ isint(name.start, name.end, &id);
+ } else
+ n = 0;
+
+ if (!is_nfs4_perms(field[1 + n].start,
+ field[1 + n].end, &permset)) {
+ /* Invalid NFSv4 perms, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ if (!is_nfs4_flags(field[2 + n].start,
+ field[2 + n].end, &permset)) {
+ /* Invalid NFSv4 flags, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ s = field[3 + n].start;
+ len = field[3 + n].end - field[3 + n].start;
+ type = 0;
+ if (len == 4) {
+ if (memcmp(s, "deny", 4) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+ } else if (len == 5) {
+ if (memcmp(s, "allow", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+ else if (memcmp(s, "audit", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+ else if (memcmp(s, "alarm", 5) == 0)
+ type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+ }
+ if (type == 0) {
+ /* Invalid entry type, skip entry */
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+ isint(field[4 + n].start, field[4 + n].end,
+ &id);
+ }
+
+ /* Add entry to the internal list. */
+ r = archive_acl_add_entry_len_l(acl, type, permset,
+ tag, id, name.start, name.end - name.start, sc);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r != ARCHIVE_OK)
+ ret = ARCHIVE_WARN;
+ types |= type;
+ }
+
+ /* Reset ACL */
+ archive_acl_reset(acl, types);
+
+ return (ret);
+}
+
+/*
+ * Parse a string to a positive decimal integer. Returns true if
+ * the string is non-empty and consists only of decimal digits,
+ * false otherwise.
+ */
+static int
+isint(const char *start, const char *end, int *result)
+{
+ int n = 0;
+ if (start >= end)
+ return (0);
+ while (start < end) {
+ if (*start < '0' || *start > '9')
+ return (0);
+ if (n > (INT_MAX / 10) ||
+ (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
+ n = INT_MAX;
+ } else {
+ n *= 10;
+ n += *start - '0';
+ }
+ start++;
+ }
+ *result = n;
+ return (1);
+}
+
+/*
+ * Parse a string as a mode field. Returns true if
+ * the string is non-empty and consists only of mode characters,
+ * false otherwise.
+ */
+static int
+ismode(const char *start, const char *end, int *permset)
+{
+ const char *p;
+
+ if (start >= end)
+ return (0);
+ p = start;
+ *permset = 0;
+ while (p < end) {
+ switch (*p++) {
+ case 'r': case 'R':
+ *permset |= ARCHIVE_ENTRY_ACL_READ;
+ break;
+ case 'w': case 'W':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE;
+ break;
+ case 'x': case 'X':
+ *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+ break;
+ case '-':
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms(const char *start, const char *end, int *permset)
+{
+ const char *p = start;
+
+ while (p < end) {
+ switch (*p++) {
+ case 'r':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+ break;
+ case 'w':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+ break;
+ case 'x':
+ *permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+ break;
+ case 'p':
+ *permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+ break;
+ case 'D':
+ *permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+ break;
+ case 'd':
+ *permset |= ARCHIVE_ENTRY_ACL_DELETE;
+ break;
+ case 'a':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+ break;
+ case 'A':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+ break;
+ case 'R':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+ break;
+ case 'W':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+ break;
+ case 'c':
+ *permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+ break;
+ case 'C':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+ break;
+ case 'o':
+ *permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+ break;
+ case 's':
+ *permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+ break;
+ case '-':
+ break;
+ default:
+ return(0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags(const char *start, const char *end, int *permset)
+{
+ const char *p = start;
+
+ while (p < end) {
+ switch(*p++) {
+ case 'f':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+ break;
+ case 'd':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+ break;
+ case 'i':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+ break;
+ case 'n':
+ *permset |=
+ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+ break;
+ case 'S':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+ break;
+ case 'F':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+ break;
+ case 'I':
+ *permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+ break;
+ case '-':
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
+ * to point to just after the separator. *start points to the first
+ * character of the matched text and *end just after the last
+ * character of the matched identifier. In particular *end - *start
+ * is the length of the field body, not including leading or trailing
+ * whitespace.
+ */
+static void
+next_field(const char **p, const char **start,
+ const char **end, char *sep)
+{
+ /* Skip leading whitespace to find start of field. */
+ while (**p == ' ' || **p == '\t' || **p == '\n') {
+ (*p)++;
+ }
+ *start = *p;
+
+ /* Scan for the separator. */
+ while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&
+ **p != '#') {
+ (*p)++;
+ }
+ *sep = **p;
+
+ /* Locate end of field, trim trailing whitespace if necessary */
+ if (*p == *start) {
+ *end = *p;
+ } else {
+ *end = *p - 1;
+ while (**end == ' ' || **end == '\t' || **end == '\n') {
+ (*end)--;
+ }
+ (*end)++;
+ }
+
+ /* Handle in-field comments */
+ if (*sep == '#') {
+ while (**p != '\0' && **p != ',' && **p != '\n') {
+ (*p)++;
+ }
+ *sep = **p;
+ }
+
+ /* Adjust scanner location. */
+ if (**p != '\0')
+ (*p)++;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_acl_private.h b/contrib/libs/libarchive/libarchive/archive_acl_private.h
new file mode 100644
index 0000000000..af108162c6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_acl_private.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED
+#define ARCHIVE_ACL_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_string.h"
+
+struct archive_acl_entry {
+ struct archive_acl_entry *next;
+ int type; /* E.g., access or default */
+ int tag; /* E.g., user/group/other/mask */
+ int permset; /* r/w/x bits */
+ int id; /* uid/gid for user/group */
+ struct archive_mstring name; /* uname/gname */
+};
+
+struct archive_acl {
+ mode_t mode;
+ struct archive_acl_entry *acl_head;
+ struct archive_acl_entry *acl_p;
+ int acl_state; /* See acl_next for details. */
+ wchar_t *acl_text_w;
+ char *acl_text;
+ int acl_types;
+};
+
+void archive_acl_clear(struct archive_acl *);
+void archive_acl_copy(struct archive_acl *, struct archive_acl *);
+int archive_acl_count(struct archive_acl *, int);
+int archive_acl_types(struct archive_acl *);
+int archive_acl_reset(struct archive_acl *, int);
+int archive_acl_next(struct archive *, struct archive_acl *, int,
+ int *, int *, int *, int *, const char **);
+
+int archive_acl_add_entry(struct archive_acl *, int, int, int, int, const char *);
+int archive_acl_add_entry_w_len(struct archive_acl *,
+ int, int, int, int, const wchar_t *, size_t);
+int archive_acl_add_entry_len(struct archive_acl *,
+ int, int, int, int, const char *, size_t);
+
+wchar_t *archive_acl_to_text_w(struct archive_acl *, ssize_t *, int,
+ struct archive *);
+char *archive_acl_to_text_l(struct archive_acl *, ssize_t *, int,
+ struct archive_string_conv *);
+
+/*
+ * ACL text parser.
+ */
+int archive_acl_from_text_w(struct archive_acl *, const wchar_t * /* wtext */,
+ int /* type */);
+int archive_acl_from_text_l(struct archive_acl *, const char * /* text */,
+ int /* type */, struct archive_string_conv *);
+
+#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_check_magic.c b/contrib/libs/libarchive/libarchive/archive_check_magic.c
new file mode 100644
index 0000000000..1f40072f81
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_check_magic.c
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_check_magic.c 201089 2009-12-28 02:20:23Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#include <winbase.h>
+#endif
+
+#include "archive_private.h"
+
+static void
+errmsg(const char *m)
+{
+ size_t s = strlen(m);
+ ssize_t written;
+
+ while (s > 0) {
+ written = write(2, m, s);
+ if (written <= 0)
+ return;
+ m += written;
+ s -= written;
+ }
+}
+
+static __LA_DEAD void
+diediedie(void)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+ /* Cause a breakpoint exception */
+ DebugBreak();
+#endif
+ abort(); /* Terminate the program abnormally. */
+}
+
+static const char *
+state_name(unsigned s)
+{
+ switch (s) {
+ case ARCHIVE_STATE_NEW: return ("new");
+ case ARCHIVE_STATE_HEADER: return ("header");
+ case ARCHIVE_STATE_DATA: return ("data");
+ case ARCHIVE_STATE_EOF: return ("eof");
+ case ARCHIVE_STATE_CLOSED: return ("closed");
+ case ARCHIVE_STATE_FATAL: return ("fatal");
+ default: return ("??");
+ }
+}
+
+static const char *
+archive_handle_type_name(unsigned m)
+{
+ switch (m) {
+ case ARCHIVE_WRITE_MAGIC: return ("archive_write");
+ case ARCHIVE_READ_MAGIC: return ("archive_read");
+ case ARCHIVE_WRITE_DISK_MAGIC: return ("archive_write_disk");
+ case ARCHIVE_READ_DISK_MAGIC: return ("archive_read_disk");
+ case ARCHIVE_MATCH_MAGIC: return ("archive_match");
+ default: return NULL;
+ }
+}
+
+
+static char *
+write_all_states(char *buff, unsigned int states)
+{
+ unsigned int lowbit;
+
+ buff[0] = '\0';
+
+ /* A trick for computing the lowest set bit. */
+ while ((lowbit = states & (1 + ~states)) != 0) {
+ states &= ~lowbit; /* Clear the low bit. */
+ strcat(buff, state_name(lowbit));
+ if (states != 0)
+ strcat(buff, "/");
+ }
+ return buff;
+}
+
+/*
+ * Check magic value and current state.
+ * Magic value mismatches are fatal and result in calls to abort().
+ * State mismatches return ARCHIVE_FATAL.
+ * Otherwise, returns ARCHIVE_OK.
+ *
+ * This is designed to catch serious programming errors that violate
+ * the libarchive API.
+ */
+int
+__archive_check_magic(struct archive *a, unsigned int magic,
+ unsigned int state, const char *function)
+{
+ char states1[64];
+ char states2[64];
+ const char *handle_type;
+
+ /*
+ * If this isn't some form of archive handle,
+ * then the library user has screwed up so bad that
+ * we don't even have a reliable way to report an error.
+ */
+ handle_type = archive_handle_type_name(a->magic);
+
+ if (!handle_type) {
+ errmsg("PROGRAMMER ERROR: Function ");
+ errmsg(function);
+ errmsg(" invoked with invalid archive handle.\n");
+ diediedie();
+ }
+
+ if (a->magic != magic) {
+ archive_set_error(a, -1,
+ "PROGRAMMER ERROR: Function '%s' invoked"
+ " on '%s' archive object, which is not supported.",
+ function,
+ handle_type);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+
+ if ((a->state & state) == 0) {
+ /* If we're already FATAL, don't overwrite the error. */
+ if (a->state != ARCHIVE_STATE_FATAL)
+ archive_set_error(a, -1,
+ "INTERNAL ERROR: Function '%s' invoked with"
+ " archive structure in state '%s',"
+ " should be in state '%s'",
+ function,
+ write_all_states(states1, a->state),
+ write_all_states(states2, state));
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ return ARCHIVE_OK;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_cmdline.c b/contrib/libs/libarchive/libarchive/archive_cmdline.c
new file mode 100644
index 0000000000..5c519cd17f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_cmdline.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_cmdline_private.h"
+#include "archive_string.h"
+
+static int cmdline_set_path(struct archive_cmdline *, const char *);
+static int cmdline_add_arg(struct archive_cmdline *, const char *);
+
+static ssize_t
+extract_quotation(struct archive_string *as, const char *p)
+{
+ const char *s;
+
+ for (s = p + 1; *s;) {
+ if (*s == '\\') {
+ if (s[1] != '\0') {
+ archive_strappend_char(as, s[1]);
+ s += 2;
+ } else
+ s++;
+ } else if (*s == '"')
+ break;
+ else {
+ archive_strappend_char(as, s[0]);
+ s++;
+ }
+ }
+ if (*s != '"')
+ return (ARCHIVE_FAILED);/* Invalid sequence. */
+ return ((ssize_t)(s + 1 - p));
+}
+
+static ssize_t
+get_argument(struct archive_string *as, const char *p)
+{
+ const char *s = p;
+
+ archive_string_empty(as);
+
+ /* Skip beginning space characters. */
+ while (*s != '\0' && *s == ' ')
+ s++;
+ /* Copy non-space characters. */
+ while (*s != '\0' && *s != ' ') {
+ if (*s == '\\') {
+ if (s[1] != '\0') {
+ archive_strappend_char(as, s[1]);
+ s += 2;
+ } else {
+ s++;/* Ignore this character.*/
+ break;
+ }
+ } else if (*s == '"') {
+ ssize_t q = extract_quotation(as, s);
+ if (q < 0)
+ return (ARCHIVE_FAILED);/* Invalid sequence. */
+ s += q;
+ } else {
+ archive_strappend_char(as, s[0]);
+ s++;
+ }
+ }
+ return ((ssize_t)(s - p));
+}
+
+/*
+ * Set up command line arguments.
+ * Returns ARCHIVE_OK if everything okey.
+ * Returns ARCHIVE_FAILED if there is a lack of the `"' terminator or an
+ * empty command line.
+ * Returns ARCHIVE_FATAL if no memory.
+ */
+int
+__archive_cmdline_parse(struct archive_cmdline *data, const char *cmd)
+{
+ struct archive_string as;
+ const char *p;
+ ssize_t al;
+ int r;
+
+ archive_string_init(&as);
+
+ /* Get first argument as a command path. */
+ al = get_argument(&as, cmd);
+ if (al < 0) {
+ r = ARCHIVE_FAILED;/* Invalid sequence. */
+ goto exit_function;
+ }
+ if (archive_strlen(&as) == 0) {
+ r = ARCHIVE_FAILED;/* An empty command path. */
+ goto exit_function;
+ }
+ r = cmdline_set_path(data, as.s);
+ if (r != ARCHIVE_OK)
+ goto exit_function;
+ p = strrchr(as.s, '/');
+ if (p == NULL)
+ p = as.s;
+ else
+ p++;
+ r = cmdline_add_arg(data, p);
+ if (r != ARCHIVE_OK)
+ goto exit_function;
+ cmd += al;
+
+ for (;;) {
+ al = get_argument(&as, cmd);
+ if (al < 0) {
+ r = ARCHIVE_FAILED;/* Invalid sequence. */
+ goto exit_function;
+ }
+ if (al == 0)
+ break;
+ cmd += al;
+ if (archive_strlen(&as) == 0 && *cmd == '\0')
+ break;
+ r = cmdline_add_arg(data, as.s);
+ if (r != ARCHIVE_OK)
+ goto exit_function;
+ }
+ r = ARCHIVE_OK;
+exit_function:
+ archive_string_free(&as);
+ return (r);
+}
+
+/*
+ * Set the program path.
+ */
+static int
+cmdline_set_path(struct archive_cmdline *data, const char *path)
+{
+ char *newptr;
+
+ newptr = realloc(data->path, strlen(path) + 1);
+ if (newptr == NULL)
+ return (ARCHIVE_FATAL);
+ data->path = newptr;
+ strcpy(data->path, path);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Add a argument for the program.
+ */
+static int
+cmdline_add_arg(struct archive_cmdline *data, const char *arg)
+{
+ char **newargv;
+
+ if (data->path == NULL)
+ return (ARCHIVE_FAILED);
+
+ newargv = realloc(data->argv, (data->argc + 2) * sizeof(char *));
+ if (newargv == NULL)
+ return (ARCHIVE_FATAL);
+ data->argv = newargv;
+ data->argv[data->argc] = strdup(arg);
+ if (data->argv[data->argc] == NULL)
+ return (ARCHIVE_FATAL);
+ /* Set the terminator of argv. */
+ data->argv[++data->argc] = NULL;
+ return (ARCHIVE_OK);
+}
+
+struct archive_cmdline *
+__archive_cmdline_allocate(void)
+{
+ return (struct archive_cmdline *)
+ calloc(1, sizeof(struct archive_cmdline));
+}
+
+/*
+ * Release the resources.
+ */
+int
+__archive_cmdline_free(struct archive_cmdline *data)
+{
+
+ if (data) {
+ free(data->path);
+ if (data->argv != NULL) {
+ int i;
+ for (i = 0; data->argv[i] != NULL; i++)
+ free(data->argv[i]);
+ free(data->argv);
+ }
+ free(data);
+ }
+ return (ARCHIVE_OK);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_cmdline_private.h b/contrib/libs/libarchive/libarchive/archive_cmdline_private.h
new file mode 100644
index 0000000000..57a19494fd
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_cmdline_private.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_CMDLINE_PRIVATE_H
+#define ARCHIVE_CMDLINE_PRIVATE_H
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+struct archive_cmdline {
+ char *path;
+ char **argv;
+ int argc;
+};
+
+struct archive_cmdline *__archive_cmdline_allocate(void);
+int __archive_cmdline_parse(struct archive_cmdline *, const char *);
+int __archive_cmdline_free(struct archive_cmdline *);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_cryptor.c b/contrib/libs/libarchive/libarchive/archive_cryptor.c
new file mode 100644
index 0000000000..112baf1613
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_cryptor.c
@@ -0,0 +1,534 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+*/
+
+#include "archive_platform.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include "archive.h"
+#include "archive_cryptor_private.h"
+
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * this file will normally define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist. This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_cryptor_build_hack(void) {
+ return 0;
+}
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len)
+{
+ CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pw,
+ pw_len, salt, salt_len, kCCPRFHmacAlgSHA1, rounds,
+ derived_key, derived_key_len);
+ return 0;
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#ifdef _MSC_VER
+#pragma comment(lib, "Bcrypt.lib")
+#endif
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len)
+{
+ NTSTATUS status;
+ BCRYPT_ALG_HANDLE hAlg;
+
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM,
+ MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if (!BCRYPT_SUCCESS(status))
+ return -1;
+
+ status = BCryptDeriveKeyPBKDF2(hAlg,
+ (PUCHAR)(uintptr_t)pw, (ULONG)pw_len,
+ (PUCHAR)(uintptr_t)salt, (ULONG)salt_len, rounds,
+ (PUCHAR)derived_key, (ULONG)derived_key_len, 0);
+
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+
+ return (BCRYPT_SUCCESS(status)) ? 0: -1;
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_PKCS5_H)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len)
+{
+ mbedtls_md_context_t ctx;
+ const mbedtls_md_info_t *info;
+ int ret;
+
+ mbedtls_md_init(&ctx);
+ info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ if (info == NULL) {
+ mbedtls_md_free(&ctx);
+ return (-1);
+ }
+ ret = mbedtls_md_setup(&ctx, info, 1);
+ if (ret != 0) {
+ mbedtls_md_free(&ctx);
+ return (-1);
+ }
+ ret = mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)pw,
+ pw_len, salt, salt_len, rounds, derived_key_len, derived_key);
+
+ mbedtls_md_free(&ctx);
+ return (ret);
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len) {
+ pbkdf2_hmac_sha1((unsigned)pw_len, (const uint8_t *)pw, rounds,
+ salt_len, salt, derived_key_len, derived_key);
+ return 0;
+}
+
+#elif defined(HAVE_LIBCRYPTO) && defined(HAVE_PKCS5_PBKDF2_HMAC_SHA1)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len) {
+
+ PKCS5_PBKDF2_HMAC_SHA1(pw, pw_len, salt, salt_len, rounds,
+ derived_key_len, derived_key);
+ return 0;
+}
+
+#else
+
+/* Stub */
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len) {
+ (void)pw; /* UNUSED */
+ (void)pw_len; /* UNUSED */
+ (void)salt; /* UNUSED */
+ (void)salt_len; /* UNUSED */
+ (void)rounds; /* UNUSED */
+ (void)derived_key; /* UNUSED */
+ (void)derived_key_len; /* UNUSED */
+ return -1; /* UNSUPPORTED */
+}
+
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+# if MAC_OS_X_VERSION_MAX_ALLOWED < 1090
+# define kCCAlgorithmAES kCCAlgorithmAES128
+# endif
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ CCCryptorStatus r;
+
+ ctx->key_len = key_len;
+ memcpy(ctx->key, key, key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ ctx->encr_pos = AES_BLOCK_SIZE;
+ r = CCCryptorCreateWithMode(kCCEncrypt, kCCModeECB, kCCAlgorithmAES,
+ ccNoPadding, NULL, key, key_len, NULL, 0, 0, 0, &ctx->ctx);
+ return (r == kCCSuccess)? 0: -1;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ CCCryptorRef ref = ctx->ctx;
+ CCCryptorStatus r;
+
+ r = CCCryptorReset(ref, NULL);
+ if (r != kCCSuccess && r != kCCUnimplemented)
+ return -1;
+ r = CCCryptorUpdate(ref, ctx->nonce, AES_BLOCK_SIZE, ctx->encr_buf,
+ AES_BLOCK_SIZE, NULL);
+ return (r == kCCSuccess)? 0: -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ memset(ctx->key, 0, ctx->key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ return 0;
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_KEY_HANDLE hKey;
+ DWORD keyObj_len, aes_key_len;
+ PBYTE keyObj;
+ ULONG result;
+ NTSTATUS status;
+ BCRYPT_KEY_LENGTHS_STRUCT key_lengths;
+
+ ctx->hAlg = NULL;
+ ctx->hKey = NULL;
+ ctx->keyObj = NULL;
+ switch (key_len) {
+ case 16: aes_key_len = 128; break;
+ case 24: aes_key_len = 192; break;
+ case 32: aes_key_len = 256; break;
+ default: return -1;
+ }
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM,
+ MS_PRIMITIVE_PROVIDER, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return -1;
+ status = BCryptGetProperty(hAlg, BCRYPT_KEY_LENGTHS, (PUCHAR)&key_lengths,
+ sizeof(key_lengths), &result, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ if (key_lengths.dwMinLength > aes_key_len
+ || key_lengths.dwMaxLength < aes_key_len) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObj_len,
+ sizeof(keyObj_len), &result, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ keyObj = (PBYTE)HeapAlloc(GetProcessHeap(), 0, keyObj_len);
+ if (keyObj == NULL) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE,
+ (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ HeapFree(GetProcessHeap(), 0, keyObj);
+ return -1;
+ }
+ status = BCryptGenerateSymmetricKey(hAlg, &hKey,
+ keyObj, keyObj_len,
+ (PUCHAR)(uintptr_t)key, (ULONG)key_len, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ HeapFree(GetProcessHeap(), 0, keyObj);
+ return -1;
+ }
+
+ ctx->hAlg = hAlg;
+ ctx->hKey = hKey;
+ ctx->keyObj = keyObj;
+ ctx->keyObj_len = keyObj_len;
+ ctx->encr_pos = AES_BLOCK_SIZE;
+
+ return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ NTSTATUS status;
+ ULONG result;
+
+ status = BCryptEncrypt(ctx->hKey, (PUCHAR)ctx->nonce, AES_BLOCK_SIZE,
+ NULL, NULL, 0, (PUCHAR)ctx->encr_buf, AES_BLOCK_SIZE,
+ &result, 0);
+ return BCRYPT_SUCCESS(status) ? 0 : -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+
+ if (ctx->hAlg != NULL) {
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+ ctx->hAlg = NULL;
+ BCryptDestroyKey(ctx->hKey);
+ ctx->hKey = NULL;
+ HeapFree(GetProcessHeap(), 0, ctx->keyObj);
+ ctx->keyObj = NULL;
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ return 0;
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ mbedtls_aes_init(&ctx->ctx);
+ ctx->key_len = key_len;
+ memcpy(ctx->key, key, key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ ctx->encr_pos = AES_BLOCK_SIZE;
+ return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ if (mbedtls_aes_setkey_enc(&ctx->ctx, ctx->key,
+ ctx->key_len * 8) != 0)
+ return (-1);
+ if (mbedtls_aes_crypt_ecb(&ctx->ctx, MBEDTLS_AES_ENCRYPT, ctx->nonce,
+ ctx->encr_buf) != 0)
+ return (-1);
+ return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ mbedtls_aes_free(&ctx->ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ return 0;
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ ctx->key_len = key_len;
+ memcpy(ctx->key, key, key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ ctx->encr_pos = AES_BLOCK_SIZE;
+ memset(&ctx->ctx, 0, sizeof(ctx->ctx));
+ return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+#if NETTLE_VERSION_MAJOR < 3
+ aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key);
+ aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce);
+#else
+ switch(ctx->key_len) {
+ case AES128_KEY_SIZE:
+ aes128_set_encrypt_key(&ctx->ctx.c128, ctx->key);
+ aes128_encrypt(&ctx->ctx.c128, AES_BLOCK_SIZE, ctx->encr_buf,
+ ctx->nonce);
+ break;
+ case AES192_KEY_SIZE:
+ aes192_set_encrypt_key(&ctx->ctx.c192, ctx->key);
+ aes192_encrypt(&ctx->ctx.c192, AES_BLOCK_SIZE, ctx->encr_buf,
+ ctx->nonce);
+ break;
+ case AES256_KEY_SIZE:
+ aes256_set_encrypt_key(&ctx->ctx.c256, ctx->key);
+ aes256_encrypt(&ctx->ctx.c256, AES_BLOCK_SIZE, ctx->encr_buf,
+ ctx->nonce);
+ break;
+ default:
+ return -1;
+ break;
+ }
+#endif
+ return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ return 0;
+}
+
+#elif defined(HAVE_LIBCRYPTO)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ if ((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL)
+ return -1;
+
+ switch (key_len) {
+ case 16: ctx->type = EVP_aes_128_ecb(); break;
+ case 24: ctx->type = EVP_aes_192_ecb(); break;
+ case 32: ctx->type = EVP_aes_256_ecb(); break;
+ default: ctx->type = NULL; return -1;
+ }
+
+ ctx->key_len = key_len;
+ memcpy(ctx->key, key, key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ ctx->encr_pos = AES_BLOCK_SIZE;
+ return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ int outl = 0;
+ int r;
+
+ r = EVP_EncryptInit_ex(ctx->ctx, ctx->type, NULL, ctx->key, NULL);
+ if (r == 0)
+ return -1;
+ r = EVP_EncryptUpdate(ctx->ctx, ctx->encr_buf, &outl, ctx->nonce,
+ AES_BLOCK_SIZE);
+ if (r == 0 || outl != AES_BLOCK_SIZE)
+ return -1;
+ return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ EVP_CIPHER_CTX_free(ctx->ctx);
+ memset(ctx->key, 0, ctx->key_len);
+ memset(ctx->nonce, 0, sizeof(ctx->nonce));
+ return 0;
+}
+
+#else
+
+#define ARCHIVE_CRYPTOR_STUB
+/* Stub */
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ (void)ctx; /* UNUSED */
+ (void)key; /* UNUSED */
+ (void)key_len; /* UNUSED */
+ return -1;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return 0;
+}
+
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_STUB
+static int
+aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in,
+ size_t in_len, uint8_t * const out, size_t *out_len)
+{
+ (void)ctx; /* UNUSED */
+ (void)in; /* UNUSED */
+ (void)in_len; /* UNUSED */
+ (void)out; /* UNUSED */
+ (void)out_len; /* UNUSED */
+ aes_ctr_encrypt_counter(ctx); /* UNUSED */ /* Fix unused function warning */
+ return -1;
+}
+
+#else
+static void
+aes_ctr_increase_counter(archive_crypto_ctx *ctx)
+{
+ uint8_t *const nonce = ctx->nonce;
+ int j;
+
+ for (j = 0; j < 8; j++) {
+ if (++nonce[j])
+ break;
+ }
+}
+
+static int
+aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in,
+ size_t in_len, uint8_t * const out, size_t *out_len)
+{
+ uint8_t *const ebuf = ctx->encr_buf;
+ unsigned pos = ctx->encr_pos;
+ unsigned max = (unsigned)((in_len < *out_len)? in_len: *out_len);
+ unsigned i;
+
+ for (i = 0; i < max; ) {
+ if (pos == AES_BLOCK_SIZE) {
+ aes_ctr_increase_counter(ctx);
+ if (aes_ctr_encrypt_counter(ctx) != 0)
+ return -1;
+ while (max -i >= AES_BLOCK_SIZE) {
+ for (pos = 0; pos < AES_BLOCK_SIZE; pos++)
+ out[i+pos] = in[i+pos] ^ ebuf[pos];
+ i += AES_BLOCK_SIZE;
+ aes_ctr_increase_counter(ctx);
+ if (aes_ctr_encrypt_counter(ctx) != 0)
+ return -1;
+ }
+ pos = 0;
+ if (i >= max)
+ break;
+ }
+ out[i] = in[i] ^ ebuf[pos++];
+ i++;
+ }
+ ctx->encr_pos = pos;
+ *out_len = i;
+
+ return 0;
+}
+#endif /* ARCHIVE_CRYPTOR_STUB */
+
+
+const struct archive_cryptor __archive_cryptor =
+{
+ &pbkdf2_sha1,
+ &aes_ctr_init,
+ &aes_ctr_update,
+ &aes_ctr_release,
+ &aes_ctr_init,
+ &aes_ctr_update,
+ &aes_ctr_release,
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_cryptor_private.h b/contrib/libs/libarchive/libarchive/archive_cryptor_private.h
new file mode 100644
index 0000000000..82591e6128
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_cryptor_private.h
@@ -0,0 +1,188 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+*/
+
+#ifndef ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED
+#define ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_cryptor.c file will normally define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist. This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_cryptor_build_hack(void);
+
+#ifdef __APPLE__
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+# define ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+# endif
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+#include <CommonCrypto/CommonCryptor.h>
+#include <CommonCrypto/CommonKeyDerivation.h>
+#define AES_BLOCK_SIZE 16
+#define AES_MAX_KEY_SIZE kCCKeySizeAES256
+
+typedef struct {
+ CCCryptorRef ctx;
+ uint8_t key[AES_MAX_KEY_SIZE];
+ unsigned key_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#define AES_MAX_KEY_SIZE 32
+#define AES_BLOCK_SIZE 16
+typedef struct {
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_KEY_HANDLE hKey;
+ PBYTE keyObj;
+ DWORD keyObj_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)
+#error #include <mbedtls/aes.h>
+#error #include <mbedtls/md.h>
+#error #include <mbedtls/pkcs5.h>
+
+#define AES_MAX_KEY_SIZE 32
+#define AES_BLOCK_SIZE 16
+
+typedef struct {
+ mbedtls_aes_context ctx;
+ uint8_t key[AES_MAX_KEY_SIZE];
+ unsigned key_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)
+#if defined(HAVE_NETTLE_PBKDF2_H)
+#error #include <nettle/pbkdf2.h>
+#endif
+#error #include <nettle/aes.h>
+#error #include <nettle/version.h>
+
+typedef struct {
+#if NETTLE_VERSION_MAJOR < 3
+ struct aes_ctx ctx;
+#else
+ union {
+ struct aes128_ctx c128;
+ struct aes192_ctx c192;
+ struct aes256_ctx c256;
+ } ctx;
+#endif
+ uint8_t key[AES_MAX_KEY_SIZE];
+ unsigned key_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBCRYPTO)
+#include "archive_openssl_evp_private.h"
+#define AES_BLOCK_SIZE 16
+#define AES_MAX_KEY_SIZE 32
+
+typedef struct {
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *type;
+ uint8_t key[AES_MAX_KEY_SIZE];
+ unsigned key_len;
+ uint8_t nonce[AES_BLOCK_SIZE];
+ uint8_t encr_buf[AES_BLOCK_SIZE];
+ unsigned encr_pos;
+} archive_crypto_ctx;
+
+#else
+
+#define AES_BLOCK_SIZE 16
+#define AES_MAX_KEY_SIZE 32
+typedef int archive_crypto_ctx;
+
+#endif
+
+/* defines */
+#define archive_pbkdf2_sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)\
+ __archive_cryptor.pbkdf2sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)
+
+#define archive_decrypto_aes_ctr_init(ctx, key, key_len) \
+ __archive_cryptor.decrypto_aes_ctr_init(ctx, key, key_len)
+#define archive_decrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \
+ __archive_cryptor.decrypto_aes_ctr_update(ctx, in, in_len, out, out_len)
+#define archive_decrypto_aes_ctr_release(ctx) \
+ __archive_cryptor.decrypto_aes_ctr_release(ctx)
+
+#define archive_encrypto_aes_ctr_init(ctx, key, key_len) \
+ __archive_cryptor.encrypto_aes_ctr_init(ctx, key, key_len)
+#define archive_encrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \
+ __archive_cryptor.encrypto_aes_ctr_update(ctx, in, in_len, out, out_len)
+#define archive_encrypto_aes_ctr_release(ctx) \
+ __archive_cryptor.encrypto_aes_ctr_release(ctx)
+
+/* Minimal interface to cryptographic functionality for internal use in
+ * libarchive */
+struct archive_cryptor
+{
+ /* PKCS5 PBKDF2 HMAC-SHA1 */
+ int (*pbkdf2sha1)(const char *pw, size_t pw_len, const uint8_t *salt,
+ size_t salt_len, unsigned rounds, uint8_t *derived_key,
+ size_t derived_key_len);
+ /* AES CTR mode(little endian version) */
+ int (*decrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t);
+ int (*decrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *,
+ size_t, uint8_t *, size_t *);
+ int (*decrypto_aes_ctr_release)(archive_crypto_ctx *);
+ int (*encrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t);
+ int (*encrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *,
+ size_t, uint8_t *, size_t *);
+ int (*encrypto_aes_ctr_release)(archive_crypto_ctx *);
+};
+
+extern const struct archive_cryptor __archive_cryptor;
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_digest.c b/contrib/libs/libarchive/libarchive/archive_digest.c
new file mode 100644
index 0000000000..3776831b21
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_digest.c
@@ -0,0 +1,1565 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* Copyright (c) 2011 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+*/
+
+#include "archive_platform.h"
+
+#include "archive.h"
+#include "archive_digest_private.h"
+
+/* In particular, force the configure probe to break if it tries
+ * to test a combination of OpenSSL and libmd. */
+#if defined(ARCHIVE_CRYPTO_OPENSSL) && defined(ARCHIVE_CRYPTO_LIBMD)
+#error Cannot use both OpenSSL and libmd.
+#endif
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+/*
+ * Message digest functions for Windows platform.
+ */
+#if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_WIN)
+
+/*
+ * Initialize a Message digest.
+ */
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+static int
+win_crypto_init(Digest_CTX *ctx, const WCHAR *algo)
+{
+ NTSTATUS status;
+ ctx->valid = 0;
+
+ status = BCryptOpenAlgorithmProvider(&ctx->hAlg, algo, NULL, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return (ARCHIVE_FAILED);
+ status = BCryptCreateHash(ctx->hAlg, &ctx->hHash, NULL, 0, NULL, 0, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+ return (ARCHIVE_FAILED);
+ }
+
+ ctx->valid = 1;
+ return (ARCHIVE_OK);
+}
+#else
+static int
+win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId)
+{
+
+ ctx->valid = 0;
+ if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
+ prov, CRYPT_VERIFYCONTEXT)) {
+ if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
+ return (ARCHIVE_FAILED);
+ if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
+ prov, CRYPT_NEWKEYSET))
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) {
+ CryptReleaseContext(ctx->cryptProv, 0);
+ return (ARCHIVE_FAILED);
+ }
+
+ ctx->valid = 1;
+ return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Update a Message digest.
+ */
+static int
+win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len)
+{
+
+ if (!ctx->valid)
+ return (ARCHIVE_FAILED);
+
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCryptHashData(ctx->hHash,
+ (PUCHAR)(uintptr_t)buf,
+ (ULONG)len, 0);
+#else
+ CryptHashData(ctx->hash,
+ (unsigned char *)(uintptr_t)buf,
+ (DWORD)len, 0);
+#endif
+ return (ARCHIVE_OK);
+}
+
+static int
+win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx)
+{
+#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA)
+ DWORD siglen = (DWORD)bufsize;
+#endif
+
+ if (!ctx->valid)
+ return (ARCHIVE_FAILED);
+
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0);
+ BCryptDestroyHash(ctx->hHash);
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+#else
+ CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0);
+ CryptDestroyHash(ctx->hash);
+ CryptReleaseContext(ctx->cryptProv, 0);
+#endif
+ ctx->valid = 0;
+ return (ARCHIVE_OK);
+}
+
+#endif /* defined(ARCHIVE_CRYPTO_*_WIN) */
+
+
+/* MD5 implementations */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ MD5Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ MD5Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ MD5Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ MD5Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ MD5Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ MD5Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ CC_MD5_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_MD5_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ CC_MD5_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ mbedtls_md5_init(ctx);
+ if (mbedtls_md5_starts_ret(ctx) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_md5_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ if (mbedtls_md5_finish_ret(ctx, md) == 0) {
+ mbedtls_md5_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_md5_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ md5_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ md5_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ md5_digest(ctx, MD5_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_md5()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so
+ * this is meant to cope with that. Real fix is probably to fix
+ * archive_write_set_format_xar.c
+ */
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_WIN)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5));
+#endif
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 16, ctx));
+}
+
+#else
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* RIPEMD160 implementations */
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ RMD160Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ RMD160Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ RMD160Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ RIPEMD160_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ RIPEMD160_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ RIPEMD160_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ mbedtls_ripemd160_init(ctx);
+ if (mbedtls_ripemd160_starts_ret(ctx) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) {
+ mbedtls_ripemd160_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_ripemd160_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ ripemd160_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ ripemd160_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ ripemd160_digest(ctx, RIPEMD160_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_ripemd160()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#else
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA1 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ SHA1Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA1Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ SHA1Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ SHA1_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA1_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ SHA1_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ CC_SHA1_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_SHA1_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ CC_SHA1_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ mbedtls_sha1_init(ctx);
+ if (mbedtls_sha1_starts_ret(ctx) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ if (mbedtls_sha1_finish_ret(ctx, md) == 0) {
+ mbedtls_sha1_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_sha1_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ sha1_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ sha1_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ sha1_digest(ctx, SHA1_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_sha1()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so
+ * this is meant to cope with that. Real fix is probably to fix
+ * archive_write_set_format_xar.c
+ */
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1));
+#endif
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 20, ctx));
+}
+
+#else
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA256 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ SHA256_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA256_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ SHA256_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ SHA256Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA256Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ SHA256Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ SHA256Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA256Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ SHA256Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ SHA256_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA256_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ SHA256_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ CC_SHA256_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_SHA256_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ CC_SHA256_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ mbedtls_sha256_init(ctx);
+ if (mbedtls_sha256_starts_ret(ctx, 0) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ if (mbedtls_sha256_finish_ret(ctx, md) == 0) {
+ mbedtls_sha256_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_sha256_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ sha256_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ sha256_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ sha256_digest(ctx, SHA256_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_sha256()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256));
+#endif
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 32, ctx));
+}
+
+#else
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA384 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ SHA384_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA384_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ SHA384_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ SHA384Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA384Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ SHA384Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ SHA384Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA384Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ SHA384Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ CC_SHA384_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_SHA384_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ CC_SHA384_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ mbedtls_sha512_init(ctx);
+ if (mbedtls_sha512_starts_ret(ctx, 1) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
+ mbedtls_sha512_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_sha512_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ sha384_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ sha384_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ sha384_digest(ctx, SHA384_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_sha384()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384));
+#endif
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 48, ctx));
+}
+
+#else
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA512 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ SHA512_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA512_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ SHA512_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ SHA512Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA512Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ SHA512Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ SHA512Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA512Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ SHA512Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ SHA512_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ SHA512_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ SHA512_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ CC_SHA512_Init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ CC_SHA512_Update(ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ CC_SHA512_Final(md, ctx);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ mbedtls_sha512_init(ctx);
+ if (mbedtls_sha512_starts_ret(ctx, 0) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
+ return (ARCHIVE_OK);
+ else
+ return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
+ mbedtls_sha512_free(ctx);
+ return (ARCHIVE_OK);
+ } else {
+ mbedtls_sha512_free(ctx);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ sha512_init(ctx);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ sha512_update(ctx, insize, indata);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ sha512_digest(ctx, SHA512_DIGEST_SIZE, md);
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ if ((*ctx = EVP_MD_CTX_new()) == NULL)
+ return (ARCHIVE_FAILED);
+ if (!EVP_DigestInit(*ctx, EVP_sha512()))
+ return (ARCHIVE_FAILED);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ EVP_DigestUpdate(*ctx, indata, insize);
+ return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ if (*ctx) {
+ EVP_DigestFinal(*ctx, md, NULL);
+ EVP_MD_CTX_free(*ctx);
+ *ctx = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM));
+#else
+ return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512));
+#endif
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ return (win_crypto_Final(md, 64, ctx));
+}
+
+#else
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+ (void)ctx; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+ size_t insize)
+{
+ (void)ctx; /* UNUSED */
+ (void)indata; /* UNUSED */
+ (void)insize; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+ (void)ctx; /* UNUSED */
+ (void)md; /* UNUSED */
+ return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* NOTE: Message Digest functions are set based on availability and by the
+ * following order of preference.
+ * 1. libc
+ * 2. libc2
+ * 3. libc3
+ * 4. libSystem
+ * 5. Nettle
+ * 6. OpenSSL
+ * 7. libmd
+ * 8. Windows API
+ */
+const struct archive_digest __archive_digest =
+{
+/* MD5 */
+ &__archive_md5init,
+ &__archive_md5update,
+ &__archive_md5final,
+
+/* RIPEMD160 */
+ &__archive_ripemd160init,
+ &__archive_ripemd160update,
+ &__archive_ripemd160final,
+
+/* SHA1 */
+ &__archive_sha1init,
+ &__archive_sha1update,
+ &__archive_sha1final,
+
+/* SHA256 */
+ &__archive_sha256init,
+ &__archive_sha256update,
+ &__archive_sha256final,
+
+/* SHA384 */
+ &__archive_sha384init,
+ &__archive_sha384update,
+ &__archive_sha384final,
+
+/* SHA512 */
+ &__archive_sha512init,
+ &__archive_sha512update,
+ &__archive_sha512final
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_digest_private.h b/contrib/libs/libarchive/libarchive/archive_digest_private.h
new file mode 100644
index 0000000000..05367c22f4
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_digest_private.h
@@ -0,0 +1,426 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* All rights reserved.
+*
+* 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+*/
+
+#ifndef ARCHIVE_DIGEST_PRIVATE_H_INCLUDED
+#define ARCHIVE_DIGEST_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+#ifndef __LIBARCHIVE_CONFIG_H_INCLUDED
+#error "Should have include config.h first!"
+#endif
+
+/*
+ * Crypto support in various Operating Systems:
+ *
+ * NetBSD:
+ * - MD5 and SHA1 in libc: without _ after algorithm name
+ * - SHA2 in libc: with _ after algorithm name
+ *
+ * OpenBSD:
+ * - MD5, SHA1 and SHA2 in libc: without _ after algorithm name
+ * - OpenBSD 4.4 and earlier have SHA2 in libc with _ after algorithm name
+ *
+ * DragonFly and FreeBSD:
+ * - MD5 libmd: without _ after algorithm name
+ * - SHA1, SHA256 and SHA512 in libmd: with _ after algorithm name
+ *
+ * Mac OS X (10.4 and later):
+ * - MD5, SHA1 and SHA2 in libSystem: with CC_ prefix and _ after algorithm name
+ *
+ * OpenSSL:
+ * - MD5, SHA1 and SHA2 in libcrypto: with _ after algorithm name
+ *
+ * Windows:
+ * - MD5, SHA1 and SHA2 in archive_crypto.c using Windows crypto API
+ */
+
+/* libc crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+#error #include <md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+#error #include <rmd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+#error #include <sha1.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+#error #include <sha2.h>
+#endif
+
+/* libmd crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+#define ARCHIVE_CRYPTO_LIBMD 1
+#endif
+
+#if defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+#error #include <md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+#include <ripemd.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+#error #include <sha.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+#error #include <sha256.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+#error #include <sha512.h>
+#endif
+
+/* libSystem crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+#include <CommonCrypto/CommonDigest.h>
+#endif
+
+/* mbed TLS crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+#error #include <mbedtls/md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+#error #include <mbedtls/ripemd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+#error #include <mbedtls/sha1.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+#error #include <mbedtls/sha256.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+#error #include <mbedtls/sha512.h>
+#endif
+
+/* Nettle crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+#error #include <nettle/md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+#error #include <nettle/ripemd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+#error #include <nettle/sha.h>
+#endif
+
+/* OpenSSL crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+#define ARCHIVE_CRYPTO_OPENSSL 1
+#include "archive_openssl_evp_private.h"
+#endif
+
+/* Windows crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+typedef struct {
+ int valid;
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_HASH_HANDLE hHash;
+} Digest_CTX;
+#else
+#include <windows.h>
+#include <wincrypt.h>
+typedef struct {
+ int valid;
+ HCRYPTPROV cryptProv;
+ HCRYPTHASH hash;
+} Digest_CTX;
+#endif
+#endif
+
+/* typedefs */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+typedef MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+typedef MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
+typedef CC_MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+typedef mbedtls_md5_context archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+typedef struct md5_ctx archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
+typedef EVP_MD_CTX *archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_WIN)
+typedef Digest_CTX archive_md5_ctx;
+#else
+typedef unsigned char archive_md5_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+typedef RMD160_CTX archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+typedef RIPEMD160_CTX archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+typedef mbedtls_ripemd160_context archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+typedef struct ripemd160_ctx archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+typedef EVP_MD_CTX *archive_rmd160_ctx;
+#else
+typedef unsigned char archive_rmd160_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+typedef SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+typedef SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
+typedef CC_SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+typedef mbedtls_sha1_context archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
+typedef struct sha1_ctx archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
+typedef EVP_MD_CTX *archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)
+typedef Digest_CTX archive_sha1_ctx;
+#else
+typedef unsigned char archive_sha1_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)
+typedef SHA2_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
+typedef CC_SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+typedef mbedtls_sha256_context archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
+typedef struct sha256_ctx archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
+typedef EVP_MD_CTX *archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)
+typedef Digest_CTX archive_sha256_ctx;
+#else
+typedef unsigned char archive_sha256_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)
+typedef SHA384_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)
+typedef SHA384_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)
+typedef SHA2_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
+typedef CC_SHA512_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
+typedef mbedtls_sha512_context archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
+typedef struct sha384_ctx archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
+typedef EVP_MD_CTX *archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)
+typedef Digest_CTX archive_sha384_ctx;
+#else
+typedef unsigned char archive_sha384_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+typedef SHA2_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+typedef CC_SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+typedef mbedtls_sha512_context archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+typedef struct sha512_ctx archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+typedef EVP_MD_CTX *archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)
+typedef Digest_CTX archive_sha512_ctx;
+#else
+typedef unsigned char archive_sha512_ctx;
+#endif
+
+/* defines */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_MD5_LIBMD) || \
+ defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_MD5_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_MD5_WIN)
+#define ARCHIVE_HAS_MD5
+#endif
+#define archive_md5_init(ctx)\
+ __archive_digest.md5init(ctx)
+#define archive_md5_final(ctx, md)\
+ __archive_digest.md5final(ctx, md)
+#define archive_md5_update(ctx, buf, n)\
+ __archive_digest.md5update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+#define ARCHIVE_HAS_RMD160
+#endif
+#define archive_rmd160_init(ctx)\
+ __archive_digest.rmd160init(ctx)
+#define archive_rmd160_final(ctx, md)\
+ __archive_digest.rmd160final(ctx, md)
+#define archive_rmd160_update(ctx, buf, n)\
+ __archive_digest.rmd160update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_LIBMD) || \
+ defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA1_WIN)
+#define ARCHIVE_HAS_SHA1
+#endif
+#define archive_sha1_init(ctx)\
+ __archive_digest.sha1init(ctx)
+#define archive_sha1_final(ctx, md)\
+ __archive_digest.sha1final(ctx, md)
+#define archive_sha1_update(ctx, buf, n)\
+ __archive_digest.sha1update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA256_WIN)
+#define ARCHIVE_HAS_SHA256
+#endif
+#define archive_sha256_init(ctx)\
+ __archive_digest.sha256init(ctx)
+#define archive_sha256_final(ctx, md)\
+ __archive_digest.sha256final(ctx, md)
+#define archive_sha256_update(ctx, buf, n)\
+ __archive_digest.sha256update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA384_WIN)
+#define ARCHIVE_HAS_SHA384
+#endif
+#define archive_sha384_init(ctx)\
+ __archive_digest.sha384init(ctx)
+#define archive_sha384_final(ctx, md)\
+ __archive_digest.sha384final(ctx, md)
+#define archive_sha384_update(ctx, buf, n)\
+ __archive_digest.sha384update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBC3) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBMD) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_NETTLE) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#define ARCHIVE_HAS_SHA512
+#endif
+#define archive_sha512_init(ctx)\
+ __archive_digest.sha512init(ctx)
+#define archive_sha512_final(ctx, md)\
+ __archive_digest.sha512final(ctx, md)
+#define archive_sha512_update(ctx, buf, n)\
+ __archive_digest.sha512update(ctx, buf, n)
+
+/* Minimal interface to digest functionality for internal use in libarchive */
+struct archive_digest
+{
+ /* Message Digest */
+ int (*md5init)(archive_md5_ctx *ctx);
+ int (*md5update)(archive_md5_ctx *, const void *, size_t);
+ int (*md5final)(archive_md5_ctx *, void *);
+ int (*rmd160init)(archive_rmd160_ctx *);
+ int (*rmd160update)(archive_rmd160_ctx *, const void *, size_t);
+ int (*rmd160final)(archive_rmd160_ctx *, void *);
+ int (*sha1init)(archive_sha1_ctx *);
+ int (*sha1update)(archive_sha1_ctx *, const void *, size_t);
+ int (*sha1final)(archive_sha1_ctx *, void *);
+ int (*sha256init)(archive_sha256_ctx *);
+ int (*sha256update)(archive_sha256_ctx *, const void *, size_t);
+ int (*sha256final)(archive_sha256_ctx *, void *);
+ int (*sha384init)(archive_sha384_ctx *);
+ int (*sha384update)(archive_sha384_ctx *, const void *, size_t);
+ int (*sha384final)(archive_sha384_ctx *, void *);
+ int (*sha512init)(archive_sha512_ctx *);
+ int (*sha512update)(archive_sha512_ctx *, const void *, size_t);
+ int (*sha512final)(archive_sha512_ctx *, void *);
+};
+
+extern const struct archive_digest __archive_digest;
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_endian.h b/contrib/libs/libarchive/libarchive/archive_endian.h
new file mode 100644
index 0000000000..e6d3f2ce5e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_endian.h
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 2002 Thomas Moestl <tmm@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_endian.h 201085 2009-12-28 02:17:15Z kientzle $
+ *
+ * Borrowed from FreeBSD's <sys/endian.h>
+ */
+
+#ifndef ARCHIVE_ENDIAN_H_INCLUDED
+#define ARCHIVE_ENDIAN_H_INCLUDED
+
+/* Note: This is a purely internal header! */
+/* Do not use this outside of libarchive internal code! */
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/*
+ * Disabling inline keyword for compilers known to choke on it:
+ * - Watcom C++ in C code. (For any version?)
+ * - SGI MIPSpro
+ * - Microsoft Visual C++ 6.0 (supposedly newer versions too)
+ * - IBM VisualAge 6 (XL v6)
+ * - Sun WorkShop C (SunPro) before 5.9
+ */
+#if defined(__WATCOMC__) || defined(__sgi) || defined(__hpux) || defined(__BORLANDC__)
+#define inline
+#elif defined(__IBMC__) && __IBMC__ < 700
+#define inline
+#elif defined(__SUNPRO_C) && __SUNPRO_C < 0x590
+#define inline
+#elif defined(_MSC_VER) || defined(__osf__)
+#define inline __inline
+#endif
+
+/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
+
+static inline uint16_t
+archive_be16dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ /* Store into unsigned temporaries before left shifting, to avoid
+ promotion to signed int and then left shifting into the sign bit,
+ which is undefined behaviour. */
+ unsigned int p1 = p[1];
+ unsigned int p0 = p[0];
+
+ return ((p0 << 8) | p1);
+}
+
+static inline uint32_t
+archive_be32dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ /* Store into unsigned temporaries before left shifting, to avoid
+ promotion to signed int and then left shifting into the sign bit,
+ which is undefined behaviour. */
+ unsigned int p3 = p[3];
+ unsigned int p2 = p[2];
+ unsigned int p1 = p[1];
+ unsigned int p0 = p[0];
+
+ return ((p0 << 24) | (p1 << 16) | (p2 << 8) | p3);
+}
+
+static inline uint64_t
+archive_be64dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4));
+}
+
+static inline uint16_t
+archive_le16dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ /* Store into unsigned temporaries before left shifting, to avoid
+ promotion to signed int and then left shifting into the sign bit,
+ which is undefined behaviour. */
+ unsigned int p1 = p[1];
+ unsigned int p0 = p[0];
+
+ return ((p1 << 8) | p0);
+}
+
+static inline uint32_t
+archive_le32dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ /* Store into unsigned temporaries before left shifting, to avoid
+ promotion to signed int and then left shifting into the sign bit,
+ which is undefined behaviour. */
+ unsigned int p3 = p[3];
+ unsigned int p2 = p[2];
+ unsigned int p1 = p[1];
+ unsigned int p0 = p[0];
+
+ return ((p3 << 24) | (p2 << 16) | (p1 << 8) | p0);
+}
+
+static inline uint64_t
+archive_le64dec(const void *pp)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p));
+}
+
+static inline void
+archive_be16enc(void *pp, uint16_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = (u >> 8) & 0xff;
+ p[1] = u & 0xff;
+}
+
+static inline void
+archive_be32enc(void *pp, uint32_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = (u >> 24) & 0xff;
+ p[1] = (u >> 16) & 0xff;
+ p[2] = (u >> 8) & 0xff;
+ p[3] = u & 0xff;
+}
+
+static inline void
+archive_be64enc(void *pp, uint64_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ archive_be32enc(p, (uint32_t)(u >> 32));
+ archive_be32enc(p + 4, (uint32_t)(u & 0xffffffff));
+}
+
+static inline void
+archive_le16enc(void *pp, uint16_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = u & 0xff;
+ p[1] = (u >> 8) & 0xff;
+}
+
+static inline void
+archive_le32enc(void *pp, uint32_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ p[0] = u & 0xff;
+ p[1] = (u >> 8) & 0xff;
+ p[2] = (u >> 16) & 0xff;
+ p[3] = (u >> 24) & 0xff;
+}
+
+static inline void
+archive_le64enc(void *pp, uint64_t u)
+{
+ unsigned char *p = (unsigned char *)pp;
+
+ archive_le32enc(p, (uint32_t)(u & 0xffffffff));
+ archive_le32enc(p + 4, (uint32_t)(u >> 32));
+}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_entry.c b/contrib/libs/libarchive/libarchive/archive_entry.c
new file mode 100644
index 0000000000..a938fd7176
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry.c
@@ -0,0 +1,2149 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41:27Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#define HAVE_MAJOR
+#elif MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#define HAVE_MAJOR
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h> /* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* for Linux file flags */
+#endif
+#include <stddef.h>
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+#if !defined(HAVE_MAJOR) && !defined(major)
+/* Replacement for major/minor/makedev. */
+#define major(x) ((int)(0x00ff & ((x) >> 8)))
+#define minor(x) ((int)(0xffff00ff & (x)))
+#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))
+#endif
+
+/* Play games to come up with a suitable makedev() definition. */
+#ifdef __QNXNTO__
+/* QNX. <sigh> */
+#error #include <sys/netmgr.h>
+#define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
+#elif defined makedev
+/* There's a "makedev" macro. */
+#define ae_makedev(maj, min) makedev((maj), (min))
+#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))
+/* Windows. <sigh> */
+#define ae_makedev(maj, min) mkdev((maj), (min))
+#else
+/* There's a "makedev" function. */
+#define ae_makedev(maj, min) makedev((maj), (min))
+#endif
+
+/*
+ * This adjustment is needed to support the following idiom for adding
+ * 1000ns to the stored time:
+ * archive_entry_set_atime(archive_entry_atime(),
+ * archive_entry_atime_nsec() + 1000)
+ * The additional if() here compensates for ambiguity in the C standard,
+ * which permits two possible interpretations of a % b when a is negative.
+ */
+#define FIX_NS(t,ns) \
+ do { \
+ t += ns / 1000000000; \
+ ns %= 1000000000; \
+ if (ns < 0) { --t; ns += 1000000000; } \
+ } while (0)
+
+static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
+static const wchar_t *ae_wcstofflags(const wchar_t *stringp,
+ unsigned long *setp, unsigned long *clrp);
+static const char *ae_strtofflags(const char *stringp,
+ unsigned long *setp, unsigned long *clrp);
+
+#ifndef HAVE_WCSCPY
+static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
+{
+ wchar_t *dest = s1;
+ while ((*s1 = *s2) != L'\0')
+ ++s1, ++s2;
+ return dest;
+}
+#endif
+#ifndef HAVE_WCSLEN
+static size_t wcslen(const wchar_t *s)
+{
+ const wchar_t *p = s;
+ while (*p != L'\0')
+ ++p;
+ return p - s;
+}
+#endif
+#ifndef HAVE_WMEMCMP
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+/****************************************************************************
+ *
+ * Public Interface
+ *
+ ****************************************************************************/
+
+struct archive_entry *
+archive_entry_clear(struct archive_entry *entry)
+{
+ if (entry == NULL)
+ return (NULL);
+ archive_mstring_clean(&entry->ae_fflags_text);
+ archive_mstring_clean(&entry->ae_gname);
+ archive_mstring_clean(&entry->ae_hardlink);
+ archive_mstring_clean(&entry->ae_pathname);
+ archive_mstring_clean(&entry->ae_sourcepath);
+ archive_mstring_clean(&entry->ae_symlink);
+ archive_mstring_clean(&entry->ae_uname);
+ archive_entry_copy_mac_metadata(entry, NULL, 0);
+ archive_acl_clear(&entry->acl);
+ archive_entry_xattr_clear(entry);
+ archive_entry_sparse_clear(entry);
+ free(entry->stat);
+ entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;
+ memset(entry, 0, sizeof(*entry));
+ return entry;
+}
+
+struct archive_entry *
+archive_entry_clone(struct archive_entry *entry)
+{
+ struct archive_entry *entry2;
+ struct ae_xattr *xp;
+ struct ae_sparse *sp;
+ size_t s;
+ const void *p;
+
+ /* Allocate new structure and copy over all of the fields. */
+ /* TODO: Should we copy the archive over? Or require a new archive
+ * as an argument? */
+ entry2 = archive_entry_new2(entry->archive);
+ if (entry2 == NULL)
+ return (NULL);
+ entry2->ae_stat = entry->ae_stat;
+ entry2->ae_fflags_set = entry->ae_fflags_set;
+ entry2->ae_fflags_clear = entry->ae_fflags_clear;
+
+ /* TODO: XXX If clone can have a different archive, what do we do here if
+ * character sets are different? XXX */
+ archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
+ archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname);
+ archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
+ archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname);
+ archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);
+ archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink);
+ entry2->ae_set = entry->ae_set;
+ archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname);
+
+ /* Copy symlink type */
+ entry2->ae_symlink_type = entry->ae_symlink_type;
+
+ /* Copy encryption status */
+ entry2->encryption = entry->encryption;
+
+ /* Copy digests */
+#define copy_digest(_e2, _e, _t) \
+ memcpy(_e2->digest._t, _e->digest._t, sizeof(_e2->digest._t))
+
+ copy_digest(entry2, entry, md5);
+ copy_digest(entry2, entry, rmd160);
+ copy_digest(entry2, entry, sha1);
+ copy_digest(entry2, entry, sha256);
+ copy_digest(entry2, entry, sha384);
+ copy_digest(entry2, entry, sha512);
+
+#undef copy_digest
+
+ /* Copy ACL data over. */
+ archive_acl_copy(&entry2->acl, &entry->acl);
+
+ /* Copy Mac OS metadata. */
+ p = archive_entry_mac_metadata(entry, &s);
+ archive_entry_copy_mac_metadata(entry2, p, s);
+
+ /* Copy xattr data over. */
+ xp = entry->xattr_head;
+ while (xp != NULL) {
+ archive_entry_xattr_add_entry(entry2,
+ xp->name, xp->value, xp->size);
+ xp = xp->next;
+ }
+
+ /* Copy sparse data over. */
+ sp = entry->sparse_head;
+ while (sp != NULL) {
+ archive_entry_sparse_add_entry(entry2,
+ sp->offset, sp->length);
+ sp = sp->next;
+ }
+
+ return (entry2);
+}
+
+void
+archive_entry_free(struct archive_entry *entry)
+{
+ archive_entry_clear(entry);
+ free(entry);
+}
+
+struct archive_entry *
+archive_entry_new(void)
+{
+ return archive_entry_new2(NULL);
+}
+
+struct archive_entry *
+archive_entry_new2(struct archive *a)
+{
+ struct archive_entry *entry;
+
+ entry = (struct archive_entry *)calloc(1, sizeof(*entry));
+ if (entry == NULL)
+ return (NULL);
+ entry->archive = a;
+ entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;
+ return (entry);
+}
+
+/*
+ * Functions for reading fields from an archive_entry.
+ */
+
+time_t
+archive_entry_atime(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_atime);
+}
+
+long
+archive_entry_atime_nsec(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_atime_nsec);
+}
+
+int
+archive_entry_atime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_ATIME);
+}
+
+time_t
+archive_entry_birthtime(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_birthtime);
+}
+
+long
+archive_entry_birthtime_nsec(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_birthtime_nsec);
+}
+
+int
+archive_entry_birthtime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_BIRTHTIME);
+}
+
+time_t
+archive_entry_ctime(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_ctime);
+}
+
+int
+archive_entry_ctime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_CTIME);
+}
+
+long
+archive_entry_ctime_nsec(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_ctime_nsec);
+}
+
+dev_t
+archive_entry_dev(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_dev_is_broken_down)
+ return ae_makedev(entry->ae_stat.aest_devmajor,
+ entry->ae_stat.aest_devminor);
+ else
+ return (entry->ae_stat.aest_dev);
+}
+
+int
+archive_entry_dev_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_DEV);
+}
+
+dev_t
+archive_entry_devmajor(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_dev_is_broken_down)
+ return (entry->ae_stat.aest_devmajor);
+ else
+ return major(entry->ae_stat.aest_dev);
+}
+
+dev_t
+archive_entry_devminor(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_dev_is_broken_down)
+ return (entry->ae_stat.aest_devminor);
+ else
+ return minor(entry->ae_stat.aest_dev);
+}
+
+__LA_MODE_T
+archive_entry_filetype(struct archive_entry *entry)
+{
+ return (AE_IFMT & entry->acl.mode);
+}
+
+void
+archive_entry_fflags(struct archive_entry *entry,
+ unsigned long *set, unsigned long *clear)
+{
+ *set = entry->ae_fflags_set;
+ *clear = entry->ae_fflags_clear;
+}
+
+/*
+ * Note: if text was provided, this just returns that text. If you
+ * really need the text to be rebuilt in a canonical form, set the
+ * text, ask for the bitmaps, then set the bitmaps. (Setting the
+ * bitmaps clears any stored text.) This design is deliberate: if
+ * we're editing archives, we don't want to discard flags just because
+ * they aren't supported on the current system. The bitmap<->text
+ * conversions are platform-specific (see below).
+ */
+const char *
+archive_entry_fflags_text(struct archive_entry *entry)
+{
+ const char *f;
+ char *p;
+
+ if (archive_mstring_get_mbs(entry->archive,
+ &entry->ae_fflags_text, &f) == 0) {
+ if (f != NULL)
+ return (f);
+ } else if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+
+ if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0)
+ return (NULL);
+
+ p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);
+ if (p == NULL)
+ return (NULL);
+
+ archive_mstring_copy_mbs(&entry->ae_fflags_text, p);
+ free(p);
+ if (archive_mstring_get_mbs(entry->archive,
+ &entry->ae_fflags_text, &f) == 0)
+ return (f);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+la_int64_t
+archive_entry_gid(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_gid);
+}
+
+const char *
+archive_entry_gname(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const char *
+archive_entry_gname_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+
+const wchar_t *
+archive_entry_gname_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_gname_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_gname, p, len, sc));
+}
+
+const char *
+archive_entry_hardlink(struct archive_entry *entry)
+{
+ const char *p;
+ if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_mbs(
+ entry->archive, &entry->ae_hardlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const char *
+archive_entry_hardlink_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_hardlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_hardlink_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_wcs(
+ entry->archive, &entry->ae_hardlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_hardlink_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ if ((entry->ae_set & AE_SET_HARDLINK) == 0) {
+ *p = NULL;
+ *len = 0;
+ return (0);
+ }
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_hardlink, p, len, sc));
+}
+
+la_int64_t
+archive_entry_ino(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_ino);
+}
+
+int
+archive_entry_ino_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_INO);
+}
+
+la_int64_t
+archive_entry_ino64(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_ino);
+}
+
+__LA_MODE_T
+archive_entry_mode(struct archive_entry *entry)
+{
+ return (entry->acl.mode);
+}
+
+time_t
+archive_entry_mtime(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_mtime);
+}
+
+long
+archive_entry_mtime_nsec(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_mtime_nsec);
+}
+
+int
+archive_entry_mtime_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_MTIME);
+}
+
+unsigned int
+archive_entry_nlink(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_nlink);
+}
+
+/* Instead, our caller could have chosen a specific encoding
+ * (archive_mstring_get_mbs, archive_mstring_get_utf8,
+ * archive_mstring_get_wcs). So we should try multiple
+ * encodings. Try mbs first because of history, even though
+ * utf8 might be better for pathname portability.
+ * Also omit wcs because of type mismatch (char * versus wchar *)
+ */
+const char *
+archive_entry_pathname(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_mbs(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+#if HAVE_EILSEQ /*{*/
+ if (errno == EILSEQ) {
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+ }
+#endif /*}*/
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const char *
+archive_entry_pathname_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_pathname_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if (archive_mstring_get_wcs(
+ entry->archive, &entry->ae_pathname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_pathname_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_pathname, p, len, sc));
+}
+
+__LA_MODE_T
+archive_entry_perm(struct archive_entry *entry)
+{
+ return (~AE_IFMT & entry->acl.mode);
+}
+
+dev_t
+archive_entry_rdev(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_rdev_is_broken_down)
+ return ae_makedev(entry->ae_stat.aest_rdevmajor,
+ entry->ae_stat.aest_rdevminor);
+ else
+ return (entry->ae_stat.aest_rdev);
+}
+
+dev_t
+archive_entry_rdevmajor(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_rdev_is_broken_down)
+ return (entry->ae_stat.aest_rdevmajor);
+ else
+ return major(entry->ae_stat.aest_rdev);
+}
+
+dev_t
+archive_entry_rdevminor(struct archive_entry *entry)
+{
+ if (entry->ae_stat.aest_rdev_is_broken_down)
+ return (entry->ae_stat.aest_rdevminor);
+ else
+ return minor(entry->ae_stat.aest_rdev);
+}
+
+la_int64_t
+archive_entry_size(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_size);
+}
+
+int
+archive_entry_size_is_set(struct archive_entry *entry)
+{
+ return (entry->ae_set & AE_SET_SIZE);
+}
+
+const char *
+archive_entry_sourcepath(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_mbs(
+ entry->archive, &entry->ae_sourcepath, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_sourcepath_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if (archive_mstring_get_wcs(
+ entry->archive, &entry->ae_sourcepath, &p) == 0)
+ return (p);
+ return (NULL);
+}
+
+const char *
+archive_entry_symlink(struct archive_entry *entry)
+{
+ const char *p;
+ if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_mbs(
+ entry->archive, &entry->ae_symlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+archive_entry_symlink_type(struct archive_entry *entry)
+{
+ return (entry->ae_symlink_type);
+}
+
+const char *
+archive_entry_symlink_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_utf8(
+ entry->archive, &entry->ae_symlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_symlink_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+ return (NULL);
+ if (archive_mstring_get_wcs(
+ entry->archive, &entry->ae_symlink, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_symlink_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+ *p = NULL;
+ *len = 0;
+ return (0);
+ }
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_symlink, p, len, sc));
+}
+
+la_int64_t
+archive_entry_uid(struct archive_entry *entry)
+{
+ return (entry->ae_stat.aest_uid);
+}
+
+const char *
+archive_entry_uname(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const char *
+archive_entry_uname_utf8(struct archive_entry *entry)
+{
+ const char *p;
+ if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+const wchar_t *
+archive_entry_uname_w(struct archive_entry *entry)
+{
+ const wchar_t *p;
+ if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0)
+ return (p);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (NULL);
+}
+
+int
+_archive_entry_uname_l(struct archive_entry *entry,
+ const char **p, size_t *len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_uname, p, len, sc));
+}
+
+int
+archive_entry_is_data_encrypted(struct archive_entry *entry)
+{
+ return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA);
+}
+
+int
+archive_entry_is_metadata_encrypted(struct archive_entry *entry)
+{
+ return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA);
+}
+
+int
+archive_entry_is_encrypted(struct archive_entry *entry)
+{
+ return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA));
+}
+
+/*
+ * Functions to set archive_entry properties.
+ */
+
+void
+archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)
+{
+ entry->stat_valid = 0;
+ entry->acl.mode &= ~AE_IFMT;
+ entry->acl.mode |= AE_IFMT & type;
+}
+
+void
+archive_entry_set_fflags(struct archive_entry *entry,
+ unsigned long set, unsigned long clear)
+{
+ archive_mstring_clean(&entry->ae_fflags_text);
+ entry->ae_fflags_set = set;
+ entry->ae_fflags_clear = clear;
+}
+
+const char *
+archive_entry_copy_fflags_text(struct archive_entry *entry,
+ const char *flags)
+{
+ archive_mstring_copy_mbs(&entry->ae_fflags_text, flags);
+ return (ae_strtofflags(flags,
+ &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
+const wchar_t *
+archive_entry_copy_fflags_text_w(struct archive_entry *entry,
+ const wchar_t *flags)
+{
+ archive_mstring_copy_wcs(&entry->ae_fflags_text, flags);
+ return (ae_wcstofflags(flags,
+ &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
+void
+archive_entry_set_gid(struct archive_entry *entry, la_int64_t g)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_gid = g;
+}
+
+void
+archive_entry_set_gname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_utf8(&entry->ae_gname, name);
+}
+
+void
+archive_entry_copy_gname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
+{
+ archive_mstring_copy_wcs(&entry->ae_gname, name);
+}
+
+int
+archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
+{
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_gname, name) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_gname_l(struct archive_entry *entry,
+ const char *name, size_t len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc));
+}
+
+void
+archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_INO;
+ entry->ae_stat.aest_ino = ino;
+}
+
+void
+archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_INO;
+ entry->ae_stat.aest_ino = ino;
+}
+
+void
+archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
+{
+ archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target)
+{
+ archive_mstring_copy_utf8(&entry->ae_hardlink, target);
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
+{
+ archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
+{
+ archive_mstring_copy_wcs(&entry->ae_hardlink, target);
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+int
+archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target)
+{
+ if (target != NULL)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_hardlink, target) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_hardlink_l(struct archive_entry *entry,
+ const char *target, size_t len, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,
+ target, len, sc);
+ if (target != NULL && r == 0)
+ entry->ae_set |= AE_SET_HARDLINK;
+ else
+ entry->ae_set &= ~AE_SET_HARDLINK;
+ return (r);
+}
+
+void
+archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
+{
+ FIX_NS(t, ns);
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_ATIME;
+ entry->ae_stat.aest_atime = t;
+ entry->ae_stat.aest_atime_nsec = ns;
+}
+
+void
+archive_entry_unset_atime(struct archive_entry *entry)
+{
+ archive_entry_set_atime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_ATIME;
+}
+
+void
+archive_entry_set_birthtime(struct archive_entry *entry, time_t t, long ns)
+{
+ FIX_NS(t, ns);
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_BIRTHTIME;
+ entry->ae_stat.aest_birthtime = t;
+ entry->ae_stat.aest_birthtime_nsec = ns;
+}
+
+void
+archive_entry_unset_birthtime(struct archive_entry *entry)
+{
+ archive_entry_set_birthtime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_BIRTHTIME;
+}
+
+void
+archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
+{
+ FIX_NS(t, ns);
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_CTIME;
+ entry->ae_stat.aest_ctime = t;
+ entry->ae_stat.aest_ctime_nsec = ns;
+}
+
+void
+archive_entry_unset_ctime(struct archive_entry *entry)
+{
+ archive_entry_set_ctime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_CTIME;
+}
+
+void
+archive_entry_set_dev(struct archive_entry *entry, dev_t d)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_DEV;
+ entry->ae_stat.aest_dev_is_broken_down = 0;
+ entry->ae_stat.aest_dev = d;
+}
+
+void
+archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_DEV;
+ entry->ae_stat.aest_dev_is_broken_down = 1;
+ entry->ae_stat.aest_devmajor = m;
+}
+
+void
+archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_DEV;
+ entry->ae_stat.aest_dev_is_broken_down = 1;
+ entry->ae_stat.aest_devminor = m;
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_set_link(struct archive_entry *entry, const char *target)
+{
+ if (entry->ae_set & AE_SET_SYMLINK)
+ archive_mstring_copy_mbs(&entry->ae_symlink, target);
+ else
+ archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_set_link_utf8(struct archive_entry *entry, const char *target)
+{
+ if (entry->ae_set & AE_SET_SYMLINK)
+ archive_mstring_copy_utf8(&entry->ae_symlink, target);
+ else
+ archive_mstring_copy_utf8(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link(struct archive_entry *entry, const char *target)
+{
+ if (entry->ae_set & AE_SET_SYMLINK)
+ archive_mstring_copy_mbs(&entry->ae_symlink, target);
+ else
+ archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
+{
+ if (entry->ae_set & AE_SET_SYMLINK)
+ archive_mstring_copy_wcs(&entry->ae_symlink, target);
+ else
+ archive_mstring_copy_wcs(&entry->ae_hardlink, target);
+}
+
+int
+archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
+{
+ int r;
+ if (entry->ae_set & AE_SET_SYMLINK)
+ r = archive_mstring_update_utf8(entry->archive,
+ &entry->ae_symlink, target);
+ else
+ r = archive_mstring_update_utf8(entry->archive,
+ &entry->ae_hardlink, target);
+ if (r == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_link_l(struct archive_entry *entry,
+ const char *target, size_t len, struct archive_string_conv *sc)
+{
+ int r;
+
+ if (entry->ae_set & AE_SET_SYMLINK)
+ r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,
+ target, len, sc);
+ else
+ r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,
+ target, len, sc);
+ return (r);
+}
+
+void
+archive_entry_set_mode(struct archive_entry *entry, mode_t m)
+{
+ entry->stat_valid = 0;
+ entry->acl.mode = m;
+}
+
+void
+archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns)
+{
+ FIX_NS(t, ns);
+ entry->stat_valid = 0;
+ entry->ae_set |= AE_SET_MTIME;
+ entry->ae_stat.aest_mtime = t;
+ entry->ae_stat.aest_mtime_nsec = ns;
+}
+
+void
+archive_entry_unset_mtime(struct archive_entry *entry)
+{
+ archive_entry_set_mtime(entry, 0, 0);
+ entry->ae_set &= ~AE_SET_MTIME;
+}
+
+void
+archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_nlink = nlink;
+}
+
+void
+archive_entry_set_pathname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_utf8(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
+{
+ archive_mstring_copy_wcs(&entry->ae_pathname, name);
+}
+
+int
+archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
+{
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_pathname, name) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_pathname_l(struct archive_entry *entry,
+ const char *name, size_t len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname,
+ name, len, sc));
+}
+
+void
+archive_entry_set_perm(struct archive_entry *entry, mode_t p)
+{
+ entry->stat_valid = 0;
+ entry->acl.mode &= AE_IFMT;
+ entry->acl.mode |= ~AE_IFMT & p;
+}
+
+void
+archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_rdev = m;
+ entry->ae_stat.aest_rdev_is_broken_down = 0;
+}
+
+void
+archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_rdev_is_broken_down = 1;
+ entry->ae_stat.aest_rdevmajor = m;
+}
+
+void
+archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_rdev_is_broken_down = 1;
+ entry->ae_stat.aest_rdevminor = m;
+}
+
+void
+archive_entry_set_size(struct archive_entry *entry, la_int64_t s)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_size = s;
+ entry->ae_set |= AE_SET_SIZE;
+}
+
+void
+archive_entry_unset_size(struct archive_entry *entry)
+{
+ archive_entry_set_size(entry, 0);
+ entry->ae_set &= ~AE_SET_SIZE;
+}
+
+void
+archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)
+{
+ archive_mstring_copy_mbs(&entry->ae_sourcepath, path);
+}
+
+void
+archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path)
+{
+ archive_mstring_copy_wcs(&entry->ae_sourcepath, path);
+}
+
+void
+archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
+{
+ archive_mstring_copy_mbs(&entry->ae_symlink, linkname);
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_set_symlink_type(struct archive_entry *entry, int type)
+{
+ entry->ae_symlink_type = type;
+}
+
+void
+archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname)
+{
+ archive_mstring_copy_utf8(&entry->ae_symlink, linkname);
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
+{
+ archive_mstring_copy_mbs(&entry->ae_symlink, linkname);
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
+{
+ archive_mstring_copy_wcs(&entry->ae_symlink, linkname);
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+int
+archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname)
+{
+ if (linkname != NULL)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_symlink, linkname) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+int
+_archive_entry_copy_symlink_l(struct archive_entry *entry,
+ const char *linkname, size_t len, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,
+ linkname, len, sc);
+ if (linkname != NULL && r == 0)
+ entry->ae_set |= AE_SET_SYMLINK;
+ else
+ entry->ae_set &= ~AE_SET_SYMLINK;
+ return (r);
+}
+
+void
+archive_entry_set_uid(struct archive_entry *entry, la_int64_t u)
+{
+ entry->stat_valid = 0;
+ entry->ae_stat.aest_uid = u;
+}
+
+void
+archive_entry_set_uname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_uname, name);
+}
+
+void
+archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_utf8(&entry->ae_uname, name);
+}
+
+void
+archive_entry_copy_uname(struct archive_entry *entry, const char *name)
+{
+ archive_mstring_copy_mbs(&entry->ae_uname, name);
+}
+
+void
+archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
+{
+ archive_mstring_copy_wcs(&entry->ae_uname, name);
+}
+
+int
+archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
+{
+ if (archive_mstring_update_utf8(entry->archive,
+ &entry->ae_uname, name) == 0)
+ return (1);
+ if (errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (0);
+}
+
+void
+archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted)
+{
+ if (is_encrypted) {
+ entry->encryption |= AE_ENCRYPTION_DATA;
+ } else {
+ entry->encryption &= ~AE_ENCRYPTION_DATA;
+ }
+}
+
+void
+archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted)
+{
+ if (is_encrypted) {
+ entry->encryption |= AE_ENCRYPTION_METADATA;
+ } else {
+ entry->encryption &= ~AE_ENCRYPTION_METADATA;
+ }
+}
+
+int
+_archive_entry_copy_uname_l(struct archive_entry *entry,
+ const char *name, size_t len, struct archive_string_conv *sc)
+{
+ return (archive_mstring_copy_mbs_len_l(&entry->ae_uname,
+ name, len, sc));
+}
+
+const void *
+archive_entry_mac_metadata(struct archive_entry *entry, size_t *s)
+{
+ *s = entry->mac_metadata_size;
+ return entry->mac_metadata;
+}
+
+void
+archive_entry_copy_mac_metadata(struct archive_entry *entry,
+ const void *p, size_t s)
+{
+ free(entry->mac_metadata);
+ if (p == NULL || s == 0) {
+ entry->mac_metadata = NULL;
+ entry->mac_metadata_size = 0;
+ } else {
+ entry->mac_metadata_size = s;
+ entry->mac_metadata = malloc(s);
+ if (entry->mac_metadata == NULL)
+ abort();
+ memcpy(entry->mac_metadata, p, s);
+ }
+}
+
+/* Digest handling */
+const unsigned char *
+archive_entry_digest(struct archive_entry *entry, int type)
+{
+ switch (type) {
+ case ARCHIVE_ENTRY_DIGEST_MD5:
+ return entry->digest.md5;
+ case ARCHIVE_ENTRY_DIGEST_RMD160:
+ return entry->digest.rmd160;
+ case ARCHIVE_ENTRY_DIGEST_SHA1:
+ return entry->digest.sha1;
+ case ARCHIVE_ENTRY_DIGEST_SHA256:
+ return entry->digest.sha256;
+ case ARCHIVE_ENTRY_DIGEST_SHA384:
+ return entry->digest.sha384;
+ case ARCHIVE_ENTRY_DIGEST_SHA512:
+ return entry->digest.sha512;
+ default:
+ return NULL;
+ }
+}
+
+int
+archive_entry_set_digest(struct archive_entry *entry, int type,
+ const unsigned char *digest)
+{
+#define copy_digest(_e, _t, _d)\
+ memcpy(_e->digest._t, _d, sizeof(_e->digest._t))
+
+ switch (type) {
+ case ARCHIVE_ENTRY_DIGEST_MD5:
+ copy_digest(entry, md5, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_RMD160:
+ copy_digest(entry, rmd160, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA1:
+ copy_digest(entry, sha1, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA256:
+ copy_digest(entry, sha256, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA384:
+ copy_digest(entry, sha384, digest);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA512:
+ copy_digest(entry, sha512, digest);
+ break;
+ default:
+ return ARCHIVE_WARN;
+ }
+
+ return ARCHIVE_OK;
+#undef copy_digest
+}
+
+/*
+ * ACL management. The following would, of course, be a lot simpler
+ * if: 1) the last draft of POSIX.1e were a really thorough and
+ * complete standard that addressed the needs of ACL archiving and 2)
+ * everyone followed it faithfully. Alas, neither is true, so the
+ * following is a lot more complex than might seem necessary to the
+ * uninitiated.
+ */
+
+struct archive_acl *
+archive_entry_acl(struct archive_entry *entry)
+{
+ return &entry->acl;
+}
+
+void
+archive_entry_acl_clear(struct archive_entry *entry)
+{
+ archive_acl_clear(&entry->acl);
+}
+
+/*
+ * Add a single ACL entry to the internal list of ACL data.
+ */
+int
+archive_entry_acl_add_entry(struct archive_entry *entry,
+ int type, int permset, int tag, int id, const char *name)
+{
+ return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name);
+}
+
+/*
+ * As above, but with a wide-character name.
+ */
+int
+archive_entry_acl_add_entry_w(struct archive_entry *entry,
+ int type, int permset, int tag, int id, const wchar_t *name)
+{
+ return archive_acl_add_entry_w_len(&entry->acl,
+ type, permset, tag, id, name, wcslen(name));
+}
+
+/*
+ * Return a bitmask of ACL types in an archive entry ACL list
+ */
+int
+archive_entry_acl_types(struct archive_entry *entry)
+{
+ return (archive_acl_types(&entry->acl));
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+int
+archive_entry_acl_count(struct archive_entry *entry, int want_type)
+{
+ return archive_acl_count(&entry->acl, want_type);
+}
+
+/*
+ * Prepare for reading entries from the ACL data. Returns a count
+ * of entries matching "want_type", or zero if there are no
+ * non-extended ACL entries of that type.
+ */
+int
+archive_entry_acl_reset(struct archive_entry *entry, int want_type)
+{
+ return archive_acl_reset(&entry->acl, want_type);
+}
+
+/*
+ * Return the next ACL entry in the list. Fake entries for the
+ * standard permissions and include them in the returned list.
+ */
+int
+archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
+ int *permset, int *tag, int *id, const char **name)
+{
+ int r;
+ r = archive_acl_next(entry->archive, &entry->acl, want_type, type,
+ permset, tag, id, name);
+ if (r == ARCHIVE_FATAL && errno == ENOMEM)
+ __archive_errx(1, "No memory");
+ return (r);
+}
+
+/*
+ * Generate a text version of the ACL. The flags parameter controls
+ * the style of the generated ACL.
+ */
+wchar_t *
+archive_entry_acl_to_text_w(struct archive_entry *entry, la_ssize_t *len,
+ int flags)
+{
+ return (archive_acl_to_text_w(&entry->acl, len, flags,
+ entry->archive));
+}
+
+char *
+archive_entry_acl_to_text(struct archive_entry *entry, la_ssize_t *len,
+ int flags)
+{
+ return (archive_acl_to_text_l(&entry->acl, len, flags, NULL));
+}
+
+char *
+_archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len,
+ int flags, struct archive_string_conv *sc)
+{
+ return (archive_acl_to_text_l(&entry->acl, len, flags, sc));
+}
+
+/*
+ * ACL text parser.
+ */
+int
+archive_entry_acl_from_text_w(struct archive_entry *entry,
+ const wchar_t *wtext, int type)
+{
+ return (archive_acl_from_text_w(&entry->acl, wtext, type));
+}
+
+int
+archive_entry_acl_from_text(struct archive_entry *entry,
+ const char *text, int type)
+{
+ return (archive_acl_from_text_l(&entry->acl, text, type, NULL));
+}
+
+int
+_archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text,
+ int type, struct archive_string_conv *sc)
+{
+ return (archive_acl_from_text_l(&entry->acl, text, type, sc));
+}
+
+/* Deprecated */
+static int
+archive_entry_acl_text_compat(int *flags)
+{
+ if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0)
+ return (1);
+
+ /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */
+ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0)
+ *flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID;
+
+ /* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */
+ if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+ *flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+ *flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA;
+
+ return (0);
+}
+
+/* Deprecated */
+const wchar_t *
+archive_entry_acl_text_w(struct archive_entry *entry, int flags)
+{
+ free(entry->acl.acl_text_w);
+ entry->acl.acl_text_w = NULL;
+ if (archive_entry_acl_text_compat(&flags) == 0)
+ entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl,
+ NULL, flags, entry->archive);
+ return (entry->acl.acl_text_w);
+}
+
+/* Deprecated */
+const char *
+archive_entry_acl_text(struct archive_entry *entry, int flags)
+{
+ free(entry->acl.acl_text);
+ entry->acl.acl_text = NULL;
+ if (archive_entry_acl_text_compat(&flags) == 0)
+ entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL,
+ flags, NULL);
+
+ return (entry->acl.acl_text);
+}
+
+/* Deprecated */
+int
+_archive_entry_acl_text_l(struct archive_entry *entry, int flags,
+ const char **acl_text, size_t *len, struct archive_string_conv *sc)
+{
+ free(entry->acl.acl_text);
+ entry->acl.acl_text = NULL;
+
+ if (archive_entry_acl_text_compat(&flags) == 0)
+ entry->acl.acl_text = archive_acl_to_text_l(&entry->acl,
+ (ssize_t *)len, flags, sc);
+
+ *acl_text = entry->acl.acl_text;
+
+ return (0);
+}
+
+/*
+ * Following code is modified from UC Berkeley sources, and
+ * is subject to the following copyright notice.
+ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+/*
+ * Supported file flags on FreeBSD and Mac OS:
+ * sappnd,sappend SF_APPEND
+ * arch,archived SF_ARCHIVED
+ * schg,schange,simmutable SF_IMMUTABLE
+ * sunlnk,sunlink SF_NOUNLINK (FreeBSD only)
+ * uappnd,uappend UF_APPEND
+ * compressed UF_COMPRESSED (Mac OS only)
+ * hidden,uhidden UF_HIDDEN
+ * uchg,uchange,uimmutable UF_IMMUTABLE
+ * nodump UF_NODUMP
+ * uunlnk,uunlink UF_NOUNLINK (FreeBSD only)
+ * offline,uoffline UF_OFFLINE (FreeBSD only)
+ * opaque UF_OPAQUE
+ * rdonly,urdonly,readonly UF_READONLY (FreeBSD only)
+ * reparse,ureparse UF_REPARSE (FreeBSD only)
+ * sparse,usparse UF_SPARSE (FreeBSD only)
+ * system,usystem UF_SYSTEM (FreeBSD only)
+ *
+ * See chflags(2) for more information
+ *
+ * Supported file attributes on Linux:
+ * a append only FS_APPEND_FL sappnd
+ * A no atime updates FS_NOATIME_FL atime
+ * c compress FS_COMPR_FL compress
+ * C no copy on write FS_NOCOW_FL cow
+ * d no dump FS_NODUMP_FL dump
+ * D synchronous directory updates FS_DIRSYNC_FL dirsync
+ * i immutable FS_IMMUTABLE_FL schg
+ * j data journalling FS_JOURNAL_DATA_FL journal
+ * P project hierarchy FS_PROJINHERIT_FL projinherit
+ * s secure deletion FS_SECRM_FL securedeletion
+ * S synchronous updates FS_SYNC_FL sync
+ * t no tail-merging FS_NOTAIL_FL tail
+ * T top of directory hierarchy FS_TOPDIR_FL topdir
+ * u undeletable FS_UNRM_FL undel
+ *
+ * See ioctl_iflags(2) for more information
+ *
+ * Equivalent file flags supported on FreeBSD / Mac OS and Linux:
+ * SF_APPEND FS_APPEND_FL sappnd
+ * SF_IMMUTABLE FS_IMMUTABLE_FL schg
+ * UF_NODUMP FS_NODUMP_FL nodump
+ */
+
+static const struct flag {
+ const char *name;
+ const wchar_t *wname;
+ unsigned long set;
+ unsigned long clear;
+} fileflags[] = {
+ /* Preferred (shorter) names per flag first, all prefixed by "no" */
+#ifdef SF_APPEND
+ { "nosappnd", L"nosappnd", SF_APPEND, 0},
+ { "nosappend", L"nosappend", SF_APPEND, 0},
+#endif
+#if defined(FS_APPEND_FL) /* 'a' */
+ { "nosappnd", L"nosappnd", FS_APPEND_FL, 0},
+ { "nosappend", L"nosappend", FS_APPEND_FL, 0},
+#elif defined(EXT2_APPEND_FL) /* 'a' */
+ { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0},
+ { "nosappend", L"nosappend", EXT2_APPEND_FL, 0},
+#endif
+#ifdef SF_ARCHIVED
+ { "noarch", L"noarch", SF_ARCHIVED, 0},
+ { "noarchived", L"noarchived", SF_ARCHIVED, 0},
+#endif
+#ifdef SF_IMMUTABLE
+ { "noschg", L"noschg", SF_IMMUTABLE, 0},
+ { "noschange", L"noschange", SF_IMMUTABLE, 0},
+ { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0},
+#endif
+#if defined(FS_IMMUTABLE_FL) /* 'i' */
+ { "noschg", L"noschg", FS_IMMUTABLE_FL, 0},
+ { "noschange", L"noschange", FS_IMMUTABLE_FL, 0},
+ { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0},
+#elif defined(EXT2_IMMUTABLE_FL) /* 'i' */
+ { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0},
+ { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0},
+ { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0},
+#endif
+#ifdef SF_NOUNLINK
+ { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0},
+ { "nosunlink", L"nosunlink", SF_NOUNLINK, 0},
+#endif
+#ifdef UF_APPEND
+ { "nouappnd", L"nouappnd", UF_APPEND, 0},
+ { "nouappend", L"nouappend", UF_APPEND, 0},
+#endif
+#ifdef UF_IMMUTABLE
+ { "nouchg", L"nouchg", UF_IMMUTABLE, 0},
+ { "nouchange", L"nouchange", UF_IMMUTABLE, 0},
+ { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0},
+#endif
+#ifdef UF_NODUMP
+ { "nodump", L"nodump", 0, UF_NODUMP},
+#endif
+#if defined(FS_NODUMP_FL) /* 'd' */
+ { "nodump", L"nodump", 0, FS_NODUMP_FL},
+#elif defined(EXT2_NODUMP_FL)
+ { "nodump", L"nodump", 0, EXT2_NODUMP_FL},
+#endif
+#ifdef UF_OPAQUE
+ { "noopaque", L"noopaque", UF_OPAQUE, 0},
+#endif
+#ifdef UF_NOUNLINK
+ { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0},
+ { "nouunlink", L"nouunlink", UF_NOUNLINK, 0},
+#endif
+#ifdef UF_COMPRESSED
+ /* Mac OS */
+ { "nocompressed", L"nocompressed", UF_COMPRESSED, 0},
+#endif
+#ifdef UF_HIDDEN
+ { "nohidden", L"nohidden", UF_HIDDEN, 0},
+ { "nouhidden", L"nouhidden", UF_HIDDEN, 0},
+#endif
+#ifdef FILE_ATTRIBUTE_HIDDEN
+ { "nohidden", L"nohidden", FILE_ATTRIBUTE_HIDDEN, 0},
+ { "nouhidden", L"nouhidden", FILE_ATTRIBUTE_HIDDEN, 0},
+#endif
+#ifdef UF_OFFLINE
+ { "nooffline", L"nooffline", UF_OFFLINE, 0},
+ { "nouoffline", L"nouoffline", UF_OFFLINE, 0},
+#endif
+#ifdef UF_READONLY
+ { "nordonly", L"nordonly", UF_READONLY, 0},
+ { "nourdonly", L"nourdonly", UF_READONLY, 0},
+ { "noreadonly", L"noreadonly", UF_READONLY, 0},
+#endif
+#ifdef FILE_ATTRIBUTE_READONLY
+ { "nordonly", L"nordonly", FILE_ATTRIBUTE_READONLY, 0},
+ { "nourdonly", L"nourdonly", FILE_ATTRIBUTE_READONLY, 0},
+ { "noreadonly", L"noreadonly", FILE_ATTRIBUTE_READONLY, 0},
+#endif
+#ifdef UF_SPARSE
+ { "nosparse", L"nosparse", UF_SPARSE, 0},
+ { "nousparse", L"nousparse", UF_SPARSE, 0},
+#endif
+#ifdef UF_REPARSE
+ { "noreparse", L"noreparse", UF_REPARSE, 0},
+ { "noureparse", L"noureparse", UF_REPARSE, 0},
+#endif
+#ifdef UF_SYSTEM
+ { "nosystem", L"nosystem", UF_SYSTEM, 0},
+ { "nousystem", L"nousystem", UF_SYSTEM, 0},
+#endif
+#ifdef FILE_ATTRIBUTE_SYSTEM
+ { "nosystem", L"nosystem", FILE_ATTRIBUTE_SYSTEM, 0},
+ { "nousystem", L"nousystem", FILE_ATTRIBUTE_SYSTEM, 0},
+#endif
+#if defined(FS_UNRM_FL) /* 'u' */
+ { "noundel", L"noundel", FS_UNRM_FL, 0},
+#elif defined(EXT2_UNRM_FL)
+ { "noundel", L"noundel", EXT2_UNRM_FL, 0},
+#endif
+
+#if defined(FS_COMPR_FL) /* 'c' */
+ { "nocompress", L"nocompress", FS_COMPR_FL, 0},
+#elif defined(EXT2_COMPR_FL)
+ { "nocompress", L"nocompress", EXT2_COMPR_FL, 0},
+#endif
+
+#if defined(FS_NOATIME_FL) /* 'A' */
+ { "noatime", L"noatime", 0, FS_NOATIME_FL},
+#elif defined(EXT2_NOATIME_FL)
+ { "noatime", L"noatime", 0, EXT2_NOATIME_FL},
+#endif
+#if defined(FS_DIRSYNC_FL) /* 'D' */
+ { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0},
+#elif defined(EXT2_DIRSYNC_FL)
+ { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0},
+#endif
+#if defined(FS_JOURNAL_DATA_FL) /* 'j' */
+ { "nojournal-data",L"nojournal-data", FS_JOURNAL_DATA_FL, 0},
+ { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0},
+#elif defined(EXT3_JOURNAL_DATA_FL)
+ { "nojournal-data",L"nojournal-data", EXT3_JOURNAL_DATA_FL, 0},
+ { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0},
+#endif
+#if defined(FS_SECRM_FL) /* 's' */
+ { "nosecdel", L"nosecdel", FS_SECRM_FL, 0},
+ { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0},
+#elif defined(EXT2_SECRM_FL)
+ { "nosecdel", L"nosecdel", EXT2_SECRM_FL, 0},
+ { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0},
+#endif
+#if defined(FS_SYNC_FL) /* 'S' */
+ { "nosync", L"nosync", FS_SYNC_FL, 0},
+#elif defined(EXT2_SYNC_FL)
+ { "nosync", L"nosync", EXT2_SYNC_FL, 0},
+#endif
+#if defined(FS_NOTAIL_FL) /* 't' */
+ { "notail", L"notail", 0, FS_NOTAIL_FL},
+#elif defined(EXT2_NOTAIL_FL)
+ { "notail", L"notail", 0, EXT2_NOTAIL_FL},
+#endif
+#if defined(FS_TOPDIR_FL) /* 'T' */
+ { "notopdir", L"notopdir", FS_TOPDIR_FL, 0},
+#elif defined(EXT2_TOPDIR_FL)
+ { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0},
+#endif
+#ifdef FS_NOCOW_FL /* 'C' */
+ { "nocow", L"nocow", 0, FS_NOCOW_FL},
+#endif
+#ifdef FS_PROJINHERIT_FL /* 'P' */
+ { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0},
+#endif
+ { NULL, NULL, 0, 0}
+};
+
+/*
+ * fflagstostr --
+ * Convert file flags to a comma-separated string. If no flags
+ * are set, return the empty string.
+ */
+static char *
+ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
+{
+ char *string, *dp;
+ const char *sp;
+ unsigned long bits;
+ const struct flag *flag;
+ size_t length;
+
+ bits = bitset | bitclear;
+ length = 0;
+ for (flag = fileflags; flag->name != NULL; flag++)
+ if (bits & (flag->set | flag->clear)) {
+ length += strlen(flag->name) + 1;
+ bits &= ~(flag->set | flag->clear);
+ }
+
+ if (length == 0)
+ return (NULL);
+ string = (char *)malloc(length);
+ if (string == NULL)
+ return (NULL);
+
+ dp = string;
+ for (flag = fileflags; flag->name != NULL; flag++) {
+ if (bitset & flag->set || bitclear & flag->clear) {
+ sp = flag->name + 2;
+ } else if (bitset & flag->clear || bitclear & flag->set) {
+ sp = flag->name;
+ } else
+ continue;
+ bitset &= ~(flag->set | flag->clear);
+ bitclear &= ~(flag->set | flag->clear);
+ if (dp > string)
+ *dp++ = ',';
+ while ((*dp++ = *sp++) != '\0')
+ ;
+ dp--;
+ }
+
+ *dp = '\0';
+ return (string);
+}
+
+/*
+ * strtofflags --
+ * Take string of arguments and return file flags. This
+ * version works a little differently than strtofflags(3).
+ * In particular, it always tests every token, skipping any
+ * unrecognized tokens. It returns a pointer to the first
+ * unrecognized token, or NULL if every token was recognized.
+ * This version is also const-correct and does not modify the
+ * provided string.
+ */
+static const char *
+ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
+{
+ const char *start, *end;
+ const struct flag *flag;
+ unsigned long set, clear;
+ const char *failed;
+
+ set = clear = 0;
+ start = s;
+ failed = NULL;
+ /* Find start of first token. */
+ while (*start == '\t' || *start == ' ' || *start == ',')
+ start++;
+ while (*start != '\0') {
+ size_t length;
+ /* Locate end of token. */
+ end = start;
+ while (*end != '\0' && *end != '\t' &&
+ *end != ' ' && *end != ',')
+ end++;
+ length = end - start;
+ for (flag = fileflags; flag->name != NULL; flag++) {
+ size_t flag_length = strlen(flag->name);
+ if (length == flag_length
+ && memcmp(start, flag->name, length) == 0) {
+ /* Matched "noXXXX", so reverse the sense. */
+ clear |= flag->set;
+ set |= flag->clear;
+ break;
+ } else if (length == flag_length - 2
+ && memcmp(start, flag->name + 2, length) == 0) {
+ /* Matched "XXXX", so don't reverse. */
+ set |= flag->set;
+ clear |= flag->clear;
+ break;
+ }
+ }
+ /* Ignore unknown flag names. */
+ if (flag->name == NULL && failed == NULL)
+ failed = start;
+
+ /* Find start of next token. */
+ start = end;
+ while (*start == '\t' || *start == ' ' || *start == ',')
+ start++;
+
+ }
+
+ if (setp)
+ *setp = set;
+ if (clrp)
+ *clrp = clear;
+
+ /* Return location of first failure. */
+ return (failed);
+}
+
+/*
+ * wcstofflags --
+ * Take string of arguments and return file flags. This
+ * version works a little differently than strtofflags(3).
+ * In particular, it always tests every token, skipping any
+ * unrecognized tokens. It returns a pointer to the first
+ * unrecognized token, or NULL if every token was recognized.
+ * This version is also const-correct and does not modify the
+ * provided string.
+ */
+static const wchar_t *
+ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
+{
+ const wchar_t *start, *end;
+ const struct flag *flag;
+ unsigned long set, clear;
+ const wchar_t *failed;
+
+ set = clear = 0;
+ start = s;
+ failed = NULL;
+ /* Find start of first token. */
+ while (*start == L'\t' || *start == L' ' || *start == L',')
+ start++;
+ while (*start != L'\0') {
+ size_t length;
+ /* Locate end of token. */
+ end = start;
+ while (*end != L'\0' && *end != L'\t' &&
+ *end != L' ' && *end != L',')
+ end++;
+ length = end - start;
+ for (flag = fileflags; flag->wname != NULL; flag++) {
+ size_t flag_length = wcslen(flag->wname);
+ if (length == flag_length
+ && wmemcmp(start, flag->wname, length) == 0) {
+ /* Matched "noXXXX", so reverse the sense. */
+ clear |= flag->set;
+ set |= flag->clear;
+ break;
+ } else if (length == flag_length - 2
+ && wmemcmp(start, flag->wname + 2, length) == 0) {
+ /* Matched "XXXX", so don't reverse. */
+ set |= flag->set;
+ clear |= flag->clear;
+ break;
+ }
+ }
+ /* Ignore unknown flag names. */
+ if (flag->wname == NULL && failed == NULL)
+ failed = start;
+
+ /* Find start of next token. */
+ start = end;
+ while (*start == L'\t' || *start == L' ' || *start == L',')
+ start++;
+
+ }
+
+ if (setp)
+ *setp = set;
+ if (clrp)
+ *clrp = clear;
+
+ /* Return location of first failure. */
+ return (failed);
+}
+
+
+#ifdef TEST
+#include <stdio.h>
+int
+main(int argc, char **argv)
+{
+ struct archive_entry *entry = archive_entry_new();
+ unsigned long set, clear;
+ const wchar_t *remainder;
+
+ remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,");
+ archive_entry_fflags(entry, &set, &clear);
+
+ wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder);
+
+ wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry));
+ return (0);
+}
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_entry.h b/contrib/libs/libarchive/libarchive/archive_entry.h
new file mode 100644
index 0000000000..88d23903db
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry.h
@@ -0,0 +1,723 @@
+/*-
+ * Copyright (c) 2003-2008 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_entry.h 201096 2009-12-28 02:41:27Z kientzle $
+ */
+
+#ifndef ARCHIVE_ENTRY_H_INCLUDED
+#define ARCHIVE_ENTRY_H_INCLUDED
+
+/* Note: Compiler will complain if this does not match archive.h! */
+#define ARCHIVE_VERSION_NUMBER 3007002
+
+/*
+ * Note: archive_entry.h is for use outside of libarchive; the
+ * configuration headers (config.h, archive_platform.h, etc.) are
+ * purely internal. Do NOT use HAVE_XXX configuration macros to
+ * control the behavior of this header! If you must conditionalize,
+ * use predefined compiler and/or platform macros.
+ */
+
+#include <sys/types.h>
+#include <stddef.h> /* for wchar_t */
+#include <stdint.h>
+#include <time.h>
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#endif
+
+/* Get a suitable 64-bit integer type. */
+#if !defined(__LA_INT64_T_DEFINED)
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_INT64_T la_int64_t
+# endif
+#define __LA_INT64_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+typedef __int64 la_int64_t;
+# else
+#include <unistd.h>
+# if defined(_SCO_DS) || defined(__osf__)
+typedef long long la_int64_t;
+# else
+typedef int64_t la_int64_t;
+# endif
+# endif
+#endif
+
+/* The la_ssize_t should match the type used in 'struct stat' */
+#if !defined(__LA_SSIZE_T_DEFINED)
+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_SSIZE_T la_ssize_t
+# endif
+#define __LA_SSIZE_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+# if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
+typedef ssize_t la_ssize_t;
+# elif defined(_WIN64)
+typedef __int64 la_ssize_t;
+# else
+typedef long la_ssize_t;
+# endif
+# else
+# include <unistd.h> /* ssize_t */
+typedef ssize_t la_ssize_t;
+# endif
+#endif
+
+/* Get a suitable definition for mode_t */
+#if ARCHIVE_VERSION_NUMBER >= 3999000
+/* Switch to plain 'int' for libarchive 4.0. It's less broken than 'mode_t' */
+# define __LA_MODE_T int
+#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__)
+# define __LA_MODE_T unsigned short
+#else
+# define __LA_MODE_T mode_t
+#endif
+
+/* Large file support for Android */
+#if defined(__LIBARCHIVE_BUILD) && defined(__ANDROID__)
+#error #include "android_lf.h"
+#endif
+
+/*
+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a
+ * .lib. The default here assumes you're building a DLL. Only
+ * libarchive source should ever define __LIBARCHIVE_BUILD.
+ */
+#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
+# ifdef __LIBARCHIVE_BUILD
+# ifdef __GNUC__
+# define __LA_DECL __attribute__((dllexport)) extern
+# else
+# define __LA_DECL __declspec(dllexport)
+# endif
+# else
+# ifdef __GNUC__
+# define __LA_DECL
+# else
+# define __LA_DECL __declspec(dllimport)
+# endif
+# endif
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+# define __LA_DECL __attribute__((visibility("default")))
+#else
+/* Static libraries on all platforms and shared libraries on non-Windows. */
+# define __LA_DECL
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1
+# define __LA_DEPRECATED __attribute__((deprecated))
+#else
+# define __LA_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Description of an archive entry.
+ *
+ * You can think of this as "struct stat" with some text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries that are
+ * supported by "pax interchange" format. However, GNU, ustar, cpio,
+ * and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry. Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).
+ */
+struct archive;
+struct archive_entry;
+
+/*
+ * File-type constants. These are returned from archive_entry_filetype()
+ * and passed to archive_entry_set_filetype().
+ *
+ * These values match S_XXX defines on every platform I've checked,
+ * including Windows, AIX, Linux, Solaris, and BSD. They're
+ * (re)defined here because platforms generally don't define the ones
+ * they don't support. For example, Windows doesn't define S_IFLNK or
+ * S_IFBLK. Instead of having a mass of conditional logic and system
+ * checks to define any S_XXX values that aren't supported locally,
+ * I've just defined a new set of such constants so that
+ * libarchive-based applications can manipulate and identify archive
+ * entries properly even if the hosting platform can't store them on
+ * disk.
+ *
+ * These values are also used directly within some portable formats,
+ * such as cpio. If you find a platform that varies from these, the
+ * correct solution is to leave these alone and translate from these
+ * portable values to platform-native values when entries are read from
+ * or written to disk.
+ */
+/*
+ * In libarchive 4.0, we can drop the casts here.
+ * They're needed to work around Borland C's broken mode_t.
+ */
+#define AE_IFMT ((__LA_MODE_T)0170000)
+#define AE_IFREG ((__LA_MODE_T)0100000)
+#define AE_IFLNK ((__LA_MODE_T)0120000)
+#define AE_IFSOCK ((__LA_MODE_T)0140000)
+#define AE_IFCHR ((__LA_MODE_T)0020000)
+#define AE_IFBLK ((__LA_MODE_T)0060000)
+#define AE_IFDIR ((__LA_MODE_T)0040000)
+#define AE_IFIFO ((__LA_MODE_T)0010000)
+
+/*
+ * Symlink types
+ */
+#define AE_SYMLINK_TYPE_UNDEFINED 0
+#define AE_SYMLINK_TYPE_FILE 1
+#define AE_SYMLINK_TYPE_DIRECTORY 2
+
+/*
+ * Basic object manipulation
+ */
+
+__LA_DECL struct archive_entry *archive_entry_clear(struct archive_entry *);
+/* The 'clone' function does a deep copy; all of the strings are copied too. */
+__LA_DECL struct archive_entry *archive_entry_clone(struct archive_entry *);
+__LA_DECL void archive_entry_free(struct archive_entry *);
+__LA_DECL struct archive_entry *archive_entry_new(void);
+
+/*
+ * This form of archive_entry_new2() will pull character-set
+ * conversion information from the specified archive handle. The
+ * older archive_entry_new(void) form is equivalent to calling
+ * archive_entry_new2(NULL) and will result in the use of an internal
+ * default character-set conversion.
+ */
+__LA_DECL struct archive_entry *archive_entry_new2(struct archive *);
+
+/*
+ * Retrieve fields from an archive_entry.
+ *
+ * There are a number of implicit conversions among these fields. For
+ * example, if a regular string field is set and you read the _w wide
+ * character field, the entry will implicitly convert narrow-to-wide
+ * using the current locale. Similarly, dev values are automatically
+ * updated when you write devmajor or devminor and vice versa.
+ *
+ * In addition, fields can be "set" or "unset." Unset string fields
+ * return NULL, non-string fields have _is_set() functions to test
+ * whether they've been set. You can "unset" a string field by
+ * assigning NULL; non-string fields have _unset() functions to
+ * unset them.
+ *
+ * Note: There is one ambiguity in the above; string fields will
+ * also return NULL when implicit character set conversions fail.
+ * This is usually what you want.
+ */
+__LA_DECL time_t archive_entry_atime(struct archive_entry *);
+__LA_DECL long archive_entry_atime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_atime_is_set(struct archive_entry *);
+__LA_DECL time_t archive_entry_birthtime(struct archive_entry *);
+__LA_DECL long archive_entry_birthtime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_birthtime_is_set(struct archive_entry *);
+__LA_DECL time_t archive_entry_ctime(struct archive_entry *);
+__LA_DECL long archive_entry_ctime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_ctime_is_set(struct archive_entry *);
+__LA_DECL dev_t archive_entry_dev(struct archive_entry *);
+__LA_DECL int archive_entry_dev_is_set(struct archive_entry *);
+__LA_DECL dev_t archive_entry_devmajor(struct archive_entry *);
+__LA_DECL dev_t archive_entry_devminor(struct archive_entry *);
+__LA_DECL __LA_MODE_T archive_entry_filetype(struct archive_entry *);
+__LA_DECL void archive_entry_fflags(struct archive_entry *,
+ unsigned long * /* set */,
+ unsigned long * /* clear */);
+__LA_DECL const char *archive_entry_fflags_text(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_gid(struct archive_entry *);
+__LA_DECL const char *archive_entry_gname(struct archive_entry *);
+__LA_DECL const char *archive_entry_gname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_gname_w(struct archive_entry *);
+__LA_DECL const char *archive_entry_hardlink(struct archive_entry *);
+__LA_DECL const char *archive_entry_hardlink_utf8(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_hardlink_w(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_ino(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_ino64(struct archive_entry *);
+__LA_DECL int archive_entry_ino_is_set(struct archive_entry *);
+__LA_DECL __LA_MODE_T archive_entry_mode(struct archive_entry *);
+__LA_DECL time_t archive_entry_mtime(struct archive_entry *);
+__LA_DECL long archive_entry_mtime_nsec(struct archive_entry *);
+__LA_DECL int archive_entry_mtime_is_set(struct archive_entry *);
+__LA_DECL unsigned int archive_entry_nlink(struct archive_entry *);
+__LA_DECL const char *archive_entry_pathname(struct archive_entry *);
+__LA_DECL const char *archive_entry_pathname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_pathname_w(struct archive_entry *);
+__LA_DECL __LA_MODE_T archive_entry_perm(struct archive_entry *);
+__LA_DECL dev_t archive_entry_rdev(struct archive_entry *);
+__LA_DECL dev_t archive_entry_rdevmajor(struct archive_entry *);
+__LA_DECL dev_t archive_entry_rdevminor(struct archive_entry *);
+__LA_DECL const char *archive_entry_sourcepath(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_sourcepath_w(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_size(struct archive_entry *);
+__LA_DECL int archive_entry_size_is_set(struct archive_entry *);
+__LA_DECL const char *archive_entry_strmode(struct archive_entry *);
+__LA_DECL const char *archive_entry_symlink(struct archive_entry *);
+__LA_DECL const char *archive_entry_symlink_utf8(struct archive_entry *);
+__LA_DECL int archive_entry_symlink_type(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_symlink_w(struct archive_entry *);
+__LA_DECL la_int64_t archive_entry_uid(struct archive_entry *);
+__LA_DECL const char *archive_entry_uname(struct archive_entry *);
+__LA_DECL const char *archive_entry_uname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t *archive_entry_uname_w(struct archive_entry *);
+__LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *);
+__LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *);
+__LA_DECL int archive_entry_is_encrypted(struct archive_entry *);
+
+/*
+ * Set fields in an archive_entry.
+ *
+ * Note: Before libarchive 2.4, there were 'set' and 'copy' versions
+ * of the string setters. 'copy' copied the actual string, 'set' just
+ * stored the pointer. In libarchive 2.4 and later, strings are
+ * always copied.
+ */
+
+__LA_DECL void archive_entry_set_atime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_atime(struct archive_entry *);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *);
+#endif
+__LA_DECL void archive_entry_set_birthtime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_birthtime(struct archive_entry *);
+__LA_DECL void archive_entry_set_ctime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_ctime(struct archive_entry *);
+__LA_DECL void archive_entry_set_dev(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_devmajor(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_devminor(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_filetype(struct archive_entry *, unsigned int);
+__LA_DECL void archive_entry_set_fflags(struct archive_entry *,
+ unsigned long /* set */, unsigned long /* clear */);
+/* Returns pointer to start of first invalid token, or NULL if none. */
+/* Note that all recognized tokens are processed, regardless. */
+__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,
+ const char *);
+__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
+ const wchar_t *);
+__LA_DECL void archive_entry_set_gid(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_set_gname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_gname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_gname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_gname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_hardlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_ino(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_set_ino64(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_set_link(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_link_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_link(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_link_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
+__LA_DECL void archive_entry_set_mtime(struct archive_entry *, time_t, long);
+__LA_DECL void archive_entry_unset_mtime(struct archive_entry *);
+__LA_DECL void archive_entry_set_nlink(struct archive_entry *, unsigned int);
+__LA_DECL void archive_entry_set_pathname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_pathname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_pathname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
+__LA_DECL void archive_entry_set_rdev(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_rdevminor(struct archive_entry *, dev_t);
+__LA_DECL void archive_entry_set_size(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_unset_size(struct archive_entry *);
+__LA_DECL void archive_entry_copy_sourcepath(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *);
+__LA_DECL void archive_entry_set_symlink(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_symlink_type(struct archive_entry *, int);
+__LA_DECL void archive_entry_set_symlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_symlink(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_symlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_uid(struct archive_entry *, la_int64_t);
+__LA_DECL void archive_entry_set_uname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_uname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_uname(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int archive_entry_update_uname_utf8(struct archive_entry *, const char *);
+__LA_DECL void archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted);
+__LA_DECL void archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted);
+/*
+ * Routines to bulk copy fields to/from a platform-native "struct
+ * stat." Libarchive used to just store a struct stat inside of each
+ * archive_entry object, but this created issues when trying to
+ * manipulate archives on systems different than the ones they were
+ * created on.
+ *
+ * TODO: On Linux and other LFS systems, provide both stat32 and
+ * stat64 versions of these functions and all of the macro glue so
+ * that archive_entry_stat is magically defined to
+ * archive_entry_stat32 or archive_entry_stat64 as appropriate.
+ */
+__LA_DECL const struct stat *archive_entry_stat(struct archive_entry *);
+__LA_DECL void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+
+/*
+ * Storage for Mac OS-specific AppleDouble metadata information.
+ * Apple-format tar files store a separate binary blob containing
+ * encoded metadata with ACL, extended attributes, etc.
+ * This provides a place to store that blob.
+ */
+
+__LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *);
+__LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t);
+
+/*
+ * Digest routine. This is used to query the raw hex digest for the
+ * given entry. The type of digest is provided as an argument.
+ */
+#define ARCHIVE_ENTRY_DIGEST_MD5 0x00000001
+#define ARCHIVE_ENTRY_DIGEST_RMD160 0x00000002
+#define ARCHIVE_ENTRY_DIGEST_SHA1 0x00000003
+#define ARCHIVE_ENTRY_DIGEST_SHA256 0x00000004
+#define ARCHIVE_ENTRY_DIGEST_SHA384 0x00000005
+#define ARCHIVE_ENTRY_DIGEST_SHA512 0x00000006
+
+__LA_DECL const unsigned char * archive_entry_digest(struct archive_entry *, int /* type */);
+
+/*
+ * ACL routines. This used to simply store and return text-format ACL
+ * strings, but that proved insufficient for a number of reasons:
+ * = clients need control over uname/uid and gname/gid mappings
+ * = there are many different ACL text formats
+ * = would like to be able to read/convert archives containing ACLs
+ * on platforms that lack ACL libraries
+ *
+ * This last point, in particular, forces me to implement a reasonably
+ * complete set of ACL support routines.
+ */
+
+/*
+ * Permission bits.
+ */
+#define ARCHIVE_ENTRY_ACL_EXECUTE 0x00000001
+#define ARCHIVE_ENTRY_ACL_WRITE 0x00000002
+#define ARCHIVE_ENTRY_ACL_READ 0x00000004
+#define ARCHIVE_ENTRY_ACL_READ_DATA 0x00000008
+#define ARCHIVE_ENTRY_ACL_LIST_DIRECTORY 0x00000008
+#define ARCHIVE_ENTRY_ACL_WRITE_DATA 0x00000010
+#define ARCHIVE_ENTRY_ACL_ADD_FILE 0x00000010
+#define ARCHIVE_ENTRY_ACL_APPEND_DATA 0x00000020
+#define ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY 0x00000020
+#define ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS 0x00000040
+#define ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS 0x00000080
+#define ARCHIVE_ENTRY_ACL_DELETE_CHILD 0x00000100
+#define ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES 0x00000200
+#define ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES 0x00000400
+#define ARCHIVE_ENTRY_ACL_DELETE 0x00000800
+#define ARCHIVE_ENTRY_ACL_READ_ACL 0x00001000
+#define ARCHIVE_ENTRY_ACL_WRITE_ACL 0x00002000
+#define ARCHIVE_ENTRY_ACL_WRITE_OWNER 0x00004000
+#define ARCHIVE_ENTRY_ACL_SYNCHRONIZE 0x00008000
+
+#define ARCHIVE_ENTRY_ACL_PERMS_POSIX1E \
+ (ARCHIVE_ENTRY_ACL_EXECUTE \
+ | ARCHIVE_ENTRY_ACL_WRITE \
+ | ARCHIVE_ENTRY_ACL_READ)
+
+#define ARCHIVE_ENTRY_ACL_PERMS_NFS4 \
+ (ARCHIVE_ENTRY_ACL_EXECUTE \
+ | ARCHIVE_ENTRY_ACL_READ_DATA \
+ | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY \
+ | ARCHIVE_ENTRY_ACL_WRITE_DATA \
+ | ARCHIVE_ENTRY_ACL_ADD_FILE \
+ | ARCHIVE_ENTRY_ACL_APPEND_DATA \
+ | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY \
+ | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS \
+ | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS \
+ | ARCHIVE_ENTRY_ACL_DELETE_CHILD \
+ | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES \
+ | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES \
+ | ARCHIVE_ENTRY_ACL_DELETE \
+ | ARCHIVE_ENTRY_ACL_READ_ACL \
+ | ARCHIVE_ENTRY_ACL_WRITE_ACL \
+ | ARCHIVE_ENTRY_ACL_WRITE_OWNER \
+ | ARCHIVE_ENTRY_ACL_SYNCHRONIZE)
+
+/*
+ * Inheritance values (NFS4 ACLs only); included in permset.
+ */
+#define ARCHIVE_ENTRY_ACL_ENTRY_INHERITED 0x01000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT 0x02000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT 0x04000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT 0x08000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY 0x10000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS 0x20000000
+#define ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS 0x40000000
+
+#define ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4 \
+ (ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT \
+ | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT \
+ | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT \
+ | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY \
+ | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS \
+ | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS \
+ | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED)
+
+/* We need to be able to specify combinations of these. */
+#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 0x00000100 /* POSIX.1e only */
+#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 0x00000200 /* POSIX.1e only */
+#define ARCHIVE_ENTRY_ACL_TYPE_ALLOW 0x00000400 /* NFS4 only */
+#define ARCHIVE_ENTRY_ACL_TYPE_DENY 0x00000800 /* NFS4 only */
+#define ARCHIVE_ENTRY_ACL_TYPE_AUDIT 0x00001000 /* NFS4 only */
+#define ARCHIVE_ENTRY_ACL_TYPE_ALARM 0x00002000 /* NFS4 only */
+#define ARCHIVE_ENTRY_ACL_TYPE_POSIX1E (ARCHIVE_ENTRY_ACL_TYPE_ACCESS \
+ | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
+#define ARCHIVE_ENTRY_ACL_TYPE_NFS4 (ARCHIVE_ENTRY_ACL_TYPE_ALLOW \
+ | ARCHIVE_ENTRY_ACL_TYPE_DENY \
+ | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \
+ | ARCHIVE_ENTRY_ACL_TYPE_ALARM)
+
+/* Tag values mimic POSIX.1e */
+#define ARCHIVE_ENTRY_ACL_USER 10001 /* Specified user. */
+#define ARCHIVE_ENTRY_ACL_USER_OBJ 10002 /* User who owns the file. */
+#define ARCHIVE_ENTRY_ACL_GROUP 10003 /* Specified group. */
+#define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */
+#define ARCHIVE_ENTRY_ACL_MASK 10005 /* Modify group access (POSIX.1e only) */
+#define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public (POSIX.1e only) */
+#define ARCHIVE_ENTRY_ACL_EVERYONE 10107 /* Everyone (NFS4 only) */
+
+/*
+ * Set the ACL by clearing it and adding entries one at a time.
+ * Unlike the POSIX.1e ACL routines, you must specify the type
+ * (access/default) for each entry. Internally, the ACL data is just
+ * a soup of entries. API calls here allow you to retrieve just the
+ * entries of interest. This design (which goes against the spirit of
+ * POSIX.1e) is useful for handling archive formats that combine
+ * default and access information in a single ACL list.
+ */
+__LA_DECL void archive_entry_acl_clear(struct archive_entry *);
+__LA_DECL int archive_entry_acl_add_entry(struct archive_entry *,
+ int /* type */, int /* permset */, int /* tag */,
+ int /* qual */, const char * /* name */);
+__LA_DECL int archive_entry_acl_add_entry_w(struct archive_entry *,
+ int /* type */, int /* permset */, int /* tag */,
+ int /* qual */, const wchar_t * /* name */);
+
+/*
+ * To retrieve the ACL, first "reset", then repeatedly ask for the
+ * "next" entry. The want_type parameter allows you to request only
+ * certain types of entries.
+ */
+__LA_DECL int archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
+__LA_DECL int archive_entry_acl_next(struct archive_entry *, int /* want_type */,
+ int * /* type */, int * /* permset */, int * /* tag */,
+ int * /* qual */, const char ** /* name */);
+
+/*
+ * Construct a text-format ACL. The flags argument is a bitmask that
+ * can include any of the following:
+ *
+ * Flags only for archive entries with POSIX.1e ACL:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.
+ * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
+ * default ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and
+ * "mask" entries.
+ *
+ * Flags only for archive entries with NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for
+ * unset permissions and flags in NFSv4 ACL permission and flag fields
+ *
+ * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
+ * each ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma
+ * instead of newline.
+ */
+#define ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 0x00000001
+#define ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 0x00000002
+#define ARCHIVE_ENTRY_ACL_STYLE_SOLARIS 0x00000004
+#define ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA 0x00000008
+#define ARCHIVE_ENTRY_ACL_STYLE_COMPACT 0x00000010
+
+__LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *,
+ la_ssize_t * /* len */, int /* flags */);
+__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *,
+ la_ssize_t * /* len */, int /* flags */);
+__LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *,
+ const wchar_t * /* wtext */, int /* type */);
+__LA_DECL int archive_entry_acl_from_text(struct archive_entry *,
+ const char * /* text */, int /* type */);
+
+/* Deprecated constants */
+#define OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID 1024
+#define OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT 2048
+
+/* Deprecated functions */
+__LA_DECL const wchar_t *archive_entry_acl_text_w(struct archive_entry *,
+ int /* flags */) __LA_DEPRECATED;
+__LA_DECL const char *archive_entry_acl_text(struct archive_entry *,
+ int /* flags */) __LA_DEPRECATED;
+
+/* Return bitmask of ACL types in an archive entry */
+__LA_DECL int archive_entry_acl_types(struct archive_entry *);
+
+/* Return a count of entries matching 'want_type' */
+__LA_DECL int archive_entry_acl_count(struct archive_entry *, int /* want_type */);
+
+/* Return an opaque ACL object. */
+/* There's not yet anything clients can actually do with this... */
+struct archive_acl;
+__LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *);
+
+/*
+ * extended attributes
+ */
+
+__LA_DECL void archive_entry_xattr_clear(struct archive_entry *);
+__LA_DECL void archive_entry_xattr_add_entry(struct archive_entry *,
+ const char * /* name */, const void * /* value */,
+ size_t /* size */);
+
+/*
+ * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * "next" entry.
+ */
+
+__LA_DECL int archive_entry_xattr_count(struct archive_entry *);
+__LA_DECL int archive_entry_xattr_reset(struct archive_entry *);
+__LA_DECL int archive_entry_xattr_next(struct archive_entry *,
+ const char ** /* name */, const void ** /* value */, size_t *);
+
+/*
+ * sparse
+ */
+
+__LA_DECL void archive_entry_sparse_clear(struct archive_entry *);
+__LA_DECL void archive_entry_sparse_add_entry(struct archive_entry *,
+ la_int64_t /* offset */, la_int64_t /* length */);
+
+/*
+ * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * "next" entry.
+ */
+
+__LA_DECL int archive_entry_sparse_count(struct archive_entry *);
+__LA_DECL int archive_entry_sparse_reset(struct archive_entry *);
+__LA_DECL int archive_entry_sparse_next(struct archive_entry *,
+ la_int64_t * /* offset */, la_int64_t * /* length */);
+
+/*
+ * Utility to match up hardlinks.
+ *
+ * The 'struct archive_entry_linkresolver' is a cache of archive entries
+ * for files with multiple links. Here's how to use it:
+ * 1. Create a lookup object with archive_entry_linkresolver_new()
+ * 2. Tell it the archive format you're using.
+ * 3. Hand each archive_entry to archive_entry_linkify().
+ * That function will return 0, 1, or 2 entries that should
+ * be written.
+ * 4. Call archive_entry_linkify(resolver, NULL) until
+ * no more entries are returned.
+ * 5. Call archive_entry_linkresolver_free(resolver) to free resources.
+ *
+ * The entries returned have their hardlink and size fields updated
+ * appropriately. If an entry is passed in that does not refer to
+ * a file with multiple links, it is returned unchanged. The intention
+ * is that you should be able to simply filter all entries through
+ * this machine.
+ *
+ * To make things more efficient, be sure that each entry has a valid
+ * nlinks value. The hardlink cache uses this to track when all links
+ * have been found. If the nlinks value is zero, it will keep every
+ * name in the cache indefinitely, which can use a lot of memory.
+ *
+ * Note that archive_entry_size() is reset to zero if the file
+ * body should not be written to the archive. Pay attention!
+ */
+struct archive_entry_linkresolver;
+
+/*
+ * There are three different strategies for marking hardlinks.
+ * The descriptions below name them after the best-known
+ * formats that rely on each strategy:
+ *
+ * "Old cpio" is the simplest, it always returns any entry unmodified.
+ * As far as I know, only cpio formats use this. Old cpio archives
+ * store every link with the full body; the onus is on the dearchiver
+ * to detect and properly link the files as they are restored.
+ * "tar" is also pretty simple; it caches a copy the first time it sees
+ * any link. Subsequent appearances are modified to be hardlink
+ * references to the first one without any body. Used by all tar
+ * formats, although the newest tar formats permit the "old cpio" strategy
+ * as well. This strategy is very simple for the dearchiver,
+ * and reasonably straightforward for the archiver.
+ * "new cpio" is trickier. It stores the body only with the last
+ * occurrence. The complication is that we might not
+ * see every link to a particular file in a single session, so
+ * there's no easy way to know when we've seen the last occurrence.
+ * The solution here is to queue one link until we see the next.
+ * At the end of the session, you can enumerate any remaining
+ * entries by calling archive_entry_linkify(NULL) and store those
+ * bodies. If you have a file with three links l1, l2, and l3,
+ * you'll get the following behavior if you see all three links:
+ * linkify(l1) => NULL (the resolver stores l1 internally)
+ * linkify(l2) => l1 (resolver stores l2, you write l1)
+ * linkify(l3) => l2, l3 (all links seen, you can write both).
+ * If you only see l1 and l2, you'll get this behavior:
+ * linkify(l1) => NULL
+ * linkify(l2) => l1
+ * linkify(NULL) => l2 (at end, you retrieve remaining links)
+ * As the name suggests, this strategy is used by newer cpio variants.
+ * It's noticeably more complex for the archiver, slightly more complex
+ * for the dearchiver than the tar strategy, but makes it straightforward
+ * to restore a file using any link by simply continuing to scan until
+ * you see a link that is stored with a body. In contrast, the tar
+ * strategy requires you to rescan the archive from the beginning to
+ * correctly extract an arbitrary link.
+ */
+
+__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
+__LA_DECL void archive_entry_linkresolver_set_strategy(
+ struct archive_entry_linkresolver *, int /* format_code */);
+__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
+__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
+ struct archive_entry **, struct archive_entry **);
+__LA_DECL struct archive_entry *archive_entry_partial_links(
+ struct archive_entry_linkresolver *res, unsigned int *links);
+#ifdef __cplusplus
+}
+#endif
+
+/* This is meaningless outside of this header. */
+#undef __LA_DECL
+
+#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_copy_bhfi.c b/contrib/libs/libarchive/libarchive/archive_entry_copy_bhfi.c
new file mode 100644
index 0000000000..77bf38e450
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_copy_bhfi.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_private.h"
+#include "archive_entry.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+__inline static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+ ULARGE_INTEGER utc;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ *t = (time_t)(utc.QuadPart / 10000000); /* milli seconds base */
+ *ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
+ } else {
+ *t = 0;
+ *ns = 0;
+ }
+}
+
+void
+archive_entry_copy_bhfi(struct archive_entry *entry,
+ BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+ time_t secs;
+ long nsecs;
+
+ fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);
+ archive_entry_set_atime(entry, secs, nsecs);
+ fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);
+ archive_entry_set_mtime(entry, secs, nsecs);
+ fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);
+ archive_entry_set_birthtime(entry, secs, nsecs);
+ archive_entry_set_ctime(entry, secs, nsecs);
+ archive_entry_set_dev(entry, bhfi->dwVolumeSerialNumber);
+ archive_entry_set_ino64(entry, (((int64_t)bhfi->nFileIndexHigh) << 32)
+ + bhfi->nFileIndexLow);
+ archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);
+ archive_entry_set_size(entry, (((int64_t)bhfi->nFileSizeHigh) << 32)
+ + bhfi->nFileSizeLow);
+ /* archive_entry_set_mode(entry, st->st_mode); */
+}
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_copy_stat.c b/contrib/libs/libarchive/libarchive/archive_entry_copy_stat.c
new file mode 100644
index 0000000000..ac83868e8f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_copy_stat.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_copy_stat.c 189466 2009-03-07 00:52:02Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+void
+archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
+{
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.tv_nsec);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_NSEC
+ archive_entry_set_atime(entry, st->st_atime, st->st_atime_nsec);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_nsec);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+ archive_entry_set_atime(entry, st->st_atime, st->st_atime_n);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+ archive_entry_set_atime(entry, st->st_atime, st->st_uatime * 1000);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_uctime * 1000);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime * 1000);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+ archive_entry_set_atime(entry, st->st_atime, st->st_atime_usec * 1000);
+ archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_usec * 1000);
+ archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec * 1000);
+#else
+ archive_entry_set_atime(entry, st->st_atime, 0);
+ archive_entry_set_ctime(entry, st->st_ctime, 0);
+ archive_entry_set_mtime(entry, st->st_mtime, 0);
+#endif
+#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
+ archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_BIRTHTIME
+ archive_entry_set_birthtime(entry, st->st_birthtime, 0);
+#else
+ archive_entry_unset_birthtime(entry);
+#endif
+ archive_entry_set_dev(entry, st->st_dev);
+ archive_entry_set_gid(entry, st->st_gid);
+ archive_entry_set_uid(entry, st->st_uid);
+ archive_entry_set_ino(entry, st->st_ino);
+ archive_entry_set_nlink(entry, st->st_nlink);
+ archive_entry_set_rdev(entry, st->st_rdev);
+ archive_entry_set_size(entry, st->st_size);
+ archive_entry_set_mode(entry, st->st_mode);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_link_resolver.c b/contrib/libs/libarchive/libarchive/archive_entry_link_resolver.c
new file mode 100644
index 0000000000..c7d59497a7
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_link_resolver.c
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_link_resolver.c 201100 2009-12-28 03:05:31Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+/*
+ * This is mostly a pretty straightforward hash table implementation.
+ * The only interesting bit is the different strategies used to
+ * match up links. These strategies match those used by various
+ * archiving formats:
+ * tar - content stored with first link, remainder refer back to it.
+ * This requires us to match each subsequent link up with the
+ * first appearance.
+ * cpio - Old cpio just stored body with each link, match-ups were
+ * implicit. This is trivial.
+ * new cpio - New cpio only stores body with last link, match-ups
+ * are implicit. This is actually quite tricky; see the notes
+ * below.
+ */
+
+/* Users pass us a format code, we translate that into a strategy here. */
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR 0
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
+
+/* Initial size of link cache. */
+#define links_cache_initial_size 1024
+
+struct links_entry {
+ struct links_entry *next;
+ struct links_entry *previous;
+ struct archive_entry *canonical;
+ struct archive_entry *entry;
+ size_t hash;
+ unsigned int links; /* # links not yet seen */
+};
+
+struct archive_entry_linkresolver {
+ struct links_entry **buckets;
+ struct links_entry *spare;
+ unsigned long number_entries;
+ size_t number_buckets;
+ int strategy;
+};
+
+#define NEXT_ENTRY_DEFERRED 1
+#define NEXT_ENTRY_PARTIAL 2
+#define NEXT_ENTRY_ALL (NEXT_ENTRY_DEFERRED | NEXT_ENTRY_PARTIAL)
+
+static struct links_entry *find_entry(struct archive_entry_linkresolver *,
+ struct archive_entry *);
+static void grow_hash(struct archive_entry_linkresolver *);
+static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
+ struct archive_entry *);
+static struct links_entry *next_entry(struct archive_entry_linkresolver *,
+ int);
+
+struct archive_entry_linkresolver *
+archive_entry_linkresolver_new(void)
+{
+ struct archive_entry_linkresolver *res;
+
+ /* Check for positive power-of-two */
+ if (links_cache_initial_size == 0 ||
+ (links_cache_initial_size & (links_cache_initial_size - 1)) != 0)
+ return (NULL);
+
+ res = calloc(1, sizeof(struct archive_entry_linkresolver));
+ if (res == NULL)
+ return (NULL);
+ res->number_buckets = links_cache_initial_size;
+ res->buckets = calloc(res->number_buckets, sizeof(res->buckets[0]));
+ if (res->buckets == NULL) {
+ free(res);
+ return (NULL);
+ }
+ return (res);
+}
+
+void
+archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
+ int fmt)
+{
+ int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
+
+ switch (fmtbase) {
+ case ARCHIVE_FORMAT_7ZIP:
+ case ARCHIVE_FORMAT_AR:
+ case ARCHIVE_FORMAT_ZIP:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+ break;
+ case ARCHIVE_FORMAT_CPIO:
+ switch (fmt) {
+ case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
+ case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
+ break;
+ default:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+ break;
+ }
+ break;
+ case ARCHIVE_FORMAT_MTREE:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
+ break;
+ case ARCHIVE_FORMAT_ISO9660:
+ case ARCHIVE_FORMAT_SHAR:
+ case ARCHIVE_FORMAT_TAR:
+ case ARCHIVE_FORMAT_XAR:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
+ break;
+ default:
+ res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+ break;
+ }
+}
+
+void
+archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
+{
+ struct links_entry *le;
+
+ if (res == NULL)
+ return;
+
+ while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL)
+ archive_entry_free(le->entry);
+ free(res->buckets);
+ free(res);
+}
+
+void
+archive_entry_linkify(struct archive_entry_linkresolver *res,
+ struct archive_entry **e, struct archive_entry **f)
+{
+ struct links_entry *le;
+ struct archive_entry *t;
+
+ *f = NULL; /* Default: Don't return a second entry. */
+
+ if (*e == NULL) {
+ le = next_entry(res, NEXT_ENTRY_DEFERRED);
+ if (le != NULL) {
+ *e = le->entry;
+ le->entry = NULL;
+ }
+ return;
+ }
+
+ /* If it has only one link, then we're done. */
+ if (archive_entry_nlink(*e) == 1)
+ return;
+ /* Directories, devices never have hardlinks. */
+ if (archive_entry_filetype(*e) == AE_IFDIR
+ || archive_entry_filetype(*e) == AE_IFBLK
+ || archive_entry_filetype(*e) == AE_IFCHR)
+ return;
+
+ switch (res->strategy) {
+ case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
+ le = find_entry(res, *e);
+ if (le != NULL) {
+ archive_entry_unset_size(*e);
+ archive_entry_copy_hardlink(*e,
+ archive_entry_pathname(le->canonical));
+ } else
+ insert_entry(res, *e);
+ return;
+ case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
+ le = find_entry(res, *e);
+ if (le != NULL) {
+ archive_entry_copy_hardlink(*e,
+ archive_entry_pathname(le->canonical));
+ } else
+ insert_entry(res, *e);
+ return;
+ case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
+ /* This one is trivial. */
+ return;
+ case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
+ le = find_entry(res, *e);
+ if (le != NULL) {
+ /*
+ * Put the new entry in le, return the
+ * old entry from le.
+ */
+ t = *e;
+ *e = le->entry;
+ le->entry = t;
+ /* Make the old entry into a hardlink. */
+ archive_entry_unset_size(*e);
+ archive_entry_copy_hardlink(*e,
+ archive_entry_pathname(le->canonical));
+ /* If we ran out of links, return the
+ * final entry as well. */
+ if (le->links == 0) {
+ *f = le->entry;
+ le->entry = NULL;
+ }
+ } else {
+ /*
+ * If we haven't seen it, tuck it away
+ * for future use.
+ */
+ le = insert_entry(res, *e);
+ if (le == NULL)
+ /* XXX We should return an error code XXX */
+ return;
+ le->entry = *e;
+ *e = NULL;
+ }
+ return;
+ default:
+ break;
+ }
+ return;
+}
+
+static struct links_entry *
+find_entry(struct archive_entry_linkresolver *res,
+ struct archive_entry *entry)
+{
+ struct links_entry *le;
+ size_t hash, bucket;
+ dev_t dev;
+ int64_t ino;
+
+ /* Free a held entry. */
+ if (res->spare != NULL) {
+ archive_entry_free(res->spare->canonical);
+ archive_entry_free(res->spare->entry);
+ free(res->spare);
+ res->spare = NULL;
+ }
+
+ dev = archive_entry_dev(entry);
+ ino = archive_entry_ino64(entry);
+ hash = (size_t)(dev ^ ino);
+
+ /* Try to locate this entry in the links cache. */
+ bucket = hash & (res->number_buckets - 1);
+ for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+ if (le->hash == hash
+ && dev == archive_entry_dev(le->canonical)
+ && ino == archive_entry_ino64(le->canonical)) {
+ /*
+ * Decrement link count each time and release
+ * the entry if it hits zero. This saves
+ * memory and is necessary for detecting
+ * missed links.
+ */
+ --le->links;
+ if (le->links > 0)
+ return (le);
+ /* Remove it from this hash bucket. */
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (res->buckets[bucket] == le)
+ res->buckets[bucket] = le->next;
+ res->number_entries--;
+ /* Defer freeing this entry. */
+ res->spare = le;
+ return (le);
+ }
+ }
+ return (NULL);
+}
+
+static struct links_entry *
+next_entry(struct archive_entry_linkresolver *res, int mode)
+{
+ struct links_entry *le;
+ size_t bucket;
+
+ /* Free a held entry. */
+ if (res->spare != NULL) {
+ archive_entry_free(res->spare->canonical);
+ archive_entry_free(res->spare->entry);
+ free(res->spare);
+ res->spare = NULL;
+ }
+
+ /* Look for next non-empty bucket in the links cache. */
+ for (bucket = 0; bucket < res->number_buckets; bucket++) {
+ for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+ if (le->entry != NULL &&
+ (mode & NEXT_ENTRY_DEFERRED) == 0)
+ continue;
+ if (le->entry == NULL &&
+ (mode & NEXT_ENTRY_PARTIAL) == 0)
+ continue;
+ /* Remove it from this hash bucket. */
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ else
+ res->buckets[bucket] = le->next;
+ res->number_entries--;
+ /* Defer freeing this entry. */
+ res->spare = le;
+ return (le);
+ }
+ }
+ return (NULL);
+}
+
+static struct links_entry *
+insert_entry(struct archive_entry_linkresolver *res,
+ struct archive_entry *entry)
+{
+ struct links_entry *le;
+ size_t hash, bucket;
+
+ /* Add this entry to the links cache. */
+ le = calloc(1, sizeof(struct links_entry));
+ if (le == NULL)
+ return (NULL);
+ le->canonical = archive_entry_clone(entry);
+
+ /* If the links cache is getting too full, enlarge the hash table. */
+ if (res->number_entries > res->number_buckets * 2)
+ grow_hash(res);
+
+ hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));
+ bucket = hash & (res->number_buckets - 1);
+
+ /* If we could allocate the entry, record it. */
+ if (res->buckets[bucket] != NULL)
+ res->buckets[bucket]->previous = le;
+ res->number_entries++;
+ le->next = res->buckets[bucket];
+ le->previous = NULL;
+ res->buckets[bucket] = le;
+ le->hash = hash;
+ le->links = archive_entry_nlink(entry) - 1;
+ return (le);
+}
+
+static void
+grow_hash(struct archive_entry_linkresolver *res)
+{
+ struct links_entry *le, **new_buckets;
+ size_t new_size;
+ size_t i, bucket;
+
+ /* Try to enlarge the bucket list. */
+ new_size = res->number_buckets * 2;
+ if (new_size < res->number_buckets)
+ return;
+ new_buckets = calloc(new_size, sizeof(struct links_entry *));
+
+ if (new_buckets == NULL)
+ return;
+
+ for (i = 0; i < res->number_buckets; i++) {
+ while (res->buckets[i] != NULL) {
+ /* Remove entry from old bucket. */
+ le = res->buckets[i];
+ res->buckets[i] = le->next;
+
+ /* Add entry to new bucket. */
+ bucket = le->hash & (new_size - 1);
+
+ if (new_buckets[bucket] != NULL)
+ new_buckets[bucket]->previous = le;
+ le->next = new_buckets[bucket];
+ le->previous = NULL;
+ new_buckets[bucket] = le;
+ }
+ }
+ free(res->buckets);
+ res->buckets = new_buckets;
+ res->number_buckets = new_size;
+}
+
+struct archive_entry *
+archive_entry_partial_links(struct archive_entry_linkresolver *res,
+ unsigned int *links)
+{
+ struct archive_entry *e;
+ struct links_entry *le;
+
+ /* Free a held entry. */
+ if (res->spare != NULL) {
+ archive_entry_free(res->spare->canonical);
+ archive_entry_free(res->spare->entry);
+ free(res->spare);
+ res->spare = NULL;
+ }
+
+ le = next_entry(res, NEXT_ENTRY_PARTIAL);
+ if (le != NULL) {
+ e = le->canonical;
+ if (links != NULL)
+ *links = le->links;
+ le->canonical = NULL;
+ } else {
+ e = NULL;
+ if (links != NULL)
+ *links = 0;
+ }
+ return (e);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_locale.h b/contrib/libs/libarchive/libarchive/archive_entry_locale.h
new file mode 100644
index 0000000000..803c0368bb
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_locale.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED
+#define ARCHIVE_ENTRY_LOCALE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+struct archive_entry;
+struct archive_string_conv;
+
+/*
+ * Utility functions to set and get entry attributes by translating
+ * character-set. These are designed for use in format readers and writers.
+ *
+ * The return code and interface of these are quite different from other
+ * functions for archive_entry defined in archive_entry.h.
+ * Common return code are:
+ * Return 0 if the string conversion succeeded.
+ * Return -1 if the string conversion failed.
+ */
+
+#define archive_entry_gname_l _archive_entry_gname_l
+int _archive_entry_gname_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_hardlink_l _archive_entry_hardlink_l
+int _archive_entry_hardlink_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_pathname_l _archive_entry_pathname_l
+int _archive_entry_pathname_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_symlink_l _archive_entry_symlink_l
+int _archive_entry_symlink_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_uname_l _archive_entry_uname_l
+int _archive_entry_uname_l(struct archive_entry *,
+ const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_acl_text_l _archive_entry_acl_text_l
+int _archive_entry_acl_text_l(struct archive_entry *, int,
+const char **, size_t *, struct archive_string_conv *) __LA_DEPRECATED;
+#define archive_entry_acl_to_text_l _archive_entry_acl_to_text_l
+char *_archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int,
+ struct archive_string_conv *);
+#define archive_entry_acl_from_text_l _archive_entry_acl_from_text_l
+int _archive_entry_acl_from_text_l(struct archive_entry *, const char* text,
+ int type, struct archive_string_conv *);
+#define archive_entry_copy_gname_l _archive_entry_copy_gname_l
+int _archive_entry_copy_gname_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_hardlink_l _archive_entry_copy_hardlink_l
+int _archive_entry_copy_hardlink_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_link_l _archive_entry_copy_link_l
+int _archive_entry_copy_link_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_pathname_l _archive_entry_copy_pathname_l
+int _archive_entry_copy_pathname_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_symlink_l _archive_entry_copy_symlink_l
+int _archive_entry_copy_symlink_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_uname_l _archive_entry_copy_uname_l
+int _archive_entry_copy_uname_l(struct archive_entry *,
+ const char *, size_t, struct archive_string_conv *);
+
+#endif /* ARCHIVE_ENTRY_LOCALE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_private.h b/contrib/libs/libarchive/libarchive/archive_entry_private.h
new file mode 100644
index 0000000000..cf4deb24ec
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_private.h
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_entry_private.h 201096 2009-12-28 02:41:27Z kientzle $
+ */
+
+#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
+#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_acl_private.h"
+#include "archive_string.h"
+
+struct ae_xattr {
+ struct ae_xattr *next;
+
+ char *name;
+ void *value;
+ size_t size;
+};
+
+struct ae_sparse {
+ struct ae_sparse *next;
+
+ int64_t offset;
+ int64_t length;
+};
+
+struct ae_digest {
+ unsigned char md5[16];
+ unsigned char rmd160[20];
+ unsigned char sha1[20];
+ unsigned char sha256[32];
+ unsigned char sha384[48];
+ unsigned char sha512[64];
+};
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, this is a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries
+ * that are supported by "pax interchange" format. However, GNU, ustar,
+ * cpio, and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry. Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry). There are tricky
+ * API issues involved, so this is not going to happen until
+ * there's a real demand for it.
+ *
+ * TODO: Design a good API for handling sparse files.
+ */
+struct archive_entry {
+ struct archive *archive;
+
+ /*
+ * Note that ae_stat.st_mode & AE_IFMT can be 0!
+ *
+ * This occurs when the actual file type of the object is not
+ * in the archive. For example, 'tar' archives store
+ * hardlinks without marking the type of the underlying
+ * object.
+ */
+
+ /*
+ * We have a "struct aest" for holding file metadata rather than just
+ * a "struct stat" because on some platforms the "struct stat" has
+ * fields which are too narrow to hold the range of possible values;
+ * we don't want to lose information if we read an archive and write
+ * out another (e.g., in "tar -cf new.tar @old.tar").
+ *
+ * The "stat" pointer points to some form of platform-specific struct
+ * stat; it is declared as a void * rather than a struct stat * as
+ * some platforms have multiple varieties of stat structures.
+ */
+ void *stat;
+ int stat_valid; /* Set to 0 whenever a field in aest changes. */
+
+ struct aest {
+ int64_t aest_atime;
+ uint32_t aest_atime_nsec;
+ int64_t aest_ctime;
+ uint32_t aest_ctime_nsec;
+ int64_t aest_mtime;
+ uint32_t aest_mtime_nsec;
+ int64_t aest_birthtime;
+ uint32_t aest_birthtime_nsec;
+ int64_t aest_gid;
+ int64_t aest_ino;
+ uint32_t aest_nlink;
+ uint64_t aest_size;
+ int64_t aest_uid;
+ /*
+ * Because converting between device codes and
+ * major/minor values is platform-specific and
+ * inherently a bit risky, we only do that conversion
+ * lazily. That way, we will do a better job of
+ * preserving information in those cases where no
+ * conversion is actually required.
+ */
+ int aest_dev_is_broken_down;
+ dev_t aest_dev;
+ dev_t aest_devmajor;
+ dev_t aest_devminor;
+ int aest_rdev_is_broken_down;
+ dev_t aest_rdev;
+ dev_t aest_rdevmajor;
+ dev_t aest_rdevminor;
+ } ae_stat;
+
+ int ae_set; /* bitmap of fields that are currently set */
+#define AE_SET_HARDLINK 1
+#define AE_SET_SYMLINK 2
+#define AE_SET_ATIME 4
+#define AE_SET_CTIME 8
+#define AE_SET_MTIME 16
+#define AE_SET_BIRTHTIME 32
+#define AE_SET_SIZE 64
+#define AE_SET_INO 128
+#define AE_SET_DEV 256
+
+ /*
+ * Use aes here so that we get transparent mbs<->wcs conversions.
+ */
+ struct archive_mstring ae_fflags_text; /* Text fflags per fflagstostr(3) */
+ unsigned long ae_fflags_set; /* Bitmap fflags */
+ unsigned long ae_fflags_clear;
+ struct archive_mstring ae_gname; /* Name of owning group */
+ struct archive_mstring ae_hardlink; /* Name of target for hardlink */
+ struct archive_mstring ae_pathname; /* Name of entry */
+ struct archive_mstring ae_symlink; /* symlink contents */
+ struct archive_mstring ae_uname; /* Name of owner */
+
+ /* Not used within libarchive; useful for some clients. */
+ struct archive_mstring ae_sourcepath; /* Path this entry is sourced from. */
+
+#define AE_ENCRYPTION_NONE 0
+#define AE_ENCRYPTION_DATA 1
+#define AE_ENCRYPTION_METADATA 2
+ char encryption;
+
+ void *mac_metadata;
+ size_t mac_metadata_size;
+
+ /* Digest support. */
+ struct ae_digest digest;
+
+ /* ACL support. */
+ struct archive_acl acl;
+
+ /* extattr support. */
+ struct ae_xattr *xattr_head;
+ struct ae_xattr *xattr_p;
+
+ /* sparse support. */
+ struct ae_sparse *sparse_head;
+ struct ae_sparse *sparse_tail;
+ struct ae_sparse *sparse_p;
+
+ /* Miscellaneous. */
+ char strmode[12];
+
+ /* Symlink type support */
+ int ae_symlink_type;
+};
+
+int
+archive_entry_set_digest(struct archive_entry *entry, int type,
+ const unsigned char *digest);
+
+#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_sparse.c b/contrib/libs/libarchive/libarchive/archive_entry_sparse.c
new file mode 100644
index 0000000000..74917b37b8
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_sparse.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+/*
+ * sparse handling
+ */
+
+void
+archive_entry_sparse_clear(struct archive_entry *entry)
+{
+ struct ae_sparse *sp;
+
+ while (entry->sparse_head != NULL) {
+ sp = entry->sparse_head->next;
+ free(entry->sparse_head);
+ entry->sparse_head = sp;
+ }
+ entry->sparse_tail = NULL;
+}
+
+void
+archive_entry_sparse_add_entry(struct archive_entry *entry,
+ la_int64_t offset, la_int64_t length)
+{
+ struct ae_sparse *sp;
+
+ if (offset < 0 || length < 0)
+ /* Invalid value */
+ return;
+ if (offset > INT64_MAX - length ||
+ offset + length > archive_entry_size(entry))
+ /* A value of "length" parameter is too large. */
+ return;
+ if ((sp = entry->sparse_tail) != NULL) {
+ if (sp->offset + sp->length > offset)
+ /* Invalid value. */
+ return;
+ if (sp->offset + sp->length == offset) {
+ if (sp->offset + sp->length + length < 0)
+ /* A value of "length" parameter is
+ * too large. */
+ return;
+ /* Expand existing sparse block size. */
+ sp->length += length;
+ return;
+ }
+ }
+
+ if ((sp = (struct ae_sparse *)malloc(sizeof(*sp))) == NULL)
+ /* XXX Error XXX */
+ return;
+
+ sp->offset = offset;
+ sp->length = length;
+ sp->next = NULL;
+
+ if (entry->sparse_head == NULL)
+ entry->sparse_head = entry->sparse_tail = sp;
+ else {
+ /* Add a new sparse block to the tail of list. */
+ if (entry->sparse_tail != NULL)
+ entry->sparse_tail->next = sp;
+ entry->sparse_tail = sp;
+ }
+}
+
+
+/*
+ * returns number of the sparse entries
+ */
+int
+archive_entry_sparse_count(struct archive_entry *entry)
+{
+ struct ae_sparse *sp;
+ int count = 0;
+
+ for (sp = entry->sparse_head; sp != NULL; sp = sp->next)
+ count++;
+
+ /*
+ * Sanity check if this entry is exactly sparse.
+ * If amount of sparse blocks is just one and it indicates the whole
+ * file data, we should remove it and return zero.
+ */
+ if (count == 1) {
+ sp = entry->sparse_head;
+ if (sp->offset == 0 &&
+ sp->length >= archive_entry_size(entry)) {
+ count = 0;
+ archive_entry_sparse_clear(entry);
+ }
+ }
+
+ return (count);
+}
+
+int
+archive_entry_sparse_reset(struct archive_entry * entry)
+{
+ entry->sparse_p = entry->sparse_head;
+
+ return archive_entry_sparse_count(entry);
+}
+
+int
+archive_entry_sparse_next(struct archive_entry * entry,
+ la_int64_t *offset, la_int64_t *length)
+{
+ if (entry->sparse_p) {
+ *offset = entry->sparse_p->offset;
+ *length = entry->sparse_p->length;
+
+ entry->sparse_p = entry->sparse_p->next;
+
+ return (ARCHIVE_OK);
+ } else {
+ *offset = 0;
+ *length = 0;
+ return (ARCHIVE_WARN);
+ }
+}
+
+/*
+ * end of sparse handling
+ */
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_stat.c b/contrib/libs/libarchive/libarchive/archive_entry_stat.c
new file mode 100644
index 0000000000..71a407b1f8
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_stat.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_stat.c 201100 2009-12-28 03:05:31Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+
+const struct stat *
+archive_entry_stat(struct archive_entry *entry)
+{
+ struct stat *st;
+ if (entry->stat == NULL) {
+ entry->stat = calloc(1, sizeof(*st));
+ if (entry->stat == NULL)
+ return (NULL);
+ entry->stat_valid = 0;
+ }
+
+ /*
+ * If none of the underlying fields have been changed, we
+ * don't need to regenerate. In theory, we could use a bitmap
+ * here to flag only those items that have changed, but the
+ * extra complexity probably isn't worth it. It will be very
+ * rare for anyone to change just one field then request a new
+ * stat structure.
+ */
+ if (entry->stat_valid)
+ return (entry->stat);
+
+ st = entry->stat;
+ /*
+ * Use the public interfaces to extract items, so that
+ * the appropriate conversions get invoked.
+ */
+ st->st_atime = archive_entry_atime(entry);
+#if HAVE_STRUCT_STAT_ST_BIRTHTIME
+ st->st_birthtime = archive_entry_birthtime(entry);
+#endif
+ st->st_ctime = archive_entry_ctime(entry);
+ st->st_mtime = archive_entry_mtime(entry);
+ st->st_dev = archive_entry_dev(entry);
+ st->st_gid = (gid_t)archive_entry_gid(entry);
+ st->st_uid = (uid_t)archive_entry_uid(entry);
+ st->st_ino = (ino_t)archive_entry_ino64(entry);
+ st->st_nlink = archive_entry_nlink(entry);
+ st->st_rdev = archive_entry_rdev(entry);
+ st->st_size = (off_t)archive_entry_size(entry);
+ st->st_mode = archive_entry_mode(entry);
+
+ /*
+ * On systems that support high-res timestamps, copy that
+ * information into struct stat.
+ */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry);
+ st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry);
+ st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ st->st_atim.tv_nsec = archive_entry_atime_nsec(entry);
+ st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry);
+ st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+ st->st_atime_n = archive_entry_atime_nsec(entry);
+ st->st_ctime_n = archive_entry_ctime_nsec(entry);
+ st->st_mtime_n = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+ st->st_uatime = archive_entry_atime_nsec(entry) / 1000;
+ st->st_uctime = archive_entry_ctime_nsec(entry) / 1000;
+ st->st_umtime = archive_entry_mtime_nsec(entry) / 1000;
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+ st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000;
+ st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000;
+ st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000;
+#endif
+#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
+ st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry);
+#endif
+
+ /*
+ * TODO: On Linux, store 32 or 64 here depending on whether
+ * the cached stat structure is a stat32 or a stat64. This
+ * will allow us to support both variants interchangeably.
+ */
+ entry->stat_valid = 1;
+
+ return (st);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_strmode.c b/contrib/libs/libarchive/libarchive/archive_entry_strmode.c
new file mode 100644
index 0000000000..af2517a321
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_strmode.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry_strmode.c,v 1.4 2008/06/15 05:14:01 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+
+const char *
+archive_entry_strmode(struct archive_entry *entry)
+{
+ static const mode_t permbits[] =
+ { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 };
+ char *bp = entry->strmode;
+ mode_t mode;
+ int i;
+
+ /* Fill in a default string, then selectively override. */
+ strcpy(bp, "?rwxrwxrwx ");
+
+ mode = archive_entry_mode(entry);
+ switch (archive_entry_filetype(entry)) {
+ case AE_IFREG: bp[0] = '-'; break;
+ case AE_IFBLK: bp[0] = 'b'; break;
+ case AE_IFCHR: bp[0] = 'c'; break;
+ case AE_IFDIR: bp[0] = 'd'; break;
+ case AE_IFLNK: bp[0] = 'l'; break;
+ case AE_IFSOCK: bp[0] = 's'; break;
+ case AE_IFIFO: bp[0] = 'p'; break;
+ default:
+ if (archive_entry_hardlink(entry) != NULL) {
+ bp[0] = 'h';
+ break;
+ }
+ }
+
+ for (i = 0; i < 9; i++)
+ if (!(mode & permbits[i]))
+ bp[i+1] = '-';
+
+ if (mode & S_ISUID) {
+ if (mode & 0100) bp[3] = 's';
+ else bp[3] = 'S';
+ }
+ if (mode & S_ISGID) {
+ if (mode & 0010) bp[6] = 's';
+ else bp[6] = 'S';
+ }
+ if (mode & S_ISVTX) {
+ if (mode & 0001) bp[9] = 't';
+ else bp[9] = 'T';
+ }
+ if (archive_entry_acl_types(entry) != 0)
+ bp[10] = '+';
+
+ return (bp);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_entry_xattr.c b/contrib/libs/libarchive/libarchive/archive_entry_xattr.c
new file mode 100644
index 0000000000..e28e515aa8
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_entry_xattr.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_xattr.c 201096 2009-12-28 02:41:27Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h> /* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* for Linux file flags */
+#endif
+#include <stddef.h>
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+/*
+ * extended attribute handling
+ */
+
+void
+archive_entry_xattr_clear(struct archive_entry *entry)
+{
+ struct ae_xattr *xp;
+
+ while (entry->xattr_head != NULL) {
+ xp = entry->xattr_head->next;
+ free(entry->xattr_head->name);
+ free(entry->xattr_head->value);
+ free(entry->xattr_head);
+ entry->xattr_head = xp;
+ }
+
+ entry->xattr_head = NULL;
+}
+
+void
+archive_entry_xattr_add_entry(struct archive_entry *entry,
+ const char *name, const void *value, size_t size)
+{
+ struct ae_xattr *xp;
+
+ if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL)
+ __archive_errx(1, "Out of memory");
+
+ if ((xp->name = strdup(name)) == NULL)
+ __archive_errx(1, "Out of memory");
+
+ if ((xp->value = malloc(size)) != NULL) {
+ memcpy(xp->value, value, size);
+ xp->size = size;
+ } else
+ xp->size = 0;
+
+ xp->next = entry->xattr_head;
+ entry->xattr_head = xp;
+}
+
+
+/*
+ * returns number of the extended attribute entries
+ */
+int
+archive_entry_xattr_count(struct archive_entry *entry)
+{
+ struct ae_xattr *xp;
+ int count = 0;
+
+ for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
+ count++;
+
+ return count;
+}
+
+int
+archive_entry_xattr_reset(struct archive_entry * entry)
+{
+ entry->xattr_p = entry->xattr_head;
+
+ return archive_entry_xattr_count(entry);
+}
+
+int
+archive_entry_xattr_next(struct archive_entry * entry,
+ const char **name, const void **value, size_t *size)
+{
+ if (entry->xattr_p) {
+ *name = entry->xattr_p->name;
+ *value = entry->xattr_p->value;
+ *size = entry->xattr_p->size;
+
+ entry->xattr_p = entry->xattr_p->next;
+
+ return (ARCHIVE_OK);
+ } else {
+ *name = NULL;
+ *value = NULL;
+ *size = (size_t)0;
+ return (ARCHIVE_WARN);
+ }
+}
+
+/*
+ * end of xattr handling
+ */
diff --git a/contrib/libs/libarchive/libarchive/archive_getdate.c b/contrib/libs/libarchive/libarchive/archive_getdate.c
new file mode 100644
index 0000000000..20ab1b1588
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_getdate.c
@@ -0,0 +1,1104 @@
+/*
+ * This code is in the public domain and has no copyright.
+ *
+ * This is a plain C recursive-descent translation of an old
+ * public-domain YACC grammar that has been used for parsing dates in
+ * very many open-source projects.
+ *
+ * Since the original authors were generous enough to donate their
+ * work to the public domain, I feel compelled to match their
+ * generosity.
+ *
+ * Tim Kientzle, February 2009.
+ */
+
+/*
+ * Header comment from original getdate.y:
+ */
+
+/*
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+**
+** This grammar has 10 shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+
+#include "archive_platform.h"
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define __LIBARCHIVE_BUILD 1
+#include "archive_getdate.h"
+
+/* Basic time units. */
+#define EPOCH 1970
+#define MINUTE (60L)
+#define HOUR (60L * MINUTE)
+#define DAY (24L * HOUR)
+
+/* Daylight-savings mode: on, off, or not yet known. */
+enum DSTMODE { DSTon, DSToff, DSTmaybe };
+/* Meridian: am or pm. */
+enum { tAM, tPM };
+/* Token types returned by nexttoken() */
+enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,
+ tUNUMBER, tZONE, tDST };
+struct token { int token; time_t value; };
+
+/*
+ * Parser state.
+ */
+struct gdstate {
+ struct token *tokenp; /* Pointer to next token. */
+ /* HaveXxxx counts how many of this kind of phrase we've seen;
+ * it's a fatal error to have more than one time, zone, day,
+ * or date phrase. */
+ int HaveYear;
+ int HaveMonth;
+ int HaveDay;
+ int HaveWeekDay; /* Day of week */
+ int HaveTime; /* Hour/minute/second */
+ int HaveZone; /* timezone and/or DST info */
+ int HaveRel; /* time offset; we can have more than one */
+ /* Absolute time values. */
+ time_t Timezone; /* Seconds offset from GMT */
+ time_t Day;
+ time_t Hour;
+ time_t Minutes;
+ time_t Month;
+ time_t Seconds;
+ time_t Year;
+ /* DST selection */
+ enum DSTMODE DSTmode;
+ /* Day of week accounting, e.g., "3rd Tuesday" */
+ time_t DayOrdinal; /* "3" in "3rd Tuesday" */
+ time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */
+ /* Relative time values: hour/day/week offsets are measured in
+ * seconds, month/year are counted in months. */
+ time_t RelMonth;
+ time_t RelSeconds;
+};
+
+/*
+ * A series of functions that recognize certain common time phrases.
+ * Each function returns 1 if it managed to make sense of some of the
+ * tokens, zero otherwise.
+ */
+
+/*
+ * hour:minute or hour:minute:second with optional AM, PM, or numeric
+ * timezone offset
+ */
+static int
+timephrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == ':'
+ && gds->tokenp[2].token == tUNUMBER
+ && gds->tokenp[3].token == ':'
+ && gds->tokenp[4].token == tUNUMBER) {
+ /* "12:14:18" or "22:08:07" */
+ ++gds->HaveTime;
+ gds->Hour = gds->tokenp[0].value;
+ gds->Minutes = gds->tokenp[2].value;
+ gds->Seconds = gds->tokenp[4].value;
+ gds->tokenp += 5;
+ }
+ else if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == ':'
+ && gds->tokenp[2].token == tUNUMBER) {
+ /* "12:14" or "22:08" */
+ ++gds->HaveTime;
+ gds->Hour = gds->tokenp[0].value;
+ gds->Minutes = gds->tokenp[2].value;
+ gds->Seconds = 0;
+ gds->tokenp += 3;
+ }
+ else if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tAMPM) {
+ /* "7" is a time if it's followed by "am" or "pm" */
+ ++gds->HaveTime;
+ gds->Hour = gds->tokenp[0].value;
+ gds->Minutes = gds->Seconds = 0;
+ /* We'll handle the AM/PM below. */
+ gds->tokenp += 1;
+ } else {
+ /* We can't handle this. */
+ return 0;
+ }
+
+ if (gds->tokenp[0].token == tAMPM) {
+ /* "7:12pm", "12:20:13am" */
+ if (gds->Hour == 12)
+ gds->Hour = 0;
+ if (gds->tokenp[0].value == tPM)
+ gds->Hour += 12;
+ gds->tokenp += 1;
+ }
+ if (gds->tokenp[0].token == '+'
+ && gds->tokenp[1].token == tUNUMBER) {
+ /* "7:14+0700" */
+ gds->HaveZone++;
+ gds->DSTmode = DSToff;
+ gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR
+ + (gds->tokenp[1].value % 100) * MINUTE);
+ gds->tokenp += 2;
+ }
+ if (gds->tokenp[0].token == '-'
+ && gds->tokenp[1].token == tUNUMBER) {
+ /* "19:14:12-0530" */
+ gds->HaveZone++;
+ gds->DSTmode = DSToff;
+ gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR
+ + (gds->tokenp[1].value % 100) * MINUTE);
+ gds->tokenp += 2;
+ }
+ return 1;
+}
+
+/*
+ * Timezone name, possibly including DST.
+ */
+static int
+zonephrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == tZONE
+ && gds->tokenp[1].token == tDST) {
+ gds->HaveZone++;
+ gds->Timezone = gds->tokenp[0].value;
+ gds->DSTmode = DSTon;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tZONE) {
+ gds->HaveZone++;
+ gds->Timezone = gds->tokenp[0].value;
+ gds->DSTmode = DSToff;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tDAYZONE) {
+ gds->HaveZone++;
+ gds->Timezone = gds->tokenp[0].value;
+ gds->DSTmode = DSTon;
+ gds->tokenp += 1;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Year/month/day in various combinations.
+ */
+static int
+datephrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == '/'
+ && gds->tokenp[2].token == tUNUMBER
+ && gds->tokenp[3].token == '/'
+ && gds->tokenp[4].token == tUNUMBER) {
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ if (gds->tokenp[0].value >= 13) {
+ /* First number is big: 2004/01/29, 99/02/17 */
+ gds->Year = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[2].value;
+ gds->Day = gds->tokenp[4].value;
+ } else if ((gds->tokenp[4].value >= 13)
+ || (gds->tokenp[2].value >= 13)) {
+ /* Last number is big: 01/07/98 */
+ /* Middle number is big: 01/29/04 */
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[2].value;
+ gds->Year = gds->tokenp[4].value;
+ } else {
+ /* No significant clues: 02/03/04 */
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[2].value;
+ gds->Year = gds->tokenp[4].value;
+ }
+ gds->tokenp += 5;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == '/'
+ && gds->tokenp[2].token == tUNUMBER) {
+ /* "1/15" */
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == '-'
+ && gds->tokenp[2].token == tUNUMBER
+ && gds->tokenp[3].token == '-'
+ && gds->tokenp[4].token == tUNUMBER) {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Year = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[2].value;
+ gds->Day = gds->tokenp[4].value;
+ gds->tokenp += 5;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == '-'
+ && gds->tokenp[2].token == tMONTH
+ && gds->tokenp[3].token == '-'
+ && gds->tokenp[4].token == tUNUMBER) {
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ if (gds->tokenp[0].value > 31) {
+ /* e.g. 1992-Jun-17 */
+ gds->Year = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[2].value;
+ gds->Day = gds->tokenp[4].value;
+ } else {
+ /* e.g. 17-JUN-1992. */
+ gds->Day = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[2].value;
+ gds->Year = gds->tokenp[4].value;
+ }
+ gds->tokenp += 5;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tMONTH
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == ','
+ && gds->tokenp[3].token == tUNUMBER) {
+ /* "June 17, 2001" */
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[1].value;
+ gds->Year = gds->tokenp[3].value;
+ gds->tokenp += 4;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tMONTH
+ && gds->tokenp[1].token == tUNUMBER) {
+ /* "May 3" */
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Month = gds->tokenp[0].value;
+ gds->Day = gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tMONTH
+ && gds->tokenp[2].token == tUNUMBER) {
+ /* "12 Sept 1997" */
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Day = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[1].value;
+ gds->Year = gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tMONTH) {
+ /* "12 Sept" */
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Day = gds->tokenp[0].value;
+ gds->Month = gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc.
+ */
+static int
+relunitphrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == '-'
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == tSEC_UNIT) {
+ /* "-3 hours" */
+ gds->HaveRel++;
+ gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+ if (gds->tokenp[0].token == '+'
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == tSEC_UNIT) {
+ /* "+1 minute" */
+ gds->HaveRel++;
+ gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tSEC_UNIT) {
+ /* "1 day" */
+ gds->HaveRel++;
+ gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+ if (gds->tokenp[0].token == '-'
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == tMONTH_UNIT) {
+ /* "-3 months" */
+ gds->HaveRel++;
+ gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+ if (gds->tokenp[0].token == '+'
+ && gds->tokenp[1].token == tUNUMBER
+ && gds->tokenp[2].token == tMONTH_UNIT) {
+ /* "+5 years" */
+ gds->HaveRel++;
+ gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value;
+ gds->tokenp += 3;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tMONTH_UNIT) {
+ /* "2 years" */
+ gds->HaveRel++;
+ gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tSEC_UNIT) {
+ /* "now", "tomorrow" */
+ gds->HaveRel++;
+ gds->RelSeconds += gds->tokenp[0].value;
+ gds->tokenp += 1;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tMONTH_UNIT) {
+ /* "month" */
+ gds->HaveRel++;
+ gds->RelMonth += gds->tokenp[0].value;
+ gds->tokenp += 1;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Day of the week specification.
+ */
+static int
+dayphrase(struct gdstate *gds)
+{
+ if (gds->tokenp[0].token == tDAY) {
+ /* "tues", "wednesday," */
+ gds->HaveWeekDay++;
+ gds->DayOrdinal = 1;
+ gds->DayNumber = gds->tokenp[0].value;
+ gds->tokenp += 1;
+ if (gds->tokenp[0].token == ',')
+ gds->tokenp += 1;
+ return 1;
+ }
+ if (gds->tokenp[0].token == tUNUMBER
+ && gds->tokenp[1].token == tDAY) {
+ /* "second tues" "3 wed" */
+ gds->HaveWeekDay++;
+ gds->DayOrdinal = gds->tokenp[0].value;
+ gds->DayNumber = gds->tokenp[1].value;
+ gds->tokenp += 2;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Try to match a phrase using one of the above functions.
+ * This layer also deals with a couple of generic issues.
+ */
+static int
+phrase(struct gdstate *gds)
+{
+ if (timephrase(gds))
+ return 1;
+ if (zonephrase(gds))
+ return 1;
+ if (datephrase(gds))
+ return 1;
+ if (dayphrase(gds))
+ return 1;
+ if (relunitphrase(gds)) {
+ if (gds->tokenp[0].token == tAGO) {
+ gds->RelSeconds = -gds->RelSeconds;
+ gds->RelMonth = -gds->RelMonth;
+ gds->tokenp += 1;
+ }
+ return 1;
+ }
+
+ /* Bare numbers sometimes have meaning. */
+ if (gds->tokenp[0].token == tUNUMBER) {
+ if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {
+ gds->HaveYear++;
+ gds->Year = gds->tokenp[0].value;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if(gds->tokenp[0].value > 10000) {
+ /* "20040301" */
+ gds->HaveYear++;
+ gds->HaveMonth++;
+ gds->HaveDay++;
+ gds->Day= (gds->tokenp[0].value)%100;
+ gds->Month= (gds->tokenp[0].value/100)%100;
+ gds->Year = gds->tokenp[0].value/10000;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if (gds->tokenp[0].value < 24) {
+ gds->HaveTime++;
+ gds->Hour = gds->tokenp[0].value;
+ gds->Minutes = 0;
+ gds->Seconds = 0;
+ gds->tokenp += 1;
+ return 1;
+ }
+
+ if ((gds->tokenp[0].value / 100 < 24)
+ && (gds->tokenp[0].value % 100 < 60)) {
+ /* "513" is same as "5:13" */
+ gds->Hour = gds->tokenp[0].value / 100;
+ gds->Minutes = gds->tokenp[0].value % 100;
+ gds->Seconds = 0;
+ gds->tokenp += 1;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * A dictionary of time words.
+ */
+static struct LEXICON {
+ size_t abbrev;
+ const char *name;
+ int type;
+ time_t value;
+} const TimeWords[] = {
+ /* am/pm */
+ { 0, "am", tAMPM, tAM },
+ { 0, "pm", tAMPM, tPM },
+
+ /* Month names. */
+ { 3, "january", tMONTH, 1 },
+ { 3, "february", tMONTH, 2 },
+ { 3, "march", tMONTH, 3 },
+ { 3, "april", tMONTH, 4 },
+ { 3, "may", tMONTH, 5 },
+ { 3, "june", tMONTH, 6 },
+ { 3, "july", tMONTH, 7 },
+ { 3, "august", tMONTH, 8 },
+ { 3, "september", tMONTH, 9 },
+ { 3, "october", tMONTH, 10 },
+ { 3, "november", tMONTH, 11 },
+ { 3, "december", tMONTH, 12 },
+
+ /* Days of the week. */
+ { 2, "sunday", tDAY, 0 },
+ { 3, "monday", tDAY, 1 },
+ { 2, "tuesday", tDAY, 2 },
+ { 3, "wednesday", tDAY, 3 },
+ { 2, "thursday", tDAY, 4 },
+ { 2, "friday", tDAY, 5 },
+ { 2, "saturday", tDAY, 6 },
+
+ /* Timezones: Offsets are in seconds. */
+ { 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */
+ { 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */
+ { 0, "utc", tZONE, 0*HOUR },
+ { 0, "wet", tZONE, 0*HOUR }, /* Western European */
+ { 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */
+ { 0, "wat", tZONE, 1*HOUR }, /* West Africa */
+ { 0, "at", tZONE, 2*HOUR }, /* Azores */
+ /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */
+ /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/
+ { 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */
+ { 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */
+ { 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */
+ { 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */
+ { 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */
+ { 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */
+ { 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */
+ { 0, "cst", tZONE, 6*HOUR }, /* Central Standard */
+ { 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */
+ { 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */
+ { 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */
+ { 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */
+ { 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */
+ { 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */
+ { 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */
+ { 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */
+ { 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */
+ { 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */
+ { 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */
+ { 0, "nt", tZONE, 11*HOUR }, /* Nome */
+ { 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */
+ { 0, "cet", tZONE, -1*HOUR }, /* Central European */
+ { 0, "met", tZONE, -1*HOUR }, /* Middle European */
+ { 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */
+ { 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */
+ { 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */
+ { 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */
+ { 0, "fwt", tZONE, -1*HOUR }, /* French Winter */
+ { 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */
+ { 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */
+ { 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */
+ { 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */
+ { 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */
+ { 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */
+ { 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */
+ { 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */
+ /* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */
+ /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */
+ { 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */
+ { 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */
+ { 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/
+ { 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */
+ { 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */
+ { 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */
+ { 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */
+ { 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */
+ { 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */
+ { 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */
+ { 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */
+ { 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */
+ { 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */
+ { 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */
+
+ { 0, "dst", tDST, 0 },
+
+ /* Time units. */
+ { 4, "years", tMONTH_UNIT, 12 },
+ { 5, "months", tMONTH_UNIT, 1 },
+ { 9, "fortnights", tSEC_UNIT, 14 * DAY },
+ { 4, "weeks", tSEC_UNIT, 7 * DAY },
+ { 3, "days", tSEC_UNIT, DAY },
+ { 4, "hours", tSEC_UNIT, HOUR },
+ { 3, "minutes", tSEC_UNIT, MINUTE },
+ { 3, "seconds", tSEC_UNIT, 1 },
+
+ /* Relative-time words. */
+ { 0, "tomorrow", tSEC_UNIT, DAY },
+ { 0, "yesterday", tSEC_UNIT, -DAY },
+ { 0, "today", tSEC_UNIT, 0 },
+ { 0, "now", tSEC_UNIT, 0 },
+ { 0, "last", tUNUMBER, -1 },
+ { 0, "this", tSEC_UNIT, 0 },
+ { 0, "next", tUNUMBER, 2 },
+ { 0, "first", tUNUMBER, 1 },
+ { 0, "1st", tUNUMBER, 1 },
+/* { 0, "second", tUNUMBER, 2 }, */
+ { 0, "2nd", tUNUMBER, 2 },
+ { 0, "third", tUNUMBER, 3 },
+ { 0, "3rd", tUNUMBER, 3 },
+ { 0, "fourth", tUNUMBER, 4 },
+ { 0, "4th", tUNUMBER, 4 },
+ { 0, "fifth", tUNUMBER, 5 },
+ { 0, "5th", tUNUMBER, 5 },
+ { 0, "sixth", tUNUMBER, 6 },
+ { 0, "seventh", tUNUMBER, 7 },
+ { 0, "eighth", tUNUMBER, 8 },
+ { 0, "ninth", tUNUMBER, 9 },
+ { 0, "tenth", tUNUMBER, 10 },
+ { 0, "eleventh", tUNUMBER, 11 },
+ { 0, "twelfth", tUNUMBER, 12 },
+ { 0, "ago", tAGO, 1 },
+
+ /* Military timezones. */
+ { 0, "a", tZONE, 1*HOUR },
+ { 0, "b", tZONE, 2*HOUR },
+ { 0, "c", tZONE, 3*HOUR },
+ { 0, "d", tZONE, 4*HOUR },
+ { 0, "e", tZONE, 5*HOUR },
+ { 0, "f", tZONE, 6*HOUR },
+ { 0, "g", tZONE, 7*HOUR },
+ { 0, "h", tZONE, 8*HOUR },
+ { 0, "i", tZONE, 9*HOUR },
+ { 0, "k", tZONE, 10*HOUR },
+ { 0, "l", tZONE, 11*HOUR },
+ { 0, "m", tZONE, 12*HOUR },
+ { 0, "n", tZONE, -1*HOUR },
+ { 0, "o", tZONE, -2*HOUR },
+ { 0, "p", tZONE, -3*HOUR },
+ { 0, "q", tZONE, -4*HOUR },
+ { 0, "r", tZONE, -5*HOUR },
+ { 0, "s", tZONE, -6*HOUR },
+ { 0, "t", tZONE, -7*HOUR },
+ { 0, "u", tZONE, -8*HOUR },
+ { 0, "v", tZONE, -9*HOUR },
+ { 0, "w", tZONE, -10*HOUR },
+ { 0, "x", tZONE, -11*HOUR },
+ { 0, "y", tZONE, -12*HOUR },
+ { 0, "z", tZONE, 0*HOUR },
+
+ /* End of table. */
+ { 0, NULL, 0, 0 }
+};
+
+/*
+ * Year is either:
+ * = A number from 0 to 99, which means a year from 1970 to 2069, or
+ * = The actual year (>=100).
+ */
+static time_t
+Convert(time_t Month, time_t Day, time_t Year,
+ time_t Hours, time_t Minutes, time_t Seconds,
+ time_t Timezone, enum DSTMODE DSTmode)
+{
+ signed char DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t Julian;
+ int i;
+ struct tm *ltime;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+
+ if (Year < 69)
+ Year += 2000;
+ else if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
+ I'm too lazy to try to check for time_t overflow in another way. */
+ if (Year < EPOCH || Year >= 2038
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month]
+ || Hours < 0 || Hours > 23
+ || Minutes < 0 || Minutes > 59
+ || Seconds < 0 || Seconds > 59)
+ return -1;
+
+ Julian = Day - 1;
+ for (i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= DAY;
+ Julian += Timezone;
+ Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ ltime = localtime_r(&Julian, &tmbuf);
+#else
+ ltime = localtime(&Julian);
+#endif
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && ltime->tm_isdst))
+ Julian -= HOUR;
+ return Julian;
+}
+
+static time_t
+DSTcorrect(time_t Start, time_t Future)
+{
+ time_t StartDay;
+ time_t FutureDay;
+ struct tm *ltime;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ ltime = localtime_r(&Start, &tmbuf);
+#else
+ ltime = localtime(&Start);
+#endif
+ StartDay = (ltime->tm_hour + 1) % 24;
+#if defined(HAVE_LOCALTIME_S)
+ ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ ltime = localtime_r(&Future, &tmbuf);
+#else
+ ltime = localtime(&Future);
+#endif
+ FutureDay = (ltime->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * HOUR;
+}
+
+
+static time_t
+RelativeDate(time_t Start, time_t zone, int dstmode,
+ time_t DayOrdinal, time_t DayNumber)
+{
+ struct tm *tm;
+ time_t t, now;
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
+ struct tm tmbuf;
+#endif
+
+ t = Start - zone;
+#if defined(HAVE_GMTIME_S)
+ tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_GMTIME_R)
+ tm = gmtime_r(&t, &tmbuf);
+#else
+ tm = gmtime(&t);
+#endif
+ now = Start;
+ now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ if (dstmode == DSTmaybe)
+ return DSTcorrect(Start, now);
+ return now - Start;
+}
+
+
+static time_t
+RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+
+ if (RelMonth == 0)
+ return 0;
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ tm = localtime_r(&Start, &tmbuf);
+#else
+ tm = localtime(&Start);
+#endif
+ Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ Timezone, DSTmaybe));
+}
+
+/*
+ * Tokenizer.
+ */
+static int
+nexttoken(const char **in, time_t *value)
+{
+ char c;
+ char buff[64];
+
+ for ( ; ; ) {
+ while (isspace((unsigned char)**in))
+ ++*in;
+
+ /* Skip parenthesized comments. */
+ if (**in == '(') {
+ int Count = 0;
+ do {
+ c = *(*in)++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ continue;
+ }
+
+ /* Try the next token in the word table first. */
+ /* This allows us to match "2nd", for example. */
+ {
+ const char *src = *in;
+ const struct LEXICON *tp;
+ unsigned i = 0;
+
+ /* Force to lowercase and strip '.' characters. */
+ while (*src != '\0'
+ && (isalnum((unsigned char)*src) || *src == '.')
+ && i < sizeof(buff)-1) {
+ if (*src != '.') {
+ if (isupper((unsigned char)*src))
+ buff[i++] = tolower((unsigned char)*src);
+ else
+ buff[i++] = *src;
+ }
+ src++;
+ }
+ buff[i] = '\0';
+
+ /*
+ * Find the first match. If the word can be
+ * abbreviated, make sure we match at least
+ * the minimum abbreviation.
+ */
+ for (tp = TimeWords; tp->name; tp++) {
+ size_t abbrev = tp->abbrev;
+ if (abbrev == 0)
+ abbrev = strlen(tp->name);
+ if (strlen(buff) >= abbrev
+ && strncmp(tp->name, buff, strlen(buff))
+ == 0) {
+ /* Skip over token. */
+ *in = src;
+ /* Return the match. */
+ *value = tp->value;
+ return tp->type;
+ }
+ }
+ }
+
+ /*
+ * Not in the word table, maybe it's a number. Note:
+ * Because '-' and '+' have other special meanings, I
+ * don't deal with signed numbers here.
+ */
+ if (isdigit((unsigned char)(c = **in))) {
+ for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); )
+ *value = 10 * *value + c - '0';
+ (*in)--;
+ return (tUNUMBER);
+ }
+
+ return *(*in)++;
+ }
+}
+
+#define TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds. */
+static long
+difftm (struct tm *a, struct tm *b)
+{
+ int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+ int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+ int days = (
+ /* difference in day of year */
+ a->tm_yday - b->tm_yday
+ /* + intervening leap days */
+ + ((ay >> 2) - (by >> 2))
+ - (ay/100 - by/100)
+ + ((ay/100 >> 2) - (by/100 >> 2))
+ /* + difference in years * 365 */
+ + (long)(ay-by) * 365
+ );
+ return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR
+ + (a->tm_min - b->tm_min) * MINUTE
+ + (a->tm_sec - b->tm_sec));
+}
+
+/*
+ *
+ * The public function.
+ *
+ * TODO: tokens[] array should be dynamically sized.
+ */
+time_t
+__archive_get_date(time_t now, const char *p)
+{
+ struct token tokens[256];
+ struct gdstate _gds;
+ struct token *lasttoken;
+ struct gdstate *gds;
+ struct tm local, *tm;
+ struct tm gmt, *gmt_ptr;
+ time_t Start;
+ time_t tod;
+ long tzone;
+
+ /* Clear out the parsed token array. */
+ memset(tokens, 0, sizeof(tokens));
+ /* Initialize the parser state. */
+ memset(&_gds, 0, sizeof(_gds));
+ gds = &_gds;
+
+ /* Look up the current time. */
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&local, &now) ? NULL : &local;
+#elif defined(HAVE_LOCALTIME_R)
+ tm = localtime_r(&now, &local);
+#else
+ memset(&local, 0, sizeof(local));
+ tm = localtime(&now);
+#endif
+ if (tm == NULL)
+ return -1;
+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S)
+ local = *tm;
+#endif
+
+ /* Look up UTC if we can and use that to determine the current
+ * timezone offset. */
+#if defined(HAVE_GMTIME_S)
+ gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
+ gmt_ptr = gmtime_r(&now, &gmt);
+#else
+ memset(&gmt, 0, sizeof(gmt));
+ gmt_ptr = gmtime(&now);
+ if (gmt_ptr != NULL) {
+ /* Copy, in case localtime and gmtime use the same buffer. */
+ gmt = *gmt_ptr;
+ }
+#endif
+ if (gmt_ptr != NULL)
+ tzone = difftm (&gmt, &local);
+ else
+ /* This system doesn't understand timezones; fake it. */
+ tzone = 0;
+ if(local.tm_isdst)
+ tzone += HOUR;
+
+ /* Tokenize the input string. */
+ lasttoken = tokens;
+ while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {
+ ++lasttoken;
+ if (lasttoken > tokens + 255)
+ return -1;
+ }
+ gds->tokenp = tokens;
+
+ /* Match phrases until we run out of input tokens. */
+ while (gds->tokenp < lasttoken) {
+ if (!phrase(gds))
+ return -1;
+ }
+
+ /* Use current local timezone if none was specified. */
+ if (!gds->HaveZone) {
+ gds->Timezone = tzone;
+ gds->DSTmode = DSTmaybe;
+ }
+
+ /* If a timezone was specified, use that for generating the default
+ * time components instead of the local timezone. */
+ if (gds->HaveZone && gmt_ptr != NULL) {
+ now -= gds->Timezone;
+#if defined(HAVE_GMTIME_S)
+ gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
+ gmt_ptr = gmtime_r(&now, &gmt);
+#else
+ gmt_ptr = gmtime(&now);
+#endif
+ if (gmt_ptr != NULL)
+ local = *gmt_ptr;
+ now += gds->Timezone;
+ }
+
+ if (!gds->HaveYear)
+ gds->Year = local.tm_year + 1900;
+ if (!gds->HaveMonth)
+ gds->Month = local.tm_mon + 1;
+ if (!gds->HaveDay)
+ gds->Day = local.tm_mday;
+ /* Note: No default for hour/min/sec; a specifier that just
+ * gives date always refers to 00:00 on that date. */
+
+ /* If we saw more than one time, timezone, weekday, year, month,
+ * or day, then give up. */
+ if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1
+ || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)
+ return -1;
+
+ /* Compute an absolute time based on whatever absolute information
+ * we collected. */
+ if (gds->HaveYear || gds->HaveMonth || gds->HaveDay
+ || gds->HaveTime || gds->HaveWeekDay) {
+ Start = Convert(gds->Month, gds->Day, gds->Year,
+ gds->Hour, gds->Minutes, gds->Seconds,
+ gds->Timezone, gds->DSTmode);
+ if (Start < 0)
+ return -1;
+ } else {
+ Start = now;
+ if (!gds->HaveRel)
+ Start -= local.tm_hour * HOUR + local.tm_min * MINUTE
+ + local.tm_sec;
+ }
+
+ /* Add the relative offset. */
+ Start += gds->RelSeconds;
+ Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);
+
+ /* Adjust for day-of-week offsets. */
+ if (gds->HaveWeekDay
+ && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {
+ tod = RelativeDate(Start, gds->Timezone,
+ gds->DSTmode, gds->DayOrdinal, gds->DayNumber);
+ Start += tod;
+ }
+
+ /* -1 is an error indicator, so return 0 instead of -1 if
+ * that's the actual time. */
+ return Start == -1 ? 0 : Start;
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+int
+main(int argc, char **argv)
+{
+ time_t d;
+ time_t now = time(NULL);
+
+ while (*++argv != NULL) {
+ (void)printf("Input: %s\n", *argv);
+ d = get_date(now, *argv);
+ if (d == -1)
+ (void)printf("Bad format - couldn't convert.\n");
+ else
+ (void)printf("Output: %s\n", ctime(&d));
+ }
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/contrib/libs/libarchive/libarchive/archive_getdate.h b/contrib/libs/libarchive/libarchive/archive_getdate.h
new file mode 100644
index 0000000000..900a8f692e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_getdate.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2003-2015 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_GETDATE_H_INCLUDED
+#define ARCHIVE_GETDATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <time.h>
+
+time_t __archive_get_date(time_t now, const char *);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_hmac.c b/contrib/libs/libarchive/libarchive/archive_hmac.c
new file mode 100644
index 0000000000..edb3bf5abd
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_hmac.c
@@ -0,0 +1,339 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+*/
+
+#include "archive_platform.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include "archive.h"
+#include "archive_hmac_private.h"
+
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_hmac.c file is expected to define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist. This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_hmac_build_hack(void) {
+ return 0;
+}
+
+
+#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ CCHmacInit(ctx, kCCHmacAlgSHA1, key, key_len);
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ CCHmacUpdate(ctx, data, data_len);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ CCHmacFinal(ctx, out);
+ *out_len = 20;
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+
+#ifndef BCRYPT_HASH_REUSABLE_FLAG
+# define BCRYPT_HASH_REUSABLE_FLAG 0x00000020
+#endif
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_HASH_HANDLE hHash;
+ DWORD hash_len;
+ PBYTE hash;
+ ULONG result;
+ NTSTATUS status;
+
+ ctx->hAlg = NULL;
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM,
+ MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);
+ if (!BCRYPT_SUCCESS(status))
+ return -1;
+ status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_len,
+ sizeof(hash_len), &result, 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hash_len);
+ if (hash == NULL) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ return -1;
+ }
+ status = BCryptCreateHash(hAlg, &hHash, NULL, 0,
+ (PUCHAR)key, (ULONG)key_len, BCRYPT_HASH_REUSABLE_FLAG);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ HeapFree(GetProcessHeap(), 0, hash);
+ return -1;
+ }
+
+ ctx->hAlg = hAlg;
+ ctx->hHash = hHash;
+ ctx->hash_len = hash_len;
+ ctx->hash = hash;
+
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)data, (ULONG)data_len, 0);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ BCryptFinishHash(ctx->hHash, ctx->hash, ctx->hash_len, 0);
+ if (ctx->hash_len == *out_len)
+ memcpy(out, ctx->hash, *out_len);
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ if (ctx->hAlg != NULL) {
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+ HeapFree(GetProcessHeap(), 0, ctx->hash);
+ ctx->hAlg = NULL;
+ }
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ const mbedtls_md_info_t *info;
+ int ret;
+
+ mbedtls_md_init(ctx);
+ info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+ if (info == NULL) {
+ mbedtls_md_free(ctx);
+ return (-1);
+ }
+ ret = mbedtls_md_setup(ctx, info, 1);
+ if (ret != 0) {
+ mbedtls_md_free(ctx);
+ return (-1);
+ }
+ ret = mbedtls_md_hmac_starts(ctx, key, key_len);
+ if (ret != 0) {
+ mbedtls_md_free(ctx);
+ return (-1);
+ }
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ mbedtls_md_hmac_update(ctx, data, data_len);
+}
+
+static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ (void)out_len; /* UNUSED */
+
+ mbedtls_md_hmac_finish(ctx, out);
+}
+
+static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ mbedtls_md_free(ctx);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ hmac_sha1_set_key(ctx, key_len, key);
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ hmac_sha1_update(ctx, data_len, data);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ hmac_sha1_digest(ctx, (unsigned)*out_len, out);
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(HAVE_LIBCRYPTO)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC *mac;
+
+ char sha1[] = "SHA1";
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_utf8_string("digest", sha1, sizeof(sha1) - 1),
+ OSSL_PARAM_END
+ };
+
+ mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+ *ctx = EVP_MAC_CTX_new(mac);
+ EVP_MAC_free(mac);
+ if (*ctx == NULL)
+ return -1;
+
+ EVP_MAC_init(*ctx, key, key_len, params);
+#else
+ *ctx = HMAC_CTX_new();
+ if (*ctx == NULL)
+ return -1;
+ HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL);
+#endif
+ return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_update(*ctx, data, data_len);
+#else
+ HMAC_Update(*ctx, data, data_len);
+#endif
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ size_t len = *out_len;
+#else
+ unsigned int len = (unsigned int)*out_len;
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_final(*ctx, out, &len, *out_len);
+#else
+ HMAC_Final(*ctx, out, &len);
+#endif
+ *out_len = len;
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_CTX_free(*ctx);
+#else
+ HMAC_CTX_free(*ctx);
+#endif
+ *ctx = NULL;
+}
+
+#else
+
+/* Stub */
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+ (void)ctx;/* UNUSED */
+ (void)key;/* UNUSED */
+ (void)key_len;/* UNUSED */
+ return -1;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+ size_t data_len)
+{
+ (void)ctx;/* UNUSED */
+ (void)data;/* UNUSED */
+ (void)data_len;/* UNUSED */
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+ (void)ctx;/* UNUSED */
+ (void)out;/* UNUSED */
+ (void)out_len;/* UNUSED */
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+ (void)ctx;/* UNUSED */
+}
+
+#endif
+
+const struct archive_hmac __archive_hmac = {
+ &__hmac_sha1_init,
+ &__hmac_sha1_update,
+ &__hmac_sha1_final,
+ &__hmac_sha1_cleanup,
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_hmac_private.h b/contrib/libs/libarchive/libarchive/archive_hmac_private.h
new file mode 100644
index 0000000000..8d13b0fd72
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_hmac_private.h
@@ -0,0 +1,119 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* All rights reserved.
+*
+* 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+*/
+
+#ifndef ARCHIVE_HMAC_PRIVATE_H_INCLUDED
+#define ARCHIVE_HMAC_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_hmac.c file is expected to define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist. This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_hmac_build_hack(void);
+
+#ifdef __APPLE__
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+# define ARCHIVE_HMAC_USE_Apple_CommonCrypto
+# endif
+#endif
+
+#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto
+#include <CommonCrypto/CommonHMAC.h>
+
+typedef CCHmacContext archive_hmac_sha1_ctx;
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#include <bcrypt.h>
+
+typedef struct {
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_HASH_HANDLE hHash;
+ DWORD hash_len;
+ PBYTE hash;
+
+} archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)
+#error #include <mbedtls/md.h>
+
+typedef mbedtls_md_context_t archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
+#error #include <nettle/hmac.h>
+
+typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBCRYPTO)
+#include <openssl/opensslv.h>
+#include <openssl/hmac.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#error #include <openssl/params.h>
+
+typedef EVP_MAC_CTX *archive_hmac_sha1_ctx;
+
+#else
+#include "archive_openssl_hmac_private.h"
+
+typedef HMAC_CTX* archive_hmac_sha1_ctx;
+#endif
+
+#else
+
+typedef int archive_hmac_sha1_ctx;
+
+#endif
+
+
+/* HMAC */
+#define archive_hmac_sha1_init(ctx, key, key_len)\
+ __archive_hmac.__hmac_sha1_init(ctx, key, key_len)
+#define archive_hmac_sha1_update(ctx, data, data_len)\
+ __archive_hmac.__hmac_sha1_update(ctx, data, data_len)
+#define archive_hmac_sha1_final(ctx, out, out_len)\
+ __archive_hmac.__hmac_sha1_final(ctx, out, out_len)
+#define archive_hmac_sha1_cleanup(ctx)\
+ __archive_hmac.__hmac_sha1_cleanup(ctx)
+
+
+struct archive_hmac {
+ /* HMAC */
+ int (*__hmac_sha1_init)(archive_hmac_sha1_ctx *, const uint8_t *,
+ size_t);
+ void (*__hmac_sha1_update)(archive_hmac_sha1_ctx *, const uint8_t *,
+ size_t);
+ void (*__hmac_sha1_final)(archive_hmac_sha1_ctx *, uint8_t *, size_t *);
+ void (*__hmac_sha1_cleanup)(archive_hmac_sha1_ctx *);
+};
+
+extern const struct archive_hmac __archive_hmac;
+#endif /* ARCHIVE_HMAC_PRIVATE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_match.c b/contrib/libs/libarchive/libarchive/archive_match.c
new file mode 100644
index 0000000000..04747b1f66
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_match.c
@@ -0,0 +1,1875 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+#include "archive_getdate.h"
+#include "archive_pathmatch.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+
+struct match {
+ struct match *next;
+ int matches;
+ struct archive_mstring pattern;
+};
+
+struct match_list {
+ struct match *first;
+ struct match **last;
+ int count;
+ int unmatched_count;
+ struct match *unmatched_next;
+ int unmatched_eof;
+};
+
+struct match_file {
+ struct archive_rb_node node;
+ struct match_file *next;
+ struct archive_mstring pathname;
+ int flag;
+ time_t mtime_sec;
+ long mtime_nsec;
+ time_t ctime_sec;
+ long ctime_nsec;
+};
+
+struct entry_list {
+ struct match_file *first;
+ struct match_file **last;
+ int count;
+};
+
+struct id_array {
+ size_t size;/* Allocated size */
+ size_t count;
+ int64_t *ids;
+};
+
+#define PATTERN_IS_SET 1
+#define TIME_IS_SET 2
+#define ID_IS_SET 4
+
+struct archive_match {
+ struct archive archive;
+
+ /* exclusion/inclusion set flag. */
+ int setflag;
+
+ /* Recursively include directory content? */
+ int recursive_include;
+
+ /*
+ * Matching filename patterns.
+ */
+ struct match_list exclusions;
+ struct match_list inclusions;
+
+ /*
+ * Matching time stamps.
+ */
+ time_t now;
+ int newer_mtime_filter;
+ time_t newer_mtime_sec;
+ long newer_mtime_nsec;
+ int newer_ctime_filter;
+ time_t newer_ctime_sec;
+ long newer_ctime_nsec;
+ int older_mtime_filter;
+ time_t older_mtime_sec;
+ long older_mtime_nsec;
+ int older_ctime_filter;
+ time_t older_ctime_sec;
+ long older_ctime_nsec;
+ /*
+ * Matching time stamps with its filename.
+ */
+ struct archive_rb_tree exclusion_tree;
+ struct entry_list exclusion_entry_list;
+
+ /*
+ * Matching file owners.
+ */
+ struct id_array inclusion_uids;
+ struct id_array inclusion_gids;
+ struct match_list inclusion_unames;
+ struct match_list inclusion_gnames;
+};
+
+static int add_pattern_from_file(struct archive_match *,
+ struct match_list *, int, const void *, int);
+static int add_entry(struct archive_match *, int,
+ struct archive_entry *);
+static int add_owner_id(struct archive_match *, struct id_array *,
+ int64_t);
+static int add_owner_name(struct archive_match *, struct match_list *,
+ int, const void *);
+static int add_pattern_mbs(struct archive_match *, struct match_list *,
+ const char *);
+static int add_pattern_wcs(struct archive_match *, struct match_list *,
+ const wchar_t *);
+static int cmp_key_mbs(const struct archive_rb_node *, const void *);
+static int cmp_key_wcs(const struct archive_rb_node *, const void *);
+static int cmp_node_mbs(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int cmp_node_wcs(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static void entry_list_add(struct entry_list *, struct match_file *);
+static void entry_list_free(struct entry_list *);
+static void entry_list_init(struct entry_list *);
+static int error_nomem(struct archive_match *);
+static void match_list_add(struct match_list *, struct match *);
+static void match_list_free(struct match_list *);
+static void match_list_init(struct match_list *);
+static int match_list_unmatched_inclusions_next(struct archive_match *,
+ struct match_list *, int, const void **);
+static int match_owner_id(struct id_array *, int64_t);
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int match_owner_name_mbs(struct archive_match *,
+ struct match_list *, const char *);
+#else
+static int match_owner_name_wcs(struct archive_match *,
+ struct match_list *, const wchar_t *);
+#endif
+static int match_path_exclusion(struct archive_match *,
+ struct match *, int, const void *);
+static int match_path_inclusion(struct archive_match *,
+ struct match *, int, const void *);
+static int owner_excluded(struct archive_match *,
+ struct archive_entry *);
+static int path_excluded(struct archive_match *, int, const void *);
+static int set_timefilter(struct archive_match *, int, time_t, long,
+ time_t, long);
+static int set_timefilter_pathname_mbs(struct archive_match *,
+ int, const char *);
+static int set_timefilter_pathname_wcs(struct archive_match *,
+ int, const wchar_t *);
+static int set_timefilter_date(struct archive_match *, int, const char *);
+static int set_timefilter_date_w(struct archive_match *, int,
+ const wchar_t *);
+static int time_excluded(struct archive_match *,
+ struct archive_entry *);
+static int validate_time_flag(struct archive *, int, const char *);
+
+#define get_date __archive_get_date
+
+static const struct archive_rb_tree_ops rb_ops_mbs = {
+ cmp_node_mbs, cmp_key_mbs
+};
+
+static const struct archive_rb_tree_ops rb_ops_wcs = {
+ cmp_node_wcs, cmp_key_wcs
+};
+
+/*
+ * The matching logic here needs to be re-thought. I started out to
+ * try to mimic gtar's matching logic, but it's not entirely
+ * consistent. In particular 'tar -t' and 'tar -x' interpret patterns
+ * on the command line as anchored, but --exclude doesn't.
+ */
+
+static int
+error_nomem(struct archive_match *a)
+{
+ archive_set_error(&(a->archive), ENOMEM, "No memory");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Create an ARCHIVE_MATCH object.
+ */
+struct archive *
+archive_match_new(void)
+{
+ struct archive_match *a;
+
+ a = (struct archive_match *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_MATCH_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->recursive_include = 1;
+ match_list_init(&(a->inclusions));
+ match_list_init(&(a->exclusions));
+ __archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);
+ entry_list_init(&(a->exclusion_entry_list));
+ match_list_init(&(a->inclusion_unames));
+ match_list_init(&(a->inclusion_gnames));
+ time(&a->now);
+ return (&(a->archive));
+}
+
+/*
+ * Free an ARCHIVE_MATCH object.
+ */
+int
+archive_match_free(struct archive *_a)
+{
+ struct archive_match *a;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");
+ a = (struct archive_match *)_a;
+ match_list_free(&(a->inclusions));
+ match_list_free(&(a->exclusions));
+ entry_list_free(&(a->exclusion_entry_list));
+ free(a->inclusion_uids.ids);
+ free(a->inclusion_gids.ids);
+ match_list_free(&(a->inclusion_unames));
+ match_list_free(&(a->inclusion_gnames));
+ free(a);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Convenience function to perform all exclusion tests.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_excluded(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_excluded_ae");
+
+ a = (struct archive_match *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ r = 0;
+ if (a->setflag & PATTERN_IS_SET) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = path_excluded(a, 0, archive_entry_pathname_w(entry));
+#else
+ r = path_excluded(a, 1, archive_entry_pathname(entry));
+#endif
+ if (r != 0)
+ return (r);
+ }
+
+ if (a->setflag & TIME_IS_SET) {
+ r = time_excluded(a, entry);
+ if (r != 0)
+ return (r);
+ }
+
+ if (a->setflag & ID_IS_SET)
+ r = owner_excluded(a, entry);
+ return (r);
+}
+
+/*
+ * Utility functions to manage exclusion/inclusion patterns
+ */
+
+int
+archive_match_exclude_pattern(struct archive *_a, const char *pattern)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");
+ a = (struct archive_match *)_a;
+
+ if (pattern == NULL || *pattern == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");
+ a = (struct archive_match *)_a;
+
+ if (pattern == NULL || *pattern == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_match_exclude_pattern_from_file(struct archive *_a,
+ const char *pathname, int nullSeparator)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");
+ a = (struct archive_match *)_a;
+
+ return add_pattern_from_file(a, &(a->exclusions), 1, pathname,
+ nullSeparator);
+}
+
+int
+archive_match_exclude_pattern_from_file_w(struct archive *_a,
+ const wchar_t *pathname, int nullSeparator)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");
+ a = (struct archive_match *)_a;
+
+ return add_pattern_from_file(a, &(a->exclusions), 0, pathname,
+ nullSeparator);
+}
+
+int
+archive_match_include_pattern(struct archive *_a, const char *pattern)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_pattern");
+ a = (struct archive_match *)_a;
+
+ if (pattern == NULL || *pattern == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");
+ a = (struct archive_match *)_a;
+
+ if (pattern == NULL || *pattern == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_match_include_pattern_from_file(struct archive *_a,
+ const char *pathname, int nullSeparator)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");
+ a = (struct archive_match *)_a;
+
+ return add_pattern_from_file(a, &(a->inclusions), 1, pathname,
+ nullSeparator);
+}
+
+int
+archive_match_include_pattern_from_file_w(struct archive *_a,
+ const wchar_t *pathname, int nullSeparator)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");
+ a = (struct archive_match *)_a;
+
+ return add_pattern_from_file(a, &(a->inclusions), 0, pathname,
+ nullSeparator);
+}
+
+/*
+ * Test functions for pathname patterns.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_path_excluded(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_path_excluded");
+
+ a = (struct archive_match *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have exclusion/inclusion pattern set at all,
+ * the entry is always not excluded. */
+ if ((a->setflag & PATTERN_IS_SET) == 0)
+ return (0);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
+#else
+ return (path_excluded(a, 1, archive_entry_pathname(entry)));
+#endif
+}
+
+/*
+ * When recursive inclusion of directory content is enabled,
+ * an inclusion pattern that matches a directory will also
+ * include everything beneath that directory. Enabled by default.
+ *
+ * For compatibility with GNU tar, exclusion patterns always
+ * match if a subset of the full patch matches (i.e., they are
+ * are not rooted at the beginning of the path) and thus there
+ * is no corresponding non-recursive exclusion mode.
+ */
+int
+archive_match_set_inclusion_recursion(struct archive *_a, int enabled)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");
+ a = (struct archive_match *)_a;
+ a->recursive_include = enabled;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Utility functions to get statistic information for inclusion patterns.
+ */
+int
+archive_match_path_unmatched_inclusions(struct archive *_a)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");
+ a = (struct archive_match *)_a;
+
+ return (a->inclusions.unmatched_count);
+}
+
+int
+archive_match_path_unmatched_inclusions_next(struct archive *_a,
+ const char **_p)
+{
+ struct archive_match *a;
+ const void *v;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");
+ a = (struct archive_match *)_a;
+
+ r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);
+ *_p = (const char *)v;
+ return (r);
+}
+
+int
+archive_match_path_unmatched_inclusions_next_w(struct archive *_a,
+ const wchar_t **_p)
+{
+ struct archive_match *a;
+ const void *v;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");
+ a = (struct archive_match *)_a;
+
+ r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);
+ *_p = (const wchar_t *)v;
+ return (r);
+}
+
+/*
+ * Add inclusion/exclusion patterns.
+ */
+static int
+add_pattern_mbs(struct archive_match *a, struct match_list *list,
+ const char *pattern)
+{
+ struct match *match;
+ size_t len;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ /* Both "foo/" and "foo" should match "foo/bar". */
+ len = strlen(pattern);
+ if (len && pattern[len - 1] == '/')
+ --len;
+ archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);
+ match_list_add(list, match);
+ a->setflag |= PATTERN_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_wcs(struct archive_match *a, struct match_list *list,
+ const wchar_t *pattern)
+{
+ struct match *match;
+ size_t len;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ /* Both "foo/" and "foo" should match "foo/bar". */
+ len = wcslen(pattern);
+ if (len && pattern[len - 1] == L'/')
+ --len;
+ archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);
+ match_list_add(list, match);
+ a->setflag |= PATTERN_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_from_file(struct archive_match *a, struct match_list *mlist,
+ int mbs, const void *pathname, int nullSeparator)
+{
+ struct archive *ar;
+ struct archive_entry *ae;
+ struct archive_string as;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+ int r;
+
+ ar = archive_read_new();
+ if (ar == NULL) {
+ archive_set_error(&(a->archive), ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ r = archive_read_support_format_raw(ar);
+ r = archive_read_support_format_empty(ar);
+ if (r != ARCHIVE_OK) {
+ archive_copy_error(&(a->archive), ar);
+ archive_read_free(ar);
+ return (r);
+ }
+ if (mbs)
+ r = archive_read_open_filename(ar, pathname, 512*20);
+ else
+ r = archive_read_open_filename_w(ar, pathname, 512*20);
+ if (r != ARCHIVE_OK) {
+ archive_copy_error(&(a->archive), ar);
+ archive_read_free(ar);
+ return (r);
+ }
+ r = archive_read_next_header(ar, &ae);
+ if (r != ARCHIVE_OK) {
+ archive_read_free(ar);
+ if (r == ARCHIVE_EOF) {
+ return (ARCHIVE_OK);
+ } else {
+ archive_copy_error(&(a->archive), ar);
+ return (r);
+ }
+ }
+
+ archive_string_init(&as);
+
+ while ((r = archive_read_data_block(ar, &buff, &size, &offset))
+ == ARCHIVE_OK) {
+ const char *b = (const char *)buff;
+
+ while (size) {
+ const char *s = (const char *)b;
+ size_t length = 0;
+ int found_separator = 0;
+
+ while (length < size) {
+ if (nullSeparator) {
+ if (*b == '\0') {
+ found_separator = 1;
+ break;
+ }
+ } else {
+ if (*b == 0x0d || *b == 0x0a) {
+ found_separator = 1;
+ break;
+ }
+ }
+ b++;
+ length++;
+ }
+ if (!found_separator) {
+ archive_strncat(&as, s, length);
+ /* Read next data block. */
+ break;
+ }
+ b++;
+ size -= length + 1;
+ archive_strncat(&as, s, length);
+
+ /* If the line is not empty, add the pattern. */
+ if (archive_strlen(&as) > 0) {
+ /* Add pattern. */
+ r = add_pattern_mbs(a, mlist, as.s);
+ if (r != ARCHIVE_OK) {
+ archive_read_free(ar);
+ archive_string_free(&as);
+ return (r);
+ }
+ archive_string_empty(&as);
+ }
+ }
+ }
+
+ /* If an error occurred, report it immediately. */
+ if (r < ARCHIVE_OK) {
+ archive_copy_error(&(a->archive), ar);
+ archive_read_free(ar);
+ archive_string_free(&as);
+ return (r);
+ }
+
+ /* If the line is not empty, add the pattern. */
+ if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {
+ /* Add pattern. */
+ r = add_pattern_mbs(a, mlist, as.s);
+ if (r != ARCHIVE_OK) {
+ archive_read_free(ar);
+ archive_string_free(&as);
+ return (r);
+ }
+ }
+ archive_read_free(ar);
+ archive_string_free(&as);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Test if pathname is excluded by inclusion/exclusion patterns.
+ */
+static int
+path_excluded(struct archive_match *a, int mbs, const void *pathname)
+{
+ struct match *match;
+ struct match *matched;
+ int r;
+
+ if (a == NULL)
+ return (0);
+
+ /* Mark off any unmatched inclusions. */
+ /* In particular, if a filename does appear in the archive and
+ * is explicitly included and excluded, then we don't report
+ * it as missing even though we don't extract it.
+ */
+ matched = NULL;
+ for (match = a->inclusions.first; match != NULL;
+ match = match->next){
+ if (match->matches == 0 &&
+ (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+ if (r < 0)
+ return (r);
+ a->inclusions.unmatched_count--;
+ match->matches++;
+ matched = match;
+ }
+ }
+
+ /* Exclusions take priority */
+ for (match = a->exclusions.first; match != NULL;
+ match = match->next){
+ r = match_path_exclusion(a, match, mbs, pathname);
+ if (r)
+ return (r);
+ }
+
+ /* It's not excluded and we found an inclusion above, so it's
+ * included. */
+ if (matched != NULL)
+ return (0);
+
+
+ /* We didn't find an unmatched inclusion, check the remaining ones. */
+ for (match = a->inclusions.first; match != NULL;
+ match = match->next){
+ /* We looked at previously-unmatched inclusions already. */
+ if (match->matches > 0 &&
+ (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+ if (r < 0)
+ return (r);
+ match->matches++;
+ return (0);
+ }
+ }
+
+ /* If there were inclusions, default is to exclude. */
+ if (a->inclusions.first != NULL)
+ return (1);
+
+ /* No explicit inclusions, default is to match. */
+ return (0);
+}
+
+/*
+ * This is a little odd, but it matches the default behavior of
+ * gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar'
+ *
+ */
+static int
+match_path_exclusion(struct archive_match *a, struct match *m,
+ int mbs, const void *pn)
+{
+ int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
+ int r;
+
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch(p, (const char *)pn, flag));
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch_w(p, (const wchar_t *)pn,
+ flag));
+ }
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ return (0);
+}
+
+/*
+ * Again, mimic gtar: inclusions are always anchored (have to match
+ * the beginning of the path) even though exclusions are not anchored.
+ */
+static int
+match_path_inclusion(struct archive_match *a, struct match *m,
+ int mbs, const void *pn)
+{
+ /* Recursive operation requires only a prefix match. */
+ int flag = a->recursive_include ?
+ PATHMATCH_NO_ANCHOR_END :
+ 0;
+ int r;
+
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch(p, (const char *)pn, flag));
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+ if (r == 0)
+ return (archive_pathmatch_w(p, (const wchar_t *)pn,
+ flag));
+ }
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ return (0);
+}
+
+static void
+match_list_init(struct match_list *list)
+{
+ list->first = NULL;
+ list->last = &(list->first);
+ list->count = 0;
+}
+
+static void
+match_list_free(struct match_list *list)
+{
+ struct match *p, *q;
+
+ for (p = list->first; p != NULL; ) {
+ q = p;
+ p = p->next;
+ archive_mstring_clean(&(q->pattern));
+ free(q);
+ }
+}
+
+static void
+match_list_add(struct match_list *list, struct match *m)
+{
+ *list->last = m;
+ list->last = &(m->next);
+ list->count++;
+ list->unmatched_count++;
+}
+
+static int
+match_list_unmatched_inclusions_next(struct archive_match *a,
+ struct match_list *list, int mbs, const void **vp)
+{
+ struct match *m;
+
+ *vp = NULL;
+ if (list->unmatched_eof) {
+ list->unmatched_eof = 0;
+ return (ARCHIVE_EOF);
+ }
+ if (list->unmatched_next == NULL) {
+ if (list->unmatched_count == 0)
+ return (ARCHIVE_EOF);
+ list->unmatched_next = list->first;
+ }
+
+ for (m = list->unmatched_next; m != NULL; m = m->next) {
+ int r;
+
+ if (m->matches)
+ continue;
+ if (mbs) {
+ const char *p;
+ r = archive_mstring_get_mbs(&(a->archive),
+ &(m->pattern), &p);
+ if (r < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p == NULL)
+ p = "";
+ *vp = p;
+ } else {
+ const wchar_t *p;
+ r = archive_mstring_get_wcs(&(a->archive),
+ &(m->pattern), &p);
+ if (r < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p == NULL)
+ p = L"";
+ *vp = p;
+ }
+ list->unmatched_next = m->next;
+ if (list->unmatched_next == NULL)
+ /* To return EOF next time. */
+ list->unmatched_eof = 1;
+ return (ARCHIVE_OK);
+ }
+ list->unmatched_next = NULL;
+ return (ARCHIVE_EOF);
+}
+
+/*
+ * Utility functions to manage inclusion timestamps.
+ */
+int
+archive_match_include_time(struct archive *_a, int flag, time_t sec,
+ long nsec)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_time");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return set_timefilter((struct archive_match *)_a, flag,
+ sec, nsec, sec, nsec);
+}
+
+int
+archive_match_include_date(struct archive *_a, int flag,
+ const char *datestr)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_date");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return set_timefilter_date((struct archive_match *)_a, flag, datestr);
+}
+
+int
+archive_match_include_date_w(struct archive *_a, int flag,
+ const wchar_t *datestr)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_date_w");
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);
+}
+
+int
+archive_match_include_file_time(struct archive *_a, int flag,
+ const char *pathname)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_file_time");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return set_timefilter_pathname_mbs((struct archive_match *)_a,
+ flag, pathname);
+}
+
+int
+archive_match_include_file_time_w(struct archive *_a, int flag,
+ const wchar_t *pathname)
+{
+ int r;
+
+ r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return set_timefilter_pathname_wcs((struct archive_match *)_a,
+ flag, pathname);
+}
+
+int
+archive_match_exclude_entry(struct archive *_a, int flag,
+ struct archive_entry *entry)
+{
+ struct archive_match *a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_time_include_entry");
+ a = (struct archive_match *)_a;
+
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+ r = validate_time_flag(_a, flag, "archive_match_exclude_entry");
+ if (r != ARCHIVE_OK)
+ return (r);
+ return (add_entry(a, flag, entry));
+}
+
+/*
+ * Test function for time stamps.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_time_excluded(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");
+
+ a = (struct archive_match *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have inclusion time set at all, the entry is always
+ * not excluded. */
+ if ((a->setflag & TIME_IS_SET) == 0)
+ return (0);
+ return (time_excluded(a, entry));
+}
+
+static int
+validate_time_flag(struct archive *_a, int flag, const char *_fn)
+{
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, _fn);
+
+ /* Check a type of time. */
+ if (flag &
+ ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {
+ archive_set_error(_a, EINVAL, "Invalid time flag");
+ return (ARCHIVE_FAILED);
+ }
+ if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {
+ archive_set_error(_a, EINVAL, "No time flag");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Check a type of comparison. */
+ if (flag &
+ ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
+ | ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {
+ archive_set_error(_a, EINVAL, "Invalid comparison flag");
+ return (ARCHIVE_FAILED);
+ }
+ if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
+ | ARCHIVE_MATCH_EQUAL)) == 0) {
+ archive_set_error(_a, EINVAL, "No comparison flag");
+ return (ARCHIVE_FAILED);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+#define JUST_EQUAL(t) (((t) & (ARCHIVE_MATCH_EQUAL |\
+ ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)
+static int
+set_timefilter(struct archive_match *a, int timetype,
+ time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)
+{
+ if (timetype & ARCHIVE_MATCH_MTIME) {
+ if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
+ a->newer_mtime_filter = timetype;
+ a->newer_mtime_sec = mtime_sec;
+ a->newer_mtime_nsec = mtime_nsec;
+ a->setflag |= TIME_IS_SET;
+ }
+ if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
+ a->older_mtime_filter = timetype;
+ a->older_mtime_sec = mtime_sec;
+ a->older_mtime_nsec = mtime_nsec;
+ a->setflag |= TIME_IS_SET;
+ }
+ }
+ if (timetype & ARCHIVE_MATCH_CTIME) {
+ if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
+ a->newer_ctime_filter = timetype;
+ a->newer_ctime_sec = ctime_sec;
+ a->newer_ctime_nsec = ctime_nsec;
+ a->setflag |= TIME_IS_SET;
+ }
+ if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
+ a->older_ctime_filter = timetype;
+ a->older_ctime_sec = ctime_sec;
+ a->older_ctime_nsec = ctime_nsec;
+ a->setflag |= TIME_IS_SET;
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+set_timefilter_date(struct archive_match *a, int timetype, const char *datestr)
+{
+ time_t t;
+
+ if (datestr == NULL || *datestr == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "date is empty");
+ return (ARCHIVE_FAILED);
+ }
+ t = get_date(a->now, datestr);
+ if (t == (time_t)-1) {
+ archive_set_error(&(a->archive), EINVAL, "invalid date string");
+ return (ARCHIVE_FAILED);
+ }
+ return set_timefilter(a, timetype, t, 0, t, 0);
+}
+
+static int
+set_timefilter_date_w(struct archive_match *a, int timetype,
+ const wchar_t *datestr)
+{
+ struct archive_string as;
+ time_t t;
+
+ if (datestr == NULL || *datestr == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "date is empty");
+ return (ARCHIVE_FAILED);
+ }
+
+ archive_string_init(&as);
+ if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {
+ archive_string_free(&as);
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ archive_set_error(&(a->archive), -1,
+ "Failed to convert WCS to MBS");
+ return (ARCHIVE_FAILED);
+ }
+ t = get_date(a->now, as.s);
+ archive_string_free(&as);
+ if (t == (time_t)-1) {
+ archive_set_error(&(a->archive), EINVAL, "invalid date string");
+ return (ARCHIVE_FAILED);
+ }
+ return set_timefilter(a, timetype, t, 0, t, 0);
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static int
+set_timefilter_find_data(struct archive_match *a, int timetype,
+ DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,
+ DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)
+{
+ ULARGE_INTEGER utc;
+ time_t ctime_sec, mtime_sec;
+ long ctime_ns, mtime_ns;
+
+ utc.HighPart = ftCreationTime_dwHighDateTime;
+ utc.LowPart = ftCreationTime_dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ ctime_sec = (time_t)(utc.QuadPart / 10000000);
+ ctime_ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ ctime_sec = 0;
+ ctime_ns = 0;
+ }
+ utc.HighPart = ftLastWriteTime_dwHighDateTime;
+ utc.LowPart = ftLastWriteTime_dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ mtime_sec = (time_t)(utc.QuadPart / 10000000);
+ mtime_ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ mtime_sec = 0;
+ mtime_ns = 0;
+ }
+ return set_timefilter(a, timetype,
+ mtime_sec, mtime_ns, ctime_sec, ctime_ns);
+}
+
+static int
+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
+ const char *path)
+{
+ /* NOTE: stat() on Windows cannot handle nano seconds. */
+ HANDLE h;
+ WIN32_FIND_DATAA d;
+
+ if (path == NULL || *path == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ h = FindFirstFileA(path, &d);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&(a->archive), errno,
+ "Failed to FindFirstFileA");
+ return (ARCHIVE_FAILED);
+ }
+ FindClose(h);
+ return set_timefilter_find_data(a, timetype,
+ d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
+ d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
+}
+
+static int
+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
+ const wchar_t *path)
+{
+ HANDLE h;
+ WIN32_FIND_DATAW d;
+
+ if (path == NULL || *path == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ h = FindFirstFileW(path, &d);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&(a->archive), errno,
+ "Failed to FindFirstFile");
+ return (ARCHIVE_FAILED);
+ }
+ FindClose(h);
+ return set_timefilter_find_data(a, timetype,
+ d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
+ d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
+}
+
+#else /* _WIN32 && !__CYGWIN__ */
+
+static int
+set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)
+{
+ struct archive_entry *ae;
+ time_t ctime_sec, mtime_sec;
+ long ctime_ns, mtime_ns;
+
+ ae = archive_entry_new();
+ if (ae == NULL)
+ return (error_nomem(a));
+ archive_entry_copy_stat(ae, st);
+ ctime_sec = archive_entry_ctime(ae);
+ ctime_ns = archive_entry_ctime_nsec(ae);
+ mtime_sec = archive_entry_mtime(ae);
+ mtime_ns = archive_entry_mtime_nsec(ae);
+ archive_entry_free(ae);
+ return set_timefilter(a, timetype, mtime_sec, mtime_ns,
+ ctime_sec, ctime_ns);
+}
+
+static int
+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
+ const char *path)
+{
+ struct stat st;
+
+ if (path == NULL || *path == '\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+ if (la_stat(path, &st) != 0) {
+ archive_set_error(&(a->archive), errno, "Failed to stat()");
+ return (ARCHIVE_FAILED);
+ }
+ return (set_timefilter_stat(a, timetype, &st));
+}
+
+static int
+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
+ const wchar_t *path)
+{
+ struct archive_string as;
+ int r;
+
+ if (path == NULL || *path == L'\0') {
+ archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Convert WCS filename to MBS filename. */
+ archive_string_init(&as);
+ if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
+ archive_string_free(&as);
+ if (errno == ENOMEM)
+ return (error_nomem(a));
+ archive_set_error(&(a->archive), -1,
+ "Failed to convert WCS to MBS");
+ return (ARCHIVE_FAILED);
+ }
+
+ r = set_timefilter_pathname_mbs(a, timetype, as.s);
+ archive_string_free(&as);
+
+ return (r);
+}
+#endif /* _WIN32 && !__CYGWIN__ */
+
+/*
+ * Call back functions for archive_rb.
+ */
+static int
+cmp_node_mbs(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
+ struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
+ const char *p1, *p2;
+
+ archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
+ archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
+ if (p1 == NULL)
+ return (1);
+ if (p2 == NULL)
+ return (-1);
+ return (strcmp(p1, p2));
+}
+
+static int
+cmp_key_mbs(const struct archive_rb_node *n, const void *key)
+{
+ struct match_file *f = (struct match_file *)(uintptr_t)n;
+ const char *p;
+
+ archive_mstring_get_mbs(NULL, &(f->pathname), &p);
+ if (p == NULL)
+ return (-1);
+ return (strcmp(p, (const char *)key));
+}
+
+static int
+cmp_node_wcs(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
+ struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
+ const wchar_t *p1, *p2;
+
+ archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
+ archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
+ if (p1 == NULL)
+ return (1);
+ if (p2 == NULL)
+ return (-1);
+ return (wcscmp(p1, p2));
+}
+
+static int
+cmp_key_wcs(const struct archive_rb_node *n, const void *key)
+{
+ struct match_file *f = (struct match_file *)(uintptr_t)n;
+ const wchar_t *p;
+
+ archive_mstring_get_wcs(NULL, &(f->pathname), &p);
+ if (p == NULL)
+ return (-1);
+ return (wcscmp(p, (const wchar_t *)key));
+}
+
+static void
+entry_list_init(struct entry_list *list)
+{
+ list->first = NULL;
+ list->last = &(list->first);
+ list->count = 0;
+}
+
+static void
+entry_list_free(struct entry_list *list)
+{
+ struct match_file *p, *q;
+
+ for (p = list->first; p != NULL; ) {
+ q = p;
+ p = p->next;
+ archive_mstring_clean(&(q->pathname));
+ free(q);
+ }
+}
+
+static void
+entry_list_add(struct entry_list *list, struct match_file *file)
+{
+ *list->last = file;
+ list->last = &(file->next);
+ list->count++;
+}
+
+static int
+add_entry(struct archive_match *a, int flag,
+ struct archive_entry *entry)
+{
+ struct match_file *f;
+ const void *pathname;
+ int r;
+
+ f = calloc(1, sizeof(*f));
+ if (f == NULL)
+ return (error_nomem(a));
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ pathname = archive_entry_pathname_w(entry);
+ if (pathname == NULL) {
+ free(f);
+ archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+ return (ARCHIVE_FAILED);
+ }
+ archive_mstring_copy_wcs(&(f->pathname), pathname);
+ a->exclusion_tree.rbt_ops = &rb_ops_wcs;
+#else
+ (void)rb_ops_wcs;
+ pathname = archive_entry_pathname(entry);
+ if (pathname == NULL) {
+ free(f);
+ archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+ return (ARCHIVE_FAILED);
+ }
+ archive_mstring_copy_mbs(&(f->pathname), pathname);
+ a->exclusion_tree.rbt_ops = &rb_ops_mbs;
+#endif
+ f->flag = flag;
+ f->mtime_sec = archive_entry_mtime(entry);
+ f->mtime_nsec = archive_entry_mtime_nsec(entry);
+ f->ctime_sec = archive_entry_ctime(entry);
+ f->ctime_nsec = archive_entry_ctime_nsec(entry);
+ r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));
+ if (!r) {
+ struct match_file *f2;
+
+ /* Get the duplicated file. */
+ f2 = (struct match_file *)__archive_rb_tree_find_node(
+ &(a->exclusion_tree), pathname);
+
+ /*
+ * We always overwrite comparison condition.
+ * If you do not want to overwrite it, you should not
+ * call archive_match_exclude_entry(). We cannot know
+ * what behavior you really expect since overwriting
+ * condition might be different with the flag.
+ */
+ if (f2 != NULL) {
+ f2->flag = f->flag;
+ f2->mtime_sec = f->mtime_sec;
+ f2->mtime_nsec = f->mtime_nsec;
+ f2->ctime_sec = f->ctime_sec;
+ f2->ctime_nsec = f->ctime_nsec;
+ }
+ /* Release the duplicated file. */
+ archive_mstring_clean(&(f->pathname));
+ free(f);
+ return (ARCHIVE_OK);
+ }
+ entry_list_add(&(a->exclusion_entry_list), f);
+ a->setflag |= TIME_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Test if entry is excluded by its timestamp.
+ */
+static int
+time_excluded(struct archive_match *a, struct archive_entry *entry)
+{
+ struct match_file *f;
+ const void *pathname;
+ time_t sec;
+ long nsec;
+
+ /*
+ * If this file/dir is excluded by a time comparison, skip it.
+ */
+ if (a->newer_ctime_filter) {
+ /* If ctime is not set, use mtime instead. */
+ if (archive_entry_ctime_is_set(entry))
+ sec = archive_entry_ctime(entry);
+ else
+ sec = archive_entry_mtime(entry);
+ if (sec < a->newer_ctime_sec)
+ return (1); /* Too old, skip it. */
+ if (sec == a->newer_ctime_sec) {
+ if (archive_entry_ctime_is_set(entry))
+ nsec = archive_entry_ctime_nsec(entry);
+ else
+ nsec = archive_entry_mtime_nsec(entry);
+ if (nsec < a->newer_ctime_nsec)
+ return (1); /* Too old, skip it. */
+ if (nsec == a->newer_ctime_nsec &&
+ (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)
+ == 0)
+ return (1); /* Equal, skip it. */
+ }
+ }
+ if (a->older_ctime_filter) {
+ /* If ctime is not set, use mtime instead. */
+ if (archive_entry_ctime_is_set(entry))
+ sec = archive_entry_ctime(entry);
+ else
+ sec = archive_entry_mtime(entry);
+ if (sec > a->older_ctime_sec)
+ return (1); /* Too new, skip it. */
+ if (sec == a->older_ctime_sec) {
+ if (archive_entry_ctime_is_set(entry))
+ nsec = archive_entry_ctime_nsec(entry);
+ else
+ nsec = archive_entry_mtime_nsec(entry);
+ if (nsec > a->older_ctime_nsec)
+ return (1); /* Too new, skip it. */
+ if (nsec == a->older_ctime_nsec &&
+ (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
+ == 0)
+ return (1); /* Equal, skip it. */
+ }
+ }
+ if (a->newer_mtime_filter) {
+ sec = archive_entry_mtime(entry);
+ if (sec < a->newer_mtime_sec)
+ return (1); /* Too old, skip it. */
+ if (sec == a->newer_mtime_sec) {
+ nsec = archive_entry_mtime_nsec(entry);
+ if (nsec < a->newer_mtime_nsec)
+ return (1); /* Too old, skip it. */
+ if (nsec == a->newer_mtime_nsec &&
+ (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)
+ == 0)
+ return (1); /* Equal, skip it. */
+ }
+ }
+ if (a->older_mtime_filter) {
+ sec = archive_entry_mtime(entry);
+ if (sec > a->older_mtime_sec)
+ return (1); /* Too new, skip it. */
+ nsec = archive_entry_mtime_nsec(entry);
+ if (sec == a->older_mtime_sec) {
+ if (nsec > a->older_mtime_nsec)
+ return (1); /* Too new, skip it. */
+ if (nsec == a->older_mtime_nsec &&
+ (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)
+ == 0)
+ return (1); /* Equal, skip it. */
+ }
+ }
+
+ /* If there is no exclusion list, include the file. */
+ if (a->exclusion_entry_list.count == 0)
+ return (0);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ pathname = archive_entry_pathname_w(entry);
+ a->exclusion_tree.rbt_ops = &rb_ops_wcs;
+#else
+ (void)rb_ops_wcs;
+ pathname = archive_entry_pathname(entry);
+ a->exclusion_tree.rbt_ops = &rb_ops_mbs;
+#endif
+ if (pathname == NULL)
+ return (0);
+
+ f = (struct match_file *)__archive_rb_tree_find_node(
+ &(a->exclusion_tree), pathname);
+ /* If the file wasn't rejected, include it. */
+ if (f == NULL)
+ return (0);
+
+ if (f->flag & ARCHIVE_MATCH_CTIME) {
+ sec = archive_entry_ctime(entry);
+ if (f->ctime_sec > sec) {
+ if (f->flag & ARCHIVE_MATCH_OLDER)
+ return (1);
+ } else if (f->ctime_sec < sec) {
+ if (f->flag & ARCHIVE_MATCH_NEWER)
+ return (1);
+ } else {
+ nsec = archive_entry_ctime_nsec(entry);
+ if (f->ctime_nsec > nsec) {
+ if (f->flag & ARCHIVE_MATCH_OLDER)
+ return (1);
+ } else if (f->ctime_nsec < nsec) {
+ if (f->flag & ARCHIVE_MATCH_NEWER)
+ return (1);
+ } else if (f->flag & ARCHIVE_MATCH_EQUAL)
+ return (1);
+ }
+ }
+ if (f->flag & ARCHIVE_MATCH_MTIME) {
+ sec = archive_entry_mtime(entry);
+ if (f->mtime_sec > sec) {
+ if (f->flag & ARCHIVE_MATCH_OLDER)
+ return (1);
+ } else if (f->mtime_sec < sec) {
+ if (f->flag & ARCHIVE_MATCH_NEWER)
+ return (1);
+ } else {
+ nsec = archive_entry_mtime_nsec(entry);
+ if (f->mtime_nsec > nsec) {
+ if (f->flag & ARCHIVE_MATCH_OLDER)
+ return (1);
+ } else if (f->mtime_nsec < nsec) {
+ if (f->flag & ARCHIVE_MATCH_NEWER)
+ return (1);
+ } else if (f->flag & ARCHIVE_MATCH_EQUAL)
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Utility functions to manage inclusion owners
+ */
+
+int
+archive_match_include_uid(struct archive *_a, la_int64_t uid)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_uid");
+ a = (struct archive_match *)_a;
+ return (add_owner_id(a, &(a->inclusion_uids), uid));
+}
+
+int
+archive_match_include_gid(struct archive *_a, la_int64_t gid)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_gid");
+ a = (struct archive_match *)_a;
+ return (add_owner_id(a, &(a->inclusion_gids), gid));
+}
+
+int
+archive_match_include_uname(struct archive *_a, const char *uname)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_uname");
+ a = (struct archive_match *)_a;
+ return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
+}
+
+int
+archive_match_include_uname_w(struct archive *_a, const wchar_t *uname)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_uname_w");
+ a = (struct archive_match *)_a;
+ return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
+}
+
+int
+archive_match_include_gname(struct archive *_a, const char *gname)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_gname");
+ a = (struct archive_match *)_a;
+ return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
+}
+
+int
+archive_match_include_gname_w(struct archive *_a, const wchar_t *gname)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_include_gname_w");
+ a = (struct archive_match *)_a;
+ return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
+}
+
+/*
+ * Test function for owner(uid, gid, uname, gname).
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_owner_excluded(struct archive *_a,
+ struct archive_entry *entry)
+{
+ struct archive_match *a;
+
+ archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");
+
+ a = (struct archive_match *)_a;
+ if (entry == NULL) {
+ archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* If we don't have inclusion id set at all, the entry is always
+ * not excluded. */
+ if ((a->setflag & ID_IS_SET) == 0)
+ return (0);
+ return (owner_excluded(a, entry));
+}
+
+static int
+add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)
+{
+ unsigned i;
+
+ if (ids->count + 1 >= ids->size) {
+ void *p;
+
+ if (ids->size == 0)
+ ids->size = 8;
+ else
+ ids->size *= 2;
+ p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
+ if (p == NULL)
+ return (error_nomem(a));
+ ids->ids = (int64_t *)p;
+ }
+
+ /* Find an insert point. */
+ for (i = 0; i < ids->count; i++) {
+ if (ids->ids[i] >= id)
+ break;
+ }
+
+ /* Add owner id. */
+ if (i == ids->count)
+ ids->ids[ids->count++] = id;
+ else if (ids->ids[i] != id) {
+ memmove(&(ids->ids[i+1]), &(ids->ids[i]),
+ (ids->count - i) * sizeof(ids->ids[0]));
+ ids->ids[i] = id;
+ ids->count++;
+ }
+ a->setflag |= ID_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+static int
+match_owner_id(struct id_array *ids, int64_t id)
+{
+ unsigned b, m, t;
+
+ t = 0;
+ b = (unsigned)ids->count;
+ while (t < b) {
+ m = (t + b)>>1;
+ if (ids->ids[m] == id)
+ return (1);
+ if (ids->ids[m] < id)
+ t = m + 1;
+ else
+ b = m;
+ }
+ return (0);
+}
+
+static int
+add_owner_name(struct archive_match *a, struct match_list *list,
+ int mbs, const void *name)
+{
+ struct match *match;
+
+ match = calloc(1, sizeof(*match));
+ if (match == NULL)
+ return (error_nomem(a));
+ if (mbs)
+ archive_mstring_copy_mbs(&(match->pattern), name);
+ else
+ archive_mstring_copy_wcs(&(match->pattern), name);
+ match_list_add(list, match);
+ a->setflag |= ID_IS_SET;
+ return (ARCHIVE_OK);
+}
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int
+match_owner_name_mbs(struct archive_match *a, struct match_list *list,
+ const char *name)
+{
+ struct match *m;
+ const char *p;
+
+ if (name == NULL || *name == '\0')
+ return (0);
+ for (m = list->first; m; m = m->next) {
+ if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
+ < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p != NULL && strcmp(p, name) == 0) {
+ m->matches++;
+ return (1);
+ }
+ }
+ return (0);
+}
+#else
+static int
+match_owner_name_wcs(struct archive_match *a, struct match_list *list,
+ const wchar_t *name)
+{
+ struct match *m;
+ const wchar_t *p;
+
+ if (name == NULL || *name == L'\0')
+ return (0);
+ for (m = list->first; m; m = m->next) {
+ if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
+ < 0 && errno == ENOMEM)
+ return (error_nomem(a));
+ if (p != NULL && wcscmp(p, name) == 0) {
+ m->matches++;
+ return (1);
+ }
+ }
+ return (0);
+}
+#endif
+
+/*
+ * Test if entry is excluded by uid, gid, uname or gname.
+ */
+static int
+owner_excluded(struct archive_match *a, struct archive_entry *entry)
+{
+ int r;
+
+ if (a->inclusion_uids.count) {
+ if (!match_owner_id(&(a->inclusion_uids),
+ archive_entry_uid(entry)))
+ return (1);
+ }
+
+ if (a->inclusion_gids.count) {
+ if (!match_owner_id(&(a->inclusion_gids),
+ archive_entry_gid(entry)))
+ return (1);
+ }
+
+ if (a->inclusion_unames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = match_owner_name_wcs(a, &(a->inclusion_unames),
+ archive_entry_uname_w(entry));
+#else
+ r = match_owner_name_mbs(a, &(a->inclusion_unames),
+ archive_entry_uname(entry));
+#endif
+ if (!r)
+ return (1);
+ else if (r < 0)
+ return (r);
+ }
+
+ if (a->inclusion_gnames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ r = match_owner_name_wcs(a, &(a->inclusion_gnames),
+ archive_entry_gname_w(entry));
+#else
+ r = match_owner_name_mbs(a, &(a->inclusion_gnames),
+ archive_entry_gname(entry));
+#endif
+ if (!r)
+ return (1);
+ else if (r < 0)
+ return (r);
+ }
+ return (0);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_openssl_evp_private.h b/contrib/libs/libarchive/libarchive/archive_openssl_evp_private.h
new file mode 100644
index 0000000000..8ac4772808
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_openssl_evp_private.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+static inline EVP_MD_CTX *EVP_MD_CTX_new(void)
+{
+ EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX));
+ return ctx;
+}
+
+static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
+{
+ EVP_MD_CTX_cleanup(ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+}
+#endif
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_openssl_hmac_private.h b/contrib/libs/libarchive/libarchive/archive_openssl_hmac_private.h
new file mode 100644
index 0000000000..25c8dda654
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_openssl_hmac_private.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#ifndef ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <openssl/hmac.h>
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+static inline HMAC_CTX *HMAC_CTX_new(void)
+{
+ HMAC_CTX *ctx = (HMAC_CTX *)calloc(1, sizeof(HMAC_CTX));
+ return ctx;
+}
+
+static inline void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+ HMAC_CTX_cleanup(ctx);
+ memset(ctx, 0, sizeof(*ctx));
+ free(ctx);
+}
+#endif
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_options.c b/contrib/libs/libarchive/libarchive/archive_options.c
new file mode 100644
index 0000000000..6496025a5f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_options.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive_options_private.h"
+
+static const char *
+parse_option(const char **str,
+ const char **mod, const char **opt, const char **val);
+
+int
+_archive_set_option(struct archive *a,
+ const char *m, const char *o, const char *v,
+ int magic, const char *fn, option_handler use_option)
+{
+ const char *mp, *op, *vp;
+ int r;
+
+ archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
+
+ mp = (m != NULL && m[0] != '\0') ? m : NULL;
+ op = (o != NULL && o[0] != '\0') ? o : NULL;
+ vp = (v != NULL && v[0] != '\0') ? v : NULL;
+
+ if (op == NULL && vp == NULL)
+ return (ARCHIVE_OK);
+ if (op == NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC, "Empty option");
+ return (ARCHIVE_FAILED);
+ }
+
+ r = use_option(a, mp, op, vp);
+ if (r == ARCHIVE_WARN - 1) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unknown module name: `%s'", mp);
+ return (ARCHIVE_FAILED);
+ }
+ if (r == ARCHIVE_WARN) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Undefined option: `%s%s%s%s%s%s'",
+ vp?"":"!", mp?mp:"", mp?":":"", op, vp?"=":"", vp?vp:"");
+ return (ARCHIVE_FAILED);
+ }
+ return (r);
+}
+
+int
+_archive_set_either_option(struct archive *a, const char *m, const char *o, const char *v,
+ option_handler use_format_option, option_handler use_filter_option)
+{
+ int r1, r2;
+
+ if (o == NULL && v == NULL)
+ return (ARCHIVE_OK);
+ if (o == NULL)
+ return (ARCHIVE_FAILED);
+
+ r1 = use_format_option(a, m, o, v);
+ if (r1 == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+
+ r2 = use_filter_option(a, m, o, v);
+ if (r2 == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+
+ if (r2 == ARCHIVE_WARN - 1)
+ return r1;
+ return r1 > r2 ? r1 : r2;
+}
+
+int
+_archive_set_options(struct archive *a, const char *options,
+ int magic, const char *fn, option_handler use_option)
+{
+ int allok = 1, anyok = 0, ignore_mod_err = 0, r;
+ char *data;
+ const char *s, *mod, *opt, *val;
+
+ archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
+
+ if (options == NULL || options[0] == '\0')
+ return ARCHIVE_OK;
+
+ if ((data = strdup(options)) == NULL) {
+ archive_set_error(a,
+ ENOMEM, "Out of memory adding file to list");
+ return (ARCHIVE_FATAL);
+ }
+ s = (const char *)data;
+
+ do {
+ mod = opt = val = NULL;
+
+ parse_option(&s, &mod, &opt, &val);
+ if (mod == NULL && opt != NULL &&
+ strcmp("__ignore_wrong_module_name__", opt) == 0) {
+ /* Ignore module name error */
+ if (val != NULL) {
+ ignore_mod_err = 1;
+ anyok = 1;
+ }
+ continue;
+ }
+
+ r = use_option(a, mod, opt, val);
+ if (r == ARCHIVE_FATAL) {
+ free(data);
+ return (ARCHIVE_FATAL);
+ }
+ if (r == ARCHIVE_FAILED && mod != NULL) {
+ free(data);
+ return (ARCHIVE_FAILED);
+ }
+ if (r == ARCHIVE_WARN - 1) {
+ if (ignore_mod_err)
+ continue;
+ /* The module name is wrong. */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unknown module name: `%s'", mod);
+ free(data);
+ return (ARCHIVE_FAILED);
+ }
+ if (r == ARCHIVE_WARN) {
+ /* The option name is wrong. No-one used this. */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Undefined option: `%s%s%s'",
+ mod?mod:"", mod?":":"", opt);
+ free(data);
+ return (ARCHIVE_FAILED);
+ }
+ if (r == ARCHIVE_OK)
+ anyok = 1;
+ else
+ allok = 0;
+ } while (s != NULL);
+
+ free(data);
+ return allok ? ARCHIVE_OK : anyok ? ARCHIVE_WARN : ARCHIVE_FAILED;
+}
+
+static const char *
+parse_option(const char **s, const char **m, const char **o, const char **v)
+{
+ const char *end, *mod, *opt, *val;
+ char *p;
+
+ end = NULL;
+ mod = NULL;
+ opt = *s;
+ val = "1";
+
+ p = strchr(opt, ',');
+
+ if (p != NULL) {
+ *p = '\0';
+ end = ((const char *)p) + 1;
+ }
+
+ if (0 == strlen(opt)) {
+ *s = end;
+ *m = NULL;
+ *o = NULL;
+ *v = NULL;
+ return end;
+ }
+
+ p = strchr(opt, ':');
+ if (p != NULL) {
+ *p = '\0';
+ mod = opt;
+ opt = ++p;
+ }
+
+ p = strchr(opt, '=');
+ if (p != NULL) {
+ *p = '\0';
+ val = ++p;
+ } else if (opt[0] == '!') {
+ ++opt;
+ val = NULL;
+ }
+
+ *s = end;
+ *m = mod;
+ *o = opt;
+ *v = val;
+
+ return end;
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_options_private.h b/contrib/libs/libarchive/libarchive/archive_options_private.h
new file mode 100644
index 0000000000..9a7f8080d2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_options_private.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#ifndef ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_private.h"
+
+typedef int (*option_handler)(struct archive *a,
+ const char *mod, const char *opt, const char *val);
+
+int
+_archive_set_option(struct archive *a,
+ const char *mod, const char *opt, const char *val,
+ int magic, const char *fn, option_handler use_option);
+
+int
+_archive_set_options(struct archive *a, const char *options,
+ int magic, const char *fn, option_handler use_option);
+
+int
+_archive_set_either_option(struct archive *a,
+ const char *m, const char *o, const char *v,
+ option_handler use_format_option, option_handler use_filter_option);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_pack_dev.c b/contrib/libs/libarchive/libarchive/archive_pack_dev.c
new file mode 100644
index 0000000000..23dc934bc7
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_pack_dev.c
@@ -0,0 +1,337 @@
+/* $NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/* Originally from NetBSD's mknod(8) source. */
+
+#include "archive_platform.h"
+
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+#if !defined(lint)
+__RCSID("$NetBSD$");
+#endif /* not lint */
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#define HAVE_MAJOR
+#elif MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#define HAVE_MAJOR
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive_pack_dev.h"
+
+static pack_t pack_netbsd;
+static pack_t pack_freebsd;
+static pack_t pack_8_8;
+static pack_t pack_12_20;
+static pack_t pack_14_18;
+static pack_t pack_8_24;
+static pack_t pack_bsdos;
+static int __LA_LIBC_CC compare_format(const void *, const void *);
+
+static const char iMajorError[] = "invalid major number";
+static const char iMinorError[] = "invalid minor number";
+static const char tooManyFields[] = "too many fields for format";
+
+/* This is blatantly stolen from libarchive/archive_entry.c,
+ * in an attempt to get this to play nice on MinGW... */
+#if !defined(HAVE_MAJOR) && !defined(major)
+/* Replacement for major/minor/makedev. */
+#define major(x) ((int)(0x00ff & ((x) >> 8)))
+#define minor(x) ((int)(0xffff00ff & (x)))
+#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))
+#endif
+
+/* Play games to come up with a suitable makedev() definition. */
+#ifdef __QNXNTO__
+/* QNX. <sigh> */
+#error #include <sys/netmgr.h>
+#define apd_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
+#elif defined makedev
+/* There's a "makedev" macro. */
+#define apd_makedev(maj, min) makedev((maj), (min))
+#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))
+/* Windows. <sigh> */
+#define apd_makedev(maj, min) mkdev((maj), (min))
+#else
+/* There's a "makedev" function. */
+#define apd_makedev(maj, min) makedev((maj), (min))
+#endif
+
+/* exported */
+dev_t
+pack_native(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = apd_makedev(numbers[0], numbers[1]);
+ if ((unsigned long)major(dev) != numbers[0])
+ *error = iMajorError;
+ else if ((unsigned long)minor(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+static dev_t
+pack_netbsd(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_netbsd(numbers[0], numbers[1]);
+ if ((unsigned long)major_netbsd(dev) != numbers[0])
+ *error = iMajorError;
+ else if ((unsigned long)minor_netbsd(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8))
+#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0))
+#define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \
+ (((y) << 0) & 0xffff00ff)))
+
+static dev_t
+pack_freebsd(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_freebsd(numbers[0], numbers[1]);
+ if ((unsigned long)major_freebsd(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_freebsd(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8))
+#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0))
+#define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \
+ (((y) << 0) & 0x000000ff)))
+
+static dev_t
+pack_8_8(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_8_8(numbers[0], numbers[1]);
+ if ((unsigned long)major_8_8(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_8_8(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20))
+#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0))
+#define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \
+ (((y) << 0) & 0x000fffff)))
+
+static dev_t
+pack_12_20(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_12_20(numbers[0], numbers[1]);
+ if ((unsigned long)major_12_20(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_12_20(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18))
+#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0))
+#define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \
+ (((y) << 0) & 0x0003ffff)))
+
+static dev_t
+pack_14_18(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_14_18(numbers[0], numbers[1]);
+ if ((unsigned long)major_14_18(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_14_18(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24))
+#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0))
+#define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \
+ (((y) << 0) & 0x00ffffff)))
+
+static dev_t
+pack_8_24(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_8_24(numbers[0], numbers[1]);
+ if ((unsigned long)major_8_24(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_8_24(dev) != numbers[1])
+ *error = iMinorError;
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20))
+#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8))
+#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0))
+#define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \
+ (((y) << 8) & 0x000fff00) | \
+ (((z) << 0) & 0x000000ff)))
+
+static dev_t
+pack_bsdos(int n, unsigned long numbers[], const char **error)
+{
+ dev_t dev = 0;
+
+ if (n == 2) {
+ dev = makedev_12_20(numbers[0], numbers[1]);
+ if ((unsigned long)major_12_20(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)minor_12_20(dev) != numbers[1])
+ *error = iMinorError;
+ } else if (n == 3) {
+ dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]);
+ if ((unsigned long)major_12_12_8(dev) != numbers[0])
+ *error = iMajorError;
+ if ((unsigned long)unit_12_12_8(dev) != numbers[1])
+ *error = "invalid unit number";
+ if ((unsigned long)subunit_12_12_8(dev) != numbers[2])
+ *error = "invalid subunit number";
+ } else
+ *error = tooManyFields;
+ return (dev);
+}
+
+
+ /* list of formats and pack functions */
+ /* this list must be sorted lexically */
+static const struct format {
+ const char *name;
+ pack_t *pack;
+} formats[] = {
+ {"386bsd", pack_8_8},
+ {"4bsd", pack_8_8},
+ {"bsdos", pack_bsdos},
+ {"freebsd", pack_freebsd},
+ {"hpux", pack_8_24},
+ {"isc", pack_8_8},
+ {"linux", pack_8_8},
+ {"native", pack_native},
+ {"netbsd", pack_netbsd},
+ {"osf1", pack_12_20},
+ {"sco", pack_8_8},
+ {"solaris", pack_14_18},
+ {"sunos", pack_8_8},
+ {"svr3", pack_8_8},
+ {"svr4", pack_14_18},
+ {"ultrix", pack_8_8},
+};
+
+static int
+__LA_LIBC_CC
+compare_format(const void *key, const void *element)
+{
+ const char *name;
+ const struct format *format;
+
+ name = key;
+ format = element;
+
+ return (strcmp(name, format->name));
+}
+
+
+pack_t *
+pack_find(const char *name)
+{
+ struct format *format;
+
+ format = bsearch(name, formats,
+ sizeof(formats)/sizeof(formats[0]),
+ sizeof(formats[0]), compare_format);
+ if (format == 0)
+ return (NULL);
+ return (format->pack);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_pack_dev.h b/contrib/libs/libarchive/libarchive/archive_pack_dev.h
new file mode 100644
index 0000000000..eaf23e3883
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_pack_dev.h
@@ -0,0 +1,49 @@
+/* $NetBSD: pack_dev.h,v 1.8 2013/06/14 16:28:20 tsutsui Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/* Originally from NetBSD's mknod(8) source. */
+
+#ifndef ARCHIVE_PACK_DEV_H
+#define ARCHIVE_PACK_DEV_H
+
+typedef dev_t pack_t(int, unsigned long [], const char **);
+
+pack_t *pack_find(const char *);
+pack_t pack_native;
+
+#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8)))
+#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \
+ (((x) & 0x000000ff) >> 0)))
+#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \
+ (((y) << 12) & 0xfff00000) | \
+ (((y) << 0) & 0x000000ff)))
+
+#endif /* ARCHIVE_PACK_DEV_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_pathmatch.c b/contrib/libs/libarchive/libarchive/archive_pathmatch.c
new file mode 100644
index 0000000000..0867a268ee
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_pathmatch.c
@@ -0,0 +1,463 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive_pathmatch.h"
+
+/*
+ * Check whether a character 'c' is matched by a list specification [...]:
+ * * Leading '!' or '^' negates the class.
+ * * <char>-<char> is a range of characters
+ * * \<char> removes any special meaning for <char>
+ *
+ * Some interesting boundary cases:
+ * a-d-e is one range (a-d) followed by two single characters - and e.
+ * \a-\d is same as a-d
+ * a\-d is three single characters: a, d, -
+ * Trailing - is not special (so [a-] is two characters a and -).
+ * Initial - is not special ([a-] is same as [-a] is same as [\\-a])
+ * This function never sees a trailing \.
+ * [] always fails
+ * [!] always succeeds
+ */
+static int
+pm_list(const char *start, const char *end, const char c, int flags)
+{
+ const char *p = start;
+ char rangeStart = '\0', nextRangeStart;
+ int match = 1, nomatch = 0;
+
+ /* This will be used soon... */
+ (void)flags; /* UNUSED */
+
+ /* If this is a negated class, return success for nomatch. */
+ if ((*p == '!' || *p == '^') && p < end) {
+ match = 0;
+ nomatch = 1;
+ ++p;
+ }
+
+ while (p < end) {
+ nextRangeStart = '\0';
+ switch (*p) {
+ case '-':
+ /* Trailing or initial '-' is not special. */
+ if ((rangeStart == '\0') || (p == end - 1)) {
+ if (*p == c)
+ return (match);
+ } else {
+ char rangeEnd = *++p;
+ if (rangeEnd == '\\')
+ rangeEnd = *++p;
+ if ((rangeStart <= c) && (c <= rangeEnd))
+ return (match);
+ }
+ break;
+ case '\\':
+ ++p;
+ /* Fall through */
+ default:
+ if (*p == c)
+ return (match);
+ nextRangeStart = *p; /* Possible start of range. */
+ }
+ rangeStart = nextRangeStart;
+ ++p;
+ }
+ return (nomatch);
+}
+
+static int
+pm_list_w(const wchar_t *start, const wchar_t *end, const wchar_t c, int flags)
+{
+ const wchar_t *p = start;
+ wchar_t rangeStart = L'\0', nextRangeStart;
+ int match = 1, nomatch = 0;
+
+ /* This will be used soon... */
+ (void)flags; /* UNUSED */
+
+ /* If this is a negated class, return success for nomatch. */
+ if ((*p == L'!' || *p == L'^') && p < end) {
+ match = 0;
+ nomatch = 1;
+ ++p;
+ }
+
+ while (p < end) {
+ nextRangeStart = L'\0';
+ switch (*p) {
+ case L'-':
+ /* Trailing or initial '-' is not special. */
+ if ((rangeStart == L'\0') || (p == end - 1)) {
+ if (*p == c)
+ return (match);
+ } else {
+ wchar_t rangeEnd = *++p;
+ if (rangeEnd == L'\\')
+ rangeEnd = *++p;
+ if ((rangeStart <= c) && (c <= rangeEnd))
+ return (match);
+ }
+ break;
+ case L'\\':
+ ++p;
+ /* Fall through */
+ default:
+ if (*p == c)
+ return (match);
+ nextRangeStart = *p; /* Possible start of range. */
+ }
+ rangeStart = nextRangeStart;
+ ++p;
+ }
+ return (nomatch);
+}
+
+/*
+ * If s is pointing to "./", ".//", "./././" or the like, skip it.
+ */
+static const char *
+pm_slashskip(const char *s) {
+ while ((*s == '/')
+ || (s[0] == '.' && s[1] == '/')
+ || (s[0] == '.' && s[1] == '\0'))
+ ++s;
+ return (s);
+}
+
+static const wchar_t *
+pm_slashskip_w(const wchar_t *s) {
+ while ((*s == L'/')
+ || (s[0] == L'.' && s[1] == L'/')
+ || (s[0] == L'.' && s[1] == L'\0'))
+ ++s;
+ return (s);
+}
+
+static int
+pm(const char *p, const char *s, int flags)
+{
+ const char *end;
+
+ /*
+ * Ignore leading './', './/', '././', etc.
+ */
+ if (s[0] == '.' && s[1] == '/')
+ s = pm_slashskip(s + 1);
+ if (p[0] == '.' && p[1] == '/')
+ p = pm_slashskip(p + 1);
+
+ for (;;) {
+ switch (*p) {
+ case '\0':
+ if (s[0] == '/') {
+ if (flags & PATHMATCH_NO_ANCHOR_END)
+ return (1);
+ /* "dir" == "dir/" == "dir/." */
+ s = pm_slashskip(s);
+ }
+ return (*s == '\0');
+ case '?':
+ /* ? always succeeds, unless we hit end of 's' */
+ if (*s == '\0')
+ return (0);
+ break;
+ case '*':
+ /* "*" == "**" == "***" ... */
+ while (*p == '*')
+ ++p;
+ /* Trailing '*' always succeeds. */
+ if (*p == '\0')
+ return (1);
+ while (*s) {
+ if (archive_pathmatch(p, s, flags))
+ return (1);
+ ++s;
+ }
+ return (0);
+ case '[':
+ /*
+ * Find the end of the [...] character class,
+ * ignoring \] that might occur within the class.
+ */
+ end = p + 1;
+ while (*end != '\0' && *end != ']') {
+ if (*end == '\\' && end[1] != '\0')
+ ++end;
+ ++end;
+ }
+ if (*end == ']') {
+ /* We found [...], try to match it. */
+ if (!pm_list(p + 1, end, *s, flags))
+ return (0);
+ p = end; /* Jump to trailing ']' char. */
+ break;
+ } else
+ /* No final ']', so just match '['. */
+ if (*p != *s)
+ return (0);
+ break;
+ case '\\':
+ /* Trailing '\\' matches itself. */
+ if (p[1] == '\0') {
+ if (*s != '\\')
+ return (0);
+ } else {
+ ++p;
+ if (*p != *s)
+ return (0);
+ }
+ break;
+ case '/':
+ if (*s != '/' && *s != '\0')
+ return (0);
+ /* Note: pattern "/\./" won't match "/";
+ * pm_slashskip() correctly stops at backslash. */
+ p = pm_slashskip(p);
+ s = pm_slashskip(s);
+ if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+ return (1);
+ --p; /* Counteract the increment below. */
+ --s;
+ break;
+ case '$':
+ /* '$' is special only at end of pattern and only
+ * if PATHMATCH_NO_ANCHOR_END is specified. */
+ if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+ /* "dir" == "dir/" == "dir/." */
+ return (*pm_slashskip(s) == '\0');
+ }
+ /* Otherwise, '$' is not special. */
+ /* FALL THROUGH */
+ default:
+ if (*p != *s)
+ return (0);
+ break;
+ }
+ ++p;
+ ++s;
+ }
+}
+
+static int
+pm_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+ const wchar_t *end;
+
+ /*
+ * Ignore leading './', './/', '././', etc.
+ */
+ if (s[0] == L'.' && s[1] == L'/')
+ s = pm_slashskip_w(s + 1);
+ if (p[0] == L'.' && p[1] == L'/')
+ p = pm_slashskip_w(p + 1);
+
+ for (;;) {
+ switch (*p) {
+ case L'\0':
+ if (s[0] == L'/') {
+ if (flags & PATHMATCH_NO_ANCHOR_END)
+ return (1);
+ /* "dir" == "dir/" == "dir/." */
+ s = pm_slashskip_w(s);
+ }
+ return (*s == L'\0');
+ case L'?':
+ /* ? always succeeds, unless we hit end of 's' */
+ if (*s == L'\0')
+ return (0);
+ break;
+ case L'*':
+ /* "*" == "**" == "***" ... */
+ while (*p == L'*')
+ ++p;
+ /* Trailing '*' always succeeds. */
+ if (*p == L'\0')
+ return (1);
+ while (*s) {
+ if (archive_pathmatch_w(p, s, flags))
+ return (1);
+ ++s;
+ }
+ return (0);
+ case L'[':
+ /*
+ * Find the end of the [...] character class,
+ * ignoring \] that might occur within the class.
+ */
+ end = p + 1;
+ while (*end != L'\0' && *end != L']') {
+ if (*end == L'\\' && end[1] != L'\0')
+ ++end;
+ ++end;
+ }
+ if (*end == L']') {
+ /* We found [...], try to match it. */
+ if (!pm_list_w(p + 1, end, *s, flags))
+ return (0);
+ p = end; /* Jump to trailing ']' char. */
+ break;
+ } else
+ /* No final ']', so just match '['. */
+ if (*p != *s)
+ return (0);
+ break;
+ case L'\\':
+ /* Trailing '\\' matches itself. */
+ if (p[1] == L'\0') {
+ if (*s != L'\\')
+ return (0);
+ } else {
+ ++p;
+ if (*p != *s)
+ return (0);
+ }
+ break;
+ case L'/':
+ if (*s != L'/' && *s != L'\0')
+ return (0);
+ /* Note: pattern "/\./" won't match "/";
+ * pm_slashskip() correctly stops at backslash. */
+ p = pm_slashskip_w(p);
+ s = pm_slashskip_w(s);
+ if (*p == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+ return (1);
+ --p; /* Counteract the increment below. */
+ --s;
+ break;
+ case L'$':
+ /* '$' is special only at end of pattern and only
+ * if PATHMATCH_NO_ANCHOR_END is specified. */
+ if (p[1] == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+ /* "dir" == "dir/" == "dir/." */
+ return (*pm_slashskip_w(s) == L'\0');
+ }
+ /* Otherwise, '$' is not special. */
+ /* FALL THROUGH */
+ default:
+ if (*p != *s)
+ return (0);
+ break;
+ }
+ ++p;
+ ++s;
+ }
+}
+
+/* Main entry point. */
+int
+__archive_pathmatch(const char *p, const char *s, int flags)
+{
+ /* Empty pattern only matches the empty string. */
+ if (p == NULL || *p == '\0')
+ return (s == NULL || *s == '\0');
+ else if (s == NULL)
+ return (0);
+
+ /* Leading '^' anchors the start of the pattern. */
+ if (*p == '^') {
+ ++p;
+ flags &= ~PATHMATCH_NO_ANCHOR_START;
+ }
+
+ if (*p == '/' && *s != '/')
+ return (0);
+
+ /* Certain patterns anchor implicitly. */
+ if (*p == '*' || *p == '/') {
+ while (*p == '/')
+ ++p;
+ while (*s == '/')
+ ++s;
+ return (pm(p, s, flags));
+ }
+
+ /* If start is unanchored, try to match start of each path element. */
+ if (flags & PATHMATCH_NO_ANCHOR_START) {
+ for ( ; s != NULL; s = strchr(s, '/')) {
+ if (*s == '/')
+ s++;
+ if (pm(p, s, flags))
+ return (1);
+ }
+ return (0);
+ }
+
+ /* Default: Match from beginning. */
+ return (pm(p, s, flags));
+}
+
+int
+__archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+ /* Empty pattern only matches the empty string. */
+ if (p == NULL || *p == L'\0')
+ return (s == NULL || *s == L'\0');
+ else if (s == NULL)
+ return (0);
+
+ /* Leading '^' anchors the start of the pattern. */
+ if (*p == L'^') {
+ ++p;
+ flags &= ~PATHMATCH_NO_ANCHOR_START;
+ }
+
+ if (*p == L'/' && *s != L'/')
+ return (0);
+
+ /* Certain patterns anchor implicitly. */
+ if (*p == L'*' || *p == L'/') {
+ while (*p == L'/')
+ ++p;
+ while (*s == L'/')
+ ++s;
+ return (pm_w(p, s, flags));
+ }
+
+ /* If start is unanchored, try to match start of each path element. */
+ if (flags & PATHMATCH_NO_ANCHOR_START) {
+ for ( ; s != NULL; s = wcschr(s, L'/')) {
+ if (*s == L'/')
+ s++;
+ if (pm_w(p, s, flags))
+ return (1);
+ }
+ return (0);
+ }
+
+ /* Default: Match from beginning. */
+ return (pm_w(p, s, flags));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_pathmatch.h b/contrib/libs/libarchive/libarchive/archive_pathmatch.h
new file mode 100644
index 0000000000..9995142921
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_pathmatch.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_PATHMATCH_H
+#define ARCHIVE_PATHMATCH_H
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+/* Don't anchor at beginning unless the pattern starts with "^" */
+#define PATHMATCH_NO_ANCHOR_START 1
+/* Don't anchor at end unless the pattern ends with "$" */
+#define PATHMATCH_NO_ANCHOR_END 2
+
+/* Note that "^" and "$" are not special unless you set the corresponding
+ * flag above. */
+
+int __archive_pathmatch(const char *p, const char *s, int flags);
+int __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags);
+
+#define archive_pathmatch(p, s, f) __archive_pathmatch(p, s, f)
+#define archive_pathmatch_w(p, s, f) __archive_pathmatch_w(p, s, f)
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_platform.h b/contrib/libs/libarchive/libarchive/archive_platform.h
new file mode 100644
index 0000000000..2f13414766
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_platform.h
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_platform.h 201090 2009-12-28 02:22:04Z kientzle $
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+/*
+ * This header is the first thing included in any of the libarchive
+ * source files. As far as possible, platform-specific issues should
+ * be dealt with here and not within individual source files. I'm
+ * actively trying to minimize #if blocks within the main source,
+ * since they obfuscate the code.
+ */
+
+#ifndef ARCHIVE_PLATFORM_H_INCLUDED
+#define ARCHIVE_PLATFORM_H_INCLUDED
+
+/* archive.h and archive_entry.h require this. */
+#define __LIBARCHIVE_BUILD 1
+
+#if defined(PLATFORM_CONFIG_H)
+/* Use hand-built config.h in environments that need it. */
+#error #include PLATFORM_CONFIG_H
+#elif defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "config.h"
+#else
+/* Warn if the library hasn't been (automatically or manually) configured. */
+#error Oops: No config.h and no pre-built configuration in archive_platform.h.
+#endif
+
+/* On macOS check for some symbols based on the deployment target version. */
+#if defined(__APPLE__)
+# undef HAVE_FUTIMENS
+# undef HAVE_UTIMENSAT
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
+# define HAVE_FUTIMENS 1
+# define HAVE_UTIMENSAT 1
+# endif
+#endif
+
+/* It should be possible to get rid of this by extending the feature-test
+ * macros to cover Windows API functions, probably along with non-trivial
+ * refactoring of code to find structures that sit more cleanly on top of
+ * either Windows or Posix APIs. */
+#if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
+#include "archive_windows.h"
+/* The C library on Windows specifies a calling convention for callback
+ * functions and exports; when we interact with them (capture pointers,
+ * call and pass function pointers) we need to match their calling
+ * convention.
+ * This only matters when libarchive is built with /Gr, /Gz or /Gv
+ * (which change the default calling convention.) */
+#define __LA_LIBC_CC __cdecl
+#else
+#define la_stat(path,stref) stat(path,stref)
+#define __LA_LIBC_CC
+#endif
+
+/*
+ * The config files define a lot of feature macros. The following
+ * uses those macros to select/define replacements and include key
+ * headers as required.
+ */
+
+/* Get a real definition for __FBSDID or __RCSID if we can */
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+/* If not, define them so as to avoid dangling semicolons. */
+#ifndef __FBSDID
+#define __FBSDID(a) struct _undefined_hack
+#endif
+#ifndef __RCSID
+#define __RCSID(a) struct _undefined_hack
+#endif
+
+/* Try to get standard C99-style integer type definitions. */
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/* Borland warns about its own constants! */
+#if defined(__BORLANDC__)
+# if HAVE_DECL_UINT64_MAX
+# undef UINT64_MAX
+# undef HAVE_DECL_UINT64_MAX
+# endif
+# if HAVE_DECL_UINT64_MIN
+# undef UINT64_MIN
+# undef HAVE_DECL_UINT64_MIN
+# endif
+# if HAVE_DECL_INT64_MAX
+# undef INT64_MAX
+# undef HAVE_DECL_INT64_MAX
+# endif
+# if HAVE_DECL_INT64_MIN
+# undef INT64_MIN
+# undef HAVE_DECL_INT64_MIN
+# endif
+#endif
+
+/* Some platforms lack the standard *_MAX definitions. */
+#if !HAVE_DECL_SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+#if !HAVE_DECL_SSIZE_MAX
+#define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))
+#endif
+#if !HAVE_DECL_UINT32_MAX
+#define UINT32_MAX (~(uint32_t)0)
+#endif
+#if !HAVE_DECL_INT32_MAX
+#define INT32_MAX ((int32_t)(UINT32_MAX >> 1))
+#endif
+#if !HAVE_DECL_INT32_MIN
+#define INT32_MIN ((int32_t)(~INT32_MAX))
+#endif
+#if !HAVE_DECL_UINT64_MAX
+#define UINT64_MAX (~(uint64_t)0)
+#endif
+#if !HAVE_DECL_INT64_MAX
+#define INT64_MAX ((int64_t)(UINT64_MAX >> 1))
+#endif
+#if !HAVE_DECL_INT64_MIN
+#define INT64_MIN ((int64_t)(~INT64_MAX))
+#endif
+#if !HAVE_DECL_UINTMAX_MAX
+#define UINTMAX_MAX (~(uintmax_t)0)
+#endif
+#if !HAVE_DECL_INTMAX_MAX
+#define INTMAX_MAX ((intmax_t)(UINTMAX_MAX >> 1))
+#endif
+#if !HAVE_DECL_INTMAX_MIN
+#define INTMAX_MIN ((intmax_t)(~INTMAX_MAX))
+#endif
+
+/* Some platforms lack the standard PRIxN/PRIdN definitions. */
+#if !HAVE_INTTYPES_H || !defined(PRIx32) || !defined(PRId32)
+#ifndef PRIx32
+#if SIZEOF_INT == 4
+#define PRIx32 "x"
+#elif SIZEOF_LONG == 4
+#define PRIx32 "lx"
+#else
+#error No suitable 32-bit unsigned integer type found for this platform
+#endif
+#endif // PRIx32
+#ifndef PRId32
+#if SIZEOF_INT == 4
+#define PRId32 "d"
+#elif SIZEOF_LONG == 4
+#define PRId32 "ld"
+#else
+#error No suitable 32-bit signed integer type found for this platform
+#endif
+#endif // PRId32
+#endif // !HAVE_INTTYPES_H || !defined(PRIx32) || !defined(PRId32)
+
+/*
+ * If we can't restore metadata using a file descriptor, then
+ * for compatibility's sake, close files before trying to restore metadata.
+ */
+#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN)
+#define CAN_RESTORE_METADATA_FD
+#endif
+
+/*
+ * glibc 2.24 deprecates readdir_r
+ * bionic c deprecates readdir_r too
+ */
+#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24)) && (!defined(__ANDROID__))
+#define USE_READDIR_R 1
+#else
+#undef USE_READDIR_R
+#endif
+
+/* Set up defaults for internal error codes. */
+#ifndef ARCHIVE_ERRNO_FILE_FORMAT
+#if HAVE_EFTYPE
+#define ARCHIVE_ERRNO_FILE_FORMAT EFTYPE
+#else
+#if HAVE_EILSEQ
+#define ARCHIVE_ERRNO_FILE_FORMAT EILSEQ
+#else
+#define ARCHIVE_ERRNO_FILE_FORMAT EINVAL
+#endif
+#endif
+#endif
+
+#ifndef ARCHIVE_ERRNO_PROGRAMMER
+#define ARCHIVE_ERRNO_PROGRAMMER EINVAL
+#endif
+
+#ifndef ARCHIVE_ERRNO_MISC
+#define ARCHIVE_ERRNO_MISC (-1)
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ >= 7)
+#define __LA_FALLTHROUGH __attribute__((fallthrough))
+#else
+#define __LA_FALLTHROUGH
+#endif
+
+#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_platform_acl.h b/contrib/libs/libarchive/libarchive/archive_platform_acl.h
new file mode 100644
index 0000000000..264e6de375
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_platform_acl.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+#ifndef ARCHIVE_PLATFORM_ACL_H_INCLUDED
+#define ARCHIVE_PLATFORM_ACL_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST_COMMON
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+/*
+ * Determine what ACL types are supported
+ */
+#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_SUNOS || ARCHIVE_ACL_LIBACL
+#define ARCHIVE_ACL_POSIX1E 1
+#endif
+
+#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_SUNOS_NFS4 || \
+ ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
+#define ARCHIVE_ACL_NFS4 1
+#endif
+
+#if ARCHIVE_ACL_POSIX1E || ARCHIVE_ACL_NFS4
+#define ARCHIVE_ACL_SUPPORT 1
+#endif
+
+#endif /* ARCHIVE_PLATFORM_ACL_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd7.c b/contrib/libs/libarchive/libarchive/archive_ppmd7.c
new file mode 100644
index 0000000000..cc3f778203
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd7.c
@@ -0,0 +1,1168 @@
+/* Ppmd7.c -- PPMdH codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include <stdlib.h>
+
+#include "archive_ppmd7_private.h"
+
+#ifdef PPMD_32BIT
+ #define Ppmd7_GetPtr(p, ptr) (ptr)
+ #define Ppmd7_GetContext(p, ptr) (ptr)
+ #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats)
+#else
+ #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
+ #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs)))
+ #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats)))
+#endif
+
+#define Ppmd7_GetBinSumm(p) \
+ &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \
+ p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \
+ (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \
+ 2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \
+ ((p->RunLength >> 26) & 0x20)]
+
+#define kTopValue (1 << 24)
+#define MAX_FREQ 124
+#define UNIT_SIZE 12
+
+#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
+#define U2I(nu) (p->Units2Indx[(nu) - 1])
+#define I2U(indx) (p->Indx2Units[indx])
+
+#ifdef PPMD_32BIT
+ #define REF(ptr) (ptr)
+#else
+ #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
+#endif
+
+#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
+
+#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))
+#define STATS(ctx) Ppmd7_GetStats(p, ctx)
+#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)
+#define SUFFIX(ctx) CTX((ctx)->Suffix)
+
+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
+static const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+
+typedef CPpmd7_Context * CTX_PTR;
+
+struct CPpmd7_Node_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd7_Node_ *
+ #else
+ UInt32
+ #endif
+ CPpmd7_Node_Ref;
+
+typedef struct CPpmd7_Node_
+{
+ UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */
+ UInt16 NU;
+ CPpmd7_Node_Ref Next; /* must be at offset >= 4 */
+ CPpmd7_Node_Ref Prev;
+} CPpmd7_Node;
+
+#ifdef PPMD_32BIT
+ #define NODE(ptr) (ptr)
+#else
+ #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs)))
+#endif
+
+static void Ppmd7_Update1(CPpmd7 *p);
+static void Ppmd7_Update1_0(CPpmd7 *p);
+static void Ppmd7_Update2(CPpmd7 *p);
+static void Ppmd7_UpdateBin(CPpmd7 *p);
+static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked,
+ UInt32 *scale);
+
+/* ----------- Base ----------- */
+
+static void Ppmd7_Construct(CPpmd7 *p)
+{
+ unsigned i, k, m;
+
+ p->Base = 0;
+
+ for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
+ do { p->Units2Indx[k++] = (Byte)i; } while(--step);
+ p->Indx2Units[i] = (Byte)k;
+ }
+
+ p->NS2BSIndx[0] = (0 << 1);
+ p->NS2BSIndx[1] = (1 << 1);
+ memset(p->NS2BSIndx + 2, (2 << 1), 9);
+ memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
+
+ for (i = 0; i < 3; i++)
+ p->NS2Indx[i] = (Byte)i;
+ for (m = i, k = 1; i < 256; i++)
+ {
+ p->NS2Indx[i] = (Byte)m;
+ if (--k == 0)
+ k = (++m) - 2;
+ }
+
+ memset(p->HB2Flag, 0, 0x40);
+ memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40);
+}
+
+static void Ppmd7_Free(CPpmd7 *p)
+{
+ free(p->Base);
+ p->Size = 0;
+ p->Base = 0;
+}
+
+static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size)
+{
+ if (p->Base == 0 || p->Size != size)
+ {
+ /* RestartModel() below assumes that p->Size >= UNIT_SIZE
+ (see the calculation of m->MinContext). */
+ if (size < UNIT_SIZE) {
+ return False;
+ }
+ Ppmd7_Free(p);
+ p->AlignOffset =
+ #ifdef PPMD_32BIT
+ (4 - size) & 3;
+ #else
+ 4 - (size & 3);
+ #endif
+ if ((p->Base = (Byte *)malloc(p->AlignOffset + size
+ #ifndef PPMD_32BIT
+ + UNIT_SIZE
+ #endif
+ )) == 0)
+ return False;
+ p->Size = size;
+ }
+ return True;
+}
+
+static void InsertNode(CPpmd7 *p, void *node, unsigned indx)
+{
+ *((CPpmd_Void_Ref *)node) = p->FreeList[indx];
+ p->FreeList[indx] = REF(node);
+}
+
+static void *RemoveNode(CPpmd7 *p, unsigned indx)
+{
+ CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]);
+ p->FreeList[indx] = *node;
+ return node;
+}
+
+static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
+{
+ unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
+ ptr = (Byte *)ptr + U2B(I2U(newIndx));
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
+ }
+ InsertNode(p, ptr, i);
+}
+
+static void GlueFreeBlocks(CPpmd7 *p)
+{
+ #ifdef PPMD_32BIT
+ CPpmd7_Node headItem;
+ CPpmd7_Node_Ref head = &headItem;
+ #else
+ CPpmd7_Node_Ref head = p->AlignOffset + p->Size;
+ #endif
+
+ CPpmd7_Node_Ref n = head;
+ unsigned i;
+
+ p->GlueCount = 255;
+
+ /* create doubly-linked list of free blocks */
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ UInt16 nu = I2U(i);
+ CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i];
+ p->FreeList[i] = 0;
+ while (next != 0)
+ {
+ CPpmd7_Node *node = NODE(next);
+ node->Next = n;
+ n = NODE(n)->Prev = next;
+ next = *(const CPpmd7_Node_Ref *)node;
+ node->Stamp = 0;
+ node->NU = (UInt16)nu;
+ }
+ }
+ NODE(head)->Stamp = 1;
+ NODE(head)->Next = n;
+ NODE(n)->Prev = head;
+ if (p->LoUnit != p->HiUnit)
+ ((CPpmd7_Node *)p->LoUnit)->Stamp = 1;
+
+ /* Glue free blocks */
+ while (n != head)
+ {
+ CPpmd7_Node *node = NODE(n);
+ UInt32 nu = (UInt32)node->NU;
+ for (;;)
+ {
+ CPpmd7_Node *node2 = NODE(n) + nu;
+ nu += node2->NU;
+ if (node2->Stamp != 0 || nu >= 0x10000)
+ break;
+ NODE(node2->Prev)->Next = node2->Next;
+ NODE(node2->Next)->Prev = node2->Prev;
+ node->NU = (UInt16)nu;
+ }
+ n = node->Next;
+ }
+
+ /* Fill lists of free blocks */
+ for (n = NODE(head)->Next; n != head;)
+ {
+ CPpmd7_Node *node = NODE(n);
+ unsigned nu;
+ CPpmd7_Node_Ref next = node->Next;
+ for (nu = node->NU; nu > 128; nu -= 128, node += 128)
+ InsertNode(p, node, PPMD_NUM_INDEXES - 1);
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, node + k, nu - k - 1);
+ }
+ InsertNode(p, node, i);
+ n = next;
+ }
+}
+
+static void *AllocUnitsRare(CPpmd7 *p, unsigned indx)
+{
+ unsigned i;
+ void *retVal;
+ if (p->GlueCount == 0)
+ {
+ GlueFreeBlocks(p);
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ }
+ i = indx;
+ do
+ {
+ if (++i == PPMD_NUM_INDEXES)
+ {
+ UInt32 numBytes = U2B(I2U(indx));
+ p->GlueCount--;
+ return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
+ }
+ }
+ while (p->FreeList[i] == 0);
+ retVal = RemoveNode(p, i);
+ SplitBlock(p, retVal, i, indx);
+ return retVal;
+}
+
+static void *AllocUnits(CPpmd7 *p, unsigned indx)
+{
+ UInt32 numBytes;
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ numBytes = U2B(I2U(indx));
+ if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
+ {
+ void *retVal = p->LoUnit;
+ p->LoUnit += numBytes;
+ return retVal;
+ }
+ return AllocUnitsRare(p, indx);
+}
+
+#define MyMem12Cpy(dest, src, num) \
+ { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \
+ do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); }
+
+static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
+{
+ unsigned i0 = U2I(oldNU);
+ unsigned i1 = U2I(newNU);
+ if (i0 == i1)
+ return oldPtr;
+ if (p->FreeList[i1] != 0)
+ {
+ void *ptr = RemoveNode(p, i1);
+ MyMem12Cpy(ptr, oldPtr, newNU);
+ InsertNode(p, oldPtr, i0);
+ return ptr;
+ }
+ SplitBlock(p, oldPtr, i0, i1);
+ return oldPtr;
+}
+
+#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
+
+static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
+{
+ (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
+ (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
+}
+
+static void RestartModel(CPpmd7 *p)
+{
+ unsigned i, k, m;
+
+ memset(p->FreeList, 0, sizeof(p->FreeList));
+ p->Text = p->Base + p->AlignOffset;
+ p->HiUnit = p->Text + p->Size;
+ p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
+ p->GlueCount = 0;
+
+ p->OrderFall = p->MaxOrder;
+ p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
+ p->PrevSuccess = 0;
+
+ p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
+ p->MinContext->Suffix = 0;
+ p->MinContext->NumStats = 256;
+ p->MinContext->SummFreq = 256 + 1;
+ p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
+ p->LoUnit += U2B(256 / 2);
+ p->MinContext->Stats = REF(p->FoundState);
+ for (i = 0; i < 256; i++)
+ {
+ CPpmd_State *s = &p->FoundState[i];
+ s->Symbol = (Byte)i;
+ s->Freq = 1;
+ SetSuccessor(s, 0);
+ }
+
+ for (i = 0; i < 128; i++)
+ for (k = 0; k < 8; k++)
+ {
+ UInt16 *dest = p->BinSumm[i] + k;
+ UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2));
+ for (m = 0; m < 64; m += 8)
+ dest[m] = val;
+ }
+
+ for (i = 0; i < 25; i++)
+ for (k = 0; k < 16; k++)
+ {
+ CPpmd_See *s = &p->See[i][k];
+ s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4));
+ s->Count = 4;
+ }
+}
+
+static void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)
+{
+ p->MaxOrder = maxOrder;
+ RestartModel(p);
+ p->DummySee.Shift = PPMD_PERIOD_BITS;
+ p->DummySee.Summ = 0; /* unused */
+ p->DummySee.Count = 64; /* unused */
+}
+
+static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)
+{
+ CPpmd_State upState;
+ CTX_PTR c = p->MinContext;
+ CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
+ CPpmd_State *ps[PPMD7_MAX_ORDER];
+ unsigned numPs = 0;
+
+ if (!skip)
+ ps[numPs++] = p->FoundState;
+
+ while (c->Suffix)
+ {
+ CPpmd_Void_Ref successor;
+ CPpmd_State *s;
+ c = SUFFIX(c);
+ if (c->NumStats != 1)
+ {
+ for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
+ }
+ else
+ s = ONE_STATE(c);
+ successor = SUCCESSOR(s);
+ if (successor != upBranch)
+ {
+ c = CTX(successor);
+ if (numPs == 0)
+ return c;
+ break;
+ }
+ ps[numPs++] = s;
+ }
+
+ upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch);
+ SetSuccessor(&upState, upBranch + 1);
+
+ if (c->NumStats == 1)
+ upState.Freq = ONE_STATE(c)->Freq;
+ else
+ {
+ UInt32 cf, s0;
+ CPpmd_State *s;
+ for (s = STATS(c); s->Symbol != upState.Symbol; s++);
+ cf = s->Freq - 1;
+ s0 = c->SummFreq - c->NumStats - cf;
+ upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))));
+ }
+
+ while (numPs != 0)
+ {
+ /* Create Child */
+ CTX_PTR c1; /* = AllocContext(p); */
+ if (p->HiUnit != p->LoUnit)
+ c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
+ else if (p->FreeList[0] != 0)
+ c1 = (CTX_PTR)RemoveNode(p, 0);
+ else
+ {
+ c1 = (CTX_PTR)AllocUnitsRare(p, 0);
+ if (!c1)
+ return NULL;
+ }
+ c1->NumStats = 1;
+ *ONE_STATE(c1) = upState;
+ c1->Suffix = REF(c);
+ SetSuccessor(ps[--numPs], REF(c1));
+ c = c1;
+ }
+
+ return c;
+}
+
+static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
+{
+ CPpmd_State tmp = *t1;
+ *t1 = *t2;
+ *t2 = tmp;
+}
+
+static void UpdateModel(CPpmd7 *p)
+{
+ CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
+ CTX_PTR c;
+ unsigned s0, ns;
+
+ if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
+ {
+ c = SUFFIX(p->MinContext);
+
+ if (c->NumStats == 1)
+ {
+ CPpmd_State *s = ONE_STATE(c);
+ if (s->Freq < 32)
+ s->Freq++;
+ }
+ else
+ {
+ CPpmd_State *s = STATS(c);
+ if (s->Symbol != p->FoundState->Symbol)
+ {
+ do { s++; } while (s->Symbol != p->FoundState->Symbol);
+ if (s[0].Freq >= s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ s--;
+ }
+ }
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq += 2;
+ c->SummFreq += 2;
+ }
+ }
+ }
+
+ if (p->OrderFall == 0)
+ {
+ p->MinContext = p->MaxContext = CreateSuccessors(p, True);
+ if (p->MinContext == 0)
+ {
+ RestartModel(p);
+ return;
+ }
+ SetSuccessor(p->FoundState, REF(p->MinContext));
+ return;
+ }
+
+ *p->Text++ = p->FoundState->Symbol;
+ successor = REF(p->Text);
+ if (p->Text >= p->UnitsStart)
+ {
+ RestartModel(p);
+ return;
+ }
+
+ if (fSuccessor)
+ {
+ if (fSuccessor <= successor)
+ {
+ CTX_PTR cs = CreateSuccessors(p, False);
+ if (cs == NULL)
+ {
+ RestartModel(p);
+ return;
+ }
+ fSuccessor = REF(cs);
+ }
+ if (--p->OrderFall == 0)
+ {
+ successor = fSuccessor;
+ p->Text -= (p->MaxContext != p->MinContext);
+ }
+ }
+ else
+ {
+ SetSuccessor(p->FoundState, successor);
+ fSuccessor = REF(p->MinContext);
+ }
+
+ s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1);
+
+ for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c))
+ {
+ unsigned ns1;
+ UInt32 cf, sf;
+ if ((ns1 = c->NumStats) != 1)
+ {
+ if ((ns1 & 1) == 0)
+ {
+ /* Expand for one UNIT */
+ unsigned oldNU = ns1 >> 1;
+ unsigned i = U2I(oldNU);
+ if (i != U2I(oldNU + 1))
+ {
+ void *ptr = AllocUnits(p, i + 1);
+ void *oldPtr;
+ if (!ptr)
+ {
+ RestartModel(p);
+ return;
+ }
+ oldPtr = STATS(c);
+ MyMem12Cpy(ptr, oldPtr, oldNU);
+ InsertNode(p, oldPtr, i);
+ c->Stats = STATS_REF(ptr);
+ }
+ }
+ c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1)));
+ }
+ else
+ {
+ CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);
+ if (!s)
+ {
+ RestartModel(p);
+ return;
+ }
+ *s = *ONE_STATE(c);
+ c->Stats = REF(s);
+ if (s->Freq < MAX_FREQ / 4 - 1)
+ s->Freq <<= 1;
+ else
+ s->Freq = MAX_FREQ - 4;
+ c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3));
+ }
+ cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6);
+ sf = (UInt32)s0 + c->SummFreq;
+ if (cf < 6 * sf)
+ {
+ cf = 1 + (cf > sf) + (cf >= 4 * sf);
+ c->SummFreq += 3;
+ }
+ else
+ {
+ cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);
+ c->SummFreq = (UInt16)(c->SummFreq + cf);
+ }
+ {
+ CPpmd_State *s = STATS(c) + ns1;
+ SetSuccessor(s, successor);
+ s->Symbol = p->FoundState->Symbol;
+ s->Freq = (Byte)cf;
+ c->NumStats = (UInt16)(ns1 + 1);
+ }
+ }
+ p->MaxContext = p->MinContext = CTX(fSuccessor);
+}
+
+static void Rescale(CPpmd7 *p)
+{
+ unsigned i, adder, sumFreq, escFreq;
+ CPpmd_State *stats = STATS(p->MinContext);
+ CPpmd_State *s = p->FoundState;
+ {
+ CPpmd_State tmp = *s;
+ for (; s != stats; s--)
+ s[0] = s[-1];
+ *s = tmp;
+ }
+ escFreq = p->MinContext->SummFreq - s->Freq;
+ s->Freq += 4;
+ adder = (p->OrderFall != 0);
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq = s->Freq;
+
+ i = p->MinContext->NumStats - 1;
+ do
+ {
+ escFreq -= (++s)->Freq;
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq += s->Freq;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ CPpmd_State *s1 = s;
+ CPpmd_State tmp = *s1;
+ do
+ s1[0] = s1[-1];
+ while (--s1 != stats && tmp.Freq > s1[-1].Freq);
+ *s1 = tmp;
+ }
+ }
+ while (--i);
+
+ if (s->Freq == 0)
+ {
+ unsigned numStats = p->MinContext->NumStats;
+ unsigned n0, n1;
+ do { i++; } while ((--s)->Freq == 0);
+ escFreq += i;
+ p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i);
+ if (p->MinContext->NumStats == 1)
+ {
+ CPpmd_State tmp = *stats;
+ do
+ {
+ tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1));
+ escFreq >>= 1;
+ }
+ while (escFreq > 1);
+ InsertNode(p, stats, U2I(((numStats + 1) >> 1)));
+ *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
+ return;
+ }
+ n0 = (numStats + 1) >> 1;
+ n1 = (p->MinContext->NumStats + 1) >> 1;
+ if (n0 != n1)
+ p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
+ }
+ p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
+ p->FoundState = STATS(p->MinContext);
+}
+
+static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)
+{
+ CPpmd_See *see;
+ unsigned nonMasked = p->MinContext->NumStats - numMasked;
+ if (p->MinContext->NumStats != 256)
+ {
+ see = p->See[p->NS2Indx[nonMasked - 1]] +
+ (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) +
+ 2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) +
+ 4 * (numMasked > nonMasked) +
+ p->HiBitsFlag;
+ {
+ unsigned r = (see->Summ >> see->Shift);
+ see->Summ = (UInt16)(see->Summ - r);
+ *escFreq = r + (r == 0);
+ }
+ }
+ else
+ {
+ see = &p->DummySee;
+ *escFreq = 1;
+ }
+ return see;
+}
+
+static void NextContext(CPpmd7 *p)
+{
+ CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
+ if (p->OrderFall == 0 && (Byte *)c > p->Text)
+ p->MinContext = p->MaxContext = c;
+ else
+ UpdateModel(p);
+}
+
+static void Ppmd7_Update1(CPpmd7 *p)
+{
+ CPpmd_State *s = p->FoundState;
+ s->Freq += 4;
+ p->MinContext->SummFreq += 4;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ p->FoundState = --s;
+ if (s->Freq > MAX_FREQ)
+ Rescale(p);
+ }
+ NextContext(p);
+}
+
+static void Ppmd7_Update1_0(CPpmd7 *p)
+{
+ p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq);
+ p->RunLength += p->PrevSuccess;
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ NextContext(p);
+}
+
+static void Ppmd7_UpdateBin(CPpmd7 *p)
+{
+ p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0));
+ p->PrevSuccess = 1;
+ p->RunLength++;
+ NextContext(p);
+}
+
+static void Ppmd7_Update2(CPpmd7 *p)
+{
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ p->RunLength = p->InitRL;
+ UpdateModel(p);
+}
+
+/* ---------- Decode ---------- */
+
+static Bool Ppmd_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ unsigned i;
+ p->Low = p->Bottom = 0;
+ p->Range = 0xFFFFFFFF;
+ for (i = 0; i < 4; i++)
+ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+ return (p->Code < 0xFFFFFFFF);
+}
+
+static Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ if (p->Stream->Read((void *)p->Stream) != 0)
+ return False;
+ return Ppmd_RangeDec_Init(p);
+}
+
+static Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+ if (!Ppmd_RangeDec_Init(p))
+ return False;
+ p->Bottom = 0x8000;
+ return True;
+}
+
+static UInt32 Range_GetThreshold(void *pp, UInt32 total)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ return (p->Code - p->Low) / (p->Range /= total);
+}
+
+static void Range_Normalize(CPpmd7z_RangeDec *p)
+{
+ while (1)
+ {
+ if((p->Low ^ (p->Low + p->Range)) >= kTopValue)
+ {
+ if(p->Range >= p->Bottom)
+ break;
+ else
+ p->Range = ((uint32_t)(-(int32_t)p->Low)) & (p->Bottom - 1);
+ }
+ p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+ p->Range <<= 8;
+ p->Low <<= 8;
+ }
+}
+
+static void Range_Decode_7z(void *pp, UInt32 start, UInt32 size)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ p->Code -= start * p->Range;
+ p->Range *= size;
+ Range_Normalize(p);
+}
+
+static void Range_Decode_RAR(void *pp, UInt32 start, UInt32 size)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ p->Low += start * p->Range;
+ p->Range *= size;
+ Range_Normalize(p);
+}
+
+static UInt32 Range_DecodeBit_7z(void *pp, UInt32 size0)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ UInt32 newBound = (p->Range >> 14) * size0;
+ UInt32 symbol;
+ if (p->Code < newBound)
+ {
+ symbol = 0;
+ p->Range = newBound;
+ }
+ else
+ {
+ symbol = 1;
+ p->Code -= newBound;
+ p->Range -= newBound;
+ }
+ Range_Normalize(p);
+ return symbol;
+}
+
+static UInt32 Range_DecodeBit_RAR(void *pp, UInt32 size0)
+{
+ CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+ UInt32 bit, value = p->p.GetThreshold(p, PPMD_BIN_SCALE);
+ if(value < size0)
+ {
+ bit = 0;
+ p->p.Decode(p, 0, size0);
+ }
+ else
+ {
+ bit = 1;
+ p->p.Decode(p, size0, PPMD_BIN_SCALE - size0);
+ }
+ return bit;
+}
+
+static void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+ p->p.GetThreshold = Range_GetThreshold;
+ p->p.Decode = Range_Decode_7z;
+ p->p.DecodeBit = Range_DecodeBit_7z;
+}
+
+static void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+ p->p.GetThreshold = Range_GetThreshold;
+ p->p.Decode = Range_Decode_RAR;
+ p->p.DecodeBit = Range_DecodeBit_RAR;
+}
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+static int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc)
+{
+ size_t charMask[256 / sizeof(size_t)];
+ if (p->MinContext->NumStats != 1)
+ {
+ CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
+ unsigned i;
+ UInt32 count, hiCnt;
+ if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
+ {
+ Byte symbol;
+ rc->Decode(rc, 0, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update1_0(p);
+ return symbol;
+ }
+ p->PrevSuccess = 0;
+ i = p->MinContext->NumStats - 1;
+ do
+ {
+ if ((hiCnt += (++s)->Freq) > count)
+ {
+ Byte symbol;
+ rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update1(p);
+ return symbol;
+ }
+ }
+ while (--i);
+ if (count >= p->MinContext->SummFreq)
+ return -2;
+ p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
+ rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt);
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ i = p->MinContext->NumStats - 1;
+ do { MASK((--s)->Symbol) = 0; } while (--i);
+ }
+ else
+ {
+ UInt16 *prob = Ppmd7_GetBinSumm(p);
+ if (rc->DecodeBit(rc, *prob) == 0)
+ {
+ Byte symbol;
+ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+ symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol;
+ Ppmd7_UpdateBin(p);
+ return symbol;
+ }
+ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+ p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0;
+ p->PrevSuccess = 0;
+ }
+ for (;;)
+ {
+ CPpmd_State *ps[256], *s;
+ UInt32 freqSum, count, hiCnt;
+ CPpmd_See *see;
+ unsigned i, num, numMasked = p->MinContext->NumStats;
+ do
+ {
+ p->OrderFall++;
+ if (!p->MinContext->Suffix)
+ return -1;
+ p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
+ }
+ while (p->MinContext->NumStats == numMasked);
+ hiCnt = 0;
+ s = Ppmd7_GetStats(p, p->MinContext);
+ i = 0;
+ num = p->MinContext->NumStats - numMasked;
+ do
+ {
+ int k = (int)(MASK(s->Symbol));
+ hiCnt += (s->Freq & k);
+ ps[i] = s++;
+ i -= k;
+ }
+ while (i != num);
+
+ see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum);
+ freqSum += hiCnt;
+ count = rc->GetThreshold(rc, freqSum);
+
+ if (count < hiCnt)
+ {
+ Byte symbol;
+ CPpmd_State **pps = ps;
+ for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
+ s = *pps;
+ rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+ Ppmd_See_Update(see);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd7_Update2(p);
+ return symbol;
+ }
+ if (count >= freqSum)
+ return -2;
+ rc->Decode(rc, hiCnt, freqSum - hiCnt);
+ see->Summ = (UInt16)(see->Summ + freqSum);
+ do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
+ }
+}
+
+/* ---------- Encode ---------- Ppmd7Enc.c */
+
+#define kTopValue (1 << 24)
+
+static void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p)
+{
+ p->Low = 0;
+ p->Range = 0xFFFFFFFF;
+ p->Cache = 0;
+ p->CacheSize = 1;
+}
+
+static void RangeEnc_ShiftLow(CPpmd7z_RangeEnc *p)
+{
+ if ((UInt32)p->Low < (UInt32)0xFF000000 || (unsigned)(p->Low >> 32) != 0)
+ {
+ Byte temp = p->Cache;
+ do
+ {
+ p->Stream->Write(p->Stream, (Byte)(temp + (Byte)(p->Low >> 32)));
+ temp = 0xFF;
+ }
+ while(--p->CacheSize != 0);
+ p->Cache = (Byte)((UInt32)p->Low >> 24);
+ }
+ p->CacheSize++;
+ p->Low = ((UInt32)p->Low << 8) & 0xFFFFFFFF;
+}
+
+static void RangeEnc_Encode(CPpmd7z_RangeEnc *p, UInt32 start, UInt32 size, UInt32 total)
+{
+ p->Low += (UInt64)start * (UInt64)(p->Range /= total);
+ p->Range *= size;
+ while (p->Range < kTopValue)
+ {
+ p->Range <<= 8;
+ RangeEnc_ShiftLow(p);
+ }
+}
+
+static void RangeEnc_EncodeBit_0(CPpmd7z_RangeEnc *p, UInt32 size0)
+{
+ p->Range = (p->Range >> 14) * size0;
+ while (p->Range < kTopValue)
+ {
+ p->Range <<= 8;
+ RangeEnc_ShiftLow(p);
+ }
+}
+
+static void RangeEnc_EncodeBit_1(CPpmd7z_RangeEnc *p, UInt32 size0)
+{
+ UInt32 newBound = (p->Range >> 14) * size0;
+ p->Low += newBound;
+ p->Range -= newBound;
+ while (p->Range < kTopValue)
+ {
+ p->Range <<= 8;
+ RangeEnc_ShiftLow(p);
+ }
+}
+
+static void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p)
+{
+ unsigned i;
+ for (i = 0; i < 5; i++)
+ RangeEnc_ShiftLow(p);
+}
+
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+static void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol)
+{
+ size_t charMask[256 / sizeof(size_t)];
+ if (p->MinContext->NumStats != 1)
+ {
+ CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
+ UInt32 sum;
+ unsigned i;
+ if (s->Symbol == symbol)
+ {
+ RangeEnc_Encode(rc, 0, s->Freq, p->MinContext->SummFreq);
+ p->FoundState = s;
+ Ppmd7_Update1_0(p);
+ return;
+ }
+ p->PrevSuccess = 0;
+ sum = s->Freq;
+ i = p->MinContext->NumStats - 1;
+ do
+ {
+ if ((++s)->Symbol == symbol)
+ {
+ RangeEnc_Encode(rc, sum, s->Freq, p->MinContext->SummFreq);
+ p->FoundState = s;
+ Ppmd7_Update1(p);
+ return;
+ }
+ sum += s->Freq;
+ }
+ while (--i);
+
+ p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ i = p->MinContext->NumStats - 1;
+ do { MASK((--s)->Symbol) = 0; } while (--i);
+ RangeEnc_Encode(rc, sum, p->MinContext->SummFreq - sum, p->MinContext->SummFreq);
+ }
+ else
+ {
+ UInt16 *prob = Ppmd7_GetBinSumm(p);
+ CPpmd_State *s = Ppmd7Context_OneState(p->MinContext);
+ if (s->Symbol == symbol)
+ {
+ RangeEnc_EncodeBit_0(rc, *prob);
+ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+ p->FoundState = s;
+ Ppmd7_UpdateBin(p);
+ return;
+ }
+ else
+ {
+ RangeEnc_EncodeBit_1(rc, *prob);
+ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+ p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ p->PrevSuccess = 0;
+ }
+ }
+ for (;;)
+ {
+ UInt32 escFreq;
+ CPpmd_See *see;
+ CPpmd_State *s;
+ UInt32 sum;
+ unsigned i, numMasked = p->MinContext->NumStats;
+ do
+ {
+ p->OrderFall++;
+ if (!p->MinContext->Suffix)
+ return; /* EndMarker (symbol = -1) */
+ p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
+ }
+ while (p->MinContext->NumStats == numMasked);
+
+ see = Ppmd7_MakeEscFreq(p, numMasked, &escFreq);
+ s = Ppmd7_GetStats(p, p->MinContext);
+ sum = 0;
+ i = p->MinContext->NumStats;
+ do
+ {
+ int cur = s->Symbol;
+ if (cur == symbol)
+ {
+ UInt32 low = sum;
+ CPpmd_State *s1 = s;
+ do
+ {
+ sum += (s->Freq & (int)(MASK(s->Symbol)));
+ s++;
+ }
+ while (--i);
+ RangeEnc_Encode(rc, low, s1->Freq, sum + escFreq);
+ Ppmd_See_Update(see);
+ p->FoundState = s1;
+ Ppmd7_Update2(p);
+ return;
+ }
+ sum += (s->Freq & (int)(MASK(cur)));
+ MASK(cur) = 0;
+ s++;
+ }
+ while (--i);
+
+ RangeEnc_Encode(rc, sum, escFreq, sum + escFreq);
+ see->Summ = (UInt16)(see->Summ + sum + escFreq);
+ }
+}
+
+const IPpmd7 __archive_ppmd7_functions =
+{
+ &Ppmd7_Construct,
+ &Ppmd7_Alloc,
+ &Ppmd7_Free,
+ &Ppmd7_Init,
+ &Ppmd7z_RangeDec_CreateVTable,
+ &PpmdRAR_RangeDec_CreateVTable,
+ &Ppmd7z_RangeDec_Init,
+ &PpmdRAR_RangeDec_Init,
+ &Ppmd7_DecodeSymbol,
+ &Ppmd7z_RangeEnc_Init,
+ &Ppmd7z_RangeEnc_FlushData,
+ &Ppmd7_EncodeSymbol
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd7_private.h b/contrib/libs/libarchive/libarchive/archive_ppmd7_private.h
new file mode 100644
index 0000000000..71b954458c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd7_private.h
@@ -0,0 +1,119 @@
+/* Ppmd7.h -- PPMdH compression codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+/* This code supports virtual RangeDecoder and includes the implementation
+of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H.
+If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */
+
+#ifndef ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_ppmd_private.h"
+
+#define PPMD7_MIN_ORDER 2
+#define PPMD7_MAX_ORDER 64
+
+#define PPMD7_MIN_MEM_SIZE (1 << 11)
+#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFFu - 12 * 3)
+
+struct CPpmd7_Context_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd7_Context_ *
+ #else
+ UInt32
+ #endif
+ CPpmd7_Context_Ref;
+
+typedef struct CPpmd7_Context_
+{
+ UInt16 NumStats;
+ UInt16 SummFreq;
+ CPpmd_State_Ref Stats;
+ CPpmd7_Context_Ref Suffix;
+} CPpmd7_Context;
+
+#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
+
+typedef struct
+{
+ CPpmd7_Context *MinContext, *MaxContext;
+ CPpmd_State *FoundState;
+ unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag;
+ Int32 RunLength, InitRL; /* must be 32-bit at least */
+
+ UInt32 Size;
+ UInt32 GlueCount;
+ Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
+ UInt32 AlignOffset;
+
+ Byte Indx2Units[PPMD_NUM_INDEXES];
+ Byte Units2Indx[128];
+ CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
+ Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
+ CPpmd_See DummySee, See[25][16];
+ UInt16 BinSumm[128][64];
+} CPpmd7;
+
+/* ---------- Decode ---------- */
+
+typedef struct
+{
+ UInt32 (*GetThreshold)(void *p, UInt32 total);
+ void (*Decode)(void *p, UInt32 start, UInt32 size);
+ UInt32 (*DecodeBit)(void *p, UInt32 size0);
+} IPpmd7_RangeDec;
+
+typedef struct
+{
+ IPpmd7_RangeDec p;
+ UInt32 Range;
+ UInt32 Code;
+ UInt32 Low;
+ UInt32 Bottom;
+ IByteIn *Stream;
+} CPpmd7z_RangeDec;
+
+/* ---------- Encode ---------- */
+
+typedef struct
+{
+ UInt64 Low;
+ UInt32 Range;
+ Byte Cache;
+ UInt64 CacheSize;
+ IByteOut *Stream;
+} CPpmd7z_RangeEnc;
+
+typedef struct
+{
+ /* Base Functions */
+ void (*Ppmd7_Construct)(CPpmd7 *p);
+ Bool (*Ppmd7_Alloc)(CPpmd7 *p, UInt32 size);
+ void (*Ppmd7_Free)(CPpmd7 *p);
+ void (*Ppmd7_Init)(CPpmd7 *p, unsigned maxOrder);
+ #define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
+
+ /* Decode Functions */
+ void (*Ppmd7z_RangeDec_CreateVTable)(CPpmd7z_RangeDec *p);
+ void (*PpmdRAR_RangeDec_CreateVTable)(CPpmd7z_RangeDec *p);
+ Bool (*Ppmd7z_RangeDec_Init)(CPpmd7z_RangeDec *p);
+ Bool (*PpmdRAR_RangeDec_Init)(CPpmd7z_RangeDec *p);
+ #define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
+ int (*Ppmd7_DecodeSymbol)(CPpmd7 *p, IPpmd7_RangeDec *rc);
+
+ /* Encode Functions */
+ void (*Ppmd7z_RangeEnc_Init)(CPpmd7z_RangeEnc *p);
+ void (*Ppmd7z_RangeEnc_FlushData)(CPpmd7z_RangeEnc *p);
+
+ void (*Ppmd7_EncodeSymbol)(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol);
+} IPpmd7;
+
+extern const IPpmd7 __archive_ppmd7_functions;
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd8.c b/contrib/libs/libarchive/libarchive/archive_ppmd8.c
new file mode 100644
index 0000000000..d1779395da
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd8.c
@@ -0,0 +1,1287 @@
+/* Ppmd8.c -- PPMdI codec
+2016-05-21 : Igor Pavlov : Public domain
+This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include <string.h>
+
+#include "archive_ppmd8_private.h"
+
+const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
+
+#define MAX_FREQ 124
+#define UNIT_SIZE 12
+
+#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
+#define U2I(nu) (p->Units2Indx[(nu) - 1])
+#define I2U(indx) (p->Indx2Units[indx])
+
+#ifdef PPMD_32BIT
+ #define REF(ptr) (ptr)
+#else
+ #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
+#endif
+
+#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
+
+#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref))
+#define STATS(ctx) Ppmd8_GetStats(p, ctx)
+#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx)
+#define SUFFIX(ctx) CTX((ctx)->Suffix)
+
+#define kTop (1 << 24)
+#define kBot (1 << 15)
+
+typedef CPpmd8_Context * CTX_PTR;
+
+struct CPpmd8_Node_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd8_Node_ *
+ #else
+ UInt32
+ #endif
+ CPpmd8_Node_Ref;
+
+typedef struct CPpmd8_Node_
+{
+ UInt32 Stamp;
+ CPpmd8_Node_Ref Next;
+ UInt32 NU;
+} CPpmd8_Node;
+
+#ifdef PPMD_32BIT
+ #define NODE(ptr) (ptr)
+#else
+ #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs)))
+#endif
+
+#define EMPTY_NODE 0xFFFFFFFF
+
+void Ppmd8_Construct(CPpmd8 *p)
+{
+ unsigned i, k, m;
+
+ p->Base = 0;
+
+ for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
+ do { p->Units2Indx[k++] = (Byte)i; } while (--step);
+ p->Indx2Units[i] = (Byte)k;
+ }
+
+ p->NS2BSIndx[0] = (0 << 1);
+ p->NS2BSIndx[1] = (1 << 1);
+ memset(p->NS2BSIndx + 2, (2 << 1), 9);
+ memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
+
+ for (i = 0; i < 5; i++)
+ p->NS2Indx[i] = (Byte)i;
+ for (m = i, k = 1; i < 260; i++)
+ {
+ p->NS2Indx[i] = (Byte)m;
+ if (--k == 0)
+ k = (++m) - 4;
+ }
+}
+
+void Ppmd8_Free(CPpmd8 *p)
+{
+ free(p->Base);
+ p->Size = 0;
+ p->Base = 0;
+}
+
+Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size)
+{
+ if (p->Base == 0 || p->Size != size)
+ {
+ Ppmd8_Free(p);
+ p->AlignOffset =
+ #ifdef PPMD_32BIT
+ (4 - size) & 3;
+ #else
+ 4 - (size & 3);
+ #endif
+ if ((p->Base = (Byte *)malloc(p->AlignOffset + size)) == 0)
+ return False;
+ p->Size = size;
+ }
+ return True;
+}
+
+static void InsertNode(CPpmd8 *p, void *node, unsigned indx)
+{
+ ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE;
+ ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx];
+ ((CPpmd8_Node *)node)->NU = I2U(indx);
+ p->FreeList[indx] = REF(node);
+ p->Stamps[indx]++;
+}
+
+static void *RemoveNode(CPpmd8 *p, unsigned indx)
+{
+ CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]);
+ p->FreeList[indx] = node->Next;
+ p->Stamps[indx]--;
+ return node;
+}
+
+static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
+{
+ unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
+ ptr = (Byte *)ptr + U2B(I2U(newIndx));
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
+ }
+ InsertNode(p, ptr, i);
+}
+
+static void GlueFreeBlocks(CPpmd8 *p)
+{
+ CPpmd8_Node_Ref head = 0;
+ CPpmd8_Node_Ref *prev = &head;
+ unsigned i;
+
+ p->GlueCount = 1 << 13;
+ memset(p->Stamps, 0, sizeof(p->Stamps));
+
+ /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end.
+ All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */
+ if (p->LoUnit != p->HiUnit)
+ ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;
+
+ /* Glue free blocks */
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i];
+ p->FreeList[i] = 0;
+ while (next != 0)
+ {
+ CPpmd8_Node *node = NODE(next);
+ if (node->NU != 0)
+ {
+ CPpmd8_Node *node2;
+ *prev = next;
+ prev = &(node->Next);
+ while ((node2 = node + node->NU)->Stamp == EMPTY_NODE)
+ {
+ node->NU += node2->NU;
+ node2->NU = 0;
+ }
+ }
+ next = node->Next;
+ }
+ }
+ *prev = 0;
+
+ /* Fill lists of free blocks */
+ while (head != 0)
+ {
+ CPpmd8_Node *node = NODE(head);
+ unsigned nu;
+ head = node->Next;
+ nu = node->NU;
+ if (nu == 0)
+ continue;
+ for (; nu > 128; nu -= 128, node += 128)
+ InsertNode(p, node, PPMD_NUM_INDEXES - 1);
+ if (I2U(i = U2I(nu)) != nu)
+ {
+ unsigned k = I2U(--i);
+ InsertNode(p, node + k, nu - k - 1);
+ }
+ InsertNode(p, node, i);
+ }
+}
+
+static void *AllocUnitsRare(CPpmd8 *p, unsigned indx)
+{
+ unsigned i;
+ void *retVal;
+ if (p->GlueCount == 0)
+ {
+ GlueFreeBlocks(p);
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ }
+ i = indx;
+ do
+ {
+ if (++i == PPMD_NUM_INDEXES)
+ {
+ UInt32 numBytes = U2B(I2U(indx));
+ p->GlueCount--;
+ return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
+ }
+ }
+ while (p->FreeList[i] == 0);
+ retVal = RemoveNode(p, i);
+ SplitBlock(p, retVal, i, indx);
+ return retVal;
+}
+
+static void *AllocUnits(CPpmd8 *p, unsigned indx)
+{
+ UInt32 numBytes;
+ if (p->FreeList[indx] != 0)
+ return RemoveNode(p, indx);
+ numBytes = U2B(I2U(indx));
+ if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
+ {
+ void *retVal = p->LoUnit;
+ p->LoUnit += numBytes;
+ return retVal;
+ }
+ return AllocUnitsRare(p, indx);
+}
+
+#define MyMem12Cpy(dest, src, num) \
+ { UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \
+ do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); }
+
+static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
+{
+ unsigned i0 = U2I(oldNU);
+ unsigned i1 = U2I(newNU);
+ if (i0 == i1)
+ return oldPtr;
+ if (p->FreeList[i1] != 0)
+ {
+ void *ptr = RemoveNode(p, i1);
+ MyMem12Cpy(ptr, oldPtr, newNU);
+ InsertNode(p, oldPtr, i0);
+ return ptr;
+ }
+ SplitBlock(p, oldPtr, i0, i1);
+ return oldPtr;
+}
+
+static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu)
+{
+ InsertNode(p, ptr, U2I(nu));
+}
+
+static void SpecialFreeUnit(CPpmd8 *p, void *ptr)
+{
+ if ((Byte *)ptr != p->UnitsStart)
+ InsertNode(p, ptr, 0);
+ else
+ {
+ #ifdef PPMD8_FREEZE_SUPPORT
+ *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */
+ #endif
+ p->UnitsStart += UNIT_SIZE;
+ }
+}
+
+static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu)
+{
+ unsigned indx = U2I(nu);
+ void *ptr;
+ if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx])
+ return oldPtr;
+ ptr = RemoveNode(p, indx);
+ MyMem12Cpy(ptr, oldPtr, nu);
+ if ((Byte*)oldPtr != p->UnitsStart)
+ InsertNode(p, oldPtr, indx);
+ else
+ p->UnitsStart += U2B(I2U(indx));
+ return ptr;
+}
+
+static void ExpandTextArea(CPpmd8 *p)
+{
+ UInt32 count[PPMD_NUM_INDEXES];
+ unsigned i;
+ memset(count, 0, sizeof(count));
+ if (p->LoUnit != p->HiUnit)
+ ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;
+
+ {
+ CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart;
+ for (; node->Stamp == EMPTY_NODE; node += node->NU)
+ {
+ node->Stamp = 0;
+ count[U2I(node->NU)]++;
+ }
+ p->UnitsStart = (Byte *)node;
+ }
+
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ {
+ CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i];
+ while (count[i] != 0)
+ {
+ CPpmd8_Node *node = NODE(*next);
+ while (node->Stamp == 0)
+ {
+ *next = node->Next;
+ node = NODE(*next);
+ p->Stamps[i]--;
+ if (--count[i] == 0)
+ break;
+ }
+ next = &node->Next;
+ }
+ }
+}
+
+#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
+
+static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
+{
+ (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
+ (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
+}
+
+#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); }
+
+static void RestartModel(CPpmd8 *p)
+{
+ unsigned i, k, m, r;
+
+ memset(p->FreeList, 0, sizeof(p->FreeList));
+ memset(p->Stamps, 0, sizeof(p->Stamps));
+ RESET_TEXT(0);
+ p->HiUnit = p->Text + p->Size;
+ p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
+ p->GlueCount = 0;
+
+ p->OrderFall = p->MaxOrder;
+ p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
+ p->PrevSuccess = 0;
+
+ p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
+ p->MinContext->Suffix = 0;
+ p->MinContext->NumStats = 255;
+ p->MinContext->Flags = 0;
+ p->MinContext->SummFreq = 256 + 1;
+ p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
+ p->LoUnit += U2B(256 / 2);
+ p->MinContext->Stats = REF(p->FoundState);
+ for (i = 0; i < 256; i++)
+ {
+ CPpmd_State *s = &p->FoundState[i];
+ s->Symbol = (Byte)i;
+ s->Freq = 1;
+ SetSuccessor(s, 0);
+ }
+
+ for (i = m = 0; m < 25; m++)
+ {
+ while (p->NS2Indx[i] == m)
+ i++;
+ for (k = 0; k < 8; k++)
+ {
+ UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1));
+ UInt16 *dest = p->BinSumm[m] + k;
+ for (r = 0; r < 64; r += 8)
+ dest[r] = val;
+ }
+ }
+
+ for (i = m = 0; m < 24; m++)
+ {
+ while (p->NS2Indx[i + 3] == m + 3)
+ i++;
+ for (k = 0; k < 32; k++)
+ {
+ CPpmd_See *s = &p->See[m][k];
+ s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4));
+ s->Count = 7;
+ }
+ }
+}
+
+void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)
+{
+ p->MaxOrder = maxOrder;
+ p->RestoreMethod = restoreMethod;
+ RestartModel(p);
+ p->DummySee.Shift = PPMD_PERIOD_BITS;
+ p->DummySee.Summ = 0; /* unused */
+ p->DummySee.Count = 64; /* unused */
+}
+
+static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale)
+{
+ unsigned i = ctx->NumStats, escFreq, sumFreq, flags;
+ CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1);
+ ctx->Stats = REF(s);
+ #ifdef PPMD8_FREEZE_SUPPORT
+ /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */
+ scale |= (ctx->SummFreq >= ((UInt32)1 << 15));
+ #endif
+ flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40);
+ escFreq = ctx->SummFreq - s->Freq;
+ sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale));
+ do
+ {
+ escFreq -= (++s)->Freq;
+ sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale));
+ flags |= 0x08 * (s->Symbol >= 0x40);
+ }
+ while (--i);
+ ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale));
+ ctx->Flags = (Byte)flags;
+}
+
+static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
+{
+ CPpmd_State tmp = *t1;
+ *t1 = *t2;
+ *t2 = tmp;
+}
+
+static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order)
+{
+ int i;
+ unsigned tmp;
+ CPpmd_State *s;
+
+ if (!ctx->NumStats)
+ {
+ s = ONE_STATE(ctx);
+ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart)
+ {
+ if (order < p->MaxOrder)
+ SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));
+ else
+ SetSuccessor(s, 0);
+ if (SUCCESSOR(s) || order <= 9) /* O_BOUND */
+ return REF(ctx);
+ }
+ SpecialFreeUnit(p, ctx);
+ return 0;
+ }
+
+ ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1));
+
+ for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--)
+ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart)
+ {
+ CPpmd_State *s2 = STATS(ctx) + (i--);
+ SetSuccessor(s, 0);
+ SwapStates(s, s2);
+ }
+ else if (order < p->MaxOrder)
+ SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));
+ else
+ SetSuccessor(s, 0);
+
+ if (i != ctx->NumStats && order)
+ {
+ ctx->NumStats = (Byte)i;
+ s = STATS(ctx);
+ if (i < 0)
+ {
+ FreeUnits(p, s, tmp);
+ SpecialFreeUnit(p, ctx);
+ return 0;
+ }
+ if (i == 0)
+ {
+ ctx->Flags = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));
+ *ONE_STATE(ctx) = *s;
+ FreeUnits(p, s, tmp);
+ /* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */
+ ONE_STATE(ctx)->Freq = (Byte)(((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3);
+ }
+ else
+ Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i);
+ }
+ return REF(ctx);
+}
+
+#ifdef PPMD8_FREEZE_SUPPORT
+static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order)
+{
+ CPpmd_State *s;
+ if (!ctx->NumStats)
+ {
+ s = ONE_STATE(ctx);
+ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)
+ SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));
+ else
+ SetSuccessor(s, 0);
+ /* Suffix context can be removed already, since different (high-order)
+ Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */
+ if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF))
+ {
+ FreeUnits(p, ctx, 1);
+ return 0;
+ }
+ else
+ return REF(ctx);
+ }
+
+ for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--)
+ if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)
+ SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));
+ else
+ SetSuccessor(s, 0);
+
+ return REF(ctx);
+}
+#endif
+
+static UInt32 GetUsedMemory(const CPpmd8 *p)
+{
+ UInt32 v = 0;
+ unsigned i;
+ for (i = 0; i < PPMD_NUM_INDEXES; i++)
+ v += p->Stamps[i] * I2U(i);
+ return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v);
+}
+
+#ifdef PPMD8_FREEZE_SUPPORT
+ #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor)
+#else
+ #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1)
+#endif
+
+static void RestoreModel(CPpmd8 *p, CTX_PTR c1
+ #ifdef PPMD8_FREEZE_SUPPORT
+ , CTX_PTR fSuccessor
+ #endif
+ )
+{
+ CTX_PTR c;
+ CPpmd_State *s;
+ RESET_TEXT(0);
+ for (c = p->MaxContext; c != c1; c = SUFFIX(c))
+ if (--(c->NumStats) == 0)
+ {
+ s = STATS(c);
+ c->Flags = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));
+ *ONE_STATE(c) = *s;
+ SpecialFreeUnit(p, s);
+ ONE_STATE(c)->Freq = (Byte)(((unsigned)ONE_STATE(c)->Freq + 11) >> 3);
+ }
+ else
+ Refresh(p, c, (c->NumStats+3) >> 1, 0);
+
+ for (; c != p->MinContext; c = SUFFIX(c))
+ if (!c->NumStats)
+ ONE_STATE(c)->Freq = (Byte)(ONE_STATE(c)->Freq - (ONE_STATE(c)->Freq >> 1));
+ else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats)
+ Refresh(p, c, (c->NumStats + 2) >> 1, 1);
+
+ #ifdef PPMD8_FREEZE_SUPPORT
+ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ p->MaxContext = fSuccessor;
+ p->GlueCount += !(p->Stamps[1] & 1);
+ }
+ else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ while (p->MaxContext->Suffix)
+ p->MaxContext = SUFFIX(p->MaxContext);
+ RemoveBinContexts(p, p->MaxContext, 0);
+ p->RestoreMethod++;
+ p->GlueCount = 0;
+ p->OrderFall = p->MaxOrder;
+ }
+ else
+ #endif
+ if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1))
+ RestartModel(p);
+ else
+ {
+ while (p->MaxContext->Suffix)
+ p->MaxContext = SUFFIX(p->MaxContext);
+ do
+ {
+ CutOff(p, p->MaxContext, 0);
+ ExpandTextArea(p);
+ }
+ while (GetUsedMemory(p) > 3 * (p->Size >> 2));
+ p->GlueCount = 0;
+ p->OrderFall = p->MaxOrder;
+ }
+}
+
+static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c)
+{
+ CPpmd_State upState;
+ Byte flags;
+ CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
+ /* fixed over Shkarin's code. Maybe it could work without + 1 too. */
+ CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
+ unsigned numPs = 0;
+
+ if (!skip)
+ ps[numPs++] = p->FoundState;
+
+ while (c->Suffix)
+ {
+ CPpmd_Void_Ref successor;
+ CPpmd_State *s;
+ c = SUFFIX(c);
+ if (s1)
+ {
+ s = s1;
+ s1 = NULL;
+ }
+ else if (c->NumStats != 0)
+ {
+ for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq++;
+ c->SummFreq++;
+ }
+ }
+ else
+ {
+ s = ONE_STATE(c);
+ s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24)));
+ }
+ successor = SUCCESSOR(s);
+ if (successor != upBranch)
+ {
+ c = CTX(successor);
+ if (numPs == 0)
+ return c;
+ break;
+ }
+ ps[numPs++] = s;
+ }
+
+ upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch);
+ SetSuccessor(&upState, upBranch + 1);
+ flags = (Byte)(0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40));
+
+ if (c->NumStats == 0)
+ upState.Freq = ONE_STATE(c)->Freq;
+ else
+ {
+ UInt32 cf, s0;
+ CPpmd_State *s;
+ for (s = STATS(c); s->Symbol != upState.Symbol; s++);
+ cf = s->Freq - 1;
+ s0 = c->SummFreq - c->NumStats - cf;
+ upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0)));
+ }
+
+ do
+ {
+ /* Create Child */
+ CTX_PTR c1; /* = AllocContext(p); */
+ if (p->HiUnit != p->LoUnit)
+ c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
+ else if (p->FreeList[0] != 0)
+ c1 = (CTX_PTR)RemoveNode(p, 0);
+ else
+ {
+ c1 = (CTX_PTR)AllocUnitsRare(p, 0);
+ if (!c1)
+ return NULL;
+ }
+ c1->NumStats = 0;
+ c1->Flags = flags;
+ *ONE_STATE(c1) = upState;
+ c1->Suffix = REF(c);
+ SetSuccessor(ps[--numPs], REF(c1));
+ c = c1;
+ }
+ while (numPs != 0);
+
+ return c;
+}
+
+static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c)
+{
+ CPpmd_State *s = NULL;
+ CTX_PTR c1 = c;
+ CPpmd_Void_Ref upBranch = REF(p->Text);
+
+ #ifdef PPMD8_FREEZE_SUPPORT
+ /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */
+ CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
+ unsigned numPs = 0;
+ ps[numPs++] = p->FoundState;
+ #endif
+
+ SetSuccessor(p->FoundState, upBranch);
+ p->OrderFall++;
+
+ for (;;)
+ {
+ if (s1)
+ {
+ c = SUFFIX(c);
+ s = s1;
+ s1 = NULL;
+ }
+ else
+ {
+ if (!c->Suffix)
+ {
+ #ifdef PPMD8_FREEZE_SUPPORT
+ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
+ RESET_TEXT(1);
+ p->OrderFall = 1;
+ }
+ #endif
+ return c;
+ }
+ c = SUFFIX(c);
+ if (c->NumStats)
+ {
+ if ((s = STATS(c))->Symbol != p->FoundState->Symbol)
+ do { s++; } while (s->Symbol != p->FoundState->Symbol);
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq += 2;
+ c->SummFreq += 2;
+ }
+ }
+ else
+ {
+ s = ONE_STATE(c);
+ s->Freq = (Byte)(s->Freq + (s->Freq < 32));
+ }
+ }
+ if (SUCCESSOR(s))
+ break;
+ #ifdef PPMD8_FREEZE_SUPPORT
+ ps[numPs++] = s;
+ #endif
+ SetSuccessor(s, upBranch);
+ p->OrderFall++;
+ }
+
+ #ifdef PPMD8_FREEZE_SUPPORT
+ if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ c = CTX(SUCCESSOR(s));
+ do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
+ RESET_TEXT(1);
+ p->OrderFall = 1;
+ return c;
+ }
+ else
+ #endif
+ if (SUCCESSOR(s) <= upBranch)
+ {
+ CTX_PTR successor;
+ CPpmd_State *s2 = p->FoundState;
+ p->FoundState = s;
+
+ successor = CreateSuccessors(p, False, NULL, c);
+ if (successor == NULL)
+ SetSuccessor(s, 0);
+ else
+ SetSuccessor(s, REF(successor));
+ p->FoundState = s2;
+ }
+
+ if (p->OrderFall == 1 && c1 == p->MaxContext)
+ {
+ SetSuccessor(p->FoundState, SUCCESSOR(s));
+ p->Text--;
+ }
+ if (SUCCESSOR(s) == 0)
+ return NULL;
+ return CTX(SUCCESSOR(s));
+}
+
+static void UpdateModel(CPpmd8 *p)
+{
+ CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
+ CTX_PTR c;
+ unsigned s0, ns, fFreq = p->FoundState->Freq;
+ Byte flag, fSymbol = p->FoundState->Symbol;
+ CPpmd_State *s = NULL;
+
+ if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
+ {
+ c = SUFFIX(p->MinContext);
+
+ if (c->NumStats == 0)
+ {
+ s = ONE_STATE(c);
+ if (s->Freq < 32)
+ s->Freq++;
+ }
+ else
+ {
+ s = STATS(c);
+ if (s->Symbol != p->FoundState->Symbol)
+ {
+ do { s++; } while (s->Symbol != p->FoundState->Symbol);
+ if (s[0].Freq >= s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ s--;
+ }
+ }
+ if (s->Freq < MAX_FREQ - 9)
+ {
+ s->Freq += 2;
+ c->SummFreq += 2;
+ }
+ }
+ }
+
+ c = p->MaxContext;
+ if (p->OrderFall == 0 && fSuccessor)
+ {
+ CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext);
+ if (cs == 0)
+ {
+ SetSuccessor(p->FoundState, 0);
+ RESTORE_MODEL(c, CTX(fSuccessor));
+ }
+ else
+ {
+ SetSuccessor(p->FoundState, REF(cs));
+ p->MaxContext = cs;
+ }
+ return;
+ }
+
+ *p->Text++ = p->FoundState->Symbol;
+ successor = REF(p->Text);
+ if (p->Text >= p->UnitsStart)
+ {
+ RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */
+ return;
+ }
+
+ if (!fSuccessor)
+ {
+ CTX_PTR cs = ReduceOrder(p, s, p->MinContext);
+ if (cs == NULL)
+ {
+ RESTORE_MODEL(c, 0);
+ return;
+ }
+ fSuccessor = REF(cs);
+ }
+ else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart)
+ {
+ CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext);
+ if (cs == NULL)
+ {
+ RESTORE_MODEL(c, 0);
+ return;
+ }
+ fSuccessor = REF(cs);
+ }
+
+ if (--p->OrderFall == 0)
+ {
+ successor = fSuccessor;
+ p->Text -= (p->MaxContext != p->MinContext);
+ }
+ #ifdef PPMD8_FREEZE_SUPPORT
+ else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+ {
+ successor = fSuccessor;
+ RESET_TEXT(0);
+ p->OrderFall = 0;
+ }
+ #endif
+
+ s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq;
+ flag = (Byte)(0x08 * (fSymbol >= 0x40));
+
+ for (; c != p->MinContext; c = SUFFIX(c))
+ {
+ unsigned ns1;
+ UInt32 cf, sf;
+ if ((ns1 = c->NumStats) != 0)
+ {
+ if ((ns1 & 1) != 0)
+ {
+ /* Expand for one UNIT */
+ unsigned oldNU = (ns1 + 1) >> 1;
+ unsigned i = U2I(oldNU);
+ if (i != U2I(oldNU + 1))
+ {
+ void *ptr = AllocUnits(p, i + 1);
+ void *oldPtr;
+ if (!ptr)
+ {
+ RESTORE_MODEL(c, CTX(fSuccessor));
+ return;
+ }
+ oldPtr = STATS(c);
+ MyMem12Cpy(ptr, oldPtr, oldNU);
+ InsertNode(p, oldPtr, i);
+ c->Stats = STATS_REF(ptr);
+ }
+ }
+ c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns));
+ }
+ else
+ {
+ CPpmd_State *s2 = (CPpmd_State*)AllocUnits(p, 0);
+ if (!s2)
+ {
+ RESTORE_MODEL(c, CTX(fSuccessor));
+ return;
+ }
+ *s2 = *ONE_STATE(c);
+ c->Stats = REF(s2);
+ if (s2->Freq < MAX_FREQ / 4 - 1)
+ s2->Freq <<= 1;
+ else
+ s2->Freq = MAX_FREQ - 4;
+ c->SummFreq = (UInt16)(s2->Freq + p->InitEsc + (ns > 2));
+ }
+ cf = 2 * fFreq * (c->SummFreq + 6);
+ sf = (UInt32)s0 + c->SummFreq;
+ if (cf < 6 * sf)
+ {
+ cf = 1 + (cf > sf) + (cf >= 4 * sf);
+ c->SummFreq += 4;
+ }
+ else
+ {
+ cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf);
+ c->SummFreq = (UInt16)(c->SummFreq + cf);
+ }
+ {
+ CPpmd_State *s2 = STATS(c) + ns1 + 1;
+ SetSuccessor(s2, successor);
+ s2->Symbol = fSymbol;
+ s2->Freq = (Byte)cf;
+ c->Flags |= flag;
+ c->NumStats = (Byte)(ns1 + 1);
+ }
+ }
+ p->MaxContext = p->MinContext = CTX(fSuccessor);
+}
+
+static void Rescale(CPpmd8 *p)
+{
+ unsigned i, adder, sumFreq, escFreq;
+ CPpmd_State *stats = STATS(p->MinContext);
+ CPpmd_State *s = p->FoundState;
+ {
+ CPpmd_State tmp = *s;
+ for (; s != stats; s--)
+ s[0] = s[-1];
+ *s = tmp;
+ }
+ escFreq = p->MinContext->SummFreq - s->Freq;
+ s->Freq += 4;
+ adder = (p->OrderFall != 0
+ #ifdef PPMD8_FREEZE_SUPPORT
+ || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE
+ #endif
+ );
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq = s->Freq;
+
+ i = p->MinContext->NumStats;
+ do
+ {
+ escFreq -= (++s)->Freq;
+ s->Freq = (Byte)((s->Freq + adder) >> 1);
+ sumFreq += s->Freq;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ CPpmd_State *s1 = s;
+ CPpmd_State tmp = *s1;
+ do
+ s1[0] = s1[-1];
+ while (--s1 != stats && tmp.Freq > s1[-1].Freq);
+ *s1 = tmp;
+ }
+ }
+ while (--i);
+
+ if (s->Freq == 0)
+ {
+ unsigned numStats = p->MinContext->NumStats;
+ unsigned n0, n1;
+ do { i++; } while ((--s)->Freq == 0);
+ escFreq += i;
+ p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i);
+ if (p->MinContext->NumStats == 0)
+ {
+ CPpmd_State tmp = *stats;
+ tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq);
+ if (tmp.Freq > MAX_FREQ / 3)
+ tmp.Freq = MAX_FREQ / 3;
+ InsertNode(p, stats, U2I((numStats + 2) >> 1));
+ p->MinContext->Flags = (Byte)((p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40));
+ *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
+ return;
+ }
+ n0 = (numStats + 2) >> 1;
+ n1 = (p->MinContext->NumStats + 2) >> 1;
+ if (n0 != n1)
+ p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
+ p->MinContext->Flags &= ~0x08;
+ p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40);
+ i = p->MinContext->NumStats;
+ do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i);
+ }
+ p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
+ p->MinContext->Flags |= 0x4;
+ p->FoundState = STATS(p->MinContext);
+}
+
+CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)
+{
+ CPpmd_See *see;
+ if (p->MinContext->NumStats != 0xFF)
+ {
+ see = p->See[(unsigned)p->NS2Indx[(unsigned)p->MinContext->NumStats + 2] - 3] +
+ (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) +
+ 2 * (unsigned)(2 * (unsigned)p->MinContext->NumStats <
+ ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) +
+ p->MinContext->Flags;
+ {
+ unsigned r = (see->Summ >> see->Shift);
+ see->Summ = (UInt16)(see->Summ - r);
+ *escFreq = r + (r == 0);
+ }
+ }
+ else
+ {
+ see = &p->DummySee;
+ *escFreq = 1;
+ }
+ return see;
+}
+
+static void NextContext(CPpmd8 *p)
+{
+ CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
+ if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart)
+ p->MinContext = p->MaxContext = c;
+ else
+ {
+ UpdateModel(p);
+ p->MinContext = p->MaxContext;
+ }
+}
+
+void Ppmd8_Update1(CPpmd8 *p)
+{
+ CPpmd_State *s = p->FoundState;
+ s->Freq += 4;
+ p->MinContext->SummFreq += 4;
+ if (s[0].Freq > s[-1].Freq)
+ {
+ SwapStates(&s[0], &s[-1]);
+ p->FoundState = --s;
+ if (s->Freq > MAX_FREQ)
+ Rescale(p);
+ }
+ NextContext(p);
+}
+
+void Ppmd8_Update1_0(CPpmd8 *p)
+{
+ p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq);
+ p->RunLength += p->PrevSuccess;
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ NextContext(p);
+}
+
+void Ppmd8_UpdateBin(CPpmd8 *p)
+{
+ p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196));
+ p->PrevSuccess = 1;
+ p->RunLength++;
+ NextContext(p);
+}
+
+void Ppmd8_Update2(CPpmd8 *p)
+{
+ p->MinContext->SummFreq += 4;
+ if ((p->FoundState->Freq += 4) > MAX_FREQ)
+ Rescale(p);
+ p->RunLength = p->InitRL;
+ UpdateModel(p);
+ p->MinContext = p->MaxContext;
+}
+
+/* Ppmd8Dec.c -- PPMdI Decoder
+2010-04-16 : Igor Pavlov : Public domain
+This code is based on:
+ PPMd var.I (2002): Dmitry Shkarin : Public domain
+ Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
+
+Bool Ppmd8_RangeDec_Init(CPpmd8 *p)
+{
+ unsigned i;
+ p->Low = 0;
+ p->Range = 0xFFFFFFFF;
+ p->Code = 0;
+ for (i = 0; i < 4; i++)
+ p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);
+ return (p->Code < 0xFFFFFFFF);
+}
+
+static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total)
+{
+ return p->Code / (p->Range /= total);
+}
+
+static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size)
+{
+ start *= p->Range;
+ p->Low += start;
+ p->Code -= start;
+ p->Range *= size;
+
+ while ((p->Low ^ (p->Low + p->Range)) < kTop ||
+ (p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1)))
+ {
+ p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);
+ p->Range <<= 8;
+ p->Low <<= 8;
+ }
+}
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+int Ppmd8_DecodeSymbol(CPpmd8 *p)
+{
+ size_t charMask[256 / sizeof(size_t)];
+ if (p->MinContext->NumStats != 0)
+ {
+ CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext);
+ unsigned i;
+ UInt32 count, hiCnt;
+ if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
+ {
+ Byte symbol;
+ RangeDec_Decode(p, 0, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd8_Update1_0(p);
+ return symbol;
+ }
+ p->PrevSuccess = 0;
+ i = p->MinContext->NumStats;
+ do
+ {
+ if ((hiCnt += (++s)->Freq) > count)
+ {
+ Byte symbol;
+ RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd8_Update1(p);
+ return symbol;
+ }
+ }
+ while (--i);
+ if (count >= p->MinContext->SummFreq)
+ return -2;
+ RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt);
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(s->Symbol) = 0;
+ i = p->MinContext->NumStats;
+ do { MASK((--s)->Symbol) = 0; } while (--i);
+ }
+ else
+ {
+ UInt16 *prob = Ppmd8_GetBinSumm(p);
+ if (((p->Code / (p->Range >>= 14)) < *prob))
+ {
+ Byte symbol;
+ RangeDec_Decode(p, 0, *prob);
+ *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+ symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol;
+ Ppmd8_UpdateBin(p);
+ return symbol;
+ }
+ RangeDec_Decode(p, *prob, (1 << 14) - *prob);
+ *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+ p->InitEsc = PPMD8_kExpEscape[*prob >> 10];
+ PPMD_SetAllBitsIn256Bytes(charMask);
+ MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0;
+ p->PrevSuccess = 0;
+ }
+ for (;;)
+ {
+ CPpmd_State *ps[256], *s;
+ UInt32 freqSum, count, hiCnt;
+ CPpmd_See *see;
+ unsigned i, num, numMasked = p->MinContext->NumStats;
+ do
+ {
+ p->OrderFall++;
+ if (!p->MinContext->Suffix)
+ return -1;
+ p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix);
+ }
+ while (p->MinContext->NumStats == numMasked);
+ hiCnt = 0;
+ s = Ppmd8_GetStats(p, p->MinContext);
+ i = 0;
+ num = p->MinContext->NumStats - numMasked;
+ do
+ {
+ int k = (int)(MASK(s->Symbol));
+ hiCnt += (s->Freq & k);
+ ps[i] = s++;
+ i -= k;
+ }
+ while (i != num);
+
+ see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum);
+ freqSum += hiCnt;
+ count = RangeDec_GetThreshold(p, freqSum);
+
+ if (count < hiCnt)
+ {
+ Byte symbol;
+ CPpmd_State **pps = ps;
+ for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
+ s = *pps;
+ RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);
+ Ppmd_See_Update(see);
+ p->FoundState = s;
+ symbol = s->Symbol;
+ Ppmd8_Update2(p);
+ return symbol;
+ }
+ if (count >= freqSum)
+ return -2;
+ RangeDec_Decode(p, hiCnt, freqSum - hiCnt);
+ see->Summ = (UInt16)(see->Summ + freqSum);
+ do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
+ }
+}
+
+/* H->I changes:
+ NS2Indx
+ GlewCount, and Glue method
+ BinSum
+ See / EscFreq
+ CreateSuccessors updates more suffix contexts
+ UpdateModel consts.
+ PrevSuccess Update
+*/
+
+const IPpmd8 __archive_ppmd8_functions =
+{
+ &Ppmd8_Construct,
+ &Ppmd8_Alloc,
+ &Ppmd8_Free,
+ &Ppmd8_Init,
+ &Ppmd8_RangeDec_Init,
+ &Ppmd8_DecodeSymbol,
+};
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd8_private.h b/contrib/libs/libarchive/libarchive/archive_ppmd8_private.h
new file mode 100644
index 0000000000..454b75f41f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd8_private.h
@@ -0,0 +1,148 @@
+/* Ppmd8.h -- PPMdI codec
+2011-01-27 : Igor Pavlov : Public domain
+This code is based on:
+ PPMd var.I (2002): Dmitry Shkarin : Public domain
+ Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
+
+#ifndef ARCHIVE_PPMD8_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD8_PRIVATE_H_INCLUDED
+
+#include "archive_ppmd_private.h"
+
+#define PPMD8_MIN_ORDER 2
+#define PPMD8_MAX_ORDER 16
+
+struct CPpmd8_Context_;
+
+typedef
+ #ifdef PPMD_32BIT
+ struct CPpmd8_Context_ *
+ #else
+ UInt32
+ #endif
+ CPpmd8_Context_Ref;
+
+#pragma pack(push, 1)
+
+typedef struct CPpmd8_Context_
+{
+ Byte NumStats;
+ Byte Flags;
+ UInt16 SummFreq;
+ CPpmd_State_Ref Stats;
+ CPpmd8_Context_Ref Suffix;
+} CPpmd8_Context;
+
+#pragma pack(pop)
+
+#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
+
+/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed
+ code is not compatible with original code for some files compressed
+ in FREEZE mode. So we disable FREEZE mode support. */
+
+enum
+{
+ PPMD8_RESTORE_METHOD_RESTART,
+ PPMD8_RESTORE_METHOD_CUT_OFF
+ #ifdef PPMD8_FREEZE_SUPPORT
+ , PPMD8_RESTORE_METHOD_FREEZE
+ #endif
+};
+
+typedef struct
+{
+ CPpmd8_Context *MinContext, *MaxContext;
+ CPpmd_State *FoundState;
+ unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder;
+ Int32 RunLength, InitRL; /* must be 32-bit at least */
+
+ UInt32 Size;
+ UInt32 GlueCount;
+ Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
+ UInt32 AlignOffset;
+ unsigned RestoreMethod;
+
+ /* Range Coder */
+ UInt32 Range;
+ UInt32 Code;
+ UInt32 Low;
+ union
+ {
+ IByteIn *In;
+ IByteOut *Out;
+ } Stream;
+
+ Byte Indx2Units[PPMD_NUM_INDEXES];
+ Byte Units2Indx[128];
+ CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
+ UInt32 Stamps[PPMD_NUM_INDEXES];
+
+ Byte NS2BSIndx[256], NS2Indx[260];
+ CPpmd_See DummySee, See[24][32];
+ UInt16 BinSumm[25][64];
+} CPpmd8;
+
+void Ppmd8_Construct(CPpmd8 *p);
+Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size);
+void Ppmd8_Free(CPpmd8 *p);
+void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod);
+#define Ppmd8_WasAllocated(p) ((p)->Base != NULL)
+
+
+/* ---------- Internal Functions ---------- */
+
+extern const Byte PPMD8_kExpEscape[16];
+
+#ifdef PPMD_32BIT
+ #define Ppmd8_GetPtr(p, ptr) (ptr)
+ #define Ppmd8_GetContext(p, ptr) (ptr)
+ #define Ppmd8_GetStats(p, ctx) ((ctx)->Stats)
+#else
+ #define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
+ #define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs)))
+ #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats)))
+#endif
+
+void Ppmd8_Update1(CPpmd8 *p);
+void Ppmd8_Update1_0(CPpmd8 *p);
+void Ppmd8_Update2(CPpmd8 *p);
+void Ppmd8_UpdateBin(CPpmd8 *p);
+
+#define Ppmd8_GetBinSumm(p) \
+ &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \
+ p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \
+ p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)]
+
+CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale);
+
+
+/* ---------- Decode ---------- */
+
+Bool Ppmd8_RangeDec_Init(CPpmd8 *p);
+#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
+int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */
+
+/* ---------- Encode ---------- */
+
+#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; }
+void Ppmd8_RangeEnc_FlushData(CPpmd8 *p);
+void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */
+
+typedef struct
+{
+ /* Base Functions */
+ void (*Ppmd8_Construct)(CPpmd8 *p);
+ Bool (*Ppmd8_Alloc)(CPpmd8 *p, UInt32 size);
+ void (*Ppmd8_Free)(CPpmd8 *p);
+ void (*Ppmd8_Init)(CPpmd8 *p, unsigned max_order, unsigned restore_method);
+ #define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
+
+ /* Decode Functions */
+ int (*Ppmd8_RangeDec_Init)(CPpmd8 *p);
+ int (*Ppmd8_DecodeSymbol)(CPpmd8 *p);
+} IPpmd8;
+
+extern const IPpmd8 __archive_ppmd8_functions;
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_ppmd_private.h b/contrib/libs/libarchive/libarchive/archive_ppmd_private.h
new file mode 100644
index 0000000000..582803e5fd
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_ppmd_private.h
@@ -0,0 +1,151 @@
+/* Ppmd.h -- PPMD codec common code
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#ifndef ARCHIVE_PPMD_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <stddef.h>
+
+#include "archive_read_private.h"
+
+/*** Begin defined in Types.h ***/
+
+#if !defined(ZCONF_H)
+typedef unsigned char Byte;
+#endif
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef long Int32;
+typedef unsigned long UInt32;
+#else
+typedef int Int32;
+typedef unsigned int UInt32;
+#endif
+
+#ifdef _SZ_NO_INT_64
+
+/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
+ NOTES: Some code will work incorrectly in that case! */
+
+typedef long Int64;
+typedef unsigned long UInt64;
+
+#else
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#define UINT64_CONST(n) n
+#else
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#define UINT64_CONST(n) n ## ULL
+#endif
+
+#endif
+
+typedef int Bool;
+#define True 1
+#define False 0
+
+/* The following interfaces use first parameter as pointer to structure */
+
+typedef struct
+{
+ struct archive_read *a;
+ Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */
+} IByteIn;
+
+typedef struct
+{
+ struct archive_write *a;
+ void (*Write)(void *p, Byte b);
+} IByteOut;
+
+/*** End defined in Types.h ***/
+/*** Begin defined in CpuArch.h ***/
+
+#if defined(_M_IX86) || defined(__i386__)
+#define MY_CPU_X86
+#endif
+
+#if defined(MY_CPU_X86) || defined(_M_ARM)
+#define MY_CPU_32BIT
+#endif
+
+#ifdef MY_CPU_32BIT
+#define PPMD_32BIT
+#endif
+
+/*** End defined in CpuArch.h ***/
+
+#define PPMD_INT_BITS 7
+#define PPMD_PERIOD_BITS 7
+#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS))
+
+#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift))
+#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2)
+#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob))
+#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob))
+
+#define PPMD_N1 4
+#define PPMD_N2 4
+#define PPMD_N3 4
+#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4)
+#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4)
+
+/* SEE-contexts for PPM-contexts with masked symbols */
+typedef struct
+{
+ UInt16 Summ; /* Freq */
+ Byte Shift; /* Speed of Freq change; low Shift is for fast change */
+ Byte Count; /* Count to next change of Shift */
+} CPpmd_See;
+
+#define Ppmd_See_Update(p) if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \
+ { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); }
+
+typedef struct
+{
+ Byte Symbol;
+ Byte Freq;
+ UInt16 SuccessorLow;
+ UInt16 SuccessorHigh;
+} CPpmd_State;
+
+typedef
+ #ifdef PPMD_32BIT
+ CPpmd_State *
+ #else
+ UInt32
+ #endif
+ CPpmd_State_Ref;
+
+typedef
+ #ifdef PPMD_32BIT
+ void *
+ #else
+ UInt32
+ #endif
+ CPpmd_Void_Ref;
+
+typedef
+ #ifdef PPMD_32BIT
+ Byte *
+ #else
+ UInt32
+ #endif
+ CPpmd_Byte_Ref;
+
+#define PPMD_SetAllBitsIn256Bytes(p) \
+ { unsigned j; for (j = 0; j < 256 / sizeof(p[0]); j += 8) { \
+ p[j+7] = p[j+6] = p[j+5] = p[j+4] = p[j+3] = p[j+2] = p[j+1] = p[j+0] = ~(size_t)0; }}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_private.h b/contrib/libs/libarchive/libarchive/archive_private.h
new file mode 100644
index 0000000000..b2a2cda250
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_private.h
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_private.h 201098 2009-12-28 02:58:14Z kientzle $
+ */
+
+#ifndef ARCHIVE_PRIVATE_H_INCLUDED
+#define ARCHIVE_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#if HAVE_ICONV_H
+#include <iconv.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+
+#if defined(__GNUC__) && (__GNUC__ > 2 || \
+ (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))
+#define __LA_DEAD __attribute__((__noreturn__))
+#else
+#define __LA_DEAD
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 2 || \
+ (__GNUC__ == 2 && __GNUC_MINOR__ >= 7))
+#define __LA_UNUSED __attribute__((__unused__))
+#else
+#define __LA_UNUSED
+#endif
+
+#define ARCHIVE_WRITE_MAGIC (0xb0c5c0deU)
+#define ARCHIVE_READ_MAGIC (0xdeb0c5U)
+#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
+#define ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
+#define ARCHIVE_MATCH_MAGIC (0xcad11c9U)
+
+#define ARCHIVE_STATE_NEW 1U
+#define ARCHIVE_STATE_HEADER 2U
+#define ARCHIVE_STATE_DATA 4U
+#define ARCHIVE_STATE_EOF 0x10U
+#define ARCHIVE_STATE_CLOSED 0x20U
+#define ARCHIVE_STATE_FATAL 0x8000U
+#define ARCHIVE_STATE_ANY (0xFFFFU & ~ARCHIVE_STATE_FATAL)
+
+struct archive_vtable {
+ int (*archive_close)(struct archive *);
+ int (*archive_free)(struct archive *);
+ int (*archive_write_header)(struct archive *,
+ struct archive_entry *);
+ int (*archive_write_finish_entry)(struct archive *);
+ ssize_t (*archive_write_data)(struct archive *,
+ const void *, size_t);
+ ssize_t (*archive_write_data_block)(struct archive *,
+ const void *, size_t, int64_t);
+
+ int (*archive_read_next_header)(struct archive *,
+ struct archive_entry **);
+ int (*archive_read_next_header2)(struct archive *,
+ struct archive_entry *);
+ int (*archive_read_data_block)(struct archive *,
+ const void **, size_t *, int64_t *);
+
+ int (*archive_filter_count)(struct archive *);
+ int64_t (*archive_filter_bytes)(struct archive *, int);
+ int (*archive_filter_code)(struct archive *, int);
+ const char * (*archive_filter_name)(struct archive *, int);
+};
+
+struct archive_string_conv;
+
+struct archive {
+ /*
+ * The magic/state values are used to sanity-check the
+ * client's usage. If an API function is called at a
+ * ridiculous time, or the client passes us an invalid
+ * pointer, these values allow me to catch that.
+ */
+ unsigned int magic;
+ unsigned int state;
+
+ /*
+ * Some public API functions depend on the "real" type of the
+ * archive object.
+ */
+ const struct archive_vtable *vtable;
+
+ int archive_format;
+ const char *archive_format_name;
+
+ /* Number of file entries processed. */
+ int file_count;
+
+ int archive_error_number;
+ const char *error;
+ struct archive_string error_string;
+
+ char *current_code;
+ unsigned current_codepage; /* Current ACP(ANSI CodePage). */
+ unsigned current_oemcp; /* Current OEMCP(OEM CodePage). */
+ struct archive_string_conv *sconv;
+
+ /*
+ * Used by archive_read_data() to track blocks and copy
+ * data to client buffers, filling gaps with zero bytes.
+ */
+ const char *read_data_block;
+ int64_t read_data_offset;
+ int64_t read_data_output_offset;
+ size_t read_data_remaining;
+
+ /*
+ * Used by formats/filters to determine the amount of data
+ * requested from a call to archive_read_data(). This is only
+ * useful when the format/filter has seek support.
+ */
+ char read_data_is_posix_read;
+ size_t read_data_requested;
+};
+
+/* Check magic value and state; return(ARCHIVE_FATAL) if it isn't valid. */
+int __archive_check_magic(struct archive *, unsigned int magic,
+ unsigned int state, const char *func);
+#define archive_check_magic(a, expected_magic, allowed_states, function_name) \
+ do { \
+ int magic_test = __archive_check_magic((a), (expected_magic), \
+ (allowed_states), (function_name)); \
+ if (magic_test == ARCHIVE_FATAL) \
+ return ARCHIVE_FATAL; \
+ } while (0)
+
+void __archive_errx(int retvalue, const char *msg) __LA_DEAD;
+
+void __archive_ensure_cloexec_flag(int fd);
+int __archive_mktemp(const char *tmpdir);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+int __archive_mkstemp(wchar_t *template);
+#else
+int __archive_mkstemp(char *template);
+#endif
+
+int __archive_clean(struct archive *);
+
+void __archive_reset_read_data(struct archive *);
+
+#define err_combine(a,b) ((a) < (b) ? (a) : (b))
+
+#if defined(__BORLANDC__) || (defined(_MSC_VER) && _MSC_VER <= 1300)
+# define ARCHIVE_LITERAL_LL(x) x##i64
+# define ARCHIVE_LITERAL_ULL(x) x##ui64
+#else
+# define ARCHIVE_LITERAL_LL(x) x##ll
+# define ARCHIVE_LITERAL_ULL(x) x##ull
+#endif
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_random.c b/contrib/libs/libarchive/libarchive/archive_random.c
new file mode 100644
index 0000000000..a410dc089f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_random.c
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+
+#ifdef HAVE_FCNTL
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+static void la_arc4random_buf(void *, size_t);
+
+#endif /* HAVE_ARC4RANDOM_BUF */
+
+#include "archive.h"
+#include "archive_random_private.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
+#include <wincrypt.h>
+#endif
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+/*
+ * Random number generator function.
+ * This simply calls arc4random_buf function if the platform provides it.
+ */
+
+int
+archive_random(void *buf, size_t nbytes)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ NTSTATUS status;
+ BCRYPT_ALG_HANDLE hAlg;
+
+ status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return ARCHIVE_FAILED;
+ status = BCryptGenRandom(hAlg, buf, (ULONG)nbytes, 0);
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+ if (!BCRYPT_SUCCESS(status))
+ return ARCHIVE_FAILED;
+
+ return ARCHIVE_OK;
+# else
+ HCRYPTPROV hProv;
+ BOOL success;
+
+ success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT);
+ if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) {
+ success = CryptAcquireContext(&hProv, NULL, NULL,
+ PROV_RSA_FULL, CRYPT_NEWKEYSET);
+ }
+ if (success) {
+ success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf);
+ CryptReleaseContext(hProv, 0);
+ if (success)
+ return ARCHIVE_OK;
+ }
+ /* TODO: Does this case really happen? */
+ return ARCHIVE_FAILED;
+# endif
+#elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+ la_arc4random_buf(buf, nbytes);
+ return ARCHIVE_OK;
+#else
+ arc4random_buf(buf, nbytes);
+ return ARCHIVE_OK;
+#endif
+}
+
+#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+
+/* $OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $ */
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Arc4 random number generator for OpenBSD.
+ *
+ * This code is derived from section 17.1 of Applied Cryptography,
+ * second edition, which describes a stream cipher allegedly
+ * compatible with RSA Labs "RC4" cipher (the actual description of
+ * which is a trade secret). The same algorithm is used as a stream
+ * cipher called "arcfour" in Tatu Ylonen's ssh package.
+ *
+ * RC4 is a registered trademark of RSA Laboratories.
+ */
+
+#ifdef __GNUC__
+#define inline __inline
+#else /* !__GNUC__ */
+#define inline
+#endif /* !__GNUC__ */
+
+struct arc4_stream {
+ uint8_t i;
+ uint8_t j;
+ uint8_t s[256];
+};
+
+#define RANDOMDEV "/dev/urandom"
+#define KEYSIZE 128
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
+#define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx);
+#define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx);
+#else
+#define _ARC4_LOCK()
+#define _ARC4_UNLOCK()
+#endif
+
+static int rs_initialized;
+static struct arc4_stream rs;
+static pid_t arc4_stir_pid;
+static int arc4_count;
+
+static inline uint8_t arc4_getbyte(void);
+static void arc4_stir(void);
+
+static inline void
+arc4_init(void)
+{
+ int n;
+
+ for (n = 0; n < 256; n++)
+ rs.s[n] = n;
+ rs.i = 0;
+ rs.j = 0;
+}
+
+static inline void
+arc4_addrandom(uint8_t *dat, int datlen)
+{
+ int n;
+ uint8_t si;
+
+ rs.i--;
+ for (n = 0; n < 256; n++) {
+ rs.i = (rs.i + 1);
+ si = rs.s[rs.i];
+ rs.j = (rs.j + si + dat[n % datlen]);
+ rs.s[rs.i] = rs.s[rs.j];
+ rs.s[rs.j] = si;
+ }
+ rs.j = rs.i;
+}
+
+static void
+arc4_stir(void)
+{
+ int done, fd, i;
+ struct {
+ struct timeval tv;
+ pid_t pid;
+ uint8_t rnd[KEYSIZE];
+ } rdat;
+
+ if (!rs_initialized) {
+ arc4_init();
+ rs_initialized = 1;
+ }
+ done = 0;
+ fd = open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0);
+ if (fd >= 0) {
+ if (read(fd, &rdat, KEYSIZE) == KEYSIZE)
+ done = 1;
+ (void)close(fd);
+ }
+ if (!done) {
+ (void)gettimeofday(&rdat.tv, NULL);
+ rdat.pid = getpid();
+ /* We'll just take whatever was on the stack too... */
+ }
+
+ arc4_addrandom((uint8_t *)&rdat, KEYSIZE);
+
+ /*
+ * Discard early keystream, as per recommendations in:
+ * "(Not So) Random Shuffles of RC4" by Ilya Mironov.
+ * As per the Network Operations Division, cryptographic requirements
+ * published on wikileaks on March 2017.
+ */
+
+ for (i = 0; i < 3072; i++)
+ (void)arc4_getbyte();
+ arc4_count = 1600000;
+}
+
+static void
+arc4_stir_if_needed(void)
+{
+ pid_t pid = getpid();
+
+ if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) {
+ arc4_stir_pid = pid;
+ arc4_stir();
+ }
+}
+
+static inline uint8_t
+arc4_getbyte(void)
+{
+ uint8_t si, sj;
+
+ rs.i = (rs.i + 1);
+ si = rs.s[rs.i];
+ rs.j = (rs.j + si);
+ sj = rs.s[rs.j];
+ rs.s[rs.i] = sj;
+ rs.s[rs.j] = si;
+ return (rs.s[(si + sj) & 0xff]);
+}
+
+static void
+la_arc4random_buf(void *_buf, size_t n)
+{
+ uint8_t *buf = (uint8_t *)_buf;
+ _ARC4_LOCK();
+ arc4_stir_if_needed();
+ while (n--) {
+ if (--arc4_count <= 0)
+ arc4_stir();
+ buf[n] = arc4_getbyte();
+ }
+ _ARC4_UNLOCK();
+}
+
+#endif /* !HAVE_ARC4RANDOM_BUF */
diff --git a/contrib/libs/libarchive/libarchive/archive_random_private.h b/contrib/libs/libarchive/libarchive/archive_random_private.h
new file mode 100644
index 0000000000..08b91b3b7a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_random_private.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
+#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/* Random number generator. */
+int archive_random(void *buf, size_t nbytes);
+
+#endif /* ARCHIVE_RANDOM_PRIVATE_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_rb.c b/contrib/libs/libarchive/libarchive/archive_rb.c
new file mode 100644
index 0000000000..cf58ac3354
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_rb.c
@@ -0,0 +1,709 @@
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas <matt@3am-software.com>.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ *
+ * Based on: NetBSD: rb.c,v 1.6 2010/04/30 13:58:09 joerg Exp
+ */
+
+#include "archive_platform.h"
+
+#include <stddef.h>
+
+#include "archive_rb.h"
+
+/* Keep in sync with archive_rb.h */
+#define RB_DIR_LEFT 0
+#define RB_DIR_RIGHT 1
+#define RB_DIR_OTHER 1
+#define rb_left rb_nodes[RB_DIR_LEFT]
+#define rb_right rb_nodes[RB_DIR_RIGHT]
+
+#define RB_FLAG_POSITION 0x2
+#define RB_FLAG_RED 0x1
+#define RB_FLAG_MASK (RB_FLAG_POSITION|RB_FLAG_RED)
+#define RB_FATHER(rb) \
+ ((struct archive_rb_node *)((rb)->rb_info & ~RB_FLAG_MASK))
+#define RB_SET_FATHER(rb, father) \
+ ((void)((rb)->rb_info = (uintptr_t)(father)|((rb)->rb_info & RB_FLAG_MASK)))
+
+#define RB_SENTINEL_P(rb) ((rb) == NULL)
+#define RB_LEFT_SENTINEL_P(rb) RB_SENTINEL_P((rb)->rb_left)
+#define RB_RIGHT_SENTINEL_P(rb) RB_SENTINEL_P((rb)->rb_right)
+#define RB_FATHER_SENTINEL_P(rb) RB_SENTINEL_P(RB_FATHER((rb)))
+#define RB_CHILDLESS_P(rb) \
+ (RB_SENTINEL_P(rb) || (RB_LEFT_SENTINEL_P(rb) && RB_RIGHT_SENTINEL_P(rb)))
+#define RB_TWOCHILDREN_P(rb) \
+ (!RB_SENTINEL_P(rb) && !RB_LEFT_SENTINEL_P(rb) && !RB_RIGHT_SENTINEL_P(rb))
+
+#define RB_POSITION(rb) \
+ (((rb)->rb_info & RB_FLAG_POSITION) ? RB_DIR_RIGHT : RB_DIR_LEFT)
+#define RB_RIGHT_P(rb) (RB_POSITION(rb) == RB_DIR_RIGHT)
+#define RB_LEFT_P(rb) (RB_POSITION(rb) == RB_DIR_LEFT)
+#define RB_RED_P(rb) (!RB_SENTINEL_P(rb) && ((rb)->rb_info & RB_FLAG_RED) != 0)
+#define RB_BLACK_P(rb) (RB_SENTINEL_P(rb) || ((rb)->rb_info & RB_FLAG_RED) == 0)
+#define RB_MARK_RED(rb) ((void)((rb)->rb_info |= RB_FLAG_RED))
+#define RB_MARK_BLACK(rb) ((void)((rb)->rb_info &= ~RB_FLAG_RED))
+#define RB_INVERT_COLOR(rb) ((void)((rb)->rb_info ^= RB_FLAG_RED))
+#define RB_ROOT_P(rbt, rb) ((rbt)->rbt_root == (rb))
+#define RB_SET_POSITION(rb, position) \
+ ((void)((position) ? ((rb)->rb_info |= RB_FLAG_POSITION) : \
+ ((rb)->rb_info &= ~RB_FLAG_POSITION)))
+#define RB_ZERO_PROPERTIES(rb) ((void)((rb)->rb_info &= ~RB_FLAG_MASK))
+#define RB_COPY_PROPERTIES(dst, src) \
+ ((void)((dst)->rb_info ^= ((dst)->rb_info ^ (src)->rb_info) & RB_FLAG_MASK))
+#define RB_SWAP_PROPERTIES(a, b) do { \
+ uintptr_t xorinfo = ((a)->rb_info ^ (b)->rb_info) & RB_FLAG_MASK; \
+ (a)->rb_info ^= xorinfo; \
+ (b)->rb_info ^= xorinfo; \
+ } while (/*CONSTCOND*/ 0)
+
+static void __archive_rb_tree_insert_rebalance(struct archive_rb_tree *,
+ struct archive_rb_node *);
+static void __archive_rb_tree_removal_rebalance(struct archive_rb_tree *,
+ struct archive_rb_node *, unsigned int);
+
+#define RB_SENTINEL_NODE NULL
+
+#define T 1
+#define F 0
+
+void
+__archive_rb_tree_init(struct archive_rb_tree *rbt,
+ const struct archive_rb_tree_ops *ops)
+{
+ rbt->rbt_ops = ops;
+ *((struct archive_rb_node **)&rbt->rbt_root) = RB_SENTINEL_NODE;
+}
+
+struct archive_rb_node *
+__archive_rb_tree_find_node(struct archive_rb_tree *rbt, const void *key)
+{
+ archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+ struct archive_rb_node *parent = rbt->rbt_root;
+
+ while (!RB_SENTINEL_P(parent)) {
+ const signed int diff = (*compare_key)(parent, key);
+ if (diff == 0)
+ return parent;
+ parent = parent->rb_nodes[diff > 0];
+ }
+
+ return NULL;
+}
+
+struct archive_rb_node *
+__archive_rb_tree_find_node_geq(struct archive_rb_tree *rbt, const void *key)
+{
+ archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+ struct archive_rb_node *parent = rbt->rbt_root;
+ struct archive_rb_node *last = NULL;
+
+ while (!RB_SENTINEL_P(parent)) {
+ const signed int diff = (*compare_key)(parent, key);
+ if (diff == 0)
+ return parent;
+ if (diff < 0)
+ last = parent;
+ parent = parent->rb_nodes[diff > 0];
+ }
+
+ return last;
+}
+
+struct archive_rb_node *
+__archive_rb_tree_find_node_leq(struct archive_rb_tree *rbt, const void *key)
+{
+ archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+ struct archive_rb_node *parent = rbt->rbt_root;
+ struct archive_rb_node *last = NULL;
+
+ while (!RB_SENTINEL_P(parent)) {
+ const signed int diff = (*compare_key)(parent, key);
+ if (diff == 0)
+ return parent;
+ if (diff > 0)
+ last = parent;
+ parent = parent->rb_nodes[diff > 0];
+ }
+
+ return last;
+}
+
+int
+__archive_rb_tree_insert_node(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self)
+{
+ archive_rbto_compare_nodes_fn compare_nodes = rbt->rbt_ops->rbto_compare_nodes;
+ struct archive_rb_node *parent, *tmp;
+ unsigned int position;
+ int rebalance;
+
+ tmp = rbt->rbt_root;
+ /*
+ * This is a hack. Because rbt->rbt_root is just a
+ * struct archive_rb_node *, just like rb_node->rb_nodes[RB_DIR_LEFT],
+ * we can use this fact to avoid a lot of tests for root and know
+ * that even at root, updating
+ * RB_FATHER(rb_node)->rb_nodes[RB_POSITION(rb_node)] will
+ * update rbt->rbt_root.
+ */
+ parent = (struct archive_rb_node *)(void *)&rbt->rbt_root;
+ position = RB_DIR_LEFT;
+
+ /*
+ * Find out where to place this new leaf.
+ */
+ while (!RB_SENTINEL_P(tmp)) {
+ const signed int diff = (*compare_nodes)(tmp, self);
+ if (diff == 0) {
+ /*
+ * Node already exists; don't insert.
+ */
+ return F;
+ }
+ parent = tmp;
+ position = (diff > 0);
+ tmp = parent->rb_nodes[position];
+ }
+
+ /*
+ * Initialize the node and insert as a leaf into the tree.
+ */
+ RB_SET_FATHER(self, parent);
+ RB_SET_POSITION(self, position);
+ if (parent == (struct archive_rb_node *)(void *)&rbt->rbt_root) {
+ RB_MARK_BLACK(self); /* root is always black */
+ rebalance = F;
+ } else {
+ /*
+ * All new nodes are colored red. We only need to rebalance
+ * if our parent is also red.
+ */
+ RB_MARK_RED(self);
+ rebalance = RB_RED_P(parent);
+ }
+ self->rb_left = parent->rb_nodes[position];
+ self->rb_right = parent->rb_nodes[position];
+ parent->rb_nodes[position] = self;
+
+ /*
+ * Rebalance tree after insertion
+ */
+ if (rebalance)
+ __archive_rb_tree_insert_rebalance(rbt, self);
+
+ return T;
+}
+
+/*
+ * Swap the location and colors of 'self' and its child @ which. The child
+ * can not be a sentinel node. This is our rotation function. However,
+ * since it preserves coloring, it great simplifies both insertion and
+ * removal since rotation almost always involves the exchanging of colors
+ * as a separate step.
+ */
+/*ARGSUSED*/
+static void
+__archive_rb_tree_reparent_nodes(
+ struct archive_rb_node *old_father, const unsigned int which)
+{
+ const unsigned int other = which ^ RB_DIR_OTHER;
+ struct archive_rb_node * const grandpa = RB_FATHER(old_father);
+ struct archive_rb_node * const old_child = old_father->rb_nodes[which];
+ struct archive_rb_node * const new_father = old_child;
+ struct archive_rb_node * const new_child = old_father;
+
+ if (new_father == NULL)
+ return;
+ /*
+ * Exchange descendant linkages.
+ */
+ grandpa->rb_nodes[RB_POSITION(old_father)] = new_father;
+ new_child->rb_nodes[which] = old_child->rb_nodes[other];
+ new_father->rb_nodes[other] = new_child;
+
+ /*
+ * Update ancestor linkages
+ */
+ RB_SET_FATHER(new_father, grandpa);
+ RB_SET_FATHER(new_child, new_father);
+
+ /*
+ * Exchange properties between new_father and new_child. The only
+ * change is that new_child's position is now on the other side.
+ */
+ RB_SWAP_PROPERTIES(new_father, new_child);
+ RB_SET_POSITION(new_child, other);
+
+ /*
+ * Make sure to reparent the new child to ourself.
+ */
+ if (!RB_SENTINEL_P(new_child->rb_nodes[which])) {
+ RB_SET_FATHER(new_child->rb_nodes[which], new_child);
+ RB_SET_POSITION(new_child->rb_nodes[which], which);
+ }
+
+}
+
+static void
+__archive_rb_tree_insert_rebalance(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self)
+{
+ struct archive_rb_node * father = RB_FATHER(self);
+ struct archive_rb_node * grandpa;
+ struct archive_rb_node * uncle;
+ unsigned int which;
+ unsigned int other;
+
+ for (;;) {
+ /*
+ * We are red and our parent is red, therefore we must have a
+ * grandfather and he must be black.
+ */
+ grandpa = RB_FATHER(father);
+ which = (father == grandpa->rb_right);
+ other = which ^ RB_DIR_OTHER;
+ uncle = grandpa->rb_nodes[other];
+
+ if (RB_BLACK_P(uncle))
+ break;
+
+ /*
+ * Case 1: our uncle is red
+ * Simply invert the colors of our parent and
+ * uncle and make our grandparent red. And
+ * then solve the problem up at his level.
+ */
+ RB_MARK_BLACK(uncle);
+ RB_MARK_BLACK(father);
+ if (RB_ROOT_P(rbt, grandpa)) {
+ /*
+ * If our grandpa is root, don't bother
+ * setting him to red, just return.
+ */
+ return;
+ }
+ RB_MARK_RED(grandpa);
+ self = grandpa;
+ father = RB_FATHER(self);
+ if (RB_BLACK_P(father)) {
+ /*
+ * If our great-grandpa is black, we're done.
+ */
+ return;
+ }
+ }
+
+ /*
+ * Case 2&3: our uncle is black.
+ */
+ if (self == father->rb_nodes[other]) {
+ /*
+ * Case 2: we are on the same side as our uncle
+ * Swap ourselves with our parent so this case
+ * becomes case 3. Basically our parent becomes our
+ * child.
+ */
+ __archive_rb_tree_reparent_nodes(father, other);
+ }
+ /*
+ * Case 3: we are opposite a child of a black uncle.
+ * Swap our parent and grandparent. Since our grandfather
+ * is black, our father will become black and our new sibling
+ * (former grandparent) will become red.
+ */
+ __archive_rb_tree_reparent_nodes(grandpa, which);
+
+ /*
+ * Final step: Set the root to black.
+ */
+ RB_MARK_BLACK(rbt->rbt_root);
+}
+
+static void
+__archive_rb_tree_prune_node(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self, int rebalance)
+{
+ const unsigned int which = RB_POSITION(self);
+ struct archive_rb_node *father = RB_FATHER(self);
+
+ /*
+ * Since we are childless, we know that self->rb_left is pointing
+ * to the sentinel node.
+ */
+ father->rb_nodes[which] = self->rb_left;
+
+ /*
+ * Rebalance if requested.
+ */
+ if (rebalance)
+ __archive_rb_tree_removal_rebalance(rbt, father, which);
+}
+
+/*
+ * When deleting an interior node
+ */
+static void
+__archive_rb_tree_swap_prune_and_rebalance(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self, struct archive_rb_node *standin)
+{
+ const unsigned int standin_which = RB_POSITION(standin);
+ unsigned int standin_other = standin_which ^ RB_DIR_OTHER;
+ struct archive_rb_node *standin_son;
+ struct archive_rb_node *standin_father = RB_FATHER(standin);
+ int rebalance = RB_BLACK_P(standin);
+
+ if (standin_father == self) {
+ /*
+ * As a child of self, any children would be opposite of
+ * our parent.
+ */
+ standin_son = standin->rb_nodes[standin_which];
+ } else {
+ /*
+ * Since we aren't a child of self, any children would be
+ * on the same side as our parent.
+ */
+ standin_son = standin->rb_nodes[standin_other];
+ }
+
+ if (RB_RED_P(standin_son)) {
+ /*
+ * We know we have a red child so if we flip it to black
+ * we don't have to rebalance.
+ */
+ RB_MARK_BLACK(standin_son);
+ rebalance = F;
+
+ if (standin_father != self) {
+ /*
+ * Change the son's parentage to point to his grandpa.
+ */
+ RB_SET_FATHER(standin_son, standin_father);
+ RB_SET_POSITION(standin_son, standin_which);
+ }
+ }
+
+ if (standin_father == self) {
+ /*
+ * If we are about to delete the standin's father, then when
+ * we call rebalance, we need to use ourselves as our father.
+ * Otherwise remember our original father. Also, since we are
+ * our standin's father we only need to reparent the standin's
+ * brother.
+ *
+ * | R --> S |
+ * | Q S --> Q T |
+ * | t --> |
+ *
+ * Have our son/standin adopt his brother as his new son.
+ */
+ standin_father = standin;
+ } else {
+ /*
+ * | R --> S . |
+ * | / \ | T --> / \ | / |
+ * | ..... | S --> ..... | T |
+ *
+ * Sever standin's connection to his father.
+ */
+ standin_father->rb_nodes[standin_which] = standin_son;
+ /*
+ * Adopt the far son.
+ */
+ standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];
+ RB_SET_FATHER(standin->rb_nodes[standin_other], standin);
+ /*
+ * Use standin_other because we need to preserve standin_which
+ * for the removal_rebalance.
+ */
+ standin_other = standin_which;
+ }
+
+ /*
+ * Move the only remaining son to our standin. If our standin is our
+ * son, this will be the only son needed to be moved.
+ */
+ standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];
+ RB_SET_FATHER(standin->rb_nodes[standin_other], standin);
+
+ /*
+ * Now copy the result of self to standin and then replace
+ * self with standin in the tree.
+ */
+ RB_COPY_PROPERTIES(standin, self);
+ RB_SET_FATHER(standin, RB_FATHER(self));
+ RB_FATHER(standin)->rb_nodes[RB_POSITION(standin)] = standin;
+
+ if (rebalance)
+ __archive_rb_tree_removal_rebalance(rbt, standin_father, standin_which);
+}
+
+/*
+ * We could do this by doing
+ * __archive_rb_tree_node_swap(rbt, self, which);
+ * __archive_rb_tree_prune_node(rbt, self, F);
+ *
+ * But it's more efficient to just evaluate and recolor the child.
+ */
+static void
+__archive_rb_tree_prune_blackred_branch(
+ struct archive_rb_node *self, unsigned int which)
+{
+ struct archive_rb_node *father = RB_FATHER(self);
+ struct archive_rb_node *son = self->rb_nodes[which];
+
+ /*
+ * Remove ourselves from the tree and give our former child our
+ * properties (position, color, root).
+ */
+ RB_COPY_PROPERTIES(son, self);
+ father->rb_nodes[RB_POSITION(son)] = son;
+ RB_SET_FATHER(son, father);
+}
+/*
+ *
+ */
+void
+__archive_rb_tree_remove_node(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self)
+{
+ struct archive_rb_node *standin;
+ unsigned int which;
+
+ /*
+ * In the following diagrams, we (the node to be removed) are S. Red
+ * nodes are lowercase. T could be either red or black.
+ *
+ * Remember the major axiom of the red-black tree: the number of
+ * black nodes from the root to each leaf is constant across all
+ * leaves, only the number of red nodes varies.
+ *
+ * Thus removing a red leaf doesn't require any other changes to a
+ * red-black tree. So if we must remove a node, attempt to rearrange
+ * the tree so we can remove a red node.
+ *
+ * The simplest case is a childless red node or a childless root node:
+ *
+ * | T --> T | or | R --> * |
+ * | s --> * |
+ */
+ if (RB_CHILDLESS_P(self)) {
+ const int rebalance = RB_BLACK_P(self) && !RB_ROOT_P(rbt, self);
+ __archive_rb_tree_prune_node(rbt, self, rebalance);
+ return;
+ }
+ if (!RB_TWOCHILDREN_P(self)) {
+ /*
+ * The next simplest case is the node we are deleting is
+ * black and has one red child.
+ *
+ * | T --> T --> T |
+ * | S --> R --> R |
+ * | r --> s --> * |
+ */
+ which = RB_LEFT_SENTINEL_P(self) ? RB_DIR_RIGHT : RB_DIR_LEFT;
+ __archive_rb_tree_prune_blackred_branch(self, which);
+ return;
+ }
+
+ /*
+ * We invert these because we prefer to remove from the inside of
+ * the tree.
+ */
+ which = RB_POSITION(self) ^ RB_DIR_OTHER;
+
+ /*
+ * Let's find the node closes to us opposite of our parent
+ * Now swap it with ourself, "prune" it, and rebalance, if needed.
+ */
+ standin = __archive_rb_tree_iterate(rbt, self, which);
+ __archive_rb_tree_swap_prune_and_rebalance(rbt, self, standin);
+}
+
+static void
+__archive_rb_tree_removal_rebalance(struct archive_rb_tree *rbt,
+ struct archive_rb_node *parent, unsigned int which)
+{
+
+ while (RB_BLACK_P(parent->rb_nodes[which])) {
+ unsigned int other = which ^ RB_DIR_OTHER;
+ struct archive_rb_node *brother = parent->rb_nodes[other];
+
+ if (brother == NULL)
+ return;/* The tree may be broken. */
+ /*
+ * For cases 1, 2a, and 2b, our brother's children must
+ * be black and our father must be black
+ */
+ if (RB_BLACK_P(parent)
+ && RB_BLACK_P(brother->rb_left)
+ && RB_BLACK_P(brother->rb_right)) {
+ if (RB_RED_P(brother)) {
+ /*
+ * Case 1: Our brother is red, swap its
+ * position (and colors) with our parent.
+ * This should now be case 2b (unless C or E
+ * has a red child which is case 3; thus no
+ * explicit branch to case 2b).
+ *
+ * B -> D
+ * A d -> b E
+ * C E -> A C
+ */
+ __archive_rb_tree_reparent_nodes(parent, other);
+ brother = parent->rb_nodes[other];
+ if (brother == NULL)
+ return;/* The tree may be broken. */
+ } else {
+ /*
+ * Both our parent and brother are black.
+ * Change our brother to red, advance up rank
+ * and go through the loop again.
+ *
+ * B -> *B
+ * *A D -> A d
+ * C E -> C E
+ */
+ RB_MARK_RED(brother);
+ if (RB_ROOT_P(rbt, parent))
+ return; /* root == parent == black */
+ which = RB_POSITION(parent);
+ parent = RB_FATHER(parent);
+ continue;
+ }
+ }
+ /*
+ * Avoid an else here so that case 2a above can hit either
+ * case 2b, 3, or 4.
+ */
+ if (RB_RED_P(parent)
+ && RB_BLACK_P(brother)
+ && RB_BLACK_P(brother->rb_left)
+ && RB_BLACK_P(brother->rb_right)) {
+ /*
+ * We are black, our father is red, our brother and
+ * both nephews are black. Simply invert/exchange the
+ * colors of our father and brother (to black and red
+ * respectively).
+ *
+ * | f --> F |
+ * | * B --> * b |
+ * | N N --> N N |
+ */
+ RB_MARK_BLACK(parent);
+ RB_MARK_RED(brother);
+ break; /* We're done! */
+ } else {
+ /*
+ * Our brother must be black and have at least one
+ * red child (it may have two).
+ */
+ if (RB_BLACK_P(brother->rb_nodes[other])) {
+ /*
+ * Case 3: our brother is black, our near
+ * nephew is red, and our far nephew is black.
+ * Swap our brother with our near nephew.
+ * This result in a tree that matches case 4.
+ * (Our father could be red or black).
+ *
+ * | F --> F |
+ * | x B --> x B |
+ * | n --> n |
+ */
+ __archive_rb_tree_reparent_nodes(brother, which);
+ brother = parent->rb_nodes[other];
+ }
+ /*
+ * Case 4: our brother is black and our far nephew
+ * is red. Swap our father and brother locations and
+ * change our far nephew to black. (these can be
+ * done in either order so we change the color first).
+ * The result is a valid red-black tree and is a
+ * terminal case. (again we don't care about the
+ * father's color)
+ *
+ * If the father is red, we will get a red-black-black
+ * tree:
+ * | f -> f --> b |
+ * | B -> B --> F N |
+ * | n -> N --> |
+ *
+ * If the father is black, we will get an all black
+ * tree:
+ * | F -> F --> B |
+ * | B -> B --> F N |
+ * | n -> N --> |
+ *
+ * If we had two red nephews, then after the swap,
+ * our former father would have a red grandson.
+ */
+ if (brother->rb_nodes[other] == NULL)
+ return;/* The tree may be broken. */
+ RB_MARK_BLACK(brother->rb_nodes[other]);
+ __archive_rb_tree_reparent_nodes(parent, other);
+ break; /* We're done! */
+ }
+ }
+}
+
+struct archive_rb_node *
+__archive_rb_tree_iterate(struct archive_rb_tree *rbt,
+ struct archive_rb_node *self, const unsigned int direction)
+{
+ const unsigned int other = direction ^ RB_DIR_OTHER;
+
+ if (self == NULL) {
+ self = rbt->rbt_root;
+ if (RB_SENTINEL_P(self))
+ return NULL;
+ while (!RB_SENTINEL_P(self->rb_nodes[direction]))
+ self = self->rb_nodes[direction];
+ return self;
+ }
+ /*
+ * We can't go any further in this direction. We proceed up in the
+ * opposite direction until our parent is in direction we want to go.
+ */
+ if (RB_SENTINEL_P(self->rb_nodes[direction])) {
+ while (!RB_ROOT_P(rbt, self)) {
+ if (other == (unsigned int)RB_POSITION(self))
+ return RB_FATHER(self);
+ self = RB_FATHER(self);
+ }
+ return NULL;
+ }
+
+ /*
+ * Advance down one in current direction and go down as far as possible
+ * in the opposite direction.
+ */
+ self = self->rb_nodes[direction];
+ while (!RB_SENTINEL_P(self->rb_nodes[other]))
+ self = self->rb_nodes[other];
+ return self;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_rb.h b/contrib/libs/libarchive/libarchive/archive_rb.h
new file mode 100644
index 0000000000..8851f10818
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_rb.h
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas <matt@3am-software.com>.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ *
+ * Based on NetBSD: rb.h,v 1.13 2009/08/16 10:57:01 yamt Exp
+ */
+
+#ifndef ARCHIVE_RB_H_INCLUDED
+#define ARCHIVE_RB_H_INCLUDED
+
+struct archive_rb_node {
+ struct archive_rb_node *rb_nodes[2];
+ /*
+ * rb_info contains the two flags and the parent back pointer.
+ * We put the two flags in the low two bits since we know that
+ * rb_node will have an alignment of 4 or 8 bytes.
+ */
+ uintptr_t rb_info;
+};
+
+#define ARCHIVE_RB_DIR_LEFT 0
+#define ARCHIVE_RB_DIR_RIGHT 1
+
+#define ARCHIVE_RB_TREE_MIN(T) \
+ __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_LEFT)
+#define ARCHIVE_RB_TREE_MAX(T) \
+ __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_RIGHT)
+#define ARCHIVE_RB_TREE_NEXT(T, N) \
+ __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_RIGHT)
+#define ARCHIVE_RB_TREE_PREV(T, N) \
+ __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_LEFT)
+#define ARCHIVE_RB_TREE_FOREACH(N, T) \
+ for ((N) = ARCHIVE_RB_TREE_MIN(T); (N); \
+ (N) = ARCHIVE_RB_TREE_NEXT((T), (N)))
+#define ARCHIVE_RB_TREE_FOREACH_REVERSE(N, T) \
+ for ((N) = ARCHIVE_RB_TREE_MAX(T); (N); \
+ (N) = ARCHIVE_RB_TREE_PREV((T), (N)))
+#define ARCHIVE_RB_TREE_FOREACH_SAFE(N, T, S) \
+ for ((N) = ARCHIVE_RB_TREE_MIN(T); \
+ (N) && ((S) = ARCHIVE_RB_TREE_NEXT((T), (N)), 1); \
+ (N) = (S))
+#define ARCHIVE_RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
+ for ((N) = ARCHIVE_RB_TREE_MAX(T); \
+ (N) && ((S) = ARCHIVE_RB_TREE_PREV((T), (N)), 1); \
+ (N) = (S))
+
+/*
+ * archive_rbto_compare_nodes_fn:
+ * return a positive value if the first node < the second node.
+ * return a negative value if the first node > the second node.
+ * return 0 if they are considered same.
+ *
+ * archive_rbto_compare_key_fn:
+ * return a positive value if the node < the key.
+ * return a negative value if the node > the key.
+ * return 0 if they are considered same.
+ */
+
+typedef signed int (*const archive_rbto_compare_nodes_fn)(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+typedef signed int (*const archive_rbto_compare_key_fn)(const struct archive_rb_node *,
+ const void *);
+
+struct archive_rb_tree_ops {
+ archive_rbto_compare_nodes_fn rbto_compare_nodes;
+ archive_rbto_compare_key_fn rbto_compare_key;
+};
+
+struct archive_rb_tree {
+ struct archive_rb_node *rbt_root;
+ const struct archive_rb_tree_ops *rbt_ops;
+};
+
+void __archive_rb_tree_init(struct archive_rb_tree *,
+ const struct archive_rb_tree_ops *);
+int __archive_rb_tree_insert_node(struct archive_rb_tree *,
+ struct archive_rb_node *);
+struct archive_rb_node *
+ __archive_rb_tree_find_node(struct archive_rb_tree *, const void *);
+struct archive_rb_node *
+ __archive_rb_tree_find_node_geq(struct archive_rb_tree *, const void *);
+struct archive_rb_node *
+ __archive_rb_tree_find_node_leq(struct archive_rb_tree *, const void *);
+void __archive_rb_tree_remove_node(struct archive_rb_tree *, struct archive_rb_node *);
+struct archive_rb_node *
+ __archive_rb_tree_iterate(struct archive_rb_tree *,
+ struct archive_rb_node *, const unsigned int);
+
+#endif /* ARCHIVE_RB_H_*/
diff --git a/contrib/libs/libarchive/libarchive/archive_read.c b/contrib/libs/libarchive/libarchive/archive_read.c
new file mode 100644
index 0000000000..45a38aed02
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read.c
@@ -0,0 +1,1756 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+/*
+ * This file contains the "essential" portions of the read API, that
+ * is, stuff that will probably always be used by any client that
+ * actually needs to read an archive. Optional pieces have been, as
+ * far as possible, separated out into separate files to avoid
+ * needlessly bloating statically-linked clients.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:23Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define minimum(a, b) (a < b ? a : b)
+
+static int choose_filters(struct archive_read *);
+static int choose_format(struct archive_read *);
+static int close_filters(struct archive_read *);
+static int64_t _archive_filter_bytes(struct archive *, int);
+static int _archive_filter_code(struct archive *, int);
+static const char *_archive_filter_name(struct archive *, int);
+static int _archive_filter_count(struct archive *);
+static int _archive_read_close(struct archive *);
+static int _archive_read_data_block(struct archive *,
+ const void **, size_t *, int64_t *);
+static int _archive_read_free(struct archive *);
+static int _archive_read_next_header(struct archive *,
+ struct archive_entry **);
+static int _archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+static int64_t advance_file_pointer(struct archive_read_filter *, int64_t);
+
+static const struct archive_vtable
+archive_read_vtable = {
+ .archive_filter_bytes = _archive_filter_bytes,
+ .archive_filter_code = _archive_filter_code,
+ .archive_filter_name = _archive_filter_name,
+ .archive_filter_count = _archive_filter_count,
+ .archive_read_data_block = _archive_read_data_block,
+ .archive_read_next_header = _archive_read_next_header,
+ .archive_read_next_header2 = _archive_read_next_header2,
+ .archive_free = _archive_read_free,
+ .archive_close = _archive_read_close,
+};
+
+/*
+ * Allocate, initialize and return a struct archive object.
+ */
+struct archive *
+archive_read_new(void)
+{
+ struct archive_read *a;
+
+ a = (struct archive_read *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_READ_MAGIC;
+
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->entry = archive_entry_new2(&a->archive);
+ a->archive.vtable = &archive_read_vtable;
+
+ a->passphrases.last = &a->passphrases.first;
+
+ return (&a->archive);
+}
+
+/*
+ * Record the do-not-extract-to file. This belongs in archive_read_extract.c.
+ */
+void
+archive_read_extract_set_skip_file(struct archive *_a, la_int64_t d,
+ la_int64_t i)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file"))
+ return;
+ a->skip_file_set = 1;
+ a->skip_file_dev = d;
+ a->skip_file_ino = i;
+}
+
+/*
+ * Open the archive
+ */
+int
+archive_read_open(struct archive *a, void *client_data,
+ archive_open_callback *client_opener, archive_read_callback *client_reader,
+ archive_close_callback *client_closer)
+{
+ /* Old archive_read_open() is just a thin shell around
+ * archive_read_open1. */
+ archive_read_set_open_callback(a, client_opener);
+ archive_read_set_read_callback(a, client_reader);
+ archive_read_set_close_callback(a, client_closer);
+ archive_read_set_callback_data(a, client_data);
+ return archive_read_open1(a);
+}
+
+
+int
+archive_read_open2(struct archive *a, void *client_data,
+ archive_open_callback *client_opener,
+ archive_read_callback *client_reader,
+ archive_skip_callback *client_skipper,
+ archive_close_callback *client_closer)
+{
+ /* Old archive_read_open2() is just a thin shell around
+ * archive_read_open1. */
+ archive_read_set_callback_data(a, client_data);
+ archive_read_set_open_callback(a, client_opener);
+ archive_read_set_read_callback(a, client_reader);
+ archive_read_set_skip_callback(a, client_skipper);
+ archive_read_set_close_callback(a, client_closer);
+ return archive_read_open1(a);
+}
+
+static ssize_t
+client_read_proxy(struct archive_read_filter *self, const void **buff)
+{
+ ssize_t r;
+ r = (self->archive->client.reader)(&self->archive->archive,
+ self->data, buff);
+ return (r);
+}
+
+static int64_t
+client_skip_proxy(struct archive_read_filter *self, int64_t request)
+{
+ if (request < 0)
+ __archive_errx(1, "Negative skip requested.");
+ if (request == 0)
+ return 0;
+
+ if (self->archive->client.skipper != NULL) {
+ /* Seek requests over 1GiB are broken down into
+ * multiple seeks. This avoids overflows when the
+ * requests get passed through 32-bit arguments. */
+ int64_t skip_limit = (int64_t)1 << 30;
+ int64_t total = 0;
+ for (;;) {
+ int64_t get, ask = request;
+ if (ask > skip_limit)
+ ask = skip_limit;
+ get = (self->archive->client.skipper)
+ (&self->archive->archive, self->data, ask);
+ total += get;
+ if (get == 0 || get == request)
+ return (total);
+ if (get > request)
+ return ARCHIVE_FATAL;
+ request -= get;
+ }
+ } else if (self->archive->client.seeker != NULL
+ && request > 64 * 1024) {
+ /* If the client provided a seeker but not a skipper,
+ * we can use the seeker to skip forward.
+ *
+ * Note: This isn't always a good idea. The client
+ * skipper is allowed to skip by less than requested
+ * if it needs to maintain block alignment. The
+ * seeker is not allowed to play such games, so using
+ * the seeker here may be a performance loss compared
+ * to just reading and discarding. That's why we
+ * only do this for skips of over 64k.
+ */
+ int64_t before = self->position;
+ int64_t after = (self->archive->client.seeker)
+ (&self->archive->archive, self->data, request, SEEK_CUR);
+ if (after != before + request)
+ return ARCHIVE_FATAL;
+ return after - before;
+ }
+ return 0;
+}
+
+static int64_t
+client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence)
+{
+ /* DO NOT use the skipper here! If we transparently handled
+ * forward seek here by using the skipper, that will break
+ * other libarchive code that assumes a successful forward
+ * seek means it can also seek backwards.
+ */
+ if (self->archive->client.seeker == NULL) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Current client reader does not support seeking a device");
+ return (ARCHIVE_FAILED);
+ }
+ return (self->archive->client.seeker)(&self->archive->archive,
+ self->data, offset, whence);
+}
+
+static int
+read_client_close_proxy(struct archive_read *a)
+{
+ int r = ARCHIVE_OK, r2;
+ unsigned int i;
+
+ if (a->client.closer == NULL)
+ return (r);
+ for (i = 0; i < a->client.nodes; i++)
+ {
+ r2 = (a->client.closer)
+ ((struct archive *)a, a->client.dataset[i].data);
+ if (r > r2)
+ r = r2;
+ }
+ return (r);
+}
+
+static int
+client_close_proxy(struct archive_read_filter *self)
+{
+ return read_client_close_proxy(self->archive);
+}
+
+static int
+client_open_proxy(struct archive_read_filter *self)
+{
+ int r = ARCHIVE_OK;
+ if (self->archive->client.opener != NULL)
+ r = (self->archive->client.opener)(
+ (struct archive *)self->archive, self->data);
+ return (r);
+}
+
+static int
+client_switch_proxy(struct archive_read_filter *self, unsigned int iindex)
+{
+ int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK;
+ void *data2 = NULL;
+
+ /* Don't do anything if already in the specified data node */
+ if (self->archive->client.cursor == iindex)
+ return (ARCHIVE_OK);
+
+ self->archive->client.cursor = iindex;
+ data2 = self->archive->client.dataset[self->archive->client.cursor].data;
+ if (self->archive->client.switcher != NULL)
+ {
+ r1 = r2 = (self->archive->client.switcher)
+ ((struct archive *)self->archive, self->data, data2);
+ self->data = data2;
+ }
+ else
+ {
+ /* Attempt to call close and open instead */
+ if (self->archive->client.closer != NULL)
+ r1 = (self->archive->client.closer)
+ ((struct archive *)self->archive, self->data);
+ self->data = data2;
+ r2 = client_open_proxy(self);
+ }
+ return (r1 < r2) ? r1 : r2;
+}
+
+int
+archive_read_set_open_callback(struct archive *_a,
+ archive_open_callback *client_opener)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_open_callback");
+ a->client.opener = client_opener;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_read_callback(struct archive *_a,
+ archive_read_callback *client_reader)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_read_callback");
+ a->client.reader = client_reader;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_skip_callback(struct archive *_a,
+ archive_skip_callback *client_skipper)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_skip_callback");
+ a->client.skipper = client_skipper;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_seek_callback(struct archive *_a,
+ archive_seek_callback *client_seeker)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_seek_callback");
+ a->client.seeker = client_seeker;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_close_callback(struct archive *_a,
+ archive_close_callback *client_closer)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_close_callback");
+ a->client.closer = client_closer;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_switch_callback(struct archive *_a,
+ archive_switch_callback *client_switcher)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_switch_callback");
+ a->client.switcher = client_switcher;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_set_callback_data(struct archive *_a, void *client_data)
+{
+ return archive_read_set_callback_data2(_a, client_data, 0);
+}
+
+int
+archive_read_set_callback_data2(struct archive *_a, void *client_data,
+ unsigned int iindex)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_callback_data2");
+
+ if (a->client.nodes == 0)
+ {
+ a->client.dataset = (struct archive_read_data_node *)
+ calloc(1, sizeof(*a->client.dataset));
+ if (a->client.dataset == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory.");
+ return ARCHIVE_FATAL;
+ }
+ a->client.nodes = 1;
+ }
+
+ if (iindex > a->client.nodes - 1)
+ {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid index specified.");
+ return ARCHIVE_FATAL;
+ }
+ a->client.dataset[iindex].data = client_data;
+ a->client.dataset[iindex].begin_position = -1;
+ a->client.dataset[iindex].total_size = -1;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_add_callback_data(struct archive *_a, void *client_data,
+ unsigned int iindex)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ void *p;
+ unsigned int i;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_add_callback_data");
+ if (iindex > a->client.nodes) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid index specified.");
+ return ARCHIVE_FATAL;
+ }
+ p = realloc(a->client.dataset, sizeof(*a->client.dataset)
+ * (++(a->client.nodes)));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory.");
+ return ARCHIVE_FATAL;
+ }
+ a->client.dataset = (struct archive_read_data_node *)p;
+ for (i = a->client.nodes - 1; i > iindex; i--) {
+ a->client.dataset[i].data = a->client.dataset[i-1].data;
+ a->client.dataset[i].begin_position = -1;
+ a->client.dataset[i].total_size = -1;
+ }
+ a->client.dataset[iindex].data = client_data;
+ a->client.dataset[iindex].begin_position = -1;
+ a->client.dataset[iindex].total_size = -1;
+ return ARCHIVE_OK;
+}
+
+int
+archive_read_append_callback_data(struct archive *_a, void *client_data)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ return archive_read_add_callback_data(_a, client_data, a->client.nodes);
+}
+
+int
+archive_read_prepend_callback_data(struct archive *_a, void *client_data)
+{
+ return archive_read_add_callback_data(_a, client_data, 0);
+}
+
+static const struct archive_read_filter_vtable
+none_reader_vtable = {
+ .read = client_read_proxy,
+ .close = client_close_proxy,
+};
+
+int
+archive_read_open1(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_filter *filter, *tmp;
+ int slot, e = ARCHIVE_OK;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_open");
+ archive_clear_error(&a->archive);
+
+ if (a->client.reader == NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "No reader function provided to archive_read_open");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Open data source. */
+ if (a->client.opener != NULL) {
+ e = (a->client.opener)(&a->archive, a->client.dataset[0].data);
+ if (e != 0) {
+ /* If the open failed, call the closer to clean up. */
+ read_client_close_proxy(a);
+ return (e);
+ }
+ }
+
+ filter = calloc(1, sizeof(*filter));
+ if (filter == NULL)
+ return (ARCHIVE_FATAL);
+ filter->bidder = NULL;
+ filter->upstream = NULL;
+ filter->archive = a;
+ filter->data = a->client.dataset[0].data;
+ filter->vtable = &none_reader_vtable;
+ filter->name = "none";
+ filter->code = ARCHIVE_FILTER_NONE;
+ filter->can_skip = 1;
+ filter->can_seek = 1;
+
+ a->client.dataset[0].begin_position = 0;
+ if (!a->filter || !a->bypass_filter_bidding)
+ {
+ a->filter = filter;
+ /* Build out the input pipeline. */
+ e = choose_filters(a);
+ if (e < ARCHIVE_WARN) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ }
+ else
+ {
+ /* Need to add "NONE" type filter at the end of the filter chain */
+ tmp = a->filter;
+ while (tmp->upstream)
+ tmp = tmp->upstream;
+ tmp->upstream = filter;
+ }
+
+ if (!a->format)
+ {
+ slot = choose_format(a);
+ if (slot < 0) {
+ close_filters(a);
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ a->format = &(a->formats[slot]);
+ }
+
+ a->archive.state = ARCHIVE_STATE_HEADER;
+
+ /* Ensure libarchive starts from the first node in a multivolume set */
+ client_switch_proxy(a->filter, 0);
+ return (e);
+}
+
+/*
+ * Allow each registered stream transform to bid on whether
+ * it wants to handle this stream. Repeat until we've finished
+ * building the pipeline.
+ */
+
+/* We won't build a filter pipeline with more stages than this. */
+#define MAX_NUMBER_FILTERS 25
+
+static int
+choose_filters(struct archive_read *a)
+{
+ int number_bidders, i, bid, best_bid, number_filters;
+ struct archive_read_filter_bidder *bidder, *best_bidder;
+ struct archive_read_filter *filter;
+ ssize_t avail;
+ int r;
+
+ for (number_filters = 0; number_filters < MAX_NUMBER_FILTERS; ++number_filters) {
+ number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ best_bid = 0;
+ best_bidder = NULL;
+
+ bidder = a->bidders;
+ for (i = 0; i < number_bidders; i++, bidder++) {
+ if (bidder->vtable == NULL)
+ continue;
+ bid = (bidder->vtable->bid)(bidder, a->filter);
+ if (bid > best_bid) {
+ best_bid = bid;
+ best_bidder = bidder;
+ }
+ }
+
+ /* If no bidder, we're done. */
+ if (best_bidder == NULL) {
+ /* Verify the filter by asking it for some data. */
+ __archive_read_filter_ahead(a->filter, 1, &avail);
+ if (avail < 0) {
+ __archive_read_free_filters(a);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+ }
+
+ filter
+ = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+ if (filter == NULL)
+ return (ARCHIVE_FATAL);
+ filter->bidder = best_bidder;
+ filter->archive = a;
+ filter->upstream = a->filter;
+ a->filter = filter;
+ r = (best_bidder->vtable->init)(a->filter);
+ if (r != ARCHIVE_OK) {
+ __archive_read_free_filters(a);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Input requires too many filters for decoding");
+ return (ARCHIVE_FATAL);
+}
+
+int
+__archive_read_header(struct archive_read *a, struct archive_entry *entry)
+{
+ if (!a->filter->vtable->read_header)
+ return (ARCHIVE_OK);
+ return a->filter->vtable->read_header(a->filter, entry);
+}
+
+/*
+ * Read header of next entry.
+ */
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r1 = ARCHIVE_OK, r2;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_next_header");
+
+ archive_entry_clear(entry);
+ archive_clear_error(&a->archive);
+
+ /*
+ * If client didn't consume entire data, skip any remainder
+ * (This is especially important for GNU incremental directories.)
+ */
+ if (a->archive.state == ARCHIVE_STATE_DATA) {
+ r1 = archive_read_data_skip(&a->archive);
+ if (r1 == ARCHIVE_EOF)
+ archive_set_error(&a->archive, EIO,
+ "Premature end-of-file.");
+ if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* Record start-of-header offset in uncompressed stream. */
+ a->header_position = a->filter->position;
+
+ ++_a->file_count;
+ r2 = (a->format->read_header)(a, entry);
+
+ /*
+ * EOF and FATAL are persistent at this layer. By
+ * modifying the state, we guarantee that future calls to
+ * read a header or read data will fail.
+ */
+ switch (r2) {
+ case ARCHIVE_EOF:
+ a->archive.state = ARCHIVE_STATE_EOF;
+ --_a->file_count;/* Revert a file counter. */
+ break;
+ case ARCHIVE_OK:
+ a->archive.state = ARCHIVE_STATE_DATA;
+ break;
+ case ARCHIVE_WARN:
+ a->archive.state = ARCHIVE_STATE_DATA;
+ break;
+ case ARCHIVE_RETRY:
+ break;
+ case ARCHIVE_FATAL:
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ break;
+ }
+
+ __archive_reset_read_data(&a->archive);
+
+ a->data_start_node = a->client.cursor;
+ /* EOF always wins; otherwise return the worst error. */
+ return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1;
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+ int ret;
+ struct archive_read *a = (struct archive_read *)_a;
+ *entryp = NULL;
+ ret = _archive_read_next_header2(_a, a->entry);
+ *entryp = a->entry;
+ return ret;
+}
+
+/*
+ * Allow each registered format to bid on whether it wants to handle
+ * the next entry. Return index of winning bidder.
+ */
+static int
+choose_format(struct archive_read *a)
+{
+ int slots;
+ int i;
+ int bid, best_bid;
+ int best_bid_slot;
+
+ slots = sizeof(a->formats) / sizeof(a->formats[0]);
+ best_bid = -1;
+ best_bid_slot = -1;
+
+ /* Set up a->format for convenience of bidders. */
+ a->format = &(a->formats[0]);
+ for (i = 0; i < slots; i++, a->format++) {
+ if (a->format->bid) {
+ bid = (a->format->bid)(a, best_bid);
+ if (bid == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+ if (a->filter->position != 0)
+ __archive_read_seek(a, 0, SEEK_SET);
+ if ((bid > best_bid) || (best_bid_slot < 0)) {
+ best_bid = bid;
+ best_bid_slot = i;
+ }
+ }
+ }
+
+ /*
+ * There were no bidders; this is a serious programmer error
+ * and demands a quick and definitive abort.
+ */
+ if (best_bid_slot < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "No formats registered");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * There were bidders, but no non-zero bids; this means we
+ * can't support this stream.
+ */
+ if (best_bid < 1) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unrecognized archive format");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (best_bid_slot);
+}
+
+/*
+ * Return the file offset (within the uncompressed data stream) where
+ * the last header started.
+ */
+la_int64_t
+archive_read_header_position(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_header_position");
+ return (a->header_position);
+}
+
+/*
+ * Returns 1 if the archive contains at least one encrypted entry.
+ * If the archive format not support encryption at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
+ * If for any other reason (e.g. not enough data read so far)
+ * we cannot say whether there are encrypted entries, then
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
+ * In general, this function will return values below zero when the
+ * reader is uncertain or totally incapable of encryption support.
+ * When this function returns 0 you can be sure that the reader
+ * supports encryption detection but no encrypted entries have
+ * been found yet.
+ *
+ * NOTE: If the metadata/header of an archive is also encrypted, you
+ * cannot rely on the number of encrypted entries. That is why this
+ * function does not return the number of encrypted entries but#
+ * just shows that there are some.
+ */
+int
+archive_read_has_encrypted_entries(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int format_supports_encryption = archive_read_format_capabilities(_a)
+ & (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+
+ if (!_a || !format_supports_encryption) {
+ /* Format in general doesn't support encryption */
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
+ }
+
+ /* A reader potentially has read enough data now. */
+ if (a->format && a->format->has_encrypted_entries) {
+ return (a->format->has_encrypted_entries)(a);
+ }
+
+ /* For any other reason we cannot say how many entries are there. */
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+/*
+ * Returns a bitmask of capabilities that are supported by the archive format reader.
+ * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.
+ */
+int
+archive_read_format_capabilities(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ if (a && a->format && a->format->format_capabilties) {
+ return (a->format->format_capabilties)(a);
+ }
+ return ARCHIVE_READ_FORMAT_CAPS_NONE;
+}
+
+/*
+ * Read data from an archive entry, using a read(2)-style interface.
+ * This is a convenience routine that just calls
+ * archive_read_data_block and copies the results into the client
+ * buffer, filling any gaps with zero bytes. Clients using this
+ * API can be completely ignorant of sparse-file issues; sparse files
+ * will simply be padded with nulls.
+ *
+ * DO NOT intermingle calls to this function and archive_read_data_block
+ * to read a single entry body.
+ */
+la_ssize_t
+archive_read_data(struct archive *_a, void *buff, size_t s)
+{
+ struct archive *a = (struct archive *)_a;
+ char *dest;
+ const void *read_buf;
+ size_t bytes_read;
+ size_t len;
+ int r;
+
+ bytes_read = 0;
+ dest = (char *)buff;
+
+ while (s > 0) {
+ if (a->read_data_offset == a->read_data_output_offset &&
+ a->read_data_remaining == 0) {
+ read_buf = a->read_data_block;
+ a->read_data_is_posix_read = 1;
+ a->read_data_requested = s;
+ r = archive_read_data_block(a, &read_buf,
+ &a->read_data_remaining, &a->read_data_offset);
+ a->read_data_block = read_buf;
+ if (r == ARCHIVE_EOF)
+ return (bytes_read);
+ /*
+ * Error codes are all negative, so the status
+ * return here cannot be confused with a valid
+ * byte count. (ARCHIVE_OK is zero.)
+ */
+ if (r < ARCHIVE_OK)
+ return (r);
+ }
+
+ if (a->read_data_offset < a->read_data_output_offset) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encountered out-of-order sparse blocks");
+ return (ARCHIVE_RETRY);
+ }
+
+ /* Compute the amount of zero padding needed. */
+ if (a->read_data_output_offset + (int64_t)s <
+ a->read_data_offset) {
+ len = s;
+ } else if (a->read_data_output_offset <
+ a->read_data_offset) {
+ len = (size_t)(a->read_data_offset -
+ a->read_data_output_offset);
+ } else
+ len = 0;
+
+ /* Add zeroes. */
+ memset(dest, 0, len);
+ s -= len;
+ a->read_data_output_offset += len;
+ dest += len;
+ bytes_read += len;
+
+ /* Copy data if there is any space left. */
+ if (s > 0) {
+ len = a->read_data_remaining;
+ if (len > s)
+ len = s;
+ if (len) {
+ memcpy(dest, a->read_data_block, len);
+ s -= len;
+ a->read_data_block += len;
+ a->read_data_remaining -= len;
+ a->read_data_output_offset += len;
+ a->read_data_offset += len;
+ dest += len;
+ bytes_read += len;
+ }
+ }
+ }
+ a->read_data_is_posix_read = 0;
+ a->read_data_requested = 0;
+ return (bytes_read);
+}
+
+/*
+ * Reset the read_data_* variables, used for starting a new entry.
+ */
+void __archive_reset_read_data(struct archive * a)
+{
+ a->read_data_output_offset = 0;
+ a->read_data_remaining = 0;
+ a->read_data_is_posix_read = 0;
+ a->read_data_requested = 0;
+
+ /* extra resets, from rar.c */
+ a->read_data_block = NULL;
+ a->read_data_offset = 0;
+}
+
+/*
+ * Skip over all remaining data in this entry.
+ */
+int
+archive_read_data_skip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_skip");
+
+ if (a->format->read_data_skip != NULL)
+ r = (a->format->read_data_skip)(a);
+ else {
+ while ((r = archive_read_data_block(&a->archive,
+ &buff, &size, &offset))
+ == ARCHIVE_OK)
+ ;
+ }
+
+ if (r == ARCHIVE_EOF)
+ r = ARCHIVE_OK;
+
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ return (r);
+}
+
+la_int64_t
+archive_seek_data(struct archive *_a, int64_t offset, int whence)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_seek_data_block");
+
+ if (a->format->seek_data == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: "
+ "No format_seek_data_block function registered");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (a->format->seek_data)(a, offset, whence);
+}
+
+/*
+ * Read the next block of entry data from the archive.
+ * This is a zero-copy interface; the client receives a pointer,
+ * size, and file offset of the next available block of data.
+ *
+ * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if
+ * the end of entry is encountered.
+ */
+static int
+_archive_read_data_block(struct archive *_a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_block");
+
+ if (a->format->read_data == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: "
+ "No format->read_data function registered");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (a->format->read_data)(a, buff, size, offset);
+}
+
+static int
+close_filters(struct archive_read *a)
+{
+ struct archive_read_filter *f = a->filter;
+ int r = ARCHIVE_OK;
+ /* Close each filter in the pipeline. */
+ while (f != NULL) {
+ struct archive_read_filter *t = f->upstream;
+ if (!f->closed && f->vtable != NULL) {
+ int r1 = (f->vtable->close)(f);
+ f->closed = 1;
+ if (r1 < r)
+ r = r1;
+ }
+ free(f->buffer);
+ f->buffer = NULL;
+ f = t;
+ }
+ return r;
+}
+
+void
+__archive_read_free_filters(struct archive_read *a)
+{
+ /* Make sure filters are closed and their buffers are freed */
+ close_filters(a);
+
+ while (a->filter != NULL) {
+ struct archive_read_filter *t = a->filter->upstream;
+ free(a->filter);
+ a->filter = t;
+ }
+}
+
+/*
+ * return the count of # of filters in use
+ */
+static int
+_archive_filter_count(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_filter *p = a->filter;
+ int count = 0;
+ while(p) {
+ count++;
+ p = p->upstream;
+ }
+ return count;
+}
+
+/*
+ * Close the file and all I/O.
+ */
+static int
+_archive_read_close(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+ if (a->archive.state == ARCHIVE_STATE_CLOSED)
+ return (ARCHIVE_OK);
+ archive_clear_error(&a->archive);
+ a->archive.state = ARCHIVE_STATE_CLOSED;
+
+ /* TODO: Clean up the formatters. */
+
+ /* Release the filter objects. */
+ r1 = close_filters(a);
+ if (r1 < r)
+ r = r1;
+
+ return (r);
+}
+
+/*
+ * Release memory and other resources.
+ */
+static int
+_archive_read_free(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_passphrase *p;
+ int i, n;
+ int slots;
+ int r = ARCHIVE_OK;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+ if (a->archive.state != ARCHIVE_STATE_CLOSED
+ && a->archive.state != ARCHIVE_STATE_FATAL)
+ r = archive_read_close(&a->archive);
+
+ /* Call cleanup functions registered by optional components. */
+ if (a->cleanup_archive_extract != NULL)
+ r = (a->cleanup_archive_extract)(a);
+
+ /* Cleanup format-specific data. */
+ slots = sizeof(a->formats) / sizeof(a->formats[0]);
+ for (i = 0; i < slots; i++) {
+ a->format = &(a->formats[i]);
+ if (a->formats[i].cleanup)
+ (a->formats[i].cleanup)(a);
+ }
+
+ /* Free the filters */
+ __archive_read_free_filters(a);
+
+ /* Release the bidder objects. */
+ n = sizeof(a->bidders)/sizeof(a->bidders[0]);
+ for (i = 0; i < n; i++) {
+ if (a->bidders[i].vtable == NULL ||
+ a->bidders[i].vtable->free == NULL)
+ continue;
+ (a->bidders[i].vtable->free)(&a->bidders[i]);
+ }
+
+ /* Release passphrase list. */
+ p = a->passphrases.first;
+ while (p != NULL) {
+ struct archive_read_passphrase *np = p->next;
+
+ /* A passphrase should be cleaned. */
+ memset(p->passphrase, 0, strlen(p->passphrase));
+ free(p->passphrase);
+ free(p);
+ p = np;
+ }
+
+ archive_string_free(&a->archive.error_string);
+ archive_entry_free(a->entry);
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a->client.dataset);
+ free(a);
+ return (r);
+}
+
+static struct archive_read_filter *
+get_filter(struct archive *_a, int n)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_filter *f = a->filter;
+ /* We use n == -1 for 'the last filter', which is always the
+ * client proxy. */
+ if (n == -1 && f != NULL) {
+ struct archive_read_filter *last = f;
+ f = f->upstream;
+ while (f != NULL) {
+ last = f;
+ f = f->upstream;
+ }
+ return (last);
+ }
+ if (n < 0)
+ return NULL;
+ while (n > 0 && f != NULL) {
+ f = f->upstream;
+ --n;
+ }
+ return (f);
+}
+
+static int
+_archive_filter_code(struct archive *_a, int n)
+{
+ struct archive_read_filter *f = get_filter(_a, n);
+ return f == NULL ? -1 : f->code;
+}
+
+static const char *
+_archive_filter_name(struct archive *_a, int n)
+{
+ struct archive_read_filter *f = get_filter(_a, n);
+ return f != NULL ? f->name : NULL;
+}
+
+static int64_t
+_archive_filter_bytes(struct archive *_a, int n)
+{
+ struct archive_read_filter *f = get_filter(_a, n);
+ return f == NULL ? -1 : f->position;
+}
+
+/*
+ * Used internally by read format handlers to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_register_format(struct archive_read *a,
+ void *format_data,
+ const char *name,
+ int (*bid)(struct archive_read *, int),
+ int (*options)(struct archive_read *, const char *, const char *),
+ int (*read_header)(struct archive_read *, struct archive_entry *),
+ int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),
+ int (*read_data_skip)(struct archive_read *),
+ int64_t (*seek_data)(struct archive_read *, int64_t, int),
+ int (*cleanup)(struct archive_read *),
+ int (*format_capabilities)(struct archive_read *),
+ int (*has_encrypted_entries)(struct archive_read *))
+{
+ int i, number_slots;
+
+ archive_check_magic(&a->archive,
+ ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "__archive_read_register_format");
+
+ number_slots = sizeof(a->formats) / sizeof(a->formats[0]);
+
+ for (i = 0; i < number_slots; i++) {
+ if (a->formats[i].bid == bid)
+ return (ARCHIVE_WARN); /* We've already installed */
+ if (a->formats[i].bid == NULL) {
+ a->formats[i].bid = bid;
+ a->formats[i].options = options;
+ a->formats[i].read_header = read_header;
+ a->formats[i].read_data = read_data;
+ a->formats[i].read_data_skip = read_data_skip;
+ a->formats[i].seek_data = seek_data;
+ a->formats[i].cleanup = cleanup;
+ a->formats[i].data = format_data;
+ a->formats[i].name = name;
+ a->formats[i].format_capabilties = format_capabilities;
+ a->formats[i].has_encrypted_entries = has_encrypted_entries;
+ return (ARCHIVE_OK);
+ }
+ }
+
+ archive_set_error(&a->archive, ENOMEM,
+ "Not enough slots for format registration");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Used internally by decompression routines to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_register_bidder(struct archive_read *a,
+ void *bidder_data,
+ const char *name,
+ const struct archive_read_filter_bidder_vtable *vtable)
+{
+ struct archive_read_filter_bidder *bidder;
+ int i, number_slots;
+
+ archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "__archive_read_register_bidder");
+
+ number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ for (i = 0; i < number_slots; i++) {
+ if (a->bidders[i].vtable != NULL)
+ continue;
+ memset(a->bidders + i, 0, sizeof(a->bidders[0]));
+ bidder = (a->bidders + i);
+ bidder->data = bidder_data;
+ bidder->name = name;
+ bidder->vtable = vtable;
+ if (bidder->vtable->bid == NULL || bidder->vtable->init == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: "
+ "no bid/init for filter bidder");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+ }
+
+ archive_set_error(&a->archive, ENOMEM,
+ "Not enough slots for filter registration");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * The next section implements the peek/consume internal I/O
+ * system used by archive readers. This system allows simple
+ * read-ahead for consumers while preserving zero-copy operation
+ * most of the time.
+ *
+ * The two key operations:
+ * * The read-ahead function returns a pointer to a block of data
+ * that satisfies a minimum request.
+ * * The consume function advances the file pointer.
+ *
+ * In the ideal case, filters generate blocks of data
+ * and __archive_read_ahead() just returns pointers directly into
+ * those blocks. Then __archive_read_consume() just bumps those
+ * pointers. Only if your request would span blocks does the I/O
+ * layer use a copy buffer to provide you with a contiguous block of
+ * data.
+ *
+ * A couple of useful idioms:
+ * * "I just want some data." Ask for 1 byte and pay attention to
+ * the "number of bytes available" from __archive_read_ahead().
+ * Consume whatever you actually use.
+ * * "I want to output a large block of data." As above, ask for 1 byte,
+ * emit all that's available (up to whatever limit you have), consume
+ * it all, then repeat until you're done. This effectively means that
+ * you're passing along the blocks that came from your provider.
+ * * "I want to peek ahead by a large amount." Ask for 4k or so, then
+ * double and repeat until you get an error or have enough. Note
+ * that the I/O layer will likely end up expanding its copy buffer
+ * to fit your request, so use this technique cautiously. This
+ * technique is used, for example, by some of the format tasting
+ * code that has uncertain look-ahead needs.
+ */
+
+/*
+ * Looks ahead in the input stream:
+ * * If 'avail' pointer is provided, that returns number of bytes available
+ * in the current buffer, which may be much larger than requested.
+ * * If end-of-file, *avail gets set to zero.
+ * * If error, *avail gets error code.
+ * * If request can be met, returns pointer to data.
+ * * If minimum request cannot be met, returns NULL.
+ *
+ * Note: If you just want "some data", ask for 1 byte and pay attention
+ * to *avail, which will have the actual amount available. If you
+ * know exactly how many bytes you need, just ask for that and treat
+ * a NULL return as an error.
+ *
+ * Important: This does NOT move the file pointer. See
+ * __archive_read_consume() below.
+ */
+const void *
+__archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
+{
+ return (__archive_read_filter_ahead(a->filter, min, avail));
+}
+
+const void *
+__archive_read_filter_ahead(struct archive_read_filter *filter,
+ size_t min, ssize_t *avail)
+{
+ ssize_t bytes_read;
+ size_t tocopy;
+
+ if (filter->fatal) {
+ if (avail)
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+
+ /*
+ * Keep pulling more data until we can satisfy the request.
+ */
+ for (;;) {
+
+ /*
+ * If we can satisfy from the copy buffer (and the
+ * copy buffer isn't empty), we're done. In particular,
+ * note that min == 0 is a perfectly well-defined
+ * request.
+ */
+ if (filter->avail >= min && filter->avail > 0) {
+ if (avail != NULL)
+ *avail = filter->avail;
+ return (filter->next);
+ }
+
+ /*
+ * We can satisfy directly from client buffer if everything
+ * currently in the copy buffer is still in the client buffer.
+ */
+ if (filter->client_total >= filter->client_avail + filter->avail
+ && filter->client_avail + filter->avail >= min) {
+ /* "Roll back" to client buffer. */
+ filter->client_avail += filter->avail;
+ filter->client_next -= filter->avail;
+ /* Copy buffer is now empty. */
+ filter->avail = 0;
+ filter->next = filter->buffer;
+ /* Return data from client buffer. */
+ if (avail != NULL)
+ *avail = filter->client_avail;
+ return (filter->client_next);
+ }
+
+ /* Move data forward in copy buffer if necessary. */
+ if (filter->next > filter->buffer &&
+ filter->next + min > filter->buffer + filter->buffer_size) {
+ if (filter->avail > 0)
+ memmove(filter->buffer, filter->next,
+ filter->avail);
+ filter->next = filter->buffer;
+ }
+
+ /* If we've used up the client data, get more. */
+ if (filter->client_avail <= 0) {
+ if (filter->end_of_file) {
+ if (avail != NULL)
+ *avail = 0;
+ return (NULL);
+ }
+ bytes_read = (filter->vtable->read)(filter,
+ &filter->client_buff);
+ if (bytes_read < 0) { /* Read error. */
+ filter->client_total = filter->client_avail = 0;
+ filter->client_next =
+ filter->client_buff = NULL;
+ filter->fatal = 1;
+ if (avail != NULL)
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ if (bytes_read == 0) {
+ /* Check for another client object first */
+ if (filter->archive->client.cursor !=
+ filter->archive->client.nodes - 1) {
+ if (client_switch_proxy(filter,
+ filter->archive->client.cursor + 1)
+ == ARCHIVE_OK)
+ continue;
+ }
+ /* Premature end-of-file. */
+ filter->client_total = filter->client_avail = 0;
+ filter->client_next =
+ filter->client_buff = NULL;
+ filter->end_of_file = 1;
+ /* Return whatever we do have. */
+ if (avail != NULL)
+ *avail = filter->avail;
+ return (NULL);
+ }
+ filter->client_total = bytes_read;
+ filter->client_avail = filter->client_total;
+ filter->client_next = filter->client_buff;
+ } else {
+ /*
+ * We can't satisfy the request from the copy
+ * buffer or the existing client data, so we
+ * need to copy more client data over to the
+ * copy buffer.
+ */
+
+ /* Ensure the buffer is big enough. */
+ if (min > filter->buffer_size) {
+ size_t s, t;
+ char *p;
+
+ /* Double the buffer; watch for overflow. */
+ s = t = filter->buffer_size;
+ if (s == 0)
+ s = min;
+ while (s < min) {
+ t *= 2;
+ if (t <= s) { /* Integer overflow! */
+ archive_set_error(
+ &filter->archive->archive,
+ ENOMEM,
+ "Unable to allocate copy"
+ " buffer");
+ filter->fatal = 1;
+ if (avail != NULL)
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ s = t;
+ }
+ /* Now s >= min, so allocate a new buffer. */
+ p = (char *)malloc(s);
+ if (p == NULL) {
+ archive_set_error(
+ &filter->archive->archive,
+ ENOMEM,
+ "Unable to allocate copy buffer");
+ filter->fatal = 1;
+ if (avail != NULL)
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ /* Move data into newly-enlarged buffer. */
+ if (filter->avail > 0)
+ memmove(p, filter->next, filter->avail);
+ free(filter->buffer);
+ filter->next = filter->buffer = p;
+ filter->buffer_size = s;
+ }
+
+ /* We can add client data to copy buffer. */
+ /* First estimate: copy to fill rest of buffer. */
+ tocopy = (filter->buffer + filter->buffer_size)
+ - (filter->next + filter->avail);
+ /* Don't waste time buffering more than we need to. */
+ if (tocopy + filter->avail > min)
+ tocopy = min - filter->avail;
+ /* Don't copy more than is available. */
+ if (tocopy > filter->client_avail)
+ tocopy = filter->client_avail;
+
+ memcpy(filter->next + filter->avail,
+ filter->client_next, tocopy);
+ /* Remove this data from client buffer. */
+ filter->client_next += tocopy;
+ filter->client_avail -= tocopy;
+ /* add it to copy buffer. */
+ filter->avail += tocopy;
+ }
+ }
+}
+
+/*
+ * Move the file pointer forward.
+ */
+int64_t
+__archive_read_consume(struct archive_read *a, int64_t request)
+{
+ return (__archive_read_filter_consume(a->filter, request));
+}
+
+int64_t
+__archive_read_filter_consume(struct archive_read_filter * filter,
+ int64_t request)
+{
+ int64_t skipped;
+
+ if (request < 0)
+ return ARCHIVE_FATAL;
+ if (request == 0)
+ return 0;
+
+ skipped = advance_file_pointer(filter, request);
+ if (skipped == request)
+ return (skipped);
+ /* We hit EOF before we satisfied the skip request. */
+ if (skipped < 0) /* Map error code to 0 for error message below. */
+ skipped = 0;
+ archive_set_error(&filter->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Truncated input file (needed %jd bytes, only %jd available)",
+ (intmax_t)request, (intmax_t)skipped);
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Advance the file pointer by the amount requested.
+ * Returns the amount actually advanced, which may be less than the
+ * request if EOF is encountered first.
+ * Returns a negative value if there's an I/O error.
+ */
+static int64_t
+advance_file_pointer(struct archive_read_filter *filter, int64_t request)
+{
+ int64_t bytes_skipped, total_bytes_skipped = 0;
+ ssize_t bytes_read;
+ size_t min;
+
+ if (filter->fatal)
+ return (-1);
+
+ /* Use up the copy buffer first. */
+ if (filter->avail > 0) {
+ min = (size_t)minimum(request, (int64_t)filter->avail);
+ filter->next += min;
+ filter->avail -= min;
+ request -= min;
+ filter->position += min;
+ total_bytes_skipped += min;
+ }
+
+ /* Then use up the client buffer. */
+ if (filter->client_avail > 0) {
+ min = (size_t)minimum(request, (int64_t)filter->client_avail);
+ filter->client_next += min;
+ filter->client_avail -= min;
+ request -= min;
+ filter->position += min;
+ total_bytes_skipped += min;
+ }
+ if (request == 0)
+ return (total_bytes_skipped);
+
+ /* If there's an optimized skip function, use it. */
+ if (filter->can_skip != 0) {
+ bytes_skipped = client_skip_proxy(filter, request);
+ if (bytes_skipped < 0) { /* error */
+ filter->fatal = 1;
+ return (bytes_skipped);
+ }
+ filter->position += bytes_skipped;
+ total_bytes_skipped += bytes_skipped;
+ request -= bytes_skipped;
+ if (request == 0)
+ return (total_bytes_skipped);
+ }
+
+ /* Use ordinary reads as necessary to complete the request. */
+ for (;;) {
+ bytes_read = (filter->vtable->read)(filter, &filter->client_buff);
+ if (bytes_read < 0) {
+ filter->client_buff = NULL;
+ filter->fatal = 1;
+ return (bytes_read);
+ }
+
+ if (bytes_read == 0) {
+ if (filter->archive->client.cursor !=
+ filter->archive->client.nodes - 1) {
+ if (client_switch_proxy(filter,
+ filter->archive->client.cursor + 1)
+ == ARCHIVE_OK)
+ continue;
+ }
+ filter->client_buff = NULL;
+ filter->end_of_file = 1;
+ return (total_bytes_skipped);
+ }
+
+ if (bytes_read >= request) {
+ filter->client_next =
+ ((const char *)filter->client_buff) + request;
+ filter->client_avail = (size_t)(bytes_read - request);
+ filter->client_total = bytes_read;
+ total_bytes_skipped += request;
+ filter->position += request;
+ return (total_bytes_skipped);
+ }
+
+ filter->position += bytes_read;
+ total_bytes_skipped += bytes_read;
+ request -= bytes_read;
+ }
+}
+
+/**
+ * Returns ARCHIVE_FAILED if seeking isn't supported.
+ */
+int64_t
+__archive_read_seek(struct archive_read *a, int64_t offset, int whence)
+{
+ return __archive_read_filter_seek(a->filter, offset, whence);
+}
+
+int64_t
+__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
+ int whence)
+{
+ struct archive_read_client *client;
+ int64_t r;
+ unsigned int cursor;
+
+ if (filter->closed || filter->fatal)
+ return (ARCHIVE_FATAL);
+ if (filter->can_seek == 0)
+ return (ARCHIVE_FAILED);
+
+ client = &(filter->archive->client);
+ switch (whence) {
+ case SEEK_CUR:
+ /* Adjust the offset and use SEEK_SET instead */
+ offset += filter->position;
+ __LA_FALLTHROUGH;
+ case SEEK_SET:
+ cursor = 0;
+ while (1)
+ {
+ if (client->dataset[cursor].begin_position < 0 ||
+ client->dataset[cursor].total_size < 0 ||
+ client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size - 1 > offset ||
+ cursor + 1 >= client->nodes)
+ break;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ client->dataset[++cursor].begin_position = r;
+ }
+ while (1) {
+ r = client_switch_proxy(filter, cursor);
+ if (r != ARCHIVE_OK)
+ return r;
+ if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+ return r;
+ client->dataset[cursor].total_size = r;
+ if (client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size - 1 > offset ||
+ cursor + 1 >= client->nodes)
+ break;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ client->dataset[++cursor].begin_position = r;
+ }
+ offset -= client->dataset[cursor].begin_position;
+ if (offset < 0
+ || offset > client->dataset[cursor].total_size)
+ return ARCHIVE_FATAL;
+ if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0)
+ return r;
+ break;
+
+ case SEEK_END:
+ cursor = 0;
+ while (1) {
+ if (client->dataset[cursor].begin_position < 0 ||
+ client->dataset[cursor].total_size < 0 ||
+ cursor + 1 >= client->nodes)
+ break;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ client->dataset[++cursor].begin_position = r;
+ }
+ while (1) {
+ r = client_switch_proxy(filter, cursor);
+ if (r != ARCHIVE_OK)
+ return r;
+ if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+ return r;
+ client->dataset[cursor].total_size = r;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ if (cursor + 1 >= client->nodes)
+ break;
+ client->dataset[++cursor].begin_position = r;
+ }
+ while (1) {
+ if (r + offset >=
+ client->dataset[cursor].begin_position)
+ break;
+ offset += client->dataset[cursor].total_size;
+ if (cursor == 0)
+ break;
+ cursor--;
+ r = client->dataset[cursor].begin_position +
+ client->dataset[cursor].total_size;
+ }
+ offset = (r + offset) - client->dataset[cursor].begin_position;
+ if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK)
+ return r;
+ r = client_seek_proxy(filter, offset, SEEK_SET);
+ if (r < ARCHIVE_OK)
+ return r;
+ break;
+
+ default:
+ return (ARCHIVE_FATAL);
+ }
+ r += client->dataset[cursor].begin_position;
+
+ if (r >= 0) {
+ /*
+ * Ouch. Clearing the buffer like this hurts, especially
+ * at bid time. A lot of our efficiency at bid time comes
+ * from having bidders reuse the data we've already read.
+ *
+ * TODO: If the seek request is in data we already
+ * have, then don't call the seek callback.
+ *
+ * TODO: Zip seeks to end-of-file at bid time. If
+ * other formats also start doing this, we may need to
+ * find a way for clients to fudge the seek offset to
+ * a block boundary.
+ *
+ * Hmmm... If whence was SEEK_END, we know the file
+ * size is (r - offset). Can we use that to simplify
+ * the TODO items above?
+ */
+ filter->avail = filter->client_avail = 0;
+ filter->next = filter->buffer;
+ filter->position = r;
+ filter->end_of_file = 0;
+ }
+ return r;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_add_passphrase.c b/contrib/libs/libarchive/libarchive/archive_read_add_passphrase.c
new file mode 100644
index 0000000000..f0b1ab9330
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_add_passphrase.c
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "archive_read_private.h"
+
+static void
+add_passphrase_to_tail(struct archive_read *a,
+ struct archive_read_passphrase *p)
+{
+ *a->passphrases.last = p;
+ a->passphrases.last = &p->next;
+ p->next = NULL;
+}
+
+static struct archive_read_passphrase *
+remove_passphrases_from_head(struct archive_read *a)
+{
+ struct archive_read_passphrase *p;
+
+ p = a->passphrases.first;
+ if (p != NULL)
+ a->passphrases.first = p->next;
+ return (p);
+}
+
+static void
+insert_passphrase_to_head(struct archive_read *a,
+ struct archive_read_passphrase *p)
+{
+ p->next = a->passphrases.first;
+ a->passphrases.first = p;
+ if (&a->passphrases.first == a->passphrases.last) {
+ a->passphrases.last = &p->next;
+ p->next = NULL;
+ }
+}
+
+static struct archive_read_passphrase *
+new_read_passphrase(struct archive_read *a, const char *passphrase)
+{
+ struct archive_read_passphrase *p;
+
+ p = malloc(sizeof(*p));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (NULL);
+ }
+ p->passphrase = strdup(passphrase);
+ if (p->passphrase == NULL) {
+ free(p);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (NULL);
+ }
+ return (p);
+}
+
+int
+archive_read_add_passphrase(struct archive *_a, const char *passphrase)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_passphrase *p;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_add_passphrase");
+
+ if (passphrase == NULL || passphrase[0] == '\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Empty passphrase is unacceptable");
+ return (ARCHIVE_FAILED);
+ }
+
+ p = new_read_passphrase(a, passphrase);
+ if (p == NULL)
+ return (ARCHIVE_FATAL);
+ add_passphrase_to_tail(a, p);
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_set_passphrase_callback(struct archive *_a, void *client_data,
+ archive_passphrase_callback *cb)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_set_passphrase_callback");
+
+ a->passphrases.callback = cb;
+ a->passphrases.client_data = client_data;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Call this in advance when you start to get a passphrase for decryption
+ * for a entry.
+ */
+void
+__archive_read_reset_passphrase(struct archive_read *a)
+{
+
+ a->passphrases.candidate = -1;
+}
+
+/*
+ * Get a passphrase for decryption.
+ */
+const char *
+__archive_read_next_passphrase(struct archive_read *a)
+{
+ struct archive_read_passphrase *p;
+ const char *passphrase;
+
+ if (a->passphrases.candidate < 0) {
+ /* Count out how many passphrases we have. */
+ int cnt = 0;
+
+ for (p = a->passphrases.first; p != NULL; p = p->next)
+ cnt++;
+ a->passphrases.candidate = cnt;
+ p = a->passphrases.first;
+ } else if (a->passphrases.candidate > 1) {
+ /* Rotate a passphrase list. */
+ a->passphrases.candidate--;
+ p = remove_passphrases_from_head(a);
+ add_passphrase_to_tail(a, p);
+ /* Pick a new passphrase candidate up. */
+ p = a->passphrases.first;
+ } else if (a->passphrases.candidate == 1) {
+ /* This case is that all candidates failed to decrypt. */
+ a->passphrases.candidate = 0;
+ if (a->passphrases.first->next != NULL) {
+ /* Rotate a passphrase list. */
+ p = remove_passphrases_from_head(a);
+ add_passphrase_to_tail(a, p);
+ }
+ p = NULL;
+ } else /* There is no passphrase candidate. */
+ p = NULL;
+
+ if (p != NULL)
+ passphrase = p->passphrase;
+ else if (a->passphrases.callback != NULL) {
+ /* Get a passphrase through a call-back function
+ * since we tried all passphrases out or we don't
+ * have it. */
+ passphrase = a->passphrases.callback(&a->archive,
+ a->passphrases.client_data);
+ if (passphrase != NULL) {
+ p = new_read_passphrase(a, passphrase);
+ if (p == NULL)
+ return (NULL);
+ insert_passphrase_to_head(a, p);
+ a->passphrases.candidate = 1;
+ }
+ } else
+ passphrase = NULL;
+
+ return (passphrase);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_append_filter.c b/contrib/libs/libarchive/libarchive/archive_read_append_filter.c
new file mode 100644
index 0000000000..25dc4b2a2b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_append_filter.c
@@ -0,0 +1,204 @@
+/*-
+ * Copyright (c) 2003-2012 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_append_filter(struct archive *_a, int code)
+{
+ int r1, r2, number_bidders, i;
+ char str[20];
+ struct archive_read_filter_bidder *bidder;
+ struct archive_read_filter *filter;
+ struct archive_read *a = (struct archive_read *)_a;
+
+ r2 = (ARCHIVE_OK);
+ switch (code)
+ {
+ case ARCHIVE_FILTER_NONE:
+ /* No filter to add, so do nothing.
+ * NOTE: An initial "NONE" type filter is always set at the end of the
+ * filter chain.
+ */
+ r1 = (ARCHIVE_OK);
+ break;
+ case ARCHIVE_FILTER_GZIP:
+ strcpy(str, "gzip");
+ r1 = archive_read_support_filter_gzip(_a);
+ break;
+ case ARCHIVE_FILTER_BZIP2:
+ strcpy(str, "bzip2");
+ r1 = archive_read_support_filter_bzip2(_a);
+ break;
+ case ARCHIVE_FILTER_COMPRESS:
+ strcpy(str, "compress (.Z)");
+ r1 = archive_read_support_filter_compress(_a);
+ break;
+ case ARCHIVE_FILTER_PROGRAM:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Cannot append program filter using archive_read_append_filter");
+ return (ARCHIVE_FATAL);
+ case ARCHIVE_FILTER_LZMA:
+ strcpy(str, "lzma");
+ r1 = archive_read_support_filter_lzma(_a);
+ break;
+ case ARCHIVE_FILTER_XZ:
+ strcpy(str, "xz");
+ r1 = archive_read_support_filter_xz(_a);
+ break;
+ case ARCHIVE_FILTER_UU:
+ strcpy(str, "uu");
+ r1 = archive_read_support_filter_uu(_a);
+ break;
+ case ARCHIVE_FILTER_RPM:
+ strcpy(str, "rpm");
+ r1 = archive_read_support_filter_rpm(_a);
+ break;
+ case ARCHIVE_FILTER_LZ4:
+ strcpy(str, "lz4");
+ r1 = archive_read_support_filter_lz4(_a);
+ break;
+ case ARCHIVE_FILTER_ZSTD:
+ strcpy(str, "zstd");
+ r1 = archive_read_support_filter_zstd(_a);
+ break;
+ case ARCHIVE_FILTER_LZIP:
+ strcpy(str, "lzip");
+ r1 = archive_read_support_filter_lzip(_a);
+ break;
+ case ARCHIVE_FILTER_LRZIP:
+ strcpy(str, "lrzip");
+ r1 = archive_read_support_filter_lrzip(_a);
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Invalid filter code specified");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (code != ARCHIVE_FILTER_NONE)
+ {
+ number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ bidder = a->bidders;
+ for (i = 0; i < number_bidders; i++, bidder++)
+ {
+ if (!bidder->name || !strcmp(bidder->name, str))
+ break;
+ }
+ if (!bidder->name || strcmp(bidder->name, str))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Unable to append filter");
+ return (ARCHIVE_FATAL);
+ }
+
+ filter
+ = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+ if (filter == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ filter->bidder = bidder;
+ filter->archive = a;
+ filter->upstream = a->filter;
+ a->filter = filter;
+ r2 = (bidder->vtable->init)(a->filter);
+ if (r2 != ARCHIVE_OK) {
+ __archive_read_free_filters(a);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ a->bypass_filter_bidding = 1;
+ return (r1 < r2) ? r1 : r2;
+}
+
+int
+archive_read_append_filter_program(struct archive *_a, const char *cmd)
+{
+ return (archive_read_append_filter_program_signature(_a, cmd, NULL, 0));
+}
+
+int
+archive_read_append_filter_program_signature(struct archive *_a,
+ const char *cmd, const void *signature, size_t signature_len)
+{
+ int r, number_bidders, i;
+ struct archive_read_filter_bidder *bidder;
+ struct archive_read_filter *filter;
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (archive_read_support_filter_program_signature(_a, cmd, signature,
+ signature_len) != (ARCHIVE_OK))
+ return (ARCHIVE_FATAL);
+
+ number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ bidder = a->bidders;
+ for (i = 0; i < number_bidders; i++, bidder++)
+ {
+ /* Program bidder name set to filter name after initialization */
+ if (bidder->data && !bidder->name)
+ break;
+ }
+ if (!bidder->data)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Unable to append program filter");
+ return (ARCHIVE_FATAL);
+ }
+
+ filter
+ = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+ if (filter == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ filter->bidder = bidder;
+ filter->archive = a;
+ filter->upstream = a->filter;
+ a->filter = filter;
+ r = (bidder->vtable->init)(a->filter);
+ if (r != ARCHIVE_OK) {
+ __archive_read_free_filters(a);
+ return (ARCHIVE_FATAL);
+ }
+ bidder->name = a->filter->name;
+
+ a->bypass_filter_bidding = 1;
+ return r;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_data_into_fd.c b/contrib/libs/libarchive/libarchive/archive_read_data_into_fd.c
new file mode 100644
index 0000000000..f16ca5c82b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_data_into_fd.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.16 2008/05/23 05:01:29 cperciva Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* Maximum amount of data to write at one time. */
+#define MAX_WRITE (1024 * 1024)
+
+/*
+ * This implementation minimizes copying of data and is sparse-file aware.
+ */
+static int
+pad_to(struct archive *a, int fd, int can_lseek,
+ size_t nulls_size, const char *nulls,
+ int64_t target_offset, int64_t actual_offset)
+{
+ size_t to_write;
+ ssize_t bytes_written;
+
+ if (can_lseek) {
+ actual_offset = lseek(fd,
+ target_offset - actual_offset, SEEK_CUR);
+ if (actual_offset != target_offset) {
+ archive_set_error(a, errno, "Seek error");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+ }
+ while (target_offset > actual_offset) {
+ to_write = nulls_size;
+ if (target_offset < actual_offset + (int64_t)nulls_size)
+ to_write = (size_t)(target_offset - actual_offset);
+ bytes_written = write(fd, nulls, to_write);
+ if (bytes_written < 0) {
+ archive_set_error(a, errno, "Write error");
+ return (ARCHIVE_FATAL);
+ }
+ actual_offset += bytes_written;
+ }
+ return (ARCHIVE_OK);
+}
+
+
+int
+archive_read_data_into_fd(struct archive *a, int fd)
+{
+ struct stat st;
+ int r, r2;
+ const void *buff;
+ size_t size, bytes_to_write;
+ ssize_t bytes_written;
+ int64_t target_offset;
+ int64_t actual_offset = 0;
+ int can_lseek;
+ char *nulls = NULL;
+ size_t nulls_size = 16384;
+
+ archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_into_fd");
+
+ can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);
+ if (!can_lseek) {
+ nulls = calloc(1, nulls_size);
+ if (!nulls) {
+ r = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ }
+
+ while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==
+ ARCHIVE_OK) {
+ const char *p = buff;
+ if (target_offset > actual_offset) {
+ r = pad_to(a, fd, can_lseek, nulls_size, nulls,
+ target_offset, actual_offset);
+ if (r != ARCHIVE_OK)
+ break;
+ actual_offset = target_offset;
+ }
+ while (size > 0) {
+ bytes_to_write = size;
+ if (bytes_to_write > MAX_WRITE)
+ bytes_to_write = MAX_WRITE;
+ bytes_written = write(fd, p, bytes_to_write);
+ if (bytes_written < 0) {
+ archive_set_error(a, errno, "Write error");
+ r = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ actual_offset += bytes_written;
+ p += bytes_written;
+ size -= bytes_written;
+ }
+ }
+
+ if (r == ARCHIVE_EOF && target_offset > actual_offset) {
+ r2 = pad_to(a, fd, can_lseek, nulls_size, nulls,
+ target_offset, actual_offset);
+ if (r2 != ARCHIVE_OK)
+ r = r2;
+ }
+
+cleanup:
+ free(nulls);
+ if (r != ARCHIVE_EOF)
+ return (r);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_entry_from_file.c b/contrib/libs/libarchive/libarchive/archive_read_disk_entry_from_file.c
new file mode 100644
index 0000000000..bff25b627c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_entry_from_file.c
@@ -0,0 +1,1086 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD");
+
+/* This is the tree-walking code for POSIX systems. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if defined(HAVE_SYS_XATTR_H)
+#include <sys/xattr.h>
+#elif defined(HAVE_ATTR_XATTR_H)
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_SYS_EA_H
+#error #include <sys/ea.h>
+#endif
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#ifdef HAVE_LINUX_FIEMAP_H
+#include <linux/fiemap.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+static int setup_mac_metadata(struct archive_read_disk *,
+ struct archive_entry *, int *fd);
+#ifdef ARCHIVE_XATTR_FREEBSD
+static int setup_xattrs_namespace(struct archive_read_disk *,
+ struct archive_entry *, int *, int);
+#endif
+static int setup_xattrs(struct archive_read_disk *,
+ struct archive_entry *, int *fd);
+static int setup_sparse(struct archive_read_disk *,
+ struct archive_entry *, int *fd);
+#if defined(HAVE_LINUX_FIEMAP_H)
+static int setup_sparse_fiemap(struct archive_read_disk *,
+ struct archive_entry *, int *fd);
+#endif
+
+#if !ARCHIVE_ACL_SUPPORT
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Enter working directory and return working pathname of archive_entry.
+ * If a pointer to an integer is provided and its value is below zero
+ * open a file descriptor on this pathname.
+ */
+const char *
+archive_read_disk_entry_setup_path(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ const char *path;
+
+ path = archive_entry_sourcepath(entry);
+
+ if (path == NULL || (a->tree != NULL &&
+ a->tree_enter_working_dir(a->tree) != 0))
+ path = archive_entry_pathname(entry);
+ if (path == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Couldn't determine path");
+ } else if (fd != NULL && *fd < 0 && a->tree != NULL &&
+ (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) {
+ *fd = a->open_on_current_dir(a->tree, path,
+ O_RDONLY | O_NONBLOCK);
+ }
+ return (path);
+}
+
+int
+archive_read_disk_entry_from_file(struct archive *_a,
+ struct archive_entry *entry,
+ int fd,
+ const struct stat *st)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ const char *path, *name;
+ struct stat s;
+ int initial_fd = fd;
+ int r, r1;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+ "archive_read_disk_entry_from_file");
+
+ archive_clear_error(_a);
+ path = archive_entry_sourcepath(entry);
+ if (path == NULL)
+ path = archive_entry_pathname(entry);
+
+ if (a->tree == NULL) {
+ if (st == NULL) {
+#if HAVE_FSTAT
+ if (fd >= 0) {
+ if (fstat(fd, &s) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't fstat");
+ return (ARCHIVE_FAILED);
+ }
+ } else
+#endif
+#if HAVE_LSTAT
+ if (!a->follow_symlinks) {
+ if (lstat(path, &s) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't lstat %s", path);
+ return (ARCHIVE_FAILED);
+ }
+ } else
+#endif
+ if (la_stat(path, &s) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't stat %s", path);
+ return (ARCHIVE_FAILED);
+ }
+ st = &s;
+ }
+ archive_entry_copy_stat(entry, st);
+ }
+
+ /* Lookup uname/gname */
+ name = archive_read_disk_uname(_a, archive_entry_uid(entry));
+ if (name != NULL)
+ archive_entry_copy_uname(entry, name);
+ name = archive_read_disk_gname(_a, archive_entry_gid(entry));
+ if (name != NULL)
+ archive_entry_copy_gname(entry, name);
+
+#ifdef HAVE_STRUCT_STAT_ST_FLAGS
+ /* On FreeBSD, we get flags for free with the stat. */
+ /* TODO: Does this belong in copy_stat()? */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0)
+ archive_entry_set_fflags(entry, st->st_flags, 0);
+#endif
+
+#if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+ /* Linux requires an extra ioctl to pull the flags. Although
+ * this is an extra step, it has a nice side-effect: We get an
+ * open file descriptor which we can use in the subsequent lookups. */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 &&
+ (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
+ if (fd < 0) {
+ if (a->tree != NULL)
+ fd = a->open_on_current_dir(a->tree, path,
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ else
+ fd = open(path, O_RDONLY | O_NONBLOCK |
+ O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ }
+ if (fd >= 0) {
+ int stflags;
+ r = ioctl(fd,
+#if defined(FS_IOC_GETFLAGS)
+ FS_IOC_GETFLAGS,
+#else
+ EXT2_IOC_GETFLAGS,
+#endif
+ &stflags);
+ if (r == 0 && stflags != 0)
+ archive_entry_set_fflags(entry, stflags, 0);
+ }
+ }
+#endif
+
+#if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)
+ if (S_ISLNK(st->st_mode)) {
+ size_t linkbuffer_len = st->st_size;
+ char *linkbuffer;
+ int lnklen;
+
+ linkbuffer = malloc(linkbuffer_len + 1);
+ if (linkbuffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't read link data");
+ return (ARCHIVE_FAILED);
+ }
+ if (a->tree != NULL) {
+#ifdef HAVE_READLINKAT
+ lnklen = readlinkat(a->tree_current_dir_fd(a->tree),
+ path, linkbuffer, linkbuffer_len);
+#else
+ if (a->tree_enter_working_dir(a->tree) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't read link data");
+ free(linkbuffer);
+ return (ARCHIVE_FAILED);
+ }
+ lnklen = readlink(path, linkbuffer, linkbuffer_len);
+#endif /* HAVE_READLINKAT */
+ } else
+ lnklen = readlink(path, linkbuffer, linkbuffer_len);
+ if (lnklen < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't read link data");
+ free(linkbuffer);
+ return (ARCHIVE_FAILED);
+ }
+ linkbuffer[lnklen] = '\0';
+ archive_entry_set_symlink(entry, linkbuffer);
+ free(linkbuffer);
+ }
+#endif /* HAVE_READLINK || HAVE_READLINKAT */
+
+ r = 0;
+ if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0)
+ r = archive_read_disk_entry_setup_acls(a, entry, &fd);
+ if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) {
+ r1 = setup_xattrs(a, entry, &fd);
+ if (r1 < r)
+ r = r1;
+ }
+ if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
+ r1 = setup_mac_metadata(a, entry, &fd);
+ if (r1 < r)
+ r = r1;
+ }
+ if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+ r1 = setup_sparse(a, entry, &fd);
+ if (r1 < r)
+ r = r1;
+ }
+
+ /* If we opened the file earlier in this function, close it. */
+ if (initial_fd != fd)
+ close(fd);
+ return (r);
+}
+
+#if defined(__APPLE__) && defined(HAVE_COPYFILE_H)
+/*
+ * The Mac OS "copyfile()" API copies the extended metadata for a
+ * file into a separate file in AppleDouble format (see RFC 1740).
+ *
+ * Mac OS tar and cpio implementations store this extended
+ * metadata as a separate entry just before the regular entry
+ * with a "._" prefix added to the filename.
+ *
+ * Note that this is currently done unconditionally; the tar program has
+ * an option to discard this information before the archive is written.
+ *
+ * TODO: If there's a failure, report it and return ARCHIVE_WARN.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ int tempfd = -1;
+ int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
+ struct stat copyfile_stat;
+ int ret = ARCHIVE_OK;
+ void *buff = NULL;
+ int have_attrs;
+ const char *name, *tempdir;
+ struct archive_string tempfile;
+
+ (void)fd; /* UNUSED */
+
+ name = archive_read_disk_entry_setup_path(a, entry, NULL);
+ if (name == NULL)
+ return (ARCHIVE_WARN);
+
+ /* Short-circuit if there's nothing to do. */
+ have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
+ if (have_attrs == -1) {
+ archive_set_error(&a->archive, errno,
+ "Could not check extended attributes");
+ return (ARCHIVE_WARN);
+ }
+ if (have_attrs == 0)
+ return (ARCHIVE_OK);
+
+ tempdir = NULL;
+ if (issetugid() == 0)
+ tempdir = getenv("TMPDIR");
+ if (tempdir == NULL)
+ tempdir = _PATH_TMP;
+ archive_string_init(&tempfile);
+ archive_strcpy(&tempfile, tempdir);
+ archive_strcat(&tempfile, "tar.md.XXXXXX");
+ tempfd = mkstemp(tempfile.s);
+ if (tempfd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Could not open extended attribute file");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ __archive_ensure_cloexec_flag(tempfd);
+
+ /* XXX I wish copyfile() could pack directly to a memory
+ * buffer; that would avoid the temp file here. For that
+ * matter, it would be nice if fcopyfile() actually worked,
+ * that would reduce the many open/close races here. */
+ if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) {
+ archive_set_error(&a->archive, errno,
+ "Could not pack extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ if (fstat(tempfd, &copyfile_stat)) {
+ archive_set_error(&a->archive, errno,
+ "Could not check size of extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ buff = malloc(copyfile_stat.st_size);
+ if (buff == NULL) {
+ archive_set_error(&a->archive, errno,
+ "Could not allocate memory for extended attributes");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) {
+ archive_set_error(&a->archive, errno,
+ "Could not read extended attributes into memory");
+ ret = ARCHIVE_WARN;
+ goto cleanup;
+ }
+ archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);
+
+cleanup:
+ if (tempfd >= 0) {
+ close(tempfd);
+ unlink(tempfile.s);
+ }
+ archive_string_free(&tempfile);
+ free(buff);
+ return (ret);
+}
+
+#else
+
+/*
+ * Stub implementation for non-Mac systems.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#endif
+
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
+
+/*
+ * Linux, Darwin and AIX extended attribute support.
+ *
+ * TODO: By using a stack-allocated buffer for the first
+ * call to getxattr(), we might be able to avoid the second
+ * call entirely. We only need the second call if the
+ * stack-allocated buffer is too small. But a modest buffer
+ * of 1024 bytes or so will often be big enough. Same applies
+ * to listxattr().
+ */
+
+
+static int
+setup_xattr(struct archive_read_disk *a,
+ struct archive_entry *entry, const char *name, int fd, const char *accpath)
+{
+ ssize_t size;
+ void *value = NULL;
+
+
+ if (fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ size = fgetxattr(fd, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ size = fgetxattr(fd, name, NULL, 0, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ size = fgetea(fd, name, NULL, 0);
+#endif
+ } else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+ size = lgetxattr(accpath, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ size = lgetea(accpath, name, NULL, 0);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ size = getxattr(accpath, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ size = getxattr(accpath, name, NULL, 0, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ size = getea(accpath, name, NULL, 0);
+#endif
+ }
+
+ if (size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't query extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ if (size > 0 && (value = malloc(size)) == NULL) {
+ archive_set_error(&a->archive, errno, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+
+ if (fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ size = fgetxattr(fd, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+ size = fgetxattr(fd, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ size = fgetea(fd, name, value, size);
+#endif
+ } else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+ size = lgetxattr(accpath, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+ size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ size = lgetea(accpath, name, value, size);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ size = getxattr(accpath, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+ size = getxattr(accpath, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ size = getea(accpath, name, value, size);
+#endif
+ }
+
+ if (size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't read extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ archive_entry_xattr_add_entry(entry, name, value, size);
+
+ free(value);
+ return (ARCHIVE_OK);
+}
+
+static int
+setup_xattrs(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ char *list, *p;
+ const char *path;
+ ssize_t list_size;
+
+ path = NULL;
+
+ if (*fd < 0) {
+ path = archive_read_disk_entry_setup_path(a, entry, fd);
+ if (path == NULL)
+ return (ARCHIVE_WARN);
+ }
+
+ if (*fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ list_size = flistxattr(*fd, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = flistxattr(*fd, NULL, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ list_size = flistea(*fd, NULL, 0);
+#endif
+ } else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+ list_size = llistxattr(path, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ list_size = llistea(path, NULL, 0);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ list_size = listxattr(path, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = listxattr(path, NULL, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ list_size = listea(path, NULL, 0);
+#endif
+ }
+
+ if (list_size == -1) {
+ if (errno == ENOTSUP || errno == ENOSYS)
+ return (ARCHIVE_OK);
+ archive_set_error(&a->archive, errno,
+ "Couldn't list extended attributes");
+ return (ARCHIVE_WARN);
+ }
+
+ if (list_size == 0)
+ return (ARCHIVE_OK);
+
+ if ((list = malloc(list_size)) == NULL) {
+ archive_set_error(&a->archive, errno, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (*fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ list_size = flistxattr(*fd, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = flistxattr(*fd, list, list_size, 0);
+#elif ARCHIVE_XATTR_AIX
+ list_size = flistea(*fd, list, list_size);
+#endif
+ } else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+ list_size = llistxattr(path, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ list_size = llistea(path, list, list_size);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ list_size = listxattr(path, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+ list_size = listxattr(path, list, list_size, 0);
+#elif ARCHIVE_XATTR_AIX
+ list_size = listea(path, list, list_size);
+#endif
+ }
+
+ if (list_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't retrieve extended attributes");
+ free(list);
+ return (ARCHIVE_WARN);
+ }
+
+ for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
+#if ARCHIVE_XATTR_LINUX
+ /* Linux: skip POSIX.1e ACL extended attributes */
+ if (strncmp(p, "system.", 7) == 0 &&
+ (strcmp(p + 7, "posix_acl_access") == 0 ||
+ strcmp(p + 7, "posix_acl_default") == 0))
+ continue;
+ if (strncmp(p, "trusted.SGI_", 12) == 0 &&
+ (strcmp(p + 12, "ACL_DEFAULT") == 0 ||
+ strcmp(p + 12, "ACL_FILE") == 0))
+ continue;
+
+ /* Linux: xfsroot namespace is obsolete and unsupported */
+ if (strncmp(p, "xfsroot.", 8) == 0)
+ continue;
+#endif
+ setup_xattr(a, entry, p, *fd, path);
+ }
+
+ free(list);
+ return (ARCHIVE_OK);
+}
+
+#elif ARCHIVE_XATTR_FREEBSD
+
+/*
+ * FreeBSD extattr interface.
+ */
+
+/* TODO: Implement this. Follow the Linux model above, but
+ * with FreeBSD-specific system calls, of course. Be careful
+ * to not include the system extattrs that hold ACLs; we handle
+ * those separately.
+ */
+static int
+setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
+ int namespace, const char *name, const char *fullname, int fd,
+ const char *path);
+
+static int
+setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
+ int namespace, const char *name, const char *fullname, int fd,
+ const char *accpath)
+{
+ ssize_t size;
+ void *value = NULL;
+
+ if (fd >= 0)
+ size = extattr_get_fd(fd, namespace, name, NULL, 0);
+ else if (!a->follow_symlinks)
+ size = extattr_get_link(accpath, namespace, name, NULL, 0);
+ else
+ size = extattr_get_file(accpath, namespace, name, NULL, 0);
+
+ if (size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't query extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ if (size > 0 && (value = malloc(size)) == NULL) {
+ archive_set_error(&a->archive, errno, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (fd >= 0)
+ size = extattr_get_fd(fd, namespace, name, value, size);
+ else if (!a->follow_symlinks)
+ size = extattr_get_link(accpath, namespace, name, value, size);
+ else
+ size = extattr_get_file(accpath, namespace, name, value, size);
+
+ if (size == -1) {
+ free(value);
+ archive_set_error(&a->archive, errno,
+ "Couldn't read extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ archive_entry_xattr_add_entry(entry, fullname, value, size);
+
+ free(value);
+ return (ARCHIVE_OK);
+}
+
+static int
+setup_xattrs_namespace(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd, int namespace)
+{
+ char buff[512];
+ char *list, *p;
+ ssize_t list_size;
+ const char *path;
+
+ path = NULL;
+
+ if (*fd < 0) {
+ path = archive_read_disk_entry_setup_path(a, entry, fd);
+ if (path == NULL)
+ return (ARCHIVE_WARN);
+ }
+
+ if (*fd >= 0)
+ list_size = extattr_list_fd(*fd, namespace, NULL, 0);
+ else if (!a->follow_symlinks)
+ list_size = extattr_list_link(path, namespace, NULL, 0);
+ else
+ list_size = extattr_list_file(path, namespace, NULL, 0);
+
+ if (list_size == -1 && errno == EOPNOTSUPP)
+ return (ARCHIVE_OK);
+ if (list_size == -1 && errno == EPERM)
+ return (ARCHIVE_OK);
+ if (list_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't list extended attributes");
+ return (ARCHIVE_WARN);
+ }
+
+ if (list_size == 0)
+ return (ARCHIVE_OK);
+
+ if ((list = malloc(list_size)) == NULL) {
+ archive_set_error(&a->archive, errno, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (*fd >= 0)
+ list_size = extattr_list_fd(*fd, namespace, list, list_size);
+ else if (!a->follow_symlinks)
+ list_size = extattr_list_link(path, namespace, list, list_size);
+ else
+ list_size = extattr_list_file(path, namespace, list, list_size);
+
+ if (list_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't retrieve extended attributes");
+ free(list);
+ return (ARCHIVE_WARN);
+ }
+
+ p = list;
+ while ((p - list) < list_size) {
+ size_t len = 255 & (int)*p;
+ char *name;
+
+ if (namespace == EXTATTR_NAMESPACE_SYSTEM) {
+ if (!strcmp(p + 1, "nfs4.acl") ||
+ !strcmp(p + 1, "posix1e.acl_access") ||
+ !strcmp(p + 1, "posix1e.acl_default")) {
+ p += 1 + len;
+ continue;
+ }
+ strcpy(buff, "system.");
+ } else {
+ strcpy(buff, "user.");
+ }
+ name = buff + strlen(buff);
+ memcpy(name, p + 1, len);
+ name[len] = '\0';
+ setup_xattr(a, entry, namespace, name, buff, *fd, path);
+ p += 1 + len;
+ }
+
+ free(list);
+ return (ARCHIVE_OK);
+}
+
+static int
+setup_xattrs(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ int namespaces[2];
+ int i, res;
+
+ namespaces[0] = EXTATTR_NAMESPACE_USER;
+ namespaces[1] = EXTATTR_NAMESPACE_SYSTEM;
+
+ for (i = 0; i < 2; i++) {
+ res = setup_xattrs_namespace(a, entry, fd,
+ namespaces[i]);
+ switch (res) {
+ case (ARCHIVE_OK):
+ case (ARCHIVE_WARN):
+ break;
+ default:
+ return (res);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ * Generic (stub) extended attribute support.
+ */
+static int
+setup_xattrs(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif
+
+#if defined(HAVE_LINUX_FIEMAP_H)
+
+/*
+ * Linux FIEMAP sparse interface.
+ *
+ * The FIEMAP ioctl returns an "extent" for each physical allocation
+ * on disk. We need to process those to generate a more compact list
+ * of logical file blocks. We also need to be very careful to use
+ * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes
+ * does not report allocations for newly-written data that hasn't
+ * been synced to disk.
+ *
+ * It's important to return a minimal sparse file list because we want
+ * to not trigger sparse file extensions if we don't have to, since
+ * not all readers support them.
+ */
+
+static int
+setup_sparse_fiemap(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ char buff[4096];
+ struct fiemap *fm;
+ struct fiemap_extent *fe;
+ int64_t size;
+ int count, do_fiemap, iters;
+ int exit_sts = ARCHIVE_OK;
+ const char *path;
+
+ if (archive_entry_filetype(entry) != AE_IFREG
+ || archive_entry_size(entry) <= 0
+ || archive_entry_hardlink(entry) != NULL)
+ return (ARCHIVE_OK);
+
+ if (*fd < 0) {
+ path = archive_read_disk_entry_setup_path(a, entry, NULL);
+ if (path == NULL)
+ return (ARCHIVE_FAILED);
+
+ if (a->tree != NULL)
+ *fd = a->open_on_current_dir(a->tree, path,
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ else
+ *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ if (*fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't open `%s'", path);
+ return (ARCHIVE_FAILED);
+ }
+ __archive_ensure_cloexec_flag(*fd);
+ }
+
+ /* Initialize buffer to avoid the error valgrind complains about. */
+ memset(buff, 0, sizeof(buff));
+ count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);
+ fm = (struct fiemap *)buff;
+ fm->fm_start = 0;
+ fm->fm_length = ~0ULL;;
+ fm->fm_flags = FIEMAP_FLAG_SYNC;
+ fm->fm_extent_count = count;
+ do_fiemap = 1;
+ size = archive_entry_size(entry);
+ for (iters = 0; ; ++iters) {
+ int i, r;
+
+ r = ioctl(*fd, FS_IOC_FIEMAP, fm);
+ if (r < 0) {
+ /* When something error happens, it is better we
+ * should return ARCHIVE_OK because an earlier
+ * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */
+ goto exit_setup_sparse_fiemap;
+ }
+ if (fm->fm_mapped_extents == 0) {
+ if (iters == 0) {
+ /* Fully sparse file; insert a zero-length "data" entry */
+ archive_entry_sparse_add_entry(entry, 0, 0);
+ }
+ break;
+ }
+ fe = fm->fm_extents;
+ for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {
+ if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
+ /* The fe_length of the last block does not
+ * adjust itself to its size files. */
+ int64_t length = fe->fe_length;
+ if (fe->fe_logical + length > (uint64_t)size)
+ length -= fe->fe_logical + length - size;
+ if (fe->fe_logical == 0 && length == size) {
+ /* This is not sparse. */
+ do_fiemap = 0;
+ break;
+ }
+ if (length > 0)
+ archive_entry_sparse_add_entry(entry,
+ fe->fe_logical, length);
+ }
+ if (fe->fe_flags & FIEMAP_EXTENT_LAST)
+ do_fiemap = 0;
+ }
+ if (do_fiemap) {
+ fe = fm->fm_extents + fm->fm_mapped_extents -1;
+ fm->fm_start = fe->fe_logical + fe->fe_length;
+ } else
+ break;
+ }
+exit_setup_sparse_fiemap:
+ return (exit_sts);
+}
+
+#if !defined(SEEK_HOLE) || !defined(SEEK_DATA)
+static int
+setup_sparse(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ return setup_sparse_fiemap(a, entry, fd);
+}
+#endif
+#endif /* defined(HAVE_LINUX_FIEMAP_H) */
+
+#if defined(SEEK_HOLE) && defined(SEEK_DATA)
+
+/*
+ * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris)
+ */
+
+static int
+setup_sparse(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ int64_t size;
+ off_t initial_off;
+ off_t off_s, off_e;
+ int exit_sts = ARCHIVE_OK;
+ int check_fully_sparse = 0;
+ const char *path;
+
+ if (archive_entry_filetype(entry) != AE_IFREG
+ || archive_entry_size(entry) <= 0
+ || archive_entry_hardlink(entry) != NULL)
+ return (ARCHIVE_OK);
+
+ /* Does filesystem support the reporting of hole ? */
+ if (*fd < 0)
+ path = archive_read_disk_entry_setup_path(a, entry, fd);
+ else
+ path = NULL;
+
+ if (*fd >= 0) {
+#ifdef _PC_MIN_HOLE_SIZE
+ if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0)
+ return (ARCHIVE_OK);
+#endif
+ initial_off = lseek(*fd, 0, SEEK_CUR);
+ if (initial_off != 0)
+ lseek(*fd, 0, SEEK_SET);
+ } else {
+ if (path == NULL)
+ return (ARCHIVE_FAILED);
+#ifdef _PC_MIN_HOLE_SIZE
+ if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
+ return (ARCHIVE_OK);
+#endif
+ *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ if (*fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't open `%s'", path);
+ return (ARCHIVE_FAILED);
+ }
+ __archive_ensure_cloexec_flag(*fd);
+ initial_off = 0;
+ }
+
+#ifndef _PC_MIN_HOLE_SIZE
+ /* Check if the underlying filesystem supports seek hole */
+ off_s = lseek(*fd, 0, SEEK_HOLE);
+ if (off_s < 0)
+#if defined(HAVE_LINUX_FIEMAP_H)
+ return setup_sparse_fiemap(a, entry, fd);
+#else
+ goto exit_setup_sparse;
+#endif
+ else if (off_s > 0)
+ lseek(*fd, 0, SEEK_SET);
+#endif
+
+ off_s = 0;
+ size = archive_entry_size(entry);
+ while (off_s < size) {
+ off_s = lseek(*fd, off_s, SEEK_DATA);
+ if (off_s == (off_t)-1) {
+ if (errno == ENXIO) {
+ /* no more hole */
+ if (archive_entry_sparse_count(entry) == 0) {
+ /* Potentially a fully-sparse file. */
+ check_fully_sparse = 1;
+ }
+ break;
+ }
+ archive_set_error(&a->archive, errno,
+ "lseek(SEEK_HOLE) failed");
+ exit_sts = ARCHIVE_FAILED;
+ goto exit_setup_sparse;
+ }
+ off_e = lseek(*fd, off_s, SEEK_HOLE);
+ if (off_e == (off_t)-1) {
+ if (errno == ENXIO) {
+ off_e = lseek(*fd, 0, SEEK_END);
+ if (off_e != (off_t)-1)
+ break;/* no more data */
+ }
+ archive_set_error(&a->archive, errno,
+ "lseek(SEEK_DATA) failed");
+ exit_sts = ARCHIVE_FAILED;
+ goto exit_setup_sparse;
+ }
+ if (off_s == 0 && off_e == size)
+ break;/* This is not sparse. */
+ archive_entry_sparse_add_entry(entry, off_s,
+ off_e - off_s);
+ off_s = off_e;
+ }
+
+ if (check_fully_sparse) {
+ if (lseek(*fd, 0, SEEK_HOLE) == 0 &&
+ lseek(*fd, 0, SEEK_END) == size) {
+ /* Fully sparse file; insert a zero-length "data" entry */
+ archive_entry_sparse_add_entry(entry, 0, 0);
+ }
+ }
+exit_setup_sparse:
+ lseek(*fd, initial_off, SEEK_SET);
+ return (exit_sts);
+}
+
+#elif !defined(HAVE_LINUX_FIEMAP_H)
+
+/*
+ * Generic (stub) sparse support.
+ */
+static int
+setup_sparse(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+ (void)fd; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif
+
+#endif /* !defined(_WIN32) || defined(__CYGWIN__) */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_posix.c b/contrib/libs/libarchive/libarchive/archive_read_disk_posix.c
new file mode 100644
index 0000000000..fbed06adf2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_posix.c
@@ -0,0 +1,2760 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+/* This is the tree-walking code for POSIX systems. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_LINUX_MAGIC_H
+#include <linux/magic.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#elif HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef HAVE_FCHDIR
+#error fchdir function required.
+#endif
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#if defined(__hpux) && !defined(HAVE_DIRFD)
+#define dirfd(x) ((x)->__dd_fd)
+#define HAVE_DIRFD
+#endif
+
+/*-
+ * This is a new directory-walking system that addresses a number
+ * of problems I've had with fts(3). In particular, it has no
+ * pathname-length limits (other than the size of 'int'), handles
+ * deep logical traversals, uses considerably less memory, and has
+ * an opaque interface (easier to modify in the future).
+ *
+ * Internally, it keeps a single list of "tree_entry" items that
+ * represent filesystem objects that require further attention.
+ * Non-directories are not kept in memory: they are pulled from
+ * readdir(), returned to the client, then freed as soon as possible.
+ * Any directory entry to be traversed gets pushed onto the stack.
+ *
+ * There is surprisingly little information that needs to be kept for
+ * each item on the stack. Just the name, depth (represented here as the
+ * string length of the parent directory's pathname), and some markers
+ * indicating how to get back to the parent (via chdir("..") for a
+ * regular dir or via fchdir(2) for a symlink).
+ */
+/*
+ * TODO:
+ * 1) Loop checking.
+ * 3) Arbitrary logical traversals by closing/reopening intermediate fds.
+ */
+
+struct restore_time {
+ const char *name;
+ time_t mtime;
+ long mtime_nsec;
+ time_t atime;
+ long atime_nsec;
+ mode_t filetype;
+ int noatime;
+};
+
+struct tree_entry {
+ int depth;
+ struct tree_entry *next;
+ struct tree_entry *parent;
+ struct archive_string name;
+ size_t dirname_length;
+ int64_t dev;
+ int64_t ino;
+ int flags;
+ int filesystem_id;
+ /* How to return back to the parent of a symlink. */
+ int symlink_parent_fd;
+ /* How to restore time of a directory. */
+ struct restore_time restore_time;
+};
+
+struct filesystem {
+ int64_t dev;
+ int synthetic;
+ int remote;
+ int noatime;
+#if defined(USE_READDIR_R)
+ size_t name_max;
+#endif
+ long incr_xfer_size;
+ long max_xfer_size;
+ long min_xfer_size;
+ long xfer_align;
+
+ /*
+ * Buffer used for reading file contents.
+ */
+ /* Exactly allocated memory pointer. */
+ unsigned char *allocation_ptr;
+ /* Pointer adjusted to the filesystem alignment . */
+ unsigned char *buff;
+ size_t buff_size;
+};
+
+/* Definitions for tree_entry.flags bitmap. */
+#define isDir 1 /* This entry is a regular directory. */
+#define isDirLink 2 /* This entry is a symbolic link to a directory. */
+#define needsFirstVisit 4 /* This is an initial entry. */
+#define needsDescent 8 /* This entry needs to be previsited. */
+#define needsOpen 16 /* This is a directory that needs to be opened. */
+#define needsAscent 32 /* This entry needs to be postvisited. */
+
+/*
+ * Local data for this package.
+ */
+struct tree {
+ struct tree_entry *stack;
+ struct tree_entry *current;
+ DIR *d;
+#define INVALID_DIR_HANDLE NULL
+ struct dirent *de;
+#if defined(USE_READDIR_R)
+ struct dirent *dirent;
+ size_t dirent_allocated;
+#endif
+ int flags;
+ int visit_type;
+ /* Error code from last failed operation. */
+ int tree_errno;
+
+ /* Dynamically-sized buffer for holding path */
+ struct archive_string path;
+
+ /* Last path element */
+ const char *basename;
+ /* Leading dir length */
+ size_t dirname_length;
+
+ int depth;
+ int openCount;
+ int maxOpenCount;
+ int initial_dir_fd;
+ int working_dir_fd;
+
+ struct stat lst;
+ struct stat st;
+ int descend;
+ int nlink;
+ /* How to restore time of a file. */
+ struct restore_time restore_time;
+
+ struct entry_sparse {
+ int64_t length;
+ int64_t offset;
+ } *sparse_list, *current_sparse;
+ int sparse_count;
+ int sparse_list_size;
+
+ char initial_symlink_mode;
+ char symlink_mode;
+ struct filesystem *current_filesystem;
+ struct filesystem *filesystem_table;
+ int initial_filesystem_id;
+ int current_filesystem_id;
+ int max_filesystem_id;
+ int allocated_filesystem;
+
+ int entry_fd;
+ int entry_eof;
+ int64_t entry_remaining_bytes;
+ int64_t entry_total;
+ unsigned char *entry_buff;
+ size_t entry_buff_size;
+};
+
+/* Definitions for tree.flags bitmap. */
+#define hasStat 16 /* The st entry is valid. */
+#define hasLstat 32 /* The lst entry is valid. */
+#define onWorkingDir 64 /* We are on the working dir where we are
+ * reading directory entry at this time. */
+#define needsRestoreTimes 128
+#define onInitialDir 256 /* We are on the initial dir. */
+
+static int
+tree_dir_next_posix(struct tree *t);
+
+#ifdef HAVE_DIRENT_D_NAMLEN
+/* BSD extension; avoids need for a strlen() call. */
+#define D_NAMELEN(dp) (dp)->d_namlen
+#else
+#define D_NAMELEN(dp) (strlen((dp)->d_name))
+#endif
+
+/* Initiate/terminate a tree traversal. */
+static struct tree *tree_open(const char *, int, int);
+static struct tree *tree_reopen(struct tree *, const char *, int);
+static void tree_close(struct tree *);
+static void tree_free(struct tree *);
+static void tree_push(struct tree *, const char *, int, int64_t, int64_t,
+ struct restore_time *);
+static int tree_enter_initial_dir(struct tree *);
+static int tree_enter_working_dir(struct tree *);
+static int tree_current_dir_fd(struct tree *);
+
+/*
+ * tree_next() returns Zero if there is no next entry, non-zero if
+ * there is. Note that directories are visited three times.
+ * Directories are always visited first as part of enumerating their
+ * parent; that is a "regular" visit. If tree_descend() is invoked at
+ * that time, the directory is added to a work list and will
+ * subsequently be visited two more times: once just after descending
+ * into the directory ("postdescent") and again just after ascending
+ * back to the parent ("postascent").
+ *
+ * TREE_ERROR_DIR is returned if the descent failed (because the
+ * directory couldn't be opened, for instance). This is returned
+ * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a
+ * fatal error, but it does imply that the relevant subtree won't be
+ * visited. TREE_ERROR_FATAL is returned for an error that left the
+ * traversal completely hosed. Right now, this is only returned for
+ * chdir() failures during ascent.
+ */
+#define TREE_REGULAR 1
+#define TREE_POSTDESCENT 2
+#define TREE_POSTASCENT 3
+#define TREE_ERROR_DIR -1
+#define TREE_ERROR_FATAL -2
+
+static int tree_next(struct tree *);
+
+/*
+ * Return information about the current entry.
+ */
+
+/*
+ * The current full pathname, length of the full pathname, and a name
+ * that can be used to access the file. Because tree does use chdir
+ * extensively, the access path is almost never the same as the full
+ * current path.
+ *
+ * TODO: On platforms that support it, use openat()-style operations
+ * to eliminate the chdir() operations entirely while still supporting
+ * arbitrarily deep traversals. This makes access_path troublesome to
+ * support, of course, which means we'll need a rich enough interface
+ * that clients can function without it. (In particular, we'll need
+ * tree_current_open() that returns an open file descriptor.)
+ *
+ */
+static const char *tree_current_path(struct tree *);
+static const char *tree_current_access_path(struct tree *);
+
+/*
+ * Request the lstat() or stat() data for the current path. Since the
+ * tree package needs to do some of this anyway, and caches the
+ * results, you should take advantage of it here if you need it rather
+ * than make a redundant stat() or lstat() call of your own.
+ */
+static const struct stat *tree_current_stat(struct tree *);
+static const struct stat *tree_current_lstat(struct tree *);
+static int tree_current_is_symblic_link_target(struct tree *);
+
+/* The following functions use tricks to avoid a certain number of
+ * stat()/lstat() calls. */
+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_dir(struct tree *);
+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
+static int tree_current_is_dir(struct tree *);
+static int update_current_filesystem(struct archive_read_disk *a,
+ int64_t dev);
+static int setup_current_filesystem(struct archive_read_disk *);
+static int tree_target_is_same_as_parent(struct tree *, const struct stat *);
+
+static int _archive_read_disk_open(struct archive *, const char *);
+static int _archive_read_free(struct archive *);
+static int _archive_read_close(struct archive *);
+static int _archive_read_data_block(struct archive *,
+ const void **, size_t *, int64_t *);
+static int _archive_read_next_header(struct archive *,
+ struct archive_entry **);
+static int _archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+static const char *trivial_lookup_gname(void *, int64_t gid);
+static const char *trivial_lookup_uname(void *, int64_t uid);
+static int setup_sparse(struct archive_read_disk *, struct archive_entry *);
+static int close_and_restore_time(int fd, struct tree *,
+ struct restore_time *);
+static int open_on_current_dir(struct tree *, const char *, int);
+static int tree_dup(int);
+
+
+static const struct archive_vtable
+archive_read_disk_vtable = {
+ .archive_free = _archive_read_free,
+ .archive_close = _archive_read_close,
+ .archive_read_data_block = _archive_read_data_block,
+ .archive_read_next_header = _archive_read_next_header,
+ .archive_read_next_header2 = _archive_read_next_header2,
+};
+
+const char *
+archive_read_disk_gname(struct archive *_a, la_int64_t gid)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_gname"))
+ return (NULL);
+ if (a->lookup_gname == NULL)
+ return (NULL);
+ return ((*a->lookup_gname)(a->lookup_gname_data, gid));
+}
+
+const char *
+archive_read_disk_uname(struct archive *_a, la_int64_t uid)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_uname"))
+ return (NULL);
+ if (a->lookup_uname == NULL)
+ return (NULL);
+ return ((*a->lookup_uname)(a->lookup_uname_data, uid));
+}
+
+int
+archive_read_disk_set_gname_lookup(struct archive *_a,
+ void *private_data,
+ const char * (*lookup_gname)(void *private, la_int64_t gid),
+ void (*cleanup_gname)(void *private))
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");
+
+ if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+ (a->cleanup_gname)(a->lookup_gname_data);
+
+ a->lookup_gname = lookup_gname;
+ a->cleanup_gname = cleanup_gname;
+ a->lookup_gname_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_uname_lookup(struct archive *_a,
+ void *private_data,
+ const char * (*lookup_uname)(void *private, la_int64_t uid),
+ void (*cleanup_uname)(void *private))
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");
+
+ if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+ (a->cleanup_uname)(a->lookup_uname_data);
+
+ a->lookup_uname = lookup_uname;
+ a->cleanup_uname = cleanup_uname;
+ a->lookup_uname_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Create a new archive_read_disk object and initialize it with global state.
+ */
+struct archive *
+archive_read_disk_new(void)
+{
+ struct archive_read_disk *a;
+
+ a = (struct archive_read_disk *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->archive.vtable = &archive_read_disk_vtable;
+ a->entry = archive_entry_new2(&a->archive);
+ a->lookup_uname = trivial_lookup_uname;
+ a->lookup_gname = trivial_lookup_gname;
+ a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
+ a->open_on_current_dir = open_on_current_dir;
+ a->tree_current_dir_fd = tree_current_dir_fd;
+ a->tree_enter_working_dir = tree_enter_working_dir;
+ return (&a->archive);
+}
+
+static int
+_archive_read_free(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ int r;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+
+ if (a->archive.state != ARCHIVE_STATE_CLOSED)
+ r = _archive_read_close(&a->archive);
+ else
+ r = ARCHIVE_OK;
+
+ tree_free(a->tree);
+ if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+ (a->cleanup_gname)(a->lookup_gname_data);
+ if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+ (a->cleanup_uname)(a->lookup_uname_data);
+ archive_string_free(&a->archive.error_string);
+ archive_entry_free(a->entry);
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a);
+ return (r);
+}
+
+static int
+_archive_read_close(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+
+ if (a->archive.state != ARCHIVE_STATE_FATAL)
+ a->archive.state = ARCHIVE_STATE_CLOSED;
+
+ tree_close(a->tree);
+
+ return (ARCHIVE_OK);
+}
+
+static void
+setup_symlink_mode(struct archive_read_disk *a, char symlink_mode,
+ int follow_symlinks)
+{
+ a->symlink_mode = symlink_mode;
+ a->follow_symlinks = follow_symlinks;
+ if (a->tree != NULL) {
+ a->tree->initial_symlink_mode = a->symlink_mode;
+ a->tree->symlink_mode = a->symlink_mode;
+ }
+}
+
+int
+archive_read_disk_set_symlink_logical(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical");
+ setup_symlink_mode(a, 'L', 1);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_physical(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical");
+ setup_symlink_mode(a, 'P', 0);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_hybrid(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid");
+ setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_atime_restored(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");
+#ifdef HAVE_UTIMES
+ a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;
+ if (a->tree != NULL)
+ a->tree->flags |= needsRestoreTimes;
+ return (ARCHIVE_OK);
+#else
+ /* Display warning and unset flag */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore access time on this system");
+ a->flags &= ~ARCHIVE_READDISK_RESTORE_ATIME;
+ return (ARCHIVE_WARN);
+#endif
+}
+
+int
+archive_read_disk_set_behavior(struct archive *_a, int flags)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ int r = ARCHIVE_OK;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+
+ a->flags = flags;
+
+ if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
+ r = archive_read_disk_set_atime_restored(_a);
+ else {
+ if (a->tree != NULL)
+ a->tree->flags &= ~needsRestoreTimes;
+ }
+ return (r);
+}
+
+/*
+ * Trivial implementations of gname/uname lookup functions.
+ * These are normally overridden by the client, but these stub
+ * versions ensure that we always have something that works.
+ */
+static const char *
+trivial_lookup_gname(void *private_data, int64_t gid)
+{
+ (void)private_data; /* UNUSED */
+ (void)gid; /* UNUSED */
+ return (NULL);
+}
+
+static const char *
+trivial_lookup_uname(void *private_data, int64_t uid)
+{
+ (void)private_data; /* UNUSED */
+ (void)uid; /* UNUSED */
+ return (NULL);
+}
+
+/*
+ * Allocate memory for the reading buffer adjusted to the filesystem
+ * alignment.
+ */
+static int
+setup_suitable_read_buffer(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct filesystem *cf = t->current_filesystem;
+ size_t asize;
+ size_t s;
+
+ if (cf->allocation_ptr == NULL) {
+ /* If we couldn't get a filesystem alignment,
+ * we use 4096 as default value but we won't use
+ * O_DIRECT to open() and openat() operations. */
+ long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align;
+
+ if (cf->max_xfer_size != -1)
+ asize = cf->max_xfer_size + xfer_align;
+ else {
+ long incr = cf->incr_xfer_size;
+ /* Some platform does not set a proper value to
+ * incr_xfer_size.*/
+ if (incr < 0)
+ incr = cf->min_xfer_size;
+ if (cf->min_xfer_size < 0) {
+ incr = xfer_align;
+ asize = xfer_align;
+ } else
+ asize = cf->min_xfer_size;
+
+ /* Increase a buffer size up to 64K bytes in
+ * a proper increment size. */
+ while (asize < 1024*64)
+ asize += incr;
+ /* Take a margin to adjust to the filesystem
+ * alignment. */
+ asize += xfer_align;
+ }
+ cf->allocation_ptr = malloc(asize);
+ if (cf->allocation_ptr == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Calculate proper address for the filesystem.
+ */
+ s = (uintptr_t)cf->allocation_ptr;
+ s %= xfer_align;
+ if (s > 0)
+ s = xfer_align - s;
+
+ /*
+ * Set a read buffer pointer in the proper alignment of
+ * the current filesystem.
+ */
+ cf->buff = cf->allocation_ptr + s;
+ cf->buff_size = asize - xfer_align;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+_archive_read_data_block(struct archive *_a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+ int r;
+ ssize_t bytes;
+ int64_t sparse_bytes;
+ size_t buffbytes;
+ int empty_sparse_region = 0;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_block");
+
+ if (t->entry_eof || t->entry_remaining_bytes <= 0) {
+ r = ARCHIVE_EOF;
+ goto abort_read_data;
+ }
+
+ /*
+ * Open the current file.
+ */
+ if (t->entry_fd < 0) {
+ int flags = O_RDONLY | O_BINARY | O_CLOEXEC;
+
+ /*
+ * Eliminate or reduce cache effects if we can.
+ *
+ * Carefully consider this to be enabled.
+ */
+#if defined(O_DIRECT) && 0/* Disabled for now */
+ if (t->current_filesystem->xfer_align != -1 &&
+ t->nlink == 1)
+ flags |= O_DIRECT;
+#endif
+#if defined(O_NOATIME)
+ /*
+ * Linux has O_NOATIME flag; use it if we need.
+ */
+ if ((t->flags & needsRestoreTimes) != 0 &&
+ t->restore_time.noatime == 0)
+ flags |= O_NOATIME;
+#endif
+ t->entry_fd = open_on_current_dir(t,
+ tree_current_access_path(t), flags);
+ __archive_ensure_cloexec_flag(t->entry_fd);
+#if defined(O_NOATIME)
+ /*
+ * When we did open the file with O_NOATIME flag,
+ * if successful, set 1 to t->restore_time.noatime
+ * not to restore an atime of the file later.
+ * if failed by EPERM, retry it without O_NOATIME flag.
+ */
+ if (flags & O_NOATIME) {
+ if (t->entry_fd >= 0)
+ t->restore_time.noatime = 1;
+ else if (errno == EPERM)
+ flags &= ~O_NOATIME;
+ }
+#endif
+ if (t->entry_fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't open %s", tree_current_path(t));
+ r = ARCHIVE_FAILED;
+ tree_enter_initial_dir(t);
+ goto abort_read_data;
+ }
+ tree_enter_initial_dir(t);
+ }
+
+ /*
+ * Allocate read buffer if not allocated.
+ */
+ if (t->current_filesystem->allocation_ptr == NULL) {
+ r = setup_suitable_read_buffer(a);
+ if (r != ARCHIVE_OK) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ goto abort_read_data;
+ }
+ }
+ t->entry_buff = t->current_filesystem->buff;
+ t->entry_buff_size = t->current_filesystem->buff_size;
+
+ buffbytes = t->entry_buff_size;
+ if ((int64_t)buffbytes > t->current_sparse->length)
+ buffbytes = t->current_sparse->length;
+
+ if (t->current_sparse->length == 0)
+ empty_sparse_region = 1;
+
+ /*
+ * Skip hole.
+ * TODO: Should we consider t->current_filesystem->xfer_align?
+ */
+ if (t->current_sparse->offset > t->entry_total) {
+ if (lseek(t->entry_fd,
+ (off_t)t->current_sparse->offset, SEEK_SET) < 0) {
+ archive_set_error(&a->archive, errno, "Seek error");
+ r = ARCHIVE_FATAL;
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ goto abort_read_data;
+ }
+ sparse_bytes = t->current_sparse->offset - t->entry_total;
+ t->entry_remaining_bytes -= sparse_bytes;
+ t->entry_total += sparse_bytes;
+ }
+
+ /*
+ * Read file contents.
+ */
+ if (buffbytes > 0) {
+ bytes = read(t->entry_fd, t->entry_buff, buffbytes);
+ if (bytes < 0) {
+ archive_set_error(&a->archive, errno, "Read error");
+ r = ARCHIVE_FATAL;
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ goto abort_read_data;
+ }
+ } else
+ bytes = 0;
+ /*
+ * Return an EOF unless we've read a leading empty sparse region, which
+ * is used to represent fully-sparse files.
+ */
+ if (bytes == 0 && !empty_sparse_region) {
+ /* Get EOF */
+ t->entry_eof = 1;
+ r = ARCHIVE_EOF;
+ goto abort_read_data;
+ }
+ *buff = t->entry_buff;
+ *size = bytes;
+ *offset = t->entry_total;
+ t->entry_total += bytes;
+ t->entry_remaining_bytes -= bytes;
+ if (t->entry_remaining_bytes == 0) {
+ /* Close the current file descriptor */
+ close_and_restore_time(t->entry_fd, t, &t->restore_time);
+ t->entry_fd = -1;
+ t->entry_eof = 1;
+ }
+ t->current_sparse->offset += bytes;
+ t->current_sparse->length -= bytes;
+ if (t->current_sparse->length == 0 && !t->entry_eof)
+ t->current_sparse++;
+ return (ARCHIVE_OK);
+
+abort_read_data:
+ *buff = NULL;
+ *size = 0;
+ *offset = t->entry_total;
+ if (t->entry_fd >= 0) {
+ /* Close the current file descriptor */
+ close_and_restore_time(t->entry_fd, t, &t->restore_time);
+ t->entry_fd = -1;
+ }
+ return (r);
+}
+
+static int
+next_entry(struct archive_read_disk *a, struct tree *t,
+ struct archive_entry *entry)
+{
+ const struct stat *st; /* info to use for this entry */
+ const struct stat *lst;/* lstat() information */
+ const char *name;
+ int delayed, delayed_errno, descend, r;
+ struct archive_string delayed_str;
+
+ delayed = ARCHIVE_OK;
+ delayed_errno = 0;
+ archive_string_init(&delayed_str);
+
+ st = NULL;
+ lst = NULL;
+ t->descend = 0;
+ do {
+ switch (tree_next(t)) {
+ case TREE_ERROR_FATAL:
+ archive_set_error(&a->archive, t->tree_errno,
+ "%s: Unable to continue traversing directory tree",
+ tree_current_path(t));
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_FATAL);
+ case TREE_ERROR_DIR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: Couldn't visit directory",
+ tree_current_path(t));
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_FAILED);
+ case 0:
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_EOF);
+ case TREE_POSTDESCENT:
+ case TREE_POSTASCENT:
+ break;
+ case TREE_REGULAR:
+ lst = tree_current_lstat(t);
+ if (lst == NULL) {
+ if (errno == ENOENT && t->depth > 0) {
+ delayed = ARCHIVE_WARN;
+ delayed_errno = errno;
+ if (delayed_str.length == 0) {
+ archive_string_sprintf(&delayed_str,
+ "%s", tree_current_path(t));
+ } else {
+ archive_string_sprintf(&delayed_str,
+ " %s", tree_current_path(t));
+ }
+ } else {
+ archive_set_error(&a->archive, errno,
+ "%s: Cannot stat",
+ tree_current_path(t));
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_FAILED);
+ }
+ }
+ break;
+ }
+ } while (lst == NULL);
+
+#ifdef __APPLE__
+ if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
+ /* If we're using copyfile(), ignore "._XXX" files. */
+ const char *bname = strrchr(tree_current_path(t), '/');
+ if (bname == NULL)
+ bname = tree_current_path(t);
+ else
+ ++bname;
+ if (bname[0] == '.' && bname[1] == '_')
+ return (ARCHIVE_RETRY);
+ }
+#endif
+
+ archive_entry_copy_pathname(entry, tree_current_path(t));
+ /*
+ * Perform path matching.
+ */
+ if (a->matching) {
+ r = archive_match_path_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /*
+ * Distinguish 'L'/'P'/'H' symlink following.
+ */
+ switch(t->symlink_mode) {
+ case 'H':
+ /* 'H': After the first item, rest like 'P'. */
+ t->symlink_mode = 'P';
+ /* 'H': First item (from command line) like 'L'. */
+ /* FALLTHROUGH */
+ case 'L':
+ /* 'L': Do descend through a symlink to dir. */
+ descend = tree_current_is_dir(t);
+ /* 'L': Follow symlinks to files. */
+ a->symlink_mode = 'L';
+ a->follow_symlinks = 1;
+ /* 'L': Archive symlinks as targets, if we can. */
+ st = tree_current_stat(t);
+ if (st != NULL && !tree_target_is_same_as_parent(t, st))
+ break;
+ /* If stat fails, we have a broken symlink;
+ * in that case, don't follow the link. */
+ /* FALLTHROUGH */
+ default:
+ /* 'P': Don't descend through a symlink to dir. */
+ descend = tree_current_is_physical_dir(t);
+ /* 'P': Don't follow symlinks to files. */
+ a->symlink_mode = 'P';
+ a->follow_symlinks = 0;
+ /* 'P': Archive symlinks as symlinks. */
+ st = lst;
+ break;
+ }
+
+ if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ tree_enter_initial_dir(t);
+ return (ARCHIVE_FATAL);
+ }
+ if (t->initial_filesystem_id == -1)
+ t->initial_filesystem_id = t->current_filesystem_id;
+ if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {
+ if (t->initial_filesystem_id != t->current_filesystem_id)
+ descend = 0;
+ }
+ t->descend = descend;
+
+ /*
+ * Honor nodump flag.
+ * If the file is marked with nodump flag, do not return this entry.
+ */
+ if (a->flags & ARCHIVE_READDISK_HONOR_NODUMP) {
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
+ if (st->st_flags & UF_NODUMP)
+ return (ARCHIVE_RETRY);
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_NODUMP_FL) && \
+ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && \
+ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+ if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) {
+ int stflags;
+
+ t->entry_fd = open_on_current_dir(t,
+ tree_current_access_path(t),
+ O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(t->entry_fd);
+ if (t->entry_fd >= 0) {
+ r = ioctl(t->entry_fd,
+#ifdef FS_IOC_GETFLAGS
+ FS_IOC_GETFLAGS,
+#else
+ EXT2_IOC_GETFLAGS,
+#endif
+ &stflags);
+#ifdef FS_NODUMP_FL
+ if (r == 0 && (stflags & FS_NODUMP_FL) != 0)
+#else
+ if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0)
+#endif
+ return (ARCHIVE_RETRY);
+ }
+ }
+#endif
+ }
+
+ archive_entry_copy_stat(entry, st);
+
+ /* Save the times to be restored. This must be in before
+ * calling archive_read_disk_descend() or any chance of it,
+ * especially, invoking a callback. */
+ t->restore_time.mtime = archive_entry_mtime(entry);
+ t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry);
+ t->restore_time.atime = archive_entry_atime(entry);
+ t->restore_time.atime_nsec = archive_entry_atime_nsec(entry);
+ t->restore_time.filetype = archive_entry_filetype(entry);
+ t->restore_time.noatime = t->current_filesystem->noatime;
+
+ /*
+ * Perform time matching.
+ */
+ if (a->matching) {
+ r = archive_match_time_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /* Lookup uname/gname */
+ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));
+ if (name != NULL)
+ archive_entry_copy_uname(entry, name);
+ name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));
+ if (name != NULL)
+ archive_entry_copy_gname(entry, name);
+
+ /*
+ * Perform owner matching.
+ */
+ if (a->matching) {
+ r = archive_match_owner_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /*
+ * Invoke a meta data filter callback.
+ */
+ if (a->metadata_filter_func) {
+ if (!a->metadata_filter_func(&(a->archive),
+ a->metadata_filter_data, entry))
+ return (ARCHIVE_RETRY);
+ }
+
+ /*
+ * Populate the archive_entry with metadata from the disk.
+ */
+ archive_entry_copy_sourcepath(entry, tree_current_access_path(t));
+ r = archive_read_disk_entry_from_file(&(a->archive), entry,
+ t->entry_fd, st);
+
+ if (r == ARCHIVE_OK) {
+ r = delayed;
+ if (r != ARCHIVE_OK) {
+ archive_string_sprintf(&delayed_str, ": %s",
+ "File removed before we read it");
+ archive_set_error(&(a->archive), delayed_errno,
+ "%s", delayed_str.s);
+ }
+ }
+ archive_string_free(&delayed_str);
+
+ return (r);
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+ int ret;
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ *entryp = NULL;
+ ret = _archive_read_next_header2(_a, a->entry);
+ *entryp = a->entry;
+ return ret;
+}
+
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_next_header2");
+
+ t = a->tree;
+ if (t->entry_fd >= 0) {
+ close_and_restore_time(t->entry_fd, t, &t->restore_time);
+ t->entry_fd = -1;
+ }
+
+ archive_entry_clear(entry);
+
+ for (;;) {
+ r = next_entry(a, t, entry);
+ if (t->entry_fd >= 0) {
+ close(t->entry_fd);
+ t->entry_fd = -1;
+ }
+
+ if (r == ARCHIVE_RETRY) {
+ archive_entry_clear(entry);
+ continue;
+ }
+ break;
+ }
+
+ /* Return to the initial directory. */
+ tree_enter_initial_dir(t);
+
+ /*
+ * EOF and FATAL are persistent at this layer. By
+ * modifying the state, we guarantee that future calls to
+ * read a header or read data will fail.
+ */
+ switch (r) {
+ case ARCHIVE_EOF:
+ a->archive.state = ARCHIVE_STATE_EOF;
+ break;
+ case ARCHIVE_OK:
+ case ARCHIVE_WARN:
+ /* Overwrite the sourcepath based on the initial directory. */
+ archive_entry_copy_sourcepath(entry, tree_current_path(t));
+ t->entry_total = 0;
+ if (archive_entry_filetype(entry) == AE_IFREG) {
+ t->nlink = archive_entry_nlink(entry);
+ t->entry_remaining_bytes = archive_entry_size(entry);
+ t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0;
+ if (!t->entry_eof &&
+ setup_sparse(a, entry) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ t->entry_remaining_bytes = 0;
+ t->entry_eof = 1;
+ }
+ a->archive.state = ARCHIVE_STATE_DATA;
+ break;
+ case ARCHIVE_RETRY:
+ break;
+ case ARCHIVE_FATAL:
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ break;
+ }
+
+ __archive_reset_read_data(&a->archive);
+ return (r);
+}
+
+static int
+setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)
+{
+ struct tree *t = a->tree;
+ int64_t length, offset;
+ int i;
+
+ t->sparse_count = archive_entry_sparse_reset(entry);
+ if (t->sparse_count+1 > t->sparse_list_size) {
+ free(t->sparse_list);
+ t->sparse_list_size = t->sparse_count + 1;
+ t->sparse_list = malloc(sizeof(t->sparse_list[0]) *
+ t->sparse_list_size);
+ if (t->sparse_list == NULL) {
+ t->sparse_list_size = 0;
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ }
+ for (i = 0; i < t->sparse_count; i++) {
+ archive_entry_sparse_next(entry, &offset, &length);
+ t->sparse_list[i].offset = offset;
+ t->sparse_list[i].length = length;
+ }
+ if (i == 0) {
+ t->sparse_list[i].offset = 0;
+ t->sparse_list[i].length = archive_entry_size(entry);
+ } else {
+ t->sparse_list[i].offset = archive_entry_size(entry);
+ t->sparse_list[i].length = 0;
+ }
+ t->current_sparse = t->sparse_list;
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,
+ void (*_excluded_func)(struct archive *, void *, struct archive_entry *),
+ void *_client_data)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");
+ a->matching = _ma;
+ a->excluded_cb_func = _excluded_func;
+ a->excluded_cb_data = _client_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_metadata_filter_callback(struct archive *_a,
+ int (*_metadata_filter_func)(struct archive *, void *,
+ struct archive_entry *), void *_client_data)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+ "archive_read_disk_set_metadata_filter_callback");
+
+ a->metadata_filter_func = _metadata_filter_func;
+ a->metadata_filter_data = _client_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_can_descend(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_disk_can_descend");
+
+ return (t->visit_type == TREE_REGULAR && t->descend);
+}
+
+/*
+ * Called by the client to mark the directory just returned from
+ * tree_next() as needing to be visited.
+ */
+int
+archive_read_disk_descend(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_disk_descend");
+
+ if (!archive_read_disk_can_descend(_a))
+ return (ARCHIVE_OK);
+
+ /*
+ * We must not treat the initial specified path as a physical dir,
+ * because if we do then we will try and ascend out of it by opening
+ * ".." which is (a) wrong and (b) causes spurious permissions errors
+ * if ".." is not readable by us. Instead, treat it as if it were a
+ * symlink. (This uses an extra fd, but it can only happen once at the
+ * top level of a traverse.) But we can't necessarily assume t->st is
+ * valid here (though t->lst is), which complicates the logic a
+ * little.
+ */
+ if (tree_current_is_physical_dir(t)) {
+ tree_push(t, t->basename, t->current_filesystem_id,
+ t->lst.st_dev, t->lst.st_ino, &t->restore_time);
+ if (t->stack->parent->parent != NULL)
+ t->stack->flags |= isDir;
+ else
+ t->stack->flags |= isDirLink;
+ } else if (tree_current_is_dir(t)) {
+ tree_push(t, t->basename, t->current_filesystem_id,
+ t->st.st_dev, t->st.st_ino, &t->restore_time);
+ t->stack->flags |= isDirLink;
+ }
+ t->descend = 0;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+ "archive_read_disk_open");
+ archive_clear_error(&a->archive);
+
+ return (_archive_read_disk_open(_a, pathname));
+}
+
+int
+archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct archive_string path;
+ int ret;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+ "archive_read_disk_open_w");
+ archive_clear_error(&a->archive);
+
+ /* Make a char string from a wchar_t string. */
+ archive_string_init(&path);
+ if (archive_string_append_from_wcs(&path, pathname,
+ wcslen(pathname)) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't convert a path to a char string");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ ret = ARCHIVE_FATAL;
+ } else
+ ret = _archive_read_disk_open(_a, path.s);
+
+ archive_string_free(&path);
+ return (ret);
+}
+
+static int
+_archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ if (a->tree != NULL)
+ a->tree = tree_reopen(a->tree, pathname,
+ a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+ else
+ a->tree = tree_open(pathname, a->symlink_mode,
+ a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+ if (a->tree == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate tar data");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ a->archive.state = ARCHIVE_STATE_HEADER;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return a current filesystem ID which is index of the filesystem entry
+ * you've visited through archive_read_disk.
+ */
+int
+archive_read_disk_current_filesystem(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem_id);
+}
+
+static int
+update_current_filesystem(struct archive_read_disk *a, int64_t dev)
+{
+ struct tree *t = a->tree;
+ int i, fid;
+
+ if (t->current_filesystem != NULL &&
+ t->current_filesystem->dev == dev)
+ return (ARCHIVE_OK);
+
+ for (i = 0; i < t->max_filesystem_id; i++) {
+ if (t->filesystem_table[i].dev == dev) {
+ /* There is the filesystem ID we've already generated. */
+ t->current_filesystem_id = i;
+ t->current_filesystem = &(t->filesystem_table[i]);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /*
+ * This is the new filesystem which we have to generate a new ID for.
+ */
+ fid = t->max_filesystem_id++;
+ if (t->max_filesystem_id > t->allocated_filesystem) {
+ size_t s;
+ void *p;
+
+ s = t->max_filesystem_id * 2;
+ p = realloc(t->filesystem_table,
+ s * sizeof(*t->filesystem_table));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate tar data");
+ return (ARCHIVE_FATAL);
+ }
+ t->filesystem_table = (struct filesystem *)p;
+ t->allocated_filesystem = s;
+ }
+ t->current_filesystem_id = fid;
+ t->current_filesystem = &(t->filesystem_table[fid]);
+ t->current_filesystem->dev = dev;
+ t->current_filesystem->allocation_ptr = NULL;
+ t->current_filesystem->buff = NULL;
+
+ /* Setup the current filesystem properties which depend on
+ * platform specific. */
+ return (setup_current_filesystem(a));
+}
+
+/*
+ * Returns 1 if current filesystem is generated filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_synthetic(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem->synthetic);
+}
+
+/*
+ * Returns 1 if current filesystem is remote filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_remote(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem->remote);
+}
+
+#if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\
+ defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN)
+static int
+get_xfer_size(struct tree *t, int fd, const char *path)
+{
+ t->current_filesystem->xfer_align = -1;
+ errno = 0;
+ if (fd >= 0) {
+ t->current_filesystem->incr_xfer_size =
+ fpathconf(fd, _PC_REC_INCR_XFER_SIZE);
+ t->current_filesystem->max_xfer_size =
+ fpathconf(fd, _PC_REC_MAX_XFER_SIZE);
+ t->current_filesystem->min_xfer_size =
+ fpathconf(fd, _PC_REC_MIN_XFER_SIZE);
+ t->current_filesystem->xfer_align =
+ fpathconf(fd, _PC_REC_XFER_ALIGN);
+ } else if (path != NULL) {
+ t->current_filesystem->incr_xfer_size =
+ pathconf(path, _PC_REC_INCR_XFER_SIZE);
+ t->current_filesystem->max_xfer_size =
+ pathconf(path, _PC_REC_MAX_XFER_SIZE);
+ t->current_filesystem->min_xfer_size =
+ pathconf(path, _PC_REC_MIN_XFER_SIZE);
+ t->current_filesystem->xfer_align =
+ pathconf(path, _PC_REC_XFER_ALIGN);
+ }
+ /* At least we need an alignment size. */
+ if (t->current_filesystem->xfer_align == -1)
+ return ((errno == EINVAL)?1:-1);
+ else
+ return (0);
+}
+#else
+static int
+get_xfer_size(struct tree *t, int fd, const char *path)
+{
+ (void)t; /* UNUSED */
+ (void)fd; /* UNUSED */
+ (void)path; /* UNUSED */
+ return (1);/* Not supported */
+}
+#endif
+
+#if defined(HAVE_STATVFS)
+static inline __LA_UNUSED void
+set_statvfs_transfer_size(struct filesystem *fs, const struct statvfs *sfs)
+{
+ fs->xfer_align = sfs->f_frsize > 0 ? (long)sfs->f_frsize : -1;
+ fs->max_xfer_size = -1;
+#if defined(HAVE_STRUCT_STATVFS_F_IOSIZE)
+ fs->min_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+ fs->incr_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+#else
+ fs->min_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+ fs->incr_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+#endif
+}
+#endif
+
+#if defined(HAVE_STRUCT_STATFS)
+static inline __LA_UNUSED void
+set_statfs_transfer_size(struct filesystem *fs, const struct statfs *sfs)
+{
+ fs->xfer_align = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+ fs->max_xfer_size = -1;
+#if defined(HAVE_STRUCT_STATFS_F_IOSIZE)
+ fs->min_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+ fs->incr_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+#else
+ fs->min_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+ fs->incr_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+#endif
+}
+#endif
+
+#if defined(HAVE_STRUCT_STATFS) && defined(HAVE_STATFS) && \
+ defined(HAVE_FSTATFS) && defined(MNT_LOCAL) && !defined(ST_LOCAL)
+
+/*
+ * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X.
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct statfs sfs;
+#if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC)
+/* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make
+ * this accurate; some platforms have both and we need the one that's
+ * used by getvfsbyname()
+ *
+ * Then the following would become:
+ * #if defined(GETVFSBYNAME_ARG_TYPE)
+ * GETVFSBYNAME_ARG_TYPE vfc;
+ * #endif
+ */
+# if defined(HAVE_STRUCT_XVFSCONF)
+ struct xvfsconf vfc;
+# else
+ struct vfsconf vfc;
+# endif
+#endif
+ int r, xr = 0;
+#if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX)
+ long nm;
+#endif
+
+ t->current_filesystem->synthetic = -1;
+ t->current_filesystem->remote = -1;
+ if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+ /*
+ * Get file system statistics on any directory
+ * where current is.
+ */
+ int fd = openat(tree_current_dir_fd(t),
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "openat failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = fstatfs(fd, &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, fd, NULL);
+ close(fd);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = statfs(tree_current_access_path(t), &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+ } else {
+ r = fstatfs(tree_current_dir_fd(t), &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+ }
+ if (r == -1 || xr == -1) {
+ archive_set_error(&a->archive, errno, "statfs failed");
+ return (ARCHIVE_FAILED);
+ } else if (xr == 1) {
+ /* pathconf(_PC_REX_*) operations are not supported. */
+ set_statfs_transfer_size(t->current_filesystem, &sfs);
+ }
+ if (sfs.f_flags & MNT_LOCAL)
+ t->current_filesystem->remote = 0;
+ else
+ t->current_filesystem->remote = 1;
+
+#if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC)
+ r = getvfsbyname(sfs.f_fstypename, &vfc);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno, "getvfsbyname failed");
+ return (ARCHIVE_FAILED);
+ }
+ if (vfc.vfc_flags & VFCF_SYNTHETIC)
+ t->current_filesystem->synthetic = 1;
+ else
+ t->current_filesystem->synthetic = 0;
+#endif
+
+#if defined(MNT_NOATIME)
+ if (sfs.f_flags & MNT_NOATIME)
+ t->current_filesystem->noatime = 1;
+ else
+#endif
+ t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+ /* Set maximum filename length. */
+#if defined(HAVE_STRUCT_STATFS_F_NAMEMAX)
+ t->current_filesystem->name_max = sfs.f_namemax;
+#else
+# if defined(_PC_NAME_MAX)
+ /* Mac OS X does not have f_namemax in struct statfs. */
+ if (tree_current_is_symblic_link_target(t)) {
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);
+ } else
+ nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);
+# else
+ nm = -1;
+# endif
+ if (nm == -1)
+ t->current_filesystem->name_max = NAME_MAX;
+ else
+ t->current_filesystem->name_max = nm;
+#endif
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif /* USE_READDIR_R */
+ return (ARCHIVE_OK);
+}
+
+#elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL)
+
+/*
+ * Gather current filesystem properties on NetBSD
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct statvfs svfs;
+ int r, xr = 0;
+
+ t->current_filesystem->synthetic = -1;
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ if (tree_current_is_symblic_link_target(t)) {
+ r = statvfs(tree_current_access_path(t), &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, tree_current_access_path(t));
+ } else {
+#ifdef HAVE_FSTATVFS
+ r = fstatvfs(tree_current_dir_fd(t), &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+ r = statvfs(".", &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, ".");
+#endif
+ }
+ if (r == -1 || xr == -1) {
+ t->current_filesystem->remote = -1;
+ archive_set_error(&a->archive, errno, "statvfs failed");
+ return (ARCHIVE_FAILED);
+ } else if (xr == 1) {
+ /* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN
+ * for pathconf() function. */
+ set_statvfs_transfer_size(t->current_filesystem, &svfs);
+ }
+ if (svfs.f_flag & ST_LOCAL)
+ t->current_filesystem->remote = 0;
+ else
+ t->current_filesystem->remote = 1;
+
+#if defined(ST_NOATIME)
+ if (svfs.f_flag & ST_NOATIME)
+ t->current_filesystem->noatime = 1;
+ else
+#endif
+ t->current_filesystem->noatime = 0;
+
+ /* Set maximum filename length. */
+ t->current_filesystem->name_max = svfs.f_namemax;
+ return (ARCHIVE_OK);
+}
+
+#elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\
+ defined(HAVE_STATFS) && defined(HAVE_FSTATFS)
+/*
+ * Note: statfs is deprecated since LSB 3.2
+ */
+
+#ifndef CIFS_SUPER_MAGIC
+#define CIFS_SUPER_MAGIC 0xFF534D42
+#endif
+#ifndef DEVFS_SUPER_MAGIC
+#define DEVFS_SUPER_MAGIC 0x1373
+#endif
+
+/*
+ * Gather current filesystem properties on Linux
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct statfs sfs;
+#if defined(HAVE_STATVFS)
+ struct statvfs svfs;
+#endif
+ int r, vr = 0, xr = 0;
+
+ if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+ /*
+ * Get file system statistics on any directory
+ * where current is.
+ */
+ int fd = openat(tree_current_dir_fd(t),
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "openat failed");
+ return (ARCHIVE_FAILED);
+ }
+#if defined(HAVE_FSTATVFS)
+ vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */
+#endif
+ r = fstatfs(fd, &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, fd, NULL);
+ close(fd);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+#if defined(HAVE_STATVFS)
+ vr = statvfs(tree_current_access_path(t), &svfs);
+#endif
+ r = statfs(tree_current_access_path(t), &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+ } else {
+#ifdef HAVE_FSTATFS
+#if defined(HAVE_FSTATVFS)
+ vr = fstatvfs(tree_current_dir_fd(t), &svfs);
+#endif
+ r = fstatfs(tree_current_dir_fd(t), &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+#if defined(HAVE_STATVFS)
+ vr = statvfs(".", &svfs);
+#endif
+ r = statfs(".", &sfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, ".");
+#endif
+ }
+ if (r == -1 || xr == -1 || vr == -1) {
+ t->current_filesystem->synthetic = -1;
+ t->current_filesystem->remote = -1;
+ archive_set_error(&a->archive, errno, "statfs failed");
+ return (ARCHIVE_FAILED);
+ } else if (xr == 1) {
+ /* pathconf(_PC_REX_*) operations are not supported. */
+#if defined(HAVE_STATVFS)
+ set_statvfs_transfer_size(t->current_filesystem, &svfs);
+#else
+ set_statfs_transfer_size(t->current_filesystem, &sfs);
+#endif
+ }
+ switch (sfs.f_type) {
+ case AFS_SUPER_MAGIC:
+ case CIFS_SUPER_MAGIC:
+ case CODA_SUPER_MAGIC:
+ case NCP_SUPER_MAGIC:/* NetWare */
+ case NFS_SUPER_MAGIC:
+ case SMB_SUPER_MAGIC:
+ t->current_filesystem->remote = 1;
+ t->current_filesystem->synthetic = 0;
+ break;
+ case DEVFS_SUPER_MAGIC:
+ case PROC_SUPER_MAGIC:
+ case USBDEVICE_SUPER_MAGIC:
+ t->current_filesystem->remote = 0;
+ t->current_filesystem->synthetic = 1;
+ break;
+ default:
+ t->current_filesystem->remote = 0;
+ t->current_filesystem->synthetic = 0;
+ break;
+ }
+
+#if defined(ST_NOATIME)
+#if defined(HAVE_STATVFS)
+ if (svfs.f_flag & ST_NOATIME)
+#else
+ if (sfs.f_flags & ST_NOATIME)
+#endif
+ t->current_filesystem->noatime = 1;
+ else
+#endif
+ t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+ /* Set maximum filename length. */
+#if defined(HAVE_STATVFS)
+ t->current_filesystem->name_max = svfs.f_namemax;
+#else
+ t->current_filesystem->name_max = sfs.f_namelen;
+#endif
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif
+ return (ARCHIVE_OK);
+}
+
+#elif defined(HAVE_SYS_STATVFS_H) &&\
+ (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS))
+
+/*
+ * Gather current filesystem properties on other posix platform.
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ struct statvfs svfs;
+ int r, xr = 0;
+
+ t->current_filesystem->synthetic = -1;/* Not supported */
+ t->current_filesystem->remote = -1;/* Not supported */
+ if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+ /*
+ * Get file system statistics on any directory
+ * where current is.
+ */
+ int fd = openat(tree_current_dir_fd(t),
+ tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "openat failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = fstatvfs(fd, &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, fd, NULL);
+ close(fd);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = statvfs(tree_current_access_path(t), &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+ } else {
+#ifdef HAVE_FSTATVFS
+ r = fstatvfs(tree_current_dir_fd(t), &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ r = statvfs(".", &svfs);
+ if (r == 0)
+ xr = get_xfer_size(t, -1, ".");
+#endif
+ }
+ if (r == -1 || xr == -1) {
+ t->current_filesystem->synthetic = -1;
+ t->current_filesystem->remote = -1;
+ archive_set_error(&a->archive, errno, "statvfs failed");
+ return (ARCHIVE_FAILED);
+ } else if (xr == 1) {
+ /* pathconf(_PC_REX_*) operations are not supported. */
+ set_statvfs_transfer_size(t->current_filesystem, &svfs);
+ }
+
+#if defined(ST_NOATIME)
+ if (svfs.f_flag & ST_NOATIME)
+ t->current_filesystem->noatime = 1;
+ else
+#endif
+ t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+ /* Set maximum filename length. */
+ t->current_filesystem->name_max = svfs.f_namemax;
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif
+ return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ * Generic: Gather current filesystem properties.
+ * TODO: Is this generic function really needed?
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+#if defined(_PC_NAME_MAX) && defined(USE_READDIR_R)
+ long nm;
+#endif
+ t->current_filesystem->synthetic = -1;/* Not supported */
+ t->current_filesystem->remote = -1;/* Not supported */
+ t->current_filesystem->noatime = 0;
+ (void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */
+ t->current_filesystem->xfer_align = -1;/* Unknown */
+ t->current_filesystem->max_xfer_size = -1;
+ t->current_filesystem->min_xfer_size = -1;
+ t->current_filesystem->incr_xfer_size = -1;
+
+#if defined(USE_READDIR_R)
+ /* Set maximum filename length. */
+# if defined(_PC_NAME_MAX)
+ if (tree_current_is_symblic_link_target(t)) {
+ if (tree_enter_working_dir(t) != 0) {
+ archive_set_error(&a->archive, errno, "fchdir failed");
+ return (ARCHIVE_FAILED);
+ }
+ nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);
+ } else
+ nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);
+ if (nm == -1)
+# endif /* _PC_NAME_MAX */
+ /*
+ * Some systems (HP-UX or others?) incorrectly defined
+ * NAME_MAX macro to be a smaller value.
+ */
+# if defined(NAME_MAX) && NAME_MAX >= 255
+ t->current_filesystem->name_max = NAME_MAX;
+# else
+ /* No way to get a trusted value of maximum filename
+ * length. */
+ t->current_filesystem->name_max = PATH_MAX;
+# endif /* NAME_MAX */
+# if defined(_PC_NAME_MAX)
+ else
+ t->current_filesystem->name_max = nm;
+# endif /* _PC_NAME_MAX */
+ if (t->current_filesystem->name_max == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot determine name_max");
+ return (ARCHIVE_FAILED);
+ }
+#endif /* USE_READDIR_R */
+ return (ARCHIVE_OK);
+}
+
+#endif
+
+static int
+close_and_restore_time(int fd, struct tree *t, struct restore_time *rt)
+{
+#ifndef HAVE_UTIMES
+ (void)t; /* UNUSED */
+ (void)rt; /* UNUSED */
+ return (close(fd));
+#else
+#if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__)
+ struct timespec timespecs[2];
+#endif
+ struct timeval times[2];
+
+ if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) {
+ if (fd >= 0)
+ return (close(fd));
+ else
+ return (0);
+ }
+
+#if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__)
+ timespecs[1].tv_sec = rt->mtime;
+ timespecs[1].tv_nsec = rt->mtime_nsec;
+
+ timespecs[0].tv_sec = rt->atime;
+ timespecs[0].tv_nsec = rt->atime_nsec;
+ /* futimens() is defined in POSIX.1-2008. */
+ if (futimens(fd, timespecs) == 0)
+ return (close(fd));
+#endif
+
+ times[1].tv_sec = rt->mtime;
+ times[1].tv_usec = rt->mtime_nsec / 1000;
+
+ times[0].tv_sec = rt->atime;
+ times[0].tv_usec = rt->atime_nsec / 1000;
+
+#if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__)
+ if (futimes(fd, times) == 0)
+ return (close(fd));
+#endif
+ close(fd);
+#if defined(HAVE_FUTIMESAT)
+ if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0)
+ return (0);
+#endif
+#ifdef HAVE_LUTIMES
+ if (lutimes(rt->name, times) != 0)
+#else
+ if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0)
+#endif
+ return (-1);
+#endif
+ return (0);
+}
+
+static int
+open_on_current_dir(struct tree *t, const char *path, int flags)
+{
+#ifdef HAVE_OPENAT
+ return (openat(tree_current_dir_fd(t), path, flags));
+#else
+ if (tree_enter_working_dir(t) != 0)
+ return (-1);
+ return (open(path, flags));
+#endif
+}
+
+static int
+tree_dup(int fd)
+{
+ int new_fd;
+#ifdef F_DUPFD_CLOEXEC
+ static volatile int can_dupfd_cloexec = 1;
+
+ if (can_dupfd_cloexec) {
+ new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ if (new_fd != -1)
+ return (new_fd);
+ /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC,
+ * but it cannot be used. So we have to try dup(). */
+ /* We won't try F_DUPFD_CLOEXEC. */
+ can_dupfd_cloexec = 0;
+ }
+#endif /* F_DUPFD_CLOEXEC */
+ new_fd = dup(fd);
+ __archive_ensure_cloexec_flag(new_fd);
+ return (new_fd);
+}
+
+/*
+ * Add a directory path to the current stack.
+ */
+static void
+tree_push(struct tree *t, const char *path, int filesystem_id,
+ int64_t dev, int64_t ino, struct restore_time *rt)
+{
+ struct tree_entry *te;
+
+ te = calloc(1, sizeof(*te));
+ if (te == NULL)
+ __archive_errx(1, "Out of memory");
+ te->next = t->stack;
+ te->parent = t->current;
+ if (te->parent)
+ te->depth = te->parent->depth + 1;
+ t->stack = te;
+ archive_string_init(&te->name);
+ te->symlink_parent_fd = -1;
+ archive_strcpy(&te->name, path);
+ te->flags = needsDescent | needsOpen | needsAscent;
+ te->filesystem_id = filesystem_id;
+ te->dev = dev;
+ te->ino = ino;
+ te->dirname_length = t->dirname_length;
+ te->restore_time.name = te->name.s;
+ if (rt != NULL) {
+ te->restore_time.mtime = rt->mtime;
+ te->restore_time.mtime_nsec = rt->mtime_nsec;
+ te->restore_time.atime = rt->atime;
+ te->restore_time.atime_nsec = rt->atime_nsec;
+ te->restore_time.filetype = rt->filetype;
+ te->restore_time.noatime = rt->noatime;
+ }
+}
+
+/*
+ * Append a name to the current dir path.
+ */
+static void
+tree_append(struct tree *t, const char *name, size_t name_length)
+{
+ size_t size_needed;
+
+ t->path.s[t->dirname_length] = '\0';
+ t->path.length = t->dirname_length;
+ /* Strip trailing '/' from name, unless entire name is "/". */
+ while (name_length > 1 && name[name_length - 1] == '/')
+ name_length--;
+
+ /* Resize pathname buffer as needed. */
+ size_needed = name_length + t->dirname_length + 2;
+ archive_string_ensure(&t->path, size_needed);
+ /* Add a separating '/' if it's needed. */
+ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/')
+ archive_strappend_char(&t->path, '/');
+ t->basename = t->path.s + archive_strlen(&t->path);
+ archive_strncat(&t->path, name, name_length);
+ t->restore_time.name = t->basename;
+}
+
+/*
+ * Open a directory tree for traversal.
+ */
+static struct tree *
+tree_open(const char *path, int symlink_mode, int restore_time)
+{
+ struct tree *t;
+
+ if ((t = calloc(1, sizeof(*t))) == NULL)
+ return (NULL);
+ archive_string_init(&t->path);
+ archive_string_ensure(&t->path, 31);
+ t->initial_symlink_mode = symlink_mode;
+ return (tree_reopen(t, path, restore_time));
+}
+
+static struct tree *
+tree_reopen(struct tree *t, const char *path, int restore_time)
+{
+#if defined(O_PATH)
+ /* Linux */
+ const int o_flag = O_PATH;
+#elif defined(O_SEARCH)
+ /* SunOS */
+ const int o_flag = O_SEARCH;
+#elif defined(__FreeBSD__) && defined(O_EXEC)
+ /* FreeBSD */
+ const int o_flag = O_EXEC;
+#endif
+
+ t->flags = (restore_time != 0)?needsRestoreTimes:0;
+ t->flags |= onInitialDir;
+ t->visit_type = 0;
+ t->tree_errno = 0;
+ t->dirname_length = 0;
+ t->depth = 0;
+ t->descend = 0;
+ t->current = NULL;
+ t->d = INVALID_DIR_HANDLE;
+ t->symlink_mode = t->initial_symlink_mode;
+ archive_string_empty(&t->path);
+ t->entry_fd = -1;
+ t->entry_eof = 0;
+ t->entry_remaining_bytes = 0;
+ t->initial_filesystem_id = -1;
+
+ /* First item is set up a lot like a symlink traversal. */
+ tree_push(t, path, 0, 0, 0, NULL);
+ t->stack->flags = needsFirstVisit;
+ t->maxOpenCount = t->openCount = 1;
+ t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC);
+#if defined(O_PATH) || defined(O_SEARCH) || \
+ (defined(__FreeBSD__) && defined(O_EXEC))
+ /*
+ * Most likely reason to fail opening "." is that it's not readable,
+ * so try again for execute. The consequences of not opening this are
+ * unhelpful and unnecessary errors later.
+ */
+ if (t->initial_dir_fd < 0)
+ t->initial_dir_fd = open(".", o_flag | O_CLOEXEC);
+#endif
+ __archive_ensure_cloexec_flag(t->initial_dir_fd);
+ t->working_dir_fd = tree_dup(t->initial_dir_fd);
+ return (t);
+}
+
+static int
+tree_descent(struct tree *t)
+{
+ int flag, new_fd, r = 0;
+
+ t->dirname_length = archive_strlen(&t->path);
+ flag = O_RDONLY | O_CLOEXEC;
+#if defined(O_DIRECTORY)
+ flag |= O_DIRECTORY;
+#endif
+ new_fd = open_on_current_dir(t, t->stack->name.s, flag);
+ __archive_ensure_cloexec_flag(new_fd);
+ if (new_fd < 0) {
+ t->tree_errno = errno;
+ r = TREE_ERROR_DIR;
+ } else {
+ t->depth++;
+ /* If it is a link, set up fd for the ascent. */
+ if (t->stack->flags & isDirLink) {
+ t->stack->symlink_parent_fd = t->working_dir_fd;
+ t->openCount++;
+ if (t->openCount > t->maxOpenCount)
+ t->maxOpenCount = t->openCount;
+ } else
+ close(t->working_dir_fd);
+ /* Renew the current working directory. */
+ t->working_dir_fd = new_fd;
+ t->flags &= ~onWorkingDir;
+ }
+ return (r);
+}
+
+/*
+ * We've finished a directory; ascend back to the parent.
+ */
+static int
+tree_ascend(struct tree *t)
+{
+ struct tree_entry *te;
+ int new_fd, r = 0, prev_dir_fd;
+
+ te = t->stack;
+ prev_dir_fd = t->working_dir_fd;
+ if (te->flags & isDirLink)
+ new_fd = te->symlink_parent_fd;
+ else {
+ new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(new_fd);
+ }
+ if (new_fd < 0) {
+ t->tree_errno = errno;
+ r = TREE_ERROR_FATAL;
+ } else {
+ /* Renew the current working directory. */
+ t->working_dir_fd = new_fd;
+ t->flags &= ~onWorkingDir;
+ /* Current directory has been changed, we should
+ * close an fd of previous working directory. */
+ close_and_restore_time(prev_dir_fd, t, &te->restore_time);
+ if (te->flags & isDirLink) {
+ t->openCount--;
+ te->symlink_parent_fd = -1;
+ }
+ t->depth--;
+ }
+ return (r);
+}
+
+/*
+ * Return to the initial directory where tree_open() was performed.
+ */
+static int
+tree_enter_initial_dir(struct tree *t)
+{
+ int r = 0;
+
+ if ((t->flags & onInitialDir) == 0) {
+ r = fchdir(t->initial_dir_fd);
+ if (r == 0) {
+ t->flags &= ~onWorkingDir;
+ t->flags |= onInitialDir;
+ }
+ }
+ return (r);
+}
+
+/*
+ * Restore working directory of directory traversals.
+ */
+static int
+tree_enter_working_dir(struct tree *t)
+{
+ int r = 0;
+
+ /*
+ * Change the current directory if really needed.
+ * Sometimes this is unneeded when we did not do
+ * descent.
+ */
+ if (t->depth > 0 && (t->flags & onWorkingDir) == 0) {
+ r = fchdir(t->working_dir_fd);
+ if (r == 0) {
+ t->flags &= ~onInitialDir;
+ t->flags |= onWorkingDir;
+ }
+ }
+ return (r);
+}
+
+static int
+tree_current_dir_fd(struct tree *t)
+{
+ return (t->working_dir_fd);
+}
+
+/*
+ * Pop the working stack.
+ */
+static void
+tree_pop(struct tree *t)
+{
+ struct tree_entry *te;
+
+ t->path.s[t->dirname_length] = '\0';
+ t->path.length = t->dirname_length;
+ if (t->stack == t->current && t->current != NULL)
+ t->current = t->current->parent;
+ te = t->stack;
+ t->stack = te->next;
+ t->dirname_length = te->dirname_length;
+ t->basename = t->path.s + t->dirname_length;
+ while (t->basename[0] == '/')
+ t->basename++;
+ archive_string_free(&te->name);
+ free(te);
+}
+
+/*
+ * Get the next item in the tree traversal.
+ */
+static int
+tree_next(struct tree *t)
+{
+ int r;
+
+ while (t->stack != NULL) {
+ /* If there's an open dir, get the next entry from there. */
+ if (t->d != INVALID_DIR_HANDLE) {
+ r = tree_dir_next_posix(t);
+ if (r == 0)
+ continue;
+ return (r);
+ }
+
+ if (t->stack->flags & needsFirstVisit) {
+ /* Top stack item needs a regular visit. */
+ t->current = t->stack;
+ tree_append(t, t->stack->name.s,
+ archive_strlen(&(t->stack->name)));
+ /* t->dirname_length = t->path_length; */
+ /* tree_pop(t); */
+ t->stack->flags &= ~needsFirstVisit;
+ return (t->visit_type = TREE_REGULAR);
+ } else if (t->stack->flags & needsDescent) {
+ /* Top stack item is dir to descend into. */
+ t->current = t->stack;
+ tree_append(t, t->stack->name.s,
+ archive_strlen(&(t->stack->name)));
+ t->stack->flags &= ~needsDescent;
+ r = tree_descent(t);
+ if (r != 0) {
+ tree_pop(t);
+ t->visit_type = r;
+ } else
+ t->visit_type = TREE_POSTDESCENT;
+ return (t->visit_type);
+ } else if (t->stack->flags & needsOpen) {
+ t->stack->flags &= ~needsOpen;
+ r = tree_dir_next_posix(t);
+ if (r == 0)
+ continue;
+ return (r);
+ } else if (t->stack->flags & needsAscent) {
+ /* Top stack item is dir and we're done with it. */
+ r = tree_ascend(t);
+ tree_pop(t);
+ t->visit_type = r != 0 ? r : TREE_POSTASCENT;
+ return (t->visit_type);
+ } else {
+ /* Top item on stack is dead. */
+ tree_pop(t);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ }
+ }
+ return (t->visit_type = 0);
+}
+
+static int
+tree_dir_next_posix(struct tree *t)
+{
+ int r;
+ const char *name;
+ size_t namelen;
+
+ if (t->d == NULL) {
+#if defined(USE_READDIR_R)
+ size_t dirent_size;
+#endif
+
+#if defined(HAVE_FDOPENDIR)
+ t->d = fdopendir(tree_dup(t->working_dir_fd));
+#else /* HAVE_FDOPENDIR */
+ if (tree_enter_working_dir(t) == 0) {
+ t->d = opendir(".");
+#ifdef HAVE_DIRFD
+ __archive_ensure_cloexec_flag(dirfd(t->d));
+#endif
+ }
+#endif /* HAVE_FDOPENDIR */
+ if (t->d == NULL) {
+ r = tree_ascend(t); /* Undo "chdir" */
+ tree_pop(t);
+ t->tree_errno = errno;
+ t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
+ return (t->visit_type);
+ }
+#if defined(USE_READDIR_R)
+ dirent_size = offsetof(struct dirent, d_name) +
+ t->filesystem_table[t->current->filesystem_id].name_max + 1;
+ if (t->dirent == NULL || t->dirent_allocated < dirent_size) {
+ free(t->dirent);
+ t->dirent = malloc(dirent_size);
+ if (t->dirent == NULL) {
+ closedir(t->d);
+ t->d = INVALID_DIR_HANDLE;
+ (void)tree_ascend(t);
+ tree_pop(t);
+ t->tree_errno = ENOMEM;
+ t->visit_type = TREE_ERROR_DIR;
+ return (t->visit_type);
+ }
+ t->dirent_allocated = dirent_size;
+ }
+#endif /* USE_READDIR_R */
+ }
+ for (;;) {
+ errno = 0;
+#if defined(USE_READDIR_R)
+ r = readdir_r(t->d, t->dirent, &t->de);
+#ifdef _AIX
+ /* Note: According to the man page, return value 9 indicates
+ * that the readdir_r was not successful and the error code
+ * is set to the global errno variable. And then if the end
+ * of directory entries was reached, the return value is 9
+ * and the third parameter is set to NULL and errno is
+ * unchanged. */
+ if (r == 9)
+ r = errno;
+#endif /* _AIX */
+ if (r != 0 || t->de == NULL) {
+#else
+ t->de = readdir(t->d);
+ if (t->de == NULL) {
+ r = errno;
+#endif
+ closedir(t->d);
+ t->d = INVALID_DIR_HANDLE;
+ if (r != 0) {
+ t->tree_errno = r;
+ t->visit_type = TREE_ERROR_DIR;
+ return (t->visit_type);
+ } else
+ return (0);
+ }
+ name = t->de->d_name;
+ namelen = D_NAMELEN(t->de);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ if (name[0] == '.' && name[1] == '\0')
+ continue;
+ if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
+ continue;
+ tree_append(t, name, namelen);
+ return (t->visit_type = TREE_REGULAR);
+ }
+}
+
+
+/*
+ * Get the stat() data for the entry just returned from tree_next().
+ */
+static const struct stat *
+tree_current_stat(struct tree *t)
+{
+ if (!(t->flags & hasStat)) {
+#ifdef HAVE_FSTATAT
+ if (fstatat(tree_current_dir_fd(t),
+ tree_current_access_path(t), &t->st, 0) != 0)
+#else
+ if (tree_enter_working_dir(t) != 0)
+ return NULL;
+ if (la_stat(tree_current_access_path(t), &t->st) != 0)
+#endif
+ return NULL;
+ t->flags |= hasStat;
+ }
+ return (&t->st);
+}
+
+/*
+ * Get the lstat() data for the entry just returned from tree_next().
+ */
+static const struct stat *
+tree_current_lstat(struct tree *t)
+{
+ if (!(t->flags & hasLstat)) {
+#ifdef HAVE_FSTATAT
+ if (fstatat(tree_current_dir_fd(t),
+ tree_current_access_path(t), &t->lst,
+ AT_SYMLINK_NOFOLLOW) != 0)
+#else
+ if (tree_enter_working_dir(t) != 0)
+ return NULL;
+#ifdef HAVE_LSTAT
+ if (lstat(tree_current_access_path(t), &t->lst) != 0)
+#else
+ if (la_stat(tree_current_access_path(t), &t->lst) != 0)
+#endif
+#endif
+ return NULL;
+ t->flags |= hasLstat;
+ }
+ return (&t->lst);
+}
+
+/*
+ * Test whether current entry is a dir or link to a dir.
+ */
+static int
+tree_current_is_dir(struct tree *t)
+{
+ const struct stat *st;
+ /*
+ * If we already have lstat() info, then try some
+ * cheap tests to determine if this is a dir.
+ */
+ if (t->flags & hasLstat) {
+ /* If lstat() says it's a dir, it must be a dir. */
+ st = tree_current_lstat(t);
+ if (st == NULL)
+ return 0;
+ if (S_ISDIR(st->st_mode))
+ return 1;
+ /* Not a dir; might be a link to a dir. */
+ /* If it's not a link, then it's not a link to a dir. */
+ if (!S_ISLNK(st->st_mode))
+ return 0;
+ /*
+ * It's a link, but we don't know what it's a link to,
+ * so we'll have to use stat().
+ */
+ }
+
+ st = tree_current_stat(t);
+ /* If we can't stat it, it's not a dir. */
+ if (st == NULL)
+ return 0;
+ /* Use the definitive test. Hopefully this is cached. */
+ return (S_ISDIR(st->st_mode));
+}
+
+/*
+ * Test whether current entry is a physical directory. Usually, we
+ * already have at least one of stat() or lstat() in memory, so we
+ * use tricks to try to avoid an extra trip to the disk.
+ */
+static int
+tree_current_is_physical_dir(struct tree *t)
+{
+ const struct stat *st;
+
+ /*
+ * If stat() says it isn't a dir, then it's not a dir.
+ * If stat() data is cached, this check is free, so do it first.
+ */
+ if (t->flags & hasStat) {
+ st = tree_current_stat(t);
+ if (st == NULL)
+ return (0);
+ if (!S_ISDIR(st->st_mode))
+ return (0);
+ }
+
+ /*
+ * Either stat() said it was a dir (in which case, we have
+ * to determine whether it's really a link to a dir) or
+ * stat() info wasn't available. So we use lstat(), which
+ * hopefully is already cached.
+ */
+
+ st = tree_current_lstat(t);
+ /* If we can't stat it, it's not a dir. */
+ if (st == NULL)
+ return 0;
+ /* Use the definitive test. Hopefully this is cached. */
+ return (S_ISDIR(st->st_mode));
+}
+
+/*
+ * Test whether the same file has been in the tree as its parent.
+ */
+static int
+tree_target_is_same_as_parent(struct tree *t, const struct stat *st)
+{
+ struct tree_entry *te;
+
+ for (te = t->current->parent; te != NULL; te = te->parent) {
+ if (te->dev == (int64_t)st->st_dev &&
+ te->ino == (int64_t)st->st_ino)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Test whether the current file is symbolic link target and
+ * on the other filesystem.
+ */
+static int
+tree_current_is_symblic_link_target(struct tree *t)
+{
+ static const struct stat *lst, *st;
+
+ lst = tree_current_lstat(t);
+ st = tree_current_stat(t);
+ return (st != NULL && lst != NULL &&
+ (int64_t)st->st_dev == t->current_filesystem->dev &&
+ st->st_dev != lst->st_dev);
+}
+
+/*
+ * Return the access path for the entry just returned from tree_next().
+ */
+static const char *
+tree_current_access_path(struct tree *t)
+{
+ return (t->basename);
+}
+
+/*
+ * Return the full path for the entry just returned from tree_next().
+ */
+static const char *
+tree_current_path(struct tree *t)
+{
+ return (t->path.s);
+}
+
+/*
+ * Terminate the traversal.
+ */
+static void
+tree_close(struct tree *t)
+{
+
+ if (t == NULL)
+ return;
+ if (t->entry_fd >= 0) {
+ close_and_restore_time(t->entry_fd, t, &t->restore_time);
+ t->entry_fd = -1;
+ }
+ /* Close the handle of readdir(). */
+ if (t->d != INVALID_DIR_HANDLE) {
+ closedir(t->d);
+ t->d = INVALID_DIR_HANDLE;
+ }
+ /* Release anything remaining in the stack. */
+ while (t->stack != NULL) {
+ if (t->stack->flags & isDirLink)
+ close(t->stack->symlink_parent_fd);
+ tree_pop(t);
+ }
+ if (t->working_dir_fd >= 0) {
+ close(t->working_dir_fd);
+ t->working_dir_fd = -1;
+ }
+ if (t->initial_dir_fd >= 0) {
+ close(t->initial_dir_fd);
+ t->initial_dir_fd = -1;
+ }
+}
+
+/*
+ * Release any resources.
+ */
+static void
+tree_free(struct tree *t)
+{
+ int i;
+
+ if (t == NULL)
+ return;
+ archive_string_free(&t->path);
+#if defined(USE_READDIR_R)
+ free(t->dirent);
+#endif
+ free(t->sparse_list);
+ for (i = 0; i < t->max_filesystem_id; i++)
+ free(t->filesystem_table[i].allocation_ptr);
+ free(t->filesystem_table);
+ free(t);
+}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_private.h b/contrib/libs/libarchive/libarchive/archive_read_disk_private.h
new file mode 100644
index 0000000000..bc8abc15d1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_private.h
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_read_disk_private.h 201105 2009-12-28 03:20:54Z kientzle $
+ */
+
+#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
+#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_platform_acl.h"
+
+struct tree;
+struct archive_entry;
+
+struct archive_read_disk {
+ struct archive archive;
+
+ /* Reused by archive_read_next_header() */
+ struct archive_entry *entry;
+
+ /*
+ * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid,
+ * following an old BSD convention. 'L' follows all symlinks,
+ * 'P' follows none, 'H' follows symlinks only for the first
+ * item.
+ */
+ char symlink_mode;
+
+ /*
+ * Since symlink interaction changes, we need to track whether
+ * we're following symlinks for the current item. 'L' mode above
+ * sets this true, 'P' sets it false, 'H' changes it as we traverse.
+ */
+ char follow_symlinks; /* Either 'L' or 'P'. */
+
+ /* Directory traversals. */
+ struct tree *tree;
+ int (*open_on_current_dir)(struct tree*, const char *, int);
+ int (*tree_current_dir_fd)(struct tree*);
+ int (*tree_enter_working_dir)(struct tree*);
+
+ /* Bitfield with ARCHIVE_READDISK_* tunables */
+ int flags;
+
+ const char * (*lookup_gname)(void *private, int64_t gid);
+ void (*cleanup_gname)(void *private);
+ void *lookup_gname_data;
+ const char * (*lookup_uname)(void *private, int64_t uid);
+ void (*cleanup_uname)(void *private);
+ void *lookup_uname_data;
+
+ int (*metadata_filter_func)(struct archive *, void *,
+ struct archive_entry *);
+ void *metadata_filter_data;
+
+ /* ARCHIVE_MATCH object. */
+ struct archive *matching;
+ /* Callback function, this will be invoked when ARCHIVE_MATCH
+ * archive_match_*_excluded_ae return true. */
+ void (*excluded_cb_func)(struct archive *, void *,
+ struct archive_entry *);
+ void *excluded_cb_data;
+};
+
+const char *
+archive_read_disk_entry_setup_path(struct archive_read_disk *,
+ struct archive_entry *, int *);
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *,
+ struct archive_entry *, int *);
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_set_standard_lookup.c b/contrib/libs/libarchive/libarchive/archive_read_disk_set_standard_lookup.c
new file mode 100644
index 0000000000..c7fd2471ec
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_set_standard_lookup.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_set_standard_lookup.c 201109 2009-12-28 03:30:31Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+int
+archive_read_disk_set_standard_lookup(struct archive *a)
+{
+ archive_set_error(a, -1, "Standard lookups not available on Windows");
+ return (ARCHIVE_FATAL);
+}
+#else /* ! (_WIN32 && !__CYGWIN__) */
+#define name_cache_size 127
+
+static const char * const NO_NAME = "(noname)";
+
+struct name_cache {
+ struct archive *archive;
+ char *buff;
+ size_t buff_size;
+ int probes;
+ int hits;
+ size_t size;
+ struct {
+ id_t id;
+ const char *name;
+ } cache[name_cache_size];
+};
+
+static const char * lookup_gname(void *, int64_t);
+static const char * lookup_uname(void *, int64_t);
+static void cleanup(void *);
+static const char * lookup_gname_helper(struct name_cache *, id_t gid);
+static const char * lookup_uname_helper(struct name_cache *, id_t uid);
+
+/*
+ * Installs functions that use getpwuid()/getgrgid()---along with
+ * a simple cache to accelerate such lookups---into the archive_read_disk
+ * object. This is in a separate file because getpwuid()/getgrgid()
+ * can pull in a LOT of library code (including NIS/LDAP functions, which
+ * pull in DNS resolvers, etc). This can easily top 500kB, which makes
+ * it inappropriate for some space-constrained applications.
+ *
+ * Applications that are size-sensitive may want to just use the
+ * real default functions (defined in archive_read_disk.c) that just
+ * use the uid/gid without the lookup. Or define your own custom functions
+ * if you prefer.
+ */
+int
+archive_read_disk_set_standard_lookup(struct archive *a)
+{
+ struct name_cache *ucache = malloc(sizeof(struct name_cache));
+ struct name_cache *gcache = malloc(sizeof(struct name_cache));
+
+ if (ucache == NULL || gcache == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate uname/gname lookup cache");
+ free(ucache);
+ free(gcache);
+ return (ARCHIVE_FATAL);
+ }
+
+ memset(ucache, 0, sizeof(*ucache));
+ ucache->archive = a;
+ ucache->size = name_cache_size;
+ memset(gcache, 0, sizeof(*gcache));
+ gcache->archive = a;
+ gcache->size = name_cache_size;
+
+ archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
+ archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
+
+ return (ARCHIVE_OK);
+}
+
+static void
+cleanup(void *data)
+{
+ struct name_cache *cache = (struct name_cache *)data;
+ size_t i;
+
+ if (cache != NULL) {
+ for (i = 0; i < cache->size; i++) {
+ if (cache->cache[i].name != NULL &&
+ cache->cache[i].name != NO_NAME)
+ free((void *)(uintptr_t)cache->cache[i].name);
+ }
+ free(cache->buff);
+ free(cache);
+ }
+}
+
+/*
+ * Lookup uid/gid from uname/gname, return NULL if no match.
+ */
+static const char *
+lookup_name(struct name_cache *cache,
+ const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)
+{
+ const char *name;
+ int slot;
+
+
+ cache->probes++;
+
+ slot = id % cache->size;
+ if (cache->cache[slot].name != NULL) {
+ if (cache->cache[slot].id == id) {
+ cache->hits++;
+ if (cache->cache[slot].name == NO_NAME)
+ return (NULL);
+ return (cache->cache[slot].name);
+ }
+ if (cache->cache[slot].name != NO_NAME)
+ free((void *)(uintptr_t)cache->cache[slot].name);
+ cache->cache[slot].name = NULL;
+ }
+
+ name = (lookup_fn)(cache, id);
+ if (name == NULL) {
+ /* Cache and return the negative response. */
+ cache->cache[slot].name = NO_NAME;
+ cache->cache[slot].id = id;
+ return (NULL);
+ }
+
+ cache->cache[slot].name = name;
+ cache->cache[slot].id = id;
+ return (cache->cache[slot].name);
+}
+
+static const char *
+lookup_uname(void *data, int64_t uid)
+{
+ struct name_cache *uname_cache = (struct name_cache *)data;
+ return (lookup_name(uname_cache,
+ &lookup_uname_helper, (id_t)uid));
+}
+
+#if HAVE_GETPWUID_R
+static const char *
+lookup_uname_helper(struct name_cache *cache, id_t id)
+{
+ struct passwd pwent, *result;
+ char * nbuff;
+ size_t nbuff_size;
+ int r;
+
+ if (cache->buff_size == 0) {
+ cache->buff_size = 256;
+ cache->buff = malloc(cache->buff_size);
+ }
+ if (cache->buff == NULL)
+ return (NULL);
+ for (;;) {
+ result = &pwent; /* Old getpwuid_r ignores last arg. */
+ r = getpwuid_r((uid_t)id, &pwent,
+ cache->buff, cache->buff_size, &result);
+ if (r == 0)
+ break;
+ if (r != ERANGE)
+ break;
+ /* ERANGE means our buffer was too small, but POSIX
+ * doesn't tell us how big the buffer should be, so
+ * we just double it and try again. Because the buffer
+ * is kept around in the cache object, we shouldn't
+ * have to do this very often. */
+ nbuff_size = cache->buff_size * 2;
+ nbuff = realloc(cache->buff, nbuff_size);
+ if (nbuff == NULL)
+ break;
+ cache->buff = nbuff;
+ cache->buff_size = nbuff_size;
+ }
+ if (r != 0) {
+ archive_set_error(cache->archive, errno,
+ "Can't lookup user for id %d", (int)id);
+ return (NULL);
+ }
+ if (result == NULL)
+ return (NULL);
+
+ return strdup(result->pw_name);
+}
+#else
+static const char *
+lookup_uname_helper(struct name_cache *cache, id_t id)
+{
+ struct passwd *result;
+ (void)cache; /* UNUSED */
+
+ result = getpwuid((uid_t)id);
+
+ if (result == NULL)
+ return (NULL);
+
+ return strdup(result->pw_name);
+}
+#endif
+
+static const char *
+lookup_gname(void *data, int64_t gid)
+{
+ struct name_cache *gname_cache = (struct name_cache *)data;
+ return (lookup_name(gname_cache,
+ &lookup_gname_helper, (id_t)gid));
+}
+
+#if HAVE_GETGRGID_R
+static const char *
+lookup_gname_helper(struct name_cache *cache, id_t id)
+{
+ struct group grent, *result;
+ char * nbuff;
+ size_t nbuff_size;
+ int r;
+
+ if (cache->buff_size == 0) {
+ cache->buff_size = 256;
+ cache->buff = malloc(cache->buff_size);
+ }
+ if (cache->buff == NULL)
+ return (NULL);
+ for (;;) {
+ result = &grent; /* Old getgrgid_r ignores last arg. */
+ r = getgrgid_r((gid_t)id, &grent,
+ cache->buff, cache->buff_size, &result);
+ if (r == 0)
+ break;
+ if (r != ERANGE)
+ break;
+ /* ERANGE means our buffer was too small, but POSIX
+ * doesn't tell us how big the buffer should be, so
+ * we just double it and try again. */
+ nbuff_size = cache->buff_size * 2;
+ nbuff = realloc(cache->buff, nbuff_size);
+ if (nbuff == NULL)
+ break;
+ cache->buff = nbuff;
+ cache->buff_size = nbuff_size;
+ }
+ if (r != 0) {
+ archive_set_error(cache->archive, errno,
+ "Can't lookup group for id %d", (int)id);
+ return (NULL);
+ }
+ if (result == NULL)
+ return (NULL);
+
+ return strdup(result->gr_name);
+}
+#else
+static const char *
+lookup_gname_helper(struct name_cache *cache, id_t id)
+{
+ struct group *result;
+ (void)cache; /* UNUSED */
+
+ result = getgrgid((gid_t)id);
+
+ if (result == NULL)
+ return (NULL);
+
+ return strdup(result->gr_name);
+}
+#endif
+
+#endif /* ! (_WIN32 && !__CYGWIN__) */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_disk_windows.c b/contrib/libs/libarchive/libarchive/archive_read_disk_windows.c
new file mode 100644
index 0000000000..f92a78a21e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_disk_windows.c
@@ -0,0 +1,2547 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <winioctl.h>
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
+#define IO_REPARSE_TAG_SYMLINK 0xA000000CL
+#endif
+
+/*-
+ * This is a new directory-walking system that addresses a number
+ * of problems I've had with fts(3). In particular, it has no
+ * pathname-length limits (other than the size of 'int'), handles
+ * deep logical traversals, uses considerably less memory, and has
+ * an opaque interface (easier to modify in the future).
+ *
+ * Internally, it keeps a single list of "tree_entry" items that
+ * represent filesystem objects that require further attention.
+ * Non-directories are not kept in memory: they are pulled from
+ * readdir(), returned to the client, then freed as soon as possible.
+ * Any directory entry to be traversed gets pushed onto the stack.
+ *
+ * There is surprisingly little information that needs to be kept for
+ * each item on the stack. Just the name, depth (represented here as the
+ * string length of the parent directory's pathname), and some markers
+ * indicating how to get back to the parent (via chdir("..") for a
+ * regular dir or via fchdir(2) for a symlink).
+ */
+
+struct restore_time {
+ const wchar_t *full_path;
+ FILETIME lastWriteTime;
+ FILETIME lastAccessTime;
+ mode_t filetype;
+};
+
+struct tree_entry {
+ int depth;
+ struct tree_entry *next;
+ struct tree_entry *parent;
+ size_t full_path_dir_length;
+ struct archive_wstring name;
+ struct archive_wstring full_path;
+ size_t dirname_length;
+ int64_t dev;
+ int64_t ino;
+ int flags;
+ int filesystem_id;
+ /* How to restore time of a directory. */
+ struct restore_time restore_time;
+};
+
+struct filesystem {
+ int64_t dev;
+ int synthetic;
+ int remote;
+ DWORD bytesPerSector;
+};
+
+/* Definitions for tree_entry.flags bitmap. */
+#define isDir 1 /* This entry is a regular directory. */
+#define isDirLink 2 /* This entry is a symbolic link to a directory. */
+#define needsFirstVisit 4 /* This is an initial entry. */
+#define needsDescent 8 /* This entry needs to be previsited. */
+#define needsOpen 16 /* This is a directory that needs to be opened. */
+#define needsAscent 32 /* This entry needs to be postvisited. */
+
+/*
+ * On Windows, "first visit" is handled as a pattern to be handed to
+ * _findfirst(). This is consistent with Windows conventions that
+ * file patterns are handled within the application. On Posix,
+ * "first visit" is just returned to the client.
+ */
+
+#define MAX_OVERLAPPED 8
+#define READ_BUFFER_SIZE (1024 * 64) /* Default to 64KB per https://technet.microsoft.com/en-us/library/cc938632.aspx */
+#define DIRECT_IO 0/* Disabled */
+#define ASYNC_IO 1/* Enabled */
+
+/*
+ * Local data for this package.
+ */
+struct tree {
+ struct tree_entry *stack;
+ struct tree_entry *current;
+ HANDLE d;
+ WIN32_FIND_DATAW _findData;
+ WIN32_FIND_DATAW *findData;
+ int flags;
+ int visit_type;
+ /* Error code from last failed operation. */
+ int tree_errno;
+
+ /* A full path with "\\?\" prefix. */
+ struct archive_wstring full_path;
+ size_t full_path_dir_length;
+ /* Dynamically-sized buffer for holding path */
+ struct archive_wstring path;
+
+ /* Last path element */
+ const wchar_t *basename;
+ /* Leading dir length */
+ size_t dirname_length;
+
+ int depth;
+
+ BY_HANDLE_FILE_INFORMATION lst;
+ BY_HANDLE_FILE_INFORMATION st;
+ int descend;
+ /* How to restore time of a file. */
+ struct restore_time restore_time;
+
+ struct entry_sparse {
+ int64_t length;
+ int64_t offset;
+ } *sparse_list, *current_sparse;
+ int sparse_count;
+ int sparse_list_size;
+
+ char initial_symlink_mode;
+ char symlink_mode;
+ struct filesystem *current_filesystem;
+ struct filesystem *filesystem_table;
+ int initial_filesystem_id;
+ int current_filesystem_id;
+ int max_filesystem_id;
+ int allocated_filesystem;
+
+ HANDLE entry_fh;
+ int entry_eof;
+ int64_t entry_remaining_bytes;
+ int64_t entry_total;
+
+ int ol_idx_doing;
+ int ol_idx_done;
+ int ol_num_doing;
+ int ol_num_done;
+ int64_t ol_remaining_bytes;
+ int64_t ol_total;
+ struct la_overlapped {
+ OVERLAPPED ol;
+ struct archive * _a;
+ unsigned char *buff;
+ size_t buff_size;
+ int64_t offset;
+ size_t bytes_expected;
+ size_t bytes_transferred;
+ } ol[MAX_OVERLAPPED];
+ int direct_io;
+ int async_io;
+};
+
+#define bhfi_dev(bhfi) ((bhfi)->dwVolumeSerialNumber)
+/* Treat FileIndex as i-node. We should remove a sequence number
+ * which is high-16-bits of nFileIndexHigh. */
+#define bhfi_ino(bhfi) \
+ ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \
+ + (bhfi)->nFileIndexLow)
+
+/* Definitions for tree.flags bitmap. */
+#define hasStat 16 /* The st entry is valid. */
+#define hasLstat 32 /* The lst entry is valid. */
+#define needsRestoreTimes 128
+
+static int
+tree_dir_next_windows(struct tree *t, const wchar_t *pattern);
+
+/* Initiate/terminate a tree traversal. */
+static struct tree *tree_open(const wchar_t *, int, int);
+static struct tree *tree_reopen(struct tree *, const wchar_t *, int);
+static void tree_close(struct tree *);
+static void tree_free(struct tree *);
+static void tree_push(struct tree *, const wchar_t *, const wchar_t *,
+ int, int64_t, int64_t, struct restore_time *);
+
+/*
+ * tree_next() returns Zero if there is no next entry, non-zero if
+ * there is. Note that directories are visited three times.
+ * Directories are always visited first as part of enumerating their
+ * parent; that is a "regular" visit. If tree_descend() is invoked at
+ * that time, the directory is added to a work list and will
+ * subsequently be visited two more times: once just after descending
+ * into the directory ("postdescent") and again just after ascending
+ * back to the parent ("postascent").
+ *
+ * TREE_ERROR_DIR is returned if the descent failed (because the
+ * directory couldn't be opened, for instance). This is returned
+ * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a
+ * fatal error, but it does imply that the relevant subtree won't be
+ * visited. TREE_ERROR_FATAL is returned for an error that left the
+ * traversal completely hosed. Right now, this is only returned for
+ * chdir() failures during ascent.
+ */
+#define TREE_REGULAR 1
+#define TREE_POSTDESCENT 2
+#define TREE_POSTASCENT 3
+#define TREE_ERROR_DIR -1
+#define TREE_ERROR_FATAL -2
+
+static int tree_next(struct tree *);
+
+/*
+ * Return information about the current entry.
+ */
+
+/*
+ * The current full pathname, length of the full pathname, and a name
+ * that can be used to access the file. Because tree does use chdir
+ * extensively, the access path is almost never the same as the full
+ * current path.
+ *
+ */
+static const wchar_t *tree_current_path(struct tree *);
+static const wchar_t *tree_current_access_path(struct tree *);
+
+/*
+ * Request the lstat() or stat() data for the current path. Since the
+ * tree package needs to do some of this anyway, and caches the
+ * results, you should take advantage of it here if you need it rather
+ * than make a redundant stat() or lstat() call of your own.
+ */
+static const BY_HANDLE_FILE_INFORMATION *tree_current_stat(struct tree *);
+static const BY_HANDLE_FILE_INFORMATION *tree_current_lstat(struct tree *);
+
+/* The following functions use tricks to avoid a certain number of
+ * stat()/lstat() calls. */
+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_dir(struct tree *);
+/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_link(struct tree *);
+/* Instead of archive_entry_copy_stat for BY_HANDLE_FILE_INFORMATION */
+static void tree_archive_entry_copy_bhfi(struct archive_entry *,
+ struct tree *, const BY_HANDLE_FILE_INFORMATION *);
+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
+static int tree_current_is_dir(struct tree *);
+static int update_current_filesystem(struct archive_read_disk *a,
+ int64_t dev);
+static int setup_current_filesystem(struct archive_read_disk *);
+static int tree_target_is_same_as_parent(struct tree *,
+ const BY_HANDLE_FILE_INFORMATION *);
+
+static int _archive_read_disk_open_w(struct archive *, const wchar_t *);
+static int _archive_read_free(struct archive *);
+static int _archive_read_close(struct archive *);
+static int _archive_read_data_block(struct archive *,
+ const void **, size_t *, int64_t *);
+static int _archive_read_next_header(struct archive *,
+ struct archive_entry **);
+static int _archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+static const char *trivial_lookup_gname(void *, int64_t gid);
+static const char *trivial_lookup_uname(void *, int64_t uid);
+static int setup_sparse(struct archive_read_disk *, struct archive_entry *);
+static int close_and_restore_time(HANDLE, struct tree *,
+ struct restore_time *);
+static int setup_sparse_from_disk(struct archive_read_disk *,
+ struct archive_entry *, HANDLE);
+static int la_linkname_from_handle(HANDLE, wchar_t **, int *);
+static int la_linkname_from_pathw(const wchar_t *, wchar_t **, int *);
+static void entry_symlink_from_pathw(struct archive_entry *,
+ const wchar_t *path);
+
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ } DUMMYUNIONNAME;
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+/*
+ * Reads the target of a symbolic link
+ *
+ * Returns 0 on success and -1 on failure
+ * outbuf is allocated in the function
+ */
+static int
+la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype)
+{
+ DWORD inbytes;
+ REPARSE_DATA_BUFFER *buf;
+ BY_HANDLE_FILE_INFORMATION st;
+ size_t len;
+ BOOL ret;
+ BYTE *indata;
+ wchar_t *tbuf;
+
+ ret = GetFileInformationByHandle(h, &st);
+ if (ret == 0 ||
+ (st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
+ return (-1);
+ }
+
+ indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+ ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata,
+ 1024, &inbytes, NULL);
+ if (ret == 0) {
+ la_dosmaperr(GetLastError());
+ free(indata);
+ return (-1);
+ }
+
+ buf = (REPARSE_DATA_BUFFER *) indata;
+ if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+ free(indata);
+ /* File is not a symbolic link */
+ errno = EINVAL;
+ return (-1);
+ }
+
+ len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
+ if (len <= 0) {
+ free(indata);
+ return (-1);
+ }
+
+ tbuf = malloc(len + 1 * sizeof(wchar_t));
+ if (tbuf == NULL) {
+ free(indata);
+ return (-1);
+ }
+
+ memcpy(tbuf, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer)
+ [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len);
+ free(indata);
+
+ tbuf[len / sizeof(wchar_t)] = L'\0';
+
+ *linkname = tbuf;
+
+ /*
+ * Translate backslashes to slashes for libarchive internal use
+ */
+ while(*tbuf != L'\0') {
+ if (*tbuf == L'\\')
+ *tbuf = L'/';
+ tbuf++;
+ }
+
+ if ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ *linktype = AE_SYMLINK_TYPE_FILE;
+ else
+ *linktype = AE_SYMLINK_TYPE_DIRECTORY;
+
+ return (0);
+}
+
+/*
+ * Returns AE_SYMLINK_TYPE_FILE, AE_SYMLINK_TYPE_DIRECTORY or -1 on error
+ */
+static int
+la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype)
+{
+ HANDLE h;
+ const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OPEN_REPARSE_POINT;
+ int ret;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(path, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ ret = la_linkname_from_handle(h, outbuf, linktype);
+ CloseHandle(h);
+
+ return (ret);
+}
+
+static void
+entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path)
+{
+ wchar_t *linkname = NULL;
+ int ret, linktype;
+
+ ret = la_linkname_from_pathw(path, &linkname, &linktype);
+ if (ret != 0)
+ return;
+ if (linktype >= 0) {
+ archive_entry_copy_symlink_w(entry, linkname);
+ archive_entry_set_symlink_type(entry, linktype);
+ }
+ free(linkname);
+
+ return;
+}
+
+static const struct archive_vtable
+archive_read_disk_vtable = {
+ .archive_free = _archive_read_free,
+ .archive_close = _archive_read_close,
+ .archive_read_data_block = _archive_read_data_block,
+ .archive_read_next_header = _archive_read_next_header,
+ .archive_read_next_header2 = _archive_read_next_header2,
+};
+
+const char *
+archive_read_disk_gname(struct archive *_a, la_int64_t gid)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_gname"))
+ return (NULL);
+ if (a->lookup_gname == NULL)
+ return (NULL);
+ return ((*a->lookup_gname)(a->lookup_gname_data, gid));
+}
+
+const char *
+archive_read_disk_uname(struct archive *_a, la_int64_t uid)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_uname"))
+ return (NULL);
+ if (a->lookup_uname == NULL)
+ return (NULL);
+ return ((*a->lookup_uname)(a->lookup_uname_data, uid));
+}
+
+int
+archive_read_disk_set_gname_lookup(struct archive *_a,
+ void *private_data,
+ const char * (*lookup_gname)(void *private, la_int64_t gid),
+ void (*cleanup_gname)(void *private))
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");
+
+ if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+ (a->cleanup_gname)(a->lookup_gname_data);
+
+ a->lookup_gname = lookup_gname;
+ a->cleanup_gname = cleanup_gname;
+ a->lookup_gname_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_uname_lookup(struct archive *_a,
+ void *private_data,
+ const char * (*lookup_uname)(void *private, int64_t uid),
+ void (*cleanup_uname)(void *private))
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");
+
+ if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+ (a->cleanup_uname)(a->lookup_uname_data);
+
+ a->lookup_uname = lookup_uname;
+ a->cleanup_uname = cleanup_uname;
+ a->lookup_uname_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Create a new archive_read_disk object and initialize it with global state.
+ */
+struct archive *
+archive_read_disk_new(void)
+{
+ struct archive_read_disk *a;
+
+ a = (struct archive_read_disk *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->archive.vtable = &archive_read_disk_vtable;
+ a->entry = archive_entry_new2(&a->archive);
+ a->lookup_uname = trivial_lookup_uname;
+ a->lookup_gname = trivial_lookup_gname;
+ a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
+ return (&a->archive);
+}
+
+static int
+_archive_read_free(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ int r;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+
+ if (a->archive.state != ARCHIVE_STATE_CLOSED)
+ r = _archive_read_close(&a->archive);
+ else
+ r = ARCHIVE_OK;
+
+ tree_free(a->tree);
+ if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+ (a->cleanup_gname)(a->lookup_gname_data);
+ if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+ (a->cleanup_uname)(a->lookup_uname_data);
+ archive_string_free(&a->archive.error_string);
+ archive_entry_free(a->entry);
+ a->archive.magic = 0;
+ free(a);
+ return (r);
+}
+
+static int
+_archive_read_close(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+
+ if (a->archive.state != ARCHIVE_STATE_FATAL)
+ a->archive.state = ARCHIVE_STATE_CLOSED;
+
+ tree_close(a->tree);
+
+ return (ARCHIVE_OK);
+}
+
+static void
+setup_symlink_mode(struct archive_read_disk *a, char symlink_mode,
+ int follow_symlinks)
+{
+ a->symlink_mode = symlink_mode;
+ a->follow_symlinks = follow_symlinks;
+ if (a->tree != NULL) {
+ a->tree->initial_symlink_mode = a->symlink_mode;
+ a->tree->symlink_mode = a->symlink_mode;
+ }
+}
+
+int
+archive_read_disk_set_symlink_logical(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical");
+ setup_symlink_mode(a, 'L', 1);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_physical(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical");
+ setup_symlink_mode(a, 'P', 0);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_hybrid(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid");
+ setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_atime_restored(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");
+ a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;
+ if (a->tree != NULL)
+ a->tree->flags |= needsRestoreTimes;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_behavior(struct archive *_a, int flags)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ int r = ARCHIVE_OK;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+
+ a->flags = flags;
+
+ if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
+ r = archive_read_disk_set_atime_restored(_a);
+ else {
+ if (a->tree != NULL)
+ a->tree->flags &= ~needsRestoreTimes;
+ }
+ return (r);
+}
+
+/*
+ * Trivial implementations of gname/uname lookup functions.
+ * These are normally overridden by the client, but these stub
+ * versions ensure that we always have something that works.
+ */
+static const char *
+trivial_lookup_gname(void *private_data, int64_t gid)
+{
+ (void)private_data; /* UNUSED */
+ (void)gid; /* UNUSED */
+ return (NULL);
+}
+
+static const char *
+trivial_lookup_uname(void *private_data, int64_t uid)
+{
+ (void)private_data; /* UNUSED */
+ (void)uid; /* UNUSED */
+ return (NULL);
+}
+
+static int64_t
+align_num_per_sector(struct tree *t, int64_t size)
+{
+ int64_t surplus;
+
+ size += t->current_filesystem->bytesPerSector -1;
+ surplus = size % t->current_filesystem->bytesPerSector;
+ size -= surplus;
+ return (size);
+}
+
+static int
+start_next_async_read(struct archive_read_disk *a, struct tree *t)
+{
+ struct la_overlapped *olp;
+ DWORD buffbytes, rbytes;
+
+ if (t->ol_remaining_bytes == 0)
+ return (ARCHIVE_EOF);
+
+ olp = &(t->ol[t->ol_idx_doing]);
+ t->ol_idx_doing = (t->ol_idx_doing + 1) % MAX_OVERLAPPED;
+
+ /* Allocate read buffer. */
+ if (olp->buff == NULL) {
+ void *p;
+ size_t s = (size_t)align_num_per_sector(t, READ_BUFFER_SIZE);
+ p = VirtualAlloc(NULL, s, MEM_COMMIT, PAGE_READWRITE);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ olp->buff = p;
+ olp->buff_size = s;
+ olp->_a = &a->archive;
+ olp->ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+ if (olp->ol.hEvent == NULL) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "CreateEvent failed");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ } else
+ ResetEvent(olp->ol.hEvent);
+
+ buffbytes = (DWORD)olp->buff_size;
+ if (buffbytes > t->current_sparse->length)
+ buffbytes = (DWORD)t->current_sparse->length;
+
+ /* Skip hole. */
+ if (t->current_sparse->offset > t->ol_total) {
+ t->ol_remaining_bytes -=
+ t->current_sparse->offset - t->ol_total;
+ }
+
+ olp->offset = t->current_sparse->offset;
+ olp->ol.Offset = (DWORD)(olp->offset & 0xffffffff);
+ olp->ol.OffsetHigh = (DWORD)(olp->offset >> 32);
+
+ if (t->ol_remaining_bytes > buffbytes) {
+ olp->bytes_expected = buffbytes;
+ t->ol_remaining_bytes -= buffbytes;
+ } else {
+ olp->bytes_expected = (size_t)t->ol_remaining_bytes;
+ t->ol_remaining_bytes = 0;
+ }
+ olp->bytes_transferred = 0;
+ t->current_sparse->offset += buffbytes;
+ t->current_sparse->length -= buffbytes;
+ t->ol_total = t->current_sparse->offset;
+ if (t->current_sparse->length == 0 && t->ol_remaining_bytes > 0)
+ t->current_sparse++;
+
+ if (!ReadFile(t->entry_fh, olp->buff, buffbytes, &rbytes, &(olp->ol))) {
+ DWORD lasterr;
+
+ lasterr = GetLastError();
+ if (lasterr == ERROR_HANDLE_EOF) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Reading file truncated");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ } else if (lasterr != ERROR_IO_PENDING) {
+ if (lasterr == ERROR_NO_DATA)
+ errno = EAGAIN;
+ else if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ archive_set_error(&a->archive, errno, "Read error");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ } else
+ olp->bytes_transferred = rbytes;
+ t->ol_num_doing++;
+
+ return (t->ol_remaining_bytes == 0)? ARCHIVE_EOF: ARCHIVE_OK;
+}
+
+static void
+cancel_async(struct tree *t)
+{
+ if (t->ol_num_doing != t->ol_num_done) {
+ CancelIo(t->entry_fh);
+ t->ol_num_doing = t->ol_num_done = 0;
+ }
+}
+
+static int
+_archive_read_data_block(struct archive *_a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+ struct la_overlapped *olp;
+ DWORD bytes_transferred;
+ int r = ARCHIVE_FATAL;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_data_block");
+
+ if (t->entry_eof || t->entry_remaining_bytes <= 0) {
+ r = ARCHIVE_EOF;
+ goto abort_read_data;
+ }
+
+ /*
+ * Make a request to read the file in asynchronous.
+ */
+ if (t->ol_num_doing == 0) {
+ do {
+ r = start_next_async_read(a, t);
+ if (r == ARCHIVE_FATAL)
+ goto abort_read_data;
+ if (!t->async_io)
+ break;
+ } while (r == ARCHIVE_OK && t->ol_num_doing < MAX_OVERLAPPED);
+ } else {
+ if ((r = start_next_async_read(a, t)) == ARCHIVE_FATAL)
+ goto abort_read_data;
+ }
+
+ olp = &(t->ol[t->ol_idx_done]);
+ t->ol_idx_done = (t->ol_idx_done + 1) % MAX_OVERLAPPED;
+ if (olp->bytes_transferred)
+ bytes_transferred = (DWORD)olp->bytes_transferred;
+ else if (!GetOverlappedResult(t->entry_fh, &(olp->ol),
+ &bytes_transferred, TRUE)) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "GetOverlappedResult failed");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ r = ARCHIVE_FATAL;
+ goto abort_read_data;
+ }
+ t->ol_num_done++;
+
+ if (bytes_transferred == 0 ||
+ olp->bytes_expected != bytes_transferred) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Reading file truncated");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ r = ARCHIVE_FATAL;
+ goto abort_read_data;
+ }
+
+ *buff = olp->buff;
+ *size = bytes_transferred;
+ *offset = olp->offset;
+ if (olp->offset > t->entry_total)
+ t->entry_remaining_bytes -= olp->offset - t->entry_total;
+ t->entry_total = olp->offset + *size;
+ t->entry_remaining_bytes -= *size;
+ if (t->entry_remaining_bytes == 0) {
+ /* Close the current file descriptor */
+ close_and_restore_time(t->entry_fh, t, &t->restore_time);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ t->entry_eof = 1;
+ }
+ return (ARCHIVE_OK);
+
+abort_read_data:
+ *buff = NULL;
+ *size = 0;
+ *offset = t->entry_total;
+ if (t->entry_fh != INVALID_HANDLE_VALUE) {
+ cancel_async(t);
+ /* Close the current file descriptor */
+ close_and_restore_time(t->entry_fh, t, &t->restore_time);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ }
+ return (r);
+}
+
+static int
+next_entry(struct archive_read_disk *a, struct tree *t,
+ struct archive_entry *entry)
+{
+ const BY_HANDLE_FILE_INFORMATION *st;
+ const BY_HANDLE_FILE_INFORMATION *lst;
+ const char*name;
+ int descend, r;
+
+ st = NULL;
+ lst = NULL;
+ t->descend = 0;
+ do {
+ switch (tree_next(t)) {
+ case TREE_ERROR_FATAL:
+ archive_set_error(&a->archive, t->tree_errno,
+ "%ls: Unable to continue traversing directory tree",
+ tree_current_path(t));
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ case TREE_ERROR_DIR:
+ archive_set_error(&a->archive, t->tree_errno,
+ "%ls: Couldn't visit directory",
+ tree_current_path(t));
+ return (ARCHIVE_FAILED);
+ case 0:
+ return (ARCHIVE_EOF);
+ case TREE_POSTDESCENT:
+ case TREE_POSTASCENT:
+ break;
+ case TREE_REGULAR:
+ lst = tree_current_lstat(t);
+ if (lst == NULL) {
+ archive_set_error(&a->archive, t->tree_errno,
+ "%ls: Cannot stat",
+ tree_current_path(t));
+ return (ARCHIVE_FAILED);
+ }
+ break;
+ }
+ } while (lst == NULL);
+
+ archive_entry_copy_pathname_w(entry, tree_current_path(t));
+
+ /*
+ * Perform path matching.
+ */
+ if (a->matching) {
+ r = archive_match_path_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /*
+ * Distinguish 'L'/'P'/'H' symlink following.
+ */
+ switch(t->symlink_mode) {
+ case 'H':
+ /* 'H': After the first item, rest like 'P'. */
+ t->symlink_mode = 'P';
+ /* 'H': First item (from command line) like 'L'. */
+ /* FALLTHROUGH */
+ case 'L':
+ /* 'L': Do descend through a symlink to dir. */
+ descend = tree_current_is_dir(t);
+ /* 'L': Follow symlinks to files. */
+ a->symlink_mode = 'L';
+ a->follow_symlinks = 1;
+ /* 'L': Archive symlinks as targets, if we can. */
+ st = tree_current_stat(t);
+ if (st != NULL && !tree_target_is_same_as_parent(t, st))
+ break;
+ /* If stat fails, we have a broken symlink;
+ * in that case, don't follow the link. */
+ /* FALLTHROUGH */
+ default:
+ /* 'P': Don't descend through a symlink to dir. */
+ descend = tree_current_is_physical_dir(t);
+ /* 'P': Don't follow symlinks to files. */
+ a->symlink_mode = 'P';
+ a->follow_symlinks = 0;
+ /* 'P': Archive symlinks as symlinks. */
+ st = lst;
+ break;
+ }
+
+ if (update_current_filesystem(a, bhfi_dev(st)) != ARCHIVE_OK) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (t->initial_filesystem_id == -1)
+ t->initial_filesystem_id = t->current_filesystem_id;
+ if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {
+ if (t->initial_filesystem_id != t->current_filesystem_id)
+ return (ARCHIVE_RETRY);
+ }
+ t->descend = descend;
+
+ tree_archive_entry_copy_bhfi(entry, t, st);
+
+ /* Save the times to be restored. This must be in before
+ * calling archive_read_disk_descend() or any chance of it,
+ * especially, invoking a callback. */
+ t->restore_time.lastWriteTime = st->ftLastWriteTime;
+ t->restore_time.lastAccessTime = st->ftLastAccessTime;
+ t->restore_time.filetype = archive_entry_filetype(entry);
+
+ /*
+ * Perform time matching.
+ */
+ if (a->matching) {
+ r = archive_match_time_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /* Lookup uname/gname */
+ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));
+ if (name != NULL)
+ archive_entry_copy_uname(entry, name);
+ name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));
+ if (name != NULL)
+ archive_entry_copy_gname(entry, name);
+
+ /*
+ * Perform owner matching.
+ */
+ if (a->matching) {
+ r = archive_match_owner_excluded(a->matching, entry);
+ if (r < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Failed : %s", archive_error_string(a->matching));
+ return (r);
+ }
+ if (r) {
+ if (a->excluded_cb_func)
+ a->excluded_cb_func(&(a->archive),
+ a->excluded_cb_data, entry);
+ return (ARCHIVE_RETRY);
+ }
+ }
+
+ /*
+ * File attributes
+ */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+ const int supported_attrs =
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM;
+ DWORD file_attrs = st->dwFileAttributes & supported_attrs;
+ if (file_attrs != 0)
+ archive_entry_set_fflags(entry, file_attrs, 0);
+ }
+
+ /*
+ * Invoke a meta data filter callback.
+ */
+ if (a->metadata_filter_func) {
+ if (!a->metadata_filter_func(&(a->archive),
+ a->metadata_filter_data, entry))
+ return (ARCHIVE_RETRY);
+ }
+
+ archive_entry_copy_sourcepath_w(entry, tree_current_access_path(t));
+
+ r = ARCHIVE_OK;
+ if (archive_entry_filetype(entry) == AE_IFREG &&
+ archive_entry_size(entry) > 0) {
+ DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+ if (t->async_io)
+ flags |= FILE_FLAG_OVERLAPPED;
+ if (t->direct_io)
+ flags |= FILE_FLAG_NO_BUFFERING;
+ else
+ flags |= FILE_FLAG_SEQUENTIAL_SCAN;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flags;
+ t->entry_fh = CreateFile2(tree_current_access_path(t),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ t->entry_fh = CreateFileW(tree_current_access_path(t),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, flags, NULL);
+#endif
+ if (t->entry_fh == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Couldn't open %ls", tree_current_path(a->tree));
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Find sparse data from the disk. */
+ if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+ if (archive_entry_hardlink(entry) == NULL &&
+ (st->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0)
+ r = setup_sparse_from_disk(a, entry, t->entry_fh);
+ }
+ }
+ return (r);
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+ int ret;
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ *entryp = NULL;
+ ret = _archive_read_next_header2(_a, a->entry);
+ *entryp = a->entry;
+ return ret;
+}
+
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_next_header2");
+
+ t = a->tree;
+ if (t->entry_fh != INVALID_HANDLE_VALUE) {
+ cancel_async(t);
+ close_and_restore_time(t->entry_fh, t, &t->restore_time);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ }
+
+ archive_entry_clear(entry);
+
+ while ((r = next_entry(a, t, entry)) == ARCHIVE_RETRY)
+ archive_entry_clear(entry);
+
+ /*
+ * EOF and FATAL are persistent at this layer. By
+ * modifying the state, we guarantee that future calls to
+ * read a header or read data will fail.
+ */
+ switch (r) {
+ case ARCHIVE_EOF:
+ a->archive.state = ARCHIVE_STATE_EOF;
+ break;
+ case ARCHIVE_OK:
+ case ARCHIVE_WARN:
+ t->entry_total = 0;
+ if (archive_entry_filetype(entry) == AE_IFREG) {
+ t->entry_remaining_bytes = archive_entry_size(entry);
+ t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0;
+ if (!t->entry_eof &&
+ setup_sparse(a, entry) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ t->entry_remaining_bytes = 0;
+ t->entry_eof = 1;
+ }
+ t->ol_idx_doing = t->ol_idx_done = 0;
+ t->ol_num_doing = t->ol_num_done = 0;
+ t->ol_remaining_bytes = t->entry_remaining_bytes;
+ t->ol_total = 0;
+ a->archive.state = ARCHIVE_STATE_DATA;
+ break;
+ case ARCHIVE_RETRY:
+ break;
+ case ARCHIVE_FATAL:
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ break;
+ }
+
+ __archive_reset_read_data(&a->archive);
+ return (r);
+}
+
+static int
+setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)
+{
+ struct tree *t = a->tree;
+ int64_t aligned, length, offset;
+ int i;
+
+ t->sparse_count = archive_entry_sparse_reset(entry);
+ if (t->sparse_count+1 > t->sparse_list_size) {
+ free(t->sparse_list);
+ t->sparse_list_size = t->sparse_count + 1;
+ t->sparse_list = malloc(sizeof(t->sparse_list[0]) *
+ t->sparse_list_size);
+ if (t->sparse_list == NULL) {
+ t->sparse_list_size = 0;
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ }
+ /*
+ * Get sparse list and make sure those offsets and lengths are
+ * aligned by a sector size.
+ */
+ for (i = 0; i < t->sparse_count; i++) {
+ archive_entry_sparse_next(entry, &offset, &length);
+ aligned = align_num_per_sector(t, offset);
+ if (aligned != offset) {
+ aligned -= t->current_filesystem->bytesPerSector;
+ length += offset - aligned;
+ }
+ t->sparse_list[i].offset = aligned;
+ aligned = align_num_per_sector(t, length);
+ t->sparse_list[i].length = aligned;
+ }
+
+ aligned = align_num_per_sector(t, archive_entry_size(entry));
+ if (i == 0) {
+ t->sparse_list[i].offset = 0;
+ t->sparse_list[i].length = aligned;
+ } else {
+ int j, last = i;
+
+ t->sparse_list[i].offset = aligned;
+ t->sparse_list[i].length = 0;
+ for (i = 0; i < last; i++) {
+ if ((t->sparse_list[i].offset +
+ t->sparse_list[i].length) <=
+ t->sparse_list[i+1].offset)
+ continue;
+ /*
+ * Now sparse_list[i+1] is overlapped by sparse_list[i].
+ * Merge those two.
+ */
+ length = t->sparse_list[i+1].offset -
+ t->sparse_list[i].offset;
+ t->sparse_list[i+1].offset = t->sparse_list[i].offset;
+ t->sparse_list[i+1].length += length;
+ /* Remove sparse_list[i]. */
+ for (j = i; j < last; j++) {
+ t->sparse_list[j].offset =
+ t->sparse_list[j+1].offset;
+ t->sparse_list[j].length =
+ t->sparse_list[j+1].length;
+ }
+ last--;
+ }
+ }
+ t->current_sparse = t->sparse_list;
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,
+ void (*_excluded_func)(struct archive *, void *, struct archive_entry *),
+ void *_client_data)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");
+ a->matching = _ma;
+ a->excluded_cb_func = _excluded_func;
+ a->excluded_cb_data = _client_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_metadata_filter_callback(struct archive *_a,
+ int (*_metadata_filter_func)(struct archive *, void *,
+ struct archive_entry *), void *_client_data)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+ "archive_read_disk_set_metadata_filter_callback");
+
+ a->metadata_filter_func = _metadata_filter_func;
+ a->metadata_filter_data = _client_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_can_descend(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_disk_can_descend");
+
+ return (t->visit_type == TREE_REGULAR && t->descend);
+}
+
+/*
+ * Called by the client to mark the directory just returned from
+ * tree_next() as needing to be visited.
+ */
+int
+archive_read_disk_descend(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct tree *t = a->tree;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_read_disk_descend");
+
+ if (!archive_read_disk_can_descend(_a))
+ return (ARCHIVE_OK);
+
+ if (tree_current_is_physical_dir(t)) {
+ tree_push(t, t->basename, t->full_path.s,
+ t->current_filesystem_id,
+ bhfi_dev(&(t->lst)), bhfi_ino(&(t->lst)),
+ &t->restore_time);
+ t->stack->flags |= isDir;
+ } else if (tree_current_is_dir(t)) {
+ tree_push(t, t->basename, t->full_path.s,
+ t->current_filesystem_id,
+ bhfi_dev(&(t->st)), bhfi_ino(&(t->st)),
+ &t->restore_time);
+ t->stack->flags |= isDirLink;
+ }
+ t->descend = 0;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ struct archive_wstring wpath;
+ int ret;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+ "archive_read_disk_open");
+ archive_clear_error(&a->archive);
+
+ /* Make a wchar_t string from a char string. */
+ archive_string_init(&wpath);
+ if (archive_wstring_append_from_mbs(&wpath, pathname,
+ strlen(pathname)) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't convert a path to a wchar_t string");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ ret = ARCHIVE_FATAL;
+ } else
+ ret = _archive_read_disk_open_w(_a, wpath.s);
+
+ archive_wstring_free(&wpath);
+ return (ret);
+}
+
+int
+archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+ "archive_read_disk_open_w");
+ archive_clear_error(&a->archive);
+
+ return (_archive_read_disk_open_w(_a, pathname));
+}
+
+static int
+_archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ if (a->tree != NULL)
+ a->tree = tree_reopen(a->tree, pathname,
+ a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+ else
+ a->tree = tree_open(pathname, a->symlink_mode,
+ a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+ if (a->tree == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate directory traversal data");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ a->archive.state = ARCHIVE_STATE_HEADER;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return a current filesystem ID which is index of the filesystem entry
+ * you've visited through archive_read_disk.
+ */
+int
+archive_read_disk_current_filesystem(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem_id);
+}
+
+static int
+update_current_filesystem(struct archive_read_disk *a, int64_t dev)
+{
+ struct tree *t = a->tree;
+ int i, fid;
+
+ if (t->current_filesystem != NULL &&
+ t->current_filesystem->dev == dev)
+ return (ARCHIVE_OK);
+
+ for (i = 0; i < t->max_filesystem_id; i++) {
+ if (t->filesystem_table[i].dev == dev) {
+ /* There is the filesystem ID we've already generated. */
+ t->current_filesystem_id = i;
+ t->current_filesystem = &(t->filesystem_table[i]);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /*
+ * There is a new filesystem, we generate a new ID for.
+ */
+ fid = t->max_filesystem_id++;
+ if (t->max_filesystem_id > t->allocated_filesystem) {
+ size_t s;
+ void *p;
+
+ s = t->max_filesystem_id * 2;
+ p = realloc(t->filesystem_table,
+ s * sizeof(*t->filesystem_table));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate tar data");
+ return (ARCHIVE_FATAL);
+ }
+ t->filesystem_table = (struct filesystem *)p;
+ t->allocated_filesystem = (int)s;
+ }
+ t->current_filesystem_id = fid;
+ t->current_filesystem = &(t->filesystem_table[fid]);
+ t->current_filesystem->dev = dev;
+
+ return (setup_current_filesystem(a));
+}
+
+/*
+ * Returns 1 if current filesystem is generated filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_synthetic(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem->synthetic);
+}
+
+/*
+ * Returns 1 if current filesystem is remote filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_remote(struct archive *_a)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+ archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+ "archive_read_disk_current_filesystem");
+
+ return (a->tree->current_filesystem->remote);
+}
+
+/*
+ * If symlink is broken, statfs or statvfs will fail.
+ * Use its directory path instead.
+ */
+static wchar_t *
+safe_path_for_statfs(struct tree *t)
+{
+ const wchar_t *path;
+ wchar_t *cp, *p = NULL;
+
+ path = tree_current_access_path(t);
+ if (tree_current_stat(t) == NULL) {
+ p = _wcsdup(path);
+ cp = wcsrchr(p, '/');
+ if (cp != NULL && wcslen(cp) >= 2) {
+ cp[1] = '.';
+ cp[2] = '\0';
+ path = p;
+ }
+ } else
+ p = _wcsdup(path);
+ return (p);
+}
+
+/*
+ * Get conditions of synthetic and remote on Windows
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+ struct tree *t = a->tree;
+ wchar_t vol[256];
+ wchar_t *path;
+
+ t->current_filesystem->synthetic = -1;/* Not supported */
+ path = safe_path_for_statfs(t);
+ if (!GetVolumePathNameW(path, vol, sizeof(vol)/sizeof(vol[0]))) {
+ free(path);
+ t->current_filesystem->remote = -1;
+ t->current_filesystem->bytesPerSector = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "GetVolumePathName failed: %d", (int)GetLastError());
+ return (ARCHIVE_FAILED);
+ }
+ free(path);
+ switch (GetDriveTypeW(vol)) {
+ case DRIVE_UNKNOWN:
+ case DRIVE_NO_ROOT_DIR:
+ t->current_filesystem->remote = -1;
+ break;
+ case DRIVE_REMOTE:
+ t->current_filesystem->remote = 1;
+ break;
+ default:
+ t->current_filesystem->remote = 0;
+ break;
+ }
+
+ if (!GetDiskFreeSpaceW(vol, NULL,
+ &(t->current_filesystem->bytesPerSector), NULL, NULL)) {
+ t->current_filesystem->bytesPerSector = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "GetDiskFreeSpace failed: %d", (int)GetLastError());
+ return (ARCHIVE_FAILED);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt)
+{
+ HANDLE handle;
+ int r = 0;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype)
+ return (0);
+
+ /* Close a file descriptor.
+ * It will not be used for SetFileTime() because it has been opened
+ * by a read only mode.
+ */
+ if (h != INVALID_HANDLE_VALUE)
+ CloseHandle(h);
+ if ((t->flags & needsRestoreTimes) == 0)
+ return (r);
+
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ handle = CreateFile2(rt->full_path, FILE_WRITE_ATTRIBUTES,
+ 0, OPEN_EXISTING, &createExParams);
+#else
+ handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES,
+ 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+ if (handle == INVALID_HANDLE_VALUE) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (SetFileTime(handle, NULL, &rt->lastAccessTime,
+ &rt->lastWriteTime) == 0) {
+ errno = EINVAL;
+ r = -1;
+ } else
+ r = 0;
+ CloseHandle(handle);
+ return (r);
+}
+
+/*
+ * Add a directory path to the current stack.
+ */
+static void
+tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path,
+ int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt)
+{
+ struct tree_entry *te;
+
+ te = calloc(1, sizeof(*te));
+ te->next = t->stack;
+ te->parent = t->current;
+ if (te->parent)
+ te->depth = te->parent->depth + 1;
+ t->stack = te;
+ archive_string_init(&te->name);
+ archive_wstrcpy(&te->name, path);
+ archive_string_init(&te->full_path);
+ archive_wstrcpy(&te->full_path, full_path);
+ te->flags = needsDescent | needsOpen | needsAscent;
+ te->filesystem_id = filesystem_id;
+ te->dev = dev;
+ te->ino = ino;
+ te->dirname_length = t->dirname_length;
+ te->full_path_dir_length = t->full_path_dir_length;
+ te->restore_time.full_path = te->full_path.s;
+ if (rt != NULL) {
+ te->restore_time.lastWriteTime = rt->lastWriteTime;
+ te->restore_time.lastAccessTime = rt->lastAccessTime;
+ te->restore_time.filetype = rt->filetype;
+ }
+}
+
+/*
+ * Append a name to the current dir path.
+ */
+static void
+tree_append(struct tree *t, const wchar_t *name, size_t name_length)
+{
+ size_t size_needed;
+
+ t->path.s[t->dirname_length] = L'\0';
+ t->path.length = t->dirname_length;
+ /* Strip trailing '/' from name, unless entire name is "/". */
+ while (name_length > 1 && name[name_length - 1] == L'/')
+ name_length--;
+
+ /* Resize pathname buffer as needed. */
+ size_needed = name_length + t->dirname_length + 2;
+ archive_wstring_ensure(&t->path, size_needed);
+ /* Add a separating '/' if it's needed. */
+ if (t->dirname_length > 0 &&
+ t->path.s[archive_strlen(&t->path)-1] != L'/')
+ archive_wstrappend_wchar(&t->path, L'/');
+ t->basename = t->path.s + archive_strlen(&t->path);
+ archive_wstrncat(&t->path, name, name_length);
+ t->restore_time.full_path = t->basename;
+ if (t->full_path_dir_length > 0) {
+ t->full_path.s[t->full_path_dir_length] = L'\0';
+ t->full_path.length = t->full_path_dir_length;
+ size_needed = name_length + t->full_path_dir_length + 2;
+ archive_wstring_ensure(&t->full_path, size_needed);
+ /* Add a separating '\' if it's needed. */
+ if (t->full_path.s[archive_strlen(&t->full_path)-1] != L'\\')
+ archive_wstrappend_wchar(&t->full_path, L'\\');
+ archive_wstrncat(&t->full_path, name, name_length);
+ t->restore_time.full_path = t->full_path.s;
+ }
+}
+
+/*
+ * Open a directory tree for traversal.
+ */
+static struct tree *
+tree_open(const wchar_t *path, int symlink_mode, int restore_time)
+{
+ struct tree *t;
+
+ t = calloc(1, sizeof(*t));
+ archive_string_init(&(t->full_path));
+ archive_string_init(&t->path);
+ archive_wstring_ensure(&t->path, 15);
+ t->initial_symlink_mode = symlink_mode;
+ return (tree_reopen(t, path, restore_time));
+}
+
+static struct tree *
+tree_reopen(struct tree *t, const wchar_t *path, int restore_time)
+{
+ struct archive_wstring ws;
+ wchar_t *pathname, *p, *base;
+
+ t->flags = (restore_time != 0)?needsRestoreTimes:0;
+ t->visit_type = 0;
+ t->tree_errno = 0;
+ t->full_path_dir_length = 0;
+ t->dirname_length = 0;
+ t->depth = 0;
+ t->descend = 0;
+ t->current = NULL;
+ t->d = INVALID_HANDLE_VALUE;
+ t->symlink_mode = t->initial_symlink_mode;
+ archive_string_empty(&(t->full_path));
+ archive_string_empty(&t->path);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ t->entry_eof = 0;
+ t->entry_remaining_bytes = 0;
+ t->initial_filesystem_id = -1;
+
+ /* Get wchar_t strings from char strings. */
+ archive_string_init(&ws);
+ archive_wstrcpy(&ws, path);
+ pathname = ws.s;
+ /* Get a full-path-name. */
+ p = __la_win_permissive_name_w(pathname);
+ if (p == NULL)
+ goto failed;
+ archive_wstrcpy(&(t->full_path), p);
+ free(p);
+
+ /* Convert path separators from '\' to '/' */
+ for (p = pathname; *p != L'\0'; ++p) {
+ if (*p == L'\\')
+ *p = L'/';
+ }
+ base = pathname;
+
+ /* First item is set up a lot like a symlink traversal. */
+ /* printf("Looking for wildcard in %s\n", path); */
+ if ((base[0] == L'/' && base[1] == L'/' &&
+ base[2] == L'?' && base[3] == L'/' &&
+ (wcschr(base+4, L'*') || wcschr(base+4, L'?'))) ||
+ (!(base[0] == L'/' && base[1] == L'/' &&
+ base[2] == L'?' && base[3] == L'/') &&
+ (wcschr(base, L'*') || wcschr(base, L'?')))) {
+ // It has a wildcard in it...
+ // Separate the last element.
+ p = wcsrchr(base, L'/');
+ if (p != NULL) {
+ *p = L'\0';
+ tree_append(t, base, p - base);
+ t->dirname_length = archive_strlen(&t->path);
+ base = p + 1;
+ }
+ p = wcsrchr(t->full_path.s, L'\\');
+ if (p != NULL) {
+ *p = L'\0';
+ t->full_path.length = wcslen(t->full_path.s);
+ t->full_path_dir_length = archive_strlen(&t->full_path);
+ }
+ }
+ tree_push(t, base, t->full_path.s, 0, 0, 0, NULL);
+ archive_wstring_free(&ws);
+ t->stack->flags = needsFirstVisit;
+ /*
+ * Debug flag for Direct IO(No buffering) or Async IO.
+ * Those dependent on environment variable switches
+ * will be removed until next release.
+ */
+ {
+ const char *e;
+ if ((e = getenv("LIBARCHIVE_DIRECT_IO")) != NULL) {
+ if (e[0] == '0')
+ t->direct_io = 0;
+ else
+ t->direct_io = 1;
+ fprintf(stderr, "LIBARCHIVE_DIRECT_IO=%s\n",
+ (t->direct_io)?"Enabled":"Disabled");
+ } else
+ t->direct_io = DIRECT_IO;
+ if ((e = getenv("LIBARCHIVE_ASYNC_IO")) != NULL) {
+ if (e[0] == '0')
+ t->async_io = 0;
+ else
+ t->async_io = 1;
+ fprintf(stderr, "LIBARCHIVE_ASYNC_IO=%s\n",
+ (t->async_io)?"Enabled":"Disabled");
+ } else
+ t->async_io = ASYNC_IO;
+ }
+ return (t);
+failed:
+ archive_wstring_free(&ws);
+ tree_free(t);
+ return (NULL);
+}
+
+static int
+tree_descent(struct tree *t)
+{
+ t->dirname_length = archive_strlen(&t->path);
+ t->full_path_dir_length = archive_strlen(&t->full_path);
+ t->depth++;
+ return (0);
+}
+
+/*
+ * We've finished a directory; ascend back to the parent.
+ */
+static int
+tree_ascend(struct tree *t)
+{
+ struct tree_entry *te;
+
+ te = t->stack;
+ t->depth--;
+ close_and_restore_time(INVALID_HANDLE_VALUE, t, &te->restore_time);
+ return (0);
+}
+
+/*
+ * Pop the working stack.
+ */
+static void
+tree_pop(struct tree *t)
+{
+ struct tree_entry *te;
+
+ t->full_path.s[t->full_path_dir_length] = L'\0';
+ t->full_path.length = t->full_path_dir_length;
+ t->path.s[t->dirname_length] = L'\0';
+ t->path.length = t->dirname_length;
+ if (t->stack == t->current && t->current != NULL)
+ t->current = t->current->parent;
+ te = t->stack;
+ t->stack = te->next;
+ t->dirname_length = te->dirname_length;
+ t->basename = t->path.s + t->dirname_length;
+ t->full_path_dir_length = te->full_path_dir_length;
+ while (t->basename[0] == L'/')
+ t->basename++;
+ archive_wstring_free(&te->name);
+ archive_wstring_free(&te->full_path);
+ free(te);
+}
+
+/*
+ * Get the next item in the tree traversal.
+ */
+static int
+tree_next(struct tree *t)
+{
+ int r;
+
+ while (t->stack != NULL) {
+ /* If there's an open dir, get the next entry from there. */
+ if (t->d != INVALID_HANDLE_VALUE) {
+ r = tree_dir_next_windows(t, NULL);
+ if (r == 0)
+ continue;
+ return (r);
+ }
+
+ if (t->stack->flags & needsFirstVisit) {
+ wchar_t *d = t->stack->name.s;
+ t->stack->flags &= ~needsFirstVisit;
+ if (!(d[0] == L'/' && d[1] == L'/' &&
+ d[2] == L'?' && d[3] == L'/') &&
+ (wcschr(d, L'*') || wcschr(d, L'?'))) {
+ r = tree_dir_next_windows(t, d);
+ if (r == 0)
+ continue;
+ return (r);
+ } else {
+ HANDLE h = FindFirstFileW(t->stack->full_path.s, &t->_findData);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ t->tree_errno = errno;
+ t->visit_type = TREE_ERROR_DIR;
+ return (t->visit_type);
+ }
+ t->findData = &t->_findData;
+ FindClose(h);
+ }
+ /* Top stack item needs a regular visit. */
+ t->current = t->stack;
+ tree_append(t, t->stack->name.s,
+ archive_strlen(&(t->stack->name)));
+ //t->dirname_length = t->path_length;
+ //tree_pop(t);
+ t->stack->flags &= ~needsFirstVisit;
+ return (t->visit_type = TREE_REGULAR);
+ } else if (t->stack->flags & needsDescent) {
+ /* Top stack item is dir to descend into. */
+ t->current = t->stack;
+ tree_append(t, t->stack->name.s,
+ archive_strlen(&(t->stack->name)));
+ t->stack->flags &= ~needsDescent;
+ r = tree_descent(t);
+ if (r != 0) {
+ tree_pop(t);
+ t->visit_type = r;
+ } else
+ t->visit_type = TREE_POSTDESCENT;
+ return (t->visit_type);
+ } else if (t->stack->flags & needsOpen) {
+ t->stack->flags &= ~needsOpen;
+ r = tree_dir_next_windows(t, L"*");
+ if (r == 0)
+ continue;
+ return (r);
+ } else if (t->stack->flags & needsAscent) {
+ /* Top stack item is dir and we're done with it. */
+ r = tree_ascend(t);
+ tree_pop(t);
+ t->visit_type = r != 0 ? r : TREE_POSTASCENT;
+ return (t->visit_type);
+ } else {
+ /* Top item on stack is dead. */
+ tree_pop(t);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ }
+ }
+ return (t->visit_type = 0);
+}
+
+static int
+tree_dir_next_windows(struct tree *t, const wchar_t *pattern)
+{
+ const wchar_t *name;
+ size_t namelen;
+ int r;
+
+ for (;;) {
+ if (pattern != NULL) {
+ struct archive_wstring pt;
+
+ archive_string_init(&pt);
+ archive_wstring_ensure(&pt,
+ archive_strlen(&(t->full_path))
+ + 2 + wcslen(pattern));
+ archive_wstring_copy(&pt, &(t->full_path));
+ archive_wstrappend_wchar(&pt, L'\\');
+ archive_wstrcat(&pt, pattern);
+ t->d = FindFirstFileW(pt.s, &t->_findData);
+ archive_wstring_free(&pt);
+ if (t->d == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ t->tree_errno = errno;
+ r = tree_ascend(t); /* Undo "chdir" */
+ tree_pop(t);
+ t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
+ return (t->visit_type);
+ }
+ t->findData = &t->_findData;
+ pattern = NULL;
+ } else if (!FindNextFileW(t->d, &t->_findData)) {
+ FindClose(t->d);
+ t->d = INVALID_HANDLE_VALUE;
+ t->findData = NULL;
+ return (0);
+ }
+ name = t->findData->cFileName;
+ namelen = wcslen(name);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ if (name[0] == L'.' && name[1] == L'\0')
+ continue;
+ if (name[0] == L'.' && name[1] == L'.' && name[2] == L'\0')
+ continue;
+ tree_append(t, name, namelen);
+ return (t->visit_type = TREE_REGULAR);
+ }
+}
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+ ULARGE_INTEGER utc;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ /* milli seconds base */
+ *t = (time_t)(utc.QuadPart / 10000000);
+ /* nano seconds base */
+ *ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ *t = 0;
+ *ns = 0;
+ }
+}
+
+static void
+entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path,
+ const WIN32_FIND_DATAW *findData,
+ const BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+ time_t secs;
+ long nsecs;
+ mode_t mode;
+
+ fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);
+ archive_entry_set_atime(entry, secs, nsecs);
+ fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);
+ archive_entry_set_mtime(entry, secs, nsecs);
+ fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);
+ archive_entry_set_birthtime(entry, secs, nsecs);
+ archive_entry_set_ctime(entry, secs, nsecs);
+ archive_entry_set_dev(entry, bhfi_dev(bhfi));
+ archive_entry_set_ino64(entry, bhfi_ino(bhfi));
+ if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ archive_entry_set_nlink(entry, bhfi->nNumberOfLinks + 1);
+ else
+ archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);
+ archive_entry_set_size(entry,
+ (((int64_t)bhfi->nFileSizeHigh) << 32)
+ + bhfi->nFileSizeLow);
+ archive_entry_set_uid(entry, 0);
+ archive_entry_set_gid(entry, 0);
+ archive_entry_set_rdev(entry, 0);
+
+ mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+ mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ findData != NULL &&
+ findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
+ mode |= S_IFLNK;
+ entry_symlink_from_pathw(entry, path);
+ } else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else {
+ const wchar_t *p;
+
+ mode |= S_IFREG;
+ p = wcsrchr(path, L'.');
+ if (p != NULL && wcslen(p) == 4) {
+ switch (p[1]) {
+ case L'B': case L'b':
+ if ((p[2] == L'A' || p[2] == L'a' ) &&
+ (p[3] == L'T' || p[3] == L't' ))
+ mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'C': case L'c':
+ if (((p[2] == L'M' || p[2] == L'm' ) &&
+ (p[3] == L'D' || p[3] == L'd' )))
+ mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'E': case L'e':
+ if ((p[2] == L'X' || p[2] == L'x' ) &&
+ (p[3] == L'E' || p[3] == L'e' ))
+ mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ archive_entry_set_mode(entry, mode);
+}
+
+static void
+tree_archive_entry_copy_bhfi(struct archive_entry *entry, struct tree *t,
+ const BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+ entry_copy_bhfi(entry, tree_current_path(t), t->findData, bhfi);
+}
+
+static int
+tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st,
+ int sim_lstat)
+{
+ HANDLE h;
+ int r;
+ DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ if (sim_lstat && tree_current_is_physical_link(t))
+ flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(tree_current_access_path(t), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(tree_current_access_path(t), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ t->tree_errno = errno;
+ return (0);
+ }
+ r = GetFileInformationByHandle(h, st);
+ CloseHandle(h);
+ return (r);
+}
+
+/*
+ * Get the stat() data for the entry just returned from tree_next().
+ */
+static const BY_HANDLE_FILE_INFORMATION *
+tree_current_stat(struct tree *t)
+{
+ if (!(t->flags & hasStat)) {
+ if (!tree_current_file_information(t, &t->st, 0))
+ return NULL;
+ t->flags |= hasStat;
+ }
+ return (&t->st);
+}
+
+/*
+ * Get the lstat() data for the entry just returned from tree_next().
+ */
+static const BY_HANDLE_FILE_INFORMATION *
+tree_current_lstat(struct tree *t)
+{
+ if (!(t->flags & hasLstat)) {
+ if (!tree_current_file_information(t, &t->lst, 1))
+ return NULL;
+ t->flags |= hasLstat;
+ }
+ return (&t->lst);
+}
+
+/*
+ * Test whether current entry is a dir or link to a dir.
+ */
+static int
+tree_current_is_dir(struct tree *t)
+{
+ if (t->findData)
+ return (t->findData->dwFileAttributes
+ & FILE_ATTRIBUTE_DIRECTORY);
+ return (0);
+}
+
+/*
+ * Test whether current entry is a physical directory. Usually, we
+ * already have at least one of stat() or lstat() in memory, so we
+ * use tricks to try to avoid an extra trip to the disk.
+ */
+static int
+tree_current_is_physical_dir(struct tree *t)
+{
+ if (tree_current_is_physical_link(t))
+ return (0);
+ return (tree_current_is_dir(t));
+}
+
+/*
+ * Test whether current entry is a symbolic link.
+ */
+static int
+tree_current_is_physical_link(struct tree *t)
+{
+ if (t->findData)
+ return ((t->findData->dwFileAttributes
+ & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (t->findData->dwReserved0
+ == IO_REPARSE_TAG_SYMLINK));
+ return (0);
+}
+
+/*
+ * Test whether the same file has been in the tree as its parent.
+ */
+static int
+tree_target_is_same_as_parent(struct tree *t,
+ const BY_HANDLE_FILE_INFORMATION *st)
+{
+ struct tree_entry *te;
+ int64_t dev = bhfi_dev(st);
+ int64_t ino = bhfi_ino(st);
+
+ for (te = t->current->parent; te != NULL; te = te->parent) {
+ if (te->dev == dev && te->ino == ino)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Return the access path for the entry just returned from tree_next().
+ */
+static const wchar_t *
+tree_current_access_path(struct tree *t)
+{
+ return (t->full_path.s);
+}
+
+/*
+ * Return the full path for the entry just returned from tree_next().
+ */
+static const wchar_t *
+tree_current_path(struct tree *t)
+{
+ return (t->path.s);
+}
+
+/*
+ * Terminate the traversal.
+ */
+static void
+tree_close(struct tree *t)
+{
+
+ if (t == NULL)
+ return;
+ if (t->entry_fh != INVALID_HANDLE_VALUE) {
+ cancel_async(t);
+ close_and_restore_time(t->entry_fh, t, &t->restore_time);
+ t->entry_fh = INVALID_HANDLE_VALUE;
+ }
+ /* Close the handle of FindFirstFileW */
+ if (t->d != INVALID_HANDLE_VALUE) {
+ FindClose(t->d);
+ t->d = INVALID_HANDLE_VALUE;
+ t->findData = NULL;
+ }
+ /* Release anything remaining in the stack. */
+ while (t->stack != NULL)
+ tree_pop(t);
+}
+
+/*
+ * Release any resources.
+ */
+static void
+tree_free(struct tree *t)
+{
+ int i;
+
+ if (t == NULL)
+ return;
+ archive_wstring_free(&t->path);
+ archive_wstring_free(&t->full_path);
+ free(t->sparse_list);
+ free(t->filesystem_table);
+ for (i = 0; i < MAX_OVERLAPPED; i++) {
+ if (t->ol[i].buff)
+ VirtualFree(t->ol[i].buff, 0, MEM_RELEASE);
+ CloseHandle(t->ol[i].ol.hEvent);
+ }
+ free(t);
+}
+
+
+/*
+ * Populate the archive_entry with metadata from the disk.
+ */
+int
+archive_read_disk_entry_from_file(struct archive *_a,
+ struct archive_entry *entry, int fd, const struct stat *st)
+{
+ struct archive_read_disk *a = (struct archive_read_disk *)_a;
+ const wchar_t *path;
+ const wchar_t *wname;
+ const char *name;
+ HANDLE h;
+ BY_HANDLE_FILE_INFORMATION bhfi;
+ DWORD fileAttributes = 0;
+ int r;
+
+ archive_clear_error(_a);
+ wname = archive_entry_sourcepath_w(entry);
+ if (wname == NULL)
+ wname = archive_entry_pathname_w(entry);
+ if (wname == NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "Can't get a wide character version of the path");
+ return (ARCHIVE_FAILED);
+ }
+ path = __la_win_permissive_name_w(wname);
+
+ if (st == NULL) {
+ /*
+ * Get metadata through GetFileInformationByHandle().
+ */
+ if (fd >= 0) {
+ h = (HANDLE)_get_osfhandle(fd);
+ r = GetFileInformationByHandle(h, &bhfi);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't GetFileInformationByHandle");
+ return (ARCHIVE_FAILED);
+ }
+ entry_copy_bhfi(entry, path, NULL, &bhfi);
+ } else {
+ WIN32_FIND_DATAW findData;
+ DWORD flag, desiredAccess;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ h = FindFirstFileW(path, &findData);
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't FindFirstFileW");
+ return (ARCHIVE_FAILED);
+ }
+ FindClose(h);
+
+ flag = FILE_FLAG_BACKUP_SEMANTICS;
+ if (!a->follow_symlinks &&
+ (findData.dwFileAttributes
+ & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
+ flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+ desiredAccess = 0;
+ } else if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ desiredAccess = 0;
+ } else
+ desiredAccess = GENERIC_READ;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(path, desiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, desiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't CreateFileW");
+ return (ARCHIVE_FAILED);
+ }
+ r = GetFileInformationByHandle(h, &bhfi);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't GetFileInformationByHandle");
+ CloseHandle(h);
+ return (ARCHIVE_FAILED);
+ }
+ entry_copy_bhfi(entry, path, &findData, &bhfi);
+ }
+ fileAttributes = bhfi.dwFileAttributes;
+ } else {
+ archive_entry_copy_stat(entry, st);
+ if (st->st_mode & S_IFLNK)
+ entry_symlink_from_pathw(entry, path);
+ h = INVALID_HANDLE_VALUE;
+ }
+
+ /* Lookup uname/gname */
+ name = archive_read_disk_uname(_a, archive_entry_uid(entry));
+ if (name != NULL)
+ archive_entry_copy_uname(entry, name);
+ name = archive_read_disk_gname(_a, archive_entry_gid(entry));
+ if (name != NULL)
+ archive_entry_copy_gname(entry, name);
+
+ /*
+ * File attributes
+ */
+ if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+ const int supported_attrs =
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_SYSTEM;
+ DWORD file_attrs = fileAttributes & supported_attrs;
+ if (file_attrs != 0)
+ archive_entry_set_fflags(entry, file_attrs, 0);
+ }
+
+ /*
+ * Can this file be sparse file ?
+ */
+ if (archive_entry_filetype(entry) != AE_IFREG
+ || archive_entry_size(entry) <= 0
+ || archive_entry_hardlink(entry) != NULL) {
+ if (h != INVALID_HANDLE_VALUE && fd < 0)
+ CloseHandle(h);
+ return (ARCHIVE_OK);
+ }
+
+ if (h == INVALID_HANDLE_VALUE) {
+ if (fd >= 0) {
+ h = (HANDLE)_get_osfhandle(fd);
+ } else {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ h = CreateFile2(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't CreateFileW");
+ return (ARCHIVE_FAILED);
+ }
+ }
+ r = GetFileInformationByHandle(h, &bhfi);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't GetFileInformationByHandle");
+ if (h != INVALID_HANDLE_VALUE && fd < 0)
+ CloseHandle(h);
+ return (ARCHIVE_FAILED);
+ }
+ fileAttributes = bhfi.dwFileAttributes;
+ }
+
+ /* Sparse file must be set a mark, FILE_ATTRIBUTE_SPARSE_FILE */
+ if ((fileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
+ if (fd < 0)
+ CloseHandle(h);
+ return (ARCHIVE_OK);
+ }
+
+ if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+ r = setup_sparse_from_disk(a, entry, h);
+ if (fd < 0)
+ CloseHandle(h);
+ }
+
+ return (r);
+}
+
+/*
+ * Windows sparse interface.
+ */
+#if defined(__MINGW32__) && !defined(FSCTL_QUERY_ALLOCATED_RANGES)
+#define FSCTL_QUERY_ALLOCATED_RANGES 0x940CF
+typedef struct {
+ LARGE_INTEGER FileOffset;
+ LARGE_INTEGER Length;
+} FILE_ALLOCATED_RANGE_BUFFER;
+#endif
+
+static int
+setup_sparse_from_disk(struct archive_read_disk *a,
+ struct archive_entry *entry, HANDLE handle)
+{
+ FILE_ALLOCATED_RANGE_BUFFER range, *outranges = NULL;
+ size_t outranges_size;
+ int64_t entry_size = archive_entry_size(entry);
+ int exit_sts = ARCHIVE_OK;
+
+ range.FileOffset.QuadPart = 0;
+ range.Length.QuadPart = entry_size;
+ outranges_size = 2048;
+ outranges = (FILE_ALLOCATED_RANGE_BUFFER *)malloc(outranges_size);
+ if (outranges == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ exit_sts = ARCHIVE_FATAL;
+ goto exit_setup_sparse;
+ }
+
+ for (;;) {
+ DWORD retbytes;
+ BOOL ret;
+
+ for (;;) {
+ ret = DeviceIoControl(handle,
+ FSCTL_QUERY_ALLOCATED_RANGES,
+ &range, sizeof(range), outranges,
+ (DWORD)outranges_size, &retbytes, NULL);
+ if (ret == 0 && GetLastError() == ERROR_MORE_DATA) {
+ free(outranges);
+ outranges_size *= 2;
+ outranges = (FILE_ALLOCATED_RANGE_BUFFER *)
+ malloc(outranges_size);
+ if (outranges == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory");
+ exit_sts = ARCHIVE_FATAL;
+ goto exit_setup_sparse;
+ }
+ continue;
+ } else
+ break;
+ }
+ if (ret != 0) {
+ if (retbytes > 0) {
+ DWORD i, n;
+
+ n = retbytes / sizeof(outranges[0]);
+ if (n == 1 &&
+ outranges[0].FileOffset.QuadPart == 0 &&
+ outranges[0].Length.QuadPart == entry_size)
+ break;/* This is not sparse. */
+ for (i = 0; i < n; i++)
+ archive_entry_sparse_add_entry(entry,
+ outranges[i].FileOffset.QuadPart,
+ outranges[i].Length.QuadPart);
+ range.FileOffset.QuadPart =
+ outranges[n-1].FileOffset.QuadPart
+ + outranges[n-1].Length.QuadPart;
+ range.Length.QuadPart =
+ entry_size - range.FileOffset.QuadPart;
+ if (range.Length.QuadPart > 0)
+ continue;
+ } else {
+ /* The entire file is a hole. Add one data block of size 0 at the end. */
+ archive_entry_sparse_add_entry(entry,
+ entry_size,
+ 0);
+ }
+ break;
+ } else {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "DeviceIoControl Failed: %lu", GetLastError());
+ exit_sts = ARCHIVE_FAILED;
+ goto exit_setup_sparse;
+ }
+ }
+exit_setup_sparse:
+ free(outranges);
+
+ return (exit_sts);
+}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_extract.c b/contrib/libs/libarchive/libarchive/archive_read_extract.c
new file mode 100644
index 0000000000..b7973fa8e0
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_extract.c
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
+{
+ struct archive_read_extract *extract;
+ struct archive_read * a = (struct archive_read *)_a;
+
+ extract = __archive_read_get_extract(a);
+ if (extract == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* If we haven't initialized the archive_write_disk object, do it now. */
+ if (extract->ad == NULL) {
+ extract->ad = archive_write_disk_new();
+ if (extract->ad == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't extract");
+ return (ARCHIVE_FATAL);
+ }
+ archive_write_disk_set_standard_lookup(extract->ad);
+ }
+
+ archive_write_disk_set_options(extract->ad, flags);
+ return (archive_read_extract2(&a->archive, entry, extract->ad));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_extract2.c b/contrib/libs/libarchive/libarchive/archive_read_extract2.c
new file mode 100644
index 0000000000..4febd8ce05
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_extract2.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.61 2008/05/26 17:00:22 kientzle Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static int copy_data(struct archive *ar, struct archive *aw);
+static int archive_read_extract_cleanup(struct archive_read *);
+
+
+/* Retrieve an extract object without initialising the associated
+ * archive_write_disk object.
+ */
+struct archive_read_extract *
+__archive_read_get_extract(struct archive_read *a)
+{
+ if (a->extract == NULL) {
+ a->extract = (struct archive_read_extract *)calloc(1, sizeof(*a->extract));
+ if (a->extract == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't extract");
+ return (NULL);
+ }
+ a->cleanup_archive_extract = archive_read_extract_cleanup;
+ }
+ return (a->extract);
+}
+
+/*
+ * Cleanup function for archive_extract.
+ */
+static int
+archive_read_extract_cleanup(struct archive_read *a)
+{
+ int ret = ARCHIVE_OK;
+
+ if (a->extract->ad != NULL) {
+ ret = archive_write_free(a->extract->ad);
+ }
+ free(a->extract);
+ a->extract = NULL;
+ return (ret);
+}
+
+int
+archive_read_extract2(struct archive *_a, struct archive_entry *entry,
+ struct archive *ad)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r, r2;
+
+ /* Set up for this particular entry. */
+ if (a->skip_file_set)
+ archive_write_disk_set_skip_file(ad,
+ a->skip_file_dev, a->skip_file_ino);
+ r = archive_write_header(ad, entry);
+ if (r < ARCHIVE_WARN)
+ r = ARCHIVE_WARN;
+ if (r != ARCHIVE_OK)
+ /* If _write_header failed, copy the error. */
+ archive_copy_error(&a->archive, ad);
+ else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0)
+ /* Otherwise, pour data into the entry. */
+ r = copy_data(_a, ad);
+ r2 = archive_write_finish_entry(ad);
+ if (r2 < ARCHIVE_WARN)
+ r2 = ARCHIVE_WARN;
+ /* Use the first message. */
+ if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
+ archive_copy_error(&a->archive, ad);
+ /* Use the worst error return. */
+ if (r2 < r)
+ r = r2;
+ return (r);
+}
+
+void
+archive_read_extract_set_progress_callback(struct archive *_a,
+ void (*progress_func)(void *), void *user_data)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct archive_read_extract *extract = __archive_read_get_extract(a);
+ if (extract != NULL) {
+ extract->extract_progress = progress_func;
+ extract->extract_progress_user_data = user_data;
+ }
+}
+
+static int
+copy_data(struct archive *ar, struct archive *aw)
+{
+ int64_t offset;
+ const void *buff;
+ struct archive_read_extract *extract;
+ size_t size;
+ int r;
+
+ extract = __archive_read_get_extract((struct archive_read *)ar);
+ if (extract == NULL)
+ return (ARCHIVE_FATAL);
+ for (;;) {
+ r = archive_read_data_block(ar, &buff, &size, &offset);
+ if (r == ARCHIVE_EOF)
+ return (ARCHIVE_OK);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = (int)archive_write_data_block(aw, buff, size, offset);
+ if (r < ARCHIVE_WARN)
+ r = ARCHIVE_WARN;
+ if (r < ARCHIVE_OK) {
+ archive_set_error(ar, archive_errno(aw),
+ "%s", archive_error_string(aw));
+ return (r);
+ }
+ if (extract->extract_progress)
+ (extract->extract_progress)
+ (extract->extract_progress_user_data);
+ }
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_open_fd.c b/contrib/libs/libarchive/libarchive/archive_read_open_fd.c
new file mode 100644
index 0000000000..f59cd07fe6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_open_fd.c
@@ -0,0 +1,211 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_fd.c 201103 2009-12-28 03:13:49Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct read_fd_data {
+ int fd;
+ size_t block_size;
+ char use_lseek;
+ void *buffer;
+};
+
+static int file_close(struct archive *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+static int64_t file_seek(struct archive *, void *, int64_t request, int);
+static int64_t file_skip(struct archive *, void *, int64_t request);
+
+int
+archive_read_open_fd(struct archive *a, int fd, size_t block_size)
+{
+ struct stat st;
+ struct read_fd_data *mine;
+ void *b;
+
+ archive_clear_error(a);
+ if (fstat(fd, &st) != 0) {
+ archive_set_error(a, errno, "Can't stat fd %d", fd);
+ return (ARCHIVE_FATAL);
+ }
+
+ mine = (struct read_fd_data *)calloc(1, sizeof(*mine));
+ b = malloc(block_size);
+ if (mine == NULL || b == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ free(mine);
+ free(b);
+ return (ARCHIVE_FATAL);
+ }
+ mine->block_size = block_size;
+ mine->buffer = b;
+ mine->fd = fd;
+ /*
+ * Skip support is a performance optimization for anything
+ * that supports lseek(). On FreeBSD, only regular files and
+ * raw disk devices support lseek() and there's no portable
+ * way to determine if a device is a raw disk device, so we
+ * only enable this optimization for regular files.
+ */
+ if (S_ISREG(st.st_mode)) {
+ archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+ mine->use_lseek = 1;
+ }
+#if defined(__CYGWIN__) || defined(_WIN32)
+ setmode(mine->fd, O_BINARY);
+#endif
+
+ archive_read_set_read_callback(a, file_read);
+ archive_read_set_skip_callback(a, file_skip);
+ archive_read_set_seek_callback(a, file_seek);
+ archive_read_set_close_callback(a, file_close);
+ archive_read_set_callback_data(a, mine);
+ return (archive_read_open1(a));
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_fd_data *mine = (struct read_fd_data *)client_data;
+ ssize_t bytes_read;
+
+ *buff = mine->buffer;
+ for (;;) {
+ bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+ if (bytes_read < 0) {
+ if (errno == EINTR)
+ continue;
+ archive_set_error(a, errno, "Error reading fd %d",
+ mine->fd);
+ }
+ return (bytes_read);
+ }
+}
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+ struct read_fd_data *mine = (struct read_fd_data *)client_data;
+ int64_t skip = request;
+ int64_t old_offset, new_offset;
+ int skip_bits = sizeof(skip) * 8 - 1; /* off_t is a signed type. */
+
+ if (!mine->use_lseek)
+ return (0);
+
+ /* Reduce a request that would overflow the 'skip' variable. */
+ if (sizeof(request) > sizeof(skip)) {
+ int64_t max_skip =
+ (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+ if (request > max_skip)
+ skip = max_skip;
+ }
+
+ /* Reduce request to the next smallest multiple of block_size */
+ request = (request / mine->block_size) * mine->block_size;
+ if (request == 0)
+ return (0);
+
+ if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) &&
+ ((new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0))
+ return (new_offset - old_offset);
+
+ /* If seek failed once, it will probably fail again. */
+ mine->use_lseek = 0;
+
+ /* Let libarchive recover with read+discard. */
+ if (errno == ESPIPE)
+ return (0);
+
+ /*
+ * There's been an error other than ESPIPE. This is most
+ * likely caused by a programmer error (too large request)
+ * or a corrupted archive file.
+ */
+ archive_set_error(a, errno, "Error seeking");
+ return (-1);
+}
+
+/*
+ * TODO: Store the offset and use it in the read callback.
+ */
+static int64_t
+file_seek(struct archive *a, void *client_data, int64_t request, int whence)
+{
+ struct read_fd_data *mine = (struct read_fd_data *)client_data;
+ int64_t r;
+
+ /* We use off_t here because lseek() is declared that way. */
+ /* See above for notes about when off_t is less than 64 bits. */
+ r = lseek(mine->fd, request, whence);
+ if (r >= 0)
+ return r;
+
+ if (errno == ESPIPE) {
+ archive_set_error(a, errno,
+ "A file descriptor(%d) is not seekable(PIPE)", mine->fd);
+ return (ARCHIVE_FAILED);
+ } else {
+ /* If the input is corrupted or truncated, fail. */
+ archive_set_error(a, errno,
+ "Error seeking in a file descriptor(%d)", mine->fd);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+ struct read_fd_data *mine = (struct read_fd_data *)client_data;
+
+ (void)a; /* UNUSED */
+ free(mine->buffer);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_open_file.c b/contrib/libs/libarchive/libarchive/archive_read_open_file.c
new file mode 100644
index 0000000000..03719e8bff
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_open_file.c
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_file.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct read_FILE_data {
+ FILE *f;
+ size_t block_size;
+ void *buffer;
+ char can_skip;
+};
+
+static int file_close(struct archive *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+static int64_t file_skip(struct archive *, void *, int64_t request);
+
+int
+archive_read_open_FILE(struct archive *a, FILE *f)
+{
+ struct stat st;
+ struct read_FILE_data *mine;
+ size_t block_size = 128 * 1024;
+ void *b;
+
+ archive_clear_error(a);
+ mine = (struct read_FILE_data *)malloc(sizeof(*mine));
+ b = malloc(block_size);
+ if (mine == NULL || b == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ free(mine);
+ free(b);
+ return (ARCHIVE_FATAL);
+ }
+ mine->block_size = block_size;
+ mine->buffer = b;
+ mine->f = f;
+ /*
+ * If we can't fstat() the file, it may just be that it's not
+ * a file. (On some platforms, FILE * objects can wrap I/O
+ * streams that don't support fileno()). As a result, fileno()
+ * should be used cautiously.)
+ */
+ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) {
+ archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+ /* Enable the seek optimization only for regular files. */
+ mine->can_skip = 1;
+ } else
+ mine->can_skip = 0;
+
+#if defined(__CYGWIN__) || defined(_WIN32)
+ setmode(fileno(mine->f), O_BINARY);
+#endif
+
+ archive_read_set_read_callback(a, file_read);
+ archive_read_set_skip_callback(a, file_skip);
+ archive_read_set_close_callback(a, file_close);
+ archive_read_set_callback_data(a, mine);
+ return (archive_read_open1(a));
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+ size_t bytes_read;
+
+ *buff = mine->buffer;
+ bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f);
+ if (bytes_read < mine->block_size && ferror(mine->f)) {
+ archive_set_error(a, errno, "Error reading file");
+ }
+ return (bytes_read);
+}
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+#if HAVE_FSEEKO
+ off_t skip = (off_t)request;
+#elif HAVE__FSEEKI64
+ int64_t skip = request;
+#else
+ long skip = (long)request;
+#endif
+ int skip_bits = sizeof(skip) * 8 - 1;
+
+ (void)a; /* UNUSED */
+
+ /*
+ * If we can't skip, return 0 as the amount we did step and
+ * the caller will work around by reading and discarding.
+ */
+ if (!mine->can_skip)
+ return (0);
+ if (request == 0)
+ return (0);
+
+ /* If request is too big for a long or an off_t, reduce it. */
+ if (sizeof(request) > sizeof(skip)) {
+ int64_t max_skip =
+ (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+ if (request > max_skip)
+ skip = max_skip;
+ }
+
+#ifdef __ANDROID__
+ /* fileno() isn't safe on all platforms ... see above. */
+ if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0)
+#elif HAVE__FSEEKI64
+ if (_fseeki64(mine->f, skip, SEEK_CUR) != 0)
+#elif HAVE_FSEEKO
+ if (fseeko(mine->f, skip, SEEK_CUR) != 0)
+#else
+ if (fseek(mine->f, skip, SEEK_CUR) != 0)
+#endif
+ {
+ mine->can_skip = 0;
+ return (0);
+ }
+ return (request);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+ struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+
+ (void)a; /* UNUSED */
+ free(mine->buffer);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_open_filename.c b/contrib/libs/libarchive/libarchive/archive_read_open_filename.c
new file mode 100644
index 0000000000..f3b8d09f70
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_open_filename.c
@@ -0,0 +1,586 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_filename.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/disk.h>
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/disklabel.h>
+#error #include <sys/dkio.h>
+#elif defined(__DragonFly__)
+#error #include <sys/diskslice.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+struct read_file_data {
+ int fd;
+ size_t block_size;
+ void *buffer;
+ mode_t st_mode; /* Mode bits for opened file. */
+ char use_lseek;
+ enum fnt_e { FNT_STDIN, FNT_MBS, FNT_WCS } filename_type;
+ union {
+ char m[1];/* MBS filename. */
+ wchar_t w[1];/* WCS filename. */
+ } filename; /* Must be last! */
+};
+
+static int file_open(struct archive *, void *);
+static int file_close(struct archive *, void *);
+static int file_close2(struct archive *, void *);
+static int file_switch(struct archive *, void *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+static int64_t file_seek(struct archive *, void *, int64_t request, int);
+static int64_t file_skip(struct archive *, void *, int64_t request);
+static int64_t file_skip_lseek(struct archive *, void *, int64_t request);
+
+int
+archive_read_open_file(struct archive *a, const char *filename,
+ size_t block_size)
+{
+ return (archive_read_open_filename(a, filename, block_size));
+}
+
+int
+archive_read_open_filename(struct archive *a, const char *filename,
+ size_t block_size)
+{
+ const char *filenames[2];
+ filenames[0] = filename;
+ filenames[1] = NULL;
+ return archive_read_open_filenames(a, filenames, block_size);
+}
+
+int
+archive_read_open_filenames(struct archive *a, const char **filenames,
+ size_t block_size)
+{
+ struct read_file_data *mine;
+ const char *filename = NULL;
+ if (filenames)
+ filename = *(filenames++);
+
+ archive_clear_error(a);
+ do
+ {
+ if (filename == NULL)
+ filename = "";
+ mine = (struct read_file_data *)calloc(1,
+ sizeof(*mine) + strlen(filename));
+ if (mine == NULL)
+ goto no_memory;
+ strcpy(mine->filename.m, filename);
+ mine->block_size = block_size;
+ mine->fd = -1;
+ mine->buffer = NULL;
+ mine->st_mode = mine->use_lseek = 0;
+ if (filename == NULL || filename[0] == '\0') {
+ mine->filename_type = FNT_STDIN;
+ } else
+ mine->filename_type = FNT_MBS;
+ if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK))
+ return (ARCHIVE_FATAL);
+ if (filenames == NULL)
+ break;
+ filename = *(filenames++);
+ } while (filename != NULL && filename[0] != '\0');
+ archive_read_set_open_callback(a, file_open);
+ archive_read_set_read_callback(a, file_read);
+ archive_read_set_skip_callback(a, file_skip);
+ archive_read_set_close_callback(a, file_close);
+ archive_read_set_switch_callback(a, file_switch);
+ archive_read_set_seek_callback(a, file_seek);
+
+ return (archive_read_open1(a));
+no_memory:
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+}
+
+int
+archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename,
+ size_t block_size)
+{
+ struct read_file_data *mine = (struct read_file_data *)calloc(1,
+ sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t));
+ if (!mine)
+ {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->fd = -1;
+ mine->block_size = block_size;
+
+ if (wfilename == NULL || wfilename[0] == L'\0') {
+ mine->filename_type = FNT_STDIN;
+ } else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ mine->filename_type = FNT_WCS;
+ wcscpy(mine->filename.w, wfilename);
+#else
+ /*
+ * POSIX system does not support a wchar_t interface for
+ * open() system call, so we have to translate a wchar_t
+ * filename to multi-byte one and use it.
+ */
+ struct archive_string fn;
+
+ archive_string_init(&fn);
+ if (archive_string_append_from_wcs(&fn, wfilename,
+ wcslen(wfilename)) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(a, errno,
+ "Can't allocate memory");
+ else
+ archive_set_error(a, EINVAL,
+ "Failed to convert a wide-character"
+ " filename to a multi-byte filename");
+ archive_string_free(&fn);
+ free(mine);
+ return (ARCHIVE_FATAL);
+ }
+ mine->filename_type = FNT_MBS;
+ strcpy(mine->filename.m, fn.s);
+ archive_string_free(&fn);
+#endif
+ }
+ if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK))
+ return (ARCHIVE_FATAL);
+ archive_read_set_open_callback(a, file_open);
+ archive_read_set_read_callback(a, file_read);
+ archive_read_set_skip_callback(a, file_skip);
+ archive_read_set_close_callback(a, file_close);
+ archive_read_set_switch_callback(a, file_switch);
+ archive_read_set_seek_callback(a, file_seek);
+
+ return (archive_read_open1(a));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+ struct stat st;
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+ void *buffer;
+ const char *filename = NULL;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wfilename = NULL;
+#endif
+ int fd = -1;
+ int is_disk_like = 0;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ struct disklabel dl;
+#elif defined(__DragonFly__)
+ struct partinfo pi;
+#endif
+
+ archive_clear_error(a);
+ if (mine->filename_type == FNT_STDIN) {
+ /* We used to delegate stdin support by
+ * directly calling archive_read_open_fd(a,0,block_size)
+ * here, but that doesn't (and shouldn't) handle the
+ * end-of-file flush when reading stdout from a pipe.
+ * Basically, read_open_fd() is intended for folks who
+ * are willing to handle such details themselves. This
+ * API is intended to be a little smarter for folks who
+ * want easy handling of the common case.
+ */
+ fd = 0;
+#if defined(__CYGWIN__) || defined(_WIN32)
+ setmode(0, O_BINARY);
+#endif
+ filename = "";
+ } else if (mine->filename_type == FNT_MBS) {
+ filename = mine->filename.m;
+ fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd < 0) {
+ archive_set_error(a, errno,
+ "Failed to open '%s'", filename);
+ return (ARCHIVE_FATAL);
+ }
+ } else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ wfilename = mine->filename.w;
+ fd = _wopen(wfilename, O_RDONLY | O_BINARY);
+ if (fd < 0 && errno == ENOENT) {
+ wchar_t *fullpath;
+ fullpath = __la_win_permissive_name_w(wfilename);
+ if (fullpath != NULL) {
+ fd = _wopen(fullpath, O_RDONLY | O_BINARY);
+ free(fullpath);
+ }
+ }
+ if (fd < 0) {
+ archive_set_error(a, errno,
+ "Failed to open '%S'", wfilename);
+ return (ARCHIVE_FATAL);
+ }
+#else
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unexpedted operation in archive_read_open_filename");
+ goto fail;
+#endif
+ }
+ if (fstat(fd, &st) != 0) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (mine->filename_type == FNT_WCS)
+ archive_set_error(a, errno, "Can't stat '%S'",
+ wfilename);
+ else
+#endif
+ archive_set_error(a, errno, "Can't stat '%s'",
+ filename);
+ goto fail;
+ }
+
+ /*
+ * Determine whether the input looks like a disk device or a
+ * tape device. The results are used below to select an I/O
+ * strategy:
+ * = "disk-like" devices support arbitrary lseek() and will
+ * support I/O requests of any size. So we get easy skipping
+ * and can cheat on block sizes to get better performance.
+ * = "tape-like" devices require strict blocking and use
+ * specialized ioctls for seeking.
+ * = "socket-like" devices cannot seek at all but can improve
+ * performance by using nonblocking I/O to read "whatever is
+ * available right now".
+ *
+ * Right now, we only specially recognize disk-like devices,
+ * but it should be straightforward to add probes and strategy
+ * here for tape-like and socket-like devices.
+ */
+ if (S_ISREG(st.st_mode)) {
+ /* Safety: Tell the extractor not to overwrite the input. */
+ archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+ /* Regular files act like disks. */
+ is_disk_like = 1;
+ }
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /* FreeBSD: if it supports DIOCGMEDIASIZE ioctl, it's disk-like. */
+ else if (S_ISCHR(st.st_mode) &&
+ ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0 &&
+ mediasize > 0) {
+ is_disk_like = 1;
+ }
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ /* Net/OpenBSD: if it supports DIOCGDINFO ioctl, it's disk-like. */
+ else if ((S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) &&
+ ioctl(fd, DIOCGDINFO, &dl) == 0 &&
+ dl.d_partitions[DISKPART(st.st_rdev)].p_size > 0) {
+ is_disk_like = 1;
+ }
+#elif defined(__DragonFly__)
+ /* DragonFly BSD: if it supports DIOCGPART ioctl, it's disk-like. */
+ else if (S_ISCHR(st.st_mode) &&
+ ioctl(fd, DIOCGPART, &pi) == 0 &&
+ pi.media_size > 0) {
+ is_disk_like = 1;
+ }
+#elif defined(__linux__)
+ /* Linux: All block devices are disk-like. */
+ else if (S_ISBLK(st.st_mode) &&
+ lseek(fd, 0, SEEK_CUR) == 0 &&
+ lseek(fd, 0, SEEK_SET) == 0 &&
+ lseek(fd, 0, SEEK_END) > 0 &&
+ lseek(fd, 0, SEEK_SET) == 0) {
+ is_disk_like = 1;
+ }
+#endif
+ /* TODO: Add an "is_tape_like" variable and appropriate tests. */
+
+ /* Disk-like devices prefer power-of-two block sizes. */
+ /* Use provided block_size as a guide so users have some control. */
+ if (is_disk_like) {
+ size_t new_block_size = 64 * 1024;
+ while (new_block_size < mine->block_size
+ && new_block_size < 64 * 1024 * 1024)
+ new_block_size *= 2;
+ mine->block_size = new_block_size;
+ }
+ buffer = malloc(mine->block_size);
+ if (buffer == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ goto fail;
+ }
+ mine->buffer = buffer;
+ mine->fd = fd;
+ /* Remember mode so close can decide whether to flush. */
+ mine->st_mode = st.st_mode;
+
+ /* Disk-like inputs can use lseek(). */
+ if (is_disk_like)
+ mine->use_lseek = 1;
+
+ return (ARCHIVE_OK);
+fail:
+ /*
+ * Don't close file descriptors not opened or ones pointing referring
+ * to `FNT_STDIN`.
+ */
+ if (fd != -1 && fd != 0)
+ close(fd);
+ return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+ ssize_t bytes_read;
+
+ /* TODO: If a recent lseek() operation has left us
+ * mis-aligned, read and return a short block to try to get
+ * us back in alignment. */
+
+ /* TODO: Someday, try mmap() here; if that succeeds, give
+ * the entire file to libarchive as a single block. That
+ * could be a lot faster than block-by-block manual I/O. */
+
+ /* TODO: We might be able to improve performance on pipes and
+ * sockets by setting non-blocking I/O and just accepting
+ * whatever we get here instead of waiting for a full block
+ * worth of data. */
+
+ *buff = mine->buffer;
+ for (;;) {
+ bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+ if (bytes_read < 0) {
+ if (errno == EINTR)
+ continue;
+ else if (mine->filename_type == FNT_STDIN)
+ archive_set_error(a, errno,
+ "Error reading stdin");
+ else if (mine->filename_type == FNT_MBS)
+ archive_set_error(a, errno,
+ "Error reading '%s'", mine->filename.m);
+ else
+ archive_set_error(a, errno,
+ "Error reading '%S'", mine->filename.w);
+ }
+ return (bytes_read);
+ }
+}
+
+/*
+ * Regular files and disk-like block devices can use simple lseek
+ * without needing to round the request to the block size.
+ *
+ * TODO: This can leave future reads mis-aligned. Since we know the
+ * offset here, we should store it and use it in file_read() above
+ * to determine whether we should perform a short read to get back
+ * into alignment. Long series of mis-aligned reads can negatively
+ * impact disk throughput. (Of course, the performance impact should
+ * be carefully tested; extra code complexity is only worthwhile if
+ * it does provide measurable improvement.)
+ *
+ * TODO: Be lazy about the actual seek. There are a few pathological
+ * cases where libarchive makes a bunch of seek requests in a row
+ * without any intervening reads. This isn't a huge performance
+ * problem, since the kernel handles seeks lazily already, but
+ * it would be very slightly faster if we simply remembered the
+ * seek request here and then actually performed the seek at the
+ * top of the read callback above.
+ */
+static int64_t
+file_skip_lseek(struct archive *a, void *client_data, int64_t request)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* We use _lseeki64() on Windows. */
+ int64_t old_offset, new_offset;
+#else
+ off_t old_offset, new_offset;
+#endif
+
+ /* We use off_t here because lseek() is declared that way. */
+
+ /* TODO: Deal with case where off_t isn't 64 bits.
+ * This shouldn't be a problem on Linux or other POSIX
+ * systems, since the configuration logic for libarchive
+ * tries to obtain a 64-bit off_t.
+ */
+ if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0 &&
+ (new_offset = lseek(mine->fd, request, SEEK_CUR)) >= 0)
+ return (new_offset - old_offset);
+
+ /* If lseek() fails, don't bother trying again. */
+ mine->use_lseek = 0;
+
+ /* Let libarchive recover with read+discard */
+ if (errno == ESPIPE)
+ return (0);
+
+ /* If the input is corrupted or truncated, fail. */
+ if (mine->filename_type == FNT_STDIN)
+ archive_set_error(a, errno, "Error seeking in stdin");
+ else if (mine->filename_type == FNT_MBS)
+ archive_set_error(a, errno, "Error seeking in '%s'",
+ mine->filename.m);
+ else
+ archive_set_error(a, errno, "Error seeking in '%S'",
+ mine->filename.w);
+ return (-1);
+}
+
+
+/*
+ * TODO: Implement another file_skip_XXXX that uses MTIO ioctls to
+ * accelerate operation on tape drives.
+ */
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+
+ /* Delegate skip requests. */
+ if (mine->use_lseek)
+ return (file_skip_lseek(a, client_data, request));
+
+ /* If we can't skip, return 0; libarchive will read+discard instead. */
+ return (0);
+}
+
+/*
+ * TODO: Store the offset and use it in the read callback.
+ */
+static int64_t
+file_seek(struct archive *a, void *client_data, int64_t request, int whence)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+ int64_t r;
+
+ /* We use off_t here because lseek() is declared that way. */
+ /* See above for notes about when off_t is less than 64 bits. */
+ r = lseek(mine->fd, request, whence);
+ if (r >= 0)
+ return r;
+
+ /* If the input is corrupted or truncated, fail. */
+ if (mine->filename_type == FNT_STDIN)
+ archive_set_error(a, errno, "Error seeking in stdin");
+ else if (mine->filename_type == FNT_MBS)
+ archive_set_error(a, errno, "Error seeking in '%s'",
+ mine->filename.m);
+ else
+ archive_set_error(a, errno, "Error seeking in '%S'",
+ mine->filename.w);
+ return (ARCHIVE_FATAL);
+}
+
+static int
+file_close2(struct archive *a, void *client_data)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+
+ (void)a; /* UNUSED */
+
+ /* Only flush and close if open succeeded. */
+ if (mine->fd >= 0) {
+ /*
+ * Sometimes, we should flush the input before closing.
+ * Regular files: faster to just close without flush.
+ * Disk-like devices: Ditto.
+ * Tapes: must not flush (user might need to
+ * read the "next" item on a non-rewind device).
+ * Pipes and sockets: must flush (otherwise, the
+ * program feeding the pipe or socket may complain).
+ * Here, I flush everything except for regular files and
+ * device nodes.
+ */
+ if (!S_ISREG(mine->st_mode)
+ && !S_ISCHR(mine->st_mode)
+ && !S_ISBLK(mine->st_mode)) {
+ ssize_t bytesRead;
+ do {
+ bytesRead = read(mine->fd, mine->buffer,
+ mine->block_size);
+ } while (bytesRead > 0);
+ }
+ /* If a named file was opened, then it needs to be closed. */
+ if (mine->filename_type != FNT_STDIN)
+ close(mine->fd);
+ }
+ free(mine->buffer);
+ mine->buffer = NULL;
+ mine->fd = -1;
+ return (ARCHIVE_OK);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+ struct read_file_data *mine = (struct read_file_data *)client_data;
+ file_close2(a, client_data);
+ free(mine);
+ return (ARCHIVE_OK);
+}
+
+static int
+file_switch(struct archive *a, void *client_data1, void *client_data2)
+{
+ file_close2(a, client_data1);
+ return file_open(a, client_data2);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_open_memory.c b/contrib/libs/libarchive/libarchive/archive_read_open_memory.c
new file mode 100644
index 0000000000..311be47046
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_open_memory.c
@@ -0,0 +1,186 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.6 2007/07/06 15:51:59 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+/*
+ * Glue to read an archive from a block of memory.
+ *
+ * This is mostly a huge help in building test harnesses;
+ * test programs can build archives in memory and read them
+ * back again without having to mess with files on disk.
+ */
+
+struct read_memory_data {
+ const unsigned char *start;
+ const unsigned char *p;
+ const unsigned char *end;
+ ssize_t read_size;
+};
+
+static int memory_read_close(struct archive *, void *);
+static int memory_read_open(struct archive *, void *);
+static int64_t memory_read_seek(struct archive *, void *, int64_t offset, int whence);
+static int64_t memory_read_skip(struct archive *, void *, int64_t request);
+static ssize_t memory_read(struct archive *, void *, const void **buff);
+
+int
+archive_read_open_memory(struct archive *a, const void *buff, size_t size)
+{
+ return archive_read_open_memory2(a, buff, size, size);
+}
+
+/*
+ * Don't use _open_memory2() in production code; the archive_read_open_memory()
+ * version is the one you really want. This is just here so that
+ * test harnesses can exercise block operations inside the library.
+ */
+int
+archive_read_open_memory2(struct archive *a, const void *buff,
+ size_t size, size_t read_size)
+{
+ struct read_memory_data *mine;
+
+ mine = (struct read_memory_data *)calloc(1, sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->start = mine->p = (const unsigned char *)buff;
+ mine->end = mine->start + size;
+ mine->read_size = read_size;
+ archive_read_set_open_callback(a, memory_read_open);
+ archive_read_set_read_callback(a, memory_read);
+ archive_read_set_seek_callback(a, memory_read_seek);
+ archive_read_set_skip_callback(a, memory_read_skip);
+ archive_read_set_close_callback(a, memory_read_close);
+ archive_read_set_callback_data(a, mine);
+ return (archive_read_open1(a));
+}
+
+/*
+ * There's nothing to open.
+ */
+static int
+memory_read_open(struct archive *a, void *client_data)
+{
+ (void)a; /* UNUSED */
+ (void)client_data; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This is scary simple: Just advance a pointer. Limiting
+ * to read_size is not technically necessary, but it exercises
+ * more of the internal logic when used with a small block size
+ * in a test harness. Production use should not specify a block
+ * size; then this is much faster.
+ */
+static ssize_t
+memory_read(struct archive *a, void *client_data, const void **buff)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+ ssize_t size;
+
+ (void)a; /* UNUSED */
+ *buff = mine->p;
+ size = mine->end - mine->p;
+ if (size > mine->read_size)
+ size = mine->read_size;
+ mine->p += size;
+ return (size);
+}
+
+/*
+ * Advancing is just as simple. Again, this is doing more than
+ * necessary in order to better exercise internal code when used
+ * as a test harness.
+ */
+static int64_t
+memory_read_skip(struct archive *a, void *client_data, int64_t skip)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+ (void)a; /* UNUSED */
+ if ((int64_t)skip > (int64_t)(mine->end - mine->p))
+ skip = mine->end - mine->p;
+ /* Round down to block size. */
+ skip /= mine->read_size;
+ skip *= mine->read_size;
+ mine->p += skip;
+ return (skip);
+}
+
+/*
+ * Seeking.
+ */
+static int64_t
+memory_read_seek(struct archive *a, void *client_data, int64_t offset, int whence)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+ (void)a; /* UNUSED */
+ switch (whence) {
+ case SEEK_SET:
+ mine->p = mine->start + offset;
+ break;
+ case SEEK_CUR:
+ mine->p += offset;
+ break;
+ case SEEK_END:
+ mine->p = mine->end + offset;
+ break;
+ default:
+ return ARCHIVE_FATAL;
+ }
+ if (mine->p < mine->start) {
+ mine->p = mine->start;
+ return ARCHIVE_FAILED;
+ }
+ if (mine->p > mine->end) {
+ mine->p = mine->end;
+ return ARCHIVE_FAILED;
+ }
+ return (mine->p - mine->start);
+}
+
+/*
+ * Close is just cleaning up our one small bit of data.
+ */
+static int
+memory_read_close(struct archive *a, void *client_data)
+{
+ struct read_memory_data *mine = (struct read_memory_data *)client_data;
+ (void)a; /* UNUSED */
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_private.h b/contrib/libs/libarchive/libarchive/archive_read_private.h
new file mode 100644
index 0000000000..383405d529
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_private.h
@@ -0,0 +1,267 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_read_private.h 201088 2009-12-28 02:18:55Z kientzle $
+ */
+
+#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
+#define ARCHIVE_READ_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+struct archive_read;
+struct archive_read_filter_bidder;
+struct archive_read_filter;
+
+struct archive_read_filter_bidder_vtable {
+ /* Taste the upstream filter to see if we handle this. */
+ int (*bid)(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+ /* Initialize a newly-created filter. */
+ int (*init)(struct archive_read_filter *);
+ /* Release the bidder's configuration data. */
+ void (*free)(struct archive_read_filter_bidder *);
+};
+
+/*
+ * How bidding works for filters:
+ * * The bid manager initializes the client-provided reader as the
+ * first filter.
+ * * It invokes the bidder for each registered filter with the
+ * current head filter.
+ * * The bidders can use archive_read_filter_ahead() to peek ahead
+ * at the incoming data to compose their bids.
+ * * The bid manager creates a new filter structure for the winning
+ * bidder and gives the winning bidder a chance to initialize it.
+ * * The new filter becomes the new top filter and we repeat the
+ * process.
+ * This ends only when no bidder provides a non-zero bid. Then
+ * we perform a similar dance with the registered format handlers.
+ */
+struct archive_read_filter_bidder {
+ /* Configuration data for the bidder. */
+ void *data;
+ /* Name of the filter */
+ const char *name;
+ const struct archive_read_filter_bidder_vtable *vtable;
+};
+
+struct archive_read_filter_vtable {
+ /* Return next block. */
+ ssize_t (*read)(struct archive_read_filter *, const void **);
+ /* Close (just this filter) and free(self). */
+ int (*close)(struct archive_read_filter *self);
+ /* Read any header metadata if available. */
+ int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry);
+};
+
+/*
+ * This structure is allocated within the archive_read core
+ * and initialized by archive_read and the init() method of the
+ * corresponding bidder above.
+ */
+struct archive_read_filter {
+ int64_t position;
+ /* Essentially all filters will need these values, so
+ * just declare them here. */
+ struct archive_read_filter_bidder *bidder; /* My bidder. */
+ struct archive_read_filter *upstream; /* Who I read from. */
+ struct archive_read *archive; /* Associated archive. */
+ const struct archive_read_filter_vtable *vtable;
+ /* My private data. */
+ void *data;
+
+ const char *name;
+ int code;
+ int can_skip;
+ int can_seek;
+
+ /* Used by reblocking logic. */
+ char *buffer;
+ size_t buffer_size;
+ char *next; /* Current read location. */
+ size_t avail; /* Bytes in my buffer. */
+ const void *client_buff; /* Client buffer information. */
+ size_t client_total;
+ const char *client_next;
+ size_t client_avail;
+ char end_of_file;
+ char closed;
+ char fatal;
+};
+
+/*
+ * The client looks a lot like a filter, so we just wrap it here.
+ *
+ * TODO: Make archive_read_filter and archive_read_client identical so
+ * that users of the library can easily register their own
+ * transformation filters. This will probably break the API/ABI and
+ * so should be deferred at least until libarchive 3.0.
+ */
+struct archive_read_data_node {
+ int64_t begin_position;
+ int64_t total_size;
+ void *data;
+};
+struct archive_read_client {
+ archive_open_callback *opener;
+ archive_read_callback *reader;
+ archive_skip_callback *skipper;
+ archive_seek_callback *seeker;
+ archive_close_callback *closer;
+ archive_switch_callback *switcher;
+ unsigned int nodes;
+ unsigned int cursor;
+ int64_t position;
+ struct archive_read_data_node *dataset;
+};
+struct archive_read_passphrase {
+ char *passphrase;
+ struct archive_read_passphrase *next;
+};
+
+struct archive_read_extract {
+ struct archive *ad; /* archive_write_disk object */
+
+ /* Progress function invoked during extract. */
+ void (*extract_progress)(void *);
+ void *extract_progress_user_data;
+};
+
+struct archive_read {
+ struct archive archive;
+
+ struct archive_entry *entry;
+
+ /* Dev/ino of the archive being read/written. */
+ int skip_file_set;
+ int64_t skip_file_dev;
+ int64_t skip_file_ino;
+
+ /* Callbacks to open/read/write/close client archive streams. */
+ struct archive_read_client client;
+
+ /* Registered filter bidders. */
+ struct archive_read_filter_bidder bidders[16];
+
+ /* Last filter in chain */
+ struct archive_read_filter *filter;
+
+ /* Whether to bypass filter bidding process */
+ int bypass_filter_bidding;
+
+ /* File offset of beginning of most recently-read header. */
+ int64_t header_position;
+
+ /* Nodes and offsets of compressed data block */
+ unsigned int data_start_node;
+ unsigned int data_end_node;
+
+ /*
+ * Format detection is mostly the same as compression
+ * detection, with one significant difference: The bidders
+ * use the read_ahead calls above to examine the stream rather
+ * than having the supervisor hand them a block of data to
+ * examine.
+ */
+
+ struct archive_format_descriptor {
+ void *data;
+ const char *name;
+ int (*bid)(struct archive_read *, int best_bid);
+ int (*options)(struct archive_read *, const char *key,
+ const char *value);
+ int (*read_header)(struct archive_read *, struct archive_entry *);
+ int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *);
+ int (*read_data_skip)(struct archive_read *);
+ int64_t (*seek_data)(struct archive_read *, int64_t, int);
+ int (*cleanup)(struct archive_read *);
+ int (*format_capabilties)(struct archive_read *);
+ int (*has_encrypted_entries)(struct archive_read *);
+ } formats[16];
+ struct archive_format_descriptor *format; /* Active format. */
+
+ /*
+ * Various information needed by archive_extract.
+ */
+ struct archive_read_extract *extract;
+ int (*cleanup_archive_extract)(struct archive_read *);
+
+ /*
+ * Decryption passphrase.
+ */
+ struct {
+ struct archive_read_passphrase *first;
+ struct archive_read_passphrase **last;
+ int candidate;
+ archive_passphrase_callback *callback;
+ void *client_data;
+ } passphrases;
+};
+
+int __archive_read_register_format(struct archive_read *a,
+ void *format_data,
+ const char *name,
+ int (*bid)(struct archive_read *, int),
+ int (*options)(struct archive_read *, const char *, const char *),
+ int (*read_header)(struct archive_read *, struct archive_entry *),
+ int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),
+ int (*read_data_skip)(struct archive_read *),
+ int64_t (*seek_data)(struct archive_read *, int64_t, int),
+ int (*cleanup)(struct archive_read *),
+ int (*format_capabilities)(struct archive_read *),
+ int (*has_encrypted_entries)(struct archive_read *));
+
+int __archive_read_register_bidder(struct archive_read *a,
+ void *bidder_data,
+ const char *name,
+ const struct archive_read_filter_bidder_vtable *vtable);
+
+const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);
+const void *__archive_read_filter_ahead(struct archive_read_filter *,
+ size_t, ssize_t *);
+int64_t __archive_read_seek(struct archive_read*, int64_t, int);
+int64_t __archive_read_filter_seek(struct archive_read_filter *, int64_t, int);
+int64_t __archive_read_consume(struct archive_read *, int64_t);
+int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t);
+int __archive_read_header(struct archive_read *, struct archive_entry *);
+int __archive_read_program(struct archive_read_filter *, const char *);
+void __archive_read_free_filters(struct archive_read *);
+struct archive_read_extract *__archive_read_get_extract(struct archive_read *);
+
+
+/*
+ * Get a decryption passphrase.
+ */
+void __archive_read_reset_passphrase(struct archive_read *a);
+const char * __archive_read_next_passphrase(struct archive_read *a);
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_set_format.c b/contrib/libs/libarchive/libarchive/archive_read_set_format.c
new file mode 100644
index 0000000000..796dcdcced
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_set_format.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 2003-2012 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_set_format(struct archive *_a, int code)
+{
+ int r1, r2, slots, i;
+ char str[10];
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if ((r1 = archive_read_support_format_by_code(_a, code)) < (ARCHIVE_OK))
+ return r1;
+
+ r1 = r2 = (ARCHIVE_OK);
+ if (a->format)
+ r2 = (ARCHIVE_WARN);
+ switch (code & ARCHIVE_FORMAT_BASE_MASK)
+ {
+ case ARCHIVE_FORMAT_7ZIP:
+ strcpy(str, "7zip");
+ break;
+ case ARCHIVE_FORMAT_AR:
+ strcpy(str, "ar");
+ break;
+ case ARCHIVE_FORMAT_CAB:
+ strcpy(str, "cab");
+ break;
+ case ARCHIVE_FORMAT_CPIO:
+ strcpy(str, "cpio");
+ break;
+ case ARCHIVE_FORMAT_EMPTY:
+ strcpy(str, "empty");
+ break;
+ case ARCHIVE_FORMAT_ISO9660:
+ strcpy(str, "iso9660");
+ break;
+ case ARCHIVE_FORMAT_LHA:
+ strcpy(str, "lha");
+ break;
+ case ARCHIVE_FORMAT_MTREE:
+ strcpy(str, "mtree");
+ break;
+ case ARCHIVE_FORMAT_RAR:
+ strcpy(str, "rar");
+ break;
+ case ARCHIVE_FORMAT_RAR_V5:
+ strcpy(str, "rar5");
+ break;
+ case ARCHIVE_FORMAT_RAW:
+ strcpy(str, "raw");
+ break;
+ case ARCHIVE_FORMAT_TAR:
+ strcpy(str, "tar");
+ break;
+ case ARCHIVE_FORMAT_WARC:
+ strcpy(str, "warc");
+ break;
+ case ARCHIVE_FORMAT_XAR:
+ strcpy(str, "xar");
+ break;
+ case ARCHIVE_FORMAT_ZIP:
+ strcpy(str, "zip");
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Invalid format code specified");
+ return (ARCHIVE_FATAL);
+ }
+
+ slots = sizeof(a->formats) / sizeof(a->formats[0]);
+ a->format = &(a->formats[0]);
+ for (i = 0; i < slots; i++, a->format++) {
+ if (!a->format->name || !strcmp(a->format->name, str))
+ break;
+ }
+ if (!a->format->name || strcmp(a->format->name, str))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Unable to set format");
+ r1 = (ARCHIVE_FATAL);
+ }
+
+ return (r1 < r2) ? r1 : r2;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_set_options.c b/contrib/libs/libarchive/libarchive/archive_read_set_options.c
new file mode 100644
index 0000000000..2bd9b811ea
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_set_options.c
@@ -0,0 +1,133 @@
+/*-
+ * Copyright (c) 2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_read_private.h"
+#include "archive_options_private.h"
+
+static int archive_set_format_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+static int archive_set_filter_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+static int archive_set_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+
+int
+archive_read_set_format_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_READ_MAGIC, "archive_read_set_format_option",
+ archive_set_format_option);
+}
+
+int
+archive_read_set_filter_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_READ_MAGIC, "archive_read_set_filter_option",
+ archive_set_filter_option);
+}
+
+int
+archive_read_set_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_READ_MAGIC, "archive_read_set_option",
+ archive_set_option);
+}
+
+int
+archive_read_set_options(struct archive *a, const char *options)
+{
+ return _archive_set_options(a, options,
+ ARCHIVE_READ_MAGIC, "archive_read_set_options",
+ archive_set_option);
+}
+
+static int
+archive_set_format_option(struct archive *_a, const char *m, const char *o,
+ const char *v)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ size_t i;
+ int r, rv = ARCHIVE_WARN, matched_modules = 0;
+
+ for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) {
+ struct archive_format_descriptor *format = &a->formats[i];
+
+ if (format->options == NULL || format->name == NULL)
+ /* This format does not support option. */
+ continue;
+ if (m != NULL) {
+ if (strcmp(format->name, m) != 0)
+ continue;
+ ++matched_modules;
+ }
+
+ a->format = format;
+ r = format->options(a, o, v);
+ a->format = NULL;
+
+ if (r == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+
+ if (r == ARCHIVE_OK)
+ rv = ARCHIVE_OK;
+ }
+ /* If the format name didn't match, return a special code for
+ * _archive_set_option[s]. */
+ if (m != NULL && matched_modules == 0)
+ return ARCHIVE_WARN - 1;
+ return (rv);
+}
+
+static int
+archive_set_filter_option(struct archive *_a, const char *m, const char *o,
+ const char *v)
+{
+ (void)_a; /* UNUSED */
+ (void)o; /* UNUSED */
+ (void)v; /* UNUSED */
+
+ /* If the filter name didn't match, return a special code for
+ * _archive_set_option[s]. */
+ if (m != NULL)
+ return ARCHIVE_WARN - 1;
+ return ARCHIVE_WARN;
+}
+
+static int
+archive_set_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_either_option(a, m, o, v,
+ archive_set_format_option,
+ archive_set_filter_option);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_all.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_all.c
new file mode 100644
index 0000000000..edb508c1df
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_all.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_all(struct archive *a)
+{
+ return archive_read_support_filter_all(a);
+}
+#endif
+
+int
+archive_read_support_filter_all(struct archive *a)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_filter_all");
+
+ /* Bzip falls back to "bunzip2" command-line */
+ archive_read_support_filter_bzip2(a);
+ /* The decompress code doesn't use an outside library. */
+ archive_read_support_filter_compress(a);
+ /* Gzip decompress falls back to "gzip -d" command-line. */
+ archive_read_support_filter_gzip(a);
+ /* Lzip falls back to "unlzip" command-line program. */
+ archive_read_support_filter_lzip(a);
+ /* The LZMA file format has a very weak signature, so it
+ * may not be feasible to keep this here, but we'll try.
+ * This will come back out if there are problems. */
+ /* Lzma falls back to "unlzma" command-line program. */
+ archive_read_support_filter_lzma(a);
+ /* Xz falls back to "unxz" command-line program. */
+ archive_read_support_filter_xz(a);
+ /* The decode code doesn't use an outside library. */
+ archive_read_support_filter_uu(a);
+ /* The decode code doesn't use an outside library. */
+ archive_read_support_filter_rpm(a);
+ /* The decode code always uses "lrzip -q -d" command-line. */
+ archive_read_support_filter_lrzip(a);
+ /* Lzop decompress falls back to "lzop -d" command-line. */
+ archive_read_support_filter_lzop(a);
+ /* The decode code always uses "grzip -d" command-line. */
+ archive_read_support_filter_grzip(a);
+ /* Lz4 falls back to "lz4 -d" command-line program. */
+ archive_read_support_filter_lz4(a);
+ /* Zstd falls back to "zstd -d" command-line program. */
+ archive_read_support_filter_zstd(a);
+
+ /* Note: We always return ARCHIVE_OK here, even if some of the
+ * above return ARCHIVE_WARN. The intent here is to enable
+ * "as much as possible." Clients who need specific
+ * compression should enable those individually so they can
+ * verify the level of support. */
+ /* Clear any warning messages set by the above functions. */
+ archive_clear_error(a);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_by_code.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_by_code.c
new file mode 100644
index 0000000000..94c4af695f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_by_code.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2020 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_filter_by_code(struct archive *a, int filter_code)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_filter_by_code");
+
+ switch (filter_code) {
+ case ARCHIVE_FILTER_NONE:
+ return archive_read_support_filter_none(a);
+ break;
+ case ARCHIVE_FILTER_GZIP:
+ return archive_read_support_filter_gzip(a);
+ break;
+ case ARCHIVE_FILTER_BZIP2:
+ return archive_read_support_filter_bzip2(a);
+ break;
+ case ARCHIVE_FILTER_COMPRESS:
+ return archive_read_support_filter_compress(a);
+ break;
+ case ARCHIVE_FILTER_LZMA:
+ return archive_read_support_filter_lzma(a);
+ break;
+ case ARCHIVE_FILTER_XZ:
+ return archive_read_support_filter_xz(a);
+ break;
+ case ARCHIVE_FILTER_UU:
+ return archive_read_support_filter_uu(a);
+ break;
+ case ARCHIVE_FILTER_RPM:
+ return archive_read_support_filter_rpm(a);
+ break;
+ case ARCHIVE_FILTER_LZIP:
+ return archive_read_support_filter_lzip(a);
+ break;
+ case ARCHIVE_FILTER_LRZIP:
+ return archive_read_support_filter_lrzip(a);
+ break;
+ case ARCHIVE_FILTER_LZOP:
+ return archive_read_support_filter_lzop(a);
+ break;
+ case ARCHIVE_FILTER_GRZIP:
+ return archive_read_support_filter_grzip(a);
+ break;
+ case ARCHIVE_FILTER_LZ4:
+ return archive_read_support_filter_lz4(a);
+ break;
+ case ARCHIVE_FILTER_ZSTD:
+ return archive_read_support_filter_zstd(a);
+ break;
+ }
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_bzip2.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_bzip2.c
new file mode 100644
index 0000000000..9158e668eb
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_bzip2.c
@@ -0,0 +1,365 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+struct private_data {
+ bz_stream stream;
+ char *out_block;
+ size_t out_block_size;
+ char valid; /* True = decompressor is initialized */
+ char eof; /* True = found end of compressed data. */
+};
+
+/* Bzip2 filter */
+static ssize_t bzip2_filter_read(struct archive_read_filter *, const void **);
+static int bzip2_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect bzip2 archives even if we can't decompress
+ * them. (In fact, we like detecting them because we can give better
+ * error messages.) So the bid framework here gets compiled even
+ * if bzlib is unavailable.
+ */
+static int bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int bzip2_reader_init(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_bzip2(struct archive *a)
+{
+ return archive_read_support_filter_bzip2(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+bzip2_bidder_vtable = {
+ .bid = bzip2_reader_bid,
+ .init = bzip2_reader_init,
+};
+
+int
+archive_read_support_filter_bzip2(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "bzip2",
+ &bzip2_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external bzip2 program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails. It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ /* Minimal bzip2 archive is 14 bytes. */
+ buffer = __archive_read_filter_ahead(filter, 14, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /* First three bytes must be "BZh" */
+ bits_checked = 0;
+ if (memcmp(buffer, "BZh", 3) != 0)
+ return (0);
+ bits_checked += 24;
+
+ /* Next follows a compression flag which must be an ASCII digit. */
+ if (buffer[3] < '1' || buffer[3] > '9')
+ return (0);
+ bits_checked += 5;
+
+ /* After BZh[1-9], there must be either a data block
+ * which begins with 0x314159265359 or an end-of-data
+ * marker of 0x177245385090. */
+ if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0)
+ bits_checked += 48;
+ else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0)
+ bits_checked += 48;
+ else
+ return (0);
+
+ return (bits_checked);
+}
+
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+
+/*
+ * If we don't have the library on this system, we can't actually do the
+ * decompression. We can, however, still detect compressed archives
+ * and emit a useful message.
+ */
+static int
+bzip2_reader_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "bzip2 -d");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_BZIP2;
+ self->name = "bzip2";
+ return (r);
+}
+
+
+#else
+
+static const struct archive_read_filter_vtable
+bzip2_reader_vtable = {
+ .read = bzip2_filter_read,
+ .close = bzip2_filter_close,
+};
+
+/*
+ * Setup the callbacks.
+ */
+static int
+bzip2_reader_init(struct archive_read_filter *self)
+{
+ static const size_t out_block_size = 64 * 1024;
+ void *out_block;
+ struct private_data *state;
+
+ self->code = ARCHIVE_FILTER_BZIP2;
+ self->name = "bzip2";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = (unsigned char *)malloc(out_block_size);
+ if (state == NULL || out_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for bzip2 decompression");
+ free(out_block);
+ free(state);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ self->vtable = &bzip2_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+bzip2_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state;
+ size_t decompressed;
+ const char *read_buf;
+ ssize_t ret;
+
+ state = (struct private_data *)self->data;
+
+ if (state->eof) {
+ *p = NULL;
+ return (0);
+ }
+
+ /* Empty our output buffer. */
+ state->stream.next_out = state->out_block;
+ state->stream.avail_out = (uint32_t)state->out_block_size;
+
+ /* Try to fill the output buffer. */
+ for (;;) {
+ if (!state->valid) {
+ if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {
+ state->eof = 1;
+ *p = state->out_block;
+ decompressed = state->stream.next_out
+ - state->out_block;
+ return (decompressed);
+ }
+ /* Initialize compression library. */
+ ret = BZ2_bzDecompressInit(&(state->stream),
+ 0 /* library verbosity */,
+ 0 /* don't use low-mem algorithm */);
+
+ /* If init fails, try low-memory algorithm instead. */
+ if (ret == BZ_MEM_ERROR)
+ ret = BZ2_bzDecompressInit(&(state->stream),
+ 0 /* library verbosity */,
+ 1 /* do use low-mem algo */);
+
+ if (ret != BZ_OK) {
+ const char *detail = NULL;
+ int err = ARCHIVE_ERRNO_MISC;
+ switch (ret) {
+ case BZ_PARAM_ERROR:
+ detail = "invalid setup parameter";
+ break;
+ case BZ_MEM_ERROR:
+ err = ENOMEM;
+ detail = "out of memory";
+ break;
+ case BZ_CONFIG_ERROR:
+ detail = "mis-compiled library";
+ break;
+ }
+ archive_set_error(&self->archive->archive, err,
+ "Internal error initializing decompressor%s%s",
+ detail == NULL ? "" : ": ",
+ detail);
+ return (ARCHIVE_FATAL);
+ }
+ state->valid = 1;
+ }
+
+ /* stream.next_in is really const, but bzlib
+ * doesn't declare it so. <sigh> */
+ read_buf =
+ __archive_read_filter_ahead(self->upstream, 1, &ret);
+ if (read_buf == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated bzip2 input");
+ return (ARCHIVE_FATAL);
+ }
+ state->stream.next_in = (char *)(uintptr_t)read_buf;
+ state->stream.avail_in = (uint32_t)ret;
+ /* There is no more data, return whatever we have. */
+ if (ret == 0) {
+ state->eof = 1;
+ *p = state->out_block;
+ decompressed = state->stream.next_out
+ - state->out_block;
+ return (decompressed);
+ }
+
+ /* Decompress as much as we can in one pass. */
+ ret = BZ2_bzDecompress(&(state->stream));
+ __archive_read_filter_consume(self->upstream,
+ state->stream.next_in - read_buf);
+
+ switch (ret) {
+ case BZ_STREAM_END: /* Found end of stream. */
+ switch (BZ2_bzDecompressEnd(&(state->stream))) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up decompressor");
+ return (ARCHIVE_FATAL);
+ }
+ state->valid = 0;
+ /* FALLTHROUGH */
+ case BZ_OK: /* Decompressor made some progress. */
+ /* If we filled our buffer, update stats and return. */
+ if (state->stream.avail_out == 0) {
+ *p = state->out_block;
+ decompressed = state->stream.next_out
+ - state->out_block;
+ return (decompressed);
+ }
+ break;
+ default: /* Return an error. */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "bzip decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+bzip2_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ int ret = ARCHIVE_OK;
+
+ state = (struct private_data *)self->data;
+
+ if (state->valid) {
+ switch (BZ2_bzDecompressEnd(&state->stream)) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up decompressor");
+ ret = ARCHIVE_FATAL;
+ }
+ state->valid = 0;
+ }
+
+ free(state->out_block);
+ free(state);
+ return (ret);
+}
+
+#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_compress.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_compress.c
new file mode 100644
index 0000000000..05b80a576a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_compress.c
@@ -0,0 +1,452 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+/*
+ * This code borrows heavily from "compress" source code, which is
+ * protected by the following copyright. (Clause 3 dropped by request
+ * of the Regents.)
+ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+/*
+ * Because LZW decompression is pretty simple, I've just implemented
+ * the whole decompressor here (cribbing from "compress" source code,
+ * of course), rather than relying on an external library. I have
+ * made an effort to clarify and simplify the algorithm, so the
+ * names and structure here don't exactly match those used by compress.
+ */
+
+struct private_data {
+ /* Input variables. */
+ const unsigned char *next_in;
+ size_t avail_in;
+ size_t consume_unnotified;
+ int bit_buffer;
+ int bits_avail;
+ size_t bytes_in_section;
+
+ /* Output variables. */
+ size_t out_block_size;
+ void *out_block;
+
+ /* Decompression status variables. */
+ int use_reset_code;
+ int end_of_stream; /* EOF status. */
+ int maxcode; /* Largest code. */
+ int maxcode_bits; /* Length of largest code. */
+ int section_end_code; /* When to increase bits. */
+ int bits; /* Current code length. */
+ int oldcode; /* Previous code. */
+ int finbyte; /* Last byte of prev code. */
+
+ /* Dictionary. */
+ int free_ent; /* Next dictionary entry. */
+ unsigned char suffix[65536];
+ uint16_t prefix[65536];
+
+ /*
+ * Scratch area for expanding dictionary entries. Note:
+ * "worst" case here comes from compressing /dev/zero: the
+ * last code in the dictionary will code a sequence of
+ * 65536-256 zero bytes. Thus, we need stack space to expand
+ * a 65280-byte dictionary entry. (Of course, 32640:1
+ * compression could also be considered the "best" case. ;-)
+ */
+ unsigned char *stackp;
+ unsigned char stack[65300];
+};
+
+static int compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int compress_bidder_init(struct archive_read_filter *);
+
+static ssize_t compress_filter_read(struct archive_read_filter *, const void **);
+static int compress_filter_close(struct archive_read_filter *);
+
+static int getbits(struct archive_read_filter *, int n);
+static int next_code(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_compress(struct archive *a)
+{
+ return archive_read_support_filter_compress(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+compress_bidder_vtable = {
+ .bid = compress_bidder_bid,
+ .init = compress_bidder_init,
+};
+
+int
+archive_read_support_filter_compress(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ return __archive_read_register_bidder(a, NULL, "compress (.Z)",
+ &compress_bidder_vtable);
+}
+
+/*
+ * Test whether we can handle this data.
+ * This logic returns zero if any part of the signature fails.
+ */
+static int
+compress_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ /* Shortest valid compress file is 3 bytes. */
+ buffer = __archive_read_filter_ahead(filter, 3, &avail);
+
+ if (buffer == NULL)
+ return (0);
+
+ bits_checked = 0;
+ /* First two bytes are the magic value */
+ if (buffer[0] != 0x1F || buffer[1] != 0x9D)
+ return (0);
+ /* Third byte holds compression parameters. */
+ if (buffer[2] & 0x20) /* Reserved bit, must be zero. */
+ return (0);
+ if (buffer[2] & 0x40) /* Reserved bit, must be zero. */
+ return (0);
+ bits_checked += 18;
+
+ return (bits_checked);
+}
+
+static const struct archive_read_filter_vtable
+compress_reader_vtable = {
+ .read = compress_filter_read,
+ .close = compress_filter_close,
+};
+
+/*
+ * Setup the callbacks.
+ */
+static int
+compress_bidder_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ static const size_t out_block_size = 64 * 1024;
+ void *out_block;
+ int code;
+
+ self->code = ARCHIVE_FILTER_COMPRESS;
+ self->name = "compress (.Z)";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = malloc(out_block_size);
+ if (state == NULL || out_block == NULL) {
+ free(out_block);
+ free(state);
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for %s decompression",
+ self->name);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ self->vtable = &compress_reader_vtable;
+
+ /* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */
+
+ (void)getbits(self, 8); /* Skip first signature byte. */
+ (void)getbits(self, 8); /* Skip second signature byte. */
+
+ /* Get compression parameters. */
+ code = getbits(self, 8);
+ if ((code & 0x1f) > 16) {
+ archive_set_error(&self->archive->archive, -1,
+ "Invalid compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ state->maxcode_bits = code & 0x1f;
+ state->maxcode = (1 << state->maxcode_bits);
+ state->use_reset_code = code & 0x80;
+
+ /* Initialize decompressor. */
+ state->free_ent = 256;
+ state->stackp = state->stack;
+ if (state->use_reset_code)
+ state->free_ent++;
+ state->bits = 9;
+ state->section_end_code = (1<<state->bits) - 1;
+ state->oldcode = -1;
+ for (code = 255; code >= 0; code--) {
+ state->prefix[code] = 0;
+ state->suffix[code] = code;
+ }
+ next_code(self);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return a block of data from the decompression buffer. Decompress more
+ * as necessary.
+ */
+static ssize_t
+compress_filter_read(struct archive_read_filter *self, const void **pblock)
+{
+ struct private_data *state;
+ unsigned char *p, *start, *end;
+ int ret;
+
+ state = (struct private_data *)self->data;
+ if (state->end_of_stream) {
+ *pblock = NULL;
+ return (0);
+ }
+ p = start = (unsigned char *)state->out_block;
+ end = start + state->out_block_size;
+
+ while (p < end && !state->end_of_stream) {
+ if (state->stackp > state->stack) {
+ *p++ = *--state->stackp;
+ } else {
+ ret = next_code(self);
+ if (ret == -1)
+ state->end_of_stream = ret;
+ else if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+ }
+
+ *pblock = start;
+ return (p - start);
+}
+
+/*
+ * Close and release the filter.
+ */
+static int
+compress_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+
+ free(state->out_block);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Process the next code and fill the stack with the expansion
+ * of the code. Returns ARCHIVE_FATAL if there is a fatal I/O or
+ * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
+ */
+static int
+next_code(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ int code, newcode;
+
+ static int debug_buff[1024];
+ static unsigned debug_index;
+
+ code = newcode = getbits(self, state->bits);
+ if (code < 0)
+ return (code);
+
+ debug_buff[debug_index++] = code;
+ if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0]))
+ debug_index = 0;
+
+ /* If it's a reset code, reset the dictionary. */
+ if ((code == 256) && state->use_reset_code) {
+ /*
+ * The original 'compress' implementation blocked its
+ * I/O in a manner that resulted in junk bytes being
+ * inserted after every reset. The next section skips
+ * this junk. (Yes, the number of *bytes* to skip is
+ * a function of the current *bit* length.)
+ */
+ int skip_bytes = state->bits -
+ (state->bytes_in_section % state->bits);
+ skip_bytes %= state->bits;
+ state->bits_avail = 0; /* Discard rest of this byte. */
+ while (skip_bytes-- > 0) {
+ code = getbits(self, 8);
+ if (code < 0)
+ return (code);
+ }
+ /* Now, actually do the reset. */
+ state->bytes_in_section = 0;
+ state->bits = 9;
+ state->section_end_code = (1 << state->bits) - 1;
+ state->free_ent = 257;
+ state->oldcode = -1;
+ return (next_code(self));
+ }
+
+ if (code > state->free_ent
+ || (code == state->free_ent && state->oldcode < 0)) {
+ /* An invalid code is a fatal error. */
+ archive_set_error(&(self->archive->archive), -1,
+ "Invalid compressed data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Special case for KwKwK string. */
+ if (code >= state->free_ent) {
+ *state->stackp++ = state->finbyte;
+ code = state->oldcode;
+ }
+
+ /* Generate output characters in reverse order. */
+ while (code >= 256) {
+ *state->stackp++ = state->suffix[code];
+ code = state->prefix[code];
+ }
+ *state->stackp++ = state->finbyte = code;
+
+ /* Generate the new entry. */
+ code = state->free_ent;
+ if (code < state->maxcode && state->oldcode >= 0) {
+ state->prefix[code] = state->oldcode;
+ state->suffix[code] = state->finbyte;
+ ++state->free_ent;
+ }
+ if (state->free_ent > state->section_end_code) {
+ state->bits++;
+ state->bytes_in_section = 0;
+ if (state->bits == state->maxcode_bits)
+ state->section_end_code = state->maxcode;
+ else
+ state->section_end_code = (1 << state->bits) - 1;
+ }
+
+ /* Remember previous code. */
+ state->oldcode = newcode;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return next 'n' bits from stream.
+ *
+ * -1 indicates end of available data.
+ */
+static int
+getbits(struct archive_read_filter *self, int n)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ int code;
+ ssize_t ret;
+ static const int mask[] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
+ 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+ };
+
+ while (state->bits_avail < n) {
+ if (state->avail_in <= 0) {
+ if (state->consume_unnotified) {
+ __archive_read_filter_consume(self->upstream,
+ state->consume_unnotified);
+ state->consume_unnotified = 0;
+ }
+ state->next_in
+ = __archive_read_filter_ahead(self->upstream,
+ 1, &ret);
+ if (ret == 0)
+ return (-1);
+ if (ret < 0 || state->next_in == NULL)
+ return (ARCHIVE_FATAL);
+ state->consume_unnotified = state->avail_in = ret;
+ }
+ state->bit_buffer |= *state->next_in++ << state->bits_avail;
+ state->avail_in--;
+ state->bits_avail += 8;
+ state->bytes_in_section++;
+ }
+
+ code = state->bit_buffer;
+ state->bit_buffer >>= n;
+ state->bits_avail -= n;
+
+ return (code & mask[n]);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_grzip.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_grzip.c
new file mode 100644
index 0000000000..d4d1737cd9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_grzip.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static const unsigned char grzip_magic[] = {
+ 0x47, 0x52, 0x5a, 0x69, 0x70, 0x49, 0x49, 0x00,
+ 0x02, 0x04, 0x3a, 0x29 };
+
+static int grzip_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int grzip_bidder_init(struct archive_read_filter *);
+
+
+static const struct archive_read_filter_bidder_vtable
+grzip_bidder_vtable = {
+ .bid = grzip_bidder_bid,
+ .init = grzip_bidder_init,
+};
+
+int
+archive_read_support_filter_grzip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, NULL,
+ &grzip_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* This filter always uses an external program. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external grzip program for grzip decompression");
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+grzip_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *p;
+ ssize_t avail;
+
+ (void)self; /* UNUSED */
+
+ p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), &avail);
+ if (p == NULL || avail == 0)
+ return (0);
+
+ if (memcmp(p, grzip_magic, sizeof(grzip_magic)))
+ return (0);
+
+ return (sizeof(grzip_magic) * 8);
+}
+
+static int
+grzip_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "grzip -d");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_GRZIP;
+ self->name = "grzip";
+ return (r);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_gzip.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_gzip.c
new file mode 100644
index 0000000000..4135a63618
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_gzip.c
@@ -0,0 +1,536 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#ifdef HAVE_ZLIB_H
+struct private_data {
+ z_stream stream;
+ char in_stream;
+ unsigned char *out_block;
+ size_t out_block_size;
+ int64_t total_out;
+ unsigned long crc;
+ uint32_t mtime;
+ char *name;
+ char eof; /* True = found end of compressed data. */
+};
+
+/* Gzip Filter. */
+static ssize_t gzip_filter_read(struct archive_read_filter *, const void **);
+static int gzip_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect gzip archives even if we can't decompress
+ * them. (In fact, we like detecting them because we can give better
+ * error messages.) So the bid framework here gets compiled even
+ * if zlib is unavailable.
+ *
+ * TODO: If zlib is unavailable, gzip_bidder_init() should
+ * use the compress_program framework to try to fire up an external
+ * gzip program.
+ */
+static int gzip_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int gzip_bidder_init(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_gzip(struct archive *a)
+{
+ return archive_read_support_filter_gzip(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+gzip_bidder_vtable = {
+ .bid = gzip_bidder_bid,
+ .init = gzip_bidder_init,
+};
+
+int
+archive_read_support_filter_gzip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "gzip",
+ &gzip_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Signal the extent of gzip support with the return value here. */
+#if HAVE_ZLIB_H
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external gzip program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Read and verify the header.
+ *
+ * Returns zero if the header couldn't be validated, else returns
+ * number of bytes in header. If pbits is non-NULL, it receives a
+ * count of bits verified, suitable for use by bidder.
+ */
+static ssize_t
+peek_at_header(struct archive_read_filter *filter, int *pbits,
+#ifdef HAVE_ZLIB_H
+ struct private_data *state
+#else
+ void *state
+#endif
+ )
+{
+ const unsigned char *p;
+ ssize_t avail, len;
+ int bits = 0;
+ int header_flags;
+#ifndef HAVE_ZLIB_H
+ (void)state; /* UNUSED */
+#endif
+
+ /* Start by looking at the first ten bytes of the header, which
+ * is all fixed layout. */
+ len = 10;
+ p = __archive_read_filter_ahead(filter, len, &avail);
+ if (p == NULL || avail == 0)
+ return (0);
+ /* We only support deflation- third byte must be 0x08. */
+ if (memcmp(p, "\x1F\x8B\x08", 3) != 0)
+ return (0);
+ bits += 24;
+ if ((p[3] & 0xE0)!= 0) /* No reserved flags set. */
+ return (0);
+ bits += 3;
+ header_flags = p[3];
+ /* Bytes 4-7 are mod time in little endian. */
+#ifdef HAVE_ZLIB_H
+ if (state)
+ state->mtime = archive_le32dec(p + 4);
+#endif
+ /* Byte 8 is deflate flags. */
+ /* XXXX TODO: return deflate flags back to consume_header for use
+ in initializing the decompressor. */
+ /* Byte 9 is OS. */
+
+ /* Optional extra data: 2 byte length plus variable body. */
+ if (header_flags & 4) {
+ p = __archive_read_filter_ahead(filter, len + 2, &avail);
+ if (p == NULL)
+ return (0);
+ len += ((int)p[len + 1] << 8) | (int)p[len];
+ len += 2;
+ }
+
+ /* Null-terminated optional filename. */
+ if (header_flags & 8) {
+#ifdef HAVE_ZLIB_H
+ ssize_t file_start = len;
+#endif
+ do {
+ ++len;
+ if (avail < len)
+ p = __archive_read_filter_ahead(filter,
+ len, &avail);
+ if (p == NULL)
+ return (0);
+ } while (p[len - 1] != 0);
+
+#ifdef HAVE_ZLIB_H
+ if (state) {
+ /* Reset the name in case of repeat header reads. */
+ free(state->name);
+ state->name = strdup((const char *)&p[file_start]);
+ }
+#endif
+ }
+
+ /* Null-terminated optional comment. */
+ if (header_flags & 16) {
+ do {
+ ++len;
+ if (avail < len)
+ p = __archive_read_filter_ahead(filter,
+ len, &avail);
+ if (p == NULL)
+ return (0);
+ } while (p[len - 1] != 0);
+ }
+
+ /* Optional header CRC */
+ if ((header_flags & 2)) {
+ p = __archive_read_filter_ahead(filter, len + 2, &avail);
+ if (p == NULL)
+ return (0);
+#if 0
+ int hcrc = ((int)p[len + 1] << 8) | (int)p[len];
+ int crc = /* XXX TODO: Compute header CRC. */;
+ if (crc != hcrc)
+ return (0);
+ bits += 16;
+#endif
+ len += 2;
+ }
+
+ if (pbits != NULL)
+ *pbits = bits;
+ return (len);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+gzip_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ if (peek_at_header(filter, &bits_checked, NULL))
+ return (bits_checked);
+ return (0);
+}
+
+#ifndef HAVE_ZLIB_H
+
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly. We can, however, try to run "gzip -d"
+ * in case that's available.
+ */
+static int
+gzip_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "gzip -d");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_GZIP;
+ self->name = "gzip";
+ return (r);
+}
+
+#else
+
+static int
+gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+{
+ struct private_data *state;
+
+ state = (struct private_data *)self->data;
+
+ /* A mtime of 0 is considered invalid/missing. */
+ if (state->mtime != 0)
+ archive_entry_set_mtime(entry, state->mtime, 0);
+
+ /* If the name is available, extract it. */
+ if (state->name)
+ archive_entry_set_pathname(entry, state->name);
+
+ return (ARCHIVE_OK);
+}
+
+static const struct archive_read_filter_vtable
+gzip_reader_vtable = {
+ .read = gzip_filter_read,
+ .close = gzip_filter_close,
+#ifdef HAVE_ZLIB_H
+ .read_header = gzip_read_header,
+#endif
+};
+
+/*
+ * Initialize the filter object.
+ */
+static int
+gzip_bidder_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ static const size_t out_block_size = 64 * 1024;
+ void *out_block;
+
+ self->code = ARCHIVE_FILTER_GZIP;
+ self->name = "gzip";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = (unsigned char *)malloc(out_block_size);
+ if (state == NULL || out_block == NULL) {
+ free(out_block);
+ free(state);
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for gzip decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ self->vtable = &gzip_reader_vtable;
+
+ state->in_stream = 0; /* We're not actually within a stream yet. */
+
+ return (ARCHIVE_OK);
+}
+
+static int
+consume_header(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ ssize_t avail;
+ size_t len;
+ int ret;
+
+ state = (struct private_data *)self->data;
+
+ /* If this is a real header, consume it. */
+ len = peek_at_header(self->upstream, NULL, state);
+ if (len == 0)
+ return (ARCHIVE_EOF);
+ __archive_read_filter_consume(self->upstream, len);
+
+ /* Initialize CRC accumulator. */
+ state->crc = crc32(0L, NULL, 0);
+
+ /* Initialize compression library. */
+ state->stream.next_in = (unsigned char *)(uintptr_t)
+ __archive_read_filter_ahead(self->upstream, 1, &avail);
+ state->stream.avail_in = (uInt)avail;
+ ret = inflateInit2(&(state->stream),
+ -15 /* Don't check for zlib header */);
+
+ /* Decipher the error code. */
+ switch (ret) {
+ case Z_OK:
+ state->in_stream = 1;
+ return (ARCHIVE_OK);
+ case Z_STREAM_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "invalid setup parameter");
+ break;
+ case Z_MEM_ERROR:
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Internal error initializing compression library: "
+ "out of memory");
+ break;
+ case Z_VERSION_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "invalid library version");
+ break;
+ default:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ " Zlib error %d", ret);
+ break;
+ }
+ return (ARCHIVE_FATAL);
+}
+
+static int
+consume_trailer(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ const unsigned char *p;
+ ssize_t avail;
+
+ state = (struct private_data *)self->data;
+
+ state->in_stream = 0;
+ switch (inflateEnd(&(state->stream))) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up gzip decompressor");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* GZip trailer is a fixed 8 byte structure. */
+ p = __archive_read_filter_ahead(self->upstream, 8, &avail);
+ if (p == NULL || avail == 0)
+ return (ARCHIVE_FATAL);
+
+ /* XXX TODO: Verify the length and CRC. */
+
+ /* We've verified the trailer, so consume it now. */
+ __archive_read_filter_consume(self->upstream, 8);
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+gzip_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state;
+ size_t decompressed;
+ ssize_t avail_in, max_in;
+ int ret;
+
+ state = (struct private_data *)self->data;
+
+ /* Empty our output buffer. */
+ state->stream.next_out = state->out_block;
+ state->stream.avail_out = (uInt)state->out_block_size;
+
+ /* Try to fill the output buffer. */
+ while (state->stream.avail_out > 0 && !state->eof) {
+ /* If we're not in a stream, read a header
+ * and initialize the decompression library. */
+ if (!state->in_stream) {
+ ret = consume_header(self);
+ if (ret == ARCHIVE_EOF) {
+ state->eof = 1;
+ break;
+ }
+ if (ret < ARCHIVE_OK)
+ return (ret);
+ }
+
+ /* Peek at the next available data. */
+ /* ZLib treats stream.next_in as const but doesn't declare
+ * it so, hence this ugly cast. */
+ state->stream.next_in = (unsigned char *)(uintptr_t)
+ __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+ if (state->stream.next_in == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated gzip input");
+ return (ARCHIVE_FATAL);
+ }
+ if (UINT_MAX >= SSIZE_MAX)
+ max_in = SSIZE_MAX;
+ else
+ max_in = UINT_MAX;
+ if (avail_in > max_in)
+ avail_in = max_in;
+ state->stream.avail_in = (uInt)avail_in;
+
+ /* Decompress and consume some of that data. */
+ ret = inflate(&(state->stream), 0);
+ switch (ret) {
+ case Z_OK: /* Decompressor made some progress. */
+ __archive_read_filter_consume(self->upstream,
+ avail_in - state->stream.avail_in);
+ break;
+ case Z_STREAM_END: /* Found end of stream. */
+ __archive_read_filter_consume(self->upstream,
+ avail_in - state->stream.avail_in);
+ /* Consume the stream trailer; release the
+ * decompression library. */
+ ret = consume_trailer(self);
+ if (ret < ARCHIVE_OK)
+ return (ret);
+ break;
+ default:
+ /* Return an error. */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "gzip decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* We've read as much as we can. */
+ decompressed = state->stream.next_out - state->out_block;
+ state->total_out += decompressed;
+ if (decompressed == 0)
+ *p = NULL;
+ else
+ *p = state->out_block;
+ return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+gzip_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ int ret;
+
+ state = (struct private_data *)self->data;
+ ret = ARCHIVE_OK;
+
+ if (state->in_stream) {
+ switch (inflateEnd(&(state->stream))) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up gzip compressor");
+ ret = ARCHIVE_FATAL;
+ }
+ }
+
+ free(state->name);
+ free(state->out_block);
+ free(state);
+ return (ret);
+}
+
+#endif /* HAVE_ZLIB_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_lrzip.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lrzip.c
new file mode 100644
index 0000000000..a2389894f1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lrzip.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define LRZIP_HEADER_MAGIC "LRZI"
+#define LRZIP_HEADER_MAGIC_LEN 4
+
+static int lrzip_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int lrzip_bidder_init(struct archive_read_filter *);
+
+
+static const struct archive_read_filter_bidder_vtable
+lrzip_bidder_vtable = {
+ .bid = lrzip_bidder_bid,
+ .init = lrzip_bidder_init,
+};
+
+int
+archive_read_support_filter_lrzip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "lrzip",
+ &lrzip_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* This filter always uses an external program. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lrzip program for lrzip decompression");
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+lrzip_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *p;
+ ssize_t avail, len;
+ int i;
+
+ (void)self; /* UNUSED */
+ /* Start by looking at the first six bytes of the header, which
+ * is all fixed layout. */
+ len = 6;
+ p = __archive_read_filter_ahead(filter, len, &avail);
+ if (p == NULL || avail == 0)
+ return (0);
+
+ if (memcmp(p, LRZIP_HEADER_MAGIC, LRZIP_HEADER_MAGIC_LEN))
+ return (0);
+
+ /* current major version is always 0, verify this */
+ if (p[LRZIP_HEADER_MAGIC_LEN])
+ return 0;
+ /* support only v0.6+ lrzip for sanity */
+ i = p[LRZIP_HEADER_MAGIC_LEN + 1];
+ if ((i < 6) || (i > 10))
+ return 0;
+
+ return (int)len;
+}
+
+static int
+lrzip_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lrzip -d -q");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LRZIP;
+ self->name = "lrzip";
+ return (r);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_lz4.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lz4.c
new file mode 100644
index 0000000000..d0fc1a83e4
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lz4.c
@@ -0,0 +1,742 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_xxhash.h"
+
+#define LZ4_MAGICNUMBER 0x184d2204
+#define LZ4_SKIPPABLED 0x184d2a50
+#define LZ4_LEGACY 0x184c2102
+
+#if defined(HAVE_LIBLZ4)
+struct private_data {
+ enum { SELECT_STREAM,
+ READ_DEFAULT_STREAM,
+ READ_DEFAULT_BLOCK,
+ READ_LEGACY_STREAM,
+ READ_LEGACY_BLOCK,
+ } stage;
+ struct {
+ unsigned block_independence:1;
+ unsigned block_checksum:3;
+ unsigned stream_size:1;
+ unsigned stream_checksum:1;
+ unsigned preset_dictionary:1;
+ int block_maximum_size;
+ } flags;
+ int64_t stream_size;
+ uint32_t dict_id;
+ char *out_block;
+ size_t out_block_size;
+
+ /* Bytes read but not yet consumed via __archive_read_consume() */
+ size_t unconsumed;
+ size_t decoded_size;
+ void *xxh32_state;
+
+ char valid; /* True = decompressor is initialized */
+ char eof; /* True = found end of compressed data. */
+};
+
+#define LEGACY_BLOCK_SIZE (8 * 1024 * 1024)
+
+/* Lz4 filter */
+static ssize_t lz4_filter_read(struct archive_read_filter *, const void **);
+static int lz4_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect lz4 archives even if we can't decompress
+ * them. (In fact, we like detecting them because we can give better
+ * error messages.) So the bid framework here gets compiled even
+ * if liblz4 is unavailable.
+ */
+static int lz4_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int lz4_reader_init(struct archive_read_filter *);
+#if defined(HAVE_LIBLZ4)
+static ssize_t lz4_filter_read_default_stream(struct archive_read_filter *,
+ const void **);
+static ssize_t lz4_filter_read_legacy_stream(struct archive_read_filter *,
+ const void **);
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+lz4_bidder_vtable = {
+ .bid = lz4_reader_bid,
+ .init = lz4_reader_init,
+};
+
+int
+archive_read_support_filter_lz4(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "lz4",
+ &lz4_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if defined(HAVE_LIBLZ4)
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lz4 program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails. It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+lz4_reader_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ int bits_checked;
+ uint32_t number;
+
+ (void)self; /* UNUSED */
+
+ /* Minimal lz4 archive is 11 bytes. */
+ buffer = __archive_read_filter_ahead(filter, 11, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /* First four bytes must be LZ4 magic numbers. */
+ bits_checked = 0;
+ if ((number = archive_le32dec(buffer)) == LZ4_MAGICNUMBER) {
+ unsigned char flag, BD;
+
+ bits_checked += 32;
+ /* Next follows a stream descriptor. */
+ /* Descriptor Flags. */
+ flag = buffer[4];
+ /* A version number must be "01". */
+ if (((flag & 0xc0) >> 6) != 1)
+ return (0);
+ /* A reserved bit must be "0". */
+ if (flag & 2)
+ return (0);
+ bits_checked += 8;
+ BD = buffer[5];
+ /* A block maximum size should be more than 3. */
+ if (((BD & 0x70) >> 4) < 4)
+ return (0);
+ /* Reserved bits must be "0". */
+ if (BD & ~0x70)
+ return (0);
+ bits_checked += 8;
+ } else if (number == LZ4_LEGACY) {
+ bits_checked += 32;
+ }
+
+ return (bits_checked);
+}
+
+#if !defined(HAVE_LIBLZ4)
+
+/*
+ * If we don't have the library on this system, we can't actually do the
+ * decompression. We can, however, still detect compressed archives
+ * and emit a useful message.
+ */
+static int
+lz4_reader_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lz4 -d -q");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LZ4;
+ self->name = "lz4";
+ return (r);
+}
+
+
+#else
+
+static const struct archive_read_filter_vtable
+lz4_reader_vtable = {
+ .read = lz4_filter_read,
+ .close = lz4_filter_close,
+};
+
+/*
+ * Setup the callbacks.
+ */
+static int
+lz4_reader_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+
+ self->code = ARCHIVE_FILTER_LZ4;
+ self->name = "lz4";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ if (state == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lz4 decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->stage = SELECT_STREAM;
+ self->vtable = &lz4_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+lz4_allocate_out_block(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ size_t out_block_size = state->flags.block_maximum_size;
+ void *out_block;
+
+ if (!state->flags.block_independence)
+ out_block_size += 64 * 1024;
+ if (state->out_block_size < out_block_size) {
+ free(state->out_block);
+ out_block = (unsigned char *)malloc(out_block_size);
+ state->out_block_size = out_block_size;
+ if (out_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lz4 decompression");
+ return (ARCHIVE_FATAL);
+ }
+ state->out_block = out_block;
+ }
+ if (!state->flags.block_independence)
+ memset(state->out_block, 0, 64 * 1024);
+ return (ARCHIVE_OK);
+}
+
+static int
+lz4_allocate_out_block_for_legacy(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ size_t out_block_size = LEGACY_BLOCK_SIZE;
+ void *out_block;
+
+ if (state->out_block_size < out_block_size) {
+ free(state->out_block);
+ out_block = (unsigned char *)malloc(out_block_size);
+ state->out_block_size = out_block_size;
+ if (out_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lz4 decompression");
+ return (ARCHIVE_FATAL);
+ }
+ state->out_block = out_block;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+lz4_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ ssize_t ret;
+
+ if (state->eof) {
+ *p = NULL;
+ return (0);
+ }
+
+ __archive_read_filter_consume(self->upstream, state->unconsumed);
+ state->unconsumed = 0;
+
+ switch (state->stage) {
+ case SELECT_STREAM:
+ break;
+ case READ_DEFAULT_STREAM:
+ case READ_LEGACY_STREAM:
+ /* Reading a lz4 stream already failed. */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Invalid sequence.");
+ return (ARCHIVE_FATAL);
+ case READ_DEFAULT_BLOCK:
+ ret = lz4_filter_read_default_stream(self, p);
+ if (ret != 0 || state->stage != SELECT_STREAM)
+ return ret;
+ break;
+ case READ_LEGACY_BLOCK:
+ ret = lz4_filter_read_legacy_stream(self, p);
+ if (ret != 0 || state->stage != SELECT_STREAM)
+ return ret;
+ break;
+ default:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Program error.");
+ return (ARCHIVE_FATAL);
+ break;
+ }
+
+ while (state->stage == SELECT_STREAM) {
+ const char *read_buf;
+
+ /* Read a magic number. */
+ read_buf = __archive_read_filter_ahead(self->upstream, 4,
+ NULL);
+ if (read_buf == NULL) {
+ state->eof = 1;
+ *p = NULL;
+ return (0);
+ }
+ uint32_t number = archive_le32dec(read_buf);
+ __archive_read_filter_consume(self->upstream, 4);
+ if (number == LZ4_MAGICNUMBER)
+ return lz4_filter_read_default_stream(self, p);
+ else if (number == LZ4_LEGACY)
+ return lz4_filter_read_legacy_stream(self, p);
+ else if ((number & ~0xF) == LZ4_SKIPPABLED) {
+ read_buf = __archive_read_filter_ahead(
+ self->upstream, 4, NULL);
+ if (read_buf == NULL) {
+ archive_set_error(
+ &self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Malformed lz4 data");
+ return (ARCHIVE_FATAL);
+ }
+ uint32_t skip_bytes = archive_le32dec(read_buf);
+ __archive_read_filter_consume(self->upstream,
+ 4 + skip_bytes);
+ } else {
+ /* Ignore following unrecognized data. */
+ state->eof = 1;
+ *p = NULL;
+ return (0);
+ }
+ }
+ state->eof = 1;
+ *p = NULL;
+ return (0);
+}
+
+static int
+lz4_filter_read_descriptor(struct archive_read_filter *self)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ const char *read_buf;
+ ssize_t bytes_remaining;
+ ssize_t descriptor_bytes;
+ unsigned char flag, bd;
+ unsigned int chsum, chsum_verifier;
+
+ /* Make sure we have 2 bytes for flags. */
+ read_buf = __archive_read_filter_ahead(self->upstream, 2,
+ &bytes_remaining);
+ if (read_buf == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ Parse flags.
+ */
+ flag = (unsigned char)read_buf[0];
+ /* Verify version number. */
+ if ((flag & 0xc0) != 1<<6)
+ goto malformed_error;
+ /* A reserved bit must be zero. */
+ if (flag & 0x02)
+ goto malformed_error;
+ state->flags.block_independence = (flag & 0x20) != 0;
+ state->flags.block_checksum = (flag & 0x10)?4:0;
+ state->flags.stream_size = (flag & 0x08) != 0;
+ state->flags.stream_checksum = (flag & 0x04) != 0;
+ state->flags.preset_dictionary = (flag & 0x01) != 0;
+
+ /* BD */
+ bd = (unsigned char)read_buf[1];
+ /* Reserved bits must be zero. */
+ if (bd & 0x8f)
+ goto malformed_error;
+ /* Get a maximum block size. */
+ switch (read_buf[1] >> 4) {
+ case 4: /* 64 KB */
+ state->flags.block_maximum_size = 64 * 1024;
+ break;
+ case 5: /* 256 KB */
+ state->flags.block_maximum_size = 256 * 1024;
+ break;
+ case 6: /* 1 MB */
+ state->flags.block_maximum_size = 1024 * 1024;
+ break;
+ case 7: /* 4 MB */
+ state->flags.block_maximum_size = 4 * 1024 * 1024;
+ break;
+ default:
+ goto malformed_error;
+ }
+
+ /* Read the whole descriptor in a stream block. */
+ descriptor_bytes = 3;
+ if (state->flags.stream_size)
+ descriptor_bytes += 8;
+ if (state->flags.preset_dictionary)
+ descriptor_bytes += 4;
+ if (bytes_remaining < descriptor_bytes) {
+ read_buf = __archive_read_filter_ahead(self->upstream,
+ descriptor_bytes, &bytes_remaining);
+ if (read_buf == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ /* Check if a descriptor is corrupted */
+ chsum = __archive_xxhash.XXH32(read_buf, (int)descriptor_bytes -1, 0);
+ chsum = (chsum >> 8) & 0xff;
+ chsum_verifier = read_buf[descriptor_bytes-1] & 0xff;
+ if (chsum != chsum_verifier)
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ goto malformed_error;
+#endif
+
+ __archive_read_filter_consume(self->upstream, descriptor_bytes);
+
+ /* Make sure we have a large enough buffer for uncompressed data. */
+ if (lz4_allocate_out_block(self) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (state->flags.stream_checksum)
+ state->xxh32_state = __archive_xxhash.XXH32_init(0);
+
+ state->decoded_size = 0;
+ /* Success */
+ return (ARCHIVE_OK);
+malformed_error:
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "malformed lz4 data");
+ return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ ssize_t compressed_size;
+ const char *read_buf;
+ ssize_t bytes_remaining;
+ int checksum_size;
+ ssize_t uncompressed_size;
+ size_t prefix64k;
+
+ *p = NULL;
+
+ /* Make sure we have 4 bytes for a block size. */
+ read_buf = __archive_read_filter_ahead(self->upstream, 4,
+ &bytes_remaining);
+ if (read_buf == NULL)
+ goto truncated_error;
+ compressed_size = archive_le32dec(read_buf);
+ if ((compressed_size & 0x7fffffff) > state->flags.block_maximum_size)
+ goto malformed_error;
+ /* A compressed size == 0 means the end of stream blocks. */
+ if (compressed_size == 0) {
+ __archive_read_filter_consume(self->upstream, 4);
+ return 0;
+ }
+
+ checksum_size = state->flags.block_checksum;
+ /* Check if the block is uncompressed. */
+ if (compressed_size & 0x80000000U) {
+ compressed_size &= 0x7fffffff;
+ uncompressed_size = compressed_size;
+ } else
+ uncompressed_size = 0;/* Unknown yet. */
+
+ /*
+ Unfortunately, lz4 decompression API requires a whole block
+ for its decompression speed, so we read a whole block and allocate
+ a huge buffer used for decoded data.
+ */
+ read_buf = __archive_read_filter_ahead(self->upstream,
+ 4 + compressed_size + checksum_size, &bytes_remaining);
+ if (read_buf == NULL)
+ goto truncated_error;
+
+ /* Optional processing, checking a block sum. */
+ if (checksum_size) {
+ unsigned int chsum = __archive_xxhash.XXH32(
+ read_buf + 4, (int)compressed_size, 0);
+ unsigned int chsum_block =
+ archive_le32dec(read_buf + 4 + compressed_size);
+ if (chsum != chsum_block)
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ goto malformed_error;
+#endif
+ }
+
+
+ /* If the block is uncompressed, there is nothing to do. */
+ if (uncompressed_size) {
+ /* Prepare a prefix 64k block for next block. */
+ if (!state->flags.block_independence) {
+ prefix64k = 64 * 1024;
+ if (uncompressed_size < (ssize_t)prefix64k) {
+ memcpy(state->out_block
+ + prefix64k - uncompressed_size,
+ read_buf + 4,
+ uncompressed_size);
+ memset(state->out_block, 0,
+ prefix64k - uncompressed_size);
+ } else {
+ memcpy(state->out_block,
+ read_buf + 4
+ + uncompressed_size - prefix64k,
+ prefix64k);
+ }
+ state->decoded_size = 0;
+ }
+ state->unconsumed = 4 + uncompressed_size + checksum_size;
+ *p = read_buf + 4;
+ return uncompressed_size;
+ }
+
+ /*
+ Decompress a block data.
+ */
+ if (state->flags.block_independence) {
+ prefix64k = 0;
+ uncompressed_size = LZ4_decompress_safe(read_buf + 4,
+ state->out_block, (int)compressed_size,
+ state->flags.block_maximum_size);
+ } else {
+ prefix64k = 64 * 1024;
+ if (state->decoded_size) {
+ if (state->decoded_size < prefix64k) {
+ memmove(state->out_block
+ + prefix64k - state->decoded_size,
+ state->out_block + prefix64k,
+ state->decoded_size);
+ memset(state->out_block, 0,
+ prefix64k - state->decoded_size);
+ } else {
+ memmove(state->out_block,
+ state->out_block + state->decoded_size,
+ prefix64k);
+ }
+ }
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ uncompressed_size = LZ4_decompress_safe_usingDict(
+ read_buf + 4,
+ state->out_block + prefix64k, (int)compressed_size,
+ state->flags.block_maximum_size,
+ state->out_block,
+ (int)prefix64k);
+#else
+ uncompressed_size = LZ4_decompress_safe_withPrefix64k(
+ read_buf + 4,
+ state->out_block + prefix64k, (int)compressed_size,
+ state->flags.block_maximum_size);
+#endif
+ }
+
+ /* Check if an error occurred in the decompression process. */
+ if (uncompressed_size < 0) {
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+
+ state->unconsumed = 4 + compressed_size + checksum_size;
+ *p = state->out_block + prefix64k;
+ state->decoded_size = uncompressed_size;
+ return uncompressed_size;
+
+malformed_error:
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "malformed lz4 data");
+ return (ARCHIVE_FATAL);
+truncated_error:
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ const char *read_buf;
+ ssize_t bytes_remaining;
+ ssize_t ret;
+
+ if (state->stage == SELECT_STREAM) {
+ state->stage = READ_DEFAULT_STREAM;
+ /* First, read a descriptor. */
+ if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK)
+ return (ret);
+ state->stage = READ_DEFAULT_BLOCK;
+ }
+ /* Decompress a block. */
+ ret = lz4_filter_read_data_block(self, p);
+
+ /* If the end of block is detected, change the filter status
+ to read next stream. */
+ if (ret == 0 && *p == NULL)
+ state->stage = SELECT_STREAM;
+
+ /* Optional processing, checking a stream sum. */
+ if (state->flags.stream_checksum) {
+ if (state->stage == SELECT_STREAM) {
+ unsigned int checksum;
+ unsigned int checksum_stream;
+ read_buf = __archive_read_filter_ahead(self->upstream,
+ 4, &bytes_remaining);
+ if (read_buf == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+ checksum = archive_le32dec(read_buf);
+ __archive_read_filter_consume(self->upstream, 4);
+ checksum_stream = __archive_xxhash.XXH32_digest(
+ state->xxh32_state);
+ state->xxh32_state = NULL;
+ if (checksum != checksum_stream) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "lz4 stream checksum error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ } else if (ret > 0)
+ __archive_xxhash.XXH32_update(state->xxh32_state,
+ *p, (int)ret);
+ }
+ return (ret);
+}
+
+static ssize_t
+lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state = (struct private_data *)self->data;
+ uint32_t compressed;
+ const char *read_buf;
+ ssize_t ret;
+
+ *p = NULL;
+ ret = lz4_allocate_out_block_for_legacy(self);
+ if (ret != ARCHIVE_OK)
+ return ret;
+
+ /* Make sure we have 4 bytes for a block size. */
+ read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (read_buf == NULL) {
+ if (state->stage == SELECT_STREAM) {
+ state->stage = READ_LEGACY_STREAM;
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+ state->stage = SELECT_STREAM;
+ return 0;
+ }
+ state->stage = READ_LEGACY_BLOCK;
+ compressed = archive_le32dec(read_buf);
+ if (compressed > LZ4_COMPRESSBOUND(LEGACY_BLOCK_SIZE)) {
+ state->stage = SELECT_STREAM;
+ return 0;
+ }
+
+ /* Make sure we have a whole block. */
+ read_buf = __archive_read_filter_ahead(self->upstream,
+ 4 + compressed, NULL);
+ if (read_buf == NULL) {
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC, "truncated lz4 input");
+ return (ARCHIVE_FATAL);
+ }
+ ret = LZ4_decompress_safe(read_buf + 4, state->out_block,
+ compressed, (int)state->out_block_size);
+ if (ret < 0) {
+ archive_set_error(&(self->archive->archive),
+ ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+ *p = state->out_block;
+ state->unconsumed = 4 + compressed;
+ return ret;
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+lz4_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ int ret = ARCHIVE_OK;
+
+ state = (struct private_data *)self->data;
+ free(state->xxh32_state);
+ free(state->out_block);
+ free(state);
+ return (ret);
+}
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_lzop.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lzop.c
new file mode 100644
index 0000000000..54e6e198c9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_lzop.c
@@ -0,0 +1,499 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_LZO_LZOCONF_H
+#error #include <lzo/lzoconf.h>
+#endif
+#ifdef HAVE_LZO_LZO1X_H
+#error #include <lzo/lzo1x.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* for crc32 and adler32 */
+#endif
+
+#include "archive.h"
+#if !defined(HAVE_ZLIB_H) &&\
+ defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+#error #include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#ifndef HAVE_ZLIB_H
+#define adler32 lzo_adler32
+#endif
+
+#define LZOP_HEADER_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
+#define LZOP_HEADER_MAGIC_LEN 9
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+struct read_lzop {
+ unsigned char *out_block;
+ size_t out_block_size;
+ int64_t total_out;
+ int flags;
+ uint32_t compressed_cksum;
+ uint32_t uncompressed_cksum;
+ size_t compressed_size;
+ size_t uncompressed_size;
+ size_t unconsumed_bytes;
+ char in_stream;
+ char eof; /* True = found end of compressed data. */
+};
+
+#define FILTER 0x0800
+#define CRC32_HEADER 0x1000
+#define EXTRA_FIELD 0x0040
+#define ADLER32_UNCOMPRESSED 0x0001
+#define ADLER32_COMPRESSED 0x0002
+#define CRC32_UNCOMPRESSED 0x0100
+#define CRC32_COMPRESSED 0x0200
+#define MAX_BLOCK_SIZE (64 * 1024 * 1024)
+
+static ssize_t lzop_filter_read(struct archive_read_filter *, const void **);
+static int lzop_filter_close(struct archive_read_filter *);
+#endif
+
+static int lzop_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int lzop_bidder_init(struct archive_read_filter *);
+
+static const struct archive_read_filter_bidder_vtable
+lzop_bidder_vtable = {
+ .bid = lzop_bidder_bid,
+ .init = lzop_bidder_init,
+};
+
+int
+archive_read_support_filter_lzop(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, NULL,
+ &lzop_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Signal the extent of lzop support with the return value here. */
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+ return (ARCHIVE_OK);
+#else
+ /* Return ARCHIVE_WARN since this always uses an external program. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lzop program for lzop decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+lzop_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *p;
+ ssize_t avail;
+
+ (void)self; /* UNUSED */
+
+ p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, &avail);
+ if (p == NULL || avail == 0)
+ return (0);
+
+ if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))
+ return (0);
+
+ return (LZOP_HEADER_MAGIC_LEN * 8);
+}
+
+#if !defined(HAVE_LZO_LZOCONF_H) || !defined(HAVE_LZO_LZO1X_H)
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly. We can, however, try to run "lzop -d"
+ * in case that's available.
+ */
+static int
+lzop_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lzop -d");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LZOP;
+ self->name = "lzop";
+ return (r);
+}
+#else
+
+static const struct archive_read_filter_vtable
+lzop_reader_vtable = {
+ .read = lzop_filter_read,
+ .close = lzop_filter_close
+};
+
+/*
+ * Initialize the filter object.
+ */
+static int
+lzop_bidder_init(struct archive_read_filter *self)
+{
+ struct read_lzop *state;
+
+ self->code = ARCHIVE_FILTER_LZOP;
+ self->name = "lzop";
+
+ state = (struct read_lzop *)calloc(sizeof(*state), 1);
+ if (state == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lzop decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ self->vtable = &lzop_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+consume_header(struct archive_read_filter *self)
+{
+ struct read_lzop *state = (struct read_lzop *)self->data;
+ const unsigned char *p, *_p;
+ unsigned checksum, flags, len, method, version;
+
+ /*
+ * Check LZOP magic code.
+ */
+ p = __archive_read_filter_ahead(self->upstream,
+ LZOP_HEADER_MAGIC_LEN, NULL);
+ if (p == NULL)
+ return (ARCHIVE_EOF);
+
+ if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))
+ return (ARCHIVE_EOF);
+ __archive_read_filter_consume(self->upstream,
+ LZOP_HEADER_MAGIC_LEN);
+
+ p = __archive_read_filter_ahead(self->upstream, 29, NULL);
+ if (p == NULL)
+ goto truncated;
+ _p = p;
+ version = archive_be16dec(p);
+ p += 4;/* version(2 bytes) + library version(2 bytes) */
+
+ if (version >= 0x940) {
+ unsigned reqversion = archive_be16dec(p); p += 2;
+ if (reqversion < 0x900) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Invalid required version");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ method = *p++;
+ if (method < 1 || method > 3) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Unsupported method");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (version >= 0x940) {
+ unsigned level = *p++;
+#if 0
+ unsigned default_level[] = {0, 3, 1, 9};
+#endif
+ if (level == 0)
+ /* Method is 1..3 here due to check above. */
+#if 0 /* Avoid an error Clang Static Analyzer claims
+ "Value stored to 'level' is never read". */
+ level = default_level[method];
+#else
+ ;/* NOP */
+#endif
+ else if (level > 9) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Invalid level");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ flags = archive_be32dec(p); p += 4;
+
+ if (flags & FILTER)
+ p += 4; /* Skip filter */
+ p += 4; /* Skip mode */
+ if (version >= 0x940)
+ p += 8; /* Skip mtime */
+ else
+ p += 4; /* Skip mtime */
+ len = *p++; /* Read filename length */
+ len += p - _p;
+ /* Make sure we have all bytes we need to calculate checksum. */
+ p = __archive_read_filter_ahead(self->upstream, len + 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ if (flags & CRC32_HEADER)
+ checksum = crc32(crc32(0, NULL, 0), p, len);
+ else
+ checksum = adler32(adler32(0, NULL, 0), p, len);
+ if (archive_be32dec(p + len) != checksum)
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ goto corrupted;
+#endif
+ __archive_read_filter_consume(self->upstream, len + 4);
+ if (flags & EXTRA_FIELD) {
+ /* Skip extra field */
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ len = archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, len + 4 + 4);
+ }
+ state->flags = flags;
+ state->in_stream = 1;
+ return (ARCHIVE_OK);
+truncated:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+ return (ARCHIVE_FAILED);
+corrupted:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
+ return (ARCHIVE_FAILED);
+}
+
+static int
+consume_block_info(struct archive_read_filter *self)
+{
+ struct read_lzop *state = (struct read_lzop *)self->data;
+ const unsigned char *p;
+ unsigned flags = state->flags;
+
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ state->uncompressed_size = archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, 4);
+ if (state->uncompressed_size == 0)
+ return (ARCHIVE_EOF);
+ if (state->uncompressed_size > MAX_BLOCK_SIZE)
+ goto corrupted;
+
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ state->compressed_size = archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, 4);
+ if (state->compressed_size > state->uncompressed_size)
+ goto corrupted;
+
+ if (flags & (CRC32_UNCOMPRESSED | ADLER32_UNCOMPRESSED)) {
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ state->compressed_cksum = state->uncompressed_cksum =
+ archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, 4);
+ }
+ if ((flags & (CRC32_COMPRESSED | ADLER32_COMPRESSED)) &&
+ state->compressed_size < state->uncompressed_size) {
+ p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ state->compressed_cksum = archive_be32dec(p);
+ __archive_read_filter_consume(self->upstream, 4);
+ }
+ return (ARCHIVE_OK);
+truncated:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+ return (ARCHIVE_FAILED);
+corrupted:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
+ return (ARCHIVE_FAILED);
+}
+
+static ssize_t
+lzop_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct read_lzop *state = (struct read_lzop *)self->data;
+ const void *b;
+ lzo_uint out_size;
+ uint32_t cksum;
+ int ret, r;
+
+ if (state->unconsumed_bytes) {
+ __archive_read_filter_consume(self->upstream,
+ state->unconsumed_bytes);
+ state->unconsumed_bytes = 0;
+ }
+ if (state->eof)
+ return (0);
+
+ for (;;) {
+ if (!state->in_stream) {
+ ret = consume_header(self);
+ if (ret < ARCHIVE_OK)
+ return (ret);
+ if (ret == ARCHIVE_EOF) {
+ state->eof = 1;
+ return (0);
+ }
+ }
+ ret = consume_block_info(self);
+ if (ret < ARCHIVE_OK)
+ return (ret);
+ if (ret == ARCHIVE_EOF)
+ state->in_stream = 0;
+ else
+ break;
+ }
+
+ if (state->out_block == NULL ||
+ state->out_block_size < state->uncompressed_size) {
+ void *new_block;
+
+ new_block = realloc(state->out_block, state->uncompressed_size);
+ if (new_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for lzop decompression");
+ return (ARCHIVE_FATAL);
+ }
+ state->out_block = new_block;
+ state->out_block_size = state->uncompressed_size;
+ }
+
+ b = __archive_read_filter_ahead(self->upstream,
+ state->compressed_size, NULL);
+ if (b == NULL) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+ return (ARCHIVE_FATAL);
+ }
+ if (state->flags & CRC32_COMPRESSED)
+ cksum = crc32(crc32(0, NULL, 0), b, state->compressed_size);
+ else if (state->flags & ADLER32_COMPRESSED)
+ cksum = adler32(adler32(0, NULL, 0), b, state->compressed_size);
+ else
+ cksum = state->compressed_cksum;
+ if (cksum != state->compressed_cksum) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Corrupted data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * If the both uncompressed size and compressed size are the same,
+ * we do not decompress this block.
+ */
+ if (state->uncompressed_size == state->compressed_size) {
+ *p = b;
+ state->total_out += state->compressed_size;
+ state->unconsumed_bytes = state->compressed_size;
+ return ((ssize_t)state->uncompressed_size);
+ }
+
+ /*
+ * Drive lzo uncompression.
+ */
+ out_size = (lzo_uint)state->uncompressed_size;
+ r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size,
+ state->out_block, &out_size, NULL);
+ switch (r) {
+ case LZO_E_OK:
+ if (out_size == state->uncompressed_size)
+ break;
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Corrupted data");
+ return (ARCHIVE_FATAL);
+ case LZO_E_OUT_OF_MEMORY:
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "lzop decompression failed: out of memory");
+ return (ARCHIVE_FATAL);
+ default:
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "lzop decompression failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ if (state->flags & CRC32_UNCOMPRESSED)
+ cksum = crc32(crc32(0, NULL, 0), state->out_block,
+ state->uncompressed_size);
+ else if (state->flags & ADLER32_UNCOMPRESSED)
+ cksum = adler32(adler32(0, NULL, 0), state->out_block,
+ state->uncompressed_size);
+ else
+ cksum = state->uncompressed_cksum;
+ if (cksum != state->uncompressed_cksum) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC, "Corrupted data");
+ return (ARCHIVE_FATAL);
+ }
+
+ __archive_read_filter_consume(self->upstream, state->compressed_size);
+ *p = state->out_block;
+ state->total_out += out_size;
+ return ((ssize_t)out_size);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+lzop_filter_close(struct archive_read_filter *self)
+{
+ struct read_lzop *state = (struct read_lzop *)self->data;
+
+ free(state->out_block);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_none.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_none.c
new file mode 100644
index 0000000000..95e5cfdb15
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_none.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_none(struct archive *a)
+{
+ return archive_read_support_filter_none(a);
+}
+#endif
+
+/*
+ * Uncompressed streams are handled implicitly by the read core,
+ * so this is now a no-op.
+ */
+int
+archive_read_support_filter_none(struct archive *a)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_filter_none");
+
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_program.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_program.c
new file mode 100644
index 0000000000..885b2c2056
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_program.c
@@ -0,0 +1,496 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_read_private.h"
+#include "filter_fork.h"
+
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_program(struct archive *a, const char *cmd)
+{
+ return archive_read_support_filter_program(a, cmd);
+}
+
+int
+archive_read_support_compression_program_signature(struct archive *a,
+ const char *cmd, const void *signature, size_t signature_len)
+{
+ return archive_read_support_filter_program_signature(a,
+ cmd, signature, signature_len);
+}
+#endif
+
+int
+archive_read_support_filter_program(struct archive *a, const char *cmd)
+{
+ return (archive_read_support_filter_program_signature(a, cmd, NULL, 0));
+}
+
+/*
+ * The bidder object stores the command and the signature to watch for.
+ * The 'inhibit' entry here is used to ensure that unchecked filters never
+ * bid twice in the same pipeline.
+ */
+struct program_bidder {
+ char *description;
+ char *cmd;
+ void *signature;
+ size_t signature_len;
+ int inhibit;
+};
+
+static int program_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *upstream);
+static int program_bidder_init(struct archive_read_filter *);
+static void program_bidder_free(struct archive_read_filter_bidder *);
+
+/*
+ * The actual filter needs to track input and output data.
+ */
+struct program_filter {
+ struct archive_string description;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE child;
+#else
+ pid_t child;
+#endif
+ int exit_status;
+ int waitpid_return;
+ int child_stdin, child_stdout;
+
+ char *out_buf;
+ size_t out_buf_len;
+};
+
+static ssize_t program_filter_read(struct archive_read_filter *,
+ const void **);
+static int program_filter_close(struct archive_read_filter *);
+static void free_state(struct program_bidder *);
+
+static const struct archive_read_filter_bidder_vtable
+program_bidder_vtable = {
+ .bid = program_bidder_bid,
+ .init = program_bidder_init,
+ .free = program_bidder_free,
+};
+
+int
+archive_read_support_filter_program_signature(struct archive *_a,
+ const char *cmd, const void *signature, size_t signature_len)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct program_bidder *state;
+
+ /*
+ * Allocate our private state.
+ */
+ state = (struct program_bidder *)calloc(1, sizeof (*state));
+ if (state == NULL)
+ goto memerr;
+ state->cmd = strdup(cmd);
+ if (state->cmd == NULL)
+ goto memerr;
+
+ if (signature != NULL && signature_len > 0) {
+ state->signature_len = signature_len;
+ state->signature = malloc(signature_len);
+ memcpy(state->signature, signature, signature_len);
+ }
+
+ if (__archive_read_register_bidder(a, state, NULL,
+ &program_bidder_vtable) != ARCHIVE_OK) {
+ free_state(state);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+
+memerr:
+ free_state(state);
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+}
+
+static void
+program_bidder_free(struct archive_read_filter_bidder *self)
+{
+ struct program_bidder *state = (struct program_bidder *)self->data;
+
+ free_state(state);
+}
+
+static void
+free_state(struct program_bidder *state)
+{
+
+ if (state) {
+ free(state->cmd);
+ free(state->signature);
+ free(state);
+ }
+}
+
+/*
+ * If we do have a signature, bid only if that matches.
+ *
+ * If there's no signature, we bid INT_MAX the first time
+ * we're called, then never bid again.
+ */
+static int
+program_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *upstream)
+{
+ struct program_bidder *state = self->data;
+ const char *p;
+
+ /* If we have a signature, use that to match. */
+ if (state->signature_len > 0) {
+ p = __archive_read_filter_ahead(upstream,
+ state->signature_len, NULL);
+ if (p == NULL)
+ return (0);
+ /* No match, so don't bid. */
+ if (memcmp(p, state->signature, state->signature_len) != 0)
+ return (0);
+ return ((int)state->signature_len * 8);
+ }
+
+ /* Otherwise, bid once and then never bid again. */
+ if (state->inhibit)
+ return (0);
+ state->inhibit = 1;
+ return (INT_MAX);
+}
+
+/*
+ * Shut down the child, return ARCHIVE_OK if it exited normally.
+ *
+ * Note that the return value is sticky; if we're called again,
+ * we won't reap the child again, but we will return the same status
+ * (including error message if the child came to a bad end).
+ */
+static int
+child_stop(struct archive_read_filter *self, struct program_filter *state)
+{
+ /* Close our side of the I/O with the child. */
+ if (state->child_stdin != -1) {
+ close(state->child_stdin);
+ state->child_stdin = -1;
+ }
+ if (state->child_stdout != -1) {
+ close(state->child_stdout);
+ state->child_stdout = -1;
+ }
+
+ if (state->child != 0) {
+ /* Reap the child. */
+ do {
+ state->waitpid_return
+ = waitpid(state->child, &state->exit_status, 0);
+ } while (state->waitpid_return == -1 && errno == EINTR);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ CloseHandle(state->child);
+#endif
+ state->child = 0;
+ }
+
+ if (state->waitpid_return < 0) {
+ /* waitpid() failed? This is ugly. */
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Child process exited badly");
+ return (ARCHIVE_WARN);
+ }
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ if (WIFSIGNALED(state->exit_status)) {
+#ifdef SIGPIPE
+ /* If the child died because we stopped reading before
+ * it was done, that's okay. Some archive formats
+ * have padding at the end that we routinely ignore. */
+ /* The alternative to this would be to add a step
+ * before close(child_stdout) above to read from the
+ * child until the child has no more to write. */
+ if (WTERMSIG(state->exit_status) == SIGPIPE)
+ return (ARCHIVE_OK);
+#endif
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Child process exited with signal %d",
+ WTERMSIG(state->exit_status));
+ return (ARCHIVE_WARN);
+ }
+#endif /* !_WIN32 || __CYGWIN__ */
+
+ if (WIFEXITED(state->exit_status)) {
+ if (WEXITSTATUS(state->exit_status) == 0)
+ return (ARCHIVE_OK);
+
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Child process exited with status %d",
+ WEXITSTATUS(state->exit_status));
+ return (ARCHIVE_WARN);
+ }
+
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Use select() to decide whether the child is ready for read or write.
+ */
+static ssize_t
+child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
+{
+ struct program_filter *state = self->data;
+ ssize_t ret, requested, avail;
+ const char *p;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE handle = (HANDLE)_get_osfhandle(state->child_stdout);
+#endif
+
+ requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
+
+ for (;;) {
+ do {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Avoid infinity wait.
+ * Note: If there is no data in the pipe, ReadFile()
+ * called in read() never returns and so we won't
+ * write remaining encoded data to the pipe.
+ * Note: This way may cause performance problem.
+ * we are looking forward to great code to resolve
+ * this. */
+ DWORD pipe_avail = -1;
+ int cnt = 2;
+
+ while (PeekNamedPipe(handle, NULL, 0, NULL,
+ &pipe_avail, NULL) != 0 && pipe_avail == 0 &&
+ cnt--)
+ Sleep(5);
+ if (pipe_avail == 0) {
+ ret = -1;
+ errno = EAGAIN;
+ break;
+ }
+#endif
+ ret = read(state->child_stdout, buf, requested);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret > 0)
+ return (ret);
+ if (ret == 0 || (ret == -1 && errno == EPIPE))
+ /* Child has closed its output; reap the child
+ * and return the status. */
+ return (child_stop(self, state));
+ if (ret == -1 && errno != EAGAIN)
+ return (-1);
+
+ if (state->child_stdin == -1) {
+ /* Block until child has some I/O ready. */
+ __archive_check_child(state->child_stdin,
+ state->child_stdout);
+ continue;
+ }
+
+ /* Get some more data from upstream. */
+ p = __archive_read_filter_ahead(self->upstream, 1, &avail);
+ if (p == NULL) {
+ close(state->child_stdin);
+ state->child_stdin = -1;
+ fcntl(state->child_stdout, F_SETFL, 0);
+ if (avail < 0)
+ return (avail);
+ continue;
+ }
+
+ do {
+ ret = write(state->child_stdin, p, avail);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret > 0) {
+ /* Consume whatever we managed to write. */
+ __archive_read_filter_consume(self->upstream, ret);
+ } else if (ret == -1 && errno == EAGAIN) {
+ /* Block until child has some I/O ready. */
+ __archive_check_child(state->child_stdin,
+ state->child_stdout);
+ } else {
+ /* Write failed. */
+ close(state->child_stdin);
+ state->child_stdin = -1;
+ fcntl(state->child_stdout, F_SETFL, 0);
+ /* If it was a bad error, we're done; otherwise
+ * it was EPIPE or EOF, and we can still read
+ * from the child. */
+ if (ret == -1 && errno != EPIPE)
+ return (-1);
+ }
+ }
+}
+
+static const struct archive_read_filter_vtable
+program_reader_vtable = {
+ .read = program_filter_read,
+ .close = program_filter_close,
+};
+
+int
+__archive_read_program(struct archive_read_filter *self, const char *cmd)
+{
+ struct program_filter *state;
+ static const size_t out_buf_len = 65536;
+ char *out_buf;
+ const char *prefix = "Program: ";
+ int ret;
+ size_t l;
+
+ l = strlen(prefix) + strlen(cmd) + 1;
+ state = (struct program_filter *)calloc(1, sizeof(*state));
+ out_buf = (char *)malloc(out_buf_len);
+ if (state == NULL || out_buf == NULL ||
+ archive_string_ensure(&state->description, l) == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate input data");
+ if (state != NULL) {
+ archive_string_free(&state->description);
+ free(state);
+ }
+ free(out_buf);
+ return (ARCHIVE_FATAL);
+ }
+ archive_strcpy(&state->description, prefix);
+ archive_strcat(&state->description, cmd);
+
+ self->code = ARCHIVE_FILTER_PROGRAM;
+ self->name = state->description.s;
+
+ state->out_buf = out_buf;
+ state->out_buf_len = out_buf_len;
+
+ ret = __archive_create_child(cmd, &state->child_stdin,
+ &state->child_stdout, &state->child);
+ if (ret != ARCHIVE_OK) {
+ free(state->out_buf);
+ archive_string_free(&state->description);
+ free(state);
+ archive_set_error(&self->archive->archive, EINVAL,
+ "Can't initialize filter; unable to run program \"%s\"",
+ cmd);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ self->vtable = &program_reader_vtable;
+
+ /* XXX Check that we can read at least one byte? */
+ return (ARCHIVE_OK);
+}
+
+static int
+program_bidder_init(struct archive_read_filter *self)
+{
+ struct program_bidder *bidder_state;
+
+ bidder_state = (struct program_bidder *)self->bidder->data;
+ return (__archive_read_program(self, bidder_state->cmd));
+}
+
+static ssize_t
+program_filter_read(struct archive_read_filter *self, const void **buff)
+{
+ struct program_filter *state;
+ ssize_t bytes;
+ size_t total;
+ char *p;
+
+ state = (struct program_filter *)self->data;
+
+ total = 0;
+ p = state->out_buf;
+ while (state->child_stdout != -1 && total < state->out_buf_len) {
+ bytes = child_read(self, p, state->out_buf_len - total);
+ if (bytes < 0)
+ /* No recovery is possible if we can no longer
+ * read from the child. */
+ return (ARCHIVE_FATAL);
+ if (bytes == 0)
+ /* We got EOF from the child. */
+ break;
+ total += bytes;
+ p += bytes;
+ }
+
+ *buff = state->out_buf;
+ return (total);
+}
+
+static int
+program_filter_close(struct archive_read_filter *self)
+{
+ struct program_filter *state;
+ int e;
+
+ state = (struct program_filter *)self->data;
+ e = child_stop(self, state);
+
+ /* Release our private data. */
+ free(state->out_buf);
+ archive_string_free(&state->description);
+ free(state);
+
+ return (e);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_rpm.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_rpm.c
new file mode 100644
index 0000000000..67a979cd78
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_rpm.c
@@ -0,0 +1,287 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct rpm {
+ int64_t total_in;
+ size_t hpos;
+ size_t hlen;
+ unsigned char header[16];
+ enum {
+ ST_LEAD, /* Skipping 'Lead' section. */
+ ST_HEADER, /* Reading 'Header' section;
+ * first 16 bytes. */
+ ST_HEADER_DATA, /* Skipping 'Header' section. */
+ ST_PADDING, /* Skipping padding data after the
+ * 'Header' section. */
+ ST_ARCHIVE /* Reading 'Archive' section. */
+ } state;
+ int first_header;
+};
+#define RPM_LEAD_SIZE 96 /* Size of 'Lead' section. */
+
+static int rpm_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int rpm_bidder_init(struct archive_read_filter *);
+
+static ssize_t rpm_filter_read(struct archive_read_filter *,
+ const void **);
+static int rpm_filter_close(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_rpm(struct archive *a)
+{
+ return archive_read_support_filter_rpm(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+rpm_bidder_vtable = {
+ .bid = rpm_bidder_bid,
+ .init = rpm_bidder_init,
+};
+
+int
+archive_read_support_filter_rpm(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ return __archive_read_register_bidder(a, NULL, "rpm",
+ &rpm_bidder_vtable);
+}
+
+static int
+rpm_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *b;
+ ssize_t avail;
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ b = __archive_read_filter_ahead(filter, 8, &avail);
+ if (b == NULL)
+ return (0);
+
+ bits_checked = 0;
+ /*
+ * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB
+ */
+ if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)
+ return (0);
+ bits_checked += 32;
+ /*
+ * Check major version.
+ */
+ if (b[4] != 3 && b[4] != 4)
+ return (0);
+ bits_checked += 8;
+ /*
+ * Check package type; binary or source.
+ */
+ if (b[6] != 0)
+ return (0);
+ bits_checked += 8;
+ if (b[7] != 0 && b[7] != 1)
+ return (0);
+ bits_checked += 8;
+
+ return (bits_checked);
+}
+
+static const struct archive_read_filter_vtable
+rpm_reader_vtable = {
+ .read = rpm_filter_read,
+ .close = rpm_filter_close,
+};
+
+static int
+rpm_bidder_init(struct archive_read_filter *self)
+{
+ struct rpm *rpm;
+
+ self->code = ARCHIVE_FILTER_RPM;
+ self->name = "rpm";
+
+ rpm = (struct rpm *)calloc(sizeof(*rpm), 1);
+ if (rpm == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for rpm");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = rpm;
+ rpm->state = ST_LEAD;
+ self->vtable = &rpm_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+rpm_filter_read(struct archive_read_filter *self, const void **buff)
+{
+ struct rpm *rpm;
+ const unsigned char *b;
+ ssize_t avail_in, total;
+ size_t used, n;
+ uint32_t section;
+ uint32_t bytes;
+
+ rpm = (struct rpm *)self->data;
+ *buff = NULL;
+ total = avail_in = 0;
+ b = NULL;
+ used = 0;
+ do {
+ if (b == NULL) {
+ b = __archive_read_filter_ahead(self->upstream, 1,
+ &avail_in);
+ if (b == NULL) {
+ if (avail_in < 0)
+ return (ARCHIVE_FATAL);
+ else
+ break;
+ }
+ }
+
+ switch (rpm->state) {
+ case ST_LEAD:
+ if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
+ used += avail_in;
+ else {
+ n = (size_t)(RPM_LEAD_SIZE - rpm->total_in);
+ used += n;
+ b += n;
+ rpm->state = ST_HEADER;
+ rpm->hpos = 0;
+ rpm->hlen = 0;
+ rpm->first_header = 1;
+ }
+ break;
+ case ST_HEADER:
+ n = 16 - rpm->hpos;
+ if (n > avail_in - used)
+ n = avail_in - used;
+ memcpy(rpm->header+rpm->hpos, b, n);
+ b += n;
+ used += n;
+ rpm->hpos += n;
+
+ if (rpm->hpos == 16) {
+ if (rpm->header[0] != 0x8e ||
+ rpm->header[1] != 0xad ||
+ rpm->header[2] != 0xe8 ||
+ rpm->header[3] != 0x01) {
+ if (rpm->first_header) {
+ archive_set_error(
+ &self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unrecognized rpm header");
+ return (ARCHIVE_FATAL);
+ }
+ rpm->state = ST_ARCHIVE;
+ *buff = rpm->header;
+ total = rpm->hpos;
+ break;
+ }
+ /* Calculate 'Header' length. */
+ section = archive_be32dec(rpm->header+8);
+ bytes = archive_be32dec(rpm->header+12);
+ rpm->hlen = 16 + section * 16 + bytes;
+ rpm->state = ST_HEADER_DATA;
+ rpm->first_header = 0;
+ }
+ break;
+ case ST_HEADER_DATA:
+ n = rpm->hlen - rpm->hpos;
+ if (n > avail_in - used)
+ n = avail_in - used;
+ b += n;
+ used += n;
+ rpm->hpos += n;
+ if (rpm->hpos == rpm->hlen)
+ rpm->state = ST_PADDING;
+ break;
+ case ST_PADDING:
+ while (used < (size_t)avail_in) {
+ if (*b != 0) {
+ /* Read next header. */
+ rpm->state = ST_HEADER;
+ rpm->hpos = 0;
+ rpm->hlen = 0;
+ break;
+ }
+ b++;
+ used++;
+ }
+ break;
+ case ST_ARCHIVE:
+ *buff = b;
+ total = avail_in;
+ used = avail_in;
+ break;
+ }
+ if (used == (size_t)avail_in) {
+ rpm->total_in += used;
+ __archive_read_filter_consume(self->upstream, used);
+ b = NULL;
+ used = 0;
+ }
+ } while (total == 0 && avail_in > 0);
+
+ if (used > 0 && b != NULL) {
+ rpm->total_in += used;
+ __archive_read_filter_consume(self->upstream, used);
+ }
+ return (total);
+}
+
+static int
+rpm_filter_close(struct archive_read_filter *self)
+{
+ struct rpm *rpm;
+
+ rpm = (struct rpm *)self->data;
+ free(rpm);
+
+ return (ARCHIVE_OK);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_uu.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_uu.c
new file mode 100644
index 0000000000..cd79638e73
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_uu.c
@@ -0,0 +1,731 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+/* Maximum lookahead during bid phase */
+#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */
+
+struct uudecode {
+ int64_t total;
+ unsigned char *in_buff;
+#define IN_BUFF_SIZE (1024)
+ int in_cnt;
+ size_t in_allocated;
+ unsigned char *out_buff;
+#define OUT_BUFF_SIZE (64 * 1024)
+ int state;
+#define ST_FIND_HEAD 0
+#define ST_READ_UU 1
+#define ST_UUEND 2
+#define ST_READ_BASE64 3
+#define ST_IGNORE 4
+ mode_t mode;
+ int mode_set;
+ char *name;
+};
+
+static int uudecode_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *filter);
+static int uudecode_bidder_init(struct archive_read_filter *);
+
+static int uudecode_read_header(struct archive_read_filter *,
+ struct archive_entry *entry);
+static ssize_t uudecode_filter_read(struct archive_read_filter *,
+ const void **);
+static int uudecode_filter_close(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_uu(struct archive *a)
+{
+ return archive_read_support_filter_uu(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+uudecode_bidder_vtable = {
+ .bid = uudecode_bidder_bid,
+ .init = uudecode_bidder_init,
+};
+
+int
+archive_read_support_filter_uu(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ return __archive_read_register_bidder(a, NULL, "uu",
+ &uudecode_bidder_vtable);
+}
+
+static const unsigned char ascii[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const unsigned char uuchar[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const unsigned char base64[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const int base64num[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 62, 0, 0, 0, 63, /* 20 - 2F */
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 0, 0, 0, 0, 0, 0, /* 30 - 3F */
+ 0, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */
+ 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 0, 0, 0, 0, 0, /* 50 - 5F */
+ 0, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
+ 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 0, 0, 0, 0, 0, /* 70 - 7F */
+};
+
+static ssize_t
+get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
+{
+ ssize_t len;
+
+ len = 0;
+ while (len < avail) {
+ switch (ascii[*b]) {
+ case 0: /* Non-ascii character or control character. */
+ if (nlsize != NULL)
+ *nlsize = 0;
+ return (-1);
+ case '\r':
+ if (avail-len > 1 && b[1] == '\n') {
+ if (nlsize != NULL)
+ *nlsize = 2;
+ return (len+2);
+ }
+ /* FALL THROUGH */
+ case '\n':
+ if (nlsize != NULL)
+ *nlsize = 1;
+ return (len+1);
+ case 1:
+ b++;
+ len++;
+ break;
+ }
+ }
+ if (nlsize != NULL)
+ *nlsize = 0;
+ return (avail);
+}
+
+static ssize_t
+bid_get_line(struct archive_read_filter *filter,
+ const unsigned char **b, ssize_t *avail, ssize_t *ravail,
+ ssize_t *nl, size_t* nbytes_read)
+{
+ ssize_t len;
+ int quit;
+
+ quit = 0;
+ if (*avail == 0) {
+ *nl = 0;
+ len = 0;
+ } else
+ len = get_line(*b, *avail, nl);
+
+ /*
+ * Read bytes more while it does not reach the end of line.
+ */
+ while (*nl == 0 && len == *avail && !quit &&
+ *nbytes_read < UUENCODE_BID_MAX_READ) {
+ ssize_t diff = *ravail - *avail;
+ size_t nbytes_req = (*ravail+1023) & ~1023U;
+ ssize_t tested;
+
+ /* Increase reading bytes if it is not enough to at least
+ * new two lines. */
+ if (nbytes_req < (size_t)*ravail + 160)
+ nbytes_req <<= 1;
+
+ *b = __archive_read_filter_ahead(filter, nbytes_req, avail);
+ if (*b == NULL) {
+ if (*ravail >= *avail)
+ return (0);
+ /* Reading bytes reaches the end of a stream. */
+ *b = __archive_read_filter_ahead(filter, *avail, avail);
+ quit = 1;
+ }
+ *nbytes_read = *avail;
+ *ravail = *avail;
+ *b += diff;
+ *avail -= diff;
+ tested = len;/* Skip some bytes we already determined. */
+ len = get_line(*b + tested, *avail - tested, nl);
+ if (len >= 0)
+ len += tested;
+ }
+ return (len);
+}
+
+#define UUDECODE(c) (((c) - 0x20) & 0x3f)
+
+static int
+uudecode_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *b;
+ ssize_t avail, ravail;
+ ssize_t len, nl;
+ int l;
+ int firstline;
+ size_t nbytes_read;
+
+ (void)self; /* UNUSED */
+
+ b = __archive_read_filter_ahead(filter, 1, &avail);
+ if (b == NULL)
+ return (0);
+
+ firstline = 20;
+ ravail = avail;
+ nbytes_read = avail;
+ for (;;) {
+ len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+ if (len < 0 || nl == 0)
+ return (0); /* No match found. */
+ if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
+ l = 6;
+ else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0)
+ l = 13;
+ else
+ l = 0;
+
+ if (l > 0 && (b[l] < '0' || b[l] > '7' ||
+ b[l+1] < '0' || b[l+1] > '7' ||
+ b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
+ l = 0;
+
+ b += len;
+ avail -= len;
+ if (l)
+ break;
+ firstline = 0;
+
+ /* Do not read more than UUENCODE_BID_MAX_READ bytes */
+ if (nbytes_read >= UUENCODE_BID_MAX_READ)
+ return (0);
+ }
+ if (!avail)
+ return (0);
+ len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+ if (len < 0 || nl == 0)
+ return (0);/* There are non-ascii characters. */
+ avail -= len;
+
+ if (l == 6) {
+ /* "begin " */
+ if (!uuchar[*b])
+ return (0);
+ /* Get a length of decoded bytes. */
+ l = UUDECODE(*b++); len--;
+ if (l > 45)
+ /* Normally, maximum length is 45(character 'M'). */
+ return (0);
+ if (l > len - nl)
+ return (0); /* Line too short. */
+ while (l) {
+ if (!uuchar[*b++])
+ return (0);
+ --len;
+ --l;
+ }
+ if (len-nl == 1 &&
+ (uuchar[*b] || /* Check sum. */
+ (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
+ ++b;
+ --len;
+ }
+ b += nl;
+ if (avail && uuchar[*b])
+ return (firstline+30);
+ } else if (l == 13) {
+ /* "begin-base64 " */
+ while (len-nl > 0) {
+ if (!base64[*b++])
+ return (0);
+ --len;
+ }
+ b += nl;
+
+ if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
+ return (firstline+40);
+ if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
+ return (firstline+40);
+ if (avail > 0 && base64[*b])
+ return (firstline+30);
+ }
+
+ return (0);
+}
+
+static const struct archive_read_filter_vtable
+uudecode_reader_vtable = {
+ .read = uudecode_filter_read,
+ .close = uudecode_filter_close,
+ .read_header = uudecode_read_header
+};
+
+static int
+uudecode_bidder_init(struct archive_read_filter *self)
+{
+ struct uudecode *uudecode;
+ void *out_buff;
+ void *in_buff;
+
+ self->code = ARCHIVE_FILTER_UU;
+ self->name = "uu";
+
+ uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
+ out_buff = malloc(OUT_BUFF_SIZE);
+ in_buff = malloc(IN_BUFF_SIZE);
+ if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for uudecode");
+ free(uudecode);
+ free(out_buff);
+ free(in_buff);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = uudecode;
+ uudecode->in_buff = in_buff;
+ uudecode->in_cnt = 0;
+ uudecode->in_allocated = IN_BUFF_SIZE;
+ uudecode->out_buff = out_buff;
+ uudecode->state = ST_FIND_HEAD;
+ uudecode->mode_set = 0;
+ uudecode->name = NULL;
+ self->vtable = &uudecode_reader_vtable;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+ensure_in_buff_size(struct archive_read_filter *self,
+ struct uudecode *uudecode, size_t size)
+{
+
+ if (size > uudecode->in_allocated) {
+ unsigned char *ptr;
+ size_t newsize;
+
+ /*
+ * Calculate a new buffer size for in_buff.
+ * Increase its value until it has enough size we need.
+ */
+ newsize = uudecode->in_allocated;
+ do {
+ if (newsize < IN_BUFF_SIZE*32)
+ newsize <<= 1;
+ else
+ newsize += IN_BUFF_SIZE;
+ } while (size > newsize);
+ /* Allocate the new buffer. */
+ ptr = malloc(newsize);
+ if (ptr == NULL) {
+ free(ptr);
+ archive_set_error(&self->archive->archive,
+ ENOMEM,
+ "Can't allocate data for uudecode");
+ return (ARCHIVE_FATAL);
+ }
+ /* Move the remaining data in in_buff into the new buffer. */
+ if (uudecode->in_cnt)
+ memmove(ptr, uudecode->in_buff, uudecode->in_cnt);
+ /* Replace in_buff with the new buffer. */
+ free(uudecode->in_buff);
+ uudecode->in_buff = ptr;
+ uudecode->in_allocated = newsize;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+uudecode_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+{
+
+ struct uudecode *uudecode;
+ uudecode = (struct uudecode *)self->data;
+
+ if (uudecode->mode_set != 0)
+ archive_entry_set_mode(entry, S_IFREG | uudecode->mode);
+
+ if (uudecode->name != NULL)
+ archive_entry_set_pathname(entry, uudecode->name);
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+uudecode_filter_read(struct archive_read_filter *self, const void **buff)
+{
+ struct uudecode *uudecode;
+ const unsigned char *b, *d;
+ unsigned char *out;
+ ssize_t avail_in, ravail;
+ ssize_t used;
+ ssize_t total;
+ ssize_t len, llen, nl, namelen;
+
+ uudecode = (struct uudecode *)self->data;
+
+read_more:
+ d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+ if (d == NULL && avail_in < 0)
+ return (ARCHIVE_FATAL);
+ /* Quiet a code analyzer; make sure avail_in must be zero
+ * when d is NULL. */
+ if (d == NULL)
+ avail_in = 0;
+ used = 0;
+ total = 0;
+ out = uudecode->out_buff;
+ ravail = avail_in;
+ if (uudecode->state == ST_IGNORE) {
+ used = avail_in;
+ goto finish;
+ }
+ if (uudecode->in_cnt) {
+ /*
+ * If there is remaining data which is saved by
+ * previous calling, use it first.
+ */
+ if (ensure_in_buff_size(self, uudecode,
+ avail_in + uudecode->in_cnt) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ memcpy(uudecode->in_buff + uudecode->in_cnt,
+ d, avail_in);
+ d = uudecode->in_buff;
+ avail_in += uudecode->in_cnt;
+ uudecode->in_cnt = 0;
+ }
+ for (;used < avail_in; d += llen, used += llen) {
+ int64_t l, body;
+
+ b = d;
+ len = get_line(b, avail_in - used, &nl);
+ if (len < 0) {
+ /* Non-ascii character is found. */
+ if (uudecode->state == ST_FIND_HEAD &&
+ (uudecode->total > 0 || total > 0)) {
+ uudecode->state = ST_IGNORE;
+ used = avail_in;
+ goto finish;
+ }
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ llen = len;
+ if ((nl == 0) && (uudecode->state != ST_UUEND)) {
+ if (total == 0 && ravail <= 0) {
+ /* There is nothing more to read, fail */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Missing format data");
+ return (ARCHIVE_FATAL);
+ }
+ /*
+ * Save remaining data which does not contain
+ * NL('\n','\r').
+ */
+ if (ensure_in_buff_size(self, uudecode, len)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (uudecode->in_buff != b)
+ memmove(uudecode->in_buff, b, len);
+ uudecode->in_cnt = (int)len;
+ if (total == 0) {
+ /* Do not return 0; it means end-of-file.
+ * We should try to read bytes more. */
+ __archive_read_filter_consume(
+ self->upstream, ravail);
+ goto read_more;
+ }
+ used += len;
+ break;
+ }
+ switch (uudecode->state) {
+ default:
+ case ST_FIND_HEAD:
+ /* Do not read more than UUENCODE_BID_MAX_READ bytes */
+ if (total + len >= UUENCODE_BID_MAX_READ) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid format data");
+ return (ARCHIVE_FATAL);
+ }
+ if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
+ l = 6;
+ else if (len - nl >= 18 &&
+ memcmp(b, "begin-base64 ", 13) == 0)
+ l = 13;
+ else
+ l = 0;
+ if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
+ b[l+1] >= '0' && b[l+1] <= '7' &&
+ b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
+ if (l == 6)
+ uudecode->state = ST_READ_UU;
+ else
+ uudecode->state = ST_READ_BASE64;
+ uudecode->mode = (mode_t)(
+ ((int)(b[l] - '0') * 64) +
+ ((int)(b[l+1] - '0') * 8) +
+ (int)(b[l+2] - '0'));
+ uudecode->mode_set = 1;
+ namelen = len - nl - 4 - l;
+ if (namelen > 1) {
+ if (uudecode->name != NULL)
+ free(uudecode->name);
+ uudecode->name = malloc(namelen + 1);
+ if (uudecode->name == NULL) {
+ archive_set_error(
+ &self->archive->archive,
+ ENOMEM,
+ "Can't allocate data for uudecode");
+ return (ARCHIVE_FATAL);
+ }
+ strncpy(uudecode->name,
+ (const char *)(b + l + 4),
+ namelen);
+ uudecode->name[namelen] = '\0';
+ }
+ }
+ break;
+ case ST_READ_UU:
+ if (total + len * 2 > OUT_BUFF_SIZE)
+ goto finish;
+ body = len - nl;
+ if (!uuchar[*b] || body <= 0) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ /* Get length of undecoded bytes of current line. */
+ l = UUDECODE(*b++);
+ body--;
+ if (l > body) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ if (l == 0) {
+ uudecode->state = ST_UUEND;
+ break;
+ }
+ while (l > 0) {
+ int n = 0;
+
+ if (!uuchar[b[0]] || !uuchar[b[1]])
+ break;
+ n = UUDECODE(*b++) << 18;
+ n |= UUDECODE(*b++) << 12;
+ *out++ = n >> 16; total++;
+ --l;
+
+ if (l > 0) {
+ if (!uuchar[b[0]])
+ break;
+ n |= UUDECODE(*b++) << 6;
+ *out++ = (n >> 8) & 0xFF; total++;
+ --l;
+ }
+ if (l > 0) {
+ if (!uuchar[b[0]])
+ break;
+ n |= UUDECODE(*b++);
+ *out++ = n & 0xFF; total++;
+ --l;
+ }
+ }
+ if (l) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ case ST_UUEND:
+ if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
+ uudecode->state = ST_FIND_HEAD;
+ else {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ case ST_READ_BASE64:
+ if (total + len * 2 > OUT_BUFF_SIZE)
+ goto finish;
+ l = len - nl;
+ if (l >= 3 && b[0] == '=' && b[1] == '=' &&
+ b[2] == '=') {
+ uudecode->state = ST_FIND_HEAD;
+ break;
+ }
+ while (l > 0) {
+ int n = 0;
+
+ if (!base64[b[0]] || !base64[b[1]])
+ break;
+ n = base64num[*b++] << 18;
+ n |= base64num[*b++] << 12;
+ *out++ = n >> 16; total++;
+ l -= 2;
+
+ if (l > 0) {
+ if (*b == '=')
+ break;
+ if (!base64[*b])
+ break;
+ n |= base64num[*b++] << 6;
+ *out++ = (n >> 8) & 0xFF; total++;
+ --l;
+ }
+ if (l > 0) {
+ if (*b == '=')
+ break;
+ if (!base64[*b])
+ break;
+ n |= base64num[*b++];
+ *out++ = n & 0xFF; total++;
+ --l;
+ }
+ }
+ if (l && *b != '=') {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Insufficient compressed data");
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ }
+ }
+finish:
+ if (ravail < avail_in)
+ used -= avail_in - ravail;
+ __archive_read_filter_consume(self->upstream, used);
+
+ *buff = uudecode->out_buff;
+ uudecode->total += total;
+ return (total);
+}
+
+static int
+uudecode_filter_close(struct archive_read_filter *self)
+{
+ struct uudecode *uudecode;
+
+ uudecode = (struct uudecode *)self->data;
+ free(uudecode->in_buff);
+ free(uudecode->out_buff);
+ free(uudecode->name);
+ free(uudecode);
+
+ return (ARCHIVE_OK);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_xz.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_xz.c
new file mode 100644
index 0000000000..13d4ebd7e3
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_xz.c
@@ -0,0 +1,793 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+
+struct private_data {
+ lzma_stream stream;
+ unsigned char *out_block;
+ size_t out_block_size;
+ int64_t total_out;
+ char eof; /* True = found end of compressed data. */
+ char in_stream;
+
+ /* Following variables are used for lzip only. */
+ char lzip_ver;
+ uint32_t crc32;
+ int64_t member_in;
+ int64_t member_out;
+};
+
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT (1U << 30)
+#endif
+
+/* Combined lzip/lzma/xz filter */
+static ssize_t xz_filter_read(struct archive_read_filter *, const void **);
+static int xz_filter_close(struct archive_read_filter *);
+static int xz_lzma_bidder_init(struct archive_read_filter *);
+
+#endif
+
+/*
+ * Note that we can detect xz and lzma compressed files even if we
+ * can't decompress them. (In fact, we like detecting them because we
+ * can give better error messages.) So the bid framework here gets
+ * compiled even if no lzma library is available.
+ */
+static int xz_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int xz_bidder_init(struct archive_read_filter *);
+static int lzma_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int lzma_bidder_init(struct archive_read_filter *);
+static int lzip_has_member(struct archive_read_filter *);
+static int lzip_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int lzip_bidder_init(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_xz(struct archive *a)
+{
+ return archive_read_support_filter_xz(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+xz_bidder_vtable = {
+ .bid = xz_bidder_bid,
+ .init = xz_bidder_init,
+};
+
+int
+archive_read_support_filter_xz(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "xz",
+ &xz_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external xz program for xz decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_read_support_compression_lzma(struct archive *a)
+{
+ return archive_read_support_filter_lzma(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+lzma_bidder_vtable = {
+ .bid = lzma_bidder_bid,
+ .init = lzma_bidder_init,
+};
+
+int
+archive_read_support_filter_lzma(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "lzma",
+ &lzma_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lzma program for lzma decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_read_support_compression_lzip(struct archive *a)
+{
+ return archive_read_support_filter_lzip(a);
+}
+#endif
+
+static const struct archive_read_filter_bidder_vtable
+lzip_bidder_vtable = {
+ .bid = lzip_bidder_bid,
+ .init = lzip_bidder_init,
+};
+
+int
+archive_read_support_filter_lzip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "lzip",
+ &lzip_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lzip program for lzip decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ */
+static int
+xz_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+
+ (void)self; /* UNUSED */
+
+ buffer = __archive_read_filter_ahead(filter, 6, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /*
+ * Verify Header Magic Bytes : FD 37 7A 58 5A 00
+ */
+ if (memcmp(buffer, "\xFD\x37\x7A\x58\x5A\x00", 6) != 0)
+ return (0);
+
+ return (48);
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * <sigh> LZMA has a rather poor file signature. Zeros do not
+ * make good signature bytes as a rule, and the only non-zero byte
+ * here is an ASCII character. For example, an uncompressed tar
+ * archive whose first file is ']' would satisfy this check. It may
+ * be necessary to exclude LZMA from compression_all() because of
+ * this. Clients of libarchive would then have to explicitly enable
+ * LZMA checking instead of (or in addition to) compression_all() when
+ * they have other evidence (file name, command-line option) to go on.
+ */
+static int
+lzma_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ uint32_t dicsize;
+ uint64_t uncompressed_size;
+ int bits_checked;
+
+ (void)self; /* UNUSED */
+
+ buffer = __archive_read_filter_ahead(filter, 14, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /* First byte of raw LZMA stream is commonly 0x5d.
+ * The first byte is a special number, which consists of
+ * three parameters of LZMA compression, a number of literal
+ * context bits(which is from 0 to 8, default is 3), a number
+ * of literal pos bits(which is from 0 to 4, default is 0),
+ * a number of pos bits(which is from 0 to 4, default is 2).
+ * The first byte is made by
+ * (pos bits * 5 + literal pos bit) * 9 + * literal contest bit,
+ * and so the default value in this field is
+ * (2 * 5 + 0) * 9 + 3 = 0x5d.
+ * lzma of LZMA SDK has options to change those parameters.
+ * It means a range of this field is from 0 to 224. And lzma of
+ * XZ Utils with option -e records 0x5e in this field. */
+ /* NOTE: If this checking of the first byte increases false
+ * recognition, we should allow only 0x5d and 0x5e for the first
+ * byte of LZMA stream. */
+ bits_checked = 0;
+ if (buffer[0] > (4 * 5 + 4) * 9 + 8)
+ return (0);
+ /* Most likely value in the first byte of LZMA stream. */
+ if (buffer[0] == 0x5d || buffer[0] == 0x5e)
+ bits_checked += 8;
+
+ /* Sixth through fourteenth bytes are uncompressed size,
+ * stored in little-endian order. `-1' means uncompressed
+ * size is unknown and lzma of XZ Utils always records `-1'
+ * in this field. */
+ uncompressed_size = archive_le64dec(buffer+5);
+ if (uncompressed_size == (uint64_t)ARCHIVE_LITERAL_LL(-1))
+ bits_checked += 64;
+
+ /* Second through fifth bytes are dictionary size, stored in
+ * little-endian order. The minimum dictionary size is
+ * 1 << 12(4KiB) which the lzma of LZMA SDK uses with option
+ * -d12 and the maximum dictionary size is 1 << 29(512MiB)
+ * which the one uses with option -d29.
+ * NOTE: A comment of LZMA SDK source code says this dictionary
+ * range is from 1 << 12 to 1 << 30. */
+ dicsize = archive_le32dec(buffer+1);
+ switch (dicsize) {
+ case 0x00001000:/* lzma of LZMA SDK option -d12. */
+ case 0x00002000:/* lzma of LZMA SDK option -d13. */
+ case 0x00004000:/* lzma of LZMA SDK option -d14. */
+ case 0x00008000:/* lzma of LZMA SDK option -d15. */
+ case 0x00010000:/* lzma of XZ Utils option -0 and -1.
+ * lzma of LZMA SDK option -d16. */
+ case 0x00020000:/* lzma of LZMA SDK option -d17. */
+ case 0x00040000:/* lzma of LZMA SDK option -d18. */
+ case 0x00080000:/* lzma of XZ Utils option -2.
+ * lzma of LZMA SDK option -d19. */
+ case 0x00100000:/* lzma of XZ Utils option -3.
+ * lzma of LZMA SDK option -d20. */
+ case 0x00200000:/* lzma of XZ Utils option -4.
+ * lzma of LZMA SDK option -d21. */
+ case 0x00400000:/* lzma of XZ Utils option -5.
+ * lzma of LZMA SDK option -d22. */
+ case 0x00800000:/* lzma of XZ Utils option -6.
+ * lzma of LZMA SDK option -d23. */
+ case 0x01000000:/* lzma of XZ Utils option -7.
+ * lzma of LZMA SDK option -d24. */
+ case 0x02000000:/* lzma of XZ Utils option -8.
+ * lzma of LZMA SDK option -d25. */
+ case 0x04000000:/* lzma of XZ Utils option -9.
+ * lzma of LZMA SDK option -d26. */
+ case 0x08000000:/* lzma of LZMA SDK option -d27. */
+ bits_checked += 32;
+ break;
+ default:
+ /* If a memory usage for encoding was not enough on
+ * the platform where LZMA stream was made, lzma of
+ * XZ Utils automatically decreased the dictionary
+ * size to enough memory for encoding by 1Mi bytes
+ * (1 << 20).*/
+ if (dicsize <= 0x03F00000 && dicsize >= 0x00300000 &&
+ (dicsize & ((1 << 20)-1)) == 0 &&
+ bits_checked == 8 + 64) {
+ bits_checked += 32;
+ break;
+ }
+ /* Otherwise dictionary size is unlikely. But it is
+ * possible that someone makes lzma stream with
+ * liblzma/LZMA SDK in one's dictionary size. */
+ return (0);
+ }
+
+ /* TODO: The above test is still very weak. It would be
+ * good to do better. */
+
+ return (bits_checked);
+}
+
+static int
+lzip_has_member(struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ int bits_checked;
+ int log2dic;
+
+ buffer = __archive_read_filter_ahead(filter, 6, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ /*
+ * Verify Header Magic Bytes : 4C 5A 49 50 (`LZIP')
+ */
+ bits_checked = 0;
+ if (memcmp(buffer, "LZIP", 4) != 0)
+ return (0);
+ bits_checked += 32;
+
+ /* A version number must be 0 or 1 */
+ if (buffer[4] != 0 && buffer[4] != 1)
+ return (0);
+ bits_checked += 8;
+
+ /* Dictionary size. */
+ log2dic = buffer[5] & 0x1f;
+ if (log2dic < 12 || log2dic > 29)
+ return (0);
+ bits_checked += 8;
+
+ return (bits_checked);
+}
+
+static int
+lzip_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+
+ (void)self; /* UNUSED */
+ return (lzip_has_member(filter));
+}
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+
+/*
+ * liblzma 4.999.7 and later support both lzma and xz streams.
+ */
+static int
+xz_bidder_init(struct archive_read_filter *self)
+{
+ self->code = ARCHIVE_FILTER_XZ;
+ self->name = "xz";
+ return (xz_lzma_bidder_init(self));
+}
+
+static int
+lzma_bidder_init(struct archive_read_filter *self)
+{
+ self->code = ARCHIVE_FILTER_LZMA;
+ self->name = "lzma";
+ return (xz_lzma_bidder_init(self));
+}
+
+static int
+lzip_bidder_init(struct archive_read_filter *self)
+{
+ self->code = ARCHIVE_FILTER_LZIP;
+ self->name = "lzip";
+ return (xz_lzma_bidder_init(self));
+}
+
+/*
+ * Set an error code and choose an error message
+ */
+static void
+set_error(struct archive_read_filter *self, int ret)
+{
+
+ switch (ret) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ case LZMA_OK: /* Decompressor made some progress. */
+ break;
+ case LZMA_MEM_ERROR:
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Lzma library error: Cannot allocate memory");
+ break;
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Lzma library error: Out of memory");
+ break;
+ case LZMA_FORMAT_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: format not recognized");
+ break;
+ case LZMA_OPTIONS_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: Invalid options");
+ break;
+ case LZMA_DATA_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: Corrupted input data");
+ break;
+ case LZMA_BUF_ERROR:
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: No progress is possible");
+ break;
+ default:
+ /* Return an error. */
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma decompression failed: Unknown error");
+ break;
+ }
+}
+
+static const struct archive_read_filter_vtable
+xz_lzma_reader_vtable = {
+ .read = xz_filter_read,
+ .close = xz_filter_close,
+};
+
+/*
+ * Setup the callbacks.
+ */
+static int
+xz_lzma_bidder_init(struct archive_read_filter *self)
+{
+ static const size_t out_block_size = 64 * 1024;
+ void *out_block;
+ struct private_data *state;
+ int ret;
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = (unsigned char *)malloc(out_block_size);
+ if (state == NULL || out_block == NULL) {
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for xz decompression");
+ free(out_block);
+ free(state);
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ self->vtable = &xz_lzma_reader_vtable;
+
+ state->stream.avail_in = 0;
+
+ state->stream.next_out = state->out_block;
+ state->stream.avail_out = state->out_block_size;
+
+ state->crc32 = 0;
+ if (self->code == ARCHIVE_FILTER_LZIP) {
+ /*
+ * We have to read a lzip header and use it to initialize
+ * compression library, thus we cannot initialize the
+ * library for lzip here.
+ */
+ state->in_stream = 0;
+ return (ARCHIVE_OK);
+ } else
+ state->in_stream = 1;
+
+ /* Initialize compression library. */
+ if (self->code == ARCHIVE_FILTER_XZ)
+ ret = lzma_stream_decoder(&(state->stream),
+ LZMA_MEMLIMIT,/* memlimit */
+ LZMA_CONCATENATED);
+ else
+ ret = lzma_alone_decoder(&(state->stream),
+ LZMA_MEMLIMIT);/* memlimit */
+
+ if (ret == LZMA_OK)
+ return (ARCHIVE_OK);
+
+ /* Library setup failed: Choose an error message and clean up. */
+ set_error(self, ret);
+
+ free(state->out_block);
+ free(state);
+ self->data = NULL;
+ return (ARCHIVE_FATAL);
+}
+
+static int
+lzip_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ const unsigned char *h;
+ lzma_filter filters[2];
+ unsigned char props[5];
+ ssize_t avail_in;
+ uint32_t dicsize;
+ int log2dic, ret;
+
+ state = (struct private_data *)self->data;
+ h = __archive_read_filter_ahead(self->upstream, 6, &avail_in);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* Get a version number. */
+ state->lzip_ver = h[4];
+
+ /*
+ * Setup lzma property.
+ */
+ props[0] = 0x5d;
+
+ /* Get dictionary size. */
+ log2dic = h[5] & 0x1f;
+ if (log2dic < 12 || log2dic > 29)
+ return (ARCHIVE_FATAL);
+ dicsize = 1U << log2dic;
+ if (log2dic > 12)
+ dicsize -= (dicsize / 16) * (h[5] >> 5);
+ archive_le32enc(props+1, dicsize);
+
+ /* Consume lzip header. */
+ __archive_read_filter_consume(self->upstream, 6);
+ state->member_in = 6;
+
+ filters[0].id = LZMA_FILTER_LZMA1;
+ filters[0].options = NULL;
+ filters[1].id = LZMA_VLI_UNKNOWN;
+ filters[1].options = NULL;
+
+ ret = lzma_properties_decode(&filters[0], NULL, props, sizeof(props));
+ if (ret != LZMA_OK) {
+ set_error(self, ret);
+ return (ARCHIVE_FATAL);
+ }
+ ret = lzma_raw_decoder(&(state->stream), filters);
+ free(filters[0].options);
+ if (ret != LZMA_OK) {
+ set_error(self, ret);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+lzip_tail(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ const unsigned char *f;
+ ssize_t avail_in;
+ int tail;
+
+ state = (struct private_data *)self->data;
+ if (state->lzip_ver == 0)
+ tail = 12;
+ else
+ tail = 20;
+ f = __archive_read_filter_ahead(self->upstream, tail, &avail_in);
+ if (f == NULL && avail_in < 0)
+ return (ARCHIVE_FATAL);
+ if (f == NULL || avail_in < tail) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Lzip: Remaining data is less bytes");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Check the crc32 value of the uncompressed data of the current
+ * member */
+ if (state->crc32 != archive_le32dec(f)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Lzip: CRC32 error");
+ return (ARCHIVE_FAILED);
+#endif
+ }
+
+ /* Check the uncompressed size of the current member */
+ if ((uint64_t)state->member_out != archive_le64dec(f + 4)) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Lzip: Uncompressed size error");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Check the total size of the current member */
+ if (state->lzip_ver == 1 &&
+ (uint64_t)state->member_in + tail != archive_le64dec(f + 12)) {
+ archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+ "Lzip: Member size error");
+ return (ARCHIVE_FAILED);
+ }
+ __archive_read_filter_consume(self->upstream, tail);
+
+ /* If current lzip data consists of multi member, try decompressing
+ * a next member. */
+ if (lzip_has_member(self->upstream) != 0) {
+ state->in_stream = 0;
+ state->crc32 = 0;
+ state->member_out = 0;
+ state->member_in = 0;
+ state->eof = 0;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+xz_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state;
+ size_t decompressed;
+ ssize_t avail_in;
+ int ret;
+
+ state = (struct private_data *)self->data;
+
+ /* Empty our output buffer. */
+ state->stream.next_out = state->out_block;
+ state->stream.avail_out = state->out_block_size;
+
+ /* Try to fill the output buffer. */
+ while (state->stream.avail_out > 0 && !state->eof) {
+ if (!state->in_stream) {
+ /*
+ * Initialize liblzma for lzip
+ */
+ ret = lzip_init(self);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ state->in_stream = 1;
+ }
+ state->stream.next_in =
+ __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+ if (state->stream.next_in == NULL && avail_in < 0) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "truncated input");
+ return (ARCHIVE_FATAL);
+ }
+ state->stream.avail_in = avail_in;
+
+ /* Decompress as much as we can in one pass. */
+ ret = lzma_code(&(state->stream),
+ (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
+ switch (ret) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ state->eof = 1;
+ /* FALL THROUGH */
+ case LZMA_OK: /* Decompressor made some progress. */
+ __archive_read_filter_consume(self->upstream,
+ avail_in - state->stream.avail_in);
+ state->member_in +=
+ avail_in - state->stream.avail_in;
+ break;
+ default:
+ set_error(self, ret);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ decompressed = state->stream.next_out - state->out_block;
+ state->total_out += decompressed;
+ state->member_out += decompressed;
+ if (decompressed == 0)
+ *p = NULL;
+ else {
+ *p = state->out_block;
+ if (self->code == ARCHIVE_FILTER_LZIP) {
+ state->crc32 = lzma_crc32(state->out_block,
+ decompressed, state->crc32);
+ if (state->eof) {
+ ret = lzip_tail(self);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+ }
+ }
+ return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+xz_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+
+ state = (struct private_data *)self->data;
+ lzma_end(&(state->stream));
+ free(state->out_block);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ *
+ * If we have no suitable library on this system, we can't actually do
+ * the decompression. We can, however, still detect compressed
+ * archives and emit a useful message.
+ *
+ */
+static int
+lzma_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lzma -d -qq");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LZMA;
+ self->name = "lzma";
+ return (r);
+}
+
+static int
+xz_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "xz -d -qq");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_XZ;
+ self->name = "xz";
+ return (r);
+}
+
+static int
+lzip_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "lzip -d -q");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_LZIP;
+ self->name = "lzip";
+ return (r);
+}
+
+#endif /* HAVE_LZMA_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_filter_zstd.c b/contrib/libs/libarchive/libarchive/archive_read_support_filter_zstd.c
new file mode 100644
index 0000000000..1959b5ac39
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_filter_zstd.c
@@ -0,0 +1,297 @@
+/*-
+ * Copyright (c) 2009-2011 Sean Purcell
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+
+struct private_data {
+ ZSTD_DStream *dstream;
+ unsigned char *out_block;
+ size_t out_block_size;
+ int64_t total_out;
+ char in_frame; /* True = in the middle of a zstd frame. */
+ char eof; /* True = found end of compressed data. */
+};
+
+/* Zstd Filter. */
+static ssize_t zstd_filter_read(struct archive_read_filter *, const void**);
+static int zstd_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect zstd compressed files even if we can't decompress
+ * them. (In fact, we like detecting them because we can give better error
+ * messages.) So the bid framework here gets compiled even if no zstd library
+ * is available.
+ */
+static int zstd_bidder_bid(struct archive_read_filter_bidder *,
+ struct archive_read_filter *);
+static int zstd_bidder_init(struct archive_read_filter *);
+
+static const struct archive_read_filter_bidder_vtable
+zstd_bidder_vtable = {
+ .bid = zstd_bidder_bid,
+ .init = zstd_bidder_init,
+};
+
+int
+archive_read_support_filter_zstd(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+
+ if (__archive_read_register_bidder(a, NULL, "zstd",
+ &zstd_bidder_vtable) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external zstd program for zstd decompression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ */
+static int
+zstd_bidder_bid(struct archive_read_filter_bidder *self,
+ struct archive_read_filter *filter)
+{
+ const unsigned char *buffer;
+ ssize_t avail;
+ unsigned prefix;
+
+ /* Zstd frame magic values */
+ unsigned zstd_magic = 0xFD2FB528U;
+ unsigned zstd_magic_skippable_start = 0x184D2A50U;
+ unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
+
+ (void) self; /* UNUSED */
+
+ buffer = __archive_read_filter_ahead(filter, 4, &avail);
+ if (buffer == NULL)
+ return (0);
+
+ prefix = archive_le32dec(buffer);
+ if (prefix == zstd_magic)
+ return (32);
+ if ((prefix & zstd_magic_skippable_mask) == zstd_magic_skippable_start)
+ return (32);
+
+ return (0);
+}
+
+#if !(HAVE_ZSTD_H && HAVE_LIBZSTD)
+
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly. We can, however, try to run "zstd -d"
+ * in case that's available.
+ */
+static int
+zstd_bidder_init(struct archive_read_filter *self)
+{
+ int r;
+
+ r = __archive_read_program(self, "zstd -d -qq");
+ /* Note: We set the format here even if __archive_read_program()
+ * above fails. We do, after all, know what the format is
+ * even if we weren't able to read it. */
+ self->code = ARCHIVE_FILTER_ZSTD;
+ self->name = "zstd";
+ return (r);
+}
+
+#else
+
+static const struct archive_read_filter_vtable
+zstd_reader_vtable = {
+ .read = zstd_filter_read,
+ .close = zstd_filter_close,
+};
+
+/*
+ * Initialize the filter object
+ */
+static int
+zstd_bidder_init(struct archive_read_filter *self)
+{
+ struct private_data *state;
+ size_t out_block_size = ZSTD_DStreamOutSize();
+ void *out_block;
+ ZSTD_DStream *dstream;
+
+ self->code = ARCHIVE_FILTER_ZSTD;
+ self->name = "zstd";
+
+ state = (struct private_data *)calloc(sizeof(*state), 1);
+ out_block = (unsigned char *)malloc(out_block_size);
+ dstream = ZSTD_createDStream();
+
+ if (state == NULL || out_block == NULL || dstream == NULL) {
+ free(out_block);
+ free(state);
+ ZSTD_freeDStream(dstream); /* supports free on NULL */
+ archive_set_error(&self->archive->archive, ENOMEM,
+ "Can't allocate data for zstd decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ self->data = state;
+
+ state->out_block_size = out_block_size;
+ state->out_block = out_block;
+ state->dstream = dstream;
+ self->vtable = &zstd_reader_vtable;
+
+ state->eof = 0;
+ state->in_frame = 0;
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+zstd_filter_read(struct archive_read_filter *self, const void **p)
+{
+ struct private_data *state;
+ size_t decompressed;
+ ssize_t avail_in;
+ ZSTD_outBuffer out;
+ ZSTD_inBuffer in;
+ size_t ret;
+
+ state = (struct private_data *)self->data;
+
+ out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 };
+
+ /* Try to fill the output buffer. */
+ while (out.pos < out.size && !state->eof) {
+ if (!state->in_frame) {
+ ret = ZSTD_initDStream(state->dstream);
+ if (ZSTD_isError(ret)) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Error initializing zstd decompressor: %s",
+ ZSTD_getErrorName(ret));
+ return (ARCHIVE_FATAL);
+ }
+ }
+ in.src = __archive_read_filter_ahead(self->upstream, 1,
+ &avail_in);
+ if (avail_in < 0) {
+ return avail_in;
+ }
+ if (in.src == NULL && avail_in == 0) {
+ if (!state->in_frame) {
+ /* end of stream */
+ state->eof = 1;
+ break;
+ } else {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Truncated zstd input");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ in.size = avail_in;
+ in.pos = 0;
+
+ {
+ ret = ZSTD_decompressStream(state->dstream, &out, &in);
+
+ if (ZSTD_isError(ret)) {
+ archive_set_error(&self->archive->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Zstd decompression failed: %s",
+ ZSTD_getErrorName(ret));
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Decompressor made some progress */
+ __archive_read_filter_consume(self->upstream, in.pos);
+
+ /* ret guaranteed to be > 0 if frame isn't done yet */
+ state->in_frame = (ret != 0);
+ }
+ }
+
+ decompressed = out.pos;
+ state->total_out += decompressed;
+ if (decompressed == 0)
+ *p = NULL;
+ else
+ *p = state->out_block;
+ return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+zstd_filter_close(struct archive_read_filter *self)
+{
+ struct private_data *state;
+
+ state = (struct private_data *)self->data;
+
+ ZSTD_freeDStream(state->dstream);
+ free(state->out_block);
+ free(state);
+
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H && HAVE_LIBZSTD */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_7zip.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_7zip.c
new file mode 100644
index 0000000000..393866700b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_7zip.c
@@ -0,0 +1,4152 @@
+/*-
+ * Copyright (c) 2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+
+#define _7ZIP_SIGNATURE "7z\xBC\xAF\x27\x1C"
+#define SFX_MIN_ADDR 0x27000
+#define SFX_MAX_ADDR 0x60000
+
+
+/*
+ * Codec ID
+ */
+#define _7Z_COPY 0
+#define _7Z_LZMA 0x030101
+#define _7Z_LZMA2 0x21
+#define _7Z_DEFLATE 0x040108
+#define _7Z_BZ2 0x040202
+#define _7Z_PPMD 0x030401
+#define _7Z_DELTA 0x03
+#define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */
+#define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */
+#define _7Z_CRYPTO_AES_256_SHA_256 0x06F10701 /* AES-256 + SHA-256 */
+
+
+#define _7Z_X86 0x03030103
+#define _7Z_X86_BCJ2 0x0303011B
+#define _7Z_POWERPC 0x03030205
+#define _7Z_IA64 0x03030401
+#define _7Z_ARM 0x03030501
+#define _7Z_ARMTHUMB 0x03030701
+#define _7Z_ARM64 0xa
+#define _7Z_SPARC 0x03030805
+
+#define _7Z_ZSTD 0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */
+
+/*
+ * 7-Zip header property IDs.
+ */
+#define kEnd 0x00
+#define kHeader 0x01
+#define kArchiveProperties 0x02
+#define kAdditionalStreamsInfo 0x03
+#define kMainStreamsInfo 0x04
+#define kFilesInfo 0x05
+#define kPackInfo 0x06
+#define kUnPackInfo 0x07
+#define kSubStreamsInfo 0x08
+#define kSize 0x09
+#define kCRC 0x0A
+#define kFolder 0x0B
+#define kCodersUnPackSize 0x0C
+#define kNumUnPackStream 0x0D
+#define kEmptyStream 0x0E
+#define kEmptyFile 0x0F
+#define kAnti 0x10
+#define kName 0x11
+#define kCTime 0x12
+#define kATime 0x13
+#define kMTime 0x14
+#define kAttributes 0x15
+#define kEncodedHeader 0x17
+#define kDummy 0x19
+
+// Check that some windows file attribute constants are defined.
+// Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+#ifndef FILE_ATTRIBUTE_READONLY
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#endif
+
+#ifndef FILE_ATTRIBUTE_HIDDEN
+#define FILE_ATTRIBUTE_HIDDEN 0x00000002
+#endif
+
+#ifndef FILE_ATTRIBUTE_SYSTEM
+#define FILE_ATTRIBUTE_SYSTEM 0x00000004
+#endif
+
+#ifndef FILE_ATTRIBUTE_DIRECTORY
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#endif
+
+// This value is defined in 7zip with the comment "trick for Unix".
+//
+// 7z archives created on unix have this bit set in the high 16 bits of
+// the attr field along with the unix permissions.
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000
+
+struct _7z_digests {
+ unsigned char *defineds;
+ uint32_t *digests;
+};
+
+
+struct _7z_folder {
+ uint64_t numCoders;
+ struct _7z_coder {
+ unsigned long codec;
+ uint64_t numInStreams;
+ uint64_t numOutStreams;
+ uint64_t propertiesSize;
+ unsigned char *properties;
+ } *coders;
+ uint64_t numBindPairs;
+ struct {
+ uint64_t inIndex;
+ uint64_t outIndex;
+ } *bindPairs;
+ uint64_t numPackedStreams;
+ uint64_t *packedStreams;
+ uint64_t numInStreams;
+ uint64_t numOutStreams;
+ uint64_t *unPackSize;
+ unsigned char digest_defined;
+ uint32_t digest;
+ uint64_t numUnpackStreams;
+ uint32_t packIndex;
+ /* Unoperated bytes. */
+ uint64_t skipped_bytes;
+};
+
+struct _7z_coders_info {
+ uint64_t numFolders;
+ struct _7z_folder *folders;
+ uint64_t dataStreamIndex;
+};
+
+struct _7z_pack_info {
+ uint64_t pos;
+ uint64_t numPackStreams;
+ uint64_t *sizes;
+ struct _7z_digests digest;
+ /* Calculated from pos and numPackStreams. */
+ uint64_t *positions;
+};
+
+struct _7z_substream_info {
+ size_t unpack_streams;
+ uint64_t *unpackSizes;
+ unsigned char *digestsDefined;
+ uint32_t *digests;
+};
+
+struct _7z_stream_info {
+ struct _7z_pack_info pi;
+ struct _7z_coders_info ci;
+ struct _7z_substream_info ss;
+};
+
+struct _7z_header_info {
+ uint64_t dataIndex;
+
+ unsigned char *emptyStreamBools;
+ unsigned char *emptyFileBools;
+ unsigned char *antiBools;
+ unsigned char *attrBools;
+};
+
+struct _7zip_entry {
+ size_t name_len;
+ unsigned char *utf16name;
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+ const wchar_t *wname;
+#endif
+ uint32_t folderIndex;
+ uint32_t ssIndex;
+ unsigned flg;
+#define MTIME_IS_SET (1<<0)
+#define ATIME_IS_SET (1<<1)
+#define CTIME_IS_SET (1<<2)
+#define CRC32_IS_SET (1<<3)
+#define HAS_STREAM (1<<4)
+
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+ long mtime_ns;
+ long atime_ns;
+ long ctime_ns;
+ uint32_t mode;
+ uint32_t attr;
+};
+
+struct _7zip {
+ /* Structural information about the archive. */
+ struct _7z_stream_info si;
+
+ int header_is_being_read;
+ int header_is_encoded;
+ uint64_t header_bytes_remaining;
+ unsigned long header_crc32;
+ /* Header offset to check that reading points of the file contents
+ * will not exceed the header. */
+ uint64_t header_offset;
+ /* Base offset of the archive file for a seek in case reading SFX. */
+ uint64_t seek_base;
+
+ /* List of entries */
+ size_t entries_remaining;
+ uint64_t numFiles;
+ struct _7zip_entry *entries;
+ struct _7zip_entry *entry;
+ unsigned char *entry_names;
+
+ /* entry_bytes_remaining is the number of bytes we expect. */
+ int64_t entry_offset;
+ uint64_t entry_bytes_remaining;
+
+ /* Running CRC32 of the decompressed data */
+ unsigned long entry_crc32;
+
+ /* Flags to mark progress of decompression. */
+ char end_of_entry;
+
+ /* Uncompressed buffer control. */
+#define UBUFF_SIZE (64 * 1024)
+ unsigned char *uncompressed_buffer;
+ unsigned char *uncompressed_buffer_pointer;
+ size_t uncompressed_buffer_size;
+ size_t uncompressed_buffer_bytes_remaining;
+
+ /* Offset of the compressed data. */
+ int64_t stream_offset;
+
+ /*
+ * Decompressing control data.
+ */
+ unsigned folder_index;
+ uint64_t folder_outbytes_remaining;
+ unsigned pack_stream_index;
+ unsigned pack_stream_remaining;
+ uint64_t pack_stream_inbytes_remaining;
+ size_t pack_stream_bytes_unconsumed;
+
+ /* The codec information of a folder. */
+ unsigned long codec;
+ unsigned long codec2;
+
+ /*
+ * Decompressor controllers.
+ */
+ /* Decoding LZMA1 and LZMA2 data. */
+#ifdef HAVE_LZMA_H
+ lzma_stream lzstream;
+ int lzstream_valid;
+#endif
+ /* Decoding bzip2 data. */
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ bz_stream bzstream;
+ int bzstream_valid;
+#endif
+ /* Decoding deflate data. */
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ int stream_valid;
+#endif
+ /* Decoding Zstandard data. */
+#if HAVE_ZSTD_H
+ ZSTD_DStream *zstd_dstream;
+ int zstdstream_valid;
+#endif
+ /* Decoding PPMd data. */
+ int ppmd7_stat;
+ CPpmd7 ppmd7_context;
+ CPpmd7z_RangeDec range_dec;
+ IByteIn bytein;
+ struct {
+ const unsigned char *next_in;
+ int64_t avail_in;
+ int64_t total_in;
+ int64_t stream_in;
+ unsigned char *next_out;
+ int64_t avail_out;
+ int64_t total_out;
+ int overconsumed;
+ } ppstream;
+ int ppmd7_valid;
+
+ /* Decoding BCJ and BCJ2 data. */
+ uint32_t bcj_state;
+ size_t odd_bcj_size;
+ unsigned char odd_bcj[4];
+ /* Decoding BCJ data. */
+ size_t bcj_prevPosT;
+ uint32_t bcj_prevMask;
+ uint32_t bcj_ip;
+
+ /* Decoding BCJ2 data. */
+ size_t main_stream_bytes_remaining;
+ unsigned char *sub_stream_buff[3];
+ size_t sub_stream_size[3];
+ size_t sub_stream_bytes_remaining[3];
+ unsigned char *tmp_stream_buff;
+ size_t tmp_stream_buff_size;
+ size_t tmp_stream_bytes_avail;
+ size_t tmp_stream_bytes_remaining;
+#ifdef _LZMA_PROB32
+#define CProb uint32_t
+#else
+#define CProb uint16_t
+#endif
+ CProb bcj2_p[256 + 2];
+ uint8_t bcj2_prevByte;
+ uint32_t bcj2_range;
+ uint32_t bcj2_code;
+ uint64_t bcj2_outPos;
+
+ /* Filename character-set conversion data. */
+ struct archive_string_conv *sconv;
+
+ char format_name[64];
+
+ /* Custom value that is non-zero if this archive contains encrypted entries. */
+ int has_encrypted_entries;
+};
+
+/* Maximum entry size. This limitation prevents reading intentional
+ * corrupted 7-zip files on assuming there are not so many entries in
+ * the files. */
+#define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000)
+
+static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *);
+static int archive_read_support_format_7zip_capabilities(struct archive_read *a);
+static int archive_read_format_7zip_bid(struct archive_read *, int);
+static int archive_read_format_7zip_cleanup(struct archive_read *);
+static int archive_read_format_7zip_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_7zip_read_data_skip(struct archive_read *);
+static int archive_read_format_7zip_read_header(struct archive_read *,
+ struct archive_entry *);
+static int check_7zip_header_in_sfx(const char *);
+static unsigned long decode_codec_id(const unsigned char *, size_t);
+static int decode_encoded_header_info(struct archive_read *,
+ struct _7z_stream_info *);
+static int decompress(struct archive_read *, struct _7zip *,
+ void *, size_t *, const void *, size_t *);
+static ssize_t extract_pack_stream(struct archive_read *, size_t);
+static void fileTimeToUtc(uint64_t, time_t *, long *);
+static uint64_t folder_uncompressed_size(struct _7z_folder *);
+static void free_CodersInfo(struct _7z_coders_info *);
+static void free_Digest(struct _7z_digests *);
+static void free_Folder(struct _7z_folder *);
+static void free_Header(struct _7z_header_info *);
+static void free_PackInfo(struct _7z_pack_info *);
+static void free_StreamsInfo(struct _7z_stream_info *);
+static void free_SubStreamsInfo(struct _7z_substream_info *);
+static int free_decompression(struct archive_read *, struct _7zip *);
+static ssize_t get_uncompressed_data(struct archive_read *, const void **,
+ size_t, size_t);
+static const unsigned char * header_bytes(struct archive_read *, size_t);
+static int init_decompression(struct archive_read *, struct _7zip *,
+ const struct _7z_coder *, const struct _7z_coder *);
+static int parse_7zip_uint64(struct archive_read *, uint64_t *);
+static int read_Bools(struct archive_read *, unsigned char *, size_t);
+static int read_CodersInfo(struct archive_read *,
+ struct _7z_coders_info *);
+static int read_Digests(struct archive_read *, struct _7z_digests *,
+ size_t);
+static int read_Folder(struct archive_read *, struct _7z_folder *);
+static int read_Header(struct archive_read *, struct _7z_header_info *,
+ int);
+static int read_PackInfo(struct archive_read *, struct _7z_pack_info *);
+static int read_StreamsInfo(struct archive_read *,
+ struct _7z_stream_info *);
+static int read_SubStreamsInfo(struct archive_read *,
+ struct _7z_substream_info *, struct _7z_folder *, size_t);
+static int read_Times(struct archive_read *, struct _7z_header_info *,
+ int);
+static void read_consume(struct archive_read *);
+static ssize_t read_stream(struct archive_read *, const void **, size_t,
+ size_t);
+static int seek_pack(struct archive_read *);
+static int64_t skip_stream(struct archive_read *, size_t);
+static int skip_sfx(struct archive_read *, ssize_t);
+static int slurp_central_directory(struct archive_read *, struct _7zip *,
+ struct _7z_header_info *);
+static int setup_decode_folder(struct archive_read *, struct _7z_folder *,
+ int);
+static void x86_Init(struct _7zip *);
+static size_t x86_Convert(struct _7zip *, uint8_t *, size_t);
+static void arm_Init(struct _7zip *);
+static size_t arm_Convert(struct _7zip *, uint8_t *, size_t);
+static size_t arm64_Convert(struct _7zip *, uint8_t *, size_t);
+static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
+
+
+int
+archive_read_support_format_7zip(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct _7zip *zip;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_7zip");
+
+ zip = calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate 7zip data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Until enough data has been read, we cannot tell about
+ * any encrypted entries yet.
+ */
+ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+
+
+ r = __archive_read_register_format(a,
+ zip,
+ "7zip",
+ archive_read_format_7zip_bid,
+ NULL,
+ archive_read_format_7zip_read_header,
+ archive_read_format_7zip_read_data,
+ archive_read_format_7zip_read_data_skip,
+ NULL,
+ archive_read_format_7zip_cleanup,
+ archive_read_support_format_7zip_capabilities,
+ archive_read_format_7zip_has_encrypted_entries);
+
+ if (r != ARCHIVE_OK)
+ free(zip);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_support_format_7zip_capabilities(struct archive_read * a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+ ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+
+static int
+archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a)
+{
+ if (_a && _a->format) {
+ struct _7zip * zip = (struct _7zip *)_a->format->data;
+ if (zip) {
+ return zip->has_encrypted_entries;
+ }
+ }
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+static int
+archive_read_format_7zip_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+
+ /* If someone has already bid more than 32, then avoid
+ trashing the look-ahead buffers with a seek. */
+ if (best_bid > 32)
+ return (-1);
+
+ if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
+ return (0);
+
+ /* If first six bytes are the 7-Zip signature,
+ * return the bid right now. */
+ if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0)
+ return (48);
+
+ /*
+ * It may a 7-Zip SFX archive file. If first two bytes are
+ * 'M' and 'Z' available on Windows or first four bytes are
+ * "\x7F\x45LF" available on posix like system, seek the 7-Zip
+ * signature. Although we will perform a seek when reading
+ * a header, what we do not use __archive_read_seek() here is
+ * due to a bidding performance.
+ */
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+ ssize_t offset = SFX_MIN_ADDR;
+ ssize_t window = 4096;
+ ssize_t bytes_avail;
+ while (offset + window <= (SFX_MAX_ADDR)) {
+ const char *buff = __archive_read_ahead(a,
+ offset + window, &bytes_avail);
+ if (buff == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ return (0);
+ continue;
+ }
+ p = buff + offset;
+ while (p + 32 < buff + bytes_avail) {
+ int step = check_7zip_header_in_sfx(p);
+ if (step == 0)
+ return (48);
+ p += step;
+ }
+ offset = p - buff;
+ }
+ }
+ return (0);
+}
+
+static int
+check_7zip_header_in_sfx(const char *p)
+{
+ switch ((unsigned char)p[5]) {
+ case 0x1C:
+ if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0)
+ return (6);
+ /*
+ * Test the CRC because its extraction code has 7-Zip
+ * Magic Code, so we should do this in order not to
+ * make a mis-detection.
+ */
+ if (crc32(0, (const unsigned char *)p + 12, 20)
+ != archive_le32dec(p + 8))
+ return (6);
+ /* Hit the header! */
+ return (0);
+ case 0x37: return (5);
+ case 0x7A: return (4);
+ case 0xBC: return (3);
+ case 0xAF: return (2);
+ case 0x27: return (1);
+ default: return (6);
+ }
+}
+
+static int
+skip_sfx(struct archive_read *a, ssize_t bytes_avail)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, offset;
+ ssize_t bytes, window;
+
+ /*
+ * If bytes_avail > SFX_MIN_ADDR we do not have to call
+ * __archive_read_seek() at this time since we have
+ * already had enough data.
+ */
+ if (bytes_avail > SFX_MIN_ADDR)
+ __archive_read_consume(a, SFX_MIN_ADDR);
+ else if (__archive_read_seek(a, SFX_MIN_ADDR, SEEK_SET) < 0)
+ return (ARCHIVE_FATAL);
+
+ offset = 0;
+ window = 1;
+ while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) {
+ h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ goto fatal;
+ continue;
+ }
+ if (bytes < 6) {
+ /* This case might happen when window == 1. */
+ window = 4096;
+ continue;
+ }
+ p = (const char *)h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the 7-Zip header.
+ */
+ while (p + 32 < q) {
+ int step = check_7zip_header_in_sfx(p);
+ if (step == 0) {
+ struct _7zip *zip =
+ (struct _7zip *)a->format->data;
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ zip->seek_base = SFX_MIN_ADDR + offset + skip;
+ return (ARCHIVE_OK);
+ }
+ p += step;
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ offset += skip;
+ if (window == 1)
+ window = 4096;
+ }
+fatal:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out 7-Zip header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_7zip_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ struct _7zip_entry *zip_entry;
+ int r, ret = ARCHIVE_OK;
+ struct _7z_folder *folder = 0;
+ uint64_t fidx = 0;
+
+ /*
+ * It should be sufficient to call archive_read_next_header() for
+ * a reader to determine if an entry is encrypted or not. If the
+ * encryption of an entry is only detectable when calling
+ * archive_read_data(), so be it. We'll do the same check there
+ * as well.
+ */
+ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "7-Zip";
+
+ if (zip->entries == NULL) {
+ struct _7z_header_info header;
+
+ memset(&header, 0, sizeof(header));
+ r = slurp_central_directory(a, zip, &header);
+ free_Header(&header);
+ if (r != ARCHIVE_OK)
+ return (r);
+ zip->entries_remaining = (size_t)zip->numFiles;
+ zip->entry = zip->entries;
+ } else {
+ ++zip->entry;
+ }
+ zip_entry = zip->entry;
+
+ if (zip->entries_remaining <= 0 || zip_entry == NULL)
+ return ARCHIVE_EOF;
+ --zip->entries_remaining;
+
+ zip->entry_offset = 0;
+ zip->end_of_entry = 0;
+ zip->entry_crc32 = crc32(0, NULL, 0);
+
+ /* Setup a string conversion for a filename. */
+ if (zip->sconv == NULL) {
+ zip->sconv = archive_string_conversion_from_charset(
+ &a->archive, "UTF-16LE", 1);
+ if (zip->sconv == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Figure out if the entry is encrypted by looking at the folder
+ that is associated to the current 7zip entry. If the folder
+ has a coder with a _7Z_CRYPTO codec then the folder is encrypted.
+ Hence the entry must also be encrypted. */
+ if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) {
+ folder = &(zip->si.ci.folders[zip_entry->folderIndex]);
+ for (fidx=0; folder && fidx<folder->numCoders; fidx++) {
+ switch(folder->coders[fidx].codec) {
+ case _7Z_CRYPTO_MAIN_ZIP:
+ case _7Z_CRYPTO_RAR_29:
+ case _7Z_CRYPTO_AES_256_SHA_256: {
+ archive_entry_set_is_data_encrypted(entry, 1);
+ zip->has_encrypted_entries = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Now that we've checked for encryption, if there were still no
+ * encrypted entries found we can say for sure that there are none.
+ */
+ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ if (archive_entry_copy_pathname_l(entry,
+ (const char *)zip_entry->utf16name,
+ zip_entry->name_len, zip->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(zip->sconv));
+ ret = ARCHIVE_WARN;
+ }
+
+ /* Populate some additional entry fields: */
+ archive_entry_set_mode(entry, zip_entry->mode);
+ if (zip_entry->flg & MTIME_IS_SET)
+ archive_entry_set_mtime(entry, zip_entry->mtime,
+ zip_entry->mtime_ns);
+ if (zip_entry->flg & CTIME_IS_SET)
+ archive_entry_set_ctime(entry, zip_entry->ctime,
+ zip_entry->ctime_ns);
+ if (zip_entry->flg & ATIME_IS_SET)
+ archive_entry_set_atime(entry, zip_entry->atime,
+ zip_entry->atime_ns);
+ if (zip_entry->ssIndex != (uint32_t)-1) {
+ zip->entry_bytes_remaining =
+ zip->si.ss.unpackSizes[zip_entry->ssIndex];
+ archive_entry_set_size(entry, zip->entry_bytes_remaining);
+ } else {
+ zip->entry_bytes_remaining = 0;
+ archive_entry_set_size(entry, 0);
+ }
+
+ // These attributes are supported by the windows implementation of archive_write_disk.
+ const int supported_attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
+
+ if (zip_entry->attr & supported_attrs) {
+ char *fflags_text, *ptr;
+ /* allocate for "rdonly,hidden,system," */
+ fflags_text = malloc(22 * sizeof(char));
+ if (fflags_text != NULL) {
+ ptr = fflags_text;
+ if (zip_entry->attr & FILE_ATTRIBUTE_READONLY) {
+ strcpy(ptr, "rdonly,");
+ ptr = ptr + 7;
+ }
+ if (zip_entry->attr & FILE_ATTRIBUTE_HIDDEN) {
+ strcpy(ptr, "hidden,");
+ ptr = ptr + 7;
+ }
+ if (zip_entry->attr & FILE_ATTRIBUTE_SYSTEM) {
+ strcpy(ptr, "system,");
+ ptr = ptr + 7;
+ }
+ if (ptr > fflags_text) {
+ /* Delete trailing comma */
+ *(ptr - 1) = '\0';
+ archive_entry_copy_fflags_text(entry,
+ fflags_text);
+ }
+ free(fflags_text);
+ }
+ }
+
+ /* If there's no body, force read_data() to return EOF immediately. */
+ if (zip->entry_bytes_remaining < 1)
+ zip->end_of_entry = 1;
+
+ if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) {
+ unsigned char *symname = NULL;
+ size_t symsize = 0;
+
+ /*
+ * Symbolic-name is recorded as its contents. We have to
+ * read the contents at this time.
+ */
+ while (zip->entry_bytes_remaining > 0) {
+ const void *buff;
+ unsigned char *mem;
+ size_t size;
+ int64_t offset;
+
+ r = archive_read_format_7zip_read_data(a, &buff,
+ &size, &offset);
+ if (r < ARCHIVE_WARN) {
+ free(symname);
+ return (r);
+ }
+ mem = realloc(symname, symsize + size + 1);
+ if (mem == NULL) {
+ free(symname);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Symname");
+ return (ARCHIVE_FATAL);
+ }
+ symname = mem;
+ memcpy(symname+symsize, buff, size);
+ symsize += size;
+ }
+ if (symsize == 0) {
+ /* If there is no symname, handle it as a regular
+ * file. */
+ zip_entry->mode &= ~AE_IFMT;
+ zip_entry->mode |= AE_IFREG;
+ archive_entry_set_mode(entry, zip_entry->mode);
+ } else {
+ symname[symsize] = '\0';
+ archive_entry_copy_symlink(entry,
+ (const char *)symname);
+ }
+ free(symname);
+ archive_entry_set_size(entry, 0);
+ }
+
+ /* Set up a more descriptive format name. */
+ snprintf(zip->format_name, sizeof(zip->format_name), "7-Zip");
+ a->archive.archive_format_name = zip->format_name;
+
+ return (ret);
+}
+
+static int
+archive_read_format_7zip_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct _7zip *zip;
+ ssize_t bytes;
+ int ret = ARCHIVE_OK;
+
+ zip = (struct _7zip *)(a->format->data);
+
+ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+
+ *offset = zip->entry_offset;
+ *size = 0;
+ *buff = NULL;
+ /*
+ * If we hit end-of-entry last time, clean up and return
+ * ARCHIVE_EOF this time.
+ */
+ if (zip->end_of_entry)
+ return (ARCHIVE_EOF);
+
+ const uint64_t max_read_size = 16 * 1024 * 1024; // Don't try to read more than 16 MB at a time
+ size_t bytes_to_read = max_read_size;
+ if ((uint64_t)bytes_to_read > zip->entry_bytes_remaining) {
+ bytes_to_read = zip->entry_bytes_remaining;
+ }
+ bytes = read_stream(a, buff, bytes_to_read, 0);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ zip->entry_bytes_remaining -= bytes;
+ if (zip->entry_bytes_remaining == 0)
+ zip->end_of_entry = 1;
+
+ /* Update checksum */
+ if ((zip->entry->flg & CRC32_IS_SET) && bytes)
+ zip->entry_crc32 = crc32(zip->entry_crc32, *buff,
+ (unsigned)bytes);
+
+ /* If we hit the end, swallow any end-of-data marker. */
+ if (zip->end_of_entry) {
+ /* Check computed CRC against file contents. */
+ if ((zip->entry->flg & CRC32_IS_SET) &&
+ zip->si.ss.digests[zip->entry->ssIndex] !=
+ zip->entry_crc32) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "7-Zip bad CRC: 0x%lx should be 0x%lx",
+ (unsigned long)zip->entry_crc32,
+ (unsigned long)zip->si.ss.digests[
+ zip->entry->ssIndex]);
+ ret = ARCHIVE_WARN;
+ }
+ }
+
+ *size = bytes;
+ *offset = zip->entry_offset;
+ zip->entry_offset += bytes;
+
+ return (ret);
+}
+
+static int
+archive_read_format_7zip_read_data_skip(struct archive_read *a)
+{
+ struct _7zip *zip;
+ int64_t bytes_skipped;
+
+ zip = (struct _7zip *)(a->format->data);
+
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+
+ /* If we've already read to end of data, we're done. */
+ if (zip->end_of_entry)
+ return (ARCHIVE_OK);
+
+ /*
+ * If the length is at the beginning, we can skip the
+ * compressed data much more quickly.
+ */
+ bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+ zip->entry_bytes_remaining = 0;
+
+ /* This entry is finished and done. */
+ zip->end_of_entry = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_7zip_cleanup(struct archive_read *a)
+{
+ struct _7zip *zip;
+
+ zip = (struct _7zip *)(a->format->data);
+ free_StreamsInfo(&(zip->si));
+ free(zip->entries);
+ free(zip->entry_names);
+ free_decompression(a, zip);
+ free(zip->uncompressed_buffer);
+ free(zip->sub_stream_buff[0]);
+ free(zip->sub_stream_buff[1]);
+ free(zip->sub_stream_buff[2]);
+ free(zip->tmp_stream_buff);
+ free(zip);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static void
+read_consume(struct archive_read *a)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+
+ if (zip->pack_stream_bytes_unconsumed) {
+ __archive_read_consume(a, zip->pack_stream_bytes_unconsumed);
+ zip->stream_offset += zip->pack_stream_bytes_unconsumed;
+ zip->pack_stream_bytes_unconsumed = 0;
+ }
+}
+
+#ifdef HAVE_LZMA_H
+
+/*
+ * Set an error code and choose an error message for liblzma.
+ */
+static void
+set_error(struct archive_read *a, int ret)
+{
+
+ switch (ret) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ case LZMA_OK: /* Decompressor made some progress. */
+ break;
+ case LZMA_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Lzma library error: Cannot allocate memory");
+ break;
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Lzma library error: Out of memory");
+ break;
+ case LZMA_FORMAT_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: format not recognized");
+ break;
+ case LZMA_OPTIONS_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: Invalid options");
+ break;
+ case LZMA_DATA_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: Corrupted input data");
+ break;
+ case LZMA_BUF_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma library error: No progress is possible");
+ break;
+ default:
+ /* Return an error. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Lzma decompression failed: Unknown error");
+ break;
+ }
+}
+
+#endif
+
+static unsigned long
+decode_codec_id(const unsigned char *codecId, size_t id_size)
+{
+ unsigned i;
+ unsigned long id = 0;
+
+ for (i = 0; i < id_size; i++) {
+ id <<= 8;
+ id += codecId[i];
+ }
+ return (id);
+}
+
+static Byte
+ppmd_read(void *p)
+{
+ struct archive_read *a = ((IByteIn*)p)->a;
+ struct _7zip *zip = (struct _7zip *)(a->format->data);
+ Byte b;
+
+ if (zip->ppstream.avail_in <= 0) {
+ /*
+ * Ppmd7_DecodeSymbol might require reading multiple bytes
+ * and we are on boundary;
+ * last resort to read using __archive_read_ahead.
+ */
+ ssize_t bytes_avail = 0;
+ const uint8_t* data = __archive_read_ahead(a,
+ zip->ppstream.stream_in+1, &bytes_avail);
+ if(bytes_avail < zip->ppstream.stream_in+1) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7z file data");
+ zip->ppstream.overconsumed = 1;
+ return (0);
+ }
+ zip->ppstream.next_in++;
+ b = data[zip->ppstream.stream_in];
+ } else {
+ b = *zip->ppstream.next_in++;
+ }
+ zip->ppstream.avail_in--;
+ zip->ppstream.total_in++;
+ zip->ppstream.stream_in++;
+ return (b);
+}
+
+static int
+init_decompression(struct archive_read *a, struct _7zip *zip,
+ const struct _7z_coder *coder1, const struct _7z_coder *coder2)
+{
+ int r;
+
+ zip->codec = coder1->codec;
+ zip->codec2 = -1;
+
+ switch (zip->codec) {
+ case _7Z_COPY:
+ case _7Z_BZ2:
+ case _7Z_DEFLATE:
+ case _7Z_ZSTD:
+ case _7Z_PPMD:
+ if (coder2 != NULL) {
+ if (coder2->codec != _7Z_X86 &&
+ coder2->codec != _7Z_X86_BCJ2 &&
+ coder2->codec != _7Z_ARM &&
+ coder2->codec != _7Z_ARM64) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Unsupported filter %lx for %lx",
+ coder2->codec, coder1->codec);
+ return (ARCHIVE_FAILED);
+ }
+ zip->codec2 = coder2->codec;
+ zip->bcj_state = 0;
+ if (coder2->codec == _7Z_X86)
+ x86_Init(zip);
+ else if (coder2->codec == _7Z_ARM)
+ arm_Init(zip);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (zip->codec) {
+ case _7Z_COPY:
+ break;
+
+ case _7Z_LZMA: case _7Z_LZMA2:
+#ifdef HAVE_LZMA_H
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT (1U << 30)
+#endif
+ {
+ lzma_options_delta delta_opt;
+ lzma_filter filters[LZMA_FILTERS_MAX], *ff;
+ int fi = 0;
+
+ if (zip->lzstream_valid) {
+ lzma_end(&(zip->lzstream));
+ zip->lzstream_valid = 0;
+ }
+
+ /*
+ * NOTE: liblzma incompletely handle the BCJ+LZMA compressed
+ * data made by 7-Zip because 7-Zip does not add End-Of-
+ * Payload Marker(EOPM) at the end of LZMA compressed data,
+ * and so liblzma cannot know the end of the compressed data
+ * without EOPM. So consequently liblzma will not return last
+ * three or four bytes of uncompressed data because
+ * LZMA_FILTER_X86 filter does not handle input data if its
+ * data size is less than five bytes. If liblzma detect EOPM
+ * or know the uncompressed data size, liblzma will flush out
+ * the remaining that three or four bytes of uncompressed
+ * data. That is why we have to use our converting program
+ * for BCJ+LZMA. If we were able to tell the uncompressed
+ * size to liblzma when using lzma_raw_decoder() liblzma
+ * could correctly deal with BCJ+LZMA. But unfortunately
+ * there is no way to do that.
+ * Discussion about this can be found at XZ Utils forum.
+ */
+ if (coder2 != NULL) {
+ zip->codec2 = coder2->codec;
+
+ filters[fi].options = NULL;
+ switch (zip->codec2) {
+ case _7Z_X86:
+ if (zip->codec == _7Z_LZMA2) {
+ filters[fi].id = LZMA_FILTER_X86;
+ fi++;
+ } else
+ /* Use our filter. */
+ x86_Init(zip);
+ break;
+ case _7Z_X86_BCJ2:
+ /* Use our filter. */
+ zip->bcj_state = 0;
+ break;
+ case _7Z_DELTA:
+ if (coder2->propertiesSize != 1) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Delta parameter");
+ return (ARCHIVE_FAILED);
+ }
+ filters[fi].id = LZMA_FILTER_DELTA;
+ memset(&delta_opt, 0, sizeof(delta_opt));
+ delta_opt.type = LZMA_DELTA_TYPE_BYTE;
+ delta_opt.dist =
+ (uint32_t)coder2->properties[0] + 1;
+ filters[fi].options = &delta_opt;
+ fi++;
+ break;
+ /* Following filters have not been tested yet. */
+ case _7Z_POWERPC:
+ filters[fi].id = LZMA_FILTER_POWERPC;
+ fi++;
+ break;
+ case _7Z_IA64:
+ filters[fi].id = LZMA_FILTER_IA64;
+ fi++;
+ break;
+ case _7Z_ARM:
+ filters[fi].id = LZMA_FILTER_ARM;
+ fi++;
+ break;
+ case _7Z_ARMTHUMB:
+ filters[fi].id = LZMA_FILTER_ARMTHUMB;
+ fi++;
+ break;
+#ifdef LZMA_FILTER_ARM64
+ case _7Z_ARM64:
+ filters[fi].id = LZMA_FILTER_ARM64;
+ fi++;
+ break;
+#endif
+ case _7Z_SPARC:
+ filters[fi].id = LZMA_FILTER_SPARC;
+ fi++;
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Unexpected codec ID: %lX", zip->codec2);
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ if (zip->codec == _7Z_LZMA2)
+ filters[fi].id = LZMA_FILTER_LZMA2;
+ else
+ filters[fi].id = LZMA_FILTER_LZMA1;
+ filters[fi].options = NULL;
+ ff = &filters[fi];
+ r = lzma_properties_decode(&filters[fi], NULL,
+ coder1->properties, (size_t)coder1->propertiesSize);
+ if (r != LZMA_OK) {
+ set_error(a, r);
+ return (ARCHIVE_FAILED);
+ }
+ fi++;
+
+ filters[fi].id = LZMA_VLI_UNKNOWN;
+ filters[fi].options = NULL;
+ r = lzma_raw_decoder(&(zip->lzstream), filters);
+ free(ff->options);
+ if (r != LZMA_OK) {
+ set_error(a, r);
+ return (ARCHIVE_FAILED);
+ }
+ zip->lzstream_valid = 1;
+ zip->lzstream.total_in = 0;
+ zip->lzstream.total_out = 0;
+ break;
+ }
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LZMA codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ case _7Z_BZ2:
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ if (zip->bzstream_valid) {
+ BZ2_bzDecompressEnd(&(zip->bzstream));
+ zip->bzstream_valid = 0;
+ }
+ r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 0);
+ if (r == BZ_MEM_ERROR)
+ r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 1);
+ if (r != BZ_OK) {
+ int err = ARCHIVE_ERRNO_MISC;
+ const char *detail = NULL;
+ switch (r) {
+ case BZ_PARAM_ERROR:
+ detail = "invalid setup parameter";
+ break;
+ case BZ_MEM_ERROR:
+ err = ENOMEM;
+ detail = "out of memory";
+ break;
+ case BZ_CONFIG_ERROR:
+ detail = "mis-compiled library";
+ break;
+ }
+ archive_set_error(&a->archive, err,
+ "Internal error initializing decompressor: %s",
+ detail != NULL ? detail : "??");
+ zip->bzstream_valid = 0;
+ return (ARCHIVE_FAILED);
+ }
+ zip->bzstream_valid = 1;
+ zip->bzstream.total_in_lo32 = 0;
+ zip->bzstream.total_in_hi32 = 0;
+ zip->bzstream.total_out_lo32 = 0;
+ zip->bzstream.total_out_hi32 = 0;
+ break;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "BZ2 codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ case _7Z_ZSTD:
+ {
+#if defined(HAVE_ZSTD_H)
+ if (zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstd_dstream);
+ zip->zstdstream_valid = 0;
+ }
+ zip->zstd_dstream = ZSTD_createDStream();
+ zip->zstdstream_valid = 1;
+ break;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZSTD codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ }
+ case _7Z_DEFLATE:
+#ifdef HAVE_ZLIB_H
+ if (zip->stream_valid)
+ r = inflateReset(&(zip->stream));
+ else
+ r = inflateInit2(&(zip->stream),
+ -15 /* Don't check for zlib header */);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Couldn't initialize zlib stream.");
+ return (ARCHIVE_FAILED);
+ }
+ zip->stream_valid = 1;
+ zip->stream.total_in = 0;
+ zip->stream.total_out = 0;
+ break;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "DEFLATE codec is unsupported");
+ return (ARCHIVE_FAILED);
+#endif
+ case _7Z_PPMD:
+ {
+ unsigned order;
+ uint32_t msize;
+
+ if (zip->ppmd7_valid) {
+ __archive_ppmd7_functions.Ppmd7_Free(
+ &zip->ppmd7_context);
+ zip->ppmd7_valid = 0;
+ }
+
+ if (coder1->propertiesSize < 5) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed PPMd parameter");
+ return (ARCHIVE_FAILED);
+ }
+ order = coder1->properties[0];
+ msize = archive_le32dec(&(coder1->properties[1]));
+ if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER ||
+ msize < PPMD7_MIN_MEM_SIZE || msize > PPMD7_MAX_MEM_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed PPMd parameter");
+ return (ARCHIVE_FAILED);
+ }
+ __archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context);
+ r = __archive_ppmd7_functions.Ppmd7_Alloc(
+ &zip->ppmd7_context, msize);
+ if (r == 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Coludn't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_ppmd7_functions.Ppmd7_Init(
+ &zip->ppmd7_context, order);
+ __archive_ppmd7_functions.Ppmd7z_RangeDec_CreateVTable(
+ &zip->range_dec);
+ zip->ppmd7_valid = 1;
+ zip->ppmd7_stat = 0;
+ zip->ppstream.overconsumed = 0;
+ zip->ppstream.total_in = 0;
+ zip->ppstream.total_out = 0;
+ break;
+ }
+ case _7Z_X86:
+ case _7Z_X86_BCJ2:
+ case _7Z_POWERPC:
+ case _7Z_IA64:
+ case _7Z_ARM:
+ case _7Z_ARMTHUMB:
+ case _7Z_ARM64:
+ case _7Z_SPARC:
+ case _7Z_DELTA:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Unexpected codec ID: %lX", zip->codec);
+ return (ARCHIVE_FAILED);
+ case _7Z_CRYPTO_MAIN_ZIP:
+ case _7Z_CRYPTO_RAR_29:
+ case _7Z_CRYPTO_AES_256_SHA_256:
+ if (a->entry) {
+ archive_entry_set_is_metadata_encrypted(a->entry, 1);
+ archive_entry_set_is_data_encrypted(a->entry, 1);
+ zip->has_encrypted_entries = 1;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Crypto codec not supported yet (ID: 0x%lX)", zip->codec);
+ return (ARCHIVE_FAILED);
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Unknown codec ID: %lX", zip->codec);
+ return (ARCHIVE_FAILED);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+decompress(struct archive_read *a, struct _7zip *zip,
+ void *buff, size_t *outbytes, const void *b, size_t *used)
+{
+ const uint8_t *t_next_in;
+ uint8_t *t_next_out;
+ size_t o_avail_in, o_avail_out;
+ size_t t_avail_in, t_avail_out;
+ uint8_t *bcj2_next_out;
+ size_t bcj2_avail_out;
+ int r, ret = ARCHIVE_OK;
+
+ t_avail_in = o_avail_in = *used;
+ t_avail_out = o_avail_out = *outbytes;
+ t_next_in = b;
+ t_next_out = buff;
+
+ if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {
+ int i;
+
+ /* Do not copy out the BCJ remaining bytes when the output
+ * buffer size is less than five bytes. */
+ if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) {
+ *used = 0;
+ *outbytes = 0;
+ return (ret);
+ }
+ for (i = 0; zip->odd_bcj_size > 0 && t_avail_out; i++) {
+ *t_next_out++ = zip->odd_bcj[i];
+ t_avail_out--;
+ zip->odd_bcj_size--;
+ }
+ if (o_avail_in == 0 || t_avail_out == 0) {
+ *used = o_avail_in - t_avail_in;
+ *outbytes = o_avail_out - t_avail_out;
+ if (o_avail_in == 0)
+ ret = ARCHIVE_EOF;
+ return (ret);
+ }
+ }
+
+ bcj2_next_out = t_next_out;
+ bcj2_avail_out = t_avail_out;
+ if (zip->codec2 == _7Z_X86_BCJ2) {
+ /*
+ * Decord a remaining decompressed main stream for BCJ2.
+ */
+ if (zip->tmp_stream_bytes_remaining) {
+ ssize_t bytes;
+ size_t remaining = zip->tmp_stream_bytes_remaining;
+ bytes = Bcj2_Decode(zip, t_next_out, t_avail_out);
+ if (bytes < 0) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "BCJ2 conversion Failed");
+ return (ARCHIVE_FAILED);
+ }
+ zip->main_stream_bytes_remaining -=
+ remaining - zip->tmp_stream_bytes_remaining;
+ t_avail_out -= bytes;
+ if (o_avail_in == 0 || t_avail_out == 0) {
+ *used = 0;
+ *outbytes = o_avail_out - t_avail_out;
+ if (o_avail_in == 0 &&
+ zip->tmp_stream_bytes_remaining)
+ ret = ARCHIVE_EOF;
+ return (ret);
+ }
+ t_next_out += bytes;
+ bcj2_next_out = t_next_out;
+ bcj2_avail_out = t_avail_out;
+ }
+ t_next_out = zip->tmp_stream_buff;
+ t_avail_out = zip->tmp_stream_buff_size;
+ }
+
+ switch (zip->codec) {
+ case _7Z_COPY:
+ {
+ size_t bytes =
+ (t_avail_in > t_avail_out)?t_avail_out:t_avail_in;
+
+ memcpy(t_next_out, t_next_in, bytes);
+ t_avail_in -= bytes;
+ t_avail_out -= bytes;
+ if (o_avail_in == 0)
+ ret = ARCHIVE_EOF;
+ break;
+ }
+#ifdef HAVE_LZMA_H
+ case _7Z_LZMA: case _7Z_LZMA2:
+ zip->lzstream.next_in = t_next_in;
+ zip->lzstream.avail_in = t_avail_in;
+ zip->lzstream.next_out = t_next_out;
+ zip->lzstream.avail_out = t_avail_out;
+
+ r = lzma_code(&(zip->lzstream), LZMA_RUN);
+ switch (r) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ lzma_end(&(zip->lzstream));
+ zip->lzstream_valid = 0;
+ ret = ARCHIVE_EOF;
+ break;
+ case LZMA_OK: /* Decompressor made some progress. */
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Decompression failed(%d)",
+ r);
+ return (ARCHIVE_FAILED);
+ }
+ t_avail_in = zip->lzstream.avail_in;
+ t_avail_out = zip->lzstream.avail_out;
+ break;
+#endif
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ case _7Z_BZ2:
+ zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;
+ zip->bzstream.avail_in = (uint32_t)t_avail_in;
+ zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
+ zip->bzstream.avail_out = (uint32_t)t_avail_out;
+ r = BZ2_bzDecompress(&(zip->bzstream));
+ switch (r) {
+ case BZ_STREAM_END: /* Found end of stream. */
+ switch (BZ2_bzDecompressEnd(&(zip->bzstream))) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up decompressor");
+ return (ARCHIVE_FAILED);
+ }
+ zip->bzstream_valid = 0;
+ ret = ARCHIVE_EOF;
+ break;
+ case BZ_OK: /* Decompressor made some progress. */
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "bzip decompression failed");
+ return (ARCHIVE_FAILED);
+ }
+ t_avail_in = zip->bzstream.avail_in;
+ t_avail_out = zip->bzstream.avail_out;
+ break;
+#endif
+#ifdef HAVE_ZLIB_H
+ case _7Z_DEFLATE:
+ zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in;
+ zip->stream.avail_in = (uInt)t_avail_in;
+ zip->stream.next_out = t_next_out;
+ zip->stream.avail_out = (uInt)t_avail_out;
+ r = inflate(&(zip->stream), 0);
+ switch (r) {
+ case Z_STREAM_END: /* Found end of stream. */
+ ret = ARCHIVE_EOF;
+ break;
+ case Z_OK: /* Decompressor made some progress.*/
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "File decompression failed (%d)", r);
+ return (ARCHIVE_FAILED);
+ }
+ t_avail_in = zip->stream.avail_in;
+ t_avail_out = zip->stream.avail_out;
+ break;
+#endif
+#ifdef HAVE_ZSTD_H
+ case _7Z_ZSTD:
+ {
+ ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos
+ ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos
+
+ size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input);
+ if (ZSTD_isError(zret)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret));
+ return ARCHIVE_FAILED;
+ }
+ t_avail_in -= input.pos;
+ t_avail_out -= output.pos;
+ break;
+ }
+#endif
+ case _7Z_PPMD:
+ {
+ uint64_t flush_bytes;
+
+ if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 ||
+ t_avail_out <= 0) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Decompression internal error");
+ return (ARCHIVE_FAILED);
+ }
+ zip->ppstream.next_in = t_next_in;
+ zip->ppstream.avail_in = t_avail_in;
+ zip->ppstream.stream_in = 0;
+ zip->ppstream.next_out = t_next_out;
+ zip->ppstream.avail_out = t_avail_out;
+ if (zip->ppmd7_stat == 0) {
+ zip->bytein.a = a;
+ zip->bytein.Read = &ppmd_read;
+ zip->range_dec.Stream = &zip->bytein;
+ r = __archive_ppmd7_functions.Ppmd7z_RangeDec_Init(
+ &(zip->range_dec));
+ if (r == 0) {
+ zip->ppmd7_stat = -1;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to initialize PPMd range decoder");
+ return (ARCHIVE_FAILED);
+ }
+ if (zip->ppstream.overconsumed) {
+ zip->ppmd7_stat = -1;
+ return (ARCHIVE_FAILED);
+ }
+ zip->ppmd7_stat = 1;
+ }
+
+ if (t_avail_in == 0)
+ /* XXX Flush out remaining decoded data XXX */
+ flush_bytes = zip->folder_outbytes_remaining;
+ else
+ flush_bytes = 0;
+
+ do {
+ int sym;
+
+ sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &(zip->ppmd7_context), &(zip->range_dec.p));
+ if (sym < 0) {
+ zip->ppmd7_stat = -1;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to decode PPMd");
+ return (ARCHIVE_FAILED);
+ }
+ if (zip->ppstream.overconsumed) {
+ zip->ppmd7_stat = -1;
+ return (ARCHIVE_FAILED);
+ }
+ *zip->ppstream.next_out++ = (unsigned char)sym;
+ zip->ppstream.avail_out--;
+ zip->ppstream.total_out++;
+ if (flush_bytes)
+ flush_bytes--;
+ } while (zip->ppstream.avail_out &&
+ (zip->ppstream.avail_in || flush_bytes));
+
+ t_avail_in = (size_t)zip->ppstream.avail_in;
+ t_avail_out = (size_t)zip->ppstream.avail_out;
+ break;
+ }
+ default:
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Decompression internal error");
+ return (ARCHIVE_FAILED);
+ }
+ if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF)
+ return (ret);
+
+ *used = o_avail_in - t_avail_in;
+ *outbytes = o_avail_out - t_avail_out;
+
+ /*
+ * Decord BCJ.
+ */
+ if (zip->codec != _7Z_LZMA2) {
+ if (zip->codec2 == _7Z_X86) {
+ size_t l = x86_Convert(zip, buff, *outbytes);
+
+ zip->odd_bcj_size = *outbytes - l;
+ if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
+ o_avail_in && ret != ARCHIVE_EOF) {
+ memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
+ zip->odd_bcj_size);
+ *outbytes = l;
+ } else
+ zip->odd_bcj_size = 0;
+ } else if (zip->codec2 == _7Z_ARM) {
+ *outbytes = arm_Convert(zip, buff, *outbytes);
+ } else if (zip->codec2 == _7Z_ARM64) {
+ *outbytes = arm64_Convert(zip, buff, *outbytes);
+ }
+ }
+
+ /*
+ * Decord BCJ2 with a decompressed main stream.
+ */
+ if (zip->codec2 == _7Z_X86_BCJ2) {
+ ssize_t bytes;
+
+ zip->tmp_stream_bytes_avail =
+ zip->tmp_stream_buff_size - t_avail_out;
+ if (zip->tmp_stream_bytes_avail >
+ zip->main_stream_bytes_remaining)
+ zip->tmp_stream_bytes_avail =
+ zip->main_stream_bytes_remaining;
+ zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail;
+ bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out);
+ if (bytes < 0) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed");
+ return (ARCHIVE_FAILED);
+ }
+ zip->main_stream_bytes_remaining -=
+ zip->tmp_stream_bytes_avail
+ - zip->tmp_stream_bytes_remaining;
+ bcj2_avail_out -= bytes;
+ *outbytes = o_avail_out - bcj2_avail_out;
+ }
+
+ return (ret);
+}
+
+static int
+free_decompression(struct archive_read *a, struct _7zip *zip)
+{
+ int r = ARCHIVE_OK;
+
+#if !defined(HAVE_ZLIB_H) &&\
+ !(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR))
+ (void)a;/* UNUSED */
+#endif
+#ifdef HAVE_LZMA_H
+ if (zip->lzstream_valid)
+ lzma_end(&(zip->lzstream));
+#endif
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ if (zip->bzstream_valid) {
+ if (BZ2_bzDecompressEnd(&(zip->bzstream)) != BZ_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up bzip2 decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ zip->bzstream_valid = 0;
+ }
+#endif
+#ifdef HAVE_ZLIB_H
+ if (zip->stream_valid) {
+ if (inflateEnd(&(zip->stream)) != Z_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up zlib decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ zip->stream_valid = 0;
+ }
+#endif
+ if (zip->ppmd7_valid) {
+ __archive_ppmd7_functions.Ppmd7_Free(
+ &zip->ppmd7_context);
+ zip->ppmd7_valid = 0;
+ }
+ return (r);
+}
+
+static int
+parse_7zip_uint64(struct archive_read *a, uint64_t *val)
+{
+ const unsigned char *p;
+ unsigned char avail, mask;
+ int i;
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ avail = *p;
+ mask = 0x80;
+ *val = 0;
+ for (i = 0; i < 8; i++) {
+ if (avail & mask) {
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ *val |= ((uint64_t)*p) << (8 * i);
+ mask >>= 1;
+ continue;
+ }
+ *val += ((uint64_t)(avail & (mask -1))) << (8 * i);
+ break;
+ }
+ return (0);
+}
+
+static int
+read_Bools(struct archive_read *a, unsigned char *data, size_t num)
+{
+ const unsigned char *p;
+ unsigned i, mask = 0, avail = 0;
+
+ for (i = 0; i < num; i++) {
+ if (mask == 0) {
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ avail = *p;
+ mask = 0x80;
+ }
+ data[i] = (avail & mask)?1:0;
+ mask >>= 1;
+ }
+ return (0);
+}
+
+static void
+free_Digest(struct _7z_digests *d)
+{
+ free(d->defineds);
+ free(d->digests);
+}
+
+static int
+read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num)
+{
+ const unsigned char *p;
+ unsigned i;
+
+ if (num == 0)
+ return (-1);
+ memset(d, 0, sizeof(*d));
+
+ d->defineds = malloc(num);
+ if (d->defineds == NULL)
+ return (-1);
+ /*
+ * Read Bools.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == 0) {
+ if (read_Bools(a, d->defineds, num) < 0)
+ return (-1);
+ } else
+ /* All are defined */
+ memset(d->defineds, 1, num);
+
+ d->digests = calloc(num, sizeof(*d->digests));
+ if (d->digests == NULL)
+ return (-1);
+ for (i = 0; i < num; i++) {
+ if (d->defineds[i]) {
+ if ((p = header_bytes(a, 4)) == NULL)
+ return (-1);
+ d->digests[i] = archive_le32dec(p);
+ }
+ }
+
+ return (0);
+}
+
+static void
+free_PackInfo(struct _7z_pack_info *pi)
+{
+ free(pi->sizes);
+ free(pi->positions);
+ free_Digest(&(pi->digest));
+}
+
+static int
+read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
+{
+ const unsigned char *p;
+ unsigned i;
+
+ memset(pi, 0, sizeof(*pi));
+
+ /*
+ * Read PackPos.
+ */
+ if (parse_7zip_uint64(a, &(pi->pos)) < 0)
+ return (-1);
+
+ /*
+ * Read NumPackStreams.
+ */
+ if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0)
+ return (-1);
+ if (pi->numPackStreams == 0)
+ return (-1);
+ if (UMAX_ENTRY < pi->numPackStreams)
+ return (-1);
+
+ /*
+ * Read PackSizes[num]
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == kEnd)
+ /* PackSizes[num] are not present. */
+ return (0);
+ if (*p != kSize)
+ return (-1);
+ pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+ pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+ if (pi->sizes == NULL || pi->positions == NULL)
+ return (-1);
+
+ for (i = 0; i < pi->numPackStreams; i++) {
+ if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0)
+ return (-1);
+ }
+
+ /*
+ * Read PackStreamDigests[num]
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == kEnd) {
+ /* PackStreamDigests[num] are not present. */
+ pi->digest.defineds =
+ calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds));
+ pi->digest.digests =
+ calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests));
+ if (pi->digest.defineds == NULL || pi->digest.digests == NULL)
+ return (-1);
+ return (0);
+ }
+
+ if (*p != kCRC)
+ return (-1);
+
+ if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)
+ return (-1);
+
+ /*
+ * Must be marked by kEnd.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p != kEnd)
+ return (-1);
+ return (0);
+}
+
+static void
+free_Folder(struct _7z_folder *f)
+{
+ unsigned i;
+
+ if (f->coders) {
+ for (i = 0; i< f->numCoders; i++) {
+ free(f->coders[i].properties);
+ }
+ free(f->coders);
+ }
+ free(f->bindPairs);
+ free(f->packedStreams);
+ free(f->unPackSize);
+}
+
+static int
+read_Folder(struct archive_read *a, struct _7z_folder *f)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+ uint64_t numInStreamsTotal = 0;
+ uint64_t numOutStreamsTotal = 0;
+ unsigned i;
+
+ memset(f, 0, sizeof(*f));
+
+ /*
+ * Read NumCoders.
+ */
+ if (parse_7zip_uint64(a, &(f->numCoders)) < 0)
+ return (-1);
+ if (f->numCoders > 4)
+ /* Too many coders. */
+ return (-1);
+
+ f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders));
+ if (f->coders == NULL)
+ return (-1);
+ for (i = 0; i< f->numCoders; i++) {
+ size_t codec_size;
+ int simple, attr;
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ /*
+ * 0:3 CodecIdSize
+ * 4: 0 - IsSimple
+ * 1 - Is not Simple
+ * 5: 0 - No Attributes
+ * 1 - There are Attributes;
+ * 7: Must be zero.
+ */
+ codec_size = *p & 0xf;
+ simple = (*p & 0x10)?0:1;
+ attr = *p & 0x20;
+ if (*p & 0x80)
+ return (-1);/* Not supported. */
+
+ /*
+ * Read Decompression Method IDs.
+ */
+ if ((p = header_bytes(a, codec_size)) == NULL)
+ return (-1);
+
+ f->coders[i].codec = decode_codec_id(p, codec_size);
+
+ if (simple) {
+ f->coders[i].numInStreams = 1;
+ f->coders[i].numOutStreams = 1;
+ } else {
+ if (parse_7zip_uint64(
+ a, &(f->coders[i].numInStreams)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->coders[i].numInStreams)
+ return (-1);
+ if (parse_7zip_uint64(
+ a, &(f->coders[i].numOutStreams)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->coders[i].numOutStreams)
+ return (-1);
+ }
+
+ if (attr) {
+ if (parse_7zip_uint64(
+ a, &(f->coders[i].propertiesSize)) < 0)
+ return (-1);
+ if ((p = header_bytes(
+ a, (size_t)f->coders[i].propertiesSize)) == NULL)
+ return (-1);
+ f->coders[i].properties =
+ malloc((size_t)f->coders[i].propertiesSize);
+ if (f->coders[i].properties == NULL)
+ return (-1);
+ memcpy(f->coders[i].properties, p,
+ (size_t)f->coders[i].propertiesSize);
+ }
+
+ numInStreamsTotal += f->coders[i].numInStreams;
+ numOutStreamsTotal += f->coders[i].numOutStreams;
+ }
+
+ if (numOutStreamsTotal == 0 ||
+ numInStreamsTotal < numOutStreamsTotal-1)
+ return (-1);
+
+ f->numBindPairs = numOutStreamsTotal - 1;
+ if (zip->header_bytes_remaining < f->numBindPairs)
+ return (-1);
+ if (f->numBindPairs > 0) {
+ f->bindPairs =
+ calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs));
+ if (f->bindPairs == NULL)
+ return (-1);
+ } else
+ f->bindPairs = NULL;
+ for (i = 0; i < f->numBindPairs; i++) {
+ if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->bindPairs[i].inIndex)
+ return (-1);
+ if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->bindPairs[i].outIndex)
+ return (-1);
+ }
+
+ f->numPackedStreams = numInStreamsTotal - f->numBindPairs;
+ f->packedStreams =
+ calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams));
+ if (f->packedStreams == NULL)
+ return (-1);
+ if (f->numPackedStreams == 1) {
+ for (i = 0; i < numInStreamsTotal; i++) {
+ unsigned j;
+ for (j = 0; j < f->numBindPairs; j++) {
+ if (f->bindPairs[j].inIndex == i)
+ break;
+ }
+ if (j == f->numBindPairs)
+ break;
+ }
+ if (i == numInStreamsTotal)
+ return (-1);
+ f->packedStreams[0] = i;
+ } else {
+ for (i = 0; i < f->numPackedStreams; i++) {
+ if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f->packedStreams[i])
+ return (-1);
+ }
+ }
+ f->numInStreams = numInStreamsTotal;
+ f->numOutStreams = numOutStreamsTotal;
+
+ return (0);
+}
+
+static void
+free_CodersInfo(struct _7z_coders_info *ci)
+{
+ unsigned i;
+
+ if (ci->folders) {
+ for (i = 0; i < ci->numFolders; i++)
+ free_Folder(&(ci->folders[i]));
+ free(ci->folders);
+ }
+}
+
+static int
+read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
+{
+ const unsigned char *p;
+ struct _7z_digests digest;
+ unsigned i;
+
+ memset(ci, 0, sizeof(*ci));
+ memset(&digest, 0, sizeof(digest));
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p != kFolder)
+ goto failed;
+
+ /*
+ * Read NumFolders.
+ */
+ if (parse_7zip_uint64(a, &(ci->numFolders)) < 0)
+ goto failed;
+ if (UMAX_ENTRY < ci->numFolders)
+ return (-1);
+
+ /*
+ * Read External.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ switch (*p) {
+ case 0:
+ ci->folders =
+ calloc((size_t)ci->numFolders, sizeof(*ci->folders));
+ if (ci->folders == NULL)
+ return (-1);
+ for (i = 0; i < ci->numFolders; i++) {
+ if (read_Folder(a, &(ci->folders[i])) < 0)
+ goto failed;
+ }
+ break;
+ case 1:
+ if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < ci->dataStreamIndex)
+ return (-1);
+ if (ci->numFolders > 0) {
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ goto failed;
+ }
+ break;
+ default:
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ goto failed;
+ }
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p != kCodersUnPackSize)
+ goto failed;
+
+ for (i = 0; i < ci->numFolders; i++) {
+ struct _7z_folder *folder = &(ci->folders[i]);
+ unsigned j;
+
+ folder->unPackSize =
+ calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize));
+ if (folder->unPackSize == NULL)
+ goto failed;
+ for (j = 0; j < folder->numOutStreams; j++) {
+ if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0)
+ goto failed;
+ }
+ }
+
+ /*
+ * Read CRCs.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p == kEnd)
+ return (0);
+ if (*p != kCRC)
+ goto failed;
+ if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0)
+ goto failed;
+ for (i = 0; i < ci->numFolders; i++) {
+ ci->folders[i].digest_defined = digest.defineds[i];
+ ci->folders[i].digest = digest.digests[i];
+ }
+
+ /*
+ * Must be kEnd.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p != kEnd)
+ goto failed;
+ free_Digest(&digest);
+ return (0);
+failed:
+ free_Digest(&digest);
+ return (-1);
+}
+
+static uint64_t
+folder_uncompressed_size(struct _7z_folder *f)
+{
+ int n = (int)f->numOutStreams;
+ unsigned pairs = (unsigned)f->numBindPairs;
+
+ while (--n >= 0) {
+ unsigned i;
+ for (i = 0; i < pairs; i++) {
+ if (f->bindPairs[i].outIndex == (uint64_t)n)
+ break;
+ }
+ if (i >= pairs)
+ return (f->unPackSize[n]);
+ }
+ return (0);
+}
+
+static void
+free_SubStreamsInfo(struct _7z_substream_info *ss)
+{
+ free(ss->unpackSizes);
+ free(ss->digestsDefined);
+ free(ss->digests);
+}
+
+static int
+read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
+ struct _7z_folder *f, size_t numFolders)
+{
+ const unsigned char *p;
+ uint64_t *usizes;
+ size_t unpack_streams;
+ int type;
+ unsigned i;
+ uint32_t numDigests;
+
+ memset(ss, 0, sizeof(*ss));
+
+ for (i = 0; i < numFolders; i++)
+ f[i].numUnpackStreams = 1;
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+
+ if (type == kNumUnPackStream) {
+ unpack_streams = 0;
+ for (i = 0; i < numFolders; i++) {
+ if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < f[i].numUnpackStreams)
+ return (-1);
+ if (unpack_streams > SIZE_MAX - UMAX_ENTRY) {
+ return (-1);
+ }
+ unpack_streams += (size_t)f[i].numUnpackStreams;
+ }
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+ } else
+ unpack_streams = numFolders;
+
+ ss->unpack_streams = unpack_streams;
+ if (unpack_streams) {
+ ss->unpackSizes = calloc(unpack_streams,
+ sizeof(*ss->unpackSizes));
+ ss->digestsDefined = calloc(unpack_streams,
+ sizeof(*ss->digestsDefined));
+ ss->digests = calloc(unpack_streams,
+ sizeof(*ss->digests));
+ if (ss->unpackSizes == NULL || ss->digestsDefined == NULL ||
+ ss->digests == NULL)
+ return (-1);
+ }
+
+ usizes = ss->unpackSizes;
+ for (i = 0; i < numFolders; i++) {
+ unsigned pack;
+ uint64_t sum;
+
+ if (f[i].numUnpackStreams == 0)
+ continue;
+
+ sum = 0;
+ if (type == kSize) {
+ for (pack = 1; pack < f[i].numUnpackStreams; pack++) {
+ if (parse_7zip_uint64(a, usizes) < 0)
+ return (-1);
+ sum += *usizes++;
+ }
+ }
+ *usizes++ = folder_uncompressed_size(&f[i]) - sum;
+ }
+
+ if (type == kSize) {
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+ }
+
+ for (i = 0; i < unpack_streams; i++) {
+ ss->digestsDefined[i] = 0;
+ ss->digests[i] = 0;
+ }
+
+ numDigests = 0;
+ for (i = 0; i < numFolders; i++) {
+ if (f[i].numUnpackStreams != 1 || !f[i].digest_defined)
+ numDigests += (uint32_t)f[i].numUnpackStreams;
+ }
+
+ if (type == kCRC) {
+ struct _7z_digests tmpDigests;
+ unsigned char *digestsDefined = ss->digestsDefined;
+ uint32_t * digests = ss->digests;
+ int di = 0;
+
+ memset(&tmpDigests, 0, sizeof(tmpDigests));
+ if (read_Digests(a, &(tmpDigests), numDigests) < 0) {
+ free_Digest(&tmpDigests);
+ return (-1);
+ }
+ for (i = 0; i < numFolders; i++) {
+ if (f[i].numUnpackStreams == 1 && f[i].digest_defined) {
+ *digestsDefined++ = 1;
+ *digests++ = f[i].digest;
+ } else {
+ unsigned j;
+
+ for (j = 0; j < f[i].numUnpackStreams;
+ j++, di++) {
+ *digestsDefined++ =
+ tmpDigests.defineds[di];
+ *digests++ =
+ tmpDigests.digests[di];
+ }
+ }
+ }
+ free_Digest(&tmpDigests);
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+ }
+
+ /*
+ * Must be kEnd.
+ */
+ if (type != kEnd)
+ return (-1);
+ return (0);
+}
+
+static void
+free_StreamsInfo(struct _7z_stream_info *si)
+{
+ free_PackInfo(&(si->pi));
+ free_CodersInfo(&(si->ci));
+ free_SubStreamsInfo(&(si->ss));
+}
+
+static int
+read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+ unsigned i;
+
+ memset(si, 0, sizeof(*si));
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == kPackInfo) {
+ uint64_t packPos;
+
+ if (read_PackInfo(a, &(si->pi)) < 0)
+ return (-1);
+
+ if (si->pi.positions == NULL || si->pi.sizes == NULL)
+ return (-1);
+ /*
+ * Calculate packed stream positions.
+ */
+ packPos = si->pi.pos;
+ for (i = 0; i < si->pi.numPackStreams; i++) {
+ si->pi.positions[i] = packPos;
+ packPos += si->pi.sizes[i];
+ if (packPos > zip->header_offset)
+ return (-1);
+ }
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+ if (*p == kUnPackInfo) {
+ uint32_t packIndex;
+ struct _7z_folder *f;
+
+ if (read_CodersInfo(a, &(si->ci)) < 0)
+ return (-1);
+
+ /*
+ * Calculate packed stream indexes.
+ */
+ packIndex = 0;
+ f = si->ci.folders;
+ for (i = 0; i < si->ci.numFolders; i++) {
+ f[i].packIndex = packIndex;
+ packIndex += (uint32_t)f[i].numPackedStreams;
+ if (packIndex > si->pi.numPackStreams)
+ return (-1);
+ }
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+
+ if (*p == kSubStreamsInfo) {
+ if (read_SubStreamsInfo(a, &(si->ss),
+ si->ci.folders, (size_t)si->ci.numFolders) < 0)
+ return (-1);
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+
+ /*
+ * Must be kEnd.
+ */
+ if (*p != kEnd)
+ return (-1);
+ return (0);
+}
+
+static void
+free_Header(struct _7z_header_info *h)
+{
+ free(h->emptyStreamBools);
+ free(h->emptyFileBools);
+ free(h->antiBools);
+ free(h->attrBools);
+}
+
+static int
+read_Header(struct archive_read *a, struct _7z_header_info *h,
+ int check_header_id)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+ struct _7z_folder *folders;
+ struct _7z_stream_info *si = &(zip->si);
+ struct _7zip_entry *entries;
+ uint32_t folderIndex, indexInFolder;
+ unsigned i;
+ int eindex, empty_streams, sindex;
+
+ if (check_header_id) {
+ /*
+ * Read Header.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p != kHeader)
+ return (-1);
+ }
+
+ /*
+ * Read ArchiveProperties.
+ */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == kArchiveProperties) {
+ for (;;) {
+ uint64_t size;
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ if (*p == 0)
+ break;
+ if (parse_7zip_uint64(a, &size) < 0)
+ return (-1);
+ }
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+
+ /*
+ * Read MainStreamsInfo.
+ */
+ if (*p == kMainStreamsInfo) {
+ if (read_StreamsInfo(a, &(zip->si)) < 0)
+ return (-1);
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ }
+ if (*p == kEnd)
+ return (0);
+
+ /*
+ * Read FilesInfo.
+ */
+ if (*p != kFilesInfo)
+ return (-1);
+
+ if (parse_7zip_uint64(a, &(zip->numFiles)) < 0)
+ return (-1);
+ if (UMAX_ENTRY < zip->numFiles)
+ return (-1);
+
+ zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));
+ if (zip->entries == NULL)
+ return (-1);
+ entries = zip->entries;
+
+ empty_streams = 0;
+ for (;;) {
+ int type;
+ uint64_t size;
+ size_t ll;
+
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ type = *p;
+ if (type == kEnd)
+ break;
+
+ if (parse_7zip_uint64(a, &size) < 0)
+ return (-1);
+ if (zip->header_bytes_remaining < size)
+ return (-1);
+ ll = (size_t)size;
+
+ switch (type) {
+ case kEmptyStream:
+ if (h->emptyStreamBools != NULL)
+ return (-1);
+ h->emptyStreamBools = calloc((size_t)zip->numFiles,
+ sizeof(*h->emptyStreamBools));
+ if (h->emptyStreamBools == NULL)
+ return (-1);
+ if (read_Bools(
+ a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)
+ return (-1);
+ empty_streams = 0;
+ for (i = 0; i < zip->numFiles; i++) {
+ if (h->emptyStreamBools[i])
+ empty_streams++;
+ }
+ break;
+ case kEmptyFile:
+ if (empty_streams <= 0) {
+ /* Unexcepted sequence. Skip this. */
+ if (header_bytes(a, ll) == NULL)
+ return (-1);
+ break;
+ }
+ if (h->emptyFileBools != NULL)
+ return (-1);
+ h->emptyFileBools = calloc(empty_streams,
+ sizeof(*h->emptyFileBools));
+ if (h->emptyFileBools == NULL)
+ return (-1);
+ if (read_Bools(a, h->emptyFileBools, empty_streams) < 0)
+ return (-1);
+ break;
+ case kAnti:
+ if (empty_streams <= 0) {
+ /* Unexcepted sequence. Skip this. */
+ if (header_bytes(a, ll) == NULL)
+ return (-1);
+ break;
+ }
+ if (h->antiBools != NULL)
+ return (-1);
+ h->antiBools = calloc(empty_streams,
+ sizeof(*h->antiBools));
+ if (h->antiBools == NULL)
+ return (-1);
+ if (read_Bools(a, h->antiBools, empty_streams) < 0)
+ return (-1);
+ break;
+ case kCTime:
+ case kATime:
+ case kMTime:
+ if (read_Times(a, h, type) < 0)
+ return (-1);
+ break;
+ case kName:
+ {
+ unsigned char *np;
+ size_t nl, nb;
+
+ /* Skip one byte. */
+ if ((p = header_bytes(a, 1)) == NULL)
+ return (-1);
+ ll--;
+
+ if ((ll & 1) || ll < zip->numFiles * 4)
+ return (-1);
+
+ if (zip->entry_names != NULL)
+ return (-1);
+ zip->entry_names = malloc(ll);
+ if (zip->entry_names == NULL)
+ return (-1);
+ np = zip->entry_names;
+ nb = ll;
+ /*
+ * Copy whole file names.
+ * NOTE: This loop prevents from expanding
+ * the uncompressed buffer in order not to
+ * use extra memory resource.
+ */
+ while (nb) {
+ size_t b;
+ if (nb > UBUFF_SIZE)
+ b = UBUFF_SIZE;
+ else
+ b = nb;
+ if ((p = header_bytes(a, b)) == NULL)
+ return (-1);
+ memcpy(np, p, b);
+ np += b;
+ nb -= b;
+ }
+ np = zip->entry_names;
+ nl = ll;
+
+ for (i = 0; i < zip->numFiles; i++) {
+ entries[i].utf16name = np;
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+ entries[i].wname = (wchar_t *)np;
+#endif
+
+ /* Find a terminator. */
+ while (nl >= 2 && (np[0] || np[1])) {
+ np += 2;
+ nl -= 2;
+ }
+ if (nl < 2)
+ return (-1);/* Terminator not found */
+ entries[i].name_len = np - entries[i].utf16name;
+ np += 2;
+ nl -= 2;
+ }
+ break;
+ }
+ case kAttributes:
+ {
+ int allAreDefined;
+
+ if ((p = header_bytes(a, 2)) == NULL)
+ return (-1);
+ allAreDefined = *p;
+ if (h->attrBools != NULL)
+ return (-1);
+ h->attrBools = calloc((size_t)zip->numFiles,
+ sizeof(*h->attrBools));
+ if (h->attrBools == NULL)
+ return (-1);
+ if (allAreDefined)
+ memset(h->attrBools, 1, (size_t)zip->numFiles);
+ else {
+ if (read_Bools(a, h->attrBools,
+ (size_t)zip->numFiles) < 0)
+ return (-1);
+ }
+ for (i = 0; i < zip->numFiles; i++) {
+ if (h->attrBools[i]) {
+ if ((p = header_bytes(a, 4)) == NULL)
+ return (-1);
+ entries[i].attr = archive_le32dec(p);
+ }
+ }
+ break;
+ }
+ case kDummy:
+ if (ll == 0)
+ break;
+ __LA_FALLTHROUGH;
+ default:
+ if (header_bytes(a, ll) == NULL)
+ return (-1);
+ break;
+ }
+ }
+
+ /*
+ * Set up entry's attributes.
+ */
+ folders = si->ci.folders;
+ eindex = sindex = 0;
+ folderIndex = indexInFolder = 0;
+ for (i = 0; i < zip->numFiles; i++) {
+ if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0)
+ entries[i].flg |= HAS_STREAM;
+ /* The high 16 bits of attributes is a posix file mode. */
+ entries[i].mode = entries[i].attr >> 16;
+
+ if (!(entries[i].attr & FILE_ATTRIBUTE_UNIX_EXTENSION)) {
+ // Only windows permissions specified for this entry. Translate to
+ // reasonable corresponding unix permissions.
+
+ if (entries[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
+ if (entries[i].attr & FILE_ATTRIBUTE_READONLY) {
+ // Read-only directory.
+ entries[i].mode = AE_IFDIR | 0555;
+ } else {
+ // Read-write directory.
+ entries[i].mode = AE_IFDIR | 0755;
+ }
+ } else if (entries[i].attr & FILE_ATTRIBUTE_READONLY) {
+ // Readonly file.
+ entries[i].mode = AE_IFREG | 0444;
+ } else {
+ // Assume read-write file.
+ entries[i].mode = AE_IFREG | 0644;
+ }
+ }
+
+ if (entries[i].flg & HAS_STREAM) {
+ if ((size_t)sindex >= si->ss.unpack_streams)
+ return (-1);
+ if (entries[i].mode == 0)
+ entries[i].mode = AE_IFREG | 0666;
+ if (si->ss.digestsDefined[sindex])
+ entries[i].flg |= CRC32_IS_SET;
+ entries[i].ssIndex = sindex;
+ sindex++;
+ } else {
+ int dir;
+ if (h->emptyFileBools == NULL)
+ dir = 1;
+ else {
+ if (h->emptyFileBools[eindex])
+ dir = 0;
+ else
+ dir = 1;
+ eindex++;
+ }
+ if (entries[i].mode == 0) {
+ if (dir)
+ entries[i].mode = AE_IFDIR | 0777;
+ else
+ entries[i].mode = AE_IFREG | 0666;
+ } else if (dir &&
+ (entries[i].mode & AE_IFMT) != AE_IFDIR) {
+ entries[i].mode &= ~AE_IFMT;
+ entries[i].mode |= AE_IFDIR;
+ }
+ if ((entries[i].mode & AE_IFMT) == AE_IFDIR &&
+ entries[i].name_len >= 2 &&
+ (entries[i].utf16name[entries[i].name_len-2] != '/' ||
+ entries[i].utf16name[entries[i].name_len-1] != 0)) {
+ entries[i].utf16name[entries[i].name_len] = '/';
+ entries[i].utf16name[entries[i].name_len+1] = 0;
+ entries[i].name_len += 2;
+ }
+ entries[i].ssIndex = -1;
+ }
+ if (entries[i].attr & FILE_ATTRIBUTE_READONLY)
+ entries[i].mode &= ~0222;/* Read only. */
+
+ if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) {
+ /*
+ * The entry is an empty file or a directory file,
+ * those both have no contents.
+ */
+ entries[i].folderIndex = -1;
+ continue;
+ }
+ if (indexInFolder == 0) {
+ for (;;) {
+ if (folderIndex >= si->ci.numFolders)
+ return (-1);
+ if (folders[folderIndex].numUnpackStreams)
+ break;
+ folderIndex++;
+ }
+ }
+ entries[i].folderIndex = folderIndex;
+ if ((entries[i].flg & HAS_STREAM) == 0)
+ continue;
+ indexInFolder++;
+ if (indexInFolder >= folders[folderIndex].numUnpackStreams) {
+ folderIndex++;
+ indexInFolder = 0;
+ }
+ }
+
+ return (0);
+}
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static void
+fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns)
+{
+
+ if (fileTime >= EPOC_TIME) {
+ fileTime -= EPOC_TIME;
+ /* milli seconds base */
+ *timep = (time_t)(fileTime / 10000000);
+ /* nano seconds base */
+ *ns = (long)(fileTime % 10000000) * 100;
+ } else {
+ *timep = 0;
+ *ns = 0;
+ }
+}
+
+static int
+read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+ struct _7zip_entry *entries = zip->entries;
+ unsigned char *timeBools;
+ int allAreDefined;
+ unsigned i;
+
+ timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools));
+ if (timeBools == NULL)
+ return (-1);
+
+ /* Read allAreDefined. */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ allAreDefined = *p;
+ if (allAreDefined)
+ memset(timeBools, 1, (size_t)zip->numFiles);
+ else {
+ if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0)
+ goto failed;
+ }
+
+ /* Read external. */
+ if ((p = header_bytes(a, 1)) == NULL)
+ goto failed;
+ if (*p) {
+ if (parse_7zip_uint64(a, &(h->dataIndex)) < 0)
+ goto failed;
+ if (UMAX_ENTRY < h->dataIndex)
+ goto failed;
+ }
+
+ for (i = 0; i < zip->numFiles; i++) {
+ if (!timeBools[i])
+ continue;
+ if ((p = header_bytes(a, 8)) == NULL)
+ goto failed;
+ switch (type) {
+ case kCTime:
+ fileTimeToUtc(archive_le64dec(p),
+ &(entries[i].ctime),
+ &(entries[i].ctime_ns));
+ entries[i].flg |= CTIME_IS_SET;
+ break;
+ case kATime:
+ fileTimeToUtc(archive_le64dec(p),
+ &(entries[i].atime),
+ &(entries[i].atime_ns));
+ entries[i].flg |= ATIME_IS_SET;
+ break;
+ case kMTime:
+ fileTimeToUtc(archive_le64dec(p),
+ &(entries[i].mtime),
+ &(entries[i].mtime_ns));
+ entries[i].flg |= MTIME_IS_SET;
+ break;
+ }
+ }
+
+ free(timeBools);
+ return (0);
+failed:
+ free(timeBools);
+ return (-1);
+}
+
+static int
+decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+
+ errno = 0;
+ if (read_StreamsInfo(a, si) < 0) {
+ if (errno == ENOMEM)
+ archive_set_error(&a->archive, -1,
+ "Couldn't allocate memory");
+ else
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) {
+ archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (zip->header_offset < si->pi.pos + si->pi.sizes[0] ||
+ (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 ||
+ si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) {
+ archive_set_error(&a->archive, -1, "Malformed Header offset");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static const unsigned char *
+header_bytes(struct archive_read *a, size_t rbytes)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const unsigned char *p;
+
+ if (zip->header_bytes_remaining < rbytes)
+ return (NULL);
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+
+ if (zip->header_is_encoded == 0) {
+ p = __archive_read_ahead(a, rbytes, NULL);
+ if (p == NULL)
+ return (NULL);
+ zip->header_bytes_remaining -= rbytes;
+ zip->pack_stream_bytes_unconsumed = rbytes;
+ } else {
+ const void *buff;
+ ssize_t bytes;
+
+ bytes = read_stream(a, &buff, rbytes, rbytes);
+ if (bytes <= 0)
+ return (NULL);
+ zip->header_bytes_remaining -= bytes;
+ p = buff;
+ }
+
+ /* Update checksum */
+ zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes);
+ return (p);
+}
+
+static int
+slurp_central_directory(struct archive_read *a, struct _7zip *zip,
+ struct _7z_header_info *header)
+{
+ const unsigned char *p;
+ uint64_t next_header_offset;
+ uint64_t next_header_size;
+ uint32_t next_header_crc;
+ ssize_t bytes_avail;
+ int check_header_crc, r;
+
+ if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)
+ return (ARCHIVE_FATAL);
+
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+ /* This is an executable ? Must be self-extracting... */
+ r = skip_sfx(a, bytes_avail);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ zip->seek_base += 32;
+
+ if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) {
+ archive_set_error(&a->archive, -1, "Not 7-Zip archive file");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* CRC check. */
+ if (crc32(0, (const unsigned char *)p + 12, 20)
+ != archive_le32dec(p + 8)) {
+#ifdef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, -1, "Header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+
+ next_header_offset = archive_le64dec(p + 12);
+ next_header_size = archive_le64dec(p + 20);
+ next_header_crc = archive_le32dec(p + 28);
+
+ if (next_header_size == 0)
+ /* There is no entry in an archive file. */
+ return (ARCHIVE_EOF);
+
+ if (((int64_t)next_header_offset) < 0) {
+ archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_read_consume(a, 32);
+ if (next_header_offset != 0) {
+ if (bytes_avail >= (ssize_t)next_header_offset)
+ __archive_read_consume(a, next_header_offset);
+ else if (__archive_read_seek(a,
+ next_header_offset + zip->seek_base, SEEK_SET) < 0)
+ return (ARCHIVE_FATAL);
+ }
+ zip->stream_offset = next_header_offset;
+ zip->header_offset = next_header_offset;
+ zip->header_bytes_remaining = next_header_size;
+ zip->header_crc32 = 0;
+ zip->header_is_encoded = 0;
+ zip->header_is_being_read = 1;
+ zip->has_encrypted_entries = 0;
+ check_header_crc = 1;
+
+ if ((p = header_bytes(a, 1)) == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ /* Parse ArchiveProperties. */
+ switch (p[0]) {
+ case kEncodedHeader:
+ /*
+ * The archive has an encoded header and we have to decode it
+ * in order to parse the header correctly.
+ */
+ r = decode_encoded_header_info(a, &(zip->si));
+
+ /* Check the EncodedHeader CRC.*/
+ if (r == 0 && zip->header_crc32 != next_header_crc) {
+ archive_set_error(&a->archive, -1,
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ "Damaged 7-Zip archive");
+ r = -1;
+#endif
+ }
+ if (r == 0) {
+ if (zip->si.ci.folders[0].digest_defined)
+ next_header_crc = zip->si.ci.folders[0].digest;
+ else
+ check_header_crc = 0;
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+ r = setup_decode_folder(a, zip->si.ci.folders, 1);
+ if (r == 0) {
+ zip->header_bytes_remaining =
+ zip->folder_outbytes_remaining;
+ r = seek_pack(a);
+ }
+ }
+ /* Clean up StreamsInfo. */
+ free_StreamsInfo(&(zip->si));
+ memset(&(zip->si), 0, sizeof(zip->si));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ zip->header_is_encoded = 1;
+ zip->header_crc32 = 0;
+ /* FALL THROUGH */
+ case kHeader:
+ /*
+ * Parse the header.
+ */
+ errno = 0;
+ r = read_Header(a, header, zip->header_is_encoded);
+ if (r < 0) {
+ if (errno == ENOMEM)
+ archive_set_error(&a->archive, -1,
+ "Couldn't allocate memory");
+ else
+ archive_set_error(&a->archive, -1,
+ "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Must be kEnd.
+ */
+ if ((p = header_bytes(a, 1)) == NULL ||*p != kEnd) {
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Check the Header CRC.*/
+ if (check_header_crc && zip->header_crc32 != next_header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, -1,
+ "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ break;
+ default:
+ archive_set_error(&a->archive, -1,
+ "Unexpected Property ID = %X", p[0]);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Clean up variables be used for decoding the archive header */
+ zip->pack_stream_remaining = 0;
+ zip->pack_stream_index = 0;
+ zip->folder_outbytes_remaining = 0;
+ zip->uncompressed_buffer_bytes_remaining = 0;
+ zip->pack_stream_bytes_unconsumed = 0;
+ zip->header_is_being_read = 0;
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
+ size_t minimum)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ ssize_t bytes_avail;
+
+ if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+ /* Copy mode. */
+
+ *buff = __archive_read_ahead(a, minimum, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file data");
+ return (ARCHIVE_FATAL);
+ }
+ if ((size_t)bytes_avail >
+ zip->uncompressed_buffer_bytes_remaining)
+ bytes_avail = (ssize_t)
+ zip->uncompressed_buffer_bytes_remaining;
+ if ((size_t)bytes_avail > size)
+ bytes_avail = (ssize_t)size;
+
+ zip->pack_stream_bytes_unconsumed = bytes_avail;
+ } else if (zip->uncompressed_buffer_pointer == NULL) {
+ /* Decompression has failed. */
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ } else {
+ /* Packed mode. */
+ if (minimum > zip->uncompressed_buffer_bytes_remaining) {
+ /*
+ * If remaining uncompressed data size is less than
+ * the minimum size, fill the buffer up to the
+ * minimum size.
+ */
+ if (extract_pack_stream(a, minimum) < 0)
+ return (ARCHIVE_FATAL);
+ }
+ if (size > zip->uncompressed_buffer_bytes_remaining)
+ bytes_avail = (ssize_t)
+ zip->uncompressed_buffer_bytes_remaining;
+ else
+ bytes_avail = (ssize_t)size;
+ *buff = zip->uncompressed_buffer_pointer;
+ zip->uncompressed_buffer_pointer += bytes_avail;
+ }
+ zip->uncompressed_buffer_bytes_remaining -= bytes_avail;
+ return (bytes_avail);
+}
+
+static ssize_t
+extract_pack_stream(struct archive_read *a, size_t minimum)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ ssize_t bytes_avail;
+ int r;
+
+ if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+ if (minimum == 0)
+ minimum = 1;
+ if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL
+ || bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ if ((uint64_t)bytes_avail > zip->pack_stream_inbytes_remaining)
+ bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;
+ zip->pack_stream_inbytes_remaining -= bytes_avail;
+ if ((uint64_t)bytes_avail > zip->folder_outbytes_remaining)
+ bytes_avail = (ssize_t)zip->folder_outbytes_remaining;
+ zip->folder_outbytes_remaining -= bytes_avail;
+ zip->uncompressed_buffer_bytes_remaining = bytes_avail;
+ return (ARCHIVE_OK);
+ }
+
+ /* If the buffer hasn't been allocated, allocate it now. */
+ if (zip->uncompressed_buffer == NULL) {
+ zip->uncompressed_buffer_size = UBUFF_SIZE;
+ if (zip->uncompressed_buffer_size < minimum) {
+ zip->uncompressed_buffer_size = minimum + 1023;
+ zip->uncompressed_buffer_size &= ~0x3ff;
+ }
+ zip->uncompressed_buffer =
+ malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for 7-Zip decompression");
+ return (ARCHIVE_FATAL);
+ }
+ zip->uncompressed_buffer_bytes_remaining = 0;
+ } else if (zip->uncompressed_buffer_size < minimum ||
+ zip->uncompressed_buffer_bytes_remaining < minimum) {
+ /*
+ * Make sure the uncompressed buffer can have bytes
+ * at least `minimum' bytes.
+ * NOTE: This case happen when reading the header.
+ */
+ size_t used;
+ if (zip->uncompressed_buffer_pointer != 0)
+ used = zip->uncompressed_buffer_pointer -
+ zip->uncompressed_buffer;
+ else
+ used = 0;
+ if (zip->uncompressed_buffer_size < minimum) {
+ /*
+ * Expand the uncompressed buffer up to
+ * the minimum size.
+ */
+ void *p;
+ size_t new_size;
+
+ new_size = minimum + 1023;
+ new_size &= ~0x3ff;
+ p = realloc(zip->uncompressed_buffer, new_size);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for 7-Zip decompression");
+ return (ARCHIVE_FATAL);
+ }
+ zip->uncompressed_buffer = (unsigned char *)p;
+ zip->uncompressed_buffer_size = new_size;
+ }
+ /*
+ * Move unconsumed bytes to the head.
+ */
+ if (used) {
+ memmove(zip->uncompressed_buffer,
+ zip->uncompressed_buffer + used,
+ zip->uncompressed_buffer_bytes_remaining);
+ }
+ } else
+ zip->uncompressed_buffer_bytes_remaining = 0;
+ zip->uncompressed_buffer_pointer = NULL;
+ for (;;) {
+ size_t bytes_in, bytes_out;
+ const void *buff_in;
+ unsigned char *buff_out;
+ int end_of_data;
+
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ buff_in = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ buff_out = zip->uncompressed_buffer
+ + zip->uncompressed_buffer_bytes_remaining;
+ bytes_out = zip->uncompressed_buffer_size
+ - zip->uncompressed_buffer_bytes_remaining;
+ bytes_in = bytes_avail;
+ if (bytes_in > zip->pack_stream_inbytes_remaining)
+ bytes_in = (size_t)zip->pack_stream_inbytes_remaining;
+ /* Drive decompression. */
+ r = decompress(a, zip, buff_out, &bytes_out,
+ buff_in, &bytes_in);
+ switch (r) {
+ case ARCHIVE_OK:
+ end_of_data = 0;
+ break;
+ case ARCHIVE_EOF:
+ end_of_data = 1;
+ break;
+ default:
+ return (ARCHIVE_FATAL);
+ }
+ zip->pack_stream_inbytes_remaining -= bytes_in;
+ if (bytes_out > zip->folder_outbytes_remaining)
+ bytes_out = (size_t)zip->folder_outbytes_remaining;
+ zip->folder_outbytes_remaining -= bytes_out;
+ zip->uncompressed_buffer_bytes_remaining += bytes_out;
+ zip->pack_stream_bytes_unconsumed = bytes_in;
+
+ /*
+ * Continue decompression until uncompressed_buffer is full.
+ */
+ if (zip->uncompressed_buffer_bytes_remaining ==
+ zip->uncompressed_buffer_size)
+ break;
+ if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size &&
+ zip->uncompressed_buffer_bytes_remaining + 5 >
+ zip->uncompressed_buffer_size)
+ break;
+ if (zip->pack_stream_inbytes_remaining == 0 &&
+ zip->folder_outbytes_remaining == 0)
+ break;
+ if (end_of_data || (bytes_in == 0 && bytes_out == 0)) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ read_consume(a);
+ }
+ if (zip->uncompressed_buffer_bytes_remaining < minimum) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ zip->uncompressed_buffer_pointer = zip->uncompressed_buffer;
+ return (ARCHIVE_OK);
+}
+
+static int
+seek_pack(struct archive_read *a)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ int64_t pack_offset;
+
+ if (zip->pack_stream_remaining <= 0) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ zip->pack_stream_inbytes_remaining =
+ zip->si.pi.sizes[zip->pack_stream_index];
+ pack_offset = zip->si.pi.positions[zip->pack_stream_index];
+ if (zip->stream_offset != pack_offset) {
+ if (0 > __archive_read_seek(a, pack_offset + zip->seek_base,
+ SEEK_SET))
+ return (ARCHIVE_FATAL);
+ zip->stream_offset = pack_offset;
+ }
+ zip->pack_stream_index++;
+ zip->pack_stream_remaining--;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+read_stream(struct archive_read *a, const void **buff, size_t size,
+ size_t minimum)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ uint64_t skip_bytes = 0;
+ ssize_t r;
+
+ if (zip->uncompressed_buffer_bytes_remaining == 0) {
+ if (zip->pack_stream_inbytes_remaining > 0) {
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+ return (get_uncompressed_data(a, buff, size, minimum));
+ } else if (zip->folder_outbytes_remaining > 0) {
+ /* Extract a remaining pack stream. */
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+ return (get_uncompressed_data(a, buff, size, minimum));
+ }
+ } else
+ return (get_uncompressed_data(a, buff, size, minimum));
+
+ /*
+ * Current pack stream has been consumed.
+ */
+ if (zip->pack_stream_remaining == 0) {
+ if (zip->header_is_being_read) {
+ /* Invalid sequence. This might happen when
+ * reading a malformed archive. */
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * All current folder's pack streams have been
+ * consumed. Switch to next folder.
+ */
+ if (zip->folder_index == 0 &&
+ (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
+ || zip->folder_index != zip->entry->folderIndex)) {
+ zip->folder_index = zip->entry->folderIndex;
+ skip_bytes =
+ zip->si.ci.folders[zip->folder_index].skipped_bytes;
+ }
+
+ if (zip->folder_index >= zip->si.ci.numFolders) {
+ /*
+ * We have consumed all folders and its pack streams.
+ */
+ *buff = NULL;
+ return (0);
+ }
+ r = setup_decode_folder(a,
+ &(zip->si.ci.folders[zip->folder_index]), 0);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ zip->folder_index++;
+ }
+
+ /*
+ * Switch to next pack stream.
+ */
+ r = seek_pack(a);
+ if (r < 0)
+ return (r);
+
+ /* Extract a new pack stream. */
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+
+ /*
+ * Skip the bytes we already has skipped in skip_stream().
+ */
+ while (skip_bytes) {
+ ssize_t skipped;
+
+ if (zip->uncompressed_buffer_bytes_remaining == 0) {
+ if (zip->pack_stream_inbytes_remaining > 0) {
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+ } else if (zip->folder_outbytes_remaining > 0) {
+ /* Extract a remaining pack stream. */
+ r = extract_pack_stream(a, 0);
+ if (r < 0)
+ return (r);
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ skipped = get_uncompressed_data(
+ a, buff, (size_t)skip_bytes, 0);
+ if (skipped < 0)
+ return (skipped);
+ skip_bytes -= skipped;
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+ }
+
+ return (get_uncompressed_data(a, buff, size, minimum));
+}
+
+static int
+setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
+ int header)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const struct _7z_coder *coder1, *coder2;
+ const char *cname = (header)?"archive header":"file content";
+ unsigned i;
+ int r, found_bcj2 = 0;
+
+ /*
+ * Release the memory which the previous folder used for BCJ2.
+ */
+ for (i = 0; i < 3; i++) {
+ free(zip->sub_stream_buff[i]);
+ zip->sub_stream_buff[i] = NULL;
+ }
+
+ /*
+ * Initialize a stream reader.
+ */
+ zip->pack_stream_remaining = (unsigned)folder->numPackedStreams;
+ zip->pack_stream_index = (unsigned)folder->packIndex;
+ zip->folder_outbytes_remaining = folder_uncompressed_size(folder);
+ zip->uncompressed_buffer_bytes_remaining = 0;
+
+ /*
+ * Check coder types.
+ */
+ for (i = 0; i < folder->numCoders; i++) {
+ switch(folder->coders[i].codec) {
+ case _7Z_CRYPTO_MAIN_ZIP:
+ case _7Z_CRYPTO_RAR_29:
+ case _7Z_CRYPTO_AES_256_SHA_256: {
+ /* For entry that is associated with this folder, mark
+ it as encrypted (data+metadata). */
+ zip->has_encrypted_entries = 1;
+ if (a->entry) {
+ archive_entry_set_is_data_encrypted(a->entry, 1);
+ archive_entry_set_is_metadata_encrypted(a->entry, 1);
+ }
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "The %s is encrypted, "
+ "but currently not supported", cname);
+ return (ARCHIVE_FATAL);
+ }
+ case _7Z_X86_BCJ2: {
+ found_bcj2++;
+ break;
+ }
+ }
+ }
+ /* Now that we've checked for encryption, if there were still no
+ * encrypted entries found we can say for sure that there are none.
+ */
+ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "The %s is encoded with many filters, "
+ "but currently not supported", cname);
+ return (ARCHIVE_FATAL);
+ }
+ coder1 = &(folder->coders[0]);
+ if (folder->numCoders == 2)
+ coder2 = &(folder->coders[1]);
+ else
+ coder2 = NULL;
+
+ if (found_bcj2) {
+ /*
+ * Preparation to decode BCJ2.
+ * Decoding BCJ2 requires four sources. Those are at least,
+ * as far as I know, two types of the storage form.
+ */
+ const struct _7z_coder *fc = folder->coders;
+ static const struct _7z_coder coder_copy = {0, 1, 1, 0, NULL};
+ const struct _7z_coder *scoder[3] =
+ {&coder_copy, &coder_copy, &coder_copy};
+ const void *buff;
+ ssize_t bytes;
+ unsigned char *b[3] = {NULL, NULL, NULL};
+ uint64_t sunpack[3] ={-1, -1, -1};
+ size_t s[3] = {0, 0, 0};
+ int idx[3] = {0, 1, 2};
+
+ if (folder->numCoders == 4 && fc[3].codec == _7Z_X86_BCJ2 &&
+ folder->numInStreams == 7 && folder->numOutStreams == 4 &&
+ zip->pack_stream_remaining == 4) {
+ /* Source type 1 made by 7zr or 7z with -m options. */
+ if (folder->bindPairs[0].inIndex == 5) {
+ /* The form made by 7zr */
+ idx[0] = 1; idx[1] = 2; idx[2] = 0;
+ scoder[1] = &(fc[1]);
+ scoder[2] = &(fc[0]);
+ sunpack[1] = folder->unPackSize[1];
+ sunpack[2] = folder->unPackSize[0];
+ coder1 = &(fc[2]);
+ } else {
+ /*
+ * NOTE: Some patterns do not work.
+ * work:
+ * 7z a -m0=BCJ2 -m1=COPY -m2=COPY
+ * -m3=(any)
+ * 7z a -m0=BCJ2 -m1=COPY -m2=(any)
+ * -m3=COPY
+ * 7z a -m0=BCJ2 -m1=(any) -m2=COPY
+ * -m3=COPY
+ * not work:
+ * other patterns.
+ *
+ * We have to handle this like `pipe' or
+ * our libarchive7s filter frame work,
+ * decoding the BCJ2 main stream sequentially,
+ * m3 -> m2 -> m1 -> BCJ2.
+ *
+ */
+ if (fc[0].codec == _7Z_COPY &&
+ fc[1].codec == _7Z_COPY)
+ coder1 = &(folder->coders[2]);
+ else if (fc[0].codec == _7Z_COPY &&
+ fc[2].codec == _7Z_COPY)
+ coder1 = &(folder->coders[1]);
+ else if (fc[1].codec == _7Z_COPY &&
+ fc[2].codec == _7Z_COPY)
+ coder1 = &(folder->coders[0]);
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unsupported form of "
+ "BCJ2 streams");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ coder2 = &(fc[3]);
+ zip->main_stream_bytes_remaining =
+ (size_t)folder->unPackSize[2];
+ } else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 &&
+ zip->pack_stream_remaining == 4 &&
+ folder->numInStreams == 5 && folder->numOutStreams == 2) {
+ /* Source type 0 made by 7z */
+ zip->main_stream_bytes_remaining =
+ (size_t)folder->unPackSize[0];
+ } else {
+ /* We got an unexpected form. */
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unsupported form of BCJ2 streams");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Skip the main stream at this time. */
+ if ((r = seek_pack(a)) < 0)
+ return (r);
+ zip->pack_stream_bytes_unconsumed =
+ (size_t)zip->pack_stream_inbytes_remaining;
+ read_consume(a);
+
+ /* Read following three sub streams. */
+ for (i = 0; i < 3; i++) {
+ const struct _7z_coder *coder = scoder[i];
+
+ if ((r = seek_pack(a)) < 0) {
+ free(b[0]); free(b[1]); free(b[2]);
+ return (r);
+ }
+
+ if (sunpack[i] == (uint64_t)-1)
+ zip->folder_outbytes_remaining =
+ zip->pack_stream_inbytes_remaining;
+ else
+ zip->folder_outbytes_remaining = sunpack[i];
+
+ r = init_decompression(a, zip, coder, NULL);
+ if (r != ARCHIVE_OK) {
+ free(b[0]); free(b[1]); free(b[2]);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Allocate memory for the decoded data of a sub
+ * stream. */
+ b[i] = malloc((size_t)zip->folder_outbytes_remaining);
+ if (b[i] == NULL) {
+ free(b[0]); free(b[1]); free(b[2]);
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for 7-Zip decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Extract a sub stream. */
+ while (zip->pack_stream_inbytes_remaining > 0) {
+ r = (int)extract_pack_stream(a, 0);
+ if (r < 0) {
+ free(b[0]); free(b[1]); free(b[2]);
+ return (r);
+ }
+ bytes = get_uncompressed_data(a, &buff,
+ zip->uncompressed_buffer_bytes_remaining,
+ 0);
+ if (bytes < 0) {
+ free(b[0]); free(b[1]); free(b[2]);
+ return ((int)bytes);
+ }
+ memcpy(b[i]+s[i], buff, bytes);
+ s[i] += bytes;
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+ }
+ }
+
+ /* Set the sub streams to the right place. */
+ for (i = 0; i < 3; i++) {
+ zip->sub_stream_buff[i] = b[idx[i]];
+ zip->sub_stream_size[i] = s[idx[i]];
+ zip->sub_stream_bytes_remaining[i] = s[idx[i]];
+ }
+
+ /* Allocate memory used for decoded main stream bytes. */
+ if (zip->tmp_stream_buff == NULL) {
+ zip->tmp_stream_buff_size = 32 * 1024;
+ zip->tmp_stream_buff =
+ malloc(zip->tmp_stream_buff_size);
+ if (zip->tmp_stream_buff == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for 7-Zip decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ zip->tmp_stream_bytes_avail = 0;
+ zip->tmp_stream_bytes_remaining = 0;
+ zip->odd_bcj_size = 0;
+ zip->bcj2_outPos = 0;
+
+ /*
+ * Reset a stream reader in order to read the main stream
+ * of BCJ2.
+ */
+ zip->pack_stream_remaining = 1;
+ zip->pack_stream_index = (unsigned)folder->packIndex;
+ zip->folder_outbytes_remaining =
+ folder_uncompressed_size(folder);
+ zip->uncompressed_buffer_bytes_remaining = 0;
+ }
+
+ /*
+ * Initialize the decompressor for the new folder's pack streams.
+ */
+ r = init_decompression(a, zip, coder1, coder2);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+skip_stream(struct archive_read *a, size_t skip_bytes)
+{
+ struct _7zip *zip = (struct _7zip *)a->format->data;
+ const void *p;
+ int64_t skipped_bytes;
+ size_t bytes = skip_bytes;
+
+ if (zip->folder_index == 0) {
+ /*
+ * Optimization for a list mode.
+ * Avoid unnecessary decoding operations.
+ */
+ zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
+ += skip_bytes;
+ return (skip_bytes);
+ }
+
+ while (bytes) {
+ skipped_bytes = read_stream(a, &p, bytes, 0);
+ if (skipped_bytes < 0)
+ return (skipped_bytes);
+ if (skipped_bytes == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated 7-Zip file body");
+ return (ARCHIVE_FATAL);
+ }
+ bytes -= (size_t)skipped_bytes;
+ if (zip->pack_stream_bytes_unconsumed)
+ read_consume(a);
+ }
+ return (skip_bytes);
+}
+
+/*
+ * Brought from LZMA SDK.
+ *
+ * Bra86.c -- Converter for x86 code (BCJ)
+ * 2008-10-04 : Igor Pavlov : Public domain
+ *
+ */
+
+#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF)
+
+static void
+x86_Init(struct _7zip *zip)
+{
+ zip->bcj_state = 0;
+ zip->bcj_prevPosT = (size_t)0 - 1;
+ zip->bcj_prevMask = 0;
+ zip->bcj_ip = 5;
+}
+
+static size_t
+x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)
+{
+ static const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0};
+ static const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3};
+ size_t bufferPos, prevPosT;
+ uint32_t ip, prevMask;
+
+ if (size < 5)
+ return 0;
+
+ bufferPos = 0;
+ prevPosT = zip->bcj_prevPosT;
+ prevMask = zip->bcj_prevMask;
+ ip = zip->bcj_ip;
+
+ for (;;) {
+ uint8_t *p = data + bufferPos;
+ uint8_t *limit = data + size - 4;
+
+ for (; p < limit; p++)
+ if ((*p & 0xFE) == 0xE8)
+ break;
+ bufferPos = (size_t)(p - data);
+ if (p >= limit)
+ break;
+ prevPosT = bufferPos - prevPosT;
+ if (prevPosT > 3)
+ prevMask = 0;
+ else {
+ prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
+ if (prevMask != 0) {
+ unsigned char b =
+ p[4 - kMaskToBitNumber[prevMask]];
+ if (!kMaskToAllowedStatus[prevMask] ||
+ Test86MSByte(b)) {
+ prevPosT = bufferPos;
+ prevMask = ((prevMask << 1) & 0x7) | 1;
+ bufferPos++;
+ continue;
+ }
+ }
+ }
+ prevPosT = bufferPos;
+
+ if (Test86MSByte(p[4])) {
+ uint32_t src = ((uint32_t)p[4] << 24) |
+ ((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) |
+ ((uint32_t)p[1]);
+ uint32_t dest;
+ for (;;) {
+ uint8_t b;
+ int b_index;
+
+ dest = src - (ip + (uint32_t)bufferPos);
+ if (prevMask == 0)
+ break;
+ b_index = kMaskToBitNumber[prevMask] * 8;
+ b = (uint8_t)(dest >> (24 - b_index));
+ if (!Test86MSByte(b))
+ break;
+ src = dest ^ ((1 << (32 - b_index)) - 1);
+ }
+ p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
+ p[3] = (uint8_t)(dest >> 16);
+ p[2] = (uint8_t)(dest >> 8);
+ p[1] = (uint8_t)dest;
+ bufferPos += 5;
+ } else {
+ prevMask = ((prevMask << 1) & 0x7) | 1;
+ bufferPos++;
+ }
+ }
+ zip->bcj_prevPosT = prevPosT;
+ zip->bcj_prevMask = prevMask;
+ zip->bcj_ip += (uint32_t)bufferPos;
+ return (bufferPos);
+}
+
+static void
+arm_Init(struct _7zip *zip)
+{
+ zip->bcj_ip = 8;
+}
+
+static size_t
+arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+ // This function was adapted from
+ // static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+ // in https://git.tukaani.org/xz-embedded.git
+
+ /*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <https://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+ size_t i;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ if (buf[i + 3] == 0xEB) {
+ // Calculate the transformed addr.
+ addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+ | ((uint32_t)buf[i + 2] << 16);
+ addr <<= 2;
+ addr -= zip->bcj_ip + (uint32_t)i;
+ addr >>= 2;
+
+ // Store the transformed addr in buf.
+ buf[i] = (uint8_t)addr;
+ buf[i + 1] = (uint8_t)(addr >> 8);
+ buf[i + 2] = (uint8_t)(addr >> 16);
+ }
+ }
+
+ zip->bcj_ip += (uint32_t)i;
+
+ return i;
+}
+
+static size_t
+arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+ // This function was adapted from
+ // static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+ // in https://git.tukaani.org/xz-embedded.git
+
+ /*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ * Igor Pavlov <https://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+ size_t i;
+ uint32_t instr;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ instr = (uint32_t)buf[i]
+ | ((uint32_t)buf[i+1] << 8)
+ | ((uint32_t)buf[i+2] << 16)
+ | ((uint32_t)buf[i+3] << 24);
+
+ if ((instr >> 26) == 0x25) {
+ /* BL instruction */
+ addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2);
+ instr = 0x94000000 | (addr & 0x03FFFFFF);
+
+ buf[i] = (uint8_t)instr;
+ buf[i+1] = (uint8_t)(instr >> 8);
+ buf[i+2] = (uint8_t)(instr >> 16);
+ buf[i+3] = (uint8_t)(instr >> 24);
+ } else if ((instr & 0x9F000000) == 0x90000000) {
+ /* ADRP instruction */
+ addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC);
+
+ /* Only convert values in the range +/-512 MiB. */
+ if ((addr + 0x020000) & 0x1C0000)
+ continue;
+
+ addr -= (zip->bcj_ip + (uint32_t)i) >> 12;
+
+ instr &= 0x9000001F;
+ instr |= (addr & 3) << 29;
+ instr |= (addr & 0x03FFFC) << 3;
+ instr |= (0U - (addr & 0x020000)) & 0xE00000;
+
+ buf[i] = (uint8_t)instr;
+ buf[i+1] = (uint8_t)(instr >> 8);
+ buf[i+2] = (uint8_t)(instr >> 16);
+ buf[i+3] = (uint8_t)(instr >> 24);
+ }
+ }
+
+ zip->bcj_ip += (uint32_t)i;
+
+ return i;
+}
+
+/*
+ * Brought from LZMA SDK.
+ *
+ * Bcj2.c -- Converter for x86 code (BCJ2)
+ * 2008-10-04 : Igor Pavlov : Public domain
+ *
+ */
+
+#define SZ_ERROR_DATA ARCHIVE_FAILED
+
+#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80)
+#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1))
+
+#define kNumTopBits 24
+#define kTopValue ((uint32_t)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+#define RC_READ_BYTE (*buffer++)
+#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; }
+#define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \
+ { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }}
+
+#define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }
+
+#define IF_BIT_0(p) ttt = *(p); bound = (zip->bcj2_range >> kNumBitModelTotalBits) * ttt; if (zip->bcj2_code < bound)
+#define UPDATE_0(p) zip->bcj2_range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE;
+#define UPDATE_1(p) zip->bcj2_range -= bound; zip->bcj2_code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE;
+
+static ssize_t
+Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)
+{
+ size_t inPos = 0, outPos = 0;
+ const uint8_t *buf0, *buf1, *buf2, *buf3;
+ size_t size0, size1, size2, size3;
+ const uint8_t *buffer, *bufferLim;
+ unsigned int i, j;
+
+ size0 = zip->tmp_stream_bytes_remaining;
+ buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0;
+ size1 = zip->sub_stream_bytes_remaining[0];
+ buf1 = zip->sub_stream_buff[0] + zip->sub_stream_size[0] - size1;
+ size2 = zip->sub_stream_bytes_remaining[1];
+ buf2 = zip->sub_stream_buff[1] + zip->sub_stream_size[1] - size2;
+ size3 = zip->sub_stream_bytes_remaining[2];
+ buf3 = zip->sub_stream_buff[2] + zip->sub_stream_size[2] - size3;
+
+ buffer = buf3;
+ bufferLim = buffer + size3;
+
+ if (zip->bcj_state == 0) {
+ /*
+ * Initialize.
+ */
+ zip->bcj2_prevByte = 0;
+ for (i = 0;
+ i < sizeof(zip->bcj2_p) / sizeof(zip->bcj2_p[0]); i++)
+ zip->bcj2_p[i] = kBitModelTotal >> 1;
+ RC_INIT2;
+ zip->bcj_state = 1;
+ }
+
+ /*
+ * Gather the odd bytes of a previous call.
+ */
+ for (i = 0; zip->odd_bcj_size > 0 && outPos < outSize; i++) {
+ outBuf[outPos++] = zip->odd_bcj[i];
+ zip->odd_bcj_size--;
+ }
+
+ if (outSize == 0) {
+ zip->bcj2_outPos += outPos;
+ return (outPos);
+ }
+
+ for (;;) {
+ uint8_t b;
+ CProb *prob;
+ uint32_t bound;
+ uint32_t ttt;
+
+ size_t limit = size0 - inPos;
+ if (outSize - outPos < limit)
+ limit = outSize - outPos;
+
+ if (zip->bcj_state == 1) {
+ while (limit != 0) {
+ uint8_t bb = buf0[inPos];
+ outBuf[outPos++] = bb;
+ if (IsJ(zip->bcj2_prevByte, bb)) {
+ zip->bcj_state = 2;
+ break;
+ }
+ inPos++;
+ zip->bcj2_prevByte = bb;
+ limit--;
+ }
+ }
+
+ if (limit == 0 || outPos == outSize)
+ break;
+ zip->bcj_state = 1;
+
+ b = buf0[inPos++];
+
+ if (b == 0xE8)
+ prob = zip->bcj2_p + zip->bcj2_prevByte;
+ else if (b == 0xE9)
+ prob = zip->bcj2_p + 256;
+ else
+ prob = zip->bcj2_p + 257;
+
+ IF_BIT_0(prob) {
+ UPDATE_0(prob)
+ zip->bcj2_prevByte = b;
+ } else {
+ uint32_t dest;
+ const uint8_t *v;
+ uint8_t out[4];
+
+ UPDATE_1(prob)
+ if (b == 0xE8) {
+ v = buf1;
+ if (size1 < 4)
+ return SZ_ERROR_DATA;
+ buf1 += 4;
+ size1 -= 4;
+ } else {
+ v = buf2;
+ if (size2 < 4)
+ return SZ_ERROR_DATA;
+ buf2 += 4;
+ size2 -= 4;
+ }
+ dest = (((uint32_t)v[0] << 24) |
+ ((uint32_t)v[1] << 16) |
+ ((uint32_t)v[2] << 8) |
+ ((uint32_t)v[3])) -
+ ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4);
+ out[0] = (uint8_t)dest;
+ out[1] = (uint8_t)(dest >> 8);
+ out[2] = (uint8_t)(dest >> 16);
+ out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24);
+
+ for (i = 0; i < 4 && outPos < outSize; i++)
+ outBuf[outPos++] = out[i];
+ if (i < 4) {
+ /*
+ * Save odd bytes which we could not add into
+ * the output buffer because of out of space.
+ */
+ zip->odd_bcj_size = 4 -i;
+ for (; i < 4; i++) {
+ j = i - 4 + (unsigned)zip->odd_bcj_size;
+ zip->odd_bcj[j] = out[i];
+ }
+ break;
+ }
+ }
+ }
+ zip->tmp_stream_bytes_remaining -= inPos;
+ zip->sub_stream_bytes_remaining[0] = size1;
+ zip->sub_stream_bytes_remaining[1] = size2;
+ zip->sub_stream_bytes_remaining[2] = bufferLim - buffer;
+ zip->bcj2_outPos += outPos;
+
+ return ((ssize_t)outPos);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_all.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_all.c
new file mode 100644
index 0000000000..dea558bbfc
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_all.c
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_all.c 174991 2007-12-30 04:58:22Z kientzle $");
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_format_all(struct archive *a)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_all");
+
+ /* TODO: It would be nice to compute the ordering
+ * here automatically so that people who enable just
+ * a few formats can still get the benefits. That
+ * may just require the format registration to include
+ * a "maximum read-ahead" value (anything that uses seek
+ * would be essentially infinite read-ahead). The core
+ * bid management can then sort the bidders before calling
+ * them.
+ *
+ * If you implement the above, please return the list below
+ * to alphabetic order.
+ */
+
+ /*
+ * These bidders are all pretty cheap; they just examine a
+ * small initial part of the archive. If one of these bids
+ * high, we can maybe avoid running any of the more expensive
+ * bidders below.
+ */
+ archive_read_support_format_ar(a);
+ archive_read_support_format_cpio(a);
+ archive_read_support_format_empty(a);
+ archive_read_support_format_lha(a);
+ archive_read_support_format_mtree(a);
+ archive_read_support_format_tar(a);
+ archive_read_support_format_xar(a);
+ archive_read_support_format_warc(a);
+
+ /*
+ * Install expensive bidders last. By doing them last, we
+ * increase the chance that a high bid from someone else will
+ * make it unnecessary for these to do anything at all.
+ */
+ /* These three have potentially large look-ahead. */
+ archive_read_support_format_7zip(a);
+ archive_read_support_format_cab(a);
+ archive_read_support_format_rar(a);
+ archive_read_support_format_rar5(a);
+ archive_read_support_format_iso9660(a);
+ /* Seek is really bad, since it forces the read-ahead
+ * logic to discard buffered data. */
+ archive_read_support_format_zip(a);
+
+ /* Note: We always return ARCHIVE_OK here, even if some of the
+ * above return ARCHIVE_WARN. The intent here is to enable
+ * "as much as possible." Clients who need specific
+ * compression should enable those individually so they can
+ * verify the level of support. */
+ /* Clear any warning messages set by the above functions. */
+ archive_clear_error(a);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_ar.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_ar.c
new file mode 100644
index 0000000000..296b7db041
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_ar.c
@@ -0,0 +1,638 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_ar.c 201101 2009-12-28 03:06:27Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct ar {
+ int64_t entry_bytes_remaining;
+ /* unconsumed is purely to track data we've gotten from readahead,
+ * but haven't yet marked as consumed. Must be paired with
+ * entry_bytes_remaining usage/modification.
+ */
+ size_t entry_bytes_unconsumed;
+ int64_t entry_offset;
+ int64_t entry_padding;
+ char *strtab;
+ size_t strtab_size;
+ char read_global_header;
+};
+
+/*
+ * Define structure of the "ar" header.
+ */
+#define AR_name_offset 0
+#define AR_name_size 16
+#define AR_date_offset 16
+#define AR_date_size 12
+#define AR_uid_offset 28
+#define AR_uid_size 6
+#define AR_gid_offset 34
+#define AR_gid_size 6
+#define AR_mode_offset 40
+#define AR_mode_size 8
+#define AR_size_offset 48
+#define AR_size_size 10
+#define AR_fmag_offset 58
+#define AR_fmag_size 2
+
+static int archive_read_format_ar_bid(struct archive_read *a, int);
+static int archive_read_format_ar_cleanup(struct archive_read *a);
+static int archive_read_format_ar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset);
+static int archive_read_format_ar_skip(struct archive_read *a);
+static int archive_read_format_ar_read_header(struct archive_read *a,
+ struct archive_entry *e);
+static uint64_t ar_atol8(const char *p, unsigned char_cnt);
+static uint64_t ar_atol10(const char *p, unsigned char_cnt);
+static int ar_parse_gnu_filename_table(struct archive_read *a);
+static int ar_parse_common_header(struct ar *ar, struct archive_entry *,
+ const char *h);
+
+int
+archive_read_support_format_ar(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct ar *ar;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_ar");
+
+ ar = (struct ar *)calloc(1, sizeof(*ar));
+ if (ar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ar data");
+ return (ARCHIVE_FATAL);
+ }
+ ar->strtab = NULL;
+
+ r = __archive_read_register_format(a,
+ ar,
+ "ar",
+ archive_read_format_ar_bid,
+ NULL,
+ archive_read_format_ar_read_header,
+ archive_read_format_ar_read_data,
+ archive_read_format_ar_skip,
+ NULL,
+ archive_read_format_ar_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK) {
+ free(ar);
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_cleanup(struct archive_read *a)
+{
+ struct ar *ar;
+
+ ar = (struct ar *)(a->format->data);
+ free(ar->strtab);
+ free(ar);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_bid(struct archive_read *a, int best_bid)
+{
+ const void *h;
+
+ (void)best_bid; /* UNUSED */
+
+ /*
+ * Verify the 8-byte file signature.
+ * TODO: Do we need to check more than this?
+ */
+ if ((h = __archive_read_ahead(a, 8, NULL)) == NULL)
+ return (-1);
+ if (memcmp(h, "!<arch>\n", 8) == 0) {
+ return (64);
+ }
+ return (-1);
+}
+
+static int
+_ar_read_header(struct archive_read *a, struct archive_entry *entry,
+ struct ar *ar, const char *h, size_t *unconsumed)
+{
+ char filename[AR_name_size + 1];
+ uint64_t number; /* Used to hold parsed numbers before validation. */
+ size_t bsd_name_length, entry_size;
+ char *p, *st;
+ const void *b;
+ int r;
+
+ /* Verify the magic signature on the file header. */
+ if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "Incorrect file header signature");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Copy filename into work buffer. */
+ strncpy(filename, h + AR_name_offset, AR_name_size);
+ filename[AR_name_size] = '\0';
+
+ /*
+ * Guess the format variant based on the filename.
+ */
+ if (a->archive.archive_format == ARCHIVE_FORMAT_AR) {
+ /* We don't already know the variant, so let's guess. */
+ /*
+ * Biggest clue is presence of '/': GNU starts special
+ * filenames with '/', appends '/' as terminator to
+ * non-special names, so anything with '/' should be
+ * GNU except for BSD long filenames.
+ */
+ if (strncmp(filename, "#1/", 3) == 0)
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+ else if (strchr(filename, '/') != NULL)
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
+ else if (strncmp(filename, "__.SYMDEF", 9) == 0)
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+ /*
+ * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/'
+ * if name exactly fills 16-byte field? If so, we
+ * can't assume entries without '/' are BSD. XXX
+ */
+ }
+
+ /* Update format name from the code. */
+ if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU)
+ a->archive.archive_format_name = "ar (GNU/SVR4)";
+ else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD)
+ a->archive.archive_format_name = "ar (BSD)";
+ else
+ a->archive.archive_format_name = "ar";
+
+ /*
+ * Remove trailing spaces from the filename. GNU and BSD
+ * variants both pad filename area out with spaces.
+ * This will only be wrong if GNU/SVR4 'ar' implementations
+ * omit trailing '/' for 16-char filenames and we have
+ * a 16-char filename that ends in ' '.
+ */
+ p = filename + AR_name_size - 1;
+ while (p >= filename && *p == ' ') {
+ *p = '\0';
+ p--;
+ }
+
+ /*
+ * Remove trailing slash unless first character is '/'.
+ * (BSD entries never end in '/', so this will only trim
+ * GNU-format entries. GNU special entries start with '/'
+ * and are not terminated in '/', so we don't trim anything
+ * that starts with '/'.)
+ */
+ if (filename[0] != '/' && p > filename && *p == '/') {
+ *p = '\0';
+ }
+
+ if (p < filename) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Found entry with empty filename");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * '//' is the GNU filename table.
+ * Later entries can refer to names in this table.
+ */
+ if (strcmp(filename, "//") == 0) {
+ /* This must come before any call to _read_ahead. */
+ ar_parse_common_header(ar, entry, h);
+ archive_entry_copy_pathname(entry, filename);
+ archive_entry_set_filetype(entry, AE_IFREG);
+ /* Get the size of the filename table. */
+ number = ar_atol10(h + AR_size_offset, AR_size_size);
+ if (number > SIZE_MAX || number > 1024 * 1024 * 1024) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Filename table too large");
+ return (ARCHIVE_FATAL);
+ }
+ entry_size = (size_t)number;
+ if (entry_size == 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid string table");
+ return (ARCHIVE_FATAL);
+ }
+ if (ar->strtab != NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "More than one string tables exist");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Read the filename table into memory. */
+ st = malloc(entry_size);
+ if (st == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate filename table buffer");
+ return (ARCHIVE_FATAL);
+ }
+ ar->strtab = st;
+ ar->strtab_size = entry_size;
+
+ if (*unconsumed) {
+ __archive_read_consume(a, *unconsumed);
+ *unconsumed = 0;
+ }
+
+ if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ memcpy(st, b, entry_size);
+ __archive_read_consume(a, entry_size);
+ /* All contents are consumed. */
+ ar->entry_bytes_remaining = 0;
+ archive_entry_set_size(entry, ar->entry_bytes_remaining);
+
+ /* Parse the filename table. */
+ return (ar_parse_gnu_filename_table(a));
+ }
+
+ /*
+ * GNU variant handles long filenames by storing /<number>
+ * to indicate a name stored in the filename table.
+ * XXX TODO: Verify that it's all digits... Don't be fooled
+ * by "/9xyz" XXX
+ */
+ if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') {
+ number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
+ /*
+ * If we can't look up the real name, warn and return
+ * the entry with the wrong name.
+ */
+ if (ar->strtab == NULL || number >= ar->strtab_size) {
+ archive_set_error(&a->archive, EINVAL,
+ "Can't find long filename for GNU/SVR4 archive entry");
+ archive_entry_copy_pathname(entry, filename);
+ /* Parse the time, owner, mode, size fields. */
+ ar_parse_common_header(ar, entry, h);
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);
+ /* Parse the time, owner, mode, size fields. */
+ return (ar_parse_common_header(ar, entry, h));
+ }
+
+ /*
+ * BSD handles long filenames by storing "#1/" followed by the
+ * length of filename as a decimal number, then prepends the
+ * the filename to the file contents.
+ */
+ if (strncmp(filename, "#1/", 3) == 0) {
+ /* Parse the time, owner, mode, size fields. */
+ /* This must occur before _read_ahead is called again. */
+ ar_parse_common_header(ar, entry, h);
+
+ /* Parse the size of the name, adjust the file size. */
+ number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
+ /* Sanity check the filename length:
+ * = Must be <= SIZE_MAX - 1
+ * = Must be <= 1MB
+ * = Cannot be bigger than the entire entry
+ */
+ if (number > SIZE_MAX - 1
+ || number > 1024 * 1024
+ || (int64_t)number > ar->entry_bytes_remaining) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Bad input file size");
+ return (ARCHIVE_FATAL);
+ }
+ bsd_name_length = (size_t)number;
+ ar->entry_bytes_remaining -= bsd_name_length;
+ /* Adjust file size reported to client. */
+ archive_entry_set_size(entry, ar->entry_bytes_remaining);
+
+ if (*unconsumed) {
+ __archive_read_consume(a, *unconsumed);
+ *unconsumed = 0;
+ }
+
+ /* Read the long name into memory. */
+ if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated input file");
+ return (ARCHIVE_FATAL);
+ }
+ /* Store it in the entry. */
+ p = (char *)malloc(bsd_name_length + 1);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate fname buffer");
+ return (ARCHIVE_FATAL);
+ }
+ strncpy(p, b, bsd_name_length);
+ p[bsd_name_length] = '\0';
+
+ __archive_read_consume(a, bsd_name_length);
+
+ archive_entry_copy_pathname(entry, p);
+ free(p);
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * "/" is the SVR4/GNU archive symbol table.
+ * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.
+ */
+ if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) {
+ archive_entry_copy_pathname(entry, filename);
+ /* Parse the time, owner, mode, size fields. */
+ r = ar_parse_common_header(ar, entry, h);
+ /* Force the file type to a regular file. */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ return (r);
+ }
+
+ /*
+ * "__.SYMDEF" is a BSD archive symbol table.
+ */
+ if (strcmp(filename, "__.SYMDEF") == 0) {
+ archive_entry_copy_pathname(entry, filename);
+ /* Parse the time, owner, mode, size fields. */
+ return (ar_parse_common_header(ar, entry, h));
+ }
+
+ /*
+ * Otherwise, this is a standard entry. The filename
+ * has already been trimmed as much as possible, based
+ * on our current knowledge of the format.
+ */
+ archive_entry_copy_pathname(entry, filename);
+ return (ar_parse_common_header(ar, entry, h));
+}
+
+static int
+archive_read_format_ar_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct ar *ar = (struct ar*)(a->format->data);
+ size_t unconsumed;
+ const void *header_data;
+ int ret;
+
+ if (!ar->read_global_header) {
+ /*
+ * We are now at the beginning of the archive,
+ * so we need first consume the ar global header.
+ */
+ __archive_read_consume(a, 8);
+ ar->read_global_header = 1;
+ /* Set a default format code for now. */
+ a->archive.archive_format = ARCHIVE_FORMAT_AR;
+ }
+
+ /* Read the header for the next file entry. */
+ if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL)
+ /* Broken header. */
+ return (ARCHIVE_EOF);
+
+ unconsumed = 60;
+
+ ret = _ar_read_header(a, entry, ar, (const char *)header_data, &unconsumed);
+
+ if (unconsumed)
+ __archive_read_consume(a, unconsumed);
+
+ return ret;
+}
+
+
+static int
+ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
+ const char *h)
+{
+ uint64_t n;
+
+ /* Copy remaining header */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_mtime(entry,
+ (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);
+ archive_entry_set_uid(entry,
+ (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size));
+ archive_entry_set_gid(entry,
+ (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size));
+ archive_entry_set_mode(entry,
+ (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
+ n = ar_atol10(h + AR_size_offset, AR_size_size);
+
+ ar->entry_offset = 0;
+ ar->entry_padding = n % 2;
+ archive_entry_set_size(entry, n);
+ ar->entry_bytes_remaining = n;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ ssize_t bytes_read;
+ struct ar *ar;
+
+ ar = (struct ar *)(a->format->data);
+
+ if (ar->entry_bytes_unconsumed) {
+ __archive_read_consume(a, ar->entry_bytes_unconsumed);
+ ar->entry_bytes_unconsumed = 0;
+ }
+
+ if (ar->entry_bytes_remaining > 0) {
+ *buff = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated ar archive");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ if (bytes_read > ar->entry_bytes_remaining)
+ bytes_read = (ssize_t)ar->entry_bytes_remaining;
+ *size = bytes_read;
+ ar->entry_bytes_unconsumed = bytes_read;
+ *offset = ar->entry_offset;
+ ar->entry_offset += bytes_read;
+ ar->entry_bytes_remaining -= bytes_read;
+ return (ARCHIVE_OK);
+ } else {
+ int64_t skipped = __archive_read_consume(a, ar->entry_padding);
+ if (skipped >= 0) {
+ ar->entry_padding -= skipped;
+ }
+ if (ar->entry_padding) {
+ if (skipped >= 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated ar archive- failed consuming padding");
+ }
+ return (ARCHIVE_FATAL);
+ }
+ *buff = NULL;
+ *size = 0;
+ *offset = ar->entry_offset;
+ return (ARCHIVE_EOF);
+ }
+}
+
+static int
+archive_read_format_ar_skip(struct archive_read *a)
+{
+ int64_t bytes_skipped;
+ struct ar* ar;
+
+ ar = (struct ar *)(a->format->data);
+
+ bytes_skipped = __archive_read_consume(a,
+ ar->entry_bytes_remaining + ar->entry_padding
+ + ar->entry_bytes_unconsumed);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ ar->entry_bytes_remaining = 0;
+ ar->entry_bytes_unconsumed = 0;
+ ar->entry_padding = 0;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+ar_parse_gnu_filename_table(struct archive_read *a)
+{
+ struct ar *ar;
+ char *p;
+ size_t size;
+
+ ar = (struct ar*)(a->format->data);
+ size = ar->strtab_size;
+
+ for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
+ if (*p == '/') {
+ *p++ = '\0';
+ if (*p != '\n')
+ goto bad_string_table;
+ *p = '\0';
+ }
+ }
+ /*
+ * GNU ar always pads the table to an even size.
+ * The pad character is either '\n' or '`'.
+ */
+ if (p != ar->strtab + size && *p != '\n' && *p != '`')
+ goto bad_string_table;
+
+ /* Enforce zero termination. */
+ ar->strtab[size - 1] = '\0';
+
+ return (ARCHIVE_OK);
+
+bad_string_table:
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid string table");
+ free(ar->strtab);
+ ar->strtab = NULL;
+ return (ARCHIVE_FATAL);
+}
+
+static uint64_t
+ar_atol8(const char *p, unsigned char_cnt)
+{
+ uint64_t l, limit, last_digit_limit;
+ unsigned int digit, base;
+
+ base = 8;
+ limit = UINT64_MAX / base;
+ last_digit_limit = UINT64_MAX % base;
+
+ while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
+ p++;
+
+ l = 0;
+ digit = *p - '0';
+ while (*p >= '0' && digit < base && char_cnt-- > 0) {
+ if (l>limit || (l == limit && digit > last_digit_limit)) {
+ l = UINT64_MAX; /* Truncate on overflow. */
+ break;
+ }
+ l = (l * base) + digit;
+ digit = *++p - '0';
+ }
+ return (l);
+}
+
+static uint64_t
+ar_atol10(const char *p, unsigned char_cnt)
+{
+ uint64_t l, limit, last_digit_limit;
+ unsigned int base, digit;
+
+ base = 10;
+ limit = UINT64_MAX / base;
+ last_digit_limit = UINT64_MAX % base;
+
+ while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
+ p++;
+ l = 0;
+ digit = *p - '0';
+ while (*p >= '0' && digit < base && char_cnt-- > 0) {
+ if (l > limit || (l == limit && digit > last_digit_limit)) {
+ l = UINT64_MAX; /* Truncate on overflow. */
+ break;
+ }
+ l = (l * base) + digit;
+ digit = *++p - '0';
+ }
+ return (l);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_by_code.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_by_code.c
new file mode 100644
index 0000000000..89e96f1f59
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_by_code.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_format_by_code(struct archive *a, int format_code)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_by_code");
+
+ switch (format_code & ARCHIVE_FORMAT_BASE_MASK) {
+ case ARCHIVE_FORMAT_7ZIP:
+ return archive_read_support_format_7zip(a);
+ break;
+ case ARCHIVE_FORMAT_AR:
+ return archive_read_support_format_ar(a);
+ break;
+ case ARCHIVE_FORMAT_CAB:
+ return archive_read_support_format_cab(a);
+ break;
+ case ARCHIVE_FORMAT_CPIO:
+ return archive_read_support_format_cpio(a);
+ break;
+ case ARCHIVE_FORMAT_EMPTY:
+ return archive_read_support_format_empty(a);
+ break;
+ case ARCHIVE_FORMAT_ISO9660:
+ return archive_read_support_format_iso9660(a);
+ break;
+ case ARCHIVE_FORMAT_LHA:
+ return archive_read_support_format_lha(a);
+ break;
+ case ARCHIVE_FORMAT_MTREE:
+ return archive_read_support_format_mtree(a);
+ break;
+ case ARCHIVE_FORMAT_RAR:
+ return archive_read_support_format_rar(a);
+ break;
+ case ARCHIVE_FORMAT_RAR_V5:
+ return archive_read_support_format_rar5(a);
+ break;
+ case ARCHIVE_FORMAT_RAW:
+ return archive_read_support_format_raw(a);
+ break;
+ case ARCHIVE_FORMAT_TAR:
+ return archive_read_support_format_tar(a);
+ break;
+ case ARCHIVE_FORMAT_WARC:
+ return archive_read_support_format_warc(a);
+ break;
+ case ARCHIVE_FORMAT_XAR:
+ return archive_read_support_format_xar(a);
+ break;
+ case ARCHIVE_FORMAT_ZIP:
+ return archive_read_support_format_zip(a);
+ break;
+ }
+ archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+ "Invalid format code specified");
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_cab.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_cab.c
new file mode 100644
index 0000000000..3b552a84de
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_cab.c
@@ -0,0 +1,3228 @@
+/*-
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+
+struct lzx_dec {
+ /* Decoding status. */
+ int state;
+
+ /*
+ * Window to see last decoded data, from 32KBi to 2MBi.
+ */
+ int w_size;
+ int w_mask;
+ /* Window buffer, which is a loop buffer. */
+ unsigned char *w_buff;
+ /* The insert position to the window. */
+ int w_pos;
+ /* The position where we can copy decoded code from the window. */
+ int copy_pos;
+ /* The length how many bytes we can copy decoded code from
+ * the window. */
+ int copy_len;
+ /* Translation reversal for x86 processor CALL byte sequence(E8).
+ * This is used for LZX only. */
+ uint32_t translation_size;
+ char translation;
+ char block_type;
+#define VERBATIM_BLOCK 1
+#define ALIGNED_OFFSET_BLOCK 2
+#define UNCOMPRESSED_BLOCK 3
+ size_t block_size;
+ size_t block_bytes_avail;
+ /* Repeated offset. */
+ int r0, r1, r2;
+ unsigned char rbytes[4];
+ int rbytes_avail;
+ int length_header;
+ int position_slot;
+ int offset_bits;
+
+ struct lzx_pos_tbl {
+ int base;
+ int footer_bits;
+ } *pos_tbl;
+ /*
+ * Bit stream reader.
+ */
+ struct lzx_br {
+#define CACHE_TYPE uint64_t
+#define CACHE_BITS (8 * sizeof(CACHE_TYPE))
+ /* Cache buffer. */
+ CACHE_TYPE cache_buffer;
+ /* Indicates how many bits avail in cache_buffer. */
+ int cache_avail;
+ unsigned char odd;
+ char have_odd;
+ } br;
+
+ /*
+ * Huffman coding.
+ */
+ struct huffman {
+ int len_size;
+ int freq[17];
+ unsigned char *bitlen;
+
+ /*
+ * Use a index table. It's faster than searching a huffman
+ * coding tree, which is a binary tree. But a use of a large
+ * index table causes L1 cache read miss many times.
+ */
+ int max_bits;
+ int tbl_bits;
+ int tree_used;
+ /* Direct access table. */
+ uint16_t *tbl;
+ } at, lt, mt, pt;
+
+ int loop;
+ int error;
+};
+
+static const int slots[] = {
+ 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
+};
+#define SLOT_BASE 15
+#define SLOT_MAX 21/*->25*/
+
+struct lzx_stream {
+ const unsigned char *next_in;
+ int64_t avail_in;
+ int64_t total_in;
+ unsigned char *next_out;
+ int64_t avail_out;
+ int64_t total_out;
+ struct lzx_dec *ds;
+};
+
+/*
+ * Cabinet file definitions.
+ */
+/* CFHEADER offset */
+#define CFHEADER_signature 0
+#define CFHEADER_cbCabinet 8
+#define CFHEADER_coffFiles 16
+#define CFHEADER_versionMinor 24
+#define CFHEADER_versionMajor 25
+#define CFHEADER_cFolders 26
+#define CFHEADER_cFiles 28
+#define CFHEADER_flags 30
+#define CFHEADER_setID 32
+#define CFHEADER_iCabinet 34
+#define CFHEADER_cbCFHeader 36
+#define CFHEADER_cbCFFolder 38
+#define CFHEADER_cbCFData 39
+
+/* CFFOLDER offset */
+#define CFFOLDER_coffCabStart 0
+#define CFFOLDER_cCFData 4
+#define CFFOLDER_typeCompress 6
+#define CFFOLDER_abReserve 8
+
+/* CFFILE offset */
+#define CFFILE_cbFile 0
+#define CFFILE_uoffFolderStart 4
+#define CFFILE_iFolder 8
+#define CFFILE_date_time 10
+#define CFFILE_attribs 14
+
+/* CFDATA offset */
+#define CFDATA_csum 0
+#define CFDATA_cbData 4
+#define CFDATA_cbUncomp 6
+
+static const char * const compression_name[] = {
+ "NONE",
+ "MSZIP",
+ "Quantum",
+ "LZX",
+};
+
+struct cfdata {
+ /* Sum value of this CFDATA. */
+ uint32_t sum;
+ uint16_t compressed_size;
+ uint16_t compressed_bytes_remaining;
+ uint16_t uncompressed_size;
+ uint16_t uncompressed_bytes_remaining;
+ /* To know how many bytes we have decompressed. */
+ uint16_t uncompressed_avail;
+ /* Offset from the beginning of compressed data of this CFDATA */
+ uint16_t read_offset;
+ int64_t unconsumed;
+ /* To keep memory image of this CFDATA to compute the sum. */
+ size_t memimage_size;
+ unsigned char *memimage;
+ /* Result of calculation of sum. */
+ uint32_t sum_calculated;
+ unsigned char sum_extra[4];
+ int sum_extra_avail;
+ const void *sum_ptr;
+};
+
+struct cffolder {
+ uint32_t cfdata_offset_in_cab;
+ uint16_t cfdata_count;
+ uint16_t comptype;
+#define COMPTYPE_NONE 0x0000
+#define COMPTYPE_MSZIP 0x0001
+#define COMPTYPE_QUANTUM 0x0002
+#define COMPTYPE_LZX 0x0003
+ uint16_t compdata;
+ const char *compname;
+ /* At the time reading CFDATA */
+ struct cfdata cfdata;
+ int cfdata_index;
+ /* Flags to mark progress of decompression. */
+ char decompress_init;
+};
+
+struct cffile {
+ uint32_t uncompressed_size;
+ uint32_t offset;
+ time_t mtime;
+ uint16_t folder;
+#define iFoldCONTINUED_FROM_PREV 0xFFFD
+#define iFoldCONTINUED_TO_NEXT 0xFFFE
+#define iFoldCONTINUED_PREV_AND_NEXT 0xFFFF
+ unsigned char attr;
+#define ATTR_RDONLY 0x01
+#define ATTR_NAME_IS_UTF 0x80
+ struct archive_string pathname;
+};
+
+struct cfheader {
+ /* Total bytes of all file size in a Cabinet. */
+ uint32_t total_bytes;
+ uint32_t files_offset;
+ uint16_t folder_count;
+ uint16_t file_count;
+ uint16_t flags;
+#define PREV_CABINET 0x0001
+#define NEXT_CABINET 0x0002
+#define RESERVE_PRESENT 0x0004
+ uint16_t setid;
+ uint16_t cabinet;
+ /* Version number. */
+ unsigned char major;
+ unsigned char minor;
+ unsigned char cffolder;
+ unsigned char cfdata;
+ /* All folders in a cabinet. */
+ struct cffolder *folder_array;
+ /* All files in a cabinet. */
+ struct cffile *file_array;
+ int file_index;
+};
+
+struct cab {
+ /* entry_bytes_remaining is the number of bytes we expect. */
+ int64_t entry_offset;
+ int64_t entry_bytes_remaining;
+ int64_t entry_unconsumed;
+ int64_t entry_compressed_bytes_read;
+ int64_t entry_uncompressed_bytes_read;
+ struct cffolder *entry_cffolder;
+ struct cffile *entry_cffile;
+ struct cfdata *entry_cfdata;
+
+ /* Offset from beginning of a cabinet file. */
+ int64_t cab_offset;
+ struct cfheader cfheader;
+ struct archive_wstring ws;
+
+ /* Flag to mark progress that an archive was read their first header.*/
+ char found_header;
+ char end_of_archive;
+ char end_of_entry;
+ char end_of_entry_cleanup;
+ char read_data_invoked;
+ int64_t bytes_skipped;
+
+ unsigned char *uncompressed_buffer;
+ size_t uncompressed_buffer_size;
+
+ int init_default_conversion;
+ struct archive_string_conv *sconv;
+ struct archive_string_conv *sconv_default;
+ struct archive_string_conv *sconv_utf8;
+ char format_name[64];
+
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ char stream_valid;
+#endif
+ struct lzx_stream xstrm;
+};
+
+static int archive_read_format_cab_bid(struct archive_read *, int);
+static int archive_read_format_cab_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_cab_read_header(struct archive_read *,
+ struct archive_entry *);
+static int archive_read_format_cab_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_cab_read_data_skip(struct archive_read *);
+static int archive_read_format_cab_cleanup(struct archive_read *);
+
+static int cab_skip_sfx(struct archive_read *);
+static time_t cab_dos_time(const unsigned char *);
+static int cab_read_data(struct archive_read *, const void **,
+ size_t *, int64_t *);
+static int cab_read_header(struct archive_read *);
+static uint32_t cab_checksum_cfdata_4(const void *, size_t bytes, uint32_t);
+static uint32_t cab_checksum_cfdata(const void *, size_t bytes, uint32_t);
+static void cab_checksum_update(struct archive_read *, size_t);
+static int cab_checksum_finish(struct archive_read *);
+static int cab_next_cfdata(struct archive_read *);
+static const void *cab_read_ahead_cfdata(struct archive_read *, ssize_t *);
+static const void *cab_read_ahead_cfdata_none(struct archive_read *, ssize_t *);
+static const void *cab_read_ahead_cfdata_deflate(struct archive_read *,
+ ssize_t *);
+static const void *cab_read_ahead_cfdata_lzx(struct archive_read *,
+ ssize_t *);
+static int64_t cab_consume_cfdata(struct archive_read *, int64_t);
+static int64_t cab_minimum_consume_cfdata(struct archive_read *, int64_t);
+static int lzx_decode_init(struct lzx_stream *, int);
+static int lzx_read_blocks(struct lzx_stream *, int);
+static int lzx_decode_blocks(struct lzx_stream *, int);
+static void lzx_decode_free(struct lzx_stream *);
+static void lzx_translation(struct lzx_stream *, void *, size_t, uint32_t);
+static void lzx_cleanup_bitstream(struct lzx_stream *);
+static int lzx_decode(struct lzx_stream *, int);
+static int lzx_read_pre_tree(struct lzx_stream *);
+static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, int);
+static int lzx_huffman_init(struct huffman *, size_t, int);
+static void lzx_huffman_free(struct huffman *);
+static int lzx_make_huffman_table(struct huffman *);
+static inline int lzx_decode_huffman(struct huffman *, unsigned);
+
+
+int
+archive_read_support_format_cab(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct cab *cab;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_cab");
+
+ cab = (struct cab *)calloc(1, sizeof(*cab));
+ if (cab == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate CAB data");
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_init(&cab->ws);
+ archive_wstring_ensure(&cab->ws, 256);
+
+ r = __archive_read_register_format(a,
+ cab,
+ "cab",
+ archive_read_format_cab_bid,
+ archive_read_format_cab_options,
+ archive_read_format_cab_read_header,
+ archive_read_format_cab_read_data,
+ archive_read_format_cab_read_data_skip,
+ NULL,
+ archive_read_format_cab_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK)
+ free(cab);
+ return (ARCHIVE_OK);
+}
+
+static int
+find_cab_magic(const char *p)
+{
+ switch (p[4]) {
+ case 0:
+ /*
+ * Note: Self-Extraction program has 'MSCF' string in their
+ * program. If we were finding 'MSCF' string only, we got
+ * wrong place for Cabinet header, thus, we have to check
+ * following four bytes which are reserved and must be set
+ * to zero.
+ */
+ if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)
+ return 0;
+ return 5;
+ case 'F': return 1;
+ case 'C': return 2;
+ case 'S': return 3;
+ case 'M': return 4;
+ default: return 5;
+ }
+}
+
+static int
+archive_read_format_cab_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+ ssize_t bytes_avail, offset, window;
+
+ /* If there's already a better bid than we can ever
+ make, don't bother testing. */
+ if (best_bid > 64)
+ return (-1);
+
+ if ((p = __archive_read_ahead(a, 8, NULL)) == NULL)
+ return (-1);
+
+ if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)
+ return (64);
+
+ /*
+ * Attempt to handle self-extracting archives
+ * by noting a PE header and searching forward
+ * up to 128k for a 'MSCF' marker.
+ */
+ if (p[0] == 'M' && p[1] == 'Z') {
+ offset = 0;
+ window = 4096;
+ while (offset < (1024 * 128)) {
+ const char *h = __archive_read_ahead(a, offset + window,
+ &bytes_avail);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 128)
+ return (0);
+ continue;
+ }
+ p = h + offset;
+ while (p + 8 < h + bytes_avail) {
+ int next;
+ if ((next = find_cab_magic(p)) == 0)
+ return (64);
+ p += next;
+ }
+ offset = p - h;
+ }
+ }
+ return (0);
+}
+
+static int
+archive_read_format_cab_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct cab *cab;
+ int ret = ARCHIVE_FAILED;
+
+ cab = (struct cab *)(a->format->data);
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "cab: hdrcharset option needs a character-set name");
+ else {
+ cab->sconv = archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (cab->sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+cab_skip_sfx(struct archive_read *a)
+{
+ const char *p, *q;
+ size_t skip;
+ ssize_t bytes, window;
+
+ window = 4096;
+ for (;;) {
+ const char *h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining size are less than window. */
+ window >>= 1;
+ if (window < 128) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out CAB header");
+ return (ARCHIVE_FATAL);
+ }
+ continue;
+ }
+ p = h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the cab header.
+ */
+ while (p + 8 < q) {
+ int next;
+ if ((next = find_cab_magic(p)) == 0) {
+ skip = p - h;
+ __archive_read_consume(a, skip);
+ return (ARCHIVE_OK);
+ }
+ p += next;
+ }
+ skip = p - h;
+ __archive_read_consume(a, skip);
+ }
+}
+
+static int
+truncated_error(struct archive_read *a)
+{
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated CAB header");
+ return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+cab_strnlen(const unsigned char *p, size_t maxlen)
+{
+ size_t i;
+
+ for (i = 0; i <= maxlen; i++) {
+ if (p[i] == 0)
+ break;
+ }
+ if (i > maxlen)
+ return (-1);/* invalid */
+ return ((ssize_t)i);
+}
+
+/* Read bytes as much as remaining. */
+static const void *
+cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail)
+{
+ const void *p;
+
+ while (min > 0) {
+ p = __archive_read_ahead(a, min, avail);
+ if (p != NULL)
+ return (p);
+ min--;
+ }
+ return (NULL);
+}
+
+/* Convert a path separator '\' -> '/' */
+static int
+cab_convert_path_separator_1(struct archive_string *fn, unsigned char attr)
+{
+ size_t i;
+ int mb;
+
+ /* Easy check if we have '\' in multi-byte string. */
+ mb = 0;
+ for (i = 0; i < archive_strlen(fn); i++) {
+ if (fn->s[i] == '\\') {
+ if (mb) {
+ /* This may be second byte of multi-byte
+ * character. */
+ break;
+ }
+ fn->s[i] = '/';
+ mb = 0;
+ } else if ((fn->s[i] & 0x80) && !(attr & ATTR_NAME_IS_UTF))
+ mb = 1;
+ else
+ mb = 0;
+ }
+ if (i == archive_strlen(fn))
+ return (0);
+ return (-1);
+}
+
+/*
+ * Replace a character '\' with '/' in wide character.
+ */
+static void
+cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry)
+{
+ const wchar_t *wp;
+ size_t i;
+
+ /* If a conversion to wide character failed, force the replacement. */
+ if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+ archive_wstrcpy(&(cab->ws), wp);
+ for (i = 0; i < archive_strlen(&(cab->ws)); i++) {
+ if (cab->ws.s[i] == L'\\')
+ cab->ws.s[i] = L'/';
+ }
+ archive_entry_copy_pathname_w(entry, cab->ws.s);
+ }
+}
+
+/*
+ * Read CFHEADER, CFFOLDER and CFFILE.
+ */
+static int
+cab_read_header(struct archive_read *a)
+{
+ const unsigned char *p;
+ struct cab *cab;
+ struct cfheader *hd;
+ size_t bytes, used;
+ ssize_t len;
+ int64_t skip;
+ int err, i;
+ int cur_folder, prev_folder;
+ uint32_t offset32;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CAB;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "CAB";
+
+ if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
+ return (truncated_error(a));
+
+ cab = (struct cab *)(a->format->data);
+ if (cab->found_header == 0 &&
+ p[0] == 'M' && p[1] == 'Z') {
+ /* This is an executable? Must be self-extracting... */
+ err = cab_skip_sfx(a);
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+ /* Re-read header after processing the SFX. */
+ if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
+ return (truncated_error(a));
+ }
+
+ cab->cab_offset = 0;
+ /*
+ * Read CFHEADER.
+ */
+ hd = &cab->cfheader;
+ if (p[CFHEADER_signature+0] != 'M' || p[CFHEADER_signature+1] != 'S' ||
+ p[CFHEADER_signature+2] != 'C' || p[CFHEADER_signature+3] != 'F') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out CAB header");
+ return (ARCHIVE_FATAL);
+ }
+ hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet);
+ hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles);
+ hd->minor = p[CFHEADER_versionMinor];
+ hd->major = p[CFHEADER_versionMajor];
+ hd->folder_count = archive_le16dec(p + CFHEADER_cFolders);
+ if (hd->folder_count == 0)
+ goto invalid;
+ hd->file_count = archive_le16dec(p + CFHEADER_cFiles);
+ if (hd->file_count == 0)
+ goto invalid;
+ hd->flags = archive_le16dec(p + CFHEADER_flags);
+ hd->setid = archive_le16dec(p + CFHEADER_setID);
+ hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet);
+ used = CFHEADER_iCabinet + 2;
+ if (hd->flags & RESERVE_PRESENT) {
+ uint16_t cfheader;
+ cfheader = archive_le16dec(p + CFHEADER_cbCFHeader);
+ if (cfheader > 60000U)
+ goto invalid;
+ hd->cffolder = p[CFHEADER_cbCFFolder];
+ hd->cfdata = p[CFHEADER_cbCFData];
+ used += 4;/* cbCFHeader, cbCFFolder and cbCFData */
+ used += cfheader;/* abReserve */
+ } else
+ hd->cffolder = 0;/* Avoid compiling warning. */
+ if (hd->flags & PREV_CABINET) {
+ /* How many bytes are used for szCabinetPrev. */
+ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p + used, 255)) <= 0)
+ goto invalid;
+ used += len + 1;
+ /* How many bytes are used for szDiskPrev. */
+ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p + used, 255)) <= 0)
+ goto invalid;
+ used += len + 1;
+ }
+ if (hd->flags & NEXT_CABINET) {
+ /* How many bytes are used for szCabinetNext. */
+ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p + used, 255)) <= 0)
+ goto invalid;
+ used += len + 1;
+ /* How many bytes are used for szDiskNext. */
+ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p + used, 255)) <= 0)
+ goto invalid;
+ used += len + 1;
+ }
+ __archive_read_consume(a, used);
+ cab->cab_offset += used;
+ used = 0;
+
+ /*
+ * Read CFFOLDER.
+ */
+ hd->folder_array = (struct cffolder *)calloc(
+ hd->folder_count, sizeof(struct cffolder));
+ if (hd->folder_array == NULL)
+ goto nomem;
+
+ bytes = 8;
+ if (hd->flags & RESERVE_PRESENT)
+ bytes += hd->cffolder;
+ bytes *= hd->folder_count;
+ if ((p = __archive_read_ahead(a, bytes, NULL)) == NULL)
+ return (truncated_error(a));
+ offset32 = 0;
+ for (i = 0; i < hd->folder_count; i++) {
+ struct cffolder *folder = &(hd->folder_array[i]);
+ folder->cfdata_offset_in_cab =
+ archive_le32dec(p + CFFOLDER_coffCabStart);
+ folder->cfdata_count = archive_le16dec(p+CFFOLDER_cCFData);
+ folder->comptype =
+ archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F;
+ folder->compdata =
+ archive_le16dec(p+CFFOLDER_typeCompress) >> 8;
+ /* Get a compression name. */
+ if (folder->comptype <
+ sizeof(compression_name) / sizeof(compression_name[0]))
+ folder->compname = compression_name[folder->comptype];
+ else
+ folder->compname = "UNKNOWN";
+ p += 8;
+ used += 8;
+ if (hd->flags & RESERVE_PRESENT) {
+ p += hd->cffolder;/* abReserve */
+ used += hd->cffolder;
+ }
+ /*
+ * Sanity check if each data is acceptable.
+ */
+ if (offset32 >= folder->cfdata_offset_in_cab)
+ goto invalid;
+ offset32 = folder->cfdata_offset_in_cab;
+
+ /* Set a request to initialize zlib for the CFDATA of
+ * this folder. */
+ folder->decompress_init = 0;
+ }
+ __archive_read_consume(a, used);
+ cab->cab_offset += used;
+
+ /*
+ * Read CFFILE.
+ */
+ /* Seek read pointer to the offset of CFFILE if needed. */
+ skip = (int64_t)hd->files_offset - cab->cab_offset;
+ if (skip < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid offset of CFFILE %jd < %jd",
+ (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset);
+ return (ARCHIVE_FATAL);
+ }
+ if (skip) {
+ __archive_read_consume(a, skip);
+ cab->cab_offset += skip;
+ }
+ /* Allocate memory for CFDATA */
+ hd->file_array = (struct cffile *)calloc(
+ hd->file_count, sizeof(struct cffile));
+ if (hd->file_array == NULL)
+ goto nomem;
+
+ prev_folder = -1;
+ for (i = 0; i < hd->file_count; i++) {
+ struct cffile *file = &(hd->file_array[i]);
+ ssize_t avail;
+
+ if ((p = __archive_read_ahead(a, 16, NULL)) == NULL)
+ return (truncated_error(a));
+ file->uncompressed_size = archive_le32dec(p + CFFILE_cbFile);
+ file->offset = archive_le32dec(p + CFFILE_uoffFolderStart);
+ file->folder = archive_le16dec(p + CFFILE_iFolder);
+ file->mtime = cab_dos_time(p + CFFILE_date_time);
+ file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs);
+ __archive_read_consume(a, 16);
+
+ cab->cab_offset += 16;
+ if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL)
+ return (truncated_error(a));
+ if ((len = cab_strnlen(p, avail-1)) <= 0)
+ goto invalid;
+
+ /* Copy a pathname. */
+ archive_string_init(&(file->pathname));
+ archive_strncpy(&(file->pathname), p, len);
+ __archive_read_consume(a, len + 1);
+ cab->cab_offset += len + 1;
+
+ /*
+ * Sanity check if each data is acceptable.
+ */
+ if (file->uncompressed_size > 0x7FFF8000)
+ goto invalid;/* Too large */
+ if ((int64_t)file->offset + (int64_t)file->uncompressed_size
+ > ARCHIVE_LITERAL_LL(0x7FFF8000))
+ goto invalid;/* Too large */
+ switch (file->folder) {
+ case iFoldCONTINUED_TO_NEXT:
+ /* This must be last file in a folder. */
+ if (i != hd->file_count -1)
+ goto invalid;
+ cur_folder = hd->folder_count -1;
+ break;
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ /* This must be only one file in a folder. */
+ if (hd->file_count != 1)
+ goto invalid;
+ /* FALL THROUGH */
+ case iFoldCONTINUED_FROM_PREV:
+ /* This must be first file in a folder. */
+ if (i != 0)
+ goto invalid;
+ prev_folder = cur_folder = 0;
+ offset32 = file->offset;
+ break;
+ default:
+ if (file->folder >= hd->folder_count)
+ goto invalid;
+ cur_folder = file->folder;
+ break;
+ }
+ /* Dot not back track. */
+ if (cur_folder < prev_folder)
+ goto invalid;
+ if (cur_folder != prev_folder)
+ offset32 = 0;
+ prev_folder = cur_folder;
+
+ /* Make sure there are not any blanks from last file
+ * contents. */
+ if (offset32 != file->offset)
+ goto invalid;
+ offset32 += file->uncompressed_size;
+
+ /* CFDATA is available for file contents. */
+ if (file->uncompressed_size > 0 &&
+ hd->folder_array[cur_folder].cfdata_count == 0)
+ goto invalid;
+ }
+
+ if (hd->cabinet != 0 || hd->flags & (PREV_CABINET | NEXT_CABINET)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Multivolume cabinet file is unsupported");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid CAB header");
+ return (ARCHIVE_FATAL);
+nomem:
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for CAB data");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_cab_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct cab *cab;
+ struct cfheader *hd;
+ struct cffolder *prev_folder;
+ struct cffile *file;
+ struct archive_string_conv *sconv;
+ int err = ARCHIVE_OK, r;
+
+ cab = (struct cab *)(a->format->data);
+ if (cab->found_header == 0) {
+ err = cab_read_header(a);
+ if (err < ARCHIVE_WARN)
+ return (err);
+ /* We've found the header. */
+ cab->found_header = 1;
+ }
+ hd = &cab->cfheader;
+
+ if (hd->file_index >= hd->file_count) {
+ cab->end_of_archive = 1;
+ return (ARCHIVE_EOF);
+ }
+ file = &hd->file_array[hd->file_index++];
+
+ cab->end_of_entry = 0;
+ cab->end_of_entry_cleanup = 0;
+ cab->entry_compressed_bytes_read = 0;
+ cab->entry_uncompressed_bytes_read = 0;
+ cab->entry_unconsumed = 0;
+ cab->entry_cffile = file;
+
+ /*
+ * Choose a proper folder.
+ */
+ prev_folder = cab->entry_cffolder;
+ switch (file->folder) {
+ case iFoldCONTINUED_FROM_PREV:
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ cab->entry_cffolder = &hd->folder_array[0];
+ break;
+ case iFoldCONTINUED_TO_NEXT:
+ cab->entry_cffolder = &hd->folder_array[hd->folder_count-1];
+ break;
+ default:
+ cab->entry_cffolder = &hd->folder_array[file->folder];
+ break;
+ }
+ /* If a cffolder of this file is changed, reset a cfdata to read
+ * file contents from next cfdata. */
+ if (prev_folder != cab->entry_cffolder)
+ cab->entry_cfdata = NULL;
+
+ /* If a pathname is UTF-8, prepare a string conversion object
+ * for UTF-8 and use it. */
+ if (file->attr & ATTR_NAME_IS_UTF) {
+ if (cab->sconv_utf8 == NULL) {
+ cab->sconv_utf8 =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (cab->sconv_utf8 == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ sconv = cab->sconv_utf8;
+ } else if (cab->sconv != NULL) {
+ /* Choose the conversion specified by the option. */
+ sconv = cab->sconv;
+ } else {
+ /* Choose the default conversion. */
+ if (!cab->init_default_conversion) {
+ cab->sconv_default =
+ archive_string_default_conversion_for_read(
+ &(a->archive));
+ cab->init_default_conversion = 1;
+ }
+ sconv = cab->sconv_default;
+ }
+
+ /*
+ * Set a default value and common data
+ */
+ r = cab_convert_path_separator_1(&(file->pathname), file->attr);
+ if (archive_entry_copy_pathname_l(entry, file->pathname.s,
+ archive_strlen(&(file->pathname)), sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(sconv));
+ err = ARCHIVE_WARN;
+ }
+ if (r < 0) {
+ /* Convert a path separator '\' -> '/' */
+ cab_convert_path_separator_2(cab, entry);
+ }
+
+ archive_entry_set_size(entry, file->uncompressed_size);
+ if (file->attr & ATTR_RDONLY)
+ archive_entry_set_mode(entry, AE_IFREG | 0555);
+ else
+ archive_entry_set_mode(entry, AE_IFREG | 0666);
+ archive_entry_set_mtime(entry, file->mtime, 0);
+
+ cab->entry_bytes_remaining = file->uncompressed_size;
+ cab->entry_offset = 0;
+ /* We don't need compress data. */
+ if (file->uncompressed_size == 0)
+ cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+
+ /* Set up a more descriptive format name. */
+ snprintf(cab->format_name, sizeof(cab->format_name), "CAB %d.%d (%s)",
+ hd->major, hd->minor, cab->entry_cffolder->compname);
+ a->archive.archive_format_name = cab->format_name;
+
+ return (err);
+}
+
+static int
+archive_read_format_cab_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ int r;
+
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_FROM_PREV:
+ case iFoldCONTINUED_TO_NEXT:
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ *buff = NULL;
+ *size = 0;
+ *offset = 0;
+ archive_clear_error(&a->archive);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Cannot restore this file split in multivolume.");
+ return (ARCHIVE_FAILED);
+ default:
+ break;
+ }
+ if (cab->read_data_invoked == 0) {
+ if (cab->bytes_skipped) {
+ if (cab->entry_cfdata == NULL) {
+ r = cab_next_cfdata(a);
+ if (r < 0)
+ return (r);
+ }
+ if (cab_consume_cfdata(a, cab->bytes_skipped) < 0)
+ return (ARCHIVE_FATAL);
+ cab->bytes_skipped = 0;
+ }
+ cab->read_data_invoked = 1;
+ }
+ if (cab->entry_unconsumed) {
+ /* Consume as much as the compressor actually used. */
+ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
+ cab->entry_unconsumed = 0;
+ if (r < 0)
+ return (r);
+ }
+ if (cab->end_of_archive || cab->end_of_entry) {
+ if (!cab->end_of_entry_cleanup) {
+ /* End-of-entry cleanup done. */
+ cab->end_of_entry_cleanup = 1;
+ }
+ *offset = cab->entry_offset;
+ *size = 0;
+ *buff = NULL;
+ return (ARCHIVE_EOF);
+ }
+
+ return (cab_read_data(a, buff, size, offset));
+}
+
+static uint32_t
+cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed)
+{
+ const unsigned char *b;
+ unsigned u32num;
+ uint32_t sum;
+
+ u32num = (unsigned)bytes / 4;
+ sum = seed;
+ b = p;
+ for (;u32num > 0; --u32num) {
+ sum ^= archive_le32dec(b);
+ b += 4;
+ }
+ return (sum);
+}
+
+static uint32_t
+cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed)
+{
+ const unsigned char *b;
+ uint32_t sum;
+ uint32_t t;
+
+ sum = cab_checksum_cfdata_4(p, bytes, seed);
+ b = p;
+ b += bytes & ~3;
+ t = 0;
+ switch (bytes & 3) {
+ case 3:
+ t |= ((uint32_t)(*b++)) << 16;
+ /* FALL THROUGH */
+ case 2:
+ t |= ((uint32_t)(*b++)) << 8;
+ /* FALL THROUGH */
+ case 1:
+ t |= *b;
+ /* FALL THROUGH */
+ default:
+ break;
+ }
+ sum ^= t;
+
+ return (sum);
+}
+
+static void
+cab_checksum_update(struct archive_read *a, size_t bytes)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata = cab->entry_cfdata;
+ const unsigned char *p;
+ size_t sumbytes;
+
+ if (cfdata->sum == 0 || cfdata->sum_ptr == NULL)
+ return;
+ /*
+ * Calculate the sum of this CFDATA.
+ * Make sure CFDATA must be calculated in four bytes.
+ */
+ p = cfdata->sum_ptr;
+ sumbytes = bytes;
+ if (cfdata->sum_extra_avail) {
+ while (cfdata->sum_extra_avail < 4 && sumbytes > 0) {
+ cfdata->sum_extra[
+ cfdata->sum_extra_avail++] = *p++;
+ sumbytes--;
+ }
+ if (cfdata->sum_extra_avail == 4) {
+ cfdata->sum_calculated = cab_checksum_cfdata_4(
+ cfdata->sum_extra, 4, cfdata->sum_calculated);
+ cfdata->sum_extra_avail = 0;
+ }
+ }
+ if (sumbytes) {
+ int odd = sumbytes & 3;
+ if ((int)(sumbytes - odd) > 0)
+ cfdata->sum_calculated = cab_checksum_cfdata_4(
+ p, sumbytes - odd, cfdata->sum_calculated);
+ if (odd)
+ memcpy(cfdata->sum_extra, p + sumbytes - odd, odd);
+ cfdata->sum_extra_avail = odd;
+ }
+ cfdata->sum_ptr = NULL;
+}
+
+static int
+cab_checksum_finish(struct archive_read *a)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata = cab->entry_cfdata;
+ int l;
+
+ /* Do not need to compute a sum. */
+ if (cfdata->sum == 0)
+ return (ARCHIVE_OK);
+
+ /*
+ * Calculate the sum of remaining CFDATA.
+ */
+ if (cfdata->sum_extra_avail) {
+ cfdata->sum_calculated =
+ cab_checksum_cfdata(cfdata->sum_extra,
+ cfdata->sum_extra_avail, cfdata->sum_calculated);
+ cfdata->sum_extra_avail = 0;
+ }
+
+ l = 4;
+ if (cab->cfheader.flags & RESERVE_PRESENT)
+ l += cab->cfheader.cfdata;
+ cfdata->sum_calculated = cab_checksum_cfdata(
+ cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);
+ if (cfdata->sum_calculated != cfdata->sum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Checksum error CFDATA[%d] %" PRIx32 ":%" PRIx32 " in %d bytes",
+ cab->entry_cffolder->cfdata_index -1,
+ cfdata->sum, cfdata->sum_calculated,
+ cfdata->compressed_size);
+ return (ARCHIVE_FAILED);
+#endif
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Read CFDATA if needed.
+ */
+static int
+cab_next_cfdata(struct archive_read *a)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata = cab->entry_cfdata;
+
+ /* There are remaining bytes in current CFDATA, use it first. */
+ if (cfdata != NULL && cfdata->uncompressed_bytes_remaining > 0)
+ return (ARCHIVE_OK);
+
+ if (cfdata == NULL) {
+ int64_t skip;
+
+ cab->entry_cffolder->cfdata_index = 0;
+
+ /* Seek read pointer to the offset of CFDATA if needed. */
+ skip = cab->entry_cffolder->cfdata_offset_in_cab
+ - cab->cab_offset;
+ if (skip < 0) {
+ int folder_index;
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_FROM_PREV:
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ folder_index = 0;
+ break;
+ case iFoldCONTINUED_TO_NEXT:
+ folder_index = cab->cfheader.folder_count-1;
+ break;
+ default:
+ folder_index = cab->entry_cffile->folder;
+ break;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid offset of CFDATA in folder(%d) %jd < %jd",
+ folder_index,
+ (intmax_t)cab->entry_cffolder->cfdata_offset_in_cab,
+ (intmax_t)cab->cab_offset);
+ return (ARCHIVE_FATAL);
+ }
+ if (skip > 0) {
+ if (__archive_read_consume(a, skip) < 0)
+ return (ARCHIVE_FATAL);
+ cab->cab_offset =
+ cab->entry_cffolder->cfdata_offset_in_cab;
+ }
+ }
+
+ /*
+ * Read a CFDATA.
+ */
+ if (cab->entry_cffolder->cfdata_index <
+ cab->entry_cffolder->cfdata_count) {
+ const unsigned char *p;
+ int l;
+
+ cfdata = &(cab->entry_cffolder->cfdata);
+ cab->entry_cffolder->cfdata_index++;
+ cab->entry_cfdata = cfdata;
+ cfdata->sum_calculated = 0;
+ cfdata->sum_extra_avail = 0;
+ cfdata->sum_ptr = NULL;
+ l = 8;
+ if (cab->cfheader.flags & RESERVE_PRESENT)
+ l += cab->cfheader.cfdata;
+ if ((p = __archive_read_ahead(a, l, NULL)) == NULL)
+ return (truncated_error(a));
+ cfdata->sum = archive_le32dec(p + CFDATA_csum);
+ cfdata->compressed_size = archive_le16dec(p + CFDATA_cbData);
+ cfdata->compressed_bytes_remaining = cfdata->compressed_size;
+ cfdata->uncompressed_size =
+ archive_le16dec(p + CFDATA_cbUncomp);
+ cfdata->uncompressed_bytes_remaining =
+ cfdata->uncompressed_size;
+ cfdata->uncompressed_avail = 0;
+ cfdata->read_offset = 0;
+ cfdata->unconsumed = 0;
+
+ /*
+ * Sanity check if data size is acceptable.
+ */
+ if (cfdata->compressed_size == 0 ||
+ cfdata->compressed_size > (0x8000+6144))
+ goto invalid;
+ if (cfdata->uncompressed_size > 0x8000)
+ goto invalid;
+ if (cfdata->uncompressed_size == 0) {
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ case iFoldCONTINUED_TO_NEXT:
+ break;
+ case iFoldCONTINUED_FROM_PREV:
+ default:
+ goto invalid;
+ }
+ }
+ /* If CFDATA is not last in a folder, an uncompressed
+ * size must be 0x8000(32KBi) */
+ if ((cab->entry_cffolder->cfdata_index <
+ cab->entry_cffolder->cfdata_count) &&
+ cfdata->uncompressed_size != 0x8000)
+ goto invalid;
+
+ /* A compressed data size and an uncompressed data size must
+ * be the same in no compression mode. */
+ if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
+ cfdata->compressed_size != cfdata->uncompressed_size)
+ goto invalid;
+
+ /*
+ * Save CFDATA image for sum check.
+ */
+ if (cfdata->memimage_size < (size_t)l) {
+ free(cfdata->memimage);
+ cfdata->memimage = malloc(l);
+ if (cfdata->memimage == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for CAB data");
+ return (ARCHIVE_FATAL);
+ }
+ cfdata->memimage_size = l;
+ }
+ memcpy(cfdata->memimage, p, l);
+
+ /* Consume bytes as much as we used. */
+ __archive_read_consume(a, l);
+ cab->cab_offset += l;
+ } else if (cab->entry_cffolder->cfdata_count > 0) {
+ /* Run out of all CFDATA in a folder. */
+ cfdata->compressed_size = 0;
+ cfdata->uncompressed_size = 0;
+ cfdata->compressed_bytes_remaining = 0;
+ cfdata->uncompressed_bytes_remaining = 0;
+ } else {
+ /* Current folder does not have any CFDATA. */
+ cfdata = &(cab->entry_cffolder->cfdata);
+ cab->entry_cfdata = cfdata;
+ memset(cfdata, 0, sizeof(*cfdata));
+ }
+ return (ARCHIVE_OK);
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid CFDATA");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Read ahead CFDATA.
+ */
+static const void *
+cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ int err;
+
+ err = cab_next_cfdata(a);
+ if (err < ARCHIVE_OK) {
+ *avail = err;
+ return (NULL);
+ }
+
+ switch (cab->entry_cffolder->comptype) {
+ case COMPTYPE_NONE:
+ return (cab_read_ahead_cfdata_none(a, avail));
+ case COMPTYPE_MSZIP:
+ return (cab_read_ahead_cfdata_deflate(a, avail));
+ case COMPTYPE_LZX:
+ return (cab_read_ahead_cfdata_lzx(a, avail));
+ default: /* Unsupported compression. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported CAB compression : %s",
+ cab->entry_cffolder->compname);
+ *avail = ARCHIVE_FAILED;
+ return (NULL);
+ }
+}
+
+/*
+ * Read ahead CFDATA as uncompressed data.
+ */
+static const void *
+cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ const void *d;
+
+ cfdata = cab->entry_cfdata;
+
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ d = __archive_read_ahead(a, 1, avail);
+ if (*avail <= 0) {
+ *avail = truncated_error(a);
+ return (NULL);
+ }
+ if (*avail > cfdata->uncompressed_bytes_remaining)
+ *avail = cfdata->uncompressed_bytes_remaining;
+ cfdata->uncompressed_avail = cfdata->uncompressed_size;
+ cfdata->unconsumed = *avail;
+ cfdata->sum_ptr = d;
+ return (d);
+}
+
+/*
+ * Read ahead CFDATA as deflate data.
+ */
+#ifdef HAVE_ZLIB_H
+static const void *
+cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ const void *d;
+ int r, mszip;
+ uint16_t uavail;
+ char eod = 0;
+
+ cfdata = cab->entry_cfdata;
+ /* If the buffer hasn't been allocated, allocate it now. */
+ if (cab->uncompressed_buffer == NULL) {
+ cab->uncompressed_buffer_size = 0x8000;
+ cab->uncompressed_buffer
+ = (unsigned char *)malloc(cab->uncompressed_buffer_size);
+ if (cab->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for CAB reader");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ uavail = cfdata->uncompressed_avail;
+ if (uavail == cfdata->uncompressed_size) {
+ d = cab->uncompressed_buffer + cfdata->read_offset;
+ *avail = uavail - cfdata->read_offset;
+ return (d);
+ }
+
+ if (!cab->entry_cffolder->decompress_init) {
+ cab->stream.next_in = NULL;
+ cab->stream.avail_in = 0;
+ cab->stream.total_in = 0;
+ cab->stream.next_out = NULL;
+ cab->stream.avail_out = 0;
+ cab->stream.total_out = 0;
+ if (cab->stream_valid)
+ r = inflateReset(&cab->stream);
+ else
+ r = inflateInit2(&cab->stream,
+ -15 /* Don't check for zlib header */);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize deflate decompression.");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ /* Stream structure has been set up. */
+ cab->stream_valid = 1;
+ /* We've initialized decompression for this stream. */
+ cab->entry_cffolder->decompress_init = 1;
+ }
+
+ if (cfdata->compressed_bytes_remaining == cfdata->compressed_size)
+ mszip = 2;
+ else
+ mszip = 0;
+ eod = 0;
+ cab->stream.total_out = uavail;
+ /*
+ * We always uncompress all data in current CFDATA.
+ */
+ while (!eod && cab->stream.total_out < cfdata->uncompressed_size) {
+ ssize_t bytes_avail;
+
+ cab->stream.next_out =
+ cab->uncompressed_buffer + cab->stream.total_out;
+ cab->stream.avail_out =
+ cfdata->uncompressed_size - cab->stream.total_out;
+
+ d = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ *avail = truncated_error(a);
+ return (NULL);
+ }
+ if (bytes_avail > cfdata->compressed_bytes_remaining)
+ bytes_avail = cfdata->compressed_bytes_remaining;
+ /*
+ * A bug in zlib.h: stream.next_in should be marked 'const'
+ * but isn't (the library never alters data through the
+ * next_in pointer, only reads it). The result: this ugly
+ * cast to remove 'const'.
+ */
+ cab->stream.next_in = (Bytef *)(uintptr_t)d;
+ cab->stream.avail_in = (uInt)bytes_avail;
+ cab->stream.total_in = 0;
+
+ /* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */
+ if (mszip > 0) {
+ if (bytes_avail <= 0)
+ goto nomszip;
+ if (bytes_avail <= mszip) {
+ if (mszip == 2) {
+ if (cab->stream.next_in[0] != 0x43)
+ goto nomszip;
+ if (bytes_avail > 1 &&
+ cab->stream.next_in[1] != 0x4b)
+ goto nomszip;
+ } else if (cab->stream.next_in[0] != 0x4b)
+ goto nomszip;
+ cfdata->unconsumed = bytes_avail;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(
+ a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ mszip -= (int)bytes_avail;
+ continue;
+ }
+ if (mszip == 1 && cab->stream.next_in[0] != 0x4b)
+ goto nomszip;
+ else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 ||
+ cab->stream.next_in[1] != 0x4b))
+ goto nomszip;
+ cab->stream.next_in += mszip;
+ cab->stream.avail_in -= mszip;
+ cab->stream.total_in += mszip;
+ mszip = 0;
+ }
+
+ r = inflate(&cab->stream, 0);
+ switch (r) {
+ case Z_OK:
+ break;
+ case Z_STREAM_END:
+ eod = 1;
+ break;
+ default:
+ goto zlibfailed;
+ }
+ cfdata->unconsumed = cab->stream.total_in;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+ uavail = (uint16_t)cab->stream.total_out;
+
+ if (uavail < cfdata->uncompressed_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid uncompressed size (%d < %d)",
+ uavail, cfdata->uncompressed_size);
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+
+ /*
+ * Note: I suspect there is a bug in makecab.exe because, in rare
+ * case, compressed bytes are still remaining regardless we have
+ * gotten all uncompressed bytes, which size is recorded in CFDATA,
+ * as much as we need, and we have to use the garbage so as to
+ * correctly compute the sum of CFDATA accordingly.
+ */
+ if (cfdata->compressed_bytes_remaining > 0) {
+ ssize_t bytes_avail;
+
+ d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
+ &bytes_avail);
+ if (bytes_avail <= 0) {
+ *avail = truncated_error(a);
+ return (NULL);
+ }
+ cfdata->unconsumed = cfdata->compressed_bytes_remaining;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ /*
+ * Set dictionary data for decompressing of next CFDATA, which
+ * in the same folder. This is why we always do decompress CFDATA
+ * even if beginning CFDATA or some of CFDATA are not used in
+ * skipping file data.
+ */
+ if (cab->entry_cffolder->cfdata_index <
+ cab->entry_cffolder->cfdata_count) {
+ r = inflateReset(&cab->stream);
+ if (r != Z_OK)
+ goto zlibfailed;
+ r = inflateSetDictionary(&cab->stream,
+ cab->uncompressed_buffer, cfdata->uncompressed_size);
+ if (r != Z_OK)
+ goto zlibfailed;
+ }
+
+ d = cab->uncompressed_buffer + cfdata->read_offset;
+ *avail = uavail - cfdata->read_offset;
+ cfdata->uncompressed_avail = uavail;
+
+ return (d);
+
+zlibfailed:
+ switch (r) {
+ case Z_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory for deflate decompression");
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Deflate decompression failed (%d)", r);
+ break;
+ }
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+nomszip:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "CFDATA incorrect(no MSZIP signature)");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+}
+
+#else /* HAVE_ZLIB_H */
+
+static const void *
+cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
+{
+ *avail = ARCHIVE_FATAL;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "libarchive compiled without deflate support (no libz)");
+ return (NULL);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static const void *
+cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ const void *d;
+ int r;
+ uint16_t uavail;
+
+ cfdata = cab->entry_cfdata;
+ /* If the buffer hasn't been allocated, allocate it now. */
+ if (cab->uncompressed_buffer == NULL) {
+ cab->uncompressed_buffer_size = 0x8000;
+ cab->uncompressed_buffer
+ = (unsigned char *)malloc(cab->uncompressed_buffer_size);
+ if (cab->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for CAB reader");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ uavail = cfdata->uncompressed_avail;
+ if (uavail == cfdata->uncompressed_size) {
+ d = cab->uncompressed_buffer + cfdata->read_offset;
+ *avail = uavail - cfdata->read_offset;
+ return (d);
+ }
+
+ if (!cab->entry_cffolder->decompress_init) {
+ r = lzx_decode_init(&cab->xstrm,
+ cab->entry_cffolder->compdata);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize LZX decompression.");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ /* We've initialized decompression for this stream. */
+ cab->entry_cffolder->decompress_init = 1;
+ }
+
+ /* Clean up remaining bits of previous CFDATA. */
+ lzx_cleanup_bitstream(&cab->xstrm);
+ cab->xstrm.total_out = uavail;
+ while (cab->xstrm.total_out < cfdata->uncompressed_size) {
+ ssize_t bytes_avail;
+
+ cab->xstrm.next_out =
+ cab->uncompressed_buffer + cab->xstrm.total_out;
+ cab->xstrm.avail_out =
+ cfdata->uncompressed_size - cab->xstrm.total_out;
+
+ d = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated CAB file data");
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ if (bytes_avail > cfdata->compressed_bytes_remaining)
+ bytes_avail = cfdata->compressed_bytes_remaining;
+
+ cab->xstrm.next_in = d;
+ cab->xstrm.avail_in = bytes_avail;
+ cab->xstrm.total_in = 0;
+ r = lzx_decode(&cab->xstrm,
+ cfdata->compressed_bytes_remaining == bytes_avail);
+ switch (r) {
+ case ARCHIVE_OK:
+ case ARCHIVE_EOF:
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LZX decompression failed (%d)", r);
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ cfdata->unconsumed = cab->xstrm.total_in;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ uavail = (uint16_t)cab->xstrm.total_out;
+ /*
+ * Make sure a read pointer advances to next CFDATA.
+ */
+ if (cfdata->compressed_bytes_remaining > 0) {
+ ssize_t bytes_avail;
+
+ d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
+ &bytes_avail);
+ if (bytes_avail <= 0) {
+ *avail = truncated_error(a);
+ return (NULL);
+ }
+ cfdata->unconsumed = cfdata->compressed_bytes_remaining;
+ cfdata->sum_ptr = d;
+ if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+ *avail = ARCHIVE_FATAL;
+ return (NULL);
+ }
+ }
+
+ /*
+ * Translation reversal of x86 processor CALL byte sequence(E8).
+ */
+ lzx_translation(&cab->xstrm, cab->uncompressed_buffer,
+ cfdata->uncompressed_size,
+ (cab->entry_cffolder->cfdata_index-1) * 0x8000);
+
+ d = cab->uncompressed_buffer + cfdata->read_offset;
+ *avail = uavail - cfdata->read_offset;
+ cfdata->uncompressed_avail = uavail;
+
+ return (d);
+}
+
+/*
+ * Consume CFDATA.
+ * We always decompress CFDATA to consume CFDATA as much as we need
+ * in uncompressed bytes because all CFDATA in a folder are related
+ * so we do not skip any CFDATA without decompressing.
+ * Note: If the folder of a CFFILE is iFoldCONTINUED_PREV_AND_NEXT or
+ * iFoldCONTINUED_FROM_PREV, we won't decompress because a CFDATA for
+ * the CFFILE is remaining bytes of previous Multivolume CAB file.
+ */
+static int64_t
+cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ int64_t cbytes, rbytes;
+ int err;
+
+ rbytes = cab_minimum_consume_cfdata(a, consumed_bytes);
+ if (rbytes < 0)
+ return (ARCHIVE_FATAL);
+
+ cfdata = cab->entry_cfdata;
+ while (rbytes > 0) {
+ ssize_t avail;
+
+ if (cfdata->compressed_size == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid CFDATA");
+ return (ARCHIVE_FATAL);
+ }
+ cbytes = cfdata->uncompressed_bytes_remaining;
+ if (cbytes > rbytes)
+ cbytes = rbytes;
+ rbytes -= cbytes;
+
+ if (cfdata->uncompressed_avail == 0 &&
+ (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT ||
+ cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) {
+ /* We have not read any data yet. */
+ if (cbytes == cfdata->uncompressed_bytes_remaining) {
+ /* Skip whole current CFDATA. */
+ __archive_read_consume(a,
+ cfdata->compressed_size);
+ cab->cab_offset += cfdata->compressed_size;
+ cfdata->compressed_bytes_remaining = 0;
+ cfdata->uncompressed_bytes_remaining = 0;
+ err = cab_next_cfdata(a);
+ if (err < 0)
+ return (err);
+ cfdata = cab->entry_cfdata;
+ if (cfdata->uncompressed_size == 0) {
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ case iFoldCONTINUED_TO_NEXT:
+ case iFoldCONTINUED_FROM_PREV:
+ rbytes = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ continue;
+ }
+ cfdata->read_offset += (uint16_t)cbytes;
+ cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+ break;
+ } else if (cbytes == 0) {
+ err = cab_next_cfdata(a);
+ if (err < 0)
+ return (err);
+ cfdata = cab->entry_cfdata;
+ if (cfdata->uncompressed_size == 0) {
+ switch (cab->entry_cffile->folder) {
+ case iFoldCONTINUED_PREV_AND_NEXT:
+ case iFoldCONTINUED_TO_NEXT:
+ case iFoldCONTINUED_FROM_PREV:
+ return (ARCHIVE_FATAL);
+ default:
+ break;
+ }
+ }
+ continue;
+ }
+ while (cbytes > 0) {
+ (void)cab_read_ahead_cfdata(a, &avail);
+ if (avail <= 0)
+ return (ARCHIVE_FATAL);
+ if (avail > cbytes)
+ avail = (ssize_t)cbytes;
+ if (cab_minimum_consume_cfdata(a, avail) < 0)
+ return (ARCHIVE_FATAL);
+ cbytes -= avail;
+ }
+ }
+ return (consumed_bytes);
+}
+
+/*
+ * Consume CFDATA as much as we have already gotten and
+ * compute the sum of CFDATA.
+ */
+static int64_t
+cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfdata *cfdata;
+ int64_t cbytes, rbytes;
+ int err;
+
+ cfdata = cab->entry_cfdata;
+ rbytes = consumed_bytes;
+ if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {
+ if (consumed_bytes < cfdata->unconsumed)
+ cbytes = consumed_bytes;
+ else
+ cbytes = cfdata->unconsumed;
+ rbytes -= cbytes;
+ cfdata->read_offset += (uint16_t)cbytes;
+ cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+ cfdata->unconsumed -= cbytes;
+ } else {
+ cbytes = cfdata->uncompressed_avail - cfdata->read_offset;
+ if (cbytes > 0) {
+ if (consumed_bytes < cbytes)
+ cbytes = consumed_bytes;
+ rbytes -= cbytes;
+ cfdata->read_offset += (uint16_t)cbytes;
+ cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+ }
+
+ if (cfdata->unconsumed) {
+ cbytes = cfdata->unconsumed;
+ cfdata->unconsumed = 0;
+ } else
+ cbytes = 0;
+ }
+ if (cbytes) {
+ /* Compute the sum. */
+ cab_checksum_update(a, (size_t)cbytes);
+
+ /* Consume as much as the compressor actually used. */
+ __archive_read_consume(a, cbytes);
+ cab->cab_offset += cbytes;
+ cfdata->compressed_bytes_remaining -= (uint16_t)cbytes;
+ if (cfdata->compressed_bytes_remaining == 0) {
+ err = cab_checksum_finish(a);
+ if (err < 0)
+ return (err);
+ }
+ }
+ return (rbytes);
+}
+
+/*
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * cab->end_of_entry if it consumes all of the data.
+ */
+static int
+cab_read_data(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ ssize_t bytes_avail;
+
+ if (cab->entry_bytes_remaining == 0) {
+ *buff = NULL;
+ *size = 0;
+ *offset = cab->entry_offset;
+ cab->end_of_entry = 1;
+ return (ARCHIVE_OK);
+ }
+
+ *buff = cab_read_ahead_cfdata(a, &bytes_avail);
+ if (bytes_avail <= 0) {
+ *buff = NULL;
+ *size = 0;
+ *offset = 0;
+ if (bytes_avail == 0 &&
+ cab->entry_cfdata->uncompressed_size == 0) {
+ /* All of CFDATA in a folder has been handled. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA");
+ return (ARCHIVE_FATAL);
+ } else
+ return ((int)bytes_avail);
+ }
+ if (bytes_avail > cab->entry_bytes_remaining)
+ bytes_avail = (ssize_t)cab->entry_bytes_remaining;
+
+ *size = bytes_avail;
+ *offset = cab->entry_offset;
+ cab->entry_offset += bytes_avail;
+ cab->entry_bytes_remaining -= bytes_avail;
+ if (cab->entry_bytes_remaining == 0)
+ cab->end_of_entry = 1;
+ cab->entry_unconsumed = bytes_avail;
+ if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {
+ /* Don't consume more than current entry used. */
+ if (cab->entry_cfdata->unconsumed > cab->entry_unconsumed)
+ cab->entry_cfdata->unconsumed = cab->entry_unconsumed;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cab_read_data_skip(struct archive_read *a)
+{
+ struct cab *cab;
+ int64_t bytes_skipped;
+ int r;
+
+ cab = (struct cab *)(a->format->data);
+
+ if (cab->end_of_archive)
+ return (ARCHIVE_EOF);
+
+ if (!cab->read_data_invoked) {
+ cab->bytes_skipped += cab->entry_bytes_remaining;
+ cab->entry_bytes_remaining = 0;
+ /* This entry is finished and done. */
+ cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+ return (ARCHIVE_OK);
+ }
+
+ if (cab->entry_unconsumed) {
+ /* Consume as much as the compressor actually used. */
+ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
+ cab->entry_unconsumed = 0;
+ if (r < 0)
+ return (r);
+ } else if (cab->entry_cfdata == NULL) {
+ r = cab_next_cfdata(a);
+ if (r < 0)
+ return (r);
+ }
+
+ /* if we've already read to end of data, we're done. */
+ if (cab->end_of_entry_cleanup)
+ return (ARCHIVE_OK);
+
+ /*
+ * If the length is at the beginning, we can skip the
+ * compressed data much more quickly.
+ */
+ bytes_skipped = cab_consume_cfdata(a, cab->entry_bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ /* If the compression type is none(uncompressed), we've already
+ * consumed data as much as the current entry size. */
+ if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
+ cab->entry_cfdata != NULL)
+ cab->entry_cfdata->unconsumed = 0;
+
+ /* This entry is finished and done. */
+ cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cab_cleanup(struct archive_read *a)
+{
+ struct cab *cab = (struct cab *)(a->format->data);
+ struct cfheader *hd = &cab->cfheader;
+ int i;
+
+ if (hd->folder_array != NULL) {
+ for (i = 0; i < hd->folder_count; i++)
+ free(hd->folder_array[i].cfdata.memimage);
+ free(hd->folder_array);
+ }
+ if (hd->file_array != NULL) {
+ for (i = 0; i < cab->cfheader.file_count; i++)
+ archive_string_free(&(hd->file_array[i].pathname));
+ free(hd->file_array);
+ }
+#ifdef HAVE_ZLIB_H
+ if (cab->stream_valid)
+ inflateEnd(&cab->stream);
+#endif
+ lzx_decode_free(&cab->xstrm);
+ archive_wstring_free(&cab->ws);
+ free(cab->uncompressed_buffer);
+ free(cab);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+cab_dos_time(const unsigned char *p)
+{
+ int msTime, msDate;
+ struct tm ts;
+
+ msDate = archive_le16dec(p);
+ msTime = archive_le16dec(p+2);
+
+ memset(&ts, 0, sizeof(ts));
+ ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
+ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
+ ts.tm_mday = msDate & 0x1f; /* Day of month. */
+ ts.tm_hour = (msTime >> 11) & 0x1f;
+ ts.tm_min = (msTime >> 5) & 0x3f;
+ ts.tm_sec = (msTime << 1) & 0x3e;
+ ts.tm_isdst = -1;
+ return (mktime(&ts));
+}
+
+/*****************************************************************
+ *
+ * LZX decompression code.
+ *
+ *****************************************************************/
+
+/*
+ * Initialize LZX decoder.
+ *
+ * Returns ARCHIVE_OK if initialization was successful.
+ * Returns ARCHIVE_FAILED if w_bits has unsupported value.
+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation
+ * error occurred.
+ */
+static int
+lzx_decode_init(struct lzx_stream *strm, int w_bits)
+{
+ struct lzx_dec *ds;
+ int slot, w_size, w_slot;
+ int base, footer;
+ int base_inc[18];
+
+ if (strm->ds == NULL) {
+ strm->ds = calloc(1, sizeof(*strm->ds));
+ if (strm->ds == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ ds = strm->ds;
+ ds->error = ARCHIVE_FAILED;
+
+ /* Allow bits from 15(32KBi) up to 21(2MBi) */
+ if (w_bits < SLOT_BASE || w_bits > SLOT_MAX)
+ return (ARCHIVE_FAILED);
+
+ ds->error = ARCHIVE_FATAL;
+
+ /*
+ * Alloc window
+ */
+ w_size = ds->w_size;
+ w_slot = slots[w_bits - SLOT_BASE];
+ ds->w_size = 1U << w_bits;
+ ds->w_mask = ds->w_size -1;
+ if (ds->w_buff == NULL || w_size != ds->w_size) {
+ free(ds->w_buff);
+ ds->w_buff = malloc(ds->w_size);
+ if (ds->w_buff == NULL)
+ return (ARCHIVE_FATAL);
+ free(ds->pos_tbl);
+ ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot);
+ if (ds->pos_tbl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ for (footer = 0; footer < 18; footer++)
+ base_inc[footer] = 1 << footer;
+ base = footer = 0;
+ for (slot = 0; slot < w_slot; slot++) {
+ int n;
+ if (footer == 0)
+ base = slot;
+ else
+ base += base_inc[footer];
+ if (footer < 17) {
+ footer = -2;
+ for (n = base; n; n >>= 1)
+ footer++;
+ if (footer <= 0)
+ footer = 0;
+ }
+ ds->pos_tbl[slot].base = base;
+ ds->pos_tbl[slot].footer_bits = footer;
+ }
+
+ ds->w_pos = 0;
+ ds->state = 0;
+ ds->br.cache_buffer = 0;
+ ds->br.cache_avail = 0;
+ ds->r0 = ds->r1 = ds->r2 = 1;
+
+ /* Initialize aligned offset tree. */
+ if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Initialize pre-tree. */
+ if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Initialize Main tree. */
+ if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Initialize Length tree. */
+ if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ ds->error = 0;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Release LZX decoder.
+ */
+static void
+lzx_decode_free(struct lzx_stream *strm)
+{
+
+ if (strm->ds == NULL)
+ return;
+ free(strm->ds->w_buff);
+ free(strm->ds->pos_tbl);
+ lzx_huffman_free(&(strm->ds->at));
+ lzx_huffman_free(&(strm->ds->pt));
+ lzx_huffman_free(&(strm->ds->mt));
+ lzx_huffman_free(&(strm->ds->lt));
+ free(strm->ds);
+ strm->ds = NULL;
+}
+
+/*
+ * E8 Call Translation reversal.
+ */
+static void
+lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
+{
+ struct lzx_dec *ds = strm->ds;
+ unsigned char *b, *end;
+
+ if (!ds->translation || size <= 10)
+ return;
+ b = p;
+ end = b + size - 10;
+ while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) {
+ size_t i = b - (unsigned char *)p;
+ int32_t cp, displacement, value;
+
+ cp = (int32_t)(offset + (uint32_t)i);
+ value = archive_le32dec(&b[1]);
+ if (value >= -cp && value < (int32_t)ds->translation_size) {
+ if (value >= 0)
+ displacement = value - cp;
+ else
+ displacement = value + ds->translation_size;
+ archive_le32enc(&b[1], (uint32_t)displacement);
+ }
+ b += 5;
+ }
+}
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define lzx_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define lzx_br_bits(br, n) \
+ (((uint32_t)((br)->cache_buffer >> \
+ ((br)->cache_avail - (n)))) & cache_masks[n])
+#define lzx_br_bits_forced(br, n) \
+ (((uint32_t)((br)->cache_buffer << \
+ ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ * True : completed, there is enough data in the cache buffer.
+ * False : we met that strm->next_in is empty, we have to get following
+ * bytes. */
+#define lzx_br_read_ahead_0(strm, br, n) \
+ (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br))
+/* True : the cache buffer has some bits as much as we need.
+ * False : there are no enough bits in the cache buffer to be used,
+ * we have to get following bytes if we could. */
+#define lzx_br_read_ahead(strm, br, n) \
+ (lzx_br_read_ahead_0((strm), (br), (n)) || lzx_br_has((br), (n)))
+
+/* Notify how many bits we consumed. */
+#define lzx_br_consume(br, n) ((br)->cache_avail -= (n))
+#define lzx_br_consume_unaligned_bits(br) ((br)->cache_avail &= ~0x0f)
+
+#define lzx_br_is_unaligned(br) ((br)->cache_avail & 0x0f)
+
+static const uint32_t cache_masks[] = {
+ 0x00000000, 0x00000001, 0x00000003, 0x00000007,
+ 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
+ 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
+ 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
+ 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
+ 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
+ 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
+ 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
+{
+/*
+ * x86 processor family can read misaligned data without an access error.
+ */
+ int n = CACHE_BITS - br->cache_avail;
+
+ for (;;) {
+ switch (n >> 4) {
+ case 4:
+ if (strm->avail_in >= 8) {
+ br->cache_buffer =
+ ((uint64_t)strm->next_in[1]) << 56 |
+ ((uint64_t)strm->next_in[0]) << 48 |
+ ((uint64_t)strm->next_in[3]) << 40 |
+ ((uint64_t)strm->next_in[2]) << 32 |
+ ((uint32_t)strm->next_in[5]) << 24 |
+ ((uint32_t)strm->next_in[4]) << 16 |
+ ((uint32_t)strm->next_in[7]) << 8 |
+ (uint32_t)strm->next_in[6];
+ strm->next_in += 8;
+ strm->avail_in -= 8;
+ br->cache_avail += 8 * 8;
+ return (1);
+ }
+ break;
+ case 3:
+ if (strm->avail_in >= 6) {
+ br->cache_buffer =
+ (br->cache_buffer << 48) |
+ ((uint64_t)strm->next_in[1]) << 40 |
+ ((uint64_t)strm->next_in[0]) << 32 |
+ ((uint64_t)strm->next_in[3]) << 24 |
+ ((uint64_t)strm->next_in[2]) << 16 |
+ ((uint64_t)strm->next_in[5]) << 8 |
+ (uint64_t)strm->next_in[4];
+ strm->next_in += 6;
+ strm->avail_in -= 6;
+ br->cache_avail += 6 * 8;
+ return (1);
+ }
+ break;
+ case 0:
+ /* We have enough compressed data in
+ * the cache buffer.*/
+ return (1);
+ default:
+ break;
+ }
+ if (strm->avail_in < 2) {
+ /* There is not enough compressed data to
+ * fill up the cache buffer. */
+ if (strm->avail_in == 1) {
+ br->odd = *strm->next_in++;
+ strm->avail_in--;
+ br->have_odd = 1;
+ }
+ return (0);
+ }
+ br->cache_buffer =
+ (br->cache_buffer << 16) |
+ archive_le16dec(strm->next_in);
+ strm->next_in += 2;
+ strm->avail_in -= 2;
+ br->cache_avail += 16;
+ n -= 16;
+ }
+}
+
+static void
+lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br)
+{
+ int n = CACHE_BITS - br->cache_avail;
+
+ if (br->have_odd && n >= 16 && strm->avail_in > 0) {
+ br->cache_buffer =
+ (br->cache_buffer << 16) |
+ ((uint16_t)(*strm->next_in)) << 8 | br->odd;
+ strm->next_in++;
+ strm->avail_in--;
+ br->cache_avail += 16;
+ br->have_odd = 0;
+ }
+}
+
+static void
+lzx_cleanup_bitstream(struct lzx_stream *strm)
+{
+ strm->ds->br.cache_avail = 0;
+ strm->ds->br.have_odd = 0;
+}
+
+/*
+ * Decode LZX.
+ *
+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
+ * Please set available buffer and call this function again.
+ * 2. Returns ARCHIVE_EOF if decompression has been completed.
+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
+ * is broken or you do not set 'last' flag properly.
+ */
+#define ST_RD_TRANSLATION 0
+#define ST_RD_TRANSLATION_SIZE 1
+#define ST_RD_BLOCK_TYPE 2
+#define ST_RD_BLOCK_SIZE 3
+#define ST_RD_ALIGNMENT 4
+#define ST_RD_R0 5
+#define ST_RD_R1 6
+#define ST_RD_R2 7
+#define ST_COPY_UNCOMP1 8
+#define ST_COPY_UNCOMP2 9
+#define ST_RD_ALIGNED_OFFSET 10
+#define ST_RD_VERBATIM 11
+#define ST_RD_PRE_MAIN_TREE_256 12
+#define ST_MAIN_TREE_256 13
+#define ST_RD_PRE_MAIN_TREE_REM 14
+#define ST_MAIN_TREE_REM 15
+#define ST_RD_PRE_LENGTH_TREE 16
+#define ST_LENGTH_TREE 17
+#define ST_MAIN 18
+#define ST_LENGTH 19
+#define ST_OFFSET 20
+#define ST_REAL_POS 21
+#define ST_COPY 22
+
+static int
+lzx_decode(struct lzx_stream *strm, int last)
+{
+ struct lzx_dec *ds = strm->ds;
+ int64_t avail_in;
+ int r;
+
+ if (ds->error)
+ return (ds->error);
+
+ avail_in = strm->avail_in;
+ lzx_br_fixup(strm, &(ds->br));
+ do {
+ if (ds->state < ST_MAIN)
+ r = lzx_read_blocks(strm, last);
+ else {
+ int64_t bytes_written = strm->avail_out;
+ r = lzx_decode_blocks(strm, last);
+ bytes_written -= strm->avail_out;
+ strm->next_out += bytes_written;
+ strm->total_out += bytes_written;
+ }
+ } while (r == 100);
+ strm->total_in += avail_in - strm->avail_in;
+ return (r);
+}
+
+static int
+lzx_read_blocks(struct lzx_stream *strm, int last)
+{
+ struct lzx_dec *ds = strm->ds;
+ struct lzx_br *br = &(ds->br);
+ int i, r;
+
+ for (;;) {
+ switch (ds->state) {
+ case ST_RD_TRANSLATION:
+ if (!lzx_br_read_ahead(strm, br, 1)) {
+ ds->state = ST_RD_TRANSLATION;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->translation = lzx_br_bits(br, 1);
+ lzx_br_consume(br, 1);
+ /* FALL THROUGH */
+ case ST_RD_TRANSLATION_SIZE:
+ if (ds->translation) {
+ if (!lzx_br_read_ahead(strm, br, 32)) {
+ ds->state = ST_RD_TRANSLATION_SIZE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->translation_size = lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ ds->translation_size <<= 16;
+ ds->translation_size |= lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ }
+ /* FALL THROUGH */
+ case ST_RD_BLOCK_TYPE:
+ if (!lzx_br_read_ahead(strm, br, 3)) {
+ ds->state = ST_RD_BLOCK_TYPE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->block_type = lzx_br_bits(br, 3);
+ lzx_br_consume(br, 3);
+ /* Check a block type. */
+ switch (ds->block_type) {
+ case VERBATIM_BLOCK:
+ case ALIGNED_OFFSET_BLOCK:
+ case UNCOMPRESSED_BLOCK:
+ break;
+ default:
+ goto failed;/* Invalid */
+ }
+ /* FALL THROUGH */
+ case ST_RD_BLOCK_SIZE:
+ if (!lzx_br_read_ahead(strm, br, 24)) {
+ ds->state = ST_RD_BLOCK_SIZE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->block_size = lzx_br_bits(br, 8);
+ lzx_br_consume(br, 8);
+ ds->block_size <<= 16;
+ ds->block_size |= lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ if (ds->block_size == 0)
+ goto failed;
+ ds->block_bytes_avail = ds->block_size;
+ if (ds->block_type != UNCOMPRESSED_BLOCK) {
+ if (ds->block_type == VERBATIM_BLOCK)
+ ds->state = ST_RD_VERBATIM;
+ else
+ ds->state = ST_RD_ALIGNED_OFFSET;
+ break;
+ }
+ /* FALL THROUGH */
+ case ST_RD_ALIGNMENT:
+ /*
+ * Handle an Uncompressed Block.
+ */
+ /* Skip padding to align following field on
+ * 16-bit boundary. */
+ if (lzx_br_is_unaligned(br))
+ lzx_br_consume_unaligned_bits(br);
+ else {
+ if (lzx_br_read_ahead(strm, br, 16))
+ lzx_br_consume(br, 16);
+ else {
+ ds->state = ST_RD_ALIGNMENT;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ }
+ /* Preparation to read repeated offsets R0,R1 and R2. */
+ ds->rbytes_avail = 0;
+ ds->state = ST_RD_R0;
+ /* FALL THROUGH */
+ case ST_RD_R0:
+ case ST_RD_R1:
+ case ST_RD_R2:
+ do {
+ uint16_t u16;
+ /* Drain bits in the cache buffer of
+ * bit-stream. */
+ if (lzx_br_has(br, 32)) {
+ u16 = lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ archive_le16enc(ds->rbytes, u16);
+ u16 = lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ archive_le16enc(ds->rbytes+2, u16);
+ ds->rbytes_avail = 4;
+ } else if (lzx_br_has(br, 16)) {
+ u16 = lzx_br_bits(br, 16);
+ lzx_br_consume(br, 16);
+ archive_le16enc(ds->rbytes, u16);
+ ds->rbytes_avail = 2;
+ }
+ if (ds->rbytes_avail < 4 && ds->br.have_odd) {
+ ds->rbytes[ds->rbytes_avail++] =
+ ds->br.odd;
+ ds->br.have_odd = 0;
+ }
+ while (ds->rbytes_avail < 4) {
+ if (strm->avail_in <= 0) {
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->rbytes[ds->rbytes_avail++] =
+ *strm->next_in++;
+ strm->avail_in--;
+ }
+ ds->rbytes_avail = 0;
+ if (ds->state == ST_RD_R0) {
+ ds->r0 = archive_le32dec(ds->rbytes);
+ if (ds->r0 < 0)
+ goto failed;
+ ds->state = ST_RD_R1;
+ } else if (ds->state == ST_RD_R1) {
+ ds->r1 = archive_le32dec(ds->rbytes);
+ if (ds->r1 < 0)
+ goto failed;
+ ds->state = ST_RD_R2;
+ } else if (ds->state == ST_RD_R2) {
+ ds->r2 = archive_le32dec(ds->rbytes);
+ if (ds->r2 < 0)
+ goto failed;
+ /* We've gotten all repeated offsets. */
+ ds->state = ST_COPY_UNCOMP1;
+ }
+ } while (ds->state != ST_COPY_UNCOMP1);
+ /* FALL THROUGH */
+ case ST_COPY_UNCOMP1:
+ /*
+ * Copy bytes form next_in to next_out directly.
+ */
+ while (ds->block_bytes_avail) {
+ int l;
+
+ if (strm->avail_out <= 0)
+ /* Output buffer is empty. */
+ return (ARCHIVE_OK);
+ if (strm->avail_in <= 0) {
+ /* Input buffer is empty. */
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ l = (int)ds->block_bytes_avail;
+ if (l > ds->w_size - ds->w_pos)
+ l = ds->w_size - ds->w_pos;
+ if (l > strm->avail_out)
+ l = (int)strm->avail_out;
+ if (l > strm->avail_in)
+ l = (int)strm->avail_in;
+ memcpy(strm->next_out, strm->next_in, l);
+ memcpy(&(ds->w_buff[ds->w_pos]),
+ strm->next_in, l);
+ strm->next_in += l;
+ strm->avail_in -= l;
+ strm->next_out += l;
+ strm->avail_out -= l;
+ strm->total_out += l;
+ ds->w_pos = (ds->w_pos + l) & ds->w_mask;
+ ds->block_bytes_avail -= l;
+ }
+ /* FALL THROUGH */
+ case ST_COPY_UNCOMP2:
+ /* Re-align; skip padding byte. */
+ if (ds->block_size & 1) {
+ if (strm->avail_in <= 0) {
+ /* Input buffer is empty. */
+ ds->state = ST_COPY_UNCOMP2;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ strm->next_in++;
+ strm->avail_in --;
+ }
+ /* This block ended. */
+ ds->state = ST_RD_BLOCK_TYPE;
+ return (ARCHIVE_EOF);
+ /********************/
+ case ST_RD_ALIGNED_OFFSET:
+ /*
+ * Read Aligned offset tree.
+ */
+ if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) {
+ ds->state = ST_RD_ALIGNED_OFFSET;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ memset(ds->at.freq, 0, sizeof(ds->at.freq));
+ for (i = 0; i < ds->at.len_size; i++) {
+ ds->at.bitlen[i] = lzx_br_bits(br, 3);
+ ds->at.freq[ds->at.bitlen[i]]++;
+ lzx_br_consume(br, 3);
+ }
+ if (!lzx_make_huffman_table(&ds->at))
+ goto failed;
+ /* FALL THROUGH */
+ case ST_RD_VERBATIM:
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_RD_PRE_MAIN_TREE_256:
+ /*
+ * Read Pre-tree for first 256 elements of main tree.
+ */
+ if (!lzx_read_pre_tree(strm)) {
+ ds->state = ST_RD_PRE_MAIN_TREE_256;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->pt))
+ goto failed;
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_MAIN_TREE_256:
+ /*
+ * Get path lengths of first 256 elements of main tree.
+ */
+ r = lzx_read_bitlen(strm, &ds->mt, 256);
+ if (r < 0)
+ goto failed;
+ else if (!r) {
+ ds->state = ST_MAIN_TREE_256;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_RD_PRE_MAIN_TREE_REM:
+ /*
+ * Read Pre-tree for remaining elements of main tree.
+ */
+ if (!lzx_read_pre_tree(strm)) {
+ ds->state = ST_RD_PRE_MAIN_TREE_REM;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->pt))
+ goto failed;
+ ds->loop = 256;
+ /* FALL THROUGH */
+ case ST_MAIN_TREE_REM:
+ /*
+ * Get path lengths of remaining elements of main tree.
+ */
+ r = lzx_read_bitlen(strm, &ds->mt, -1);
+ if (r < 0)
+ goto failed;
+ else if (!r) {
+ ds->state = ST_MAIN_TREE_REM;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->mt))
+ goto failed;
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_RD_PRE_LENGTH_TREE:
+ /*
+ * Read Pre-tree for remaining elements of main tree.
+ */
+ if (!lzx_read_pre_tree(strm)) {
+ ds->state = ST_RD_PRE_LENGTH_TREE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->pt))
+ goto failed;
+ ds->loop = 0;
+ /* FALL THROUGH */
+ case ST_LENGTH_TREE:
+ /*
+ * Get path lengths of remaining elements of main tree.
+ */
+ r = lzx_read_bitlen(strm, &ds->lt, -1);
+ if (r < 0)
+ goto failed;
+ else if (!r) {
+ ds->state = ST_LENGTH_TREE;
+ if (last)
+ goto failed;
+ return (ARCHIVE_OK);
+ }
+ if (!lzx_make_huffman_table(&ds->lt))
+ goto failed;
+ ds->state = ST_MAIN;
+ return (100);
+ }
+ }
+failed:
+ return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzx_decode_blocks(struct lzx_stream *strm, int last)
+{
+ struct lzx_dec *ds = strm->ds;
+ struct lzx_br bre = ds->br;
+ struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt);
+ const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl;
+ unsigned char *noutp = strm->next_out;
+ unsigned char *endp = noutp + strm->avail_out;
+ unsigned char *w_buff = ds->w_buff;
+ unsigned char *at_bitlen = at->bitlen;
+ unsigned char *lt_bitlen = lt->bitlen;
+ unsigned char *mt_bitlen = mt->bitlen;
+ size_t block_bytes_avail = ds->block_bytes_avail;
+ int at_max_bits = at->max_bits;
+ int lt_max_bits = lt->max_bits;
+ int mt_max_bits = mt->max_bits;
+ int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+ int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+ int length_header = ds->length_header;
+ int offset_bits = ds->offset_bits;
+ int position_slot = ds->position_slot;
+ int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
+ int state = ds->state;
+ char block_type = ds->block_type;
+
+ for (;;) {
+ switch (state) {
+ case ST_MAIN:
+ for (;;) {
+ if (block_bytes_avail == 0) {
+ /* This block ended. */
+ ds->state = ST_RD_BLOCK_TYPE;
+ ds->br = bre;
+ ds->block_bytes_avail =
+ block_bytes_avail;
+ ds->copy_len = copy_len;
+ ds->copy_pos = copy_pos;
+ ds->length_header = length_header;
+ ds->position_slot = position_slot;
+ ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
+ ds->w_pos = w_pos;
+ strm->avail_out = endp - noutp;
+ return (ARCHIVE_EOF);
+ }
+ if (noutp >= endp)
+ /* Output buffer is empty. */
+ goto next_data;
+
+ if (!lzx_br_read_ahead(strm, &bre,
+ mt_max_bits)) {
+ if (!last)
+ goto next_data;
+ /* Remaining bits are less than
+ * maximum bits(mt.max_bits) but maybe
+ * it still remains as much as we need,
+ * so we should try to use it with
+ * dummy bits. */
+ c = lzx_decode_huffman(mt,
+ lzx_br_bits_forced(
+ &bre, mt_max_bits));
+ lzx_br_consume(&bre, mt_bitlen[c]);
+ if (!lzx_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ c = lzx_decode_huffman(mt,
+ lzx_br_bits(&bre, mt_max_bits));
+ lzx_br_consume(&bre, mt_bitlen[c]);
+ }
+ if (c > UCHAR_MAX)
+ break;
+ /*
+ * 'c' is exactly literal code.
+ */
+ /* Save a decoded code to reference it
+ * afterward. */
+ w_buff[w_pos] = c;
+ w_pos = (w_pos + 1) & w_mask;
+ /* Store the decoded code to output buffer. */
+ *noutp++ = c;
+ block_bytes_avail--;
+ }
+ /*
+ * Get a match code, its length and offset.
+ */
+ c -= UCHAR_MAX + 1;
+ length_header = c & 7;
+ position_slot = c >> 3;
+ /* FALL THROUGH */
+ case ST_LENGTH:
+ /*
+ * Get a length.
+ */
+ if (length_header == 7) {
+ if (!lzx_br_read_ahead(strm, &bre,
+ lt_max_bits)) {
+ if (!last) {
+ state = ST_LENGTH;
+ goto next_data;
+ }
+ c = lzx_decode_huffman(lt,
+ lzx_br_bits_forced(
+ &bre, lt_max_bits));
+ lzx_br_consume(&bre, lt_bitlen[c]);
+ if (!lzx_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ c = lzx_decode_huffman(lt,
+ lzx_br_bits(&bre, lt_max_bits));
+ lzx_br_consume(&bre, lt_bitlen[c]);
+ }
+ copy_len = c + 7 + 2;
+ } else
+ copy_len = length_header + 2;
+ if ((size_t)copy_len > block_bytes_avail)
+ goto failed;
+ /*
+ * Get an offset.
+ */
+ switch (position_slot) {
+ case 0: /* Use repeated offset 0. */
+ copy_pos = r0;
+ state = ST_REAL_POS;
+ continue;
+ case 1: /* Use repeated offset 1. */
+ copy_pos = r1;
+ /* Swap repeated offset. */
+ r1 = r0;
+ r0 = copy_pos;
+ state = ST_REAL_POS;
+ continue;
+ case 2: /* Use repeated offset 2. */
+ copy_pos = r2;
+ /* Swap repeated offset. */
+ r2 = r0;
+ r0 = copy_pos;
+ state = ST_REAL_POS;
+ continue;
+ default:
+ offset_bits =
+ pos_tbl[position_slot].footer_bits;
+ break;
+ }
+ /* FALL THROUGH */
+ case ST_OFFSET:
+ /*
+ * Get the offset, which is a distance from
+ * current window position.
+ */
+ if (block_type == ALIGNED_OFFSET_BLOCK &&
+ offset_bits >= 3) {
+ int offbits = offset_bits - 3;
+
+ if (!lzx_br_read_ahead(strm, &bre, offbits)) {
+ state = ST_OFFSET;
+ if (last)
+ goto failed;
+ goto next_data;
+ }
+ copy_pos = lzx_br_bits(&bre, offbits) << 3;
+
+ /* Get an aligned number. */
+ if (!lzx_br_read_ahead(strm, &bre,
+ offbits + at_max_bits)) {
+ if (!last) {
+ state = ST_OFFSET;
+ goto next_data;
+ }
+ lzx_br_consume(&bre, offbits);
+ c = lzx_decode_huffman(at,
+ lzx_br_bits_forced(&bre,
+ at_max_bits));
+ lzx_br_consume(&bre, at_bitlen[c]);
+ if (!lzx_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ lzx_br_consume(&bre, offbits);
+ c = lzx_decode_huffman(at,
+ lzx_br_bits(&bre, at_max_bits));
+ lzx_br_consume(&bre, at_bitlen[c]);
+ }
+ /* Add an aligned number. */
+ copy_pos += c;
+ } else {
+ if (!lzx_br_read_ahead(strm, &bre,
+ offset_bits)) {
+ state = ST_OFFSET;
+ if (last)
+ goto failed;
+ goto next_data;
+ }
+ copy_pos = lzx_br_bits(&bre, offset_bits);
+ lzx_br_consume(&bre, offset_bits);
+ }
+ copy_pos += pos_tbl[position_slot].base -2;
+
+ /* Update repeated offset LRU queue. */
+ r2 = r1;
+ r1 = r0;
+ r0 = copy_pos;
+ /* FALL THROUGH */
+ case ST_REAL_POS:
+ /*
+ * Compute a real position in window.
+ */
+ copy_pos = (w_pos - copy_pos) & w_mask;
+ /* FALL THROUGH */
+ case ST_COPY:
+ /*
+ * Copy several bytes as extracted data from the window
+ * into the output buffer.
+ */
+ for (;;) {
+ const unsigned char *s;
+ int l;
+
+ l = copy_len;
+ if (copy_pos > w_pos) {
+ if (l > w_size - copy_pos)
+ l = w_size - copy_pos;
+ } else {
+ if (l > w_size - w_pos)
+ l = w_size - w_pos;
+ }
+ if (noutp + l >= endp)
+ l = (int)(endp - noutp);
+ s = w_buff + copy_pos;
+ if (l >= 8 && ((copy_pos + l < w_pos)
+ || (w_pos + l < copy_pos))) {
+ memcpy(w_buff + w_pos, s, l);
+ memcpy(noutp, s, l);
+ } else {
+ unsigned char *d;
+ int li;
+
+ d = w_buff + w_pos;
+ for (li = 0; li < l; li++)
+ noutp[li] = d[li] = s[li];
+ }
+ noutp += l;
+ copy_pos = (copy_pos + l) & w_mask;
+ w_pos = (w_pos + l) & w_mask;
+ block_bytes_avail -= l;
+ if (copy_len <= l)
+ /* A copy of current pattern ended. */
+ break;
+ copy_len -= l;
+ if (noutp >= endp) {
+ /* Output buffer is empty. */
+ state = ST_COPY;
+ goto next_data;
+ }
+ }
+ state = ST_MAIN;
+ break;
+ }
+ }
+failed:
+ return (ds->error = ARCHIVE_FAILED);
+next_data:
+ ds->br = bre;
+ ds->block_bytes_avail = block_bytes_avail;
+ ds->copy_len = copy_len;
+ ds->copy_pos = copy_pos;
+ ds->length_header = length_header;
+ ds->offset_bits = offset_bits;
+ ds->position_slot = position_slot;
+ ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
+ ds->state = state;
+ ds->w_pos = w_pos;
+ strm->avail_out = endp - noutp;
+ return (ARCHIVE_OK);
+}
+
+static int
+lzx_read_pre_tree(struct lzx_stream *strm)
+{
+ struct lzx_dec *ds = strm->ds;
+ struct lzx_br *br = &(ds->br);
+ int i;
+
+ if (ds->loop == 0)
+ memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
+ for (i = ds->loop; i < ds->pt.len_size; i++) {
+ if (!lzx_br_read_ahead(strm, br, 4)) {
+ ds->loop = i;
+ return (0);
+ }
+ ds->pt.bitlen[i] = lzx_br_bits(br, 4);
+ ds->pt.freq[ds->pt.bitlen[i]]++;
+ lzx_br_consume(br, 4);
+ }
+ ds->loop = i;
+ return (1);
+}
+
+/*
+ * Read a bunch of bit-lengths from pre-tree.
+ */
+static int
+lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end)
+{
+ struct lzx_dec *ds = strm->ds;
+ struct lzx_br *br = &(ds->br);
+ int c, i, j, ret, same;
+ unsigned rbits;
+
+ i = ds->loop;
+ if (i == 0)
+ memset(d->freq, 0, sizeof(d->freq));
+ ret = 0;
+ if (end < 0)
+ end = d->len_size;
+ while (i < end) {
+ ds->loop = i;
+ if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits))
+ goto getdata;
+ rbits = lzx_br_bits(br, ds->pt.max_bits);
+ c = lzx_decode_huffman(&(ds->pt), rbits);
+ switch (c) {
+ case 17:/* several zero lengths, from 4 to 19. */
+ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4))
+ goto getdata;
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ same = lzx_br_bits(br, 4) + 4;
+ if (i + same > end)
+ return (-1);/* Invalid */
+ lzx_br_consume(br, 4);
+ for (j = 0; j < same; j++)
+ d->bitlen[i++] = 0;
+ break;
+ case 18:/* many zero lengths, from 20 to 51. */
+ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5))
+ goto getdata;
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ same = lzx_br_bits(br, 5) + 20;
+ if (i + same > end)
+ return (-1);/* Invalid */
+ lzx_br_consume(br, 5);
+ memset(d->bitlen + i, 0, same);
+ i += same;
+ break;
+ case 19:/* a few same lengths. */
+ if (!lzx_br_read_ahead(strm, br,
+ ds->pt.bitlen[c]+1+ds->pt.max_bits))
+ goto getdata;
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ same = lzx_br_bits(br, 1) + 4;
+ if (i + same > end)
+ return (-1);
+ lzx_br_consume(br, 1);
+ rbits = lzx_br_bits(br, ds->pt.max_bits);
+ c = lzx_decode_huffman(&(ds->pt), rbits);
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ c = (d->bitlen[i] - c + 17) % 17;
+ if (c < 0)
+ return (-1);/* Invalid */
+ for (j = 0; j < same; j++)
+ d->bitlen[i++] = c;
+ d->freq[c] += same;
+ break;
+ default:
+ lzx_br_consume(br, ds->pt.bitlen[c]);
+ c = (d->bitlen[i] - c + 17) % 17;
+ if (c < 0)
+ return (-1);/* Invalid */
+ d->freq[c]++;
+ d->bitlen[i++] = c;
+ break;
+ }
+ }
+ ret = 1;
+getdata:
+ ds->loop = i;
+ return (ret);
+}
+
+static int
+lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+{
+
+ if (hf->bitlen == NULL || hf->len_size != (int)len_size) {
+ free(hf->bitlen);
+ hf->bitlen = calloc(len_size, sizeof(hf->bitlen[0]));
+ if (hf->bitlen == NULL)
+ return (ARCHIVE_FATAL);
+ hf->len_size = (int)len_size;
+ } else
+ memset(hf->bitlen, 0, len_size * sizeof(hf->bitlen[0]));
+ if (hf->tbl == NULL) {
+ hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0]));
+ if (hf->tbl == NULL)
+ return (ARCHIVE_FATAL);
+ hf->tbl_bits = tbl_bits;
+ }
+ return (ARCHIVE_OK);
+}
+
+static void
+lzx_huffman_free(struct huffman *hf)
+{
+ free(hf->bitlen);
+ free(hf->tbl);
+}
+
+/*
+ * Make a huffman coding table.
+ */
+static int
+lzx_make_huffman_table(struct huffman *hf)
+{
+ uint16_t *tbl;
+ const unsigned char *bitlen;
+ int bitptn[17], weight[17];
+ int i, maxbits = 0, ptn, tbl_size, w;
+ int len_avail;
+
+ /*
+ * Initialize bit patterns.
+ */
+ ptn = 0;
+ for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
+ bitptn[i] = ptn;
+ weight[i] = w;
+ if (hf->freq[i]) {
+ ptn += hf->freq[i] * w;
+ maxbits = i;
+ }
+ }
+ if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits)
+ return (0);/* Invalid */
+
+ hf->max_bits = maxbits;
+
+ /*
+ * Cut out extra bits which we won't house in the table.
+ * This preparation reduces the same calculation in the for-loop
+ * making the table.
+ */
+ if (maxbits < 16) {
+ int ebits = 16 - maxbits;
+ for (i = 1; i <= maxbits; i++) {
+ bitptn[i] >>= ebits;
+ weight[i] >>= ebits;
+ }
+ }
+
+ /*
+ * Make the table.
+ */
+ tbl_size = 1 << hf->tbl_bits;
+ tbl = hf->tbl;
+ bitlen = hf->bitlen;
+ len_avail = hf->len_size;
+ hf->tree_used = 0;
+ for (i = 0; i < len_avail; i++) {
+ uint16_t *p;
+ int len, cnt;
+
+ if (bitlen[i] == 0)
+ continue;
+ /* Get a bit pattern */
+ len = bitlen[i];
+ if (len > tbl_size)
+ return (0);
+ ptn = bitptn[len];
+ cnt = weight[len];
+ /* Calculate next bit pattern */
+ if ((bitptn[len] = ptn + cnt) > tbl_size)
+ return (0);/* Invalid */
+ /* Update the table */
+ p = &(tbl[ptn]);
+ while (--cnt >= 0)
+ p[cnt] = (uint16_t)i;
+ }
+ return (1);
+}
+
+static inline int
+lzx_decode_huffman(struct huffman *hf, unsigned rbits)
+{
+ int c;
+ c = hf->tbl[rbits];
+ if (c < hf->len_size)
+ return (c);
+ return (0);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_cpio.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_cpio.c
new file mode 100644
index 0000000000..9adcfd335b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_cpio.c
@@ -0,0 +1,1104 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_cpio.c 201163 2009-12-29 05:50:34Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+/* #include <stdint.h> */ /* See archive_platform.h */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define bin_magic_offset 0
+#define bin_magic_size 2
+#define bin_dev_offset 2
+#define bin_dev_size 2
+#define bin_ino_offset 4
+#define bin_ino_size 2
+#define bin_mode_offset 6
+#define bin_mode_size 2
+#define bin_uid_offset 8
+#define bin_uid_size 2
+#define bin_gid_offset 10
+#define bin_gid_size 2
+#define bin_nlink_offset 12
+#define bin_nlink_size 2
+#define bin_rdev_offset 14
+#define bin_rdev_size 2
+#define bin_mtime_offset 16
+#define bin_mtime_size 4
+#define bin_namesize_offset 20
+#define bin_namesize_size 2
+#define bin_filesize_offset 22
+#define bin_filesize_size 4
+#define bin_header_size 26
+
+#define odc_magic_offset 0
+#define odc_magic_size 6
+#define odc_dev_offset 6
+#define odc_dev_size 6
+#define odc_ino_offset 12
+#define odc_ino_size 6
+#define odc_mode_offset 18
+#define odc_mode_size 6
+#define odc_uid_offset 24
+#define odc_uid_size 6
+#define odc_gid_offset 30
+#define odc_gid_size 6
+#define odc_nlink_offset 36
+#define odc_nlink_size 6
+#define odc_rdev_offset 42
+#define odc_rdev_size 6
+#define odc_mtime_offset 48
+#define odc_mtime_size 11
+#define odc_namesize_offset 59
+#define odc_namesize_size 6
+#define odc_filesize_offset 65
+#define odc_filesize_size 11
+#define odc_header_size 76
+
+#define newc_magic_offset 0
+#define newc_magic_size 6
+#define newc_ino_offset 6
+#define newc_ino_size 8
+#define newc_mode_offset 14
+#define newc_mode_size 8
+#define newc_uid_offset 22
+#define newc_uid_size 8
+#define newc_gid_offset 30
+#define newc_gid_size 8
+#define newc_nlink_offset 38
+#define newc_nlink_size 8
+#define newc_mtime_offset 46
+#define newc_mtime_size 8
+#define newc_filesize_offset 54
+#define newc_filesize_size 8
+#define newc_devmajor_offset 62
+#define newc_devmajor_size 8
+#define newc_devminor_offset 70
+#define newc_devminor_size 8
+#define newc_rdevmajor_offset 78
+#define newc_rdevmajor_size 8
+#define newc_rdevminor_offset 86
+#define newc_rdevminor_size 8
+#define newc_namesize_offset 94
+#define newc_namesize_size 8
+#define newc_checksum_offset 102
+#define newc_checksum_size 8
+#define newc_header_size 110
+
+/*
+ * An afio large ASCII header, which they named itself.
+ * afio utility uses this header, if a file size is larger than 2G bytes
+ * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than
+ * 0x7fffffff, which we cannot record to odc header because of its limit.
+ * If not, uses odc header.
+ */
+#define afiol_magic_offset 0
+#define afiol_magic_size 6
+#define afiol_dev_offset 6
+#define afiol_dev_size 8 /* hex */
+#define afiol_ino_offset 14
+#define afiol_ino_size 16 /* hex */
+#define afiol_ino_m_offset 30 /* 'm' */
+#define afiol_mode_offset 31
+#define afiol_mode_size 6 /* oct */
+#define afiol_uid_offset 37
+#define afiol_uid_size 8 /* hex */
+#define afiol_gid_offset 45
+#define afiol_gid_size 8 /* hex */
+#define afiol_nlink_offset 53
+#define afiol_nlink_size 8 /* hex */
+#define afiol_rdev_offset 61
+#define afiol_rdev_size 8 /* hex */
+#define afiol_mtime_offset 69
+#define afiol_mtime_size 16 /* hex */
+#define afiol_mtime_n_offset 85 /* 'n' */
+#define afiol_namesize_offset 86
+#define afiol_namesize_size 4 /* hex */
+#define afiol_flag_offset 90
+#define afiol_flag_size 4 /* hex */
+#define afiol_xsize_offset 94
+#define afiol_xsize_size 4 /* hex */
+#define afiol_xsize_s_offset 98 /* 's' */
+#define afiol_filesize_offset 99
+#define afiol_filesize_size 16 /* hex */
+#define afiol_filesize_c_offset 115 /* ':' */
+#define afiol_header_size 116
+
+
+struct links_entry {
+ struct links_entry *next;
+ struct links_entry *previous;
+ unsigned int links;
+ dev_t dev;
+ int64_t ino;
+ char *name;
+};
+
+#define CPIO_MAGIC 0x13141516
+struct cpio {
+ int magic;
+ int (*read_header)(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+ struct links_entry *links_head;
+ int64_t entry_bytes_remaining;
+ int64_t entry_bytes_unconsumed;
+ int64_t entry_offset;
+ int64_t entry_padding;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+
+ int option_pwb;
+};
+
+static int64_t atol16(const char *, unsigned);
+static int64_t atol8(const char *, unsigned);
+static int archive_read_format_cpio_bid(struct archive_read *, int);
+static int archive_read_format_cpio_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_cpio_cleanup(struct archive_read *);
+static int archive_read_format_cpio_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_cpio_read_header(struct archive_read *,
+ struct archive_entry *);
+static int archive_read_format_cpio_skip(struct archive_read *);
+static int64_t be4(const unsigned char *);
+static int find_odc_header(struct archive_read *);
+static int find_newc_header(struct archive_read *);
+static int header_bin_be(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int header_bin_le(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int header_newc(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int header_odc(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int header_afiol(struct archive_read *, struct cpio *,
+ struct archive_entry *, size_t *, size_t *);
+static int is_octal(const char *, size_t);
+static int is_hex(const char *, size_t);
+static int64_t le4(const unsigned char *);
+static int record_hardlink(struct archive_read *a,
+ struct cpio *cpio, struct archive_entry *entry);
+
+int
+archive_read_support_format_cpio(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct cpio *cpio;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_cpio");
+
+ cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+ if (cpio == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+ return (ARCHIVE_FATAL);
+ }
+ cpio->magic = CPIO_MAGIC;
+
+ r = __archive_read_register_format(a,
+ cpio,
+ "cpio",
+ archive_read_format_cpio_bid,
+ archive_read_format_cpio_options,
+ archive_read_format_cpio_read_header,
+ archive_read_format_cpio_read_data,
+ archive_read_format_cpio_skip,
+ NULL,
+ archive_read_format_cpio_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK)
+ free(cpio);
+ return (ARCHIVE_OK);
+}
+
+
+static int
+archive_read_format_cpio_bid(struct archive_read *a, int best_bid)
+{
+ const unsigned char *p;
+ struct cpio *cpio;
+ int bid;
+
+ (void)best_bid; /* UNUSED */
+
+ cpio = (struct cpio *)(a->format->data);
+
+ if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
+ return (-1);
+
+ bid = 0;
+ if (memcmp(p, "070707", 6) == 0) {
+ /* ASCII cpio archive (odc, POSIX.1) */
+ cpio->read_header = header_odc;
+ bid += 48;
+ /*
+ * XXX TODO: More verification; Could check that only octal
+ * digits appear in appropriate header locations. XXX
+ */
+ } else if (memcmp(p, "070727", 6) == 0) {
+ /* afio large ASCII cpio archive */
+ cpio->read_header = header_odc;
+ bid += 48;
+ /*
+ * XXX TODO: More verification; Could check that almost hex
+ * digits appear in appropriate header locations. XXX
+ */
+ } else if (memcmp(p, "070701", 6) == 0) {
+ /* ASCII cpio archive (SVR4 without CRC) */
+ cpio->read_header = header_newc;
+ bid += 48;
+ /*
+ * XXX TODO: More verification; Could check that only hex
+ * digits appear in appropriate header locations. XXX
+ */
+ } else if (memcmp(p, "070702", 6) == 0) {
+ /* ASCII cpio archive (SVR4 with CRC) */
+ /* XXX TODO: Flag that we should check the CRC. XXX */
+ cpio->read_header = header_newc;
+ bid += 48;
+ /*
+ * XXX TODO: More verification; Could check that only hex
+ * digits appear in appropriate header locations. XXX
+ */
+ } else if (p[0] * 256 + p[1] == 070707) {
+ /* big-endian binary cpio archives */
+ cpio->read_header = header_bin_be;
+ bid += 16;
+ /* Is more verification possible here? */
+ } else if (p[0] + p[1] * 256 == 070707) {
+ /* little-endian binary cpio archives */
+ cpio->read_header = header_bin_le;
+ bid += 16;
+ /* Is more verification possible here? */
+ } else
+ return (ARCHIVE_WARN);
+
+ return (bid);
+}
+
+static int
+archive_read_format_cpio_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct cpio *cpio;
+ int ret = ARCHIVE_FAILED;
+
+ cpio = (struct cpio *)(a->format->data);
+ if (strcmp(key, "compat-2x") == 0) {
+ /* Handle filenames as libarchive 2.x */
+ cpio->init_default_conversion = (val != NULL)?1:0;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "cpio: hdrcharset option needs a character-set name");
+ else {
+ cpio->opt_sconv =
+ archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (cpio->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ } else if (strcmp(key, "pwb") == 0) {
+ if (val != NULL && val[0] != 0)
+ cpio->option_pwb = 1;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_read_format_cpio_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct cpio *cpio;
+ const void *h, *hl;
+ struct archive_string_conv *sconv;
+ size_t namelength;
+ size_t name_pad;
+ int r;
+
+ cpio = (struct cpio *)(a->format->data);
+ sconv = cpio->opt_sconv;
+ if (sconv == NULL) {
+ if (!cpio->init_default_conversion) {
+ cpio->sconv_default =
+ archive_string_default_conversion_for_read(
+ &(a->archive));
+ cpio->init_default_conversion = 1;
+ }
+ sconv = cpio->sconv_default;
+ }
+
+ r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
+
+ if (r < ARCHIVE_WARN)
+ return (r);
+
+ /* Read name from buffer. */
+ h = __archive_read_ahead(a, namelength + name_pad, NULL);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+ if (archive_entry_copy_pathname_l(entry,
+ (const char *)h, namelength, sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname can't be converted from %s to current locale.",
+ archive_string_conversion_charset_name(sconv));
+ r = ARCHIVE_WARN;
+ }
+ cpio->entry_offset = 0;
+
+ __archive_read_consume(a, namelength + name_pad);
+
+ /* If this is a symlink, read the link contents. */
+ if (archive_entry_filetype(entry) == AE_IFLNK) {
+ if (cpio->entry_bytes_remaining > 1024 * 1024) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte");
+ return (ARCHIVE_FATAL);
+ }
+ hl = __archive_read_ahead(a,
+ (size_t)cpio->entry_bytes_remaining, NULL);
+ if (hl == NULL)
+ return (ARCHIVE_FATAL);
+ if (archive_entry_copy_symlink_l(entry, (const char *)hl,
+ (size_t)cpio->entry_bytes_remaining, sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Linkname can't be converted from %s to "
+ "current locale.",
+ archive_string_conversion_charset_name(sconv));
+ r = ARCHIVE_WARN;
+ }
+ __archive_read_consume(a, cpio->entry_bytes_remaining);
+ cpio->entry_bytes_remaining = 0;
+ }
+
+ /* XXX TODO: If the full mode is 0160200, then this is a Solaris
+ * ACL description for the following entry. Read this body
+ * and parse it as a Solaris-style ACL, then read the next
+ * header. XXX */
+
+ /* Compare name to "TRAILER!!!" to test for end-of-archive. */
+ if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",
+ 10) == 0) {
+ /* TODO: Store file location of start of block. */
+ archive_clear_error(&a->archive);
+ return (ARCHIVE_EOF);
+ }
+
+ /* Detect and record hardlinks to previously-extracted entries. */
+ if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) {
+ return (ARCHIVE_FATAL);
+ }
+
+ return (r);
+}
+
+static int
+archive_read_format_cpio_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ ssize_t bytes_read;
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)(a->format->data);
+
+ if (cpio->entry_bytes_unconsumed) {
+ __archive_read_consume(a, cpio->entry_bytes_unconsumed);
+ cpio->entry_bytes_unconsumed = 0;
+ }
+
+ if (cpio->entry_bytes_remaining > 0) {
+ *buff = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read <= 0)
+ return (ARCHIVE_FATAL);
+ if (bytes_read > cpio->entry_bytes_remaining)
+ bytes_read = (ssize_t)cpio->entry_bytes_remaining;
+ *size = bytes_read;
+ cpio->entry_bytes_unconsumed = bytes_read;
+ *offset = cpio->entry_offset;
+ cpio->entry_offset += bytes_read;
+ cpio->entry_bytes_remaining -= bytes_read;
+ return (ARCHIVE_OK);
+ } else {
+ if (cpio->entry_padding !=
+ __archive_read_consume(a, cpio->entry_padding)) {
+ return (ARCHIVE_FATAL);
+ }
+ cpio->entry_padding = 0;
+ *buff = NULL;
+ *size = 0;
+ *offset = cpio->entry_offset;
+ return (ARCHIVE_EOF);
+ }
+}
+
+static int
+archive_read_format_cpio_skip(struct archive_read *a)
+{
+ struct cpio *cpio = (struct cpio *)(a->format->data);
+ int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding +
+ cpio->entry_bytes_unconsumed;
+
+ if (to_skip != __archive_read_consume(a, to_skip)) {
+ return (ARCHIVE_FATAL);
+ }
+ cpio->entry_bytes_remaining = 0;
+ cpio->entry_padding = 0;
+ cpio->entry_bytes_unconsumed = 0;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Skip forward to the next cpio newc header by searching for the
+ * 07070[12] string. This should be generalized and merged with
+ * find_odc_header below.
+ */
+static int
+is_hex(const char *p, size_t len)
+{
+ while (len-- > 0) {
+ if ((*p >= '0' && *p <= '9')
+ || (*p >= 'a' && *p <= 'f')
+ || (*p >= 'A' && *p <= 'F'))
+ ++p;
+ else
+ return (0);
+ }
+ return (1);
+}
+
+static int
+find_newc_header(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, skipped = 0;
+ ssize_t bytes;
+
+ for (;;) {
+ h = __archive_read_ahead(a, newc_header_size, &bytes);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ q = p + bytes;
+
+ /* Try the typical case first, then go into the slow search.*/
+ if (memcmp("07070", p, 5) == 0
+ && (p[5] == '1' || p[5] == '2')
+ && is_hex(p, newc_header_size))
+ return (ARCHIVE_OK);
+
+ /*
+ * Scan ahead until we find something that looks
+ * like a newc header.
+ */
+ while (p + newc_header_size <= q) {
+ switch (p[5]) {
+ case '1':
+ case '2':
+ if (memcmp("07070", p, 5) == 0
+ && is_hex(p, newc_header_size)) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ skipped += skip;
+ if (skipped > 0) {
+ archive_set_error(&a->archive,
+ 0,
+ "Skipped %d bytes before "
+ "finding valid header",
+ (int)skipped);
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+ }
+ p += 2;
+ break;
+ case '0':
+ p++;
+ break;
+ default:
+ p += 6;
+ break;
+ }
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ skipped += skip;
+ }
+}
+
+static int
+header_newc(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ const char *header;
+ int r;
+
+ r = find_newc_header(a);
+ if (r < ARCHIVE_WARN)
+ return (r);
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, newc_header_size, NULL);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* Parse out hex fields. */
+ header = (const char *)h;
+
+ if (memcmp(header + newc_magic_offset, "070701", 6) == 0) {
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+ a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)";
+ } else if (memcmp(header + newc_magic_offset, "070702", 6) == 0) {
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC;
+ a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)";
+ } else {
+ /* TODO: Abort here? */
+ }
+
+ archive_entry_set_devmajor(entry,
+ (dev_t)atol16(header + newc_devmajor_offset, newc_devmajor_size));
+ archive_entry_set_devminor(entry,
+ (dev_t)atol16(header + newc_devminor_offset, newc_devminor_size));
+ archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size));
+ archive_entry_set_mode(entry,
+ (mode_t)atol16(header + newc_mode_offset, newc_mode_size));
+ archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size));
+ archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size));
+ archive_entry_set_nlink(entry,
+ (unsigned int)atol16(header + newc_nlink_offset, newc_nlink_size));
+ archive_entry_set_rdevmajor(entry,
+ (dev_t)atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size));
+ archive_entry_set_rdevminor(entry,
+ (dev_t)atol16(header + newc_rdevminor_offset, newc_rdevminor_size));
+ archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0);
+ *namelength = (size_t)atol16(header + newc_namesize_offset, newc_namesize_size);
+ /* Pad name to 2 more than a multiple of 4. */
+ *name_pad = (2 - *namelength) & 3;
+
+ /* Make sure that the padded name length fits into size_t. */
+ if (*name_pad > SIZE_MAX - *namelength) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "cpio archive has invalid namelength");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Note: entry_bytes_remaining is at least 64 bits and
+ * therefore guaranteed to be big enough for a 33-bit file
+ * size.
+ */
+ cpio->entry_bytes_remaining =
+ atol16(header + newc_filesize_offset, newc_filesize_size);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ /* Pad file contents to a multiple of 4. */
+ cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
+ __archive_read_consume(a, newc_header_size);
+ return (r);
+}
+
+/*
+ * Skip forward to the next cpio odc header by searching for the
+ * 070707 string. This is a hand-optimized search that could
+ * probably be easily generalized to handle all character-based
+ * cpio variants.
+ */
+static int
+is_octal(const char *p, size_t len)
+{
+ while (len-- > 0) {
+ if (*p < '0' || *p > '7')
+ return (0);
+ ++p;
+ }
+ return (1);
+}
+
+static int
+is_afio_large(const char *h, size_t len)
+{
+ if (len < afiol_header_size)
+ return (0);
+ if (h[afiol_ino_m_offset] != 'm'
+ || h[afiol_mtime_n_offset] != 'n'
+ || h[afiol_xsize_s_offset] != 's'
+ || h[afiol_filesize_c_offset] != ':')
+ return (0);
+ if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset))
+ return (0);
+ if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset))
+ return (0);
+ if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset))
+ return (0);
+ if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size))
+ return (0);
+ return (1);
+}
+
+static int
+find_odc_header(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, skipped = 0;
+ ssize_t bytes;
+
+ for (;;) {
+ h = __archive_read_ahead(a, odc_header_size, &bytes);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ q = p + bytes;
+
+ /* Try the typical case first, then go into the slow search.*/
+ if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size))
+ return (ARCHIVE_OK);
+ if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) {
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Scan ahead until we find something that looks
+ * like an odc header.
+ */
+ while (p + odc_header_size <= q) {
+ switch (p[5]) {
+ case '7':
+ if ((memcmp("070707", p, 6) == 0
+ && is_octal(p, odc_header_size))
+ || (memcmp("070727", p, 6) == 0
+ && is_afio_large(p, q - p))) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ skipped += skip;
+ if (p[4] == '2')
+ a->archive.archive_format =
+ ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+ if (skipped > 0) {
+ archive_set_error(&a->archive,
+ 0,
+ "Skipped %d bytes before "
+ "finding valid header",
+ (int)skipped);
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+ }
+ p += 2;
+ break;
+ case '0':
+ p++;
+ break;
+ default:
+ p += 6;
+ break;
+ }
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ skipped += skip;
+ }
+}
+
+static int
+header_odc(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ int r;
+ const char *header;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+ a->archive.archive_format_name = "POSIX octet-oriented cpio";
+
+ /* Find the start of the next header. */
+ r = find_odc_header(a);
+ if (r < ARCHIVE_WARN)
+ return (r);
+
+ if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) {
+ int r2 = (header_afiol(a, cpio, entry, namelength, name_pad));
+ if (r2 == ARCHIVE_OK)
+ return (r);
+ else
+ return (r2);
+ }
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, odc_header_size, NULL);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* Parse out octal fields. */
+ header = (const char *)h;
+
+ archive_entry_set_dev(entry,
+ (dev_t)atol8(header + odc_dev_offset, odc_dev_size));
+ archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size));
+ archive_entry_set_mode(entry,
+ (mode_t)atol8(header + odc_mode_offset, odc_mode_size));
+ archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size));
+ archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size));
+ archive_entry_set_nlink(entry,
+ (unsigned int)atol8(header + odc_nlink_offset, odc_nlink_size));
+ archive_entry_set_rdev(entry,
+ (dev_t)atol8(header + odc_rdev_offset, odc_rdev_size));
+ archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0);
+ *namelength = (size_t)atol8(header + odc_namesize_offset, odc_namesize_size);
+ *name_pad = 0; /* No padding of filename. */
+
+ /*
+ * Note: entry_bytes_remaining is at least 64 bits and
+ * therefore guaranteed to be big enough for a 33-bit file
+ * size.
+ */
+ cpio->entry_bytes_remaining =
+ atol8(header + odc_filesize_offset, odc_filesize_size);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ cpio->entry_padding = 0;
+ __archive_read_consume(a, odc_header_size);
+ return (r);
+}
+
+/*
+ * NOTE: if a filename suffix is ".z", it is the file gziped by afio.
+ * it would be nice that we can show uncompressed file size and we can
+ * uncompressed file contents automatically, unfortunately we have nothing
+ * to get a uncompressed file size while reading each header. It means
+ * we also cannot uncompress file contents under our framework.
+ */
+static int
+header_afiol(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ const char *header;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+ a->archive.archive_format_name = "afio large ASCII";
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, afiol_header_size, NULL);
+ if (h == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* Parse out octal fields. */
+ header = (const char *)h;
+
+ archive_entry_set_dev(entry,
+ (dev_t)atol16(header + afiol_dev_offset, afiol_dev_size));
+ archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size));
+ archive_entry_set_mode(entry,
+ (mode_t)atol8(header + afiol_mode_offset, afiol_mode_size));
+ archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size));
+ archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size));
+ archive_entry_set_nlink(entry,
+ (unsigned int)atol16(header + afiol_nlink_offset, afiol_nlink_size));
+ archive_entry_set_rdev(entry,
+ (dev_t)atol16(header + afiol_rdev_offset, afiol_rdev_size));
+ archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0);
+ *namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size);
+ *name_pad = 0; /* No padding of filename. */
+
+ cpio->entry_bytes_remaining =
+ atol16(header + afiol_filesize_offset, afiol_filesize_size);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ cpio->entry_padding = 0;
+ __archive_read_consume(a, afiol_header_size);
+ return (ARCHIVE_OK);
+}
+
+
+static int
+header_bin_le(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ const unsigned char *header;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
+ a->archive.archive_format_name = "cpio (little-endian binary)";
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, bin_header_size, NULL);
+ if (h == NULL) {
+ archive_set_error(&a->archive, 0,
+ "End of file trying to read next cpio header");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Parse out binary fields. */
+ header = (const unsigned char *)h;
+
+ archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256);
+ archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256);
+ archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256);
+ if (cpio->option_pwb) {
+ /* turn off random bits left over from V6 inode */
+ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777);
+ if ((archive_entry_mode(entry) & AE_IFMT) == 0)
+ archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG);
+ }
+ archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256);
+ archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256);
+ archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256);
+ archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256);
+ archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0);
+ *namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256;
+ *name_pad = *namelength & 1; /* Pad to even. */
+
+ cpio->entry_bytes_remaining = le4(header + bin_filesize_offset);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+ __archive_read_consume(a, bin_header_size);
+ return (ARCHIVE_OK);
+}
+
+static int
+header_bin_be(struct archive_read *a, struct cpio *cpio,
+ struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+ const void *h;
+ const unsigned char *header;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
+ a->archive.archive_format_name = "cpio (big-endian binary)";
+
+ /* Read fixed-size portion of header. */
+ h = __archive_read_ahead(a, bin_header_size, NULL);
+ if (h == NULL) {
+ archive_set_error(&a->archive, 0,
+ "End of file trying to read next cpio header");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Parse out binary fields. */
+ header = (const unsigned char *)h;
+
+ archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]);
+ archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]);
+ archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]);
+ if (cpio->option_pwb) {
+ /* turn off random bits left over from V6 inode */
+ archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777);
+ if ((archive_entry_mode(entry) & AE_IFMT) == 0)
+ archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG);
+ }
+ archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]);
+ archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]);
+ archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]);
+ archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]);
+ archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0);
+ *namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1];
+ *name_pad = *namelength & 1; /* Pad to even. */
+
+ cpio->entry_bytes_remaining = be4(header + bin_filesize_offset);
+ archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+ cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+ __archive_read_consume(a, bin_header_size);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cpio_cleanup(struct archive_read *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)(a->format->data);
+ /* Free inode->name map */
+ while (cpio->links_head != NULL) {
+ struct links_entry *lp = cpio->links_head->next;
+
+ free(cpio->links_head->name);
+ free(cpio->links_head);
+ cpio->links_head = lp;
+ }
+ free(cpio);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+le4(const unsigned char *p)
+{
+ return ((p[0] << 16) | (((int64_t)p[1]) << 24) | (p[2] << 0) | (p[3] << 8));
+}
+
+
+static int64_t
+be4(const unsigned char *p)
+{
+ return ((((int64_t)p[0]) << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]));
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+atol8(const char *p, unsigned char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= '0' && *p <= '7')
+ digit = *p - '0';
+ else
+ return (l);
+ p++;
+ l <<= 3;
+ l |= digit;
+ }
+ return (l);
+}
+
+static int64_t
+atol16(const char *p, unsigned char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= 'a' && *p <= 'f')
+ digit = *p - 'a' + 10;
+ else if (*p >= 'A' && *p <= 'F')
+ digit = *p - 'A' + 10;
+ else if (*p >= '0' && *p <= '9')
+ digit = *p - '0';
+ else
+ return (l);
+ p++;
+ l <<= 4;
+ l |= digit;
+ }
+ return (l);
+}
+
+static int
+record_hardlink(struct archive_read *a,
+ struct cpio *cpio, struct archive_entry *entry)
+{
+ struct links_entry *le;
+ dev_t dev;
+ int64_t ino;
+
+ if (archive_entry_nlink(entry) <= 1)
+ return (ARCHIVE_OK);
+
+ dev = archive_entry_dev(entry);
+ ino = archive_entry_ino64(entry);
+
+ /*
+ * First look in the list of multiply-linked files. If we've
+ * already dumped it, convert this entry to a hard link entry.
+ */
+ for (le = cpio->links_head; le; le = le->next) {
+ if (le->dev == dev && le->ino == ino) {
+ archive_entry_copy_hardlink(entry, le->name);
+
+ if (--le->links <= 0) {
+ if (le->previous != NULL)
+ le->previous->next = le->next;
+ if (le->next != NULL)
+ le->next->previous = le->previous;
+ if (cpio->links_head == le)
+ cpio->links_head = le->next;
+ free(le->name);
+ free(le);
+ }
+
+ return (ARCHIVE_OK);
+ }
+ }
+
+ le = (struct links_entry *)malloc(sizeof(struct links_entry));
+ if (le == NULL) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory adding file to list");
+ return (ARCHIVE_FATAL);
+ }
+ if (cpio->links_head != NULL)
+ cpio->links_head->previous = le;
+ le->next = cpio->links_head;
+ le->previous = NULL;
+ cpio->links_head = le;
+ le->dev = dev;
+ le->ino = ino;
+ le->links = archive_entry_nlink(entry) - 1;
+ le->name = strdup(archive_entry_pathname(entry));
+ if (le->name == NULL) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory adding file to list");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_empty.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_empty.c
new file mode 100644
index 0000000000..53fb6cc474
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_empty.c
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_empty.c 191524 2009-04-26 18:24:14Z kientzle $");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static int archive_read_format_empty_bid(struct archive_read *, int);
+static int archive_read_format_empty_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_empty_read_header(struct archive_read *,
+ struct archive_entry *);
+int
+archive_read_support_format_empty(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_empty");
+
+ r = __archive_read_register_format(a,
+ NULL,
+ "empty",
+ archive_read_format_empty_bid,
+ NULL,
+ archive_read_format_empty_read_header,
+ archive_read_format_empty_read_data,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ return (r);
+}
+
+
+static int
+archive_read_format_empty_bid(struct archive_read *a, int best_bid)
+{
+ if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) == NULL)
+ return (1);
+ return (-1);
+}
+
+static int
+archive_read_format_empty_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ (void)a; /* UNUSED */
+ (void)entry; /* UNUSED */
+
+ a->archive.archive_format = ARCHIVE_FORMAT_EMPTY;
+ a->archive.archive_format_name = "Empty file";
+
+ return (ARCHIVE_EOF);
+}
+
+static int
+archive_read_format_empty_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ (void)a; /* UNUSED */
+ (void)buff; /* UNUSED */
+ (void)size; /* UNUSED */
+ (void)offset; /* UNUSED */
+
+ return (ARCHIVE_EOF);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_iso9660.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_iso9660.c
new file mode 100644
index 0000000000..f5414be2a5
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_iso9660.c
@@ -0,0 +1,3279 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2009 Andreas Henriksson <andreas@fatal.se>
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_iso9660.c 201246 2009-12-30 05:30:35Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+/* #include <stdint.h> */ /* See archive_platform.h */
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_string.h"
+
+/*
+ * An overview of ISO 9660 format:
+ *
+ * Each disk is laid out as follows:
+ * * 32k reserved for private use
+ * * Volume descriptor table. Each volume descriptor
+ * is 2k and specifies basic format information.
+ * The "Primary Volume Descriptor" (PVD) is defined by the
+ * standard and should always be present; other volume
+ * descriptors include various vendor-specific extensions.
+ * * Files and directories. Each file/dir is specified by
+ * an "extent" (starting sector and length in bytes).
+ * Dirs are just files with directory records packed one
+ * after another. The PVD contains a single dir entry
+ * specifying the location of the root directory. Everything
+ * else follows from there.
+ *
+ * This module works by first reading the volume descriptors, then
+ * building a list of directory entries, sorted by starting
+ * sector. At each step, I look for the earliest dir entry that
+ * hasn't yet been read, seek forward to that location and read
+ * that entry. If it's a dir, I slurp in the new dir entries and
+ * add them to the heap; if it's a regular file, I return the
+ * corresponding archive_entry and wait for the client to request
+ * the file body. This strategy allows us to read most compliant
+ * CDs with a single pass through the data, as required by libarchive.
+ */
+#define LOGICAL_BLOCK_SIZE 2048
+#define SYSTEM_AREA_BLOCK 16
+
+/* Structure of on-disk primary volume descriptor. */
+#define PVD_type_offset 0
+#define PVD_type_size 1
+#define PVD_id_offset (PVD_type_offset + PVD_type_size)
+#define PVD_id_size 5
+#define PVD_version_offset (PVD_id_offset + PVD_id_size)
+#define PVD_version_size 1
+#define PVD_reserved1_offset (PVD_version_offset + PVD_version_size)
+#define PVD_reserved1_size 1
+#define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size)
+#define PVD_system_id_size 32
+#define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size)
+#define PVD_volume_id_size 32
+#define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size)
+#define PVD_reserved2_size 8
+#define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size)
+#define PVD_volume_space_size_size 8
+#define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size)
+#define PVD_reserved3_size 32
+#define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size)
+#define PVD_volume_set_size_size 4
+#define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size)
+#define PVD_volume_sequence_number_size 4
+#define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size)
+#define PVD_logical_block_size_size 4
+#define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size)
+#define PVD_path_table_size_size 8
+#define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size)
+#define PVD_type_1_path_table_size 4
+#define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size)
+#define PVD_opt_type_1_path_table_size 4
+#define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size)
+#define PVD_type_m_path_table_size 4
+#define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size)
+#define PVD_opt_type_m_path_table_size 4
+#define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size)
+#define PVD_root_directory_record_size 34
+#define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size)
+#define PVD_volume_set_id_size 128
+#define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size)
+#define PVD_publisher_id_size 128
+#define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size)
+#define PVD_preparer_id_size 128
+#define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size)
+#define PVD_application_id_size 128
+#define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size)
+#define PVD_copyright_file_id_size 37
+#define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size)
+#define PVD_abstract_file_id_size 37
+#define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size)
+#define PVD_bibliographic_file_id_size 37
+#define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size)
+#define PVD_creation_date_size 17
+#define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size)
+#define PVD_modification_date_size 17
+#define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size)
+#define PVD_expiration_date_size 17
+#define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size)
+#define PVD_effective_date_size 17
+#define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size)
+#define PVD_file_structure_version_size 1
+#define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size)
+#define PVD_reserved4_size 1
+#define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size)
+#define PVD_application_data_size 512
+#define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size)
+#define PVD_reserved5_size (2048 - PVD_reserved5_offset)
+
+/* TODO: It would make future maintenance easier to just hardcode the
+ * above values. In particular, ECMA119 states the offsets as part of
+ * the standard. That would eliminate the need for the following check.*/
+#if PVD_reserved5_offset != 1395
+#error PVD offset and size definitions are wrong.
+#endif
+
+
+/* Structure of optional on-disk supplementary volume descriptor. */
+#define SVD_type_offset 0
+#define SVD_type_size 1
+#define SVD_id_offset (SVD_type_offset + SVD_type_size)
+#define SVD_id_size 5
+#define SVD_version_offset (SVD_id_offset + SVD_id_size)
+#define SVD_version_size 1
+/* ... */
+#define SVD_reserved1_offset 72
+#define SVD_reserved1_size 8
+#define SVD_volume_space_size_offset 80
+#define SVD_volume_space_size_size 8
+#define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size)
+#define SVD_escape_sequences_size 32
+/* ... */
+#define SVD_logical_block_size_offset 128
+#define SVD_logical_block_size_size 4
+#define SVD_type_L_path_table_offset 140
+#define SVD_type_M_path_table_offset 148
+/* ... */
+#define SVD_root_directory_record_offset 156
+#define SVD_root_directory_record_size 34
+#define SVD_file_structure_version_offset 881
+#define SVD_reserved2_offset 882
+#define SVD_reserved2_size 1
+#define SVD_reserved3_offset 1395
+#define SVD_reserved3_size 653
+/* ... */
+/* FIXME: validate correctness of last SVD entry offset. */
+
+/* Structure of an on-disk directory record. */
+/* Note: ISO9660 stores each multi-byte integer twice, once in
+ * each byte order. The sizes here are the size of just one
+ * of the two integers. (This is why the offset of a field isn't
+ * the same as the offset+size of the previous field.) */
+#define DR_length_offset 0
+#define DR_length_size 1
+#define DR_ext_attr_length_offset 1
+#define DR_ext_attr_length_size 1
+#define DR_extent_offset 2
+#define DR_extent_size 4
+#define DR_size_offset 10
+#define DR_size_size 4
+#define DR_date_offset 18
+#define DR_date_size 7
+#define DR_flags_offset 25
+#define DR_flags_size 1
+#define DR_file_unit_size_offset 26
+#define DR_file_unit_size_size 1
+#define DR_interleave_offset 27
+#define DR_interleave_size 1
+#define DR_volume_sequence_number_offset 28
+#define DR_volume_sequence_number_size 2
+#define DR_name_len_offset 32
+#define DR_name_len_size 1
+#define DR_name_offset 33
+
+#ifdef HAVE_ZLIB_H
+static const unsigned char zisofs_magic[8] = {
+ 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
+};
+
+struct zisofs {
+ /* Set 1 if this file compressed by paged zlib */
+ int pz;
+ int pz_log2_bs; /* Log2 of block size */
+ uint64_t pz_uncompressed_size;
+
+ int initialized;
+ unsigned char *uncompressed_buffer;
+ size_t uncompressed_buffer_size;
+
+ uint32_t pz_offset;
+ unsigned char header[16];
+ size_t header_avail;
+ int header_passed;
+ unsigned char *block_pointers;
+ size_t block_pointers_alloc;
+ size_t block_pointers_size;
+ size_t block_pointers_avail;
+ size_t block_off;
+ uint32_t block_avail;
+
+ z_stream stream;
+ int stream_valid;
+};
+#else
+struct zisofs {
+ /* Set 1 if this file compressed by paged zlib */
+ int pz;
+};
+#endif
+
+struct content {
+ uint64_t offset;/* Offset on disk. */
+ uint64_t size; /* File size in bytes. */
+ struct content *next;
+};
+
+/* In-memory storage for a directory record. */
+struct file_info {
+ struct file_info *use_next;
+ struct file_info *parent;
+ struct file_info *next;
+ struct file_info *re_next;
+ int subdirs;
+ uint64_t key; /* Heap Key. */
+ uint64_t offset; /* Offset on disk. */
+ uint64_t size; /* File size in bytes. */
+ uint32_t ce_offset; /* Offset of CE. */
+ uint32_t ce_size; /* Size of CE. */
+ char rr_moved; /* Flag to rr_moved. */
+ char rr_moved_has_re_only;
+ char re; /* Having RRIP "RE" extension. */
+ char re_descendant;
+ uint64_t cl_offset; /* Having RRIP "CL" extension. */
+ int birthtime_is_set;
+ time_t birthtime; /* File created time. */
+ time_t mtime; /* File last modified time. */
+ time_t atime; /* File last accessed time. */
+ time_t ctime; /* File attribute change time. */
+ uint64_t rdev; /* Device number. */
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ int64_t number;
+ int nlinks;
+ struct archive_string name; /* Pathname */
+ unsigned char *utf16be_name;
+ size_t utf16be_bytes;
+ char name_continues; /* Non-zero if name continues */
+ struct archive_string symlink;
+ char symlink_continues; /* Non-zero if link continues */
+ /* Set 1 if this file compressed by paged zlib(zisofs) */
+ int pz;
+ int pz_log2_bs; /* Log2 of block size */
+ uint64_t pz_uncompressed_size;
+ /* Set 1 if this file is multi extent. */
+ int multi_extent;
+ struct {
+ struct content *first;
+ struct content **last;
+ } contents;
+ struct {
+ struct file_info *first;
+ struct file_info **last;
+ } rede_files;
+};
+
+struct heap_queue {
+ struct file_info **files;
+ int allocated;
+ int used;
+};
+
+struct iso9660 {
+ int magic;
+#define ISO9660_MAGIC 0x96609660
+
+ int opt_support_joliet;
+ int opt_support_rockridge;
+
+ struct archive_string pathname;
+ char seenRockridge; /* Set true if RR extensions are used. */
+ char seenSUSP; /* Set true if SUSP is being used. */
+ char seenJoliet;
+
+ unsigned char suspOffset;
+ struct file_info *rr_moved;
+ struct read_ce_queue {
+ struct read_ce_req {
+ uint64_t offset;/* Offset of CE on disk. */
+ struct file_info *file;
+ } *reqs;
+ int cnt;
+ int allocated;
+ } read_ce_req;
+
+ int64_t previous_number;
+ struct archive_string previous_pathname;
+
+ struct file_info *use_files;
+ struct heap_queue pending_files;
+ struct {
+ struct file_info *first;
+ struct file_info **last;
+ } cache_files;
+ struct {
+ struct file_info *first;
+ struct file_info **last;
+ } re_files;
+
+ uint64_t current_position;
+ ssize_t logical_block_size;
+ uint64_t volume_size; /* Total size of volume in bytes. */
+ int32_t volume_block;/* Total size of volume in logical blocks. */
+
+ struct vd {
+ int location; /* Location of Extent. */
+ uint32_t size;
+ } primary, joliet;
+
+ int64_t entry_sparse_offset;
+ int64_t entry_bytes_remaining;
+ size_t entry_bytes_unconsumed;
+ struct zisofs entry_zisofs;
+ struct content *entry_content;
+ struct archive_string_conv *sconv_utf16be;
+ /*
+ * Buffers for a full pathname in UTF-16BE in Joliet extensions.
+ */
+#define UTF16_NAME_MAX 1024
+ unsigned char *utf16be_path;
+ size_t utf16be_path_len;
+ unsigned char *utf16be_previous_path;
+ size_t utf16be_previous_path_len;
+ /* Null buffer used in bidder to improve its performance. */
+ unsigned char null[2048];
+};
+
+static int archive_read_format_iso9660_bid(struct archive_read *, int);
+static int archive_read_format_iso9660_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_iso9660_cleanup(struct archive_read *);
+static int archive_read_format_iso9660_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_iso9660_read_data_skip(struct archive_read *);
+static int archive_read_format_iso9660_read_header(struct archive_read *,
+ struct archive_entry *);
+static const char *build_pathname(struct archive_string *, struct file_info *, int);
+static int build_pathname_utf16be(unsigned char *, size_t, size_t *,
+ struct file_info *);
+#if DEBUG
+static void dump_isodirrec(FILE *, const unsigned char *isodirrec);
+#endif
+static time_t time_from_tm(struct tm *);
+static time_t isodate17(const unsigned char *);
+static time_t isodate7(const unsigned char *);
+static int isBootRecord(struct iso9660 *, const unsigned char *);
+static int isVolumePartition(struct iso9660 *, const unsigned char *);
+static int isVDSetTerminator(struct iso9660 *, const unsigned char *);
+static int isJolietSVD(struct iso9660 *, const unsigned char *);
+static int isSVD(struct iso9660 *, const unsigned char *);
+static int isEVD(struct iso9660 *, const unsigned char *);
+static int isPVD(struct iso9660 *, const unsigned char *);
+static int next_cache_entry(struct archive_read *, struct iso9660 *,
+ struct file_info **);
+static int next_entry_seek(struct archive_read *, struct iso9660 *,
+ struct file_info **);
+static struct file_info *
+ parse_file_info(struct archive_read *a,
+ struct file_info *parent, const unsigned char *isodirrec,
+ size_t reclen);
+static int parse_rockridge(struct archive_read *a,
+ struct file_info *file, const unsigned char *start,
+ const unsigned char *end);
+static int register_CE(struct archive_read *a, int32_t location,
+ struct file_info *file);
+static int read_CE(struct archive_read *a, struct iso9660 *iso9660);
+static void parse_rockridge_NM1(struct file_info *,
+ const unsigned char *, int);
+static void parse_rockridge_SL1(struct file_info *,
+ const unsigned char *, int);
+static void parse_rockridge_TF1(struct file_info *,
+ const unsigned char *, int);
+static void parse_rockridge_ZF1(struct file_info *,
+ const unsigned char *, int);
+static void register_file(struct iso9660 *, struct file_info *);
+static void release_files(struct iso9660 *);
+static unsigned toi(const void *p, int n);
+static inline void re_add_entry(struct iso9660 *, struct file_info *);
+static inline struct file_info * re_get_entry(struct iso9660 *);
+static inline int rede_add_entry(struct file_info *);
+static inline struct file_info * rede_get_entry(struct file_info *);
+static inline void cache_add_entry(struct iso9660 *iso9660,
+ struct file_info *file);
+static inline struct file_info *cache_get_entry(struct iso9660 *iso9660);
+static int heap_add_entry(struct archive_read *a, struct heap_queue *heap,
+ struct file_info *file, uint64_t key);
+static struct file_info *heap_get_entry(struct heap_queue *heap);
+
+#define add_entry(arch, iso9660, file) \
+ heap_add_entry(arch, &((iso9660)->pending_files), file, file->offset)
+#define next_entry(iso9660) \
+ heap_get_entry(&((iso9660)->pending_files))
+
+int
+archive_read_support_format_iso9660(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct iso9660 *iso9660;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_iso9660");
+
+ iso9660 = (struct iso9660 *)calloc(1, sizeof(*iso9660));
+ if (iso9660 == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate iso9660 data");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->magic = ISO9660_MAGIC;
+ iso9660->cache_files.first = NULL;
+ iso9660->cache_files.last = &(iso9660->cache_files.first);
+ iso9660->re_files.first = NULL;
+ iso9660->re_files.last = &(iso9660->re_files.first);
+ /* Enable to support Joliet extensions by default. */
+ iso9660->opt_support_joliet = 1;
+ /* Enable to support Rock Ridge extensions by default. */
+ iso9660->opt_support_rockridge = 1;
+
+ r = __archive_read_register_format(a,
+ iso9660,
+ "iso9660",
+ archive_read_format_iso9660_bid,
+ archive_read_format_iso9660_options,
+ archive_read_format_iso9660_read_header,
+ archive_read_format_iso9660_read_data,
+ archive_read_format_iso9660_read_data_skip,
+ NULL,
+ archive_read_format_iso9660_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK) {
+ free(iso9660);
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+
+static int
+archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
+{
+ struct iso9660 *iso9660;
+ ssize_t bytes_read;
+ const unsigned char *p;
+ int seenTerminator;
+
+ /* If there's already a better bid than we can ever
+ make, don't bother testing. */
+ if (best_bid > 48)
+ return (-1);
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ /*
+ * Skip the first 32k (reserved area) and get the first
+ * 8 sectors of the volume descriptor table. Of course,
+ * if the I/O layer gives us more, we'll take it.
+ */
+#define RESERVED_AREA (SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE)
+ p = __archive_read_ahead(a,
+ RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE,
+ &bytes_read);
+ if (p == NULL)
+ return (-1);
+
+ /* Skip the reserved area. */
+ bytes_read -= RESERVED_AREA;
+ p += RESERVED_AREA;
+
+ /* Check each volume descriptor. */
+ seenTerminator = 0;
+ for (; bytes_read > LOGICAL_BLOCK_SIZE;
+ bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) {
+ /* Do not handle undefined Volume Descriptor Type. */
+ if (p[0] >= 4 && p[0] <= 254)
+ return (0);
+ /* Standard Identifier must be "CD001" */
+ if (memcmp(p + 1, "CD001", 5) != 0)
+ return (0);
+ if (isPVD(iso9660, p))
+ continue;
+ if (!iso9660->joliet.location) {
+ if (isJolietSVD(iso9660, p))
+ continue;
+ }
+ if (isBootRecord(iso9660, p))
+ continue;
+ if (isEVD(iso9660, p))
+ continue;
+ if (isSVD(iso9660, p))
+ continue;
+ if (isVolumePartition(iso9660, p))
+ continue;
+ if (isVDSetTerminator(iso9660, p)) {
+ seenTerminator = 1;
+ break;
+ }
+ return (0);
+ }
+ /*
+ * ISO 9660 format must have Primary Volume Descriptor and
+ * Volume Descriptor Set Terminator.
+ */
+ if (seenTerminator && iso9660->primary.location > 16)
+ return (48);
+
+ /* We didn't find a valid PVD; return a bid of zero. */
+ return (0);
+}
+
+static int
+archive_read_format_iso9660_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct iso9660 *iso9660;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ if (strcmp(key, "joliet") == 0) {
+ if (val == NULL || strcmp(val, "off") == 0 ||
+ strcmp(val, "ignore") == 0 ||
+ strcmp(val, "disable") == 0 ||
+ strcmp(val, "0") == 0)
+ iso9660->opt_support_joliet = 0;
+ else
+ iso9660->opt_support_joliet = 1;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "rockridge") == 0 ||
+ strcmp(key, "Rockridge") == 0) {
+ iso9660->opt_support_rockridge = val != NULL;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset,
+unsigned bytes)
+{
+
+ while (bytes >= sizeof(iso9660->null)) {
+ if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null)))
+ return (0);
+ offset += sizeof(iso9660->null);
+ bytes -= sizeof(iso9660->null);
+ }
+ if (bytes)
+ return memcmp(iso9660->null, h + offset, bytes) == 0;
+ else
+ return (1);
+}
+
+static int
+isBootRecord(struct iso9660 *iso9660, const unsigned char *h)
+{
+ (void)iso9660; /* UNUSED */
+
+ /* Type of the Volume Descriptor Boot Record must be 0. */
+ if (h[0] != 0)
+ return (0);
+
+ /* Volume Descriptor Version must be 1. */
+ if (h[6] != 1)
+ return (0);
+
+ return (1);
+}
+
+static int
+isVolumePartition(struct iso9660 *iso9660, const unsigned char *h)
+{
+ int32_t location;
+
+ /* Type of the Volume Partition Descriptor must be 3. */
+ if (h[0] != 3)
+ return (0);
+
+ /* Volume Descriptor Version must be 1. */
+ if (h[6] != 1)
+ return (0);
+ /* Unused Field */
+ if (h[7] != 0)
+ return (0);
+
+ location = archive_le32dec(h + 72);
+ if (location <= SYSTEM_AREA_BLOCK ||
+ location >= iso9660->volume_block)
+ return (0);
+ if ((uint32_t)location != archive_be32dec(h + 76))
+ return (0);
+
+ return (1);
+}
+
+static int
+isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)
+{
+ (void)iso9660; /* UNUSED */
+
+ /* Type of the Volume Descriptor Set Terminator must be 255. */
+ if (h[0] != 255)
+ return (0);
+
+ /* Volume Descriptor Version must be 1. */
+ if (h[6] != 1)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, 7, 2048-7))
+ return (0);
+
+ return (1);
+}
+
+static int
+isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+ const unsigned char *p;
+ ssize_t logical_block_size;
+ int32_t volume_block;
+
+ /* Check if current sector is a kind of Supplementary Volume
+ * Descriptor. */
+ if (!isSVD(iso9660, h))
+ return (0);
+
+ /* FIXME: do more validations according to joliet spec. */
+
+ /* check if this SVD contains joliet extension! */
+ p = h + SVD_escape_sequences_offset;
+ /* N.B. Joliet spec says p[1] == '\\', but.... */
+ if (p[0] == '%' && p[1] == '/') {
+ int level = 0;
+
+ if (p[2] == '@')
+ level = 1;
+ else if (p[2] == 'C')
+ level = 2;
+ else if (p[2] == 'E')
+ level = 3;
+ else /* not joliet */
+ return (0);
+
+ iso9660->seenJoliet = level;
+
+ } else /* not joliet */
+ return (0);
+
+ logical_block_size =
+ archive_le16dec(h + SVD_logical_block_size_offset);
+ volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
+
+ iso9660->logical_block_size = logical_block_size;
+ iso9660->volume_block = volume_block;
+ iso9660->volume_size = logical_block_size * (uint64_t)volume_block;
+ /* Read Root Directory Record in Volume Descriptor. */
+ p = h + SVD_root_directory_record_offset;
+ iso9660->joliet.location = archive_le32dec(p + DR_extent_offset);
+ iso9660->joliet.size = archive_le32dec(p + DR_size_offset);
+
+ return (48);
+}
+
+static int
+isSVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+ const unsigned char *p;
+ ssize_t logical_block_size;
+ int32_t volume_block;
+ int32_t location;
+
+ (void)iso9660; /* UNUSED */
+
+ /* Type 2 means it's a SVD. */
+ if (h[SVD_type_offset] != 2)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size))
+ return (0);
+ if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size))
+ return (0);
+ if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size))
+ return (0);
+
+ /* File structure version must be 1 for ISO9660/ECMA119. */
+ if (h[SVD_file_structure_version_offset] != 1)
+ return (0);
+
+ logical_block_size =
+ archive_le16dec(h + SVD_logical_block_size_offset);
+ if (logical_block_size <= 0)
+ return (0);
+
+ volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
+ if (volume_block <= SYSTEM_AREA_BLOCK+4)
+ return (0);
+
+ /* Location of Occurrence of Type L Path Table must be
+ * available location,
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_le32dec(h+SVD_type_L_path_table_offset);
+ if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+ return (0);
+
+ /* The Type M Path Table must be at a valid location (WinISO
+ * and probably other programs omit this, so we allow zero)
+ *
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_be32dec(h+SVD_type_M_path_table_offset);
+ if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+ || location >= volume_block)
+ return (0);
+
+ /* Read Root Directory Record in Volume Descriptor. */
+ p = h + SVD_root_directory_record_offset;
+ if (p[DR_length_offset] != 34)
+ return (0);
+
+ return (48);
+}
+
+static int
+isEVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+ const unsigned char *p;
+ ssize_t logical_block_size;
+ int32_t volume_block;
+ int32_t location;
+
+ (void)iso9660; /* UNUSED */
+
+ /* Type of the Enhanced Volume Descriptor must be 2. */
+ if (h[PVD_type_offset] != 2)
+ return (0);
+
+ /* EVD version must be 2. */
+ if (h[PVD_version_offset] != 2)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (h[PVD_reserved1_offset] != 0)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+ return (0);
+
+ /* Logical block size must be > 0. */
+ /* I've looked at Ecma 119 and can't find any stronger
+ * restriction on this field. */
+ logical_block_size =
+ archive_le16dec(h + PVD_logical_block_size_offset);
+ if (logical_block_size <= 0)
+ return (0);
+
+ volume_block =
+ archive_le32dec(h + PVD_volume_space_size_offset);
+ if (volume_block <= SYSTEM_AREA_BLOCK+4)
+ return (0);
+
+ /* File structure version must be 2 for ISO9660:1999. */
+ if (h[PVD_file_structure_version_offset] != 2)
+ return (0);
+
+ /* Location of Occurrence of Type L Path Table must be
+ * available location,
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_le32dec(h+PVD_type_1_path_table_offset);
+ if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+ return (0);
+
+ /* Location of Occurrence of Type M Path Table must be
+ * available location,
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_be32dec(h+PVD_type_m_path_table_offset);
+ if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+ || location >= volume_block)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size))
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+ return (0);
+
+ /* Read Root Directory Record in Volume Descriptor. */
+ p = h + PVD_root_directory_record_offset;
+ if (p[DR_length_offset] != 34)
+ return (0);
+
+ return (48);
+}
+
+static int
+isPVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+ const unsigned char *p;
+ ssize_t logical_block_size;
+ int32_t volume_block;
+ int32_t location;
+ int i;
+
+ /* Type of the Primary Volume Descriptor must be 1. */
+ if (h[PVD_type_offset] != 1)
+ return (0);
+
+ /* PVD version must be 1. */
+ if (h[PVD_version_offset] != 1)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (h[PVD_reserved1_offset] != 0)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+ return (0);
+
+ /* Logical block size must be > 0. */
+ /* I've looked at Ecma 119 and can't find any stronger
+ * restriction on this field. */
+ logical_block_size =
+ archive_le16dec(h + PVD_logical_block_size_offset);
+ if (logical_block_size <= 0)
+ return (0);
+
+ volume_block = archive_le32dec(h + PVD_volume_space_size_offset);
+ if (volume_block <= SYSTEM_AREA_BLOCK+4)
+ return (0);
+
+ /* File structure version must be 1 for ISO9660/ECMA119. */
+ if (h[PVD_file_structure_version_offset] != 1)
+ return (0);
+
+ /* Location of Occurrence of Type L Path Table must be
+ * available location,
+ * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_le32dec(h+PVD_type_1_path_table_offset);
+ if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+ return (0);
+
+ /* The Type M Path Table must also be at a valid location
+ * (although ECMA 119 requires a Type M Path Table, WinISO and
+ * probably other programs omit it, so we permit a zero here)
+ *
+ * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+ location = archive_be32dec(h+PVD_type_m_path_table_offset);
+ if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+ || location >= volume_block)
+ return (0);
+
+ /* Reserved field must be 0. */
+ /* But accept NetBSD/FreeBSD "makefs" images with 0x20 here. */
+ for (i = 0; i < PVD_reserved4_size; ++i)
+ if (h[PVD_reserved4_offset + i] != 0
+ && h[PVD_reserved4_offset + i] != 0x20)
+ return (0);
+
+ /* Reserved field must be 0. */
+ if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+ return (0);
+
+ /* XXX TODO: Check other values for sanity; reject more
+ * malformed PVDs. XXX */
+
+ /* Read Root Directory Record in Volume Descriptor. */
+ p = h + PVD_root_directory_record_offset;
+ if (p[DR_length_offset] != 34)
+ return (0);
+
+ if (!iso9660->primary.location) {
+ iso9660->logical_block_size = logical_block_size;
+ iso9660->volume_block = volume_block;
+ iso9660->volume_size =
+ logical_block_size * (uint64_t)volume_block;
+ iso9660->primary.location =
+ archive_le32dec(p + DR_extent_offset);
+ iso9660->primary.size = archive_le32dec(p + DR_size_offset);
+ }
+
+ return (48);
+}
+
+static int
+read_children(struct archive_read *a, struct file_info *parent)
+{
+ struct iso9660 *iso9660;
+ const unsigned char *b, *p;
+ struct file_info *multi;
+ size_t step, skip_size;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+ /* flush any remaining bytes from the last round to ensure
+ * we're positioned */
+ if (iso9660->entry_bytes_unconsumed) {
+ __archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+ iso9660->entry_bytes_unconsumed = 0;
+ }
+ if (iso9660->current_position > parent->offset) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring out-of-order directory (%s) %jd > %jd",
+ parent->name.s,
+ (intmax_t)iso9660->current_position,
+ (intmax_t)parent->offset);
+ return (ARCHIVE_WARN);
+ }
+ if (parent->offset + parent->size > iso9660->volume_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Directory is beyond end-of-media: %s",
+ parent->name.s);
+ return (ARCHIVE_WARN);
+ }
+ if (iso9660->current_position < parent->offset) {
+ int64_t skipsize;
+
+ skipsize = parent->offset - iso9660->current_position;
+ skipsize = __archive_read_consume(a, skipsize);
+ if (skipsize < 0)
+ return ((int)skipsize);
+ iso9660->current_position = parent->offset;
+ }
+
+ step = (size_t)(((parent->size + iso9660->logical_block_size -1) /
+ iso9660->logical_block_size) * iso9660->logical_block_size);
+ b = __archive_read_ahead(a, step, NULL);
+ if (b == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to read full block when scanning "
+ "ISO9660 directory list");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->current_position += step;
+ multi = NULL;
+ skip_size = step;
+ while (step) {
+ p = b;
+ b += iso9660->logical_block_size;
+ step -= iso9660->logical_block_size;
+ for (; *p != 0 && p + DR_name_offset < b && p + *p <= b;
+ p += *p) {
+ struct file_info *child;
+
+ /* N.B.: these special directory identifiers
+ * are 8 bit "values" even on a
+ * Joliet CD with UCS-2 (16bit) encoding.
+ */
+
+ /* Skip '.' entry. */
+ if (*(p + DR_name_len_offset) == 1
+ && *(p + DR_name_offset) == '\0')
+ continue;
+ /* Skip '..' entry. */
+ if (*(p + DR_name_len_offset) == 1
+ && *(p + DR_name_offset) == '\001')
+ continue;
+ child = parse_file_info(a, parent, p, b - p);
+ if (child == NULL) {
+ __archive_read_consume(a, skip_size);
+ return (ARCHIVE_FATAL);
+ }
+ if (child->cl_offset == 0 &&
+ (child->multi_extent || multi != NULL)) {
+ struct content *con;
+
+ if (multi == NULL) {
+ multi = child;
+ multi->contents.first = NULL;
+ multi->contents.last =
+ &(multi->contents.first);
+ }
+ con = malloc(sizeof(struct content));
+ if (con == NULL) {
+ archive_set_error(
+ &a->archive, ENOMEM,
+ "No memory for multi extent");
+ __archive_read_consume(a, skip_size);
+ return (ARCHIVE_FATAL);
+ }
+ con->offset = child->offset;
+ con->size = child->size;
+ con->next = NULL;
+ *multi->contents.last = con;
+ multi->contents.last = &(con->next);
+ if (multi == child) {
+ if (add_entry(a, iso9660, child)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ multi->size += child->size;
+ if (!child->multi_extent)
+ multi = NULL;
+ }
+ } else
+ if (add_entry(a, iso9660, child) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ __archive_read_consume(a, skip_size);
+
+ /* Read data which recorded by RRIP "CE" extension. */
+ if (read_CE(a, iso9660) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+choose_volume(struct archive_read *a, struct iso9660 *iso9660)
+{
+ struct file_info *file;
+ int64_t skipsize;
+ struct vd *vd;
+ const void *block;
+ char seenJoliet;
+
+ vd = &(iso9660->primary);
+ if (!iso9660->opt_support_joliet)
+ iso9660->seenJoliet = 0;
+ if (iso9660->seenJoliet &&
+ vd->location > iso9660->joliet.location)
+ /* This condition is unlikely; by way of caution. */
+ vd = &(iso9660->joliet);
+
+ skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;
+ skipsize = __archive_read_consume(a, skipsize);
+ if (skipsize < 0)
+ return ((int)skipsize);
+ iso9660->current_position = skipsize;
+
+ block = __archive_read_ahead(a, vd->size, NULL);
+ if (block == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to read full block when scanning "
+ "ISO9660 directory list");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * While reading Root Directory, flag seenJoliet must be zero to
+ * avoid converting special name 0x00(Current Directory) and
+ * next byte to UCS2.
+ */
+ seenJoliet = iso9660->seenJoliet;/* Save flag. */
+ iso9660->seenJoliet = 0;
+ file = parse_file_info(a, NULL, block, vd->size);
+ if (file == NULL)
+ return (ARCHIVE_FATAL);
+ iso9660->seenJoliet = seenJoliet;
+
+ /*
+ * If the iso image has both RockRidge and Joliet, we preferentially
+ * use RockRidge Extensions rather than Joliet ones.
+ */
+ if (vd == &(iso9660->primary) && iso9660->seenRockridge
+ && iso9660->seenJoliet)
+ iso9660->seenJoliet = 0;
+
+ if (vd == &(iso9660->primary) && !iso9660->seenRockridge
+ && iso9660->seenJoliet) {
+ /* Switch reading data from primary to joliet. */
+ vd = &(iso9660->joliet);
+ skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;
+ skipsize -= iso9660->current_position;
+ skipsize = __archive_read_consume(a, skipsize);
+ if (skipsize < 0)
+ return ((int)skipsize);
+ iso9660->current_position += skipsize;
+
+ block = __archive_read_ahead(a, vd->size, NULL);
+ if (block == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to read full block when scanning "
+ "ISO9660 directory list");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->seenJoliet = 0;
+ file = parse_file_info(a, NULL, block, vd->size);
+ if (file == NULL)
+ return (ARCHIVE_FATAL);
+ iso9660->seenJoliet = seenJoliet;
+ }
+
+ /* Store the root directory in the pending list. */
+ if (add_entry(a, iso9660, file) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (iso9660->seenRockridge) {
+ a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE;
+ a->archive.archive_format_name =
+ "ISO9660 with Rockridge extensions";
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct iso9660 *iso9660;
+ struct file_info *file;
+ int r, rd_r = ARCHIVE_OK;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ if (!a->archive.archive_format) {
+ a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
+ a->archive.archive_format_name = "ISO9660";
+ }
+
+ if (iso9660->current_position == 0) {
+ r = choose_volume(a, iso9660);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ file = NULL;/* Eliminate a warning. */
+ /* Get the next entry that appears after the current offset. */
+ r = next_entry_seek(a, iso9660, &file);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ if (iso9660->seenJoliet) {
+ /*
+ * Convert UTF-16BE of a filename to local locale MBS
+ * and store the result into a filename field.
+ */
+ if (iso9660->sconv_utf16be == NULL) {
+ iso9660->sconv_utf16be =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ }
+ if (iso9660->utf16be_path == NULL) {
+ iso9660->utf16be_path = malloc(UTF16_NAME_MAX);
+ if (iso9660->utf16be_path == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (iso9660->utf16be_previous_path == NULL) {
+ iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX);
+ if (iso9660->utf16be_previous_path == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ iso9660->utf16be_path_len = 0;
+ if (build_pathname_utf16be(iso9660->utf16be_path,
+ UTF16_NAME_MAX, &(iso9660->utf16be_path_len), file) != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname is too long");
+ return (ARCHIVE_FATAL);
+ }
+
+ r = archive_entry_copy_pathname_l(entry,
+ (const char *)iso9660->utf16be_path,
+ iso9660->utf16be_path_len,
+ iso9660->sconv_utf16be);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(
+ iso9660->sconv_utf16be));
+
+ rd_r = ARCHIVE_WARN;
+ }
+ } else {
+ const char *path = build_pathname(&iso9660->pathname, file, 0);
+ if (path == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname is too long");
+ return (ARCHIVE_FATAL);
+ } else {
+ archive_string_empty(&iso9660->pathname);
+ archive_entry_set_pathname(entry, path);
+ }
+ }
+
+ iso9660->entry_bytes_remaining = file->size;
+ /* Offset for sparse-file-aware clients. */
+ iso9660->entry_sparse_offset = 0;
+
+ if (file->offset + file->size > iso9660->volume_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "File is beyond end-of-media: %s",
+ archive_entry_pathname(entry));
+ iso9660->entry_bytes_remaining = 0;
+ return (ARCHIVE_WARN);
+ }
+
+ /* Set up the entry structure with information about this entry. */
+ archive_entry_set_mode(entry, file->mode);
+ archive_entry_set_uid(entry, file->uid);
+ archive_entry_set_gid(entry, file->gid);
+ archive_entry_set_nlink(entry, file->nlinks);
+ if (file->birthtime_is_set)
+ archive_entry_set_birthtime(entry, file->birthtime, 0);
+ else
+ archive_entry_unset_birthtime(entry);
+ archive_entry_set_mtime(entry, file->mtime, 0);
+ archive_entry_set_ctime(entry, file->ctime, 0);
+ archive_entry_set_atime(entry, file->atime, 0);
+ /* N.B.: Rock Ridge supports 64-bit device numbers. */
+ archive_entry_set_rdev(entry, (dev_t)file->rdev);
+ archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
+ if (file->symlink.s != NULL)
+ archive_entry_copy_symlink(entry, file->symlink.s);
+
+ /* Note: If the input isn't seekable, we can't rewind to
+ * return the same body again, so if the next entry refers to
+ * the same data, we have to return it as a hardlink to the
+ * original entry. */
+ if (file->number != -1 &&
+ file->number == iso9660->previous_number) {
+ if (iso9660->seenJoliet) {
+ r = archive_entry_copy_hardlink_l(entry,
+ (const char *)iso9660->utf16be_previous_path,
+ iso9660->utf16be_previous_path_len,
+ iso9660->sconv_utf16be);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Linkname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(
+ iso9660->sconv_utf16be));
+ rd_r = ARCHIVE_WARN;
+ }
+ } else
+ archive_entry_set_hardlink(entry,
+ iso9660->previous_pathname.s);
+ archive_entry_unset_size(entry);
+ iso9660->entry_bytes_remaining = 0;
+ return (rd_r);
+ }
+
+ if ((file->mode & AE_IFMT) != AE_IFDIR &&
+ file->offset < iso9660->current_position) {
+ int64_t r64;
+
+ r64 = __archive_read_seek(a, file->offset, SEEK_SET);
+ if (r64 != (int64_t)file->offset) {
+ /* We can't seek backwards to extract it, so issue
+ * a warning. Note that this can only happen if
+ * this entry was added to the heap after we passed
+ * this offset, that is, only if the directory
+ * mentioning this entry is later than the body of
+ * the entry. Such layouts are very unusual; most
+ * ISO9660 writers lay out and record all directory
+ * information first, then store all file bodies. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring out-of-order file @%jx (%s) %jd < %jd",
+ (intmax_t)file->number,
+ iso9660->pathname.s,
+ (intmax_t)file->offset,
+ (intmax_t)iso9660->current_position);
+ iso9660->entry_bytes_remaining = 0;
+ return (ARCHIVE_WARN);
+ }
+ iso9660->current_position = (uint64_t)r64;
+ }
+
+ /* Initialize zisofs variables. */
+ iso9660->entry_zisofs.pz = file->pz;
+ if (file->pz) {
+#ifdef HAVE_ZLIB_H
+ struct zisofs *zisofs;
+
+ zisofs = &iso9660->entry_zisofs;
+ zisofs->initialized = 0;
+ zisofs->pz_log2_bs = file->pz_log2_bs;
+ zisofs->pz_uncompressed_size = file->pz_uncompressed_size;
+ zisofs->pz_offset = 0;
+ zisofs->header_avail = 0;
+ zisofs->header_passed = 0;
+ zisofs->block_pointers_avail = 0;
+#endif
+ archive_entry_set_size(entry, file->pz_uncompressed_size);
+ }
+
+ iso9660->previous_number = file->number;
+ if (iso9660->seenJoliet) {
+ memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path,
+ iso9660->utf16be_path_len);
+ iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len;
+ } else
+ archive_strcpy(
+ &iso9660->previous_pathname, iso9660->pathname.s);
+
+ /* Reset entry_bytes_remaining if the file is multi extent. */
+ iso9660->entry_content = file->contents.first;
+ if (iso9660->entry_content != NULL)
+ iso9660->entry_bytes_remaining = iso9660->entry_content->size;
+
+ if (archive_entry_filetype(entry) == AE_IFDIR) {
+ /* Overwrite nlinks by proper link number which is
+ * calculated from number of sub directories. */
+ archive_entry_set_nlink(entry, 2 + file->subdirs);
+ /* Directory data has been read completely. */
+ iso9660->entry_bytes_remaining = 0;
+ }
+
+ if (rd_r != ARCHIVE_OK)
+ return (rd_r);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_read_data_skip(struct archive_read *a)
+{
+ /* Because read_next_header always does an explicit skip
+ * to the next entry, we don't need to do anything here. */
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+zisofs_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct iso9660 *iso9660;
+ struct zisofs *zisofs;
+ const unsigned char *p;
+ size_t avail;
+ ssize_t bytes_read;
+ size_t uncompressed_size;
+ int r;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+ zisofs = &iso9660->entry_zisofs;
+
+ p = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read <= 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated zisofs file body");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_read > iso9660->entry_bytes_remaining)
+ bytes_read = (ssize_t)iso9660->entry_bytes_remaining;
+ avail = bytes_read;
+ uncompressed_size = 0;
+
+ if (!zisofs->initialized) {
+ size_t ceil, xsize;
+
+ /* Allocate block pointers buffer. */
+ ceil = (size_t)((zisofs->pz_uncompressed_size +
+ (((int64_t)1) << zisofs->pz_log2_bs) - 1)
+ >> zisofs->pz_log2_bs);
+ xsize = (ceil + 1) * 4;
+ if (zisofs->block_pointers_alloc < xsize) {
+ size_t alloc;
+
+ if (zisofs->block_pointers != NULL)
+ free(zisofs->block_pointers);
+ alloc = ((xsize >> 10) + 1) << 10;
+ zisofs->block_pointers = malloc(alloc);
+ if (zisofs->block_pointers == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for zisofs decompression");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->block_pointers_alloc = alloc;
+ }
+ zisofs->block_pointers_size = xsize;
+
+ /* Allocate uncompressed data buffer. */
+ xsize = (size_t)1UL << zisofs->pz_log2_bs;
+ if (zisofs->uncompressed_buffer_size < xsize) {
+ if (zisofs->uncompressed_buffer != NULL)
+ free(zisofs->uncompressed_buffer);
+ zisofs->uncompressed_buffer = malloc(xsize);
+ if (zisofs->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for zisofs decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ zisofs->uncompressed_buffer_size = xsize;
+
+ /*
+ * Read the file header, and check the magic code of zisofs.
+ */
+ if (zisofs->header_avail < sizeof(zisofs->header)) {
+ xsize = sizeof(zisofs->header) - zisofs->header_avail;
+ if (avail < xsize)
+ xsize = avail;
+ memcpy(zisofs->header + zisofs->header_avail, p, xsize);
+ zisofs->header_avail += xsize;
+ avail -= xsize;
+ p += xsize;
+ }
+ if (!zisofs->header_passed &&
+ zisofs->header_avail == sizeof(zisofs->header)) {
+ int err = 0;
+
+ if (memcmp(zisofs->header, zisofs_magic,
+ sizeof(zisofs_magic)) != 0)
+ err = 1;
+ if (archive_le32dec(zisofs->header + 8)
+ != zisofs->pz_uncompressed_size)
+ err = 1;
+ if (zisofs->header[12] != 4)
+ err = 1;
+ if (zisofs->header[13] != zisofs->pz_log2_bs)
+ err = 1;
+ if (err) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs file body");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->header_passed = 1;
+ }
+ /*
+ * Read block pointers.
+ */
+ if (zisofs->header_passed &&
+ zisofs->block_pointers_avail < zisofs->block_pointers_size) {
+ xsize = zisofs->block_pointers_size
+ - zisofs->block_pointers_avail;
+ if (avail < xsize)
+ xsize = avail;
+ memcpy(zisofs->block_pointers
+ + zisofs->block_pointers_avail, p, xsize);
+ zisofs->block_pointers_avail += xsize;
+ avail -= xsize;
+ p += xsize;
+ if (zisofs->block_pointers_avail
+ == zisofs->block_pointers_size) {
+ /* We've got all block pointers and initialize
+ * related variables. */
+ zisofs->block_off = 0;
+ zisofs->block_avail = 0;
+ /* Complete a initialization */
+ zisofs->initialized = 1;
+ }
+ }
+
+ if (!zisofs->initialized)
+ goto next_data; /* We need more data. */
+ }
+
+ /*
+ * Get block offsets from block pointers.
+ */
+ if (zisofs->block_avail == 0) {
+ uint32_t bst, bed;
+
+ if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
+ /* There isn't a pair of offsets. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers");
+ return (ARCHIVE_FATAL);
+ }
+ bst = archive_le32dec(
+ zisofs->block_pointers + zisofs->block_off);
+ if (bst != zisofs->pz_offset + (bytes_read - avail)) {
+ /* TODO: Should we seek offset of current file
+ * by bst ? */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers(cannot seek)");
+ return (ARCHIVE_FATAL);
+ }
+ bed = archive_le32dec(
+ zisofs->block_pointers + zisofs->block_off + 4);
+ if (bed < bst) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->block_avail = bed - bst;
+ zisofs->block_off += 4;
+
+ /* Initialize compression library for new block. */
+ if (zisofs->stream_valid)
+ r = inflateReset(&zisofs->stream);
+ else
+ r = inflateInit(&zisofs->stream);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize zisofs decompression.");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->stream_valid = 1;
+ zisofs->stream.total_in = 0;
+ zisofs->stream.total_out = 0;
+ }
+
+ /*
+ * Make uncompressed data.
+ */
+ if (zisofs->block_avail == 0) {
+ memset(zisofs->uncompressed_buffer, 0,
+ zisofs->uncompressed_buffer_size);
+ uncompressed_size = zisofs->uncompressed_buffer_size;
+ } else {
+ zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;
+ if (avail > zisofs->block_avail)
+ zisofs->stream.avail_in = zisofs->block_avail;
+ else
+ zisofs->stream.avail_in = (uInt)avail;
+ zisofs->stream.next_out = zisofs->uncompressed_buffer;
+ zisofs->stream.avail_out =
+ (uInt)zisofs->uncompressed_buffer_size;
+
+ r = inflate(&zisofs->stream, 0);
+ switch (r) {
+ case Z_OK: /* Decompressor made some progress.*/
+ case Z_STREAM_END: /* Found end of stream. */
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "zisofs decompression failed (%d)", r);
+ return (ARCHIVE_FATAL);
+ }
+ uncompressed_size =
+ zisofs->uncompressed_buffer_size - zisofs->stream.avail_out;
+ avail -= zisofs->stream.next_in - p;
+ zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);
+ }
+next_data:
+ bytes_read -= avail;
+ *buff = zisofs->uncompressed_buffer;
+ *size = uncompressed_size;
+ *offset = iso9660->entry_sparse_offset;
+ iso9660->entry_sparse_offset += uncompressed_size;
+ iso9660->entry_bytes_remaining -= bytes_read;
+ iso9660->current_position += bytes_read;
+ zisofs->pz_offset += (uint32_t)bytes_read;
+ iso9660->entry_bytes_unconsumed += bytes_read;
+
+ return (ARCHIVE_OK);
+}
+
+#else /* HAVE_ZLIB_H */
+
+static int
+zisofs_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+
+ (void)buff;/* UNUSED */
+ (void)size;/* UNUSED */
+ (void)offset;/* UNUSED */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "zisofs is not supported on this platform.");
+ return (ARCHIVE_FAILED);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+archive_read_format_iso9660_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ ssize_t bytes_read;
+ struct iso9660 *iso9660;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ if (iso9660->entry_bytes_unconsumed) {
+ __archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+ iso9660->entry_bytes_unconsumed = 0;
+ }
+
+ if (iso9660->entry_bytes_remaining <= 0) {
+ if (iso9660->entry_content != NULL)
+ iso9660->entry_content = iso9660->entry_content->next;
+ if (iso9660->entry_content == NULL) {
+ *buff = NULL;
+ *size = 0;
+ *offset = iso9660->entry_sparse_offset;
+ return (ARCHIVE_EOF);
+ }
+ /* Seek forward to the start of the entry. */
+ if (iso9660->current_position < iso9660->entry_content->offset) {
+ int64_t step;
+
+ step = iso9660->entry_content->offset -
+ iso9660->current_position;
+ step = __archive_read_consume(a, step);
+ if (step < 0)
+ return ((int)step);
+ iso9660->current_position =
+ iso9660->entry_content->offset;
+ }
+ if (iso9660->entry_content->offset < iso9660->current_position) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring out-of-order file (%s) %jd < %jd",
+ iso9660->pathname.s,
+ (intmax_t)iso9660->entry_content->offset,
+ (intmax_t)iso9660->current_position);
+ *buff = NULL;
+ *size = 0;
+ *offset = iso9660->entry_sparse_offset;
+ return (ARCHIVE_WARN);
+ }
+ iso9660->entry_bytes_remaining = iso9660->entry_content->size;
+ }
+ if (iso9660->entry_zisofs.pz)
+ return (zisofs_read_data(a, buff, size, offset));
+
+ *buff = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated input file");
+ if (*buff == NULL)
+ return (ARCHIVE_FATAL);
+ if (bytes_read > iso9660->entry_bytes_remaining)
+ bytes_read = (ssize_t)iso9660->entry_bytes_remaining;
+ *size = bytes_read;
+ *offset = iso9660->entry_sparse_offset;
+ iso9660->entry_sparse_offset += bytes_read;
+ iso9660->entry_bytes_remaining -= bytes_read;
+ iso9660->entry_bytes_unconsumed = bytes_read;
+ iso9660->current_position += bytes_read;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_cleanup(struct archive_read *a)
+{
+ struct iso9660 *iso9660;
+ int r = ARCHIVE_OK;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+ release_files(iso9660);
+ free(iso9660->read_ce_req.reqs);
+ archive_string_free(&iso9660->pathname);
+ archive_string_free(&iso9660->previous_pathname);
+ free(iso9660->pending_files.files);
+#ifdef HAVE_ZLIB_H
+ free(iso9660->entry_zisofs.uncompressed_buffer);
+ free(iso9660->entry_zisofs.block_pointers);
+ if (iso9660->entry_zisofs.stream_valid) {
+ if (inflateEnd(&iso9660->entry_zisofs.stream) != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up zlib decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ }
+#endif
+ free(iso9660->utf16be_path);
+ free(iso9660->utf16be_previous_path);
+ free(iso9660);
+ (a->format->data) = NULL;
+ return (r);
+}
+
+/*
+ * This routine parses a single ISO directory record, makes sense
+ * of any extensions, and stores the result in memory.
+ */
+static struct file_info *
+parse_file_info(struct archive_read *a, struct file_info *parent,
+ const unsigned char *isodirrec, size_t reclen)
+{
+ struct iso9660 *iso9660;
+ struct file_info *file, *filep;
+ size_t name_len;
+ const unsigned char *rr_start, *rr_end;
+ const unsigned char *p;
+ size_t dr_len = 0;
+ uint64_t fsize, offset;
+ int32_t location;
+ int flags;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ if (reclen != 0)
+ dr_len = (size_t)isodirrec[DR_length_offset];
+ /*
+ * Sanity check that reclen is not zero and dr_len is greater than
+ * reclen but at least 34
+ */
+ if (reclen == 0 || reclen < dr_len || dr_len < 34) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid length of directory record");
+ return (NULL);
+ }
+ name_len = (size_t)isodirrec[DR_name_len_offset];
+ location = archive_le32dec(isodirrec + DR_extent_offset);
+ fsize = toi(isodirrec + DR_size_offset, DR_size_size);
+ /* Sanity check that name_len doesn't exceed dr_len. */
+ if (dr_len - 33 < name_len || name_len == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid length of file identifier");
+ return (NULL);
+ }
+ /* Sanity check that location doesn't exceed volume block.
+ * Don't check lower limit of location; it's possibility
+ * the location has negative value when file type is symbolic
+ * link or file size is zero. As far as I know latest mkisofs
+ * do that.
+ */
+ if (location > 0 &&
+ (location + ((fsize + iso9660->logical_block_size -1)
+ / iso9660->logical_block_size))
+ > (uint32_t)iso9660->volume_block) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid location of extent of file");
+ return (NULL);
+ }
+ /* Sanity check that location doesn't have a negative value
+ * when the file is not empty. it's too large. */
+ if (fsize != 0 && location < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid location of extent of file");
+ return (NULL);
+ }
+
+ /* Sanity check that this entry does not create a cycle. */
+ offset = iso9660->logical_block_size * (uint64_t)location;
+ for (filep = parent; filep != NULL; filep = filep->parent) {
+ if (filep->offset == offset) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Directory structure contains loop");
+ return (NULL);
+ }
+ }
+
+ /* Create a new file entry and copy data from the ISO dir record. */
+ file = (struct file_info *)calloc(1, sizeof(*file));
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for file entry");
+ return (NULL);
+ }
+ file->parent = parent;
+ file->offset = offset;
+ file->size = fsize;
+ file->mtime = isodate7(isodirrec + DR_date_offset);
+ file->ctime = file->atime = file->mtime;
+ file->rede_files.first = NULL;
+ file->rede_files.last = &(file->rede_files.first);
+
+ p = isodirrec + DR_name_offset;
+ /* Rockridge extensions (if any) follow name. Compute this
+ * before fidgeting the name_len below. */
+ rr_start = p + name_len + (name_len & 1 ? 0 : 1);
+ rr_end = isodirrec + dr_len;
+
+ if (iso9660->seenJoliet) {
+ /* Joliet names are max 64 chars (128 bytes) according to spec,
+ * but genisoimage/mkisofs allows recording longer Joliet
+ * names which are 103 UCS2 characters(206 bytes) by their
+ * option '-joliet-long'.
+ */
+ if (name_len > 206)
+ name_len = 206;
+ name_len &= ~1;
+
+ /* trim trailing first version and dot from filename.
+ *
+ * Remember we were in UTF-16BE land!
+ * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both
+ * 16 bits big endian characters on Joliet.
+ *
+ * TODO: sanitize filename?
+ * Joliet allows any UCS-2 char except:
+ * *, /, :, ;, ? and \.
+ */
+ /* Chop off trailing ';1' from files. */
+ if (name_len > 4 && p[name_len-4] == 0 && p[name_len-3] == ';'
+ && p[name_len-2] == 0 && p[name_len-1] == '1')
+ name_len -= 4;
+#if 0 /* XXX: this somehow manages to strip of single-character file extensions, like '.c'. */
+ /* Chop off trailing '.' from filenames. */
+ if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.')
+ name_len -= 2;
+#endif
+ if ((file->utf16be_name = malloc(name_len)) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for file name");
+ goto fail;
+ }
+ memcpy(file->utf16be_name, p, name_len);
+ file->utf16be_bytes = name_len;
+ } else {
+ /* Chop off trailing ';1' from files. */
+ if (name_len > 2 && p[name_len - 2] == ';' &&
+ p[name_len - 1] == '1')
+ name_len -= 2;
+ /* Chop off trailing '.' from filenames. */
+ if (name_len > 1 && p[name_len - 1] == '.')
+ --name_len;
+
+ archive_strncpy(&file->name, (const char *)p, name_len);
+ }
+
+ flags = isodirrec[DR_flags_offset];
+ if (flags & 0x02)
+ file->mode = AE_IFDIR | 0700;
+ else
+ file->mode = AE_IFREG | 0400;
+ if (flags & 0x80)
+ file->multi_extent = 1;
+ else
+ file->multi_extent = 0;
+ /*
+ * Use a location for the file number, which is treated as an inode
+ * number to find out hardlink target. If Rockridge extensions is
+ * being used, the file number will be overwritten by FILE SERIAL
+ * NUMBER of RRIP "PX" extension.
+ * Note: Old mkisofs did not record that FILE SERIAL NUMBER
+ * in ISO images.
+ * Note2: xorriso set 0 to the location of a symlink file.
+ */
+ if (file->size == 0 && location >= 0) {
+ /* If file->size is zero, its location points wrong place,
+ * and so we should not use it for the file number.
+ * When the location has negative value, it can be used
+ * for the file number.
+ */
+ file->number = -1;
+ /* Do not appear before any directory entries. */
+ file->offset = -1;
+ } else
+ file->number = (int64_t)(uint32_t)location;
+
+ /* Rockridge extensions overwrite information from above. */
+ if (iso9660->opt_support_rockridge) {
+ if (parent == NULL && rr_end - rr_start >= 7) {
+ p = rr_start;
+ if (memcmp(p, "SP\x07\x01\xbe\xef", 6) == 0) {
+ /*
+ * SP extension stores the suspOffset
+ * (Number of bytes to skip between
+ * filename and SUSP records.)
+ * It is mandatory by the SUSP standard
+ * (IEEE 1281).
+ *
+ * It allows SUSP to coexist with
+ * non-SUSP uses of the System
+ * Use Area by placing non-SUSP data
+ * before SUSP data.
+ *
+ * SP extension must be in the root
+ * directory entry, disable all SUSP
+ * processing if not found.
+ */
+ iso9660->suspOffset = p[6];
+ iso9660->seenSUSP = 1;
+ rr_start += 7;
+ }
+ }
+ if (iso9660->seenSUSP) {
+ int r;
+
+ file->name_continues = 0;
+ file->symlink_continues = 0;
+ rr_start += iso9660->suspOffset;
+ r = parse_rockridge(a, file, rr_start, rr_end);
+ if (r != ARCHIVE_OK)
+ goto fail;
+ /*
+ * A file size of symbolic link files in ISO images
+ * made by makefs is not zero and its location is
+ * the same as those of next regular file. That is
+ * the same as hard like file and it causes unexpected
+ * error.
+ */
+ if (file->size > 0 &&
+ (file->mode & AE_IFMT) == AE_IFLNK) {
+ file->size = 0;
+ file->number = -1;
+ file->offset = -1;
+ }
+ } else
+ /* If there isn't SUSP, disable parsing
+ * rock ridge extensions. */
+ iso9660->opt_support_rockridge = 0;
+ }
+
+ file->nlinks = 1;/* Reset nlink. we'll calculate it later. */
+ /* Tell file's parent how many children that parent has. */
+ if (parent != NULL && (flags & 0x02))
+ parent->subdirs++;
+
+ if (iso9660->seenRockridge) {
+ if (parent != NULL && parent->parent == NULL &&
+ (flags & 0x02) && iso9660->rr_moved == NULL &&
+ file->name.s &&
+ (strcmp(file->name.s, "rr_moved") == 0 ||
+ strcmp(file->name.s, ".rr_moved") == 0)) {
+ iso9660->rr_moved = file;
+ file->rr_moved = 1;
+ file->rr_moved_has_re_only = 1;
+ file->re = 0;
+ parent->subdirs--;
+ } else if (file->re) {
+ /*
+ * Sanity check: file's parent is rr_moved.
+ */
+ if (parent == NULL || parent->rr_moved == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge RE");
+ goto fail;
+ }
+ /*
+ * Sanity check: file does not have "CL" extension.
+ */
+ if (file->cl_offset) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge RE and CL");
+ goto fail;
+ }
+ /*
+ * Sanity check: The file type must be a directory.
+ */
+ if ((flags & 0x02) == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge RE");
+ goto fail;
+ }
+ } else if (parent != NULL && parent->rr_moved)
+ file->rr_moved_has_re_only = 0;
+ else if (parent != NULL && (flags & 0x02) &&
+ (parent->re || parent->re_descendant))
+ file->re_descendant = 1;
+ if (file->cl_offset) {
+ struct file_info *r;
+
+ if (parent == NULL || parent->parent == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge CL");
+ goto fail;
+ }
+ /*
+ * Sanity check: The file type must be a regular file.
+ */
+ if ((flags & 0x02) != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge CL");
+ goto fail;
+ }
+ parent->subdirs++;
+ /* Overwrite an offset and a number of this "CL" entry
+ * to appear before other dirs. "+1" to those is to
+ * make sure to appear after "RE" entry which this
+ * "CL" entry should be connected with. */
+ file->offset = file->number = file->cl_offset + 1;
+
+ /*
+ * Sanity check: cl_offset does not point at its
+ * the parents or itself.
+ */
+ for (r = parent; r; r = r->parent) {
+ if (r->offset == file->cl_offset) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge CL");
+ goto fail;
+ }
+ }
+ if (file->cl_offset == file->offset ||
+ parent->rr_moved) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid Rockridge CL");
+ goto fail;
+ }
+ }
+ }
+
+#if DEBUG
+ /* DEBUGGING: Warn about attributes I don't yet fully support. */
+ if ((flags & ~0x02) != 0) {
+ fprintf(stderr, "\n ** Unrecognized flag: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ } else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) {
+ fprintf(stderr, "\n ** Unrecognized sequence number: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ } else if (*(isodirrec + DR_file_unit_size_offset) != 0) {
+ fprintf(stderr, "\n ** Unexpected file unit size: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ } else if (*(isodirrec + DR_interleave_offset) != 0) {
+ fprintf(stderr, "\n ** Unexpected interleave: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ } else if (*(isodirrec + DR_ext_attr_length_offset) != 0) {
+ fprintf(stderr, "\n ** Unexpected extended attribute length: ");
+ dump_isodirrec(stderr, isodirrec);
+ fprintf(stderr, "\n");
+ }
+#endif
+ register_file(iso9660, file);
+ return (file);
+fail:
+ archive_string_free(&file->name);
+ free(file);
+ return (NULL);
+}
+
+static int
+parse_rockridge(struct archive_read *a, struct file_info *file,
+ const unsigned char *p, const unsigned char *end)
+{
+ struct iso9660 *iso9660;
+ int entry_seen = 0;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+
+ while (p + 4 <= end /* Enough space for another entry. */
+ && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */
+ && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */
+ && p[2] >= 4 /* Sanity-check length. */
+ && p + p[2] <= end) { /* Sanity-check length. */
+ const unsigned char *data = p + 4;
+ int data_length = p[2] - 4;
+ int version = p[3];
+
+ switch(p[0]) {
+ case 'C':
+ if (p[1] == 'E') {
+ if (version == 1 && data_length == 24) {
+ /*
+ * CE extension comprises:
+ * 8 byte sector containing extension
+ * 8 byte offset w/in above sector
+ * 8 byte length of continuation
+ */
+ int32_t location =
+ archive_le32dec(data);
+ file->ce_offset =
+ archive_le32dec(data+8);
+ file->ce_size =
+ archive_le32dec(data+16);
+ if (register_CE(a, location, file)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ }
+ else if (p[1] == 'L') {
+ if (version == 1 && data_length == 8) {
+ file->cl_offset = (uint64_t)
+ iso9660->logical_block_size *
+ (uint64_t)archive_le32dec(data);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ break;
+ case 'N':
+ if (p[1] == 'M') {
+ if (version == 1) {
+ parse_rockridge_NM1(file,
+ data, data_length);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ break;
+ case 'P':
+ /*
+ * PD extension is padding;
+ * contents are always ignored.
+ *
+ * PL extension won't appear;
+ * contents are always ignored.
+ */
+ if (p[1] == 'N') {
+ if (version == 1 && data_length == 16) {
+ file->rdev = toi(data,4);
+ file->rdev <<= 32;
+ file->rdev |= toi(data + 8, 4);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ else if (p[1] == 'X') {
+ /*
+ * PX extension comprises:
+ * 8 bytes for mode,
+ * 8 bytes for nlinks,
+ * 8 bytes for uid,
+ * 8 bytes for gid,
+ * 8 bytes for inode.
+ */
+ if (version == 1) {
+ if (data_length >= 8)
+ file->mode
+ = toi(data, 4);
+ if (data_length >= 16)
+ file->nlinks
+ = toi(data + 8, 4);
+ if (data_length >= 24)
+ file->uid
+ = toi(data + 16, 4);
+ if (data_length >= 32)
+ file->gid
+ = toi(data + 24, 4);
+ if (data_length >= 40)
+ file->number
+ = toi(data + 32, 4);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ break;
+ case 'R':
+ if (p[1] == 'E' && version == 1) {
+ file->re = 1;
+ iso9660->seenRockridge = 1;
+ }
+ else if (p[1] == 'R' && version == 1) {
+ /*
+ * RR extension comprises:
+ * one byte flag value
+ * This extension is obsolete,
+ * so contents are always ignored.
+ */
+ }
+ break;
+ case 'S':
+ if (p[1] == 'L') {
+ if (version == 1) {
+ parse_rockridge_SL1(file,
+ data, data_length);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ else if (p[1] == 'T'
+ && data_length == 0 && version == 1) {
+ /*
+ * ST extension marks end of this
+ * block of SUSP entries.
+ *
+ * It allows SUSP to coexist with
+ * non-SUSP uses of the System
+ * Use Area by placing non-SUSP data
+ * after SUSP data.
+ */
+ iso9660->seenSUSP = 0;
+ iso9660->seenRockridge = 0;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'T':
+ if (p[1] == 'F') {
+ if (version == 1) {
+ parse_rockridge_TF1(file,
+ data, data_length);
+ iso9660->seenRockridge = 1;
+ }
+ }
+ break;
+ case 'Z':
+ if (p[1] == 'F') {
+ if (version == 1)
+ parse_rockridge_ZF1(file,
+ data, data_length);
+ }
+ break;
+ default:
+ break;
+ }
+
+ p += p[2];
+ entry_seen = 1;
+ }
+
+ if (entry_seen)
+ return (ARCHIVE_OK);
+ else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Tried to parse Rockridge extensions, but none found");
+ return (ARCHIVE_WARN);
+ }
+}
+
+static int
+register_CE(struct archive_read *a, int32_t location,
+ struct file_info *file)
+{
+ struct iso9660 *iso9660;
+ struct read_ce_queue *heap;
+ struct read_ce_req *p;
+ uint64_t offset, parent_offset;
+ int hole, parent;
+
+ iso9660 = (struct iso9660 *)(a->format->data);
+ offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size;
+ if (((file->mode & AE_IFMT) == AE_IFREG &&
+ offset >= file->offset) ||
+ offset < iso9660->current_position ||
+ (((uint64_t)file->ce_offset) + file->ce_size)
+ > (uint64_t)iso9660->logical_block_size ||
+ offset + file->ce_offset + file->ce_size
+ > iso9660->volume_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid parameter in SUSP \"CE\" extension");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Expand our CE list as necessary. */
+ heap = &(iso9660->read_ce_req);
+ if (heap->cnt >= heap->allocated) {
+ int new_size;
+
+ if (heap->allocated < 16)
+ new_size = 16;
+ else
+ new_size = heap->allocated * 2;
+ /* Overflow might keep us from growing the list. */
+ if (new_size <= heap->allocated) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ p = calloc(new_size, sizeof(p[0]));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (heap->reqs != NULL) {
+ memcpy(p, heap->reqs, heap->cnt * sizeof(*p));
+ free(heap->reqs);
+ }
+ heap->reqs = p;
+ heap->allocated = new_size;
+ }
+
+ /*
+ * Start with hole at end, walk it up tree to find insertion point.
+ */
+ hole = heap->cnt++;
+ while (hole > 0) {
+ parent = (hole - 1)/2;
+ parent_offset = heap->reqs[parent].offset;
+ if (offset >= parent_offset) {
+ heap->reqs[hole].offset = offset;
+ heap->reqs[hole].file = file;
+ return (ARCHIVE_OK);
+ }
+ /* Move parent into hole <==> move hole up tree. */
+ heap->reqs[hole] = heap->reqs[parent];
+ hole = parent;
+ }
+ heap->reqs[0].offset = offset;
+ heap->reqs[0].file = file;
+ return (ARCHIVE_OK);
+}
+
+static void
+next_CE(struct read_ce_queue *heap)
+{
+ uint64_t a_offset, b_offset, c_offset;
+ int a, b, c;
+ struct read_ce_req tmp;
+
+ if (heap->cnt < 1)
+ return;
+
+ /*
+ * Move the last item in the heap to the root of the tree
+ */
+ heap->reqs[0] = heap->reqs[--(heap->cnt)];
+
+ /*
+ * Rebalance the heap.
+ */
+ a = 0; /* Starting element and its offset */
+ a_offset = heap->reqs[a].offset;
+ for (;;) {
+ b = a + a + 1; /* First child */
+ if (b >= heap->cnt)
+ return;
+ b_offset = heap->reqs[b].offset;
+ c = b + 1; /* Use second child if it is smaller. */
+ if (c < heap->cnt) {
+ c_offset = heap->reqs[c].offset;
+ if (c_offset < b_offset) {
+ b = c;
+ b_offset = c_offset;
+ }
+ }
+ if (a_offset <= b_offset)
+ return;
+ tmp = heap->reqs[a];
+ heap->reqs[a] = heap->reqs[b];
+ heap->reqs[b] = tmp;
+ a = b;
+ }
+}
+
+
+static int
+read_CE(struct archive_read *a, struct iso9660 *iso9660)
+{
+ struct read_ce_queue *heap;
+ const unsigned char *b, *p, *end;
+ struct file_info *file;
+ size_t step;
+ int r;
+
+ /* Read data which RRIP "CE" extension points. */
+ heap = &(iso9660->read_ce_req);
+ step = iso9660->logical_block_size;
+ while (heap->cnt &&
+ heap->reqs[0].offset == iso9660->current_position) {
+ b = __archive_read_ahead(a, step, NULL);
+ if (b == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to read full block when scanning "
+ "ISO9660 directory list");
+ return (ARCHIVE_FATAL);
+ }
+ do {
+ file = heap->reqs[0].file;
+ if (file->ce_offset + file->ce_size > step) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed CE information");
+ return (ARCHIVE_FATAL);
+ }
+ p = b + file->ce_offset;
+ end = p + file->ce_size;
+ next_CE(heap);
+ r = parse_rockridge(a, file, p, end);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } while (heap->cnt &&
+ heap->reqs[0].offset == iso9660->current_position);
+ /* NOTE: Do not move this consume's code to front of
+ * do-while loop. Registration of nested CE extension
+ * might cause error because of current position. */
+ __archive_read_consume(a, step);
+ iso9660->current_position += step;
+ }
+ return (ARCHIVE_OK);
+}
+
+static void
+parse_rockridge_NM1(struct file_info *file,
+ const unsigned char *data, int data_length)
+{
+ if (!file->name_continues)
+ archive_string_empty(&file->name);
+ file->name_continues = 0;
+ if (data_length < 1)
+ return;
+ /*
+ * NM version 1 extension comprises:
+ * 1 byte flag, value is one of:
+ * = 0: remainder is name
+ * = 1: remainder is name, next NM entry continues name
+ * = 2: "."
+ * = 4: ".."
+ * = 32: Implementation specific
+ * All other values are reserved.
+ */
+ switch(data[0]) {
+ case 0:
+ if (data_length < 2)
+ return;
+ archive_strncat(&file->name,
+ (const char *)data + 1, data_length - 1);
+ break;
+ case 1:
+ if (data_length < 2)
+ return;
+ archive_strncat(&file->name,
+ (const char *)data + 1, data_length - 1);
+ file->name_continues = 1;
+ break;
+ case 2:
+ archive_strcat(&file->name, ".");
+ break;
+ case 4:
+ archive_strcat(&file->name, "..");
+ break;
+ default:
+ return;
+ }
+
+}
+
+static void
+parse_rockridge_TF1(struct file_info *file, const unsigned char *data,
+ int data_length)
+{
+ char flag;
+ /*
+ * TF extension comprises:
+ * one byte flag
+ * create time (optional)
+ * modify time (optional)
+ * access time (optional)
+ * attribute time (optional)
+ * Time format and presence of fields
+ * is controlled by flag bits.
+ */
+ if (data_length < 1)
+ return;
+ flag = data[0];
+ ++data;
+ --data_length;
+ if (flag & 0x80) {
+ /* Use 17-byte time format. */
+ if ((flag & 1) && data_length >= 17) {
+ /* Create time. */
+ file->birthtime_is_set = 1;
+ file->birthtime = isodate17(data);
+ data += 17;
+ data_length -= 17;
+ }
+ if ((flag & 2) && data_length >= 17) {
+ /* Modify time. */
+ file->mtime = isodate17(data);
+ data += 17;
+ data_length -= 17;
+ }
+ if ((flag & 4) && data_length >= 17) {
+ /* Access time. */
+ file->atime = isodate17(data);
+ data += 17;
+ data_length -= 17;
+ }
+ if ((flag & 8) && data_length >= 17) {
+ /* Attribute change time. */
+ file->ctime = isodate17(data);
+ }
+ } else {
+ /* Use 7-byte time format. */
+ if ((flag & 1) && data_length >= 7) {
+ /* Create time. */
+ file->birthtime_is_set = 1;
+ file->birthtime = isodate7(data);
+ data += 7;
+ data_length -= 7;
+ }
+ if ((flag & 2) && data_length >= 7) {
+ /* Modify time. */
+ file->mtime = isodate7(data);
+ data += 7;
+ data_length -= 7;
+ }
+ if ((flag & 4) && data_length >= 7) {
+ /* Access time. */
+ file->atime = isodate7(data);
+ data += 7;
+ data_length -= 7;
+ }
+ if ((flag & 8) && data_length >= 7) {
+ /* Attribute change time. */
+ file->ctime = isodate7(data);
+ }
+ }
+}
+
+static void
+parse_rockridge_SL1(struct file_info *file, const unsigned char *data,
+ int data_length)
+{
+ const char *separator = "";
+
+ if (!file->symlink_continues || file->symlink.length < 1)
+ archive_string_empty(&file->symlink);
+ file->symlink_continues = 0;
+
+ /*
+ * Defined flag values:
+ * 0: This is the last SL record for this symbolic link
+ * 1: this symbolic link field continues in next SL entry
+ * All other values are reserved.
+ */
+ if (data_length < 1)
+ return;
+ switch(*data) {
+ case 0:
+ break;
+ case 1:
+ file->symlink_continues = 1;
+ break;
+ default:
+ return;
+ }
+ ++data; /* Skip flag byte. */
+ --data_length;
+
+ /*
+ * SL extension body stores "components".
+ * Basically, this is a complicated way of storing
+ * a POSIX path. It also interferes with using
+ * symlinks for storing non-path data. <sigh>
+ *
+ * Each component is 2 bytes (flag and length)
+ * possibly followed by name data.
+ */
+ while (data_length >= 2) {
+ unsigned char flag = *data++;
+ unsigned char nlen = *data++;
+ data_length -= 2;
+
+ archive_strcat(&file->symlink, separator);
+ separator = "/";
+
+ switch(flag) {
+ case 0: /* Usual case, this is text. */
+ if (data_length < nlen)
+ return;
+ archive_strncat(&file->symlink,
+ (const char *)data, nlen);
+ break;
+ case 0x01: /* Text continues in next component. */
+ if (data_length < nlen)
+ return;
+ archive_strncat(&file->symlink,
+ (const char *)data, nlen);
+ separator = "";
+ break;
+ case 0x02: /* Current dir. */
+ archive_strcat(&file->symlink, ".");
+ break;
+ case 0x04: /* Parent dir. */
+ archive_strcat(&file->symlink, "..");
+ break;
+ case 0x08: /* Root of filesystem. */
+ archive_strcat(&file->symlink, "/");
+ separator = "";
+ break;
+ case 0x10: /* Undefined (historically "volume root" */
+ archive_string_empty(&file->symlink);
+ archive_strcat(&file->symlink, "ROOT");
+ break;
+ case 0x20: /* Undefined (historically "hostname") */
+ archive_strcat(&file->symlink, "hostname");
+ break;
+ default:
+ /* TODO: issue a warning ? */
+ return;
+ }
+ data += nlen;
+ data_length -= nlen;
+ }
+}
+
+static void
+parse_rockridge_ZF1(struct file_info *file, const unsigned char *data,
+ int data_length)
+{
+
+ if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {
+ /* paged zlib */
+ file->pz = 1;
+ file->pz_log2_bs = data[3];
+ file->pz_uncompressed_size = archive_le32dec(&data[4]);
+ }
+}
+
+static void
+register_file(struct iso9660 *iso9660, struct file_info *file)
+{
+
+ file->use_next = iso9660->use_files;
+ iso9660->use_files = file;
+}
+
+static void
+release_files(struct iso9660 *iso9660)
+{
+ struct content *con, *connext;
+ struct file_info *file;
+
+ file = iso9660->use_files;
+ while (file != NULL) {
+ struct file_info *next = file->use_next;
+
+ archive_string_free(&file->name);
+ archive_string_free(&file->symlink);
+ free(file->utf16be_name);
+ con = file->contents.first;
+ while (con != NULL) {
+ connext = con->next;
+ free(con);
+ con = connext;
+ }
+ free(file);
+ file = next;
+ }
+}
+
+static int
+next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
+ struct file_info **pfile)
+{
+ struct file_info *file;
+ int r;
+
+ r = next_cache_entry(a, iso9660, pfile);
+ if (r != ARCHIVE_OK)
+ return (r);
+ file = *pfile;
+
+ /* Don't waste time seeking for zero-length bodies. */
+ if (file->size == 0)
+ file->offset = iso9660->current_position;
+
+ /* flush any remaining bytes from the last round to ensure
+ * we're positioned */
+ if (iso9660->entry_bytes_unconsumed) {
+ __archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+ iso9660->entry_bytes_unconsumed = 0;
+ }
+
+ /* Seek forward to the start of the entry. */
+ if (iso9660->current_position < file->offset) {
+ int64_t step;
+
+ step = file->offset - iso9660->current_position;
+ step = __archive_read_consume(a, step);
+ if (step < 0)
+ return ((int)step);
+ iso9660->current_position = file->offset;
+ }
+
+ /* We found body of file; handle it now. */
+ return (ARCHIVE_OK);
+}
+
+static int
+next_cache_entry(struct archive_read *a, struct iso9660 *iso9660,
+ struct file_info **pfile)
+{
+ struct file_info *file;
+ struct {
+ struct file_info *first;
+ struct file_info **last;
+ } empty_files;
+ int64_t number;
+ int count;
+
+ file = cache_get_entry(iso9660);
+ if (file != NULL) {
+ *pfile = file;
+ return (ARCHIVE_OK);
+ }
+
+ for (;;) {
+ struct file_info *re, *d;
+
+ *pfile = file = next_entry(iso9660);
+ if (file == NULL) {
+ /*
+ * If directory entries all which are descendant of
+ * rr_moved are still remaining, expose their.
+ */
+ if (iso9660->re_files.first != NULL &&
+ iso9660->rr_moved != NULL &&
+ iso9660->rr_moved->rr_moved_has_re_only)
+ /* Expose "rr_moved" entry. */
+ cache_add_entry(iso9660, iso9660->rr_moved);
+ while ((re = re_get_entry(iso9660)) != NULL) {
+ /* Expose its descendant dirs. */
+ while ((d = rede_get_entry(re)) != NULL)
+ cache_add_entry(iso9660, d);
+ }
+ if (iso9660->cache_files.first != NULL)
+ return (next_cache_entry(a, iso9660, pfile));
+ return (ARCHIVE_EOF);
+ }
+
+ if (file->cl_offset) {
+ struct file_info *first_re = NULL;
+ int nexted_re = 0;
+
+ /*
+ * Find "RE" dir for the current file, which
+ * has "CL" flag.
+ */
+ while ((re = re_get_entry(iso9660))
+ != first_re) {
+ if (first_re == NULL)
+ first_re = re;
+ if (re->offset == file->cl_offset) {
+ re->parent->subdirs--;
+ re->parent = file->parent;
+ re->re = 0;
+ if (re->parent->re_descendant) {
+ nexted_re = 1;
+ re->re_descendant = 1;
+ if (rede_add_entry(re) < 0)
+ goto fatal_rr;
+ /* Move a list of descendants
+ * to a new ancestor. */
+ while ((d = rede_get_entry(
+ re)) != NULL)
+ if (rede_add_entry(d)
+ < 0)
+ goto fatal_rr;
+ break;
+ }
+ /* Replace the current file
+ * with "RE" dir */
+ *pfile = file = re;
+ /* Expose its descendant */
+ while ((d = rede_get_entry(
+ file)) != NULL)
+ cache_add_entry(
+ iso9660, d);
+ break;
+ } else
+ re_add_entry(iso9660, re);
+ }
+ if (nexted_re) {
+ /*
+ * Do not expose this at this time
+ * because we have not gotten its full-path
+ * name yet.
+ */
+ continue;
+ }
+ } else if ((file->mode & AE_IFMT) == AE_IFDIR) {
+ int r;
+
+ /* Read file entries in this dir. */
+ r = read_children(a, file);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /*
+ * Handle a special dir of Rockridge extensions,
+ * "rr_moved".
+ */
+ if (file->rr_moved) {
+ /*
+ * If this has only the subdirectories which
+ * have "RE" flags, do not expose at this time.
+ */
+ if (file->rr_moved_has_re_only)
+ continue;
+ /* Otherwise expose "rr_moved" entry. */
+ } else if (file->re) {
+ /*
+ * Do not expose this at this time
+ * because we have not gotten its full-path
+ * name yet.
+ */
+ re_add_entry(iso9660, file);
+ continue;
+ } else if (file->re_descendant) {
+ /*
+ * If the top level "RE" entry of this entry
+ * is not exposed, we, accordingly, should not
+ * expose this entry at this time because
+ * we cannot make its proper full-path name.
+ */
+ if (rede_add_entry(file) == 0)
+ continue;
+ /* Otherwise we can expose this entry because
+ * it seems its top level "RE" has already been
+ * exposed. */
+ }
+ }
+ break;
+ }
+
+ if ((file->mode & AE_IFMT) != AE_IFREG || file->number == -1)
+ return (ARCHIVE_OK);
+
+ count = 0;
+ number = file->number;
+ iso9660->cache_files.first = NULL;
+ iso9660->cache_files.last = &(iso9660->cache_files.first);
+ empty_files.first = NULL;
+ empty_files.last = &empty_files.first;
+ /* Collect files which has the same file serial number.
+ * Peek pending_files so that file which number is different
+ * is not put back. */
+ while (iso9660->pending_files.used > 0 &&
+ (iso9660->pending_files.files[0]->number == -1 ||
+ iso9660->pending_files.files[0]->number == number)) {
+ if (file->number == -1) {
+ /* This file has the same offset
+ * but it's wrong offset which empty files
+ * and symlink files have.
+ * NOTE: This wrong offset was recorded by
+ * old mkisofs utility. If ISO images is
+ * created by latest mkisofs, this does not
+ * happen.
+ */
+ file->next = NULL;
+ *empty_files.last = file;
+ empty_files.last = &(file->next);
+ } else {
+ count++;
+ cache_add_entry(iso9660, file);
+ }
+ file = next_entry(iso9660);
+ }
+
+ if (count == 0) {
+ *pfile = file;
+ return ((file == NULL)?ARCHIVE_EOF:ARCHIVE_OK);
+ }
+ if (file->number == -1) {
+ file->next = NULL;
+ *empty_files.last = file;
+ empty_files.last = &(file->next);
+ } else {
+ count++;
+ cache_add_entry(iso9660, file);
+ }
+
+ if (count > 1) {
+ /* The count is the same as number of hardlink,
+ * so much so that each nlinks of files in cache_file
+ * is overwritten by value of the count.
+ */
+ for (file = iso9660->cache_files.first;
+ file != NULL; file = file->next)
+ file->nlinks = count;
+ }
+ /* If there are empty files, that files are added
+ * to the tail of the cache_files. */
+ if (empty_files.first != NULL) {
+ *iso9660->cache_files.last = empty_files.first;
+ iso9660->cache_files.last = empty_files.last;
+ }
+ *pfile = cache_get_entry(iso9660);
+ return ((*pfile == NULL)?ARCHIVE_EOF:ARCHIVE_OK);
+
+fatal_rr:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of "
+ "Rockridge extensions: current position = %jd, CL offset = %jd",
+ (intmax_t)iso9660->current_position, (intmax_t)file->cl_offset);
+ return (ARCHIVE_FATAL);
+}
+
+static inline void
+re_add_entry(struct iso9660 *iso9660, struct file_info *file)
+{
+ file->re_next = NULL;
+ *iso9660->re_files.last = file;
+ iso9660->re_files.last = &(file->re_next);
+}
+
+static inline struct file_info *
+re_get_entry(struct iso9660 *iso9660)
+{
+ struct file_info *file;
+
+ if ((file = iso9660->re_files.first) != NULL) {
+ iso9660->re_files.first = file->re_next;
+ if (iso9660->re_files.first == NULL)
+ iso9660->re_files.last =
+ &(iso9660->re_files.first);
+ }
+ return (file);
+}
+
+static inline int
+rede_add_entry(struct file_info *file)
+{
+ struct file_info *re;
+
+ /*
+ * Find "RE" entry.
+ */
+ re = file->parent;
+ while (re != NULL && !re->re)
+ re = re->parent;
+ if (re == NULL)
+ return (-1);
+
+ file->re_next = NULL;
+ *re->rede_files.last = file;
+ re->rede_files.last = &(file->re_next);
+ return (0);
+}
+
+static inline struct file_info *
+rede_get_entry(struct file_info *re)
+{
+ struct file_info *file;
+
+ if ((file = re->rede_files.first) != NULL) {
+ re->rede_files.first = file->re_next;
+ if (re->rede_files.first == NULL)
+ re->rede_files.last =
+ &(re->rede_files.first);
+ }
+ return (file);
+}
+
+static inline void
+cache_add_entry(struct iso9660 *iso9660, struct file_info *file)
+{
+ file->next = NULL;
+ *iso9660->cache_files.last = file;
+ iso9660->cache_files.last = &(file->next);
+}
+
+static inline struct file_info *
+cache_get_entry(struct iso9660 *iso9660)
+{
+ struct file_info *file;
+
+ if ((file = iso9660->cache_files.first) != NULL) {
+ iso9660->cache_files.first = file->next;
+ if (iso9660->cache_files.first == NULL)
+ iso9660->cache_files.last =
+ &(iso9660->cache_files.first);
+ }
+ return (file);
+}
+
+static int
+heap_add_entry(struct archive_read *a, struct heap_queue *heap,
+ struct file_info *file, uint64_t key)
+{
+ uint64_t file_key, parent_key;
+ int hole, parent;
+
+ /* Expand our pending files list as necessary. */
+ if (heap->used >= heap->allocated) {
+ struct file_info **new_pending_files;
+ int new_size = heap->allocated * 2;
+
+ if (heap->allocated < 1024)
+ new_size = 1024;
+ /* Overflow might keep us from growing the list. */
+ if (new_size <= heap->allocated) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ new_pending_files = (struct file_info **)
+ malloc(new_size * sizeof(new_pending_files[0]));
+ if (new_pending_files == NULL) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (heap->allocated)
+ memcpy(new_pending_files, heap->files,
+ heap->allocated * sizeof(new_pending_files[0]));
+ free(heap->files);
+ heap->files = new_pending_files;
+ heap->allocated = new_size;
+ }
+
+ file_key = file->key = key;
+
+ /*
+ * Start with hole at end, walk it up tree to find insertion point.
+ */
+ hole = heap->used++;
+ while (hole > 0) {
+ parent = (hole - 1)/2;
+ parent_key = heap->files[parent]->key;
+ if (file_key >= parent_key) {
+ heap->files[hole] = file;
+ return (ARCHIVE_OK);
+ }
+ /* Move parent into hole <==> move hole up tree. */
+ heap->files[hole] = heap->files[parent];
+ hole = parent;
+ }
+ heap->files[0] = file;
+
+ return (ARCHIVE_OK);
+}
+
+static struct file_info *
+heap_get_entry(struct heap_queue *heap)
+{
+ uint64_t a_key, b_key, c_key;
+ int a, b, c;
+ struct file_info *r, *tmp;
+
+ if (heap->used < 1)
+ return (NULL);
+
+ /*
+ * The first file in the list is the earliest; we'll return this.
+ */
+ r = heap->files[0];
+
+ /*
+ * Move the last item in the heap to the root of the tree
+ */
+ heap->files[0] = heap->files[--(heap->used)];
+
+ /*
+ * Rebalance the heap.
+ */
+ a = 0; /* Starting element and its heap key */
+ a_key = heap->files[a]->key;
+ for (;;) {
+ b = a + a + 1; /* First child */
+ if (b >= heap->used)
+ return (r);
+ b_key = heap->files[b]->key;
+ c = b + 1; /* Use second child if it is smaller. */
+ if (c < heap->used) {
+ c_key = heap->files[c]->key;
+ if (c_key < b_key) {
+ b = c;
+ b_key = c_key;
+ }
+ }
+ if (a_key <= b_key)
+ return (r);
+ tmp = heap->files[a];
+ heap->files[a] = heap->files[b];
+ heap->files[b] = tmp;
+ a = b;
+ }
+}
+
+static unsigned int
+toi(const void *p, int n)
+{
+ const unsigned char *v = (const unsigned char *)p;
+ if (n > 1)
+ return v[0] + 256 * toi(v + 1, n - 1);
+ if (n == 1)
+ return v[0];
+ return (0);
+}
+
+static time_t
+isodate7(const unsigned char *v)
+{
+ struct tm tm;
+ int offset;
+ time_t t;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = v[0];
+ tm.tm_mon = v[1] - 1;
+ tm.tm_mday = v[2];
+ tm.tm_hour = v[3];
+ tm.tm_min = v[4];
+ tm.tm_sec = v[5];
+ /* v[6] is the signed timezone offset, in 1/4-hour increments. */
+ offset = ((const signed char *)v)[6];
+ if (offset > -48 && offset < 52) {
+ tm.tm_hour -= offset / 4;
+ tm.tm_min -= (offset % 4) * 15;
+ }
+ t = time_from_tm(&tm);
+ if (t == (time_t)-1)
+ return ((time_t)0);
+ return (t);
+}
+
+static time_t
+isodate17(const unsigned char *v)
+{
+ struct tm tm;
+ int offset;
+ time_t t;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100
+ + (v[2] - '0') * 10 + (v[3] - '0')
+ - 1900;
+ tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0');
+ tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0');
+ tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0');
+ tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0');
+ tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0');
+ /* v[16] is the signed timezone offset, in 1/4-hour increments. */
+ offset = ((const signed char *)v)[16];
+ if (offset > -48 && offset < 52) {
+ tm.tm_hour -= offset / 4;
+ tm.tm_min -= (offset % 4) * 15;
+ }
+ t = time_from_tm(&tm);
+ if (t == (time_t)-1)
+ return ((time_t)0);
+ return (t);
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
+ /* Use platform timegm() if available. */
+ return (timegm(t));
+#else
+ /* Else use direct calculation using POSIX assumptions. */
+ /* First, fix up tm_yday based on the year/month/day. */
+ if (mktime(t) == (time_t)-1)
+ return ((time_t)-1);
+ /* Then we can compute timegm() from first principles. */
+ return (t->tm_sec
+ + t->tm_min * 60
+ + t->tm_hour * 3600
+ + t->tm_yday * 86400
+ + (t->tm_year - 70) * 31536000
+ + ((t->tm_year - 69) / 4) * 86400
+ - ((t->tm_year - 1) / 100) * 86400
+ + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static const char *
+build_pathname(struct archive_string *as, struct file_info *file, int depth)
+{
+ // Plain ISO9660 only allows 8 dir levels; if we get
+ // to 1000, then something is very, very wrong.
+ if (depth > 1000) {
+ return NULL;
+ }
+ if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) {
+ if (build_pathname(as, file->parent, depth + 1) == NULL) {
+ return NULL;
+ }
+ archive_strcat(as, "/");
+ }
+ if (archive_strlen(&file->name) == 0)
+ archive_strcat(as, ".");
+ else
+ archive_string_concat(as, &file->name);
+ return (as->s);
+}
+
+static int
+build_pathname_utf16be(unsigned char *p, size_t max, size_t *len,
+ struct file_info *file)
+{
+ if (file->parent != NULL && file->parent->utf16be_bytes > 0) {
+ if (build_pathname_utf16be(p, max, len, file->parent) != 0)
+ return (-1);
+ p[*len] = 0;
+ p[*len + 1] = '/';
+ *len += 2;
+ }
+ if (file->utf16be_bytes == 0) {
+ if (*len + 2 > max)
+ return (-1);/* Path is too long! */
+ p[*len] = 0;
+ p[*len + 1] = '.';
+ *len += 2;
+ } else {
+ if (*len + file->utf16be_bytes > max)
+ return (-1);/* Path is too long! */
+ memcpy(p + *len, file->utf16be_name, file->utf16be_bytes);
+ *len += file->utf16be_bytes;
+ }
+ return (0);
+}
+
+#if DEBUG
+static void
+dump_isodirrec(FILE *out, const unsigned char *isodirrec)
+{
+ fprintf(out, " l %d,",
+ toi(isodirrec + DR_length_offset, DR_length_size));
+ fprintf(out, " a %d,",
+ toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size));
+ fprintf(out, " ext 0x%x,",
+ toi(isodirrec + DR_extent_offset, DR_extent_size));
+ fprintf(out, " s %d,",
+ toi(isodirrec + DR_size_offset, DR_extent_size));
+ fprintf(out, " f 0x%x,",
+ toi(isodirrec + DR_flags_offset, DR_flags_size));
+ fprintf(out, " u %d,",
+ toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size));
+ fprintf(out, " ilv %d,",
+ toi(isodirrec + DR_interleave_offset, DR_interleave_size));
+ fprintf(out, " seq %d,",
+ toi(isodirrec + DR_volume_sequence_number_offset,
+ DR_volume_sequence_number_size));
+ fprintf(out, " nl %d:",
+ toi(isodirrec + DR_name_len_offset, DR_name_len_size));
+ fprintf(out, " `%.*s'",
+ toi(isodirrec + DR_name_len_offset, DR_name_len_size),
+ isodirrec + DR_name_offset);
+}
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_lha.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_lha.c
new file mode 100644
index 0000000000..1c64b2900b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_lha.c
@@ -0,0 +1,2919 @@
+/*-
+ * Copyright (c) 2008-2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+
+#define MAXMATCH 256 /* Maximum match length. */
+#define MINMATCH 3 /* Minimum match length. */
+/*
+ * Literal table format:
+ * +0 +256 +510
+ * +---------------+-------------------------+
+ * | literal code | match length |
+ * | 0 ... 255 | MINMATCH ... MAXMATCH |
+ * +---------------+-------------------------+
+ * <--- LT_BITLEN_SIZE --->
+ */
+/* Literal table size. */
+#define LT_BITLEN_SIZE (UCHAR_MAX + 1 + MAXMATCH - MINMATCH + 1)
+/* Position table size.
+ * Note: this used for both position table and pre literal table.*/
+#define PT_BITLEN_SIZE (3 + 16)
+
+struct lzh_dec {
+ /* Decoding status. */
+ int state;
+
+ /*
+ * Window to see last 8Ki(lh5),32Ki(lh6),64Ki(lh7) bytes of decoded
+ * data.
+ */
+ int w_size;
+ int w_mask;
+ /* Window buffer, which is a loop buffer. */
+ unsigned char *w_buff;
+ /* The insert position to the window. */
+ int w_pos;
+ /* The position where we can copy decoded code from the window. */
+ int copy_pos;
+ /* The length how many bytes we can copy decoded code from
+ * the window. */
+ int copy_len;
+
+ /*
+ * Bit stream reader.
+ */
+ struct lzh_br {
+#define CACHE_TYPE uint64_t
+#define CACHE_BITS (8 * sizeof(CACHE_TYPE))
+ /* Cache buffer. */
+ CACHE_TYPE cache_buffer;
+ /* Indicates how many bits avail in cache_buffer. */
+ int cache_avail;
+ } br;
+
+ /*
+ * Huffman coding.
+ */
+ struct huffman {
+ int len_size;
+ int len_avail;
+ int len_bits;
+ int freq[17];
+ unsigned char *bitlen;
+
+ /*
+ * Use a index table. It's faster than searching a huffman
+ * coding tree, which is a binary tree. But a use of a large
+ * index table causes L1 cache read miss many times.
+ */
+#define HTBL_BITS 10
+ int max_bits;
+ int shift_bits;
+ int tbl_bits;
+ int tree_used;
+ int tree_avail;
+ /* Direct access table. */
+ uint16_t *tbl;
+ /* Binary tree table for extra bits over the direct access. */
+ struct htree_t {
+ uint16_t left;
+ uint16_t right;
+ } *tree;
+ } lt, pt;
+
+ int blocks_avail;
+ int pos_pt_len_size;
+ int pos_pt_len_bits;
+ int literal_pt_len_size;
+ int literal_pt_len_bits;
+ int reading_position;
+ int loop;
+ int error;
+};
+
+struct lzh_stream {
+ const unsigned char *next_in;
+ int avail_in;
+ int64_t total_in;
+ const unsigned char *ref_ptr;
+ int avail_out;
+ int64_t total_out;
+ struct lzh_dec *ds;
+};
+
+struct lha {
+ /* entry_bytes_remaining is the number of bytes we expect. */
+ int64_t entry_offset;
+ int64_t entry_bytes_remaining;
+ int64_t entry_unconsumed;
+ uint16_t entry_crc_calculated;
+
+ size_t header_size; /* header size */
+ unsigned char level; /* header level */
+ char method[3]; /* compress type */
+ int64_t compsize; /* compressed data size */
+ int64_t origsize; /* original file size */
+ int setflag;
+#define BIRTHTIME_IS_SET 1
+#define ATIME_IS_SET 2
+#define UNIX_MODE_IS_SET 4
+#define CRC_IS_SET 8
+ time_t birthtime;
+ long birthtime_tv_nsec;
+ time_t mtime;
+ long mtime_tv_nsec;
+ time_t atime;
+ long atime_tv_nsec;
+ mode_t mode;
+ int64_t uid;
+ int64_t gid;
+ struct archive_string uname;
+ struct archive_string gname;
+ uint16_t header_crc;
+ uint16_t crc;
+ /* dirname and filename could be in different codepages */
+ struct archive_string_conv *sconv_dir;
+ struct archive_string_conv *sconv_fname;
+ struct archive_string_conv *opt_sconv;
+
+ struct archive_string dirname;
+ struct archive_string filename;
+ struct archive_wstring ws;
+
+ unsigned char dos_attr;
+
+ /* Flag to mark progress that an archive was read their first header.*/
+ char found_first_header;
+ /* Flag to mark that indicates an empty directory. */
+ char directory;
+
+ /* Flags to mark progress of decompression. */
+ char decompress_init;
+ char end_of_entry;
+ char end_of_entry_cleanup;
+ char entry_is_compressed;
+
+ char format_name[64];
+
+ struct lzh_stream strm;
+};
+
+/*
+ * LHA header common member offset.
+ */
+#define H_METHOD_OFFSET 2 /* Compress type. */
+#define H_ATTR_OFFSET 19 /* DOS attribute. */
+#define H_LEVEL_OFFSET 20 /* Header Level. */
+#define H_SIZE 22 /* Minimum header size. */
+
+static int archive_read_format_lha_bid(struct archive_read *, int);
+static int archive_read_format_lha_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_lha_read_header(struct archive_read *,
+ struct archive_entry *);
+static int archive_read_format_lha_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_lha_read_data_skip(struct archive_read *);
+static int archive_read_format_lha_cleanup(struct archive_read *);
+
+static void lha_replace_path_separator(struct lha *,
+ struct archive_entry *);
+static int lha_read_file_header_0(struct archive_read *, struct lha *);
+static int lha_read_file_header_1(struct archive_read *, struct lha *);
+static int lha_read_file_header_2(struct archive_read *, struct lha *);
+static int lha_read_file_header_3(struct archive_read *, struct lha *);
+static int lha_read_file_extended_header(struct archive_read *,
+ struct lha *, uint16_t *, int, size_t, size_t *);
+static size_t lha_check_header_format(const void *);
+static int lha_skip_sfx(struct archive_read *);
+static time_t lha_dos_time(const unsigned char *);
+static time_t lha_win_time(uint64_t, long *);
+static unsigned char lha_calcsum(unsigned char, const void *,
+ int, size_t);
+static int lha_parse_linkname(struct archive_wstring *,
+ struct archive_wstring *);
+static int lha_read_data_none(struct archive_read *, const void **,
+ size_t *, int64_t *);
+static int lha_read_data_lzh(struct archive_read *, const void **,
+ size_t *, int64_t *);
+static void lha_crc16_init(void);
+static uint16_t lha_crc16(uint16_t, const void *, size_t);
+static int lzh_decode_init(struct lzh_stream *, const char *);
+static void lzh_decode_free(struct lzh_stream *);
+static int lzh_decode(struct lzh_stream *, int);
+static int lzh_br_fillup(struct lzh_stream *, struct lzh_br *);
+static int lzh_huffman_init(struct huffman *, size_t, int);
+static void lzh_huffman_free(struct huffman *);
+static int lzh_read_pt_bitlen(struct lzh_stream *, int start, int end);
+static int lzh_make_fake_table(struct huffman *, uint16_t);
+static int lzh_make_huffman_table(struct huffman *);
+static inline int lzh_decode_huffman(struct huffman *, unsigned);
+static int lzh_decode_huffman_tree(struct huffman *, unsigned, int);
+
+
+int
+archive_read_support_format_lha(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct lha *lha;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_lha");
+
+ lha = (struct lha *)calloc(1, sizeof(*lha));
+ if (lha == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate lha data");
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_init(&lha->ws);
+
+ r = __archive_read_register_format(a,
+ lha,
+ "lha",
+ archive_read_format_lha_bid,
+ archive_read_format_lha_options,
+ archive_read_format_lha_read_header,
+ archive_read_format_lha_read_data,
+ archive_read_format_lha_read_data_skip,
+ NULL,
+ archive_read_format_lha_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK)
+ free(lha);
+ return (ARCHIVE_OK);
+}
+
+static size_t
+lha_check_header_format(const void *h)
+{
+ const unsigned char *p = h;
+ size_t next_skip_bytes;
+
+ switch (p[H_METHOD_OFFSET+3]) {
+ /*
+ * "-lh0-" ... "-lh7-" "-lhd-"
+ * "-lzs-" "-lz5-"
+ */
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case 'd':
+ case 's':
+ next_skip_bytes = 4;
+
+ /* b0 == 0 means the end of an LHa archive file. */
+ if (p[0] == 0)
+ break;
+ if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l'
+ || p[H_METHOD_OFFSET+4] != '-')
+ break;
+
+ if (p[H_METHOD_OFFSET+2] == 'h') {
+ /* "-lh?-" */
+ if (p[H_METHOD_OFFSET+3] == 's')
+ break;
+ if (p[H_LEVEL_OFFSET] == 0)
+ return (0);
+ if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20)
+ return (0);
+ }
+ if (p[H_METHOD_OFFSET+2] == 'z') {
+ /* LArc extensions: -lzs-,-lz4- and -lz5- */
+ if (p[H_LEVEL_OFFSET] != 0)
+ break;
+ if (p[H_METHOD_OFFSET+3] == 's'
+ || p[H_METHOD_OFFSET+3] == '4'
+ || p[H_METHOD_OFFSET+3] == '5')
+ return (0);
+ }
+ break;
+ case 'h': next_skip_bytes = 1; break;
+ case 'z': next_skip_bytes = 1; break;
+ case 'l': next_skip_bytes = 2; break;
+ case '-': next_skip_bytes = 3; break;
+ default : next_skip_bytes = 4; break;
+ }
+
+ return (next_skip_bytes);
+}
+
+static int
+archive_read_format_lha_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+ const void *buff;
+ ssize_t bytes_avail, offset, window;
+ size_t next;
+
+ /* If there's already a better bid than we can ever
+ make, don't bother testing. */
+ if (best_bid > 30)
+ return (-1);
+
+ if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL)
+ return (-1);
+
+ if (lha_check_header_format(p) == 0)
+ return (30);
+
+ if (p[0] == 'M' && p[1] == 'Z') {
+ /* PE file */
+ offset = 0;
+ window = 4096;
+ while (offset < (1024 * 20)) {
+ buff = __archive_read_ahead(a, offset + window,
+ &bytes_avail);
+ if (buff == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < (H_SIZE + 3))
+ return (0);
+ continue;
+ }
+ p = (const char *)buff + offset;
+ while (p + H_SIZE < (const char *)buff + bytes_avail) {
+ if ((next = lha_check_header_format(p)) == 0)
+ return (30);
+ p += next;
+ }
+ offset = p - (const char *)buff;
+ }
+ }
+ return (0);
+}
+
+static int
+archive_read_format_lha_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct lha *lha;
+ int ret = ARCHIVE_FAILED;
+
+ lha = (struct lha *)(a->format->data);
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "lha: hdrcharset option needs a character-set name");
+ else {
+ lha->opt_sconv =
+ archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (lha->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+lha_skip_sfx(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t next, skip;
+ ssize_t bytes, window;
+
+ window = 4096;
+ for (;;) {
+ h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < (H_SIZE + 3))
+ goto fatal;
+ continue;
+ }
+ if (bytes < H_SIZE)
+ goto fatal;
+ p = h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the lha header.
+ */
+ while (p + H_SIZE < q) {
+ if ((next = lha_check_header_format(p)) == 0) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ return (ARCHIVE_OK);
+ }
+ p += next;
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ }
+fatal:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+truncated_error(struct archive_read *a)
+{
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_lha_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct archive_wstring linkname;
+ struct archive_wstring pathname;
+ struct lha *lha;
+ const unsigned char *p;
+ const char *signature;
+ int err;
+ struct archive_mstring conv_buffer;
+ const wchar_t *conv_buffer_p;
+
+ lha_crc16_init();
+
+ a->archive.archive_format = ARCHIVE_FORMAT_LHA;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "lha";
+
+ lha = (struct lha *)(a->format->data);
+ lha->decompress_init = 0;
+ lha->end_of_entry = 0;
+ lha->end_of_entry_cleanup = 0;
+ lha->entry_unconsumed = 0;
+
+ if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) {
+ /*
+ * LHa archiver added 0 to the tail of its archive file as
+ * the mark of the end of the archive.
+ */
+ signature = __archive_read_ahead(a, sizeof(signature[0]), NULL);
+ if (signature == NULL || signature[0] == 0)
+ return (ARCHIVE_EOF);
+ return (truncated_error(a));
+ }
+
+ signature = (const char *)p;
+ if (lha->found_first_header == 0 &&
+ signature[0] == 'M' && signature[1] == 'Z') {
+ /* This is an executable? Must be self-extracting... */
+ err = lha_skip_sfx(a);
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+ if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL)
+ return (truncated_error(a));
+ signature = (const char *)p;
+ }
+ /* signature[0] == 0 means the end of an LHa archive file. */
+ if (signature[0] == 0)
+ return (ARCHIVE_EOF);
+
+ /*
+ * Check the header format and method type.
+ */
+ if (lha_check_header_format(p) != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad LHa file");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* We've found the first header. */
+ lha->found_first_header = 1;
+ /* Set a default value and common data */
+ lha->header_size = 0;
+ lha->level = p[H_LEVEL_OFFSET];
+ lha->method[0] = p[H_METHOD_OFFSET+1];
+ lha->method[1] = p[H_METHOD_OFFSET+2];
+ lha->method[2] = p[H_METHOD_OFFSET+3];
+ if (memcmp(lha->method, "lhd", 3) == 0)
+ lha->directory = 1;
+ else
+ lha->directory = 0;
+ if (memcmp(lha->method, "lh0", 3) == 0 ||
+ memcmp(lha->method, "lz4", 3) == 0)
+ lha->entry_is_compressed = 0;
+ else
+ lha->entry_is_compressed = 1;
+
+ lha->compsize = 0;
+ lha->origsize = 0;
+ lha->setflag = 0;
+ lha->birthtime = 0;
+ lha->birthtime_tv_nsec = 0;
+ lha->mtime = 0;
+ lha->mtime_tv_nsec = 0;
+ lha->atime = 0;
+ lha->atime_tv_nsec = 0;
+ lha->mode = (lha->directory)? 0777 : 0666;
+ lha->uid = 0;
+ lha->gid = 0;
+ archive_string_empty(&lha->dirname);
+ archive_string_empty(&lha->filename);
+ lha->dos_attr = 0;
+ if (lha->opt_sconv != NULL) {
+ lha->sconv_dir = lha->opt_sconv;
+ lha->sconv_fname = lha->opt_sconv;
+ } else {
+ lha->sconv_dir = NULL;
+ lha->sconv_fname = NULL;
+ }
+
+ switch (p[H_LEVEL_OFFSET]) {
+ case 0:
+ err = lha_read_file_header_0(a, lha);
+ break;
+ case 1:
+ err = lha_read_file_header_1(a, lha);
+ break;
+ case 2:
+ err = lha_read_file_header_2(a, lha);
+ break;
+ case 3:
+ err = lha_read_file_header_3(a, lha);
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported LHa header level %d", p[H_LEVEL_OFFSET]);
+ err = ARCHIVE_FATAL;
+ break;
+ }
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+
+ if (!lha->directory && archive_strlen(&lha->filename) == 0)
+ /* The filename has not been set */
+ return (truncated_error(a));
+
+ /*
+ * Make a pathname from a dirname and a filename, after converting to Unicode.
+ * This is because codepages might differ between dirname and filename.
+ */
+ archive_string_init(&pathname);
+ archive_string_init(&linkname);
+ archive_string_init(&conv_buffer.aes_mbs);
+ archive_string_init(&conv_buffer.aes_mbs_in_locale);
+ archive_string_init(&conv_buffer.aes_utf8);
+ archive_string_init(&conv_buffer.aes_wcs);
+ if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->dirname.s, lha->dirname.length, lha->sconv_dir)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to Unicode.",
+ archive_string_conversion_charset_name(lha->sconv_dir));
+ err = ARCHIVE_FATAL;
+ } else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))
+ err = ARCHIVE_FATAL;
+ if (err == ARCHIVE_FATAL) {
+ archive_mstring_clean(&conv_buffer);
+ archive_wstring_free(&pathname);
+ archive_wstring_free(&linkname);
+ return (err);
+ }
+ archive_wstring_copy(&pathname, &conv_buffer.aes_wcs);
+
+ archive_string_empty(&conv_buffer.aes_mbs);
+ archive_string_empty(&conv_buffer.aes_mbs_in_locale);
+ archive_string_empty(&conv_buffer.aes_utf8);
+ archive_wstring_empty(&conv_buffer.aes_wcs);
+ if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->filename.s, lha->filename.length, lha->sconv_fname)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to Unicode.",
+ archive_string_conversion_charset_name(lha->sconv_fname));
+ err = ARCHIVE_FATAL;
+ }
+ else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))
+ err = ARCHIVE_FATAL;
+ if (err == ARCHIVE_FATAL) {
+ archive_mstring_clean(&conv_buffer);
+ archive_wstring_free(&pathname);
+ archive_wstring_free(&linkname);
+ return (err);
+ }
+ archive_wstring_concat(&pathname, &conv_buffer.aes_wcs);
+ archive_mstring_clean(&conv_buffer);
+
+ if ((lha->mode & AE_IFMT) == AE_IFLNK) {
+ /*
+ * Extract the symlink-name if it's included in the pathname.
+ */
+ if (!lha_parse_linkname(&linkname, &pathname)) {
+ /* We couldn't get the symlink-name. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown symlink-name");
+ archive_wstring_free(&pathname);
+ archive_wstring_free(&linkname);
+ return (ARCHIVE_FAILED);
+ }
+ } else {
+ /*
+ * Make sure a file-type is set.
+ * The mode has been overridden if it is in the extended data.
+ */
+ lha->mode = (lha->mode & ~AE_IFMT) |
+ ((lha->directory)? AE_IFDIR: AE_IFREG);
+ }
+ if ((lha->setflag & UNIX_MODE_IS_SET) == 0 &&
+ (lha->dos_attr & 1) != 0)
+ lha->mode &= ~(0222);/* read only. */
+
+ /*
+ * Set basic file parameters.
+ */
+ archive_entry_copy_pathname_w(entry, pathname.s);
+ archive_wstring_free(&pathname);
+ if (archive_strlen(&linkname) > 0) {
+ archive_entry_copy_symlink_w(entry, linkname.s);
+ } else
+ archive_entry_set_symlink(entry, NULL);
+ archive_wstring_free(&linkname);
+ /*
+ * When a header level is 0, there is a possibility that
+ * a pathname and a symlink has '\' character, a directory
+ * separator in DOS/Windows. So we should convert it to '/'.
+ */
+ if (p[H_LEVEL_OFFSET] == 0)
+ lha_replace_path_separator(lha, entry);
+
+ archive_entry_set_mode(entry, lha->mode);
+ archive_entry_set_uid(entry, lha->uid);
+ archive_entry_set_gid(entry, lha->gid);
+ if (archive_strlen(&lha->uname) > 0)
+ archive_entry_set_uname(entry, lha->uname.s);
+ if (archive_strlen(&lha->gname) > 0)
+ archive_entry_set_gname(entry, lha->gname.s);
+ if (lha->setflag & BIRTHTIME_IS_SET) {
+ archive_entry_set_birthtime(entry, lha->birthtime,
+ lha->birthtime_tv_nsec);
+ archive_entry_set_ctime(entry, lha->birthtime,
+ lha->birthtime_tv_nsec);
+ } else {
+ archive_entry_unset_birthtime(entry);
+ archive_entry_unset_ctime(entry);
+ }
+ archive_entry_set_mtime(entry, lha->mtime, lha->mtime_tv_nsec);
+ if (lha->setflag & ATIME_IS_SET)
+ archive_entry_set_atime(entry, lha->atime,
+ lha->atime_tv_nsec);
+ else
+ archive_entry_unset_atime(entry);
+ if (lha->directory || archive_entry_symlink(entry) != NULL)
+ archive_entry_unset_size(entry);
+ else
+ archive_entry_set_size(entry, lha->origsize);
+
+ /*
+ * Prepare variables used to read a file content.
+ */
+ lha->entry_bytes_remaining = lha->compsize;
+ if (lha->entry_bytes_remaining < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa entry size");
+ return (ARCHIVE_FATAL);
+ }
+ lha->entry_offset = 0;
+ lha->entry_crc_calculated = 0;
+
+ /*
+ * This file does not have a content.
+ */
+ if (lha->directory || lha->compsize == 0)
+ lha->end_of_entry = 1;
+
+ snprintf(lha->format_name, sizeof(lha->format_name), "lha -%c%c%c-",
+ lha->method[0], lha->method[1], lha->method[2]);
+ a->archive.archive_format_name = lha->format_name;
+
+ return (err);
+}
+
+/*
+ * Replace a DOS path separator '\' by a character '/'.
+ * Some multi-byte character set have a character '\' in its second byte.
+ */
+static void
+lha_replace_path_separator(struct lha *lha, struct archive_entry *entry)
+{
+ const wchar_t *wp;
+ size_t i;
+
+ if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+ archive_wstrcpy(&(lha->ws), wp);
+ for (i = 0; i < archive_strlen(&(lha->ws)); i++) {
+ if (lha->ws.s[i] == L'\\')
+ lha->ws.s[i] = L'/';
+ }
+ archive_entry_copy_pathname_w(entry, lha->ws.s);
+ }
+
+ if ((wp = archive_entry_symlink_w(entry)) != NULL) {
+ archive_wstrcpy(&(lha->ws), wp);
+ for (i = 0; i < archive_strlen(&(lha->ws)); i++) {
+ if (lha->ws.s[i] == L'\\')
+ lha->ws.s[i] = L'/';
+ }
+ archive_entry_copy_symlink_w(entry, lha->ws.s);
+ }
+}
+
+/*
+ * Header 0 format
+ *
+ * +0 +1 +2 +7 +11
+ * +---------------+----------+----------------+-------------------+
+ * |header size(*1)|header sum|compression type|compressed size(*2)|
+ * +---------------+----------+----------------+-------------------+
+ * <---------------------(*1)----------*
+ *
+ * +11 +15 +17 +19 +20 +21
+ * +-----------------+---------+---------+--------------+----------------+
+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=0)|
+ * +-----------------+---------+---------+--------------+----------------+
+ * *--------------------------------(*1)---------------------------------*
+ *
+ * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+2+(*4)
+ * +---------------+---------+----------+----------------+------------------+
+ * |name length(*3)|file name|file CRC16|extra header(*4)| compressed data |
+ * +---------------+---------+----------+----------------+------------------+
+ * <--(*3)-> <------(*2)------>
+ * *----------------------(*1)-------------------------->
+ *
+ */
+#define H0_HEADER_SIZE_OFFSET 0
+#define H0_HEADER_SUM_OFFSET 1
+#define H0_COMP_SIZE_OFFSET 7
+#define H0_ORIG_SIZE_OFFSET 11
+#define H0_DOS_TIME_OFFSET 15
+#define H0_NAME_LEN_OFFSET 21
+#define H0_FILE_NAME_OFFSET 22
+#define H0_FIXED_SIZE 24
+static int
+lha_read_file_header_0(struct archive_read *a, struct lha *lha)
+{
+ const unsigned char *p;
+ int extdsize, namelen;
+ unsigned char headersum, sum_calculated;
+
+ if ((p = __archive_read_ahead(a, H0_FIXED_SIZE, NULL)) == NULL)
+ return (truncated_error(a));
+ lha->header_size = p[H0_HEADER_SIZE_OFFSET] + 2;
+ headersum = p[H0_HEADER_SUM_OFFSET];
+ lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET);
+ lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET);
+ lha->mtime = lha_dos_time(p + H0_DOS_TIME_OFFSET);
+ namelen = p[H0_NAME_LEN_OFFSET];
+ extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen;
+ if ((namelen > 221 || extdsize < 0) && extdsize != -2) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa header");
+ return (ARCHIVE_FATAL);
+ }
+ if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
+ return (truncated_error(a));
+
+ archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen);
+ /* When extdsize == -2, A CRC16 value is not present in the header. */
+ if (extdsize >= 0) {
+ lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen);
+ lha->setflag |= CRC_IS_SET;
+ }
+ sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
+
+ /* Read an extended header */
+ if (extdsize > 0) {
+ /* This extended data is set by 'LHa for UNIX' only.
+ * Maybe fixed size.
+ */
+ p += H0_FILE_NAME_OFFSET + namelen + 2;
+ if (p[0] == 'U' && extdsize == 12) {
+ /* p[1] is a minor version. */
+ lha->mtime = archive_le32dec(&p[2]);
+ lha->mode = archive_le16dec(&p[6]);
+ lha->uid = archive_le16dec(&p[8]);
+ lha->gid = archive_le16dec(&p[10]);
+ lha->setflag |= UNIX_MODE_IS_SET;
+ }
+ }
+ __archive_read_consume(a, lha->header_size);
+
+ if (sum_calculated != headersum) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LHa header sum error");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Header 1 format
+ *
+ * +0 +1 +2 +7 +11
+ * +---------------+----------+----------------+-------------+
+ * |header size(*1)|header sum|compression type|skip size(*2)|
+ * +---------------+----------+----------------+-------------+
+ * <---------------(*1)----------*
+ *
+ * +11 +15 +17 +19 +20 +21
+ * +-----------------+---------+---------+--------------+----------------+
+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=1)|
+ * +-----------------+---------+---------+--------------+----------------+
+ * *-------------------------------(*1)----------------------------------*
+ *
+ * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+3 +22+(*3)+3+(*4)
+ * +---------------+---------+----------+-----------+-----------+
+ * |name length(*3)|file name|file CRC16| creator |padding(*4)|
+ * +---------------+---------+----------+-----------+-----------+
+ * <--(*3)->
+ * *----------------------------(*1)----------------------------*
+ *
+ * +22+(*3)+3+(*4) +22+(*3)+3+(*4)+2 +22+(*3)+3+(*4)+2+(*5)
+ * +----------------+---------------------+------------------------+
+ * |next header size| extended header(*5) | compressed data |
+ * +----------------+---------------------+------------------------+
+ * *------(*1)-----> <--------------------(*2)-------------------->
+ */
+#define H1_HEADER_SIZE_OFFSET 0
+#define H1_HEADER_SUM_OFFSET 1
+#define H1_COMP_SIZE_OFFSET 7
+#define H1_ORIG_SIZE_OFFSET 11
+#define H1_DOS_TIME_OFFSET 15
+#define H1_NAME_LEN_OFFSET 21
+#define H1_FILE_NAME_OFFSET 22
+#define H1_FIXED_SIZE 27
+static int
+lha_read_file_header_1(struct archive_read *a, struct lha *lha)
+{
+ const unsigned char *p;
+ size_t extdsize;
+ int i, err, err2;
+ int namelen, padding;
+ unsigned char headersum, sum_calculated;
+
+ err = ARCHIVE_OK;
+
+ if ((p = __archive_read_ahead(a, H1_FIXED_SIZE, NULL)) == NULL)
+ return (truncated_error(a));
+
+ lha->header_size = p[H1_HEADER_SIZE_OFFSET] + 2;
+ headersum = p[H1_HEADER_SUM_OFFSET];
+ /* Note: An extended header size is included in a compsize. */
+ lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET);
+ lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET);
+ lha->mtime = lha_dos_time(p + H1_DOS_TIME_OFFSET);
+ namelen = p[H1_NAME_LEN_OFFSET];
+ /* Calculate a padding size. The result will be normally 0 only(?) */
+ padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen;
+
+ if (namelen > 230 || padding < 0)
+ goto invalid;
+
+ if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
+ return (truncated_error(a));
+
+ for (i = 0; i < namelen; i++) {
+ if (p[i + H1_FILE_NAME_OFFSET] == 0xff)
+ goto invalid;/* Invalid filename. */
+ }
+ archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen);
+ lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen);
+ lha->setflag |= CRC_IS_SET;
+
+ sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
+ /* Consume used bytes but not include `next header size' data
+ * since it will be consumed in lha_read_file_extended_header(). */
+ __archive_read_consume(a, lha->header_size - 2);
+
+ /* Read extended headers */
+ err2 = lha_read_file_extended_header(a, lha, NULL, 2,
+ (size_t)(lha->compsize + 2), &extdsize);
+ if (err2 < ARCHIVE_WARN)
+ return (err2);
+ if (err2 < err)
+ err = err2;
+ /* Get a real compressed file size. */
+ lha->compsize -= extdsize - 2;
+
+ if (lha->compsize < 0)
+ goto invalid; /* Invalid compressed file size */
+
+ if (sum_calculated != headersum) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LHa header sum error");
+ return (ARCHIVE_FATAL);
+ }
+ return (err);
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Header 2 format
+ *
+ * +0 +2 +7 +11 +15
+ * +---------------+----------------+-------------------+-----------------+
+ * |header size(*1)|compression type|compressed size(*2)|uncompressed size|
+ * +---------------+----------------+-------------------+-----------------+
+ * <--------------------------------(*1)---------------------------------*
+ *
+ * +15 +19 +20 +21 +23 +24
+ * +-----------------+------------+----------------+----------+-----------+
+ * |data/time(time_t)| 0x20 fixed |header level(=2)|file CRC16| creator |
+ * +-----------------+------------+----------------+----------+-----------+
+ * *---------------------------------(*1)---------------------------------*
+ *
+ * +24 +26 +26+(*3) +26+(*3)+(*4)
+ * +----------------+-------------------+-------------+-------------------+
+ * |next header size|extended header(*3)| padding(*4) | compressed data |
+ * +----------------+-------------------+-------------+-------------------+
+ * *--------------------------(*1)-------------------> <------(*2)------->
+ *
+ */
+#define H2_HEADER_SIZE_OFFSET 0
+#define H2_COMP_SIZE_OFFSET 7
+#define H2_ORIG_SIZE_OFFSET 11
+#define H2_TIME_OFFSET 15
+#define H2_CRC_OFFSET 21
+#define H2_FIXED_SIZE 24
+static int
+lha_read_file_header_2(struct archive_read *a, struct lha *lha)
+{
+ const unsigned char *p;
+ size_t extdsize;
+ int err, padding;
+ uint16_t header_crc;
+
+ if ((p = __archive_read_ahead(a, H2_FIXED_SIZE, NULL)) == NULL)
+ return (truncated_error(a));
+
+ lha->header_size =archive_le16dec(p + H2_HEADER_SIZE_OFFSET);
+ lha->compsize = archive_le32dec(p + H2_COMP_SIZE_OFFSET);
+ lha->origsize = archive_le32dec(p + H2_ORIG_SIZE_OFFSET);
+ lha->mtime = archive_le32dec(p + H2_TIME_OFFSET);
+ lha->crc = archive_le16dec(p + H2_CRC_OFFSET);
+ lha->setflag |= CRC_IS_SET;
+
+ if (lha->header_size < H2_FIXED_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa header size");
+ return (ARCHIVE_FATAL);
+ }
+
+ header_crc = lha_crc16(0, p, H2_FIXED_SIZE);
+ __archive_read_consume(a, H2_FIXED_SIZE);
+
+ /* Read extended headers */
+ err = lha_read_file_extended_header(a, lha, &header_crc, 2,
+ lha->header_size - H2_FIXED_SIZE, &extdsize);
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+ /* Calculate a padding size. The result will be normally 0 or 1. */
+ padding = (int)lha->header_size - (int)(H2_FIXED_SIZE + extdsize);
+ if (padding > 0) {
+ if ((p = __archive_read_ahead(a, padding, NULL)) == NULL)
+ return (truncated_error(a));
+ header_crc = lha_crc16(header_crc, p, padding);
+ __archive_read_consume(a, padding);
+ }
+
+ if (header_crc != lha->header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "LHa header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ return (err);
+}
+
+/*
+ * Header 3 format
+ *
+ * +0 +2 +7 +11 +15
+ * +------------+----------------+-------------------+-----------------+
+ * | 0x04 fixed |compression type|compressed size(*2)|uncompressed size|
+ * +------------+----------------+-------------------+-----------------+
+ * <-------------------------------(*1)-------------------------------*
+ *
+ * +15 +19 +20 +21 +23 +24
+ * +-----------------+------------+----------------+----------+-----------+
+ * |date/time(time_t)| 0x20 fixed |header level(=3)|file CRC16| creator |
+ * +-----------------+------------+----------------+----------+-----------+
+ * *--------------------------------(*1)----------------------------------*
+ *
+ * +24 +28 +32 +32+(*3)
+ * +---------------+----------------+-------------------+-----------------+
+ * |header size(*1)|next header size|extended header(*3)| compressed data |
+ * +---------------+----------------+-------------------+-----------------+
+ * *------------------------(*1)-----------------------> <------(*2)----->
+ *
+ */
+#define H3_FIELD_LEN_OFFSET 0
+#define H3_COMP_SIZE_OFFSET 7
+#define H3_ORIG_SIZE_OFFSET 11
+#define H3_TIME_OFFSET 15
+#define H3_CRC_OFFSET 21
+#define H3_HEADER_SIZE_OFFSET 24
+#define H3_FIXED_SIZE 28
+static int
+lha_read_file_header_3(struct archive_read *a, struct lha *lha)
+{
+ const unsigned char *p;
+ size_t extdsize;
+ int err;
+ uint16_t header_crc;
+
+ if ((p = __archive_read_ahead(a, H3_FIXED_SIZE, NULL)) == NULL)
+ return (truncated_error(a));
+
+ if (archive_le16dec(p + H3_FIELD_LEN_OFFSET) != 4)
+ goto invalid;
+ lha->header_size =archive_le32dec(p + H3_HEADER_SIZE_OFFSET);
+ lha->compsize = archive_le32dec(p + H3_COMP_SIZE_OFFSET);
+ lha->origsize = archive_le32dec(p + H3_ORIG_SIZE_OFFSET);
+ lha->mtime = archive_le32dec(p + H3_TIME_OFFSET);
+ lha->crc = archive_le16dec(p + H3_CRC_OFFSET);
+ lha->setflag |= CRC_IS_SET;
+
+ if (lha->header_size < H3_FIXED_SIZE + 4)
+ goto invalid;
+ header_crc = lha_crc16(0, p, H3_FIXED_SIZE);
+ __archive_read_consume(a, H3_FIXED_SIZE);
+
+ /* Read extended headers */
+ err = lha_read_file_extended_header(a, lha, &header_crc, 4,
+ lha->header_size - H3_FIXED_SIZE, &extdsize);
+ if (err < ARCHIVE_WARN)
+ return (err);
+
+ if (header_crc != lha->header_crc) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "LHa header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ return (err);
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Extended header format
+ *
+ * +0 +2 +3 -- used in header 1 and 2
+ * +0 +4 +5 -- used in header 3
+ * +--------------+---------+-------------------+--------------+--
+ * |ex-header size|header id| data |ex-header size| .......
+ * +--------------+---------+-------------------+--------------+--
+ * <-------------( ex-header size)------------> <-- next extended header --*
+ *
+ * If the ex-header size is zero, it is the make of the end of extended
+ * headers.
+ *
+ */
+static int
+lha_read_file_extended_header(struct archive_read *a, struct lha *lha,
+ uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size)
+{
+ const void *h;
+ const unsigned char *extdheader;
+ size_t extdsize;
+ size_t datasize;
+ unsigned int i;
+ unsigned char extdtype;
+
+#define EXT_HEADER_CRC 0x00 /* Header CRC and information*/
+#define EXT_FILENAME 0x01 /* Filename */
+#define EXT_DIRECTORY 0x02 /* Directory name */
+#define EXT_DOS_ATTR 0x40 /* MS-DOS attribute */
+#define EXT_TIMESTAMP 0x41 /* Windows time stamp */
+#define EXT_FILESIZE 0x42 /* Large file size */
+#define EXT_TIMEZONE 0x43 /* Time zone */
+#define EXT_UTF16_FILENAME 0x44 /* UTF-16 filename */
+#define EXT_UTF16_DIRECTORY 0x45 /* UTF-16 directory name */
+#define EXT_CODEPAGE 0x46 /* Codepage */
+#define EXT_UNIX_MODE 0x50 /* File permission */
+#define EXT_UNIX_GID_UID 0x51 /* gid,uid */
+#define EXT_UNIX_GNAME 0x52 /* Group name */
+#define EXT_UNIX_UNAME 0x53 /* User name */
+#define EXT_UNIX_MTIME 0x54 /* Modified time */
+#define EXT_OS2_NEW_ATTR 0x7f /* new attribute(OS/2 only) */
+#define EXT_NEW_ATTR 0xff /* new attribute */
+
+ *total_size = sizefield_length;
+
+ for (;;) {
+ /* Read an extended header size. */
+ if ((h =
+ __archive_read_ahead(a, sizefield_length, NULL)) == NULL)
+ return (truncated_error(a));
+ /* Check if the size is the zero indicates the end of the
+ * extended header. */
+ if (sizefield_length == sizeof(uint16_t))
+ extdsize = archive_le16dec(h);
+ else
+ extdsize = archive_le32dec(h);
+ if (extdsize == 0) {
+ /* End of extended header */
+ if (crc != NULL)
+ *crc = lha_crc16(*crc, h, sizefield_length);
+ __archive_read_consume(a, sizefield_length);
+ return (ARCHIVE_OK);
+ }
+
+ /* Sanity check to the extended header size. */
+ if (((uint64_t)*total_size + extdsize) >
+ (uint64_t)limitsize ||
+ extdsize <= (size_t)sizefield_length)
+ goto invalid;
+
+ /* Read the extended header. */
+ if ((h = __archive_read_ahead(a, extdsize, NULL)) == NULL)
+ return (truncated_error(a));
+ *total_size += extdsize;
+
+ extdheader = (const unsigned char *)h;
+ /* Get the extended header type. */
+ extdtype = extdheader[sizefield_length];
+ /* Calculate an extended data size. */
+ datasize = extdsize - (1 + sizefield_length);
+ /* Skip an extended header size field and type field. */
+ extdheader += sizefield_length + 1;
+
+ if (crc != NULL && extdtype != EXT_HEADER_CRC)
+ *crc = lha_crc16(*crc, h, extdsize);
+ switch (extdtype) {
+ case EXT_HEADER_CRC:
+ /* We only use a header CRC. Following data will not
+ * be used. */
+ if (datasize >= 2) {
+ lha->header_crc = archive_le16dec(extdheader);
+ if (crc != NULL) {
+ static const char zeros[2] = {0, 0};
+ *crc = lha_crc16(*crc, h,
+ extdsize - datasize);
+ /* CRC value itself as zero */
+ *crc = lha_crc16(*crc, zeros, 2);
+ *crc = lha_crc16(*crc,
+ extdheader+2, datasize - 2);
+ }
+ }
+ break;
+ case EXT_FILENAME:
+ if (datasize == 0) {
+ /* maybe directory header */
+ archive_string_empty(&lha->filename);
+ break;
+ }
+ if (extdheader[0] == '\0')
+ goto invalid;
+ archive_strncpy(&lha->filename,
+ (const char *)extdheader, datasize);
+ break;
+ case EXT_UTF16_FILENAME:
+ if (datasize == 0) {
+ /* maybe directory header */
+ archive_string_empty(&lha->filename);
+ break;
+ } else if (datasize & 1) {
+ /* UTF-16 characters take always 2 or 4 bytes */
+ goto invalid;
+ }
+ if (extdheader[0] == '\0')
+ goto invalid;
+ archive_string_empty(&lha->filename);
+ archive_array_append(&lha->filename,
+ (const char *)extdheader, datasize);
+ /* Setup a string conversion for a filename. */
+ lha->sconv_fname =
+ archive_string_conversion_from_charset(&a->archive,
+ "UTF-16LE", 1);
+ if (lha->sconv_fname == NULL)
+ return (ARCHIVE_FATAL);
+ break;
+ case EXT_DIRECTORY:
+ if (datasize == 0 || extdheader[0] == '\0')
+ /* no directory name data. exit this case. */
+ goto invalid;
+
+ archive_strncpy(&lha->dirname,
+ (const char *)extdheader, datasize);
+ /*
+ * Convert directory delimiter from 0xFF
+ * to '/' for local system.
+ */
+ for (i = 0; i < lha->dirname.length; i++) {
+ if ((unsigned char)lha->dirname.s[i] == 0xFF)
+ lha->dirname.s[i] = '/';
+ }
+ /* Is last character directory separator? */
+ if (lha->dirname.s[lha->dirname.length-1] != '/')
+ /* invalid directory data */
+ goto invalid;
+ break;
+ case EXT_UTF16_DIRECTORY:
+ /* UTF-16 characters take always 2 or 4 bytes */
+ if (datasize == 0 || (datasize & 1) ||
+ extdheader[0] == '\0') {
+ /* no directory name data. exit this case. */
+ goto invalid;
+ }
+
+ archive_string_empty(&lha->dirname);
+ archive_array_append(&lha->dirname,
+ (const char *)extdheader, datasize);
+ lha->sconv_dir =
+ archive_string_conversion_from_charset(&a->archive,
+ "UTF-16LE", 1);
+ if (lha->sconv_dir == NULL)
+ return (ARCHIVE_FATAL);
+ else {
+ /*
+ * Convert directory delimiter from 0xFFFF
+ * to '/' for local system.
+ */
+ uint16_t dirSep;
+ uint16_t d = 1;
+ if (archive_be16dec(&d) == 1)
+ dirSep = 0x2F00;
+ else
+ dirSep = 0x002F;
+
+ /* UTF-16LE character */
+ uint16_t *utf16name =
+ (uint16_t *)lha->dirname.s;
+ for (i = 0; i < lha->dirname.length / 2; i++) {
+ if (utf16name[i] == 0xFFFF) {
+ utf16name[i] = dirSep;
+ }
+ }
+ /* Is last character directory separator? */
+ if (utf16name[lha->dirname.length / 2 - 1] !=
+ dirSep) {
+ /* invalid directory data */
+ goto invalid;
+ }
+ }
+ break;
+ case EXT_DOS_ATTR:
+ if (datasize == 2)
+ lha->dos_attr = (unsigned char)
+ (archive_le16dec(extdheader) & 0xff);
+ break;
+ case EXT_TIMESTAMP:
+ if (datasize == (sizeof(uint64_t) * 3)) {
+ lha->birthtime = lha_win_time(
+ archive_le64dec(extdheader),
+ &lha->birthtime_tv_nsec);
+ extdheader += sizeof(uint64_t);
+ lha->mtime = lha_win_time(
+ archive_le64dec(extdheader),
+ &lha->mtime_tv_nsec);
+ extdheader += sizeof(uint64_t);
+ lha->atime = lha_win_time(
+ archive_le64dec(extdheader),
+ &lha->atime_tv_nsec);
+ lha->setflag |= BIRTHTIME_IS_SET |
+ ATIME_IS_SET;
+ }
+ break;
+ case EXT_FILESIZE:
+ if (datasize == sizeof(uint64_t) * 2) {
+ lha->compsize = archive_le64dec(extdheader);
+ extdheader += sizeof(uint64_t);
+ lha->origsize = archive_le64dec(extdheader);
+ }
+ break;
+ case EXT_CODEPAGE:
+ /* Get an archived filename charset from codepage.
+ * This overwrites the charset specified by
+ * hdrcharset option. */
+ if (datasize == sizeof(uint32_t)) {
+ struct archive_string cp;
+ const char *charset;
+
+ archive_string_init(&cp);
+ switch (archive_le32dec(extdheader)) {
+ case 65001: /* UTF-8 */
+ charset = "UTF-8";
+ break;
+ default:
+ archive_string_sprintf(&cp, "CP%d",
+ (int)archive_le32dec(extdheader));
+ charset = cp.s;
+ break;
+ }
+ lha->sconv_dir =
+ archive_string_conversion_from_charset(
+ &(a->archive), charset, 1);
+ lha->sconv_fname =
+ archive_string_conversion_from_charset(
+ &(a->archive), charset, 1);
+ archive_string_free(&cp);
+ if (lha->sconv_dir == NULL)
+ return (ARCHIVE_FATAL);
+ if (lha->sconv_fname == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ case EXT_UNIX_MODE:
+ if (datasize == sizeof(uint16_t)) {
+ lha->mode = archive_le16dec(extdheader);
+ lha->setflag |= UNIX_MODE_IS_SET;
+ }
+ break;
+ case EXT_UNIX_GID_UID:
+ if (datasize == (sizeof(uint16_t) * 2)) {
+ lha->gid = archive_le16dec(extdheader);
+ lha->uid = archive_le16dec(extdheader+2);
+ }
+ break;
+ case EXT_UNIX_GNAME:
+ if (datasize > 0)
+ archive_strncpy(&lha->gname,
+ (const char *)extdheader, datasize);
+ break;
+ case EXT_UNIX_UNAME:
+ if (datasize > 0)
+ archive_strncpy(&lha->uname,
+ (const char *)extdheader, datasize);
+ break;
+ case EXT_UNIX_MTIME:
+ if (datasize == sizeof(uint32_t))
+ lha->mtime = archive_le32dec(extdheader);
+ break;
+ case EXT_OS2_NEW_ATTR:
+ /* This extended header is OS/2 depend. */
+ if (datasize == 16) {
+ lha->dos_attr = (unsigned char)
+ (archive_le16dec(extdheader) & 0xff);
+ lha->mode = archive_le16dec(extdheader+2);
+ lha->gid = archive_le16dec(extdheader+4);
+ lha->uid = archive_le16dec(extdheader+6);
+ lha->birthtime = archive_le32dec(extdheader+8);
+ lha->atime = archive_le32dec(extdheader+12);
+ lha->setflag |= UNIX_MODE_IS_SET
+ | BIRTHTIME_IS_SET | ATIME_IS_SET;
+ }
+ break;
+ case EXT_NEW_ATTR:
+ if (datasize == 20) {
+ lha->mode = (mode_t)archive_le32dec(extdheader);
+ lha->gid = archive_le32dec(extdheader+4);
+ lha->uid = archive_le32dec(extdheader+8);
+ lha->birthtime = archive_le32dec(extdheader+12);
+ lha->atime = archive_le32dec(extdheader+16);
+ lha->setflag |= UNIX_MODE_IS_SET
+ | BIRTHTIME_IS_SET | ATIME_IS_SET;
+ }
+ break;
+ case EXT_TIMEZONE: /* Not supported */
+ break;
+ default:
+ break;
+ }
+
+ __archive_read_consume(a, extdsize);
+ }
+invalid:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid extended LHa header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+lha_end_of_entry(struct archive_read *a)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+ int r = ARCHIVE_EOF;
+
+ if (!lha->end_of_entry_cleanup) {
+ if ((lha->setflag & CRC_IS_SET) &&
+ lha->crc != lha->entry_crc_calculated) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "LHa data CRC error");
+ r = ARCHIVE_WARN;
+ }
+
+ /* End-of-entry cleanup done. */
+ lha->end_of_entry_cleanup = 1;
+ }
+ return (r);
+}
+
+static int
+archive_read_format_lha_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+ int r;
+
+ if (lha->entry_unconsumed) {
+ /* Consume as much as the decompressor actually used. */
+ __archive_read_consume(a, lha->entry_unconsumed);
+ lha->entry_unconsumed = 0;
+ }
+ if (lha->end_of_entry) {
+ *offset = lha->entry_offset;
+ *size = 0;
+ *buff = NULL;
+ return (lha_end_of_entry(a));
+ }
+
+ if (lha->entry_is_compressed)
+ r = lha_read_data_lzh(a, buff, size, offset);
+ else
+ /* No compression. */
+ r = lha_read_data_none(a, buff, size, offset);
+ return (r);
+}
+
+/*
+ * Read a file content in no compression.
+ *
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * lha->end_of_entry if it consumes all of the data.
+ */
+static int
+lha_read_data_none(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+ ssize_t bytes_avail;
+
+ if (lha->entry_bytes_remaining == 0) {
+ *buff = NULL;
+ *size = 0;
+ *offset = lha->entry_offset;
+ lha->end_of_entry = 1;
+ return (ARCHIVE_OK);
+ }
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ *buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated LHa file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_avail > lha->entry_bytes_remaining)
+ bytes_avail = (ssize_t)lha->entry_bytes_remaining;
+ lha->entry_crc_calculated =
+ lha_crc16(lha->entry_crc_calculated, *buff, bytes_avail);
+ *size = bytes_avail;
+ *offset = lha->entry_offset;
+ lha->entry_offset += bytes_avail;
+ lha->entry_bytes_remaining -= bytes_avail;
+ if (lha->entry_bytes_remaining == 0)
+ lha->end_of_entry = 1;
+ lha->entry_unconsumed = bytes_avail;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Read a file content in LZHUFF encoding.
+ *
+ * Returns ARCHIVE_OK if successful, returns ARCHIVE_WARN if compression is
+ * unsupported, ARCHIVE_FATAL otherwise, sets lha->end_of_entry if it consumes
+ * all of the data.
+ */
+static int
+lha_read_data_lzh(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+ ssize_t bytes_avail;
+ int r;
+
+ /* If we haven't yet read any data, initialize the decompressor. */
+ if (!lha->decompress_init) {
+ r = lzh_decode_init(&(lha->strm), lha->method);
+ switch (r) {
+ case ARCHIVE_OK:
+ break;
+ case ARCHIVE_FAILED:
+ /* Unsupported compression. */
+ *buff = NULL;
+ *size = 0;
+ *offset = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported lzh compression method -%c%c%c-",
+ lha->method[0], lha->method[1], lha->method[2]);
+ /* We know compressed size; just skip it. */
+ archive_read_format_lha_read_data_skip(a);
+ return (ARCHIVE_WARN);
+ default:
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory "
+ "for lzh decompression");
+ return (ARCHIVE_FATAL);
+ }
+ /* We've initialized decompression for this stream. */
+ lha->decompress_init = 1;
+ lha->strm.avail_out = 0;
+ lha->strm.total_out = 0;
+ }
+
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ lha->strm.next_in = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated LHa file body");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_avail > lha->entry_bytes_remaining)
+ bytes_avail = (ssize_t)lha->entry_bytes_remaining;
+
+ lha->strm.avail_in = (int)bytes_avail;
+ lha->strm.total_in = 0;
+ lha->strm.avail_out = 0;
+
+ r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining);
+ switch (r) {
+ case ARCHIVE_OK:
+ break;
+ case ARCHIVE_EOF:
+ lha->end_of_entry = 1;
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Bad lzh data");
+ return (ARCHIVE_FAILED);
+ }
+ lha->entry_unconsumed = lha->strm.total_in;
+ lha->entry_bytes_remaining -= lha->strm.total_in;
+
+ if (lha->strm.avail_out) {
+ *offset = lha->entry_offset;
+ *size = lha->strm.avail_out;
+ *buff = lha->strm.ref_ptr;
+ lha->entry_crc_calculated =
+ lha_crc16(lha->entry_crc_calculated, *buff, *size);
+ lha->entry_offset += *size;
+ } else {
+ *offset = lha->entry_offset;
+ *size = 0;
+ *buff = NULL;
+ if (lha->end_of_entry)
+ return (lha_end_of_entry(a));
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Skip a file content.
+ */
+static int
+archive_read_format_lha_read_data_skip(struct archive_read *a)
+{
+ struct lha *lha;
+ int64_t bytes_skipped;
+
+ lha = (struct lha *)(a->format->data);
+
+ if (lha->entry_unconsumed) {
+ /* Consume as much as the decompressor actually used. */
+ __archive_read_consume(a, lha->entry_unconsumed);
+ lha->entry_unconsumed = 0;
+ }
+
+ /* if we've already read to end of data, we're done. */
+ if (lha->end_of_entry_cleanup)
+ return (ARCHIVE_OK);
+
+ /*
+ * If the length is at the beginning, we can skip the
+ * compressed data much more quickly.
+ */
+ bytes_skipped = __archive_read_consume(a, lha->entry_bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ /* This entry is finished and done. */
+ lha->end_of_entry_cleanup = lha->end_of_entry = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_lha_cleanup(struct archive_read *a)
+{
+ struct lha *lha = (struct lha *)(a->format->data);
+
+ lzh_decode_free(&(lha->strm));
+ archive_string_free(&(lha->dirname));
+ archive_string_free(&(lha->filename));
+ archive_string_free(&(lha->uname));
+ archive_string_free(&(lha->gname));
+ archive_wstring_free(&(lha->ws));
+ free(lha);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * 'LHa for UNIX' utility has archived a symbolic-link name after
+ * a pathname with '|' character.
+ * This function extracts the symbolic-link name from the pathname.
+ *
+ * example.
+ * 1. a symbolic-name is 'aaa/bb/cc'
+ * 2. a filename is 'xxx/bbb'
+ * then a archived pathname is 'xxx/bbb|aaa/bb/cc'
+ */
+static int
+lha_parse_linkname(struct archive_wstring *linkname,
+ struct archive_wstring *pathname)
+{
+ wchar_t * linkptr;
+ size_t symlen;
+
+ linkptr = wcschr(pathname->s, L'|');
+ if (linkptr != NULL) {
+ symlen = wcslen(linkptr + 1);
+ archive_wstrncpy(linkname, linkptr+1, symlen);
+
+ *linkptr = 0;
+ pathname->length = wcslen(pathname->s);
+
+ return (1);
+ }
+ return (0);
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+lha_dos_time(const unsigned char *p)
+{
+ int msTime, msDate;
+ struct tm ts;
+
+ msTime = archive_le16dec(p);
+ msDate = archive_le16dec(p+2);
+
+ memset(&ts, 0, sizeof(ts));
+ ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
+ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
+ ts.tm_mday = msDate & 0x1f; /* Day of month. */
+ ts.tm_hour = (msTime >> 11) & 0x1f;
+ ts.tm_min = (msTime >> 5) & 0x3f;
+ ts.tm_sec = (msTime << 1) & 0x3e;
+ ts.tm_isdst = -1;
+ return (mktime(&ts));
+}
+
+/* Convert an MS-Windows-style date/time into Unix-style time. */
+static time_t
+lha_win_time(uint64_t wintime, long *ns)
+{
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+ if (wintime >= EPOC_TIME) {
+ wintime -= EPOC_TIME; /* 1970-01-01 00:00:00 (UTC) */
+ if (ns != NULL)
+ *ns = (long)(wintime % 10000000) * 100;
+ return (wintime / 10000000);
+ } else {
+ if (ns != NULL)
+ *ns = 0;
+ return (0);
+ }
+}
+
+static unsigned char
+lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size)
+{
+ unsigned char const *p = (unsigned char const *)pp;
+
+ p += offset;
+ for (;size > 0; --size)
+ sum += *p++;
+ return (sum);
+}
+
+static uint16_t crc16tbl[2][256];
+static void
+lha_crc16_init(void)
+{
+ unsigned int i;
+ static int crc16init = 0;
+
+ if (crc16init)
+ return;
+ crc16init = 1;
+
+ for (i = 0; i < 256; i++) {
+ unsigned int j;
+ uint16_t crc = (uint16_t)i;
+ for (j = 8; j; j--)
+ crc = (crc >> 1) ^ ((crc & 1) * 0xA001);
+ crc16tbl[0][i] = crc;
+ }
+
+ for (i = 0; i < 256; i++) {
+ crc16tbl[1][i] = (crc16tbl[0][i] >> 8)
+ ^ crc16tbl[0][crc16tbl[0][i] & 0xff];
+ }
+}
+
+static uint16_t
+lha_crc16(uint16_t crc, const void *pp, size_t len)
+{
+ const unsigned char *p = (const unsigned char *)pp;
+ const uint16_t *buff;
+ const union {
+ uint32_t i;
+ char c[4];
+ } u = { 0x01020304 };
+
+ if (len == 0)
+ return crc;
+
+ /* Process unaligned address. */
+ if (((uintptr_t)p) & (uintptr_t)0x1) {
+ crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];
+ len--;
+ }
+ buff = (const uint16_t *)p;
+ /*
+ * Modern C compiler such as GCC does not unroll automatically yet
+ * without unrolling pragma, and Clang is so. So we should
+ * unroll this loop for its performance.
+ */
+ for (;len >= 8; len -= 8) {
+ /* This if statement expects compiler optimization will
+ * remove the statement which will not be executed. */
+#undef bswap16
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */
+# define bswap16(x) _byteswap_ushort(x)
+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4)
+/* GCC 4.8 and later has __builtin_bswap16() */
+# define bswap16(x) __builtin_bswap16(x)
+#elif defined(__clang__) && __has_builtin(__builtin_bswap16)
+/* Newer clang versions have __builtin_bswap16() */
+# define bswap16(x) __builtin_bswap16(x)
+#else
+# define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8))
+#endif
+#define CRC16W do { \
+ if(u.c[0] == 1) { /* Big endian */ \
+ crc ^= bswap16(*buff); buff++; \
+ } else \
+ crc ^= *buff++; \
+ crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\
+} while (0)
+ CRC16W;
+ CRC16W;
+ CRC16W;
+ CRC16W;
+#undef CRC16W
+#undef bswap16
+ }
+
+ p = (const unsigned char *)buff;
+ for (;len; len--) {
+ crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];
+ }
+ return crc;
+}
+
+/*
+ * Initialize LZHUF decoder.
+ *
+ * Returns ARCHIVE_OK if initialization was successful.
+ * Returns ARCHIVE_FAILED if method is unsupported.
+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation
+ * error occurred.
+ */
+static int
+lzh_decode_init(struct lzh_stream *strm, const char *method)
+{
+ struct lzh_dec *ds;
+ int w_bits, w_size;
+
+ if (strm->ds == NULL) {
+ strm->ds = calloc(1, sizeof(*strm->ds));
+ if (strm->ds == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ ds = strm->ds;
+ ds->error = ARCHIVE_FAILED;
+ if (method == NULL || method[0] != 'l' || method[1] != 'h')
+ return (ARCHIVE_FAILED);
+ switch (method[2]) {
+ case '5':
+ w_bits = 13;/* 8KiB for window */
+ break;
+ case '6':
+ w_bits = 15;/* 32KiB for window */
+ break;
+ case '7':
+ w_bits = 16;/* 64KiB for window */
+ break;
+ default:
+ return (ARCHIVE_FAILED);/* Not supported. */
+ }
+ ds->error = ARCHIVE_FATAL;
+ /* Expand a window size up to 128 KiB for decompressing process
+ * performance whatever its original window size is. */
+ ds->w_size = 1U << 17;
+ ds->w_mask = ds->w_size -1;
+ if (ds->w_buff == NULL) {
+ ds->w_buff = malloc(ds->w_size);
+ if (ds->w_buff == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ w_size = 1U << w_bits;
+ memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size);
+ ds->w_pos = 0;
+ ds->state = 0;
+ ds->pos_pt_len_size = w_bits + 1;
+ ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4;
+ ds->literal_pt_len_size = PT_BITLEN_SIZE;
+ ds->literal_pt_len_bits = 5;
+ ds->br.cache_buffer = 0;
+ ds->br.cache_avail = 0;
+
+ if (lzh_huffman_init(&(ds->lt), LT_BITLEN_SIZE, 16)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ ds->lt.len_bits = 9;
+ if (lzh_huffman_init(&(ds->pt), PT_BITLEN_SIZE, 16)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ ds->error = 0;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Release LZHUF decoder.
+ */
+static void
+lzh_decode_free(struct lzh_stream *strm)
+{
+
+ if (strm->ds == NULL)
+ return;
+ free(strm->ds->w_buff);
+ lzh_huffman_free(&(strm->ds->lt));
+ lzh_huffman_free(&(strm->ds->pt));
+ free(strm->ds);
+ strm->ds = NULL;
+}
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define lzh_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define lzh_br_bits(br, n) \
+ (((uint16_t)((br)->cache_buffer >> \
+ ((br)->cache_avail - (n)))) & cache_masks[n])
+#define lzh_br_bits_forced(br, n) \
+ (((uint16_t)((br)->cache_buffer << \
+ ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ * True : completed, there is enough data in the cache buffer.
+ * False : we met that strm->next_in is empty, we have to get following
+ * bytes. */
+#define lzh_br_read_ahead_0(strm, br, n) \
+ (lzh_br_has(br, (n)) || lzh_br_fillup(strm, br))
+/* True : the cache buffer has some bits as much as we need.
+ * False : there are no enough bits in the cache buffer to be used,
+ * we have to get following bytes if we could. */
+#define lzh_br_read_ahead(strm, br, n) \
+ (lzh_br_read_ahead_0((strm), (br), (n)) || lzh_br_has((br), (n)))
+
+/* Notify how many bits we consumed. */
+#define lzh_br_consume(br, n) ((br)->cache_avail -= (n))
+#define lzh_br_unconsume(br, n) ((br)->cache_avail += (n))
+
+static const uint16_t cache_masks[] = {
+ 0x0000, 0x0001, 0x0003, 0x0007,
+ 0x000F, 0x001F, 0x003F, 0x007F,
+ 0x00FF, 0x01FF, 0x03FF, 0x07FF,
+ 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
+ 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br)
+{
+ int n = CACHE_BITS - br->cache_avail;
+
+ for (;;) {
+ const int x = n >> 3;
+ if (strm->avail_in >= x) {
+ switch (x) {
+ case 8:
+ br->cache_buffer =
+ ((uint64_t)strm->next_in[0]) << 56 |
+ ((uint64_t)strm->next_in[1]) << 48 |
+ ((uint64_t)strm->next_in[2]) << 40 |
+ ((uint64_t)strm->next_in[3]) << 32 |
+ ((uint32_t)strm->next_in[4]) << 24 |
+ ((uint32_t)strm->next_in[5]) << 16 |
+ ((uint32_t)strm->next_in[6]) << 8 |
+ (uint32_t)strm->next_in[7];
+ strm->next_in += 8;
+ strm->avail_in -= 8;
+ br->cache_avail += 8 * 8;
+ return (1);
+ case 7:
+ br->cache_buffer =
+ (br->cache_buffer << 56) |
+ ((uint64_t)strm->next_in[0]) << 48 |
+ ((uint64_t)strm->next_in[1]) << 40 |
+ ((uint64_t)strm->next_in[2]) << 32 |
+ ((uint64_t)strm->next_in[3]) << 24 |
+ ((uint64_t)strm->next_in[4]) << 16 |
+ ((uint64_t)strm->next_in[5]) << 8 |
+ (uint64_t)strm->next_in[6];
+ strm->next_in += 7;
+ strm->avail_in -= 7;
+ br->cache_avail += 7 * 8;
+ return (1);
+ case 6:
+ br->cache_buffer =
+ (br->cache_buffer << 48) |
+ ((uint64_t)strm->next_in[0]) << 40 |
+ ((uint64_t)strm->next_in[1]) << 32 |
+ ((uint64_t)strm->next_in[2]) << 24 |
+ ((uint64_t)strm->next_in[3]) << 16 |
+ ((uint64_t)strm->next_in[4]) << 8 |
+ (uint64_t)strm->next_in[5];
+ strm->next_in += 6;
+ strm->avail_in -= 6;
+ br->cache_avail += 6 * 8;
+ return (1);
+ case 0:
+ /* We have enough compressed data in
+ * the cache buffer.*/
+ return (1);
+ default:
+ break;
+ }
+ }
+ if (strm->avail_in == 0) {
+ /* There is not enough compressed data to fill up the
+ * cache buffer. */
+ return (0);
+ }
+ br->cache_buffer =
+ (br->cache_buffer << 8) | *strm->next_in++;
+ strm->avail_in--;
+ br->cache_avail += 8;
+ n -= 8;
+ }
+}
+
+/*
+ * Decode LZHUF.
+ *
+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
+ * Please set available buffer and call this function again.
+ * 2. Returns ARCHIVE_EOF if decompression has been completed.
+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
+ * is broken or you do not set 'last' flag properly.
+ * 4. 'last' flag is very important, you must set 1 to the flag if there
+ * is no input data. The lha compressed data format does not provide how
+ * to know the compressed data is really finished.
+ * Note: lha command utility check if the total size of output bytes is
+ * reached the uncompressed size recorded in its header. it does not mind
+ * that the decoding process is properly finished.
+ * GNU ZIP can decompress another compressed file made by SCO LZH compress.
+ * it handles EOF as null to fill read buffer with zero until the decoding
+ * process meet 2 bytes of zeros at reading a size of a next chunk, so the
+ * zeros are treated as the mark of the end of the data although the zeros
+ * is dummy, not the file data.
+ */
+static int lzh_read_blocks(struct lzh_stream *, int);
+static int lzh_decode_blocks(struct lzh_stream *, int);
+#define ST_RD_BLOCK 0
+#define ST_RD_PT_1 1
+#define ST_RD_PT_2 2
+#define ST_RD_PT_3 3
+#define ST_RD_PT_4 4
+#define ST_RD_LITERAL_1 5
+#define ST_RD_LITERAL_2 6
+#define ST_RD_LITERAL_3 7
+#define ST_RD_POS_DATA_1 8
+#define ST_GET_LITERAL 9
+#define ST_GET_POS_1 10
+#define ST_GET_POS_2 11
+#define ST_COPY_DATA 12
+
+static int
+lzh_decode(struct lzh_stream *strm, int last)
+{
+ struct lzh_dec *ds = strm->ds;
+ int avail_in;
+ int r;
+
+ if (ds->error)
+ return (ds->error);
+
+ avail_in = strm->avail_in;
+ do {
+ if (ds->state < ST_GET_LITERAL)
+ r = lzh_read_blocks(strm, last);
+ else
+ r = lzh_decode_blocks(strm, last);
+ } while (r == 100);
+ strm->total_in += avail_in - strm->avail_in;
+ return (r);
+}
+
+static void
+lzh_emit_window(struct lzh_stream *strm, size_t s)
+{
+ strm->ref_ptr = strm->ds->w_buff;
+ strm->avail_out = (int)s;
+ strm->total_out += s;
+}
+
+static int
+lzh_read_blocks(struct lzh_stream *strm, int last)
+{
+ struct lzh_dec *ds = strm->ds;
+ struct lzh_br *br = &(ds->br);
+ int c = 0, i;
+ unsigned rbits;
+
+ for (;;) {
+ switch (ds->state) {
+ case ST_RD_BLOCK:
+ /*
+ * Read a block number indicates how many blocks
+ * we will handle. The block is composed of a
+ * literal and a match, sometimes a literal only
+ * in particular, there are no reference data at
+ * the beginning of the decompression.
+ */
+ if (!lzh_br_read_ahead_0(strm, br, 16)) {
+ if (!last)
+ /* We need following data. */
+ return (ARCHIVE_OK);
+ if (lzh_br_has(br, 8)) {
+ /*
+ * It seems there are extra bits.
+ * 1. Compressed data is broken.
+ * 2. `last' flag does not properly
+ * set.
+ */
+ goto failed;
+ }
+ if (ds->w_pos > 0) {
+ lzh_emit_window(strm, ds->w_pos);
+ ds->w_pos = 0;
+ return (ARCHIVE_OK);
+ }
+ /* End of compressed data; we have completely
+ * handled all compressed data. */
+ return (ARCHIVE_EOF);
+ }
+ ds->blocks_avail = lzh_br_bits(br, 16);
+ if (ds->blocks_avail == 0)
+ goto failed;
+ lzh_br_consume(br, 16);
+ /*
+ * Read a literal table compressed in huffman
+ * coding.
+ */
+ ds->pt.len_size = ds->literal_pt_len_size;
+ ds->pt.len_bits = ds->literal_pt_len_bits;
+ ds->reading_position = 0;
+ /* FALL THROUGH */
+ case ST_RD_PT_1:
+ /* Note: ST_RD_PT_1, ST_RD_PT_2 and ST_RD_PT_4 are
+ * used in reading both a literal table and a
+ * position table. */
+ if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) {
+ if (last)
+ goto failed;/* Truncated data. */
+ ds->state = ST_RD_PT_1;
+ return (ARCHIVE_OK);
+ }
+ ds->pt.len_avail = lzh_br_bits(br, ds->pt.len_bits);
+ lzh_br_consume(br, ds->pt.len_bits);
+ /* FALL THROUGH */
+ case ST_RD_PT_2:
+ if (ds->pt.len_avail == 0) {
+ /* There is no bitlen. */
+ if (!lzh_br_read_ahead(strm, br,
+ ds->pt.len_bits)) {
+ if (last)
+ goto failed;/* Truncated data.*/
+ ds->state = ST_RD_PT_2;
+ return (ARCHIVE_OK);
+ }
+ if (!lzh_make_fake_table(&(ds->pt),
+ lzh_br_bits(br, ds->pt.len_bits)))
+ goto failed;/* Invalid data. */
+ lzh_br_consume(br, ds->pt.len_bits);
+ if (ds->reading_position)
+ ds->state = ST_GET_LITERAL;
+ else
+ ds->state = ST_RD_LITERAL_1;
+ break;
+ } else if (ds->pt.len_avail > ds->pt.len_size)
+ goto failed;/* Invalid data. */
+ ds->loop = 0;
+ memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
+ if (ds->pt.len_avail < 3 ||
+ ds->pt.len_size == ds->pos_pt_len_size) {
+ ds->state = ST_RD_PT_4;
+ break;
+ }
+ /* FALL THROUGH */
+ case ST_RD_PT_3:
+ ds->loop = lzh_read_pt_bitlen(strm, ds->loop, 3);
+ if (ds->loop < 3) {
+ if (ds->loop < 0 || last)
+ goto failed;/* Invalid data. */
+ /* Not completed, get following data. */
+ ds->state = ST_RD_PT_3;
+ return (ARCHIVE_OK);
+ }
+ /* There are some null in bitlen of the literal. */
+ if (!lzh_br_read_ahead(strm, br, 2)) {
+ if (last)
+ goto failed;/* Truncated data. */
+ ds->state = ST_RD_PT_3;
+ return (ARCHIVE_OK);
+ }
+ c = lzh_br_bits(br, 2);
+ lzh_br_consume(br, 2);
+ if (c > ds->pt.len_avail - 3)
+ goto failed;/* Invalid data. */
+ for (i = 3; c-- > 0 ;)
+ ds->pt.bitlen[i++] = 0;
+ ds->loop = i;
+ /* FALL THROUGH */
+ case ST_RD_PT_4:
+ ds->loop = lzh_read_pt_bitlen(strm, ds->loop,
+ ds->pt.len_avail);
+ if (ds->loop < ds->pt.len_avail) {
+ if (ds->loop < 0 || last)
+ goto failed;/* Invalid data. */
+ /* Not completed, get following data. */
+ ds->state = ST_RD_PT_4;
+ return (ARCHIVE_OK);
+ }
+ if (!lzh_make_huffman_table(&(ds->pt)))
+ goto failed;/* Invalid data */
+ if (ds->reading_position) {
+ ds->state = ST_GET_LITERAL;
+ break;
+ }
+ /* FALL THROUGH */
+ case ST_RD_LITERAL_1:
+ if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) {
+ if (last)
+ goto failed;/* Truncated data. */
+ ds->state = ST_RD_LITERAL_1;
+ return (ARCHIVE_OK);
+ }
+ ds->lt.len_avail = lzh_br_bits(br, ds->lt.len_bits);
+ lzh_br_consume(br, ds->lt.len_bits);
+ /* FALL THROUGH */
+ case ST_RD_LITERAL_2:
+ if (ds->lt.len_avail == 0) {
+ /* There is no bitlen. */
+ if (!lzh_br_read_ahead(strm, br,
+ ds->lt.len_bits)) {
+ if (last)
+ goto failed;/* Truncated data.*/
+ ds->state = ST_RD_LITERAL_2;
+ return (ARCHIVE_OK);
+ }
+ if (!lzh_make_fake_table(&(ds->lt),
+ lzh_br_bits(br, ds->lt.len_bits)))
+ goto failed;/* Invalid data */
+ lzh_br_consume(br, ds->lt.len_bits);
+ ds->state = ST_RD_POS_DATA_1;
+ break;
+ } else if (ds->lt.len_avail > ds->lt.len_size)
+ goto failed;/* Invalid data */
+ ds->loop = 0;
+ memset(ds->lt.freq, 0, sizeof(ds->lt.freq));
+ /* FALL THROUGH */
+ case ST_RD_LITERAL_3:
+ i = ds->loop;
+ while (i < ds->lt.len_avail) {
+ if (!lzh_br_read_ahead(strm, br,
+ ds->pt.max_bits)) {
+ if (last)
+ goto failed;/* Truncated data.*/
+ ds->loop = i;
+ ds->state = ST_RD_LITERAL_3;
+ return (ARCHIVE_OK);
+ }
+ rbits = lzh_br_bits(br, ds->pt.max_bits);
+ c = lzh_decode_huffman(&(ds->pt), rbits);
+ if (c > 2) {
+ /* Note: 'c' will never be more than
+ * eighteen since it's limited by
+ * PT_BITLEN_SIZE, which is being set
+ * to ds->pt.len_size through
+ * ds->literal_pt_len_size. */
+ lzh_br_consume(br, ds->pt.bitlen[c]);
+ c -= 2;
+ ds->lt.freq[c]++;
+ ds->lt.bitlen[i++] = c;
+ } else if (c == 0) {
+ lzh_br_consume(br, ds->pt.bitlen[c]);
+ ds->lt.bitlen[i++] = 0;
+ } else {
+ /* c == 1 or c == 2 */
+ int n = (c == 1)?4:9;
+ if (!lzh_br_read_ahead(strm, br,
+ ds->pt.bitlen[c] + n)) {
+ if (last) /* Truncated data. */
+ goto failed;
+ ds->loop = i;
+ ds->state = ST_RD_LITERAL_3;
+ return (ARCHIVE_OK);
+ }
+ lzh_br_consume(br, ds->pt.bitlen[c]);
+ c = lzh_br_bits(br, n);
+ lzh_br_consume(br, n);
+ c += (n == 4)?3:20;
+ if (i + c > ds->lt.len_avail)
+ goto failed;/* Invalid data */
+ memset(&(ds->lt.bitlen[i]), 0, c);
+ i += c;
+ }
+ }
+ if (i > ds->lt.len_avail ||
+ !lzh_make_huffman_table(&(ds->lt)))
+ goto failed;/* Invalid data */
+ /* FALL THROUGH */
+ case ST_RD_POS_DATA_1:
+ /*
+ * Read a position table compressed in huffman
+ * coding.
+ */
+ ds->pt.len_size = ds->pos_pt_len_size;
+ ds->pt.len_bits = ds->pos_pt_len_bits;
+ ds->reading_position = 1;
+ ds->state = ST_RD_PT_1;
+ break;
+ case ST_GET_LITERAL:
+ return (100);
+ }
+ }
+failed:
+ return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzh_decode_blocks(struct lzh_stream *strm, int last)
+{
+ struct lzh_dec *ds = strm->ds;
+ struct lzh_br bre = ds->br;
+ struct huffman *lt = &(ds->lt);
+ struct huffman *pt = &(ds->pt);
+ unsigned char *w_buff = ds->w_buff;
+ unsigned char *lt_bitlen = lt->bitlen;
+ unsigned char *pt_bitlen = pt->bitlen;
+ int blocks_avail = ds->blocks_avail, c = 0;
+ int copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+ int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+ int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits;
+ int state = ds->state;
+
+ for (;;) {
+ switch (state) {
+ case ST_GET_LITERAL:
+ for (;;) {
+ if (blocks_avail == 0) {
+ /* We have decoded all blocks.
+ * Let's handle next blocks. */
+ ds->state = ST_RD_BLOCK;
+ ds->br = bre;
+ ds->blocks_avail = 0;
+ ds->w_pos = w_pos;
+ ds->copy_pos = 0;
+ return (100);
+ }
+
+ /* lzh_br_read_ahead() always try to fill the
+ * cache buffer up. In specific situation we
+ * are close to the end of the data, the cache
+ * buffer will not be full and thus we have to
+ * determine if the cache buffer has some bits
+ * as much as we need after lzh_br_read_ahead()
+ * failed. */
+ if (!lzh_br_read_ahead(strm, &bre,
+ lt_max_bits)) {
+ if (!last)
+ goto next_data;
+ /* Remaining bits are less than
+ * maximum bits(lt.max_bits) but maybe
+ * it still remains as much as we need,
+ * so we should try to use it with
+ * dummy bits. */
+ c = lzh_decode_huffman(lt,
+ lzh_br_bits_forced(&bre,
+ lt_max_bits));
+ lzh_br_consume(&bre, lt_bitlen[c]);
+ if (!lzh_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ c = lzh_decode_huffman(lt,
+ lzh_br_bits(&bre, lt_max_bits));
+ lzh_br_consume(&bre, lt_bitlen[c]);
+ }
+ blocks_avail--;
+ if (c > UCHAR_MAX)
+ /* Current block is a match data. */
+ break;
+ /*
+ * 'c' is exactly a literal code.
+ */
+ /* Save a decoded code to reference it
+ * afterward. */
+ w_buff[w_pos] = c;
+ if (++w_pos >= w_size) {
+ w_pos = 0;
+ lzh_emit_window(strm, w_size);
+ goto next_data;
+ }
+ }
+ /* 'c' is the length of a match pattern we have
+ * already extracted, which has be stored in
+ * window(ds->w_buff). */
+ copy_len = c - (UCHAR_MAX + 1) + MINMATCH;
+ /* FALL THROUGH */
+ case ST_GET_POS_1:
+ /*
+ * Get a reference position.
+ */
+ if (!lzh_br_read_ahead(strm, &bre, pt_max_bits)) {
+ if (!last) {
+ state = ST_GET_POS_1;
+ ds->copy_len = copy_len;
+ goto next_data;
+ }
+ copy_pos = lzh_decode_huffman(pt,
+ lzh_br_bits_forced(&bre, pt_max_bits));
+ lzh_br_consume(&bre, pt_bitlen[copy_pos]);
+ if (!lzh_br_has(&bre, 0))
+ goto failed;/* Over read. */
+ } else {
+ copy_pos = lzh_decode_huffman(pt,
+ lzh_br_bits(&bre, pt_max_bits));
+ lzh_br_consume(&bre, pt_bitlen[copy_pos]);
+ }
+ /* FALL THROUGH */
+ case ST_GET_POS_2:
+ if (copy_pos > 1) {
+ /* We need an additional adjustment number to
+ * the position. */
+ int p = copy_pos - 1;
+ if (!lzh_br_read_ahead(strm, &bre, p)) {
+ if (last)
+ goto failed;/* Truncated data.*/
+ state = ST_GET_POS_2;
+ ds->copy_len = copy_len;
+ ds->copy_pos = copy_pos;
+ goto next_data;
+ }
+ copy_pos = (1 << p) + lzh_br_bits(&bre, p);
+ lzh_br_consume(&bre, p);
+ }
+ /* The position is actually a distance from the last
+ * code we had extracted and thus we have to convert
+ * it to a position of the window. */
+ copy_pos = (w_pos - copy_pos - 1) & w_mask;
+ /* FALL THROUGH */
+ case ST_COPY_DATA:
+ /*
+ * Copy `copy_len' bytes as extracted data from
+ * the window into the output buffer.
+ */
+ for (;;) {
+ int l;
+
+ l = copy_len;
+ if (copy_pos > w_pos) {
+ if (l > w_size - copy_pos)
+ l = w_size - copy_pos;
+ } else {
+ if (l > w_size - w_pos)
+ l = w_size - w_pos;
+ }
+ if ((copy_pos + l < w_pos)
+ || (w_pos + l < copy_pos)) {
+ /* No overlap. */
+ memcpy(w_buff + w_pos,
+ w_buff + copy_pos, l);
+ } else {
+ const unsigned char *s;
+ unsigned char *d;
+ int li;
+
+ d = w_buff + w_pos;
+ s = w_buff + copy_pos;
+ for (li = 0; li < l-1;) {
+ d[li] = s[li];li++;
+ d[li] = s[li];li++;
+ }
+ if (li < l)
+ d[li] = s[li];
+ }
+ w_pos += l;
+ if (w_pos == w_size) {
+ w_pos = 0;
+ lzh_emit_window(strm, w_size);
+ if (copy_len <= l)
+ state = ST_GET_LITERAL;
+ else {
+ state = ST_COPY_DATA;
+ ds->copy_len = copy_len - l;
+ ds->copy_pos =
+ (copy_pos + l) & w_mask;
+ }
+ goto next_data;
+ }
+ if (copy_len <= l)
+ /* A copy of current pattern ended. */
+ break;
+ copy_len -= l;
+ copy_pos = (copy_pos + l) & w_mask;
+ }
+ state = ST_GET_LITERAL;
+ break;
+ }
+ }
+failed:
+ return (ds->error = ARCHIVE_FAILED);
+next_data:
+ ds->br = bre;
+ ds->blocks_avail = blocks_avail;
+ ds->state = state;
+ ds->w_pos = w_pos;
+ return (ARCHIVE_OK);
+}
+
+static int
+lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+{
+ int bits;
+
+ if (hf->bitlen == NULL) {
+ hf->bitlen = malloc(len_size * sizeof(hf->bitlen[0]));
+ if (hf->bitlen == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ if (hf->tbl == NULL) {
+ if (tbl_bits < HTBL_BITS)
+ bits = tbl_bits;
+ else
+ bits = HTBL_BITS;
+ hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0]));
+ if (hf->tbl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ if (hf->tree == NULL && tbl_bits > HTBL_BITS) {
+ hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4);
+ hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0]));
+ if (hf->tree == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ hf->len_size = (int)len_size;
+ hf->tbl_bits = tbl_bits;
+ return (ARCHIVE_OK);
+}
+
+static void
+lzh_huffman_free(struct huffman *hf)
+{
+ free(hf->bitlen);
+ free(hf->tbl);
+ free(hf->tree);
+}
+
+static const char bitlen_tbl[0x400] = {
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 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,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16, 0
+};
+static int
+lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end)
+{
+ struct lzh_dec *ds = strm->ds;
+ struct lzh_br *br = &(ds->br);
+ int c, i;
+
+ for (i = start; i < end; ) {
+ /*
+ * bit pattern the number we need
+ * 000 -> 0
+ * 001 -> 1
+ * 010 -> 2
+ * ...
+ * 110 -> 6
+ * 1110 -> 7
+ * 11110 -> 8
+ * ...
+ * 1111111111110 -> 16
+ */
+ if (!lzh_br_read_ahead(strm, br, 3))
+ return (i);
+ if ((c = lzh_br_bits(br, 3)) == 7) {
+ if (!lzh_br_read_ahead(strm, br, 13))
+ return (i);
+ c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF];
+ if (c)
+ lzh_br_consume(br, c - 3);
+ else
+ return (-1);/* Invalid data. */
+ } else
+ lzh_br_consume(br, 3);
+ ds->pt.bitlen[i++] = c;
+ ds->pt.freq[c]++;
+ }
+ return (i);
+}
+
+static int
+lzh_make_fake_table(struct huffman *hf, uint16_t c)
+{
+ if (c >= hf->len_size)
+ return (0);
+ hf->tbl[0] = c;
+ hf->max_bits = 0;
+ hf->shift_bits = 0;
+ hf->bitlen[hf->tbl[0]] = 0;
+ return (1);
+}
+
+/*
+ * Make a huffman coding table.
+ */
+static int
+lzh_make_huffman_table(struct huffman *hf)
+{
+ uint16_t *tbl;
+ const unsigned char *bitlen;
+ int bitptn[17], weight[17];
+ int i, maxbits = 0, ptn, tbl_size, w;
+ int diffbits, len_avail;
+
+ /*
+ * Initialize bit patterns.
+ */
+ ptn = 0;
+ for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
+ bitptn[i] = ptn;
+ weight[i] = w;
+ if (hf->freq[i]) {
+ ptn += hf->freq[i] * w;
+ maxbits = i;
+ }
+ }
+ if (ptn != 0x10000 || maxbits > hf->tbl_bits)
+ return (0);/* Invalid */
+
+ hf->max_bits = maxbits;
+
+ /*
+ * Cut out extra bits which we won't house in the table.
+ * This preparation reduces the same calculation in the for-loop
+ * making the table.
+ */
+ if (maxbits < 16) {
+ int ebits = 16 - maxbits;
+ for (i = 1; i <= maxbits; i++) {
+ bitptn[i] >>= ebits;
+ weight[i] >>= ebits;
+ }
+ }
+ if (maxbits > HTBL_BITS) {
+ unsigned htbl_max;
+ uint16_t *p;
+
+ diffbits = maxbits - HTBL_BITS;
+ for (i = 1; i <= HTBL_BITS; i++) {
+ bitptn[i] >>= diffbits;
+ weight[i] >>= diffbits;
+ }
+ htbl_max = bitptn[HTBL_BITS] +
+ weight[HTBL_BITS] * hf->freq[HTBL_BITS];
+ p = &(hf->tbl[htbl_max]);
+ while (p < &hf->tbl[1U<<HTBL_BITS])
+ *p++ = 0;
+ } else
+ diffbits = 0;
+ hf->shift_bits = diffbits;
+
+ /*
+ * Make the table.
+ */
+ tbl_size = 1 << HTBL_BITS;
+ tbl = hf->tbl;
+ bitlen = hf->bitlen;
+ len_avail = hf->len_avail;
+ hf->tree_used = 0;
+ for (i = 0; i < len_avail; i++) {
+ uint16_t *p;
+ int len, cnt;
+ uint16_t bit;
+ int extlen;
+ struct htree_t *ht;
+
+ if (bitlen[i] == 0)
+ continue;
+ /* Get a bit pattern */
+ len = bitlen[i];
+ ptn = bitptn[len];
+ cnt = weight[len];
+ if (len <= HTBL_BITS) {
+ /* Calculate next bit pattern */
+ if ((bitptn[len] = ptn + cnt) > tbl_size)
+ return (0);/* Invalid */
+ /* Update the table */
+ p = &(tbl[ptn]);
+ if (cnt > 7) {
+ uint16_t *pc;
+
+ cnt -= 8;
+ pc = &p[cnt];
+ pc[0] = (uint16_t)i;
+ pc[1] = (uint16_t)i;
+ pc[2] = (uint16_t)i;
+ pc[3] = (uint16_t)i;
+ pc[4] = (uint16_t)i;
+ pc[5] = (uint16_t)i;
+ pc[6] = (uint16_t)i;
+ pc[7] = (uint16_t)i;
+ if (cnt > 7) {
+ cnt -= 8;
+ memcpy(&p[cnt], pc,
+ 8 * sizeof(uint16_t));
+ pc = &p[cnt];
+ while (cnt > 15) {
+ cnt -= 16;
+ memcpy(&p[cnt], pc,
+ 16 * sizeof(uint16_t));
+ }
+ }
+ if (cnt)
+ memcpy(p, pc, cnt * sizeof(uint16_t));
+ } else {
+ while (cnt > 1) {
+ p[--cnt] = (uint16_t)i;
+ p[--cnt] = (uint16_t)i;
+ }
+ if (cnt)
+ p[--cnt] = (uint16_t)i;
+ }
+ continue;
+ }
+
+ /*
+ * A bit length is too big to be housed to a direct table,
+ * so we use a tree model for its extra bits.
+ */
+ bitptn[len] = ptn + cnt;
+ bit = 1U << (diffbits -1);
+ extlen = len - HTBL_BITS;
+
+ p = &(tbl[ptn >> diffbits]);
+ if (*p == 0) {
+ *p = len_avail + hf->tree_used;
+ ht = &(hf->tree[hf->tree_used++]);
+ if (hf->tree_used > hf->tree_avail)
+ return (0);/* Invalid */
+ ht->left = 0;
+ ht->right = 0;
+ } else {
+ if (*p < len_avail ||
+ *p >= (len_avail + hf->tree_used))
+ return (0);/* Invalid */
+ ht = &(hf->tree[*p - len_avail]);
+ }
+ while (--extlen > 0) {
+ if (ptn & bit) {
+ if (ht->left < len_avail) {
+ ht->left = len_avail + hf->tree_used;
+ ht = &(hf->tree[hf->tree_used++]);
+ if (hf->tree_used > hf->tree_avail)
+ return (0);/* Invalid */
+ ht->left = 0;
+ ht->right = 0;
+ } else {
+ ht = &(hf->tree[ht->left - len_avail]);
+ }
+ } else {
+ if (ht->right < len_avail) {
+ ht->right = len_avail + hf->tree_used;
+ ht = &(hf->tree[hf->tree_used++]);
+ if (hf->tree_used > hf->tree_avail)
+ return (0);/* Invalid */
+ ht->left = 0;
+ ht->right = 0;
+ } else {
+ ht = &(hf->tree[ht->right - len_avail]);
+ }
+ }
+ bit >>= 1;
+ }
+ if (ptn & bit) {
+ if (ht->left != 0)
+ return (0);/* Invalid */
+ ht->left = (uint16_t)i;
+ } else {
+ if (ht->right != 0)
+ return (0);/* Invalid */
+ ht->right = (uint16_t)i;
+ }
+ }
+ return (1);
+}
+
+static int
+lzh_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c)
+{
+ struct htree_t *ht;
+ int extlen;
+
+ ht = hf->tree;
+ extlen = hf->shift_bits;
+ while (c >= hf->len_avail) {
+ c -= hf->len_avail;
+ if (extlen-- <= 0 || c >= hf->tree_used)
+ return (0);
+ if (rbits & (1U << extlen))
+ c = ht[c].left;
+ else
+ c = ht[c].right;
+ }
+ return (c);
+}
+
+static inline int
+lzh_decode_huffman(struct huffman *hf, unsigned rbits)
+{
+ int c;
+ /*
+ * At first search an index table for a bit pattern.
+ * If it fails, search a huffman tree for.
+ */
+ c = hf->tbl[rbits >> hf->shift_bits];
+ if (c < hf->len_avail || hf->len_avail == 0)
+ return (c);
+ /* This bit pattern needs to be found out at a huffman tree. */
+ return (lzh_decode_huffman_tree(hf, rbits, c));
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_mtree.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_mtree.c
new file mode 100644
index 0000000000..a5fa30e3c2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_mtree.c
@@ -0,0 +1,2156 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 201165 2009-12-29 05:52:13Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stddef.h>
+/* #include <stdint.h> */ /* See archive_platform.h */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_read_private.h"
+#include "archive_string.h"
+#include "archive_pack_dev.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#define MTREE_HAS_DEVICE 0x0001
+#define MTREE_HAS_FFLAGS 0x0002
+#define MTREE_HAS_GID 0x0004
+#define MTREE_HAS_GNAME 0x0008
+#define MTREE_HAS_MTIME 0x0010
+#define MTREE_HAS_NLINK 0x0020
+#define MTREE_HAS_PERM 0x0040
+#define MTREE_HAS_SIZE 0x0080
+#define MTREE_HAS_TYPE 0x0100
+#define MTREE_HAS_UID 0x0200
+#define MTREE_HAS_UNAME 0x0400
+
+#define MTREE_HAS_OPTIONAL 0x0800
+#define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */
+
+#define MAX_LINE_LEN (1024 * 1024)
+
+struct mtree_option {
+ struct mtree_option *next;
+ char *value;
+};
+
+struct mtree_entry {
+ struct archive_rb_node rbnode;
+ struct mtree_entry *next_dup;
+ struct mtree_entry *next;
+ struct mtree_option *options;
+ char *name;
+ char full;
+ char used;
+};
+
+struct mtree {
+ struct archive_string line;
+ size_t buffsize;
+ char *buff;
+ int64_t offset;
+ int fd;
+ int archive_format;
+ const char *archive_format_name;
+ struct mtree_entry *entries;
+ struct mtree_entry *this_entry;
+ struct archive_rb_tree entry_rbtree;
+ struct archive_string current_dir;
+ struct archive_string contents_name;
+
+ struct archive_entry_linkresolver *resolver;
+ struct archive_rb_tree rbtree;
+
+ int64_t cur_size;
+ char checkfs;
+};
+
+static int bid_keycmp(const char *, const char *, ssize_t);
+static int cleanup(struct archive_read *);
+static int detect_form(struct archive_read *, int *);
+static int mtree_bid(struct archive_read *, int);
+static int parse_file(struct archive_read *, struct archive_entry *,
+ struct mtree *, struct mtree_entry *, int *);
+static void parse_escapes(char *, struct mtree_entry *);
+static int parse_line(struct archive_read *, struct archive_entry *,
+ struct mtree *, struct mtree_entry *, int *);
+static int parse_keyword(struct archive_read *, struct mtree *,
+ struct archive_entry *, struct mtree_option *, int *);
+static int read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset);
+static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t);
+static int skip(struct archive_read *a);
+static int read_header(struct archive_read *,
+ struct archive_entry *);
+static int64_t mtree_atol(char **, int base);
+#ifndef HAVE_STRNLEN
+static size_t mtree_strnlen(const char *, size_t);
+#endif
+
+/*
+ * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them
+ * here. TODO: Move this to configure time, but be careful
+ * about cross-compile environments.
+ */
+static int64_t
+get_time_t_max(void)
+{
+#if defined(TIME_T_MAX)
+ return TIME_T_MAX;
+#else
+ /* ISO C allows time_t to be a floating-point type,
+ but POSIX requires an integer type. The following
+ should work on any system that follows the POSIX
+ conventions. */
+ if (((time_t)0) < ((time_t)-1)) {
+ /* Time_t is unsigned */
+ return (~(time_t)0);
+ } else {
+ /* Time_t is signed. */
+ /* Assume it's the same as int64_t or int32_t */
+ if (sizeof(time_t) == sizeof(int64_t)) {
+ return (time_t)INT64_MAX;
+ } else {
+ return (time_t)INT32_MAX;
+ }
+ }
+#endif
+}
+
+static int64_t
+get_time_t_min(void)
+{
+#if defined(TIME_T_MIN)
+ return TIME_T_MIN;
+#else
+ if (((time_t)0) < ((time_t)-1)) {
+ /* Time_t is unsigned */
+ return (time_t)0;
+ } else {
+ /* Time_t is signed. */
+ if (sizeof(time_t) == sizeof(int64_t)) {
+ return (time_t)INT64_MIN;
+ } else {
+ return (time_t)INT32_MIN;
+ }
+ }
+#endif
+}
+
+#ifdef HAVE_STRNLEN
+#define mtree_strnlen(a,b) strnlen(a,b)
+#else
+static size_t
+mtree_strnlen(const char *p, size_t maxlen)
+{
+ size_t i;
+
+ for (i = 0; i <= maxlen; i++) {
+ if (p[i] == 0)
+ break;
+ }
+ if (i > maxlen)
+ return (-1);/* invalid */
+ return (i);
+}
+#endif
+
+static int
+archive_read_format_mtree_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct mtree *mtree;
+
+ mtree = (struct mtree *)(a->format->data);
+ if (strcmp(key, "checkfs") == 0) {
+ /* Allows to read information missing from the mtree from the file system */
+ if (val == NULL || val[0] == 0) {
+ mtree->checkfs = 0;
+ } else {
+ mtree->checkfs = 1;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static void
+free_options(struct mtree_option *head)
+{
+ struct mtree_option *next;
+
+ for (; head != NULL; head = next) {
+ next = head->next;
+ free(head->value);
+ free(head);
+ }
+}
+
+static int
+mtree_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
+ const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
+
+ return (strcmp(e1->name, e2->name));
+}
+
+static int
+mtree_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct mtree_entry *e = (const struct mtree_entry *)n;
+
+ return (strcmp(e->name, key));
+}
+
+int
+archive_read_support_format_mtree(struct archive *_a)
+{
+ static const struct archive_rb_tree_ops rb_ops = {
+ mtree_cmp_node, mtree_cmp_key,
+ };
+ struct archive_read *a = (struct archive_read *)_a;
+ struct mtree *mtree;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_mtree");
+
+ mtree = (struct mtree *)calloc(1, sizeof(*mtree));
+ if (mtree == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate mtree data");
+ return (ARCHIVE_FATAL);
+ }
+ mtree->checkfs = 0;
+ mtree->fd = -1;
+
+ __archive_rb_tree_init(&mtree->rbtree, &rb_ops);
+
+ r = __archive_read_register_format(a, mtree, "mtree",
+ mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL);
+
+ if (r != ARCHIVE_OK)
+ free(mtree);
+ return (ARCHIVE_OK);
+}
+
+static int
+cleanup(struct archive_read *a)
+{
+ struct mtree *mtree;
+ struct mtree_entry *p, *q;
+
+ mtree = (struct mtree *)(a->format->data);
+
+ p = mtree->entries;
+ while (p != NULL) {
+ q = p->next;
+ free(p->name);
+ free_options(p->options);
+ free(p);
+ p = q;
+ }
+ archive_string_free(&mtree->line);
+ archive_string_free(&mtree->current_dir);
+ archive_string_free(&mtree->contents_name);
+ archive_entry_linkresolver_free(mtree->resolver);
+
+ free(mtree->buff);
+ free(mtree);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+get_line_size(const char *b, ssize_t avail, ssize_t *nlsize)
+{
+ ssize_t len;
+
+ len = 0;
+ while (len < avail) {
+ switch (*b) {
+ case '\0':/* Non-ascii character or control character. */
+ if (nlsize != NULL)
+ *nlsize = 0;
+ return (-1);
+ case '\r':
+ if (avail-len > 1 && b[1] == '\n') {
+ if (nlsize != NULL)
+ *nlsize = 2;
+ return (len+2);
+ }
+ /* FALL THROUGH */
+ case '\n':
+ if (nlsize != NULL)
+ *nlsize = 1;
+ return (len+1);
+ default:
+ b++;
+ len++;
+ break;
+ }
+ }
+ if (nlsize != NULL)
+ *nlsize = 0;
+ return (avail);
+}
+
+/*
+ * <---------------- ravail --------------------->
+ * <-- diff ------> <--- avail ----------------->
+ * <---- len ----------->
+ * | Previous lines | line being parsed nl extra |
+ * ^
+ * b
+ *
+ */
+static ssize_t
+next_line(struct archive_read *a,
+ const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
+{
+ ssize_t len;
+ int quit;
+
+ quit = 0;
+ if (*avail == 0) {
+ *nl = 0;
+ len = 0;
+ } else
+ len = get_line_size(*b, *avail, nl);
+ /*
+ * Read bytes more while it does not reach the end of line.
+ */
+ while (*nl == 0 && len == *avail && !quit) {
+ ssize_t diff = *ravail - *avail;
+ size_t nbytes_req = (*ravail+1023) & ~1023U;
+ ssize_t tested;
+
+ /*
+ * Place an arbitrary limit on the line length.
+ * mtree is almost free-form input and without line length limits,
+ * it can consume a lot of memory.
+ */
+ if (len >= MAX_LINE_LEN)
+ return (-1);
+
+ /* Increase reading bytes if it is not enough to at least
+ * new two lines. */
+ if (nbytes_req < (size_t)*ravail + 160)
+ nbytes_req <<= 1;
+
+ *b = __archive_read_ahead(a, nbytes_req, avail);
+ if (*b == NULL) {
+ if (*ravail >= *avail)
+ return (0);
+ /* Reading bytes reaches the end of file. */
+ *b = __archive_read_ahead(a, *avail, avail);
+ quit = 1;
+ }
+ *ravail = *avail;
+ *b += diff;
+ *avail -= diff;
+ tested = len;/* Skip some bytes we already determined. */
+ len = get_line_size(*b + len, *avail - len, nl);
+ if (len >= 0)
+ len += tested;
+ }
+ return (len);
+}
+
+/*
+ * Compare characters with a mtree keyword.
+ * Returns the length of a mtree keyword if matched.
+ * Returns 0 if not matched.
+ */
+static int
+bid_keycmp(const char *p, const char *key, ssize_t len)
+{
+ int match_len = 0;
+
+ while (len > 0 && *p && *key) {
+ if (*p == *key) {
+ --len;
+ ++p;
+ ++key;
+ ++match_len;
+ continue;
+ }
+ return (0);/* Not match */
+ }
+ if (*key != '\0')
+ return (0);/* Not match */
+
+ /* A following character should be specified characters */
+ if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' ||
+ p[0] == '\n' || p[0] == '\r' ||
+ (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')))
+ return (match_len);
+ return (0);/* Not match */
+}
+
+/*
+ * Test whether the characters 'p' has is mtree keyword.
+ * Returns the length of a detected keyword.
+ * Returns 0 if any keywords were not found.
+ */
+static int
+bid_keyword(const char *p, ssize_t len)
+{
+ static const char * const keys_c[] = {
+ "content", "contents", "cksum", NULL
+ };
+ static const char * const keys_df[] = {
+ "device", "flags", NULL
+ };
+ static const char * const keys_g[] = {
+ "gid", "gname", NULL
+ };
+ static const char * const keys_il[] = {
+ "ignore", "inode", "link", NULL
+ };
+ static const char * const keys_m[] = {
+ "md5", "md5digest", "mode", NULL
+ };
+ static const char * const keys_no[] = {
+ "nlink", "nochange", "optional", NULL
+ };
+ static const char * const keys_r[] = {
+ "resdevice", "rmd160", "rmd160digest", NULL
+ };
+ static const char * const keys_s[] = {
+ "sha1", "sha1digest",
+ "sha256", "sha256digest",
+ "sha384", "sha384digest",
+ "sha512", "sha512digest",
+ "size", NULL
+ };
+ static const char * const keys_t[] = {
+ "tags", "time", "type", NULL
+ };
+ static const char * const keys_u[] = {
+ "uid", "uname", NULL
+ };
+ const char * const *keys;
+ int i;
+
+ switch (*p) {
+ case 'c': keys = keys_c; break;
+ case 'd': case 'f': keys = keys_df; break;
+ case 'g': keys = keys_g; break;
+ case 'i': case 'l': keys = keys_il; break;
+ case 'm': keys = keys_m; break;
+ case 'n': case 'o': keys = keys_no; break;
+ case 'r': keys = keys_r; break;
+ case 's': keys = keys_s; break;
+ case 't': keys = keys_t; break;
+ case 'u': keys = keys_u; break;
+ default: return (0);/* Unknown key */
+ }
+
+ for (i = 0; keys[i] != NULL; i++) {
+ int l = bid_keycmp(p, keys[i], len);
+ if (l > 0)
+ return (l);
+ }
+ return (0);/* Unknown key */
+}
+
+/*
+ * Test whether there is a set of mtree keywords.
+ * Returns the number of keyword.
+ * Returns -1 if we got incorrect sequence.
+ * This function expects a set of "<space characters>keyword=value".
+ * When "unset" is specified, expects a set of "<space characters>keyword".
+ */
+static int
+bid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path)
+{
+ int l;
+ int keycnt = 0;
+
+ while (len > 0 && *p) {
+ int blank = 0;
+
+ /* Test whether there are blank characters in the line. */
+ while (len >0 && (*p == ' ' || *p == '\t')) {
+ ++p;
+ --len;
+ blank = 1;
+ }
+ if (*p == '\n' || *p == '\r')
+ break;
+ if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))
+ break;
+ if (!blank && !last_is_path) /* No blank character. */
+ return (-1);
+ if (last_is_path && len == 0)
+ return (keycnt);
+
+ if (unset) {
+ l = bid_keycmp(p, "all", len);
+ if (l > 0)
+ return (1);
+ }
+ /* Test whether there is a correct key in the line. */
+ l = bid_keyword(p, len);
+ if (l == 0)
+ return (-1);/* Unknown keyword was found. */
+ p += l;
+ len -= l;
+ keycnt++;
+
+ /* Skip value */
+ if (*p == '=') {
+ int value = 0;
+ ++p;
+ --len;
+ while (len > 0 && *p != ' ' && *p != '\t') {
+ ++p;
+ --len;
+ value = 1;
+ }
+ /* A keyword should have a its value unless
+ * "/unset" operation. */
+ if (!unset && value == 0)
+ return (-1);
+ }
+ }
+ return (keycnt);
+}
+
+static int
+bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path)
+{
+ int f = 0;
+ static const unsigned char safe_char[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ /* !"$%&'()*+,-./ EXCLUSION:( )(#) */
+ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+ /* 0123456789:;<>? EXCLUSION:(=) */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
+ /* @ABCDEFGHIJKLMNO */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ /* PQRSTUVWXYZ[\]^_ */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+ /* `abcdefghijklmno */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+ /* pqrstuvwxyz{|}~ */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+ };
+ ssize_t ll;
+ const char *pp = p;
+ const char * const pp_end = pp + len;
+
+ *last_is_path = 0;
+ /*
+ * Skip the path-name which is quoted.
+ */
+ for (;pp < pp_end; ++pp) {
+ if (!safe_char[*(const unsigned char *)pp]) {
+ if (*pp != ' ' && *pp != '\t' && *pp != '\r'
+ && *pp != '\n')
+ f = 0;
+ break;
+ }
+ f = 1;
+ }
+ ll = pp_end - pp;
+
+ /* If a path-name was not found at the first, try to check
+ * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates,
+ * which places the path-name at the last. */
+ if (f == 0) {
+ const char *pb = p + len - nl;
+ int name_len = 0;
+ int slash;
+
+ /* The form D accepts only a single line for an entry. */
+ if (pb-2 >= p &&
+ pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t'))
+ return (-1);
+ if (pb-1 >= p && pb[-1] == '\\')
+ return (-1);
+
+ slash = 0;
+ while (p <= --pb && *pb != ' ' && *pb != '\t') {
+ if (!safe_char[*(const unsigned char *)pb])
+ return (-1);
+ name_len++;
+ /* The pathname should have a slash in this
+ * format. */
+ if (*pb == '/')
+ slash = 1;
+ }
+ if (name_len == 0 || slash == 0)
+ return (-1);
+ /* If '/' is placed at the first in this field, this is not
+ * a valid filename. */
+ if (pb[1] == '/')
+ return (-1);
+ ll = len - nl - name_len;
+ pp = p;
+ *last_is_path = 1;
+ }
+
+ return (bid_keyword_list(pp, ll, 0, *last_is_path));
+}
+
+#define MAX_BID_ENTRY 3
+
+static int
+mtree_bid(struct archive_read *a, int best_bid)
+{
+ const char *signature = "#mtree";
+ const char *p;
+
+ (void)best_bid; /* UNUSED */
+
+ /* Now let's look at the actual header and see if it matches. */
+ p = __archive_read_ahead(a, strlen(signature), NULL);
+ if (p == NULL)
+ return (-1);
+
+ if (memcmp(p, signature, strlen(signature)) == 0)
+ return (8 * (int)strlen(signature));
+
+ /*
+ * There is not a mtree signature. Let's try to detect mtree format.
+ */
+ return (detect_form(a, NULL));
+}
+
+static int
+detect_form(struct archive_read *a, int *is_form_d)
+{
+ const char *p;
+ ssize_t avail, ravail;
+ ssize_t len, nl;
+ int entry_cnt = 0, multiline = 0;
+ int form_D = 0;/* The archive is generated by `NetBSD mtree -D'
+ * (In this source we call it `form D') . */
+
+ if (is_form_d != NULL)
+ *is_form_d = 0;
+ p = __archive_read_ahead(a, 1, &avail);
+ if (p == NULL)
+ return (-1);
+ ravail = avail;
+ for (;;) {
+ len = next_line(a, &p, &avail, &ravail, &nl);
+ /* The terminal character of the line should be
+ * a new line character, '\r\n' or '\n'. */
+ if (len <= 0 || nl == 0)
+ break;
+ if (!multiline) {
+ /* Leading whitespace is never significant,
+ * ignore it. */
+ while (len > 0 && (*p == ' ' || *p == '\t')) {
+ ++p;
+ --avail;
+ --len;
+ }
+ /* Skip comment or empty line. */
+ if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') {
+ p += len;
+ avail -= len;
+ continue;
+ }
+ } else {
+ /* A continuance line; the terminal
+ * character of previous line was '\' character. */
+ if (bid_keyword_list(p, len, 0, 0) <= 0)
+ break;
+ if (p[len-nl-1] != '\\') {
+ if (multiline == 1 &&
+ ++entry_cnt >= MAX_BID_ENTRY)
+ break;
+ multiline = 0;
+ }
+ p += len;
+ avail -= len;
+ continue;
+ }
+ if (p[0] != '/') {
+ int last_is_path, keywords;
+
+ keywords = bid_entry(p, len, nl, &last_is_path);
+ if (keywords >= 0) {
+ if (form_D == 0) {
+ if (last_is_path)
+ form_D = 1;
+ else if (keywords > 0)
+ /* This line is not `form D'. */
+ form_D = -1;
+ } else if (form_D == 1) {
+ if (!last_is_path && keywords > 0)
+ /* This this is not `form D'
+ * and We cannot accept mixed
+ * format. */
+ break;
+ }
+ if (!last_is_path && p[len-nl-1] == '\\')
+ /* This line continues. */
+ multiline = 1;
+ else {
+ /* We've got plenty of correct lines
+ * to assume that this file is a mtree
+ * format. */
+ if (++entry_cnt >= MAX_BID_ENTRY)
+ break;
+ }
+ } else
+ break;
+ } else if (len > 4 && strncmp(p, "/set", 4) == 0) {
+ if (bid_keyword_list(p+4, len-4, 0, 0) <= 0)
+ break;
+ /* This line continues. */
+ if (p[len-nl-1] == '\\')
+ multiline = 2;
+ } else if (len > 6 && strncmp(p, "/unset", 6) == 0) {
+ if (bid_keyword_list(p+6, len-6, 1, 0) <= 0)
+ break;
+ /* This line continues. */
+ if (p[len-nl-1] == '\\')
+ multiline = 2;
+ } else
+ break;
+
+ /* Test next line. */
+ p += len;
+ avail -= len;
+ }
+ if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) {
+ if (is_form_d != NULL) {
+ if (form_D == 1)
+ *is_form_d = 1;
+ }
+ return (32);
+ }
+
+ return (0);
+}
+
+/*
+ * The extended mtree format permits multiple lines specifying
+ * attributes for each file. For those entries, only the last line
+ * is actually used. Practically speaking, that means we have
+ * to read the entire mtree file into memory up front.
+ *
+ * The parsing is done in two steps. First, it is decided if a line
+ * changes the global defaults and if it is, processed accordingly.
+ * Otherwise, the options of the line are merged with the current
+ * global options.
+ */
+static int
+add_option(struct archive_read *a, struct mtree_option **global,
+ const char *value, size_t len)
+{
+ struct mtree_option *opt;
+
+ if ((opt = malloc(sizeof(*opt))) == NULL) {
+ archive_set_error(&a->archive, errno, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ if ((opt->value = malloc(len + 1)) == NULL) {
+ free(opt);
+ archive_set_error(&a->archive, errno, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(opt->value, value, len);
+ opt->value[len] = '\0';
+ opt->next = *global;
+ *global = opt;
+ return (ARCHIVE_OK);
+}
+
+static void
+remove_option(struct mtree_option **global, const char *value, size_t len)
+{
+ struct mtree_option *iter, *last;
+
+ last = NULL;
+ for (iter = *global; iter != NULL; last = iter, iter = iter->next) {
+ if (strncmp(iter->value, value, len) == 0 &&
+ (iter->value[len] == '\0' ||
+ iter->value[len] == '='))
+ break;
+ }
+ if (iter == NULL)
+ return;
+ if (last == NULL)
+ *global = iter->next;
+ else
+ last->next = iter->next;
+
+ free(iter->value);
+ free(iter);
+}
+
+static int
+process_global_set(struct archive_read *a,
+ struct mtree_option **global, const char *line)
+{
+ const char *next, *eq;
+ size_t len;
+ int r;
+
+ line += 4;
+ for (;;) {
+ next = line + strspn(line, " \t\r\n");
+ if (*next == '\0')
+ return (ARCHIVE_OK);
+ line = next;
+ next = line + strcspn(line, " \t\r\n");
+ eq = strchr(line, '=');
+ if (eq > next)
+ len = next - line;
+ else
+ len = eq - line;
+
+ remove_option(global, line, len);
+ r = add_option(a, global, line, next - line);
+ if (r != ARCHIVE_OK)
+ return (r);
+ line = next;
+ }
+}
+
+static int
+process_global_unset(struct archive_read *a,
+ struct mtree_option **global, const char *line)
+{
+ const char *next;
+ size_t len;
+
+ line += 6;
+ if (strchr(line, '=') != NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "/unset shall not contain `='");
+ return ARCHIVE_FATAL;
+ }
+
+ for (;;) {
+ next = line + strspn(line, " \t\r\n");
+ if (*next == '\0')
+ return (ARCHIVE_OK);
+ line = next;
+ len = strcspn(line, " \t\r\n");
+
+ if (len == 3 && strncmp(line, "all", 3) == 0) {
+ free_options(*global);
+ *global = NULL;
+ } else {
+ remove_option(global, line, len);
+ }
+
+ line += len;
+ }
+}
+
+static int
+process_add_entry(struct archive_read *a, struct mtree *mtree,
+ struct mtree_option **global, const char *line, ssize_t line_len,
+ struct mtree_entry **last_entry, int is_form_d)
+{
+ struct mtree_entry *entry;
+ struct mtree_option *iter;
+ const char *next, *eq, *name, *end;
+ size_t name_len, len;
+ int r, i;
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ archive_set_error(&a->archive, errno, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ entry->next = NULL;
+ entry->options = NULL;
+ entry->name = NULL;
+ entry->used = 0;
+ entry->full = 0;
+
+ /* Add this entry to list. */
+ if (*last_entry == NULL)
+ mtree->entries = entry;
+ else
+ (*last_entry)->next = entry;
+ *last_entry = entry;
+
+ if (is_form_d) {
+ /* Filename is last item on line. */
+ /* Adjust line_len to trim trailing whitespace */
+ while (line_len > 0) {
+ char last_character = line[line_len - 1];
+ if (last_character == '\r'
+ || last_character == '\n'
+ || last_character == '\t'
+ || last_character == ' ') {
+ line_len--;
+ } else {
+ break;
+ }
+ }
+ /* Name starts after the last whitespace separator */
+ name = line;
+ for (i = 0; i < line_len; i++) {
+ if (line[i] == '\r'
+ || line[i] == '\n'
+ || line[i] == '\t'
+ || line[i] == ' ') {
+ name = line + i + 1;
+ }
+ }
+ name_len = line + line_len - name;
+ end = name;
+ } else {
+ /* Filename is first item on line */
+ name_len = strcspn(line, " \t\r\n");
+ name = line;
+ line += name_len;
+ end = line + line_len;
+ }
+ /* name/name_len is the name within the line. */
+ /* line..end brackets the entire line except the name */
+
+ if ((entry->name = malloc(name_len + 1)) == NULL) {
+ archive_set_error(&a->archive, errno, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ memcpy(entry->name, name, name_len);
+ entry->name[name_len] = '\0';
+ parse_escapes(entry->name, entry);
+
+ entry->next_dup = NULL;
+ if (entry->full) {
+ if (!__archive_rb_tree_insert_node(&mtree->rbtree, &entry->rbnode)) {
+ struct mtree_entry *alt;
+ alt = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &mtree->rbtree, entry->name);
+ if (alt != NULL) {
+ while (alt->next_dup)
+ alt = alt->next_dup;
+ alt->next_dup = entry;
+ }
+ }
+ }
+
+ for (iter = *global; iter != NULL; iter = iter->next) {
+ r = add_option(a, &entry->options, iter->value,
+ strlen(iter->value));
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ for (;;) {
+ next = line + strspn(line, " \t\r\n");
+ if (*next == '\0')
+ return (ARCHIVE_OK);
+ if (next >= end)
+ return (ARCHIVE_OK);
+ line = next;
+ next = line + strcspn(line, " \t\r\n");
+ eq = strchr(line, '=');
+ if (eq == NULL || eq > next)
+ len = next - line;
+ else
+ len = eq - line;
+
+ remove_option(&entry->options, line, len);
+ r = add_option(a, &entry->options, line, next - line);
+ if (r != ARCHIVE_OK)
+ return (r);
+ line = next;
+ }
+}
+
+static int
+read_mtree(struct archive_read *a, struct mtree *mtree)
+{
+ ssize_t len;
+ uintmax_t counter;
+ char *p, *s;
+ struct mtree_option *global;
+ struct mtree_entry *last_entry;
+ int r, is_form_d;
+
+ mtree->archive_format = ARCHIVE_FORMAT_MTREE;
+ mtree->archive_format_name = "mtree";
+
+ global = NULL;
+ last_entry = NULL;
+
+ (void)detect_form(a, &is_form_d);
+
+ for (counter = 1; ; ++counter) {
+ r = ARCHIVE_OK;
+ len = readline(a, mtree, &p, 65536);
+ if (len == 0) {
+ mtree->this_entry = mtree->entries;
+ free_options(global);
+ return (ARCHIVE_OK);
+ }
+ if (len < 0) {
+ free_options(global);
+ return ((int)len);
+ }
+ /* Leading whitespace is never significant, ignore it. */
+ while (*p == ' ' || *p == '\t') {
+ ++p;
+ --len;
+ }
+ /* Skip content lines and blank lines. */
+ if (*p == '#')
+ continue;
+ if (*p == '\r' || *p == '\n' || *p == '\0')
+ continue;
+ /* Non-printable characters are not allowed */
+ for (s = p;s < p + len - 1; s++) {
+ if (!isprint((unsigned char)*s) && *s != '\t') {
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ }
+ if (r != ARCHIVE_OK)
+ break;
+ if (*p != '/') {
+ r = process_add_entry(a, mtree, &global, p, len,
+ &last_entry, is_form_d);
+ } else if (len > 4 && strncmp(p, "/set", 4) == 0) {
+ if (p[4] != ' ' && p[4] != '\t')
+ break;
+ r = process_global_set(a, &global, p);
+ } else if (len > 6 && strncmp(p, "/unset", 6) == 0) {
+ if (p[6] != ' ' && p[6] != '\t')
+ break;
+ r = process_global_unset(a, &global, p);
+ } else
+ break;
+
+ if (r != ARCHIVE_OK) {
+ free_options(global);
+ return r;
+ }
+ }
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't parse line %ju", counter);
+ free_options(global);
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Read in the entire mtree file into memory on the first request.
+ * Then use the next unused file to satisfy each header request.
+ */
+static int
+read_header(struct archive_read *a, struct archive_entry *entry)
+{
+ struct mtree *mtree;
+ char *p;
+ int r, use_next;
+
+ mtree = (struct mtree *)(a->format->data);
+
+ if (mtree->fd >= 0) {
+ close(mtree->fd);
+ mtree->fd = -1;
+ }
+
+ if (mtree->entries == NULL) {
+ mtree->resolver = archive_entry_linkresolver_new();
+ if (mtree->resolver == NULL)
+ return ARCHIVE_FATAL;
+ archive_entry_linkresolver_set_strategy(mtree->resolver,
+ ARCHIVE_FORMAT_MTREE);
+ r = read_mtree(a, mtree);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ a->archive.archive_format = mtree->archive_format;
+ a->archive.archive_format_name = mtree->archive_format_name;
+
+ for (;;) {
+ if (mtree->this_entry == NULL)
+ return (ARCHIVE_EOF);
+ if (strcmp(mtree->this_entry->name, "..") == 0) {
+ mtree->this_entry->used = 1;
+ if (archive_strlen(&mtree->current_dir) > 0) {
+ /* Roll back current path. */
+ p = mtree->current_dir.s
+ + mtree->current_dir.length - 1;
+ while (p >= mtree->current_dir.s && *p != '/')
+ --p;
+ if (p >= mtree->current_dir.s)
+ --p;
+ mtree->current_dir.length
+ = p - mtree->current_dir.s + 1;
+ }
+ }
+ if (!mtree->this_entry->used) {
+ use_next = 0;
+ r = parse_file(a, entry, mtree, mtree->this_entry,
+ &use_next);
+ if (use_next == 0)
+ return (r);
+ }
+ mtree->this_entry = mtree->this_entry->next;
+ }
+}
+
+/*
+ * A single file can have multiple lines contribute specifications.
+ * Parse as many lines as necessary, then pull additional information
+ * from a backing file on disk as necessary.
+ */
+static int
+parse_file(struct archive_read *a, struct archive_entry *entry,
+ struct mtree *mtree, struct mtree_entry *mentry, int *use_next)
+{
+ const char *path;
+ struct stat st_storage, *st;
+ struct mtree_entry *mp;
+ struct archive_entry *sparse_entry;
+ int r = ARCHIVE_OK, r1, parsed_kws;
+
+ mentry->used = 1;
+
+ /* Initialize reasonable defaults. */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_size(entry, 0);
+ archive_string_empty(&mtree->contents_name);
+
+ /* Parse options from this line. */
+ parsed_kws = 0;
+ r = parse_line(a, entry, mtree, mentry, &parsed_kws);
+
+ if (mentry->full) {
+ archive_entry_copy_pathname(entry, mentry->name);
+ /*
+ * "Full" entries are allowed to have multiple lines
+ * and those lines aren't required to be adjacent. We
+ * don't support multiple lines for "relative" entries
+ * nor do we make any attempt to merge data from
+ * separate "relative" and "full" entries. (Merging
+ * "relative" and "full" entries would require dealing
+ * with pathname canonicalization, which is a very
+ * tricky subject.)
+ */
+ mp = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &mtree->rbtree, mentry->name);
+ for (; mp; mp = mp->next_dup) {
+ if (mp->full && !mp->used) {
+ /* Later lines override earlier ones. */
+ mp->used = 1;
+ r1 = parse_line(a, entry, mtree, mp, &parsed_kws);
+ if (r1 < r)
+ r = r1;
+ }
+ }
+ } else {
+ /*
+ * Relative entries require us to construct
+ * the full path and possibly update the
+ * current directory.
+ */
+ size_t n = archive_strlen(&mtree->current_dir);
+ if (n > 0)
+ archive_strcat(&mtree->current_dir, "/");
+ archive_strcat(&mtree->current_dir, mentry->name);
+ archive_entry_copy_pathname(entry, mtree->current_dir.s);
+ if (archive_entry_filetype(entry) != AE_IFDIR)
+ mtree->current_dir.length = n;
+ }
+
+ if (mtree->checkfs) {
+ /*
+ * Try to open and stat the file to get the real size
+ * and other file info. It would be nice to avoid
+ * this here so that getting a listing of an mtree
+ * wouldn't require opening every referenced contents
+ * file. But then we wouldn't know the actual
+ * contents size, so I don't see a really viable way
+ * around this. (Also, we may want to someday pull
+ * other unspecified info from the contents file on
+ * disk.)
+ */
+ mtree->fd = -1;
+ if (archive_strlen(&mtree->contents_name) > 0)
+ path = mtree->contents_name.s;
+ else
+ path = archive_entry_pathname(entry);
+
+ if (archive_entry_filetype(entry) == AE_IFREG ||
+ archive_entry_filetype(entry) == AE_IFDIR) {
+ mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(mtree->fd);
+ if (mtree->fd == -1 && (
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * On Windows, attempting to open a file with an
+ * invalid name result in EINVAL (Error 22)
+ */
+ (errno != ENOENT && errno != EINVAL)
+#else
+ errno != ENOENT
+#endif
+ || archive_strlen(&mtree->contents_name) > 0)) {
+ archive_set_error(&a->archive, errno,
+ "Can't open %s", path);
+ r = ARCHIVE_WARN;
+ }
+ }
+
+ st = &st_storage;
+ if (mtree->fd >= 0) {
+ if (fstat(mtree->fd, st) == -1) {
+ archive_set_error(&a->archive, errno,
+ "Could not fstat %s", path);
+ r = ARCHIVE_WARN;
+ /* If we can't stat it, don't keep it open. */
+ close(mtree->fd);
+ mtree->fd = -1;
+ st = NULL;
+ }
+ }
+#ifdef HAVE_LSTAT
+ else if (lstat(path, st) == -1)
+#else
+ else if (la_stat(path, st) == -1)
+#endif
+ {
+ st = NULL;
+ }
+
+ /*
+ * Check for a mismatch between the type in the specification
+ * and the type of the contents object on disk.
+ */
+ if (st != NULL) {
+ if (((st->st_mode & S_IFMT) == S_IFREG &&
+ archive_entry_filetype(entry) == AE_IFREG)
+#ifdef S_IFLNK
+ ||((st->st_mode & S_IFMT) == S_IFLNK &&
+ archive_entry_filetype(entry) == AE_IFLNK)
+#endif
+#ifdef S_IFSOCK
+ ||((st->st_mode & S_IFSOCK) == S_IFSOCK &&
+ archive_entry_filetype(entry) == AE_IFSOCK)
+#endif
+#ifdef S_IFCHR
+ ||((st->st_mode & S_IFMT) == S_IFCHR &&
+ archive_entry_filetype(entry) == AE_IFCHR)
+#endif
+#ifdef S_IFBLK
+ ||((st->st_mode & S_IFMT) == S_IFBLK &&
+ archive_entry_filetype(entry) == AE_IFBLK)
+#endif
+ ||((st->st_mode & S_IFMT) == S_IFDIR &&
+ archive_entry_filetype(entry) == AE_IFDIR)
+#ifdef S_IFIFO
+ ||((st->st_mode & S_IFMT) == S_IFIFO &&
+ archive_entry_filetype(entry) == AE_IFIFO)
+#endif
+ ) {
+ /* Types match. */
+ } else {
+ /* Types don't match; bail out gracefully. */
+ if (mtree->fd >= 0)
+ close(mtree->fd);
+ mtree->fd = -1;
+ if (parsed_kws & MTREE_HAS_OPTIONAL) {
+ /* It's not an error for an optional
+ * entry to not match disk. */
+ *use_next = 1;
+ } else if (r == ARCHIVE_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "mtree specification has different"
+ " type for %s",
+ archive_entry_pathname(entry));
+ r = ARCHIVE_WARN;
+ }
+ return (r);
+ }
+ }
+
+ /*
+ * If there is a contents file on disk, pick some of the
+ * metadata from that file. For most of these, we only
+ * set it from the contents if it wasn't already parsed
+ * from the specification.
+ */
+ if (st != NULL) {
+ if (((parsed_kws & MTREE_HAS_DEVICE) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0) &&
+ (archive_entry_filetype(entry) == AE_IFCHR ||
+ archive_entry_filetype(entry) == AE_IFBLK))
+ archive_entry_set_rdev(entry, st->st_rdev);
+ if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME))
+ == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_gid(entry, st->st_gid);
+ if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME))
+ == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_uid(entry, st->st_uid);
+ if ((parsed_kws & MTREE_HAS_MTIME) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0) {
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_mtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_mtim.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_mtime_n);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_umtime*1000);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+ archive_entry_set_mtime(entry, st->st_mtime,
+ st->st_mtime_usec*1000);
+#else
+ archive_entry_set_mtime(entry, st->st_mtime, 0);
+#endif
+ }
+ if ((parsed_kws & MTREE_HAS_NLINK) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_nlink(entry, st->st_nlink);
+ if ((parsed_kws & MTREE_HAS_PERM) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_perm(entry, st->st_mode);
+ if ((parsed_kws & MTREE_HAS_SIZE) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+ archive_entry_set_size(entry, st->st_size);
+ archive_entry_set_ino(entry, st->st_ino);
+ archive_entry_set_dev(entry, st->st_dev);
+
+ archive_entry_linkify(mtree->resolver, &entry,
+ &sparse_entry);
+ } else if (parsed_kws & MTREE_HAS_OPTIONAL) {
+ /*
+ * Couldn't open the entry, stat it or the on-disk type
+ * didn't match. If this entry is optional, just
+ * ignore it and read the next header entry.
+ */
+ *use_next = 1;
+ return ARCHIVE_OK;
+ }
+ }
+
+ mtree->cur_size = archive_entry_size(entry);
+ mtree->offset = 0;
+
+ return r;
+}
+
+/*
+ * Each line contains a sequence of keywords.
+ */
+static int
+parse_line(struct archive_read *a, struct archive_entry *entry,
+ struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws)
+{
+ struct mtree_option *iter;
+ int r = ARCHIVE_OK, r1;
+
+ for (iter = mp->options; iter != NULL; iter = iter->next) {
+ r1 = parse_keyword(a, mtree, entry, iter, parsed_kws);
+ if (r1 < r)
+ r = r1;
+ }
+ if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Missing type keyword in mtree specification");
+ return (ARCHIVE_WARN);
+ }
+ return (r);
+}
+
+/*
+ * Device entries have one of the following forms:
+ * - raw dev_t
+ * - format,major,minor[,subdevice]
+ * When parsing succeeded, `pdev' will contain the appropriate dev_t value.
+ */
+
+/* strsep() is not in C90, but strcspn() is. */
+/* Taken from http://unixpapa.com/incnote/string.html */
+static char *
+la_strsep(char **sp, const char *sep)
+{
+ char *p, *s;
+ if (sp == NULL || *sp == NULL || **sp == '\0')
+ return(NULL);
+ s = *sp;
+ p = s + strcspn(s, sep);
+ if (*p != '\0')
+ *p++ = '\0';
+ *sp = p;
+ return(s);
+}
+
+static int
+parse_device(dev_t *pdev, struct archive *a, char *val)
+{
+#define MAX_PACK_ARGS 3
+ unsigned long numbers[MAX_PACK_ARGS];
+ char *p, *dev;
+ int argc;
+ pack_t *pack;
+ dev_t result;
+ const char *error = NULL;
+
+ memset(pdev, 0, sizeof(*pdev));
+ if ((dev = strchr(val, ',')) != NULL) {
+ /*
+ * Device's major/minor are given in a specified format.
+ * Decode and pack it accordingly.
+ */
+ *dev++ = '\0';
+ if ((pack = pack_find(val)) == NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown format `%s'", val);
+ return ARCHIVE_WARN;
+ }
+ argc = 0;
+ while ((p = la_strsep(&dev, ",")) != NULL) {
+ if (*p == '\0') {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Missing number");
+ return ARCHIVE_WARN;
+ }
+ if (argc >= MAX_PACK_ARGS) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too many arguments");
+ return ARCHIVE_WARN;
+ }
+ numbers[argc++] = (unsigned long)mtree_atol(&p, 0);
+ }
+ if (argc < 2) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Not enough arguments");
+ return ARCHIVE_WARN;
+ }
+ result = (*pack)(argc, numbers, &error);
+ if (error != NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "%s", error);
+ return ARCHIVE_WARN;
+ }
+ } else {
+ /* file system raw value. */
+ result = (dev_t)mtree_atol(&val, 0);
+ }
+ *pdev = result;
+ return ARCHIVE_OK;
+#undef MAX_PACK_ARGS
+}
+
+static int
+parse_hex_nibble(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return 10 + c - 'a';
+#if 0
+ /* XXX: Is uppercase something we should support? */
+ if (c >= 'A' && c <= 'F')
+ return 10 + c - 'A';
+#endif
+
+ return -1;
+}
+
+static int
+parse_digest(struct archive_read *a, struct archive_entry *entry,
+ const char *digest, int type)
+{
+ unsigned char digest_buf[64];
+ int high, low;
+ size_t i, j, len;
+
+ switch (type) {
+ case ARCHIVE_ENTRY_DIGEST_MD5:
+ len = sizeof(entry->digest.md5);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_RMD160:
+ len = sizeof(entry->digest.rmd160);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA1:
+ len = sizeof(entry->digest.sha1);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA256:
+ len = sizeof(entry->digest.sha256);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA384:
+ len = sizeof(entry->digest.sha384);
+ break;
+ case ARCHIVE_ENTRY_DIGEST_SHA512:
+ len = sizeof(entry->digest.sha512);
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Unknown digest type");
+ return ARCHIVE_FATAL;
+ }
+
+ if (len > sizeof(digest_buf)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal error: Digest storage too large");
+ return ARCHIVE_FATAL;
+ }
+
+ len *= 2;
+
+ if (mtree_strnlen(digest, len+1) != len) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "incorrect digest length, ignoring");
+ return ARCHIVE_WARN;
+ }
+
+ for (i = 0, j = 0; i < len; i += 2, j++) {
+ high = parse_hex_nibble(digest[i]);
+ low = parse_hex_nibble(digest[i+1]);
+ if (high == -1 || low == -1) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "invalid digest data, ignoring");
+ return ARCHIVE_WARN;
+ }
+
+ digest_buf[j] = high << 4 | low;
+ }
+
+ return archive_entry_set_digest(entry, type, digest_buf);
+}
+
+/*
+ * Parse a single keyword and its value.
+ */
+static int
+parse_keyword(struct archive_read *a, struct mtree *mtree,
+ struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws)
+{
+ char *val, *key;
+
+ key = opt->value;
+
+ if (*key == '\0')
+ return (ARCHIVE_OK);
+
+ if (strcmp(key, "nochange") == 0) {
+ *parsed_kws |= MTREE_HAS_NOCHANGE;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "optional") == 0) {
+ *parsed_kws |= MTREE_HAS_OPTIONAL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "ignore") == 0) {
+ /*
+ * The mtree processing is not recursive, so
+ * recursion will only happen for explicitly listed
+ * entries.
+ */
+ return (ARCHIVE_OK);
+ }
+
+ val = strchr(key, '=');
+ if (val == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed attribute \"%s\" (%d)", key, key[0]);
+ return (ARCHIVE_WARN);
+ }
+
+ *val = '\0';
+ ++val;
+
+ switch (key[0]) {
+ case 'c':
+ if (strcmp(key, "content") == 0
+ || strcmp(key, "contents") == 0) {
+ parse_escapes(val, NULL);
+ archive_strcpy(&mtree->contents_name, val);
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "cksum") == 0)
+ return (ARCHIVE_OK);
+ break;
+ case 'd':
+ if (strcmp(key, "device") == 0) {
+ /* stat(2) st_rdev field, e.g. the major/minor IDs
+ * of a char/block special file */
+ int r;
+ dev_t dev;
+
+ *parsed_kws |= MTREE_HAS_DEVICE;
+ r = parse_device(&dev, &a->archive, val);
+ if (r == ARCHIVE_OK)
+ archive_entry_set_rdev(entry, dev);
+ return r;
+ }
+ break;
+ case 'f':
+ if (strcmp(key, "flags") == 0) {
+ *parsed_kws |= MTREE_HAS_FFLAGS;
+ archive_entry_copy_fflags_text(entry, val);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'g':
+ if (strcmp(key, "gid") == 0) {
+ *parsed_kws |= MTREE_HAS_GID;
+ archive_entry_set_gid(entry, mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "gname") == 0) {
+ *parsed_kws |= MTREE_HAS_GNAME;
+ archive_entry_copy_gname(entry, val);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'i':
+ if (strcmp(key, "inode") == 0) {
+ archive_entry_set_ino(entry, mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'l':
+ if (strcmp(key, "link") == 0) {
+ parse_escapes(val, NULL);
+ archive_entry_copy_symlink(entry, val);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'm':
+ if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_MD5);
+ }
+ if (strcmp(key, "mode") == 0) {
+ if (val[0] < '0' || val[0] > '7') {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Symbolic or non-octal mode \"%s\" unsupported", val);
+ return (ARCHIVE_WARN);
+ }
+ *parsed_kws |= MTREE_HAS_PERM;
+ archive_entry_set_perm(entry, (mode_t)mtree_atol(&val, 8));
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'n':
+ if (strcmp(key, "nlink") == 0) {
+ *parsed_kws |= MTREE_HAS_NLINK;
+ archive_entry_set_nlink(entry,
+ (unsigned int)mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'r':
+ if (strcmp(key, "resdevice") == 0) {
+ /* stat(2) st_dev field, e.g. the device ID where the
+ * inode resides */
+ int r;
+ dev_t dev;
+
+ r = parse_device(&dev, &a->archive, val);
+ if (r == ARCHIVE_OK)
+ archive_entry_set_dev(entry, dev);
+ return r;
+ }
+ if (strcmp(key, "rmd160") == 0 ||
+ strcmp(key, "rmd160digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_RMD160);
+ }
+ break;
+ case 's':
+ if (strcmp(key, "sha1") == 0 ||
+ strcmp(key, "sha1digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_SHA1);
+ }
+ if (strcmp(key, "sha256") == 0 ||
+ strcmp(key, "sha256digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_SHA256);
+ }
+ if (strcmp(key, "sha384") == 0 ||
+ strcmp(key, "sha384digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_SHA384);
+ }
+ if (strcmp(key, "sha512") == 0 ||
+ strcmp(key, "sha512digest") == 0) {
+ return parse_digest(a, entry, val,
+ ARCHIVE_ENTRY_DIGEST_SHA512);
+ }
+ if (strcmp(key, "size") == 0) {
+ archive_entry_set_size(entry, mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 't':
+ if (strcmp(key, "tags") == 0) {
+ /*
+ * Comma delimited list of tags.
+ * Ignore the tags for now, but the interface
+ * should be extended to allow inclusion/exclusion.
+ */
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "time") == 0) {
+ int64_t m;
+ int64_t my_time_t_max = get_time_t_max();
+ int64_t my_time_t_min = get_time_t_min();
+ long ns = 0;
+
+ *parsed_kws |= MTREE_HAS_MTIME;
+ m = mtree_atol(&val, 10);
+ /* Replicate an old mtree bug:
+ * 123456789.1 represents 123456789
+ * seconds and 1 nanosecond. */
+ if (*val == '.') {
+ ++val;
+ ns = (long)mtree_atol(&val, 10);
+ if (ns < 0)
+ ns = 0;
+ else if (ns > 999999999)
+ ns = 999999999;
+ }
+ if (m > my_time_t_max)
+ m = my_time_t_max;
+ else if (m < my_time_t_min)
+ m = my_time_t_min;
+ archive_entry_set_mtime(entry, (time_t)m, ns);
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "type") == 0) {
+ switch (val[0]) {
+ case 'b':
+ if (strcmp(val, "block") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFBLK);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'c':
+ if (strcmp(val, "char") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFCHR);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'd':
+ if (strcmp(val, "dir") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFDIR);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'f':
+ if (strcmp(val, "fifo") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFIFO);
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(val, "file") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFREG);
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'l':
+ if (strcmp(val, "link") == 0) {
+ *parsed_kws |= MTREE_HAS_TYPE;
+ archive_entry_set_filetype(entry,
+ AE_IFLNK);
+ return (ARCHIVE_OK);
+ }
+ break;
+ default:
+ break;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unrecognized file type \"%s\"; "
+ "assuming \"file\"", val);
+ archive_entry_set_filetype(entry, AE_IFREG);
+ return (ARCHIVE_WARN);
+ }
+ break;
+ case 'u':
+ if (strcmp(key, "uid") == 0) {
+ *parsed_kws |= MTREE_HAS_UID;
+ archive_entry_set_uid(entry, mtree_atol(&val, 10));
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "uname") == 0) {
+ *parsed_kws |= MTREE_HAS_UNAME;
+ archive_entry_copy_uname(entry, val);
+ return (ARCHIVE_OK);
+ }
+ break;
+ default:
+ break;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unrecognized key %s=%s", key, val);
+ return (ARCHIVE_WARN);
+}
+
+static int
+read_data(struct archive_read *a, const void **buff, size_t *size,
+ int64_t *offset)
+{
+ size_t bytes_to_read;
+ ssize_t bytes_read;
+ struct mtree *mtree;
+
+ mtree = (struct mtree *)(a->format->data);
+ if (mtree->fd < 0) {
+ *buff = NULL;
+ *offset = 0;
+ *size = 0;
+ return (ARCHIVE_EOF);
+ }
+ if (mtree->buff == NULL) {
+ mtree->buffsize = 64 * 1024;
+ mtree->buff = malloc(mtree->buffsize);
+ if (mtree->buff == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ *buff = mtree->buff;
+ *offset = mtree->offset;
+ if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset)
+ bytes_to_read = (size_t)(mtree->cur_size - mtree->offset);
+ else
+ bytes_to_read = mtree->buffsize;
+ bytes_read = read(mtree->fd, mtree->buff, bytes_to_read);
+ if (bytes_read < 0) {
+ archive_set_error(&a->archive, errno, "Can't read");
+ return (ARCHIVE_WARN);
+ }
+ if (bytes_read == 0) {
+ *size = 0;
+ return (ARCHIVE_EOF);
+ }
+ mtree->offset += bytes_read;
+ *size = bytes_read;
+ return (ARCHIVE_OK);
+}
+
+/* Skip does nothing except possibly close the contents file. */
+static int
+skip(struct archive_read *a)
+{
+ struct mtree *mtree;
+
+ mtree = (struct mtree *)(a->format->data);
+ if (mtree->fd >= 0) {
+ close(mtree->fd);
+ mtree->fd = -1;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Since parsing backslash sequences always makes strings shorter,
+ * we can always do this conversion in-place.
+ */
+static void
+parse_escapes(char *src, struct mtree_entry *mentry)
+{
+ char *dest = src;
+ char c;
+
+ if (mentry != NULL && strcmp(src, ".") == 0)
+ mentry->full = 1;
+
+ while (*src != '\0') {
+ c = *src++;
+ if (c == '/' && mentry != NULL)
+ mentry->full = 1;
+ if (c == '\\') {
+ switch (src[0]) {
+ case '0':
+ if (src[1] < '0' || src[1] > '7') {
+ c = 0;
+ ++src;
+ break;
+ }
+ /* FALLTHROUGH */
+ case '1':
+ case '2':
+ case '3':
+ if (src[1] >= '0' && src[1] <= '7' &&
+ src[2] >= '0' && src[2] <= '7') {
+ c = (src[0] - '0') << 6;
+ c |= (src[1] - '0') << 3;
+ c |= (src[2] - '0');
+ src += 3;
+ }
+ break;
+ case 'a':
+ c = '\a';
+ ++src;
+ break;
+ case 'b':
+ c = '\b';
+ ++src;
+ break;
+ case 'f':
+ c = '\f';
+ ++src;
+ break;
+ case 'n':
+ c = '\n';
+ ++src;
+ break;
+ case 'r':
+ c = '\r';
+ ++src;
+ break;
+ case 's':
+ c = ' ';
+ ++src;
+ break;
+ case 't':
+ c = '\t';
+ ++src;
+ break;
+ case 'v':
+ c = '\v';
+ ++src;
+ break;
+ case '\\':
+ c = '\\';
+ ++src;
+ break;
+ }
+ }
+ *dest++ = c;
+ }
+ *dest = '\0';
+}
+
+/* Parse a hex digit. */
+static int
+parsedigit(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a';
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A';
+ else
+ return -1;
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+mtree_atol(char **p, int base)
+{
+ int64_t l, limit;
+ int digit, last_digit_limit;
+
+ if (base == 0) {
+ if (**p != '0')
+ base = 10;
+ else if ((*p)[1] == 'x' || (*p)[1] == 'X') {
+ *p += 2;
+ base = 16;
+ } else {
+ base = 8;
+ }
+ }
+
+ if (**p == '-') {
+ limit = INT64_MIN / base;
+ last_digit_limit = -(INT64_MIN % base);
+ ++(*p);
+
+ l = 0;
+ digit = parsedigit(**p);
+ while (digit >= 0 && digit < base) {
+ if (l < limit || (l == limit && digit >= last_digit_limit))
+ return INT64_MIN;
+ l = (l * base) - digit;
+ digit = parsedigit(*++(*p));
+ }
+ return l;
+ } else {
+ limit = INT64_MAX / base;
+ last_digit_limit = INT64_MAX % base;
+
+ l = 0;
+ digit = parsedigit(**p);
+ while (digit >= 0 && digit < base) {
+ if (l > limit || (l == limit && digit > last_digit_limit))
+ return INT64_MAX;
+ l = (l * base) + digit;
+ digit = parsedigit(*++(*p));
+ }
+ return l;
+ }
+}
+
+/*
+ * Returns length of line (including trailing newline)
+ * or negative on error. 'start' argument is updated to
+ * point to first character of line.
+ */
+static ssize_t
+readline(struct archive_read *a, struct mtree *mtree, char **start,
+ ssize_t limit)
+{
+ ssize_t bytes_read;
+ ssize_t total_size = 0;
+ ssize_t find_off = 0;
+ const void *t;
+ void *nl;
+ char *u;
+
+ /* Accumulate line in a line buffer. */
+ for (;;) {
+ /* Read some more. */
+ t = __archive_read_ahead(a, 1, &bytes_read);
+ if (t == NULL)
+ return (0);
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ nl = memchr(t, '\n', bytes_read);
+ /* If we found '\n', trim the read to end exactly there. */
+ if (nl != NULL) {
+ bytes_read = ((const char *)nl) - ((const char *)t) + 1;
+ }
+ if (total_size + bytes_read + 1 > limit) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Line too long");
+ return (ARCHIVE_FATAL);
+ }
+ if (archive_string_ensure(&mtree->line,
+ total_size + bytes_read + 1) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate working buffer");
+ return (ARCHIVE_FATAL);
+ }
+ /* Append new bytes to string. */
+ memcpy(mtree->line.s + total_size, t, bytes_read);
+ __archive_read_consume(a, bytes_read);
+ total_size += bytes_read;
+ mtree->line.s[total_size] = '\0';
+
+ for (u = mtree->line.s + find_off; *u; ++u) {
+ if (u[0] == '\n') {
+ /* Ends with unescaped newline. */
+ *start = mtree->line.s;
+ return total_size;
+ } else if (u[0] == '#') {
+ /* Ends with comment sequence #...\n */
+ if (nl == NULL) {
+ /* But we've not found the \n yet */
+ break;
+ }
+ } else if (u[0] == '\\') {
+ if (u[1] == '\n') {
+ /* Trim escaped newline. */
+ total_size -= 2;
+ mtree->line.s[total_size] = '\0';
+ break;
+ } else if (u[1] != '\0') {
+ /* Skip the two-char escape sequence */
+ ++u;
+ }
+ }
+ }
+ find_off = u - mtree->line.s;
+ }
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_rar.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_rar.c
new file mode 100644
index 0000000000..6452f5b5d6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_rar.c
@@ -0,0 +1,3788 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* All rights reserved.
+*
+* 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+*/
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+#include <limits.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* crc32 */
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+/* RAR signature, also known as the mark header */
+#define RAR_SIGNATURE "\x52\x61\x72\x21\x1A\x07\x00"
+
+/* Header types */
+#define MARK_HEAD 0x72
+#define MAIN_HEAD 0x73
+#define FILE_HEAD 0x74
+#define COMM_HEAD 0x75
+#define AV_HEAD 0x76
+#define SUB_HEAD 0x77
+#define PROTECT_HEAD 0x78
+#define SIGN_HEAD 0x79
+#define NEWSUB_HEAD 0x7a
+#define ENDARC_HEAD 0x7b
+
+/* Main Header Flags */
+#define MHD_VOLUME 0x0001
+#define MHD_COMMENT 0x0002
+#define MHD_LOCK 0x0004
+#define MHD_SOLID 0x0008
+#define MHD_NEWNUMBERING 0x0010
+#define MHD_AV 0x0020
+#define MHD_PROTECT 0x0040
+#define MHD_PASSWORD 0x0080
+#define MHD_FIRSTVOLUME 0x0100
+#define MHD_ENCRYPTVER 0x0200
+
+/* Flags common to all headers */
+#define HD_MARKDELETION 0x4000
+#define HD_ADD_SIZE_PRESENT 0x8000
+
+/* File Header Flags */
+#define FHD_SPLIT_BEFORE 0x0001
+#define FHD_SPLIT_AFTER 0x0002
+#define FHD_PASSWORD 0x0004
+#define FHD_COMMENT 0x0008
+#define FHD_SOLID 0x0010
+#define FHD_LARGE 0x0100
+#define FHD_UNICODE 0x0200
+#define FHD_SALT 0x0400
+#define FHD_VERSION 0x0800
+#define FHD_EXTTIME 0x1000
+#define FHD_EXTFLAGS 0x2000
+
+/* File dictionary sizes */
+#define DICTIONARY_SIZE_64 0x00
+#define DICTIONARY_SIZE_128 0x20
+#define DICTIONARY_SIZE_256 0x40
+#define DICTIONARY_SIZE_512 0x60
+#define DICTIONARY_SIZE_1024 0x80
+#define DICTIONARY_SIZE_2048 0xA0
+#define DICTIONARY_SIZE_4096 0xC0
+#define FILE_IS_DIRECTORY 0xE0
+#define DICTIONARY_MASK FILE_IS_DIRECTORY
+
+/* OS Flags */
+#define OS_MSDOS 0
+#define OS_OS2 1
+#define OS_WIN32 2
+#define OS_UNIX 3
+#define OS_MAC_OS 4
+#define OS_BEOS 5
+
+/* Compression Methods */
+#define COMPRESS_METHOD_STORE 0x30
+/* LZSS */
+#define COMPRESS_METHOD_FASTEST 0x31
+#define COMPRESS_METHOD_FAST 0x32
+#define COMPRESS_METHOD_NORMAL 0x33
+/* PPMd Variant H */
+#define COMPRESS_METHOD_GOOD 0x34
+#define COMPRESS_METHOD_BEST 0x35
+
+#define CRC_POLYNOMIAL 0xEDB88320
+
+#define NS_UNIT 10000000
+
+#define DICTIONARY_MAX_SIZE 0x400000
+
+#define MAINCODE_SIZE 299
+#define OFFSETCODE_SIZE 60
+#define LOWOFFSETCODE_SIZE 17
+#define LENGTHCODE_SIZE 28
+#define HUFFMAN_TABLE_SIZE \
+ MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE
+
+#define MAX_SYMBOL_LENGTH 0xF
+#define MAX_SYMBOLS 20
+
+/* Virtual Machine Properties */
+#define VM_MEMORY_SIZE 0x40000
+#define VM_MEMORY_MASK (VM_MEMORY_SIZE - 1)
+#define PROGRAM_WORK_SIZE 0x3C000
+#define PROGRAM_GLOBAL_SIZE 0x2000
+#define PROGRAM_SYSTEM_GLOBAL_ADDRESS PROGRAM_WORK_SIZE
+#define PROGRAM_SYSTEM_GLOBAL_SIZE 0x40
+#define PROGRAM_USER_GLOBAL_ADDRESS (PROGRAM_SYSTEM_GLOBAL_ADDRESS + PROGRAM_SYSTEM_GLOBAL_SIZE)
+#define PROGRAM_USER_GLOBAL_SIZE (PROGRAM_GLOBAL_SIZE - PROGRAM_SYSTEM_GLOBAL_SIZE)
+
+/*
+ * Considering L1,L2 cache miss and a calling of write system-call,
+ * the best size of the output buffer(uncompressed buffer) is 128K.
+ * If the structure of extracting process is changed, this value
+ * might be researched again.
+ */
+#define UNP_BUFFER_SIZE (128 * 1024)
+
+/* Define this here for non-Windows platforms */
+#if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__))
+#define FILE_ATTRIBUTE_DIRECTORY 0x10
+#endif
+
+#undef minimum
+#define minimum(a, b) ((a)<(b)?(a):(b))
+
+/* Stack overflow check */
+#define MAX_COMPRESS_DEPTH 1024
+
+/* Fields common to all headers */
+struct rar_header
+{
+ char crc[2];
+ char type;
+ char flags[2];
+ char size[2];
+};
+
+/* Fields common to all file headers */
+struct rar_file_header
+{
+ char pack_size[4];
+ char unp_size[4];
+ char host_os;
+ char file_crc[4];
+ char file_time[4];
+ char unp_ver;
+ char method;
+ char name_size[2];
+ char file_attr[4];
+};
+
+struct huffman_tree_node
+{
+ int branches[2];
+};
+
+struct huffman_table_entry
+{
+ unsigned int length;
+ int value;
+};
+
+struct huffman_code
+{
+ struct huffman_tree_node *tree;
+ int numentries;
+ int numallocatedentries;
+ int minlength;
+ int maxlength;
+ int tablesize;
+ struct huffman_table_entry *table;
+};
+
+struct lzss
+{
+ unsigned char *window;
+ int mask;
+ int64_t position;
+};
+
+struct data_block_offsets
+{
+ int64_t header_size;
+ int64_t start_offset;
+ int64_t end_offset;
+};
+
+struct rar_program_code
+{
+ uint8_t *staticdata;
+ uint32_t staticdatalen;
+ uint8_t *globalbackup;
+ uint32_t globalbackuplen;
+ uint64_t fingerprint;
+ uint32_t usagecount;
+ uint32_t oldfilterlength;
+ struct rar_program_code *next;
+};
+
+struct rar_filter
+{
+ struct rar_program_code *prog;
+ uint32_t initialregisters[8];
+ uint8_t *globaldata;
+ uint32_t globaldatalen;
+ size_t blockstartpos;
+ uint32_t blocklength;
+ uint32_t filteredblockaddress;
+ uint32_t filteredblocklength;
+ struct rar_filter *next;
+};
+
+struct memory_bit_reader
+{
+ const uint8_t *bytes;
+ size_t length;
+ size_t offset;
+ uint64_t bits;
+ int available;
+ int at_eof;
+};
+
+struct rar_virtual_machine
+{
+ uint32_t registers[8];
+ uint8_t memory[VM_MEMORY_SIZE + sizeof(uint32_t)];
+};
+
+struct rar_filters
+{
+ struct rar_virtual_machine *vm;
+ struct rar_program_code *progs;
+ struct rar_filter *stack;
+ int64_t filterstart;
+ uint32_t lastfilternum;
+ int64_t lastend;
+ uint8_t *bytes;
+ size_t bytes_ready;
+};
+
+struct audio_state
+{
+ int8_t weight[5];
+ int16_t delta[4];
+ int8_t lastdelta;
+ int error[11];
+ int count;
+ uint8_t lastbyte;
+};
+
+struct rar
+{
+ /* Entries from main RAR header */
+ unsigned main_flags;
+ unsigned long file_crc;
+ char reserved1[2];
+ char reserved2[4];
+ char encryptver;
+
+ /* File header entries */
+ char compression_method;
+ unsigned file_flags;
+ int64_t packed_size;
+ int64_t unp_size;
+ time_t mtime;
+ long mnsec;
+ mode_t mode;
+ char *filename;
+ char *filename_save;
+ size_t filename_save_size;
+ size_t filename_allocated;
+
+ /* File header optional entries */
+ char salt[8];
+ time_t atime;
+ long ansec;
+ time_t ctime;
+ long cnsec;
+ time_t arctime;
+ long arcnsec;
+
+ /* Fields to help with tracking decompression of files. */
+ int64_t bytes_unconsumed;
+ int64_t bytes_remaining;
+ int64_t bytes_uncopied;
+ int64_t offset;
+ int64_t offset_outgoing;
+ int64_t offset_seek;
+ char valid;
+ unsigned int unp_offset;
+ unsigned int unp_buffer_size;
+ unsigned char *unp_buffer;
+ unsigned int dictionary_size;
+ char start_new_block;
+ char entry_eof;
+ unsigned long crc_calculated;
+ int found_first_header;
+ char has_endarc_header;
+ struct data_block_offsets *dbo;
+ unsigned int cursor;
+ unsigned int nodes;
+ char filename_must_match;
+
+ /* LZSS members */
+ struct huffman_code maincode;
+ struct huffman_code offsetcode;
+ struct huffman_code lowoffsetcode;
+ struct huffman_code lengthcode;
+ unsigned char lengthtable[HUFFMAN_TABLE_SIZE];
+ struct lzss lzss;
+ unsigned int lastlength;
+ unsigned int lastoffset;
+ unsigned int oldoffset[4];
+ unsigned int lastlowoffset;
+ unsigned int numlowoffsetrepeats;
+ char start_new_table;
+
+ /* Filters */
+ struct rar_filters filters;
+
+ /* PPMd Variant H members */
+ char ppmd_valid;
+ char ppmd_eod;
+ char is_ppmd_block;
+ int ppmd_escape;
+ CPpmd7 ppmd7_context;
+ CPpmd7z_RangeDec range_dec;
+ IByteIn bytein;
+
+ /*
+ * String conversion object.
+ */
+ int init_default_conversion;
+ struct archive_string_conv *sconv_default;
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_utf8;
+ struct archive_string_conv *sconv_utf16be;
+
+ /*
+ * Bit stream reader.
+ */
+ struct rar_br {
+#define CACHE_TYPE uint64_t
+#define CACHE_BITS (8 * sizeof(CACHE_TYPE))
+ /* Cache buffer. */
+ CACHE_TYPE cache_buffer;
+ /* Indicates how many bits avail in cache_buffer. */
+ int cache_avail;
+ ssize_t avail_in;
+ const unsigned char *next_in;
+ } br;
+
+ /*
+ * Custom field to denote that this archive contains encrypted entries
+ */
+ int has_encrypted_entries;
+};
+
+static int archive_read_support_format_rar_capabilities(struct archive_read *);
+static int archive_read_format_rar_has_encrypted_entries(struct archive_read *);
+static int archive_read_format_rar_bid(struct archive_read *, int);
+static int archive_read_format_rar_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_rar_read_header(struct archive_read *,
+ struct archive_entry *);
+static int archive_read_format_rar_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_rar_read_data_skip(struct archive_read *a);
+static int64_t archive_read_format_rar_seek_data(struct archive_read *, int64_t,
+ int);
+static int archive_read_format_rar_cleanup(struct archive_read *);
+
+/* Support functions */
+static int read_header(struct archive_read *, struct archive_entry *, char);
+static time_t get_time(int);
+static int read_exttime(const char *, struct rar *, const char *);
+static int read_symlink_stored(struct archive_read *, struct archive_entry *,
+ struct archive_string_conv *);
+static int read_data_stored(struct archive_read *, const void **, size_t *,
+ int64_t *);
+static int read_data_compressed(struct archive_read *, const void **, size_t *,
+ int64_t *, size_t);
+static int rar_br_preparation(struct archive_read *, struct rar_br *);
+static int parse_codes(struct archive_read *);
+static void free_codes(struct archive_read *);
+static int read_next_symbol(struct archive_read *, struct huffman_code *);
+static int create_code(struct archive_read *, struct huffman_code *,
+ unsigned char *, int, char);
+static int add_value(struct archive_read *, struct huffman_code *, int, int,
+ int);
+static int new_node(struct huffman_code *);
+static int make_table(struct archive_read *, struct huffman_code *);
+static int make_table_recurse(struct archive_read *, struct huffman_code *, int,
+ struct huffman_table_entry *, int, int);
+static int expand(struct archive_read *, int64_t *);
+static int copy_from_lzss_window_to_unp(struct archive_read *, const void **,
+ int64_t, int);
+static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *);
+static int parse_filter(struct archive_read *, const uint8_t *, uint16_t,
+ uint8_t);
+static int run_filters(struct archive_read *);
+static void clear_filters(struct rar_filters *);
+static struct rar_filter *create_filter(struct rar_program_code *,
+ const uint8_t *, uint32_t,
+ uint32_t[8], size_t, uint32_t);
+static void delete_filter(struct rar_filter *filter);
+static struct rar_program_code *compile_program(const uint8_t *, size_t);
+static void delete_program_code(struct rar_program_code *prog);
+static uint32_t membr_next_rarvm_number(struct memory_bit_reader *br);
+static inline uint32_t membr_bits(struct memory_bit_reader *br, int bits);
+static int membr_fill(struct memory_bit_reader *br, int bits);
+static int read_filter(struct archive_read *, int64_t *);
+static int rar_decode_byte(struct archive_read*, uint8_t *);
+static int execute_filter(struct archive_read*, struct rar_filter *,
+ struct rar_virtual_machine *, size_t);
+static int copy_from_lzss_window(struct archive_read *, void *, int64_t, int);
+static inline void vm_write_32(struct rar_virtual_machine*, size_t, uint32_t);
+static inline uint32_t vm_read_32(struct rar_virtual_machine*, size_t);
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define rar_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define rar_br_bits(br, n) \
+ (((uint32_t)((br)->cache_buffer >> \
+ ((br)->cache_avail - (n)))) & cache_masks[n])
+#define rar_br_bits_forced(br, n) \
+ (((uint32_t)((br)->cache_buffer << \
+ ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ * True : completed, there is enough data in the cache buffer.
+ * False : there is no data in the stream. */
+#define rar_br_read_ahead(a, br, n) \
+ ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))
+/* Notify how many bits we consumed. */
+#define rar_br_consume(br, n) ((br)->cache_avail -= (n))
+#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)
+
+static const uint32_t cache_masks[] = {
+ 0x00000000, 0x00000001, 0x00000003, 0x00000007,
+ 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
+ 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
+ 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
+ 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
+ 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
+ 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
+ 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+rar_br_fillup(struct archive_read *a, struct rar_br *br)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ int n = CACHE_BITS - br->cache_avail;
+
+ for (;;) {
+ switch (n >> 3) {
+ case 8:
+ if (br->avail_in >= 8) {
+ br->cache_buffer =
+ ((uint64_t)br->next_in[0]) << 56 |
+ ((uint64_t)br->next_in[1]) << 48 |
+ ((uint64_t)br->next_in[2]) << 40 |
+ ((uint64_t)br->next_in[3]) << 32 |
+ ((uint32_t)br->next_in[4]) << 24 |
+ ((uint32_t)br->next_in[5]) << 16 |
+ ((uint32_t)br->next_in[6]) << 8 |
+ (uint32_t)br->next_in[7];
+ br->next_in += 8;
+ br->avail_in -= 8;
+ br->cache_avail += 8 * 8;
+ rar->bytes_unconsumed += 8;
+ rar->bytes_remaining -= 8;
+ return (1);
+ }
+ break;
+ case 7:
+ if (br->avail_in >= 7) {
+ br->cache_buffer =
+ (br->cache_buffer << 56) |
+ ((uint64_t)br->next_in[0]) << 48 |
+ ((uint64_t)br->next_in[1]) << 40 |
+ ((uint64_t)br->next_in[2]) << 32 |
+ ((uint32_t)br->next_in[3]) << 24 |
+ ((uint32_t)br->next_in[4]) << 16 |
+ ((uint32_t)br->next_in[5]) << 8 |
+ (uint32_t)br->next_in[6];
+ br->next_in += 7;
+ br->avail_in -= 7;
+ br->cache_avail += 7 * 8;
+ rar->bytes_unconsumed += 7;
+ rar->bytes_remaining -= 7;
+ return (1);
+ }
+ break;
+ case 6:
+ if (br->avail_in >= 6) {
+ br->cache_buffer =
+ (br->cache_buffer << 48) |
+ ((uint64_t)br->next_in[0]) << 40 |
+ ((uint64_t)br->next_in[1]) << 32 |
+ ((uint32_t)br->next_in[2]) << 24 |
+ ((uint32_t)br->next_in[3]) << 16 |
+ ((uint32_t)br->next_in[4]) << 8 |
+ (uint32_t)br->next_in[5];
+ br->next_in += 6;
+ br->avail_in -= 6;
+ br->cache_avail += 6 * 8;
+ rar->bytes_unconsumed += 6;
+ rar->bytes_remaining -= 6;
+ return (1);
+ }
+ break;
+ case 0:
+ /* We have enough compressed data in
+ * the cache buffer.*/
+ return (1);
+ default:
+ break;
+ }
+ if (br->avail_in <= 0) {
+
+ if (rar->bytes_unconsumed > 0) {
+ /* Consume as much as the decompressor
+ * actually used. */
+ __archive_read_consume(a, rar->bytes_unconsumed);
+ rar->bytes_unconsumed = 0;
+ }
+ br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
+ if (br->next_in == NULL)
+ return (0);
+ if (br->avail_in == 0)
+ return (0);
+ }
+ br->cache_buffer =
+ (br->cache_buffer << 8) | *br->next_in++;
+ br->avail_in--;
+ br->cache_avail += 8;
+ n -= 8;
+ rar->bytes_unconsumed++;
+ rar->bytes_remaining--;
+ }
+}
+
+static int
+rar_br_preparation(struct archive_read *a, struct rar_br *br)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ if (rar->bytes_remaining > 0) {
+ br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
+ if (br->next_in == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (br->cache_avail == 0)
+ (void)rar_br_fillup(a, br);
+ }
+ return (ARCHIVE_OK);
+}
+
+/* Find last bit set */
+static inline int
+rar_fls(unsigned int word)
+{
+ word |= (word >> 1);
+ word |= (word >> 2);
+ word |= (word >> 4);
+ word |= (word >> 8);
+ word |= (word >> 16);
+ return word - (word >> 1);
+}
+
+/* LZSS functions */
+static inline int64_t
+lzss_position(struct lzss *lzss)
+{
+ return lzss->position;
+}
+
+static inline int
+lzss_mask(struct lzss *lzss)
+{
+ return lzss->mask;
+}
+
+static inline int
+lzss_size(struct lzss *lzss)
+{
+ return lzss->mask + 1;
+}
+
+static inline int
+lzss_offset_for_position(struct lzss *lzss, int64_t pos)
+{
+ return (int)(pos & lzss->mask);
+}
+
+static inline unsigned char *
+lzss_pointer_for_position(struct lzss *lzss, int64_t pos)
+{
+ return &lzss->window[lzss_offset_for_position(lzss, pos)];
+}
+
+static inline int
+lzss_current_offset(struct lzss *lzss)
+{
+ return lzss_offset_for_position(lzss, lzss->position);
+}
+
+static inline uint8_t *
+lzss_current_pointer(struct lzss *lzss)
+{
+ return lzss_pointer_for_position(lzss, lzss->position);
+}
+
+static inline void
+lzss_emit_literal(struct rar *rar, uint8_t literal)
+{
+ *lzss_current_pointer(&rar->lzss) = literal;
+ rar->lzss.position++;
+}
+
+static inline void
+lzss_emit_match(struct rar *rar, int offset, int length)
+{
+ int dstoffs = lzss_current_offset(&rar->lzss);
+ int srcoffs = (dstoffs - offset) & lzss_mask(&rar->lzss);
+ int l, li, remaining;
+ unsigned char *d, *s;
+
+ remaining = length;
+ while (remaining > 0) {
+ l = remaining;
+ if (dstoffs > srcoffs) {
+ if (l > lzss_size(&rar->lzss) - dstoffs)
+ l = lzss_size(&rar->lzss) - dstoffs;
+ } else {
+ if (l > lzss_size(&rar->lzss) - srcoffs)
+ l = lzss_size(&rar->lzss) - srcoffs;
+ }
+ d = &(rar->lzss.window[dstoffs]);
+ s = &(rar->lzss.window[srcoffs]);
+ if ((dstoffs + l < srcoffs) || (srcoffs + l < dstoffs))
+ memcpy(d, s, l);
+ else {
+ for (li = 0; li < l; li++)
+ d[li] = s[li];
+ }
+ remaining -= l;
+ dstoffs = (dstoffs + l) & lzss_mask(&(rar->lzss));
+ srcoffs = (srcoffs + l) & lzss_mask(&(rar->lzss));
+ }
+ rar->lzss.position += length;
+}
+
+static Byte
+ppmd_read(void *p)
+{
+ struct archive_read *a = ((IByteIn*)p)->a;
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+ Byte b;
+ if (!rar_br_read_ahead(a, br, 8))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return 0;
+ }
+ b = rar_br_bits(br, 8);
+ rar_br_consume(br, 8);
+ return b;
+}
+
+int
+archive_read_support_format_rar(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct rar *rar;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_support_format_rar");
+
+ rar = (struct rar *)calloc(sizeof(*rar), 1);
+ if (rar == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Until enough data has been read, we cannot tell about
+ * any encrypted entries yet.
+ */
+ rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+
+ r = __archive_read_register_format(a,
+ rar,
+ "rar",
+ archive_read_format_rar_bid,
+ archive_read_format_rar_options,
+ archive_read_format_rar_read_header,
+ archive_read_format_rar_read_data,
+ archive_read_format_rar_read_data_skip,
+ archive_read_format_rar_seek_data,
+ archive_read_format_rar_cleanup,
+ archive_read_support_format_rar_capabilities,
+ archive_read_format_rar_has_encrypted_entries);
+
+ if (r != ARCHIVE_OK)
+ free(rar);
+ return (r);
+}
+
+static int
+archive_read_support_format_rar_capabilities(struct archive_read * a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA
+ | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+static int
+archive_read_format_rar_has_encrypted_entries(struct archive_read *_a)
+{
+ if (_a && _a->format) {
+ struct rar * rar = (struct rar *)_a->format->data;
+ if (rar) {
+ return rar->has_encrypted_entries;
+ }
+ }
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+
+static int
+archive_read_format_rar_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+
+ /* If there's already a bid > 30, we'll never win. */
+ if (best_bid > 30)
+ return (-1);
+
+ if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return (-1);
+
+ if (memcmp(p, RAR_SIGNATURE, 7) == 0)
+ return (30);
+
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+ /* This is a PE file */
+ ssize_t offset = 0x10000;
+ ssize_t window = 4096;
+ ssize_t bytes_avail;
+ while (offset + window <= (1024 * 128)) {
+ const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);
+ if (buff == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ return (0);
+ continue;
+ }
+ p = buff + offset;
+ while (p + 7 < buff + bytes_avail) {
+ if (memcmp(p, RAR_SIGNATURE, 7) == 0)
+ return (30);
+ p += 0x10;
+ }
+ offset = p - buff;
+ }
+ }
+ return (0);
+}
+
+static int
+skip_sfx(struct archive_read *a)
+{
+ const void *h;
+ const char *p, *q;
+ size_t skip, total;
+ ssize_t bytes, window;
+
+ total = 0;
+ window = 4096;
+ while (total + window <= (1024 * 128)) {
+ h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ goto fatal;
+ continue;
+ }
+ if (bytes < 0x40)
+ goto fatal;
+ p = h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the RAR header.
+ */
+ while (p + 7 < q) {
+ if (memcmp(p, RAR_SIGNATURE, 7) == 0) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ return (ARCHIVE_OK);
+ }
+ p += 0x10;
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ total += skip;
+ }
+fatal:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out RAR header");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_rar_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct rar *rar;
+ int ret = ARCHIVE_FAILED;
+
+ rar = (struct rar *)(a->format->data);
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "rar: hdrcharset option needs a character-set name");
+ else {
+ rar->opt_sconv =
+ archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (rar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_read_format_rar_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ const void *h;
+ const char *p;
+ struct rar *rar;
+ size_t skip;
+ char head_type;
+ int ret;
+ unsigned flags;
+ unsigned long crc32_expected;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_RAR;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "RAR";
+
+ rar = (struct rar *)(a->format->data);
+
+ /*
+ * It should be sufficient to call archive_read_next_header() for
+ * a reader to determine if an entry is encrypted or not. If the
+ * encryption of an entry is only detectable when calling
+ * archive_read_data(), so be it. We'll do the same check there
+ * as well.
+ */
+ if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ rar->has_encrypted_entries = 0;
+ }
+
+ /* RAR files can be generated without EOF headers, so return ARCHIVE_EOF if
+ * this fails.
+ */
+ if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return (ARCHIVE_EOF);
+
+ p = h;
+ if (rar->found_first_header == 0 &&
+ ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)) {
+ /* This is an executable ? Must be self-extracting... */
+ ret = skip_sfx(a);
+ if (ret < ARCHIVE_WARN)
+ return (ret);
+ }
+ rar->found_first_header = 1;
+
+ while (1)
+ {
+ unsigned long crc32_val;
+
+ if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+
+ head_type = p[2];
+ switch(head_type)
+ {
+ case MARK_HEAD:
+ if (memcmp(p, RAR_SIGNATURE, 7) != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid marker header");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_read_consume(a, 7);
+ break;
+
+ case MAIN_HEAD:
+ rar->main_flags = archive_le16dec(p + 3);
+ skip = archive_le16dec(p + 5);
+ if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ memcpy(rar->reserved1, p + 7, sizeof(rar->reserved1));
+ memcpy(rar->reserved2, p + 7 + sizeof(rar->reserved1),
+ sizeof(rar->reserved2));
+ if (rar->main_flags & MHD_ENCRYPTVER) {
+ if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)+1) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ rar->encryptver = *(p + 7 + sizeof(rar->reserved1) +
+ sizeof(rar->reserved2));
+ }
+
+ /* Main header is password encrypted, so we cannot read any
+ file names or any other info about files from the header. */
+ if (rar->main_flags & MHD_PASSWORD)
+ {
+ archive_entry_set_is_metadata_encrypted(entry, 1);
+ archive_entry_set_is_data_encrypted(entry, 1);
+ rar->has_encrypted_entries = 1;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "RAR encryption support unavailable.");
+ return (ARCHIVE_FATAL);
+ }
+
+ crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2);
+ if ((crc32_val & 0xffff) != archive_le16dec(p)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ __archive_read_consume(a, skip);
+ break;
+
+ case FILE_HEAD:
+ return read_header(a, entry, head_type);
+
+ case COMM_HEAD:
+ case AV_HEAD:
+ case SUB_HEAD:
+ case PROTECT_HEAD:
+ case SIGN_HEAD:
+ case ENDARC_HEAD:
+ flags = archive_le16dec(p + 3);
+ skip = archive_le16dec(p + 5);
+ if (skip < 7) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size too small");
+ return (ARCHIVE_FATAL);
+ }
+ if (flags & HD_ADD_SIZE_PRESENT)
+ {
+ if (skip < 7 + 4) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size too small");
+ return (ARCHIVE_FATAL);
+ }
+ if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ skip += archive_le32dec(p + 7);
+ }
+
+ /* Skip over the 2-byte CRC at the beginning of the header. */
+ crc32_expected = archive_le16dec(p);
+ __archive_read_consume(a, 2);
+ skip -= 2;
+
+ /* Skim the entire header and compute the CRC. */
+ crc32_val = 0;
+ while (skip > 0) {
+ size_t to_read = skip;
+ if (to_read > 32 * 1024)
+ to_read = 32 * 1024;
+ if ((h = __archive_read_ahead(a, to_read, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file");
+ return (ARCHIVE_FATAL);
+ }
+ p = h;
+ crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned int)to_read);
+ __archive_read_consume(a, to_read);
+ skip -= to_read;
+ }
+ if ((crc32_val & 0xffff) != crc32_expected) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ if (head_type == ENDARC_HEAD)
+ return (ARCHIVE_EOF);
+ break;
+
+ case NEWSUB_HEAD:
+ if ((ret = read_header(a, entry, head_type)) < ARCHIVE_WARN)
+ return ret;
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file");
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+static int
+archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ int ret;
+
+ if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ rar->has_encrypted_entries = 0;
+ }
+
+ if (rar->bytes_unconsumed > 0) {
+ /* Consume as much as the decompressor actually used. */
+ __archive_read_consume(a, rar->bytes_unconsumed);
+ rar->bytes_unconsumed = 0;
+ }
+
+ *buff = NULL;
+ if (rar->entry_eof || rar->offset_seek >= rar->unp_size) {
+ *size = 0;
+ *offset = rar->offset;
+ if (*offset < rar->unp_size)
+ *offset = rar->unp_size;
+ return (ARCHIVE_EOF);
+ }
+
+ switch (rar->compression_method)
+ {
+ case COMPRESS_METHOD_STORE:
+ ret = read_data_stored(a, buff, size, offset);
+ break;
+
+ case COMPRESS_METHOD_FASTEST:
+ case COMPRESS_METHOD_FAST:
+ case COMPRESS_METHOD_NORMAL:
+ case COMPRESS_METHOD_GOOD:
+ case COMPRESS_METHOD_BEST:
+ ret = read_data_compressed(a, buff, size, offset, 0);
+ if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) {
+ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+ rar->start_new_table = 1;
+ rar->ppmd_valid = 0;
+ }
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported compression method for RAR file.");
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ return (ret);
+}
+
+static int
+archive_read_format_rar_read_data_skip(struct archive_read *a)
+{
+ struct rar *rar;
+ int64_t bytes_skipped;
+ int ret;
+
+ rar = (struct rar *)(a->format->data);
+
+ if (rar->bytes_unconsumed > 0) {
+ /* Consume as much as the decompressor actually used. */
+ __archive_read_consume(a, rar->bytes_unconsumed);
+ rar->bytes_unconsumed = 0;
+ }
+
+ if (rar->bytes_remaining > 0) {
+ bytes_skipped = __archive_read_consume(a, rar->bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Compressed data to skip must be read from each header in a multivolume
+ * archive.
+ */
+ if (rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)
+ {
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret == (ARCHIVE_EOF))
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret != (ARCHIVE_OK))
+ return ret;
+ return archive_read_format_rar_read_data_skip(a);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,
+ int whence)
+{
+ int64_t client_offset, ret;
+ unsigned int i;
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ if (rar->compression_method == COMPRESS_METHOD_STORE)
+ {
+ /* Modify the offset for use with SEEK_SET */
+ switch (whence)
+ {
+ case SEEK_CUR:
+ client_offset = rar->offset_seek;
+ break;
+ case SEEK_END:
+ client_offset = rar->unp_size;
+ break;
+ case SEEK_SET:
+ default:
+ client_offset = 0;
+ }
+ client_offset += offset;
+ if (client_offset < 0)
+ {
+ /* Can't seek past beginning of data block */
+ return -1;
+ }
+ else if (client_offset > rar->unp_size)
+ {
+ /*
+ * Set the returned offset but only seek to the end of
+ * the data block.
+ */
+ rar->offset_seek = client_offset;
+ client_offset = rar->unp_size;
+ }
+
+ client_offset += rar->dbo[0].start_offset;
+ i = 0;
+ while (i < rar->cursor)
+ {
+ i++;
+ client_offset += rar->dbo[i].start_offset - rar->dbo[i-1].end_offset;
+ }
+ if (rar->main_flags & MHD_VOLUME)
+ {
+ /* Find the appropriate offset among the multivolume archive */
+ while (1)
+ {
+ if (client_offset < rar->dbo[rar->cursor].start_offset &&
+ rar->file_flags & FHD_SPLIT_BEFORE)
+ {
+ /* Search backwards for the correct data block */
+ if (rar->cursor == 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Attempt to seek past beginning of RAR data block");
+ return (ARCHIVE_FAILED);
+ }
+ rar->cursor--;
+ client_offset -= rar->dbo[rar->cursor+1].start_offset -
+ rar->dbo[rar->cursor].end_offset;
+ if (client_offset < rar->dbo[rar->cursor].start_offset)
+ continue;
+ ret = __archive_read_seek(a, rar->dbo[rar->cursor].start_offset -
+ rar->dbo[rar->cursor].header_size, SEEK_SET);
+ if (ret < (ARCHIVE_OK))
+ return ret;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret != (ARCHIVE_OK))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error during seek of RAR file");
+ return (ARCHIVE_FAILED);
+ }
+ rar->cursor--;
+ break;
+ }
+ else if (client_offset > rar->dbo[rar->cursor].end_offset &&
+ rar->file_flags & FHD_SPLIT_AFTER)
+ {
+ /* Search forward for the correct data block */
+ rar->cursor++;
+ if (rar->cursor < rar->nodes &&
+ client_offset > rar->dbo[rar->cursor].end_offset)
+ {
+ client_offset += rar->dbo[rar->cursor].start_offset -
+ rar->dbo[rar->cursor-1].end_offset;
+ continue;
+ }
+ rar->cursor--;
+ ret = __archive_read_seek(a, rar->dbo[rar->cursor].end_offset,
+ SEEK_SET);
+ if (ret < (ARCHIVE_OK))
+ return ret;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret == (ARCHIVE_EOF))
+ {
+ rar->has_endarc_header = 1;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ }
+ if (ret != (ARCHIVE_OK))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error during seek of RAR file");
+ return (ARCHIVE_FAILED);
+ }
+ client_offset += rar->dbo[rar->cursor].start_offset -
+ rar->dbo[rar->cursor-1].end_offset;
+ continue;
+ }
+ break;
+ }
+ }
+
+ ret = __archive_read_seek(a, client_offset, SEEK_SET);
+ if (ret < (ARCHIVE_OK))
+ return ret;
+ rar->bytes_remaining = rar->dbo[rar->cursor].end_offset - ret;
+ i = rar->cursor;
+ while (i > 0)
+ {
+ i--;
+ ret -= rar->dbo[i+1].start_offset - rar->dbo[i].end_offset;
+ }
+ ret -= rar->dbo[0].start_offset;
+
+ /* Always restart reading the file after a seek */
+ __archive_reset_read_data(&a->archive);
+
+ rar->bytes_unconsumed = 0;
+ rar->offset = 0;
+
+ /*
+ * If a seek past the end of file was requested, return the requested
+ * offset.
+ */
+ if (ret == rar->unp_size && rar->offset_seek > rar->unp_size)
+ return rar->offset_seek;
+
+ /* Return the new offset */
+ rar->offset_seek = ret;
+ return rar->offset_seek;
+ }
+ else
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Seeking of compressed RAR files is unsupported");
+ }
+ return (ARCHIVE_FAILED);
+}
+
+static int
+archive_read_format_rar_cleanup(struct archive_read *a)
+{
+ struct rar *rar;
+
+ rar = (struct rar *)(a->format->data);
+ free_codes(a);
+ clear_filters(&rar->filters);
+ free(rar->filename);
+ free(rar->filename_save);
+ free(rar->dbo);
+ free(rar->unp_buffer);
+ free(rar->lzss.window);
+ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+ free(rar);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+read_header(struct archive_read *a, struct archive_entry *entry,
+ char head_type)
+{
+ const void *h;
+ const char *p, *endp;
+ struct rar *rar;
+ struct rar_header rar_header;
+ struct rar_file_header file_header;
+ int64_t header_size;
+ unsigned filename_size, end;
+ char *filename;
+ char *strp;
+ char packed_size[8];
+ char unp_size[8];
+ int ttime;
+ struct archive_string_conv *sconv, *fn_sconv;
+ unsigned long crc32_val;
+ int ret = (ARCHIVE_OK), ret2;
+
+ rar = (struct rar *)(a->format->data);
+
+ /* Setup a string conversion object for non-rar-unicode filenames. */
+ sconv = rar->opt_sconv;
+ if (sconv == NULL) {
+ if (!rar->init_default_conversion) {
+ rar->sconv_default =
+ archive_string_default_conversion_for_read(
+ &(a->archive));
+ rar->init_default_conversion = 1;
+ }
+ sconv = rar->sconv_default;
+ }
+
+
+ if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ memcpy(&rar_header, p, sizeof(rar_header));
+ rar->file_flags = archive_le16dec(rar_header.flags);
+ header_size = archive_le16dec(rar_header.size);
+ if (header_size < (int64_t)sizeof(file_header) + 7) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ crc32_val = crc32(0, (const unsigned char *)p + 2, 7 - 2);
+ __archive_read_consume(a, 7);
+
+ if (!(rar->file_flags & FHD_SOLID))
+ {
+ rar->compression_method = 0;
+ rar->packed_size = 0;
+ rar->unp_size = 0;
+ rar->mtime = 0;
+ rar->ctime = 0;
+ rar->atime = 0;
+ rar->arctime = 0;
+ rar->mode = 0;
+ memset(&rar->salt, 0, sizeof(rar->salt));
+ rar->atime = 0;
+ rar->ansec = 0;
+ rar->ctime = 0;
+ rar->cnsec = 0;
+ rar->mtime = 0;
+ rar->mnsec = 0;
+ rar->arctime = 0;
+ rar->arcnsec = 0;
+ }
+ else
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "RAR solid archive support unavailable.");
+ return (ARCHIVE_FATAL);
+ }
+
+ if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+
+ /* File Header CRC check. */
+ crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7));
+ if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ /* If no CRC error, Go on parsing File Header. */
+ p = h;
+ endp = p + header_size - 7;
+ memcpy(&file_header, p, sizeof(file_header));
+ p += sizeof(file_header);
+
+ rar->compression_method = file_header.method;
+
+ ttime = archive_le32dec(file_header.file_time);
+ rar->mtime = get_time(ttime);
+
+ rar->file_crc = archive_le32dec(file_header.file_crc);
+
+ if (rar->file_flags & FHD_PASSWORD)
+ {
+ archive_entry_set_is_data_encrypted(entry, 1);
+ rar->has_encrypted_entries = 1;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "RAR encryption support unavailable.");
+ /* Since it is only the data part itself that is encrypted we can at least
+ extract information about the currently processed entry and don't need
+ to return ARCHIVE_FATAL here. */
+ /*return (ARCHIVE_FATAL);*/
+ }
+
+ if (rar->file_flags & FHD_LARGE)
+ {
+ memcpy(packed_size, file_header.pack_size, 4);
+ memcpy(packed_size + 4, p, 4); /* High pack size */
+ p += 4;
+ memcpy(unp_size, file_header.unp_size, 4);
+ memcpy(unp_size + 4, p, 4); /* High unpack size */
+ p += 4;
+ rar->packed_size = archive_le64dec(&packed_size);
+ rar->unp_size = archive_le64dec(&unp_size);
+ }
+ else
+ {
+ rar->packed_size = archive_le32dec(file_header.pack_size);
+ rar->unp_size = archive_le32dec(file_header.unp_size);
+ }
+
+ if (rar->packed_size < 0 || rar->unp_size < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid sizes specified.");
+ return (ARCHIVE_FATAL);
+ }
+
+ rar->bytes_remaining = rar->packed_size;
+
+ /* TODO: RARv3 subblocks contain comments. For now the complete block is
+ * consumed at the end.
+ */
+ if (head_type == NEWSUB_HEAD) {
+ size_t distance = p - (const char *)h;
+ header_size += rar->packed_size;
+ /* Make sure we have the extended data. */
+ if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+ endp = p + header_size - 7;
+ p += distance;
+ }
+
+ filename_size = archive_le16dec(file_header.name_size);
+ if (p + filename_size > endp) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid filename size");
+ return (ARCHIVE_FATAL);
+ }
+ if (rar->filename_allocated < filename_size * 2 + 2) {
+ char *newptr;
+ size_t newsize = filename_size * 2 + 2;
+ newptr = realloc(rar->filename, newsize);
+ if (newptr == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory.");
+ return (ARCHIVE_FATAL);
+ }
+ rar->filename = newptr;
+ rar->filename_allocated = newsize;
+ }
+ filename = rar->filename;
+ memcpy(filename, p, filename_size);
+ filename[filename_size] = '\0';
+ if (rar->file_flags & FHD_UNICODE)
+ {
+ if (filename_size != strlen(filename))
+ {
+ unsigned char highbyte, flagbits, flagbyte;
+ unsigned fn_end, offset;
+
+ end = filename_size;
+ fn_end = filename_size * 2;
+ filename_size = 0;
+ offset = (unsigned)strlen(filename) + 1;
+ highbyte = *(p + offset++);
+ flagbits = 0;
+ flagbyte = 0;
+ while (offset < end && filename_size < fn_end)
+ {
+ if (!flagbits)
+ {
+ flagbyte = *(p + offset++);
+ flagbits = 8;
+ }
+
+ flagbits -= 2;
+ switch((flagbyte >> flagbits) & 3)
+ {
+ case 0:
+ filename[filename_size++] = '\0';
+ filename[filename_size++] = *(p + offset++);
+ break;
+ case 1:
+ filename[filename_size++] = highbyte;
+ filename[filename_size++] = *(p + offset++);
+ break;
+ case 2:
+ filename[filename_size++] = *(p + offset + 1);
+ filename[filename_size++] = *(p + offset);
+ offset += 2;
+ break;
+ case 3:
+ {
+ char extra, high;
+ uint8_t length = *(p + offset++);
+
+ if (length & 0x80) {
+ extra = *(p + offset++);
+ high = (char)highbyte;
+ } else
+ extra = high = 0;
+ length = (length & 0x7f) + 2;
+ while (length && filename_size < fn_end) {
+ unsigned cp = filename_size >> 1;
+ filename[filename_size++] = high;
+ filename[filename_size++] = p[cp] + extra;
+ length--;
+ }
+ }
+ break;
+ }
+ }
+ if (filename_size > fn_end) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid filename");
+ return (ARCHIVE_FATAL);
+ }
+ filename[filename_size++] = '\0';
+ /*
+ * Do not increment filename_size here as the computations below
+ * add the space for the terminating NUL explicitly.
+ */
+ filename[filename_size] = '\0';
+
+ /* Decoded unicode form is UTF-16BE, so we have to update a string
+ * conversion object for it. */
+ if (rar->sconv_utf16be == NULL) {
+ rar->sconv_utf16be = archive_string_conversion_from_charset(
+ &a->archive, "UTF-16BE", 1);
+ if (rar->sconv_utf16be == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ fn_sconv = rar->sconv_utf16be;
+
+ strp = filename;
+ while (memcmp(strp, "\x00\x00", 2))
+ {
+ if (!memcmp(strp, "\x00\\", 2))
+ *(strp + 1) = '/';
+ strp += 2;
+ }
+ p += offset;
+ } else {
+ /*
+ * If FHD_UNICODE is set but no unicode data, this file name form
+ * is UTF-8, so we have to update a string conversion object for
+ * it accordingly.
+ */
+ if (rar->sconv_utf8 == NULL) {
+ rar->sconv_utf8 = archive_string_conversion_from_charset(
+ &a->archive, "UTF-8", 1);
+ if (rar->sconv_utf8 == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ fn_sconv = rar->sconv_utf8;
+ while ((strp = strchr(filename, '\\')) != NULL)
+ *strp = '/';
+ p += filename_size;
+ }
+ }
+ else
+ {
+ fn_sconv = sconv;
+ while ((strp = strchr(filename, '\\')) != NULL)
+ *strp = '/';
+ p += filename_size;
+ }
+
+ /* Split file in multivolume RAR. No more need to process header. */
+ if (rar->filename_save &&
+ filename_size == rar->filename_save_size &&
+ !memcmp(rar->filename, rar->filename_save, filename_size + 1))
+ {
+ __archive_read_consume(a, header_size - 7);
+ rar->cursor++;
+ if (rar->cursor >= rar->nodes)
+ {
+ rar->nodes++;
+ if ((rar->dbo =
+ realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+ return (ARCHIVE_FATAL);
+ }
+ rar->dbo[rar->cursor].header_size = header_size;
+ rar->dbo[rar->cursor].start_offset = -1;
+ rar->dbo[rar->cursor].end_offset = -1;
+ }
+ if (rar->dbo[rar->cursor].start_offset < 0)
+ {
+ rar->dbo[rar->cursor].start_offset = a->filter->position;
+ rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset +
+ rar->packed_size;
+ }
+ return ret;
+ }
+ else if (rar->filename_must_match)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Mismatch of file parts split across multi-volume archive");
+ return (ARCHIVE_FATAL);
+ }
+
+ rar->filename_save = (char*)realloc(rar->filename_save,
+ filename_size + 1);
+ memcpy(rar->filename_save, rar->filename, filename_size + 1);
+ rar->filename_save_size = filename_size;
+
+ /* Set info for seeking */
+ free(rar->dbo);
+ if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+ return (ARCHIVE_FATAL);
+ }
+ rar->dbo[0].header_size = header_size;
+ rar->dbo[0].start_offset = -1;
+ rar->dbo[0].end_offset = -1;
+ rar->cursor = 0;
+ rar->nodes = 1;
+
+ if (rar->file_flags & FHD_SALT)
+ {
+ if (p + 8 > endp) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(rar->salt, p, 8);
+ p += 8;
+ }
+
+ if (rar->file_flags & FHD_EXTTIME) {
+ if (read_exttime(p, rar, endp) < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header size");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ __archive_read_consume(a, header_size - 7);
+ rar->dbo[0].start_offset = a->filter->position;
+ rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size;
+
+ switch(file_header.host_os)
+ {
+ case OS_MSDOS:
+ case OS_OS2:
+ case OS_WIN32:
+ rar->mode = archive_le32dec(file_header.file_attr);
+ if (rar->mode & FILE_ATTRIBUTE_DIRECTORY)
+ rar->mode = AE_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else
+ rar->mode = AE_IFREG;
+ rar->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ break;
+
+ case OS_UNIX:
+ case OS_MAC_OS:
+ case OS_BEOS:
+ rar->mode = archive_le32dec(file_header.file_attr);
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown file attributes from RAR file's host OS");
+ return (ARCHIVE_FATAL);
+ }
+
+ rar->bytes_uncopied = rar->bytes_unconsumed = 0;
+ rar->lzss.position = rar->offset = 0;
+ rar->offset_seek = 0;
+ rar->dictionary_size = 0;
+ rar->offset_outgoing = 0;
+ rar->br.cache_avail = 0;
+ rar->br.avail_in = 0;
+ rar->crc_calculated = 0;
+ rar->entry_eof = 0;
+ rar->valid = 1;
+ rar->is_ppmd_block = 0;
+ rar->start_new_table = 1;
+ free(rar->unp_buffer);
+ rar->unp_buffer = NULL;
+ rar->unp_offset = 0;
+ rar->unp_buffer_size = UNP_BUFFER_SIZE;
+ memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
+ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+ rar->ppmd_valid = rar->ppmd_eod = 0;
+ rar->filters.filterstart = INT64_MAX;
+
+ /* Don't set any archive entries for non-file header types */
+ if (head_type == NEWSUB_HEAD)
+ return ret;
+
+ archive_entry_set_mtime(entry, rar->mtime, rar->mnsec);
+ archive_entry_set_ctime(entry, rar->ctime, rar->cnsec);
+ archive_entry_set_atime(entry, rar->atime, rar->ansec);
+ archive_entry_set_size(entry, rar->unp_size);
+ archive_entry_set_mode(entry, rar->mode);
+
+ if (archive_entry_copy_pathname_l(entry, filename, filename_size, fn_sconv))
+ {
+ if (errno == ENOMEM)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(fn_sconv));
+ ret = (ARCHIVE_WARN);
+ }
+
+ if (((rar->mode) & AE_IFMT) == AE_IFLNK)
+ {
+ /* Make sure a symbolic-link file does not have its body. */
+ rar->bytes_remaining = 0;
+ archive_entry_set_size(entry, 0);
+
+ /* Read a symbolic-link name. */
+ if ((ret2 = read_symlink_stored(a, entry, sconv)) < (ARCHIVE_WARN))
+ return ret2;
+ if (ret > ret2)
+ ret = ret2;
+ }
+
+ if (rar->bytes_remaining == 0)
+ rar->entry_eof = 1;
+
+ return ret;
+}
+
+static time_t
+get_time(int ttime)
+{
+ struct tm tm;
+ tm.tm_sec = 2 * (ttime & 0x1f);
+ tm.tm_min = (ttime >> 5) & 0x3f;
+ tm.tm_hour = (ttime >> 11) & 0x1f;
+ tm.tm_mday = (ttime >> 16) & 0x1f;
+ tm.tm_mon = ((ttime >> 21) & 0x0f) - 1;
+ tm.tm_year = ((ttime >> 25) & 0x7f) + 80;
+ tm.tm_isdst = -1;
+ return mktime(&tm);
+}
+
+static int
+read_exttime(const char *p, struct rar *rar, const char *endp)
+{
+ unsigned rmode, flags, rem, j, count;
+ int ttime, i;
+ struct tm *tm;
+ time_t t;
+ long nsec;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+
+ if (p + 2 > endp)
+ return (-1);
+ flags = archive_le16dec(p);
+ p += 2;
+
+ for (i = 3; i >= 0; i--)
+ {
+ t = 0;
+ if (i == 3)
+ t = rar->mtime;
+ rmode = flags >> i * 4;
+ if (rmode & 8)
+ {
+ if (!t)
+ {
+ if (p + 4 > endp)
+ return (-1);
+ ttime = archive_le32dec(p);
+ t = get_time(ttime);
+ p += 4;
+ }
+ rem = 0;
+ count = rmode & 3;
+ if (p + count > endp)
+ return (-1);
+ for (j = 0; j < count; j++)
+ {
+ rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);
+ p++;
+ }
+#if defined(HAVE_LOCALTIME_S)
+ tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ tm = localtime_r(&t, &tmbuf);
+#else
+ tm = localtime(&t);
+#endif
+ nsec = tm->tm_sec + rem / NS_UNIT;
+ if (rmode & 4)
+ {
+ tm->tm_sec++;
+ t = mktime(tm);
+ }
+ if (i == 3)
+ {
+ rar->mtime = t;
+ rar->mnsec = nsec;
+ }
+ else if (i == 2)
+ {
+ rar->ctime = t;
+ rar->cnsec = nsec;
+ }
+ else if (i == 1)
+ {
+ rar->atime = t;
+ rar->ansec = nsec;
+ }
+ else
+ {
+ rar->arctime = t;
+ rar->arcnsec = nsec;
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
+ struct archive_string_conv *sconv)
+{
+ const void *h;
+ const char *p;
+ struct rar *rar;
+ int ret = (ARCHIVE_OK);
+
+ rar = (struct rar *)(a->format->data);
+ if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL)
+ return (ARCHIVE_FATAL);
+ p = h;
+
+ if (archive_entry_copy_symlink_l(entry,
+ p, (size_t)rar->packed_size, sconv))
+ {
+ if (errno == ENOMEM)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for link");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "link cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(sconv));
+ ret = (ARCHIVE_WARN);
+ }
+ __archive_read_consume(a, rar->packed_size);
+ return ret;
+}
+
+static int
+read_data_stored(struct archive_read *a, const void **buff, size_t *size,
+ int64_t *offset)
+{
+ struct rar *rar;
+ ssize_t bytes_avail;
+
+ rar = (struct rar *)(a->format->data);
+ if (rar->bytes_remaining == 0 &&
+ !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER))
+ {
+ *buff = NULL;
+ *size = 0;
+ *offset = rar->offset;
+ if (rar->file_crc != rar->crc_calculated) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "File CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ rar->entry_eof = 1;
+ return (ARCHIVE_EOF);
+ }
+
+ *buff = rar_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+
+ *size = bytes_avail;
+ *offset = rar->offset;
+ rar->offset += bytes_avail;
+ rar->offset_seek += bytes_avail;
+ rar->bytes_remaining -= bytes_avail;
+ rar->bytes_unconsumed = bytes_avail;
+ /* Calculate File CRC. */
+ rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+ (unsigned)bytes_avail);
+ return (ARCHIVE_OK);
+}
+
+static int
+read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
+ int64_t *offset, size_t looper)
+{
+ if (looper++ > MAX_COMPRESS_DEPTH)
+ return (ARCHIVE_FATAL);
+
+ struct rar *rar;
+ int64_t start, end;
+ size_t bs;
+ int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i;
+
+ rar = (struct rar *)(a->format->data);
+
+ do {
+ if (!rar->valid)
+ return (ARCHIVE_FATAL);
+
+ if (rar->filters.bytes_ready > 0)
+ {
+ /* Flush unp_buffer first */
+ if (rar->unp_offset > 0)
+ {
+ *buff = rar->unp_buffer;
+ *size = rar->unp_offset;
+ rar->unp_offset = 0;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+ }
+ else
+ {
+ *buff = rar->filters.bytes;
+ *size = rar->filters.bytes_ready;
+
+ rar->offset += *size;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+
+ rar->filters.bytes_ready -= *size;
+ rar->filters.bytes += *size;
+ }
+ goto ending_block;
+ }
+
+ if (rar->ppmd_eod ||
+ (rar->dictionary_size && rar->offset >= rar->unp_size))
+ {
+ if (rar->unp_offset > 0) {
+ /*
+ * We have unprocessed extracted data. write it out.
+ */
+ *buff = rar->unp_buffer;
+ *size = rar->unp_offset;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+ /* Calculate File CRC. */
+ rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+ (unsigned)*size);
+ rar->unp_offset = 0;
+ return (ARCHIVE_OK);
+ }
+ *buff = NULL;
+ *size = 0;
+ *offset = rar->offset;
+ if (rar->file_crc != rar->crc_calculated) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "File CRC error");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ rar->entry_eof = 1;
+ return (ARCHIVE_EOF);
+ }
+
+ if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0)
+ {
+ if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))
+ bs = rar->unp_buffer_size - rar->unp_offset;
+ else
+ bs = (size_t)rar->bytes_uncopied;
+ ret = copy_from_lzss_window_to_unp(a, buff, rar->offset, (int)bs);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ rar->offset += bs;
+ rar->bytes_uncopied -= bs;
+ if (*buff != NULL) {
+ rar->unp_offset = 0;
+ *size = rar->unp_buffer_size;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+ /* Calculate File CRC. */
+ rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+ (unsigned)*size);
+ return (ret);
+ }
+ continue;
+ }
+
+ if (rar->filters.lastend == rar->filters.filterstart)
+ {
+ if (!run_filters(a))
+ return (ARCHIVE_FATAL);
+ continue;
+ }
+
+ if (!rar->br.next_in &&
+ (ret = rar_br_preparation(a, &(rar->br))) < ARCHIVE_WARN)
+ return (ret);
+ if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN)))
+ return (ret);
+
+ if (rar->is_ppmd_block)
+ {
+ if ((sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ if(sym != rar->ppmd_escape)
+ {
+ lzss_emit_literal(rar, sym);
+ rar->bytes_uncopied++;
+ }
+ else
+ {
+ if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+
+ switch(code)
+ {
+ case 0:
+ rar->start_new_table = 1;
+ return read_data_compressed(a, buff, size, offset, looper);
+
+ case 2:
+ rar->ppmd_eod = 1;/* End Of ppmd Data. */
+ continue;
+
+ case 3:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Parsing filters is unsupported.");
+ return (ARCHIVE_FAILED);
+
+ case 4:
+ lzss_offset = 0;
+ for (i = 2; i >= 0; i--)
+ {
+ if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ lzss_offset |= code << (i * 8);
+ }
+ if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ lzss_emit_match(rar, lzss_offset + 2, length + 32);
+ rar->bytes_uncopied += length + 32;
+ break;
+
+ case 5:
+ if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+ &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid symbol");
+ return (ARCHIVE_FATAL);
+ }
+ lzss_emit_match(rar, 1, length + 4);
+ rar->bytes_uncopied += length + 4;
+ break;
+
+ default:
+ lzss_emit_literal(rar, sym);
+ rar->bytes_uncopied++;
+ }
+ }
+ }
+ else
+ {
+ start = rar->offset;
+ end = start + rar->dictionary_size;
+ if (rar->filters.filterstart < end) {
+ end = rar->filters.filterstart;
+ }
+
+ ret = expand(a, &end);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ rar->bytes_uncopied = end - start;
+ rar->filters.lastend = end;
+ if (rar->filters.lastend != rar->filters.filterstart && rar->bytes_uncopied == 0) {
+ /* Broken RAR files cause this case.
+ * NOTE: If this case were possible on a normal RAR file
+ * we would find out where it was actually bad and
+ * what we would do to solve it. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Internal error extracting RAR file");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))
+ bs = rar->unp_buffer_size - rar->unp_offset;
+ else
+ bs = (size_t)rar->bytes_uncopied;
+ ret = copy_from_lzss_window_to_unp(a, buff, rar->offset, (int)bs);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ rar->offset += bs;
+ rar->bytes_uncopied -= bs;
+ /*
+ * If *buff is NULL, it means unp_buffer is not full.
+ * So we have to continue extracting a RAR file.
+ */
+ } while (*buff == NULL);
+
+ rar->unp_offset = 0;
+ *size = rar->unp_buffer_size;
+ *offset = rar->offset_outgoing;
+ rar->offset_outgoing += *size;
+ending_block:
+ /* Calculate File CRC. */
+ rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size);
+ return ret;
+}
+
+static int
+parse_codes(struct archive_read *a)
+{
+ int i, j, val, n, r;
+ unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;
+ unsigned int maxorder;
+ struct huffman_code precode;
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+
+ free_codes(a);
+
+ /* Skip to the next byte */
+ rar_br_consume_unalined_bits(br);
+
+ /* PPMd block flag */
+ if (!rar_br_read_ahead(a, br, 1))
+ goto truncated_data;
+ if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0)
+ {
+ rar_br_consume(br, 1);
+ if (!rar_br_read_ahead(a, br, 7))
+ goto truncated_data;
+ ppmd_flags = rar_br_bits(br, 7);
+ rar_br_consume(br, 7);
+
+ /* Memory is allocated in MB */
+ if (ppmd_flags & 0x20)
+ {
+ if (!rar_br_read_ahead(a, br, 8))
+ goto truncated_data;
+ rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20;
+ rar_br_consume(br, 8);
+ }
+
+ if (ppmd_flags & 0x40)
+ {
+ if (!rar_br_read_ahead(a, br, 8))
+ goto truncated_data;
+ rar->ppmd_escape = rar->ppmd7_context.InitEsc = rar_br_bits(br, 8);
+ rar_br_consume(br, 8);
+ }
+ else
+ rar->ppmd_escape = 2;
+
+ if (ppmd_flags & 0x20)
+ {
+ maxorder = (ppmd_flags & 0x1F) + 1;
+ if(maxorder > 16)
+ maxorder = 16 + (maxorder - 16) * 3;
+
+ if (maxorder == 1)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Make sure ppmd7_contest is freed before Ppmd7_Construct
+ * because reading a broken file cause this abnormal sequence. */
+ __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+
+ rar->bytein.a = a;
+ rar->bytein.Read = &ppmd_read;
+ __archive_ppmd7_functions.PpmdRAR_RangeDec_CreateVTable(&rar->range_dec);
+ rar->range_dec.Stream = &rar->bytein;
+ __archive_ppmd7_functions.Ppmd7_Construct(&rar->ppmd7_context);
+
+ if (rar->dictionary_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid zero dictionary size");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context,
+ rar->dictionary_size))
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unable to initialize PPMd range decoder");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_ppmd7_functions.Ppmd7_Init(&rar->ppmd7_context, maxorder);
+ rar->ppmd_valid = 1;
+ }
+ else
+ {
+ if (!rar->ppmd_valid) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid PPMd sequence");
+ return (ARCHIVE_FATAL);
+ }
+ if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unable to initialize PPMd range decoder");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+ else
+ {
+ rar_br_consume(br, 1);
+
+ /* Keep existing table flag */
+ if (!rar_br_read_ahead(a, br, 1))
+ goto truncated_data;
+ if (!rar_br_bits(br, 1))
+ memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
+ rar_br_consume(br, 1);
+
+ memset(&bitlengths, 0, sizeof(bitlengths));
+ for (i = 0; i < MAX_SYMBOLS;)
+ {
+ if (!rar_br_read_ahead(a, br, 4))
+ goto truncated_data;
+ bitlengths[i++] = rar_br_bits(br, 4);
+ rar_br_consume(br, 4);
+ if (bitlengths[i-1] == 0xF)
+ {
+ if (!rar_br_read_ahead(a, br, 4))
+ goto truncated_data;
+ zerocount = rar_br_bits(br, 4);
+ rar_br_consume(br, 4);
+ if (zerocount)
+ {
+ i--;
+ for (j = 0; j < zerocount + 2 && i < MAX_SYMBOLS; j++)
+ bitlengths[i++] = 0;
+ }
+ }
+ }
+
+ memset(&precode, 0, sizeof(precode));
+ r = create_code(a, &precode, bitlengths, MAX_SYMBOLS, MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK) {
+ free(precode.tree);
+ free(precode.table);
+ return (r);
+ }
+
+ for (i = 0; i < HUFFMAN_TABLE_SIZE;)
+ {
+ if ((val = read_next_symbol(a, &precode)) < 0) {
+ free(precode.tree);
+ free(precode.table);
+ return (ARCHIVE_FATAL);
+ }
+ if (val < 16)
+ {
+ rar->lengthtable[i] = (rar->lengthtable[i] + val) & 0xF;
+ i++;
+ }
+ else if (val < 18)
+ {
+ if (i == 0)
+ {
+ free(precode.tree);
+ free(precode.table);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Internal error extracting RAR file.");
+ return (ARCHIVE_FATAL);
+ }
+
+ if(val == 16) {
+ if (!rar_br_read_ahead(a, br, 3)) {
+ free(precode.tree);
+ free(precode.table);
+ goto truncated_data;
+ }
+ n = rar_br_bits(br, 3) + 3;
+ rar_br_consume(br, 3);
+ } else {
+ if (!rar_br_read_ahead(a, br, 7)) {
+ free(precode.tree);
+ free(precode.table);
+ goto truncated_data;
+ }
+ n = rar_br_bits(br, 7) + 11;
+ rar_br_consume(br, 7);
+ }
+
+ for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++)
+ {
+ rar->lengthtable[i] = rar->lengthtable[i-1];
+ i++;
+ }
+ }
+ else
+ {
+ if(val == 18) {
+ if (!rar_br_read_ahead(a, br, 3)) {
+ free(precode.tree);
+ free(precode.table);
+ goto truncated_data;
+ }
+ n = rar_br_bits(br, 3) + 3;
+ rar_br_consume(br, 3);
+ } else {
+ if (!rar_br_read_ahead(a, br, 7)) {
+ free(precode.tree);
+ free(precode.table);
+ goto truncated_data;
+ }
+ n = rar_br_bits(br, 7) + 11;
+ rar_br_consume(br, 7);
+ }
+
+ for(j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++)
+ rar->lengthtable[i++] = 0;
+ }
+ }
+ free(precode.tree);
+ free(precode.table);
+
+ r = create_code(a, &rar->maincode, &rar->lengthtable[0], MAINCODE_SIZE,
+ MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = create_code(a, &rar->offsetcode, &rar->lengthtable[MAINCODE_SIZE],
+ OFFSETCODE_SIZE, MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = create_code(a, &rar->lowoffsetcode,
+ &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE],
+ LOWOFFSETCODE_SIZE, MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = create_code(a, &rar->lengthcode,
+ &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE +
+ LOWOFFSETCODE_SIZE], LENGTHCODE_SIZE, MAX_SYMBOL_LENGTH);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ if (!rar->dictionary_size || !rar->lzss.window)
+ {
+ /* Seems as though dictionary sizes are not used. Even so, minimize
+ * memory usage as much as possible.
+ */
+ void *new_window;
+ unsigned int new_size;
+
+ if (rar->unp_size >= DICTIONARY_MAX_SIZE)
+ new_size = DICTIONARY_MAX_SIZE;
+ else
+ new_size = rar_fls((unsigned int)rar->unp_size) << 1;
+ if (new_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Zero window size is invalid.");
+ return (ARCHIVE_FATAL);
+ }
+ new_window = realloc(rar->lzss.window, new_size);
+ if (new_window == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for uncompressed data.");
+ return (ARCHIVE_FATAL);
+ }
+ rar->lzss.window = (unsigned char *)new_window;
+ rar->dictionary_size = new_size;
+ memset(rar->lzss.window, 0, rar->dictionary_size);
+ rar->lzss.mask = rar->dictionary_size - 1;
+ }
+
+ rar->start_new_table = 0;
+ return (ARCHIVE_OK);
+truncated_data:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return (ARCHIVE_FATAL);
+}
+
+static void
+free_codes(struct archive_read *a)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ free(rar->maincode.tree);
+ free(rar->offsetcode.tree);
+ free(rar->lowoffsetcode.tree);
+ free(rar->lengthcode.tree);
+ free(rar->maincode.table);
+ free(rar->offsetcode.table);
+ free(rar->lowoffsetcode.table);
+ free(rar->lengthcode.table);
+ memset(&rar->maincode, 0, sizeof(rar->maincode));
+ memset(&rar->offsetcode, 0, sizeof(rar->offsetcode));
+ memset(&rar->lowoffsetcode, 0, sizeof(rar->lowoffsetcode));
+ memset(&rar->lengthcode, 0, sizeof(rar->lengthcode));
+}
+
+
+static int
+read_next_symbol(struct archive_read *a, struct huffman_code *code)
+{
+ unsigned char bit;
+ unsigned int bits;
+ int length, value, node;
+ struct rar *rar;
+ struct rar_br *br;
+
+ if (!code->table)
+ {
+ if (make_table(a, code) != (ARCHIVE_OK))
+ return -1;
+ }
+
+ rar = (struct rar *)(a->format->data);
+ br = &(rar->br);
+
+ /* Look ahead (peek) at bits */
+ if (!rar_br_read_ahead(a, br, code->tablesize)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return -1;
+ }
+ bits = rar_br_bits(br, code->tablesize);
+
+ length = code->table[bits].length;
+ value = code->table[bits].value;
+
+ if (length < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid prefix code in bitstream");
+ return -1;
+ }
+
+ if (length <= code->tablesize)
+ {
+ /* Skip length bits */
+ rar_br_consume(br, length);
+ return value;
+ }
+
+ /* Skip tablesize bits */
+ rar_br_consume(br, code->tablesize);
+
+ node = value;
+ while (!(code->tree[node].branches[0] ==
+ code->tree[node].branches[1]))
+ {
+ if (!rar_br_read_ahead(a, br, 1)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return -1;
+ }
+ bit = rar_br_bits(br, 1);
+ rar_br_consume(br, 1);
+
+ if (code->tree[node].branches[bit] < 0)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid prefix code in bitstream");
+ return -1;
+ }
+ node = code->tree[node].branches[bit];
+ }
+
+ return code->tree[node].branches[0];
+}
+
+static int
+create_code(struct archive_read *a, struct huffman_code *code,
+ unsigned char *lengths, int numsymbols, char maxlength)
+{
+ int i, j, codebits = 0, symbolsleft = numsymbols;
+
+ code->numentries = 0;
+ code->numallocatedentries = 0;
+ if (new_node(code) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for node data.");
+ return (ARCHIVE_FATAL);
+ }
+ code->numentries = 1;
+ code->minlength = INT_MAX;
+ code->maxlength = INT_MIN;
+ codebits = 0;
+ for(i = 1; i <= maxlength; i++)
+ {
+ for(j = 0; j < numsymbols; j++)
+ {
+ if (lengths[j] != i) continue;
+ if (add_value(a, code, j, codebits, i) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ codebits++;
+ if (--symbolsleft <= 0)
+ break;
+ }
+ if (symbolsleft <= 0)
+ break;
+ codebits <<= 1;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+add_value(struct archive_read *a, struct huffman_code *code, int value,
+ int codebits, int length)
+{
+ int lastnode, bitpos, bit;
+ /* int repeatpos, repeatnode, nextnode; */
+
+ free(code->table);
+ code->table = NULL;
+
+ if(length > code->maxlength)
+ code->maxlength = length;
+ if(length < code->minlength)
+ code->minlength = length;
+
+ /*
+ * Dead code, repeatpos was is -1
+ *
+ repeatpos = -1;
+ if (repeatpos == 0 || (repeatpos >= 0
+ && (((codebits >> (repeatpos - 1)) & 3) == 0
+ || ((codebits >> (repeatpos - 1)) & 3) == 3)))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid repeat position");
+ return (ARCHIVE_FATAL);
+ }
+ */
+
+ lastnode = 0;
+ for (bitpos = length - 1; bitpos >= 0; bitpos--)
+ {
+ bit = (codebits >> bitpos) & 1;
+
+ /* Leaf node check */
+ if (code->tree[lastnode].branches[0] ==
+ code->tree[lastnode].branches[1])
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Prefix found");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Dead code, repeatpos was -1, bitpos >=0
+ *
+ if (bitpos == repeatpos)
+ {
+ * Open branch check *
+ if (!(code->tree[lastnode].branches[bit] < 0))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid repeating code");
+ return (ARCHIVE_FATAL);
+ }
+
+ if ((repeatnode = new_node(code)) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for node data.");
+ return (ARCHIVE_FATAL);
+ }
+ if ((nextnode = new_node(code)) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for node data.");
+ return (ARCHIVE_FATAL);
+ }
+
+ * Set branches *
+ code->tree[lastnode].branches[bit] = repeatnode;
+ code->tree[repeatnode].branches[bit] = repeatnode;
+ code->tree[repeatnode].branches[bit^1] = nextnode;
+ lastnode = nextnode;
+
+ bitpos++; * terminating bit already handled, skip it *
+ }
+ else
+ {
+ */
+ /* Open branch check */
+ if (code->tree[lastnode].branches[bit] < 0)
+ {
+ if (new_node(code) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for node data.");
+ return (ARCHIVE_FATAL);
+ }
+ code->tree[lastnode].branches[bit] = code->numentries++;
+ }
+
+ /* set to branch */
+ lastnode = code->tree[lastnode].branches[bit];
+ /* } */
+ }
+
+ if (!(code->tree[lastnode].branches[0] == -1
+ && code->tree[lastnode].branches[1] == -2))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Prefix found");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Set leaf value */
+ code->tree[lastnode].branches[0] = value;
+ code->tree[lastnode].branches[1] = value;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+new_node(struct huffman_code *code)
+{
+ void *new_tree;
+ if (code->numallocatedentries == code->numentries) {
+ int new_num_entries = 256;
+ if (code->numentries > 0) {
+ new_num_entries = code->numentries * 2;
+ }
+ new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree));
+ if (new_tree == NULL)
+ return (-1);
+ code->tree = (struct huffman_tree_node *)new_tree;
+ code->numallocatedentries = new_num_entries;
+ }
+ code->tree[code->numentries].branches[0] = -1;
+ code->tree[code->numentries].branches[1] = -2;
+ return 1;
+}
+
+static int
+make_table(struct archive_read *a, struct huffman_code *code)
+{
+ if (code->maxlength < code->minlength || code->maxlength > 10)
+ code->tablesize = 10;
+ else
+ code->tablesize = code->maxlength;
+
+ code->table =
+ (struct huffman_table_entry *)calloc(1, sizeof(*code->table)
+ * ((size_t)1 << code->tablesize));
+
+ return make_table_recurse(a, code, 0, code->table, 0, code->tablesize);
+}
+
+static int
+make_table_recurse(struct archive_read *a, struct huffman_code *code, int node,
+ struct huffman_table_entry *table, int depth,
+ int maxdepth)
+{
+ int currtablesize, i, ret = (ARCHIVE_OK);
+
+ if (!code->tree)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Huffman tree was not created.");
+ return (ARCHIVE_FATAL);
+ }
+ if (node < 0 || node >= code->numentries)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid location to Huffman tree specified.");
+ return (ARCHIVE_FATAL);
+ }
+
+ currtablesize = 1 << (maxdepth - depth);
+
+ if (code->tree[node].branches[0] ==
+ code->tree[node].branches[1])
+ {
+ for(i = 0; i < currtablesize; i++)
+ {
+ table[i].length = depth;
+ table[i].value = code->tree[node].branches[0];
+ }
+ }
+ /*
+ * Dead code, node >= 0
+ *
+ else if (node < 0)
+ {
+ for(i = 0; i < currtablesize; i++)
+ table[i].length = -1;
+ }
+ */
+ else
+ {
+ if(depth == maxdepth)
+ {
+ table[0].length = maxdepth + 1;
+ table[0].value = node;
+ }
+ else
+ {
+ ret |= make_table_recurse(a, code, code->tree[node].branches[0], table,
+ depth + 1, maxdepth);
+ ret |= make_table_recurse(a, code, code->tree[node].branches[1],
+ table + currtablesize / 2, depth + 1, maxdepth);
+ }
+ }
+ return ret;
+}
+
+static int
+expand(struct archive_read *a, int64_t *end)
+{
+ static const unsigned char lengthbases[] =
+ { 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 10, 12, 14, 16, 20,
+ 24, 28, 32, 40, 48, 56, 64,
+ 80, 96, 112, 128, 160, 192, 224 };
+ static const unsigned char lengthbits[] =
+ { 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 2, 2,
+ 2, 2, 3, 3, 3, 3, 4,
+ 4, 4, 4, 5, 5, 5, 5 };
+ static const int lengthb_min = minimum(
+ (int)(sizeof(lengthbases)/sizeof(lengthbases[0])),
+ (int)(sizeof(lengthbits)/sizeof(lengthbits[0]))
+ );
+ static const unsigned int offsetbases[] =
+ { 0, 1, 2, 3, 4, 6,
+ 8, 12, 16, 24, 32, 48,
+ 64, 96, 128, 192, 256, 384,
+ 512, 768, 1024, 1536, 2048, 3072,
+ 4096, 6144, 8192, 12288, 16384, 24576,
+ 32768, 49152, 65536, 98304, 131072, 196608,
+ 262144, 327680, 393216, 458752, 524288, 589824,
+ 655360, 720896, 786432, 851968, 917504, 983040,
+ 1048576, 1310720, 1572864, 1835008, 2097152, 2359296,
+ 2621440, 2883584, 3145728, 3407872, 3670016, 3932160 };
+ static const unsigned char offsetbits[] =
+ { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+ 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 };
+ static const int offsetb_min = minimum(
+ (int)(sizeof(offsetbases)/sizeof(offsetbases[0])),
+ (int)(sizeof(offsetbits)/sizeof(offsetbits[0]))
+ );
+ static const unsigned char shortbases[] =
+ { 0, 4, 8, 16, 32, 64, 128, 192 };
+ static const unsigned char shortbits[] =
+ { 2, 2, 3, 4, 5, 6, 6, 6 };
+
+ int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol;
+ unsigned char newfile;
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+
+ if (rar->filters.filterstart < *end)
+ *end = rar->filters.filterstart;
+
+ while (1)
+ {
+ if(lzss_position(&rar->lzss) >= *end) {
+ return (ARCHIVE_OK);
+ }
+
+ if(rar->is_ppmd_block) {
+ *end = lzss_position(&rar->lzss);
+ return (ARCHIVE_OK);
+ }
+
+ if ((symbol = read_next_symbol(a, &rar->maincode)) < 0)
+ return (ARCHIVE_FATAL);
+
+ if (symbol < 256)
+ {
+ lzss_emit_literal(rar, symbol);
+ continue;
+ }
+ else if (symbol == 256)
+ {
+ if (!rar_br_read_ahead(a, br, 1))
+ goto truncated_data;
+ newfile = !rar_br_bits(br, 1);
+ rar_br_consume(br, 1);
+
+ if(newfile)
+ {
+ rar->start_new_block = 1;
+ if (!rar_br_read_ahead(a, br, 1))
+ goto truncated_data;
+ rar->start_new_table = rar_br_bits(br, 1);
+ rar_br_consume(br, 1);
+ *end = lzss_position(&rar->lzss);
+ return (ARCHIVE_OK);
+ }
+ else
+ {
+ if (parse_codes(a) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ continue;
+ }
+ }
+ else if(symbol==257)
+ {
+ if (!read_filter(a, end))
+ return (ARCHIVE_FATAL);
+ continue;
+ }
+ else if(symbol==258)
+ {
+ if(rar->lastlength == 0)
+ continue;
+
+ offs = rar->lastoffset;
+ len = rar->lastlength;
+ }
+ else if (symbol <= 262)
+ {
+ offsindex = symbol - 259;
+ offs = rar->oldoffset[offsindex];
+
+ if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0)
+ goto bad_data;
+ if (lensymbol > lengthb_min)
+ goto bad_data;
+ len = lengthbases[lensymbol] + 2;
+ if (lengthbits[lensymbol] > 0) {
+ if (!rar_br_read_ahead(a, br, lengthbits[lensymbol]))
+ goto truncated_data;
+ len += rar_br_bits(br, lengthbits[lensymbol]);
+ rar_br_consume(br, lengthbits[lensymbol]);
+ }
+
+ for (i = offsindex; i > 0; i--)
+ rar->oldoffset[i] = rar->oldoffset[i-1];
+ rar->oldoffset[0] = offs;
+ }
+ else if(symbol<=270)
+ {
+ offs = shortbases[symbol-263] + 1;
+ if(shortbits[symbol-263] > 0) {
+ if (!rar_br_read_ahead(a, br, shortbits[symbol-263]))
+ goto truncated_data;
+ offs += rar_br_bits(br, shortbits[symbol-263]);
+ rar_br_consume(br, shortbits[symbol-263]);
+ }
+
+ len = 2;
+
+ for(i = 3; i > 0; i--)
+ rar->oldoffset[i] = rar->oldoffset[i-1];
+ rar->oldoffset[0] = offs;
+ }
+ else
+ {
+ if (symbol-271 > lengthb_min)
+ goto bad_data;
+ len = lengthbases[symbol-271]+3;
+ if(lengthbits[symbol-271] > 0) {
+ if (!rar_br_read_ahead(a, br, lengthbits[symbol-271]))
+ goto truncated_data;
+ len += rar_br_bits(br, lengthbits[symbol-271]);
+ rar_br_consume(br, lengthbits[symbol-271]);
+ }
+
+ if ((offssymbol = read_next_symbol(a, &rar->offsetcode)) < 0)
+ goto bad_data;
+ if (offssymbol > offsetb_min)
+ goto bad_data;
+ offs = offsetbases[offssymbol]+1;
+ if(offsetbits[offssymbol] > 0)
+ {
+ if(offssymbol > 9)
+ {
+ if(offsetbits[offssymbol] > 4) {
+ if (!rar_br_read_ahead(a, br, offsetbits[offssymbol] - 4))
+ goto truncated_data;
+ offs += rar_br_bits(br, offsetbits[offssymbol] - 4) << 4;
+ rar_br_consume(br, offsetbits[offssymbol] - 4);
+ }
+
+ if(rar->numlowoffsetrepeats > 0)
+ {
+ rar->numlowoffsetrepeats--;
+ offs += rar->lastlowoffset;
+ }
+ else
+ {
+ if ((lowoffsetsymbol =
+ read_next_symbol(a, &rar->lowoffsetcode)) < 0)
+ return (ARCHIVE_FATAL);
+ if(lowoffsetsymbol == 16)
+ {
+ rar->numlowoffsetrepeats = 15;
+ offs += rar->lastlowoffset;
+ }
+ else
+ {
+ offs += lowoffsetsymbol;
+ rar->lastlowoffset = lowoffsetsymbol;
+ }
+ }
+ }
+ else {
+ if (!rar_br_read_ahead(a, br, offsetbits[offssymbol]))
+ goto truncated_data;
+ offs += rar_br_bits(br, offsetbits[offssymbol]);
+ rar_br_consume(br, offsetbits[offssymbol]);
+ }
+ }
+
+ if (offs >= 0x40000)
+ len++;
+ if (offs >= 0x2000)
+ len++;
+
+ for(i = 3; i > 0; i--)
+ rar->oldoffset[i] = rar->oldoffset[i-1];
+ rar->oldoffset[0] = offs;
+ }
+
+ rar->lastoffset = offs;
+ rar->lastlength = len;
+
+ lzss_emit_match(rar, rar->lastoffset, rar->lastlength);
+ }
+truncated_data:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated RAR file data");
+ rar->valid = 0;
+ return (ARCHIVE_FATAL);
+bad_data:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+copy_from_lzss_window(struct archive_read *a, void *buffer,
+ int64_t startpos, int length)
+{
+ int windowoffs, firstpart;
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
+ firstpart = lzss_size(&rar->lzss) - windowoffs;
+ if (firstpart < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (firstpart < length) {
+ memcpy(buffer, &rar->lzss.window[windowoffs], firstpart);
+ memcpy(buffer, &rar->lzss.window[0], length - firstpart);
+ } else {
+ memcpy(buffer, &rar->lzss.window[windowoffs], length);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer,
+ int64_t startpos, int length)
+{
+ int windowoffs, firstpart;
+ struct rar *rar = (struct rar *)(a->format->data);
+
+ if (!rar->unp_buffer)
+ {
+ if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL)
+ {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for uncompressed data.");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
+ if(windowoffs + length <= lzss_size(&rar->lzss)) {
+ memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs],
+ length);
+ } else if (length <= lzss_size(&rar->lzss)) {
+ firstpart = lzss_size(&rar->lzss) - windowoffs;
+ if (firstpart < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (firstpart < length) {
+ memcpy(&rar->unp_buffer[rar->unp_offset],
+ &rar->lzss.window[windowoffs], firstpart);
+ memcpy(&rar->unp_buffer[rar->unp_offset + firstpart],
+ &rar->lzss.window[0], length - firstpart);
+ } else {
+ memcpy(&rar->unp_buffer[rar->unp_offset],
+ &rar->lzss.window[windowoffs], length);
+ }
+ } else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Bad RAR file data");
+ return (ARCHIVE_FATAL);
+ }
+ rar->unp_offset += length;
+ if (rar->unp_offset >= rar->unp_buffer_size)
+ *buffer = rar->unp_buffer;
+ else
+ *buffer = NULL;
+ return (ARCHIVE_OK);
+}
+
+static const void *
+rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ const void *h = __archive_read_ahead(a, min, avail);
+ int ret;
+ if (avail)
+ {
+ if (a->archive.read_data_is_posix_read && *avail > (ssize_t)a->archive.read_data_requested)
+ *avail = a->archive.read_data_requested;
+ if (*avail > rar->bytes_remaining)
+ *avail = (ssize_t)rar->bytes_remaining;
+ if (*avail < 0)
+ return NULL;
+ else if (*avail == 0 && rar->main_flags & MHD_VOLUME &&
+ rar->file_flags & FHD_SPLIT_AFTER)
+ {
+ rar->filename_must_match = 1;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ if (ret == (ARCHIVE_EOF))
+ {
+ rar->has_endarc_header = 1;
+ ret = archive_read_format_rar_read_header(a, a->entry);
+ }
+ rar->filename_must_match = 0;
+ if (ret != (ARCHIVE_OK))
+ return NULL;
+ return rar_read_ahead(a, min, avail);
+ }
+ }
+ return h;
+}
+
+static int
+parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint8_t flags)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_filters *filters = &rar->filters;
+
+ struct memory_bit_reader br = { 0 };
+ struct rar_program_code *prog;
+ struct rar_filter *filter, **nextfilter;
+
+ uint32_t numprogs, num, blocklength, globaldatalen;
+ uint8_t *globaldata;
+ size_t blockstartpos;
+ uint32_t registers[8] = { 0 };
+ uint32_t i;
+
+ br.bytes = bytes;
+ br.length = length;
+
+ numprogs = 0;
+ for (prog = filters->progs; prog; prog = prog->next)
+ numprogs++;
+
+ if ((flags & 0x80))
+ {
+ num = membr_next_rarvm_number(&br);
+ if (num == 0)
+ {
+ delete_filter(filters->stack);
+ filters->stack = NULL;
+ delete_program_code(filters->progs);
+ filters->progs = NULL;
+ }
+ else
+ num--;
+ if (num > numprogs) {
+ return 0;
+ }
+ filters->lastfilternum = num;
+ }
+ else
+ num = filters->lastfilternum;
+
+ prog = filters->progs;
+ for (i = 0; i < num; i++)
+ prog = prog->next;
+ if (prog)
+ prog->usagecount++;
+
+ blockstartpos = membr_next_rarvm_number(&br) + (size_t)lzss_position(&rar->lzss);
+ if ((flags & 0x40))
+ blockstartpos += 258;
+ if ((flags & 0x20))
+ blocklength = membr_next_rarvm_number(&br);
+ else
+ blocklength = prog ? prog->oldfilterlength : 0;
+
+ registers[3] = PROGRAM_SYSTEM_GLOBAL_ADDRESS;
+ registers[4] = blocklength;
+ registers[5] = prog ? prog->usagecount : 0;
+ registers[7] = VM_MEMORY_SIZE;
+
+ if ((flags & 0x10))
+ {
+ uint8_t mask = (uint8_t)membr_bits(&br, 7);
+ for (i = 0; i < 7; i++)
+ if ((mask & (1 << i)))
+ registers[i] = membr_next_rarvm_number(&br);
+ }
+
+ if (!prog)
+ {
+ uint32_t len = membr_next_rarvm_number(&br);
+ uint8_t *bytecode;
+ struct rar_program_code **next;
+
+ if (len == 0 || len > 0x10000)
+ return 0;
+ bytecode = malloc(len);
+ if (!bytecode)
+ return 0;
+ for (i = 0; i < len; i++)
+ bytecode[i] = (uint8_t)membr_bits(&br, 8);
+ prog = compile_program(bytecode, len);
+ if (!prog) {
+ free(bytecode);
+ return 0;
+ }
+ free(bytecode);
+ next = &filters->progs;
+ while (*next)
+ next = &(*next)->next;
+ *next = prog;
+ }
+ prog->oldfilterlength = blocklength;
+
+ globaldata = NULL;
+ globaldatalen = 0;
+ if ((flags & 0x08))
+ {
+ globaldatalen = membr_next_rarvm_number(&br);
+ if (globaldatalen > PROGRAM_USER_GLOBAL_SIZE)
+ return 0;
+ globaldata = malloc(globaldatalen + PROGRAM_SYSTEM_GLOBAL_SIZE);
+ if (!globaldata)
+ return 0;
+ for (i = 0; i < globaldatalen; i++)
+ globaldata[i + PROGRAM_SYSTEM_GLOBAL_SIZE] = (uint8_t)membr_bits(&br, 8);
+ }
+
+ if (br.at_eof)
+ {
+ free(globaldata);
+ return 0;
+ }
+
+ filter = create_filter(prog, globaldata, globaldatalen, registers, blockstartpos, blocklength);
+ free(globaldata);
+ if (!filter)
+ return 0;
+
+ for (i = 0; i < 7; i++)
+ archive_le32enc(&filter->globaldata[i * 4], registers[i]);
+ archive_le32enc(&filter->globaldata[0x1C], blocklength);
+ archive_le32enc(&filter->globaldata[0x20], 0);
+ archive_le32enc(&filter->globaldata[0x2C], prog->usagecount);
+
+ nextfilter = &filters->stack;
+ while (*nextfilter)
+ nextfilter = &(*nextfilter)->next;
+ *nextfilter = filter;
+
+ if (!filters->stack->next)
+ filters->filterstart = blockstartpos;
+
+ return 1;
+}
+
+static struct rar_filter *
+create_filter(struct rar_program_code *prog, const uint8_t *globaldata, uint32_t globaldatalen, uint32_t registers[8], size_t startpos, uint32_t length)
+{
+ struct rar_filter *filter;
+
+ filter = calloc(1, sizeof(*filter));
+ if (!filter)
+ return NULL;
+ filter->prog = prog;
+ filter->globaldatalen = globaldatalen > PROGRAM_SYSTEM_GLOBAL_SIZE ? globaldatalen : PROGRAM_SYSTEM_GLOBAL_SIZE;
+ filter->globaldata = calloc(1, filter->globaldatalen);
+ if (!filter->globaldata)
+ return NULL;
+ if (globaldata)
+ memcpy(filter->globaldata, globaldata, globaldatalen);
+ if (registers)
+ memcpy(filter->initialregisters, registers, sizeof(filter->initialregisters));
+ filter->blockstartpos = startpos;
+ filter->blocklength = length;
+
+ return filter;
+}
+
+static int
+run_filters(struct archive_read *a)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_filters *filters = &rar->filters;
+ struct rar_filter *filter = filters->stack;
+ struct rar_filter *f;
+ size_t start, end;
+ int64_t tend;
+ uint32_t lastfilteraddress;
+ uint32_t lastfilterlength;
+ int ret;
+
+ if (filters == NULL || filter == NULL)
+ return (0);
+
+ start = filters->filterstart;
+ end = start + filter->blocklength;
+
+ filters->filterstart = INT64_MAX;
+ tend = (int64_t)end;
+ ret = expand(a, &tend);
+ if (ret != ARCHIVE_OK)
+ return 0;
+
+ /* Check if filter stack was modified in expand() */
+ ret = ARCHIVE_FATAL;
+ f = filters->stack;
+ while (f)
+ {
+ if (f == filter)
+ {
+ ret = ARCHIVE_OK;
+ break;
+ }
+ f = f->next;
+ }
+ if (ret != ARCHIVE_OK)
+ return 0;
+
+ if (tend < 0)
+ return 0;
+ end = (size_t)tend;
+ if (end != start + filter->blocklength)
+ return 0;
+
+ if (!filters->vm)
+ {
+ filters->vm = calloc(1, sizeof(*filters->vm));
+ if (!filters->vm)
+ return 0;
+ }
+
+ ret = copy_from_lzss_window(a, filters->vm->memory, start, filter->blocklength);
+ if (ret != ARCHIVE_OK)
+ return 0;
+ if (!execute_filter(a, filter, filters->vm, rar->offset))
+ return 0;
+
+ lastfilteraddress = filter->filteredblockaddress;
+ lastfilterlength = filter->filteredblocklength;
+ filters->stack = filter->next;
+ filter->next = NULL;
+ delete_filter(filter);
+
+ while ((filter = filters->stack) != NULL && (int64_t)filter->blockstartpos == filters->filterstart && filter->blocklength == lastfilterlength)
+ {
+ memmove(&filters->vm->memory[0], &filters->vm->memory[lastfilteraddress], lastfilterlength);
+ if (!execute_filter(a, filter, filters->vm, rar->offset))
+ return 0;
+
+ lastfilteraddress = filter->filteredblockaddress;
+ lastfilterlength = filter->filteredblocklength;
+ filters->stack = filter->next;
+ filter->next = NULL;
+ delete_filter(filter);
+ }
+
+ if (filters->stack)
+ {
+ if (filters->stack->blockstartpos < end)
+ return 0;
+ filters->filterstart = filters->stack->blockstartpos;
+ }
+
+ filters->lastend = end;
+ filters->bytes = &filters->vm->memory[lastfilteraddress];
+ filters->bytes_ready = lastfilterlength;
+
+ return 1;
+}
+
+static struct rar_program_code *
+compile_program(const uint8_t *bytes, size_t length)
+{
+ struct memory_bit_reader br = { 0 };
+ struct rar_program_code *prog;
+ // uint32_t instrcount = 0;
+ uint8_t xor;
+ size_t i;
+
+ xor = 0;
+ for (i = 1; i < length; i++)
+ xor ^= bytes[i];
+ if (!length || xor != bytes[0])
+ return NULL;
+
+ br.bytes = bytes;
+ br.length = length;
+ br.offset = 1;
+
+ prog = calloc(1, sizeof(*prog));
+ if (!prog)
+ return NULL;
+ prog->fingerprint = crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32);
+
+ if (membr_bits(&br, 1))
+ {
+ prog->staticdatalen = membr_next_rarvm_number(&br) + 1;
+ prog->staticdata = malloc(prog->staticdatalen);
+ if (!prog->staticdata)
+ {
+ delete_program_code(prog);
+ return NULL;
+ }
+ for (i = 0; i < prog->staticdatalen; i++)
+ prog->staticdata[i] = (uint8_t)membr_bits(&br, 8);
+ }
+
+ return prog;
+}
+
+static void
+delete_filter(struct rar_filter *filter)
+{
+ while (filter)
+ {
+ struct rar_filter *next = filter->next;
+ free(filter->globaldata);
+ free(filter);
+ filter = next;
+ }
+}
+
+static void
+clear_filters(struct rar_filters *filters)
+{
+ delete_filter(filters->stack);
+ delete_program_code(filters->progs);
+ free(filters->vm);
+}
+
+static void
+delete_program_code(struct rar_program_code *prog)
+{
+ while (prog)
+ {
+ struct rar_program_code *next = prog->next;
+ free(prog->staticdata);
+ free(prog->globalbackup);
+ free(prog);
+ prog = next;
+ }
+}
+
+static uint32_t
+membr_next_rarvm_number(struct memory_bit_reader *br)
+{
+ uint32_t val;
+ switch (membr_bits(br, 2))
+ {
+ case 0:
+ return membr_bits(br, 4);
+ case 1:
+ val = membr_bits(br, 8);
+ if (val >= 16)
+ return val;
+ return 0xFFFFFF00 | (val << 4) | membr_bits(br, 4);
+ case 2:
+ return membr_bits(br, 16);
+ default:
+ return membr_bits(br, 32);
+ }
+}
+
+static inline uint32_t
+membr_bits(struct memory_bit_reader *br, int bits)
+{
+ if (bits > br->available && (br->at_eof || !membr_fill(br, bits)))
+ return 0;
+ return (uint32_t)((br->bits >> (br->available -= bits)) & (((uint64_t)1 << bits) - 1));
+}
+
+static int
+membr_fill(struct memory_bit_reader *br, int bits)
+{
+ while (br->available < bits && br->offset < br->length)
+ {
+ br->bits = (br->bits << 8) | br->bytes[br->offset++];
+ br->available += 8;
+ }
+ if (bits > br->available)
+ {
+ br->at_eof = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+read_filter(struct archive_read *a, int64_t *end)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ uint8_t flags, val, *code;
+ uint16_t length, i;
+
+ if (!rar_decode_byte(a, &flags))
+ return 0;
+ length = (flags & 0x07) + 1;
+ if (length == 7)
+ {
+ if (!rar_decode_byte(a, &val))
+ return 0;
+ length = val + 7;
+ }
+ else if (length == 8)
+ {
+ if (!rar_decode_byte(a, &val))
+ return 0;
+ length = val << 8;
+ if (!rar_decode_byte(a, &val))
+ return 0;
+ length |= val;
+ }
+
+ code = malloc(length);
+ if (!code)
+ return 0;
+ for (i = 0; i < length; i++)
+ {
+ if (!rar_decode_byte(a, &code[i]))
+ {
+ free(code);
+ return 0;
+ }
+ }
+ if (!parse_filter(a, code, length, flags))
+ {
+ free(code);
+ return 0;
+ }
+ free(code);
+
+ if (rar->filters.filterstart < *end)
+ *end = rar->filters.filterstart;
+
+ return 1;
+}
+
+static int
+execute_filter_delta(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+ uint32_t length = filter->initialregisters[4];
+ uint32_t numchannels = filter->initialregisters[0];
+ uint8_t *src, *dst;
+ uint32_t i, idx;
+
+ if (length > PROGRAM_WORK_SIZE / 2)
+ return 0;
+
+ src = &vm->memory[0];
+ dst = &vm->memory[length];
+ for (i = 0; i < numchannels; i++)
+ {
+ uint8_t lastbyte = 0;
+ for (idx = i; idx < length; idx += numchannels)
+ lastbyte = dst[idx] = lastbyte - *src++;
+ }
+
+ filter->filteredblockaddress = length;
+ filter->filteredblocklength = length;
+
+ return 1;
+}
+
+static int
+execute_filter_e8(struct rar_filter *filter, struct rar_virtual_machine *vm, size_t pos, int e9also)
+{
+ uint32_t length = filter->initialregisters[4];
+ uint32_t filesize = 0x1000000;
+ uint32_t i;
+
+ if (length > PROGRAM_WORK_SIZE || length < 4)
+ return 0;
+
+ for (i = 0; i <= length - 5; i++)
+ {
+ if (vm->memory[i] == 0xE8 || (e9also && vm->memory[i] == 0xE9))
+ {
+ uint32_t currpos = (uint32_t)pos + i + 1;
+ int32_t address = (int32_t)vm_read_32(vm, i + 1);
+ if (address < 0 && currpos >= (uint32_t)-address)
+ vm_write_32(vm, i + 1, address + filesize);
+ else if (address >= 0 && (uint32_t)address < filesize)
+ vm_write_32(vm, i + 1, address - currpos);
+ i += 4;
+ }
+ }
+
+ filter->filteredblockaddress = 0;
+ filter->filteredblocklength = length;
+
+ return 1;
+}
+
+static int
+execute_filter_rgb(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+ uint32_t stride = filter->initialregisters[0];
+ uint32_t byteoffset = filter->initialregisters[1];
+ uint32_t blocklength = filter->initialregisters[4];
+ uint8_t *src, *dst;
+ uint32_t i, j;
+
+ if (blocklength > PROGRAM_WORK_SIZE / 2 || stride > blocklength)
+ return 0;
+
+ src = &vm->memory[0];
+ dst = &vm->memory[blocklength];
+ for (i = 0; i < 3; i++) {
+ uint8_t byte = 0;
+ uint8_t *prev = dst + i - stride;
+ for (j = i; j < blocklength; j += 3)
+ {
+ if (prev >= dst)
+ {
+ uint32_t delta1 = abs(prev[3] - prev[0]);
+ uint32_t delta2 = abs(byte - prev[0]);
+ uint32_t delta3 = abs(prev[3] - prev[0] + byte - prev[0]);
+ if (delta1 > delta2 || delta1 > delta3)
+ byte = delta2 <= delta3 ? prev[3] : prev[0];
+ }
+ byte -= *src++;
+ dst[j] = byte;
+ prev += 3;
+ }
+ }
+ for (i = byteoffset; i < blocklength - 2; i += 3)
+ {
+ dst[i] += dst[i + 1];
+ dst[i + 2] += dst[i + 1];
+ }
+
+ filter->filteredblockaddress = blocklength;
+ filter->filteredblocklength = blocklength;
+
+ return 1;
+}
+
+static int
+execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+ uint32_t length = filter->initialregisters[4];
+ uint32_t numchannels = filter->initialregisters[0];
+ uint8_t *src, *dst;
+ uint32_t i, j;
+
+ if (length > PROGRAM_WORK_SIZE / 2)
+ return 0;
+
+ src = &vm->memory[0];
+ dst = &vm->memory[length];
+ for (i = 0; i < numchannels; i++)
+ {
+ struct audio_state state;
+ memset(&state, 0, sizeof(state));
+ for (j = i; j < length; j += numchannels)
+ {
+ int8_t delta = (int8_t)*src++;
+ uint8_t predbyte, byte;
+ int prederror;
+ state.delta[2] = state.delta[1];
+ state.delta[1] = state.lastdelta - state.delta[0];
+ state.delta[0] = state.lastdelta;
+ predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF;
+ byte = (predbyte - delta) & 0xFF;
+ prederror = delta << 3;
+ state.error[0] += abs(prederror);
+ state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]);
+ state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]);
+ state.error[5] += abs(prederror - state.delta[2]); state.error[6] += abs(prederror + state.delta[2]);
+ state.lastdelta = (int8_t)(byte - state.lastbyte);
+ dst[j] = state.lastbyte = byte;
+ if (!(state.count++ & 0x1F))
+ {
+ uint8_t k, idx = 0;
+ for (k = 1; k < 7; k++)
+ {
+ if (state.error[k] < state.error[idx])
+ idx = k;
+ }
+ memset(state.error, 0, sizeof(state.error));
+ switch (idx)
+ {
+ case 1: if (state.weight[0] >= -16) state.weight[0]--; break;
+ case 2: if (state.weight[0] < 16) state.weight[0]++; break;
+ case 3: if (state.weight[1] >= -16) state.weight[1]--; break;
+ case 4: if (state.weight[1] < 16) state.weight[1]++; break;
+ case 5: if (state.weight[2] >= -16) state.weight[2]--; break;
+ case 6: if (state.weight[2] < 16) state.weight[2]++; break;
+ }
+ }
+ }
+ }
+
+ filter->filteredblockaddress = length;
+ filter->filteredblocklength = length;
+
+ return 1;
+}
+
+
+static int
+execute_filter(struct archive_read *a, struct rar_filter *filter, struct rar_virtual_machine *vm, size_t pos)
+{
+ if (filter->prog->fingerprint == 0x1D0E06077D)
+ return execute_filter_delta(filter, vm);
+ if (filter->prog->fingerprint == 0x35AD576887)
+ return execute_filter_e8(filter, vm, pos, 0);
+ if (filter->prog->fingerprint == 0x393CD7E57E)
+ return execute_filter_e8(filter, vm, pos, 1);
+ if (filter->prog->fingerprint == 0x951C2C5DC8)
+ return execute_filter_rgb(filter, vm);
+ if (filter->prog->fingerprint == 0xD8BC85E701)
+ return execute_filter_audio(filter, vm);
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No support for RAR VM program filter");
+ return 0;
+}
+
+static int
+rar_decode_byte(struct archive_read *a, uint8_t *byte)
+{
+ struct rar *rar = (struct rar *)(a->format->data);
+ struct rar_br *br = &(rar->br);
+ if (!rar_br_read_ahead(a, br, 8))
+ return 0;
+ *byte = (uint8_t)rar_br_bits(br, 8);
+ rar_br_consume(br, 8);
+ return 1;
+}
+
+static inline void
+vm_write_32(struct rar_virtual_machine* vm, size_t offset, uint32_t u32)
+{
+ archive_le32enc(vm->memory + offset, u32);
+}
+
+static inline uint32_t
+vm_read_32(struct rar_virtual_machine* vm, size_t offset)
+{
+ return archive_le32dec(vm->memory + offset);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_rar5.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_rar5.c
new file mode 100644
index 0000000000..52fe8b10ff
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_rar5.c
@@ -0,0 +1,4251 @@
+/*-
+* Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org)
+* All rights reserved.
+*
+* 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.
+*
+* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+*/
+
+#include "archive_platform.h"
+#include "archive_endian.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* crc32 */
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_entry_private.h"
+
+#ifdef HAVE_BLAKE2_H
+#include <blake2.h>
+#else
+#error #include "archive_blake2.h"
+#endif
+
+/*#define CHECK_CRC_ON_SOLID_SKIP*/
+/*#define DONT_FAIL_ON_CRC_ERROR*/
+/*#define DEBUG*/
+
+#define rar5_min(a, b) (((a) > (b)) ? (b) : (a))
+#define rar5_max(a, b) (((a) > (b)) ? (a) : (b))
+#define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X)))
+
+#if defined DEBUG
+#define DEBUG_CODE if(1)
+#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0)
+#else
+#define DEBUG_CODE if(0)
+#endif
+
+/* Real RAR5 magic number is:
+ *
+ * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00
+ * "Rar!→•☺·\x00"
+ *
+ * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't
+ * want to put this magic sequence in each binary that uses libarchive, so
+ * applications that scan through the file for this marker won't trigger on
+ * this "false" one.
+ *
+ * The array itself is decrypted in `rar5_init` function. */
+
+static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 };
+static const size_t g_unpack_window_size = 0x20000;
+
+/* These could have been static const's, but they aren't, because of
+ * Visual Studio. */
+#define MAX_NAME_IN_CHARS 2048
+#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS)
+
+struct file_header {
+ ssize_t bytes_remaining;
+ ssize_t unpacked_size;
+ int64_t last_offset; /* Used in sanity checks. */
+ int64_t last_size; /* Used in sanity checks. */
+
+ uint8_t solid : 1; /* Is this a solid stream? */
+ uint8_t service : 1; /* Is this file a service data? */
+ uint8_t eof : 1; /* Did we finish unpacking the file? */
+ uint8_t dir : 1; /* Is this file entry a directory? */
+
+ /* Optional time fields. */
+ uint64_t e_mtime;
+ uint64_t e_ctime;
+ uint64_t e_atime;
+ uint32_t e_unix_ns;
+
+ /* Optional hash fields. */
+ uint32_t stored_crc32;
+ uint32_t calculated_crc32;
+ uint8_t blake2sp[32];
+ blake2sp_state b2state;
+ char has_blake2;
+
+ /* Optional redir fields */
+ uint64_t redir_type;
+ uint64_t redir_flags;
+
+ ssize_t solid_window_size; /* Used in file format check. */
+};
+
+enum EXTRA {
+ EX_CRYPT = 0x01,
+ EX_HASH = 0x02,
+ EX_HTIME = 0x03,
+ EX_VERSION = 0x04,
+ EX_REDIR = 0x05,
+ EX_UOWNER = 0x06,
+ EX_SUBDATA = 0x07
+};
+
+#define REDIR_SYMLINK_IS_DIR 1
+
+enum REDIR_TYPE {
+ REDIR_TYPE_NONE = 0,
+ REDIR_TYPE_UNIXSYMLINK = 1,
+ REDIR_TYPE_WINSYMLINK = 2,
+ REDIR_TYPE_JUNCTION = 3,
+ REDIR_TYPE_HARDLINK = 4,
+ REDIR_TYPE_FILECOPY = 5,
+};
+
+#define OWNER_USER_NAME 0x01
+#define OWNER_GROUP_NAME 0x02
+#define OWNER_USER_UID 0x04
+#define OWNER_GROUP_GID 0x08
+#define OWNER_MAXNAMELEN 256
+
+enum FILTER_TYPE {
+ FILTER_DELTA = 0, /* Generic pattern. */
+ FILTER_E8 = 1, /* Intel x86 code. */
+ FILTER_E8E9 = 2, /* Intel x86 code. */
+ FILTER_ARM = 3, /* ARM code. */
+ FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */
+ FILTER_RGB = 5, /* Color palette, not used in RARv5. */
+ FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */
+ FILTER_PPM = 7, /* Predictive pattern matching, not used in
+ RARv5. */
+ FILTER_NONE = 8,
+};
+
+struct filter_info {
+ int type;
+ int channels;
+ int pos_r;
+
+ int64_t block_start;
+ ssize_t block_length;
+ uint16_t width;
+};
+
+struct data_ready {
+ char used;
+ const uint8_t* buf;
+ size_t size;
+ int64_t offset;
+};
+
+struct cdeque {
+ uint16_t beg_pos;
+ uint16_t end_pos;
+ uint16_t cap_mask;
+ uint16_t size;
+ size_t* arr;
+};
+
+struct decode_table {
+ uint32_t size;
+ int32_t decode_len[16];
+ uint32_t decode_pos[16];
+ uint32_t quick_bits;
+ uint8_t quick_len[1 << 10];
+ uint16_t quick_num[1 << 10];
+ uint16_t decode_num[306];
+};
+
+struct comp_state {
+ /* Flag used to specify if unpacker needs to reinitialize the
+ uncompression context. */
+ uint8_t initialized : 1;
+
+ /* Flag used when applying filters. */
+ uint8_t all_filters_applied : 1;
+
+ /* Flag used to skip file context reinitialization, used when unpacker
+ is skipping through different multivolume archives. */
+ uint8_t switch_multivolume : 1;
+
+ /* Flag used to specify if unpacker has processed the whole data block
+ or just a part of it. */
+ uint8_t block_parsing_finished : 1;
+
+ signed int notused : 4;
+
+ int flags; /* Uncompression flags. */
+ int method; /* Uncompression algorithm method. */
+ int version; /* Uncompression algorithm version. */
+ ssize_t window_size; /* Size of window_buf. */
+ uint8_t* window_buf; /* Circular buffer used during
+ decompression. */
+ uint8_t* filtered_buf; /* Buffer used when applying filters. */
+ const uint8_t* block_buf; /* Buffer used when merging blocks. */
+ size_t window_mask; /* Convenience field; window_size - 1. */
+ int64_t write_ptr; /* This amount of data has been unpacked
+ in the window buffer. */
+ int64_t last_write_ptr; /* This amount of data has been stored in
+ the output file. */
+ int64_t last_unstore_ptr; /* Counter of bytes extracted during
+ unstoring. This is separate from
+ last_write_ptr because of how SERVICE
+ base blocks are handled during skipping
+ in solid multiarchive archives. */
+ int64_t solid_offset; /* Additional offset inside the window
+ buffer, used in unpacking solid
+ archives. */
+ ssize_t cur_block_size; /* Size of current data block. */
+ int last_len; /* Flag used in lzss decompression. */
+
+ /* Decode tables used during lzss uncompression. */
+
+#define HUFF_BC 20
+ struct decode_table bd; /* huffman bit lengths */
+#define HUFF_NC 306
+ struct decode_table ld; /* literals */
+#define HUFF_DC 64
+ struct decode_table dd; /* distances */
+#define HUFF_LDC 16
+ struct decode_table ldd; /* lower bits of distances */
+#define HUFF_RC 44
+ struct decode_table rd; /* repeating distances */
+#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC)
+
+ /* Circular deque for storing filters. */
+ struct cdeque filters;
+ int64_t last_block_start; /* Used for sanity checking. */
+ ssize_t last_block_length; /* Used for sanity checking. */
+
+ /* Distance cache used during lzss uncompression. */
+ int dist_cache[4];
+
+ /* Data buffer stack. */
+ struct data_ready dready[2];
+};
+
+/* Bit reader state. */
+struct bit_reader {
+ int8_t bit_addr; /* Current bit pointer inside current byte. */
+ int in_addr; /* Current byte pointer. */
+};
+
+/* RARv5 block header structure. Use bf_* functions to get values from
+ * block_flags_u8 field. I.e. bf_byte_count, etc. */
+struct compressed_block_header {
+ /* block_flags_u8 contain fields encoded in little-endian bitfield:
+ *
+ * - table present flag (shr 7, and 1),
+ * - last block flag (shr 6, and 1),
+ * - byte_count (shr 3, and 7),
+ * - bit_size (shr 0, and 7).
+ */
+ uint8_t block_flags_u8;
+ uint8_t block_cksum;
+};
+
+/* RARv5 main header structure. */
+struct main_header {
+ /* Does the archive contain solid streams? */
+ uint8_t solid : 1;
+
+ /* If this a multi-file archive? */
+ uint8_t volume : 1;
+ uint8_t endarc : 1;
+ uint8_t notused : 5;
+
+ unsigned int vol_no;
+};
+
+struct generic_header {
+ uint8_t split_after : 1;
+ uint8_t split_before : 1;
+ uint8_t padding : 6;
+ int size;
+ int last_header_id;
+};
+
+struct multivolume {
+ unsigned int expected_vol_no;
+ uint8_t* push_buf;
+};
+
+/* Main context structure. */
+struct rar5 {
+ int header_initialized;
+
+ /* Set to 1 if current file is positioned AFTER the magic value
+ * of the archive file. This is used in header reading functions. */
+ int skipped_magic;
+
+ /* Set to not zero if we're in skip mode (either by calling
+ * rar5_data_skip function or when skipping over solid streams).
+ * Set to 0 when in * extraction mode. This is used during checksum
+ * calculation functions. */
+ int skip_mode;
+
+ /* Set to not zero if we're in block merging mode (i.e. when switching
+ * to another file in multivolume archive, last block from 1st archive
+ * needs to be merged with 1st block from 2nd archive). This flag
+ * guards against recursive use of the merging function, which doesn't
+ * support recursive calls. */
+ int merge_mode;
+
+ /* An offset to QuickOpen list. This is not supported by this unpacker,
+ * because we're focusing on streaming interface. QuickOpen is designed
+ * to make things quicker for non-stream interfaces, so it's not our
+ * use case. */
+ uint64_t qlist_offset;
+
+ /* An offset to additional Recovery data. This is not supported by this
+ * unpacker. Recovery data are additional Reed-Solomon codes that could
+ * be used to calculate bytes that are missing in archive or are
+ * corrupted. */
+ uint64_t rr_offset;
+
+ /* Various context variables grouped to different structures. */
+ struct generic_header generic;
+ struct main_header main;
+ struct comp_state cstate;
+ struct file_header file;
+ struct bit_reader bits;
+ struct multivolume vol;
+
+ /* The header of currently processed RARv5 block. Used in main
+ * decompression logic loop. */
+ struct compressed_block_header last_block_hdr;
+};
+
+/* Forward function declarations. */
+
+static void rar5_signature(char *buf);
+static int verify_global_checksums(struct archive_read* a);
+static int rar5_read_data_skip(struct archive_read *a);
+static int push_data_ready(struct archive_read* a, struct rar5* rar,
+ const uint8_t* buf, size_t size, int64_t offset);
+
+/* CDE_xxx = Circular Double Ended (Queue) return values. */
+enum CDE_RETURN_VALUES {
+ CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,
+};
+
+/* Clears the contents of this circular deque. */
+static void cdeque_clear(struct cdeque* d) {
+ d->size = 0;
+ d->beg_pos = 0;
+ d->end_pos = 0;
+}
+
+/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32,
+ * 64, 256, etc. When the user will add another item above current capacity,
+ * the circular deque will overwrite the oldest entry. */
+static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {
+ if(d == NULL || max_capacity_power_of_2 == 0)
+ return CDE_PARAM;
+
+ d->cap_mask = max_capacity_power_of_2 - 1;
+ d->arr = NULL;
+
+ if((max_capacity_power_of_2 & d->cap_mask) != 0)
+ return CDE_PARAM;
+
+ cdeque_clear(d);
+ d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);
+
+ return d->arr ? CDE_OK : CDE_ALLOC;
+}
+
+/* Return the current size (not capacity) of circular deque `d`. */
+static size_t cdeque_size(struct cdeque* d) {
+ return d->size;
+}
+
+/* Returns the first element of current circular deque. Note that this function
+ * doesn't perform any bounds checking. If you need bounds checking, use
+ * `cdeque_front()` function instead. */
+static void cdeque_front_fast(struct cdeque* d, void** value) {
+ *value = (void*) d->arr[d->beg_pos];
+}
+
+/* Returns the first element of current circular deque. This function
+ * performs bounds checking. */
+static int cdeque_front(struct cdeque* d, void** value) {
+ if(d->size > 0) {
+ cdeque_front_fast(d, value);
+ return CDE_OK;
+ } else
+ return CDE_OUT_OF_BOUNDS;
+}
+
+/* Pushes a new element into the end of this circular deque object. If current
+ * size will exceed capacity, the oldest element will be overwritten. */
+static int cdeque_push_back(struct cdeque* d, void* item) {
+ if(d == NULL)
+ return CDE_PARAM;
+
+ if(d->size == d->cap_mask + 1)
+ return CDE_OUT_OF_BOUNDS;
+
+ d->arr[d->end_pos] = (size_t) item;
+ d->end_pos = (d->end_pos + 1) & d->cap_mask;
+ d->size++;
+
+ return CDE_OK;
+}
+
+/* Pops a front element of this circular deque object and returns its value.
+ * This function doesn't perform any bounds checking. */
+static void cdeque_pop_front_fast(struct cdeque* d, void** value) {
+ *value = (void*) d->arr[d->beg_pos];
+ d->beg_pos = (d->beg_pos + 1) & d->cap_mask;
+ d->size--;
+}
+
+/* Pops a front element of this circular deque object and returns its value.
+ * This function performs bounds checking. */
+static int cdeque_pop_front(struct cdeque* d, void** value) {
+ if(!d || !value)
+ return CDE_PARAM;
+
+ if(d->size == 0)
+ return CDE_OUT_OF_BOUNDS;
+
+ cdeque_pop_front_fast(d, value);
+ return CDE_OK;
+}
+
+/* Convenience function to cast filter_info** to void **. */
+static void** cdeque_filter_p(struct filter_info** f) {
+ return (void**) (size_t) f;
+}
+
+/* Convenience function to cast filter_info* to void *. */
+static void* cdeque_filter(struct filter_info* f) {
+ return (void**) (size_t) f;
+}
+
+/* Destroys this circular deque object. Deallocates the memory of the
+ * collection buffer, but doesn't deallocate the memory of any pointer passed
+ * to this deque as a value. */
+static void cdeque_free(struct cdeque* d) {
+ if(!d)
+ return;
+
+ if(!d->arr)
+ return;
+
+ free(d->arr);
+
+ d->arr = NULL;
+ d->beg_pos = -1;
+ d->end_pos = -1;
+ d->cap_mask = 0;
+}
+
+static inline
+uint8_t bf_bit_size(const struct compressed_block_header* hdr) {
+ return hdr->block_flags_u8 & 7;
+}
+
+static inline
+uint8_t bf_byte_count(const struct compressed_block_header* hdr) {
+ return (hdr->block_flags_u8 >> 3) & 7;
+}
+
+static inline
+uint8_t bf_is_table_present(const struct compressed_block_header* hdr) {
+ return (hdr->block_flags_u8 >> 7) & 1;
+}
+
+static inline struct rar5* get_context(struct archive_read* a) {
+ return (struct rar5*) a->format->data;
+}
+
+/* Convenience functions used by filter implementations. */
+static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask,
+ int64_t start, int64_t end)
+{
+ if((start & mask) > (end & mask)) {
+ ssize_t len1 = mask + 1 - (start & mask);
+ ssize_t len2 = end & mask;
+
+ memcpy(dst, &window[start & mask], len1);
+ memcpy(dst + len1, window, len2);
+ } else {
+ memcpy(dst, &window[start & mask], (size_t) (end - start));
+ }
+}
+
+static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) {
+ uint8_t linear_buf[4];
+ circular_memcpy(linear_buf, rar->cstate.window_buf,
+ rar->cstate.window_mask, offset, offset + 4);
+ return archive_le32dec(linear_buf);
+}
+
+static void write_filter_data(struct rar5* rar, uint32_t offset,
+ uint32_t value)
+{
+ archive_le32enc(&rar->cstate.filtered_buf[offset], value);
+}
+
+/* Allocates a new filter descriptor and adds it to the filter array. */
+static struct filter_info* add_new_filter(struct rar5* rar) {
+ struct filter_info* f =
+ (struct filter_info*) calloc(1, sizeof(struct filter_info));
+
+ if(!f) {
+ return NULL;
+ }
+
+ cdeque_push_back(&rar->cstate.filters, cdeque_filter(f));
+ return f;
+}
+
+static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
+ int i;
+ ssize_t dest_pos, src_pos = 0;
+
+ for(i = 0; i < flt->channels; i++) {
+ uint8_t prev_byte = 0;
+ for(dest_pos = i;
+ dest_pos < flt->block_length;
+ dest_pos += flt->channels)
+ {
+ uint8_t byte;
+
+ byte = rar->cstate.window_buf[
+ (rar->cstate.solid_offset + flt->block_start +
+ src_pos) & rar->cstate.window_mask];
+
+ prev_byte -= byte;
+ rar->cstate.filtered_buf[dest_pos] = prev_byte;
+ src_pos++;
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,
+ int extended)
+{
+ const uint32_t file_size = 0x1000000;
+ ssize_t i;
+
+ circular_memcpy(rar->cstate.filtered_buf,
+ rar->cstate.window_buf, rar->cstate.window_mask,
+ rar->cstate.solid_offset + flt->block_start,
+ rar->cstate.solid_offset + flt->block_start + flt->block_length);
+
+ for(i = 0; i < flt->block_length - 4;) {
+ uint8_t b = rar->cstate.window_buf[
+ (rar->cstate.solid_offset + flt->block_start +
+ i++) & rar->cstate.window_mask];
+
+ /*
+ * 0xE8 = x86's call <relative_addr_uint32> (function call)
+ * 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump)
+ */
+ if(b == 0xE8 || (extended && b == 0xE9)) {
+
+ uint32_t addr;
+ uint32_t offset = (i + flt->block_start) % file_size;
+
+ addr = read_filter_data(rar,
+ (uint32_t)(rar->cstate.solid_offset +
+ flt->block_start + i) & rar->cstate.window_mask);
+
+ if(addr & 0x80000000) {
+ if(((addr + offset) & 0x80000000) == 0) {
+ write_filter_data(rar, (uint32_t)i,
+ addr + file_size);
+ }
+ } else {
+ if((addr - file_size) & 0x80000000) {
+ uint32_t naddr = addr - offset;
+ write_filter_data(rar, (uint32_t)i,
+ naddr);
+ }
+ }
+
+ i += 4;
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int run_arm_filter(struct rar5* rar, struct filter_info* flt) {
+ ssize_t i = 0;
+ uint32_t offset;
+
+ circular_memcpy(rar->cstate.filtered_buf,
+ rar->cstate.window_buf, rar->cstate.window_mask,
+ rar->cstate.solid_offset + flt->block_start,
+ rar->cstate.solid_offset + flt->block_start + flt->block_length);
+
+ for(i = 0; i < flt->block_length - 3; i += 4) {
+ uint8_t* b = &rar->cstate.window_buf[
+ (rar->cstate.solid_offset +
+ flt->block_start + i + 3) & rar->cstate.window_mask];
+
+ if(*b == 0xEB) {
+ /* 0xEB = ARM's BL (branch + link) instruction. */
+ offset = read_filter_data(rar,
+ (rar->cstate.solid_offset + flt->block_start + i) &
+ (uint32_t)rar->cstate.window_mask) & 0x00ffffff;
+
+ offset -= (uint32_t) ((i + flt->block_start) / 4);
+ offset = (offset & 0x00ffffff) | 0xeb000000;
+ write_filter_data(rar, (uint32_t)i, offset);
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int run_filter(struct archive_read* a, struct filter_info* flt) {
+ int ret;
+ struct rar5* rar = get_context(a);
+
+ free(rar->cstate.filtered_buf);
+
+ rar->cstate.filtered_buf = malloc(flt->block_length);
+ if(!rar->cstate.filtered_buf) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for filter data.");
+ return ARCHIVE_FATAL;
+ }
+
+ switch(flt->type) {
+ case FILTER_DELTA:
+ ret = run_delta_filter(rar, flt);
+ break;
+
+ case FILTER_E8:
+ /* fallthrough */
+ case FILTER_E8E9:
+ ret = run_e8e9_filter(rar, flt,
+ flt->type == FILTER_E8E9);
+ break;
+
+ case FILTER_ARM:
+ ret = run_arm_filter(rar, flt);
+ break;
+
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported filter type: 0x%x", flt->type);
+ return ARCHIVE_FATAL;
+ }
+
+ if(ret != ARCHIVE_OK) {
+ /* Filter has failed. */
+ return ret;
+ }
+
+ if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,
+ flt->block_length, rar->cstate.last_write_ptr))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Stack overflow when submitting unpacked data");
+
+ return ARCHIVE_FATAL;
+ }
+
+ rar->cstate.last_write_ptr += flt->block_length;
+ return ARCHIVE_OK;
+}
+
+/* The `push_data` function submits the selected data range to the user.
+ * Next call of `use_data` will use the pointer, size and offset arguments
+ * that are specified here. These arguments are pushed to the FIFO stack here,
+ * and popped from the stack by the `use_data` function. */
+static void push_data(struct archive_read* a, struct rar5* rar,
+ const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
+{
+ const uint64_t wmask = rar->cstate.window_mask;
+ const ssize_t solid_write_ptr = (rar->cstate.solid_offset +
+ rar->cstate.last_write_ptr) & wmask;
+
+ idx_begin += rar->cstate.solid_offset;
+ idx_end += rar->cstate.solid_offset;
+
+ /* Check if our unpacked data is wrapped inside the window circular
+ * buffer. If it's not wrapped, it can be copied out by using
+ * a single memcpy, but when it's wrapped, we need to copy the first
+ * part with one memcpy, and the second part with another memcpy. */
+
+ if((idx_begin & wmask) > (idx_end & wmask)) {
+ /* The data is wrapped (begin offset sis bigger than end
+ * offset). */
+ const ssize_t frag1_size = rar->cstate.window_size -
+ (idx_begin & wmask);
+ const ssize_t frag2_size = idx_end & wmask;
+
+ /* Copy the first part of the buffer first. */
+ push_data_ready(a, rar, buf + solid_write_ptr, frag1_size,
+ rar->cstate.last_write_ptr);
+
+ /* Copy the second part of the buffer. */
+ push_data_ready(a, rar, buf, frag2_size,
+ rar->cstate.last_write_ptr + frag1_size);
+
+ rar->cstate.last_write_ptr += frag1_size + frag2_size;
+ } else {
+ /* Data is not wrapped, so we can just use one call to copy the
+ * data. */
+ push_data_ready(a, rar,
+ buf + solid_write_ptr, (idx_end - idx_begin) & wmask,
+ rar->cstate.last_write_ptr);
+
+ rar->cstate.last_write_ptr += idx_end - idx_begin;
+ }
+}
+
+/* Convenience function that submits the data to the user. It uses the
+ * unpack window buffer as a source location. */
+static void push_window_data(struct archive_read* a, struct rar5* rar,
+ int64_t idx_begin, int64_t idx_end)
+{
+ push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);
+}
+
+static int apply_filters(struct archive_read* a) {
+ struct filter_info* flt;
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ rar->cstate.all_filters_applied = 0;
+
+ /* Get the first filter that can be applied to our data. The data
+ * needs to be fully unpacked before the filter can be run. */
+ if(CDE_OK == cdeque_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt))) {
+ /* Check if our unpacked data fully covers this filter's
+ * range. */
+ if(rar->cstate.write_ptr > flt->block_start &&
+ rar->cstate.write_ptr >= flt->block_start +
+ flt->block_length) {
+ /* Check if we have some data pending to be written
+ * right before the filter's start offset. */
+ if(rar->cstate.last_write_ptr == flt->block_start) {
+ /* Run the filter specified by descriptor
+ * `flt`. */
+ ret = run_filter(a, flt);
+ if(ret != ARCHIVE_OK) {
+ /* Filter failure, return error. */
+ return ret;
+ }
+
+ /* Filter descriptor won't be needed anymore
+ * after it's used, * so remove it from the
+ * filter list and free its memory. */
+ (void) cdeque_pop_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt));
+
+ free(flt);
+ } else {
+ /* We can't run filters yet, dump the memory
+ * right before the filter. */
+ push_window_data(a, rar,
+ rar->cstate.last_write_ptr,
+ flt->block_start);
+ }
+
+ /* Return 'filter applied or not needed' state to the
+ * caller. */
+ return ARCHIVE_RETRY;
+ }
+ }
+
+ rar->cstate.all_filters_applied = 1;
+ return ARCHIVE_OK;
+}
+
+static void dist_cache_push(struct rar5* rar, int value) {
+ int* q = rar->cstate.dist_cache;
+
+ q[3] = q[2];
+ q[2] = q[1];
+ q[1] = q[0];
+ q[0] = value;
+}
+
+static int dist_cache_touch(struct rar5* rar, int idx) {
+ int* q = rar->cstate.dist_cache;
+ int i, dist = q[idx];
+
+ for(i = idx; i > 0; i--)
+ q[i] = q[i - 1];
+
+ q[0] = dist;
+ return dist;
+}
+
+static void free_filters(struct rar5* rar) {
+ struct cdeque* d = &rar->cstate.filters;
+
+ /* Free any remaining filters. All filters should be naturally
+ * consumed by the unpacking function, so remaining filters after
+ * unpacking normally mean that unpacking wasn't successful.
+ * But still of course we shouldn't leak memory in such case. */
+
+ /* cdeque_size() is a fast operation, so we can use it as a loop
+ * expression. */
+ while(cdeque_size(d) > 0) {
+ struct filter_info* f = NULL;
+
+ /* Pop_front will also decrease the collection's size. */
+ if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))
+ free(f);
+ }
+
+ cdeque_clear(d);
+
+ /* Also clear out the variables needed for sanity checking. */
+ rar->cstate.last_block_start = 0;
+ rar->cstate.last_block_length = 0;
+}
+
+static void reset_file_context(struct rar5* rar) {
+ memset(&rar->file, 0, sizeof(rar->file));
+ blake2sp_init(&rar->file.b2state, 32);
+
+ if(rar->main.solid) {
+ rar->cstate.solid_offset += rar->cstate.write_ptr;
+ } else {
+ rar->cstate.solid_offset = 0;
+ }
+
+ rar->cstate.write_ptr = 0;
+ rar->cstate.last_write_ptr = 0;
+ rar->cstate.last_unstore_ptr = 0;
+
+ rar->file.redir_type = REDIR_TYPE_NONE;
+ rar->file.redir_flags = 0;
+
+ free_filters(rar);
+}
+
+static inline int get_archive_read(struct archive* a,
+ struct archive_read** ar)
+{
+ *ar = (struct archive_read*) a;
+ archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_read_support_format_rar5");
+
+ return ARCHIVE_OK;
+}
+
+static int read_ahead(struct archive_read* a, size_t how_many,
+ const uint8_t** ptr)
+{
+ ssize_t avail = -1;
+ if(!ptr)
+ return 0;
+
+ *ptr = __archive_read_ahead(a, how_many, &avail);
+ if(*ptr == NULL) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int consume(struct archive_read* a, int64_t how_many) {
+ int ret;
+
+ ret = how_many == __archive_read_consume(a, how_many)
+ ? ARCHIVE_OK
+ : ARCHIVE_FATAL;
+
+ return ret;
+}
+
+/**
+ * Read a RAR5 variable sized numeric value. This value will be stored in
+ * `pvalue`. The `pvalue_len` argument points to a variable that will receive
+ * the byte count that was consumed in order to decode the `pvalue` value, plus
+ * one.
+ *
+ * pvalue_len is optional and can be NULL.
+ *
+ * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume
+ * the number of bytes that `pvalue_len` value contains. If the `pvalue_len`
+ * is NULL, this consuming operation is done automatically.
+ *
+ * Returns 1 if *pvalue was successfully read.
+ * Returns 0 if there was an error. In this case, *pvalue contains an
+ * invalid value.
+ */
+
+static int read_var(struct archive_read* a, uint64_t* pvalue,
+ uint64_t* pvalue_len)
+{
+ uint64_t result = 0;
+ size_t shift, i;
+ const uint8_t* p;
+ uint8_t b;
+
+ /* We will read maximum of 8 bytes. We don't have to handle the
+ * situation to read the RAR5 variable-sized value stored at the end of
+ * the file, because such situation will never happen. */
+ if(!read_ahead(a, 8, &p))
+ return 0;
+
+ for(shift = 0, i = 0; i < 8; i++, shift += 7) {
+ b = p[i];
+
+ /* Strip the MSB from the input byte and add the resulting
+ * number to the `result`. */
+ result += (b & (uint64_t)0x7F) << shift;
+
+ /* MSB set to 1 means we need to continue decoding process.
+ * MSB set to 0 means we're done.
+ *
+ * This conditional checks for the second case. */
+ if((b & 0x80) == 0) {
+ if(pvalue) {
+ *pvalue = result;
+ }
+
+ /* If the caller has passed the `pvalue_len` pointer,
+ * store the number of consumed bytes in it and do NOT
+ * consume those bytes, since the caller has all the
+ * information it needs to perform */
+ if(pvalue_len) {
+ *pvalue_len = 1 + i;
+ } else {
+ /* If the caller did not provide the
+ * `pvalue_len` pointer, it will not have the
+ * possibility to advance the file pointer,
+ * because it will not know how many bytes it
+ * needs to consume. This is why we handle
+ * such situation here automatically. */
+ if(ARCHIVE_OK != consume(a, 1 + i)) {
+ return 0;
+ }
+ }
+
+ /* End of decoding process, return success. */
+ return 1;
+ }
+ }
+
+ /* The decoded value takes the maximum number of 8 bytes.
+ * It's a maximum number of bytes, so end decoding process here
+ * even if the first bit of last byte is 1. */
+ if(pvalue) {
+ *pvalue = result;
+ }
+
+ if(pvalue_len) {
+ *pvalue_len = 9;
+ } else {
+ if(ARCHIVE_OK != consume(a, 9)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int read_var_sized(struct archive_read* a, size_t* pvalue,
+ size_t* pvalue_len)
+{
+ uint64_t v;
+ uint64_t v_size = 0;
+
+ const int ret = pvalue_len ? read_var(a, &v, &v_size)
+ : read_var(a, &v, NULL);
+
+ if(ret == 1 && pvalue) {
+ *pvalue = (size_t) v;
+ }
+
+ if(pvalue_len) {
+ /* Possible data truncation should be safe. */
+ *pvalue_len = (size_t) v_size;
+ }
+
+ return ret;
+}
+
+static int read_bits_32(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, uint32_t* value)
+{
+ if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Premature end of stream during extraction of data (#1)");
+ return ARCHIVE_FATAL;
+ }
+
+ uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24;
+ bits |= p[rar->bits.in_addr + 1] << 16;
+ bits |= p[rar->bits.in_addr + 2] << 8;
+ bits |= p[rar->bits.in_addr + 3];
+ bits <<= rar->bits.bit_addr;
+ bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr);
+ *value = bits;
+ return ARCHIVE_OK;
+}
+
+static int read_bits_16(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, uint16_t* value)
+{
+ if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Premature end of stream during extraction of data (#2)");
+ return ARCHIVE_FATAL;
+ }
+
+ int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16;
+ bits |= (int) p[rar->bits.in_addr + 1] << 8;
+ bits |= (int) p[rar->bits.in_addr + 2];
+ bits >>= (8 - rar->bits.bit_addr);
+ *value = bits & 0xffff;
+ return ARCHIVE_OK;
+}
+
+static void skip_bits(struct rar5* rar, int bits) {
+ const int new_bits = rar->bits.bit_addr + bits;
+ rar->bits.in_addr += new_bits >> 3;
+ rar->bits.bit_addr = new_bits & 7;
+}
+
+/* n = up to 16 */
+static int read_consume_bits(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, int n, int* value)
+{
+ uint16_t v;
+ int ret, num;
+
+ if(n == 0 || n > 16) {
+ /* This is a programmer error and should never happen
+ * in runtime. */
+ return ARCHIVE_FATAL;
+ }
+
+ ret = read_bits_16(a, rar, p, &v);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ num = (int) v;
+ num >>= 16 - n;
+
+ skip_bits(rar, n);
+
+ if(value)
+ *value = num;
+
+ return ARCHIVE_OK;
+}
+
+static int read_u32(struct archive_read* a, uint32_t* pvalue) {
+ const uint8_t* p;
+ if(!read_ahead(a, 4, &p))
+ return 0;
+
+ *pvalue = archive_le32dec(p);
+ return ARCHIVE_OK == consume(a, 4) ? 1 : 0;
+}
+
+static int read_u64(struct archive_read* a, uint64_t* pvalue) {
+ const uint8_t* p;
+ if(!read_ahead(a, 8, &p))
+ return 0;
+
+ *pvalue = archive_le64dec(p);
+ return ARCHIVE_OK == consume(a, 8) ? 1 : 0;
+}
+
+static int bid_standard(struct archive_read* a) {
+ const uint8_t* p;
+ char signature[sizeof(rar5_signature_xor)];
+
+ rar5_signature(signature);
+
+ if(!read_ahead(a, sizeof(rar5_signature_xor), &p))
+ return -1;
+
+ if(!memcmp(signature, p, sizeof(rar5_signature_xor)))
+ return 30;
+
+ return -1;
+}
+
+static int bid_sfx(struct archive_read *a)
+{
+ const char *p;
+
+ if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return -1;
+
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+ /* This is a PE file */
+ char signature[sizeof(rar5_signature_xor)];
+ ssize_t offset = 0x10000;
+ ssize_t window = 4096;
+ ssize_t bytes_avail;
+
+ rar5_signature(signature);
+
+ while (offset + window <= (1024 * 512)) {
+ const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);
+ if (buff == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ return 0;
+ continue;
+ }
+ p = buff + offset;
+ while (p + 8 < buff + bytes_avail) {
+ if (memcmp(p, signature, sizeof(signature)) == 0)
+ return 30;
+ p += 0x10;
+ }
+ offset = p - buff;
+ }
+ }
+
+ return 0;
+}
+
+static int rar5_bid(struct archive_read* a, int best_bid) {
+ int my_bid;
+
+ if(best_bid > 30)
+ return -1;
+
+ my_bid = bid_standard(a);
+ if(my_bid > -1) {
+ return my_bid;
+ }
+ my_bid = bid_sfx(a);
+ if (my_bid > -1) {
+ return my_bid;
+ }
+
+ return -1;
+}
+
+static int rar5_options(struct archive_read *a, const char *key,
+ const char *val) {
+ (void) a;
+ (void) key;
+ (void) val;
+
+ /* No options supported in this version. Return the ARCHIVE_WARN code
+ * to signal the options supervisor that the unpacker didn't handle
+ * setting this option. */
+
+ return ARCHIVE_WARN;
+}
+
+static void init_header(struct archive_read* a) {
+ a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;
+ a->archive.archive_format_name = "RAR5";
+}
+
+static void init_window_mask(struct rar5* rar) {
+ if (rar->cstate.window_size)
+ rar->cstate.window_mask = rar->cstate.window_size - 1;
+ else
+ rar->cstate.window_mask = 0;
+}
+
+enum HEADER_FLAGS {
+ HFL_EXTRA_DATA = 0x0001,
+ HFL_DATA = 0x0002,
+ HFL_SKIP_IF_UNKNOWN = 0x0004,
+ HFL_SPLIT_BEFORE = 0x0008,
+ HFL_SPLIT_AFTER = 0x0010,
+ HFL_CHILD = 0x0020,
+ HFL_INHERITED = 0x0040
+};
+
+static int process_main_locator_extra_block(struct archive_read* a,
+ struct rar5* rar)
+{
+ uint64_t locator_flags;
+
+ enum LOCATOR_FLAGS {
+ QLIST = 0x01, RECOVERY = 0x02,
+ };
+
+ if(!read_var(a, &locator_flags, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(locator_flags & QLIST) {
+ if(!read_var(a, &rar->qlist_offset, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* qlist is not used */
+ }
+
+ if(locator_flags & RECOVERY) {
+ if(!read_var(a, &rar->rr_offset, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* rr is not used */
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
+ ssize_t* extra_data_size)
+{
+ size_t hash_type = 0;
+ size_t value_len;
+
+ enum HASH_TYPE {
+ BLAKE2sp = 0x00
+ };
+
+ if(!read_var_sized(a, &hash_type, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* The file uses BLAKE2sp checksum algorithm instead of plain old
+ * CRC32. */
+ if(hash_type == BLAKE2sp) {
+ const uint8_t* p;
+ const int hash_size = sizeof(rar->file.blake2sp);
+
+ if(!read_ahead(a, hash_size, &p))
+ return ARCHIVE_EOF;
+
+ rar->file.has_blake2 = 1;
+ memcpy(&rar->file.blake2sp, p, hash_size);
+
+ if(ARCHIVE_OK != consume(a, hash_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ *extra_data_size -= hash_size;
+ } else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported hash type (0x%x)", (int) hash_type);
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static uint64_t time_win_to_unix(uint64_t win_time) {
+ const size_t ns_in_sec = 10000000;
+ const uint64_t sec_to_unix = 11644473600LL;
+ return win_time / ns_in_sec - sec_to_unix;
+}
+
+static int parse_htime_item(struct archive_read* a, char unix_time,
+ uint64_t* where, ssize_t* extra_data_size)
+{
+ if(unix_time) {
+ uint32_t time_val;
+ if(!read_u32(a, &time_val))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= 4;
+ *where = (uint64_t) time_val;
+ } else {
+ uint64_t windows_time;
+ if(!read_u64(a, &windows_time))
+ return ARCHIVE_EOF;
+
+ *where = time_win_to_unix(windows_time);
+ *extra_data_size -= 8;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_version(struct archive_read* a,
+ struct archive_entry* e, ssize_t* extra_data_size)
+{
+ size_t flags = 0;
+ size_t version = 0;
+ size_t value_len = 0;
+ struct archive_string version_string;
+ struct archive_string name_utf8_string;
+ const char* cur_filename;
+
+ /* Flags are ignored. */
+ if(!read_var_sized(a, &flags, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len))
+ return ARCHIVE_EOF;
+
+ if(!read_var_sized(a, &version, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len))
+ return ARCHIVE_EOF;
+
+ /* extra_data_size should be zero here. */
+
+ cur_filename = archive_entry_pathname_utf8(e);
+ if(cur_filename == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Version entry without file name");
+ return ARCHIVE_FATAL;
+ }
+
+ archive_string_init(&version_string);
+ archive_string_init(&name_utf8_string);
+
+ /* Prepare a ;123 suffix for the filename, where '123' is the version
+ * value of this file. */
+ archive_string_sprintf(&version_string, ";%zu", version);
+
+ /* Build the new filename. */
+ archive_strcat(&name_utf8_string, cur_filename);
+ archive_strcat(&name_utf8_string, version_string.s);
+
+ /* Apply the new filename into this file's context. */
+ archive_entry_update_pathname_utf8(e, name_utf8_string.s);
+
+ /* Free buffers. */
+ archive_string_free(&version_string);
+ archive_string_free(&name_utf8_string);
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_htime(struct archive_read* a,
+ struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
+{
+ char unix_time = 0;
+ size_t flags = 0;
+ size_t value_len;
+
+ enum HTIME_FLAGS {
+ IS_UNIX = 0x01,
+ HAS_MTIME = 0x02,
+ HAS_CTIME = 0x04,
+ HAS_ATIME = 0x08,
+ HAS_UNIX_NS = 0x10,
+ };
+
+ if(!read_var_sized(a, &flags, &value_len))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= value_len;
+ if(ARCHIVE_OK != consume(a, value_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ unix_time = flags & IS_UNIX;
+
+ if(flags & HAS_MTIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_mtime,
+ extra_data_size);
+ archive_entry_set_mtime(e, rar->file.e_mtime, 0);
+ }
+
+ if(flags & HAS_CTIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_ctime,
+ extra_data_size);
+ archive_entry_set_ctime(e, rar->file.e_ctime, 0);
+ }
+
+ if(flags & HAS_ATIME) {
+ parse_htime_item(a, unix_time, &rar->file.e_atime,
+ extra_data_size);
+ archive_entry_set_atime(e, rar->file.e_atime, 0);
+ }
+
+ if(flags & HAS_UNIX_NS) {
+ if(!read_u32(a, &rar->file.e_unix_ns))
+ return ARCHIVE_EOF;
+
+ *extra_data_size -= 4;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_redir(struct archive_read* a,
+ struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
+{
+ uint64_t value_size = 0;
+ size_t target_size = 0;
+ char target_utf8_buf[MAX_NAME_IN_BYTES];
+ const uint8_t* p;
+
+ if(!read_var(a, &rar->file.redir_type, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if(!read_var(a, &rar->file.redir_flags, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if(!read_var_sized(a, &target_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= target_size + 1;
+
+ if(!read_ahead(a, target_size, &p))
+ return ARCHIVE_EOF;
+
+ if(target_size > (MAX_NAME_IN_CHARS - 1)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Link target is too long");
+ return ARCHIVE_FATAL;
+ }
+
+ if(target_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "No link target specified");
+ return ARCHIVE_FATAL;
+ }
+
+ memcpy(target_utf8_buf, p, target_size);
+ target_utf8_buf[target_size] = 0;
+
+ if(ARCHIVE_OK != consume(a, (int64_t)target_size))
+ return ARCHIVE_EOF;
+
+ switch(rar->file.redir_type) {
+ case REDIR_TYPE_UNIXSYMLINK:
+ case REDIR_TYPE_WINSYMLINK:
+ archive_entry_set_filetype(e, AE_IFLNK);
+ archive_entry_update_symlink_utf8(e, target_utf8_buf);
+ if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) {
+ archive_entry_set_symlink_type(e,
+ AE_SYMLINK_TYPE_DIRECTORY);
+ } else {
+ archive_entry_set_symlink_type(e,
+ AE_SYMLINK_TYPE_FILE);
+ }
+ break;
+
+ case REDIR_TYPE_HARDLINK:
+ archive_entry_set_filetype(e, AE_IFREG);
+ archive_entry_update_hardlink_utf8(e, target_utf8_buf);
+ break;
+
+ default:
+ /* Unknown redir type, skip it. */
+ break;
+ }
+ return ARCHIVE_OK;
+}
+
+static int parse_file_extra_owner(struct archive_read* a,
+ struct archive_entry* e, ssize_t* extra_data_size)
+{
+ uint64_t flags = 0;
+ uint64_t value_size = 0;
+ uint64_t id = 0;
+ size_t name_len = 0;
+ size_t name_size = 0;
+ char namebuf[OWNER_MAXNAMELEN];
+ const uint8_t* p;
+
+ if(!read_var(a, &flags, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ if ((flags & OWNER_USER_NAME) != 0) {
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= name_size + 1;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if (name_size >= OWNER_MAXNAMELEN) {
+ name_len = OWNER_MAXNAMELEN - 1;
+ } else {
+ name_len = name_size;
+ }
+
+ memcpy(namebuf, p, name_len);
+ namebuf[name_len] = 0;
+ if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+ return ARCHIVE_EOF;
+
+ archive_entry_set_uname(e, namebuf);
+ }
+ if ((flags & OWNER_GROUP_NAME) != 0) {
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+ *extra_data_size -= name_size + 1;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if (name_size >= OWNER_MAXNAMELEN) {
+ name_len = OWNER_MAXNAMELEN - 1;
+ } else {
+ name_len = name_size;
+ }
+
+ memcpy(namebuf, p, name_len);
+ namebuf[name_len] = 0;
+ if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+ return ARCHIVE_EOF;
+
+ archive_entry_set_gname(e, namebuf);
+ }
+ if ((flags & OWNER_USER_UID) != 0) {
+ if(!read_var(a, &id, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ archive_entry_set_uid(e, (la_int64_t)id);
+ }
+ if ((flags & OWNER_GROUP_GID) != 0) {
+ if(!read_var(a, &id, &value_size))
+ return ARCHIVE_EOF;
+ if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+ return ARCHIVE_EOF;
+ *extra_data_size -= value_size;
+
+ archive_entry_set_gid(e, (la_int64_t)id);
+ }
+ return ARCHIVE_OK;
+}
+
+static int process_head_file_extra(struct archive_read* a,
+ struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size)
+{
+ size_t extra_field_size;
+ size_t extra_field_id = 0;
+ int ret = ARCHIVE_FATAL;
+ size_t var_size;
+
+ while(extra_data_size > 0) {
+ if(!read_var_sized(a, &extra_field_size, &var_size))
+ return ARCHIVE_EOF;
+
+ extra_data_size -= var_size;
+ if(ARCHIVE_OK != consume(a, var_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &extra_field_id, &var_size))
+ return ARCHIVE_EOF;
+
+ extra_data_size -= var_size;
+ if(ARCHIVE_OK != consume(a, var_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ switch(extra_field_id) {
+ case EX_HASH:
+ ret = parse_file_extra_hash(a, rar,
+ &extra_data_size);
+ break;
+ case EX_HTIME:
+ ret = parse_file_extra_htime(a, e, rar,
+ &extra_data_size);
+ break;
+ case EX_REDIR:
+ ret = parse_file_extra_redir(a, e, rar,
+ &extra_data_size);
+ break;
+ case EX_UOWNER:
+ ret = parse_file_extra_owner(a, e,
+ &extra_data_size);
+ break;
+ case EX_VERSION:
+ ret = parse_file_extra_version(a, e,
+ &extra_data_size);
+ break;
+ case EX_CRYPT:
+ /* fallthrough */
+ case EX_SUBDATA:
+ /* fallthrough */
+ default:
+ /* Skip unsupported entry. */
+ return consume(a, extra_data_size);
+ }
+ }
+
+ if(ret != ARCHIVE_OK) {
+ /* Attribute not implemented. */
+ return ret;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int process_head_file(struct archive_read* a, struct rar5* rar,
+ struct archive_entry* entry, size_t block_flags)
+{
+ ssize_t extra_data_size = 0;
+ size_t data_size = 0;
+ size_t file_flags = 0;
+ size_t file_attr = 0;
+ size_t compression_info = 0;
+ size_t host_os = 0;
+ size_t name_size = 0;
+ uint64_t unpacked_size, window_size;
+ uint32_t mtime = 0, crc = 0;
+ int c_method = 0, c_version = 0;
+ char name_utf8_buf[MAX_NAME_IN_BYTES];
+ const uint8_t* p;
+
+ enum FILE_FLAGS {
+ DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
+ UNKNOWN_UNPACKED_SIZE = 0x0008,
+ };
+
+ enum FILE_ATTRS {
+ ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
+ ATTR_DIRECTORY = 0x10,
+ };
+
+ enum COMP_INFO_FLAGS {
+ SOLID = 0x0040,
+ };
+
+ enum HOST_OS {
+ HOST_WINDOWS = 0,
+ HOST_UNIX = 1,
+ };
+
+ archive_entry_clear(entry);
+
+ /* Do not reset file context if we're switching archives. */
+ if(!rar->cstate.switch_multivolume) {
+ reset_file_context(rar);
+ }
+
+ if(block_flags & HFL_EXTRA_DATA) {
+ size_t edata_size = 0;
+ if(!read_var_sized(a, &edata_size, NULL))
+ return ARCHIVE_EOF;
+
+ /* Intentional type cast from unsigned to signed. */
+ extra_data_size = (ssize_t) edata_size;
+ }
+
+ if(block_flags & HFL_DATA) {
+ if(!read_var_sized(a, &data_size, NULL))
+ return ARCHIVE_EOF;
+
+ rar->file.bytes_remaining = data_size;
+ } else {
+ rar->file.bytes_remaining = 0;
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "no data found in file/service block");
+ return ARCHIVE_FATAL;
+ }
+
+ if(!read_var_sized(a, &file_flags, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_var(a, &unpacked_size, NULL))
+ return ARCHIVE_EOF;
+
+ if(file_flags & UNKNOWN_UNPACKED_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Files with unknown unpacked size are not supported");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
+
+ if(!read_var_sized(a, &file_attr, NULL))
+ return ARCHIVE_EOF;
+
+ if(file_flags & UTIME) {
+ if(!read_u32(a, &mtime))
+ return ARCHIVE_EOF;
+ }
+
+ if(file_flags & CRC32) {
+ if(!read_u32(a, &crc))
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &compression_info, NULL))
+ return ARCHIVE_EOF;
+
+ c_method = (int) (compression_info >> 7) & 0x7;
+ c_version = (int) (compression_info & 0x3f);
+
+ /* RAR5 seems to limit the dictionary size to 64MB. */
+ window_size = (rar->file.dir > 0) ?
+ 0 :
+ g_unpack_window_size << ((compression_info >> 10) & 15);
+ rar->cstate.method = c_method;
+ rar->cstate.version = c_version + 50;
+ rar->file.solid = (compression_info & SOLID) > 0;
+
+ /* Archives which declare solid files without initializing the window
+ * buffer first are invalid. */
+
+ if(rar->file.solid > 0 && rar->cstate.window_buf == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Declared solid file, but no window buffer "
+ "initialized yet.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Check if window_size is a sane value. Also, if the file is not
+ * declared as a directory, disallow window_size == 0. */
+ if(window_size > (64 * 1024 * 1024) ||
+ (rar->file.dir == 0 && window_size == 0))
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Declared dictionary size is not supported.");
+ return ARCHIVE_FATAL;
+ }
+
+ if(rar->file.solid > 0) {
+ /* Re-check if current window size is the same as previous
+ * window size (for solid files only). */
+ if(rar->file.solid_window_size > 0 &&
+ rar->file.solid_window_size != (ssize_t) window_size)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Window size for this solid file doesn't match "
+ "the window size used in previous solid file. ");
+ return ARCHIVE_FATAL;
+ }
+ }
+
+ if(rar->cstate.window_size < (ssize_t) window_size &&
+ rar->cstate.window_buf)
+ {
+ /* If window_buf has been allocated before, reallocate it, so
+ * that its size will match new window_size. */
+
+ uint8_t* new_window_buf =
+ realloc(rar->cstate.window_buf, window_size);
+
+ if(!new_window_buf) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Not enough memory when trying to realloc the window "
+ "buffer.");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->cstate.window_buf = new_window_buf;
+ }
+
+ /* Values up to 64M should fit into ssize_t on every
+ * architecture. */
+ rar->cstate.window_size = (ssize_t) window_size;
+
+ if(rar->file.solid > 0 && rar->file.solid_window_size == 0) {
+ /* Solid files have to have the same window_size across
+ whole archive. Remember the window_size parameter
+ for first solid file found. */
+ rar->file.solid_window_size = rar->cstate.window_size;
+ }
+
+ init_window_mask(rar);
+
+ rar->file.service = 0;
+
+ if(!read_var_sized(a, &host_os, NULL))
+ return ARCHIVE_EOF;
+
+ if(host_os == HOST_WINDOWS) {
+ /* Host OS is Windows */
+
+ __LA_MODE_T mode;
+
+ if(file_attr & ATTR_DIRECTORY) {
+ if (file_attr & ATTR_READONLY) {
+ mode = 0555 | AE_IFDIR;
+ } else {
+ mode = 0755 | AE_IFDIR;
+ }
+ } else {
+ if (file_attr & ATTR_READONLY) {
+ mode = 0444 | AE_IFREG;
+ } else {
+ mode = 0644 | AE_IFREG;
+ }
+ }
+
+ archive_entry_set_mode(entry, mode);
+
+ if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) {
+ char *fflags_text, *ptr;
+ /* allocate for "rdonly,hidden,system," */
+ fflags_text = malloc(22 * sizeof(char));
+ if (fflags_text != NULL) {
+ ptr = fflags_text;
+ if (file_attr & ATTR_READONLY) {
+ strcpy(ptr, "rdonly,");
+ ptr = ptr + 7;
+ }
+ if (file_attr & ATTR_HIDDEN) {
+ strcpy(ptr, "hidden,");
+ ptr = ptr + 7;
+ }
+ if (file_attr & ATTR_SYSTEM) {
+ strcpy(ptr, "system,");
+ ptr = ptr + 7;
+ }
+ if (ptr > fflags_text) {
+ /* Delete trailing comma */
+ *(ptr - 1) = '\0';
+ archive_entry_copy_fflags_text(entry,
+ fflags_text);
+ }
+ free(fflags_text);
+ }
+ }
+ } else if(host_os == HOST_UNIX) {
+ /* Host OS is Unix */
+ archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);
+ } else {
+ /* Unknown host OS */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported Host OS: 0x%x", (int) host_os);
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(!read_var_sized(a, &name_size, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_ahead(a, name_size, &p))
+ return ARCHIVE_EOF;
+
+ if(name_size > (MAX_NAME_IN_CHARS - 1)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Filename is too long");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(name_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "No filename specified");
+
+ return ARCHIVE_FATAL;
+ }
+
+ memcpy(name_utf8_buf, p, name_size);
+ name_utf8_buf[name_size] = 0;
+ if(ARCHIVE_OK != consume(a, name_size)) {
+ return ARCHIVE_EOF;
+ }
+
+ archive_entry_update_pathname_utf8(entry, name_utf8_buf);
+
+ if(extra_data_size > 0) {
+ int ret = process_head_file_extra(a, entry, rar,
+ extra_data_size);
+
+ /*
+ * TODO: rewrite or remove useless sanity check
+ * as extra_data_size is not passed as a pointer
+ *
+ if(extra_data_size < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "File extra data size is not zero");
+ return ARCHIVE_FATAL;
+ }
+ */
+
+ if(ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
+ rar->file.unpacked_size = (ssize_t) unpacked_size;
+ if(rar->file.redir_type == REDIR_TYPE_NONE)
+ archive_entry_set_size(entry, unpacked_size);
+ }
+
+ if(file_flags & UTIME) {
+ archive_entry_set_mtime(entry, (time_t) mtime, 0);
+ }
+
+ if(file_flags & CRC32) {
+ rar->file.stored_crc32 = crc;
+ }
+
+ if(!rar->cstate.switch_multivolume) {
+ /* Do not reinitialize unpacking state if we're switching
+ * archives. */
+ rar->cstate.block_parsing_finished = 1;
+ rar->cstate.all_filters_applied = 1;
+ rar->cstate.initialized = 0;
+ }
+
+ if(rar->generic.split_before > 0) {
+ /* If now we're standing on a header that has a 'split before'
+ * mark, it means we're standing on a 'continuation' file
+ * header. Signal the caller that if it wants to move to
+ * another file, it must call rar5_read_header() function
+ * again. */
+
+ return ARCHIVE_RETRY;
+ } else {
+ return ARCHIVE_OK;
+ }
+}
+
+static int process_head_service(struct archive_read* a, struct rar5* rar,
+ struct archive_entry* entry, size_t block_flags)
+{
+ /* Process this SERVICE block the same way as FILE blocks. */
+ int ret = process_head_file(a, rar, entry, block_flags);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ rar->file.service = 1;
+
+ /* But skip the data part automatically. It's no use for the user
+ * anyway. It contains only service data, not even needed to
+ * properly unpack the file. */
+ ret = rar5_read_data_skip(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ /* After skipping, try parsing another block automatically. */
+ return ARCHIVE_RETRY;
+}
+
+static int process_head_main(struct archive_read* a, struct rar5* rar,
+ struct archive_entry* entry, size_t block_flags)
+{
+ int ret;
+ size_t extra_data_size = 0;
+ size_t extra_field_size = 0;
+ size_t extra_field_id = 0;
+ size_t archive_flags = 0;
+
+ enum MAIN_FLAGS {
+ VOLUME = 0x0001, /* multi-volume archive */
+ VOLUME_NUMBER = 0x0002, /* volume number, first vol doesn't
+ * have it */
+ SOLID = 0x0004, /* solid archive */
+ PROTECT = 0x0008, /* contains Recovery info */
+ LOCK = 0x0010, /* readonly flag, not used */
+ };
+
+ enum MAIN_EXTRA {
+ // Just one attribute here.
+ LOCATOR = 0x01,
+ };
+
+ (void) entry;
+
+ if(block_flags & HFL_EXTRA_DATA) {
+ if(!read_var_sized(a, &extra_data_size, NULL))
+ return ARCHIVE_EOF;
+ } else {
+ extra_data_size = 0;
+ }
+
+ if(!read_var_sized(a, &archive_flags, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ rar->main.volume = (archive_flags & VOLUME) > 0;
+ rar->main.solid = (archive_flags & SOLID) > 0;
+
+ if(archive_flags & VOLUME_NUMBER) {
+ size_t v = 0;
+ if(!read_var_sized(a, &v, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if (v > UINT_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid volume number");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->main.vol_no = (unsigned int) v;
+ } else {
+ rar->main.vol_no = 0;
+ }
+
+ if(rar->vol.expected_vol_no > 0 &&
+ rar->main.vol_no != rar->vol.expected_vol_no)
+ {
+ /* Returning EOF instead of FATAL because of strange
+ * libarchive behavior. When opening multiple files via
+ * archive_read_open_filenames(), after reading up the whole
+ * last file, the __archive_read_ahead function wraps up to
+ * the first archive instead of returning EOF. */
+ return ARCHIVE_EOF;
+ }
+
+ if(extra_data_size == 0) {
+ /* Early return. */
+ return ARCHIVE_OK;
+ }
+
+ if(!read_var_sized(a, &extra_field_size, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &extra_field_id, NULL)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(extra_field_size == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid extra field size");
+ return ARCHIVE_FATAL;
+ }
+
+ switch(extra_field_id) {
+ case LOCATOR:
+ ret = process_main_locator_extra_block(a, rar);
+ if(ret != ARCHIVE_OK) {
+ /* Error while parsing main locator extra
+ * block. */
+ return ret;
+ }
+
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported extra type (0x%x)",
+ (int) extra_field_id);
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int skip_unprocessed_bytes(struct archive_read* a) {
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ if(rar->file.bytes_remaining) {
+ /* Use different skipping method in block merging mode than in
+ * normal mode. If merge mode is active, rar5_read_data_skip
+ * can't be used, because it could allow recursive use of
+ * merge_block() * function, and this function doesn't support
+ * recursive use. */
+ if(rar->merge_mode) {
+ /* Discard whole merged block. This is valid in solid
+ * mode as well, because the code will discard blocks
+ * only if those blocks are safe to discard (i.e.
+ * they're not FILE blocks). */
+ ret = consume(a, rar->file.bytes_remaining);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ rar->file.bytes_remaining = 0;
+ } else {
+ /* If we're not in merge mode, use safe skipping code.
+ * This will ensure we'll handle solid archives
+ * properly. */
+ ret = rar5_read_data_skip(a);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int scan_for_signature(struct archive_read* a);
+
+/* Base block processing function. A 'base block' is a RARv5 header block
+ * that tells the reader what kind of data is stored inside the block.
+ *
+ * From the birds-eye view a RAR file looks file this:
+ *
+ * <magic><base_block_1><base_block_2>...<base_block_n>
+ *
+ * There are a few types of base blocks. Those types are specified inside
+ * the 'switch' statement in this function. For example purposes, I'll write
+ * how a standard RARv5 file could look like here:
+ *
+ * <magic><MAIN><FILE><FILE><FILE><SERVICE><ENDARC>
+ *
+ * The structure above could describe an archive file with 3 files in it,
+ * one service "QuickOpen" block (that is ignored by this parser), and an
+ * end of file base block marker.
+ *
+ * If the file is stored in multiple archive files ("multiarchive"), it might
+ * look like this:
+ *
+ * .part01.rar: <magic><MAIN><FILE><ENDARC>
+ * .part02.rar: <magic><MAIN><FILE><ENDARC>
+ * .part03.rar: <magic><MAIN><FILE><ENDARC>
+ *
+ * This example could describe 3 RAR files that contain ONE archived file.
+ * Or it could describe 3 RAR files that contain 3 different files. Or 3
+ * RAR files than contain 2 files. It all depends what metadata is stored in
+ * the headers of <FILE> blocks.
+ *
+ * Each <FILE> block contains info about its size, the name of the file it's
+ * storing inside, and whether this FILE block is a continuation block of
+ * previous archive ('split before'), and is this FILE block should be
+ * continued in another archive ('split after'). By parsing the 'split before'
+ * and 'split after' flags, we're able to tell if multiple <FILE> base blocks
+ * are describing one file, or multiple files (with the same filename, for
+ * example).
+ *
+ * One thing to note is that if we're parsing the first <FILE> block, and
+ * we see 'split after' flag, then we need to jump over to another <FILE>
+ * block to be able to decompress rest of the data. To do this, we need
+ * to skip the <ENDARC> block, then switch to another file, then skip the
+ * <magic> block, <MAIN> block, and then we're standing on the proper
+ * <FILE> block.
+ */
+
+static int process_base_block(struct archive_read* a,
+ struct archive_entry* entry)
+{
+ const size_t SMALLEST_RAR5_BLOCK_SIZE = 3;
+
+ struct rar5* rar = get_context(a);
+ uint32_t hdr_crc, computed_crc;
+ size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
+ size_t header_id = 0;
+ size_t header_flags = 0;
+ const uint8_t* p;
+ int ret;
+
+ enum HEADER_TYPE {
+ HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02,
+ HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
+ HEAD_UNKNOWN = 0xff,
+ };
+
+ /* Skip any unprocessed data for this file. */
+ ret = skip_unprocessed_bytes(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ /* Read the expected CRC32 checksum. */
+ if(!read_u32(a, &hdr_crc)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Read header size. */
+ if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ hdr_size = raw_hdr_size + hdr_size_len;
+
+ /* Sanity check, maximum header size for RAR5 is 2MB. */
+ if(hdr_size > (2 * 1024 * 1024)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Base block header is too large");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Additional sanity checks to weed out invalid files. */
+ if(raw_hdr_size == 0 || hdr_size_len == 0 ||
+ hdr_size < SMALLEST_RAR5_BLOCK_SIZE)
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too small block encountered (%zu bytes)",
+ raw_hdr_size);
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Read the whole header data into memory, maximum memory use here is
+ * 2MB. */
+ if(!read_ahead(a, hdr_size, &p)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Verify the CRC32 of the header data. */
+ computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);
+ if(computed_crc != hdr_crc) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header CRC error");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* If the checksum is OK, we proceed with parsing. */
+ if(ARCHIVE_OK != consume(a, hdr_size_len)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_var_sized(a, &header_id, NULL))
+ return ARCHIVE_EOF;
+
+ if(!read_var_sized(a, &header_flags, NULL))
+ return ARCHIVE_EOF;
+
+ rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;
+ rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;
+ rar->generic.size = (int)hdr_size;
+ rar->generic.last_header_id = (int)header_id;
+ rar->main.endarc = 0;
+
+ /* Those are possible header ids in RARv5. */
+ switch(header_id) {
+ case HEAD_MAIN:
+ ret = process_head_main(a, rar, entry, header_flags);
+
+ /* Main header doesn't have any files in it, so it's
+ * pointless to return to the caller. Retry to next
+ * header, which should be HEAD_FILE/HEAD_SERVICE. */
+ if(ret == ARCHIVE_OK)
+ return ARCHIVE_RETRY;
+
+ return ret;
+ case HEAD_SERVICE:
+ ret = process_head_service(a, rar, entry, header_flags);
+ return ret;
+ case HEAD_FILE:
+ ret = process_head_file(a, rar, entry, header_flags);
+ return ret;
+ case HEAD_CRYPT:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encryption is not supported");
+ return ARCHIVE_FATAL;
+ case HEAD_ENDARC:
+ rar->main.endarc = 1;
+
+ /* After encountering an end of file marker, we need
+ * to take into consideration if this archive is
+ * continued in another file (i.e. is it part01.rar:
+ * is there a part02.rar?) */
+ if(rar->main.volume) {
+ /* In case there is part02.rar, position the
+ * read pointer in a proper place, so we can
+ * resume parsing. */
+ ret = scan_for_signature(a);
+ if(ret == ARCHIVE_FATAL) {
+ return ARCHIVE_EOF;
+ } else {
+ if(rar->vol.expected_vol_no ==
+ UINT_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header error");
+ return ARCHIVE_FATAL;
+ }
+
+ rar->vol.expected_vol_no =
+ rar->main.vol_no + 1;
+ return ARCHIVE_OK;
+ }
+ } else {
+ return ARCHIVE_EOF;
+ }
+ case HEAD_MARK:
+ return ARCHIVE_EOF;
+ default:
+ if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Header type error");
+ return ARCHIVE_FATAL;
+ } else {
+ /* If the block is marked as 'skip if unknown',
+ * do as the flag says: skip the block
+ * instead on failing on it. */
+ return ARCHIVE_RETRY;
+ }
+ }
+
+#if !defined WIN32
+ // Not reached.
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Internal unpacker error");
+ return ARCHIVE_FATAL;
+#endif
+}
+
+static int skip_base_block(struct archive_read* a) {
+ int ret;
+ struct rar5* rar = get_context(a);
+
+ /* Create a new local archive_entry structure that will be operated on
+ * by header reader; operations on this archive_entry will be discarded.
+ */
+ struct archive_entry* entry = archive_entry_new();
+ ret = process_base_block(a, entry);
+
+ /* Discard operations on this archive_entry structure. */
+ archive_entry_free(entry);
+ if(ret == ARCHIVE_FATAL)
+ return ret;
+
+ if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)
+ return ARCHIVE_OK;
+
+ if(ret == ARCHIVE_OK)
+ return ARCHIVE_RETRY;
+ else
+ return ret;
+}
+
+static int try_skip_sfx(struct archive_read *a)
+{
+ const char *p;
+
+ if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+ return ARCHIVE_EOF;
+
+ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)
+ {
+ char signature[sizeof(rar5_signature_xor)];
+ const void *h;
+ const char *q;
+ size_t skip, total = 0;
+ ssize_t bytes, window = 4096;
+
+ rar5_signature(signature);
+
+ while (total + window <= (1024 * 512)) {
+ h = __archive_read_ahead(a, window, &bytes);
+ if (h == NULL) {
+ /* Remaining bytes are less than window. */
+ window >>= 1;
+ if (window < 0x40)
+ goto fatal;
+ continue;
+ }
+ if (bytes < 0x40)
+ goto fatal;
+ p = h;
+ q = p + bytes;
+
+ /*
+ * Scan ahead until we find something that looks
+ * like the RAR header.
+ */
+ while (p + 8 < q) {
+ if (memcmp(p, signature, sizeof(signature)) == 0) {
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ return (ARCHIVE_OK);
+ }
+ p += 0x10;
+ }
+ skip = p - (const char *)h;
+ __archive_read_consume(a, skip);
+ total += skip;
+ }
+ }
+
+ return ARCHIVE_OK;
+fatal:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Couldn't find out RAR header");
+ return (ARCHIVE_FATAL);
+}
+
+static int rar5_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ if(rar->header_initialized == 0) {
+ init_header(a);
+ if ((ret = try_skip_sfx(a)) < ARCHIVE_WARN)
+ return ret;
+ rar->header_initialized = 1;
+ }
+
+ if(rar->skipped_magic == 0) {
+ if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {
+ return ARCHIVE_EOF;
+ }
+
+ rar->skipped_magic = 1;
+ }
+
+ do {
+ ret = process_base_block(a, entry);
+ } while(ret == ARCHIVE_RETRY ||
+ (rar->main.endarc > 0 && ret == ARCHIVE_OK));
+
+ return ret;
+}
+
+static void init_unpack(struct rar5* rar) {
+ rar->file.calculated_crc32 = 0;
+ init_window_mask(rar);
+
+ free(rar->cstate.window_buf);
+ free(rar->cstate.filtered_buf);
+
+ if(rar->cstate.window_size > 0) {
+ rar->cstate.window_buf = calloc(1, rar->cstate.window_size);
+ rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size);
+ } else {
+ rar->cstate.window_buf = NULL;
+ rar->cstate.filtered_buf = NULL;
+ }
+
+ rar->cstate.write_ptr = 0;
+ rar->cstate.last_write_ptr = 0;
+
+ memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd));
+ memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld));
+ memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd));
+ memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd));
+ memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd));
+}
+
+static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
+ int verify_crc;
+
+ if(rar->skip_mode) {
+#if defined CHECK_CRC_ON_SOLID_SKIP
+ verify_crc = 1;
+#else
+ verify_crc = 0;
+#endif
+ } else
+ verify_crc = 1;
+
+ if(verify_crc) {
+ /* Don't update CRC32 if the file doesn't have the
+ * `stored_crc32` info filled in. */
+ if(rar->file.stored_crc32 > 0) {
+ rar->file.calculated_crc32 =
+ crc32(rar->file.calculated_crc32, p, (unsigned int)to_read);
+ }
+
+ /* Check if the file uses an optional BLAKE2sp checksum
+ * algorithm. */
+ if(rar->file.has_blake2 > 0) {
+ /* Return value of the `update` function is always 0,
+ * so we can explicitly ignore it here. */
+ (void) blake2sp_update(&rar->file.b2state, p, to_read);
+ }
+ }
+}
+
+static int create_decode_tables(uint8_t* bit_length,
+ struct decode_table* table, int size)
+{
+ int code, upper_limit = 0, i, lc[16];
+ uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];
+ ssize_t cur_len, quick_data_size;
+
+ memset(&lc, 0, sizeof(lc));
+ memset(table->decode_num, 0, sizeof(table->decode_num));
+ table->size = size;
+ table->quick_bits = size == HUFF_NC ? 10 : 7;
+
+ for(i = 0; i < size; i++) {
+ lc[bit_length[i] & 15]++;
+ }
+
+ lc[0] = 0;
+ table->decode_pos[0] = 0;
+ table->decode_len[0] = 0;
+
+ for(i = 1; i < 16; i++) {
+ upper_limit += lc[i];
+
+ table->decode_len[i] = upper_limit << (16 - i);
+ table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];
+
+ upper_limit <<= 1;
+ }
+
+ memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));
+
+ for(i = 0; i < size; i++) {
+ uint8_t clen = bit_length[i] & 15;
+ if(clen > 0) {
+ int last_pos = decode_pos_clone[clen];
+ table->decode_num[last_pos] = i;
+ decode_pos_clone[clen]++;
+ }
+ }
+
+ quick_data_size = (int64_t)1 << table->quick_bits;
+ cur_len = 1;
+ for(code = 0; code < quick_data_size; code++) {
+ int bit_field = code << (16 - table->quick_bits);
+ int dist, pos;
+
+ while(cur_len < rar5_countof(table->decode_len) &&
+ bit_field >= table->decode_len[cur_len]) {
+ cur_len++;
+ }
+
+ table->quick_len[code] = (uint8_t) cur_len;
+
+ dist = bit_field - table->decode_len[cur_len - 1];
+ dist >>= (16 - cur_len);
+
+ pos = table->decode_pos[cur_len & 15] + dist;
+ if(cur_len < rar5_countof(table->decode_pos) && pos < size) {
+ table->quick_num[code] = table->decode_num[pos];
+ } else {
+ table->quick_num[code] = 0;
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int decode_number(struct archive_read* a, struct decode_table* table,
+ const uint8_t* p, uint16_t* num)
+{
+ int i, bits, dist, ret;
+ uint16_t bitfield;
+ uint32_t pos;
+ struct rar5* rar = get_context(a);
+
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) {
+ return ret;
+ }
+
+ bitfield &= 0xfffe;
+
+ if(bitfield < table->decode_len[table->quick_bits]) {
+ int code = bitfield >> (16 - table->quick_bits);
+ skip_bits(rar, table->quick_len[code]);
+ *num = table->quick_num[code];
+ return ARCHIVE_OK;
+ }
+
+ bits = 15;
+
+ for(i = table->quick_bits + 1; i < 15; i++) {
+ if(bitfield < table->decode_len[i]) {
+ bits = i;
+ break;
+ }
+ }
+
+ skip_bits(rar, bits);
+
+ dist = bitfield - table->decode_len[bits - 1];
+ dist >>= (16 - bits);
+ pos = table->decode_pos[bits] + dist;
+
+ if(pos >= table->size)
+ pos = 0;
+
+ *num = table->decode_num[pos];
+ return ARCHIVE_OK;
+}
+
+/* Reads and parses Huffman tables from the beginning of the block. */
+static int parse_tables(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p)
+{
+ int ret, value, i, w, idx = 0;
+ uint8_t bit_length[HUFF_BC],
+ table[HUFF_TABLE_SIZE],
+ nibble_mask = 0xF0,
+ nibble_shift = 4;
+
+ enum { ESCAPE = 15 };
+
+ /* The data for table generation is compressed using a simple RLE-like
+ * algorithm when storing zeroes, so we need to unpack it first. */
+ for(w = 0, i = 0; w < HUFF_BC;) {
+ if(i >= rar->cstate.cur_block_size) {
+ /* Truncated data, can't continue. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated data in huffman tables");
+ return ARCHIVE_FATAL;
+ }
+
+ value = (p[i] & nibble_mask) >> nibble_shift;
+
+ if(nibble_mask == 0x0F)
+ ++i;
+
+ nibble_mask ^= 0xFF;
+ nibble_shift ^= 4;
+
+ /* Values smaller than 15 is data, so we write it directly.
+ * Value 15 is a flag telling us that we need to unpack more
+ * bytes. */
+ if(value == ESCAPE) {
+ value = (p[i] & nibble_mask) >> nibble_shift;
+ if(nibble_mask == 0x0F)
+ ++i;
+ nibble_mask ^= 0xFF;
+ nibble_shift ^= 4;
+
+ if(value == 0) {
+ /* We sometimes need to write the actual value
+ * of 15, so this case handles that. */
+ bit_length[w++] = ESCAPE;
+ } else {
+ int k;
+
+ /* Fill zeroes. */
+ for(k = 0; (k < value + 2) && (w < HUFF_BC);
+ k++) {
+ bit_length[w++] = 0;
+ }
+ }
+ } else {
+ bit_length[w++] = value;
+ }
+ }
+
+ rar->bits.in_addr = i;
+ rar->bits.bit_addr = nibble_shift ^ 4;
+
+ ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Decoding huffman tables failed");
+ return ARCHIVE_FATAL;
+ }
+
+ for(i = 0; i < HUFF_TABLE_SIZE;) {
+ uint16_t num;
+
+ ret = decode_number(a, &rar->cstate.bd, p, &num);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Decoding huffman tables failed");
+ return ARCHIVE_FATAL;
+ }
+
+ if(num < 16) {
+ /* 0..15: store directly */
+ table[i] = (uint8_t) num;
+ i++;
+ } else if(num < 18) {
+ /* 16..17: repeat previous code */
+ uint16_t n;
+
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+ return ret;
+
+ if(num == 16) {
+ n >>= 13;
+ n += 3;
+ skip_bits(rar, 3);
+ } else {
+ n >>= 9;
+ n += 11;
+ skip_bits(rar, 7);
+ }
+
+ if(i > 0) {
+ while(n-- > 0 && i < HUFF_TABLE_SIZE) {
+ table[i] = table[i - 1];
+ i++;
+ }
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unexpected error when decoding "
+ "huffman tables");
+ return ARCHIVE_FATAL;
+ }
+ } else {
+ /* other codes: fill with zeroes `n` times */
+ uint16_t n;
+
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+ return ret;
+
+ if(num == 18) {
+ n >>= 13;
+ n += 3;
+ skip_bits(rar, 3);
+ } else {
+ n >>= 9;
+ n += 11;
+ skip_bits(rar, 7);
+ }
+
+ while(n-- > 0 && i < HUFF_TABLE_SIZE)
+ table[i++] = 0;
+ }
+ }
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create literal table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_NC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create distance table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_DC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create lower bits of distances table");
+ return ARCHIVE_FATAL;
+ }
+
+ idx += HUFF_LDC;
+
+ ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC);
+ if(ret != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Failed to create repeating distances table");
+ return ARCHIVE_FATAL;
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Parses the block header, verifies its CRC byte, and saves the header
+ * fields inside the `hdr` pointer. */
+static int parse_block_header(struct archive_read* a, const uint8_t* p,
+ ssize_t* block_size, struct compressed_block_header* hdr)
+{
+ uint8_t calculated_cksum;
+ memcpy(hdr, p, sizeof(struct compressed_block_header));
+
+ if(bf_byte_count(hdr) > 2) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported block header size (was %d, max is 2)",
+ bf_byte_count(hdr));
+ return ARCHIVE_FATAL;
+ }
+
+ /* This should probably use bit reader interface in order to be more
+ * future-proof. */
+ *block_size = 0;
+ switch(bf_byte_count(hdr)) {
+ /* 1-byte block size */
+ case 0:
+ *block_size = *(const uint8_t*) &p[2];
+ break;
+
+ /* 2-byte block size */
+ case 1:
+ *block_size = archive_le16dec(&p[2]);
+ break;
+
+ /* 3-byte block size */
+ case 2:
+ *block_size = archive_le32dec(&p[2]);
+ *block_size &= 0x00FFFFFF;
+ break;
+
+ /* Other block sizes are not supported. This case is not
+ * reached, because we have an 'if' guard before the switch
+ * that makes sure of it. */
+ default:
+ return ARCHIVE_FATAL;
+ }
+
+ /* Verify the block header checksum. 0x5A is a magic value and is
+ * always * constant. */
+ calculated_cksum = 0x5A
+ ^ (uint8_t) hdr->block_flags_u8
+ ^ (uint8_t) *block_size
+ ^ (uint8_t) (*block_size >> 8)
+ ^ (uint8_t) (*block_size >> 16);
+
+ if(calculated_cksum != hdr->block_cksum) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Block checksum error: got 0x%x, expected 0x%x",
+ hdr->block_cksum, calculated_cksum);
+
+ return ARCHIVE_FATAL;
+#endif
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Convenience function used during filter processing. */
+static int parse_filter_data(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, uint32_t* filter_data)
+{
+ int i, bytes, ret;
+ uint32_t data = 0;
+
+ if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes)))
+ return ret;
+
+ bytes++;
+
+ for(i = 0; i < bytes; i++) {
+ uint16_t byte;
+
+ if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) {
+ return ret;
+ }
+
+ /* Cast to uint32_t will ensure the shift operation will not
+ * produce undefined result. */
+ data += ((uint32_t) byte >> 8) << (i * 8);
+ skip_bits(rar, 8);
+ }
+
+ *filter_data = data;
+ return ARCHIVE_OK;
+}
+
+/* Function is used during sanity checking. */
+static int is_valid_filter_block_start(struct rar5* rar,
+ uint32_t start)
+{
+ const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr;
+ const int64_t last_bs = rar->cstate.last_block_start;
+ const ssize_t last_bl = rar->cstate.last_block_length;
+
+ if(last_bs == 0 || last_bl == 0) {
+ /* We didn't have any filters yet, so accept this offset. */
+ return 1;
+ }
+
+ if(block_start >= last_bs + last_bl) {
+ /* Current offset is bigger than last block's end offset, so
+ * accept current offset. */
+ return 1;
+ }
+
+ /* Any other case is not a normal situation and we should fail. */
+ return 0;
+}
+
+/* The function will create a new filter, read its parameters from the input
+ * stream and add it to the filter collection. */
+static int parse_filter(struct archive_read* ar, const uint8_t* p) {
+ uint32_t block_start, block_length;
+ uint16_t filter_type;
+ struct filter_info* filt = NULL;
+ struct rar5* rar = get_context(ar);
+ int ret;
+
+ /* Read the parameters from the input stream. */
+ if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start)))
+ return ret;
+
+ if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length)))
+ return ret;
+
+ if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type)))
+ return ret;
+
+ filter_type >>= 13;
+ skip_bits(rar, 3);
+
+ /* Perform some sanity checks on this filter parameters. Note that we
+ * allow only DELTA, E8/E9 and ARM filters here, because rest of
+ * filters are not used in RARv5. */
+
+ if(block_length < 4 ||
+ block_length > 0x400000 ||
+ filter_type > FILTER_ARM ||
+ !is_valid_filter_block_start(rar, block_start))
+ {
+ archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid filter encountered");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Allocate a new filter. */
+ filt = add_new_filter(rar);
+ if(filt == NULL) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate memory for a filter descriptor.");
+ return ARCHIVE_FATAL;
+ }
+
+ filt->type = filter_type;
+ filt->block_start = rar->cstate.write_ptr + block_start;
+ filt->block_length = block_length;
+
+ rar->cstate.last_block_start = filt->block_start;
+ rar->cstate.last_block_length = filt->block_length;
+
+ /* Read some more data in case this is a DELTA filter. Other filter
+ * types don't require any additional data over what was already
+ * read. */
+ if(filter_type == FILTER_DELTA) {
+ int channels;
+
+ if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar, p, 5, &channels)))
+ return ret;
+
+ filt->channels = channels + 1;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int decode_code_length(struct archive_read* a, struct rar5* rar,
+ const uint8_t* p, uint16_t code)
+{
+ int lbits, length = 2;
+
+ if(code < 8) {
+ lbits = 0;
+ length += code;
+ } else {
+ lbits = code / 4 - 1;
+ length += (4 | (code & 3)) << lbits;
+ }
+
+ if(lbits > 0) {
+ int add;
+
+ if(ARCHIVE_OK != read_consume_bits(a, rar, p, lbits, &add))
+ return -1;
+
+ length += add;
+ }
+
+ return length;
+}
+
+static int copy_string(struct archive_read* a, int len, int dist) {
+ struct rar5* rar = get_context(a);
+ const uint64_t cmask = rar->cstate.window_mask;
+ const uint64_t write_ptr = rar->cstate.write_ptr +
+ rar->cstate.solid_offset;
+ int i;
+
+ if (rar->cstate.window_buf == NULL)
+ return ARCHIVE_FATAL;
+
+ /* The unpacker spends most of the time in this function. It would be
+ * a good idea to introduce some optimizations here.
+ *
+ * Just remember that this loop treats buffers that overlap differently
+ * than buffers that do not overlap. This is why a simple memcpy(3)
+ * call will not be enough. */
+
+ for(i = 0; i < len; i++) {
+ const ssize_t write_idx = (write_ptr + i) & cmask;
+ const ssize_t read_idx = (write_ptr + i - dist) & cmask;
+ rar->cstate.window_buf[write_idx] =
+ rar->cstate.window_buf[read_idx];
+ }
+
+ rar->cstate.write_ptr += len;
+ return ARCHIVE_OK;
+}
+
+static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
+ struct rar5* rar = get_context(a);
+ uint16_t num;
+ int ret;
+
+ const uint64_t cmask = rar->cstate.window_mask;
+ const struct compressed_block_header* hdr = &rar->last_block_hdr;
+ const uint8_t bit_size = 1 + bf_bit_size(hdr);
+
+ while(1) {
+ if(rar->cstate.write_ptr - rar->cstate.last_write_ptr >
+ (rar->cstate.window_size >> 1)) {
+ /* Don't allow growing data by more than half of the
+ * window size at a time. In such case, break the loop;
+ * next call to this function will continue processing
+ * from this moment. */
+ break;
+ }
+
+ if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 ||
+ (rar->bits.in_addr == rar->cstate.cur_block_size - 1 &&
+ rar->bits.bit_addr >= bit_size))
+ {
+ /* If the program counter is here, it means the
+ * function has finished processing the block. */
+ rar->cstate.block_parsing_finished = 1;
+ break;
+ }
+
+ /* Decode the next literal. */
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {
+ return ARCHIVE_EOF;
+ }
+
+ /* Num holds a decompression literal, or 'command code'.
+ *
+ * - Values lower than 256 are just bytes. Those codes
+ * can be stored in the output buffer directly.
+ *
+ * - Code 256 defines a new filter, which is later used to
+ * ransform the data block accordingly to the filter type.
+ * The data block needs to be fully uncompressed first.
+ *
+ * - Code bigger than 257 and smaller than 262 define
+ * a repetition pattern that should be copied from
+ * an already uncompressed chunk of data.
+ */
+
+ if(num < 256) {
+ /* Directly store the byte. */
+ int64_t write_idx = rar->cstate.solid_offset +
+ rar->cstate.write_ptr++;
+
+ rar->cstate.window_buf[write_idx & cmask] =
+ (uint8_t) num;
+ continue;
+ } else if(num >= 262) {
+ uint16_t dist_slot;
+ int len = decode_code_length(a, rar, p, num - 262),
+ dbits,
+ dist = 1;
+
+ if(len == -1) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the code length");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p,
+ &dist_slot))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the distance slot");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(dist_slot < 4) {
+ dbits = 0;
+ dist += dist_slot;
+ } else {
+ dbits = dist_slot / 2 - 1;
+
+ /* Cast to uint32_t will make sure the shift
+ * left operation won't produce undefined
+ * result. Then, the uint32_t type will
+ * be implicitly casted to int. */
+ dist += (uint32_t) (2 |
+ (dist_slot & 1)) << dbits;
+ }
+
+ if(dbits > 0) {
+ if(dbits >= 4) {
+ uint32_t add = 0;
+ uint16_t low_dist;
+
+ if(dbits > 4) {
+ if(ARCHIVE_OK != (ret = read_bits_32(
+ a, rar, p, &add))) {
+ /* Return EOF if we
+ * can't read more
+ * data. */
+ return ret;
+ }
+
+ skip_bits(rar, dbits - 4);
+ add = (add >> (
+ 36 - dbits)) << 4;
+ dist += add;
+ }
+
+ if(ARCHIVE_OK != decode_number(a,
+ &rar->cstate.ldd, p, &low_dist))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to decode the "
+ "distance slot");
+
+ return ARCHIVE_FATAL;
+ }
+
+ if(dist >= INT_MAX - low_dist - 1) {
+ /* This only happens in
+ * invalid archives. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Distance pointer "
+ "overflow");
+ return ARCHIVE_FATAL;
+ }
+
+ dist += low_dist;
+ } else {
+ /* dbits is one of [0,1,2,3] */
+ int add;
+
+ if(ARCHIVE_OK != (ret = read_consume_bits(a, rar,
+ p, dbits, &add))) {
+ /* Return EOF if we can't read
+ * more data. */
+ return ret;
+ }
+
+ dist += add;
+ }
+ }
+
+ if(dist > 0x100) {
+ len++;
+
+ if(dist > 0x2000) {
+ len++;
+
+ if(dist > 0x40000) {
+ len++;
+ }
+ }
+ }
+
+ dist_cache_push(rar, dist);
+ rar->cstate.last_len = len;
+
+ if(ARCHIVE_OK != copy_string(a, len, dist))
+ return ARCHIVE_FATAL;
+
+ continue;
+ } else if(num == 256) {
+ /* Create a filter. */
+ ret = parse_filter(a, p);
+ if(ret != ARCHIVE_OK)
+ return ret;
+
+ continue;
+ } else if(num == 257) {
+ if(rar->cstate.last_len != 0) {
+ if(ARCHIVE_OK != copy_string(a,
+ rar->cstate.last_len,
+ rar->cstate.dist_cache[0]))
+ {
+ return ARCHIVE_FATAL;
+ }
+ }
+
+ continue;
+ } else {
+ /* num < 262 */
+ const int idx = num - 258;
+ const int dist = dist_cache_touch(rar, idx);
+
+ uint16_t len_slot;
+ int len;
+
+ if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p,
+ &len_slot)) {
+ return ARCHIVE_FATAL;
+ }
+
+ len = decode_code_length(a, rar, p, len_slot);
+ if (len == -1) {
+ return ARCHIVE_FATAL;
+ }
+
+ rar->cstate.last_len = len;
+
+ if(ARCHIVE_OK != copy_string(a, len, dist))
+ return ARCHIVE_FATAL;
+
+ continue;
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Binary search for the RARv5 signature. */
+static int scan_for_signature(struct archive_read* a) {
+ const uint8_t* p;
+ const int chunk_size = 512;
+ ssize_t i;
+ char signature[sizeof(rar5_signature_xor)];
+
+ /* If we're here, it means we're on an 'unknown territory' data.
+ * There's no indication what kind of data we're reading here.
+ * It could be some text comment, any kind of binary data,
+ * digital sign, dragons, etc.
+ *
+ * We want to find a valid RARv5 magic header inside this unknown
+ * data. */
+
+ /* Is it possible in libarchive to just skip everything until the
+ * end of the file? If so, it would be a better approach than the
+ * current implementation of this function. */
+
+ rar5_signature(signature);
+
+ while(1) {
+ if(!read_ahead(a, chunk_size, &p))
+ return ARCHIVE_EOF;
+
+ for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor);
+ i++) {
+ if(memcmp(&p[i], signature,
+ sizeof(rar5_signature_xor)) == 0) {
+ /* Consume the number of bytes we've used to
+ * search for the signature, as well as the
+ * number of bytes used by the signature
+ * itself. After this we should be standing
+ * on a valid base block header. */
+ (void) consume(a,
+ i + sizeof(rar5_signature_xor));
+ return ARCHIVE_OK;
+ }
+ }
+
+ consume(a, chunk_size);
+ }
+
+ return ARCHIVE_FATAL;
+}
+
+/* This function will switch the multivolume archive file to another file,
+ * i.e. from part03 to part 04. */
+static int advance_multivolume(struct archive_read* a) {
+ int lret;
+ struct rar5* rar = get_context(a);
+
+ /* A small state machine that will skip unnecessary data, needed to
+ * switch from one multivolume to another. Such skipping is needed if
+ * we want to be an stream-oriented (instead of file-oriented)
+ * unpacker.
+ *
+ * The state machine starts with `rar->main.endarc` == 0. It also
+ * assumes that current stream pointer points to some base block
+ * header.
+ *
+ * The `endarc` field is being set when the base block parsing
+ * function encounters the 'end of archive' marker.
+ */
+
+ while(1) {
+ if(rar->main.endarc == 1) {
+ int looping = 1;
+
+ rar->main.endarc = 0;
+
+ while(looping) {
+ lret = skip_base_block(a);
+ switch(lret) {
+ case ARCHIVE_RETRY:
+ /* Continue looping. */
+ break;
+ case ARCHIVE_OK:
+ /* Break loop. */
+ looping = 0;
+ break;
+ default:
+ /* Forward any errors to the
+ * caller. */
+ return lret;
+ }
+ }
+
+ break;
+ } else {
+ /* Skip current base block. In order to properly skip
+ * it, we really need to simply parse it and discard
+ * the results. */
+
+ lret = skip_base_block(a);
+ if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED)
+ return lret;
+
+ /* The `skip_base_block` function tells us if we
+ * should continue with skipping, or we should stop
+ * skipping. We're trying to skip everything up to
+ * a base FILE block. */
+
+ if(lret != ARCHIVE_RETRY) {
+ /* If there was an error during skipping, or we
+ * have just skipped a FILE base block... */
+
+ if(rar->main.endarc == 0) {
+ return lret;
+ } else {
+ continue;
+ }
+ }
+ }
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Merges the partial block from the first multivolume archive file, and
+ * partial block from the second multivolume archive file. The result is
+ * a chunk of memory containing the whole block, and the stream pointer
+ * is advanced to the next block in the second multivolume archive file. */
+static int merge_block(struct archive_read* a, ssize_t block_size,
+ const uint8_t** p)
+{
+ struct rar5* rar = get_context(a);
+ ssize_t cur_block_size, partial_offset = 0;
+ const uint8_t* lp;
+ int ret;
+
+ if(rar->merge_mode) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Recursive merge is not allowed");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Set a flag that we're in the switching mode. */
+ rar->cstate.switch_multivolume = 1;
+
+ /* Reallocate the memory which will hold the whole block. */
+ if(rar->vol.push_buf)
+ free((void*) rar->vol.push_buf);
+
+ /* Increasing the allocation block by 8 is due to bit reading functions,
+ * which are using additional 2 or 4 bytes. Allocating the block size
+ * by exact value would make bit reader perform reads from invalid
+ * memory block when reading the last byte from the buffer. */
+ rar->vol.push_buf = malloc(block_size + 8);
+ if(!rar->vol.push_buf) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a merge block buffer.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Valgrind complains if the extension block for bit reader is not
+ * initialized, so initialize it. */
+ memset(&rar->vol.push_buf[block_size], 0, 8);
+
+ /* A single block can span across multiple multivolume archive files,
+ * so we use a loop here. This loop will consume enough multivolume
+ * archive files until the whole block is read. */
+
+ while(1) {
+ /* Get the size of current block chunk in this multivolume
+ * archive file and read it. */
+ cur_block_size = rar5_min(rar->file.bytes_remaining,
+ block_size - partial_offset);
+
+ if(cur_block_size == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encountered block size == 0 during block merge");
+ return ARCHIVE_FATAL;
+ }
+
+ if(!read_ahead(a, cur_block_size, &lp))
+ return ARCHIVE_EOF;
+
+ /* Sanity check; there should never be a situation where this
+ * function reads more data than the block's size. */
+ if(partial_offset + cur_block_size > block_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Consumed too much data when merging blocks.");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Merge previous block chunk with current block chunk,
+ * or create first block chunk if this is our first
+ * iteration. */
+ memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size);
+
+ /* Advance the stream read pointer by this block chunk size. */
+ if(ARCHIVE_OK != consume(a, cur_block_size))
+ return ARCHIVE_EOF;
+
+ /* Update the pointers. `partial_offset` contains information
+ * about the sum of merged block chunks. */
+ partial_offset += cur_block_size;
+ rar->file.bytes_remaining -= cur_block_size;
+
+ /* If `partial_offset` is the same as `block_size`, this means
+ * we've merged all block chunks and we have a valid full
+ * block. */
+ if(partial_offset == block_size) {
+ break;
+ }
+
+ /* If we don't have any bytes to read, this means we should
+ * switch to another multivolume archive file. */
+ if(rar->file.bytes_remaining == 0) {
+ rar->merge_mode++;
+ ret = advance_multivolume(a);
+ rar->merge_mode--;
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+ }
+ }
+
+ *p = rar->vol.push_buf;
+
+ /* If we're here, we can resume unpacking by processing the block
+ * pointed to by the `*p` memory pointer. */
+
+ return ARCHIVE_OK;
+}
+
+static int process_block(struct archive_read* a) {
+ const uint8_t* p;
+ struct rar5* rar = get_context(a);
+ int ret;
+
+ /* If we don't have any data to be processed, this most probably means
+ * we need to switch to the next volume. */
+ if(rar->main.volume && rar->file.bytes_remaining == 0) {
+ ret = advance_multivolume(a);
+ if(ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ if(rar->cstate.block_parsing_finished) {
+ ssize_t block_size;
+ ssize_t to_skip;
+ ssize_t cur_block_size;
+
+ /* The header size won't be bigger than 6 bytes. */
+ if(!read_ahead(a, 6, &p)) {
+ /* Failed to prefetch data block header. */
+ return ARCHIVE_EOF;
+ }
+
+ /*
+ * Read block_size by parsing block header. Validate the header
+ * by calculating CRC byte stored inside the header. Size of
+ * the header is not constant (block size can be stored either
+ * in 1 or 2 bytes), that's why block size is left out from the
+ * `compressed_block_header` structure and returned by
+ * `parse_block_header` as the second argument. */
+
+ ret = parse_block_header(a, p, &block_size,
+ &rar->last_block_hdr);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ /* Skip block header. Next data is huffman tables,
+ * if present. */
+ to_skip = sizeof(struct compressed_block_header) +
+ bf_byte_count(&rar->last_block_hdr) + 1;
+
+ if(ARCHIVE_OK != consume(a, to_skip))
+ return ARCHIVE_EOF;
+
+ rar->file.bytes_remaining -= to_skip;
+
+ /* The block size gives information about the whole block size,
+ * but the block could be stored in split form when using
+ * multi-volume archives. In this case, the block size will be
+ * bigger than the actual data stored in this file. Remaining
+ * part of the data will be in another file. */
+
+ cur_block_size =
+ rar5_min(rar->file.bytes_remaining, block_size);
+
+ if(block_size > rar->file.bytes_remaining) {
+ /* If current blocks' size is bigger than our data
+ * size, this means we have a multivolume archive.
+ * In this case, skip all base headers until the end
+ * of the file, proceed to next "partXXX.rar" volume,
+ * find its signature, skip all headers up to the first
+ * FILE base header, and continue from there.
+ *
+ * Note that `merge_block` will update the `rar`
+ * context structure quite extensively. */
+
+ ret = merge_block(a, block_size, &p);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ cur_block_size = block_size;
+
+ /* Current stream pointer should be now directly
+ * *after* the block that spanned through multiple
+ * archive files. `p` pointer should have the data of
+ * the *whole* block (merged from partial blocks
+ * stored in multiple archives files). */
+ } else {
+ rar->cstate.switch_multivolume = 0;
+
+ /* Read the whole block size into memory. This can take
+ * up to 8 megabytes of memory in theoretical cases.
+ * Might be worth to optimize this and use a standard
+ * chunk of 4kb's. */
+ if(!read_ahead(a, 4 + cur_block_size, &p)) {
+ /* Failed to prefetch block data. */
+ return ARCHIVE_EOF;
+ }
+ }
+
+ rar->cstate.block_buf = p;
+ rar->cstate.cur_block_size = cur_block_size;
+ rar->cstate.block_parsing_finished = 0;
+
+ rar->bits.in_addr = 0;
+ rar->bits.bit_addr = 0;
+
+ if(bf_is_table_present(&rar->last_block_hdr)) {
+ /* Load Huffman tables. */
+ ret = parse_tables(a, rar, p);
+ if(ret != ARCHIVE_OK) {
+ /* Error during decompression of Huffman
+ * tables. */
+ return ret;
+ }
+ }
+ } else {
+ /* Block parsing not finished, reuse previous memory buffer. */
+ p = rar->cstate.block_buf;
+ }
+
+ /* Uncompress the block, or a part of it, depending on how many bytes
+ * will be generated by uncompressing the block.
+ *
+ * In case too many bytes will be generated, calling this function
+ * again will resume the uncompression operation. */
+ ret = do_uncompress_block(a, p);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->cstate.block_parsing_finished &&
+ rar->cstate.switch_multivolume == 0 &&
+ rar->cstate.cur_block_size > 0)
+ {
+ /* If we're processing a normal block, consume the whole
+ * block. We can do this because we've already read the whole
+ * block to memory. */
+ if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size))
+ return ARCHIVE_FATAL;
+
+ rar->file.bytes_remaining -= rar->cstate.cur_block_size;
+ } else if(rar->cstate.switch_multivolume) {
+ /* Don't consume the block if we're doing multivolume
+ * processing. The volume switching function will consume
+ * the proper count of bytes instead. */
+ rar->cstate.switch_multivolume = 0;
+ }
+
+ return ARCHIVE_OK;
+}
+
+/* Pops the `buf`, `size` and `offset` from the "data ready" stack.
+ *
+ * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY
+ * when there is no data on the stack. */
+static int use_data(struct rar5* rar, const void** buf, size_t* size,
+ int64_t* offset)
+{
+ int i;
+
+ for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
+ struct data_ready *d = &rar->cstate.dready[i];
+
+ if(d->used) {
+ if(buf) *buf = d->buf;
+ if(size) *size = d->size;
+ if(offset) *offset = d->offset;
+
+ d->used = 0;
+ return ARCHIVE_OK;
+ }
+ }
+
+ return ARCHIVE_RETRY;
+}
+
+/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready
+ * FIFO stack. Those values will be popped from this stack by the `use_data`
+ * function. */
+static int push_data_ready(struct archive_read* a, struct rar5* rar,
+ const uint8_t* buf, size_t size, int64_t offset)
+{
+ int i;
+
+ /* Don't push if we're in skip mode. This is needed because solid
+ * streams need full processing even if we're skipping data. After
+ * fully processing the stream, we need to discard the generated bytes,
+ * because we're interested only in the side effect: building up the
+ * internal window circular buffer. This window buffer will be used
+ * later during unpacking of requested data. */
+ if(rar->skip_mode)
+ return ARCHIVE_OK;
+
+ /* Sanity check. */
+ if(offset != rar->file.last_offset + rar->file.last_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Sanity check error: output stream is not continuous");
+ return ARCHIVE_FATAL;
+ }
+
+ for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
+ struct data_ready* d = &rar->cstate.dready[i];
+ if(!d->used) {
+ d->used = 1;
+ d->buf = buf;
+ d->size = size;
+ d->offset = offset;
+
+ /* These fields are used only in sanity checking. */
+ rar->file.last_offset = offset;
+ rar->file.last_size = size;
+
+ /* Calculate the checksum of this new block before
+ * submitting data to libarchive's engine. */
+ update_crc(rar, d->buf, d->size);
+
+ return ARCHIVE_OK;
+ }
+ }
+
+ /* Program counter will reach this code if the `rar->cstate.data_ready`
+ * stack will be filled up so that no new entries will be allowed. The
+ * code shouldn't allow such situation to occur. So we treat this case
+ * as an internal error. */
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Error: premature end of data_ready stack");
+ return ARCHIVE_FATAL;
+}
+
+/* This function uncompresses the data that is stored in the <FILE> base
+ * block.
+ *
+ * The FILE base block looks like this:
+ *
+ * <header><huffman tables><block_1><block_2>...<block_n>
+ *
+ * The <header> is a block header, that is parsed in parse_block_header().
+ * It's a "compressed_block_header" structure, containing metadata needed
+ * to know when we should stop looking for more <block_n> blocks.
+ *
+ * <huffman tables> contain data needed to set up the huffman tables, needed
+ * for the actual decompression.
+ *
+ * Each <block_n> consists of series of literals:
+ *
+ * <literal><literal><literal>...<literal>
+ *
+ * Those literals generate the uncompression data. They operate on a circular
+ * buffer, sometimes writing raw data into it, sometimes referencing
+ * some previous data inside this buffer, and sometimes declaring a filter
+ * that will need to be executed on the data stored in the circular buffer.
+ * It all depends on the literal that is used.
+ *
+ * Sometimes blocks produce output data, sometimes they don't. For example, for
+ * some huge files that use lots of filters, sometimes a block is filled with
+ * only filter declaration literals. Such blocks won't produce any data in the
+ * circular buffer.
+ *
+ * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte,
+ * because a literal can reference previously decompressed data. For example,
+ * there can be a literal that says: 'append a byte 0xFE here', and after
+ * it another literal can say 'append 1 megabyte of data from circular buffer
+ * offset 0x12345'. This is how RAR format handles compressing repeated
+ * patterns.
+ *
+ * The RAR compressor creates those literals and the actual efficiency of
+ * compression depends on what those literals are. The literals can also
+ * be seen as a kind of a non-turing-complete virtual machine that simply
+ * tells the decompressor what it should do.
+ * */
+
+static int do_uncompress_file(struct archive_read* a) {
+ struct rar5* rar = get_context(a);
+ int ret;
+ int64_t max_end_pos;
+
+ if(!rar->cstate.initialized) {
+ /* Don't perform full context reinitialization if we're
+ * processing a solid archive. */
+ if(!rar->main.solid || !rar->cstate.window_buf) {
+ init_unpack(rar);
+ }
+
+ rar->cstate.initialized = 1;
+ }
+
+ /* Don't allow extraction if window_size is invalid. */
+ if(rar->cstate.window_size == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid window size declaration in this file");
+
+ /* This should never happen in valid files. */
+ return ARCHIVE_FATAL;
+ }
+
+ if(rar->cstate.all_filters_applied == 1) {
+ /* We use while(1) here, but standard case allows for just 1
+ * iteration. The loop will iterate if process_block() didn't
+ * generate any data at all. This can happen if the block
+ * contains only filter definitions (this is common in big
+ * files). */
+ while(1) {
+ ret = process_block(a);
+ if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL)
+ return ret;
+
+ if(rar->cstate.last_write_ptr ==
+ rar->cstate.write_ptr) {
+ /* The block didn't generate any new data,
+ * so just process a new block. */
+ continue;
+ }
+
+ /* The block has generated some new data, so break
+ * the loop. */
+ break;
+ }
+ }
+
+ /* Try to run filters. If filters won't be applied, it means that
+ * insufficient data was generated. */
+ ret = apply_filters(a);
+ if(ret == ARCHIVE_RETRY) {
+ return ARCHIVE_OK;
+ } else if(ret == ARCHIVE_FATAL) {
+ return ARCHIVE_FATAL;
+ }
+
+ /* If apply_filters() will return ARCHIVE_OK, we can continue here. */
+
+ if(cdeque_size(&rar->cstate.filters) > 0) {
+ /* Check if we can write something before hitting first
+ * filter. */
+ struct filter_info* flt;
+
+ /* Get the block_start offset from the first filter. */
+ if(CDE_OK != cdeque_front(&rar->cstate.filters,
+ cdeque_filter_p(&flt)))
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Can't read first filter");
+ return ARCHIVE_FATAL;
+ }
+
+ max_end_pos = rar5_min(flt->block_start,
+ rar->cstate.write_ptr);
+ } else {
+ /* There are no filters defined, or all filters were applied.
+ * This means we can just store the data without any
+ * postprocessing. */
+ max_end_pos = rar->cstate.write_ptr;
+ }
+
+ if(max_end_pos == rar->cstate.last_write_ptr) {
+ /* We can't write anything yet. The block uncompression
+ * function did not generate enough data, and no filter can be
+ * applied. At the same time we don't have any data that can be
+ * stored without filter postprocessing. This means we need to
+ * wait for more data to be generated, so we can apply the
+ * filters.
+ *
+ * Signal the caller that we need more data to be able to do
+ * anything.
+ */
+ return ARCHIVE_RETRY;
+ } else {
+ /* We can write the data before hitting the first filter.
+ * So let's do it. The push_window_data() function will
+ * effectively return the selected data block to the user
+ * application. */
+ push_window_data(a, rar, rar->cstate.last_write_ptr,
+ max_end_pos);
+ rar->cstate.last_write_ptr = max_end_pos;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int uncompress_file(struct archive_read* a) {
+ int ret;
+
+ while(1) {
+ /* Sometimes the uncompression function will return a
+ * 'retry' signal. If this will happen, we have to retry
+ * the function. */
+ ret = do_uncompress_file(a);
+ if(ret != ARCHIVE_RETRY)
+ return ret;
+ }
+}
+
+
+static int do_unstore_file(struct archive_read* a,
+ struct rar5* rar, const void** buf, size_t* size, int64_t* offset)
+{
+ size_t to_read;
+ const uint8_t* p;
+
+ if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&
+ rar->generic.split_after > 0)
+ {
+ int ret;
+
+ rar->cstate.switch_multivolume = 1;
+ ret = advance_multivolume(a);
+ rar->cstate.switch_multivolume = 0;
+
+ if(ret != ARCHIVE_OK) {
+ /* Failed to advance to next multivolume archive
+ * file. */
+ return ret;
+ }
+ }
+
+ to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
+ if(to_read == 0) {
+ return ARCHIVE_EOF;
+ }
+
+ if(!read_ahead(a, to_read, &p)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "I/O error when unstoring file");
+ return ARCHIVE_FATAL;
+ }
+
+ if(ARCHIVE_OK != consume(a, to_read)) {
+ return ARCHIVE_EOF;
+ }
+
+ if(buf) *buf = p;
+ if(size) *size = to_read;
+ if(offset) *offset = rar->cstate.last_unstore_ptr;
+
+ rar->file.bytes_remaining -= to_read;
+ rar->cstate.last_unstore_ptr += to_read;
+
+ update_crc(rar, p, to_read);
+ return ARCHIVE_OK;
+}
+
+static int do_unpack(struct archive_read* a, struct rar5* rar,
+ const void** buf, size_t* size, int64_t* offset)
+{
+ enum COMPRESSION_METHOD {
+ STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4,
+ BEST = 5
+ };
+
+ if(rar->file.service > 0) {
+ return do_unstore_file(a, rar, buf, size, offset);
+ } else {
+ switch(rar->cstate.method) {
+ case STORE:
+ return do_unstore_file(a, rar, buf, size,
+ offset);
+ case FASTEST:
+ /* fallthrough */
+ case FAST:
+ /* fallthrough */
+ case NORMAL:
+ /* fallthrough */
+ case GOOD:
+ /* fallthrough */
+ case BEST:
+ /* No data is returned here. But because a sparse-file aware
+ * caller (like archive_read_data_into_fd) may treat zero-size
+ * as a sparse file block, we need to update the offset
+ * accordingly. At this point the decoder doesn't have any
+ * pending uncompressed data blocks, so the current position in
+ * the output file should be last_write_ptr. */
+ if (offset) *offset = rar->cstate.last_write_ptr;
+ return uncompress_file(a);
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Compression method not supported: 0x%x",
+ rar->cstate.method);
+
+ return ARCHIVE_FATAL;
+ }
+ }
+
+#if !defined WIN32
+ /* Not reached. */
+ return ARCHIVE_OK;
+#endif
+}
+
+static int verify_checksums(struct archive_read* a) {
+ int verify_crc;
+ struct rar5* rar = get_context(a);
+
+ /* Check checksums only when actually unpacking the data. There's no
+ * need to calculate checksum when we're skipping data in solid archives
+ * (skipping in solid archives is the same thing as unpacking compressed
+ * data and discarding the result). */
+
+ if(!rar->skip_mode) {
+ /* Always check checksums if we're not in skip mode */
+ verify_crc = 1;
+ } else {
+ /* We can override the logic above with a compile-time option
+ * NO_CRC_ON_SOLID_SKIP. This option is used during debugging,
+ * and it will check checksums of unpacked data even when
+ * we're skipping it. */
+
+#if defined CHECK_CRC_ON_SOLID_SKIP
+ /* Debug case */
+ verify_crc = 1;
+#else
+ /* Normal case */
+ verify_crc = 0;
+#endif
+ }
+
+ if(verify_crc) {
+ /* During unpacking, on each unpacked block we're calling the
+ * update_crc() function. Since we are here, the unpacking
+ * process is already over and we can check if calculated
+ * checksum (CRC32 or BLAKE2sp) is the same as what is stored
+ * in the archive. */
+ if(rar->file.stored_crc32 > 0) {
+ /* Check CRC32 only when the file contains a CRC32
+ * value for this file. */
+
+ if(rar->file.calculated_crc32 !=
+ rar->file.stored_crc32) {
+ /* Checksums do not match; the unpacked file
+ * is corrupted. */
+
+ DEBUG_CODE {
+ printf("Checksum error: CRC32 "
+ "(was: %08" PRIx32 ", expected: %08" PRIx32 ")\n",
+ rar->file.calculated_crc32,
+ rar->file.stored_crc32);
+ }
+
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Checksum error: CRC32");
+ return ARCHIVE_FATAL;
+#endif
+ } else {
+ DEBUG_CODE {
+ printf("Checksum OK: CRC32 "
+ "(%08" PRIx32 "/%08" PRIx32 ")\n",
+ rar->file.stored_crc32,
+ rar->file.calculated_crc32);
+ }
+ }
+ }
+
+ if(rar->file.has_blake2 > 0) {
+ /* BLAKE2sp is an optional checksum algorithm that is
+ * added to RARv5 archives when using the `-htb` switch
+ * during creation of archive.
+ *
+ * We now finalize the hash calculation by calling the
+ * `final` function. This will generate the final hash
+ * value we can use to compare it with the BLAKE2sp
+ * checksum that is stored in the archive.
+ *
+ * The return value of this `final` function is not
+ * very helpful, as it guards only against improper use.
+ * This is why we're explicitly ignoring it. */
+
+ uint8_t b2_buf[32];
+ (void) blake2sp_final(&rar->file.b2state, b2_buf, 32);
+
+ if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Checksum error: BLAKE2");
+
+ return ARCHIVE_FATAL;
+#endif
+ }
+ }
+ }
+
+ /* Finalization for this file has been successfully completed. */
+ return ARCHIVE_OK;
+}
+
+static int verify_global_checksums(struct archive_read* a) {
+ return verify_checksums(a);
+}
+
+/*
+ * Decryption function for the magic signature pattern. Check the comment near
+ * the `rar5_signature_xor` symbol to read the rationale behind this.
+ */
+static void rar5_signature(char *buf) {
+ size_t i;
+
+ for(i = 0; i < sizeof(rar5_signature_xor); i++) {
+ buf[i] = rar5_signature_xor[i] ^ 0xA1;
+ }
+}
+
+static int rar5_read_data(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset) {
+ int ret;
+ struct rar5* rar = get_context(a);
+
+ if (size)
+ *size = 0;
+
+ if(rar->file.dir > 0) {
+ /* Don't process any data if this file entry was declared
+ * as a directory. This is needed, because entries marked as
+ * directory doesn't have any dictionary buffer allocated, so
+ * it's impossible to perform any decompression. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't decompress an entry marked as a directory");
+ return ARCHIVE_FAILED;
+ }
+
+ if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Unpacker has written too many bytes");
+ return ARCHIVE_FATAL;
+ }
+
+ ret = use_data(rar, buff, size, offset);
+ if(ret == ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->file.eof == 1) {
+ return ARCHIVE_EOF;
+ }
+
+ ret = do_unpack(a, rar, buff, size, offset);
+ if(ret != ARCHIVE_OK) {
+ return ret;
+ }
+
+ if(rar->file.bytes_remaining == 0 &&
+ rar->cstate.last_write_ptr == rar->file.unpacked_size)
+ {
+ /* If all bytes of current file were processed, run
+ * finalization.
+ *
+ * Finalization will check checksum against proper values. If
+ * some of the checksums will not match, we'll return an error
+ * value in the last `archive_read_data` call to signal an error
+ * to the user. */
+
+ rar->file.eof = 1;
+ return verify_global_checksums(a);
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int rar5_read_data_skip(struct archive_read *a) {
+ struct rar5* rar = get_context(a);
+
+ if(rar->main.solid) {
+ /* In solid archives, instead of skipping the data, we need to
+ * extract it, and dispose the result. The side effect of this
+ * operation will be setting up the initial window buffer state
+ * needed to be able to extract the selected file. */
+
+ int ret;
+
+ /* Make sure to process all blocks in the compressed stream. */
+ while(rar->file.bytes_remaining > 0) {
+ /* Setting the "skip mode" will allow us to skip
+ * checksum checks during data skipping. Checking the
+ * checksum of skipped data isn't really necessary and
+ * it's only slowing things down.
+ *
+ * This is incremented instead of setting to 1 because
+ * this data skipping function can be called
+ * recursively. */
+ rar->skip_mode++;
+
+ /* We're disposing 1 block of data, so we use triple
+ * NULLs in arguments. */
+ ret = rar5_read_data(a, NULL, NULL, NULL);
+
+ /* Turn off "skip mode". */
+ rar->skip_mode--;
+
+ if(ret < 0 || ret == ARCHIVE_EOF) {
+ /* Propagate any potential error conditions
+ * to the caller. */
+ return ret;
+ }
+ }
+ } else {
+ /* In standard archives, we can just jump over the compressed
+ * stream. Each file in non-solid archives starts from an empty
+ * window buffer. */
+
+ if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {
+ return ARCHIVE_FATAL;
+ }
+
+ rar->file.bytes_remaining = 0;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int64_t rar5_seek_data(struct archive_read *a, int64_t offset,
+ int whence)
+{
+ (void) a;
+ (void) offset;
+ (void) whence;
+
+ /* We're a streaming unpacker, and we don't support seeking. */
+
+ return ARCHIVE_FATAL;
+}
+
+static int rar5_cleanup(struct archive_read *a) {
+ struct rar5* rar = get_context(a);
+
+ free(rar->cstate.window_buf);
+ free(rar->cstate.filtered_buf);
+
+ free(rar->vol.push_buf);
+
+ free_filters(rar);
+ cdeque_free(&rar->cstate.filters);
+
+ free(rar);
+ a->format->data = NULL;
+
+ return ARCHIVE_OK;
+}
+
+static int rar5_capabilities(struct archive_read * a) {
+ (void) a;
+ return 0;
+}
+
+static int rar5_has_encrypted_entries(struct archive_read *_a) {
+ (void) _a;
+
+ /* Unsupported for now. */
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
+}
+
+static int rar5_init(struct rar5* rar) {
+ memset(rar, 0, sizeof(struct rar5));
+
+ if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
+ return ARCHIVE_FATAL;
+
+ return ARCHIVE_OK;
+}
+
+int archive_read_support_format_rar5(struct archive *_a) {
+ struct archive_read* ar;
+ int ret;
+ struct rar5* rar;
+
+ if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
+ return ret;
+
+ rar = malloc(sizeof(*rar));
+ if(rar == NULL) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate rar5 data");
+ return ARCHIVE_FATAL;
+ }
+
+ if(ARCHIVE_OK != rar5_init(rar)) {
+ archive_set_error(&ar->archive, ENOMEM,
+ "Can't allocate rar5 filter buffer");
+ free(rar);
+ return ARCHIVE_FATAL;
+ }
+
+ ret = __archive_read_register_format(ar,
+ rar,
+ "rar5",
+ rar5_bid,
+ rar5_options,
+ rar5_read_header,
+ rar5_read_data,
+ rar5_read_data_skip,
+ rar5_seek_data,
+ rar5_cleanup,
+ rar5_capabilities,
+ rar5_has_encrypted_entries);
+
+ if(ret != ARCHIVE_OK) {
+ (void) rar5_cleanup(ar);
+ }
+
+ return ret;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_raw.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_raw.c
new file mode 100644
index 0000000000..ec0520b60a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_raw.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_raw.c 201107 2009-12-28 03:25:33Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct raw_info {
+ int64_t offset; /* Current position in the file. */
+ int64_t unconsumed;
+ int end_of_file;
+};
+
+static int archive_read_format_raw_bid(struct archive_read *, int);
+static int archive_read_format_raw_cleanup(struct archive_read *);
+static int archive_read_format_raw_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int archive_read_format_raw_read_data_skip(struct archive_read *);
+static int archive_read_format_raw_read_header(struct archive_read *,
+ struct archive_entry *);
+
+int
+archive_read_support_format_raw(struct archive *_a)
+{
+ struct raw_info *info;
+ struct archive_read *a = (struct archive_read *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_raw");
+
+ info = (struct raw_info *)calloc(1, sizeof(*info));
+ if (info == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate raw_info data");
+ return (ARCHIVE_FATAL);
+ }
+
+ r = __archive_read_register_format(a,
+ info,
+ "raw",
+ archive_read_format_raw_bid,
+ NULL,
+ archive_read_format_raw_read_header,
+ archive_read_format_raw_read_data,
+ archive_read_format_raw_read_data_skip,
+ NULL,
+ archive_read_format_raw_cleanup,
+ NULL,
+ NULL);
+ if (r != ARCHIVE_OK)
+ free(info);
+ return (r);
+}
+
+/*
+ * Bid 1 if this is a non-empty file. Anyone who can really support
+ * this should outbid us, so it should generally be safe to use "raw"
+ * in conjunction with other formats. But, this could really confuse
+ * folks if there are bid errors or minor file damage, so we don't
+ * include "raw" as part of support_format_all().
+ */
+static int
+archive_read_format_raw_bid(struct archive_read *a, int best_bid)
+{
+ if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) != NULL)
+ return (1);
+ return (-1);
+}
+
+/*
+ * Mock up a fake header.
+ */
+static int
+archive_read_format_raw_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct raw_info *info;
+
+ info = (struct raw_info *)(a->format->data);
+ if (info->end_of_file)
+ return (ARCHIVE_EOF);
+
+ a->archive.archive_format = ARCHIVE_FORMAT_RAW;
+ a->archive.archive_format_name = "raw";
+ archive_entry_set_pathname(entry, "data");
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_set_perm(entry, 0644);
+ /* I'm deliberately leaving most fields unset here. */
+
+ /* Let the filter fill out any fields it might have. */
+ return __archive_read_header(a, entry);
+}
+
+static int
+archive_read_format_raw_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct raw_info *info;
+ ssize_t avail;
+
+ info = (struct raw_info *)(a->format->data);
+
+ /* Consume the bytes we read last time. */
+ if (info->unconsumed) {
+ __archive_read_consume(a, info->unconsumed);
+ info->unconsumed = 0;
+ }
+
+ if (info->end_of_file)
+ return (ARCHIVE_EOF);
+
+ /* Get whatever bytes are immediately available. */
+ *buff = __archive_read_ahead(a, 1, &avail);
+ if (avail > 0) {
+ /* Return the bytes we just read */
+ *size = avail;
+ *offset = info->offset;
+ info->offset += *size;
+ info->unconsumed = avail;
+ return (ARCHIVE_OK);
+ } else if (0 == avail) {
+ /* Record and return end-of-file. */
+ info->end_of_file = 1;
+ *size = 0;
+ *offset = info->offset;
+ return (ARCHIVE_EOF);
+ } else {
+ /* Record and return an error. */
+ *size = 0;
+ *offset = info->offset;
+ return ((int)avail);
+ }
+}
+
+static int
+archive_read_format_raw_read_data_skip(struct archive_read *a)
+{
+ struct raw_info *info = (struct raw_info *)(a->format->data);
+
+ /* Consume the bytes we read last time. */
+ if (info->unconsumed) {
+ __archive_read_consume(a, info->unconsumed);
+ info->unconsumed = 0;
+ }
+ info->end_of_file = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_raw_cleanup(struct archive_read *a)
+{
+ struct raw_info *info;
+
+ info = (struct raw_info *)(a->format->data);
+ free(info);
+ a->format->data = NULL;
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_tar.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_tar.c
new file mode 100644
index 0000000000..93c3fd5857
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_tar.c
@@ -0,0 +1,2946 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_tar.c 201161 2009-12-29 05:44:39Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stddef.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_acl_private.h" /* For ACL parsing routines. */
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define tar_min(a,b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Layout of POSIX 'ustar' tar header.
+ */
+struct archive_entry_header_ustar {
+ char name[100];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char checksum[8];
+ char typeflag[1];
+ char linkname[100]; /* "old format" header ends here */
+ char magic[6]; /* For POSIX: "ustar\0" */
+ char version[2]; /* For POSIX: "00" */
+ char uname[32];
+ char gname[32];
+ char rdevmajor[8];
+ char rdevminor[8];
+ char prefix[155];
+};
+
+/*
+ * Structure of GNU tar header
+ */
+struct gnu_sparse {
+ char offset[12];
+ char numbytes[12];
+};
+
+struct archive_entry_header_gnutar {
+ char name[100];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char checksum[8];
+ char typeflag[1];
+ char linkname[100];
+ char magic[8]; /* "ustar \0" (note blank/blank/null at end) */
+ char uname[32];
+ char gname[32];
+ char rdevmajor[8];
+ char rdevminor[8];
+ char atime[12];
+ char ctime[12];
+ char offset[12];
+ char longnames[4];
+ char unused[1];
+ struct gnu_sparse sparse[4];
+ char isextended[1];
+ char realsize[12];
+ /*
+ * Old GNU format doesn't use POSIX 'prefix' field; they use
+ * the 'L' (longname) entry instead.
+ */
+};
+
+/*
+ * Data specific to this format.
+ */
+struct sparse_block {
+ struct sparse_block *next;
+ int64_t offset;
+ int64_t remaining;
+ int hole;
+};
+
+struct tar {
+ struct archive_string acl_text;
+ struct archive_string entry_pathname;
+ /* For "GNU.sparse.name" and other similar path extensions. */
+ struct archive_string entry_pathname_override;
+ struct archive_string entry_linkpath;
+ struct archive_string entry_uname;
+ struct archive_string entry_gname;
+ struct archive_string longlink;
+ struct archive_string longname;
+ struct archive_string pax_header;
+ struct archive_string pax_global;
+ struct archive_string line;
+ int pax_hdrcharset_binary;
+ int header_recursion_depth;
+ int64_t entry_bytes_remaining;
+ int64_t entry_offset;
+ int64_t entry_padding;
+ int64_t entry_bytes_unconsumed;
+ int64_t realsize;
+ int sparse_allowed;
+ struct sparse_block *sparse_list;
+ struct sparse_block *sparse_last;
+ int64_t sparse_offset;
+ int64_t sparse_numbytes;
+ int sparse_gnu_major;
+ int sparse_gnu_minor;
+ char sparse_gnu_pending;
+
+ struct archive_string localname;
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv;
+ struct archive_string_conv *sconv_acl;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+ int compat_2x;
+ int process_mac_extensions;
+ int read_concatenated_archives;
+ int realsize_override;
+};
+
+static int archive_block_is_null(const char *p);
+static char *base64_decode(const char *, size_t, size_t *);
+static int gnu_add_sparse_entry(struct archive_read *, struct tar *,
+ int64_t offset, int64_t remaining);
+
+static void gnu_clear_sparse_list(struct tar *);
+static int gnu_sparse_old_read(struct archive_read *, struct tar *,
+ const struct archive_entry_header_gnutar *header, size_t *);
+static int gnu_sparse_old_parse(struct archive_read *, struct tar *,
+ const struct gnu_sparse *sparse, int length);
+static int gnu_sparse_01_parse(struct archive_read *, struct tar *,
+ const char *);
+static ssize_t gnu_sparse_10_read(struct archive_read *, struct tar *,
+ size_t *);
+static int header_Solaris_ACL(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *, size_t *);
+static int header_common(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *);
+static int header_old_tar(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *);
+static int header_pax_extensions(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *, size_t *);
+static int header_pax_global(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int header_longlink(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int header_longname(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int read_mac_metadata_blob(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int header_volume(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int header_ustar(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h);
+static int header_gnutar(struct archive_read *, struct tar *,
+ struct archive_entry *, const void *h, size_t *);
+static int archive_read_format_tar_bid(struct archive_read *, int);
+static int archive_read_format_tar_options(struct archive_read *,
+ const char *, const char *);
+static int archive_read_format_tar_cleanup(struct archive_read *);
+static int archive_read_format_tar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset);
+static int archive_read_format_tar_skip(struct archive_read *a);
+static int archive_read_format_tar_read_header(struct archive_read *,
+ struct archive_entry *);
+static int checksum(struct archive_read *, const void *);
+static int pax_attribute(struct archive_read *, struct tar *,
+ struct archive_entry *, const char *key, const char *value,
+ size_t value_length);
+static int pax_attribute_acl(struct archive_read *, struct tar *,
+ struct archive_entry *, const char *, int);
+static int pax_attribute_xattr(struct archive_entry *, const char *,
+ const char *);
+static int pax_header(struct archive_read *, struct tar *,
+ struct archive_entry *, struct archive_string *);
+static void pax_time(const char *, int64_t *sec, long *nanos);
+static ssize_t readline(struct archive_read *, struct tar *, const char **,
+ ssize_t limit, size_t *);
+static int read_body_to_string(struct archive_read *, struct tar *,
+ struct archive_string *, const void *h, size_t *);
+static int solaris_sparse_parse(struct archive_read *, struct tar *,
+ struct archive_entry *, const char *);
+static int64_t tar_atol(const char *, size_t);
+static int64_t tar_atol10(const char *, size_t);
+static int64_t tar_atol256(const char *, size_t);
+static int64_t tar_atol8(const char *, size_t);
+static int tar_read_header(struct archive_read *, struct tar *,
+ struct archive_entry *, size_t *);
+static int tohex(int c);
+static char *url_decode(const char *);
+static void tar_flush_unconsumed(struct archive_read *, size_t *);
+
+
+int
+archive_read_support_format_gnutar(struct archive *a)
+{
+ archive_check_magic(a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_gnutar");
+ return (archive_read_support_format_tar(a));
+}
+
+
+int
+archive_read_support_format_tar(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct tar *tar;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_tar");
+
+ tar = (struct tar *)calloc(1, sizeof(*tar));
+ if (tar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate tar data");
+ return (ARCHIVE_FATAL);
+ }
+#ifdef HAVE_COPYFILE_H
+ /* Set this by default on Mac OS. */
+ tar->process_mac_extensions = 1;
+#endif
+
+ r = __archive_read_register_format(a, tar, "tar",
+ archive_read_format_tar_bid,
+ archive_read_format_tar_options,
+ archive_read_format_tar_read_header,
+ archive_read_format_tar_read_data,
+ archive_read_format_tar_skip,
+ NULL,
+ archive_read_format_tar_cleanup,
+ NULL,
+ NULL);
+
+ if (r != ARCHIVE_OK)
+ free(tar);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_tar_cleanup(struct archive_read *a)
+{
+ struct tar *tar;
+
+ tar = (struct tar *)(a->format->data);
+ gnu_clear_sparse_list(tar);
+ archive_string_free(&tar->acl_text);
+ archive_string_free(&tar->entry_pathname);
+ archive_string_free(&tar->entry_pathname_override);
+ archive_string_free(&tar->entry_linkpath);
+ archive_string_free(&tar->entry_uname);
+ archive_string_free(&tar->entry_gname);
+ archive_string_free(&tar->line);
+ archive_string_free(&tar->pax_global);
+ archive_string_free(&tar->pax_header);
+ archive_string_free(&tar->longname);
+ archive_string_free(&tar->longlink);
+ archive_string_free(&tar->localname);
+ free(tar);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Validate number field
+ *
+ * This has to be pretty lenient in order to accommodate the enormous
+ * variety of tar writers in the world:
+ * = POSIX (IEEE Std 1003.1-1988) ustar requires octal values with leading
+ * zeros and allows fields to be terminated with space or null characters
+ * = Many writers use different termination (in particular, libarchive
+ * omits terminator bytes to squeeze one or two more digits)
+ * = Many writers pad with space and omit leading zeros
+ * = GNU tar and star write base-256 values if numbers are too
+ * big to be represented in octal
+ *
+ * Examples of specific tar headers that we should support:
+ * = Perl Archive::Tar terminates uid, gid, devminor and devmajor with two
+ * null bytes, pads size with spaces and other numeric fields with zeroes
+ * = plexus-archiver prior to 2.6.3 (before switching to commons-compress)
+ * may have uid and gid fields filled with spaces without any octal digits
+ * at all and pads all numeric fields with spaces
+ *
+ * This should tolerate all variants in use. It will reject a field
+ * where the writer just left garbage after a trailing NUL.
+ */
+static int
+validate_number_field(const char* p_field, size_t i_size)
+{
+ unsigned char marker = (unsigned char)p_field[0];
+ if (marker == 128 || marker == 255 || marker == 0) {
+ /* Base-256 marker, there's nothing we can check. */
+ return 1;
+ } else {
+ /* Must be octal */
+ size_t i = 0;
+ /* Skip any leading spaces */
+ while (i < i_size && p_field[i] == ' ') {
+ ++i;
+ }
+ /* Skip octal digits. */
+ while (i < i_size && p_field[i] >= '0' && p_field[i] <= '7') {
+ ++i;
+ }
+ /* Any remaining characters must be space or NUL padding. */
+ while (i < i_size) {
+ if (p_field[i] != ' ' && p_field[i] != 0) {
+ return 0;
+ }
+ ++i;
+ }
+ return 1;
+ }
+}
+
+static int
+archive_read_format_tar_bid(struct archive_read *a, int best_bid)
+{
+ int bid;
+ const char *h;
+ const struct archive_entry_header_ustar *header;
+
+ (void)best_bid; /* UNUSED */
+
+ bid = 0;
+
+ /* Now let's look at the actual header and see if it matches. */
+ h = __archive_read_ahead(a, 512, NULL);
+ if (h == NULL)
+ return (-1);
+
+ /* If it's an end-of-archive mark, we can handle it. */
+ if (h[0] == 0 && archive_block_is_null(h)) {
+ /*
+ * Usually, I bid the number of bits verified, but
+ * in this case, 4096 seems excessive so I picked 10 as
+ * an arbitrary but reasonable-seeming value.
+ */
+ return (10);
+ }
+
+ /* If it's not an end-of-archive mark, it must have a valid checksum.*/
+ if (!checksum(a, h))
+ return (0);
+ bid += 48; /* Checksum is usually 6 octal digits. */
+
+ header = (const struct archive_entry_header_ustar *)h;
+
+ /* Recognize POSIX formats. */
+ if ((memcmp(header->magic, "ustar\0", 6) == 0)
+ && (memcmp(header->version, "00", 2) == 0))
+ bid += 56;
+
+ /* Recognize GNU tar format. */
+ if ((memcmp(header->magic, "ustar ", 6) == 0)
+ && (memcmp(header->version, " \0", 2) == 0))
+ bid += 56;
+
+ /* Type flag must be null, digit or A-Z, a-z. */
+ if (header->typeflag[0] != 0 &&
+ !( header->typeflag[0] >= '0' && header->typeflag[0] <= '9') &&
+ !( header->typeflag[0] >= 'A' && header->typeflag[0] <= 'Z') &&
+ !( header->typeflag[0] >= 'a' && header->typeflag[0] <= 'z') )
+ return (0);
+ bid += 2; /* 6 bits of variation in an 8-bit field leaves 2 bits. */
+
+ /*
+ * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields.
+ */
+ if (validate_number_field(header->mode, sizeof(header->mode)) == 0
+ || validate_number_field(header->uid, sizeof(header->uid)) == 0
+ || validate_number_field(header->gid, sizeof(header->gid)) == 0
+ || validate_number_field(header->mtime, sizeof(header->mtime)) == 0
+ || validate_number_field(header->size, sizeof(header->size)) == 0
+ || validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0
+ || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0) {
+ bid = 0;
+ }
+
+ return (bid);
+}
+
+static int
+archive_read_format_tar_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct tar *tar;
+ int ret = ARCHIVE_FAILED;
+
+ tar = (struct tar *)(a->format->data);
+ if (strcmp(key, "compat-2x") == 0) {
+ /* Handle UTF-8 filenames as libarchive 2.x */
+ tar->compat_2x = (val != NULL && val[0] != 0);
+ tar->init_default_conversion = tar->compat_2x;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "tar: hdrcharset option needs a character-set name");
+ else {
+ tar->opt_sconv =
+ archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (tar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ } else if (strcmp(key, "mac-ext") == 0) {
+ tar->process_mac_extensions = (val != NULL && val[0] != 0);
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "read_concatenated_archives") == 0) {
+ tar->read_concatenated_archives = (val != NULL && val[0] != 0);
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/* utility function- this exists to centralize the logic of tracking
+ * how much unconsumed data we have floating around, and to consume
+ * anything outstanding since we're going to do read_aheads
+ */
+static void
+tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed)
+{
+ if (*unconsumed) {
+/*
+ void *data = (void *)__archive_read_ahead(a, *unconsumed, NULL);
+ * this block of code is to poison claimed unconsumed space, ensuring
+ * things break if it is in use still.
+ * currently it WILL break things, so enable it only for debugging this issue
+ if (data) {
+ memset(data, 0xff, *unconsumed);
+ }
+*/
+ __archive_read_consume(a, *unconsumed);
+ *unconsumed = 0;
+ }
+}
+
+/*
+ * The function invoked by archive_read_next_header(). This
+ * just sets up a few things and then calls the internal
+ * tar_read_header() function below.
+ */
+static int
+archive_read_format_tar_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ /*
+ * When converting tar archives to cpio archives, it is
+ * essential that each distinct file have a distinct inode
+ * number. To simplify this, we keep a static count here to
+ * assign fake dev/inode numbers to each tar entry. Note that
+ * pax format archives may overwrite this with something more
+ * useful.
+ *
+ * Ideally, we would track every file read from the archive so
+ * that we could assign the same dev/ino pair to hardlinks,
+ * but the memory required to store a complete lookup table is
+ * probably not worthwhile just to support the relatively
+ * obscure tar->cpio conversion case.
+ */
+ static int default_inode;
+ static int default_dev;
+ struct tar *tar;
+ const char *p;
+ const wchar_t *wp;
+ int r;
+ size_t l, unconsumed = 0;
+
+ /* Assign default device/inode values. */
+ archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */
+ archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */
+ /* Limit generated st_ino number to 16 bits. */
+ if (default_inode >= 0xffff) {
+ ++default_dev;
+ default_inode = 0;
+ }
+
+ tar = (struct tar *)(a->format->data);
+ tar->entry_offset = 0;
+ gnu_clear_sparse_list(tar);
+ tar->realsize = -1; /* Mark this as "unset" */
+ tar->realsize_override = 0;
+
+ /* Setup default string conversion. */
+ tar->sconv = tar->opt_sconv;
+ if (tar->sconv == NULL) {
+ if (!tar->init_default_conversion) {
+ tar->sconv_default =
+ archive_string_default_conversion_for_read(&(a->archive));
+ tar->init_default_conversion = 1;
+ }
+ tar->sconv = tar->sconv_default;
+ }
+
+ r = tar_read_header(a, tar, entry, &unconsumed);
+
+ tar_flush_unconsumed(a, &unconsumed);
+
+ /*
+ * "non-sparse" files are really just sparse files with
+ * a single block.
+ */
+ if (tar->sparse_list == NULL) {
+ if (gnu_add_sparse_entry(a, tar, 0, tar->entry_bytes_remaining)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ struct sparse_block *sb;
+
+ for (sb = tar->sparse_list; sb != NULL; sb = sb->next) {
+ if (!sb->hole)
+ archive_entry_sparse_add_entry(entry,
+ sb->offset, sb->remaining);
+ }
+ }
+
+ if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) {
+ /*
+ * "Regular" entry with trailing '/' is really
+ * directory: This is needed for certain old tar
+ * variants and even for some broken newer ones.
+ */
+ if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+ l = wcslen(wp);
+ if (l > 0 && wp[l - 1] == L'/') {
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ tar->entry_bytes_remaining = 0;
+ tar->entry_padding = 0;
+ }
+ } else if ((p = archive_entry_pathname(entry)) != NULL) {
+ l = strlen(p);
+ if (l > 0 && p[l - 1] == '/') {
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ tar->entry_bytes_remaining = 0;
+ tar->entry_padding = 0;
+ }
+ }
+ }
+ return (r);
+}
+
+static int
+archive_read_format_tar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ ssize_t bytes_read;
+ struct tar *tar;
+ struct sparse_block *p;
+
+ tar = (struct tar *)(a->format->data);
+
+ for (;;) {
+ /* Remove exhausted entries from sparse list. */
+ while (tar->sparse_list != NULL &&
+ tar->sparse_list->remaining == 0) {
+ p = tar->sparse_list;
+ tar->sparse_list = p->next;
+ free(p);
+ }
+
+ if (tar->entry_bytes_unconsumed) {
+ __archive_read_consume(a, tar->entry_bytes_unconsumed);
+ tar->entry_bytes_unconsumed = 0;
+ }
+
+ /* If we're at end of file, return EOF. */
+ if (tar->sparse_list == NULL ||
+ tar->entry_bytes_remaining == 0) {
+ if (__archive_read_consume(a, tar->entry_padding) < 0)
+ return (ARCHIVE_FATAL);
+ tar->entry_padding = 0;
+ *buff = NULL;
+ *size = 0;
+ *offset = tar->realsize;
+ return (ARCHIVE_EOF);
+ }
+
+ *buff = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ if (*buff == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated tar archive");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_read > tar->entry_bytes_remaining)
+ bytes_read = (ssize_t)tar->entry_bytes_remaining;
+ /* Don't read more than is available in the
+ * current sparse block. */
+ if (tar->sparse_list->remaining < bytes_read)
+ bytes_read = (ssize_t)tar->sparse_list->remaining;
+ *size = bytes_read;
+ *offset = tar->sparse_list->offset;
+ tar->sparse_list->remaining -= bytes_read;
+ tar->sparse_list->offset += bytes_read;
+ tar->entry_bytes_remaining -= bytes_read;
+ tar->entry_bytes_unconsumed = bytes_read;
+
+ if (!tar->sparse_list->hole)
+ return (ARCHIVE_OK);
+ /* Current is hole data and skip this. */
+ }
+}
+
+static int
+archive_read_format_tar_skip(struct archive_read *a)
+{
+ int64_t bytes_skipped;
+ int64_t request;
+ struct sparse_block *p;
+ struct tar* tar;
+
+ tar = (struct tar *)(a->format->data);
+
+ /* Do not consume the hole of a sparse file. */
+ request = 0;
+ for (p = tar->sparse_list; p != NULL; p = p->next) {
+ if (!p->hole) {
+ if (p->remaining >= INT64_MAX - request) {
+ return ARCHIVE_FATAL;
+ }
+ request += p->remaining;
+ }
+ }
+ if (request > tar->entry_bytes_remaining)
+ request = tar->entry_bytes_remaining;
+ request += tar->entry_padding + tar->entry_bytes_unconsumed;
+
+ bytes_skipped = __archive_read_consume(a, request);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ tar->entry_bytes_remaining = 0;
+ tar->entry_bytes_unconsumed = 0;
+ tar->entry_padding = 0;
+
+ /* Free the sparse list. */
+ gnu_clear_sparse_list(tar);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This function recursively interprets all of the headers associated
+ * with a single entry.
+ */
+static int
+tar_read_header(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, size_t *unconsumed)
+{
+ ssize_t bytes;
+ int err, eof_vol_header;
+ const char *h;
+ const struct archive_entry_header_ustar *header;
+ const struct archive_entry_header_gnutar *gnuheader;
+
+ eof_vol_header = 0;
+
+ /* Loop until we find a workable header record. */
+ for (;;) {
+ tar_flush_unconsumed(a, unconsumed);
+
+ /* Read 512-byte header record */
+ h = __archive_read_ahead(a, 512, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes == 0) { /* EOF at a block boundary. */
+ /* Some writers do omit the block of nulls. <sigh> */
+ return (ARCHIVE_EOF);
+ }
+ if (bytes < 512) { /* Short block at EOF; this is bad. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated tar archive");
+ return (ARCHIVE_FATAL);
+ }
+ *unconsumed = 512;
+
+ /* Header is workable if it's not an end-of-archive mark. */
+ if (h[0] != 0 || !archive_block_is_null(h))
+ break;
+
+ /* Ensure format is set for archives with only null blocks. */
+ if (a->archive.archive_format_name == NULL) {
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+ a->archive.archive_format_name = "tar";
+ }
+
+ if (!tar->read_concatenated_archives) {
+ /* Try to consume a second all-null record, as well. */
+ tar_flush_unconsumed(a, unconsumed);
+ h = __archive_read_ahead(a, 512, NULL);
+ if (h != NULL && h[0] == 0 && archive_block_is_null(h))
+ __archive_read_consume(a, 512);
+ archive_clear_error(&a->archive);
+ return (ARCHIVE_EOF);
+ }
+
+ /*
+ * We're reading concatenated archives, ignore this block and
+ * loop to get the next.
+ */
+ }
+
+ /*
+ * Note: If the checksum fails and we return ARCHIVE_RETRY,
+ * then the client is likely to just retry. This is a very
+ * crude way to search for the next valid header!
+ *
+ * TODO: Improve this by implementing a real header scan.
+ */
+ if (!checksum(a, h)) {
+ tar_flush_unconsumed(a, unconsumed);
+ archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
+ return (ARCHIVE_RETRY); /* Retryable: Invalid header */
+ }
+
+ if (++tar->header_recursion_depth > 32) {
+ tar_flush_unconsumed(a, unconsumed);
+ archive_set_error(&a->archive, EINVAL, "Too many special headers");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Determine the format variant. */
+ header = (const struct archive_entry_header_ustar *)h;
+
+ switch(header->typeflag[0]) {
+ case 'A': /* Solaris tar ACL */
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "Solaris tar";
+ err = header_Solaris_ACL(a, tar, entry, h, unconsumed);
+ break;
+ case 'g': /* POSIX-standard 'g' header. */
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "POSIX pax interchange format";
+ err = header_pax_global(a, tar, entry, h, unconsumed);
+ if (err == ARCHIVE_EOF)
+ return (err);
+ break;
+ case 'K': /* Long link name (GNU tar, others) */
+ err = header_longlink(a, tar, entry, h, unconsumed);
+ break;
+ case 'L': /* Long filename (GNU tar, others) */
+ err = header_longname(a, tar, entry, h, unconsumed);
+ break;
+ case 'V': /* GNU volume header */
+ err = header_volume(a, tar, entry, h, unconsumed);
+ if (err == ARCHIVE_EOF)
+ eof_vol_header = 1;
+ break;
+ case 'X': /* Used by SUN tar; same as 'x'. */
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name =
+ "POSIX pax interchange format (Sun variant)";
+ err = header_pax_extensions(a, tar, entry, h, unconsumed);
+ break;
+ case 'x': /* POSIX-standard 'x' header. */
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "POSIX pax interchange format";
+ err = header_pax_extensions(a, tar, entry, h, unconsumed);
+ break;
+ default:
+ gnuheader = (const struct archive_entry_header_gnutar *)h;
+ if (memcmp(gnuheader->magic, "ustar \0", 8) == 0) {
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+ a->archive.archive_format_name = "GNU tar format";
+ err = header_gnutar(a, tar, entry, h, unconsumed);
+ } else if (memcmp(header->magic, "ustar", 5) == 0) {
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+ a->archive.archive_format_name = "POSIX ustar format";
+ }
+ err = header_ustar(a, tar, entry, h);
+ } else {
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+ a->archive.archive_format_name = "tar (non-POSIX)";
+ err = header_old_tar(a, tar, entry, h);
+ }
+ }
+ if (err == ARCHIVE_FATAL)
+ return (err);
+
+ tar_flush_unconsumed(a, unconsumed);
+
+ h = NULL;
+ header = NULL;
+
+ --tar->header_recursion_depth;
+ /* Yuck. Apple's design here ends up storing long pathname
+ * extensions for both the AppleDouble extension entry and the
+ * regular entry.
+ */
+ if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) &&
+ tar->header_recursion_depth == 0 &&
+ tar->process_mac_extensions) {
+ int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed);
+ if (err2 < err)
+ err = err2;
+ }
+
+ /* We return warnings or success as-is. Anything else is fatal. */
+ if (err == ARCHIVE_WARN || err == ARCHIVE_OK) {
+ if (tar->sparse_gnu_pending) {
+ if (tar->sparse_gnu_major == 1 &&
+ tar->sparse_gnu_minor == 0) {
+ ssize_t bytes_read;
+
+ tar->sparse_gnu_pending = 0;
+ /* Read initial sparse map. */
+ bytes_read = gnu_sparse_10_read(a, tar, unconsumed);
+ if (bytes_read < 0)
+ return ((int)bytes_read);
+ tar->entry_bytes_remaining -= bytes_read;
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Unrecognized GNU sparse file format");
+ return (ARCHIVE_WARN);
+ }
+ tar->sparse_gnu_pending = 0;
+ }
+ return (err);
+ }
+ if (err == ARCHIVE_EOF) {
+ if (!eof_vol_header) {
+ /* EOF when recursively reading a header is bad. */
+ archive_set_error(&a->archive, EINVAL,
+ "Damaged tar archive");
+ } else {
+ /* If we encounter just a GNU volume header treat
+ * this situation as an empty archive */
+ return (ARCHIVE_EOF);
+ }
+ }
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return true if block checksum is correct.
+ */
+static int
+checksum(struct archive_read *a, const void *h)
+{
+ const unsigned char *bytes;
+ const struct archive_entry_header_ustar *header;
+ int check, sum;
+ size_t i;
+
+ (void)a; /* UNUSED */
+ bytes = (const unsigned char *)h;
+ header = (const struct archive_entry_header_ustar *)h;
+
+ /* Checksum field must hold an octal number */
+ for (i = 0; i < sizeof(header->checksum); ++i) {
+ char c = header->checksum[i];
+ if (c != ' ' && c != '\0' && (c < '0' || c > '7'))
+ return 0;
+ }
+
+ /*
+ * Test the checksum. Note that POSIX specifies _unsigned_
+ * bytes for this calculation.
+ */
+ sum = (int)tar_atol(header->checksum, sizeof(header->checksum));
+ check = 0;
+ for (i = 0; i < 148; i++)
+ check += (unsigned char)bytes[i];
+ for (; i < 156; i++)
+ check += 32;
+ for (; i < 512; i++)
+ check += (unsigned char)bytes[i];
+ if (sum == check)
+ return (1);
+
+ /*
+ * Repeat test with _signed_ bytes, just in case this archive
+ * was created by an old BSD, Solaris, or HP-UX tar with a
+ * broken checksum calculation.
+ */
+ check = 0;
+ for (i = 0; i < 148; i++)
+ check += (signed char)bytes[i];
+ for (; i < 156; i++)
+ check += 32;
+ for (; i < 512; i++)
+ check += (signed char)bytes[i];
+ if (sum == check)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Return true if this block contains only nulls.
+ */
+static int
+archive_block_is_null(const char *p)
+{
+ unsigned i;
+
+ for (i = 0; i < 512; i++)
+ if (*p++)
+ return (0);
+ return (1);
+}
+
+/*
+ * Interpret 'A' Solaris ACL header
+ */
+static int
+header_Solaris_ACL(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ const struct archive_entry_header_ustar *header;
+ size_t size;
+ int err, acl_type;
+ int64_t type;
+ char *acl, *p;
+
+ /*
+ * read_body_to_string adds a NUL terminator, but we need a little
+ * more to make sure that we don't overrun acl_text later.
+ */
+ header = (const struct archive_entry_header_ustar *)h;
+ size = (size_t)tar_atol(header->size, sizeof(header->size));
+ err = read_body_to_string(a, tar, &(tar->acl_text), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+
+ /* Recursively read next header */
+ err = tar_read_header(a, tar, entry, unconsumed);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+
+ /* TODO: Examine the first characters to see if this
+ * is an AIX ACL descriptor. We'll likely never support
+ * them, but it would be polite to recognize and warn when
+ * we do see them. */
+
+ /* Leading octal number indicates ACL type and number of entries. */
+ p = acl = tar->acl_text.s;
+ type = 0;
+ while (*p != '\0' && p < acl + size) {
+ if (*p < '0' || *p > '7') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (invalid digit)");
+ return(ARCHIVE_WARN);
+ }
+ type <<= 3;
+ type += *p - '0';
+ if (type > 077777777) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (count too large)");
+ return (ARCHIVE_WARN);
+ }
+ p++;
+ }
+ switch ((int)type & ~0777777) {
+ case 01000000:
+ /* POSIX.1e ACL */
+ acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ break;
+ case 03000000:
+ /* NFSv4 ACL */
+ acl_type = ARCHIVE_ENTRY_ACL_TYPE_NFS4;
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (unsupported type %o)",
+ (int)type);
+ return (ARCHIVE_WARN);
+ }
+ p++;
+
+ if (p >= acl + size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (body overflow)");
+ return(ARCHIVE_WARN);
+ }
+
+ /* ACL text is null-terminated; find the end. */
+ size -= (p - acl);
+ acl = p;
+
+ while (*p != '\0' && p < acl + size)
+ p++;
+
+ if (tar->sconv_acl == NULL) {
+ tar->sconv_acl = archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (tar->sconv_acl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ archive_strncpy(&(tar->localname), acl, p - acl);
+ err = archive_acl_from_text_l(archive_entry_acl(entry),
+ tar->localname.s, acl_type, tar->sconv_acl);
+ if (err != ARCHIVE_OK) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for ACL");
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Malformed Solaris ACL attribute (unparsable)");
+ }
+ return (err);
+}
+
+/*
+ * Interpret 'K' long linkname header.
+ */
+static int
+header_longlink(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int err;
+
+ err = read_body_to_string(a, tar, &(tar->longlink), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+ err = tar_read_header(a, tar, entry, unconsumed);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+ /* Set symlink if symlink already set, else hardlink. */
+ archive_entry_copy_link(entry, tar->longlink.s);
+ return (ARCHIVE_OK);
+}
+
+static int
+set_conversion_failed_error(struct archive_read *a,
+ struct archive_string_conv *sconv, const char *name)
+{
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for %s", name);
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "%s can't be converted from %s to current locale.",
+ name, archive_string_conversion_charset_name(sconv));
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Interpret 'L' long filename header.
+ */
+static int
+header_longname(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int err;
+
+ err = read_body_to_string(a, tar, &(tar->longname), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+ /* Read and parse "real" header, then override name. */
+ err = tar_read_header(a, tar, entry, unconsumed);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+ if (archive_entry_copy_pathname_l(entry, tar->longname.s,
+ archive_strlen(&(tar->longname)), tar->sconv) != 0)
+ err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+ return (err);
+}
+
+
+/*
+ * Interpret 'V' GNU tar volume header.
+ */
+static int
+header_volume(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ (void)h;
+
+ /* Just skip this and read the next header. */
+ return (tar_read_header(a, tar, entry, unconsumed));
+}
+
+/*
+ * Read body of an archive entry into an archive_string object.
+ */
+static int
+read_body_to_string(struct archive_read *a, struct tar *tar,
+ struct archive_string *as, const void *h, size_t *unconsumed)
+{
+ int64_t size;
+ const struct archive_entry_header_ustar *header;
+ const void *src;
+
+ (void)tar; /* UNUSED */
+ header = (const struct archive_entry_header_ustar *)h;
+ size = tar_atol(header->size, sizeof(header->size));
+ if ((size > 1048576) || (size < 0)) {
+ archive_set_error(&a->archive, EINVAL,
+ "Special header too large");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Fail if we can't make our buffer big enough. */
+ if (archive_string_ensure(as, (size_t)size+1) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ tar_flush_unconsumed(a, unconsumed);
+
+ /* Read the body into the string. */
+ *unconsumed = (size_t)((size + 511) & ~ 511);
+ src = __archive_read_ahead(a, *unconsumed, NULL);
+ if (src == NULL) {
+ *unconsumed = 0;
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(as->s, src, (size_t)size);
+ as->s[size] = '\0';
+ as->length = (size_t)size;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Parse out common header elements.
+ *
+ * This would be the same as header_old_tar, except that the
+ * filename is handled slightly differently for old and POSIX
+ * entries (POSIX entries support a 'prefix'). This factoring
+ * allows header_old_tar and header_ustar
+ * to handle filenames differently, while still putting most of the
+ * common parsing into one place.
+ */
+static int
+header_common(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h)
+{
+ const struct archive_entry_header_ustar *header;
+ char tartype;
+ int err = ARCHIVE_OK;
+
+ header = (const struct archive_entry_header_ustar *)h;
+ if (header->linkname[0])
+ archive_strncpy(&(tar->entry_linkpath),
+ header->linkname, sizeof(header->linkname));
+ else
+ archive_string_empty(&(tar->entry_linkpath));
+
+ /* Parse out the numeric fields (all are octal) */
+ archive_entry_set_mode(entry,
+ (mode_t)tar_atol(header->mode, sizeof(header->mode)));
+ archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
+ archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
+ tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size));
+ if (tar->entry_bytes_remaining < 0) {
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Tar entry has negative size");
+ return (ARCHIVE_FATAL);
+ }
+ if (tar->entry_bytes_remaining == INT64_MAX) {
+ /* Note: tar_atol returns INT64_MAX on overflow */
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Tar entry size overflow");
+ return (ARCHIVE_FATAL);
+ }
+ tar->realsize = tar->entry_bytes_remaining;
+ archive_entry_set_size(entry, tar->entry_bytes_remaining);
+ archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
+
+ /* Handle the tar type flag appropriately. */
+ tartype = header->typeflag[0];
+
+ switch (tartype) {
+ case '1': /* Hard link */
+ if (archive_entry_copy_hardlink_l(entry, tar->entry_linkpath.s,
+ archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv,
+ "Linkname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+ /*
+ * The following may seem odd, but: Technically, tar
+ * does not store the file type for a "hard link"
+ * entry, only the fact that it is a hard link. So, I
+ * leave the type zero normally. But, pax interchange
+ * format allows hard links to have data, which
+ * implies that the underlying entry is a regular
+ * file.
+ */
+ if (archive_entry_size(entry) > 0)
+ archive_entry_set_filetype(entry, AE_IFREG);
+
+ /*
+ * A tricky point: Traditionally, tar readers have
+ * ignored the size field when reading hardlink
+ * entries, and some writers put non-zero sizes even
+ * though the body is empty. POSIX blessed this
+ * convention in the 1988 standard, but broke with
+ * this tradition in 2001 by permitting hardlink
+ * entries to store valid bodies in pax interchange
+ * format, but not in ustar format. Since there is no
+ * hard and fast way to distinguish pax interchange
+ * from earlier archives (the 'x' and 'g' entries are
+ * optional, after all), we need a heuristic.
+ */
+ if (archive_entry_size(entry) == 0) {
+ /* If the size is already zero, we're done. */
+ } else if (a->archive.archive_format
+ == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+ /* Definitely pax extended; must obey hardlink size. */
+ } else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR
+ || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR)
+ {
+ /* Old-style or GNU tar: we must ignore the size. */
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ } else if (archive_read_format_tar_bid(a, 50) > 50) {
+ /*
+ * We don't know if it's pax: If the bid
+ * function sees a valid ustar header
+ * immediately following, then let's ignore
+ * the hardlink size.
+ */
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ }
+ /*
+ * TODO: There are still two cases I'd like to handle:
+ * = a ustar non-pax archive with a hardlink entry at
+ * end-of-archive. (Look for block of nulls following?)
+ * = a pax archive that has not seen any pax headers
+ * and has an entry which is a hardlink entry storing
+ * a body containing an uncompressed tar archive.
+ * The first is worth addressing; I don't see any reliable
+ * way to deal with the second possibility.
+ */
+ break;
+ case '2': /* Symlink */
+ archive_entry_set_filetype(entry, AE_IFLNK);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ if (archive_entry_copy_symlink_l(entry, tar->entry_linkpath.s,
+ archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv,
+ "Linkname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+ break;
+ case '3': /* Character device */
+ archive_entry_set_filetype(entry, AE_IFCHR);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ break;
+ case '4': /* Block device */
+ archive_entry_set_filetype(entry, AE_IFBLK);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ break;
+ case '5': /* Dir */
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ break;
+ case '6': /* FIFO device */
+ archive_entry_set_filetype(entry, AE_IFIFO);
+ archive_entry_set_size(entry, 0);
+ tar->entry_bytes_remaining = 0;
+ break;
+ case 'D': /* GNU incremental directory type */
+ /*
+ * No special handling is actually required here.
+ * It might be nice someday to preprocess the file list and
+ * provide it to the client, though.
+ */
+ archive_entry_set_filetype(entry, AE_IFDIR);
+ break;
+ case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/
+ /*
+ * As far as I can tell, this is just like a regular file
+ * entry, except that the contents should be _appended_ to
+ * the indicated file at the indicated offset. This may
+ * require some API work to fully support.
+ */
+ break;
+ case 'N': /* Old GNU "long filename" entry. */
+ /* The body of this entry is a script for renaming
+ * previously-extracted entries. Ugh. It will never
+ * be supported by libarchive. */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ break;
+ case 'S': /* GNU sparse files */
+ /*
+ * Sparse files are really just regular files with
+ * sparse information in the extended area.
+ */
+ /* FALLTHROUGH */
+ case '0':
+ /*
+ * Enable sparse file "read" support only for regular
+ * files and explicit GNU sparse files. However, we
+ * don't allow non-standard file types to be sparse.
+ */
+ tar->sparse_allowed = 1;
+ /* FALLTHROUGH */
+ default: /* Regular file and non-standard types */
+ /*
+ * Per POSIX: non-recognized types should always be
+ * treated as regular files.
+ */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ break;
+ }
+ return (err);
+}
+
+/*
+ * Parse out header elements for "old-style" tar archives.
+ */
+static int
+header_old_tar(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h)
+{
+ const struct archive_entry_header_ustar *header;
+ int err = ARCHIVE_OK, err2;
+
+ /* Copy filename over (to ensure null termination). */
+ header = (const struct archive_entry_header_ustar *)h;
+ if (archive_entry_copy_pathname_l(entry,
+ header->name, sizeof(header->name), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Grab rest of common fields */
+ err2 = header_common(a, tar, entry, h);
+ if (err > err2)
+ err = err2;
+
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+ return (err);
+}
+
+/*
+ * Read a Mac AppleDouble-encoded blob of file metadata,
+ * if there is one.
+ */
+static int
+read_mac_metadata_blob(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int64_t size;
+ size_t msize;
+ const void *data;
+ const char *p, *name;
+ const wchar_t *wp, *wname;
+
+ (void)h; /* UNUSED */
+
+ wname = wp = archive_entry_pathname_w(entry);
+ if (wp != NULL) {
+ /* Find the last path element. */
+ for (; *wp != L'\0'; ++wp) {
+ if (wp[0] == '/' && wp[1] != L'\0')
+ wname = wp + 1;
+ }
+ /*
+ * If last path element starts with "._", then
+ * this is a Mac extension.
+ */
+ if (wname[0] != L'.' || wname[1] != L'_' || wname[2] == L'\0')
+ return ARCHIVE_OK;
+ } else {
+ /* Find the last path element. */
+ name = p = archive_entry_pathname(entry);
+ if (p == NULL)
+ return (ARCHIVE_FAILED);
+ for (; *p != '\0'; ++p) {
+ if (p[0] == '/' && p[1] != '\0')
+ name = p + 1;
+ }
+ /*
+ * If last path element starts with "._", then
+ * this is a Mac extension.
+ */
+ if (name[0] != '.' || name[1] != '_' || name[2] == '\0')
+ return ARCHIVE_OK;
+ }
+
+ /* Read the body as a Mac OS metadata blob. */
+ size = archive_entry_size(entry);
+ msize = (size_t)size;
+ if (size < 0 || (uintmax_t)msize != (uintmax_t)size) {
+ *unconsumed = 0;
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * TODO: Look beyond the body here to peek at the next header.
+ * If it's a regular header (not an extension header)
+ * that has the wrong name, just return the current
+ * entry as-is, without consuming the body here.
+ * That would reduce the risk of us mis-identifying
+ * an ordinary file that just happened to have
+ * a name starting with "._".
+ *
+ * Q: Is the above idea really possible? Even
+ * when there are GNU or pax extension entries?
+ */
+ data = __archive_read_ahead(a, msize, NULL);
+ if (data == NULL) {
+ *unconsumed = 0;
+ return (ARCHIVE_FATAL);
+ }
+ archive_entry_copy_mac_metadata(entry, data, msize);
+ *unconsumed = (msize + 511) & ~ 511;
+ tar_flush_unconsumed(a, unconsumed);
+ return (tar_read_header(a, tar, entry, unconsumed));
+}
+
+/*
+ * Parse a file header for a pax extended archive entry.
+ */
+static int
+header_pax_global(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int err;
+
+ err = read_body_to_string(a, tar, &(tar->pax_global), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+ err = tar_read_header(a, tar, entry, unconsumed);
+ return (err);
+}
+
+static int
+header_pax_extensions(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ int err, err2;
+
+ err = read_body_to_string(a, tar, &(tar->pax_header), h, unconsumed);
+ if (err != ARCHIVE_OK)
+ return (err);
+
+ /* Parse the next header. */
+ err = tar_read_header(a, tar, entry, unconsumed);
+ if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+ return (err);
+
+ /*
+ * TODO: Parse global/default options into 'entry' struct here
+ * before handling file-specific options.
+ *
+ * This design (parse standard header, then overwrite with pax
+ * extended attribute data) usually works well, but isn't ideal;
+ * it would be better to parse the pax extended attributes first
+ * and then skip any fields in the standard header that were
+ * defined in the pax header.
+ */
+ err2 = pax_header(a, tar, entry, &tar->pax_header);
+ err = err_combine(err, err2);
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+ return (err);
+}
+
+
+/*
+ * Parse a file header for a Posix "ustar" archive entry. This also
+ * handles "pax" or "extended ustar" entries.
+ */
+static int
+header_ustar(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h)
+{
+ const struct archive_entry_header_ustar *header;
+ struct archive_string *as;
+ int err = ARCHIVE_OK, r;
+
+ header = (const struct archive_entry_header_ustar *)h;
+
+ /* Copy name into an internal buffer to ensure null-termination. */
+ as = &(tar->entry_pathname);
+ if (header->prefix[0]) {
+ archive_strncpy(as, header->prefix, sizeof(header->prefix));
+ if (as->s[archive_strlen(as) - 1] != '/')
+ archive_strappend_char(as, '/');
+ archive_strncat(as, header->name, sizeof(header->name));
+ } else {
+ archive_strncpy(as, header->name, sizeof(header->name));
+ }
+ if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as),
+ tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Handle rest of common fields. */
+ r = header_common(a, tar, entry, h);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ if (r < err)
+ err = r;
+
+ /* Handle POSIX ustar fields. */
+ if (archive_entry_copy_uname_l(entry,
+ header->uname, sizeof(header->uname), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Uname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ if (archive_entry_copy_gname_l(entry,
+ header->gname, sizeof(header->gname), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Gname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Parse out device numbers only for char and block specials. */
+ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
+ archive_entry_set_rdevmajor(entry, (dev_t)
+ tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
+ archive_entry_set_rdevminor(entry, (dev_t)
+ tar_atol(header->rdevminor, sizeof(header->rdevminor)));
+ }
+
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+
+ return (err);
+}
+
+
+/*
+ * Parse the pax extended attributes record.
+ *
+ * Returns non-zero if there's an error in the data.
+ */
+static int
+pax_header(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, struct archive_string *in_as)
+{
+ size_t attr_length, l, line_length, value_length;
+ char *p;
+ char *key, *value;
+ struct archive_string *as;
+ struct archive_string_conv *sconv;
+ int err, err2;
+ char *attr = in_as->s;
+
+ attr_length = in_as->length;
+ tar->pax_hdrcharset_binary = 0;
+ archive_string_empty(&(tar->entry_gname));
+ archive_string_empty(&(tar->entry_linkpath));
+ archive_string_empty(&(tar->entry_pathname));
+ archive_string_empty(&(tar->entry_pathname_override));
+ archive_string_empty(&(tar->entry_uname));
+ err = ARCHIVE_OK;
+ while (attr_length > 0) {
+ /* Parse decimal length field at start of line. */
+ line_length = 0;
+ l = attr_length;
+ p = attr; /* Record start of line. */
+ while (l>0) {
+ if (*p == ' ') {
+ p++;
+ l--;
+ break;
+ }
+ if (*p < '0' || *p > '9') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring malformed pax extended attributes");
+ return (ARCHIVE_WARN);
+ }
+ line_length *= 10;
+ line_length += *p - '0';
+ if (line_length > 999999) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Rejecting pax extended attribute > 1MB");
+ return (ARCHIVE_WARN);
+ }
+ p++;
+ l--;
+ }
+
+ /*
+ * Parsed length must be no bigger than available data,
+ * at least 1, and the last character of the line must
+ * be '\n'.
+ */
+ if (line_length > attr_length
+ || line_length < 1
+ || attr[line_length - 1] != '\n')
+ {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignoring malformed pax extended attribute");
+ return (ARCHIVE_WARN);
+ }
+
+ /* Null-terminate the line. */
+ attr[line_length - 1] = '\0';
+
+ /* Find end of key and null terminate it. */
+ key = p;
+ if (key[0] == '=')
+ return (-1);
+ while (*p && *p != '=')
+ ++p;
+ if (*p == '\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid pax extended attributes");
+ return (ARCHIVE_WARN);
+ }
+ *p = '\0';
+
+ value = p + 1;
+
+ /* Some values may be binary data */
+ value_length = attr + line_length - 1 - value;
+
+ /* Identify this attribute and set it in the entry. */
+ err2 = pax_attribute(a, tar, entry, key, value, value_length);
+ if (err2 == ARCHIVE_FATAL)
+ return (err2);
+ err = err_combine(err, err2);
+
+ /* Skip to next line */
+ attr += line_length;
+ attr_length -= line_length;
+ }
+
+ /*
+ * PAX format uses UTF-8 as default charset for its metadata
+ * unless hdrcharset=BINARY is present in its header.
+ * We apply the charset specified by the hdrcharset option only
+ * when the hdrcharset attribute(in PAX header) is BINARY because
+ * we respect the charset described in PAX header and BINARY also
+ * means that metadata(filename,uname and gname) character-set
+ * is unknown.
+ */
+ if (tar->pax_hdrcharset_binary)
+ sconv = tar->opt_sconv;
+ else {
+ sconv = archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (sconv == NULL)
+ return (ARCHIVE_FATAL);
+ if (tar->compat_2x)
+ archive_string_conversion_set_opt(sconv,
+ SCONV_SET_OPT_UTF8_LIBARCHIVE2X);
+ }
+
+ if (archive_strlen(&(tar->entry_gname)) > 0) {
+ if (archive_entry_copy_gname_l(entry, tar->entry_gname.s,
+ archive_strlen(&(tar->entry_gname)), sconv) != 0) {
+ err = set_conversion_failed_error(a, sconv, "Gname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ /* Use a converted an original name. */
+ archive_entry_copy_gname(entry, tar->entry_gname.s);
+ }
+ }
+ if (archive_strlen(&(tar->entry_linkpath)) > 0) {
+ if (archive_entry_copy_link_l(entry, tar->entry_linkpath.s,
+ archive_strlen(&(tar->entry_linkpath)), sconv) != 0) {
+ err = set_conversion_failed_error(a, sconv, "Linkname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ /* Use a converted an original name. */
+ archive_entry_copy_link(entry, tar->entry_linkpath.s);
+ }
+ }
+ /*
+ * Some extensions (such as the GNU sparse file extensions)
+ * deliberately store a synthetic name under the regular 'path'
+ * attribute and the real file name under a different attribute.
+ * Since we're supposed to not care about the order, we
+ * have no choice but to store all of the various filenames
+ * we find and figure it all out afterwards. This is the
+ * figuring out part.
+ */
+ as = NULL;
+ if (archive_strlen(&(tar->entry_pathname_override)) > 0)
+ as = &(tar->entry_pathname_override);
+ else if (archive_strlen(&(tar->entry_pathname)) > 0)
+ as = &(tar->entry_pathname);
+ if (as != NULL) {
+ if (archive_entry_copy_pathname_l(entry, as->s,
+ archive_strlen(as), sconv) != 0) {
+ err = set_conversion_failed_error(a, sconv, "Pathname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ /* Use a converted an original name. */
+ archive_entry_copy_pathname(entry, as->s);
+ }
+ }
+ if (archive_strlen(&(tar->entry_uname)) > 0) {
+ if (archive_entry_copy_uname_l(entry, tar->entry_uname.s,
+ archive_strlen(&(tar->entry_uname)), sconv) != 0) {
+ err = set_conversion_failed_error(a, sconv, "Uname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ /* Use a converted an original name. */
+ archive_entry_copy_uname(entry, tar->entry_uname.s);
+ }
+ }
+ return (err);
+}
+
+static int
+pax_attribute_xattr(struct archive_entry *entry,
+ const char *name, const char *value)
+{
+ char *name_decoded;
+ void *value_decoded;
+ size_t value_len;
+
+ if (strlen(name) < 18 || (memcmp(name, "LIBARCHIVE.xattr.", 17)) != 0)
+ return 3;
+
+ name += 17;
+
+ /* URL-decode name */
+ name_decoded = url_decode(name);
+ if (name_decoded == NULL)
+ return 2;
+
+ /* Base-64 decode value */
+ value_decoded = base64_decode(value, strlen(value), &value_len);
+ if (value_decoded == NULL) {
+ free(name_decoded);
+ return 1;
+ }
+
+ archive_entry_xattr_add_entry(entry, name_decoded,
+ value_decoded, value_len);
+
+ free(name_decoded);
+ free(value_decoded);
+ return 0;
+}
+
+static int
+pax_attribute_schily_xattr(struct archive_entry *entry,
+ const char *name, const char *value, size_t value_length)
+{
+ if (strlen(name) < 14 || (memcmp(name, "SCHILY.xattr.", 13)) != 0)
+ return 1;
+
+ name += 13;
+
+ archive_entry_xattr_add_entry(entry, name, value, value_length);
+
+ return 0;
+}
+
+static int
+pax_attribute_rht_security_selinux(struct archive_entry *entry,
+ const char *value, size_t value_length)
+{
+ archive_entry_xattr_add_entry(entry, "security.selinux",
+ value, value_length);
+
+ return 0;
+}
+
+static int
+pax_attribute_acl(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const char *value, int type)
+{
+ int r;
+ const char* errstr;
+
+ switch (type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ errstr = "SCHILY.acl.access";
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ errstr = "SCHILY.acl.default";
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+ errstr = "SCHILY.acl.ace";
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Unknown ACL type: %d", type);
+ return(ARCHIVE_FATAL);
+ }
+
+ if (tar->sconv_acl == NULL) {
+ tar->sconv_acl =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (tar->sconv_acl == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ r = archive_acl_from_text_l(archive_entry_acl(entry), value, type,
+ tar->sconv_acl);
+ if (r != ARCHIVE_OK) {
+ if (r == ARCHIVE_FATAL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "%s %s", "Can't allocate memory for ",
+ errstr);
+ return (r);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr);
+ }
+ return (r);
+}
+
+/*
+ * Parse a single key=value attribute. key/value pointers are
+ * assumed to point into reasonably long-lived storage.
+ *
+ * Note that POSIX reserves all-lowercase keywords. Vendor-specific
+ * extensions should always have keywords of the form "VENDOR.attribute"
+ * In particular, it's quite feasible to support many different
+ * vendor extensions here. I'm using "LIBARCHIVE" for extensions
+ * unique to this library.
+ *
+ * Investigate other vendor-specific extensions and see if
+ * any of them look useful.
+ */
+static int
+pax_attribute(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const char *key, const char *value, size_t value_length)
+{
+ int64_t s;
+ long n;
+ int err = ARCHIVE_OK, r;
+
+ if (value == NULL)
+ value = ""; /* Disable compiler warning; do not pass
+ * NULL pointer to strlen(). */
+ switch (key[0]) {
+ case 'G':
+ /* Reject GNU.sparse.* headers on non-regular files. */
+ if (strncmp(key, "GNU.sparse", 10) == 0 &&
+ !tar->sparse_allowed) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Non-regular file cannot be sparse");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* GNU "0.0" sparse pax format. */
+ if (strcmp(key, "GNU.sparse.numblocks") == 0) {
+ tar->sparse_offset = -1;
+ tar->sparse_numbytes = -1;
+ tar->sparse_gnu_major = 0;
+ tar->sparse_gnu_minor = 0;
+ }
+ if (strcmp(key, "GNU.sparse.offset") == 0) {
+ tar->sparse_offset = tar_atol10(value, strlen(value));
+ if (tar->sparse_numbytes != -1) {
+ if (gnu_add_sparse_entry(a, tar,
+ tar->sparse_offset, tar->sparse_numbytes)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ tar->sparse_offset = -1;
+ tar->sparse_numbytes = -1;
+ }
+ }
+ if (strcmp(key, "GNU.sparse.numbytes") == 0) {
+ tar->sparse_numbytes = tar_atol10(value, strlen(value));
+ if (tar->sparse_offset != -1) {
+ if (gnu_add_sparse_entry(a, tar,
+ tar->sparse_offset, tar->sparse_numbytes)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ tar->sparse_offset = -1;
+ tar->sparse_numbytes = -1;
+ }
+ }
+ if (strcmp(key, "GNU.sparse.size") == 0) {
+ tar->realsize = tar_atol10(value, strlen(value));
+ archive_entry_set_size(entry, tar->realsize);
+ tar->realsize_override = 1;
+ }
+
+ /* GNU "0.1" sparse pax format. */
+ if (strcmp(key, "GNU.sparse.map") == 0) {
+ tar->sparse_gnu_major = 0;
+ tar->sparse_gnu_minor = 1;
+ if (gnu_sparse_01_parse(a, tar, value) != ARCHIVE_OK)
+ return (ARCHIVE_WARN);
+ }
+
+ /* GNU "1.0" sparse pax format */
+ if (strcmp(key, "GNU.sparse.major") == 0) {
+ tar->sparse_gnu_major = (int)tar_atol10(value, strlen(value));
+ tar->sparse_gnu_pending = 1;
+ }
+ if (strcmp(key, "GNU.sparse.minor") == 0) {
+ tar->sparse_gnu_minor = (int)tar_atol10(value, strlen(value));
+ tar->sparse_gnu_pending = 1;
+ }
+ if (strcmp(key, "GNU.sparse.name") == 0) {
+ /*
+ * The real filename; when storing sparse
+ * files, GNU tar puts a synthesized name into
+ * the regular 'path' attribute in an attempt
+ * to limit confusion. ;-)
+ */
+ archive_strcpy(&(tar->entry_pathname_override), value);
+ }
+ if (strcmp(key, "GNU.sparse.realsize") == 0) {
+ tar->realsize = tar_atol10(value, strlen(value));
+ archive_entry_set_size(entry, tar->realsize);
+ tar->realsize_override = 1;
+ }
+ break;
+ case 'L':
+ /* Our extensions */
+/* TODO: Handle arbitrary extended attributes... */
+/*
+ if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0)
+ archive_entry_set_xxxxxx(entry, value);
+*/
+ if (strcmp(key, "LIBARCHIVE.creationtime") == 0) {
+ pax_time(value, &s, &n);
+ archive_entry_set_birthtime(entry, s, n);
+ }
+ if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) {
+ if (strcmp(value, "file") == 0) {
+ archive_entry_set_symlink_type(entry,
+ AE_SYMLINK_TYPE_FILE);
+ } else if (strcmp(value, "dir") == 0) {
+ archive_entry_set_symlink_type(entry,
+ AE_SYMLINK_TYPE_DIRECTORY);
+ }
+ }
+ if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0)
+ pax_attribute_xattr(entry, key, value);
+ break;
+ case 'R':
+ /* GNU tar uses RHT.security header to store SELinux xattrs
+ * SCHILY.xattr.security.selinux == RHT.security.selinux */
+ if (strcmp(key, "RHT.security.selinux") == 0) {
+ pax_attribute_rht_security_selinux(entry, value,
+ value_length);
+ }
+ break;
+ case 'S':
+ /* We support some keys used by the "star" archiver */
+ if (strcmp(key, "SCHILY.acl.access") == 0) {
+ r = pax_attribute_acl(a, tar, entry, value,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ } else if (strcmp(key, "SCHILY.acl.default") == 0) {
+ r = pax_attribute_acl(a, tar, entry, value,
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ } else if (strcmp(key, "SCHILY.acl.ace") == 0) {
+ r = pax_attribute_acl(a, tar, entry, value,
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ } else if (strcmp(key, "SCHILY.devmajor") == 0) {
+ archive_entry_set_rdevmajor(entry,
+ (dev_t)tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.devminor") == 0) {
+ archive_entry_set_rdevminor(entry,
+ (dev_t)tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.fflags") == 0) {
+ archive_entry_copy_fflags_text(entry, value);
+ } else if (strcmp(key, "SCHILY.dev") == 0) {
+ archive_entry_set_dev(entry,
+ (dev_t)tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.ino") == 0) {
+ archive_entry_set_ino(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.nlink") == 0) {
+ archive_entry_set_nlink(entry, (unsigned)
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "SCHILY.realsize") == 0) {
+ tar->realsize = tar_atol10(value, strlen(value));
+ tar->realsize_override = 1;
+ archive_entry_set_size(entry, tar->realsize);
+ } else if (strncmp(key, "SCHILY.xattr.", 13) == 0) {
+ pax_attribute_schily_xattr(entry, key, value,
+ value_length);
+ } else if (strcmp(key, "SUN.holesdata") == 0) {
+ /* A Solaris extension for sparse. */
+ r = solaris_sparse_parse(a, tar, entry, value);
+ if (r < err) {
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ err = r;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Parse error: SUN.holesdata");
+ }
+ }
+ break;
+ case 'a':
+ if (strcmp(key, "atime") == 0) {
+ pax_time(value, &s, &n);
+ archive_entry_set_atime(entry, s, n);
+ }
+ break;
+ case 'c':
+ if (strcmp(key, "ctime") == 0) {
+ pax_time(value, &s, &n);
+ archive_entry_set_ctime(entry, s, n);
+ } else if (strcmp(key, "charset") == 0) {
+ /* TODO: Publish charset information in entry. */
+ } else if (strcmp(key, "comment") == 0) {
+ /* TODO: Publish comment in entry. */
+ }
+ break;
+ case 'g':
+ if (strcmp(key, "gid") == 0) {
+ archive_entry_set_gid(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "gname") == 0) {
+ archive_strcpy(&(tar->entry_gname), value);
+ }
+ break;
+ case 'h':
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (strcmp(value, "BINARY") == 0)
+ /* Binary mode. */
+ tar->pax_hdrcharset_binary = 1;
+ else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0)
+ tar->pax_hdrcharset_binary = 0;
+ }
+ break;
+ case 'l':
+ /* pax interchange doesn't distinguish hardlink vs. symlink. */
+ if (strcmp(key, "linkpath") == 0) {
+ archive_strcpy(&(tar->entry_linkpath), value);
+ }
+ break;
+ case 'm':
+ if (strcmp(key, "mtime") == 0) {
+ pax_time(value, &s, &n);
+ archive_entry_set_mtime(entry, s, n);
+ }
+ break;
+ case 'p':
+ if (strcmp(key, "path") == 0) {
+ archive_strcpy(&(tar->entry_pathname), value);
+ }
+ break;
+ case 'r':
+ /* POSIX has reserved 'realtime.*' */
+ break;
+ case 's':
+ /* POSIX has reserved 'security.*' */
+ /* Someday: if (strcmp(key, "security.acl") == 0) { ... } */
+ if (strcmp(key, "size") == 0) {
+ /* "size" is the size of the data in the entry. */
+ tar->entry_bytes_remaining
+ = tar_atol10(value, strlen(value));
+ if (tar->entry_bytes_remaining < 0) {
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Tar size attribute is negative");
+ return (ARCHIVE_FATAL);
+ }
+ if (tar->entry_bytes_remaining == INT64_MAX) {
+ /* Note: tar_atol returns INT64_MAX on overflow */
+ tar->entry_bytes_remaining = 0;
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Tar size attribute overflow");
+ return (ARCHIVE_FATAL);
+ }
+ /*
+ * The "size" pax header keyword always overrides the
+ * "size" field in the tar header.
+ * GNU.sparse.realsize, GNU.sparse.size and
+ * SCHILY.realsize override this value.
+ */
+ if (!tar->realsize_override) {
+ archive_entry_set_size(entry,
+ tar->entry_bytes_remaining);
+ tar->realsize
+ = tar->entry_bytes_remaining;
+ }
+ }
+ break;
+ case 'u':
+ if (strcmp(key, "uid") == 0) {
+ archive_entry_set_uid(entry,
+ tar_atol10(value, strlen(value)));
+ } else if (strcmp(key, "uname") == 0) {
+ archive_strcpy(&(tar->entry_uname), value);
+ }
+ break;
+ }
+ return (err);
+}
+
+
+
+/*
+ * parse a decimal time value, which may include a fractional portion
+ */
+static void
+pax_time(const char *p, int64_t *ps, long *pn)
+{
+ char digit;
+ int64_t s;
+ unsigned long l;
+ int sign;
+ int64_t limit, last_digit_limit;
+
+ limit = INT64_MAX / 10;
+ last_digit_limit = INT64_MAX % 10;
+
+ s = 0;
+ sign = 1;
+ if (*p == '-') {
+ sign = -1;
+ p++;
+ }
+ while (*p >= '0' && *p <= '9') {
+ digit = *p - '0';
+ if (s > limit ||
+ (s == limit && digit > last_digit_limit)) {
+ s = INT64_MAX;
+ break;
+ }
+ s = (s * 10) + digit;
+ ++p;
+ }
+
+ *ps = s * sign;
+
+ /* Calculate nanoseconds. */
+ *pn = 0;
+
+ if (*p != '.')
+ return;
+
+ l = 100000000UL;
+ do {
+ ++p;
+ if (*p >= '0' && *p <= '9')
+ *pn += (*p - '0') * l;
+ else
+ break;
+ } while (l /= 10);
+}
+
+/*
+ * Parse GNU tar header
+ */
+static int
+header_gnutar(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+ const struct archive_entry_header_gnutar *header;
+ int64_t t;
+ int err = ARCHIVE_OK;
+
+ /*
+ * GNU header is like POSIX ustar, except 'prefix' is
+ * replaced with some other fields. This also means the
+ * filename is stored as in old-style archives.
+ */
+
+ /* Grab fields common to all tar variants. */
+ err = header_common(a, tar, entry, h);
+ if (err == ARCHIVE_FATAL)
+ return (err);
+
+ /* Copy filename over (to ensure null termination). */
+ header = (const struct archive_entry_header_gnutar *)h;
+ if (archive_entry_copy_pathname_l(entry,
+ header->name, sizeof(header->name), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Fields common to ustar and GNU */
+ /* XXX Can the following be factored out since it's common
+ * to ustar and gnu tar? Is it okay to move it down into
+ * header_common, perhaps? */
+ if (archive_entry_copy_uname_l(entry,
+ header->uname, sizeof(header->uname), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Uname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ if (archive_entry_copy_gname_l(entry,
+ header->gname, sizeof(header->gname), tar->sconv) != 0) {
+ err = set_conversion_failed_error(a, tar->sconv, "Gname");
+ if (err == ARCHIVE_FATAL)
+ return (err);
+ }
+
+ /* Parse out device numbers only for char and block specials */
+ if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
+ archive_entry_set_rdevmajor(entry, (dev_t)
+ tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
+ archive_entry_set_rdevminor(entry, (dev_t)
+ tar_atol(header->rdevminor, sizeof(header->rdevminor)));
+ } else
+ archive_entry_set_rdev(entry, 0);
+
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+
+ /* Grab GNU-specific fields. */
+ t = tar_atol(header->atime, sizeof(header->atime));
+ if (t > 0)
+ archive_entry_set_atime(entry, t, 0);
+ t = tar_atol(header->ctime, sizeof(header->ctime));
+ if (t > 0)
+ archive_entry_set_ctime(entry, t, 0);
+
+ if (header->realsize[0] != 0) {
+ tar->realsize
+ = tar_atol(header->realsize, sizeof(header->realsize));
+ archive_entry_set_size(entry, tar->realsize);
+ tar->realsize_override = 1;
+ }
+
+ if (header->sparse[0].offset[0] != 0) {
+ if (gnu_sparse_old_read(a, tar, header, unconsumed)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ if (header->isextended[0] != 0) {
+ /* XXX WTF? XXX */
+ }
+ }
+
+ return (err);
+}
+
+static int
+gnu_add_sparse_entry(struct archive_read *a, struct tar *tar,
+ int64_t offset, int64_t remaining)
+{
+ struct sparse_block *p;
+
+ p = (struct sparse_block *)calloc(1, sizeof(*p));
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (tar->sparse_last != NULL)
+ tar->sparse_last->next = p;
+ else
+ tar->sparse_list = p;
+ tar->sparse_last = p;
+ if (remaining < 0 || offset < 0 || offset > INT64_MAX - remaining) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data");
+ return (ARCHIVE_FATAL);
+ }
+ p->offset = offset;
+ p->remaining = remaining;
+ return (ARCHIVE_OK);
+}
+
+static void
+gnu_clear_sparse_list(struct tar *tar)
+{
+ struct sparse_block *p;
+
+ while (tar->sparse_list != NULL) {
+ p = tar->sparse_list;
+ tar->sparse_list = p->next;
+ free(p);
+ }
+ tar->sparse_last = NULL;
+}
+
+/*
+ * GNU tar old-format sparse data.
+ *
+ * GNU old-format sparse data is stored in a fixed-field
+ * format. Offset/size values are 11-byte octal fields (same
+ * format as 'size' field in ustart header). These are
+ * stored in the header, allocating subsequent header blocks
+ * as needed. Extending the header in this way is a pretty
+ * severe POSIX violation; this design has earned GNU tar a
+ * lot of criticism.
+ */
+
+static int
+gnu_sparse_old_read(struct archive_read *a, struct tar *tar,
+ const struct archive_entry_header_gnutar *header, size_t *unconsumed)
+{
+ ssize_t bytes_read;
+ const void *data;
+ struct extended {
+ struct gnu_sparse sparse[21];
+ char isextended[1];
+ char padding[7];
+ };
+ const struct extended *ext;
+
+ if (gnu_sparse_old_parse(a, tar, header->sparse, 4) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (header->isextended[0] == 0)
+ return (ARCHIVE_OK);
+
+ do {
+ tar_flush_unconsumed(a, unconsumed);
+ data = __archive_read_ahead(a, 512, &bytes_read);
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ if (bytes_read < 512) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated tar archive "
+ "detected while reading sparse file data");
+ return (ARCHIVE_FATAL);
+ }
+ *unconsumed = 512;
+ ext = (const struct extended *)data;
+ if (gnu_sparse_old_parse(a, tar, ext->sparse, 21) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } while (ext->isextended[0] != 0);
+ if (tar->sparse_list != NULL)
+ tar->entry_offset = tar->sparse_list->offset;
+ return (ARCHIVE_OK);
+}
+
+static int
+gnu_sparse_old_parse(struct archive_read *a, struct tar *tar,
+ const struct gnu_sparse *sparse, int length)
+{
+ while (length > 0 && sparse->offset[0] != 0) {
+ if (gnu_add_sparse_entry(a, tar,
+ tar_atol(sparse->offset, sizeof(sparse->offset)),
+ tar_atol(sparse->numbytes, sizeof(sparse->numbytes)))
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ sparse++;
+ length--;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * GNU tar sparse format 0.0
+ *
+ * Beginning with GNU tar 1.15, sparse files are stored using
+ * information in the pax extended header. The GNU tar maintainers
+ * have gone through a number of variations in the process of working
+ * out this scheme; fortunately, they're all numbered.
+ *
+ * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the
+ * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to
+ * store offset/size for each block. The repeated instances of these
+ * latter fields violate the pax specification (which frowns on
+ * duplicate keys), so this format was quickly replaced.
+ */
+
+/*
+ * GNU tar sparse format 0.1
+ *
+ * This version replaced the offset/numbytes attributes with
+ * a single "map" attribute that stored a list of integers. This
+ * format had two problems: First, the "map" attribute could be very
+ * long, which caused problems for some implementations. More
+ * importantly, the sparse data was lost when extracted by archivers
+ * that didn't recognize this extension.
+ */
+
+static int
+gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p)
+{
+ const char *e;
+ int64_t offset = -1, size = -1;
+
+ for (;;) {
+ e = p;
+ while (*e != '\0' && *e != ',') {
+ if (*e < '0' || *e > '9')
+ return (ARCHIVE_WARN);
+ e++;
+ }
+ if (offset < 0) {
+ offset = tar_atol10(p, e - p);
+ if (offset < 0)
+ return (ARCHIVE_WARN);
+ } else {
+ size = tar_atol10(p, e - p);
+ if (size < 0)
+ return (ARCHIVE_WARN);
+ if (gnu_add_sparse_entry(a, tar, offset, size)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ offset = -1;
+ }
+ if (*e == '\0')
+ return (ARCHIVE_OK);
+ p = e + 1;
+ }
+}
+
+/*
+ * GNU tar sparse format 1.0
+ *
+ * The idea: The offset/size data is stored as a series of base-10
+ * ASCII numbers prepended to the file data, so that dearchivers that
+ * don't support this format will extract the block map along with the
+ * data and a separate post-process can restore the sparseness.
+ *
+ * Unfortunately, GNU tar 1.16 had a bug that added unnecessary
+ * padding to the body of the file when using this format. GNU tar
+ * 1.17 corrected this bug without bumping the version number, so
+ * it's not possible to support both variants. This code supports
+ * the later variant at the expense of not supporting the former.
+ *
+ * This variant also replaced GNU.sparse.size with GNU.sparse.realsize
+ * and introduced the GNU.sparse.major/GNU.sparse.minor attributes.
+ */
+
+/*
+ * Read the next line from the input, and parse it as a decimal
+ * integer followed by '\n'. Returns positive integer value or
+ * negative on error.
+ */
+static int64_t
+gnu_sparse_10_atol(struct archive_read *a, struct tar *tar,
+ int64_t *remaining, size_t *unconsumed)
+{
+ int64_t l, limit, last_digit_limit;
+ const char *p;
+ ssize_t bytes_read;
+ int base, digit;
+
+ base = 10;
+ limit = INT64_MAX / base;
+ last_digit_limit = INT64_MAX % base;
+
+ /*
+ * Skip any lines starting with '#'; GNU tar specs
+ * don't require this, but they should.
+ */
+ do {
+ bytes_read = readline(a, tar, &p,
+ (ssize_t)tar_min(*remaining, 100), unconsumed);
+ if (bytes_read <= 0)
+ return (ARCHIVE_FATAL);
+ *remaining -= bytes_read;
+ } while (p[0] == '#');
+
+ l = 0;
+ while (bytes_read > 0) {
+ if (*p == '\n')
+ return (l);
+ if (*p < '0' || *p >= '0' + base)
+ return (ARCHIVE_WARN);
+ digit = *p - '0';
+ if (l > limit || (l == limit && digit > last_digit_limit))
+ l = INT64_MAX; /* Truncate on overflow. */
+ else
+ l = (l * base) + digit;
+ p++;
+ bytes_read--;
+ }
+ /* TODO: Error message. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Returns length (in bytes) of the sparse data description
+ * that was read.
+ */
+static ssize_t
+gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed)
+{
+ ssize_t bytes_read;
+ int entries;
+ int64_t offset, size, to_skip, remaining;
+
+ /* Clear out the existing sparse list. */
+ gnu_clear_sparse_list(tar);
+
+ remaining = tar->entry_bytes_remaining;
+
+ /* Parse entries. */
+ entries = (int)gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+ if (entries < 0)
+ return (ARCHIVE_FATAL);
+ /* Parse the individual entries. */
+ while (entries-- > 0) {
+ /* Parse offset/size */
+ offset = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+ if (offset < 0)
+ return (ARCHIVE_FATAL);
+ size = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+ if (size < 0)
+ return (ARCHIVE_FATAL);
+ /* Add a new sparse entry. */
+ if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ /* Skip rest of block... */
+ tar_flush_unconsumed(a, unconsumed);
+ bytes_read = (ssize_t)(tar->entry_bytes_remaining - remaining);
+ to_skip = 0x1ff & -bytes_read;
+ /* Fail if tar->entry_bytes_remaing would get negative */
+ if (to_skip > remaining)
+ return (ARCHIVE_FATAL);
+ if (to_skip != __archive_read_consume(a, to_skip))
+ return (ARCHIVE_FATAL);
+ return ((ssize_t)(bytes_read + to_skip));
+}
+
+/*
+ * Solaris pax extension for a sparse file. This is recorded with the
+ * data and hole pairs. The way recording sparse information by Solaris'
+ * pax simply indicates where data and sparse are, so the stored contents
+ * consist of both data and hole.
+ */
+static int
+solaris_sparse_parse(struct archive_read *a, struct tar *tar,
+ struct archive_entry *entry, const char *p)
+{
+ const char *e;
+ int64_t start, end;
+ int hole = 1;
+
+ (void)entry; /* UNUSED */
+
+ end = 0;
+ if (*p == ' ')
+ p++;
+ else
+ return (ARCHIVE_WARN);
+ for (;;) {
+ e = p;
+ while (*e != '\0' && *e != ' ') {
+ if (*e < '0' || *e > '9')
+ return (ARCHIVE_WARN);
+ e++;
+ }
+ start = end;
+ end = tar_atol10(p, e - p);
+ if (end < 0)
+ return (ARCHIVE_WARN);
+ if (start < end) {
+ if (gnu_add_sparse_entry(a, tar, start,
+ end - start) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ tar->sparse_last->hole = hole;
+ }
+ if (*e == '\0')
+ return (ARCHIVE_OK);
+ p = e + 1;
+ hole = hole == 0;
+ }
+}
+
+/*-
+ * Convert text->integer.
+ *
+ * Traditional tar formats (including POSIX) specify base-8 for
+ * all of the standard numeric fields. This is a significant limitation
+ * in practice:
+ * = file size is limited to 8GB
+ * = rdevmajor and rdevminor are limited to 21 bits
+ * = uid/gid are limited to 21 bits
+ *
+ * There are two workarounds for this:
+ * = pax extended headers, which use variable-length string fields
+ * = GNU tar and STAR both allow either base-8 or base-256 in
+ * most fields. The high bit is set to indicate base-256.
+ *
+ * On read, this implementation supports both extensions.
+ */
+static int64_t
+tar_atol(const char *p, size_t char_cnt)
+{
+ /*
+ * Technically, GNU tar considers a field to be in base-256
+ * only if the first byte is 0xff or 0x80.
+ */
+ if (*p & 0x80)
+ return (tar_atol256(p, char_cnt));
+ return (tar_atol8(p, char_cnt));
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+tar_atol_base_n(const char *p, size_t char_cnt, int base)
+{
+ int64_t l, maxval, limit, last_digit_limit;
+ int digit, sign;
+
+ maxval = INT64_MAX;
+ limit = INT64_MAX / base;
+ last_digit_limit = INT64_MAX % base;
+
+ /* the pointer will not be dereferenced if char_cnt is zero
+ * due to the way the && operator is evaluated.
+ */
+ while (char_cnt != 0 && (*p == ' ' || *p == '\t')) {
+ p++;
+ char_cnt--;
+ }
+
+ sign = 1;
+ if (char_cnt != 0 && *p == '-') {
+ sign = -1;
+ p++;
+ char_cnt--;
+
+ maxval = INT64_MIN;
+ limit = -(INT64_MIN / base);
+ last_digit_limit = -(INT64_MIN % base);
+ }
+
+ l = 0;
+ if (char_cnt != 0) {
+ digit = *p - '0';
+ while (digit >= 0 && digit < base && char_cnt != 0) {
+ if (l>limit || (l == limit && digit >= last_digit_limit)) {
+ return maxval; /* Truncate on overflow. */
+ }
+ l = (l * base) + digit;
+ digit = *++p - '0';
+ char_cnt--;
+ }
+ }
+ return (sign < 0) ? -l : l;
+}
+
+static int64_t
+tar_atol8(const char *p, size_t char_cnt)
+{
+ return tar_atol_base_n(p, char_cnt, 8);
+}
+
+static int64_t
+tar_atol10(const char *p, size_t char_cnt)
+{
+ return tar_atol_base_n(p, char_cnt, 10);
+}
+
+/*
+ * Parse a base-256 integer. This is just a variable-length
+ * twos-complement signed binary value in big-endian order, except
+ * that the high-order bit is ignored. The values here can be up to
+ * 12 bytes, so we need to be careful about overflowing 64-bit
+ * (8-byte) integers.
+ *
+ * This code unashamedly assumes that the local machine uses 8-bit
+ * bytes and twos-complement arithmetic.
+ */
+static int64_t
+tar_atol256(const char *_p, size_t char_cnt)
+{
+ uint64_t l;
+ const unsigned char *p = (const unsigned char *)_p;
+ unsigned char c, neg;
+
+ /* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */
+ c = *p;
+ if (c & 0x40) {
+ neg = 0xff;
+ c |= 0x80;
+ l = ~ARCHIVE_LITERAL_ULL(0);
+ } else {
+ neg = 0;
+ c &= 0x7f;
+ l = 0;
+ }
+
+ /* If more than 8 bytes, check that we can ignore
+ * high-order bits without overflow. */
+ while (char_cnt > sizeof(int64_t)) {
+ --char_cnt;
+ if (c != neg)
+ return neg ? INT64_MIN : INT64_MAX;
+ c = *++p;
+ }
+
+ /* c is first byte that fits; if sign mismatch, return overflow */
+ if ((c ^ neg) & 0x80) {
+ return neg ? INT64_MIN : INT64_MAX;
+ }
+
+ /* Accumulate remaining bytes. */
+ while (--char_cnt > 0) {
+ l = (l << 8) | c;
+ c = *++p;
+ }
+ l = (l << 8) | c;
+ /* Return signed twos-complement value. */
+ return (int64_t)(l);
+}
+
+/*
+ * Returns length of line (including trailing newline)
+ * or negative on error. 'start' argument is updated to
+ * point to first character of line. This avoids copying
+ * when possible.
+ */
+static ssize_t
+readline(struct archive_read *a, struct tar *tar, const char **start,
+ ssize_t limit, size_t *unconsumed)
+{
+ ssize_t bytes_read;
+ ssize_t total_size = 0;
+ const void *t;
+ const char *s;
+ void *p;
+
+ tar_flush_unconsumed(a, unconsumed);
+
+ t = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read <= 0)
+ return (ARCHIVE_FATAL);
+ s = t; /* Start of line? */
+ p = memchr(t, '\n', bytes_read);
+ /* If we found '\n' in the read buffer, return pointer to that. */
+ if (p != NULL) {
+ bytes_read = 1 + ((const char *)p) - s;
+ if (bytes_read > limit) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Line too long");
+ return (ARCHIVE_FATAL);
+ }
+ *unconsumed = bytes_read;
+ *start = s;
+ return (bytes_read);
+ }
+ *unconsumed = bytes_read;
+ /* Otherwise, we need to accumulate in a line buffer. */
+ for (;;) {
+ if (total_size + bytes_read > limit) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Line too long");
+ return (ARCHIVE_FATAL);
+ }
+ if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate working buffer");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(tar->line.s + total_size, t, bytes_read);
+ tar_flush_unconsumed(a, unconsumed);
+ total_size += bytes_read;
+ /* If we found '\n', clean up and return. */
+ if (p != NULL) {
+ *start = tar->line.s;
+ return (total_size);
+ }
+ /* Read some more. */
+ t = __archive_read_ahead(a, 1, &bytes_read);
+ if (bytes_read <= 0)
+ return (ARCHIVE_FATAL);
+ s = t; /* Start of line? */
+ p = memchr(t, '\n', bytes_read);
+ /* If we found '\n', trim the read. */
+ if (p != NULL) {
+ bytes_read = 1 + ((const char *)p) - s;
+ }
+ *unconsumed = bytes_read;
+ }
+}
+
+/*
+ * base64_decode - Base64 decode
+ *
+ * This accepts most variations of base-64 encoding, including:
+ * * with or without line breaks
+ * * with or without the final group padded with '=' or '_' characters
+ * (The most economical Base-64 variant does not pad the last group and
+ * omits line breaks; RFC1341 used for MIME requires both.)
+ */
+static char *
+base64_decode(const char *s, size_t len, size_t *out_len)
+{
+ static const unsigned char digits[64] = {
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N',
+ 'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b',
+ 'c','d','e','f','g','h','i','j','k','l','m','n','o','p',
+ 'q','r','s','t','u','v','w','x','y','z','0','1','2','3',
+ '4','5','6','7','8','9','+','/' };
+ static unsigned char decode_table[128];
+ char *out, *d;
+ const unsigned char *src = (const unsigned char *)s;
+
+ /* If the decode table is not yet initialized, prepare it. */
+ if (decode_table[digits[1]] != 1) {
+ unsigned i;
+ memset(decode_table, 0xff, sizeof(decode_table));
+ for (i = 0; i < sizeof(digits); i++)
+ decode_table[digits[i]] = i;
+ }
+
+ /* Allocate enough space to hold the entire output. */
+ /* Note that we may not use all of this... */
+ out = (char *)malloc(len - len / 4 + 1);
+ if (out == NULL) {
+ *out_len = 0;
+ return (NULL);
+ }
+ d = out;
+
+ while (len > 0) {
+ /* Collect the next group of (up to) four characters. */
+ int v = 0;
+ int group_size = 0;
+ while (group_size < 4 && len > 0) {
+ /* '=' or '_' padding indicates final group. */
+ if (*src == '=' || *src == '_') {
+ len = 0;
+ break;
+ }
+ /* Skip illegal characters (including line breaks) */
+ if (*src > 127 || *src < 32
+ || decode_table[*src] == 0xff) {
+ len--;
+ src++;
+ continue;
+ }
+ v <<= 6;
+ v |= decode_table[*src++];
+ len --;
+ group_size++;
+ }
+ /* Align a short group properly. */
+ v <<= 6 * (4 - group_size);
+ /* Unpack the group we just collected. */
+ switch (group_size) {
+ case 4: d[2] = v & 0xff;
+ /* FALLTHROUGH */
+ case 3: d[1] = (v >> 8) & 0xff;
+ /* FALLTHROUGH */
+ case 2: d[0] = (v >> 16) & 0xff;
+ break;
+ case 1: /* this is invalid! */
+ break;
+ }
+ d += group_size * 3 / 4;
+ }
+
+ *out_len = d - out;
+ return (out);
+}
+
+static char *
+url_decode(const char *in)
+{
+ char *out, *d;
+ const char *s;
+
+ out = (char *)malloc(strlen(in) + 1);
+ if (out == NULL)
+ return (NULL);
+ for (s = in, d = out; *s != '\0'; ) {
+ if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') {
+ /* Try to convert % escape */
+ int digit1 = tohex(s[1]);
+ int digit2 = tohex(s[2]);
+ if (digit1 >= 0 && digit2 >= 0) {
+ /* Looks good, consume three chars */
+ s += 3;
+ /* Convert output */
+ *d++ = ((digit1 << 4) | digit2);
+ continue;
+ }
+ /* Else fall through and treat '%' as normal char */
+ }
+ *d++ = *s++;
+ }
+ *d = '\0';
+ return (out);
+}
+
+static int
+tohex(int c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else
+ return (-1);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_warc.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_warc.c
new file mode 100644
index 0000000000..61ab29ea14
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_warc.c
@@ -0,0 +1,848 @@
+/*-
+ * Copyright (c) 2014 Sebastian Freundt
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+/**
+ * WARC is standardised by ISO TC46/SC4/WG12 and currently available as
+ * ISO 28500:2009.
+ * For the purposes of this file we used the final draft from:
+ * http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf
+ *
+ * Todo:
+ * [ ] real-world warcs can contain resources at endpoints ending in /
+ * e.g. http://bibnum.bnf.fr/warc/
+ * if you're lucky their response contains a Content-Location: header
+ * pointing to a unix-compliant filename, in the example above it's
+ * Content-Location: http://bibnum.bnf.fr/warc/index.html
+ * however, that's not mandated and github for example doesn't follow
+ * this convention.
+ * We need a set of archive options to control what to do with
+ * entries like these, at the moment care is taken to skip them.
+ *
+ **/
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+typedef enum {
+ WT_NONE,
+ /* warcinfo */
+ WT_INFO,
+ /* metadata */
+ WT_META,
+ /* resource */
+ WT_RSRC,
+ /* request, unsupported */
+ WT_REQ,
+ /* response, unsupported */
+ WT_RSP,
+ /* revisit, unsupported */
+ WT_RVIS,
+ /* conversion, unsupported */
+ WT_CONV,
+ /* continuation, unsupported at the moment */
+ WT_CONT,
+ /* invalid type */
+ LAST_WT
+} warc_type_t;
+
+typedef struct {
+ size_t len;
+ const char *str;
+} warc_string_t;
+
+typedef struct {
+ size_t len;
+ char *str;
+} warc_strbuf_t;
+
+struct warc_s {
+ /* content length ahead */
+ size_t cntlen;
+ /* and how much we've processed so far */
+ size_t cntoff;
+ /* and how much we need to consume between calls */
+ size_t unconsumed;
+
+ /* string pool */
+ warc_strbuf_t pool;
+ /* previous version */
+ unsigned int pver;
+ /* stringified format name */
+ struct archive_string sver;
+};
+
+static int _warc_bid(struct archive_read *a, int);
+static int _warc_cleanup(struct archive_read *a);
+static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*);
+static int _warc_skip(struct archive_read *a);
+static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e);
+
+/* private routines */
+static unsigned int _warc_rdver(const char *buf, size_t bsz);
+static unsigned int _warc_rdtyp(const char *buf, size_t bsz);
+static warc_string_t _warc_rduri(const char *buf, size_t bsz);
+static ssize_t _warc_rdlen(const char *buf, size_t bsz);
+static time_t _warc_rdrtm(const char *buf, size_t bsz);
+static time_t _warc_rdmtm(const char *buf, size_t bsz);
+static const char *_warc_find_eoh(const char *buf, size_t bsz);
+static const char *_warc_find_eol(const char *buf, size_t bsz);
+
+int
+archive_read_support_format_warc(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct warc_s *w;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_warc");
+
+ if ((w = calloc(1, sizeof(*w))) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate warc data");
+ return (ARCHIVE_FATAL);
+ }
+
+ r = __archive_read_register_format(
+ a, w, "warc",
+ _warc_bid, NULL, _warc_rdhdr, _warc_read,
+ _warc_skip, NULL, _warc_cleanup, NULL, NULL);
+
+ if (r != ARCHIVE_OK) {
+ free(w);
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_cleanup(struct archive_read *a)
+{
+ struct warc_s *w = a->format->data;
+
+ if (w->pool.len > 0U) {
+ free(w->pool.str);
+ }
+ archive_string_free(&w->sver);
+ free(w);
+ a->format->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_bid(struct archive_read *a, int best_bid)
+{
+ const char *hdr;
+ ssize_t nrd;
+ unsigned int ver;
+
+ (void)best_bid; /* UNUSED */
+
+ /* check first line of file, it should be a record already */
+ if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) {
+ /* no idea what to do */
+ return -1;
+ } else if (nrd < 12) {
+ /* nah, not for us, our magic cookie is at least 12 bytes */
+ return -1;
+ }
+
+ /* otherwise snarf the record's version number */
+ ver = _warc_rdver(hdr, nrd);
+ if (ver < 1200U || ver > 10000U) {
+ /* we only support WARC 0.12 to 1.0 */
+ return -1;
+ }
+
+ /* otherwise be confident */
+ return (64);
+}
+
+static int
+_warc_rdhdr(struct archive_read *a, struct archive_entry *entry)
+{
+#define HDR_PROBE_LEN (12U)
+ struct warc_s *w = a->format->data;
+ unsigned int ver;
+ const char *buf;
+ ssize_t nrd;
+ const char *eoh;
+ /* for the file name, saves some strndup()'ing */
+ warc_string_t fnam;
+ /* warc record type, not that we really use it a lot */
+ warc_type_t ftyp;
+ /* content-length+error monad */
+ ssize_t cntlen;
+ /* record time is the WARC-Date time we reinterpret it as ctime */
+ time_t rtime;
+ /* mtime is the Last-Modified time which will be the entry's mtime */
+ time_t mtime;
+
+start_over:
+ /* just use read_ahead() they keep track of unconsumed
+ * bits and bobs for us; no need to put an extra shift in
+ * and reproduce that functionality here */
+ buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd);
+
+ if (nrd < 0) {
+ /* no good */
+ archive_set_error(
+ &a->archive, ARCHIVE_ERRNO_MISC,
+ "Bad record header");
+ return (ARCHIVE_FATAL);
+ } else if (buf == NULL) {
+ /* there should be room for at least WARC/bla\r\n
+ * must be EOF therefore */
+ return (ARCHIVE_EOF);
+ }
+ /* looks good so far, try and find the end of the header now */
+ eoh = _warc_find_eoh(buf, nrd);
+ if (eoh == NULL) {
+ /* still no good, the header end might be beyond the
+ * probe we've requested, but then again who'd cram
+ * so much stuff into the header *and* be 28500-compliant */
+ archive_set_error(
+ &a->archive, ARCHIVE_ERRNO_MISC,
+ "Bad record header");
+ return (ARCHIVE_FATAL);
+ }
+ ver = _warc_rdver(buf, eoh - buf);
+ /* we currently support WARC 0.12 to 1.0 */
+ if (ver == 0U) {
+ archive_set_error(
+ &a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid record version");
+ return (ARCHIVE_FATAL);
+ } else if (ver < 1200U || ver > 10000U) {
+ archive_set_error(
+ &a->archive, ARCHIVE_ERRNO_MISC,
+ "Unsupported record version: %u.%u",
+ ver / 10000, (ver % 10000) / 100);
+ return (ARCHIVE_FATAL);
+ }
+ cntlen = _warc_rdlen(buf, eoh - buf);
+ if (cntlen < 0) {
+ /* nightmare! the specs say content-length is mandatory
+ * so I don't feel overly bad stopping the reader here */
+ archive_set_error(
+ &a->archive, EINVAL,
+ "Bad content length");
+ return (ARCHIVE_FATAL);
+ }
+ rtime = _warc_rdrtm(buf, eoh - buf);
+ if (rtime == (time_t)-1) {
+ /* record time is mandatory as per WARC/1.0,
+ * so just barf here, fast and loud */
+ archive_set_error(
+ &a->archive, EINVAL,
+ "Bad record time");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* let the world know we're a WARC archive */
+ a->archive.archive_format = ARCHIVE_FORMAT_WARC;
+ if (ver != w->pver) {
+ /* stringify this entry's version */
+ archive_string_sprintf(&w->sver,
+ "WARC/%u.%u", ver / 10000, (ver % 10000) / 100);
+ /* remember the version */
+ w->pver = ver;
+ }
+ /* start off with the type */
+ ftyp = _warc_rdtyp(buf, eoh - buf);
+ /* and let future calls know about the content */
+ w->cntlen = cntlen;
+ w->cntoff = 0U;
+ mtime = 0;/* Avoid compiling error on some platform. */
+
+ switch (ftyp) {
+ case WT_RSRC:
+ case WT_RSP:
+ /* only try and read the filename in the cases that are
+ * guaranteed to have one */
+ fnam = _warc_rduri(buf, eoh - buf);
+ /* check the last character in the URI to avoid creating
+ * directory endpoints as files, see Todo above */
+ if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') {
+ /* break here for now */
+ fnam.len = 0U;
+ fnam.str = NULL;
+ break;
+ }
+ /* bang to our string pool, so we save a
+ * malloc()+free() roundtrip */
+ if (fnam.len + 1U > w->pool.len) {
+ w->pool.len = ((fnam.len + 64U) / 64U) * 64U;
+ w->pool.str = realloc(w->pool.str, w->pool.len);
+ }
+ memcpy(w->pool.str, fnam.str, fnam.len);
+ w->pool.str[fnam.len] = '\0';
+ /* let no one else know about the pool, it's a secret, shhh */
+ fnam.str = w->pool.str;
+
+ /* snarf mtime or deduce from rtime
+ * this is a custom header added by our writer, it's quite
+ * hard to believe anyone else would go through with it
+ * (apart from being part of some http responses of course) */
+ if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) {
+ mtime = rtime;
+ }
+ break;
+ case WT_NONE:
+ case WT_INFO:
+ case WT_META:
+ case WT_REQ:
+ case WT_RVIS:
+ case WT_CONV:
+ case WT_CONT:
+ case LAST_WT:
+ default:
+ fnam.len = 0U;
+ fnam.str = NULL;
+ break;
+ }
+
+ /* now eat some of those delicious buffer bits */
+ __archive_read_consume(a, eoh - buf);
+
+ switch (ftyp) {
+ case WT_RSRC:
+ case WT_RSP:
+ if (fnam.len > 0U) {
+ /* populate entry object */
+ archive_entry_set_filetype(entry, AE_IFREG);
+ archive_entry_copy_pathname(entry, fnam.str);
+ archive_entry_set_size(entry, cntlen);
+ archive_entry_set_perm(entry, 0644);
+ /* rtime is the new ctime, mtime stays mtime */
+ archive_entry_set_ctime(entry, rtime, 0L);
+ archive_entry_set_mtime(entry, mtime, 0L);
+ break;
+ }
+ /* FALLTHROUGH */
+ case WT_NONE:
+ case WT_INFO:
+ case WT_META:
+ case WT_REQ:
+ case WT_RVIS:
+ case WT_CONV:
+ case WT_CONT:
+ case LAST_WT:
+ default:
+ /* consume the content and start over */
+ _warc_skip(a);
+ goto start_over;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off)
+{
+ struct warc_s *w = a->format->data;
+ const char *rab;
+ ssize_t nrd;
+
+ if (w->cntoff >= w->cntlen) {
+ eof:
+ /* it's our lucky day, no work, we can leave early */
+ *buf = NULL;
+ *bsz = 0U;
+ *off = w->cntoff + 4U/*for \r\n\r\n separator*/;
+ w->unconsumed = 0U;
+ return (ARCHIVE_EOF);
+ }
+
+ if (w->unconsumed) {
+ __archive_read_consume(a, w->unconsumed);
+ w->unconsumed = 0U;
+ }
+
+ rab = __archive_read_ahead(a, 1U, &nrd);
+ if (nrd < 0) {
+ *bsz = 0U;
+ /* big catastrophe */
+ return (int)nrd;
+ } else if (nrd == 0) {
+ goto eof;
+ } else if ((size_t)nrd > w->cntlen - w->cntoff) {
+ /* clamp to content-length */
+ nrd = w->cntlen - w->cntoff;
+ }
+ *off = w->cntoff;
+ *bsz = nrd;
+ *buf = rab;
+
+ w->cntoff += nrd;
+ w->unconsumed = (size_t)nrd;
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_skip(struct archive_read *a)
+{
+ struct warc_s *w = a->format->data;
+
+ __archive_read_consume(a, w->cntlen + 4U/*\r\n\r\n separator*/);
+ w->cntlen = 0U;
+ w->cntoff = 0U;
+ return (ARCHIVE_OK);
+}
+
+
+/* private routines */
+static void*
+deconst(const void *c)
+{
+ return (void *)(uintptr_t)c;
+}
+
+static char*
+xmemmem(const char *hay, const size_t haysize,
+ const char *needle, const size_t needlesize)
+{
+ const char *const eoh = hay + haysize;
+ const char *const eon = needle + needlesize;
+ const char *hp;
+ const char *np;
+ const char *cand;
+ unsigned int hsum;
+ unsigned int nsum;
+ unsigned int eqp;
+
+ /* trivial checks first
+ * a 0-sized needle is defined to be found anywhere in haystack
+ * then run strchr() to find a candidate in HAYSTACK (i.e. a portion
+ * that happens to begin with *NEEDLE) */
+ if (needlesize == 0UL) {
+ return deconst(hay);
+ } else if ((hay = memchr(hay, *needle, haysize)) == NULL) {
+ /* trivial */
+ return NULL;
+ }
+
+ /* First characters of haystack and needle are the same now. Both are
+ * guaranteed to be at least one character long. Now computes the sum
+ * of characters values of needle together with the sum of the first
+ * needle_len characters of haystack. */
+ for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U;
+ hp < eoh && np < eon;
+ hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++);
+
+ /* HP now references the (NEEDLESIZE + 1)-th character. */
+ if (np < eon) {
+ /* haystack is smaller than needle, :O */
+ return NULL;
+ } else if (eqp) {
+ /* found a match */
+ return deconst(hay);
+ }
+
+ /* now loop through the rest of haystack,
+ * updating the sum iteratively */
+ for (cand = hay; hp < eoh; hp++) {
+ hsum ^= *cand++;
+ hsum ^= *hp;
+
+ /* Since the sum of the characters is already known to be
+ * equal at that point, it is enough to check just NEEDLESIZE - 1
+ * characters for equality,
+ * also CAND is by design < HP, so no need for range checks */
+ if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) {
+ return deconst(cand);
+ }
+ }
+ return NULL;
+}
+
+static int
+strtoi_lim(const char *str, const char **ep, int llim, int ulim)
+{
+ int res = 0;
+ const char *sp;
+ /* we keep track of the number of digits via rulim */
+ int rulim;
+
+ for (sp = str, rulim = ulim > 10 ? ulim : 10;
+ res * 10 <= ulim && rulim && *sp >= '0' && *sp <= '9';
+ sp++, rulim /= 10) {
+ res *= 10;
+ res += *sp - '0';
+ }
+ if (sp == str) {
+ res = -1;
+ } else if (res < llim || res > ulim) {
+ res = -2;
+ }
+ *ep = (const char*)sp;
+ return res;
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
+ /* Use platform timegm() if available. */
+ return (timegm(t));
+#else
+ /* Else use direct calculation using POSIX assumptions. */
+ /* First, fix up tm_yday based on the year/month/day. */
+ if (mktime(t) == (time_t)-1)
+ return ((time_t)-1);
+ /* Then we can compute timegm() from first principles. */
+ return (t->tm_sec
+ + t->tm_min * 60
+ + t->tm_hour * 3600
+ + t->tm_yday * 86400
+ + (t->tm_year - 70) * 31536000
+ + ((t->tm_year - 69) / 4) * 86400
+ - ((t->tm_year - 1) / 100) * 86400
+ + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static time_t
+xstrpisotime(const char *s, char **endptr)
+{
+/** like strptime() but strictly for ISO 8601 Zulu strings */
+ struct tm tm;
+ time_t res = (time_t)-1;
+
+ /* make sure tm is clean */
+ memset(&tm, 0, sizeof(tm));
+
+ /* as a courtesy to our callers, and since this is a non-standard
+ * routine, we skip leading whitespace */
+ while (*s == ' ' || *s == '\t')
+ ++s;
+
+ /* read year */
+ if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') {
+ goto out;
+ }
+ /* read month */
+ if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') {
+ goto out;
+ }
+ /* read day-of-month */
+ if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') {
+ goto out;
+ }
+ /* read hour */
+ if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') {
+ goto out;
+ }
+ /* read minute */
+ if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') {
+ goto out;
+ }
+ /* read second */
+ if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') {
+ goto out;
+ }
+
+ /* massage TM to fulfill some of POSIX' constraints */
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+
+ /* now convert our custom tm struct to a unix stamp using UTC */
+ res = time_from_tm(&tm);
+
+out:
+ if (endptr != NULL) {
+ *endptr = deconst(s);
+ }
+ return res;
+}
+
+static unsigned int
+_warc_rdver(const char *buf, size_t bsz)
+{
+ static const char magic[] = "WARC/";
+ const char *c;
+ unsigned int ver = 0U;
+ unsigned int end = 0U;
+
+ if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) {
+ /* buffer too small or invalid magic */
+ return ver;
+ }
+ /* looks good so far, read the version number for a laugh */
+ buf += sizeof(magic) - 1U;
+
+ if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') &&
+ isdigit((unsigned char)buf[2U])) {
+ /* we support a maximum of 2 digits in the minor version */
+ if (isdigit((unsigned char)buf[3U]))
+ end = 1U;
+ /* set up major version */
+ ver = (buf[0U] - '0') * 10000U;
+ /* set up minor version */
+ if (end == 1U) {
+ ver += (buf[2U] - '0') * 1000U;
+ ver += (buf[3U] - '0') * 100U;
+ } else
+ ver += (buf[2U] - '0') * 100U;
+ /*
+ * WARC below version 0.12 has a space-separated header
+ * WARC 0.12 and above terminates the version with a CRLF
+ */
+ c = buf + 3U + end;
+ if (ver >= 1200U) {
+ if (memcmp(c, "\r\n", 2U) != 0)
+ ver = 0U;
+ } else {
+ /* ver < 1200U */
+ if (*c != ' ' && *c != '\t')
+ ver = 0U;
+ }
+ }
+ return ver;
+}
+
+static unsigned int
+_warc_rdtyp(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nWARC-Type:";
+ const char *val, *eol;
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return WT_NONE;
+ }
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* no end of line */
+ return WT_NONE;
+ }
+
+ /* overread whitespace */
+ while (val < eol && (*val == ' ' || *val == '\t'))
+ ++val;
+
+ if (val + 8U == eol) {
+ if (memcmp(val, "resource", 8U) == 0)
+ return WT_RSRC;
+ else if (memcmp(val, "response", 8U) == 0)
+ return WT_RSP;
+ }
+ return WT_NONE;
+}
+
+static warc_string_t
+_warc_rduri(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nWARC-Target-URI:";
+ const char *val, *uri, *eol, *p;
+ warc_string_t res = {0U, NULL};
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return res;
+ }
+ /* overread whitespace */
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* no end of line */
+ return res;
+ }
+
+ while (val < eol && (*val == ' ' || *val == '\t'))
+ ++val;
+
+ /* overread URL designators */
+ if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) {
+ /* not touching that! */
+ return res;
+ }
+
+ /* spaces inside uri are not allowed, CRLF should follow */
+ for (p = val; p < eol; p++) {
+ if (isspace((unsigned char)*p))
+ return res;
+ }
+
+ /* there must be at least space for ftp */
+ if (uri < (val + 3U))
+ return res;
+
+ /* move uri to point to after :// */
+ uri += 3U;
+
+ /* now then, inspect the URI */
+ if (memcmp(val, "file", 4U) == 0) {
+ /* perfect, nothing left to do here */
+
+ } else if (memcmp(val, "http", 4U) == 0 ||
+ memcmp(val, "ftp", 3U) == 0) {
+ /* overread domain, and the first / */
+ while (uri < eol && *uri++ != '/');
+ } else {
+ /* not sure what to do? best to bugger off */
+ return res;
+ }
+ res.str = uri;
+ res.len = eol - uri;
+ return res;
+}
+
+static ssize_t
+_warc_rdlen(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nContent-Length:";
+ const char *val, *eol;
+ char *on = NULL;
+ long int len;
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return -1;
+ }
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+ /* no end of line */
+ return -1;
+ }
+
+ /* skip leading whitespace */
+ while (val < eol && (*val == ' ' || *val == '\t'))
+ val++;
+ /* there must be at least one digit */
+ if (!isdigit((unsigned char)*val))
+ return -1;
+ errno = 0;
+ len = strtol(val, &on, 10);
+ if (errno != 0 || on != eol) {
+ /* line must end here */
+ return -1;
+ }
+
+ return (size_t)len;
+}
+
+static time_t
+_warc_rdrtm(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nWARC-Date:";
+ const char *val, *eol;
+ char *on = NULL;
+ time_t res;
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return (time_t)-1;
+ }
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
+ /* no end of line */
+ return -1;
+ }
+
+ /* xstrpisotime() kindly overreads whitespace for us, so use that */
+ res = xstrpisotime(val, &on);
+ if (on != eol) {
+ /* line must end here */
+ return -1;
+ }
+ return res;
+}
+
+static time_t
+_warc_rdmtm(const char *buf, size_t bsz)
+{
+ static const char _key[] = "\r\nLast-Modified:";
+ const char *val, *eol;
+ char *on = NULL;
+ time_t res;
+
+ if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+ /* no bother */
+ return (time_t)-1;
+ }
+ val += sizeof(_key) - 1U;
+ if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
+ /* no end of line */
+ return -1;
+ }
+
+ /* xstrpisotime() kindly overreads whitespace for us, so use that */
+ res = xstrpisotime(val, &on);
+ if (on != eol) {
+ /* line must end here */
+ return -1;
+ }
+ return res;
+}
+
+static const char*
+_warc_find_eoh(const char *buf, size_t bsz)
+{
+ static const char _marker[] = "\r\n\r\n";
+ const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
+
+ if (hit != NULL) {
+ hit += sizeof(_marker) - 1U;
+ }
+ return hit;
+}
+
+static const char*
+_warc_find_eol(const char *buf, size_t bsz)
+{
+ static const char _marker[] = "\r\n";
+ const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
+
+ return hit;
+}
+/* archive_read_support_format_warc.c ends here */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_xar.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_xar.c
new file mode 100644
index 0000000000..312a1db2d1
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_xar.c
@@ -0,0 +1,3332 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_LIBXML_XMLREADER_H
+#error #include <libxml/xmlreader.h>
+#elif HAVE_BSDXML_H
+#include <bsdxml.h>
+#elif HAVE_EXPAT_H
+#error #include <expat.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if (!defined(HAVE_LIBXML_XMLREADER_H) && \
+ !defined(HAVE_BSDXML_H) && !defined(HAVE_EXPAT_H)) ||\
+ !defined(HAVE_ZLIB_H) || \
+ !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ * o libxml2 or expat --- XML parser
+ * o openssl or MD5/SHA1 hash function
+ * o zlib
+ * o bzlib2 (option)
+ * o liblzma (option)
+ */
+int
+archive_read_support_format_xar(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_xar");
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Xar not supported on this platform");
+ return (ARCHIVE_WARN);
+}
+
+#else /* Support xar format */
+
+/* #define DEBUG 1 */
+/* #define DEBUG_PRINT_TOC 1 */
+#if DEBUG_PRINT_TOC
+#define PRINT_TOC(d, outbytes) do { \
+ unsigned char *x = (unsigned char *)(uintptr_t)d; \
+ unsigned char c = x[outbytes-1]; \
+ x[outbytes - 1] = 0; \
+ fprintf(stderr, "%s", x); \
+ fprintf(stderr, "%c", c); \
+ x[outbytes - 1] = c; \
+} while (0)
+#else
+#define PRINT_TOC(d, outbytes)
+#endif
+
+#define HEADER_MAGIC 0x78617221
+#define HEADER_SIZE 28
+#define HEADER_VERSION 1
+#define CKSUM_NONE 0
+#define CKSUM_SHA1 1
+#define CKSUM_MD5 2
+
+#define MD5_SIZE 16
+#define SHA1_SIZE 20
+#define MAX_SUM_SIZE 20
+
+enum enctype {
+ NONE,
+ GZIP,
+ BZIP2,
+ LZMA,
+ XZ,
+};
+
+struct chksumval {
+ int alg;
+ size_t len;
+ unsigned char val[MAX_SUM_SIZE];
+};
+
+struct chksumwork {
+ int alg;
+#ifdef ARCHIVE_HAS_MD5
+ archive_md5_ctx md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ archive_sha1_ctx sha1ctx;
+#endif
+};
+
+struct xattr {
+ struct xattr *next;
+ struct archive_string name;
+ uint64_t id;
+ uint64_t length;
+ uint64_t offset;
+ uint64_t size;
+ enum enctype encoding;
+ struct chksumval a_sum;
+ struct chksumval e_sum;
+ struct archive_string fstype;
+};
+
+struct xar_file {
+ struct xar_file *next;
+ struct xar_file *hdnext;
+ struct xar_file *parent;
+ int subdirs;
+
+ unsigned int has;
+#define HAS_DATA 0x00001
+#define HAS_PATHNAME 0x00002
+#define HAS_SYMLINK 0x00004
+#define HAS_TIME 0x00008
+#define HAS_UID 0x00010
+#define HAS_GID 0x00020
+#define HAS_MODE 0x00040
+#define HAS_TYPE 0x00080
+#define HAS_DEV 0x00100
+#define HAS_DEVMAJOR 0x00200
+#define HAS_DEVMINOR 0x00400
+#define HAS_INO 0x00800
+#define HAS_FFLAGS 0x01000
+#define HAS_XATTR 0x02000
+#define HAS_ACL 0x04000
+#define HAS_CTIME 0x08000
+#define HAS_MTIME 0x10000
+#define HAS_ATIME 0x20000
+
+ uint64_t id;
+ uint64_t length;
+ uint64_t offset;
+ uint64_t size;
+ enum enctype encoding;
+ struct chksumval a_sum;
+ struct chksumval e_sum;
+ struct archive_string pathname;
+ struct archive_string symlink;
+ time_t ctime;
+ time_t mtime;
+ time_t atime;
+ struct archive_string uname;
+ int64_t uid;
+ struct archive_string gname;
+ int64_t gid;
+ mode_t mode;
+ dev_t dev;
+ dev_t devmajor;
+ dev_t devminor;
+ int64_t ino64;
+ struct archive_string fflags_text;
+ unsigned int link;
+ unsigned int nlink;
+ struct archive_string hardlink;
+ struct xattr *xattr_list;
+};
+
+struct hdlink {
+ struct hdlink *next;
+
+ unsigned int id;
+ int cnt;
+ struct xar_file *files;
+};
+
+struct heap_queue {
+ struct xar_file **files;
+ int allocated;
+ int used;
+};
+
+enum xmlstatus {
+ INIT,
+ XAR,
+ TOC,
+ TOC_CREATION_TIME,
+ TOC_CHECKSUM,
+ TOC_CHECKSUM_OFFSET,
+ TOC_CHECKSUM_SIZE,
+ TOC_FILE,
+ FILE_DATA,
+ FILE_DATA_LENGTH,
+ FILE_DATA_OFFSET,
+ FILE_DATA_SIZE,
+ FILE_DATA_ENCODING,
+ FILE_DATA_A_CHECKSUM,
+ FILE_DATA_E_CHECKSUM,
+ FILE_DATA_CONTENT,
+ FILE_EA,
+ FILE_EA_LENGTH,
+ FILE_EA_OFFSET,
+ FILE_EA_SIZE,
+ FILE_EA_ENCODING,
+ FILE_EA_A_CHECKSUM,
+ FILE_EA_E_CHECKSUM,
+ FILE_EA_NAME,
+ FILE_EA_FSTYPE,
+ FILE_CTIME,
+ FILE_MTIME,
+ FILE_ATIME,
+ FILE_GROUP,
+ FILE_GID,
+ FILE_USER,
+ FILE_UID,
+ FILE_MODE,
+ FILE_DEVICE,
+ FILE_DEVICE_MAJOR,
+ FILE_DEVICE_MINOR,
+ FILE_DEVICENO,
+ FILE_INODE,
+ FILE_LINK,
+ FILE_TYPE,
+ FILE_NAME,
+ FILE_ACL,
+ FILE_ACL_DEFAULT,
+ FILE_ACL_ACCESS,
+ FILE_ACL_APPLEEXTENDED,
+ /* BSD file flags. */
+ FILE_FLAGS,
+ FILE_FLAGS_USER_NODUMP,
+ FILE_FLAGS_USER_IMMUTABLE,
+ FILE_FLAGS_USER_APPEND,
+ FILE_FLAGS_USER_OPAQUE,
+ FILE_FLAGS_USER_NOUNLINK,
+ FILE_FLAGS_SYS_ARCHIVED,
+ FILE_FLAGS_SYS_IMMUTABLE,
+ FILE_FLAGS_SYS_APPEND,
+ FILE_FLAGS_SYS_NOUNLINK,
+ FILE_FLAGS_SYS_SNAPSHOT,
+ /* Linux file flags. */
+ FILE_EXT2,
+ FILE_EXT2_SecureDeletion,
+ FILE_EXT2_Undelete,
+ FILE_EXT2_Compress,
+ FILE_EXT2_Synchronous,
+ FILE_EXT2_Immutable,
+ FILE_EXT2_AppendOnly,
+ FILE_EXT2_NoDump,
+ FILE_EXT2_NoAtime,
+ FILE_EXT2_CompDirty,
+ FILE_EXT2_CompBlock,
+ FILE_EXT2_NoCompBlock,
+ FILE_EXT2_CompError,
+ FILE_EXT2_BTree,
+ FILE_EXT2_HashIndexed,
+ FILE_EXT2_iMagic,
+ FILE_EXT2_Journaled,
+ FILE_EXT2_NoTail,
+ FILE_EXT2_DirSync,
+ FILE_EXT2_TopDir,
+ FILE_EXT2_Reserved,
+ UNKNOWN,
+};
+
+struct unknown_tag {
+ struct unknown_tag *next;
+ struct archive_string name;
+};
+
+struct xar {
+ uint64_t offset; /* Current position in the file. */
+ int64_t total;
+ uint64_t h_base;
+ int end_of_file;
+#define OUTBUFF_SIZE (1024 * 64)
+ unsigned char *outbuff;
+
+ enum xmlstatus xmlsts;
+ enum xmlstatus xmlsts_unknown;
+ struct unknown_tag *unknowntags;
+ int base64text;
+
+ /*
+ * TOC
+ */
+ uint64_t toc_remaining;
+ uint64_t toc_total;
+ uint64_t toc_chksum_offset;
+ uint64_t toc_chksum_size;
+
+ /*
+ * For Decoding data.
+ */
+ enum enctype rd_encoding;
+ z_stream stream;
+ int stream_valid;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ bz_stream bzstream;
+ int bzstream_valid;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ lzma_stream lzstream;
+ int lzstream_valid;
+#endif
+ /*
+ * For Checksum data.
+ */
+ struct chksumwork a_sumwrk;
+ struct chksumwork e_sumwrk;
+
+ struct xar_file *file; /* current reading file. */
+ struct xattr *xattr; /* current reading extended attribute. */
+ struct heap_queue file_queue;
+ struct xar_file *hdlink_orgs;
+ struct hdlink *hdlink_list;
+
+ int entry_init;
+ uint64_t entry_total;
+ uint64_t entry_remaining;
+ size_t entry_unconsumed;
+ uint64_t entry_size;
+ enum enctype entry_encoding;
+ struct chksumval entry_a_sum;
+ struct chksumval entry_e_sum;
+
+ struct archive_string_conv *sconv;
+};
+
+struct xmlattr {
+ struct xmlattr *next;
+ char *name;
+ char *value;
+};
+
+struct xmlattr_list {
+ struct xmlattr *first;
+ struct xmlattr **last;
+};
+
+static int xar_bid(struct archive_read *, int);
+static int xar_read_header(struct archive_read *,
+ struct archive_entry *);
+static int xar_read_data(struct archive_read *,
+ const void **, size_t *, int64_t *);
+static int xar_read_data_skip(struct archive_read *);
+static int xar_cleanup(struct archive_read *);
+static int move_reading_point(struct archive_read *, uint64_t);
+static int rd_contents_init(struct archive_read *,
+ enum enctype, int, int);
+static int rd_contents(struct archive_read *, const void **,
+ size_t *, size_t *, uint64_t);
+static uint64_t atol10(const char *, size_t);
+static int64_t atol8(const char *, size_t);
+static size_t atohex(unsigned char *, size_t, const char *, size_t);
+static time_t parse_time(const char *p, size_t n);
+static int heap_add_entry(struct archive_read *a,
+ struct heap_queue *, struct xar_file *);
+static struct xar_file *heap_get_entry(struct heap_queue *);
+static int add_link(struct archive_read *,
+ struct xar *, struct xar_file *);
+static void checksum_init(struct archive_read *, int, int);
+static void checksum_update(struct archive_read *, const void *,
+ size_t, const void *, size_t);
+static int checksum_final(struct archive_read *, const void *,
+ size_t, const void *, size_t);
+static void checksum_cleanup(struct archive_read *);
+static int decompression_init(struct archive_read *, enum enctype);
+static int decompress(struct archive_read *, const void **,
+ size_t *, const void *, size_t *);
+static int decompression_cleanup(struct archive_read *);
+static void xmlattr_cleanup(struct xmlattr_list *);
+static int file_new(struct archive_read *,
+ struct xar *, struct xmlattr_list *);
+static void file_free(struct xar_file *);
+static int xattr_new(struct archive_read *,
+ struct xar *, struct xmlattr_list *);
+static void xattr_free(struct xattr *);
+static int getencoding(struct xmlattr_list *);
+static int getsumalgorithm(struct xmlattr_list *);
+static int unknowntag_start(struct archive_read *,
+ struct xar *, const char *);
+static void unknowntag_end(struct xar *, const char *);
+static int xml_start(struct archive_read *,
+ const char *, struct xmlattr_list *);
+static void xml_end(void *, const char *);
+static void xml_data(void *, const char *, int);
+static int xml_parse_file_flags(struct xar *, const char *);
+static int xml_parse_file_ext2(struct xar *, const char *);
+#if defined(HAVE_LIBXML_XMLREADER_H)
+static int xml2_xmlattr_setup(struct archive_read *,
+ struct xmlattr_list *, xmlTextReaderPtr);
+static int xml2_read_cb(void *, char *, int);
+static int xml2_close_cb(void *);
+static void xml2_error_hdr(void *, const char *, xmlParserSeverities,
+ xmlTextReaderLocatorPtr);
+static int xml2_read_toc(struct archive_read *);
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+struct expat_userData {
+ int state;
+ struct archive_read *archive;
+};
+static int expat_xmlattr_setup(struct archive_read *,
+ struct xmlattr_list *, const XML_Char **);
+static void expat_start_cb(void *, const XML_Char *, const XML_Char **);
+static void expat_end_cb(void *, const XML_Char *);
+static void expat_data_cb(void *, const XML_Char *, int);
+static int expat_read_toc(struct archive_read *);
+#endif
+
+int
+archive_read_support_format_xar(struct archive *_a)
+{
+ struct xar *xar;
+ struct archive_read *a = (struct archive_read *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_xar");
+
+ xar = (struct xar *)calloc(1, sizeof(*xar));
+ if (xar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate xar data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* initialize xar->file_queue */
+ xar->file_queue.allocated = 0;
+ xar->file_queue.used = 0;
+ xar->file_queue.files = NULL;
+
+ r = __archive_read_register_format(a,
+ xar,
+ "xar",
+ xar_bid,
+ NULL,
+ xar_read_header,
+ xar_read_data,
+ xar_read_data_skip,
+ NULL,
+ xar_cleanup,
+ NULL,
+ NULL);
+ if (r != ARCHIVE_OK)
+ free(xar);
+ return (r);
+}
+
+static int
+xar_bid(struct archive_read *a, int best_bid)
+{
+ const unsigned char *b;
+ int bid;
+
+ (void)best_bid; /* UNUSED */
+
+ b = __archive_read_ahead(a, HEADER_SIZE, NULL);
+ if (b == NULL)
+ return (-1);
+
+ bid = 0;
+ /*
+ * Verify magic code
+ */
+ if (archive_be32dec(b) != HEADER_MAGIC)
+ return (0);
+ bid += 32;
+ /*
+ * Verify header size
+ */
+ if (archive_be16dec(b+4) != HEADER_SIZE)
+ return (0);
+ bid += 16;
+ /*
+ * Verify header version
+ */
+ if (archive_be16dec(b+6) != HEADER_VERSION)
+ return (0);
+ bid += 16;
+ /*
+ * Verify type of checksum
+ */
+ switch (archive_be32dec(b+24)) {
+ case CKSUM_NONE:
+ case CKSUM_SHA1:
+ case CKSUM_MD5:
+ bid += 32;
+ break;
+ default:
+ return (0);
+ }
+
+ return (bid);
+}
+
+static int
+read_toc(struct archive_read *a)
+{
+ struct xar *xar;
+ struct xar_file *file;
+ const unsigned char *b;
+ uint64_t toc_compressed_size;
+ uint64_t toc_uncompressed_size;
+ uint32_t toc_chksum_alg;
+ ssize_t bytes;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+
+ /*
+ * Read xar header.
+ */
+ b = __archive_read_ahead(a, HEADER_SIZE, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes < HEADER_SIZE) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated archive header");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (archive_be32dec(b) != HEADER_MAGIC) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid header magic");
+ return (ARCHIVE_FATAL);
+ }
+ if (archive_be16dec(b+6) != HEADER_VERSION) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported header version(%d)",
+ archive_be16dec(b+6));
+ return (ARCHIVE_FATAL);
+ }
+ toc_compressed_size = archive_be64dec(b+8);
+ xar->toc_remaining = toc_compressed_size;
+ toc_uncompressed_size = archive_be64dec(b+16);
+ toc_chksum_alg = archive_be32dec(b+24);
+ __archive_read_consume(a, HEADER_SIZE);
+ xar->offset += HEADER_SIZE;
+ xar->toc_total = 0;
+
+ /*
+ * Read TOC(Table of Contents).
+ */
+ /* Initialize reading contents. */
+ r = move_reading_point(a, HEADER_SIZE);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = rd_contents_init(a, GZIP, toc_chksum_alg, CKSUM_NONE);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+#ifdef HAVE_LIBXML_XMLREADER_H
+ r = xml2_read_toc(a);
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+ r = expat_read_toc(a);
+#endif
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /* Set 'The HEAP' base. */
+ xar->h_base = xar->offset;
+ if (xar->toc_total != toc_uncompressed_size) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "TOC uncompressed size error");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Checksum TOC
+ */
+ if (toc_chksum_alg != CKSUM_NONE) {
+ r = move_reading_point(a, xar->toc_chksum_offset);
+ if (r != ARCHIVE_OK)
+ return (r);
+ b = __archive_read_ahead(a,
+ (size_t)xar->toc_chksum_size, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if ((uint64_t)bytes < xar->toc_chksum_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated archive file");
+ return (ARCHIVE_FATAL);
+ }
+ r = checksum_final(a, b,
+ (size_t)xar->toc_chksum_size, NULL, 0);
+ __archive_read_consume(a, xar->toc_chksum_size);
+ xar->offset += xar->toc_chksum_size;
+ if (r != ARCHIVE_OK)
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ return (ARCHIVE_FATAL);
+#endif
+ }
+
+ /*
+ * Connect hardlinked files.
+ */
+ for (file = xar->hdlink_orgs; file != NULL; file = file->hdnext) {
+ struct hdlink **hdlink;
+
+ for (hdlink = &(xar->hdlink_list); *hdlink != NULL;
+ hdlink = &((*hdlink)->next)) {
+ if ((*hdlink)->id == file->id) {
+ struct hdlink *hltmp;
+ struct xar_file *f2;
+ int nlink = (*hdlink)->cnt + 1;
+
+ file->nlink = nlink;
+ for (f2 = (*hdlink)->files; f2 != NULL;
+ f2 = f2->hdnext) {
+ f2->nlink = nlink;
+ archive_string_copy(
+ &(f2->hardlink), &(file->pathname));
+ }
+ /* Remove resolved files from hdlist_list. */
+ hltmp = *hdlink;
+ *hdlink = hltmp->next;
+ free(hltmp);
+ break;
+ }
+ }
+ }
+ a->archive.archive_format = ARCHIVE_FORMAT_XAR;
+ a->archive.archive_format_name = "xar";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+xar_read_header(struct archive_read *a, struct archive_entry *entry)
+{
+ struct xar *xar;
+ struct xar_file *file;
+ struct xattr *xattr;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ r = ARCHIVE_OK;
+
+ if (xar->offset == 0) {
+ /* Create a character conversion object. */
+ if (xar->sconv == NULL) {
+ xar->sconv = archive_string_conversion_from_charset(
+ &(a->archive), "UTF-8", 1);
+ if (xar->sconv == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Read TOC. */
+ r = read_toc(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ for (;;) {
+ file = xar->file = heap_get_entry(&(xar->file_queue));
+ if (file == NULL) {
+ xar->end_of_file = 1;
+ return (ARCHIVE_EOF);
+ }
+ if ((file->mode & AE_IFMT) != AE_IFDIR)
+ break;
+ if (file->has != (HAS_PATHNAME | HAS_TYPE))
+ break;
+ /*
+ * If a file type is a directory and it does not have
+ * any metadata, do not export.
+ */
+ file_free(file);
+ }
+ if (file->has & HAS_ATIME) {
+ archive_entry_set_atime(entry, file->atime, 0);
+ }
+ if (file->has & HAS_CTIME) {
+ archive_entry_set_ctime(entry, file->ctime, 0);
+ }
+ if (file->has & HAS_MTIME) {
+ archive_entry_set_mtime(entry, file->mtime, 0);
+ }
+ archive_entry_set_gid(entry, file->gid);
+ if (file->gname.length > 0 &&
+ archive_entry_copy_gname_l(entry, file->gname.s,
+ archive_strlen(&(file->gname)), xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Gname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(xar->sconv));
+ r = ARCHIVE_WARN;
+ }
+ archive_entry_set_uid(entry, file->uid);
+ if (file->uname.length > 0 &&
+ archive_entry_copy_uname_l(entry, file->uname.s,
+ archive_strlen(&(file->uname)), xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Uname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(xar->sconv));
+ r = ARCHIVE_WARN;
+ }
+ archive_entry_set_mode(entry, file->mode);
+ if (archive_entry_copy_pathname_l(entry, file->pathname.s,
+ archive_strlen(&(file->pathname)), xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(xar->sconv));
+ r = ARCHIVE_WARN;
+ }
+
+
+ if (file->symlink.length > 0 &&
+ archive_entry_copy_symlink_l(entry, file->symlink.s,
+ archive_strlen(&(file->symlink)), xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Linkname cannot be converted from %s to current locale.",
+ archive_string_conversion_charset_name(xar->sconv));
+ r = ARCHIVE_WARN;
+ }
+ /* Set proper nlink. */
+ if ((file->mode & AE_IFMT) == AE_IFDIR)
+ archive_entry_set_nlink(entry, file->subdirs + 2);
+ else
+ archive_entry_set_nlink(entry, file->nlink);
+ archive_entry_set_size(entry, file->size);
+ if (archive_strlen(&(file->hardlink)) > 0)
+ archive_entry_set_hardlink(entry, file->hardlink.s);
+ archive_entry_set_ino64(entry, file->ino64);
+ if (file->has & HAS_DEV)
+ archive_entry_set_dev(entry, file->dev);
+ if (file->has & HAS_DEVMAJOR)
+ archive_entry_set_devmajor(entry, file->devmajor);
+ if (file->has & HAS_DEVMINOR)
+ archive_entry_set_devminor(entry, file->devminor);
+ if (archive_strlen(&(file->fflags_text)) > 0)
+ archive_entry_copy_fflags_text(entry, file->fflags_text.s);
+
+ xar->entry_init = 1;
+ xar->entry_total = 0;
+ xar->entry_remaining = file->length;
+ xar->entry_size = file->size;
+ xar->entry_encoding = file->encoding;
+ xar->entry_a_sum = file->a_sum;
+ xar->entry_e_sum = file->e_sum;
+ /*
+ * Read extended attributes.
+ */
+ xattr = file->xattr_list;
+ while (xattr != NULL) {
+ const void *d;
+ size_t outbytes = 0;
+ size_t used = 0;
+
+ r = move_reading_point(a, xattr->offset);
+ if (r != ARCHIVE_OK)
+ break;
+ r = rd_contents_init(a, xattr->encoding,
+ xattr->a_sum.alg, xattr->e_sum.alg);
+ if (r != ARCHIVE_OK)
+ break;
+ d = NULL;
+ r = rd_contents(a, &d, &outbytes, &used, xattr->length);
+ if (r != ARCHIVE_OK)
+ break;
+ if (outbytes != xattr->size) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Decompressed size error");
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ r = checksum_final(a,
+ xattr->a_sum.val, xattr->a_sum.len,
+ xattr->e_sum.val, xattr->e_sum.len);
+ if (r != ARCHIVE_OK) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Xattr checksum error");
+ r = ARCHIVE_WARN;
+ break;
+#endif
+ }
+ if (xattr->name.s == NULL) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Xattr name error");
+ r = ARCHIVE_WARN;
+ break;
+ }
+ archive_entry_xattr_add_entry(entry,
+ xattr->name.s, d, outbytes);
+ xattr = xattr->next;
+ }
+ if (r != ARCHIVE_OK) {
+ file_free(file);
+ return (r);
+ }
+
+ if (xar->entry_remaining > 0)
+ /* Move reading point to the beginning of current
+ * file contents. */
+ r = move_reading_point(a, file->offset);
+ else
+ r = ARCHIVE_OK;
+
+ file_free(file);
+ return (r);
+}
+
+static int
+xar_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ struct xar *xar;
+ size_t used = 0;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+
+ if (xar->entry_unconsumed) {
+ __archive_read_consume(a, xar->entry_unconsumed);
+ xar->entry_unconsumed = 0;
+ }
+
+ if (xar->end_of_file || xar->entry_remaining <= 0) {
+ r = ARCHIVE_EOF;
+ goto abort_read_data;
+ }
+
+ if (xar->entry_init) {
+ r = rd_contents_init(a, xar->entry_encoding,
+ xar->entry_a_sum.alg, xar->entry_e_sum.alg);
+ if (r != ARCHIVE_OK) {
+ xar->entry_remaining = 0;
+ return (r);
+ }
+ xar->entry_init = 0;
+ }
+
+ *buff = NULL;
+ r = rd_contents(a, buff, size, &used, xar->entry_remaining);
+ if (r != ARCHIVE_OK)
+ goto abort_read_data;
+
+ *offset = xar->entry_total;
+ xar->entry_total += *size;
+ xar->total += *size;
+ xar->offset += used;
+ xar->entry_remaining -= used;
+ xar->entry_unconsumed = used;
+
+ if (xar->entry_remaining == 0) {
+ if (xar->entry_total != xar->entry_size) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Decompressed size error");
+ r = ARCHIVE_FATAL;
+ goto abort_read_data;
+ }
+ r = checksum_final(a,
+ xar->entry_a_sum.val, xar->entry_a_sum.len,
+ xar->entry_e_sum.val, xar->entry_e_sum.len);
+ if (r != ARCHIVE_OK)
+ goto abort_read_data;
+ }
+
+ return (ARCHIVE_OK);
+abort_read_data:
+ *buff = NULL;
+ *size = 0;
+ *offset = xar->total;
+ return (r);
+}
+
+static int
+xar_read_data_skip(struct archive_read *a)
+{
+ struct xar *xar;
+ int64_t bytes_skipped;
+
+ xar = (struct xar *)(a->format->data);
+ if (xar->end_of_file)
+ return (ARCHIVE_EOF);
+ bytes_skipped = __archive_read_consume(a, xar->entry_remaining +
+ xar->entry_unconsumed);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+ xar->offset += bytes_skipped;
+ xar->entry_unconsumed = 0;
+ return (ARCHIVE_OK);
+}
+
+static int
+xar_cleanup(struct archive_read *a)
+{
+ struct xar *xar;
+ struct hdlink *hdlink;
+ int i;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ checksum_cleanup(a);
+ r = decompression_cleanup(a);
+ hdlink = xar->hdlink_list;
+ while (hdlink != NULL) {
+ struct hdlink *next = hdlink->next;
+
+ free(hdlink);
+ hdlink = next;
+ }
+ for (i = 0; i < xar->file_queue.used; i++)
+ file_free(xar->file_queue.files[i]);
+ free(xar->file_queue.files);
+ while (xar->unknowntags != NULL) {
+ struct unknown_tag *tag;
+
+ tag = xar->unknowntags;
+ xar->unknowntags = tag->next;
+ archive_string_free(&(tag->name));
+ free(tag);
+ }
+ free(xar->outbuff);
+ free(xar);
+ a->format->data = NULL;
+ return (r);
+}
+
+static int
+move_reading_point(struct archive_read *a, uint64_t offset)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)(a->format->data);
+ if (xar->offset - xar->h_base != offset) {
+ /* Seek forward to the start of file contents. */
+ int64_t step;
+
+ step = offset - (xar->offset - xar->h_base);
+ if (step > 0) {
+ step = __archive_read_consume(a, step);
+ if (step < 0)
+ return ((int)step);
+ xar->offset += step;
+ } else {
+ int64_t pos = __archive_read_seek(a, xar->h_base + offset, SEEK_SET);
+ if (pos == ARCHIVE_FAILED) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Cannot seek.");
+ return (ARCHIVE_FAILED);
+ }
+ xar->offset = pos;
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+rd_contents_init(struct archive_read *a, enum enctype encoding,
+ int a_sum_alg, int e_sum_alg)
+{
+ int r;
+
+ /* Init decompress library. */
+ if ((r = decompression_init(a, encoding)) != ARCHIVE_OK)
+ return (r);
+ /* Init checksum library. */
+ checksum_init(a, a_sum_alg, e_sum_alg);
+ return (ARCHIVE_OK);
+}
+
+static int
+rd_contents(struct archive_read *a, const void **buff, size_t *size,
+ size_t *used, uint64_t remaining)
+{
+ const unsigned char *b;
+ ssize_t bytes;
+
+ /* Get whatever bytes are immediately available. */
+ b = __archive_read_ahead(a, 1, &bytes);
+ if (bytes < 0)
+ return ((int)bytes);
+ if (bytes == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated archive file");
+ return (ARCHIVE_FATAL);
+ }
+ if ((uint64_t)bytes > remaining)
+ bytes = (ssize_t)remaining;
+
+ /*
+ * Decompress contents of file.
+ */
+ *used = bytes;
+ if (decompress(a, buff, size, b, used) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * Update checksum of a compressed data and a extracted data.
+ */
+ checksum_update(a, b, *used, *buff, *size);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+
+static uint64_t
+atol10(const char *p, size_t char_cnt)
+{
+ uint64_t l;
+ int digit;
+
+ if (char_cnt == 0)
+ return (0);
+
+ l = 0;
+ digit = *p - '0';
+ while (digit >= 0 && digit < 10 && char_cnt-- > 0) {
+ l = (l * 10) + digit;
+ digit = *++p - '0';
+ }
+ return (l);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ if (char_cnt == 0)
+ return (0);
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= '0' && *p <= '7')
+ digit = *p - '0';
+ else
+ break;
+ p++;
+ l <<= 3;
+ l |= digit;
+ }
+ return (l);
+}
+
+static size_t
+atohex(unsigned char *b, size_t bsize, const char *p, size_t psize)
+{
+ size_t fbsize = bsize;
+
+ while (bsize && psize > 1) {
+ unsigned char x;
+
+ if (p[0] >= 'a' && p[0] <= 'z')
+ x = (p[0] - 'a' + 0x0a) << 4;
+ else if (p[0] >= 'A' && p[0] <= 'Z')
+ x = (p[0] - 'A' + 0x0a) << 4;
+ else if (p[0] >= '0' && p[0] <= '9')
+ x = (p[0] - '0') << 4;
+ else
+ return (-1);
+ if (p[1] >= 'a' && p[1] <= 'z')
+ x |= p[1] - 'a' + 0x0a;
+ else if (p[1] >= 'A' && p[1] <= 'Z')
+ x |= p[1] - 'A' + 0x0a;
+ else if (p[1] >= '0' && p[1] <= '9')
+ x |= p[1] - '0';
+ else
+ return (-1);
+
+ *b++ = x;
+ bsize--;
+ p += 2;
+ psize -= 2;
+ }
+ return (fbsize - bsize);
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE__MKGMTIME
+ return _mkgmtime(t);
+#elif HAVE_TIMEGM
+ /* Use platform timegm() if available. */
+ return (timegm(t));
+#else
+ /* Else use direct calculation using POSIX assumptions. */
+ /* First, fix up tm_yday based on the year/month/day. */
+ mktime(t);
+ /* Then we can compute timegm() from first principles. */
+ return (t->tm_sec
+ + t->tm_min * 60
+ + t->tm_hour * 3600
+ + t->tm_yday * 86400
+ + (t->tm_year - 70) * 31536000
+ + ((t->tm_year - 69) / 4) * 86400
+ - ((t->tm_year - 1) / 100) * 86400
+ + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static time_t
+parse_time(const char *p, size_t n)
+{
+ struct tm tm;
+ time_t t = 0;
+ int64_t data;
+
+ memset(&tm, 0, sizeof(tm));
+ if (n != 20)
+ return (t);
+ data = atol10(p, 4);
+ if (data < 1900)
+ return (t);
+ tm.tm_year = (int)data - 1900;
+ p += 4;
+ if (*p++ != '-')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 1 || data > 12)
+ return (t);
+ tm.tm_mon = (int)data -1;
+ p += 2;
+ if (*p++ != '-')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 1 || data > 31)
+ return (t);
+ tm.tm_mday = (int)data;
+ p += 2;
+ if (*p++ != 'T')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 0 || data > 23)
+ return (t);
+ tm.tm_hour = (int)data;
+ p += 2;
+ if (*p++ != ':')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 0 || data > 59)
+ return (t);
+ tm.tm_min = (int)data;
+ p += 2;
+ if (*p++ != ':')
+ return (t);
+ data = atol10(p, 2);
+ if (data < 0 || data > 60)
+ return (t);
+ tm.tm_sec = (int)data;
+#if 0
+ p += 2;
+ if (*p != 'Z')
+ return (t);
+#endif
+
+ t = time_from_tm(&tm);
+
+ return (t);
+}
+
+static int
+heap_add_entry(struct archive_read *a,
+ struct heap_queue *heap, struct xar_file *file)
+{
+ uint64_t file_id, parent_id;
+ int hole, parent;
+
+ /* Expand our pending files list as necessary. */
+ if (heap->used >= heap->allocated) {
+ struct xar_file **new_pending_files;
+ int new_size;
+
+ if (heap->allocated < 1024)
+ new_size = 1024;
+ else
+ new_size = heap->allocated * 2;
+ /* Overflow might keep us from growing the list. */
+ if (new_size <= heap->allocated) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ new_pending_files = (struct xar_file **)
+ malloc(new_size * sizeof(new_pending_files[0]));
+ if (new_pending_files == NULL) {
+ archive_set_error(&a->archive,
+ ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (heap->allocated) {
+ memcpy(new_pending_files, heap->files,
+ heap->allocated * sizeof(new_pending_files[0]));
+ free(heap->files);
+ }
+ heap->files = new_pending_files;
+ heap->allocated = new_size;
+ }
+
+ file_id = file->id;
+
+ /*
+ * Start with hole at end, walk it up tree to find insertion point.
+ */
+ hole = heap->used++;
+ while (hole > 0) {
+ parent = (hole - 1)/2;
+ parent_id = heap->files[parent]->id;
+ if (file_id >= parent_id) {
+ heap->files[hole] = file;
+ return (ARCHIVE_OK);
+ }
+ /* Move parent into hole <==> move hole up tree. */
+ heap->files[hole] = heap->files[parent];
+ hole = parent;
+ }
+ heap->files[0] = file;
+
+ return (ARCHIVE_OK);
+}
+
+static struct xar_file *
+heap_get_entry(struct heap_queue *heap)
+{
+ uint64_t a_id, b_id, c_id;
+ int a, b, c;
+ struct xar_file *r, *tmp;
+
+ if (heap->used < 1)
+ return (NULL);
+
+ /*
+ * The first file in the list is the earliest; we'll return this.
+ */
+ r = heap->files[0];
+
+ /*
+ * Move the last item in the heap to the root of the tree
+ */
+ heap->files[0] = heap->files[--(heap->used)];
+
+ /*
+ * Rebalance the heap.
+ */
+ a = 0; /* Starting element and its heap key */
+ a_id = heap->files[a]->id;
+ for (;;) {
+ b = a + a + 1; /* First child */
+ if (b >= heap->used)
+ return (r);
+ b_id = heap->files[b]->id;
+ c = b + 1; /* Use second child if it is smaller. */
+ if (c < heap->used) {
+ c_id = heap->files[c]->id;
+ if (c_id < b_id) {
+ b = c;
+ b_id = c_id;
+ }
+ }
+ if (a_id <= b_id)
+ return (r);
+ tmp = heap->files[a];
+ heap->files[a] = heap->files[b];
+ heap->files[b] = tmp;
+ a = b;
+ }
+}
+
+static int
+add_link(struct archive_read *a, struct xar *xar, struct xar_file *file)
+{
+ struct hdlink *hdlink;
+
+ for (hdlink = xar->hdlink_list; hdlink != NULL; hdlink = hdlink->next) {
+ if (hdlink->id == file->link) {
+ file->hdnext = hdlink->files;
+ hdlink->cnt++;
+ hdlink->files = file;
+ return (ARCHIVE_OK);
+ }
+ }
+ hdlink = malloc(sizeof(*hdlink));
+ if (hdlink == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ file->hdnext = NULL;
+ hdlink->id = file->link;
+ hdlink->cnt = 1;
+ hdlink->files = file;
+ hdlink->next = xar->hdlink_list;
+ xar->hdlink_list = hdlink;
+ return (ARCHIVE_OK);
+}
+
+static void
+_checksum_init(struct chksumwork *sumwrk, int sum_alg)
+{
+ sumwrk->alg = sum_alg;
+ switch (sum_alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_init(&(sumwrk->sha1ctx));
+ break;
+ case CKSUM_MD5:
+ archive_md5_init(&(sumwrk->md5ctx));
+ break;
+ }
+}
+
+static void
+_checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
+{
+
+ switch (sumwrk->alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
+ break;
+ case CKSUM_MD5:
+ archive_md5_update(&(sumwrk->md5ctx), buff, size);
+ break;
+ }
+}
+
+static int
+_checksum_final(struct chksumwork *sumwrk, const void *val, size_t len)
+{
+ unsigned char sum[MAX_SUM_SIZE];
+ int r = ARCHIVE_OK;
+
+ switch (sumwrk->alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_final(&(sumwrk->sha1ctx), sum);
+ if (len != SHA1_SIZE ||
+ memcmp(val, sum, SHA1_SIZE) != 0)
+ r = ARCHIVE_FAILED;
+ break;
+ case CKSUM_MD5:
+ archive_md5_final(&(sumwrk->md5ctx), sum);
+ if (len != MD5_SIZE ||
+ memcmp(val, sum, MD5_SIZE) != 0)
+ r = ARCHIVE_FAILED;
+ break;
+ }
+ return (r);
+}
+
+static void
+checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)(a->format->data);
+ _checksum_init(&(xar->a_sumwrk), a_sum_alg);
+ _checksum_init(&(xar->e_sumwrk), e_sum_alg);
+}
+
+static void
+checksum_update(struct archive_read *a, const void *abuff, size_t asize,
+ const void *ebuff, size_t esize)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)(a->format->data);
+ _checksum_update(&(xar->a_sumwrk), abuff, asize);
+ _checksum_update(&(xar->e_sumwrk), ebuff, esize);
+}
+
+static int
+checksum_final(struct archive_read *a, const void *a_sum_val,
+ size_t a_sum_len, const void *e_sum_val, size_t e_sum_len)
+{
+ struct xar *xar;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len);
+ if (r == ARCHIVE_OK)
+ r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len);
+ if (r != ARCHIVE_OK)
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "Sumcheck error");
+ return (r);
+}
+
+static int
+decompression_init(struct archive_read *a, enum enctype encoding)
+{
+ struct xar *xar;
+ const char *detail;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ xar->rd_encoding = encoding;
+ switch (encoding) {
+ case NONE:
+ break;
+ case GZIP:
+ if (xar->stream_valid)
+ r = inflateReset(&(xar->stream));
+ else
+ r = inflateInit(&(xar->stream));
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Couldn't initialize zlib stream.");
+ return (ARCHIVE_FATAL);
+ }
+ xar->stream_valid = 1;
+ xar->stream.total_in = 0;
+ xar->stream.total_out = 0;
+ break;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ case BZIP2:
+ if (xar->bzstream_valid) {
+ BZ2_bzDecompressEnd(&(xar->bzstream));
+ xar->bzstream_valid = 0;
+ }
+ r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 0);
+ if (r == BZ_MEM_ERROR)
+ r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 1);
+ if (r != BZ_OK) {
+ int err = ARCHIVE_ERRNO_MISC;
+ detail = NULL;
+ switch (r) {
+ case BZ_PARAM_ERROR:
+ detail = "invalid setup parameter";
+ break;
+ case BZ_MEM_ERROR:
+ err = ENOMEM;
+ detail = "out of memory";
+ break;
+ case BZ_CONFIG_ERROR:
+ detail = "mis-compiled library";
+ break;
+ }
+ archive_set_error(&a->archive, err,
+ "Internal error initializing decompressor: %s",
+ detail == NULL ? "??" : detail);
+ xar->bzstream_valid = 0;
+ return (ARCHIVE_FATAL);
+ }
+ xar->bzstream_valid = 1;
+ xar->bzstream.total_in_lo32 = 0;
+ xar->bzstream.total_in_hi32 = 0;
+ xar->bzstream.total_out_lo32 = 0;
+ xar->bzstream.total_out_hi32 = 0;
+ break;
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT (1U << 30)
+#endif
+ case XZ:
+ case LZMA:
+ if (xar->lzstream_valid) {
+ lzma_end(&(xar->lzstream));
+ xar->lzstream_valid = 0;
+ }
+ if (xar->entry_encoding == XZ)
+ r = lzma_stream_decoder(&(xar->lzstream),
+ LZMA_MEMLIMIT,/* memlimit */
+ LZMA_CONCATENATED);
+ else
+ r = lzma_alone_decoder(&(xar->lzstream),
+ LZMA_MEMLIMIT);/* memlimit */
+ if (r != LZMA_OK) {
+ switch (r) {
+ case LZMA_MEM_ERROR:
+ archive_set_error(&a->archive,
+ ENOMEM,
+ "Internal error initializing "
+ "compression library: "
+ "Cannot allocate memory");
+ break;
+ case LZMA_OPTIONS_ERROR:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: "
+ "Invalid or unsupported options");
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "lzma library");
+ break;
+ }
+ return (ARCHIVE_FATAL);
+ }
+ xar->lzstream_valid = 1;
+ xar->lzstream.total_in = 0;
+ xar->lzstream.total_out = 0;
+ break;
+#endif
+ /*
+ * Unsupported compression.
+ */
+ default:
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+ case BZIP2:
+#endif
+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)
+ case LZMA:
+ case XZ:
+#endif
+ switch (xar->entry_encoding) {
+ case BZIP2: detail = "bzip2"; break;
+ case LZMA: detail = "lzma"; break;
+ case XZ: detail = "xz"; break;
+ default: detail = "??"; break;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s compression not supported on this platform",
+ detail);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+decompress(struct archive_read *a, const void **buff, size_t *outbytes,
+ const void *b, size_t *used)
+{
+ struct xar *xar;
+ void *outbuff;
+ size_t avail_in, avail_out;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ avail_in = *used;
+ outbuff = (void *)(uintptr_t)*buff;
+ if (outbuff == NULL) {
+ if (xar->outbuff == NULL) {
+ xar->outbuff = malloc(OUTBUFF_SIZE);
+ if (xar->outbuff == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory for out buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ outbuff = xar->outbuff;
+ *buff = outbuff;
+ avail_out = OUTBUFF_SIZE;
+ } else
+ avail_out = *outbytes;
+ switch (xar->rd_encoding) {
+ case GZIP:
+ xar->stream.next_in = (Bytef *)(uintptr_t)b;
+ xar->stream.avail_in = avail_in;
+ xar->stream.next_out = (unsigned char *)outbuff;
+ xar->stream.avail_out = avail_out;
+ r = inflate(&(xar->stream), 0);
+ switch (r) {
+ case Z_OK: /* Decompressor made some progress.*/
+ case Z_STREAM_END: /* Found end of stream. */
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "File decompression failed (%d)", r);
+ return (ARCHIVE_FATAL);
+ }
+ *used = avail_in - xar->stream.avail_in;
+ *outbytes = avail_out - xar->stream.avail_out;
+ break;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ case BZIP2:
+ xar->bzstream.next_in = (char *)(uintptr_t)b;
+ xar->bzstream.avail_in = avail_in;
+ xar->bzstream.next_out = (char *)outbuff;
+ xar->bzstream.avail_out = avail_out;
+ r = BZ2_bzDecompress(&(xar->bzstream));
+ switch (r) {
+ case BZ_STREAM_END: /* Found end of stream. */
+ switch (BZ2_bzDecompressEnd(&(xar->bzstream))) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up decompressor");
+ return (ARCHIVE_FATAL);
+ }
+ xar->bzstream_valid = 0;
+ /* FALLTHROUGH */
+ case BZ_OK: /* Decompressor made some progress. */
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "bzip decompression failed");
+ return (ARCHIVE_FATAL);
+ }
+ *used = avail_in - xar->bzstream.avail_in;
+ *outbytes = avail_out - xar->bzstream.avail_out;
+ break;
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+ case LZMA:
+ case XZ:
+ xar->lzstream.next_in = b;
+ xar->lzstream.avail_in = avail_in;
+ xar->lzstream.next_out = (unsigned char *)outbuff;
+ xar->lzstream.avail_out = avail_out;
+ r = lzma_code(&(xar->lzstream), LZMA_RUN);
+ switch (r) {
+ case LZMA_STREAM_END: /* Found end of stream. */
+ lzma_end(&(xar->lzstream));
+ xar->lzstream_valid = 0;
+ /* FALLTHROUGH */
+ case LZMA_OK: /* Decompressor made some progress. */
+ break;
+ default:
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "%s decompression failed(%d)",
+ (xar->entry_encoding == XZ)?"xz":"lzma",
+ r);
+ return (ARCHIVE_FATAL);
+ }
+ *used = avail_in - xar->lzstream.avail_in;
+ *outbytes = avail_out - xar->lzstream.avail_out;
+ break;
+#endif
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+ case BZIP2:
+#endif
+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)
+ case LZMA:
+ case XZ:
+#endif
+ case NONE:
+ default:
+ if (outbuff == xar->outbuff) {
+ *buff = b;
+ *used = avail_in;
+ *outbytes = avail_in;
+ } else {
+ if (avail_out > avail_in)
+ avail_out = avail_in;
+ memcpy(outbuff, b, avail_out);
+ *used = avail_out;
+ *outbytes = avail_out;
+ }
+ break;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+decompression_cleanup(struct archive_read *a)
+{
+ struct xar *xar;
+ int r;
+
+ xar = (struct xar *)(a->format->data);
+ r = ARCHIVE_OK;
+ if (xar->stream_valid) {
+ if (inflateEnd(&(xar->stream)) != Z_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up zlib decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ }
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ if (xar->bzstream_valid) {
+ if (BZ2_bzDecompressEnd(&(xar->bzstream)) != BZ_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up bzip2 decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ }
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+ if (xar->lzstream_valid)
+ lzma_end(&(xar->lzstream));
+#elif defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+ if (xar->lzstream_valid) {
+ if (lzmadec_end(&(xar->lzstream)) != LZMADEC_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up lzmadec decompressor");
+ r = ARCHIVE_FATAL;
+ }
+ }
+#endif
+ return (r);
+}
+
+static void
+checksum_cleanup(struct archive_read *a) {
+ struct xar *xar;
+
+ xar = (struct xar *)(a->format->data);
+
+ _checksum_final(&(xar->a_sumwrk), NULL, 0);
+ _checksum_final(&(xar->e_sumwrk), NULL, 0);
+}
+
+static void
+xmlattr_cleanup(struct xmlattr_list *list)
+{
+ struct xmlattr *attr, *next;
+
+ attr = list->first;
+ while (attr != NULL) {
+ next = attr->next;
+ free(attr->name);
+ free(attr->value);
+ free(attr);
+ attr = next;
+ }
+ list->first = NULL;
+ list->last = &(list->first);
+}
+
+static int
+file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
+{
+ struct xar_file *file;
+ struct xmlattr *attr;
+
+ file = calloc(1, sizeof(*file));
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ file->parent = xar->file;
+ file->mode = 0777 | AE_IFREG;
+ file->atime = 0;
+ file->mtime = 0;
+ xar->file = file;
+ xar->xattr = NULL;
+ for (attr = list->first; attr != NULL; attr = attr->next) {
+ if (strcmp(attr->name, "id") == 0)
+ file->id = atol10(attr->value, strlen(attr->value));
+ }
+ file->nlink = 1;
+ if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+static void
+file_free(struct xar_file *file)
+{
+ struct xattr *xattr;
+
+ archive_string_free(&(file->pathname));
+ archive_string_free(&(file->symlink));
+ archive_string_free(&(file->uname));
+ archive_string_free(&(file->gname));
+ archive_string_free(&(file->hardlink));
+ xattr = file->xattr_list;
+ while (xattr != NULL) {
+ struct xattr *next;
+
+ next = xattr->next;
+ xattr_free(xattr);
+ xattr = next;
+ }
+
+ free(file);
+}
+
+static int
+xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
+{
+ struct xattr *xattr, **nx;
+ struct xmlattr *attr;
+
+ xattr = calloc(1, sizeof(*xattr));
+ if (xattr == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ xar->xattr = xattr;
+ for (attr = list->first; attr != NULL; attr = attr->next) {
+ if (strcmp(attr->name, "id") == 0)
+ xattr->id = atol10(attr->value, strlen(attr->value));
+ }
+ /* Chain to xattr list. */
+ for (nx = &(xar->file->xattr_list);
+ *nx != NULL; nx = &((*nx)->next)) {
+ if (xattr->id < (*nx)->id)
+ break;
+ }
+ xattr->next = *nx;
+ *nx = xattr;
+
+ return (ARCHIVE_OK);
+}
+
+static void
+xattr_free(struct xattr *xattr)
+{
+ archive_string_free(&(xattr->name));
+ free(xattr);
+}
+
+static int
+getencoding(struct xmlattr_list *list)
+{
+ struct xmlattr *attr;
+ enum enctype encoding = NONE;
+
+ for (attr = list->first; attr != NULL; attr = attr->next) {
+ if (strcmp(attr->name, "style") == 0) {
+ if (strcmp(attr->value, "application/octet-stream") == 0)
+ encoding = NONE;
+ else if (strcmp(attr->value, "application/x-gzip") == 0)
+ encoding = GZIP;
+ else if (strcmp(attr->value, "application/x-bzip2") == 0)
+ encoding = BZIP2;
+ else if (strcmp(attr->value, "application/x-lzma") == 0)
+ encoding = LZMA;
+ else if (strcmp(attr->value, "application/x-xz") == 0)
+ encoding = XZ;
+ }
+ }
+ return (encoding);
+}
+
+static int
+getsumalgorithm(struct xmlattr_list *list)
+{
+ struct xmlattr *attr;
+ int alg = CKSUM_NONE;
+
+ for (attr = list->first; attr != NULL; attr = attr->next) {
+ if (strcmp(attr->name, "style") == 0) {
+ const char *v = attr->value;
+ if ((v[0] == 'S' || v[0] == 's') &&
+ (v[1] == 'H' || v[1] == 'h') &&
+ (v[2] == 'A' || v[2] == 'a') &&
+ v[3] == '1' && v[4] == '\0')
+ alg = CKSUM_SHA1;
+ if ((v[0] == 'M' || v[0] == 'm') &&
+ (v[1] == 'D' || v[1] == 'd') &&
+ v[2] == '5' && v[3] == '\0')
+ alg = CKSUM_MD5;
+ }
+ }
+ return (alg);
+}
+
+static int
+unknowntag_start(struct archive_read *a, struct xar *xar, const char *name)
+{
+ struct unknown_tag *tag;
+
+ tag = malloc(sizeof(*tag));
+ if (tag == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ tag->next = xar->unknowntags;
+ archive_string_init(&(tag->name));
+ archive_strcpy(&(tag->name), name);
+ if (xar->unknowntags == NULL) {
+#if DEBUG
+ fprintf(stderr, "UNKNOWNTAG_START:%s\n", name);
+#endif
+ xar->xmlsts_unknown = xar->xmlsts;
+ xar->xmlsts = UNKNOWN;
+ }
+ xar->unknowntags = tag;
+ return (ARCHIVE_OK);
+}
+
+static void
+unknowntag_end(struct xar *xar, const char *name)
+{
+ struct unknown_tag *tag;
+
+ tag = xar->unknowntags;
+ if (tag == NULL || name == NULL)
+ return;
+ if (strcmp(tag->name.s, name) == 0) {
+ xar->unknowntags = tag->next;
+ archive_string_free(&(tag->name));
+ free(tag);
+ if (xar->unknowntags == NULL) {
+#if DEBUG
+ fprintf(stderr, "UNKNOWNTAG_END:%s\n", name);
+#endif
+ xar->xmlsts = xar->xmlsts_unknown;
+ }
+ }
+}
+
+static int
+xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)
+{
+ struct xar *xar;
+ struct xmlattr *attr;
+
+ xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+ fprintf(stderr, "xml_sta:[%s]\n", name);
+ for (attr = list->first; attr != NULL; attr = attr->next)
+ fprintf(stderr, " attr:\"%s\"=\"%s\"\n",
+ attr->name, attr->value);
+#endif
+ xar->base64text = 0;
+ switch (xar->xmlsts) {
+ case INIT:
+ if (strcmp(name, "xar") == 0)
+ xar->xmlsts = XAR;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case XAR:
+ if (strcmp(name, "toc") == 0)
+ xar->xmlsts = TOC;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case TOC:
+ if (strcmp(name, "creation-time") == 0)
+ xar->xmlsts = TOC_CREATION_TIME;
+ else if (strcmp(name, "checksum") == 0)
+ xar->xmlsts = TOC_CHECKSUM;
+ else if (strcmp(name, "file") == 0) {
+ if (file_new(a, xar, list) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ xar->xmlsts = TOC_FILE;
+ }
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case TOC_CHECKSUM:
+ if (strcmp(name, "offset") == 0)
+ xar->xmlsts = TOC_CHECKSUM_OFFSET;
+ else if (strcmp(name, "size") == 0)
+ xar->xmlsts = TOC_CHECKSUM_SIZE;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case TOC_FILE:
+ if (strcmp(name, "file") == 0) {
+ if (file_new(a, xar, list) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ else if (strcmp(name, "data") == 0)
+ xar->xmlsts = FILE_DATA;
+ else if (strcmp(name, "ea") == 0) {
+ if (xattr_new(a, xar, list) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ xar->xmlsts = FILE_EA;
+ }
+ else if (strcmp(name, "ctime") == 0)
+ xar->xmlsts = FILE_CTIME;
+ else if (strcmp(name, "mtime") == 0)
+ xar->xmlsts = FILE_MTIME;
+ else if (strcmp(name, "atime") == 0)
+ xar->xmlsts = FILE_ATIME;
+ else if (strcmp(name, "group") == 0)
+ xar->xmlsts = FILE_GROUP;
+ else if (strcmp(name, "gid") == 0)
+ xar->xmlsts = FILE_GID;
+ else if (strcmp(name, "user") == 0)
+ xar->xmlsts = FILE_USER;
+ else if (strcmp(name, "uid") == 0)
+ xar->xmlsts = FILE_UID;
+ else if (strcmp(name, "mode") == 0)
+ xar->xmlsts = FILE_MODE;
+ else if (strcmp(name, "device") == 0)
+ xar->xmlsts = FILE_DEVICE;
+ else if (strcmp(name, "deviceno") == 0)
+ xar->xmlsts = FILE_DEVICENO;
+ else if (strcmp(name, "inode") == 0)
+ xar->xmlsts = FILE_INODE;
+ else if (strcmp(name, "link") == 0)
+ xar->xmlsts = FILE_LINK;
+ else if (strcmp(name, "type") == 0) {
+ xar->xmlsts = FILE_TYPE;
+ for (attr = list->first; attr != NULL;
+ attr = attr->next) {
+ if (strcmp(attr->name, "link") != 0)
+ continue;
+ if (strcmp(attr->value, "original") == 0) {
+ xar->file->hdnext = xar->hdlink_orgs;
+ xar->hdlink_orgs = xar->file;
+ } else {
+ xar->file->link = (unsigned)atol10(attr->value,
+ strlen(attr->value));
+ if (xar->file->link > 0)
+ if (add_link(a, xar, xar->file) != ARCHIVE_OK) {
+ return (ARCHIVE_FATAL);
+ };
+ }
+ }
+ }
+ else if (strcmp(name, "name") == 0) {
+ xar->xmlsts = FILE_NAME;
+ for (attr = list->first; attr != NULL;
+ attr = attr->next) {
+ if (strcmp(attr->name, "enctype") == 0 &&
+ strcmp(attr->value, "base64") == 0)
+ xar->base64text = 1;
+ }
+ }
+ else if (strcmp(name, "acl") == 0)
+ xar->xmlsts = FILE_ACL;
+ else if (strcmp(name, "flags") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ else if (strcmp(name, "ext2") == 0)
+ xar->xmlsts = FILE_EXT2;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_DATA:
+ if (strcmp(name, "length") == 0)
+ xar->xmlsts = FILE_DATA_LENGTH;
+ else if (strcmp(name, "offset") == 0)
+ xar->xmlsts = FILE_DATA_OFFSET;
+ else if (strcmp(name, "size") == 0)
+ xar->xmlsts = FILE_DATA_SIZE;
+ else if (strcmp(name, "encoding") == 0) {
+ xar->xmlsts = FILE_DATA_ENCODING;
+ xar->file->encoding = getencoding(list);
+ }
+ else if (strcmp(name, "archived-checksum") == 0) {
+ xar->xmlsts = FILE_DATA_A_CHECKSUM;
+ xar->file->a_sum.alg = getsumalgorithm(list);
+ }
+ else if (strcmp(name, "extracted-checksum") == 0) {
+ xar->xmlsts = FILE_DATA_E_CHECKSUM;
+ xar->file->e_sum.alg = getsumalgorithm(list);
+ }
+ else if (strcmp(name, "content") == 0)
+ xar->xmlsts = FILE_DATA_CONTENT;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_DEVICE:
+ if (strcmp(name, "major") == 0)
+ xar->xmlsts = FILE_DEVICE_MAJOR;
+ else if (strcmp(name, "minor") == 0)
+ xar->xmlsts = FILE_DEVICE_MINOR;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_DATA_CONTENT:
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_EA:
+ if (strcmp(name, "length") == 0)
+ xar->xmlsts = FILE_EA_LENGTH;
+ else if (strcmp(name, "offset") == 0)
+ xar->xmlsts = FILE_EA_OFFSET;
+ else if (strcmp(name, "size") == 0)
+ xar->xmlsts = FILE_EA_SIZE;
+ else if (strcmp(name, "encoding") == 0) {
+ xar->xmlsts = FILE_EA_ENCODING;
+ xar->xattr->encoding = getencoding(list);
+ } else if (strcmp(name, "archived-checksum") == 0)
+ xar->xmlsts = FILE_EA_A_CHECKSUM;
+ else if (strcmp(name, "extracted-checksum") == 0)
+ xar->xmlsts = FILE_EA_E_CHECKSUM;
+ else if (strcmp(name, "name") == 0)
+ xar->xmlsts = FILE_EA_NAME;
+ else if (strcmp(name, "fstype") == 0)
+ xar->xmlsts = FILE_EA_FSTYPE;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_ACL:
+ if (strcmp(name, "appleextended") == 0)
+ xar->xmlsts = FILE_ACL_APPLEEXTENDED;
+ else if (strcmp(name, "default") == 0)
+ xar->xmlsts = FILE_ACL_DEFAULT;
+ else if (strcmp(name, "access") == 0)
+ xar->xmlsts = FILE_ACL_ACCESS;
+ else
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_FLAGS:
+ if (!xml_parse_file_flags(xar, name))
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case FILE_EXT2:
+ if (!xml_parse_file_ext2(xar, name))
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ case TOC_CREATION_TIME:
+ case TOC_CHECKSUM_OFFSET:
+ case TOC_CHECKSUM_SIZE:
+ case FILE_DATA_LENGTH:
+ case FILE_DATA_OFFSET:
+ case FILE_DATA_SIZE:
+ case FILE_DATA_ENCODING:
+ case FILE_DATA_A_CHECKSUM:
+ case FILE_DATA_E_CHECKSUM:
+ case FILE_EA_LENGTH:
+ case FILE_EA_OFFSET:
+ case FILE_EA_SIZE:
+ case FILE_EA_ENCODING:
+ case FILE_EA_A_CHECKSUM:
+ case FILE_EA_E_CHECKSUM:
+ case FILE_EA_NAME:
+ case FILE_EA_FSTYPE:
+ case FILE_CTIME:
+ case FILE_MTIME:
+ case FILE_ATIME:
+ case FILE_GROUP:
+ case FILE_GID:
+ case FILE_USER:
+ case FILE_UID:
+ case FILE_INODE:
+ case FILE_DEVICE_MAJOR:
+ case FILE_DEVICE_MINOR:
+ case FILE_DEVICENO:
+ case FILE_MODE:
+ case FILE_TYPE:
+ case FILE_LINK:
+ case FILE_NAME:
+ case FILE_ACL_DEFAULT:
+ case FILE_ACL_ACCESS:
+ case FILE_ACL_APPLEEXTENDED:
+ case FILE_FLAGS_USER_NODUMP:
+ case FILE_FLAGS_USER_IMMUTABLE:
+ case FILE_FLAGS_USER_APPEND:
+ case FILE_FLAGS_USER_OPAQUE:
+ case FILE_FLAGS_USER_NOUNLINK:
+ case FILE_FLAGS_SYS_ARCHIVED:
+ case FILE_FLAGS_SYS_IMMUTABLE:
+ case FILE_FLAGS_SYS_APPEND:
+ case FILE_FLAGS_SYS_NOUNLINK:
+ case FILE_FLAGS_SYS_SNAPSHOT:
+ case FILE_EXT2_SecureDeletion:
+ case FILE_EXT2_Undelete:
+ case FILE_EXT2_Compress:
+ case FILE_EXT2_Synchronous:
+ case FILE_EXT2_Immutable:
+ case FILE_EXT2_AppendOnly:
+ case FILE_EXT2_NoDump:
+ case FILE_EXT2_NoAtime:
+ case FILE_EXT2_CompDirty:
+ case FILE_EXT2_CompBlock:
+ case FILE_EXT2_NoCompBlock:
+ case FILE_EXT2_CompError:
+ case FILE_EXT2_BTree:
+ case FILE_EXT2_HashIndexed:
+ case FILE_EXT2_iMagic:
+ case FILE_EXT2_Journaled:
+ case FILE_EXT2_NoTail:
+ case FILE_EXT2_DirSync:
+ case FILE_EXT2_TopDir:
+ case FILE_EXT2_Reserved:
+ case UNKNOWN:
+ if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ break;
+ }
+ return (ARCHIVE_OK);
+}
+
+static void
+xml_end(void *userData, const char *name)
+{
+ struct archive_read *a;
+ struct xar *xar;
+
+ a = (struct archive_read *)userData;
+ xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+ fprintf(stderr, "xml_end:[%s]\n", name);
+#endif
+ switch (xar->xmlsts) {
+ case INIT:
+ break;
+ case XAR:
+ if (strcmp(name, "xar") == 0)
+ xar->xmlsts = INIT;
+ break;
+ case TOC:
+ if (strcmp(name, "toc") == 0)
+ xar->xmlsts = XAR;
+ break;
+ case TOC_CREATION_TIME:
+ if (strcmp(name, "creation-time") == 0)
+ xar->xmlsts = TOC;
+ break;
+ case TOC_CHECKSUM:
+ if (strcmp(name, "checksum") == 0)
+ xar->xmlsts = TOC;
+ break;
+ case TOC_CHECKSUM_OFFSET:
+ if (strcmp(name, "offset") == 0)
+ xar->xmlsts = TOC_CHECKSUM;
+ break;
+ case TOC_CHECKSUM_SIZE:
+ if (strcmp(name, "size") == 0)
+ xar->xmlsts = TOC_CHECKSUM;
+ break;
+ case TOC_FILE:
+ if (strcmp(name, "file") == 0) {
+ if (xar->file->parent != NULL &&
+ ((xar->file->mode & AE_IFMT) == AE_IFDIR))
+ xar->file->parent->subdirs++;
+ xar->file = xar->file->parent;
+ if (xar->file == NULL)
+ xar->xmlsts = TOC;
+ }
+ break;
+ case FILE_DATA:
+ if (strcmp(name, "data") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_DATA_LENGTH:
+ if (strcmp(name, "length") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_OFFSET:
+ if (strcmp(name, "offset") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_SIZE:
+ if (strcmp(name, "size") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_ENCODING:
+ if (strcmp(name, "encoding") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_A_CHECKSUM:
+ if (strcmp(name, "archived-checksum") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_E_CHECKSUM:
+ if (strcmp(name, "extracted-checksum") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_DATA_CONTENT:
+ if (strcmp(name, "content") == 0)
+ xar->xmlsts = FILE_DATA;
+ break;
+ case FILE_EA:
+ if (strcmp(name, "ea") == 0) {
+ xar->xmlsts = TOC_FILE;
+ xar->xattr = NULL;
+ }
+ break;
+ case FILE_EA_LENGTH:
+ if (strcmp(name, "length") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_OFFSET:
+ if (strcmp(name, "offset") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_SIZE:
+ if (strcmp(name, "size") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_ENCODING:
+ if (strcmp(name, "encoding") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_A_CHECKSUM:
+ if (strcmp(name, "archived-checksum") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_E_CHECKSUM:
+ if (strcmp(name, "extracted-checksum") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_NAME:
+ if (strcmp(name, "name") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_EA_FSTYPE:
+ if (strcmp(name, "fstype") == 0)
+ xar->xmlsts = FILE_EA;
+ break;
+ case FILE_CTIME:
+ if (strcmp(name, "ctime") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_MTIME:
+ if (strcmp(name, "mtime") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_ATIME:
+ if (strcmp(name, "atime") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_GROUP:
+ if (strcmp(name, "group") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_GID:
+ if (strcmp(name, "gid") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_USER:
+ if (strcmp(name, "user") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_UID:
+ if (strcmp(name, "uid") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_MODE:
+ if (strcmp(name, "mode") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_DEVICE:
+ if (strcmp(name, "device") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_DEVICE_MAJOR:
+ if (strcmp(name, "major") == 0)
+ xar->xmlsts = FILE_DEVICE;
+ break;
+ case FILE_DEVICE_MINOR:
+ if (strcmp(name, "minor") == 0)
+ xar->xmlsts = FILE_DEVICE;
+ break;
+ case FILE_DEVICENO:
+ if (strcmp(name, "deviceno") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_INODE:
+ if (strcmp(name, "inode") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_LINK:
+ if (strcmp(name, "link") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_TYPE:
+ if (strcmp(name, "type") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_NAME:
+ if (strcmp(name, "name") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_ACL:
+ if (strcmp(name, "acl") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_ACL_DEFAULT:
+ if (strcmp(name, "default") == 0)
+ xar->xmlsts = FILE_ACL;
+ break;
+ case FILE_ACL_ACCESS:
+ if (strcmp(name, "access") == 0)
+ xar->xmlsts = FILE_ACL;
+ break;
+ case FILE_ACL_APPLEEXTENDED:
+ if (strcmp(name, "appleextended") == 0)
+ xar->xmlsts = FILE_ACL;
+ break;
+ case FILE_FLAGS:
+ if (strcmp(name, "flags") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_FLAGS_USER_NODUMP:
+ if (strcmp(name, "UserNoDump") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_USER_IMMUTABLE:
+ if (strcmp(name, "UserImmutable") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_USER_APPEND:
+ if (strcmp(name, "UserAppend") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_USER_OPAQUE:
+ if (strcmp(name, "UserOpaque") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_USER_NOUNLINK:
+ if (strcmp(name, "UserNoUnlink") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_ARCHIVED:
+ if (strcmp(name, "SystemArchived") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_IMMUTABLE:
+ if (strcmp(name, "SystemImmutable") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_APPEND:
+ if (strcmp(name, "SystemAppend") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_NOUNLINK:
+ if (strcmp(name, "SystemNoUnlink") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_FLAGS_SYS_SNAPSHOT:
+ if (strcmp(name, "SystemSnapshot") == 0)
+ xar->xmlsts = FILE_FLAGS;
+ break;
+ case FILE_EXT2:
+ if (strcmp(name, "ext2") == 0)
+ xar->xmlsts = TOC_FILE;
+ break;
+ case FILE_EXT2_SecureDeletion:
+ if (strcmp(name, "SecureDeletion") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Undelete:
+ if (strcmp(name, "Undelete") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Compress:
+ if (strcmp(name, "Compress") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Synchronous:
+ if (strcmp(name, "Synchronous") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Immutable:
+ if (strcmp(name, "Immutable") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_AppendOnly:
+ if (strcmp(name, "AppendOnly") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_NoDump:
+ if (strcmp(name, "NoDump") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_NoAtime:
+ if (strcmp(name, "NoAtime") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_CompDirty:
+ if (strcmp(name, "CompDirty") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_CompBlock:
+ if (strcmp(name, "CompBlock") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_NoCompBlock:
+ if (strcmp(name, "NoCompBlock") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_CompError:
+ if (strcmp(name, "CompError") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_BTree:
+ if (strcmp(name, "BTree") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_HashIndexed:
+ if (strcmp(name, "HashIndexed") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_iMagic:
+ if (strcmp(name, "iMagic") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Journaled:
+ if (strcmp(name, "Journaled") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_NoTail:
+ if (strcmp(name, "NoTail") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_DirSync:
+ if (strcmp(name, "DirSync") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_TopDir:
+ if (strcmp(name, "TopDir") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case FILE_EXT2_Reserved:
+ if (strcmp(name, "Reserved") == 0)
+ xar->xmlsts = FILE_EXT2;
+ break;
+ case UNKNOWN:
+ unknowntag_end(xar, name);
+ break;
+ }
+}
+
+static const int base64[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 00 - 0F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 10 - 1F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62, -1, -1, -1, 63, /* 20 - 2F */
+ 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, -1, -1, -1, -1, -1, -1, /* 30 - 3F */
+ -1, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */
+ 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, -1, -1, -1, -1, -1, /* 50 - 5F */
+ -1, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
+ 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, -1, -1, -1, -1, -1, /* 70 - 7F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 8F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* 90 - 9F */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* A0 - AF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* B0 - BF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* C0 - CF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* D0 - DF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* E0 - EF */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, /* F0 - FF */
+};
+
+static void
+strappend_base64(struct xar *xar,
+ struct archive_string *as, const char *s, size_t l)
+{
+ unsigned char buff[256];
+ unsigned char *out;
+ const unsigned char *b;
+ size_t len;
+
+ (void)xar; /* UNUSED */
+ len = 0;
+ out = buff;
+ b = (const unsigned char *)s;
+ while (l > 0) {
+ int n = 0;
+
+ if (base64[b[0]] < 0 || base64[b[1]] < 0)
+ break;
+ n = base64[*b++] << 18;
+ n |= base64[*b++] << 12;
+ *out++ = n >> 16;
+ len++;
+ l -= 2;
+
+ if (l > 0) {
+ if (base64[*b] < 0)
+ break;
+ n |= base64[*b++] << 6;
+ *out++ = (n >> 8) & 0xFF;
+ len++;
+ --l;
+ }
+ if (l > 0) {
+ if (base64[*b] < 0)
+ break;
+ n |= base64[*b++];
+ *out++ = n & 0xFF;
+ len++;
+ --l;
+ }
+ if (len+3 >= sizeof(buff)) {
+ archive_strncat(as, (const char *)buff, len);
+ len = 0;
+ out = buff;
+ }
+ }
+ if (len > 0)
+ archive_strncat(as, (const char *)buff, len);
+}
+
+static int
+is_string(const char *known, const char *data, size_t len)
+{
+ if (strlen(known) != len)
+ return -1;
+ return memcmp(data, known, len);
+}
+
+static void
+xml_data(void *userData, const char *s, int len)
+{
+ struct archive_read *a;
+ struct xar *xar;
+
+ a = (struct archive_read *)userData;
+ xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+ {
+ char buff[1024];
+ if (len > (int)(sizeof(buff)-1))
+ len = (int)(sizeof(buff)-1);
+ strncpy(buff, s, len);
+ buff[len] = 0;
+ fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff);
+ }
+#endif
+ switch (xar->xmlsts) {
+ case TOC_CHECKSUM_OFFSET:
+ xar->toc_chksum_offset = atol10(s, len);
+ break;
+ case TOC_CHECKSUM_SIZE:
+ xar->toc_chksum_size = atol10(s, len);
+ break;
+ default:
+ break;
+ }
+ if (xar->file == NULL)
+ return;
+
+ switch (xar->xmlsts) {
+ case FILE_NAME:
+ if (xar->file->parent != NULL) {
+ archive_string_concat(&(xar->file->pathname),
+ &(xar->file->parent->pathname));
+ archive_strappend_char(&(xar->file->pathname), '/');
+ }
+ xar->file->has |= HAS_PATHNAME;
+ if (xar->base64text) {
+ strappend_base64(xar,
+ &(xar->file->pathname), s, len);
+ } else
+ archive_strncat(&(xar->file->pathname), s, len);
+ break;
+ case FILE_LINK:
+ xar->file->has |= HAS_SYMLINK;
+ archive_strncpy(&(xar->file->symlink), s, len);
+ break;
+ case FILE_TYPE:
+ if (is_string("file", s, len) == 0 ||
+ is_string("hardlink", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFREG;
+ if (is_string("directory", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFDIR;
+ if (is_string("symlink", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFLNK;
+ if (is_string("character special", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFCHR;
+ if (is_string("block special", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFBLK;
+ if (is_string("socket", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFSOCK;
+ if (is_string("fifo", s, len) == 0)
+ xar->file->mode =
+ (xar->file->mode & ~AE_IFMT) | AE_IFIFO;
+ xar->file->has |= HAS_TYPE;
+ break;
+ case FILE_INODE:
+ xar->file->has |= HAS_INO;
+ xar->file->ino64 = atol10(s, len);
+ break;
+ case FILE_DEVICE_MAJOR:
+ xar->file->has |= HAS_DEVMAJOR;
+ xar->file->devmajor = (dev_t)atol10(s, len);
+ break;
+ case FILE_DEVICE_MINOR:
+ xar->file->has |= HAS_DEVMINOR;
+ xar->file->devminor = (dev_t)atol10(s, len);
+ break;
+ case FILE_DEVICENO:
+ xar->file->has |= HAS_DEV;
+ xar->file->dev = (dev_t)atol10(s, len);
+ break;
+ case FILE_MODE:
+ xar->file->has |= HAS_MODE;
+ xar->file->mode =
+ (xar->file->mode & AE_IFMT) |
+ ((mode_t)(atol8(s, len)) & ~AE_IFMT);
+ break;
+ case FILE_GROUP:
+ xar->file->has |= HAS_GID;
+ archive_strncpy(&(xar->file->gname), s, len);
+ break;
+ case FILE_GID:
+ xar->file->has |= HAS_GID;
+ xar->file->gid = atol10(s, len);
+ break;
+ case FILE_USER:
+ xar->file->has |= HAS_UID;
+ archive_strncpy(&(xar->file->uname), s, len);
+ break;
+ case FILE_UID:
+ xar->file->has |= HAS_UID;
+ xar->file->uid = atol10(s, len);
+ break;
+ case FILE_CTIME:
+ xar->file->has |= HAS_TIME | HAS_CTIME;
+ xar->file->ctime = parse_time(s, len);
+ break;
+ case FILE_MTIME:
+ xar->file->has |= HAS_TIME | HAS_MTIME;
+ xar->file->mtime = parse_time(s, len);
+ break;
+ case FILE_ATIME:
+ xar->file->has |= HAS_TIME | HAS_ATIME;
+ xar->file->atime = parse_time(s, len);
+ break;
+ case FILE_DATA_LENGTH:
+ xar->file->has |= HAS_DATA;
+ xar->file->length = atol10(s, len);
+ break;
+ case FILE_DATA_OFFSET:
+ xar->file->has |= HAS_DATA;
+ xar->file->offset = atol10(s, len);
+ break;
+ case FILE_DATA_SIZE:
+ xar->file->has |= HAS_DATA;
+ xar->file->size = atol10(s, len);
+ break;
+ case FILE_DATA_A_CHECKSUM:
+ xar->file->a_sum.len = atohex(xar->file->a_sum.val,
+ sizeof(xar->file->a_sum.val), s, len);
+ break;
+ case FILE_DATA_E_CHECKSUM:
+ xar->file->e_sum.len = atohex(xar->file->e_sum.val,
+ sizeof(xar->file->e_sum.val), s, len);
+ break;
+ case FILE_EA_LENGTH:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->length = atol10(s, len);
+ break;
+ case FILE_EA_OFFSET:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->offset = atol10(s, len);
+ break;
+ case FILE_EA_SIZE:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->size = atol10(s, len);
+ break;
+ case FILE_EA_A_CHECKSUM:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->a_sum.len = atohex(xar->xattr->a_sum.val,
+ sizeof(xar->xattr->a_sum.val), s, len);
+ break;
+ case FILE_EA_E_CHECKSUM:
+ xar->file->has |= HAS_XATTR;
+ xar->xattr->e_sum.len = atohex(xar->xattr->e_sum.val,
+ sizeof(xar->xattr->e_sum.val), s, len);
+ break;
+ case FILE_EA_NAME:
+ xar->file->has |= HAS_XATTR;
+ archive_strncpy(&(xar->xattr->name), s, len);
+ break;
+ case FILE_EA_FSTYPE:
+ xar->file->has |= HAS_XATTR;
+ archive_strncpy(&(xar->xattr->fstype), s, len);
+ break;
+ break;
+ case FILE_ACL_DEFAULT:
+ case FILE_ACL_ACCESS:
+ case FILE_ACL_APPLEEXTENDED:
+ xar->file->has |= HAS_ACL;
+ /* TODO */
+ break;
+ case INIT:
+ case XAR:
+ case TOC:
+ case TOC_CREATION_TIME:
+ case TOC_CHECKSUM:
+ case TOC_CHECKSUM_OFFSET:
+ case TOC_CHECKSUM_SIZE:
+ case TOC_FILE:
+ case FILE_DATA:
+ case FILE_DATA_ENCODING:
+ case FILE_DATA_CONTENT:
+ case FILE_DEVICE:
+ case FILE_EA:
+ case FILE_EA_ENCODING:
+ case FILE_ACL:
+ case FILE_FLAGS:
+ case FILE_FLAGS_USER_NODUMP:
+ case FILE_FLAGS_USER_IMMUTABLE:
+ case FILE_FLAGS_USER_APPEND:
+ case FILE_FLAGS_USER_OPAQUE:
+ case FILE_FLAGS_USER_NOUNLINK:
+ case FILE_FLAGS_SYS_ARCHIVED:
+ case FILE_FLAGS_SYS_IMMUTABLE:
+ case FILE_FLAGS_SYS_APPEND:
+ case FILE_FLAGS_SYS_NOUNLINK:
+ case FILE_FLAGS_SYS_SNAPSHOT:
+ case FILE_EXT2:
+ case FILE_EXT2_SecureDeletion:
+ case FILE_EXT2_Undelete:
+ case FILE_EXT2_Compress:
+ case FILE_EXT2_Synchronous:
+ case FILE_EXT2_Immutable:
+ case FILE_EXT2_AppendOnly:
+ case FILE_EXT2_NoDump:
+ case FILE_EXT2_NoAtime:
+ case FILE_EXT2_CompDirty:
+ case FILE_EXT2_CompBlock:
+ case FILE_EXT2_NoCompBlock:
+ case FILE_EXT2_CompError:
+ case FILE_EXT2_BTree:
+ case FILE_EXT2_HashIndexed:
+ case FILE_EXT2_iMagic:
+ case FILE_EXT2_Journaled:
+ case FILE_EXT2_NoTail:
+ case FILE_EXT2_DirSync:
+ case FILE_EXT2_TopDir:
+ case FILE_EXT2_Reserved:
+ case UNKNOWN:
+ break;
+ }
+}
+
+/*
+ * BSD file flags.
+ */
+static int
+xml_parse_file_flags(struct xar *xar, const char *name)
+{
+ const char *flag = NULL;
+
+ if (strcmp(name, "UserNoDump") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_NODUMP;
+ flag = "nodump";
+ }
+ else if (strcmp(name, "UserImmutable") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_IMMUTABLE;
+ flag = "uimmutable";
+ }
+ else if (strcmp(name, "UserAppend") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_APPEND;
+ flag = "uappend";
+ }
+ else if (strcmp(name, "UserOpaque") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_OPAQUE;
+ flag = "opaque";
+ }
+ else if (strcmp(name, "UserNoUnlink") == 0) {
+ xar->xmlsts = FILE_FLAGS_USER_NOUNLINK;
+ flag = "nouunlink";
+ }
+ else if (strcmp(name, "SystemArchived") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_ARCHIVED;
+ flag = "archived";
+ }
+ else if (strcmp(name, "SystemImmutable") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_IMMUTABLE;
+ flag = "simmutable";
+ }
+ else if (strcmp(name, "SystemAppend") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_APPEND;
+ flag = "sappend";
+ }
+ else if (strcmp(name, "SystemNoUnlink") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_NOUNLINK;
+ flag = "nosunlink";
+ }
+ else if (strcmp(name, "SystemSnapshot") == 0) {
+ xar->xmlsts = FILE_FLAGS_SYS_SNAPSHOT;
+ flag = "snapshot";
+ }
+
+ if (flag == NULL)
+ return (0);
+ xar->file->has |= HAS_FFLAGS;
+ if (archive_strlen(&(xar->file->fflags_text)) > 0)
+ archive_strappend_char(&(xar->file->fflags_text), ',');
+ archive_strcat(&(xar->file->fflags_text), flag);
+ return (1);
+}
+
+/*
+ * Linux file flags.
+ */
+static int
+xml_parse_file_ext2(struct xar *xar, const char *name)
+{
+ const char *flag = NULL;
+
+ if (strcmp(name, "SecureDeletion") == 0) {
+ xar->xmlsts = FILE_EXT2_SecureDeletion;
+ flag = "securedeletion";
+ }
+ else if (strcmp(name, "Undelete") == 0) {
+ xar->xmlsts = FILE_EXT2_Undelete;
+ flag = "nouunlink";
+ }
+ else if (strcmp(name, "Compress") == 0) {
+ xar->xmlsts = FILE_EXT2_Compress;
+ flag = "compress";
+ }
+ else if (strcmp(name, "Synchronous") == 0) {
+ xar->xmlsts = FILE_EXT2_Synchronous;
+ flag = "sync";
+ }
+ else if (strcmp(name, "Immutable") == 0) {
+ xar->xmlsts = FILE_EXT2_Immutable;
+ flag = "simmutable";
+ }
+ else if (strcmp(name, "AppendOnly") == 0) {
+ xar->xmlsts = FILE_EXT2_AppendOnly;
+ flag = "sappend";
+ }
+ else if (strcmp(name, "NoDump") == 0) {
+ xar->xmlsts = FILE_EXT2_NoDump;
+ flag = "nodump";
+ }
+ else if (strcmp(name, "NoAtime") == 0) {
+ xar->xmlsts = FILE_EXT2_NoAtime;
+ flag = "noatime";
+ }
+ else if (strcmp(name, "CompDirty") == 0) {
+ xar->xmlsts = FILE_EXT2_CompDirty;
+ flag = "compdirty";
+ }
+ else if (strcmp(name, "CompBlock") == 0) {
+ xar->xmlsts = FILE_EXT2_CompBlock;
+ flag = "comprblk";
+ }
+ else if (strcmp(name, "NoCompBlock") == 0) {
+ xar->xmlsts = FILE_EXT2_NoCompBlock;
+ flag = "nocomprblk";
+ }
+ else if (strcmp(name, "CompError") == 0) {
+ xar->xmlsts = FILE_EXT2_CompError;
+ flag = "comperr";
+ }
+ else if (strcmp(name, "BTree") == 0) {
+ xar->xmlsts = FILE_EXT2_BTree;
+ flag = "btree";
+ }
+ else if (strcmp(name, "HashIndexed") == 0) {
+ xar->xmlsts = FILE_EXT2_HashIndexed;
+ flag = "hashidx";
+ }
+ else if (strcmp(name, "iMagic") == 0) {
+ xar->xmlsts = FILE_EXT2_iMagic;
+ flag = "imagic";
+ }
+ else if (strcmp(name, "Journaled") == 0) {
+ xar->xmlsts = FILE_EXT2_Journaled;
+ flag = "journal";
+ }
+ else if (strcmp(name, "NoTail") == 0) {
+ xar->xmlsts = FILE_EXT2_NoTail;
+ flag = "notail";
+ }
+ else if (strcmp(name, "DirSync") == 0) {
+ xar->xmlsts = FILE_EXT2_DirSync;
+ flag = "dirsync";
+ }
+ else if (strcmp(name, "TopDir") == 0) {
+ xar->xmlsts = FILE_EXT2_TopDir;
+ flag = "topdir";
+ }
+ else if (strcmp(name, "Reserved") == 0) {
+ xar->xmlsts = FILE_EXT2_Reserved;
+ flag = "reserved";
+ }
+
+ if (flag == NULL)
+ return (0);
+ if (archive_strlen(&(xar->file->fflags_text)) > 0)
+ archive_strappend_char(&(xar->file->fflags_text), ',');
+ archive_strcat(&(xar->file->fflags_text), flag);
+ return (1);
+}
+
+#ifdef HAVE_LIBXML_XMLREADER_H
+
+static int
+xml2_xmlattr_setup(struct archive_read *a,
+ struct xmlattr_list *list, xmlTextReaderPtr reader)
+{
+ struct xmlattr *attr;
+ int r;
+
+ list->first = NULL;
+ list->last = &(list->first);
+ r = xmlTextReaderMoveToFirstAttribute(reader);
+ while (r == 1) {
+ attr = malloc(sizeof*(attr));
+ if (attr == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ attr->name = strdup(
+ (const char *)xmlTextReaderConstLocalName(reader));
+ if (attr->name == NULL) {
+ free(attr);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ attr->value = strdup(
+ (const char *)xmlTextReaderConstValue(reader));
+ if (attr->value == NULL) {
+ free(attr->name);
+ free(attr);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ attr->next = NULL;
+ *list->last = attr;
+ list->last = &(attr->next);
+ r = xmlTextReaderMoveToNextAttribute(reader);
+ }
+ return (r);
+}
+
+static int
+xml2_read_cb(void *context, char *buffer, int len)
+{
+ struct archive_read *a;
+ struct xar *xar;
+ const void *d;
+ size_t outbytes;
+ size_t used = 0;
+ int r;
+
+ a = (struct archive_read *)context;
+ xar = (struct xar *)(a->format->data);
+
+ if (xar->toc_remaining <= 0)
+ return (0);
+ d = buffer;
+ outbytes = len;
+ r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+ if (r != ARCHIVE_OK)
+ return (r);
+ __archive_read_consume(a, used);
+ xar->toc_remaining -= used;
+ xar->offset += used;
+ xar->toc_total += outbytes;
+ PRINT_TOC(buffer, len);
+
+ return ((int)outbytes);
+}
+
+static int
+xml2_close_cb(void *context)
+{
+
+ (void)context; /* UNUSED */
+ return (0);
+}
+
+static void
+xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity,
+ xmlTextReaderLocatorPtr locator)
+{
+ struct archive_read *a;
+
+ (void)locator; /* UNUSED */
+ a = (struct archive_read *)arg;
+ switch (severity) {
+ case XML_PARSER_SEVERITY_VALIDITY_WARNING:
+ case XML_PARSER_SEVERITY_WARNING:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "XML Parsing error: %s", msg);
+ break;
+ case XML_PARSER_SEVERITY_VALIDITY_ERROR:
+ case XML_PARSER_SEVERITY_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "XML Parsing error: %s", msg);
+ break;
+ }
+}
+
+static int
+xml2_read_toc(struct archive_read *a)
+{
+ xmlTextReaderPtr reader;
+ struct xmlattr_list list;
+ int r;
+
+ reader = xmlReaderForIO(xml2_read_cb, xml2_close_cb, a, NULL, NULL, 0);
+ if (reader == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory for xml parser");
+ return (ARCHIVE_FATAL);
+ }
+ xmlTextReaderSetErrorHandler(reader, xml2_error_hdr, a);
+
+ while ((r = xmlTextReaderRead(reader)) == 1) {
+ const char *name, *value;
+ int type, empty;
+
+ type = xmlTextReaderNodeType(reader);
+ name = (const char *)xmlTextReaderConstLocalName(reader);
+ switch (type) {
+ case XML_READER_TYPE_ELEMENT:
+ empty = xmlTextReaderIsEmptyElement(reader);
+ r = xml2_xmlattr_setup(a, &list, reader);
+ if (r == ARCHIVE_OK)
+ r = xml_start(a, name, &list);
+ xmlattr_cleanup(&list);
+ if (r != ARCHIVE_OK)
+ return (r);
+ if (empty)
+ xml_end(a, name);
+ break;
+ case XML_READER_TYPE_END_ELEMENT:
+ xml_end(a, name);
+ break;
+ case XML_READER_TYPE_TEXT:
+ value = (const char *)xmlTextReaderConstValue(reader);
+ xml_data(a, value, strlen(value));
+ break;
+ case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
+ default:
+ break;
+ }
+ if (r < 0)
+ break;
+ }
+ xmlFreeTextReader(reader);
+ xmlCleanupParser();
+
+ return ((r == 0)?ARCHIVE_OK:ARCHIVE_FATAL);
+}
+
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+
+static int
+expat_xmlattr_setup(struct archive_read *a,
+ struct xmlattr_list *list, const XML_Char **atts)
+{
+ struct xmlattr *attr;
+ char *name, *value;
+
+ list->first = NULL;
+ list->last = &(list->first);
+ if (atts == NULL)
+ return (ARCHIVE_OK);
+ while (atts[0] != NULL && atts[1] != NULL) {
+ attr = malloc(sizeof*(attr));
+ name = strdup(atts[0]);
+ value = strdup(atts[1]);
+ if (attr == NULL || name == NULL || value == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ free(attr);
+ free(name);
+ free(value);
+ return (ARCHIVE_FATAL);
+ }
+ attr->name = name;
+ attr->value = value;
+ attr->next = NULL;
+ *list->last = attr;
+ list->last = &(attr->next);
+ atts += 2;
+ }
+ return (ARCHIVE_OK);
+}
+
+static void
+expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts)
+{
+ struct expat_userData *ud = (struct expat_userData *)userData;
+ struct archive_read *a = ud->archive;
+ struct xmlattr_list list;
+ int r;
+
+ r = expat_xmlattr_setup(a, &list, atts);
+ if (r == ARCHIVE_OK)
+ r = xml_start(a, (const char *)name, &list);
+ xmlattr_cleanup(&list);
+ ud->state = r;
+}
+
+static void
+expat_end_cb(void *userData, const XML_Char *name)
+{
+ struct expat_userData *ud = (struct expat_userData *)userData;
+
+ xml_end(ud->archive, (const char *)name);
+}
+
+static void
+expat_data_cb(void *userData, const XML_Char *s, int len)
+{
+ struct expat_userData *ud = (struct expat_userData *)userData;
+
+ xml_data(ud->archive, s, len);
+}
+
+static int
+expat_read_toc(struct archive_read *a)
+{
+ struct xar *xar;
+ XML_Parser parser;
+ struct expat_userData ud;
+
+ ud.state = ARCHIVE_OK;
+ ud.archive = a;
+
+ xar = (struct xar *)(a->format->data);
+
+ /* Initialize XML Parser library. */
+ parser = XML_ParserCreate(NULL);
+ if (parser == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Couldn't allocate memory for xml parser");
+ return (ARCHIVE_FATAL);
+ }
+ XML_SetUserData(parser, &ud);
+ XML_SetElementHandler(parser, expat_start_cb, expat_end_cb);
+ XML_SetCharacterDataHandler(parser, expat_data_cb);
+ xar->xmlsts = INIT;
+
+ while (xar->toc_remaining && ud.state == ARCHIVE_OK) {
+ enum XML_Status xr;
+ const void *d;
+ size_t outbytes;
+ size_t used;
+ int r;
+
+ d = NULL;
+ r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+ if (r != ARCHIVE_OK)
+ return (r);
+ xar->toc_remaining -= used;
+ xar->offset += used;
+ xar->toc_total += outbytes;
+ PRINT_TOC(d, outbytes);
+
+ xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0);
+ __archive_read_consume(a, used);
+ if (xr == XML_STATUS_ERROR) {
+ XML_ParserFree(parser);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "XML Parsing failed");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ XML_ParserFree(parser);
+ return (ud.state);
+}
+#endif /* defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) */
+
+#endif /* Support xar format */
diff --git a/contrib/libs/libarchive/libarchive/archive_read_support_format_zip.c b/contrib/libs/libarchive/libarchive/archive_read_support_format_zip.c
new file mode 100644
index 0000000000..cca4a3af7e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_read_support_format_zip.c
@@ -0,0 +1,4270 @@
+/*-
+ * Copyright (c) 2004-2013 Tim Kientzle
+ * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA
+ * Copyright (c) 2013 Konrad Kleine
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102 2009-12-28 03:11:36Z kientzle $");
+
+/*
+ * The definitive documentation of the Zip file format is:
+ * http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+ *
+ * The Info-Zip project has pioneered various extensions to better
+ * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855
+ * "Ux", and 0x7875 "ux" extensions for time and ownership
+ * information.
+ *
+ * History of this code: The streaming Zip reader was first added to
+ * libarchive in January 2005. Support for seekable input sources was
+ * added in Nov 2011. Zip64 support (including a significant code
+ * refactoring) was added in 2014.
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_cryptor_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_hmac_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_read_private.h"
+#include "archive_ppmd8_private.h"
+
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+
+struct zip_entry {
+ struct archive_rb_node node;
+ struct zip_entry *next;
+ int64_t local_header_offset;
+ int64_t compressed_size;
+ int64_t uncompressed_size;
+ int64_t gid;
+ int64_t uid;
+ struct archive_string rsrcname;
+ time_t mtime;
+ time_t atime;
+ time_t ctime;
+ uint32_t crc32;
+ uint16_t mode;
+ uint16_t zip_flags; /* From GP Flags Field */
+ unsigned char compression;
+ unsigned char system; /* From "version written by" */
+ unsigned char flags; /* Our extra markers. */
+ unsigned char decdat;/* Used for Decryption check */
+
+ /* WinZip AES encryption extra field should be available
+ * when compression is 99. */
+ struct {
+ /* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */
+ unsigned vendor;
+#define AES_VENDOR_AE_1 0x0001
+#define AES_VENDOR_AE_2 0x0002
+ /* AES encryption strength:
+ * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */
+ unsigned strength;
+ /* Actual compression method. */
+ unsigned char compression;
+ } aes_extra;
+};
+
+struct trad_enc_ctx {
+ uint32_t keys[3];
+};
+
+/* Bits used in zip_flags. */
+#define ZIP_ENCRYPTED (1 << 0)
+#define ZIP_LENGTH_AT_END (1 << 3)
+#define ZIP_STRONG_ENCRYPTED (1 << 6)
+#define ZIP_UTF8_NAME (1 << 11)
+/* See "7.2 Single Password Symmetric Encryption Method"
+ in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */
+#define ZIP_CENTRAL_DIRECTORY_ENCRYPTED (1 << 13)
+
+/* Bits used in flags. */
+#define LA_USED_ZIP64 (1 << 0)
+#define LA_FROM_CENTRAL_DIRECTORY (1 << 1)
+
+/*
+ * See "WinZip - AES Encryption Information"
+ * http://www.winzip.com/aes_info.htm
+ */
+/* Value used in compression method. */
+#define WINZIP_AES_ENCRYPTION 99
+/* Authentication code size. */
+#define AUTH_CODE_SIZE 10
+/**/
+#define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2)
+
+struct zip {
+ /* Structural information about the archive. */
+ struct archive_string format_name;
+ int64_t central_directory_offset;
+ int64_t central_directory_offset_adjusted;
+ size_t central_directory_entries_total;
+ size_t central_directory_entries_on_this_disk;
+ int has_encrypted_entries;
+
+ /* List of entries (seekable Zip only) */
+ struct zip_entry *zip_entries;
+ struct archive_rb_tree tree;
+ struct archive_rb_tree tree_rsrc;
+
+ /* Bytes read but not yet consumed via __archive_read_consume() */
+ size_t unconsumed;
+
+ /* Information about entry we're currently reading. */
+ struct zip_entry *entry;
+ int64_t entry_bytes_remaining;
+
+ /* These count the number of bytes actually read for the entry. */
+ int64_t entry_compressed_bytes_read;
+ int64_t entry_uncompressed_bytes_read;
+
+ /* Running CRC32 of the decompressed data */
+ unsigned long entry_crc32;
+ unsigned long (*crc32func)(unsigned long, const void *,
+ size_t);
+ char ignore_crc32;
+
+ /* Flags to mark progress of decompression. */
+ char decompress_init;
+ char end_of_entry;
+
+ unsigned char *uncompressed_buffer;
+ size_t uncompressed_buffer_size;
+
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ char stream_valid;
+#endif
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ lzma_stream zipx_lzma_stream;
+ char zipx_lzma_valid;
+#endif
+
+#ifdef HAVE_BZLIB_H
+ bz_stream bzstream;
+ char bzstream_valid;
+#endif
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ ZSTD_DStream *zstdstream;
+ char zstdstream_valid;
+#endif
+
+ IByteIn zipx_ppmd_stream;
+ ssize_t zipx_ppmd_read_compressed;
+ CPpmd8 ppmd8;
+ char ppmd8_valid;
+ char ppmd8_stream_failed;
+
+ struct archive_string_conv *sconv;
+ struct archive_string_conv *sconv_default;
+ struct archive_string_conv *sconv_utf8;
+ int init_default_conversion;
+ int process_mac_extensions;
+
+ char init_decryption;
+
+ /* Decryption buffer. */
+ /*
+ * The decrypted data starts at decrypted_ptr and
+ * extends for decrypted_bytes_remaining. Decryption
+ * adds new data to the end of this block, data is returned
+ * to clients from the beginning. When the block hits the
+ * end of decrypted_buffer, it has to be shuffled back to
+ * the beginning of the buffer.
+ */
+ unsigned char *decrypted_buffer;
+ unsigned char *decrypted_ptr;
+ size_t decrypted_buffer_size;
+ size_t decrypted_bytes_remaining;
+ size_t decrypted_unconsumed_bytes;
+
+ /* Traditional PKWARE decryption. */
+ struct trad_enc_ctx tctx;
+ char tctx_valid;
+
+ /* WinZip AES decryption. */
+ /* Contexts used for AES decryption. */
+ archive_crypto_ctx cctx;
+ char cctx_valid;
+ archive_hmac_sha1_ctx hctx;
+ char hctx_valid;
+
+ /* Strong encryption's decryption header information. */
+ unsigned iv_size;
+ unsigned alg_id;
+ unsigned bit_len;
+ unsigned flags;
+ unsigned erd_size;
+ unsigned v_size;
+ unsigned v_crc32;
+ uint8_t *iv;
+ uint8_t *erd;
+ uint8_t *v_data;
+};
+
+/* Many systems define min or MIN, but not all. */
+#define zipmin(a,b) ((a) < (b) ? (a) : (b))
+
+#ifdef HAVE_ZLIB_H
+static int
+zip_read_data_deflate(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset);
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+static int
+zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset);
+#endif
+
+/* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8
+ * streams inside ZIP files. It has 2 purposes: one is to fetch the next
+ * compressed byte from the stream, second one is to increase the counter how
+ * many compressed bytes were read. */
+static Byte
+ppmd_read(void* p) {
+ /* Get the handle to current decompression context. */
+ struct archive_read *a = ((IByteIn*)p)->a;
+ struct zip *zip = (struct zip*) a->format->data;
+ ssize_t bytes_avail = 0;
+
+ /* Fetch next byte. */
+ const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 1) {
+ zip->ppmd8_stream_failed = 1;
+ return 0;
+ }
+
+ __archive_read_consume(a, 1);
+
+ /* Increment the counter. */
+ ++zip->zipx_ppmd_read_compressed;
+
+ /* Return the next compressed byte. */
+ return data[0];
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ Traditional PKWARE Decryption functions.
+ */
+
+static void
+trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)
+{
+ uint8_t t;
+#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)
+
+ ctx->keys[0] = CRC32(ctx->keys[0], c);
+ ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;
+ t = (ctx->keys[1] >> 24) & 0xff;
+ ctx->keys[2] = CRC32(ctx->keys[2], t);
+#undef CRC32
+}
+
+static uint8_t
+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)
+{
+ unsigned temp = ctx->keys[2] | 2;
+ return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
+}
+
+static void
+trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_len)
+{
+ unsigned i, max;
+
+ max = (unsigned)((in_len < out_len)? in_len: out_len);
+
+ for (i = 0; i < max; i++) {
+ uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx);
+ out[i] = t;
+ trad_enc_update_keys(ctx, t);
+ }
+}
+
+static int
+trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len,
+ const uint8_t *key, size_t key_len, uint8_t *crcchk)
+{
+ uint8_t header[12];
+
+ if (key_len < 12) {
+ *crcchk = 0xff;
+ return -1;
+ }
+
+ ctx->keys[0] = 305419896L;
+ ctx->keys[1] = 591751049L;
+ ctx->keys[2] = 878082192L;
+
+ for (;pw_len; --pw_len)
+ trad_enc_update_keys(ctx, *pw++);
+
+ trad_enc_decrypt_update(ctx, key, 12, header, 12);
+ /* Return the last byte for CRC check. */
+ *crcchk = header[11];
+ return 0;
+}
+
+#if 0
+static void
+crypt_derive_key_sha1(const void *p, int size, unsigned char *key,
+ int key_size)
+{
+#define MD_SIZE 20
+ archive_sha1_ctx ctx;
+ unsigned char md1[MD_SIZE];
+ unsigned char md2[MD_SIZE * 2];
+ unsigned char mkb[64];
+ int i;
+
+ archive_sha1_init(&ctx);
+ archive_sha1_update(&ctx, p, size);
+ archive_sha1_final(&ctx, md1);
+
+ memset(mkb, 0x36, sizeof(mkb));
+ for (i = 0; i < MD_SIZE; i++)
+ mkb[i] ^= md1[i];
+ archive_sha1_init(&ctx);
+ archive_sha1_update(&ctx, mkb, sizeof(mkb));
+ archive_sha1_final(&ctx, md2);
+
+ memset(mkb, 0x5C, sizeof(mkb));
+ for (i = 0; i < MD_SIZE; i++)
+ mkb[i] ^= md1[i];
+ archive_sha1_init(&ctx);
+ archive_sha1_update(&ctx, mkb, sizeof(mkb));
+ archive_sha1_final(&ctx, md2 + MD_SIZE);
+
+ if (key_size > 32)
+ key_size = 32;
+ memcpy(key, md2, key_size);
+#undef MD_SIZE
+}
+#endif
+
+/*
+ * Common code for streaming or seeking modes.
+ *
+ * Includes code to read local file headers, decompress data
+ * from entry bodies, and common API.
+ */
+
+static unsigned long
+real_crc32(unsigned long crc, const void *buff, size_t len)
+{
+ return crc32(crc, buff, (unsigned int)len);
+}
+
+/* Used by "ignorecrc32" option to speed up tests. */
+static unsigned long
+fake_crc32(unsigned long crc, const void *buff, size_t len)
+{
+ (void)crc; /* UNUSED */
+ (void)buff; /* UNUSED */
+ (void)len; /* UNUSED */
+ return 0;
+}
+
+static const struct {
+ int id;
+ const char * name;
+} compression_methods[] = {
+ {0, "uncompressed"}, /* The file is stored (no compression) */
+ {1, "shrinking"}, /* The file is Shrunk */
+ {2, "reduced-1"}, /* The file is Reduced with compression factor 1 */
+ {3, "reduced-2"}, /* The file is Reduced with compression factor 2 */
+ {4, "reduced-3"}, /* The file is Reduced with compression factor 3 */
+ {5, "reduced-4"}, /* The file is Reduced with compression factor 4 */
+ {6, "imploded"}, /* The file is Imploded */
+ {7, "reserved"}, /* Reserved for Tokenizing compression algorithm */
+ {8, "deflation"}, /* The file is Deflated */
+ {9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */
+ {10, "ibm-terse"},/* PKWARE Data Compression Library Imploding
+ * (old IBM TERSE) */
+ {11, "reserved"}, /* Reserved by PKWARE */
+ {12, "bzip"}, /* File is compressed using BZIP2 algorithm */
+ {13, "reserved"}, /* Reserved by PKWARE */
+ {14, "lzma"}, /* LZMA (EFS) */
+ {15, "reserved"}, /* Reserved by PKWARE */
+ {16, "reserved"}, /* Reserved by PKWARE */
+ {17, "reserved"}, /* Reserved by PKWARE */
+ {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */
+ {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */
+ {93, "zstd"}, /* Zstandard (zstd) Compression */
+ {95, "xz"}, /* XZ compressed data */
+ {96, "jpeg"}, /* JPEG compressed data */
+ {97, "wav-pack"}, /* WavPack compressed data */
+ {98, "ppmd-1"}, /* PPMd version I, Rev 1 */
+ {99, "aes"} /* WinZip AES encryption */
+};
+
+static const char *
+compression_name(const int compression)
+{
+ static const int num_compression_methods =
+ sizeof(compression_methods)/sizeof(compression_methods[0]);
+ int i=0;
+
+ while(compression >= 0 && i < num_compression_methods) {
+ if (compression_methods[i].id == compression)
+ return compression_methods[i].name;
+ i++;
+ }
+ return "??";
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+zip_time(const char *p)
+{
+ int msTime, msDate;
+ struct tm ts;
+
+ msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]);
+ msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]);
+
+ memset(&ts, 0, sizeof(ts));
+ ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
+ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
+ ts.tm_mday = msDate & 0x1f; /* Day of month. */
+ ts.tm_hour = (msTime >> 11) & 0x1f;
+ ts.tm_min = (msTime >> 5) & 0x3f;
+ ts.tm_sec = (msTime << 1) & 0x3e;
+ ts.tm_isdst = -1;
+ return mktime(&ts);
+}
+
+/*
+ * The extra data is stored as a list of
+ * id1+size1+data1 + id2+size2+data2 ...
+ * triplets. id and size are 2 bytes each.
+ */
+static int
+process_extra(struct archive_read *a, struct archive_entry *entry,
+ const char *p, size_t extra_length, struct zip_entry* zip_entry)
+{
+ unsigned offset = 0;
+ struct zip *zip = (struct zip *)(a->format->data);
+
+ if (extra_length == 0) {
+ return ARCHIVE_OK;
+ }
+
+ if (extra_length < 4) {
+ size_t i = 0;
+ /* Some ZIP files may have trailing 0 bytes. Let's check they
+ * are all 0 and ignore them instead of returning an error.
+ *
+ * This is not technically correct, but some ZIP files look
+ * like this and other tools support those files - so let's
+ * also support them.
+ */
+ for (; i < extra_length; i++) {
+ if (p[i] != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Too-small extra data: "
+ "Need at least 4 bytes, "
+ "but only found %d bytes",
+ (int)extra_length);
+ return ARCHIVE_FAILED;
+ }
+ }
+
+ return ARCHIVE_OK;
+ }
+
+ while (offset <= extra_length - 4) {
+ unsigned short headerid = archive_le16dec(p + offset);
+ unsigned short datasize = archive_le16dec(p + offset + 2);
+
+ offset += 4;
+ if (offset + datasize > extra_length) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: "
+ "Need %d bytes but only found %d bytes",
+ (int)datasize, (int)(extra_length - offset));
+ return ARCHIVE_FAILED;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Header id 0x%04x, length %d\n",
+ headerid, datasize);
+#endif
+ switch (headerid) {
+ case 0x0001:
+ /* Zip64 extended information extra field. */
+ zip_entry->flags |= LA_USED_ZIP64;
+ if (zip_entry->uncompressed_size == 0xffffffff) {
+ uint64_t t = 0;
+ if (datasize < 8
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "uncompressed size");
+ return ARCHIVE_FAILED;
+ }
+ zip_entry->uncompressed_size = t;
+ offset += 8;
+ datasize -= 8;
+ }
+ if (zip_entry->compressed_size == 0xffffffff) {
+ uint64_t t = 0;
+ if (datasize < 8
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "compressed size");
+ return ARCHIVE_FAILED;
+ }
+ zip_entry->compressed_size = t;
+ offset += 8;
+ datasize -= 8;
+ }
+ if (zip_entry->local_header_offset == 0xffffffff) {
+ uint64_t t = 0;
+ if (datasize < 8
+ || (t = archive_le64dec(p + offset)) >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed 64-bit "
+ "local header offset");
+ return ARCHIVE_FAILED;
+ }
+ zip_entry->local_header_offset = t;
+ offset += 8;
+ datasize -= 8;
+ }
+ /* archive_le32dec(p + offset) gives disk
+ * on which file starts, but we don't handle
+ * multi-volume Zip files. */
+ break;
+#ifdef DEBUG
+ case 0x0017:
+ {
+ /* Strong encryption field. */
+ if (archive_le16dec(p + offset) == 2) {
+ unsigned algId =
+ archive_le16dec(p + offset + 2);
+ unsigned bitLen =
+ archive_le16dec(p + offset + 4);
+ int flags =
+ archive_le16dec(p + offset + 6);
+ fprintf(stderr, "algId=0x%04x, bitLen=%u, "
+ "flgas=%d\n", algId, bitLen,flags);
+ }
+ break;
+ }
+#endif
+ case 0x5455:
+ {
+ /* Extended time field "UT". */
+ int flags;
+ if (datasize == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Incomplete extended time field");
+ return ARCHIVE_FAILED;
+ }
+ flags = p[offset];
+ offset++;
+ datasize--;
+ /* Flag bits indicate which dates are present. */
+ if (flags & 0x01)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "mtime: %lld -> %d\n",
+ (long long)zip_entry->mtime,
+ archive_le32dec(p + offset));
+#endif
+ if (datasize < 4)
+ break;
+ zip_entry->mtime = archive_le32dec(p + offset);
+ offset += 4;
+ datasize -= 4;
+ }
+ if (flags & 0x02)
+ {
+ if (datasize < 4)
+ break;
+ zip_entry->atime = archive_le32dec(p + offset);
+ offset += 4;
+ datasize -= 4;
+ }
+ if (flags & 0x04)
+ {
+ if (datasize < 4)
+ break;
+ zip_entry->ctime = archive_le32dec(p + offset);
+ offset += 4;
+ datasize -= 4;
+ }
+ break;
+ }
+ case 0x5855:
+ {
+ /* Info-ZIP Unix Extra Field (old version) "UX". */
+ if (datasize >= 8) {
+ zip_entry->atime = archive_le32dec(p + offset);
+ zip_entry->mtime =
+ archive_le32dec(p + offset + 4);
+ }
+ if (datasize >= 12) {
+ zip_entry->uid =
+ archive_le16dec(p + offset + 8);
+ zip_entry->gid =
+ archive_le16dec(p + offset + 10);
+ }
+ break;
+ }
+ case 0x6c78:
+ {
+ /* Experimental 'xl' field */
+ /*
+ * Introduced Dec 2013 to provide a way to
+ * include external file attributes (and other
+ * fields that ordinarily appear only in
+ * central directory) in local file header.
+ * This provides file type and permission
+ * information necessary to support full
+ * streaming extraction. Currently being
+ * discussed with other Zip developers
+ * ... subject to change.
+ *
+ * Format:
+ * The field starts with a bitmap that specifies
+ * which additional fields are included. The
+ * bitmap is variable length and can be extended in
+ * the future.
+ *
+ * n bytes - feature bitmap: first byte has low-order
+ * 7 bits. If high-order bit is set, a subsequent
+ * byte holds the next 7 bits, etc.
+ *
+ * if bitmap & 1, 2 byte "version made by"
+ * if bitmap & 2, 2 byte "internal file attributes"
+ * if bitmap & 4, 4 byte "external file attributes"
+ * if bitmap & 8, 2 byte comment length + n byte
+ * comment
+ */
+ int bitmap, bitmap_last;
+
+ if (datasize < 1)
+ break;
+ bitmap_last = bitmap = 0xff & p[offset];
+ offset += 1;
+ datasize -= 1;
+
+ /* We only support first 7 bits of bitmap; skip rest. */
+ while ((bitmap_last & 0x80) != 0
+ && datasize >= 1) {
+ bitmap_last = p[offset];
+ offset += 1;
+ datasize -= 1;
+ }
+
+ if (bitmap & 1) {
+ /* 2 byte "version made by" */
+ if (datasize < 2)
+ break;
+ zip_entry->system
+ = archive_le16dec(p + offset) >> 8;
+ offset += 2;
+ datasize -= 2;
+ }
+ if (bitmap & 2) {
+ /* 2 byte "internal file attributes" */
+ uint32_t internal_attributes;
+ if (datasize < 2)
+ break;
+ internal_attributes
+ = archive_le16dec(p + offset);
+ /* Not used by libarchive at present. */
+ (void)internal_attributes; /* UNUSED */
+ offset += 2;
+ datasize -= 2;
+ }
+ if (bitmap & 4) {
+ /* 4 byte "external file attributes" */
+ uint32_t external_attributes;
+ if (datasize < 4)
+ break;
+ external_attributes
+ = archive_le32dec(p + offset);
+ if (zip_entry->system == 3) {
+ zip_entry->mode
+ = external_attributes >> 16;
+ } else if (zip_entry->system == 0) {
+ // Interpret MSDOS directory bit
+ if (0x10 == (external_attributes &
+ 0x10)) {
+ zip_entry->mode =
+ AE_IFDIR | 0775;
+ } else {
+ zip_entry->mode =
+ AE_IFREG | 0664;
+ }
+ if (0x01 == (external_attributes &
+ 0x01)) {
+ /* Read-only bit;
+ * strip write permissions */
+ zip_entry->mode &= 0555;
+ }
+ } else {
+ zip_entry->mode = 0;
+ }
+ offset += 4;
+ datasize -= 4;
+ }
+ if (bitmap & 8) {
+ /* 2 byte comment length + comment */
+ uint32_t comment_length;
+ if (datasize < 2)
+ break;
+ comment_length
+ = archive_le16dec(p + offset);
+ offset += 2;
+ datasize -= 2;
+
+ if (datasize < comment_length)
+ break;
+ /* Comment is not supported by libarchive */
+ offset += comment_length;
+ datasize -= comment_length;
+ }
+ break;
+ }
+ case 0x7075:
+ {
+ /* Info-ZIP Unicode Path Extra Field. */
+ if (datasize < 5 || entry == NULL)
+ break;
+ offset += 5;
+ datasize -= 5;
+
+ /* The path name in this field is always encoded
+ * in UTF-8. */
+ if (zip->sconv_utf8 == NULL) {
+ zip->sconv_utf8 =
+ archive_string_conversion_from_charset(
+ &a->archive, "UTF-8", 1);
+ /* If the converter from UTF-8 is not
+ * available, then the path name from the main
+ * field will more likely be correct. */
+ if (zip->sconv_utf8 == NULL)
+ break;
+ }
+
+ /* Make sure the CRC32 of the filename matches. */
+ if (!zip->ignore_crc32) {
+ const char *cp = archive_entry_pathname(entry);
+ if (cp) {
+ unsigned long file_crc =
+ zip->crc32func(0, cp, strlen(cp));
+ unsigned long utf_crc =
+ archive_le32dec(p + offset - 4);
+ if (file_crc != utf_crc) {
+#ifdef DEBUG
+ fprintf(stderr,
+ "CRC filename mismatch; "
+ "CDE is %lx, but UTF8 "
+ "is outdated with %lx\n",
+ file_crc, utf_crc);
+#endif
+ break;
+ }
+ }
+ }
+
+ if (archive_entry_copy_pathname_l(entry,
+ p + offset, datasize, zip->sconv_utf8) != 0) {
+ /* Ignore the error, and fallback to the path
+ * name from the main field. */
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read the ZIP "
+ "0x7075 extra field path.\n");
+#endif
+ }
+ break;
+ }
+ case 0x7855:
+ /* Info-ZIP Unix Extra Field (type 2) "Ux". */
+#ifdef DEBUG
+ fprintf(stderr, "uid %d gid %d\n",
+ archive_le16dec(p + offset),
+ archive_le16dec(p + offset + 2));
+#endif
+ if (datasize >= 2)
+ zip_entry->uid = archive_le16dec(p + offset);
+ if (datasize >= 4)
+ zip_entry->gid =
+ archive_le16dec(p + offset + 2);
+ break;
+ case 0x7875:
+ {
+ /* Info-Zip Unix Extra Field (type 3) "ux". */
+ int uidsize = 0, gidsize = 0;
+
+ /* TODO: support arbitrary uidsize/gidsize. */
+ if (datasize >= 1 && p[offset] == 1) {/* version=1 */
+ if (datasize >= 4) {
+ /* get a uid size. */
+ uidsize = 0xff & (int)p[offset+1];
+ if (uidsize == 2)
+ zip_entry->uid =
+ archive_le16dec(
+ p + offset + 2);
+ else if (uidsize == 4 && datasize >= 6)
+ zip_entry->uid =
+ archive_le32dec(
+ p + offset + 2);
+ }
+ if (datasize >= (2 + uidsize + 3)) {
+ /* get a gid size. */
+ gidsize = 0xff &
+ (int)p[offset+2+uidsize];
+ if (gidsize == 2)
+ zip_entry->gid =
+ archive_le16dec(
+ p+offset+2+uidsize+1);
+ else if (gidsize == 4 &&
+ datasize >= (2 + uidsize + 5))
+ zip_entry->gid =
+ archive_le32dec(
+ p+offset+2+uidsize+1);
+ }
+ }
+ break;
+ }
+ case 0x9901:
+ /* WinZip AES extra data field. */
+ if (datasize < 6) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Incomplete AES field");
+ return ARCHIVE_FAILED;
+ }
+ if (p[offset + 2] == 'A' && p[offset + 3] == 'E') {
+ /* Vendor version. */
+ zip_entry->aes_extra.vendor =
+ archive_le16dec(p + offset);
+ /* AES encryption strength. */
+ zip_entry->aes_extra.strength = p[offset + 4];
+ /* Actual compression method. */
+ zip_entry->aes_extra.compression =
+ p[offset + 5];
+ }
+ break;
+ default:
+ break;
+ }
+ offset += datasize;
+ }
+ return ARCHIVE_OK;
+}
+
+/*
+ * Assumes file pointer is at beginning of local file header.
+ */
+static int
+zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
+ struct zip *zip)
+{
+ const char *p;
+ const void *h;
+ const wchar_t *wp;
+ const char *cp;
+ size_t len, filename_length, extra_length;
+ struct archive_string_conv *sconv;
+ struct zip_entry *zip_entry = zip->entry;
+ struct zip_entry zip_entry_central_dir;
+ int ret = ARCHIVE_OK;
+ char version;
+
+ /* Save a copy of the original for consistency checks. */
+ zip_entry_central_dir = *zip_entry;
+
+ zip->decompress_init = 0;
+ zip->end_of_entry = 0;
+ zip->entry_uncompressed_bytes_read = 0;
+ zip->entry_compressed_bytes_read = 0;
+ zip->entry_crc32 = zip->crc32func(0, NULL, 0);
+
+ /* Setup default conversion. */
+ if (zip->sconv == NULL && !zip->init_default_conversion) {
+ zip->sconv_default =
+ archive_string_default_conversion_for_read(&(a->archive));
+ zip->init_default_conversion = 1;
+ }
+
+ if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (memcmp(p, "PK\003\004", 4) != 0) {
+ archive_set_error(&a->archive, -1, "Damaged Zip archive");
+ return ARCHIVE_FATAL;
+ }
+ version = p[4];
+ zip_entry->system = p[5];
+ zip_entry->zip_flags = archive_le16dec(p + 6);
+ if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) {
+ zip->has_encrypted_entries = 1;
+ archive_entry_set_is_data_encrypted(entry, 1);
+ if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED &&
+ zip_entry->zip_flags & ZIP_ENCRYPTED &&
+ zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) {
+ archive_entry_set_is_metadata_encrypted(entry, 1);
+ return ARCHIVE_FATAL;
+ }
+ }
+ zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED);
+ zip_entry->compression = (char)archive_le16dec(p + 8);
+ zip_entry->mtime = zip_time(p + 10);
+ zip_entry->crc32 = archive_le32dec(p + 14);
+ if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ zip_entry->decdat = p[11];
+ else
+ zip_entry->decdat = p[17];
+ zip_entry->compressed_size = archive_le32dec(p + 18);
+ zip_entry->uncompressed_size = archive_le32dec(p + 22);
+ filename_length = archive_le16dec(p + 26);
+ extra_length = archive_le16dec(p + 28);
+
+ __archive_read_consume(a, 30);
+
+ /* Read the filename. */
+ if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return (ARCHIVE_FATAL);
+ }
+ if (zip_entry->zip_flags & ZIP_UTF8_NAME) {
+ /* The filename is stored to be UTF-8. */
+ if (zip->sconv_utf8 == NULL) {
+ zip->sconv_utf8 =
+ archive_string_conversion_from_charset(
+ &a->archive, "UTF-8", 1);
+ if (zip->sconv_utf8 == NULL)
+ return (ARCHIVE_FATAL);
+ }
+ sconv = zip->sconv_utf8;
+ } else if (zip->sconv != NULL)
+ sconv = zip->sconv;
+ else
+ sconv = zip->sconv_default;
+
+ if (archive_entry_copy_pathname_l(entry,
+ h, filename_length, sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Pathname cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ __archive_read_consume(a, filename_length);
+
+ /* Read the extra data. */
+ if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (ARCHIVE_OK != process_extra(a, entry, h, extra_length,
+ zip_entry)) {
+ return ARCHIVE_FATAL;
+ }
+ __archive_read_consume(a, extra_length);
+
+ /* Work around a bug in Info-Zip: When reading from a pipe, it
+ * stats the pipe instead of synthesizing a file entry. */
+ if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) {
+ zip_entry->mode &= ~ AE_IFMT;
+ zip_entry->mode |= AE_IFREG;
+ }
+
+ /* If the mode is totally empty, set some sane default. */
+ if (zip_entry->mode == 0) {
+ zip_entry->mode |= 0664;
+ }
+
+ /* Windows archivers sometimes use backslash as the directory
+ * separator. Normalize to slash. */
+ if (zip_entry->system == 0 &&
+ (wp = archive_entry_pathname_w(entry)) != NULL) {
+ if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) {
+ size_t i;
+ struct archive_wstring s;
+ archive_string_init(&s);
+ archive_wstrcpy(&s, wp);
+ for (i = 0; i < archive_strlen(&s); i++) {
+ if (s.s[i] == '\\')
+ s.s[i] = '/';
+ }
+ archive_entry_copy_pathname_w(entry, s.s);
+ archive_wstring_free(&s);
+ }
+ }
+
+ /* Make sure that entries with a trailing '/' are marked as directories
+ * even if the External File Attributes contains bogus values. If this
+ * is not a directory and there is no type, assume a regular file. */
+ if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) {
+ int has_slash;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL) {
+ len = wcslen(wp);
+ has_slash = len > 0 && wp[len - 1] == L'/';
+ } else {
+ cp = archive_entry_pathname(entry);
+ len = (cp != NULL)?strlen(cp):0;
+ has_slash = len > 0 && cp[len - 1] == '/';
+ }
+ /* Correct file type as needed. */
+ if (has_slash) {
+ zip_entry->mode &= ~AE_IFMT;
+ zip_entry->mode |= AE_IFDIR;
+ zip_entry->mode |= 0111;
+ } else if ((zip_entry->mode & AE_IFMT) == 0) {
+ zip_entry->mode |= AE_IFREG;
+ }
+ }
+
+ /* Make sure directories end in '/' */
+ if ((zip_entry->mode & AE_IFMT) == AE_IFDIR) {
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL) {
+ len = wcslen(wp);
+ if (len > 0 && wp[len - 1] != L'/') {
+ struct archive_wstring s;
+ archive_string_init(&s);
+ archive_wstrcat(&s, wp);
+ archive_wstrappend_wchar(&s, L'/');
+ archive_entry_copy_pathname_w(entry, s.s);
+ archive_wstring_free(&s);
+ }
+ } else {
+ cp = archive_entry_pathname(entry);
+ len = (cp != NULL)?strlen(cp):0;
+ if (len > 0 && cp[len - 1] != '/') {
+ struct archive_string s;
+ archive_string_init(&s);
+ archive_strcat(&s, cp);
+ archive_strappend_char(&s, '/');
+ archive_entry_set_pathname(entry, s.s);
+ archive_string_free(&s);
+ }
+ }
+ }
+
+ if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) {
+ /* If this came from the central dir, its size info
+ * is definitive, so ignore the length-at-end flag. */
+ zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END;
+ /* If local header is missing a value, use the one from
+ the central directory. If both have it, warn about
+ mismatches. */
+ if (zip_entry->crc32 == 0) {
+ zip_entry->crc32 = zip_entry_central_dir.crc32;
+ } else if (!zip->ignore_crc32
+ && zip_entry->crc32 != zip_entry_central_dir.crc32) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Inconsistent CRC32 values");
+ ret = ARCHIVE_WARN;
+ }
+ if (zip_entry->compressed_size == 0) {
+ zip_entry->compressed_size
+ = zip_entry_central_dir.compressed_size;
+ } else if (zip_entry->compressed_size
+ != zip_entry_central_dir.compressed_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Inconsistent compressed size: "
+ "%jd in central directory, %jd in local header",
+ (intmax_t)zip_entry_central_dir.compressed_size,
+ (intmax_t)zip_entry->compressed_size);
+ ret = ARCHIVE_WARN;
+ }
+ if (zip_entry->uncompressed_size == 0 ||
+ zip_entry->uncompressed_size == 0xffffffff) {
+ zip_entry->uncompressed_size
+ = zip_entry_central_dir.uncompressed_size;
+ } else if (zip_entry->uncompressed_size
+ != zip_entry_central_dir.uncompressed_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Inconsistent uncompressed size: "
+ "%jd in central directory, %jd in local header",
+ (intmax_t)zip_entry_central_dir.uncompressed_size,
+ (intmax_t)zip_entry->uncompressed_size);
+ ret = ARCHIVE_WARN;
+ }
+ }
+
+ /* Populate some additional entry fields: */
+ archive_entry_set_mode(entry, zip_entry->mode);
+ archive_entry_set_uid(entry, zip_entry->uid);
+ archive_entry_set_gid(entry, zip_entry->gid);
+ archive_entry_set_mtime(entry, zip_entry->mtime, 0);
+ archive_entry_set_ctime(entry, zip_entry->ctime, 0);
+ archive_entry_set_atime(entry, zip_entry->atime, 0);
+
+ if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) {
+ size_t linkname_length;
+
+ if (zip_entry->compressed_size > 64 * 1024) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Zip file with oversized link entry");
+ return ARCHIVE_FATAL;
+ }
+
+ linkname_length = (size_t)zip_entry->compressed_size;
+
+ archive_entry_set_size(entry, 0);
+
+ // take into account link compression if any
+ size_t linkname_full_length = linkname_length;
+ if (zip->entry->compression != 0)
+ {
+ // symlink target string appeared to be compressed
+ int status = ARCHIVE_FATAL;
+ const void *uncompressed_buffer = NULL;
+
+ switch (zip->entry->compression)
+ {
+#if HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+ zip->entry_bytes_remaining = zip_entry->compressed_size;
+ status = zip_read_data_deflate(a, &uncompressed_buffer,
+ &linkname_full_length, NULL);
+ break;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ case 14: /* ZIPx LZMA compression. */
+ /*(see zip file format specification, section 4.4.5)*/
+ zip->entry_bytes_remaining = zip_entry->compressed_size;
+ status = zip_read_data_zipx_lzma_alone(a, &uncompressed_buffer,
+ &linkname_full_length, NULL);
+ break;
+#endif
+ default: /* Unsupported compression. */
+ break;
+ }
+ if (status == ARCHIVE_OK)
+ {
+ p = uncompressed_buffer;
+ }
+ else
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported ZIP compression method "
+ "during decompression of link entry (%d: %s)",
+ zip->entry->compression,
+ compression_name(zip->entry->compression));
+ return ARCHIVE_FAILED;
+ }
+ }
+ else
+ {
+ p = __archive_read_ahead(a, linkname_length, NULL);
+ }
+
+ if (p == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Truncated Zip file");
+ return ARCHIVE_FATAL;
+ }
+
+ sconv = zip->sconv;
+ if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME))
+ sconv = zip->sconv_utf8;
+ if (sconv == NULL)
+ sconv = zip->sconv_default;
+ if (archive_entry_copy_symlink_l(entry, p, linkname_full_length,
+ sconv) != 0) {
+ if (errno != ENOMEM && sconv == zip->sconv_utf8 &&
+ (zip->entry->zip_flags & ZIP_UTF8_NAME))
+ archive_entry_copy_symlink_l(entry, p,
+ linkname_full_length, NULL);
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Symlink");
+ return (ARCHIVE_FATAL);
+ }
+ /*
+ * Since there is no character-set regulation for
+ * symlink name, do not report the conversion error
+ * in an automatic conversion.
+ */
+ if (sconv != zip->sconv_utf8 ||
+ (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Symlink cannot be converted "
+ "from %s to current locale.",
+ archive_string_conversion_charset_name(
+ sconv));
+ ret = ARCHIVE_WARN;
+ }
+ }
+ zip_entry->uncompressed_size = zip_entry->compressed_size = 0;
+
+ if (__archive_read_consume(a, linkname_length) < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Read error skipping symlink target name");
+ return ARCHIVE_FATAL;
+ }
+ } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ || zip_entry->uncompressed_size > 0) {
+ /* Set the size only if it's meaningful. */
+ archive_entry_set_size(entry, zip_entry->uncompressed_size);
+ }
+ zip->entry_bytes_remaining = zip_entry->compressed_size;
+
+ /* If there's no body, force read_data() to return EOF immediately. */
+ if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ && zip->entry_bytes_remaining < 1)
+ zip->end_of_entry = 1;
+
+ /* Set up a more descriptive format name. */
+ archive_string_empty(&zip->format_name);
+ archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)",
+ version / 10, version % 10,
+ compression_name(zip->entry->compression));
+ a->archive.archive_format_name = zip->format_name.s;
+
+ return (ret);
+}
+
+static int
+check_authentication_code(struct archive_read *a, const void *_p)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+
+ /* Check authentication code. */
+ if (zip->hctx_valid) {
+ const void *p;
+ uint8_t hmac[20];
+ size_t hmac_len = 20;
+ int cmp;
+
+ archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);
+ if (_p == NULL) {
+ /* Read authentication code. */
+ p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL);
+ if (p == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+ } else {
+ p = _p;
+ }
+ cmp = memcmp(hmac, p, AUTH_CODE_SIZE);
+ __archive_read_consume(a, AUTH_CODE_SIZE);
+ if (cmp != 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "ZIP bad Authentication code");
+ return (ARCHIVE_WARN);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Read "uncompressed" data. There are three cases:
+ * 1) We know the size of the data. This is always true for the
+ * seeking reader (we've examined the Central Directory already).
+ * 2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred.
+ * Info-ZIP seems to do this; we know the size but have to grab
+ * the CRC from the data descriptor afterwards.
+ * 3) We're streaming and ZIP_LENGTH_AT_END was specified and
+ * we have no size information. In this case, we can do pretty
+ * well by watching for the data descriptor record. The data
+ * descriptor is 16 bytes and includes a computed CRC that should
+ * provide a strong check.
+ *
+ * TODO: Technically, the PK\007\010 signature is optional.
+ * In the original spec, the data descriptor contained CRC
+ * and size fields but had no leading signature. In practice,
+ * newer writers seem to provide the signature pretty consistently.
+ *
+ * For uncompressed data, the PK\007\010 marker seems essential
+ * to be sure we've actually seen the end of the entry.
+ *
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * zip->end_of_entry if it consumes all of the data.
+ */
+static int
+zip_read_data_none(struct archive_read *a, const void **_buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip;
+ const char *buff;
+ ssize_t bytes_avail;
+ int r;
+
+ (void)offset; /* UNUSED */
+
+ zip = (struct zip *)(a->format->data);
+
+ if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) {
+ const char *p;
+ ssize_t grabbing_bytes = 24;
+
+ if (zip->hctx_valid)
+ grabbing_bytes += AUTH_CODE_SIZE;
+ /* Grab at least 24 bytes. */
+ buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail);
+ if (bytes_avail < grabbing_bytes) {
+ /* Zip archives have end-of-archive markers
+ that are longer than this, so a failure to get at
+ least 24 bytes really does indicate a truncated
+ file. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+ /* Check for a complete PK\007\010 signature, followed
+ * by the correct 4-byte CRC. */
+ p = buff;
+ if (zip->hctx_valid)
+ p += AUTH_CODE_SIZE;
+ if (p[0] == 'P' && p[1] == 'K'
+ && p[2] == '\007' && p[3] == '\010'
+ && (archive_le32dec(p + 4) == zip->entry_crc32
+ || zip->ignore_crc32
+ || (zip->hctx_valid
+ && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) {
+ if (zip->entry->flags & LA_USED_ZIP64) {
+ uint64_t compressed, uncompressed;
+ zip->entry->crc32 = archive_le32dec(p + 4);
+ compressed = archive_le64dec(p + 8);
+ uncompressed = archive_le64dec(p + 16);
+ if (compressed > INT64_MAX || uncompressed >
+ INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Overflow of 64-bit file sizes");
+ return ARCHIVE_FAILED;
+ }
+ zip->entry->compressed_size = compressed;
+ zip->entry->uncompressed_size = uncompressed;
+ zip->unconsumed = 24;
+ } else {
+ zip->entry->crc32 = archive_le32dec(p + 4);
+ zip->entry->compressed_size =
+ archive_le32dec(p + 8);
+ zip->entry->uncompressed_size =
+ archive_le32dec(p + 12);
+ zip->unconsumed = 16;
+ }
+ if (zip->hctx_valid) {
+ r = check_authentication_code(a, buff);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ zip->end_of_entry = 1;
+ return (ARCHIVE_OK);
+ }
+ /* If not at EOF, ensure we consume at least one byte. */
+ ++p;
+
+ /* Scan forward until we see where a PK\007\010 signature
+ * might be. */
+ /* Return bytes up until that point. On the next call,
+ * the code above will verify the data descriptor. */
+ while (p < buff + bytes_avail - 4) {
+ if (p[3] == 'P') { p += 3; }
+ else if (p[3] == 'K') { p += 2; }
+ else if (p[3] == '\007') { p += 1; }
+ else if (p[3] == '\010' && p[2] == '\007'
+ && p[1] == 'K' && p[0] == 'P') {
+ if (zip->hctx_valid)
+ p -= AUTH_CODE_SIZE;
+ break;
+ } else { p += 4; }
+ }
+ bytes_avail = p - buff;
+ } else {
+ if (zip->entry_bytes_remaining == 0) {
+ zip->end_of_entry = 1;
+ if (zip->hctx_valid) {
+ r = check_authentication_code(a, NULL);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ return (ARCHIVE_OK);
+ }
+ /* Grab a bunch of bytes. */
+ buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail <= 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+ if (bytes_avail > zip->entry_bytes_remaining)
+ bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+ }
+ if (zip->tctx_valid || zip->cctx_valid) {
+ size_t dec_size = bytes_avail;
+
+ if (dec_size > zip->decrypted_buffer_size)
+ dec_size = zip->decrypted_buffer_size;
+ if (zip->tctx_valid) {
+ trad_enc_decrypt_update(&zip->tctx,
+ (const uint8_t *)buff, dec_size,
+ zip->decrypted_buffer, dec_size);
+ } else {
+ size_t dsize = dec_size;
+ archive_hmac_sha1_update(&zip->hctx,
+ (const uint8_t *)buff, dec_size);
+ archive_decrypto_aes_ctr_update(&zip->cctx,
+ (const uint8_t *)buff, dec_size,
+ zip->decrypted_buffer, &dsize);
+ }
+ bytes_avail = dec_size;
+ buff = (const char *)zip->decrypted_buffer;
+ }
+ *size = bytes_avail;
+ zip->entry_bytes_remaining -= bytes_avail;
+ zip->entry_uncompressed_bytes_read += bytes_avail;
+ zip->entry_compressed_bytes_read += bytes_avail;
+ zip->unconsumed += bytes_avail;
+ *_buff = buff;
+ return (ARCHIVE_OK);
+}
+
+static int
+consume_optional_marker(struct archive_read *a, struct zip *zip)
+{
+ if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
+ const char *p;
+
+ if (NULL == (p = __archive_read_ahead(a, 24, NULL))) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP end-of-file record");
+ return (ARCHIVE_FATAL);
+ }
+ /* Consume the optional PK\007\010 marker. */
+ if (p[0] == 'P' && p[1] == 'K' &&
+ p[2] == '\007' && p[3] == '\010') {
+ p += 4;
+ zip->unconsumed = 4;
+ }
+ if (zip->entry->flags & LA_USED_ZIP64) {
+ uint64_t compressed, uncompressed;
+ zip->entry->crc32 = archive_le32dec(p);
+ compressed = archive_le64dec(p + 4);
+ uncompressed = archive_le64dec(p + 12);
+ if (compressed > INT64_MAX ||
+ uncompressed > INT64_MAX) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Overflow of 64-bit file sizes");
+ return ARCHIVE_FAILED;
+ }
+ zip->entry->compressed_size = compressed;
+ zip->entry->uncompressed_size = uncompressed;
+ zip->unconsumed += 20;
+ } else {
+ zip->entry->crc32 = archive_le32dec(p);
+ zip->entry->compressed_size = archive_le32dec(p + 4);
+ zip->entry->uncompressed_size = archive_le32dec(p + 8);
+ zip->unconsumed += 12;
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+static int
+zipx_xz_init(struct archive_read *a, struct zip *zip)
+{
+ lzma_ret r;
+
+ if(zip->zipx_lzma_valid) {
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+ }
+
+ memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));
+ r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0);
+ if (r != LZMA_OK) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "xz initialization failed(%d)",
+ r);
+
+ return (ARCHIVE_FAILED);
+ }
+
+ zip->zipx_lzma_valid = 1;
+
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for xz decompression");
+ return (ARCHIVE_FATAL);
+ }
+
+ zip->decompress_init = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
+{
+ lzma_ret r;
+ const uint8_t* p;
+
+#pragma pack(push)
+#pragma pack(1)
+ struct _alone_header {
+ uint8_t bytes[5];
+ uint64_t uncompressed_size;
+ } alone_header;
+#pragma pack(pop)
+
+ if(zip->zipx_lzma_valid) {
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+ }
+
+ /* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma
+ * that is a part of XZ Utils. The stream format stored inside ZIPX
+ * file is a modified "lzma alone" file format, that was used by the
+ * `lzma` utility which was later deprecated in favour of `xz` utility.
+ * Since those formats are nearly the same, we can use a standard
+ * "lzma alone" decoder from XZ Utils. */
+
+ memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));
+ r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX);
+ if (r != LZMA_OK) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "lzma initialization failed(%d)", r);
+
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Flag the cleanup function that we want our lzma-related structures
+ * to be freed later. */
+ zip->zipx_lzma_valid = 1;
+
+ /* The "lzma alone" file format and the stream format inside ZIPx are
+ * almost the same. Here's an example of a structure of "lzma alone"
+ * format:
+ *
+ * $ cat /bin/ls | lzma | xxd | head -n 1
+ * 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814
+ *
+ * 5 bytes 8 bytes n bytes
+ * <lzma_params><uncompressed_size><data...>
+ *
+ * lzma_params is a 5-byte blob that has to be decoded to extract
+ * parameters of this LZMA stream. The uncompressed_size field is an
+ * uint64_t value that contains information about the size of the
+ * uncompressed file, or UINT64_MAX if this value is unknown.
+ * The <data...> part is the actual lzma-compressed data stream.
+ *
+ * Now here's the structure of the stream inside the ZIPX file:
+ *
+ * $ cat stream_inside_zipx | xxd | head -n 1
+ * 00000000: 0914 0500 5d00 8000 0000 2814 .... ....
+ *
+ * 2byte 2byte 5 bytes n bytes
+ * <magic1><magic2><lzma_params><data...>
+ *
+ * This means that the ZIPX file contains an additional magic1 and
+ * magic2 headers, the lzma_params field contains the same parameter
+ * set as in the "lzma alone" format, and the <data...> field is the
+ * same as in the "lzma alone" format as well. Note that also the zipx
+ * format is missing the uncompressed_size field.
+ *
+ * So, in order to use the "lzma alone" decoder for the zipx lzma
+ * stream, we simply need to shuffle around some fields, prepare a new
+ * lzma alone header, feed it into lzma alone decoder so it will
+ * initialize itself properly, and then we can start feeding normal
+ * zipx lzma stream into the decoder.
+ */
+
+ /* Read magic1,magic2,lzma_params from the ZIPX stream. */
+ if(zip->entry_bytes_remaining < 9 || (p = __archive_read_ahead(a, 9, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated lzma data");
+ return (ARCHIVE_FATAL);
+ }
+
+ if(p[2] != 0x05 || p[3] != 0x00) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid lzma data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Prepare an lzma alone header: copy the lzma_params blob into
+ * a proper place into the lzma alone header. */
+ memcpy(&alone_header.bytes[0], p + 4, 5);
+
+ /* Initialize the 'uncompressed size' field to unknown; we'll manually
+ * monitor how many bytes there are still to be uncompressed. */
+ alone_header.uncompressed_size = UINT64_MAX;
+
+ if(!zip->uncompressed_buffer) {
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for lzma decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ zip->zipx_lzma_stream.next_in = (void*) &alone_header;
+ zip->zipx_lzma_stream.avail_in = sizeof(alone_header);
+ zip->zipx_lzma_stream.total_in = 0;
+ zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+ zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
+ zip->zipx_lzma_stream.total_out = 0;
+
+ /* Feed only the header into the lzma alone decoder. This will
+ * effectively initialize the decoder, and will not produce any
+ * output bytes yet. */
+ r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+ if (r != LZMA_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "lzma stream initialization error");
+ return ARCHIVE_FATAL;
+ }
+
+ /* We've already consumed some bytes, so take this into account. */
+ __archive_read_consume(a, 9);
+ zip->entry_bytes_remaining -= 9;
+ zip->entry_compressed_bytes_read += 9;
+
+ zip->decompress_init = 1;
+ return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip* zip = (struct zip *)(a->format->data);
+ int ret;
+ lzma_ret lz_ret;
+ const void* compressed_buf;
+ ssize_t bytes_avail, in_bytes, to_consume = 0;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompressor if not yet initialized. */
+ if (!zip->decompress_init) {
+ ret = zipx_xz_init(a, zip);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+
+ compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated xz file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+ zip->zipx_lzma_stream.next_in = compressed_buf;
+ zip->zipx_lzma_stream.avail_in = in_bytes;
+ zip->zipx_lzma_stream.total_in = 0;
+ zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+ zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
+ zip->zipx_lzma_stream.total_out = 0;
+
+ /* Perform the decompression. */
+ lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+ switch(lz_ret) {
+ case LZMA_DATA_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "xz data error (error %d)", (int) lz_ret);
+ return (ARCHIVE_FATAL);
+
+ case LZMA_NO_CHECK:
+ case LZMA_OK:
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "xz unknown error %d", (int) lz_ret);
+ return (ARCHIVE_FATAL);
+
+ case LZMA_STREAM_END:
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+
+ if((int64_t) zip->zipx_lzma_stream.total_in !=
+ zip->entry_bytes_remaining)
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xz premature end of stream");
+ return (ARCHIVE_FATAL);
+ }
+
+ zip->end_of_entry = 1;
+ break;
+ }
+
+ to_consume = zip->zipx_lzma_stream.total_in;
+
+ __archive_read_consume(a, to_consume);
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
+
+ *size = zip->zipx_lzma_stream.total_out;
+ *buff = zip->uncompressed_buffer;
+
+ ret = consume_optional_marker(a, zip);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip* zip = (struct zip *)(a->format->data);
+ int ret;
+ lzma_ret lz_ret;
+ const void* compressed_buf;
+ ssize_t bytes_avail, in_bytes, to_consume;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompressor if not yet initialized. */
+ if (!zip->decompress_init) {
+ ret = zipx_lzma_alone_init(a, zip);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+
+ /* Fetch more compressed data. The same note as in deflate handler
+ * applies here as well:
+ *
+ * Note: '1' here is a performance optimization. Recall that the
+ * decompression layer returns a count of available bytes; asking for
+ * more than that forces the decompressor to combine reads by copying
+ * data.
+ */
+ compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
+ if (bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated lzma file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Set decompressor parameters. */
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+
+ zip->zipx_lzma_stream.next_in = compressed_buf;
+ zip->zipx_lzma_stream.avail_in = in_bytes;
+ zip->zipx_lzma_stream.total_in = 0;
+ zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+ zip->zipx_lzma_stream.avail_out =
+ /* These lzma_alone streams lack end of stream marker, so let's
+ * make sure the unpacker won't try to unpack more than it's
+ * supposed to. */
+ zipmin((int64_t) zip->uncompressed_buffer_size,
+ zip->entry->uncompressed_size -
+ zip->entry_uncompressed_bytes_read);
+ zip->zipx_lzma_stream.total_out = 0;
+
+ /* Perform the decompression. */
+ lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+ switch(lz_ret) {
+ case LZMA_DATA_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "lzma data error (error %d)", (int) lz_ret);
+ return (ARCHIVE_FATAL);
+
+ /* This case is optional in lzma alone format. It can happen,
+ * but most of the files don't have it. (GitHub #1257) */
+ case LZMA_STREAM_END:
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+ if((int64_t) zip->zipx_lzma_stream.total_in !=
+ zip->entry_bytes_remaining)
+ {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "lzma alone premature end of stream");
+ return (ARCHIVE_FATAL);
+ }
+
+ zip->end_of_entry = 1;
+ break;
+
+ case LZMA_OK:
+ break;
+
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "lzma unknown error %d", (int) lz_ret);
+ return (ARCHIVE_FATAL);
+ }
+
+ to_consume = zip->zipx_lzma_stream.total_in;
+
+ /* Update pointers. */
+ __archive_read_consume(a, to_consume);
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
+
+ if(zip->entry_bytes_remaining == 0) {
+ zip->end_of_entry = 1;
+ }
+
+ /* Return values. */
+ *size = zip->zipx_lzma_stream.total_out;
+ *buff = zip->uncompressed_buffer;
+
+ /* Behave the same way as during deflate decompression. */
+ ret = consume_optional_marker(a, zip);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ /* Free lzma decoder handle because we'll no longer need it. */
+ if(zip->end_of_entry) {
+ lzma_end(&zip->zipx_lzma_stream);
+ zip->zipx_lzma_valid = 0;
+ }
+
+ /* If we're here, then we're good! */
+ return (ARCHIVE_OK);
+}
+#endif /* HAVE_LZMA_H && HAVE_LIBLZMA */
+
+static int
+zipx_ppmd8_init(struct archive_read *a, struct zip *zip)
+{
+ const void* p;
+ uint32_t val;
+ uint32_t order;
+ uint32_t mem;
+ uint32_t restore_method;
+
+ /* Remove previous decompression context if it exists. */
+ if(zip->ppmd8_valid) {
+ __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+ zip->ppmd8_valid = 0;
+ }
+
+ /* Create a new decompression context. */
+ __archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8);
+ zip->ppmd8_stream_failed = 0;
+
+ /* Setup function pointers required by Ppmd8 decompressor. The
+ * 'ppmd_read' function will feed new bytes to the decompressor,
+ * and will increment the 'zip->zipx_ppmd_read_compressed' counter. */
+ zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream;
+ zip->zipx_ppmd_stream.a = a;
+ zip->zipx_ppmd_stream.Read = &ppmd_read;
+
+ /* Reset number of read bytes to 0. */
+ zip->zipx_ppmd_read_compressed = 0;
+
+ /* Read Ppmd8 header (2 bytes). */
+ p = __archive_read_ahead(a, 2, NULL);
+ if(!p) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated file data in PPMd8 stream");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_read_consume(a, 2);
+
+ /* Decode the stream's compression parameters. */
+ val = archive_le16dec(p);
+ order = (val & 15) + 1;
+ mem = ((val >> 4) & 0xff) + 1;
+ restore_method = (val >> 12);
+
+ if(order < 2 || restore_method > 2) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Invalid parameter set in PPMd8 stream (order=%" PRId32 ", "
+ "restore=%" PRId32 ")", order, restore_method);
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Allocate the memory needed to properly decompress the file. */
+ if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Unable to allocate memory for PPMd8 stream: %" PRId32 " bytes",
+ mem << 20);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Signal the cleanup function to release Ppmd8 context in the
+ * cleanup phase. */
+ zip->ppmd8_valid = 1;
+
+ /* Perform further Ppmd8 initialization. */
+ if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "PPMd8 stream range decoder initialization error");
+ return (ARCHIVE_FATAL);
+ }
+
+ __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order,
+ restore_method);
+
+ /* Allocate the buffer that will hold uncompressed data. */
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+
+ if(zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for PPMd8 decompression");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Ppmd8 initialization is done. */
+ zip->decompress_init = 1;
+
+ /* We've already read 2 bytes in the output stream. Additionally,
+ * Ppmd8 initialization code could read some data as well. So we
+ * are advancing the stream by 2 bytes plus whatever number of
+ * bytes Ppmd8 init function used. */
+ zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed;
+
+ return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip* zip = (struct zip *)(a->format->data);
+ int ret;
+ size_t consumed_bytes = 0;
+ ssize_t bytes_avail = 0;
+
+ (void) offset; /* UNUSED */
+
+ /* If we're here for the first time, initialize Ppmd8 decompression
+ * context first. */
+ if(!zip->decompress_init) {
+ ret = zipx_ppmd8_init(a, zip);
+ if(ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ /* Fetch for more data. We're reading 1 byte here, but libarchive
+ * should prefetch more bytes. */
+ (void) __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated PPMd8 file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* This counter will be updated inside ppmd_read(), which at one
+ * point will be called by Ppmd8_DecodeSymbol. */
+ zip->zipx_ppmd_read_compressed = 0;
+
+ /* Decompression loop. */
+ do {
+ int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(
+ &zip->ppmd8);
+ if(sym < 0) {
+ zip->end_of_entry = 1;
+ break;
+ }
+
+ /* This field is set by ppmd_read() when there was no more data
+ * to be read. */
+ if(zip->ppmd8_stream_failed) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated PPMd8 file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym;
+ ++consumed_bytes;
+ } while(consumed_bytes < zip->uncompressed_buffer_size);
+
+ /* Update pointers for libarchive. */
+ *buff = zip->uncompressed_buffer;
+ *size = consumed_bytes;
+
+ /* Update pointers so we can continue decompression in another call. */
+ zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed;
+ zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed;
+ zip->entry_uncompressed_bytes_read += consumed_bytes;
+
+ /* If we're at the end of stream, deinitialize Ppmd8 context. */
+ if(zip->end_of_entry) {
+ __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+ zip->ppmd8_valid = 0;
+ }
+
+ /* Seek for optional marker, same way as in each zip entry. */
+ ret = consume_optional_marker(a, zip);
+ if (ret != ARCHIVE_OK)
+ return ret;
+
+ return ARCHIVE_OK;
+}
+
+#ifdef HAVE_BZLIB_H
+static int
+zipx_bzip2_init(struct archive_read *a, struct zip *zip)
+{
+ int r;
+
+ /* Deallocate already existing BZ2 decompression context if it
+ * exists. */
+ if(zip->bzstream_valid) {
+ BZ2_bzDecompressEnd(&zip->bzstream);
+ zip->bzstream_valid = 0;
+ }
+
+ /* Allocate a new BZ2 decompression context. */
+ memset(&zip->bzstream, 0, sizeof(bz_stream));
+ r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1);
+ if(r != BZ_OK) {
+ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+ "bzip2 initialization failed(%d)",
+ r);
+
+ return ARCHIVE_FAILED;
+ }
+
+ /* Mark the bzstream field to be released in cleanup phase. */
+ zip->bzstream_valid = 1;
+
+ /* (Re)allocate the buffer that will contain decompressed bytes. */
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for bzip2 decompression");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Initialization done. */
+ zip->decompress_init = 1;
+ return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ ssize_t bytes_avail = 0, in_bytes, to_consume;
+ const void *compressed_buff;
+ int r;
+ uint64_t total_out;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompression context if we're here for the first time. */
+ if(!zip->decompress_init) {
+ r = zipx_bzip2_init(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+ }
+
+ /* Fetch more compressed bytes. */
+ compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated bzip2 file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+ if(in_bytes < 1) {
+ /* libbz2 doesn't complain when caller feeds avail_in == 0.
+ * It will actually return success in this case, which is
+ * undesirable. This is why we need to make this check
+ * manually. */
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated bzip2 file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Setup buffer boundaries. */
+ zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
+ zip->bzstream.avail_in = (uint32_t)in_bytes;
+ zip->bzstream.total_in_hi32 = 0;
+ zip->bzstream.total_in_lo32 = 0;
+ zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
+ zip->bzstream.avail_out = (uint32_t)zip->uncompressed_buffer_size;
+ zip->bzstream.total_out_hi32 = 0;
+ zip->bzstream.total_out_lo32 = 0;
+
+ /* Perform the decompression. */
+ r = BZ2_bzDecompress(&zip->bzstream);
+ switch(r) {
+ case BZ_STREAM_END:
+ /* If we're at the end of the stream, deinitialize the
+ * decompression context now. */
+ switch(BZ2_bzDecompressEnd(&zip->bzstream)) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to clean up bzip2 "
+ "decompressor");
+ return ARCHIVE_FATAL;
+ }
+
+ zip->end_of_entry = 1;
+ break;
+ case BZ_OK:
+ /* The decompressor has successfully decoded this
+ * chunk of data, but more data is still in queue. */
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "bzip2 decompression failed");
+ return ARCHIVE_FATAL;
+ }
+
+ /* Update the pointers so decompressor can continue decoding. */
+ to_consume = zip->bzstream.total_in_lo32;
+ __archive_read_consume(a, to_consume);
+
+ total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) |
+ zip->bzstream.total_out_lo32;
+
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += total_out;
+
+ /* Give libarchive its due. */
+ *size = total_out;
+ *buff = zip->uncompressed_buffer;
+
+ /* Seek for optional marker, like in other entries. */
+ r = consume_optional_marker(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+
+ return ARCHIVE_OK;
+}
+
+#endif
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+static int
+zipx_zstd_init(struct archive_read *a, struct zip *zip)
+{
+ size_t r;
+
+ /* Deallocate already existing Zstd decompression context if it
+ * exists. */
+ if(zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstdstream);
+ zip->zstdstream_valid = 0;
+ }
+
+ /* Allocate a new Zstd decompression context. */
+ zip->zstdstream = ZSTD_createDStream();
+
+ r = ZSTD_initDStream(zip->zstdstream);
+ if (ZSTD_isError(r)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error initializing zstd decompressor: %s",
+ ZSTD_getErrorName(r));
+
+ return ARCHIVE_FAILED;
+ }
+
+ /* Mark the zstdstream field to be released in cleanup phase. */
+ zip->zstdstream_valid = 1;
+
+ /* (Re)allocate the buffer that will contain decompressed bytes. */
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = ZSTD_DStreamOutSize();
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for Zstd decompression");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Initialization done. */
+ zip->decompress_init = 1;
+ return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ ssize_t bytes_avail = 0, in_bytes, to_consume;
+ const void *compressed_buff;
+ int r;
+ size_t ret;
+ uint64_t total_out;
+ ZSTD_outBuffer out;
+ ZSTD_inBuffer in;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompression context if we're here for the first time. */
+ if(!zip->decompress_init) {
+ r = zipx_zstd_init(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+ }
+
+ /* Fetch more compressed bytes */
+ compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated zstd file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+ if(in_bytes < 1) {
+ /* zstd doesn't complain when caller feeds avail_in == 0.
+ * It will actually return success in this case, which is
+ * undesirable. This is why we need to make this check
+ * manually. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated zstd file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Setup buffer boundaries */
+ in.src = compressed_buff;
+ in.size = in_bytes;
+ in.pos = 0;
+ out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 };
+
+ /* Perform the decompression. */
+ ret = ZSTD_decompressStream(zip->zstdstream, &out, &in);
+ if (ZSTD_isError(ret)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error during zstd decompression: %s",
+ ZSTD_getErrorName(ret));
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Check end of the stream. */
+ if (ret == 0) {
+ if ((in.pos == in.size) && (out.pos < out.size)) {
+ zip->end_of_entry = 1;
+ ZSTD_freeDStream(zip->zstdstream);
+ zip->zstdstream_valid = 0;
+ }
+ }
+
+ /* Update the pointers so decompressor can continue decoding. */
+ to_consume = in.pos;
+ __archive_read_consume(a, to_consume);
+
+ total_out = out.pos;
+
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += total_out;
+
+ /* Give libarchive its due. */
+ *size = total_out;
+ *buff = zip->uncompressed_buffer;
+
+ /* Seek for optional marker, like in other entries. */
+ r = consume_optional_marker(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+
+ return ARCHIVE_OK;
+}
+#endif
+
+#ifdef HAVE_ZLIB_H
+static int
+zip_deflate_init(struct archive_read *a, struct zip *zip)
+{
+ int r;
+
+ /* If we haven't yet read any data, initialize the decompressor. */
+ if (!zip->decompress_init) {
+ if (zip->stream_valid)
+ r = inflateReset(&zip->stream);
+ else
+ r = inflateInit2(&zip->stream,
+ -15 /* Don't check for zlib header */);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize ZIP decompression.");
+ return (ARCHIVE_FATAL);
+ }
+ /* Stream structure has been set up. */
+ zip->stream_valid = 1;
+ /* We've initialized decompression for this stream. */
+ zip->decompress_init = 1;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_deflate(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip;
+ ssize_t bytes_avail;
+ const void *compressed_buff, *sp;
+ int r;
+
+ (void)offset; /* UNUSED */
+
+ zip = (struct zip *)(a->format->data);
+
+ /* If the buffer hasn't been allocated, allocate it now. */
+ if (zip->uncompressed_buffer == NULL) {
+ zip->uncompressed_buffer_size = 256 * 1024;
+ zip->uncompressed_buffer
+ = (unsigned char *)malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ZIP decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ r = zip_deflate_init(a, zip);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /*
+ * Note: '1' here is a performance optimization.
+ * Recall that the decompression layer returns a count of
+ * available bytes; asking for more than that forces the
+ * decompressor to combine reads by copying data.
+ */
+ compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail);
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && bytes_avail > zip->entry_bytes_remaining) {
+ bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+ }
+ if (bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (zip->tctx_valid || zip->cctx_valid) {
+ if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
+ size_t buff_remaining =
+ (zip->decrypted_buffer +
+ zip->decrypted_buffer_size)
+ - (zip->decrypted_ptr +
+ zip->decrypted_bytes_remaining);
+
+ if (buff_remaining > (size_t)bytes_avail)
+ buff_remaining = (size_t)bytes_avail;
+
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) &&
+ zip->entry_bytes_remaining > 0) {
+ if ((int64_t)(zip->decrypted_bytes_remaining
+ + buff_remaining)
+ > zip->entry_bytes_remaining) {
+ if (zip->entry_bytes_remaining <
+ (int64_t)zip->decrypted_bytes_remaining)
+ buff_remaining = 0;
+ else
+ buff_remaining =
+ (size_t)zip->entry_bytes_remaining
+ - zip->decrypted_bytes_remaining;
+ }
+ }
+ if (buff_remaining > 0) {
+ if (zip->tctx_valid) {
+ trad_enc_decrypt_update(&zip->tctx,
+ compressed_buff, buff_remaining,
+ zip->decrypted_ptr
+ + zip->decrypted_bytes_remaining,
+ buff_remaining);
+ } else {
+ size_t dsize = buff_remaining;
+ archive_decrypto_aes_ctr_update(
+ &zip->cctx,
+ compressed_buff, buff_remaining,
+ zip->decrypted_ptr
+ + zip->decrypted_bytes_remaining,
+ &dsize);
+ }
+ zip->decrypted_bytes_remaining +=
+ buff_remaining;
+ }
+ }
+ bytes_avail = zip->decrypted_bytes_remaining;
+ compressed_buff = (const char *)zip->decrypted_ptr;
+ }
+
+ /*
+ * A bug in zlib.h: stream.next_in should be marked 'const'
+ * but isn't (the library never alters data through the
+ * next_in pointer, only reads it). The result: this ugly
+ * cast to remove 'const'.
+ */
+ zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff;
+ zip->stream.avail_in = (uInt)bytes_avail;
+ zip->stream.total_in = 0;
+ zip->stream.next_out = zip->uncompressed_buffer;
+ zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size;
+ zip->stream.total_out = 0;
+
+ r = inflate(&zip->stream, 0);
+ switch (r) {
+ case Z_OK:
+ break;
+ case Z_STREAM_END:
+ zip->end_of_entry = 1;
+ break;
+ case Z_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory for ZIP decompression");
+ return (ARCHIVE_FATAL);
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP decompression failed (%d)", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Consume as much as the compressor actually used. */
+ bytes_avail = zip->stream.total_in;
+ if (zip->tctx_valid || zip->cctx_valid) {
+ zip->decrypted_bytes_remaining -= bytes_avail;
+ if (zip->decrypted_bytes_remaining == 0)
+ zip->decrypted_ptr = zip->decrypted_buffer;
+ else
+ zip->decrypted_ptr += bytes_avail;
+ }
+ /* Calculate compressed data as much as we used.*/
+ if (zip->hctx_valid)
+ archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail);
+ __archive_read_consume(a, bytes_avail);
+ zip->entry_bytes_remaining -= bytes_avail;
+ zip->entry_compressed_bytes_read += bytes_avail;
+
+ *size = zip->stream.total_out;
+ zip->entry_uncompressed_bytes_read += zip->stream.total_out;
+ *buff = zip->uncompressed_buffer;
+
+ if (zip->end_of_entry && zip->hctx_valid) {
+ r = check_authentication_code(a, NULL);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ r = consume_optional_marker(a, zip);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ return (ARCHIVE_OK);
+}
+#endif
+
+static int
+read_decryption_header(struct archive_read *a)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ const char *p;
+ unsigned int remaining_size;
+ unsigned int ts;
+
+ /*
+ * Read an initialization vector data field.
+ */
+ p = __archive_read_ahead(a, 2, NULL);
+ if (p == NULL)
+ goto truncated;
+ ts = zip->iv_size;
+ zip->iv_size = archive_le16dec(p);
+ __archive_read_consume(a, 2);
+ if (ts < zip->iv_size) {
+ free(zip->iv);
+ zip->iv = NULL;
+ }
+ p = __archive_read_ahead(a, zip->iv_size, NULL);
+ if (p == NULL)
+ goto truncated;
+ if (zip->iv == NULL) {
+ zip->iv = malloc(zip->iv_size);
+ if (zip->iv == NULL)
+ goto nomem;
+ }
+ memcpy(zip->iv, p, zip->iv_size);
+ __archive_read_consume(a, zip->iv_size);
+
+ /*
+ * Read a size of remaining decryption header field.
+ */
+ p = __archive_read_ahead(a, 14, NULL);
+ if (p == NULL)
+ goto truncated;
+ remaining_size = archive_le32dec(p);
+ if (remaining_size < 16 || remaining_size > (1 << 18))
+ goto corrupted;
+
+ /* Check if format version is supported. */
+ if (archive_le16dec(p+4) != 3) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported encryption format version: %u",
+ archive_le16dec(p+4));
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Read an encryption algorithm field.
+ */
+ zip->alg_id = archive_le16dec(p+6);
+ switch (zip->alg_id) {
+ case 0x6601:/* DES */
+ case 0x6602:/* RC2 */
+ case 0x6603:/* 3DES 168 */
+ case 0x6609:/* 3DES 112 */
+ case 0x660E:/* AES 128 */
+ case 0x660F:/* AES 192 */
+ case 0x6610:/* AES 256 */
+ case 0x6702:/* RC2 (version >= 5.2) */
+ case 0x6720:/* Blowfish */
+ case 0x6721:/* Twofish */
+ case 0x6801:/* RC4 */
+ /* Supported encryption algorithm. */
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown encryption algorithm: %u", zip->alg_id);
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Read a bit length field.
+ */
+ zip->bit_len = archive_le16dec(p+8);
+
+ /*
+ * Read a flags field.
+ */
+ zip->flags = archive_le16dec(p+10);
+ switch (zip->flags & 0xf000) {
+ case 0x0001: /* Password is required to decrypt. */
+ case 0x0002: /* Certificates only. */
+ case 0x0003: /* Password or certificate required to decrypt. */
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown encryption flag: %u", zip->flags);
+ return (ARCHIVE_FAILED);
+ }
+ if ((zip->flags & 0xf000) == 0 ||
+ (zip->flags & 0xf000) == 0x4000) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unknown encryption flag: %u", zip->flags);
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Read an encrypted random data field.
+ */
+ ts = zip->erd_size;
+ zip->erd_size = archive_le16dec(p+12);
+ __archive_read_consume(a, 14);
+ if ((zip->erd_size & 0xf) != 0 ||
+ (zip->erd_size + 16) > remaining_size ||
+ (zip->erd_size + 16) < zip->erd_size)
+ goto corrupted;
+
+ if (ts < zip->erd_size) {
+ free(zip->erd);
+ zip->erd = NULL;
+ }
+ p = __archive_read_ahead(a, zip->erd_size, NULL);
+ if (p == NULL)
+ goto truncated;
+ if (zip->erd == NULL) {
+ zip->erd = malloc(zip->erd_size);
+ if (zip->erd == NULL)
+ goto nomem;
+ }
+ memcpy(zip->erd, p, zip->erd_size);
+ __archive_read_consume(a, zip->erd_size);
+
+ /*
+ * Read a reserved data field.
+ */
+ p = __archive_read_ahead(a, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ /* Reserved data size should be zero. */
+ if (archive_le32dec(p) != 0)
+ goto corrupted;
+ __archive_read_consume(a, 4);
+
+ /*
+ * Read a password validation data field.
+ */
+ p = __archive_read_ahead(a, 2, NULL);
+ if (p == NULL)
+ goto truncated;
+ ts = zip->v_size;
+ zip->v_size = archive_le16dec(p);
+ __archive_read_consume(a, 2);
+ if ((zip->v_size & 0x0f) != 0 ||
+ (zip->erd_size + zip->v_size + 16) > remaining_size ||
+ (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size))
+ goto corrupted;
+ if (ts < zip->v_size) {
+ free(zip->v_data);
+ zip->v_data = NULL;
+ }
+ p = __archive_read_ahead(a, zip->v_size, NULL);
+ if (p == NULL)
+ goto truncated;
+ if (zip->v_data == NULL) {
+ zip->v_data = malloc(zip->v_size);
+ if (zip->v_data == NULL)
+ goto nomem;
+ }
+ memcpy(zip->v_data, p, zip->v_size);
+ __archive_read_consume(a, zip->v_size);
+
+ p = __archive_read_ahead(a, 4, NULL);
+ if (p == NULL)
+ goto truncated;
+ zip->v_crc32 = archive_le32dec(p);
+ __archive_read_consume(a, 4);
+
+ /*return (ARCHIVE_OK);
+ * This is not fully implemented yet.*/
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Encrypted file is unsupported");
+ return (ARCHIVE_FAILED);
+truncated:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+corrupted:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Corrupted ZIP file data");
+ return (ARCHIVE_FATAL);
+nomem:
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ZIP decryption");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+zip_alloc_decryption_buffer(struct archive_read *a)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ size_t bs = 256 * 1024;
+
+ if (zip->decrypted_buffer == NULL) {
+ zip->decrypted_buffer_size = bs;
+ zip->decrypted_buffer = malloc(bs);
+ if (zip->decrypted_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ZIP decryption");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ zip->decrypted_ptr = zip->decrypted_buffer;
+ return (ARCHIVE_OK);
+}
+
+static int
+init_traditional_PKWARE_decryption(struct archive_read *a)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ const void *p;
+ int retry;
+ int r;
+
+ if (zip->tctx_valid)
+ return (ARCHIVE_OK);
+
+ /*
+ Read the 12 bytes encryption header stored at
+ the start of the data area.
+ */
+#define ENC_HEADER_SIZE 12
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && zip->entry_bytes_remaining < ENC_HEADER_SIZE) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated Zip encrypted body: only %jd bytes available",
+ (intmax_t)zip->entry_bytes_remaining);
+ return (ARCHIVE_FATAL);
+ }
+
+ p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+
+ for (retry = 0;; retry++) {
+ const char *passphrase;
+ uint8_t crcchk;
+
+ passphrase = __archive_read_next_passphrase(a);
+ if (passphrase == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ (retry > 0)?
+ "Incorrect passphrase":
+ "Passphrase required for this entry");
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Initialize ctx for Traditional PKWARE Decryption.
+ */
+ r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase),
+ p, ENC_HEADER_SIZE, &crcchk);
+ if (r == 0 && crcchk == zip->entry->decdat)
+ break;/* The passphrase is OK. */
+ if (retry > 10000) {
+ /* Avoid infinity loop. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Too many incorrect passphrases");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ __archive_read_consume(a, ENC_HEADER_SIZE);
+ zip->tctx_valid = 1;
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
+ zip->entry_bytes_remaining -= ENC_HEADER_SIZE;
+ }
+ /*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/
+ zip->entry_compressed_bytes_read += ENC_HEADER_SIZE;
+ zip->decrypted_bytes_remaining = 0;
+
+ return (zip_alloc_decryption_buffer(a));
+#undef ENC_HEADER_SIZE
+}
+
+static int
+init_WinZip_AES_decryption(struct archive_read *a)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ const void *p;
+ const uint8_t *pv;
+ size_t key_len, salt_len;
+ uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+ int retry;
+ int r;
+
+ if (zip->cctx_valid || zip->hctx_valid)
+ return (ARCHIVE_OK);
+
+ switch (zip->entry->aes_extra.strength) {
+ case 1: salt_len = 8; key_len = 16; break;
+ case 2: salt_len = 12; key_len = 24; break;
+ case 3: salt_len = 16; key_len = 32; break;
+ default: goto corrupted;
+ }
+ p = __archive_read_ahead(a, salt_len + 2, NULL);
+ if (p == NULL)
+ goto truncated;
+
+ for (retry = 0;; retry++) {
+ const char *passphrase;
+
+ passphrase = __archive_read_next_passphrase(a);
+ if (passphrase == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ (retry > 0)?
+ "Incorrect passphrase":
+ "Passphrase required for this entry");
+ return (ARCHIVE_FAILED);
+ }
+ memset(derived_key, 0, sizeof(derived_key));
+ r = archive_pbkdf2_sha1(passphrase, strlen(passphrase),
+ p, salt_len, 1000, derived_key, key_len * 2 + 2);
+ if (r != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Decryption is unsupported due to lack of "
+ "crypto library");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Check password verification value. */
+ pv = ((const uint8_t *)p) + salt_len;
+ if (derived_key[key_len * 2] == pv[0] &&
+ derived_key[key_len * 2 + 1] == pv[1])
+ break;/* The passphrase is OK. */
+ if (retry > 10000) {
+ /* Avoid infinity loop. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Too many incorrect passphrases");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);
+ if (r != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Decryption is unsupported due to lack of crypto library");
+ return (ARCHIVE_FAILED);
+ }
+ r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len);
+ if (r != 0) {
+ archive_decrypto_aes_ctr_release(&zip->cctx);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to initialize HMAC-SHA1");
+ return (ARCHIVE_FAILED);
+ }
+ zip->cctx_valid = zip->hctx_valid = 1;
+ __archive_read_consume(a, salt_len + 2);
+ zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE;
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ && zip->entry_bytes_remaining < 0)
+ goto corrupted;
+ zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE;
+ zip->decrypted_bytes_remaining = 0;
+
+ zip->entry->compression = zip->entry->aes_extra.compression;
+ return (zip_alloc_decryption_buffer(a));
+
+truncated:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+corrupted:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Corrupted ZIP file data");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_zip_read_data(struct archive_read *a,
+ const void **buff, size_t *size, int64_t *offset)
+{
+ int r;
+ struct zip *zip = (struct zip *)(a->format->data);
+
+ if (zip->has_encrypted_entries ==
+ ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+ zip->has_encrypted_entries = 0;
+ }
+
+ *offset = zip->entry_uncompressed_bytes_read;
+ *size = 0;
+ *buff = NULL;
+
+ /* If we hit end-of-entry last time, return ARCHIVE_EOF. */
+ if (zip->end_of_entry)
+ return (ARCHIVE_EOF);
+
+ /* Return EOF immediately if this is a non-regular file. */
+ if (AE_IFREG != (zip->entry->mode & AE_IFMT))
+ return (ARCHIVE_EOF);
+
+ __archive_read_consume(a, zip->unconsumed);
+ zip->unconsumed = 0;
+
+ if (zip->init_decryption) {
+ zip->has_encrypted_entries = 1;
+ if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)
+ r = read_decryption_header(a);
+ else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)
+ r = init_WinZip_AES_decryption(a);
+ else
+ r = init_traditional_PKWARE_decryption(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ zip->init_decryption = 0;
+ }
+
+ switch(zip->entry->compression) {
+ case 0: /* No compression. */
+ r = zip_read_data_none(a, buff, size, offset);
+ break;
+#ifdef HAVE_BZLIB_H
+ case 12: /* ZIPx bzip2 compression. */
+ r = zip_read_data_zipx_bzip2(a, buff, size, offset);
+ break;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ case 14: /* ZIPx LZMA compression. */
+ r = zip_read_data_zipx_lzma_alone(a, buff, size, offset);
+ break;
+ case 95: /* ZIPx XZ compression. */
+ r = zip_read_data_zipx_xz(a, buff, size, offset);
+ break;
+#endif
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ case 93: /* ZIPx Zstd compression. */
+ r = zip_read_data_zipx_zstd(a, buff, size, offset);
+ break;
+#endif
+ /* PPMd support is built-in, so we don't need any #if guards. */
+ case 98: /* ZIPx PPMd compression. */
+ r = zip_read_data_zipx_ppmd(a, buff, size, offset);
+ break;
+
+#ifdef HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+ r = zip_read_data_deflate(a, buff, size, offset);
+ break;
+#endif
+ default: /* Unsupported compression. */
+ /* Return a warning. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported ZIP compression method (%d: %s)",
+ zip->entry->compression, compression_name(zip->entry->compression));
+ /* We can't decompress this entry, but we will
+ * be able to skip() it and try the next entry. */
+ return (ARCHIVE_FAILED);
+ break;
+ }
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Update checksum */
+ if (*size)
+ zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff,
+ (unsigned)*size);
+ /* If we hit the end, swallow any end-of-data marker. */
+ if (zip->end_of_entry) {
+ /* Check file size, CRC against these values. */
+ if (zip->entry->compressed_size !=
+ zip->entry_compressed_bytes_read) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP compressed data is wrong size "
+ "(read %jd, expected %jd)",
+ (intmax_t)zip->entry_compressed_bytes_read,
+ (intmax_t)zip->entry->compressed_size);
+ return (ARCHIVE_WARN);
+ }
+ /* Size field only stores the lower 32 bits of the actual
+ * size. */
+ if ((zip->entry->uncompressed_size & UINT32_MAX)
+ != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP uncompressed data is wrong size "
+ "(read %jd, expected %jd)\n",
+ (intmax_t)zip->entry_uncompressed_bytes_read,
+ (intmax_t)zip->entry->uncompressed_size);
+ return (ARCHIVE_WARN);
+ }
+ /* Check computed CRC against header */
+ if ((!zip->hctx_valid ||
+ zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) &&
+ zip->entry->crc32 != zip->entry_crc32
+ && !zip->ignore_crc32) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "ZIP bad CRC: 0x%lx should be 0x%lx",
+ (unsigned long)zip->entry_crc32,
+ (unsigned long)zip->entry->crc32);
+ return (ARCHIVE_WARN);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_zip_cleanup(struct archive_read *a)
+{
+ struct zip *zip;
+ struct zip_entry *zip_entry, *next_zip_entry;
+
+ zip = (struct zip *)(a->format->data);
+
+#ifdef HAVE_ZLIB_H
+ if (zip->stream_valid)
+ inflateEnd(&zip->stream);
+#endif
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+ if (zip->zipx_lzma_valid) {
+ lzma_end(&zip->zipx_lzma_stream);
+ }
+#endif
+
+#ifdef HAVE_BZLIB_H
+ if (zip->bzstream_valid) {
+ BZ2_bzDecompressEnd(&zip->bzstream);
+ }
+#endif
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ if (zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstdstream);
+ }
+#endif
+
+ free(zip->uncompressed_buffer);
+
+ if (zip->ppmd8_valid)
+ __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+
+ if (zip->zip_entries) {
+ zip_entry = zip->zip_entries;
+ while (zip_entry != NULL) {
+ next_zip_entry = zip_entry->next;
+ archive_string_free(&zip_entry->rsrcname);
+ free(zip_entry);
+ zip_entry = next_zip_entry;
+ }
+ }
+ free(zip->decrypted_buffer);
+ if (zip->cctx_valid)
+ archive_decrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ free(zip->iv);
+ free(zip->erd);
+ free(zip->v_data);
+ archive_string_free(&zip->format_name);
+ free(zip);
+ (a->format->data) = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_zip_has_encrypted_entries(struct archive_read *_a)
+{
+ if (_a && _a->format) {
+ struct zip * zip = (struct zip *)_a->format->data;
+ if (zip) {
+ return zip->has_encrypted_entries;
+ }
+ }
+ return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+static int
+archive_read_format_zip_options(struct archive_read *a,
+ const char *key, const char *val)
+{
+ struct zip *zip;
+ int ret = ARCHIVE_FAILED;
+
+ zip = (struct zip *)(a->format->data);
+ if (strcmp(key, "compat-2x") == 0) {
+ /* Handle filenames as libarchive 2.x */
+ zip->init_default_conversion = (val != NULL) ? 1 : 0;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "zip: hdrcharset option needs a character-set name"
+ );
+ else {
+ zip->sconv = archive_string_conversion_from_charset(
+ &a->archive, val, 0);
+ if (zip->sconv != NULL) {
+ if (strcmp(val, "UTF-8") == 0)
+ zip->sconv_utf8 = zip->sconv;
+ ret = ARCHIVE_OK;
+ } else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ } else if (strcmp(key, "ignorecrc32") == 0) {
+ /* Mostly useful for testing. */
+ if (val == NULL || val[0] == 0) {
+ zip->crc32func = real_crc32;
+ zip->ignore_crc32 = 0;
+ } else {
+ zip->crc32func = fake_crc32;
+ zip->ignore_crc32 = 1;
+ }
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "mac-ext") == 0) {
+ zip->process_mac_extensions = (val != NULL && val[0] != 0);
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+int
+archive_read_support_format_zip(struct archive *a)
+{
+ int r;
+ r = archive_read_support_format_zip_streamable(a);
+ if (r != ARCHIVE_OK)
+ return r;
+ return (archive_read_support_format_zip_seekable(a));
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Streaming-mode support
+ */
+
+
+static int
+archive_read_support_format_zip_capabilities_streamable(struct archive_read * a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+ ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+static int
+archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid)
+{
+ const char *p;
+
+ (void)best_bid; /* UNUSED */
+
+ if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
+ return (-1);
+
+ /*
+ * Bid of 29 here comes from:
+ * + 16 bits for "PK",
+ * + next 16-bit field has 6 options so contributes
+ * about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits
+ *
+ * So we've effectively verified ~29 total bits of check data.
+ */
+ if (p[0] == 'P' && p[1] == 'K') {
+ if ((p[2] == '\001' && p[3] == '\002')
+ || (p[2] == '\003' && p[3] == '\004')
+ || (p[2] == '\005' && p[3] == '\006')
+ || (p[2] == '\006' && p[3] == '\006')
+ || (p[2] == '\007' && p[3] == '\010')
+ || (p[2] == '0' && p[3] == '0'))
+ return (29);
+ }
+
+ /* TODO: It's worth looking ahead a little bit for a valid
+ * PK signature. In particular, that would make it possible
+ * to read some UUEncoded SFX files or SFX files coming from
+ * a network socket. */
+
+ return (0);
+}
+
+static int
+archive_read_format_zip_streamable_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct zip *zip;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "ZIP";
+
+ zip = (struct zip *)(a->format->data);
+
+ /*
+ * It should be sufficient to call archive_read_next_header() for
+ * a reader to determine if an entry is encrypted or not. If the
+ * encryption of an entry is only detectable when calling
+ * archive_read_data(), so be it. We'll do the same check there
+ * as well.
+ */
+ if (zip->has_encrypted_entries ==
+ ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)
+ zip->has_encrypted_entries = 0;
+
+ /* Make sure we have a zip_entry structure to use. */
+ if (zip->zip_entries == NULL) {
+ zip->zip_entries = malloc(sizeof(struct zip_entry));
+ if (zip->zip_entries == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory");
+ return ARCHIVE_FATAL;
+ }
+ }
+ zip->entry = zip->zip_entries;
+ memset(zip->entry, 0, sizeof(struct zip_entry));
+
+ if (zip->cctx_valid)
+ archive_decrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+ __archive_read_reset_passphrase(a);
+
+ /* Search ahead for the next local file header. */
+ __archive_read_consume(a, zip->unconsumed);
+ zip->unconsumed = 0;
+ for (;;) {
+ int64_t skipped = 0;
+ const char *p, *end;
+ ssize_t bytes;
+
+ p = __archive_read_ahead(a, 4, &bytes);
+ if (p == NULL)
+ return (ARCHIVE_FATAL);
+ end = p + bytes;
+
+ while (p + 4 <= end) {
+ if (p[0] == 'P' && p[1] == 'K') {
+ if (p[2] == '\003' && p[3] == '\004') {
+ /* Regular file entry. */
+ __archive_read_consume(a, skipped);
+ return zip_read_local_file_header(a,
+ entry, zip);
+ }
+
+ /*
+ * TODO: We cannot restore permissions
+ * based only on the local file headers.
+ * Consider scanning the central
+ * directory and returning additional
+ * entries for at least directories.
+ * This would allow us to properly set
+ * directory permissions.
+ *
+ * This won't help us fix symlinks
+ * and may not help with regular file
+ * permissions, either. <sigh>
+ */
+ if (p[2] == '\001' && p[3] == '\002') {
+ return (ARCHIVE_EOF);
+ }
+
+ /* End of central directory? Must be an
+ * empty archive. */
+ if ((p[2] == '\005' && p[3] == '\006')
+ || (p[2] == '\006' && p[3] == '\006'))
+ return (ARCHIVE_EOF);
+ }
+ ++p;
+ ++skipped;
+ }
+ __archive_read_consume(a, skipped);
+ }
+}
+
+static int
+archive_read_format_zip_read_data_skip_streamable(struct archive_read *a)
+{
+ struct zip *zip;
+ int64_t bytes_skipped;
+
+ zip = (struct zip *)(a->format->data);
+ bytes_skipped = __archive_read_consume(a, zip->unconsumed);
+ zip->unconsumed = 0;
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+
+ /* If we've already read to end of data, we're done. */
+ if (zip->end_of_entry)
+ return (ARCHIVE_OK);
+
+ /* So we know we're streaming... */
+ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+ || zip->entry->compressed_size > 0) {
+ /* We know the compressed length, so we can just skip. */
+ bytes_skipped = __archive_read_consume(a,
+ zip->entry_bytes_remaining);
+ if (bytes_skipped < 0)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+ }
+
+ if (zip->init_decryption) {
+ int r;
+
+ zip->has_encrypted_entries = 1;
+ if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)
+ r = read_decryption_header(a);
+ else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)
+ r = init_WinZip_AES_decryption(a);
+ else
+ r = init_traditional_PKWARE_decryption(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ zip->init_decryption = 0;
+ }
+
+ /* We're streaming and we don't know the length. */
+ /* If the body is compressed and we know the format, we can
+ * find an exact end-of-entry by decompressing it. */
+ switch (zip->entry->compression) {
+#ifdef HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+ while (!zip->end_of_entry) {
+ int64_t offset = 0;
+ const void *buff = NULL;
+ size_t size = 0;
+ int r;
+ r = zip_read_data_deflate(a, &buff, &size, &offset);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ return ARCHIVE_OK;
+#endif
+ default: /* Uncompressed or unknown. */
+ /* Scan for a PK\007\010 signature. */
+ for (;;) {
+ const char *p, *buff;
+ ssize_t bytes_avail;
+ buff = __archive_read_ahead(a, 16, &bytes_avail);
+ if (bytes_avail < 16) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file data");
+ return (ARCHIVE_FATAL);
+ }
+ p = buff;
+ while (p <= buff + bytes_avail - 16) {
+ if (p[3] == 'P') { p += 3; }
+ else if (p[3] == 'K') { p += 2; }
+ else if (p[3] == '\007') { p += 1; }
+ else if (p[3] == '\010' && p[2] == '\007'
+ && p[1] == 'K' && p[0] == 'P') {
+ if (zip->entry->flags & LA_USED_ZIP64)
+ __archive_read_consume(a,
+ p - buff + 24);
+ else
+ __archive_read_consume(a,
+ p - buff + 16);
+ return ARCHIVE_OK;
+ } else { p += 4; }
+ }
+ __archive_read_consume(a, p - buff);
+ }
+ }
+}
+
+int
+archive_read_support_format_zip_streamable(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct zip *zip;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_zip");
+
+ zip = (struct zip *)calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Streamable reader doesn't support mac extensions. */
+ zip->process_mac_extensions = 0;
+
+ /*
+ * Until enough data has been read, we cannot tell about
+ * any encrypted entries yet.
+ */
+ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+ zip->crc32func = real_crc32;
+
+ r = __archive_read_register_format(a,
+ zip,
+ "zip",
+ archive_read_format_zip_streamable_bid,
+ archive_read_format_zip_options,
+ archive_read_format_zip_streamable_read_header,
+ archive_read_format_zip_read_data,
+ archive_read_format_zip_read_data_skip_streamable,
+ NULL,
+ archive_read_format_zip_cleanup,
+ archive_read_support_format_zip_capabilities_streamable,
+ archive_read_format_zip_has_encrypted_entries);
+
+ if (r != ARCHIVE_OK)
+ free(zip);
+ return (ARCHIVE_OK);
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Seeking-mode support
+ */
+
+static int
+archive_read_support_format_zip_capabilities_seekable(struct archive_read * a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+ ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+/*
+ * TODO: This is a performance sink because it forces the read core to
+ * drop buffered data from the start of file, which will then have to
+ * be re-read again if this bidder loses.
+ *
+ * We workaround this a little by passing in the best bid so far so
+ * that later bidders can do nothing if they know they'll never
+ * outbid. But we can certainly do better...
+ */
+static int
+read_eocd(struct zip *zip, const char *p, int64_t current_offset)
+{
+ uint16_t disk_num;
+ uint32_t cd_size, cd_offset;
+
+ disk_num = archive_le16dec(p + 4);
+ cd_size = archive_le32dec(p + 12);
+ cd_offset = archive_le32dec(p + 16);
+
+ /* Sanity-check the EOCD we've found. */
+
+ /* This must be the first volume. */
+ if (disk_num != 0)
+ return 0;
+ /* Central directory must be on this volume. */
+ if (disk_num != archive_le16dec(p + 6))
+ return 0;
+ /* All central directory entries must be on this volume. */
+ if (archive_le16dec(p + 10) != archive_le16dec(p + 8))
+ return 0;
+ /* Central directory can't extend beyond start of EOCD record. */
+ if (cd_offset + cd_size > current_offset)
+ return 0;
+
+ /* Save the central directory location for later use. */
+ zip->central_directory_offset = cd_offset;
+ zip->central_directory_offset_adjusted = current_offset - cd_size;
+
+ /* This is just a tiny bit higher than the maximum
+ returned by the streaming Zip bidder. This ensures
+ that the more accurate seeking Zip parser wins
+ whenever seek is available. */
+ return 32;
+}
+
+/*
+ * Examine Zip64 EOCD locator: If it's valid, store the information
+ * from it.
+ */
+static int
+read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p)
+{
+ int64_t eocd64_offset;
+ int64_t eocd64_size;
+
+ /* Sanity-check the locator record. */
+
+ /* Central dir must be on first volume. */
+ if (archive_le32dec(p + 4) != 0)
+ return 0;
+ /* Must be only a single volume. */
+ if (archive_le32dec(p + 16) != 1)
+ return 0;
+
+ /* Find the Zip64 EOCD record. */
+ eocd64_offset = archive_le64dec(p + 8);
+ if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0)
+ return 0;
+ if ((p = __archive_read_ahead(a, 56, NULL)) == NULL)
+ return 0;
+ /* Make sure we can read all of it. */
+ eocd64_size = archive_le64dec(p + 4) + 12;
+ if (eocd64_size < 56 || eocd64_size > 16384)
+ return 0;
+ if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL)
+ return 0;
+
+ /* Sanity-check the EOCD64 */
+ if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */
+ return 0;
+ if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */
+ return 0;
+ /* CD can't be split. */
+ if (archive_le64dec(p + 24) != archive_le64dec(p + 32))
+ return 0;
+
+ /* Save the central directory offset for later use. */
+ zip->central_directory_offset = archive_le64dec(p + 48);
+ /* TODO: Needs scanning backwards to find the eocd64 instead of assuming */
+ zip->central_directory_offset_adjusted = zip->central_directory_offset;
+
+ return 32;
+}
+
+static int
+archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
+{
+ struct zip *zip = (struct zip *)a->format->data;
+ int64_t file_size, current_offset;
+ const char *p;
+ int i, tail;
+
+ /* If someone has already bid more than 32, then avoid
+ trashing the look-ahead buffers with a seek. */
+ if (best_bid > 32)
+ return (-1);
+
+ file_size = __archive_read_seek(a, 0, SEEK_END);
+ if (file_size <= 0)
+ return 0;
+
+ /* Search last 16k of file for end-of-central-directory
+ * record (which starts with PK\005\006) */
+ tail = (int)zipmin(1024 * 16, file_size);
+ current_offset = __archive_read_seek(a, -tail, SEEK_END);
+ if (current_offset < 0)
+ return 0;
+ if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL)
+ return 0;
+ /* Boyer-Moore search backwards from the end, since we want
+ * to match the last EOCD in the file (there can be more than
+ * one if there is an uncompressed Zip archive as a member
+ * within this Zip archive). */
+ for (i = tail - 22; i > 0;) {
+ switch (p[i]) {
+ case 'P':
+ if (memcmp(p + i, "PK\005\006", 4) == 0) {
+ int ret = read_eocd(zip, p + i,
+ current_offset + i);
+ /* Zip64 EOCD locator precedes
+ * regular EOCD if present. */
+ if (i >= 20 && memcmp(p + i - 20, "PK\006\007", 4) == 0) {
+ int ret_zip64 = read_zip64_eocd(a, zip, p + i - 20);
+ if (ret_zip64 > ret)
+ ret = ret_zip64;
+ }
+ return (ret);
+ }
+ i -= 4;
+ break;
+ case 'K': i -= 1; break;
+ case 005: i -= 2; break;
+ case 006: i -= 3; break;
+ default: i -= 4; break;
+ }
+ }
+ return 0;
+}
+
+/* The red-black trees are only used in seeking mode to manage
+ * the in-memory copy of the central directory. */
+
+static int
+cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2)
+{
+ const struct zip_entry *e1 = (const struct zip_entry *)n1;
+ const struct zip_entry *e2 = (const struct zip_entry *)n2;
+
+ if (e1->local_header_offset > e2->local_header_offset)
+ return -1;
+ if (e1->local_header_offset < e2->local_header_offset)
+ return 1;
+ return 0;
+}
+
+static int
+cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ /* This function won't be called */
+ (void)n; /* UNUSED */
+ (void)key; /* UNUSED */
+ return 1;
+}
+
+static const struct archive_rb_tree_ops rb_ops = {
+ &cmp_node, &cmp_key
+};
+
+static int
+rsrc_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct zip_entry *e1 = (const struct zip_entry *)n1;
+ const struct zip_entry *e2 = (const struct zip_entry *)n2;
+
+ return (strcmp(e2->rsrcname.s, e1->rsrcname.s));
+}
+
+static int
+rsrc_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct zip_entry *e = (const struct zip_entry *)n;
+ return (strcmp((const char *)key, e->rsrcname.s));
+}
+
+static const struct archive_rb_tree_ops rb_rsrc_ops = {
+ &rsrc_cmp_node, &rsrc_cmp_key
+};
+
+static const char *
+rsrc_basename(const char *name, size_t name_length)
+{
+ const char *s, *r;
+
+ r = s = name;
+ for (;;) {
+ s = memchr(s, '/', name_length - (s - name));
+ if (s == NULL)
+ break;
+ r = ++s;
+ }
+ return (r);
+}
+
+static void
+expose_parent_dirs(struct zip *zip, const char *name, size_t name_length)
+{
+ struct archive_string str;
+ struct zip_entry *dir;
+ char *s;
+
+ archive_string_init(&str);
+ archive_strncpy(&str, name, name_length);
+ for (;;) {
+ s = strrchr(str.s, '/');
+ if (s == NULL)
+ break;
+ *s = '\0';
+ /* Transfer the parent directory from zip->tree_rsrc RB
+ * tree to zip->tree RB tree to expose. */
+ dir = (struct zip_entry *)
+ __archive_rb_tree_find_node(&zip->tree_rsrc, str.s);
+ if (dir == NULL)
+ break;
+ __archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node);
+ archive_string_free(&dir->rsrcname);
+ __archive_rb_tree_insert_node(&zip->tree, &dir->node);
+ }
+ archive_string_free(&str);
+}
+
+static int
+slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
+ struct zip *zip)
+{
+ ssize_t i;
+ unsigned found;
+ int64_t correction;
+ ssize_t bytes_avail;
+ const char *p;
+
+ /*
+ * Find the start of the central directory. The end-of-CD
+ * record has our starting point, but there are lots of
+ * Zip archives which have had other data prepended to the
+ * file, which makes the recorded offsets all too small.
+ * So we search forward from the specified offset until we
+ * find the real start of the central directory. Then we
+ * know the correction we need to apply to account for leading
+ * padding.
+ */
+ if (__archive_read_seek(a, zip->central_directory_offset_adjusted, SEEK_SET)
+ < 0)
+ return ARCHIVE_FATAL;
+
+ found = 0;
+ while (!found) {
+ if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL)
+ return ARCHIVE_FATAL;
+ for (found = 0, i = 0; !found && i < bytes_avail - 4;) {
+ switch (p[i + 3]) {
+ case 'P': i += 3; break;
+ case 'K': i += 2; break;
+ case 001: i += 1; break;
+ case 002:
+ if (memcmp(p + i, "PK\001\002", 4) == 0) {
+ p += i;
+ found = 1;
+ } else
+ i += 4;
+ break;
+ case 005: i += 1; break;
+ case 006:
+ if (memcmp(p + i, "PK\005\006", 4) == 0) {
+ p += i;
+ found = 1;
+ } else if (memcmp(p + i, "PK\006\006", 4) == 0) {
+ p += i;
+ found = 1;
+ } else
+ i += 1;
+ break;
+ default: i += 4; break;
+ }
+ }
+ __archive_read_consume(a, i);
+ }
+ correction = archive_filter_bytes(&a->archive, 0)
+ - zip->central_directory_offset;
+
+ __archive_rb_tree_init(&zip->tree, &rb_ops);
+ __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops);
+
+ zip->central_directory_entries_total = 0;
+ while (1) {
+ struct zip_entry *zip_entry;
+ size_t filename_length, extra_length, comment_length;
+ uint32_t external_attributes;
+ const char *name, *r;
+
+ if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
+ return ARCHIVE_FATAL;
+ if (memcmp(p, "PK\006\006", 4) == 0
+ || memcmp(p, "PK\005\006", 4) == 0) {
+ break;
+ } else if (memcmp(p, "PK\001\002", 4) != 0) {
+ archive_set_error(&a->archive,
+ -1, "Invalid central directory signature");
+ return ARCHIVE_FATAL;
+ }
+ if ((p = __archive_read_ahead(a, 46, NULL)) == NULL)
+ return ARCHIVE_FATAL;
+
+ zip_entry = calloc(1, sizeof(struct zip_entry));
+ if (zip_entry == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip entry");
+ return ARCHIVE_FATAL;
+ }
+ zip_entry->next = zip->zip_entries;
+ zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY;
+ zip->zip_entries = zip_entry;
+ zip->central_directory_entries_total++;
+
+ /* version = p[4]; */
+ zip_entry->system = p[5];
+ /* version_required = archive_le16dec(p + 6); */
+ zip_entry->zip_flags = archive_le16dec(p + 8);
+ if (zip_entry->zip_flags
+ & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){
+ zip->has_encrypted_entries = 1;
+ }
+ zip_entry->compression = (char)archive_le16dec(p + 10);
+ zip_entry->mtime = zip_time(p + 12);
+ zip_entry->crc32 = archive_le32dec(p + 16);
+ if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+ zip_entry->decdat = p[13];
+ else
+ zip_entry->decdat = p[19];
+ zip_entry->compressed_size = archive_le32dec(p + 20);
+ zip_entry->uncompressed_size = archive_le32dec(p + 24);
+ filename_length = archive_le16dec(p + 28);
+ extra_length = archive_le16dec(p + 30);
+ comment_length = archive_le16dec(p + 32);
+ /* disk_start = archive_le16dec(p + 34);
+ * Better be zero.
+ * internal_attributes = archive_le16dec(p + 36);
+ * text bit */
+ external_attributes = archive_le32dec(p + 38);
+ zip_entry->local_header_offset =
+ archive_le32dec(p + 42) + correction;
+
+ /* If we can't guess the mode, leave it zero here;
+ when we read the local file header we might get
+ more information. */
+ if (zip_entry->system == 3) {
+ zip_entry->mode = external_attributes >> 16;
+ } else if (zip_entry->system == 0) {
+ // Interpret MSDOS directory bit
+ if (0x10 == (external_attributes & 0x10)) {
+ zip_entry->mode = AE_IFDIR | 0775;
+ } else {
+ zip_entry->mode = AE_IFREG | 0664;
+ }
+ if (0x01 == (external_attributes & 0x01)) {
+ // Read-only bit; strip write permissions
+ zip_entry->mode &= 0555;
+ }
+ } else {
+ zip_entry->mode = 0;
+ }
+
+ /* We're done with the regular data; get the filename and
+ * extra data. */
+ __archive_read_consume(a, 46);
+ p = __archive_read_ahead(a, filename_length + extra_length,
+ NULL);
+ if (p == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return ARCHIVE_FATAL;
+ }
+ if (ARCHIVE_OK != process_extra(a, entry, p + filename_length,
+ extra_length, zip_entry)) {
+ return ARCHIVE_FATAL;
+ }
+
+ /*
+ * Mac resource fork files are stored under the
+ * "__MACOSX/" directory, so we should check if
+ * it is.
+ */
+ if (!zip->process_mac_extensions) {
+ /* Treat every entry as a regular entry. */
+ __archive_rb_tree_insert_node(&zip->tree,
+ &zip_entry->node);
+ } else {
+ name = p;
+ r = rsrc_basename(name, filename_length);
+ if (filename_length >= 9 &&
+ strncmp("__MACOSX/", name, 9) == 0) {
+ /* If this file is not a resource fork nor
+ * a directory. We should treat it as a non
+ * resource fork file to expose it. */
+ if (name[filename_length-1] != '/' &&
+ (r - name < 3 || r[0] != '.' ||
+ r[1] != '_')) {
+ __archive_rb_tree_insert_node(
+ &zip->tree, &zip_entry->node);
+ /* Expose its parent directories. */
+ expose_parent_dirs(zip, name,
+ filename_length);
+ } else {
+ /* This file is a resource fork file or
+ * a directory. */
+ archive_strncpy(&(zip_entry->rsrcname),
+ name, filename_length);
+ __archive_rb_tree_insert_node(
+ &zip->tree_rsrc, &zip_entry->node);
+ }
+ } else {
+ /* Generate resource fork name to find its
+ * resource file at zip->tree_rsrc. */
+ archive_strcpy(&(zip_entry->rsrcname),
+ "__MACOSX/");
+ archive_strncat(&(zip_entry->rsrcname),
+ name, r - name);
+ archive_strcat(&(zip_entry->rsrcname), "._");
+ archive_strncat(&(zip_entry->rsrcname),
+ name + (r - name),
+ filename_length - (r - name));
+ /* Register an entry to RB tree to sort it by
+ * file offset. */
+ __archive_rb_tree_insert_node(&zip->tree,
+ &zip_entry->node);
+ }
+ }
+
+ /* Skip the comment too ... */
+ __archive_read_consume(a,
+ filename_length + extra_length + comment_length);
+ }
+
+ return ARCHIVE_OK;
+}
+
+static ssize_t
+zip_get_local_file_header_size(struct archive_read *a, size_t extra)
+{
+ const char *p;
+ ssize_t filename_length, extra_length;
+
+ if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ return (ARCHIVE_WARN);
+ }
+ p += extra;
+
+ if (memcmp(p, "PK\003\004", 4) != 0) {
+ archive_set_error(&a->archive, -1, "Damaged Zip archive");
+ return ARCHIVE_WARN;
+ }
+ filename_length = archive_le16dec(p + 26);
+ extra_length = archive_le16dec(p + 28);
+
+ return (30 + filename_length + extra_length);
+}
+
+static int
+zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,
+ struct zip_entry *rsrc)
+{
+ struct zip *zip = (struct zip *)a->format->data;
+ unsigned char *metadata, *mp;
+ int64_t offset = archive_filter_bytes(&a->archive, 0);
+ size_t remaining_bytes, metadata_bytes;
+ ssize_t hsize;
+ int ret = ARCHIVE_OK, eof;
+
+ switch(rsrc->compression) {
+ case 0: /* No compression. */
+ if (rsrc->uncompressed_size != rsrc->compressed_size) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Malformed OS X metadata entry: "
+ "inconsistent size");
+ return (ARCHIVE_FATAL);
+ }
+#ifdef HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+#endif
+ break;
+ default: /* Unsupported compression. */
+ /* Return a warning. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Unsupported ZIP compression method (%s)",
+ compression_name(rsrc->compression));
+ /* We can't decompress this entry, but we will
+ * be able to skip() it and try the next entry. */
+ return (ARCHIVE_WARN);
+ }
+
+ if (rsrc->uncompressed_size > (4 * 1024 * 1024)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Mac metadata is too large: %jd > 4M bytes",
+ (intmax_t)rsrc->uncompressed_size);
+ return (ARCHIVE_WARN);
+ }
+ if (rsrc->compressed_size > (4 * 1024 * 1024)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Mac metadata is too large: %jd > 4M bytes",
+ (intmax_t)rsrc->compressed_size);
+ return (ARCHIVE_WARN);
+ }
+
+ metadata = malloc((size_t)rsrc->uncompressed_size);
+ if (metadata == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Mac metadata");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (offset < rsrc->local_header_offset)
+ __archive_read_consume(a, rsrc->local_header_offset - offset);
+ else if (offset != rsrc->local_header_offset) {
+ __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET);
+ }
+
+ hsize = zip_get_local_file_header_size(a, 0);
+ __archive_read_consume(a, hsize);
+
+ remaining_bytes = (size_t)rsrc->compressed_size;
+ metadata_bytes = (size_t)rsrc->uncompressed_size;
+ mp = metadata;
+ eof = 0;
+ while (!eof && remaining_bytes) {
+ const unsigned char *p;
+ ssize_t bytes_avail;
+ size_t bytes_used;
+
+ p = __archive_read_ahead(a, 1, &bytes_avail);
+ if (p == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated ZIP file header");
+ ret = ARCHIVE_WARN;
+ goto exit_mac_metadata;
+ }
+ if ((size_t)bytes_avail > remaining_bytes)
+ bytes_avail = remaining_bytes;
+ switch(rsrc->compression) {
+ case 0: /* No compression. */
+ if ((size_t)bytes_avail > metadata_bytes)
+ bytes_avail = metadata_bytes;
+ memcpy(mp, p, bytes_avail);
+ bytes_used = (size_t)bytes_avail;
+ metadata_bytes -= bytes_used;
+ mp += bytes_used;
+ if (metadata_bytes == 0)
+ eof = 1;
+ break;
+#ifdef HAVE_ZLIB_H
+ case 8: /* Deflate compression. */
+ {
+ int r;
+
+ ret = zip_deflate_init(a, zip);
+ if (ret != ARCHIVE_OK)
+ goto exit_mac_metadata;
+ zip->stream.next_in =
+ (Bytef *)(uintptr_t)(const void *)p;
+ zip->stream.avail_in = (uInt)bytes_avail;
+ zip->stream.total_in = 0;
+ zip->stream.next_out = mp;
+ zip->stream.avail_out = (uInt)metadata_bytes;
+ zip->stream.total_out = 0;
+
+ r = inflate(&zip->stream, 0);
+ switch (r) {
+ case Z_OK:
+ break;
+ case Z_STREAM_END:
+ eof = 1;
+ break;
+ case Z_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Out of memory for ZIP decompression");
+ ret = ARCHIVE_FATAL;
+ goto exit_mac_metadata;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "ZIP decompression failed (%d)", r);
+ ret = ARCHIVE_FATAL;
+ goto exit_mac_metadata;
+ }
+ bytes_used = zip->stream.total_in;
+ metadata_bytes -= zip->stream.total_out;
+ mp += zip->stream.total_out;
+ break;
+ }
+#endif
+ default:
+ bytes_used = 0;
+ break;
+ }
+ __archive_read_consume(a, bytes_used);
+ remaining_bytes -= bytes_used;
+ }
+ archive_entry_copy_mac_metadata(entry, metadata,
+ (size_t)rsrc->uncompressed_size - metadata_bytes);
+
+exit_mac_metadata:
+ __archive_read_seek(a, offset, SEEK_SET);
+ zip->decompress_init = 0;
+ free(metadata);
+ return (ret);
+}
+
+static int
+archive_read_format_zip_seekable_read_header(struct archive_read *a,
+ struct archive_entry *entry)
+{
+ struct zip *zip = (struct zip *)a->format->data;
+ struct zip_entry *rsrc;
+ int64_t offset;
+ int r, ret = ARCHIVE_OK;
+
+ /*
+ * It should be sufficient to call archive_read_next_header() for
+ * a reader to determine if an entry is encrypted or not. If the
+ * encryption of an entry is only detectable when calling
+ * archive_read_data(), so be it. We'll do the same check there
+ * as well.
+ */
+ if (zip->has_encrypted_entries ==
+ ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)
+ zip->has_encrypted_entries = 0;
+
+ a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+ if (a->archive.archive_format_name == NULL)
+ a->archive.archive_format_name = "ZIP";
+
+ if (zip->zip_entries == NULL) {
+ r = slurp_central_directory(a, entry, zip);
+ if (r != ARCHIVE_OK)
+ return r;
+ /* Get first entry whose local header offset is lower than
+ * other entries in the archive file. */
+ zip->entry =
+ (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree);
+ } else if (zip->entry != NULL) {
+ /* Get next entry in local header offset order. */
+ zip->entry = (struct zip_entry *)__archive_rb_tree_iterate(
+ &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT);
+ }
+
+ if (zip->entry == NULL)
+ return ARCHIVE_EOF;
+
+ if (zip->entry->rsrcname.s)
+ rsrc = (struct zip_entry *)__archive_rb_tree_find_node(
+ &zip->tree_rsrc, zip->entry->rsrcname.s);
+ else
+ rsrc = NULL;
+
+ if (zip->cctx_valid)
+ archive_decrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+ __archive_read_reset_passphrase(a);
+
+ /* File entries are sorted by the header offset, we should mostly
+ * use __archive_read_consume to advance a read point to avoid
+ * redundant data reading. */
+ offset = archive_filter_bytes(&a->archive, 0);
+ if (offset < zip->entry->local_header_offset)
+ __archive_read_consume(a,
+ zip->entry->local_header_offset - offset);
+ else if (offset != zip->entry->local_header_offset) {
+ __archive_read_seek(a, zip->entry->local_header_offset,
+ SEEK_SET);
+ }
+ zip->unconsumed = 0;
+ r = zip_read_local_file_header(a, entry, zip);
+ if (r != ARCHIVE_OK)
+ return r;
+ if (rsrc) {
+ int ret2 = zip_read_mac_metadata(a, entry, rsrc);
+ if (ret2 < ret)
+ ret = ret2;
+ }
+ return (ret);
+}
+
+/*
+ * We're going to seek for the next header anyway, so we don't
+ * need to bother doing anything here.
+ */
+static int
+archive_read_format_zip_read_data_skip_seekable(struct archive_read *a)
+{
+ struct zip *zip;
+ zip = (struct zip *)(a->format->data);
+
+ zip->unconsumed = 0;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_read_support_format_zip_seekable(struct archive *_a)
+{
+ struct archive_read *a = (struct archive_read *)_a;
+ struct zip *zip;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable");
+
+ zip = (struct zip *)calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip data");
+ return (ARCHIVE_FATAL);
+ }
+
+#ifdef HAVE_COPYFILE_H
+ /* Set this by default on Mac OS. */
+ zip->process_mac_extensions = 1;
+#endif
+
+ /*
+ * Until enough data has been read, we cannot tell about
+ * any encrypted entries yet.
+ */
+ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+ zip->crc32func = real_crc32;
+
+ r = __archive_read_register_format(a,
+ zip,
+ "zip",
+ archive_read_format_zip_seekable_bid,
+ archive_read_format_zip_options,
+ archive_read_format_zip_seekable_read_header,
+ archive_read_format_zip_read_data,
+ archive_read_format_zip_read_data_skip_seekable,
+ NULL,
+ archive_read_format_zip_cleanup,
+ archive_read_support_format_zip_capabilities_seekable,
+ archive_read_format_zip_has_encrypted_entries);
+
+ if (r != ARCHIVE_OK)
+ free(zip);
+ return (ARCHIVE_OK);
+}
+
+/*# vim:set noet:*/
diff --git a/contrib/libs/libarchive/libarchive/archive_string.c b/contrib/libs/libarchive/libarchive/archive_string.c
new file mode 100644
index 0000000000..c89b315399
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_string.c
@@ -0,0 +1,4244 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33:22Z kientzle $");
+
+/*
+ * Basic resizable string support, to simplify manipulating arbitrary-sized
+ * strings while minimizing heap activity.
+ *
+ * In particular, the buffer used by a string object is only grown, it
+ * never shrinks, so you can clear and reuse the same string object
+ * without incurring additional memory allocations.
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_ICONV_H
+#include <iconv.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_LOCALCHARSET_H
+#error #include <localcharset.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#include <locale.h>
+#endif
+
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_string_composition.h"
+
+#if !defined(HAVE_WMEMCPY) && !defined(wmemcpy)
+#define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+#if !defined(HAVE_WMEMMOVE) && !defined(wmemmove)
+#define wmemmove(a,b,i) (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+#undef max
+#define max(a, b) ((a)>(b)?(a):(b))
+
+struct archive_string_conv {
+ struct archive_string_conv *next;
+ char *from_charset;
+ char *to_charset;
+ unsigned from_cp;
+ unsigned to_cp;
+ /* Set 1 if from_charset and to_charset are the same. */
+ int same;
+ int flag;
+#define SCONV_TO_CHARSET 1 /* MBS is being converted to specified
+ * charset. */
+#define SCONV_FROM_CHARSET (1<<1) /* MBS is being converted from
+ * specified charset. */
+#define SCONV_BEST_EFFORT (1<<2) /* Copy at least ASCII code. */
+#define SCONV_WIN_CP (1<<3) /* Use Windows API for converting
+ * MBS. */
+#define SCONV_UTF8_LIBARCHIVE_2 (1<<4) /* Incorrect UTF-8 made by libarchive
+ * 2.x in the wrong assumption. */
+#define SCONV_NORMALIZATION_C (1<<6) /* Need normalization to be Form C.
+ * Before UTF-8 characters are actually
+ * processed. */
+#define SCONV_NORMALIZATION_D (1<<7) /* Need normalization to be Form D.
+ * Before UTF-8 characters are actually
+ * processed.
+ * Currently this only for MAC OS X. */
+#define SCONV_TO_UTF8 (1<<8) /* "to charset" side is UTF-8. */
+#define SCONV_FROM_UTF8 (1<<9) /* "from charset" side is UTF-8. */
+#define SCONV_TO_UTF16BE (1<<10) /* "to charset" side is UTF-16BE. */
+#define SCONV_FROM_UTF16BE (1<<11) /* "from charset" side is UTF-16BE. */
+#define SCONV_TO_UTF16LE (1<<12) /* "to charset" side is UTF-16LE. */
+#define SCONV_FROM_UTF16LE (1<<13) /* "from charset" side is UTF-16LE. */
+#define SCONV_TO_UTF16 (SCONV_TO_UTF16BE | SCONV_TO_UTF16LE)
+#define SCONV_FROM_UTF16 (SCONV_FROM_UTF16BE | SCONV_FROM_UTF16LE)
+
+#if HAVE_ICONV
+ iconv_t cd;
+ iconv_t cd_w;/* Use at archive_mstring on
+ * Windows. */
+#endif
+ /* A temporary buffer for normalization. */
+ struct archive_string utftmp;
+ int (*converter[2])(struct archive_string *, const void *, size_t,
+ struct archive_string_conv *);
+ int nconverter;
+};
+
+#define CP_C_LOCALE 0 /* "C" locale only for this file. */
+#define CP_UTF16LE 1200
+#define CP_UTF16BE 1201
+
+#define IS_HIGH_SURROGATE_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDBFF)
+#define IS_LOW_SURROGATE_LA(uc) ((uc) >= 0xDC00 && (uc) <= 0xDFFF)
+#define IS_SURROGATE_PAIR_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDFFF)
+#define UNICODE_MAX 0x10FFFF
+#define UNICODE_R_CHAR 0xFFFD /* Replacement character. */
+/* Set U+FFFD(Replacement character) in UTF-8. */
+static const char utf8_replacement_char[] = {0xef, 0xbf, 0xbd};
+
+static struct archive_string_conv *find_sconv_object(struct archive *,
+ const char *, const char *);
+static void add_sconv_object(struct archive *, struct archive_string_conv *);
+static struct archive_string_conv *create_sconv_object(const char *,
+ const char *, unsigned, int);
+static void free_sconv_object(struct archive_string_conv *);
+static struct archive_string_conv *get_sconv_object(struct archive *,
+ const char *, const char *, int);
+static unsigned make_codepage_from_charset(const char *);
+static unsigned get_current_codepage(void);
+static unsigned get_current_oemcp(void);
+static size_t mbsnbytes(const void *, size_t);
+static size_t utf16nbytes(const void *, size_t);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+static int archive_wstring_append_from_mbs_in_codepage(
+ struct archive_wstring *, const char *, size_t,
+ struct archive_string_conv *);
+static int archive_string_append_from_wcs_in_codepage(struct archive_string *,
+ const wchar_t *, size_t, struct archive_string_conv *);
+static int is_big_endian(void);
+static int strncat_in_codepage(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int win_strncat_from_utf16be(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int win_strncat_from_utf16le(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int win_strncat_to_utf16be(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int win_strncat_to_utf16le(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+#endif
+static int best_effort_strncat_from_utf16be(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_from_utf16le(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_to_utf16be(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_to_utf16le(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+#if defined(HAVE_ICONV)
+static int iconv_strncat_in_locale(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+#endif
+static int best_effort_strncat_in_locale(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int _utf8_to_unicode(uint32_t *, const char *, size_t);
+static int utf8_to_unicode(uint32_t *, const char *, size_t);
+static inline uint32_t combine_surrogate_pair(uint32_t, uint32_t);
+static int cesu8_to_unicode(uint32_t *, const char *, size_t);
+static size_t unicode_to_utf8(char *, size_t, uint32_t);
+static int utf16_to_unicode(uint32_t *, const char *, size_t, int);
+static size_t unicode_to_utf16be(char *, size_t, uint32_t);
+static size_t unicode_to_utf16le(char *, size_t, uint32_t);
+static int strncat_from_utf8_libarchive2(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+static int strncat_from_utf8_to_utf8(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int archive_string_normalize_C(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int archive_string_normalize_D(struct archive_string *, const void *,
+ size_t, struct archive_string_conv *);
+static int archive_string_append_unicode(struct archive_string *,
+ const void *, size_t, struct archive_string_conv *);
+
+static struct archive_string *
+archive_string_append(struct archive_string *as, const char *p, size_t s)
+{
+ if (archive_string_ensure(as, as->length + s + 1) == NULL)
+ return (NULL);
+ if (s)
+ memmove(as->s + as->length, p, s);
+ as->length += s;
+ as->s[as->length] = 0;
+ return (as);
+}
+
+static struct archive_wstring *
+archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s)
+{
+ if (archive_wstring_ensure(as, as->length + s + 1) == NULL)
+ return (NULL);
+ if (s)
+ wmemmove(as->s + as->length, p, s);
+ as->length += s;
+ as->s[as->length] = 0;
+ return (as);
+}
+
+struct archive_string *
+archive_array_append(struct archive_string *as, const char *p, size_t s)
+{
+ return archive_string_append(as, p, s);
+}
+
+void
+archive_string_concat(struct archive_string *dest, struct archive_string *src)
+{
+ if (archive_string_append(dest, src->s, src->length) == NULL)
+ __archive_errx(1, "Out of memory");
+}
+
+void
+archive_wstring_concat(struct archive_wstring *dest,
+ struct archive_wstring *src)
+{
+ if (archive_wstring_append(dest, src->s, src->length) == NULL)
+ __archive_errx(1, "Out of memory");
+}
+
+void
+archive_string_free(struct archive_string *as)
+{
+ as->length = 0;
+ as->buffer_length = 0;
+ free(as->s);
+ as->s = NULL;
+}
+
+void
+archive_wstring_free(struct archive_wstring *as)
+{
+ as->length = 0;
+ as->buffer_length = 0;
+ free(as->s);
+ as->s = NULL;
+}
+
+struct archive_wstring *
+archive_wstring_ensure(struct archive_wstring *as, size_t s)
+{
+ return (struct archive_wstring *)
+ archive_string_ensure((struct archive_string *)as,
+ s * sizeof(wchar_t));
+}
+
+/* Returns NULL on any allocation failure. */
+struct archive_string *
+archive_string_ensure(struct archive_string *as, size_t s)
+{
+ char *p;
+ size_t new_length;
+
+ /* If buffer is already big enough, don't reallocate. */
+ if (as->s && (s <= as->buffer_length))
+ return (as);
+
+ /*
+ * Growing the buffer at least exponentially ensures that
+ * append operations are always linear in the number of
+ * characters appended. Using a smaller growth rate for
+ * larger buffers reduces memory waste somewhat at the cost of
+ * a larger constant factor.
+ */
+ if (as->buffer_length < 32)
+ /* Start with a minimum 32-character buffer. */
+ new_length = 32;
+ else if (as->buffer_length < 8192)
+ /* Buffers under 8k are doubled for speed. */
+ new_length = as->buffer_length + as->buffer_length;
+ else {
+ /* Buffers 8k and over grow by at least 25% each time. */
+ new_length = as->buffer_length + as->buffer_length / 4;
+ /* Be safe: If size wraps, fail. */
+ if (new_length < as->buffer_length) {
+ /* On failure, wipe the string and return NULL. */
+ archive_string_free(as);
+ errno = ENOMEM;/* Make sure errno has ENOMEM. */
+ return (NULL);
+ }
+ }
+ /*
+ * The computation above is a lower limit to how much we'll
+ * grow the buffer. In any case, we have to grow it enough to
+ * hold the request.
+ */
+ if (new_length < s)
+ new_length = s;
+ /* Now we can reallocate the buffer. */
+ p = (char *)realloc(as->s, new_length);
+ if (p == NULL) {
+ /* On failure, wipe the string and return NULL. */
+ archive_string_free(as);
+ errno = ENOMEM;/* Make sure errno has ENOMEM. */
+ return (NULL);
+ }
+
+ as->s = p;
+ as->buffer_length = new_length;
+ return (as);
+}
+
+/*
+ * TODO: See if there's a way to avoid scanning
+ * the source string twice. Then test to see
+ * if it actually helps (remember that we're almost
+ * always called with pretty short arguments, so
+ * such an optimization might not help).
+ */
+struct archive_string *
+archive_strncat(struct archive_string *as, const void *_p, size_t n)
+{
+ size_t s;
+ const char *p, *pp;
+
+ p = (const char *)_p;
+
+ /* Like strlen(p), except won't examine positions beyond p[n]. */
+ s = 0;
+ pp = p;
+ while (s < n && *pp) {
+ pp++;
+ s++;
+ }
+ if ((as = archive_string_append(as, p, s)) == NULL)
+ __archive_errx(1, "Out of memory");
+ return (as);
+}
+
+struct archive_wstring *
+archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n)
+{
+ size_t s;
+ const wchar_t *pp;
+
+ /* Like strlen(p), except won't examine positions beyond p[n]. */
+ s = 0;
+ pp = p;
+ while (s < n && *pp) {
+ pp++;
+ s++;
+ }
+ if ((as = archive_wstring_append(as, p, s)) == NULL)
+ __archive_errx(1, "Out of memory");
+ return (as);
+}
+
+struct archive_string *
+archive_strcat(struct archive_string *as, const void *p)
+{
+ /* strcat is just strncat without an effective limit.
+ * Assert that we'll never get called with a source
+ * string over 16MB.
+ * TODO: Review all uses of strcat in the source
+ * and try to replace them with strncat().
+ */
+ return archive_strncat(as, p, 0x1000000);
+}
+
+struct archive_wstring *
+archive_wstrcat(struct archive_wstring *as, const wchar_t *p)
+{
+ /* Ditto. */
+ return archive_wstrncat(as, p, 0x1000000);
+}
+
+struct archive_string *
+archive_strappend_char(struct archive_string *as, char c)
+{
+ if ((as = archive_string_append(as, &c, 1)) == NULL)
+ __archive_errx(1, "Out of memory");
+ return (as);
+}
+
+struct archive_wstring *
+archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c)
+{
+ if ((as = archive_wstring_append(as, &c, 1)) == NULL)
+ __archive_errx(1, "Out of memory");
+ return (as);
+}
+
+/*
+ * Get the "current character set" name to use with iconv.
+ * On FreeBSD, the empty character set name "" chooses
+ * the correct character encoding for the current locale,
+ * so this isn't necessary.
+ * But iconv on Mac OS 10.6 doesn't seem to handle this correctly;
+ * on that system, we have to explicitly call nl_langinfo()
+ * to get the right name. Not sure about other platforms.
+ *
+ * NOTE: GNU libiconv does not recognize the character-set name
+ * which some platform nl_langinfo(CODESET) returns, so we should
+ * use locale_charset() instead of nl_langinfo(CODESET) for GNU libiconv.
+ */
+static const char *
+default_iconv_charset(const char *charset) {
+ if (charset != NULL && charset[0] != '\0')
+ return charset;
+#if HAVE_LOCALE_CHARSET && !defined(__APPLE__)
+ /* locale_charset() is broken on Mac OS */
+ return locale_charset();
+#elif HAVE_NL_LANGINFO
+ return nl_langinfo(CODESET);
+#else
+ return "";
+#endif
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
+ */
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+ const char *p, size_t len)
+{
+ return archive_wstring_append_from_mbs_in_codepage(dest, p, len, NULL);
+}
+
+static int
+archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest,
+ const char *s, size_t length, struct archive_string_conv *sc)
+{
+ int count, ret = 0;
+ UINT from_cp;
+
+ if (sc != NULL)
+ from_cp = sc->from_cp;
+ else
+ from_cp = get_current_codepage();
+
+ if (from_cp == CP_C_LOCALE) {
+ /*
+ * "C" locale special processing.
+ */
+ wchar_t *ws;
+ const unsigned char *mp;
+
+ if (NULL == archive_wstring_ensure(dest,
+ dest->length + length + 1))
+ return (-1);
+
+ ws = dest->s + dest->length;
+ mp = (const unsigned char *)s;
+ count = 0;
+ while (count < (int)length && *mp) {
+ *ws++ = (wchar_t)*mp++;
+ count++;
+ }
+ } else if (sc != NULL &&
+ (sc->flag & (SCONV_NORMALIZATION_C | SCONV_NORMALIZATION_D))) {
+ /*
+ * Normalize UTF-8 and UTF-16BE and convert it directly
+ * to UTF-16 as wchar_t.
+ */
+ struct archive_string u16;
+ int saved_flag = sc->flag;/* save current flag. */
+
+ if (is_big_endian())
+ sc->flag |= SCONV_TO_UTF16BE;
+ else
+ sc->flag |= SCONV_TO_UTF16LE;
+
+ if (sc->flag & SCONV_FROM_UTF16) {
+ /*
+ * UTF-16BE/LE NFD ===> UTF-16 NFC
+ * UTF-16BE/LE NFC ===> UTF-16 NFD
+ */
+ count = (int)utf16nbytes(s, length);
+ } else {
+ /*
+ * UTF-8 NFD ===> UTF-16 NFC
+ * UTF-8 NFC ===> UTF-16 NFD
+ */
+ count = (int)mbsnbytes(s, length);
+ }
+ u16.s = (char *)dest->s;
+ u16.length = dest->length << 1;;
+ u16.buffer_length = dest->buffer_length;
+ if (sc->flag & SCONV_NORMALIZATION_C)
+ ret = archive_string_normalize_C(&u16, s, count, sc);
+ else
+ ret = archive_string_normalize_D(&u16, s, count, sc);
+ dest->s = (wchar_t *)u16.s;
+ dest->length = u16.length >> 1;
+ dest->buffer_length = u16.buffer_length;
+ sc->flag = saved_flag;/* restore the saved flag. */
+ return (ret);
+ } else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) {
+ count = (int)utf16nbytes(s, length);
+ count >>= 1; /* to be WCS length */
+ /* Allocate memory for WCS. */
+ if (NULL == archive_wstring_ensure(dest,
+ dest->length + count + 1))
+ return (-1);
+ wmemcpy(dest->s + dest->length, (const wchar_t *)s, count);
+ if ((sc->flag & SCONV_FROM_UTF16BE) && !is_big_endian()) {
+ uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
+ int b;
+ for (b = 0; b < count; b++) {
+ uint16_t val = archive_le16dec(u16+b);
+ archive_be16enc(u16+b, val);
+ }
+ } else if ((sc->flag & SCONV_FROM_UTF16LE) && is_big_endian()) {
+ uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
+ int b;
+ for (b = 0; b < count; b++) {
+ uint16_t val = archive_be16dec(u16+b);
+ archive_le16enc(u16+b, val);
+ }
+ }
+ } else {
+ DWORD mbflag;
+ size_t buffsize;
+
+ if (sc == NULL)
+ mbflag = 0;
+ else if (sc->flag & SCONV_FROM_CHARSET) {
+ /* Do not trust the length which comes from
+ * an archive file. */
+ length = mbsnbytes(s, length);
+ mbflag = 0;
+ } else
+ mbflag = MB_PRECOMPOSED;
+
+ buffsize = dest->length + length + 1;
+ do {
+ /* Allocate memory for WCS. */
+ if (NULL == archive_wstring_ensure(dest, buffsize))
+ return (-1);
+ /* Convert MBS to WCS. */
+ count = MultiByteToWideChar(from_cp,
+ mbflag, s, (int)length, dest->s + dest->length,
+ (int)(dest->buffer_length >> 1) -1);
+ if (count == 0 &&
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ /* Expand the WCS buffer. */
+ buffsize = dest->buffer_length << 1;
+ continue;
+ }
+ if (count == 0 && length != 0)
+ ret = -1;
+ break;
+ } while (1);
+ }
+ dest->length += count;
+ dest->s[dest->length] = L'\0';
+ return (ret);
+}
+
+#else
+
+/*
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
+ */
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+ const char *p, size_t len)
+{
+ size_t r;
+ int ret_val = 0;
+ /*
+ * No single byte will be more than one wide character,
+ * so this length estimate will always be big enough.
+ */
+ // size_t wcs_length = len;
+ size_t mbs_length = len;
+ const char *mbs = p;
+ wchar_t *wcs;
+#if HAVE_MBRTOWC
+ mbstate_t shift_state;
+
+ memset(&shift_state, 0, sizeof(shift_state));
+#endif
+ /*
+ * As we decided to have wcs_length == mbs_length == len
+ * we can use len here instead of wcs_length
+ */
+ if (NULL == archive_wstring_ensure(dest, dest->length + len + 1))
+ return (-1);
+ wcs = dest->s + dest->length;
+ /*
+ * We cannot use mbsrtowcs/mbstowcs here because those may convert
+ * extra MBS when strlen(p) > len and one wide character consists of
+ * multi bytes.
+ */
+ while (*mbs && mbs_length > 0) {
+ /*
+ * The buffer we allocated is always big enough.
+ * Keep this code path in a comment if we decide to choose
+ * smaller wcs_length in the future
+ */
+/*
+ if (wcs_length == 0) {
+ dest->length = wcs - dest->s;
+ dest->s[dest->length] = L'\0';
+ wcs_length = mbs_length;
+ if (NULL == archive_wstring_ensure(dest,
+ dest->length + wcs_length + 1))
+ return (-1);
+ wcs = dest->s + dest->length;
+ }
+*/
+#if HAVE_MBRTOWC
+ r = mbrtowc(wcs, mbs, mbs_length, &shift_state);
+#else
+ r = mbtowc(wcs, mbs, mbs_length);
+#endif
+ if (r == (size_t)-1 || r == (size_t)-2) {
+ ret_val = -1;
+ break;
+ }
+ if (r == 0 || r > mbs_length)
+ break;
+ wcs++;
+ // wcs_length--;
+ mbs += r;
+ mbs_length -= r;
+ }
+ dest->length = wcs - dest->s;
+ dest->s[dest->length] = L'\0';
+ return (ret_val);
+}
+
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * WCS ==> MBS.
+ * Note: returns -1 if conversion fails.
+ *
+ * Win32 builds use WideCharToMultiByte from the Windows API.
+ * (Maybe Cygwin should too? WideCharToMultiByte will know a
+ * lot more about local character encodings than the wcrtomb()
+ * wrapper is going to know.)
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+ const wchar_t *w, size_t len)
+{
+ return archive_string_append_from_wcs_in_codepage(as, w, len, NULL);
+}
+
+static int
+archive_string_append_from_wcs_in_codepage(struct archive_string *as,
+ const wchar_t *ws, size_t len, struct archive_string_conv *sc)
+{
+ BOOL defchar_used, *dp;
+ int count, ret = 0;
+ UINT to_cp;
+ int wslen = (int)len;
+
+ if (sc != NULL)
+ to_cp = sc->to_cp;
+ else
+ to_cp = get_current_codepage();
+
+ if (to_cp == CP_C_LOCALE) {
+ /*
+ * "C" locale special processing.
+ */
+ const wchar_t *wp = ws;
+ char *p;
+
+ if (NULL == archive_string_ensure(as,
+ as->length + wslen +1))
+ return (-1);
+ p = as->s + as->length;
+ count = 0;
+ defchar_used = 0;
+ while (count < wslen && *wp) {
+ if (*wp > 255) {
+ *p++ = '?';
+ wp++;
+ defchar_used = 1;
+ } else
+ *p++ = (char)*wp++;
+ count++;
+ }
+ } else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) {
+ uint16_t *u16;
+
+ if (NULL ==
+ archive_string_ensure(as, as->length + len * 2 + 2))
+ return (-1);
+ u16 = (uint16_t *)(as->s + as->length);
+ count = 0;
+ defchar_used = 0;
+ if (sc->flag & SCONV_TO_UTF16BE) {
+ while (count < (int)len && *ws) {
+ archive_be16enc(u16+count, *ws);
+ ws++;
+ count++;
+ }
+ } else {
+ while (count < (int)len && *ws) {
+ archive_le16enc(u16+count, *ws);
+ ws++;
+ count++;
+ }
+ }
+ count <<= 1; /* to be byte size */
+ } else {
+ /* Make sure the MBS buffer has plenty to set. */
+ if (NULL ==
+ archive_string_ensure(as, as->length + len * 2 + 1))
+ return (-1);
+ do {
+ defchar_used = 0;
+ if (to_cp == CP_UTF8 || sc == NULL)
+ dp = NULL;
+ else
+ dp = &defchar_used;
+ count = WideCharToMultiByte(to_cp, 0, ws, wslen,
+ as->s + as->length,
+ (int)as->buffer_length - (int)as->length - 1, NULL, dp);
+ if (count == 0 &&
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ /* Expand the MBS buffer and retry. */
+ if (NULL == archive_string_ensure(as,
+ as->buffer_length + len))
+ return (-1);
+ continue;
+ }
+ if (count == 0)
+ ret = -1;
+ break;
+ } while (1);
+ }
+ as->length += count;
+ as->s[as->length] = '\0';
+ return (defchar_used?-1:ret);
+}
+
+#elif defined(HAVE_WCTOMB) || defined(HAVE_WCRTOMB)
+
+/*
+ * Translates a wide character string into current locale character set
+ * and appends to the archive_string. Note: returns -1 if conversion
+ * fails.
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+ const wchar_t *w, size_t len)
+{
+ /* We cannot use the standard wcstombs() here because it
+ * cannot tell us how big the output buffer should be. So
+ * I've built a loop around wcrtomb() or wctomb() that
+ * converts a character at a time and resizes the string as
+ * needed. We prefer wcrtomb() when it's available because
+ * it's thread-safe. */
+ int n, ret_val = 0;
+ char *p;
+ char *end;
+#if HAVE_WCRTOMB
+ mbstate_t shift_state;
+
+ memset(&shift_state, 0, sizeof(shift_state));
+#else
+ /* Clear the shift state before starting. */
+ wctomb(NULL, L'\0');
+#endif
+ /*
+ * Allocate buffer for MBS.
+ * We need this allocation here since it is possible that
+ * as->s is still NULL.
+ */
+ if (archive_string_ensure(as, as->length + len + 1) == NULL)
+ return (-1);
+
+ p = as->s + as->length;
+ end = as->s + as->buffer_length - MB_CUR_MAX -1;
+ while (*w != L'\0' && len > 0) {
+ if (p >= end) {
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ /* Re-allocate buffer for MBS. */
+ if (archive_string_ensure(as,
+ as->length + max(len * 2,
+ (size_t)MB_CUR_MAX) + 1) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ end = as->s + as->buffer_length - MB_CUR_MAX -1;
+ }
+#if HAVE_WCRTOMB
+ n = wcrtomb(p, *w++, &shift_state);
+#else
+ n = wctomb(p, *w++);
+#endif
+ if (n == -1) {
+ if (errno == EILSEQ) {
+ /* Skip an illegal wide char. */
+ *p++ = '?';
+ ret_val = -1;
+ } else {
+ ret_val = -1;
+ break;
+ }
+ } else
+ p += n;
+ len--;
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ return (ret_val);
+}
+
+#else /* HAVE_WCTOMB || HAVE_WCRTOMB */
+
+/*
+ * TODO: Test if __STDC_ISO_10646__ is defined.
+ * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion
+ * one character at a time. If a non-Windows platform doesn't have
+ * either of these, fall back to the built-in UTF8 conversion.
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+ const wchar_t *w, size_t len)
+{
+ (void)as;/* UNUSED */
+ (void)w;/* UNUSED */
+ (void)len;/* UNUSED */
+ errno = ENOSYS;
+ return (-1);
+}
+
+#endif /* HAVE_WCTOMB || HAVE_WCRTOMB */
+
+/*
+ * Find a string conversion object by a pair of 'from' charset name
+ * and 'to' charset name from an archive object.
+ * Return NULL if not found.
+ */
+static struct archive_string_conv *
+find_sconv_object(struct archive *a, const char *fc, const char *tc)
+{
+ struct archive_string_conv *sc;
+
+ if (a == NULL)
+ return (NULL);
+
+ for (sc = a->sconv; sc != NULL; sc = sc->next) {
+ if (strcmp(sc->from_charset, fc) == 0 &&
+ strcmp(sc->to_charset, tc) == 0)
+ break;
+ }
+ return (sc);
+}
+
+/*
+ * Register a string object to an archive object.
+ */
+static void
+add_sconv_object(struct archive *a, struct archive_string_conv *sc)
+{
+ struct archive_string_conv **psc;
+
+ /* Add a new sconv to sconv list. */
+ psc = &(a->sconv);
+ while (*psc != NULL)
+ psc = &((*psc)->next);
+ *psc = sc;
+}
+
+static void
+add_converter(struct archive_string_conv *sc, int (*converter)
+ (struct archive_string *, const void *, size_t,
+ struct archive_string_conv *))
+{
+ if (sc == NULL || sc->nconverter >= 2)
+ __archive_errx(1, "Programming error");
+ sc->converter[sc->nconverter++] = converter;
+}
+
+static void
+setup_converter(struct archive_string_conv *sc)
+{
+
+ /* Reset. */
+ sc->nconverter = 0;
+
+ /*
+ * Perform special sequence for the incorrect UTF-8 filenames
+ * made by libarchive2.x.
+ */
+ if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) {
+ add_converter(sc, strncat_from_utf8_libarchive2);
+ return;
+ }
+
+ /*
+ * Convert a string to UTF-16BE/LE.
+ */
+ if (sc->flag & SCONV_TO_UTF16) {
+ /*
+ * If the current locale is UTF-8, we can translate
+ * a UTF-8 string into a UTF-16BE string.
+ */
+ if (sc->flag & SCONV_FROM_UTF8) {
+ add_converter(sc, archive_string_append_unicode);
+ return;
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (sc->flag & SCONV_WIN_CP) {
+ if (sc->flag & SCONV_TO_UTF16BE)
+ add_converter(sc, win_strncat_to_utf16be);
+ else
+ add_converter(sc, win_strncat_to_utf16le);
+ return;
+ }
+#endif
+
+#if defined(HAVE_ICONV)
+ if (sc->cd != (iconv_t)-1) {
+ add_converter(sc, iconv_strncat_in_locale);
+ return;
+ }
+#endif
+
+ if (sc->flag & SCONV_BEST_EFFORT) {
+ if (sc->flag & SCONV_TO_UTF16BE)
+ add_converter(sc,
+ best_effort_strncat_to_utf16be);
+ else
+ add_converter(sc,
+ best_effort_strncat_to_utf16le);
+ } else
+ /* Make sure we have no converter. */
+ sc->nconverter = 0;
+ return;
+ }
+
+ /*
+ * Convert a string from UTF-16BE/LE.
+ */
+ if (sc->flag & SCONV_FROM_UTF16) {
+ /*
+ * At least we should normalize a UTF-16BE string.
+ */
+ if (sc->flag & SCONV_NORMALIZATION_D)
+ add_converter(sc,archive_string_normalize_D);
+ else if (sc->flag & SCONV_NORMALIZATION_C)
+ add_converter(sc, archive_string_normalize_C);
+
+ if (sc->flag & SCONV_TO_UTF8) {
+ /*
+ * If the current locale is UTF-8, we can translate
+ * a UTF-16BE/LE string into a UTF-8 string directly.
+ */
+ if (!(sc->flag &
+ (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
+ add_converter(sc,
+ archive_string_append_unicode);
+ return;
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (sc->flag & SCONV_WIN_CP) {
+ if (sc->flag & SCONV_FROM_UTF16BE)
+ add_converter(sc, win_strncat_from_utf16be);
+ else
+ add_converter(sc, win_strncat_from_utf16le);
+ return;
+ }
+#endif
+
+#if defined(HAVE_ICONV)
+ if (sc->cd != (iconv_t)-1) {
+ add_converter(sc, iconv_strncat_in_locale);
+ return;
+ }
+#endif
+
+ if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
+ == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
+ add_converter(sc, best_effort_strncat_from_utf16be);
+ else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
+ == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
+ add_converter(sc, best_effort_strncat_from_utf16le);
+ else
+ /* Make sure we have no converter. */
+ sc->nconverter = 0;
+ return;
+ }
+
+ if (sc->flag & SCONV_FROM_UTF8) {
+ /*
+ * At least we should normalize a UTF-8 string.
+ */
+ if (sc->flag & SCONV_NORMALIZATION_D)
+ add_converter(sc,archive_string_normalize_D);
+ else if (sc->flag & SCONV_NORMALIZATION_C)
+ add_converter(sc, archive_string_normalize_C);
+
+ /*
+ * Copy UTF-8 string with a check of CESU-8.
+ * Apparently, iconv does not check surrogate pairs in UTF-8
+ * when both from-charset and to-charset are UTF-8, and then
+ * we use our UTF-8 copy code.
+ */
+ if (sc->flag & SCONV_TO_UTF8) {
+ /*
+ * If the current locale is UTF-8, we can translate
+ * a UTF-16BE string into a UTF-8 string directly.
+ */
+ if (!(sc->flag &
+ (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
+ add_converter(sc, strncat_from_utf8_to_utf8);
+ return;
+ }
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * On Windows we can use Windows API for a string conversion.
+ */
+ if (sc->flag & SCONV_WIN_CP) {
+ add_converter(sc, strncat_in_codepage);
+ return;
+ }
+#endif
+
+#if HAVE_ICONV
+ if (sc->cd != (iconv_t)-1) {
+ add_converter(sc, iconv_strncat_in_locale);
+ /*
+ * iconv generally does not support UTF-8-MAC and so
+ * we have to the output of iconv from NFC to NFD if
+ * need.
+ */
+ if ((sc->flag & SCONV_FROM_CHARSET) &&
+ (sc->flag & SCONV_TO_UTF8)) {
+ if (sc->flag & SCONV_NORMALIZATION_D)
+ add_converter(sc, archive_string_normalize_D);
+ }
+ return;
+ }
+#endif
+
+ /*
+ * Try conversion in the best effort or no conversion.
+ */
+ if ((sc->flag & SCONV_BEST_EFFORT) || sc->same)
+ add_converter(sc, best_effort_strncat_in_locale);
+ else
+ /* Make sure we have no converter. */
+ sc->nconverter = 0;
+}
+
+/*
+ * Return canonicalized charset-name but this supports just UTF-8, UTF-16BE
+ * and CP932 which are referenced in create_sconv_object().
+ */
+static const char *
+canonical_charset_name(const char *charset)
+{
+ char cs[16];
+ char *p;
+ const char *s;
+
+ if (charset == NULL || charset[0] == '\0'
+ || strlen(charset) > 15)
+ return (charset);
+
+ /* Copy name to uppercase. */
+ p = cs;
+ s = charset;
+ while (*s) {
+ char c = *s++;
+ if (c >= 'a' && c <= 'z')
+ c -= 'a' - 'A';
+ *p++ = c;
+ }
+ *p++ = '\0';
+
+ if (strcmp(cs, "UTF-8") == 0 ||
+ strcmp(cs, "UTF8") == 0)
+ return ("UTF-8");
+ if (strcmp(cs, "UTF-16BE") == 0 ||
+ strcmp(cs, "UTF16BE") == 0)
+ return ("UTF-16BE");
+ if (strcmp(cs, "UTF-16LE") == 0 ||
+ strcmp(cs, "UTF16LE") == 0)
+ return ("UTF-16LE");
+ if (strcmp(cs, "CP932") == 0)
+ return ("CP932");
+ return (charset);
+}
+
+/*
+ * Create a string conversion object.
+ */
+static struct archive_string_conv *
+create_sconv_object(const char *fc, const char *tc,
+ unsigned current_codepage, int flag)
+{
+ struct archive_string_conv *sc;
+
+ sc = calloc(1, sizeof(*sc));
+ if (sc == NULL)
+ return (NULL);
+ sc->next = NULL;
+ sc->from_charset = strdup(fc);
+ if (sc->from_charset == NULL) {
+ free(sc);
+ return (NULL);
+ }
+ sc->to_charset = strdup(tc);
+ if (sc->to_charset == NULL) {
+ free(sc->from_charset);
+ free(sc);
+ return (NULL);
+ }
+ archive_string_init(&sc->utftmp);
+
+ if (flag & SCONV_TO_CHARSET) {
+ /*
+ * Convert characters from the current locale charset to
+ * a specified charset.
+ */
+ sc->from_cp = current_codepage;
+ sc->to_cp = make_codepage_from_charset(tc);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (IsValidCodePage(sc->to_cp))
+ flag |= SCONV_WIN_CP;
+#endif
+ } else if (flag & SCONV_FROM_CHARSET) {
+ /*
+ * Convert characters from a specified charset to
+ * the current locale charset.
+ */
+ sc->to_cp = current_codepage;
+ sc->from_cp = make_codepage_from_charset(fc);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (IsValidCodePage(sc->from_cp))
+ flag |= SCONV_WIN_CP;
+#endif
+ }
+
+ /*
+ * Check if "from charset" and "to charset" are the same.
+ */
+ if (strcmp(fc, tc) == 0 ||
+ (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp))
+ sc->same = 1;
+ else
+ sc->same = 0;
+
+ /*
+ * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE.
+ */
+ if (strcmp(tc, "UTF-8") == 0)
+ flag |= SCONV_TO_UTF8;
+ else if (strcmp(tc, "UTF-16BE") == 0)
+ flag |= SCONV_TO_UTF16BE;
+ else if (strcmp(tc, "UTF-16LE") == 0)
+ flag |= SCONV_TO_UTF16LE;
+ if (strcmp(fc, "UTF-8") == 0)
+ flag |= SCONV_FROM_UTF8;
+ else if (strcmp(fc, "UTF-16BE") == 0)
+ flag |= SCONV_FROM_UTF16BE;
+ else if (strcmp(fc, "UTF-16LE") == 0)
+ flag |= SCONV_FROM_UTF16LE;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (sc->to_cp == CP_UTF8)
+ flag |= SCONV_TO_UTF8;
+ else if (sc->to_cp == CP_UTF16BE)
+ flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP;
+ else if (sc->to_cp == CP_UTF16LE)
+ flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP;
+ if (sc->from_cp == CP_UTF8)
+ flag |= SCONV_FROM_UTF8;
+ else if (sc->from_cp == CP_UTF16BE)
+ flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP;
+ else if (sc->from_cp == CP_UTF16LE)
+ flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP;
+#endif
+
+ /*
+ * Set a flag for Unicode NFD. Usually iconv cannot correctly
+ * handle it. So we have to translate NFD characters to NFC ones
+ * ourselves before iconv handles. Another reason is to prevent
+ * that the same sight of two filenames, one is NFC and other
+ * is NFD, would be in its directory.
+ * On Mac OS X, although its filesystem layer automatically
+ * convert filenames to NFD, it would be useful for filename
+ * comparing to find out the same filenames that we normalize
+ * that to be NFD ourselves.
+ */
+ if ((flag & SCONV_FROM_CHARSET) &&
+ (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) {
+#if defined(__APPLE__)
+ if (flag & SCONV_TO_UTF8)
+ flag |= SCONV_NORMALIZATION_D;
+ else
+#endif
+ flag |= SCONV_NORMALIZATION_C;
+ }
+#if defined(__APPLE__)
+ /*
+ * In case writing an archive file, make sure that a filename
+ * going to be passed to iconv is a Unicode NFC string since
+ * a filename in HFS Plus filesystem is a Unicode NFD one and
+ * iconv cannot handle it with "UTF-8" charset. It is simpler
+ * than a use of "UTF-8-MAC" charset.
+ */
+ if ((flag & SCONV_TO_CHARSET) &&
+ (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+ !(flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))
+ flag |= SCONV_NORMALIZATION_C;
+ /*
+ * In case reading an archive file. make sure that a filename
+ * will be passed to users is a Unicode NFD string in order to
+ * correctly compare the filename with other one which comes
+ * from HFS Plus filesystem.
+ */
+ if ((flag & SCONV_FROM_CHARSET) &&
+ !(flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+ (flag & SCONV_TO_UTF8))
+ flag |= SCONV_NORMALIZATION_D;
+#endif
+
+#if defined(HAVE_ICONV)
+ sc->cd_w = (iconv_t)-1;
+ /*
+ * Create an iconv object.
+ */
+ if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) &&
+ (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) ||
+ (flag & SCONV_WIN_CP)) {
+ /* This case we won't use iconv. */
+ sc->cd = (iconv_t)-1;
+ } else {
+ sc->cd = iconv_open(tc, fc);
+ if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) {
+ /*
+ * Unfortunately, all of iconv implements do support
+ * "CP932" character-set, so we should use "SJIS"
+ * instead if iconv_open failed.
+ */
+ if (strcmp(tc, "CP932") == 0)
+ sc->cd = iconv_open("SJIS", fc);
+ else if (strcmp(fc, "CP932") == 0)
+ sc->cd = iconv_open(tc, "SJIS");
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * archive_mstring on Windows directly convert multi-bytes
+ * into archive_wstring in order not to depend on locale
+ * so that you can do a I18N programming. This will be
+ * used only in archive_mstring_copy_mbs_len_l so far.
+ */
+ if (flag & SCONV_FROM_CHARSET) {
+ sc->cd_w = iconv_open("UTF-8", fc);
+ if (sc->cd_w == (iconv_t)-1 &&
+ (sc->flag & SCONV_BEST_EFFORT)) {
+ if (strcmp(fc, "CP932") == 0)
+ sc->cd_w = iconv_open("UTF-8", "SJIS");
+ }
+ }
+#endif /* _WIN32 && !__CYGWIN__ */
+ }
+#endif /* HAVE_ICONV */
+
+ sc->flag = flag;
+
+ /*
+ * Set up converters.
+ */
+ setup_converter(sc);
+
+ return (sc);
+}
+
+/*
+ * Free a string conversion object.
+ */
+static void
+free_sconv_object(struct archive_string_conv *sc)
+{
+ free(sc->from_charset);
+ free(sc->to_charset);
+ archive_string_free(&sc->utftmp);
+#if HAVE_ICONV
+ if (sc->cd != (iconv_t)-1)
+ iconv_close(sc->cd);
+ if (sc->cd_w != (iconv_t)-1)
+ iconv_close(sc->cd_w);
+#endif
+ free(sc);
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+# define GetOEMCP() CP_OEMCP
+# endif
+
+static unsigned
+my_atoi(const char *p)
+{
+ unsigned cp;
+
+ cp = 0;
+ while (*p) {
+ if (*p >= '0' && *p <= '9')
+ cp = cp * 10 + (*p - '0');
+ else
+ return (-1);
+ p++;
+ }
+ return (cp);
+}
+
+/*
+ * Translate Charset name (as used by iconv) into CodePage (as used by Windows)
+ * Return -1 if failed.
+ *
+ * Note: This translation code may be insufficient.
+ */
+static struct charset {
+ const char *name;
+ unsigned cp;
+} charsets[] = {
+ /* MUST BE SORTED! */
+ {"ASCII", 1252},
+ {"ASMO-708", 708},
+ {"BIG5", 950},
+ {"CHINESE", 936},
+ {"CP367", 1252},
+ {"CP819", 1252},
+ {"CP1025", 21025},
+ {"DOS-720", 720},
+ {"DOS-862", 862},
+ {"EUC-CN", 51936},
+ {"EUC-JP", 51932},
+ {"EUC-KR", 949},
+ {"EUCCN", 51936},
+ {"EUCJP", 51932},
+ {"EUCKR", 949},
+ {"GB18030", 54936},
+ {"GB2312", 936},
+ {"HEBREW", 1255},
+ {"HZ-GB-2312", 52936},
+ {"IBM273", 20273},
+ {"IBM277", 20277},
+ {"IBM278", 20278},
+ {"IBM280", 20280},
+ {"IBM284", 20284},
+ {"IBM285", 20285},
+ {"IBM290", 20290},
+ {"IBM297", 20297},
+ {"IBM367", 1252},
+ {"IBM420", 20420},
+ {"IBM423", 20423},
+ {"IBM424", 20424},
+ {"IBM819", 1252},
+ {"IBM871", 20871},
+ {"IBM880", 20880},
+ {"IBM905", 20905},
+ {"IBM924", 20924},
+ {"ISO-8859-1", 28591},
+ {"ISO-8859-13", 28603},
+ {"ISO-8859-15", 28605},
+ {"ISO-8859-2", 28592},
+ {"ISO-8859-3", 28593},
+ {"ISO-8859-4", 28594},
+ {"ISO-8859-5", 28595},
+ {"ISO-8859-6", 28596},
+ {"ISO-8859-7", 28597},
+ {"ISO-8859-8", 28598},
+ {"ISO-8859-9", 28599},
+ {"ISO8859-1", 28591},
+ {"ISO8859-13", 28603},
+ {"ISO8859-15", 28605},
+ {"ISO8859-2", 28592},
+ {"ISO8859-3", 28593},
+ {"ISO8859-4", 28594},
+ {"ISO8859-5", 28595},
+ {"ISO8859-6", 28596},
+ {"ISO8859-7", 28597},
+ {"ISO8859-8", 28598},
+ {"ISO8859-9", 28599},
+ {"JOHAB", 1361},
+ {"KOI8-R", 20866},
+ {"KOI8-U", 21866},
+ {"KS_C_5601-1987", 949},
+ {"LATIN1", 1252},
+ {"LATIN2", 28592},
+ {"MACINTOSH", 10000},
+ {"SHIFT-JIS", 932},
+ {"SHIFT_JIS", 932},
+ {"SJIS", 932},
+ {"US", 1252},
+ {"US-ASCII", 1252},
+ {"UTF-16", 1200},
+ {"UTF-16BE", 1201},
+ {"UTF-16LE", 1200},
+ {"UTF-8", CP_UTF8},
+ {"X-EUROPA", 29001},
+ {"X-MAC-ARABIC", 10004},
+ {"X-MAC-CE", 10029},
+ {"X-MAC-CHINESEIMP", 10008},
+ {"X-MAC-CHINESETRAD", 10002},
+ {"X-MAC-CROATIAN", 10082},
+ {"X-MAC-CYRILLIC", 10007},
+ {"X-MAC-GREEK", 10006},
+ {"X-MAC-HEBREW", 10005},
+ {"X-MAC-ICELANDIC", 10079},
+ {"X-MAC-JAPANESE", 10001},
+ {"X-MAC-KOREAN", 10003},
+ {"X-MAC-ROMANIAN", 10010},
+ {"X-MAC-THAI", 10021},
+ {"X-MAC-TURKISH", 10081},
+ {"X-MAC-UKRAINIAN", 10017},
+};
+static unsigned
+make_codepage_from_charset(const char *charset)
+{
+ char cs[16];
+ char *p;
+ unsigned cp;
+ int a, b;
+
+ if (charset == NULL || strlen(charset) > 15)
+ return -1;
+
+ /* Copy name to uppercase. */
+ p = cs;
+ while (*charset) {
+ char c = *charset++;
+ if (c >= 'a' && c <= 'z')
+ c -= 'a' - 'A';
+ *p++ = c;
+ }
+ *p++ = '\0';
+ cp = -1;
+
+ /* Look it up in the table first, so that we can easily
+ * override CP367, which we map to 1252 instead of 367. */
+ a = 0;
+ b = sizeof(charsets)/sizeof(charsets[0]);
+ while (b > a) {
+ int c = (b + a) / 2;
+ int r = strcmp(charsets[c].name, cs);
+ if (r < 0)
+ a = c + 1;
+ else if (r > 0)
+ b = c;
+ else
+ return charsets[c].cp;
+ }
+
+ /* If it's not in the table, try to parse it. */
+ switch (*cs) {
+ case 'C':
+ if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') {
+ cp = my_atoi(cs + 2);
+ } else if (strcmp(cs, "CP_ACP") == 0)
+ cp = get_current_codepage();
+ else if (strcmp(cs, "CP_OEMCP") == 0)
+ cp = get_current_oemcp();
+ break;
+ case 'I':
+ if (cs[1] == 'B' && cs[2] == 'M' &&
+ cs[3] >= '0' && cs[3] <= '9') {
+ cp = my_atoi(cs + 3);
+ }
+ break;
+ case 'W':
+ if (strncmp(cs, "WINDOWS-", 8) == 0) {
+ cp = my_atoi(cs + 8);
+ if (cp != 874 && (cp < 1250 || cp > 1258))
+ cp = -1;/* This may invalid code. */
+ }
+ break;
+ }
+ return (cp);
+}
+
+/*
+ * Return ANSI Code Page of current locale set by setlocale().
+ */
+static unsigned
+get_current_codepage(void)
+{
+ char *locale, *p;
+ unsigned cp;
+
+ locale = setlocale(LC_CTYPE, NULL);
+ if (locale == NULL)
+ return (GetACP());
+ if (locale[0] == 'C' && locale[1] == '\0')
+ return (CP_C_LOCALE);
+ p = strrchr(locale, '.');
+ if (p == NULL)
+ return (GetACP());
+ if (strcmp(p+1, "utf8") == 0)
+ return CP_UTF8;
+ cp = my_atoi(p+1);
+ if ((int)cp <= 0)
+ return (GetACP());
+ return (cp);
+}
+
+/*
+ * Translation table between Locale Name and ACP/OEMCP.
+ */
+static struct {
+ unsigned acp;
+ unsigned ocp;
+ const char *locale;
+} acp_ocp_map[] = {
+ { 950, 950, "Chinese_Taiwan" },
+ { 936, 936, "Chinese_People's Republic of China" },
+ { 950, 950, "Chinese_Taiwan" },
+ { 1250, 852, "Czech_Czech Republic" },
+ { 1252, 850, "Danish_Denmark" },
+ { 1252, 850, "Dutch_Netherlands" },
+ { 1252, 850, "Dutch_Belgium" },
+ { 1252, 437, "English_United States" },
+ { 1252, 850, "English_Australia" },
+ { 1252, 850, "English_Canada" },
+ { 1252, 850, "English_New Zealand" },
+ { 1252, 850, "English_United Kingdom" },
+ { 1252, 437, "English_United States" },
+ { 1252, 850, "Finnish_Finland" },
+ { 1252, 850, "French_France" },
+ { 1252, 850, "French_Belgium" },
+ { 1252, 850, "French_Canada" },
+ { 1252, 850, "French_Switzerland" },
+ { 1252, 850, "German_Germany" },
+ { 1252, 850, "German_Austria" },
+ { 1252, 850, "German_Switzerland" },
+ { 1253, 737, "Greek_Greece" },
+ { 1250, 852, "Hungarian_Hungary" },
+ { 1252, 850, "Icelandic_Iceland" },
+ { 1252, 850, "Italian_Italy" },
+ { 1252, 850, "Italian_Switzerland" },
+ { 932, 932, "Japanese_Japan" },
+ { 949, 949, "Korean_Korea" },
+ { 1252, 850, "Norwegian (BokmOl)_Norway" },
+ { 1252, 850, "Norwegian (BokmOl)_Norway" },
+ { 1252, 850, "Norwegian-Nynorsk_Norway" },
+ { 1250, 852, "Polish_Poland" },
+ { 1252, 850, "Portuguese_Portugal" },
+ { 1252, 850, "Portuguese_Brazil" },
+ { 1251, 866, "Russian_Russia" },
+ { 1250, 852, "Slovak_Slovakia" },
+ { 1252, 850, "Spanish_Spain" },
+ { 1252, 850, "Spanish_Mexico" },
+ { 1252, 850, "Spanish_Spain" },
+ { 1252, 850, "Swedish_Sweden" },
+ { 1254, 857, "Turkish_Turkey" },
+ { 0, 0, NULL}
+};
+
+/*
+ * Return OEM Code Page of current locale set by setlocale().
+ */
+static unsigned
+get_current_oemcp(void)
+{
+ int i;
+ char *locale, *p;
+ size_t len;
+
+ locale = setlocale(LC_CTYPE, NULL);
+ if (locale == NULL)
+ return (GetOEMCP());
+ if (locale[0] == 'C' && locale[1] == '\0')
+ return (CP_C_LOCALE);
+
+ p = strrchr(locale, '.');
+ if (p == NULL)
+ return (GetOEMCP());
+ len = p - locale;
+ for (i = 0; acp_ocp_map[i].acp; i++) {
+ if (strncmp(acp_ocp_map[i].locale, locale, len) == 0)
+ return (acp_ocp_map[i].ocp);
+ }
+ return (GetOEMCP());
+}
+#else
+
+/*
+ * POSIX platform does not use CodePage.
+ */
+
+static unsigned
+get_current_codepage(void)
+{
+ return (-1);/* Unknown */
+}
+static unsigned
+make_codepage_from_charset(const char *charset)
+{
+ (void)charset; /* UNUSED */
+ return (-1);/* Unknown */
+}
+static unsigned
+get_current_oemcp(void)
+{
+ return (-1);/* Unknown */
+}
+
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+
+/*
+ * Return a string conversion object.
+ */
+static struct archive_string_conv *
+get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag)
+{
+ struct archive_string_conv *sc;
+ unsigned current_codepage;
+
+ /* Check if we have made the sconv object. */
+ sc = find_sconv_object(a, fc, tc);
+ if (sc != NULL)
+ return (sc);
+
+ if (a == NULL)
+ current_codepage = get_current_codepage();
+ else
+ current_codepage = a->current_codepage;
+
+ sc = create_sconv_object(canonical_charset_name(fc),
+ canonical_charset_name(tc), current_codepage, flag);
+ if (sc == NULL) {
+ if (a != NULL)
+ archive_set_error(a, ENOMEM,
+ "Could not allocate memory for "
+ "a string conversion object");
+ return (NULL);
+ }
+
+ /*
+ * If there is no converter for current string conversion object,
+ * we cannot handle this conversion.
+ */
+ if (sc->nconverter == 0) {
+ if (a != NULL) {
+#if HAVE_ICONV
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "iconv_open failed : Cannot handle ``%s''",
+ (flag & SCONV_TO_CHARSET)?tc:fc);
+#else
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "A character-set conversion not fully supported "
+ "on this platform");
+#endif
+ }
+ /* Failed; free a sconv object. */
+ free_sconv_object(sc);
+ return (NULL);
+ }
+
+ /*
+ * Success!
+ */
+ if (a != NULL)
+ add_sconv_object(a, sc);
+ return (sc);
+}
+
+static const char *
+get_current_charset(struct archive *a)
+{
+ const char *cur_charset;
+
+ if (a == NULL)
+ cur_charset = default_iconv_charset("");
+ else {
+ cur_charset = default_iconv_charset(a->current_code);
+ if (a->current_code == NULL) {
+ a->current_code = strdup(cur_charset);
+ a->current_codepage = get_current_codepage();
+ a->current_oemcp = get_current_oemcp();
+ }
+ }
+ return (cur_charset);
+}
+
+/*
+ * Make and Return a string conversion object.
+ * Return NULL if the platform does not support the specified conversion
+ * and best_effort is 0.
+ * If best_effort is set, A string conversion object must be returned
+ * unless memory allocation for the object fails, but the conversion
+ * might fail when non-ASCII code is found.
+ */
+struct archive_string_conv *
+archive_string_conversion_to_charset(struct archive *a, const char *charset,
+ int best_effort)
+{
+ int flag = SCONV_TO_CHARSET;
+
+ if (best_effort)
+ flag |= SCONV_BEST_EFFORT;
+ return (get_sconv_object(a, get_current_charset(a), charset, flag));
+}
+
+struct archive_string_conv *
+archive_string_conversion_from_charset(struct archive *a, const char *charset,
+ int best_effort)
+{
+ int flag = SCONV_FROM_CHARSET;
+
+ if (best_effort)
+ flag |= SCONV_BEST_EFFORT;
+ return (get_sconv_object(a, charset, get_current_charset(a), flag));
+}
+
+/*
+ * archive_string_default_conversion_*_archive() are provided for Windows
+ * platform because other archiver application use CP_OEMCP for
+ * MultiByteToWideChar() and WideCharToMultiByte() for the filenames
+ * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP
+ * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP).
+ * So we should make a string conversion between CP_ACP and CP_OEMCP
+ * for compatibility.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *a)
+{
+ const char *cur_charset = get_current_charset(a);
+ char oemcp[16];
+
+ /* NOTE: a check of cur_charset is unneeded but we need
+ * that get_current_charset() has been surely called at
+ * this time whatever C compiler optimized. */
+ if (cur_charset != NULL &&
+ (a->current_codepage == CP_C_LOCALE ||
+ a->current_codepage == a->current_oemcp))
+ return (NULL);/* no conversion. */
+
+ _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+ /* Make sure a null termination must be set. */
+ oemcp[sizeof(oemcp)-1] = '\0';
+ return (get_sconv_object(a, oemcp, cur_charset,
+ SCONV_FROM_CHARSET));
+}
+
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *a)
+{
+ const char *cur_charset = get_current_charset(a);
+ char oemcp[16];
+
+ /* NOTE: a check of cur_charset is unneeded but we need
+ * that get_current_charset() has been surely called at
+ * this time whatever C compiler optimized. */
+ if (cur_charset != NULL &&
+ (a->current_codepage == CP_C_LOCALE ||
+ a->current_codepage == a->current_oemcp))
+ return (NULL);/* no conversion. */
+
+ _snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+ /* Make sure a null termination must be set. */
+ oemcp[sizeof(oemcp)-1] = '\0';
+ return (get_sconv_object(a, cur_charset, oemcp,
+ SCONV_TO_CHARSET));
+}
+#else
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *a)
+{
+ (void)a; /* UNUSED */
+ return (NULL);
+}
+
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *a)
+{
+ (void)a; /* UNUSED */
+ return (NULL);
+}
+#endif
+
+/*
+ * Dispose of all character conversion objects in the archive object.
+ */
+void
+archive_string_conversion_free(struct archive *a)
+{
+ struct archive_string_conv *sc;
+ struct archive_string_conv *sc_next;
+
+ for (sc = a->sconv; sc != NULL; sc = sc_next) {
+ sc_next = sc->next;
+ free_sconv_object(sc);
+ }
+ a->sconv = NULL;
+ free(a->current_code);
+ a->current_code = NULL;
+}
+
+/*
+ * Return a conversion charset name.
+ */
+const char *
+archive_string_conversion_charset_name(struct archive_string_conv *sc)
+{
+ if (sc->flag & SCONV_TO_CHARSET)
+ return (sc->to_charset);
+ else
+ return (sc->from_charset);
+}
+
+/*
+ * Change the behavior of a string conversion.
+ */
+void
+archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt)
+{
+ switch (opt) {
+ /*
+ * A filename in UTF-8 was made with libarchive 2.x in a wrong
+ * assumption that wchar_t was Unicode.
+ * This option enables simulating the assumption in order to read
+ * that filename correctly.
+ */
+ case SCONV_SET_OPT_UTF8_LIBARCHIVE2X:
+#if (defined(_WIN32) && !defined(__CYGWIN__)) \
+ || defined(__STDC_ISO_10646__) || defined(__APPLE__)
+ /*
+ * Nothing to do for it since wchar_t on these platforms
+ * is really Unicode.
+ */
+ (void)sc; /* UNUSED */
+#else
+ if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) {
+ sc->flag |= SCONV_UTF8_LIBARCHIVE_2;
+ /* Set up string converters. */
+ setup_converter(sc);
+ }
+#endif
+ break;
+ case SCONV_SET_OPT_NORMALIZATION_C:
+ if ((sc->flag & SCONV_NORMALIZATION_C) == 0) {
+ sc->flag |= SCONV_NORMALIZATION_C;
+ sc->flag &= ~SCONV_NORMALIZATION_D;
+ /* Set up string converters. */
+ setup_converter(sc);
+ }
+ break;
+ case SCONV_SET_OPT_NORMALIZATION_D:
+#if defined(HAVE_ICONV)
+ /*
+ * If iconv will take the string, do not change the
+ * setting of the normalization.
+ */
+ if (!(sc->flag & SCONV_WIN_CP) &&
+ (sc->flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+ !(sc->flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))
+ break;
+#endif
+ if ((sc->flag & SCONV_NORMALIZATION_D) == 0) {
+ sc->flag |= SCONV_NORMALIZATION_D;
+ sc->flag &= ~SCONV_NORMALIZATION_C;
+ /* Set up string converters. */
+ setup_converter(sc);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ *
+ * Copy one archive_string to another in locale conversion.
+ *
+ * archive_strncat_l();
+ * archive_strncpy_l();
+ *
+ */
+
+static size_t
+mbsnbytes(const void *_p, size_t n)
+{
+ size_t s;
+ const char *p, *pp;
+
+ if (_p == NULL)
+ return (0);
+ p = (const char *)_p;
+
+ /* Like strlen(p), except won't examine positions beyond p[n]. */
+ s = 0;
+ pp = p;
+ while (s < n && *pp) {
+ pp++;
+ s++;
+ }
+ return (s);
+}
+
+static size_t
+utf16nbytes(const void *_p, size_t n)
+{
+ size_t s;
+ const char *p, *pp;
+
+ if (_p == NULL)
+ return (0);
+ p = (const char *)_p;
+
+ /* Like strlen(p), except won't examine positions beyond p[n]. */
+ s = 0;
+ pp = p;
+ n >>= 1;
+ while (s < n && (pp[0] || pp[1])) {
+ pp += 2;
+ s++;
+ }
+ return (s<<1);
+}
+
+int
+archive_strncpy_l(struct archive_string *as, const void *_p, size_t n,
+ struct archive_string_conv *sc)
+{
+ as->length = 0;
+ return (archive_strncat_l(as, _p, n, sc));
+}
+
+int
+archive_strncat_l(struct archive_string *as, const void *_p, size_t n,
+ struct archive_string_conv *sc)
+{
+ const void *s;
+ size_t length = 0;
+ int i, r = 0, r2;
+
+ if (_p != NULL && n > 0) {
+ if (sc != NULL && (sc->flag & SCONV_FROM_UTF16))
+ length = utf16nbytes(_p, n);
+ else
+ length = mbsnbytes(_p, n);
+ }
+
+ /* We must allocate memory even if there is no data for conversion
+ * or copy. This simulates archive_string_append behavior. */
+ if (length == 0) {
+ int tn = 1;
+ if (sc != NULL && (sc->flag & SCONV_TO_UTF16))
+ tn = 2;
+ if (archive_string_ensure(as, as->length + tn) == NULL)
+ return (-1);
+ as->s[as->length] = 0;
+ if (tn == 2)
+ as->s[as->length+1] = 0;
+ return (0);
+ }
+
+ /*
+ * If sc is NULL, we just make a copy.
+ */
+ if (sc == NULL) {
+ if (archive_string_append(as, _p, length) == NULL)
+ return (-1);/* No memory */
+ return (0);
+ }
+
+ s = _p;
+ i = 0;
+ if (sc->nconverter > 1) {
+ sc->utftmp.length = 0;
+ r2 = sc->converter[0](&(sc->utftmp), s, length, sc);
+ if (r2 != 0 && errno == ENOMEM)
+ return (r2);
+ if (r > r2)
+ r = r2;
+ s = sc->utftmp.s;
+ length = sc->utftmp.length;
+ ++i;
+ }
+ r2 = sc->converter[i](as, s, length, sc);
+ if (r > r2)
+ r = r2;
+ return (r);
+}
+
+#if HAVE_ICONV
+
+/*
+ * Return -1 if conversion fails.
+ */
+static int
+iconv_strncat_in_locale(struct archive_string *as, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ ICONV_CONST char *itp;
+ size_t remaining;
+ iconv_t cd;
+ char *outp;
+ size_t avail, bs;
+ int return_value = 0; /* success */
+ int to_size, from_size;
+
+ if (sc->flag & SCONV_TO_UTF16)
+ to_size = 2;
+ else
+ to_size = 1;
+ if (sc->flag & SCONV_FROM_UTF16)
+ from_size = 2;
+ else
+ from_size = 1;
+
+ if (archive_string_ensure(as, as->length + length*2+to_size) == NULL)
+ return (-1);
+
+ cd = sc->cd;
+ itp = (char *)(uintptr_t)_p;
+ remaining = length;
+ outp = as->s + as->length;
+ avail = as->buffer_length - as->length - to_size;
+ while (remaining >= (size_t)from_size) {
+ size_t result = iconv(cd, &itp, &remaining, &outp, &avail);
+
+ if (result != (size_t)-1)
+ break; /* Conversion completed. */
+
+ if (errno == EILSEQ || errno == EINVAL) {
+ /*
+ * If an output charset is UTF-8 or UTF-16BE/LE,
+ * unknown character should be U+FFFD
+ * (replacement character).
+ */
+ if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) {
+ size_t rbytes;
+ if (sc->flag & SCONV_TO_UTF8)
+ rbytes = sizeof(utf8_replacement_char);
+ else
+ rbytes = 2;
+
+ if (avail < rbytes) {
+ as->length = outp - as->s;
+ bs = as->buffer_length +
+ (remaining * to_size) + rbytes;
+ if (NULL ==
+ archive_string_ensure(as, bs))
+ return (-1);
+ outp = as->s + as->length;
+ avail = as->buffer_length
+ - as->length - to_size;
+ }
+ if (sc->flag & SCONV_TO_UTF8)
+ memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char));
+ else if (sc->flag & SCONV_TO_UTF16BE)
+ archive_be16enc(outp, UNICODE_R_CHAR);
+ else
+ archive_le16enc(outp, UNICODE_R_CHAR);
+ outp += rbytes;
+ avail -= rbytes;
+ } else {
+ /* Skip the illegal input bytes. */
+ *outp++ = '?';
+ avail--;
+ }
+ itp += from_size;
+ remaining -= from_size;
+ return_value = -1; /* failure */
+ } else {
+ /* E2BIG no output buffer,
+ * Increase an output buffer. */
+ as->length = outp - as->s;
+ bs = as->buffer_length + remaining * 2;
+ if (NULL == archive_string_ensure(as, bs))
+ return (-1);
+ outp = as->s + as->length;
+ avail = as->buffer_length - as->length - to_size;
+ }
+ }
+ as->length = outp - as->s;
+ as->s[as->length] = 0;
+ if (to_size == 2)
+ as->s[as->length+1] = 0;
+ return (return_value);
+}
+
+#endif /* HAVE_ICONV */
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Translate a string from a some CodePage to an another CodePage by
+ * Windows APIs, and copy the result. Return -1 if conversion fails.
+ */
+static int
+strncat_in_codepage(struct archive_string *as,
+ const void *_p, size_t length, struct archive_string_conv *sc)
+{
+ const char *s = (const char *)_p;
+ struct archive_wstring aws;
+ size_t l;
+ int r, saved_flag;
+
+ archive_string_init(&aws);
+ saved_flag = sc->flag;
+ sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C);
+ r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc);
+ sc->flag = saved_flag;
+ if (r != 0) {
+ archive_wstring_free(&aws);
+ if (errno != ENOMEM)
+ archive_string_append(as, s, length);
+ return (-1);
+ }
+
+ l = as->length;
+ r = archive_string_append_from_wcs_in_codepage(
+ as, aws.s, aws.length, sc);
+ if (r != 0 && errno != ENOMEM && l == as->length)
+ archive_string_append(as, s, length);
+ archive_wstring_free(&aws);
+ return (r);
+}
+
+/*
+ * Test whether MBS ==> WCS is okay.
+ */
+static int
+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
+{
+ const char *p = (const char *)_p;
+ unsigned codepage;
+ DWORD mbflag = MB_ERR_INVALID_CHARS;
+
+ if (sc->flag & SCONV_FROM_CHARSET)
+ codepage = sc->to_cp;
+ else
+ codepage = sc->from_cp;
+
+ if (codepage == CP_C_LOCALE)
+ return (0);
+ if (codepage != CP_UTF8)
+ mbflag |= MB_PRECOMPOSED;
+
+ if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0)
+ return (-1); /* Invalid */
+ return (0); /* Okay */
+}
+
+#else
+
+/*
+ * Test whether MBS ==> WCS is okay.
+ */
+static int
+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
+{
+ const char *p = (const char *)_p;
+ size_t r;
+
+#if HAVE_MBRTOWC
+ mbstate_t shift_state;
+
+ memset(&shift_state, 0, sizeof(shift_state));
+#else
+ /* Clear the shift state before starting. */
+ mbtowc(NULL, NULL, 0);
+#endif
+ while (n) {
+ wchar_t wc;
+
+#if HAVE_MBRTOWC
+ r = mbrtowc(&wc, p, n, &shift_state);
+#else
+ r = mbtowc(&wc, p, n);
+#endif
+ if (r == (size_t)-1 || r == (size_t)-2)
+ return (-1);/* Invalid. */
+ if (r == 0)
+ break;
+ p += r;
+ n -= r;
+ }
+ (void)sc; /* UNUSED */
+ return (0); /* All Okey. */
+}
+
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+
+/*
+ * Basically returns -1 because we cannot make a conversion of charset
+ * without iconv but in some cases this would return 0.
+ * Returns 0 if all copied characters are ASCII.
+ * Returns 0 if both from-locale and to-locale are the same and those
+ * can be WCS with no error.
+ */
+static int
+best_effort_strncat_in_locale(struct archive_string *as, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ size_t remaining;
+ const uint8_t *itp;
+ int return_value = 0; /* success */
+
+ /*
+ * If both from-locale and to-locale is the same, this makes a copy.
+ * And then this checks all copied MBS can be WCS if so returns 0.
+ */
+ if (sc->same) {
+ if (archive_string_append(as, _p, length) == NULL)
+ return (-1);/* No memory */
+ return (invalid_mbs(_p, length, sc));
+ }
+
+ /*
+ * If a character is ASCII, this just copies it. If not, this
+ * assigns '?' character instead but in UTF-8 locale this assigns
+ * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD,
+ * a Replacement Character in Unicode.
+ */
+
+ remaining = length;
+ itp = (const uint8_t *)_p;
+ while (*itp && remaining > 0) {
+ if (*itp > 127) {
+ // Non-ASCII: Substitute with suitable replacement
+ if (sc->flag & SCONV_TO_UTF8) {
+ if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) {
+ __archive_errx(1, "Out of memory");
+ }
+ } else {
+ archive_strappend_char(as, '?');
+ }
+ return_value = -1;
+ } else {
+ archive_strappend_char(as, *itp);
+ }
+ ++itp;
+ }
+ return (return_value);
+}
+
+
+/*
+ * Unicode conversion functions.
+ * - UTF-8 <===> UTF-8 in removing surrogate pairs.
+ * - UTF-8 NFD ===> UTF-8 NFC in removing surrogate pairs.
+ * - UTF-8 made by libarchive 2.x ===> UTF-8.
+ * - UTF-16BE <===> UTF-8.
+ *
+ */
+
+/*
+ * Utility to convert a single UTF-8 sequence.
+ *
+ * Usually return used bytes, return used byte in negative value when
+ * a unicode character is replaced with U+FFFD.
+ * See also http://unicode.org/review/pr-121.html Public Review Issue #121
+ * Recommended Practice for Replacement Characters.
+ */
+static int
+_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ static const char utf8_count[256] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */
+ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
+ };
+ int ch, i;
+ int cnt;
+ uint32_t wc;
+
+ /* Sanity check. */
+ if (n == 0)
+ return (0);
+ /*
+ * Decode 1-4 bytes depending on the value of the first byte.
+ */
+ ch = (unsigned char)*s;
+ if (ch == 0)
+ return (0); /* Standard: return 0 for end-of-string. */
+ cnt = utf8_count[ch];
+
+ /* Invalid sequence or there are not plenty bytes. */
+ if ((int)n < cnt) {
+ cnt = (int)n;
+ for (i = 1; i < cnt; i++) {
+ if ((s[i] & 0xc0) != 0x80) {
+ cnt = i;
+ break;
+ }
+ }
+ goto invalid_sequence;
+ }
+
+ /* Make a Unicode code point from a single UTF-8 sequence. */
+ switch (cnt) {
+ case 1: /* 1 byte sequence. */
+ *pwc = ch & 0x7f;
+ return (cnt);
+ case 2: /* 2 bytes sequence. */
+ if ((s[1] & 0xc0) != 0x80) {
+ cnt = 1;
+ goto invalid_sequence;
+ }
+ *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
+ return (cnt);
+ case 3: /* 3 bytes sequence. */
+ if ((s[1] & 0xc0) != 0x80) {
+ cnt = 1;
+ goto invalid_sequence;
+ }
+ if ((s[2] & 0xc0) != 0x80) {
+ cnt = 2;
+ goto invalid_sequence;
+ }
+ wc = ((ch & 0x0f) << 12)
+ | ((s[1] & 0x3f) << 6)
+ | (s[2] & 0x3f);
+ if (wc < 0x800)
+ goto invalid_sequence;/* Overlong sequence. */
+ break;
+ case 4: /* 4 bytes sequence. */
+ if ((s[1] & 0xc0) != 0x80) {
+ cnt = 1;
+ goto invalid_sequence;
+ }
+ if ((s[2] & 0xc0) != 0x80) {
+ cnt = 2;
+ goto invalid_sequence;
+ }
+ if ((s[3] & 0xc0) != 0x80) {
+ cnt = 3;
+ goto invalid_sequence;
+ }
+ wc = ((ch & 0x07) << 18)
+ | ((s[1] & 0x3f) << 12)
+ | ((s[2] & 0x3f) << 6)
+ | (s[3] & 0x3f);
+ if (wc < 0x10000)
+ goto invalid_sequence;/* Overlong sequence. */
+ break;
+ default: /* Others are all invalid sequence. */
+ if (ch == 0xc0 || ch == 0xc1)
+ cnt = 2;
+ else if (ch >= 0xf5 && ch <= 0xf7)
+ cnt = 4;
+ else if (ch >= 0xf8 && ch <= 0xfb)
+ cnt = 5;
+ else if (ch == 0xfc || ch == 0xfd)
+ cnt = 6;
+ else
+ cnt = 1;
+ if ((int)n < cnt)
+ cnt = (int)n;
+ for (i = 1; i < cnt; i++) {
+ if ((s[i] & 0xc0) != 0x80) {
+ cnt = i;
+ break;
+ }
+ }
+ goto invalid_sequence;
+ }
+
+ /* The code point larger than 0x10FFFF is not legal
+ * Unicode values. */
+ if (wc > UNICODE_MAX)
+ goto invalid_sequence;
+ /* Correctly gets a Unicode, returns used bytes. */
+ *pwc = wc;
+ return (cnt);
+invalid_sequence:
+ *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */
+ return (cnt * -1);
+}
+
+static int
+utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ int cnt;
+
+ cnt = _utf8_to_unicode(pwc, s, n);
+ /* Any of Surrogate pair is not legal Unicode values. */
+ if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc))
+ return (-3);
+ return (cnt);
+}
+
+static inline uint32_t
+combine_surrogate_pair(uint32_t uc, uint32_t uc2)
+{
+ uc -= 0xD800;
+ uc *= 0x400;
+ uc += uc2 - 0xDC00;
+ uc += 0x10000;
+ return (uc);
+}
+
+/*
+ * Convert a single UTF-8/CESU-8 sequence to a Unicode code point in
+ * removing surrogate pairs.
+ *
+ * CESU-8: The Compatibility Encoding Scheme for UTF-16.
+ *
+ * Usually return used bytes, return used byte in negative value when
+ * a unicode character is replaced with U+FFFD.
+ */
+static int
+cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ uint32_t wc = 0;
+ int cnt;
+
+ cnt = _utf8_to_unicode(&wc, s, n);
+ if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) {
+ uint32_t wc2 = 0;
+ if (n - 3 < 3) {
+ /* Invalid byte sequence. */
+ goto invalid_sequence;
+ }
+ cnt = _utf8_to_unicode(&wc2, s+3, n-3);
+ if (cnt != 3 || !IS_LOW_SURROGATE_LA(wc2)) {
+ /* Invalid byte sequence. */
+ goto invalid_sequence;
+ }
+ wc = combine_surrogate_pair(wc, wc2);
+ cnt = 6;
+ } else if (cnt == 3 && IS_LOW_SURROGATE_LA(wc)) {
+ /* Invalid byte sequence. */
+ goto invalid_sequence;
+ }
+ *pwc = wc;
+ return (cnt);
+invalid_sequence:
+ *pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */
+ if (cnt > 0)
+ cnt *= -1;
+ return (cnt);
+}
+
+/*
+ * Convert a Unicode code point to a single UTF-8 sequence.
+ *
+ * NOTE:This function does not check if the Unicode is legal or not.
+ * Please you definitely check it before calling this.
+ */
+static size_t
+unicode_to_utf8(char *p, size_t remaining, uint32_t uc)
+{
+ char *_p = p;
+
+ /* Invalid Unicode char maps to Replacement character */
+ if (uc > UNICODE_MAX)
+ uc = UNICODE_R_CHAR;
+ /* Translate code point to UTF8 */
+ if (uc <= 0x7f) {
+ if (remaining == 0)
+ return (0);
+ *p++ = (char)uc;
+ } else if (uc <= 0x7ff) {
+ if (remaining < 2)
+ return (0);
+ *p++ = 0xc0 | ((uc >> 6) & 0x1f);
+ *p++ = 0x80 | (uc & 0x3f);
+ } else if (uc <= 0xffff) {
+ if (remaining < 3)
+ return (0);
+ *p++ = 0xe0 | ((uc >> 12) & 0x0f);
+ *p++ = 0x80 | ((uc >> 6) & 0x3f);
+ *p++ = 0x80 | (uc & 0x3f);
+ } else {
+ if (remaining < 4)
+ return (0);
+ *p++ = 0xf0 | ((uc >> 18) & 0x07);
+ *p++ = 0x80 | ((uc >> 12) & 0x3f);
+ *p++ = 0x80 | ((uc >> 6) & 0x3f);
+ *p++ = 0x80 | (uc & 0x3f);
+ }
+ return (p - _p);
+}
+
+static int
+utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ return (utf16_to_unicode(pwc, s, n, 1));
+}
+
+static int
+utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+ return (utf16_to_unicode(pwc, s, n, 0));
+}
+
+static int
+utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be)
+{
+ const char *utf16 = s;
+ unsigned uc;
+
+ if (n == 0)
+ return (0);
+ if (n == 1) {
+ /* set the Replacement Character instead. */
+ *pwc = UNICODE_R_CHAR;
+ return (-1);
+ }
+
+ if (be)
+ uc = archive_be16dec(utf16);
+ else
+ uc = archive_le16dec(utf16);
+ utf16 += 2;
+
+ /* If this is a surrogate pair, assemble the full code point.*/
+ if (IS_HIGH_SURROGATE_LA(uc)) {
+ unsigned uc2;
+
+ if (n >= 4) {
+ if (be)
+ uc2 = archive_be16dec(utf16);
+ else
+ uc2 = archive_le16dec(utf16);
+ } else
+ uc2 = 0;
+ if (IS_LOW_SURROGATE_LA(uc2)) {
+ uc = combine_surrogate_pair(uc, uc2);
+ utf16 += 2;
+ } else {
+ /* Undescribed code point should be U+FFFD
+ * (replacement character). */
+ *pwc = UNICODE_R_CHAR;
+ return (-2);
+ }
+ }
+
+ /*
+ * Surrogate pair values(0xd800 through 0xdfff) are only
+ * used by UTF-16, so, after above calculation, the code
+ * must not be surrogate values, and Unicode has no codes
+ * larger than 0x10ffff. Thus, those are not legal Unicode
+ * values.
+ */
+ if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) {
+ /* Undescribed code point should be U+FFFD
+ * (replacement character). */
+ *pwc = UNICODE_R_CHAR;
+ return (((int)(utf16 - s)) * -1);
+ }
+ *pwc = uc;
+ return ((int)(utf16 - s));
+}
+
+static size_t
+unicode_to_utf16be(char *p, size_t remaining, uint32_t uc)
+{
+ char *utf16 = p;
+
+ if (uc > 0xffff) {
+ /* We have a code point that won't fit into a
+ * wchar_t; convert it to a surrogate pair. */
+ if (remaining < 4)
+ return (0);
+ uc -= 0x10000;
+ archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
+ archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
+ return (4);
+ } else {
+ if (remaining < 2)
+ return (0);
+ archive_be16enc(utf16, uc);
+ return (2);
+ }
+}
+
+static size_t
+unicode_to_utf16le(char *p, size_t remaining, uint32_t uc)
+{
+ char *utf16 = p;
+
+ if (uc > 0xffff) {
+ /* We have a code point that won't fit into a
+ * wchar_t; convert it to a surrogate pair. */
+ if (remaining < 4)
+ return (0);
+ uc -= 0x10000;
+ archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
+ archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
+ return (4);
+ } else {
+ if (remaining < 2)
+ return (0);
+ archive_le16enc(utf16, uc);
+ return (2);
+ }
+}
+
+/*
+ * Copy UTF-8 string in checking surrogate pair.
+ * If any surrogate pair are found, it would be canonicalized.
+ */
+static int
+strncat_from_utf8_to_utf8(struct archive_string *as, const void *_p,
+ size_t len, struct archive_string_conv *sc)
+{
+ const char *s;
+ char *p, *endp;
+ int n, ret = 0;
+
+ (void)sc; /* UNUSED */
+
+ if (archive_string_ensure(as, as->length + len + 1) == NULL)
+ return (-1);
+
+ s = (const char *)_p;
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length -1;
+ do {
+ uint32_t uc;
+ const char *ss = s;
+ size_t w;
+
+ /*
+ * Forward byte sequence until a conversion of that is needed.
+ */
+ while ((n = utf8_to_unicode(&uc, s, len)) > 0) {
+ s += n;
+ len -= n;
+ }
+ if (ss < s) {
+ if (p + (s - ss) > endp) {
+ as->length = p - as->s;
+ if (archive_string_ensure(as,
+ as->buffer_length + len + 1) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length -1;
+ }
+
+ memcpy(p, ss, s - ss);
+ p += s - ss;
+ }
+
+ /*
+ * If n is negative, current byte sequence needs a replacement.
+ */
+ if (n < 0) {
+ if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) {
+ /* Current byte sequence may be CESU-8. */
+ n = cesu8_to_unicode(&uc, s, len);
+ }
+ if (n < 0) {
+ ret = -1;
+ n *= -1;/* Use a replaced unicode character. */
+ }
+
+ /* Rebuild UTF-8 byte sequence. */
+ while ((w = unicode_to_utf8(p, endp - p, uc)) == 0) {
+ as->length = p - as->s;
+ if (archive_string_ensure(as,
+ as->buffer_length + len + 1) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length -1;
+ }
+ p += w;
+ s += n;
+ len -= n;
+ }
+ } while (n > 0);
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ return (ret);
+}
+
+static int
+archive_string_append_unicode(struct archive_string *as, const void *_p,
+ size_t len, struct archive_string_conv *sc)
+{
+ const char *s;
+ char *p, *endp;
+ uint32_t uc;
+ size_t w;
+ int n, ret = 0, ts, tm;
+ int (*parse)(uint32_t *, const char *, size_t);
+ size_t (*unparse)(char *, size_t, uint32_t);
+
+ if (sc->flag & SCONV_TO_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ } else if (sc->flag & SCONV_TO_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ } else if (sc->flag & SCONV_TO_UTF8) {
+ unparse = unicode_to_utf8;
+ ts = 1;
+ } else {
+ /*
+ * This case is going to be converted to another
+ * character-set through iconv.
+ */
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ } else {
+ unparse = unicode_to_utf8;
+ ts = 1;
+ }
+ }
+
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ parse = utf16be_to_unicode;
+ tm = 1;
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ parse = utf16le_to_unicode;
+ tm = 1;
+ } else {
+ parse = cesu8_to_unicode;
+ tm = ts;
+ }
+
+ if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+ return (-1);
+
+ s = (const char *)_p;
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length - ts;
+ while ((n = parse(&uc, s, len)) != 0) {
+ if (n < 0) {
+ /* Use a replaced unicode character. */
+ n *= -1;
+ ret = -1;
+ }
+ s += n;
+ len -= n;
+ while ((w = unparse(p, endp - p, uc)) == 0) {
+ /* There is not enough output buffer so
+ * we have to expand it. */
+ as->length = p - as->s;
+ if (archive_string_ensure(as,
+ as->buffer_length + len * tm + ts) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length - ts;
+ }
+ p += w;
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ if (ts == 2)
+ as->s[as->length+1] = '\0';
+ return (ret);
+}
+
+/*
+ * Following Constants for Hangul compositions this information comes from
+ * Unicode Standard Annex #15 http://unicode.org/reports/tr15/
+ */
+#define HC_SBASE 0xAC00
+#define HC_LBASE 0x1100
+#define HC_VBASE 0x1161
+#define HC_TBASE 0x11A7
+#define HC_LCOUNT 19
+#define HC_VCOUNT 21
+#define HC_TCOUNT 28
+#define HC_NCOUNT (HC_VCOUNT * HC_TCOUNT)
+#define HC_SCOUNT (HC_LCOUNT * HC_NCOUNT)
+
+static uint32_t
+get_nfc(uint32_t uc, uint32_t uc2)
+{
+ int t, b;
+
+ t = 0;
+ b = sizeof(u_composition_table)/sizeof(u_composition_table[0]) -1;
+ while (b >= t) {
+ int m = (t + b) / 2;
+ if (u_composition_table[m].cp1 < uc)
+ t = m + 1;
+ else if (u_composition_table[m].cp1 > uc)
+ b = m - 1;
+ else if (u_composition_table[m].cp2 < uc2)
+ t = m + 1;
+ else if (u_composition_table[m].cp2 > uc2)
+ b = m - 1;
+ else
+ return (u_composition_table[m].nfc);
+ }
+ return (0);
+}
+
+#define FDC_MAX 10 /* The maximum number of Following Decomposable
+ * Characters. */
+
+/*
+ * Update first code point.
+ */
+#define UPDATE_UC(new_uc) do { \
+ uc = new_uc; \
+ ucptr = NULL; \
+} while (0)
+
+/*
+ * Replace first code point with second code point.
+ */
+#define REPLACE_UC_WITH_UC2() do { \
+ uc = uc2; \
+ ucptr = uc2ptr; \
+ n = n2; \
+} while (0)
+
+#define EXPAND_BUFFER() do { \
+ as->length = p - as->s; \
+ if (archive_string_ensure(as, \
+ as->buffer_length + len * tm + ts) == NULL)\
+ return (-1); \
+ p = as->s + as->length; \
+ endp = as->s + as->buffer_length - ts; \
+} while (0)
+
+#define UNPARSE(p, endp, uc) do { \
+ while ((w = unparse(p, (endp) - (p), uc)) == 0) {\
+ EXPAND_BUFFER(); \
+ } \
+ p += w; \
+} while (0)
+
+/*
+ * Write first code point.
+ * If the code point has not be changed from its original code,
+ * this just copies it from its original buffer pointer.
+ * If not, this converts it to UTF-8 byte sequence and copies it.
+ */
+#define WRITE_UC() do { \
+ if (ucptr) { \
+ if (p + n > endp) \
+ EXPAND_BUFFER(); \
+ switch (n) { \
+ case 4: \
+ *p++ = *ucptr++; \
+ /* FALL THROUGH */ \
+ case 3: \
+ *p++ = *ucptr++; \
+ /* FALL THROUGH */ \
+ case 2: \
+ *p++ = *ucptr++; \
+ /* FALL THROUGH */ \
+ case 1: \
+ *p++ = *ucptr; \
+ break; \
+ } \
+ ucptr = NULL; \
+ } else { \
+ UNPARSE(p, endp, uc); \
+ } \
+} while (0)
+
+/*
+ * Collect following decomposable code points.
+ */
+#define COLLECT_CPS(start) do { \
+ int _i; \
+ for (_i = start; _i < FDC_MAX ; _i++) { \
+ nx = parse(&ucx[_i], s, len); \
+ if (nx <= 0) \
+ break; \
+ cx = CCC(ucx[_i]); \
+ if (cl >= cx && cl != 228 && cx != 228)\
+ break; \
+ s += nx; \
+ len -= nx; \
+ cl = cx; \
+ ccx[_i] = cx; \
+ } \
+ if (_i >= FDC_MAX) { \
+ ret = -1; \
+ ucx_size = FDC_MAX; \
+ } else \
+ ucx_size = _i; \
+} while (0)
+
+/*
+ * Normalize UTF-8/UTF-16BE characters to Form C and copy the result.
+ *
+ * TODO: Convert composition exclusions, which are never converted
+ * from NFC,NFD,NFKC and NFKD, to Form C.
+ */
+static int
+archive_string_normalize_C(struct archive_string *as, const void *_p,
+ size_t len, struct archive_string_conv *sc)
+{
+ const char *s = (const char *)_p;
+ char *p, *endp;
+ uint32_t uc, uc2;
+ size_t w;
+ int always_replace, n, n2, ret = 0, spair, ts, tm;
+ int (*parse)(uint32_t *, const char *, size_t);
+ size_t (*unparse)(char *, size_t, uint32_t);
+
+ always_replace = 1;
+ ts = 1;/* text size. */
+ if (sc->flag & SCONV_TO_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ if (sc->flag & SCONV_FROM_UTF16BE)
+ always_replace = 0;
+ } else if (sc->flag & SCONV_TO_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ if (sc->flag & SCONV_FROM_UTF16LE)
+ always_replace = 0;
+ } else if (sc->flag & SCONV_TO_UTF8) {
+ unparse = unicode_to_utf8;
+ if (sc->flag & SCONV_FROM_UTF8)
+ always_replace = 0;
+ } else {
+ /*
+ * This case is going to be converted to another
+ * character-set through iconv.
+ */
+ always_replace = 0;
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ } else {
+ unparse = unicode_to_utf8;
+ }
+ }
+
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ parse = utf16be_to_unicode;
+ tm = 1;
+ spair = 4;/* surrogate pair size in UTF-16. */
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ parse = utf16le_to_unicode;
+ tm = 1;
+ spair = 4;/* surrogate pair size in UTF-16. */
+ } else {
+ parse = cesu8_to_unicode;
+ tm = ts;
+ spair = 6;/* surrogate pair size in UTF-8. */
+ }
+
+ if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+ return (-1);
+
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length - ts;
+ while ((n = parse(&uc, s, len)) != 0) {
+ const char *ucptr, *uc2ptr;
+
+ if (n < 0) {
+ /* Use a replaced unicode character. */
+ UNPARSE(p, endp, uc);
+ s += n*-1;
+ len -= n*-1;
+ ret = -1;
+ continue;
+ } else if (n == spair || always_replace)
+ /* uc is converted from a surrogate pair.
+ * this should be treated as a changed code. */
+ ucptr = NULL;
+ else
+ ucptr = s;
+ s += n;
+ len -= n;
+
+ /* Read second code point. */
+ while ((n2 = parse(&uc2, s, len)) > 0) {
+ uint32_t ucx[FDC_MAX];
+ int ccx[FDC_MAX];
+ int cl, cx, i, nx, ucx_size;
+ int LIndex,SIndex;
+ uint32_t nfc;
+
+ if (n2 == spair || always_replace)
+ /* uc2 is converted from a surrogate pair.
+ * this should be treated as a changed code. */
+ uc2ptr = NULL;
+ else
+ uc2ptr = s;
+ s += n2;
+ len -= n2;
+
+ /*
+ * If current second code point is out of decomposable
+ * code points, finding compositions is unneeded.
+ */
+ if (!IS_DECOMPOSABLE_BLOCK(uc2)) {
+ WRITE_UC();
+ REPLACE_UC_WITH_UC2();
+ continue;
+ }
+
+ /*
+ * Try to combine current code points.
+ */
+ /*
+ * We have to combine Hangul characters according to
+ * http://uniicode.org/reports/tr15/#Hangul
+ */
+ if (0 <= (LIndex = uc - HC_LBASE) &&
+ LIndex < HC_LCOUNT) {
+ /*
+ * Hangul Composition.
+ * 1. Two current code points are L and V.
+ */
+ int VIndex = uc2 - HC_VBASE;
+ if (0 <= VIndex && VIndex < HC_VCOUNT) {
+ /* Make syllable of form LV. */
+ UPDATE_UC(HC_SBASE +
+ (LIndex * HC_VCOUNT + VIndex) *
+ HC_TCOUNT);
+ } else {
+ WRITE_UC();
+ REPLACE_UC_WITH_UC2();
+ }
+ continue;
+ } else if (0 <= (SIndex = uc - HC_SBASE) &&
+ SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) {
+ /*
+ * Hangul Composition.
+ * 2. Two current code points are LV and T.
+ */
+ int TIndex = uc2 - HC_TBASE;
+ if (0 < TIndex && TIndex < HC_TCOUNT) {
+ /* Make syllable of form LVT. */
+ UPDATE_UC(uc + TIndex);
+ } else {
+ WRITE_UC();
+ REPLACE_UC_WITH_UC2();
+ }
+ continue;
+ } else if ((nfc = get_nfc(uc, uc2)) != 0) {
+ /* A composition to current code points
+ * is found. */
+ UPDATE_UC(nfc);
+ continue;
+ } else if ((cl = CCC(uc2)) == 0) {
+ /* Clearly 'uc2' the second code point is not
+ * a decomposable code. */
+ WRITE_UC();
+ REPLACE_UC_WITH_UC2();
+ continue;
+ }
+
+ /*
+ * Collect following decomposable code points.
+ */
+ cx = 0;
+ ucx[0] = uc2;
+ ccx[0] = cl;
+ COLLECT_CPS(1);
+
+ /*
+ * Find a composed code in the collected code points.
+ */
+ i = 1;
+ while (i < ucx_size) {
+ int j;
+
+ if ((nfc = get_nfc(uc, ucx[i])) == 0) {
+ i++;
+ continue;
+ }
+
+ /*
+ * nfc is composed of uc and ucx[i].
+ */
+ UPDATE_UC(nfc);
+
+ /*
+ * Remove ucx[i] by shifting
+ * following code points.
+ */
+ for (j = i; j+1 < ucx_size; j++) {
+ ucx[j] = ucx[j+1];
+ ccx[j] = ccx[j+1];
+ }
+ ucx_size --;
+
+ /*
+ * Collect following code points blocked
+ * by ucx[i] the removed code point.
+ */
+ if (ucx_size > 0 && i == ucx_size &&
+ nx > 0 && cx == cl) {
+ cl = ccx[ucx_size-1];
+ COLLECT_CPS(ucx_size);
+ }
+ /*
+ * Restart finding a composed code with
+ * the updated uc from the top of the
+ * collected code points.
+ */
+ i = 0;
+ }
+
+ /*
+ * Apparently the current code points are not
+ * decomposed characters or already composed.
+ */
+ WRITE_UC();
+ for (i = 0; i < ucx_size; i++)
+ UNPARSE(p, endp, ucx[i]);
+
+ /*
+ * Flush out remaining canonical combining characters.
+ */
+ if (nx > 0 && cx == cl && len > 0) {
+ while ((nx = parse(&ucx[0], s, len))
+ > 0) {
+ cx = CCC(ucx[0]);
+ if (cl > cx)
+ break;
+ s += nx;
+ len -= nx;
+ cl = cx;
+ UNPARSE(p, endp, ucx[0]);
+ }
+ }
+ break;
+ }
+ if (n2 < 0) {
+ WRITE_UC();
+ /* Use a replaced unicode character. */
+ UNPARSE(p, endp, uc2);
+ s += n2*-1;
+ len -= n2*-1;
+ ret = -1;
+ continue;
+ } else if (n2 == 0) {
+ WRITE_UC();
+ break;
+ }
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ if (ts == 2)
+ as->s[as->length+1] = '\0';
+ return (ret);
+}
+
+static int
+get_nfd(uint32_t *cp1, uint32_t *cp2, uint32_t uc)
+{
+ int t, b;
+
+ /*
+ * These are not converted to NFD on Mac OS.
+ */
+ if ((uc >= 0x2000 && uc <= 0x2FFF) ||
+ (uc >= 0xF900 && uc <= 0xFAFF) ||
+ (uc >= 0x2F800 && uc <= 0x2FAFF))
+ return (0);
+ /*
+ * Those code points are not converted to NFD on Mac OS.
+ * I do not know the reason because it is undocumented.
+ * NFC NFD
+ * 1109A ==> 11099 110BA
+ * 1109C ==> 1109B 110BA
+ * 110AB ==> 110A5 110BA
+ */
+ if (uc == 0x1109A || uc == 0x1109C || uc == 0x110AB)
+ return (0);
+
+ t = 0;
+ b = sizeof(u_decomposition_table)/sizeof(u_decomposition_table[0]) -1;
+ while (b >= t) {
+ int m = (t + b) / 2;
+ if (u_decomposition_table[m].nfc < uc)
+ t = m + 1;
+ else if (u_decomposition_table[m].nfc > uc)
+ b = m - 1;
+ else {
+ *cp1 = u_decomposition_table[m].cp1;
+ *cp2 = u_decomposition_table[m].cp2;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+#define REPLACE_UC_WITH(cp) do { \
+ uc = cp; \
+ ucptr = NULL; \
+} while (0)
+
+/*
+ * Normalize UTF-8 characters to Form D and copy the result.
+ */
+static int
+archive_string_normalize_D(struct archive_string *as, const void *_p,
+ size_t len, struct archive_string_conv *sc)
+{
+ const char *s = (const char *)_p;
+ char *p, *endp;
+ uint32_t uc, uc2;
+ size_t w;
+ int always_replace, n, n2, ret = 0, spair, ts, tm;
+ int (*parse)(uint32_t *, const char *, size_t);
+ size_t (*unparse)(char *, size_t, uint32_t);
+
+ always_replace = 1;
+ ts = 1;/* text size. */
+ if (sc->flag & SCONV_TO_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ if (sc->flag & SCONV_FROM_UTF16BE)
+ always_replace = 0;
+ } else if (sc->flag & SCONV_TO_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ if (sc->flag & SCONV_FROM_UTF16LE)
+ always_replace = 0;
+ } else if (sc->flag & SCONV_TO_UTF8) {
+ unparse = unicode_to_utf8;
+ if (sc->flag & SCONV_FROM_UTF8)
+ always_replace = 0;
+ } else {
+ /*
+ * This case is going to be converted to another
+ * character-set through iconv.
+ */
+ always_replace = 0;
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ unparse = unicode_to_utf16be;
+ ts = 2;
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ unparse = unicode_to_utf16le;
+ ts = 2;
+ } else {
+ unparse = unicode_to_utf8;
+ }
+ }
+
+ if (sc->flag & SCONV_FROM_UTF16BE) {
+ parse = utf16be_to_unicode;
+ tm = 1;
+ spair = 4;/* surrogate pair size in UTF-16. */
+ } else if (sc->flag & SCONV_FROM_UTF16LE) {
+ parse = utf16le_to_unicode;
+ tm = 1;
+ spair = 4;/* surrogate pair size in UTF-16. */
+ } else {
+ parse = cesu8_to_unicode;
+ tm = ts;
+ spair = 6;/* surrogate pair size in UTF-8. */
+ }
+
+ if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+ return (-1);
+
+ p = as->s + as->length;
+ endp = as->s + as->buffer_length - ts;
+ while ((n = parse(&uc, s, len)) != 0) {
+ const char *ucptr;
+ uint32_t cp1, cp2;
+ int SIndex;
+ struct {
+ uint32_t uc;
+ int ccc;
+ } fdc[FDC_MAX];
+ int fdi, fdj;
+ int ccc;
+
+check_first_code:
+ if (n < 0) {
+ /* Use a replaced unicode character. */
+ UNPARSE(p, endp, uc);
+ s += n*-1;
+ len -= n*-1;
+ ret = -1;
+ continue;
+ } else if (n == spair || always_replace)
+ /* uc is converted from a surrogate pair.
+ * this should be treated as a changed code. */
+ ucptr = NULL;
+ else
+ ucptr = s;
+ s += n;
+ len -= n;
+
+ /* Hangul Decomposition. */
+ if ((SIndex = uc - HC_SBASE) >= 0 && SIndex < HC_SCOUNT) {
+ int L = HC_LBASE + SIndex / HC_NCOUNT;
+ int V = HC_VBASE + (SIndex % HC_NCOUNT) / HC_TCOUNT;
+ int T = HC_TBASE + SIndex % HC_TCOUNT;
+
+ REPLACE_UC_WITH(L);
+ WRITE_UC();
+ REPLACE_UC_WITH(V);
+ WRITE_UC();
+ if (T != HC_TBASE) {
+ REPLACE_UC_WITH(T);
+ WRITE_UC();
+ }
+ continue;
+ }
+ if (IS_DECOMPOSABLE_BLOCK(uc) && CCC(uc) != 0) {
+ WRITE_UC();
+ continue;
+ }
+
+ fdi = 0;
+ while (get_nfd(&cp1, &cp2, uc) && fdi < FDC_MAX) {
+ int k;
+
+ for (k = fdi; k > 0; k--)
+ fdc[k] = fdc[k-1];
+ fdc[0].ccc = CCC(cp2);
+ fdc[0].uc = cp2;
+ fdi++;
+ REPLACE_UC_WITH(cp1);
+ }
+
+ /* Read following code points. */
+ while ((n2 = parse(&uc2, s, len)) > 0 &&
+ (ccc = CCC(uc2)) != 0 && fdi < FDC_MAX) {
+ int j, k;
+
+ s += n2;
+ len -= n2;
+ for (j = 0; j < fdi; j++) {
+ if (fdc[j].ccc > ccc)
+ break;
+ }
+ if (j < fdi) {
+ for (k = fdi; k > j; k--)
+ fdc[k] = fdc[k-1];
+ fdc[j].ccc = ccc;
+ fdc[j].uc = uc2;
+ } else {
+ fdc[fdi].ccc = ccc;
+ fdc[fdi].uc = uc2;
+ }
+ fdi++;
+ }
+
+ WRITE_UC();
+ for (fdj = 0; fdj < fdi; fdj++) {
+ REPLACE_UC_WITH(fdc[fdj].uc);
+ WRITE_UC();
+ }
+
+ if (n2 == 0)
+ break;
+ REPLACE_UC_WITH(uc2);
+ n = n2;
+ goto check_first_code;
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ if (ts == 2)
+ as->s[as->length+1] = '\0';
+ return (ret);
+}
+
+/*
+ * libarchive 2.x made incorrect UTF-8 strings in the wrong assumption
+ * that WCS is Unicode. It is true for several platforms but some are false.
+ * And then people who did not use UTF-8 locale on the non Unicode WCS
+ * platform and made a tar file with libarchive(mostly bsdtar) 2.x. Those
+ * now cannot get right filename from libarchive 3.x and later since we
+ * fixed the wrong assumption and it is incompatible to older its versions.
+ * So we provide special option, "compat-2x.x", for resolving it.
+ * That option enable the string conversion of libarchive 2.x.
+ *
+ * Translates the wrong UTF-8 string made by libarchive 2.x into current
+ * locale character set and appends to the archive_string.
+ * Note: returns -1 if conversion fails.
+ */
+static int
+strncat_from_utf8_libarchive2(struct archive_string *as,
+ const void *_p, size_t len, struct archive_string_conv *sc)
+{
+ const char *s;
+ int n;
+ char *p;
+ char *end;
+ uint32_t unicode;
+#if HAVE_WCRTOMB
+ mbstate_t shift_state;
+
+ memset(&shift_state, 0, sizeof(shift_state));
+#else
+ /* Clear the shift state before starting. */
+ wctomb(NULL, L'\0');
+#endif
+ (void)sc; /* UNUSED */
+ /*
+ * Allocate buffer for MBS.
+ * We need this allocation here since it is possible that
+ * as->s is still NULL.
+ */
+ if (archive_string_ensure(as, as->length + len + 1) == NULL)
+ return (-1);
+
+ s = (const char *)_p;
+ p = as->s + as->length;
+ end = as->s + as->buffer_length - MB_CUR_MAX -1;
+ while ((n = _utf8_to_unicode(&unicode, s, len)) != 0) {
+ wchar_t wc;
+
+ if (p >= end) {
+ as->length = p - as->s;
+ /* Re-allocate buffer for MBS. */
+ if (archive_string_ensure(as,
+ as->length + max(len * 2,
+ (size_t)MB_CUR_MAX) + 1) == NULL)
+ return (-1);
+ p = as->s + as->length;
+ end = as->s + as->buffer_length - MB_CUR_MAX -1;
+ }
+
+ /*
+ * As libarchive 2.x, translates the UTF-8 characters into
+ * wide-characters in the assumption that WCS is Unicode.
+ */
+ if (n < 0) {
+ n *= -1;
+ wc = L'?';
+ } else
+ wc = (wchar_t)unicode;
+
+ s += n;
+ len -= n;
+ /*
+ * Translates the wide-character into the current locale MBS.
+ */
+#if HAVE_WCRTOMB
+ n = (int)wcrtomb(p, wc, &shift_state);
+#else
+ n = (int)wctomb(p, wc);
+#endif
+ if (n == -1)
+ return (-1);
+ p += n;
+ }
+ as->length = p - as->s;
+ as->s[as->length] = '\0';
+ return (0);
+}
+
+
+/*
+ * Conversion functions between current locale dependent MBS and UTF-16BE.
+ * strncat_from_utf16be() : UTF-16BE --> MBS
+ * strncat_to_utf16be() : MBS --> UTF16BE
+ */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Convert a UTF-16BE/LE string to current locale and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes,
+ struct archive_string_conv *sc, int be)
+{
+ struct archive_string tmp;
+ const char *u16;
+ int ll;
+ BOOL defchar;
+ char *mbs;
+ size_t mbs_size, b;
+ int ret = 0;
+
+ bytes &= ~1;
+ if (archive_string_ensure(as, as->length + bytes +1) == NULL)
+ return (-1);
+
+ mbs = as->s + as->length;
+ mbs_size = as->buffer_length - as->length -1;
+
+ if (sc->to_cp == CP_C_LOCALE) {
+ /*
+ * "C" locale special process.
+ */
+ u16 = _p;
+ ll = 0;
+ for (b = 0; b < bytes; b += 2) {
+ uint16_t val;
+ if (be)
+ val = archive_be16dec(u16+b);
+ else
+ val = archive_le16dec(u16+b);
+ if (val > 255) {
+ *mbs++ = '?';
+ ret = -1;
+ } else
+ *mbs++ = (char)(val&0xff);
+ ll++;
+ }
+ as->length += ll;
+ as->s[as->length] = '\0';
+ return (ret);
+ }
+
+ archive_string_init(&tmp);
+ if (be) {
+ if (is_big_endian()) {
+ u16 = _p;
+ } else {
+ if (archive_string_ensure(&tmp, bytes+2) == NULL)
+ return (-1);
+ memcpy(tmp.s, _p, bytes);
+ for (b = 0; b < bytes; b += 2) {
+ uint16_t val = archive_be16dec(tmp.s+b);
+ archive_le16enc(tmp.s+b, val);
+ }
+ u16 = tmp.s;
+ }
+ } else {
+ if (!is_big_endian()) {
+ u16 = _p;
+ } else {
+ if (archive_string_ensure(&tmp, bytes+2) == NULL)
+ return (-1);
+ memcpy(tmp.s, _p, bytes);
+ for (b = 0; b < bytes; b += 2) {
+ uint16_t val = archive_le16dec(tmp.s+b);
+ archive_be16enc(tmp.s+b, val);
+ }
+ u16 = tmp.s;
+ }
+ }
+
+ do {
+ defchar = 0;
+ ll = WideCharToMultiByte(sc->to_cp, 0,
+ (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size,
+ NULL, &defchar);
+ /* Exit loop if we succeeded */
+ if (ll != 0 ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ break;
+ }
+ /* Else expand buffer and loop to try again. */
+ ll = WideCharToMultiByte(sc->to_cp, 0,
+ (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL);
+ if (archive_string_ensure(as, ll +1) == NULL)
+ return (-1);
+ mbs = as->s + as->length;
+ mbs_size = as->buffer_length - as->length -1;
+ } while (1);
+ archive_string_free(&tmp);
+ as->length += ll;
+ as->s[as->length] = '\0';
+ if (ll == 0 || defchar)
+ ret = -1;
+ return (ret);
+}
+
+static int
+win_strncat_from_utf16be(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc)
+{
+ return (win_strncat_from_utf16(as, _p, bytes, sc, 1));
+}
+
+static int
+win_strncat_from_utf16le(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc)
+{
+ return (win_strncat_from_utf16(as, _p, bytes, sc, 0));
+}
+
+static int
+is_big_endian(void)
+{
+ uint16_t d = 1;
+
+ return (archive_be16dec(&d) == 1);
+}
+
+/*
+ * Convert a current locale string to UTF-16BE/LE and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+win_strncat_to_utf16(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc, int bigendian)
+{
+ const char *s = (const char *)_p;
+ char *u16;
+ size_t count, avail;
+
+ if (archive_string_ensure(as16,
+ as16->length + (length + 1) * 2) == NULL)
+ return (-1);
+
+ u16 = as16->s + as16->length;
+ avail = as16->buffer_length - 2;
+ if (sc->from_cp == CP_C_LOCALE) {
+ /*
+ * "C" locale special process.
+ */
+ count = 0;
+ while (count < length && *s) {
+ if (bigendian)
+ archive_be16enc(u16, *s);
+ else
+ archive_le16enc(u16, *s);
+ u16 += 2;
+ s++;
+ count++;
+ }
+ as16->length += count << 1;
+ as16->s[as16->length] = 0;
+ as16->s[as16->length+1] = 0;
+ return (0);
+ }
+ do {
+ count = MultiByteToWideChar(sc->from_cp,
+ MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1);
+ /* Exit loop if we succeeded */
+ if (count != 0 ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ break;
+ }
+ /* Expand buffer and try again */
+ count = MultiByteToWideChar(sc->from_cp,
+ MB_PRECOMPOSED, s, (int)length, NULL, 0);
+ if (archive_string_ensure(as16, (count +1) * 2)
+ == NULL)
+ return (-1);
+ u16 = as16->s + as16->length;
+ avail = as16->buffer_length - 2;
+ } while (1);
+ as16->length += count * 2;
+ as16->s[as16->length] = 0;
+ as16->s[as16->length+1] = 0;
+ if (count == 0)
+ return (-1);
+
+ if (is_big_endian()) {
+ if (!bigendian) {
+ while (count > 0) {
+ uint16_t v = archive_be16dec(u16);
+ archive_le16enc(u16, v);
+ u16 += 2;
+ count--;
+ }
+ }
+ } else {
+ if (bigendian) {
+ while (count > 0) {
+ uint16_t v = archive_le16dec(u16);
+ archive_be16enc(u16, v);
+ u16 += 2;
+ count--;
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+win_strncat_to_utf16be(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ return (win_strncat_to_utf16(as16, _p, length, sc, 1));
+}
+
+static int
+win_strncat_to_utf16le(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ return (win_strncat_to_utf16(as16, _p, length, sc, 0));
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
+
+/*
+ * Do the best effort for conversions.
+ * We cannot handle UTF-16BE character-set without such iconv,
+ * but there is a chance if a string consists just ASCII code or
+ * a current locale is UTF-8.
+ */
+
+/*
+ * Convert a UTF-16BE string to current locale and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+best_effort_strncat_from_utf16(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc, int be)
+{
+ const char *utf16 = (const char *)_p;
+ char *mbs;
+ uint32_t uc;
+ int n, ret;
+
+ (void)sc; /* UNUSED */
+ /*
+ * Other case, we should do the best effort.
+ * If all character are ASCII(<0x7f), we can convert it.
+ * if not , we set a alternative character and return -1.
+ */
+ ret = 0;
+ if (archive_string_ensure(as, as->length + bytes +1) == NULL)
+ return (-1);
+ mbs = as->s + as->length;
+
+ while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) {
+ if (n < 0) {
+ n *= -1;
+ ret = -1;
+ }
+ bytes -= n;
+ utf16 += n;
+
+ if (uc > 127) {
+ /* We cannot handle it. */
+ *mbs++ = '?';
+ ret = -1;
+ } else
+ *mbs++ = (char)uc;
+ }
+ as->length = mbs - as->s;
+ as->s[as->length] = '\0';
+ return (ret);
+}
+
+static int
+best_effort_strncat_from_utf16be(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc)
+{
+ return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 1));
+}
+
+static int
+best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p,
+ size_t bytes, struct archive_string_conv *sc)
+{
+ return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 0));
+}
+
+/*
+ * Convert a current locale string to UTF-16BE/LE and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc, int bigendian)
+{
+ const char *s = (const char *)_p;
+ char *utf16;
+ size_t remaining;
+ int ret;
+
+ (void)sc; /* UNUSED */
+ /*
+ * Other case, we should do the best effort.
+ * If all character are ASCII(<0x7f), we can convert it.
+ * if not , we set a alternative character and return -1.
+ */
+ ret = 0;
+ remaining = length;
+
+ if (archive_string_ensure(as16,
+ as16->length + (length + 1) * 2) == NULL)
+ return (-1);
+
+ utf16 = as16->s + as16->length;
+ while (remaining--) {
+ unsigned c = *s++;
+ if (c > 127) {
+ /* We cannot handle it. */
+ c = UNICODE_R_CHAR;
+ ret = -1;
+ }
+ if (bigendian)
+ archive_be16enc(utf16, c);
+ else
+ archive_le16enc(utf16, c);
+ utf16 += 2;
+ }
+ as16->length = utf16 - as16->s;
+ as16->s[as16->length] = 0;
+ as16->s[as16->length+1] = 0;
+ return (ret);
+}
+
+static int
+best_effort_strncat_to_utf16be(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ return (best_effort_strncat_to_utf16(as16, _p, length, sc, 1));
+}
+
+static int
+best_effort_strncat_to_utf16le(struct archive_string *as16, const void *_p,
+ size_t length, struct archive_string_conv *sc)
+{
+ return (best_effort_strncat_to_utf16(as16, _p, length, sc, 0));
+}
+
+
+/*
+ * Multistring operations.
+ */
+
+void
+archive_mstring_clean(struct archive_mstring *aes)
+{
+ archive_wstring_free(&(aes->aes_wcs));
+ archive_string_free(&(aes->aes_mbs));
+ archive_string_free(&(aes->aes_utf8));
+ archive_string_free(&(aes->aes_mbs_in_locale));
+ aes->aes_set = 0;
+}
+
+void
+archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src)
+{
+ dest->aes_set = src->aes_set;
+ archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
+ archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
+ archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs));
+}
+
+int
+archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes,
+ const char **p)
+{
+ struct archive_string_conv *sc;
+ int r;
+
+ /* If we already have a UTF8 form, return that immediately. */
+ if (aes->aes_set & AES_SET_UTF8) {
+ *p = aes->aes_utf8.s;
+ return (0);
+ }
+
+ *p = NULL;
+ /* Try converting WCS to MBS first if MBS does not exist yet. */
+ if ((aes->aes_set & AES_SET_MBS) == 0) {
+ const char *pm; /* unused */
+ archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
+ }
+ if (aes->aes_set & AES_SET_MBS) {
+ sc = archive_string_conversion_to_charset(a, "UTF-8", 1);
+ if (sc == NULL)
+ return (-1);/* Couldn't allocate memory for sc. */
+ r = archive_strncpy_l(&(aes->aes_utf8), aes->aes_mbs.s,
+ aes->aes_mbs.length, sc);
+ if (a == NULL)
+ free_sconv_object(sc);
+ if (r == 0) {
+ aes->aes_set |= AES_SET_UTF8;
+ *p = aes->aes_utf8.s;
+ return (0);/* success. */
+ } else
+ return (-1);/* failure. */
+ }
+ return (0);/* success. */
+}
+
+int
+archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes,
+ const char **p)
+{
+ struct archive_string_conv *sc;
+ int r, ret = 0;
+
+ /* If we already have an MBS form, return that immediately. */
+ if (aes->aes_set & AES_SET_MBS) {
+ *p = aes->aes_mbs.s;
+ return (ret);
+ }
+
+ *p = NULL;
+ /* If there's a WCS form, try converting with the native locale. */
+ if (aes->aes_set & AES_SET_WCS) {
+ archive_string_empty(&(aes->aes_mbs));
+ r = archive_string_append_from_wcs(&(aes->aes_mbs),
+ aes->aes_wcs.s, aes->aes_wcs.length);
+ *p = aes->aes_mbs.s;
+ if (r == 0) {
+ aes->aes_set |= AES_SET_MBS;
+ return (ret);
+ } else
+ ret = -1;
+ }
+
+ /* If there's a UTF-8 form, try converting with the native locale. */
+ if (aes->aes_set & AES_SET_UTF8) {
+ archive_string_empty(&(aes->aes_mbs));
+ sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+ if (sc == NULL)
+ return (-1);/* Couldn't allocate memory for sc. */
+ r = archive_strncpy_l(&(aes->aes_mbs),
+ aes->aes_utf8.s, aes->aes_utf8.length, sc);
+ if (a == NULL)
+ free_sconv_object(sc);
+ *p = aes->aes_mbs.s;
+ if (r == 0) {
+ aes->aes_set |= AES_SET_MBS;
+ ret = 0;/* success; overwrite previous error. */
+ } else
+ ret = -1;/* failure. */
+ }
+ return (ret);
+}
+
+int
+archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes,
+ const wchar_t **wp)
+{
+ int r, ret = 0;
+
+ (void)a;/* UNUSED */
+ /* Return WCS form if we already have it. */
+ if (aes->aes_set & AES_SET_WCS) {
+ *wp = aes->aes_wcs.s;
+ return (ret);
+ }
+
+ *wp = NULL;
+ /* Try converting UTF8 to MBS first if MBS does not exist yet. */
+ if ((aes->aes_set & AES_SET_MBS) == 0) {
+ const char *p; /* unused */
+ archive_mstring_get_mbs(a, aes, &p); /* ignore errors, we'll handle it later */
+ }
+ /* Try converting MBS to WCS using native locale. */
+ if (aes->aes_set & AES_SET_MBS) {
+ archive_wstring_empty(&(aes->aes_wcs));
+ r = archive_wstring_append_from_mbs(&(aes->aes_wcs),
+ aes->aes_mbs.s, aes->aes_mbs.length);
+ if (r == 0) {
+ aes->aes_set |= AES_SET_WCS;
+ *wp = aes->aes_wcs.s;
+ } else
+ ret = -1;/* failure. */
+ }
+ return (ret);
+}
+
+int
+archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
+ const char **p, size_t *length, struct archive_string_conv *sc)
+{
+ int ret = 0;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ int r;
+
+ /*
+ * Internationalization programming on Windows must use Wide
+ * characters because Windows platform cannot make locale UTF-8.
+ */
+ if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) {
+ archive_string_empty(&(aes->aes_mbs_in_locale));
+ r = archive_string_append_from_wcs_in_codepage(
+ &(aes->aes_mbs_in_locale), aes->aes_wcs.s,
+ aes->aes_wcs.length, sc);
+ if (r == 0) {
+ *p = aes->aes_mbs_in_locale.s;
+ if (length != NULL)
+ *length = aes->aes_mbs_in_locale.length;
+ return (0);
+ } else if (errno == ENOMEM)
+ return (-1);
+ else
+ ret = -1;
+ }
+#endif
+
+ /* If there is not an MBS form but there is a WCS or UTF8 form, try converting
+ * with the native locale to be used for translating it to specified
+ * character-set. */
+ if ((aes->aes_set & AES_SET_MBS) == 0) {
+ const char *pm; /* unused */
+ archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
+ }
+ /* If we already have an MBS form, use it to be translated to
+ * specified character-set. */
+ if (aes->aes_set & AES_SET_MBS) {
+ if (sc == NULL) {
+ /* Conversion is unneeded. */
+ *p = aes->aes_mbs.s;
+ if (length != NULL)
+ *length = aes->aes_mbs.length;
+ return (0);
+ }
+ ret = archive_strncpy_l(&(aes->aes_mbs_in_locale),
+ aes->aes_mbs.s, aes->aes_mbs.length, sc);
+ *p = aes->aes_mbs_in_locale.s;
+ if (length != NULL)
+ *length = aes->aes_mbs_in_locale.length;
+ } else {
+ *p = NULL;
+ if (length != NULL)
+ *length = 0;
+ }
+ return (ret);
+}
+
+int
+archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs)
+{
+ if (mbs == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs)));
+}
+
+int
+archive_mstring_copy_mbs_len(struct archive_mstring *aes, const char *mbs,
+ size_t len)
+{
+ if (mbs == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+ archive_strncpy(&(aes->aes_mbs), mbs, len);
+ archive_string_empty(&(aes->aes_utf8));
+ archive_wstring_empty(&(aes->aes_wcs));
+ return (0);
+}
+
+int
+archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs)
+{
+ return archive_mstring_copy_wcs_len(aes, wcs,
+ wcs == NULL ? 0 : wcslen(wcs));
+}
+
+int
+archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8)
+{
+ if (utf8 == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ aes->aes_set = AES_SET_UTF8;
+ archive_string_empty(&(aes->aes_mbs));
+ archive_string_empty(&(aes->aes_wcs));
+ archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8));
+ return (int)strlen(utf8);
+}
+
+int
+archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs,
+ size_t len)
+{
+ if (wcs == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
+ archive_string_empty(&(aes->aes_mbs));
+ archive_string_empty(&(aes->aes_utf8));
+ archive_wstrncpy(&(aes->aes_wcs), wcs, len);
+ return (0);
+}
+
+int
+archive_mstring_copy_mbs_len_l(struct archive_mstring *aes,
+ const char *mbs, size_t len, struct archive_string_conv *sc)
+{
+ int r;
+
+ if (mbs == NULL) {
+ aes->aes_set = 0;
+ return (0);
+ }
+ archive_string_empty(&(aes->aes_mbs));
+ archive_wstring_empty(&(aes->aes_wcs));
+ archive_string_empty(&(aes->aes_utf8));
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /*
+ * Internationalization programming on Windows must use Wide
+ * characters because Windows platform cannot make locale UTF-8.
+ */
+ if (sc == NULL) {
+ if (archive_string_append(&(aes->aes_mbs),
+ mbs, mbsnbytes(mbs, len)) == NULL) {
+ aes->aes_set = 0;
+ r = -1;
+ } else {
+ aes->aes_set = AES_SET_MBS;
+ r = 0;
+ }
+#if defined(HAVE_ICONV)
+ } else if (sc != NULL && sc->cd_w != (iconv_t)-1) {
+ /*
+ * This case happens only when MultiByteToWideChar() cannot
+ * handle sc->from_cp, and we have to iconv in order to
+ * translate character-set to wchar_t,UTF-16.
+ */
+ iconv_t cd = sc->cd;
+ unsigned from_cp;
+ int flag;
+
+ /*
+ * Translate multi-bytes from some character-set to UTF-8.
+ */
+ sc->cd = sc->cd_w;
+ r = archive_strncpy_l(&(aes->aes_utf8), mbs, len, sc);
+ sc->cd = cd;
+ if (r != 0) {
+ aes->aes_set = 0;
+ return (r);
+ }
+ aes->aes_set = AES_SET_UTF8;
+
+ /*
+ * Append the UTF-8 string into wstring.
+ */
+ flag = sc->flag;
+ sc->flag &= ~(SCONV_NORMALIZATION_C
+ | SCONV_TO_UTF16| SCONV_FROM_UTF16);
+ from_cp = sc->from_cp;
+ sc->from_cp = CP_UTF8;
+ r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs),
+ aes->aes_utf8.s, aes->aes_utf8.length, sc);
+ sc->flag = flag;
+ sc->from_cp = from_cp;
+ if (r == 0)
+ aes->aes_set |= AES_SET_WCS;
+#endif
+ } else {
+ r = archive_wstring_append_from_mbs_in_codepage(
+ &(aes->aes_wcs), mbs, len, sc);
+ if (r == 0)
+ aes->aes_set = AES_SET_WCS;
+ else
+ aes->aes_set = 0;
+ }
+#else
+ r = archive_strncpy_l(&(aes->aes_mbs), mbs, len, sc);
+ if (r == 0)
+ aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+ else
+ aes->aes_set = 0;
+#endif
+ return (r);
+}
+
+/*
+ * The 'update' form tries to proactively update all forms of
+ * this string (WCS and MBS) and returns an error if any of
+ * them fail. This is used by the 'pax' handler, for instance,
+ * to detect and report character-conversion failures early while
+ * still allowing clients to get potentially useful values from
+ * the more tolerant lazy conversions. (get_mbs and get_wcs will
+ * strive to give the user something useful, so you can get hopefully
+ * usable values even if some of the character conversions are failing.)
+ */
+int
+archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes,
+ const char *utf8)
+{
+ struct archive_string_conv *sc;
+ int r;
+
+ if (utf8 == NULL) {
+ aes->aes_set = 0;
+ return (0); /* Succeeded in clearing everything. */
+ }
+
+ /* Save the UTF8 string. */
+ archive_strcpy(&(aes->aes_utf8), utf8);
+
+ /* Empty the mbs and wcs strings. */
+ archive_string_empty(&(aes->aes_mbs));
+ archive_wstring_empty(&(aes->aes_wcs));
+
+ aes->aes_set = AES_SET_UTF8; /* Only UTF8 is set now. */
+
+ /* Try converting UTF-8 to MBS, return false on failure. */
+ sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+ if (sc == NULL)
+ return (-1);/* Couldn't allocate memory for sc. */
+ r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc);
+ if (a == NULL)
+ free_sconv_object(sc);
+ if (r != 0)
+ return (-1);
+ aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */
+
+ /* Try converting MBS to WCS, return false on failure. */
+ if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s,
+ aes->aes_mbs.length))
+ return (-1);
+ aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
+
+ /* All conversions succeeded. */
+ return (0);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_string.h b/contrib/libs/libarchive/libarchive/archive_string.h
new file mode 100644
index 0000000000..49d7d3064a
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_string.h
@@ -0,0 +1,243 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_string.h 201092 2009-12-28 02:26:06Z kientzle $
+ *
+ */
+
+#ifndef ARCHIVE_STRING_H_INCLUDED
+#define ARCHIVE_STRING_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h> /* required for wchar_t on some systems */
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+
+/*
+ * Basic resizable/reusable string support similar to Java's "StringBuffer."
+ *
+ * Unlike sbuf(9), the buffers here are fully reusable and track the
+ * length throughout.
+ */
+
+struct archive_string {
+ char *s; /* Pointer to the storage */
+ size_t length; /* Length of 's' in characters */
+ size_t buffer_length; /* Length of malloc-ed storage in bytes. */
+};
+
+struct archive_wstring {
+ wchar_t *s; /* Pointer to the storage */
+ size_t length; /* Length of 's' in characters */
+ size_t buffer_length; /* Length of malloc-ed storage in bytes. */
+};
+
+struct archive_string_conv;
+
+/* Initialize an archive_string object on the stack or elsewhere. */
+#define archive_string_init(a) \
+ do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0)
+
+/* Append a C char to an archive_string, resizing as necessary. */
+struct archive_string *
+archive_strappend_char(struct archive_string *, char);
+
+/* Ditto for a wchar_t and an archive_wstring. */
+struct archive_wstring *
+archive_wstrappend_wchar(struct archive_wstring *, wchar_t);
+
+/* Append a raw array to an archive_string, resizing as necessary */
+struct archive_string *
+archive_array_append(struct archive_string *, const char *, size_t);
+
+/* Convert a Unicode string to current locale and append the result. */
+/* Returns -1 if conversion fails. */
+int
+archive_string_append_from_wcs(struct archive_string *, const wchar_t *, size_t);
+
+
+/* Create a string conversion object.
+ * Return NULL and set a error message if the conversion is not supported
+ * on the platform. */
+struct archive_string_conv *
+archive_string_conversion_to_charset(struct archive *, const char *, int);
+struct archive_string_conv *
+archive_string_conversion_from_charset(struct archive *, const char *, int);
+/* Create the default string conversion object for reading/writing an archive.
+ * Return NULL if the conversion is unneeded.
+ * Note: On non Windows platform this always returns NULL.
+ */
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *);
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *);
+/* Dispose of a string conversion object. */
+void
+archive_string_conversion_free(struct archive *);
+const char *
+archive_string_conversion_charset_name(struct archive_string_conv *);
+void
+archive_string_conversion_set_opt(struct archive_string_conv *, int);
+#define SCONV_SET_OPT_UTF8_LIBARCHIVE2X 1
+#define SCONV_SET_OPT_NORMALIZATION_C 2
+#define SCONV_SET_OPT_NORMALIZATION_D 4
+
+
+/* Copy one archive_string to another in locale conversion.
+ * Return -1 if conversion fails. */
+int
+archive_strncpy_l(struct archive_string *, const void *, size_t,
+ struct archive_string_conv *);
+
+/* Copy one archive_string to another in locale conversion.
+ * Return -1 if conversion fails. */
+int
+archive_strncat_l(struct archive_string *, const void *, size_t,
+ struct archive_string_conv *);
+
+
+/* Copy one archive_string to another */
+#define archive_string_copy(dest, src) \
+ ((dest)->length = 0, archive_string_concat((dest), (src)))
+#define archive_wstring_copy(dest, src) \
+ ((dest)->length = 0, archive_wstring_concat((dest), (src)))
+
+/* Concatenate one archive_string to another */
+void archive_string_concat(struct archive_string *dest, struct archive_string *src);
+void archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src);
+
+/* Ensure that the underlying buffer is at least as large as the request. */
+struct archive_string *
+archive_string_ensure(struct archive_string *, size_t);
+struct archive_wstring *
+archive_wstring_ensure(struct archive_wstring *, size_t);
+
+/* Append C string, which may lack trailing \0. */
+/* The source is declared void * here because this gets used with
+ * "signed char *", "unsigned char *" and "char *" arguments.
+ * Declaring it "char *" as with some of the other functions just
+ * leads to a lot of extra casts. */
+struct archive_string *
+archive_strncat(struct archive_string *, const void *, size_t);
+struct archive_wstring *
+archive_wstrncat(struct archive_wstring *, const wchar_t *, size_t);
+
+/* Append a C string to an archive_string, resizing as necessary. */
+struct archive_string *
+archive_strcat(struct archive_string *, const void *);
+struct archive_wstring *
+archive_wstrcat(struct archive_wstring *, const wchar_t *);
+
+/* Copy a C string to an archive_string, resizing as necessary. */
+#define archive_strcpy(as,p) \
+ archive_strncpy((as), (p), ((p) == NULL ? 0 : strlen(p)))
+#define archive_wstrcpy(as,p) \
+ archive_wstrncpy((as), (p), ((p) == NULL ? 0 : wcslen(p)))
+#define archive_strcpy_l(as,p,lo) \
+ archive_strncpy_l((as), (p), ((p) == NULL ? 0 : strlen(p)), (lo))
+
+/* Copy a C string to an archive_string with limit, resizing as necessary. */
+#define archive_strncpy(as,p,l) \
+ ((as)->length=0, archive_strncat((as), (p), (l)))
+#define archive_wstrncpy(as,p,l) \
+ ((as)->length = 0, archive_wstrncat((as), (p), (l)))
+
+/* Return length of string. */
+#define archive_strlen(a) ((a)->length)
+
+/* Set string length to zero. */
+#define archive_string_empty(a) ((a)->length = 0)
+#define archive_wstring_empty(a) ((a)->length = 0)
+
+/* Release any allocated storage resources. */
+void archive_string_free(struct archive_string *);
+void archive_wstring_free(struct archive_wstring *);
+
+/* Like 'vsprintf', but resizes the underlying string as necessary. */
+/* Note: This only implements a small subset of standard printf functionality. */
+void archive_string_vsprintf(struct archive_string *, const char *,
+ va_list) __LA_PRINTF(2, 0);
+void archive_string_sprintf(struct archive_string *, const char *, ...)
+ __LA_PRINTF(2, 3);
+
+/* Translates from MBS to Unicode. */
+/* Returns non-zero if conversion failed in any way. */
+int archive_wstring_append_from_mbs(struct archive_wstring *dest,
+ const char *, size_t);
+
+
+/* A "multistring" can hold Unicode, UTF8, or MBS versions of
+ * the string. If you set and read the same version, no translation
+ * is done. If you set and read different versions, the library
+ * will attempt to transparently convert.
+ */
+struct archive_mstring {
+ struct archive_string aes_mbs;
+ struct archive_string aes_utf8;
+ struct archive_wstring aes_wcs;
+ struct archive_string aes_mbs_in_locale;
+ /* Bitmap of which of the above are valid. Because we're lazy
+ * about malloc-ing and reusing the underlying storage, we
+ * can't rely on NULL pointers to indicate whether a string
+ * has been set. */
+ int aes_set;
+#define AES_SET_MBS 1
+#define AES_SET_UTF8 2
+#define AES_SET_WCS 4
+};
+
+void archive_mstring_clean(struct archive_mstring *);
+void archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src);
+int archive_mstring_get_mbs(struct archive *, struct archive_mstring *, const char **);
+int archive_mstring_get_utf8(struct archive *, struct archive_mstring *, const char **);
+int archive_mstring_get_wcs(struct archive *, struct archive_mstring *, const wchar_t **);
+int archive_mstring_get_mbs_l(struct archive *, struct archive_mstring *, const char **,
+ size_t *, struct archive_string_conv *);
+int archive_mstring_copy_mbs(struct archive_mstring *, const char *mbs);
+int archive_mstring_copy_mbs_len(struct archive_mstring *, const char *mbs,
+ size_t);
+int archive_mstring_copy_utf8(struct archive_mstring *, const char *utf8);
+int archive_mstring_copy_wcs(struct archive_mstring *, const wchar_t *wcs);
+int archive_mstring_copy_wcs_len(struct archive_mstring *,
+ const wchar_t *wcs, size_t);
+int archive_mstring_copy_mbs_len_l(struct archive_mstring *,
+ const char *mbs, size_t, struct archive_string_conv *);
+int archive_mstring_update_utf8(struct archive *, struct archive_mstring *aes, const char *utf8);
+
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_string_composition.h b/contrib/libs/libarchive/libarchive/archive_string_composition.h
new file mode 100644
index 0000000000..d0ac340961
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_string_composition.h
@@ -0,0 +1,2292 @@
+/*-
+ * Copyright (c) 2011-2012 libarchive Project
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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(S) 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * ATTENTION!
+ * This file is generated by build/utils/gen_archive_string_composition_h.sh
+ * from http://unicode.org/Public/6.0.0/ucd/UnicodeData.txt
+ *
+ * See also http://unicode.org/report/tr15/
+ */
+
+#ifndef ARCHIVE_STRING_COMPOSITION_H_INCLUDED
+#define ARCHIVE_STRING_COMPOSITION_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+struct unicode_composition_table {
+ uint32_t cp1;
+ uint32_t cp2;
+ uint32_t nfc;
+};
+
+static const struct unicode_composition_table u_composition_table[] = {
+ { 0x0003C , 0x00338 , 0x0226E },
+ { 0x0003D , 0x00338 , 0x02260 },
+ { 0x0003E , 0x00338 , 0x0226F },
+ { 0x00041 , 0x00300 , 0x000C0 },
+ { 0x00041 , 0x00301 , 0x000C1 },
+ { 0x00041 , 0x00302 , 0x000C2 },
+ { 0x00041 , 0x00303 , 0x000C3 },
+ { 0x00041 , 0x00304 , 0x00100 },
+ { 0x00041 , 0x00306 , 0x00102 },
+ { 0x00041 , 0x00307 , 0x00226 },
+ { 0x00041 , 0x00308 , 0x000C4 },
+ { 0x00041 , 0x00309 , 0x01EA2 },
+ { 0x00041 , 0x0030A , 0x000C5 },
+ { 0x00041 , 0x0030C , 0x001CD },
+ { 0x00041 , 0x0030F , 0x00200 },
+ { 0x00041 , 0x00311 , 0x00202 },
+ { 0x00041 , 0x00323 , 0x01EA0 },
+ { 0x00041 , 0x00325 , 0x01E00 },
+ { 0x00041 , 0x00328 , 0x00104 },
+ { 0x00042 , 0x00307 , 0x01E02 },
+ { 0x00042 , 0x00323 , 0x01E04 },
+ { 0x00042 , 0x00331 , 0x01E06 },
+ { 0x00043 , 0x00301 , 0x00106 },
+ { 0x00043 , 0x00302 , 0x00108 },
+ { 0x00043 , 0x00307 , 0x0010A },
+ { 0x00043 , 0x0030C , 0x0010C },
+ { 0x00043 , 0x00327 , 0x000C7 },
+ { 0x00044 , 0x00307 , 0x01E0A },
+ { 0x00044 , 0x0030C , 0x0010E },
+ { 0x00044 , 0x00323 , 0x01E0C },
+ { 0x00044 , 0x00327 , 0x01E10 },
+ { 0x00044 , 0x0032D , 0x01E12 },
+ { 0x00044 , 0x00331 , 0x01E0E },
+ { 0x00045 , 0x00300 , 0x000C8 },
+ { 0x00045 , 0x00301 , 0x000C9 },
+ { 0x00045 , 0x00302 , 0x000CA },
+ { 0x00045 , 0x00303 , 0x01EBC },
+ { 0x00045 , 0x00304 , 0x00112 },
+ { 0x00045 , 0x00306 , 0x00114 },
+ { 0x00045 , 0x00307 , 0x00116 },
+ { 0x00045 , 0x00308 , 0x000CB },
+ { 0x00045 , 0x00309 , 0x01EBA },
+ { 0x00045 , 0x0030C , 0x0011A },
+ { 0x00045 , 0x0030F , 0x00204 },
+ { 0x00045 , 0x00311 , 0x00206 },
+ { 0x00045 , 0x00323 , 0x01EB8 },
+ { 0x00045 , 0x00327 , 0x00228 },
+ { 0x00045 , 0x00328 , 0x00118 },
+ { 0x00045 , 0x0032D , 0x01E18 },
+ { 0x00045 , 0x00330 , 0x01E1A },
+ { 0x00046 , 0x00307 , 0x01E1E },
+ { 0x00047 , 0x00301 , 0x001F4 },
+ { 0x00047 , 0x00302 , 0x0011C },
+ { 0x00047 , 0x00304 , 0x01E20 },
+ { 0x00047 , 0x00306 , 0x0011E },
+ { 0x00047 , 0x00307 , 0x00120 },
+ { 0x00047 , 0x0030C , 0x001E6 },
+ { 0x00047 , 0x00327 , 0x00122 },
+ { 0x00048 , 0x00302 , 0x00124 },
+ { 0x00048 , 0x00307 , 0x01E22 },
+ { 0x00048 , 0x00308 , 0x01E26 },
+ { 0x00048 , 0x0030C , 0x0021E },
+ { 0x00048 , 0x00323 , 0x01E24 },
+ { 0x00048 , 0x00327 , 0x01E28 },
+ { 0x00048 , 0x0032E , 0x01E2A },
+ { 0x00049 , 0x00300 , 0x000CC },
+ { 0x00049 , 0x00301 , 0x000CD },
+ { 0x00049 , 0x00302 , 0x000CE },
+ { 0x00049 , 0x00303 , 0x00128 },
+ { 0x00049 , 0x00304 , 0x0012A },
+ { 0x00049 , 0x00306 , 0x0012C },
+ { 0x00049 , 0x00307 , 0x00130 },
+ { 0x00049 , 0x00308 , 0x000CF },
+ { 0x00049 , 0x00309 , 0x01EC8 },
+ { 0x00049 , 0x0030C , 0x001CF },
+ { 0x00049 , 0x0030F , 0x00208 },
+ { 0x00049 , 0x00311 , 0x0020A },
+ { 0x00049 , 0x00323 , 0x01ECA },
+ { 0x00049 , 0x00328 , 0x0012E },
+ { 0x00049 , 0x00330 , 0x01E2C },
+ { 0x0004A , 0x00302 , 0x00134 },
+ { 0x0004B , 0x00301 , 0x01E30 },
+ { 0x0004B , 0x0030C , 0x001E8 },
+ { 0x0004B , 0x00323 , 0x01E32 },
+ { 0x0004B , 0x00327 , 0x00136 },
+ { 0x0004B , 0x00331 , 0x01E34 },
+ { 0x0004C , 0x00301 , 0x00139 },
+ { 0x0004C , 0x0030C , 0x0013D },
+ { 0x0004C , 0x00323 , 0x01E36 },
+ { 0x0004C , 0x00327 , 0x0013B },
+ { 0x0004C , 0x0032D , 0x01E3C },
+ { 0x0004C , 0x00331 , 0x01E3A },
+ { 0x0004D , 0x00301 , 0x01E3E },
+ { 0x0004D , 0x00307 , 0x01E40 },
+ { 0x0004D , 0x00323 , 0x01E42 },
+ { 0x0004E , 0x00300 , 0x001F8 },
+ { 0x0004E , 0x00301 , 0x00143 },
+ { 0x0004E , 0x00303 , 0x000D1 },
+ { 0x0004E , 0x00307 , 0x01E44 },
+ { 0x0004E , 0x0030C , 0x00147 },
+ { 0x0004E , 0x00323 , 0x01E46 },
+ { 0x0004E , 0x00327 , 0x00145 },
+ { 0x0004E , 0x0032D , 0x01E4A },
+ { 0x0004E , 0x00331 , 0x01E48 },
+ { 0x0004F , 0x00300 , 0x000D2 },
+ { 0x0004F , 0x00301 , 0x000D3 },
+ { 0x0004F , 0x00302 , 0x000D4 },
+ { 0x0004F , 0x00303 , 0x000D5 },
+ { 0x0004F , 0x00304 , 0x0014C },
+ { 0x0004F , 0x00306 , 0x0014E },
+ { 0x0004F , 0x00307 , 0x0022E },
+ { 0x0004F , 0x00308 , 0x000D6 },
+ { 0x0004F , 0x00309 , 0x01ECE },
+ { 0x0004F , 0x0030B , 0x00150 },
+ { 0x0004F , 0x0030C , 0x001D1 },
+ { 0x0004F , 0x0030F , 0x0020C },
+ { 0x0004F , 0x00311 , 0x0020E },
+ { 0x0004F , 0x0031B , 0x001A0 },
+ { 0x0004F , 0x00323 , 0x01ECC },
+ { 0x0004F , 0x00328 , 0x001EA },
+ { 0x00050 , 0x00301 , 0x01E54 },
+ { 0x00050 , 0x00307 , 0x01E56 },
+ { 0x00052 , 0x00301 , 0x00154 },
+ { 0x00052 , 0x00307 , 0x01E58 },
+ { 0x00052 , 0x0030C , 0x00158 },
+ { 0x00052 , 0x0030F , 0x00210 },
+ { 0x00052 , 0x00311 , 0x00212 },
+ { 0x00052 , 0x00323 , 0x01E5A },
+ { 0x00052 , 0x00327 , 0x00156 },
+ { 0x00052 , 0x00331 , 0x01E5E },
+ { 0x00053 , 0x00301 , 0x0015A },
+ { 0x00053 , 0x00302 , 0x0015C },
+ { 0x00053 , 0x00307 , 0x01E60 },
+ { 0x00053 , 0x0030C , 0x00160 },
+ { 0x00053 , 0x00323 , 0x01E62 },
+ { 0x00053 , 0x00326 , 0x00218 },
+ { 0x00053 , 0x00327 , 0x0015E },
+ { 0x00054 , 0x00307 , 0x01E6A },
+ { 0x00054 , 0x0030C , 0x00164 },
+ { 0x00054 , 0x00323 , 0x01E6C },
+ { 0x00054 , 0x00326 , 0x0021A },
+ { 0x00054 , 0x00327 , 0x00162 },
+ { 0x00054 , 0x0032D , 0x01E70 },
+ { 0x00054 , 0x00331 , 0x01E6E },
+ { 0x00055 , 0x00300 , 0x000D9 },
+ { 0x00055 , 0x00301 , 0x000DA },
+ { 0x00055 , 0x00302 , 0x000DB },
+ { 0x00055 , 0x00303 , 0x00168 },
+ { 0x00055 , 0x00304 , 0x0016A },
+ { 0x00055 , 0x00306 , 0x0016C },
+ { 0x00055 , 0x00308 , 0x000DC },
+ { 0x00055 , 0x00309 , 0x01EE6 },
+ { 0x00055 , 0x0030A , 0x0016E },
+ { 0x00055 , 0x0030B , 0x00170 },
+ { 0x00055 , 0x0030C , 0x001D3 },
+ { 0x00055 , 0x0030F , 0x00214 },
+ { 0x00055 , 0x00311 , 0x00216 },
+ { 0x00055 , 0x0031B , 0x001AF },
+ { 0x00055 , 0x00323 , 0x01EE4 },
+ { 0x00055 , 0x00324 , 0x01E72 },
+ { 0x00055 , 0x00328 , 0x00172 },
+ { 0x00055 , 0x0032D , 0x01E76 },
+ { 0x00055 , 0x00330 , 0x01E74 },
+ { 0x00056 , 0x00303 , 0x01E7C },
+ { 0x00056 , 0x00323 , 0x01E7E },
+ { 0x00057 , 0x00300 , 0x01E80 },
+ { 0x00057 , 0x00301 , 0x01E82 },
+ { 0x00057 , 0x00302 , 0x00174 },
+ { 0x00057 , 0x00307 , 0x01E86 },
+ { 0x00057 , 0x00308 , 0x01E84 },
+ { 0x00057 , 0x00323 , 0x01E88 },
+ { 0x00058 , 0x00307 , 0x01E8A },
+ { 0x00058 , 0x00308 , 0x01E8C },
+ { 0x00059 , 0x00300 , 0x01EF2 },
+ { 0x00059 , 0x00301 , 0x000DD },
+ { 0x00059 , 0x00302 , 0x00176 },
+ { 0x00059 , 0x00303 , 0x01EF8 },
+ { 0x00059 , 0x00304 , 0x00232 },
+ { 0x00059 , 0x00307 , 0x01E8E },
+ { 0x00059 , 0x00308 , 0x00178 },
+ { 0x00059 , 0x00309 , 0x01EF6 },
+ { 0x00059 , 0x00323 , 0x01EF4 },
+ { 0x0005A , 0x00301 , 0x00179 },
+ { 0x0005A , 0x00302 , 0x01E90 },
+ { 0x0005A , 0x00307 , 0x0017B },
+ { 0x0005A , 0x0030C , 0x0017D },
+ { 0x0005A , 0x00323 , 0x01E92 },
+ { 0x0005A , 0x00331 , 0x01E94 },
+ { 0x00061 , 0x00300 , 0x000E0 },
+ { 0x00061 , 0x00301 , 0x000E1 },
+ { 0x00061 , 0x00302 , 0x000E2 },
+ { 0x00061 , 0x00303 , 0x000E3 },
+ { 0x00061 , 0x00304 , 0x00101 },
+ { 0x00061 , 0x00306 , 0x00103 },
+ { 0x00061 , 0x00307 , 0x00227 },
+ { 0x00061 , 0x00308 , 0x000E4 },
+ { 0x00061 , 0x00309 , 0x01EA3 },
+ { 0x00061 , 0x0030A , 0x000E5 },
+ { 0x00061 , 0x0030C , 0x001CE },
+ { 0x00061 , 0x0030F , 0x00201 },
+ { 0x00061 , 0x00311 , 0x00203 },
+ { 0x00061 , 0x00323 , 0x01EA1 },
+ { 0x00061 , 0x00325 , 0x01E01 },
+ { 0x00061 , 0x00328 , 0x00105 },
+ { 0x00062 , 0x00307 , 0x01E03 },
+ { 0x00062 , 0x00323 , 0x01E05 },
+ { 0x00062 , 0x00331 , 0x01E07 },
+ { 0x00063 , 0x00301 , 0x00107 },
+ { 0x00063 , 0x00302 , 0x00109 },
+ { 0x00063 , 0x00307 , 0x0010B },
+ { 0x00063 , 0x0030C , 0x0010D },
+ { 0x00063 , 0x00327 , 0x000E7 },
+ { 0x00064 , 0x00307 , 0x01E0B },
+ { 0x00064 , 0x0030C , 0x0010F },
+ { 0x00064 , 0x00323 , 0x01E0D },
+ { 0x00064 , 0x00327 , 0x01E11 },
+ { 0x00064 , 0x0032D , 0x01E13 },
+ { 0x00064 , 0x00331 , 0x01E0F },
+ { 0x00065 , 0x00300 , 0x000E8 },
+ { 0x00065 , 0x00301 , 0x000E9 },
+ { 0x00065 , 0x00302 , 0x000EA },
+ { 0x00065 , 0x00303 , 0x01EBD },
+ { 0x00065 , 0x00304 , 0x00113 },
+ { 0x00065 , 0x00306 , 0x00115 },
+ { 0x00065 , 0x00307 , 0x00117 },
+ { 0x00065 , 0x00308 , 0x000EB },
+ { 0x00065 , 0x00309 , 0x01EBB },
+ { 0x00065 , 0x0030C , 0x0011B },
+ { 0x00065 , 0x0030F , 0x00205 },
+ { 0x00065 , 0x00311 , 0x00207 },
+ { 0x00065 , 0x00323 , 0x01EB9 },
+ { 0x00065 , 0x00327 , 0x00229 },
+ { 0x00065 , 0x00328 , 0x00119 },
+ { 0x00065 , 0x0032D , 0x01E19 },
+ { 0x00065 , 0x00330 , 0x01E1B },
+ { 0x00066 , 0x00307 , 0x01E1F },
+ { 0x00067 , 0x00301 , 0x001F5 },
+ { 0x00067 , 0x00302 , 0x0011D },
+ { 0x00067 , 0x00304 , 0x01E21 },
+ { 0x00067 , 0x00306 , 0x0011F },
+ { 0x00067 , 0x00307 , 0x00121 },
+ { 0x00067 , 0x0030C , 0x001E7 },
+ { 0x00067 , 0x00327 , 0x00123 },
+ { 0x00068 , 0x00302 , 0x00125 },
+ { 0x00068 , 0x00307 , 0x01E23 },
+ { 0x00068 , 0x00308 , 0x01E27 },
+ { 0x00068 , 0x0030C , 0x0021F },
+ { 0x00068 , 0x00323 , 0x01E25 },
+ { 0x00068 , 0x00327 , 0x01E29 },
+ { 0x00068 , 0x0032E , 0x01E2B },
+ { 0x00068 , 0x00331 , 0x01E96 },
+ { 0x00069 , 0x00300 , 0x000EC },
+ { 0x00069 , 0x00301 , 0x000ED },
+ { 0x00069 , 0x00302 , 0x000EE },
+ { 0x00069 , 0x00303 , 0x00129 },
+ { 0x00069 , 0x00304 , 0x0012B },
+ { 0x00069 , 0x00306 , 0x0012D },
+ { 0x00069 , 0x00308 , 0x000EF },
+ { 0x00069 , 0x00309 , 0x01EC9 },
+ { 0x00069 , 0x0030C , 0x001D0 },
+ { 0x00069 , 0x0030F , 0x00209 },
+ { 0x00069 , 0x00311 , 0x0020B },
+ { 0x00069 , 0x00323 , 0x01ECB },
+ { 0x00069 , 0x00328 , 0x0012F },
+ { 0x00069 , 0x00330 , 0x01E2D },
+ { 0x0006A , 0x00302 , 0x00135 },
+ { 0x0006A , 0x0030C , 0x001F0 },
+ { 0x0006B , 0x00301 , 0x01E31 },
+ { 0x0006B , 0x0030C , 0x001E9 },
+ { 0x0006B , 0x00323 , 0x01E33 },
+ { 0x0006B , 0x00327 , 0x00137 },
+ { 0x0006B , 0x00331 , 0x01E35 },
+ { 0x0006C , 0x00301 , 0x0013A },
+ { 0x0006C , 0x0030C , 0x0013E },
+ { 0x0006C , 0x00323 , 0x01E37 },
+ { 0x0006C , 0x00327 , 0x0013C },
+ { 0x0006C , 0x0032D , 0x01E3D },
+ { 0x0006C , 0x00331 , 0x01E3B },
+ { 0x0006D , 0x00301 , 0x01E3F },
+ { 0x0006D , 0x00307 , 0x01E41 },
+ { 0x0006D , 0x00323 , 0x01E43 },
+ { 0x0006E , 0x00300 , 0x001F9 },
+ { 0x0006E , 0x00301 , 0x00144 },
+ { 0x0006E , 0x00303 , 0x000F1 },
+ { 0x0006E , 0x00307 , 0x01E45 },
+ { 0x0006E , 0x0030C , 0x00148 },
+ { 0x0006E , 0x00323 , 0x01E47 },
+ { 0x0006E , 0x00327 , 0x00146 },
+ { 0x0006E , 0x0032D , 0x01E4B },
+ { 0x0006E , 0x00331 , 0x01E49 },
+ { 0x0006F , 0x00300 , 0x000F2 },
+ { 0x0006F , 0x00301 , 0x000F3 },
+ { 0x0006F , 0x00302 , 0x000F4 },
+ { 0x0006F , 0x00303 , 0x000F5 },
+ { 0x0006F , 0x00304 , 0x0014D },
+ { 0x0006F , 0x00306 , 0x0014F },
+ { 0x0006F , 0x00307 , 0x0022F },
+ { 0x0006F , 0x00308 , 0x000F6 },
+ { 0x0006F , 0x00309 , 0x01ECF },
+ { 0x0006F , 0x0030B , 0x00151 },
+ { 0x0006F , 0x0030C , 0x001D2 },
+ { 0x0006F , 0x0030F , 0x0020D },
+ { 0x0006F , 0x00311 , 0x0020F },
+ { 0x0006F , 0x0031B , 0x001A1 },
+ { 0x0006F , 0x00323 , 0x01ECD },
+ { 0x0006F , 0x00328 , 0x001EB },
+ { 0x00070 , 0x00301 , 0x01E55 },
+ { 0x00070 , 0x00307 , 0x01E57 },
+ { 0x00072 , 0x00301 , 0x00155 },
+ { 0x00072 , 0x00307 , 0x01E59 },
+ { 0x00072 , 0x0030C , 0x00159 },
+ { 0x00072 , 0x0030F , 0x00211 },
+ { 0x00072 , 0x00311 , 0x00213 },
+ { 0x00072 , 0x00323 , 0x01E5B },
+ { 0x00072 , 0x00327 , 0x00157 },
+ { 0x00072 , 0x00331 , 0x01E5F },
+ { 0x00073 , 0x00301 , 0x0015B },
+ { 0x00073 , 0x00302 , 0x0015D },
+ { 0x00073 , 0x00307 , 0x01E61 },
+ { 0x00073 , 0x0030C , 0x00161 },
+ { 0x00073 , 0x00323 , 0x01E63 },
+ { 0x00073 , 0x00326 , 0x00219 },
+ { 0x00073 , 0x00327 , 0x0015F },
+ { 0x00074 , 0x00307 , 0x01E6B },
+ { 0x00074 , 0x00308 , 0x01E97 },
+ { 0x00074 , 0x0030C , 0x00165 },
+ { 0x00074 , 0x00323 , 0x01E6D },
+ { 0x00074 , 0x00326 , 0x0021B },
+ { 0x00074 , 0x00327 , 0x00163 },
+ { 0x00074 , 0x0032D , 0x01E71 },
+ { 0x00074 , 0x00331 , 0x01E6F },
+ { 0x00075 , 0x00300 , 0x000F9 },
+ { 0x00075 , 0x00301 , 0x000FA },
+ { 0x00075 , 0x00302 , 0x000FB },
+ { 0x00075 , 0x00303 , 0x00169 },
+ { 0x00075 , 0x00304 , 0x0016B },
+ { 0x00075 , 0x00306 , 0x0016D },
+ { 0x00075 , 0x00308 , 0x000FC },
+ { 0x00075 , 0x00309 , 0x01EE7 },
+ { 0x00075 , 0x0030A , 0x0016F },
+ { 0x00075 , 0x0030B , 0x00171 },
+ { 0x00075 , 0x0030C , 0x001D4 },
+ { 0x00075 , 0x0030F , 0x00215 },
+ { 0x00075 , 0x00311 , 0x00217 },
+ { 0x00075 , 0x0031B , 0x001B0 },
+ { 0x00075 , 0x00323 , 0x01EE5 },
+ { 0x00075 , 0x00324 , 0x01E73 },
+ { 0x00075 , 0x00328 , 0x00173 },
+ { 0x00075 , 0x0032D , 0x01E77 },
+ { 0x00075 , 0x00330 , 0x01E75 },
+ { 0x00076 , 0x00303 , 0x01E7D },
+ { 0x00076 , 0x00323 , 0x01E7F },
+ { 0x00077 , 0x00300 , 0x01E81 },
+ { 0x00077 , 0x00301 , 0x01E83 },
+ { 0x00077 , 0x00302 , 0x00175 },
+ { 0x00077 , 0x00307 , 0x01E87 },
+ { 0x00077 , 0x00308 , 0x01E85 },
+ { 0x00077 , 0x0030A , 0x01E98 },
+ { 0x00077 , 0x00323 , 0x01E89 },
+ { 0x00078 , 0x00307 , 0x01E8B },
+ { 0x00078 , 0x00308 , 0x01E8D },
+ { 0x00079 , 0x00300 , 0x01EF3 },
+ { 0x00079 , 0x00301 , 0x000FD },
+ { 0x00079 , 0x00302 , 0x00177 },
+ { 0x00079 , 0x00303 , 0x01EF9 },
+ { 0x00079 , 0x00304 , 0x00233 },
+ { 0x00079 , 0x00307 , 0x01E8F },
+ { 0x00079 , 0x00308 , 0x000FF },
+ { 0x00079 , 0x00309 , 0x01EF7 },
+ { 0x00079 , 0x0030A , 0x01E99 },
+ { 0x00079 , 0x00323 , 0x01EF5 },
+ { 0x0007A , 0x00301 , 0x0017A },
+ { 0x0007A , 0x00302 , 0x01E91 },
+ { 0x0007A , 0x00307 , 0x0017C },
+ { 0x0007A , 0x0030C , 0x0017E },
+ { 0x0007A , 0x00323 , 0x01E93 },
+ { 0x0007A , 0x00331 , 0x01E95 },
+ { 0x000A8 , 0x00300 , 0x01FED },
+ { 0x000A8 , 0x00301 , 0x00385 },
+ { 0x000A8 , 0x00342 , 0x01FC1 },
+ { 0x000C2 , 0x00300 , 0x01EA6 },
+ { 0x000C2 , 0x00301 , 0x01EA4 },
+ { 0x000C2 , 0x00303 , 0x01EAA },
+ { 0x000C2 , 0x00309 , 0x01EA8 },
+ { 0x000C4 , 0x00304 , 0x001DE },
+ { 0x000C5 , 0x00301 , 0x001FA },
+ { 0x000C6 , 0x00301 , 0x001FC },
+ { 0x000C6 , 0x00304 , 0x001E2 },
+ { 0x000C7 , 0x00301 , 0x01E08 },
+ { 0x000CA , 0x00300 , 0x01EC0 },
+ { 0x000CA , 0x00301 , 0x01EBE },
+ { 0x000CA , 0x00303 , 0x01EC4 },
+ { 0x000CA , 0x00309 , 0x01EC2 },
+ { 0x000CF , 0x00301 , 0x01E2E },
+ { 0x000D4 , 0x00300 , 0x01ED2 },
+ { 0x000D4 , 0x00301 , 0x01ED0 },
+ { 0x000D4 , 0x00303 , 0x01ED6 },
+ { 0x000D4 , 0x00309 , 0x01ED4 },
+ { 0x000D5 , 0x00301 , 0x01E4C },
+ { 0x000D5 , 0x00304 , 0x0022C },
+ { 0x000D5 , 0x00308 , 0x01E4E },
+ { 0x000D6 , 0x00304 , 0x0022A },
+ { 0x000D8 , 0x00301 , 0x001FE },
+ { 0x000DC , 0x00300 , 0x001DB },
+ { 0x000DC , 0x00301 , 0x001D7 },
+ { 0x000DC , 0x00304 , 0x001D5 },
+ { 0x000DC , 0x0030C , 0x001D9 },
+ { 0x000E2 , 0x00300 , 0x01EA7 },
+ { 0x000E2 , 0x00301 , 0x01EA5 },
+ { 0x000E2 , 0x00303 , 0x01EAB },
+ { 0x000E2 , 0x00309 , 0x01EA9 },
+ { 0x000E4 , 0x00304 , 0x001DF },
+ { 0x000E5 , 0x00301 , 0x001FB },
+ { 0x000E6 , 0x00301 , 0x001FD },
+ { 0x000E6 , 0x00304 , 0x001E3 },
+ { 0x000E7 , 0x00301 , 0x01E09 },
+ { 0x000EA , 0x00300 , 0x01EC1 },
+ { 0x000EA , 0x00301 , 0x01EBF },
+ { 0x000EA , 0x00303 , 0x01EC5 },
+ { 0x000EA , 0x00309 , 0x01EC3 },
+ { 0x000EF , 0x00301 , 0x01E2F },
+ { 0x000F4 , 0x00300 , 0x01ED3 },
+ { 0x000F4 , 0x00301 , 0x01ED1 },
+ { 0x000F4 , 0x00303 , 0x01ED7 },
+ { 0x000F4 , 0x00309 , 0x01ED5 },
+ { 0x000F5 , 0x00301 , 0x01E4D },
+ { 0x000F5 , 0x00304 , 0x0022D },
+ { 0x000F5 , 0x00308 , 0x01E4F },
+ { 0x000F6 , 0x00304 , 0x0022B },
+ { 0x000F8 , 0x00301 , 0x001FF },
+ { 0x000FC , 0x00300 , 0x001DC },
+ { 0x000FC , 0x00301 , 0x001D8 },
+ { 0x000FC , 0x00304 , 0x001D6 },
+ { 0x000FC , 0x0030C , 0x001DA },
+ { 0x00102 , 0x00300 , 0x01EB0 },
+ { 0x00102 , 0x00301 , 0x01EAE },
+ { 0x00102 , 0x00303 , 0x01EB4 },
+ { 0x00102 , 0x00309 , 0x01EB2 },
+ { 0x00103 , 0x00300 , 0x01EB1 },
+ { 0x00103 , 0x00301 , 0x01EAF },
+ { 0x00103 , 0x00303 , 0x01EB5 },
+ { 0x00103 , 0x00309 , 0x01EB3 },
+ { 0x00112 , 0x00300 , 0x01E14 },
+ { 0x00112 , 0x00301 , 0x01E16 },
+ { 0x00113 , 0x00300 , 0x01E15 },
+ { 0x00113 , 0x00301 , 0x01E17 },
+ { 0x0014C , 0x00300 , 0x01E50 },
+ { 0x0014C , 0x00301 , 0x01E52 },
+ { 0x0014D , 0x00300 , 0x01E51 },
+ { 0x0014D , 0x00301 , 0x01E53 },
+ { 0x0015A , 0x00307 , 0x01E64 },
+ { 0x0015B , 0x00307 , 0x01E65 },
+ { 0x00160 , 0x00307 , 0x01E66 },
+ { 0x00161 , 0x00307 , 0x01E67 },
+ { 0x00168 , 0x00301 , 0x01E78 },
+ { 0x00169 , 0x00301 , 0x01E79 },
+ { 0x0016A , 0x00308 , 0x01E7A },
+ { 0x0016B , 0x00308 , 0x01E7B },
+ { 0x0017F , 0x00307 , 0x01E9B },
+ { 0x001A0 , 0x00300 , 0x01EDC },
+ { 0x001A0 , 0x00301 , 0x01EDA },
+ { 0x001A0 , 0x00303 , 0x01EE0 },
+ { 0x001A0 , 0x00309 , 0x01EDE },
+ { 0x001A0 , 0x00323 , 0x01EE2 },
+ { 0x001A1 , 0x00300 , 0x01EDD },
+ { 0x001A1 , 0x00301 , 0x01EDB },
+ { 0x001A1 , 0x00303 , 0x01EE1 },
+ { 0x001A1 , 0x00309 , 0x01EDF },
+ { 0x001A1 , 0x00323 , 0x01EE3 },
+ { 0x001AF , 0x00300 , 0x01EEA },
+ { 0x001AF , 0x00301 , 0x01EE8 },
+ { 0x001AF , 0x00303 , 0x01EEE },
+ { 0x001AF , 0x00309 , 0x01EEC },
+ { 0x001AF , 0x00323 , 0x01EF0 },
+ { 0x001B0 , 0x00300 , 0x01EEB },
+ { 0x001B0 , 0x00301 , 0x01EE9 },
+ { 0x001B0 , 0x00303 , 0x01EEF },
+ { 0x001B0 , 0x00309 , 0x01EED },
+ { 0x001B0 , 0x00323 , 0x01EF1 },
+ { 0x001B7 , 0x0030C , 0x001EE },
+ { 0x001EA , 0x00304 , 0x001EC },
+ { 0x001EB , 0x00304 , 0x001ED },
+ { 0x00226 , 0x00304 , 0x001E0 },
+ { 0x00227 , 0x00304 , 0x001E1 },
+ { 0x00228 , 0x00306 , 0x01E1C },
+ { 0x00229 , 0x00306 , 0x01E1D },
+ { 0x0022E , 0x00304 , 0x00230 },
+ { 0x0022F , 0x00304 , 0x00231 },
+ { 0x00292 , 0x0030C , 0x001EF },
+ { 0x00391 , 0x00300 , 0x01FBA },
+ { 0x00391 , 0x00301 , 0x00386 },
+ { 0x00391 , 0x00304 , 0x01FB9 },
+ { 0x00391 , 0x00306 , 0x01FB8 },
+ { 0x00391 , 0x00313 , 0x01F08 },
+ { 0x00391 , 0x00314 , 0x01F09 },
+ { 0x00391 , 0x00345 , 0x01FBC },
+ { 0x00395 , 0x00300 , 0x01FC8 },
+ { 0x00395 , 0x00301 , 0x00388 },
+ { 0x00395 , 0x00313 , 0x01F18 },
+ { 0x00395 , 0x00314 , 0x01F19 },
+ { 0x00397 , 0x00300 , 0x01FCA },
+ { 0x00397 , 0x00301 , 0x00389 },
+ { 0x00397 , 0x00313 , 0x01F28 },
+ { 0x00397 , 0x00314 , 0x01F29 },
+ { 0x00397 , 0x00345 , 0x01FCC },
+ { 0x00399 , 0x00300 , 0x01FDA },
+ { 0x00399 , 0x00301 , 0x0038A },
+ { 0x00399 , 0x00304 , 0x01FD9 },
+ { 0x00399 , 0x00306 , 0x01FD8 },
+ { 0x00399 , 0x00308 , 0x003AA },
+ { 0x00399 , 0x00313 , 0x01F38 },
+ { 0x00399 , 0x00314 , 0x01F39 },
+ { 0x0039F , 0x00300 , 0x01FF8 },
+ { 0x0039F , 0x00301 , 0x0038C },
+ { 0x0039F , 0x00313 , 0x01F48 },
+ { 0x0039F , 0x00314 , 0x01F49 },
+ { 0x003A1 , 0x00314 , 0x01FEC },
+ { 0x003A5 , 0x00300 , 0x01FEA },
+ { 0x003A5 , 0x00301 , 0x0038E },
+ { 0x003A5 , 0x00304 , 0x01FE9 },
+ { 0x003A5 , 0x00306 , 0x01FE8 },
+ { 0x003A5 , 0x00308 , 0x003AB },
+ { 0x003A5 , 0x00314 , 0x01F59 },
+ { 0x003A9 , 0x00300 , 0x01FFA },
+ { 0x003A9 , 0x00301 , 0x0038F },
+ { 0x003A9 , 0x00313 , 0x01F68 },
+ { 0x003A9 , 0x00314 , 0x01F69 },
+ { 0x003A9 , 0x00345 , 0x01FFC },
+ { 0x003AC , 0x00345 , 0x01FB4 },
+ { 0x003AE , 0x00345 , 0x01FC4 },
+ { 0x003B1 , 0x00300 , 0x01F70 },
+ { 0x003B1 , 0x00301 , 0x003AC },
+ { 0x003B1 , 0x00304 , 0x01FB1 },
+ { 0x003B1 , 0x00306 , 0x01FB0 },
+ { 0x003B1 , 0x00313 , 0x01F00 },
+ { 0x003B1 , 0x00314 , 0x01F01 },
+ { 0x003B1 , 0x00342 , 0x01FB6 },
+ { 0x003B1 , 0x00345 , 0x01FB3 },
+ { 0x003B5 , 0x00300 , 0x01F72 },
+ { 0x003B5 , 0x00301 , 0x003AD },
+ { 0x003B5 , 0x00313 , 0x01F10 },
+ { 0x003B5 , 0x00314 , 0x01F11 },
+ { 0x003B7 , 0x00300 , 0x01F74 },
+ { 0x003B7 , 0x00301 , 0x003AE },
+ { 0x003B7 , 0x00313 , 0x01F20 },
+ { 0x003B7 , 0x00314 , 0x01F21 },
+ { 0x003B7 , 0x00342 , 0x01FC6 },
+ { 0x003B7 , 0x00345 , 0x01FC3 },
+ { 0x003B9 , 0x00300 , 0x01F76 },
+ { 0x003B9 , 0x00301 , 0x003AF },
+ { 0x003B9 , 0x00304 , 0x01FD1 },
+ { 0x003B9 , 0x00306 , 0x01FD0 },
+ { 0x003B9 , 0x00308 , 0x003CA },
+ { 0x003B9 , 0x00313 , 0x01F30 },
+ { 0x003B9 , 0x00314 , 0x01F31 },
+ { 0x003B9 , 0x00342 , 0x01FD6 },
+ { 0x003BF , 0x00300 , 0x01F78 },
+ { 0x003BF , 0x00301 , 0x003CC },
+ { 0x003BF , 0x00313 , 0x01F40 },
+ { 0x003BF , 0x00314 , 0x01F41 },
+ { 0x003C1 , 0x00313 , 0x01FE4 },
+ { 0x003C1 , 0x00314 , 0x01FE5 },
+ { 0x003C5 , 0x00300 , 0x01F7A },
+ { 0x003C5 , 0x00301 , 0x003CD },
+ { 0x003C5 , 0x00304 , 0x01FE1 },
+ { 0x003C5 , 0x00306 , 0x01FE0 },
+ { 0x003C5 , 0x00308 , 0x003CB },
+ { 0x003C5 , 0x00313 , 0x01F50 },
+ { 0x003C5 , 0x00314 , 0x01F51 },
+ { 0x003C5 , 0x00342 , 0x01FE6 },
+ { 0x003C9 , 0x00300 , 0x01F7C },
+ { 0x003C9 , 0x00301 , 0x003CE },
+ { 0x003C9 , 0x00313 , 0x01F60 },
+ { 0x003C9 , 0x00314 , 0x01F61 },
+ { 0x003C9 , 0x00342 , 0x01FF6 },
+ { 0x003C9 , 0x00345 , 0x01FF3 },
+ { 0x003CA , 0x00300 , 0x01FD2 },
+ { 0x003CA , 0x00301 , 0x00390 },
+ { 0x003CA , 0x00342 , 0x01FD7 },
+ { 0x003CB , 0x00300 , 0x01FE2 },
+ { 0x003CB , 0x00301 , 0x003B0 },
+ { 0x003CB , 0x00342 , 0x01FE7 },
+ { 0x003CE , 0x00345 , 0x01FF4 },
+ { 0x003D2 , 0x00301 , 0x003D3 },
+ { 0x003D2 , 0x00308 , 0x003D4 },
+ { 0x00406 , 0x00308 , 0x00407 },
+ { 0x00410 , 0x00306 , 0x004D0 },
+ { 0x00410 , 0x00308 , 0x004D2 },
+ { 0x00413 , 0x00301 , 0x00403 },
+ { 0x00415 , 0x00300 , 0x00400 },
+ { 0x00415 , 0x00306 , 0x004D6 },
+ { 0x00415 , 0x00308 , 0x00401 },
+ { 0x00416 , 0x00306 , 0x004C1 },
+ { 0x00416 , 0x00308 , 0x004DC },
+ { 0x00417 , 0x00308 , 0x004DE },
+ { 0x00418 , 0x00300 , 0x0040D },
+ { 0x00418 , 0x00304 , 0x004E2 },
+ { 0x00418 , 0x00306 , 0x00419 },
+ { 0x00418 , 0x00308 , 0x004E4 },
+ { 0x0041A , 0x00301 , 0x0040C },
+ { 0x0041E , 0x00308 , 0x004E6 },
+ { 0x00423 , 0x00304 , 0x004EE },
+ { 0x00423 , 0x00306 , 0x0040E },
+ { 0x00423 , 0x00308 , 0x004F0 },
+ { 0x00423 , 0x0030B , 0x004F2 },
+ { 0x00427 , 0x00308 , 0x004F4 },
+ { 0x0042B , 0x00308 , 0x004F8 },
+ { 0x0042D , 0x00308 , 0x004EC },
+ { 0x00430 , 0x00306 , 0x004D1 },
+ { 0x00430 , 0x00308 , 0x004D3 },
+ { 0x00433 , 0x00301 , 0x00453 },
+ { 0x00435 , 0x00300 , 0x00450 },
+ { 0x00435 , 0x00306 , 0x004D7 },
+ { 0x00435 , 0x00308 , 0x00451 },
+ { 0x00436 , 0x00306 , 0x004C2 },
+ { 0x00436 , 0x00308 , 0x004DD },
+ { 0x00437 , 0x00308 , 0x004DF },
+ { 0x00438 , 0x00300 , 0x0045D },
+ { 0x00438 , 0x00304 , 0x004E3 },
+ { 0x00438 , 0x00306 , 0x00439 },
+ { 0x00438 , 0x00308 , 0x004E5 },
+ { 0x0043A , 0x00301 , 0x0045C },
+ { 0x0043E , 0x00308 , 0x004E7 },
+ { 0x00443 , 0x00304 , 0x004EF },
+ { 0x00443 , 0x00306 , 0x0045E },
+ { 0x00443 , 0x00308 , 0x004F1 },
+ { 0x00443 , 0x0030B , 0x004F3 },
+ { 0x00447 , 0x00308 , 0x004F5 },
+ { 0x0044B , 0x00308 , 0x004F9 },
+ { 0x0044D , 0x00308 , 0x004ED },
+ { 0x00456 , 0x00308 , 0x00457 },
+ { 0x00474 , 0x0030F , 0x00476 },
+ { 0x00475 , 0x0030F , 0x00477 },
+ { 0x004D8 , 0x00308 , 0x004DA },
+ { 0x004D9 , 0x00308 , 0x004DB },
+ { 0x004E8 , 0x00308 , 0x004EA },
+ { 0x004E9 , 0x00308 , 0x004EB },
+ { 0x00627 , 0x00653 , 0x00622 },
+ { 0x00627 , 0x00654 , 0x00623 },
+ { 0x00627 , 0x00655 , 0x00625 },
+ { 0x00648 , 0x00654 , 0x00624 },
+ { 0x0064A , 0x00654 , 0x00626 },
+ { 0x006C1 , 0x00654 , 0x006C2 },
+ { 0x006D2 , 0x00654 , 0x006D3 },
+ { 0x006D5 , 0x00654 , 0x006C0 },
+ { 0x00928 , 0x0093C , 0x00929 },
+ { 0x00930 , 0x0093C , 0x00931 },
+ { 0x00933 , 0x0093C , 0x00934 },
+ { 0x009C7 , 0x009BE , 0x009CB },
+ { 0x009C7 , 0x009D7 , 0x009CC },
+ { 0x00B47 , 0x00B3E , 0x00B4B },
+ { 0x00B47 , 0x00B56 , 0x00B48 },
+ { 0x00B47 , 0x00B57 , 0x00B4C },
+ { 0x00B92 , 0x00BD7 , 0x00B94 },
+ { 0x00BC6 , 0x00BBE , 0x00BCA },
+ { 0x00BC6 , 0x00BD7 , 0x00BCC },
+ { 0x00BC7 , 0x00BBE , 0x00BCB },
+ { 0x00C46 , 0x00C56 , 0x00C48 },
+ { 0x00CBF , 0x00CD5 , 0x00CC0 },
+ { 0x00CC6 , 0x00CC2 , 0x00CCA },
+ { 0x00CC6 , 0x00CD5 , 0x00CC7 },
+ { 0x00CC6 , 0x00CD6 , 0x00CC8 },
+ { 0x00CCA , 0x00CD5 , 0x00CCB },
+ { 0x00D46 , 0x00D3E , 0x00D4A },
+ { 0x00D46 , 0x00D57 , 0x00D4C },
+ { 0x00D47 , 0x00D3E , 0x00D4B },
+ { 0x00DD9 , 0x00DCA , 0x00DDA },
+ { 0x00DD9 , 0x00DCF , 0x00DDC },
+ { 0x00DD9 , 0x00DDF , 0x00DDE },
+ { 0x00DDC , 0x00DCA , 0x00DDD },
+ { 0x01025 , 0x0102E , 0x01026 },
+ { 0x01B05 , 0x01B35 , 0x01B06 },
+ { 0x01B07 , 0x01B35 , 0x01B08 },
+ { 0x01B09 , 0x01B35 , 0x01B0A },
+ { 0x01B0B , 0x01B35 , 0x01B0C },
+ { 0x01B0D , 0x01B35 , 0x01B0E },
+ { 0x01B11 , 0x01B35 , 0x01B12 },
+ { 0x01B3A , 0x01B35 , 0x01B3B },
+ { 0x01B3C , 0x01B35 , 0x01B3D },
+ { 0x01B3E , 0x01B35 , 0x01B40 },
+ { 0x01B3F , 0x01B35 , 0x01B41 },
+ { 0x01B42 , 0x01B35 , 0x01B43 },
+ { 0x01E36 , 0x00304 , 0x01E38 },
+ { 0x01E37 , 0x00304 , 0x01E39 },
+ { 0x01E5A , 0x00304 , 0x01E5C },
+ { 0x01E5B , 0x00304 , 0x01E5D },
+ { 0x01E62 , 0x00307 , 0x01E68 },
+ { 0x01E63 , 0x00307 , 0x01E69 },
+ { 0x01EA0 , 0x00302 , 0x01EAC },
+ { 0x01EA0 , 0x00306 , 0x01EB6 },
+ { 0x01EA1 , 0x00302 , 0x01EAD },
+ { 0x01EA1 , 0x00306 , 0x01EB7 },
+ { 0x01EB8 , 0x00302 , 0x01EC6 },
+ { 0x01EB9 , 0x00302 , 0x01EC7 },
+ { 0x01ECC , 0x00302 , 0x01ED8 },
+ { 0x01ECD , 0x00302 , 0x01ED9 },
+ { 0x01F00 , 0x00300 , 0x01F02 },
+ { 0x01F00 , 0x00301 , 0x01F04 },
+ { 0x01F00 , 0x00342 , 0x01F06 },
+ { 0x01F00 , 0x00345 , 0x01F80 },
+ { 0x01F01 , 0x00300 , 0x01F03 },
+ { 0x01F01 , 0x00301 , 0x01F05 },
+ { 0x01F01 , 0x00342 , 0x01F07 },
+ { 0x01F01 , 0x00345 , 0x01F81 },
+ { 0x01F02 , 0x00345 , 0x01F82 },
+ { 0x01F03 , 0x00345 , 0x01F83 },
+ { 0x01F04 , 0x00345 , 0x01F84 },
+ { 0x01F05 , 0x00345 , 0x01F85 },
+ { 0x01F06 , 0x00345 , 0x01F86 },
+ { 0x01F07 , 0x00345 , 0x01F87 },
+ { 0x01F08 , 0x00300 , 0x01F0A },
+ { 0x01F08 , 0x00301 , 0x01F0C },
+ { 0x01F08 , 0x00342 , 0x01F0E },
+ { 0x01F08 , 0x00345 , 0x01F88 },
+ { 0x01F09 , 0x00300 , 0x01F0B },
+ { 0x01F09 , 0x00301 , 0x01F0D },
+ { 0x01F09 , 0x00342 , 0x01F0F },
+ { 0x01F09 , 0x00345 , 0x01F89 },
+ { 0x01F0A , 0x00345 , 0x01F8A },
+ { 0x01F0B , 0x00345 , 0x01F8B },
+ { 0x01F0C , 0x00345 , 0x01F8C },
+ { 0x01F0D , 0x00345 , 0x01F8D },
+ { 0x01F0E , 0x00345 , 0x01F8E },
+ { 0x01F0F , 0x00345 , 0x01F8F },
+ { 0x01F10 , 0x00300 , 0x01F12 },
+ { 0x01F10 , 0x00301 , 0x01F14 },
+ { 0x01F11 , 0x00300 , 0x01F13 },
+ { 0x01F11 , 0x00301 , 0x01F15 },
+ { 0x01F18 , 0x00300 , 0x01F1A },
+ { 0x01F18 , 0x00301 , 0x01F1C },
+ { 0x01F19 , 0x00300 , 0x01F1B },
+ { 0x01F19 , 0x00301 , 0x01F1D },
+ { 0x01F20 , 0x00300 , 0x01F22 },
+ { 0x01F20 , 0x00301 , 0x01F24 },
+ { 0x01F20 , 0x00342 , 0x01F26 },
+ { 0x01F20 , 0x00345 , 0x01F90 },
+ { 0x01F21 , 0x00300 , 0x01F23 },
+ { 0x01F21 , 0x00301 , 0x01F25 },
+ { 0x01F21 , 0x00342 , 0x01F27 },
+ { 0x01F21 , 0x00345 , 0x01F91 },
+ { 0x01F22 , 0x00345 , 0x01F92 },
+ { 0x01F23 , 0x00345 , 0x01F93 },
+ { 0x01F24 , 0x00345 , 0x01F94 },
+ { 0x01F25 , 0x00345 , 0x01F95 },
+ { 0x01F26 , 0x00345 , 0x01F96 },
+ { 0x01F27 , 0x00345 , 0x01F97 },
+ { 0x01F28 , 0x00300 , 0x01F2A },
+ { 0x01F28 , 0x00301 , 0x01F2C },
+ { 0x01F28 , 0x00342 , 0x01F2E },
+ { 0x01F28 , 0x00345 , 0x01F98 },
+ { 0x01F29 , 0x00300 , 0x01F2B },
+ { 0x01F29 , 0x00301 , 0x01F2D },
+ { 0x01F29 , 0x00342 , 0x01F2F },
+ { 0x01F29 , 0x00345 , 0x01F99 },
+ { 0x01F2A , 0x00345 , 0x01F9A },
+ { 0x01F2B , 0x00345 , 0x01F9B },
+ { 0x01F2C , 0x00345 , 0x01F9C },
+ { 0x01F2D , 0x00345 , 0x01F9D },
+ { 0x01F2E , 0x00345 , 0x01F9E },
+ { 0x01F2F , 0x00345 , 0x01F9F },
+ { 0x01F30 , 0x00300 , 0x01F32 },
+ { 0x01F30 , 0x00301 , 0x01F34 },
+ { 0x01F30 , 0x00342 , 0x01F36 },
+ { 0x01F31 , 0x00300 , 0x01F33 },
+ { 0x01F31 , 0x00301 , 0x01F35 },
+ { 0x01F31 , 0x00342 , 0x01F37 },
+ { 0x01F38 , 0x00300 , 0x01F3A },
+ { 0x01F38 , 0x00301 , 0x01F3C },
+ { 0x01F38 , 0x00342 , 0x01F3E },
+ { 0x01F39 , 0x00300 , 0x01F3B },
+ { 0x01F39 , 0x00301 , 0x01F3D },
+ { 0x01F39 , 0x00342 , 0x01F3F },
+ { 0x01F40 , 0x00300 , 0x01F42 },
+ { 0x01F40 , 0x00301 , 0x01F44 },
+ { 0x01F41 , 0x00300 , 0x01F43 },
+ { 0x01F41 , 0x00301 , 0x01F45 },
+ { 0x01F48 , 0x00300 , 0x01F4A },
+ { 0x01F48 , 0x00301 , 0x01F4C },
+ { 0x01F49 , 0x00300 , 0x01F4B },
+ { 0x01F49 , 0x00301 , 0x01F4D },
+ { 0x01F50 , 0x00300 , 0x01F52 },
+ { 0x01F50 , 0x00301 , 0x01F54 },
+ { 0x01F50 , 0x00342 , 0x01F56 },
+ { 0x01F51 , 0x00300 , 0x01F53 },
+ { 0x01F51 , 0x00301 , 0x01F55 },
+ { 0x01F51 , 0x00342 , 0x01F57 },
+ { 0x01F59 , 0x00300 , 0x01F5B },
+ { 0x01F59 , 0x00301 , 0x01F5D },
+ { 0x01F59 , 0x00342 , 0x01F5F },
+ { 0x01F60 , 0x00300 , 0x01F62 },
+ { 0x01F60 , 0x00301 , 0x01F64 },
+ { 0x01F60 , 0x00342 , 0x01F66 },
+ { 0x01F60 , 0x00345 , 0x01FA0 },
+ { 0x01F61 , 0x00300 , 0x01F63 },
+ { 0x01F61 , 0x00301 , 0x01F65 },
+ { 0x01F61 , 0x00342 , 0x01F67 },
+ { 0x01F61 , 0x00345 , 0x01FA1 },
+ { 0x01F62 , 0x00345 , 0x01FA2 },
+ { 0x01F63 , 0x00345 , 0x01FA3 },
+ { 0x01F64 , 0x00345 , 0x01FA4 },
+ { 0x01F65 , 0x00345 , 0x01FA5 },
+ { 0x01F66 , 0x00345 , 0x01FA6 },
+ { 0x01F67 , 0x00345 , 0x01FA7 },
+ { 0x01F68 , 0x00300 , 0x01F6A },
+ { 0x01F68 , 0x00301 , 0x01F6C },
+ { 0x01F68 , 0x00342 , 0x01F6E },
+ { 0x01F68 , 0x00345 , 0x01FA8 },
+ { 0x01F69 , 0x00300 , 0x01F6B },
+ { 0x01F69 , 0x00301 , 0x01F6D },
+ { 0x01F69 , 0x00342 , 0x01F6F },
+ { 0x01F69 , 0x00345 , 0x01FA9 },
+ { 0x01F6A , 0x00345 , 0x01FAA },
+ { 0x01F6B , 0x00345 , 0x01FAB },
+ { 0x01F6C , 0x00345 , 0x01FAC },
+ { 0x01F6D , 0x00345 , 0x01FAD },
+ { 0x01F6E , 0x00345 , 0x01FAE },
+ { 0x01F6F , 0x00345 , 0x01FAF },
+ { 0x01F70 , 0x00345 , 0x01FB2 },
+ { 0x01F74 , 0x00345 , 0x01FC2 },
+ { 0x01F7C , 0x00345 , 0x01FF2 },
+ { 0x01FB6 , 0x00345 , 0x01FB7 },
+ { 0x01FBF , 0x00300 , 0x01FCD },
+ { 0x01FBF , 0x00301 , 0x01FCE },
+ { 0x01FBF , 0x00342 , 0x01FCF },
+ { 0x01FC6 , 0x00345 , 0x01FC7 },
+ { 0x01FF6 , 0x00345 , 0x01FF7 },
+ { 0x01FFE , 0x00300 , 0x01FDD },
+ { 0x01FFE , 0x00301 , 0x01FDE },
+ { 0x01FFE , 0x00342 , 0x01FDF },
+ { 0x02190 , 0x00338 , 0x0219A },
+ { 0x02192 , 0x00338 , 0x0219B },
+ { 0x02194 , 0x00338 , 0x021AE },
+ { 0x021D0 , 0x00338 , 0x021CD },
+ { 0x021D2 , 0x00338 , 0x021CF },
+ { 0x021D4 , 0x00338 , 0x021CE },
+ { 0x02203 , 0x00338 , 0x02204 },
+ { 0x02208 , 0x00338 , 0x02209 },
+ { 0x0220B , 0x00338 , 0x0220C },
+ { 0x02223 , 0x00338 , 0x02224 },
+ { 0x02225 , 0x00338 , 0x02226 },
+ { 0x0223C , 0x00338 , 0x02241 },
+ { 0x02243 , 0x00338 , 0x02244 },
+ { 0x02245 , 0x00338 , 0x02247 },
+ { 0x02248 , 0x00338 , 0x02249 },
+ { 0x0224D , 0x00338 , 0x0226D },
+ { 0x02261 , 0x00338 , 0x02262 },
+ { 0x02264 , 0x00338 , 0x02270 },
+ { 0x02265 , 0x00338 , 0x02271 },
+ { 0x02272 , 0x00338 , 0x02274 },
+ { 0x02273 , 0x00338 , 0x02275 },
+ { 0x02276 , 0x00338 , 0x02278 },
+ { 0x02277 , 0x00338 , 0x02279 },
+ { 0x0227A , 0x00338 , 0x02280 },
+ { 0x0227B , 0x00338 , 0x02281 },
+ { 0x0227C , 0x00338 , 0x022E0 },
+ { 0x0227D , 0x00338 , 0x022E1 },
+ { 0x02282 , 0x00338 , 0x02284 },
+ { 0x02283 , 0x00338 , 0x02285 },
+ { 0x02286 , 0x00338 , 0x02288 },
+ { 0x02287 , 0x00338 , 0x02289 },
+ { 0x02291 , 0x00338 , 0x022E2 },
+ { 0x02292 , 0x00338 , 0x022E3 },
+ { 0x022A2 , 0x00338 , 0x022AC },
+ { 0x022A8 , 0x00338 , 0x022AD },
+ { 0x022A9 , 0x00338 , 0x022AE },
+ { 0x022AB , 0x00338 , 0x022AF },
+ { 0x022B2 , 0x00338 , 0x022EA },
+ { 0x022B3 , 0x00338 , 0x022EB },
+ { 0x022B4 , 0x00338 , 0x022EC },
+ { 0x022B5 , 0x00338 , 0x022ED },
+ { 0x03046 , 0x03099 , 0x03094 },
+ { 0x0304B , 0x03099 , 0x0304C },
+ { 0x0304D , 0x03099 , 0x0304E },
+ { 0x0304F , 0x03099 , 0x03050 },
+ { 0x03051 , 0x03099 , 0x03052 },
+ { 0x03053 , 0x03099 , 0x03054 },
+ { 0x03055 , 0x03099 , 0x03056 },
+ { 0x03057 , 0x03099 , 0x03058 },
+ { 0x03059 , 0x03099 , 0x0305A },
+ { 0x0305B , 0x03099 , 0x0305C },
+ { 0x0305D , 0x03099 , 0x0305E },
+ { 0x0305F , 0x03099 , 0x03060 },
+ { 0x03061 , 0x03099 , 0x03062 },
+ { 0x03064 , 0x03099 , 0x03065 },
+ { 0x03066 , 0x03099 , 0x03067 },
+ { 0x03068 , 0x03099 , 0x03069 },
+ { 0x0306F , 0x03099 , 0x03070 },
+ { 0x0306F , 0x0309A , 0x03071 },
+ { 0x03072 , 0x03099 , 0x03073 },
+ { 0x03072 , 0x0309A , 0x03074 },
+ { 0x03075 , 0x03099 , 0x03076 },
+ { 0x03075 , 0x0309A , 0x03077 },
+ { 0x03078 , 0x03099 , 0x03079 },
+ { 0x03078 , 0x0309A , 0x0307A },
+ { 0x0307B , 0x03099 , 0x0307C },
+ { 0x0307B , 0x0309A , 0x0307D },
+ { 0x0309D , 0x03099 , 0x0309E },
+ { 0x030A6 , 0x03099 , 0x030F4 },
+ { 0x030AB , 0x03099 , 0x030AC },
+ { 0x030AD , 0x03099 , 0x030AE },
+ { 0x030AF , 0x03099 , 0x030B0 },
+ { 0x030B1 , 0x03099 , 0x030B2 },
+ { 0x030B3 , 0x03099 , 0x030B4 },
+ { 0x030B5 , 0x03099 , 0x030B6 },
+ { 0x030B7 , 0x03099 , 0x030B8 },
+ { 0x030B9 , 0x03099 , 0x030BA },
+ { 0x030BB , 0x03099 , 0x030BC },
+ { 0x030BD , 0x03099 , 0x030BE },
+ { 0x030BF , 0x03099 , 0x030C0 },
+ { 0x030C1 , 0x03099 , 0x030C2 },
+ { 0x030C4 , 0x03099 , 0x030C5 },
+ { 0x030C6 , 0x03099 , 0x030C7 },
+ { 0x030C8 , 0x03099 , 0x030C9 },
+ { 0x030CF , 0x03099 , 0x030D0 },
+ { 0x030CF , 0x0309A , 0x030D1 },
+ { 0x030D2 , 0x03099 , 0x030D3 },
+ { 0x030D2 , 0x0309A , 0x030D4 },
+ { 0x030D5 , 0x03099 , 0x030D6 },
+ { 0x030D5 , 0x0309A , 0x030D7 },
+ { 0x030D8 , 0x03099 , 0x030D9 },
+ { 0x030D8 , 0x0309A , 0x030DA },
+ { 0x030DB , 0x03099 , 0x030DC },
+ { 0x030DB , 0x0309A , 0x030DD },
+ { 0x030EF , 0x03099 , 0x030F7 },
+ { 0x030F0 , 0x03099 , 0x030F8 },
+ { 0x030F1 , 0x03099 , 0x030F9 },
+ { 0x030F2 , 0x03099 , 0x030FA },
+ { 0x030FD , 0x03099 , 0x030FE },
+ { 0x11099 , 0x110BA , 0x1109A },
+ { 0x1109B , 0x110BA , 0x1109C },
+ { 0x110A5 , 0x110BA , 0x110AB },
+};
+
+#define CANONICAL_CLASS_MIN 0x0300
+#define CANONICAL_CLASS_MAX 0x1D244
+
+#define IS_DECOMPOSABLE_BLOCK(uc) \
+ (((uc)>>8) <= 0x1D2 && u_decomposable_blocks[(uc)>>8])
+static const char u_decomposable_blocks[0x1D2+1] = {
+ 0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,1,1,1,1,0,0,
+ 1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,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,0,0,0,0,0,0,0,0,
+ 0,0,0,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,0,0,1,0,
+ 0,1,0,0,0,0,0,0,0,0,1,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,1,1,
+};
+
+/* Get Canonical Combining Class(CCC). */
+#define CCC(uc) \
+ (((uc) > 0x1D244)?0:\
+ ccc_val[ccc_val_index[ccc_index[(uc)>>8]][((uc)>>4)&0x0F]][(uc)&0x0F])
+
+/* The table of the value of Canonical Combining Class */
+static const unsigned char ccc_val[][16] = {
+ /* idx=0: XXXX0 - XXXXF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=1: 00300 - 0030F */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=2: 00310 - 0031F */
+ {230, 230, 230, 230, 230, 232, 220, 220, 220, 220, 232, 216, 220, 220, 220, 220 },
+ /* idx=3: 00320 - 0032F */
+ {220, 202, 202, 220, 220, 220, 220, 202, 202, 220, 220, 220, 220, 220, 220, 220 },
+ /* idx=4: 00330 - 0033F */
+ {220, 220, 220, 220, 1, 1, 1, 1, 1, 220, 220, 220, 220, 230, 230, 230 },
+ /* idx=5: 00340 - 0034F */
+ {230, 230, 230, 230, 230, 240, 230, 220, 220, 220, 230, 230, 230, 220, 220, 0 },
+ /* idx=6: 00350 - 0035F */
+ {230, 230, 230, 220, 220, 220, 220, 230, 232, 220, 220, 230, 233, 234, 234, 233 },
+ /* idx=7: 00360 - 0036F */
+ {234, 234, 233, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=8: 00480 - 0048F */
+ {0, 0, 0, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=9: 00590 - 0059F */
+ {0, 220, 230, 230, 230, 230, 220, 230, 230, 230, 222, 220, 230, 230, 230, 230 },
+ /* idx=10: 005A0 - 005AF */
+ {230, 230, 220, 220, 220, 220, 220, 220, 230, 230, 220, 230, 230, 222, 228, 230 },
+ /* idx=11: 005B0 - 005BF */
+ {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23 },
+ /* idx=12: 005C0 - 005CF */
+ {0, 24, 25, 0, 230, 220, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=13: 00610 - 0061F */
+ {230, 230, 230, 230, 230, 230, 230, 230, 30, 31, 32, 0, 0, 0, 0, 0 },
+ /* idx=14: 00640 - 0064F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31 },
+ /* idx=15: 00650 - 0065F */
+ {32, 33, 34, 230, 230, 220, 220, 230, 230, 230, 230, 230, 220, 230, 230, 220 },
+ /* idx=16: 00670 - 0067F */
+ {35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=17: 006D0 - 006DF */
+ {0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 0, 0, 230 },
+ /* idx=18: 006E0 - 006EF */
+ {230, 230, 230, 220, 230, 0, 0, 230, 230, 0, 220, 230, 230, 220, 0, 0 },
+ /* idx=19: 00710 - 0071F */
+ {0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=20: 00730 - 0073F */
+ {230, 220, 230, 230, 220, 230, 230, 220, 220, 220, 230, 220, 220, 230, 220, 230 },
+ /* idx=21: 00740 - 0074F */
+ {230, 230, 220, 230, 220, 230, 220, 230, 220, 230, 230, 0, 0, 0, 0, 0 },
+ /* idx=22: 007E0 - 007EF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230 },
+ /* idx=23: 007F0 - 007FF */
+ {230, 230, 220, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=24: 00810 - 0081F */
+ {0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 230, 230, 230, 230, 230 },
+ /* idx=25: 00820 - 0082F */
+ {230, 230, 230, 230, 0, 230, 230, 230, 0, 230, 230, 230, 230, 230, 0, 0 },
+ /* idx=26: 00850 - 0085F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 0, 0, 0, 0 },
+ /* idx=27: 00930 - 0093F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=28: 00940 - 0094F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=29: 00950 - 0095F */
+ {0, 230, 220, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=30: 009B0 - 009BF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=31: 009C0 - 009CF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=32: 00A30 - 00A3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=33: 00A40 - 00A4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=34: 00AB0 - 00ABF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=35: 00AC0 - 00ACF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=36: 00B30 - 00B3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=37: 00B40 - 00B4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=38: 00BC0 - 00BCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=39: 00C40 - 00C4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=40: 00C50 - 00C5F */
+ {0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=41: 00CB0 - 00CBF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=42: 00CC0 - 00CCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=43: 00D40 - 00D4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=44: 00DC0 - 00DCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0 },
+ /* idx=45: 00E30 - 00E3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 103, 103, 9, 0, 0, 0, 0, 0 },
+ /* idx=46: 00E40 - 00E4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 0, 0, 0, 0 },
+ /* idx=47: 00EB0 - 00EBF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 0, 0, 0, 0, 0, 0 },
+ /* idx=48: 00EC0 - 00ECF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 122, 122, 122, 122, 0, 0, 0, 0 },
+ /* idx=49: 00F10 - 00F1F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 0, 0, 0, 0, 0, 0 },
+ /* idx=50: 00F30 - 00F3F */
+ {0, 0, 0, 0, 0, 220, 0, 220, 0, 216, 0, 0, 0, 0, 0, 0 },
+ /* idx=51: 00F70 - 00F7F */
+ {0, 129, 130, 0, 132, 0, 0, 0, 0, 0, 130, 130, 130, 130, 0, 0 },
+ /* idx=52: 00F80 - 00F8F */
+ {130, 0, 230, 230, 9, 0, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=53: 00FC0 - 00FCF */
+ {0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=54: 01030 - 0103F */
+ {0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 9, 0, 0, 0, 0, 0 },
+ /* idx=55: 01080 - 0108F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=56: 01350 - 0135F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230 },
+ /* idx=57: 01710 - 0171F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=58: 01730 - 0173F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=59: 017D0 - 017DF */
+ {0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0 },
+ /* idx=60: 018A0 - 018AF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0 },
+ /* idx=61: 01930 - 0193F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 230, 220, 0, 0, 0, 0 },
+ /* idx=62: 01A10 - 01A1F */
+ {0, 0, 0, 0, 0, 0, 0, 230, 220, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=63: 01A60 - 01A6F */
+ {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=64: 01A70 - 01A7F */
+ {0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 230, 0, 0, 220 },
+ /* idx=65: 01B30 - 01B3F */
+ {0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=66: 01B40 - 01B4F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=67: 01B60 - 01B6F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 220, 230, 230, 230 },
+ /* idx=68: 01B70 - 01B7F */
+ {230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=69: 01BA0 - 01BAF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0 },
+ /* idx=70: 01BE0 - 01BEF */
+ {0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=71: 01BF0 - 01BFF */
+ {0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=72: 01C30 - 01C3F */
+ {0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=73: 01CD0 - 01CDF */
+ {230, 230, 230, 0, 1, 220, 220, 220, 220, 220, 230, 230, 220, 220, 220, 220 },
+ /* idx=74: 01CE0 - 01CEF */
+ {230, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=75: 01DC0 - 01DCF */
+ {230, 230, 220, 230, 230, 230, 230, 230, 230, 230, 220, 230, 230, 234, 214, 220 },
+ /* idx=76: 01DD0 - 01DDF */
+ {202, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=77: 01DE0 - 01DEF */
+ {230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=78: 01DF0 - 01DFF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 220, 230, 220 },
+ /* idx=79: 020D0 - 020DF */
+ {230, 230, 1, 1, 230, 230, 230, 230, 1, 1, 1, 230, 230, 0, 0, 0 },
+ /* idx=80: 020E0 - 020EF */
+ {0, 230, 0, 0, 0, 1, 1, 230, 220, 230, 1, 1, 220, 220, 220, 220 },
+ /* idx=81: 020F0 - 020FF */
+ {230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=82: 02CE0 - 02CEF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230 },
+ /* idx=83: 02CF0 - 02CFF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=84: 02D70 - 02D7F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 },
+ /* idx=85: 02DE0 - 02DEF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=86: 02DF0 - 02DFF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=87: 03020 - 0302F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 228, 232, 222, 224, 224 },
+ /* idx=88: 03090 - 0309F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0 },
+ /* idx=89: 0A660 - 0A66F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230 },
+ /* idx=90: 0A670 - 0A67F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 0, 0 },
+ /* idx=91: 0A6F0 - 0A6FF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=92: 0A800 - 0A80F */
+ {0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=93: 0A8C0 - 0A8CF */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=94: 0A8E0 - 0A8EF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=95: 0A8F0 - 0A8FF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=96: 0A920 - 0A92F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 0, 0 },
+ /* idx=97: 0A950 - 0A95F */
+ {0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=98: 0A9B0 - 0A9BF */
+ {0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=99: 0A9C0 - 0A9CF */
+ {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=100: 0AAB0 - 0AABF */
+ {230, 0, 230, 230, 220, 0, 0, 230, 230, 0, 0, 0, 0, 0, 230, 230 },
+ /* idx=101: 0AAC0 - 0AACF */
+ {0, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=102: 0ABE0 - 0ABEF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=103: 0FB10 - 0FB1F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0 },
+ /* idx=104: 0FE20 - 0FE2F */
+ {230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=105: 101F0 - 101FF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=106: 10A00 - 10A0F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 230 },
+ /* idx=107: 10A30 - 10A3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 230, 1, 220, 0, 0, 0, 0, 9 },
+ /* idx=108: 11040 - 1104F */
+ {0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=109: 110B0 - 110BF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, 0, 0, 0, 0, 0 },
+ /* idx=110: 1D160 - 1D16F */
+ {0, 0, 0, 0, 0, 216, 216, 1, 1, 1, 0, 0, 0, 226, 216, 216 },
+ /* idx=111: 1D170 - 1D17F */
+ {216, 216, 216, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 220, 220 },
+ /* idx=112: 1D180 - 1D18F */
+ {220, 220, 220, 0, 0, 230, 230, 230, 230, 230, 220, 220, 0, 0, 0, 0 },
+ /* idx=113: 1D1A0 - 1D1AF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0 },
+ /* idx=114: 1D240 - 1D24F */
+ {0, 0, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* The index table to ccc_val[*][16] */
+static const unsigned char ccc_val_index[][16] = {
+ /* idx=0: XXX00 - XXXFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=1: 00300 - 003FF */
+ { 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=2: 00400 - 004FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=3: 00500 - 005FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,10,11,12, 0, 0, 0 },
+ /* idx=4: 00600 - 006FF */
+ { 0,13, 0, 0,14,15, 0,16, 0, 0, 0, 0, 0,17,18, 0 },
+ /* idx=5: 00700 - 007FF */
+ { 0,19, 0,20,21, 0, 0, 0, 0, 0, 0, 0, 0, 0,22,23 },
+ /* idx=6: 00800 - 008FF */
+ { 0,24,25, 0, 0,26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=7: 00900 - 009FF */
+ { 0, 0, 0,27,28,29, 0, 0, 0, 0, 0,30,31, 0, 0, 0 },
+ /* idx=8: 00A00 - 00AFF */
+ { 0, 0, 0,32,33, 0, 0, 0, 0, 0, 0,34,35, 0, 0, 0 },
+ /* idx=9: 00B00 - 00BFF */
+ { 0, 0, 0,36,37, 0, 0, 0, 0, 0, 0, 0,38, 0, 0, 0 },
+ /* idx=10: 00C00 - 00CFF */
+ { 0, 0, 0, 0,39,40, 0, 0, 0, 0, 0,41,42, 0, 0, 0 },
+ /* idx=11: 00D00 - 00DFF */
+ { 0, 0, 0, 0,43, 0, 0, 0, 0, 0, 0, 0,44, 0, 0, 0 },
+ /* idx=12: 00E00 - 00EFF */
+ { 0, 0, 0,45,46, 0, 0, 0, 0, 0, 0,47,48, 0, 0, 0 },
+ /* idx=13: 00F00 - 00FFF */
+ { 0,49, 0,50, 0, 0, 0,51,52, 0, 0, 0,53, 0, 0, 0 },
+ /* idx=14: 01000 - 010FF */
+ { 0, 0, 0,54, 0, 0, 0, 0,55, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=15: 01300 - 013FF */
+ { 0, 0, 0, 0, 0,56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=16: 01700 - 017FF */
+ { 0,57, 0,58, 0, 0, 0, 0, 0, 0, 0, 0, 0,59, 0, 0 },
+ /* idx=17: 01800 - 018FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,60, 0, 0, 0, 0, 0 },
+ /* idx=18: 01900 - 019FF */
+ { 0, 0, 0,61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=19: 01A00 - 01AFF */
+ { 0,62, 0, 0, 0, 0,63,64, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=20: 01B00 - 01BFF */
+ { 0, 0, 0,65,66, 0,67,68, 0, 0,69, 0, 0, 0,70,71 },
+ /* idx=21: 01C00 - 01CFF */
+ { 0, 0, 0,72, 0, 0, 0, 0, 0, 0, 0, 0, 0,73,74, 0 },
+ /* idx=22: 01D00 - 01DFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,75,76,77,78 },
+ /* idx=23: 02000 - 020FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,79,80,81 },
+ /* idx=24: 02C00 - 02CFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,82,83 },
+ /* idx=25: 02D00 - 02DFF */
+ { 0, 0, 0, 0, 0, 0, 0,84, 0, 0, 0, 0, 0, 0,85,86 },
+ /* idx=26: 03000 - 030FF */
+ { 0, 0,87, 0, 0, 0, 0, 0, 0,88, 0, 0, 0, 0, 0, 0 },
+ /* idx=27: 0A600 - 0A6FF */
+ { 0, 0, 0, 0, 0, 0,89,90, 0, 0, 0, 0, 0, 0, 0,91 },
+ /* idx=28: 0A800 - 0A8FF */
+ {92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,93, 0,94,95 },
+ /* idx=29: 0A900 - 0A9FF */
+ { 0, 0,96, 0, 0,97, 0, 0, 0, 0, 0,98,99, 0, 0, 0 },
+ /* idx=30: 0AA00 - 0AAFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,100,101, 0, 0, 0 },
+ /* idx=31: 0AB00 - 0ABFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,102, 0 },
+ /* idx=32: 0FB00 - 0FBFF */
+ { 0,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=33: 0FE00 - 0FEFF */
+ { 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=34: 10100 - 101FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105 },
+ /* idx=35: 10A00 - 10AFF */
+ {106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=36: 11000 - 110FF */
+ { 0, 0, 0, 0,108, 0, 0, 0, 0, 0, 0,109, 0, 0, 0, 0 },
+ /* idx=37: 1D100 - 1D1FF */
+ { 0, 0, 0, 0, 0, 0,110,111,112, 0,113, 0, 0, 0, 0, 0 },
+ /* idx=38: 1D200 - 1D2FF */
+ { 0, 0, 0, 0,114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* The index table to ccc_val_index[*][16] */
+static const unsigned char ccc_index[] = {
+ 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 0, 0,15, 0, 0, 0,16,
+ 17,18,19,20,21,22, 0, 0,23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,24,25, 0, 0,
+ 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,27, 0,
+ 28,29,30,31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32, 0, 0,33, 0, 0,34, 0, 0, 0, 0, 0, 0,
+ 0, 0,35, 0, 0, 0, 0, 0,36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,37,38,};
+
+struct unicode_decomposition_table {
+ uint32_t nfc;
+ uint32_t cp1;
+ uint32_t cp2;
+};
+
+static const struct unicode_decomposition_table u_decomposition_table[] = {
+ { 0x000C0 , 0x00041 , 0x00300 },
+ { 0x000C1 , 0x00041 , 0x00301 },
+ { 0x000C2 , 0x00041 , 0x00302 },
+ { 0x000C3 , 0x00041 , 0x00303 },
+ { 0x000C4 , 0x00041 , 0x00308 },
+ { 0x000C5 , 0x00041 , 0x0030A },
+ { 0x000C7 , 0x00043 , 0x00327 },
+ { 0x000C8 , 0x00045 , 0x00300 },
+ { 0x000C9 , 0x00045 , 0x00301 },
+ { 0x000CA , 0x00045 , 0x00302 },
+ { 0x000CB , 0x00045 , 0x00308 },
+ { 0x000CC , 0x00049 , 0x00300 },
+ { 0x000CD , 0x00049 , 0x00301 },
+ { 0x000CE , 0x00049 , 0x00302 },
+ { 0x000CF , 0x00049 , 0x00308 },
+ { 0x000D1 , 0x0004E , 0x00303 },
+ { 0x000D2 , 0x0004F , 0x00300 },
+ { 0x000D3 , 0x0004F , 0x00301 },
+ { 0x000D4 , 0x0004F , 0x00302 },
+ { 0x000D5 , 0x0004F , 0x00303 },
+ { 0x000D6 , 0x0004F , 0x00308 },
+ { 0x000D9 , 0x00055 , 0x00300 },
+ { 0x000DA , 0x00055 , 0x00301 },
+ { 0x000DB , 0x00055 , 0x00302 },
+ { 0x000DC , 0x00055 , 0x00308 },
+ { 0x000DD , 0x00059 , 0x00301 },
+ { 0x000E0 , 0x00061 , 0x00300 },
+ { 0x000E1 , 0x00061 , 0x00301 },
+ { 0x000E2 , 0x00061 , 0x00302 },
+ { 0x000E3 , 0x00061 , 0x00303 },
+ { 0x000E4 , 0x00061 , 0x00308 },
+ { 0x000E5 , 0x00061 , 0x0030A },
+ { 0x000E7 , 0x00063 , 0x00327 },
+ { 0x000E8 , 0x00065 , 0x00300 },
+ { 0x000E9 , 0x00065 , 0x00301 },
+ { 0x000EA , 0x00065 , 0x00302 },
+ { 0x000EB , 0x00065 , 0x00308 },
+ { 0x000EC , 0x00069 , 0x00300 },
+ { 0x000ED , 0x00069 , 0x00301 },
+ { 0x000EE , 0x00069 , 0x00302 },
+ { 0x000EF , 0x00069 , 0x00308 },
+ { 0x000F1 , 0x0006E , 0x00303 },
+ { 0x000F2 , 0x0006F , 0x00300 },
+ { 0x000F3 , 0x0006F , 0x00301 },
+ { 0x000F4 , 0x0006F , 0x00302 },
+ { 0x000F5 , 0x0006F , 0x00303 },
+ { 0x000F6 , 0x0006F , 0x00308 },
+ { 0x000F9 , 0x00075 , 0x00300 },
+ { 0x000FA , 0x00075 , 0x00301 },
+ { 0x000FB , 0x00075 , 0x00302 },
+ { 0x000FC , 0x00075 , 0x00308 },
+ { 0x000FD , 0x00079 , 0x00301 },
+ { 0x000FF , 0x00079 , 0x00308 },
+ { 0x00100 , 0x00041 , 0x00304 },
+ { 0x00101 , 0x00061 , 0x00304 },
+ { 0x00102 , 0x00041 , 0x00306 },
+ { 0x00103 , 0x00061 , 0x00306 },
+ { 0x00104 , 0x00041 , 0x00328 },
+ { 0x00105 , 0x00061 , 0x00328 },
+ { 0x00106 , 0x00043 , 0x00301 },
+ { 0x00107 , 0x00063 , 0x00301 },
+ { 0x00108 , 0x00043 , 0x00302 },
+ { 0x00109 , 0x00063 , 0x00302 },
+ { 0x0010A , 0x00043 , 0x00307 },
+ { 0x0010B , 0x00063 , 0x00307 },
+ { 0x0010C , 0x00043 , 0x0030C },
+ { 0x0010D , 0x00063 , 0x0030C },
+ { 0x0010E , 0x00044 , 0x0030C },
+ { 0x0010F , 0x00064 , 0x0030C },
+ { 0x00112 , 0x00045 , 0x00304 },
+ { 0x00113 , 0x00065 , 0x00304 },
+ { 0x00114 , 0x00045 , 0x00306 },
+ { 0x00115 , 0x00065 , 0x00306 },
+ { 0x00116 , 0x00045 , 0x00307 },
+ { 0x00117 , 0x00065 , 0x00307 },
+ { 0x00118 , 0x00045 , 0x00328 },
+ { 0x00119 , 0x00065 , 0x00328 },
+ { 0x0011A , 0x00045 , 0x0030C },
+ { 0x0011B , 0x00065 , 0x0030C },
+ { 0x0011C , 0x00047 , 0x00302 },
+ { 0x0011D , 0x00067 , 0x00302 },
+ { 0x0011E , 0x00047 , 0x00306 },
+ { 0x0011F , 0x00067 , 0x00306 },
+ { 0x00120 , 0x00047 , 0x00307 },
+ { 0x00121 , 0x00067 , 0x00307 },
+ { 0x00122 , 0x00047 , 0x00327 },
+ { 0x00123 , 0x00067 , 0x00327 },
+ { 0x00124 , 0x00048 , 0x00302 },
+ { 0x00125 , 0x00068 , 0x00302 },
+ { 0x00128 , 0x00049 , 0x00303 },
+ { 0x00129 , 0x00069 , 0x00303 },
+ { 0x0012A , 0x00049 , 0x00304 },
+ { 0x0012B , 0x00069 , 0x00304 },
+ { 0x0012C , 0x00049 , 0x00306 },
+ { 0x0012D , 0x00069 , 0x00306 },
+ { 0x0012E , 0x00049 , 0x00328 },
+ { 0x0012F , 0x00069 , 0x00328 },
+ { 0x00130 , 0x00049 , 0x00307 },
+ { 0x00134 , 0x0004A , 0x00302 },
+ { 0x00135 , 0x0006A , 0x00302 },
+ { 0x00136 , 0x0004B , 0x00327 },
+ { 0x00137 , 0x0006B , 0x00327 },
+ { 0x00139 , 0x0004C , 0x00301 },
+ { 0x0013A , 0x0006C , 0x00301 },
+ { 0x0013B , 0x0004C , 0x00327 },
+ { 0x0013C , 0x0006C , 0x00327 },
+ { 0x0013D , 0x0004C , 0x0030C },
+ { 0x0013E , 0x0006C , 0x0030C },
+ { 0x00143 , 0x0004E , 0x00301 },
+ { 0x00144 , 0x0006E , 0x00301 },
+ { 0x00145 , 0x0004E , 0x00327 },
+ { 0x00146 , 0x0006E , 0x00327 },
+ { 0x00147 , 0x0004E , 0x0030C },
+ { 0x00148 , 0x0006E , 0x0030C },
+ { 0x0014C , 0x0004F , 0x00304 },
+ { 0x0014D , 0x0006F , 0x00304 },
+ { 0x0014E , 0x0004F , 0x00306 },
+ { 0x0014F , 0x0006F , 0x00306 },
+ { 0x00150 , 0x0004F , 0x0030B },
+ { 0x00151 , 0x0006F , 0x0030B },
+ { 0x00154 , 0x00052 , 0x00301 },
+ { 0x00155 , 0x00072 , 0x00301 },
+ { 0x00156 , 0x00052 , 0x00327 },
+ { 0x00157 , 0x00072 , 0x00327 },
+ { 0x00158 , 0x00052 , 0x0030C },
+ { 0x00159 , 0x00072 , 0x0030C },
+ { 0x0015A , 0x00053 , 0x00301 },
+ { 0x0015B , 0x00073 , 0x00301 },
+ { 0x0015C , 0x00053 , 0x00302 },
+ { 0x0015D , 0x00073 , 0x00302 },
+ { 0x0015E , 0x00053 , 0x00327 },
+ { 0x0015F , 0x00073 , 0x00327 },
+ { 0x00160 , 0x00053 , 0x0030C },
+ { 0x00161 , 0x00073 , 0x0030C },
+ { 0x00162 , 0x00054 , 0x00327 },
+ { 0x00163 , 0x00074 , 0x00327 },
+ { 0x00164 , 0x00054 , 0x0030C },
+ { 0x00165 , 0x00074 , 0x0030C },
+ { 0x00168 , 0x00055 , 0x00303 },
+ { 0x00169 , 0x00075 , 0x00303 },
+ { 0x0016A , 0x00055 , 0x00304 },
+ { 0x0016B , 0x00075 , 0x00304 },
+ { 0x0016C , 0x00055 , 0x00306 },
+ { 0x0016D , 0x00075 , 0x00306 },
+ { 0x0016E , 0x00055 , 0x0030A },
+ { 0x0016F , 0x00075 , 0x0030A },
+ { 0x00170 , 0x00055 , 0x0030B },
+ { 0x00171 , 0x00075 , 0x0030B },
+ { 0x00172 , 0x00055 , 0x00328 },
+ { 0x00173 , 0x00075 , 0x00328 },
+ { 0x00174 , 0x00057 , 0x00302 },
+ { 0x00175 , 0x00077 , 0x00302 },
+ { 0x00176 , 0x00059 , 0x00302 },
+ { 0x00177 , 0x00079 , 0x00302 },
+ { 0x00178 , 0x00059 , 0x00308 },
+ { 0x00179 , 0x0005A , 0x00301 },
+ { 0x0017A , 0x0007A , 0x00301 },
+ { 0x0017B , 0x0005A , 0x00307 },
+ { 0x0017C , 0x0007A , 0x00307 },
+ { 0x0017D , 0x0005A , 0x0030C },
+ { 0x0017E , 0x0007A , 0x0030C },
+ { 0x001A0 , 0x0004F , 0x0031B },
+ { 0x001A1 , 0x0006F , 0x0031B },
+ { 0x001AF , 0x00055 , 0x0031B },
+ { 0x001B0 , 0x00075 , 0x0031B },
+ { 0x001CD , 0x00041 , 0x0030C },
+ { 0x001CE , 0x00061 , 0x0030C },
+ { 0x001CF , 0x00049 , 0x0030C },
+ { 0x001D0 , 0x00069 , 0x0030C },
+ { 0x001D1 , 0x0004F , 0x0030C },
+ { 0x001D2 , 0x0006F , 0x0030C },
+ { 0x001D3 , 0x00055 , 0x0030C },
+ { 0x001D4 , 0x00075 , 0x0030C },
+ { 0x001D5 , 0x000DC , 0x00304 },
+ { 0x001D6 , 0x000FC , 0x00304 },
+ { 0x001D7 , 0x000DC , 0x00301 },
+ { 0x001D8 , 0x000FC , 0x00301 },
+ { 0x001D9 , 0x000DC , 0x0030C },
+ { 0x001DA , 0x000FC , 0x0030C },
+ { 0x001DB , 0x000DC , 0x00300 },
+ { 0x001DC , 0x000FC , 0x00300 },
+ { 0x001DE , 0x000C4 , 0x00304 },
+ { 0x001DF , 0x000E4 , 0x00304 },
+ { 0x001E0 , 0x00226 , 0x00304 },
+ { 0x001E1 , 0x00227 , 0x00304 },
+ { 0x001E2 , 0x000C6 , 0x00304 },
+ { 0x001E3 , 0x000E6 , 0x00304 },
+ { 0x001E6 , 0x00047 , 0x0030C },
+ { 0x001E7 , 0x00067 , 0x0030C },
+ { 0x001E8 , 0x0004B , 0x0030C },
+ { 0x001E9 , 0x0006B , 0x0030C },
+ { 0x001EA , 0x0004F , 0x00328 },
+ { 0x001EB , 0x0006F , 0x00328 },
+ { 0x001EC , 0x001EA , 0x00304 },
+ { 0x001ED , 0x001EB , 0x00304 },
+ { 0x001EE , 0x001B7 , 0x0030C },
+ { 0x001EF , 0x00292 , 0x0030C },
+ { 0x001F0 , 0x0006A , 0x0030C },
+ { 0x001F4 , 0x00047 , 0x00301 },
+ { 0x001F5 , 0x00067 , 0x00301 },
+ { 0x001F8 , 0x0004E , 0x00300 },
+ { 0x001F9 , 0x0006E , 0x00300 },
+ { 0x001FA , 0x000C5 , 0x00301 },
+ { 0x001FB , 0x000E5 , 0x00301 },
+ { 0x001FC , 0x000C6 , 0x00301 },
+ { 0x001FD , 0x000E6 , 0x00301 },
+ { 0x001FE , 0x000D8 , 0x00301 },
+ { 0x001FF , 0x000F8 , 0x00301 },
+ { 0x00200 , 0x00041 , 0x0030F },
+ { 0x00201 , 0x00061 , 0x0030F },
+ { 0x00202 , 0x00041 , 0x00311 },
+ { 0x00203 , 0x00061 , 0x00311 },
+ { 0x00204 , 0x00045 , 0x0030F },
+ { 0x00205 , 0x00065 , 0x0030F },
+ { 0x00206 , 0x00045 , 0x00311 },
+ { 0x00207 , 0x00065 , 0x00311 },
+ { 0x00208 , 0x00049 , 0x0030F },
+ { 0x00209 , 0x00069 , 0x0030F },
+ { 0x0020A , 0x00049 , 0x00311 },
+ { 0x0020B , 0x00069 , 0x00311 },
+ { 0x0020C , 0x0004F , 0x0030F },
+ { 0x0020D , 0x0006F , 0x0030F },
+ { 0x0020E , 0x0004F , 0x00311 },
+ { 0x0020F , 0x0006F , 0x00311 },
+ { 0x00210 , 0x00052 , 0x0030F },
+ { 0x00211 , 0x00072 , 0x0030F },
+ { 0x00212 , 0x00052 , 0x00311 },
+ { 0x00213 , 0x00072 , 0x00311 },
+ { 0x00214 , 0x00055 , 0x0030F },
+ { 0x00215 , 0x00075 , 0x0030F },
+ { 0x00216 , 0x00055 , 0x00311 },
+ { 0x00217 , 0x00075 , 0x00311 },
+ { 0x00218 , 0x00053 , 0x00326 },
+ { 0x00219 , 0x00073 , 0x00326 },
+ { 0x0021A , 0x00054 , 0x00326 },
+ { 0x0021B , 0x00074 , 0x00326 },
+ { 0x0021E , 0x00048 , 0x0030C },
+ { 0x0021F , 0x00068 , 0x0030C },
+ { 0x00226 , 0x00041 , 0x00307 },
+ { 0x00227 , 0x00061 , 0x00307 },
+ { 0x00228 , 0x00045 , 0x00327 },
+ { 0x00229 , 0x00065 , 0x00327 },
+ { 0x0022A , 0x000D6 , 0x00304 },
+ { 0x0022B , 0x000F6 , 0x00304 },
+ { 0x0022C , 0x000D5 , 0x00304 },
+ { 0x0022D , 0x000F5 , 0x00304 },
+ { 0x0022E , 0x0004F , 0x00307 },
+ { 0x0022F , 0x0006F , 0x00307 },
+ { 0x00230 , 0x0022E , 0x00304 },
+ { 0x00231 , 0x0022F , 0x00304 },
+ { 0x00232 , 0x00059 , 0x00304 },
+ { 0x00233 , 0x00079 , 0x00304 },
+ { 0x00385 , 0x000A8 , 0x00301 },
+ { 0x00386 , 0x00391 , 0x00301 },
+ { 0x00388 , 0x00395 , 0x00301 },
+ { 0x00389 , 0x00397 , 0x00301 },
+ { 0x0038A , 0x00399 , 0x00301 },
+ { 0x0038C , 0x0039F , 0x00301 },
+ { 0x0038E , 0x003A5 , 0x00301 },
+ { 0x0038F , 0x003A9 , 0x00301 },
+ { 0x00390 , 0x003CA , 0x00301 },
+ { 0x003AA , 0x00399 , 0x00308 },
+ { 0x003AB , 0x003A5 , 0x00308 },
+ { 0x003AC , 0x003B1 , 0x00301 },
+ { 0x003AD , 0x003B5 , 0x00301 },
+ { 0x003AE , 0x003B7 , 0x00301 },
+ { 0x003AF , 0x003B9 , 0x00301 },
+ { 0x003B0 , 0x003CB , 0x00301 },
+ { 0x003CA , 0x003B9 , 0x00308 },
+ { 0x003CB , 0x003C5 , 0x00308 },
+ { 0x003CC , 0x003BF , 0x00301 },
+ { 0x003CD , 0x003C5 , 0x00301 },
+ { 0x003CE , 0x003C9 , 0x00301 },
+ { 0x003D3 , 0x003D2 , 0x00301 },
+ { 0x003D4 , 0x003D2 , 0x00308 },
+ { 0x00400 , 0x00415 , 0x00300 },
+ { 0x00401 , 0x00415 , 0x00308 },
+ { 0x00403 , 0x00413 , 0x00301 },
+ { 0x00407 , 0x00406 , 0x00308 },
+ { 0x0040C , 0x0041A , 0x00301 },
+ { 0x0040D , 0x00418 , 0x00300 },
+ { 0x0040E , 0x00423 , 0x00306 },
+ { 0x00419 , 0x00418 , 0x00306 },
+ { 0x00439 , 0x00438 , 0x00306 },
+ { 0x00450 , 0x00435 , 0x00300 },
+ { 0x00451 , 0x00435 , 0x00308 },
+ { 0x00453 , 0x00433 , 0x00301 },
+ { 0x00457 , 0x00456 , 0x00308 },
+ { 0x0045C , 0x0043A , 0x00301 },
+ { 0x0045D , 0x00438 , 0x00300 },
+ { 0x0045E , 0x00443 , 0x00306 },
+ { 0x00476 , 0x00474 , 0x0030F },
+ { 0x00477 , 0x00475 , 0x0030F },
+ { 0x004C1 , 0x00416 , 0x00306 },
+ { 0x004C2 , 0x00436 , 0x00306 },
+ { 0x004D0 , 0x00410 , 0x00306 },
+ { 0x004D1 , 0x00430 , 0x00306 },
+ { 0x004D2 , 0x00410 , 0x00308 },
+ { 0x004D3 , 0x00430 , 0x00308 },
+ { 0x004D6 , 0x00415 , 0x00306 },
+ { 0x004D7 , 0x00435 , 0x00306 },
+ { 0x004DA , 0x004D8 , 0x00308 },
+ { 0x004DB , 0x004D9 , 0x00308 },
+ { 0x004DC , 0x00416 , 0x00308 },
+ { 0x004DD , 0x00436 , 0x00308 },
+ { 0x004DE , 0x00417 , 0x00308 },
+ { 0x004DF , 0x00437 , 0x00308 },
+ { 0x004E2 , 0x00418 , 0x00304 },
+ { 0x004E3 , 0x00438 , 0x00304 },
+ { 0x004E4 , 0x00418 , 0x00308 },
+ { 0x004E5 , 0x00438 , 0x00308 },
+ { 0x004E6 , 0x0041E , 0x00308 },
+ { 0x004E7 , 0x0043E , 0x00308 },
+ { 0x004EA , 0x004E8 , 0x00308 },
+ { 0x004EB , 0x004E9 , 0x00308 },
+ { 0x004EC , 0x0042D , 0x00308 },
+ { 0x004ED , 0x0044D , 0x00308 },
+ { 0x004EE , 0x00423 , 0x00304 },
+ { 0x004EF , 0x00443 , 0x00304 },
+ { 0x004F0 , 0x00423 , 0x00308 },
+ { 0x004F1 , 0x00443 , 0x00308 },
+ { 0x004F2 , 0x00423 , 0x0030B },
+ { 0x004F3 , 0x00443 , 0x0030B },
+ { 0x004F4 , 0x00427 , 0x00308 },
+ { 0x004F5 , 0x00447 , 0x00308 },
+ { 0x004F8 , 0x0042B , 0x00308 },
+ { 0x004F9 , 0x0044B , 0x00308 },
+ { 0x00622 , 0x00627 , 0x00653 },
+ { 0x00623 , 0x00627 , 0x00654 },
+ { 0x00624 , 0x00648 , 0x00654 },
+ { 0x00625 , 0x00627 , 0x00655 },
+ { 0x00626 , 0x0064A , 0x00654 },
+ { 0x006C0 , 0x006D5 , 0x00654 },
+ { 0x006C2 , 0x006C1 , 0x00654 },
+ { 0x006D3 , 0x006D2 , 0x00654 },
+ { 0x00929 , 0x00928 , 0x0093C },
+ { 0x00931 , 0x00930 , 0x0093C },
+ { 0x00934 , 0x00933 , 0x0093C },
+ { 0x009CB , 0x009C7 , 0x009BE },
+ { 0x009CC , 0x009C7 , 0x009D7 },
+ { 0x00B48 , 0x00B47 , 0x00B56 },
+ { 0x00B4B , 0x00B47 , 0x00B3E },
+ { 0x00B4C , 0x00B47 , 0x00B57 },
+ { 0x00B94 , 0x00B92 , 0x00BD7 },
+ { 0x00BCA , 0x00BC6 , 0x00BBE },
+ { 0x00BCB , 0x00BC7 , 0x00BBE },
+ { 0x00BCC , 0x00BC6 , 0x00BD7 },
+ { 0x00C48 , 0x00C46 , 0x00C56 },
+ { 0x00CC0 , 0x00CBF , 0x00CD5 },
+ { 0x00CC7 , 0x00CC6 , 0x00CD5 },
+ { 0x00CC8 , 0x00CC6 , 0x00CD6 },
+ { 0x00CCA , 0x00CC6 , 0x00CC2 },
+ { 0x00CCB , 0x00CCA , 0x00CD5 },
+ { 0x00D4A , 0x00D46 , 0x00D3E },
+ { 0x00D4B , 0x00D47 , 0x00D3E },
+ { 0x00D4C , 0x00D46 , 0x00D57 },
+ { 0x00DDA , 0x00DD9 , 0x00DCA },
+ { 0x00DDC , 0x00DD9 , 0x00DCF },
+ { 0x00DDD , 0x00DDC , 0x00DCA },
+ { 0x00DDE , 0x00DD9 , 0x00DDF },
+ { 0x01026 , 0x01025 , 0x0102E },
+ { 0x01B06 , 0x01B05 , 0x01B35 },
+ { 0x01B08 , 0x01B07 , 0x01B35 },
+ { 0x01B0A , 0x01B09 , 0x01B35 },
+ { 0x01B0C , 0x01B0B , 0x01B35 },
+ { 0x01B0E , 0x01B0D , 0x01B35 },
+ { 0x01B12 , 0x01B11 , 0x01B35 },
+ { 0x01B3B , 0x01B3A , 0x01B35 },
+ { 0x01B3D , 0x01B3C , 0x01B35 },
+ { 0x01B40 , 0x01B3E , 0x01B35 },
+ { 0x01B41 , 0x01B3F , 0x01B35 },
+ { 0x01B43 , 0x01B42 , 0x01B35 },
+ { 0x01E00 , 0x00041 , 0x00325 },
+ { 0x01E01 , 0x00061 , 0x00325 },
+ { 0x01E02 , 0x00042 , 0x00307 },
+ { 0x01E03 , 0x00062 , 0x00307 },
+ { 0x01E04 , 0x00042 , 0x00323 },
+ { 0x01E05 , 0x00062 , 0x00323 },
+ { 0x01E06 , 0x00042 , 0x00331 },
+ { 0x01E07 , 0x00062 , 0x00331 },
+ { 0x01E08 , 0x000C7 , 0x00301 },
+ { 0x01E09 , 0x000E7 , 0x00301 },
+ { 0x01E0A , 0x00044 , 0x00307 },
+ { 0x01E0B , 0x00064 , 0x00307 },
+ { 0x01E0C , 0x00044 , 0x00323 },
+ { 0x01E0D , 0x00064 , 0x00323 },
+ { 0x01E0E , 0x00044 , 0x00331 },
+ { 0x01E0F , 0x00064 , 0x00331 },
+ { 0x01E10 , 0x00044 , 0x00327 },
+ { 0x01E11 , 0x00064 , 0x00327 },
+ { 0x01E12 , 0x00044 , 0x0032D },
+ { 0x01E13 , 0x00064 , 0x0032D },
+ { 0x01E14 , 0x00112 , 0x00300 },
+ { 0x01E15 , 0x00113 , 0x00300 },
+ { 0x01E16 , 0x00112 , 0x00301 },
+ { 0x01E17 , 0x00113 , 0x00301 },
+ { 0x01E18 , 0x00045 , 0x0032D },
+ { 0x01E19 , 0x00065 , 0x0032D },
+ { 0x01E1A , 0x00045 , 0x00330 },
+ { 0x01E1B , 0x00065 , 0x00330 },
+ { 0x01E1C , 0x00228 , 0x00306 },
+ { 0x01E1D , 0x00229 , 0x00306 },
+ { 0x01E1E , 0x00046 , 0x00307 },
+ { 0x01E1F , 0x00066 , 0x00307 },
+ { 0x01E20 , 0x00047 , 0x00304 },
+ { 0x01E21 , 0x00067 , 0x00304 },
+ { 0x01E22 , 0x00048 , 0x00307 },
+ { 0x01E23 , 0x00068 , 0x00307 },
+ { 0x01E24 , 0x00048 , 0x00323 },
+ { 0x01E25 , 0x00068 , 0x00323 },
+ { 0x01E26 , 0x00048 , 0x00308 },
+ { 0x01E27 , 0x00068 , 0x00308 },
+ { 0x01E28 , 0x00048 , 0x00327 },
+ { 0x01E29 , 0x00068 , 0x00327 },
+ { 0x01E2A , 0x00048 , 0x0032E },
+ { 0x01E2B , 0x00068 , 0x0032E },
+ { 0x01E2C , 0x00049 , 0x00330 },
+ { 0x01E2D , 0x00069 , 0x00330 },
+ { 0x01E2E , 0x000CF , 0x00301 },
+ { 0x01E2F , 0x000EF , 0x00301 },
+ { 0x01E30 , 0x0004B , 0x00301 },
+ { 0x01E31 , 0x0006B , 0x00301 },
+ { 0x01E32 , 0x0004B , 0x00323 },
+ { 0x01E33 , 0x0006B , 0x00323 },
+ { 0x01E34 , 0x0004B , 0x00331 },
+ { 0x01E35 , 0x0006B , 0x00331 },
+ { 0x01E36 , 0x0004C , 0x00323 },
+ { 0x01E37 , 0x0006C , 0x00323 },
+ { 0x01E38 , 0x01E36 , 0x00304 },
+ { 0x01E39 , 0x01E37 , 0x00304 },
+ { 0x01E3A , 0x0004C , 0x00331 },
+ { 0x01E3B , 0x0006C , 0x00331 },
+ { 0x01E3C , 0x0004C , 0x0032D },
+ { 0x01E3D , 0x0006C , 0x0032D },
+ { 0x01E3E , 0x0004D , 0x00301 },
+ { 0x01E3F , 0x0006D , 0x00301 },
+ { 0x01E40 , 0x0004D , 0x00307 },
+ { 0x01E41 , 0x0006D , 0x00307 },
+ { 0x01E42 , 0x0004D , 0x00323 },
+ { 0x01E43 , 0x0006D , 0x00323 },
+ { 0x01E44 , 0x0004E , 0x00307 },
+ { 0x01E45 , 0x0006E , 0x00307 },
+ { 0x01E46 , 0x0004E , 0x00323 },
+ { 0x01E47 , 0x0006E , 0x00323 },
+ { 0x01E48 , 0x0004E , 0x00331 },
+ { 0x01E49 , 0x0006E , 0x00331 },
+ { 0x01E4A , 0x0004E , 0x0032D },
+ { 0x01E4B , 0x0006E , 0x0032D },
+ { 0x01E4C , 0x000D5 , 0x00301 },
+ { 0x01E4D , 0x000F5 , 0x00301 },
+ { 0x01E4E , 0x000D5 , 0x00308 },
+ { 0x01E4F , 0x000F5 , 0x00308 },
+ { 0x01E50 , 0x0014C , 0x00300 },
+ { 0x01E51 , 0x0014D , 0x00300 },
+ { 0x01E52 , 0x0014C , 0x00301 },
+ { 0x01E53 , 0x0014D , 0x00301 },
+ { 0x01E54 , 0x00050 , 0x00301 },
+ { 0x01E55 , 0x00070 , 0x00301 },
+ { 0x01E56 , 0x00050 , 0x00307 },
+ { 0x01E57 , 0x00070 , 0x00307 },
+ { 0x01E58 , 0x00052 , 0x00307 },
+ { 0x01E59 , 0x00072 , 0x00307 },
+ { 0x01E5A , 0x00052 , 0x00323 },
+ { 0x01E5B , 0x00072 , 0x00323 },
+ { 0x01E5C , 0x01E5A , 0x00304 },
+ { 0x01E5D , 0x01E5B , 0x00304 },
+ { 0x01E5E , 0x00052 , 0x00331 },
+ { 0x01E5F , 0x00072 , 0x00331 },
+ { 0x01E60 , 0x00053 , 0x00307 },
+ { 0x01E61 , 0x00073 , 0x00307 },
+ { 0x01E62 , 0x00053 , 0x00323 },
+ { 0x01E63 , 0x00073 , 0x00323 },
+ { 0x01E64 , 0x0015A , 0x00307 },
+ { 0x01E65 , 0x0015B , 0x00307 },
+ { 0x01E66 , 0x00160 , 0x00307 },
+ { 0x01E67 , 0x00161 , 0x00307 },
+ { 0x01E68 , 0x01E62 , 0x00307 },
+ { 0x01E69 , 0x01E63 , 0x00307 },
+ { 0x01E6A , 0x00054 , 0x00307 },
+ { 0x01E6B , 0x00074 , 0x00307 },
+ { 0x01E6C , 0x00054 , 0x00323 },
+ { 0x01E6D , 0x00074 , 0x00323 },
+ { 0x01E6E , 0x00054 , 0x00331 },
+ { 0x01E6F , 0x00074 , 0x00331 },
+ { 0x01E70 , 0x00054 , 0x0032D },
+ { 0x01E71 , 0x00074 , 0x0032D },
+ { 0x01E72 , 0x00055 , 0x00324 },
+ { 0x01E73 , 0x00075 , 0x00324 },
+ { 0x01E74 , 0x00055 , 0x00330 },
+ { 0x01E75 , 0x00075 , 0x00330 },
+ { 0x01E76 , 0x00055 , 0x0032D },
+ { 0x01E77 , 0x00075 , 0x0032D },
+ { 0x01E78 , 0x00168 , 0x00301 },
+ { 0x01E79 , 0x00169 , 0x00301 },
+ { 0x01E7A , 0x0016A , 0x00308 },
+ { 0x01E7B , 0x0016B , 0x00308 },
+ { 0x01E7C , 0x00056 , 0x00303 },
+ { 0x01E7D , 0x00076 , 0x00303 },
+ { 0x01E7E , 0x00056 , 0x00323 },
+ { 0x01E7F , 0x00076 , 0x00323 },
+ { 0x01E80 , 0x00057 , 0x00300 },
+ { 0x01E81 , 0x00077 , 0x00300 },
+ { 0x01E82 , 0x00057 , 0x00301 },
+ { 0x01E83 , 0x00077 , 0x00301 },
+ { 0x01E84 , 0x00057 , 0x00308 },
+ { 0x01E85 , 0x00077 , 0x00308 },
+ { 0x01E86 , 0x00057 , 0x00307 },
+ { 0x01E87 , 0x00077 , 0x00307 },
+ { 0x01E88 , 0x00057 , 0x00323 },
+ { 0x01E89 , 0x00077 , 0x00323 },
+ { 0x01E8A , 0x00058 , 0x00307 },
+ { 0x01E8B , 0x00078 , 0x00307 },
+ { 0x01E8C , 0x00058 , 0x00308 },
+ { 0x01E8D , 0x00078 , 0x00308 },
+ { 0x01E8E , 0x00059 , 0x00307 },
+ { 0x01E8F , 0x00079 , 0x00307 },
+ { 0x01E90 , 0x0005A , 0x00302 },
+ { 0x01E91 , 0x0007A , 0x00302 },
+ { 0x01E92 , 0x0005A , 0x00323 },
+ { 0x01E93 , 0x0007A , 0x00323 },
+ { 0x01E94 , 0x0005A , 0x00331 },
+ { 0x01E95 , 0x0007A , 0x00331 },
+ { 0x01E96 , 0x00068 , 0x00331 },
+ { 0x01E97 , 0x00074 , 0x00308 },
+ { 0x01E98 , 0x00077 , 0x0030A },
+ { 0x01E99 , 0x00079 , 0x0030A },
+ { 0x01E9B , 0x0017F , 0x00307 },
+ { 0x01EA0 , 0x00041 , 0x00323 },
+ { 0x01EA1 , 0x00061 , 0x00323 },
+ { 0x01EA2 , 0x00041 , 0x00309 },
+ { 0x01EA3 , 0x00061 , 0x00309 },
+ { 0x01EA4 , 0x000C2 , 0x00301 },
+ { 0x01EA5 , 0x000E2 , 0x00301 },
+ { 0x01EA6 , 0x000C2 , 0x00300 },
+ { 0x01EA7 , 0x000E2 , 0x00300 },
+ { 0x01EA8 , 0x000C2 , 0x00309 },
+ { 0x01EA9 , 0x000E2 , 0x00309 },
+ { 0x01EAA , 0x000C2 , 0x00303 },
+ { 0x01EAB , 0x000E2 , 0x00303 },
+ { 0x01EAC , 0x01EA0 , 0x00302 },
+ { 0x01EAD , 0x01EA1 , 0x00302 },
+ { 0x01EAE , 0x00102 , 0x00301 },
+ { 0x01EAF , 0x00103 , 0x00301 },
+ { 0x01EB0 , 0x00102 , 0x00300 },
+ { 0x01EB1 , 0x00103 , 0x00300 },
+ { 0x01EB2 , 0x00102 , 0x00309 },
+ { 0x01EB3 , 0x00103 , 0x00309 },
+ { 0x01EB4 , 0x00102 , 0x00303 },
+ { 0x01EB5 , 0x00103 , 0x00303 },
+ { 0x01EB6 , 0x01EA0 , 0x00306 },
+ { 0x01EB7 , 0x01EA1 , 0x00306 },
+ { 0x01EB8 , 0x00045 , 0x00323 },
+ { 0x01EB9 , 0x00065 , 0x00323 },
+ { 0x01EBA , 0x00045 , 0x00309 },
+ { 0x01EBB , 0x00065 , 0x00309 },
+ { 0x01EBC , 0x00045 , 0x00303 },
+ { 0x01EBD , 0x00065 , 0x00303 },
+ { 0x01EBE , 0x000CA , 0x00301 },
+ { 0x01EBF , 0x000EA , 0x00301 },
+ { 0x01EC0 , 0x000CA , 0x00300 },
+ { 0x01EC1 , 0x000EA , 0x00300 },
+ { 0x01EC2 , 0x000CA , 0x00309 },
+ { 0x01EC3 , 0x000EA , 0x00309 },
+ { 0x01EC4 , 0x000CA , 0x00303 },
+ { 0x01EC5 , 0x000EA , 0x00303 },
+ { 0x01EC6 , 0x01EB8 , 0x00302 },
+ { 0x01EC7 , 0x01EB9 , 0x00302 },
+ { 0x01EC8 , 0x00049 , 0x00309 },
+ { 0x01EC9 , 0x00069 , 0x00309 },
+ { 0x01ECA , 0x00049 , 0x00323 },
+ { 0x01ECB , 0x00069 , 0x00323 },
+ { 0x01ECC , 0x0004F , 0x00323 },
+ { 0x01ECD , 0x0006F , 0x00323 },
+ { 0x01ECE , 0x0004F , 0x00309 },
+ { 0x01ECF , 0x0006F , 0x00309 },
+ { 0x01ED0 , 0x000D4 , 0x00301 },
+ { 0x01ED1 , 0x000F4 , 0x00301 },
+ { 0x01ED2 , 0x000D4 , 0x00300 },
+ { 0x01ED3 , 0x000F4 , 0x00300 },
+ { 0x01ED4 , 0x000D4 , 0x00309 },
+ { 0x01ED5 , 0x000F4 , 0x00309 },
+ { 0x01ED6 , 0x000D4 , 0x00303 },
+ { 0x01ED7 , 0x000F4 , 0x00303 },
+ { 0x01ED8 , 0x01ECC , 0x00302 },
+ { 0x01ED9 , 0x01ECD , 0x00302 },
+ { 0x01EDA , 0x001A0 , 0x00301 },
+ { 0x01EDB , 0x001A1 , 0x00301 },
+ { 0x01EDC , 0x001A0 , 0x00300 },
+ { 0x01EDD , 0x001A1 , 0x00300 },
+ { 0x01EDE , 0x001A0 , 0x00309 },
+ { 0x01EDF , 0x001A1 , 0x00309 },
+ { 0x01EE0 , 0x001A0 , 0x00303 },
+ { 0x01EE1 , 0x001A1 , 0x00303 },
+ { 0x01EE2 , 0x001A0 , 0x00323 },
+ { 0x01EE3 , 0x001A1 , 0x00323 },
+ { 0x01EE4 , 0x00055 , 0x00323 },
+ { 0x01EE5 , 0x00075 , 0x00323 },
+ { 0x01EE6 , 0x00055 , 0x00309 },
+ { 0x01EE7 , 0x00075 , 0x00309 },
+ { 0x01EE8 , 0x001AF , 0x00301 },
+ { 0x01EE9 , 0x001B0 , 0x00301 },
+ { 0x01EEA , 0x001AF , 0x00300 },
+ { 0x01EEB , 0x001B0 , 0x00300 },
+ { 0x01EEC , 0x001AF , 0x00309 },
+ { 0x01EED , 0x001B0 , 0x00309 },
+ { 0x01EEE , 0x001AF , 0x00303 },
+ { 0x01EEF , 0x001B0 , 0x00303 },
+ { 0x01EF0 , 0x001AF , 0x00323 },
+ { 0x01EF1 , 0x001B0 , 0x00323 },
+ { 0x01EF2 , 0x00059 , 0x00300 },
+ { 0x01EF3 , 0x00079 , 0x00300 },
+ { 0x01EF4 , 0x00059 , 0x00323 },
+ { 0x01EF5 , 0x00079 , 0x00323 },
+ { 0x01EF6 , 0x00059 , 0x00309 },
+ { 0x01EF7 , 0x00079 , 0x00309 },
+ { 0x01EF8 , 0x00059 , 0x00303 },
+ { 0x01EF9 , 0x00079 , 0x00303 },
+ { 0x01F00 , 0x003B1 , 0x00313 },
+ { 0x01F01 , 0x003B1 , 0x00314 },
+ { 0x01F02 , 0x01F00 , 0x00300 },
+ { 0x01F03 , 0x01F01 , 0x00300 },
+ { 0x01F04 , 0x01F00 , 0x00301 },
+ { 0x01F05 , 0x01F01 , 0x00301 },
+ { 0x01F06 , 0x01F00 , 0x00342 },
+ { 0x01F07 , 0x01F01 , 0x00342 },
+ { 0x01F08 , 0x00391 , 0x00313 },
+ { 0x01F09 , 0x00391 , 0x00314 },
+ { 0x01F0A , 0x01F08 , 0x00300 },
+ { 0x01F0B , 0x01F09 , 0x00300 },
+ { 0x01F0C , 0x01F08 , 0x00301 },
+ { 0x01F0D , 0x01F09 , 0x00301 },
+ { 0x01F0E , 0x01F08 , 0x00342 },
+ { 0x01F0F , 0x01F09 , 0x00342 },
+ { 0x01F10 , 0x003B5 , 0x00313 },
+ { 0x01F11 , 0x003B5 , 0x00314 },
+ { 0x01F12 , 0x01F10 , 0x00300 },
+ { 0x01F13 , 0x01F11 , 0x00300 },
+ { 0x01F14 , 0x01F10 , 0x00301 },
+ { 0x01F15 , 0x01F11 , 0x00301 },
+ { 0x01F18 , 0x00395 , 0x00313 },
+ { 0x01F19 , 0x00395 , 0x00314 },
+ { 0x01F1A , 0x01F18 , 0x00300 },
+ { 0x01F1B , 0x01F19 , 0x00300 },
+ { 0x01F1C , 0x01F18 , 0x00301 },
+ { 0x01F1D , 0x01F19 , 0x00301 },
+ { 0x01F20 , 0x003B7 , 0x00313 },
+ { 0x01F21 , 0x003B7 , 0x00314 },
+ { 0x01F22 , 0x01F20 , 0x00300 },
+ { 0x01F23 , 0x01F21 , 0x00300 },
+ { 0x01F24 , 0x01F20 , 0x00301 },
+ { 0x01F25 , 0x01F21 , 0x00301 },
+ { 0x01F26 , 0x01F20 , 0x00342 },
+ { 0x01F27 , 0x01F21 , 0x00342 },
+ { 0x01F28 , 0x00397 , 0x00313 },
+ { 0x01F29 , 0x00397 , 0x00314 },
+ { 0x01F2A , 0x01F28 , 0x00300 },
+ { 0x01F2B , 0x01F29 , 0x00300 },
+ { 0x01F2C , 0x01F28 , 0x00301 },
+ { 0x01F2D , 0x01F29 , 0x00301 },
+ { 0x01F2E , 0x01F28 , 0x00342 },
+ { 0x01F2F , 0x01F29 , 0x00342 },
+ { 0x01F30 , 0x003B9 , 0x00313 },
+ { 0x01F31 , 0x003B9 , 0x00314 },
+ { 0x01F32 , 0x01F30 , 0x00300 },
+ { 0x01F33 , 0x01F31 , 0x00300 },
+ { 0x01F34 , 0x01F30 , 0x00301 },
+ { 0x01F35 , 0x01F31 , 0x00301 },
+ { 0x01F36 , 0x01F30 , 0x00342 },
+ { 0x01F37 , 0x01F31 , 0x00342 },
+ { 0x01F38 , 0x00399 , 0x00313 },
+ { 0x01F39 , 0x00399 , 0x00314 },
+ { 0x01F3A , 0x01F38 , 0x00300 },
+ { 0x01F3B , 0x01F39 , 0x00300 },
+ { 0x01F3C , 0x01F38 , 0x00301 },
+ { 0x01F3D , 0x01F39 , 0x00301 },
+ { 0x01F3E , 0x01F38 , 0x00342 },
+ { 0x01F3F , 0x01F39 , 0x00342 },
+ { 0x01F40 , 0x003BF , 0x00313 },
+ { 0x01F41 , 0x003BF , 0x00314 },
+ { 0x01F42 , 0x01F40 , 0x00300 },
+ { 0x01F43 , 0x01F41 , 0x00300 },
+ { 0x01F44 , 0x01F40 , 0x00301 },
+ { 0x01F45 , 0x01F41 , 0x00301 },
+ { 0x01F48 , 0x0039F , 0x00313 },
+ { 0x01F49 , 0x0039F , 0x00314 },
+ { 0x01F4A , 0x01F48 , 0x00300 },
+ { 0x01F4B , 0x01F49 , 0x00300 },
+ { 0x01F4C , 0x01F48 , 0x00301 },
+ { 0x01F4D , 0x01F49 , 0x00301 },
+ { 0x01F50 , 0x003C5 , 0x00313 },
+ { 0x01F51 , 0x003C5 , 0x00314 },
+ { 0x01F52 , 0x01F50 , 0x00300 },
+ { 0x01F53 , 0x01F51 , 0x00300 },
+ { 0x01F54 , 0x01F50 , 0x00301 },
+ { 0x01F55 , 0x01F51 , 0x00301 },
+ { 0x01F56 , 0x01F50 , 0x00342 },
+ { 0x01F57 , 0x01F51 , 0x00342 },
+ { 0x01F59 , 0x003A5 , 0x00314 },
+ { 0x01F5B , 0x01F59 , 0x00300 },
+ { 0x01F5D , 0x01F59 , 0x00301 },
+ { 0x01F5F , 0x01F59 , 0x00342 },
+ { 0x01F60 , 0x003C9 , 0x00313 },
+ { 0x01F61 , 0x003C9 , 0x00314 },
+ { 0x01F62 , 0x01F60 , 0x00300 },
+ { 0x01F63 , 0x01F61 , 0x00300 },
+ { 0x01F64 , 0x01F60 , 0x00301 },
+ { 0x01F65 , 0x01F61 , 0x00301 },
+ { 0x01F66 , 0x01F60 , 0x00342 },
+ { 0x01F67 , 0x01F61 , 0x00342 },
+ { 0x01F68 , 0x003A9 , 0x00313 },
+ { 0x01F69 , 0x003A9 , 0x00314 },
+ { 0x01F6A , 0x01F68 , 0x00300 },
+ { 0x01F6B , 0x01F69 , 0x00300 },
+ { 0x01F6C , 0x01F68 , 0x00301 },
+ { 0x01F6D , 0x01F69 , 0x00301 },
+ { 0x01F6E , 0x01F68 , 0x00342 },
+ { 0x01F6F , 0x01F69 , 0x00342 },
+ { 0x01F70 , 0x003B1 , 0x00300 },
+ { 0x01F72 , 0x003B5 , 0x00300 },
+ { 0x01F74 , 0x003B7 , 0x00300 },
+ { 0x01F76 , 0x003B9 , 0x00300 },
+ { 0x01F78 , 0x003BF , 0x00300 },
+ { 0x01F7A , 0x003C5 , 0x00300 },
+ { 0x01F7C , 0x003C9 , 0x00300 },
+ { 0x01F80 , 0x01F00 , 0x00345 },
+ { 0x01F81 , 0x01F01 , 0x00345 },
+ { 0x01F82 , 0x01F02 , 0x00345 },
+ { 0x01F83 , 0x01F03 , 0x00345 },
+ { 0x01F84 , 0x01F04 , 0x00345 },
+ { 0x01F85 , 0x01F05 , 0x00345 },
+ { 0x01F86 , 0x01F06 , 0x00345 },
+ { 0x01F87 , 0x01F07 , 0x00345 },
+ { 0x01F88 , 0x01F08 , 0x00345 },
+ { 0x01F89 , 0x01F09 , 0x00345 },
+ { 0x01F8A , 0x01F0A , 0x00345 },
+ { 0x01F8B , 0x01F0B , 0x00345 },
+ { 0x01F8C , 0x01F0C , 0x00345 },
+ { 0x01F8D , 0x01F0D , 0x00345 },
+ { 0x01F8E , 0x01F0E , 0x00345 },
+ { 0x01F8F , 0x01F0F , 0x00345 },
+ { 0x01F90 , 0x01F20 , 0x00345 },
+ { 0x01F91 , 0x01F21 , 0x00345 },
+ { 0x01F92 , 0x01F22 , 0x00345 },
+ { 0x01F93 , 0x01F23 , 0x00345 },
+ { 0x01F94 , 0x01F24 , 0x00345 },
+ { 0x01F95 , 0x01F25 , 0x00345 },
+ { 0x01F96 , 0x01F26 , 0x00345 },
+ { 0x01F97 , 0x01F27 , 0x00345 },
+ { 0x01F98 , 0x01F28 , 0x00345 },
+ { 0x01F99 , 0x01F29 , 0x00345 },
+ { 0x01F9A , 0x01F2A , 0x00345 },
+ { 0x01F9B , 0x01F2B , 0x00345 },
+ { 0x01F9C , 0x01F2C , 0x00345 },
+ { 0x01F9D , 0x01F2D , 0x00345 },
+ { 0x01F9E , 0x01F2E , 0x00345 },
+ { 0x01F9F , 0x01F2F , 0x00345 },
+ { 0x01FA0 , 0x01F60 , 0x00345 },
+ { 0x01FA1 , 0x01F61 , 0x00345 },
+ { 0x01FA2 , 0x01F62 , 0x00345 },
+ { 0x01FA3 , 0x01F63 , 0x00345 },
+ { 0x01FA4 , 0x01F64 , 0x00345 },
+ { 0x01FA5 , 0x01F65 , 0x00345 },
+ { 0x01FA6 , 0x01F66 , 0x00345 },
+ { 0x01FA7 , 0x01F67 , 0x00345 },
+ { 0x01FA8 , 0x01F68 , 0x00345 },
+ { 0x01FA9 , 0x01F69 , 0x00345 },
+ { 0x01FAA , 0x01F6A , 0x00345 },
+ { 0x01FAB , 0x01F6B , 0x00345 },
+ { 0x01FAC , 0x01F6C , 0x00345 },
+ { 0x01FAD , 0x01F6D , 0x00345 },
+ { 0x01FAE , 0x01F6E , 0x00345 },
+ { 0x01FAF , 0x01F6F , 0x00345 },
+ { 0x01FB0 , 0x003B1 , 0x00306 },
+ { 0x01FB1 , 0x003B1 , 0x00304 },
+ { 0x01FB2 , 0x01F70 , 0x00345 },
+ { 0x01FB3 , 0x003B1 , 0x00345 },
+ { 0x01FB4 , 0x003AC , 0x00345 },
+ { 0x01FB6 , 0x003B1 , 0x00342 },
+ { 0x01FB7 , 0x01FB6 , 0x00345 },
+ { 0x01FB8 , 0x00391 , 0x00306 },
+ { 0x01FB9 , 0x00391 , 0x00304 },
+ { 0x01FBA , 0x00391 , 0x00300 },
+ { 0x01FBC , 0x00391 , 0x00345 },
+ { 0x01FC1 , 0x000A8 , 0x00342 },
+ { 0x01FC2 , 0x01F74 , 0x00345 },
+ { 0x01FC3 , 0x003B7 , 0x00345 },
+ { 0x01FC4 , 0x003AE , 0x00345 },
+ { 0x01FC6 , 0x003B7 , 0x00342 },
+ { 0x01FC7 , 0x01FC6 , 0x00345 },
+ { 0x01FC8 , 0x00395 , 0x00300 },
+ { 0x01FCA , 0x00397 , 0x00300 },
+ { 0x01FCC , 0x00397 , 0x00345 },
+ { 0x01FCD , 0x01FBF , 0x00300 },
+ { 0x01FCE , 0x01FBF , 0x00301 },
+ { 0x01FCF , 0x01FBF , 0x00342 },
+ { 0x01FD0 , 0x003B9 , 0x00306 },
+ { 0x01FD1 , 0x003B9 , 0x00304 },
+ { 0x01FD2 , 0x003CA , 0x00300 },
+ { 0x01FD6 , 0x003B9 , 0x00342 },
+ { 0x01FD7 , 0x003CA , 0x00342 },
+ { 0x01FD8 , 0x00399 , 0x00306 },
+ { 0x01FD9 , 0x00399 , 0x00304 },
+ { 0x01FDA , 0x00399 , 0x00300 },
+ { 0x01FDD , 0x01FFE , 0x00300 },
+ { 0x01FDE , 0x01FFE , 0x00301 },
+ { 0x01FDF , 0x01FFE , 0x00342 },
+ { 0x01FE0 , 0x003C5 , 0x00306 },
+ { 0x01FE1 , 0x003C5 , 0x00304 },
+ { 0x01FE2 , 0x003CB , 0x00300 },
+ { 0x01FE4 , 0x003C1 , 0x00313 },
+ { 0x01FE5 , 0x003C1 , 0x00314 },
+ { 0x01FE6 , 0x003C5 , 0x00342 },
+ { 0x01FE7 , 0x003CB , 0x00342 },
+ { 0x01FE8 , 0x003A5 , 0x00306 },
+ { 0x01FE9 , 0x003A5 , 0x00304 },
+ { 0x01FEA , 0x003A5 , 0x00300 },
+ { 0x01FEC , 0x003A1 , 0x00314 },
+ { 0x01FED , 0x000A8 , 0x00300 },
+ { 0x01FF2 , 0x01F7C , 0x00345 },
+ { 0x01FF3 , 0x003C9 , 0x00345 },
+ { 0x01FF4 , 0x003CE , 0x00345 },
+ { 0x01FF6 , 0x003C9 , 0x00342 },
+ { 0x01FF7 , 0x01FF6 , 0x00345 },
+ { 0x01FF8 , 0x0039F , 0x00300 },
+ { 0x01FFA , 0x003A9 , 0x00300 },
+ { 0x01FFC , 0x003A9 , 0x00345 },
+ { 0x0219A , 0x02190 , 0x00338 },
+ { 0x0219B , 0x02192 , 0x00338 },
+ { 0x021AE , 0x02194 , 0x00338 },
+ { 0x021CD , 0x021D0 , 0x00338 },
+ { 0x021CE , 0x021D4 , 0x00338 },
+ { 0x021CF , 0x021D2 , 0x00338 },
+ { 0x02204 , 0x02203 , 0x00338 },
+ { 0x02209 , 0x02208 , 0x00338 },
+ { 0x0220C , 0x0220B , 0x00338 },
+ { 0x02224 , 0x02223 , 0x00338 },
+ { 0x02226 , 0x02225 , 0x00338 },
+ { 0x02241 , 0x0223C , 0x00338 },
+ { 0x02244 , 0x02243 , 0x00338 },
+ { 0x02247 , 0x02245 , 0x00338 },
+ { 0x02249 , 0x02248 , 0x00338 },
+ { 0x02260 , 0x0003D , 0x00338 },
+ { 0x02262 , 0x02261 , 0x00338 },
+ { 0x0226D , 0x0224D , 0x00338 },
+ { 0x0226E , 0x0003C , 0x00338 },
+ { 0x0226F , 0x0003E , 0x00338 },
+ { 0x02270 , 0x02264 , 0x00338 },
+ { 0x02271 , 0x02265 , 0x00338 },
+ { 0x02274 , 0x02272 , 0x00338 },
+ { 0x02275 , 0x02273 , 0x00338 },
+ { 0x02278 , 0x02276 , 0x00338 },
+ { 0x02279 , 0x02277 , 0x00338 },
+ { 0x02280 , 0x0227A , 0x00338 },
+ { 0x02281 , 0x0227B , 0x00338 },
+ { 0x02284 , 0x02282 , 0x00338 },
+ { 0x02285 , 0x02283 , 0x00338 },
+ { 0x02288 , 0x02286 , 0x00338 },
+ { 0x02289 , 0x02287 , 0x00338 },
+ { 0x022AC , 0x022A2 , 0x00338 },
+ { 0x022AD , 0x022A8 , 0x00338 },
+ { 0x022AE , 0x022A9 , 0x00338 },
+ { 0x022AF , 0x022AB , 0x00338 },
+ { 0x022E0 , 0x0227C , 0x00338 },
+ { 0x022E1 , 0x0227D , 0x00338 },
+ { 0x022E2 , 0x02291 , 0x00338 },
+ { 0x022E3 , 0x02292 , 0x00338 },
+ { 0x022EA , 0x022B2 , 0x00338 },
+ { 0x022EB , 0x022B3 , 0x00338 },
+ { 0x022EC , 0x022B4 , 0x00338 },
+ { 0x022ED , 0x022B5 , 0x00338 },
+ { 0x0304C , 0x0304B , 0x03099 },
+ { 0x0304E , 0x0304D , 0x03099 },
+ { 0x03050 , 0x0304F , 0x03099 },
+ { 0x03052 , 0x03051 , 0x03099 },
+ { 0x03054 , 0x03053 , 0x03099 },
+ { 0x03056 , 0x03055 , 0x03099 },
+ { 0x03058 , 0x03057 , 0x03099 },
+ { 0x0305A , 0x03059 , 0x03099 },
+ { 0x0305C , 0x0305B , 0x03099 },
+ { 0x0305E , 0x0305D , 0x03099 },
+ { 0x03060 , 0x0305F , 0x03099 },
+ { 0x03062 , 0x03061 , 0x03099 },
+ { 0x03065 , 0x03064 , 0x03099 },
+ { 0x03067 , 0x03066 , 0x03099 },
+ { 0x03069 , 0x03068 , 0x03099 },
+ { 0x03070 , 0x0306F , 0x03099 },
+ { 0x03071 , 0x0306F , 0x0309A },
+ { 0x03073 , 0x03072 , 0x03099 },
+ { 0x03074 , 0x03072 , 0x0309A },
+ { 0x03076 , 0x03075 , 0x03099 },
+ { 0x03077 , 0x03075 , 0x0309A },
+ { 0x03079 , 0x03078 , 0x03099 },
+ { 0x0307A , 0x03078 , 0x0309A },
+ { 0x0307C , 0x0307B , 0x03099 },
+ { 0x0307D , 0x0307B , 0x0309A },
+ { 0x03094 , 0x03046 , 0x03099 },
+ { 0x0309E , 0x0309D , 0x03099 },
+ { 0x030AC , 0x030AB , 0x03099 },
+ { 0x030AE , 0x030AD , 0x03099 },
+ { 0x030B0 , 0x030AF , 0x03099 },
+ { 0x030B2 , 0x030B1 , 0x03099 },
+ { 0x030B4 , 0x030B3 , 0x03099 },
+ { 0x030B6 , 0x030B5 , 0x03099 },
+ { 0x030B8 , 0x030B7 , 0x03099 },
+ { 0x030BA , 0x030B9 , 0x03099 },
+ { 0x030BC , 0x030BB , 0x03099 },
+ { 0x030BE , 0x030BD , 0x03099 },
+ { 0x030C0 , 0x030BF , 0x03099 },
+ { 0x030C2 , 0x030C1 , 0x03099 },
+ { 0x030C5 , 0x030C4 , 0x03099 },
+ { 0x030C7 , 0x030C6 , 0x03099 },
+ { 0x030C9 , 0x030C8 , 0x03099 },
+ { 0x030D0 , 0x030CF , 0x03099 },
+ { 0x030D1 , 0x030CF , 0x0309A },
+ { 0x030D3 , 0x030D2 , 0x03099 },
+ { 0x030D4 , 0x030D2 , 0x0309A },
+ { 0x030D6 , 0x030D5 , 0x03099 },
+ { 0x030D7 , 0x030D5 , 0x0309A },
+ { 0x030D9 , 0x030D8 , 0x03099 },
+ { 0x030DA , 0x030D8 , 0x0309A },
+ { 0x030DC , 0x030DB , 0x03099 },
+ { 0x030DD , 0x030DB , 0x0309A },
+ { 0x030F4 , 0x030A6 , 0x03099 },
+ { 0x030F7 , 0x030EF , 0x03099 },
+ { 0x030F8 , 0x030F0 , 0x03099 },
+ { 0x030F9 , 0x030F1 , 0x03099 },
+ { 0x030FA , 0x030F2 , 0x03099 },
+ { 0x030FE , 0x030FD , 0x03099 },
+ { 0x1109A , 0x11099 , 0x110BA },
+ { 0x1109C , 0x1109B , 0x110BA },
+ { 0x110AB , 0x110A5 , 0x110BA },
+};
+
+#endif /* ARCHIVE_STRING_COMPOSITION_H_INCLUDED */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_string_sprintf.c b/contrib/libs/libarchive/libarchive/archive_string_sprintf.c
new file mode 100644
index 0000000000..969a5603a4
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_string_sprintf.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_string_sprintf.c 189435 2009-03-06 05:14:55Z kientzle $");
+
+/*
+ * The use of printf()-family functions can be troublesome
+ * for space-constrained applications. In addition, correctly
+ * implementing this function in terms of vsnprintf() requires
+ * two calls (one to determine the size, another to format the
+ * result), which in turn requires duplicating the argument list
+ * using va_copy, which isn't yet universally available. <sigh>
+ *
+ * So, I've implemented a bare minimum of printf()-like capability
+ * here. This is only used to format error messages, so doesn't
+ * require any floating-point support or field-width handling.
+ */
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+
+#include "archive_string.h"
+#include "archive_private.h"
+
+/*
+ * Utility functions to format signed/unsigned integers and append
+ * them to an archive_string.
+ */
+static void
+append_uint(struct archive_string *as, uintmax_t d, unsigned base)
+{
+ static const char digits[] = "0123456789abcdef";
+ if (d >= base)
+ append_uint(as, d/base, base);
+ archive_strappend_char(as, digits[d % base]);
+}
+
+static void
+append_int(struct archive_string *as, intmax_t d, unsigned base)
+{
+ uintmax_t ud;
+
+ if (d < 0) {
+ archive_strappend_char(as, '-');
+ ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d);
+ } else
+ ud = d;
+ append_uint(as, ud, base);
+}
+
+
+void
+archive_string_sprintf(struct archive_string *as, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ archive_string_vsprintf(as, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Like 'vsprintf', but ensures the target is big enough, resizing if
+ * necessary.
+ */
+void
+archive_string_vsprintf(struct archive_string *as, const char *fmt,
+ va_list ap)
+{
+ char long_flag;
+ intmax_t s; /* Signed integer temp. */
+ uintmax_t u; /* Unsigned integer temp. */
+ const char *p, *p2;
+ const wchar_t *pw;
+
+ if (archive_string_ensure(as, 64) == NULL)
+ __archive_errx(1, "Out of memory");
+
+ if (fmt == NULL) {
+ as->s[0] = 0;
+ return;
+ }
+
+ for (p = fmt; *p != '\0'; p++) {
+ const char *saved_p = p;
+
+ if (*p != '%') {
+ archive_strappend_char(as, *p);
+ continue;
+ }
+
+ p++;
+
+ long_flag = '\0';
+ switch(*p) {
+ case 'j':
+ case 'l':
+ case 'z':
+ long_flag = *p;
+ p++;
+ break;
+ }
+
+ switch (*p) {
+ case '%':
+ archive_strappend_char(as, '%');
+ break;
+ case 'c':
+ s = va_arg(ap, int);
+ archive_strappend_char(as, (char)s);
+ break;
+ case 'd':
+ switch(long_flag) {
+ case 'j': s = va_arg(ap, intmax_t); break;
+ case 'l': s = va_arg(ap, long); break;
+ case 'z': s = va_arg(ap, ssize_t); break;
+ default: s = va_arg(ap, int); break;
+ }
+ append_int(as, s, 10);
+ break;
+ case 's':
+ switch(long_flag) {
+ case 'l':
+ pw = va_arg(ap, wchar_t *);
+ if (pw == NULL)
+ pw = L"(null)";
+ if (archive_string_append_from_wcs(as, pw,
+ wcslen(pw)) != 0 && errno == ENOMEM)
+ __archive_errx(1, "Out of memory");
+ break;
+ default:
+ p2 = va_arg(ap, char *);
+ if (p2 == NULL)
+ p2 = "(null)";
+ archive_strcat(as, p2);
+ break;
+ }
+ break;
+ case 'S':
+ pw = va_arg(ap, wchar_t *);
+ if (pw == NULL)
+ pw = L"(null)";
+ if (archive_string_append_from_wcs(as, pw,
+ wcslen(pw)) != 0 && errno == ENOMEM)
+ __archive_errx(1, "Out of memory");
+ break;
+ case 'o': case 'u': case 'x': case 'X':
+ /* Common handling for unsigned integer formats. */
+ switch(long_flag) {
+ case 'j': u = va_arg(ap, uintmax_t); break;
+ case 'l': u = va_arg(ap, unsigned long); break;
+ case 'z': u = va_arg(ap, size_t); break;
+ default: u = va_arg(ap, unsigned int); break;
+ }
+ /* Format it in the correct base. */
+ switch (*p) {
+ case 'o': append_uint(as, u, 8); break;
+ case 'u': append_uint(as, u, 10); break;
+ default: append_uint(as, u, 16); break;
+ }
+ break;
+ default:
+ /* Rewind and print the initial '%' literally. */
+ p = saved_p;
+ archive_strappend_char(as, *p);
+ }
+ }
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_util.c b/contrib/libs/libarchive/libarchive/archive_util.c
new file mode 100644
index 0000000000..0fd344d05b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_util.c
@@ -0,0 +1,704 @@
+/*-
+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
+#include <wincrypt.h>
+#endif
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_string.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+static int archive_utility_string_sort_helper(char **, unsigned int);
+
+/* Generic initialization of 'struct archive' objects. */
+int
+__archive_clean(struct archive *a)
+{
+ archive_string_conversion_free(a);
+ return (ARCHIVE_OK);
+}
+
+int
+archive_version_number(void)
+{
+ return (ARCHIVE_VERSION_NUMBER);
+}
+
+const char *
+archive_version_string(void)
+{
+ return (ARCHIVE_VERSION_STRING);
+}
+
+int
+archive_errno(struct archive *a)
+{
+ return (a->archive_error_number);
+}
+
+const char *
+archive_error_string(struct archive *a)
+{
+
+ if (a->error != NULL && *a->error != '\0')
+ return (a->error);
+ else
+ return (NULL);
+}
+
+int
+archive_file_count(struct archive *a)
+{
+ return (a->file_count);
+}
+
+int
+archive_format(struct archive *a)
+{
+ return (a->archive_format);
+}
+
+const char *
+archive_format_name(struct archive *a)
+{
+ return (a->archive_format_name);
+}
+
+
+int
+archive_compression(struct archive *a)
+{
+ return archive_filter_code(a, 0);
+}
+
+const char *
+archive_compression_name(struct archive *a)
+{
+ return archive_filter_name(a, 0);
+}
+
+
+/*
+ * Return a count of the number of compressed bytes processed.
+ */
+la_int64_t
+archive_position_compressed(struct archive *a)
+{
+ return archive_filter_bytes(a, -1);
+}
+
+/*
+ * Return a count of the number of uncompressed bytes processed.
+ */
+la_int64_t
+archive_position_uncompressed(struct archive *a)
+{
+ return archive_filter_bytes(a, 0);
+}
+
+void
+archive_clear_error(struct archive *a)
+{
+ archive_string_empty(&a->error_string);
+ a->error = NULL;
+ a->archive_error_number = 0;
+}
+
+void
+archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
+{
+ va_list ap;
+
+ a->archive_error_number = error_number;
+ if (fmt == NULL) {
+ a->error = NULL;
+ return;
+ }
+
+ archive_string_empty(&(a->error_string));
+ va_start(ap, fmt);
+ archive_string_vsprintf(&(a->error_string), fmt, ap);
+ va_end(ap);
+ a->error = a->error_string.s;
+}
+
+void
+archive_copy_error(struct archive *dest, struct archive *src)
+{
+ dest->archive_error_number = src->archive_error_number;
+
+ archive_string_copy(&dest->error_string, &src->error_string);
+ dest->error = dest->error_string.s;
+}
+
+void
+__archive_errx(int retvalue, const char *msg)
+{
+ static const char msg1[] = "Fatal Internal Error in libarchive: ";
+ size_t s;
+
+ s = write(2, msg1, strlen(msg1));
+ (void)s; /* UNUSED */
+ s = write(2, msg, strlen(msg));
+ (void)s; /* UNUSED */
+ s = write(2, "\n", 1);
+ (void)s; /* UNUSED */
+ exit(retvalue);
+}
+
+/*
+ * Create a temporary file
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Do not use Windows tmpfile() function.
+ * It will make a temporary file under the root directory
+ * and it'll cause permission error if a user who is
+ * non-Administrator creates temporary files.
+ * Also Windows version of mktemp family including _mktemp_s
+ * are not secure.
+ */
+static int
+__archive_mktempx(const char *tmpdir, wchar_t *template)
+{
+ static const wchar_t prefix[] = L"libarchive_";
+ static const wchar_t suffix[] = L"XXXXXXXXXX";
+ static const wchar_t num[] = {
+ L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
+ L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
+ L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
+ L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
+ L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
+ L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
+ L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
+ L'u', L'v', L'w', L'x', L'y', L'z'
+ };
+ struct archive_wstring temp_name;
+ wchar_t *ws;
+ DWORD attr;
+ wchar_t *xp, *ep;
+ int fd;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ BCRYPT_ALG_HANDLE hAlg = NULL;
+#else
+ HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+#endif
+ fd = -1;
+ ws = NULL;
+
+ if (template == NULL) {
+ archive_string_init(&temp_name);
+
+ /* Get a temporary directory. */
+ if (tmpdir == NULL) {
+ size_t l;
+ wchar_t *tmp;
+
+ l = GetTempPathW(0, NULL);
+ if (l == 0) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+ tmp = malloc(l*sizeof(wchar_t));
+ if (tmp == NULL) {
+ errno = ENOMEM;
+ goto exit_tmpfile;
+ }
+ GetTempPathW((DWORD)l, tmp);
+ archive_wstrcpy(&temp_name, tmp);
+ free(tmp);
+ } else {
+ if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
+ strlen(tmpdir)) < 0)
+ goto exit_tmpfile;
+ if (temp_name.s[temp_name.length-1] != L'/')
+ archive_wstrappend_wchar(&temp_name, L'/');
+ }
+
+ /* Check if temp_name is a directory. */
+ attr = GetFileAttributesW(temp_name.s);
+ if (attr == (DWORD)-1) {
+ if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+ ws = __la_win_permissive_name_w(temp_name.s);
+ if (ws == NULL) {
+ errno = EINVAL;
+ goto exit_tmpfile;
+ }
+ attr = GetFileAttributesW(ws);
+ if (attr == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+ }
+ if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ errno = ENOTDIR;
+ goto exit_tmpfile;
+ }
+
+ /*
+ * Create a temporary file.
+ */
+ archive_wstrcat(&temp_name, prefix);
+ archive_wstrcat(&temp_name, suffix);
+ ep = temp_name.s + archive_strlen(&temp_name);
+ xp = ep - wcslen(suffix);
+ template = temp_name.s;
+ } else {
+ xp = wcschr(template, L'X');
+ if (xp == NULL) /* No X, programming error */
+ abort();
+ for (ep = xp; *ep == L'X'; ep++)
+ continue;
+ if (*ep) /* X followed by non X, programming error */
+ abort();
+ }
+
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM,
+ NULL, 0))) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#else
+ if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT)) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#endif
+
+ for (;;) {
+ wchar_t *p;
+ HANDLE h;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ /* Generate a random file name through CryptGenRandom(). */
+ p = xp;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p,
+ (DWORD)(ep - p)*sizeof(wchar_t), 0))) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#else
+ if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
+ (BYTE*)p)) {
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+#endif
+ for (; p < ep; p++)
+ *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
+
+ free(ws);
+ ws = __la_win_permissive_name_w(template);
+ if (ws == NULL) {
+ errno = EINVAL;
+ goto exit_tmpfile;
+ }
+ if (template == temp_name.s) {
+ attr = FILE_ATTRIBUTE_TEMPORARY |
+ FILE_FLAG_DELETE_ON_CLOSE;
+ } else {
+ /* mkstemp */
+ attr = FILE_ATTRIBUTE_NORMAL;
+ }
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = attr & 0xFFFF;
+ createExParams.dwFileFlags = attr & 0xFFF00000;
+ h = CreateFile2(ws,
+ GENERIC_READ | GENERIC_WRITE | DELETE,
+ 0,/* Not share */
+ CREATE_NEW,
+ &createExParams);
+#else
+ h = CreateFileW(ws,
+ GENERIC_READ | GENERIC_WRITE | DELETE,
+ 0,/* Not share */
+ NULL,
+ CREATE_NEW,/* Create a new file only */
+ attr,
+ NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE) {
+ /* The same file already exists. retry with
+ * a new filename. */
+ if (GetLastError() == ERROR_FILE_EXISTS)
+ continue;
+ /* Otherwise, fail creation temporary file. */
+ la_dosmaperr(GetLastError());
+ goto exit_tmpfile;
+ }
+ fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
+ if (fd == -1) {
+ la_dosmaperr(GetLastError());
+ CloseHandle(h);
+ goto exit_tmpfile;
+ } else
+ break;/* success! */
+ }
+exit_tmpfile:
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+ if (hAlg != NULL)
+ BCryptCloseAlgorithmProvider(hAlg, 0);
+#else
+ if (hProv != (HCRYPTPROV)NULL)
+ CryptReleaseContext(hProv, 0);
+#endif
+ free(ws);
+ if (template == temp_name.s)
+ archive_wstring_free(&temp_name);
+ return (fd);
+}
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+ return __archive_mktempx(tmpdir, NULL);
+}
+
+int
+__archive_mkstemp(wchar_t *template)
+{
+ return __archive_mktempx(NULL, template);
+}
+
+#else
+
+static int
+get_tempdir(struct archive_string *temppath)
+{
+ const char *tmp;
+
+ tmp = getenv("TMPDIR");
+ if (tmp == NULL)
+#ifdef _PATH_TMP
+ tmp = _PATH_TMP;
+#else
+ tmp = "/tmp";
+#endif
+ archive_strcpy(temppath, tmp);
+ if (temppath->s[temppath->length-1] != '/')
+ archive_strappend_char(temppath, '/');
+ return (ARCHIVE_OK);
+}
+
+#if defined(HAVE_MKSTEMP)
+
+/*
+ * We can use mkstemp().
+ */
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+ struct archive_string temp_name;
+ int fd = -1;
+
+ archive_string_init(&temp_name);
+ if (tmpdir == NULL) {
+ if (get_tempdir(&temp_name) != ARCHIVE_OK)
+ goto exit_tmpfile;
+ } else {
+ archive_strcpy(&temp_name, tmpdir);
+ if (temp_name.s[temp_name.length-1] != '/')
+ archive_strappend_char(&temp_name, '/');
+ }
+#ifdef O_TMPFILE
+ fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600);
+ if(fd >= 0)
+ goto exit_tmpfile;
+#endif
+ archive_strcat(&temp_name, "libarchive_XXXXXX");
+ fd = mkstemp(temp_name.s);
+ if (fd < 0)
+ goto exit_tmpfile;
+ __archive_ensure_cloexec_flag(fd);
+ unlink(temp_name.s);
+exit_tmpfile:
+ archive_string_free(&temp_name);
+ return (fd);
+}
+
+int
+__archive_mkstemp(char *template)
+{
+ int fd = -1;
+ fd = mkstemp(template);
+ if (fd >= 0)
+ __archive_ensure_cloexec_flag(fd);
+ return (fd);
+}
+
+#else /* !HAVE_MKSTEMP */
+
+/*
+ * We use a private routine.
+ */
+
+static int
+__archive_mktempx(const char *tmpdir, char *template)
+{
+ static const char num[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z'
+ };
+ struct archive_string temp_name;
+ struct stat st;
+ int fd;
+ char *tp, *ep;
+
+ fd = -1;
+ if (template == NULL) {
+ archive_string_init(&temp_name);
+ if (tmpdir == NULL) {
+ if (get_tempdir(&temp_name) != ARCHIVE_OK)
+ goto exit_tmpfile;
+ } else
+ archive_strcpy(&temp_name, tmpdir);
+ if (temp_name.s[temp_name.length-1] == '/') {
+ temp_name.s[temp_name.length-1] = '\0';
+ temp_name.length --;
+ }
+ if (la_stat(temp_name.s, &st) < 0)
+ goto exit_tmpfile;
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ goto exit_tmpfile;
+ }
+ archive_strcat(&temp_name, "/libarchive_");
+ tp = temp_name.s + archive_strlen(&temp_name);
+ archive_strcat(&temp_name, "XXXXXXXXXX");
+ ep = temp_name.s + archive_strlen(&temp_name);
+ template = temp_name.s;
+ } else {
+ tp = strchr(template, 'X');
+ if (tp == NULL) /* No X, programming error */
+ abort();
+ for (ep = tp; *ep == 'X'; ep++)
+ continue;
+ if (*ep) /* X followed by non X, programming error */
+ abort();
+ }
+
+ do {
+ char *p;
+
+ p = tp;
+ archive_random(p, ep - p);
+ while (p < ep) {
+ int d = *((unsigned char *)p) % sizeof(num);
+ *p++ = num[d];
+ }
+ fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
+ 0600);
+ } while (fd < 0 && errno == EEXIST);
+ if (fd < 0)
+ goto exit_tmpfile;
+ __archive_ensure_cloexec_flag(fd);
+ if (template == temp_name.s)
+ unlink(temp_name.s);
+exit_tmpfile:
+ if (template == temp_name.s)
+ archive_string_free(&temp_name);
+ return (fd);
+}
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+ return __archive_mktempx(tmpdir, NULL);
+}
+
+int
+__archive_mkstemp(char *template)
+{
+ return __archive_mktempx(NULL, template);
+}
+
+#endif /* !HAVE_MKSTEMP */
+#endif /* !_WIN32 || __CYGWIN__ */
+
+/*
+ * Set FD_CLOEXEC flag to a file descriptor if it is not set.
+ * We have to set the flag if the platform does not provide O_CLOEXEC
+ * or F_DUPFD_CLOEXEC flags.
+ *
+ * Note: This function is absolutely called after creating a new file
+ * descriptor even if the platform seemingly provides O_CLOEXEC or
+ * F_DUPFD_CLOEXEC macros because it is possible that the platform
+ * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
+ */
+void
+__archive_ensure_cloexec_flag(int fd)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ (void)fd; /* UNUSED */
+#else
+ int flags;
+
+ if (fd >= 0) {
+ flags = fcntl(fd, F_GETFD);
+ if (flags != -1 && (flags & FD_CLOEXEC) == 0)
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ }
+#endif
+}
+
+/*
+ * Utility function to sort a group of strings using quicksort.
+ */
+static int
+archive_utility_string_sort_helper(char **strings, unsigned int n)
+{
+ unsigned int i, lesser_count, greater_count;
+ char **lesser, **greater, **tmp, *pivot;
+ int retval1, retval2;
+
+ /* A list of 0 or 1 elements is already sorted */
+ if (n <= 1)
+ return (ARCHIVE_OK);
+
+ lesser_count = greater_count = 0;
+ lesser = greater = NULL;
+ pivot = strings[0];
+ for (i = 1; i < n; i++)
+ {
+ if (strcmp(strings[i], pivot) < 0)
+ {
+ lesser_count++;
+ tmp = (char **)realloc(lesser,
+ lesser_count * sizeof(char *));
+ if (!tmp) {
+ free(greater);
+ free(lesser);
+ return (ARCHIVE_FATAL);
+ }
+ lesser = tmp;
+ lesser[lesser_count - 1] = strings[i];
+ }
+ else
+ {
+ greater_count++;
+ tmp = (char **)realloc(greater,
+ greater_count * sizeof(char *));
+ if (!tmp) {
+ free(greater);
+ free(lesser);
+ return (ARCHIVE_FATAL);
+ }
+ greater = tmp;
+ greater[greater_count - 1] = strings[i];
+ }
+ }
+
+ /* quicksort(lesser) */
+ retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
+ for (i = 0; i < lesser_count; i++)
+ strings[i] = lesser[i];
+ free(lesser);
+
+ /* pivot */
+ strings[lesser_count] = pivot;
+
+ /* quicksort(greater) */
+ retval2 = archive_utility_string_sort_helper(greater, greater_count);
+ for (i = 0; i < greater_count; i++)
+ strings[lesser_count + 1 + i] = greater[i];
+ free(greater);
+
+ return (retval1 < retval2) ? retval1 : retval2;
+}
+
+int
+archive_utility_string_sort(char **strings)
+{
+ unsigned int size = 0;
+ while (strings[size] != NULL)
+ size++;
+ return archive_utility_string_sort_helper(strings, size);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_version_details.c b/contrib/libs/libarchive/libarchive/archive_version_details.c
new file mode 100644
index 0000000000..2a143388fc
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_version_details.c
@@ -0,0 +1,151 @@
+/*-
+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+const char *
+archive_version_details(void)
+{
+ static struct archive_string str;
+ static int init = 0;
+ const char *zlib = archive_zlib_version();
+ const char *liblzma = archive_liblzma_version();
+ const char *bzlib = archive_bzlib_version();
+ const char *liblz4 = archive_liblz4_version();
+ const char *libzstd = archive_libzstd_version();
+
+ if (!init) {
+ archive_string_init(&str);
+
+ archive_strcat(&str, ARCHIVE_VERSION_STRING);
+ if (zlib != NULL) {
+ archive_strcat(&str, " zlib/");
+ archive_strcat(&str, zlib);
+ }
+ if (liblzma) {
+ archive_strcat(&str, " liblzma/");
+ archive_strcat(&str, liblzma);
+ }
+ if (bzlib) {
+ const char *p = bzlib;
+ const char *sep = strchr(p, ',');
+ if (sep == NULL)
+ sep = p + strlen(p);
+ archive_strcat(&str, " bz2lib/");
+ archive_strncat(&str, p, sep - p);
+ }
+ if (liblz4) {
+ archive_strcat(&str, " liblz4/");
+ archive_strcat(&str, liblz4);
+ }
+ if (libzstd) {
+ archive_strcat(&str, " libzstd/");
+ archive_strcat(&str, libzstd);
+ }
+ }
+ return str.s;
+}
+
+const char *
+archive_zlib_version(void)
+{
+#ifdef HAVE_ZLIB_H
+ return ZLIB_VERSION;
+#else
+ return NULL;
+#endif
+}
+
+const char *
+archive_liblzma_version(void)
+{
+#ifdef HAVE_LZMA_H
+ return LZMA_VERSION_STRING;
+#else
+ return NULL;
+#endif
+}
+
+const char *
+archive_bzlib_version(void)
+{
+#ifdef HAVE_BZLIB_H
+ return BZ2_bzlibVersion();
+#else
+ return NULL;
+#endif
+}
+
+const char *
+archive_liblz4_version(void)
+{
+#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4)
+#define str(s) #s
+#define NUMBER(x) str(x)
+ return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE);
+#undef NUMBER
+#undef str
+#else
+ return NULL;
+#endif
+}
+
+const char *
+archive_libzstd_version(void)
+{
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ return ZSTD_VERSION_STRING;
+#else
+ return NULL;
+#endif
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_virtual.c b/contrib/libs/libarchive/libarchive/archive_virtual.c
new file mode 100644
index 0000000000..f509ee5c67
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_virtual.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_virtual.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+int
+archive_filter_code(struct archive *a, int n)
+{
+ return ((a->vtable->archive_filter_code)(a, n));
+}
+
+int
+archive_filter_count(struct archive *a)
+{
+ return ((a->vtable->archive_filter_count)(a));
+}
+
+const char *
+archive_filter_name(struct archive *a, int n)
+{
+ return ((a->vtable->archive_filter_name)(a, n));
+}
+
+la_int64_t
+archive_filter_bytes(struct archive *a, int n)
+{
+ return ((a->vtable->archive_filter_bytes)(a, n));
+}
+
+int
+archive_free(struct archive *a)
+{
+ if (a == NULL)
+ return (ARCHIVE_OK);
+ return ((a->vtable->archive_free)(a));
+}
+
+int
+archive_write_close(struct archive *a)
+{
+ return ((a->vtable->archive_close)(a));
+}
+
+int
+archive_read_close(struct archive *a)
+{
+ return ((a->vtable->archive_close)(a));
+}
+
+int
+archive_write_fail(struct archive *a)
+{
+ a->state = ARCHIVE_STATE_FATAL;
+ return a->state;
+}
+
+int
+archive_write_free(struct archive *a)
+{
+ return archive_free(a);
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* For backwards compatibility; will be removed with libarchive 4.0. */
+int
+archive_write_finish(struct archive *a)
+{
+ return archive_write_free(a);
+}
+#endif
+
+int
+archive_read_free(struct archive *a)
+{
+ return archive_free(a);
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* For backwards compatibility; will be removed with libarchive 4.0. */
+int
+archive_read_finish(struct archive *a)
+{
+ return archive_read_free(a);
+}
+#endif
+
+int
+archive_write_header(struct archive *a, struct archive_entry *entry)
+{
+ ++a->file_count;
+ return ((a->vtable->archive_write_header)(a, entry));
+}
+
+int
+archive_write_finish_entry(struct archive *a)
+{
+ return ((a->vtable->archive_write_finish_entry)(a));
+}
+
+la_ssize_t
+archive_write_data(struct archive *a, const void *buff, size_t s)
+{
+ return ((a->vtable->archive_write_data)(a, buff, s));
+}
+
+la_ssize_t
+archive_write_data_block(struct archive *a, const void *buff, size_t s,
+ la_int64_t o)
+{
+ if (a->vtable->archive_write_data_block == NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "archive_write_data_block not supported");
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ return ((a->vtable->archive_write_data_block)(a, buff, s, o));
+}
+
+int
+archive_read_next_header(struct archive *a, struct archive_entry **entry)
+{
+ return ((a->vtable->archive_read_next_header)(a, entry));
+}
+
+int
+archive_read_next_header2(struct archive *a, struct archive_entry *entry)
+{
+ return ((a->vtable->archive_read_next_header2)(a, entry));
+}
+
+int
+archive_read_data_block(struct archive *a,
+ const void **buff, size_t *s, la_int64_t *o)
+{
+ return ((a->vtable->archive_read_data_block)(a, buff, s, o));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_windows.c b/contrib/libs/libarchive/libarchive/archive_windows.c
new file mode 100644
index 0000000000..ebc5eefb80
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_windows.c
@@ -0,0 +1,938 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2007 Kees Zeelenberg
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * A set of compatibility glue for building libarchive on Windows platforms.
+ *
+ * Originally created as "libarchive-nonposix.c" by Kees Zeelenberg
+ * for the GnuWin32 project, trimmed significantly by Tim Kientzle.
+ *
+ * Much of the original file was unnecessary for libarchive, because
+ * many of the features it emulated were not strictly necessary for
+ * libarchive. I hope for this to shrink further as libarchive
+ * internals are gradually reworked to sit more naturally on both
+ * POSIX and Windows. Any ideas for this are greatly appreciated.
+ *
+ * The biggest remaining issue is the dev/ino emulation; libarchive
+ * has a couple of public APIs that rely on dev/ino uniquely
+ * identifying a file. This doesn't match well with Windows. I'm
+ * considering alternative APIs.
+ */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#include "archive_platform.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#include <sys/stat.h>
+#include <locale.h>
+#include <process.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <windows.h>
+#include <share.h>
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+#if defined(__LA_LSEEK_NEEDED)
+static BOOL SetFilePointerEx_perso(HANDLE hFile,
+ LARGE_INTEGER liDistanceToMove,
+ PLARGE_INTEGER lpNewFilePointer,
+ DWORD dwMoveMethod)
+{
+ LARGE_INTEGER li;
+ li.QuadPart = liDistanceToMove.QuadPart;
+ li.LowPart = SetFilePointer(
+ hFile, li.LowPart, &li.HighPart, dwMoveMethod);
+ if(lpNewFilePointer) {
+ lpNewFilePointer->QuadPart = li.QuadPart;
+ }
+ return li.LowPart != -1 || GetLastError() == NO_ERROR;
+}
+#endif
+
+struct ustat {
+ int64_t st_atime;
+ uint32_t st_atime_nsec;
+ int64_t st_ctime;
+ uint32_t st_ctime_nsec;
+ int64_t st_mtime;
+ uint32_t st_mtime_nsec;
+ gid_t st_gid;
+ /* 64bits ino */
+ int64_t st_ino;
+ mode_t st_mode;
+ uint32_t st_nlink;
+ uint64_t st_size;
+ uid_t st_uid;
+ dev_t st_dev;
+ dev_t st_rdev;
+};
+
+/* Transform 64-bits ino into 32-bits by hashing.
+ * You do not forget that really unique number size is 64-bits.
+ */
+#define INOSIZE (8*sizeof(ino_t)) /* 32 */
+static __inline ino_t
+getino(struct ustat *ub)
+{
+ ULARGE_INTEGER ino64;
+ ino64.QuadPart = ub->st_ino;
+ /* I don't know this hashing is correct way */
+ return ((ino_t)(ino64.LowPart ^ (ino64.LowPart >> INOSIZE)));
+}
+
+/*
+ * Prepend "\\?\" to the path name and convert it to unicode to permit
+ * an extended-length path for a maximum total path length of 32767
+ * characters.
+ * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
+ */
+wchar_t *
+__la_win_permissive_name(const char *name)
+{
+ wchar_t *wn;
+ wchar_t *ws;
+ size_t ll;
+
+ ll = strlen(name);
+ wn = malloc((ll + 1) * sizeof(wchar_t));
+ if (wn == NULL)
+ return (NULL);
+ ll = mbstowcs(wn, name, ll);
+ if (ll == (size_t)-1) {
+ free(wn);
+ return (NULL);
+ }
+ wn[ll] = L'\0';
+ ws = __la_win_permissive_name_w(wn);
+ free(wn);
+ return (ws);
+}
+
+wchar_t *
+__la_win_permissive_name_w(const wchar_t *wname)
+{
+ wchar_t *wn, *wnp;
+ wchar_t *ws, *wsp;
+ DWORD l, len, slen;
+ int unc;
+
+ /* Get a full-pathname. */
+ l = GetFullPathNameW(wname, 0, NULL, NULL);
+ if (l == 0)
+ return (NULL);
+ /* NOTE: GetFullPathNameW has a bug that if the length of the file
+ * name is just 1 then it returns incomplete buffer size. Thus, we
+ * have to add three to the size to allocate a sufficient buffer
+ * size for the full-pathname of the file name. */
+ l += 3;
+ wnp = malloc(l * sizeof(wchar_t));
+ if (wnp == NULL)
+ return (NULL);
+ len = GetFullPathNameW(wname, l, wnp, NULL);
+ wn = wnp;
+
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'?' && wnp[3] == L'\\')
+ /* We have already a permissive name. */
+ return (wn);
+
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'.' && wnp[3] == L'\\') {
+ /* This is a device name */
+ if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
+ (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
+ wnp[5] == L':' && wnp[6] == L'\\')
+ wnp[2] = L'?';/* Not device name. */
+ return (wn);
+ }
+
+ unc = 0;
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
+ wchar_t *p = &wnp[2];
+
+ /* Skip server-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\') {
+ wchar_t *rp = ++p;
+ /* Skip share-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\' && p != rp) {
+ /* Now, match patterns such as
+ * "\\server-name\share-name\" */
+ wnp += 2;
+ len -= 2;
+ unc = 1;
+ }
+ }
+ }
+
+ slen = 4 + (unc * 4) + len + 1;
+ ws = wsp = malloc(slen * sizeof(wchar_t));
+ if (ws == NULL) {
+ free(wn);
+ return (NULL);
+ }
+ /* prepend "\\?\" */
+ wcsncpy(wsp, L"\\\\?\\", 4);
+ wsp += 4;
+ slen -= 4;
+ if (unc) {
+ /* append "UNC\" ---> "\\?\UNC\" */
+ wcsncpy(wsp, L"UNC\\", 4);
+ wsp += 4;
+ slen -= 4;
+ }
+ wcsncpy(wsp, wnp, slen);
+ wsp[slen - 1] = L'\0'; /* Ensure null termination. */
+ free(wn);
+ return (ws);
+}
+
+/*
+ * Create a file handle.
+ * This can exceed MAX_PATH limitation.
+ */
+static HANDLE
+la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+ wchar_t *wpath;
+ HANDLE handle;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+ handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
+ lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+ hTemplateFile);
+ if (handle != INVALID_HANDLE_VALUE)
+ return (handle);
+ if (GetLastError() != ERROR_PATH_NOT_FOUND)
+ return (handle);
+#endif
+ wpath = __la_win_permissive_name(path);
+ if (wpath == NULL)
+ return INVALID_HANDLE_VALUE;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = dwFlagsAndAttributes & 0xFFFF;
+ createExParams.dwFileFlags = dwFlagsAndAttributes & 0xFFF00000;
+ createExParams.dwSecurityQosFlags = dwFlagsAndAttributes & 0x000F00000;
+ createExParams.lpSecurityAttributes = lpSecurityAttributes;
+ createExParams.hTemplateFile = hTemplateFile;
+ handle = CreateFile2(wpath, dwDesiredAccess, dwShareMode,
+ dwCreationDisposition, &createExParams);
+#else /* !WINAPI_PARTITION_DESKTOP */
+ handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
+ lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+ hTemplateFile);
+#endif /* !WINAPI_PARTITION_DESKTOP */
+ free(wpath);
+ return (handle);
+}
+
+#if defined(__LA_LSEEK_NEEDED)
+__int64
+__la_lseek(int fd, __int64 offset, int whence)
+{
+ LARGE_INTEGER distance;
+ LARGE_INTEGER newpointer;
+ HANDLE handle;
+
+ if (fd < 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ handle = (HANDLE)_get_osfhandle(fd);
+ if (GetFileType(handle) != FILE_TYPE_DISK) {
+ errno = EBADF;
+ return (-1);
+ }
+ distance.QuadPart = offset;
+ if (!SetFilePointerEx_perso(handle, distance, &newpointer, whence)) {
+ DWORD lasterr;
+
+ lasterr = GetLastError();
+ if (lasterr == ERROR_BROKEN_PIPE)
+ return (0);
+ if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ return (-1);
+ }
+ return (newpointer.QuadPart);
+}
+#endif
+
+/* This can exceed MAX_PATH limitation. */
+int
+__la_open(const char *path, int flags, ...)
+{
+ va_list ap;
+ wchar_t *ws;
+ int r, pmode;
+ DWORD attr;
+
+ va_start(ap, flags);
+ pmode = va_arg(ap, int);
+ va_end(ap);
+ ws = NULL;
+ if ((flags & ~O_BINARY) == O_RDONLY) {
+ /*
+ * When we open a directory, _open function returns
+ * "Permission denied" error.
+ */
+ attr = GetFileAttributesA(path);
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+ if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND)
+#endif
+ {
+ ws = __la_win_permissive_name(path);
+ if (ws == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ attr = GetFileAttributesW(ws);
+ }
+ if (attr == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ free(ws);
+ return (-1);
+ }
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ HANDLE handle;
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+ if (ws != NULL)
+ handle = CreateFileW(ws, 0, 0, NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_ATTRIBUTE_READONLY,
+ NULL);
+ else
+ handle = CreateFileA(path, 0, 0, NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_ATTRIBUTE_READONLY,
+ NULL);
+#else /* !WINAPI_PARTITION_DESKTOP */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ handle = CreateFile2(ws, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#endif /* !WINAPI_PARTITION_DESKTOP */
+ free(ws);
+ if (handle == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ r = _open_osfhandle((intptr_t)handle, _O_RDONLY);
+ return (r);
+ }
+ }
+ if (ws == NULL) {
+#if defined(__BORLANDC__)
+ /* Borland has no mode argument.
+ TODO: Fix mode of new file. */
+ r = _open(path, flags);
+#else
+ r = _open(path, flags, pmode);
+#endif
+ if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+ /* Simulate other POSIX system action to pass our test suite. */
+ attr = GetFileAttributesA(path);
+ if (attr == (DWORD)-1)
+ la_dosmaperr(GetLastError());
+ else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+ errno = EISDIR;
+ else
+ errno = EACCES;
+ return (-1);
+ }
+ if (r >= 0 || errno != ENOENT)
+ return (r);
+ ws = __la_win_permissive_name(path);
+ if (ws == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ }
+ r = _wopen(ws, flags, pmode);
+ if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+ /* Simulate other POSIX system action to pass our test suite. */
+ attr = GetFileAttributesW(ws);
+ if (attr == (DWORD)-1)
+ la_dosmaperr(GetLastError());
+ else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+ errno = EISDIR;
+ else
+ errno = EACCES;
+ }
+ free(ws);
+ return (r);
+}
+
+ssize_t
+__la_read(int fd, void *buf, size_t nbytes)
+{
+ HANDLE handle;
+ DWORD bytes_read, lasterr;
+ int r;
+
+#ifdef _WIN64
+ if (nbytes > UINT32_MAX)
+ nbytes = UINT32_MAX;
+#endif
+ if (fd < 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ /* Do not pass 0 to third parameter of ReadFile(), read bytes.
+ * This will not return to application side. */
+ if (nbytes == 0)
+ return (0);
+ handle = (HANDLE)_get_osfhandle(fd);
+ r = ReadFile(handle, buf, (uint32_t)nbytes,
+ &bytes_read, NULL);
+ if (r == 0) {
+ lasterr = GetLastError();
+ if (lasterr == ERROR_NO_DATA) {
+ errno = EAGAIN;
+ return (-1);
+ }
+ if (lasterr == ERROR_BROKEN_PIPE)
+ return (0);
+ if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ return (-1);
+ }
+ return ((ssize_t)bytes_read);
+}
+
+/* Convert Windows FILETIME to UTC */
+__inline static void
+fileTimeToUTC(const FILETIME *filetime, time_t *t, long *ns)
+{
+ ULARGE_INTEGER utc;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ *t = (time_t)(utc.QuadPart / 10000000); /* milli seconds base */
+ *ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
+ } else {
+ *t = 0;
+ *ns = 0;
+ }
+}
+
+/* Stat by handle
+ * Windows' stat() does not accept the path added "\\?\" especially "?"
+ * character.
+ * It means we cannot access the long name path longer than MAX_PATH.
+ * So I've implemented a function similar to Windows' stat() to access the
+ * long name path.
+ * And I've added some feature.
+ * 1. set st_ino by nFileIndexHigh and nFileIndexLow of
+ * BY_HANDLE_FILE_INFORMATION.
+ * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.
+ * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.
+ */
+static int
+__hstat(HANDLE handle, struct ustat *st)
+{
+ BY_HANDLE_FILE_INFORMATION info;
+ ULARGE_INTEGER ino64;
+ DWORD ftype;
+ mode_t mode;
+ time_t t;
+ long ns;
+
+ switch (ftype = GetFileType(handle)) {
+ case FILE_TYPE_UNKNOWN:
+ errno = EBADF;
+ return (-1);
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_PIPE:
+ if (ftype == FILE_TYPE_CHAR) {
+ st->st_mode = S_IFCHR;
+ st->st_size = 0;
+ } else {
+ DWORD avail;
+
+ st->st_mode = S_IFIFO;
+ if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))
+ st->st_size = avail;
+ else
+ st->st_size = 0;
+ }
+ st->st_atime = 0;
+ st->st_atime_nsec = 0;
+ st->st_mtime = 0;
+ st->st_mtime_nsec = 0;
+ st->st_ctime = 0;
+ st->st_ctime_nsec = 0;
+ st->st_ino = 0;
+ st->st_nlink = 1;
+ st->st_uid = 0;
+ st->st_gid = 0;
+ st->st_rdev = 0;
+ st->st_dev = 0;
+ return (0);
+ case FILE_TYPE_DISK:
+ break;
+ default:
+ /* This ftype is undocumented type. */
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ ZeroMemory(&info, sizeof(info));
+ if (!GetFileInformationByHandle (handle, &info)) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+ mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else
+ mode |= S_IFREG;
+ st->st_mode = mode;
+
+ fileTimeToUTC(&info.ftLastAccessTime, &t, &ns);
+ st->st_atime = t;
+ st->st_atime_nsec = ns;
+ fileTimeToUTC(&info.ftLastWriteTime, &t, &ns);
+ st->st_mtime = t;
+ st->st_mtime_nsec = ns;
+ fileTimeToUTC(&info.ftCreationTime, &t, &ns);
+ st->st_ctime = t;
+ st->st_ctime_nsec = ns;
+ st->st_size =
+ ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
+ + (int64_t)(info.nFileSizeLow);
+#ifdef SIMULATE_WIN_STAT
+ st->st_ino = 0;
+ st->st_nlink = 1;
+ st->st_dev = 0;
+#else
+ /* Getting FileIndex as i-node. We should remove a sequence which
+ * is high-16-bits of nFileIndexHigh. */
+ ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
+ ino64.LowPart = info.nFileIndexLow;
+ st->st_ino = ino64.QuadPart;
+ st->st_nlink = info.nNumberOfLinks;
+ if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ ++st->st_nlink;/* Add parent directory. */
+ st->st_dev = info.dwVolumeSerialNumber;
+#endif
+ st->st_uid = 0;
+ st->st_gid = 0;
+ st->st_rdev = 0;
+ return (0);
+}
+
+static void
+copy_stat(struct stat *st, struct ustat *us)
+{
+ st->st_atime = us->st_atime;
+ st->st_ctime = us->st_ctime;
+ st->st_mtime = us->st_mtime;
+ st->st_gid = us->st_gid;
+ st->st_ino = getino(us);
+ st->st_mode = us->st_mode;
+ st->st_nlink = us->st_nlink;
+ st->st_size = (off_t)us->st_size;
+ st->st_uid = us->st_uid;
+ st->st_dev = us->st_dev;
+ st->st_rdev = us->st_rdev;
+}
+
+/*
+ * TODO: Remove a use of __la_fstat and __la_stat.
+ * We should use GetFileInformationByHandle in place
+ * where We still use the *stat functions.
+ */
+int
+__la_fstat(int fd, struct stat *st)
+{
+ struct ustat u;
+ int ret;
+
+ if (fd < 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
+ if (ret >= 0) {
+ copy_stat(st, &u);
+ if (u.st_mode & (S_IFCHR | S_IFIFO)) {
+ st->st_dev = fd;
+ st->st_rdev = fd;
+ }
+ }
+ return (ret);
+}
+
+/* This can exceed MAX_PATH limitation. */
+int
+__la_stat(const char *path, struct stat *st)
+{
+ HANDLE handle;
+ struct ustat u;
+ int ret;
+
+ handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (handle == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ ret = __hstat(handle, &u);
+ CloseHandle(handle);
+ if (ret >= 0) {
+ char *p;
+
+ copy_stat(st, &u);
+ p = strrchr(path, '.');
+ if (p != NULL && strlen(p) == 4) {
+ char exttype[4];
+
+ ++ p;
+ exttype[0] = toupper(*p++);
+ exttype[1] = toupper(*p++);
+ exttype[2] = toupper(*p++);
+ exttype[3] = '\0';
+ if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
+ !strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))
+ st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * This waitpid is limited implementation.
+ */
+pid_t
+__la_waitpid(HANDLE child, int *status, int option)
+{
+ DWORD cs;
+
+ (void)option;/* UNUSED */
+ do {
+ if (GetExitCodeProcess(child, &cs) == 0) {
+ CloseHandle(child);
+ la_dosmaperr(GetLastError());
+ *status = 0;
+ return (-1);
+ }
+ } while (cs == STILL_ACTIVE);
+
+ *status = (int)(cs & 0xff);
+ return (0);
+}
+
+ssize_t
+__la_write(int fd, const void *buf, size_t nbytes)
+{
+ DWORD bytes_written;
+
+#ifdef _WIN64
+ if (nbytes > UINT32_MAX)
+ nbytes = UINT32_MAX;
+#endif
+ if (fd < 0) {
+ errno = EBADF;
+ return (-1);
+ }
+ if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
+ &bytes_written, NULL)) {
+ DWORD lasterr;
+
+ lasterr = GetLastError();
+ if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ return (-1);
+ }
+ return (bytes_written);
+}
+
+/*
+ * Replace the Windows path separator '\' with '/'.
+ */
+static int
+replace_pathseparator(struct archive_wstring *ws, const wchar_t *wp)
+{
+ wchar_t *w;
+ size_t path_length;
+
+ if (wp == NULL)
+ return(0);
+ if (wcschr(wp, L'\\') == NULL)
+ return(0);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(ws, path_length) == NULL)
+ return(-1);
+ archive_wstrncpy(ws, wp, path_length);
+ for (w = ws->s; *w; w++) {
+ if (*w == L'\\')
+ *w = L'/';
+ }
+ return(1);
+}
+
+static int
+fix_pathseparator(struct archive_entry *entry)
+{
+ struct archive_wstring ws;
+ const wchar_t *wp;
+ int ret = ARCHIVE_OK;
+
+ archive_string_init(&ws);
+ wp = archive_entry_pathname_w(entry);
+ switch (replace_pathseparator(&ws, wp)) {
+ case 0: /* Not replaced. */
+ break;
+ case 1: /* Replaced. */
+ archive_entry_copy_pathname_w(entry, ws.s);
+ break;
+ default:
+ ret = ARCHIVE_FAILED;
+ }
+ wp = archive_entry_hardlink_w(entry);
+ switch (replace_pathseparator(&ws, wp)) {
+ case 0: /* Not replaced. */
+ break;
+ case 1: /* Replaced. */
+ archive_entry_copy_hardlink_w(entry, ws.s);
+ break;
+ default:
+ ret = ARCHIVE_FAILED;
+ }
+ wp = archive_entry_symlink_w(entry);
+ switch (replace_pathseparator(&ws, wp)) {
+ case 0: /* Not replaced. */
+ break;
+ case 1: /* Replaced. */
+ archive_entry_copy_symlink_w(entry, ws.s);
+ break;
+ default:
+ ret = ARCHIVE_FAILED;
+ }
+ archive_wstring_free(&ws);
+ return(ret);
+}
+
+struct archive_entry *
+__la_win_entry_in_posix_pathseparator(struct archive_entry *entry)
+{
+ struct archive_entry *entry_main;
+ const wchar_t *wp;
+ int has_backslash = 0;
+ int ret;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL && wcschr(wp, L'\\') != NULL)
+ has_backslash = 1;
+ if (!has_backslash) {
+ wp = archive_entry_hardlink_w(entry);
+ if (wp != NULL && wcschr(wp, L'\\') != NULL)
+ has_backslash = 1;
+ }
+ if (!has_backslash) {
+ wp = archive_entry_symlink_w(entry);
+ if (wp != NULL && wcschr(wp, L'\\') != NULL)
+ has_backslash = 1;
+ }
+ /*
+ * If there is no backslash chars, return the original.
+ */
+ if (!has_backslash)
+ return (entry);
+
+ /* Copy entry so we can modify it as needed. */
+ entry_main = archive_entry_clone(entry);
+ if (entry_main == NULL)
+ return (NULL);
+ /* Replace the Windows path-separator '\' with '/'. */
+ ret = fix_pathseparator(entry_main);
+ if (ret < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (NULL);
+ }
+ return (entry_main);
+}
+
+
+/*
+ * The following function was modified from PostgreSQL sources and is
+ * subject to the copyright below.
+ */
+/*-------------------------------------------------------------------------
+ *
+ * win32error.c
+ * Map win32 error codes to errno values
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this
+paragraph and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*/
+
+static const struct {
+ DWORD winerr;
+ int doserr;
+} doserrors[] =
+{
+ { ERROR_INVALID_FUNCTION, EINVAL },
+ { ERROR_FILE_NOT_FOUND, ENOENT },
+ { ERROR_PATH_NOT_FOUND, ENOENT },
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
+ { ERROR_ACCESS_DENIED, EACCES },
+ { ERROR_INVALID_HANDLE, EBADF },
+ { ERROR_ARENA_TRASHED, ENOMEM },
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
+ { ERROR_INVALID_BLOCK, ENOMEM },
+ { ERROR_BAD_ENVIRONMENT, E2BIG },
+ { ERROR_BAD_FORMAT, ENOEXEC },
+ { ERROR_INVALID_ACCESS, EINVAL },
+ { ERROR_INVALID_DATA, EINVAL },
+ { ERROR_INVALID_DRIVE, ENOENT },
+ { ERROR_CURRENT_DIRECTORY, EACCES },
+ { ERROR_NOT_SAME_DEVICE, EXDEV },
+ { ERROR_NO_MORE_FILES, ENOENT },
+ { ERROR_LOCK_VIOLATION, EACCES },
+ { ERROR_SHARING_VIOLATION, EACCES },
+ { ERROR_BAD_NETPATH, ENOENT },
+ { ERROR_NETWORK_ACCESS_DENIED, EACCES },
+ { ERROR_BAD_NET_NAME, ENOENT },
+ { ERROR_FILE_EXISTS, EEXIST },
+ { ERROR_CANNOT_MAKE, EACCES },
+ { ERROR_FAIL_I24, EACCES },
+ { ERROR_INVALID_PARAMETER, EINVAL },
+ { ERROR_NO_PROC_SLOTS, EAGAIN },
+ { ERROR_DRIVE_LOCKED, EACCES },
+ { ERROR_BROKEN_PIPE, EPIPE },
+ { ERROR_DISK_FULL, ENOSPC },
+ { ERROR_INVALID_TARGET_HANDLE, EBADF },
+ { ERROR_INVALID_HANDLE, EINVAL },
+ { ERROR_WAIT_NO_CHILDREN, ECHILD },
+ { ERROR_CHILD_NOT_COMPLETE, ECHILD },
+ { ERROR_DIRECT_ACCESS_HANDLE, EBADF },
+ { ERROR_NEGATIVE_SEEK, EINVAL },
+ { ERROR_SEEK_ON_DEVICE, EACCES },
+ { ERROR_DIR_NOT_EMPTY, ENOTEMPTY },
+ { ERROR_NOT_LOCKED, EACCES },
+ { ERROR_BAD_PATHNAME, ENOENT },
+ { ERROR_MAX_THRDS_REACHED, EAGAIN },
+ { ERROR_LOCK_FAILED, EACCES },
+ { ERROR_ALREADY_EXISTS, EEXIST },
+ { ERROR_FILENAME_EXCED_RANGE, ENOENT },
+ { ERROR_NESTING_NOT_ALLOWED, EAGAIN },
+ { ERROR_NOT_ENOUGH_QUOTA, ENOMEM }
+};
+
+void
+__la_dosmaperr(unsigned long e)
+{
+ int i;
+
+ if (e == 0)
+ {
+ errno = 0;
+ return;
+ }
+
+ for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)
+ {
+ if (doserrors[i].winerr == e)
+ {
+ errno = doserrors[i].doserr;
+ return;
+ }
+ }
+
+ /* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
+ errno = EINVAL;
+ return;
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
diff --git a/contrib/libs/libarchive/libarchive/archive_windows.h b/contrib/libs/libarchive/libarchive/archive_windows.h
new file mode 100644
index 0000000000..47b7cb8e37
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_windows.h
@@ -0,0 +1,315 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2006 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * TODO: A lot of stuff in here isn't actually used by libarchive and
+ * can be trimmed out. Note that this file is used by libarchive and
+ * libarchive_test but nowhere else. (But note that it gets compiled
+ * with many different Windows environments, including MinGW, Visual
+ * Studio, and Cygwin. Significant changes should be tested in all three.)
+ */
+
+/*
+ * TODO: Don't use off_t in here. Use __int64 instead. Note that
+ * Visual Studio and the Windows SDK define off_t as 32 bits; Win32's
+ * more modern file handling APIs all use __int64 instead of off_t.
+ */
+
+#ifndef LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
+#define LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/* Start of configuration for native Win32 */
+#ifndef MINGW_HAS_SECURE_API
+#define MINGW_HAS_SECURE_API 1
+#endif
+
+#include <errno.h>
+#define set_errno(val) ((errno)=val)
+#include <io.h>
+#include <stdlib.h> //brings in NULL
+#if defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <process.h>
+#include <direct.h>
+#if defined(__MINGW32__) && defined(HAVE_UNISTD_H)
+/* Prevent build error from a type mismatch of ftruncate().
+ * This unistd.h defines it as ftruncate(int, off_t). */
+#include <unistd.h>
+#endif
+#define NOCRYPT
+#include <windows.h>
+//#define EFTYPE 7
+
+#if defined(__BORLANDC__)
+#pragma warn -8068 /* Constant out of range in comparison. */
+#pragma warn -8072 /* Suspicious pointer arithmetic. */
+#endif
+
+#ifndef NULL
+#ifdef __cplusplus
+#define NULL 0
+#else
+#define NULL ((void *)0)
+#endif
+#endif
+
+/* Alias the Windows _function to the POSIX equivalent. */
+#define close _close
+#define fcntl(fd, cmd, flg) /* No operation. */
+#ifndef fileno
+#define fileno _fileno
+#endif
+#ifdef fstat
+#undef fstat
+#endif
+#define fstat __la_fstat
+#if !defined(__BORLANDC__)
+#ifdef lseek
+#undef lseek
+#endif
+#define lseek _lseeki64
+#else
+#define lseek __la_lseek
+#define __LA_LSEEK_NEEDED
+#endif
+#define lstat __la_stat
+#define open __la_open
+#define read __la_read
+#if !defined(__BORLANDC__) && !defined(__WATCOMC__)
+#define setmode _setmode
+#endif
+#define la_stat(path,stref) __la_stat(path,stref)
+#if !defined(__WATCOMC__)
+#if !defined(__BORLANDC__)
+#define strdup _strdup
+#endif
+#define tzset _tzset
+#if !defined(__BORLANDC__)
+#define umask _umask
+#endif
+#endif
+#define waitpid __la_waitpid
+#define write __la_write
+
+#if !defined(__WATCOMC__)
+
+#ifndef O_RDONLY
+#define O_RDONLY _O_RDONLY
+#define O_WRONLY _O_WRONLY
+#define O_TRUNC _O_TRUNC
+#define O_CREAT _O_CREAT
+#define O_EXCL _O_EXCL
+#define O_BINARY _O_BINARY
+#endif
+
+#ifndef _S_IFIFO
+ #define _S_IFIFO 0010000 /* pipe */
+#endif
+#ifndef _S_IFCHR
+ #define _S_IFCHR 0020000 /* character special */
+#endif
+#ifndef _S_IFDIR
+ #define _S_IFDIR 0040000 /* directory */
+#endif
+#ifndef _S_IFBLK
+ #define _S_IFBLK 0060000 /* block special */
+#endif
+#ifndef _S_IFLNK
+ #define _S_IFLNK 0120000 /* symbolic link */
+#endif
+#ifndef _S_IFSOCK
+ #define _S_IFSOCK 0140000 /* socket */
+#endif
+#ifndef _S_IFREG
+ #define _S_IFREG 0100000 /* regular */
+#endif
+#ifndef _S_IFMT
+ #define _S_IFMT 0170000 /* file type mask */
+#endif
+
+#ifndef S_IFIFO
+#define S_IFIFO _S_IFIFO
+#endif
+//#define S_IFCHR _S_IFCHR
+//#define S_IFDIR _S_IFDIR
+#ifndef S_IFBLK
+#define S_IFBLK _S_IFBLK
+#endif
+#ifndef S_IFLNK
+#define S_IFLNK _S_IFLNK
+#endif
+#ifndef S_IFSOCK
+#define S_IFSOCK _S_IFSOCK
+#endif
+//#define S_IFREG _S_IFREG
+//#define S_IFMT _S_IFMT
+
+#ifndef S_ISBLK
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /* block special */
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* fifo or socket */
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /* char special */
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /* directory */
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /* regular file */
+#endif
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) /* Symbolic link */
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* Socket */
+
+#define _S_ISUID 0004000 /* set user id on execution */
+#define _S_ISGID 0002000 /* set group id on execution */
+#define _S_ISVTX 0001000 /* save swapped text even after use */
+
+#define S_ISUID _S_ISUID
+#define S_ISGID _S_ISGID
+#define S_ISVTX _S_ISVTX
+
+#define _S_IRWXU (_S_IREAD | _S_IWRITE | _S_IEXEC)
+#define _S_IXUSR _S_IEXEC /* read permission, user */
+#define _S_IWUSR _S_IWRITE /* write permission, user */
+#define _S_IRUSR _S_IREAD /* execute/search permission, user */
+#define _S_IRWXG (_S_IRWXU >> 3)
+#define _S_IXGRP (_S_IXUSR >> 3) /* read permission, group */
+#define _S_IWGRP (_S_IWUSR >> 3) /* write permission, group */
+#define _S_IRGRP (_S_IRUSR >> 3) /* execute/search permission, group */
+#define _S_IRWXO (_S_IRWXG >> 3)
+#define _S_IXOTH (_S_IXGRP >> 3) /* read permission, other */
+#define _S_IWOTH (_S_IWGRP >> 3) /* write permission, other */
+#define _S_IROTH (_S_IRGRP >> 3) /* execute/search permission, other */
+
+#ifndef S_IRWXU
+#define S_IRWXU _S_IRWXU
+#define S_IXUSR _S_IXUSR
+#define S_IWUSR _S_IWUSR
+#define S_IRUSR _S_IRUSR
+#endif
+#ifndef S_IRWXG
+#define S_IRWXG _S_IRWXG
+#define S_IXGRP _S_IXGRP
+#define S_IWGRP _S_IWGRP
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP _S_IRGRP
+#endif
+#ifndef S_IRWXO
+#define S_IRWXO _S_IRWXO
+#define S_IXOTH _S_IXOTH
+#define S_IWOTH _S_IWOTH
+#define S_IROTH _S_IROTH
+#endif
+
+#endif
+
+#define F_DUPFD 0 /* Duplicate file descriptor. */
+#define F_GETFD 1 /* Get file descriptor flags. */
+#define F_SETFD 2 /* Set file descriptor flags. */
+#define F_GETFL 3 /* Get file status flags. */
+#define F_SETFL 4 /* Set file status flags. */
+#define F_GETOWN 5 /* Get owner (receiver of SIGIO). */
+#define F_SETOWN 6 /* Set owner (receiver of SIGIO). */
+#define F_GETLK 7 /* Get record locking info. */
+#define F_SETLK 8 /* Set record locking info (non-blocking). */
+#define F_SETLKW 9 /* Set record locking info (blocking). */
+
+/* XXX missing */
+#define F_GETLK64 7 /* Get record locking info. */
+#define F_SETLK64 8 /* Set record locking info (non-blocking). */
+#define F_SETLKW64 9 /* Set record locking info (blocking). */
+
+/* File descriptor flags used with F_GETFD and F_SETFD. */
+#define FD_CLOEXEC 1 /* Close on exec. */
+
+//NOT SURE IF O_NONBLOCK is OK here but at least the 0x0004 flag is not used by anything else...
+#define O_NONBLOCK 0x0004 /* Non-blocking I/O. */
+//#define O_NDELAY O_NONBLOCK
+
+/* Symbolic constants for the access() function */
+#if !defined(F_OK)
+ #define R_OK 4 /* Test for read permission */
+ #define W_OK 2 /* Test for write permission */
+ #define X_OK 1 /* Test for execute permission */
+ #define F_OK 0 /* Test for existence of file */
+#endif
+
+
+/* Replacement POSIX function */
+extern int __la_fstat(int fd, struct stat *st);
+extern int __la_lstat(const char *path, struct stat *st);
+#if defined(__LA_LSEEK_NEEDED)
+extern __int64 __la_lseek(int fd, __int64 offset, int whence);
+#endif
+extern int __la_open(const char *path, int flags, ...);
+extern ssize_t __la_read(int fd, void *buf, size_t nbytes);
+extern int __la_stat(const char *path, struct stat *st);
+extern pid_t __la_waitpid(HANDLE child, int *status, int option);
+extern ssize_t __la_write(int fd, const void *buf, size_t nbytes);
+
+#define _stat64i32(path, st) __la_stat(path, st)
+#define _stat64(path, st) __la_stat(path, st)
+/* for status returned by la_waitpid */
+#define WIFEXITED(sts) ((sts & 0x100) == 0)
+#define WEXITSTATUS(sts) (sts & 0x0FF)
+
+extern wchar_t *__la_win_permissive_name(const char *name);
+extern wchar_t *__la_win_permissive_name_w(const wchar_t *wname);
+extern void __la_dosmaperr(unsigned long e);
+#define la_dosmaperr(e) __la_dosmaperr(e)
+extern struct archive_entry *__la_win_entry_in_posix_pathseparator(
+ struct archive_entry *);
+
+#if defined(HAVE_WCRTOMB) && defined(__BORLANDC__)
+typedef int mbstate_t;
+size_t wcrtomb(char *, wchar_t, mbstate_t *);
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+WINBASEAPI BOOL WINAPI GetVolumePathNameW(
+ LPCWSTR lpszFileName,
+ LPWSTR lpszVolumePathName,
+ DWORD cchBufferLength
+ );
+# if _WIN32_WINNT < 0x0500 /* windows.h not providing 0x500 API */
+typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
+ LARGE_INTEGER FileOffset;
+ LARGE_INTEGER Length;
+} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER;
+# define FSCTL_SET_SPARSE \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_WRITE_DATA)
+# define FSCTL_QUERY_ALLOCATED_RANGES \
+ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_DATA)
+# endif
+#endif
+
+#endif /* LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */
diff --git a/contrib/libs/libarchive/libarchive/archive_write.c b/contrib/libs/libarchive/libarchive/archive_write.c
new file mode 100644
index 0000000000..ec3c95c566
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write.c
@@ -0,0 +1,859 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write.c 201099 2009-12-28 03:03:00Z kientzle $");
+
+/*
+ * This file contains the "essential" portions of the write API, that
+ * is, stuff that will essentially always be used by any client that
+ * actually needs to write an archive. Optional pieces have been, as
+ * far as possible, separated out into separate files to reduce
+ * needlessly bloating statically-linked clients.
+ */
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+static int _archive_filter_code(struct archive *, int);
+static const char *_archive_filter_name(struct archive *, int);
+static int64_t _archive_filter_bytes(struct archive *, int);
+static int _archive_write_filter_count(struct archive *);
+static int _archive_write_close(struct archive *);
+static int _archive_write_free(struct archive *);
+static int _archive_write_header(struct archive *, struct archive_entry *);
+static int _archive_write_finish_entry(struct archive *);
+static ssize_t _archive_write_data(struct archive *, const void *, size_t);
+
+struct archive_none {
+ size_t buffer_size;
+ size_t avail;
+ char *buffer;
+ char *next;
+};
+
+static const struct archive_vtable
+archive_write_vtable = {
+ .archive_close = _archive_write_close,
+ .archive_filter_bytes = _archive_filter_bytes,
+ .archive_filter_code = _archive_filter_code,
+ .archive_filter_name = _archive_filter_name,
+ .archive_filter_count = _archive_write_filter_count,
+ .archive_free = _archive_write_free,
+ .archive_write_header = _archive_write_header,
+ .archive_write_finish_entry = _archive_write_finish_entry,
+ .archive_write_data = _archive_write_data,
+};
+
+/*
+ * Allocate, initialize and return an archive object.
+ */
+struct archive *
+archive_write_new(void)
+{
+ struct archive_write *a;
+ unsigned char *nulls;
+
+ a = (struct archive_write *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_WRITE_MAGIC;
+ a->archive.state = ARCHIVE_STATE_NEW;
+ a->archive.vtable = &archive_write_vtable;
+ /*
+ * The value 10240 here matches the traditional tar default,
+ * but is otherwise arbitrary.
+ * TODO: Set the default block size from the format selected.
+ */
+ a->bytes_per_block = 10240;
+ a->bytes_in_last_block = -1; /* Default */
+
+ /* Initialize a block of nulls for padding purposes. */
+ a->null_length = 1024;
+ nulls = (unsigned char *)calloc(1, a->null_length);
+ if (nulls == NULL) {
+ free(a);
+ return (NULL);
+ }
+ a->nulls = nulls;
+ return (&a->archive);
+}
+
+/*
+ * Set the block size. Returns 0 if successful.
+ */
+int
+archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");
+ a->bytes_per_block = bytes_per_block;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Get the current block size. -1 if it has never been set.
+ */
+int
+archive_write_get_bytes_per_block(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block");
+ return (a->bytes_per_block);
+}
+
+/*
+ * Set the size for the last block.
+ * Returns 0 if successful.
+ */
+int
+archive_write_set_bytes_in_last_block(struct archive *_a, int bytes)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");
+ a->bytes_in_last_block = bytes;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Return the value set above. -1 indicates it has not been set.
+ */
+int
+archive_write_get_bytes_in_last_block(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block");
+ return (a->bytes_in_last_block);
+}
+
+/*
+ * dev/ino of a file to be rejected. Used to prevent adding
+ * an archive to itself recursively.
+ */
+int
+archive_write_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_set_skip_file");
+ a->skip_file_set = 1;
+ a->skip_file_dev = d;
+ a->skip_file_ino = i;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Allocate and return the next filter structure.
+ */
+struct archive_write_filter *
+__archive_write_allocate_filter(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f;
+
+ f = calloc(1, sizeof(*f));
+
+ if (f == NULL)
+ return (NULL);
+
+ f->archive = _a;
+ f->state = ARCHIVE_WRITE_FILTER_STATE_NEW;
+ if (a->filter_first == NULL)
+ a->filter_first = f;
+ else
+ a->filter_last->next_filter = f;
+ a->filter_last = f;
+ return f;
+}
+
+/*
+ * Write data to a particular filter.
+ */
+int
+__archive_write_filter(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ int r;
+ /* Never write to non-open filters */
+ if (f->state != ARCHIVE_WRITE_FILTER_STATE_OPEN)
+ return(ARCHIVE_FATAL);
+ if (length == 0)
+ return(ARCHIVE_OK);
+ if (f->write == NULL)
+ /* If unset, a fatal error has already occurred, so this filter
+ * didn't open. We cannot write anything. */
+ return(ARCHIVE_FATAL);
+ r = (f->write)(f, buff, length);
+ f->bytes_written += length;
+ return (r);
+}
+
+/*
+ * Recursive function for opening the filter chain
+ * Last filter is opened first
+ */
+static int
+__archive_write_open_filter(struct archive_write_filter *f)
+{
+ int ret;
+
+ ret = ARCHIVE_OK;
+ if (f->next_filter != NULL)
+ ret = __archive_write_open_filter(f->next_filter);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ if (f->state != ARCHIVE_WRITE_FILTER_STATE_NEW)
+ return (ARCHIVE_FATAL);
+ if (f->open == NULL) {
+ f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN;
+ return (ARCHIVE_OK);
+ }
+ ret = (f->open)(f);
+ if (ret == ARCHIVE_OK)
+ f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN;
+ else
+ f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;
+ return (ret);
+}
+
+/*
+ * Open all filters
+ */
+static int
+__archive_write_filters_open(struct archive_write *a)
+{
+ return (__archive_write_open_filter(a->filter_first));
+}
+
+/*
+ * Close all filtes
+ */
+static int
+__archive_write_filters_close(struct archive_write *a)
+{
+ struct archive_write_filter *f;
+ int ret, ret1;
+ ret = ARCHIVE_OK;
+ for (f = a->filter_first; f != NULL; f = f->next_filter) {
+ /* Do not close filters that are not open */
+ if (f->state == ARCHIVE_WRITE_FILTER_STATE_OPEN) {
+ if (f->close != NULL) {
+ ret1 = (f->close)(f);
+ if (ret1 < ret)
+ ret = ret1;
+ if (ret1 == ARCHIVE_OK) {
+ f->state =
+ ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+ } else {
+ f->state =
+ ARCHIVE_WRITE_FILTER_STATE_FATAL;
+ }
+ } else
+ f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+ }
+ }
+ return (ret);
+}
+
+int
+__archive_write_output(struct archive_write *a, const void *buff, size_t length)
+{
+ return (__archive_write_filter(a->filter_first, buff, length));
+}
+
+static int
+__archive_write_filters_flush(struct archive_write *a)
+{
+ struct archive_write_filter *f;
+ int ret, ret1;
+
+ ret = ARCHIVE_OK;
+ for (f = a->filter_first; f != NULL; f = f->next_filter) {
+ if (f->flush != NULL && f->bytes_written > 0) {
+ ret1 = (f->flush)(f);
+ if (ret1 < ret)
+ ret = ret1;
+ if (ret1 < ARCHIVE_WARN)
+ f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;
+ }
+ }
+ return (ret);
+}
+
+int
+__archive_write_nulls(struct archive_write *a, size_t length)
+{
+ if (length == 0)
+ return (ARCHIVE_OK);
+
+ while (length > 0) {
+ size_t to_write = length < a->null_length ? length : a->null_length;
+ int r = __archive_write_output(a, a->nulls, to_write);
+ if (r < ARCHIVE_OK)
+ return (r);
+ length -= to_write;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_open(struct archive_write_filter *f)
+{
+ struct archive_write *a = (struct archive_write *)f->archive;
+ struct archive_none *state;
+ void *buffer;
+ size_t buffer_size;
+ int ret;
+
+ f->bytes_per_block = archive_write_get_bytes_per_block(f->archive);
+ f->bytes_in_last_block =
+ archive_write_get_bytes_in_last_block(f->archive);
+ buffer_size = f->bytes_per_block;
+
+ state = (struct archive_none *)calloc(1, sizeof(*state));
+ buffer = (char *)malloc(buffer_size);
+ if (state == NULL || buffer == NULL) {
+ free(state);
+ free(buffer);
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for output buffering");
+ return (ARCHIVE_FATAL);
+ }
+
+ state->buffer_size = buffer_size;
+ state->buffer = buffer;
+ state->next = state->buffer;
+ state->avail = state->buffer_size;
+ f->data = state;
+
+ if (a->client_opener == NULL)
+ return (ARCHIVE_OK);
+ ret = a->client_opener(f->archive, a->client_data);
+ if (ret != ARCHIVE_OK) {
+ free(state->buffer);
+ free(state);
+ f->data = NULL;
+ }
+ return (ret);
+}
+
+static int
+archive_write_client_write(struct archive_write_filter *f,
+ const void *_buff, size_t length)
+{
+ struct archive_write *a = (struct archive_write *)f->archive;
+ struct archive_none *state = (struct archive_none *)f->data;
+ const char *buff = (const char *)_buff;
+ ssize_t remaining, to_copy;
+ ssize_t bytes_written;
+
+ remaining = length;
+
+ /*
+ * If there is no buffer for blocking, just pass the data
+ * straight through to the client write callback. In
+ * particular, this supports "no write delay" operation for
+ * special applications. Just set the block size to zero.
+ */
+ if (state->buffer_size == 0) {
+ while (remaining > 0) {
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, buff, remaining);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ remaining -= bytes_written;
+ buff += bytes_written;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* If the copy buffer isn't empty, try to fill it. */
+ if (state->avail < state->buffer_size) {
+ /* If buffer is not empty... */
+ /* ... copy data into buffer ... */
+ to_copy = ((size_t)remaining > state->avail) ?
+ state->avail : (size_t)remaining;
+ memcpy(state->next, buff, to_copy);
+ state->next += to_copy;
+ state->avail -= to_copy;
+ buff += to_copy;
+ remaining -= to_copy;
+ /* ... if it's full, write it out. */
+ if (state->avail == 0) {
+ char *p = state->buffer;
+ size_t to_write = state->buffer_size;
+ while (to_write > 0) {
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, p, to_write);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ if ((size_t)bytes_written > to_write) {
+ archive_set_error(&(a->archive),
+ -1, "write overrun");
+ return (ARCHIVE_FATAL);
+ }
+ p += bytes_written;
+ to_write -= bytes_written;
+ }
+ state->next = state->buffer;
+ state->avail = state->buffer_size;
+ }
+ }
+
+ while ((size_t)remaining >= state->buffer_size) {
+ /* Write out full blocks directly to client. */
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, buff, state->buffer_size);
+ if (bytes_written <= 0)
+ return (ARCHIVE_FATAL);
+ buff += bytes_written;
+ remaining -= bytes_written;
+ }
+
+ if (remaining > 0) {
+ /* Copy last bit into copy buffer. */
+ memcpy(state->next, buff, remaining);
+ state->next += remaining;
+ state->avail -= remaining;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_free(struct archive_write_filter *f)
+{
+ struct archive_write *a = (struct archive_write *)f->archive;
+
+ if (a->client_freer)
+ (*a->client_freer)(&a->archive, a->client_data);
+ a->client_data = NULL;
+
+ /* Clear passphrase. */
+ if (a->passphrase != NULL) {
+ memset(a->passphrase, 0, strlen(a->passphrase));
+ free(a->passphrase);
+ a->passphrase = NULL;
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_close(struct archive_write_filter *f)
+{
+ struct archive_write *a = (struct archive_write *)f->archive;
+ struct archive_none *state = (struct archive_none *)f->data;
+ ssize_t block_length;
+ ssize_t target_block_length;
+ ssize_t bytes_written;
+ size_t to_write;
+ char *p;
+ int ret = ARCHIVE_OK;
+
+ /* If there's pending data, pad and write the last block */
+ if (state->next != state->buffer) {
+ block_length = state->buffer_size - state->avail;
+
+ /* Tricky calculation to determine size of last block */
+ if (a->bytes_in_last_block <= 0)
+ /* Default or Zero: pad to full block */
+ target_block_length = a->bytes_per_block;
+ else
+ /* Round to next multiple of bytes_in_last_block. */
+ target_block_length = a->bytes_in_last_block *
+ ( (block_length + a->bytes_in_last_block - 1) /
+ a->bytes_in_last_block);
+ if (target_block_length > a->bytes_per_block)
+ target_block_length = a->bytes_per_block;
+ if (block_length < target_block_length) {
+ memset(state->next, 0,
+ target_block_length - block_length);
+ block_length = target_block_length;
+ }
+ p = state->buffer;
+ to_write = block_length;
+ while (to_write > 0) {
+ bytes_written = (a->client_writer)(&a->archive,
+ a->client_data, p, to_write);
+ if (bytes_written <= 0) {
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ if ((size_t)bytes_written > to_write) {
+ archive_set_error(&(a->archive),
+ -1, "write overrun");
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ p += bytes_written;
+ to_write -= bytes_written;
+ }
+ }
+ if (a->client_closer)
+ (*a->client_closer)(&a->archive, a->client_data);
+ free(state->buffer);
+ free(state);
+
+ /* Clear the close handler myself not to be called again. */
+ f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+ return (ret);
+}
+
+/*
+ * Open the archive using the current settings.
+ */
+int
+archive_write_open2(struct archive *_a, void *client_data,
+ archive_open_callback *opener, archive_write_callback *writer,
+ archive_close_callback *closer, archive_free_callback *freer)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *client_filter;
+ int ret, r1;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_open");
+ archive_clear_error(&a->archive);
+
+ a->client_writer = writer;
+ a->client_opener = opener;
+ a->client_closer = closer;
+ a->client_freer = freer;
+ a->client_data = client_data;
+
+ client_filter = __archive_write_allocate_filter(_a);
+
+ if (client_filter == NULL)
+ return (ARCHIVE_FATAL);
+
+ client_filter->open = archive_write_client_open;
+ client_filter->write = archive_write_client_write;
+ client_filter->close = archive_write_client_close;
+ client_filter->free = archive_write_client_free;
+
+ ret = __archive_write_filters_open(a);
+ if (ret < ARCHIVE_WARN) {
+ r1 = __archive_write_filters_close(a);
+ __archive_write_filters_free(_a);
+ return (r1 < ret ? r1 : ret);
+ }
+
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ if (a->format_init)
+ ret = (a->format_init)(a);
+ return (ret);
+}
+
+int
+archive_write_open(struct archive *_a, void *client_data,
+ archive_open_callback *opener, archive_write_callback *writer,
+ archive_close_callback *closer)
+{
+ return archive_write_open2(_a, client_data, opener, writer,
+ closer, NULL);
+}
+
+/*
+ * Close out the archive.
+ */
+static int
+_archive_write_close(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL,
+ "archive_write_close");
+ if (a->archive.state == ARCHIVE_STATE_NEW
+ || a->archive.state == ARCHIVE_STATE_CLOSED)
+ return (ARCHIVE_OK); /* Okay to close() when not open. */
+
+ archive_clear_error(&a->archive);
+
+ /* Finish the last entry if a finish callback is specified */
+ if (a->archive.state == ARCHIVE_STATE_DATA
+ && a->format_finish_entry != NULL)
+ r = ((a->format_finish_entry)(a));
+
+ /* Finish off the archive. */
+ /* TODO: have format closers invoke compression close. */
+ if (a->format_close != NULL) {
+ r1 = (a->format_close)(a);
+ if (r1 < r)
+ r = r1;
+ }
+
+ /* Finish the compression and close the stream. */
+ r1 = __archive_write_filters_close(a);
+ if (r1 < r)
+ r = r1;
+
+ if (a->archive.state != ARCHIVE_STATE_FATAL)
+ a->archive.state = ARCHIVE_STATE_CLOSED;
+ return (r);
+}
+
+static int
+_archive_write_filter_count(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *p = a->filter_first;
+ int count = 0;
+ while(p) {
+ count++;
+ p = p->next_filter;
+ }
+ return count;
+}
+
+void
+__archive_write_filters_free(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r = ARCHIVE_OK, r1;
+
+ while (a->filter_first != NULL) {
+ struct archive_write_filter *next
+ = a->filter_first->next_filter;
+ if (a->filter_first->free != NULL) {
+ r1 = (*a->filter_first->free)(a->filter_first);
+ if (r > r1)
+ r = r1;
+ }
+ free(a->filter_first);
+ a->filter_first = next;
+ }
+ a->filter_last = NULL;
+}
+
+/*
+ * Destroy the archive structure.
+ *
+ * Be careful: user might just call write_new and then write_free.
+ * Don't assume we actually wrote anything or performed any non-trivial
+ * initialization.
+ */
+static int
+_archive_write_free(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r = ARCHIVE_OK, r1;
+
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ /* It is okay to call free() in state FATAL. */
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_free");
+ if (a->archive.state != ARCHIVE_STATE_FATAL)
+ r = archive_write_close(&a->archive);
+
+ /* Release format resources. */
+ if (a->format_free != NULL) {
+ r1 = (a->format_free)(a);
+ if (r1 < r)
+ r = r1;
+ }
+
+ __archive_write_filters_free(_a);
+
+ /* Release various dynamic buffers. */
+ free((void *)(uintptr_t)(const void *)a->nulls);
+ archive_string_free(&a->archive.error_string);
+ if (a->passphrase != NULL) {
+ /* A passphrase should be cleaned. */
+ memset(a->passphrase, 0, strlen(a->passphrase));
+ free(a->passphrase);
+ }
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a);
+ return (r);
+}
+
+/*
+ * Write the appropriate header.
+ */
+static int
+_archive_write_header(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int ret, r2;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header");
+ archive_clear_error(&a->archive);
+
+ if (a->format_write_header == NULL) {
+ archive_set_error(&(a->archive), -1,
+ "Format must be set before you can write to an archive.");
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+
+ /* In particular, "retry" and "fatal" get returned immediately. */
+ ret = archive_write_finish_entry(&a->archive);
+ if (ret == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN)
+ return (ret);
+
+ if (a->skip_file_set &&
+ archive_entry_dev_is_set(entry) &&
+ archive_entry_ino_is_set(entry) &&
+ archive_entry_dev(entry) == (dev_t)a->skip_file_dev &&
+ archive_entry_ino64(entry) == a->skip_file_ino) {
+ archive_set_error(&a->archive, 0,
+ "Can't add archive to itself");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Flush filters at boundary. */
+ r2 = __archive_write_filters_flush(a);
+ if (r2 == ARCHIVE_FAILED) {
+ return (ARCHIVE_FAILED);
+ }
+ if (r2 == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (r2 < ret)
+ ret = r2;
+
+ /* Format and write header. */
+ r2 = ((a->format_write_header)(a, entry));
+ if (r2 == ARCHIVE_FAILED) {
+ return (ARCHIVE_FAILED);
+ }
+ if (r2 == ARCHIVE_FATAL) {
+ a->archive.state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+ }
+ if (r2 < ret)
+ ret = r2;
+
+ a->archive.state = ARCHIVE_STATE_DATA;
+ return (ret);
+}
+
+static int
+_archive_write_finish_entry(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int ret = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_finish_entry");
+ if (a->archive.state & ARCHIVE_STATE_DATA
+ && a->format_finish_entry != NULL)
+ ret = (a->format_finish_entry)(a);
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ return (ret);
+}
+
+/*
+ * Note that the compressor is responsible for blocking.
+ */
+static ssize_t
+_archive_write_data(struct archive *_a, const void *buff, size_t s)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ const size_t max_write = INT_MAX;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data");
+ /* In particular, this catches attempts to pass negative values. */
+ if (s > max_write)
+ s = max_write;
+ archive_clear_error(&a->archive);
+ return ((a->format_write_data)(a, buff, s));
+}
+
+static struct archive_write_filter *
+filter_lookup(struct archive *_a, int n)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = a->filter_first;
+ if (n == -1)
+ return a->filter_last;
+ if (n < 0)
+ return NULL;
+ while (n > 0 && f != NULL) {
+ f = f->next_filter;
+ --n;
+ }
+ return f;
+}
+
+static int
+_archive_filter_code(struct archive *_a, int n)
+{
+ struct archive_write_filter *f = filter_lookup(_a, n);
+ return f == NULL ? -1 : f->code;
+}
+
+static const char *
+_archive_filter_name(struct archive *_a, int n)
+{
+ struct archive_write_filter *f = filter_lookup(_a, n);
+ return f != NULL ? f->name : NULL;
+}
+
+static int64_t
+_archive_filter_bytes(struct archive *_a, int n)
+{
+ struct archive_write_filter *f = filter_lookup(_a, n);
+ return f == NULL ? -1 : f->bytes_written;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter.c
new file mode 100644
index 0000000000..203f4142b5
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2012 Ondrej Holy
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps filter codes to functions. */
+static const
+struct { int code; int (*setter)(struct archive *); } codes[] =
+{
+ { ARCHIVE_FILTER_NONE, archive_write_add_filter_none },
+ { ARCHIVE_FILTER_GZIP, archive_write_add_filter_gzip },
+ { ARCHIVE_FILTER_BZIP2, archive_write_add_filter_bzip2 },
+ { ARCHIVE_FILTER_COMPRESS, archive_write_add_filter_compress },
+ { ARCHIVE_FILTER_GRZIP, archive_write_add_filter_grzip },
+ { ARCHIVE_FILTER_LRZIP, archive_write_add_filter_lrzip },
+ { ARCHIVE_FILTER_LZ4, archive_write_add_filter_lz4 },
+ { ARCHIVE_FILTER_LZIP, archive_write_add_filter_lzip },
+ { ARCHIVE_FILTER_LZMA, archive_write_add_filter_lzma },
+ { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzip },
+ { ARCHIVE_FILTER_UU, archive_write_add_filter_uuencode },
+ { ARCHIVE_FILTER_XZ, archive_write_add_filter_xz },
+ { ARCHIVE_FILTER_ZSTD, archive_write_add_filter_zstd },
+ { -1, NULL }
+};
+
+int
+archive_write_add_filter(struct archive *a, int code)
+{
+ int i;
+
+ for (i = 0; codes[i].code != -1; i++) {
+ if (code == codes[i].code)
+ return ((codes[i].setter)(a));
+ }
+
+ archive_set_error(a, EINVAL, "No such filter");
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_b64encode.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_b64encode.c
new file mode 100644
index 0000000000..87fdb73ecb
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_b64encode.c
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#define LBYTES 57
+
+struct private_b64encode {
+ int mode;
+ struct archive_string name;
+ struct archive_string encoded_buff;
+ size_t bs;
+ size_t hold_len;
+ unsigned char hold[LBYTES];
+};
+
+static int archive_filter_b64encode_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_filter_b64encode_open(struct archive_write_filter *);
+static int archive_filter_b64encode_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_filter_b64encode_close(struct archive_write_filter *);
+static int archive_filter_b64encode_free(struct archive_write_filter *);
+static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);
+static int64_t atol8(const char *, size_t);
+
+static const char base64[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_b64encode(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_b64encode *state;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
+
+ state = (struct private_b64encode *)calloc(1, sizeof(*state));
+ if (state == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for b64encode filter");
+ return (ARCHIVE_FATAL);
+ }
+ archive_strcpy(&state->name, "-");
+ state->mode = 0644;
+
+ f->data = state;
+ f->name = "b64encode";
+ f->code = ARCHIVE_FILTER_UU;
+ f->open = archive_filter_b64encode_open;
+ f->options = archive_filter_b64encode_options;
+ f->write = archive_filter_b64encode_write;
+ f->close = archive_filter_b64encode_close;
+ f->free = archive_filter_b64encode_free;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+ if (strcmp(key, "mode") == 0) {
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "mode option requires octal digits");
+ return (ARCHIVE_FAILED);
+ }
+ state->mode = (int)atol8(value, strlen(value)) & 0777;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "name") == 0) {
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "name option requires a string");
+ return (ARCHIVE_FAILED);
+ }
+ archive_strcpy(&state->name, value);
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_b64encode_open(struct archive_write_filter *f)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+ size_t bs = 65536, bpb;
+
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+
+ state->bs = bs;
+ if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for b64encode buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
+ state->mode, state->name.s);
+
+ f->data = state;
+ return (0);
+}
+
+static void
+la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len)
+{
+ int c;
+
+ for (; len >= 3; p += 3, len -= 3) {
+ c = p[0] >> 2;
+ archive_strappend_char(as, base64[c]);
+ c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
+ archive_strappend_char(as, base64[c]);
+ c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
+ archive_strappend_char(as, base64[c]);
+ c = p[2] & 0x3f;
+ archive_strappend_char(as, base64[c]);
+ }
+ if (len > 0) {
+ c = p[0] >> 2;
+ archive_strappend_char(as, base64[c]);
+ c = (p[0] & 0x03) << 4;
+ if (len == 1) {
+ archive_strappend_char(as, base64[c]);
+ archive_strappend_char(as, '=');
+ archive_strappend_char(as, '=');
+ } else {
+ c |= (p[1] & 0xf0) >> 4;
+ archive_strappend_char(as, base64[c]);
+ c = (p[1] & 0x0f) << 2;
+ archive_strappend_char(as, base64[c]);
+ archive_strappend_char(as, '=');
+ }
+ }
+ archive_strappend_char(as, '\n');
+}
+
+/*
+ * Write data to the encoded stream.
+ */
+static int
+archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+ const unsigned char *p = buff;
+ int ret = ARCHIVE_OK;
+
+ if (length == 0)
+ return (ret);
+
+ if (state->hold_len) {
+ while (state->hold_len < LBYTES && length > 0) {
+ state->hold[state->hold_len++] = *p++;
+ length--;
+ }
+ if (state->hold_len < LBYTES)
+ return (ret);
+ la_b64_encode(&state->encoded_buff, state->hold, LBYTES);
+ state->hold_len = 0;
+ }
+
+ for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
+ la_b64_encode(&state->encoded_buff, p, LBYTES);
+
+ /* Save remaining bytes. */
+ if (length > 0) {
+ memcpy(state->hold, p, length);
+ state->hold_len = length;
+ }
+ while (archive_strlen(&state->encoded_buff) >= state->bs) {
+ ret = __archive_write_filter(f->next_filter,
+ state->encoded_buff.s, state->bs);
+ memmove(state->encoded_buff.s,
+ state->encoded_buff.s + state->bs,
+ state->encoded_buff.length - state->bs);
+ state->encoded_buff.length -= state->bs;
+ }
+
+ return (ret);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_filter_b64encode_close(struct archive_write_filter *f)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+ /* Flush remaining bytes. */
+ if (state->hold_len != 0)
+ la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);
+ archive_string_sprintf(&state->encoded_buff, "====\n");
+ /* Write the last block */
+ archive_write_set_bytes_in_last_block(f->archive, 1);
+ return __archive_write_filter(f->next_filter,
+ state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+}
+
+static int
+archive_filter_b64encode_free(struct archive_write_filter *f)
+{
+ struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+ archive_string_free(&state->name);
+ archive_string_free(&state->encoded_buff);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= '0' && *p <= '7')
+ digit = *p - '0';
+ else
+ break;
+ p++;
+ l <<= 3;
+ l |= digit;
+ }
+ return (l);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_by_name.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_by_name.c
new file mode 100644
index 0000000000..ffa633c963
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_by_name.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*setter)(struct archive *); } names[] =
+{
+ { "b64encode", archive_write_add_filter_b64encode },
+ { "bzip2", archive_write_add_filter_bzip2 },
+ { "compress", archive_write_add_filter_compress },
+ { "grzip", archive_write_add_filter_grzip },
+ { "gzip", archive_write_add_filter_gzip },
+ { "lrzip", archive_write_add_filter_lrzip },
+ { "lz4", archive_write_add_filter_lz4 },
+ { "lzip", archive_write_add_filter_lzip },
+ { "lzma", archive_write_add_filter_lzma },
+ { "lzop", archive_write_add_filter_lzop },
+ { "uuencode", archive_write_add_filter_uuencode },
+ { "xz", archive_write_add_filter_xz },
+ { "zstd", archive_write_add_filter_zstd },
+ { NULL, NULL }
+};
+
+int
+archive_write_add_filter_by_name(struct archive *a, const char *name)
+{
+ int i;
+
+ for (i = 0; names[i].name != NULL; i++) {
+ if (strcmp(name, names[i].name) == 0)
+ return ((names[i].setter)(a));
+ }
+
+ archive_set_error(a, EINVAL, "No such filter '%s'", name);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_bzip2.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_bzip2.c
new file mode 100644
index 0000000000..3e5c0891ae
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_bzip2.c
@@ -0,0 +1,401 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_bzip2(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_bzip2(a));
+}
+#endif
+
+struct private_data {
+ int compression_level;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ bz_stream stream;
+ int64_t total_in;
+ char *compressed;
+ size_t compressed_buffer_size;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_compressor_bzip2_close(struct archive_write_filter *);
+static int archive_compressor_bzip2_free(struct archive_write_filter *);
+static int archive_compressor_bzip2_open(struct archive_write_filter *);
+static int archive_compressor_bzip2_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_bzip2_write(struct archive_write_filter *,
+ const void *, size_t);
+
+/*
+ * Add a bzip2 compression filter to this write handle.
+ */
+int
+archive_write_add_filter_bzip2(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 9; /* default */
+
+ f->data = data;
+ f->options = &archive_compressor_bzip2_options;
+ f->close = &archive_compressor_bzip2_close;
+ f->free = &archive_compressor_bzip2_free;
+ f->open = &archive_compressor_bzip2_open;
+ f->code = ARCHIVE_FILTER_BZIP2;
+ f->name = "bzip2";
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("bzip2");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external bzip2 program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_bzip2_options(struct archive_write_filter *f,
+ const char *key, const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ /* Make '0' be a synonym for '1'. */
+ /* This way, bzip2 compressor supports the same 0..9
+ * range of levels as gzip. */
+ if (data->compression_level < 1)
+ data->compression_level = 1;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+/* Don't compile this if we don't have bzlib. */
+
+/*
+ * Yuck. bzlib.h is not const-correct, so I need this one bit
+ * of ugly hackery to convert a const * pointer to a non-const pointer.
+ */
+#define SET_NEXT_IN(st,src) \
+ (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int finishing);
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_bzip2_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ if (data->compressed == NULL) {
+ size_t bs = 65536, bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->compressed_buffer_size = bs;
+ data->compressed
+ = (char *)malloc(data->compressed_buffer_size);
+ if (data->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ memset(&data->stream, 0, sizeof(data->stream));
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
+ f->write = archive_compressor_bzip2_write;
+
+ /* Initialize compression library */
+ ret = BZ2_bzCompressInit(&(data->stream),
+ data->compression_level, 0, 30);
+ if (ret == BZ_OK) {
+ f->data = data;
+ return (ARCHIVE_OK);
+ }
+
+ /* Library setup failed: clean up. */
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+
+ /* Override the error message if we know what really went wrong. */
+ switch (ret) {
+ case BZ_PARAM_ERROR:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "invalid setup parameter");
+ break;
+ case BZ_MEM_ERROR:
+ archive_set_error(f->archive, ENOMEM,
+ "Internal error initializing compression library: "
+ "out of memory");
+ break;
+ case BZ_CONFIG_ERROR:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "mis-compiled library");
+ break;
+ }
+
+ return (ARCHIVE_FATAL);
+
+}
+
+/*
+ * Write data to the compressed stream.
+ *
+ * Returns ARCHIVE_OK if all data written, error otherwise.
+ */
+static int
+archive_compressor_bzip2_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ /* Update statistics */
+ data->total_in += length;
+
+ /* Compress input data to output buffer */
+ SET_NEXT_IN(data, buff);
+ data->stream.avail_in = (uint32_t)length;
+ if (drive_compressor(f, data, 0))
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression.
+ */
+static int
+archive_compressor_bzip2_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Finish compression cycle. */
+ ret = drive_compressor(f, data, 1);
+ if (ret == ARCHIVE_OK) {
+ /* Write the last block */
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size - data->stream.avail_out);
+ }
+
+ switch (BZ2_bzCompressEnd(&(data->stream))) {
+ case BZ_OK:
+ break;
+ default:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ }
+ return ret;
+}
+
+static int
+archive_compressor_bzip2_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ free(data->compressed);
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Utility function to push input data through compressor, writing
+ * full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int finishing)
+{
+ int ret;
+
+ for (;;) {
+ if (data->stream.avail_out == 0) {
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size);
+ if (ret != ARCHIVE_OK) {
+ /* TODO: Handle this write failure */
+ return (ARCHIVE_FATAL);
+ }
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = (uint32_t)data->compressed_buffer_size;
+ }
+
+ /* If there's nothing to do, we're done. */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+
+ ret = BZ2_bzCompress(&(data->stream),
+ finishing ? BZ_FINISH : BZ_RUN);
+
+ switch (ret) {
+ case BZ_RUN_OK:
+ /* In non-finishing case, did compressor
+ * consume everything? */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+ break;
+ case BZ_FINISH_OK: /* Finishing: There's more work to do */
+ break;
+ case BZ_STREAM_END: /* Finishing: all done */
+ /* Only occurs in finishing case */
+ return (ARCHIVE_OK);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(f->archive,
+ ARCHIVE_ERRNO_PROGRAMMER,
+ "Bzip2 compression failed;"
+ " BZ2_bzCompress() returned %d",
+ ret);
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
+
+static int
+archive_compressor_bzip2_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "bzip2");
+
+ /* Specify compression level. */
+ if (data->compression_level > 0) {
+ archive_strcat(&as, " -");
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+ f->write = archive_compressor_bzip2_write;
+
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_bzip2_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_compressor_bzip2_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ __archive_write_program_free(data->pdata);
+ free(data);
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_compress.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_compress.c
new file mode 100644
index 0000000000..3ed269fce9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_compress.c
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_compress.c 201111 2009-12-28 03:33:05Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#define HSIZE 69001 /* 95% occupancy */
+#define HSHIFT 8 /* 8 - trunc(log2(HSIZE / 65536)) */
+#define CHECK_GAP 10000 /* Ratio check interval. */
+
+#define MAXCODE(bits) ((1 << (bits)) - 1)
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define FIRST 257 /* First free entry. */
+#define CLEAR 256 /* Table clear output code. */
+
+struct private_data {
+ int64_t in_count, out_count, checkpoint;
+
+ int code_len; /* Number of bits/code. */
+ int cur_maxcode; /* Maximum code, given n_bits. */
+ int max_maxcode; /* Should NEVER generate this code. */
+ int hashtab [HSIZE];
+ unsigned short codetab [HSIZE];
+ int first_free; /* First unused entry. */
+ int compress_ratio;
+
+ int cur_code, cur_fcode;
+
+ int bit_offset;
+ unsigned char bit_buf;
+
+ unsigned char *compressed;
+ size_t compressed_buffer_size;
+ size_t compressed_offset;
+};
+
+static int archive_compressor_compress_open(struct archive_write_filter *);
+static int archive_compressor_compress_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_compress_close(struct archive_write_filter *);
+static int archive_compressor_compress_free(struct archive_write_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_compress(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_compress(a));
+}
+#endif
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_compress(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_compress");
+ f->open = &archive_compressor_compress_open;
+ f->code = ARCHIVE_FILTER_COMPRESS;
+ f->name = "compress";
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_compress_open(struct archive_write_filter *f)
+{
+ struct private_data *state;
+ size_t bs = 65536, bpb;
+
+ f->code = ARCHIVE_FILTER_COMPRESS;
+ f->name = "compress";
+
+ state = (struct private_data *)calloc(1, sizeof(*state));
+ if (state == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ state->compressed_buffer_size = bs;
+ state->compressed = malloc(state->compressed_buffer_size);
+
+ if (state->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ free(state);
+ return (ARCHIVE_FATAL);
+ }
+
+ f->write = archive_compressor_compress_write;
+ f->close = archive_compressor_compress_close;
+ f->free = archive_compressor_compress_free;
+
+ state->max_maxcode = 0x10000; /* Should NEVER generate this code. */
+ state->in_count = 0; /* Length of input. */
+ state->bit_buf = 0;
+ state->bit_offset = 0;
+ state->out_count = 3; /* Includes 3-byte header mojo. */
+ state->compress_ratio = 0;
+ state->checkpoint = CHECK_GAP;
+ state->code_len = 9;
+ state->cur_maxcode = MAXCODE(state->code_len);
+ state->first_free = FIRST;
+
+ memset(state->hashtab, 0xff, sizeof(state->hashtab));
+
+ /* Prime output buffer with a gzip header. */
+ state->compressed[0] = 0x1f; /* Compress */
+ state->compressed[1] = 0x9d;
+ state->compressed[2] = 0x90; /* Block mode, 16bit max */
+ state->compressed_offset = 3;
+
+ f->data = state;
+ return (0);
+}
+
+/*-
+ * Output the given code.
+ * Inputs:
+ * code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ * that n_bits <= (long)wordsize - 1.
+ * Outputs:
+ * Outputs code to the file.
+ * Assumptions:
+ * Chars are 8 bits long.
+ * Algorithm:
+ * Maintain a BITS character long buffer (so that 8 codes will
+ * fit in it exactly). Use the VAX insv instruction to insert each
+ * code in turn. When the buffer fills up empty it and start over.
+ */
+
+static const unsigned char rmask[9] =
+ {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+static int
+output_byte(struct archive_write_filter *f, unsigned char c)
+{
+ struct private_data *state = f->data;
+
+ state->compressed[state->compressed_offset++] = c;
+ ++state->out_count;
+
+ if (state->compressed_buffer_size == state->compressed_offset) {
+ int ret = __archive_write_filter(f->next_filter,
+ state->compressed, state->compressed_buffer_size);
+ if (ret != ARCHIVE_OK)
+ return ARCHIVE_FATAL;
+ state->compressed_offset = 0;
+ }
+
+ return ARCHIVE_OK;
+}
+
+static int
+output_code(struct archive_write_filter *f, int ocode)
+{
+ struct private_data *state = f->data;
+ int bits, ret, clear_flg, bit_offset;
+
+ clear_flg = ocode == CLEAR;
+
+ /*
+ * Since ocode is always >= 8 bits, only need to mask the first
+ * hunk on the left.
+ */
+ bit_offset = state->bit_offset % 8;
+ state->bit_buf |= (ocode << bit_offset) & 0xff;
+ output_byte(f, state->bit_buf);
+
+ bits = state->code_len - (8 - bit_offset);
+ ocode >>= 8 - bit_offset;
+ /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+ if (bits >= 8) {
+ output_byte(f, ocode & 0xff);
+ ocode >>= 8;
+ bits -= 8;
+ }
+ /* Last bits. */
+ state->bit_offset += state->code_len;
+ state->bit_buf = ocode & rmask[bits];
+ if (state->bit_offset == state->code_len * 8)
+ state->bit_offset = 0;
+
+ /*
+ * If the next entry is going to be too big for the ocode size,
+ * then increase it, if possible.
+ */
+ if (clear_flg || state->first_free > state->cur_maxcode) {
+ /*
+ * Write the whole buffer, because the input side won't
+ * discover the size increase until after it has read it.
+ */
+ if (state->bit_offset > 0) {
+ while (state->bit_offset < state->code_len * 8) {
+ ret = output_byte(f, state->bit_buf);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ state->bit_offset += 8;
+ state->bit_buf = 0;
+ }
+ }
+ state->bit_buf = 0;
+ state->bit_offset = 0;
+
+ if (clear_flg) {
+ state->code_len = 9;
+ state->cur_maxcode = MAXCODE(state->code_len);
+ } else {
+ state->code_len++;
+ if (state->code_len == 16)
+ state->cur_maxcode = state->max_maxcode;
+ else
+ state->cur_maxcode = MAXCODE(state->code_len);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+output_flush(struct archive_write_filter *f)
+{
+ struct private_data *state = f->data;
+ int ret;
+
+ /* At EOF, write the rest of the buffer. */
+ if (state->bit_offset % 8) {
+ state->code_len = (state->bit_offset % 8 + 7) / 8;
+ ret = output_byte(f, state->bit_buf);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_compress_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *state = (struct private_data *)f->data;
+ int i;
+ int ratio;
+ int c, disp, ret;
+ const unsigned char *bp;
+
+ if (length == 0)
+ return ARCHIVE_OK;
+
+ bp = buff;
+
+ if (state->in_count == 0) {
+ state->cur_code = *bp++;
+ ++state->in_count;
+ --length;
+ }
+
+ while (length--) {
+ c = *bp++;
+ state->in_count++;
+ state->cur_fcode = (c << 16) | state->cur_code;
+ i = ((c << HSHIFT) ^ state->cur_code); /* Xor hashing. */
+
+ if (state->hashtab[i] == state->cur_fcode) {
+ state->cur_code = state->codetab[i];
+ continue;
+ }
+ if (state->hashtab[i] < 0) /* Empty slot. */
+ goto nomatch;
+ /* Secondary hash (after G. Knott). */
+ if (i == 0)
+ disp = 1;
+ else
+ disp = HSIZE - i;
+ probe:
+ if ((i -= disp) < 0)
+ i += HSIZE;
+
+ if (state->hashtab[i] == state->cur_fcode) {
+ state->cur_code = state->codetab[i];
+ continue;
+ }
+ if (state->hashtab[i] >= 0)
+ goto probe;
+ nomatch:
+ ret = output_code(f, state->cur_code);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ state->cur_code = c;
+ if (state->first_free < state->max_maxcode) {
+ state->codetab[i] = state->first_free++; /* code -> hashtable */
+ state->hashtab[i] = state->cur_fcode;
+ continue;
+ }
+ if (state->in_count < state->checkpoint)
+ continue;
+
+ state->checkpoint = state->in_count + CHECK_GAP;
+
+ if (state->in_count <= 0x007fffff && state->out_count != 0)
+ ratio = (int)(state->in_count * 256 / state->out_count);
+ else if ((ratio = (int)(state->out_count / 256)) == 0)
+ ratio = 0x7fffffff;
+ else
+ ratio = (int)(state->in_count / ratio);
+
+ if (ratio > state->compress_ratio)
+ state->compress_ratio = ratio;
+ else {
+ state->compress_ratio = 0;
+ memset(state->hashtab, 0xff, sizeof(state->hashtab));
+ state->first_free = FIRST;
+ ret = output_code(f, CLEAR);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_compress_close(struct archive_write_filter *f)
+{
+ struct private_data *state = (struct private_data *)f->data;
+ int ret;
+
+ ret = output_code(f, state->cur_code);
+ if (ret != ARCHIVE_OK)
+ return ret;
+ ret = output_flush(f);
+ if (ret != ARCHIVE_OK)
+ return ret;
+
+ /* Write the last block */
+ ret = __archive_write_filter(f->next_filter,
+ state->compressed, state->compressed_offset);
+ return (ret);
+}
+
+static int
+archive_compressor_compress_free(struct archive_write_filter *f)
+{
+ struct private_data *state = (struct private_data *)f->data;
+
+ free(state->compressed);
+ free(state);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_grzip.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_grzip.c
new file mode 100644
index 0000000000..371102d74c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_grzip.c
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_write_private.h"
+
+struct write_grzip {
+ struct archive_write_program_data *pdata;
+};
+
+static int archive_write_grzip_open(struct archive_write_filter *);
+static int archive_write_grzip_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_write_grzip_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_write_grzip_close(struct archive_write_filter *);
+static int archive_write_grzip_free(struct archive_write_filter *);
+
+int
+archive_write_add_filter_grzip(struct archive *_a)
+{
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct write_grzip *data;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_grzip");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->pdata = __archive_write_program_allocate("grzip");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ f->name = "grzip";
+ f->code = ARCHIVE_FILTER_GRZIP;
+ f->data = data;
+ f->open = archive_write_grzip_open;
+ f->options = archive_write_grzip_options;
+ f->write = archive_write_grzip_write;
+ f->close = archive_write_grzip_close;
+ f->free = archive_write_grzip_free;
+
+ /* Note: This filter always uses an external program, so we
+ * return "warn" to inform of the fact. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external grzip program for grzip compression");
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_grzip_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ (void)f; /* UNUSED */
+ (void)key; /* UNUSED */
+ (void)value; /* UNUSED */
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_grzip_open(struct archive_write_filter *f)
+{
+ struct write_grzip *data = (struct write_grzip *)f->data;
+
+ return __archive_write_program_open(f, data->pdata, "grzip");
+}
+
+static int
+archive_write_grzip_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct write_grzip *data = (struct write_grzip *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_grzip_close(struct archive_write_filter *f)
+{
+ struct write_grzip *data = (struct write_grzip *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_write_grzip_free(struct archive_write_filter *f)
+{
+ struct write_grzip *data = (struct write_grzip *)f->data;
+
+ __archive_write_program_free(data->pdata);
+ free(data);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_gzip.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_gzip.c
new file mode 100644
index 0000000000..8670d5ca74
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_gzip.c
@@ -0,0 +1,442 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_gzip.c 201081 2009-12-28 02:04:42Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_gzip(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_gzip(a));
+}
+#endif
+
+/* Don't compile this if we don't have zlib. */
+
+struct private_data {
+ int compression_level;
+ int timestamp;
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ int64_t total_in;
+ unsigned char *compressed;
+ size_t compressed_buffer_size;
+ unsigned long crc;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+/*
+ * Yuck. zlib.h is not const-correct, so I need this one bit
+ * of ugly hackery to convert a const * pointer to a non-const pointer.
+ */
+#define SET_NEXT_IN(st,src) \
+ (st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src)
+
+static int archive_compressor_gzip_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_gzip_open(struct archive_write_filter *);
+static int archive_compressor_gzip_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_gzip_close(struct archive_write_filter *);
+static int archive_compressor_gzip_free(struct archive_write_filter *);
+#ifdef HAVE_ZLIB_H
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int finishing);
+#endif
+
+
+/*
+ * Add a gzip compression filter to this write handle.
+ */
+int
+archive_write_add_filter_gzip(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ f->data = data;
+ f->open = &archive_compressor_gzip_open;
+ f->options = &archive_compressor_gzip_options;
+ f->close = &archive_compressor_gzip_close;
+ f->free = &archive_compressor_gzip_free;
+ f->code = ARCHIVE_FILTER_GZIP;
+ f->name = "gzip";
+#ifdef HAVE_ZLIB_H
+ data->compression_level = Z_DEFAULT_COMPRESSION;
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("gzip");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external gzip program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_compressor_gzip_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+#ifdef HAVE_ZLIB_H
+ free(data->compressed);
+#else
+ __archive_write_program_free(data->pdata);
+#endif
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "timestamp") == 0) {
+ data->timestamp = (value == NULL)?-1:1;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#ifdef HAVE_ZLIB_H
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_gzip_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ if (data->compressed == NULL) {
+ size_t bs = 65536, bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of
+ * the of bytes per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->compressed_buffer_size = bs;
+ data->compressed
+ = (unsigned char *)malloc(data->compressed_buffer_size);
+ if (data->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ data->crc = crc32(0L, NULL, 0);
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = (uInt)data->compressed_buffer_size;
+
+ /* Prime output buffer with a gzip header. */
+ data->compressed[0] = 0x1f; /* GZip signature bytes */
+ data->compressed[1] = 0x8b;
+ data->compressed[2] = 0x08; /* "Deflate" compression */
+ data->compressed[3] = 0; /* No options */
+ if (data->timestamp >= 0) {
+ time_t t = time(NULL);
+ data->compressed[4] = (uint8_t)(t)&0xff; /* Timestamp */
+ data->compressed[5] = (uint8_t)(t>>8)&0xff;
+ data->compressed[6] = (uint8_t)(t>>16)&0xff;
+ data->compressed[7] = (uint8_t)(t>>24)&0xff;
+ } else
+ memset(&data->compressed[4], 0, 4);
+ if (data->compression_level == 9)
+ data->compressed[8] = 2;
+ else if(data->compression_level == 1)
+ data->compressed[8] = 4;
+ else
+ data->compressed[8] = 0;
+ data->compressed[9] = 3; /* OS=Unix */
+ data->stream.next_out += 10;
+ data->stream.avail_out -= 10;
+
+ f->write = archive_compressor_gzip_write;
+
+ /* Initialize compression library. */
+ ret = deflateInit2(&(data->stream),
+ data->compression_level,
+ Z_DEFLATED,
+ -15 /* < 0 to suppress zlib header */,
+ 8,
+ Z_DEFAULT_STRATEGY);
+
+ if (ret == Z_OK) {
+ f->data = data;
+ return (ARCHIVE_OK);
+ }
+
+ /* Library setup failed: clean up. */
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error "
+ "initializing compression library");
+
+ /* Override the error message if we know what really went wrong. */
+ switch (ret) {
+ case Z_STREAM_ERROR:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: invalid setup parameter");
+ break;
+ case Z_MEM_ERROR:
+ archive_set_error(f->archive, ENOMEM,
+ "Internal error initializing compression library");
+ break;
+ case Z_VERSION_ERROR:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: invalid library version");
+ break;
+ }
+
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Update statistics */
+ data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length);
+ data->total_in += length;
+
+ /* Compress input data to output buffer */
+ SET_NEXT_IN(data, buff);
+ data->stream.avail_in = (uInt)length;
+ if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+ return (ret);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_gzip_close(struct archive_write_filter *f)
+{
+ unsigned char trailer[8];
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Finish compression cycle */
+ ret = drive_compressor(f, data, 1);
+ if (ret == ARCHIVE_OK) {
+ /* Write the last compressed data. */
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size - data->stream.avail_out);
+ }
+ if (ret == ARCHIVE_OK) {
+ /* Build and write out 8-byte trailer. */
+ trailer[0] = (uint8_t)(data->crc)&0xff;
+ trailer[1] = (uint8_t)(data->crc >> 8)&0xff;
+ trailer[2] = (uint8_t)(data->crc >> 16)&0xff;
+ trailer[3] = (uint8_t)(data->crc >> 24)&0xff;
+ trailer[4] = (uint8_t)(data->total_in)&0xff;
+ trailer[5] = (uint8_t)(data->total_in >> 8)&0xff;
+ trailer[6] = (uint8_t)(data->total_in >> 16)&0xff;
+ trailer[7] = (uint8_t)(data->total_in >> 24)&0xff;
+ ret = __archive_write_filter(f->next_filter, trailer, 8);
+ }
+
+ switch (deflateEnd(&(data->stream))) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ }
+ return ret;
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int finishing)
+{
+ int ret;
+
+ for (;;) {
+ if (data->stream.avail_out == 0) {
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out =
+ (uInt)data->compressed_buffer_size;
+ }
+
+ /* If there's nothing to do, we're done. */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+
+ ret = deflate(&(data->stream),
+ finishing ? Z_FINISH : Z_NO_FLUSH );
+
+ switch (ret) {
+ case Z_OK:
+ /* In non-finishing case, check if compressor
+ * consumed everything */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+ /* In finishing case, this return always means
+ * there's more work */
+ break;
+ case Z_STREAM_END:
+ /* This return can only occur in finishing case. */
+ return (ARCHIVE_OK);
+ default:
+ /* Any other return value indicates an error. */
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "GZip compression failed:"
+ " deflate() call returned status %d",
+ ret);
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+#else /* HAVE_ZLIB_H */
+
+static int
+archive_compressor_gzip_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "gzip");
+
+ /* Specify compression level. */
+ if (data->compression_level > 0) {
+ archive_strcat(&as, " -");
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+ if (data->timestamp < 0)
+ /* Do not save timestamp. */
+ archive_strcat(&as, " -n");
+ else if (data->timestamp > 0)
+ /* Save timestamp. */
+ archive_strcat(&as, " -N");
+
+ f->write = archive_compressor_gzip_write;
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_gzip_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+#endif /* HAVE_ZLIB_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_lrzip.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lrzip.c
new file mode 100644
index 0000000000..e215f89032
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lrzip.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+struct write_lrzip {
+ struct archive_write_program_data *pdata;
+ int compression_level;
+ enum { lzma = 0, bzip2, gzip, lzo, none, zpaq } compression;
+};
+
+static int archive_write_lrzip_open(struct archive_write_filter *);
+static int archive_write_lrzip_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_write_lrzip_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_write_lrzip_close(struct archive_write_filter *);
+static int archive_write_lrzip_free(struct archive_write_filter *);
+
+int
+archive_write_add_filter_lrzip(struct archive *_a)
+{
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct write_lrzip *data;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lrzip");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->pdata = __archive_write_program_allocate("lrzip");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ f->name = "lrzip";
+ f->code = ARCHIVE_FILTER_LRZIP;
+ f->data = data;
+ f->open = archive_write_lrzip_open;
+ f->options = archive_write_lrzip_options;
+ f->write = archive_write_lrzip_write;
+ f->close = archive_write_lrzip_close;
+ f->free = archive_write_lrzip_free;
+
+ /* Note: This filter always uses an external program, so we
+ * return "warn" to inform of the fact. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lrzip program for lrzip compression");
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+ if (strcmp(key, "compression") == 0) {
+ if (value == NULL)
+ return (ARCHIVE_WARN);
+ else if (strcmp(value, "bzip2") == 0)
+ data->compression = bzip2;
+ else if (strcmp(value, "gzip") == 0)
+ data->compression = gzip;
+ else if (strcmp(value, "lzo") == 0)
+ data->compression = lzo;
+ else if (strcmp(value, "none") == 0)
+ data->compression = none;
+ else if (strcmp(value, "zpaq") == 0)
+ data->compression = zpaq;
+ else
+ return (ARCHIVE_WARN);
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_lrzip_open(struct archive_write_filter *f)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "lrzip -q");
+
+ /* Specify compression type. */
+ switch (data->compression) {
+ case lzma:/* default compression */
+ break;
+ case bzip2:
+ archive_strcat(&as, " -b");
+ break;
+ case gzip:
+ archive_strcat(&as, " -g");
+ break;
+ case lzo:
+ archive_strcat(&as, " -l");
+ break;
+ case none:
+ archive_strcat(&as, " -n");
+ break;
+ case zpaq:
+ archive_strcat(&as, " -z");
+ break;
+ }
+
+ /* Specify compression level. */
+ if (data->compression_level > 0) {
+ archive_strcat(&as, " -L ");
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_write_lrzip_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_lrzip_close(struct archive_write_filter *f)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_write_lrzip_free(struct archive_write_filter *f)
+{
+ struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+ __archive_write_program_free(data->pdata);
+ free(data);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_lz4.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lz4.c
new file mode 100644
index 0000000000..6ac450357d
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lz4.c
@@ -0,0 +1,700 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+#ifdef HAVE_LZ4HC_H
+#include <lz4hc.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_xxhash.h"
+
+#define LZ4_MAGICNUMBER 0x184d2204
+
+struct private_data {
+ int compression_level;
+ unsigned header_written:1;
+ unsigned version_number:1;
+ unsigned block_independence:1;
+ unsigned block_checksum:1;
+ unsigned stream_size:1;
+ unsigned stream_checksum:1;
+ unsigned preset_dictionary:1;
+ unsigned block_maximum_size:3;
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+ int64_t total_in;
+ char *out;
+ char *out_buffer;
+ size_t out_buffer_size;
+ size_t out_block_size;
+ char *in;
+ char *in_buffer_allocated;
+ char *in_buffer;
+ size_t in_buffer_size;
+ size_t block_size;
+
+ void *xxh32_state;
+ void *lz4_stream;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_filter_lz4_close(struct archive_write_filter *);
+static int archive_filter_lz4_free(struct archive_write_filter *);
+static int archive_filter_lz4_open(struct archive_write_filter *);
+static int archive_filter_lz4_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_filter_lz4_write(struct archive_write_filter *,
+ const void *, size_t);
+
+/*
+ * Add a lz4 compression filter to this write handle.
+ */
+int
+archive_write_add_filter_lz4(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Setup default settings.
+ */
+ data->compression_level = 1;
+ data->version_number = 0x01;
+ data->block_independence = 1;
+ data->block_checksum = 0;
+ data->stream_size = 0;
+ data->stream_checksum = 1;
+ data->preset_dictionary = 0;
+ data->block_maximum_size = 7;
+
+ /*
+ * Setup a filter setting.
+ */
+ f->data = data;
+ f->options = &archive_filter_lz4_options;
+ f->close = &archive_filter_lz4_close;
+ f->free = &archive_filter_lz4_free;
+ f->open = &archive_filter_lz4_open;
+ f->code = ARCHIVE_FILTER_LZ4;
+ f->name = "lz4";
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+ return (ARCHIVE_OK);
+#else
+ /*
+ * We don't have lz4 library, and execute external lz4 program
+ * instead.
+ */
+ data->pdata = __archive_write_program_allocate("lz4");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external lz4 program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_lz4_options(struct archive_write_filter *f,
+ const char *key, const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ int val;
+ if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+
+#ifndef HAVE_LZ4HC_H
+ if(val >= 3)
+ {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
+ "High compression not included in this build");
+ return (ARCHIVE_FATAL);
+ }
+#endif
+ data->compression_level = val;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "stream-checksum") == 0) {
+ data->stream_checksum = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "block-checksum") == 0) {
+ data->block_checksum = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "block-size") == 0) {
+ if (value == NULL || !(value[0] >= '4' && value[0] <= '7') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->block_maximum_size = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "block-dependence") == 0) {
+ data->block_independence = value == NULL;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+/* Don't compile this if we don't have liblz4. */
+
+static int drive_compressor(struct archive_write_filter *, const char *,
+ size_t);
+static int drive_compressor_independence(struct archive_write_filter *,
+ const char *, size_t);
+static int drive_compressor_dependence(struct archive_write_filter *,
+ const char *, size_t);
+static int lz4_write_stream_descriptor(struct archive_write_filter *);
+static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *,
+ size_t);
+
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_lz4_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ size_t required_size;
+ static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
+ 4 * 1024 * 1024 };
+ size_t pre_block_size;
+
+ if (data->block_maximum_size < 4)
+ data->block_size = bkmap[0];
+ else
+ data->block_size = bkmap[data->block_maximum_size - 4];
+
+ required_size = 4 + 15 + 4 + data->block_size + 4 + 4;
+ if (data->out_buffer_size < required_size) {
+ size_t bs = required_size, bpb;
+ free(data->out_buffer);
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of
+ * the of bytes per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0) {
+ bs += bpb;
+ bs -= bs % bpb;
+ }
+ }
+ data->out_block_size = bs;
+ bs += required_size;
+ data->out_buffer = malloc(bs);
+ data->out = data->out_buffer;
+ data->out_buffer_size = bs;
+ }
+
+ pre_block_size = (data->block_independence)? 0: 64 * 1024;
+ if (data->in_buffer_size < data->block_size + pre_block_size) {
+ free(data->in_buffer_allocated);
+ data->in_buffer_size = data->block_size;
+ data->in_buffer_allocated =
+ malloc(data->in_buffer_size + pre_block_size);
+ data->in_buffer = data->in_buffer_allocated + pre_block_size;
+ if (!data->block_independence && data->compression_level >= 3)
+ data->in_buffer = data->in_buffer_allocated;
+ data->in = data->in_buffer;
+ data->in_buffer_size = data->block_size;
+ }
+
+ if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ f->write = archive_filter_lz4_write;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the out stream.
+ *
+ * Returns ARCHIVE_OK if all data written, error otherwise.
+ */
+static int
+archive_filter_lz4_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret = ARCHIVE_OK;
+ const char *p;
+ size_t remaining;
+ ssize_t size;
+
+ /* If we haven't written a stream descriptor, we have to do it first. */
+ if (!data->header_written) {
+ ret = lz4_write_stream_descriptor(f);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ data->header_written = 1;
+ }
+
+ /* Update statistics */
+ data->total_in += length;
+
+ p = (const char *)buff;
+ remaining = length;
+ while (remaining) {
+ size_t l;
+ /* Compress input data to output buffer */
+ size = lz4_write_one_block(f, p, remaining);
+ if (size < ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ l = data->out - data->out_buffer;
+ if (l >= data->out_block_size) {
+ ret = __archive_write_filter(f->next_filter,
+ data->out_buffer, data->out_block_size);
+ l -= data->out_block_size;
+ memcpy(data->out_buffer,
+ data->out_buffer + data->out_block_size, l);
+ data->out = data->out_buffer + l;
+ if (ret < ARCHIVE_WARN)
+ break;
+ }
+ p += size;
+ remaining -= size;
+ }
+
+ return (ret);
+}
+
+/*
+ * Finish the compression.
+ */
+static int
+archive_filter_lz4_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Finish compression cycle. */
+ ret = (int)lz4_write_one_block(f, NULL, 0);
+ if (ret >= 0) {
+ /*
+ * Write the last block and the end of the stream data.
+ */
+
+ /* Write End Of Stream. */
+ memset(data->out, 0, 4); data->out += 4;
+ /* Write Stream checksum if needed. */
+ if (data->stream_checksum) {
+ unsigned int checksum;
+ checksum = __archive_xxhash.XXH32_digest(
+ data->xxh32_state);
+ data->xxh32_state = NULL;
+ archive_le32enc(data->out, checksum);
+ data->out += 4;
+ }
+ ret = __archive_write_filter(f->next_filter,
+ data->out_buffer, data->out - data->out_buffer);
+ }
+ return ret;
+}
+
+static int
+archive_filter_lz4_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->lz4_stream != NULL) {
+#ifdef HAVE_LZ4HC_H
+ if (data->compression_level >= 3)
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ LZ4_freeStreamHC(data->lz4_stream);
+#else
+ LZ4_freeHC(data->lz4_stream);
+#endif
+ else
+#endif
+#if LZ4_VERSION_MINOR >= 3
+ LZ4_freeStream(data->lz4_stream);
+#else
+ LZ4_free(data->lz4_stream);
+#endif
+ }
+ free(data->out_buffer);
+ free(data->in_buffer_allocated);
+ free(data->xxh32_state);
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+lz4_write_stream_descriptor(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ uint8_t *sd;
+
+ sd = (uint8_t *)data->out;
+ /* Write Magic Number. */
+ archive_le32enc(&sd[0], LZ4_MAGICNUMBER);
+ /* FLG */
+ sd[4] = (data->version_number << 6)
+ | (data->block_independence << 5)
+ | (data->block_checksum << 4)
+ | (data->stream_size << 3)
+ | (data->stream_checksum << 2)
+ | (data->preset_dictionary << 0);
+ /* BD */
+ sd[5] = (data->block_maximum_size << 4);
+ sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;
+ data->out += 7;
+ if (data->stream_checksum)
+ data->xxh32_state = __archive_xxhash.XXH32_init(0);
+ else
+ data->xxh32_state = NULL;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+lz4_write_one_block(struct archive_write_filter *f, const char *p,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ ssize_t r;
+
+ if (p == NULL) {
+ /* Compress remaining uncompressed data. */
+ if (data->in_buffer == data->in)
+ return 0;
+ else {
+ size_t l = data->in - data->in_buffer;
+ r = drive_compressor(f, data->in_buffer, l);
+ if (r == ARCHIVE_OK)
+ r = (ssize_t)l;
+ }
+ } else if ((data->block_independence || data->compression_level < 3) &&
+ data->in_buffer == data->in && length >= data->block_size) {
+ r = drive_compressor(f, p, data->block_size);
+ if (r == ARCHIVE_OK)
+ r = (ssize_t)data->block_size;
+ } else {
+ size_t remaining_size = data->in_buffer_size -
+ (data->in - data->in_buffer);
+ size_t l = (remaining_size > length)? length: remaining_size;
+ memcpy(data->in, p, l);
+ data->in += l;
+ if (l == remaining_size) {
+ r = drive_compressor(f, data->in_buffer,
+ data->block_size);
+ if (r == ARCHIVE_OK)
+ r = (ssize_t)l;
+ data->in = data->in_buffer;
+ } else
+ r = (ssize_t)l;
+ }
+
+ return (r);
+}
+
+
+/*
+ * Utility function to push input data through compressor, writing
+ * full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f, const char *p, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->stream_checksum)
+ __archive_xxhash.XXH32_update(data->xxh32_state,
+ p, (int)length);
+ if (data->block_independence)
+ return drive_compressor_independence(f, p, length);
+ else
+ return drive_compressor_dependence(f, p, length);
+}
+
+static int
+drive_compressor_independence(struct archive_write_filter *f, const char *p,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ unsigned int outsize;
+
+#ifdef HAVE_LZ4HC_H
+ if (data->compression_level >= 3)
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ outsize = LZ4_compress_HC(p, data->out + 4,
+ (int)length, (int)data->block_size,
+ data->compression_level);
+#else
+ outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,
+ (int)length, (int)data->block_size,
+ data->compression_level);
+#endif
+ else
+#endif
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ outsize = LZ4_compress_default(p, data->out + 4,
+ (int)length, (int)data->block_size);
+#else
+ outsize = LZ4_compress_limitedOutput(p, data->out + 4,
+ (int)length, (int)data->block_size);
+#endif
+
+ if (outsize) {
+ /* The buffer is compressed. */
+ archive_le32enc(data->out, outsize);
+ data->out += 4;
+ } else {
+ /* The buffer is not compressed. The compressed size was
+ * bigger than its uncompressed size. */
+ archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
+ data->out += 4;
+ memcpy(data->out, p, length);
+ outsize = (uint32_t)length;
+ }
+ data->out += outsize;
+ if (data->block_checksum) {
+ unsigned int checksum =
+ __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
+ archive_le32enc(data->out, checksum);
+ data->out += 4;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+drive_compressor_dependence(struct archive_write_filter *f, const char *p,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int outsize;
+
+#define DICT_SIZE (64 * 1024)
+#ifdef HAVE_LZ4HC_H
+ if (data->compression_level >= 3) {
+ if (data->lz4_stream == NULL) {
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ data->lz4_stream = LZ4_createStreamHC();
+ LZ4_resetStreamHC(data->lz4_stream, data->compression_level);
+#else
+ data->lz4_stream =
+ LZ4_createHC(data->in_buffer_allocated);
+#endif
+ if (data->lz4_stream == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression"
+ " buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ else
+ LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ outsize = LZ4_compress_HC_continue(
+ data->lz4_stream, p, data->out + 4, (int)length,
+ (int)data->block_size);
+#else
+ outsize = LZ4_compressHC2_limitedOutput_continue(
+ data->lz4_stream, p, data->out + 4, (int)length,
+ (int)data->block_size, data->compression_level);
+#endif
+ } else
+#endif
+ {
+ if (data->lz4_stream == NULL) {
+ data->lz4_stream = LZ4_createStream();
+ if (data->lz4_stream == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression"
+ " buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ else
+ LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ outsize = LZ4_compress_fast_continue(
+ data->lz4_stream, p, data->out + 4, (int)length,
+ (int)data->block_size, 1);
+#else
+ outsize = LZ4_compress_limitedOutput_continue(
+ data->lz4_stream, p, data->out + 4, (int)length,
+ (int)data->block_size);
+#endif
+ }
+
+ if (outsize) {
+ /* The buffer is compressed. */
+ archive_le32enc(data->out, outsize);
+ data->out += 4;
+ } else {
+ /* The buffer is not compressed. The compressed size was
+ * bigger than its uncompressed size. */
+ archive_le32enc(data->out, (uint32_t)(length | 0x80000000));
+ data->out += 4;
+ memcpy(data->out, p, length);
+ outsize = (uint32_t)length;
+ }
+ data->out += outsize;
+ if (data->block_checksum) {
+ unsigned int checksum =
+ __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
+ archive_le32enc(data->out, checksum);
+ data->out += 4;
+ }
+
+ if (length == data->block_size) {
+#ifdef HAVE_LZ4HC_H
+ if (data->compression_level >= 3) {
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+ LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+#else
+ LZ4_slideInputBufferHC(data->lz4_stream);
+#endif
+ data->in_buffer = data->in_buffer_allocated + DICT_SIZE;
+ }
+ else
+#endif
+ LZ4_saveDict(data->lz4_stream,
+ data->in_buffer_allocated, DICT_SIZE);
+#undef DICT_SIZE
+ }
+ return (ARCHIVE_OK);
+}
+
+#else /* HAVE_LIBLZ4 */
+
+static int
+archive_filter_lz4_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "lz4 -z -q -q");
+
+ /* Specify a compression level. */
+ if (data->compression_level > 0) {
+ archive_strcat(&as, " -");
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+ /* Specify a block size. */
+ archive_strcat(&as, " -B");
+ archive_strappend_char(&as, '0' + data->block_maximum_size);
+
+ if (data->block_checksum)
+ archive_strcat(&as, " -BX");
+ if (data->stream_checksum == 0)
+ archive_strcat(&as, " --no-frame-crc");
+ if (data->block_independence == 0)
+ archive_strcat(&as, " -BD");
+
+ f->write = archive_filter_lz4_write;
+
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_filter_lz4_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_filter_lz4_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ __archive_write_program_free(data->pdata);
+ free(data);
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_lzop.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lzop.c
new file mode 100644
index 0000000000..c4cb9ed733
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_lzop.c
@@ -0,0 +1,478 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+//#undef HAVE_LZO_LZOCONF_H
+//#undef HAVE_LZO_LZO1X_H
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_LZO_LZOCONF_H
+#error #include <lzo/lzoconf.h>
+#endif
+#ifdef HAVE_LZO_LZO1X_H
+#error #include <lzo/lzo1x.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_endian.h"
+#include "archive_write_private.h"
+
+enum lzo_method {
+ METHOD_LZO1X_1 = 1,
+ METHOD_LZO1X_1_15 = 2,
+ METHOD_LZO1X_999 = 3
+};
+struct write_lzop {
+ int compression_level;
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+ unsigned char *uncompressed;
+ size_t uncompressed_buffer_size;
+ size_t uncompressed_avail_bytes;
+ unsigned char *compressed;
+ size_t compressed_buffer_size;
+ enum lzo_method method;
+ unsigned char level;
+ lzo_voidp work_buffer;
+ lzo_uint32 work_buffer_size;
+ char header_written;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_write_lzop_open(struct archive_write_filter *);
+static int archive_write_lzop_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_write_lzop_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_write_lzop_close(struct archive_write_filter *);
+static int archive_write_lzop_free(struct archive_write_filter *);
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+/* Maximum block size. */
+#define BLOCK_SIZE (256 * 1024)
+/* Block information is composed of uncompressed size(4 bytes),
+ * compressed size(4 bytes) and the checksum of uncompressed data(4 bytes)
+ * in this lzop writer. */
+#define BLOCK_INfO_SIZE 12
+
+#define HEADER_VERSION 9
+#define HEADER_LIBVERSION 11
+#define HEADER_METHOD 15
+#define HEADER_LEVEL 16
+#define HEADER_MTIME_LOW 25
+#define HEADER_MTIME_HIGH 29
+#define HEADER_H_CHECKSUM 34
+
+/*
+ * Header template.
+ */
+static const unsigned char header[] = {
+ /* LZOP Magic code 9 bytes */
+ 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a,
+ /* LZOP utility version(fake data) 2 bytes */
+ 0x10, 0x30,
+ /* LZO library version 2 bytes */
+ 0x09, 0x40,
+ /* Minimum required LZO library version 2 bytes */
+ 0x09, 0x40,
+ /* Method */
+ 1,
+ /* Level */
+ 5,
+ /* Flags 4 bytes
+ * -OS Unix
+ * -Stdout
+ * -Stdin
+ * -Adler32 used for uncompressed data 4 bytes */
+ 0x03, 0x00, 0x00, 0x0d,
+ /* Mode (AE_IFREG | 0644) 4 bytes */
+ 0x00, 0x00, 0x81, 0xa4,
+ /* Mtime low 4 bytes */
+ 0x00, 0x00, 0x00, 0x00,
+ /* Mtime high 4 bytes */
+ 0x00, 0x00, 0x00, 0x00,
+ /* Filename length */
+ 0x00,
+ /* Header checksum 4 bytes */
+ 0x00, 0x00, 0x00, 0x00,
+};
+#endif
+
+int
+archive_write_add_filter_lzop(struct archive *_a)
+{
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct write_lzop *data;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ f->name = "lzop";
+ f->code = ARCHIVE_FILTER_LZOP;
+ f->data = data;
+ f->open = archive_write_lzop_open;
+ f->options = archive_write_lzop_options;
+ f->write = archive_write_lzop_write;
+ f->close = archive_write_lzop_close;
+ f->free = archive_write_lzop_free;
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+ if (lzo_init() != LZO_E_OK) {
+ free(data);
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "lzo_init(type check) failed");
+ return (ARCHIVE_FATAL);
+ }
+ if (lzo_version() < 0x940) {
+ free(data);
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "liblzo library is too old(%s < 0.940)",
+ lzo_version_string());
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 5;
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("lzop");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(_a, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ /* Note: We return "warn" to inform of using an external lzop
+ * program. */
+ archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+ "Using external lzop program for lzop compression");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_write_lzop_free(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+ free(data->uncompressed);
+ free(data->compressed);
+ free(data->work_buffer);
+#else
+ __archive_write_program_free(data->pdata);
+#endif
+ free(data);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+static int
+archive_write_lzop_open(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+ switch (data->compression_level) {
+ case 1:
+ data->method = METHOD_LZO1X_1_15; data->level = 1; break;
+ default:
+ case 2: case 3: case 4: case 5: case 6:
+ data->method = METHOD_LZO1X_1; data->level = 5; break;
+ case 7:
+ data->method = METHOD_LZO1X_999; data->level = 7; break;
+ case 8:
+ data->method = METHOD_LZO1X_999; data->level = 8; break;
+ case 9:
+ data->method = METHOD_LZO1X_999; data->level = 9; break;
+ }
+ switch (data->method) {
+ case METHOD_LZO1X_1:
+ data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
+ case METHOD_LZO1X_1_15:
+ data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
+ case METHOD_LZO1X_999:
+ data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
+ }
+ if (data->work_buffer == NULL) {
+ data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size);
+ if (data->work_buffer == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (data->compressed == NULL) {
+ data->compressed_buffer_size = sizeof(header) +
+ BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3;
+ data->compressed = (unsigned char *)
+ malloc(data->compressed_buffer_size);
+ if (data->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (data->uncompressed == NULL) {
+ data->uncompressed_buffer_size = BLOCK_SIZE;
+ data->uncompressed = (unsigned char *)
+ malloc(data->uncompressed_buffer_size);
+ if (data->uncompressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ data->uncompressed_avail_bytes = BLOCK_SIZE;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+make_header(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ int64_t t;
+ uint32_t checksum;
+
+ memcpy(data->compressed, header, sizeof(header));
+ /* Overwrite library version. */
+ data->compressed[HEADER_LIBVERSION] = (unsigned char )
+ (lzo_version() >> 8) & 0xff;
+ data->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
+ lzo_version() & 0xff;
+ /* Overwrite method and level. */
+ data->compressed[HEADER_METHOD] = (unsigned char)data->method;
+ data->compressed[HEADER_LEVEL] = data->level;
+ /* Overwrite mtime with current time. */
+ t = (int64_t)time(NULL);
+ archive_be32enc(&data->compressed[HEADER_MTIME_LOW],
+ (uint32_t)(t & 0xffffffff));
+ archive_be32enc(&data->compressed[HEADER_MTIME_HIGH],
+ (uint32_t)((t >> 32) & 0xffffffff));
+ /* Overwrite header checksum with calculated value. */
+ checksum = lzo_adler32(1, data->compressed + HEADER_VERSION,
+ (lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION));
+ archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum);
+ return (sizeof(header));
+}
+
+static int
+drive_compressor(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ unsigned char *p;
+ const int block_info_bytes = 12;
+ int header_bytes, r;
+ lzo_uint usize, csize;
+ uint32_t checksum;
+
+ if (!data->header_written) {
+ header_bytes = make_header(f);
+ data->header_written = 1;
+ } else
+ header_bytes = 0;
+ p = data->compressed;
+
+ usize = (lzo_uint)
+ (data->uncompressed_buffer_size - data->uncompressed_avail_bytes);
+ csize = 0;
+ switch (data->method) {
+ default:
+ case METHOD_LZO1X_1:
+ r = lzo1x_1_compress(data->uncompressed, usize,
+ p + header_bytes + block_info_bytes, &csize,
+ data->work_buffer);
+ break;
+ case METHOD_LZO1X_1_15:
+ r = lzo1x_1_15_compress(data->uncompressed, usize,
+ p + header_bytes + block_info_bytes, &csize,
+ data->work_buffer);
+ break;
+ case METHOD_LZO1X_999:
+ r = lzo1x_999_compress_level(data->uncompressed, usize,
+ p + header_bytes + block_info_bytes, &csize,
+ data->work_buffer, NULL, 0, 0, data->level);
+ break;
+ }
+ if (r != LZO_E_OK) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Lzop compression failed: returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Store uncompressed size. */
+ archive_be32enc(p + header_bytes, (uint32_t)usize);
+ /* Store the checksum of the uncompressed data. */
+ checksum = lzo_adler32(1, data->uncompressed, usize);
+ archive_be32enc(p + header_bytes + 8, checksum);
+
+ if (csize < usize) {
+ /* Store compressed size. */
+ archive_be32enc(p + header_bytes + 4, (uint32_t)csize);
+ r = __archive_write_filter(f->next_filter, data->compressed,
+ header_bytes + block_info_bytes + csize);
+ } else {
+ /*
+ * This case, we output uncompressed data instead.
+ */
+ /* Store uncompressed size as compressed size. */
+ archive_be32enc(p + header_bytes + 4, (uint32_t)usize);
+ r = __archive_write_filter(f->next_filter, data->compressed,
+ header_bytes + block_info_bytes);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ r = __archive_write_filter(f->next_filter, data->uncompressed,
+ usize);
+ }
+
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ const char *p = buff;
+ int r;
+
+ do {
+ if (data->uncompressed_avail_bytes > length) {
+ memcpy(data->uncompressed
+ + data->uncompressed_buffer_size
+ - data->uncompressed_avail_bytes,
+ p, length);
+ data->uncompressed_avail_bytes -= length;
+ return (ARCHIVE_OK);
+ }
+
+ memcpy(data->uncompressed + data->uncompressed_buffer_size
+ - data->uncompressed_avail_bytes,
+ p, data->uncompressed_avail_bytes);
+ length -= data->uncompressed_avail_bytes;
+ p += data->uncompressed_avail_bytes;
+ data->uncompressed_avail_bytes = 0;
+
+ r = drive_compressor(f);
+ if (r != ARCHIVE_OK) return (r);
+ data->uncompressed_avail_bytes = BLOCK_SIZE;
+ } while (length);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_close(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ const uint32_t endmark = 0;
+ int r;
+
+ if (data->uncompressed_avail_bytes < BLOCK_SIZE) {
+ /* Compress and output remaining data. */
+ r = drive_compressor(f);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ /* Write a zero uncompressed size as the end mark of the series of
+ * compressed block. */
+ return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark));
+}
+
+#else
+static int
+archive_write_lzop_open(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_strcpy(&as, "lzop");
+ /* Specify compression level. */
+ if (data->compression_level > 0) {
+ archive_strappend_char(&as, ' ');
+ archive_strappend_char(&as, '-');
+ archive_strappend_char(&as, '0' + data->compression_level);
+ }
+
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_write_lzop_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_lzop_close(struct archive_write_filter *f)
+{
+ struct write_lzop *data = (struct write_lzop *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_none.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_none.c
new file mode 100644
index 0000000000..3c06c642e7
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_none.c
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_none.c 201080 2009-12-28 02:03:54Z kientzle $");
+
+#include "archive.h"
+
+int
+archive_write_set_compression_none(struct archive *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_add_filter_none(struct archive *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_program.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_program.c
new file mode 100644
index 0000000000..c096e7227b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_program.c
@@ -0,0 +1,391 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $");
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+#include "filter_fork.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_program(struct archive *a, const char *cmd)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_program(a, cmd));
+}
+#endif
+
+struct archive_write_program_data {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE child;
+#else
+ pid_t child;
+#endif
+ int child_stdin, child_stdout;
+
+ char *child_buf;
+ size_t child_buf_len, child_buf_avail;
+ char *program_name;
+};
+
+struct private_data {
+ struct archive_write_program_data *pdata;
+ struct archive_string description;
+ char *cmd;
+};
+
+static int archive_compressor_program_open(struct archive_write_filter *);
+static int archive_compressor_program_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_program_close(struct archive_write_filter *);
+static int archive_compressor_program_free(struct archive_write_filter *);
+
+/*
+ * Add a filter to this write handle that passes all data through an
+ * external program.
+ */
+int
+archive_write_add_filter_program(struct archive *_a, const char *cmd)
+{
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+ static const char prefix[] = "Program: ";
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
+
+ f->data = calloc(1, sizeof(*data));
+ if (f->data == NULL)
+ goto memerr;
+ data = (struct private_data *)f->data;
+
+ data->cmd = strdup(cmd);
+ if (data->cmd == NULL)
+ goto memerr;
+
+ data->pdata = __archive_write_program_allocate(cmd);
+ if (data->pdata == NULL)
+ goto memerr;
+
+ /* Make up a description string. */
+ if (archive_string_ensure(&data->description,
+ strlen(prefix) + strlen(cmd) + 1) == NULL)
+ goto memerr;
+ archive_strcpy(&data->description, prefix);
+ archive_strcat(&data->description, cmd);
+
+ f->name = data->description.s;
+ f->code = ARCHIVE_FILTER_PROGRAM;
+ f->open = archive_compressor_program_open;
+ f->write = archive_compressor_program_write;
+ f->close = archive_compressor_program_close;
+ f->free = archive_compressor_program_free;
+ return (ARCHIVE_OK);
+memerr:
+ archive_compressor_program_free(f);
+ archive_set_error(_a, ENOMEM,
+ "Can't allocate memory for filter program");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+archive_compressor_program_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_open(f, data->pdata, data->cmd);
+}
+
+static int
+archive_compressor_program_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_program_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_compressor_program_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data) {
+ free(data->cmd);
+ archive_string_free(&data->description);
+ __archive_write_program_free(data->pdata);
+ free(data);
+ f->data = NULL;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Allocate resources for executing an external program.
+ */
+struct archive_write_program_data *
+__archive_write_program_allocate(const char *program)
+{
+ struct archive_write_program_data *data;
+
+ data = calloc(1, sizeof(struct archive_write_program_data));
+ if (data == NULL)
+ return (data);
+ data->child_stdin = -1;
+ data->child_stdout = -1;
+ data->program_name = strdup(program);
+ return (data);
+}
+
+/*
+ * Release the resources.
+ */
+int
+__archive_write_program_free(struct archive_write_program_data *data)
+{
+
+ if (data) {
+ free(data->program_name);
+ free(data->child_buf);
+ free(data);
+ }
+ return (ARCHIVE_OK);
+}
+
+int
+__archive_write_program_open(struct archive_write_filter *f,
+ struct archive_write_program_data *data, const char *cmd)
+{
+ int ret;
+
+ if (data->child_buf == NULL) {
+ data->child_buf_len = 65536;
+ data->child_buf_avail = 0;
+ data->child_buf = malloc(data->child_buf_len);
+
+ if (data->child_buf == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ ret = __archive_create_child(cmd, &data->child_stdin,
+ &data->child_stdout, &data->child);
+ if (ret != ARCHIVE_OK) {
+ archive_set_error(f->archive, EINVAL,
+ "Can't launch external program: %s", cmd);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+child_write(struct archive_write_filter *f,
+ struct archive_write_program_data *data, const char *buf, size_t buf_len)
+{
+ ssize_t ret;
+
+ if (data->child_stdin == -1)
+ return (-1);
+
+ if (buf_len == 0)
+ return (-1);
+
+ for (;;) {
+ do {
+ ret = write(data->child_stdin, buf, buf_len);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret > 0)
+ return (ret);
+ if (ret == 0) {
+ close(data->child_stdin);
+ data->child_stdin = -1;
+ fcntl(data->child_stdout, F_SETFL, 0);
+ return (0);
+ }
+ if (ret == -1 && errno != EAGAIN)
+ return (-1);
+
+ if (data->child_stdout == -1) {
+ fcntl(data->child_stdin, F_SETFL, 0);
+ __archive_check_child(data->child_stdin,
+ data->child_stdout);
+ continue;
+ }
+
+ do {
+ ret = read(data->child_stdout,
+ data->child_buf + data->child_buf_avail,
+ data->child_buf_len - data->child_buf_avail);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == 0 || (ret == -1 && errno == EPIPE)) {
+ close(data->child_stdout);
+ data->child_stdout = -1;
+ fcntl(data->child_stdin, F_SETFL, 0);
+ continue;
+ }
+ if (ret == -1 && errno == EAGAIN) {
+ __archive_check_child(data->child_stdin,
+ data->child_stdout);
+ continue;
+ }
+ if (ret == -1)
+ return (-1);
+
+ data->child_buf_avail += ret;
+
+ ret = __archive_write_filter(f->next_filter,
+ data->child_buf, data->child_buf_avail);
+ if (ret != ARCHIVE_OK)
+ return (-1);
+ data->child_buf_avail = 0;
+ }
+}
+
+/*
+ * Write data to the filter stream.
+ */
+int
+__archive_write_program_write(struct archive_write_filter *f,
+ struct archive_write_program_data *data, const void *buff, size_t length)
+{
+ ssize_t ret;
+ const char *buf;
+
+ if (data->child == 0)
+ return (ARCHIVE_OK);
+
+ buf = buff;
+ while (length > 0) {
+ ret = child_write(f, data, buf, length);
+ if (ret == -1 || ret == 0) {
+ archive_set_error(f->archive, EIO,
+ "Can't write to program: %s", data->program_name);
+ return (ARCHIVE_FATAL);
+ }
+ length -= ret;
+ buf += ret;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Finish the filtering...
+ */
+int
+__archive_write_program_close(struct archive_write_filter *f,
+ struct archive_write_program_data *data)
+{
+ int ret, status;
+ ssize_t bytes_read;
+
+ if (data->child == 0)
+ return ARCHIVE_OK;
+
+ ret = 0;
+ close(data->child_stdin);
+ data->child_stdin = -1;
+ fcntl(data->child_stdout, F_SETFL, 0);
+
+ for (;;) {
+ do {
+ bytes_read = read(data->child_stdout,
+ data->child_buf + data->child_buf_avail,
+ data->child_buf_len - data->child_buf_avail);
+ } while (bytes_read == -1 && errno == EINTR);
+
+ if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
+ break;
+
+ if (bytes_read == -1) {
+ archive_set_error(f->archive, errno,
+ "Error reading from program: %s", data->program_name);
+ ret = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ data->child_buf_avail += bytes_read;
+
+ ret = __archive_write_filter(f->next_filter,
+ data->child_buf, data->child_buf_avail);
+ if (ret != ARCHIVE_OK) {
+ ret = ARCHIVE_FATAL;
+ goto cleanup;
+ }
+ data->child_buf_avail = 0;
+ }
+
+cleanup:
+ /* Shut down the child. */
+ if (data->child_stdin != -1)
+ close(data->child_stdin);
+ if (data->child_stdout != -1)
+ close(data->child_stdout);
+ while (waitpid(data->child, &status, 0) == -1 && errno == EINTR)
+ continue;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ CloseHandle(data->child);
+#endif
+ data->child = 0;
+
+ if (status != 0) {
+ archive_set_error(f->archive, EIO,
+ "Error closing program: %s", data->program_name);
+ ret = ARCHIVE_FATAL;
+ }
+ return ret;
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_uuencode.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_uuencode.c
new file mode 100644
index 0000000000..1ad4589219
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_uuencode.c
@@ -0,0 +1,295 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#define LBYTES 45
+
+struct private_uuencode {
+ int mode;
+ struct archive_string name;
+ struct archive_string encoded_buff;
+ size_t bs;
+ size_t hold_len;
+ unsigned char hold[LBYTES];
+};
+
+static int archive_filter_uuencode_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_filter_uuencode_open(struct archive_write_filter *);
+static int archive_filter_uuencode_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_filter_uuencode_close(struct archive_write_filter *);
+static int archive_filter_uuencode_free(struct archive_write_filter *);
+static void uu_encode(struct archive_string *, const unsigned char *, size_t);
+static int64_t atol8(const char *, size_t);
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_uuencode(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_uuencode *state;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
+
+ state = (struct private_uuencode *)calloc(1, sizeof(*state));
+ if (state == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for uuencode filter");
+ return (ARCHIVE_FATAL);
+ }
+ archive_strcpy(&state->name, "-");
+ state->mode = 0644;
+
+ f->data = state;
+ f->name = "uuencode";
+ f->code = ARCHIVE_FILTER_UU;
+ f->open = archive_filter_uuencode_open;
+ f->options = archive_filter_uuencode_options;
+ f->write = archive_filter_uuencode_write;
+ f->close = archive_filter_uuencode_close;
+ f->free = archive_filter_uuencode_free;
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+ if (strcmp(key, "mode") == 0) {
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "mode option requires octal digits");
+ return (ARCHIVE_FAILED);
+ }
+ state->mode = (int)atol8(value, strlen(value)) & 0777;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "name") == 0) {
+ if (value == NULL) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "name option requires a string");
+ return (ARCHIVE_FAILED);
+ }
+ archive_strcpy(&state->name, value);
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_uuencode_open(struct archive_write_filter *f)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+ size_t bs = 65536, bpb;
+
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+
+ state->bs = bs;
+ if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for uuencode buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
+ state->mode, state->name.s);
+
+ f->data = state;
+ return (0);
+}
+
+static void
+uu_encode(struct archive_string *as, const unsigned char *p, size_t len)
+{
+ int c;
+
+ c = (int)len;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ for (; len >= 3; p += 3, len -= 3) {
+ c = p[0] >> 2;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = p[2] & 0x3f;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ }
+ if (len > 0) {
+ c = p[0] >> 2;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = (p[0] & 0x03) << 4;
+ if (len == 1) {
+ archive_strappend_char(as, c?c + 0x20:'`');
+ archive_strappend_char(as, '`');
+ archive_strappend_char(as, '`');
+ } else {
+ c |= (p[1] & 0xf0) >> 4;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ c = (p[1] & 0x0f) << 2;
+ archive_strappend_char(as, c?c + 0x20:'`');
+ archive_strappend_char(as, '`');
+ }
+ }
+ archive_strappend_char(as, '\n');
+}
+
+/*
+ * Write data to the encoded stream.
+ */
+static int
+archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+ const unsigned char *p = buff;
+ int ret = ARCHIVE_OK;
+
+ if (length == 0)
+ return (ret);
+
+ if (state->hold_len) {
+ while (state->hold_len < LBYTES && length > 0) {
+ state->hold[state->hold_len++] = *p++;
+ length--;
+ }
+ if (state->hold_len < LBYTES)
+ return (ret);
+ uu_encode(&state->encoded_buff, state->hold, LBYTES);
+ state->hold_len = 0;
+ }
+
+ for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
+ uu_encode(&state->encoded_buff, p, LBYTES);
+
+ /* Save remaining bytes. */
+ if (length > 0) {
+ memcpy(state->hold, p, length);
+ state->hold_len = length;
+ }
+ while (archive_strlen(&state->encoded_buff) >= state->bs) {
+ ret = __archive_write_filter(f->next_filter,
+ state->encoded_buff.s, state->bs);
+ memmove(state->encoded_buff.s,
+ state->encoded_buff.s + state->bs,
+ state->encoded_buff.length - state->bs);
+ state->encoded_buff.length -= state->bs;
+ }
+
+ return (ret);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_filter_uuencode_close(struct archive_write_filter *f)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+ /* Flush remaining bytes. */
+ if (state->hold_len != 0)
+ uu_encode(&state->encoded_buff, state->hold, state->hold_len);
+ archive_string_sprintf(&state->encoded_buff, "`\nend\n");
+ /* Write the last block */
+ archive_write_set_bytes_in_last_block(f->archive, 1);
+ return __archive_write_filter(f->next_filter,
+ state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+}
+
+static int
+archive_filter_uuencode_free(struct archive_write_filter *f)
+{
+ struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+ archive_string_free(&state->name);
+ archive_string_free(&state->encoded_buff);
+ free(state);
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+ int64_t l;
+ int digit;
+
+ l = 0;
+ while (char_cnt-- > 0) {
+ if (*p >= '0' && *p <= '7')
+ digit = *p - '0';
+ else
+ break;
+ p++;
+ l <<= 3;
+ l |= digit;
+ }
+ return (l);
+}
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_xz.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_xz.c
new file mode 100644
index 0000000000..7ecf701544
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_xz.c
@@ -0,0 +1,545 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_xz.c 201108 2009-12-28 03:28:21Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_lzip(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_lzip(a));
+}
+
+int
+archive_write_set_compression_lzma(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_lzma(a));
+}
+
+int
+archive_write_set_compression_xz(struct archive *a)
+{
+ __archive_write_filters_free(a);
+ return (archive_write_add_filter_xz(a));
+}
+
+#endif
+
+#ifndef HAVE_LZMA_H
+int
+archive_write_add_filter_xz(struct archive *a)
+{
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "xz compression not supported on this platform");
+ return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_add_filter_lzma(struct archive *a)
+{
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma compression not supported on this platform");
+ return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_add_filter_lzip(struct archive *a)
+{
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma compression not supported on this platform");
+ return (ARCHIVE_FATAL);
+}
+#else
+/* Don't compile this if we don't have liblzma. */
+
+struct private_data {
+ int compression_level;
+ uint32_t threads;
+ lzma_stream stream;
+ lzma_filter lzmafilters[2];
+ lzma_options_lzma lzma_opt;
+ int64_t total_in;
+ unsigned char *compressed;
+ size_t compressed_buffer_size;
+ int64_t total_out;
+ /* the CRC32 value of uncompressed data for lzip */
+ uint32_t crc32;
+};
+
+static int archive_compressor_xz_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_xz_open(struct archive_write_filter *);
+static int archive_compressor_xz_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_xz_close(struct archive_write_filter *);
+static int archive_compressor_xz_free(struct archive_write_filter *);
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int finishing);
+
+struct option_value {
+ uint32_t dict_size;
+ uint32_t nice_len;
+ lzma_match_finder mf;
+};
+static const struct option_value option_values[] = {
+ { 1 << 16, 32, LZMA_MF_HC3},
+ { 1 << 20, 32, LZMA_MF_HC3},
+ { 3 << 19, 32, LZMA_MF_HC4},
+ { 1 << 21, 32, LZMA_MF_BT4},
+ { 3 << 20, 32, LZMA_MF_BT4},
+ { 1 << 22, 32, LZMA_MF_BT4},
+ { 1 << 23, 64, LZMA_MF_BT4},
+ { 1 << 24, 64, LZMA_MF_BT4},
+ { 3 << 23, 64, LZMA_MF_BT4},
+ { 1 << 25, 64, LZMA_MF_BT4}
+};
+
+static int
+common_setup(struct archive_write_filter *f)
+{
+ struct private_data *data;
+ struct archive_write *a = (struct archive_write *)f->archive;
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ f->data = data;
+ data->compression_level = LZMA_PRESET_DEFAULT;
+ data->threads = 1;
+ f->open = &archive_compressor_xz_open;
+ f->close = archive_compressor_xz_close;
+ f->free = archive_compressor_xz_free;
+ f->options = &archive_compressor_xz_options;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Add an xz compression filter to this write handle.
+ */
+int
+archive_write_add_filter_xz(struct archive *_a)
+{
+ struct archive_write_filter *f;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_xz");
+ f = __archive_write_allocate_filter(_a);
+ r = common_setup(f);
+ if (r == ARCHIVE_OK) {
+ f->code = ARCHIVE_FILTER_XZ;
+ f->name = "xz";
+ }
+ return (r);
+}
+
+/* LZMA is handled identically, we just need a different compression
+ * code set. (The liblzma setup looks at the code to determine
+ * the one place that XZ and LZMA require different handling.) */
+int
+archive_write_add_filter_lzma(struct archive *_a)
+{
+ struct archive_write_filter *f;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lzma");
+ f = __archive_write_allocate_filter(_a);
+ r = common_setup(f);
+ if (r == ARCHIVE_OK) {
+ f->code = ARCHIVE_FILTER_LZMA;
+ f->name = "lzma";
+ }
+ return (r);
+}
+
+int
+archive_write_add_filter_lzip(struct archive *_a)
+{
+ struct archive_write_filter *f;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_lzip");
+ f = __archive_write_allocate_filter(_a);
+ r = common_setup(f);
+ if (r == ARCHIVE_OK) {
+ f->code = ARCHIVE_FILTER_LZIP;
+ f->name = "lzip";
+ }
+ return (r);
+}
+
+static int
+archive_compressor_xz_init_stream(struct archive_write_filter *f,
+ struct private_data *data)
+{
+ static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
+ int ret;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ lzma_mt mt_options;
+#endif
+
+ data->stream = lzma_stream_init_data;
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = data->compressed_buffer_size;
+ if (f->code == ARCHIVE_FILTER_XZ) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ if (data->threads != 1) {
+ memset(&mt_options, 0, sizeof(mt_options));
+ mt_options.threads = data->threads;
+ mt_options.timeout = 300;
+ mt_options.filters = data->lzmafilters;
+ mt_options.check = LZMA_CHECK_CRC64;
+ ret = lzma_stream_encoder_mt(&(data->stream),
+ &mt_options);
+ } else
+#endif
+ ret = lzma_stream_encoder(&(data->stream),
+ data->lzmafilters, LZMA_CHECK_CRC64);
+ } else if (f->code == ARCHIVE_FILTER_LZMA) {
+ ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt);
+ } else { /* ARCHIVE_FILTER_LZIP */
+ int dict_size = data->lzma_opt.dict_size;
+ int ds, log2dic, wedges;
+
+ /* Calculate a coded dictionary size */
+ if (dict_size < (1 << 12) || dict_size > (1 << 29)) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Unacceptable dictionary size for lzip: %d",
+ dict_size);
+ return (ARCHIVE_FATAL);
+ }
+ for (log2dic = 29; log2dic >= 12; log2dic--) {
+ if (dict_size & (1 << log2dic))
+ break;
+ }
+ if (dict_size > (1 << log2dic)) {
+ log2dic++;
+ wedges =
+ ((1 << log2dic) - dict_size) / (1 << (log2dic - 4));
+ } else
+ wedges = 0;
+ ds = ((wedges << 5) & 0xe0) | (log2dic & 0x1f);
+
+ data->crc32 = 0;
+ /* Make a header */
+ data->compressed[0] = 0x4C;
+ data->compressed[1] = 0x5A;
+ data->compressed[2] = 0x49;
+ data->compressed[3] = 0x50;
+ data->compressed[4] = 1;/* Version */
+ data->compressed[5] = (unsigned char)ds;
+ data->stream.next_out += 6;
+ data->stream.avail_out -= 6;
+
+ ret = lzma_raw_encoder(&(data->stream), data->lzmafilters);
+ }
+ if (ret == LZMA_OK)
+ return (ARCHIVE_OK);
+
+ switch (ret) {
+ case LZMA_MEM_ERROR:
+ archive_set_error(f->archive, ENOMEM,
+ "Internal error initializing compression library: "
+ "Cannot allocate memory");
+ break;
+ default:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "It's a bug in liblzma");
+ break;
+ }
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_xz_open(struct archive_write_filter *f)
+{
+ struct private_data *data = f->data;
+ int ret;
+
+ if (data->compressed == NULL) {
+ size_t bs = 65536, bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of the of bytes
+ * per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->compressed_buffer_size = bs;
+ data->compressed
+ = (unsigned char *)malloc(data->compressed_buffer_size);
+ if (data->compressed == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ f->write = archive_compressor_xz_write;
+
+ /* Initialize compression library. */
+ if (f->code == ARCHIVE_FILTER_LZIP) {
+ const struct option_value *val =
+ &option_values[data->compression_level];
+
+ data->lzma_opt.dict_size = val->dict_size;
+ data->lzma_opt.preset_dict = NULL;
+ data->lzma_opt.preset_dict_size = 0;
+ data->lzma_opt.lc = LZMA_LC_DEFAULT;
+ data->lzma_opt.lp = LZMA_LP_DEFAULT;
+ data->lzma_opt.pb = LZMA_PB_DEFAULT;
+ data->lzma_opt.mode =
+ data->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL;
+ data->lzma_opt.nice_len = val->nice_len;
+ data->lzma_opt.mf = val->mf;
+ data->lzma_opt.depth = 0;
+ data->lzmafilters[0].id = LZMA_FILTER_LZMA1;
+ data->lzmafilters[0].options = &data->lzma_opt;
+ data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+ } else {
+ if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ }
+ data->lzmafilters[0].id = LZMA_FILTER_LZMA2;
+ data->lzmafilters[0].options = &data->lzma_opt;
+ data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+ }
+ ret = archive_compressor_xz_init_stream(f, data);
+ if (ret == LZMA_OK) {
+ f->data = data;
+ return (0);
+ }
+ return (ARCHIVE_FATAL);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_xz_options(struct archive_write_filter *f,
+ const char *key, const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0')
+ return (ARCHIVE_WARN);
+ data->compression_level = value[0] - '0';
+ if (data->compression_level > 9)
+ data->compression_level = 9;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "threads") == 0) {
+ char *endptr;
+
+ if (value == NULL)
+ return (ARCHIVE_WARN);
+ errno = 0;
+ data->threads = (int)strtoul(value, &endptr, 10);
+ if (errno != 0 || *endptr != '\0') {
+ data->threads = 1;
+ return (ARCHIVE_WARN);
+ }
+ if (data->threads == 0) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ data->threads = lzma_cputhreads();
+#else
+ data->threads = 1;
+#endif
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_xz_write(struct archive_write_filter *f,
+ const void *buff, size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Update statistics */
+ data->total_in += length;
+ if (f->code == ARCHIVE_FILTER_LZIP)
+ data->crc32 = lzma_crc32(buff, length, data->crc32);
+
+ /* Compress input data to output buffer */
+ data->stream.next_in = buff;
+ data->stream.avail_in = length;
+ if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+ return (ret);
+
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_xz_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ ret = drive_compressor(f, data, 1);
+ if (ret == ARCHIVE_OK) {
+ data->total_out +=
+ data->compressed_buffer_size - data->stream.avail_out;
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size - data->stream.avail_out);
+ if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) {
+ archive_le32enc(data->compressed, data->crc32);
+ archive_le64enc(data->compressed+4, data->total_in);
+ archive_le64enc(data->compressed+12, data->total_out + 20);
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed, 20);
+ }
+ }
+ lzma_end(&(data->stream));
+ return ret;
+}
+
+static int
+archive_compressor_xz_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ free(data->compressed);
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int finishing)
+{
+ int ret;
+
+ for (;;) {
+ if (data->stream.avail_out == 0) {
+ data->total_out += data->compressed_buffer_size;
+ ret = __archive_write_filter(f->next_filter,
+ data->compressed,
+ data->compressed_buffer_size);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ data->stream.next_out = data->compressed;
+ data->stream.avail_out = data->compressed_buffer_size;
+ }
+
+ /* If there's nothing to do, we're done. */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+
+ ret = lzma_code(&(data->stream),
+ finishing ? LZMA_FINISH : LZMA_RUN );
+
+ switch (ret) {
+ case LZMA_OK:
+ /* In non-finishing case, check if compressor
+ * consumed everything */
+ if (!finishing && data->stream.avail_in == 0)
+ return (ARCHIVE_OK);
+ /* In finishing case, this return always means
+ * there's more work */
+ break;
+ case LZMA_STREAM_END:
+ /* This return can only occur in finishing case. */
+ if (finishing)
+ return (ARCHIVE_OK);
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "lzma compression data error");
+ return (ARCHIVE_FATAL);
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(f->archive, ENOMEM,
+ "lzma compression error: "
+ "%ju MiB would have been needed",
+ (uintmax_t)((lzma_memusage(&(data->stream))
+ + 1024 * 1024 -1)
+ / (1024 * 1024)));
+ return (ARCHIVE_FATAL);
+ default:
+ /* Any other return value indicates an error. */
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "lzma compression failed:"
+ " lzma_code() call returned status %d",
+ ret);
+ return (ARCHIVE_FATAL);
+ }
+ }
+}
+
+#endif /* HAVE_LZMA_H */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_add_filter_zstd.c b/contrib/libs/libarchive/libarchive/archive_write_add_filter_zstd.c
new file mode 100644
index 0000000000..c78e840d89
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_add_filter_zstd.c
@@ -0,0 +1,503 @@
+/*-
+ * Copyright (c) 2017 Sean Purcell
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+/* Don't compile this if we don't have zstd.h */
+
+struct private_data {
+ int compression_level;
+ int threads;
+ int long_distance;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ enum {
+ running,
+ finishing,
+ resetting,
+ } state;
+ int frame_per_file;
+ size_t min_frame_size;
+ size_t max_frame_size;
+ size_t cur_frame;
+ size_t cur_frame_in;
+ size_t cur_frame_out;
+ size_t total_in;
+ ZSTD_CStream *cstream;
+ ZSTD_outBuffer out;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+/* If we don't have the library use default range values (zstdcli.c v1.4.0) */
+#define CLEVEL_MIN -99
+#define CLEVEL_STD_MIN 0 /* prior to 1.3.4 and more recent without using --fast */
+#define CLEVEL_DEFAULT 3
+#define CLEVEL_STD_MAX 19 /* without using --ultra */
+#define CLEVEL_MAX 22
+
+#define LONG_STD 27
+
+#define MINVER_NEGCLEVEL 10304
+#define MINVER_MINCLEVEL 10306
+#define MINVER_LONG 10302
+
+static int archive_compressor_zstd_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_zstd_open(struct archive_write_filter *);
+static int archive_compressor_zstd_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_zstd_flush(struct archive_write_filter *);
+static int archive_compressor_zstd_close(struct archive_write_filter *);
+static int archive_compressor_zstd_free(struct archive_write_filter *);
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int, const void *, size_t);
+#endif
+
+
+/*
+ * Add a zstd compression filter to this write handle.
+ */
+int
+archive_write_add_filter_zstd(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ f->data = data;
+ f->open = &archive_compressor_zstd_open;
+ f->options = &archive_compressor_zstd_options;
+ f->flush = &archive_compressor_zstd_flush;
+ f->close = &archive_compressor_zstd_close;
+ f->free = &archive_compressor_zstd_free;
+ f->code = ARCHIVE_FILTER_ZSTD;
+ f->name = "zstd";
+ data->compression_level = CLEVEL_DEFAULT;
+ data->threads = 0;
+ data->long_distance = 0;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ data->frame_per_file = 0;
+ data->min_frame_size = 0;
+ data->max_frame_size = SIZE_MAX;
+ data->cur_frame_in = 0;
+ data->cur_frame_out = 0;
+ data->cstream = ZSTD_createCStream();
+ if (data->cstream == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM,
+ "Failed to allocate zstd compressor object");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("zstd");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external zstd program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_compressor_zstd_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ ZSTD_freeCStream(data->cstream);
+ free(data->out.dst);
+#else
+ __archive_write_program_free(data->pdata);
+#endif
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int string_to_number(const char *string, intmax_t *numberp)
+{
+ char *end;
+
+ if (string == NULL || *string == '\0')
+ return (ARCHIVE_WARN);
+ *numberp = strtoimax(string, &end, 10);
+ if (end == string || *end != '\0' || errno == EOVERFLOW) {
+ *numberp = 0;
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ intmax_t level;
+ if (string_to_number(value, &level) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ /* If we don't have the library, hard-code the max level */
+ int minimum = CLEVEL_MIN;
+ int maximum = CLEVEL_MAX;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ maximum = ZSTD_maxCLevel();
+#if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL
+ if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) {
+ minimum = ZSTD_minCLevel();
+ }
+ else
+#endif
+ if (ZSTD_versionNumber() < MINVER_NEGCLEVEL) {
+ minimum = CLEVEL_STD_MIN;
+ }
+#endif
+ if (level < minimum || level > maximum) {
+ return (ARCHIVE_WARN);
+ }
+ data->compression_level = (int)level;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "threads") == 0) {
+ intmax_t threads;
+ if (string_to_number(value, &threads) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ if (threads < 0) {
+ return (ARCHIVE_WARN);
+ }
+ data->threads = (int)threads;
+ return (ARCHIVE_OK);
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+ } else if (strcmp(key, "frame-per-file") == 0) {
+ data->frame_per_file = 1;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "min-frame-size") == 0) {
+ intmax_t min_frame_size;
+ if (string_to_number(value, &min_frame_size) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ if (min_frame_size < 0) {
+ return (ARCHIVE_WARN);
+ }
+ data->min_frame_size = min_frame_size;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "max-frame-size") == 0) {
+ intmax_t max_frame_size;
+ if (string_to_number(value, &max_frame_size) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+ if (max_frame_size < 1024) {
+ return (ARCHIVE_WARN);
+ }
+ data->max_frame_size = max_frame_size;
+ return (ARCHIVE_OK);
+#endif
+ }
+ else if (strcmp(key, "long") == 0) {
+ intmax_t long_distance;
+ if (string_to_number(value, &long_distance) != ARCHIVE_OK) {
+ return (ARCHIVE_WARN);
+ }
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR && ZSTD_VERSION_NUMBER >= MINVER_LONG
+ ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_c_windowLog);
+ if (ZSTD_isError(bounds.error)) {
+ int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31));
+ if (((int)long_distance) < 10 || (int)long_distance > max_distance)
+ return (ARCHIVE_WARN);
+ } else {
+ if ((int)long_distance < bounds.lowerBound || (int)long_distance > bounds.upperBound)
+ return (ARCHIVE_WARN);
+ }
+#else
+ int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31));
+ if (((int)long_distance) < 10 || (int)long_distance > max_distance)
+ return (ARCHIVE_WARN);
+#endif
+ data->long_distance = (int)long_distance;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_zstd_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->out.dst == NULL) {
+ size_t bs = ZSTD_CStreamOutSize(), bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of
+ * the of bytes per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->out.size = bs;
+ data->out.pos = 0;
+ data->out.dst
+ = (unsigned char *)malloc(data->out.size);
+ if (data->out.dst == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ f->write = archive_compressor_zstd_write;
+
+ if (ZSTD_isError(ZSTD_initCStream(data->cstream,
+ data->compression_level))) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing zstd compressor object");
+ return (ARCHIVE_FATAL);
+ }
+
+ ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads);
+
+#if ZSTD_VERSION_NUMBER >= MINVER_LONG
+ ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_windowLog, data->long_distance);
+#endif
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return (drive_compressor(f, data, 0, buff, length));
+}
+
+/*
+ * Flush the compressed stream.
+ */
+static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->frame_per_file && data->state == running &&
+ data->cur_frame_out > data->min_frame_size)
+ data->state = finishing;
+ return (drive_compressor(f, data, 1, NULL, 0));
+}
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (data->state == running)
+ data->state = finishing;
+ return (drive_compressor(f, data, 1, NULL, 0));
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int flush, const void *src, size_t length)
+{
+ ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 };
+ size_t ipos, opos, zstdret = 0;
+ int ret;
+
+ for (;;) {
+ ipos = in.pos;
+ opos = data->out.pos;
+ switch (data->state) {
+ case running:
+ if (in.pos == in.size)
+ return (ARCHIVE_OK);
+ zstdret = ZSTD_compressStream(data->cstream,
+ &data->out, &in);
+ if (ZSTD_isError(zstdret))
+ goto zstd_fatal;
+ break;
+ case finishing:
+ zstdret = ZSTD_endStream(data->cstream, &data->out);
+ if (ZSTD_isError(zstdret))
+ goto zstd_fatal;
+ if (zstdret == 0)
+ data->state = resetting;
+ break;
+ case resetting:
+ ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only);
+ data->cur_frame++;
+ data->cur_frame_in = 0;
+ data->cur_frame_out = 0;
+ data->state = running;
+ break;
+ }
+ data->total_in += in.pos - ipos;
+ data->cur_frame_in += in.pos - ipos;
+ data->cur_frame_out += data->out.pos - opos;
+ if (data->state == running &&
+ data->cur_frame_in >= data->max_frame_size) {
+ data->state = finishing;
+ }
+ if (data->out.pos == data->out.size ||
+ (flush && data->out.pos > 0)) {
+ ret = __archive_write_filter(f->next_filter,
+ data->out.dst, data->out.pos);
+ if (ret != ARCHIVE_OK)
+ goto fatal;
+ data->out.pos = 0;
+ }
+ }
+zstd_fatal:
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Zstd compression failed: %s",
+ ZSTD_getErrorName(zstdret));
+fatal:
+ return (ARCHIVE_FATAL);
+}
+
+#else /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */
+
+static int
+archive_compressor_zstd_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ /* --no-check matches library default */
+ archive_strcpy(&as, "zstd --no-check");
+
+ if (data->compression_level < CLEVEL_STD_MIN) {
+ archive_string_sprintf(&as, " --fast=%d", -data->compression_level);
+ } else {
+ archive_string_sprintf(&as, " -%d", data->compression_level);
+ }
+
+ if (data->compression_level > CLEVEL_STD_MAX) {
+ archive_strcat(&as, " --ultra");
+ }
+
+ if (data->threads != 0) {
+ archive_string_sprintf(&as, " --threads=%d", data->threads);
+ }
+
+ if (data->long_distance != 0) {
+ archive_string_sprintf(&as, " --long=%d", data->long_distance);
+ }
+
+ f->write = archive_compressor_zstd_write;
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+ (void)f; /* UNUSED */
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+#endif /* HAVE_ZSTD_H && HAVE_LIBZSTD_COMPRESSOR */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_disk_posix.c b/contrib/libs/libarchive/libarchive/archive_write_disk_posix.c
new file mode 100644
index 0000000000..33aa2f4d89
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_disk_posix.c
@@ -0,0 +1,4759 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+#if HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#elif HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_SYS_EA_H
+#error #include <sys/ea.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h> /* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#error #include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#error #include <ext2fs/ext2_fs.h> /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#ifdef F_GETTIMES /* Tru64 specific */
+#error #include <sys/fcntl1.h>
+#endif
+
+/*
+ * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared.
+ *
+ * It assumes that the input is an integer type of no more than 64 bits.
+ * If the number is less than zero, t must be a signed type, so it fits in
+ * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t
+ * without loss. But it could be a large unsigned value, so we have to clip it
+ * to INT64_MAX.*
+ */
+#define to_int64_time(t) \
+ ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t))
+
+#if __APPLE__
+#include <TargetConditionals.h>
+#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H
+#error #include <quarantine.h>
+#define HAVE_QUARANTINE 1
+#endif
+#endif
+
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+/* TODO: Support Mac OS 'quarantine' feature. This is really just a
+ * standard tag to mark files that have been downloaded as "tainted".
+ * On Mac OS, we should mark the extracted files as tainted if the
+ * archive being read was tainted. Windows has a similar feature; we
+ * should investigate ways to support this generically. */
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_string.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_disk_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+/* Ignore non-int O_NOFOLLOW constant. */
+/* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */
+#if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX)
+#undef O_NOFOLLOW
+#endif
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+#ifndef AT_FDCWD
+#define AT_FDCWD -100
+#endif
+
+struct fixup_entry {
+ struct fixup_entry *next;
+ struct archive_acl acl;
+ mode_t mode;
+ __LA_MODE_T filetype;
+ int64_t atime;
+ int64_t birthtime;
+ int64_t mtime;
+ int64_t ctime;
+ unsigned long atime_nanos;
+ unsigned long birthtime_nanos;
+ unsigned long mtime_nanos;
+ unsigned long ctime_nanos;
+ unsigned long fflags_set;
+ size_t mac_metadata_size;
+ void *mac_metadata;
+ int fixup; /* bitmask of what needs fixing */
+ char *name;
+};
+
+/*
+ * We use a bitmask to track which operations remain to be done for
+ * this file. In particular, this helps us avoid unnecessary
+ * operations when it's possible to take care of one step as a
+ * side-effect of another. For example, mkdir() can specify the mode
+ * for the newly-created object but symlink() cannot. This means we
+ * can skip chmod() if mkdir() succeeded, but we must explicitly
+ * chmod() if we're trying to create a directory that already exists
+ * (mkdir() failed) or if we're restoring a symlink. Similarly, we
+ * need to verify UID/GID before trying to restore SUID/SGID bits;
+ * that verification can occur explicitly through a stat() call or
+ * implicitly because of a successful chown() call.
+ */
+#define TODO_MODE_FORCE 0x40000000
+#define TODO_MODE_BASE 0x20000000
+#define TODO_SUID 0x10000000
+#define TODO_SUID_CHECK 0x08000000
+#define TODO_SGID 0x04000000
+#define TODO_SGID_CHECK 0x02000000
+#define TODO_APPLEDOUBLE 0x01000000
+#define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID)
+#define TODO_TIMES ARCHIVE_EXTRACT_TIME
+#define TODO_OWNER ARCHIVE_EXTRACT_OWNER
+#define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS
+#define TODO_ACLS ARCHIVE_EXTRACT_ACL
+#define TODO_XATTR ARCHIVE_EXTRACT_XATTR
+#define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA
+#define TODO_HFS_COMPRESSION ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED
+
+struct archive_write_disk {
+ struct archive archive;
+
+ mode_t user_umask;
+ struct fixup_entry *fixup_list;
+ struct fixup_entry *current_fixup;
+ int64_t user_uid;
+ int skip_file_set;
+ int64_t skip_file_dev;
+ int64_t skip_file_ino;
+ time_t start_time;
+
+ int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);
+ void (*cleanup_gid)(void *private);
+ void *lookup_gid_data;
+ int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);
+ void (*cleanup_uid)(void *private);
+ void *lookup_uid_data;
+
+ /*
+ * Full path of last file to satisfy symlink checks.
+ */
+ struct archive_string path_safe;
+
+ /*
+ * Cached stat data from disk for the current entry.
+ * If this is valid, pst points to st. Otherwise,
+ * pst is null.
+ */
+ struct stat st;
+ struct stat *pst;
+
+ /* Information about the object being restored right now. */
+ struct archive_entry *entry; /* Entry being extracted. */
+ char *name; /* Name of entry, possibly edited. */
+ struct archive_string _name_data; /* backing store for 'name' */
+ char *tmpname; /* Temporary name * */
+ struct archive_string _tmpname_data; /* backing store for 'tmpname' */
+ /* Tasks remaining for this object. */
+ int todo;
+ /* Tasks deferred until end-of-archive. */
+ int deferred;
+ /* Options requested by the client. */
+ int flags;
+ /* Handle for the file we're restoring. */
+ int fd;
+ /* Current offset for writing data to the file. */
+ int64_t offset;
+ /* Last offset actually written to disk. */
+ int64_t fd_offset;
+ /* Total bytes actually written to files. */
+ int64_t total_bytes_written;
+ /* Maximum size of file, -1 if unknown. */
+ int64_t filesize;
+ /* Dir we were in before this restore; only for deep paths. */
+ int restore_pwd;
+ /* Mode we should use for this entry; affected by _PERM and umask. */
+ mode_t mode;
+ /* UID/GID to use in restoring this entry. */
+ int64_t uid;
+ int64_t gid;
+ /*
+ * HFS+ Compression.
+ */
+ /* Xattr "com.apple.decmpfs". */
+ uint32_t decmpfs_attr_size;
+ unsigned char *decmpfs_header_p;
+ /* ResourceFork set options used for fsetxattr. */
+ int rsrc_xattr_options;
+ /* Xattr "com.apple.ResourceFork". */
+ unsigned char *resource_fork;
+ size_t resource_fork_allocated_size;
+ unsigned int decmpfs_block_count;
+ uint32_t *decmpfs_block_info;
+ /* Buffer for compressed data. */
+ unsigned char *compressed_buffer;
+ size_t compressed_buffer_size;
+ size_t compressed_buffer_remaining;
+ /* The offset of the ResourceFork where compressed data will
+ * be placed. */
+ uint32_t compressed_rsrc_position;
+ uint32_t compressed_rsrc_position_v;
+ /* Buffer for uncompressed data. */
+ char *uncompressed_buffer;
+ size_t block_remaining_bytes;
+ size_t file_remaining_bytes;
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+ int stream_valid;
+ int decmpfs_compression_level;
+#endif
+};
+
+/*
+ * Default mode for dirs created automatically (will be modified by umask).
+ * Note that POSIX specifies 0777 for implicitly-created dirs, "modified
+ * by the process' file creation mask."
+ */
+#define DEFAULT_DIR_MODE 0777
+/*
+ * Dir modes are restored in two steps: During the extraction, the permissions
+ * in the archive are modified to match the following limits. During
+ * the post-extract fixup pass, the permissions from the archive are
+ * applied.
+ */
+#define MINIMUM_DIR_MODE 0700
+#define MAXIMUM_DIR_MODE 0775
+
+/*
+ * Maximum uncompressed size of a decmpfs block.
+ */
+#define MAX_DECMPFS_BLOCK_SIZE (64 * 1024)
+/*
+ * HFS+ compression type.
+ */
+#define CMP_XATTR 3/* Compressed data in xattr. */
+#define CMP_RESOURCE_FORK 4/* Compressed data in resource fork. */
+/*
+ * HFS+ compression resource fork.
+ */
+#define RSRC_H_SIZE 260 /* Base size of Resource fork header. */
+#define RSRC_F_SIZE 50 /* Size of Resource fork footer. */
+/* Size to write compressed data to resource fork. */
+#define COMPRESSED_W_SIZE (64 * 1024)
+/* decmpfs definitions. */
+#define MAX_DECMPFS_XATTR_SIZE 3802
+#ifndef DECMPFS_XATTR_NAME
+#define DECMPFS_XATTR_NAME "com.apple.decmpfs"
+#endif
+#define DECMPFS_MAGIC 0x636d7066
+#define DECMPFS_COMPRESSION_MAGIC 0
+#define DECMPFS_COMPRESSION_TYPE 4
+#define DECMPFS_UNCOMPRESSED_SIZE 8
+#define DECMPFS_HEADER_SIZE 16
+
+#define HFS_BLOCKS(s) ((s) >> 12)
+
+
+static int la_opendirat(int, const char *);
+static int la_mktemp(struct archive_write_disk *);
+static int la_verify_filetype(mode_t, __LA_MODE_T);
+static void fsobj_error(int *, struct archive_string *, int, const char *,
+ const char *);
+static int check_symlinks_fsobj(char *, int *, struct archive_string *,
+ int, int);
+static int check_symlinks(struct archive_write_disk *);
+static int create_filesystem_object(struct archive_write_disk *);
+static struct fixup_entry *current_fixup(struct archive_write_disk *,
+ const char *pathname);
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+static void edit_deep_directories(struct archive_write_disk *ad);
+#endif
+static int cleanup_pathname_fsobj(char *, int *, struct archive_string *,
+ int);
+static int cleanup_pathname(struct archive_write_disk *);
+static int create_dir(struct archive_write_disk *, char *);
+static int create_parent_dir(struct archive_write_disk *, char *);
+static ssize_t hfs_write_data_block(struct archive_write_disk *,
+ const char *, size_t);
+static int fixup_appledouble(struct archive_write_disk *, const char *);
+static int older(struct stat *, struct archive_entry *);
+static int restore_entry(struct archive_write_disk *);
+static int set_mac_metadata(struct archive_write_disk *, const char *,
+ const void *, size_t);
+static int set_xattrs(struct archive_write_disk *);
+static int clear_nochange_fflags(struct archive_write_disk *);
+static int set_fflags(struct archive_write_disk *);
+static int set_fflags_platform(struct archive_write_disk *, int fd,
+ const char *name, mode_t mode,
+ unsigned long fflags_set, unsigned long fflags_clear);
+static int set_ownership(struct archive_write_disk *);
+static int set_mode(struct archive_write_disk *, int mode);
+static int set_time(int, int, const char *, time_t, long, time_t, long);
+static int set_times(struct archive_write_disk *, int, int, const char *,
+ time_t, long, time_t, long, time_t, long, time_t, long);
+static int set_times_from_entry(struct archive_write_disk *);
+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
+static ssize_t write_data_block(struct archive_write_disk *,
+ const char *, size_t);
+static void close_file_descriptor(struct archive_write_disk *);
+
+static int _archive_write_disk_close(struct archive *);
+static int _archive_write_disk_free(struct archive *);
+static int _archive_write_disk_header(struct archive *,
+ struct archive_entry *);
+static int64_t _archive_write_disk_filter_bytes(struct archive *, int);
+static int _archive_write_disk_finish_entry(struct archive *);
+static ssize_t _archive_write_disk_data(struct archive *, const void *,
+ size_t);
+static ssize_t _archive_write_disk_data_block(struct archive *, const void *,
+ size_t, int64_t);
+
+static int
+la_mktemp(struct archive_write_disk *a)
+{
+ int oerrno, fd;
+ mode_t mode;
+
+ archive_string_empty(&a->_tmpname_data);
+ archive_string_sprintf(&a->_tmpname_data, "%s.XXXXXX", a->name);
+ a->tmpname = a->_tmpname_data.s;
+
+ fd = __archive_mkstemp(a->tmpname);
+ if (fd == -1)
+ return -1;
+
+ mode = a->mode & 0777 & ~a->user_umask;
+ if (fchmod(fd, mode) == -1) {
+ oerrno = errno;
+ close(fd);
+ errno = oerrno;
+ return -1;
+ }
+ return fd;
+}
+
+static int
+la_opendirat(int fd, const char *path) {
+ const int flags = O_CLOEXEC
+#if defined(O_BINARY)
+ | O_BINARY
+#endif
+#if defined(O_DIRECTORY)
+ | O_DIRECTORY
+#endif
+#if defined(O_PATH)
+ | O_PATH
+#elif defined(O_SEARCH)
+ | O_SEARCH
+#elif defined(__FreeBSD__) && defined(O_EXEC)
+ | O_EXEC
+#else
+ | O_RDONLY
+#endif
+ ;
+
+#if !defined(HAVE_OPENAT)
+ if (fd != AT_FDCWD) {
+ errno = ENOTSUP;
+ return (-1);
+ } else
+ return (open(path, flags));
+#else
+ return (openat(fd, path, flags));
+#endif
+}
+
+static int
+la_verify_filetype(mode_t mode, __LA_MODE_T filetype) {
+ int ret = 0;
+
+ switch (filetype) {
+ case AE_IFREG:
+ ret = (S_ISREG(mode));
+ break;
+ case AE_IFDIR:
+ ret = (S_ISDIR(mode));
+ break;
+ case AE_IFLNK:
+ ret = (S_ISLNK(mode));
+ break;
+ case AE_IFSOCK:
+ ret = (S_ISSOCK(mode));
+ break;
+ case AE_IFCHR:
+ ret = (S_ISCHR(mode));
+ break;
+ case AE_IFBLK:
+ ret = (S_ISBLK(mode));
+ break;
+ case AE_IFIFO:
+ ret = (S_ISFIFO(mode));
+ break;
+ default:
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+lazy_stat(struct archive_write_disk *a)
+{
+ if (a->pst != NULL) {
+ /* Already have stat() data available. */
+ return (ARCHIVE_OK);
+ }
+#ifdef HAVE_FSTAT
+ if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) {
+ a->pst = &a->st;
+ return (ARCHIVE_OK);
+ }
+#endif
+ /*
+ * XXX At this point, symlinks should not be hit, otherwise
+ * XXX a race occurred. Do we want to check explicitly for that?
+ */
+#ifdef HAVE_LSTAT
+ if (lstat(a->name, &a->st) == 0)
+#else
+ if (la_stat(a->name, &a->st) == 0)
+#endif
+ {
+ a->pst = &a->st;
+ return (ARCHIVE_OK);
+ }
+ archive_set_error(&a->archive, errno, "Couldn't stat file");
+ return (ARCHIVE_WARN);
+}
+
+static const struct archive_vtable
+archive_write_disk_vtable = {
+ .archive_close = _archive_write_disk_close,
+ .archive_filter_bytes = _archive_write_disk_filter_bytes,
+ .archive_free = _archive_write_disk_free,
+ .archive_write_header = _archive_write_disk_header,
+ .archive_write_finish_entry = _archive_write_disk_finish_entry,
+ .archive_write_data = _archive_write_disk_data,
+ .archive_write_data_block = _archive_write_disk_data_block,
+};
+
+static int64_t
+_archive_write_disk_filter_bytes(struct archive *_a, int n)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ (void)n; /* UNUSED */
+ if (n == -1 || n == 0)
+ return (a->total_bytes_written);
+ return (-1);
+}
+
+
+int
+archive_write_disk_set_options(struct archive *_a, int flags)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+ a->flags = flags;
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks. According to the standards, we're
+ * supposed to check each extracted hardlink and squawk if it refers
+ * to a file that we didn't restore. I'm not entirely convinced this
+ * is a good idea, but more importantly: Is there any way to validate
+ * hardlinks without keeping a complete list of filenames from the
+ * entire archive?? Ugh.
+ *
+ */
+static int
+_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ struct fixup_entry *fe;
+ const char *linkname;
+ int ret, r;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_disk_header");
+ archive_clear_error(&a->archive);
+ if (a->archive.state & ARCHIVE_STATE_DATA) {
+ r = _archive_write_disk_finish_entry(&a->archive);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ }
+
+ /* Set up for this particular entry. */
+ a->pst = NULL;
+ a->current_fixup = NULL;
+ a->deferred = 0;
+ if (a->entry) {
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ }
+ a->entry = archive_entry_clone(entry);
+ a->fd = -1;
+ a->fd_offset = 0;
+ a->offset = 0;
+ a->restore_pwd = -1;
+ a->uid = a->user_uid;
+ a->mode = archive_entry_mode(a->entry);
+ if (archive_entry_size_is_set(a->entry))
+ a->filesize = archive_entry_size(a->entry);
+ else
+ a->filesize = -1;
+ archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));
+ a->name = a->_name_data.s;
+ archive_clear_error(&a->archive);
+
+ /*
+ * Clean up the requested path. This is necessary for correct
+ * dir restores; the dir restore logic otherwise gets messed
+ * up by nonsense like "dir/.".
+ */
+ ret = cleanup_pathname(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ /*
+ * Check if we have a hardlink that points to itself.
+ */
+ linkname = archive_entry_hardlink(a->entry);
+ if (linkname != NULL && strcmp(a->name, linkname) == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Skipping hardlink pointing to itself: %s",
+ a->name);
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * Query the umask so we get predictable mode settings.
+ * This gets done on every call to _write_header in case the
+ * user edits their umask during the extraction for some
+ * reason.
+ */
+ umask(a->user_umask = umask(0));
+
+ /* Figure out what we need to do for this entry. */
+ a->todo = TODO_MODE_BASE;
+ if (a->flags & ARCHIVE_EXTRACT_PERM) {
+ a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */
+ /*
+ * SGID requires an extra "check" step because we
+ * cannot easily predict the GID that the system will
+ * assign. (Different systems assign GIDs to files
+ * based on a variety of criteria, including process
+ * credentials and the gid of the enclosing
+ * directory.) We can only restore the SGID bit if
+ * the file has the right GID, and we only know the
+ * GID if we either set it (see set_ownership) or if
+ * we've actually called stat() on the file after it
+ * was restored. Since there are several places at
+ * which we might verify the GID, we need a TODO bit
+ * to keep track.
+ */
+ if (a->mode & S_ISGID)
+ a->todo |= TODO_SGID | TODO_SGID_CHECK;
+ /*
+ * Verifying the SUID is simpler, but can still be
+ * done in multiple ways, hence the separate "check" bit.
+ */
+ if (a->mode & S_ISUID)
+ a->todo |= TODO_SUID | TODO_SUID_CHECK;
+ } else {
+ /*
+ * User didn't request full permissions, so don't
+ * restore SUID, SGID bits and obey umask.
+ */
+ a->mode &= ~S_ISUID;
+ a->mode &= ~S_ISGID;
+ a->mode &= ~S_ISVTX;
+ a->mode &= ~a->user_umask;
+ }
+ if (a->flags & ARCHIVE_EXTRACT_OWNER)
+ a->todo |= TODO_OWNER;
+ if (a->flags & ARCHIVE_EXTRACT_TIME)
+ a->todo |= TODO_TIMES;
+ if (a->flags & ARCHIVE_EXTRACT_ACL) {
+#if ARCHIVE_ACL_DARWIN
+ /*
+ * On MacOS, platform ACLs get stored in mac_metadata, too.
+ * If we intend to extract mac_metadata and it is present
+ * we skip extracting libarchive NFSv4 ACLs.
+ */
+ size_t metadata_size;
+
+ if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||
+ archive_entry_mac_metadata(a->entry,
+ &metadata_size) == NULL || metadata_size == 0)
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+ /*
+ * RichACLs are stored in an extended attribute.
+ * If we intend to extract extended attributes and have this
+ * attribute we skip extracting libarchive NFSv4 ACLs.
+ */
+ short extract_acls = 1;
+ if (a->flags & ARCHIVE_EXTRACT_XATTR && (
+ archive_entry_acl_types(a->entry) &
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4)) {
+ const char *attr_name;
+ const void *attr_value;
+ size_t attr_size;
+ int i = archive_entry_xattr_reset(a->entry);
+ while (i--) {
+ archive_entry_xattr_next(a->entry, &attr_name,
+ &attr_value, &attr_size);
+ if (attr_name != NULL && attr_value != NULL &&
+ attr_size > 0 && strcmp(attr_name,
+ "trusted.richacl") == 0) {
+ extract_acls = 0;
+ break;
+ }
+ }
+ }
+ if (extract_acls)
+#endif
+#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
+ {
+#endif
+ if (archive_entry_filetype(a->entry) == AE_IFDIR)
+ a->deferred |= TODO_ACLS;
+ else
+ a->todo |= TODO_ACLS;
+#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
+ }
+#endif
+ }
+ if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) {
+ if (archive_entry_filetype(a->entry) == AE_IFDIR)
+ a->deferred |= TODO_MAC_METADATA;
+ else
+ a->todo |= TODO_MAC_METADATA;
+ }
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+ if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) {
+ unsigned long set, clear;
+ archive_entry_fflags(a->entry, &set, &clear);
+ if ((set & ~clear) & UF_COMPRESSED) {
+ a->todo |= TODO_HFS_COMPRESSION;
+ a->decmpfs_block_count = (unsigned)-1;
+ }
+ }
+ if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 &&
+ (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) {
+ a->todo |= TODO_HFS_COMPRESSION;
+ a->decmpfs_block_count = (unsigned)-1;
+ }
+ {
+ const char *p;
+
+ /* Check if the current file name is a type of the
+ * resource fork file. */
+ p = strrchr(a->name, '/');
+ if (p == NULL)
+ p = a->name;
+ else
+ p++;
+ if (p[0] == '.' && p[1] == '_') {
+ /* Do not compress "._XXX" files. */
+ a->todo &= ~TODO_HFS_COMPRESSION;
+ if (a->filesize > 0)
+ a->todo |= TODO_APPLEDOUBLE;
+ }
+ }
+#endif
+
+ if (a->flags & ARCHIVE_EXTRACT_XATTR) {
+#if ARCHIVE_XATTR_DARWIN
+ /*
+ * On MacOS, extended attributes get stored in mac_metadata,
+ * too. If we intend to extract mac_metadata and it is present
+ * we skip extracting extended attributes.
+ */
+ size_t metadata_size;
+
+ if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||
+ archive_entry_mac_metadata(a->entry,
+ &metadata_size) == NULL || metadata_size == 0)
+#endif
+ a->todo |= TODO_XATTR;
+ }
+ if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
+ a->todo |= TODO_FFLAGS;
+ if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+ ret = check_symlinks(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+ /* If path exceeds PATH_MAX, shorten the path. */
+ edit_deep_directories(a);
+#endif
+
+ ret = restore_entry(a);
+
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+ /*
+ * Check if the filesystem the file is restoring on supports
+ * HFS+ Compression. If not, cancel HFS+ Compression.
+ */
+ if (a->todo | TODO_HFS_COMPRESSION) {
+ /*
+ * NOTE: UF_COMPRESSED is ignored even if the filesystem
+ * supports HFS+ Compression because the file should
+ * have at least an extended attribute "com.apple.decmpfs"
+ * before the flag is set to indicate that the file have
+ * been compressed. If the filesystem does not support
+ * HFS+ Compression the system call will fail.
+ */
+ if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0)
+ a->todo &= ~TODO_HFS_COMPRESSION;
+ }
+#endif
+
+ /*
+ * TODO: There are rumours that some extended attributes must
+ * be restored before file data is written. If this is true,
+ * then we either need to write all extended attributes both
+ * before and after restoring the data, or find some rule for
+ * determining which must go first and which last. Due to the
+ * many ways people are using xattrs, this may prove to be an
+ * intractable problem.
+ */
+
+#ifdef HAVE_FCHDIR
+ /* If we changed directory above, restore it here. */
+ if (a->restore_pwd >= 0) {
+ r = fchdir(a->restore_pwd);
+ if (r != 0) {
+ archive_set_error(&a->archive, errno,
+ "chdir() failure");
+ ret = ARCHIVE_FATAL;
+ }
+ close(a->restore_pwd);
+ a->restore_pwd = -1;
+ }
+#endif
+
+ /*
+ * Fixup uses the unedited pathname from archive_entry_pathname(),
+ * because it is relative to the base dir and the edited path
+ * might be relative to some intermediate dir as a result of the
+ * deep restore logic.
+ */
+ if (a->deferred & TODO_MODE) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->fixup |= TODO_MODE_BASE;
+ fe->mode = a->mode;
+ }
+
+ if ((a->deferred & TODO_TIMES)
+ && (archive_entry_mtime_is_set(entry)
+ || archive_entry_atime_is_set(entry))) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->mode = a->mode;
+ fe->fixup |= TODO_TIMES;
+ if (archive_entry_atime_is_set(entry)) {
+ fe->atime = archive_entry_atime(entry);
+ fe->atime_nanos = archive_entry_atime_nsec(entry);
+ } else {
+ /* If atime is unset, use start time. */
+ fe->atime = a->start_time;
+ fe->atime_nanos = 0;
+ }
+ if (archive_entry_mtime_is_set(entry)) {
+ fe->mtime = archive_entry_mtime(entry);
+ fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+ } else {
+ /* If mtime is unset, use start time. */
+ fe->mtime = a->start_time;
+ fe->mtime_nanos = 0;
+ }
+ if (archive_entry_birthtime_is_set(entry)) {
+ fe->birthtime = archive_entry_birthtime(entry);
+ fe->birthtime_nanos = archive_entry_birthtime_nsec(
+ entry);
+ } else {
+ /* If birthtime is unset, use mtime. */
+ fe->birthtime = fe->mtime;
+ fe->birthtime_nanos = fe->mtime_nanos;
+ }
+ }
+
+ if (a->deferred & TODO_ACLS) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->fixup |= TODO_ACLS;
+ archive_acl_copy(&fe->acl, archive_entry_acl(entry));
+ }
+
+ if (a->deferred & TODO_MAC_METADATA) {
+ const void *metadata;
+ size_t metadata_size;
+ metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
+ if (metadata != NULL && metadata_size > 0) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->mac_metadata = malloc(metadata_size);
+ if (fe->mac_metadata != NULL) {
+ memcpy(fe->mac_metadata, metadata,
+ metadata_size);
+ fe->mac_metadata_size = metadata_size;
+ fe->fixup |= TODO_MAC_METADATA;
+ }
+ }
+ }
+
+ if (a->deferred & TODO_FFLAGS) {
+ fe = current_fixup(a, archive_entry_pathname(entry));
+ if (fe == NULL)
+ return (ARCHIVE_FATAL);
+ fe->filetype = archive_entry_filetype(entry);
+ fe->fixup |= TODO_FFLAGS;
+ /* TODO: Complete this.. defer fflags from below. */
+ }
+
+ /* We've created the object and are ready to pour data into it. */
+ if (ret >= ARCHIVE_WARN)
+ a->archive.state = ARCHIVE_STATE_DATA;
+ /*
+ * If it's not open, tell our client not to try writing.
+ * In particular, dirs, links, etc, don't get written to.
+ */
+ if (a->fd < 0) {
+ archive_entry_set_size(entry, 0);
+ a->filesize = 0;
+ }
+
+ return (ret);
+}
+
+int
+archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");
+ a->skip_file_set = 1;
+ a->skip_file_dev = d;
+ a->skip_file_ino = i;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
+{
+ uint64_t start_size = size;
+ ssize_t bytes_written = 0;
+ ssize_t block_size = 0, bytes_to_write;
+
+ if (size == 0)
+ return (ARCHIVE_OK);
+
+ if (a->filesize == 0 || a->fd < 0) {
+ archive_set_error(&a->archive, 0,
+ "Attempt to write to an empty file");
+ return (ARCHIVE_WARN);
+ }
+
+ if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
+#if HAVE_STRUCT_STAT_ST_BLKSIZE
+ int r;
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+ block_size = a->pst->st_blksize;
+#else
+ /* XXX TODO XXX Is there a more appropriate choice here ? */
+ /* This needn't match the filesystem allocation size. */
+ block_size = 16*1024;
+#endif
+ }
+
+ /* If this write would run beyond the file size, truncate it. */
+ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+ start_size = size = (size_t)(a->filesize - a->offset);
+
+ /* Write the data. */
+ while (size > 0) {
+ if (block_size == 0) {
+ bytes_to_write = size;
+ } else {
+ /* We're sparsifying the file. */
+ const char *p, *end;
+ int64_t block_end;
+
+ /* Skip leading zero bytes. */
+ for (p = buff, end = buff + size; p < end; ++p) {
+ if (*p != '\0')
+ break;
+ }
+ a->offset += p - buff;
+ size -= p - buff;
+ buff = p;
+ if (size == 0)
+ break;
+
+ /* Calculate next block boundary after offset. */
+ block_end
+ = (a->offset / block_size + 1) * block_size;
+
+ /* If the adjusted write would cross block boundary,
+ * truncate it to the block boundary. */
+ bytes_to_write = size;
+ if (a->offset + bytes_to_write > block_end)
+ bytes_to_write = block_end - a->offset;
+ }
+ /* Seek if necessary to the specified offset. */
+ if (a->offset != a->fd_offset) {
+ if (lseek(a->fd, a->offset, SEEK_SET) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Seek failed");
+ return (ARCHIVE_FATAL);
+ }
+ a->fd_offset = a->offset;
+ }
+ bytes_written = write(a->fd, buff, bytes_to_write);
+ if (bytes_written < 0) {
+ archive_set_error(&a->archive, errno, "Write failed");
+ return (ARCHIVE_WARN);
+ }
+ buff += bytes_written;
+ size -= bytes_written;
+ a->total_bytes_written += bytes_written;
+ a->offset += bytes_written;
+ a->fd_offset = a->offset;
+ }
+ return (start_size - size);
+}
+
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
+ && defined(HAVE_ZLIB_H)
+
+/*
+ * Set UF_COMPRESSED file flag.
+ * This have to be called after hfs_write_decmpfs() because if the
+ * file does not have "com.apple.decmpfs" xattr the flag is ignored.
+ */
+static int
+hfs_set_compressed_fflag(struct archive_write_disk *a)
+{
+ int r;
+
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+
+ a->st.st_flags |= UF_COMPRESSED;
+ if (fchflags(a->fd, a->st.st_flags) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to set UF_COMPRESSED file flag");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * HFS+ Compression decmpfs
+ *
+ * +------------------------------+ +0
+ * | Magic(LE 4 bytes) |
+ * +------------------------------+
+ * | Type(LE 4 bytes) |
+ * +------------------------------+
+ * | Uncompressed size(LE 8 bytes)|
+ * +------------------------------+ +16
+ * | |
+ * | Compressed data |
+ * | (Placed only if Type == 3) |
+ * | |
+ * +------------------------------+ +3802 = MAX_DECMPFS_XATTR_SIZE
+ *
+ * Type is 3: decmpfs has compressed data.
+ * Type is 4: Resource Fork has compressed data.
+ */
+/*
+ * Write "com.apple.decmpfs"
+ */
+static int
+hfs_write_decmpfs(struct archive_write_disk *a)
+{
+ int r;
+ uint32_t compression_type;
+
+ r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p,
+ a->decmpfs_attr_size, 0, 0);
+ if (r < 0) {
+ archive_set_error(&a->archive, errno,
+ "Cannot restore xattr:%s", DECMPFS_XATTR_NAME);
+ compression_type = archive_le32dec(
+ &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]);
+ if (compression_type == CMP_RESOURCE_FORK)
+ fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME,
+ XATTR_SHOWCOMPRESSION);
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * HFS+ Compression Resource Fork
+ *
+ * +-----------------------------+
+ * | Header(260 bytes) |
+ * +-----------------------------+
+ * | Block count(LE 4 bytes) |
+ * +-----------------------------+ --+
+ * +-- | Offset (LE 4 bytes) | |
+ * | | [distance from Block count] | | Block 0
+ * | +-----------------------------+ |
+ * | | Compressed size(LE 4 bytes) | |
+ * | +-----------------------------+ --+
+ * | | |
+ * | | .................. |
+ * | | |
+ * | +-----------------------------+ --+
+ * | | Offset (LE 4 bytes) | |
+ * | +-----------------------------+ | Block (Block count -1)
+ * | | Compressed size(LE 4 bytes) | |
+ * +-> +-----------------------------+ --+
+ * | Compressed data(n bytes) | Block 0
+ * +-----------------------------+
+ * | |
+ * | .................. |
+ * | |
+ * +-----------------------------+
+ * | Compressed data(n bytes) | Block (Block count -1)
+ * +-----------------------------+
+ * | Footer(50 bytes) |
+ * +-----------------------------+
+ *
+ */
+/*
+ * Write the header of "com.apple.ResourceFork"
+ */
+static int
+hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff,
+ size_t bytes, uint32_t position)
+{
+ int ret;
+
+ ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes,
+ position, a->rsrc_xattr_options);
+ if (ret < 0) {
+ archive_set_error(&a->archive, errno,
+ "Cannot restore xattr: %s at %u pos %u bytes",
+ XATTR_RESOURCEFORK_NAME,
+ (unsigned)position,
+ (unsigned)bytes);
+ return (ARCHIVE_WARN);
+ }
+ a->rsrc_xattr_options &= ~XATTR_CREATE;
+ return (ARCHIVE_OK);
+}
+
+static int
+hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed)
+{
+ int ret;
+
+ ret = hfs_write_resource_fork(a, a->compressed_buffer,
+ bytes_compressed, a->compressed_rsrc_position);
+ if (ret == ARCHIVE_OK)
+ a->compressed_rsrc_position += bytes_compressed;
+ return (ret);
+}
+
+static int
+hfs_write_resource_fork_header(struct archive_write_disk *a)
+{
+ unsigned char *buff;
+ uint32_t rsrc_bytes;
+ uint32_t rsrc_header_bytes;
+
+ /*
+ * Write resource fork header + block info.
+ */
+ buff = a->resource_fork;
+ rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE;
+ rsrc_header_bytes =
+ RSRC_H_SIZE + /* Header base size. */
+ 4 + /* Block count. */
+ (a->decmpfs_block_count * 8);/* Block info */
+ archive_be32enc(buff, 0x100);
+ archive_be32enc(buff + 4, rsrc_bytes);
+ archive_be32enc(buff + 8, rsrc_bytes - 256);
+ archive_be32enc(buff + 12, 0x32);
+ memset(buff + 16, 0, 240);
+ archive_be32enc(buff + 256, rsrc_bytes - 260);
+ return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0);
+}
+
+static size_t
+hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size)
+{
+ static const char rsrc_footer[RSRC_F_SIZE] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c', 'm',
+ 'p', 'f', 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+ if (buff_size < sizeof(rsrc_footer))
+ return (0);
+ memcpy(buff, rsrc_footer, sizeof(rsrc_footer));
+ return (sizeof(rsrc_footer));
+}
+
+static int
+hfs_reset_compressor(struct archive_write_disk *a)
+{
+ int ret;
+
+ if (a->stream_valid)
+ ret = deflateReset(&a->stream);
+ else
+ ret = deflateInit(&a->stream, a->decmpfs_compression_level);
+
+ if (ret != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to initialize compressor");
+ return (ARCHIVE_FATAL);
+ } else
+ a->stream_valid = 1;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+hfs_decompress(struct archive_write_disk *a)
+{
+ uint32_t *block_info;
+ unsigned int block_count;
+ uint32_t data_pos, data_size;
+ ssize_t r;
+ ssize_t bytes_written, bytes_to_write;
+ unsigned char *b;
+
+ block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
+ block_count = archive_le32dec(block_info++);
+ while (block_count--) {
+ data_pos = RSRC_H_SIZE + archive_le32dec(block_info++);
+ data_size = archive_le32dec(block_info++);
+ r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME,
+ a->compressed_buffer, data_size, data_pos, 0);
+ if (r != data_size) {
+ archive_set_error(&a->archive,
+ (r < 0)?errno:ARCHIVE_ERRNO_MISC,
+ "Failed to read resource fork");
+ return (ARCHIVE_WARN);
+ }
+ if (a->compressed_buffer[0] == 0xff) {
+ bytes_to_write = data_size -1;
+ b = a->compressed_buffer + 1;
+ } else {
+ uLong dest_len = MAX_DECMPFS_BLOCK_SIZE;
+ int zr;
+
+ zr = uncompress((Bytef *)a->uncompressed_buffer,
+ &dest_len, a->compressed_buffer, data_size);
+ if (zr != Z_OK) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to decompress resource fork");
+ return (ARCHIVE_WARN);
+ }
+ bytes_to_write = dest_len;
+ b = (unsigned char *)a->uncompressed_buffer;
+ }
+ do {
+ bytes_written = write(a->fd, b, bytes_to_write);
+ if (bytes_written < 0) {
+ archive_set_error(&a->archive, errno,
+ "Write failed");
+ return (ARCHIVE_WARN);
+ }
+ bytes_to_write -= bytes_written;
+ b += bytes_written;
+ } while (bytes_to_write > 0);
+ }
+ r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to remove resource fork");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+hfs_drive_compressor(struct archive_write_disk *a, const char *buff,
+ size_t size)
+{
+ unsigned char *buffer_compressed;
+ size_t bytes_compressed;
+ size_t bytes_used;
+ int ret;
+
+ ret = hfs_reset_compressor(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ if (a->compressed_buffer == NULL) {
+ size_t block_size;
+
+ block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE +
+ + compressBound(MAX_DECMPFS_BLOCK_SIZE);
+ a->compressed_buffer = malloc(block_size);
+ if (a->compressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Resource Fork");
+ return (ARCHIVE_FATAL);
+ }
+ a->compressed_buffer_size = block_size;
+ a->compressed_buffer_remaining = block_size;
+ }
+
+ buffer_compressed = a->compressed_buffer +
+ a->compressed_buffer_size - a->compressed_buffer_remaining;
+ a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;
+ a->stream.avail_in = size;
+ a->stream.next_out = buffer_compressed;
+ a->stream.avail_out = a->compressed_buffer_remaining;
+ do {
+ ret = deflate(&a->stream, Z_FINISH);
+ switch (ret) {
+ case Z_OK:
+ case Z_STREAM_END:
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to compress data");
+ return (ARCHIVE_FAILED);
+ }
+ } while (ret == Z_OK);
+ bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out;
+
+ /*
+ * If the compressed size is larger than the original size,
+ * throw away compressed data, use uncompressed data instead.
+ */
+ if (bytes_compressed > size) {
+ buffer_compressed[0] = 0xFF;/* uncompressed marker. */
+ memcpy(buffer_compressed + 1, buff, size);
+ bytes_compressed = size + 1;
+ }
+ a->compressed_buffer_remaining -= bytes_compressed;
+
+ /*
+ * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE
+ * and the block count in the file is only one, store compressed
+ * data to decmpfs xattr instead of the resource fork.
+ */
+ if (a->decmpfs_block_count == 1 &&
+ (a->decmpfs_attr_size + bytes_compressed)
+ <= MAX_DECMPFS_XATTR_SIZE) {
+ archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],
+ CMP_XATTR);
+ memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE,
+ buffer_compressed, bytes_compressed);
+ a->decmpfs_attr_size += bytes_compressed;
+ a->compressed_buffer_remaining = a->compressed_buffer_size;
+ /*
+ * Finish HFS+ Compression.
+ * - Write the decmpfs xattr.
+ * - Set the UF_COMPRESSED file flag.
+ */
+ ret = hfs_write_decmpfs(a);
+ if (ret == ARCHIVE_OK)
+ ret = hfs_set_compressed_fflag(a);
+ return (ret);
+ }
+
+ /* Update block info. */
+ archive_le32enc(a->decmpfs_block_info++,
+ a->compressed_rsrc_position_v - RSRC_H_SIZE);
+ archive_le32enc(a->decmpfs_block_info++, bytes_compressed);
+ a->compressed_rsrc_position_v += bytes_compressed;
+
+ /*
+ * Write the compressed data to the resource fork.
+ */
+ bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining;
+ while (bytes_used >= COMPRESSED_W_SIZE) {
+ ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ bytes_used -= COMPRESSED_W_SIZE;
+ if (bytes_used > COMPRESSED_W_SIZE)
+ memmove(a->compressed_buffer,
+ a->compressed_buffer + COMPRESSED_W_SIZE,
+ bytes_used);
+ else
+ memcpy(a->compressed_buffer,
+ a->compressed_buffer + COMPRESSED_W_SIZE,
+ bytes_used);
+ }
+ a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used;
+
+ /*
+ * If the current block is the last block, write the remaining
+ * compressed data and the resource fork footer.
+ */
+ if (a->file_remaining_bytes == 0) {
+ size_t rsrc_size;
+ int64_t bk;
+
+ /* Append the resource footer. */
+ rsrc_size = hfs_set_resource_fork_footer(
+ a->compressed_buffer + bytes_used,
+ a->compressed_buffer_remaining);
+ ret = hfs_write_compressed_data(a, bytes_used + rsrc_size);
+ a->compressed_buffer_remaining = a->compressed_buffer_size;
+
+ /* If the compressed size is not enough smaller than
+ * the uncompressed size. cancel HFS+ compression.
+ * TODO: study a behavior of ditto utility and improve
+ * the condition to fall back into no HFS+ compression. */
+ bk = HFS_BLOCKS(a->compressed_rsrc_position);
+ bk += bk >> 7;
+ if (bk > HFS_BLOCKS(a->filesize))
+ return hfs_decompress(a);
+ /*
+ * Write the resourcefork header.
+ */
+ if (ret == ARCHIVE_OK)
+ ret = hfs_write_resource_fork_header(a);
+ /*
+ * Finish HFS+ Compression.
+ * - Write the decmpfs xattr.
+ * - Set the UF_COMPRESSED file flag.
+ */
+ if (ret == ARCHIVE_OK)
+ ret = hfs_write_decmpfs(a);
+ if (ret == ARCHIVE_OK)
+ ret = hfs_set_compressed_fflag(a);
+ }
+ return (ret);
+}
+
+static ssize_t
+hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,
+ size_t size)
+{
+ const char *buffer_to_write;
+ size_t bytes_to_write;
+ int ret;
+
+ if (a->decmpfs_block_count == (unsigned)-1) {
+ void *new_block;
+ size_t new_size;
+ unsigned int block_count;
+
+ if (a->decmpfs_header_p == NULL) {
+ new_block = malloc(MAX_DECMPFS_XATTR_SIZE
+ + sizeof(uint32_t));
+ if (new_block == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for decmpfs");
+ return (ARCHIVE_FATAL);
+ }
+ a->decmpfs_header_p = new_block;
+ }
+ a->decmpfs_attr_size = DECMPFS_HEADER_SIZE;
+ archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC],
+ DECMPFS_MAGIC);
+ archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],
+ CMP_RESOURCE_FORK);
+ archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE],
+ a->filesize);
+
+ /* Calculate a block count of the file. */
+ block_count =
+ (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) /
+ MAX_DECMPFS_BLOCK_SIZE;
+ /*
+ * Allocate buffer for resource fork.
+ * Set up related pointers;
+ */
+ new_size =
+ RSRC_H_SIZE + /* header */
+ 4 + /* Block count */
+ (block_count * sizeof(uint32_t) * 2) +
+ RSRC_F_SIZE; /* footer */
+ if (new_size > a->resource_fork_allocated_size) {
+ new_block = realloc(a->resource_fork, new_size);
+ if (new_block == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for ResourceFork");
+ return (ARCHIVE_FATAL);
+ }
+ a->resource_fork_allocated_size = new_size;
+ a->resource_fork = new_block;
+ }
+
+ /* Allocate uncompressed buffer */
+ if (a->uncompressed_buffer == NULL) {
+ new_block = malloc(MAX_DECMPFS_BLOCK_SIZE);
+ if (new_block == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for decmpfs");
+ return (ARCHIVE_FATAL);
+ }
+ a->uncompressed_buffer = new_block;
+ }
+ a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;
+ a->file_remaining_bytes = a->filesize;
+ a->compressed_buffer_remaining = a->compressed_buffer_size;
+
+ /*
+ * Set up a resource fork.
+ */
+ a->rsrc_xattr_options = XATTR_CREATE;
+ /* Get the position where we are going to set a bunch
+ * of block info. */
+ a->decmpfs_block_info =
+ (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
+ /* Set the block count to the resource fork. */
+ archive_le32enc(a->decmpfs_block_info++, block_count);
+ /* Get the position where we are going to set compressed
+ * data. */
+ a->compressed_rsrc_position =
+ RSRC_H_SIZE + 4 + (block_count * 8);
+ a->compressed_rsrc_position_v = a->compressed_rsrc_position;
+ a->decmpfs_block_count = block_count;
+ }
+
+ /* Ignore redundant bytes. */
+ if (a->file_remaining_bytes == 0)
+ return ((ssize_t)size);
+
+ /* Do not overrun a block size. */
+ if (size > a->block_remaining_bytes)
+ bytes_to_write = a->block_remaining_bytes;
+ else
+ bytes_to_write = size;
+ /* Do not overrun the file size. */
+ if (bytes_to_write > a->file_remaining_bytes)
+ bytes_to_write = a->file_remaining_bytes;
+
+ /* For efficiency, if a copy length is full of the uncompressed
+ * buffer size, do not copy writing data to it. */
+ if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE)
+ buffer_to_write = buff;
+ else {
+ memcpy(a->uncompressed_buffer +
+ MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes,
+ buff, bytes_to_write);
+ buffer_to_write = a->uncompressed_buffer;
+ }
+ a->block_remaining_bytes -= bytes_to_write;
+ a->file_remaining_bytes -= bytes_to_write;
+
+ if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) {
+ ret = hfs_drive_compressor(a, buffer_to_write,
+ MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes);
+ if (ret < 0)
+ return (ret);
+ a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;
+ }
+ /* Ignore redundant bytes. */
+ if (a->file_remaining_bytes == 0)
+ return ((ssize_t)size);
+ return (bytes_to_write);
+}
+
+static ssize_t
+hfs_write_data_block(struct archive_write_disk *a, const char *buff,
+ size_t size)
+{
+ uint64_t start_size = size;
+ ssize_t bytes_written = 0;
+ ssize_t bytes_to_write;
+
+ if (size == 0)
+ return (ARCHIVE_OK);
+
+ if (a->filesize == 0 || a->fd < 0) {
+ archive_set_error(&a->archive, 0,
+ "Attempt to write to an empty file");
+ return (ARCHIVE_WARN);
+ }
+
+ /* If this write would run beyond the file size, truncate it. */
+ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+ start_size = size = (size_t)(a->filesize - a->offset);
+
+ /* Write the data. */
+ while (size > 0) {
+ bytes_to_write = size;
+ /* Seek if necessary to the specified offset. */
+ if (a->offset < a->fd_offset) {
+ /* Can't support backward move. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Seek failed");
+ return (ARCHIVE_FATAL);
+ } else if (a->offset > a->fd_offset) {
+ uint64_t skip = a->offset - a->fd_offset;
+ char nullblock[1024];
+
+ memset(nullblock, 0, sizeof(nullblock));
+ while (skip > 0) {
+ if (skip > sizeof(nullblock))
+ bytes_written = hfs_write_decmpfs_block(
+ a, nullblock, sizeof(nullblock));
+ else
+ bytes_written = hfs_write_decmpfs_block(
+ a, nullblock, skip);
+ if (bytes_written < 0) {
+ archive_set_error(&a->archive, errno,
+ "Write failed");
+ return (ARCHIVE_WARN);
+ }
+ skip -= bytes_written;
+ }
+
+ a->fd_offset = a->offset;
+ }
+ bytes_written =
+ hfs_write_decmpfs_block(a, buff, bytes_to_write);
+ if (bytes_written < 0)
+ return (bytes_written);
+ buff += bytes_written;
+ size -= bytes_written;
+ a->total_bytes_written += bytes_written;
+ a->offset += bytes_written;
+ a->fd_offset = a->offset;
+ }
+ return (start_size - size);
+}
+#else
+static ssize_t
+hfs_write_data_block(struct archive_write_disk *a, const char *buff,
+ size_t size)
+{
+ return (write_data_block(a, buff, size));
+}
+#endif
+
+static ssize_t
+_archive_write_disk_data_block(struct archive *_a,
+ const void *buff, size_t size, int64_t offset)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ ssize_t r;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data_block");
+
+ a->offset = offset;
+ if (a->todo & TODO_HFS_COMPRESSION)
+ r = hfs_write_data_block(a, buff, size);
+ else
+ r = write_data_block(a, buff, size);
+ if (r < ARCHIVE_OK)
+ return (r);
+ if ((size_t)r < size) {
+ archive_set_error(&a->archive, 0,
+ "Too much data: Truncating file at %ju bytes",
+ (uintmax_t)a->filesize);
+ return (ARCHIVE_WARN);
+ }
+#if ARCHIVE_VERSION_NUMBER < 3999000
+ return (ARCHIVE_OK);
+#else
+ return (size);
+#endif
+}
+
+static ssize_t
+_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data");
+
+ if (a->todo & TODO_HFS_COMPRESSION)
+ return (hfs_write_data_block(a, buff, size));
+ return (write_data_block(a, buff, size));
+}
+
+static int
+_archive_write_disk_finish_entry(struct archive *_a)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ int ret = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_finish_entry");
+ if (a->archive.state & ARCHIVE_STATE_HEADER)
+ return (ARCHIVE_OK);
+ archive_clear_error(&a->archive);
+
+ /* Pad or truncate file to the right size. */
+ if (a->fd < 0) {
+ /* There's no file. */
+ } else if (a->filesize < 0) {
+ /* File size is unknown, so we can't set the size. */
+ } else if (a->fd_offset == a->filesize) {
+ /* Last write ended at exactly the filesize; we're done. */
+ /* Hopefully, this is the common case. */
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+ } else if (a->todo & TODO_HFS_COMPRESSION) {
+ char null_d[1024];
+ ssize_t r;
+
+ if (a->file_remaining_bytes)
+ memset(null_d, 0, sizeof(null_d));
+ while (a->file_remaining_bytes) {
+ if (a->file_remaining_bytes > sizeof(null_d))
+ r = hfs_write_data_block(
+ a, null_d, sizeof(null_d));
+ else
+ r = hfs_write_data_block(
+ a, null_d, a->file_remaining_bytes);
+ if (r < 0) {
+ close_file_descriptor(a);
+ return ((int)r);
+ }
+ }
+#endif
+ } else {
+#if HAVE_FTRUNCATE
+ if (ftruncate(a->fd, a->filesize) == -1 &&
+ a->filesize == 0) {
+ archive_set_error(&a->archive, errno,
+ "File size could not be restored");
+ close_file_descriptor(a);
+ return (ARCHIVE_FAILED);
+ }
+#endif
+ /*
+ * Not all platforms implement the XSI option to
+ * extend files via ftruncate. Stat() the file again
+ * to see what happened.
+ */
+ a->pst = NULL;
+ if ((ret = lazy_stat(a)) != ARCHIVE_OK) {
+ close_file_descriptor(a);
+ return (ret);
+ }
+ /* We can use lseek()/write() to extend the file if
+ * ftruncate didn't work or isn't available. */
+ if (a->st.st_size < a->filesize) {
+ const char nul = '\0';
+ if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Seek failed");
+ close_file_descriptor(a);
+ return (ARCHIVE_FATAL);
+ }
+ if (write(a->fd, &nul, 1) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Write to restore size failed");
+ close_file_descriptor(a);
+ return (ARCHIVE_FATAL);
+ }
+ a->pst = NULL;
+ }
+ }
+
+ /* Restore metadata. */
+
+ /*
+ * This is specific to Mac OS X.
+ * If the current file is an AppleDouble file, it should be
+ * linked with the data fork file and remove it.
+ */
+ if (a->todo & TODO_APPLEDOUBLE) {
+ int r2 = fixup_appledouble(a, a->name);
+ if (r2 == ARCHIVE_EOF) {
+ /* The current file has been successfully linked
+ * with the data fork file and removed. So there
+ * is nothing to do on the current file. */
+ goto finish_metadata;
+ }
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Look up the "real" UID only if we're going to need it.
+ * TODO: the TODO_SGID condition can be dropped here, can't it?
+ */
+ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
+ a->uid = archive_write_disk_uid(&a->archive,
+ archive_entry_uname(a->entry),
+ archive_entry_uid(a->entry));
+ }
+ /* Look up the "real" GID only if we're going to need it. */
+ /* TODO: the TODO_SUID condition can be dropped here, can't it? */
+ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
+ a->gid = archive_write_disk_gid(&a->archive,
+ archive_entry_gname(a->entry),
+ archive_entry_gid(a->entry));
+ }
+
+ /*
+ * Restore ownership before set_mode tries to restore suid/sgid
+ * bits. If we set the owner, we know what it is and can skip
+ * a stat() call to examine the ownership of the file on disk.
+ */
+ if (a->todo & TODO_OWNER) {
+ int r2 = set_ownership(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * HYPOTHESIS:
+ * If we're not root, we won't be setting any security
+ * attributes that may be wiped by the set_mode() routine
+ * below. We also can't set xattr on non-owner-writable files,
+ * which may be the state after set_mode(). Perform
+ * set_xattrs() first based on these constraints.
+ */
+ if (a->user_uid != 0 &&
+ (a->todo & TODO_XATTR)) {
+ int r2 = set_xattrs(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * set_mode must precede ACLs on systems such as Solaris and
+ * FreeBSD where setting the mode implicitly clears extended ACLs
+ */
+ if (a->todo & TODO_MODE) {
+ int r2 = set_mode(a, a->mode);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Security-related extended attributes (such as
+ * security.capability on Linux) have to be restored last,
+ * since they're implicitly removed by other file changes.
+ * We do this last only when root.
+ */
+ if (a->user_uid == 0 &&
+ (a->todo & TODO_XATTR)) {
+ int r2 = set_xattrs(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Some flags prevent file modification; they must be restored after
+ * file contents are written.
+ */
+ if (a->todo & TODO_FFLAGS) {
+ int r2 = set_fflags(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Time must follow most other metadata;
+ * otherwise atime will get changed.
+ */
+ if (a->todo & TODO_TIMES) {
+ int r2 = set_times_from_entry(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Mac extended metadata includes ACLs.
+ */
+ if (a->todo & TODO_MAC_METADATA) {
+ const void *metadata;
+ size_t metadata_size;
+ metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
+ if (metadata != NULL && metadata_size > 0) {
+ int r2 = set_mac_metadata(a, archive_entry_pathname(
+ a->entry), metadata, metadata_size);
+ if (r2 < ret) ret = r2;
+ }
+ }
+
+ /*
+ * ACLs must be restored after timestamps because there are
+ * ACLs that prevent attribute changes (including time).
+ */
+ if (a->todo & TODO_ACLS) {
+ int r2;
+ r2 = archive_write_disk_set_acls(&a->archive, a->fd,
+ archive_entry_pathname(a->entry),
+ archive_entry_acl(a->entry),
+ archive_entry_mode(a->entry));
+ if (r2 < ret) ret = r2;
+ }
+
+finish_metadata:
+ /* If there's an fd, we can close it now. */
+ if (a->fd >= 0) {
+ close(a->fd);
+ a->fd = -1;
+ if (a->tmpname) {
+ if (rename(a->tmpname, a->name) == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to rename temporary file");
+ ret = ARCHIVE_FAILED;
+ unlink(a->tmpname);
+ }
+ a->tmpname = NULL;
+ }
+ }
+ /* If there's an entry, we can release it now. */
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ return (ret);
+}
+
+int
+archive_write_disk_set_group_lookup(struct archive *_a,
+ void *private_data,
+ la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid),
+ void (*cleanup_gid)(void *private))
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");
+
+ if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)
+ (a->cleanup_gid)(a->lookup_gid_data);
+
+ a->lookup_gid = lookup_gid;
+ a->cleanup_gid = cleanup_gid;
+ a->lookup_gid_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_user_lookup(struct archive *_a,
+ void *private_data,
+ int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid),
+ void (*cleanup_uid)(void *private))
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");
+
+ if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)
+ (a->cleanup_uid)(a->lookup_uid_data);
+
+ a->lookup_uid = lookup_uid;
+ a->cleanup_uid = cleanup_uid;
+ a->lookup_uid_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int64_t
+archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_gid");
+ if (a->lookup_gid)
+ return (a->lookup_gid)(a->lookup_gid_data, name, id);
+ return (id);
+}
+
+int64_t
+archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_uid");
+ if (a->lookup_uid)
+ return (a->lookup_uid)(a->lookup_uid_data, name, id);
+ return (id);
+}
+
+/*
+ * Create a new archive_write_disk object and initialize it with global state.
+ */
+struct archive *
+archive_write_disk_new(void)
+{
+ struct archive_write_disk *a;
+
+ a = (struct archive_write_disk *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
+ /* We're ready to write a header immediately. */
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ a->archive.vtable = &archive_write_disk_vtable;
+ a->start_time = time(NULL);
+ /* Query and restore the umask. */
+ umask(a->user_umask = umask(0));
+#ifdef HAVE_GETEUID
+ a->user_uid = geteuid();
+#endif /* HAVE_GETEUID */
+ if (archive_string_ensure(&a->path_safe, 512) == NULL) {
+ free(a);
+ return (NULL);
+ }
+ a->path_safe.s[0] = 0;
+
+#ifdef HAVE_ZLIB_H
+ a->decmpfs_compression_level = 5;
+#endif
+ return (&a->archive);
+}
+
+
+/*
+ * If pathname is longer than PATH_MAX, chdir to a suitable
+ * intermediate dir and edit the path down to a shorter suffix. Note
+ * that this routine never returns an error; if the chdir() attempt
+ * fails for any reason, we just go ahead with the long pathname. The
+ * object creation is likely to fail, but any error will get handled
+ * at that time.
+ */
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+static void
+edit_deep_directories(struct archive_write_disk *a)
+{
+ int ret;
+ char *tail = a->name;
+
+ /* If path is short, avoid the open() below. */
+ if (strlen(tail) < PATH_MAX)
+ return;
+
+ /* Try to record our starting dir. */
+ a->restore_pwd = la_opendirat(AT_FDCWD, ".");
+ __archive_ensure_cloexec_flag(a->restore_pwd);
+ if (a->restore_pwd < 0)
+ return;
+
+ /* As long as the path is too long... */
+ while (strlen(tail) >= PATH_MAX) {
+ /* Locate a dir prefix shorter than PATH_MAX. */
+ tail += PATH_MAX - 8;
+ while (tail > a->name && *tail != '/')
+ tail--;
+ /* Exit if we find a too-long path component. */
+ if (tail <= a->name)
+ return;
+ /* Create the intermediate dir and chdir to it. */
+ *tail = '\0'; /* Terminate dir portion */
+ ret = create_dir(a, a->name);
+ if (ret == ARCHIVE_OK && chdir(a->name) != 0)
+ ret = ARCHIVE_FAILED;
+ *tail = '/'; /* Restore the / we removed. */
+ if (ret != ARCHIVE_OK)
+ return;
+ tail++;
+ /* The chdir() succeeded; we've now shortened the path. */
+ a->name = tail;
+ }
+ return;
+}
+#endif
+
+/*
+ * The main restore function.
+ */
+static int
+restore_entry(struct archive_write_disk *a)
+{
+ int ret = ARCHIVE_OK, en;
+
+ if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+ /*
+ * TODO: Fix this. Apparently, there are platforms
+ * that still allow root to hose the entire filesystem
+ * by unlinking a dir. The S_ISDIR() test above
+ * prevents us from using unlink() here if the new
+ * object is a dir, but that doesn't mean the old
+ * object isn't a dir.
+ */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+ if (unlink(a->name) == 0) {
+ /* We removed it, reset cached stat. */
+ a->pst = NULL;
+ } else if (errno == ENOENT) {
+ /* File didn't exist, that's just as good. */
+ } else if (rmdir(a->name) == 0) {
+ /* It was a dir, but now it's gone. */
+ a->pst = NULL;
+ } else {
+ /* We tried, but couldn't get rid of it. */
+ archive_set_error(&a->archive, errno,
+ "Could not unlink");
+ return(ARCHIVE_FAILED);
+ }
+ }
+
+ /* Try creating it first; if this fails, we'll try to recover. */
+ en = create_filesystem_object(a);
+
+ if ((en == ENOTDIR || en == ENOENT)
+ && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
+ /* If the parent dir doesn't exist, try creating it. */
+ create_parent_dir(a, a->name);
+ /* Now try to create the object again. */
+ en = create_filesystem_object(a);
+ }
+
+ if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {
+ archive_set_error(&a->archive, en,
+ "Hard-link target '%s' does not exist.",
+ archive_entry_hardlink(a->entry));
+ return (ARCHIVE_FAILED);
+ }
+
+ if ((en == EISDIR || en == EEXIST)
+ && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ /* If we're not overwriting, we're done. */
+ if (S_ISDIR(a->mode)) {
+ /* Don't overwrite any settings on existing directories. */
+ a->todo = 0;
+ }
+ archive_entry_unset_size(a->entry);
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Some platforms return EISDIR if you call
+ * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
+ * return EEXIST. POSIX is ambiguous, requiring EISDIR
+ * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
+ * on an existing item.
+ */
+ if (en == EISDIR) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't remove already-existing dir");
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else if (en == EEXIST) {
+ /*
+ * We know something is in the way, but we don't know what;
+ * we need to find out before we go any further.
+ */
+ int r = 0;
+ /*
+ * The SECURE_SYMLINKS logic has already removed a
+ * symlink to a dir if the client wants that. So
+ * follow the symlink if we're creating a dir.
+ */
+ if (S_ISDIR(a->mode))
+ r = la_stat(a->name, &a->st);
+ /*
+ * If it's not a dir (or it's a broken symlink),
+ * then don't follow it.
+ */
+ if (r != 0 || !S_ISDIR(a->mode))
+#ifdef HAVE_LSTAT
+ r = lstat(a->name, &a->st);
+#else
+ r = la_stat(a->name, &a->st);
+#endif
+ if (r != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't stat existing object");
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * NO_OVERWRITE_NEWER doesn't apply to directories.
+ */
+ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)
+ && !S_ISDIR(a->st.st_mode)) {
+ if (!older(&(a->st), a->entry)) {
+ archive_entry_unset_size(a->entry);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /* If it's our archive, we're done. */
+ if (a->skip_file_set &&
+ a->st.st_dev == (dev_t)a->skip_file_dev &&
+ a->st.st_ino == (ino_t)a->skip_file_ino) {
+ archive_set_error(&a->archive, 0,
+ "Refusing to overwrite archive");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!S_ISDIR(a->st.st_mode)) {
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+
+ if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&
+ S_ISREG(a->st.st_mode)) {
+ /* Use a temporary file to extract */
+ if ((a->fd = la_mktemp(a)) == -1) {
+ archive_set_error(&a->archive, errno,
+ "Can't create temporary file");
+ return ARCHIVE_FAILED;
+ }
+ a->pst = NULL;
+ en = 0;
+ } else {
+ /* A non-dir is in the way, unlink it. */
+ if (unlink(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't unlink already-existing "
+ "object");
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ /* Try again. */
+ en = create_filesystem_object(a);
+ }
+ } else if (!S_ISDIR(a->mode)) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+ if (rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't replace existing directory with non-directory");
+ return (ARCHIVE_FAILED);
+ }
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else {
+ /*
+ * There's a dir in the way of a dir. Don't
+ * waste time with rmdir()/mkdir(), just fix
+ * up the permissions on the existing dir.
+ * Note that we don't change perms on existing
+ * dirs unless _EXTRACT_PERM is specified.
+ */
+ if ((a->mode != a->st.st_mode)
+ && (a->todo & TODO_MODE_FORCE))
+ a->deferred |= (a->todo & TODO_MODE);
+ /* Ownership doesn't need deferred fixup. */
+ en = 0; /* Forget the EEXIST. */
+ }
+ }
+
+ if (en) {
+ /* Everything failed; give up here. */
+ if ((&a->archive)->error == NULL)
+ archive_set_error(&a->archive, en, "Can't create '%s'",
+ a->name);
+ return (ARCHIVE_FAILED);
+ }
+
+ a->pst = NULL; /* Cached stat data no longer valid. */
+ return (ret);
+}
+
+/*
+ * Returns 0 if creation succeeds, or else returns errno value from
+ * the failed system call. Note: This function should only ever perform
+ * a single system call.
+ */
+static int
+create_filesystem_object(struct archive_write_disk *a)
+{
+ /* Create the entry. */
+ const char *linkname;
+ mode_t final_mode, mode;
+ int r;
+ /* these for check_symlinks_fsobj */
+ char *linkname_copy; /* non-const copy of linkname */
+ struct stat st;
+ struct archive_string error_string;
+ int error_number;
+
+ /* We identify hard/symlinks according to the link names. */
+ /* Since link(2) and symlink(2) don't handle modes, we're done here. */
+ linkname = archive_entry_hardlink(a->entry);
+ if (linkname != NULL) {
+#if !HAVE_LINK
+ return (EPERM);
+#else
+ archive_string_init(&error_string);
+ linkname_copy = strdup(linkname);
+ if (linkname_copy == NULL) {
+ return (EPERM);
+ }
+ /*
+ * TODO: consider using the cleaned-up path as the link
+ * target?
+ */
+ r = cleanup_pathname_fsobj(linkname_copy, &error_number,
+ &error_string, a->flags);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ free(linkname_copy);
+ archive_string_free(&error_string);
+ /*
+ * EPERM is more appropriate than error_number for our
+ * callers
+ */
+ return (EPERM);
+ }
+ r = check_symlinks_fsobj(linkname_copy, &error_number,
+ &error_string, a->flags, 1);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ free(linkname_copy);
+ archive_string_free(&error_string);
+ /*
+ * EPERM is more appropriate than error_number for our
+ * callers
+ */
+ return (EPERM);
+ }
+ free(linkname_copy);
+ archive_string_free(&error_string);
+ /*
+ * Unlinking and linking here is really not atomic,
+ * but doing it right, would require us to construct
+ * an mktemplink() function, and then use rename(2).
+ */
+ if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES)
+ unlink(a->name);
+#ifdef HAVE_LINKAT
+ r = linkat(AT_FDCWD, linkname, AT_FDCWD, a->name,
+ 0) ? errno : 0;
+#else
+ r = link(linkname, a->name) ? errno : 0;
+#endif
+ /*
+ * New cpio and pax formats allow hardlink entries
+ * to carry data, so we may have to open the file
+ * for hardlink entries.
+ *
+ * If the hardlink was successfully created and
+ * the archive doesn't have carry data for it,
+ * consider it to be non-authoritative for meta data.
+ * This is consistent with GNU tar and BSD pax.
+ * If the hardlink does carry data, let the last
+ * archive entry decide ownership.
+ */
+ if (r == 0 && a->filesize <= 0) {
+ a->todo = 0;
+ a->deferred = 0;
+ } else if (r == 0 && a->filesize > 0) {
+#ifdef HAVE_LSTAT
+ r = lstat(a->name, &st);
+#else
+ r = la_stat(a->name, &st);
+#endif
+ if (r != 0)
+ r = errno;
+ else if ((st.st_mode & AE_IFMT) == AE_IFREG) {
+ a->fd = open(a->name, O_WRONLY | O_TRUNC |
+ O_BINARY | O_CLOEXEC | O_NOFOLLOW);
+ __archive_ensure_cloexec_flag(a->fd);
+ if (a->fd < 0)
+ r = errno;
+ }
+ }
+ return (r);
+#endif
+ }
+ linkname = archive_entry_symlink(a->entry);
+ if (linkname != NULL) {
+#if HAVE_SYMLINK
+ /*
+ * Unlinking and linking here is really not atomic,
+ * but doing it right, would require us to construct
+ * an mktempsymlink() function, and then use rename(2).
+ */
+ if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES)
+ unlink(a->name);
+ return symlink(linkname, a->name) ? errno : 0;
+#else
+ return (EPERM);
+#endif
+ }
+
+ /*
+ * The remaining system calls all set permissions, so let's
+ * try to take advantage of that to avoid an extra chmod()
+ * call. (Recall that umask is set to zero right now!)
+ */
+
+ /* Mode we want for the final restored object (w/o file type bits). */
+ final_mode = a->mode & 07777;
+ /*
+ * The mode that will actually be restored in this step. Note
+ * that SUID, SGID, etc, require additional work to ensure
+ * security, so we never restore them at this point.
+ */
+ mode = final_mode & 0777 & ~a->user_umask;
+
+ /*
+ * Always create writable such that [f]setxattr() works if we're not
+ * root.
+ */
+ if (a->user_uid != 0 &&
+ a->todo & (TODO_HFS_COMPRESSION | TODO_XATTR)) {
+ mode |= 0200;
+ }
+
+ switch (a->mode & AE_IFMT) {
+ default:
+ /* POSIX requires that we fall through here. */
+ /* FALLTHROUGH */
+ case AE_IFREG:
+ a->tmpname = NULL;
+ a->fd = open(a->name,
+ O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode);
+ __archive_ensure_cloexec_flag(a->fd);
+ r = (a->fd < 0);
+ break;
+ case AE_IFCHR:
+#ifdef HAVE_MKNOD
+ /* Note: we use AE_IFCHR for the case label, and
+ * S_IFCHR for the mknod() call. This is correct. */
+ r = mknod(a->name, mode | S_IFCHR,
+ archive_entry_rdev(a->entry));
+ break;
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a char device node. */
+ return (EINVAL);
+#endif /* HAVE_MKNOD */
+ case AE_IFBLK:
+#ifdef HAVE_MKNOD
+ r = mknod(a->name, mode | S_IFBLK,
+ archive_entry_rdev(a->entry));
+ break;
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a block device node. */
+ return (EINVAL);
+#endif /* HAVE_MKNOD */
+ case AE_IFDIR:
+ mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
+ r = mkdir(a->name, mode);
+ if (r == 0) {
+ /* Defer setting dir times. */
+ a->deferred |= (a->todo & TODO_TIMES);
+ a->todo &= ~TODO_TIMES;
+ /* Never use an immediate chmod(). */
+ /* We can't avoid the chmod() entirely if EXTRACT_PERM
+ * because of SysV SGID inheritance. */
+ if ((mode != final_mode)
+ || (a->flags & ARCHIVE_EXTRACT_PERM))
+ a->deferred |= (a->todo & TODO_MODE);
+ a->todo &= ~TODO_MODE;
+ }
+ break;
+ case AE_IFIFO:
+#ifdef HAVE_MKFIFO
+ r = mkfifo(a->name, mode);
+ break;
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a fifo. */
+ return (EINVAL);
+#endif /* HAVE_MKFIFO */
+ }
+
+ /* All the system calls above set errno on failure. */
+ if (r)
+ return (errno);
+
+ /* If we managed to set the final mode, we've avoided a chmod(). */
+ if (mode == final_mode)
+ a->todo &= ~TODO_MODE;
+ return (0);
+}
+
+/*
+ * Cleanup function for archive_extract. Mostly, this involves processing
+ * the fixup list, which is used to address a number of problems:
+ * * Dir permissions might prevent us from restoring a file in that
+ * dir, so we restore the dir with minimum 0700 permissions first,
+ * then correct the mode at the end.
+ * * Similarly, the act of restoring a file touches the directory
+ * and changes the timestamp on the dir, so we have to touch-up dir
+ * timestamps at the end as well.
+ * * Some file flags can interfere with the restore by, for example,
+ * preventing the creation of hardlinks to those files.
+ * * Mac OS extended metadata includes ACLs, so must be deferred on dirs.
+ *
+ * Note that tar/cpio do not require that archives be in a particular
+ * order; there is no way to know when the last file has been restored
+ * within a directory, so there's no way to optimize the memory usage
+ * here by fixing up the directory any earlier than the
+ * end-of-archive.
+ *
+ * XXX TODO: Directory ACLs should be restored here, for the same
+ * reason we set directory perms here. XXX
+ */
+static int
+_archive_write_disk_close(struct archive *_a)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ struct fixup_entry *next, *p;
+ struct stat st;
+ char *c;
+ int fd, ret, openflags;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_disk_close");
+ ret = _archive_write_disk_finish_entry(&a->archive);
+
+ /* Sort dir list so directories are fixed up in depth-first order. */
+ p = sort_dir_list(a->fixup_list);
+
+ while (p != NULL) {
+ fd = -1;
+ a->pst = NULL; /* Mark stat cache as out-of-date. */
+
+ /* We must strip trailing slashes from the path to avoid
+ dereferencing symbolic links to directories */
+ c = p->name;
+ while (*c != '\0')
+ c++;
+ while (c != p->name && *(c - 1) == '/') {
+ c--;
+ *c = '\0';
+ }
+
+ if (p->fixup == 0)
+ goto skip_fixup_entry;
+ else {
+ /*
+ * We need to verify if the type of the file
+ * we are going to open matches the file type
+ * of the fixup entry.
+ */
+ openflags = O_BINARY | O_NOFOLLOW | O_RDONLY
+ | O_CLOEXEC;
+#if defined(O_DIRECTORY)
+ if (p->filetype == AE_IFDIR)
+ openflags |= O_DIRECTORY;
+#endif
+ fd = open(p->name, openflags);
+
+#if defined(O_DIRECTORY)
+ /*
+ * If we support O_DIRECTORY and open was
+ * successful we can skip the file type check
+ * for directories. For other file types
+ * we need to verify via fstat() or lstat()
+ */
+ if (fd == -1 || p->filetype != AE_IFDIR) {
+#if HAVE_FSTAT
+ if (fd > 0 && (
+ fstat(fd, &st) != 0 ||
+ la_verify_filetype(st.st_mode,
+ p->filetype) == 0)) {
+ goto skip_fixup_entry;
+ } else
+#endif
+ if (
+#ifdef HAVE_LSTAT
+ lstat(p->name, &st) != 0 ||
+#else
+ la_stat(p->name, &st) != 0 ||
+#endif
+ la_verify_filetype(st.st_mode,
+ p->filetype) == 0) {
+ goto skip_fixup_entry;
+ }
+ }
+#else
+#if HAVE_FSTAT
+ if (fd > 0 && (
+ fstat(fd, &st) != 0 ||
+ la_verify_filetype(st.st_mode,
+ p->filetype) == 0)) {
+ goto skip_fixup_entry;
+ } else
+#endif
+ if (
+#ifdef HAVE_LSTAT
+ lstat(p->name, &st) != 0 ||
+#else
+ la_stat(p->name, &st) != 0 ||
+#endif
+ la_verify_filetype(st.st_mode,
+ p->filetype) == 0) {
+ goto skip_fixup_entry;
+ }
+#endif
+ }
+ if (p->fixup & TODO_TIMES) {
+ set_times(a, fd, p->mode, p->name,
+ p->atime, p->atime_nanos,
+ p->birthtime, p->birthtime_nanos,
+ p->mtime, p->mtime_nanos,
+ p->ctime, p->ctime_nanos);
+ }
+ if (p->fixup & TODO_MODE_BASE) {
+#ifdef HAVE_FCHMOD
+ if (fd >= 0)
+ fchmod(fd, p->mode & 07777);
+ else
+#endif
+#ifdef HAVE_LCHMOD
+ lchmod(p->name, p->mode & 07777);
+#else
+ chmod(p->name, p->mode & 07777);
+#endif
+ }
+ if (p->fixup & TODO_ACLS)
+ archive_write_disk_set_acls(&a->archive, fd,
+ p->name, &p->acl, p->mode);
+ if (p->fixup & TODO_FFLAGS)
+ set_fflags_platform(a, fd, p->name,
+ p->mode, p->fflags_set, 0);
+ if (p->fixup & TODO_MAC_METADATA)
+ set_mac_metadata(a, p->name, p->mac_metadata,
+ p->mac_metadata_size);
+skip_fixup_entry:
+ next = p->next;
+ archive_acl_clear(&p->acl);
+ free(p->mac_metadata);
+ free(p->name);
+ if (fd >= 0)
+ close(fd);
+ free(p);
+ p = next;
+ }
+ a->fixup_list = NULL;
+ return (ret);
+}
+
+static int
+_archive_write_disk_free(struct archive *_a)
+{
+ struct archive_write_disk *a;
+ int ret;
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free");
+ a = (struct archive_write_disk *)_a;
+ ret = _archive_write_disk_close(&a->archive);
+ archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL);
+ archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);
+ archive_entry_free(a->entry);
+ archive_string_free(&a->_name_data);
+ archive_string_free(&a->_tmpname_data);
+ archive_string_free(&a->archive.error_string);
+ archive_string_free(&a->path_safe);
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a->decmpfs_header_p);
+ free(a->resource_fork);
+ free(a->compressed_buffer);
+ free(a->uncompressed_buffer);
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
+ && defined(HAVE_ZLIB_H)
+ if (a->stream_valid) {
+ switch (deflateEnd(&a->stream)) {
+ case Z_OK:
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ }
+#endif
+ free(a);
+ return (ret);
+}
+
+/*
+ * Simple O(n log n) merge sort to order the fixup list. In
+ * particular, we want to restore dir timestamps depth-first.
+ */
+static struct fixup_entry *
+sort_dir_list(struct fixup_entry *p)
+{
+ struct fixup_entry *a, *b, *t;
+
+ if (p == NULL)
+ return (NULL);
+ /* A one-item list is already sorted. */
+ if (p->next == NULL)
+ return (p);
+
+ /* Step 1: split the list. */
+ t = p;
+ a = p->next->next;
+ while (a != NULL) {
+ /* Step a twice, t once. */
+ a = a->next;
+ if (a != NULL)
+ a = a->next;
+ t = t->next;
+ }
+ /* Now, t is at the mid-point, so break the list here. */
+ b = t->next;
+ t->next = NULL;
+ a = p;
+
+ /* Step 2: Recursively sort the two sub-lists. */
+ a = sort_dir_list(a);
+ b = sort_dir_list(b);
+
+ /* Step 3: Merge the returned lists. */
+ /* Pick the first element for the merged list. */
+ if (strcmp(a->name, b->name) > 0) {
+ t = p = a;
+ a = a->next;
+ } else {
+ t = p = b;
+ b = b->next;
+ }
+
+ /* Always put the later element on the list first. */
+ while (a != NULL && b != NULL) {
+ if (strcmp(a->name, b->name) > 0) {
+ t->next = a;
+ a = a->next;
+ } else {
+ t->next = b;
+ b = b->next;
+ }
+ t = t->next;
+ }
+
+ /* Only one list is non-empty, so just splice it on. */
+ if (a != NULL)
+ t->next = a;
+ if (b != NULL)
+ t->next = b;
+
+ return (p);
+}
+
+/*
+ * Returns a new, initialized fixup entry.
+ *
+ * TODO: Reduce the memory requirements for this list by using a tree
+ * structure rather than a simple list of names.
+ */
+static struct fixup_entry *
+new_fixup(struct archive_write_disk *a, const char *pathname)
+{
+ struct fixup_entry *fe;
+
+ fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));
+ if (fe == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a fixup");
+ return (NULL);
+ }
+ fe->next = a->fixup_list;
+ a->fixup_list = fe;
+ fe->fixup = 0;
+ fe->filetype = 0;
+ fe->name = strdup(pathname);
+ return (fe);
+}
+
+/*
+ * Returns a fixup structure for the current entry.
+ */
+static struct fixup_entry *
+current_fixup(struct archive_write_disk *a, const char *pathname)
+{
+ if (a->current_fixup == NULL)
+ a->current_fixup = new_fixup(a, pathname);
+ return (a->current_fixup);
+}
+
+/* Error helper for new *_fsobj functions */
+static void
+fsobj_error(int *a_eno, struct archive_string *a_estr,
+ int err, const char *errstr, const char *path)
+{
+ if (a_eno)
+ *a_eno = err;
+ if (a_estr)
+ archive_string_sprintf(a_estr, "%s%s", errstr, path);
+}
+
+/*
+ * TODO: Someday, integrate this with the deep dir support; they both
+ * scan the path and both can be optimized by comparing against other
+ * recent paths.
+ */
+/*
+ * Checks the given path to see if any elements along it are symlinks. Returns
+ * ARCHIVE_OK if there are none, otherwise puts an error in errmsg.
+ */
+static int
+check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
+ int flags, int checking_linkname)
+{
+#if !defined(HAVE_LSTAT) && \
+ !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT))
+ /* Platform doesn't have lstat, so we can't look for symlinks. */
+ (void)path; /* UNUSED */
+ (void)a_eno; /* UNUSED */
+ (void)a_estr; /* UNUSED */
+ (void)flags; /* UNUSED */
+ (void)checking_linkname; /* UNUSED */
+ return (ARCHIVE_OK);
+#else
+ int res = ARCHIVE_OK;
+ char *tail;
+ char *head;
+ int last;
+ char c = '\0';
+ int r;
+ struct stat st;
+ int chdir_fd;
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ int fd;
+#endif
+
+ /* Nothing to do here if name is empty */
+ if(path[0] == '\0')
+ return (ARCHIVE_OK);
+
+ /*
+ * Guard against symlink tricks. Reject any archive entry whose
+ * destination would be altered by a symlink.
+ *
+ * Walk the filename in chunks separated by '/'. For each segment:
+ * - if it doesn't exist, continue
+ * - if it's symlink, abort or remove it
+ * - if it's a directory and it's not the last chunk, cd into it
+ * As we go:
+ * head points to the current (relative) path
+ * tail points to the temporary \0 terminating the segment we're
+ * currently examining
+ * c holds what used to be in *tail
+ * last is 1 if this is the last tail
+ */
+ chdir_fd = la_opendirat(AT_FDCWD, ".");
+ __archive_ensure_cloexec_flag(chdir_fd);
+ if (chdir_fd < 0) {
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not open ", path);
+ return (ARCHIVE_FATAL);
+ }
+ head = path;
+ tail = path;
+ last = 0;
+ /* TODO: reintroduce a safe cache here? */
+ /* Skip the root directory if the path is absolute. */
+ if(tail == path && tail[0] == '/')
+ ++tail;
+ /* Keep going until we've checked the entire name.
+ * head, tail, path all alias the same string, which is
+ * temporarily zeroed at tail, so be careful restoring the
+ * stashed (c=tail[0]) for error messages.
+ * Exiting the loop with break is okay; continue is not.
+ */
+ while (!last) {
+ /*
+ * Skip the separator we just consumed, plus any adjacent ones
+ */
+ while (*tail == '/')
+ ++tail;
+ /* Skip the next path element. */
+ while (*tail != '\0' && *tail != '/')
+ ++tail;
+ /* is this the last path component? */
+ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0');
+ /* temporarily truncate the string here */
+ c = tail[0];
+ tail[0] = '\0';
+ /* Check that we haven't hit a symlink. */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW);
+#elif defined(HAVE_LSTAT)
+ r = lstat(head, &st);
+#else
+ r = la_stat(head, &st);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ /* We've hit a dir that doesn't exist; stop now. */
+ if (errno == ENOENT) {
+ break;
+ } else {
+ /*
+ * Treat any other error as fatal - best to be
+ * paranoid here.
+ * Note: This effectively disables deep
+ * directory support when security checks are
+ * enabled. Otherwise, very long pathnames that
+ * trigger an error here could evade the
+ * sandbox.
+ * TODO: We could do better, but it would
+ * probably require merging the symlink checks
+ * with the deep-directory editing.
+ */
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not stat ", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ } else if (S_ISDIR(st.st_mode)) {
+ if (!last) {
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ fd = la_opendirat(chdir_fd, head);
+ if (fd < 0)
+ r = -1;
+ else {
+ r = 0;
+ close(chdir_fd);
+ chdir_fd = fd;
+ }
+#else
+ r = chdir(head);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not chdir ", path);
+ res = (ARCHIVE_FATAL);
+ break;
+ }
+ /* Our view is now from inside this dir: */
+ head = tail + 1;
+ }
+ } else if (S_ISLNK(st.st_mode)) {
+ if (last && checking_linkname) {
+#ifdef HAVE_LINKAT
+ /*
+ * Hardlinks to symlinks are safe to write
+ * if linkat() is supported as it does not
+ * follow symlinks.
+ */
+ res = ARCHIVE_OK;
+#else
+ /*
+ * We return ARCHIVE_FAILED here as we are
+ * not able to safely write hardlinks
+ * to symlinks.
+ */
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, errno,
+ "Cannot write hardlink to symlink ",
+ path);
+ res = ARCHIVE_FAILED;
+#endif
+ break;
+ } else
+ if (last) {
+ /*
+ * Last element is symlink; remove it
+ * so we can overwrite it with the
+ * item being extracted.
+ */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ r = unlinkat(chdir_fd, head, 0);
+#else
+ r = unlink(head);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, errno,
+ "Could not remove symlink ",
+ path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ /*
+ * Even if we did remove it, a warning
+ * is in order. The warning is silly,
+ * though, if we're just replacing one
+ * symlink with another symlink.
+ */
+ tail[0] = c;
+ /*
+ * FIXME: not sure how important this is to
+ * restore
+ */
+ /*
+ if (!S_ISLNK(path)) {
+ fsobj_error(a_eno, a_estr, 0,
+ "Removing symlink ", path);
+ }
+ */
+ /* Symlink gone. No more problem! */
+ res = ARCHIVE_OK;
+ break;
+ } else if (flags & ARCHIVE_EXTRACT_UNLINK) {
+ /* User asked us to remove problems. */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ r = unlinkat(chdir_fd, head, 0);
+#else
+ r = unlink(head);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot remove intervening "
+ "symlink ", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ tail[0] = c;
+ } else if ((flags &
+ ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) {
+ /*
+ * We are not the last element and we want to
+ * follow symlinks if they are a directory.
+ *
+ * This is needed to extract hardlinks over
+ * symlinks.
+ */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ r = fstatat(chdir_fd, head, &st, 0);
+#else
+ r = la_stat(head, &st);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ if (errno == ENOENT) {
+ break;
+ } else {
+ fsobj_error(a_eno, a_estr,
+ errno,
+ "Could not stat ", path);
+ res = (ARCHIVE_FAILED);
+ break;
+ }
+ } else if (S_ISDIR(st.st_mode)) {
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ fd = la_opendirat(chdir_fd, head);
+ if (fd < 0)
+ r = -1;
+ else {
+ r = 0;
+ close(chdir_fd);
+ chdir_fd = fd;
+ }
+#else
+ r = chdir(head);
+#endif
+ if (r != 0) {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr,
+ errno,
+ "Could not chdir ", path);
+ res = (ARCHIVE_FATAL);
+ break;
+ }
+ /*
+ * Our view is now from inside
+ * this dir:
+ */
+ head = tail + 1;
+ } else {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot extract through "
+ "symlink ", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ } else {
+ tail[0] = c;
+ fsobj_error(a_eno, a_estr, 0,
+ "Cannot extract through symlink ", path);
+ res = ARCHIVE_FAILED;
+ break;
+ }
+ }
+ /* be sure to always maintain this */
+ tail[0] = c;
+ if (tail[0] != '\0')
+ tail++; /* Advance to the next segment. */
+ }
+ /* Catches loop exits via break */
+ tail[0] = c;
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+ /* If we operate with openat(), fstatat() and unlinkat() there was
+ * no chdir(), so just close the fd */
+ if (chdir_fd >= 0)
+ close(chdir_fd);
+#elif HAVE_FCHDIR
+ /* If we changed directory above, restore it here. */
+ if (chdir_fd >= 0) {
+ r = fchdir(chdir_fd);
+ if (r != 0) {
+ fsobj_error(a_eno, a_estr, errno,
+ "chdir() failure", "");
+ }
+ close(chdir_fd);
+ chdir_fd = -1;
+ if (r != 0) {
+ res = (ARCHIVE_FATAL);
+ }
+ }
+#endif
+ /* TODO: reintroduce a safe cache here? */
+ return res;
+#endif
+}
+
+/*
+ * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise
+ * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED}
+ */
+static int
+check_symlinks(struct archive_write_disk *a)
+{
+ struct archive_string error_string;
+ int error_number;
+ int rc;
+ archive_string_init(&error_string);
+ rc = check_symlinks_fsobj(a->name, &error_number, &error_string,
+ a->flags, 0);
+ if (rc != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ }
+ archive_string_free(&error_string);
+ a->pst = NULL; /* to be safe */
+ return rc;
+}
+
+
+#if defined(__CYGWIN__)
+/*
+ * 1. Convert a path separator from '\' to '/' .
+ * We shouldn't check multibyte character directly because some
+ * character-set have been using the '\' character for a part of
+ * its multibyte character code.
+ * 2. Replace unusable characters in Windows with underscore('_').
+ * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx
+ */
+static void
+cleanup_pathname_win(char *path)
+{
+ wchar_t wc;
+ char *p;
+ size_t alen, l;
+ int mb, complete, utf8;
+
+ alen = 0;
+ mb = 0;
+ complete = 1;
+ utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0;
+ for (p = path; *p != '\0'; p++) {
+ ++alen;
+ if (*p == '\\') {
+ /* If previous byte is smaller than 128,
+ * this is not second byte of multibyte characters,
+ * so we can replace '\' with '/'. */
+ if (utf8 || !mb)
+ *p = '/';
+ else
+ complete = 0;/* uncompleted. */
+ } else if (*(unsigned char *)p > 127)
+ mb = 1;
+ else
+ mb = 0;
+ /* Rewrite the path name if its next character is unusable. */
+ if (*p == ':' || *p == '*' || *p == '?' || *p == '"' ||
+ *p == '<' || *p == '>' || *p == '|')
+ *p = '_';
+ }
+ if (complete)
+ return;
+
+ /*
+ * Convert path separator in wide-character.
+ */
+ p = path;
+ while (*p != '\0' && alen) {
+ l = mbtowc(&wc, p, alen);
+ if (l == (size_t)-1) {
+ while (*p != '\0') {
+ if (*p == '\\')
+ *p = '/';
+ ++p;
+ }
+ break;
+ }
+ if (l == 1 && wc == L'\\')
+ *p = '/';
+ p += l;
+ alen -= l;
+ }
+}
+#endif
+
+/*
+ * Canonicalize the pathname. In particular, this strips duplicate
+ * '/' characters, '.' elements, and trailing '/'. It also raises an
+ * error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is
+ * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS
+ * is set) if the path is absolute.
+ */
+static int
+cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
+ int flags)
+{
+ char *dest, *src;
+ char separator = '\0';
+
+ dest = src = path;
+ if (*src == '\0') {
+ fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
+ "Invalid empty ", "pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+#if defined(__CYGWIN__)
+ cleanup_pathname_win(path);
+#endif
+ /* Skip leading '/'. */
+ if (*src == '/') {
+ if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
+ fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
+ "Path is ", "absolute");
+ return (ARCHIVE_FAILED);
+ }
+
+ separator = *src++;
+ }
+
+ /* Scan the pathname one element at a time. */
+ for (;;) {
+ /* src points to first char after '/' */
+ if (src[0] == '\0') {
+ break;
+ } else if (src[0] == '/') {
+ /* Found '//', ignore second one. */
+ src++;
+ continue;
+ } else if (src[0] == '.') {
+ if (src[1] == '\0') {
+ /* Ignore trailing '.' */
+ break;
+ } else if (src[1] == '/') {
+ /* Skip './'. */
+ src += 2;
+ continue;
+ } else if (src[1] == '.') {
+ if (src[2] == '/' || src[2] == '\0') {
+ /* Conditionally warn about '..' */
+ if (flags
+ & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+ fsobj_error(a_eno, a_estr,
+ ARCHIVE_ERRNO_MISC,
+ "Path contains ", "'..'");
+ return (ARCHIVE_FAILED);
+ }
+ }
+ /*
+ * Note: Under no circumstances do we
+ * remove '..' elements. In
+ * particular, restoring
+ * '/foo/../bar/' should create the
+ * 'foo' dir as a side-effect.
+ */
+ }
+ }
+
+ /* Copy current element, including leading '/'. */
+ if (separator)
+ *dest++ = '/';
+ while (*src != '\0' && *src != '/') {
+ *dest++ = *src++;
+ }
+
+ if (*src == '\0')
+ break;
+
+ /* Skip '/' separator. */
+ separator = *src++;
+ }
+ /*
+ * We've just copied zero or more path elements, not including the
+ * final '/'.
+ */
+ if (dest == path) {
+ /*
+ * Nothing got copied. The path must have been something
+ * like '.' or '/' or './' or '/././././/./'.
+ */
+ if (separator)
+ *dest++ = '/';
+ else
+ *dest++ = '.';
+ }
+ /* Terminate the result. */
+ *dest = '\0';
+ return (ARCHIVE_OK);
+}
+
+static int
+cleanup_pathname(struct archive_write_disk *a)
+{
+ struct archive_string error_string;
+ int error_number;
+ int rc;
+ archive_string_init(&error_string);
+ rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string,
+ a->flags);
+ if (rc != ARCHIVE_OK) {
+ archive_set_error(&a->archive, error_number, "%s",
+ error_string.s);
+ }
+ archive_string_free(&error_string);
+ return rc;
+}
+
+/*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+ */
+static int
+create_parent_dir(struct archive_write_disk *a, char *path)
+{
+ char *slash;
+ int r;
+
+ /* Remove tail element to obtain parent name. */
+ slash = strrchr(path, '/');
+ if (slash == NULL)
+ return (ARCHIVE_OK);
+ *slash = '\0';
+ r = create_dir(a, path);
+ *slash = '/';
+ return (r);
+}
+
+/*
+ * Create the specified dir, recursing to create parents as necessary.
+ *
+ * Returns ARCHIVE_OK if the path exists when we're done here.
+ * Otherwise, returns ARCHIVE_FAILED.
+ * Assumes path is in mutable storage; path is unchanged on exit.
+ */
+static int
+create_dir(struct archive_write_disk *a, char *path)
+{
+ struct stat st;
+ struct fixup_entry *le;
+ char *slash, *base;
+ mode_t mode_final, mode;
+ int r;
+
+ /* Check for special names and just skip them. */
+ slash = strrchr(path, '/');
+ if (slash == NULL)
+ base = path;
+ else
+ base = slash + 1;
+
+ if (base[0] == '\0' ||
+ (base[0] == '.' && base[1] == '\0') ||
+ (base[0] == '.' && base[1] == '.' && base[2] == '\0')) {
+ /* Don't bother trying to create null path, '.', or '..'. */
+ if (slash != NULL) {
+ *slash = '\0';
+ r = create_dir(a, path);
+ *slash = '/';
+ return (r);
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Yes, this should be stat() and not lstat(). Using lstat()
+ * here loses the ability to extract through symlinks. Also note
+ * that this should not use the a->st cache.
+ */
+ if (la_stat(path, &st) == 0) {
+ if (S_ISDIR(st.st_mode))
+ return (ARCHIVE_OK);
+ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ archive_set_error(&a->archive, EEXIST,
+ "Can't create directory '%s'", path);
+ return (ARCHIVE_FAILED);
+ }
+ if (unlink(path) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't create directory '%s': "
+ "Conflicting file cannot be removed",
+ path);
+ return (ARCHIVE_FAILED);
+ }
+ } else if (errno != ENOENT && errno != ENOTDIR) {
+ /* Stat failed? */
+ archive_set_error(&a->archive, errno,
+ "Can't test directory '%s'", path);
+ return (ARCHIVE_FAILED);
+ } else if (slash != NULL) {
+ *slash = '\0';
+ r = create_dir(a, path);
+ *slash = '/';
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ /*
+ * Mode we want for the final restored directory. Per POSIX,
+ * implicitly-created dirs must be created obeying the umask.
+ * There's no mention whether this is different for privileged
+ * restores (which the rest of this code handles by pretending
+ * umask=0). I've chosen here to always obey the user's umask for
+ * implicit dirs, even if _EXTRACT_PERM was specified.
+ */
+ mode_final = DEFAULT_DIR_MODE & ~a->user_umask;
+ /* Mode we want on disk during the restore process. */
+ mode = mode_final;
+ mode |= MINIMUM_DIR_MODE;
+ mode &= MAXIMUM_DIR_MODE;
+ if (mkdir(path, mode) == 0) {
+ if (mode != mode_final) {
+ le = new_fixup(a, path);
+ if (le == NULL)
+ return (ARCHIVE_FATAL);
+ le->fixup |=TODO_MODE_BASE;
+ le->mode = mode_final;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Without the following check, a/b/../b/c/d fails at the
+ * second visit to 'b', so 'd' can't be created. Note that we
+ * don't add it to the fixup list here, as it's already been
+ * added.
+ */
+ if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+ return (ARCHIVE_OK);
+
+ archive_set_error(&a->archive, errno, "Failed to create dir '%s'",
+ path);
+ return (ARCHIVE_FAILED);
+}
+
+/*
+ * Note: Although we can skip setting the user id if the desired user
+ * id matches the current user, we cannot skip setting the group, as
+ * many systems set the gid based on the containing directory. So
+ * we have to perform a chown syscall if we want to set the SGID
+ * bit. (The alternative is to stat() and then possibly chown(); it's
+ * more efficient to skip the stat() and just always chown().) Note
+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which
+ * allows set_mode to skip the stat() check for the GID.
+ */
+static int
+set_ownership(struct archive_write_disk *a)
+{
+#if !defined(__CYGWIN__) && !defined(__linux__)
+/*
+ * On Linux, a process may have the CAP_CHOWN capability.
+ * On Windows there is no 'root' user with uid 0.
+ * Elsewhere we can skip calling chown if we are not root and the desired
+ * user id does not match the current user.
+ */
+ if (a->user_uid != 0 && a->user_uid != a->uid) {
+ archive_set_error(&a->archive, errno,
+ "Can't set UID=%jd", (intmax_t)a->uid);
+ return (ARCHIVE_WARN);
+ }
+#endif
+
+#ifdef HAVE_FCHOWN
+ /* If we have an fd, we can avoid a race. */
+ if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
+#endif
+
+ /* We prefer lchown() but will use chown() if that's all we have. */
+ /* Of course, if we have neither, this will always fail. */
+#ifdef HAVE_LCHOWN
+ if (lchown(a->name, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
+#elif HAVE_CHOWN
+ if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
+#endif
+
+ archive_set_error(&a->archive, errno,
+ "Can't set user=%jd/group=%jd for %s",
+ (intmax_t)a->uid, (intmax_t)a->gid, a->name);
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Note: Returns 0 on success, non-zero on failure.
+ */
+static int
+set_time(int fd, int mode, const char *name,
+ time_t atime, long atime_nsec,
+ time_t mtime, long mtime_nsec)
+{
+ /* Select the best implementation for this platform. */
+#if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS)
+ /*
+ * utimensat() and futimens() are defined in
+ * POSIX.1-2008. They support ns resolution and setting times
+ * on fds and symlinks.
+ */
+ struct timespec ts[2];
+ (void)mode; /* UNUSED */
+ ts[0].tv_sec = atime;
+ ts[0].tv_nsec = atime_nsec;
+ ts[1].tv_sec = mtime;
+ ts[1].tv_nsec = mtime_nsec;
+ if (fd >= 0)
+ return futimens(fd, ts);
+ return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW);
+
+#elif HAVE_UTIMES
+ /*
+ * The utimes()-family functions support µs-resolution and
+ * setting times fds and symlinks. utimes() is documented as
+ * LEGACY by POSIX, futimes() and lutimes() are not described
+ * in POSIX.
+ */
+ struct timeval times[2];
+
+ times[0].tv_sec = atime;
+ times[0].tv_usec = atime_nsec / 1000;
+ times[1].tv_sec = mtime;
+ times[1].tv_usec = mtime_nsec / 1000;
+
+#ifdef HAVE_FUTIMES
+ if (fd >= 0)
+ return (futimes(fd, times));
+#else
+ (void)fd; /* UNUSED */
+#endif
+#ifdef HAVE_LUTIMES
+ (void)mode; /* UNUSED */
+ return (lutimes(name, times));
+#else
+ if (S_ISLNK(mode))
+ return (0);
+ return (utimes(name, times));
+#endif
+
+#elif defined(HAVE_UTIME)
+ /*
+ * utime() is POSIX-standard but only supports 1s resolution and
+ * does not support fds or symlinks.
+ */
+ struct utimbuf times;
+ (void)fd; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)atime_nsec; /* UNUSED */
+ (void)mtime_nsec; /* UNUSED */
+ times.actime = atime;
+ times.modtime = mtime;
+ if (S_ISLNK(mode))
+ return (ARCHIVE_OK);
+ return (utime(name, &times));
+
+#else
+ /*
+ * We don't know how to set the time on this platform.
+ */
+ (void)fd; /* UNUSED */
+ (void)mode; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)atime; /* UNUSED */
+ (void)atime_nsec; /* UNUSED */
+ (void)mtime; /* UNUSED */
+ (void)mtime_nsec; /* UNUSED */
+ return (ARCHIVE_WARN);
+#endif
+}
+
+#ifdef F_SETTIMES
+static int
+set_time_tru64(int fd, int mode, const char *name,
+ time_t atime, long atime_nsec,
+ time_t mtime, long mtime_nsec,
+ time_t ctime, long ctime_nsec)
+{
+ struct attr_timbuf tstamp;
+ tstamp.atime.tv_sec = atime;
+ tstamp.mtime.tv_sec = mtime;
+ tstamp.ctime.tv_sec = ctime;
+#if defined (__hpux) && defined (__ia64)
+ tstamp.atime.tv_nsec = atime_nsec;
+ tstamp.mtime.tv_nsec = mtime_nsec;
+ tstamp.ctime.tv_nsec = ctime_nsec;
+#else
+ tstamp.atime.tv_usec = atime_nsec / 1000;
+ tstamp.mtime.tv_usec = mtime_nsec / 1000;
+ tstamp.ctime.tv_usec = ctime_nsec / 1000;
+#endif
+ return (fcntl(fd,F_SETTIMES,&tstamp));
+}
+#endif /* F_SETTIMES */
+
+static int
+set_times(struct archive_write_disk *a,
+ int fd, int mode, const char *name,
+ time_t atime, long atime_nanos,
+ time_t birthtime, long birthtime_nanos,
+ time_t mtime, long mtime_nanos,
+ time_t cctime, long ctime_nanos)
+{
+ /* Note: set_time doesn't use libarchive return conventions!
+ * It uses syscall conventions. So 0 here instead of ARCHIVE_OK. */
+ int r1 = 0, r2 = 0;
+
+#ifdef F_SETTIMES
+ /*
+ * on Tru64 try own fcntl first which can restore even the
+ * ctime, fall back to default code path below if it fails
+ * or if we are not running as root
+ */
+ if (a->user_uid == 0 &&
+ set_time_tru64(fd, mode, name,
+ atime, atime_nanos, mtime,
+ mtime_nanos, cctime, ctime_nanos) == 0) {
+ return (ARCHIVE_OK);
+ }
+#else /* Tru64 */
+ (void)cctime; /* UNUSED */
+ (void)ctime_nanos; /* UNUSED */
+#endif /* Tru64 */
+
+#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
+ /*
+ * If you have struct stat.st_birthtime, we assume BSD
+ * birthtime semantics, in which {f,l,}utimes() updates
+ * birthtime to earliest mtime. So we set the time twice,
+ * first using the birthtime, then using the mtime. If
+ * birthtime == mtime, this isn't necessary, so we skip it.
+ * If birthtime > mtime, then this won't work, so we skip it.
+ */
+ if (birthtime < mtime
+ || (birthtime == mtime && birthtime_nanos < mtime_nanos))
+ r1 = set_time(fd, mode, name,
+ atime, atime_nanos,
+ birthtime, birthtime_nanos);
+#else
+ (void)birthtime; /* UNUSED */
+ (void)birthtime_nanos; /* UNUSED */
+#endif
+ r2 = set_time(fd, mode, name,
+ atime, atime_nanos,
+ mtime, mtime_nanos);
+ if (r1 != 0 || r2 != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't restore time");
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+set_times_from_entry(struct archive_write_disk *a)
+{
+ time_t atime, birthtime, mtime, cctime;
+ long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;
+
+ /* Suitable defaults. */
+ atime = birthtime = mtime = cctime = a->start_time;
+ atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;
+
+ /* If no time was provided, we're done. */
+ if (!archive_entry_atime_is_set(a->entry)
+#if HAVE_STRUCT_STAT_ST_BIRTHTIME
+ && !archive_entry_birthtime_is_set(a->entry)
+#endif
+ && !archive_entry_mtime_is_set(a->entry))
+ return (ARCHIVE_OK);
+
+ if (archive_entry_atime_is_set(a->entry)) {
+ atime = archive_entry_atime(a->entry);
+ atime_nsec = archive_entry_atime_nsec(a->entry);
+ }
+ if (archive_entry_birthtime_is_set(a->entry)) {
+ birthtime = archive_entry_birthtime(a->entry);
+ birthtime_nsec = archive_entry_birthtime_nsec(a->entry);
+ }
+ if (archive_entry_mtime_is_set(a->entry)) {
+ mtime = archive_entry_mtime(a->entry);
+ mtime_nsec = archive_entry_mtime_nsec(a->entry);
+ }
+ if (archive_entry_ctime_is_set(a->entry)) {
+ cctime = archive_entry_ctime(a->entry);
+ ctime_nsec = archive_entry_ctime_nsec(a->entry);
+ }
+
+ return set_times(a, a->fd, a->mode, a->name,
+ atime, atime_nsec,
+ birthtime, birthtime_nsec,
+ mtime, mtime_nsec,
+ cctime, ctime_nsec);
+}
+
+static int
+set_mode(struct archive_write_disk *a, int mode)
+{
+ int r = ARCHIVE_OK;
+ int r2;
+ mode &= 07777; /* Strip off file type bits. */
+
+ if (a->todo & TODO_SGID_CHECK) {
+ /*
+ * If we don't know the GID is right, we must stat()
+ * to verify it. We can't just check the GID of this
+ * process, since systems sometimes set GID from
+ * the enclosing dir or based on ACLs.
+ */
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+ if (a->pst->st_gid != a->gid) {
+ mode &= ~ S_ISGID;
+ if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+ /*
+ * This is only an error if you
+ * requested owner restore. If you
+ * didn't, we'll try to restore
+ * sgid/suid, but won't consider it a
+ * problem if we can't.
+ */
+ archive_set_error(&a->archive, -1,
+ "Can't restore SGID bit");
+ r = ARCHIVE_WARN;
+ }
+ }
+ /* While we're here, double-check the UID. */
+ if (a->pst->st_uid != a->uid
+ && (a->todo & TODO_SUID)) {
+ mode &= ~ S_ISUID;
+ if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+ archive_set_error(&a->archive, -1,
+ "Can't restore SUID bit");
+ r = ARCHIVE_WARN;
+ }
+ }
+ a->todo &= ~TODO_SGID_CHECK;
+ a->todo &= ~TODO_SUID_CHECK;
+ } else if (a->todo & TODO_SUID_CHECK) {
+ /*
+ * If we don't know the UID is right, we can just check
+ * the user, since all systems set the file UID from
+ * the process UID.
+ */
+ if (a->user_uid != a->uid) {
+ mode &= ~ S_ISUID;
+ if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+ archive_set_error(&a->archive, -1,
+ "Can't make file SUID");
+ r = ARCHIVE_WARN;
+ }
+ }
+ a->todo &= ~TODO_SUID_CHECK;
+ }
+
+ if (S_ISLNK(a->mode)) {
+#ifdef HAVE_LCHMOD
+ /*
+ * If this is a symlink, use lchmod(). If the
+ * platform doesn't support lchmod(), just skip it. A
+ * platform that doesn't provide a way to set
+ * permissions on symlinks probably ignores
+ * permissions on symlinks, so a failure here has no
+ * impact.
+ */
+ if (lchmod(a->name, mode) != 0) {
+ switch (errno) {
+ case ENOTSUP:
+ case ENOSYS:
+#if ENOTSUP != EOPNOTSUPP
+ case EOPNOTSUPP:
+#endif
+ /*
+ * if lchmod is defined but the platform
+ * doesn't support it, silently ignore
+ * error
+ */
+ break;
+ default:
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+ }
+#endif
+ } else if (!S_ISDIR(a->mode)) {
+ /*
+ * If it's not a symlink and not a dir, then use
+ * fchmod() or chmod(), depending on whether we have
+ * an fd. Dirs get their perms set during the
+ * post-extract fixup, which is handled elsewhere.
+ */
+#ifdef HAVE_FCHMOD
+ if (a->fd >= 0)
+ r2 = fchmod(a->fd, mode);
+ else
+#endif
+ /* If this platform lacks fchmod(), then
+ * we'll just use chmod(). */
+ r2 = chmod(a->name, mode);
+
+ if (r2 != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+ }
+ return (r);
+}
+
+static int
+set_fflags(struct archive_write_disk *a)
+{
+ struct fixup_entry *le;
+ unsigned long set, clear;
+ int r;
+ mode_t mode = archive_entry_mode(a->entry);
+ /*
+ * Make 'critical_flags' hold all file flags that can't be
+ * immediately restored. For example, on BSD systems,
+ * SF_IMMUTABLE prevents hardlinks from being created, so
+ * should not be set until after any hardlinks are created. To
+ * preserve some semblance of portability, this uses #ifdef
+ * extensively. Ugly, but it works.
+ *
+ * Yes, Virginia, this does create a security race. It's mitigated
+ * somewhat by the practice of creating dirs 0700 until the extract
+ * is done, but it would be nice if we could do more than that.
+ * People restoring critical file systems should be wary of
+ * other programs that might try to muck with files as they're
+ * being restored.
+ */
+ const int critical_flags = 0
+#ifdef SF_IMMUTABLE
+ | SF_IMMUTABLE
+#endif
+#ifdef UF_IMMUTABLE
+ | UF_IMMUTABLE
+#endif
+#ifdef SF_APPEND
+ | SF_APPEND
+#endif
+#ifdef UF_APPEND
+ | UF_APPEND
+#endif
+#if defined(FS_APPEND_FL)
+ | FS_APPEND_FL
+#elif defined(EXT2_APPEND_FL)
+ | EXT2_APPEND_FL
+#endif
+#if defined(FS_IMMUTABLE_FL)
+ | FS_IMMUTABLE_FL
+#elif defined(EXT2_IMMUTABLE_FL)
+ | EXT2_IMMUTABLE_FL
+#endif
+#ifdef FS_JOURNAL_DATA_FL
+ | FS_JOURNAL_DATA_FL
+#endif
+ ;
+
+ if (a->todo & TODO_FFLAGS) {
+ archive_entry_fflags(a->entry, &set, &clear);
+
+ /*
+ * The first test encourages the compiler to eliminate
+ * all of this if it's not necessary.
+ */
+ if ((critical_flags != 0) && (set & critical_flags)) {
+ le = current_fixup(a, a->name);
+ if (le == NULL)
+ return (ARCHIVE_FATAL);
+ le->filetype = archive_entry_filetype(a->entry);
+ le->fixup |= TODO_FFLAGS;
+ le->fflags_set = set;
+ /* Store the mode if it's not already there. */
+ if ((le->fixup & TODO_MODE) == 0)
+ le->mode = mode;
+ } else {
+ r = set_fflags_platform(a, a->fd,
+ a->name, mode, set, clear);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+clear_nochange_fflags(struct archive_write_disk *a)
+{
+ mode_t mode = archive_entry_mode(a->entry);
+ const int nochange_flags = 0
+#ifdef SF_IMMUTABLE
+ | SF_IMMUTABLE
+#endif
+#ifdef UF_IMMUTABLE
+ | UF_IMMUTABLE
+#endif
+#ifdef SF_APPEND
+ | SF_APPEND
+#endif
+#ifdef UF_APPEND
+ | UF_APPEND
+#endif
+#ifdef EXT2_APPEND_FL
+ | EXT2_APPEND_FL
+#endif
+#ifdef EXT2_IMMUTABLE_FL
+ | EXT2_IMMUTABLE_FL
+#endif
+ ;
+
+ return (set_fflags_platform(a, a->fd, a->name, mode, 0,
+ nochange_flags));
+}
+
+
+#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS)
+/*
+ * BSD reads flags using stat() and sets them with one of {f,l,}chflags()
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+ mode_t mode, unsigned long set, unsigned long clear)
+{
+ int r;
+ const int sf_mask = 0
+#ifdef SF_APPEND
+ | SF_APPEND
+#endif
+#ifdef SF_ARCHIVED
+ | SF_ARCHIVED
+#endif
+#ifdef SF_IMMUTABLE
+ | SF_IMMUTABLE
+#endif
+#ifdef SF_NOUNLINK
+ | SF_NOUNLINK
+#endif
+ ;
+ (void)mode; /* UNUSED */
+
+ if (set == 0 && clear == 0)
+ return (ARCHIVE_OK);
+
+ /*
+ * XXX Is the stat here really necessary? Or can I just use
+ * the 'set' flags directly? In particular, I'm not sure
+ * about the correct approach if we're overwriting an existing
+ * file that already has flags on it. XXX
+ */
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+
+ a->st.st_flags &= ~clear;
+ a->st.st_flags |= set;
+
+ /* Only super-user may change SF_* flags */
+
+ if (a->user_uid != 0)
+ a->st.st_flags &= ~sf_mask;
+
+#ifdef HAVE_FCHFLAGS
+ /* If platform has fchflags() and we were given an fd, use it. */
+ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0)
+ return (ARCHIVE_OK);
+#endif
+ /*
+ * If we can't use the fd to set the flags, we'll use the
+ * pathname to set flags. We prefer lchflags() but will use
+ * chflags() if we must.
+ */
+#ifdef HAVE_LCHFLAGS
+ if (lchflags(name, a->st.st_flags) == 0)
+ return (ARCHIVE_OK);
+#elif defined(HAVE_CHFLAGS)
+ if (S_ISLNK(a->st.st_mode)) {
+ archive_set_error(&a->archive, errno,
+ "Can't set file flags on symlink.");
+ return (ARCHIVE_WARN);
+ }
+ if (chflags(name, a->st.st_flags) == 0)
+ return (ARCHIVE_OK);
+#endif
+ archive_set_error(&a->archive, errno,
+ "Failed to set file flags");
+ return (ARCHIVE_WARN);
+}
+
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \
+ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \
+ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+/*
+ * Linux uses ioctl() to read and write file flags.
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+ mode_t mode, unsigned long set, unsigned long clear)
+{
+ int ret;
+ int myfd = fd;
+ int newflags, oldflags;
+ /*
+ * Linux has no define for the flags that are only settable by
+ * the root user. This code may seem a little complex, but
+ * there seem to be some Linux systems that lack these
+ * defines. (?) The code below degrades reasonably gracefully
+ * if sf_mask is incomplete.
+ */
+ const int sf_mask = 0
+#if defined(FS_IMMUTABLE_FL)
+ | FS_IMMUTABLE_FL
+#elif defined(EXT2_IMMUTABLE_FL)
+ | EXT2_IMMUTABLE_FL
+#endif
+#if defined(FS_APPEND_FL)
+ | FS_APPEND_FL
+#elif defined(EXT2_APPEND_FL)
+ | EXT2_APPEND_FL
+#endif
+#if defined(FS_JOURNAL_DATA_FL)
+ | FS_JOURNAL_DATA_FL
+#endif
+ ;
+
+ if (set == 0 && clear == 0)
+ return (ARCHIVE_OK);
+ /* Only regular files and dirs can have flags. */
+ if (!S_ISREG(mode) && !S_ISDIR(mode))
+ return (ARCHIVE_OK);
+
+ /* If we weren't given an fd, open it ourselves. */
+ if (myfd < 0) {
+ myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY |
+ O_CLOEXEC | O_NOFOLLOW);
+ __archive_ensure_cloexec_flag(myfd);
+ }
+ if (myfd < 0)
+ return (ARCHIVE_OK);
+
+ /*
+ * XXX As above, this would be way simpler if we didn't have
+ * to read the current flags from disk. XXX
+ */
+ ret = ARCHIVE_OK;
+
+ /* Read the current file flags. */
+ if (ioctl(myfd,
+#ifdef FS_IOC_GETFLAGS
+ FS_IOC_GETFLAGS,
+#else
+ EXT2_IOC_GETFLAGS,
+#endif
+ &oldflags) < 0)
+ goto fail;
+
+ /* Try setting the flags as given. */
+ newflags = (oldflags & ~clear) | set;
+ if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+ FS_IOC_SETFLAGS,
+#else
+ EXT2_IOC_SETFLAGS,
+#endif
+ &newflags) >= 0)
+ goto cleanup;
+ if (errno != EPERM)
+ goto fail;
+
+ /* If we couldn't set all the flags, try again with a subset. */
+ newflags &= ~sf_mask;
+ oldflags &= sf_mask;
+ newflags |= oldflags;
+ if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+ FS_IOC_SETFLAGS,
+#else
+ EXT2_IOC_SETFLAGS,
+#endif
+ &newflags) >= 0)
+ goto cleanup;
+
+ /* We couldn't set the flags, so report the failure. */
+fail:
+ archive_set_error(&a->archive, errno,
+ "Failed to set file flags");
+ ret = ARCHIVE_WARN;
+cleanup:
+ if (fd < 0)
+ close(myfd);
+ return (ret);
+}
+
+#else
+
+/*
+ * Of course, some systems have neither BSD chflags() nor Linux' flags
+ * support through ioctl().
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+ mode_t mode, unsigned long set, unsigned long clear)
+{
+ (void)a; /* UNUSED */
+ (void)fd; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)mode; /* UNUSED */
+ (void)set; /* UNUSED */
+ (void)clear; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif /* __linux */
+
+#ifndef HAVE_COPYFILE_H
+/* Default is to simply drop Mac extended metadata. */
+static int
+set_mac_metadata(struct archive_write_disk *a, const char *pathname,
+ const void *metadata, size_t metadata_size)
+{
+ (void)a; /* UNUSED */
+ (void)pathname; /* UNUSED */
+ (void)metadata; /* UNUSED */
+ (void)metadata_size; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+static int
+fixup_appledouble(struct archive_write_disk *a, const char *pathname)
+{
+ (void)a; /* UNUSED */
+ (void)pathname; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#else
+
+/*
+ * On Mac OS, we use copyfile() to unpack the metadata and
+ * apply it to the target file.
+ */
+
+#if defined(HAVE_SYS_XATTR_H)
+static int
+copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd)
+{
+ ssize_t xattr_size;
+ char *xattr_names = NULL, *xattr_val = NULL;
+ int ret = ARCHIVE_OK, xattr_i;
+
+ xattr_size = flistxattr(tmpfd, NULL, 0, 0);
+ if (xattr_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ xattr_names = malloc(xattr_size);
+ if (xattr_names == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for metadata(xattr)");
+ ret = ARCHIVE_FATAL;
+ goto exit_xattr;
+ }
+ xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0);
+ if (xattr_size == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ for (xattr_i = 0; xattr_i < xattr_size;
+ xattr_i += strlen(xattr_names + xattr_i) + 1) {
+ char *xattr_val_saved;
+ ssize_t s;
+ int f;
+
+ s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ xattr_val_saved = xattr_val;
+ xattr_val = realloc(xattr_val, s);
+ if (xattr_val == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ free(xattr_val_saved);
+ goto exit_xattr;
+ }
+ s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0);
+ if (f == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(xattr)");
+ ret = ARCHIVE_WARN;
+ goto exit_xattr;
+ }
+ }
+exit_xattr:
+ free(xattr_names);
+ free(xattr_val);
+ return (ret);
+}
+#endif
+
+static int
+copy_acls(struct archive_write_disk *a, int tmpfd, int dffd)
+{
+#ifndef HAVE_SYS_ACL_H
+ return 0;
+#else
+ acl_t acl, dfacl = NULL;
+ int acl_r, ret = ARCHIVE_OK;
+
+ acl = acl_get_fd(tmpfd);
+ if (acl == NULL) {
+ if (errno == ENOENT)
+ /* There are not any ACLs. */
+ return (ret);
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(acl)");
+ ret = ARCHIVE_WARN;
+ goto exit_acl;
+ }
+ dfacl = acl_dup(acl);
+ acl_r = acl_set_fd(dffd, dfacl);
+ if (acl_r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get metadata(acl)");
+ ret = ARCHIVE_WARN;
+ goto exit_acl;
+ }
+exit_acl:
+ if (acl)
+ acl_free(acl);
+ if (dfacl)
+ acl_free(dfacl);
+ return (ret);
+#endif
+}
+
+static int
+create_tempdatafork(struct archive_write_disk *a, const char *pathname)
+{
+ struct archive_string tmpdatafork;
+ int tmpfd;
+
+ archive_string_init(&tmpdatafork);
+ archive_strcpy(&tmpdatafork, "tar.md.XXXXXX");
+ tmpfd = mkstemp(tmpdatafork.s);
+ if (tmpfd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to mkstemp");
+ archive_string_free(&tmpdatafork);
+ return (-1);
+ }
+ if (copyfile(pathname, tmpdatafork.s, 0,
+ COPYFILE_UNPACK | COPYFILE_NOFOLLOW
+ | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ close(tmpfd);
+ tmpfd = -1;
+ }
+ unlink(tmpdatafork.s);
+ archive_string_free(&tmpdatafork);
+ return (tmpfd);
+}
+
+static int
+copy_metadata(struct archive_write_disk *a, const char *metadata,
+ const char *datafork, int datafork_compressed)
+{
+ int ret = ARCHIVE_OK;
+
+ if (datafork_compressed) {
+ int dffd, tmpfd;
+
+ tmpfd = create_tempdatafork(a, metadata);
+ if (tmpfd == -1)
+ return (ARCHIVE_WARN);
+
+ /*
+ * Do not open the data fork compressed by HFS+ compression
+ * with at least a writing mode(O_RDWR or O_WRONLY). it
+ * makes the data fork uncompressed.
+ */
+ dffd = open(datafork, 0);
+ if (dffd == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to open the data fork for metadata");
+ close(tmpfd);
+ return (ARCHIVE_WARN);
+ }
+
+#if defined(HAVE_SYS_XATTR_H)
+ ret = copy_xattrs(a, tmpfd, dffd);
+ if (ret == ARCHIVE_OK)
+#endif
+ ret = copy_acls(a, tmpfd, dffd);
+ close(tmpfd);
+ close(dffd);
+ } else {
+ if (copyfile(metadata, datafork, 0,
+ COPYFILE_UNPACK | COPYFILE_NOFOLLOW
+ | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ ret = ARCHIVE_WARN;
+ }
+ }
+ return (ret);
+}
+
+static int
+set_mac_metadata(struct archive_write_disk *a, const char *pathname,
+ const void *metadata, size_t metadata_size)
+{
+ struct archive_string tmp;
+ ssize_t written;
+ int fd;
+ int ret = ARCHIVE_OK;
+
+ /* This would be simpler if copyfile() could just accept the
+ * metadata as a block of memory; then we could sidestep this
+ * silly dance of writing the data to disk just so that
+ * copyfile() can read it back in again. */
+ archive_string_init(&tmp);
+ archive_strcpy(&tmp, pathname);
+ archive_strcat(&tmp, ".XXXXXX");
+ fd = mkstemp(tmp.s);
+
+ if (fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ archive_string_free(&tmp);
+ return (ARCHIVE_WARN);
+ }
+ written = write(fd, metadata, metadata_size);
+ close(fd);
+ if ((size_t)written != metadata_size) {
+ archive_set_error(&a->archive, errno,
+ "Failed to restore metadata");
+ ret = ARCHIVE_WARN;
+ } else {
+ int compressed;
+
+#if defined(UF_COMPRESSED)
+ if ((a->todo & TODO_HFS_COMPRESSION) != 0 &&
+ (ret = lazy_stat(a)) == ARCHIVE_OK)
+ compressed = a->st.st_flags & UF_COMPRESSED;
+ else
+#endif
+ compressed = 0;
+ ret = copy_metadata(a, tmp.s, pathname, compressed);
+ }
+ unlink(tmp.s);
+ archive_string_free(&tmp);
+ return (ret);
+}
+
+static int
+fixup_appledouble(struct archive_write_disk *a, const char *pathname)
+{
+ char buff[8];
+ struct stat st;
+ const char *p;
+ struct archive_string datafork;
+ int fd = -1, ret = ARCHIVE_OK;
+
+ archive_string_init(&datafork);
+ /* Check if the current file name is a type of the resource
+ * fork file. */
+ p = strrchr(pathname, '/');
+ if (p == NULL)
+ p = pathname;
+ else
+ p++;
+ if (p[0] != '.' || p[1] != '_')
+ goto skip_appledouble;
+
+ /*
+ * Check if the data fork file exists.
+ *
+ * TODO: Check if this write disk object has handled it.
+ */
+ archive_strncpy(&datafork, pathname, p - pathname);
+ archive_strcat(&datafork, p + 2);
+ if (
+#ifdef HAVE_LSTAT
+ lstat(datafork.s, &st) == -1 ||
+#else
+ la_stat(datafork.s, &st) == -1 ||
+#endif
+ (st.st_mode & AE_IFMT) != AE_IFREG)
+ goto skip_appledouble;
+
+ /*
+ * Check if the file is in the AppleDouble form.
+ */
+ fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC);
+ __archive_ensure_cloexec_flag(fd);
+ if (fd == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to open a restoring file");
+ ret = ARCHIVE_WARN;
+ goto skip_appledouble;
+ }
+ if (read(fd, buff, 8) == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read a restoring file");
+ close(fd);
+ ret = ARCHIVE_WARN;
+ goto skip_appledouble;
+ }
+ close(fd);
+ /* Check AppleDouble Magic Code. */
+ if (archive_be32dec(buff) != 0x00051607)
+ goto skip_appledouble;
+ /* Check AppleDouble Version. */
+ if (archive_be32dec(buff+4) != 0x00020000)
+ goto skip_appledouble;
+
+ ret = copy_metadata(a, pathname, datafork.s,
+#if defined(UF_COMPRESSED)
+ st.st_flags & UF_COMPRESSED);
+#else
+ 0);
+#endif
+ if (ret == ARCHIVE_OK) {
+ unlink(pathname);
+ ret = ARCHIVE_EOF;
+ }
+skip_appledouble:
+ archive_string_free(&datafork);
+ return (ret);
+}
+#endif
+
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
+/*
+ * Restore extended attributes - Linux, Darwin and AIX implementations:
+ * AIX' ea interface is syntaxwise identical to the Linux xattr interface.
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ struct archive_entry *entry = a->entry;
+ struct archive_string errlist;
+ int ret = ARCHIVE_OK;
+ int i = archive_entry_xattr_reset(entry);
+ short fail = 0;
+
+ archive_string_init(&errlist);
+
+ while (i--) {
+ const char *name;
+ const void *value;
+ size_t size;
+ int e;
+
+ archive_entry_xattr_next(entry, &name, &value, &size);
+
+ if (name == NULL)
+ continue;
+#if ARCHIVE_XATTR_LINUX
+ /* Linux: quietly skip POSIX.1e ACL extended attributes */
+ if (strncmp(name, "system.", 7) == 0 &&
+ (strcmp(name + 7, "posix_acl_access") == 0 ||
+ strcmp(name + 7, "posix_acl_default") == 0))
+ continue;
+ if (strncmp(name, "trusted.SGI_", 12) == 0 &&
+ (strcmp(name + 12, "ACL_DEFAULT") == 0 ||
+ strcmp(name + 12, "ACL_FILE") == 0))
+ continue;
+
+ /* Linux: xfsroot namespace is obsolete and unsupported */
+ if (strncmp(name, "xfsroot.", 8) == 0) {
+ fail = 1;
+ archive_strcat(&errlist, name);
+ archive_strappend_char(&errlist, ' ');
+ continue;
+ }
+#endif
+
+ if (a->fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+ e = fsetxattr(a->fd, name, value, size, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ e = fsetxattr(a->fd, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+ e = fsetea(a->fd, name, value, size, 0);
+#endif
+ } else {
+#if ARCHIVE_XATTR_LINUX
+ e = lsetxattr(archive_entry_pathname(entry),
+ name, value, size, 0);
+#elif ARCHIVE_XATTR_DARWIN
+ e = setxattr(archive_entry_pathname(entry),
+ name, value, size, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+ e = lsetea(archive_entry_pathname(entry),
+ name, value, size, 0);
+#endif
+ }
+ if (e == -1) {
+ ret = ARCHIVE_WARN;
+ archive_strcat(&errlist, name);
+ archive_strappend_char(&errlist, ' ');
+ if (errno != ENOTSUP && errno != ENOSYS)
+ fail = 1;
+ }
+ }
+
+ if (ret == ARCHIVE_WARN) {
+ if (fail && errlist.length > 0) {
+ errlist.length--;
+ errlist.s[errlist.length] = '\0';
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore extended attributes: %s",
+ errlist.s);
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore extended "
+ "attributes on this file system.");
+ }
+
+ archive_string_free(&errlist);
+ return (ret);
+}
+#elif ARCHIVE_XATTR_FREEBSD
+/*
+ * Restore extended attributes - FreeBSD implementation
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ struct archive_entry *entry = a->entry;
+ struct archive_string errlist;
+ int ret = ARCHIVE_OK;
+ int i = archive_entry_xattr_reset(entry);
+ short fail = 0;
+
+ archive_string_init(&errlist);
+
+ while (i--) {
+ const char *name;
+ const void *value;
+ size_t size;
+ archive_entry_xattr_next(entry, &name, &value, &size);
+ if (name != NULL) {
+ int e;
+ int namespace;
+
+ namespace = EXTATTR_NAMESPACE_USER;
+
+ if (strncmp(name, "user.", 5) == 0) {
+ /* "user." attributes go to user namespace */
+ name += 5;
+ namespace = EXTATTR_NAMESPACE_USER;
+ } else if (strncmp(name, "system.", 7) == 0) {
+ name += 7;
+ namespace = EXTATTR_NAMESPACE_SYSTEM;
+ if (!strcmp(name, "nfs4.acl") ||
+ !strcmp(name, "posix1e.acl_access") ||
+ !strcmp(name, "posix1e.acl_default"))
+ continue;
+ } else {
+ /* Other namespaces are unsupported */
+ archive_strcat(&errlist, name);
+ archive_strappend_char(&errlist, ' ');
+ fail = 1;
+ ret = ARCHIVE_WARN;
+ continue;
+ }
+
+ if (a->fd >= 0) {
+ /*
+ * On FreeBSD, extattr_set_fd does not
+ * return the same as
+ * extattr_set_file. It returns zero
+ * on success, non-zero on failure.
+ *
+ * We can detect the failure by
+ * manually setting errno prior to the
+ * call and checking after.
+ *
+ * If errno remains zero, fake the
+ * return value by setting e to size.
+ *
+ * This is a hack for now until I
+ * (Shawn Webb) get FreeBSD to fix the
+ * issue, if that's even possible.
+ */
+ errno = 0;
+ e = extattr_set_fd(a->fd, namespace, name,
+ value, size);
+ if (e == 0 && errno == 0) {
+ e = size;
+ }
+ } else {
+ e = extattr_set_link(
+ archive_entry_pathname(entry), namespace,
+ name, value, size);
+ }
+ if (e != (int)size) {
+ archive_strcat(&errlist, name);
+ archive_strappend_char(&errlist, ' ');
+ ret = ARCHIVE_WARN;
+ if (errno != ENOTSUP && errno != ENOSYS)
+ fail = 1;
+ }
+ }
+ }
+
+ if (ret == ARCHIVE_WARN) {
+ if (fail && errlist.length > 0) {
+ errlist.length--;
+ errlist.s[errlist.length] = '\0';
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore extended attributes: %s",
+ errlist.s);
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Cannot restore extended "
+ "attributes on this file system.");
+ }
+
+ archive_string_free(&errlist);
+ return (ret);
+}
+#else
+/*
+ * Restore extended attributes - stub implementation for unsupported systems
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ static int warning_done = 0;
+
+ /* If there aren't any extended attributes, then it's okay not
+ * to extract them, otherwise, issue a single warning. */
+ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {
+ warning_done = 1;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Cannot restore extended attributes on this system");
+ return (ARCHIVE_WARN);
+ }
+ /* Warning was already emitted; suppress further warnings. */
+ return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+older(struct stat *st, struct archive_entry *entry)
+{
+ /* First, test the seconds and return if we have a definite answer. */
+ /* Definitely older. */
+ if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry)))
+ return (1);
+ /* Definitely younger. */
+ if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry)))
+ return (0);
+ /* If this platform supports fractional seconds, try those. */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+ /* Definitely older. */
+ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry))
+ return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ /* Definitely older. */
+ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))
+ return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+ /* older. */
+ if (st->st_mtime_n < archive_entry_mtime_nsec(entry))
+ return (1);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+ /* older. */
+ if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry))
+ return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+ /* older. */
+ if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry))
+ return (1);
+#else
+ /* This system doesn't have high-res timestamps. */
+#endif
+ /* Same age or newer, so not older. */
+ return (0);
+}
+
+#ifndef ARCHIVE_ACL_SUPPORT
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+ (void)a; /* UNUSED */
+ (void)fd; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)abstract_acl; /* UNUSED */
+ (void)mode; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Close the file descriptor if one is open.
+ */
+static void close_file_descriptor(struct archive_write_disk* a)
+{
+ if (a->fd >= 0) {
+ close(a->fd);
+ a->fd = -1;
+ }
+}
+
+
+#endif /* !_WIN32 || __CYGWIN__ */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_disk_private.h b/contrib/libs/libarchive/libarchive/archive_write_disk_private.h
new file mode 100644
index 0000000000..557d7e2bf3
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_disk_private.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_write_disk_private.h 201086 2009-12-28 02:17:53Z kientzle $
+ */
+
+#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_platform_acl.h"
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+
+struct archive_write_disk;
+
+int archive_write_disk_set_acls(struct archive *, int, const char *,
+ struct archive_acl *, __LA_MODE_T);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_write_disk_set_standard_lookup.c b/contrib/libs/libarchive/libarchive/archive_write_disk_set_standard_lookup.c
new file mode 100644
index 0000000000..5fccdb9dc6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_disk_set_standard_lookup.c
@@ -0,0 +1,263 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_disk_set_standard_lookup.c 201083 2009-12-28 02:09:57Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_write_disk_private.h"
+
+struct bucket {
+ char *name;
+ int hash;
+ id_t id;
+};
+
+static const size_t cache_size = 127;
+static unsigned int hash(const char *);
+static int64_t lookup_gid(void *, const char *uname, int64_t);
+static int64_t lookup_uid(void *, const char *uname, int64_t);
+static void cleanup(void *);
+
+/*
+ * Installs functions that use getpwnam()/getgrnam()---along with
+ * a simple cache to accelerate such lookups---into the archive_write_disk
+ * object. This is in a separate file because getpwnam()/getgrnam()
+ * can pull in a LOT of library code (including NIS/LDAP functions, which
+ * pull in DNS resolvers, etc). This can easily top 500kB, which makes
+ * it inappropriate for some space-constrained applications.
+ *
+ * Applications that are size-sensitive may want to just use the
+ * real default functions (defined in archive_write_disk.c) that just
+ * use the uid/gid without the lookup. Or define your own custom functions
+ * if you prefer.
+ *
+ * TODO: Replace these hash tables with simpler move-to-front LRU
+ * lists with a bounded size (128 items?). The hash is a bit faster,
+ * but has a bad pathology in which it thrashes a single bucket. Even
+ * walking a list of 128 items is a lot faster than calling
+ * getpwnam()!
+ */
+int
+archive_write_disk_set_standard_lookup(struct archive *a)
+{
+ struct bucket *ucache = calloc(cache_size, sizeof(struct bucket));
+ struct bucket *gcache = calloc(cache_size, sizeof(struct bucket));
+ if (ucache == NULL || gcache == NULL) {
+ free(ucache);
+ free(gcache);
+ return (ARCHIVE_FATAL);
+ }
+ archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup);
+ archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup);
+ return (ARCHIVE_OK);
+}
+
+static int64_t
+lookup_gid(void *private_data, const char *gname, int64_t gid)
+{
+ int h;
+ struct bucket *b;
+ struct bucket *gcache = (struct bucket *)private_data;
+
+ /* If no gname, just use the gid provided. */
+ if (gname == NULL || *gname == '\0')
+ return (gid);
+
+ /* Try to find gname in the cache. */
+ h = hash(gname);
+ b = &gcache[h % cache_size ];
+ if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)
+ return ((gid_t)b->id);
+
+ /* Free the cache slot for a new entry. */
+ free(b->name);
+ b->name = strdup(gname);
+ /* Note: If strdup fails, that's okay; we just won't cache. */
+ b->hash = h;
+#if HAVE_GRP_H
+# if HAVE_GETGRNAM_R
+ {
+ char _buffer[128];
+ size_t bufsize = 128;
+ char *buffer = _buffer;
+ char *allocated = NULL;
+ struct group grent, *result;
+ int r;
+
+ for (;;) {
+ result = &grent; /* Old getgrnam_r ignores last arg. */
+ r = getgrnam_r(gname, &grent, buffer, bufsize, &result);
+ if (r == 0)
+ break;
+ if (r != ERANGE)
+ break;
+ bufsize *= 2;
+ free(allocated);
+ allocated = malloc(bufsize);
+ if (allocated == NULL)
+ break;
+ buffer = allocated;
+ }
+ if (result != NULL)
+ gid = result->gr_gid;
+ free(allocated);
+ }
+# else /* HAVE_GETGRNAM_R */
+ {
+ struct group *result;
+
+ result = getgrnam(gname);
+ if (result != NULL)
+ gid = result->gr_gid;
+ }
+# endif /* HAVE_GETGRNAM_R */
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+ /* TODO: do a gname->gid lookup for Windows. */
+#else
+ #error No way to perform gid lookups on this platform
+#endif
+ b->id = (gid_t)gid;
+
+ return (gid);
+}
+
+static int64_t
+lookup_uid(void *private_data, const char *uname, int64_t uid)
+{
+ int h;
+ struct bucket *b;
+ struct bucket *ucache = (struct bucket *)private_data;
+
+ /* If no uname, just use the uid provided. */
+ if (uname == NULL || *uname == '\0')
+ return (uid);
+
+ /* Try to find uname in the cache. */
+ h = hash(uname);
+ b = &ucache[h % cache_size ];
+ if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)
+ return ((uid_t)b->id);
+
+ /* Free the cache slot for a new entry. */
+ free(b->name);
+ b->name = strdup(uname);
+ /* Note: If strdup fails, that's okay; we just won't cache. */
+ b->hash = h;
+#if HAVE_PWD_H
+# if HAVE_GETPWNAM_R
+ {
+ char _buffer[128];
+ size_t bufsize = 128;
+ char *buffer = _buffer;
+ char *allocated = NULL;
+ struct passwd pwent, *result;
+ int r;
+
+ for (;;) {
+ result = &pwent; /* Old getpwnam_r ignores last arg. */
+ r = getpwnam_r(uname, &pwent, buffer, bufsize, &result);
+ if (r == 0)
+ break;
+ if (r != ERANGE)
+ break;
+ bufsize *= 2;
+ free(allocated);
+ allocated = malloc(bufsize);
+ if (allocated == NULL)
+ break;
+ buffer = allocated;
+ }
+ if (result != NULL)
+ uid = result->pw_uid;
+ free(allocated);
+ }
+# else /* HAVE_GETPWNAM_R */
+ {
+ struct passwd *result;
+
+ result = getpwnam(uname);
+ if (result != NULL)
+ uid = result->pw_uid;
+ }
+#endif /* HAVE_GETPWNAM_R */
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+ /* TODO: do a uname->uid lookup for Windows. */
+#else
+ #error No way to look up uids on this platform
+#endif
+ b->id = (uid_t)uid;
+
+ return (uid);
+}
+
+static void
+cleanup(void *private)
+{
+ size_t i;
+ struct bucket *cache = (struct bucket *)private;
+
+ for (i = 0; i < cache_size; i++)
+ free(cache[i].name);
+ free(cache);
+}
+
+
+static unsigned int
+hash(const char *p)
+{
+ /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
+ as used by ELF for hashing function names. */
+ unsigned g, h = 0;
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ if ((g = h & 0xF0000000) != 0) {
+ h ^= g >> 24;
+ h &= 0x0FFFFFFF;
+ }
+ }
+ return h;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_disk_windows.c b/contrib/libs/libarchive/libarchive/archive_write_disk_windows.c
new file mode 100644
index 0000000000..7b9ea74937
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_disk_windows.c
@@ -0,0 +1,2911 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <winioctl.h>
+
+/* TODO: Support Mac OS 'quarantine' feature. This is really just a
+ * standard tag to mark files that have been downloaded as "tainted".
+ * On Mac OS, we should mark the extracted files as tainted if the
+ * archive being read was tainted. Windows has a similar feature; we
+ * should investigate ways to support this generically. */
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
+#define IO_REPARSE_TAG_SYMLINK 0xA000000CL
+#endif
+
+static BOOL SetFilePointerEx_perso(HANDLE hFile,
+ LARGE_INTEGER liDistanceToMove,
+ PLARGE_INTEGER lpNewFilePointer,
+ DWORD dwMoveMethod)
+{
+ LARGE_INTEGER li;
+ li.QuadPart = liDistanceToMove.QuadPart;
+ li.LowPart = SetFilePointer(
+ hFile, li.LowPart, &li.HighPart, dwMoveMethod);
+ if(lpNewFilePointer) {
+ lpNewFilePointer->QuadPart = li.QuadPart;
+ }
+ return li.LowPart != (DWORD)-1 || GetLastError() == NO_ERROR;
+}
+
+struct fixup_entry {
+ struct fixup_entry *next;
+ struct archive_acl acl;
+ mode_t mode;
+ int64_t atime;
+ int64_t birthtime;
+ int64_t mtime;
+ int64_t ctime;
+ unsigned long atime_nanos;
+ unsigned long birthtime_nanos;
+ unsigned long mtime_nanos;
+ unsigned long ctime_nanos;
+ unsigned long fflags_set;
+ int fixup; /* bitmask of what needs fixing */
+ wchar_t *name;
+};
+
+/*
+ * We use a bitmask to track which operations remain to be done for
+ * this file. In particular, this helps us avoid unnecessary
+ * operations when it's possible to take care of one step as a
+ * side-effect of another. For example, mkdir() can specify the mode
+ * for the newly-created object but symlink() cannot. This means we
+ * can skip chmod() if mkdir() succeeded, but we must explicitly
+ * chmod() if we're trying to create a directory that already exists
+ * (mkdir() failed) or if we're restoring a symlink. Similarly, we
+ * need to verify UID/GID before trying to restore SUID/SGID bits;
+ * that verification can occur explicitly through a stat() call or
+ * implicitly because of a successful chown() call.
+ */
+#define TODO_MODE_FORCE 0x40000000
+#define TODO_MODE_BASE 0x20000000
+#define TODO_SUID 0x10000000
+#define TODO_SUID_CHECK 0x08000000
+#define TODO_SGID 0x04000000
+#define TODO_SGID_CHECK 0x02000000
+#define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID)
+#define TODO_TIMES ARCHIVE_EXTRACT_TIME
+#define TODO_OWNER ARCHIVE_EXTRACT_OWNER
+#define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS
+#define TODO_ACLS ARCHIVE_EXTRACT_ACL
+#define TODO_XATTR ARCHIVE_EXTRACT_XATTR
+#define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA
+
+struct archive_write_disk {
+ struct archive archive;
+
+ mode_t user_umask;
+ struct fixup_entry *fixup_list;
+ struct fixup_entry *current_fixup;
+ int64_t user_uid;
+ int skip_file_set;
+ int64_t skip_file_dev;
+ int64_t skip_file_ino;
+ time_t start_time;
+
+ int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);
+ void (*cleanup_gid)(void *private);
+ void *lookup_gid_data;
+ int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);
+ void (*cleanup_uid)(void *private);
+ void *lookup_uid_data;
+
+ /*
+ * Full path of last file to satisfy symlink checks.
+ */
+ struct archive_wstring path_safe;
+
+ /*
+ * Cached stat data from disk for the current entry.
+ * If this is valid, pst points to st. Otherwise,
+ * pst is null.
+ */
+ BY_HANDLE_FILE_INFORMATION st;
+ BY_HANDLE_FILE_INFORMATION *pst;
+
+ /* Information about the object being restored right now. */
+ struct archive_entry *entry; /* Entry being extracted. */
+ wchar_t *name; /* Name of entry, possibly edited. */
+ struct archive_wstring _name_data; /* backing store for 'name' */
+ wchar_t *tmpname; /* Temporary name */
+ struct archive_wstring _tmpname_data; /* backing store for 'tmpname' */
+ /* Tasks remaining for this object. */
+ int todo;
+ /* Tasks deferred until end-of-archive. */
+ int deferred;
+ /* Options requested by the client. */
+ int flags;
+ /* Handle for the file we're restoring. */
+ HANDLE fh;
+ /* Current offset for writing data to the file. */
+ int64_t offset;
+ /* Last offset actually written to disk. */
+ int64_t fd_offset;
+ /* Total bytes actually written to files. */
+ int64_t total_bytes_written;
+ /* Maximum size of file, -1 if unknown. */
+ int64_t filesize;
+ /* Dir we were in before this restore; only for deep paths. */
+ int restore_pwd;
+ /* Mode we should use for this entry; affected by _PERM and umask. */
+ mode_t mode;
+ /* UID/GID to use in restoring this entry. */
+ int64_t uid;
+ int64_t gid;
+};
+
+/*
+ * Default mode for dirs created automatically (will be modified by umask).
+ * Note that POSIX specifies 0777 for implicitly-created dirs, "modified
+ * by the process' file creation mask."
+ */
+#define DEFAULT_DIR_MODE 0777
+/*
+ * Dir modes are restored in two steps: During the extraction, the permissions
+ * in the archive are modified to match the following limits. During
+ * the post-extract fixup pass, the permissions from the archive are
+ * applied.
+ */
+#define MINIMUM_DIR_MODE 0700
+#define MAXIMUM_DIR_MODE 0775
+
+static int disk_unlink(const wchar_t *);
+static int disk_rmdir(const wchar_t *);
+static int check_symlinks(struct archive_write_disk *);
+static int create_filesystem_object(struct archive_write_disk *);
+static struct fixup_entry *current_fixup(struct archive_write_disk *,
+ const wchar_t *pathname);
+static int cleanup_pathname(struct archive_write_disk *, wchar_t *);
+static int create_dir(struct archive_write_disk *, wchar_t *);
+static int create_parent_dir(struct archive_write_disk *, wchar_t *);
+static int la_chmod(const wchar_t *, mode_t);
+static int la_mktemp(struct archive_write_disk *);
+static int older(BY_HANDLE_FILE_INFORMATION *, struct archive_entry *);
+static int permissive_name_w(struct archive_write_disk *);
+static int restore_entry(struct archive_write_disk *);
+static int set_acls(struct archive_write_disk *, HANDLE h,
+ const wchar_t *, struct archive_acl *);
+static int set_xattrs(struct archive_write_disk *);
+static int clear_nochange_fflags(struct archive_write_disk *);
+static int set_fflags(struct archive_write_disk *);
+static int set_fflags_platform(const wchar_t *, unsigned long,
+ unsigned long);
+static int set_ownership(struct archive_write_disk *);
+static int set_mode(struct archive_write_disk *, int mode);
+static int set_times(struct archive_write_disk *, HANDLE, int,
+ const wchar_t *, time_t, long, time_t, long, time_t,
+ long, time_t, long);
+static int set_times_from_entry(struct archive_write_disk *);
+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
+static ssize_t write_data_block(struct archive_write_disk *,
+ const char *, size_t);
+
+static int _archive_write_disk_close(struct archive *);
+static int _archive_write_disk_free(struct archive *);
+static int _archive_write_disk_header(struct archive *,
+ struct archive_entry *);
+static int64_t _archive_write_disk_filter_bytes(struct archive *, int);
+static int _archive_write_disk_finish_entry(struct archive *);
+static ssize_t _archive_write_disk_data(struct archive *, const void *,
+ size_t);
+static ssize_t _archive_write_disk_data_block(struct archive *, const void *,
+ size_t, int64_t);
+
+#define bhfi_dev(bhfi) ((bhfi)->dwVolumeSerialNumber)
+/* Treat FileIndex as i-node. We should remove a sequence number
+ * which is high-16-bits of nFileIndexHigh. */
+#define bhfi_ino(bhfi) \
+ ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \
+ | (bhfi)->nFileIndexLow)
+#define bhfi_size(bhfi) \
+ ((((int64_t)(bhfi)->nFileSizeHigh) << 32) | (bhfi)->nFileSizeLow)
+
+static int
+file_information(struct archive_write_disk *a, wchar_t *path,
+ BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat)
+{
+ HANDLE h;
+ int r;
+ DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
+ WIN32_FIND_DATAW findData;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ if (sim_lstat || mode != NULL) {
+ h = FindFirstFileW(path, &findData);
+ if (h == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ wchar_t *full;
+ full = __la_win_permissive_name_w(path);
+ h = FindFirstFileW(full, &findData);
+ free(full);
+ }
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ FindClose(h);
+ }
+
+ /* Is symlink file ? */
+ if (sim_lstat &&
+ ((findData.dwFileAttributes
+ & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)))
+ flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = flag;
+ h = CreateFile2(a->name, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(a->name, 0, 0, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ if (h == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ wchar_t *full;
+ full = __la_win_permissive_name_w(path);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ h = CreateFile2(full, 0, 0,
+ OPEN_EXISTING, &createExParams);
+#else
+ h = CreateFileW(full, 0, 0, NULL,
+ OPEN_EXISTING, flag, NULL);
+#endif
+ free(full);
+ }
+ if (h == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ r = GetFileInformationByHandle(h, st);
+ CloseHandle(h);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+
+ if (mode == NULL)
+ return (0);
+
+ *mode = S_IRUSR | S_IRGRP | S_IROTH;
+ if ((st->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+ *mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+ if ((st->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
+ *mode |= S_IFLNK;
+ else if (st->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ *mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else {
+ const wchar_t *p;
+
+ *mode |= S_IFREG;
+ p = wcsrchr(path, L'.');
+ if (p != NULL && wcslen(p) == 4) {
+ switch (p[1]) {
+ case L'B': case L'b':
+ if ((p[2] == L'A' || p[2] == L'a' ) &&
+ (p[3] == L'T' || p[3] == L't' ))
+ *mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'C': case L'c':
+ if (((p[2] == L'M' || p[2] == L'm' ) &&
+ (p[3] == L'D' || p[3] == L'd' )))
+ *mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case L'E': case L'e':
+ if ((p[2] == L'X' || p[2] == L'x' ) &&
+ (p[3] == L'E' || p[3] == L'e' ))
+ *mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Note: The path, for example, "aa/a/../b../c" will be converted to "aa/c"
+ * by GetFullPathNameW() W32 API, which __la_win_permissive_name_w uses.
+ * It means we cannot handle multiple dirs in one archive_entry.
+ * So we have to make the full-pathname in another way, which does not
+ * break "../" path string.
+ */
+static int
+permissive_name_w(struct archive_write_disk *a)
+{
+ wchar_t *wn, *wnp;
+ wchar_t *ws, *wsp;
+ DWORD l;
+
+ wnp = a->name;
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'?' && wnp[3] == L'\\')
+ /* We have already a permissive name. */
+ return (0);
+
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+ wnp[2] == L'.' && wnp[3] == L'\\') {
+ /* This is a device name */
+ if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
+ (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
+ wnp[5] == L':' && wnp[6] == L'\\') {
+ wnp[2] = L'?';/* Not device name. */
+ return (0);
+ }
+ }
+
+ /*
+ * A full-pathname starting with a drive name like "C:\abc".
+ */
+ if (((wnp[0] >= L'a' && wnp[0] <= L'z') ||
+ (wnp[0] >= L'A' && wnp[0] <= L'Z')) &&
+ wnp[1] == L':' && wnp[2] == L'\\') {
+ wn = _wcsdup(wnp);
+ if (wn == NULL)
+ return (-1);
+ archive_wstring_ensure(&(a->_name_data), 4 + wcslen(wn) + 1);
+ a->name = a->_name_data.s;
+ /* Prepend "\\?\" */
+ archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+ archive_wstrcat(&(a->_name_data), wn);
+ free(wn);
+ return (0);
+ }
+
+ /*
+ * A full-pathname pointing to a network drive
+ * like "\\<server-name>\<share-name>\file".
+ */
+ if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
+ const wchar_t *p = &wnp[2];
+
+ /* Skip server-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\') {
+ const wchar_t *rp = ++p;
+ /* Skip share-name letters. */
+ while (*p != L'\\' && *p != L'\0')
+ ++p;
+ if (*p == L'\\' && p != rp) {
+ /* Now, match patterns such as
+ * "\\server-name\share-name\" */
+ wn = _wcsdup(wnp);
+ if (wn == NULL)
+ return (-1);
+ archive_wstring_ensure(&(a->_name_data),
+ 8 + wcslen(wn) + 1);
+ a->name = a->_name_data.s;
+ /* Prepend "\\?\UNC\" */
+ archive_wstrncpy(&(a->_name_data),
+ L"\\\\?\\UNC\\", 8);
+ archive_wstrcat(&(a->_name_data), wn+2);
+ free(wn);
+ return (0);
+ }
+ }
+ return (0);
+ }
+
+ /*
+ * Get current working directory.
+ */
+ l = GetCurrentDirectoryW(0, NULL);
+ if (l == 0)
+ return (-1);
+ ws = malloc(l * sizeof(wchar_t));
+ l = GetCurrentDirectoryW(l, ws);
+ if (l == 0) {
+ free(ws);
+ return (-1);
+ }
+ wsp = ws;
+
+ /*
+ * A full-pathname starting without a drive name like "\abc".
+ */
+ if (wnp[0] == L'\\') {
+ wn = _wcsdup(wnp);
+ if (wn == NULL)
+ return (-1);
+ archive_wstring_ensure(&(a->_name_data),
+ 4 + 2 + wcslen(wn) + 1);
+ a->name = a->_name_data.s;
+ /* Prepend "\\?\" and drive name. */
+ archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+ archive_wstrncat(&(a->_name_data), wsp, 2);
+ archive_wstrcat(&(a->_name_data), wn);
+ free(wsp);
+ free(wn);
+ return (0);
+ }
+
+ wn = _wcsdup(wnp);
+ if (wn == NULL)
+ return (-1);
+ archive_wstring_ensure(&(a->_name_data), 4 + l + 1 + wcslen(wn) + 1);
+ a->name = a->_name_data.s;
+ /* Prepend "\\?\" and drive name if not already added. */
+ if (l > 3 && wsp[0] == L'\\' && wsp[1] == L'\\' &&
+ wsp[2] == L'?' && wsp[3] == L'\\')
+ {
+ archive_wstrncpy(&(a->_name_data), wsp, l);
+ }
+ else if (l > 2 && wsp[0] == L'\\' && wsp[1] == L'\\' && wsp[2] != L'\\')
+ {
+ archive_wstrncpy(&(a->_name_data), L"\\\\?\\UNC\\", 8);
+ archive_wstrncat(&(a->_name_data), wsp+2, l-2);
+ }
+ else
+ {
+ archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+ archive_wstrncat(&(a->_name_data), wsp, l);
+ }
+ archive_wstrncat(&(a->_name_data), L"\\", 1);
+ archive_wstrcat(&(a->_name_data), wn);
+ a->name = a->_name_data.s;
+ free(wsp);
+ free(wn);
+ return (0);
+}
+
+static int
+la_chmod(const wchar_t *path, mode_t mode)
+{
+ DWORD attr;
+ BOOL r;
+ wchar_t *fullname;
+ int ret = 0;
+
+ fullname = NULL;
+ attr = GetFileAttributesW(path);
+ if (attr == (DWORD)-1 &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(path);
+ attr = GetFileAttributesW(fullname);
+ }
+ if (attr == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ ret = -1;
+ goto exit_chmode;
+ }
+ if (mode & _S_IWRITE)
+ attr &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ attr |= FILE_ATTRIBUTE_READONLY;
+ if (fullname != NULL)
+ r = SetFileAttributesW(fullname, attr);
+ else
+ r = SetFileAttributesW(path, attr);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ ret = -1;
+ }
+exit_chmode:
+ free(fullname);
+ return (ret);
+}
+
+static int
+la_mktemp(struct archive_write_disk *a)
+{
+ int fd;
+ mode_t mode;
+
+ archive_wstring_empty(&(a->_tmpname_data));
+ archive_wstrcpy(&(a->_tmpname_data), a->name);
+ archive_wstrcat(&(a->_tmpname_data), L".XXXXXX");
+ a->tmpname = a->_tmpname_data.s;
+
+ fd = __archive_mkstemp(a->tmpname);
+ if (fd == -1)
+ return -1;
+
+ mode = a->mode & 0777 & ~a->user_umask;
+ if (la_chmod(a->tmpname, mode) == -1) {
+ la_dosmaperr(GetLastError());
+ _close(fd);
+ return -1;
+ }
+ return (fd);
+}
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+static void *
+la_GetFunctionKernel32(const char *name)
+{
+ static HINSTANCE lib;
+ static int set;
+ if (!set) {
+ set = 1;
+ lib = LoadLibrary(TEXT("kernel32.dll"));
+ }
+ if (lib == NULL) {
+ fprintf(stderr, "Can't load kernel32.dll?!\n");
+ exit(1);
+ }
+ return (void *)GetProcAddress(lib, name);
+}
+#endif
+
+static int
+la_CreateHardLinkW(wchar_t *linkname, wchar_t *target)
+{
+ static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
+ BOOL ret;
+
+#if _WIN32_WINNT < _WIN32_WINNT_XP
+ static int set;
+/* CreateHardLinkW is available since XP and always loaded */
+ if (!set) {
+ set = 1;
+ f = la_GetFunctionKernel32("CreateHardLinkW");
+ }
+#else
+ f = CreateHardLinkW;
+#endif
+ if (!f) {
+ errno = ENOTSUP;
+ return (0);
+ }
+ ret = (*f)(linkname, target, NULL);
+ if (!ret) {
+ /* Under windows 2000, it is necessary to remove
+ * the "\\?\" prefix. */
+#define IS_UNC(name) ((name[0] == L'U' || name[0] == L'u') && \
+ (name[1] == L'N' || name[1] == L'n') && \
+ (name[2] == L'C' || name[2] == L'c') && \
+ name[3] == L'\\')
+ if (!wcsncmp(linkname,L"\\\\?\\", 4)) {
+ linkname += 4;
+ if (IS_UNC(linkname))
+ linkname += 4;
+ }
+ if (!wcsncmp(target,L"\\\\?\\", 4)) {
+ target += 4;
+ if (IS_UNC(target))
+ target += 4;
+ }
+#undef IS_UNC
+ ret = (*f)(linkname, target, NULL);
+ }
+ return (ret);
+}
+
+/*
+ * Create file or directory symolic link
+ *
+ * If linktype is AE_SYMLINK_TYPE_UNDEFINED (or unknown), guess linktype from
+ * the link target
+ */
+static int
+la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,
+ int linktype) {
+ static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD);
+ wchar_t *ttarget, *p;
+ size_t len;
+ DWORD attrs = 0;
+ DWORD flags = 0;
+ DWORD newflags = 0;
+ BOOL ret = 0;
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+/* CreateSymbolicLinkW is available since Vista and always loaded */
+ static int set;
+ if (!set) {
+ set = 1;
+ f = la_GetFunctionKernel32("CreateSymbolicLinkW");
+ }
+#else
+# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ f = CreateSymbolicLinkW;
+# else
+ f = NULL;
+# endif
+#endif
+ if (!f)
+ return (0);
+
+ len = wcslen(target);
+ if (len == 0) {
+ errno = EINVAL;
+ return(0);
+ }
+ /*
+ * When writing path targets, we need to translate slashes
+ * to backslashes
+ */
+ ttarget = malloc((len + 1) * sizeof(wchar_t));
+ if (ttarget == NULL)
+ return(0);
+
+ p = ttarget;
+
+ while(*target != L'\0') {
+ if (*target == L'/')
+ *p = L'\\';
+ else
+ *p = *target;
+ target++;
+ p++;
+ }
+ *p = L'\0';
+
+ /*
+ * In case of undefined symlink type we guess it from the target.
+ * If the target equals ".", "..", ends with a backslash or a
+ * backslash followed by "." or ".." we assume it is a directory
+ * symlink. In all other cases we assume a file symlink.
+ */
+ if (linktype != AE_SYMLINK_TYPE_FILE && (
+ linktype == AE_SYMLINK_TYPE_DIRECTORY ||
+ *(p - 1) == L'\\' || (*(p - 1) == L'.' && (
+ len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && (
+ len == 2 || *(p - 3) == L'\\')))))) {
+#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
+ flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+#else
+ flags |= 0x1;
+#endif
+ }
+
+#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
+ newflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+#else
+ newflags = flags | 0x2;
+#endif
+
+ /*
+ * Windows won't overwrite existing links
+ */
+ attrs = GetFileAttributesW(linkname);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ disk_rmdir(linkname);
+ else
+ disk_unlink(linkname);
+ }
+
+ ret = (*f)(linkname, ttarget, newflags);
+ /*
+ * Prior to Windows 10 calling CreateSymbolicLinkW() will fail
+ * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set
+ */
+ if (!ret) {
+ ret = (*f)(linkname, ttarget, flags);
+ }
+ free(ttarget);
+ return (ret);
+}
+
+static int
+la_ftruncate(HANDLE handle, int64_t length)
+{
+ LARGE_INTEGER distance;
+
+ if (GetFileType(handle) != FILE_TYPE_DISK) {
+ errno = EBADF;
+ return (-1);
+ }
+ distance.QuadPart = length;
+ if (!SetFilePointerEx_perso(handle, distance, NULL, FILE_BEGIN)) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ if (!SetEndOfFile(handle)) {
+ la_dosmaperr(GetLastError());
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+lazy_stat(struct archive_write_disk *a)
+{
+ if (a->pst != NULL) {
+ /* Already have stat() data available. */
+ return (ARCHIVE_OK);
+ }
+ if (a->fh != INVALID_HANDLE_VALUE &&
+ GetFileInformationByHandle(a->fh, &a->st) == 0) {
+ a->pst = &a->st;
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * XXX At this point, symlinks should not be hit, otherwise
+ * XXX a race occurred. Do we want to check explicitly for that?
+ */
+ if (file_information(a, a->name, &a->st, NULL, 1) == 0) {
+ a->pst = &a->st;
+ return (ARCHIVE_OK);
+ }
+ archive_set_error(&a->archive, errno, "Couldn't stat file");
+ return (ARCHIVE_WARN);
+}
+
+static const struct archive_vtable
+archive_write_disk_vtable = {
+ .archive_close = _archive_write_disk_close,
+ .archive_filter_bytes = _archive_write_disk_filter_bytes,
+ .archive_free = _archive_write_disk_free,
+ .archive_write_header = _archive_write_disk_header,
+ .archive_write_finish_entry = _archive_write_disk_finish_entry,
+ .archive_write_data = _archive_write_disk_data,
+ .archive_write_data_block = _archive_write_disk_data_block,
+};
+
+static int64_t
+_archive_write_disk_filter_bytes(struct archive *_a, int n)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ (void)n; /* UNUSED */
+ if (n == -1 || n == 0)
+ return (a->total_bytes_written);
+ return (-1);
+}
+
+
+int
+archive_write_disk_set_options(struct archive *_a, int flags)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+ a->flags = flags;
+ return (ARCHIVE_OK);
+}
+
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks. According to the standards, we're
+ * supposed to check each extracted hardlink and squawk if it refers
+ * to a file that we didn't restore. I'm not entirely convinced this
+ * is a good idea, but more importantly: Is there any way to validate
+ * hardlinks without keeping a complete list of filenames from the
+ * entire archive?? Ugh.
+ *
+ */
+static int
+_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ struct fixup_entry *fe;
+ int ret, r;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_disk_header");
+ archive_clear_error(&a->archive);
+ if (a->archive.state & ARCHIVE_STATE_DATA) {
+ r = _archive_write_disk_finish_entry(&a->archive);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ }
+
+ /* Set up for this particular entry. */
+ a->pst = NULL;
+ a->current_fixup = NULL;
+ a->deferred = 0;
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ a->entry = archive_entry_clone(entry);
+ a->fh = INVALID_HANDLE_VALUE;
+ a->fd_offset = 0;
+ a->offset = 0;
+ a->restore_pwd = -1;
+ a->uid = a->user_uid;
+ a->mode = archive_entry_mode(a->entry);
+ if (archive_entry_size_is_set(a->entry))
+ a->filesize = archive_entry_size(a->entry);
+ else
+ a->filesize = -1;
+ archive_wstrcpy(&(a->_name_data), archive_entry_pathname_w(a->entry));
+ a->name = a->_name_data.s;
+ archive_clear_error(&a->archive);
+
+ /*
+ * Clean up the requested path. This is necessary for correct
+ * dir restores; the dir restore logic otherwise gets messed
+ * up by nonsense like "dir/.".
+ */
+ ret = cleanup_pathname(a, a->name);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ /*
+ * Generate a full-pathname and use it from here.
+ */
+ if (permissive_name_w(a) < 0) {
+ errno = EINVAL;
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Query the umask so we get predictable mode settings.
+ * This gets done on every call to _write_header in case the
+ * user edits their umask during the extraction for some
+ * reason.
+ */
+ umask(a->user_umask = umask(0));
+
+ /* Figure out what we need to do for this entry. */
+ a->todo = TODO_MODE_BASE;
+ if (a->flags & ARCHIVE_EXTRACT_PERM) {
+ a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */
+ /*
+ * SGID requires an extra "check" step because we
+ * cannot easily predict the GID that the system will
+ * assign. (Different systems assign GIDs to files
+ * based on a variety of criteria, including process
+ * credentials and the gid of the enclosing
+ * directory.) We can only restore the SGID bit if
+ * the file has the right GID, and we only know the
+ * GID if we either set it (see set_ownership) or if
+ * we've actually called stat() on the file after it
+ * was restored. Since there are several places at
+ * which we might verify the GID, we need a TODO bit
+ * to keep track.
+ */
+ if (a->mode & S_ISGID)
+ a->todo |= TODO_SGID | TODO_SGID_CHECK;
+ /*
+ * Verifying the SUID is simpler, but can still be
+ * done in multiple ways, hence the separate "check" bit.
+ */
+ if (a->mode & S_ISUID)
+ a->todo |= TODO_SUID | TODO_SUID_CHECK;
+ } else {
+ /*
+ * User didn't request full permissions, so don't
+ * restore SUID, SGID bits and obey umask.
+ */
+ a->mode &= ~S_ISUID;
+ a->mode &= ~S_ISGID;
+ a->mode &= ~S_ISVTX;
+ a->mode &= ~a->user_umask;
+ }
+#if 0
+ if (a->flags & ARCHIVE_EXTRACT_OWNER)
+ a->todo |= TODO_OWNER;
+#endif
+ if (a->flags & ARCHIVE_EXTRACT_TIME)
+ a->todo |= TODO_TIMES;
+ if (a->flags & ARCHIVE_EXTRACT_ACL) {
+ if (archive_entry_filetype(a->entry) == AE_IFDIR)
+ a->deferred |= TODO_ACLS;
+ else
+ a->todo |= TODO_ACLS;
+ }
+ if (a->flags & ARCHIVE_EXTRACT_XATTR)
+ a->todo |= TODO_XATTR;
+ if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
+ a->todo |= TODO_FFLAGS;
+ if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+ ret = check_symlinks(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+
+ ret = restore_entry(a);
+
+ /*
+ * TODO: There are rumours that some extended attributes must
+ * be restored before file data is written. If this is true,
+ * then we either need to write all extended attributes both
+ * before and after restoring the data, or find some rule for
+ * determining which must go first and which last. Due to the
+ * many ways people are using xattrs, this may prove to be an
+ * intractable problem.
+ */
+
+ /*
+ * Fixup uses the unedited pathname from archive_entry_pathname(),
+ * because it is relative to the base dir and the edited path
+ * might be relative to some intermediate dir as a result of the
+ * deep restore logic.
+ */
+ if (a->deferred & TODO_MODE) {
+ fe = current_fixup(a, archive_entry_pathname_w(entry));
+ fe->fixup |= TODO_MODE_BASE;
+ fe->mode = a->mode;
+ }
+
+ if ((a->deferred & TODO_TIMES)
+ && (archive_entry_mtime_is_set(entry)
+ || archive_entry_atime_is_set(entry))) {
+ fe = current_fixup(a, archive_entry_pathname_w(entry));
+ fe->mode = a->mode;
+ fe->fixup |= TODO_TIMES;
+ if (archive_entry_atime_is_set(entry)) {
+ fe->atime = archive_entry_atime(entry);
+ fe->atime_nanos = archive_entry_atime_nsec(entry);
+ } else {
+ /* If atime is unset, use start time. */
+ fe->atime = a->start_time;
+ fe->atime_nanos = 0;
+ }
+ if (archive_entry_mtime_is_set(entry)) {
+ fe->mtime = archive_entry_mtime(entry);
+ fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+ } else {
+ /* If mtime is unset, use start time. */
+ fe->mtime = a->start_time;
+ fe->mtime_nanos = 0;
+ }
+ if (archive_entry_birthtime_is_set(entry)) {
+ fe->birthtime = archive_entry_birthtime(entry);
+ fe->birthtime_nanos = archive_entry_birthtime_nsec(entry);
+ } else {
+ /* If birthtime is unset, use mtime. */
+ fe->birthtime = fe->mtime;
+ fe->birthtime_nanos = fe->mtime_nanos;
+ }
+ }
+
+ if (a->deferred & TODO_ACLS) {
+ fe = current_fixup(a, archive_entry_pathname_w(entry));
+ archive_acl_copy(&fe->acl, archive_entry_acl(entry));
+ }
+
+ if (a->deferred & TODO_FFLAGS) {
+ unsigned long set, clear;
+
+ fe = current_fixup(a, archive_entry_pathname_w(entry));
+ archive_entry_fflags(entry, &set, &clear);
+ fe->fflags_set = set;
+ }
+
+ /*
+ * On Windows, A creating sparse file requires a special mark.
+ */
+ if (a->fh != INVALID_HANDLE_VALUE &&
+ archive_entry_sparse_count(entry) > 0) {
+ int64_t base = 0, offset, length;
+ int i, cnt = archive_entry_sparse_reset(entry);
+ int sparse = 0;
+
+ for (i = 0; i < cnt; i++) {
+ archive_entry_sparse_next(entry, &offset, &length);
+ if (offset - base >= 4096) {
+ sparse = 1;/* we have a hole. */
+ break;
+ }
+ base = offset + length;
+ }
+ if (sparse) {
+ DWORD dmy;
+ /* Mark this file as sparse. */
+ DeviceIoControl(a->fh, FSCTL_SET_SPARSE,
+ NULL, 0, NULL, 0, &dmy, NULL);
+ }
+ }
+
+ /* We've created the object and are ready to pour data into it. */
+ if (ret >= ARCHIVE_WARN)
+ a->archive.state = ARCHIVE_STATE_DATA;
+ /*
+ * If it's not open, tell our client not to try writing.
+ * In particular, dirs, links, etc, don't get written to.
+ */
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ archive_entry_set_size(entry, 0);
+ a->filesize = 0;
+ }
+
+ return (ret);
+}
+
+int
+archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");
+ a->skip_file_set = 1;
+ a->skip_file_dev = d;
+ a->skip_file_ino = i;
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
+{
+ OVERLAPPED ol;
+ uint64_t start_size = size;
+ DWORD bytes_written = 0;
+ ssize_t block_size = 0, bytes_to_write;
+
+ if (size == 0)
+ return (ARCHIVE_OK);
+
+ if (a->filesize == 0 || a->fh == INVALID_HANDLE_VALUE) {
+ archive_set_error(&a->archive, 0,
+ "Attempt to write to an empty file");
+ return (ARCHIVE_WARN);
+ }
+
+ if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
+ /* XXX TODO XXX Is there a more appropriate choice here ? */
+ /* This needn't match the filesystem allocation size. */
+ block_size = 16*1024;
+ }
+
+ /* If this write would run beyond the file size, truncate it. */
+ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+ start_size = size = (size_t)(a->filesize - a->offset);
+
+ /* Write the data. */
+ while (size > 0) {
+ if (block_size == 0) {
+ bytes_to_write = size;
+ } else {
+ /* We're sparsifying the file. */
+ const char *p, *end;
+ int64_t block_end;
+
+ /* Skip leading zero bytes. */
+ for (p = buff, end = buff + size; p < end; ++p) {
+ if (*p != '\0')
+ break;
+ }
+ a->offset += p - buff;
+ size -= p - buff;
+ buff = p;
+ if (size == 0)
+ break;
+
+ /* Calculate next block boundary after offset. */
+ block_end
+ = (a->offset / block_size + 1) * block_size;
+
+ /* If the adjusted write would cross block boundary,
+ * truncate it to the block boundary. */
+ bytes_to_write = size;
+ if (a->offset + bytes_to_write > block_end)
+ bytes_to_write = (DWORD)(block_end - a->offset);
+ }
+ memset(&ol, 0, sizeof(ol));
+ ol.Offset = (DWORD)(a->offset & 0xFFFFFFFF);
+ ol.OffsetHigh = (DWORD)(a->offset >> 32);
+ if (!WriteFile(a->fh, buff, (uint32_t)bytes_to_write,
+ &bytes_written, &ol)) {
+ DWORD lasterr;
+
+ lasterr = GetLastError();
+ if (lasterr == ERROR_ACCESS_DENIED)
+ errno = EBADF;
+ else
+ la_dosmaperr(lasterr);
+ archive_set_error(&a->archive, errno, "Write failed");
+ return (ARCHIVE_WARN);
+ }
+ buff += bytes_written;
+ size -= bytes_written;
+ a->total_bytes_written += bytes_written;
+ a->offset += bytes_written;
+ a->fd_offset = a->offset;
+ }
+ return ((ssize_t)(start_size - size));
+}
+
+static ssize_t
+_archive_write_disk_data_block(struct archive *_a,
+ const void *buff, size_t size, int64_t offset)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ ssize_t r;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data_block");
+
+ a->offset = offset;
+ r = write_data_block(a, buff, size);
+ if (r < ARCHIVE_OK)
+ return (r);
+ if ((size_t)r < size) {
+ archive_set_error(&a->archive, 0,
+ "Write request too large");
+ return (ARCHIVE_WARN);
+ }
+#if ARCHIVE_VERSION_NUMBER < 3999000
+ return (ARCHIVE_OK);
+#else
+ return (size);
+#endif
+}
+
+static ssize_t
+_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_DATA, "archive_write_data");
+
+ return (write_data_block(a, buff, size));
+}
+
+static int
+_archive_write_disk_finish_entry(struct archive *_a)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ int ret = ARCHIVE_OK;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_finish_entry");
+ if (a->archive.state & ARCHIVE_STATE_HEADER)
+ return (ARCHIVE_OK);
+ archive_clear_error(&a->archive);
+
+ /* Pad or truncate file to the right size. */
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ /* There's no file. */
+ } else if (a->filesize < 0) {
+ /* File size is unknown, so we can't set the size. */
+ } else if (a->fd_offset == a->filesize) {
+ /* Last write ended at exactly the filesize; we're done. */
+ /* Hopefully, this is the common case. */
+ } else {
+ if (la_ftruncate(a->fh, a->filesize) == -1) {
+ archive_set_error(&a->archive, errno,
+ "File size could not be restored");
+ CloseHandle(a->fh);
+ a->fh = INVALID_HANDLE_VALUE;
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ /* Restore metadata. */
+
+ /*
+ * Look up the "real" UID only if we're going to need it.
+ * TODO: the TODO_SGID condition can be dropped here, can't it?
+ */
+ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
+ a->uid = archive_write_disk_uid(&a->archive,
+ archive_entry_uname(a->entry),
+ archive_entry_uid(a->entry));
+ }
+ /* Look up the "real" GID only if we're going to need it. */
+ /* TODO: the TODO_SUID condition can be dropped here, can't it? */
+ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
+ a->gid = archive_write_disk_gid(&a->archive,
+ archive_entry_gname(a->entry),
+ archive_entry_gid(a->entry));
+ }
+
+ /*
+ * Restore ownership before set_mode tries to restore suid/sgid
+ * bits. If we set the owner, we know what it is and can skip
+ * a stat() call to examine the ownership of the file on disk.
+ */
+ if (a->todo & TODO_OWNER)
+ ret = set_ownership(a);
+
+ /*
+ * set_mode must precede ACLs on systems such as Solaris and
+ * FreeBSD where setting the mode implicitly clears extended ACLs
+ */
+ if (a->todo & TODO_MODE) {
+ int r2 = set_mode(a, a->mode);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Security-related extended attributes (such as
+ * security.capability on Linux) have to be restored last,
+ * since they're implicitly removed by other file changes.
+ */
+ if (a->todo & TODO_XATTR) {
+ int r2 = set_xattrs(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Some flags prevent file modification; they must be restored after
+ * file contents are written.
+ */
+ if (a->todo & TODO_FFLAGS) {
+ int r2 = set_fflags(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * Time must follow most other metadata;
+ * otherwise atime will get changed.
+ */
+ if (a->todo & TODO_TIMES) {
+ int r2 = set_times_from_entry(a);
+ if (r2 < ret) ret = r2;
+ }
+
+ /*
+ * ACLs must be restored after timestamps because there are
+ * ACLs that prevent attribute changes (including time).
+ */
+ if (a->todo & TODO_ACLS) {
+ int r2 = set_acls(a, a->fh,
+ archive_entry_pathname_w(a->entry),
+ archive_entry_acl(a->entry));
+ if (r2 < ret) ret = r2;
+ }
+
+ /* If there's an fd, we can close it now. */
+ if (a->fh != INVALID_HANDLE_VALUE) {
+ CloseHandle(a->fh);
+ a->fh = INVALID_HANDLE_VALUE;
+ if (a->tmpname) {
+ /* Windows does not support atomic rename */
+ disk_unlink(a->name);
+ if (_wrename(a->tmpname, a->name) != 0) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Failed to rename temporary file");
+ ret = ARCHIVE_FAILED;
+ disk_unlink(a->tmpname);
+ }
+ a->tmpname = NULL;
+ }
+ }
+ /* If there's an entry, we can release it now. */
+ archive_entry_free(a->entry);
+ a->entry = NULL;
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ return (ret);
+}
+
+int
+archive_write_disk_set_group_lookup(struct archive *_a,
+ void *private_data,
+ la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid),
+ void (*cleanup_gid)(void *private))
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");
+
+ if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)
+ (a->cleanup_gid)(a->lookup_gid_data);
+
+ a->lookup_gid = lookup_gid;
+ a->cleanup_gid = cleanup_gid;
+ a->lookup_gid_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_user_lookup(struct archive *_a,
+ void *private_data,
+ int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid),
+ void (*cleanup_uid)(void *private))
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");
+
+ if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)
+ (a->cleanup_uid)(a->lookup_uid_data);
+
+ a->lookup_uid = lookup_uid;
+ a->cleanup_uid = cleanup_uid;
+ a->lookup_uid_data = private_data;
+ return (ARCHIVE_OK);
+}
+
+int64_t
+archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_gid");
+ if (a->lookup_gid)
+ return (a->lookup_gid)(a->lookup_gid_data, name, id);
+ return (id);
+}
+
+int64_t
+archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY, "archive_write_disk_uid");
+ if (a->lookup_uid)
+ return (a->lookup_uid)(a->lookup_uid_data, name, id);
+ return (id);
+}
+
+/*
+ * Create a new archive_write_disk object and initialize it with global state.
+ */
+struct archive *
+archive_write_disk_new(void)
+{
+ struct archive_write_disk *a;
+
+ a = (struct archive_write_disk *)calloc(1, sizeof(*a));
+ if (a == NULL)
+ return (NULL);
+ a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
+ /* We're ready to write a header immediately. */
+ a->archive.state = ARCHIVE_STATE_HEADER;
+ a->archive.vtable = &archive_write_disk_vtable;
+ a->start_time = time(NULL);
+ /* Query and restore the umask. */
+ umask(a->user_umask = umask(0));
+ if (archive_wstring_ensure(&a->path_safe, 512) == NULL) {
+ free(a);
+ return (NULL);
+ }
+ a->path_safe.s[0] = 0;
+ return (&a->archive);
+}
+
+static int
+disk_unlink(const wchar_t *path)
+{
+ wchar_t *fullname;
+ int r;
+
+ r = _wunlink(path);
+ if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(path);
+ r = _wunlink(fullname);
+ free(fullname);
+ }
+ return (r);
+}
+
+static int
+disk_rmdir(const wchar_t *path)
+{
+ wchar_t *fullname;
+ int r;
+
+ r = _wrmdir(path);
+ if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(path);
+ r = _wrmdir(fullname);
+ free(fullname);
+ }
+ return (r);
+}
+
+/*
+ * The main restore function.
+ */
+static int
+restore_entry(struct archive_write_disk *a)
+{
+ int ret = ARCHIVE_OK, en;
+
+ if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+ /*
+ * TODO: Fix this. Apparently, there are platforms
+ * that still allow root to hose the entire filesystem
+ * by unlinking a dir. The S_ISDIR() test above
+ * prevents us from using unlink() here if the new
+ * object is a dir, but that doesn't mean the old
+ * object isn't a dir.
+ */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+ if (disk_unlink(a->name) == 0) {
+ /* We removed it, reset cached stat. */
+ a->pst = NULL;
+ } else if (errno == ENOENT) {
+ /* File didn't exist, that's just as good. */
+ } else if (disk_rmdir(a->name) == 0) {
+ /* It was a dir, but now it's gone. */
+ a->pst = NULL;
+ } else {
+ /* We tried, but couldn't get rid of it. */
+ archive_set_error(&a->archive, errno,
+ "Could not unlink");
+ return(ARCHIVE_FAILED);
+ }
+ }
+
+ /* Try creating it first; if this fails, we'll try to recover. */
+ en = create_filesystem_object(a);
+
+ if ((en == ENOTDIR || en == ENOENT)
+ && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
+ wchar_t *full;
+ /* If the parent dir doesn't exist, try creating it. */
+ create_parent_dir(a, a->name);
+ /* Now try to create the object again. */
+ full = __la_win_permissive_name_w(a->name);
+ if (full == NULL) {
+ en = EINVAL;
+ } else {
+ /* Remove multiple directories such as "a/../b../c" */
+ archive_wstrcpy(&(a->_name_data), full);
+ a->name = a->_name_data.s;
+ free(full);
+ en = create_filesystem_object(a);
+ }
+ }
+
+ if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {
+ archive_set_error(&a->archive, en,
+ "Hard-link target '%s' does not exist.",
+ archive_entry_hardlink(a->entry));
+ return (ARCHIVE_FAILED);
+ }
+
+ if ((en == EISDIR || en == EEXIST)
+ && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ /* If we're not overwriting, we're done. */
+ if (S_ISDIR(a->mode)) {
+ /* Don't overwrite any settings on existing directories. */
+ a->todo = 0;
+ }
+ archive_entry_unset_size(a->entry);
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Some platforms return EISDIR if you call
+ * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
+ * return EEXIST. POSIX is ambiguous, requiring EISDIR
+ * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
+ * on an existing item.
+ */
+ if (en == EISDIR) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (disk_rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't remove already-existing dir");
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else if (en == EEXIST) {
+ mode_t st_mode;
+ mode_t lst_mode;
+ BY_HANDLE_FILE_INFORMATION lst;
+ /*
+ * We know something is in the way, but we don't know what;
+ * we need to find out before we go any further.
+ */
+ int r = 0;
+ int dirlnk = 0;
+
+ /*
+ * The SECURE_SYMLINK logic has already removed a
+ * symlink to a dir if the client wants that. So
+ * follow the symlink if we're creating a dir.
+ * If it's not a dir (or it's a broken symlink),
+ * then don't follow it.
+ *
+ * Windows distinguishes file and directory symlinks.
+ * A file symlink may erroneously point to a directory
+ * and a directory symlink to a file. Windows does not follow
+ * such symlinks. We always need both source and target
+ * information.
+ */
+ r = file_information(a, a->name, &lst, &lst_mode, 1);
+ if (r != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't stat existing object");
+ return (ARCHIVE_FAILED);
+ } else if (S_ISLNK(lst_mode)) {
+ if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ dirlnk = 1;
+ /* In case of a symlink we need target information */
+ r = file_information(a, a->name, &a->st, &st_mode, 0);
+ if (r != 0) {
+ a->st = lst;
+ st_mode = lst_mode;
+ }
+ } else {
+ a->st = lst;
+ st_mode = lst_mode;
+ }
+
+ /*
+ * NO_OVERWRITE_NEWER doesn't apply to directories.
+ */
+ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)
+ && !S_ISDIR(st_mode)) {
+ if (!older(&(a->st), a->entry)) {
+ archive_entry_unset_size(a->entry);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /* If it's our archive, we're done. */
+ if (a->skip_file_set &&
+ bhfi_dev(&a->st) == a->skip_file_dev &&
+ bhfi_ino(&a->st) == a->skip_file_ino) {
+ archive_set_error(&a->archive, 0,
+ "Refusing to overwrite archive");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!S_ISDIR(st_mode)) {
+ if (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&
+ S_ISREG(st_mode)) {
+ int fd = la_mktemp(a);
+
+ if (fd == -1) {
+ la_dosmaperr(GetLastError());
+ archive_set_error(&a->archive, errno,
+ "Can't create temporary file");
+ return (ARCHIVE_FAILED);
+ }
+ a->fh = (HANDLE)_get_osfhandle(fd);
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ en = 0;
+ } else {
+ if (dirlnk) {
+ /* Edge case: dir symlink pointing
+ * to a file */
+ if (disk_rmdir(a->name) != 0) {
+ archive_set_error(&a->archive,
+ errno, "Can't unlink "
+ "directory symlink");
+ return (ARCHIVE_FAILED);
+ }
+ } else {
+ if (disk_unlink(a->name) != 0) {
+ /* A non-dir is in the way,
+ * unlink it. */
+ archive_set_error(&a->archive,
+ errno, "Can't unlink "
+ "already-existing object");
+ return (ARCHIVE_FAILED);
+ }
+ }
+ a->pst = NULL;
+ /* Try again. */
+ en = create_filesystem_object(a);
+ }
+ } else if (!S_ISDIR(a->mode)) {
+ /* A dir is in the way of a non-dir, rmdir it. */
+ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+ (void)clear_nochange_fflags(a);
+ if (disk_rmdir(a->name) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't remove already-existing dir");
+ return (ARCHIVE_FAILED);
+ }
+ /* Try again. */
+ en = create_filesystem_object(a);
+ } else {
+ /*
+ * There's a dir in the way of a dir. Don't
+ * waste time with rmdir()/mkdir(), just fix
+ * up the permissions on the existing dir.
+ * Note that we don't change perms on existing
+ * dirs unless _EXTRACT_PERM is specified.
+ */
+ if ((a->mode != st_mode)
+ && (a->todo & TODO_MODE_FORCE))
+ a->deferred |= (a->todo & TODO_MODE);
+ /* Ownership doesn't need deferred fixup. */
+ en = 0; /* Forget the EEXIST. */
+ }
+ }
+
+ if (en) {
+ /* Everything failed; give up here. */
+ archive_set_error(&a->archive, en, "Can't create '%ls'",
+ a->name);
+ return (ARCHIVE_FAILED);
+ }
+
+ a->pst = NULL; /* Cached stat data no longer valid. */
+ return (ret);
+}
+
+/*
+ * Returns 0 if creation succeeds, or else returns errno value from
+ * the failed system call. Note: This function should only ever perform
+ * a single system call.
+ */
+static int
+create_filesystem_object(struct archive_write_disk *a)
+{
+ /* Create the entry. */
+ const wchar_t *linkname;
+ wchar_t *fullname;
+ mode_t final_mode, mode;
+ int r;
+ DWORD attrs = 0;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ /* We identify hard/symlinks according to the link names. */
+ /* Since link(2) and symlink(2) don't handle modes, we're done here. */
+ linkname = archive_entry_hardlink_w(a->entry);
+ if (linkname != NULL) {
+ wchar_t *linksanitized, *linkfull, *namefull;
+ size_t l = (wcslen(linkname) + 1) * sizeof(wchar_t);
+ linksanitized = malloc(l);
+ if (linksanitized == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for hardlink target");
+ return (-1);
+ }
+ memcpy(linksanitized, linkname, l);
+ r = cleanup_pathname(a, linksanitized);
+ if (r != ARCHIVE_OK) {
+ free(linksanitized);
+ return (r);
+ }
+ linkfull = __la_win_permissive_name_w(linksanitized);
+ free(linksanitized);
+ namefull = __la_win_permissive_name_w(a->name);
+ if (linkfull == NULL || namefull == NULL) {
+ errno = EINVAL;
+ r = -1;
+ } else {
+ /*
+ * Unlinking and linking here is really not atomic,
+ * but doing it right, would require us to construct
+ * an mktemplink() function, and then use _wrename().
+ */
+ if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) {
+ attrs = GetFileAttributesW(namefull);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ disk_rmdir(namefull);
+ else
+ disk_unlink(namefull);
+ }
+ }
+ r = la_CreateHardLinkW(namefull, linkfull);
+ if (r == 0) {
+ la_dosmaperr(GetLastError());
+ r = errno;
+ } else
+ r = 0;
+ }
+ /*
+ * New cpio and pax formats allow hardlink entries
+ * to carry data, so we may have to open the file
+ * for hardlink entries.
+ *
+ * If the hardlink was successfully created and
+ * the archive doesn't have carry data for it,
+ * consider it to be non-authoritative for meta data.
+ * This is consistent with GNU tar and BSD pax.
+ * If the hardlink does carry data, let the last
+ * archive entry decide ownership.
+ */
+ if (r == 0 && a->filesize <= 0) {
+ a->todo = 0;
+ a->deferred = 0;
+ } else if (r == 0 && a->filesize > 0) {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ a->fh = CreateFile2(namefull, GENERIC_WRITE, 0,
+ TRUNCATE_EXISTING, &createExParams);
+#else
+ a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL,
+ TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ la_dosmaperr(GetLastError());
+ r = errno;
+ }
+ }
+ free(linkfull);
+ free(namefull);
+ return (r);
+ }
+ linkname = archive_entry_symlink_w(a->entry);
+ if (linkname != NULL) {
+ /*
+ * Unlinking and linking here is really not atomic,
+ * but doing it right, would require us to construct
+ * an mktemplink() function, and then use _wrename().
+ */
+ attrs = GetFileAttributesW(a->name);
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+ disk_rmdir(a->name);
+ else
+ disk_unlink(a->name);
+ }
+#if HAVE_SYMLINK
+ return symlink(linkname, a->name) ? errno : 0;
+#else
+ errno = 0;
+ r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname,
+ archive_entry_symlink_type(a->entry));
+ if (r == 0) {
+ if (errno == 0)
+ la_dosmaperr(GetLastError());
+ r = errno;
+ } else
+ r = 0;
+ return (r);
+#endif
+ }
+
+ /*
+ * The remaining system calls all set permissions, so let's
+ * try to take advantage of that to avoid an extra chmod()
+ * call. (Recall that umask is set to zero right now!)
+ */
+
+ /* Mode we want for the final restored object (w/o file type bits). */
+ final_mode = a->mode & 07777;
+ /*
+ * The mode that will actually be restored in this step. Note
+ * that SUID, SGID, etc, require additional work to ensure
+ * security, so we never restore them at this point.
+ */
+ mode = final_mode & 0777 & ~a->user_umask;
+
+ switch (a->mode & AE_IFMT) {
+ default:
+ /* POSIX requires that we fall through here. */
+ /* FALLTHROUGH */
+ case AE_IFREG:
+ a->tmpname = NULL;
+ fullname = a->name;
+ /* O_WRONLY | O_CREAT | O_EXCL */
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+ CREATE_NEW, &createExParams);
+#else
+ a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+ if (a->fh == INVALID_HANDLE_VALUE &&
+ GetLastError() == ERROR_INVALID_NAME &&
+ fullname == a->name) {
+ fullname = __la_win_permissive_name_w(a->name);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+ CREATE_NEW, &createExParams);
+#else
+ a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+ }
+ if (a->fh == INVALID_HANDLE_VALUE) {
+ if (GetLastError() == ERROR_ACCESS_DENIED) {
+ DWORD attr;
+ /* Simulate an errno of POSIX system. */
+ attr = GetFileAttributesW(fullname);
+ if (attr == (DWORD)-1)
+ la_dosmaperr(GetLastError());
+ else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+ errno = EISDIR;
+ else
+ errno = EACCES;
+ } else
+ la_dosmaperr(GetLastError());
+ r = 1;
+ } else
+ r = 0;
+ if (fullname != a->name)
+ free(fullname);
+ break;
+ case AE_IFCHR:
+ case AE_IFBLK:
+ /* TODO: Find a better way to warn about our inability
+ * to restore a block device node. */
+ return (EINVAL);
+ case AE_IFDIR:
+ mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
+ fullname = a->name;
+ r = CreateDirectoryW(fullname, NULL);
+ if (r == 0 && GetLastError() == ERROR_INVALID_NAME &&
+ fullname == a->name) {
+ fullname = __la_win_permissive_name_w(a->name);
+ r = CreateDirectoryW(fullname, NULL);
+ }
+ if (r != 0) {
+ r = 0;
+ /* Defer setting dir times. */
+ a->deferred |= (a->todo & TODO_TIMES);
+ a->todo &= ~TODO_TIMES;
+ /* Never use an immediate chmod(). */
+ /* We can't avoid the chmod() entirely if EXTRACT_PERM
+ * because of SysV SGID inheritance. */
+ if ((mode != final_mode)
+ || (a->flags & ARCHIVE_EXTRACT_PERM))
+ a->deferred |= (a->todo & TODO_MODE);
+ a->todo &= ~TODO_MODE;
+ } else {
+ la_dosmaperr(GetLastError());
+ r = -1;
+ }
+ if (fullname != a->name)
+ free(fullname);
+ break;
+ case AE_IFIFO:
+ /* TODO: Find a better way to warn about our inability
+ * to restore a fifo. */
+ return (EINVAL);
+ }
+
+ /* All the system calls above set errno on failure. */
+ if (r)
+ return (errno);
+
+ /* If we managed to set the final mode, we've avoided a chmod(). */
+ if (mode == final_mode)
+ a->todo &= ~TODO_MODE;
+ return (0);
+}
+
+/*
+ * Cleanup function for archive_extract. Mostly, this involves processing
+ * the fixup list, which is used to address a number of problems:
+ * * Dir permissions might prevent us from restoring a file in that
+ * dir, so we restore the dir with minimum 0700 permissions first,
+ * then correct the mode at the end.
+ * * Similarly, the act of restoring a file touches the directory
+ * and changes the timestamp on the dir, so we have to touch-up dir
+ * timestamps at the end as well.
+ * * Some file flags can interfere with the restore by, for example,
+ * preventing the creation of hardlinks to those files.
+ * * Mac OS extended metadata includes ACLs, so must be deferred on dirs.
+ *
+ * Note that tar/cpio do not require that archives be in a particular
+ * order; there is no way to know when the last file has been restored
+ * within a directory, so there's no way to optimize the memory usage
+ * here by fixing up the directory any earlier than the
+ * end-of-archive.
+ *
+ * XXX TODO: Directory ACLs should be restored here, for the same
+ * reason we set directory perms here. XXX
+ */
+static int
+_archive_write_disk_close(struct archive *_a)
+{
+ struct archive_write_disk *a = (struct archive_write_disk *)_a;
+ struct fixup_entry *next, *p;
+ int ret;
+
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_disk_close");
+ ret = _archive_write_disk_finish_entry(&a->archive);
+
+ /* Sort dir list so directories are fixed up in depth-first order. */
+ p = sort_dir_list(a->fixup_list);
+
+ while (p != NULL) {
+ a->pst = NULL; /* Mark stat cache as out-of-date. */
+ if (p->fixup & TODO_TIMES) {
+ set_times(a, INVALID_HANDLE_VALUE, p->mode, p->name,
+ p->atime, p->atime_nanos,
+ p->birthtime, p->birthtime_nanos,
+ p->mtime, p->mtime_nanos,
+ p->ctime, p->ctime_nanos);
+ }
+ if (p->fixup & TODO_MODE_BASE)
+ la_chmod(p->name, p->mode);
+ if (p->fixup & TODO_ACLS)
+ set_acls(a, INVALID_HANDLE_VALUE, p->name, &p->acl);
+ if (p->fixup & TODO_FFLAGS)
+ set_fflags_platform(p->name, p->fflags_set, 0);
+ next = p->next;
+ archive_acl_clear(&p->acl);
+ free(p->name);
+ free(p);
+ p = next;
+ }
+ a->fixup_list = NULL;
+ return (ret);
+}
+
+static int
+_archive_write_disk_free(struct archive *_a)
+{
+ struct archive_write_disk *a;
+ int ret;
+ if (_a == NULL)
+ return (ARCHIVE_OK);
+ archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC,
+ ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free");
+ a = (struct archive_write_disk *)_a;
+ ret = _archive_write_disk_close(&a->archive);
+ archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL);
+ archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);
+ archive_entry_free(a->entry);
+ archive_wstring_free(&a->_name_data);
+ archive_wstring_free(&a->_tmpname_data);
+ archive_string_free(&a->archive.error_string);
+ archive_wstring_free(&a->path_safe);
+ a->archive.magic = 0;
+ __archive_clean(&a->archive);
+ free(a);
+ return (ret);
+}
+
+/*
+ * Simple O(n log n) merge sort to order the fixup list. In
+ * particular, we want to restore dir timestamps depth-first.
+ */
+static struct fixup_entry *
+sort_dir_list(struct fixup_entry *p)
+{
+ struct fixup_entry *a, *b, *t;
+
+ if (p == NULL)
+ return (NULL);
+ /* A one-item list is already sorted. */
+ if (p->next == NULL)
+ return (p);
+
+ /* Step 1: split the list. */
+ t = p;
+ a = p->next->next;
+ while (a != NULL) {
+ /* Step a twice, t once. */
+ a = a->next;
+ if (a != NULL)
+ a = a->next;
+ t = t->next;
+ }
+ /* Now, t is at the mid-point, so break the list here. */
+ b = t->next;
+ t->next = NULL;
+ a = p;
+
+ /* Step 2: Recursively sort the two sub-lists. */
+ a = sort_dir_list(a);
+ b = sort_dir_list(b);
+
+ /* Step 3: Merge the returned lists. */
+ /* Pick the first element for the merged list. */
+ if (wcscmp(a->name, b->name) > 0) {
+ t = p = a;
+ a = a->next;
+ } else {
+ t = p = b;
+ b = b->next;
+ }
+
+ /* Always put the later element on the list first. */
+ while (a != NULL && b != NULL) {
+ if (wcscmp(a->name, b->name) > 0) {
+ t->next = a;
+ a = a->next;
+ } else {
+ t->next = b;
+ b = b->next;
+ }
+ t = t->next;
+ }
+
+ /* Only one list is non-empty, so just splice it on. */
+ if (a != NULL)
+ t->next = a;
+ if (b != NULL)
+ t->next = b;
+
+ return (p);
+}
+
+/*
+ * Returns a new, initialized fixup entry.
+ *
+ * TODO: Reduce the memory requirements for this list by using a tree
+ * structure rather than a simple list of names.
+ */
+static struct fixup_entry *
+new_fixup(struct archive_write_disk *a, const wchar_t *pathname)
+{
+ struct fixup_entry *fe;
+
+ fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));
+ if (fe == NULL)
+ return (NULL);
+ fe->next = a->fixup_list;
+ a->fixup_list = fe;
+ fe->fixup = 0;
+ fe->name = _wcsdup(pathname);
+ fe->fflags_set = 0;
+ return (fe);
+}
+
+/*
+ * Returns a fixup structure for the current entry.
+ */
+static struct fixup_entry *
+current_fixup(struct archive_write_disk *a, const wchar_t *pathname)
+{
+ if (a->current_fixup == NULL)
+ a->current_fixup = new_fixup(a, pathname);
+ return (a->current_fixup);
+}
+
+/*
+ * TODO: The deep-directory support bypasses this; disable deep directory
+ * support if we're doing symlink checks.
+ */
+/*
+ * TODO: Someday, integrate this with the deep dir support; they both
+ * scan the path and both can be optimized by comparing against other
+ * recent paths.
+ */
+static int
+check_symlinks(struct archive_write_disk *a)
+{
+ wchar_t *pn, *p;
+ wchar_t c;
+ int r;
+ BY_HANDLE_FILE_INFORMATION st;
+ mode_t st_mode;
+
+ /*
+ * Guard against symlink tricks. Reject any archive entry whose
+ * destination would be altered by a symlink.
+ */
+ /* Whatever we checked last time doesn't need to be re-checked. */
+ pn = a->name;
+ p = a->path_safe.s;
+ while ((*pn != '\0') && (*p == *pn))
+ ++p, ++pn;
+ /* Skip leading backslashes */
+ while (*pn == '\\')
+ ++pn;
+ c = pn[0];
+ /* Keep going until we've checked the entire name. */
+ while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) {
+ /* Skip the next path element. */
+ while (*pn != '\0' && *pn != '\\')
+ ++pn;
+ c = pn[0];
+ pn[0] = '\0';
+ /* Check that we haven't hit a symlink. */
+ r = file_information(a, a->name, &st, &st_mode, 1);
+ if (r != 0) {
+ /* We've hit a dir that doesn't exist; stop now. */
+ if (errno == ENOENT)
+ break;
+ } else if (S_ISLNK(st_mode)) {
+ if (c == '\0') {
+ /*
+ * Last element is a file or directory symlink.
+ * Remove it so we can overwrite it with the
+ * item being extracted.
+ */
+ if (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ if (st.dwFileAttributes &
+ FILE_ATTRIBUTE_DIRECTORY) {
+ r = disk_rmdir(a->name);
+ } else {
+ r = disk_unlink(a->name);
+ }
+ if (r) {
+ archive_set_error(&a->archive, errno,
+ "Could not remove symlink %ls",
+ a->name);
+ pn[0] = c;
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ /*
+ * Even if we did remove it, a warning
+ * is in order. The warning is silly,
+ * though, if we're just replacing one
+ * symlink with another symlink.
+ */
+ if (!S_ISLNK(a->mode)) {
+ archive_set_error(&a->archive, 0,
+ "Removing symlink %ls",
+ a->name);
+ }
+ /* Symlink gone. No more problem! */
+ pn[0] = c;
+ return (0);
+ } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
+ /* User asked us to remove problems. */
+ if (a->flags &
+ ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+ (void)clear_nochange_fflags(a);
+ }
+ if (st.dwFileAttributes &
+ FILE_ATTRIBUTE_DIRECTORY) {
+ r = disk_rmdir(a->name);
+ } else {
+ r = disk_unlink(a->name);
+ }
+ if (r != 0) {
+ archive_set_error(&a->archive, 0,
+ "Cannot remove intervening "
+ "symlink %ls", a->name);
+ pn[0] = c;
+ return (ARCHIVE_FAILED);
+ }
+ a->pst = NULL;
+ } else {
+ archive_set_error(&a->archive, 0,
+ "Cannot extract through symlink %ls",
+ a->name);
+ pn[0] = c;
+ return (ARCHIVE_FAILED);
+ }
+ }
+ if (!c)
+ break;
+ pn[0] = c;
+ pn++;
+ }
+ pn[0] = c;
+ /* We've checked and/or cleaned the whole path, so remember it. */
+ archive_wstrcpy(&a->path_safe, a->name);
+ return (ARCHIVE_OK);
+}
+
+static int
+guidword(wchar_t *p, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if ((*p >= L'0' && *p <= L'9') ||
+ (*p >= L'a' && *p <= L'f') ||
+ (*p >= L'A' && *p <= L'F'))
+ p++;
+ else
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Canonicalize the pathname. In particular, this strips duplicate
+ * '\' characters, '.' elements, and trailing '\'. It also raises an
+ * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is
+ * set) any '..' in the path.
+ */
+static int
+cleanup_pathname(struct archive_write_disk *a, wchar_t *name)
+{
+ wchar_t *dest, *src, *p, *top;
+ wchar_t separator = L'\0';
+
+ p = name;
+ if (*p == L'\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid empty pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Replace '/' by '\' */
+ for (; *p != L'\0'; p++) {
+ if (*p == L'/')
+ *p = L'\\';
+ }
+ p = name;
+
+ /* Skip leading "\\.\" or "\\?\" or "\\?\UNC\" or
+ * "\\?\Volume{GUID}\"
+ * (absolute path prefixes used by Windows API) */
+ if (p[0] == L'\\' && p[1] == L'\\' &&
+ (p[2] == L'.' || p[2] == L'?') && p[3] == L'\\')
+ {
+ /* A path begin with "\\?\UNC\" */
+ if (p[2] == L'?' &&
+ (p[4] == L'U' || p[4] == L'u') &&
+ (p[5] == L'N' || p[5] == L'n') &&
+ (p[6] == L'C' || p[6] == L'c') &&
+ p[7] == L'\\')
+ p += 8;
+ /* A path begin with "\\?\Volume{GUID}\" */
+ else if (p[2] == L'?' &&
+ (p[4] == L'V' || p[4] == L'v') &&
+ (p[5] == L'O' || p[5] == L'o') &&
+ (p[6] == L'L' || p[6] == L'l') &&
+ (p[7] == L'U' || p[7] == L'u') &&
+ (p[8] == L'M' || p[8] == L'm') &&
+ (p[9] == L'E' || p[9] == L'e') &&
+ p[10] == L'{') {
+ if (guidword(p+11, 8) == 0 && p[19] == L'-' &&
+ guidword(p+20, 4) == 0 && p[24] == L'-' &&
+ guidword(p+25, 4) == 0 && p[29] == L'-' &&
+ guidword(p+30, 4) == 0 && p[34] == L'-' &&
+ guidword(p+35, 12) == 0 && p[47] == L'}' &&
+ p[48] == L'\\')
+ p += 49;
+ else
+ p += 4;
+ /* A path begin with "\\.\PhysicalDriveX" */
+ } else if (p[2] == L'.' &&
+ (p[4] == L'P' || p[4] == L'p') &&
+ (p[5] == L'H' || p[5] == L'h') &&
+ (p[6] == L'Y' || p[6] == L'y') &&
+ (p[7] == L'S' || p[7] == L's') &&
+ (p[8] == L'I' || p[8] == L'i') &&
+ (p[9] == L'C' || p[9] == L'c') &&
+ (p[9] == L'A' || p[9] == L'a') &&
+ (p[9] == L'L' || p[9] == L'l') &&
+ (p[9] == L'D' || p[9] == L'd') &&
+ (p[9] == L'R' || p[9] == L'r') &&
+ (p[9] == L'I' || p[9] == L'i') &&
+ (p[9] == L'V' || p[9] == L'v') &&
+ (p[9] == L'E' || p[9] == L'e') &&
+ (p[10] >= L'0' && p[10] <= L'9') &&
+ p[11] == L'\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Path is a physical drive name");
+ return (ARCHIVE_FAILED);
+ } else
+ p += 4;
+ /* Network drive path like "\\<server-name>\<share-name>\file" */
+ } else if (p[0] == L'\\' && p[1] == L'\\') {
+ p += 2;
+ }
+
+ /* Skip leading drive letter from archives created
+ * on Windows. */
+ if (((p[0] >= L'a' && p[0] <= L'z') ||
+ (p[0] >= L'A' && p[0] <= L'Z')) &&
+ p[1] == L':') {
+ if (p[2] == L'\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Path is a drive name");
+ return (ARCHIVE_FAILED);
+ }
+ if (p[2] == L'\\')
+ p += 2;
+ }
+
+ top = dest = src = p;
+ /* Rewrite the path name if its character is a unusable. */
+ for (; *p != L'\0'; p++) {
+ if (*p == L':' || *p == L'*' || *p == L'?' || *p == L'"' ||
+ *p == L'<' || *p == L'>' || *p == L'|')
+ *p = L'_';
+ }
+ /* Skip leading '\'. */
+ if (*src == L'\\')
+ separator = *src++;
+
+ /* Scan the pathname one element at a time. */
+ for (;;) {
+ /* src points to first char after '\' */
+ if (src[0] == L'\0') {
+ break;
+ } else if (src[0] == L'\\') {
+ /* Found '\\'('//'), ignore second one. */
+ src++;
+ continue;
+ } else if (src[0] == L'.') {
+ if (src[1] == L'\0') {
+ /* Ignore trailing '.' */
+ break;
+ } else if (src[1] == L'\\') {
+ /* Skip '.\'. */
+ src += 2;
+ continue;
+ } else if (src[1] == L'.') {
+ if (src[2] == L'\\' || src[2] == L'\0') {
+ /* Conditionally warn about '..' */
+ if (a->flags &
+ ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Path contains '..'");
+ return (ARCHIVE_FAILED);
+ }
+ }
+ /*
+ * Note: Under no circumstances do we
+ * remove '..' elements. In
+ * particular, restoring
+ * '\foo\..\bar\' should create the
+ * 'foo' dir as a side-effect.
+ */
+ }
+ }
+
+ /* Copy current element, including leading '\'. */
+ if (separator)
+ *dest++ = L'\\';
+ while (*src != L'\0' && *src != L'\\') {
+ *dest++ = *src++;
+ }
+
+ if (*src == L'\0')
+ break;
+
+ /* Skip '\' separator. */
+ separator = *src++;
+ }
+ /*
+ * We've just copied zero or more path elements, not including the
+ * final '\'.
+ */
+ if (dest == top) {
+ /*
+ * Nothing got copied. The path must have been something
+ * like '.' or '\' or './' or '/././././/./'.
+ */
+ if (separator)
+ *dest++ = L'\\';
+ else
+ *dest++ = L'.';
+ }
+ /* Terminate the result. */
+ *dest = L'\0';
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+ */
+static int
+create_parent_dir(struct archive_write_disk *a, wchar_t *path)
+{
+ wchar_t *slash;
+ int r;
+
+ /* Remove tail element to obtain parent name. */
+ slash = wcsrchr(path, L'\\');
+ if (slash == NULL)
+ return (ARCHIVE_OK);
+ *slash = L'\0';
+ r = create_dir(a, path);
+ *slash = L'\\';
+ return (r);
+}
+
+/*
+ * Create the specified dir, recursing to create parents as necessary.
+ *
+ * Returns ARCHIVE_OK if the path exists when we're done here.
+ * Otherwise, returns ARCHIVE_FAILED.
+ * Assumes path is in mutable storage; path is unchanged on exit.
+ */
+static int
+create_dir(struct archive_write_disk *a, wchar_t *path)
+{
+ BY_HANDLE_FILE_INFORMATION st;
+ struct fixup_entry *le;
+ wchar_t *slash, *base, *full;
+ mode_t mode_final, mode, st_mode;
+ int r;
+
+ /* Check for special names and just skip them. */
+ slash = wcsrchr(path, L'\\');
+ if (slash == NULL)
+ base = path;
+ else
+ base = slash + 1;
+
+ if (base[0] == L'\0' ||
+ (base[0] == L'.' && base[1] == L'\0') ||
+ (base[0] == L'.' && base[1] == L'.' && base[2] == L'\0')) {
+ /* Don't bother trying to create null path, '.', or '..'. */
+ if (slash != NULL) {
+ *slash = L'\0';
+ r = create_dir(a, path);
+ *slash = L'\\';
+ return (r);
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /*
+ * Yes, this should be stat() and not lstat(). Using lstat()
+ * here loses the ability to extract through symlinks. Also note
+ * that this should not use the a->st cache.
+ */
+ if (file_information(a, path, &st, &st_mode, 0) == 0) {
+ if (S_ISDIR(st_mode))
+ return (ARCHIVE_OK);
+ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+ archive_set_error(&a->archive, EEXIST,
+ "Can't create directory '%ls'", path);
+ return (ARCHIVE_FAILED);
+ }
+ if (disk_unlink(path) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't create directory '%ls': "
+ "Conflicting file cannot be removed",
+ path);
+ return (ARCHIVE_FAILED);
+ }
+ } else if (errno != ENOENT && errno != ENOTDIR) {
+ /* Stat failed? */
+ archive_set_error(&a->archive, errno,
+ "Can't test directory '%ls'", path);
+ return (ARCHIVE_FAILED);
+ } else if (slash != NULL) {
+ *slash = '\0';
+ r = create_dir(a, path);
+ *slash = '\\';
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ /*
+ * Mode we want for the final restored directory. Per POSIX,
+ * implicitly-created dirs must be created obeying the umask.
+ * There's no mention whether this is different for privileged
+ * restores (which the rest of this code handles by pretending
+ * umask=0). I've chosen here to always obey the user's umask for
+ * implicit dirs, even if _EXTRACT_PERM was specified.
+ */
+ mode_final = DEFAULT_DIR_MODE & ~a->user_umask;
+ /* Mode we want on disk during the restore process. */
+ mode = mode_final;
+ mode |= MINIMUM_DIR_MODE;
+ mode &= MAXIMUM_DIR_MODE;
+ /*
+ * Apply __la_win_permissive_name_w to path in order to
+ * remove '../' path string.
+ */
+ full = __la_win_permissive_name_w(path);
+ if (full == NULL)
+ errno = EINVAL;
+ else if (CreateDirectoryW(full, NULL) != 0) {
+ if (mode != mode_final) {
+ le = new_fixup(a, path);
+ le->fixup |=TODO_MODE_BASE;
+ le->mode = mode_final;
+ }
+ free(full);
+ return (ARCHIVE_OK);
+ } else {
+ la_dosmaperr(GetLastError());
+ }
+ free(full);
+
+ /*
+ * Without the following check, a/b/../b/c/d fails at the
+ * second visit to 'b', so 'd' can't be created. Note that we
+ * don't add it to the fixup list here, as it's already been
+ * added.
+ */
+ if (file_information(a, path, &st, &st_mode, 0) == 0 &&
+ S_ISDIR(st_mode))
+ return (ARCHIVE_OK);
+
+ archive_set_error(&a->archive, errno, "Failed to create dir '%ls'",
+ path);
+ return (ARCHIVE_FAILED);
+}
+
+/*
+ * Note: Although we can skip setting the user id if the desired user
+ * id matches the current user, we cannot skip setting the group, as
+ * many systems set the gid based on the containing directory. So
+ * we have to perform a chown syscall if we want to set the SGID
+ * bit. (The alternative is to stat() and then possibly chown(); it's
+ * more efficient to skip the stat() and just always chown().) Note
+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which
+ * allows set_mode to skip the stat() check for the GID.
+ */
+static int
+set_ownership(struct archive_write_disk *a)
+{
+/* unfortunately, on win32 there is no 'root' user with uid 0,
+ so we just have to try the chown and see if it works */
+
+ /* If we know we can't change it, don't bother trying. */
+ if (a->user_uid != 0 && a->user_uid != a->uid) {
+ archive_set_error(&a->archive, errno,
+ "Can't set UID=%jd", (intmax_t)a->uid);
+ return (ARCHIVE_WARN);
+ }
+
+ archive_set_error(&a->archive, errno,
+ "Can't set user=%jd/group=%jd for %ls",
+ (intmax_t)a->uid, (intmax_t)a->gid, a->name);
+ return (ARCHIVE_WARN);
+}
+
+static int
+set_times(struct archive_write_disk *a,
+ HANDLE h, int mode, const wchar_t *name,
+ time_t atime, long atime_nanos,
+ time_t birthtime, long birthtime_nanos,
+ time_t mtime, long mtime_nanos,
+ time_t ctime_sec, long ctime_nanos)
+{
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\
+ + (((nsec)/1000)*10))
+
+ HANDLE hw = 0;
+ ULARGE_INTEGER wintm;
+ FILETIME *pfbtime;
+ FILETIME fatime, fbtime, fmtime;
+
+ (void)ctime_sec; /* UNUSED */
+ (void)ctime_nanos; /* UNUSED */
+
+ if (h != INVALID_HANDLE_VALUE) {
+ hw = NULL;
+ } else {
+ wchar_t *ws;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+ if (S_ISLNK(mode))
+ return (ARCHIVE_OK);
+ ws = __la_win_permissive_name_w(name);
+ if (ws == NULL)
+ goto settimes_failed;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+ ZeroMemory(&createExParams, sizeof(createExParams));
+ createExParams.dwSize = sizeof(createExParams);
+ createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+ hw = CreateFile2(ws, FILE_WRITE_ATTRIBUTES, 0,
+ OPEN_EXISTING, &createExParams);
+#else
+ hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES,
+ 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+ free(ws);
+ if (hw == INVALID_HANDLE_VALUE)
+ goto settimes_failed;
+ h = hw;
+ }
+
+ wintm.QuadPart = WINTIME(atime, atime_nanos);
+ fatime.dwLowDateTime = wintm.LowPart;
+ fatime.dwHighDateTime = wintm.HighPart;
+ wintm.QuadPart = WINTIME(mtime, mtime_nanos);
+ fmtime.dwLowDateTime = wintm.LowPart;
+ fmtime.dwHighDateTime = wintm.HighPart;
+ /*
+ * SetFileTime() supports birthtime.
+ */
+ if (birthtime > 0 || birthtime_nanos > 0) {
+ wintm.QuadPart = WINTIME(birthtime, birthtime_nanos);
+ fbtime.dwLowDateTime = wintm.LowPart;
+ fbtime.dwHighDateTime = wintm.HighPart;
+ pfbtime = &fbtime;
+ } else
+ pfbtime = NULL;
+ if (SetFileTime(h, pfbtime, &fatime, &fmtime) == 0)
+ goto settimes_failed;
+ CloseHandle(hw);
+ return (ARCHIVE_OK);
+
+settimes_failed:
+ CloseHandle(hw);
+ archive_set_error(&a->archive, EINVAL, "Can't restore time");
+ return (ARCHIVE_WARN);
+}
+
+static int
+set_times_from_entry(struct archive_write_disk *a)
+{
+ time_t atime, birthtime, mtime, ctime_sec;
+ long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;
+
+ /* Suitable defaults. */
+ atime = birthtime = mtime = ctime_sec = a->start_time;
+ atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;
+
+ /* If no time was provided, we're done. */
+ if (!archive_entry_atime_is_set(a->entry)
+ && !archive_entry_birthtime_is_set(a->entry)
+ && !archive_entry_mtime_is_set(a->entry))
+ return (ARCHIVE_OK);
+
+ if (archive_entry_atime_is_set(a->entry)) {
+ atime = archive_entry_atime(a->entry);
+ atime_nsec = archive_entry_atime_nsec(a->entry);
+ }
+ if (archive_entry_birthtime_is_set(a->entry)) {
+ birthtime = archive_entry_birthtime(a->entry);
+ birthtime_nsec = archive_entry_birthtime_nsec(a->entry);
+ }
+ if (archive_entry_mtime_is_set(a->entry)) {
+ mtime = archive_entry_mtime(a->entry);
+ mtime_nsec = archive_entry_mtime_nsec(a->entry);
+ }
+ if (archive_entry_ctime_is_set(a->entry)) {
+ ctime_sec = archive_entry_ctime(a->entry);
+ ctime_nsec = archive_entry_ctime_nsec(a->entry);
+ }
+
+ return set_times(a, a->fh, a->mode, a->name,
+ atime, atime_nsec,
+ birthtime, birthtime_nsec,
+ mtime, mtime_nsec,
+ ctime_sec, ctime_nsec);
+}
+
+static int
+set_mode(struct archive_write_disk *a, int mode)
+{
+ int r = ARCHIVE_OK;
+ mode &= 07777; /* Strip off file type bits. */
+
+ if (a->todo & TODO_SGID_CHECK) {
+ /*
+ * If we don't know the GID is right, we must stat()
+ * to verify it. We can't just check the GID of this
+ * process, since systems sometimes set GID from
+ * the enclosing dir or based on ACLs.
+ */
+ if ((r = lazy_stat(a)) != ARCHIVE_OK)
+ return (r);
+ if (0 != a->gid) {
+ mode &= ~ S_ISGID;
+ }
+ /* While we're here, double-check the UID. */
+ if (0 != a->uid
+ && (a->todo & TODO_SUID)) {
+ mode &= ~ S_ISUID;
+ }
+ a->todo &= ~TODO_SGID_CHECK;
+ a->todo &= ~TODO_SUID_CHECK;
+ } else if (a->todo & TODO_SUID_CHECK) {
+ /*
+ * If we don't know the UID is right, we can just check
+ * the user, since all systems set the file UID from
+ * the process UID.
+ */
+ if (a->user_uid != a->uid) {
+ mode &= ~ S_ISUID;
+ }
+ a->todo &= ~TODO_SUID_CHECK;
+ }
+
+ if (S_ISLNK(a->mode)) {
+#ifdef HAVE_LCHMOD
+ /*
+ * If this is a symlink, use lchmod(). If the
+ * platform doesn't support lchmod(), just skip it. A
+ * platform that doesn't provide a way to set
+ * permissions on symlinks probably ignores
+ * permissions on symlinks, so a failure here has no
+ * impact.
+ */
+ if (lchmod(a->name, mode) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+#endif
+ } else if (!S_ISDIR(a->mode)) {
+ /*
+ * If it's not a symlink and not a dir, then use
+ * fchmod() or chmod(), depending on whether we have
+ * an fd. Dirs get their perms set during the
+ * post-extract fixup, which is handled elsewhere.
+ */
+#ifdef HAVE_FCHMOD
+ if (a->fd >= 0) {
+ if (fchmod(a->fd, mode) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+ } else
+#endif
+ /* If this platform lacks fchmod(), then
+ * we'll just use chmod(). */
+ if (la_chmod(a->name, mode) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't set permissions to 0%o", (int)mode);
+ r = ARCHIVE_WARN;
+ }
+ }
+ return (r);
+}
+
+static int set_fflags_platform(const wchar_t *name, unsigned long fflags_set,
+ unsigned long fflags_clear)
+{
+ DWORD oldflags, newflags;
+ wchar_t *fullname;
+
+ const DWORD settable_flags =
+ FILE_ATTRIBUTE_ARCHIVE |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_NORMAL |
+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
+ FILE_ATTRIBUTE_OFFLINE |
+ FILE_ATTRIBUTE_READONLY |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_TEMPORARY;
+
+ oldflags = GetFileAttributesW(name);
+ if (oldflags == (DWORD)-1 &&
+ GetLastError() == ERROR_INVALID_NAME) {
+ fullname = __la_win_permissive_name_w(name);
+ oldflags = GetFileAttributesW(fullname);
+ }
+ if (oldflags == (DWORD)-1) {
+ la_dosmaperr(GetLastError());
+ return (ARCHIVE_WARN);
+ }
+ newflags = ((oldflags & ~fflags_clear) | fflags_set) & settable_flags;
+ if(SetFileAttributesW(name, newflags) == 0)
+ return (ARCHIVE_WARN);
+ return (ARCHIVE_OK);
+}
+
+static int
+clear_nochange_fflags(struct archive_write_disk *a)
+{
+ return (set_fflags_platform(a->name, 0, FILE_ATTRIBUTE_READONLY));
+}
+
+static int
+set_fflags(struct archive_write_disk *a)
+{
+ unsigned long set, clear;
+
+ if (a->todo & TODO_FFLAGS) {
+ archive_entry_fflags(a->entry, &set, &clear);
+ if (set == 0 && clear == 0)
+ return (ARCHIVE_OK);
+ return (set_fflags_platform(a->name, set, clear));
+
+ }
+ return (ARCHIVE_OK);
+}
+
+/* Default empty function body to satisfy mainline code. */
+static int
+set_acls(struct archive_write_disk *a, HANDLE h, const wchar_t *name,
+ struct archive_acl *acl)
+{
+ (void)a; /* UNUSED */
+ (void)h; /* UNUSED */
+ (void)name; /* UNUSED */
+ (void)acl; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Restore extended attributes - stub implementation for unsupported systems
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+ static int warning_done = 0;
+
+ /* If there aren't any extended attributes, then it's okay not
+ * to extract them, otherwise, issue a single warning. */
+ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {
+ warning_done = 1;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Cannot restore extended attributes on this system");
+ return (ARCHIVE_WARN);
+ }
+ /* Warning was already emitted; suppress further warnings. */
+ return (ARCHIVE_OK);
+}
+
+static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+ ULARGE_INTEGER utc;
+
+ utc.HighPart = filetime->dwHighDateTime;
+ utc.LowPart = filetime->dwLowDateTime;
+ if (utc.QuadPart >= EPOC_TIME) {
+ utc.QuadPart -= EPOC_TIME;
+ /* milli seconds base */
+ *t = (time_t)(utc.QuadPart / 10000000);
+ /* nano seconds base */
+ *ns = (long)(utc.QuadPart % 10000000) * 100;
+ } else {
+ *t = 0;
+ *ns = 0;
+ }
+}
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+older(BY_HANDLE_FILE_INFORMATION *st, struct archive_entry *entry)
+{
+ time_t sec;
+ long nsec;
+
+ fileTimeToUtc(&st->ftLastWriteTime, &sec, &nsec);
+ /* First, test the seconds and return if we have a definite answer. */
+ /* Definitely older. */
+ if (sec < archive_entry_mtime(entry))
+ return (1);
+ /* Definitely younger. */
+ if (sec > archive_entry_mtime(entry))
+ return (0);
+ if (nsec < archive_entry_mtime_nsec(entry))
+ return (1);
+ /* Same age or newer, so not older. */
+ return (0);
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_open_fd.c b/contrib/libs/libarchive/libarchive/archive_write_open_fd.c
new file mode 100644
index 0000000000..b8d491faa2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_open_fd.c
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_fd.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct write_fd_data {
+ int fd;
+};
+
+static int file_free(struct archive *, void *);
+static int file_open(struct archive *, void *);
+static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
+
+int
+archive_write_open_fd(struct archive *a, int fd)
+{
+ struct write_fd_data *mine;
+
+ mine = (struct write_fd_data *)malloc(sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->fd = fd;
+#if defined(__CYGWIN__) || defined(_WIN32)
+ setmode(mine->fd, O_BINARY);
+#endif
+ return (archive_write_open2(a, mine,
+ file_open, file_write, NULL, file_free));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+ struct write_fd_data *mine;
+ struct stat st;
+
+ mine = (struct write_fd_data *)client_data;
+
+ if (fstat(mine->fd, &st) != 0) {
+ archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd);
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * If this is a regular file, don't add it to itself.
+ */
+ if (S_ISREG(st.st_mode))
+ archive_write_set_skip_file(a, st.st_dev, st.st_ino);
+
+ /*
+ * If client hasn't explicitly set the last block handling,
+ * then set it here.
+ */
+ if (archive_write_get_bytes_in_last_block(a) < 0) {
+ /* If the output is a block or character device, fifo,
+ * or stdout, pad the last block, otherwise leave it
+ * unpadded. */
+ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
+ S_ISFIFO(st.st_mode) || (mine->fd == 1))
+ /* Last block will be fully padded. */
+ archive_write_set_bytes_in_last_block(a, 0);
+ else
+ archive_write_set_bytes_in_last_block(a, 1);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+ struct write_fd_data *mine;
+ ssize_t bytesWritten;
+
+ mine = (struct write_fd_data *)client_data;
+ for (;;) {
+ bytesWritten = write(mine->fd, buff, length);
+ if (bytesWritten <= 0) {
+ if (errno == EINTR)
+ continue;
+ archive_set_error(a, errno, "Write error");
+ return (-1);
+ }
+ return (bytesWritten);
+ }
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+ struct write_fd_data *mine = (struct write_fd_data *)client_data;
+
+ (void)a; /* UNUSED */
+ if (mine == NULL)
+ return (ARCHIVE_OK);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_open_file.c b/contrib/libs/libarchive/libarchive/archive_write_open_file.c
new file mode 100644
index 0000000000..bf5b55a672
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_open_file.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_file.c,v 1.19 2007/01/09 08:05:56 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct write_FILE_data {
+ FILE *f;
+};
+
+static int file_free(struct archive *, void *);
+static int file_open(struct archive *, void *);
+static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
+
+int
+archive_write_open_FILE(struct archive *a, FILE *f)
+{
+ struct write_FILE_data *mine;
+
+ mine = (struct write_FILE_data *)malloc(sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->f = f;
+ return (archive_write_open2(a, mine, file_open, file_write,
+ NULL, file_free));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+ (void)a; /* UNUSED */
+ (void)client_data; /* UNUSED */
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+ struct write_FILE_data *mine;
+ size_t bytesWritten;
+
+ mine = client_data;
+ for (;;) {
+ bytesWritten = fwrite(buff, 1, length, mine->f);
+ if (bytesWritten <= 0) {
+ if (errno == EINTR)
+ continue;
+ archive_set_error(a, errno, "Write error");
+ return (-1);
+ }
+ return (bytesWritten);
+ }
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+ struct write_FILE_data *mine = client_data;
+
+ (void)a; /* UNUSED */
+ if (mine == NULL)
+ return (ARCHIVE_OK);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_open_filename.c b/contrib/libs/libarchive/libarchive/archive_write_open_filename.c
new file mode 100644
index 0000000000..9ceefb19bc
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_open_filename.c
@@ -0,0 +1,270 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_filename.c 191165 2009-04-17 00:39:35Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+struct write_file_data {
+ int fd;
+ struct archive_mstring filename;
+};
+
+static int file_close(struct archive *, void *);
+static int file_free(struct archive *, void *);
+static int file_open(struct archive *, void *);
+static ssize_t file_write(struct archive *, void *, const void *buff, size_t);
+static int open_filename(struct archive *, int, const void *);
+
+int
+archive_write_open_file(struct archive *a, const char *filename)
+{
+ return (archive_write_open_filename(a, filename));
+}
+
+int
+archive_write_open_filename(struct archive *a, const char *filename)
+{
+
+ if (filename == NULL || filename[0] == '\0')
+ return (archive_write_open_fd(a, 1));
+
+ return (open_filename(a, 1, filename));
+}
+
+int
+archive_write_open_filename_w(struct archive *a, const wchar_t *filename)
+{
+
+ if (filename == NULL || filename[0] == L'\0')
+ return (archive_write_open_fd(a, 1));
+
+ return (open_filename(a, 0, filename));
+}
+
+static int
+open_filename(struct archive *a, int mbs_fn, const void *filename)
+{
+ struct write_file_data *mine;
+ int r;
+
+ mine = (struct write_file_data *)calloc(1, sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (mbs_fn)
+ r = archive_mstring_copy_mbs(&mine->filename, filename);
+ else
+ r = archive_mstring_copy_wcs(&mine->filename, filename);
+ if (r < 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (mbs_fn)
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Can't convert '%s' to WCS",
+ (const char *)filename);
+ else
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Can't convert '%S' to MBS",
+ (const wchar_t *)filename);
+ return (ARCHIVE_FAILED);
+ }
+ mine->fd = -1;
+ return (archive_write_open2(a, mine,
+ file_open, file_write, file_close, file_free));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+ int flags;
+ struct write_file_data *mine;
+ struct stat st;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ wchar_t *fullpath;
+#endif
+ const wchar_t *wcs;
+ const char *mbs;
+
+ mine = (struct write_file_data *)client_data;
+ flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC;
+
+ /*
+ * Open the file.
+ */
+ mbs = NULL; wcs = NULL;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (archive_mstring_get_wcs(a, &mine->filename, &wcs) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(a, errno, "No memory");
+ else {
+ archive_mstring_get_mbs(a, &mine->filename, &mbs);
+ archive_set_error(a, errno,
+ "Can't convert '%s' to WCS", mbs);
+ }
+ return (ARCHIVE_FATAL);
+ }
+ fullpath = __la_win_permissive_name_w(wcs);
+ if (fullpath != NULL) {
+ mine->fd = _wopen(fullpath, flags, 0666);
+ free(fullpath);
+ } else
+ mine->fd = _wopen(wcs, flags, 0666);
+#else
+ if (archive_mstring_get_mbs(a, &mine->filename, &mbs) != 0) {
+ if (errno == ENOMEM)
+ archive_set_error(a, errno, "No memory");
+ else {
+ archive_mstring_get_wcs(a, &mine->filename, &wcs);
+ archive_set_error(a, errno,
+ "Can't convert '%S' to MBS", wcs);
+ }
+ return (ARCHIVE_FATAL);
+ }
+ mine->fd = open(mbs, flags, 0666);
+ __archive_ensure_cloexec_flag(mine->fd);
+#endif
+ if (mine->fd < 0) {
+ if (mbs != NULL)
+ archive_set_error(a, errno, "Failed to open '%s'", mbs);
+ else
+ archive_set_error(a, errno, "Failed to open '%S'", wcs);
+ return (ARCHIVE_FATAL);
+ }
+
+ if (fstat(mine->fd, &st) != 0) {
+ if (mbs != NULL)
+ archive_set_error(a, errno, "Couldn't stat '%s'", mbs);
+ else
+ archive_set_error(a, errno, "Couldn't stat '%S'", wcs);
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Set up default last block handling.
+ */
+ if (archive_write_get_bytes_in_last_block(a) < 0) {
+ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
+ S_ISFIFO(st.st_mode))
+ /* Pad last block when writing to device or FIFO. */
+ archive_write_set_bytes_in_last_block(a, 0);
+ else
+ /* Don't pad last block otherwise. */
+ archive_write_set_bytes_in_last_block(a, 1);
+ }
+
+ /*
+ * If the output file is a regular file, don't add it to
+ * itself. If it's a device file, it's okay to add the device
+ * entry to the output archive.
+ */
+ if (S_ISREG(st.st_mode))
+ archive_write_set_skip_file(a, st.st_dev, st.st_ino);
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff,
+ size_t length)
+{
+ struct write_file_data *mine;
+ ssize_t bytesWritten;
+
+ mine = (struct write_file_data *)client_data;
+ for (;;) {
+ bytesWritten = write(mine->fd, buff, length);
+ if (bytesWritten <= 0) {
+ if (errno == EINTR)
+ continue;
+ archive_set_error(a, errno, "Write error");
+ return (-1);
+ }
+ return (bytesWritten);
+ }
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+ struct write_file_data *mine = (struct write_file_data *)client_data;
+
+ (void)a; /* UNUSED */
+
+ if (mine == NULL)
+ return (ARCHIVE_FATAL);
+
+ if (mine->fd >= 0)
+ close(mine->fd);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+ struct write_file_data *mine = (struct write_file_data *)client_data;
+
+ (void)a; /* UNUSED */
+
+ if (mine == NULL)
+ return (ARCHIVE_OK);
+
+ archive_mstring_clean(&mine->filename);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_open_memory.c b/contrib/libs/libarchive/libarchive/archive_write_open_memory.c
new file mode 100644
index 0000000000..a8a0b817fc
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_open_memory.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+struct write_memory_data {
+ size_t used;
+ size_t size;
+ size_t * client_size;
+ unsigned char * buff;
+};
+
+static int memory_write_free(struct archive *, void *);
+static int memory_write_open(struct archive *, void *);
+static ssize_t memory_write(struct archive *, void *, const void *buff, size_t);
+
+/*
+ * Client provides a pointer to a block of memory to receive
+ * the data. The 'size' param both tells us the size of the
+ * client buffer and lets us tell the client the final size.
+ */
+int
+archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used)
+{
+ struct write_memory_data *mine;
+
+ mine = (struct write_memory_data *)calloc(1, sizeof(*mine));
+ if (mine == NULL) {
+ archive_set_error(a, ENOMEM, "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ mine->buff = buff;
+ mine->size = buffSize;
+ mine->client_size = used;
+ return (archive_write_open2(a, mine,
+ memory_write_open, memory_write, NULL, memory_write_free));
+}
+
+static int
+memory_write_open(struct archive *a, void *client_data)
+{
+ struct write_memory_data *mine;
+ mine = client_data;
+ mine->used = 0;
+ if (mine->client_size != NULL)
+ *mine->client_size = mine->used;
+ /* Disable padding if it hasn't been set explicitly. */
+ if (-1 == archive_write_get_bytes_in_last_block(a))
+ archive_write_set_bytes_in_last_block(a, 1);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Copy the data into the client buffer.
+ * Note that we update mine->client_size on every write.
+ * In particular, this means the client can follow exactly
+ * how much has been written into their buffer at any time.
+ */
+static ssize_t
+memory_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+ struct write_memory_data *mine;
+ mine = client_data;
+
+ if (mine->used + length > mine->size) {
+ archive_set_error(a, ENOMEM, "Buffer exhausted");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(mine->buff + mine->used, buff, length);
+ mine->used += length;
+ if (mine->client_size != NULL)
+ *mine->client_size = mine->used;
+ return (length);
+}
+
+static int
+memory_write_free(struct archive *a, void *client_data)
+{
+ struct write_memory_data *mine;
+ (void)a; /* UNUSED */
+ mine = client_data;
+ if (mine == NULL)
+ return (ARCHIVE_OK);
+ free(mine);
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_private.h b/contrib/libs/libarchive/libarchive/archive_write_private.h
new file mode 100644
index 0000000000..6522e6521b
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_private.h
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_write_private.h 201155 2009-12-29 05:20:12Z kientzle $
+ */
+
+#ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+#define ARCHIVE_WRITE_FILTER_STATE_NEW 1U
+#define ARCHIVE_WRITE_FILTER_STATE_OPEN 2U
+#define ARCHIVE_WRITE_FILTER_STATE_CLOSED 4U
+#define ARCHIVE_WRITE_FILTER_STATE_FATAL 0x8000U
+
+struct archive_write;
+
+struct archive_write_filter {
+ int64_t bytes_written;
+ struct archive *archive; /* Associated archive. */
+ struct archive_write_filter *next_filter; /* Who I write to. */
+ int (*options)(struct archive_write_filter *,
+ const char *key, const char *value);
+ int (*open)(struct archive_write_filter *);
+ int (*write)(struct archive_write_filter *, const void *, size_t);
+ int (*flush)(struct archive_write_filter *);
+ int (*close)(struct archive_write_filter *);
+ int (*free)(struct archive_write_filter *);
+ void *data;
+ const char *name;
+ int code;
+ int bytes_per_block;
+ int bytes_in_last_block;
+ int state;
+};
+
+#if ARCHIVE_VERSION < 4000000
+void __archive_write_filters_free(struct archive *);
+#endif
+
+struct archive_write_filter *__archive_write_allocate_filter(struct archive *);
+
+int __archive_write_output(struct archive_write *, const void *, size_t);
+int __archive_write_nulls(struct archive_write *, size_t);
+int __archive_write_filter(struct archive_write_filter *, const void *, size_t);
+
+struct archive_write {
+ struct archive archive;
+
+ /* Dev/ino of the archive being written. */
+ int skip_file_set;
+ int64_t skip_file_dev;
+ int64_t skip_file_ino;
+
+ /* Utility: Pointer to a block of nulls. */
+ const unsigned char *nulls;
+ size_t null_length;
+
+ /* Callbacks to open/read/write/close archive stream. */
+ archive_open_callback *client_opener;
+ archive_write_callback *client_writer;
+ archive_close_callback *client_closer;
+ archive_free_callback *client_freer;
+ void *client_data;
+
+ /*
+ * Blocking information. Note that bytes_in_last_block is
+ * misleadingly named; I should find a better name. These
+ * control the final output from all compressors, including
+ * compression_none.
+ */
+ int bytes_per_block;
+ int bytes_in_last_block;
+
+ /*
+ * First and last write filters in the pipeline.
+ */
+ struct archive_write_filter *filter_first;
+ struct archive_write_filter *filter_last;
+
+ /*
+ * Pointers to format-specific functions for writing. They're
+ * initialized by archive_write_set_format_XXX() calls.
+ */
+ void *format_data;
+ const char *format_name;
+ int (*format_init)(struct archive_write *);
+ int (*format_options)(struct archive_write *,
+ const char *key, const char *value);
+ int (*format_finish_entry)(struct archive_write *);
+ int (*format_write_header)(struct archive_write *,
+ struct archive_entry *);
+ ssize_t (*format_write_data)(struct archive_write *,
+ const void *buff, size_t);
+ int (*format_close)(struct archive_write *);
+ int (*format_free)(struct archive_write *);
+
+
+ /*
+ * Encryption passphrase.
+ */
+ char *passphrase;
+ archive_passphrase_callback *passphrase_callback;
+ void *passphrase_client_data;
+};
+
+/*
+ * Utility function to format a USTAR header into a buffer. If
+ * "strict" is set, this tries to create the absolutely most portable
+ * version of a ustar header. If "strict" is set to 0, then it will
+ * relax certain requirements.
+ *
+ * Generally, format-specific declarations don't belong in this
+ * header; this is a rare example of a function that is shared by
+ * two very similar formats (ustar and pax).
+ */
+int
+__archive_write_format_header_ustar(struct archive_write *, char buff[512],
+ struct archive_entry *, int tartype, int strict,
+ struct archive_string_conv *);
+
+struct archive_write_program_data;
+struct archive_write_program_data * __archive_write_program_allocate(const char *program_name);
+int __archive_write_program_free(struct archive_write_program_data *);
+int __archive_write_program_open(struct archive_write_filter *,
+ struct archive_write_program_data *, const char *);
+int __archive_write_program_close(struct archive_write_filter *,
+ struct archive_write_program_data *);
+int __archive_write_program_write(struct archive_write_filter *,
+ struct archive_write_program_data *, const void *, size_t);
+
+/*
+ * Get a encryption passphrase.
+ */
+const char * __archive_write_get_passphrase(struct archive_write *a);
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format.c b/contrib/libs/libarchive/libarchive/archive_write_set_format.c
new file mode 100644
index 0000000000..1f65fa4a77
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_set_format_private.h"
+
+/* A table that maps format codes to functions. */
+static const
+struct { int code; int (*setter)(struct archive *); } codes[] =
+{
+ { ARCHIVE_FORMAT_7ZIP, archive_write_set_format_7zip },
+ { ARCHIVE_FORMAT_CPIO, archive_write_set_format_cpio },
+ { ARCHIVE_FORMAT_CPIO_BIN_LE, archive_write_set_format_cpio_bin },
+ { ARCHIVE_FORMAT_CPIO_PWB, archive_write_set_format_cpio_pwb },
+ { ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio_odc },
+ { ARCHIVE_FORMAT_CPIO_SVR4_NOCRC, archive_write_set_format_cpio_newc },
+ { ARCHIVE_FORMAT_ISO9660, archive_write_set_format_iso9660 },
+ { ARCHIVE_FORMAT_MTREE, archive_write_set_format_mtree },
+ { ARCHIVE_FORMAT_RAW, archive_write_set_format_raw },
+ { ARCHIVE_FORMAT_SHAR, archive_write_set_format_shar },
+ { ARCHIVE_FORMAT_SHAR_BASE, archive_write_set_format_shar },
+ { ARCHIVE_FORMAT_SHAR_DUMP, archive_write_set_format_shar_dump },
+ { ARCHIVE_FORMAT_TAR, archive_write_set_format_pax_restricted },
+ { ARCHIVE_FORMAT_TAR_GNUTAR, archive_write_set_format_gnutar },
+ { ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax },
+ { ARCHIVE_FORMAT_TAR_PAX_RESTRICTED,
+ archive_write_set_format_pax_restricted },
+ { ARCHIVE_FORMAT_TAR_USTAR, archive_write_set_format_ustar },
+ { ARCHIVE_FORMAT_WARC, archive_write_set_format_warc },
+ { ARCHIVE_FORMAT_XAR, archive_write_set_format_xar },
+ { ARCHIVE_FORMAT_ZIP, archive_write_set_format_zip },
+ { 0, NULL }
+};
+
+int
+archive_write_set_format(struct archive *a, int code)
+{
+ int i;
+
+ for (i = 0; codes[i].code != 0; i++) {
+ if (code == codes[i].code)
+ return ((codes[i].setter)(a));
+ }
+
+ archive_set_error(a, EINVAL, "No such format");
+ return (ARCHIVE_FATAL);
+}
+
+void
+__archive_write_entry_filetype_unsupported(struct archive *a,
+ struct archive_entry *entry, const char *format)
+{
+ const char *name = NULL;
+
+ switch (archive_entry_filetype(entry)) {
+ /*
+ * All formats should be able to archive regular files (AE_IFREG)
+ */
+ case AE_IFDIR:
+ name = "directories";
+ break;
+ case AE_IFLNK:
+ name = "symbolic links";
+ break;
+ case AE_IFCHR:
+ name = "character devices";
+ break;
+ case AE_IFBLK:
+ name = "block devices";
+ break;
+ case AE_IFIFO:
+ name = "named pipes";
+ break;
+ case AE_IFSOCK:
+ name = "sockets";
+ break;
+ default:
+ break;
+ }
+
+ if (name != NULL) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "%s: %s format cannot archive %s",
+ archive_entry_pathname(entry), format, name);
+ } else {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "%s: %s format cannot archive files with mode 0%lo",
+ archive_entry_pathname(entry), format,
+ (unsigned long)archive_entry_mode(entry));
+ }
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_7zip.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_7zip.c
new file mode 100644
index 0000000000..6f6e894209
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_7zip.c
@@ -0,0 +1,2347 @@
+/*-
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+/*
+ * Codec ID
+ */
+#define _7Z_COPY 0
+#define _7Z_LZMA1 0x030101
+#define _7Z_LZMA2 0x21
+#define _7Z_DEFLATE 0x040108
+#define _7Z_BZIP2 0x040202
+#define _7Z_PPMD 0x030401
+
+/*
+ * 7-Zip header property IDs.
+ */
+#define kEnd 0x00
+#define kHeader 0x01
+#define kArchiveProperties 0x02
+#define kAdditionalStreamsInfo 0x03
+#define kMainStreamsInfo 0x04
+#define kFilesInfo 0x05
+#define kPackInfo 0x06
+#define kUnPackInfo 0x07
+#define kSubStreamsInfo 0x08
+#define kSize 0x09
+#define kCRC 0x0A
+#define kFolder 0x0B
+#define kCodersUnPackSize 0x0C
+#define kNumUnPackStream 0x0D
+#define kEmptyStream 0x0E
+#define kEmptyFile 0x0F
+#define kAnti 0x10
+#define kName 0x11
+#define kCTime 0x12
+#define kATime 0x13
+#define kMTime 0x14
+#define kAttributes 0x15
+#define kEncodedHeader 0x17
+
+// Check that some windows file attribute constants are defined.
+// Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+#ifndef FILE_ATTRIBUTE_READONLY
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#endif
+
+#ifndef FILE_ATTRIBUTE_DIRECTORY
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#endif
+
+#ifndef FILE_ATTRIBUTE_ARCHIVE
+#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
+#endif
+
+// This value is defined in 7zip with the comment "trick for Unix".
+//
+// 7z archives created on unix have this bit set in the high 16 bits of
+// the attr field along with the unix permissions.
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000
+
+enum la_zaction {
+ ARCHIVE_Z_FINISH,
+ ARCHIVE_Z_RUN
+};
+
+/*
+ * A stream object of universal compressor.
+ */
+struct la_zstream {
+ const uint8_t *next_in;
+ size_t avail_in;
+ uint64_t total_in;
+
+ uint8_t *next_out;
+ size_t avail_out;
+ uint64_t total_out;
+
+ uint32_t prop_size;
+ uint8_t *props;
+
+ int valid;
+ void *real_stream;
+ int (*code) (struct archive *a,
+ struct la_zstream *lastrm,
+ enum la_zaction action);
+ int (*end)(struct archive *a,
+ struct la_zstream *lastrm);
+};
+
+#define PPMD7_DEFAULT_ORDER 6
+#define PPMD7_DEFAULT_MEM_SIZE (1 << 24)
+
+struct ppmd_stream {
+ int stat;
+ CPpmd7 ppmd7_context;
+ CPpmd7z_RangeEnc range_enc;
+ IByteOut byteout;
+ uint8_t *buff;
+ uint8_t *buff_ptr;
+ uint8_t *buff_end;
+ size_t buff_bytes;
+};
+
+struct coder {
+ unsigned codec;
+ size_t prop_size;
+ uint8_t *props;
+};
+
+struct file {
+ struct archive_rb_node rbnode;
+
+ struct file *next;
+ unsigned name_len;
+ uint8_t *utf16name;/* UTF16-LE name. */
+ uint64_t size;
+ unsigned flg;
+#define MTIME_IS_SET (1<<0)
+#define ATIME_IS_SET (1<<1)
+#define CTIME_IS_SET (1<<2)
+#define CRC32_IS_SET (1<<3)
+#define HAS_STREAM (1<<4)
+
+ struct {
+ time_t time;
+ long time_ns;
+ } times[3];
+#define MTIME 0
+#define ATIME 1
+#define CTIME 2
+
+ mode_t mode;
+ uint32_t crc32;
+
+ unsigned dir:1;
+};
+
+struct _7zip {
+ int temp_fd;
+ uint64_t temp_offset;
+
+ struct file *cur_file;
+ size_t total_number_entry;
+ size_t total_number_nonempty_entry;
+ size_t total_number_empty_entry;
+ size_t total_number_dir_entry;
+ size_t total_bytes_entry_name;
+ size_t total_number_time_defined[3];
+ uint64_t total_bytes_compressed;
+ uint64_t total_bytes_uncompressed;
+ uint64_t entry_bytes_remaining;
+ uint32_t entry_crc32;
+ uint32_t precode_crc32;
+ uint32_t encoded_crc32;
+ int crc32flg;
+#define PRECODE_CRC32 1
+#define ENCODED_CRC32 2
+
+ unsigned opt_compression;
+ int opt_compression_level;
+
+ struct la_zstream stream;
+ struct coder coder;
+
+ struct archive_string_conv *sconv;
+
+ /*
+ * Compressed data buffer.
+ */
+ unsigned char wbuff[512 * 20 * 6];
+ size_t wbuff_remaining;
+
+ /*
+ * The list of the file entries which has its contents is used to
+ * manage struct file objects.
+ * We use 'next' (a member of struct file) to chain.
+ */
+ struct {
+ struct file *first;
+ struct file **last;
+ } file_list, empty_list;
+ struct archive_rb_tree rbtree;/* for empty files */
+};
+
+static int _7z_options(struct archive_write *,
+ const char *, const char *);
+static int _7z_write_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t _7z_write_data(struct archive_write *,
+ const void *, size_t);
+static int _7z_finish_entry(struct archive_write *);
+static int _7z_close(struct archive_write *);
+static int _7z_free(struct archive_write *);
+static int file_cmp_node(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int file_cmp_key(const struct archive_rb_node *, const void *);
+static int file_new(struct archive_write *a, struct archive_entry *,
+ struct file **);
+static void file_free(struct file *);
+static void file_register(struct _7zip *, struct file *);
+static void file_register_empty(struct _7zip *, struct file *);
+static void file_init_register(struct _7zip *);
+static void file_init_register_empty(struct _7zip *);
+static void file_free_register(struct _7zip *);
+static ssize_t compress_out(struct archive_write *, const void *, size_t ,
+ enum la_zaction);
+static int compression_init_encoder_copy(struct archive *,
+ struct la_zstream *);
+static int compression_code_copy(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_copy(struct archive *, struct la_zstream *);
+static int compression_init_encoder_deflate(struct archive *,
+ struct la_zstream *, int, int);
+#ifdef HAVE_ZLIB_H
+static int compression_code_deflate(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_deflate(struct archive *, struct la_zstream *);
+#endif
+static int compression_init_encoder_bzip2(struct archive *,
+ struct la_zstream *, int);
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int compression_code_bzip2(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_bzip2(struct archive *, struct la_zstream *);
+#endif
+static int compression_init_encoder_lzma1(struct archive *,
+ struct la_zstream *, int);
+static int compression_init_encoder_lzma2(struct archive *,
+ struct la_zstream *, int);
+#if defined(HAVE_LZMA_H)
+static int compression_code_lzma(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_lzma(struct archive *, struct la_zstream *);
+#endif
+static int compression_init_encoder_ppmd(struct archive *,
+ struct la_zstream *, unsigned, uint32_t);
+static int compression_code_ppmd(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_ppmd(struct archive *, struct la_zstream *);
+static int _7z_compression_init_encoder(struct archive_write *, unsigned,
+ int);
+static int compression_code(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end(struct archive *,
+ struct la_zstream *);
+static int enc_uint64(struct archive_write *, uint64_t);
+static int make_header(struct archive_write *, uint64_t, uint64_t,
+ uint64_t, int, struct coder *);
+static int make_streamsInfo(struct archive_write *, uint64_t, uint64_t,
+ uint64_t, int, struct coder *, int, uint32_t);
+
+int
+archive_write_set_format_7zip(struct archive *_a)
+{
+ static const struct archive_rb_tree_ops rb_ops = {
+ file_cmp_node, file_cmp_key
+ };
+ struct archive_write *a = (struct archive_write *)_a;
+ struct _7zip *zip;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_7zip");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ zip = calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate 7-Zip data");
+ return (ARCHIVE_FATAL);
+ }
+ zip->temp_fd = -1;
+ __archive_rb_tree_init(&(zip->rbtree), &rb_ops);
+ file_init_register(zip);
+ file_init_register_empty(zip);
+
+ /* Set default compression type and its level. */
+#if HAVE_LZMA_H
+ zip->opt_compression = _7Z_LZMA1;
+#elif defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ zip->opt_compression = _7Z_BZIP2;
+#elif defined(HAVE_ZLIB_H)
+ zip->opt_compression = _7Z_DEFLATE;
+#else
+ zip->opt_compression = _7Z_COPY;
+#endif
+ zip->opt_compression_level = 6;
+
+ a->format_data = zip;
+
+ a->format_name = "7zip";
+ a->format_options = _7z_options;
+ a->format_write_header = _7z_write_header;
+ a->format_write_data = _7z_write_data;
+ a->format_finish_entry = _7z_finish_entry;
+ a->format_close = _7z_close;
+ a->format_free = _7z_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
+ a->archive.archive_format_name = "7zip";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+_7z_options(struct archive_write *a, const char *key, const char *value)
+{
+ struct _7zip *zip;
+
+ zip = (struct _7zip *)a->format_data;
+
+ if (strcmp(key, "compression") == 0) {
+ const char *name = NULL;
+
+ if (value == NULL || strcmp(value, "copy") == 0 ||
+ strcmp(value, "COPY") == 0 ||
+ strcmp(value, "store") == 0 ||
+ strcmp(value, "STORE") == 0)
+ zip->opt_compression = _7Z_COPY;
+ else if (strcmp(value, "deflate") == 0 ||
+ strcmp(value, "DEFLATE") == 0)
+#if HAVE_ZLIB_H
+ zip->opt_compression = _7Z_DEFLATE;
+#else
+ name = "deflate";
+#endif
+ else if (strcmp(value, "bzip2") == 0 ||
+ strcmp(value, "BZIP2") == 0)
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ zip->opt_compression = _7Z_BZIP2;
+#else
+ name = "bzip2";
+#endif
+ else if (strcmp(value, "lzma1") == 0 ||
+ strcmp(value, "LZMA1") == 0)
+#if HAVE_LZMA_H
+ zip->opt_compression = _7Z_LZMA1;
+#else
+ name = "lzma1";
+#endif
+ else if (strcmp(value, "lzma2") == 0 ||
+ strcmp(value, "LZMA2") == 0)
+#if HAVE_LZMA_H
+ zip->opt_compression = _7Z_LZMA2;
+#else
+ name = "lzma2";
+#endif
+ else if (strcmp(value, "ppmd") == 0 ||
+ strcmp(value, "PPMD") == 0 ||
+ strcmp(value, "PPMd") == 0)
+ zip->opt_compression = _7Z_PPMD;
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unknown compression name: `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ if (name != NULL) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "`%s' compression not supported "
+ "on this platform",
+ name);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL ||
+ !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0') {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Illegal value `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ zip->opt_compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+_7z_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct _7zip *zip;
+ struct file *file;
+ int r;
+
+ zip = (struct _7zip *)a->format_data;
+ zip->cur_file = NULL;
+ zip->entry_bytes_remaining = 0;
+
+ if (zip->sconv == NULL) {
+ zip->sconv = archive_string_conversion_to_charset(
+ &a->archive, "UTF-16LE", 1);
+ if (zip->sconv == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ r = file_new(a, entry, &file);
+ if (r < ARCHIVE_WARN) {
+ if (file != NULL)
+ file_free(file);
+ return (r);
+ }
+ if (file->size == 0 && file->dir) {
+ if (!__archive_rb_tree_insert_node(&(zip->rbtree),
+ (struct archive_rb_node *)file)) {
+ /* We have already had the same file. */
+ file_free(file);
+ return (ARCHIVE_OK);
+ }
+ }
+
+ if (file->flg & MTIME_IS_SET)
+ zip->total_number_time_defined[MTIME]++;
+ if (file->flg & CTIME_IS_SET)
+ zip->total_number_time_defined[CTIME]++;
+ if (file->flg & ATIME_IS_SET)
+ zip->total_number_time_defined[ATIME]++;
+
+ zip->total_number_entry++;
+ zip->total_bytes_entry_name += file->name_len + 2;
+ if (file->size == 0) {
+ /* Count up the number of empty files. */
+ zip->total_number_empty_entry++;
+ if (file->dir)
+ zip->total_number_dir_entry++;
+ else
+ file_register_empty(zip, file);
+ return (r);
+ }
+
+ /*
+ * Init compression.
+ */
+ if ((zip->total_number_entry - zip->total_number_empty_entry) == 1) {
+ r = _7z_compression_init_encoder(a, zip->opt_compression,
+ zip->opt_compression_level);
+ if (r < 0) {
+ file_free(file);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* Register a non-empty file. */
+ file_register(zip, file);
+
+ /*
+ * Set the current file to cur_file to read its contents.
+ */
+ zip->cur_file = file;
+
+
+ /* Save a offset of current file in temporary file. */
+ zip->entry_bytes_remaining = file->size;
+ zip->entry_crc32 = 0;
+
+ /*
+ * Store a symbolic link name as file contents.
+ */
+ if (archive_entry_filetype(entry) == AE_IFLNK) {
+ ssize_t bytes;
+ const void *p = (const void *)archive_entry_symlink(entry);
+ bytes = compress_out(a, p, (size_t)file->size, ARCHIVE_Z_RUN);
+ if (bytes < 0)
+ return ((int)bytes);
+ zip->entry_crc32 = crc32(zip->entry_crc32, p, (unsigned)bytes);
+ zip->entry_bytes_remaining -= bytes;
+ }
+
+ return (r);
+}
+
+/*
+ * Write data to a temporary file.
+ */
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ struct _7zip *zip;
+ const unsigned char *p;
+ ssize_t ws;
+
+ zip = (struct _7zip *)a->format_data;
+
+ /*
+ * Open a temporary file.
+ */
+ if (zip->temp_fd == -1) {
+ zip->temp_offset = 0;
+ zip->temp_fd = __archive_mktemp(NULL);
+ if (zip->temp_fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't create temporary file");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ p = (const unsigned char *)buff;
+ while (s) {
+ ws = write(zip->temp_fd, p, s);
+ if (ws < 0) {
+ archive_set_error(&(a->archive), errno,
+ "fwrite function failed");
+ return (ARCHIVE_FATAL);
+ }
+ s -= ws;
+ p += ws;
+ zip->temp_offset += ws;
+ }
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+compress_out(struct archive_write *a, const void *buff, size_t s,
+ enum la_zaction run)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ int r;
+
+ if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0)
+ return (0);
+
+ if ((zip->crc32flg & PRECODE_CRC32) && s)
+ zip->precode_crc32 = crc32(zip->precode_crc32, buff,
+ (unsigned)s);
+ zip->stream.next_in = (const unsigned char *)buff;
+ zip->stream.avail_in = s;
+ for (;;) {
+ /* Compress file data. */
+ r = compression_code(&(a->archive), &(zip->stream), run);
+ if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+ return (ARCHIVE_FATAL);
+ if (zip->stream.avail_out == 0) {
+ if (write_to_temp(a, zip->wbuff, sizeof(zip->wbuff))
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->stream.next_out = zip->wbuff;
+ zip->stream.avail_out = sizeof(zip->wbuff);
+ if (zip->crc32flg & ENCODED_CRC32)
+ zip->encoded_crc32 = crc32(zip->encoded_crc32,
+ zip->wbuff, sizeof(zip->wbuff));
+ if (run == ARCHIVE_Z_FINISH && r != ARCHIVE_EOF)
+ continue;
+ }
+ if (zip->stream.avail_in == 0)
+ break;
+ }
+ if (run == ARCHIVE_Z_FINISH) {
+ uint64_t bytes = sizeof(zip->wbuff) - zip->stream.avail_out;
+ if (write_to_temp(a, zip->wbuff, (size_t)bytes) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if ((zip->crc32flg & ENCODED_CRC32) && bytes)
+ zip->encoded_crc32 = crc32(zip->encoded_crc32,
+ zip->wbuff, (unsigned)bytes);
+ }
+
+ return (s);
+}
+
+static ssize_t
+_7z_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct _7zip *zip;
+ ssize_t bytes;
+
+ zip = (struct _7zip *)a->format_data;
+
+ if (s > zip->entry_bytes_remaining)
+ s = (size_t)zip->entry_bytes_remaining;
+ if (s == 0 || zip->cur_file == NULL)
+ return (0);
+ bytes = compress_out(a, buff, s, ARCHIVE_Z_RUN);
+ if (bytes < 0)
+ return (bytes);
+ zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)bytes);
+ zip->entry_bytes_remaining -= bytes;
+ return (bytes);
+}
+
+static int
+_7z_finish_entry(struct archive_write *a)
+{
+ struct _7zip *zip;
+ size_t s;
+ ssize_t r;
+
+ zip = (struct _7zip *)a->format_data;
+ if (zip->cur_file == NULL)
+ return (ARCHIVE_OK);
+
+ while (zip->entry_bytes_remaining > 0) {
+ s = (size_t)zip->entry_bytes_remaining;
+ if (s > a->null_length)
+ s = a->null_length;
+ r = _7z_write_data(a, a->nulls, s);
+ if (r < 0)
+ return ((int)r);
+ }
+ zip->total_bytes_compressed += zip->stream.total_in;
+ zip->total_bytes_uncompressed += zip->stream.total_out;
+ zip->cur_file->crc32 = zip->entry_crc32;
+ zip->cur_file = NULL;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+flush_wbuff(struct archive_write *a)
+{
+ struct _7zip *zip;
+ int r;
+ size_t s;
+
+ zip = (struct _7zip *)a->format_data;
+ s = sizeof(zip->wbuff) - zip->wbuff_remaining;
+ r = __archive_write_output(a, zip->wbuff, s);
+ if (r != ARCHIVE_OK)
+ return (r);
+ zip->wbuff_remaining = sizeof(zip->wbuff);
+ return (r);
+}
+
+static int
+copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
+{
+ struct _7zip *zip;
+ int r;
+
+ zip = (struct _7zip *)a->format_data;
+ if (zip->temp_offset > 0 &&
+ lseek(zip->temp_fd, offset, SEEK_SET) < 0) {
+ archive_set_error(&(a->archive), errno, "lseek failed");
+ return (ARCHIVE_FATAL);
+ }
+ while (length) {
+ size_t rsize;
+ ssize_t rs;
+ unsigned char *wb;
+
+ if (length > zip->wbuff_remaining)
+ rsize = zip->wbuff_remaining;
+ else
+ rsize = (size_t)length;
+ wb = zip->wbuff + (sizeof(zip->wbuff) - zip->wbuff_remaining);
+ rs = read(zip->temp_fd, wb, rsize);
+ if (rs < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Can't read temporary file(%jd)",
+ (intmax_t)rs);
+ return (ARCHIVE_FATAL);
+ }
+ if (rs == 0) {
+ archive_set_error(&(a->archive), 0,
+ "Truncated 7-Zip archive");
+ return (ARCHIVE_FATAL);
+ }
+ zip->wbuff_remaining -= rs;
+ length -= rs;
+ if (zip->wbuff_remaining == 0) {
+ r = flush_wbuff(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+_7z_close(struct archive_write *a)
+{
+ struct _7zip *zip;
+ unsigned char *wb;
+ uint64_t header_offset, header_size, header_unpacksize;
+ uint64_t length;
+ uint32_t header_crc32;
+ int r;
+
+ zip = (struct _7zip *)a->format_data;
+
+ if (zip->total_number_entry > 0) {
+ struct archive_rb_node *n;
+ uint64_t data_offset, data_size, data_unpacksize;
+ unsigned header_compression;
+
+ r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+ if (r < 0)
+ return (r);
+ data_offset = 0;
+ data_size = zip->stream.total_out;
+ data_unpacksize = zip->stream.total_in;
+ zip->coder.codec = zip->opt_compression;
+ zip->coder.prop_size = zip->stream.prop_size;
+ zip->coder.props = zip->stream.props;
+ zip->stream.prop_size = 0;
+ zip->stream.props = NULL;
+ zip->total_number_nonempty_entry =
+ zip->total_number_entry - zip->total_number_empty_entry;
+
+ /* Connect an empty file list. */
+ if (zip->empty_list.first != NULL) {
+ *zip->file_list.last = zip->empty_list.first;
+ zip->file_list.last = zip->empty_list.last;
+ }
+ /* Connect a directory file list. */
+ ARCHIVE_RB_TREE_FOREACH(n, &(zip->rbtree)) {
+ file_register(zip, (struct file *)n);
+ }
+
+ /*
+ * NOTE: 7z command supports just LZMA1, LZMA2 and COPY for
+ * the compression type for encoding the header.
+ */
+#if HAVE_LZMA_H
+ header_compression = _7Z_LZMA1;
+ if(zip->opt_compression == _7Z_LZMA2 ||
+ zip->opt_compression == _7Z_COPY)
+ header_compression = zip->opt_compression;
+
+ /* If the stored file is only one, do not encode the header.
+ * This is the same way 7z command does. */
+ if (zip->total_number_entry == 1)
+ header_compression = _7Z_COPY;
+#else
+ header_compression = _7Z_COPY;
+#endif
+ r = _7z_compression_init_encoder(a, header_compression,
+ zip->opt_compression_level);
+ if (r < 0)
+ return (r);
+ zip->crc32flg = PRECODE_CRC32;
+ zip->precode_crc32 = 0;
+ r = make_header(a, data_offset, data_size, data_unpacksize,
+ 1, &(zip->coder));
+ if (r < 0)
+ return (r);
+ r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+ if (r < 0)
+ return (r);
+ header_offset = data_offset + data_size;
+ header_size = zip->stream.total_out;
+ header_crc32 = zip->precode_crc32;
+ header_unpacksize = zip->stream.total_in;
+
+ if (header_compression != _7Z_COPY) {
+ /*
+ * Encode the header in order to reduce the size
+ * of the archive.
+ */
+ free(zip->coder.props);
+ zip->coder.codec = header_compression;
+ zip->coder.prop_size = zip->stream.prop_size;
+ zip->coder.props = zip->stream.props;
+ zip->stream.prop_size = 0;
+ zip->stream.props = NULL;
+
+ r = _7z_compression_init_encoder(a, _7Z_COPY, 0);
+ if (r < 0)
+ return (r);
+ zip->crc32flg = ENCODED_CRC32;
+ zip->encoded_crc32 = 0;
+
+ /*
+ * Make EncodedHeader.
+ */
+ r = enc_uint64(a, kEncodedHeader);
+ if (r < 0)
+ return (r);
+ r = make_streamsInfo(a, header_offset, header_size,
+ header_unpacksize, 1, &(zip->coder), 0,
+ header_crc32);
+ if (r < 0)
+ return (r);
+ r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+ if (r < 0)
+ return (r);
+ header_offset = header_offset + header_size;
+ header_size = zip->stream.total_out;
+ header_crc32 = zip->encoded_crc32;
+ }
+ zip->crc32flg = 0;
+ } else {
+ header_offset = header_size = 0;
+ header_crc32 = 0;
+ }
+
+ length = zip->temp_offset;
+
+ /*
+ * Make the zip header on wbuff(write buffer).
+ */
+ wb = zip->wbuff;
+ zip->wbuff_remaining = sizeof(zip->wbuff);
+ memcpy(&wb[0], "7z\xBC\xAF\x27\x1C", 6);
+ wb[6] = 0;/* Major version. */
+ wb[7] = 3;/* Minor version. */
+ archive_le64enc(&wb[12], header_offset);/* Next Header Offset */
+ archive_le64enc(&wb[20], header_size);/* Next Header Size */
+ archive_le32enc(&wb[28], header_crc32);/* Next Header CRC */
+ archive_le32enc(&wb[8], crc32(0, &wb[12], 20));/* Start Header CRC */
+ zip->wbuff_remaining -= 32;
+
+ /*
+ * Read all file contents and an encoded header from the temporary
+ * file and write out it.
+ */
+ r = copy_out(a, 0, length);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = flush_wbuff(a);
+ return (r);
+}
+
+/*
+ * Encode 64 bits value into 7-Zip's encoded UINT64 value.
+ */
+static int
+enc_uint64(struct archive_write *a, uint64_t val)
+{
+ unsigned mask = 0x80;
+ uint8_t numdata[9];
+ int i;
+
+ numdata[0] = 0;
+ for (i = 1; i < (int)sizeof(numdata); i++) {
+ if (val < mask) {
+ numdata[0] |= (uint8_t)val;
+ break;
+ }
+ numdata[i] = (uint8_t)val;
+ val >>= 8;
+ numdata[0] |= mask;
+ mask >>= 1;
+ }
+ return ((int)compress_out(a, numdata, i, ARCHIVE_Z_RUN));
+}
+
+static int
+make_substreamsInfo(struct archive_write *a, struct coder *coders)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct file *file;
+ int r;
+
+ /*
+ * Make SubStreamsInfo.
+ */
+ r = enc_uint64(a, kSubStreamsInfo);
+ if (r < 0)
+ return (r);
+
+ if (zip->total_number_nonempty_entry > 1 && coders->codec != _7Z_COPY) {
+ /*
+ * Make NumUnPackStream.
+ */
+ r = enc_uint64(a, kNumUnPackStream);
+ if (r < 0)
+ return (r);
+
+ /* Write numUnpackStreams */
+ r = enc_uint64(a, zip->total_number_nonempty_entry);
+ if (r < 0)
+ return (r);
+
+ /*
+ * Make kSize.
+ */
+ r = enc_uint64(a, kSize);
+ if (r < 0)
+ return (r);
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->next == NULL ||
+ file->next->size == 0)
+ break;
+ r = enc_uint64(a, file->size);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ /*
+ * Make CRC.
+ */
+ r = enc_uint64(a, kCRC);
+ if (r < 0)
+ return (r);
+
+
+ /* All are defined */
+ r = enc_uint64(a, 1);
+ if (r < 0)
+ return (r);
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ uint8_t crc[4];
+ if (file->size == 0)
+ break;
+ archive_le32enc(crc, file->crc32);
+ r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+ return (ARCHIVE_OK);
+}
+
+static int
+make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size,
+ uint64_t unpack_size, int num_coder, struct coder *coders, int substrm,
+ uint32_t header_crc)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ uint8_t codec_buff[8];
+ int numFolders, fi;
+ int codec_size;
+ int i, r;
+
+ if (coders->codec == _7Z_COPY)
+ numFolders = (int)zip->total_number_nonempty_entry;
+ else
+ numFolders = 1;
+
+ /*
+ * Make PackInfo.
+ */
+ r = enc_uint64(a, kPackInfo);
+ if (r < 0)
+ return (r);
+
+ /* Write PackPos. */
+ r = enc_uint64(a, offset);
+ if (r < 0)
+ return (r);
+
+ /* Write NumPackStreams. */
+ r = enc_uint64(a, numFolders);
+ if (r < 0)
+ return (r);
+
+ /* Make Size. */
+ r = enc_uint64(a, kSize);
+ if (r < 0)
+ return (r);
+
+ if (numFolders > 1) {
+ struct file *file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->size == 0)
+ break;
+ r = enc_uint64(a, file->size);
+ if (r < 0)
+ return (r);
+ }
+ } else {
+ /* Write size. */
+ r = enc_uint64(a, pack_size);
+ if (r < 0)
+ return (r);
+ }
+
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ /*
+ * Make UnPackInfo.
+ */
+ r = enc_uint64(a, kUnPackInfo);
+ if (r < 0)
+ return (r);
+
+ /*
+ * Make Folder.
+ */
+ r = enc_uint64(a, kFolder);
+ if (r < 0)
+ return (r);
+
+ /* Write NumFolders. */
+ r = enc_uint64(a, numFolders);
+ if (r < 0)
+ return (r);
+
+ /* Write External. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+ for (fi = 0; fi < numFolders; fi++) {
+ /* Write NumCoders. */
+ r = enc_uint64(a, num_coder);
+ if (r < 0)
+ return (r);
+
+ for (i = 0; i < num_coder; i++) {
+ unsigned codec_id = coders[i].codec;
+
+ /* Write Codec flag. */
+ archive_be64enc(codec_buff, codec_id);
+ for (codec_size = 8; codec_size > 0; codec_size--) {
+ if (codec_buff[8 - codec_size])
+ break;
+ }
+ if (codec_size == 0)
+ codec_size = 1;
+ if (coders[i].prop_size)
+ r = enc_uint64(a, codec_size | 0x20);
+ else
+ r = enc_uint64(a, codec_size);
+ if (r < 0)
+ return (r);
+
+ /* Write Codec ID. */
+ codec_size &= 0x0f;
+ r = (int)compress_out(a, &codec_buff[8-codec_size],
+ codec_size, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+
+ if (coders[i].prop_size) {
+ /* Write Codec property size. */
+ r = enc_uint64(a, coders[i].prop_size);
+ if (r < 0)
+ return (r);
+
+ /* Write Codec properties. */
+ r = (int)compress_out(a, coders[i].props,
+ coders[i].prop_size, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+ }
+ }
+
+ /*
+ * Make CodersUnPackSize.
+ */
+ r = enc_uint64(a, kCodersUnPackSize);
+ if (r < 0)
+ return (r);
+
+ if (numFolders > 1) {
+ struct file *file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->size == 0)
+ break;
+ r = enc_uint64(a, file->size);
+ if (r < 0)
+ return (r);
+ }
+
+ } else {
+ /* Write UnPackSize. */
+ r = enc_uint64(a, unpack_size);
+ if (r < 0)
+ return (r);
+ }
+
+ if (!substrm) {
+ uint8_t crc[4];
+ /*
+ * Make CRC.
+ */
+ r = enc_uint64(a, kCRC);
+ if (r < 0)
+ return (r);
+
+ /* All are defined */
+ r = enc_uint64(a, 1);
+ if (r < 0)
+ return (r);
+ archive_le32enc(crc, header_crc);
+ r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ if (substrm) {
+ /*
+ * Make SubStreamsInfo.
+ */
+ r = make_substreamsInfo(a, coders);
+ if (r < 0)
+ return (r);
+ }
+
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ return (ARCHIVE_OK);
+}
+
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static uint64_t
+utcToFiletime(time_t t, long ns)
+{
+ uint64_t fileTime;
+
+ fileTime = t;
+ fileTime *= 10000000;
+ fileTime += ns / 100;
+ fileTime += EPOC_TIME;
+ return (fileTime);
+}
+
+static int
+make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti)
+{
+ uint8_t filetime[8];
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct file *file;
+ int r;
+ uint8_t b, mask;
+
+ /*
+ * Make Time Bools.
+ */
+ if (zip->total_number_time_defined[ti] == zip->total_number_entry) {
+ /* Write Time Type. */
+ r = enc_uint64(a, type);
+ if (r < 0)
+ return (r);
+ /* Write EmptyStream Size. */
+ r = enc_uint64(a, 2 + zip->total_number_entry * 8);
+ if (r < 0)
+ return (r);
+ /* All are defined. */
+ r = enc_uint64(a, 1);
+ if (r < 0)
+ return (r);
+ } else {
+ if (zip->total_number_time_defined[ti] == 0)
+ return (ARCHIVE_OK);
+
+ /* Write Time Type. */
+ r = enc_uint64(a, type);
+ if (r < 0)
+ return (r);
+ /* Write EmptyStream Size. */
+ r = enc_uint64(a, 2 + ((zip->total_number_entry + 7) >> 3)
+ + zip->total_number_time_defined[ti] * 8);
+ if (r < 0)
+ return (r);
+
+ /* All are not defined. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+ b = 0;
+ mask = 0x80;
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->flg & flg)
+ b |= mask;
+ mask >>= 1;
+ if (mask == 0) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ mask = 0x80;
+ b = 0;
+ }
+ }
+ if (mask != 0x80) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ /* External. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+
+ /*
+ * Make Times.
+ */
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if ((file->flg & flg) == 0)
+ continue;
+ archive_le64enc(filetime, utcToFiletime(file->times[ti].time,
+ file->times[ti].time_ns));
+ r = (int)compress_out(a, filetime, 8, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size,
+ uint64_t unpack_size, int codernum, struct coder *coders)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+ struct file *file;
+ int r;
+ uint8_t b, mask;
+
+ /*
+ * Make FilesInfo.
+ */
+ r = enc_uint64(a, kHeader);
+ if (r < 0)
+ return (r);
+
+ /*
+ * If there are empty files only, do not write MainStreamInfo.
+ */
+ if (zip->total_number_nonempty_entry) {
+ /*
+ * Make MainStreamInfo.
+ */
+ r = enc_uint64(a, kMainStreamsInfo);
+ if (r < 0)
+ return (r);
+ r = make_streamsInfo(a, offset, pack_size, unpack_size,
+ codernum, coders, 1, 0);
+ if (r < 0)
+ return (r);
+ }
+
+ /*
+ * Make FilesInfo.
+ */
+ r = enc_uint64(a, kFilesInfo);
+ if (r < 0)
+ return (r);
+
+ /* Write numFiles. */
+ r = enc_uint64(a, zip->total_number_entry);
+ if (r < 0)
+ return (r);
+
+ if (zip->total_number_empty_entry > 0) {
+ /* Make EmptyStream. */
+ r = enc_uint64(a, kEmptyStream);
+ if (r < 0)
+ return (r);
+
+ /* Write EmptyStream Size. */
+ r = enc_uint64(a, (zip->total_number_entry+7)>>3);
+ if (r < 0)
+ return (r);
+
+ b = 0;
+ mask = 0x80;
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->size == 0)
+ b |= mask;
+ mask >>= 1;
+ if (mask == 0) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ mask = 0x80;
+ b = 0;
+ }
+ }
+ if (mask != 0x80) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ if (zip->total_number_empty_entry > zip->total_number_dir_entry) {
+ /* Make EmptyFile. */
+ r = enc_uint64(a, kEmptyFile);
+ if (r < 0)
+ return (r);
+
+ /* Write EmptyFile Size. */
+ r = enc_uint64(a, (zip->total_number_empty_entry + 7) >> 3);
+ if (r < 0)
+ return (r);
+
+ b = 0;
+ mask = 0x80;
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ if (file->size)
+ continue;
+ if (!file->dir)
+ b |= mask;
+ mask >>= 1;
+ if (mask == 0) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ mask = 0x80;
+ b = 0;
+ }
+ }
+ if (mask != 0x80) {
+ r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ /* Make Name. */
+ r = enc_uint64(a, kName);
+ if (r < 0)
+ return (r);
+
+ /* Write Name size. */
+ r = enc_uint64(a, zip->total_bytes_entry_name+1);
+ if (r < 0)
+ return (r);
+
+ /* Write dmy byte. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ r = (int)compress_out(a, file->utf16name, file->name_len+2,
+ ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Make MTime. */
+ r = make_time(a, kMTime, MTIME_IS_SET, MTIME);
+ if (r < 0)
+ return (r);
+
+ /* Make CTime. */
+ r = make_time(a, kCTime, CTIME_IS_SET, CTIME);
+ if (r < 0)
+ return (r);
+
+ /* Make ATime. */
+ r = make_time(a, kATime, ATIME_IS_SET, ATIME);
+ if (r < 0)
+ return (r);
+
+ /* Make Attributes. */
+ r = enc_uint64(a, kAttributes);
+ if (r < 0)
+ return (r);
+
+ /* Write Attributes size. */
+ r = enc_uint64(a, 2 + zip->total_number_entry * 4);
+ if (r < 0)
+ return (r);
+
+ /* Write "All Are Defined". */
+ r = enc_uint64(a, 1);
+ if (r < 0)
+ return (r);
+
+ /* Write dmy byte. */
+ r = enc_uint64(a, 0);
+ if (r < 0)
+ return (r);
+
+ file = zip->file_list.first;
+ for (;file != NULL; file = file->next) {
+ /*
+ * High 16bits is unix mode.
+ * Low 16bits is Windows attributes.
+ */
+ uint32_t encattr, attr = 0;
+
+ if (file->dir)
+ attr |= FILE_ATTRIBUTE_DIRECTORY;
+ else
+ attr |= FILE_ATTRIBUTE_ARCHIVE;
+
+ if ((file->mode & 0222) == 0)
+ attr |= FILE_ATTRIBUTE_READONLY;
+
+ attr |= FILE_ATTRIBUTE_UNIX_EXTENSION;
+ attr |= ((uint32_t)file->mode) << 16;
+
+ archive_le32enc(&encattr, attr);
+ r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ /* Write End. */
+ r = enc_uint64(a, kEnd);
+ if (r < 0)
+ return (r);
+
+ return (ARCHIVE_OK);
+}
+
+
+static int
+_7z_free(struct archive_write *a)
+{
+ struct _7zip *zip = (struct _7zip *)a->format_data;
+
+ /* Close the temporary file. */
+ if (zip->temp_fd >= 0)
+ close(zip->temp_fd);
+
+ file_free_register(zip);
+ compression_end(&(a->archive), &(zip->stream));
+ free(zip->coder.props);
+ free(zip);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+file_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct file *f1 = (const struct file *)n1;
+ const struct file *f2 = (const struct file *)n2;
+
+ if (f1->name_len == f2->name_len)
+ return (memcmp(f1->utf16name, f2->utf16name, f1->name_len));
+ return (f1->name_len > f2->name_len)?1:-1;
+}
+
+static int
+file_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct file *f = (const struct file *)n;
+
+ return (f->name_len - *(const char *)key);
+}
+
+static int
+file_new(struct archive_write *a, struct archive_entry *entry,
+ struct file **newfile)
+{
+ struct _7zip *zip;
+ struct file *file;
+ const char *u16;
+ size_t u16len;
+ int ret = ARCHIVE_OK;
+
+ zip = (struct _7zip *)a->format_data;
+ *newfile = NULL;
+
+ file = calloc(1, sizeof(*file));
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (0 > archive_entry_pathname_l(entry, &u16, &u16len, zip->sconv)) {
+ if (errno == ENOMEM) {
+ free(file);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for UTF-16LE");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "A filename cannot be converted to UTF-16LE;"
+ "You should disable making Joliet extension");
+ ret = ARCHIVE_WARN;
+ }
+ file->utf16name = malloc(u16len + 2);
+ if (file->utf16name == NULL) {
+ free(file);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Name");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(file->utf16name, u16, u16len);
+ file->utf16name[u16len+0] = 0;
+ file->utf16name[u16len+1] = 0;
+ file->name_len = (unsigned)u16len;
+ file->mode = archive_entry_mode(entry);
+ if (archive_entry_filetype(entry) == AE_IFREG)
+ file->size = archive_entry_size(entry);
+ else
+ archive_entry_set_size(entry, 0);
+ if (archive_entry_filetype(entry) == AE_IFDIR)
+ file->dir = 1;
+ else if (archive_entry_filetype(entry) == AE_IFLNK)
+ file->size = strlen(archive_entry_symlink(entry));
+ if (archive_entry_mtime_is_set(entry)) {
+ file->flg |= MTIME_IS_SET;
+ file->times[MTIME].time = archive_entry_mtime(entry);
+ file->times[MTIME].time_ns = archive_entry_mtime_nsec(entry);
+ }
+ if (archive_entry_atime_is_set(entry)) {
+ file->flg |= ATIME_IS_SET;
+ file->times[ATIME].time = archive_entry_atime(entry);
+ file->times[ATIME].time_ns = archive_entry_atime_nsec(entry);
+ }
+ if (archive_entry_ctime_is_set(entry)) {
+ file->flg |= CTIME_IS_SET;
+ file->times[CTIME].time = archive_entry_ctime(entry);
+ file->times[CTIME].time_ns = archive_entry_ctime_nsec(entry);
+ }
+
+ *newfile = file;
+ return (ret);
+}
+
+static void
+file_free(struct file *file)
+{
+ free(file->utf16name);
+ free(file);
+}
+
+static void
+file_register(struct _7zip *zip, struct file *file)
+{
+ file->next = NULL;
+ *zip->file_list.last = file;
+ zip->file_list.last = &(file->next);
+}
+
+static void
+file_init_register(struct _7zip *zip)
+{
+ zip->file_list.first = NULL;
+ zip->file_list.last = &(zip->file_list.first);
+}
+
+static void
+file_free_register(struct _7zip *zip)
+{
+ struct file *file, *file_next;
+
+ file = zip->file_list.first;
+ while (file != NULL) {
+ file_next = file->next;
+ file_free(file);
+ file = file_next;
+ }
+}
+
+static void
+file_register_empty(struct _7zip *zip, struct file *file)
+{
+ file->next = NULL;
+ *zip->empty_list.last = file;
+ zip->empty_list.last = &(file->next);
+}
+
+static void
+file_init_register_empty(struct _7zip *zip)
+{
+ zip->empty_list.first = NULL;
+ zip->empty_list.last = &(zip->empty_list.first);
+}
+
+#if !defined(HAVE_ZLIB_H) || !defined(HAVE_BZLIB_H) ||\
+ !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)
+static int
+compression_unsupported_encoder(struct archive *a,
+ struct la_zstream *lastrm, const char *name)
+{
+
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "%s compression not supported on this platform", name);
+ lastrm->valid = 0;
+ lastrm->real_stream = NULL;
+ return (ARCHIVE_FAILED);
+}
+#endif
+
+/*
+ * _7_COPY compressor.
+ */
+static int
+compression_init_encoder_copy(struct archive *a, struct la_zstream *lastrm)
+{
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ lastrm->valid = 1;
+ lastrm->code = compression_code_copy;
+ lastrm->end = compression_end_copy;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_copy(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ size_t bytes;
+
+ (void)a; /* UNUSED */
+ if (lastrm->avail_out > lastrm->avail_in)
+ bytes = lastrm->avail_in;
+ else
+ bytes = lastrm->avail_out;
+ if (bytes) {
+ memcpy(lastrm->next_out, lastrm->next_in, bytes);
+ lastrm->next_in += bytes;
+ lastrm->avail_in -= bytes;
+ lastrm->total_in += bytes;
+ lastrm->next_out += bytes;
+ lastrm->avail_out -= bytes;
+ lastrm->total_out += bytes;
+ }
+ if (action == ARCHIVE_Z_FINISH && lastrm->avail_in == 0)
+ return (ARCHIVE_EOF);
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_end_copy(struct archive *a, struct la_zstream *lastrm)
+{
+ (void)a; /* UNUSED */
+ lastrm->valid = 0;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * _7_DEFLATE compressor.
+ */
+#ifdef HAVE_ZLIB_H
+static int
+compression_init_encoder_deflate(struct archive *a,
+ struct la_zstream *lastrm, int level, int withheader)
+{
+ z_stream *strm;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for gzip stream");
+ return (ARCHIVE_FATAL);
+ }
+ /* zlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = (uInt)lastrm->avail_in;
+ strm->total_in = (uLong)lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = (uInt)lastrm->avail_out;
+ strm->total_out = (uLong)lastrm->total_out;
+ if (deflateInit2(strm, level, Z_DEFLATED,
+ (withheader)?15:-15,
+ 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_deflate;
+ lastrm->end = compression_end_deflate;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_deflate(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ z_stream *strm;
+ int r;
+
+ strm = (z_stream *)lastrm->real_stream;
+ /* zlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = (uInt)lastrm->avail_in;
+ strm->total_in = (uLong)lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = (uInt)lastrm->avail_out;
+ strm->total_out = (uLong)lastrm->total_out;
+ r = deflate(strm,
+ (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);
+ lastrm->next_in = strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in = strm->total_in;
+ lastrm->next_out = strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out = strm->total_out;
+ switch (r) {
+ case Z_OK:
+ return (ARCHIVE_OK);
+ case Z_STREAM_END:
+ return (ARCHIVE_EOF);
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "GZip compression failed:"
+ " deflate() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_deflate(struct archive *a, struct la_zstream *lastrm)
+{
+ z_stream *strm;
+ int r;
+
+ strm = (z_stream *)lastrm->real_stream;
+ r = deflateEnd(strm);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ if (r != Z_OK) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_deflate(struct archive *a,
+ struct la_zstream *lastrm, int level, int withheader)
+{
+
+ (void) level; /* UNUSED */
+ (void) withheader; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "deflate"));
+}
+#endif
+
+/*
+ * _7_BZIP2 compressor.
+ */
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int
+compression_init_encoder_bzip2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ bz_stream *strm;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for bzip2 stream");
+ return (ARCHIVE_FATAL);
+ }
+ /* bzlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = (uint32_t)lastrm->avail_in;
+ strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+ strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+ strm->next_out = (char *)lastrm->next_out;
+ strm->avail_out = (uint32_t)lastrm->avail_out;
+ strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+ strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+ if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_bzip2;
+ lastrm->end = compression_end_bzip2;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_bzip2(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ bz_stream *strm;
+ int r;
+
+ strm = (bz_stream *)lastrm->real_stream;
+ /* bzlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = (uint32_t)lastrm->avail_in;
+ strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+ strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+ strm->next_out = (char *)lastrm->next_out;
+ strm->avail_out = (uint32_t)lastrm->avail_out;
+ strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+ strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+ r = BZ2_bzCompress(strm,
+ (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);
+ lastrm->next_in = (const unsigned char *)strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in =
+ (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)
+ + (uint64_t)(uint32_t)strm->total_in_lo32;
+ lastrm->next_out = (unsigned char *)strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out =
+ (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)
+ + (uint64_t)(uint32_t)strm->total_out_lo32;
+ switch (r) {
+ case BZ_RUN_OK: /* Non-finishing */
+ case BZ_FINISH_OK: /* Finishing: There's more work to do */
+ return (ARCHIVE_OK);
+ case BZ_STREAM_END: /* Finishing: all done */
+ /* Only occurs in finishing case */
+ return (ARCHIVE_EOF);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Bzip2 compression failed:"
+ " BZ2_bzCompress() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)
+{
+ bz_stream *strm;
+ int r;
+
+ strm = (bz_stream *)lastrm->real_stream;
+ r = BZ2_bzCompressEnd(strm);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ if (r != BZ_OK) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+#else
+static int
+compression_init_encoder_bzip2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "bzip2"));
+}
+#endif
+
+/*
+ * _7_LZMA1, _7_LZMA2 compressor.
+ */
+#if defined(HAVE_LZMA_H)
+static int
+compression_init_encoder_lzma(struct archive *a,
+ struct la_zstream *lastrm, int level, uint64_t filter_id)
+{
+ static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+ lzma_stream *strm;
+ lzma_filter *lzmafilters;
+ lzma_options_lzma lzma_opt;
+ int r;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for lzma stream");
+ return (ARCHIVE_FATAL);
+ }
+ lzmafilters = (lzma_filter *)(strm+1);
+ if (level > 9)
+ level = 9;
+ if (lzma_lzma_preset(&lzma_opt, level)) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lzmafilters[0].id = filter_id;
+ lzmafilters[0].options = &lzma_opt;
+ lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+
+ r = lzma_properties_size(&(lastrm->prop_size), lzmafilters);
+ if (r != LZMA_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma_properties_size failed");
+ return (ARCHIVE_FATAL);
+ }
+ if (lastrm->prop_size) {
+ lastrm->props = malloc(lastrm->prop_size);
+ if (lastrm->props == NULL) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Cannot allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ r = lzma_properties_encode(lzmafilters, lastrm->props);
+ if (r != LZMA_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma_properties_encode failed");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ *strm = lzma_init_data;
+ r = lzma_raw_encoder(strm, lzmafilters);
+ switch (r) {
+ case LZMA_OK:
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_lzma;
+ lastrm->end = compression_end_lzma;
+ r = ARCHIVE_OK;
+ break;
+ case LZMA_MEM_ERROR:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library: "
+ "Cannot allocate memory");
+ r = ARCHIVE_FATAL;
+ break;
+ default:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "It's a bug in liblzma");
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ return (r);
+}
+
+static int
+compression_init_encoder_lzma1(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ return compression_init_encoder_lzma(a, lastrm, level,
+ LZMA_FILTER_LZMA1);
+}
+
+static int
+compression_init_encoder_lzma2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ return compression_init_encoder_lzma(a, lastrm, level,
+ LZMA_FILTER_LZMA2);
+}
+
+static int
+compression_code_lzma(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ lzma_stream *strm;
+ int r;
+
+ strm = (lzma_stream *)lastrm->real_stream;
+ strm->next_in = lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in = lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out = lastrm->total_out;
+ r = lzma_code(strm,
+ (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);
+ lastrm->next_in = strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in = strm->total_in;
+ lastrm->next_out = strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out = strm->total_out;
+ switch (r) {
+ case LZMA_OK:
+ /* Non-finishing case */
+ return (ARCHIVE_OK);
+ case LZMA_STREAM_END:
+ /* This return can only occur in finishing case. */
+ return (ARCHIVE_EOF);
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(a, ENOMEM,
+ "lzma compression error:"
+ " %ju MiB would have been needed",
+ (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)
+ / (1024 * 1024)));
+ return (ARCHIVE_FATAL);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma compression failed:"
+ " lzma_code() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_lzma(struct archive *a, struct la_zstream *lastrm)
+{
+ lzma_stream *strm;
+
+ (void)a; /* UNUSED */
+ strm = (lzma_stream *)lastrm->real_stream;
+ lzma_end(strm);
+ free(strm);
+ lastrm->valid = 0;
+ lastrm->real_stream = NULL;
+ return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_lzma1(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+static int
+compression_init_encoder_lzma2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+#endif
+
+/*
+ * _7_PPMD compressor.
+ */
+static void
+ppmd_write(void *p, Byte b)
+{
+ struct archive_write *a = ((IByteOut *)p)->a;
+ struct _7zip *zip = (struct _7zip *)(a->format_data);
+ struct la_zstream *lastrm = &(zip->stream);
+ struct ppmd_stream *strm;
+
+ if (lastrm->avail_out) {
+ *lastrm->next_out++ = b;
+ lastrm->avail_out--;
+ lastrm->total_out++;
+ return;
+ }
+ strm = (struct ppmd_stream *)lastrm->real_stream;
+ if (strm->buff_ptr < strm->buff_end) {
+ *strm->buff_ptr++ = b;
+ strm->buff_bytes++;
+ }
+}
+
+static int
+compression_init_encoder_ppmd(struct archive *a,
+ struct la_zstream *lastrm, unsigned maxOrder, uint32_t msize)
+{
+ struct ppmd_stream *strm;
+ uint8_t *props;
+ int r;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ strm->buff = malloc(32);
+ if (strm->buff == NULL) {
+ free(strm);
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ strm->buff_ptr = strm->buff;
+ strm->buff_end = strm->buff + 32;
+
+ props = malloc(1+4);
+ if (props == NULL) {
+ free(strm->buff);
+ free(strm);
+ archive_set_error(a, ENOMEM,
+ "Coludn't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ props[0] = maxOrder;
+ archive_le32enc(props+1, msize);
+ __archive_ppmd7_functions.Ppmd7_Construct(&strm->ppmd7_context);
+ r = __archive_ppmd7_functions.Ppmd7_Alloc(
+ &strm->ppmd7_context, msize);
+ if (r == 0) {
+ free(strm->buff);
+ free(strm);
+ free(props);
+ archive_set_error(a, ENOMEM,
+ "Coludn't allocate memory for PPMd");
+ return (ARCHIVE_FATAL);
+ }
+ __archive_ppmd7_functions.Ppmd7_Init(&(strm->ppmd7_context), maxOrder);
+ strm->byteout.a = (struct archive_write *)a;
+ strm->byteout.Write = ppmd_write;
+ strm->range_enc.Stream = &(strm->byteout);
+ __archive_ppmd7_functions.Ppmd7z_RangeEnc_Init(&(strm->range_enc));
+ strm->stat = 0;
+
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_ppmd;
+ lastrm->end = compression_end_ppmd;
+ lastrm->prop_size = 5;
+ lastrm->props = props;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_ppmd(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ struct ppmd_stream *strm;
+
+ (void)a; /* UNUSED */
+
+ strm = (struct ppmd_stream *)lastrm->real_stream;
+
+ /* Copy encoded data if there are remaining bytes from previous call. */
+ if (strm->buff_bytes) {
+ uint8_t *p = strm->buff_ptr - strm->buff_bytes;
+ while (lastrm->avail_out && strm->buff_bytes) {
+ *lastrm->next_out++ = *p++;
+ lastrm->avail_out--;
+ lastrm->total_out++;
+ strm->buff_bytes--;
+ }
+ if (strm->buff_bytes)
+ return (ARCHIVE_OK);
+ if (strm->stat == 1)
+ return (ARCHIVE_EOF);
+ strm->buff_ptr = strm->buff;
+ }
+ while (lastrm->avail_in && lastrm->avail_out) {
+ __archive_ppmd7_functions.Ppmd7_EncodeSymbol(
+ &(strm->ppmd7_context), &(strm->range_enc),
+ *lastrm->next_in++);
+ lastrm->avail_in--;
+ lastrm->total_in++;
+ }
+ if (lastrm->avail_in == 0 && action == ARCHIVE_Z_FINISH) {
+ __archive_ppmd7_functions.Ppmd7z_RangeEnc_FlushData(
+ &(strm->range_enc));
+ strm->stat = 1;
+ /* Return EOF if there are no remaining bytes. */
+ if (strm->buff_bytes == 0)
+ return (ARCHIVE_EOF);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_end_ppmd(struct archive *a, struct la_zstream *lastrm)
+{
+ struct ppmd_stream *strm;
+
+ (void)a; /* UNUSED */
+
+ strm = (struct ppmd_stream *)lastrm->real_stream;
+ __archive_ppmd7_functions.Ppmd7_Free(&strm->ppmd7_context);
+ free(strm->buff);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Universal compressor initializer.
+ */
+static int
+_7z_compression_init_encoder(struct archive_write *a, unsigned compression,
+ int compression_level)
+{
+ struct _7zip *zip;
+ int r;
+
+ zip = (struct _7zip *)a->format_data;
+ switch (compression) {
+ case _7Z_DEFLATE:
+ r = compression_init_encoder_deflate(
+ &(a->archive), &(zip->stream),
+ compression_level, 0);
+ break;
+ case _7Z_BZIP2:
+ r = compression_init_encoder_bzip2(
+ &(a->archive), &(zip->stream),
+ compression_level);
+ break;
+ case _7Z_LZMA1:
+ r = compression_init_encoder_lzma1(
+ &(a->archive), &(zip->stream),
+ compression_level);
+ break;
+ case _7Z_LZMA2:
+ r = compression_init_encoder_lzma2(
+ &(a->archive), &(zip->stream),
+ compression_level);
+ break;
+ case _7Z_PPMD:
+ r = compression_init_encoder_ppmd(
+ &(a->archive), &(zip->stream),
+ PPMD7_DEFAULT_ORDER, PPMD7_DEFAULT_MEM_SIZE);
+ break;
+ case _7Z_COPY:
+ default:
+ r = compression_init_encoder_copy(
+ &(a->archive), &(zip->stream));
+ break;
+ }
+ if (r == ARCHIVE_OK) {
+ zip->stream.total_in = 0;
+ zip->stream.next_out = zip->wbuff;
+ zip->stream.avail_out = sizeof(zip->wbuff);
+ zip->stream.total_out = 0;
+ }
+
+ return (r);
+}
+
+static int
+compression_code(struct archive *a, struct la_zstream *lastrm,
+ enum la_zaction action)
+{
+ if (lastrm->valid)
+ return (lastrm->code(a, lastrm, action));
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_end(struct archive *a, struct la_zstream *lastrm)
+{
+ if (lastrm->valid) {
+ lastrm->prop_size = 0;
+ free(lastrm->props);
+ lastrm->props = NULL;
+ return (lastrm->end(a, lastrm));
+ }
+ return (ARCHIVE_OK);
+}
+
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_ar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_ar.c
new file mode 100644
index 0000000000..fc0de1e9f6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_ar.c
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ar.c 201108 2009-12-28 03:28:21Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct ar_w {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+ int is_strtab;
+ int has_strtab;
+ char wrote_global_header;
+ char *strtab;
+};
+
+/*
+ * Define structure of the "ar" header.
+ */
+#define AR_name_offset 0
+#define AR_name_size 16
+#define AR_date_offset 16
+#define AR_date_size 12
+#define AR_uid_offset 28
+#define AR_uid_size 6
+#define AR_gid_offset 34
+#define AR_gid_size 6
+#define AR_mode_offset 40
+#define AR_mode_size 8
+#define AR_size_offset 48
+#define AR_size_size 10
+#define AR_fmag_offset 58
+#define AR_fmag_size 2
+
+static int archive_write_set_format_ar(struct archive_write *);
+static int archive_write_ar_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t archive_write_ar_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_ar_free(struct archive_write *);
+static int archive_write_ar_close(struct archive_write *);
+static int archive_write_ar_finish_entry(struct archive_write *);
+static const char *ar_basename(const char *path);
+static int format_octal(int64_t v, char *p, int s);
+static int format_decimal(int64_t v, char *p, int s);
+
+int
+archive_write_set_format_ar_bsd(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_ar_bsd");
+ r = archive_write_set_format_ar(a);
+ if (r == ARCHIVE_OK) {
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+ a->archive.archive_format_name = "ar (BSD)";
+ }
+ return (r);
+}
+
+int
+archive_write_set_format_ar_svr4(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_ar_svr4");
+ r = archive_write_set_format_ar(a);
+ if (r == ARCHIVE_OK) {
+ a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
+ a->archive.archive_format_name = "ar (GNU/SVR4)";
+ }
+ return (r);
+}
+
+/*
+ * Generic initialization.
+ */
+static int
+archive_write_set_format_ar(struct archive_write *a)
+{
+ struct ar_w *ar;
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ ar = (struct ar_w *)calloc(1, sizeof(*ar));
+ if (ar == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = ar;
+
+ a->format_name = "ar";
+ a->format_write_header = archive_write_ar_header;
+ a->format_write_data = archive_write_ar_data;
+ a->format_close = archive_write_ar_close;
+ a->format_free = archive_write_ar_free;
+ a->format_finish_entry = archive_write_ar_finish_entry;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
+{
+ int ret, append_fn;
+ char buff[60];
+ char *ss, *se;
+ struct ar_w *ar;
+ const char *pathname;
+ const char *filename;
+ int64_t size;
+
+ append_fn = 0;
+ ar = (struct ar_w *)a->format_data;
+ ar->is_strtab = 0;
+ filename = NULL;
+ size = archive_entry_size(entry);
+
+
+ /*
+ * Reject files with empty name.
+ */
+ pathname = archive_entry_pathname(entry);
+ if (pathname == NULL || *pathname == '\0') {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid filename");
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * If we are now at the beginning of the archive,
+ * we need first write the ar global header.
+ */
+ if (!ar->wrote_global_header) {
+ __archive_write_output(a, "!<arch>\n", 8);
+ ar->wrote_global_header = 1;
+ }
+
+ memset(buff, ' ', 60);
+ memcpy(&buff[AR_fmag_offset], "`\n", 2);
+
+ if (strcmp(pathname, "/") == 0 ) {
+ /* Entry is archive symbol table in GNU format */
+ buff[AR_name_offset] = '/';
+ goto stat;
+ }
+ if (strcmp(pathname, "/SYM64/") == 0) {
+ /* Entry is archive symbol table in GNU 64-bit format */
+ memcpy(buff + AR_name_offset, "/SYM64/", 7);
+ goto stat;
+ }
+ if (strcmp(pathname, "__.SYMDEF") == 0) {
+ /* Entry is archive symbol table in BSD format */
+ memcpy(buff + AR_name_offset, "__.SYMDEF", 9);
+ goto stat;
+ }
+ if (strcmp(pathname, "//") == 0) {
+ /*
+ * Entry is archive filename table, inform that we should
+ * collect strtab in next _data call.
+ */
+ ar->is_strtab = 1;
+ buff[AR_name_offset] = buff[AR_name_offset + 1] = '/';
+ /*
+ * For archive string table, only ar_size field should
+ * be set.
+ */
+ goto size;
+ }
+
+ /*
+ * Otherwise, entry is a normal archive member.
+ * Strip leading paths from filenames, if any.
+ */
+ if ((filename = ar_basename(pathname)) == NULL) {
+ /* Reject filenames with trailing "/" */
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid filename");
+ return (ARCHIVE_WARN);
+ }
+
+ if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {
+ /*
+ * SVR4/GNU variant use a "/" to mark then end of the filename,
+ * make it possible to have embedded spaces in the filename.
+ * So, the longest filename here (without extension) is
+ * actually 15 bytes.
+ */
+ if (strlen(filename) <= 15) {
+ memcpy(&buff[AR_name_offset],
+ filename, strlen(filename));
+ buff[AR_name_offset + strlen(filename)] = '/';
+ } else {
+ /*
+ * For filename longer than 15 bytes, GNU variant
+ * makes use of a string table and instead stores the
+ * offset of the real filename to in the ar_name field.
+ * The string table should have been written before.
+ */
+ if (ar->has_strtab <= 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "Can't find string table");
+ return (ARCHIVE_WARN);
+ }
+
+ se = (char *)malloc(strlen(filename) + 3);
+ if (se == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate filename buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ memcpy(se, filename, strlen(filename));
+ strcpy(se + strlen(filename), "/\n");
+
+ ss = strstr(ar->strtab, se);
+ free(se);
+
+ if (ss == NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid string table");
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * GNU variant puts "/" followed by digits into
+ * ar_name field. These digits indicates the real
+ * filename string's offset to the string table.
+ */
+ buff[AR_name_offset] = '/';
+ if (format_decimal(ss - ar->strtab,
+ buff + AR_name_offset + 1,
+ AR_name_size - 1)) {
+ archive_set_error(&a->archive, ERANGE,
+ "string table offset too large");
+ return (ARCHIVE_WARN);
+ }
+ }
+ } else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) {
+ /*
+ * BSD variant: for any file name which is more than
+ * 16 chars or contains one or more embedded space(s), the
+ * string "#1/" followed by the ASCII length of the name is
+ * put into the ar_name field. The file size (stored in the
+ * ar_size field) is incremented by the length of the name.
+ * The name is then written immediately following the
+ * archive header.
+ */
+ if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
+ memcpy(&buff[AR_name_offset], filename, strlen(filename));
+ buff[AR_name_offset + strlen(filename)] = ' ';
+ }
+ else {
+ memcpy(buff + AR_name_offset, "#1/", 3);
+ if (format_decimal(strlen(filename),
+ buff + AR_name_offset + 3,
+ AR_name_size - 3)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File name too long");
+ return (ARCHIVE_WARN);
+ }
+ append_fn = 1;
+ size += strlen(filename);
+ }
+ }
+
+stat:
+ if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File modification time too large");
+ return (ARCHIVE_WARN);
+ }
+ if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric user ID too large");
+ return (ARCHIVE_WARN);
+ }
+ if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric group ID too large");
+ return (ARCHIVE_WARN);
+ }
+ if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric mode too large");
+ return (ARCHIVE_WARN);
+ }
+ /*
+ * Sanity Check: A non-pseudo archive member should always be
+ * a regular file.
+ */
+ if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) {
+ archive_set_error(&a->archive, EINVAL,
+ "Regular file required for non-pseudo member");
+ return (ARCHIVE_WARN);
+ }
+
+size:
+ if (format_decimal(size, buff + AR_size_offset, AR_size_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File size out of range");
+ return (ARCHIVE_WARN);
+ }
+
+ ret = __archive_write_output(a, buff, 60);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ ar->entry_bytes_remaining = size;
+ ar->entry_padding = ar->entry_bytes_remaining % 2;
+
+ if (append_fn > 0) {
+ ret = __archive_write_output(a, filename, strlen(filename));
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ ar->entry_bytes_remaining -= strlen(filename);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct ar_w *ar;
+ int ret;
+
+ ar = (struct ar_w *)a->format_data;
+ if (s > ar->entry_bytes_remaining)
+ s = (size_t)ar->entry_bytes_remaining;
+
+ if (ar->is_strtab > 0) {
+ if (ar->has_strtab > 0) {
+ archive_set_error(&a->archive, EINVAL,
+ "More than one string tables exist");
+ return (ARCHIVE_WARN);
+ }
+
+ ar->strtab = (char *)malloc(s + 1);
+ if (ar->strtab == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate strtab buffer");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(ar->strtab, buff, s);
+ ar->strtab[s] = '\0';
+ ar->has_strtab = 1;
+ }
+
+ ret = __archive_write_output(a, buff, s);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ ar->entry_bytes_remaining -= s;
+ return (s);
+}
+
+static int
+archive_write_ar_free(struct archive_write *a)
+{
+ struct ar_w *ar;
+
+ ar = (struct ar_w *)a->format_data;
+
+ if (ar == NULL)
+ return (ARCHIVE_OK);
+
+ if (ar->has_strtab > 0) {
+ free(ar->strtab);
+ ar->strtab = NULL;
+ }
+
+ free(ar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_close(struct archive_write *a)
+{
+ struct ar_w *ar;
+ int ret;
+
+ /*
+ * If we haven't written anything yet, we need to write
+ * the ar global header now to make it a valid ar archive.
+ */
+ ar = (struct ar_w *)a->format_data;
+ if (!ar->wrote_global_header) {
+ ar->wrote_global_header = 1;
+ ret = __archive_write_output(a, "!<arch>\n", 8);
+ return (ret);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_finish_entry(struct archive_write *a)
+{
+ struct ar_w *ar;
+ int ret;
+
+ ar = (struct ar_w *)a->format_data;
+
+ if (ar->entry_bytes_remaining != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Entry remaining bytes larger than 0");
+ return (ARCHIVE_WARN);
+ }
+
+ if (ar->entry_padding == 0) {
+ return (ARCHIVE_OK);
+ }
+
+ if (ar->entry_padding != 1) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Padding wrong size: %ju should be 1 or 0",
+ (uintmax_t)ar->entry_padding);
+ return (ARCHIVE_WARN);
+ }
+
+ ret = __archive_write_output(a, "\n", 1);
+ return (ret);
+}
+
+/*
+ * Format a number into the specified field using base-8.
+ * NB: This version is slightly different from the one in
+ * _ustar.c
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+ int len;
+ char *h;
+
+ len = s;
+ h = p;
+
+ /* Octal values can't be negative, so use 0. */
+ if (v < 0) {
+ while (len-- > 0)
+ *p++ = '0';
+ return (-1);
+ }
+
+ p += s; /* Start at the end and work backwards. */
+ do {
+ *--p = (char)('0' + (v & 7));
+ v >>= 3;
+ } while (--s > 0 && v > 0);
+
+ if (v == 0) {
+ memmove(h, p, len - s);
+ p = h + len - s;
+ while (s-- > 0)
+ *p++ = ' ';
+ return (0);
+ }
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '7';
+
+ return (-1);
+}
+
+/*
+ * Format a number into the specified field using base-10.
+ */
+static int
+format_decimal(int64_t v, char *p, int s)
+{
+ int len;
+ char *h;
+
+ len = s;
+ h = p;
+
+ /* Negative values in ar header are meaningless, so use 0. */
+ if (v < 0) {
+ while (len-- > 0)
+ *p++ = '0';
+ return (-1);
+ }
+
+ p += s;
+ do {
+ *--p = (char)('0' + (v % 10));
+ v /= 10;
+ } while (--s > 0 && v > 0);
+
+ if (v == 0) {
+ memmove(h, p, len - s);
+ p = h + len - s;
+ while (s-- > 0)
+ *p++ = ' ';
+ return (0);
+ }
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '9';
+
+ return (-1);
+}
+
+static const char *
+ar_basename(const char *path)
+{
+ const char *endp, *startp;
+
+ endp = path + strlen(path) - 1;
+ /*
+ * For filename with trailing slash(es), we return
+ * NULL indicating an error.
+ */
+ if (*endp == '/')
+ return (NULL);
+
+ /* Find the start of the base */
+ startp = endp;
+ while (startp > path && *(startp - 1) != '/')
+ startp--;
+
+ return (startp);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_by_name.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_by_name.c
new file mode 100644
index 0000000000..bfb4b3545f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_by_name.c
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*setter)(struct archive *); } names[] =
+{
+ { "7zip", archive_write_set_format_7zip },
+ { "ar", archive_write_set_format_ar_bsd },
+ { "arbsd", archive_write_set_format_ar_bsd },
+ { "argnu", archive_write_set_format_ar_svr4 },
+ { "arsvr4", archive_write_set_format_ar_svr4 },
+ { "bin", archive_write_set_format_cpio_bin },
+ { "bsdtar", archive_write_set_format_pax_restricted },
+ { "cd9660", archive_write_set_format_iso9660 },
+ { "cpio", archive_write_set_format_cpio },
+ { "gnutar", archive_write_set_format_gnutar },
+ { "iso", archive_write_set_format_iso9660 },
+ { "iso9660", archive_write_set_format_iso9660 },
+ { "mtree", archive_write_set_format_mtree },
+ { "mtree-classic", archive_write_set_format_mtree_classic },
+ { "newc", archive_write_set_format_cpio_newc },
+ { "odc", archive_write_set_format_cpio_odc },
+ { "oldtar", archive_write_set_format_v7tar },
+ { "pax", archive_write_set_format_pax },
+ { "paxr", archive_write_set_format_pax_restricted },
+ { "posix", archive_write_set_format_pax },
+ { "pwb", archive_write_set_format_cpio_pwb },
+ { "raw", archive_write_set_format_raw },
+ { "rpax", archive_write_set_format_pax_restricted },
+ { "shar", archive_write_set_format_shar },
+ { "shardump", archive_write_set_format_shar_dump },
+ { "ustar", archive_write_set_format_ustar },
+ { "v7tar", archive_write_set_format_v7tar },
+ { "v7", archive_write_set_format_v7tar },
+ { "warc", archive_write_set_format_warc },
+ { "xar", archive_write_set_format_xar },
+ { "zip", archive_write_set_format_zip },
+ { NULL, NULL }
+};
+
+int
+archive_write_set_format_by_name(struct archive *a, const char *name)
+{
+ int i;
+
+ for (i = 0; names[i].name != NULL; i++) {
+ if (strcmp(name, names[i].name) == 0)
+ return ((names[i].setter)(a));
+ }
+
+ archive_set_error(a, EINVAL, "No such format '%s'", name);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio.c
new file mode 100644
index 0000000000..47152cc6a9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio.c
@@ -0,0 +1,11 @@
+#include "archive_platform.h"
+#include "archive.h"
+
+/*
+ * Set output format to the default 'cpio' format.
+ */
+int
+archive_write_set_format_cpio(struct archive *_a)
+{
+ return archive_write_set_format_cpio_odc(_a);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_binary.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_binary.c
new file mode 100644
index 0000000000..d6ce35a7bc
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_binary.c
@@ -0,0 +1,610 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+static ssize_t archive_write_binary_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_binary_close(struct archive_write *);
+static int archive_write_binary_free(struct archive_write *);
+static int archive_write_binary_finish_entry(struct archive_write *);
+static int archive_write_binary_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_binary_options(struct archive_write *,
+ const char *, const char *);
+static int write_header(struct archive_write *, struct archive_entry *);
+
+struct cpio {
+ uint64_t entry_bytes_remaining;
+
+ int64_t ino_next;
+
+ struct { int64_t old; int new;} *ino_list;
+ size_t ino_list_size;
+ size_t ino_list_next;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+/* This struct needs to be packed to get the header right */
+
+#if defined(__GNUC__)
+#define PACKED(x) x __attribute__((packed))
+#elif defined(_MSC_VER)
+#define PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
+#else
+#define PACKED(x) x
+#endif
+
+#define HSIZE 26
+
+PACKED(struct cpio_binary_header {
+ uint16_t h_magic;
+ uint16_t h_dev;
+ uint16_t h_ino;
+ uint16_t h_mode;
+ uint16_t h_uid;
+ uint16_t h_gid;
+ uint16_t h_nlink;
+ uint16_t h_majmin;
+ uint32_t h_mtime;
+ uint16_t h_namesize;
+ uint32_t h_filesize;
+});
+
+/* Back in the day, the 7th Edition cpio.c had this, to
+ * adapt to, as the comment said, "VAX, Interdata, ...":
+ *
+ * union { long l; short s[2]; char c[4]; } U;
+ * #define MKSHORT(v,lv) {U.l=1L;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,v[0]=U.s[0],v[1]=U.s[1];}
+ * long mklong(v)
+ * short v[];
+ * {
+ * U.l = 1;
+ * if(U.c[0])
+ * U.s[0] = v[1], U.s[1] = v[0];
+ * else
+ * U.s[0] = v[0], U.s[1] = v[1];
+ * return U.l;
+ * }
+ *
+ * Of course, that assumes that all machines have little-endian shorts,
+ * and just adapts the others to the special endianness of the PDP-11.
+ *
+ * Now, we could do this:
+ *
+ * union { uint32_t l; uint16_t s[2]; uint8_t c[4]; } U;
+ * #define PUTI16(v,sv) {U.s[0]=1;if(U.c[0]) v=sv; else U.s[0]=sv,U.c[2]=U.c[1],U.c[3]=U.c[0],v=U.s[1];}
+ * #define PUTI32(v,lv) {char_t Ut;U.l=1;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,Ut=U.c[0],U.c[0]=U.c[1],U.c[1]=Ut,Ut=U.c[2],U.c[2]=U.c[3],U.c[3]=Ut,v[0]=U.s[0],v[1]=U.s[1];}
+ *
+ * ...but it feels a little better to do it like this:
+ */
+
+static uint16_t la_swap16(uint16_t in) {
+ union {
+ uint16_t s[2];
+ uint8_t c[4];
+ } U;
+ U.s[0] = 1;
+ if (U.c[0])
+ return in;
+ else {
+ U.s[0] = in;
+ U.c[2] = U.c[1];
+ U.c[3] = U.c[0];
+ return U.s[1];
+ }
+ /* NOTREACHED */
+}
+
+static uint32_t la_swap32(uint32_t in) {
+ union {
+ uint32_t l;
+ uint16_t s[2];
+ uint8_t c[4];
+ } U;
+ U.l = 1;
+ if (U.c[0]) { /* Little-endian */
+ uint16_t t;
+ U.l = in;
+ t = U.s[0];
+ U.s[0] = U.s[1];
+ U.s[1] = t;
+ } else if (U.c[3]) { /* Big-endian */
+ U.l = in;
+ U.s[0] = la_swap16(U.s[0]);
+ U.s[1] = la_swap16(U.s[1]);
+ } else { /* PDP-endian */
+ U.l = in;
+ }
+ return U.l;
+}
+
+/*
+ * Set output format to the selected binary variant
+ */
+static int
+archive_write_set_format_cpio_binary(struct archive *_a, int format)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct cpio *cpio;
+
+ if (sizeof(struct cpio_binary_header) != HSIZE) {
+ archive_set_error(&a->archive, EINVAL,
+ "Binary cpio format not supported on this platform");
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_binary");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+ if (cpio == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = cpio;
+ a->format_name = "cpio";
+ a->format_options = archive_write_binary_options;
+ a->format_write_header = archive_write_binary_header;
+ a->format_write_data = archive_write_binary_data;
+ a->format_finish_entry = archive_write_binary_finish_entry;
+ a->format_close = archive_write_binary_close;
+ a->format_free = archive_write_binary_free;
+ a->archive.archive_format = format;
+ switch (format) {
+ case ARCHIVE_FORMAT_CPIO_PWB:
+ a->archive.archive_format_name = "PWB cpio";
+ break;
+ case ARCHIVE_FORMAT_CPIO_BIN_LE:
+ a->archive.archive_format_name = "7th Edition cpio";
+ break;
+ default:
+ archive_set_error(&a->archive, EINVAL, "binary format must be 'pwb' or 'bin'");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set output format to PWB (6th Edition) binary format
+ */
+int
+archive_write_set_format_cpio_pwb(struct archive *_a)
+{
+ return archive_write_set_format_cpio_binary(_a, ARCHIVE_FORMAT_CPIO_PWB);
+}
+
+/*
+ * Set output format to 7th Edition binary format
+ */
+int
+archive_write_set_format_cpio_bin(struct archive *_a)
+{
+ return archive_write_set_format_cpio_binary(_a, ARCHIVE_FORMAT_CPIO_BIN_LE);
+}
+
+static int
+archive_write_binary_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct cpio *cpio = (struct cpio *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ cpio->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (cpio->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Ino values are as long as 64 bits on some systems; cpio format
+ * only allows 16 bits and relies on the ino values to identify hardlinked
+ * files. So, we can't merely "hash" the ino numbers since collisions
+ * would corrupt the archive. Instead, we generate synthetic ino values
+ * to store in the archive and maintain a map of original ino values to
+ * synthetic ones so we can preserve hardlink information.
+ *
+ * TODO: Make this more efficient. It's not as bad as it looks (most
+ * files don't have any hardlinks and we don't do any work here for those),
+ * but it wouldn't be hard to do better.
+ *
+ * TODO: Work with dev/ino pairs here instead of just ino values.
+ */
+static int
+synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
+{
+ int64_t ino = archive_entry_ino64(entry);
+ int ino_new;
+ size_t i;
+
+ /*
+ * If no index number was given, don't assign one. In
+ * particular, this handles the end-of-archive marker
+ * correctly by giving it a zero index value. (This is also
+ * why we start our synthetic index numbers with one below.)
+ */
+ if (ino == 0)
+ return (0);
+
+ /* Don't store a mapping if we don't need to. */
+ if (archive_entry_nlink(entry) < 2) {
+ return (int)(++cpio->ino_next);
+ }
+
+ /* Look up old ino; if we have it, this is a hardlink
+ * and we reuse the same value. */
+ for (i = 0; i < cpio->ino_list_next; ++i) {
+ if (cpio->ino_list[i].old == ino)
+ return (cpio->ino_list[i].new);
+ }
+
+ /* Assign a new index number. */
+ ino_new = (int)(++cpio->ino_next);
+
+ /* Ensure space for the new mapping. */
+ if (cpio->ino_list_size <= cpio->ino_list_next) {
+ size_t newsize = cpio->ino_list_size < 512
+ ? 512 : cpio->ino_list_size * 2;
+ void *newlist = realloc(cpio->ino_list,
+ sizeof(cpio->ino_list[0]) * newsize);
+ if (newlist == NULL)
+ return (-1);
+
+ cpio->ino_list_size = newsize;
+ cpio->ino_list = newlist;
+ }
+
+ /* Record and return the new value. */
+ cpio->ino_list[cpio->ino_list_next].old = ino;
+ cpio->ino_list[cpio->ino_list_next].new = ino_new;
+ ++cpio->ino_list_next;
+ return (ino_new);
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+ struct cpio *cpio;
+ struct archive_string_conv *sconv;
+
+ cpio = (struct cpio *)a->format_data;
+ sconv = cpio->opt_sconv;
+ if (sconv == NULL) {
+ if (!cpio->init_default_conversion) {
+ cpio->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ cpio->init_default_conversion = 1;
+ }
+ sconv = cpio->sconv_default;
+ }
+ return (sconv);
+}
+
+static int
+archive_write_binary_header(struct archive_write *a, struct archive_entry *entry)
+{
+ const char *path;
+ size_t len;
+
+ if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
+ archive_set_error(&a->archive, -1, "Filetype required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+ && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ if (len == 0 || path == NULL || path[0] == '\0') {
+ archive_set_error(&a->archive, -1, "Pathname required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
+ archive_set_error(&a->archive, -1, "Size required");
+ return (ARCHIVE_FAILED);
+ }
+ return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct cpio *cpio;
+ const char *p, *path;
+ int pathlength, ret, ret_final;
+ int64_t ino;
+ struct cpio_binary_header h;
+ struct archive_string_conv *sconv;
+ struct archive_entry *entry_main;
+ size_t len;
+
+ cpio = (struct cpio *)a->format_data;
+ ret_final = ARCHIVE_OK;
+ sconv = get_sconv(a);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+
+ ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ /* Include trailing null */
+ pathlength = (int)len + 1;
+
+ h.h_magic = la_swap16(070707);
+ h.h_dev = la_swap16(archive_entry_dev(entry));
+
+ ino = synthesize_ino_value(cpio, entry);
+ if (ino < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ino translation table");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ } else if (ino > 077777) {
+ archive_set_error(&a->archive, ERANGE,
+ "Too many files for this cpio format");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ h.h_ino = la_swap16((uint16_t)ino);
+
+ h.h_mode = archive_entry_mode(entry);
+ if (((h.h_mode & AE_IFMT) == AE_IFSOCK) || ((h.h_mode & AE_IFMT) == AE_IFIFO)) {
+ archive_set_error(&a->archive, EINVAL,
+ "sockets and fifos cannot be represented in the binary cpio formats");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) {
+ if ((h.h_mode & AE_IFMT) == AE_IFLNK) {
+ archive_set_error(&a->archive, EINVAL,
+ "symbolic links cannot be represented in the PWB cpio format");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ /* we could turn off AE_IFREG here, but it does no harm, */
+ /* and allows v7 cpio to read the entry without confusion */
+ }
+ h.h_mode = la_swap16(h.h_mode);
+
+ h.h_uid = la_swap16((uint16_t)archive_entry_uid(entry));
+ h.h_gid = la_swap16((uint16_t)archive_entry_gid(entry));
+ h.h_nlink = la_swap16((uint16_t)archive_entry_nlink(entry));
+
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR)
+ h.h_majmin = la_swap16(archive_entry_rdev(entry));
+ else
+ h.h_majmin = 0;
+
+ h.h_mtime = la_swap32((uint32_t)archive_entry_mtime(entry));
+ h.h_namesize = la_swap16(pathlength);
+
+ /* Non-regular files don't store bodies. */
+ if (archive_entry_filetype(entry) != AE_IFREG)
+ archive_entry_set_size(entry, 0);
+
+ /* Symlinks get the link written as the body of the entry. */
+ ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_symlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+
+ if (len > 0 && p != NULL && *p != '\0') {
+ if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) {
+ archive_set_error(&a->archive, EINVAL,
+ "symlinks are not supported by UNIX V6 or by PWB cpio");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ h.h_filesize = la_swap32((uint32_t)strlen(p)); /* symlink */
+ } else {
+ if ((a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) &&
+ (archive_entry_size(entry) > 256*256*256-1)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File is too large for PWB binary cpio format.");
+ ret_final = ARCHIVE_FAILED;
+ goto exit_write_header;
+ } else if (archive_entry_size(entry) > INT32_MAX) {
+ archive_set_error(&a->archive, ERANGE,
+ "File is too large for binary cpio format.");
+ ret_final = ARCHIVE_FAILED;
+ goto exit_write_header;
+ }
+ h.h_filesize = la_swap32((uint32_t)archive_entry_size(entry)); /* file */
+ }
+
+ ret = __archive_write_output(a, &h, HSIZE);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ ret = __archive_write_output(a, path, pathlength);
+ if ((ret == ARCHIVE_OK) && ((pathlength % 2) != 0))
+ ret = __archive_write_nulls(a, 1);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ cpio->entry_bytes_remaining = archive_entry_size(entry);
+ if ((cpio->entry_bytes_remaining % 2) != 0)
+ cpio->entry_bytes_remaining++;
+
+ /* Write the symlink now. */
+ if (p != NULL && *p != '\0') {
+ ret = __archive_write_output(a, p, strlen(p));
+ if ((ret == ARCHIVE_OK) && ((strlen(p) % 2) != 0))
+ ret = __archive_write_nulls(a, 1);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ }
+
+exit_write_header:
+ archive_entry_free(entry_main);
+ return (ret_final);
+}
+
+static ssize_t
+archive_write_binary_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct cpio *cpio;
+ int ret;
+
+ cpio = (struct cpio *)a->format_data;
+ if (s > cpio->entry_bytes_remaining)
+ s = (size_t)cpio->entry_bytes_remaining;
+
+ ret = __archive_write_output(a, buff, s);
+ cpio->entry_bytes_remaining -= s;
+ if (ret >= 0)
+ return (s);
+ else
+ return (ret);
+}
+
+static int
+archive_write_binary_close(struct archive_write *a)
+{
+ int er;
+ struct archive_entry *trailer;
+
+ trailer = archive_entry_new2(NULL);
+ /* nlink = 1 here for GNU cpio compat. */
+ archive_entry_set_nlink(trailer, 1);
+ archive_entry_set_size(trailer, 0);
+ archive_entry_set_pathname(trailer, "TRAILER!!!");
+ er = write_header(a, trailer);
+ archive_entry_free(trailer);
+ return (er);
+}
+
+static int
+archive_write_binary_free(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ free(cpio->ino_list);
+ free(cpio);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_binary_finish_entry(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ return (__archive_write_nulls(a,
+ (size_t)cpio->entry_bytes_remaining));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_newc.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_newc.c
new file mode 100644
index 0000000000..f0f39809da
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_newc.c
@@ -0,0 +1,457 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio_newc.c 201160 2009-12-29 05:41:57Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+static ssize_t archive_write_newc_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_newc_close(struct archive_write *);
+static int archive_write_newc_free(struct archive_write *);
+static int archive_write_newc_finish_entry(struct archive_write *);
+static int archive_write_newc_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_newc_options(struct archive_write *,
+ const char *, const char *);
+static int format_hex(int64_t, void *, int);
+static int64_t format_hex_recursive(int64_t, char *, int);
+static int write_header(struct archive_write *, struct archive_entry *);
+
+struct cpio {
+ uint64_t entry_bytes_remaining;
+ int padding;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+#define c_magic_offset 0
+#define c_magic_size 6
+#define c_ino_offset 6
+#define c_ino_size 8
+#define c_mode_offset 14
+#define c_mode_size 8
+#define c_uid_offset 22
+#define c_uid_size 8
+#define c_gid_offset 30
+#define c_gid_size 8
+#define c_nlink_offset 38
+#define c_nlink_size 8
+#define c_mtime_offset 46
+#define c_mtime_size 8
+#define c_filesize_offset 54
+#define c_filesize_size 8
+#define c_devmajor_offset 62
+#define c_devmajor_size 8
+#define c_devminor_offset 70
+#define c_devminor_size 8
+#define c_rdevmajor_offset 78
+#define c_rdevmajor_size 8
+#define c_rdevminor_offset 86
+#define c_rdevminor_size 8
+#define c_namesize_offset 94
+#define c_namesize_size 8
+#define c_checksum_offset 102
+#define c_checksum_size 8
+#define c_header_size 110
+
+/* Logic trick: difference between 'n' and next multiple of 4 */
+#define PAD4(n) (3 & (1 + ~(n)))
+
+/*
+ * Set output format to 'cpio' format.
+ */
+int
+archive_write_set_format_cpio_newc(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct cpio *cpio;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+ if (cpio == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = cpio;
+ a->format_name = "cpio";
+ a->format_options = archive_write_newc_options;
+ a->format_write_header = archive_write_newc_header;
+ a->format_write_data = archive_write_newc_data;
+ a->format_finish_entry = archive_write_newc_finish_entry;
+ a->format_close = archive_write_newc_close;
+ a->format_free = archive_write_newc_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+ a->archive.archive_format_name = "SVR4 cpio nocrc";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_newc_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct cpio *cpio = (struct cpio *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ cpio->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (cpio->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+ struct cpio *cpio;
+ struct archive_string_conv *sconv;
+
+ cpio = (struct cpio *)a->format_data;
+ sconv = cpio->opt_sconv;
+ if (sconv == NULL) {
+ if (!cpio->init_default_conversion) {
+ cpio->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ cpio->init_default_conversion = 1;
+ }
+ sconv = cpio->sconv_default;
+ }
+ return (sconv);
+}
+
+static int
+archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
+{
+ const char *path;
+ size_t len;
+
+ if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
+ archive_set_error(&a->archive, -1, "Filetype required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+ && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ if (len == 0 || path == NULL || path[0] == '\0') {
+ archive_set_error(&a->archive, -1, "Pathname required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (archive_entry_hardlink(entry) == NULL
+ && (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0)) {
+ archive_set_error(&a->archive, -1, "Size required");
+ return (ARCHIVE_FAILED);
+ }
+ return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ int64_t ino;
+ struct cpio *cpio;
+ const char *p, *path;
+ int pathlength, ret, ret_final;
+ char h[c_header_size];
+ struct archive_string_conv *sconv;
+ struct archive_entry *entry_main;
+ size_t len;
+ int pad;
+
+ cpio = (struct cpio *)a->format_data;
+ ret_final = ARCHIVE_OK;
+ sconv = get_sconv(a);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+
+ ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ pathlength = (int)len + 1; /* Include trailing null. */
+
+ memset(h, 0, c_header_size);
+ format_hex(0x070701, h + c_magic_offset, c_magic_size);
+ format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset,
+ c_devmajor_size);
+ format_hex(archive_entry_devminor(entry), h + c_devminor_offset,
+ c_devminor_size);
+
+ ino = archive_entry_ino64(entry);
+ if (ino > 0xffffffff) {
+ archive_set_error(&a->archive, ERANGE,
+ "large inode number truncated");
+ ret_final = ARCHIVE_WARN;
+ }
+
+ /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
+ format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size);
+ format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
+ format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
+ format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
+ format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR) {
+ format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size);
+ format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size);
+ } else {
+ format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size);
+ format_hex(0, h + c_rdevminor_offset, c_rdevminor_size);
+ }
+ format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
+ format_hex(pathlength, h + c_namesize_offset, c_namesize_size);
+ format_hex(0, h + c_checksum_offset, c_checksum_size);
+
+ /* Non-regular files don't store bodies. */
+ if (archive_entry_filetype(entry) != AE_IFREG)
+ archive_entry_set_size(entry, 0);
+
+ /* Symlinks get the link written as the body of the entry. */
+ ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Likname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_symlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ if (len > 0 && p != NULL && *p != '\0')
+ ret = format_hex(strlen(p), h + c_filesize_offset,
+ c_filesize_size);
+ else
+ ret = format_hex(archive_entry_size(entry),
+ h + c_filesize_offset, c_filesize_size);
+ if (ret) {
+ archive_set_error(&a->archive, ERANGE,
+ "File is too large for this format.");
+ ret_final = ARCHIVE_FAILED;
+ goto exit_write_header;
+ }
+
+ ret = __archive_write_output(a, h, c_header_size);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ /* Pad pathname to even length. */
+ ret = __archive_write_output(a, path, pathlength);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ pad = PAD4(pathlength + c_header_size);
+ if (pad) {
+ ret = __archive_write_output(a, "\0\0\0", pad);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ }
+
+ cpio->entry_bytes_remaining = archive_entry_size(entry);
+ cpio->padding = (int)PAD4(cpio->entry_bytes_remaining);
+
+ /* Write the symlink now. */
+ if (p != NULL && *p != '\0') {
+ ret = __archive_write_output(a, p, strlen(p));
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ pad = PAD4(strlen(p));
+ ret = __archive_write_output(a, "\0\0\0", pad);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ }
+exit_write_header:
+ archive_entry_free(entry_main);
+ return (ret_final);
+}
+
+static ssize_t
+archive_write_newc_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct cpio *cpio;
+ int ret;
+
+ cpio = (struct cpio *)a->format_data;
+ if (s > cpio->entry_bytes_remaining)
+ s = (size_t)cpio->entry_bytes_remaining;
+
+ ret = __archive_write_output(a, buff, s);
+ cpio->entry_bytes_remaining -= s;
+ if (ret >= 0)
+ return (s);
+ else
+ return (ret);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_hex(int64_t v, void *p, int digits)
+{
+ int64_t max;
+ int ret;
+
+ max = (((int64_t)1) << (digits * 4)) - 1;
+ if (v >= 0 && v <= max) {
+ format_hex_recursive(v, (char *)p, digits);
+ ret = 0;
+ } else {
+ format_hex_recursive(max, (char *)p, digits);
+ ret = -1;
+ }
+ return (ret);
+}
+
+static int64_t
+format_hex_recursive(int64_t v, char *p, int s)
+{
+ if (s == 0)
+ return (v);
+ v = format_hex_recursive(v, p+1, s-1);
+ *p = "0123456789abcdef"[v & 0xf];
+ return (v >> 4);
+}
+
+static int
+archive_write_newc_close(struct archive_write *a)
+{
+ int er;
+ struct archive_entry *trailer;
+
+ trailer = archive_entry_new();
+ archive_entry_set_nlink(trailer, 1);
+ archive_entry_set_size(trailer, 0);
+ archive_entry_set_pathname(trailer, "TRAILER!!!");
+ /* Bypass the required data checks. */
+ er = write_header(a, trailer);
+ archive_entry_free(trailer);
+ return (er);
+}
+
+static int
+archive_write_newc_free(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ free(cpio);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_newc_finish_entry(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ return (__archive_write_nulls(a,
+ (size_t)cpio->entry_bytes_remaining + cpio->padding));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_odc.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_odc.c
new file mode 100644
index 0000000000..091925a2f9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_cpio_odc.c
@@ -0,0 +1,500 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+static ssize_t archive_write_odc_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_odc_close(struct archive_write *);
+static int archive_write_odc_free(struct archive_write *);
+static int archive_write_odc_finish_entry(struct archive_write *);
+static int archive_write_odc_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_odc_options(struct archive_write *,
+ const char *, const char *);
+static int format_octal(int64_t, void *, int);
+static int64_t format_octal_recursive(int64_t, char *, int);
+static int write_header(struct archive_write *, struct archive_entry *);
+
+struct cpio {
+ uint64_t entry_bytes_remaining;
+
+ int64_t ino_next;
+
+ struct { int64_t old; int new;} *ino_list;
+ size_t ino_list_size;
+ size_t ino_list_next;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+#define c_magic_offset 0
+#define c_magic_size 6
+#define c_dev_offset 6
+#define c_dev_size 6
+#define c_ino_offset 12
+#define c_ino_size 6
+#define c_mode_offset 18
+#define c_mode_size 6
+#define c_uid_offset 24
+#define c_uid_size 6
+#define c_gid_offset 30
+#define c_gid_size 6
+#define c_nlink_offset 36
+#define c_nlink_size 6
+#define c_rdev_offset 42
+#define c_rdev_size 6
+#define c_mtime_offset 48
+#define c_mtime_size 11
+#define c_namesize_offset 59
+#define c_namesize_size 6
+#define c_filesize_offset 65
+#define c_filesize_size 11
+
+/*
+ * Set output format to 'cpio' format.
+ */
+int
+archive_write_set_format_cpio_odc(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct cpio *cpio;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_odc");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+ if (cpio == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = cpio;
+ a->format_name = "cpio";
+ a->format_options = archive_write_odc_options;
+ a->format_write_header = archive_write_odc_header;
+ a->format_write_data = archive_write_odc_data;
+ a->format_finish_entry = archive_write_odc_finish_entry;
+ a->format_close = archive_write_odc_close;
+ a->format_free = archive_write_odc_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+ a->archive.archive_format_name = "POSIX cpio";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_odc_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct cpio *cpio = (struct cpio *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ cpio->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (cpio->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Ino values are as long as 64 bits on some systems; cpio format
+ * only allows 18 bits and relies on the ino values to identify hardlinked
+ * files. So, we can't merely "hash" the ino numbers since collisions
+ * would corrupt the archive. Instead, we generate synthetic ino values
+ * to store in the archive and maintain a map of original ino values to
+ * synthetic ones so we can preserve hardlink information.
+ *
+ * TODO: Make this more efficient. It's not as bad as it looks (most
+ * files don't have any hardlinks and we don't do any work here for those),
+ * but it wouldn't be hard to do better.
+ *
+ * TODO: Work with dev/ino pairs here instead of just ino values.
+ */
+static int
+synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
+{
+ int64_t ino = archive_entry_ino64(entry);
+ int ino_new;
+ size_t i;
+
+ /*
+ * If no index number was given, don't assign one. In
+ * particular, this handles the end-of-archive marker
+ * correctly by giving it a zero index value. (This is also
+ * why we start our synthetic index numbers with one below.)
+ */
+ if (ino == 0)
+ return (0);
+
+ /* Don't store a mapping if we don't need to. */
+ if (archive_entry_nlink(entry) < 2) {
+ return (int)(++cpio->ino_next);
+ }
+
+ /* Look up old ino; if we have it, this is a hardlink
+ * and we reuse the same value. */
+ for (i = 0; i < cpio->ino_list_next; ++i) {
+ if (cpio->ino_list[i].old == ino)
+ return (cpio->ino_list[i].new);
+ }
+
+ /* Assign a new index number. */
+ ino_new = (int)(++cpio->ino_next);
+
+ /* Ensure space for the new mapping. */
+ if (cpio->ino_list_size <= cpio->ino_list_next) {
+ size_t newsize = cpio->ino_list_size < 512
+ ? 512 : cpio->ino_list_size * 2;
+ void *newlist = realloc(cpio->ino_list,
+ sizeof(cpio->ino_list[0]) * newsize);
+ if (newlist == NULL)
+ return (-1);
+
+ cpio->ino_list_size = newsize;
+ cpio->ino_list = newlist;
+ }
+
+ /* Record and return the new value. */
+ cpio->ino_list[cpio->ino_list_next].old = ino;
+ cpio->ino_list[cpio->ino_list_next].new = ino_new;
+ ++cpio->ino_list_next;
+ return (ino_new);
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+ struct cpio *cpio;
+ struct archive_string_conv *sconv;
+
+ cpio = (struct cpio *)a->format_data;
+ sconv = cpio->opt_sconv;
+ if (sconv == NULL) {
+ if (!cpio->init_default_conversion) {
+ cpio->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ cpio->init_default_conversion = 1;
+ }
+ sconv = cpio->sconv_default;
+ }
+ return (sconv);
+}
+
+static int
+archive_write_odc_header(struct archive_write *a, struct archive_entry *entry)
+{
+ const char *path;
+ size_t len;
+
+ if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
+ archive_set_error(&a->archive, -1, "Filetype required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+ && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ if (len == 0 || path == NULL || path[0] == '\0') {
+ archive_set_error(&a->archive, -1, "Pathname required");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
+ archive_set_error(&a->archive, -1, "Size required");
+ return (ARCHIVE_FAILED);
+ }
+ return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct cpio *cpio;
+ const char *p, *path;
+ int pathlength, ret, ret_final;
+ int64_t ino;
+ char h[76];
+ struct archive_string_conv *sconv;
+ struct archive_entry *entry_main;
+ size_t len;
+
+ cpio = (struct cpio *)a->format_data;
+ ret_final = ARCHIVE_OK;
+ sconv = get_sconv(a);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+
+ ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ /* Include trailing null. */
+ pathlength = (int)len + 1;
+
+ memset(h, 0, sizeof(h));
+ format_octal(070707, h + c_magic_offset, c_magic_size);
+ format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size);
+
+ ino = synthesize_ino_value(cpio, entry);
+ if (ino < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for ino translation table");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ } else if (ino > 0777777) {
+ archive_set_error(&a->archive, ERANGE,
+ "Too many files for this cpio format");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ format_octal(ino & 0777777, h + c_ino_offset, c_ino_size);
+
+ /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
+ format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
+ format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
+ format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
+ format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR)
+ format_octal(archive_entry_rdev(entry), h + c_rdev_offset, c_rdev_size);
+ else
+ format_octal(0, h + c_rdev_offset, c_rdev_size);
+ format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
+ format_octal(pathlength, h + c_namesize_offset, c_namesize_size);
+
+ /* Non-regular files don't store bodies. */
+ if (archive_entry_filetype(entry) != AE_IFREG)
+ archive_entry_set_size(entry, 0);
+
+ /* Symlinks get the link written as the body of the entry. */
+ ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+ if (ret != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_symlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret_final = ARCHIVE_WARN;
+ }
+ if (len > 0 && p != NULL && *p != '\0')
+ ret = format_octal(strlen(p), h + c_filesize_offset,
+ c_filesize_size);
+ else
+ ret = format_octal(archive_entry_size(entry),
+ h + c_filesize_offset, c_filesize_size);
+ if (ret) {
+ archive_set_error(&a->archive, ERANGE,
+ "File is too large for cpio format.");
+ ret_final = ARCHIVE_FAILED;
+ goto exit_write_header;
+ }
+
+ ret = __archive_write_output(a, h, sizeof(h));
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ ret = __archive_write_output(a, path, pathlength);
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+
+ cpio->entry_bytes_remaining = archive_entry_size(entry);
+
+ /* Write the symlink now. */
+ if (p != NULL && *p != '\0') {
+ ret = __archive_write_output(a, p, strlen(p));
+ if (ret != ARCHIVE_OK) {
+ ret_final = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ }
+exit_write_header:
+ archive_entry_free(entry_main);
+ return (ret_final);
+}
+
+static ssize_t
+archive_write_odc_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct cpio *cpio;
+ int ret;
+
+ cpio = (struct cpio *)a->format_data;
+ if (s > cpio->entry_bytes_remaining)
+ s = (size_t)cpio->entry_bytes_remaining;
+
+ ret = __archive_write_output(a, buff, s);
+ cpio->entry_bytes_remaining -= s;
+ if (ret >= 0)
+ return (s);
+ else
+ return (ret);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, void *p, int digits)
+{
+ int64_t max;
+ int ret;
+
+ max = (((int64_t)1) << (digits * 3)) - 1;
+ if (v >= 0 && v <= max) {
+ format_octal_recursive(v, (char *)p, digits);
+ ret = 0;
+ } else {
+ format_octal_recursive(max, (char *)p, digits);
+ ret = -1;
+ }
+ return (ret);
+}
+
+static int64_t
+format_octal_recursive(int64_t v, char *p, int s)
+{
+ if (s == 0)
+ return (v);
+ v = format_octal_recursive(v, p+1, s-1);
+ *p = '0' + ((char)v & 7);
+ return (v >> 3);
+}
+
+static int
+archive_write_odc_close(struct archive_write *a)
+{
+ int er;
+ struct archive_entry *trailer;
+
+ trailer = archive_entry_new2(NULL);
+ /* nlink = 1 here for GNU cpio compat. */
+ archive_entry_set_nlink(trailer, 1);
+ archive_entry_set_size(trailer, 0);
+ archive_entry_set_pathname(trailer, "TRAILER!!!");
+ er = write_header(a, trailer);
+ archive_entry_free(trailer);
+ return (er);
+}
+
+static int
+archive_write_odc_free(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ free(cpio->ino_list);
+ free(cpio);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_odc_finish_entry(struct archive_write *a)
+{
+ struct cpio *cpio;
+
+ cpio = (struct cpio *)a->format_data;
+ return (__archive_write_nulls(a,
+ (size_t)cpio->entry_bytes_remaining));
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_filter_by_ext.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_filter_by_ext.c
new file mode 100644
index 0000000000..9fe21e4542
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_filter_by_ext.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2015 Okhotnikov Kirill
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*format)(struct archive *); int (*filter)(struct archive *); } names[] =
+{
+ { ".7z", archive_write_set_format_7zip, archive_write_add_filter_none},
+ { ".zip", archive_write_set_format_zip, archive_write_add_filter_none},
+ { ".jar", archive_write_set_format_zip, archive_write_add_filter_none},
+ { ".cpio", archive_write_set_format_cpio, archive_write_add_filter_none},
+ { ".iso", archive_write_set_format_iso9660, archive_write_add_filter_none},
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
+ { ".a", archive_write_set_format_ar_bsd, archive_write_add_filter_none},
+ { ".ar", archive_write_set_format_ar_bsd, archive_write_add_filter_none},
+#else
+ { ".a", archive_write_set_format_ar_svr4, archive_write_add_filter_none},
+ { ".ar", archive_write_set_format_ar_svr4, archive_write_add_filter_none},
+#endif
+ { ".tar", archive_write_set_format_pax_restricted, archive_write_add_filter_none},
+ { ".tgz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip},
+ { ".tar.gz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip},
+ { ".tar.bz2", archive_write_set_format_pax_restricted, archive_write_add_filter_bzip2},
+ { ".tar.xz", archive_write_set_format_pax_restricted, archive_write_add_filter_xz},
+ { NULL, NULL, NULL }
+};
+
+static
+int cmpsuff(const char *str, const char *suffix)
+{
+ size_t length_str, length_suffix;
+
+ if ((str == NULL) || (suffix == NULL))
+ return -1;
+
+ length_str = strlen(str);
+ length_suffix = strlen(suffix);
+
+ if (length_str >= length_suffix) {
+ return strcmp(str + (length_str - length_suffix), suffix);
+ } else {
+ return -1;
+ }
+}
+
+static int get_array_index(const char *name)
+{
+ int i;
+
+ for (i = 0; names[i].name != NULL; i++)
+ {
+ if (cmpsuff(name, names[i].name) == 0)
+ return i;
+ }
+ return -1;
+
+}
+
+int
+archive_write_set_format_filter_by_ext(struct archive *a, const char *filename)
+{
+ int names_index = get_array_index(filename);
+
+ if (names_index >= 0)
+ {
+ int format_state = (names[names_index].format)(a);
+ if (format_state == ARCHIVE_OK)
+ return ((names[names_index].filter)(a));
+ else
+ return format_state;
+ }
+
+ archive_set_error(a, EINVAL, "No such format '%s'", filename);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext)
+{
+ int names_index = get_array_index(filename);
+
+ if (names_index < 0)
+ names_index = get_array_index(def_ext);
+
+ if (names_index >= 0)
+ {
+ int format_state = (names[names_index].format)(a);
+ if (format_state == ARCHIVE_OK)
+ return ((names[names_index].filter)(a));
+ else
+ return format_state;
+ }
+
+ archive_set_error(a, EINVAL, "No such format '%s'", filename);
+ a->state = ARCHIVE_STATE_FATAL;
+ return (ARCHIVE_FATAL);
+}
+
+
+
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_gnutar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_gnutar.c
new file mode 100644
index 0000000000..ec29c5c418
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_gnutar.c
@@ -0,0 +1,755 @@
+/*-
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Author: Jonas Gastal <jgastal@profusion.mobi>
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_gnu_tar.c 191579 2009-04-27 18:35:03Z gastal $");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct gnutar {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+ const char * linkname;
+ size_t linkname_length;
+ const char * pathname;
+ size_t pathname_length;
+ const char * uname;
+ size_t uname_length;
+ const char * gname;
+ size_t gname_length;
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+/*
+ * Define structure of GNU tar header.
+ */
+#define GNUTAR_name_offset 0
+#define GNUTAR_name_size 100
+#define GNUTAR_mode_offset 100
+#define GNUTAR_mode_size 7
+#define GNUTAR_mode_max_size 8
+#define GNUTAR_uid_offset 108
+#define GNUTAR_uid_size 7
+#define GNUTAR_uid_max_size 8
+#define GNUTAR_gid_offset 116
+#define GNUTAR_gid_size 7
+#define GNUTAR_gid_max_size 8
+#define GNUTAR_size_offset 124
+#define GNUTAR_size_size 11
+#define GNUTAR_size_max_size 12
+#define GNUTAR_mtime_offset 136
+#define GNUTAR_mtime_size 11
+#define GNUTAR_mtime_max_size 11
+#define GNUTAR_checksum_offset 148
+#define GNUTAR_checksum_size 8
+#define GNUTAR_typeflag_offset 156
+#define GNUTAR_typeflag_size 1
+#define GNUTAR_linkname_offset 157
+#define GNUTAR_linkname_size 100
+#define GNUTAR_magic_offset 257
+#define GNUTAR_magic_size 6
+#define GNUTAR_version_offset 263
+#define GNUTAR_version_size 2
+#define GNUTAR_uname_offset 265
+#define GNUTAR_uname_size 32
+#define GNUTAR_gname_offset 297
+#define GNUTAR_gname_size 32
+#define GNUTAR_rdevmajor_offset 329
+#define GNUTAR_rdevmajor_size 6
+#define GNUTAR_rdevmajor_max_size 8
+#define GNUTAR_rdevminor_offset 337
+#define GNUTAR_rdevminor_size 6
+#define GNUTAR_rdevminor_max_size 8
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+ /* name: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Mode, null termination: 8 bytes */
+ '0','0','0','0','0','0', '0','\0',
+ /* uid, null termination: 8 bytes */
+ '0','0','0','0','0','0', '0','\0',
+ /* gid, null termination: 8 bytes */
+ '0','0','0','0','0','0', '0','\0',
+ /* size, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', '\0',
+ /* mtime, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', '\0',
+ /* Initial checksum value: 8 spaces */
+ ' ',' ',' ',' ',' ',' ',' ',' ',
+ /* Typeflag: 1 byte */
+ '0', /* '0' = regular file */
+ /* Linkname: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Magic: 8 bytes */
+ 'u','s','t','a','r',' ', ' ','\0',
+ /* Uname: 32 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ /* Gname: 32 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ /* rdevmajor + null padding: 8 bytes */
+ '\0','\0','\0','\0','\0','\0', '\0','\0',
+ /* rdevminor + null padding: 8 bytes */
+ '\0','\0','\0','\0','\0','\0', '\0','\0',
+ /* Padding: 167 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0
+};
+
+static int archive_write_gnutar_options(struct archive_write *,
+ const char *, const char *);
+static int archive_format_gnutar_header(struct archive_write *, char h[512],
+ struct archive_entry *, int tartype);
+static int archive_write_gnutar_header(struct archive_write *,
+ struct archive_entry *entry);
+static ssize_t archive_write_gnutar_data(struct archive_write *a, const void *buff,
+ size_t s);
+static int archive_write_gnutar_free(struct archive_write *);
+static int archive_write_gnutar_close(struct archive_write *);
+static int archive_write_gnutar_finish_entry(struct archive_write *);
+static int format_256(int64_t, char *, int);
+static int format_number(int64_t, char *, int size, int maxsize);
+static int format_octal(int64_t, char *, int);
+
+/*
+ * Set output format to 'GNU tar' format.
+ */
+int
+archive_write_set_format_gnutar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct gnutar *gnutar;
+
+ gnutar = (struct gnutar *)calloc(1, sizeof(*gnutar));
+ if (gnutar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate gnutar data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = gnutar;
+ a->format_name = "gnutar";
+ a->format_options = archive_write_gnutar_options;
+ a->format_write_header = archive_write_gnutar_header;
+ a->format_write_data = archive_write_gnutar_data;
+ a->format_close = archive_write_gnutar_close;
+ a->format_free = archive_write_gnutar_free;
+ a->format_finish_entry = archive_write_gnutar_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+ a->archive.archive_format_name = "GNU tar";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_gnutar_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct gnutar *gnutar = (struct gnutar *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ gnutar->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (gnutar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_gnutar_close(struct archive_write *a)
+{
+ return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_gnutar_free(struct archive_write *a)
+{
+ struct gnutar *gnutar;
+
+ gnutar = (struct gnutar *)a->format_data;
+ free(gnutar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_gnutar_finish_entry(struct archive_write *a)
+{
+ struct gnutar *gnutar;
+ int ret;
+
+ gnutar = (struct gnutar *)a->format_data;
+ ret = __archive_write_nulls(a, (size_t)
+ (gnutar->entry_bytes_remaining + gnutar->entry_padding));
+ gnutar->entry_bytes_remaining = gnutar->entry_padding = 0;
+ return (ret);
+}
+
+static ssize_t
+archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct gnutar *gnutar;
+ int ret;
+
+ gnutar = (struct gnutar *)a->format_data;
+ if (s > gnutar->entry_bytes_remaining)
+ s = (size_t)gnutar->entry_bytes_remaining;
+ ret = __archive_write_output(a, buff, s);
+ gnutar->entry_bytes_remaining -= s;
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ return (s);
+}
+
+static int
+archive_write_gnutar_header(struct archive_write *a,
+ struct archive_entry *entry)
+{
+ char buff[512];
+ int r, ret, ret2 = ARCHIVE_OK;
+ int tartype;
+ struct gnutar *gnutar;
+ struct archive_string_conv *sconv;
+ struct archive_entry *entry_main;
+
+ gnutar = (struct gnutar *)a->format_data;
+
+ /* Setup default string conversion. */
+ if (gnutar->opt_sconv == NULL) {
+ if (!gnutar->init_default_conversion) {
+ gnutar->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ gnutar->init_default_conversion = 1;
+ }
+ sconv = gnutar->sconv_default;
+ } else
+ sconv = gnutar->opt_sconv;
+
+ /* Only regular files (not hardlinks) have data. */
+ if (archive_entry_hardlink(entry) != NULL ||
+ archive_entry_symlink(entry) != NULL ||
+ !(archive_entry_filetype(entry) == AE_IFREG))
+ archive_entry_set_size(entry, 0);
+
+ if (AE_IFDIR == archive_entry_filetype(entry)) {
+ const char *p;
+ size_t path_length;
+ /*
+ * Ensure a trailing '/'. Modify the entry so
+ * the client sees the change.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wp;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+ struct archive_wstring ws;
+
+ archive_string_init(&ws);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(&ws,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ archive_wstring_free(&ws);
+ return(ARCHIVE_FATAL);
+ }
+ /* Should we keep '\' ? */
+ if (wp[path_length -1] == L'\\')
+ path_length--;
+ archive_wstrncpy(&ws, wp, path_length);
+ archive_wstrappend_wchar(&ws, L'/');
+ archive_entry_copy_pathname_w(entry, ws.s);
+ archive_wstring_free(&ws);
+ p = NULL;
+ } else
+#endif
+ p = archive_entry_pathname(entry);
+ /*
+ * On Windows, this is a backup operation just in
+ * case getting WCS failed. On POSIX, this is a
+ * normal operation.
+ */
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+ struct archive_string as;
+
+ archive_string_init(&as);
+ path_length = strlen(p);
+ if (archive_string_ensure(&as,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ archive_string_free(&as);
+ return(ARCHIVE_FATAL);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: This might break the pathname
+ * if the current code page is CP932 and
+ * the pathname includes a character '\'
+ * as a part of its multibyte pathname. */
+ if (p[strlen(p) -1] == '\\')
+ path_length--;
+ else
+#endif
+ archive_strncpy(&as, p, path_length);
+ archive_strappend_char(&as, '/');
+ archive_entry_copy_pathname(entry, as.s);
+ archive_string_free(&as);
+ }
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+ r = archive_entry_pathname_l(entry, &(gnutar->pathname),
+ &(gnutar->pathname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathame");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ r = archive_entry_uname_l(entry, &(gnutar->uname),
+ &(gnutar->uname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate uname '%s' to %s",
+ archive_entry_uname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ r = archive_entry_gname_l(entry, &(gnutar->gname),
+ &(gnutar->gname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate gname '%s' to %s",
+ archive_entry_gname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+
+ /* If linkname is longer than 100 chars we need to add a 'K' header. */
+ r = archive_entry_hardlink_l(entry, &(gnutar->linkname),
+ &(gnutar->linkname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_hardlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ if (gnutar->linkname_length == 0) {
+ r = archive_entry_symlink_l(entry, &(gnutar->linkname),
+ &(gnutar->linkname_length), sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ ret = ARCHIVE_FATAL;
+ goto exit_write_header;
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ archive_entry_hardlink(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ }
+ if (gnutar->linkname_length > GNUTAR_linkname_size) {
+ size_t length = gnutar->linkname_length + 1;
+ struct archive_entry *temp = archive_entry_new2(&a->archive);
+
+ /* Uname/gname here don't really matter since no one reads them;
+ * these are the values that GNU tar happens to use on FreeBSD. */
+ archive_entry_set_uname(temp, "root");
+ archive_entry_set_gname(temp, "wheel");
+
+ archive_entry_set_pathname(temp, "././@LongLink");
+ archive_entry_set_size(temp, length);
+ ret = archive_format_gnutar_header(a, buff, temp, 'K');
+ archive_entry_free(temp);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ ret = __archive_write_output(a, buff, 512);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ /* Write name and trailing null byte. */
+ ret = __archive_write_output(a, gnutar->linkname, length);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ /* Pad to 512 bytes */
+ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ }
+
+ /* If pathname is longer than 100 chars we need to add an 'L' header. */
+ if (gnutar->pathname_length > GNUTAR_name_size) {
+ const char *pathname = gnutar->pathname;
+ size_t length = gnutar->pathname_length + 1;
+ struct archive_entry *temp = archive_entry_new2(&a->archive);
+
+ /* Uname/gname here don't really matter since no one reads them;
+ * these are the values that GNU tar happens to use on FreeBSD. */
+ archive_entry_set_uname(temp, "root");
+ archive_entry_set_gname(temp, "wheel");
+
+ archive_entry_set_pathname(temp, "././@LongLink");
+ archive_entry_set_size(temp, length);
+ ret = archive_format_gnutar_header(a, buff, temp, 'L');
+ archive_entry_free(temp);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ ret = __archive_write_output(a, buff, 512);
+ if(ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ /* Write pathname + trailing null byte. */
+ ret = __archive_write_output(a, pathname, length);
+ if(ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ /* Pad to multiple of 512 bytes. */
+ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ }
+
+ if (archive_entry_hardlink(entry) != NULL) {
+ tartype = '1';
+ } else
+ switch (archive_entry_filetype(entry)) {
+ case AE_IFREG: tartype = '0' ; break;
+ case AE_IFLNK: tartype = '2' ; break;
+ case AE_IFCHR: tartype = '3' ; break;
+ case AE_IFBLK: tartype = '4' ; break;
+ case AE_IFDIR: tartype = '5' ; break;
+ case AE_IFIFO: tartype = '6' ; break;
+ default: /* AE_IFSOCK and unknown */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "gnutar");
+ ret = ARCHIVE_FAILED;
+ goto exit_write_header;
+ }
+
+ ret = archive_format_gnutar_header(a, buff, entry, tartype);
+ if (ret < ARCHIVE_WARN)
+ goto exit_write_header;
+ if (ret2 < ret)
+ ret = ret2;
+ ret2 = __archive_write_output(a, buff, 512);
+ if (ret2 < ARCHIVE_WARN) {
+ ret = ret2;
+ goto exit_write_header;
+ }
+ if (ret2 < ret)
+ ret = ret2;
+
+ gnutar->entry_bytes_remaining = archive_entry_size(entry);
+ gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining);
+exit_write_header:
+ archive_entry_free(entry_main);
+ return (ret);
+}
+
+static int
+archive_format_gnutar_header(struct archive_write *a, char h[512],
+ struct archive_entry *entry, int tartype)
+{
+ unsigned int checksum;
+ int i, ret;
+ size_t copy_length;
+ const char *p;
+ struct gnutar *gnutar;
+
+ gnutar = (struct gnutar *)a->format_data;
+
+ ret = 0;
+
+ /*
+ * The "template header" already includes the signature,
+ * various end-of-field markers, and other required elements.
+ */
+ memcpy(h, &template_header, 512);
+
+ /*
+ * Because the block is already null-filled, and strings
+ * are allowed to exactly fill their destination (without null),
+ * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+ */
+
+ if (tartype == 'K' || tartype == 'L') {
+ p = archive_entry_pathname(entry);
+ copy_length = strlen(p);
+ } else {
+ p = gnutar->pathname;
+ copy_length = gnutar->pathname_length;
+ }
+ if (copy_length > GNUTAR_name_size)
+ copy_length = GNUTAR_name_size;
+ memcpy(h + GNUTAR_name_offset, p, copy_length);
+
+ if ((copy_length = gnutar->linkname_length) > 0) {
+ if (copy_length > GNUTAR_linkname_size)
+ copy_length = GNUTAR_linkname_size;
+ memcpy(h + GNUTAR_linkname_offset, gnutar->linkname,
+ copy_length);
+ }
+
+ /* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */
+ if (tartype == 'K' || tartype == 'L') {
+ p = archive_entry_uname(entry);
+ copy_length = strlen(p);
+ } else {
+ p = gnutar->uname;
+ copy_length = gnutar->uname_length;
+ }
+ if (copy_length > 0) {
+ if (copy_length > GNUTAR_uname_size)
+ copy_length = GNUTAR_uname_size;
+ memcpy(h + GNUTAR_uname_offset, p, copy_length);
+ }
+
+ /* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */
+ if (tartype == 'K' || tartype == 'L') {
+ p = archive_entry_gname(entry);
+ copy_length = strlen(p);
+ } else {
+ p = gnutar->gname;
+ copy_length = gnutar->gname_length;
+ }
+ if (copy_length > 0) {
+ if (strlen(p) > GNUTAR_gname_size)
+ copy_length = GNUTAR_gname_size;
+ memcpy(h + GNUTAR_gname_offset, p, copy_length);
+ }
+
+ /* By truncating the mode here, we ensure it always fits. */
+ format_octal(archive_entry_mode(entry) & 07777,
+ h + GNUTAR_mode_offset, GNUTAR_mode_size);
+
+ /* GNU tar supports base-256 here, so should never overflow. */
+ if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset,
+ GNUTAR_uid_size, GNUTAR_uid_max_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric user ID %jd too large",
+ (intmax_t)archive_entry_uid(entry));
+ ret = ARCHIVE_FAILED;
+ }
+
+ /* GNU tar supports base-256 here, so should never overflow. */
+ if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset,
+ GNUTAR_gid_size, GNUTAR_gid_max_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric group ID %jd too large",
+ (intmax_t)archive_entry_gid(entry));
+ ret = ARCHIVE_FAILED;
+ }
+
+ /* GNU tar supports base-256 here, so should never overflow. */
+ if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset,
+ GNUTAR_size_size, GNUTAR_size_max_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File size out of range");
+ ret = ARCHIVE_FAILED;
+ }
+
+ /* Shouldn't overflow before 2106, since mtime field is 33 bits. */
+ format_octal(archive_entry_mtime(entry),
+ h + GNUTAR_mtime_offset, GNUTAR_mtime_size);
+
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR) {
+ if (format_octal(archive_entry_rdevmajor(entry),
+ h + GNUTAR_rdevmajor_offset,
+ GNUTAR_rdevmajor_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Major device number too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_octal(archive_entry_rdevminor(entry),
+ h + GNUTAR_rdevminor_offset,
+ GNUTAR_rdevminor_size)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Minor device number too large");
+ ret = ARCHIVE_FAILED;
+ }
+ }
+
+ h[GNUTAR_typeflag_offset] = tartype;
+
+ checksum = 0;
+ for (i = 0; i < 512; i++)
+ checksum += 255 & (unsigned int)h[i];
+ h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
+ /* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
+ format_octal(checksum, h + GNUTAR_checksum_offset, 6);
+ return (ret);
+}
+
+/*
+ * Format a number into a field, falling back to base-256 if necessary.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize)
+{
+ int64_t limit = ((int64_t)1 << (s*3));
+
+ if (v < limit)
+ return (format_octal(v, p, s));
+ return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+ p += s;
+ while (s-- > 0) {
+ *--p = (char)(v & 0xff);
+ v >>= 8;
+ }
+ *p |= 0x80; /* Set the base-256 marker bit. */
+ return (0);
+}
+
+/*
+ * Format a number into the specified field using octal.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+ int len = s;
+
+ /* Octal values can't be negative, so use 0. */
+ if (v < 0)
+ v = 0;
+
+ p += s; /* Start at the end and work backwards. */
+ while (s-- > 0) {
+ *--p = (char)('0' + (v & 7));
+ v >>= 3;
+ }
+
+ if (v == 0)
+ return (0);
+
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '7';
+
+ return (-1);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_iso9660.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_iso9660.c
new file mode 100644
index 0000000000..2a3ae07fa2
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_iso9660.c
@@ -0,0 +1,8161 @@
+/*-
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_write_private.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define getuid() 0
+#define getgid() 0
+#endif
+
+/*#define DEBUG 1*/
+#ifdef DEBUG
+/* To compare to the ISO image file made by mkisofs. */
+#define COMPAT_MKISOFS 1
+#endif
+
+#define LOGICAL_BLOCK_BITS 11
+#define LOGICAL_BLOCK_SIZE 2048
+#define PATH_TABLE_BLOCK_SIZE 4096
+
+#define SYSTEM_AREA_BLOCK 16
+#define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 1
+#define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 1
+#define BOOT_RECORD_DESCRIPTOR_BLOCK 1
+#define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK 1
+#define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK 1
+#define RRIP_ER_BLOCK 1
+#define PADDING_BLOCK 150
+
+#define FD_1_2M_SIZE (1024 * 1200)
+#define FD_1_44M_SIZE (1024 * 1440)
+#define FD_2_88M_SIZE (1024 * 2880)
+#define MULTI_EXTENT_SIZE (ARCHIVE_LITERAL_LL(1) << 32) /* 4Gi bytes. */
+#define MAX_DEPTH 8
+#define RR_CE_SIZE 28 /* SUSP "CE" extension size */
+
+#define FILE_FLAG_EXISTENCE 0x01
+#define FILE_FLAG_DIRECTORY 0x02
+#define FILE_FLAG_ASSOCIATED 0x04
+#define FILE_FLAG_RECORD 0x08
+#define FILE_FLAG_PROTECTION 0x10
+#define FILE_FLAG_MULTI_EXTENT 0x80
+
+static const char rrip_identifier[] =
+ "RRIP_1991A";
+static const char rrip_descriptor[] =
+ "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR "
+ "POSIX FILE SYSTEM SEMANTICS";
+static const char rrip_source[] =
+ "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. "
+ "SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR "
+ "CONTACT INFORMATION.";
+#define RRIP_ER_ID_SIZE (sizeof(rrip_identifier)-1)
+#define RRIP_ER_DSC_SIZE (sizeof(rrip_descriptor)-1)
+#define RRIP_ER_SRC_SIZE (sizeof(rrip_source)-1)
+#define RRIP_ER_SIZE (8 + RRIP_ER_ID_SIZE + \
+ RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE)
+
+static const unsigned char zisofs_magic[8] = {
+ 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
+};
+
+#define ZF_HEADER_SIZE 16 /* zisofs header size. */
+#define ZF_LOG2_BS 15 /* log2 block size; 32K bytes. */
+#define ZF_BLOCK_SIZE (1UL << ZF_LOG2_BS)
+
+/*
+ * Manage extra records.
+ */
+struct extr_rec {
+ int location;
+ int offset;
+ unsigned char buf[LOGICAL_BLOCK_SIZE];
+ struct extr_rec *next;
+};
+
+struct ctl_extr_rec {
+ int use_extr;
+ unsigned char *bp;
+ struct isoent *isoent;
+ unsigned char *ce_ptr;
+ int cur_len;
+ int dr_len;
+ int limit;
+ int extr_off;
+ int extr_loc;
+};
+#define DR_SAFETY RR_CE_SIZE
+#define DR_LIMIT (254 - DR_SAFETY)
+
+/*
+ * The relation of struct isofile and isoent and archive_entry.
+ *
+ * Primary volume tree --> struct isoent
+ * |
+ * v
+ * struct isofile --> archive_entry
+ * ^
+ * |
+ * Joliet volume tree --> struct isoent
+ *
+ * struct isoent has specific information for volume.
+ */
+
+struct isofile {
+ /* Used for managing struct isofile list. */
+ struct isofile *allnext;
+ struct isofile *datanext;
+ /* Used for managing a hardlinked struct isofile list. */
+ struct isofile *hlnext;
+ struct isofile *hardlink_target;
+
+ struct archive_entry *entry;
+
+ /*
+ * Used for making a directory tree.
+ */
+ struct archive_string parentdir;
+ struct archive_string basename;
+ struct archive_string basename_utf16;
+ struct archive_string symlink;
+ int dircnt; /* The number of elements of
+ * its parent directory */
+
+ /*
+ * Used for a Directory Record.
+ */
+ struct content {
+ int64_t offset_of_temp;
+ int64_t size;
+ int blocks;
+ uint32_t location;
+ /*
+ * One extent equals one content.
+ * If this entry has multi extent, `next' variable points
+ * next content data.
+ */
+ struct content *next; /* next content */
+ } content, *cur_content;
+ int write_content;
+
+ enum {
+ NO = 0,
+ BOOT_CATALOG,
+ BOOT_IMAGE
+ } boot;
+
+ /*
+ * Used for a zisofs.
+ */
+ struct {
+ unsigned char header_size;
+ unsigned char log2_bs;
+ uint32_t uncompressed_size;
+ } zisofs;
+};
+
+struct isoent {
+ /* Keep `rbnode' at the first member of struct isoent. */
+ struct archive_rb_node rbnode;
+
+ struct isofile *file;
+
+ struct isoent *parent;
+ /* A list of children.(use chnext) */
+ struct {
+ struct isoent *first;
+ struct isoent **last;
+ int cnt;
+ } children;
+ struct archive_rb_tree rbtree;
+
+ /* A list of sub directories.(use drnext) */
+ struct {
+ struct isoent *first;
+ struct isoent **last;
+ int cnt;
+ } subdirs;
+ /* A sorted list of sub directories. */
+ struct isoent **children_sorted;
+ /* Used for managing struct isoent list. */
+ struct isoent *chnext;
+ struct isoent *drnext;
+ struct isoent *ptnext;
+
+ /*
+ * Used for making a Directory Record.
+ */
+ int dir_number;
+ struct {
+ int vd;
+ int self;
+ int parent;
+ int normal;
+ } dr_len;
+ uint32_t dir_location;
+ int dir_block;
+
+ /*
+ * Identifier:
+ * on primary, ISO9660 file/directory name.
+ * on joliet, UCS2 file/directory name.
+ * ext_off : offset of identifier extension.
+ * ext_len : length of identifier extension.
+ * id_len : byte size of identifier.
+ * on primary, this is ext_off + ext_len + version length.
+ * on joliet, this is ext_off + ext_len.
+ * mb_len : length of multibyte-character of identifier.
+ * on primary, mb_len and id_len are always the same.
+ * on joliet, mb_len and id_len are different.
+ */
+ char *identifier;
+ int ext_off;
+ int ext_len;
+ int id_len;
+ int mb_len;
+
+ /*
+ * Used for making a Rockridge extension.
+ * This is a part of Directory Records.
+ */
+ struct isoent *rr_parent;
+ struct isoent *rr_child;
+
+ /* Extra Record.(which we call in this source file)
+ * A maximum size of the Directory Record is 254.
+ * so, if generated RRIP data of a file cannot into a Directory
+ * Record because of its size, that surplus data relocate this
+ * Extra Record.
+ */
+ struct {
+ struct extr_rec *first;
+ struct extr_rec **last;
+ struct extr_rec *current;
+ } extr_rec_list;
+
+ unsigned int virtual:1;
+ /* If set to one, this file type is a directory.
+ * A convenience flag to be used as
+ * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR".
+ */
+ unsigned int dir:1;
+};
+
+struct hardlink {
+ struct archive_rb_node rbnode;
+ int nlink;
+ struct {
+ struct isofile *first;
+ struct isofile **last;
+ } file_list;
+};
+
+/*
+ * ISO writer options
+ */
+struct iso_option {
+ /*
+ * Usage : abstract-file=<value>
+ * Type : string, max 37 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -abstract <value>
+ *
+ * Specifies Abstract Filename.
+ * This file shall be described in the Root Directory
+ * and containing a abstract statement.
+ */
+ unsigned int abstract_file:1;
+#define OPT_ABSTRACT_FILE_DEFAULT 0 /* Not specified */
+#define ABSTRACT_FILE_SIZE 37
+
+ /*
+ * Usage : application-id=<value>
+ * Type : string, max 128 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -A/-appid <value>.
+ *
+ * Specifies Application Identifier.
+ * If the first byte is set to '_'(5F), the remaining
+ * bytes of this option shall specify an identifier
+ * for a file containing the identification of the
+ * application.
+ * This file shall be described in the Root Directory.
+ */
+ unsigned int application_id:1;
+#define OPT_APPLICATION_ID_DEFAULT 0 /* Use default identifier */
+#define APPLICATION_IDENTIFIER_SIZE 128
+
+ /*
+ * Usage : !allow-vernum
+ * Type : boolean
+ * Default: Enabled
+ * : Violates the ISO9660 standard if disable.
+ * COMPAT: mkisofs -N
+ *
+ * Allow filenames to use version numbers.
+ */
+ unsigned int allow_vernum:1;
+#define OPT_ALLOW_VERNUM_DEFAULT 1 /* Enabled */
+
+ /*
+ * Usage : biblio-file=<value>
+ * Type : string, max 37 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -biblio <value>
+ *
+ * Specifies Bibliographic Filename.
+ * This file shall be described in the Root Directory
+ * and containing bibliographic records.
+ */
+ unsigned int biblio_file:1;
+#define OPT_BIBLIO_FILE_DEFAULT 0 /* Not specified */
+#define BIBLIO_FILE_SIZE 37
+
+ /*
+ * Usage : boot=<value>
+ * Type : string
+ * Default: Not specified
+ * COMPAT : mkisofs -b/-eltorito-boot <value>
+ *
+ * Specifies "El Torito" boot image file to make
+ * a bootable CD.
+ */
+ unsigned int boot:1;
+#define OPT_BOOT_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : boot-catalog=<value>
+ * Type : string
+ * Default: "boot.catalog"
+ * COMPAT : mkisofs -c/-eltorito-catalog <value>
+ *
+ * Specifies a fullpath of El Torito boot catalog.
+ */
+ unsigned int boot_catalog:1;
+#define OPT_BOOT_CATALOG_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : boot-info-table
+ * Type : boolean
+ * Default: Disabled
+ * COMPAT : mkisofs -boot-info-table
+ *
+ * Modify the boot image file specified by `boot'
+ * option; ISO writer stores boot file information
+ * into the boot file in ISO image at offset 8
+ * through offset 64.
+ */
+ unsigned int boot_info_table:1;
+#define OPT_BOOT_INFO_TABLE_DEFAULT 0 /* Disabled */
+
+ /*
+ * Usage : boot-load-seg=<value>
+ * Type : hexadecimal
+ * Default: Not specified
+ * COMPAT : mkisofs -boot-load-seg <value>
+ *
+ * Specifies a load segment for boot image.
+ * This is used with no-emulation mode.
+ */
+ unsigned int boot_load_seg:1;
+#define OPT_BOOT_LOAD_SEG_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : boot-load-size=<value>
+ * Type : decimal
+ * Default: Not specified
+ * COMPAT : mkisofs -boot-load-size <value>
+ *
+ * Specifies a sector count for boot image.
+ * This is used with no-emulation mode.
+ */
+ unsigned int boot_load_size:1;
+#define OPT_BOOT_LOAD_SIZE_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : boot-type=<boot-media-type>
+ * : 'no-emulation' : 'no emulation' image
+ * : 'fd' : floppy disk image
+ * : 'hard-disk' : hard disk image
+ * Type : string
+ * Default: Auto detect
+ * : We check a size of boot image;
+ * : If the size is just 1.22M/1.44M/2.88M,
+ * : we assume boot_type is 'fd';
+ * : otherwise boot_type is 'no-emulation'.
+ * COMPAT :
+ * boot=no-emulation
+ * mkisofs -no-emul-boot
+ * boot=fd
+ * This is a default on the mkisofs.
+ * boot=hard-disk
+ * mkisofs -hard-disk-boot
+ *
+ * Specifies a type of "El Torito" boot image.
+ */
+ unsigned int boot_type:2;
+#define OPT_BOOT_TYPE_AUTO 0 /* auto detect */
+#define OPT_BOOT_TYPE_NO_EMU 1 /* ``no emulation'' image */
+#define OPT_BOOT_TYPE_FD 2 /* floppy disk image */
+#define OPT_BOOT_TYPE_HARD_DISK 3 /* hard disk image */
+#define OPT_BOOT_TYPE_DEFAULT OPT_BOOT_TYPE_AUTO
+
+ /*
+ * Usage : compression-level=<value>
+ * Type : decimal
+ * Default: Not specified
+ * COMPAT : NONE
+ *
+ * Specifies compression level for option zisofs=direct.
+ */
+ unsigned int compression_level:1;
+#define OPT_COMPRESSION_LEVEL_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : copyright-file=<value>
+ * Type : string, max 37 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -copyright <value>
+ *
+ * Specifies Copyright Filename.
+ * This file shall be described in the Root Directory
+ * and containing a copyright statement.
+ */
+ unsigned int copyright_file:1;
+#define OPT_COPYRIGHT_FILE_DEFAULT 0 /* Not specified */
+#define COPYRIGHT_FILE_SIZE 37
+
+ /*
+ * Usage : gid=<value>
+ * Type : decimal
+ * Default: Not specified
+ * COMPAT : mkisofs -gid <value>
+ *
+ * Specifies a group id to rewrite the group id of all files.
+ */
+ unsigned int gid:1;
+#define OPT_GID_DEFAULT 0 /* Not specified */
+
+ /*
+ * Usage : iso-level=[1234]
+ * Type : decimal
+ * Default: 1
+ * COMPAT : mkisofs -iso-level <value>
+ *
+ * Specifies ISO9600 Level.
+ * Level 1: [DEFAULT]
+ * - limits each file size less than 4Gi bytes;
+ * - a File Name shall not contain more than eight
+ * d-characters or eight d1-characters;
+ * - a File Name Extension shall not contain more than
+ * three d-characters or three d1-characters;
+ * - a Directory Identifier shall not contain more
+ * than eight d-characters or eight d1-characters.
+ * Level 2:
+ * - limits each file size less than 4Giga bytes;
+ * - a File Name shall not contain more than thirty
+ * d-characters or thirty d1-characters;
+ * - a File Name Extension shall not contain more than
+ * thirty d-characters or thirty d1-characters;
+ * - a Directory Identifier shall not contain more
+ * than thirty-one d-characters or thirty-one
+ * d1-characters.
+ * Level 3:
+ * - no limit of file size; use multi extent.
+ * Level 4:
+ * - this level 4 simulates mkisofs option
+ * '-iso-level 4';
+ * - crate a enhanced volume as mkisofs doing;
+ * - allow a File Name to have leading dot;
+ * - allow a File Name to have all ASCII letters;
+ * - allow a File Name to have multiple dots;
+ * - allow more then 8 depths of directory trees;
+ * - disable a version number to a File Name;
+ * - disable a forced period to the tail of a File Name;
+ * - the maximum length of files and directories is raised to 193.
+ * if rockridge option is disabled, raised to 207.
+ */
+ unsigned int iso_level:3;
+#define OPT_ISO_LEVEL_DEFAULT 1 /* ISO Level 1 */
+
+ /*
+ * Usage : joliet[=long]
+ * : !joliet
+ * : Do not generate Joliet Volume and Records.
+ * : joliet [DEFAULT]
+ * : Generates Joliet Volume and Directory Records.
+ * : [COMPAT: mkisofs -J/-joliet]
+ * : joliet=long
+ * : The joliet filenames are up to 103 Unicode
+ * : characters.
+ * : This option breaks the Joliet specification.
+ * : [COMPAT: mkisofs -J -joliet-long]
+ * Type : boolean/string
+ * Default: Enabled
+ * COMPAT : mkisofs -J / -joliet-long
+ *
+ * Generates Joliet Volume and Directory Records.
+ */
+ unsigned int joliet:2;
+#define OPT_JOLIET_DISABLE 0 /* Not generate Joliet Records. */
+#define OPT_JOLIET_ENABLE 1 /* Generate Joliet Records. */
+#define OPT_JOLIET_LONGNAME 2 /* Use long joliet filenames.*/
+#define OPT_JOLIET_DEFAULT OPT_JOLIET_ENABLE
+
+ /*
+ * Usage : !limit-depth
+ * Type : boolean
+ * Default: Enabled
+ * : Violates the ISO9660 standard if disable.
+ * COMPAT : mkisofs -D/-disable-deep-relocation
+ *
+ * The number of levels in hierarchy cannot exceed eight.
+ */
+ unsigned int limit_depth:1;
+#define OPT_LIMIT_DEPTH_DEFAULT 1 /* Enabled */
+
+ /*
+ * Usage : !limit-dirs
+ * Type : boolean
+ * Default: Enabled
+ * : Violates the ISO9660 standard if disable.
+ * COMPAT : mkisofs -no-limit-pathtables
+ *
+ * Limits the number of directories less than 65536 due
+ * to the size of the Parent Directory Number of Path
+ * Table.
+ */
+ unsigned int limit_dirs:1;
+#define OPT_LIMIT_DIRS_DEFAULT 1 /* Enabled */
+
+ /*
+ * Usage : !pad
+ * Type : boolean
+ * Default: Enabled
+ * COMPAT : -pad/-no-pad
+ *
+ * Pads the end of the ISO image by null of 300Ki bytes.
+ */
+ unsigned int pad:1;
+#define OPT_PAD_DEFAULT 1 /* Enabled */
+
+ /*
+ * Usage : publisher=<value>
+ * Type : string, max 128 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -publisher <value>
+ *
+ * Specifies Publisher Identifier.
+ * If the first byte is set to '_'(5F), the remaining
+ * bytes of this option shall specify an identifier
+ * for a file containing the identification of the user.
+ * This file shall be described in the Root Directory.
+ */
+ unsigned int publisher:1;
+#define OPT_PUBLISHER_DEFAULT 0 /* Not specified */
+#define PUBLISHER_IDENTIFIER_SIZE 128
+
+ /*
+ * Usage : rockridge
+ * : !rockridge
+ * : disable to generate SUSP and RR records.
+ * : rockridge
+ * : the same as 'rockridge=useful'.
+ * : rockridge=strict
+ * : generate SUSP and RR records.
+ * : [COMPAT: mkisofs -R]
+ * : rockridge=useful [DEFAULT]
+ * : generate SUSP and RR records.
+ * : [COMPAT: mkisofs -r]
+ * : NOTE Our rockridge=useful option does not set a zero
+ * : to uid and gid, you should use application
+ * : option such as --gid,--gname,--uid and --uname
+ * : bsdtar options instead.
+ * Type : boolean/string
+ * Default: Enabled as rockridge=useful
+ * COMPAT : mkisofs -r / -R
+ *
+ * Generates SUSP and RR records.
+ */
+ unsigned int rr:2;
+#define OPT_RR_DISABLED 0
+#define OPT_RR_STRICT 1
+#define OPT_RR_USEFUL 2
+#define OPT_RR_DEFAULT OPT_RR_USEFUL
+
+ /*
+ * Usage : volume-id=<value>
+ * Type : string, max 32 bytes
+ * Default: Not specified
+ * COMPAT : mkisofs -V <value>
+ *
+ * Specifies Volume Identifier.
+ */
+ unsigned int volume_id:1;
+#define OPT_VOLUME_ID_DEFAULT 0 /* Use default identifier */
+#define VOLUME_IDENTIFIER_SIZE 32
+
+ /*
+ * Usage : !zisofs [DEFAULT]
+ * : Disable to generate RRIP 'ZF' extension.
+ * : zisofs
+ * : Make files zisofs file and generate RRIP 'ZF'
+ * : extension. So you do not need mkzftree utility
+ * : for making zisofs.
+ * : When the file size is less than one Logical Block
+ * : size, that file will not zisofs'ed since it does
+ * : reduce an ISO-image size.
+ * :
+ * : When you specify option 'boot=<boot-image>', that
+ * : 'boot-image' file won't be converted to zisofs file.
+ * Type : boolean
+ * Default: Disabled
+ *
+ * Generates RRIP 'ZF' System Use Entry.
+ */
+ unsigned int zisofs:1;
+#define OPT_ZISOFS_DISABLED 0
+#define OPT_ZISOFS_DIRECT 1
+#define OPT_ZISOFS_DEFAULT OPT_ZISOFS_DISABLED
+
+};
+
+struct iso9660 {
+ /* The creation time of ISO image. */
+ time_t birth_time;
+ /* A file stream of a temporary file, which file contents
+ * save to until ISO image can be created. */
+ int temp_fd;
+
+ struct isofile *cur_file;
+ struct isoent *cur_dirent;
+ struct archive_string cur_dirstr;
+ uint64_t bytes_remaining;
+ int need_multi_extent;
+
+ /* Temporary string buffer for Joliet extension. */
+ struct archive_string utf16be;
+ struct archive_string mbs;
+
+ struct archive_string_conv *sconv_to_utf16be;
+ struct archive_string_conv *sconv_from_utf16be;
+
+ /* A list of all of struct isofile entries. */
+ struct {
+ struct isofile *first;
+ struct isofile **last;
+ } all_file_list;
+
+ /* A list of struct isofile entries which have its
+ * contents and are not a directory, a hardlinked file
+ * and a symlink file. */
+ struct {
+ struct isofile *first;
+ struct isofile **last;
+ } data_file_list;
+
+ /* Used for managing to find hardlinking files. */
+ struct archive_rb_tree hardlink_rbtree;
+
+ /* Used for making the Path Table Record. */
+ struct vdd {
+ /* the root of entry tree. */
+ struct isoent *rootent;
+ enum vdd_type {
+ VDD_PRIMARY,
+ VDD_JOLIET,
+ VDD_ENHANCED
+ } vdd_type;
+
+ struct path_table {
+ struct isoent *first;
+ struct isoent **last;
+ struct isoent **sorted;
+ int cnt;
+ } *pathtbl;
+ int max_depth;
+
+ int path_table_block;
+ int path_table_size;
+ int location_type_L_path_table;
+ int location_type_M_path_table;
+ int total_dir_block;
+ } primary, joliet;
+
+ /* Used for making a Volume Descriptor. */
+ int volume_space_size;
+ int volume_sequence_number;
+ int total_file_block;
+ struct archive_string volume_identifier;
+ struct archive_string publisher_identifier;
+ struct archive_string data_preparer_identifier;
+ struct archive_string application_identifier;
+ struct archive_string copyright_file_identifier;
+ struct archive_string abstract_file_identifier;
+ struct archive_string bibliographic_file_identifier;
+
+ /* Used for making rockridge extensions. */
+ int location_rrip_er;
+
+ /* Used for making zisofs. */
+ struct {
+ unsigned int detect_magic:1;
+ unsigned int making:1;
+ unsigned int allzero:1;
+ unsigned char magic_buffer[64];
+ int magic_cnt;
+
+#ifdef HAVE_ZLIB_H
+ /*
+ * Copy a compressed file to iso9660.zisofs.temp_fd
+ * and also copy a uncompressed file(original file) to
+ * iso9660.temp_fd . If the number of logical block
+ * of the compressed file is less than the number of
+ * logical block of the uncompressed file, use it and
+ * remove the copy of the uncompressed file.
+ * but if not, we use uncompressed file and remove
+ * the copy of the compressed file.
+ */
+ uint32_t *block_pointers;
+ size_t block_pointers_allocated;
+ int block_pointers_cnt;
+ int block_pointers_idx;
+ int64_t total_size;
+ int64_t block_offset;
+
+ z_stream stream;
+ int stream_valid;
+ int64_t remaining;
+ int compression_level;
+#endif
+ } zisofs;
+
+ struct isoent *directories_too_deep;
+ int dircnt_max;
+
+ /* Write buffer. */
+#define wb_buffmax() (LOGICAL_BLOCK_SIZE * 32)
+#define wb_remaining(a) (((struct iso9660 *)(a)->format_data)->wbuff_remaining)
+#define wb_offset(a) (((struct iso9660 *)(a)->format_data)->wbuff_offset \
+ + wb_buffmax() - wb_remaining(a))
+ unsigned char wbuff[LOGICAL_BLOCK_SIZE * 32];
+ size_t wbuff_remaining;
+ enum {
+ WB_TO_STREAM,
+ WB_TO_TEMP
+ } wbuff_type;
+ int64_t wbuff_offset;
+ int64_t wbuff_written;
+ int64_t wbuff_tail;
+
+ /* 'El Torito' boot data. */
+ struct {
+ /* boot catalog file */
+ struct archive_string catalog_filename;
+ struct isoent *catalog;
+ /* boot image file */
+ struct archive_string boot_filename;
+ struct isoent *boot;
+
+ unsigned char platform_id;
+#define BOOT_PLATFORM_X86 0
+#define BOOT_PLATFORM_PPC 1
+#define BOOT_PLATFORM_MAC 2
+ struct archive_string id;
+ unsigned char media_type;
+#define BOOT_MEDIA_NO_EMULATION 0
+#define BOOT_MEDIA_1_2M_DISKETTE 1
+#define BOOT_MEDIA_1_44M_DISKETTE 2
+#define BOOT_MEDIA_2_88M_DISKETTE 3
+#define BOOT_MEDIA_HARD_DISK 4
+ unsigned char system_type;
+ uint16_t boot_load_seg;
+ uint16_t boot_load_size;
+#define BOOT_LOAD_SIZE 4
+ } el_torito;
+
+ struct iso_option opt;
+};
+
+/*
+ * Types of Volume Descriptor
+ */
+enum VD_type {
+ VDT_BOOT_RECORD=0, /* Boot Record Volume Descriptor */
+ VDT_PRIMARY=1, /* Primary Volume Descriptor */
+ VDT_SUPPLEMENTARY=2, /* Supplementary Volume Descriptor */
+ VDT_TERMINATOR=255 /* Volume Descriptor Set Terminator */
+};
+
+/*
+ * Types of Directory Record
+ */
+enum dir_rec_type {
+ DIR_REC_VD, /* Stored in Volume Descriptor. */
+ DIR_REC_SELF, /* Stored as Current Directory. */
+ DIR_REC_PARENT, /* Stored as Parent Directory. */
+ DIR_REC_NORMAL /* Stored as Child. */
+};
+
+/*
+ * Kinds of Volume Descriptor Character
+ */
+enum vdc {
+ VDC_STD,
+ VDC_LOWERCASE,
+ VDC_UCS2,
+ VDC_UCS2_DIRECT
+};
+
+/*
+ * IDentifier Resolver.
+ * Used for resolving duplicated filenames.
+ */
+struct idr {
+ struct idrent {
+ struct archive_rb_node rbnode;
+ /* Used in wait_list. */
+ struct idrent *wnext;
+ struct idrent *avail;
+
+ struct isoent *isoent;
+ int weight;
+ int noff;
+ int rename_num;
+ } *idrent_pool;
+
+ struct archive_rb_tree rbtree;
+
+ struct {
+ struct idrent *first;
+ struct idrent **last;
+ } wait_list;
+
+ int pool_size;
+ int pool_idx;
+ int num_size;
+ int null_size;
+
+ char char_map[0x80];
+};
+
+enum char_type {
+ A_CHAR,
+ D_CHAR
+};
+
+
+static int iso9660_options(struct archive_write *,
+ const char *, const char *);
+static int iso9660_write_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t iso9660_write_data(struct archive_write *,
+ const void *, size_t);
+static int iso9660_finish_entry(struct archive_write *);
+static int iso9660_close(struct archive_write *);
+static int iso9660_free(struct archive_write *);
+
+static void get_system_identitier(char *, size_t);
+static void set_str(unsigned char *, const char *, size_t, char,
+ const char *);
+static inline int joliet_allowed_char(unsigned char, unsigned char);
+static int set_str_utf16be(struct archive_write *, unsigned char *,
+ const char *, size_t, uint16_t, enum vdc);
+static int set_str_a_characters_bp(struct archive_write *,
+ unsigned char *, int, int, const char *, enum vdc);
+static int set_str_d_characters_bp(struct archive_write *,
+ unsigned char *, int, int, const char *, enum vdc);
+static void set_VD_bp(unsigned char *, enum VD_type, unsigned char);
+static inline void set_unused_field_bp(unsigned char *, int, int);
+
+static unsigned char *extra_open_record(unsigned char *, int,
+ struct isoent *, struct ctl_extr_rec *);
+static void extra_close_record(struct ctl_extr_rec *, int);
+static unsigned char * extra_next_record(struct ctl_extr_rec *, int);
+static unsigned char *extra_get_record(struct isoent *, int *, int *, int *);
+static void extra_tell_used_size(struct ctl_extr_rec *, int);
+static int extra_setup_location(struct isoent *, int);
+static int set_directory_record_rr(unsigned char *, int,
+ struct isoent *, struct iso9660 *, enum dir_rec_type);
+static int set_directory_record(unsigned char *, size_t,
+ struct isoent *, struct iso9660 *, enum dir_rec_type,
+ enum vdd_type);
+static inline int get_dir_rec_size(struct iso9660 *, struct isoent *,
+ enum dir_rec_type, enum vdd_type);
+static inline unsigned char *wb_buffptr(struct archive_write *);
+static int wb_write_out(struct archive_write *);
+static int wb_consume(struct archive_write *, size_t);
+#ifdef HAVE_ZLIB_H
+static int wb_set_offset(struct archive_write *, int64_t);
+#endif
+static int write_null(struct archive_write *, size_t);
+static int write_VD_terminator(struct archive_write *);
+static int set_file_identifier(unsigned char *, int, int, enum vdc,
+ struct archive_write *, struct vdd *,
+ struct archive_string *, const char *, int,
+ enum char_type);
+static int write_VD(struct archive_write *, struct vdd *);
+static int write_VD_boot_record(struct archive_write *);
+static int write_information_block(struct archive_write *);
+static int write_path_table(struct archive_write *, int,
+ struct vdd *);
+static int write_directory_descriptors(struct archive_write *,
+ struct vdd *);
+static int write_file_descriptors(struct archive_write *);
+static int write_rr_ER(struct archive_write *);
+static void calculate_path_table_size(struct vdd *);
+
+static void isofile_init_entry_list(struct iso9660 *);
+static void isofile_add_entry(struct iso9660 *, struct isofile *);
+static void isofile_free_all_entries(struct iso9660 *);
+static void isofile_init_entry_data_file_list(struct iso9660 *);
+static void isofile_add_data_file(struct iso9660 *, struct isofile *);
+static struct isofile * isofile_new(struct archive_write *,
+ struct archive_entry *);
+static void isofile_free(struct isofile *);
+static int isofile_gen_utility_names(struct archive_write *,
+ struct isofile *);
+static int isofile_register_hardlink(struct archive_write *,
+ struct isofile *);
+static void isofile_connect_hardlink_files(struct iso9660 *);
+static void isofile_init_hardlinks(struct iso9660 *);
+static void isofile_free_hardlinks(struct iso9660 *);
+
+static struct isoent *isoent_new(struct isofile *);
+static int isoent_clone_tree(struct archive_write *,
+ struct isoent **, struct isoent *);
+static void _isoent_free(struct isoent *isoent);
+static void isoent_free_all(struct isoent *);
+static struct isoent * isoent_create_virtual_dir(struct archive_write *,
+ struct iso9660 *, const char *);
+static int isoent_cmp_node(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int isoent_cmp_key(const struct archive_rb_node *,
+ const void *);
+static int isoent_add_child_head(struct isoent *, struct isoent *);
+static int isoent_add_child_tail(struct isoent *, struct isoent *);
+static void isoent_remove_child(struct isoent *, struct isoent *);
+static void isoent_setup_directory_location(struct iso9660 *,
+ int, struct vdd *);
+static void isoent_setup_file_location(struct iso9660 *, int);
+static int get_path_component(char *, size_t, const char *);
+static int isoent_tree(struct archive_write *, struct isoent **);
+static struct isoent *isoent_find_child(struct isoent *, const char *);
+static struct isoent *isoent_find_entry(struct isoent *, const char *);
+static void idr_relaxed_filenames(char *);
+static void idr_init(struct iso9660 *, struct vdd *, struct idr *);
+static void idr_cleanup(struct idr *);
+static int idr_ensure_poolsize(struct archive_write *, struct idr *,
+ int);
+static int idr_start(struct archive_write *, struct idr *,
+ int, int, int, int, const struct archive_rb_tree_ops *);
+static void idr_register(struct idr *, struct isoent *, int,
+ int);
+static void idr_extend_identifier(struct idrent *, int, int);
+static void idr_resolve(struct idr *, void (*)(unsigned char *, int));
+static void idr_set_num(unsigned char *, int);
+static void idr_set_num_beutf16(unsigned char *, int);
+static int isoent_gen_iso9660_identifier(struct archive_write *,
+ struct isoent *, struct idr *);
+static int isoent_gen_joliet_identifier(struct archive_write *,
+ struct isoent *, struct idr *);
+static int isoent_cmp_iso9660_identifier(const struct isoent *,
+ const struct isoent *);
+static int isoent_cmp_node_iso9660(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int isoent_cmp_key_iso9660(const struct archive_rb_node *,
+ const void *);
+static int isoent_cmp_joliet_identifier(const struct isoent *,
+ const struct isoent *);
+static int isoent_cmp_node_joliet(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int isoent_cmp_key_joliet(const struct archive_rb_node *,
+ const void *);
+static inline void path_table_add_entry(struct path_table *, struct isoent *);
+static inline struct isoent * path_table_last_entry(struct path_table *);
+static int isoent_make_path_table(struct archive_write *);
+static int isoent_find_out_boot_file(struct archive_write *,
+ struct isoent *);
+static int isoent_create_boot_catalog(struct archive_write *,
+ struct isoent *);
+static size_t fd_boot_image_size(int);
+static int make_boot_catalog(struct archive_write *);
+static int setup_boot_information(struct archive_write *);
+
+static int zisofs_init(struct archive_write *, struct isofile *);
+static void zisofs_detect_magic(struct archive_write *,
+ const void *, size_t);
+static int zisofs_write_to_temp(struct archive_write *,
+ const void *, size_t);
+static int zisofs_finish_entry(struct archive_write *);
+static int zisofs_rewind_boot_file(struct archive_write *);
+static int zisofs_free(struct archive_write *);
+
+int
+archive_write_set_format_iso9660(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct iso9660 *iso9660;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ iso9660 = calloc(1, sizeof(*iso9660));
+ if (iso9660 == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate iso9660 data");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->birth_time = 0;
+ iso9660->temp_fd = -1;
+ iso9660->cur_file = NULL;
+ iso9660->primary.max_depth = 0;
+ iso9660->primary.vdd_type = VDD_PRIMARY;
+ iso9660->primary.pathtbl = NULL;
+ iso9660->joliet.rootent = NULL;
+ iso9660->joliet.max_depth = 0;
+ iso9660->joliet.vdd_type = VDD_JOLIET;
+ iso9660->joliet.pathtbl = NULL;
+ isofile_init_entry_list(iso9660);
+ isofile_init_entry_data_file_list(iso9660);
+ isofile_init_hardlinks(iso9660);
+ iso9660->directories_too_deep = NULL;
+ iso9660->dircnt_max = 1;
+ iso9660->wbuff_remaining = wb_buffmax();
+ iso9660->wbuff_type = WB_TO_TEMP;
+ iso9660->wbuff_offset = 0;
+ iso9660->wbuff_written = 0;
+ iso9660->wbuff_tail = 0;
+ archive_string_init(&(iso9660->utf16be));
+ archive_string_init(&(iso9660->mbs));
+
+ /*
+ * Init Identifiers used for PVD and SVD.
+ */
+ archive_string_init(&(iso9660->volume_identifier));
+ archive_strcpy(&(iso9660->volume_identifier), "CDROM");
+ archive_string_init(&(iso9660->publisher_identifier));
+ archive_string_init(&(iso9660->data_preparer_identifier));
+ archive_string_init(&(iso9660->application_identifier));
+ archive_strcpy(&(iso9660->application_identifier),
+ archive_version_string());
+ archive_string_init(&(iso9660->copyright_file_identifier));
+ archive_string_init(&(iso9660->abstract_file_identifier));
+ archive_string_init(&(iso9660->bibliographic_file_identifier));
+
+ /*
+ * Init El Torito bootable CD variables.
+ */
+ archive_string_init(&(iso9660->el_torito.catalog_filename));
+ iso9660->el_torito.catalog = NULL;
+ /* Set default file name of boot catalog */
+ archive_strcpy(&(iso9660->el_torito.catalog_filename),
+ "boot.catalog");
+ archive_string_init(&(iso9660->el_torito.boot_filename));
+ iso9660->el_torito.boot = NULL;
+ iso9660->el_torito.platform_id = BOOT_PLATFORM_X86;
+ archive_string_init(&(iso9660->el_torito.id));
+ iso9660->el_torito.boot_load_seg = 0;
+ iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE;
+
+ /*
+ * Init zisofs variables.
+ */
+#ifdef HAVE_ZLIB_H
+ iso9660->zisofs.block_pointers = NULL;
+ iso9660->zisofs.block_pointers_allocated = 0;
+ iso9660->zisofs.stream_valid = 0;
+ iso9660->zisofs.compression_level = 9;
+ memset(&(iso9660->zisofs.stream), 0,
+ sizeof(iso9660->zisofs.stream));
+#endif
+
+ /*
+ * Set default value of iso9660 options.
+ */
+ iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT;
+ iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT;
+ iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT;
+ iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT;
+ iso9660->opt.boot = OPT_BOOT_DEFAULT;
+ iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT;
+ iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT;
+ iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT;
+ iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT;
+ iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT;
+ iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT;
+ iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT;
+ iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT;
+ iso9660->opt.joliet = OPT_JOLIET_DEFAULT;
+ iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT;
+ iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT;
+ iso9660->opt.pad = OPT_PAD_DEFAULT;
+ iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT;
+ iso9660->opt.rr = OPT_RR_DEFAULT;
+ iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT;
+ iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT;
+
+ /* Create the root directory. */
+ iso9660->primary.rootent =
+ isoent_create_virtual_dir(a, iso9660, "");
+ if (iso9660->primary.rootent == NULL) {
+ free(iso9660);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->primary.rootent->parent = iso9660->primary.rootent;
+ iso9660->cur_dirent = iso9660->primary.rootent;
+ archive_string_init(&(iso9660->cur_dirstr));
+ archive_string_ensure(&(iso9660->cur_dirstr), 1);
+ iso9660->cur_dirstr.s[0] = 0;
+ iso9660->sconv_to_utf16be = NULL;
+ iso9660->sconv_from_utf16be = NULL;
+
+ a->format_data = iso9660;
+ a->format_name = "iso9660";
+ a->format_options = iso9660_options;
+ a->format_write_header = iso9660_write_header;
+ a->format_write_data = iso9660_write_data;
+ a->format_finish_entry = iso9660_finish_entry;
+ a->format_close = iso9660_close;
+ a->format_free = iso9660_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
+ a->archive.archive_format_name = "ISO9660";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+get_str_opt(struct archive_write *a, struct archive_string *s,
+ size_t maxsize, const char *key, const char *value)
+{
+
+ if (strlen(value) > maxsize) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Value is longer than %zu characters "
+ "for option ``%s''", maxsize, key);
+ return (ARCHIVE_FATAL);
+ }
+ archive_strcpy(s, value);
+ return (ARCHIVE_OK);
+}
+
+static int
+get_num_opt(struct archive_write *a, int *num, int high, int low,
+ const char *key, const char *value)
+{
+ const char *p = value;
+ int data = 0;
+ int neg = 0;
+
+ if (p == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value(empty) for option ``%s''", key);
+ return (ARCHIVE_FATAL);
+ }
+ if (*p == '-') {
+ neg = 1;
+ p++;
+ }
+ while (*p) {
+ if (*p >= '0' && *p <= '9')
+ data = data * 10 + *p - '0';
+ else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value for option ``%s''", key);
+ return (ARCHIVE_FATAL);
+ }
+ if (data > high) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value(over %d) for "
+ "option ``%s''", high, key);
+ return (ARCHIVE_FATAL);
+ }
+ if (data < low) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value(under %d) for "
+ "option ``%s''", low, key);
+ return (ARCHIVE_FATAL);
+ }
+ p++;
+ }
+ if (neg)
+ data *= -1;
+ *num = data;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+iso9660_options(struct archive_write *a, const char *key, const char *value)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ const char *p;
+ int r;
+
+ switch (key[0]) {
+ case 'a':
+ if (strcmp(key, "abstract-file") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->abstract_file_identifier),
+ ABSTRACT_FILE_SIZE, key, value);
+ iso9660->opt.abstract_file = r == ARCHIVE_OK;
+ return (r);
+ }
+ if (strcmp(key, "application-id") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->application_identifier),
+ APPLICATION_IDENTIFIER_SIZE, key, value);
+ iso9660->opt.application_id = r == ARCHIVE_OK;
+ return (r);
+ }
+ if (strcmp(key, "allow-vernum") == 0) {
+ iso9660->opt.allow_vernum = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'b':
+ if (strcmp(key, "biblio-file") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->bibliographic_file_identifier),
+ BIBLIO_FILE_SIZE, key, value);
+ iso9660->opt.biblio_file = r == ARCHIVE_OK;
+ return (r);
+ }
+ if (strcmp(key, "boot") == 0) {
+ if (value == NULL)
+ iso9660->opt.boot = 0;
+ else {
+ iso9660->opt.boot = 1;
+ archive_strcpy(
+ &(iso9660->el_torito.boot_filename),
+ value);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "boot-catalog") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->el_torito.catalog_filename),
+ 1024, key, value);
+ iso9660->opt.boot_catalog = r == ARCHIVE_OK;
+ return (r);
+ }
+ if (strcmp(key, "boot-info-table") == 0) {
+ iso9660->opt.boot_info_table = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "boot-load-seg") == 0) {
+ uint32_t seg;
+
+ iso9660->opt.boot_load_seg = 0;
+ if (value == NULL)
+ goto invalid_value;
+ seg = 0;
+ p = value;
+ if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
+ p += 2;
+ while (*p) {
+ if (seg)
+ seg <<= 4;
+ if (*p >= 'A' && *p <= 'F')
+ seg += *p - 'A' + 0x0a;
+ else if (*p >= 'a' && *p <= 'f')
+ seg += *p - 'a' + 0x0a;
+ else if (*p >= '0' && *p <= '9')
+ seg += *p - '0';
+ else
+ goto invalid_value;
+ if (seg > 0xffff) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Invalid value(over 0xffff) for "
+ "option ``%s''", key);
+ return (ARCHIVE_FATAL);
+ }
+ p++;
+ }
+ iso9660->el_torito.boot_load_seg = (uint16_t)seg;
+ iso9660->opt.boot_load_seg = 1;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "boot-load-size") == 0) {
+ int num = 0;
+ r = get_num_opt(a, &num, 0xffff, 1, key, value);
+ iso9660->opt.boot_load_size = r == ARCHIVE_OK;
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->el_torito.boot_load_size = (uint16_t)num;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "boot-type") == 0) {
+ if (value == NULL)
+ goto invalid_value;
+ if (strcmp(value, "no-emulation") == 0)
+ iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU;
+ else if (strcmp(value, "fd") == 0)
+ iso9660->opt.boot_type = OPT_BOOT_TYPE_FD;
+ else if (strcmp(value, "hard-disk") == 0)
+ iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK;
+ else
+ goto invalid_value;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'c':
+ if (strcmp(key, "compression-level") == 0) {
+#ifdef HAVE_ZLIB_H
+ if (value == NULL ||
+ !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0')
+ goto invalid_value;
+ iso9660->zisofs.compression_level = value[0] - '0';
+ iso9660->opt.compression_level = 1;
+ return (ARCHIVE_OK);
+#else
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Option ``%s'' "
+ "is not supported on this platform.", key);
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ if (strcmp(key, "copyright-file") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->copyright_file_identifier),
+ COPYRIGHT_FILE_SIZE, key, value);
+ iso9660->opt.copyright_file = r == ARCHIVE_OK;
+ return (r);
+ }
+#ifdef DEBUG
+ /* Specifies Volume creation date and time;
+ * year(4),month(2),day(2),hour(2),minute(2),second(2).
+ * e.g. "20090929033757"
+ */
+ if (strcmp(key, "creation") == 0) {
+ struct tm tm;
+ char buf[5];
+
+ p = value;
+ if (p == NULL || strlen(p) < 14)
+ goto invalid_value;
+ memset(&tm, 0, sizeof(tm));
+ memcpy(buf, p, 4); buf[4] = '\0'; p += 4;
+ tm.tm_year = strtol(buf, NULL, 10) - 1900;
+ memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+ tm.tm_mon = strtol(buf, NULL, 10) - 1;
+ memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+ tm.tm_mday = strtol(buf, NULL, 10);
+ memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+ tm.tm_hour = strtol(buf, NULL, 10);
+ memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+ tm.tm_min = strtol(buf, NULL, 10);
+ memcpy(buf, p, 2); buf[2] = '\0';
+ tm.tm_sec = strtol(buf, NULL, 10);
+ iso9660->birth_time = mktime(&tm);
+ return (ARCHIVE_OK);
+ }
+#endif
+ break;
+ case 'i':
+ if (strcmp(key, "iso-level") == 0) {
+ if (value != NULL && value[1] == '\0' &&
+ (value[0] >= '1' && value[0] <= '4')) {
+ iso9660->opt.iso_level = value[0]-'0';
+ return (ARCHIVE_OK);
+ }
+ goto invalid_value;
+ }
+ break;
+ case 'j':
+ if (strcmp(key, "joliet") == 0) {
+ if (value == NULL)
+ iso9660->opt.joliet = OPT_JOLIET_DISABLE;
+ else if (strcmp(value, "1") == 0)
+ iso9660->opt.joliet = OPT_JOLIET_ENABLE;
+ else if (strcmp(value, "long") == 0)
+ iso9660->opt.joliet = OPT_JOLIET_LONGNAME;
+ else
+ goto invalid_value;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'l':
+ if (strcmp(key, "limit-depth") == 0) {
+ iso9660->opt.limit_depth = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "limit-dirs") == 0) {
+ iso9660->opt.limit_dirs = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'p':
+ if (strcmp(key, "pad") == 0) {
+ iso9660->opt.pad = value != NULL;
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "publisher") == 0) {
+ r = get_str_opt(a,
+ &(iso9660->publisher_identifier),
+ PUBLISHER_IDENTIFIER_SIZE, key, value);
+ iso9660->opt.publisher = r == ARCHIVE_OK;
+ return (r);
+ }
+ break;
+ case 'r':
+ if (strcmp(key, "rockridge") == 0 ||
+ strcmp(key, "Rockridge") == 0) {
+ if (value == NULL)
+ iso9660->opt.rr = OPT_RR_DISABLED;
+ else if (strcmp(value, "1") == 0)
+ iso9660->opt.rr = OPT_RR_USEFUL;
+ else if (strcmp(value, "strict") == 0)
+ iso9660->opt.rr = OPT_RR_STRICT;
+ else if (strcmp(value, "useful") == 0)
+ iso9660->opt.rr = OPT_RR_USEFUL;
+ else
+ goto invalid_value;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'v':
+ if (strcmp(key, "volume-id") == 0) {
+ r = get_str_opt(a, &(iso9660->volume_identifier),
+ VOLUME_IDENTIFIER_SIZE, key, value);
+ iso9660->opt.volume_id = r == ARCHIVE_OK;
+ return (r);
+ }
+ break;
+ case 'z':
+ if (strcmp(key, "zisofs") == 0) {
+ if (value == NULL)
+ iso9660->opt.zisofs = OPT_ZISOFS_DISABLED;
+ else {
+#ifdef HAVE_ZLIB_H
+ iso9660->opt.zisofs = OPT_ZISOFS_DIRECT;
+#else
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "``zisofs'' "
+ "is not supported on this platform.");
+ return (ARCHIVE_FATAL);
+#endif
+ }
+ return (ARCHIVE_OK);
+ }
+ break;
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+
+invalid_value:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid value for option ``%s''", key);
+ return (ARCHIVE_FAILED);
+}
+
+static int
+iso9660_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct iso9660 *iso9660;
+ struct isofile *file;
+ struct isoent *isoent;
+ int r, ret = ARCHIVE_OK;
+
+ iso9660 = a->format_data;
+
+ iso9660->cur_file = NULL;
+ iso9660->bytes_remaining = 0;
+ iso9660->need_multi_extent = 0;
+ if (archive_entry_filetype(entry) == AE_IFLNK
+ && iso9660->opt.rr == OPT_RR_DISABLED) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Ignore symlink file.");
+ iso9660->cur_file = NULL;
+ return (ARCHIVE_WARN);
+ }
+ if (archive_entry_filetype(entry) == AE_IFREG &&
+ archive_entry_size(entry) >= MULTI_EXTENT_SIZE) {
+ if (iso9660->opt.iso_level < 3) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Ignore over %lld bytes file. "
+ "This file too large.",
+ MULTI_EXTENT_SIZE);
+ iso9660->cur_file = NULL;
+ return (ARCHIVE_WARN);
+ }
+ iso9660->need_multi_extent = 1;
+ }
+
+ file = isofile_new(a, entry);
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ return (ARCHIVE_FATAL);
+ }
+ r = isofile_gen_utility_names(a, file);
+ if (r < ARCHIVE_WARN) {
+ isofile_free(file);
+ return (r);
+ }
+ else if (r < ret)
+ ret = r;
+
+ /*
+ * Ignore a path which looks like the top of directory name
+ * since we have already made the root directory of an ISO image.
+ */
+ if (archive_strlen(&(file->parentdir)) == 0 &&
+ archive_strlen(&(file->basename)) == 0) {
+ isofile_free(file);
+ return (r);
+ }
+
+ isofile_add_entry(iso9660, file);
+ isoent = isoent_new(file);
+ if (isoent == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ return (ARCHIVE_FATAL);
+ }
+ if (isoent->file->dircnt > iso9660->dircnt_max)
+ iso9660->dircnt_max = isoent->file->dircnt;
+
+ /* Add the current file into tree */
+ r = isoent_tree(a, &isoent);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /* If there is the same file in tree and
+ * the current file is older than the file in tree.
+ * So we don't need the current file data anymore. */
+ if (isoent->file != file)
+ return (ARCHIVE_OK);
+
+ /* Non regular files contents are unneeded to be saved to
+ * temporary files. */
+ if (archive_entry_filetype(file->entry) != AE_IFREG)
+ return (ret);
+
+ /*
+ * Set the current file to cur_file to read its contents.
+ */
+ iso9660->cur_file = file;
+
+ if (archive_entry_nlink(file->entry) > 1) {
+ r = isofile_register_hardlink(a, file);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Prepare to save the contents of the file.
+ */
+ if (iso9660->temp_fd < 0) {
+ iso9660->temp_fd = __archive_mktemp(NULL);
+ if (iso9660->temp_fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't create temporary file");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* Save an offset of current file in temporary file. */
+ file->content.offset_of_temp = wb_offset(a);
+ file->cur_content = &(file->content);
+ r = zisofs_init(a, file);
+ if (r < ret)
+ ret = r;
+ iso9660->bytes_remaining = archive_entry_size(file->entry);
+
+ return (ret);
+}
+
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ ssize_t written;
+ const unsigned char *b;
+
+ b = (const unsigned char *)buff;
+ while (s) {
+ written = write(iso9660->temp_fd, b, s);
+ if (written < 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't write to temporary file");
+ return (ARCHIVE_FATAL);
+ }
+ s -= written;
+ b += written;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+wb_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ const char *xp = buff;
+ size_t xs = s;
+
+ /*
+ * If a written data size is big enough to use system-call
+ * and there is no waiting data, this calls write_to_temp() in
+ * order to reduce a extra memory copy.
+ */
+ if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) {
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ xs = s % LOGICAL_BLOCK_SIZE;
+ iso9660->wbuff_offset += s - xs;
+ if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (xs == 0)
+ return (ARCHIVE_OK);
+ xp += s - xs;
+ }
+
+ while (xs) {
+ size_t size = xs;
+ if (size > wb_remaining(a))
+ size = wb_remaining(a);
+ memcpy(wb_buffptr(a), xp, size);
+ if (wb_consume(a, size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ xs -= size;
+ xp += size;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+wb_write_padding_to_temp(struct archive_write *a, int64_t csize)
+{
+ size_t ns;
+ int ret;
+
+ ns = (size_t)(csize % LOGICAL_BLOCK_SIZE);
+ if (ns != 0)
+ ret = write_null(a, LOGICAL_BLOCK_SIZE - ns);
+ else
+ ret = ARCHIVE_OK;
+ return (ret);
+}
+
+static ssize_t
+write_iso9660_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ size_t ws;
+
+ if (iso9660->temp_fd < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Couldn't create temporary file");
+ return (ARCHIVE_FATAL);
+ }
+
+ ws = s;
+ if (iso9660->need_multi_extent &&
+ (iso9660->cur_file->cur_content->size + ws) >=
+ (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) {
+ struct content *con;
+ size_t ts;
+
+ ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE -
+ iso9660->cur_file->cur_content->size);
+
+ if (iso9660->zisofs.detect_magic)
+ zisofs_detect_magic(a, buff, ts);
+
+ if (iso9660->zisofs.making) {
+ if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->cur_file->cur_content->size += ts;
+ }
+
+ /* Write padding. */
+ if (wb_write_padding_to_temp(a,
+ iso9660->cur_file->cur_content->size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Compute the logical block number. */
+ iso9660->cur_file->cur_content->blocks = (int)
+ ((iso9660->cur_file->cur_content->size
+ + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+
+ /*
+ * Make next extent.
+ */
+ ws -= ts;
+ buff = (const void *)(((const unsigned char *)buff) + ts);
+ /* Make a content for next extent. */
+ con = calloc(1, sizeof(*con));
+ if (con == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate content data");
+ return (ARCHIVE_FATAL);
+ }
+ con->offset_of_temp = wb_offset(a);
+ iso9660->cur_file->cur_content->next = con;
+ iso9660->cur_file->cur_content = con;
+#ifdef HAVE_ZLIB_H
+ iso9660->zisofs.block_offset = 0;
+#endif
+ }
+
+ if (iso9660->zisofs.detect_magic)
+ zisofs_detect_magic(a, buff, ws);
+
+ if (iso9660->zisofs.making) {
+ if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->cur_file->cur_content->size += ws;
+ }
+
+ return (s);
+}
+
+static ssize_t
+iso9660_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ ssize_t r;
+
+ if (iso9660->cur_file == NULL)
+ return (0);
+ if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)
+ return (0);
+ if (s > iso9660->bytes_remaining)
+ s = (size_t)iso9660->bytes_remaining;
+ if (s == 0)
+ return (0);
+
+ r = write_iso9660_data(a, buff, s);
+ if (r > 0)
+ iso9660->bytes_remaining -= r;
+ return (r);
+}
+
+static int
+iso9660_finish_entry(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+
+ if (iso9660->cur_file == NULL)
+ return (ARCHIVE_OK);
+ if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)
+ return (ARCHIVE_OK);
+ if (iso9660->cur_file->content.size == 0)
+ return (ARCHIVE_OK);
+
+ /* If there are unwritten data, write null data instead. */
+ while (iso9660->bytes_remaining > 0) {
+ size_t s;
+
+ s = (iso9660->bytes_remaining > a->null_length)?
+ a->null_length: (size_t)iso9660->bytes_remaining;
+ if (write_iso9660_data(a, a->nulls, s) < 0)
+ return (ARCHIVE_FATAL);
+ iso9660->bytes_remaining -= s;
+ }
+
+ if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write padding. */
+ if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Compute the logical block number. */
+ iso9660->cur_file->cur_content->blocks = (int)
+ ((iso9660->cur_file->cur_content->size
+ + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+
+ /* Add the current file to data file list. */
+ isofile_add_data_file(iso9660, iso9660->cur_file);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+iso9660_close(struct archive_write *a)
+{
+ struct iso9660 *iso9660;
+ int ret, blocks;
+
+ iso9660 = a->format_data;
+
+ /*
+ * Write remaining data out to the temporary file.
+ */
+ if (wb_remaining(a) > 0) {
+ ret = wb_write_out(a);
+ if (ret < 0)
+ return (ret);
+ }
+
+ /*
+ * Preparations...
+ */
+#ifdef DEBUG
+ if (iso9660->birth_time == 0)
+#endif
+ time(&(iso9660->birth_time));
+
+ /*
+ * Prepare a bootable ISO image.
+ */
+ if (iso9660->opt.boot) {
+ /* Find out the boot file entry. */
+ ret = isoent_find_out_boot_file(a, iso9660->primary.rootent);
+ if (ret < 0)
+ return (ret);
+ /* Reconvert the boot file from zisofs'ed form to
+ * plain form. */
+ ret = zisofs_rewind_boot_file(a);
+ if (ret < 0)
+ return (ret);
+ /* Write remaining data out to the temporary file. */
+ if (wb_remaining(a) > 0) {
+ ret = wb_write_out(a);
+ if (ret < 0)
+ return (ret);
+ }
+ /* Create the boot catalog. */
+ ret = isoent_create_boot_catalog(a, iso9660->primary.rootent);
+ if (ret < 0)
+ return (ret);
+ }
+
+ /*
+ * Prepare joliet extensions.
+ */
+ if (iso9660->opt.joliet) {
+ /* Make a new tree for joliet. */
+ ret = isoent_clone_tree(a, &(iso9660->joliet.rootent),
+ iso9660->primary.rootent);
+ if (ret < 0)
+ return (ret);
+ /* Make sure we have UTF-16BE converters.
+ * if there is no file entry, converters are still
+ * uninitialized. */
+ if (iso9660->sconv_to_utf16be == NULL) {
+ iso9660->sconv_to_utf16be =
+ archive_string_conversion_to_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_to_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ iso9660->sconv_from_utf16be =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_from_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /*
+ * Make Path Tables.
+ */
+ ret = isoent_make_path_table(a);
+ if (ret < 0)
+ return (ret);
+
+ /*
+ * Calculate a total volume size and setup all locations of
+ * contents of an iso9660 image.
+ */
+ blocks = SYSTEM_AREA_BLOCK
+ + PRIMARY_VOLUME_DESCRIPTOR_BLOCK
+ + VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK
+ + NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
+ if (iso9660->opt.boot)
+ blocks += BOOT_RECORD_DESCRIPTOR_BLOCK;
+ if (iso9660->opt.joliet)
+ blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;
+ if (iso9660->opt.iso_level == 4)
+ blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;
+
+ /* Setup the locations of Path Table. */
+ iso9660->primary.location_type_L_path_table = blocks;
+ blocks += iso9660->primary.path_table_block;
+ iso9660->primary.location_type_M_path_table = blocks;
+ blocks += iso9660->primary.path_table_block;
+ if (iso9660->opt.joliet) {
+ iso9660->joliet.location_type_L_path_table = blocks;
+ blocks += iso9660->joliet.path_table_block;
+ iso9660->joliet.location_type_M_path_table = blocks;
+ blocks += iso9660->joliet.path_table_block;
+ }
+
+ /* Setup the locations of directories. */
+ isoent_setup_directory_location(iso9660, blocks,
+ &(iso9660->primary));
+ blocks += iso9660->primary.total_dir_block;
+ if (iso9660->opt.joliet) {
+ isoent_setup_directory_location(iso9660, blocks,
+ &(iso9660->joliet));
+ blocks += iso9660->joliet.total_dir_block;
+ }
+
+ if (iso9660->opt.rr) {
+ iso9660->location_rrip_er = blocks;
+ blocks += RRIP_ER_BLOCK;
+ }
+
+ /* Setup the locations of all file contents. */
+ isoent_setup_file_location(iso9660, blocks);
+ blocks += iso9660->total_file_block;
+ if (iso9660->opt.boot && iso9660->opt.boot_info_table) {
+ ret = setup_boot_information(a);
+ if (ret < 0)
+ return (ret);
+ }
+
+ /* Now we have a total volume size. */
+ iso9660->volume_space_size = blocks;
+ if (iso9660->opt.pad)
+ iso9660->volume_space_size += PADDING_BLOCK;
+ iso9660->volume_sequence_number = 1;
+
+
+ /*
+ * Write an ISO 9660 image.
+ */
+
+ /* Switch to start using wbuff as file buffer. */
+ iso9660->wbuff_remaining = wb_buffmax();
+ iso9660->wbuff_type = WB_TO_STREAM;
+ iso9660->wbuff_offset = 0;
+ iso9660->wbuff_written = 0;
+ iso9660->wbuff_tail = 0;
+
+ /* Write The System Area */
+ ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Primary Volume Descriptor */
+ ret = write_VD(a, &(iso9660->primary));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ if (iso9660->opt.boot) {
+ /* Write Boot Record Volume Descriptor */
+ ret = write_VD_boot_record(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ if (iso9660->opt.iso_level == 4) {
+ /* Write Enhanced Volume Descriptor */
+ iso9660->primary.vdd_type = VDD_ENHANCED;
+ ret = write_VD(a, &(iso9660->primary));
+ iso9660->primary.vdd_type = VDD_PRIMARY;
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ if (iso9660->opt.joliet) {
+ ret = write_VD(a, &(iso9660->joliet));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Write Volume Descriptor Set Terminator */
+ ret = write_VD_terminator(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Non-ISO File System Information */
+ ret = write_information_block(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Type L Path Table */
+ ret = write_path_table(a, 0, &(iso9660->primary));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Type M Path Table */
+ ret = write_path_table(a, 1, &(iso9660->primary));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ if (iso9660->opt.joliet) {
+ /* Write Type L Path Table */
+ ret = write_path_table(a, 0, &(iso9660->joliet));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Type M Path Table */
+ ret = write_path_table(a, 1, &(iso9660->joliet));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Write Directory Descriptors */
+ ret = write_directory_descriptors(a, &(iso9660->primary));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ if (iso9660->opt.joliet) {
+ ret = write_directory_descriptors(a, &(iso9660->joliet));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ if (iso9660->opt.rr) {
+ /* Write Rockridge ER(Extensions Reference) */
+ ret = write_rr_ER(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Write File Descriptors */
+ ret = write_file_descriptors(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Write Padding */
+ if (iso9660->opt.pad) {
+ ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ if (iso9660->directories_too_deep != NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: Directories too deep.",
+ archive_entry_pathname(
+ iso9660->directories_too_deep->file->entry));
+ return (ARCHIVE_WARN);
+ }
+
+ /* Write remaining data out. */
+ ret = wb_write_out(a);
+
+ return (ret);
+}
+
+static int
+iso9660_free(struct archive_write *a)
+{
+ struct iso9660 *iso9660;
+ int i, ret;
+
+ iso9660 = a->format_data;
+
+ /* Close the temporary file. */
+ if (iso9660->temp_fd >= 0)
+ close(iso9660->temp_fd);
+
+ /* Free some stuff for zisofs operations. */
+ ret = zisofs_free(a);
+
+ /* Remove directory entries in tree which includes file entries. */
+ isoent_free_all(iso9660->primary.rootent);
+ for (i = 0; i < iso9660->primary.max_depth; i++)
+ free(iso9660->primary.pathtbl[i].sorted);
+ free(iso9660->primary.pathtbl);
+
+ if (iso9660->opt.joliet) {
+ isoent_free_all(iso9660->joliet.rootent);
+ for (i = 0; i < iso9660->joliet.max_depth; i++)
+ free(iso9660->joliet.pathtbl[i].sorted);
+ free(iso9660->joliet.pathtbl);
+ }
+
+ /* Remove isofile entries. */
+ isofile_free_all_entries(iso9660);
+ isofile_free_hardlinks(iso9660);
+
+ archive_string_free(&(iso9660->cur_dirstr));
+ archive_string_free(&(iso9660->volume_identifier));
+ archive_string_free(&(iso9660->publisher_identifier));
+ archive_string_free(&(iso9660->data_preparer_identifier));
+ archive_string_free(&(iso9660->application_identifier));
+ archive_string_free(&(iso9660->copyright_file_identifier));
+ archive_string_free(&(iso9660->abstract_file_identifier));
+ archive_string_free(&(iso9660->bibliographic_file_identifier));
+ archive_string_free(&(iso9660->el_torito.catalog_filename));
+ archive_string_free(&(iso9660->el_torito.boot_filename));
+ archive_string_free(&(iso9660->el_torito.id));
+ archive_string_free(&(iso9660->utf16be));
+ archive_string_free(&(iso9660->mbs));
+
+ free(iso9660);
+ a->format_data = NULL;
+
+ return (ret);
+}
+
+/*
+ * Get the System Identifier
+ */
+static void
+get_system_identitier(char *system_id, size_t size)
+{
+#if defined(HAVE_SYS_UTSNAME_H)
+ struct utsname u;
+
+ uname(&u);
+ strncpy(system_id, u.sysname, size-1);
+ system_id[size-1] = '\0';
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+ strncpy(system_id, "Windows", size-1);
+ system_id[size-1] = '\0';
+#else
+ strncpy(system_id, "Unknown", size-1);
+ system_id[size-1] = '\0';
+#endif
+}
+
+static void
+set_str(unsigned char *p, const char *s, size_t l, char f, const char *map)
+{
+ unsigned char c;
+
+ if (s == NULL)
+ s = "";
+ while ((c = *s++) != 0 && l > 0) {
+ if (c >= 0x80 || map[c] == 0)
+ {
+ /* illegal character */
+ if (c >= 'a' && c <= 'z') {
+ /* convert c from a-z to A-Z */
+ c -= 0x20;
+ } else
+ c = 0x5f;
+ }
+ *p++ = c;
+ l--;
+ }
+ /* If l isn't zero, fill p buffer by the character
+ * which indicated by f. */
+ if (l > 0)
+ memset(p , f, l);
+}
+
+static inline int
+joliet_allowed_char(unsigned char high, unsigned char low)
+{
+ int utf16 = (high << 8) | low;
+
+ if (utf16 <= 0x001F)
+ return (0);
+
+ switch (utf16) {
+ case 0x002A: /* '*' */
+ case 0x002F: /* '/' */
+ case 0x003A: /* ':' */
+ case 0x003B: /* ';' */
+ case 0x003F: /* '?' */
+ case 0x005C: /* '\' */
+ return (0);/* Not allowed. */
+ }
+ return (1);
+}
+
+static int
+set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s,
+ size_t l, uint16_t uf, enum vdc vdc)
+{
+ size_t size, i;
+ int onepad;
+
+ if (s == NULL)
+ s = "";
+ if (l & 0x01) {
+ onepad = 1;
+ l &= ~1;
+ } else
+ onepad = 0;
+ if (vdc == VDC_UCS2) {
+ struct iso9660 *iso9660 = a->format_data;
+ if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s),
+ iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for UTF-16BE");
+ return (ARCHIVE_FATAL);
+ }
+ size = iso9660->utf16be.length;
+ if (size > l)
+ size = l;
+ memcpy(p, iso9660->utf16be.s, size);
+ } else {
+ const uint16_t *u16 = (const uint16_t *)s;
+
+ size = 0;
+ while (*u16++)
+ size += 2;
+ if (size > l)
+ size = l;
+ memcpy(p, s, size);
+ }
+ for (i = 0; i < size; i += 2, p += 2) {
+ if (!joliet_allowed_char(p[0], p[1]))
+ archive_be16enc(p, 0x005F);/* '_' */
+ }
+ l -= size;
+ while (l > 0) {
+ archive_be16enc(p, uf);
+ p += 2;
+ l -= 2;
+ }
+ if (onepad)
+ *p = 0;
+ return (ARCHIVE_OK);
+}
+
+static const char a_characters_map[0x80] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char a1_characters_map[0x80] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char d_characters_map[0x80] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char d1_characters_map[0x80] = {
+/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static int
+set_str_a_characters_bp(struct archive_write *a, unsigned char *bp,
+ int from, int to, const char *s, enum vdc vdc)
+{
+ int r;
+
+ switch (vdc) {
+ case VDC_STD:
+ set_str(bp+from, s, to - from + 1, 0x20,
+ a_characters_map);
+ r = ARCHIVE_OK;
+ break;
+ case VDC_LOWERCASE:
+ set_str(bp+from, s, to - from + 1, 0x20,
+ a1_characters_map);
+ r = ARCHIVE_OK;
+ break;
+ case VDC_UCS2:
+ case VDC_UCS2_DIRECT:
+ r = set_str_utf16be(a, bp+from, s, to - from + 1,
+ 0x0020, vdc);
+ break;
+ default:
+ r = ARCHIVE_FATAL;
+ }
+ return (r);
+}
+
+static int
+set_str_d_characters_bp(struct archive_write *a, unsigned char *bp,
+ int from, int to, const char *s, enum vdc vdc)
+{
+ int r;
+
+ switch (vdc) {
+ case VDC_STD:
+ set_str(bp+from, s, to - from + 1, 0x20,
+ d_characters_map);
+ r = ARCHIVE_OK;
+ break;
+ case VDC_LOWERCASE:
+ set_str(bp+from, s, to - from + 1, 0x20,
+ d1_characters_map);
+ r = ARCHIVE_OK;
+ break;
+ case VDC_UCS2:
+ case VDC_UCS2_DIRECT:
+ r = set_str_utf16be(a, bp+from, s, to - from + 1,
+ 0x0020, vdc);
+ break;
+ default:
+ r = ARCHIVE_FATAL;
+ }
+ return (r);
+}
+
+static void
+set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver)
+{
+
+ /* Volume Descriptor Type */
+ bp[1] = (unsigned char)type;
+ /* Standard Identifier */
+ memcpy(bp + 2, "CD001", 5);
+ /* Volume Descriptor Version */
+ bp[7] = ver;
+}
+
+static inline void
+set_unused_field_bp(unsigned char *bp, int from, int to)
+{
+ memset(bp + from, 0, to - from + 1);
+}
+
+/*
+ * 8-bit unsigned numerical values.
+ * ISO9660 Standard 7.1.1
+ */
+static inline void
+set_num_711(unsigned char *p, unsigned char value)
+{
+ *p = value;
+}
+
+/*
+ * 8-bit signed numerical values.
+ * ISO9660 Standard 7.1.2
+ */
+static inline void
+set_num_712(unsigned char *p, char value)
+{
+ *((char *)p) = value;
+}
+
+/*
+ * Least significant byte first.
+ * ISO9660 Standard 7.2.1
+ */
+static inline void
+set_num_721(unsigned char *p, uint16_t value)
+{
+ archive_le16enc(p, value);
+}
+
+/*
+ * Most significant byte first.
+ * ISO9660 Standard 7.2.2
+ */
+static inline void
+set_num_722(unsigned char *p, uint16_t value)
+{
+ archive_be16enc(p, value);
+}
+
+/*
+ * Both-byte orders.
+ * ISO9660 Standard 7.2.3
+ */
+static void
+set_num_723(unsigned char *p, uint16_t value)
+{
+ archive_le16enc(p, value);
+ archive_be16enc(p+2, value);
+}
+
+/*
+ * Least significant byte first.
+ * ISO9660 Standard 7.3.1
+ */
+static inline void
+set_num_731(unsigned char *p, uint32_t value)
+{
+ archive_le32enc(p, value);
+}
+
+/*
+ * Most significant byte first.
+ * ISO9660 Standard 7.3.2
+ */
+static inline void
+set_num_732(unsigned char *p, uint32_t value)
+{
+ archive_be32enc(p, value);
+}
+
+/*
+ * Both-byte orders.
+ * ISO9660 Standard 7.3.3
+ */
+static inline void
+set_num_733(unsigned char *p, uint32_t value)
+{
+ archive_le32enc(p, value);
+ archive_be32enc(p+4, value);
+}
+
+static void
+set_digit(unsigned char *p, size_t s, int value)
+{
+
+ while (s--) {
+ p[s] = '0' + (value % 10);
+ value /= 10;
+ }
+}
+
+#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
+#define get_gmoffset(tm) ((tm)->tm_gmtoff)
+#elif defined(HAVE_STRUCT_TM___TM_GMTOFF)
+#define get_gmoffset(tm) ((tm)->__tm_gmtoff)
+#else
+static long
+get_gmoffset(struct tm *tm)
+{
+ long offset;
+
+#if defined(HAVE__GET_TIMEZONE)
+ _get_timezone(&offset);
+#elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
+ offset = _timezone;
+#else
+ offset = timezone;
+#endif
+ offset *= -1;
+ if (tm->tm_isdst)
+ offset += 3600;
+ return (offset);
+}
+#endif
+
+static void
+get_tmfromtime(struct tm *tm, time_t *t)
+{
+#if HAVE_LOCALTIME_S
+ localtime_s(tm, t);
+#elif HAVE_LOCALTIME_R
+ tzset();
+ localtime_r(t, tm);
+#else
+ memcpy(tm, localtime(t), sizeof(*tm));
+#endif
+}
+
+/*
+ * Date and Time Format.
+ * ISO9660 Standard 8.4.26.1
+ */
+static void
+set_date_time(unsigned char *p, time_t t)
+{
+ struct tm tm;
+
+ get_tmfromtime(&tm, &t);
+ set_digit(p, 4, tm.tm_year + 1900);
+ set_digit(p+4, 2, tm.tm_mon + 1);
+ set_digit(p+6, 2, tm.tm_mday);
+ set_digit(p+8, 2, tm.tm_hour);
+ set_digit(p+10, 2, tm.tm_min);
+ set_digit(p+12, 2, tm.tm_sec);
+ set_digit(p+14, 2, 0);
+ set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15)));
+}
+
+static void
+set_date_time_null(unsigned char *p)
+{
+ memset(p, (int)'0', 16);
+ p[16] = 0;
+}
+
+static void
+set_time_915(unsigned char *p, time_t t)
+{
+ struct tm tm;
+
+ get_tmfromtime(&tm, &t);
+ set_num_711(p+0, tm.tm_year);
+ set_num_711(p+1, tm.tm_mon+1);
+ set_num_711(p+2, tm.tm_mday);
+ set_num_711(p+3, tm.tm_hour);
+ set_num_711(p+4, tm.tm_min);
+ set_num_711(p+5, tm.tm_sec);
+ set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15)));
+}
+
+
+/*
+ * Write SUSP "CE" System Use Entry.
+ */
+static int
+set_SUSP_CE(unsigned char *p, int location, int offset, int size)
+{
+ unsigned char *bp = p -1;
+ /* Extend the System Use Area
+ * "CE" Format:
+ * len ver
+ * +----+----+----+----+-----------+-----------+
+ * | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 |
+ * +----+----+----+----+-----------+-----------+
+ * 0 1 2 3 4 12 20
+ * +-----------+
+ * | LOCATION3 |
+ * +-----------+
+ * 20 28
+ * LOCATION1 : Location of Continuation of System Use Area.
+ * LOCATION2 : Offset to Start of Continuation.
+ * LOCATION3 : Length of the Continuation.
+ */
+
+ bp[1] = 'C';
+ bp[2] = 'E';
+ bp[3] = RR_CE_SIZE; /* length */
+ bp[4] = 1; /* version */
+ set_num_733(bp+5, location);
+ set_num_733(bp+13, offset);
+ set_num_733(bp+21, size);
+ return (RR_CE_SIZE);
+}
+
+/*
+ * The functions, which names are beginning with extra_, are used to
+ * control extra records.
+ * The maximum size of a Directory Record is 254. When a filename is
+ * very long, all of RRIP data of a file won't stored to the Directory
+ * Record and so remaining RRIP data store to an extra record instead.
+ */
+static unsigned char *
+extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent,
+ struct ctl_extr_rec *ctl)
+{
+ ctl->bp = bp;
+ if (bp != NULL)
+ bp += dr_len;
+ ctl->use_extr = 0;
+ ctl->isoent = isoent;
+ ctl->ce_ptr = NULL;
+ ctl->cur_len = ctl->dr_len = dr_len;
+ ctl->limit = DR_LIMIT;
+
+ return (bp);
+}
+
+static void
+extra_close_record(struct ctl_extr_rec *ctl, int ce_size)
+{
+ int padding = 0;
+
+ if (ce_size > 0)
+ extra_tell_used_size(ctl, ce_size);
+ /* Padding. */
+ if (ctl->cur_len & 0x01) {
+ ctl->cur_len++;
+ if (ctl->bp != NULL)
+ ctl->bp[ctl->cur_len] = 0;
+ padding = 1;
+ }
+ if (ctl->use_extr) {
+ if (ctl->ce_ptr != NULL)
+ set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc,
+ ctl->extr_off, ctl->cur_len - padding);
+ } else
+ ctl->dr_len = ctl->cur_len;
+}
+
+#define extra_space(ctl) ((ctl)->limit - (ctl)->cur_len)
+
+static unsigned char *
+extra_next_record(struct ctl_extr_rec *ctl, int length)
+{
+ int cur_len = ctl->cur_len;/* save cur_len */
+
+ /* Close the current extra record or Directory Record. */
+ extra_close_record(ctl, RR_CE_SIZE);
+
+ /* Get a next extra record. */
+ ctl->use_extr = 1;
+ if (ctl->bp != NULL) {
+ /* Storing data into an extra record. */
+ unsigned char *p;
+
+ /* Save the pointer where a CE extension will be
+ * stored to. */
+ ctl->ce_ptr = &ctl->bp[cur_len+1];
+ p = extra_get_record(ctl->isoent,
+ &ctl->limit, &ctl->extr_off, &ctl->extr_loc);
+ ctl->bp = p - 1;/* the base of bp offset is 1. */
+ } else
+ /* Calculating the size of an extra record. */
+ (void)extra_get_record(ctl->isoent,
+ &ctl->limit, NULL, NULL);
+ ctl->cur_len = 0;
+ /* Check if an extra record is almost full.
+ * If so, get a next one. */
+ if (extra_space(ctl) < length)
+ (void)extra_next_record(ctl, length);
+
+ return (ctl->bp);
+}
+
+static inline struct extr_rec *
+extra_last_record(struct isoent *isoent)
+{
+ if (isoent->extr_rec_list.first == NULL)
+ return (NULL);
+ return ((struct extr_rec *)(void *)
+ ((char *)(isoent->extr_rec_list.last)
+ - offsetof(struct extr_rec, next)));
+}
+
+static unsigned char *
+extra_get_record(struct isoent *isoent, int *space, int *off, int *loc)
+{
+ struct extr_rec *rec;
+
+ isoent = isoent->parent;
+ if (off != NULL) {
+ /* Storing data into an extra record. */
+ rec = isoent->extr_rec_list.current;
+ if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset)
+ rec = rec->next;
+ } else {
+ /* Calculating the size of an extra record. */
+ rec = extra_last_record(isoent);
+ if (rec == NULL ||
+ DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) {
+ rec = malloc(sizeof(*rec));
+ if (rec == NULL)
+ return (NULL);
+ rec->location = 0;
+ rec->offset = 0;
+ /* Insert `rec` into the tail of isoent->extr_rec_list */
+ rec->next = NULL;
+ /*
+ * Note: testing isoent->extr_rec_list.last == NULL
+ * here is really unneeded since it has been already
+ * initialized at isoent_new function but Clang Static
+ * Analyzer claims that it is dereference of null
+ * pointer.
+ */
+ if (isoent->extr_rec_list.last == NULL)
+ isoent->extr_rec_list.last =
+ &(isoent->extr_rec_list.first);
+ *isoent->extr_rec_list.last = rec;
+ isoent->extr_rec_list.last = &(rec->next);
+ }
+ }
+ *space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY;
+ if (*space & 0x01)
+ *space -= 1;/* Keep padding space. */
+ if (off != NULL)
+ *off = rec->offset;
+ if (loc != NULL)
+ *loc = rec->location;
+ isoent->extr_rec_list.current = rec;
+
+ return (&rec->buf[rec->offset]);
+}
+
+static void
+extra_tell_used_size(struct ctl_extr_rec *ctl, int size)
+{
+ struct isoent *isoent;
+ struct extr_rec *rec;
+
+ if (ctl->use_extr) {
+ isoent = ctl->isoent->parent;
+ rec = isoent->extr_rec_list.current;
+ if (rec != NULL)
+ rec->offset += size;
+ }
+ ctl->cur_len += size;
+}
+
+static int
+extra_setup_location(struct isoent *isoent, int location)
+{
+ struct extr_rec *rec;
+ int cnt;
+
+ cnt = 0;
+ rec = isoent->extr_rec_list.first;
+ isoent->extr_rec_list.current = rec;
+ while (rec) {
+ cnt++;
+ rec->location = location++;
+ rec->offset = 0;
+ rec = rec->next;
+ }
+ return (cnt);
+}
+
+/*
+ * Create the RRIP entries.
+ */
+static int
+set_directory_record_rr(unsigned char *bp, int dr_len,
+ struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t)
+{
+ /* Flags(BP 5) of the Rockridge "RR" System Use Field */
+ unsigned char rr_flag;
+#define RR_USE_PX 0x01
+#define RR_USE_PN 0x02
+#define RR_USE_SL 0x04
+#define RR_USE_NM 0x08
+#define RR_USE_CL 0x10
+#define RR_USE_PL 0x20
+#define RR_USE_RE 0x40
+#define RR_USE_TF 0x80
+ int length;
+ struct ctl_extr_rec ctl;
+ struct isoent *rr_parent, *pxent;
+ struct isofile *file;
+
+ bp = extra_open_record(bp, dr_len, isoent, &ctl);
+
+ if (t == DIR_REC_PARENT) {
+ rr_parent = isoent->rr_parent;
+ pxent = isoent->parent;
+ if (rr_parent != NULL)
+ isoent = rr_parent;
+ else
+ isoent = isoent->parent;
+ } else {
+ rr_parent = NULL;
+ pxent = isoent;
+ }
+ file = isoent->file;
+
+ if (t != DIR_REC_NORMAL) {
+ rr_flag = RR_USE_PX | RR_USE_TF;
+ if (rr_parent != NULL)
+ rr_flag |= RR_USE_PL;
+ } else {
+ rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF;
+ if (archive_entry_filetype(file->entry) == AE_IFLNK)
+ rr_flag |= RR_USE_SL;
+ if (isoent->rr_parent != NULL)
+ rr_flag |= RR_USE_RE;
+ if (isoent->rr_child != NULL)
+ rr_flag |= RR_USE_CL;
+ if (archive_entry_filetype(file->entry) == AE_IFCHR ||
+ archive_entry_filetype(file->entry) == AE_IFBLK)
+ rr_flag |= RR_USE_PN;
+#ifdef COMPAT_MKISOFS
+ /*
+ * mkisofs 2.01.01a63 records "RE" extension to
+ * the entry of "rr_moved" directory.
+ * I don't understand this behavior.
+ */
+ if (isoent->virtual &&
+ isoent->parent == iso9660->primary.rootent &&
+ strcmp(isoent->file->basename.s, "rr_moved") == 0)
+ rr_flag |= RR_USE_RE;
+#endif
+ }
+
+ /* Write "SP" System Use Entry. */
+ if (t == DIR_REC_SELF && isoent == isoent->parent) {
+ length = 7;
+ if (bp != NULL) {
+ bp[1] = 'S';
+ bp[2] = 'P';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp[5] = 0xBE; /* Check Byte */
+ bp[6] = 0xEF; /* Check Byte */
+ bp[7] = 0;
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "RR" System Use Entry. */
+ length = 5;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'R';
+ bp[2] = 'R';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp[5] = rr_flag;
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+
+ /* Write "NM" System Use Entry. */
+ if (rr_flag & RR_USE_NM) {
+ /*
+ * "NM" Format:
+ * e.g. a basename is 'foo'
+ * len ver flg
+ * +----+----+----+----+----+----+----+----+
+ * | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'|
+ * +----+----+----+----+----+----+----+----+
+ * <----------------- len ----------------->
+ */
+ size_t nmlen = file->basename.length;
+ const char *nm = file->basename.s;
+ size_t nmmax;
+
+ if (extra_space(&ctl) < 6)
+ bp = extra_next_record(&ctl, 6);
+ if (bp != NULL) {
+ bp[1] = 'N';
+ bp[2] = 'M';
+ bp[4] = 1; /* version */
+ }
+ nmmax = extra_space(&ctl);
+ if (nmmax > 0xff)
+ nmmax = 0xff;
+ while (nmlen + 5 > nmmax) {
+ length = (int)nmmax;
+ if (bp != NULL) {
+ bp[3] = length;
+ bp[5] = 0x01;/* Alternate Name continues
+ * in next "NM" field */
+ memcpy(bp+6, nm, length - 5);
+ bp += length;
+ }
+ nmlen -= length - 5;
+ nm += length - 5;
+ extra_tell_used_size(&ctl, length);
+ if (extra_space(&ctl) < 6) {
+ bp = extra_next_record(&ctl, 6);
+ nmmax = extra_space(&ctl);
+ if (nmmax > 0xff)
+ nmmax = 0xff;
+ }
+ if (bp != NULL) {
+ bp[1] = 'N';
+ bp[2] = 'M';
+ bp[4] = 1; /* version */
+ }
+ }
+ length = 5 + (int)nmlen;
+ if (bp != NULL) {
+ bp[3] = length;
+ bp[5] = 0;
+ memcpy(bp+6, nm, nmlen);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "PX" System Use Entry. */
+ if (rr_flag & RR_USE_PX) {
+ /*
+ * "PX" Format:
+ * len ver
+ * +----+----+----+----+-----------+-----------+
+ * | 'P'| 'X'| 2C | 01 | FILE MODE | LINKS |
+ * +----+----+----+----+-----------+-----------+
+ * 0 1 2 3 4 12 20
+ * +-----------+-----------+------------------+
+ * | USER ID | GROUP ID |FILE SERIAL NUMBER|
+ * +-----------+-----------+------------------+
+ * 20 28 36 44
+ */
+ length = 44;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ mode_t mode;
+ int64_t uid;
+ int64_t gid;
+
+ mode = archive_entry_mode(file->entry);
+ uid = archive_entry_uid(file->entry);
+ gid = archive_entry_gid(file->entry);
+ if (iso9660->opt.rr == OPT_RR_USEFUL) {
+ /*
+ * This action is similar to mkisofs -r option
+ * but our rockridge=useful option does not
+ * set a zero to uid and gid.
+ */
+ /* set all read bit ON */
+ mode |= 0444;
+#if !defined(_WIN32) && !defined(__CYGWIN__)
+ if (mode & 0111)
+#endif
+ /* set all exec bit ON */
+ mode |= 0111;
+ /* clear all write bits. */
+ mode &= ~0222;
+ /* clear setuid,setgid,sticky bits. */
+ mode &= ~07000;
+ }
+
+ bp[1] = 'P';
+ bp[2] = 'X';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ /* file mode */
+ set_num_733(bp+5, mode);
+ /* file links (stat.st_nlink) */
+ set_num_733(bp+13,
+ archive_entry_nlink(file->entry));
+ set_num_733(bp+21, (uint32_t)uid);
+ set_num_733(bp+29, (uint32_t)gid);
+ /* File Serial Number */
+ if (pxent->dir)
+ set_num_733(bp+37, pxent->dir_location);
+ else if (file->hardlink_target != NULL)
+ set_num_733(bp+37,
+ file->hardlink_target->cur_content->location);
+ else
+ set_num_733(bp+37,
+ file->cur_content->location);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "SL" System Use Entry. */
+ if (rr_flag & RR_USE_SL) {
+ /*
+ * "SL" Format:
+ * e.g. a symbolic name is 'foo/bar'
+ * len ver flg
+ * +----+----+----+----+----+------------+
+ * | 'S'| 'L'| 0F | 01 | 00 | components |
+ * +----+----+----+----+----+-----+------+
+ * 0 1 2 3 4 5 ...|... 15
+ * <----------------- len --------+------>
+ * components : |
+ * cflg clen |
+ * +----+----+----+----+----+ |
+ * | 00 | 03 | 'f'| 'o'| 'o'| <---+
+ * +----+----+----+----+----+ |
+ * 5 6 7 8 9 10 |
+ * cflg clen |
+ * +----+----+----+----+----+ |
+ * | 00 | 03 | 'b'| 'a'| 'r'| <---+
+ * +----+----+----+----+----+
+ * 10 11 12 13 14 15
+ *
+ * - cflg : flag of component
+ * - clen : length of component
+ */
+ const char *sl;
+ char sl_last;
+
+ if (extra_space(&ctl) < 7)
+ bp = extra_next_record(&ctl, 7);
+ sl = file->symlink.s;
+ sl_last = '\0';
+ if (bp != NULL) {
+ bp[1] = 'S';
+ bp[2] = 'L';
+ bp[4] = 1; /* version */
+ }
+ for (;;) {
+ unsigned char *nc, *cf, *cl, cldmy = 0;
+ int sllen, slmax;
+
+ slmax = extra_space(&ctl);
+ if (slmax > 0xff)
+ slmax = 0xff;
+ if (bp != NULL)
+ nc = &bp[6];
+ else
+ nc = NULL;
+ cf = cl = NULL;
+ sllen = 0;
+ while (*sl && sllen + 11 < slmax) {
+ if (sl_last == '\0' && sl[0] == '/') {
+ /*
+ * flg len
+ * +----+----+
+ * | 08 | 00 | ROOT component.
+ * +----+----+ ("/")
+ *
+ * Root component has to appear
+ * at the first component only.
+ */
+ if (nc != NULL) {
+ cf = nc++;
+ *cf = 0x08; /* ROOT */
+ *nc++ = 0;
+ }
+ sllen += 2;
+ sl++;
+ sl_last = '/';
+ cl = NULL;
+ continue;
+ }
+ if (((sl_last == '\0' || sl_last == '/') &&
+ sl[0] == '.' && sl[1] == '.' &&
+ (sl[2] == '/' || sl[2] == '\0')) ||
+ (sl[0] == '/' &&
+ sl[1] == '.' && sl[2] == '.' &&
+ (sl[3] == '/' || sl[3] == '\0'))) {
+ /*
+ * flg len
+ * +----+----+
+ * | 04 | 00 | PARENT component.
+ * +----+----+ ("..")
+ */
+ if (nc != NULL) {
+ cf = nc++;
+ *cf = 0x04; /* PARENT */
+ *nc++ = 0;
+ }
+ sllen += 2;
+ if (sl[0] == '/')
+ sl += 3;/* skip "/.." */
+ else
+ sl += 2;/* skip ".." */
+ sl_last = '.';
+ cl = NULL;
+ continue;
+ }
+ if (((sl_last == '\0' || sl_last == '/') &&
+ sl[0] == '.' &&
+ (sl[1] == '/' || sl[1] == '\0')) ||
+ (sl[0] == '/' && sl[1] == '.' &&
+ (sl[2] == '/' || sl[2] == '\0'))) {
+ /*
+ * flg len
+ * +----+----+
+ * | 02 | 00 | CURRENT component.
+ * +----+----+ (".")
+ */
+ if (nc != NULL) {
+ cf = nc++;
+ *cf = 0x02; /* CURRENT */
+ *nc++ = 0;
+ }
+ sllen += 2;
+ if (sl[0] == '/')
+ sl += 2;/* skip "/." */
+ else
+ sl ++; /* skip "." */
+ sl_last = '.';
+ cl = NULL;
+ continue;
+ }
+ if (sl[0] == '/' || cl == NULL) {
+ if (nc != NULL) {
+ cf = nc++;
+ *cf = 0;
+ cl = nc++;
+ *cl = 0;
+ } else
+ cl = &cldmy;
+ sllen += 2;
+ if (sl[0] == '/') {
+ sl_last = *sl++;
+ continue;
+ }
+ }
+ sl_last = *sl++;
+ if (nc != NULL) {
+ *nc++ = sl_last;
+ (*cl) ++;
+ }
+ sllen++;
+ }
+ if (*sl) {
+ length = 5 + sllen;
+ if (bp != NULL) {
+ /*
+ * Mark flg as CONTINUE component.
+ */
+ *cf |= 0x01;
+ /*
+ * len ver flg
+ * +----+----+----+----+----+-
+ * | 'S'| 'L'| XX | 01 | 01 |
+ * +----+----+----+----+----+-
+ * ^
+ * continues in next "SL"
+ */
+ bp[3] = length;
+ bp[5] = 0x01;/* This Symbolic Link
+ * continues in next
+ * "SL" field */
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ if (extra_space(&ctl) < 11)
+ bp = extra_next_record(&ctl, 11);
+ if (bp != NULL) {
+ /* Next 'SL' */
+ bp[1] = 'S';
+ bp[2] = 'L';
+ bp[4] = 1; /* version */
+ }
+ } else {
+ length = 5 + sllen;
+ if (bp != NULL) {
+ bp[3] = length;
+ bp[5] = 0;
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ break;
+ }
+ }
+ }
+
+ /* Write "TF" System Use Entry. */
+ if (rr_flag & RR_USE_TF) {
+ /*
+ * "TF" Format:
+ * len ver
+ * +----+----+----+----+-----+-------------+
+ * | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS |
+ * +----+----+----+----+-----+-------------+
+ * 0 1 2 3 4 5 XX
+ * TIME STAMPS : ISO 9660 Standard 9.1.5.
+ * If TF_LONG_FORM FLAGS is set,
+ * use ISO9660 Standard 8.4.26.1.
+ */
+#define TF_CREATION 0x01 /* Creation time recorded */
+#define TF_MODIFY 0x02 /* Modification time recorded */
+#define TF_ACCESS 0x04 /* Last Access time recorded */
+#define TF_ATTRIBUTES 0x08 /* Last Attribute Change time recorded */
+#define TF_BACKUP 0x10 /* Last Backup time recorded */
+#define TF_EXPIRATION 0x20 /* Expiration time recorded */
+#define TF_EFFECTIVE 0x40 /* Effective time recorded */
+#define TF_LONG_FORM 0x80 /* ISO 9660 17-byte time format used */
+ unsigned char tf_flags;
+
+ length = 5;
+ tf_flags = 0;
+#ifndef COMPAT_MKISOFS
+ if (archive_entry_birthtime_is_set(file->entry) &&
+ archive_entry_birthtime(file->entry) <=
+ archive_entry_mtime(file->entry)) {
+ length += 7;
+ tf_flags |= TF_CREATION;
+ }
+#endif
+ if (archive_entry_mtime_is_set(file->entry)) {
+ length += 7;
+ tf_flags |= TF_MODIFY;
+ }
+ if (archive_entry_atime_is_set(file->entry)) {
+ length += 7;
+ tf_flags |= TF_ACCESS;
+ }
+ if (archive_entry_ctime_is_set(file->entry)) {
+ length += 7;
+ tf_flags |= TF_ATTRIBUTES;
+ }
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'T';
+ bp[2] = 'F';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp[5] = tf_flags;
+ bp += 5;
+ /* Creation time */
+ if (tf_flags & TF_CREATION) {
+ set_time_915(bp+1,
+ archive_entry_birthtime(file->entry));
+ bp += 7;
+ }
+ /* Modification time */
+ if (tf_flags & TF_MODIFY) {
+ set_time_915(bp+1,
+ archive_entry_mtime(file->entry));
+ bp += 7;
+ }
+ /* Last Access time */
+ if (tf_flags & TF_ACCESS) {
+ set_time_915(bp+1,
+ archive_entry_atime(file->entry));
+ bp += 7;
+ }
+ /* Last Attribute Change time */
+ if (tf_flags & TF_ATTRIBUTES) {
+ set_time_915(bp+1,
+ archive_entry_ctime(file->entry));
+ bp += 7;
+ }
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "RE" System Use Entry. */
+ if (rr_flag & RR_USE_RE) {
+ /*
+ * "RE" Format:
+ * len ver
+ * +----+----+----+----+
+ * | 'R'| 'E'| 04 | 01 |
+ * +----+----+----+----+
+ * 0 1 2 3 4
+ */
+ length = 4;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'R';
+ bp[2] = 'E';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "PL" System Use Entry. */
+ if (rr_flag & RR_USE_PL) {
+ /*
+ * "PL" Format:
+ * len ver
+ * +----+----+----+----+------------+
+ * | 'P'| 'L'| 0C | 01 | *LOCATION |
+ * +----+----+----+----+------------+
+ * 0 1 2 3 4 12
+ * *LOCATION: location of parent directory
+ */
+ length = 12;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'P';
+ bp[2] = 'L';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ set_num_733(bp + 5,
+ rr_parent->dir_location);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "CL" System Use Entry. */
+ if (rr_flag & RR_USE_CL) {
+ /*
+ * "CL" Format:
+ * len ver
+ * +----+----+----+----+------------+
+ * | 'C'| 'L'| 0C | 01 | *LOCATION |
+ * +----+----+----+----+------------+
+ * 0 1 2 3 4 12
+ * *LOCATION: location of child directory
+ */
+ length = 12;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'C';
+ bp[2] = 'L';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ set_num_733(bp + 5,
+ isoent->rr_child->dir_location);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "PN" System Use Entry. */
+ if (rr_flag & RR_USE_PN) {
+ /*
+ * "PN" Format:
+ * len ver
+ * +----+----+----+----+------------+------------+
+ * | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low |
+ * +----+----+----+----+------------+------------+
+ * 0 1 2 3 4 12 20
+ */
+ length = 20;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ uint64_t dev;
+
+ bp[1] = 'P';
+ bp[2] = 'N';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ dev = (uint64_t)archive_entry_rdev(file->entry);
+ set_num_733(bp + 5, (uint32_t)(dev >> 32));
+ set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF));
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "ZF" System Use Entry. */
+ if (file->zisofs.header_size) {
+ /*
+ * "ZF" Format:
+ * len ver
+ * +----+----+----+----+----+----+-------------+
+ * | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size |
+ * +----+----+----+----+----+----+-------------+
+ * 0 1 2 3 4 5 6 7
+ * +--------------------+-------------------+
+ * | Log2 of block Size | Uncompressed Size |
+ * +--------------------+-------------------+
+ * 7 8 16
+ */
+ length = 16;
+ if (extra_space(&ctl) < length)
+ bp = extra_next_record(&ctl, length);
+ if (bp != NULL) {
+ bp[1] = 'Z';
+ bp[2] = 'F';
+ bp[3] = length;
+ bp[4] = 1; /* version */
+ bp[5] = 'p';
+ bp[6] = 'z';
+ bp[7] = file->zisofs.header_size;
+ bp[8] = file->zisofs.log2_bs;
+ set_num_733(bp + 9, file->zisofs.uncompressed_size);
+ bp += length;
+ }
+ extra_tell_used_size(&ctl, length);
+ }
+
+ /* Write "CE" System Use Entry. */
+ if (t == DIR_REC_SELF && isoent == isoent->parent) {
+ length = RR_CE_SIZE;
+ if (bp != NULL)
+ set_SUSP_CE(bp+1, iso9660->location_rrip_er,
+ 0, RRIP_ER_SIZE);
+ extra_tell_used_size(&ctl, length);
+ }
+
+ extra_close_record(&ctl, 0);
+
+ return (ctl.dr_len);
+}
+
+/*
+ * Write data of a Directory Record or calculate writing bytes itself.
+ * If parameter `p' is NULL, calculates the size of writing data, which
+ * a Directory Record needs to write, then it saved and return
+ * the calculated size.
+ * Parameter `n' is a remaining size of buffer. when parameter `p' is
+ * not NULL, check whether that `n' is not less than the saved size.
+ * if that `n' is small, return zero.
+ *
+ * This format of the Directory Record is according to
+ * ISO9660 Standard 9.1
+ */
+static int
+set_directory_record(unsigned char *p, size_t n, struct isoent *isoent,
+ struct iso9660 *iso9660, enum dir_rec_type t,
+ enum vdd_type vdd_type)
+{
+ unsigned char *bp;
+ size_t dr_len;
+ size_t fi_len;
+
+ if (p != NULL) {
+ /*
+ * Check whether a write buffer size is less than the
+ * saved size which is needed to write this Directory
+ * Record.
+ */
+ switch (t) {
+ case DIR_REC_VD:
+ dr_len = isoent->dr_len.vd; break;
+ case DIR_REC_SELF:
+ dr_len = isoent->dr_len.self; break;
+ case DIR_REC_PARENT:
+ dr_len = isoent->dr_len.parent; break;
+ case DIR_REC_NORMAL:
+ default:
+ dr_len = isoent->dr_len.normal; break;
+ }
+ if (dr_len > n)
+ return (0);/* Needs more buffer size. */
+ }
+
+ if (t == DIR_REC_NORMAL && isoent->identifier != NULL)
+ fi_len = isoent->id_len;
+ else
+ fi_len = 1;
+
+ if (p != NULL) {
+ struct isoent *xisoent;
+ struct isofile *file;
+ unsigned char flag;
+
+ if (t == DIR_REC_PARENT)
+ xisoent = isoent->parent;
+ else
+ xisoent = isoent;
+ file = isoent->file;
+ if (file->hardlink_target != NULL)
+ file = file->hardlink_target;
+ /* Make a file flag. */
+ if (xisoent->dir)
+ flag = FILE_FLAG_DIRECTORY;
+ else {
+ if (file->cur_content->next != NULL)
+ flag = FILE_FLAG_MULTI_EXTENT;
+ else
+ flag = 0;
+ }
+
+ bp = p -1;
+ /* Extended Attribute Record Length */
+ set_num_711(bp+2, 0);
+ /* Location of Extent */
+ if (xisoent->dir)
+ set_num_733(bp+3, xisoent->dir_location);
+ else
+ set_num_733(bp+3, file->cur_content->location);
+ /* Data Length */
+ if (xisoent->dir)
+ set_num_733(bp+11,
+ xisoent->dir_block * LOGICAL_BLOCK_SIZE);
+ else
+ set_num_733(bp+11, (uint32_t)file->cur_content->size);
+ /* Recording Date and Time */
+ /* NOTE:
+ * If a file type is symbolic link, you are seeing this
+ * field value is different from a value mkisofs makes.
+ * libarchive uses lstat to get this one, but it
+ * seems mkisofs uses stat to get.
+ */
+ set_time_915(bp+19,
+ archive_entry_mtime(xisoent->file->entry));
+ /* File Flags */
+ bp[26] = flag;
+ /* File Unit Size */
+ set_num_711(bp+27, 0);
+ /* Interleave Gap Size */
+ set_num_711(bp+28, 0);
+ /* Volume Sequence Number */
+ set_num_723(bp+29, iso9660->volume_sequence_number);
+ /* Length of File Identifier */
+ set_num_711(bp+33, (unsigned char)fi_len);
+ /* File Identifier */
+ switch (t) {
+ case DIR_REC_VD:
+ case DIR_REC_SELF:
+ set_num_711(bp+34, 0);
+ break;
+ case DIR_REC_PARENT:
+ set_num_711(bp+34, 1);
+ break;
+ case DIR_REC_NORMAL:
+ if (isoent->identifier != NULL)
+ memcpy(bp+34, isoent->identifier, fi_len);
+ else
+ set_num_711(bp+34, 0);
+ break;
+ }
+ } else
+ bp = NULL;
+ dr_len = 33 + fi_len;
+ /* Padding Field */
+ if (dr_len & 0x01) {
+ dr_len ++;
+ if (p != NULL)
+ bp[dr_len] = 0;
+ }
+
+ /* Volume Descriptor does not record extension. */
+ if (t == DIR_REC_VD) {
+ if (p != NULL)
+ /* Length of Directory Record */
+ set_num_711(p, (unsigned char)dr_len);
+ else
+ isoent->dr_len.vd = (int)dr_len;
+ return ((int)dr_len);
+ }
+
+ /* Rockridge */
+ if (iso9660->opt.rr && vdd_type != VDD_JOLIET)
+ dr_len = set_directory_record_rr(bp, (int)dr_len,
+ isoent, iso9660, t);
+
+ if (p != NULL)
+ /* Length of Directory Record */
+ set_num_711(p, (unsigned char)dr_len);
+ else {
+ /*
+ * Save the size which is needed to write this
+ * Directory Record.
+ */
+ switch (t) {
+ case DIR_REC_VD:
+ /* This case does not come, but compiler
+ * complains that DIR_REC_VD not handled
+ * in switch .... */
+ break;
+ case DIR_REC_SELF:
+ isoent->dr_len.self = (int)dr_len; break;
+ case DIR_REC_PARENT:
+ isoent->dr_len.parent = (int)dr_len; break;
+ case DIR_REC_NORMAL:
+ isoent->dr_len.normal = (int)dr_len; break;
+ }
+ }
+
+ return ((int)dr_len);
+}
+
+/*
+ * Calculate the size of a directory record.
+ */
+static inline int
+get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent,
+ enum dir_rec_type t, enum vdd_type vdd_type)
+{
+
+ return (set_directory_record(NULL, SIZE_MAX,
+ isoent, iso9660, t, vdd_type));
+}
+
+/*
+ * Manage to write ISO-image data with wbuff to reduce calling
+ * __archive_write_output() for performance.
+ */
+
+
+static inline unsigned char *
+wb_buffptr(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+
+ return (&(iso9660->wbuff[sizeof(iso9660->wbuff)
+ - iso9660->wbuff_remaining]));
+}
+
+static int
+wb_write_out(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ size_t wsize, nw;
+ int r;
+
+ wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;
+ nw = wsize % LOGICAL_BLOCK_SIZE;
+ if (iso9660->wbuff_type == WB_TO_STREAM)
+ r = __archive_write_output(a, iso9660->wbuff, wsize - nw);
+ else
+ r = write_to_temp(a, iso9660->wbuff, wsize - nw);
+ /* Increase the offset. */
+ iso9660->wbuff_offset += wsize - nw;
+ if (iso9660->wbuff_offset > iso9660->wbuff_written)
+ iso9660->wbuff_written = iso9660->wbuff_offset;
+ iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+ if (nw) {
+ iso9660->wbuff_remaining -= nw;
+ memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw);
+ }
+ return (r);
+}
+
+static int
+wb_consume(struct archive_write *a, size_t size)
+{
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+
+ if (size > iso9660->wbuff_remaining ||
+ iso9660->wbuff_remaining == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal Programming error: iso9660:wb_consume()"
+ " size=%jd, wbuff_remaining=%jd",
+ (intmax_t)size, (intmax_t)iso9660->wbuff_remaining);
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->wbuff_remaining -= size;
+ if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE)
+ return (wb_write_out(a));
+ return (ARCHIVE_OK);
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+wb_set_offset(struct archive_write *a, int64_t off)
+{
+ struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+ int64_t used, ext_bytes;
+
+ if (iso9660->wbuff_type != WB_TO_TEMP) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal Programming error: iso9660:wb_set_offset()");
+ return (ARCHIVE_FATAL);
+ }
+
+ used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;
+ if (iso9660->wbuff_offset + used > iso9660->wbuff_tail)
+ iso9660->wbuff_tail = iso9660->wbuff_offset + used;
+ if (iso9660->wbuff_offset < iso9660->wbuff_written) {
+ if (used > 0 &&
+ write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->wbuff_offset = iso9660->wbuff_written;
+ lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET);
+ iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+ used = 0;
+ }
+ if (off < iso9660->wbuff_offset) {
+ /*
+ * Write out waiting data.
+ */
+ if (used > 0) {
+ if (wb_write_out(a) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ lseek(iso9660->temp_fd, off, SEEK_SET);
+ iso9660->wbuff_offset = off;
+ iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+ } else if (off <= iso9660->wbuff_tail) {
+ iso9660->wbuff_remaining = (size_t)
+ (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset));
+ } else {
+ ext_bytes = off - iso9660->wbuff_tail;
+ iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff)
+ - (iso9660->wbuff_tail - iso9660->wbuff_offset));
+ while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) {
+ if (write_null(a, (size_t)iso9660->wbuff_remaining)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ ext_bytes -= iso9660->wbuff_remaining;
+ }
+ if (ext_bytes > 0) {
+ if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+write_null(struct archive_write *a, size_t size)
+{
+ size_t remaining;
+ unsigned char *p, *old;
+ int r;
+
+ remaining = wb_remaining(a);
+ p = wb_buffptr(a);
+ if (size <= remaining) {
+ memset(p, 0, size);
+ return (wb_consume(a, size));
+ }
+ memset(p, 0, remaining);
+ r = wb_consume(a, remaining);
+ if (r != ARCHIVE_OK)
+ return (r);
+ size -= remaining;
+ old = p;
+ p = wb_buffptr(a);
+ memset(p, 0, old - p);
+ remaining = wb_remaining(a);
+ while (size) {
+ size_t wsize = size;
+
+ if (wsize > remaining)
+ wsize = remaining;
+ r = wb_consume(a, wsize);
+ if (r != ARCHIVE_OK)
+ return (r);
+ size -= wsize;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write Volume Descriptor Set Terminator
+ */
+static int
+write_VD_terminator(struct archive_write *a)
+{
+ unsigned char *bp;
+
+ bp = wb_buffptr(a) -1;
+ set_VD_bp(bp, VDT_TERMINATOR, 1);
+ set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc,
+ struct archive_write *a, struct vdd *vdd, struct archive_string *id,
+ const char *label, int leading_under, enum char_type char_type)
+{
+ char identifier[256];
+ struct isoent *isoent;
+ const char *ids;
+ size_t len;
+ int r;
+
+ if (id->length > 0 && leading_under && id->s[0] != '_') {
+ if (char_type == A_CHAR)
+ r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc);
+ else
+ r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc);
+ } else if (id->length > 0) {
+ ids = id->s;
+ if (leading_under)
+ ids++;
+ isoent = isoent_find_entry(vdd->rootent, ids);
+ if (isoent == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Not Found %s `%s'.",
+ label, ids);
+ return (ARCHIVE_FATAL);
+ }
+ len = isoent->ext_off + isoent->ext_len;
+ if (vdd->vdd_type == VDD_JOLIET) {
+ if (len > sizeof(identifier)-2)
+ len = sizeof(identifier)-2;
+ } else {
+ if (len > sizeof(identifier)-1)
+ len = sizeof(identifier)-1;
+ }
+ memcpy(identifier, isoent->identifier, len);
+ identifier[len] = '\0';
+ if (vdd->vdd_type == VDD_JOLIET) {
+ identifier[len+1] = 0;
+ vdc = VDC_UCS2_DIRECT;
+ }
+ if (char_type == A_CHAR)
+ r = set_str_a_characters_bp(a, bp, from, to,
+ identifier, vdc);
+ else
+ r = set_str_d_characters_bp(a, bp, from, to,
+ identifier, vdc);
+ } else {
+ if (char_type == A_CHAR)
+ r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc);
+ else
+ r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc);
+ }
+ return (r);
+}
+
+/*
+ * Write Primary/Supplementary Volume Descriptor
+ */
+static int
+write_VD(struct archive_write *a, struct vdd *vdd)
+{
+ struct iso9660 *iso9660;
+ unsigned char *bp;
+ uint16_t volume_set_size = 1;
+ char identifier[256];
+ enum VD_type vdt;
+ enum vdc vdc;
+ unsigned char vd_ver, fst_ver;
+ int r;
+
+ iso9660 = a->format_data;
+ switch (vdd->vdd_type) {
+ case VDD_JOLIET:
+ vdt = VDT_SUPPLEMENTARY;
+ vd_ver = fst_ver = 1;
+ vdc = VDC_UCS2;
+ break;
+ case VDD_ENHANCED:
+ vdt = VDT_SUPPLEMENTARY;
+ vd_ver = fst_ver = 2;
+ vdc = VDC_LOWERCASE;
+ break;
+ case VDD_PRIMARY:
+ default:
+ vdt = VDT_PRIMARY;
+ vd_ver = fst_ver = 1;
+#ifdef COMPAT_MKISOFS
+ vdc = VDC_LOWERCASE;
+#else
+ vdc = VDC_STD;
+#endif
+ break;
+ }
+
+ bp = wb_buffptr(a) -1;
+ /* Volume Descriptor Type */
+ set_VD_bp(bp, vdt, vd_ver);
+ /* Unused Field */
+ set_unused_field_bp(bp, 8, 8);
+ /* System Identifier */
+ get_system_identitier(identifier, sizeof(identifier));
+ r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Volume Identifier */
+ r = set_str_d_characters_bp(a, bp, 41, 72,
+ iso9660->volume_identifier.s, vdc);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Unused Field */
+ set_unused_field_bp(bp, 73, 80);
+ /* Volume Space Size */
+ set_num_733(bp+81, iso9660->volume_space_size);
+ if (vdd->vdd_type == VDD_JOLIET) {
+ /* Escape Sequences */
+ bp[89] = 0x25;/* UCS-2 Level 3 */
+ bp[90] = 0x2F;
+ bp[91] = 0x45;
+ memset(bp + 92, 0, 120 - 92 + 1);
+ } else {
+ /* Unused Field */
+ set_unused_field_bp(bp, 89, 120);
+ }
+ /* Volume Set Size */
+ set_num_723(bp+121, volume_set_size);
+ /* Volume Sequence Number */
+ set_num_723(bp+125, iso9660->volume_sequence_number);
+ /* Logical Block Size */
+ set_num_723(bp+129, LOGICAL_BLOCK_SIZE);
+ /* Path Table Size */
+ set_num_733(bp+133, vdd->path_table_size);
+ /* Location of Occurrence of Type L Path Table */
+ set_num_731(bp+141, vdd->location_type_L_path_table);
+ /* Location of Optional Occurrence of Type L Path Table */
+ set_num_731(bp+145, 0);
+ /* Location of Occurrence of Type M Path Table */
+ set_num_732(bp+149, vdd->location_type_M_path_table);
+ /* Location of Optional Occurrence of Type M Path Table */
+ set_num_732(bp+153, 0);
+ /* Directory Record for Root Directory(BP 157 to 190) */
+ set_directory_record(bp+157, 190-157+1, vdd->rootent,
+ iso9660, DIR_REC_VD, vdd->vdd_type);
+ /* Volume Set Identifier */
+ r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Publisher Identifier */
+ r = set_file_identifier(bp, 319, 446, vdc, a, vdd,
+ &(iso9660->publisher_identifier),
+ "Publisher File", 1, A_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Data Preparer Identifier */
+ r = set_file_identifier(bp, 447, 574, vdc, a, vdd,
+ &(iso9660->data_preparer_identifier),
+ "Data Preparer File", 1, A_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Application Identifier */
+ r = set_file_identifier(bp, 575, 702, vdc, a, vdd,
+ &(iso9660->application_identifier),
+ "Application File", 1, A_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Copyright File Identifier */
+ r = set_file_identifier(bp, 703, 739, vdc, a, vdd,
+ &(iso9660->copyright_file_identifier),
+ "Copyright File", 0, D_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Abstract File Identifier */
+ r = set_file_identifier(bp, 740, 776, vdc, a, vdd,
+ &(iso9660->abstract_file_identifier),
+ "Abstract File", 0, D_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Bibliographic File Identifier */
+ r = set_file_identifier(bp, 777, 813, vdc, a, vdd,
+ &(iso9660->bibliographic_file_identifier),
+ "Bibliongraphic File", 0, D_CHAR);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* Volume Creation Date and Time */
+ set_date_time(bp+814, iso9660->birth_time);
+ /* Volume Modification Date and Time */
+ set_date_time(bp+831, iso9660->birth_time);
+ /* Volume Expiration Date and Time(obsolete) */
+ set_date_time_null(bp+848);
+ /* Volume Effective Date and Time */
+ set_date_time(bp+865, iso9660->birth_time);
+ /* File Structure Version */
+ bp[882] = fst_ver;
+ /* Reserved */
+ bp[883] = 0;
+ /* Application Use */
+ memset(bp + 884, 0x20, 1395 - 884 + 1);
+ /* Reserved */
+ set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+/*
+ * Write Boot Record Volume Descriptor
+ */
+static int
+write_VD_boot_record(struct archive_write *a)
+{
+ struct iso9660 *iso9660;
+ unsigned char *bp;
+
+ iso9660 = a->format_data;
+ bp = wb_buffptr(a) -1;
+ /* Volume Descriptor Type */
+ set_VD_bp(bp, VDT_BOOT_RECORD, 1);
+ /* Boot System Identifier */
+ memcpy(bp+8, "EL TORITO SPECIFICATION", 23);
+ set_unused_field_bp(bp, 8+23, 39);
+ /* Unused */
+ set_unused_field_bp(bp, 40, 71);
+ /* Absolute pointer to first sector of Boot Catalog */
+ set_num_731(bp+72,
+ iso9660->el_torito.catalog->file->content.location);
+ /* Unused */
+ set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+enum keytype {
+ KEY_FLG,
+ KEY_STR,
+ KEY_INT,
+ KEY_HEX
+};
+static void
+set_option_info(struct archive_string *info, int *opt, const char *key,
+ enum keytype type, ...)
+{
+ va_list ap;
+ char prefix;
+ const char *s;
+ int d;
+
+ prefix = (*opt==0)? ' ':',';
+ va_start(ap, type);
+ switch (type) {
+ case KEY_FLG:
+ d = va_arg(ap, int);
+ archive_string_sprintf(info, "%c%s%s",
+ prefix, (d == 0)?"!":"", key);
+ break;
+ case KEY_STR:
+ s = va_arg(ap, const char *);
+ archive_string_sprintf(info, "%c%s=%s",
+ prefix, key, s);
+ break;
+ case KEY_INT:
+ d = va_arg(ap, int);
+ archive_string_sprintf(info, "%c%s=%d",
+ prefix, key, d);
+ break;
+ case KEY_HEX:
+ d = va_arg(ap, int);
+ archive_string_sprintf(info, "%c%s=%x",
+ prefix, key, d);
+ break;
+ }
+ va_end(ap);
+
+ *opt = 1;
+}
+
+/*
+ * Make Non-ISO File System Information
+ */
+static int
+write_information_block(struct archive_write *a)
+{
+ struct iso9660 *iso9660;
+ char buf[128];
+ const char *v;
+ int opt, r;
+ struct archive_string info;
+ size_t info_size = LOGICAL_BLOCK_SIZE *
+ NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
+
+ iso9660 = (struct iso9660 *)a->format_data;
+ if (info_size > wb_remaining(a)) {
+ r = wb_write_out(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ archive_string_init(&info);
+ if (archive_string_ensure(&info, info_size) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ memset(info.s, 0, info_size);
+ opt = 0;
+#if defined(HAVE_CTIME_S)
+ ctime_s(buf, sizeof(buf), &(iso9660->birth_time));
+#elif defined(HAVE_CTIME_R)
+ ctime_r(&(iso9660->birth_time), buf);
+#else
+ strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+#endif
+ archive_string_sprintf(&info,
+ "INFO %s%s", buf, archive_version_string());
+ if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT)
+ set_option_info(&info, &opt, "abstract-file",
+ KEY_STR, iso9660->abstract_file_identifier.s);
+ if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT)
+ set_option_info(&info, &opt, "application-id",
+ KEY_STR, iso9660->application_identifier.s);
+ if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT)
+ set_option_info(&info, &opt, "allow-vernum",
+ KEY_FLG, iso9660->opt.allow_vernum);
+ if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT)
+ set_option_info(&info, &opt, "biblio-file",
+ KEY_STR, iso9660->bibliographic_file_identifier.s);
+ if (iso9660->opt.boot != OPT_BOOT_DEFAULT)
+ set_option_info(&info, &opt, "boot",
+ KEY_STR, iso9660->el_torito.boot_filename.s);
+ if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT)
+ set_option_info(&info, &opt, "boot-catalog",
+ KEY_STR, iso9660->el_torito.catalog_filename.s);
+ if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT)
+ set_option_info(&info, &opt, "boot-info-table",
+ KEY_FLG, iso9660->opt.boot_info_table);
+ if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT)
+ set_option_info(&info, &opt, "boot-load-seg",
+ KEY_HEX, iso9660->el_torito.boot_load_seg);
+ if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT)
+ set_option_info(&info, &opt, "boot-load-size",
+ KEY_INT, iso9660->el_torito.boot_load_size);
+ if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) {
+ v = "no-emulation";
+ if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD)
+ v = "fd";
+ if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK)
+ v = "hard-disk";
+ set_option_info(&info, &opt, "boot-type",
+ KEY_STR, v);
+ }
+#ifdef HAVE_ZLIB_H
+ if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT)
+ set_option_info(&info, &opt, "compression-level",
+ KEY_INT, iso9660->zisofs.compression_level);
+#endif
+ if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT)
+ set_option_info(&info, &opt, "copyright-file",
+ KEY_STR, iso9660->copyright_file_identifier.s);
+ if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT)
+ set_option_info(&info, &opt, "iso-level",
+ KEY_INT, iso9660->opt.iso_level);
+ if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) {
+ if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
+ set_option_info(&info, &opt, "joliet",
+ KEY_STR, "long");
+ else
+ set_option_info(&info, &opt, "joliet",
+ KEY_FLG, iso9660->opt.joliet);
+ }
+ if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT)
+ set_option_info(&info, &opt, "limit-depth",
+ KEY_FLG, iso9660->opt.limit_depth);
+ if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT)
+ set_option_info(&info, &opt, "limit-dirs",
+ KEY_FLG, iso9660->opt.limit_dirs);
+ if (iso9660->opt.pad != OPT_PAD_DEFAULT)
+ set_option_info(&info, &opt, "pad",
+ KEY_FLG, iso9660->opt.pad);
+ if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT)
+ set_option_info(&info, &opt, "publisher",
+ KEY_STR, iso9660->publisher_identifier.s);
+ if (iso9660->opt.rr != OPT_RR_DEFAULT) {
+ if (iso9660->opt.rr == OPT_RR_DISABLED)
+ set_option_info(&info, &opt, "rockridge",
+ KEY_FLG, iso9660->opt.rr);
+ else if (iso9660->opt.rr == OPT_RR_STRICT)
+ set_option_info(&info, &opt, "rockridge",
+ KEY_STR, "strict");
+ else if (iso9660->opt.rr == OPT_RR_USEFUL)
+ set_option_info(&info, &opt, "rockridge",
+ KEY_STR, "useful");
+ }
+ if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT)
+ set_option_info(&info, &opt, "volume-id",
+ KEY_STR, iso9660->volume_identifier.s);
+ if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT)
+ set_option_info(&info, &opt, "zisofs",
+ KEY_FLG, iso9660->opt.zisofs);
+
+ memcpy(wb_buffptr(a), info.s, info_size);
+ archive_string_free(&info);
+ return (wb_consume(a, info_size));
+}
+
+static int
+write_rr_ER(struct archive_write *a)
+{
+ unsigned char *p;
+
+ p = wb_buffptr(a);
+
+ memset(p, 0, LOGICAL_BLOCK_SIZE);
+ p[0] = 'E';
+ p[1] = 'R';
+ p[3] = 0x01;
+ p[2] = RRIP_ER_SIZE;
+ p[4] = RRIP_ER_ID_SIZE;
+ p[5] = RRIP_ER_DSC_SIZE;
+ p[6] = RRIP_ER_SRC_SIZE;
+ p[7] = 0x01;
+ memcpy(&p[8], rrip_identifier, p[4]);
+ memcpy(&p[8+p[4]], rrip_descriptor, p[5]);
+ memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static void
+calculate_path_table_size(struct vdd *vdd)
+{
+ int depth, size;
+ struct path_table *pt;
+
+ pt = vdd->pathtbl;
+ size = 0;
+ for (depth = 0; depth < vdd->max_depth; depth++) {
+ struct isoent **ptbl;
+ int i, cnt;
+
+ if ((cnt = pt[depth].cnt) == 0)
+ break;
+
+ ptbl = pt[depth].sorted;
+ for (i = 0; i < cnt; i++) {
+ int len;
+
+ if (ptbl[i]->identifier == NULL)
+ len = 1; /* root directory */
+ else
+ len = ptbl[i]->id_len;
+ if (len & 0x01)
+ len++; /* Padding Field */
+ size += 8 + len;
+ }
+ }
+ vdd->path_table_size = size;
+ vdd->path_table_block =
+ ((size + PATH_TABLE_BLOCK_SIZE -1) /
+ PATH_TABLE_BLOCK_SIZE) *
+ (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE);
+}
+
+static int
+_write_path_table(struct archive_write *a, int type_m, int depth,
+ struct vdd *vdd)
+{
+ unsigned char *bp, *wb;
+ struct isoent **ptbl;
+ size_t wbremaining;
+ int i, r, wsize;
+
+ if (vdd->pathtbl[depth].cnt == 0)
+ return (0);
+
+ wsize = 0;
+ wb = wb_buffptr(a);
+ wbremaining = wb_remaining(a);
+ bp = wb - 1;
+ ptbl = vdd->pathtbl[depth].sorted;
+ for (i = 0; i < vdd->pathtbl[depth].cnt; i++) {
+ struct isoent *np;
+ size_t len;
+
+ np = ptbl[i];
+ if (np->identifier == NULL)
+ len = 1; /* root directory */
+ else
+ len = np->id_len;
+ if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) {
+ r = wb_consume(a, (bp+1) - wb);
+ if (r < 0)
+ return (r);
+ wb = wb_buffptr(a);
+ wbremaining = wb_remaining(a);
+ bp = wb -1;
+ }
+ /* Length of Directory Identifier */
+ set_num_711(bp+1, (unsigned char)len);
+ /* Extended Attribute Record Length */
+ set_num_711(bp+2, 0);
+ /* Location of Extent */
+ if (type_m)
+ set_num_732(bp+3, np->dir_location);
+ else
+ set_num_731(bp+3, np->dir_location);
+ /* Parent Directory Number */
+ if (type_m)
+ set_num_722(bp+7, np->parent->dir_number);
+ else
+ set_num_721(bp+7, np->parent->dir_number);
+ /* Directory Identifier */
+ if (np->identifier == NULL)
+ bp[9] = 0;
+ else
+ memcpy(&bp[9], np->identifier, len);
+ if (len & 0x01) {
+ /* Padding Field */
+ bp[9+len] = 0;
+ len++;
+ }
+ wsize += 8 + (int)len;
+ bp += 8 + len;
+ }
+ if ((bp + 1) > wb) {
+ r = wb_consume(a, (bp+1)-wb);
+ if (r < 0)
+ return (r);
+ }
+ return (wsize);
+}
+
+static int
+write_path_table(struct archive_write *a, int type_m, struct vdd *vdd)
+{
+ int depth, r;
+ size_t path_table_size;
+
+ r = ARCHIVE_OK;
+ path_table_size = 0;
+ for (depth = 0; depth < vdd->max_depth; depth++) {
+ r = _write_path_table(a, type_m, depth, vdd);
+ if (r < 0)
+ return (r);
+ path_table_size += r;
+ }
+
+ /* Write padding data. */
+ path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE;
+ if (path_table_size > 0)
+ r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size);
+ return (r);
+}
+
+static int
+calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd,
+ struct isoent *isoent, int depth)
+{
+ struct isoent **enttbl;
+ int bs, block, i;
+
+ block = 1;
+ bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type);
+ bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type);
+
+ if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&
+ !iso9660->opt.rr && depth + 1 >= vdd->max_depth))
+ return (block);
+
+ enttbl = isoent->children_sorted;
+ for (i = 0; i < isoent->children.cnt; i++) {
+ struct isoent *np = enttbl[i];
+ struct isofile *file;
+
+ file = np->file;
+ if (file->hardlink_target != NULL)
+ file = file->hardlink_target;
+ file->cur_content = &(file->content);
+ do {
+ int dr_l;
+
+ dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL,
+ vdd->vdd_type);
+ if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) {
+ block ++;
+ bs = dr_l;
+ } else
+ bs += dr_l;
+ file->cur_content = file->cur_content->next;
+ } while (file->cur_content != NULL);
+ }
+ return (block);
+}
+
+static int
+_write_directory_descriptors(struct archive_write *a, struct vdd *vdd,
+ struct isoent *isoent, int depth)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent **enttbl;
+ unsigned char *p, *wb;
+ int i, r;
+ int dr_l;
+
+ p = wb = wb_buffptr(a);
+#define WD_REMAINING (LOGICAL_BLOCK_SIZE - (p - wb))
+ p += set_directory_record(p, WD_REMAINING, isoent,
+ iso9660, DIR_REC_SELF, vdd->vdd_type);
+ p += set_directory_record(p, WD_REMAINING, isoent,
+ iso9660, DIR_REC_PARENT, vdd->vdd_type);
+
+ if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&
+ !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) {
+ memset(p, 0, WD_REMAINING);
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+ }
+
+ enttbl = isoent->children_sorted;
+ for (i = 0; i < isoent->children.cnt; i++) {
+ struct isoent *np = enttbl[i];
+ struct isofile *file = np->file;
+
+ if (file->hardlink_target != NULL)
+ file = file->hardlink_target;
+ file->cur_content = &(file->content);
+ do {
+ dr_l = set_directory_record(p, WD_REMAINING,
+ np, iso9660, DIR_REC_NORMAL,
+ vdd->vdd_type);
+ if (dr_l == 0) {
+ memset(p, 0, WD_REMAINING);
+ r = wb_consume(a, LOGICAL_BLOCK_SIZE);
+ if (r < 0)
+ return (r);
+ p = wb = wb_buffptr(a);
+ dr_l = set_directory_record(p,
+ WD_REMAINING, np, iso9660,
+ DIR_REC_NORMAL, vdd->vdd_type);
+ }
+ p += dr_l;
+ file->cur_content = file->cur_content->next;
+ } while (file->cur_content != NULL);
+ }
+ memset(p, 0, WD_REMAINING);
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+write_directory_descriptors(struct archive_write *a, struct vdd *vdd)
+{
+ struct isoent *np;
+ int depth, r;
+
+ depth = 0;
+ np = vdd->rootent;
+ do {
+ struct extr_rec *extr;
+
+ r = _write_directory_descriptors(a, vdd, np, depth);
+ if (r < 0)
+ return (r);
+ if (vdd->vdd_type != VDD_JOLIET) {
+ /*
+ * This extract record is used by SUSP,RRIP.
+ * Not for joliet.
+ */
+ for (extr = np->extr_rec_list.first;
+ extr != NULL;
+ extr = extr->next) {
+ unsigned char *wb;
+
+ wb = wb_buffptr(a);
+ memcpy(wb, extr->buf, extr->offset);
+ memset(wb + extr->offset, 0,
+ LOGICAL_BLOCK_SIZE - extr->offset);
+ r = wb_consume(a, LOGICAL_BLOCK_SIZE);
+ if (r < 0)
+ return (r);
+ }
+ }
+
+ if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ while (np != np->parent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Read file contents from the temporary file, and write it.
+ */
+static int
+write_file_contents(struct archive_write *a, int64_t offset, int64_t size)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ int r;
+
+ lseek(iso9660->temp_fd, offset, SEEK_SET);
+
+ while (size) {
+ size_t rsize;
+ ssize_t rs;
+ unsigned char *wb;
+
+ wb = wb_buffptr(a);
+ rsize = wb_remaining(a);
+ if (rsize > (size_t)size)
+ rsize = (size_t)size;
+ rs = read(iso9660->temp_fd, wb, rsize);
+ if (rs <= 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't read temporary file(%jd)", (intmax_t)rs);
+ return (ARCHIVE_FATAL);
+ }
+ size -= rs;
+ r = wb_consume(a, rs);
+ if (r < 0)
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+write_file_descriptors(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file;
+ int64_t blocks, offset;
+ int r;
+
+ blocks = 0;
+ offset = 0;
+
+ /* Make the boot catalog contents, and write it. */
+ if (iso9660->el_torito.catalog != NULL) {
+ r = make_boot_catalog(a);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Write the boot file contents. */
+ if (iso9660->el_torito.boot != NULL) {
+ file = iso9660->el_torito.boot->file;
+ blocks = file->content.blocks;
+ offset = file->content.offset_of_temp;
+ if (offset != 0) {
+ r = write_file_contents(a, offset,
+ blocks << LOGICAL_BLOCK_BITS);
+ if (r < 0)
+ return (r);
+ blocks = 0;
+ offset = 0;
+ }
+ }
+
+ /* Write out all file contents. */
+ for (file = iso9660->data_file_list.first;
+ file != NULL; file = file->datanext) {
+
+ if (!file->write_content)
+ continue;
+
+ if ((offset + (blocks << LOGICAL_BLOCK_BITS)) <
+ file->content.offset_of_temp) {
+ if (blocks > 0) {
+ r = write_file_contents(a, offset,
+ blocks << LOGICAL_BLOCK_BITS);
+ if (r < 0)
+ return (r);
+ }
+ blocks = 0;
+ offset = file->content.offset_of_temp;
+ }
+
+ file->cur_content = &(file->content);
+ do {
+ blocks += file->cur_content->blocks;
+ /* Next fragment */
+ file->cur_content = file->cur_content->next;
+ } while (file->cur_content != NULL);
+ }
+
+ /* Flush out remaining blocks. */
+ if (blocks > 0) {
+ r = write_file_contents(a, offset,
+ blocks << LOGICAL_BLOCK_BITS);
+ if (r < 0)
+ return (r);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static void
+isofile_init_entry_list(struct iso9660 *iso9660)
+{
+ iso9660->all_file_list.first = NULL;
+ iso9660->all_file_list.last = &(iso9660->all_file_list.first);
+}
+
+static void
+isofile_add_entry(struct iso9660 *iso9660, struct isofile *file)
+{
+ file->allnext = NULL;
+ *iso9660->all_file_list.last = file;
+ iso9660->all_file_list.last = &(file->allnext);
+}
+
+static void
+isofile_free_all_entries(struct iso9660 *iso9660)
+{
+ struct isofile *file, *file_next;
+
+ file = iso9660->all_file_list.first;
+ while (file != NULL) {
+ file_next = file->allnext;
+ isofile_free(file);
+ file = file_next;
+ }
+}
+
+static void
+isofile_init_entry_data_file_list(struct iso9660 *iso9660)
+{
+ iso9660->data_file_list.first = NULL;
+ iso9660->data_file_list.last = &(iso9660->data_file_list.first);
+}
+
+static void
+isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file)
+{
+ file->datanext = NULL;
+ *iso9660->data_file_list.last = file;
+ iso9660->data_file_list.last = &(file->datanext);
+}
+
+
+static struct isofile *
+isofile_new(struct archive_write *a, struct archive_entry *entry)
+{
+ struct isofile *file;
+
+ file = calloc(1, sizeof(*file));
+ if (file == NULL)
+ return (NULL);
+
+ if (entry != NULL)
+ file->entry = archive_entry_clone(entry);
+ else
+ file->entry = archive_entry_new2(&a->archive);
+ if (file->entry == NULL) {
+ free(file);
+ return (NULL);
+ }
+ archive_string_init(&(file->parentdir));
+ archive_string_init(&(file->basename));
+ archive_string_init(&(file->basename_utf16));
+ archive_string_init(&(file->symlink));
+ file->cur_content = &(file->content);
+
+ return (file);
+}
+
+static void
+isofile_free(struct isofile *file)
+{
+ struct content *con, *tmp;
+
+ con = file->content.next;
+ while (con != NULL) {
+ tmp = con;
+ con = con->next;
+ free(tmp);
+ }
+ archive_entry_free(file->entry);
+ archive_string_free(&(file->parentdir));
+ archive_string_free(&(file->basename));
+ archive_string_free(&(file->basename_utf16));
+ archive_string_free(&(file->symlink));
+ free(file);
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static int
+cleanup_backslash_1(char *p)
+{
+ int mb, dos;
+
+ mb = dos = 0;
+ while (*p) {
+ if (*(unsigned char *)p > 127)
+ mb = 1;
+ if (*p == '\\') {
+ /* If we have not met any multi-byte characters,
+ * we can replace '\' with '/'. */
+ if (!mb)
+ *p = '/';
+ dos = 1;
+ }
+ p++;
+ }
+ if (!mb || !dos)
+ return (0);
+ return (-1);
+}
+
+static void
+cleanup_backslash_2(wchar_t *p)
+{
+
+ /* Convert a path-separator from '\' to '/' */
+ while (*p != L'\0') {
+ if (*p == L'\\')
+ *p = L'/';
+ p++;
+ }
+}
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+isofile_gen_utility_names(struct archive_write *a, struct isofile *file)
+{
+ struct iso9660 *iso9660;
+ const char *pathname;
+ char *p, *dirname, *slash;
+ size_t len;
+ int ret = ARCHIVE_OK;
+
+ iso9660 = a->format_data;
+
+ archive_string_empty(&(file->parentdir));
+ archive_string_empty(&(file->basename));
+ archive_string_empty(&(file->basename_utf16));
+ archive_string_empty(&(file->symlink));
+
+ pathname = archive_entry_pathname(file->entry);
+ if (pathname == NULL || pathname[0] == '\0') {/* virtual root */
+ file->dircnt = 0;
+ return (ret);
+ }
+
+ /*
+ * Make a UTF-16BE basename if Joliet extension enabled.
+ */
+ if (iso9660->opt.joliet) {
+ const char *u16, *ulast;
+ size_t u16len, ulen_last;
+
+ if (iso9660->sconv_to_utf16be == NULL) {
+ iso9660->sconv_to_utf16be =
+ archive_string_conversion_to_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_to_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ iso9660->sconv_from_utf16be =
+ archive_string_conversion_from_charset(
+ &(a->archive), "UTF-16BE", 1);
+ if (iso9660->sconv_from_utf16be == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Convert a filename to UTF-16BE.
+ */
+ if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len,
+ iso9660->sconv_to_utf16be)) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for UTF-16BE");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "A filename cannot be converted to UTF-16BE;"
+ "You should disable making Joliet extension");
+ ret = ARCHIVE_WARN;
+ }
+
+ /*
+ * Make sure a path separator is not in the last;
+ * Remove trailing '/'.
+ */
+ while (u16len >= 2) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (u16[u16len-2] == 0 &&
+ (u16[u16len-1] == '/' || u16[u16len-1] == '\\'))
+#else
+ if (u16[u16len-2] == 0 && u16[u16len-1] == '/')
+#endif
+ {
+ u16len -= 2;
+ } else
+ break;
+ }
+
+ /*
+ * Find a basename in UTF-16BE.
+ */
+ ulast = u16;
+ u16len >>= 1;
+ ulen_last = u16len;
+ while (u16len > 0) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\'))
+#else
+ if (u16[0] == 0 && u16[1] == '/')
+#endif
+ {
+ ulast = u16 + 2;
+ ulen_last = u16len -1;
+ }
+ u16 += 2;
+ u16len --;
+ }
+ ulen_last <<= 1;
+ if (archive_string_ensure(&(file->basename_utf16),
+ ulen_last) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for UTF-16BE");
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Set UTF-16BE basename.
+ */
+ memcpy(file->basename_utf16.s, ulast, ulen_last);
+ file->basename_utf16.length = ulen_last;
+ }
+
+ archive_strcpy(&(file->parentdir), pathname);
+#if defined(_WIN32) || defined(__CYGWIN__)
+ /*
+ * Convert a path-separator from '\' to '/'
+ */
+ if (cleanup_backslash_1(file->parentdir.s) != 0) {
+ const wchar_t *wp = archive_entry_pathname_w(file->entry);
+ struct archive_wstring ws;
+
+ if (wp != NULL) {
+ int r;
+ archive_string_init(&ws);
+ archive_wstrcpy(&ws, wp);
+ cleanup_backslash_2(ws.s);
+ archive_string_empty(&(file->parentdir));
+ r = archive_string_append_from_wcs(&(file->parentdir),
+ ws.s, ws.length);
+ archive_wstring_free(&ws);
+ if (r < 0 && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+#endif
+
+ len = file->parentdir.length;
+ p = dirname = file->parentdir.s;
+
+ /*
+ * Remove leading '/', '../' and './' elements
+ */
+ while (*p) {
+ if (p[0] == '/') {
+ p++;
+ len--;
+ } else if (p[0] != '.')
+ break;
+ else if (p[1] == '.' && p[2] == '/') {
+ p += 3;
+ len -= 3;
+ } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
+ p += 2;
+ len -= 2;
+ } else if (p[1] == '\0') {
+ p++;
+ len--;
+ } else
+ break;
+ }
+ if (p != dirname) {
+ memmove(dirname, p, len+1);
+ p = dirname;
+ }
+ /*
+ * Remove "/","/." and "/.." elements from tail.
+ */
+ while (len > 0) {
+ size_t ll = len;
+
+ if (len > 0 && p[len-1] == '/') {
+ p[len-1] = '\0';
+ len--;
+ }
+ if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+ p[len-2] = '\0';
+ len -= 2;
+ }
+ if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+ p[len-1] == '.') {
+ p[len-3] = '\0';
+ len -= 3;
+ }
+ if (ll == len)
+ break;
+ }
+ while (*p) {
+ if (p[0] == '/') {
+ if (p[1] == '/')
+ /* Convert '//' --> '/' */
+ memmove(p, p+1, strlen(p+1) + 1);
+ else if (p[1] == '.' && p[2] == '/')
+ /* Convert '/./' --> '/' */
+ memmove(p, p+2, strlen(p+2) + 1);
+ else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+ /* Convert 'dir/dir1/../dir2/'
+ * --> 'dir/dir2/'
+ */
+ char *rp = p -1;
+ while (rp >= dirname) {
+ if (*rp == '/')
+ break;
+ --rp;
+ }
+ if (rp > dirname) {
+ strcpy(rp, p+3);
+ p = rp;
+ } else {
+ strcpy(dirname, p+4);
+ p = dirname;
+ }
+ } else
+ p++;
+ } else
+ p++;
+ }
+ p = dirname;
+ len = strlen(p);
+
+ if (archive_entry_filetype(file->entry) == AE_IFLNK) {
+ /* Convert symlink name too. */
+ pathname = archive_entry_symlink(file->entry);
+ archive_strcpy(&(file->symlink), pathname);
+#if defined(_WIN32) || defined(__CYGWIN__)
+ /*
+ * Convert a path-separator from '\' to '/'
+ */
+ if (archive_strlen(&(file->symlink)) > 0 &&
+ cleanup_backslash_1(file->symlink.s) != 0) {
+ const wchar_t *wp =
+ archive_entry_symlink_w(file->entry);
+ struct archive_wstring ws;
+
+ if (wp != NULL) {
+ int r;
+ archive_string_init(&ws);
+ archive_wstrcpy(&ws, wp);
+ cleanup_backslash_2(ws.s);
+ archive_string_empty(&(file->symlink));
+ r = archive_string_append_from_wcs(
+ &(file->symlink),
+ ws.s, ws.length);
+ archive_wstring_free(&ws);
+ if (r < 0 && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+#endif
+ }
+ /*
+ * - Count up directory elements.
+ * - Find out the position which points the last position of
+ * path separator('/').
+ */
+ slash = NULL;
+ file->dircnt = 0;
+ for (; *p != '\0'; p++)
+ if (*p == '/') {
+ slash = p;
+ file->dircnt++;
+ }
+ if (slash == NULL) {
+ /* The pathname doesn't have a parent directory. */
+ file->parentdir.length = len;
+ archive_string_copy(&(file->basename), &(file->parentdir));
+ archive_string_empty(&(file->parentdir));
+ *file->parentdir.s = '\0';
+ return (ret);
+ }
+
+ /* Make a basename from dirname and slash */
+ *slash = '\0';
+ file->parentdir.length = slash - dirname;
+ archive_strcpy(&(file->basename), slash + 1);
+ if (archive_entry_filetype(file->entry) == AE_IFDIR)
+ file->dircnt ++;
+ return (ret);
+}
+
+/*
+ * Register a entry to get a hardlink target.
+ */
+static int
+isofile_register_hardlink(struct archive_write *a, struct isofile *file)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct hardlink *hl;
+ const char *pathname;
+
+ archive_entry_set_nlink(file->entry, 1);
+ pathname = archive_entry_hardlink(file->entry);
+ if (pathname == NULL) {
+ /* This `file` is a hardlink target. */
+ hl = malloc(sizeof(*hl));
+ if (hl == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ hl->nlink = 1;
+ /* A hardlink target must be the first position. */
+ file->hlnext = NULL;
+ hl->file_list.first = file;
+ hl->file_list.last = &(file->hlnext);
+ __archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree),
+ (struct archive_rb_node *)hl);
+ } else {
+ hl = (struct hardlink *)__archive_rb_tree_find_node(
+ &(iso9660->hardlink_rbtree), pathname);
+ if (hl != NULL) {
+ /* Insert `file` entry into the tail. */
+ file->hlnext = NULL;
+ *hl->file_list.last = file;
+ hl->file_list.last = &(file->hlnext);
+ hl->nlink++;
+ }
+ archive_entry_unset_size(file->entry);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Hardlinked files have to have the same location of extent.
+ * We have to find out hardlink target entries for the entries
+ * which have a hardlink target name.
+ */
+static void
+isofile_connect_hardlink_files(struct iso9660 *iso9660)
+{
+ struct archive_rb_node *n;
+ struct hardlink *hl;
+ struct isofile *target, *nf;
+
+ ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) {
+ hl = (struct hardlink *)n;
+
+ /* The first entry must be a hardlink target. */
+ target = hl->file_list.first;
+ archive_entry_set_nlink(target->entry, hl->nlink);
+ /* Set a hardlink target to reference entries. */
+ for (nf = target->hlnext;
+ nf != NULL; nf = nf->hlnext) {
+ nf->hardlink_target = target;
+ archive_entry_set_nlink(nf->entry, hl->nlink);
+ }
+ }
+}
+
+static int
+isofile_hd_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct hardlink *h1 = (const struct hardlink *)n1;
+ const struct hardlink *h2 = (const struct hardlink *)n2;
+
+ return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
+ archive_entry_pathname(h2->file_list.first->entry)));
+}
+
+static int
+isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct hardlink *h = (const struct hardlink *)n;
+
+ return (strcmp(archive_entry_pathname(h->file_list.first->entry),
+ (const char *)key));
+}
+
+static void
+isofile_init_hardlinks(struct iso9660 *iso9660)
+{
+ static const struct archive_rb_tree_ops rb_ops = {
+ isofile_hd_cmp_node, isofile_hd_cmp_key,
+ };
+
+ __archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops);
+}
+
+static void
+isofile_free_hardlinks(struct iso9660 *iso9660)
+{
+ struct archive_rb_node *n, *tmp;
+
+ ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(iso9660->hardlink_rbtree), tmp) {
+ __archive_rb_tree_remove_node(&(iso9660->hardlink_rbtree), n);
+ free(n);
+ }
+}
+
+static struct isoent *
+isoent_new(struct isofile *file)
+{
+ struct isoent *isoent;
+ static const struct archive_rb_tree_ops rb_ops = {
+ isoent_cmp_node, isoent_cmp_key,
+ };
+
+ isoent = calloc(1, sizeof(*isoent));
+ if (isoent == NULL)
+ return (NULL);
+ isoent->file = file;
+ isoent->children.first = NULL;
+ isoent->children.last = &(isoent->children.first);
+ __archive_rb_tree_init(&(isoent->rbtree), &rb_ops);
+ isoent->subdirs.first = NULL;
+ isoent->subdirs.last = &(isoent->subdirs.first);
+ isoent->extr_rec_list.first = NULL;
+ isoent->extr_rec_list.last = &(isoent->extr_rec_list.first);
+ isoent->extr_rec_list.current = NULL;
+ if (archive_entry_filetype(file->entry) == AE_IFDIR)
+ isoent->dir = 1;
+
+ return (isoent);
+}
+
+static inline struct isoent *
+isoent_clone(struct isoent *src)
+{
+ return (isoent_new(src->file));
+}
+
+static void
+_isoent_free(struct isoent *isoent)
+{
+ struct extr_rec *er, *er_next;
+
+ free(isoent->children_sorted);
+ free(isoent->identifier);
+ er = isoent->extr_rec_list.first;
+ while (er != NULL) {
+ er_next = er->next;
+ free(er);
+ er = er_next;
+ }
+ free(isoent);
+}
+
+static void
+isoent_free_all(struct isoent *isoent)
+{
+ struct isoent *np, *np_temp;
+
+ if (isoent == NULL)
+ return;
+ np = isoent;
+ for (;;) {
+ if (np->dir) {
+ if (np->children.first != NULL) {
+ /* Enter to sub directories. */
+ np = np->children.first;
+ continue;
+ }
+ }
+ for (;;) {
+ np_temp = np;
+ if (np->chnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ _isoent_free(np_temp);
+ if (np == np_temp)
+ return;
+ } else {
+ np = np->chnext;
+ _isoent_free(np_temp);
+ break;
+ }
+ }
+ }
+}
+
+static struct isoent *
+isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname)
+{
+ struct isofile *file;
+ struct isoent *isoent;
+
+ file = isofile_new(a, NULL);
+ if (file == NULL)
+ return (NULL);
+ archive_entry_set_pathname(file->entry, pathname);
+ archive_entry_unset_mtime(file->entry);
+ archive_entry_unset_atime(file->entry);
+ archive_entry_unset_ctime(file->entry);
+ archive_entry_set_uid(file->entry, getuid());
+ archive_entry_set_gid(file->entry, getgid());
+ archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
+ archive_entry_set_nlink(file->entry, 2);
+ if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {
+ isofile_free(file);
+ return (NULL);
+ }
+ isofile_add_entry(iso9660, file);
+
+ isoent = isoent_new(file);
+ if (isoent == NULL)
+ return (NULL);
+ isoent->dir = 1;
+ isoent->virtual = 1;
+
+ return (isoent);
+}
+
+static int
+isoent_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct isoent *e1 = (const struct isoent *)n1;
+ const struct isoent *e2 = (const struct isoent *)n2;
+
+ return (strcmp(e1->file->basename.s, e2->file->basename.s));
+}
+
+static int
+isoent_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct isoent *e = (const struct isoent *)n;
+
+ return (strcmp(e->file->basename.s, (const char *)key));
+}
+
+static int
+isoent_add_child_head(struct isoent *parent, struct isoent *child)
+{
+
+ if (!__archive_rb_tree_insert_node(
+ &(parent->rbtree), (struct archive_rb_node *)child))
+ return (0);
+ if ((child->chnext = parent->children.first) == NULL)
+ parent->children.last = &(child->chnext);
+ parent->children.first = child;
+ parent->children.cnt++;
+ child->parent = parent;
+
+ /* Add a child to a sub-directory chain */
+ if (child->dir) {
+ if ((child->drnext = parent->subdirs.first) == NULL)
+ parent->subdirs.last = &(child->drnext);
+ parent->subdirs.first = child;
+ parent->subdirs.cnt++;
+ child->parent = parent;
+ } else
+ child->drnext = NULL;
+ return (1);
+}
+
+static int
+isoent_add_child_tail(struct isoent *parent, struct isoent *child)
+{
+
+ if (!__archive_rb_tree_insert_node(
+ &(parent->rbtree), (struct archive_rb_node *)child))
+ return (0);
+ child->chnext = NULL;
+ *parent->children.last = child;
+ parent->children.last = &(child->chnext);
+ parent->children.cnt++;
+ child->parent = parent;
+
+ /* Add a child to a sub-directory chain */
+ child->drnext = NULL;
+ if (child->dir) {
+ *parent->subdirs.last = child;
+ parent->subdirs.last = &(child->drnext);
+ parent->subdirs.cnt++;
+ child->parent = parent;
+ }
+ return (1);
+}
+
+static void
+isoent_remove_child(struct isoent *parent, struct isoent *child)
+{
+ struct isoent *ent;
+
+ /* Remove a child entry from children chain. */
+ ent = parent->children.first;
+ while (ent->chnext != child)
+ ent = ent->chnext;
+ if ((ent->chnext = ent->chnext->chnext) == NULL)
+ parent->children.last = &(ent->chnext);
+ parent->children.cnt--;
+
+ if (child->dir) {
+ /* Remove a child entry from sub-directory chain. */
+ ent = parent->subdirs.first;
+ while (ent->drnext != child)
+ ent = ent->drnext;
+ if ((ent->drnext = ent->drnext->drnext) == NULL)
+ parent->subdirs.last = &(ent->drnext);
+ parent->subdirs.cnt--;
+ }
+
+ __archive_rb_tree_remove_node(&(parent->rbtree),
+ (struct archive_rb_node *)child);
+}
+
+static int
+isoent_clone_tree(struct archive_write *a, struct isoent **nroot,
+ struct isoent *root)
+{
+ struct isoent *np, *xroot, *newent;
+
+ np = root;
+ xroot = NULL;
+ do {
+ newent = isoent_clone(np);
+ if (newent == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ if (xroot == NULL) {
+ *nroot = xroot = newent;
+ newent->parent = xroot;
+ } else
+ isoent_add_child_tail(xroot, newent);
+ if (np->dir && np->children.first != NULL) {
+ /* Enter to sub directories. */
+ np = np->children.first;
+ xroot = newent;
+ continue;
+ }
+ while (np != np->parent) {
+ if (np->chnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ xroot = xroot->parent;
+ } else {
+ np = np->chnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Setup directory locations.
+ */
+static void
+isoent_setup_directory_location(struct iso9660 *iso9660, int location,
+ struct vdd *vdd)
+{
+ struct isoent *np;
+ int depth;
+
+ vdd->total_dir_block = 0;
+ depth = 0;
+ np = vdd->rootent;
+ do {
+ int block;
+
+ np->dir_block = calculate_directory_descriptors(
+ iso9660, vdd, np, depth);
+ vdd->total_dir_block += np->dir_block;
+ np->dir_location = location;
+ location += np->dir_block;
+ block = extra_setup_location(np, location);
+ vdd->total_dir_block += block;
+ location += block;
+
+ if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ while (np != np->parent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+}
+
+static void
+_isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent,
+ int *symlocation)
+{
+ struct isoent **children;
+ int n;
+
+ if (isoent->children.cnt == 0)
+ return;
+
+ children = isoent->children_sorted;
+ for (n = 0; n < isoent->children.cnt; n++) {
+ struct isoent *np;
+ struct isofile *file;
+
+ np = children[n];
+ if (np->dir)
+ continue;
+ if (np == iso9660->el_torito.boot)
+ continue;
+ file = np->file;
+ if (file->boot || file->hardlink_target != NULL)
+ continue;
+ if (archive_entry_filetype(file->entry) == AE_IFLNK ||
+ file->content.size == 0) {
+ /*
+ * Do not point a valid location.
+ * Make sure entry is not hardlink file.
+ */
+ file->content.location = (*symlocation)--;
+ continue;
+ }
+
+ file->write_content = 1;
+ }
+}
+
+/*
+ * Setup file locations.
+ */
+static void
+isoent_setup_file_location(struct iso9660 *iso9660, int location)
+{
+ struct isoent *isoent;
+ struct isoent *np;
+ struct isofile *file;
+ size_t size;
+ int block;
+ int depth;
+ int joliet;
+ int symlocation;
+ int total_block;
+
+ iso9660->total_file_block = 0;
+ if ((isoent = iso9660->el_torito.catalog) != NULL) {
+ isoent->file->content.location = location;
+ block = (int)((archive_entry_size(isoent->file->entry) +
+ LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+ location += block;
+ iso9660->total_file_block += block;
+ }
+ if ((isoent = iso9660->el_torito.boot) != NULL) {
+ isoent->file->content.location = location;
+ size = fd_boot_image_size(iso9660->el_torito.media_type);
+ if (size == 0)
+ size = (size_t)archive_entry_size(isoent->file->entry);
+ block = ((int)size + LOGICAL_BLOCK_SIZE -1)
+ >> LOGICAL_BLOCK_BITS;
+ location += block;
+ iso9660->total_file_block += block;
+ isoent->file->content.blocks = block;
+ }
+
+ depth = 0;
+ symlocation = -16;
+ if (!iso9660->opt.rr && iso9660->opt.joliet) {
+ joliet = 1;
+ np = iso9660->joliet.rootent;
+ } else {
+ joliet = 0;
+ np = iso9660->primary.rootent;
+ }
+ do {
+ _isoent_file_location(iso9660, np, &symlocation);
+
+ if (np->subdirs.first != NULL &&
+ (joliet ||
+ ((iso9660->opt.rr == OPT_RR_DISABLED &&
+ depth + 2 < iso9660->primary.max_depth) ||
+ (iso9660->opt.rr &&
+ depth + 1 < iso9660->primary.max_depth)))) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ while (np != np->parent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ total_block = 0;
+ for (file = iso9660->data_file_list.first;
+ file != NULL; file = file->datanext) {
+
+ if (!file->write_content)
+ continue;
+
+ file->cur_content = &(file->content);
+ do {
+ file->cur_content->location = location;
+ location += file->cur_content->blocks;
+ total_block += file->cur_content->blocks;
+ /* Next fragment */
+ file->cur_content = file->cur_content->next;
+ } while (file->cur_content != NULL);
+ }
+ iso9660->total_file_block += total_block;
+}
+
+static int
+get_path_component(char *name, size_t n, const char *fn)
+{
+ char *p;
+ size_t l;
+
+ p = strchr(fn, '/');
+ if (p == NULL) {
+ if ((l = strlen(fn)) == 0)
+ return (0);
+ } else
+ l = p - fn;
+ if (l > n -1)
+ return (-1);
+ memcpy(name, fn, l);
+ name[l] = '\0';
+
+ return ((int)l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+isoent_tree(struct archive_write *a, struct isoent **isoentpp)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+ char name[NAME_MAX+1];
+#else
+ char name[256];
+#endif
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent *dent, *isoent, *np;
+ struct isofile *f1, *f2;
+ const char *fn, *p;
+ int l;
+
+ isoent = *isoentpp;
+ dent = iso9660->primary.rootent;
+ if (isoent->file->parentdir.length > 0)
+ fn = p = isoent->file->parentdir.s;
+ else
+ fn = p = "";
+
+ /*
+ * If the path of the parent directory of `isoent' entry is
+ * the same as the path of `cur_dirent', add isoent to
+ * `cur_dirent'.
+ */
+ if (archive_strlen(&(iso9660->cur_dirstr))
+ == archive_strlen(&(isoent->file->parentdir)) &&
+ strcmp(iso9660->cur_dirstr.s, fn) == 0) {
+ if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) {
+ np = (struct isoent *)__archive_rb_tree_find_node(
+ &(iso9660->cur_dirent->rbtree),
+ isoent->file->basename.s);
+ goto same_entry;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ for (;;) {
+ l = get_path_component(name, sizeof(name), fn);
+ if (l == 0) {
+ np = NULL;
+ break;
+ }
+ if (l < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ _isoent_free(isoent);
+ return (ARCHIVE_FATAL);
+ }
+
+ np = isoent_find_child(dent, name);
+ if (np == NULL || fn[0] == '\0')
+ break;
+
+ /* Find next subdirectory. */
+ if (!np->dir) {
+ /* NOT Directory! */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "`%s' is not directory, we cannot insert `%s' ",
+ archive_entry_pathname(np->file->entry),
+ archive_entry_pathname(isoent->file->entry));
+ _isoent_free(isoent);
+ *isoentpp = NULL;
+ return (ARCHIVE_FAILED);
+ }
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ dent = np;
+ }
+ if (np == NULL) {
+ /*
+ * Create virtual parent directories.
+ */
+ while (fn[0] != '\0') {
+ struct isoent *vp;
+ struct archive_string as;
+
+ archive_string_init(&as);
+ archive_strncat(&as, p, fn - p + l);
+ if (as.s[as.length-1] == '/') {
+ as.s[as.length-1] = '\0';
+ as.length--;
+ }
+ vp = isoent_create_virtual_dir(a, iso9660, as.s);
+ if (vp == NULL) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ _isoent_free(isoent);
+ *isoentpp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_free(&as);
+
+ if (vp->file->dircnt > iso9660->dircnt_max)
+ iso9660->dircnt_max = vp->file->dircnt;
+ isoent_add_child_tail(dent, vp);
+ np = vp;
+
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ l = get_path_component(name, sizeof(name), fn);
+ if (l < 0) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ _isoent_free(isoent);
+ *isoentpp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ dent = np;
+ }
+
+ /* Found out the parent directory where isoent can be
+ * inserted. */
+ iso9660->cur_dirent = dent;
+ archive_string_empty(&(iso9660->cur_dirstr));
+ archive_string_ensure(&(iso9660->cur_dirstr),
+ archive_strlen(&(dent->file->parentdir)) +
+ archive_strlen(&(dent->file->basename)) + 2);
+ if (archive_strlen(&(dent->file->parentdir)) +
+ archive_strlen(&(dent->file->basename)) == 0)
+ iso9660->cur_dirstr.s[0] = 0;
+ else {
+ if (archive_strlen(&(dent->file->parentdir)) > 0) {
+ archive_string_copy(&(iso9660->cur_dirstr),
+ &(dent->file->parentdir));
+ archive_strappend_char(&(iso9660->cur_dirstr), '/');
+ }
+ archive_string_concat(&(iso9660->cur_dirstr),
+ &(dent->file->basename));
+ }
+
+ if (!isoent_add_child_tail(dent, isoent)) {
+ np = (struct isoent *)__archive_rb_tree_find_node(
+ &(dent->rbtree), isoent->file->basename.s);
+ goto same_entry;
+ }
+ return (ARCHIVE_OK);
+ }
+
+same_entry:
+ /*
+ * We have already has the entry the filename of which is
+ * the same.
+ */
+ f1 = np->file;
+ f2 = isoent->file;
+
+ /* If the file type of entries is different,
+ * we cannot handle it. */
+ if (archive_entry_filetype(f1->entry) !=
+ archive_entry_filetype(f2->entry)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Found duplicate entries `%s' and its file type is "
+ "different",
+ archive_entry_pathname(f1->entry));
+ _isoent_free(isoent);
+ *isoentpp = NULL;
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Swap file entries. */
+ np->file = f2;
+ isoent->file = f1;
+ np->virtual = 0;
+
+ _isoent_free(isoent);
+ *isoentpp = np;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Find a entry from `isoent'
+ */
+static struct isoent *
+isoent_find_child(struct isoent *isoent, const char *child_name)
+{
+ struct isoent *np;
+
+ np = (struct isoent *)__archive_rb_tree_find_node(
+ &(isoent->rbtree), child_name);
+ return (np);
+}
+
+/*
+ * Find a entry full-path of which is specified by `fn' parameter,
+ * in the tree.
+ */
+static struct isoent *
+isoent_find_entry(struct isoent *rootent, const char *fn)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+ char name[NAME_MAX+1];
+#else
+ char name[256];
+#endif
+ struct isoent *isoent, *np;
+ int l;
+
+ isoent = rootent;
+ np = NULL;
+ for (;;) {
+ l = get_path_component(name, sizeof(name), fn);
+ if (l == 0)
+ break;
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+
+ np = isoent_find_child(isoent, name);
+ if (np == NULL)
+ break;
+ if (fn[0] == '\0')
+ break;/* We found out the entry */
+
+ /* Try sub directory. */
+ isoent = np;
+ np = NULL;
+ if (!isoent->dir)
+ break;/* Not directory */
+ }
+
+ return (np);
+}
+
+/*
+ * Following idr_* functions are used for resolving duplicated filenames
+ * and unreceivable filenames to generate ISO9660/Joliet Identifiers.
+ */
+
+static void
+idr_relaxed_filenames(char *map)
+{
+ int i;
+
+ for (i = 0x21; i <= 0x2F; i++)
+ map[i] = 1;
+ for (i = 0x3A; i <= 0x41; i++)
+ map[i] = 1;
+ for (i = 0x5B; i <= 0x5E; i++)
+ map[i] = 1;
+ map[0x60] = 1;
+ for (i = 0x7B; i <= 0x7E; i++)
+ map[i] = 1;
+}
+
+static void
+idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr)
+{
+
+ idr->idrent_pool = NULL;
+ idr->pool_size = 0;
+ if (vdd->vdd_type != VDD_JOLIET) {
+ if (iso9660->opt.iso_level <= 3) {
+ memcpy(idr->char_map, d_characters_map,
+ sizeof(idr->char_map));
+ } else {
+ memcpy(idr->char_map, d1_characters_map,
+ sizeof(idr->char_map));
+ idr_relaxed_filenames(idr->char_map);
+ }
+ }
+}
+
+static void
+idr_cleanup(struct idr *idr)
+{
+ free(idr->idrent_pool);
+}
+
+static int
+idr_ensure_poolsize(struct archive_write *a, struct idr *idr,
+ int cnt)
+{
+
+ if (idr->pool_size < cnt) {
+ void *p;
+ const int bk = (1 << 7) - 1;
+ int psize;
+
+ psize = (cnt + bk) & ~bk;
+ p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ idr->idrent_pool = (struct idrent *)p;
+ idr->pool_size = psize;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax,
+ int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops)
+{
+ int r;
+
+ (void)ffmax; /* UNUSED */
+
+ r = idr_ensure_poolsize(a, idr, cnt);
+ if (r != ARCHIVE_OK)
+ return (r);
+ __archive_rb_tree_init(&(idr->rbtree), rbt_ops);
+ idr->wait_list.first = NULL;
+ idr->wait_list.last = &(idr->wait_list.first);
+ idr->pool_idx = 0;
+ idr->num_size = num_size;
+ idr->null_size = null_size;
+ return (ARCHIVE_OK);
+}
+
+static void
+idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff)
+{
+ struct idrent *idrent, *n;
+
+ idrent = &(idr->idrent_pool[idr->pool_idx++]);
+ idrent->wnext = idrent->avail = NULL;
+ idrent->isoent = isoent;
+ idrent->weight = weight;
+ idrent->noff = noff;
+ idrent->rename_num = 0;
+
+ if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) {
+ n = (struct idrent *)__archive_rb_tree_find_node(
+ &(idr->rbtree), idrent->isoent);
+ if (n != NULL) {
+ /* this `idrent' needs to rename. */
+ idrent->avail = n;
+ *idr->wait_list.last = idrent;
+ idr->wait_list.last = &(idrent->wnext);
+ }
+ }
+}
+
+static void
+idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize)
+{
+ unsigned char *p;
+ int wnp_ext_off;
+
+ wnp_ext_off = wnp->isoent->ext_off;
+ if (wnp->noff + numsize != wnp_ext_off) {
+ p = (unsigned char *)wnp->isoent->identifier;
+ /* Extend the filename; foo.c --> foo___.c */
+ memmove(p + wnp->noff + numsize, p + wnp_ext_off,
+ wnp->isoent->ext_len + nullsize);
+ wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize;
+ wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len;
+ }
+}
+
+static void
+idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num))
+{
+ struct idrent *n;
+ unsigned char *p;
+
+ for (n = idr->wait_list.first; n != NULL; n = n->wnext) {
+ idr_extend_identifier(n, idr->num_size, idr->null_size);
+ p = (unsigned char *)n->isoent->identifier + n->noff;
+ do {
+ fsetnum(p, n->avail->rename_num++);
+ } while (!__archive_rb_tree_insert_node(
+ &(idr->rbtree), &(n->rbnode)));
+ }
+}
+
+static void
+idr_set_num(unsigned char *p, int num)
+{
+ static const char xdig[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z'
+ };
+
+ num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig);
+ p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))];
+ num %= sizeof(xdig) * sizeof(xdig);
+ p[1] = xdig[ (num / sizeof(xdig))];
+ num %= sizeof(xdig);
+ p[2] = xdig[num];
+}
+
+static void
+idr_set_num_beutf16(unsigned char *p, int num)
+{
+ static const uint16_t xdig[] = {
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+ 0x0036, 0x0037, 0x0038, 0x0039,
+ 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046,
+ 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C,
+ 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052,
+ 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
+ 0x0059, 0x005A
+ };
+#define XDIG_CNT (sizeof(xdig)/sizeof(xdig[0]))
+
+ num %= XDIG_CNT * XDIG_CNT * XDIG_CNT;
+ archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]);
+ num %= XDIG_CNT * XDIG_CNT;
+ archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]);
+ num %= XDIG_CNT;
+ archive_be16enc(p+4, xdig[num]);
+}
+
+/*
+ * Generate ISO9660 Identifier.
+ */
+static int
+isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent,
+ struct idr *idr)
+{
+ struct iso9660 *iso9660;
+ struct isoent *np;
+ char *p;
+ int l, r;
+ const char *char_map;
+ char allow_ldots, allow_multidot, allow_period, allow_vernum;
+ int fnmax, ffmax, dnmax;
+ static const struct archive_rb_tree_ops rb_ops = {
+ isoent_cmp_node_iso9660, isoent_cmp_key_iso9660
+ };
+
+ if (isoent->children.cnt == 0)
+ return (0);
+
+ iso9660 = a->format_data;
+ char_map = idr->char_map;
+ if (iso9660->opt.iso_level <= 3) {
+ allow_ldots = 0;
+ allow_multidot = 0;
+ allow_period = 1;
+ allow_vernum = iso9660->opt.allow_vernum;
+ if (iso9660->opt.iso_level == 1) {
+ fnmax = 8;
+ ffmax = 12;/* fnmax + '.' + 3 */
+ dnmax = 8;
+ } else {
+ fnmax = 30;
+ ffmax = 31;
+ dnmax = 31;
+ }
+ } else {
+ allow_ldots = allow_multidot = 1;
+ allow_period = allow_vernum = 0;
+ if (iso9660->opt.rr)
+ /*
+ * MDR : The maximum size of Directory Record(254).
+ * DRL : A Directory Record Length(33).
+ * CE : A size of SUSP CE System Use Entry(28).
+ * MDR - DRL - CE = 254 - 33 - 28 = 193.
+ */
+ fnmax = ffmax = dnmax = 193;
+ else
+ /*
+ * XA : CD-ROM XA System Use Extension
+ * Information(14).
+ * MDR - DRL - XA = 254 - 33 -14 = 207.
+ */
+ fnmax = ffmax = dnmax = 207;
+ }
+
+ r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops);
+ if (r < 0)
+ return (r);
+
+ for (np = isoent->children.first; np != NULL; np = np->chnext) {
+ char *dot, *xdot;
+ int ext_off, noff, weight;
+
+ l = (int)np->file->basename.length;
+ p = malloc(l+31+2+1);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(p, np->file->basename.s, l);
+ p[l] = '\0';
+ np->identifier = p;
+
+ dot = xdot = NULL;
+ if (!allow_ldots) {
+ /*
+ * If there is a '.' character at the first byte,
+ * it has to be replaced by '_' character.
+ */
+ if (*p == '.')
+ *p++ = '_';
+ }
+ for (;*p; p++) {
+ if (*p & 0x80) {
+ *p = '_';
+ continue;
+ }
+ if (char_map[(unsigned char)*p]) {
+ /* if iso-level is '4', a character '.' is
+ * allowed by char_map. */
+ if (*p == '.') {
+ xdot = dot;
+ dot = p;
+ }
+ continue;
+ }
+ if (*p >= 'a' && *p <= 'z') {
+ *p -= 'a' - 'A';
+ continue;
+ }
+ if (*p == '.') {
+ xdot = dot;
+ dot = p;
+ if (allow_multidot)
+ continue;
+ }
+ *p = '_';
+ }
+ p = np->identifier;
+ weight = -1;
+ if (dot == NULL) {
+ int nammax;
+
+ if (np->dir)
+ nammax = dnmax;
+ else
+ nammax = fnmax;
+
+ if (l > nammax) {
+ p[nammax] = '\0';
+ weight = nammax;
+ ext_off = nammax;
+ } else
+ ext_off = l;
+ } else {
+ *dot = '.';
+ ext_off = (int)(dot - p);
+
+ if (iso9660->opt.iso_level == 1) {
+ if (dot - p <= 8) {
+ if (strlen(dot) > 4) {
+ /* A length of a file extension
+ * must be less than 4 */
+ dot[4] = '\0';
+ weight = 0;
+ }
+ } else {
+ p[8] = dot[0];
+ p[9] = dot[1];
+ p[10] = dot[2];
+ p[11] = dot[3];
+ p[12] = '\0';
+ weight = 8;
+ ext_off = 8;
+ }
+ } else if (np->dir) {
+ if (l > dnmax) {
+ p[dnmax] = '\0';
+ weight = dnmax;
+ if (ext_off > dnmax)
+ ext_off = dnmax;
+ }
+ } else if (l > ffmax) {
+ int extlen = (int)strlen(dot);
+ int xdoff;
+
+ if (xdot != NULL)
+ xdoff = (int)(xdot - p);
+ else
+ xdoff = 0;
+
+ if (extlen > 1 && xdoff < fnmax-1) {
+ int off;
+
+ if (extlen > ffmax)
+ extlen = ffmax;
+ off = ffmax - extlen;
+ if (off == 0) {
+ /* A dot('.') character
+ * doesn't place to the first
+ * byte of identifier. */
+ off ++;
+ extlen --;
+ }
+ memmove(p+off, dot, extlen);
+ p[ffmax] = '\0';
+ ext_off = off;
+ weight = off;
+#ifdef COMPAT_MKISOFS
+ } else if (xdoff >= fnmax-1) {
+ /* Simulate a bug(?) of mkisofs. */
+ p[fnmax-1] = '\0';
+ ext_off = fnmax-1;
+ weight = fnmax-1;
+#endif
+ } else {
+ p[fnmax] = '\0';
+ ext_off = fnmax;
+ weight = fnmax;
+ }
+ }
+ }
+ /* Save an offset of a file name extension to sort files. */
+ np->ext_off = ext_off;
+ np->ext_len = (int)strlen(&p[ext_off]);
+ np->id_len = l = ext_off + np->ext_len;
+
+ /* Make an offset of the number which is used to be set
+ * hexadecimal number to avoid duplicate identifier. */
+ if (iso9660->opt.iso_level == 1) {
+ if (ext_off >= 5)
+ noff = 5;
+ else
+ noff = ext_off;
+ } else {
+ if (l == ffmax)
+ noff = ext_off - 3;
+ else if (l == ffmax-1)
+ noff = ext_off - 2;
+ else if (l == ffmax-2)
+ noff = ext_off - 1;
+ else
+ noff = ext_off;
+ }
+ /* Register entry to the identifier resolver. */
+ idr_register(idr, np, weight, noff);
+ }
+
+ /* Resolve duplicate identifier. */
+ idr_resolve(idr, idr_set_num);
+
+ /* Add a period and a version number to identifiers. */
+ for (np = isoent->children.first; np != NULL; np = np->chnext) {
+ if (!np->dir && np->rr_child == NULL) {
+ p = np->identifier + np->ext_off + np->ext_len;
+ if (np->ext_len == 0 && allow_period) {
+ *p++ = '.';
+ np->ext_len = 1;
+ }
+ if (np->ext_len == 1 && !allow_period) {
+ *--p = '\0';
+ np->ext_len = 0;
+ }
+ np->id_len = np->ext_off + np->ext_len;
+ if (allow_vernum) {
+ *p++ = ';';
+ *p++ = '1';
+ np->id_len += 2;
+ }
+ *p = '\0';
+ } else
+ np->id_len = np->ext_off + np->ext_len;
+ np->mb_len = np->id_len;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Generate Joliet Identifier.
+ */
+static int
+isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
+ struct idr *idr)
+{
+ struct iso9660 *iso9660;
+ struct isoent *np;
+ unsigned char *p;
+ size_t l;
+ int r;
+ size_t ffmax, parent_len;
+ static const struct archive_rb_tree_ops rb_ops = {
+ isoent_cmp_node_joliet, isoent_cmp_key_joliet
+ };
+
+ if (isoent->children.cnt == 0)
+ return (0);
+
+ iso9660 = a->format_data;
+ if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
+ ffmax = 206;
+ else
+ ffmax = 128;
+
+ r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops);
+ if (r < 0)
+ return (r);
+
+ parent_len = 1;
+ for (np = isoent; np->parent != np; np = np->parent)
+ parent_len += np->mb_len + 1;
+
+ for (np = isoent->children.first; np != NULL; np = np->chnext) {
+ unsigned char *dot;
+ int ext_off, noff, weight;
+ size_t lt;
+
+ if ((l = np->file->basename_utf16.length) > ffmax)
+ l = ffmax;
+
+ p = malloc((l+1)*2);
+ if (p == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(p, np->file->basename_utf16.s, l);
+ p[l] = 0;
+ p[l+1] = 0;
+
+ np->identifier = (char *)p;
+ lt = l;
+ dot = p + l;
+ weight = 0;
+ while (lt > 0) {
+ if (!joliet_allowed_char(p[0], p[1]))
+ archive_be16enc(p, 0x005F); /* '_' */
+ else if (p[0] == 0 && p[1] == 0x2E) /* '.' */
+ dot = p;
+ p += 2;
+ lt -= 2;
+ }
+ ext_off = (int)(dot - (unsigned char *)np->identifier);
+ np->ext_off = ext_off;
+ np->ext_len = (int)l - ext_off;
+ np->id_len = (int)l;
+
+ /*
+ * Get a length of MBS of a full-pathname.
+ */
+ if (np->file->basename_utf16.length > ffmax) {
+ if (archive_strncpy_l(&iso9660->mbs,
+ (const char *)np->identifier, l,
+ iso9660->sconv_from_utf16be) != 0 &&
+ errno == ENOMEM) {
+ archive_set_error(&a->archive, errno,
+ "No memory");
+ return (ARCHIVE_FATAL);
+ }
+ np->mb_len = (int)iso9660->mbs.length;
+ if (np->mb_len != (int)np->file->basename.length)
+ weight = np->mb_len;
+ } else
+ np->mb_len = (int)np->file->basename.length;
+
+ /* If a length of full-pathname is longer than 240 bytes,
+ * it violates Joliet extensions regulation. */
+ if (parent_len > 240
+ || np->mb_len > 240
+ || parent_len + np->mb_len > 240) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "The regulation of Joliet extensions;"
+ " A length of a full-pathname of `%s' is "
+ "longer than 240 bytes, (p=%d, b=%d)",
+ archive_entry_pathname(np->file->entry),
+ (int)parent_len, (int)np->mb_len);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Make an offset of the number which is used to be set
+ * hexadecimal number to avoid duplicate identifier. */
+ if (l == ffmax)
+ noff = ext_off - 6;
+ else if (l == ffmax-2)
+ noff = ext_off - 4;
+ else if (l == ffmax-4)
+ noff = ext_off - 2;
+ else
+ noff = ext_off;
+ /* Register entry to the identifier resolver. */
+ idr_register(idr, np, weight, noff);
+ }
+
+ /* Resolve duplicate identifier with Joliet Volume. */
+ idr_resolve(idr, idr_set_num_beutf16);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This comparing rule is according to ISO9660 Standard 9.3
+ */
+static int
+isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2)
+{
+ const char *s1, *s2;
+ int cmp;
+ int l;
+
+ s1 = p1->identifier;
+ s2 = p2->identifier;
+
+ /* Compare File Name */
+ l = p1->ext_off;
+ if (l > p2->ext_off)
+ l = p2->ext_off;
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ if (p1->ext_off < p2->ext_off) {
+ s2 += l;
+ l = p2->ext_off - p1->ext_off;
+ while (l--)
+ if (0x20 != *s2++)
+ return (0x20
+ - *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_off > p2->ext_off) {
+ s1 += l;
+ l = p1->ext_off - p2->ext_off;
+ while (l--)
+ if (0x20 != *s1++)
+ return (*(const unsigned char *)(s1 - 1)
+ - 0x20);
+ }
+ /* Compare File Name Extension */
+ if (p1->ext_len == 0 && p2->ext_len == 0)
+ return (0);
+ if (p1->ext_len == 1 && p2->ext_len == 1)
+ return (0);
+ if (p1->ext_len <= 1)
+ return (-1);
+ if (p2->ext_len <= 1)
+ return (1);
+ l = p1->ext_len;
+ if (l > p2->ext_len)
+ l = p2->ext_len;
+ s1 = p1->identifier + p1->ext_off;
+ s2 = p2->identifier + p2->ext_off;
+ if (l > 1) {
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ }
+ if (p1->ext_len < p2->ext_len) {
+ s2 += l;
+ l = p2->ext_len - p1->ext_len;
+ while (l--)
+ if (0x20 != *s2++)
+ return (0x20
+ - *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_len > p2->ext_len) {
+ s1 += l;
+ l = p1->ext_len - p2->ext_len;
+ while (l--)
+ if (0x20 != *s1++)
+ return (*(const unsigned char *)(s1 - 1)
+ - 0x20);
+ }
+ /* Compare File Version Number */
+ /* No operation. The File Version Number is always one. */
+
+ return (cmp);
+}
+
+static int
+isoent_cmp_node_iso9660(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct idrent *e1 = (const struct idrent *)n1;
+ const struct idrent *e2 = (const struct idrent *)n2;
+
+ return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent));
+}
+
+static int
+isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key)
+{
+ const struct isoent *isoent = (const struct isoent *)key;
+ const struct idrent *idrent = (const struct idrent *)node;
+
+ return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent));
+}
+
+static int
+isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2)
+{
+ const unsigned char *s1, *s2;
+ int cmp;
+ int l;
+
+ s1 = (const unsigned char *)p1->identifier;
+ s2 = (const unsigned char *)p2->identifier;
+
+ /* Compare File Name */
+ l = p1->ext_off;
+ if (l > p2->ext_off)
+ l = p2->ext_off;
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ if (p1->ext_off < p2->ext_off) {
+ s2 += l;
+ l = p2->ext_off - p1->ext_off;
+ while (l--)
+ if (0 != *s2++)
+ return (- *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_off > p2->ext_off) {
+ s1 += l;
+ l = p1->ext_off - p2->ext_off;
+ while (l--)
+ if (0 != *s1++)
+ return (*(const unsigned char *)(s1 - 1));
+ }
+ /* Compare File Name Extension */
+ if (p1->ext_len == 0 && p2->ext_len == 0)
+ return (0);
+ if (p1->ext_len == 2 && p2->ext_len == 2)
+ return (0);
+ if (p1->ext_len <= 2)
+ return (-1);
+ if (p2->ext_len <= 2)
+ return (1);
+ l = p1->ext_len;
+ if (l > p2->ext_len)
+ l = p2->ext_len;
+ s1 = (unsigned char *)(p1->identifier + p1->ext_off);
+ s2 = (unsigned char *)(p2->identifier + p2->ext_off);
+ if (l > 1) {
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ }
+ if (p1->ext_len < p2->ext_len) {
+ s2 += l;
+ l = p2->ext_len - p1->ext_len;
+ while (l--)
+ if (0 != *s2++)
+ return (- *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_len > p2->ext_len) {
+ s1 += l;
+ l = p1->ext_len - p2->ext_len;
+ while (l--)
+ if (0 != *s1++)
+ return (*(const unsigned char *)(s1 - 1));
+ }
+ /* Compare File Version Number */
+ /* No operation. The File Version Number is always one. */
+
+ return (cmp);
+}
+
+static int
+isoent_cmp_node_joliet(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct idrent *e1 = (const struct idrent *)n1;
+ const struct idrent *e2 = (const struct idrent *)n2;
+
+ return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent));
+}
+
+static int
+isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key)
+{
+ const struct isoent *isoent = (const struct isoent *)key;
+ const struct idrent *idrent = (const struct idrent *)node;
+
+ return (isoent_cmp_joliet_identifier(isoent, idrent->isoent));
+}
+
+static int
+isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent,
+ struct idr *idr)
+{
+ struct archive_rb_node *rn;
+ struct isoent **children;
+
+ children = malloc(isoent->children.cnt * sizeof(struct isoent *));
+ if (children == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ isoent->children_sorted = children;
+
+ ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) {
+ struct idrent *idrent = (struct idrent *)rn;
+ *children ++ = idrent->isoent;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * - Generate ISO9660 and Joliet identifiers from basenames.
+ * - Sort files by each directory.
+ */
+static int
+isoent_traverse_tree(struct archive_write *a, struct vdd* vdd)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent *np;
+ struct idr idr;
+ int depth;
+ int r;
+ int (*genid)(struct archive_write *, struct isoent *, struct idr *);
+
+ idr_init(iso9660, vdd, &idr);
+ np = vdd->rootent;
+ depth = 0;
+ if (vdd->vdd_type == VDD_JOLIET)
+ genid = isoent_gen_joliet_identifier;
+ else
+ genid = isoent_gen_iso9660_identifier;
+ do {
+ if (np->virtual &&
+ !archive_entry_mtime_is_set(np->file->entry)) {
+ /* Set properly times to virtual directory */
+ archive_entry_set_mtime(np->file->entry,
+ iso9660->birth_time, 0);
+ archive_entry_set_atime(np->file->entry,
+ iso9660->birth_time, 0);
+ archive_entry_set_ctime(np->file->entry,
+ iso9660->birth_time, 0);
+ }
+ if (np->children.first != NULL) {
+ if (vdd->vdd_type != VDD_JOLIET &&
+ !iso9660->opt.rr && depth + 1 >= vdd->max_depth) {
+ if (np->children.cnt > 0)
+ iso9660->directories_too_deep = np;
+ } else {
+ /* Generate Identifier */
+ r = genid(a, np, &idr);
+ if (r < 0)
+ goto exit_traverse_tree;
+ r = isoent_make_sorted_files(a, np, &idr);
+ if (r < 0)
+ goto exit_traverse_tree;
+
+ if (np->subdirs.first != NULL &&
+ depth + 1 < vdd->max_depth) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ }
+ }
+ while (np != np->parent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ r = ARCHIVE_OK;
+exit_traverse_tree:
+ idr_cleanup(&idr);
+
+ return (r);
+}
+
+/*
+ * Collect directory entries into path_table by a directory depth.
+ */
+static int
+isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth)
+{
+ struct isoent *np;
+
+ if (rootent == NULL)
+ rootent = vdd->rootent;
+ np = rootent;
+ do {
+ /* Register current directory to pathtable. */
+ path_table_add_entry(&(vdd->pathtbl[depth]), np);
+
+ if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+ /* Enter to sub directories. */
+ np = np->subdirs.first;
+ depth++;
+ continue;
+ }
+ while (np != rootent) {
+ if (np->drnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ depth--;
+ } else {
+ np = np->drnext;
+ break;
+ }
+ }
+ } while (np != rootent);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * The entry whose number of levels in a directory hierarchy is
+ * large than eight relocate to rr_move directory.
+ */
+static int
+isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved,
+ struct isoent *curent, struct isoent **newent)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent *rrmoved, *mvent, *np;
+
+ if ((rrmoved = *rr_moved) == NULL) {
+ struct isoent *rootent = iso9660->primary.rootent;
+ /* There isn't rr_move entry.
+ * Create rr_move entry and insert it into the root entry.
+ */
+ rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved");
+ if (rrmoved == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ /* Add "rr_moved" entry to the root entry. */
+ isoent_add_child_head(rootent, rrmoved);
+ archive_entry_set_nlink(rootent->file->entry,
+ archive_entry_nlink(rootent->file->entry) + 1);
+ /* Register "rr_moved" entry to second level pathtable. */
+ path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved);
+ /* Save rr_moved. */
+ *rr_moved = rrmoved;
+ }
+ /*
+ * Make a clone of curent which is going to be relocated
+ * to rr_moved.
+ */
+ mvent = isoent_clone(curent);
+ if (mvent == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ /* linking.. and use for creating "CL", "PL" and "RE" */
+ mvent->rr_parent = curent->parent;
+ curent->rr_child = mvent;
+ /*
+ * Move subdirectories from the curent to mvent
+ */
+ if (curent->children.first != NULL) {
+ *mvent->children.last = curent->children.first;
+ mvent->children.last = curent->children.last;
+ }
+ for (np = mvent->children.first; np != NULL; np = np->chnext)
+ np->parent = mvent;
+ mvent->children.cnt = curent->children.cnt;
+ curent->children.cnt = 0;
+ curent->children.first = NULL;
+ curent->children.last = &curent->children.first;
+
+ if (curent->subdirs.first != NULL) {
+ *mvent->subdirs.last = curent->subdirs.first;
+ mvent->subdirs.last = curent->subdirs.last;
+ }
+ mvent->subdirs.cnt = curent->subdirs.cnt;
+ curent->subdirs.cnt = 0;
+ curent->subdirs.first = NULL;
+ curent->subdirs.last = &curent->subdirs.first;
+
+ /*
+ * The mvent becomes a child of the rr_moved entry.
+ */
+ isoent_add_child_tail(rrmoved, mvent);
+ archive_entry_set_nlink(rrmoved->file->entry,
+ archive_entry_nlink(rrmoved->file->entry) + 1);
+ /*
+ * This entry which relocated to the rr_moved directory
+ * has to set the flag as a file.
+ * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry.
+ */
+ curent->dir = 0;
+
+ *newent = mvent;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+isoent_rr_move(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct path_table *pt;
+ struct isoent *rootent, *rr_moved;
+ struct isoent *np, *last;
+ int r;
+
+ pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]);
+ /* There aren't level 8 directories reaching a deeper level. */
+ if (pt->cnt == 0)
+ return (ARCHIVE_OK);
+
+ rootent = iso9660->primary.rootent;
+ /* If "rr_moved" directory is already existing,
+ * we have to use it. */
+ rr_moved = isoent_find_child(rootent, "rr_moved");
+ if (rr_moved != NULL &&
+ rr_moved != rootent->children.first) {
+ /*
+ * It's necessary that rr_move is the first entry
+ * of the root.
+ */
+ /* Remove "rr_moved" entry from children chain. */
+ isoent_remove_child(rootent, rr_moved);
+
+ /* Add "rr_moved" entry into the head of children chain. */
+ isoent_add_child_head(rootent, rr_moved);
+ }
+
+ /*
+ * Check level 8 path_table.
+ * If find out sub directory entries, that entries move to rr_move.
+ */
+ np = pt->first;
+ while (np != NULL) {
+ last = path_table_last_entry(pt);
+ for (; np != NULL; np = np->ptnext) {
+ struct isoent *mvent;
+ struct isoent *newent;
+
+ if (!np->dir)
+ continue;
+ for (mvent = np->subdirs.first;
+ mvent != NULL; mvent = mvent->drnext) {
+ r = isoent_rr_move_dir(a, &rr_moved,
+ mvent, &newent);
+ if (r < 0)
+ return (r);
+ isoent_collect_dirs(&(iso9660->primary),
+ newent, 2);
+ }
+ }
+ /* If new entries are added to level 8 path_talbe,
+ * its sub directory entries move to rr_move too.
+ */
+ np = last->ptnext;
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * This comparing rule is according to ISO9660 Standard 6.9.1
+ */
+static int
+__LA_LIBC_CC
+_compare_path_table(const void *v1, const void *v2)
+{
+ const struct isoent *p1, *p2;
+ const char *s1, *s2;
+ int cmp, l;
+
+ p1 = *((const struct isoent **)(uintptr_t)v1);
+ p2 = *((const struct isoent **)(uintptr_t)v2);
+
+ /* Compare parent directory number */
+ cmp = p1->parent->dir_number - p2->parent->dir_number;
+ if (cmp != 0)
+ return (cmp);
+
+ /* Compare identifier */
+ s1 = p1->identifier;
+ s2 = p2->identifier;
+ l = p1->ext_off;
+ if (l > p2->ext_off)
+ l = p2->ext_off;
+ cmp = strncmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ if (p1->ext_off < p2->ext_off) {
+ s2 += l;
+ l = p2->ext_off - p1->ext_off;
+ while (l--)
+ if (0x20 != *s2++)
+ return (0x20
+ - *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_off > p2->ext_off) {
+ s1 += l;
+ l = p1->ext_off - p2->ext_off;
+ while (l--)
+ if (0x20 != *s1++)
+ return (*(const unsigned char *)(s1 - 1)
+ - 0x20);
+ }
+ return (0);
+}
+
+static int
+__LA_LIBC_CC
+_compare_path_table_joliet(const void *v1, const void *v2)
+{
+ const struct isoent *p1, *p2;
+ const unsigned char *s1, *s2;
+ int cmp, l;
+
+ p1 = *((const struct isoent **)(uintptr_t)v1);
+ p2 = *((const struct isoent **)(uintptr_t)v2);
+
+ /* Compare parent directory number */
+ cmp = p1->parent->dir_number - p2->parent->dir_number;
+ if (cmp != 0)
+ return (cmp);
+
+ /* Compare identifier */
+ s1 = (const unsigned char *)p1->identifier;
+ s2 = (const unsigned char *)p2->identifier;
+ l = p1->ext_off;
+ if (l > p2->ext_off)
+ l = p2->ext_off;
+ cmp = memcmp(s1, s2, l);
+ if (cmp != 0)
+ return (cmp);
+ if (p1->ext_off < p2->ext_off) {
+ s2 += l;
+ l = p2->ext_off - p1->ext_off;
+ while (l--)
+ if (0 != *s2++)
+ return (- *(const unsigned char *)(s2 - 1));
+ } else if (p1->ext_off > p2->ext_off) {
+ s1 += l;
+ l = p1->ext_off - p2->ext_off;
+ while (l--)
+ if (0 != *s1++)
+ return (*(const unsigned char *)(s1 - 1));
+ }
+ return (0);
+}
+
+static inline void
+path_table_add_entry(struct path_table *pathtbl, struct isoent *ent)
+{
+ ent->ptnext = NULL;
+ *pathtbl->last = ent;
+ pathtbl->last = &(ent->ptnext);
+ pathtbl->cnt ++;
+}
+
+static inline struct isoent *
+path_table_last_entry(struct path_table *pathtbl)
+{
+ if (pathtbl->first == NULL)
+ return (NULL);
+ return (((struct isoent *)(void *)
+ ((char *)(pathtbl->last) - offsetof(struct isoent, ptnext))));
+}
+
+/*
+ * Sort directory entries in path_table
+ * and assign directory number to each entries.
+ */
+static int
+isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd,
+ int depth, int *dir_number)
+{
+ struct isoent *np;
+ struct isoent **enttbl;
+ struct path_table *pt;
+ int i;
+
+ pt = &vdd->pathtbl[depth];
+ if (pt->cnt == 0) {
+ pt->sorted = NULL;
+ return (ARCHIVE_OK);
+ }
+ enttbl = malloc(pt->cnt * sizeof(struct isoent *));
+ if (enttbl == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ pt->sorted = enttbl;
+ for (np = pt->first; np != NULL; np = np->ptnext)
+ *enttbl ++ = np;
+ enttbl = pt->sorted;
+
+ switch (vdd->vdd_type) {
+ case VDD_PRIMARY:
+ case VDD_ENHANCED:
+#ifdef __COMPAR_FN_T
+ qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+ (__compar_fn_t)_compare_path_table);
+#else
+ qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+ _compare_path_table);
+#endif
+ break;
+ case VDD_JOLIET:
+#ifdef __COMPAR_FN_T
+ qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+ (__compar_fn_t)_compare_path_table_joliet);
+#else
+ qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+ _compare_path_table_joliet);
+#endif
+ break;
+ }
+ for (i = 0; i < pt->cnt; i++)
+ enttbl[i]->dir_number = (*dir_number)++;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd,
+ int max_depth)
+{
+ int i;
+
+ vdd->max_depth = max_depth;
+ vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth);
+ if (vdd->pathtbl == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ for (i = 0; i < vdd->max_depth; i++) {
+ vdd->pathtbl[i].first = NULL;
+ vdd->pathtbl[i].last = &(vdd->pathtbl[i].first);
+ vdd->pathtbl[i].sorted = NULL;
+ vdd->pathtbl[i].cnt = 0;
+ }
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Make Path Tables
+ */
+static int
+isoent_make_path_table(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ int depth, r;
+ int dir_number;
+
+ /*
+ * Init Path Table.
+ */
+ if (iso9660->dircnt_max >= MAX_DEPTH &&
+ (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4))
+ r = isoent_alloc_path_table(a, &(iso9660->primary),
+ iso9660->dircnt_max + 1);
+ else
+ /* The number of levels in the hierarchy cannot exceed
+ * eight. */
+ r = isoent_alloc_path_table(a, &(iso9660->primary),
+ MAX_DEPTH);
+ if (r < 0)
+ return (r);
+ if (iso9660->opt.joliet) {
+ r = isoent_alloc_path_table(a, &(iso9660->joliet),
+ iso9660->dircnt_max + 1);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Step 0.
+ * - Collect directories for primary and joliet.
+ */
+ isoent_collect_dirs(&(iso9660->primary), NULL, 0);
+ if (iso9660->opt.joliet)
+ isoent_collect_dirs(&(iso9660->joliet), NULL, 0);
+ /*
+ * Rockridge; move deeper depth directories to rr_moved.
+ */
+ if (iso9660->opt.rr) {
+ r = isoent_rr_move(a);
+ if (r < 0)
+ return (r);
+ }
+
+ /* Update nlink. */
+ isofile_connect_hardlink_files(iso9660);
+
+ /* Step 1.
+ * - Renew a value of the depth of that directories.
+ * - Resolve hardlinks.
+ * - Convert pathnames to ISO9660 name or UCS2(joliet).
+ * - Sort files by each directory.
+ */
+ r = isoent_traverse_tree(a, &(iso9660->primary));
+ if (r < 0)
+ return (r);
+ if (iso9660->opt.joliet) {
+ r = isoent_traverse_tree(a, &(iso9660->joliet));
+ if (r < 0)
+ return (r);
+ }
+
+ /* Step 2.
+ * - Sort directories.
+ * - Assign all directory number.
+ */
+ dir_number = 1;
+ for (depth = 0; depth < iso9660->primary.max_depth; depth++) {
+ r = isoent_make_path_table_2(a, &(iso9660->primary),
+ depth, &dir_number);
+ if (r < 0)
+ return (r);
+ }
+ if (iso9660->opt.joliet) {
+ dir_number = 1;
+ for (depth = 0; depth < iso9660->joliet.max_depth; depth++) {
+ r = isoent_make_path_table_2(a, &(iso9660->joliet),
+ depth, &dir_number);
+ if (r < 0)
+ return (r);
+ }
+ }
+ if (iso9660->opt.limit_dirs && dir_number > 0xffff) {
+ /*
+ * Maximum number of directories is 65535(0xffff)
+ * doe to size(16bit) of Parent Directory Number of
+ * the Path Table.
+ * See also ISO9660 Standard 9.4.
+ */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Too many directories(%d) over 65535.", dir_number);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Get the size of the Path Table. */
+ calculate_path_table_size(&(iso9660->primary));
+ if (iso9660->opt.joliet)
+ calculate_path_table_size(&(iso9660->joliet));
+
+ return (ARCHIVE_OK);
+}
+
+static int
+isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent)
+{
+ struct iso9660 *iso9660 = a->format_data;
+
+ /* Find a isoent of the boot file. */
+ iso9660->el_torito.boot = isoent_find_entry(rootent,
+ iso9660->el_torito.boot_filename.s);
+ if (iso9660->el_torito.boot == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't find the boot image file ``%s''",
+ iso9660->el_torito.boot_filename.s);
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->el_torito.boot->file->boot = BOOT_IMAGE;
+ return (ARCHIVE_OK);
+}
+
+static int
+isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file;
+ struct isoent *isoent;
+ struct archive_entry *entry;
+
+ (void)rootent; /* UNUSED */
+ /*
+ * Create the entry which is the "boot.catalog" file.
+ */
+ file = isofile_new(a, NULL);
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ archive_entry_set_pathname(file->entry,
+ iso9660->el_torito.catalog_filename.s);
+ archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE);
+ archive_entry_set_mtime(file->entry, iso9660->birth_time, 0);
+ archive_entry_set_atime(file->entry, iso9660->birth_time, 0);
+ archive_entry_set_ctime(file->entry, iso9660->birth_time, 0);
+ archive_entry_set_uid(file->entry, getuid());
+ archive_entry_set_gid(file->entry, getgid());
+ archive_entry_set_mode(file->entry, AE_IFREG | 0444);
+ archive_entry_set_nlink(file->entry, 1);
+
+ if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {
+ isofile_free(file);
+ return (ARCHIVE_FATAL);
+ }
+ file->boot = BOOT_CATALOG;
+ file->content.size = LOGICAL_BLOCK_SIZE;
+ isofile_add_entry(iso9660, file);
+
+ isoent = isoent_new(file);
+ if (isoent == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ isoent->virtual = 1;
+
+ /* Add the "boot.catalog" entry into tree */
+ if (isoent_tree(a, &isoent) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ iso9660->el_torito.catalog = isoent;
+ /*
+ * Get a boot media type.
+ */
+ switch (iso9660->opt.boot_type) {
+ default:
+ case OPT_BOOT_TYPE_AUTO:
+ /* Try detecting a media type of the boot image. */
+ entry = iso9660->el_torito.boot->file->entry;
+ if (archive_entry_size(entry) == FD_1_2M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_1_2M_DISKETTE;
+ else if (archive_entry_size(entry) == FD_1_44M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_1_44M_DISKETTE;
+ else if (archive_entry_size(entry) == FD_2_88M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_2_88M_DISKETTE;
+ else
+ /* We cannot decide whether the boot image is
+ * hard-disk. */
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_NO_EMULATION;
+ break;
+ case OPT_BOOT_TYPE_NO_EMU:
+ iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION;
+ break;
+ case OPT_BOOT_TYPE_HARD_DISK:
+ iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK;
+ break;
+ case OPT_BOOT_TYPE_FD:
+ entry = iso9660->el_torito.boot->file->entry;
+ if (archive_entry_size(entry) <= FD_1_2M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_1_2M_DISKETTE;
+ else if (archive_entry_size(entry) <= FD_1_44M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_1_44M_DISKETTE;
+ else if (archive_entry_size(entry) <= FD_2_88M_SIZE)
+ iso9660->el_torito.media_type =
+ BOOT_MEDIA_2_88M_DISKETTE;
+ else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Boot image file(``%s'') size is too big "
+ "for fd type.",
+ iso9660->el_torito.boot_filename.s);
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ }
+
+ /*
+ * Get a system type.
+ * TODO: `El Torito' specification says "A copy of byte 5 from the
+ * Partition Table found in the boot image".
+ */
+ iso9660->el_torito.system_type = 0;
+
+ /*
+ * Get an ID.
+ */
+ if (iso9660->opt.publisher)
+ archive_string_copy(&(iso9660->el_torito.id),
+ &(iso9660->publisher_identifier));
+
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * If a media type is floppy, return its image size.
+ * otherwise return 0.
+ */
+static size_t
+fd_boot_image_size(int media_type)
+{
+ switch (media_type) {
+ case BOOT_MEDIA_1_2M_DISKETTE:
+ return (FD_1_2M_SIZE);
+ case BOOT_MEDIA_1_44M_DISKETTE:
+ return (FD_1_44M_SIZE);
+ case BOOT_MEDIA_2_88M_DISKETTE:
+ return (FD_2_88M_SIZE);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Make a boot catalog image data.
+ */
+static int
+make_boot_catalog(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ unsigned char *block;
+ unsigned char *p;
+ uint16_t sum, *wp;
+
+ block = wb_buffptr(a);
+ memset(block, 0, LOGICAL_BLOCK_SIZE);
+ p = block;
+ /*
+ * Validation Entry
+ */
+ /* Header ID */
+ p[0] = 1;
+ /* Platform ID */
+ p[1] = iso9660->el_torito.platform_id;
+ /* Reserved */
+ p[2] = p[3] = 0;
+ /* ID */
+ if (archive_strlen(&(iso9660->el_torito.id)) > 0)
+ strncpy((char *)p+4, iso9660->el_torito.id.s, 23);
+ p[27] = 0;
+ /* Checksum */
+ p[28] = p[29] = 0;
+ /* Key */
+ p[30] = 0x55;
+ p[31] = 0xAA;
+
+ sum = 0;
+ wp = (uint16_t *)block;
+ while (wp < (uint16_t *)&block[32])
+ sum += archive_le16dec(wp++);
+ set_num_721(&block[28], (~sum) + 1);
+
+ /*
+ * Initial/Default Entry
+ */
+ p = &block[32];
+ /* Boot Indicator */
+ p[0] = 0x88;
+ /* Boot media type */
+ p[1] = iso9660->el_torito.media_type;
+ /* Load Segment */
+ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)
+ set_num_721(&p[2], iso9660->el_torito.boot_load_seg);
+ else
+ set_num_721(&p[2], 0);
+ /* System Type */
+ p[4] = iso9660->el_torito.system_type;
+ /* Unused */
+ p[5] = 0;
+ /* Sector Count */
+ if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)
+ set_num_721(&p[6], iso9660->el_torito.boot_load_size);
+ else
+ set_num_721(&p[6], 1);
+ /* Load RBA */
+ set_num_731(&p[8],
+ iso9660->el_torito.boot->file->content.location);
+ /* Unused */
+ memset(&p[12], 0, 20);
+
+ return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+setup_boot_information(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isoent *np;
+ int64_t size;
+ uint32_t sum;
+ unsigned char buff[4096];
+
+ np = iso9660->el_torito.boot;
+ lseek(iso9660->temp_fd,
+ np->file->content.offset_of_temp + 64, SEEK_SET);
+ size = archive_entry_size(np->file->entry) - 64;
+ if (size <= 0) {
+ archive_set_error(&a->archive, errno,
+ "Boot file(%jd) is too small", (intmax_t)size + 64);
+ return (ARCHIVE_FATAL);
+ }
+ sum = 0;
+ while (size > 0) {
+ size_t rsize;
+ ssize_t i, rs;
+
+ if (size > (int64_t)sizeof(buff))
+ rsize = sizeof(buff);
+ else
+ rsize = (size_t)size;
+
+ rs = read(iso9660->temp_fd, buff, rsize);
+ if (rs <= 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't read temporary file(%jd)",
+ (intmax_t)rs);
+ return (ARCHIVE_FATAL);
+ }
+ for (i = 0; i < rs; i += 4)
+ sum += archive_le32dec(buff + i);
+ size -= rs;
+ }
+ /* Set the location of Primary Volume Descriptor. */
+ set_num_731(buff, SYSTEM_AREA_BLOCK);
+ /* Set the location of the boot file. */
+ set_num_731(buff+4, np->file->content.location);
+ /* Set the size of the boot file. */
+ size = fd_boot_image_size(iso9660->el_torito.media_type);
+ if (size == 0)
+ size = archive_entry_size(np->file->entry);
+ set_num_731(buff+8, (uint32_t)size);
+ /* Set the sum of the boot file. */
+ set_num_731(buff+12, sum);
+ /* Clear reserved bytes. */
+ memset(buff+16, 0, 40);
+
+ /* Overwrite the boot file. */
+ lseek(iso9660->temp_fd,
+ np->file->content.offset_of_temp + 8, SEEK_SET);
+ return (write_to_temp(a, buff, 56));
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+zisofs_init_zstream(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ int r;
+
+ iso9660->zisofs.stream.next_in = NULL;
+ iso9660->zisofs.stream.avail_in = 0;
+ iso9660->zisofs.stream.total_in = 0;
+ iso9660->zisofs.stream.total_out = 0;
+ if (iso9660->zisofs.stream_valid)
+ r = deflateReset(&(iso9660->zisofs.stream));
+ else {
+ r = deflateInit(&(iso9660->zisofs.stream),
+ iso9660->zisofs.compression_level);
+ iso9660->zisofs.stream_valid = 1;
+ }
+ switch (r) {
+ case Z_OK:
+ break;
+ default:
+ case Z_STREAM_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: invalid setup parameter");
+ return (ARCHIVE_FATAL);
+ case Z_MEM_ERROR:
+ archive_set_error(&a->archive, ENOMEM,
+ "Internal error initializing "
+ "compression library");
+ return (ARCHIVE_FATAL);
+ case Z_VERSION_ERROR:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing "
+ "compression library: invalid library version");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+zisofs_init(struct archive_write *a, struct isofile *file)
+{
+ struct iso9660 *iso9660 = a->format_data;
+#ifdef HAVE_ZLIB_H
+ uint64_t tsize;
+ size_t _ceil, bpsize;
+ int r;
+#endif
+
+ iso9660->zisofs.detect_magic = 0;
+ iso9660->zisofs.making = 0;
+
+ if (!iso9660->opt.rr || !iso9660->opt.zisofs)
+ return (ARCHIVE_OK);
+
+ if (archive_entry_size(file->entry) >= 24 &&
+ archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) {
+ /* Acceptable file size for zisofs. */
+ iso9660->zisofs.detect_magic = 1;
+ iso9660->zisofs.magic_cnt = 0;
+ }
+ if (!iso9660->zisofs.detect_magic)
+ return (ARCHIVE_OK);
+
+#ifdef HAVE_ZLIB_H
+ /* The number of Logical Blocks which uncompressed data
+ * will use in iso-image file is the same as the number of
+ * Logical Blocks which zisofs(compressed) data will use
+ * in ISO-image file. It won't reduce iso-image file size. */
+ if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE)
+ return (ARCHIVE_OK);
+
+ /* Initialize compression library */
+ r = zisofs_init_zstream(a);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Mark file->zisofs to create RRIP 'ZF' Use Entry. */
+ file->zisofs.header_size = ZF_HEADER_SIZE >> 2;
+ file->zisofs.log2_bs = ZF_LOG2_BS;
+ file->zisofs.uncompressed_size =
+ (uint32_t)archive_entry_size(file->entry);
+
+ /* Calculate a size of Block Pointers of zisofs. */
+ _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1)
+ >> file->zisofs.log2_bs;
+ iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1;
+ iso9660->zisofs.block_pointers_idx = 0;
+
+ /* Ensure a buffer size used for Block Pointers */
+ bpsize = iso9660->zisofs.block_pointers_cnt *
+ sizeof(iso9660->zisofs.block_pointers[0]);
+ if (iso9660->zisofs.block_pointers_allocated < bpsize) {
+ free(iso9660->zisofs.block_pointers);
+ iso9660->zisofs.block_pointers = malloc(bpsize);
+ if (iso9660->zisofs.block_pointers == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ return (ARCHIVE_FATAL);
+ }
+ iso9660->zisofs.block_pointers_allocated = bpsize;
+ }
+
+ /*
+ * Skip zisofs header and Block Pointers, which we will write
+ * after all compressed data of a file written to the temporary
+ * file.
+ */
+ tsize = ZF_HEADER_SIZE + bpsize;
+ if (write_null(a, (size_t)tsize) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * Initialize some variables to make zisofs.
+ */
+ archive_le32enc(&(iso9660->zisofs.block_pointers[0]),
+ (uint32_t)tsize);
+ iso9660->zisofs.remaining = file->zisofs.uncompressed_size;
+ iso9660->zisofs.making = 1;
+ iso9660->zisofs.allzero = 1;
+ iso9660->zisofs.block_offset = tsize;
+ iso9660->zisofs.total_size = tsize;
+ iso9660->cur_file->cur_content->size = tsize;
+#endif
+
+ return (ARCHIVE_OK);
+}
+
+static void
+zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file = iso9660->cur_file;
+ const unsigned char *p, *endp;
+ const unsigned char *magic_buff;
+ uint32_t uncompressed_size;
+ unsigned char header_size;
+ unsigned char log2_bs;
+ size_t _ceil, doff;
+ uint32_t bst, bed;
+ int magic_max;
+ int64_t entry_size;
+
+ entry_size = archive_entry_size(file->entry);
+ if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size)
+ magic_max = (int)entry_size;
+ else
+ magic_max = sizeof(iso9660->zisofs.magic_buffer);
+
+ if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max)
+ /* It's unnecessary we copy buffer. */
+ magic_buff = buff;
+ else {
+ if (iso9660->zisofs.magic_cnt < magic_max) {
+ size_t l;
+
+ l = sizeof(iso9660->zisofs.magic_buffer)
+ - iso9660->zisofs.magic_cnt;
+ if (l > s)
+ l = s;
+ memcpy(iso9660->zisofs.magic_buffer
+ + iso9660->zisofs.magic_cnt, buff, l);
+ iso9660->zisofs.magic_cnt += (int)l;
+ if (iso9660->zisofs.magic_cnt < magic_max)
+ return;
+ }
+ magic_buff = iso9660->zisofs.magic_buffer;
+ }
+ iso9660->zisofs.detect_magic = 0;
+ p = magic_buff;
+
+ /* Check the magic code of zisofs. */
+ if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)
+ /* This is not zisofs file which made by mkzftree. */
+ return;
+ p += sizeof(zisofs_magic);
+
+ /* Read a zisofs header. */
+ uncompressed_size = archive_le32dec(p);
+ header_size = p[4];
+ log2_bs = p[5];
+ if (uncompressed_size < 24 || header_size != 4 ||
+ log2_bs > 30 || log2_bs < 7)
+ return;/* Invalid or not supported header. */
+
+ /* Calculate a size of Block Pointers of zisofs. */
+ _ceil = (uncompressed_size +
+ (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs;
+ doff = (_ceil + 1) * 4 + 16;
+ if (entry_size < (int64_t)doff)
+ return;/* Invalid data. */
+
+ /* Check every Block Pointer has valid value. */
+ p = magic_buff + 16;
+ endp = magic_buff + magic_max;
+ while (_ceil && p + 8 <= endp) {
+ bst = archive_le32dec(p);
+ if (bst != doff)
+ return;/* Invalid data. */
+ p += 4;
+ bed = archive_le32dec(p);
+ if (bed < bst || bed > entry_size)
+ return;/* Invalid data. */
+ doff += bed - bst;
+ _ceil--;
+ }
+
+ file->zisofs.uncompressed_size = uncompressed_size;
+ file->zisofs.header_size = header_size;
+ file->zisofs.log2_bs = log2_bs;
+
+ /* Disable making a zisofs image. */
+ iso9660->zisofs.making = 0;
+}
+
+#ifdef HAVE_ZLIB_H
+
+/*
+ * Compress data and write it to a temporary file.
+ */
+static int
+zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file = iso9660->cur_file;
+ const unsigned char *b;
+ z_stream *zstrm;
+ size_t avail, csize;
+ int flush, r;
+
+ zstrm = &(iso9660->zisofs.stream);
+ zstrm->next_out = wb_buffptr(a);
+ zstrm->avail_out = (uInt)wb_remaining(a);
+ b = (const unsigned char *)buff;
+ do {
+ avail = ZF_BLOCK_SIZE - zstrm->total_in;
+ if (s < avail) {
+ avail = s;
+ flush = Z_NO_FLUSH;
+ } else
+ flush = Z_FINISH;
+ iso9660->zisofs.remaining -= avail;
+ if (iso9660->zisofs.remaining <= 0)
+ flush = Z_FINISH;
+
+ zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b;
+ zstrm->avail_in = (uInt)avail;
+
+ /*
+ * Check if current data block are all zero.
+ */
+ if (iso9660->zisofs.allzero) {
+ const unsigned char *nonzero = b;
+ const unsigned char *nonzeroend = b + avail;
+
+ while (nonzero < nonzeroend)
+ if (*nonzero++) {
+ iso9660->zisofs.allzero = 0;
+ break;
+ }
+ }
+ b += avail;
+ s -= avail;
+
+ /*
+ * If current data block are all zero, we do not use
+ * compressed data.
+ */
+ if (flush == Z_FINISH && iso9660->zisofs.allzero &&
+ avail + zstrm->total_in == ZF_BLOCK_SIZE) {
+ if (iso9660->zisofs.block_offset !=
+ file->cur_content->size) {
+ int64_t diff;
+
+ r = wb_set_offset(a,
+ file->cur_content->offset_of_temp +
+ iso9660->zisofs.block_offset);
+ if (r != ARCHIVE_OK)
+ return (r);
+ diff = file->cur_content->size -
+ iso9660->zisofs.block_offset;
+ file->cur_content->size -= diff;
+ iso9660->zisofs.total_size -= diff;
+ }
+ zstrm->avail_in = 0;
+ }
+
+ /*
+ * Compress file data.
+ */
+ while (zstrm->avail_in > 0) {
+ csize = zstrm->total_out;
+ r = deflate(zstrm, flush);
+ switch (r) {
+ case Z_OK:
+ case Z_STREAM_END:
+ csize = zstrm->total_out - csize;
+ if (wb_consume(a, csize) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->zisofs.total_size += csize;
+ iso9660->cur_file->cur_content->size += csize;
+ zstrm->next_out = wb_buffptr(a);
+ zstrm->avail_out = (uInt)wb_remaining(a);
+ break;
+ default:
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Compression failed:"
+ " deflate() call returned status %d",
+ r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ if (flush == Z_FINISH) {
+ /*
+ * Save the information of one zisofs block.
+ */
+ iso9660->zisofs.block_pointers_idx ++;
+ archive_le32enc(&(iso9660->zisofs.block_pointers[
+ iso9660->zisofs.block_pointers_idx]),
+ (uint32_t)iso9660->zisofs.total_size);
+ r = zisofs_init_zstream(a);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ iso9660->zisofs.allzero = 1;
+ iso9660->zisofs.block_offset = file->cur_content->size;
+ }
+ } while (s);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+zisofs_finish_entry(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file = iso9660->cur_file;
+ unsigned char buff[16];
+ size_t s;
+ int64_t tail;
+
+ /* Direct temp file stream to zisofs temp file stream. */
+ archive_entry_set_size(file->entry, iso9660->zisofs.total_size);
+
+ /*
+ * Save a file pointer which points the end of current zisofs data.
+ */
+ tail = wb_offset(a);
+
+ /*
+ * Make a header.
+ *
+ * +-----------------+----------------+-----------------+
+ * | Header 16 bytes | Block Pointers | Compressed data |
+ * +-----------------+----------------+-----------------+
+ * 0 16 +X
+ * Block Pointers :
+ * 4 * (((Uncompressed file size + block_size -1) / block_size) + 1)
+ *
+ * Write zisofs header.
+ * Magic number
+ * +----+----+----+----+----+----+----+----+
+ * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 |
+ * +----+----+----+----+----+----+----+----+
+ * 0 1 2 3 4 5 6 7 8
+ *
+ * +------------------------+------------------+
+ * | Uncompressed file size | header_size >> 2 |
+ * +------------------------+------------------+
+ * 8 12 13
+ *
+ * +-----------------+----------------+
+ * | log2 block_size | Reserved(0000) |
+ * +-----------------+----------------+
+ * 13 14 16
+ */
+ memcpy(buff, zisofs_magic, 8);
+ set_num_731(buff+8, file->zisofs.uncompressed_size);
+ buff[12] = file->zisofs.header_size;
+ buff[13] = file->zisofs.log2_bs;
+ buff[14] = buff[15] = 0;/* Reserved */
+
+ /* Move to the right position to write the header. */
+ wb_set_offset(a, file->content.offset_of_temp);
+
+ /* Write the header. */
+ if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * Write zisofs Block Pointers.
+ */
+ s = iso9660->zisofs.block_pointers_cnt *
+ sizeof(iso9660->zisofs.block_pointers[0]);
+ if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s)
+ != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Set a file pointer back to the end of the temporary file. */
+ wb_set_offset(a, tail);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+zisofs_free(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ int ret = ARCHIVE_OK;
+
+ free(iso9660->zisofs.block_pointers);
+ if (iso9660->zisofs.stream_valid &&
+ deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ }
+ iso9660->zisofs.block_pointers = NULL;
+ iso9660->zisofs.stream_valid = 0;
+ return (ret);
+}
+
+struct zisofs_extract {
+ int pz_log2_bs; /* Log2 of block size */
+ uint64_t pz_uncompressed_size;
+ size_t uncompressed_buffer_size;
+
+ unsigned int initialized:1;
+ unsigned int header_passed:1;
+
+ uint32_t pz_offset;
+ unsigned char *block_pointers;
+ size_t block_pointers_size;
+ size_t block_pointers_avail;
+ size_t block_off;
+ uint32_t block_avail;
+
+ z_stream stream;
+ int stream_valid;
+};
+
+static ssize_t
+zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs,
+ const unsigned char *p, size_t bytes)
+{
+ size_t avail = bytes;
+ size_t _ceil, xsize;
+
+ /* Allocate block pointers buffer. */
+ _ceil = (size_t)((zisofs->pz_uncompressed_size +
+ (((int64_t)1) << zisofs->pz_log2_bs) - 1)
+ >> zisofs->pz_log2_bs);
+ xsize = (_ceil + 1) * 4;
+ if (zisofs->block_pointers == NULL) {
+ size_t alloc = ((xsize >> 10) + 1) << 10;
+ zisofs->block_pointers = malloc(alloc);
+ if (zisofs->block_pointers == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for zisofs decompression");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ zisofs->block_pointers_size = xsize;
+
+ /* Allocate uncompressed data buffer. */
+ zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs;
+
+ /*
+ * Read the file header, and check the magic code of zisofs.
+ */
+ if (!zisofs->header_passed) {
+ int err = 0;
+ if (avail < 16) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)
+ err = 1;
+ else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size)
+ err = 1;
+ else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs)
+ err = 1;
+ if (err) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs file body");
+ return (ARCHIVE_FATAL);
+ }
+ avail -= 16;
+ p += 16;
+ zisofs->header_passed = 1;
+ }
+
+ /*
+ * Read block pointers.
+ */
+ if (zisofs->header_passed &&
+ zisofs->block_pointers_avail < zisofs->block_pointers_size) {
+ xsize = zisofs->block_pointers_size
+ - zisofs->block_pointers_avail;
+ if (avail < xsize)
+ xsize = avail;
+ memcpy(zisofs->block_pointers
+ + zisofs->block_pointers_avail, p, xsize);
+ zisofs->block_pointers_avail += xsize;
+ avail -= xsize;
+ if (zisofs->block_pointers_avail
+ == zisofs->block_pointers_size) {
+ /* We've got all block pointers and initialize
+ * related variables. */
+ zisofs->block_off = 0;
+ zisofs->block_avail = 0;
+ /* Complete a initialization */
+ zisofs->initialized = 1;
+ }
+ }
+ return ((ssize_t)avail);
+}
+
+static ssize_t
+zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs,
+ const unsigned char *p, size_t bytes)
+{
+ size_t avail;
+ int r;
+
+ if (!zisofs->initialized) {
+ ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes);
+ if (rs < 0)
+ return (rs);
+ if (!zisofs->initialized) {
+ /* We need more data. */
+ zisofs->pz_offset += (uint32_t)bytes;
+ return (bytes);
+ }
+ avail = rs;
+ p += bytes - avail;
+ } else
+ avail = bytes;
+
+ /*
+ * Get block offsets from block pointers.
+ */
+ if (zisofs->block_avail == 0) {
+ uint32_t bst, bed;
+
+ if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
+ /* There isn't a pair of offsets. */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers");
+ return (ARCHIVE_FATAL);
+ }
+ bst = archive_le32dec(
+ zisofs->block_pointers + zisofs->block_off);
+ if (bst != zisofs->pz_offset + (bytes - avail)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers(cannot seek)");
+ return (ARCHIVE_FATAL);
+ }
+ bed = archive_le32dec(
+ zisofs->block_pointers + zisofs->block_off + 4);
+ if (bed < bst) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Illegal zisofs block pointers");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->block_avail = bed - bst;
+ zisofs->block_off += 4;
+
+ /* Initialize compression library for new block. */
+ if (zisofs->stream_valid)
+ r = inflateReset(&zisofs->stream);
+ else
+ r = inflateInit(&zisofs->stream);
+ if (r != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't initialize zisofs decompression.");
+ return (ARCHIVE_FATAL);
+ }
+ zisofs->stream_valid = 1;
+ zisofs->stream.total_in = 0;
+ zisofs->stream.total_out = 0;
+ }
+
+ /*
+ * Make uncompressed data.
+ */
+ if (zisofs->block_avail == 0) {
+ /*
+ * It's basically 32K bytes NUL data.
+ */
+ unsigned char *wb;
+ size_t size, wsize;
+
+ size = zisofs->uncompressed_buffer_size;
+ while (size) {
+ wb = wb_buffptr(a);
+ if (size > wb_remaining(a))
+ wsize = wb_remaining(a);
+ else
+ wsize = size;
+ memset(wb, 0, wsize);
+ r = wb_consume(a, wsize);
+ if (r < 0)
+ return (r);
+ size -= wsize;
+ }
+ } else {
+ zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;
+ if (avail > zisofs->block_avail)
+ zisofs->stream.avail_in = zisofs->block_avail;
+ else
+ zisofs->stream.avail_in = (uInt)avail;
+ zisofs->stream.next_out = wb_buffptr(a);
+ zisofs->stream.avail_out = (uInt)wb_remaining(a);
+
+ r = inflate(&zisofs->stream, 0);
+ switch (r) {
+ case Z_OK: /* Decompressor made some progress.*/
+ case Z_STREAM_END: /* Found end of stream. */
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "zisofs decompression failed (%d)", r);
+ return (ARCHIVE_FATAL);
+ }
+ avail -= zisofs->stream.next_in - p;
+ zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);
+ r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out);
+ if (r < 0)
+ return (r);
+ }
+ zisofs->pz_offset += (uint32_t)bytes;
+ return (bytes - avail);
+}
+
+static int
+zisofs_rewind_boot_file(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+ struct isofile *file;
+ unsigned char *rbuff;
+ ssize_t r;
+ size_t remaining, rbuff_size;
+ struct zisofs_extract zext;
+ int64_t read_offset, write_offset, new_offset;
+ int fd, ret = ARCHIVE_OK;
+
+ file = iso9660->el_torito.boot->file;
+ /*
+ * There is nothing to do if this boot file does not have
+ * zisofs header.
+ */
+ if (file->zisofs.header_size == 0)
+ return (ARCHIVE_OK);
+
+ /*
+ * Uncompress the zisofs'ed file contents.
+ */
+ memset(&zext, 0, sizeof(zext));
+ zext.pz_uncompressed_size = file->zisofs.uncompressed_size;
+ zext.pz_log2_bs = file->zisofs.log2_bs;
+
+ fd = iso9660->temp_fd;
+ new_offset = wb_offset(a);
+ read_offset = file->content.offset_of_temp;
+ remaining = (size_t)file->content.size;
+ if (remaining > 1024 * 32)
+ rbuff_size = 1024 * 32;
+ else
+ rbuff_size = remaining;
+
+ rbuff = malloc(rbuff_size);
+ if (rbuff == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ while (remaining) {
+ size_t rsize;
+ ssize_t rs;
+
+ /* Get the current file pointer. */
+ write_offset = lseek(fd, 0, SEEK_CUR);
+
+ /* Change the file pointer to read. */
+ lseek(fd, read_offset, SEEK_SET);
+
+ rsize = rbuff_size;
+ if (rsize > remaining)
+ rsize = remaining;
+ rs = read(iso9660->temp_fd, rbuff, rsize);
+ if (rs <= 0) {
+ archive_set_error(&a->archive, errno,
+ "Can't read temporary file(%jd)", (intmax_t)rs);
+ ret = ARCHIVE_FATAL;
+ break;
+ }
+ remaining -= rs;
+ read_offset += rs;
+
+ /* Put the file pointer back to write. */
+ lseek(fd, write_offset, SEEK_SET);
+
+ r = zisofs_extract(a, &zext, rbuff, rs);
+ if (r < 0) {
+ ret = (int)r;
+ break;
+ }
+ }
+
+ if (ret == ARCHIVE_OK) {
+ /*
+ * Change the boot file content from zisofs'ed data
+ * to plain data.
+ */
+ file->content.offset_of_temp = new_offset;
+ file->content.size = file->zisofs.uncompressed_size;
+ archive_entry_set_size(file->entry, file->content.size);
+ /* Set to be no zisofs. */
+ file->zisofs.header_size = 0;
+ file->zisofs.log2_bs = 0;
+ file->zisofs.uncompressed_size = 0;
+ r = wb_write_padding_to_temp(a, file->content.size);
+ if (r < 0)
+ ret = ARCHIVE_FATAL;
+ }
+
+ /*
+ * Free the resource we used in this function only.
+ */
+ free(rbuff);
+ free(zext.block_pointers);
+ if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ ret = ARCHIVE_FATAL;
+ }
+
+ return (ret);
+}
+
+#else
+
+static int
+zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ (void)buff; /* UNUSED */
+ (void)s; /* UNUSED */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programming error");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+zisofs_rewind_boot_file(struct archive_write *a)
+{
+ struct iso9660 *iso9660 = a->format_data;
+
+ if (iso9660->el_torito.boot->file->zisofs.header_size != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "We cannot extract the zisofs imaged boot file;"
+ " this may not boot in being zisofs imaged");
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+zisofs_finish_entry(struct archive_write *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+static int
+zisofs_free(struct archive_write *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_mtree.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_mtree.c
new file mode 100644
index 0000000000..619b7714ee
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_mtree.c
@@ -0,0 +1,2217 @@
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_mtree.c 201171 2009-12-29 06:39:07Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#define INDENTNAMELEN 15
+#define MAXLINELEN 80
+#define SET_KEYS \
+ (F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME)
+
+struct attr_counter {
+ struct attr_counter *prev;
+ struct attr_counter *next;
+ struct mtree_entry *m_entry;
+ int count;
+};
+
+struct att_counter_set {
+ struct attr_counter *uid_list;
+ struct attr_counter *gid_list;
+ struct attr_counter *mode_list;
+ struct attr_counter *flags_list;
+};
+
+struct mtree_chain {
+ struct mtree_entry *first;
+ struct mtree_entry **last;
+};
+
+/*
+ * The Data only for a directory file.
+ */
+struct dir_info {
+ struct archive_rb_tree rbtree;
+ struct mtree_chain children;
+ struct mtree_entry *chnext;
+ int virtual;
+};
+
+/*
+ * The Data only for a regular file.
+ */
+struct reg_info {
+ int compute_sum;
+ uint32_t crc;
+ struct ae_digest digest;
+};
+
+struct mtree_entry {
+ struct archive_rb_node rbnode;
+ struct mtree_entry *next;
+ struct mtree_entry *parent;
+ struct dir_info *dir_info;
+ struct reg_info *reg_info;
+
+ struct archive_string parentdir;
+ struct archive_string basename;
+ struct archive_string pathname;
+ struct archive_string symlink;
+ struct archive_string uname;
+ struct archive_string gname;
+ struct archive_string fflags_text;
+ unsigned int nlink;
+ mode_t filetype;
+ mode_t mode;
+ int64_t size;
+ int64_t uid;
+ int64_t gid;
+ time_t mtime;
+ long mtime_nsec;
+ unsigned long fflags_set;
+ unsigned long fflags_clear;
+ dev_t rdevmajor;
+ dev_t rdevminor;
+ dev_t devmajor;
+ dev_t devminor;
+ int64_t ino;
+};
+
+struct mtree_writer {
+ struct mtree_entry *mtree_entry;
+ struct mtree_entry *root;
+ struct mtree_entry *cur_dirent;
+ struct archive_string cur_dirstr;
+ struct mtree_chain file_list;
+
+ struct archive_string ebuf;
+ struct archive_string buf;
+ int first;
+ uint64_t entry_bytes_remaining;
+
+ /*
+ * Set global value.
+ */
+ struct {
+ int processing;
+ mode_t type;
+ int keys;
+ int64_t uid;
+ int64_t gid;
+ mode_t mode;
+ unsigned long fflags_set;
+ unsigned long fflags_clear;
+ } set;
+ struct att_counter_set acs;
+ int classic;
+ int depth;
+
+ /* check sum */
+ int compute_sum;
+ uint32_t crc;
+ uint64_t crc_len;
+#ifdef ARCHIVE_HAS_MD5
+ archive_md5_ctx md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ archive_rmd160_ctx rmd160ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ archive_sha1_ctx sha1ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ archive_sha256_ctx sha256ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ archive_sha384_ctx sha384ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ archive_sha512_ctx sha512ctx;
+#endif
+ /* Keyword options */
+ int keys;
+#define F_CKSUM 0x00000001 /* checksum */
+#define F_DEV 0x00000002 /* device type */
+#define F_DONE 0x00000004 /* directory done */
+#define F_FLAGS 0x00000008 /* file flags */
+#define F_GID 0x00000010 /* gid */
+#define F_GNAME 0x00000020 /* group name */
+#define F_IGN 0x00000040 /* ignore */
+#define F_MAGIC 0x00000080 /* name has magic chars */
+#define F_MD5 0x00000100 /* MD5 digest */
+#define F_MODE 0x00000200 /* mode */
+#define F_NLINK 0x00000400 /* number of links */
+#define F_NOCHANGE 0x00000800 /* If owner/mode "wrong", do
+ * not change */
+#define F_OPT 0x00001000 /* existence optional */
+#define F_RMD160 0x00002000 /* RIPEMD160 digest */
+#define F_SHA1 0x00004000 /* SHA-1 digest */
+#define F_SIZE 0x00008000 /* size */
+#define F_SLINK 0x00010000 /* symbolic link */
+#define F_TAGS 0x00020000 /* tags */
+#define F_TIME 0x00040000 /* modification time */
+#define F_TYPE 0x00080000 /* file type */
+#define F_UID 0x00100000 /* uid */
+#define F_UNAME 0x00200000 /* user name */
+#define F_VISIT 0x00400000 /* file visited */
+#define F_SHA256 0x00800000 /* SHA-256 digest */
+#define F_SHA384 0x01000000 /* SHA-384 digest */
+#define F_SHA512 0x02000000 /* SHA-512 digest */
+#define F_INO 0x04000000 /* inode number */
+#define F_RESDEV 0x08000000 /* device ID on which the
+ * entry resides */
+
+ /* Options */
+ int dironly; /* If it is set, ignore all files except
+ * directory files, like mtree(8) -d option. */
+ int indent; /* If it is set, indent output data. */
+ int output_global_set; /* If it is set, use /set keyword to set
+ * global values. When generating mtree
+ * classic format, it is set by default. */
+};
+
+#define DEFAULT_KEYS (F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\
+ | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\
+ | F_UNAME)
+#define attr_counter_set_reset attr_counter_set_free
+
+static void attr_counter_free(struct attr_counter **);
+static int attr_counter_inc(struct attr_counter **, struct attr_counter *,
+ struct attr_counter *, struct mtree_entry *);
+static struct attr_counter * attr_counter_new(struct mtree_entry *,
+ struct attr_counter *);
+static int attr_counter_set_collect(struct mtree_writer *,
+ struct mtree_entry *);
+static void attr_counter_set_free(struct mtree_writer *);
+static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);
+static int mtree_entry_add_child_tail(struct mtree_entry *,
+ struct mtree_entry *);
+static int mtree_entry_create_virtual_dir(struct archive_write *, const char *,
+ struct mtree_entry **);
+static int mtree_entry_cmp_node(const struct archive_rb_node *,
+ const struct archive_rb_node *);
+static int mtree_entry_cmp_key(const struct archive_rb_node *, const void *);
+static int mtree_entry_exchange_same_entry(struct archive_write *,
+ struct mtree_entry *, struct mtree_entry *);
+static void mtree_entry_free(struct mtree_entry *);
+static int mtree_entry_new(struct archive_write *, struct archive_entry *,
+ struct mtree_entry **);
+static void mtree_entry_register_free(struct mtree_writer *);
+static void mtree_entry_register_init(struct mtree_writer *);
+static int mtree_entry_setup_filenames(struct archive_write *,
+ struct mtree_entry *, struct archive_entry *);
+static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);
+static void sum_init(struct mtree_writer *);
+static void sum_update(struct mtree_writer *, const void *, size_t);
+static void sum_final(struct mtree_writer *, struct reg_info *);
+static void sum_write(struct archive_string *, struct reg_info *);
+static int write_mtree_entry(struct archive_write *, struct mtree_entry *);
+static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);
+
+#define COMPUTE_CRC(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
+static const uint32_t crctab[] = {
+ 0x0,
+ 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+ 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+ 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+ 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+ 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+ 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+ 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+ 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+ 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+ 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+ 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+ 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+ 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+ 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+ 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+ 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+ 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+ 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+ 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+ 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+ 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+ 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+ 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+ 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+ 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+ 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+ 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+ 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+ 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+ 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+ 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+ 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+ 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+ 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+ 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+ 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+ 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+ 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+static const unsigned char safe_char[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+ /* !"$%&'()*+,-./ EXCLUSION:0x20( ) 0x23(#) */
+ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+ /* 0123456789:;<>? EXCLUSION:0x3d(=) */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
+ /* @ABCDEFGHIJKLMNO */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+ /* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\) */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */
+ /* `abcdefghijklmno */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+ /* pqrstuvwxyz{|}~ */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static void
+mtree_quote(struct archive_string *s, const char *str)
+{
+ const char *start;
+ char buf[4];
+ unsigned char c;
+
+ for (start = str; *str != '\0'; ++str) {
+ if (safe_char[*(const unsigned char *)str])
+ continue;
+ if (start != str)
+ archive_strncat(s, start, str - start);
+ c = (unsigned char)*str;
+ buf[0] = '\\';
+ buf[1] = (c / 64) + '0';
+ buf[2] = (c / 8 % 8) + '0';
+ buf[3] = (c % 8) + '0';
+ archive_strncat(s, buf, 4);
+ start = str + 1;
+ }
+
+ if (start != str)
+ archive_strncat(s, start, str - start);
+}
+
+/*
+ * Indent a line as the mtree utility does so it is readable for people.
+ */
+static void
+mtree_indent(struct mtree_writer *mtree)
+{
+ int i, fn, nd, pd;
+ const char *r, *s, *x;
+
+ if (mtree->classic) {
+ if (mtree->indent) {
+ nd = 0;
+ pd = mtree->depth * 4;
+ } else {
+ nd = mtree->depth?4:0;
+ pd = 0;
+ }
+ } else
+ nd = pd = 0;
+ fn = 1;
+ s = r = mtree->ebuf.s;
+ x = NULL;
+ while (*r == ' ')
+ r++;
+ while ((r = strchr(r, ' ')) != NULL) {
+ if (fn) {
+ fn = 0;
+ for (i = 0; i < nd + pd; i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ archive_strncat(&mtree->buf, s, r - s);
+ if (nd + (r -s) > INDENTNAMELEN) {
+ archive_strncat(&mtree->buf, " \\\n", 3);
+ for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ } else {
+ for (i = (int)(r -s + nd);
+ i < (INDENTNAMELEN + 1); i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ }
+ s = ++r;
+ x = NULL;
+ continue;
+ }
+ if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN)
+ x = r++;
+ else {
+ if (x == NULL)
+ x = r;
+ archive_strncat(&mtree->buf, s, x - s);
+ archive_strncat(&mtree->buf, " \\\n", 3);
+ for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ s = r = ++x;
+ x = NULL;
+ }
+ }
+ if (fn) {
+ for (i = 0; i < nd + pd; i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ archive_strcat(&mtree->buf, s);
+ s += strlen(s);
+ }
+ if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) {
+ /* Last keyword is longer. */
+ archive_strncat(&mtree->buf, s, x - s);
+ archive_strncat(&mtree->buf, " \\\n", 3);
+ for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ s = ++x;
+ }
+ archive_strcat(&mtree->buf, s);
+ archive_string_empty(&mtree->ebuf);
+}
+
+/*
+ * Write /set keyword.
+ * Set the most used value of uid, gid, mode and fflags, which are
+ * collected by the attr_counter_set_collect() function.
+ */
+static void
+write_global(struct mtree_writer *mtree)
+{
+ struct archive_string setstr;
+ struct archive_string unsetstr;
+ struct att_counter_set *acs;
+ int keys, oldkeys, effkeys;
+
+ archive_string_init(&setstr);
+ archive_string_init(&unsetstr);
+ keys = mtree->keys & SET_KEYS;
+ oldkeys = mtree->set.keys;
+ effkeys = keys;
+ acs = &mtree->acs;
+ if (mtree->set.processing) {
+ /*
+ * Check if the global data needs updating.
+ */
+ effkeys &= ~F_TYPE;
+ if (acs->uid_list == NULL)
+ effkeys &= ~(F_UNAME | F_UID);
+ else if (oldkeys & (F_UNAME | F_UID)) {
+ if (acs->uid_list->count < 2 ||
+ mtree->set.uid == acs->uid_list->m_entry->uid)
+ effkeys &= ~(F_UNAME | F_UID);
+ }
+ if (acs->gid_list == NULL)
+ effkeys &= ~(F_GNAME | F_GID);
+ else if (oldkeys & (F_GNAME | F_GID)) {
+ if (acs->gid_list->count < 2 ||
+ mtree->set.gid == acs->gid_list->m_entry->gid)
+ effkeys &= ~(F_GNAME | F_GID);
+ }
+ if (acs->mode_list == NULL)
+ effkeys &= ~F_MODE;
+ else if (oldkeys & F_MODE) {
+ if (acs->mode_list->count < 2 ||
+ mtree->set.mode == acs->mode_list->m_entry->mode)
+ effkeys &= ~F_MODE;
+ }
+ if (acs->flags_list == NULL)
+ effkeys &= ~F_FLAGS;
+ else if ((oldkeys & F_FLAGS) != 0) {
+ if (acs->flags_list->count < 2 ||
+ (acs->flags_list->m_entry->fflags_set ==
+ mtree->set.fflags_set &&
+ acs->flags_list->m_entry->fflags_clear ==
+ mtree->set.fflags_clear))
+ effkeys &= ~F_FLAGS;
+ }
+ } else {
+ if (acs->uid_list == NULL)
+ keys &= ~(F_UNAME | F_UID);
+ if (acs->gid_list == NULL)
+ keys &= ~(F_GNAME | F_GID);
+ if (acs->mode_list == NULL)
+ keys &= ~F_MODE;
+ if (acs->flags_list == NULL)
+ keys &= ~F_FLAGS;
+ }
+ if ((keys & effkeys & F_TYPE) != 0) {
+ if (mtree->dironly) {
+ archive_strcat(&setstr, " type=dir");
+ mtree->set.type = AE_IFDIR;
+ } else {
+ archive_strcat(&setstr, " type=file");
+ mtree->set.type = AE_IFREG;
+ }
+ }
+ if ((keys & effkeys & F_UNAME) != 0) {
+ if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) {
+ archive_strcat(&setstr, " uname=");
+ mtree_quote(&setstr, acs->uid_list->m_entry->uname.s);
+ } else {
+ keys &= ~F_UNAME;
+ if ((oldkeys & F_UNAME) != 0)
+ archive_strcat(&unsetstr, " uname");
+ }
+ }
+ if ((keys & effkeys & F_UID) != 0) {
+ mtree->set.uid = acs->uid_list->m_entry->uid;
+ archive_string_sprintf(&setstr, " uid=%jd",
+ (intmax_t)mtree->set.uid);
+ }
+ if ((keys & effkeys & F_GNAME) != 0) {
+ if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) {
+ archive_strcat(&setstr, " gname=");
+ mtree_quote(&setstr, acs->gid_list->m_entry->gname.s);
+ } else {
+ keys &= ~F_GNAME;
+ if ((oldkeys & F_GNAME) != 0)
+ archive_strcat(&unsetstr, " gname");
+ }
+ }
+ if ((keys & effkeys & F_GID) != 0) {
+ mtree->set.gid = acs->gid_list->m_entry->gid;
+ archive_string_sprintf(&setstr, " gid=%jd",
+ (intmax_t)mtree->set.gid);
+ }
+ if ((keys & effkeys & F_MODE) != 0) {
+ mtree->set.mode = acs->mode_list->m_entry->mode;
+ archive_string_sprintf(&setstr, " mode=%o",
+ (unsigned int)mtree->set.mode);
+ }
+ if ((keys & effkeys & F_FLAGS) != 0) {
+ if (archive_strlen(
+ &(acs->flags_list->m_entry->fflags_text)) > 0) {
+ archive_strcat(&setstr, " flags=");
+ mtree_quote(&setstr,
+ acs->flags_list->m_entry->fflags_text.s);
+ mtree->set.fflags_set =
+ acs->flags_list->m_entry->fflags_set;
+ mtree->set.fflags_clear =
+ acs->flags_list->m_entry->fflags_clear;
+ } else {
+ keys &= ~F_FLAGS;
+ if ((oldkeys & F_FLAGS) != 0)
+ archive_strcat(&unsetstr, " flags");
+ }
+ }
+ if (unsetstr.length > 0)
+ archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);
+ archive_string_free(&unsetstr);
+ if (setstr.length > 0)
+ archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s);
+ archive_string_free(&setstr);
+ mtree->set.keys = keys;
+ mtree->set.processing = 1;
+}
+
+static struct attr_counter *
+attr_counter_new(struct mtree_entry *me, struct attr_counter *prev)
+{
+ struct attr_counter *ac;
+
+ ac = malloc(sizeof(*ac));
+ if (ac != NULL) {
+ ac->prev = prev;
+ ac->next = NULL;
+ ac->count = 1;
+ ac->m_entry = me;
+ }
+ return (ac);
+}
+
+static void
+attr_counter_free(struct attr_counter **top)
+{
+ struct attr_counter *ac, *tac;
+
+ if (*top == NULL)
+ return;
+ ac = *top;
+ while (ac != NULL) {
+ tac = ac->next;
+ free(ac);
+ ac = tac;
+ }
+ *top = NULL;
+}
+
+static int
+attr_counter_inc(struct attr_counter **top, struct attr_counter *ac,
+ struct attr_counter *last, struct mtree_entry *me)
+{
+ struct attr_counter *pac;
+
+ if (ac != NULL) {
+ ac->count++;
+ if (*top == ac || ac->prev->count >= ac->count)
+ return (0);
+ for (pac = ac->prev; pac; pac = pac->prev) {
+ if (pac->count >= ac->count)
+ break;
+ }
+ ac->prev->next = ac->next;
+ if (ac->next != NULL)
+ ac->next->prev = ac->prev;
+ if (pac != NULL) {
+ ac->prev = pac;
+ ac->next = pac->next;
+ pac->next = ac;
+ if (ac->next != NULL)
+ ac->next->prev = ac;
+ } else {
+ ac->prev = NULL;
+ ac->next = *top;
+ *top = ac;
+ ac->next->prev = ac;
+ }
+ } else if (last != NULL) {
+ ac = attr_counter_new(me, last);
+ if (ac == NULL)
+ return (-1);
+ last->next = ac;
+ }
+ return (0);
+}
+
+/*
+ * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set.
+ */
+static int
+attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
+{
+ struct attr_counter *ac, *last;
+ struct att_counter_set *acs = &mtree->acs;
+ int keys = mtree->keys;
+
+ if (keys & (F_UNAME | F_UID)) {
+ if (acs->uid_list == NULL) {
+ acs->uid_list = attr_counter_new(me, NULL);
+ if (acs->uid_list == NULL)
+ return (-1);
+ } else {
+ last = NULL;
+ for (ac = acs->uid_list; ac; ac = ac->next) {
+ if (ac->m_entry->uid == me->uid)
+ break;
+ last = ac;
+ }
+ if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0)
+ return (-1);
+ }
+ }
+ if (keys & (F_GNAME | F_GID)) {
+ if (acs->gid_list == NULL) {
+ acs->gid_list = attr_counter_new(me, NULL);
+ if (acs->gid_list == NULL)
+ return (-1);
+ } else {
+ last = NULL;
+ for (ac = acs->gid_list; ac; ac = ac->next) {
+ if (ac->m_entry->gid == me->gid)
+ break;
+ last = ac;
+ }
+ if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0)
+ return (-1);
+ }
+ }
+ if (keys & F_MODE) {
+ if (acs->mode_list == NULL) {
+ acs->mode_list = attr_counter_new(me, NULL);
+ if (acs->mode_list == NULL)
+ return (-1);
+ } else {
+ last = NULL;
+ for (ac = acs->mode_list; ac; ac = ac->next) {
+ if (ac->m_entry->mode == me->mode)
+ break;
+ last = ac;
+ }
+ if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0)
+ return (-1);
+ }
+ }
+ if (keys & F_FLAGS) {
+ if (acs->flags_list == NULL) {
+ acs->flags_list = attr_counter_new(me, NULL);
+ if (acs->flags_list == NULL)
+ return (-1);
+ } else {
+ last = NULL;
+ for (ac = acs->flags_list; ac; ac = ac->next) {
+ if (ac->m_entry->fflags_set == me->fflags_set &&
+ ac->m_entry->fflags_clear ==
+ me->fflags_clear)
+ break;
+ last = ac;
+ }
+ if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0)
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+attr_counter_set_free(struct mtree_writer *mtree)
+{
+ struct att_counter_set *acs = &mtree->acs;
+
+ attr_counter_free(&acs->uid_list);
+ attr_counter_free(&acs->gid_list);
+ attr_counter_free(&acs->mode_list);
+ attr_counter_free(&acs->flags_list);
+}
+
+static int
+get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)
+{
+ int keys;
+
+ keys = mtree->keys;
+
+ /*
+ * If a keyword has been set by /set, we do not need to
+ * output it.
+ */
+ if (mtree->set.keys == 0)
+ return (keys);/* /set is not used. */
+
+ if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&
+ mtree->set.gid == me->gid)
+ keys &= ~(F_GNAME | F_GID);
+ if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&
+ mtree->set.uid == me->uid)
+ keys &= ~(F_UNAME | F_UID);
+ if (mtree->set.keys & F_FLAGS) {
+ if (mtree->set.fflags_set == me->fflags_set &&
+ mtree->set.fflags_clear == me->fflags_clear)
+ keys &= ~F_FLAGS;
+ }
+ if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode)
+ keys &= ~F_MODE;
+
+ switch (me->filetype) {
+ case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
+ case AE_IFBLK: case AE_IFIFO:
+ break;
+ case AE_IFDIR:
+ if ((mtree->set.keys & F_TYPE) != 0 &&
+ mtree->set.type == AE_IFDIR)
+ keys &= ~F_TYPE;
+ break;
+ case AE_IFREG:
+ default: /* Handle unknown file types as regular files. */
+ if ((mtree->set.keys & F_TYPE) != 0 &&
+ mtree->set.type == AE_IFREG)
+ keys &= ~F_TYPE;
+ break;
+ }
+
+ return (keys);
+}
+
+static int
+mtree_entry_new(struct archive_write *a, struct archive_entry *entry,
+ struct mtree_entry **m_entry)
+{
+ struct mtree_entry *me;
+ const char *s;
+ int r;
+ static const struct archive_rb_tree_ops rb_ops = {
+ mtree_entry_cmp_node, mtree_entry_cmp_key
+ };
+
+ me = calloc(1, sizeof(*me));
+ if (me == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a mtree entry");
+ *m_entry = NULL;
+ return (ARCHIVE_FATAL);
+ }
+
+ r = mtree_entry_setup_filenames(a, me, entry);
+ if (r < ARCHIVE_WARN) {
+ mtree_entry_free(me);
+ *m_entry = NULL;
+ return (r);
+ }
+
+ if ((s = archive_entry_symlink(entry)) != NULL)
+ archive_strcpy(&me->symlink, s);
+ me->nlink = archive_entry_nlink(entry);
+ me->filetype = archive_entry_filetype(entry);
+ me->mode = archive_entry_mode(entry) & 07777;
+ me->uid = archive_entry_uid(entry);
+ me->gid = archive_entry_gid(entry);
+ if ((s = archive_entry_uname(entry)) != NULL)
+ archive_strcpy(&me->uname, s);
+ if ((s = archive_entry_gname(entry)) != NULL)
+ archive_strcpy(&me->gname, s);
+ if ((s = archive_entry_fflags_text(entry)) != NULL)
+ archive_strcpy(&me->fflags_text, s);
+ archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear);
+ me->mtime = archive_entry_mtime(entry);
+ me->mtime_nsec = archive_entry_mtime_nsec(entry);
+ me->rdevmajor = archive_entry_rdevmajor(entry);
+ me->rdevminor = archive_entry_rdevminor(entry);
+ me->devmajor = archive_entry_devmajor(entry);
+ me->devminor = archive_entry_devminor(entry);
+ me->ino = archive_entry_ino(entry);
+ me->size = archive_entry_size(entry);
+ if (me->filetype == AE_IFDIR) {
+ me->dir_info = calloc(1, sizeof(*me->dir_info));
+ if (me->dir_info == NULL) {
+ mtree_entry_free(me);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a mtree entry");
+ *m_entry = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ __archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops);
+ me->dir_info->children.first = NULL;
+ me->dir_info->children.last = &(me->dir_info->children.first);
+ me->dir_info->chnext = NULL;
+ } else if (me->filetype == AE_IFREG) {
+ me->reg_info = calloc(1, sizeof(*me->reg_info));
+ if (me->reg_info == NULL) {
+ mtree_entry_free(me);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for a mtree entry");
+ *m_entry = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ me->reg_info->compute_sum = 0;
+ }
+
+ *m_entry = me;
+ return (ARCHIVE_OK);
+}
+
+static void
+mtree_entry_free(struct mtree_entry *me)
+{
+ archive_string_free(&me->parentdir);
+ archive_string_free(&me->basename);
+ archive_string_free(&me->pathname);
+ archive_string_free(&me->symlink);
+ archive_string_free(&me->uname);
+ archive_string_free(&me->gname);
+ archive_string_free(&me->fflags_text);
+ free(me->dir_info);
+ free(me->reg_info);
+ free(me);
+}
+
+static int
+archive_write_mtree_header(struct archive_write *a,
+ struct archive_entry *entry)
+{
+ struct mtree_writer *mtree= a->format_data;
+ struct mtree_entry *mtree_entry;
+ int r, r2;
+
+ if (mtree->first) {
+ mtree->first = 0;
+ archive_strcat(&mtree->buf, "#mtree\n");
+ if ((mtree->keys & SET_KEYS) == 0)
+ mtree->output_global_set = 0;/* Disabled. */
+ }
+
+ mtree->entry_bytes_remaining = archive_entry_size(entry);
+
+ /* While directory only mode, we do not handle non directory files. */
+ if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR)
+ return (ARCHIVE_OK);
+
+ r2 = mtree_entry_new(a, entry, &mtree_entry);
+ if (r2 < ARCHIVE_WARN)
+ return (r2);
+ r = mtree_entry_tree_add(a, &mtree_entry);
+ if (r < ARCHIVE_WARN) {
+ mtree_entry_free(mtree_entry);
+ return (r);
+ }
+ mtree->mtree_entry = mtree_entry;
+
+ /* If the current file is a regular file, we have to
+ * compute the sum of its content.
+ * Initialize a bunch of checksum context. */
+ if (mtree_entry->reg_info)
+ sum_init(mtree);
+
+ return (r2);
+}
+
+static int
+write_mtree_entry(struct archive_write *a, struct mtree_entry *me)
+{
+ struct mtree_writer *mtree = a->format_data;
+ struct archive_string *str;
+ int keys, ret;
+
+ if (me->dir_info) {
+ if (mtree->classic) {
+ /*
+ * Output a comment line to describe the full
+ * pathname of the entry as mtree utility does
+ * while generating classic format.
+ */
+ if (!mtree->dironly)
+ archive_strappend_char(&mtree->buf, '\n');
+ if (me->parentdir.s)
+ archive_string_sprintf(&mtree->buf,
+ "# %s/%s\n",
+ me->parentdir.s, me->basename.s);
+ else
+ archive_string_sprintf(&mtree->buf,
+ "# %s\n",
+ me->basename.s);
+ }
+ if (mtree->output_global_set)
+ write_global(mtree);
+ }
+ archive_string_empty(&mtree->ebuf);
+ str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf;
+
+ if (!mtree->classic && me->parentdir.s) {
+ /*
+ * If generating format is not classic one(v1), output
+ * a full pathname.
+ */
+ mtree_quote(str, me->parentdir.s);
+ archive_strappend_char(str, '/');
+ }
+ mtree_quote(str, me->basename.s);
+
+ keys = get_global_set_keys(mtree, me);
+ if ((keys & F_NLINK) != 0 &&
+ me->nlink != 1 && me->filetype != AE_IFDIR)
+ archive_string_sprintf(str, " nlink=%u", me->nlink);
+
+ if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) {
+ archive_strcat(str, " gname=");
+ mtree_quote(str, me->gname.s);
+ }
+ if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) {
+ archive_strcat(str, " uname=");
+ mtree_quote(str, me->uname.s);
+ }
+ if ((keys & F_FLAGS) != 0) {
+ if (archive_strlen(&me->fflags_text) > 0) {
+ archive_strcat(str, " flags=");
+ mtree_quote(str, me->fflags_text.s);
+ } else if (mtree->set.processing &&
+ (mtree->set.keys & F_FLAGS) != 0)
+ /* Overwrite the global parameter. */
+ archive_strcat(str, " flags=none");
+ }
+ if ((keys & F_TIME) != 0)
+ archive_string_sprintf(str, " time=%jd.%jd",
+ (intmax_t)me->mtime, (intmax_t)me->mtime_nsec);
+ if ((keys & F_MODE) != 0)
+ archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode);
+ if ((keys & F_GID) != 0)
+ archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid);
+ if ((keys & F_UID) != 0)
+ archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid);
+
+ if ((keys & F_INO) != 0)
+ archive_string_sprintf(str, " inode=%jd", (intmax_t)me->ino);
+ if ((keys & F_RESDEV) != 0) {
+ archive_string_sprintf(str,
+ " resdevice=native,%ju,%ju",
+ (uintmax_t)me->devmajor,
+ (uintmax_t)me->devminor);
+ }
+
+ switch (me->filetype) {
+ case AE_IFLNK:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=link");
+ if ((keys & F_SLINK) != 0) {
+ archive_strcat(str, " link=");
+ mtree_quote(str, me->symlink.s);
+ }
+ break;
+ case AE_IFSOCK:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=socket");
+ break;
+ case AE_IFCHR:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=char");
+ if ((keys & F_DEV) != 0) {
+ archive_string_sprintf(str,
+ " device=native,%ju,%ju",
+ (uintmax_t)me->rdevmajor,
+ (uintmax_t)me->rdevminor);
+ }
+ break;
+ case AE_IFBLK:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=block");
+ if ((keys & F_DEV) != 0) {
+ archive_string_sprintf(str,
+ " device=native,%ju,%ju",
+ (uintmax_t)me->rdevmajor,
+ (uintmax_t)me->rdevminor);
+ }
+ break;
+ case AE_IFDIR:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=dir");
+ break;
+ case AE_IFIFO:
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=fifo");
+ break;
+ case AE_IFREG:
+ default: /* Handle unknown file types as regular files. */
+ if ((keys & F_TYPE) != 0)
+ archive_strcat(str, " type=file");
+ if ((keys & F_SIZE) != 0)
+ archive_string_sprintf(str, " size=%jd",
+ (intmax_t)me->size);
+ break;
+ }
+
+ /* Write a bunch of sum. */
+ if (me->reg_info)
+ sum_write(str, me->reg_info);
+
+ archive_strappend_char(str, '\n');
+ if (mtree->indent || mtree->classic)
+ mtree_indent(mtree);
+
+ if (mtree->buf.length > 32768) {
+ ret = __archive_write_output(
+ a, mtree->buf.s, mtree->buf.length);
+ archive_string_empty(&mtree->buf);
+ } else
+ ret = ARCHIVE_OK;
+ return (ret);
+}
+
+static int
+write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
+{
+ struct mtree_writer *mtree = a->format_data;
+ int ret;
+
+ if (n->parentdir.s) {
+ if (mtree->indent) {
+ int i, pd = mtree->depth * 4;
+ for (i = 0; i < pd; i++)
+ archive_strappend_char(&mtree->buf, ' ');
+ }
+ archive_string_sprintf(&mtree->buf, "# %s/%s\n",
+ n->parentdir.s, n->basename.s);
+ }
+
+ if (mtree->indent) {
+ archive_string_empty(&mtree->ebuf);
+ archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4);
+ mtree_indent(mtree);
+ } else
+ archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4);
+
+ if (mtree->buf.length > 32768) {
+ ret = __archive_write_output(
+ a, mtree->buf.s, mtree->buf.length);
+ archive_string_empty(&mtree->buf);
+ } else
+ ret = ARCHIVE_OK;
+ return (ret);
+}
+
+/*
+ * Write mtree entries saved at attr_counter_set_collect() function.
+ */
+static int
+write_mtree_entry_tree(struct archive_write *a)
+{
+ struct mtree_writer *mtree = a->format_data;
+ struct mtree_entry *np = mtree->root;
+ struct archive_rb_node *n;
+ int ret;
+
+ do {
+ if (mtree->output_global_set) {
+ /*
+ * Collect attribute information to know which value
+ * is frequently used among the children.
+ */
+ attr_counter_set_reset(mtree);
+ ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
+ struct mtree_entry *e = (struct mtree_entry *)n;
+ if (attr_counter_set_collect(mtree, e) < 0) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+ if (!np->dir_info->virtual || mtree->classic) {
+ ret = write_mtree_entry(a, np);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ } else {
+ /* Whenever output_global_set is enabled
+ * output global value(/set keywords)
+ * even if the directory entry is not allowed
+ * to be written because the global values
+ * can be used for the children. */
+ if (mtree->output_global_set)
+ write_global(mtree);
+ }
+ /*
+ * Output the attribute of all files except directory files.
+ */
+ mtree->depth++;
+ ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
+ struct mtree_entry *e = (struct mtree_entry *)n;
+
+ if (e->dir_info)
+ mtree_entry_add_child_tail(np, e);
+ else {
+ ret = write_mtree_entry(a, e);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ }
+ mtree->depth--;
+
+ if (np->dir_info->children.first != NULL) {
+ /*
+ * Descend the tree.
+ */
+ np = np->dir_info->children.first;
+ if (mtree->indent)
+ mtree->depth++;
+ continue;
+ } else if (mtree->classic) {
+ /*
+ * While printing mtree classic, if there are not
+ * any directory files(except "." and "..") in the
+ * directory, output two dots ".." as returning
+ * the parent directory.
+ */
+ ret = write_dot_dot_entry(a, np);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ while (np != np->parent) {
+ if (np->dir_info->chnext == NULL) {
+ /*
+ * Ascend the tree; go back to the parent.
+ */
+ if (mtree->indent)
+ mtree->depth--;
+ if (mtree->classic) {
+ ret = write_dot_dot_entry(a,
+ np->parent);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ np = np->parent;
+ } else {
+ /*
+ * Switch to next mtree entry in the directory.
+ */
+ np = np->dir_info->chnext;
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_finish_entry(struct archive_write *a)
+{
+ struct mtree_writer *mtree = a->format_data;
+ struct mtree_entry *me;
+
+ if ((me = mtree->mtree_entry) == NULL)
+ return (ARCHIVE_OK);
+ mtree->mtree_entry = NULL;
+
+ if (me->reg_info)
+ sum_final(mtree, me->reg_info);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_close(struct archive_write *a)
+{
+ struct mtree_writer *mtree= a->format_data;
+ int ret;
+
+ if (mtree->root != NULL) {
+ ret = write_mtree_entry_tree(a);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ archive_write_set_bytes_in_last_block(&a->archive, 1);
+
+ return __archive_write_output(a, mtree->buf.s, mtree->buf.length);
+}
+
+static ssize_t
+archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
+{
+ struct mtree_writer *mtree= a->format_data;
+
+ if (n > mtree->entry_bytes_remaining)
+ n = (size_t)mtree->entry_bytes_remaining;
+ mtree->entry_bytes_remaining -= n;
+
+ /* We don't need to compute a regular file sum */
+ if (mtree->mtree_entry == NULL)
+ return (n);
+
+ if (mtree->mtree_entry->filetype == AE_IFREG)
+ sum_update(mtree, buff, n);
+
+ return (n);
+}
+
+static int
+archive_write_mtree_free(struct archive_write *a)
+{
+ struct mtree_writer *mtree= a->format_data;
+
+ if (mtree == NULL)
+ return (ARCHIVE_OK);
+
+ /* Make sure we do not leave any entries. */
+ mtree_entry_register_free(mtree);
+ archive_string_free(&mtree->cur_dirstr);
+ archive_string_free(&mtree->ebuf);
+ archive_string_free(&mtree->buf);
+ attr_counter_set_free(mtree);
+ free(mtree);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_options(struct archive_write *a, const char *key,
+ const char *value)
+{
+ struct mtree_writer *mtree= a->format_data;
+ int keybit = 0;
+
+ switch (key[0]) {
+ case 'a':
+ if (strcmp(key, "all") == 0)
+ keybit = ~0;
+ break;
+ case 'c':
+ if (strcmp(key, "cksum") == 0)
+ keybit = F_CKSUM;
+ break;
+ case 'd':
+ if (strcmp(key, "device") == 0)
+ keybit = F_DEV;
+ else if (strcmp(key, "dironly") == 0) {
+ mtree->dironly = (value != NULL)? 1: 0;
+ return (ARCHIVE_OK);
+ }
+ break;
+ case 'f':
+ if (strcmp(key, "flags") == 0)
+ keybit = F_FLAGS;
+ break;
+ case 'g':
+ if (strcmp(key, "gid") == 0)
+ keybit = F_GID;
+ else if (strcmp(key, "gname") == 0)
+ keybit = F_GNAME;
+ break;
+ case 'i':
+ if (strcmp(key, "indent") == 0) {
+ mtree->indent = (value != NULL)? 1: 0;
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "inode") == 0) {
+ keybit = F_INO;
+ }
+ break;
+ case 'l':
+ if (strcmp(key, "link") == 0)
+ keybit = F_SLINK;
+ break;
+ case 'm':
+ if (strcmp(key, "md5") == 0 ||
+ strcmp(key, "md5digest") == 0)
+ keybit = F_MD5;
+ if (strcmp(key, "mode") == 0)
+ keybit = F_MODE;
+ break;
+ case 'n':
+ if (strcmp(key, "nlink") == 0)
+ keybit = F_NLINK;
+ break;
+ case 'r':
+ if (strcmp(key, "resdevice") == 0) {
+ keybit = F_RESDEV;
+ } else if (strcmp(key, "ripemd160digest") == 0 ||
+ strcmp(key, "rmd160") == 0 ||
+ strcmp(key, "rmd160digest") == 0)
+ keybit = F_RMD160;
+ break;
+ case 's':
+ if (strcmp(key, "sha1") == 0 ||
+ strcmp(key, "sha1digest") == 0)
+ keybit = F_SHA1;
+ if (strcmp(key, "sha256") == 0 ||
+ strcmp(key, "sha256digest") == 0)
+ keybit = F_SHA256;
+ if (strcmp(key, "sha384") == 0 ||
+ strcmp(key, "sha384digest") == 0)
+ keybit = F_SHA384;
+ if (strcmp(key, "sha512") == 0 ||
+ strcmp(key, "sha512digest") == 0)
+ keybit = F_SHA512;
+ if (strcmp(key, "size") == 0)
+ keybit = F_SIZE;
+ break;
+ case 't':
+ if (strcmp(key, "time") == 0)
+ keybit = F_TIME;
+ else if (strcmp(key, "type") == 0)
+ keybit = F_TYPE;
+ break;
+ case 'u':
+ if (strcmp(key, "uid") == 0)
+ keybit = F_UID;
+ else if (strcmp(key, "uname") == 0)
+ keybit = F_UNAME;
+ else if (strcmp(key, "use-set") == 0) {
+ mtree->output_global_set = (value != NULL)? 1: 0;
+ return (ARCHIVE_OK);
+ }
+ break;
+ }
+ if (keybit != 0) {
+ if (value != NULL)
+ mtree->keys |= keybit;
+ else
+ mtree->keys &= ~keybit;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_set_format_mtree_default(struct archive *_a, const char *fn)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct mtree_writer *mtree;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);
+
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate mtree data");
+ return (ARCHIVE_FATAL);
+ }
+
+ mtree->mtree_entry = NULL;
+ mtree->first = 1;
+ memset(&(mtree->set), 0, sizeof(mtree->set));
+ mtree->keys = DEFAULT_KEYS;
+ mtree->dironly = 0;
+ mtree->indent = 0;
+ archive_string_init(&mtree->ebuf);
+ archive_string_init(&mtree->buf);
+ mtree_entry_register_init(mtree);
+ a->format_data = mtree;
+ a->format_free = archive_write_mtree_free;
+ a->format_name = "mtree";
+ a->format_options = archive_write_mtree_options;
+ a->format_write_header = archive_write_mtree_header;
+ a->format_close = archive_write_mtree_close;
+ a->format_write_data = archive_write_mtree_data;
+ a->format_finish_entry = archive_write_mtree_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
+ a->archive.archive_format_name = "mtree";
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_set_format_mtree(struct archive *_a)
+{
+ return archive_write_set_format_mtree_default(_a,
+ "archive_write_set_format_mtree");
+}
+
+int
+archive_write_set_format_mtree_classic(struct archive *_a)
+{
+ int r;
+
+ r = archive_write_set_format_mtree_default(_a,
+ "archive_write_set_format_mtree_classic");
+ if (r == ARCHIVE_OK) {
+ struct archive_write *a = (struct archive_write *)_a;
+ struct mtree_writer *mtree;
+
+ mtree = (struct mtree_writer *)a->format_data;
+
+ /* Set to output a mtree archive in classic format. */
+ mtree->classic = 1;
+ /* Basically, mtree classic format uses '/set' global
+ * value. */
+ mtree->output_global_set = 1;
+ }
+ return (r);
+}
+
+static void
+sum_init(struct mtree_writer *mtree)
+{
+
+ mtree->compute_sum = 0;
+
+ if (mtree->keys & F_CKSUM) {
+ mtree->compute_sum |= F_CKSUM;
+ mtree->crc = 0;
+ mtree->crc_len = 0;
+ }
+#ifdef ARCHIVE_HAS_MD5
+ if (mtree->keys & F_MD5) {
+ if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_MD5;
+ else
+ mtree->keys &= ~F_MD5;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ if (mtree->keys & F_RMD160) {
+ if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_RMD160;
+ else
+ mtree->keys &= ~F_RMD160;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ if (mtree->keys & F_SHA1) {
+ if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_SHA1;
+ else
+ mtree->keys &= ~F_SHA1;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ if (mtree->keys & F_SHA256) {
+ if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_SHA256;
+ else
+ mtree->keys &= ~F_SHA256;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ if (mtree->keys & F_SHA384) {
+ if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_SHA384;
+ else
+ mtree->keys &= ~F_SHA384;/* Not supported. */
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ if (mtree->keys & F_SHA512) {
+ if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK)
+ mtree->compute_sum |= F_SHA512;
+ else
+ mtree->keys &= ~F_SHA512;/* Not supported. */
+ }
+#endif
+}
+
+static void
+sum_update(struct mtree_writer *mtree, const void *buff, size_t n)
+{
+ if (mtree->compute_sum & F_CKSUM) {
+ /*
+ * Compute a POSIX 1003.2 checksum
+ */
+ const unsigned char *p;
+ size_t nn;
+
+ for (nn = n, p = buff; nn--; ++p)
+ COMPUTE_CRC(mtree->crc, *p);
+ mtree->crc_len += n;
+ }
+#ifdef ARCHIVE_HAS_MD5
+ if (mtree->compute_sum & F_MD5)
+ archive_md5_update(&mtree->md5ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ if (mtree->compute_sum & F_RMD160)
+ archive_rmd160_update(&mtree->rmd160ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ if (mtree->compute_sum & F_SHA1)
+ archive_sha1_update(&mtree->sha1ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ if (mtree->compute_sum & F_SHA256)
+ archive_sha256_update(&mtree->sha256ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ if (mtree->compute_sum & F_SHA384)
+ archive_sha384_update(&mtree->sha384ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ if (mtree->compute_sum & F_SHA512)
+ archive_sha512_update(&mtree->sha512ctx, buff, n);
+#endif
+}
+
+static void
+sum_final(struct mtree_writer *mtree, struct reg_info *reg)
+{
+
+ if (mtree->compute_sum & F_CKSUM) {
+ uint64_t len;
+ /* Include the length of the file. */
+ for (len = mtree->crc_len; len != 0; len >>= 8)
+ COMPUTE_CRC(mtree->crc, len & 0xff);
+ reg->crc = ~mtree->crc;
+ }
+#ifdef ARCHIVE_HAS_MD5
+ if (mtree->compute_sum & F_MD5)
+ archive_md5_final(&mtree->md5ctx, reg->digest.md5);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ if (mtree->compute_sum & F_RMD160)
+ archive_rmd160_final(&mtree->rmd160ctx, reg->digest.rmd160);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ if (mtree->compute_sum & F_SHA1)
+ archive_sha1_final(&mtree->sha1ctx, reg->digest.sha1);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ if (mtree->compute_sum & F_SHA256)
+ archive_sha256_final(&mtree->sha256ctx, reg->digest.sha256);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ if (mtree->compute_sum & F_SHA384)
+ archive_sha384_final(&mtree->sha384ctx, reg->digest.sha384);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ if (mtree->compute_sum & F_SHA512)
+ archive_sha512_final(&mtree->sha512ctx, reg->digest.sha512);
+#endif
+ /* Save what types of sum are computed. */
+ reg->compute_sum = mtree->compute_sum;
+}
+
+#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
+ defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
+ defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
+static void
+strappend_bin(struct archive_string *s, const unsigned char *bin, int n)
+{
+ static const char hex[] = "0123456789abcdef";
+ int i;
+
+ for (i = 0; i < n; i++) {
+ archive_strappend_char(s, hex[bin[i] >> 4]);
+ archive_strappend_char(s, hex[bin[i] & 0x0f]);
+ }
+}
+#endif
+
+static void
+sum_write(struct archive_string *str, struct reg_info *reg)
+{
+
+ if (reg->compute_sum & F_CKSUM) {
+ archive_string_sprintf(str, " cksum=%ju",
+ (uintmax_t)reg->crc);
+ }
+
+#define append_digest(_s, _r, _t) \
+ strappend_bin(_s, _r->digest._t, sizeof(_r->digest._t))
+
+#ifdef ARCHIVE_HAS_MD5
+ if (reg->compute_sum & F_MD5) {
+ archive_strcat(str, " md5digest=");
+ append_digest(str, reg, md5);
+ }
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+ if (reg->compute_sum & F_RMD160) {
+ archive_strcat(str, " rmd160digest=");
+ append_digest(str, reg, rmd160);
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ if (reg->compute_sum & F_SHA1) {
+ archive_strcat(str, " sha1digest=");
+ append_digest(str, reg, sha1);
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+ if (reg->compute_sum & F_SHA256) {
+ archive_strcat(str, " sha256digest=");
+ append_digest(str, reg, sha256);
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+ if (reg->compute_sum & F_SHA384) {
+ archive_strcat(str, " sha384digest=");
+ append_digest(str, reg, sha384);
+ }
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+ if (reg->compute_sum & F_SHA512) {
+ archive_strcat(str, " sha512digest=");
+ append_digest(str, reg, sha512);
+ }
+#endif
+#undef append_digest
+}
+
+static int
+mtree_entry_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
+ const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
+
+ return (strcmp(e2->basename.s, e1->basename.s));
+}
+
+static int
+mtree_entry_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct mtree_entry *e = (const struct mtree_entry *)n;
+
+ return (strcmp((const char *)key, e->basename.s));
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static int
+cleanup_backslash_1(char *p)
+{
+ int mb, dos;
+
+ mb = dos = 0;
+ while (*p) {
+ if (*(unsigned char *)p > 127)
+ mb = 1;
+ if (*p == '\\') {
+ /* If we have not met any multi-byte characters,
+ * we can replace '\' with '/'. */
+ if (!mb)
+ *p = '/';
+ dos = 1;
+ }
+ p++;
+ }
+ if (!mb || !dos)
+ return (0);
+ return (-1);
+}
+
+static void
+cleanup_backslash_2(wchar_t *p)
+{
+
+ /* Convert a path-separator from '\' to '/' */
+ while (*p != L'\0') {
+ if (*p == L'\\')
+ *p = L'/';
+ p++;
+ }
+}
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file,
+ struct archive_entry *entry)
+{
+ const char *pathname;
+ char *p, *dirname, *slash;
+ size_t len;
+ int ret = ARCHIVE_OK;
+
+ archive_strcpy(&file->pathname, archive_entry_pathname(entry));
+#if defined(_WIN32) || defined(__CYGWIN__)
+ /*
+ * Convert a path-separator from '\' to '/'
+ */
+ if (cleanup_backslash_1(file->pathname.s) != 0) {
+ const wchar_t *wp = archive_entry_pathname_w(entry);
+ struct archive_wstring ws;
+
+ if (wp != NULL) {
+ int r;
+ archive_string_init(&ws);
+ archive_wstrcpy(&ws, wp);
+ cleanup_backslash_2(ws.s);
+ archive_string_empty(&(file->pathname));
+ r = archive_string_append_from_wcs(&(file->pathname),
+ ws.s, ws.length);
+ archive_wstring_free(&ws);
+ if (r < 0 && errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+#else
+ (void)a; /* UNUSED */
+#endif
+ pathname = file->pathname.s;
+ if (strcmp(pathname, ".") == 0) {
+ archive_strcpy(&file->basename, ".");
+ return (ARCHIVE_OK);
+ }
+
+ archive_strcpy(&(file->parentdir), pathname);
+
+ len = file->parentdir.length;
+ p = dirname = file->parentdir.s;
+
+ /*
+ * Remove leading '/' and '../' elements
+ */
+ while (*p) {
+ if (p[0] == '/') {
+ p++;
+ len--;
+ } else if (p[0] != '.')
+ break;
+ else if (p[1] == '.' && p[2] == '/') {
+ p += 3;
+ len -= 3;
+ } else
+ break;
+ }
+ if (p != dirname) {
+ memmove(dirname, p, len+1);
+ p = dirname;
+ }
+ /*
+ * Remove "/","/." and "/.." elements from tail.
+ */
+ while (len > 0) {
+ size_t ll = len;
+
+ if (len > 0 && p[len-1] == '/') {
+ p[len-1] = '\0';
+ len--;
+ }
+ if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+ p[len-2] = '\0';
+ len -= 2;
+ }
+ if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+ p[len-1] == '.') {
+ p[len-3] = '\0';
+ len -= 3;
+ }
+ if (ll == len)
+ break;
+ }
+ while (*p) {
+ if (p[0] == '/') {
+ if (p[1] == '/')
+ /* Convert '//' --> '/' */
+ memmove(p, p+1, strlen(p+1) + 1);
+ else if (p[1] == '.' && p[2] == '/')
+ /* Convert '/./' --> '/' */
+ memmove(p, p+2, strlen(p+2) + 1);
+ else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+ /* Convert 'dir/dir1/../dir2/'
+ * --> 'dir/dir2/'
+ */
+ char *rp = p -1;
+ while (rp >= dirname) {
+ if (*rp == '/')
+ break;
+ --rp;
+ }
+ if (rp > dirname) {
+ strcpy(rp, p+3);
+ p = rp;
+ } else {
+ strcpy(dirname, p+4);
+ p = dirname;
+ }
+ } else
+ p++;
+ } else
+ p++;
+ }
+ p = dirname;
+ len = strlen(p);
+
+ /*
+ * Add "./" prefix.
+ * NOTE: If the pathname does not have a path separator, we have
+ * to add "./" to the head of the pathname because mtree reader
+ * will suppose that it is v1(a.k.a classic) mtree format and
+ * change the directory unexpectedly and so it will make a wrong
+ * path.
+ */
+ if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) {
+ struct archive_string as;
+ archive_string_init(&as);
+ archive_strcpy(&as, "./");
+ archive_strncat(&as, p, len);
+ archive_string_empty(&file->parentdir);
+ archive_string_concat(&file->parentdir, &as);
+ archive_string_free(&as);
+ p = file->parentdir.s;
+ len = archive_strlen(&file->parentdir);
+ }
+
+ /*
+ * Find out the position which points the last position of
+ * path separator('/').
+ */
+ slash = NULL;
+ for (; *p != '\0'; p++) {
+ if (*p == '/')
+ slash = p;
+ }
+ if (slash == NULL) {
+ /* The pathname doesn't have a parent directory. */
+ file->parentdir.length = len;
+ archive_string_copy(&(file->basename), &(file->parentdir));
+ archive_string_empty(&(file->parentdir));
+ *file->parentdir.s = '\0';
+ return (ret);
+ }
+
+ /* Make a basename from file->parentdir.s and slash */
+ *slash = '\0';
+ file->parentdir.length = slash - file->parentdir.s;
+ archive_strcpy(&(file->basename), slash + 1);
+ return (ret);
+}
+
+static int
+mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname,
+ struct mtree_entry **m_entry)
+{
+ struct archive_entry *entry;
+ struct mtree_entry *file;
+ int r;
+
+ entry = archive_entry_new();
+ if (entry == NULL) {
+ *m_entry = NULL;
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ archive_entry_copy_pathname(entry, pathname);
+ archive_entry_set_mode(entry, AE_IFDIR | 0755);
+ archive_entry_set_mtime(entry, time(NULL), 0);
+
+ r = mtree_entry_new(a, entry, &file);
+ archive_entry_free(entry);
+ if (r < ARCHIVE_WARN) {
+ *m_entry = NULL;
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ file->dir_info->virtual = 1;
+
+ *m_entry = file;
+ return (ARCHIVE_OK);
+}
+
+static void
+mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
+{
+ file->next = NULL;
+ *mtree->file_list.last = file;
+ mtree->file_list.last = &(file->next);
+}
+
+static void
+mtree_entry_register_init(struct mtree_writer *mtree)
+{
+ mtree->file_list.first = NULL;
+ mtree->file_list.last = &(mtree->file_list.first);
+}
+
+static void
+mtree_entry_register_free(struct mtree_writer *mtree)
+{
+ struct mtree_entry *file, *file_next;
+
+ file = mtree->file_list.first;
+ while (file != NULL) {
+ file_next = file->next;
+ mtree_entry_free(file);
+ file = file_next;
+ }
+}
+
+static int
+mtree_entry_add_child_tail(struct mtree_entry *parent,
+ struct mtree_entry *child)
+{
+ child->dir_info->chnext = NULL;
+ *parent->dir_info->children.last = child;
+ parent->dir_info->children.last = &(child->dir_info->chnext);
+ return (1);
+}
+
+/*
+ * Find a entry from a parent entry with the name.
+ */
+static struct mtree_entry *
+mtree_entry_find_child(struct mtree_entry *parent, const char *child_name)
+{
+ struct mtree_entry *np;
+
+ if (parent == NULL)
+ return (NULL);
+ np = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &(parent->dir_info->rbtree), child_name);
+ return (np);
+}
+
+static int
+get_path_component(char *name, size_t n, const char *fn)
+{
+ char *p;
+ size_t l;
+
+ p = strchr(fn, '/');
+ if (p == NULL) {
+ if ((l = strlen(fn)) == 0)
+ return (0);
+ } else
+ l = p - fn;
+ if (l > n -1)
+ return (-1);
+ memcpy(name, fn, l);
+ name[l] = '\0';
+
+ return ((int)l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+ char name[NAME_MAX+1];
+#else
+ char name[256];
+#endif
+ struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;
+ struct mtree_entry *dent, *file, *np;
+ const char *fn, *p;
+ int l, r;
+
+ file = *filep;
+ if (file->parentdir.length == 0 && file->basename.length == 1 &&
+ file->basename.s[0] == '.') {
+ file->parent = file;
+ if (mtree->root != NULL) {
+ np = mtree->root;
+ goto same_entry;
+ }
+ mtree->root = file;
+ mtree_entry_register_add(mtree, file);
+ return (ARCHIVE_OK);
+ }
+
+ if (file->parentdir.length == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal programming error "
+ "in generating canonical name for %s",
+ file->pathname.s);
+ return (ARCHIVE_FAILED);
+ }
+
+ fn = p = file->parentdir.s;
+
+ /*
+ * If the path of the parent directory of `file' entry is
+ * the same as the path of `cur_dirent', add `file' entry to
+ * `cur_dirent'.
+ */
+ if (archive_strlen(&(mtree->cur_dirstr))
+ == archive_strlen(&(file->parentdir)) &&
+ strcmp(mtree->cur_dirstr.s, fn) == 0) {
+ if (!__archive_rb_tree_insert_node(
+ &(mtree->cur_dirent->dir_info->rbtree),
+ (struct archive_rb_node *)file)) {
+ /* There is the same name in the tree. */
+ np = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &(mtree->cur_dirent->dir_info->rbtree),
+ file->basename.s);
+ goto same_entry;
+ }
+ file->parent = mtree->cur_dirent;
+ mtree_entry_register_add(mtree, file);
+ return (ARCHIVE_OK);
+ }
+
+ dent = mtree->root;
+ for (;;) {
+ l = get_path_component(name, sizeof(name), fn);
+ if (l == 0) {
+ np = NULL;
+ break;
+ }
+ if (l < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ return (ARCHIVE_FATAL);
+ }
+ if (l == 1 && name[0] == '.' && dent != NULL &&
+ dent == mtree->root) {
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ continue;
+ }
+
+ np = mtree_entry_find_child(dent, name);
+ if (np == NULL || fn[0] == '\0')
+ break;
+
+ /* Find next sub directory. */
+ if (!np->dir_info) {
+ /* NOT Directory! */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "`%s' is not directory, we cannot insert `%s' ",
+ np->pathname.s, file->pathname.s);
+ return (ARCHIVE_FAILED);
+ }
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ dent = np;
+ }
+ if (np == NULL) {
+ /*
+ * Create virtual parent directories.
+ */
+ while (fn[0] != '\0') {
+ struct mtree_entry *vp;
+ struct archive_string as;
+
+ archive_string_init(&as);
+ archive_strncat(&as, p, fn - p + l);
+ if (as.s[as.length-1] == '/') {
+ as.s[as.length-1] = '\0';
+ as.length--;
+ }
+ r = mtree_entry_create_virtual_dir(a, as.s, &vp);
+ archive_string_free(&as);
+ if (r < ARCHIVE_WARN)
+ return (r);
+
+ if (strcmp(vp->pathname.s, ".") == 0) {
+ vp->parent = vp;
+ mtree->root = vp;
+ } else {
+ __archive_rb_tree_insert_node(
+ &(dent->dir_info->rbtree),
+ (struct archive_rb_node *)vp);
+ vp->parent = dent;
+ }
+ mtree_entry_register_add(mtree, vp);
+ np = vp;
+
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ l = get_path_component(name, sizeof(name), fn);
+ if (l < 0) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ return (ARCHIVE_FATAL);
+ }
+ dent = np;
+ }
+
+ /* Found out the parent directory where `file' can be
+ * inserted. */
+ mtree->cur_dirent = dent;
+ archive_string_empty(&(mtree->cur_dirstr));
+ archive_string_ensure(&(mtree->cur_dirstr),
+ archive_strlen(&(dent->parentdir)) +
+ archive_strlen(&(dent->basename)) + 2);
+ if (archive_strlen(&(dent->parentdir)) +
+ archive_strlen(&(dent->basename)) == 0)
+ mtree->cur_dirstr.s[0] = 0;
+ else {
+ if (archive_strlen(&(dent->parentdir)) > 0) {
+ archive_string_copy(&(mtree->cur_dirstr),
+ &(dent->parentdir));
+ archive_strappend_char(
+ &(mtree->cur_dirstr), '/');
+ }
+ archive_string_concat(&(mtree->cur_dirstr),
+ &(dent->basename));
+ }
+
+ if (!__archive_rb_tree_insert_node(
+ &(dent->dir_info->rbtree),
+ (struct archive_rb_node *)file)) {
+ np = (struct mtree_entry *)__archive_rb_tree_find_node(
+ &(dent->dir_info->rbtree), file->basename.s);
+ goto same_entry;
+ }
+ file->parent = dent;
+ mtree_entry_register_add(mtree, file);
+ return (ARCHIVE_OK);
+ }
+
+same_entry:
+ /*
+ * We have already has the entry the filename of which is
+ * the same.
+ */
+ r = mtree_entry_exchange_same_entry(a, np, file);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (np->dir_info)
+ np->dir_info->virtual = 0;
+ *filep = np;
+ mtree_entry_free(file);
+ return (ARCHIVE_WARN);
+}
+
+static int
+mtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np,
+ struct mtree_entry *file)
+{
+
+ if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Found duplicate entries `%s' and its file type is "
+ "different",
+ np->pathname.s);
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Update the existent mtree entry's attributes by the new one's. */
+ archive_string_empty(&np->symlink);
+ archive_string_concat(&np->symlink, &file->symlink);
+ archive_string_empty(&np->uname);
+ archive_string_concat(&np->uname, &file->uname);
+ archive_string_empty(&np->gname);
+ archive_string_concat(&np->gname, &file->gname);
+ archive_string_empty(&np->fflags_text);
+ archive_string_concat(&np->fflags_text, &file->fflags_text);
+ np->nlink = file->nlink;
+ np->filetype = file->filetype;
+ np->mode = file->mode;
+ np->size = file->size;
+ np->uid = file->uid;
+ np->gid = file->gid;
+ np->fflags_set = file->fflags_set;
+ np->fflags_clear = file->fflags_clear;
+ np->mtime = file->mtime;
+ np->mtime_nsec = file->mtime_nsec;
+ np->rdevmajor = file->rdevmajor;
+ np->rdevminor = file->rdevminor;
+ np->devmajor = file->devmajor;
+ np->devminor = file->devminor;
+ np->ino = file->ino;
+
+ return (ARCHIVE_WARN);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_pax.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_pax.c
new file mode 100644
index 0000000000..1eb9a9a4b6
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_pax.c
@@ -0,0 +1,2078 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_pax.c 201162 2009-12-29 05:47:46Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct sparse_block {
+ struct sparse_block *next;
+ int is_hole;
+ uint64_t offset;
+ uint64_t remaining;
+};
+
+struct pax {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+ struct archive_string l_url_encoded_name;
+ struct archive_string pax_header;
+ struct archive_string sparse_map;
+ size_t sparse_map_padding;
+ struct sparse_block *sparse_list;
+ struct sparse_block *sparse_tail;
+ struct archive_string_conv *sconv_utf8;
+ int opt_binary;
+
+ unsigned flags;
+#define WRITE_SCHILY_XATTR (1 << 0)
+#define WRITE_LIBARCHIVE_XATTR (1 << 1)
+};
+
+static void add_pax_attr(struct archive_string *, const char *key,
+ const char *value);
+static void add_pax_attr_binary(struct archive_string *,
+ const char *key,
+ const char *value, size_t value_len);
+static void add_pax_attr_int(struct archive_string *,
+ const char *key, int64_t value);
+static void add_pax_attr_time(struct archive_string *,
+ const char *key, int64_t sec,
+ unsigned long nanos);
+static int add_pax_acl(struct archive_write *,
+ struct archive_entry *, struct pax *, int);
+static ssize_t archive_write_pax_data(struct archive_write *,
+ const void *, size_t);
+static int archive_write_pax_close(struct archive_write *);
+static int archive_write_pax_free(struct archive_write *);
+static int archive_write_pax_finish_entry(struct archive_write *);
+static int archive_write_pax_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_pax_options(struct archive_write *,
+ const char *, const char *);
+static char *base64_encode(const char *src, size_t len);
+static char *build_gnu_sparse_name(char *dest, const char *src);
+static char *build_pax_attribute_name(char *dest, const char *src);
+static char *build_ustar_entry_name(char *dest, const char *src,
+ size_t src_length, const char *insert);
+static char *format_int(char *dest, int64_t);
+static int has_non_ASCII(const char *);
+static void sparse_list_clear(struct pax *);
+static int sparse_list_add(struct pax *, int64_t, int64_t);
+static char *url_encode(const char *in);
+static time_t get_ustar_max_mtime(void);
+
+/*
+ * Set output format to 'restricted pax' format.
+ *
+ * This is the same as normal 'pax', but tries to suppress
+ * the pax header whenever possible. This is the default for
+ * bsdtar, for instance.
+ */
+int
+archive_write_set_format_pax_restricted(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int r;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_pax_restricted");
+
+ r = archive_write_set_format_pax(&a->archive);
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
+ a->archive.archive_format_name = "restricted POSIX pax interchange";
+ return (r);
+}
+
+/*
+ * Set output format to 'pax' format.
+ */
+int
+archive_write_set_format_pax(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct pax *pax;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_pax");
+
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ pax = (struct pax *)calloc(1, sizeof(*pax));
+ if (pax == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate pax data");
+ return (ARCHIVE_FATAL);
+ }
+ pax->flags = WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;
+
+ a->format_data = pax;
+ a->format_name = "pax";
+ a->format_options = archive_write_pax_options;
+ a->format_write_header = archive_write_pax_header;
+ a->format_write_data = archive_write_pax_data;
+ a->format_close = archive_write_pax_close;
+ a->format_free = archive_write_pax_free;
+ a->format_finish_entry = archive_write_pax_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+ a->archive.archive_format_name = "POSIX pax interchange";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_pax_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct pax *pax = (struct pax *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ /*
+ * The character-set we can use are defined in
+ * IEEE Std 1003.1-2001
+ */
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: hdrcharset option needs a character-set name");
+ else if (strcmp(val, "BINARY") == 0 ||
+ strcmp(val, "binary") == 0) {
+ /*
+ * Specify binary mode. We will not convert
+ * filenames, uname and gname to any charsets.
+ */
+ pax->opt_binary = 1;
+ ret = ARCHIVE_OK;
+ } else if (strcmp(val, "UTF-8") == 0) {
+ /*
+ * Specify UTF-8 character-set to be used for
+ * filenames. This is almost the test that
+ * running platform supports the string conversion.
+ * Especially libarchive_test needs this trick for
+ * its test.
+ */
+ pax->sconv_utf8 = archive_string_conversion_to_charset(
+ &(a->archive), "UTF-8", 0);
+ if (pax->sconv_utf8 == NULL)
+ ret = ARCHIVE_FATAL;
+ else
+ ret = ARCHIVE_OK;
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: invalid charset name");
+ return (ret);
+ } else if (strcmp(key, "xattrheader") == 0) {
+ if (val == NULL || val[0] == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: xattrheader requires a value");
+ } else if (strcmp(val, "ALL") == 0 ||
+ strcmp(val, "all") == 0) {
+ pax->flags |= WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;
+ ret = ARCHIVE_OK;
+ } else if (strcmp(val, "SCHILY") == 0 ||
+ strcmp(val, "schily") == 0) {
+ pax->flags |= WRITE_SCHILY_XATTR;
+ pax->flags &= ~WRITE_LIBARCHIVE_XATTR;
+ ret = ARCHIVE_OK;
+ } else if (strcmp(val, "LIBARCHIVE") == 0 ||
+ strcmp(val, "libarchive") == 0) {
+ pax->flags |= WRITE_LIBARCHIVE_XATTR;
+ pax->flags &= ~WRITE_SCHILY_XATTR;
+ ret = ARCHIVE_OK;
+ } else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "pax: invalid xattr header name");
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+/*
+ * Note: This code assumes that 'nanos' has the same sign as 'sec',
+ * which implies that sec=-1, nanos=200000000 represents -1.2 seconds
+ * and not -0.8 seconds. This is a pretty pedantic point, as we're
+ * unlikely to encounter many real files created before Jan 1, 1970,
+ * much less ones with timestamps recorded to sub-second resolution.
+ */
+static void
+add_pax_attr_time(struct archive_string *as, const char *key,
+ int64_t sec, unsigned long nanos)
+{
+ int digit, i;
+ char *t;
+ /*
+ * Note that each byte contributes fewer than 3 base-10
+ * digits, so this will always be big enough.
+ */
+ char tmp[1 + 3*sizeof(sec) + 1 + 3*sizeof(nanos)];
+
+ tmp[sizeof(tmp) - 1] = 0;
+ t = tmp + sizeof(tmp) - 1;
+
+ /* Skip trailing zeros in the fractional part. */
+ for (digit = 0, i = 10; i > 0 && digit == 0; i--) {
+ digit = nanos % 10;
+ nanos /= 10;
+ }
+
+ /* Only format the fraction if it's non-zero. */
+ if (i > 0) {
+ while (i > 0) {
+ *--t = "0123456789"[digit];
+ digit = nanos % 10;
+ nanos /= 10;
+ i--;
+ }
+ *--t = '.';
+ }
+ t = format_int(t, sec);
+
+ add_pax_attr(as, key, t);
+}
+
+static char *
+format_int(char *t, int64_t i)
+{
+ uint64_t ui;
+
+ if (i < 0)
+ ui = (i == INT64_MIN) ? (uint64_t)(INT64_MAX) + 1 : (uint64_t)(-i);
+ else
+ ui = i;
+
+ do {
+ *--t = "0123456789"[ui % 10];
+ } while (ui /= 10);
+ if (i < 0)
+ *--t = '-';
+ return (t);
+}
+
+static void
+add_pax_attr_int(struct archive_string *as, const char *key, int64_t value)
+{
+ char tmp[1 + 3 * sizeof(value)];
+
+ tmp[sizeof(tmp) - 1] = 0;
+ add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value));
+}
+
+/*
+ * Add a key/value attribute to the pax header. This function handles
+ * the length field and various other syntactic requirements.
+ */
+static void
+add_pax_attr(struct archive_string *as, const char *key, const char *value)
+{
+ add_pax_attr_binary(as, key, value, strlen(value));
+}
+
+/*
+ * Add a key/value attribute to the pax header. This function handles
+ * binary values.
+ */
+static void
+add_pax_attr_binary(struct archive_string *as, const char *key,
+ const char *value, size_t value_len)
+{
+ int digits, i, len, next_ten;
+ char tmp[1 + 3 * sizeof(int)]; /* < 3 base-10 digits per byte */
+
+ /*-
+ * PAX attributes have the following layout:
+ * <len> <space> <key> <=> <value> <nl>
+ */
+ len = 1 + (int)strlen(key) + 1 + (int)value_len + 1;
+
+ /*
+ * The <len> field includes the length of the <len> field, so
+ * computing the correct length is tricky. I start by
+ * counting the number of base-10 digits in 'len' and
+ * computing the next higher power of 10.
+ */
+ next_ten = 1;
+ digits = 0;
+ i = len;
+ while (i > 0) {
+ i = i / 10;
+ digits++;
+ next_ten = next_ten * 10;
+ }
+ /*
+ * For example, if string without the length field is 99
+ * chars, then adding the 2 digit length "99" will force the
+ * total length past 100, requiring an extra digit. The next
+ * statement adjusts for this effect.
+ */
+ if (len + digits >= next_ten)
+ digits++;
+
+ /* Now, we have the right length so we can build the line. */
+ tmp[sizeof(tmp) - 1] = 0; /* Null-terminate the work area. */
+ archive_strcat(as, format_int(tmp + sizeof(tmp) - 1, len + digits));
+ archive_strappend_char(as, ' ');
+ archive_strcat(as, key);
+ archive_strappend_char(as, '=');
+ archive_array_append(as, value, value_len);
+ archive_strappend_char(as, '\n');
+}
+
+static void
+archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name,
+ const void *value, size_t value_len)
+{
+ struct archive_string s;
+ char *encoded_value;
+
+ if (encoded_name == NULL)
+ return;
+
+ if (pax->flags & WRITE_LIBARCHIVE_XATTR) {
+ encoded_value = base64_encode((const char *)value, value_len);
+ if (encoded_value != NULL) {
+ archive_string_init(&s);
+ archive_strcpy(&s, "LIBARCHIVE.xattr.");
+ archive_strcat(&s, encoded_name);
+ add_pax_attr(&(pax->pax_header), s.s, encoded_value);
+ archive_string_free(&s);
+ }
+ free(encoded_value);
+ }
+ if (pax->flags & WRITE_SCHILY_XATTR) {
+ archive_string_init(&s);
+ archive_strcpy(&s, "SCHILY.xattr.");
+ archive_strcat(&s, encoded_name);
+ add_pax_attr_binary(&(pax->pax_header), s.s, value, value_len);
+ archive_string_free(&s);
+ }
+}
+
+static int
+archive_write_pax_header_xattrs(struct archive_write *a,
+ struct pax *pax, struct archive_entry *entry)
+{
+ int i = archive_entry_xattr_reset(entry);
+
+ while (i--) {
+ const char *name;
+ const void *value;
+ char *url_encoded_name = NULL, *encoded_name = NULL;
+ size_t size;
+ int r;
+
+ archive_entry_xattr_next(entry, &name, &value, &size);
+ url_encoded_name = url_encode(name);
+ if (url_encoded_name == NULL)
+ goto malloc_error;
+ else {
+ /* Convert narrow-character to UTF-8. */
+ r = archive_strcpy_l(&(pax->l_url_encoded_name),
+ url_encoded_name, pax->sconv_utf8);
+ free(url_encoded_name); /* Done with this. */
+ if (r == 0)
+ encoded_name = pax->l_url_encoded_name.s;
+ else if (r == -1)
+ goto malloc_error;
+ else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Error encoding pax extended attribute");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ archive_write_pax_header_xattr(pax, encoded_name,
+ value, size);
+
+ }
+ return (ARCHIVE_OK);
+malloc_error:
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+}
+
+static int
+get_entry_hardlink(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_hardlink_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_pathname(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_pathname_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_uname(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_uname_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_gname(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_gname_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+get_entry_symlink(struct archive_write *a, struct archive_entry *entry,
+ const char **name, size_t *length, struct archive_string_conv *sc)
+{
+ int r;
+
+ r = archive_entry_symlink_l(entry, name, length, sc);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_WARN);
+ }
+ return (ARCHIVE_OK);
+}
+
+/* Add ACL to pax header */
+static int
+add_pax_acl(struct archive_write *a,
+ struct archive_entry *entry, struct pax *pax, int flags)
+{
+ char *p;
+ const char *attr;
+ int acl_types;
+
+ acl_types = archive_entry_acl_types(entry);
+
+ if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+ attr = "SCHILY.acl.ace";
+ else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+ attr = "SCHILY.acl.access";
+ else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+ attr = "SCHILY.acl.default";
+ else
+ return (ARCHIVE_FATAL);
+
+ p = archive_entry_acl_to_text_l(entry, NULL, flags, pax->sconv_utf8);
+ if (p == NULL) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM, "%s %s",
+ "Can't allocate memory for ", attr);
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s",
+ "Can't translate ", attr, " to UTF-8");
+ return(ARCHIVE_WARN);
+ }
+
+ if (*p != '\0') {
+ add_pax_attr(&(pax->pax_header),
+ attr, p);
+ }
+ free(p);
+ return(ARCHIVE_OK);
+}
+
+/*
+ * TODO: Consider adding 'comment' and 'charset' fields to
+ * archive_entry so that clients can specify them. Also, consider
+ * adding generic key/value tags so clients can add arbitrary
+ * key/value data.
+ *
+ * TODO: Break up this 700-line function!!!! Yowza!
+ */
+static int
+archive_write_pax_header(struct archive_write *a,
+ struct archive_entry *entry_original)
+{
+ struct archive_entry *entry_main;
+ const char *p;
+ const char *suffix;
+ int need_extension, r, ret;
+ int acl_types;
+ int sparse_count;
+ uint64_t sparse_total, real_size;
+ struct pax *pax;
+ const char *hardlink;
+ const char *path = NULL, *linkpath = NULL;
+ const char *uname = NULL, *gname = NULL;
+ const void *mac_metadata;
+ size_t mac_metadata_size;
+ struct archive_string_conv *sconv;
+ size_t hardlink_length, path_length, linkpath_length;
+ size_t uname_length, gname_length;
+
+ char paxbuff[512];
+ char ustarbuff[512];
+ char ustar_entry_name[256];
+ char pax_entry_name[256];
+ char gnu_sparse_name[256];
+ struct archive_string entry_name;
+
+ ret = ARCHIVE_OK;
+ need_extension = 0;
+ pax = (struct pax *)a->format_data;
+
+ const time_t ustar_max_mtime = get_ustar_max_mtime();
+
+ /* Sanity check. */
+ if (archive_entry_pathname(entry_original) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't record entry in tar file without pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+ /*
+ * Choose a header encoding.
+ */
+ if (pax->opt_binary)
+ sconv = NULL;/* Binary mode. */
+ else {
+ /* Header encoding is UTF-8. */
+ if (pax->sconv_utf8 == NULL) {
+ /* Initialize the string conversion object
+ * we must need */
+ pax->sconv_utf8 = archive_string_conversion_to_charset(
+ &(a->archive), "UTF-8", 1);
+ if (pax->sconv_utf8 == NULL)
+ /* Couldn't allocate memory */
+ return (ARCHIVE_FAILED);
+ }
+ sconv = pax->sconv_utf8;
+ }
+
+ r = get_entry_hardlink(a, entry_original, &hardlink,
+ &hardlink_length, sconv);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ else if (r != ARCHIVE_OK) {
+ r = get_entry_hardlink(a, entry_original, &hardlink,
+ &hardlink_length, NULL);
+ if (r == ARCHIVE_FATAL)
+ return (r);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s", hardlink,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;/* The header charset switches to binary mode. */
+ }
+
+ /* Make sure this is a type of entry that we can handle here */
+ if (hardlink == NULL) {
+ switch (archive_entry_filetype(entry_original)) {
+ case AE_IFBLK:
+ case AE_IFCHR:
+ case AE_IFIFO:
+ case AE_IFLNK:
+ case AE_IFREG:
+ break;
+ case AE_IFDIR:
+ {
+ /*
+ * Ensure a trailing '/'. Modify the original
+ * entry so the client sees the change.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wp;
+
+ wp = archive_entry_pathname_w(entry_original);
+ if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+ struct archive_wstring ws;
+
+ archive_string_init(&ws);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(&ws,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate pax data");
+ archive_wstring_free(&ws);
+ return(ARCHIVE_FATAL);
+ }
+ /* Should we keep '\' ? */
+ if (wp[path_length -1] == L'\\')
+ path_length--;
+ archive_wstrncpy(&ws, wp, path_length);
+ archive_wstrappend_wchar(&ws, L'/');
+ archive_entry_copy_pathname_w(
+ entry_original, ws.s);
+ archive_wstring_free(&ws);
+ p = NULL;
+ } else
+#endif
+ p = archive_entry_pathname(entry_original);
+ /*
+ * On Windows, this is a backup operation just in
+ * case getting WCS failed. On POSIX, this is a
+ * normal operation.
+ */
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+ struct archive_string as;
+
+ archive_string_init(&as);
+ path_length = strlen(p);
+ if (archive_string_ensure(&as,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate pax data");
+ archive_string_free(&as);
+ return(ARCHIVE_FATAL);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: This might break the pathname
+ * if the current code page is CP932 and
+ * the pathname includes a character '\'
+ * as a part of its multibyte pathname. */
+ if (p[strlen(p) -1] == '\\')
+ path_length--;
+ else
+#endif
+ archive_strncpy(&as, p, path_length);
+ archive_strappend_char(&as, '/');
+ archive_entry_copy_pathname(
+ entry_original, as.s);
+ archive_string_free(&as);
+ }
+ break;
+ }
+ default: /* AE_IFSOCK and unknown */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry_original, "pax");
+ return (ARCHIVE_FAILED);
+ }
+ }
+
+ /*
+ * If Mac OS metadata blob is here, recurse to write that
+ * as a separate entry. This is really a pretty poor design:
+ * In particular, it doubles the overhead for long filenames.
+ * TODO: Help Apple folks design something better and figure
+ * out how to transition from this legacy format.
+ *
+ * Note that this code is present on every platform; clients
+ * on non-Mac are unlikely to ever provide this data, but
+ * applications that copy entries from one archive to another
+ * should not lose data just because the local filesystem
+ * can't store it.
+ */
+ mac_metadata =
+ archive_entry_mac_metadata(entry_original, &mac_metadata_size);
+ if (mac_metadata != NULL) {
+ const char *oname;
+ char *name, *bname;
+ size_t name_length;
+ struct archive_entry *extra = archive_entry_new2(&a->archive);
+
+ oname = archive_entry_pathname(entry_original);
+ name_length = strlen(oname);
+ name = malloc(name_length + 3);
+ if (name == NULL || extra == NULL) {
+ /* XXX error message */
+ archive_entry_free(extra);
+ free(name);
+ return (ARCHIVE_FAILED);
+ }
+ strcpy(name, oname);
+ /* Find last '/'; strip trailing '/' characters */
+ bname = strrchr(name, '/');
+ while (bname != NULL && bname[1] == '\0') {
+ *bname = '\0';
+ bname = strrchr(name, '/');
+ }
+ if (bname == NULL) {
+ memmove(name + 2, name, name_length + 1);
+ memmove(name, "._", 2);
+ } else {
+ bname += 1;
+ memmove(bname + 2, bname, strlen(bname) + 1);
+ memmove(bname, "._", 2);
+ }
+ archive_entry_copy_pathname(extra, name);
+ free(name);
+
+ archive_entry_set_size(extra, mac_metadata_size);
+ archive_entry_set_filetype(extra, AE_IFREG);
+ archive_entry_set_perm(extra,
+ archive_entry_perm(entry_original));
+ archive_entry_set_mtime(extra,
+ archive_entry_mtime(entry_original),
+ archive_entry_mtime_nsec(entry_original));
+ archive_entry_set_gid(extra,
+ archive_entry_gid(entry_original));
+ archive_entry_set_gname(extra,
+ archive_entry_gname(entry_original));
+ archive_entry_set_uid(extra,
+ archive_entry_uid(entry_original));
+ archive_entry_set_uname(extra,
+ archive_entry_uname(entry_original));
+
+ /* Recurse to write the special copyfile entry. */
+ r = archive_write_pax_header(a, extra);
+ archive_entry_free(extra);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r < ret)
+ ret = r;
+ r = (int)archive_write_pax_data(a, mac_metadata,
+ mac_metadata_size);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r < ret)
+ ret = r;
+ r = archive_write_pax_finish_entry(a);
+ if (r < ARCHIVE_WARN)
+ return (r);
+ if (r < ret)
+ ret = r;
+ }
+
+ /* Copy entry so we can modify it as needed. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry_original);
+ if (entry_main == entry_original)
+ entry_main = archive_entry_clone(entry_original);
+#else
+ entry_main = archive_entry_clone(entry_original);
+#endif
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate pax data");
+ return(ARCHIVE_FATAL);
+ }
+ archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
+ archive_string_empty(&(pax->sparse_map));
+ sparse_total = 0;
+ sparse_list_clear(pax);
+
+ if (hardlink == NULL &&
+ archive_entry_filetype(entry_main) == AE_IFREG)
+ sparse_count = archive_entry_sparse_reset(entry_main);
+ else
+ sparse_count = 0;
+ if (sparse_count) {
+ int64_t offset, length, last_offset = 0;
+ /* Get the last entry of sparse block. */
+ while (archive_entry_sparse_next(
+ entry_main, &offset, &length) == ARCHIVE_OK)
+ last_offset = offset + length;
+
+ /* If the last sparse block does not reach the end of file,
+ * We have to add a empty sparse block as the last entry to
+ * manage storing file data. */
+ if (last_offset < archive_entry_size(entry_main))
+ archive_entry_sparse_add_entry(entry_main,
+ archive_entry_size(entry_main), 0);
+ sparse_count = archive_entry_sparse_reset(entry_main);
+ }
+
+ /*
+ * First, check the name fields and see if any of them
+ * require binary coding. If any of them does, then all of
+ * them do.
+ */
+ r = get_entry_pathname(a, entry_main, &path, &path_length, sconv);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ } else if (r != ARCHIVE_OK) {
+ r = get_entry_pathname(a, entry_main, &path,
+ &path_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s", path,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;/* The header charset switches to binary mode. */
+ }
+ r = get_entry_uname(a, entry_main, &uname, &uname_length, sconv);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ } else if (r != ARCHIVE_OK) {
+ r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate uname '%s' to %s", uname,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;/* The header charset switches to binary mode. */
+ }
+ r = get_entry_gname(a, entry_main, &gname, &gname_length, sconv);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ } else if (r != ARCHIVE_OK) {
+ r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate gname '%s' to %s", gname,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;/* The header charset switches to binary mode. */
+ }
+ linkpath = hardlink;
+ linkpath_length = hardlink_length;
+ if (linkpath == NULL) {
+ r = get_entry_symlink(a, entry_main, &linkpath,
+ &linkpath_length, sconv);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ } else if (r != ARCHIVE_OK) {
+ r = get_entry_symlink(a, entry_main, &linkpath,
+ &linkpath_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s", linkpath,
+ archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ sconv = NULL;
+ }
+ }
+
+ /* If any string conversions failed, get all attributes
+ * in binary-mode. */
+ if (sconv == NULL && !pax->opt_binary) {
+ if (hardlink != NULL) {
+ r = get_entry_hardlink(a, entry_main, &hardlink,
+ &hardlink_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ linkpath = hardlink;
+ linkpath_length = hardlink_length;
+ }
+ r = get_entry_pathname(a, entry_main, &path,
+ &path_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);
+ if (r == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ return (r);
+ }
+ }
+
+ /* Store the header encoding first, to be nice to readers. */
+ if (sconv == NULL)
+ add_pax_attr(&(pax->pax_header), "hdrcharset", "BINARY");
+
+
+ /*
+ * If name is too long, or has non-ASCII characters, add
+ * 'path' to pax extended attrs. (Note that an unconvertible
+ * name must have non-ASCII characters.)
+ */
+ if (has_non_ASCII(path)) {
+ /* We have non-ASCII characters. */
+ add_pax_attr(&(pax->pax_header), "path", path);
+ archive_entry_set_pathname(entry_main,
+ build_ustar_entry_name(ustar_entry_name,
+ path, path_length, NULL));
+ need_extension = 1;
+ } else {
+ /* We have an all-ASCII path; we'd like to just store
+ * it in the ustar header if it will fit. Yes, this
+ * duplicates some of the logic in
+ * archive_write_set_format_ustar.c
+ */
+ if (path_length <= 100) {
+ /* Fits in the old 100-char tar name field. */
+ } else {
+ /* Find largest suffix that will fit. */
+ /* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */
+ suffix = strchr(path + path_length - 100 - 1, '/');
+ /* Don't attempt an empty prefix. */
+ if (suffix == path)
+ suffix = strchr(suffix + 1, '/');
+ /* We can put it in the ustar header if it's
+ * all ASCII and it's either <= 100 characters
+ * or can be split at a '/' into a prefix <=
+ * 155 chars and a suffix <= 100 chars. (Note
+ * the strchr() above will return NULL exactly
+ * when the path can't be split.)
+ */
+ if (suffix == NULL /* Suffix > 100 chars. */
+ || suffix[1] == '\0' /* empty suffix */
+ || suffix - path > 155) /* Prefix > 155 chars */
+ {
+ add_pax_attr(&(pax->pax_header), "path", path);
+ archive_entry_set_pathname(entry_main,
+ build_ustar_entry_name(ustar_entry_name,
+ path, path_length, NULL));
+ need_extension = 1;
+ }
+ }
+ }
+
+ if (linkpath != NULL) {
+ /* If link name is too long or has non-ASCII characters, add
+ * 'linkpath' to pax extended attrs. */
+ if (linkpath_length > 100 || has_non_ASCII(linkpath)) {
+ add_pax_attr(&(pax->pax_header), "linkpath", linkpath);
+ if (linkpath_length > 100) {
+ if (hardlink != NULL)
+ archive_entry_set_hardlink(entry_main,
+ "././@LongHardLink");
+ else
+ archive_entry_set_symlink(entry_main,
+ "././@LongSymLink");
+ }
+ need_extension = 1;
+ }
+ }
+ /* Save a pathname since it will be renamed if `entry_main` has
+ * sparse blocks. */
+ archive_string_init(&entry_name);
+ archive_strcpy(&entry_name, archive_entry_pathname(entry_main));
+
+ /* If file size is too large, we need pax extended attrs. */
+ if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {
+ need_extension = 1;
+ }
+
+ /* If numeric GID is too large, add 'gid' to pax extended attrs. */
+ if ((unsigned int)archive_entry_gid(entry_main) >= (1 << 18)) {
+ add_pax_attr_int(&(pax->pax_header), "gid",
+ archive_entry_gid(entry_main));
+ need_extension = 1;
+ }
+
+ /* If group name is too large or has non-ASCII characters, add
+ * 'gname' to pax extended attrs. */
+ if (gname != NULL) {
+ if (gname_length > 31 || has_non_ASCII(gname)) {
+ add_pax_attr(&(pax->pax_header), "gname", gname);
+ need_extension = 1;
+ }
+ }
+
+ /* If numeric UID is too large, add 'uid' to pax extended attrs. */
+ if ((unsigned int)archive_entry_uid(entry_main) >= (1 << 18)) {
+ add_pax_attr_int(&(pax->pax_header), "uid",
+ archive_entry_uid(entry_main));
+ need_extension = 1;
+ }
+
+ /* Add 'uname' to pax extended attrs if necessary. */
+ if (uname != NULL) {
+ if (uname_length > 31 || has_non_ASCII(uname)) {
+ add_pax_attr(&(pax->pax_header), "uname", uname);
+ need_extension = 1;
+ }
+ }
+
+ /*
+ * POSIX/SUSv3 doesn't provide a standard key for large device
+ * numbers. I use the same keys here that Joerg Schilling
+ * used for 'star.' (Which, somewhat confusingly, are called
+ * "devXXX" even though they code "rdev" values.) No doubt,
+ * other implementations use other keys. Note that there's no
+ * reason we can't write the same information into a number of
+ * different keys.
+ *
+ * Of course, this is only needed for block or char device entries.
+ */
+ if (archive_entry_filetype(entry_main) == AE_IFBLK
+ || archive_entry_filetype(entry_main) == AE_IFCHR) {
+ /*
+ * If rdevmajor is too large, add 'SCHILY.devmajor' to
+ * extended attributes.
+ */
+ int rdevmajor, rdevminor;
+ rdevmajor = archive_entry_rdevmajor(entry_main);
+ rdevminor = archive_entry_rdevminor(entry_main);
+ if (rdevmajor >= (1 << 18)) {
+ add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor",
+ rdevmajor);
+ /*
+ * Non-strict formatting below means we don't
+ * have to truncate here. Not truncating improves
+ * the chance that some more modern tar archivers
+ * (such as GNU tar 1.13) can restore the full
+ * value even if they don't understand the pax
+ * extended attributes. See my rant below about
+ * file size fields for additional details.
+ */
+ /* archive_entry_set_rdevmajor(entry_main,
+ rdevmajor & ((1 << 18) - 1)); */
+ need_extension = 1;
+ }
+
+ /*
+ * If devminor is too large, add 'SCHILY.devminor' to
+ * extended attributes.
+ */
+ if (rdevminor >= (1 << 18)) {
+ add_pax_attr_int(&(pax->pax_header), "SCHILY.devminor",
+ rdevminor);
+ /* Truncation is not necessary here, either. */
+ /* archive_entry_set_rdevminor(entry_main,
+ rdevminor & ((1 << 18) - 1)); */
+ need_extension = 1;
+ }
+ }
+
+ /*
+ * Yes, this check is duplicated just below; this helps to
+ * avoid writing an mtime attribute just to handle a
+ * high-resolution timestamp in "restricted pax" mode.
+ */
+ if (!need_extension &&
+ ((archive_entry_mtime(entry_main) < 0)
+ || (archive_entry_mtime(entry_main) >= ustar_max_mtime)))
+ need_extension = 1;
+
+ /* I use a star-compatible file flag attribute. */
+ p = archive_entry_fflags_text(entry_main);
+ if (!need_extension && p != NULL && *p != '\0')
+ need_extension = 1;
+
+ /* If there are extended attributes, we need an extension */
+ if (!need_extension && archive_entry_xattr_count(entry_original) > 0)
+ need_extension = 1;
+
+ /* If there are sparse info, we need an extension */
+ if (!need_extension && sparse_count > 0)
+ need_extension = 1;
+
+ acl_types = archive_entry_acl_types(entry_original);
+
+ /* If there are any ACL entries, we need an extension */
+ if (!need_extension && acl_types != 0)
+ need_extension = 1;
+
+ /* If the symlink type is defined, we need an extension */
+ if (!need_extension && archive_entry_symlink_type(entry_main) > 0)
+ need_extension = 1;
+
+ /*
+ * Libarchive used to include these in extended headers for
+ * restricted pax format, but that confused people who
+ * expected ustar-like time semantics. So now we only include
+ * them in full pax format.
+ */
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED) {
+ if (archive_entry_ctime(entry_main) != 0 ||
+ archive_entry_ctime_nsec(entry_main) != 0)
+ add_pax_attr_time(&(pax->pax_header), "ctime",
+ archive_entry_ctime(entry_main),
+ archive_entry_ctime_nsec(entry_main));
+
+ if (archive_entry_atime(entry_main) != 0 ||
+ archive_entry_atime_nsec(entry_main) != 0)
+ add_pax_attr_time(&(pax->pax_header), "atime",
+ archive_entry_atime(entry_main),
+ archive_entry_atime_nsec(entry_main));
+
+ /* Store birth/creationtime only if it's earlier than mtime */
+ if (archive_entry_birthtime_is_set(entry_main) &&
+ archive_entry_birthtime(entry_main)
+ < archive_entry_mtime(entry_main))
+ add_pax_attr_time(&(pax->pax_header),
+ "LIBARCHIVE.creationtime",
+ archive_entry_birthtime(entry_main),
+ archive_entry_birthtime_nsec(entry_main));
+ }
+
+ /*
+ * The following items are handled differently in "pax
+ * restricted" format. In particular, in "pax restricted"
+ * format they won't be added unless need_extension is
+ * already set (we're already generating an extended header, so
+ * may as well include these).
+ */
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
+ need_extension) {
+ if (archive_entry_mtime(entry_main) < 0 ||
+ archive_entry_mtime(entry_main) >= ustar_max_mtime ||
+ archive_entry_mtime_nsec(entry_main) != 0)
+ add_pax_attr_time(&(pax->pax_header), "mtime",
+ archive_entry_mtime(entry_main),
+ archive_entry_mtime_nsec(entry_main));
+
+ /* I use a star-compatible file flag attribute. */
+ p = archive_entry_fflags_text(entry_main);
+ if (p != NULL && *p != '\0')
+ add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p);
+
+ /* I use star-compatible ACL attributes. */
+ if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ ret = add_pax_acl(a, entry_original, pax,
+ ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+ ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA |
+ ARCHIVE_ENTRY_ACL_STYLE_COMPACT);
+ if (ret == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+ ret = add_pax_acl(a, entry_original, pax,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+ ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+ ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+ if (ret == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) {
+ ret = add_pax_acl(a, entry_original, pax,
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+ ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+ ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+ if (ret == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /* We use GNU-tar-compatible sparse attributes. */
+ if (sparse_count > 0) {
+ int64_t soffset, slength;
+
+ add_pax_attr_int(&(pax->pax_header),
+ "GNU.sparse.major", 1);
+ add_pax_attr_int(&(pax->pax_header),
+ "GNU.sparse.minor", 0);
+ /*
+ * Make sure to store the original path, since
+ * truncation to ustar limit happened already.
+ */
+ add_pax_attr(&(pax->pax_header),
+ "GNU.sparse.name", path);
+ add_pax_attr_int(&(pax->pax_header),
+ "GNU.sparse.realsize",
+ archive_entry_size(entry_main));
+
+ /* Rename the file name which will be used for
+ * ustar header to a special name, which GNU
+ * PAX Format 1.0 requires */
+ archive_entry_set_pathname(entry_main,
+ build_gnu_sparse_name(gnu_sparse_name,
+ entry_name.s));
+
+ /*
+ * - Make a sparse map, which will precede a file data.
+ * - Get the total size of available data of sparse.
+ */
+ archive_string_sprintf(&(pax->sparse_map), "%d\n",
+ sparse_count);
+ while (archive_entry_sparse_next(entry_main,
+ &soffset, &slength) == ARCHIVE_OK) {
+ archive_string_sprintf(&(pax->sparse_map),
+ "%jd\n%jd\n",
+ (intmax_t)soffset,
+ (intmax_t)slength);
+ sparse_total += slength;
+ if (sparse_list_add(pax, soffset, slength)
+ != ARCHIVE_OK) {
+ archive_set_error(&a->archive,
+ ENOMEM,
+ "Can't allocate memory");
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ }
+
+ /* Store extended attributes */
+ if (archive_write_pax_header_xattrs(a, pax, entry_original)
+ == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Store extended symlink information */
+ if (archive_entry_symlink_type(entry_main) ==
+ AE_SYMLINK_TYPE_FILE) {
+ add_pax_attr(&(pax->pax_header),
+ "LIBARCHIVE.symlinktype", "file");
+ } else if (archive_entry_symlink_type(entry_main) ==
+ AE_SYMLINK_TYPE_DIRECTORY) {
+ add_pax_attr(&(pax->pax_header),
+ "LIBARCHIVE.symlinktype", "dir");
+ }
+ }
+
+ /* Only regular files have data. */
+ if (archive_entry_filetype(entry_main) != AE_IFREG)
+ archive_entry_set_size(entry_main, 0);
+
+ /*
+ * Pax-restricted does not store data for hardlinks, in order
+ * to improve compatibility with ustar.
+ */
+ if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
+ hardlink != NULL)
+ archive_entry_set_size(entry_main, 0);
+
+ /*
+ * XXX Full pax interchange format does permit a hardlink
+ * entry to have data associated with it. I'm not supporting
+ * that here because the client expects me to tell them whether
+ * or not this format expects data for hardlinks. If I
+ * don't check here, then every pax archive will end up with
+ * duplicated data for hardlinks. Someday, there may be
+ * need to select this behavior, in which case the following
+ * will need to be revisited. XXX
+ */
+ if (hardlink != NULL)
+ archive_entry_set_size(entry_main, 0);
+
+ /* Save a real file size. */
+ real_size = archive_entry_size(entry_main);
+ /*
+ * Overwrite a file size by the total size of sparse blocks and
+ * the size of sparse map info. That file size is the length of
+ * the data, which we will exactly store into an archive file.
+ */
+ if (archive_strlen(&(pax->sparse_map))) {
+ size_t mapsize = archive_strlen(&(pax->sparse_map));
+ pax->sparse_map_padding = 0x1ff & (-(ssize_t)mapsize);
+ archive_entry_set_size(entry_main,
+ mapsize + pax->sparse_map_padding + sparse_total);
+ }
+
+ /* If file size is too large, add 'size' to pax extended attrs. */
+ if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {
+ add_pax_attr_int(&(pax->pax_header), "size",
+ archive_entry_size(entry_main));
+ }
+
+ /* Format 'ustar' header for main entry.
+ *
+ * The trouble with file size: If the reader can't understand
+ * the file size, they may not be able to locate the next
+ * entry and the rest of the archive is toast. Pax-compliant
+ * readers are supposed to ignore the file size in the main
+ * header, so the question becomes how to maximize portability
+ * for readers that don't support pax attribute extensions.
+ * For maximum compatibility, I permit numeric extensions in
+ * the main header so that the file size stored will always be
+ * correct, even if it's in a format that only some
+ * implementations understand. The technique used here is:
+ *
+ * a) If possible, follow the standard exactly. This handles
+ * files up to 8 gigabytes minus 1.
+ *
+ * b) If that fails, try octal but omit the field terminator.
+ * That handles files up to 64 gigabytes minus 1.
+ *
+ * c) Otherwise, use base-256 extensions. That handles files
+ * up to 2^63 in this implementation, with the potential to
+ * go up to 2^94. That should hold us for a while. ;-)
+ *
+ * The non-strict formatter uses similar logic for other
+ * numeric fields, though they're less critical.
+ */
+ if (__archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0,
+ NULL) == ARCHIVE_FATAL) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+
+ /* If we built any extended attributes, write that entry first. */
+ if (archive_strlen(&(pax->pax_header)) > 0) {
+ struct archive_entry *pax_attr_entry;
+ time_t s;
+ int64_t uid, gid;
+ int mode;
+
+ pax_attr_entry = archive_entry_new2(&a->archive);
+ p = entry_name.s;
+ archive_entry_set_pathname(pax_attr_entry,
+ build_pax_attribute_name(pax_entry_name, p));
+ archive_entry_set_size(pax_attr_entry,
+ archive_strlen(&(pax->pax_header)));
+ /* Copy uid/gid (but clip to ustar limits). */
+ uid = archive_entry_uid(entry_main);
+ if (uid >= 1 << 18)
+ uid = (1 << 18) - 1;
+ archive_entry_set_uid(pax_attr_entry, uid);
+ gid = archive_entry_gid(entry_main);
+ if (gid >= 1 << 18)
+ gid = (1 << 18) - 1;
+ archive_entry_set_gid(pax_attr_entry, gid);
+ /* Copy mode over (but not setuid/setgid bits) */
+ mode = archive_entry_mode(entry_main);
+#ifdef S_ISUID
+ mode &= ~S_ISUID;
+#endif
+#ifdef S_ISGID
+ mode &= ~S_ISGID;
+#endif
+#ifdef S_ISVTX
+ mode &= ~S_ISVTX;
+#endif
+ archive_entry_set_mode(pax_attr_entry, mode);
+
+ /* Copy uname/gname. */
+ archive_entry_set_uname(pax_attr_entry,
+ archive_entry_uname(entry_main));
+ archive_entry_set_gname(pax_attr_entry,
+ archive_entry_gname(entry_main));
+
+ /* Copy mtime, but clip to ustar limits. */
+ s = archive_entry_mtime(entry_main);
+ if (s < 0) { s = 0; }
+ if (s > ustar_max_mtime) { s = ustar_max_mtime; }
+ archive_entry_set_mtime(pax_attr_entry, s, 0);
+
+ /* Standard ustar doesn't support atime. */
+ archive_entry_set_atime(pax_attr_entry, 0, 0);
+
+ /* Standard ustar doesn't support ctime. */
+ archive_entry_set_ctime(pax_attr_entry, 0, 0);
+
+ r = __archive_write_format_header_ustar(a, paxbuff,
+ pax_attr_entry, 'x', 1, NULL);
+
+ archive_entry_free(pax_attr_entry);
+
+ /* Note that the 'x' header shouldn't ever fail to format */
+ if (r < ARCHIVE_WARN) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "archive_write_pax_header: "
+ "'x' header failed?! This can't happen.\n");
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ } else if (r < ret)
+ ret = r;
+ r = __archive_write_output(a, paxbuff, 512);
+ if (r != ARCHIVE_OK) {
+ sparse_list_clear(pax);
+ pax->entry_bytes_remaining = 0;
+ pax->entry_padding = 0;
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+
+ pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header));
+ pax->entry_padding =
+ 0x1ff & (-(int64_t)pax->entry_bytes_remaining);
+
+ r = __archive_write_output(a, pax->pax_header.s,
+ archive_strlen(&(pax->pax_header)));
+ if (r != ARCHIVE_OK) {
+ /* If a write fails, we're pretty much toast. */
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ /* Pad out the end of the entry. */
+ r = __archive_write_nulls(a, (size_t)pax->entry_padding);
+ if (r != ARCHIVE_OK) {
+ /* If a write fails, we're pretty much toast. */
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (ARCHIVE_FATAL);
+ }
+ pax->entry_bytes_remaining = pax->entry_padding = 0;
+ }
+
+ /* Write the header for main entry. */
+ r = __archive_write_output(a, ustarbuff, 512);
+ if (r != ARCHIVE_OK) {
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+ return (r);
+ }
+
+ /*
+ * Inform the client of the on-disk size we're using, so
+ * they can avoid unnecessarily writing a body for something
+ * that we're just going to ignore.
+ */
+ archive_entry_set_size(entry_original, real_size);
+ if (pax->sparse_list == NULL && real_size > 0) {
+ /* This is not a sparse file but we handle its data as
+ * a sparse block. */
+ sparse_list_add(pax, 0, real_size);
+ sparse_total = real_size;
+ }
+ pax->entry_padding = 0x1ff & (-(int64_t)sparse_total);
+ archive_entry_free(entry_main);
+ archive_string_free(&entry_name);
+
+ return (ret);
+}
+
+/*
+ * We need a valid name for the regular 'ustar' entry. This routine
+ * tries to hack something more-or-less reasonable.
+ *
+ * The approach here tries to preserve leading dir names. We do so by
+ * working with four sections:
+ * 1) "prefix" directory names,
+ * 2) "suffix" directory names,
+ * 3) inserted dir name (optional),
+ * 4) filename.
+ *
+ * These sections must satisfy the following requirements:
+ * * Parts 1 & 2 together form an initial portion of the dir name.
+ * * Part 3 is specified by the caller. (It should not contain a leading
+ * or trailing '/'.)
+ * * Part 4 forms an initial portion of the base filename.
+ * * The filename must be <= 99 chars to fit the ustar 'name' field.
+ * * Parts 2, 3, 4 together must be <= 99 chars to fit the ustar 'name' fld.
+ * * Part 1 must be <= 155 chars to fit the ustar 'prefix' field.
+ * * If the original name ends in a '/', the new name must also end in a '/'
+ * * Trailing '/.' sequences may be stripped.
+ *
+ * Note: Recall that the ustar format does not store the '/' separating
+ * parts 1 & 2, but does store the '/' separating parts 2 & 3.
+ */
+static char *
+build_ustar_entry_name(char *dest, const char *src, size_t src_length,
+ const char *insert)
+{
+ const char *prefix, *prefix_end;
+ const char *suffix, *suffix_end;
+ const char *filename, *filename_end;
+ char *p;
+ int need_slash = 0; /* Was there a trailing slash? */
+ size_t suffix_length = 99;
+ size_t insert_length;
+
+ /* Length of additional dir element to be added. */
+ if (insert == NULL)
+ insert_length = 0;
+ else
+ /* +2 here allows for '/' before and after the insert. */
+ insert_length = strlen(insert) + 2;
+
+ /* Step 0: Quick bailout in a common case. */
+ if (src_length < 100 && insert == NULL) {
+ strncpy(dest, src, src_length);
+ dest[src_length] = '\0';
+ return (dest);
+ }
+
+ /* Step 1: Locate filename and enforce the length restriction. */
+ filename_end = src + src_length;
+ /* Remove trailing '/' chars and '/.' pairs. */
+ for (;;) {
+ if (filename_end > src && filename_end[-1] == '/') {
+ filename_end --;
+ need_slash = 1; /* Remember to restore trailing '/'. */
+ continue;
+ }
+ if (filename_end > src + 1 && filename_end[-1] == '.'
+ && filename_end[-2] == '/') {
+ filename_end -= 2;
+ need_slash = 1; /* "foo/." will become "foo/" */
+ continue;
+ }
+ break;
+ }
+ if (need_slash)
+ suffix_length--;
+ /* Find start of filename. */
+ filename = filename_end - 1;
+ while ((filename > src) && (*filename != '/'))
+ filename --;
+ if ((*filename == '/') && (filename < filename_end - 1))
+ filename ++;
+ /* Adjust filename_end so that filename + insert fits in 99 chars. */
+ suffix_length -= insert_length;
+ if (filename_end > filename + suffix_length)
+ filename_end = filename + suffix_length;
+ /* Calculate max size for "suffix" section (#3 above). */
+ suffix_length -= filename_end - filename;
+
+ /* Step 2: Locate the "prefix" section of the dirname, including
+ * trailing '/'. */
+ prefix = src;
+ prefix_end = prefix + 155;
+ if (prefix_end > filename)
+ prefix_end = filename;
+ while (prefix_end > prefix && *prefix_end != '/')
+ prefix_end--;
+ if ((prefix_end < filename) && (*prefix_end == '/'))
+ prefix_end++;
+
+ /* Step 3: Locate the "suffix" section of the dirname,
+ * including trailing '/'. */
+ suffix = prefix_end;
+ suffix_end = suffix + suffix_length; /* Enforce limit. */
+ if (suffix_end > filename)
+ suffix_end = filename;
+ if (suffix_end < suffix)
+ suffix_end = suffix;
+ while (suffix_end > suffix && *suffix_end != '/')
+ suffix_end--;
+ if ((suffix_end < filename) && (*suffix_end == '/'))
+ suffix_end++;
+
+ /* Step 4: Build the new name. */
+ /* The OpenBSD strlcpy function is safer, but less portable. */
+ /* Rather than maintain two versions, just use the strncpy version. */
+ p = dest;
+ if (prefix_end > prefix) {
+ strncpy(p, prefix, prefix_end - prefix);
+ p += prefix_end - prefix;
+ }
+ if (suffix_end > suffix) {
+ strncpy(p, suffix, suffix_end - suffix);
+ p += suffix_end - suffix;
+ }
+ if (insert != NULL) {
+ /* Note: assume insert does not have leading or trailing '/' */
+ strcpy(p, insert);
+ p += strlen(insert);
+ *p++ = '/';
+ }
+ strncpy(p, filename, filename_end - filename);
+ p += filename_end - filename;
+ if (need_slash)
+ *p++ = '/';
+ *p = '\0';
+
+ return (dest);
+}
+
+/*
+ * The ustar header for the pax extended attributes must have a
+ * reasonable name: SUSv3 requires 'dirname'/PaxHeader.'pid'/'filename'
+ * where 'pid' is the PID of the archiving process. Unfortunately,
+ * that makes testing a pain since the output varies for each run,
+ * so I'm sticking with the simpler 'dirname'/PaxHeader/'filename'
+ * for now. (Someday, I'll make this settable. Then I can use the
+ * SUS recommendation as default and test harnesses can override it
+ * to get predictable results.)
+ *
+ * Joerg Schilling has argued that this is unnecessary because, in
+ * practice, if the pax extended attributes get extracted as regular
+ * files, no one is going to bother reading those attributes to
+ * manually restore them. Based on this, 'star' uses
+ * /tmp/PaxHeader/'basename' as the ustar header name. This is a
+ * tempting argument, in part because it's simpler than the SUSv3
+ * recommendation, but I'm not entirely convinced. I'm also
+ * uncomfortable with the fact that "/tmp" is a Unix-ism.
+ *
+ * The following routine leverages build_ustar_entry_name() above and
+ * so is simpler than you might think. It just needs to provide the
+ * additional path element and handle a few pathological cases).
+ */
+static char *
+build_pax_attribute_name(char *dest, const char *src)
+{
+ char buff[64];
+ const char *p;
+
+ /* Handle the null filename case. */
+ if (src == NULL || *src == '\0') {
+ strcpy(dest, "PaxHeader/blank");
+ return (dest);
+ }
+
+ /* Prune final '/' and other unwanted final elements. */
+ p = src + strlen(src);
+ for (;;) {
+ /* Ends in "/", remove the '/' */
+ if (p > src && p[-1] == '/') {
+ --p;
+ continue;
+ }
+ /* Ends in "/.", remove the '.' */
+ if (p > src + 1 && p[-1] == '.'
+ && p[-2] == '/') {
+ --p;
+ continue;
+ }
+ break;
+ }
+
+ /* Pathological case: After above, there was nothing left.
+ * This includes "/." "/./." "/.//./." etc. */
+ if (p == src) {
+ strcpy(dest, "/PaxHeader/rootdir");
+ return (dest);
+ }
+
+ /* Convert unadorned "." into a suitable filename. */
+ if (*src == '.' && p == src + 1) {
+ strcpy(dest, "PaxHeader/currentdir");
+ return (dest);
+ }
+
+ /*
+ * TODO: Push this string into the 'pax' structure to avoid
+ * recomputing it every time. That will also open the door
+ * to having clients override it.
+ */
+#if HAVE_GETPID && 0 /* Disable this for now; see above comment. */
+ snprintf(buff, sizeof(buff), "PaxHeader.%d", getpid());
+#else
+ /* If the platform can't fetch the pid, don't include it. */
+ strcpy(buff, "PaxHeader");
+#endif
+ /* General case: build a ustar-compatible name adding
+ * "/PaxHeader/". */
+ build_ustar_entry_name(dest, src, p - src, buff);
+
+ return (dest);
+}
+
+/*
+ * GNU PAX Format 1.0 requires the special name, which pattern is:
+ * <dir>/GNUSparseFile.<pid>/<original file name>
+ *
+ * Since reproducible archives are more important, use 0 as pid.
+ *
+ * This function is used for only Sparse file, a file type of which
+ * is regular file.
+ */
+static char *
+build_gnu_sparse_name(char *dest, const char *src)
+{
+ const char *p;
+
+ /* Handle the null filename case. */
+ if (src == NULL || *src == '\0') {
+ strcpy(dest, "GNUSparseFile/blank");
+ return (dest);
+ }
+
+ /* Prune final '/' and other unwanted final elements. */
+ p = src + strlen(src);
+ for (;;) {
+ /* Ends in "/", remove the '/' */
+ if (p > src && p[-1] == '/') {
+ --p;
+ continue;
+ }
+ /* Ends in "/.", remove the '.' */
+ if (p > src + 1 && p[-1] == '.'
+ && p[-2] == '/') {
+ --p;
+ continue;
+ }
+ break;
+ }
+
+ /* General case: build a ustar-compatible name adding
+ * "/GNUSparseFile/". */
+ build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0");
+
+ return (dest);
+}
+
+/* Write two null blocks for the end of archive */
+static int
+archive_write_pax_close(struct archive_write *a)
+{
+ return (__archive_write_nulls(a, 512 * 2));
+}
+
+static int
+archive_write_pax_free(struct archive_write *a)
+{
+ struct pax *pax;
+
+ pax = (struct pax *)a->format_data;
+ if (pax == NULL)
+ return (ARCHIVE_OK);
+
+ archive_string_free(&pax->pax_header);
+ archive_string_free(&pax->sparse_map);
+ archive_string_free(&pax->l_url_encoded_name);
+ sparse_list_clear(pax);
+ free(pax);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_pax_finish_entry(struct archive_write *a)
+{
+ struct pax *pax;
+ uint64_t remaining;
+ int ret;
+
+ pax = (struct pax *)a->format_data;
+ remaining = pax->entry_bytes_remaining;
+ if (remaining == 0) {
+ while (pax->sparse_list) {
+ struct sparse_block *sb;
+ if (!pax->sparse_list->is_hole)
+ remaining += pax->sparse_list->remaining;
+ sb = pax->sparse_list->next;
+ free(pax->sparse_list);
+ pax->sparse_list = sb;
+ }
+ }
+ ret = __archive_write_nulls(a, (size_t)(remaining + pax->entry_padding));
+ pax->entry_bytes_remaining = pax->entry_padding = 0;
+ return (ret);
+}
+
+static ssize_t
+archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct pax *pax;
+ size_t ws;
+ size_t total;
+ int ret;
+
+ pax = (struct pax *)a->format_data;
+
+ /*
+ * According to GNU PAX format 1.0, write a sparse map
+ * before the body.
+ */
+ if (archive_strlen(&(pax->sparse_map))) {
+ ret = __archive_write_output(a, pax->sparse_map.s,
+ archive_strlen(&(pax->sparse_map)));
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ ret = __archive_write_nulls(a, pax->sparse_map_padding);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ archive_string_empty(&(pax->sparse_map));
+ }
+
+ total = 0;
+ while (total < s) {
+ const unsigned char *p;
+
+ while (pax->sparse_list != NULL &&
+ pax->sparse_list->remaining == 0) {
+ struct sparse_block *sb = pax->sparse_list->next;
+ free(pax->sparse_list);
+ pax->sparse_list = sb;
+ }
+
+ if (pax->sparse_list == NULL)
+ return (total);
+
+ p = ((const unsigned char *)buff) + total;
+ ws = s - total;
+ if (ws > pax->sparse_list->remaining)
+ ws = (size_t)pax->sparse_list->remaining;
+
+ if (pax->sparse_list->is_hole) {
+ /* Current block is hole thus we do not write
+ * the body. */
+ pax->sparse_list->remaining -= ws;
+ total += ws;
+ continue;
+ }
+
+ ret = __archive_write_output(a, p, ws);
+ pax->sparse_list->remaining -= ws;
+ total += ws;
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+ return (total);
+}
+
+static int
+has_non_ASCII(const char *_p)
+{
+ const unsigned char *p = (const unsigned char *)_p;
+
+ if (p == NULL)
+ return (1);
+ while (*p != '\0' && *p < 128)
+ p++;
+ return (*p != '\0');
+}
+
+/*
+ * Used by extended attribute support; encodes the name
+ * so that there will be no '=' characters in the result.
+ */
+static char *
+url_encode(const char *in)
+{
+ const char *s;
+ char *d;
+ size_t out_len = 0;
+ char *out;
+
+ for (s = in; *s != '\0'; s++) {
+ if (*s < 33 || *s > 126 || *s == '%' || *s == '=') {
+ if (SIZE_MAX - out_len < 4)
+ return (NULL);
+ out_len += 3;
+ } else {
+ if (SIZE_MAX - out_len < 2)
+ return (NULL);
+ out_len++;
+ }
+ }
+
+ out = (char *)malloc(out_len + 1);
+ if (out == NULL)
+ return (NULL);
+
+ for (s = in, d = out; *s != '\0'; s++) {
+ /* encode any non-printable ASCII character or '%' or '=' */
+ if (*s < 33 || *s > 126 || *s == '%' || *s == '=') {
+ /* URL encoding is '%' followed by two hex digits */
+ *d++ = '%';
+ *d++ = "0123456789ABCDEF"[0x0f & (*s >> 4)];
+ *d++ = "0123456789ABCDEF"[0x0f & *s];
+ } else {
+ *d++ = *s;
+ }
+ }
+ *d = '\0';
+ return (out);
+}
+
+/*
+ * Encode a sequence of bytes into a C string using base-64 encoding.
+ *
+ * Returns a null-terminated C string allocated with malloc(); caller
+ * is responsible for freeing the result.
+ */
+static char *
+base64_encode(const char *s, size_t len)
+{
+ static const char digits[64] =
+ { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
+ 'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d',
+ 'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
+ 't','u','v','w','x','y','z','0','1','2','3','4','5','6','7',
+ '8','9','+','/' };
+ int v;
+ char *d, *out;
+
+ /* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */
+ out = (char *)malloc((len * 4 + 2) / 3 + 1);
+ if (out == NULL)
+ return (NULL);
+ d = out;
+
+ /* Convert each group of 3 bytes into 4 characters. */
+ while (len >= 3) {
+ v = (((int)s[0] << 16) & 0xff0000)
+ | (((int)s[1] << 8) & 0xff00)
+ | (((int)s[2]) & 0x00ff);
+ s += 3;
+ len -= 3;
+ *d++ = digits[(v >> 18) & 0x3f];
+ *d++ = digits[(v >> 12) & 0x3f];
+ *d++ = digits[(v >> 6) & 0x3f];
+ *d++ = digits[(v) & 0x3f];
+ }
+ /* Handle final group of 1 byte (2 chars) or 2 bytes (3 chars). */
+ switch (len) {
+ case 0: break;
+ case 1:
+ v = (((int)s[0] << 16) & 0xff0000);
+ *d++ = digits[(v >> 18) & 0x3f];
+ *d++ = digits[(v >> 12) & 0x3f];
+ break;
+ case 2:
+ v = (((int)s[0] << 16) & 0xff0000)
+ | (((int)s[1] << 8) & 0xff00);
+ *d++ = digits[(v >> 18) & 0x3f];
+ *d++ = digits[(v >> 12) & 0x3f];
+ *d++ = digits[(v >> 6) & 0x3f];
+ break;
+ }
+ /* Add trailing NUL character so output is a valid C string. */
+ *d = '\0';
+ return (out);
+}
+
+static void
+sparse_list_clear(struct pax *pax)
+{
+ while (pax->sparse_list != NULL) {
+ struct sparse_block *sb = pax->sparse_list;
+ pax->sparse_list = sb->next;
+ free(sb);
+ }
+ pax->sparse_tail = NULL;
+}
+
+static int
+_sparse_list_add_block(struct pax *pax, int64_t offset, int64_t length,
+ int is_hole)
+{
+ struct sparse_block *sb;
+
+ sb = (struct sparse_block *)malloc(sizeof(*sb));
+ if (sb == NULL)
+ return (ARCHIVE_FATAL);
+ sb->next = NULL;
+ sb->is_hole = is_hole;
+ sb->offset = offset;
+ sb->remaining = length;
+ if (pax->sparse_list == NULL || pax->sparse_tail == NULL)
+ pax->sparse_list = pax->sparse_tail = sb;
+ else {
+ pax->sparse_tail->next = sb;
+ pax->sparse_tail = sb;
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+sparse_list_add(struct pax *pax, int64_t offset, int64_t length)
+{
+ int64_t last_offset;
+ int r;
+
+ if (pax->sparse_tail == NULL)
+ last_offset = 0;
+ else {
+ last_offset = pax->sparse_tail->offset +
+ pax->sparse_tail->remaining;
+ }
+ if (last_offset < offset) {
+ /* Add a hole block. */
+ r = _sparse_list_add_block(pax, last_offset,
+ offset - last_offset, 1);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ /* Add data block. */
+ return (_sparse_list_add_block(pax, offset, length, 0));
+}
+
+static time_t
+get_ustar_max_mtime(void)
+{
+ /*
+ * Technically, the mtime field in the ustar header can
+ * support 33 bits. We are using all of them to keep
+ * tar/test/test_option_C_mtree.c simple and passing after 2038.
+ * For platforms that use signed 32-bit time values we
+ * use the 32-bit maximum.
+ */
+ if (sizeof(time_t) > sizeof(int32_t))
+ return (time_t)0x1ffffffff;
+ else
+ return (time_t)0x7fffffff;
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_private.h b/contrib/libs/libarchive/libarchive/archive_write_set_format_private.h
new file mode 100644
index 0000000000..e20022755f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_private.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2020 Martin Matuska
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+void __archive_write_entry_filetype_unsupported(struct archive *a,
+ struct archive_entry *entry, const char *format);
+#endif
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_raw.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_raw.c
new file mode 100644
index 0000000000..feff936977
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_raw.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2013 Marek Kubica
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_write_private.h"
+
+static ssize_t archive_write_raw_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_raw_free(struct archive_write *);
+static int archive_write_raw_header(struct archive_write *,
+ struct archive_entry *);
+
+struct raw {
+ int entries_written;
+};
+
+/*
+ * Set output format to 'raw' format.
+ */
+int
+archive_write_set_format_raw(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct raw *raw;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_raw");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ raw = (struct raw *)calloc(1, sizeof(*raw));
+ if (raw == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate raw data");
+ return (ARCHIVE_FATAL);
+ }
+ raw->entries_written = 0;
+ a->format_data = raw;
+ a->format_name = "raw";
+ /* no options exist for this format */
+ a->format_options = NULL;
+ a->format_write_header = archive_write_raw_header;
+ a->format_write_data = archive_write_raw_data;
+ a->format_finish_entry = NULL;
+ /* nothing needs to be done on closing */
+ a->format_close = NULL;
+ a->format_free = archive_write_raw_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_RAW;
+ a->archive.archive_format_name = "RAW";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_raw_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct raw *raw = (struct raw *)a->format_data;
+
+ if (archive_entry_filetype(entry) != AE_IFREG) {
+ archive_set_error(&a->archive, ERANGE,
+ "Raw format only supports filetype AE_IFREG");
+ return (ARCHIVE_FATAL);
+ }
+
+
+ if (raw->entries_written > 0) {
+ archive_set_error(&a->archive, ERANGE,
+ "Raw format only supports one entry per archive");
+ return (ARCHIVE_FATAL);
+ }
+ raw->entries_written++;
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_raw_data(struct archive_write *a, const void *buff, size_t s)
+{
+ int ret;
+
+ ret = __archive_write_output(a, buff, s);
+ if (ret >= 0)
+ return (s);
+ else
+ return (ret);
+}
+
+static int
+archive_write_raw_free(struct archive_write *a)
+{
+ struct raw *raw;
+
+ raw = (struct raw *)a->format_data;
+ free(raw);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_shar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_shar.c
new file mode 100644
index 0000000000..9e4931c95c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_shar.c
@@ -0,0 +1,641 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_shar.c 189438 2009-03-06 05:58:56Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct shar {
+ int dump;
+ int end_of_line;
+ struct archive_entry *entry;
+ int has_data;
+ char *last_dir;
+
+ /* Line buffer for uuencoded dump format */
+ char outbuff[45];
+ size_t outpos;
+
+ int wrote_header;
+ struct archive_string work;
+ struct archive_string quoted_name;
+};
+
+static int archive_write_shar_close(struct archive_write *);
+static int archive_write_shar_free(struct archive_write *);
+static int archive_write_shar_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t archive_write_shar_data_sed(struct archive_write *,
+ const void * buff, size_t);
+static ssize_t archive_write_shar_data_uuencode(struct archive_write *,
+ const void * buff, size_t);
+static int archive_write_shar_finish_entry(struct archive_write *);
+
+/*
+ * Copy the given string to the buffer, quoting all shell meta characters
+ * found.
+ */
+static void
+shar_quote(struct archive_string *buf, const char *str, int in_shell)
+{
+ static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
+ size_t len;
+
+ while (*str != '\0') {
+ if ((len = strcspn(str, meta)) != 0) {
+ archive_strncat(buf, str, len);
+ str += len;
+ } else if (*str == '\n') {
+ if (in_shell)
+ archive_strcat(buf, "\"\n\"");
+ else
+ archive_strcat(buf, "\\n");
+ ++str;
+ } else {
+ archive_strappend_char(buf, '\\');
+ archive_strappend_char(buf, *str);
+ ++str;
+ }
+ }
+}
+
+/*
+ * Set output format to 'shar' format.
+ */
+int
+archive_write_set_format_shar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct shar *shar;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_shar");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ shar = (struct shar *)calloc(1, sizeof(*shar));
+ if (shar == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_init(&shar->work);
+ archive_string_init(&shar->quoted_name);
+ a->format_data = shar;
+ a->format_name = "shar";
+ a->format_write_header = archive_write_shar_header;
+ a->format_close = archive_write_shar_close;
+ a->format_free = archive_write_shar_free;
+ a->format_write_data = archive_write_shar_data_sed;
+ a->format_finish_entry = archive_write_shar_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
+ a->archive.archive_format_name = "shar";
+ return (ARCHIVE_OK);
+}
+
+/*
+ * An alternate 'shar' that uses uudecode instead of 'sed' to encode
+ * file contents and can therefore be used to archive binary files.
+ * In addition, this variant also attempts to restore ownership, file modes,
+ * and other extended file information.
+ */
+int
+archive_write_set_format_shar_dump(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct shar *shar;
+
+ archive_write_set_format_shar(&a->archive);
+ shar = (struct shar *)a->format_data;
+ shar->dump = 1;
+ a->format_write_data = archive_write_shar_data_uuencode;
+ a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
+ a->archive.archive_format_name = "shar dump";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
+{
+ const char *linkname;
+ const char *name;
+ char *p, *pp;
+ struct shar *shar;
+
+ shar = (struct shar *)a->format_data;
+ if (!shar->wrote_header) {
+ archive_strcat(&shar->work, "#!/bin/sh\n");
+ archive_strcat(&shar->work, "# This is a shell archive\n");
+ shar->wrote_header = 1;
+ }
+
+ /* Save the entry for the closing. */
+ archive_entry_free(shar->entry);
+ shar->entry = archive_entry_clone(entry);
+ name = archive_entry_pathname(entry);
+
+ /* Handle some preparatory issues. */
+ switch(archive_entry_filetype(entry)) {
+ case AE_IFREG:
+ /* Only regular files have non-zero size. */
+ break;
+ case AE_IFDIR:
+ archive_entry_set_size(entry, 0);
+ /* Don't bother trying to recreate '.' */
+ if (strcmp(name, ".") == 0 || strcmp(name, "./") == 0)
+ return (ARCHIVE_OK);
+ break;
+ case AE_IFIFO:
+ case AE_IFCHR:
+ case AE_IFBLK:
+ /* All other file types have zero size in the archive. */
+ archive_entry_set_size(entry, 0);
+ break;
+ default:
+ archive_entry_set_size(entry, 0);
+ if (archive_entry_hardlink(entry) == NULL &&
+ archive_entry_symlink(entry) == NULL) {
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "shar");
+ return (ARCHIVE_WARN);
+ }
+ }
+
+ archive_string_empty(&shar->quoted_name);
+ shar_quote(&shar->quoted_name, name, 1);
+
+ /* Stock preparation for all file types. */
+ archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);
+
+ if (archive_entry_filetype(entry) != AE_IFDIR) {
+ /* Try to create the dir. */
+ p = strdup(name);
+ pp = strrchr(p, '/');
+ /* If there is a / character, try to create the dir. */
+ if (pp != NULL) {
+ *pp = '\0';
+
+ /* Try to avoid a lot of redundant mkdir commands. */
+ if (strcmp(p, ".") == 0) {
+ /* Don't try to "mkdir ." */
+ free(p);
+ } else if (shar->last_dir == NULL) {
+ archive_strcat(&shar->work, "mkdir -p ");
+ shar_quote(&shar->work, p, 1);
+ archive_strcat(&shar->work,
+ " > /dev/null 2>&1\n");
+ shar->last_dir = p;
+ } else if (strcmp(p, shar->last_dir) == 0) {
+ /* We've already created this exact dir. */
+ free(p);
+ } else if (strlen(p) < strlen(shar->last_dir) &&
+ strncmp(p, shar->last_dir, strlen(p)) == 0) {
+ /* We've already created a subdir. */
+ free(p);
+ } else {
+ archive_strcat(&shar->work, "mkdir -p ");
+ shar_quote(&shar->work, p, 1);
+ archive_strcat(&shar->work,
+ " > /dev/null 2>&1\n");
+ shar->last_dir = p;
+ }
+ } else {
+ free(p);
+ }
+ }
+
+ /* Handle file-type specific issues. */
+ shar->has_data = 0;
+ if ((linkname = archive_entry_hardlink(entry)) != NULL) {
+ archive_strcat(&shar->work, "ln -f ");
+ shar_quote(&shar->work, linkname, 1);
+ archive_string_sprintf(&shar->work, " %s\n",
+ shar->quoted_name.s);
+ } else if ((linkname = archive_entry_symlink(entry)) != NULL) {
+ archive_strcat(&shar->work, "ln -fs ");
+ shar_quote(&shar->work, linkname, 1);
+ archive_string_sprintf(&shar->work, " %s\n",
+ shar->quoted_name.s);
+ } else {
+ switch(archive_entry_filetype(entry)) {
+ case AE_IFREG:
+ if (archive_entry_size(entry) == 0) {
+ /* More portable than "touch." */
+ archive_string_sprintf(&shar->work,
+ "test -e \"%s\" || :> \"%s\"\n",
+ shar->quoted_name.s, shar->quoted_name.s);
+ } else {
+ if (shar->dump) {
+ unsigned int mode = archive_entry_mode(entry) & 0777;
+ archive_string_sprintf(&shar->work,
+ "uudecode -p > %s << 'SHAR_END'\n",
+ shar->quoted_name.s);
+ archive_string_sprintf(&shar->work,
+ "begin %o ", mode);
+ shar_quote(&shar->work, name, 0);
+ archive_strcat(&shar->work, "\n");
+ } else {
+ archive_string_sprintf(&shar->work,
+ "sed 's/^X//' > %s << 'SHAR_END'\n",
+ shar->quoted_name.s);
+ }
+ shar->has_data = 1;
+ shar->end_of_line = 1;
+ shar->outpos = 0;
+ }
+ break;
+ case AE_IFDIR:
+ archive_string_sprintf(&shar->work,
+ "mkdir -p %s > /dev/null 2>&1\n",
+ shar->quoted_name.s);
+ /* Record that we just created this directory. */
+ free(shar->last_dir);
+
+ shar->last_dir = strdup(name);
+ /* Trim a trailing '/'. */
+ pp = strrchr(shar->last_dir, '/');
+ if (pp != NULL && pp[1] == '\0')
+ *pp = '\0';
+ /*
+ * TODO: Put dir name/mode on a list to be fixed
+ * up at end of archive.
+ */
+ break;
+ case AE_IFIFO:
+ archive_string_sprintf(&shar->work,
+ "mkfifo %s\n", shar->quoted_name.s);
+ break;
+ case AE_IFCHR:
+ archive_string_sprintf(&shar->work,
+ "mknod %s c %ju %ju\n", shar->quoted_name.s,
+ (uintmax_t)archive_entry_rdevmajor(entry),
+ (uintmax_t)archive_entry_rdevminor(entry));
+ break;
+ case AE_IFBLK:
+ archive_string_sprintf(&shar->work,
+ "mknod %s b %ju %ju\n", shar->quoted_name.s,
+ (uintmax_t)archive_entry_rdevmajor(entry),
+ (uintmax_t)archive_entry_rdevminor(entry));
+ break;
+ default:
+ return (ARCHIVE_WARN);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
+{
+ static const size_t ensured = 65533;
+ struct shar *shar;
+ const char *src;
+ char *buf, *buf_end;
+ int ret;
+ size_t written = n;
+
+ shar = (struct shar *)a->format_data;
+ if (!shar->has_data || n == 0)
+ return (0);
+
+ src = (const char *)buff;
+
+ /*
+ * ensure is the number of bytes in buffer before expanding the
+ * current character. Each operation writes the current character
+ * and optionally the start-of-new-line marker. This can happen
+ * twice before entering the loop, so make sure three additional
+ * bytes can be written.
+ */
+ if (archive_string_ensure(&shar->work, ensured + 3) == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (shar->work.length > ensured) {
+ ret = __archive_write_output(a, shar->work.s,
+ shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ archive_string_empty(&shar->work);
+ }
+ buf = shar->work.s + shar->work.length;
+ buf_end = shar->work.s + ensured;
+
+ if (shar->end_of_line) {
+ *buf++ = 'X';
+ shar->end_of_line = 0;
+ }
+
+ while (n-- != 0) {
+ if ((*buf++ = *src++) == '\n') {
+ if (n == 0)
+ shar->end_of_line = 1;
+ else
+ *buf++ = 'X';
+ }
+
+ if (buf >= buf_end) {
+ shar->work.length = buf - shar->work.s;
+ ret = __archive_write_output(a, shar->work.s,
+ shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ archive_string_empty(&shar->work);
+ buf = shar->work.s;
+ }
+ }
+
+ shar->work.length = buf - shar->work.s;
+
+ return (written);
+}
+
+#define UUENC(c) (((c)!=0) ? ((c) & 077) + ' ': '`')
+
+static void
+uuencode_group(const char _in[3], char out[4])
+{
+ const unsigned char *in = (const unsigned char *)_in;
+ int t;
+
+ t = (in[0] << 16) | (in[1] << 8) | in[2];
+ out[0] = UUENC( 0x3f & (t >> 18) );
+ out[1] = UUENC( 0x3f & (t >> 12) );
+ out[2] = UUENC( 0x3f & (t >> 6) );
+ out[3] = UUENC( 0x3f & t );
+}
+
+static int
+_uuencode_line(struct archive_write *a, struct shar *shar, const char *inbuf, size_t len)
+{
+ char *buf;
+ size_t alloc_len;
+
+ /* len <= 45 -> expanded to 60 + len byte + new line */
+ alloc_len = shar->work.length + 62;
+ if (archive_string_ensure(&shar->work, alloc_len) == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+
+ buf = shar->work.s + shar->work.length;
+ *buf++ = UUENC(len);
+ while (len >= 3) {
+ uuencode_group(inbuf, buf);
+ len -= 3;
+ inbuf += 3;
+ buf += 4;
+ }
+ if (len != 0) {
+ char tmp_buf[3];
+ tmp_buf[0] = inbuf[0];
+ if (len == 1)
+ tmp_buf[1] = '\0';
+ else
+ tmp_buf[1] = inbuf[1];
+ tmp_buf[2] = '\0';
+ uuencode_group(tmp_buf, buf);
+ buf += 4;
+ }
+ *buf++ = '\n';
+ if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62)) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "Buffer overflow");
+ return (ARCHIVE_FATAL);
+ }
+ shar->work.length = buf - shar->work.s;
+ return (ARCHIVE_OK);
+}
+
+#define uuencode_line(__a, __shar, __inbuf, __len) \
+ do { \
+ int r = _uuencode_line(__a, __shar, __inbuf, __len); \
+ if (r != ARCHIVE_OK) \
+ return (ARCHIVE_FATAL); \
+ } while (0)
+
+static ssize_t
+archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
+ size_t length)
+{
+ struct shar *shar;
+ const char *src;
+ size_t n;
+ int ret;
+
+ shar = (struct shar *)a->format_data;
+ if (!shar->has_data)
+ return (ARCHIVE_OK);
+ src = (const char *)buff;
+
+ if (shar->outpos != 0) {
+ n = 45 - shar->outpos;
+ if (n > length)
+ n = length;
+ memcpy(shar->outbuff + shar->outpos, src, n);
+ if (shar->outpos + n < 45) {
+ shar->outpos += n;
+ return length;
+ }
+ uuencode_line(a, shar, shar->outbuff, 45);
+ src += n;
+ n = length - n;
+ } else {
+ n = length;
+ }
+
+ while (n >= 45) {
+ uuencode_line(a, shar, src, 45);
+ src += 45;
+ n -= 45;
+
+ if (shar->work.length < 65536)
+ continue;
+ ret = __archive_write_output(a, shar->work.s,
+ shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ archive_string_empty(&shar->work);
+ }
+ if (n != 0) {
+ memcpy(shar->outbuff, src, n);
+ shar->outpos = n;
+ }
+ return (length);
+}
+
+static int
+archive_write_shar_finish_entry(struct archive_write *a)
+{
+ const char *g, *p, *u;
+ struct shar *shar;
+ int ret;
+
+ shar = (struct shar *)a->format_data;
+ if (shar->entry == NULL)
+ return (0);
+
+ if (shar->dump) {
+ /* Finish uuencoded data. */
+ if (shar->has_data) {
+ if (shar->outpos > 0)
+ uuencode_line(a, shar, shar->outbuff,
+ shar->outpos);
+ archive_strcat(&shar->work, "`\nend\n");
+ archive_strcat(&shar->work, "SHAR_END\n");
+ }
+ /* Restore file mode, owner, flags. */
+ /*
+ * TODO: Don't immediately restore mode for
+ * directories; defer that to end of script.
+ */
+ archive_string_sprintf(&shar->work, "chmod %o ",
+ (unsigned int)(archive_entry_mode(shar->entry) & 07777));
+ shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);
+ archive_strcat(&shar->work, "\n");
+
+ u = archive_entry_uname(shar->entry);
+ g = archive_entry_gname(shar->entry);
+ if (u != NULL || g != NULL) {
+ archive_strcat(&shar->work, "chown ");
+ if (u != NULL)
+ shar_quote(&shar->work, u, 1);
+ if (g != NULL) {
+ archive_strcat(&shar->work, ":");
+ shar_quote(&shar->work, g, 1);
+ }
+ archive_strcat(&shar->work, " ");
+ shar_quote(&shar->work,
+ archive_entry_pathname(shar->entry), 1);
+ archive_strcat(&shar->work, "\n");
+ }
+
+ if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
+ archive_string_sprintf(&shar->work, "chflags %s ", p);
+ shar_quote(&shar->work,
+ archive_entry_pathname(shar->entry), 1);
+ archive_strcat(&shar->work, "\n");
+ }
+
+ /* TODO: restore ACLs */
+
+ } else {
+ if (shar->has_data) {
+ /* Finish sed-encoded data: ensure last line ends. */
+ if (!shar->end_of_line)
+ archive_strappend_char(&shar->work, '\n');
+ archive_strcat(&shar->work, "SHAR_END\n");
+ }
+ }
+
+ archive_entry_free(shar->entry);
+ shar->entry = NULL;
+
+ if (shar->work.length < 65536)
+ return (ARCHIVE_OK);
+
+ ret = __archive_write_output(a, shar->work.s, shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ archive_string_empty(&shar->work);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_close(struct archive_write *a)
+{
+ struct shar *shar;
+ int ret;
+
+ /*
+ * TODO: Accumulate list of directory names/modes and
+ * fix them all up at end-of-archive.
+ */
+
+ shar = (struct shar *)a->format_data;
+
+ /*
+ * Only write the end-of-archive markers if the archive was
+ * actually started. This avoids problems if someone sets
+ * shar format, then sets another format (which would invoke
+ * shar_finish to free the format-specific data).
+ */
+ if (shar->wrote_header == 0)
+ return (ARCHIVE_OK);
+
+ archive_strcat(&shar->work, "exit\n");
+
+ ret = __archive_write_output(a, shar->work.s, shar->work.length);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+
+ /* Shar output is never padded. */
+ archive_write_set_bytes_in_last_block(&a->archive, 1);
+ /*
+ * TODO: shar should also suppress padding of
+ * uncompressed data within gzip/bzip2 streams.
+ */
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_free(struct archive_write *a)
+{
+ struct shar *shar;
+
+ shar = (struct shar *)a->format_data;
+ if (shar == NULL)
+ return (ARCHIVE_OK);
+
+ archive_entry_free(shar->entry);
+ free(shar->last_dir);
+ archive_string_free(&(shar->work));
+ archive_string_free(&(shar->quoted_name));
+ free(shar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_ustar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_ustar.c
new file mode 100644
index 0000000000..d1a06bc4f7
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_ustar.c
@@ -0,0 +1,758 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ustar.c 191579 2009-04-27 18:35:03Z kientzle $");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct ustar {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+/*
+ * Define structure of POSIX 'ustar' tar header.
+ */
+#define USTAR_name_offset 0
+#define USTAR_name_size 100
+#define USTAR_mode_offset 100
+#define USTAR_mode_size 6
+#define USTAR_mode_max_size 8
+#define USTAR_uid_offset 108
+#define USTAR_uid_size 6
+#define USTAR_uid_max_size 8
+#define USTAR_gid_offset 116
+#define USTAR_gid_size 6
+#define USTAR_gid_max_size 8
+#define USTAR_size_offset 124
+#define USTAR_size_size 11
+#define USTAR_size_max_size 12
+#define USTAR_mtime_offset 136
+#define USTAR_mtime_size 11
+#define USTAR_mtime_max_size 11
+#define USTAR_checksum_offset 148
+#define USTAR_checksum_size 8
+#define USTAR_typeflag_offset 156
+#define USTAR_typeflag_size 1
+#define USTAR_linkname_offset 157
+#define USTAR_linkname_size 100
+#define USTAR_magic_offset 257
+#define USTAR_magic_size 6
+#define USTAR_version_offset 263
+#define USTAR_version_size 2
+#define USTAR_uname_offset 265
+#define USTAR_uname_size 32
+#define USTAR_gname_offset 297
+#define USTAR_gname_size 32
+#define USTAR_rdevmajor_offset 329
+#define USTAR_rdevmajor_size 6
+#define USTAR_rdevmajor_max_size 8
+#define USTAR_rdevminor_offset 337
+#define USTAR_rdevminor_size 6
+#define USTAR_rdevminor_max_size 8
+#define USTAR_prefix_offset 345
+#define USTAR_prefix_size 155
+#define USTAR_padding_offset 500
+#define USTAR_padding_size 12
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+ /* name: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Mode, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* uid, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* gid, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* size, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', ' ',
+ /* mtime, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', ' ',
+ /* Initial checksum value: 8 spaces */
+ ' ',' ',' ',' ',' ',' ',' ',' ',
+ /* Typeflag: 1 byte */
+ '0', /* '0' = regular file */
+ /* Linkname: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Magic: 6 bytes, Version: 2 bytes */
+ 'u','s','t','a','r','\0', '0','0',
+ /* Uname: 32 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ /* Gname: 32 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ /* rdevmajor + space/null padding: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* rdevminor + space/null padding: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* Prefix: 155 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,
+ /* Padding: 12 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0
+};
+
+static ssize_t archive_write_ustar_data(struct archive_write *a, const void *buff,
+ size_t s);
+static int archive_write_ustar_free(struct archive_write *);
+static int archive_write_ustar_close(struct archive_write *);
+static int archive_write_ustar_finish_entry(struct archive_write *);
+static int archive_write_ustar_header(struct archive_write *,
+ struct archive_entry *entry);
+static int archive_write_ustar_options(struct archive_write *,
+ const char *, const char *);
+static int format_256(int64_t, char *, int);
+static int format_number(int64_t, char *, int size, int max, int strict);
+static int format_octal(int64_t, char *, int);
+
+/*
+ * Set output format to 'ustar' format.
+ */
+int
+archive_write_set_format_ustar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct ustar *ustar;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_ustar");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ /* Basic internal sanity test. */
+ if (sizeof(template_header) != 512) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal: template_header wrong size: %zu should be 512",
+ sizeof(template_header));
+ return (ARCHIVE_FATAL);
+ }
+
+ ustar = (struct ustar *)calloc(1, sizeof(*ustar));
+ if (ustar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = ustar;
+ a->format_name = "ustar";
+ a->format_options = archive_write_ustar_options;
+ a->format_write_header = archive_write_ustar_header;
+ a->format_write_data = archive_write_ustar_data;
+ a->format_close = archive_write_ustar_close;
+ a->format_free = archive_write_ustar_free;
+ a->format_finish_entry = archive_write_ustar_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+ a->archive.archive_format_name = "POSIX ustar";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ustar_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct ustar *ustar = (struct ustar *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ ustar->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (ustar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
+{
+ char buff[512];
+ int ret, ret2;
+ struct ustar *ustar;
+ struct archive_entry *entry_main;
+ struct archive_string_conv *sconv;
+
+ ustar = (struct ustar *)a->format_data;
+
+ /* Setup default string conversion. */
+ if (ustar->opt_sconv == NULL) {
+ if (!ustar->init_default_conversion) {
+ ustar->sconv_default =
+ archive_string_default_conversion_for_write(&(a->archive));
+ ustar->init_default_conversion = 1;
+ }
+ sconv = ustar->sconv_default;
+ } else
+ sconv = ustar->opt_sconv;
+
+ /* Sanity check. */
+ if (archive_entry_pathname(entry) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't record entry in tar file without pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Only regular files (not hardlinks) have data. */
+ if (archive_entry_hardlink(entry) != NULL ||
+ archive_entry_symlink(entry) != NULL ||
+ !(archive_entry_filetype(entry) == AE_IFREG))
+ archive_entry_set_size(entry, 0);
+
+ if (AE_IFDIR == archive_entry_filetype(entry)) {
+ const char *p;
+ size_t path_length;
+ /*
+ * Ensure a trailing '/'. Modify the entry so
+ * the client sees the change.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wp;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+ struct archive_wstring ws;
+
+ archive_string_init(&ws);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(&ws,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ archive_wstring_free(&ws);
+ return(ARCHIVE_FATAL);
+ }
+ /* Should we keep '\' ? */
+ if (wp[path_length -1] == L'\\')
+ path_length--;
+ archive_wstrncpy(&ws, wp, path_length);
+ archive_wstrappend_wchar(&ws, L'/');
+ archive_entry_copy_pathname_w(entry, ws.s);
+ archive_wstring_free(&ws);
+ p = NULL;
+ } else
+#endif
+ p = archive_entry_pathname(entry);
+ /*
+ * On Windows, this is a backup operation just in
+ * case getting WCS failed. On POSIX, this is a
+ * normal operation.
+ */
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+ struct archive_string as;
+
+ archive_string_init(&as);
+ path_length = strlen(p);
+ if (archive_string_ensure(&as,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ archive_string_free(&as);
+ return(ARCHIVE_FATAL);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: This might break the pathname
+ * if the current code page is CP932 and
+ * the pathname includes a character '\'
+ * as a part of its multibyte pathname. */
+ if (p[strlen(p) -1] == '\\')
+ path_length--;
+ else
+#endif
+ archive_strncpy(&as, p, path_length);
+ archive_strappend_char(&as, '/');
+ archive_entry_copy_pathname(entry, as.s);
+ archive_string_free(&as);
+ }
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate ustar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+ ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv);
+ if (ret < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (ret);
+ }
+ ret2 = __archive_write_output(a, buff, 512);
+ if (ret2 < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (ret2);
+ }
+ if (ret2 < ret)
+ ret = ret2;
+
+ ustar->entry_bytes_remaining = archive_entry_size(entry);
+ ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
+ archive_entry_free(entry_main);
+ return (ret);
+}
+
+/*
+ * Format a basic 512-byte "ustar" header.
+ *
+ * Returns -1 if format failed (due to field overflow).
+ * Note that this always formats as much of the header as possible.
+ * If "strict" is set to zero, it will extend numeric fields as
+ * necessary (overwriting terminators or using base-256 extensions).
+ *
+ * This is exported so that other 'tar' formats can use it.
+ */
+int
+__archive_write_format_header_ustar(struct archive_write *a, char h[512],
+ struct archive_entry *entry, int tartype, int strict,
+ struct archive_string_conv *sconv)
+{
+ unsigned int checksum;
+ int i, r, ret;
+ size_t copy_length;
+ const char *p, *pp;
+ int mytartype;
+
+ ret = 0;
+ mytartype = -1;
+ /*
+ * The "template header" already includes the "ustar"
+ * signature, various end-of-field markers and other required
+ * elements.
+ */
+ memcpy(h, &template_header, 512);
+
+ /*
+ * Because the block is already null-filled, and strings
+ * are allowed to exactly fill their destination (without null),
+ * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+ */
+ r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ pp, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length <= USTAR_name_size)
+ memcpy(h + USTAR_name_offset, pp, copy_length);
+ else {
+ /* Store in two pieces, splitting at a '/'. */
+ p = strchr(pp + copy_length - USTAR_name_size - 1, '/');
+ /*
+ * Look for the next '/' if we chose the first character
+ * as the separator. (ustar format doesn't permit
+ * an empty prefix.)
+ */
+ if (p == pp)
+ p = strchr(p + 1, '/');
+ /* Fail if the name won't fit. */
+ if (!p) {
+ /* No separator. */
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Pathname too long");
+ ret = ARCHIVE_FAILED;
+ } else if (p[1] == '\0') {
+ /*
+ * The only feasible separator is a final '/';
+ * this would result in a non-empty prefix and
+ * an empty name, which POSIX doesn't
+ * explicitly forbid, but it just feels wrong.
+ */
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Pathname too long");
+ ret = ARCHIVE_FAILED;
+ } else if (p > pp + USTAR_prefix_size) {
+ /* Prefix is too long. */
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Pathname too long");
+ ret = ARCHIVE_FAILED;
+ } else {
+ /* Copy prefix and remainder to appropriate places */
+ memcpy(h + USTAR_prefix_offset, pp, p - pp);
+ memcpy(h + USTAR_name_offset, p + 1,
+ pp + copy_length - p - 1);
+ }
+ }
+
+ r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length > 0)
+ mytartype = '1';
+ else {
+ r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ }
+ if (copy_length > 0) {
+ if (copy_length > USTAR_linkname_size) {
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Link contents too long");
+ ret = ARCHIVE_FAILED;
+ copy_length = USTAR_linkname_size;
+ }
+ memcpy(h + USTAR_linkname_offset, p, copy_length);
+ }
+
+ r = archive_entry_uname_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate uname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length > 0) {
+ if (copy_length > USTAR_uname_size) {
+ if (tartype != 'x') {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "Username too long");
+ ret = ARCHIVE_FAILED;
+ }
+ copy_length = USTAR_uname_size;
+ }
+ memcpy(h + USTAR_uname_offset, p, copy_length);
+ }
+
+ r = archive_entry_gname_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate gname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length > 0) {
+ if (strlen(p) > USTAR_gname_size) {
+ if (tartype != 'x') {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC, "Group name too long");
+ ret = ARCHIVE_FAILED;
+ }
+ copy_length = USTAR_gname_size;
+ }
+ memcpy(h + USTAR_gname_offset, p, copy_length);
+ }
+
+ if (format_number(archive_entry_mode(entry) & 07777,
+ h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric mode too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_uid(entry),
+ h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric user ID too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_gid(entry),
+ h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric group ID too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_size(entry),
+ h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File size out of range");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_mtime(entry),
+ h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File modification time too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (archive_entry_filetype(entry) == AE_IFBLK
+ || archive_entry_filetype(entry) == AE_IFCHR) {
+ if (format_number(archive_entry_rdevmajor(entry),
+ h + USTAR_rdevmajor_offset, USTAR_rdevmajor_size,
+ USTAR_rdevmajor_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Major device number too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_rdevminor(entry),
+ h + USTAR_rdevminor_offset, USTAR_rdevminor_size,
+ USTAR_rdevminor_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Minor device number too large");
+ ret = ARCHIVE_FAILED;
+ }
+ }
+
+ if (tartype >= 0) {
+ h[USTAR_typeflag_offset] = tartype;
+ } else if (mytartype >= 0) {
+ h[USTAR_typeflag_offset] = mytartype;
+ } else {
+ switch (archive_entry_filetype(entry)) {
+ case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
+ case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
+ case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
+ case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
+ case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
+ case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;
+ default: /* AE_IFSOCK and unknown */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "ustar");
+ ret = ARCHIVE_FAILED;
+ }
+ }
+
+ checksum = 0;
+ for (i = 0; i < 512; i++)
+ checksum += 255 & (unsigned int)h[i];
+ h[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
+ /* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
+ format_octal(checksum, h + USTAR_checksum_offset, 6);
+ return (ret);
+}
+
+/*
+ * Format a number into a field, with some intelligence.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize, int strict)
+{
+ int64_t limit;
+
+ limit = ((int64_t)1 << (s*3));
+
+ /* "Strict" only permits octal values with proper termination. */
+ if (strict)
+ return (format_octal(v, p, s));
+
+ /*
+ * In non-strict mode, we allow the number to overwrite one or
+ * more bytes of the field termination. Even old tar
+ * implementations should be able to handle this with no
+ * problem.
+ */
+ if (v >= 0) {
+ while (s <= maxsize) {
+ if (v < limit)
+ return (format_octal(v, p, s));
+ s++;
+ limit <<= 3;
+ }
+ }
+
+ /* Base-256 can handle any number, positive or negative. */
+ return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+ p += s;
+ while (s-- > 0) {
+ *--p = (char)(v & 0xff);
+ v >>= 8;
+ }
+ *p |= 0x80; /* Set the base-256 marker bit. */
+ return (0);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+ int len;
+
+ len = s;
+
+ /* Octal values can't be negative, so use 0. */
+ if (v < 0) {
+ while (len-- > 0)
+ *p++ = '0';
+ return (-1);
+ }
+
+ p += s; /* Start at the end and work backwards. */
+ while (s-- > 0) {
+ *--p = (char)('0' + (v & 7));
+ v >>= 3;
+ }
+
+ if (v == 0)
+ return (0);
+
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '7';
+
+ return (-1);
+}
+
+static int
+archive_write_ustar_close(struct archive_write *a)
+{
+ return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_ustar_free(struct archive_write *a)
+{
+ struct ustar *ustar;
+
+ ustar = (struct ustar *)a->format_data;
+ free(ustar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ustar_finish_entry(struct archive_write *a)
+{
+ struct ustar *ustar;
+ int ret;
+
+ ustar = (struct ustar *)a->format_data;
+ ret = __archive_write_nulls(a,
+ (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding));
+ ustar->entry_bytes_remaining = ustar->entry_padding = 0;
+ return (ret);
+}
+
+static ssize_t
+archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct ustar *ustar;
+ int ret;
+
+ ustar = (struct ustar *)a->format_data;
+ if (s > ustar->entry_bytes_remaining)
+ s = (size_t)ustar->entry_bytes_remaining;
+ ret = __archive_write_output(a, buff, s);
+ ustar->entry_bytes_remaining -= s;
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ return (s);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_v7tar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_v7tar.c
new file mode 100644
index 0000000000..5994071441
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_v7tar.c
@@ -0,0 +1,638 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct v7tar {
+ uint64_t entry_bytes_remaining;
+ uint64_t entry_padding;
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ int init_default_conversion;
+};
+
+/*
+ * Define structure of POSIX 'v7tar' tar header.
+ */
+#define V7TAR_name_offset 0
+#define V7TAR_name_size 100
+#define V7TAR_mode_offset 100
+#define V7TAR_mode_size 6
+#define V7TAR_mode_max_size 8
+#define V7TAR_uid_offset 108
+#define V7TAR_uid_size 6
+#define V7TAR_uid_max_size 8
+#define V7TAR_gid_offset 116
+#define V7TAR_gid_size 6
+#define V7TAR_gid_max_size 8
+#define V7TAR_size_offset 124
+#define V7TAR_size_size 11
+#define V7TAR_size_max_size 12
+#define V7TAR_mtime_offset 136
+#define V7TAR_mtime_size 11
+#define V7TAR_mtime_max_size 12
+#define V7TAR_checksum_offset 148
+#define V7TAR_checksum_size 8
+#define V7TAR_typeflag_offset 156
+#define V7TAR_typeflag_size 1
+#define V7TAR_linkname_offset 157
+#define V7TAR_linkname_size 100
+#define V7TAR_padding_offset 257
+#define V7TAR_padding_size 255
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+ /* name: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Mode, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* uid, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* gid, space-null termination: 8 bytes */
+ '0','0','0','0','0','0', ' ','\0',
+ /* size, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', ' ',
+ /* mtime, space termination: 12 bytes */
+ '0','0','0','0','0','0','0','0','0','0','0', ' ',
+ /* Initial checksum value: 8 spaces */
+ ' ',' ',' ',' ',' ',' ',' ',' ',
+ /* Typeflag: 1 byte */
+ 0,
+ /* Linkname: 100 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,
+ /* Padding: 255 bytes */
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0
+};
+
+static ssize_t archive_write_v7tar_data(struct archive_write *a, const void *buff,
+ size_t s);
+static int archive_write_v7tar_free(struct archive_write *);
+static int archive_write_v7tar_close(struct archive_write *);
+static int archive_write_v7tar_finish_entry(struct archive_write *);
+static int archive_write_v7tar_header(struct archive_write *,
+ struct archive_entry *entry);
+static int archive_write_v7tar_options(struct archive_write *,
+ const char *, const char *);
+static int format_256(int64_t, char *, int);
+static int format_number(int64_t, char *, int size, int max, int strict);
+static int format_octal(int64_t, char *, int);
+static int format_header_v7tar(struct archive_write *, char h[512],
+ struct archive_entry *, int, struct archive_string_conv *);
+
+/*
+ * Set output format to 'v7tar' format.
+ */
+int
+archive_write_set_format_v7tar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct v7tar *v7tar;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");
+
+ /* If someone else was already registered, unregister them. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ /* Basic internal sanity test. */
+ if (sizeof(template_header) != 512) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Internal: template_header wrong size: %zu should be 512",
+ sizeof(template_header));
+ return (ARCHIVE_FATAL);
+ }
+
+ v7tar = (struct v7tar *)calloc(1, sizeof(*v7tar));
+ if (v7tar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate v7tar data");
+ return (ARCHIVE_FATAL);
+ }
+ a->format_data = v7tar;
+ a->format_name = "tar (non-POSIX)";
+ a->format_options = archive_write_v7tar_options;
+ a->format_write_header = archive_write_v7tar_header;
+ a->format_write_data = archive_write_v7tar_data;
+ a->format_close = archive_write_v7tar_close;
+ a->format_free = archive_write_v7tar_free;
+ a->format_finish_entry = archive_write_v7tar_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+ a->archive.archive_format_name = "tar (non-POSIX)";
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_v7tar_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct v7tar *v7tar = (struct v7tar *)a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "hdrcharset") == 0) {
+ if (val == NULL || val[0] == 0)
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ else {
+ v7tar->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (v7tar->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
+{
+ char buff[512];
+ int ret, ret2;
+ struct v7tar *v7tar;
+ struct archive_entry *entry_main;
+ struct archive_string_conv *sconv;
+
+ v7tar = (struct v7tar *)a->format_data;
+
+ /* Setup default string conversion. */
+ if (v7tar->opt_sconv == NULL) {
+ if (!v7tar->init_default_conversion) {
+ v7tar->sconv_default =
+ archive_string_default_conversion_for_write(
+ &(a->archive));
+ v7tar->init_default_conversion = 1;
+ }
+ sconv = v7tar->sconv_default;
+ } else
+ sconv = v7tar->opt_sconv;
+
+ /* Sanity check. */
+ if (archive_entry_pathname(entry) == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't record entry in tar file without pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Only regular files (not hardlinks) have data. */
+ if (archive_entry_hardlink(entry) != NULL ||
+ archive_entry_symlink(entry) != NULL ||
+ !(archive_entry_filetype(entry) == AE_IFREG))
+ archive_entry_set_size(entry, 0);
+
+ if (AE_IFDIR == archive_entry_filetype(entry)) {
+ const char *p;
+ size_t path_length;
+ /*
+ * Ensure a trailing '/'. Modify the entry so
+ * the client sees the change.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ const wchar_t *wp;
+
+ wp = archive_entry_pathname_w(entry);
+ if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+ struct archive_wstring ws;
+
+ archive_string_init(&ws);
+ path_length = wcslen(wp);
+ if (archive_wstring_ensure(&ws,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate v7tar data");
+ archive_wstring_free(&ws);
+ return(ARCHIVE_FATAL);
+ }
+ /* Should we keep '\' ? */
+ if (wp[path_length -1] == L'\\')
+ path_length--;
+ archive_wstrncpy(&ws, wp, path_length);
+ archive_wstrappend_wchar(&ws, L'/');
+ archive_entry_copy_pathname_w(entry, ws.s);
+ archive_wstring_free(&ws);
+ p = NULL;
+ } else
+#endif
+ p = archive_entry_pathname(entry);
+ /*
+ * On Windows, this is a backup operation just in
+ * case getting WCS failed. On POSIX, this is a
+ * normal operation.
+ */
+ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+ struct archive_string as;
+
+ archive_string_init(&as);
+ path_length = strlen(p);
+ if (archive_string_ensure(&as,
+ path_length + 2) == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate v7tar data");
+ archive_string_free(&as);
+ return(ARCHIVE_FATAL);
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* NOTE: This might break the pathname
+ * if the current code page is CP932 and
+ * the pathname includes a character '\'
+ * as a part of its multibyte pathname. */
+ if (p[strlen(p) -1] == '\\')
+ path_length--;
+ else
+#endif
+ archive_strncpy(&as, p, path_length);
+ archive_strappend_char(&as, '/');
+ archive_entry_copy_pathname(entry, as.s);
+ archive_string_free(&as);
+ }
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ entry_main = __la_win_entry_in_posix_pathseparator(entry);
+ if (entry_main == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate v7tar data");
+ return(ARCHIVE_FATAL);
+ }
+ if (entry != entry_main)
+ entry = entry_main;
+ else
+ entry_main = NULL;
+#else
+ entry_main = NULL;
+#endif
+ ret = format_header_v7tar(a, buff, entry, 1, sconv);
+ if (ret < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (ret);
+ }
+ ret2 = __archive_write_output(a, buff, 512);
+ if (ret2 < ARCHIVE_WARN) {
+ archive_entry_free(entry_main);
+ return (ret2);
+ }
+ if (ret2 < ret)
+ ret = ret2;
+
+ v7tar->entry_bytes_remaining = archive_entry_size(entry);
+ v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining);
+ archive_entry_free(entry_main);
+ return (ret);
+}
+
+/*
+ * Format a basic 512-byte "v7tar" header.
+ *
+ * Returns -1 if format failed (due to field overflow).
+ * Note that this always formats as much of the header as possible.
+ * If "strict" is set to zero, it will extend numeric fields as
+ * necessary (overwriting terminators or using base-256 extensions).
+ *
+ */
+static int
+format_header_v7tar(struct archive_write *a, char h[512],
+ struct archive_entry *entry, int strict,
+ struct archive_string_conv *sconv)
+{
+ unsigned int checksum;
+ int i, r, ret;
+ size_t copy_length;
+ const char *p, *pp;
+ int mytartype;
+
+ ret = 0;
+ mytartype = -1;
+ /*
+ * The "template header" already includes the "v7tar"
+ * signature, various end-of-field markers and other required
+ * elements.
+ */
+ memcpy(h, &template_header, 512);
+
+ /*
+ * Because the block is already null-filled, and strings
+ * are allowed to exactly fill their destination (without null),
+ * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+ */
+ r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to %s",
+ pp, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (strict && copy_length < V7TAR_name_size)
+ memcpy(h + V7TAR_name_offset, pp, copy_length);
+ else if (!strict && copy_length <= V7TAR_name_size)
+ memcpy(h + V7TAR_name_offset, pp, copy_length);
+ else {
+ /* Prefix is too long. */
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Pathname too long");
+ ret = ARCHIVE_FAILED;
+ }
+
+ r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ if (copy_length > 0)
+ mytartype = '1';
+ else {
+ r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate linkname '%s' to %s",
+ p, archive_string_conversion_charset_name(sconv));
+ ret = ARCHIVE_WARN;
+ }
+ }
+ if (copy_length > 0) {
+ if (copy_length >= V7TAR_linkname_size) {
+ archive_set_error(&a->archive, ENAMETOOLONG,
+ "Link contents too long");
+ ret = ARCHIVE_FAILED;
+ copy_length = V7TAR_linkname_size;
+ }
+ memcpy(h + V7TAR_linkname_offset, p, copy_length);
+ }
+
+ if (format_number(archive_entry_mode(entry) & 07777,
+ h + V7TAR_mode_offset, V7TAR_mode_size,
+ V7TAR_mode_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric mode too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_uid(entry),
+ h + V7TAR_uid_offset, V7TAR_uid_size, V7TAR_uid_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric user ID too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_gid(entry),
+ h + V7TAR_gid_offset, V7TAR_gid_size, V7TAR_gid_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "Numeric group ID too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_size(entry),
+ h + V7TAR_size_offset, V7TAR_size_size,
+ V7TAR_size_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File size out of range");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (format_number(archive_entry_mtime(entry),
+ h + V7TAR_mtime_offset, V7TAR_mtime_size,
+ V7TAR_mtime_max_size, strict)) {
+ archive_set_error(&a->archive, ERANGE,
+ "File modification time too large");
+ ret = ARCHIVE_FAILED;
+ }
+
+ if (mytartype >= 0) {
+ h[V7TAR_typeflag_offset] = mytartype;
+ } else {
+ switch (archive_entry_filetype(entry)) {
+ case AE_IFREG: case AE_IFDIR:
+ break;
+ case AE_IFLNK:
+ h[V7TAR_typeflag_offset] = '2';
+ break;
+ default:
+ /* AE_IFBLK, AE_IFCHR, AE_IFIFO, AE_IFSOCK
+ * and unknown */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "v7tar");
+ ret = ARCHIVE_FAILED;
+ }
+ }
+
+ checksum = 0;
+ for (i = 0; i < 512; i++)
+ checksum += 255 & (unsigned int)h[i];
+ format_octal(checksum, h + V7TAR_checksum_offset, 6);
+ /* Can't be pre-set in the template. */
+ h[V7TAR_checksum_offset + 6] = '\0';
+ return (ret);
+}
+
+/*
+ * Format a number into a field, with some intelligence.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize, int strict)
+{
+ int64_t limit;
+
+ limit = ((int64_t)1 << (s*3));
+
+ /* "Strict" only permits octal values with proper termination. */
+ if (strict)
+ return (format_octal(v, p, s));
+
+ /*
+ * In non-strict mode, we allow the number to overwrite one or
+ * more bytes of the field termination. Even old tar
+ * implementations should be able to handle this with no
+ * problem.
+ */
+ if (v >= 0) {
+ while (s <= maxsize) {
+ if (v < limit)
+ return (format_octal(v, p, s));
+ s++;
+ limit <<= 3;
+ }
+ }
+
+ /* Base-256 can handle any number, positive or negative. */
+ return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+ p += s;
+ while (s-- > 0) {
+ *--p = (char)(v & 0xff);
+ v >>= 8;
+ }
+ *p |= 0x80; /* Set the base-256 marker bit. */
+ return (0);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+ int len;
+
+ len = s;
+
+ /* Octal values can't be negative, so use 0. */
+ if (v < 0) {
+ while (len-- > 0)
+ *p++ = '0';
+ return (-1);
+ }
+
+ p += s; /* Start at the end and work backwards. */
+ while (s-- > 0) {
+ *--p = (char)('0' + (v & 7));
+ v >>= 3;
+ }
+
+ if (v == 0)
+ return (0);
+
+ /* If it overflowed, fill field with max value. */
+ while (len-- > 0)
+ *p++ = '7';
+
+ return (-1);
+}
+
+static int
+archive_write_v7tar_close(struct archive_write *a)
+{
+ return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_v7tar_free(struct archive_write *a)
+{
+ struct v7tar *v7tar;
+
+ v7tar = (struct v7tar *)a->format_data;
+ free(v7tar);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_v7tar_finish_entry(struct archive_write *a)
+{
+ struct v7tar *v7tar;
+ int ret;
+
+ v7tar = (struct v7tar *)a->format_data;
+ ret = __archive_write_nulls(a,
+ (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));
+ v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;
+ return (ret);
+}
+
+static ssize_t
+archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct v7tar *v7tar;
+ int ret;
+
+ v7tar = (struct v7tar *)a->format_data;
+ if (s > v7tar->entry_bytes_remaining)
+ s = (size_t)v7tar->entry_bytes_remaining;
+ ret = __archive_write_output(a, buff, s);
+ v7tar->entry_bytes_remaining -= s;
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ return (s);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_warc.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_warc.c
new file mode 100644
index 0000000000..0ef003e2fc
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_warc.c
@@ -0,0 +1,444 @@
+/*-
+ * Copyright (c) 2014 Sebastian Freundt
+ * Author: Sebastian Freundt <devel@fresse.org>
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct warc_s {
+ unsigned int omit_warcinfo:1;
+
+ time_t now;
+ mode_t typ;
+ unsigned int rng;
+ /* populated size */
+ uint64_t populz;
+};
+
+static const char warcinfo[] =
+ "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n"
+ "format: WARC file version 1.0\r\n";
+
+typedef enum {
+ WT_NONE,
+ /* warcinfo */
+ WT_INFO,
+ /* metadata */
+ WT_META,
+ /* resource */
+ WT_RSRC,
+ /* request, unsupported */
+ WT_REQ,
+ /* response, unsupported */
+ WT_RSP,
+ /* revisit, unsupported */
+ WT_RVIS,
+ /* conversion, unsupported */
+ WT_CONV,
+ /* continuation, unsupported at the moment */
+ WT_CONT,
+ /* invalid type */
+ LAST_WT
+} warc_type_t;
+
+typedef struct {
+ warc_type_t type;
+ const char *tgturi;
+ const char *recid;
+ time_t rtime;
+ time_t mtime;
+ const char *cnttyp;
+ uint64_t cntlen;
+} warc_essential_hdr_t;
+
+typedef struct {
+ unsigned int u[4U];
+} warc_uuid_t;
+
+static int _warc_options(struct archive_write*, const char *key, const char *v);
+static int _warc_header(struct archive_write *a, struct archive_entry *entry);
+static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz);
+static int _warc_finish_entry(struct archive_write *a);
+static int _warc_close(struct archive_write *a);
+static int _warc_free(struct archive_write *a);
+
+/* private routines */
+static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t);
+static int _gen_uuid(warc_uuid_t *tgt);
+
+
+/*
+ * Set output format to ISO 28500 (aka WARC) format.
+ */
+int
+archive_write_set_format_warc(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct warc_s *w;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_warc");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL) {
+ (a->format_free)(a);
+ }
+
+ w = malloc(sizeof(*w));
+ if (w == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate warc data");
+ return (ARCHIVE_FATAL);
+ }
+ /* by default we're emitting a file wide header */
+ w->omit_warcinfo = 0U;
+ /* obtain current time for date fields */
+ w->now = time(NULL);
+ /* reset file type info */
+ w->typ = 0;
+ /* also initialise our rng */
+ w->rng = (unsigned int)w->now;
+
+ a->format_data = w;
+ a->format_name = "WARC/1.0";
+ a->format_options = _warc_options;
+ a->format_write_header = _warc_header;
+ a->format_write_data = _warc_data;
+ a->format_close = _warc_close;
+ a->format_free = _warc_free;
+ a->format_finish_entry = _warc_finish_entry;
+ a->archive.archive_format = ARCHIVE_FORMAT_WARC;
+ a->archive.archive_format_name = "WARC/1.0";
+ return (ARCHIVE_OK);
+}
+
+
+/* archive methods */
+static int
+_warc_options(struct archive_write *a, const char *key, const char *val)
+{
+ struct warc_s *w = a->format_data;
+
+ if (strcmp(key, "omit-warcinfo") == 0) {
+ if (val == NULL || strcmp(val, "true") == 0) {
+ /* great */
+ w->omit_warcinfo = 1U;
+ return (ARCHIVE_OK);
+ }
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+_warc_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct warc_s *w = a->format_data;
+ struct archive_string hdr;
+#define MAX_HDR_SIZE 512
+
+ /* check whether warcinfo record needs outputting */
+ if (!w->omit_warcinfo) {
+ ssize_t r;
+ warc_essential_hdr_t wi = {
+ WT_INFO,
+ /*uri*/NULL,
+ /*urn*/NULL,
+ /*rtm*/0,
+ /*mtm*/0,
+ /*cty*/"application/warc-fields",
+ /*len*/sizeof(warcinfo) - 1U,
+ };
+ wi.rtime = w->now;
+ wi.mtime = w->now;
+
+ archive_string_init(&hdr);
+ r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi);
+ if (r >= 0) {
+ /* jackpot! */
+ /* now also use HDR buffer for the actual warcinfo */
+ archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1);
+
+ /* append end-of-record indicator */
+ archive_strncat(&hdr, "\r\n\r\n", 4);
+
+ /* write to output stream */
+ __archive_write_output(a, hdr.s, archive_strlen(&hdr));
+ }
+ /* indicate we're done with file header writing */
+ w->omit_warcinfo = 1U;
+ archive_string_free(&hdr);
+ }
+
+ if (archive_entry_pathname(entry) == NULL) {
+ archive_set_error(&a->archive, EINVAL,
+ "Invalid filename");
+ return (ARCHIVE_WARN);
+ }
+
+ w->typ = archive_entry_filetype(entry);
+ w->populz = 0U;
+ if (w->typ == AE_IFREG) {
+ warc_essential_hdr_t rh = {
+ WT_RSRC,
+ /*uri*/NULL,
+ /*urn*/NULL,
+ /*rtm*/0,
+ /*mtm*/0,
+ /*cty*/NULL,
+ /*len*/0,
+ };
+ ssize_t r;
+ rh.tgturi = archive_entry_pathname(entry);
+ rh.rtime = w->now;
+ rh.mtime = archive_entry_mtime(entry);
+ rh.cntlen = (size_t)archive_entry_size(entry);
+
+ archive_string_init(&hdr);
+ r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh);
+ if (r < 0) {
+ /* don't bother */
+ archive_set_error(
+ &a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "cannot archive file");
+ return (ARCHIVE_WARN);
+ }
+ /* otherwise append to output stream */
+ __archive_write_output(a, hdr.s, r);
+ /* and let subsequent calls to _data() know about the size */
+ w->populz = rh.cntlen;
+ archive_string_free(&hdr);
+ return (ARCHIVE_OK);
+ }
+ /* just resort to erroring as per Tim's advice */
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "WARC");
+ return (ARCHIVE_FAILED);
+}
+
+static ssize_t
+_warc_data(struct archive_write *a, const void *buf, size_t len)
+{
+ struct warc_s *w = a->format_data;
+
+ if (w->typ == AE_IFREG) {
+ int rc;
+
+ /* never write more bytes than announced */
+ if (len > w->populz) {
+ len = (size_t)w->populz;
+ }
+
+ /* now then, out we put the whole shebang */
+ rc = __archive_write_output(a, buf, len);
+ if (rc != ARCHIVE_OK) {
+ return rc;
+ }
+ }
+ return len;
+}
+
+static int
+_warc_finish_entry(struct archive_write *a)
+{
+ static const char _eor[] = "\r\n\r\n";
+ struct warc_s *w = a->format_data;
+
+ if (w->typ == AE_IFREG) {
+ int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U);
+
+ if (rc != ARCHIVE_OK) {
+ return rc;
+ }
+ }
+ /* reset type info */
+ w->typ = 0;
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_close(struct archive_write *a)
+{
+ (void)a; /* UNUSED */
+ return (ARCHIVE_OK);
+}
+
+static int
+_warc_free(struct archive_write *a)
+{
+ struct warc_s *w = a->format_data;
+
+ free(w);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+
+/* private routines */
+static void
+xstrftime(struct archive_string *as, const char *fmt, time_t t)
+{
+/** like strftime(3) but for time_t objects */
+ struct tm *rt;
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
+ struct tm timeHere;
+#endif
+ char strtime[100];
+ size_t len;
+
+#if defined(HAVE_GMTIME_S)
+ rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere;
+#elif defined(HAVE_GMTIME_R)
+ rt = gmtime_r(&t, &timeHere);
+#else
+ rt = gmtime(&t);
+#endif
+ if (!rt)
+ return;
+ /* leave the hard yacker to our role model strftime() */
+ len = strftime(strtime, sizeof(strtime)-1, fmt, rt);
+ archive_strncat(as, strtime, len);
+}
+
+static ssize_t
+_popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr)
+{
+ static const char _ver[] = "WARC/1.0\r\n";
+ static const char * const _typ[LAST_WT] = {
+ NULL, "warcinfo", "metadata", "resource", NULL
+ };
+ char std_uuid[48U];
+
+ if (hdr.type == WT_NONE || hdr.type > WT_RSRC) {
+ /* brilliant, how exactly did we get here? */
+ return -1;
+ }
+
+ archive_strcpy(tgt, _ver);
+
+ archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]);
+
+ if (hdr.tgturi != NULL) {
+ /* check if there's a xyz:// */
+ static const char _uri[] = "";
+ static const char _fil[] = "file://";
+ const char *u;
+ char *chk = strchr(hdr.tgturi, ':');
+
+ if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') {
+ /* yep, it's definitely a URI */
+ u = _uri;
+ } else {
+ /* hm, best to prepend file:// then */
+ u = _fil;
+ }
+ archive_string_sprintf(tgt,
+ "WARC-Target-URI: %s%s\r\n", u, hdr.tgturi);
+ }
+
+ /* record time is usually when the http is sent off,
+ * just treat the archive writing as such for a moment */
+ xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime);
+
+ /* while we're at it, record the mtime */
+ xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime);
+
+ if (hdr.recid == NULL) {
+ /* generate one, grrrr */
+ warc_uuid_t u;
+
+ _gen_uuid(&u);
+ /* Unfortunately, archive_string_sprintf does not
+ * handle the minimum number following '%'.
+ * So we have to use snprintf function here instead
+ * of archive_string_snprintf function. */
+#if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900)
+#define snprintf _snprintf
+#endif
+ snprintf(
+ std_uuid, sizeof(std_uuid),
+ "<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",
+ u.u[0U],
+ u.u[1U] >> 16U, u.u[1U] & 0xffffU,
+ u.u[2U] >> 16U, u.u[2U] & 0xffffU,
+ u.u[3U]);
+ hdr.recid = std_uuid;
+ }
+
+ /* record-id is mandatory, fingers crossed we won't fail */
+ archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid);
+
+ if (hdr.cnttyp != NULL) {
+ archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp);
+ }
+
+ /* next one is mandatory */
+ archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen);
+ /**/
+ archive_strncat(tgt, "\r\n", 2);
+
+ return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt);
+}
+
+static int
+_gen_uuid(warc_uuid_t *tgt)
+{
+ archive_random(tgt->u, sizeof(tgt->u));
+ /* obey uuid version 4 rules */
+ tgt->u[1U] &= 0xffff0fffU;
+ tgt->u[1U] |= 0x4000U;
+ tgt->u[2U] &= 0x3fffffffU;
+ tgt->u[2U] |= 0x80000000U;
+ return 0;
+}
+
+/* archive_write_set_format_warc.c ends here */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_xar.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_xar.c
new file mode 100644
index 0000000000..c3acf86b57
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_xar.c
@@ -0,0 +1,3255 @@
+/*-
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdlib.h>
+#if HAVE_LIBXML_XMLWRITER_H
+#error #include <libxml/xmlwriter.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#error #include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+/*
+ * Differences to xar utility.
+ * - Subdocument is not supported yet.
+ * - ACL is not supported yet.
+ * - When writing an XML element <link type="<file-type>">, <file-type>
+ * which is a file type a symbolic link is referencing is always marked
+ * as "broken". Xar utility uses stat(2) to get the file type, but, in
+ * libarchive format writer, we should not use it; if it is needed, we
+ * should get about it at archive_read_disk.c.
+ * - It is possible to appear both <flags> and <ext2> elements.
+ * Xar utility generates <flags> on BSD platform and <ext2> on Linux
+ * platform.
+ *
+ */
+
+#if !(defined(HAVE_LIBXML_XMLWRITER_H) && defined(LIBXML_VERSION) &&\
+ LIBXML_VERSION >= 20703) ||\
+ !defined(HAVE_ZLIB_H) || \
+ !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ * o libxml2
+ * o openssl or MD5/SHA1 hash function
+ * o zlib
+ * o bzlib2 (option)
+ * o liblzma (option)
+ */
+int
+archive_write_set_format_xar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Xar not supported on this platform");
+ return (ARCHIVE_WARN);
+}
+
+#else /* Support xar format */
+
+/*#define DEBUG_PRINT_TOC 1 */
+
+#define BAD_CAST_CONST (const xmlChar *)
+
+#define HEADER_MAGIC 0x78617221
+#define HEADER_SIZE 28
+#define HEADER_VERSION 1
+
+enum sumalg {
+ CKSUM_NONE = 0,
+ CKSUM_SHA1 = 1,
+ CKSUM_MD5 = 2
+};
+
+#define MD5_SIZE 16
+#define SHA1_SIZE 20
+#define MAX_SUM_SIZE 20
+#define MD5_NAME "md5"
+#define SHA1_NAME "sha1"
+
+enum enctype {
+ NONE,
+ GZIP,
+ BZIP2,
+ LZMA,
+ XZ,
+};
+
+struct chksumwork {
+ enum sumalg alg;
+#ifdef ARCHIVE_HAS_MD5
+ archive_md5_ctx md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+ archive_sha1_ctx sha1ctx;
+#endif
+};
+
+enum la_zaction {
+ ARCHIVE_Z_FINISH,
+ ARCHIVE_Z_RUN
+};
+
+/*
+ * Universal zstream.
+ */
+struct la_zstream {
+ const unsigned char *next_in;
+ size_t avail_in;
+ uint64_t total_in;
+
+ unsigned char *next_out;
+ size_t avail_out;
+ uint64_t total_out;
+
+ int valid;
+ void *real_stream;
+ int (*code) (struct archive *a,
+ struct la_zstream *lastrm,
+ enum la_zaction action);
+ int (*end)(struct archive *a,
+ struct la_zstream *lastrm);
+};
+
+struct chksumval {
+ enum sumalg alg;
+ size_t len;
+ unsigned char val[MAX_SUM_SIZE];
+};
+
+struct heap_data {
+ int id;
+ struct heap_data *next;
+ uint64_t temp_offset;
+ uint64_t length; /* archived size. */
+ uint64_t size; /* extracted size. */
+ enum enctype compression;
+ struct chksumval a_sum; /* archived checksum. */
+ struct chksumval e_sum; /* extracted checksum. */
+};
+
+struct file {
+ struct archive_rb_node rbnode;
+
+ int id;
+ struct archive_entry *entry;
+
+ struct archive_rb_tree rbtree;
+ struct file *next;
+ struct file *chnext;
+ struct file *hlnext;
+ /* For hardlinked files.
+ * Use only when archive_entry_nlink() > 1 */
+ struct file *hardlink_target;
+ struct file *parent; /* parent directory entry */
+ /*
+ * To manage sub directory files.
+ * We use 'chnext' (a member of struct file) to chain.
+ */
+ struct {
+ struct file *first;
+ struct file **last;
+ } children;
+
+ /* For making a directory tree. */
+ struct archive_string parentdir;
+ struct archive_string basename;
+ struct archive_string symlink;
+
+ int ea_idx;
+ struct {
+ struct heap_data *first;
+ struct heap_data **last;
+ } xattr;
+ struct heap_data data;
+ struct archive_string script;
+
+ unsigned int virtual:1;
+ unsigned int dir:1;
+};
+
+struct hardlink {
+ struct archive_rb_node rbnode;
+ int nlink;
+ struct {
+ struct file *first;
+ struct file **last;
+ } file_list;
+};
+
+struct xar {
+ int temp_fd;
+ uint64_t temp_offset;
+
+ int file_idx;
+ struct file *root;
+ struct file *cur_dirent;
+ struct archive_string cur_dirstr;
+ struct file *cur_file;
+ uint64_t bytes_remaining;
+ struct archive_string tstr;
+ struct archive_string vstr;
+
+ enum sumalg opt_toc_sumalg;
+ enum sumalg opt_sumalg;
+ enum enctype opt_compression;
+ int opt_compression_level;
+ uint32_t opt_threads;
+
+ struct chksumwork a_sumwrk; /* archived checksum. */
+ struct chksumwork e_sumwrk; /* extracted checksum. */
+ struct la_zstream stream;
+ struct archive_string_conv *sconv;
+ /*
+ * Compressed data buffer.
+ */
+ unsigned char wbuff[1024 * 64];
+ size_t wbuff_remaining;
+
+ struct heap_data toc;
+ /*
+ * The list of all file entries is used to manage struct file
+ * objects.
+ * We use 'next' (a member of struct file) to chain.
+ */
+ struct {
+ struct file *first;
+ struct file **last;
+ } file_list;
+ /*
+ * The list of hard-linked file entries.
+ * We use 'hlnext' (a member of struct file) to chain.
+ */
+ struct archive_rb_tree hardlink_rbtree;
+};
+
+static int xar_options(struct archive_write *,
+ const char *, const char *);
+static int xar_write_header(struct archive_write *,
+ struct archive_entry *);
+static ssize_t xar_write_data(struct archive_write *,
+ const void *, size_t);
+static int xar_finish_entry(struct archive_write *);
+static int xar_close(struct archive_write *);
+static int xar_free(struct archive_write *);
+
+static struct file *file_new(struct archive_write *a, struct archive_entry *);
+static void file_free(struct file *);
+static struct file *file_create_virtual_dir(struct archive_write *a, struct xar *,
+ const char *);
+static int file_add_child_tail(struct file *, struct file *);
+static struct file *file_find_child(struct file *, const char *);
+static int file_gen_utility_names(struct archive_write *,
+ struct file *);
+static int get_path_component(char *, int, const char *);
+static int file_tree(struct archive_write *, struct file **);
+static void file_register(struct xar *, struct file *);
+static void file_init_register(struct xar *);
+static void file_free_register(struct xar *);
+static int file_register_hardlink(struct archive_write *,
+ struct file *);
+static void file_connect_hardlink_files(struct xar *);
+static void file_init_hardlinks(struct xar *);
+static void file_free_hardlinks(struct xar *);
+
+static void checksum_init(struct chksumwork *, enum sumalg);
+static void checksum_update(struct chksumwork *, const void *, size_t);
+static void checksum_final(struct chksumwork *, struct chksumval *);
+static int compression_init_encoder_gzip(struct archive *,
+ struct la_zstream *, int, int);
+static int compression_code_gzip(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_gzip(struct archive *, struct la_zstream *);
+static int compression_init_encoder_bzip2(struct archive *,
+ struct la_zstream *, int);
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int compression_code_bzip2(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_bzip2(struct archive *, struct la_zstream *);
+#endif
+static int compression_init_encoder_lzma(struct archive *,
+ struct la_zstream *, int);
+static int compression_init_encoder_xz(struct archive *,
+ struct la_zstream *, int, int);
+#if defined(HAVE_LZMA_H)
+static int compression_code_lzma(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end_lzma(struct archive *, struct la_zstream *);
+#endif
+static int xar_compression_init_encoder(struct archive_write *);
+static int compression_code(struct archive *,
+ struct la_zstream *, enum la_zaction);
+static int compression_end(struct archive *,
+ struct la_zstream *);
+static int save_xattrs(struct archive_write *, struct file *);
+static int getalgsize(enum sumalg);
+static const char *getalgname(enum sumalg);
+
+int
+archive_write_set_format_xar(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct xar *xar;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_xar");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ xar = calloc(1, sizeof(*xar));
+ if (xar == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate xar data");
+ return (ARCHIVE_FATAL);
+ }
+ xar->temp_fd = -1;
+ file_init_register(xar);
+ file_init_hardlinks(xar);
+ archive_string_init(&(xar->tstr));
+ archive_string_init(&(xar->vstr));
+
+ /*
+ * Create the root directory.
+ */
+ xar->root = file_create_virtual_dir(a, xar, "");
+ if (xar->root == NULL) {
+ free(xar);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate xar data");
+ return (ARCHIVE_FATAL);
+ }
+ xar->root->parent = xar->root;
+ file_register(xar, xar->root);
+ xar->cur_dirent = xar->root;
+ archive_string_init(&(xar->cur_dirstr));
+ archive_string_ensure(&(xar->cur_dirstr), 1);
+ xar->cur_dirstr.s[0] = 0;
+
+ /*
+ * Initialize option.
+ */
+ /* Set default checksum type. */
+ xar->opt_toc_sumalg = CKSUM_SHA1;
+ xar->opt_sumalg = CKSUM_SHA1;
+ /* Set default compression type, level, and number of threads. */
+ xar->opt_compression = GZIP;
+ xar->opt_compression_level = 6;
+ xar->opt_threads = 1;
+
+ a->format_data = xar;
+
+ a->format_name = "xar";
+ a->format_options = xar_options;
+ a->format_write_header = xar_write_header;
+ a->format_write_data = xar_write_data;
+ a->format_finish_entry = xar_finish_entry;
+ a->format_close = xar_close;
+ a->format_free = xar_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_XAR;
+ a->archive.archive_format_name = "xar";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+xar_options(struct archive_write *a, const char *key, const char *value)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)a->format_data;
+
+ if (strcmp(key, "checksum") == 0) {
+ if (value == NULL)
+ xar->opt_sumalg = CKSUM_NONE;
+ else if (strcmp(value, "none") == 0)
+ xar->opt_sumalg = CKSUM_NONE;
+ else if (strcmp(value, "sha1") == 0)
+ xar->opt_sumalg = CKSUM_SHA1;
+ else if (strcmp(value, "md5") == 0)
+ xar->opt_sumalg = CKSUM_MD5;
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unknown checksum name: `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "compression") == 0) {
+ const char *name = NULL;
+
+ if (value == NULL)
+ xar->opt_compression = NONE;
+ else if (strcmp(value, "none") == 0)
+ xar->opt_compression = NONE;
+ else if (strcmp(value, "gzip") == 0)
+ xar->opt_compression = GZIP;
+ else if (strcmp(value, "bzip2") == 0)
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+ xar->opt_compression = BZIP2;
+#else
+ name = "bzip2";
+#endif
+ else if (strcmp(value, "lzma") == 0)
+#if HAVE_LZMA_H
+ xar->opt_compression = LZMA;
+#else
+ name = "lzma";
+#endif
+ else if (strcmp(value, "xz") == 0)
+#if HAVE_LZMA_H
+ xar->opt_compression = XZ;
+#else
+ name = "xz";
+#endif
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unknown compression name: `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ if (name != NULL) {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "`%s' compression not supported "
+ "on this platform",
+ name);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "compression-level") == 0) {
+ if (value == NULL ||
+ !(value[0] >= '0' && value[0] <= '9') ||
+ value[1] != '\0') {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Illegal value `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ xar->opt_compression_level = value[0] - '0';
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "toc-checksum") == 0) {
+ if (value == NULL)
+ xar->opt_toc_sumalg = CKSUM_NONE;
+ else if (strcmp(value, "none") == 0)
+ xar->opt_toc_sumalg = CKSUM_NONE;
+ else if (strcmp(value, "sha1") == 0)
+ xar->opt_toc_sumalg = CKSUM_SHA1;
+ else if (strcmp(value, "md5") == 0)
+ xar->opt_toc_sumalg = CKSUM_MD5;
+ else {
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Unknown checksum name: `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ return (ARCHIVE_OK);
+ }
+ if (strcmp(key, "threads") == 0) {
+ char *endptr;
+
+ if (value == NULL)
+ return (ARCHIVE_FAILED);
+ errno = 0;
+ xar->opt_threads = (int)strtoul(value, &endptr, 10);
+ if (errno != 0 || *endptr != '\0') {
+ xar->opt_threads = 1;
+ archive_set_error(&(a->archive),
+ ARCHIVE_ERRNO_MISC,
+ "Illegal value `%s'",
+ value);
+ return (ARCHIVE_FAILED);
+ }
+ if (xar->opt_threads == 0) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ xar->opt_threads = lzma_cputhreads();
+#else
+ xar->opt_threads = 1;
+#endif
+ }
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+static int
+xar_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+ struct xar *xar;
+ struct file *file;
+ struct archive_entry *file_entry;
+ int r, r2;
+
+ xar = (struct xar *)a->format_data;
+ xar->cur_file = NULL;
+ xar->bytes_remaining = 0;
+
+ if (xar->sconv == NULL) {
+ xar->sconv = archive_string_conversion_to_charset(
+ &a->archive, "UTF-8", 1);
+ if (xar->sconv == NULL)
+ return (ARCHIVE_FATAL);
+ }
+
+ file = file_new(a, entry);
+ if (file == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data");
+ return (ARCHIVE_FATAL);
+ }
+ r2 = file_gen_utility_names(a, file);
+ if (r2 < ARCHIVE_WARN)
+ return (r2);
+
+ /*
+ * Ignore a path which looks like the top of directory name
+ * since we have already made the root directory of an Xar archive.
+ */
+ if (archive_strlen(&(file->parentdir)) == 0 &&
+ archive_strlen(&(file->basename)) == 0) {
+ file_free(file);
+ return (r2);
+ }
+
+ /* Add entry into tree */
+ file_entry = file->entry;
+ r = file_tree(a, &file);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /* There is the same file in tree and
+ * the current file is older than the file in tree.
+ * So we don't need the current file data anymore. */
+ if (file->entry != file_entry)
+ return (r2);
+ if (file->id == 0)
+ file_register(xar, file);
+
+ /* A virtual file, which is a directory, does not have
+ * any contents and we won't store it into a archive
+ * file other than its name. */
+ if (file->virtual)
+ return (r2);
+
+ /*
+ * Prepare to save the contents of the file.
+ */
+ if (xar->temp_fd == -1) {
+ int algsize;
+ xar->temp_offset = 0;
+ xar->temp_fd = __archive_mktemp(NULL);
+ if (xar->temp_fd < 0) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't create temporary file");
+ return (ARCHIVE_FATAL);
+ }
+ algsize = getalgsize(xar->opt_toc_sumalg);
+ if (algsize > 0) {
+ if (lseek(xar->temp_fd, algsize, SEEK_SET) < 0) {
+ archive_set_error(&(a->archive), errno,
+ "lseek failed");
+ return (ARCHIVE_FATAL);
+ }
+ xar->temp_offset = algsize;
+ }
+ }
+
+ if (archive_entry_hardlink(file->entry) == NULL) {
+ r = save_xattrs(a, file);
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Non regular files contents are unneeded to be saved to
+ * a temporary file. */
+ if (archive_entry_filetype(file->entry) != AE_IFREG)
+ return (r2);
+
+ /*
+ * Set the current file to cur_file to read its contents.
+ */
+ xar->cur_file = file;
+
+ if (archive_entry_nlink(file->entry) > 1) {
+ r = file_register_hardlink(a, file);
+ if (r != ARCHIVE_OK)
+ return (r);
+ if (archive_entry_hardlink(file->entry) != NULL) {
+ archive_entry_unset_size(file->entry);
+ return (r2);
+ }
+ }
+
+ /* Save a offset of current file in temporary file. */
+ file->data.temp_offset = xar->temp_offset;
+ file->data.size = archive_entry_size(file->entry);
+ file->data.compression = xar->opt_compression;
+ xar->bytes_remaining = archive_entry_size(file->entry);
+ checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
+ checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
+ r = xar_compression_init_encoder(a);
+
+ if (r != ARCHIVE_OK)
+ return (r);
+ else
+ return (r2);
+}
+
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+ struct xar *xar;
+ const unsigned char *p;
+ ssize_t ws;
+
+ xar = (struct xar *)a->format_data;
+ p = (const unsigned char *)buff;
+ while (s) {
+ ws = write(xar->temp_fd, p, s);
+ if (ws < 0) {
+ archive_set_error(&(a->archive), errno,
+ "fwrite function failed");
+ return (ARCHIVE_FATAL);
+ }
+ s -= ws;
+ p += ws;
+ xar->temp_offset += ws;
+ }
+ return (ARCHIVE_OK);
+}
+
+static ssize_t
+xar_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+ struct xar *xar;
+ enum la_zaction run;
+ size_t size = 0;
+ size_t rsize;
+ int r;
+
+ xar = (struct xar *)a->format_data;
+
+ if (s > xar->bytes_remaining)
+ s = (size_t)xar->bytes_remaining;
+ if (s == 0 || xar->cur_file == NULL)
+ return (0);
+ if (xar->cur_file->data.compression == NONE) {
+ checksum_update(&(xar->e_sumwrk), buff, s);
+ checksum_update(&(xar->a_sumwrk), buff, s);
+ size = rsize = s;
+ } else {
+ xar->stream.next_in = (const unsigned char *)buff;
+ xar->stream.avail_in = s;
+ if (xar->bytes_remaining > s)
+ run = ARCHIVE_Z_RUN;
+ else
+ run = ARCHIVE_Z_FINISH;
+ /* Compress file data. */
+ for (;;) {
+ r = compression_code(&(a->archive), &(xar->stream),
+ run);
+ if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+ return (ARCHIVE_FATAL);
+ if (xar->stream.avail_out == 0 ||
+ run == ARCHIVE_Z_FINISH) {
+ size = sizeof(xar->wbuff) -
+ xar->stream.avail_out;
+ checksum_update(&(xar->a_sumwrk), xar->wbuff,
+ size);
+ xar->cur_file->data.length += size;
+ if (write_to_temp(a, xar->wbuff,
+ size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ if (r == ARCHIVE_OK) {
+ /* Output buffer was full */
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out =
+ sizeof(xar->wbuff);
+ } else {
+ /* ARCHIVE_EOF - We are done */
+ break;
+ }
+ } else {
+ /* Compressor wants more input */
+ break;
+ }
+ }
+ rsize = s - xar->stream.avail_in;
+ checksum_update(&(xar->e_sumwrk), buff, rsize);
+ }
+#if !defined(_WIN32) || defined(__CYGWIN__)
+ if (xar->bytes_remaining ==
+ (uint64_t)archive_entry_size(xar->cur_file->entry)) {
+ /*
+ * Get the path of a shell script if so.
+ */
+ const unsigned char *b = (const unsigned char *)buff;
+
+ archive_string_empty(&(xar->cur_file->script));
+ if (rsize > 2 && b[0] == '#' && b[1] == '!') {
+ size_t i, end, off;
+
+ off = 2;
+ if (b[off] == ' ')
+ off++;
+#ifdef PATH_MAX
+ if ((rsize - off) > PATH_MAX)
+ end = off + PATH_MAX;
+ else
+#endif
+ end = rsize;
+ /* Find the end of a script path. */
+ for (i = off; i < end && b[i] != '\0' &&
+ b[i] != '\n' && b[i] != '\r' &&
+ b[i] != ' ' && b[i] != '\t'; i++)
+ ;
+ archive_strncpy(&(xar->cur_file->script), b + off,
+ i - off);
+ }
+ }
+#endif
+
+ if (xar->cur_file->data.compression == NONE) {
+ if (write_to_temp(a, buff, size) != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ xar->cur_file->data.length += size;
+ }
+ xar->bytes_remaining -= rsize;
+
+ return (rsize);
+}
+
+static int
+xar_finish_entry(struct archive_write *a)
+{
+ struct xar *xar;
+ struct file *file;
+ size_t s;
+ ssize_t w;
+
+ xar = (struct xar *)a->format_data;
+ if (xar->cur_file == NULL)
+ return (ARCHIVE_OK);
+
+ while (xar->bytes_remaining > 0) {
+ s = (size_t)xar->bytes_remaining;
+ if (s > a->null_length)
+ s = a->null_length;
+ w = xar_write_data(a, a->nulls, s);
+ if (w > 0)
+ xar->bytes_remaining -= w;
+ else
+ return (w);
+ }
+ file = xar->cur_file;
+ checksum_final(&(xar->e_sumwrk), &(file->data.e_sum));
+ checksum_final(&(xar->a_sumwrk), &(file->data.a_sum));
+ xar->cur_file = NULL;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_string_attr(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, const char *value,
+ const char *attrkey, const char *attrvalue)
+{
+ int r;
+
+ r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ if (attrkey != NULL && attrvalue != NULL) {
+ r = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST_CONST(attrkey), BAD_CAST_CONST(attrvalue));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ if (value != NULL) {
+ r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteString() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_string(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, const char *value)
+{
+ int r;
+
+ if (value == NULL)
+ return (ARCHIVE_OK);
+
+ r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ if (value != NULL) {
+ r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteString() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_fstring(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, const char *fmt, ...)
+{
+ struct xar *xar;
+ va_list ap;
+
+ xar = (struct xar *)a->format_data;
+ va_start(ap, fmt);
+ archive_string_empty(&xar->vstr);
+ archive_string_vsprintf(&xar->vstr, fmt, ap);
+ va_end(ap);
+ return (xmlwrite_string(a, writer, key, xar->vstr.s));
+}
+
+static int
+xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, time_t t, int z)
+{
+ char timestr[100];
+ struct tm tm;
+
+#if defined(HAVE_GMTIME_S)
+ gmtime_s(&tm, &t);
+#elif defined(HAVE_GMTIME_R)
+ gmtime_r(&t, &tm);
+#else
+ memcpy(&tm, gmtime(&t), sizeof(tm));
+#endif
+ memset(&timestr, 0, sizeof(timestr));
+ /* Do not use %F and %T for portability. */
+ strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &tm);
+ if (z)
+ strcat(timestr, "Z");
+ return (xmlwrite_string(a, writer, key, timestr));
+}
+
+static int
+xmlwrite_mode(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, mode_t mode)
+{
+ char ms[5];
+
+ ms[0] = '0';
+ ms[1] = '0' + ((mode >> 6) & 07);
+ ms[2] = '0' + ((mode >> 3) & 07);
+ ms[3] = '0' + (mode & 07);
+ ms[4] = '\0';
+
+ return (xmlwrite_string(a, writer, key, ms));
+}
+
+static int
+xmlwrite_sum(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *key, struct chksumval *sum)
+{
+ const char *algname;
+ int algsize;
+ char buff[MAX_SUM_SIZE*2 + 1];
+ char *p;
+ unsigned char *s;
+ int i, r;
+
+ if (sum->len > 0) {
+ algname = getalgname(sum->alg);
+ algsize = getalgsize(sum->alg);
+ if (algname != NULL) {
+ const char *hex = "0123456789abcdef";
+ p = buff;
+ s = sum->val;
+ for (i = 0; i < algsize; i++) {
+ *p++ = hex[(*s >> 4)];
+ *p++ = hex[(*s & 0x0f)];
+ s++;
+ }
+ *p = '\0';
+ r = xmlwrite_string_attr(a, writer,
+ key, buff,
+ "style", algname);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_heap(struct archive_write *a, xmlTextWriterPtr writer,
+ struct heap_data *heap)
+{
+ const char *encname;
+ int r;
+
+ r = xmlwrite_fstring(a, writer, "length", "%ju", heap->length);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_fstring(a, writer, "offset", "%ju", heap->temp_offset);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_fstring(a, writer, "size", "%ju", heap->size);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ switch (heap->compression) {
+ case GZIP:
+ encname = "application/x-gzip"; break;
+ case BZIP2:
+ encname = "application/x-bzip2"; break;
+ case LZMA:
+ encname = "application/x-lzma"; break;
+ case XZ:
+ encname = "application/x-xz"; break;
+ default:
+ encname = "application/octet-stream"; break;
+ }
+ r = xmlwrite_string_attr(a, writer, "encoding", NULL,
+ "style", encname);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_sum(a, writer, "archived-checksum", &(heap->a_sum));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_sum(a, writer, "extracted-checksum", &(heap->e_sum));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ return (ARCHIVE_OK);
+}
+
+/*
+ * xar utility records fflags as following xml elements:
+ * <flags>
+ * <UserNoDump/>
+ * .....
+ * </flags>
+ * or
+ * <ext2>
+ * <NoDump/>
+ * .....
+ * </ext2>
+ * If xar is running on BSD platform, records <flags>..</flags>;
+ * if xar is running on linux platform, records <ext2>..</ext2>;
+ * otherwise does not record.
+ *
+ * Our implements records both <flags> and <ext2> if it's necessary.
+ */
+static int
+make_fflags_entry(struct archive_write *a, xmlTextWriterPtr writer,
+ const char *element, const char *fflags_text)
+{
+ static const struct flagentry {
+ const char *name;
+ const char *xarname;
+ }
+ flagbsd[] = {
+ { "sappnd", "SystemAppend"},
+ { "sappend", "SystemAppend"},
+ { "arch", "SystemArchived"},
+ { "archived", "SystemArchived"},
+ { "schg", "SystemImmutable"},
+ { "schange", "SystemImmutable"},
+ { "simmutable", "SystemImmutable"},
+ { "nosunlnk", "SystemNoUnlink"},
+ { "nosunlink", "SystemNoUnlink"},
+ { "snapshot", "SystemSnapshot"},
+ { "uappnd", "UserAppend"},
+ { "uappend", "UserAppend"},
+ { "uchg", "UserImmutable"},
+ { "uchange", "UserImmutable"},
+ { "uimmutable", "UserImmutable"},
+ { "nodump", "UserNoDump"},
+ { "noopaque", "UserOpaque"},
+ { "nouunlnk", "UserNoUnlink"},
+ { "nouunlink", "UserNoUnlink"},
+ { NULL, NULL}
+ },
+ flagext2[] = {
+ { "sappnd", "AppendOnly"},
+ { "sappend", "AppendOnly"},
+ { "schg", "Immutable"},
+ { "schange", "Immutable"},
+ { "simmutable", "Immutable"},
+ { "nodump", "NoDump"},
+ { "nouunlnk", "Undelete"},
+ { "nouunlink", "Undelete"},
+ { "btree", "BTree"},
+ { "comperr", "CompError"},
+ { "compress", "Compress"},
+ { "noatime", "NoAtime"},
+ { "compdirty", "CompDirty"},
+ { "comprblk", "CompBlock"},
+ { "dirsync", "DirSync"},
+ { "hashidx", "HashIndexed"},
+ { "imagic", "iMagic"},
+ { "journal", "Journaled"},
+ { "securedeletion", "SecureDeletion"},
+ { "sync", "Synchronous"},
+ { "notail", "NoTail"},
+ { "topdir", "TopDir"},
+ { "reserved", "Reserved"},
+ { NULL, NULL}
+ };
+ const struct flagentry *fe, *flagentry;
+#define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd))
+ const struct flagentry *avail[FLAGENTRY_MAXSIZE];
+ const char *p;
+ int i, n, r;
+
+ if (strcmp(element, "ext2") == 0)
+ flagentry = flagext2;
+ else
+ flagentry = flagbsd;
+ n = 0;
+ p = fflags_text;
+ do {
+ const char *cp;
+
+ cp = strchr(p, ',');
+ if (cp == NULL)
+ cp = p + strlen(p);
+
+ for (fe = flagentry; fe->name != NULL; fe++) {
+ if (fe->name[cp - p] != '\0'
+ || p[0] != fe->name[0])
+ continue;
+ if (strncmp(p, fe->name, cp - p) == 0) {
+ avail[n++] = fe;
+ break;
+ }
+ }
+ if (*cp == ',')
+ p = cp + 1;
+ else
+ p = NULL;
+ } while (p != NULL);
+
+ if (n > 0) {
+ r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(element));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ for (i = 0; i < n; i++) {
+ r = xmlwrite_string(a, writer,
+ avail[i]->xarname, NULL);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+make_file_entry(struct archive_write *a, xmlTextWriterPtr writer,
+ struct file *file)
+{
+ struct xar *xar;
+ const char *filetype, *filelink, *fflags;
+ struct archive_string linkto;
+ struct heap_data *heap;
+ unsigned char *tmp;
+ const char *p;
+ size_t len;
+ int r, r2, l, ll;
+
+ xar = (struct xar *)a->format_data;
+ r2 = ARCHIVE_OK;
+
+ /*
+ * Make a file name entry, "<name>".
+ */
+ l = ll = archive_strlen(&(file->basename));
+ tmp = malloc(l);
+ if (tmp == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ r = UTF8Toisolat1(tmp, &l, BAD_CAST(file->basename.s), &ll);
+ free(tmp);
+ if (r < 0) {
+ r = xmlTextWriterStartElement(writer, BAD_CAST("name"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlTextWriterWriteAttribute(writer,
+ BAD_CAST("enctype"), BAD_CAST("base64"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlTextWriterWriteBase64(writer, file->basename.s,
+ 0, archive_strlen(&(file->basename)));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteBase64() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ } else {
+ r = xmlwrite_string(a, writer, "name", file->basename.s);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a file type entry, "<type>".
+ */
+ filelink = NULL;
+ archive_string_init(&linkto);
+ switch (archive_entry_filetype(file->entry)) {
+ case AE_IFDIR:
+ filetype = "directory"; break;
+ case AE_IFLNK:
+ filetype = "symlink"; break;
+ case AE_IFCHR:
+ filetype = "character special"; break;
+ case AE_IFBLK:
+ filetype = "block special"; break;
+ case AE_IFSOCK:
+ filetype = "socket"; break;
+ case AE_IFIFO:
+ filetype = "fifo"; break;
+ case AE_IFREG:
+ default:
+ if (file->hardlink_target != NULL) {
+ filetype = "hardlink";
+ filelink = "link";
+ if (file->hardlink_target == file)
+ archive_strcpy(&linkto, "original");
+ else
+ archive_string_sprintf(&linkto, "%d",
+ file->hardlink_target->id);
+ } else
+ filetype = "file";
+ break;
+ }
+ r = xmlwrite_string_attr(a, writer, "type", filetype,
+ filelink, linkto.s);
+ archive_string_free(&linkto);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * On a virtual directory, we record "name" and "type" only.
+ */
+ if (file->virtual)
+ return (ARCHIVE_OK);
+
+ switch (archive_entry_filetype(file->entry)) {
+ case AE_IFLNK:
+ /*
+ * xar utility has checked a file type, which
+ * a symbolic-link file has referenced.
+ * For example:
+ * <link type="directory">../ref/</link>
+ * The symlink target file is "../ref/" and its
+ * file type is a directory.
+ *
+ * <link type="file">../f</link>
+ * The symlink target file is "../f" and its
+ * file type is a regular file.
+ *
+ * But our implementation cannot do it, and then we
+ * always record that a attribute "type" is "broken",
+ * for example:
+ * <link type="broken">foo/bar</link>
+ * It means "foo/bar" is not reachable.
+ */
+ r = xmlwrite_string_attr(a, writer, "link",
+ file->symlink.s,
+ "type", "broken");
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ break;
+ case AE_IFCHR:
+ case AE_IFBLK:
+ r = xmlTextWriterStartElement(writer, BAD_CAST("device"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlwrite_fstring(a, writer, "major",
+ "%d", archive_entry_rdevmajor(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_fstring(a, writer, "minor",
+ "%d", archive_entry_rdevminor(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Make a inode entry, "<inode>".
+ */
+ r = xmlwrite_fstring(a, writer, "inode",
+ "%jd", archive_entry_ino64(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ if (archive_entry_dev(file->entry) != 0) {
+ r = xmlwrite_fstring(a, writer, "deviceno",
+ "%d", archive_entry_dev(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a file mode entry, "<mode>".
+ */
+ r = xmlwrite_mode(a, writer, "mode",
+ archive_entry_mode(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ /*
+ * Make a user entry, "<uid>" and "<user>.
+ */
+ r = xmlwrite_fstring(a, writer, "uid",
+ "%d", archive_entry_uid(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = archive_entry_uname_l(file->entry, &p, &len, xar->sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Uname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate uname '%s' to UTF-8",
+ archive_entry_uname(file->entry));
+ r2 = ARCHIVE_WARN;
+ }
+ if (len > 0) {
+ r = xmlwrite_string(a, writer, "user", p);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a group entry, "<gid>" and "<group>.
+ */
+ r = xmlwrite_fstring(a, writer, "gid",
+ "%d", archive_entry_gid(file->entry));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = archive_entry_gname_l(file->entry, &p, &len, xar->sconv);
+ if (r != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Gname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate gname '%s' to UTF-8",
+ archive_entry_gname(file->entry));
+ r2 = ARCHIVE_WARN;
+ }
+ if (len > 0) {
+ r = xmlwrite_string(a, writer, "group", p);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a ctime entry, "<ctime>".
+ */
+ if (archive_entry_ctime_is_set(file->entry)) {
+ r = xmlwrite_time(a, writer, "ctime",
+ archive_entry_ctime(file->entry), 1);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a mtime entry, "<mtime>".
+ */
+ if (archive_entry_mtime_is_set(file->entry)) {
+ r = xmlwrite_time(a, writer, "mtime",
+ archive_entry_mtime(file->entry), 1);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make a atime entry, "<atime>".
+ */
+ if (archive_entry_atime_is_set(file->entry)) {
+ r = xmlwrite_time(a, writer, "atime",
+ archive_entry_atime(file->entry), 1);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ }
+
+ /*
+ * Make fflags entries, "<flags>" and "<ext2>".
+ */
+ fflags = archive_entry_fflags_text(file->entry);
+ if (fflags != NULL) {
+ r = make_fflags_entry(a, writer, "flags", fflags);
+ if (r < 0)
+ return (r);
+ r = make_fflags_entry(a, writer, "ext2", fflags);
+ if (r < 0)
+ return (r);
+ }
+
+ /*
+ * Make extended attribute entries, "<ea>".
+ */
+ archive_entry_xattr_reset(file->entry);
+ for (heap = file->xattr.first; heap != NULL; heap = heap->next) {
+ const char *name;
+ const void *value;
+ size_t size;
+
+ archive_entry_xattr_next(file->entry,
+ &name, &value, &size);
+ r = xmlTextWriterStartElement(writer, BAD_CAST("ea"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlTextWriterWriteFormatAttribute(writer,
+ BAD_CAST("id"), "%d", heap->id);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ r = xmlwrite_heap(a, writer, heap);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+ r = xmlwrite_string(a, writer, "name", name);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ /*
+ * Make a file data entry, "<data>".
+ */
+ if (file->data.length > 0) {
+ r = xmlTextWriterStartElement(writer, BAD_CAST("data"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ r = xmlwrite_heap(a, writer, &(file->data));
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ if (archive_strlen(&file->script) > 0) {
+ r = xmlTextWriterStartElement(writer, BAD_CAST("content"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+
+ r = xmlwrite_string(a, writer,
+ "interpreter", file->script.s);
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ r = xmlwrite_string(a, writer, "type", "script");
+ if (r < 0)
+ return (ARCHIVE_FATAL);
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ return (r2);
+}
+
+/*
+ * Make the TOC
+ */
+static int
+make_toc(struct archive_write *a)
+{
+ struct xar *xar;
+ struct file *np;
+ xmlBufferPtr bp;
+ xmlTextWriterPtr writer;
+ int algsize;
+ int r, ret;
+
+ xar = (struct xar *)a->format_data;
+
+ ret = ARCHIVE_FATAL;
+
+ /*
+ * Initialize xml writer.
+ */
+ writer = NULL;
+ bp = xmlBufferCreate();
+ if (bp == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "xmlBufferCreate() "
+ "couldn't create xml buffer");
+ goto exit_toc;
+ }
+ writer = xmlNewTextWriterMemory(bp, 0);
+ if (writer == NULL) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlNewTextWriterMemory() "
+ "couldn't create xml writer");
+ goto exit_toc;
+ }
+ r = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartDocument() failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterSetIndent(writer, 4);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterSetIndent() failed: %d", r);
+ goto exit_toc;
+ }
+
+ /*
+ * Start recording TOC
+ */
+ r = xmlTextWriterStartElement(writer, BAD_CAST("xar"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterStartElement(writer, BAD_CAST("toc"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartDocument() failed: %d", r);
+ goto exit_toc;
+ }
+
+ /*
+ * Record the creation time of the archive file.
+ */
+ r = xmlwrite_time(a, writer, "creation-time", time(NULL), 0);
+ if (r < 0)
+ goto exit_toc;
+
+ /*
+ * Record the checksum value of TOC
+ */
+ algsize = getalgsize(xar->opt_toc_sumalg);
+ if (algsize) {
+ /*
+ * Record TOC checksum
+ */
+ r = xmlTextWriterStartElement(writer, BAD_CAST("checksum"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"),
+ BAD_CAST_CONST(getalgname(xar->opt_toc_sumalg)));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() failed: %d", r);
+ goto exit_toc;
+ }
+
+ /*
+ * Record the offset of the value of checksum of TOC
+ */
+ r = xmlwrite_string(a, writer, "offset", "0");
+ if (r < 0)
+ goto exit_toc;
+
+ /*
+ * Record the size of the value of checksum of TOC
+ */
+ r = xmlwrite_fstring(a, writer, "size", "%d", algsize);
+ if (r < 0)
+ goto exit_toc;
+
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() failed: %d", r);
+ goto exit_toc;
+ }
+ }
+
+ np = xar->root;
+ do {
+ if (np != np->parent) {
+ r = make_file_entry(a, writer, np);
+ if (r != ARCHIVE_OK)
+ goto exit_toc;
+ }
+
+ if (np->dir && np->children.first != NULL) {
+ /* Enter to sub directories. */
+ np = np->children.first;
+ r = xmlTextWriterStartElement(writer,
+ BAD_CAST("file"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterWriteFormatAttribute(
+ writer, BAD_CAST("id"), "%d", np->id);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ continue;
+ }
+ while (np != np->parent) {
+ r = xmlTextWriterEndElement(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndElement() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ if (np->chnext == NULL) {
+ /* Return to the parent directory. */
+ np = np->parent;
+ } else {
+ np = np->chnext;
+ r = xmlTextWriterStartElement(writer,
+ BAD_CAST("file"));
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterStartElement() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ r = xmlTextWriterWriteFormatAttribute(
+ writer, BAD_CAST("id"), "%d", np->id);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterWriteAttribute() "
+ "failed: %d", r);
+ goto exit_toc;
+ }
+ break;
+ }
+ }
+ } while (np != np->parent);
+
+ r = xmlTextWriterEndDocument(writer);
+ if (r < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "xmlTextWriterEndDocument() failed: %d", r);
+ goto exit_toc;
+ }
+#if DEBUG_PRINT_TOC
+ fprintf(stderr, "\n---TOC-- %d bytes --\n%s\n",
+ strlen((const char *)bp->content), bp->content);
+#endif
+
+ /*
+ * Compress the TOC and calculate the sum of the TOC.
+ */
+ xar->toc.temp_offset = xar->temp_offset;
+ xar->toc.size = bp->use;
+ checksum_init(&(xar->a_sumwrk), xar->opt_toc_sumalg);
+
+ r = compression_init_encoder_gzip(&(a->archive),
+ &(xar->stream), 6, 1);
+ if (r != ARCHIVE_OK)
+ goto exit_toc;
+ xar->stream.next_in = bp->content;
+ xar->stream.avail_in = bp->use;
+ xar->stream.total_in = 0;
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out = sizeof(xar->wbuff);
+ xar->stream.total_out = 0;
+ for (;;) {
+ size_t size;
+
+ r = compression_code(&(a->archive),
+ &(xar->stream), ARCHIVE_Z_FINISH);
+ if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+ goto exit_toc;
+ size = sizeof(xar->wbuff) - xar->stream.avail_out;
+ checksum_update(&(xar->a_sumwrk), xar->wbuff, size);
+ if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK)
+ goto exit_toc;
+ if (r == ARCHIVE_EOF)
+ break;
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out = sizeof(xar->wbuff);
+ }
+ r = compression_end(&(a->archive), &(xar->stream));
+ if (r != ARCHIVE_OK)
+ goto exit_toc;
+ xar->toc.length = xar->stream.total_out;
+ xar->toc.compression = GZIP;
+ checksum_final(&(xar->a_sumwrk), &(xar->toc.a_sum));
+
+ ret = ARCHIVE_OK;
+exit_toc:
+ if (writer)
+ xmlFreeTextWriter(writer);
+ if (bp)
+ xmlBufferFree(bp);
+
+ return (ret);
+}
+
+static int
+flush_wbuff(struct archive_write *a)
+{
+ struct xar *xar;
+ int r;
+ size_t s;
+
+ xar = (struct xar *)a->format_data;
+ s = sizeof(xar->wbuff) - xar->wbuff_remaining;
+ r = __archive_write_output(a, xar->wbuff, s);
+ if (r != ARCHIVE_OK)
+ return (r);
+ xar->wbuff_remaining = sizeof(xar->wbuff);
+ return (r);
+}
+
+static int
+copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
+{
+ struct xar *xar;
+ int r;
+
+ xar = (struct xar *)a->format_data;
+ if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) {
+ archive_set_error(&(a->archive), errno, "lseek failed");
+ return (ARCHIVE_FATAL);
+ }
+ while (length) {
+ size_t rsize;
+ ssize_t rs;
+ unsigned char *wb;
+
+ if (length > xar->wbuff_remaining)
+ rsize = xar->wbuff_remaining;
+ else
+ rsize = (size_t)length;
+ wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
+ rs = read(xar->temp_fd, wb, rsize);
+ if (rs < 0) {
+ archive_set_error(&(a->archive), errno,
+ "Can't read temporary file(%jd)",
+ (intmax_t)rs);
+ return (ARCHIVE_FATAL);
+ }
+ if (rs == 0) {
+ archive_set_error(&(a->archive), 0,
+ "Truncated xar archive");
+ return (ARCHIVE_FATAL);
+ }
+ xar->wbuff_remaining -= rs;
+ length -= rs;
+ if (xar->wbuff_remaining == 0) {
+ r = flush_wbuff(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+xar_close(struct archive_write *a)
+{
+ struct xar *xar;
+ unsigned char *wb;
+ uint64_t length;
+ int r;
+
+ xar = (struct xar *)a->format_data;
+
+ /* Empty! */
+ if (xar->root->children.first == NULL)
+ return (ARCHIVE_OK);
+
+ /* Save the length of all file extended attributes and contents. */
+ length = xar->temp_offset;
+
+ /* Connect hardlinked files */
+ file_connect_hardlink_files(xar);
+
+ /* Make the TOC */
+ r = make_toc(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ /*
+ * Make the xar header on wbuff(write buffer).
+ */
+ wb = xar->wbuff;
+ xar->wbuff_remaining = sizeof(xar->wbuff);
+ archive_be32enc(&wb[0], HEADER_MAGIC);
+ archive_be16enc(&wb[4], HEADER_SIZE);
+ archive_be16enc(&wb[6], HEADER_VERSION);
+ archive_be64enc(&wb[8], xar->toc.length);
+ archive_be64enc(&wb[16], xar->toc.size);
+ archive_be32enc(&wb[24], xar->toc.a_sum.alg);
+ xar->wbuff_remaining -= HEADER_SIZE;
+
+ /*
+ * Write the TOC
+ */
+ r = copy_out(a, xar->toc.temp_offset, xar->toc.length);
+ if (r != ARCHIVE_OK)
+ return (r);
+
+ /* Write the checksum value of the TOC. */
+ if (xar->toc.a_sum.len) {
+ if (xar->wbuff_remaining < xar->toc.a_sum.len) {
+ r = flush_wbuff(a);
+ if (r != ARCHIVE_OK)
+ return (r);
+ }
+ wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
+ memcpy(wb, xar->toc.a_sum.val, xar->toc.a_sum.len);
+ xar->wbuff_remaining -= xar->toc.a_sum.len;
+ }
+
+ /*
+ * Write all file extended attributes and contents.
+ */
+ r = copy_out(a, xar->toc.a_sum.len, length);
+ if (r != ARCHIVE_OK)
+ return (r);
+ r = flush_wbuff(a);
+ return (r);
+}
+
+static int
+xar_free(struct archive_write *a)
+{
+ struct xar *xar;
+
+ xar = (struct xar *)a->format_data;
+
+ /* Close the temporary file. */
+ if (xar->temp_fd >= 0)
+ close(xar->temp_fd);
+
+ archive_string_free(&(xar->cur_dirstr));
+ archive_string_free(&(xar->tstr));
+ archive_string_free(&(xar->vstr));
+ file_free_hardlinks(xar);
+ file_free_register(xar);
+ compression_end(&(a->archive), &(xar->stream));
+ free(xar);
+
+ return (ARCHIVE_OK);
+}
+
+static int
+file_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct file *f1 = (const struct file *)n1;
+ const struct file *f2 = (const struct file *)n2;
+
+ return (strcmp(f1->basename.s, f2->basename.s));
+}
+
+static int
+file_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct file *f = (const struct file *)n;
+
+ return (strcmp(f->basename.s, (const char *)key));
+}
+
+static struct file *
+file_new(struct archive_write *a, struct archive_entry *entry)
+{
+ struct file *file;
+ static const struct archive_rb_tree_ops rb_ops = {
+ file_cmp_node, file_cmp_key
+ };
+
+ file = calloc(1, sizeof(*file));
+ if (file == NULL)
+ return (NULL);
+
+ if (entry != NULL)
+ file->entry = archive_entry_clone(entry);
+ else
+ file->entry = archive_entry_new2(&a->archive);
+ if (file->entry == NULL) {
+ free(file);
+ return (NULL);
+ }
+ __archive_rb_tree_init(&(file->rbtree), &rb_ops);
+ file->children.first = NULL;
+ file->children.last = &(file->children.first);
+ file->xattr.first = NULL;
+ file->xattr.last = &(file->xattr.first);
+ archive_string_init(&(file->parentdir));
+ archive_string_init(&(file->basename));
+ archive_string_init(&(file->symlink));
+ archive_string_init(&(file->script));
+ if (entry != NULL && archive_entry_filetype(entry) == AE_IFDIR)
+ file->dir = 1;
+
+ return (file);
+}
+
+static void
+file_free(struct file *file)
+{
+ struct heap_data *heap, *next_heap;
+
+ heap = file->xattr.first;
+ while (heap != NULL) {
+ next_heap = heap->next;
+ free(heap);
+ heap = next_heap;
+ }
+ archive_string_free(&(file->parentdir));
+ archive_string_free(&(file->basename));
+ archive_string_free(&(file->symlink));
+ archive_string_free(&(file->script));
+ archive_entry_free(file->entry);
+ free(file);
+}
+
+static struct file *
+file_create_virtual_dir(struct archive_write *a, struct xar *xar,
+ const char *pathname)
+{
+ struct file *file;
+
+ (void)xar; /* UNUSED */
+
+ file = file_new(a, NULL);
+ if (file == NULL)
+ return (NULL);
+ archive_entry_set_pathname(file->entry, pathname);
+ archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
+
+ file->dir = 1;
+ file->virtual = 1;
+
+ return (file);
+}
+
+static int
+file_add_child_tail(struct file *parent, struct file *child)
+{
+ if (!__archive_rb_tree_insert_node(
+ &(parent->rbtree), (struct archive_rb_node *)child))
+ return (0);
+ child->chnext = NULL;
+ *parent->children.last = child;
+ parent->children.last = &(child->chnext);
+ child->parent = parent;
+ return (1);
+}
+
+/*
+ * Find a entry from `parent'
+ */
+static struct file *
+file_find_child(struct file *parent, const char *child_name)
+{
+ struct file *np;
+
+ np = (struct file *)__archive_rb_tree_find_node(
+ &(parent->rbtree), child_name);
+ return (np);
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static void
+cleanup_backslash(char *utf8, size_t len)
+{
+
+ /* Convert a path-separator from '\' to '/' */
+ while (*utf8 != '\0' && len) {
+ if (*utf8 == '\\')
+ *utf8 = '/';
+ ++utf8;
+ --len;
+ }
+}
+#else
+#define cleanup_backslash(p, len) /* nop */
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+file_gen_utility_names(struct archive_write *a, struct file *file)
+{
+ struct xar *xar;
+ const char *pp;
+ char *p, *dirname, *slash;
+ size_t len;
+ int r = ARCHIVE_OK;
+
+ xar = (struct xar *)a->format_data;
+ archive_string_empty(&(file->parentdir));
+ archive_string_empty(&(file->basename));
+ archive_string_empty(&(file->symlink));
+
+ if (file->parent == file)/* virtual root */
+ return (ARCHIVE_OK);
+
+ if (archive_entry_pathname_l(file->entry, &pp, &len, xar->sconv)
+ != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate pathname '%s' to UTF-8",
+ archive_entry_pathname(file->entry));
+ r = ARCHIVE_WARN;
+ }
+ archive_strncpy(&(file->parentdir), pp, len);
+ len = file->parentdir.length;
+ p = dirname = file->parentdir.s;
+ /*
+ * Convert a path-separator from '\' to '/'
+ */
+ cleanup_backslash(p, len);
+
+ /*
+ * Remove leading '/', '../' and './' elements
+ */
+ while (*p) {
+ if (p[0] == '/') {
+ p++;
+ len--;
+ } else if (p[0] != '.')
+ break;
+ else if (p[1] == '.' && p[2] == '/') {
+ p += 3;
+ len -= 3;
+ } else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
+ p += 2;
+ len -= 2;
+ } else if (p[1] == '\0') {
+ p++;
+ len--;
+ } else
+ break;
+ }
+ if (p != dirname) {
+ memmove(dirname, p, len+1);
+ p = dirname;
+ }
+ /*
+ * Remove "/","/." and "/.." elements from tail.
+ */
+ while (len > 0) {
+ size_t ll = len;
+
+ if (p[len-1] == '/') {
+ p[len-1] = '\0';
+ len--;
+ }
+ if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+ p[len-2] = '\0';
+ len -= 2;
+ }
+ if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+ p[len-1] == '.') {
+ p[len-3] = '\0';
+ len -= 3;
+ }
+ if (ll == len)
+ break;
+ }
+ while (*p) {
+ if (p[0] == '/') {
+ if (p[1] == '/')
+ /* Convert '//' --> '/' */
+ memmove(p, p+1, strlen(p+1) + 1);
+ else if (p[1] == '.' && p[2] == '/')
+ /* Convert '/./' --> '/' */
+ memmove(p, p+2, strlen(p+2) + 1);
+ else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+ /* Convert 'dir/dir1/../dir2/'
+ * --> 'dir/dir2/'
+ */
+ char *rp = p -1;
+ while (rp >= dirname) {
+ if (*rp == '/')
+ break;
+ --rp;
+ }
+ if (rp > dirname) {
+ strcpy(rp, p+3);
+ p = rp;
+ } else {
+ strcpy(dirname, p+4);
+ p = dirname;
+ }
+ } else
+ p++;
+ } else
+ p++;
+ }
+ p = dirname;
+ len = strlen(p);
+
+ if (archive_entry_filetype(file->entry) == AE_IFLNK) {
+ size_t len2;
+ /* Convert symlink name too. */
+ if (archive_entry_symlink_l(file->entry, &pp, &len2,
+ xar->sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate symlink '%s' to UTF-8",
+ archive_entry_symlink(file->entry));
+ r = ARCHIVE_WARN;
+ }
+ archive_strncpy(&(file->symlink), pp, len2);
+ cleanup_backslash(file->symlink.s, file->symlink.length);
+ }
+ /*
+ * - Count up directory elements.
+ * - Find out the position which points the last position of
+ * path separator('/').
+ */
+ slash = NULL;
+ for (; *p != '\0'; p++)
+ if (*p == '/')
+ slash = p;
+ if (slash == NULL) {
+ /* The pathname doesn't have a parent directory. */
+ file->parentdir.length = len;
+ archive_string_copy(&(file->basename), &(file->parentdir));
+ archive_string_empty(&(file->parentdir));
+ *file->parentdir.s = '\0';
+ return (r);
+ }
+
+ /* Make a basename from dirname and slash */
+ *slash = '\0';
+ file->parentdir.length = slash - dirname;
+ archive_strcpy(&(file->basename), slash + 1);
+ return (r);
+}
+
+static int
+get_path_component(char *name, int n, const char *fn)
+{
+ char *p;
+ int l;
+
+ p = strchr(fn, '/');
+ if (p == NULL) {
+ if ((l = strlen(fn)) == 0)
+ return (0);
+ } else
+ l = p - fn;
+ if (l > n -1)
+ return (-1);
+ memcpy(name, fn, l);
+ name[l] = '\0';
+
+ return (l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+file_tree(struct archive_write *a, struct file **filepp)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+ char name[NAME_MAX+1];
+#else
+ char name[256];
+#endif
+ struct xar *xar = (struct xar *)a->format_data;
+ struct file *dent, *file, *np;
+ struct archive_entry *ent;
+ const char *fn, *p;
+ int l;
+
+ file = *filepp;
+ dent = xar->root;
+ if (file->parentdir.length > 0)
+ fn = p = file->parentdir.s;
+ else
+ fn = p = "";
+
+ /*
+ * If the path of the parent directory of `file' entry is
+ * the same as the path of `cur_dirent', add isoent to
+ * `cur_dirent'.
+ */
+ if (archive_strlen(&(xar->cur_dirstr))
+ == archive_strlen(&(file->parentdir)) &&
+ strcmp(xar->cur_dirstr.s, fn) == 0) {
+ if (!file_add_child_tail(xar->cur_dirent, file)) {
+ np = (struct file *)__archive_rb_tree_find_node(
+ &(xar->cur_dirent->rbtree),
+ file->basename.s);
+ goto same_entry;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ for (;;) {
+ l = get_path_component(name, sizeof(name), fn);
+ if (l == 0) {
+ np = NULL;
+ break;
+ }
+ if (l < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+
+ np = file_find_child(dent, name);
+ if (np == NULL || fn[0] == '\0')
+ break;
+
+ /* Find next subdirectory. */
+ if (!np->dir) {
+ /* NOT Directory! */
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "`%s' is not directory, we cannot insert `%s' ",
+ archive_entry_pathname(np->entry),
+ archive_entry_pathname(file->entry));
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FAILED);
+ }
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ dent = np;
+ }
+ if (np == NULL) {
+ /*
+ * Create virtual parent directories.
+ */
+ while (fn[0] != '\0') {
+ struct file *vp;
+ struct archive_string as;
+
+ archive_string_init(&as);
+ archive_strncat(&as, p, fn - p + l);
+ if (as.s[as.length-1] == '/') {
+ as.s[as.length-1] = '\0';
+ as.length--;
+ }
+ vp = file_create_virtual_dir(a, xar, as.s);
+ if (vp == NULL) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ archive_string_free(&as);
+ if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED)
+ return (ARCHIVE_FATAL);
+ file_add_child_tail(dent, vp);
+ file_register(xar, vp);
+ np = vp;
+
+ fn += l;
+ if (fn[0] == '/')
+ fn++;
+ l = get_path_component(name, sizeof(name), fn);
+ if (l < 0) {
+ archive_string_free(&as);
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "A name buffer is too small");
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FATAL);
+ }
+ dent = np;
+ }
+
+ /* Found out the parent directory where isoent can be
+ * inserted. */
+ xar->cur_dirent = dent;
+ archive_string_empty(&(xar->cur_dirstr));
+ archive_string_ensure(&(xar->cur_dirstr),
+ archive_strlen(&(dent->parentdir)) +
+ archive_strlen(&(dent->basename)) + 2);
+ if (archive_strlen(&(dent->parentdir)) +
+ archive_strlen(&(dent->basename)) == 0)
+ xar->cur_dirstr.s[0] = 0;
+ else {
+ if (archive_strlen(&(dent->parentdir)) > 0) {
+ archive_string_copy(&(xar->cur_dirstr),
+ &(dent->parentdir));
+ archive_strappend_char(&(xar->cur_dirstr), '/');
+ }
+ archive_string_concat(&(xar->cur_dirstr),
+ &(dent->basename));
+ }
+
+ if (!file_add_child_tail(dent, file)) {
+ np = (struct file *)__archive_rb_tree_find_node(
+ &(dent->rbtree), file->basename.s);
+ goto same_entry;
+ }
+ return (ARCHIVE_OK);
+ }
+
+same_entry:
+ /*
+ * We have already has the entry the filename of which is
+ * the same.
+ */
+ if (archive_entry_filetype(np->entry) !=
+ archive_entry_filetype(file->entry)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Found duplicate entries `%s' and its file type is "
+ "different",
+ archive_entry_pathname(np->entry));
+ file_free(file);
+ *filepp = NULL;
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Swap files. */
+ ent = np->entry;
+ np->entry = file->entry;
+ file->entry = ent;
+ np->virtual = 0;
+
+ file_free(file);
+ *filepp = np;
+ return (ARCHIVE_OK);
+}
+
+static void
+file_register(struct xar *xar, struct file *file)
+{
+ file->id = xar->file_idx++;
+ file->next = NULL;
+ *xar->file_list.last = file;
+ xar->file_list.last = &(file->next);
+}
+
+static void
+file_init_register(struct xar *xar)
+{
+ xar->file_list.first = NULL;
+ xar->file_list.last = &(xar->file_list.first);
+}
+
+static void
+file_free_register(struct xar *xar)
+{
+ struct file *file, *file_next;
+
+ file = xar->file_list.first;
+ while (file != NULL) {
+ file_next = file->next;
+ file_free(file);
+ file = file_next;
+ }
+}
+
+/*
+ * Register entry to get a hardlink target.
+ */
+static int
+file_register_hardlink(struct archive_write *a, struct file *file)
+{
+ struct xar *xar = (struct xar *)a->format_data;
+ struct hardlink *hl;
+ const char *pathname;
+
+ archive_entry_set_nlink(file->entry, 1);
+ pathname = archive_entry_hardlink(file->entry);
+ if (pathname == NULL) {
+ /* This `file` is a hardlink target. */
+ hl = malloc(sizeof(*hl));
+ if (hl == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory");
+ return (ARCHIVE_FATAL);
+ }
+ hl->nlink = 1;
+ /* A hardlink target must be the first position. */
+ file->hlnext = NULL;
+ hl->file_list.first = file;
+ hl->file_list.last = &(file->hlnext);
+ __archive_rb_tree_insert_node(&(xar->hardlink_rbtree),
+ (struct archive_rb_node *)hl);
+ } else {
+ hl = (struct hardlink *)__archive_rb_tree_find_node(
+ &(xar->hardlink_rbtree), pathname);
+ if (hl != NULL) {
+ /* Insert `file` entry into the tail. */
+ file->hlnext = NULL;
+ *hl->file_list.last = file;
+ hl->file_list.last = &(file->hlnext);
+ hl->nlink++;
+ }
+ archive_entry_unset_size(file->entry);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Hardlinked files have to have the same location of extent.
+ * We have to find out hardlink target entries for entries which
+ * have a hardlink target name.
+ */
+static void
+file_connect_hardlink_files(struct xar *xar)
+{
+ struct archive_rb_node *n;
+ struct hardlink *hl;
+ struct file *target, *nf;
+
+ ARCHIVE_RB_TREE_FOREACH(n, &(xar->hardlink_rbtree)) {
+ hl = (struct hardlink *)n;
+
+ /* The first entry must be a hardlink target. */
+ target = hl->file_list.first;
+ archive_entry_set_nlink(target->entry, hl->nlink);
+ if (hl->nlink > 1)
+ /* It means this file is a hardlink
+ * target itself. */
+ target->hardlink_target = target;
+ for (nf = target->hlnext;
+ nf != NULL; nf = nf->hlnext) {
+ nf->hardlink_target = target;
+ archive_entry_set_nlink(nf->entry, hl->nlink);
+ }
+ }
+}
+
+static int
+file_hd_cmp_node(const struct archive_rb_node *n1,
+ const struct archive_rb_node *n2)
+{
+ const struct hardlink *h1 = (const struct hardlink *)n1;
+ const struct hardlink *h2 = (const struct hardlink *)n2;
+
+ return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
+ archive_entry_pathname(h2->file_list.first->entry)));
+}
+
+static int
+file_hd_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+ const struct hardlink *h = (const struct hardlink *)n;
+
+ return (strcmp(archive_entry_pathname(h->file_list.first->entry),
+ (const char *)key));
+}
+
+
+static void
+file_init_hardlinks(struct xar *xar)
+{
+ static const struct archive_rb_tree_ops rb_ops = {
+ file_hd_cmp_node, file_hd_cmp_key,
+ };
+
+ __archive_rb_tree_init(&(xar->hardlink_rbtree), &rb_ops);
+}
+
+static void
+file_free_hardlinks(struct xar *xar)
+{
+ struct archive_rb_node *n, *tmp;
+
+ ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(xar->hardlink_rbtree), tmp) {
+ __archive_rb_tree_remove_node(&(xar->hardlink_rbtree), n);
+ free(n);
+ }
+}
+
+static void
+checksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg)
+{
+ sumwrk->alg = sum_alg;
+ switch (sum_alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_init(&(sumwrk->sha1ctx));
+ break;
+ case CKSUM_MD5:
+ archive_md5_init(&(sumwrk->md5ctx));
+ break;
+ }
+}
+
+static void
+checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
+{
+
+ switch (sumwrk->alg) {
+ case CKSUM_NONE:
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
+ break;
+ case CKSUM_MD5:
+ archive_md5_update(&(sumwrk->md5ctx), buff, size);
+ break;
+ }
+}
+
+static void
+checksum_final(struct chksumwork *sumwrk, struct chksumval *sumval)
+{
+
+ switch (sumwrk->alg) {
+ case CKSUM_NONE:
+ sumval->len = 0;
+ break;
+ case CKSUM_SHA1:
+ archive_sha1_final(&(sumwrk->sha1ctx), sumval->val);
+ sumval->len = SHA1_SIZE;
+ break;
+ case CKSUM_MD5:
+ archive_md5_final(&(sumwrk->md5ctx), sumval->val);
+ sumval->len = MD5_SIZE;
+ break;
+ }
+ sumval->alg = sumwrk->alg;
+}
+
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)
+static int
+compression_unsupported_encoder(struct archive *a,
+ struct la_zstream *lastrm, const char *name)
+{
+
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "%s compression not supported on this platform", name);
+ lastrm->valid = 0;
+ lastrm->real_stream = NULL;
+ return (ARCHIVE_FAILED);
+}
+#endif
+
+static int
+compression_init_encoder_gzip(struct archive *a,
+ struct la_zstream *lastrm, int level, int withheader)
+{
+ z_stream *strm;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for gzip stream");
+ return (ARCHIVE_FATAL);
+ }
+ /* zlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in = (uLong)lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out = (uLong)lastrm->total_out;
+ if (deflateInit2(strm, level, Z_DEFLATED,
+ (withheader)?15:-15,
+ 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_gzip;
+ lastrm->end = compression_end_gzip;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_gzip(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ z_stream *strm;
+ int r;
+
+ strm = (z_stream *)lastrm->real_stream;
+ /* zlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in = (uLong)lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out = (uLong)lastrm->total_out;
+ r = deflate(strm,
+ (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);
+ lastrm->next_in = strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in = strm->total_in;
+ lastrm->next_out = strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out = strm->total_out;
+ switch (r) {
+ case Z_OK:
+ return (ARCHIVE_OK);
+ case Z_STREAM_END:
+ return (ARCHIVE_EOF);
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "GZip compression failed:"
+ " deflate() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_gzip(struct archive *a, struct la_zstream *lastrm)
+{
+ z_stream *strm;
+ int r;
+
+ strm = (z_stream *)lastrm->real_stream;
+ r = deflateEnd(strm);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ if (r != Z_OK) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int
+compression_init_encoder_bzip2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ bz_stream *strm;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for bzip2 stream");
+ return (ARCHIVE_FATAL);
+ }
+ /* bzlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+ strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+ strm->next_out = (char *)lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+ strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+ if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_bzip2;
+ lastrm->end = compression_end_bzip2;
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_code_bzip2(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ bz_stream *strm;
+ int r;
+
+ strm = (bz_stream *)lastrm->real_stream;
+ /* bzlib.h is not const-correct, so we need this one bit
+ * of ugly hackery to convert a const * pointer to
+ * a non-const pointer. */
+ strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+ strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+ strm->next_out = (char *)lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+ strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+ r = BZ2_bzCompress(strm,
+ (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);
+ lastrm->next_in = (const unsigned char *)strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in =
+ (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)
+ + (uint64_t)(uint32_t)strm->total_in_lo32;
+ lastrm->next_out = (unsigned char *)strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out =
+ (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)
+ + (uint64_t)(uint32_t)strm->total_out_lo32;
+ switch (r) {
+ case BZ_RUN_OK: /* Non-finishing */
+ case BZ_FINISH_OK: /* Finishing: There's more work to do */
+ return (ARCHIVE_OK);
+ case BZ_STREAM_END: /* Finishing: all done */
+ /* Only occurs in finishing case */
+ return (ARCHIVE_EOF);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Bzip2 compression failed:"
+ " BZ2_bzCompress() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)
+{
+ bz_stream *strm;
+ int r;
+
+ strm = (bz_stream *)lastrm->real_stream;
+ r = BZ2_bzCompressEnd(strm);
+ free(strm);
+ lastrm->real_stream = NULL;
+ lastrm->valid = 0;
+ if (r != BZ_OK) {
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Failed to clean up compressor");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+#else
+static int
+compression_init_encoder_bzip2(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "bzip2"));
+}
+#endif
+
+#if defined(HAVE_LZMA_H)
+static int
+compression_init_encoder_lzma(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+ static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+ lzma_stream *strm;
+ lzma_options_lzma lzma_opt;
+ int r;
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ if (lzma_lzma_preset(&lzma_opt, level)) {
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ strm = calloc(1, sizeof(*strm));
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for lzma stream");
+ return (ARCHIVE_FATAL);
+ }
+ *strm = lzma_init_data;
+ r = lzma_alone_encoder(strm, &lzma_opt);
+ switch (r) {
+ case LZMA_OK:
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_lzma;
+ lastrm->end = compression_end_lzma;
+ r = ARCHIVE_OK;
+ break;
+ case LZMA_MEM_ERROR:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library: "
+ "Cannot allocate memory");
+ r = ARCHIVE_FATAL;
+ break;
+ default:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "It's a bug in liblzma");
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ return (r);
+}
+
+static int
+compression_init_encoder_xz(struct archive *a,
+ struct la_zstream *lastrm, int level, int threads)
+{
+ static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+ lzma_stream *strm;
+ lzma_filter *lzmafilters;
+ lzma_options_lzma lzma_opt;
+ int r;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ lzma_mt mt_options;
+#endif
+
+ (void)threads; /* UNUSED (if multi-threaded LZMA library not avail) */
+
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);
+ if (strm == NULL) {
+ archive_set_error(a, ENOMEM,
+ "Can't allocate memory for xz stream");
+ return (ARCHIVE_FATAL);
+ }
+ lzmafilters = (lzma_filter *)(strm+1);
+ if (level > 9)
+ level = 9;
+ if (lzma_lzma_preset(&lzma_opt, level)) {
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library");
+ return (ARCHIVE_FATAL);
+ }
+ lzmafilters[0].id = LZMA_FILTER_LZMA2;
+ lzmafilters[0].options = &lzma_opt;
+ lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+
+ *strm = lzma_init_data;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+ if (threads > 1) {
+ memset(&mt_options, 0, sizeof(mt_options));
+ mt_options.threads = threads;
+ mt_options.timeout = 300;
+ mt_options.filters = lzmafilters;
+ mt_options.check = LZMA_CHECK_CRC64;
+ r = lzma_stream_encoder_mt(strm, &mt_options);
+ } else
+#endif
+ r = lzma_stream_encoder(strm, lzmafilters, LZMA_CHECK_CRC64);
+ switch (r) {
+ case LZMA_OK:
+ lastrm->real_stream = strm;
+ lastrm->valid = 1;
+ lastrm->code = compression_code_lzma;
+ lastrm->end = compression_end_lzma;
+ r = ARCHIVE_OK;
+ break;
+ case LZMA_MEM_ERROR:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ENOMEM,
+ "Internal error initializing compression library: "
+ "Cannot allocate memory");
+ r = ARCHIVE_FATAL;
+ break;
+ default:
+ free(strm);
+ lastrm->real_stream = NULL;
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing compression library: "
+ "It's a bug in liblzma");
+ r = ARCHIVE_FATAL;
+ break;
+ }
+ return (r);
+}
+
+static int
+compression_code_lzma(struct archive *a,
+ struct la_zstream *lastrm, enum la_zaction action)
+{
+ lzma_stream *strm;
+ int r;
+
+ strm = (lzma_stream *)lastrm->real_stream;
+ strm->next_in = lastrm->next_in;
+ strm->avail_in = lastrm->avail_in;
+ strm->total_in = lastrm->total_in;
+ strm->next_out = lastrm->next_out;
+ strm->avail_out = lastrm->avail_out;
+ strm->total_out = lastrm->total_out;
+ r = lzma_code(strm,
+ (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);
+ lastrm->next_in = strm->next_in;
+ lastrm->avail_in = strm->avail_in;
+ lastrm->total_in = strm->total_in;
+ lastrm->next_out = strm->next_out;
+ lastrm->avail_out = strm->avail_out;
+ lastrm->total_out = strm->total_out;
+ switch (r) {
+ case LZMA_OK:
+ /* Non-finishing case */
+ return (ARCHIVE_OK);
+ case LZMA_STREAM_END:
+ /* This return can only occur in finishing case. */
+ return (ARCHIVE_EOF);
+ case LZMA_MEMLIMIT_ERROR:
+ archive_set_error(a, ENOMEM,
+ "lzma compression error:"
+ " %ju MiB would have been needed",
+ (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)
+ / (1024 * 1024)));
+ return (ARCHIVE_FATAL);
+ default:
+ /* Any other return value indicates an error */
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "lzma compression failed:"
+ " lzma_code() call returned status %d", r);
+ return (ARCHIVE_FATAL);
+ }
+}
+
+static int
+compression_end_lzma(struct archive *a, struct la_zstream *lastrm)
+{
+ lzma_stream *strm;
+
+ (void)a; /* UNUSED */
+ strm = (lzma_stream *)lastrm->real_stream;
+ lzma_end(strm);
+ free(strm);
+ lastrm->valid = 0;
+ lastrm->real_stream = NULL;
+ return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_lzma(struct archive *a,
+ struct la_zstream *lastrm, int level)
+{
+
+ (void) level; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+static int
+compression_init_encoder_xz(struct archive *a,
+ struct la_zstream *lastrm, int level, int threads)
+{
+
+ (void) level; /* UNUSED */
+ (void) threads; /* UNUSED */
+ if (lastrm->valid)
+ compression_end(a, lastrm);
+ return (compression_unsupported_encoder(a, lastrm, "xz"));
+}
+#endif
+
+static int
+xar_compression_init_encoder(struct archive_write *a)
+{
+ struct xar *xar;
+ int r;
+
+ xar = (struct xar *)a->format_data;
+ switch (xar->opt_compression) {
+ case GZIP:
+ r = compression_init_encoder_gzip(
+ &(a->archive), &(xar->stream),
+ xar->opt_compression_level, 1);
+ break;
+ case BZIP2:
+ r = compression_init_encoder_bzip2(
+ &(a->archive), &(xar->stream),
+ xar->opt_compression_level);
+ break;
+ case LZMA:
+ r = compression_init_encoder_lzma(
+ &(a->archive), &(xar->stream),
+ xar->opt_compression_level);
+ break;
+ case XZ:
+ r = compression_init_encoder_xz(
+ &(a->archive), &(xar->stream),
+ xar->opt_compression_level, xar->opt_threads);
+ break;
+ default:
+ r = ARCHIVE_OK;
+ break;
+ }
+ if (r == ARCHIVE_OK) {
+ xar->stream.total_in = 0;
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out = sizeof(xar->wbuff);
+ xar->stream.total_out = 0;
+ }
+
+ return (r);
+}
+
+static int
+compression_code(struct archive *a, struct la_zstream *lastrm,
+ enum la_zaction action)
+{
+ if (lastrm->valid)
+ return (lastrm->code(a, lastrm, action));
+ return (ARCHIVE_OK);
+}
+
+static int
+compression_end(struct archive *a, struct la_zstream *lastrm)
+{
+ if (lastrm->valid)
+ return (lastrm->end(a, lastrm));
+ return (ARCHIVE_OK);
+}
+
+
+static int
+save_xattrs(struct archive_write *a, struct file *file)
+{
+ struct xar *xar;
+ const char *name;
+ const void *value;
+ struct heap_data *heap;
+ size_t size;
+ int count, r;
+
+ xar = (struct xar *)a->format_data;
+ count = archive_entry_xattr_reset(file->entry);
+ if (count == 0)
+ return (ARCHIVE_OK);
+ while (count--) {
+ archive_entry_xattr_next(file->entry,
+ &name, &value, &size);
+ checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
+ checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
+
+ heap = calloc(1, sizeof(*heap));
+ if (heap == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for xattr");
+ return (ARCHIVE_FATAL);
+ }
+ heap->id = file->ea_idx++;
+ heap->temp_offset = xar->temp_offset;
+ heap->size = size;/* save a extracted size */
+ heap->compression = xar->opt_compression;
+ /* Get a extracted sumcheck value. */
+ checksum_update(&(xar->e_sumwrk), value, size);
+ checksum_final(&(xar->e_sumwrk), &(heap->e_sum));
+
+ /*
+ * Not compression to xattr is simple way.
+ */
+ if (heap->compression == NONE) {
+ checksum_update(&(xar->a_sumwrk), value, size);
+ checksum_final(&(xar->a_sumwrk), &(heap->a_sum));
+ if (write_to_temp(a, value, size)
+ != ARCHIVE_OK) {
+ free(heap);
+ return (ARCHIVE_FATAL);
+ }
+ heap->length = size;
+ /* Add heap to the tail of file->xattr. */
+ heap->next = NULL;
+ *file->xattr.last = heap;
+ file->xattr.last = &(heap->next);
+ /* Next xattr */
+ continue;
+ }
+
+ /*
+ * Init compression library.
+ */
+ r = xar_compression_init_encoder(a);
+ if (r != ARCHIVE_OK) {
+ free(heap);
+ return (ARCHIVE_FATAL);
+ }
+
+ xar->stream.next_in = (const unsigned char *)value;
+ xar->stream.avail_in = size;
+ for (;;) {
+ r = compression_code(&(a->archive),
+ &(xar->stream), ARCHIVE_Z_FINISH);
+ if (r != ARCHIVE_OK && r != ARCHIVE_EOF) {
+ free(heap);
+ return (ARCHIVE_FATAL);
+ }
+ size = sizeof(xar->wbuff) - xar->stream.avail_out;
+ checksum_update(&(xar->a_sumwrk),
+ xar->wbuff, size);
+ if (write_to_temp(a, xar->wbuff, size)
+ != ARCHIVE_OK) {
+ free(heap);
+ return (ARCHIVE_FATAL);
+ }
+ if (r == ARCHIVE_OK) {
+ xar->stream.next_out = xar->wbuff;
+ xar->stream.avail_out = sizeof(xar->wbuff);
+ } else {
+ checksum_final(&(xar->a_sumwrk),
+ &(heap->a_sum));
+ heap->length = xar->stream.total_out;
+ /* Add heap to the tail of file->xattr. */
+ heap->next = NULL;
+ *file->xattr.last = heap;
+ file->xattr.last = &(heap->next);
+ break;
+ }
+ }
+ /* Clean up compression library. */
+ r = compression_end(&(a->archive), &(xar->stream));
+ if (r != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+getalgsize(enum sumalg sumalg)
+{
+ switch (sumalg) {
+ default:
+ case CKSUM_NONE:
+ return (0);
+ case CKSUM_SHA1:
+ return (SHA1_SIZE);
+ case CKSUM_MD5:
+ return (MD5_SIZE);
+ }
+}
+
+static const char *
+getalgname(enum sumalg sumalg)
+{
+ switch (sumalg) {
+ default:
+ case CKSUM_NONE:
+ return (NULL);
+ case CKSUM_SHA1:
+ return (SHA1_NAME);
+ case CKSUM_MD5:
+ return (MD5_NAME);
+ }
+}
+
+#endif /* Support xar format */
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_format_zip.c b/contrib/libs/libarchive/libarchive/archive_write_set_format_zip.c
new file mode 100644
index 0000000000..5ac7e03d38
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_format_zip.c
@@ -0,0 +1,1693 @@
+/*-
+ * Copyright (c) 2008 Anselm Strauss
+ * Copyright (c) 2009 Joerg Sonnenberger
+ * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+/*
+ * Development supported by Google Summer of Code 2008.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_cryptor_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_hmac_private.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+#ifndef HAVE_ZLIB_H
+#error #include "archive_crc32.h"
+#endif
+
+#define ZIP_ENTRY_FLAG_ENCRYPTED (1<<0)
+#define ZIP_ENTRY_FLAG_LENGTH_AT_END (1<<3)
+#define ZIP_ENTRY_FLAG_UTF8_NAME (1 << 11)
+
+#define ZIP_4GB_MAX ARCHIVE_LITERAL_LL(0xffffffff)
+#define ZIP_4GB_MAX_UNCOMPRESSED ARCHIVE_LITERAL_LL(0xff000000)
+
+enum compression {
+ COMPRESSION_UNSPECIFIED = -1,
+ COMPRESSION_STORE = 0,
+ COMPRESSION_DEFLATE = 8
+};
+
+#ifdef HAVE_ZLIB_H
+#define COMPRESSION_DEFAULT COMPRESSION_DEFLATE
+#else
+#define COMPRESSION_DEFAULT COMPRESSION_STORE
+#endif
+
+enum encryption {
+ ENCRYPTION_NONE = 0,
+ ENCRYPTION_TRADITIONAL, /* Traditional PKWARE encryption. */
+ ENCRYPTION_WINZIP_AES128, /* WinZIP AES-128 encryption. */
+ ENCRYPTION_WINZIP_AES256, /* WinZIP AES-256 encryption. */
+};
+
+#define TRAD_HEADER_SIZE 12
+/*
+ * See "WinZip - AES Encryption Information"
+ * http://www.winzip.com/aes_info.htm
+ */
+/* Value used in compression method. */
+#define WINZIP_AES_ENCRYPTION 99
+/* A WinZip AES header size which is stored at the beginning of
+ * file contents. */
+#define WINZIP_AES128_HEADER_SIZE (8 + 2)
+#define WINZIP_AES256_HEADER_SIZE (16 + 2)
+/* AES vendor version. */
+#define AES_VENDOR_AE_1 0x0001
+#define AES_VENDOR_AE_2 0x0002
+/* Authentication code size. */
+#define AUTH_CODE_SIZE 10
+/**/
+#define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2)
+
+struct cd_segment {
+ struct cd_segment *next;
+ size_t buff_size;
+ unsigned char *buff;
+ unsigned char *p;
+};
+
+struct trad_enc_ctx {
+ uint32_t keys[3];
+};
+
+struct zip {
+
+ int64_t entry_offset;
+ int64_t entry_compressed_size;
+ int64_t entry_uncompressed_size;
+ int64_t entry_compressed_written;
+ int64_t entry_uncompressed_written;
+ int64_t entry_uncompressed_limit;
+ struct archive_entry *entry;
+ uint32_t entry_crc32;
+ enum compression entry_compression;
+ enum encryption entry_encryption;
+ int entry_flags;
+ int entry_uses_zip64;
+ int experiments;
+ struct trad_enc_ctx tctx;
+ char tctx_valid;
+ unsigned char trad_chkdat;
+ unsigned aes_vendor;
+ archive_crypto_ctx cctx;
+ char cctx_valid;
+ archive_hmac_sha1_ctx hctx;
+ char hctx_valid;
+
+ unsigned char *file_header;
+ size_t file_header_extra_offset;
+ unsigned long (*crc32func)(unsigned long crc, const void *buff, size_t len);
+
+ struct cd_segment *central_directory;
+ struct cd_segment *central_directory_last;
+ size_t central_directory_bytes;
+ size_t central_directory_entries;
+
+ int64_t written_bytes; /* Overall position in file. */
+
+ struct archive_string_conv *opt_sconv;
+ struct archive_string_conv *sconv_default;
+ enum compression requested_compression;
+ int deflate_compression_level;
+ int init_default_conversion;
+ enum encryption encryption_type;
+
+#define ZIP_FLAG_AVOID_ZIP64 1
+#define ZIP_FLAG_FORCE_ZIP64 2
+#define ZIP_FLAG_EXPERIMENT_xl 4
+ int flags;
+
+#ifdef HAVE_ZLIB_H
+ z_stream stream;
+#endif
+ size_t len_buf;
+ unsigned char *buf;
+};
+
+/* Don't call this min or MIN, since those are already defined
+ on lots of platforms (but not all). */
+#define zipmin(a, b) ((a) > (b) ? (b) : (a))
+
+static ssize_t archive_write_zip_data(struct archive_write *,
+ const void *buff, size_t s);
+static int archive_write_zip_close(struct archive_write *);
+static int archive_write_zip_free(struct archive_write *);
+static int archive_write_zip_finish_entry(struct archive_write *);
+static int archive_write_zip_header(struct archive_write *,
+ struct archive_entry *);
+static int archive_write_zip_options(struct archive_write *,
+ const char *, const char *);
+static unsigned int dos_time(const time_t);
+static size_t path_length(struct archive_entry *);
+static int write_path(struct archive_entry *, struct archive_write *);
+static void copy_path(struct archive_entry *, unsigned char *);
+static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *);
+static int trad_enc_init(struct trad_enc_ctx *, const char *, size_t);
+static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *, const uint8_t *,
+ size_t, uint8_t *, size_t);
+static int init_traditional_pkware_encryption(struct archive_write *);
+static int is_traditional_pkware_encryption_supported(void);
+static int init_winzip_aes_encryption(struct archive_write *);
+static int is_winzip_aes_encryption_supported(int encryption);
+
+static unsigned char *
+cd_alloc(struct zip *zip, size_t length)
+{
+ unsigned char *p;
+
+ if (zip->central_directory == NULL
+ || (zip->central_directory_last->p + length
+ > zip->central_directory_last->buff + zip->central_directory_last->buff_size)) {
+ struct cd_segment *segment = calloc(1, sizeof(*segment));
+ if (segment == NULL)
+ return NULL;
+ segment->buff_size = 64 * 1024;
+ segment->buff = malloc(segment->buff_size);
+ if (segment->buff == NULL) {
+ free(segment);
+ return NULL;
+ }
+ segment->p = segment->buff;
+
+ if (zip->central_directory == NULL) {
+ zip->central_directory
+ = zip->central_directory_last
+ = segment;
+ } else {
+ zip->central_directory_last->next = segment;
+ zip->central_directory_last = segment;
+ }
+ }
+
+ p = zip->central_directory_last->p;
+ zip->central_directory_last->p += length;
+ zip->central_directory_bytes += length;
+ return (p);
+}
+
+static unsigned long
+real_crc32(unsigned long crc, const void *buff, size_t len)
+{
+ return crc32(crc, buff, (unsigned int)len);
+}
+
+static unsigned long
+fake_crc32(unsigned long crc, const void *buff, size_t len)
+{
+ (void)crc; /* UNUSED */
+ (void)buff; /* UNUSED */
+ (void)len; /* UNUSED */
+ return 0;
+}
+
+static int
+archive_write_zip_options(struct archive_write *a, const char *key,
+ const char *val)
+{
+ struct zip *zip = a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ if (strcmp(key, "compression") == 0) {
+ /*
+ * Set compression to use on all future entries.
+ * This only affects regular files.
+ */
+ if (val == NULL || val[0] == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: compression option needs a compression name",
+ a->format_name);
+ } else if (strcmp(val, "deflate") == 0) {
+#ifdef HAVE_ZLIB_H
+ zip->requested_compression = COMPRESSION_DEFLATE;
+ ret = ARCHIVE_OK;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "deflate compression not supported");
+#endif
+ } else if (strcmp(val, "store") == 0) {
+ zip->requested_compression = COMPRESSION_STORE;
+ ret = ARCHIVE_OK;
+ }
+ return (ret);
+ } else if (strcmp(key, "compression-level") == 0) {
+ if (val == NULL || !(val[0] >= '0' && val[0] <= '9') || val[1] != '\0') {
+ return ARCHIVE_WARN;
+ }
+
+ if (val[0] == '0') {
+ zip->requested_compression = COMPRESSION_STORE;
+ return ARCHIVE_OK;
+ } else {
+#ifdef HAVE_ZLIB_H
+ zip->requested_compression = COMPRESSION_DEFLATE;
+ zip->deflate_compression_level = val[0] - '0';
+ return ARCHIVE_OK;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "deflate compression not supported");
+#endif
+ }
+ } else if (strcmp(key, "encryption") == 0) {
+ if (val == NULL) {
+ zip->encryption_type = ENCRYPTION_NONE;
+ ret = ARCHIVE_OK;
+ } else if (val[0] == '1' || strcmp(val, "traditional") == 0
+ || strcmp(val, "zipcrypt") == 0
+ || strcmp(val, "ZipCrypt") == 0) {
+ if (is_traditional_pkware_encryption_supported()) {
+ zip->encryption_type = ENCRYPTION_TRADITIONAL;
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "encryption not supported");
+ }
+ } else if (strcmp(val, "aes128") == 0) {
+ if (is_winzip_aes_encryption_supported(
+ ENCRYPTION_WINZIP_AES128)) {
+ zip->encryption_type = ENCRYPTION_WINZIP_AES128;
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "encryption not supported");
+ }
+ } else if (strcmp(val, "aes256") == 0) {
+ if (is_winzip_aes_encryption_supported(
+ ENCRYPTION_WINZIP_AES256)) {
+ zip->encryption_type = ENCRYPTION_WINZIP_AES256;
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "encryption not supported");
+ }
+ } else {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: unknown encryption '%s'",
+ a->format_name, val);
+ }
+ return (ret);
+ } else if (strcmp(key, "experimental") == 0) {
+ if (val == NULL || val[0] == 0) {
+ zip->flags &= ~ ZIP_FLAG_EXPERIMENT_xl;
+ } else {
+ zip->flags |= ZIP_FLAG_EXPERIMENT_xl;
+ }
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "fakecrc32") == 0) {
+ /*
+ * FOR TESTING ONLY: disable CRC calculation to speed up
+ * certain complex tests.
+ */
+ if (val == NULL || val[0] == 0) {
+ zip->crc32func = real_crc32;
+ } else {
+ zip->crc32func = fake_crc32;
+ }
+ return (ARCHIVE_OK);
+ } else if (strcmp(key, "hdrcharset") == 0) {
+ /*
+ * Set the character set used in translating filenames.
+ */
+ if (val == NULL || val[0] == 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "%s: hdrcharset option needs a character-set name",
+ a->format_name);
+ } else {
+ zip->opt_sconv = archive_string_conversion_to_charset(
+ &a->archive, val, 0);
+ if (zip->opt_sconv != NULL)
+ ret = ARCHIVE_OK;
+ else
+ ret = ARCHIVE_FATAL;
+ }
+ return (ret);
+ } else if (strcmp(key, "zip64") == 0) {
+ /*
+ * Bias decisions about Zip64: force them to be
+ * generated in certain cases where they are not
+ * forbidden or avoid them in certain cases where they
+ * are not strictly required.
+ */
+ if (val != NULL && *val != '\0') {
+ zip->flags |= ZIP_FLAG_FORCE_ZIP64;
+ zip->flags &= ~ZIP_FLAG_AVOID_ZIP64;
+ } else {
+ zip->flags &= ~ZIP_FLAG_FORCE_ZIP64;
+ zip->flags |= ZIP_FLAG_AVOID_ZIP64;
+ }
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+int
+archive_write_zip_set_compression_deflate(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ int ret = ARCHIVE_FAILED;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_zip_set_compression_deflate");
+ if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can only use archive_write_zip_set_compression_deflate"
+ " with zip format");
+ ret = ARCHIVE_FATAL;
+ } else {
+#ifdef HAVE_ZLIB_H
+ struct zip *zip = a->format_data;
+ zip->requested_compression = COMPRESSION_DEFLATE;
+ ret = ARCHIVE_OK;
+#else
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "deflate compression not supported");
+ ret = ARCHIVE_FAILED;
+#endif
+ }
+ return (ret);
+}
+
+int
+archive_write_zip_set_compression_store(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct zip *zip = a->format_data;
+ int ret = ARCHIVE_FAILED;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+ "archive_write_zip_set_compression_deflate");
+ if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can only use archive_write_zip_set_compression_store"
+ " with zip format");
+ ret = ARCHIVE_FATAL;
+ } else {
+ zip->requested_compression = COMPRESSION_STORE;
+ ret = ARCHIVE_OK;
+ }
+ return (ret);
+}
+
+int
+archive_write_set_format_zip(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct zip *zip;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_set_format_zip");
+
+ /* If another format was already registered, unregister it. */
+ if (a->format_free != NULL)
+ (a->format_free)(a);
+
+ zip = (struct zip *) calloc(1, sizeof(*zip));
+ if (zip == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip data");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* "Unspecified" lets us choose the appropriate compression. */
+ zip->requested_compression = COMPRESSION_UNSPECIFIED;
+#ifdef HAVE_ZLIB_H
+ zip->deflate_compression_level = Z_DEFAULT_COMPRESSION;
+#endif
+ zip->crc32func = real_crc32;
+
+ /* A buffer used for both compression and encryption. */
+ zip->len_buf = 65536;
+ zip->buf = malloc(zip->len_buf);
+ if (zip->buf == NULL) {
+ free(zip);
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+
+ a->format_data = zip;
+ a->format_name = "zip";
+ a->format_options = archive_write_zip_options;
+ a->format_write_header = archive_write_zip_header;
+ a->format_write_data = archive_write_zip_data;
+ a->format_finish_entry = archive_write_zip_finish_entry;
+ a->format_close = archive_write_zip_close;
+ a->format_free = archive_write_zip_free;
+ a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+ a->archive.archive_format_name = "ZIP";
+
+ return (ARCHIVE_OK);
+}
+
+static int
+is_all_ascii(const char *p)
+{
+ const unsigned char *pp = (const unsigned char *)p;
+
+ while (*pp) {
+ if (*pp++ > 127)
+ return (0);
+ }
+ return (1);
+}
+
+static int
+archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
+{
+ unsigned char local_header[32];
+ unsigned char local_extra[144];
+ struct zip *zip = a->format_data;
+ unsigned char *e;
+ unsigned char *cd_extra;
+ size_t filename_length;
+ const char *slink = NULL;
+ size_t slink_size = 0;
+ struct archive_string_conv *sconv = get_sconv(a, zip);
+ int ret, ret2 = ARCHIVE_OK;
+ mode_t type;
+ int version_needed = 10;
+
+ /* Ignore types of entries that we don't support. */
+ type = archive_entry_filetype(entry);
+ if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {
+ __archive_write_entry_filetype_unsupported(
+ &a->archive, entry, "zip");
+ return ARCHIVE_FAILED;
+ };
+
+ /* If we're not using Zip64, reject large files. */
+ if (zip->flags & ZIP_FLAG_AVOID_ZIP64) {
+ /* Reject entries over 4GB. */
+ if (archive_entry_size_is_set(entry)
+ && (archive_entry_size(entry) > ZIP_4GB_MAX)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Files > 4GB require Zip64 extensions");
+ return ARCHIVE_FAILED;
+ }
+ /* Reject entries if archive is > 4GB. */
+ if (zip->written_bytes > ZIP_4GB_MAX) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Archives > 4GB require Zip64 extensions");
+ return ARCHIVE_FAILED;
+ }
+ }
+
+ /* Only regular files can have size > 0. */
+ if (type != AE_IFREG)
+ archive_entry_set_size(entry, 0);
+
+
+ /* Reset information from last entry. */
+ zip->entry_offset = zip->written_bytes;
+ zip->entry_uncompressed_limit = INT64_MAX;
+ zip->entry_compressed_size = 0;
+ zip->entry_uncompressed_size = 0;
+ zip->entry_compressed_written = 0;
+ zip->entry_uncompressed_written = 0;
+ zip->entry_flags = 0;
+ zip->entry_uses_zip64 = 0;
+ zip->entry_crc32 = zip->crc32func(0, NULL, 0);
+ zip->entry_encryption = 0;
+ archive_entry_free(zip->entry);
+ zip->entry = NULL;
+
+ if (zip->cctx_valid)
+ archive_encrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+
+ if (type == AE_IFREG
+ &&(!archive_entry_size_is_set(entry)
+ || archive_entry_size(entry) > 0)) {
+ switch (zip->encryption_type) {
+ case ENCRYPTION_TRADITIONAL:
+ case ENCRYPTION_WINZIP_AES128:
+ case ENCRYPTION_WINZIP_AES256:
+ zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED;
+ zip->entry_encryption = zip->encryption_type;
+ break;
+ case ENCRYPTION_NONE:
+ default:
+ break;
+ }
+ }
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Make sure the path separators in pathname, hardlink and symlink
+ * are all slash '/', not the Windows path separator '\'. */
+ zip->entry = __la_win_entry_in_posix_pathseparator(entry);
+ if (zip->entry == entry)
+ zip->entry = archive_entry_clone(entry);
+#else
+ zip->entry = archive_entry_clone(entry);
+#endif
+ if (zip->entry == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip header data");
+ return (ARCHIVE_FATAL);
+ }
+
+ if (sconv != NULL) {
+ const char *p;
+ size_t len;
+
+ if (archive_entry_pathname_l(entry, &p, &len, sconv) != 0) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Pathname");
+ return (ARCHIVE_FATAL);
+ }
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_FILE_FORMAT,
+ "Can't translate Pathname '%s' to %s",
+ archive_entry_pathname(entry),
+ archive_string_conversion_charset_name(sconv));
+ ret2 = ARCHIVE_WARN;
+ }
+ if (len > 0)
+ archive_entry_set_pathname(zip->entry, p);
+
+ /*
+ * There is no standard for symlink handling; we convert
+ * it using the same character-set translation that we use
+ * for filename.
+ */
+ if (type == AE_IFLNK) {
+ if (archive_entry_symlink_l(entry, &p, &len, sconv)) {
+ if (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory "
+ " for Symlink");
+ return (ARCHIVE_FATAL);
+ }
+ /* No error if we can't convert. */
+ } else if (len > 0)
+ archive_entry_set_symlink(zip->entry, p);
+ }
+ }
+
+ /* If filename isn't ASCII and we can use UTF-8, set the UTF-8 flag. */
+ if (!is_all_ascii(archive_entry_pathname(zip->entry))) {
+ if (zip->opt_sconv != NULL) {
+ if (strcmp(archive_string_conversion_charset_name(
+ zip->opt_sconv), "UTF-8") == 0)
+ zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME;
+#if HAVE_NL_LANGINFO
+ } else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
+ zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME;
+#endif
+ }
+ }
+ filename_length = path_length(zip->entry);
+
+ /* Determine appropriate compression and size for this entry. */
+ if (type == AE_IFLNK) {
+ slink = archive_entry_symlink(zip->entry);
+ if (slink != NULL)
+ slink_size = strlen(slink);
+ else
+ slink_size = 0;
+ zip->entry_uncompressed_limit = slink_size;
+ zip->entry_compressed_size = slink_size;
+ zip->entry_uncompressed_size = slink_size;
+ zip->entry_crc32 = zip->crc32func(zip->entry_crc32,
+ (const unsigned char *)slink, slink_size);
+ zip->entry_compression = COMPRESSION_STORE;
+ version_needed = 20;
+ } else if (type != AE_IFREG) {
+ zip->entry_compression = COMPRESSION_STORE;
+ zip->entry_uncompressed_limit = 0;
+ version_needed = 20;
+ } else if (archive_entry_size_is_set(zip->entry)) {
+ int64_t size = archive_entry_size(zip->entry);
+ int64_t additional_size = 0;
+
+ zip->entry_uncompressed_limit = size;
+ zip->entry_compression = zip->requested_compression;
+ if (zip->entry_compression == COMPRESSION_UNSPECIFIED) {
+ zip->entry_compression = COMPRESSION_DEFAULT;
+ }
+ if (zip->entry_compression == COMPRESSION_STORE) {
+ zip->entry_compressed_size = size;
+ zip->entry_uncompressed_size = size;
+ version_needed = 10;
+ } else {
+ zip->entry_uncompressed_size = size;
+ version_needed = 20;
+ }
+
+ if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+ switch (zip->entry_encryption) {
+ case ENCRYPTION_TRADITIONAL:
+ additional_size = TRAD_HEADER_SIZE;
+ version_needed = 20;
+ break;
+ case ENCRYPTION_WINZIP_AES128:
+ additional_size = WINZIP_AES128_HEADER_SIZE
+ + AUTH_CODE_SIZE;
+ version_needed = 20;
+ break;
+ case ENCRYPTION_WINZIP_AES256:
+ additional_size = WINZIP_AES256_HEADER_SIZE
+ + AUTH_CODE_SIZE;
+ version_needed = 20;
+ break;
+ case ENCRYPTION_NONE:
+ default:
+ break;
+ }
+ if (zip->entry_compression == COMPRESSION_STORE)
+ zip->entry_compressed_size += additional_size;
+ }
+
+ /*
+ * Set Zip64 extension in any of the following cases
+ * (this was suggested by discussion on info-zip-dev
+ * mailing list):
+ * = Zip64 is being forced by user
+ * = File is over 4GiB uncompressed
+ * (including encryption header, if any)
+ * = File is close to 4GiB and is being compressed
+ * (compression might make file larger)
+ */
+ if ((zip->flags & ZIP_FLAG_FORCE_ZIP64)
+ || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX)
+ || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED
+ && zip->entry_compression != COMPRESSION_STORE)) {
+ zip->entry_uses_zip64 = 1;
+ version_needed = 45;
+ }
+
+ /* We may know the size, but never the CRC. */
+ zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;
+ } else {
+ /* We don't know the size. Use the default
+ * compression unless specified otherwise.
+ * We enable Zip64 extensions unless we're told not to.
+ */
+
+ zip->entry_compression = zip->requested_compression;
+ if(zip->entry_compression == COMPRESSION_UNSPECIFIED){
+ zip->entry_compression = COMPRESSION_DEFAULT;
+ }
+
+ zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;
+ if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) {
+ zip->entry_uses_zip64 = 1;
+ version_needed = 45;
+ } else if (zip->entry_compression == COMPRESSION_STORE) {
+ version_needed = 10;
+ } else {
+ version_needed = 20;
+ }
+
+ if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+ switch (zip->entry_encryption) {
+ case ENCRYPTION_TRADITIONAL:
+ case ENCRYPTION_WINZIP_AES128:
+ case ENCRYPTION_WINZIP_AES256:
+ if (version_needed < 20)
+ version_needed = 20;
+ break;
+ case ENCRYPTION_NONE:
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Format the local header. */
+ memset(local_header, 0, sizeof(local_header));
+ memcpy(local_header, "PK\003\004", 4);
+ archive_le16enc(local_header + 4, version_needed);
+ archive_le16enc(local_header + 6, zip->entry_flags);
+ if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+ || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)
+ archive_le16enc(local_header + 8, WINZIP_AES_ENCRYPTION);
+ else
+ archive_le16enc(local_header + 8, zip->entry_compression);
+ archive_le32enc(local_header + 10,
+ dos_time(archive_entry_mtime(zip->entry)));
+ archive_le32enc(local_header + 14, zip->entry_crc32);
+ if (zip->entry_uses_zip64) {
+ /* Zip64 data in the local header "must" include both
+ * compressed and uncompressed sizes AND those fields
+ * are included only if these are 0xffffffff;
+ * THEREFORE these must be set this way, even if we
+ * know one of them is smaller. */
+ archive_le32enc(local_header + 18, ZIP_4GB_MAX);
+ archive_le32enc(local_header + 22, ZIP_4GB_MAX);
+ } else {
+ archive_le32enc(local_header + 18, (uint32_t)zip->entry_compressed_size);
+ archive_le32enc(local_header + 22, (uint32_t)zip->entry_uncompressed_size);
+ }
+ archive_le16enc(local_header + 26, (uint16_t)filename_length);
+
+ if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) {
+ if (zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END)
+ zip->trad_chkdat = local_header[11];
+ else
+ zip->trad_chkdat = local_header[17];
+ }
+
+ /* Format as much of central directory file header as we can: */
+ zip->file_header = cd_alloc(zip, 46);
+ /* If (zip->file_header == NULL) XXXX */
+ ++zip->central_directory_entries;
+ memset(zip->file_header, 0, 46);
+ memcpy(zip->file_header, "PK\001\002", 4);
+ /* "Made by PKZip 2.0 on Unix." */
+ archive_le16enc(zip->file_header + 4, 3 * 256 + version_needed);
+ archive_le16enc(zip->file_header + 6, version_needed);
+ archive_le16enc(zip->file_header + 8, zip->entry_flags);
+ if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+ || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)
+ archive_le16enc(zip->file_header + 10, WINZIP_AES_ENCRYPTION);
+ else
+ archive_le16enc(zip->file_header + 10, zip->entry_compression);
+ archive_le32enc(zip->file_header + 12,
+ dos_time(archive_entry_mtime(zip->entry)));
+ archive_le16enc(zip->file_header + 28, (uint16_t)filename_length);
+ /* Following Info-Zip, store mode in the "external attributes" field. */
+ archive_le32enc(zip->file_header + 38,
+ ((uint32_t)archive_entry_mode(zip->entry)) << 16);
+ e = cd_alloc(zip, filename_length);
+ /* If (e == NULL) XXXX */
+ copy_path(zip->entry, e);
+
+ /* Format extra data. */
+ memset(local_extra, 0, sizeof(local_extra));
+ e = local_extra;
+
+ /* First, extra blocks that are the same between
+ * the local file header and the central directory.
+ * We format them once and then duplicate them. */
+
+ /* UT timestamp, length depends on what timestamps are set. */
+ memcpy(e, "UT", 2);
+ archive_le16enc(e + 2,
+ 1
+ + (archive_entry_mtime_is_set(entry) ? 4 : 0)
+ + (archive_entry_atime_is_set(entry) ? 4 : 0)
+ + (archive_entry_ctime_is_set(entry) ? 4 : 0));
+ e += 4;
+ *e++ =
+ (archive_entry_mtime_is_set(entry) ? 1 : 0)
+ | (archive_entry_atime_is_set(entry) ? 2 : 0)
+ | (archive_entry_ctime_is_set(entry) ? 4 : 0);
+ if (archive_entry_mtime_is_set(entry)) {
+ archive_le32enc(e, (uint32_t)archive_entry_mtime(entry));
+ e += 4;
+ }
+ if (archive_entry_atime_is_set(entry)) {
+ archive_le32enc(e, (uint32_t)archive_entry_atime(entry));
+ e += 4;
+ }
+ if (archive_entry_ctime_is_set(entry)) {
+ archive_le32enc(e, (uint32_t)archive_entry_ctime(entry));
+ e += 4;
+ }
+
+ /* ux Unix extra data, length 11, version 1 */
+ /* TODO: If uid < 64k, use 2 bytes, ditto for gid. */
+ memcpy(e, "ux\013\000\001", 5);
+ e += 5;
+ *e++ = 4; /* Length of following UID */
+ archive_le32enc(e, (uint32_t)archive_entry_uid(entry));
+ e += 4;
+ *e++ = 4; /* Length of following GID */
+ archive_le32enc(e, (uint32_t)archive_entry_gid(entry));
+ e += 4;
+
+ /* AES extra data field: WinZIP AES information, ID=0x9901 */
+ if ((zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED)
+ && (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+ || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)) {
+
+ memcpy(e, "\001\231\007\000\001\000AE", 8);
+ /* AES vendor version AE-2 does not store a CRC.
+ * WinZip 11 uses AE-1, which does store the CRC,
+ * but it does not store the CRC when the file size
+ * is less than 20 bytes. So we simulate what
+ * WinZip 11 does.
+ * NOTE: WinZip 9.0 and 10.0 uses AE-2 by default. */
+ if (archive_entry_size_is_set(zip->entry)
+ && archive_entry_size(zip->entry) < 20) {
+ archive_le16enc(e+4, AES_VENDOR_AE_2);
+ zip->aes_vendor = AES_VENDOR_AE_2;/* no CRC. */
+ } else
+ zip->aes_vendor = AES_VENDOR_AE_1;
+ e += 8;
+ /* AES encryption strength. */
+ *e++ = (zip->entry_encryption == ENCRYPTION_WINZIP_AES128)?1:3;
+ /* Actual compression method. */
+ archive_le16enc(e, zip->entry_compression);
+ e += 2;
+ }
+
+ /* Copy UT ,ux, and AES-extra into central directory as well. */
+ zip->file_header_extra_offset = zip->central_directory_bytes;
+ cd_extra = cd_alloc(zip, e - local_extra);
+ memcpy(cd_extra, local_extra, e - local_extra);
+
+ /*
+ * Following extra blocks vary between local header and
+ * central directory. These are the local header versions.
+ * Central directory versions get formatted in
+ * archive_write_zip_finish_entry() below.
+ */
+
+ /* "[Zip64 entry] in the local header MUST include BOTH
+ * original [uncompressed] and compressed size fields." */
+ if (zip->entry_uses_zip64) {
+ unsigned char *zip64_start = e;
+ memcpy(e, "\001\000\020\000", 4);
+ e += 4;
+ archive_le64enc(e, zip->entry_uncompressed_size);
+ e += 8;
+ archive_le64enc(e, zip->entry_compressed_size);
+ e += 8;
+ archive_le16enc(zip64_start + 2, (uint16_t)(e - (zip64_start + 4)));
+ }
+
+ if (zip->flags & ZIP_FLAG_EXPERIMENT_xl) {
+ /* Experimental 'xl' extension to improve streaming. */
+ unsigned char *external_info = e;
+ int included = 7;
+ memcpy(e, "xl\000\000", 4); // 0x6c65 + 2-byte length
+ e += 4;
+ e[0] = included; /* bitmap of included fields */
+ e += 1;
+ if (included & 1) {
+ archive_le16enc(e, /* "Version created by" */
+ 3 * 256 + version_needed);
+ e += 2;
+ }
+ if (included & 2) {
+ archive_le16enc(e, 0); /* internal file attributes */
+ e += 2;
+ }
+ if (included & 4) {
+ archive_le32enc(e, /* external file attributes */
+ ((uint32_t)archive_entry_mode(zip->entry)) << 16);
+ e += 4;
+ }
+ if (included & 8) {
+ // Libarchive does not currently support file comments.
+ }
+ archive_le16enc(external_info + 2, (uint16_t)(e - (external_info + 4)));
+ }
+
+ /* Update local header with size of extra data and write it all out: */
+ archive_le16enc(local_header + 28, (uint16_t)(e - local_extra));
+
+ ret = __archive_write_output(a, local_header, 30);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += 30;
+
+ ret = write_path(zip->entry, a);
+ if (ret <= ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += ret;
+
+ ret = __archive_write_output(a, local_extra, e - local_extra);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += e - local_extra;
+
+ /* For symlinks, write the body now. */
+ if (slink != NULL) {
+ ret = __archive_write_output(a, slink, slink_size);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->entry_compressed_written += slink_size;
+ zip->entry_uncompressed_written += slink_size;
+ zip->written_bytes += slink_size;
+ }
+
+#ifdef HAVE_ZLIB_H
+ if (zip->entry_compression == COMPRESSION_DEFLATE) {
+ zip->stream.zalloc = Z_NULL;
+ zip->stream.zfree = Z_NULL;
+ zip->stream.opaque = Z_NULL;
+ zip->stream.next_out = zip->buf;
+ zip->stream.avail_out = (uInt)zip->len_buf;
+ if (deflateInit2(&zip->stream, zip->deflate_compression_level,
+ Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't init deflate compressor");
+ return (ARCHIVE_FATAL);
+ }
+ }
+#endif
+
+ return (ret2);
+}
+
+static ssize_t
+archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
+{
+ int ret;
+ struct zip *zip = a->format_data;
+
+ if ((int64_t)s > zip->entry_uncompressed_limit)
+ s = (size_t)zip->entry_uncompressed_limit;
+ zip->entry_uncompressed_written += s;
+
+ if (s == 0) return 0;
+
+ if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+ switch (zip->entry_encryption) {
+ case ENCRYPTION_TRADITIONAL:
+ /* Initialize traditional PKWARE encryption context. */
+ if (!zip->tctx_valid) {
+ ret = init_traditional_pkware_encryption(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->tctx_valid = 1;
+ }
+ break;
+ case ENCRYPTION_WINZIP_AES128:
+ case ENCRYPTION_WINZIP_AES256:
+ if (!zip->cctx_valid) {
+ ret = init_winzip_aes_encryption(a);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->cctx_valid = zip->hctx_valid = 1;
+ }
+ break;
+ case ENCRYPTION_NONE:
+ default:
+ break;
+ }
+ }
+
+ switch (zip->entry_compression) {
+ case COMPRESSION_STORE:
+ if (zip->tctx_valid || zip->cctx_valid) {
+ const uint8_t *rb = (const uint8_t *)buff;
+ const uint8_t * const re = rb + s;
+
+ while (rb < re) {
+ size_t l;
+
+ if (zip->tctx_valid) {
+ l = trad_enc_encrypt_update(&zip->tctx,
+ rb, re - rb,
+ zip->buf, zip->len_buf);
+ } else {
+ l = zip->len_buf;
+ ret = archive_encrypto_aes_ctr_update(
+ &zip->cctx,
+ rb, re - rb, zip->buf, &l);
+ if (ret < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to encrypt file");
+ return (ARCHIVE_FAILED);
+ }
+ archive_hmac_sha1_update(&zip->hctx,
+ zip->buf, l);
+ }
+ ret = __archive_write_output(a, zip->buf, l);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->entry_compressed_written += l;
+ zip->written_bytes += l;
+ rb += l;
+ }
+ } else {
+ ret = __archive_write_output(a, buff, s);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->written_bytes += s;
+ zip->entry_compressed_written += s;
+ }
+ break;
+#if HAVE_ZLIB_H
+ case COMPRESSION_DEFLATE:
+ zip->stream.next_in = (unsigned char*)(uintptr_t)buff;
+ zip->stream.avail_in = (uInt)s;
+ do {
+ ret = deflate(&zip->stream, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR)
+ return (ARCHIVE_FATAL);
+ if (zip->stream.avail_out == 0) {
+ if (zip->tctx_valid) {
+ trad_enc_encrypt_update(&zip->tctx,
+ zip->buf, zip->len_buf,
+ zip->buf, zip->len_buf);
+ } else if (zip->cctx_valid) {
+ size_t outl = zip->len_buf;
+ ret = archive_encrypto_aes_ctr_update(
+ &zip->cctx,
+ zip->buf, zip->len_buf,
+ zip->buf, &outl);
+ if (ret < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to encrypt file");
+ return (ARCHIVE_FAILED);
+ }
+ archive_hmac_sha1_update(&zip->hctx,
+ zip->buf, zip->len_buf);
+ }
+ ret = __archive_write_output(a, zip->buf,
+ zip->len_buf);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->entry_compressed_written += zip->len_buf;
+ zip->written_bytes += zip->len_buf;
+ zip->stream.next_out = zip->buf;
+ zip->stream.avail_out = (uInt)zip->len_buf;
+ }
+ } while (zip->stream.avail_in != 0);
+ break;
+#endif
+
+ case COMPRESSION_UNSPECIFIED:
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid ZIP compression type");
+ return ARCHIVE_FATAL;
+ }
+
+ zip->entry_uncompressed_limit -= s;
+ if (!zip->cctx_valid || zip->aes_vendor != AES_VENDOR_AE_2)
+ zip->entry_crc32 =
+ zip->crc32func(zip->entry_crc32, buff, (unsigned)s);
+ return (s);
+
+}
+
+static int
+archive_write_zip_finish_entry(struct archive_write *a)
+{
+ struct zip *zip = a->format_data;
+ int ret;
+
+#if HAVE_ZLIB_H
+ if (zip->entry_compression == COMPRESSION_DEFLATE) {
+ for (;;) {
+ size_t remainder;
+
+ ret = deflate(&zip->stream, Z_FINISH);
+ if (ret == Z_STREAM_ERROR)
+ return (ARCHIVE_FATAL);
+ remainder = zip->len_buf - zip->stream.avail_out;
+ if (zip->tctx_valid) {
+ trad_enc_encrypt_update(&zip->tctx,
+ zip->buf, remainder, zip->buf, remainder);
+ } else if (zip->cctx_valid) {
+ size_t outl = remainder;
+ ret = archive_encrypto_aes_ctr_update(
+ &zip->cctx, zip->buf, remainder,
+ zip->buf, &outl);
+ if (ret < 0) {
+ archive_set_error(&a->archive,
+ ARCHIVE_ERRNO_MISC,
+ "Failed to encrypt file");
+ return (ARCHIVE_FAILED);
+ }
+ archive_hmac_sha1_update(&zip->hctx,
+ zip->buf, remainder);
+ }
+ ret = __archive_write_output(a, zip->buf, remainder);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->entry_compressed_written += remainder;
+ zip->written_bytes += remainder;
+ zip->stream.next_out = zip->buf;
+ if (zip->stream.avail_out != 0)
+ break;
+ zip->stream.avail_out = (uInt)zip->len_buf;
+ }
+ deflateEnd(&zip->stream);
+ }
+#endif
+ if (zip->hctx_valid) {
+ uint8_t hmac[20];
+ size_t hmac_len = 20;
+
+ archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);
+ ret = __archive_write_output(a, hmac, AUTH_CODE_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->entry_compressed_written += AUTH_CODE_SIZE;
+ zip->written_bytes += AUTH_CODE_SIZE;
+ }
+
+ /* Write trailing data descriptor. */
+ if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) != 0) {
+ char d[24];
+ memcpy(d, "PK\007\010", 4);
+ if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2)
+ archive_le32enc(d + 4, 0);/* no CRC.*/
+ else
+ archive_le32enc(d + 4, zip->entry_crc32);
+ if (zip->entry_uses_zip64) {
+ archive_le64enc(d + 8,
+ (uint64_t)zip->entry_compressed_written);
+ archive_le64enc(d + 16,
+ (uint64_t)zip->entry_uncompressed_written);
+ ret = __archive_write_output(a, d, 24);
+ zip->written_bytes += 24;
+ } else {
+ archive_le32enc(d + 8,
+ (uint32_t)zip->entry_compressed_written);
+ archive_le32enc(d + 12,
+ (uint32_t)zip->entry_uncompressed_written);
+ ret = __archive_write_output(a, d, 16);
+ zip->written_bytes += 16;
+ }
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Append Zip64 extra data to central directory information. */
+ if (zip->entry_compressed_written > ZIP_4GB_MAX
+ || zip->entry_uncompressed_written > ZIP_4GB_MAX
+ || zip->entry_offset > ZIP_4GB_MAX) {
+ unsigned char zip64[32];
+ unsigned char *z = zip64, *zd;
+ memcpy(z, "\001\000\000\000", 4);
+ z += 4;
+ if (zip->entry_uncompressed_written >= ZIP_4GB_MAX) {
+ archive_le64enc(z, zip->entry_uncompressed_written);
+ z += 8;
+ }
+ if (zip->entry_compressed_written >= ZIP_4GB_MAX) {
+ archive_le64enc(z, zip->entry_compressed_written);
+ z += 8;
+ }
+ if (zip->entry_offset >= ZIP_4GB_MAX) {
+ archive_le64enc(z, zip->entry_offset);
+ z += 8;
+ }
+ archive_le16enc(zip64 + 2, (uint16_t)(z - (zip64 + 4)));
+ zd = cd_alloc(zip, z - zip64);
+ if (zd == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate zip data");
+ return (ARCHIVE_FATAL);
+ }
+ memcpy(zd, zip64, z - zip64);
+ /* Zip64 means version needs to be set to at least 4.5 */
+ if (archive_le16dec(zip->file_header + 6) < 45)
+ archive_le16enc(zip->file_header + 6, 45);
+ }
+
+ /* Fix up central directory file header. */
+ if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2)
+ archive_le32enc(zip->file_header + 16, 0);/* no CRC.*/
+ else
+ archive_le32enc(zip->file_header + 16, zip->entry_crc32);
+ archive_le32enc(zip->file_header + 20,
+ (uint32_t)zipmin(zip->entry_compressed_written,
+ ZIP_4GB_MAX));
+ archive_le32enc(zip->file_header + 24,
+ (uint32_t)zipmin(zip->entry_uncompressed_written,
+ ZIP_4GB_MAX));
+ archive_le16enc(zip->file_header + 30,
+ (uint16_t)(zip->central_directory_bytes - zip->file_header_extra_offset));
+ archive_le32enc(zip->file_header + 42,
+ (uint32_t)zipmin(zip->entry_offset,
+ ZIP_4GB_MAX));
+
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_zip_close(struct archive_write *a)
+{
+ uint8_t buff[64];
+ int64_t offset_start, offset_end;
+ struct zip *zip = a->format_data;
+ struct cd_segment *segment;
+ int ret;
+
+ offset_start = zip->written_bytes;
+ segment = zip->central_directory;
+ while (segment != NULL) {
+ ret = __archive_write_output(a,
+ segment->buff, segment->p - segment->buff);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += segment->p - segment->buff;
+ segment = segment->next;
+ }
+ offset_end = zip->written_bytes;
+
+ /* If central dir info is too large, write Zip64 end-of-cd */
+ if (offset_end - offset_start > ZIP_4GB_MAX
+ || offset_start > ZIP_4GB_MAX
+ || zip->central_directory_entries > 0xffffUL
+ || (zip->flags & ZIP_FLAG_FORCE_ZIP64)) {
+ /* Zip64 end-of-cd record */
+ memset(buff, 0, 56);
+ memcpy(buff, "PK\006\006", 4);
+ archive_le64enc(buff + 4, 44);
+ archive_le16enc(buff + 12, 45);
+ archive_le16enc(buff + 14, 45);
+ /* This is disk 0 of 0. */
+ archive_le64enc(buff + 24, zip->central_directory_entries);
+ archive_le64enc(buff + 32, zip->central_directory_entries);
+ archive_le64enc(buff + 40, offset_end - offset_start);
+ archive_le64enc(buff + 48, offset_start);
+ ret = __archive_write_output(a, buff, 56);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += 56;
+
+ /* Zip64 end-of-cd locator record. */
+ memset(buff, 0, 20);
+ memcpy(buff, "PK\006\007", 4);
+ archive_le32enc(buff + 4, 0);
+ archive_le64enc(buff + 8, offset_end);
+ archive_le32enc(buff + 16, 1);
+ ret = __archive_write_output(a, buff, 20);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += 20;
+
+ }
+
+ /* Format and write end of central directory. */
+ memset(buff, 0, sizeof(buff));
+ memcpy(buff, "PK\005\006", 4);
+ archive_le16enc(buff + 8, (uint16_t)zipmin(0xffffU,
+ zip->central_directory_entries));
+ archive_le16enc(buff + 10, (uint16_t)zipmin(0xffffU,
+ zip->central_directory_entries));
+ archive_le32enc(buff + 12,
+ (uint32_t)zipmin(ZIP_4GB_MAX, (offset_end - offset_start)));
+ archive_le32enc(buff + 16,
+ (uint32_t)zipmin(ZIP_4GB_MAX, offset_start));
+ ret = __archive_write_output(a, buff, 22);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ zip->written_bytes += 22;
+ return (ARCHIVE_OK);
+}
+
+static int
+archive_write_zip_free(struct archive_write *a)
+{
+ struct zip *zip;
+ struct cd_segment *segment;
+
+ zip = a->format_data;
+ while (zip->central_directory != NULL) {
+ segment = zip->central_directory;
+ zip->central_directory = segment->next;
+ free(segment->buff);
+ free(segment);
+ }
+ free(zip->buf);
+ archive_entry_free(zip->entry);
+ if (zip->cctx_valid)
+ archive_encrypto_aes_ctr_release(&zip->cctx);
+ if (zip->hctx_valid)
+ archive_hmac_sha1_cleanup(&zip->hctx);
+ /* TODO: Free opt_sconv, sconv_default */
+
+ free(zip);
+ a->format_data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/* Convert into MSDOS-style date/time. */
+static unsigned int
+dos_time(const time_t unix_time)
+{
+ struct tm *t;
+ unsigned int dt;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+ struct tm tmbuf;
+#endif
+
+#if defined(HAVE_LOCALTIME_S)
+ t = localtime_s(&tmbuf, &unix_time) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+ t = localtime_r(&unix_time, &tmbuf);
+#else
+ t = localtime(&unix_time);
+#endif
+
+ /* MSDOS-style date/time is only between 1980-01-01 and 2107-12-31 */
+ if (t->tm_year < 1980 - 1900)
+ /* Set minimum date/time '1980-01-01 00:00:00'. */
+ dt = 0x00210000U;
+ else if (t->tm_year > 2107 - 1900)
+ /* Set maximum date/time '2107-12-31 23:59:58'. */
+ dt = 0xff9fbf7dU;
+ else {
+ dt = 0;
+ dt += ((t->tm_year - 80) & 0x7f) << 9;
+ dt += ((t->tm_mon + 1) & 0x0f) << 5;
+ dt += (t->tm_mday & 0x1f);
+ dt <<= 16;
+ dt += (t->tm_hour & 0x1f) << 11;
+ dt += (t->tm_min & 0x3f) << 5;
+ dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */
+ }
+ return dt;
+}
+
+static size_t
+path_length(struct archive_entry *entry)
+{
+ mode_t type;
+ const char *path;
+ size_t len;
+
+ type = archive_entry_filetype(entry);
+ path = archive_entry_pathname(entry);
+
+ if (path == NULL)
+ return (0);
+ len = strlen(path);
+ if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/'))
+ ++len; /* Space for the trailing / */
+ return len;
+}
+
+static int
+write_path(struct archive_entry *entry, struct archive_write *archive)
+{
+ int ret;
+ const char *path;
+ mode_t type;
+ size_t written_bytes;
+
+ path = archive_entry_pathname(entry);
+ type = archive_entry_filetype(entry);
+ written_bytes = 0;
+
+ if (path == NULL)
+ return (ARCHIVE_FATAL);
+
+ ret = __archive_write_output(archive, path, strlen(path));
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ written_bytes += strlen(path);
+
+ /* Folders are recognized by a trailing slash. */
+ if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
+ ret = __archive_write_output(archive, "/", 1);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ written_bytes += 1;
+ }
+
+ return ((int)written_bytes);
+}
+
+static void
+copy_path(struct archive_entry *entry, unsigned char *p)
+{
+ const char *path;
+ size_t pathlen;
+ mode_t type;
+
+ path = archive_entry_pathname(entry);
+ pathlen = strlen(path);
+ type = archive_entry_filetype(entry);
+
+ memcpy(p, path, pathlen);
+
+ /* Folders are recognized by a trailing slash. */
+ if ((type == AE_IFDIR) && (path[pathlen - 1] != '/'))
+ p[pathlen] = '/';
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a, struct zip *zip)
+{
+ if (zip->opt_sconv != NULL)
+ return (zip->opt_sconv);
+
+ if (!zip->init_default_conversion) {
+ zip->sconv_default =
+ archive_string_default_conversion_for_write(&(a->archive));
+ zip->init_default_conversion = 1;
+ }
+ return (zip->sconv_default);
+}
+
+/*
+ Traditional PKWARE Decryption functions.
+ */
+
+static void
+trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)
+{
+ uint8_t t;
+#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)
+
+ ctx->keys[0] = CRC32(ctx->keys[0], c);
+ ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;
+ t = (ctx->keys[1] >> 24) & 0xff;
+ ctx->keys[2] = CRC32(ctx->keys[2], t);
+#undef CRC32
+}
+
+static uint8_t
+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)
+{
+ unsigned temp = ctx->keys[2] | 2;
+ return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
+}
+
+static unsigned
+trad_enc_encrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,
+ size_t in_len, uint8_t *out, size_t out_len)
+{
+ unsigned i, max;
+
+ max = (unsigned)((in_len < out_len)? in_len: out_len);
+
+ for (i = 0; i < max; i++) {
+ uint8_t t = in[i];
+ out[i] = t ^ trad_enc_decrypt_byte(ctx);
+ trad_enc_update_keys(ctx, t);
+ }
+ return i;
+}
+
+static int
+trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len)
+{
+
+ ctx->keys[0] = 305419896L;
+ ctx->keys[1] = 591751049L;
+ ctx->keys[2] = 878082192L;
+
+ for (;pw_len; --pw_len)
+ trad_enc_update_keys(ctx, *pw++);
+ return 0;
+}
+
+static int
+is_traditional_pkware_encryption_supported(void)
+{
+ uint8_t key[TRAD_HEADER_SIZE];
+
+ if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK)
+ return (0);
+ return (1);
+}
+
+static int
+init_traditional_pkware_encryption(struct archive_write *a)
+{
+ struct zip *zip = a->format_data;
+ const char *passphrase;
+ uint8_t key[TRAD_HEADER_SIZE];
+ uint8_t key_encrypted[TRAD_HEADER_SIZE];
+ int ret;
+
+ passphrase = __archive_write_get_passphrase(a);
+ if (passphrase == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Encryption needs passphrase");
+ return ARCHIVE_FAILED;
+ }
+ if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't generate random number for encryption");
+ return ARCHIVE_FATAL;
+ }
+ trad_enc_init(&zip->tctx, passphrase, strlen(passphrase));
+ /* Set the last key code which will be used as a check code
+ * for verifying passphrase in decryption. */
+ key[TRAD_HEADER_SIZE-1] = zip->trad_chkdat;
+ trad_enc_encrypt_update(&zip->tctx, key, TRAD_HEADER_SIZE,
+ key_encrypted, TRAD_HEADER_SIZE);
+ /* Write encrypted keys in the top of the file content. */
+ ret = __archive_write_output(a, key_encrypted, TRAD_HEADER_SIZE);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->written_bytes += TRAD_HEADER_SIZE;
+ zip->entry_compressed_written += TRAD_HEADER_SIZE;
+ return (ret);
+}
+
+static int
+init_winzip_aes_encryption(struct archive_write *a)
+{
+ struct zip *zip = a->format_data;
+ const char *passphrase;
+ size_t key_len, salt_len;
+ uint8_t salt[16 + 2];
+ uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+ int ret;
+
+ passphrase = __archive_write_get_passphrase(a);
+ if (passphrase == NULL) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Encryption needs passphrase");
+ return (ARCHIVE_FAILED);
+ }
+ if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128) {
+ salt_len = 8;
+ key_len = 16;
+ } else {
+ /* AES 256 */
+ salt_len = 16;
+ key_len = 32;
+ }
+ if (archive_random(salt, salt_len) != ARCHIVE_OK) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Can't generate random number for encryption");
+ return (ARCHIVE_FATAL);
+ }
+ archive_pbkdf2_sha1(passphrase, strlen(passphrase),
+ salt, salt_len, 1000, derived_key, key_len * 2 + 2);
+
+ ret = archive_encrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);
+ if (ret != 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Decryption is unsupported due to lack of crypto library");
+ return (ARCHIVE_FAILED);
+ }
+ ret = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len,
+ key_len);
+ if (ret != 0) {
+ archive_encrypto_aes_ctr_release(&zip->cctx);
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Failed to initialize HMAC-SHA1");
+ return (ARCHIVE_FAILED);
+ }
+
+ /* Set a password verification value after the 'salt'. */
+ salt[salt_len] = derived_key[key_len * 2];
+ salt[salt_len + 1] = derived_key[key_len * 2 + 1];
+
+ /* Write encrypted keys in the top of the file content. */
+ ret = __archive_write_output(a, salt, salt_len + 2);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ zip->written_bytes += salt_len + 2;
+ zip->entry_compressed_written += salt_len + 2;
+
+ return (ARCHIVE_OK);
+}
+
+static int
+is_winzip_aes_encryption_supported(int encryption)
+{
+ size_t key_len, salt_len;
+ uint8_t salt[16 + 2];
+ uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+ archive_crypto_ctx cctx;
+ archive_hmac_sha1_ctx hctx;
+ int ret;
+
+ if (encryption == ENCRYPTION_WINZIP_AES128) {
+ salt_len = 8;
+ key_len = 16;
+ } else {
+ /* AES 256 */
+ salt_len = 16;
+ key_len = 32;
+ }
+ if (archive_random(salt, salt_len) != ARCHIVE_OK)
+ return (0);
+ ret = archive_pbkdf2_sha1("p", 1, salt, salt_len, 1000,
+ derived_key, key_len * 2 + 2);
+ if (ret != 0)
+ return (0);
+
+ ret = archive_encrypto_aes_ctr_init(&cctx, derived_key, key_len);
+ if (ret != 0)
+ return (0);
+ ret = archive_hmac_sha1_init(&hctx, derived_key + key_len,
+ key_len);
+ archive_encrypto_aes_ctr_release(&cctx);
+ if (ret != 0)
+ return (0);
+ archive_hmac_sha1_cleanup(&hctx);
+ return (1);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_options.c b/contrib/libs/libarchive/libarchive/archive_write_set_options.c
new file mode 100644
index 0000000000..962309ada5
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_options.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_write_private.h"
+#include "archive_options_private.h"
+
+static int archive_set_format_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+static int archive_set_filter_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+static int archive_set_option(struct archive *a,
+ const char *m, const char *o, const char *v);
+
+int
+archive_write_set_format_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_WRITE_MAGIC, "archive_write_set_format_option",
+ archive_set_format_option);
+}
+
+int
+archive_write_set_filter_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_WRITE_MAGIC, "archive_write_set_filter_option",
+ archive_set_filter_option);
+}
+
+int
+archive_write_set_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_option(a, m, o, v,
+ ARCHIVE_WRITE_MAGIC, "archive_write_set_option",
+ archive_set_option);
+}
+
+int
+archive_write_set_options(struct archive *a, const char *options)
+{
+ return _archive_set_options(a, options,
+ ARCHIVE_WRITE_MAGIC, "archive_write_set_options",
+ archive_set_option);
+}
+
+static int
+archive_set_format_option(struct archive *_a, const char *m, const char *o,
+ const char *v)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+
+ if (a->format_name == NULL)
+ return (m == NULL)?ARCHIVE_FAILED:ARCHIVE_WARN - 1;
+ /* If the format name didn't match, return a special code for
+ * _archive_set_option[s]. */
+ if (m != NULL && strcmp(m, a->format_name) != 0)
+ return (ARCHIVE_WARN - 1);
+ if (a->format_options == NULL)
+ return (ARCHIVE_WARN);
+ return a->format_options(a, o, v);
+}
+
+static int
+archive_set_filter_option(struct archive *_a, const char *m, const char *o,
+ const char *v)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *filter;
+ int r, rv = ARCHIVE_WARN;
+
+ for (filter = a->filter_first; filter != NULL; filter = filter->next_filter) {
+ if (filter->options == NULL)
+ continue;
+ if (m != NULL && strcmp(filter->name, m) != 0)
+ continue;
+
+ r = filter->options(filter, o, v);
+
+ if (r == ARCHIVE_FATAL)
+ return (ARCHIVE_FATAL);
+
+ if (m != NULL)
+ return (r);
+
+ if (r == ARCHIVE_OK)
+ rv = ARCHIVE_OK;
+ }
+ /* If the filter name didn't match, return a special code for
+ * _archive_set_option[s]. */
+ if (rv == ARCHIVE_WARN && m != NULL)
+ rv = ARCHIVE_WARN - 1;
+ return (rv);
+}
+
+static int
+archive_set_option(struct archive *a, const char *m, const char *o,
+ const char *v)
+{
+ return _archive_set_either_option(a, m, o, v,
+ archive_set_format_option,
+ archive_set_filter_option);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_write_set_passphrase.c b/contrib/libs/libarchive/libarchive/archive_write_set_passphrase.c
new file mode 100644
index 0000000000..710ecba52c
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_write_set_passphrase.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "archive_write_private.h"
+
+int
+archive_write_set_passphrase(struct archive *_a, const char *p)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_write_set_passphrase");
+
+ if (p == NULL || p[0] == '\0') {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Empty passphrase is unacceptable");
+ return (ARCHIVE_FAILED);
+ }
+ free(a->passphrase);
+ a->passphrase = strdup(p);
+ if (a->passphrase == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data for passphrase");
+ return (ARCHIVE_FATAL);
+ }
+ return (ARCHIVE_OK);
+}
+
+
+int
+archive_write_set_passphrase_callback(struct archive *_a, void *client_data,
+ archive_passphrase_callback *cb)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+
+ archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,
+ "archive_write_set_passphrase_callback");
+
+ a->passphrase_callback = cb;
+ a->passphrase_client_data = client_data;
+ return (ARCHIVE_OK);
+}
+
+
+const char *
+__archive_write_get_passphrase(struct archive_write *a)
+{
+
+ if (a->passphrase != NULL)
+ return (a->passphrase);
+
+ if (a->passphrase_callback != NULL) {
+ const char *p;
+ p = a->passphrase_callback(&a->archive,
+ a->passphrase_client_data);
+ if (p != NULL) {
+ a->passphrase = strdup(p);
+ if (a->passphrase == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate data for passphrase");
+ return (NULL);
+ }
+ return (a->passphrase);
+ }
+ }
+ return (NULL);
+}
diff --git a/contrib/libs/libarchive/libarchive/archive_xxhash.h b/contrib/libs/libarchive/libarchive/archive_xxhash.h
new file mode 100644
index 0000000000..1c7131ca1e
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/archive_xxhash.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ */
+
+#ifndef ARCHIVE_XXHASH_H_INCLUDED
+#define ARCHIVE_XXHASH_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+struct archive_xxhash {
+ unsigned int (*XXH32)(const void* input, unsigned int len,
+ unsigned int seed);
+ void* (*XXH32_init)(unsigned int seed);
+ XXH_errorcode (*XXH32_update)(void* state, const void* input,
+ unsigned int len);
+ unsigned int (*XXH32_digest)(void* state);
+};
+
+extern const struct archive_xxhash __archive_xxhash;
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/filter_fork.h b/contrib/libs/libarchive/libarchive/filter_fork.h
new file mode 100644
index 0000000000..2bf290c4d9
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/filter_fork.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD: head/lib/libarchive/filter_fork.h 201087 2009-12-28 02:18:26Z kientzle $
+ */
+
+#ifndef FILTER_FORK_H
+#define FILTER_FORK_H
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HANDLE *out_child);
+#else
+ pid_t *out_child);
+#endif
+
+void
+__archive_check_child(int in, int out);
+
+#endif
diff --git a/contrib/libs/libarchive/libarchive/filter_fork_posix.c b/contrib/libs/libarchive/libarchive/filter_fork_posix.c
new file mode 100644
index 0000000000..62085a7099
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/filter_fork_posix.c
@@ -0,0 +1,240 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+/* This capability is only available on POSIX systems. */
+#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \
+ (defined(HAVE_FORK) || defined(HAVE_VFORK) || defined(HAVE_POSIX_SPAWNP))
+
+__FBSDID("$FreeBSD: head/lib/libarchive/filter_fork.c 182958 2008-09-12 05:33:00Z kientzle $");
+
+#if defined(HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
+# if defined(HAVE_POLL_H)
+# include <poll.h>
+# elif defined(HAVE_SYS_POLL_H)
+# include <sys/poll.h>
+# endif
+#elif defined(HAVE_SELECT)
+# if defined(HAVE_SYS_SELECT_H)
+# include <sys/select.h>
+# elif defined(HAVE_UNISTD_H)
+# include <unistd.h>
+# endif
+#endif
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_SPAWN_H
+# include <spawn.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_cmdline_private.h"
+
+#include "filter_fork.h"
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+ pid_t *out_child)
+{
+ pid_t child = -1;
+ int stdin_pipe[2], stdout_pipe[2], tmp;
+#if HAVE_POSIX_SPAWNP
+ posix_spawn_file_actions_t actions;
+ int r;
+#endif
+ struct archive_cmdline *cmdline;
+
+ cmdline = __archive_cmdline_allocate();
+ if (cmdline == NULL)
+ goto state_allocated;
+ if (__archive_cmdline_parse(cmdline, cmd) != ARCHIVE_OK)
+ goto state_allocated;
+
+ if (pipe(stdin_pipe) == -1)
+ goto state_allocated;
+ if (stdin_pipe[0] == 1 /* stdout */) {
+ if ((tmp = dup(stdin_pipe[0])) == -1)
+ goto stdin_opened;
+ close(stdin_pipe[0]);
+ stdin_pipe[0] = tmp;
+ }
+ if (pipe(stdout_pipe) == -1)
+ goto stdin_opened;
+ if (stdout_pipe[1] == 0 /* stdin */) {
+ if ((tmp = dup(stdout_pipe[1])) == -1)
+ goto stdout_opened;
+ close(stdout_pipe[1]);
+ stdout_pipe[1] = tmp;
+ }
+
+#if HAVE_POSIX_SPAWNP
+
+ r = posix_spawn_file_actions_init(&actions);
+ if (r != 0) {
+ errno = r;
+ goto stdout_opened;
+ }
+ r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[1]);
+ if (r != 0)
+ goto actions_inited;
+ r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[0]);
+ if (r != 0)
+ goto actions_inited;
+ /* Setup for stdin. */
+ r = posix_spawn_file_actions_adddup2(&actions, stdin_pipe[0], 0);
+ if (r != 0)
+ goto actions_inited;
+ if (stdin_pipe[0] != 0 /* stdin */) {
+ r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[0]);
+ if (r != 0)
+ goto actions_inited;
+ }
+ /* Setup for stdout. */
+ r = posix_spawn_file_actions_adddup2(&actions, stdout_pipe[1], 1);
+ if (r != 0)
+ goto actions_inited;
+ if (stdout_pipe[1] != 1 /* stdout */) {
+ r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[1]);
+ if (r != 0)
+ goto actions_inited;
+ }
+ r = posix_spawnp(&child, cmdline->path, &actions, NULL,
+ cmdline->argv, NULL);
+ if (r != 0)
+ goto actions_inited;
+ posix_spawn_file_actions_destroy(&actions);
+
+#else /* HAVE_POSIX_SPAWNP */
+
+#if HAVE_VFORK
+ child = vfork();
+#else
+ child = fork();
+#endif
+ if (child == -1)
+ goto stdout_opened;
+ if (child == 0) {
+ close(stdin_pipe[1]);
+ close(stdout_pipe[0]);
+ if (dup2(stdin_pipe[0], 0 /* stdin */) == -1)
+ _exit(254);
+ if (stdin_pipe[0] != 0 /* stdin */)
+ close(stdin_pipe[0]);
+ if (dup2(stdout_pipe[1], 1 /* stdout */) == -1)
+ _exit(254);
+ if (stdout_pipe[1] != 1 /* stdout */)
+ close(stdout_pipe[1]);
+ execvp(cmdline->path, cmdline->argv);
+ _exit(254);
+ }
+#endif /* HAVE_POSIX_SPAWNP */
+
+ close(stdin_pipe[0]);
+ close(stdout_pipe[1]);
+
+ *child_stdin = stdin_pipe[1];
+ fcntl(*child_stdin, F_SETFL, O_NONBLOCK);
+ *child_stdout = stdout_pipe[0];
+ fcntl(*child_stdout, F_SETFL, O_NONBLOCK);
+ __archive_cmdline_free(cmdline);
+
+ *out_child = child;
+ return ARCHIVE_OK;
+
+#if HAVE_POSIX_SPAWNP
+actions_inited:
+ errno = r;
+ posix_spawn_file_actions_destroy(&actions);
+#endif
+stdout_opened:
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+stdin_opened:
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
+state_allocated:
+ __archive_cmdline_free(cmdline);
+ return ARCHIVE_FAILED;
+}
+
+void
+__archive_check_child(int in, int out)
+{
+#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
+ struct pollfd fds[2];
+ int idx;
+
+ idx = 0;
+ if (in != -1) {
+ fds[idx].fd = in;
+ fds[idx].events = POLLOUT;
+ ++idx;
+ }
+ if (out != -1) {
+ fds[idx].fd = out;
+ fds[idx].events = POLLIN;
+ ++idx;
+ }
+
+ poll(fds, idx, -1); /* -1 == INFTIM, wait forever */
+#elif defined(HAVE_SELECT)
+ fd_set fds_in, fds_out, fds_error;
+
+ FD_ZERO(&fds_in);
+ FD_ZERO(&fds_out);
+ FD_ZERO(&fds_error);
+ if (out != -1) {
+ FD_SET(out, &fds_in);
+ FD_SET(out, &fds_error);
+ }
+ if (in != -1) {
+ FD_SET(in, &fds_out);
+ FD_SET(in, &fds_error);
+ }
+ select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
+#else
+ sleep(1);
+#endif
+}
+
+#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */
diff --git a/contrib/libs/libarchive/libarchive/filter_fork_windows.c b/contrib/libs/libarchive/libarchive/filter_fork_windows.c
new file mode 100644
index 0000000000..9e49c5655f
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/filter_fork_windows.c
@@ -0,0 +1,245 @@
+/*-
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+#include "archive_platform.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include "archive_cmdline_private.h"
+#include "archive_string.h"
+
+#include "filter_fork.h"
+
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+/* There are some editions of Windows ("nano server," for example) that
+ * do not host user32.dll. If we want to keep running on those editions,
+ * we need to delay-load WaitForInputIdle. */
+static void *
+la_GetFunctionUser32(const char *name)
+{
+ static HINSTANCE lib;
+ static int set;
+ if (!set) {
+ set = 1;
+ lib = LoadLibrary(TEXT("user32.dll"));
+ }
+ if (lib == NULL) {
+ return NULL;
+ }
+ return (void *)GetProcAddress(lib, name);
+}
+
+static int
+la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds)
+{
+ static DWORD (WINAPI *f)(HANDLE, DWORD);
+ static int set;
+
+ if (!set) {
+ set = 1;
+ f = la_GetFunctionUser32("WaitForInputIdle");
+ }
+
+ if (!f) {
+ /* An inability to wait for input idle is
+ * not _good_, but it is not catastrophic. */
+ return WAIT_FAILED;
+ }
+ return (*f)(hProcess, dwMilliseconds);
+}
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+ HANDLE *out_child)
+{
+ HANDLE childStdout[2], childStdin[2],childStderr;
+ SECURITY_ATTRIBUTES secAtts;
+ STARTUPINFOA staInfo;
+ PROCESS_INFORMATION childInfo;
+ struct archive_string cmdline;
+ struct archive_string fullpath;
+ struct archive_cmdline *acmd;
+ char *arg0, *ext;
+ int i, l;
+ DWORD fl, fl_old;
+ HANDLE child;
+
+ childStdout[0] = childStdout[1] = INVALID_HANDLE_VALUE;
+ childStdin[0] = childStdin[1] = INVALID_HANDLE_VALUE;
+ childStderr = INVALID_HANDLE_VALUE;
+ archive_string_init(&cmdline);
+ archive_string_init(&fullpath);
+
+ acmd = __archive_cmdline_allocate();
+ if (acmd == NULL)
+ goto fail;
+ if (__archive_cmdline_parse(acmd, cmd) != ARCHIVE_OK)
+ goto fail;
+
+ /*
+ * Search the full path of 'path'.
+ * NOTE: This does not need if we give CreateProcessA 'path' as
+ * a part of the cmdline and give CreateProcessA NULL as first
+ * parameter, but I do not like that way.
+ */
+ ext = strrchr(acmd->path, '.');
+ if (ext == NULL || strlen(ext) > 4)
+ /* 'path' does not have a proper extension, so we have to
+ * give SearchPath() ".exe" as the extension. */
+ ext = ".exe";
+ else
+ ext = NULL;/* 'path' has an extension. */
+
+ fl = MAX_PATH;
+ do {
+ if (archive_string_ensure(&fullpath, fl) == NULL)
+ goto fail;
+ fl_old = fl;
+ fl = SearchPathA(NULL, acmd->path, ext, fl, fullpath.s,
+ &arg0);
+ } while (fl != 0 && fl > fl_old);
+ if (fl == 0)
+ goto fail;
+
+ /*
+ * Make a command line.
+ */
+ for (l = 0, i = 0; acmd->argv[i] != NULL; i++) {
+ if (i == 0)
+ continue;
+ l += (int)strlen(acmd->argv[i]) + 1;
+ }
+ if (archive_string_ensure(&cmdline, l + 1) == NULL)
+ goto fail;
+ for (i = 0; acmd->argv[i] != NULL; i++) {
+ if (i == 0) {
+ const char *p, *sp;
+
+ if ((p = strchr(acmd->argv[i], '/')) != NULL ||
+ (p = strchr(acmd->argv[i], '\\')) != NULL)
+ p++;
+ else
+ p = acmd->argv[i];
+ if ((sp = strchr(p, ' ')) != NULL)
+ archive_strappend_char(&cmdline, '"');
+ archive_strcat(&cmdline, p);
+ if (sp != NULL)
+ archive_strappend_char(&cmdline, '"');
+ } else {
+ archive_strappend_char(&cmdline, ' ');
+ archive_strcat(&cmdline, acmd->argv[i]);
+ }
+ }
+ if (i <= 1) {
+ const char *sp;
+
+ if ((sp = strchr(arg0, ' ')) != NULL)
+ archive_strappend_char(&cmdline, '"');
+ archive_strcat(&cmdline, arg0);
+ if (sp != NULL)
+ archive_strappend_char(&cmdline, '"');
+ }
+
+ secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
+ secAtts.bInheritHandle = TRUE;
+ secAtts.lpSecurityDescriptor = NULL;
+ if (CreatePipe(&childStdout[0], &childStdout[1], &secAtts, 0) == 0)
+ goto fail;
+ if (!SetHandleInformation(childStdout[0], HANDLE_FLAG_INHERIT, 0))
+ goto fail;
+ if (CreatePipe(&childStdin[0], &childStdin[1], &secAtts, 0) == 0)
+ goto fail;
+ if (!SetHandleInformation(childStdin[1], HANDLE_FLAG_INHERIT, 0))
+ goto fail;
+ if (DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),
+ GetCurrentProcess(), &childStderr, 0, TRUE,
+ DUPLICATE_SAME_ACCESS) == 0)
+ goto fail;
+
+ memset(&staInfo, 0, sizeof(staInfo));
+ staInfo.cb = sizeof(staInfo);
+ staInfo.hStdError = childStderr;
+ staInfo.hStdOutput = childStdout[1];
+ staInfo.hStdInput = childStdin[0];
+ staInfo.wShowWindow = SW_HIDE;
+ staInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ if (CreateProcessA(fullpath.s, cmdline.s, NULL, NULL, TRUE, 0,
+ NULL, NULL, &staInfo, &childInfo) == 0)
+ goto fail;
+ la_WaitForInputIdle(childInfo.hProcess, INFINITE);
+ CloseHandle(childInfo.hProcess);
+ CloseHandle(childInfo.hThread);
+
+ *child_stdout = _open_osfhandle((intptr_t)childStdout[0], _O_RDONLY);
+ *child_stdin = _open_osfhandle((intptr_t)childStdin[1], _O_WRONLY);
+
+ child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
+ childInfo.dwProcessId);
+ if (child == NULL) // INVALID_HANDLE_VALUE ?
+ goto fail;
+
+ *out_child = child;
+
+ CloseHandle(childStdout[1]);
+ CloseHandle(childStdin[0]);
+
+ archive_string_free(&cmdline);
+ archive_string_free(&fullpath);
+ __archive_cmdline_free(acmd);
+ return ARCHIVE_OK;
+
+fail:
+ if (childStdout[0] != INVALID_HANDLE_VALUE)
+ CloseHandle(childStdout[0]);
+ if (childStdout[1] != INVALID_HANDLE_VALUE)
+ CloseHandle(childStdout[1]);
+ if (childStdin[0] != INVALID_HANDLE_VALUE)
+ CloseHandle(childStdin[0]);
+ if (childStdin[1] != INVALID_HANDLE_VALUE)
+ CloseHandle(childStdin[1]);
+ if (childStderr != INVALID_HANDLE_VALUE)
+ CloseHandle(childStderr);
+ archive_string_free(&cmdline);
+ archive_string_free(&fullpath);
+ __archive_cmdline_free(acmd);
+ return ARCHIVE_FAILED;
+}
+#else /* !WINAPI_PARTITION_DESKTOP */
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, HANDLE *out_child)
+{
+ (void)cmd; (void)child_stdin; (void) child_stdout; (void) out_child;
+ return ARCHIVE_FAILED;
+}
+#endif /* !WINAPI_PARTITION_DESKTOP */
+
+void
+__archive_check_child(int in, int out)
+{
+ (void)in; /* UNUSED */
+ (void)out; /* UNUSED */
+ Sleep(100);
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
diff --git a/contrib/libs/libarchive/libarchive/xxhash.c b/contrib/libs/libarchive/libarchive/xxhash.c
new file mode 100644
index 0000000000..beacd23912
--- /dev/null
+++ b/contrib/libs/libarchive/libarchive/xxhash.c
@@ -0,0 +1,529 @@
+/*
+xxHash - Fast Hash algorithm
+Copyright (C) 2012-2014, Yann Collet.
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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 COPYRIGHT
+OWNER OR CONTRIBUTORS 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.
+
+You can contact the author at :
+- xxHash source repository : http://code.google.com/p/xxhash/
+*/
+#include "archive_platform.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive_xxhash.h"
+
+#ifdef HAVE_LIBLZ4
+
+/***************************************
+** Tuning parameters
+****************************************/
+/* Unaligned memory access is automatically enabled for "common" CPU, such as x86.
+** For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected.
+** If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance.
+** You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32).
+*/
+#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+# define XXH_USE_UNALIGNED_ACCESS 1
+#endif
+
+/* XXH_ACCEPT_NULL_INPUT_POINTER :
+** If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
+** When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
+** This option has a very small performance cost (only measurable on small inputs).
+** By default, this option is disabled. To enable it, uncomment below define :
+** #define XXH_ACCEPT_NULL_INPUT_POINTER 1
+
+** XXH_FORCE_NATIVE_FORMAT :
+** By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
+** Results are therefore identical for little-endian and big-endian CPU.
+** This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
+** Should endian-independence be of no importance for your application, you may set the #define below to 1.
+** It will improve speed for Big-endian CPU.
+** This option has no impact on Little_Endian CPU.
+*/
+#define XXH_FORCE_NATIVE_FORMAT 0
+
+/***************************************
+** Compiler Specific Options
+****************************************/
+/* Disable some Visual warning messages */
+#ifdef _MSC_VER /* Visual Studio */
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#endif
+
+#ifdef _MSC_VER /* Visual Studio */
+# define FORCE_INLINE __forceinline
+#else
+# ifdef __GNUC__
+# define FORCE_INLINE inline __attribute__((always_inline))
+# else
+# define FORCE_INLINE inline
+# endif
+#endif
+
+/***************************************
+** Includes & Memory related functions
+****************************************/
+#define XXH_malloc malloc
+#define XXH_free free
+#define XXH_memcpy memcpy
+
+
+static unsigned int XXH32 (const void*, unsigned int, unsigned int);
+static void* XXH32_init (unsigned int);
+static XXH_errorcode XXH32_update (void*, const void*, unsigned int);
+static unsigned int XXH32_digest (void*);
+/*static int XXH32_sizeofState(void);*/
+static XXH_errorcode XXH32_resetState(void*, unsigned int);
+#define XXH32_SIZEOFSTATE 48
+typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t;
+static unsigned int XXH32_intermediateDigest (void*);
+
+/***************************************
+** Basic Types
+****************************************/
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# include <stdint.h>
+ typedef uint8_t BYTE;
+ typedef uint16_t U16;
+ typedef uint32_t U32;
+ typedef int32_t S32;
+ typedef uint64_t U64;
+#else
+ typedef unsigned char BYTE;
+ typedef unsigned short U16;
+ typedef unsigned int U32;
+ typedef signed int S32;
+ typedef unsigned long long U64;
+#endif
+
+#if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS)
+# define _PACKED __attribute__ ((packed))
+#else
+# define _PACKED
+#endif
+
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)
+# ifdef __IBMC__
+# pragma pack(1)
+# else
+# pragma pack(push, 1)
+# endif
+#endif
+
+typedef struct _U32_S { U32 v; } _PACKED U32_S;
+
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)
+# pragma pack(pop)
+#endif
+
+
+/****************************************
+** Compiler-specific Functions and Macros
+*****************************************/
+#define GCC_VERSION ((__GNUC__-0) * 100 + (__GNUC_MINOR__ - 0))
+
+#if GCC_VERSION >= 409
+__attribute__((__no_sanitize_undefined__))
+#else
+# if defined(__clang__)
+__attribute__((no_sanitize("undefined")))
+# endif
+#endif
+#if defined(_MSC_VER)
+static __inline U32 A32(const void * x)
+#else
+static inline U32 A32(const void* x)
+#endif
+{
+ return (((const U32_S *)(x))->v);
+}
+
+/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
+#if defined(_MSC_VER)
+# define XXH_rotl32(x,r) _rotl(x,r)
+#else
+# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
+#endif
+
+#if defined(_MSC_VER) /* Visual Studio */
+# define XXH_swap32 _byteswap_ulong
+#elif GCC_VERSION >= 403
+# define XXH_swap32 __builtin_bswap32
+#else
+static inline U32 XXH_swap32 (U32 x) {
+ return ((x << 24) & 0xff000000 ) |
+ ((x << 8) & 0x00ff0000 ) |
+ ((x >> 8) & 0x0000ff00 ) |
+ ((x >> 24) & 0x000000ff );}
+#endif
+
+
+/***************************************
+** Constants
+****************************************/
+#define PRIME32_1 2654435761U
+#define PRIME32_2 2246822519U
+#define PRIME32_3 3266489917U
+#define PRIME32_4 668265263U
+#define PRIME32_5 374761393U
+
+
+/***************************************
+** Architecture Macros
+****************************************/
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+#ifndef XXH_CPU_LITTLE_ENDIAN /* It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch */
+ static const int one = 1;
+# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&one))
+#endif
+
+
+/***************************************
+** Macros
+****************************************/
+#define XXH_STATIC_ASSERT(c) { enum { XXH_static_assert = 1/(!!(c)) }; } /* use only *after* variable declarations */
+
+
+/*****************************
+** Memory reads
+******************************/
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+static
+FORCE_INLINE U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align)
+{
+ if (align==XXH_unaligned)
+ return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr));
+ else
+ return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr);
+}
+
+static
+FORCE_INLINE U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); }
+
+
+/*****************************
+** Simple Hash Functions
+******************************/
+static
+FORCE_INLINE U32 XXH32_endian_align(const void* input, unsigned int len, U32 seed, XXH_endianess endian, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* bEnd = p + len;
+ U32 h32;
+#define XXH_get32bits(p) XXH_readLE32_align((const U32*)p, endian, align)
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)16; }
+#endif
+
+ if (len>=16)
+ {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = seed + PRIME32_1 + PRIME32_2;
+ U32 v2 = seed + PRIME32_2;
+ U32 v3 = seed + 0;
+ U32 v4 = seed - PRIME32_1;
+
+ do
+ {
+ v1 += XXH_get32bits(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
+ v2 += XXH_get32bits(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
+ v3 += XXH_get32bits(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
+ v4 += XXH_get32bits(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
+ } while (p<=limit);
+
+ h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+ }
+ else
+ {
+ h32 = seed + PRIME32_5;
+ }
+
+ h32 += (U32) len;
+
+ while (p<=bEnd-4)
+ {
+ h32 += XXH_get32bits(p) * PRIME32_3;
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
+ p+=4;
+ }
+
+ while (p<bEnd)
+ {
+ h32 += (*p) * PRIME32_5;
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
+ p++;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+
+U32 XXH32(const void* input, unsigned int len, U32 seed)
+{
+#if 0
+ // Simple version, good for code maintenance, but unfortunately slow for small inputs
+ void* state = XXH32_init(seed);
+ XXH32_update(state, input, len);
+ return XXH32_digest(state);
+#else
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+# if !defined(XXH_USE_UNALIGNED_ACCESS)
+ if ((((size_t)input) & 3) == 0) /* Input is aligned, let's leverage the speed advantage */
+ {
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+ else
+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+ }
+# endif
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+ else
+ return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+/*****************************
+** Advanced Hash Functions
+******************************/
+
+struct XXH_state32_t
+{
+ U64 total_len;
+ U32 seed;
+ U32 v1;
+ U32 v2;
+ U32 v3;
+ U32 v4;
+ int memsize;
+ char memory[16];
+};
+
+#if 0
+static
+int XXH32_sizeofState(void)
+{
+ XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t)); /* A compilation error here means XXH32_SIZEOFSTATE is not large enough */
+ return sizeof(struct XXH_state32_t);
+}
+#endif
+
+static
+XXH_errorcode XXH32_resetState(void* state_in, U32 seed)
+{
+ struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+ state->seed = seed;
+ state->v1 = seed + PRIME32_1 + PRIME32_2;
+ state->v2 = seed + PRIME32_2;
+ state->v3 = seed + 0;
+ state->v4 = seed - PRIME32_1;
+ state->total_len = 0;
+ state->memsize = 0;
+ return XXH_OK;
+}
+
+static
+void* XXH32_init (U32 seed)
+{
+ void* state = XXH_malloc (sizeof(struct XXH_state32_t));
+ XXH32_resetState(state, seed);
+ return state;
+}
+
+static
+FORCE_INLINE XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian)
+{
+ struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+ if (input==NULL) return XXH_ERROR;
+#endif
+
+ state->total_len += len;
+
+ if (state->memsize + len < 16) /* fill in tmp buffer */
+ {
+ XXH_memcpy(state->memory + state->memsize, input, len);
+ state->memsize += len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) /* some data left from previous update */
+ {
+ XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize);
+ {
+ const U32* p32 = (const U32*)state->memory;
+ state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++;
+ state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++;
+ state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++;
+ state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++;
+ }
+ p += 16-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p <= bEnd-16)
+ {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = state->v1;
+ U32 v2 = state->v2;
+ U32 v3 = state->v3;
+ U32 v4 = state->v4;
+
+ do
+ {
+ v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
+ v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
+ v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
+ v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
+ } while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd)
+ {
+ XXH_memcpy(state->memory, p, bEnd-p);
+ state->memsize = (int)(bEnd-p);
+ }
+
+ return XXH_OK;
+}
+
+static
+XXH_errorcode XXH32_update (void* state_in, const void* input, unsigned int len)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
+ else
+ return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+
+static
+FORCE_INLINE U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian)
+{
+ struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+ const BYTE * p = (const BYTE*)state->memory;
+ BYTE* bEnd = (BYTE*)state->memory + state->memsize;
+ U32 h32;
+
+ if (state->total_len >= 16)
+ {
+ h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
+ }
+ else
+ {
+ h32 = state->seed + PRIME32_5;
+ }
+
+ h32 += (U32) state->total_len;
+
+ while (p<=bEnd-4)
+ {
+ h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3;
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4;
+ p+=4;
+ }
+
+ while (p<bEnd)
+ {
+ h32 += (*p) * PRIME32_5;
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1;
+ p++;
+ }
+
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+
+ return h32;
+}
+
+static
+U32 XXH32_intermediateDigest (void* state_in)
+{
+ XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+ if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+ return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian);
+ else
+ return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian);
+}
+
+static
+U32 XXH32_digest (void* state_in)
+{
+ U32 h32 = XXH32_intermediateDigest(state_in);
+
+ XXH_free(state_in);
+
+ return h32;
+}
+
+const
+struct archive_xxhash __archive_xxhash = {
+ XXH32,
+ XXH32_init,
+ XXH32_update,
+ XXH32_digest
+};
+#else
+
+/*
+ * Define an empty version of the struct if we aren't using the LZ4 library.
+ */
+const
+struct archive_xxhash __archive_xxhash = {
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/contrib/libs/libarchive/ya.make b/contrib/libs/libarchive/ya.make
new file mode 100644
index 0000000000..d266dacfd5
--- /dev/null
+++ b/contrib/libs/libarchive/ya.make
@@ -0,0 +1,183 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+VERSION(3.7.2)
+
+ORIGINAL_SOURCE(https://github.com/libarchive/libarchive/archive/v3.7.2.tar.gz)
+
+LICENSE(
+ "(CC0-1.0 OR OpenSSL OR Apache-2.0)" AND
+ BSD-2-Clause AND
+ BSD-3-Clause AND
+ Bsd-Unchanged AND
+ ISC AND
+ PostgreSQL AND
+ Public-Domain
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/blake2
+ contrib/libs/libbz2
+ contrib/libs/lz4
+ contrib/libs/openssl
+ contrib/libs/zlib
+ contrib/libs/zstd
+)
+
+ADDINCL(
+ contrib/libs/blake2/include
+ contrib/libs/libarchive
+ contrib/libs/libarchive/libarchive
+ contrib/libs/libbz2
+ contrib/libs/lz4
+ contrib/libs/zstd/include
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DHAVE_CONFIG_H
+)
+
+SRCS(
+ libarchive/archive_acl.c
+ libarchive/archive_check_magic.c
+ libarchive/archive_cmdline.c
+ libarchive/archive_cryptor.c
+ libarchive/archive_digest.c
+ libarchive/archive_entry.c
+ libarchive/archive_entry_copy_stat.c
+ libarchive/archive_entry_link_resolver.c
+ libarchive/archive_entry_sparse.c
+ libarchive/archive_entry_stat.c
+ libarchive/archive_entry_strmode.c
+ libarchive/archive_entry_xattr.c
+ libarchive/archive_getdate.c
+ libarchive/archive_hmac.c
+ libarchive/archive_match.c
+ libarchive/archive_options.c
+ libarchive/archive_pack_dev.c
+ libarchive/archive_pathmatch.c
+ libarchive/archive_ppmd7.c
+ libarchive/archive_ppmd8.c
+ libarchive/archive_random.c
+ libarchive/archive_rb.c
+ libarchive/archive_read.c
+ libarchive/archive_read_add_passphrase.c
+ libarchive/archive_read_append_filter.c
+ libarchive/archive_read_data_into_fd.c
+ libarchive/archive_read_disk_entry_from_file.c
+ libarchive/archive_read_disk_posix.c
+ libarchive/archive_read_disk_set_standard_lookup.c
+ libarchive/archive_read_extract.c
+ libarchive/archive_read_extract2.c
+ libarchive/archive_read_open_fd.c
+ libarchive/archive_read_open_file.c
+ libarchive/archive_read_open_filename.c
+ libarchive/archive_read_open_memory.c
+ libarchive/archive_read_set_format.c
+ libarchive/archive_read_set_options.c
+ libarchive/archive_read_support_filter_all.c
+ libarchive/archive_read_support_filter_by_code.c
+ libarchive/archive_read_support_filter_bzip2.c
+ libarchive/archive_read_support_filter_compress.c
+ libarchive/archive_read_support_filter_grzip.c
+ libarchive/archive_read_support_filter_gzip.c
+ libarchive/archive_read_support_filter_lrzip.c
+ libarchive/archive_read_support_filter_lz4.c
+ libarchive/archive_read_support_filter_lzop.c
+ libarchive/archive_read_support_filter_none.c
+ libarchive/archive_read_support_filter_program.c
+ libarchive/archive_read_support_filter_rpm.c
+ libarchive/archive_read_support_filter_uu.c
+ libarchive/archive_read_support_filter_xz.c
+ libarchive/archive_read_support_filter_zstd.c
+ libarchive/archive_read_support_format_7zip.c
+ libarchive/archive_read_support_format_all.c
+ libarchive/archive_read_support_format_ar.c
+ libarchive/archive_read_support_format_by_code.c
+ libarchive/archive_read_support_format_cab.c
+ libarchive/archive_read_support_format_cpio.c
+ libarchive/archive_read_support_format_empty.c
+ libarchive/archive_read_support_format_iso9660.c
+ libarchive/archive_read_support_format_lha.c
+ libarchive/archive_read_support_format_mtree.c
+ libarchive/archive_read_support_format_rar.c
+ libarchive/archive_read_support_format_rar5.c
+ libarchive/archive_read_support_format_raw.c
+ libarchive/archive_read_support_format_tar.c
+ libarchive/archive_read_support_format_warc.c
+ libarchive/archive_read_support_format_xar.c
+ libarchive/archive_read_support_format_zip.c
+ libarchive/archive_string.c
+ libarchive/archive_string_sprintf.c
+ libarchive/archive_util.c
+ libarchive/archive_version_details.c
+ libarchive/archive_virtual.c
+ libarchive/archive_write.c
+ libarchive/archive_write_add_filter.c
+ libarchive/archive_write_add_filter_b64encode.c
+ libarchive/archive_write_add_filter_by_name.c
+ libarchive/archive_write_add_filter_bzip2.c
+ libarchive/archive_write_add_filter_compress.c
+ libarchive/archive_write_add_filter_grzip.c
+ libarchive/archive_write_add_filter_gzip.c
+ libarchive/archive_write_add_filter_lrzip.c
+ libarchive/archive_write_add_filter_lz4.c
+ libarchive/archive_write_add_filter_lzop.c
+ libarchive/archive_write_add_filter_none.c
+ libarchive/archive_write_add_filter_program.c
+ libarchive/archive_write_add_filter_uuencode.c
+ libarchive/archive_write_add_filter_xz.c
+ libarchive/archive_write_add_filter_zstd.c
+ libarchive/archive_write_disk_posix.c
+ libarchive/archive_write_disk_set_standard_lookup.c
+ libarchive/archive_write_open_fd.c
+ libarchive/archive_write_open_file.c
+ libarchive/archive_write_open_filename.c
+ libarchive/archive_write_open_memory.c
+ libarchive/archive_write_set_format.c
+ libarchive/archive_write_set_format_7zip.c
+ libarchive/archive_write_set_format_ar.c
+ libarchive/archive_write_set_format_by_name.c
+ libarchive/archive_write_set_format_cpio.c
+ libarchive/archive_write_set_format_cpio_binary.c
+ libarchive/archive_write_set_format_cpio_newc.c
+ libarchive/archive_write_set_format_cpio_odc.c
+ libarchive/archive_write_set_format_filter_by_ext.c
+ libarchive/archive_write_set_format_gnutar.c
+ libarchive/archive_write_set_format_iso9660.c
+ libarchive/archive_write_set_format_mtree.c
+ libarchive/archive_write_set_format_pax.c
+ libarchive/archive_write_set_format_raw.c
+ libarchive/archive_write_set_format_shar.c
+ libarchive/archive_write_set_format_ustar.c
+ libarchive/archive_write_set_format_v7tar.c
+ libarchive/archive_write_set_format_warc.c
+ libarchive/archive_write_set_format_xar.c
+ libarchive/archive_write_set_format_zip.c
+ libarchive/archive_write_set_options.c
+ libarchive/archive_write_set_passphrase.c
+ libarchive/filter_fork_posix.c
+ libarchive/xxhash.c
+)
+
+IF (OS_WINDOWS)
+ CFLAGS(
+ GLOBAL -DLIBARCHIVE_STATIC
+ )
+ SRCS(
+ libarchive/archive_entry_copy_bhfi.c
+ libarchive/archive_read_disk_windows.c
+ libarchive/archive_windows.c
+ libarchive/archive_write_disk_windows.c
+ libarchive/filter_fork_windows.c
+ )
+ENDIF()
+
+END()
diff --git a/contrib/libs/libmagic/AUTHORS b/contrib/libs/libmagic/AUTHORS
new file mode 100644
index 0000000000..bac5d5bcc9
--- /dev/null
+++ b/contrib/libs/libmagic/AUTHORS
@@ -0,0 +1 @@
+See COPYING.
diff --git a/contrib/libs/libmagic/COPYING b/contrib/libs/libmagic/COPYING
new file mode 100644
index 0000000000..16410a17f2
--- /dev/null
+++ b/contrib/libs/libmagic/COPYING
@@ -0,0 +1,29 @@
+$File: COPYING,v 1.2 2018/09/09 20:33:28 christos Exp $
+Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995.
+Software written by Ian F. Darwin and others;
+maintained 1994- Christos Zoulas.
+
+This software is not subject to any export provision of the United States
+Department of Commerce, and may be exported to any country or planet.
+
+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 immediately at the beginning of the file, without modification,
+ 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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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/contrib/libs/libmagic/ChangeLog b/contrib/libs/libmagic/ChangeLog
new file mode 100644
index 0000000000..fdf1cff6e2
--- /dev/null
+++ b/contrib/libs/libmagic/ChangeLog
@@ -0,0 +1,2129 @@
+2023-07-27 15:45 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.45
+
+2023-07-17 11:53 Christos Zoulas <christos@zoulas.com>
+
+ * PR/465: psrok1: Avoid muslc asctime_r crash
+
+2023-05-21 13:05 Christos Zoulas <christos@zoulas.com>
+
+ * add SIMH tape format support
+
+2023-02-09 12:50 Christos Zoulas <christos@zoulas.com>
+
+ * bump the max size of the elf section notes to be read to 128K
+ and make it configurable
+
+2023-01-08 1:08 Christos Zoulas <christos@zoulas.com>
+
+ * PR/415: Fix decompression with program returning empty
+
+2022-12-26 1:47 Christos Zoulas <christos@zoulas.com>
+
+ * PR/408: fix -p with seccomp
+ * PR/412: fix MinGW compilation
+
+2022-12-26 12:26 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.44
+
+2022-12-14 9:24 Christos Zoulas <christos@zoulas.com>
+
+ * Handle nan's so that we don't get internal floating point exceptions
+ when they are enabled (Vincent Mihalkovic)
+
+2022-10-23 10:21 Christos Zoulas <christos@zoulas.com>
+
+ * PR/397: Restore the ability to process files from stdin immediately.
+
+2022-09-20 17:12 Christos Zoulas <christos@zoulas.com>
+
+ * fixed various clustefuzz issues
+
+2022-09-19 15:54 Christos Zoulas <christos@zoulas.com>
+
+ * Fix error detection for decompression code (Vincent Mihalkovic)
+
+2022-09-15 13:50 Christos Zoulas <christos@zoulas.com>
+
+ * Add MAGIC_NO_COMPRESS_FORK and use it to produce a more
+ meaningful error message if we are sandboxing.
+
+2022-09-15 10:45 Christos Zoulas <christos@zoulas.com>
+
+ * Add built-in lzip decompression support (Michal Gorny)
+
+2022-09-14 10:35 Christos Zoulas <christos@zoulas.com>
+
+ * Add built-in zstd decompression support (Martin Rodriguez Reboredo)
+
+2022-09-13 14:55 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.43
+
+2022-09-10 9:17 Christos Zoulas <christos@zoulas.com>
+
+ * Add octal indirect magic (Michal Gorny)
+
+2022-08-17 11:43 Christos Zoulas <christos@zoulas.com>
+
+ * PR/374: avoid infinite loop in non-wide code (piru)
+ * PR/373: Obey MAGIC_CONTINUE with multiple magic files (vismarli)
+
+2022-07-26 11:10 Christos Zoulas <christos@zoulas.com>
+
+ * Fix bug with large flist (Florian Weimer)
+
+2022-07-07 13:21 Christos Zoulas <christos@zoulas.com>
+
+ * PR/364: Detect non-nul-terminated core filenames from QEMU
+ (mam-ableton)
+
+2022-07-04 15:45 Christos Zoulas <christos@zoulas.com>
+
+ * PR/359: Add support for http://ndjson.org/ (darose)
+ * PR/362: Fix wide printing (ro-ee)
+ * PR/358: Fix width for -f - (jpalus)
+ * PR/356: Fix JSON constant parsing (davewhite)
+
+2022-06-10 9:40 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.42
+
+2022-05-31 14:50 Christos Zoulas <christos@zoulas.com>
+
+ * PR/348: add missing cases to prevent file from aborting on
+ random magic files.
+
+2022-05-27 21:05 Christos Zoulas <christos@zoulas.com>
+
+ * PR/351: octalify filenames when not raw before printing.
+
+2022-04-18 17:51 Christos Zoulas <christos@zoulas.com>
+
+ * fix regex cacheing bug (Dirk Mueller)
+ * merge file_regcomp and file_regerror() to simplify the code
+ and reduce memory requirements for storing regexes (Dirk Mueller)
+
+2022-03-19 12:56 Christos Zoulas <christos@zoulas.com>
+
+ * cache regex (Dirk Mueller)
+ * detect filesystem full by flushing output (Dirk Mueller)
+
+2021-11-19 12:36 Christos Zoulas <christos@zoulas.com>
+
+ * implement running decompressor programs using
+ posix_spawnp(2) instead of vfork(2)
+
+2021-10-24 11:51 Christos Zoulas <christos@zoulas.com>
+
+ * Add support for msdos dates and times
+
+2021-10-20 9:55 Christos Zoulas <christos@zoulas.com>
+
+ * use the system byte swapping functions if available (Werner Fink)
+
+2021-10-18 11:57 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.41
+
+2021-09-23 03:51 Christos Zoulas <christos@zoulas.com>
+
+ * Avinash Sonawane: Fix tzname detection
+
+2021-09-03 09:17 Christos Zoulas <christos@zoulas.com>
+
+ * Fix relationship tests with "search" magic, don't short circuit
+ logic
+
+2021-07-13 01:06 Christos Zoulas <christos@zoulas.com>
+
+ * Fix memory leak in compile mode
+
+2021-07-01 03:51 Christos Zoulas <christos@zoulas.com>
+
+ * PR/272: kiefermat: Only set returnval = 1 when we printed something
+ (in all cases print or !print). This simplifies the logic and fixes
+ the issue in the PR with -k and --mime-type there was no continuation
+ printed before the default case.
+
+2021-06-30 13:07 Christos Zoulas <christos@zoulas.com>
+
+ * PR/270: Don't translate unprintable characters in %s magic formats
+ when -r
+ * PR/269: Avoid undefined behavior with clang (adding offset to NULL)
+
+2021-05-09 18:38 Christos Zoulas <christos@zoulas.com>
+
+ * Add a new flag (f) that requires that the match is a full word,
+ not a partial word match.
+ * Add varint types (unused)
+
+2021-04-19 17:17 Christos Zoulas <christos@zoulas.com>
+
+ * PR/256: mutableVoid: If the file is less than 3 bytes, use the file
+ length to determine type
+ * PR/259: aleksandr.v.novichkov: mime printing through indirect magic
+ is not taken into account, use match directly so that it does.
+
+2021-04-04 17:02 Christos Zoulas <christos@zoulas.com>
+
+ * count the total bytes found not the total byte positions
+ in order to determine encoding (Anatol Belski)
+
+2021-03-30 20:21 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.40
+
+2021-02-05 16:31 Christos Zoulas <christos@zoulas.com>
+
+ * PR/234: Add limit to the number of bytes to scan for encoding
+ * PR/230: Fix /T (trim flag) for regex
+
+2021-02-01 12:31 Christos Zoulas <christos@zoulas.com>
+ * PR/77: Trim trailing separator.
+
+2020-12-17 15:44 Christos Zoulas <christos@zoulas.com>
+
+ * PR/211: Convert system read errors from corrupt ELF
+ files into human readable error messages
+
+2020-12-08 16:24 Christos Zoulas <christos@zoulas.com>
+
+ * fix multithreaded decompression file descriptor issue
+ by using close-on-exec (Denys Vlasenko)
+
+2020-06-27 11:58 Christos Zoulas <christos@zoulas.com>
+
+ * Exclude surrogate pairs from utf-8 detection (Michael Liu)
+
+2020-06-25 12:53 Christos Zoulas <christos@zoulas.com>
+
+ * Include # to the list of ignored format chars (Werner Fink)
+
+2020-06-14 20:02 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.39
+
+2020-06-07 20:00 Christos Zoulas <christos@zoulas.com>
+
+ * Remove unused subtype_mime (Steve Grubb)
+ * Remove unused check in okstat (Steve Grubb)
+ * Fix mime-type in elf binaries by making sure $x is set
+ * Fix indirect negative offsets broken by OFFNEGATIVE
+ * Fix GUID equality check
+ * PR/165: Handle empty array and strings in JSON
+ * PR/162: Add --exclude-quiet
+
+2020-06-06 15:33 Christos Zoulas <christos@zoulas.com>
+
+ * Fix memory leak in ascmagic (Steve Grubb)
+
+2020-06-04 00:21 Christos Zoulas <christos@zoulas.com>
+
+ * Fix string comparison length with ignore whitespace
+
+2020-05-31 00:11 Christos Zoulas <christos@zoulas.com>
+
+ * Fix mingwin 64 compilation
+
+2020-05-30 23:56 Christos Zoulas <christos@zoulas.com>
+
+ * PR/159: whitelist getpid needed for file_pipe2file()
+
+2020-05-09 18:57 Christos Zoulas <christos@zoulas.com>
+
+ * Indicate negative offsets with a flag OFFNEGATIVE
+ so that -0 works.
+ * Introduce "offset" magic type that can be used to
+ detect the file size, and bail on short files.
+ * document DER better in the magic man page.
+
+2020-03-11 21:53 Christos Zoulas <christos@zoulas.com>
+
+ * fix memory leaks (SonarQube)
+
+2020-03-08 21:33 Christos Zoulas <christos@zoulas.com>
+
+ * fix memory leaks (SonarQube)
+ * rewrite confusing loops (SonarQube)
+ * fix bogus test (SonarQube)
+ * pass a sized buffer to file_fmttime() (SonarQube)
+
+ * fix memory leaks (SonarQube)
+
+2020-02-20 15:50 Christos Zoulas <christos@zoulas.com>
+
+ * Don't allow * in printf formats, or the code itself (Christoph Biedl)
+ * Introduce a printf output size checker to avoid DoS attacks
+
+2020-02-17 17:22 Christos Zoulas <christos@zoulas.com>
+
+ * Avoid memory leak on error (oss-fuzz)
+ * Check length of string on DER before derefercing and add new types
+ * Add missing DER string (oss-fuzz)
+
+2020-02-16 20:45 Christos Zoulas <christos@zoulas.com>
+
+ * Add missing DER types, and debugging
+
+2020-02-13 13:10 Christos Zoulas <christos@zoulas.com>
+
+ * PR/140: Avoid abort with hand-crafted magic file (gockelhahn)
+ * PR/139 Avoid DoS in printf with hand-crafted magic file (gockelhahn)
+ * PR/138: Avoid crash with hand-crafted magic file (gockelhahn)
+
+2020-02-12 17:30 Christos Zoulas <christos@zoulas.com>
+
+ * PR/136: Fix static build by adding a libmagic.pc (Fabrice Fontaine)
+
+2019-12-24 14:16 Christos Zoulas <christos@zoulas.com>
+
+ * add guid support
+
+2019-12-16 21:11 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.38
+
+2019-12-15 22:13 Christos Zoulas <christos@zoulas.com>
+ Document changes since the previous release:
+ - Always accept -S (no sandbox) even if we don't support sandboxing
+ - More syscalls elided for sandboxing
+ - For ELF dynamic means having an interpreter not just PT_DYNAMIC
+ - Check for large ELF session header offset
+ - When saving and restoring a locale, keep the locale name in our
+ own storage.
+ - Add a flag to disable CSV file detection.
+ - Don't pass NULL/0 to memset to appease sanitizers.
+ - Avoid spurious prints when looks for extensions or apple strings
+ in fsmagic.
+ - Add builtin decompressors for xz and and bzip.
+ - Add a limit for the number of CDF elements.
+ - More checks for overflow in CDF.
+
+2019-05-14 22:26 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.37
+
+2019-05-09 22:27 Christos Zoulas <christos@zoulas.com>
+
+ * Make sure that continuation separators are printed
+ with -k within softmagic
+
+2019-05-06 22:27 Christos Zoulas <christos@zoulas.com>
+
+ * Change SIGPIPE saving and restoring during compression to use
+ sigaction(2) instead of signal(3) and cache it. (Denys Vlasenko)
+ * Cache stat(2) calls more to reduce number of calls (Denys Vlasenko)
+
+2019-05-06 17:25 Christos Zoulas <christos@zoulas.com>
+
+ * PR/77: Handle --mime-type and -k correctly.
+
+2019-05-03 15:26 Christos Zoulas <christos@zoulas.com>
+
+ * Switch decompression code to use vfork() because
+ tools like rpmdiff and rpmbuild call libmagic
+ with large process footprints (Denys Vlasenko)
+
+2019-04-07 14:05 Christos Zoulas <christos@zoulas.com>
+
+ * PR/75: --enable-zlib, did not work.
+
+2019-02-27 11:54 Christos Zoulas <christos@zoulas.com>
+
+ * Improve regex efficiency (Michael Schroeder) by:
+ 1. Prefixing regex searches with regular search
+ for keywords where possible
+ 2. Using memmem(3) where available
+
+2019-02-20 10:16 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.36
+
+2019-02-19 15:30 Christos Zoulas <christos@zoulas.com>
+
+ * Fix cast to use cast macros
+ * Add UCS-32 builtin detection (PR/61) reported by tmc
+
+2019-02-18 18:24 Christos Zoulas <christos@zoulas.com>
+
+ * Fix stack read (PR/62) and write (PR/64) stack overflows
+ reported by spinpx
+
+2018-10-18 19:32 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.35
+
+2018-09-10 20:38 Christos Zoulas <christos@zoulas.com>
+
+ * Add FreeBSD ELF core file support (John Baldwin)
+
+2018-08-20 18:40 Christos Zoulas <christos@zoulas.com>
+
+ * PR/30: Allow all parameter values to be set (don't treat 0 specially)
+ * handle default annotations on the softmagic match instead at the
+ end.
+
+2018-07-25 10:17 Christos Zoulas <christos@zoulas.com>
+
+ * PR/23: Recognize JSON files
+
+2018-07-25 10:17 Christos Zoulas <christos@zoulas.com>
+
+ * PR/18: file --mime-encoding should not print mime-type
+
+2018-07-25 8:50 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.34
+
+2018-06-22 16:38 Christos Zoulas <christos@zoulas.com>
+
+ * Add Quad indirect offsets
+
+2018-05-24 14:10 Christos Zoulas <christos@zoulas.com>
+
+ * Enable parsing of ELF dynamic sections to handle PIE better
+
+2018-04-15 14:52 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.33
+
+2018-02-24 14:50 Christos Zoulas <christos@zoulas.com>
+
+ * extend the support for ${x?:} expansions for magic descriptions
+
+2018-02-21 16:25 Christos Zoulas <christos@zoulas.com>
+
+ * add support for ${x?:} in mime types to handle
+ pie binaries.
+
+2017-11-03 9:23 Christos Zoulas <christos@zoulas.com>
+
+ * add support for negative offsets (offsets from the end of file)
+
+2017-09-26 8:22 Christos Zoulas <christos@zoulas.com>
+
+ * close the file on error when writing magic (Steve Grubb)
+
+2017-09-24 12:02 Christos Zoulas <christos@zoulas.com>
+
+ * seccomp support (Paul Moore)
+
+2017-09-02 11:53 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.32
+
+2017-08-28 16:37 Christos Zoulas <christos@zoulas.com>
+
+ * Always reset state in {file,buffer}_apprentice (Krzysztof Wilczynski)
+
+2017-08-27 03:55 Christos Zoulas <christos@zoulas.com>
+
+ * Fix always true condition (Thomas Jarosch)
+
+2017-05-24 17:30 Christos Zoulas <christos@zoulas.com>
+
+ * pickier parsing of numeric values in magic files.
+
+2017-05-23 17:55 Christos Zoulas <christos@zoulas.com>
+
+ * PR/615 add magic_getflags()
+
+2017-05-23 13:55 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.31
+
+2017-03-17 20:32 Christos Zoulas <christos@zoulas.com>
+
+ * remove trailing spaces from magic files
+ * refactor is_tar
+ * better bounds checks for cdf
+
+2017-02-10 12:24 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.30
+
+2017-02-07 23:27 Christos Zoulas <christos@zoulas.com>
+
+ * If we exceeded the offset in a search return no match
+ (Christoph Biedl)
+ * Be more lenient on corrupt CDF files (Christoph Biedl)
+
+2017-02-04 16:46 Christos Zoulas <christos@zoulas.com>
+
+ * pacify ubsan sign extension (oss-fuzz/524)
+
+2017-02-01 12:42 Christos Zoulas <christos@zoulas.com>
+
+ * off by one in cdf parsing (PR/593)
+ * report debugging sections in elf (PR/591)
+
+2016-11-06 10:52 Christos Zoulas <christos@zoulas.com>
+
+ * Allow @@@ in extensions
+ * Add missing overflow check in der magic (Jonas Wagner)
+
+2016-10-25 10:40 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.29
+
+2016-10-24 11:20 Christos Zoulas <christos@zoulas.com>
+
+ * der getlength overflow (Jonas Wagner)
+ * multiple magic file load failure (Christoph Biedl)
+
+2016-10-17 11:26 Christos Zoulas <christos@zoulas.com>
+
+ * CDF parsing improvements (Guy Helmer)
+
+2016-07-20 7:26 Christos Zoulas <christos@zoulas.com>
+
+ * Add support for signed indirect offsets
+
+2016-07-18 7:41 Christos Zoulas <christos@zoulas.com>
+
+ * cat /dev/null | file - should print empty (Christoph Biedl)
+
+2016-07-05 15:20 Christos Zoulas <christos@zoulas.com>
+
+ * Bump string size from 64 to 96.
+
+2016-06-13 20:20 Christos Zoulas <christos@zoulas.com>
+
+ * PR/556: Fix separators on annotations.
+
+2016-06-13 19:40 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.28
+ * fix leak on allocation failure
+
+2016-06-01 1:20 Christos Zoulas <christos@zoulas.com>
+
+ * PR/555: Avoid overflow for offset > nbytes
+ * PR/550: Segv on DER parsing:
+ - use the correct variable for length
+ - set offset to 0 on failure.
+
+2016-05-13 12:00 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.27
+
+2016-04-18 9:35 Christos Zoulas <christos@zoulas.com>
+
+ * Errors comparing DER entries or computing offsets
+ are just indications of malformed non-DER files.
+ Don't print them.
+ * Offset comparison was off-by-one.
+ * Fix compression code (Werner Fink)
+ * Put new bytes constant in the right file (not the generated one)
+
+2016-04-16 18:34 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.26
+
+2016-03-31 13:50 Christos Zoulas <christos@zoulas.com>
+
+ * make the number of bytes read from files configurable.
+
+2016-03-21 13:40 Christos Zoulas <christos@zoulas.com>
+
+ * Add bounds checks for DER code (discovered by Thomas Jarosch)
+ * Change indirect recursion limit to indirect use count and
+ bump from 15 to 50 to prevent abuse.
+
+2016-03-13 20:39 Christos Zoulas <christos@zoulas.com>
+
+ * Add -00 which prints filename\0description\0
+
+2016-03-01 13:28 Christos Zoulas <christos@zoulas.com>
+
+ * Fix ID3 indirect parsing
+
+2016-01-19 10:18 Christos Zoulas <christos@zoulas.com>
+
+ * add DER parsing capability
+
+2015-11-13 10:35 Christos Zoulas <christos@zoulas.com>
+
+ * provide dprintf(3) for the OS's that don't have it.
+
+2015-11-11 16:25 Christos Zoulas <christos@zoulas.com>
+
+ * redo the compression code report decompression errors
+
+2015-11-10 23:25 Christos Zoulas <christos@zoulas.com>
+
+ * REG_STARTEND code is not working as expected, delete it.
+
+2015-11-09 16:05 Christos Zoulas <christos@zoulas.com>
+
+ * Add zlib support if we have it.
+
+2015-11-05 11:22 Christos Zoulas <christos@zoulas.com>
+
+ * PR/492: compression forking was broken with magic_buffer.
+
+2015-09-16 9:50 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.25
+
+2015-09-11 13:25 Christos Zoulas <christos@zoulas.com>
+
+ * add a limit to the length of regex searches
+
+2015-09-08 9:50 Christos Zoulas <christos@zoulas.com>
+
+ * fix problems with --parameter (Christoph Biedl)
+
+2015-07-11 10:35 Christos Zoulas <christos@zoulas.com>
+
+ * Windows fixes PR/466 (Jason Hood)
+
+2015-07-09 10:35 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.24
+
+2015-06-11 8:52 Christos Zoulas <christos@zoulas.com>
+
+ * redo long option encoding to fix off-by-one in 5.23
+
+2015-06-10 13:50 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.23
+
+2015-06-09 16:10 Christos Zoulas <christos@zoulas.com>
+
+ * Fix issue with regex range for magic with offset
+ * Always return true from mget with USE (success to mget not match
+ indication). Fixes mime evaluation after USE magic
+ * PR/459: Don't insert magic entries to the list if there are parsing
+ errors for them.
+
+2015-06-03 16:00 Christos Zoulas <christos@zoulas.com>
+
+ * PR/455: Add utf-7 encoding
+
+2015-06-03 14:30 Christos Zoulas <christos@zoulas.com>
+
+ * PR/455: Implement -Z, look inside, but don't report on compression
+ * PR/454: Fix allocation error on bad magic.
+
+2015-05-29 10:30 Christos Zoulas <christos@zoulas.com>
+
+ * handle MAGIC_CONTINUE everywhere, not just in softmagic
+
+2015-05-21 14:30 Christos Zoulas <christos@zoulas.com>
+
+ * don't print descriptions for NAME types when mime.
+
+2015-04-09 15:59 Christos Zoulas <christos@zoulas.com>
+
+ * Add --extension to list the known extensions for this file type
+ Idea by Andrew J Roazen
+
+2015-02-14 12:23 Christos Zoulas <christos@zoulas.com>
+
+ * Bump file search buffer size to 1M.
+
+2015-01-09 14:35 Christos Zoulas <christos@zoulas.com>
+
+ * Fix multiple issues with date formats reported by Christoph Biedl:
+ - T_LOCAL meaning was reversed
+ - Arithmetic did not work
+ Also stop adjusting daylight savings for gmt printing.
+
+2015-01-05 13:00 Christos Zoulas <christos@zoulas.com>
+
+ * PR/411: Fix memory corruption from corrupt cdf file.
+
+2015-01-02 15:15 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.22
+
+2015-01-01 12:01 Christos Zoulas <christos@zoulas.com>
+
+ * add indirect relative for TIFF/Exif
+
+2014-12-16 18:10 Christos Zoulas <christos@zoulas.com>
+
+ * restructure elf note printing to avoid repeated messages
+ * add note limit, suggested by Alexander Cherepanov
+
+2014-12-16 16:53 Christos Zoulas <christos@zoulas.com>
+
+ * Bail out on partial pread()'s (Alexander Cherepanov)
+ * Fix incorrect bounds check in file_printable (Alexander Cherepanov)
+
+2014-12-11 20:01 Christos Zoulas <christos@zoulas.com>
+
+ * PR/405: ignore SIGPIPE from uncompress programs
+ * change printable -> file_printable and use it in
+ more places for safety
+ * in ELF, instead of "(uses dynamic libraries)" when PT_INTERP
+ is present print the interpreter name.
+
+2014-12-10 20:01 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.21
+
+2014-11-27 18:40 Christos Zoulas <christos@zoulas.com>
+
+ * Allow setting more parameters from the command line.
+ * Split name/use and indirect magic recursion limits.
+
+2014-11-27 11:12 Christos Zoulas <christos@zoulas.com>
+
+ * Adjust ELF parameters and the default recursion
+ level.
+ * Allow setting the recursion level dynamically.
+
+2014-11-24 8:55 Christos Zoulas <christos@zoulas.com>
+
+ * The following fixes resulted from Thomas Jarosch's fuzzing
+ tests that revealed severe performance issues on pathological
+ input:
+ - limit number of elf program and sections processing
+ - abort elf note processing quickly
+ - reduce the number of recursion levels from 20 to 10
+ - preserve error messages in indirect magic handling
+
+ This is tracked as CVE-2014-8116 and CVE-2014-8117
+
+2014-11-12 10:30 Christos Zoulas <christos@zoulas.com>
+
+ * fix bogus free in the user buffer case.
+
+2014-11-11 12:35 Christos Zoulas <christos@zoulas.com>
+
+ * fix out of bounds read for pascal strings
+ * fix memory leak (not freeing the head of each mlist)
+
+2014-11-07 10:25 Christos Zoulas <christos@zoulas.com>
+
+ * When printing strings from a file, convert them to printable
+ on a byte by byte basis, so that we don't get issues with
+ locale's trying to interpret random byte streams as UTF-8 and
+ having printf error out with EILSEQ.
+
+2014-10-17 11:48 Christos Zoulas <christos@zoulas.com>
+
+ * fix bounds in note reading (Francisco Alonso / Red Hat)
+
+2014-10-11 15:02 Christos Zoulas <christos@zoulas.com>
+
+ * fix autoconf glue for setlocale and locale_t; some OS's
+ have locale_t in xlocale.h
+
+2014-10-10 15:01 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.20
+
+2014-08-17 10:01 Christos Zoulas <christos@zoulas.com>
+
+ * recognize encrypted CDF documents
+
+2014-08-04 9:18 Christos Zoulas <christos@zoulas.com>
+
+ * add magic_load_buffers from Brooks Davis
+
+2014-07-24 16:40 Christos Zoulas <christos@zoulas.com>
+
+ * add thumbs.db support
+
+2014-06-12 12:28 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.19
+
+2014-06-09 9:04 Christos Zoulas <christos@zoulas.com>
+
+ * Misc buffer overruns and missing buffer size tests in cdf parsing
+ (Francisco Alonso, Jan Kaluza)
+
+2014-06-02 14:50 Christos Zoulas <christos@zoulas.com>
+
+ * Enforce limit of 8K on regex searches that have no limits
+ * Allow the l modifier for regex to mean line count. Default
+ to byte count. If line count is specified, assume a max
+ of 80 characters per line to limit the byte count.
+ * Don't allow conversions to be used for dates, allowing
+ the mask field to be used as an offset.
+
+2014-05-30 12:51 Christos Zoulas <christos@zoulas.com>
+
+ * Make the range operator limit the length of the
+ regex search.
+
+2014-05-14 19:23 Christos Zoulas <christos@zoulas.com>
+
+ * PR/347: Windows fixes
+ * PR/352: Hangul word processor recognition
+ * PR/354: Encoding irregularities in text files
+
+2014-05-06 6:12 Christos Zoulas <christos@zoulas.com>
+
+ * Fix uninitialized title in CDF files (Jan Kaluza)
+
+2014-05-04 14:55 Christos Zoulas <christos@zoulas.com>
+
+ * PR/351: Fix compilation of empty files
+
+2014-04-30 17:39 Christos Zoulas <christos@zoulas.com>
+
+ * Fix integer formats: We don't specify 'l' or
+ 'h' and 'hh' specifiers anymore, only 'll' for
+ quads and nothing for the rest. This is so that
+ magic writing is simpler.
+
+2014-04-01 15:25 Christos Zoulas <christos@zoulas.com>
+
+ * PR/341: Jan Kaluza, fix memory leak
+ * PR/342: Jan Kaluza, fix out of bounds read
+
+2014-03-28 15:25 Christos Zoulas <christos@zoulas.com>
+
+ * Fix issue with long formats not matching fmtcheck
+
+2014-03-26 11:25 Christos Zoulas <christos@zoulas.com>
+
+ * release 5.18
+
+2014-03-15 17:45 Christos Zoulas <christos@zoulas.com>
+
+ * add fmtcheck(3) for those who don't have it
+
+2014-03-14 15:12 Christos Zoulas <christos@zoulas.com>
+
+ * prevent mime entries from being attached to magic
+ entries with no descriptions
+
+ * adjust magic strength for regex type
+
+ * remove superfluous ascmagic with encoding test
+
+2014-03-06 12:01 Christos Zoulas <christos@zoulas.com>
+
+ * fix regression fix echo -ne "\012\013\014" | file -i -
+ which printed "binary" instead of "application/octet-stream"
+
+ * add size_t overflow check for magic file size
+
+2014-02-27 16:01 Christos Zoulas <christos@zoulas.com>
+
+ * experimental support for matching with CFD CLSID
+
+2014-02-18 13:04 Kimmo Suominen (kimmo@suominen.com)
+
+ * Cache old LC_CTYPE locale before setting it to "C", so
+ we can use it to restore LC_CTYPE instead of asking
+ setlocale() to scan the environment variables.
+
+2014-02-12 18:21 Christos Zoulas <christos@zoulas.com>
+
+ * Count recursion levels through indirect magic
+
+2014-02-11 10:40 Christos Zoulas <christos@zoulas.com>
+
+ * Prevent infinite recursion on files with indirect offsets of 0
+
+2014-01-30 21:00 Christos Zoulas <christos@zoulas.com>
+
+ * Add -E flag that makes file print filesystem errors to stderr
+ and exit.
+
+2014-01-08 17:20 Christos Zoulas <christos@zoulas.com>
+
+ * mime printing could print results from multiple magic entries
+ if there were multiple matches.
+ * in some cases overflow was not detected when computing offsets
+ in softmagic.
+
+2013-12-05 12:00 Christos Zoulas <christos@zoulas.com>
+
+ * use strcasestr() to for cdf strings
+ * reset to the "C" locale while doing regex operations, or case
+ insensitive comparisons; this is provisional
+
+2013-11-19 20:10 Christos Zoulas <christos@zoulas.com>
+
+ * always leave magic file loaded, don't unload for magic_check, etc.
+ * fix default encoding to binary instead of unknown which broke recently
+ * handle empty and one byte files, less specially so that
+ --mime-encoding does not break completely.
+ `
+2013-11-06 14:40 Christos Zoulas <christos@zoulas.com>
+
+ * fix erroneous non-zero exit code from non-existent file and message
+
+2013-10-29 14:25 Christos Zoulas <christos@zoulas.com>
+
+ * add CDF MSI file detection (Guy Helmer)
+
+2013-09-03 11:56 Christos Zoulas <christos@zoulas.com>
+
+ * Don't mix errors and regular output if there was an error
+ * in magic_descriptor() don't close the file and try to restore
+ its position
+
+2013-05-30 17:25 Christos Zoulas <christos@zoulas.com>
+
+ * Don't treat magic as an error if offset was past EOF (Christoph Biedl)
+
+2013-05-28 17:25 Christos Zoulas <christos@zoulas.com>
+
+ * Fix spacing issues in softmagic and elf (Jan Kaluza)
+
+2013-05-02 18:00 Christos Zoulas <christos@zoulas.com>
+
+ * Fix segmentation fault with multiple magic_load commands.
+
+2013-04-22 11:20 Christos Zoulas <christos@zoulas.com>
+
+ * The way "default" was implemented was not very useful
+ because the "if something was printed at that level"
+ was not easily controlled by the user, and the format
+ was bound to a string which is too restrictive. Add
+ a "clear" for that level keyword and make "default"
+ void. This way one can do:
+
+ >>13 clear x
+ >>13 lelong 1 foo
+ >>13 lelong 2 bar
+ >>13 default x
+ >>>13 lelong x unknown %x
+
+2013-03-25 13:20 Christos Zoulas <christos@zoulas.com>
+
+ * disallow strength setting in "name" entries
+
+2013-03-06 21:24 Christos Zoulas <christos@zoulas.com>
+
+ * fix recursive magic separator printing
+
+2013-02-26 19:28 Christos Zoulas <christos@zoulas.com>
+
+ * limit recursion level for mget
+ * fix pread() related breakage in cdf
+ * handle offsets properly in recursive "use"
+
+2013-02-18 10:39 Christos Zoulas <christos@zoulas.com>
+
+ * add elf reading of debug info to determine if file is stripped
+ (Jan Kaluza)
+ * use pread()
+
+2013-01-25 18:05 Christos Zoulas <christos@zoulas.com>
+
+ * change mime description size from 64 to 80 to accommodate OOXML.
+
+2013-01-11 14:50 Christos Zoulas <christos@zoulas.com>
+
+ * Warn about inconsistent continuation levels.
+ * Change fsmagic to add a space after it prints.
+
+2013-01-10 21:00 Christos Zoulas <christos@zoulas.com>
+
+ * Make getline public so that file can link against it.
+ Perhaps it is better to rename it, or hide it differently.
+ Fixes builds on platforms that do not provide it.
+
+2013-01-07 16:30 Christos Zoulas <christos@zoulas.com>
+
+ * Add SuS d{,1,2,4,8}, u{,1,2,4,8} and document
+ what long, int, short, etc is (Guy Harris)
+
+2013-01-06 11:20 Christos Zoulas <christos@zoulas.com>
+
+ * add magic_version function and constant
+ * Redo memory allocation and de-allocation.
+ (prevents double frees on non mmap platforms)
+ * Fix bug with name/use having to do with passing
+ found state from the parent to the child and back.
+
+2012-12-19 8:47 Christos Zoulas <christos@zoulas.com>
+
+ * Only print elf capabilities for archs we know (Jan Kaluza)
+
+2012-10-30 19:14 Christos Zoulas <christos@zoulas.com>
+
+ * Add "name" and "use" file types in order to look
+ inside mach-o files.
+
+2012-09-06 10:40 Christos Zoulas <christos@zoulas.com>
+
+ * make --version exit 0 (Matthew Schultz)
+ * add string/T (Jan Kaluza)
+
+2012-08-09 2:15 Christos Zoulas <christos@zoulas.com>
+
+ * add z and t modifiers for our own vasprintf
+ * search for $HOME/.magic.mgc if it is there first
+ * fix reads from a pipe, and preserve errno
+
+2012-05-15 13:12 Christos Zoulas <christos@zoulas.com>
+
+ * use ctime_r, asctime_r
+
+2012-04-06 17:18 Christos Zoulas <christos@zoulas.com>
+
+ * Fixes for indirect offsets to handle apple disk formats
+
+2012-04-03 18:26 Christos Zoulas <christos@zoulas.com>
+
+ * Add windows date field types
+ * More info for windows shortcuts (incomplete)
+
+2012-02-20 17:33 Christos Zoulas <christos@zoulas.com>
+
+ * Fix CDF parsing issues found by CERT's fuzzing tool (Will Dormann)
+
+2011-12-15 12:17 Chris Metcalf <cmetcalf@tilera.com>
+
+ * Support Tilera architectures (tile64, tilepro, tilegx).
+
+2011-12-16 16:33 Reuben Thomas <rrt@sc3d.org>
+
+ * Add magic for /usr/bin/env Perl scripts
+ * Weaken generic script magic to avoid clashing with
+ language-specific magic.
+
+2011-12-08 13:37 Reuben Thomas <rrt@sc3d.org>
+
+ * Simplify if (p) free(p) to free(p).
+
+2011-12-08 13:07 Reuben Thomas <rrt@sc3d.org>
+
+ * Remove hardwired token finding (names.h), turning it into soft
+ magic. Patterns are either anchored regexs or search/8192. English
+ language detection and PL/1 detection have been removed as they
+ were too fragile. -e tokens is still accepted for backwards
+ compatibility.
+ * Move 3ds patterns (which are commented out anyway) into autodesk
+ (they were, oddly, in c-lang).
+
+2011-12-06 00:16 Reuben Thomas <rrt@sc3d.org>
+
+ * Tweak strength of generic hash-bang detectors to be less than
+ specific ones.
+ * Make an inconsistent description of Python scripts consistent.
+
+2011-12-05 23:58 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix minor error in file(1).
+
+2011-11-05 00:00 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix issue #150 (I hope).
+
+2011-09-22 12:57 Christos Zoulas <christos@zoulas.com>
+
+ * Python3 binding fixes from Kelly Anderson
+
+2011-09-20 11:32 Christos Zoulas <christos@zoulas.com>
+
+ * If a string type magic entry is marked as text or binary
+ only match text files against text entries and binary
+ files against binary entries.
+
+2011-09-01 12:12 Christos Zoulas <christos@zoulas.com>
+
+ * Don't wait for any subprocess, just the one we forked.
+
+2011-08-26 16:40 Christos Zoulas <christos@zoulas.com>
+
+ * If the application name is not set in a cdf file, try to see
+ if it has a directory with the application name on it.
+
+2011-08-17 14:32 Christos Zoulas <christos@zoulas.com>
+
+ * Fix ELF lseek(2) madness. Inspired by PR/134 by Jan Kaluza
+
+2011-08-14 09:03 Christos Zoulas <christos@zoulas.com>
+
+ * Don't use variable string formats.
+
+2011-07-12 12:32 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix detection of Zip files (Mantis #128).
+ * Make some minor improvements to file(1).
+ * Rename MIME types for filesystem objects for consistency with
+ xdg-utils. Typically this means that application/x-foo becomes
+ inode/foo, but some names also change slightly, e.g.
+ application/x-character-device becomes inode/chardevice.
+
+2011-05-10 20:57 Christos Zoulas <christos@zoulas.com>
+
+ * fix mingw compilation (Abradoks)
+
+2011-05-10 20:57 Christos Zoulas <christos@zoulas.com>
+
+ * remove patchlevel.h
+ * Fix read past allocated memory caused by double-incrementing
+ a pointer in a loop (reported by Roberto Maar)
+
+2011-03-30 15:45 Christos Zoulas <christos@zoulas.com>
+
+ * Fix cdf string buffer setting (Sven Anders)
+
+2011-03-20 16:35 Christos Zoulas <christos@zoulas.com>
+
+ * Eliminate MAXPATHLEN and use dynamic allocation for
+ path and file buffers.
+
+2011-03-15 18:15 Christos Zoulas <christos@zoulas.com>
+
+ * binary tests on magic entries with masks could spuriously
+ get converted to ascii.
+
+2011-03-12 18:06 Reuben Thomas <rrt@sc3d.org>
+
+ * Improve file.man (remove BUGS, present email addresses consistently).
+
+2011-03-07 19:38 Christos Zoulas <christos@zoulas.com>
+
+ * add lrzip support (from Ville Skytta)
+
+2011-02-10 16:36 Christos Zoulas <christos@zoulas.com>
+
+ * fix CDF bounds checking (Guy Helmer)
+
+2011-02-10 12:03 Christos Zoulas <christos@zoulas.com>
+
+ * add cdf_ctime() that prints a meaningful error when time cannot
+ be converted.
+
+2011-02-02 20:40 Christos Zoulas <christos@zoulas.com>
+
+ * help and version output to stdout.
+
+ * When matching softmagic for ascii files, don't just print
+ the softmagic classification, keep going and print the
+ text classification too. This fixes broken troff files when
+ we moved them from keyword recognition to softmagic
+ (they stopped printing "with CRLF" etc.)
+ Reported by Doug McIlroy.
+
+2011-01-16 19:31 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix two potential buffer overruns in apprentice_list.
+
+2011-01-14 22:33 Reuben Thomas <rrt@sc3d.org>
+
+ * New Python binding in pure Python.
+ * Update libmagic(3).
+
+2011-01-06 21:40 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix Python bindings (including recent Python 3 compatibility
+ update).
+
+2011-01-04 18:43 Reuben Thomas <rrt@sc3d.org>
+
+ * magic/Makefile.am: make it easier to recover from magic build failures.
+ * Fix pstring length specifier parsing to avoid generating invalid
+ magic files.
+ * Add pstring length "J" (for "JPEG") to specify that the length
+ include itself.
+ * Fix JPEG comment parsing at last using pstring/HJ!
+ * Ignore section 5 man pages in doc/.cvsignore.
+
+2010-12-22 13:12 Christos Zoulas <christos@zoulas.com>
+
+ * Add pstring/BHhLl to specify the type of the length of pascal
+ strings.
+
+2010-11-26 18:39 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix "-e soft": it was ignored when softmagic was called
+ during asciimagic.
+ * Improve comments and use "unsigned char" in tar.h/is_tar.c.
+
+2010-11-05 17:26 Reuben Thomas <rrt@sc3d.org>
+
+ * Make bug reporting addresses more visible.
+
+2010-11-01 18:35 Reuben Thomas <rrt@sc3d.org>
+
+ * Add tcl magic from Gustaf Neumann
+
+2010-10-24 10:42 Christos Zoulas <christos@zoulas.com>
+
+ * Fix the whitespace comparing code (Christopher Chittleborough)
+
+2010-10-06 21:05 Christos Zoulas <christos@zoulas.com>
+
+ * allow string/t to work (Jan Kaluza)
+
+2010-09-20 22:11 Reuben Thomas <rrt@sc3d.org>
+
+ * Apply some patches from Ubuntu and Fedora.
+
+2010-09-20 21:16 Reuben Thomas <rrt@sc3d.org>
+
+ * Apply all patches from Debian package 5.04-6 which have not
+ already been applied and are not Debian-specific.
+
+2010-09-20 15:24 Reuben Thomas <rrt@sc3d.org>
+
+ * Minor security fix to softmagic.c (don't use untrusted
+ string as printf format).
+
+2010-07-21 12:20 Christos Zoulas <christos@zoulas.com>
+
+ * MINGW32 portability from LRN
+
+ * Don't warn about escaping magic regex chars when we are in a regex.
+
+2010-07-19 10:55 Christos Zoulas <christos@zoulas.com>
+
+ * Only try to print prpsinfo for core files. (Jan Kaluza)
+
+2010-04-22 12:55 Christos Zoulas <christos@zoulas.com>
+
+ * Try more elf offsets for Debian core files. (Arnaud Giersch)
+
+2010-02-20 15:18 Reuben Thomas <rrt@sc3d.org>
+
+ * Clarify which sort of CDF we mean.
+
+2010-02-14 22:58 Reuben Thomas <rrt@sc3d.org>
+
+ * Re-jig Zip file type magic so that unsupported special
+ Zip types (those with "mimetype" at offset 30) can be
+ recognized.
+
+2010-02-02 21:50 Reuben Thomas <rrt@sc3d.org>
+
+ * Add support for OCF (EPUB) files (application/epub+zip)
+
+2010-01-28 18:25 Christos Zoulas <christos@zoulas.com>
+
+ * Fix core-dump from unbound loop:
+ https://bugzilla.redhat.com/show_bug.cgi?id=533245
+
+2010-01-22 15:45 Christos Zoulas <christos@zoulas.com>
+
+ * print proper mime for crystal reports file
+
+ * print the last summary information of a cdf document, not the
+ first so that nested documents print the right info
+
+2010-01-16 18:42 Charles Longeau <chl@tuxfamily.org>
+
+ * bring back some fixes from OpenBSD:
+ - make gcc2 builds file
+ - fix typos in a magic file comment
+
+2009-11-17 18:35 Christos Zoulas <christos@zoulas.com>
+
+ * ctime/asctime can return NULL on some OS's although
+ they should not (Toshit Antani)
+
+2009-09-14 13:49 Christos Zoulas <christos@zoulas.com>
+
+ * Centralize magic path handling routines and remove the
+ special-casing from file.c so that the python module for
+ example comes up with the same magic path (Fixes ~/.magic
+ handling) (from Gab)
+
+2009-09-11 23:38 Reuben Thomas <rrt@sc3d.org>
+
+ * When magic argument is a directory, read the files in
+ strcmp-sorted order (fixes Debian bug #488562 and our own FIXME).
+
+2009-09-11 13:11 Reuben Thomas <rrt@sc3d.org>
+
+ * Combine overlapping epoc and psion magic files into one (epoc).
+
+ * Add some more EPOC MIME types.
+
+2009-08-19 15:55 Christos Zoulas <christos@zoulas.com>
+
+ * Fix 3 bugs (From Ian Darwin):
+ - file_showstr could move one past the end of the array
+ - parse_apple did not nul terminate the string in the overflow case
+ - parse_mime truncated the wrong string in the overflow case
+
+2009-08-12 12:28 Robert Byrnes <byrnes@wildpumpkin.net>
+
+ * Include Localstuff when compiling magic.
+
+2009-07-15 10:05 Christos Zoulas <christos@zoulas.com>
+
+ * Fix logic for including mygetopts.h
+
+ * Make cdf.c compile again with debugging
+
+ * Add the necessary field handling for crystal reports files to work
+
+2009-06-23 01:34 Reuben Thomas <rrt@sc3d.org>
+
+ * Stop "(if" identifying Lisp files, that's plain dumb!
+
+2009-06-09 22:13 Reuben Thomas <rrt@sc3d.org>
+
+ * Add a couple of missing MP3 MIME types.
+
+2009-05-27 23:00 Reuben Thomas <rrt@sc3d.org>
+
+ * Add full range of hash-bang tests for Python and Ruby.
+
+ * Add MIME types for Python and Ruby scripts.
+
+2009-05-13 10:44 Christos Zoulas <christos@zoulas.com>
+
+ * off by one in parsing hw capabilities in elf
+ (Cheng Renquan)
+
+2009-05-08 13:40 Christos Zoulas <christos@zoulas.com>
+
+ * lint fixes and more from NetBSD
+
+2009-05-06 10:25 Christos Zoulas <christos@zoulas.com>
+
+ * Avoid null dereference in cdf code (Drew Yao)
+
+ * More cdf bounds checks and overflow checks
+
+2009-05-01 18:37 Christos Zoulas <christos@zoulas.com>
+
+ * Buffer overflow fixes from Drew Yao
+
+2009-04-30 17:10 Christos Zoulas <christos@zoulas.com>
+
+ * Fix more cdf lossage. All the documents I have
+ right now print the correct information.
+
+2009-03-27 18:43 Christos Zoulas <christos@zoulas.com>
+
+ * don't print \012- separators in the same magic entry
+ if it consists of multiple magic printing lines.
+
+2009-03-23 10:20 Christos Zoulas <christos@zoulas.com>
+
+ * Avoid file descriptor leak in compress code from
+ (Daniel Novotny)
+
+2009-03-18 16:50 Christos Zoulas <christos@zoulas.com>
+
+ * Allow escaping of relation characters, so that we can say \^[A-Z]
+ and the ^ is not eaten as a relation char.
+
+ * Fix troff and fortran to their previous glory using
+ regex. This was broken since their removel from ascmagic.
+
+2009-03-10 16:50 Christos Zoulas <christos@zoulas.com>
+
+ * don't use strlen in strndup() (Toby Peterson)
+
+2009-03-10 7:45 Christos Zoulas <christos@zoulas.com>
+
+ * avoid c99 syntax.
+
+2009-02-23 15:45 Christos Zoulas <christos@zoulas.com>
+
+ * make the cdf code use the buffer first if available,
+ and then the fd code.
+
+2009-02-13 13:45 Christos Zoulas <christos@zoulas.com>
+
+ * look for struct option to determine if getopt.h is usable for IRIX.
+
+ * sanitize cdf document strings
+
+2009-02-04 13:25 Christos Zoulas <christos@zoulas.com>
+
+ * fix OS/2 warnings.
+
+2008-12-12 15:50 Christos Zoulas <christos@zoulas.com>
+
+ * fix initial offset calculation for non 4K sector files
+
+ * add loop limits to avoid DoS attacks by constructing
+ looping sector references.
+
+2008-12-03 13:05 Christos Zoulas <christos@zoulas.com>
+
+ * fix memory botches on cdf file parsing.
+
+ * exit with non-zero value for any error, not just for the last
+ file processed.
+
+2008-11-09 20:42 Charles Longeau <chl@tuxfamily.org>
+
+ * Replace all str{cpy,cat} functions with strl{cpy,cat}
+ * Ensure that strl{cpy,cat} are included in libmagic,
+ as needed.
+
+2008-11-06 18:18 Christos Zoulas <christos@zoulas.com>
+
+ * Handle ID3 format files.
+
+2008-11-06 23:00 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix --mime, --mime-type and --mime-encoding under new scheme.
+
+ * Rename "ascii" to "text" and add "encoding" test.
+
+ * Return a precise ("utf-16le" or "utf-16be") MIME charset for
+ UTF-16.
+
+ * Fix error in comment caused by automatic indentation adding
+ words!
+
+2008-11-06 10:35 Christos Zoulas <christos@astron.com>
+
+ * use memchr instead of strchr because the string
+ might not be NUL terminated (Scott MacVicar)
+
+2008-11-03 07:31 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix a printf with a non-literal format string.
+
+ * Fix formatting and punctuation of help for "--apple".
+
+2008-10-30 11:00 Reuben Thomas <rrt@sc3d.org>
+
+ * Correct words counts in comments of struct magic.
+
+ * Fix handle_annotation to allow both Apple and MIME types to be
+ printed, and to return correct code if MIME type is
+ printed (1, not 0) or if there's an error (-1 not 1).
+
+ * Fix output of charset for MIME type (precede with semi-colon;
+ fixes Debian bug #501460).
+
+ * Fix potential attacks via conversion specifications in magic
+ strings.
+
+ * Add a FIXME for Debian bug #488562 (magic files should be
+ read in a defined order, by sorting the names).
+
+2008-10-18 16:45 Christos Zoulas <christos@astron.com>
+
+ * Added APPLE file creator/type
+
+2008-10-12 10:20 Christos Zoulas <christos@astron.com>
+
+ * Added CDF parsing
+
+2008-10-09 16:40 Christos Zoulas <christos@astron.com>
+
+ * filesystem and msdos patches (Joerg Jenderek)
+
+2008-10-09 13:20 Christos Zoulas <christos@astron.com>
+
+ * correct --exclude documentation issues: remove troff and fortran
+ and rename "token" to "tokens". (Randy McMurchy)
+
+2008-10-01 10:30 Christos Zoulas <christos@astron.com>
+
+ * Read ~/.magic in addition to the default magic file not instead
+ of, as documented in the man page.
+
+2008-09-10 21:30 Reuben Thomas <rrt@sc3d.org>
+
+ * Comment out graphviz patterns, as they match too many files.
+
+2008-08-30 12:54 Christos Zoulas <christos@astron.com>
+
+ * Don't eat trailing \n in magic enties.
+
+ * Cast defines to allow compilation using a c++ compiler.
+
+2008-08-25 23:56 Reuben Thomas <rrt@sc3d.org>
+
+ * Add text/x-lua MIME type for Lua scripts.
+
+ * Escape { in regex in graphviz patterns.
+
+2008-07-26 00:59 Reuben Thomas <rrt@sc3d.org>
+
+ * Add MIME types for special files.
+
+ * Use access to give more accurate information for files that
+ can't be opened.
+
+ * Add a TODO list.
+
+2008-07-02 11:15 Christos Zoulas <christos@astron.com>
+
+ * add !:strength op to adjust magic strength (experimental)
+
+2008-06-16 21:41 Reuben Thomas <rrt@sc3d.org>
+
+ * Fix automake error in configure.ac.
+
+ * Add MIME type for Psion Sketch files.
+
+2008-06-05 08:59 Christos Zoulas <christos@astron.com>
+
+ * Don't print warnings about bad namesize in stripped
+ binaries with PT_NOTE is still there, and the actual
+ note is gone (Jakub Jelinek)
+
+2008-05-28 15:12 Robert Byrnes <byrnes@wildpumpkin.net>
+
+ * magic/Magdir/elf:
+ Note invalid byte order for little-endian SPARC32PLUS.
+ Add SPARC V9 vendor extensions and memory model.
+
+ * src/elfclass.h:
+ Pass target machine to doshn (for Solaris hardware capabilities).
+
+ * src/readelf.c (doshn):
+ Add support for Solaris hardware/software capabilities.
+
+ * src/readelf.h:
+ Ditto.
+
+ * src/vasprintf.c (dispatch):
+ Add support for ll modifier.
+
+2008-05-16 10:25 Christos Zoulas <christos@astron.com>
+
+ * Fix compiler warnings.
+
+ * remove stray printf, and fix a vprintf bug. (Martin Dorey)
+
+2008-05-06 00:13 Robert Byrnes <byrnes@wildpumpkin.net>
+
+ * src/Makefile.am:
+ Ensure that getopt_long and [v]asprintf are included in libmagic,
+ as needed.
+
+ Remove unnecessary EXTRA_DIST.
+
+ * src/Makefile.in:
+ Rerun automake.
+
+ * src/vasprintf.c (dispatch):
+ Fix variable precision bug: be sure to step past '*'.
+
+ * src/vasprintf.c (core):
+ Remove unreachable code.
+
+ * src/apprentice.c (set_test_type):
+ Add cast to avoid compiler warning.
+
+2008-04-22 23:45 Christos Zoulas <christos@astron.com>
+
+ * Add magic submission guidelines (Abel Cheung)
+
+ * split msdos and windows magic (Abel Cheung)
+
+2008-04-04 11:00 Christos Zoulas <christos@astron.com>
+
+ * >= <= is not supported, so fix the magic and warn about it.
+ reported by: Thien-Thi Nguyen <ttn@gnuvola.org>
+
+2008-03-27 16:16 Robert Byrnes <byrnes@wildpumpkin.net>
+
+ * src/readelf.c (donote):
+ ELF core file command name/line bug fixes and enhancements:
+
+ Try larger offsets first to avoid false matches
+ from earlier data that happen to look like strings;
+ this primarily affected SunOS 5.x 32-bit Intel core files.
+
+ Add support for command line (instead of just short name)
+ for SunOS 5.x.
+
+ Add information about NT_PSINFO for SunOS 5.x.
+
+ Only trim whitespace from end of command line.
+
+2007-02-11 01:36 Reuben Thomas <rrt@sc3d.org>
+
+ * Change strength of ! from MULT to 0, as it matches almost
+ anything (Reuben Thomas)
+
+ * Debian fixes (Reuben Thomas)
+
+2007-02-11 00:17 Reuben Thomas <rrt@sc3d.org>
+
+ * Clarify UTF-8 BOM message (Reuben Thomas)
+
+ * Add HTML comment to token list in names.h
+
+2007-02-04 15:50 Christos Zoulas <christos@astron.com>
+
+ * Debian fixes (Reuben Thomas)
+
+2007-02-04 11:31 Christos Zoulas <christos@astron.com>
+
+ * !:mime annotations in magic files (Reuben Thomas)
+
+2007-01-29 15:35 Christos Zoulas <christos@astron.com>
+
+ * zero out utime/utimes structs (Gavin Atkinson)
+
+2007-01-26 13:45 Christos Zoulas <christos@astron.com>
+
+ * reduce writable data from Diego "Flameeyes" Petten
+
+2007-12-28 15:06 Christos Zoulas <christos@astron.com>
+
+ * strtof detection
+
+ * remove bogus regex magic that could cause a DoS
+
+ * better mismatch version message
+
+2007-12-27 11:35 Christos Zoulas <christos@astron.com>
+
+ * bring back some fixes from OpenBSD
+
+ * treat ELF dynamic objects as executables
+
+ * fix gcc warnings
+
+2007-12-01 19:55 Christos Zoulas <christos@astron.com>
+
+ * make sure we have zlib.h and libz to compile the builtin
+ decompress code
+
+2007-10-28 20:48 Christos Zoulas <christos@astron.com>
+
+ * float and double magic support (Behan Webster)
+
+2007-10-28 20:48 Christos Zoulas <christos@astron.com>
+
+ * Convert fortran to a soft test (Reuben Thomas)
+
+2007-10-23 5:25 Christos Zoulas <christos@astron.com>
+
+ * Add --with-filename, and --no-filename (Reuben Thomas)
+
+2007-10-23 3:59 Christos Zoulas <christos@astron.com>
+
+ * Rest of the mime split (Reuben Thomas)
+
+ * Make usage message generated from the flags so that
+ they stay consistent (Reuben Thomas)
+
+2007-10-20 3:06 Christos Zoulas <christos@astron.com>
+
+ * typo in comment, missing ifdef QUICK, remove unneeded code
+ (Charles Longeau)
+
+2007-10-17 3:33 Christos Zoulas <christos@astron.com>
+
+ * Fix problem printing -\012 in some entries
+
+ * Separate magic type and encoding flags (Reuben Thomas)
+
+2007-10-09 3:55 Christos Zoulas <christos@astron.com>
+
+ * configure fix for int64 and strndup (Reuben Thomas)
+
+2007-09-26 4:45 Christos Zoulas <christos@astron.com>
+
+ * Add magic_descriptor() function.
+
+ * Fix regression in elf reading code where the core name was
+ not being printed.
+
+ * Don't convert NUL's to spaces in {l,b}estring16 (Daniel Dawson)
+
+2007-08-19 6:30 Christos Zoulas <christos@astron.com>
+
+ * Make mime format consistent so that it can
+ be easily parsed:
+ mimetype [charset=character-set] [encoding=encoding-mime-type]
+
+ Remove spurious extra text from some MIME type printouts
+ (mostly in is_tar).
+
+ Fix one case where -i produced nothing at all (for a 1-byte file,
+ which is now classed as application/octet-stream).
+
+ Remove 7/8bit classifications, since they were arbitrary
+ and not based on the file data.
+
+ This work was done by Reuben Thomas
+
+2007-05-24 10:00 Christos Zoulas <christos@astron.com>
+
+ * Fix another integer overflow (Colin Percival)
+
+2007-03-26 13:58 Christos Zoulas <christos@astron.com>
+
+ * make sure that all of struct magic_set is initialized appropriately
+ (Brett)
+
+2007-03-25 17:44 Christos Zoulas <christos@astron.com>
+
+ * reset left bytes in the buffer (Dmitry V. Levin)
+
+ * compilation failed with COMPILE_ONLY and ENABLE_CONDITIONALS
+ (Peter Avalos)
+
+2007-03-15 10:51 Christos Zoulas <christos@astron.com>
+
+ * fix fortran and nroff reversed tests (Dmitry V. Levin)
+
+ * fix exclude option (Dmitry V. Levin)
+
+2007-02-08 17:30 Christos Zoulas <christos@astron.com>
+
+ * fix integer underflow in file_printf which can lead to
+ to exploitable heap overflow (Jean-Sebastien Guay-Lero)
+
+2007-02-05 11:35 Christos Zoulas <christos@astron.com>
+
+ * make socket/pipe reading more robust
+
+2007-01-25 16:01 Christos Zoulas <christos@astron.com>
+
+ * Centralize all the tests in file_buffer.
+
+ * Add exclude flag.
+
+2007-01-18 05:29 Anon Ymous <do@not.spam.me>
+
+ * Move the "type" detection code from parse() into its own table
+ driven routine. This avoids maintaining multiple lists in
+ file.h.
+
+ * Add an optional conditional field (ust before the type field).
+ This code is wrapped in "#ifdef ENABLE_CONDITIONALS" as it is
+ likely to go away.
+
+2007-01-16 23:24 Anon Ymous <do@not.spam.me>
+
+ * Fix an initialization bug in check_mem().
+
+2007-01-16 14:58 Anon Ymous <do@not.spam.me>
+
+ * Add a "default" type to print a message if nothing previously
+ matched at that level or since the last default at that
+ level. This is useful for setting up switch-like statements.
+ It can also be used to do if/else constructions without a
+ redundant second test.
+
+ * Fix the "x" special case test so that one can test for that
+ string with "=x".
+
+ * Allow "search" to search the entire buffer if the "/N"
+ search count is missing.
+
+ * Make "regex" work! It now starts its search at the
+ specified offset and takes an (optional) "/N" line count to
+ specify the search range; otherwise it searches to the end
+ of the file. The match is now grabbed correctly for format
+ strings and the offset set to the end of the match.
+
+ * Add a "/s" flag to "regex" and "search" to set the offset to
+ the start of the match. By default the offset is set to the
+ end of the match, as it is with other tests. This is mostly
+ useful for "regex".
+
+ * Make "search", "string" and "pstring" use the same
+ file_strncmp() routine so that they support the same flags;
+ "bestring16" and "lestring16" call the same routine, but
+ with flags = 0. Also add a "/C" flag (in analogy to "/c")
+ to ignore the case on uppercase (lowercase) characters in
+ the test string.
+
+ * Strict adherence to C style string escapes. A warnings are
+ printed when compiling. Note: previously "\a" was
+ incorrectly translated to 'a' instead of an <alert> (i.e.,
+ BELL, typically 0x07).
+
+ * Make this compile with "-Wall -Wextra" and all the warning
+ flags used with WARNS=4 in the NetBSD source. Also make it
+ pass lint.
+
+ * Many "cleanups" and hopefully not too many new bugs!
+
+2007-01-16 14:56 Anon Ymous <do@not.spam.me>
+
+ * make several more files compile with gcc warnings
+ on and also make them pass lint.
+
+2007-01-16 14:54 Anon Ymous <do@not.spam.me>
+
+ * fix a puts()/putc() usage goof in file.c
+
+ * make file.c compile with gcc warnings and pass lint
+
+2006-12-11 16:49 Christos Zoulas <christos@astron.com>
+
+ * fix byteswapping issue
+
+ * report the number of bytes we tried to
+ allocate when allocation fails
+
+ * add a few missed cases in the strength routine
+
+2006-12-08 16:32 Christos Zoulas <christos@astron.com>
+
+ * store and print the line number of the magic
+ entry for debugging.
+
+ * if the magic entry did not print anything,
+ don't treat it as a match
+
+ * change the magic strength algorithm to take
+ into account the relationship op.
+
+ * fix a bug in search where we could accidentally
+ return a match.
+
+ * propagate the error return from match to
+ file_softmagic.
+
+2006-11-25 13:35 Christos Zoulas <christos@astron.com>
+
+ * Don't store the current offset in the magic
+ struct, because it needs to be restored and
+ it was not done properly all the time. Bug
+ found by: Arkadiusz Miskiewicz
+
+ * Fix problem in the '\0' separator; and don't
+ print it as an additional separator; print
+ it as the only separator.
+
+2006-11-17 10:51 Christos Zoulas <christos@astron.com>
+
+ * Added a -0 option to print a '\0' separator
+ Etienne Buira <etienne.buira@free.fr>
+
+2006-10-31 15:14 Christos Zoulas <christos@astron.com>
+
+ * Check offset before copying (Mike Frysinger)
+
+ * merge duplicated code
+
+ * add quad date support
+
+ * make sure that we nul terminate desc (Ryoji Kanai)
+
+ * don't process elf notes multiple times
+
+ * allow -z to report empty compressed files
+
+ * use calloc to initialize the ascii buffers (Jos van den Oever)
+
+2006-06-08 11:11 Christos Zoulas <christos@astron.com>
+
+ * QNX fixes (Mike Gorchak)
+
+ * Add quad support.
+
+ * FIFO checks (Dr. Werner Fink)
+
+ * Linux ELF fixes (Dr. Werner Fink)
+
+ * Magic format checks (Dr. Werner Fink)
+
+ * Magic format function improvement (Karl Chen)
+
+2006-05-03 11:11 Christos Zoulas <christos@astron.com>
+
+ * Pick up some elf changes and some constant fixes from SUSE
+
+ * Identify gnu tar vs. posix tar
+
+ * When keep going, don't print spurious newlines (Radek Vokal)
+
+2006-04-01 12:02 Christos Zoulas <christos@astron.com>
+
+ * Use calloc instead of malloc (Mike Frysinger)
+
+ * Fix configure script to detect wctypes.h (Mike Frysinger)
+
+2006-03-02 16:06 Christos Zoulas <christos@astron.com>
+
+ * Print empty if the file is (Mike Frysinger)
+
+ * Don't try to read past the end of the buffer (Mike Frysinger)
+
+ * Sort magic entries by strength [experimental]
+
+2005-11-29 13:26 Christos Zoulas <christos@astron.com>
+
+ * Use iswprint() to convert the output string.
+ (Bastien Nocera)
+
+2005-10-31 8:54 Christos Zoulas <christos@astron.com>
+
+ * Fix regression where the core info was not completely processed
+ (Radek Vokal)
+
+2005-10-20 11:15 Christos Zoulas <christos@astron.com>
+
+ * Middle Endian magic (Diomidis Spinellis)
+
+2005-10-17 11:15 Christos Zoulas <christos@astron.com>
+
+ * Open with O_BINARY for CYGWIN (Corinna Vinschen)
+
+ * Don't close stdin (Arkadiusz Miskiewicz)
+
+ * Look for note sections in non executables.
+
+2005-09-20 13:33 Christos Zoulas <christos@astron.com>
+
+ * Don't print SVR4 Style in core files multiple times
+ (Radek Vokal)
+
+2005-08-27 04:09 Christos Zoulas <christos@astron.com>
+
+ * Cygwin changes Corinna Vinschen
+
+2005-08-18 09:53 Christos Zoulas <christos@astron.com>
+
+ * Remove erroreous mention of /etc/magic in the file man page
+ This is gentoo bug 101639. (Mike Frysinger)
+
+ * Cross-compile support and detection (Mike Frysinger)
+
+2005-08-12 10:17 Christos Zoulas <christos@astron.com>
+
+ * Add -h flag and dereference symlinks if POSIXLY_CORRECT
+ is set.
+
+2005-07-29 13:57 Christos Zoulas <christos@astron.com>
+
+ * Avoid search and regex buffer overflows (Kelledin)
+
+2005-07-12 11:48 Christos Zoulas <christos@astron.com>
+
+ * Provide stub implementations for {v,}nsprintf() for older
+ OS's that don't have them.
+ * Change mbstate_t autoconf detection macro from AC_MBSTATE_T
+ to AC_TYPE_MBSTATE_T.
+
+2005-06-25 11:48 Christos Zoulas <christos@astron.com>
+
+ * Dynamically allocate the string buffers and make the
+ default read size 256K.
+
+2005-06-01 00:00 Joerg Sonnenberger <joerg@britannica.bec.de>
+
+ * Dragonfly ELF note support
+
+2005-03-14 00:00 Giuliano Bertoletti <gb@symbolic.it>
+
+ * Avoid NULL pointer dereference in time conversion.
+
+2005-03-06 00:00 Joerg Walter <jwalt@mail.garni.ch>
+
+ * Add indirect magic offset support, and search mode.
+
+2005-01-12 00:00 Stepan Kasal <kasal@ucw.cz>
+
+ * src/ascmagic.c (file_ascmagic): Fix three bugs about text files:
+ If a CRLF text file happens to have CR at offset HOWMANY - 1
+ (currently 0xffff), it should not be counted as CR line
+ terminator.
+ If a line has length exactly MAXLINELEN, it should not yet be
+ treated as a ``very long line'', as MAXLINELEN is ``longest sane
+ line length''.
+ With CRLF, the line length was not computed correctly, and even
+ lines of length MAXLINELEN - 1 were treated as ``very long''.
+
+2004-12-07 14:15 Christos Zoulas <christos@astron.com>
+
+ * bzip2 needs a lot of input buffer space on some files
+ before it can begin uncompressing. This makes file -z
+ fail on some bz2 files. Fix it by giving it a copy of
+ the file descriptor to read as much as it wants if we
+ have access to it. <christos@astron.com>
+
+2004-11-24 12:39 Christos Zoulas <christos@astron.com>
+
+ * Stack smash fix, and ELF more conservative reading.
+ Jakub Bogusz <qboosh@pld-linux.org>
+
+2004-11-20 18:50 Christos Zoulas <christos@astron.com>
+
+ * New FreeBSD version parsing code:
+ Jon Noack <noackjr@alumni.rice.edu>
+
+ * Hackish support for ucs16 strings <christos@astron.com>
+
+2004-11-13 03:07 Christos Zoulas <christos@astron.com>
+
+ * print the file name and line number in syntax errors.
+
+2004 10-12 10:50 Christos Zoulas <christos@astron.com>
+
+ * Fix stack overwriting on 0 length strings: Tim Waugh
+ <twaugh@redhat.com> Ned Ludd <solar@gentoo.org>
+
+2004-09-27 11:30 Christos Zoulas <christos@astron.com>
+
+ * Remove 3rd and 4th copyright clause; approved by Ian Darwin.
+
+ * Fix small memory leaks; caught by: Tamas Sarlos
+ <stamas@csillag.ilab.sztaki.hu>
+
+2004-07-24 16:33 Christos Zoulas <christos@astron.com>
+
+ * magic.mime update Danny Milosavljevic <danny.milo@gmx.net>
+
+ * FreeBSD version update Oliver Eikemeier <eikemeier@fillmore-labs.com>
+
+ * utime/utimes detection Ian Lance Taylor <ian@wasabisystems.com>
+
+ * errors reading elf magic Jakub Bogusz <qboosh@pld-linux.org>
+
+2004-04-12 10:55 Christos Zoulas <christos@astron.com>
+
+ * make sure that magic formats match magic types during compilation
+
+ * fix broken sgi magic file
+
+2004-04-06 20:36 Christos Zoulas <christos@astron.com>
+
+ * detect present of mbstate_t Petter Reinholdtsen <pere@hungry.com>
+
+ * magic fixes
+
+2004-03-22 15:25 Christos Zoulas <christos@astron.com>
+
+ * Lots of mime fixes
+ (Joerg Ostertag) <ostertag@rechengilde.de>
+
+ * FreeBSD ELF version handling
+ (Edwin Groothuis) <edwin@mavetju.org>
+
+ * correct cleanup in all cases; don't just close the file.
+ (Christos Zoulas) <christos@astron.com>
+
+ * add gettext message catalogue support
+ (Michael Piefel) <piefel@debian.org>
+
+ * better printout for unreadable files
+ (Michael Piefel) <piefel@debian.org>
+
+ * compensate for missing MAXPATHLEN
+ (Michael Piefel) <piefel@debian.org>
+
+ * add wide character string length computation
+ (Michael Piefel) <piefel@debian.org>
+
+ * Avoid infinite loops caused by bad elf alignments
+ or name and description note sizes. Reported by
+ (Mikael Magnusson) <mmikael@comhem.se>
+
+2004-03-09 13:55 Christos Zoulas <christos@astron.com>
+
+ * Fix possible memory leak on error and add missing regfree
+ (Dmitry V. Levin) <ldv@altlinux.org>
+
+2003-12-23 12:12 Christos Zoulas <christos@astron.com>
+
+ * fix -k flag (Maciej W. Rozycki)
+
+2003-11-18 14:10 Christos Zoulas <christos@astron.com>
+
+ * Try to give us much info as possible on corrupt elf files.
+ (Willy Tarreau) <willy@w.ods.org>
+ * Updated python bindings (Brett Funderburg)
+ <brettf@deepfile.com>
+
+2003-11-11 15:03 Christos Zoulas <christos@astron.com>
+
+ * Include file.h first, because it includes config.h
+ breaks largefile test macros otherwise.
+ (Paul Eggert <eggert@CS.UCLA.EDU> via
+ Lars Hecking <lhecking@nmrc.ie>)
+
+2003-10-14 21:39 Christos Zoulas <christos@astron.com>
+
+ * Python bindings (Brett Funderburg) <brettf@deepfile.com>
+ * Don't lookup past the end of the buffer
+ (Chad Hanson) <chanson@tcs-sec.com>
+ * Add MAGIC_ERROR and api on magic_errno()
+
+2003-10-08 12:40 Christos Zoulas <christos@astron.com>
+
+ * handle error conditions from compile as fatal
+ (Antti Kantee) <pooka@netbsd.org>
+ * handle magic filename parsing sanely
+ * more magic fixes.
+ * fix a memory leak (Illes Marton) <illes.marton@balabit.hu>
+ * describe magic file handling
+ (Bryan Henderson) <bryanh@giraffe-data.com>
+
+2003-09-12 15:09 Christos Zoulas <christos@astron.com>
+
+ * update magic files.
+ * remove largefile support from file.h; it breaks things on most OS's
+
+2003-08-10 10:25 Christos Zoulas <christos@astron.com>
+
+ * fix unmapping'ing of mmaped files.
+
+2003-07-10 12:03 Christos Zoulas <christos@astron.com>
+
+ * don't exit with -1 on error; always exit 1 (Marty Leisner)
+ * restore utimes code.
+
+2003-06-10 17:03 Christos Zoulas <christos@astron.com>
+
+ * make sure we don't access uninitialized memory.
+ * pass lint
+ * #ifdef __cplusplus in magic.h
+
+2003-05-25 19:23 Christos Zoulas <christos@astron.com>
+
+ * rename cvs magic file to revision to deal with
+ case insensitive filesystems.
+
+2003-05-23 17:03 Christos Zoulas <christos@astron.com>
+
+ * documentation fixes from Michael Piefel <piefel@debian.org>
+ * magic fixes (various)
+ * revert basename magic in .mgc name determination
+ * buffer protection in uncompress,
+ signness issues,
+ close files
+ Maciej W. Rozycki <macro@ds2.pg.gda.pl
+
+2003-04-21 20:12 Christos Zoulas <christos@astron.com>
+
+ * fix zsh magic
+
+2003-04-04 16:59 Christos Zoulas <christos@astron.com>
+
+ * fix operand sort order in string.
+
+2003-04-02 17:30 Christos Zoulas <christos@astron.com>
+
+ * cleanup namespace in magic.h
+
+2003-04-02 13:50 Christos Zoulas <christos@astron.com>
+
+ * Magic additions (Alex Ott)
+ * Fix bug that broke VPATH compilation (Peter Breitenlohner)
+
+2003-03-28 16:03 Christos Zoulas <christos@astron.com>
+
+ * remove packed attribute from magic struct.
+ * make the magic struct properly aligned.
+ * bump version number of compiled files to 2.
+
+2003-03-27 13:10 Christos Zoulas <christos@astron.com>
+
+ * separate tar detection and run it before softmagic.
+ * fix reversed symlink test.
+ * fix version printing.
+ * make separator a string instead of a char.
+ * update manual page and sort options.
+
+2003-03-26 11:00 Christos Zoulas <christos@astron.com>
+
+ * Pass lint
+ * make NULL in magic_file mean stdin
+ * Fix "-" argument to file to pass NULL to magic_file
+ * avoid pointer casts by using memcpy
+ * rename magic_buf -> magic_buffer
+ * keep only the first error
+ * manual page: new sentence, new line
+ * fix typo in api function (magic_buf -> magic_buffer)
diff --git a/contrib/libs/libmagic/INSTALL b/contrib/libs/libmagic/INSTALL
new file mode 100644
index 0000000000..7d1c323bea
--- /dev/null
+++ b/contrib/libs/libmagic/INSTALL
@@ -0,0 +1,365 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+ Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package. Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below. The lack of an optional feature in a given package is not
+necessarily a bug. More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+ The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package, generally using the just-built uninstalled binaries.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation. When installing into a prefix owned by root, it is
+ recommended that the package be configured and built as a regular
+ user, and only the `make install' phase executed with root
+ privileges.
+
+ 5. Optionally, type `make installcheck' to repeat any self-tests, but
+ this time using the binaries in their final installed location.
+ This target does not install anything. Running this target as a
+ regular user, particularly if the prior `make install' required
+ root privileges, verifies that the installation completed
+ correctly.
+
+ 6. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 7. Often, you can also type `make uninstall' to remove the installed
+ files again. In practice, not all packages have tested that
+ uninstallation works correctly, even though it is required by the
+ GNU Coding Standards.
+
+ 8. Some packages, particularly those that use Automake, provide `make
+ distcheck', which can by used by developers to test that all other
+ targets like `make install' and `make uninstall' work correctly.
+ This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'. This
+is known as a "VPATH" build.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+ On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor. Like
+this:
+
+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CPP="gcc -E" CXXCPP="g++ -E"
+
+ This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+ By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them. In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+ The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+ The first method involves providing an override variable for each
+affected directory. For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'. Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated. The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+ The second method involves providing the `DESTDIR' variable. For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names. The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters. On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+ Some packages offer the ability to configure how verbose the
+execution of `make' will be. For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
+Particular systems
+==================
+
+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file. The option `-nodtk' can be used as
+a workaround. If GNU CC is not installed, it is therefore recommended
+to try
+
+ ./configure CC="cc"
+
+and if that doesn't work, try
+
+ ./configure CC="cc -nodtk"
+
+ On Solaris, don't put `/usr/ucb' early in your `PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+ On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+ Print a summary of the options unique to this package's
+ `configure', and exit. The `short' variant lists options used
+ only in the top level, while the `recursive' variant lists options
+ also present in any nested packages.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+ Use DIR as the installation prefix. *note Installation Names::
+ for more details, including other options available for fine-tuning
+ the installation locations.
+
+`--no-create'
+`-n'
+ Run the configure checks, but stop before creating any output
+ files.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/contrib/libs/libmagic/NEWS b/contrib/libs/libmagic/NEWS
new file mode 100644
index 0000000000..898a3dab34
--- /dev/null
+++ b/contrib/libs/libmagic/NEWS
@@ -0,0 +1 @@
+See ChangeLog.
diff --git a/contrib/libs/libmagic/README.DEVELOPER b/contrib/libs/libmagic/README.DEVELOPER
new file mode 100644
index 0000000000..dfe27b973e
--- /dev/null
+++ b/contrib/libs/libmagic/README.DEVELOPER
@@ -0,0 +1,49 @@
+# How to get started developing
+
+@(#) $File: README.DEVELOPER,v 1.9 2021/09/20 14:04:39 christos Exp $
+
+## Auto files
+
+After checking out the source, run the following:
+
+ autoreconf -f -i
+ make distclean # this can fail if you have not built before
+ ./configure --disable-silent-rules
+ make -j4
+ make -C tests check
+
+If you see errors, make sure you have the latest libtool and autoconf
+This has been tested with autoconf-2.69 and libtool-2.4.2
+
+## Installing dependencies
+
+If your platform doesn't have the above tools, install the following
+packages first.
+
+### Debian
+
+ apt-get install \
+ automake \
+ gcc \
+ libtool \
+ make \
+ python \
+ zlib1g-dev \
+
+See also `.travis.yml`.
+
+### Mac OS X (MacPorts)
+
+ port install \
+ autoconf \
+ automake \
+ libtool \
+
+### Mac OS X (HomeBrew)
+
+ brew install autoconf automake libtool
+
+Tested with:
+ autoconf 2.69
+ automake 1.16.1
+ libtool 2.4.6
diff --git a/contrib/libs/libmagic/README.md b/contrib/libs/libmagic/README.md
new file mode 100644
index 0000000000..26e3804581
--- /dev/null
+++ b/contrib/libs/libmagic/README.md
@@ -0,0 +1,156 @@
+## README for file(1) Command and the libmagic(3) library ##
+
+ @(#) $File: README.md,v 1.5 2023/05/28 13:59:47 christos Exp $
+
+- Bug Tracker: <https://bugs.astron.com/>
+- Build Status: <https://travis-ci.org/file/file>
+- Download link: <ftp://ftp.astron.com/pub/file/>
+- E-mail: <christos@astron.com>
+- Fuzzing link: <https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:file>
+- Home page: https://www.darwinsys.com/file/
+- Mailing List archives: <https://mailman.astron.com/pipermail/file/>
+- Mailing List: <file@astron.com>
+- Public repo: <https://github.com/file/file>
+- Test framework: <https://github.com/file/file-tests>
+
+Phone: Do not even think of telephoning me about this program. Send
+cash first!
+
+This is Release 5.x of Ian Darwin's (copyright but distributable)
+file(1) command, an implementation of the Unix File(1) command.
+It knows the 'magic number' of several thousands of file types.
+This version is the standard "file" command for Linux, *BSD, and
+other systems. (See "patchlevel.h" for the exact release number).
+
+The major changes for 5.x are CDF file parsing, indirect magic,
+name/use (recursion) and overhaul in mime and ascii encoding
+handling.
+
+The major feature of 4.x is the refactoring of the code into a
+library, and the re-write of the file command in terms of that
+library. The library itself, libmagic can be used by 3rd party
+programs that wish to identify file types without having to fork()
+and exec() file. The prime contributor for 4.0 was Mans Rullgard.
+
+UNIX is a trademark of UNIX System Laboratories.
+
+The prime contributor to Release 3.8 was Guy Harris, who put in
+megachanges including byte-order independence.
+
+The prime contributor to Release 3.0 was Christos Zoulas, who put
+in hundreds of lines of source code changes, including his own
+ANSIfication of the code (I liked my own ANSIfication better, but
+his (__P()) is the "Berkeley standard" way of doing it, and I wanted
+UCB to include the code...), his HP-like "indirection" (a feature
+of the HP file command, I think), and his mods that finally got
+the uncompress (-z) mode finished and working.
+
+This release has compiled in numerous environments; see PORTING
+for a list and problems.
+
+This fine freeware file(1) follows the USG (System V) model of the
+file command, rather than the Research (V7) version or the V7-derived
+4.[23] Berkeley one. That is, the file /etc/magic contains much of
+the ritual information that is the source of this program's power.
+My version knows a little more magic (including tar archives) than
+System V; the /etc/magic parsing seems to be compatible with the
+(poorly documented) System V /etc/magic format (with one exception;
+see the man page).
+
+In addition, the /etc/magic file is built from a subdirectory
+for easier(?) maintenance. I will act as a clearinghouse for
+magic numbers assigned to all sorts of data files that
+are in reasonable circulation. Send your magic numbers,
+in magic(5) format please, to the maintainer, Christos Zoulas.
+
+COPYING - read this first.
+* `README` - read this second (you are currently reading this file).
+* `INSTALL` - read on how to install
+* `src/apprentice.c` - parses /etc/magic to learn magic
+* `src/apptype.c` - used for OS/2 specific application type magic
+* `src/ascmagic.c` - third & last set of tests, based on hardwired assumptions.
+* `src/asctime_r.c` - replacement for OS's that don't have it.
+* `src/asprintf.c` - replacement for OS's that don't have it.
+* `src/buffer.c` - buffer handling functions.
+* `src/cdf.[ch]` - parser for Microsoft Compound Document Files
+* `src/cdf_time.c` - time converter for CDF.
+* `src/compress.c` - handles decompressing files to look inside.
+* `src/ctime_r.c` - replacement for OS's that don't have it.
+* `src/der.[ch]` - parser for Distinguished Encoding Rules
+* `src/dprintf.c` - replacement for OS's that don't have it.
+* `src/elfclass.h` - common code for elf 32/64.
+* `src/encoding.c` - handles unicode encodings
+* `src/file.c` - the main program
+* `src/file.h` - header file
+* `src/file_opts.h` - list of options
+* `src/fmtcheck.c` - replacement for OS's that don't have it.
+* `src/fsmagic.c` - first set of tests the program runs, based on filesystem info
+* `src/funcs.c` - utilility functions
+* `src/getline.c` - replacement for OS's that don't have it.
+* `src/getopt_long.c` - replacement for OS's that don't have it.
+* `src/gmtime_r.c` - replacement for OS's that don't have it.
+* `src/is_csv.c` - knows about Comma Separated Value file format (RFC 4180).
+* `src/is_json.c` - knows about JavaScript Object Notation format (RFC 8259).
+* `src/is_simh.c` - knows about SIMH tape file format.
+* `src/is_tar.c, tar.h` - knows about Tape ARchive format (courtesy John Gilmore).
+* `src/localtime_r.c` - replacement for OS's that don't have it.
+* `src/magic.h.in` - source file for magic.h
+* `src/mygetopt.h` - replacement for OS's that don't have it.
+* `src/magic.c` - the libmagic api
+* `src/names.h` - header file for ascmagic.c
+* `src/pread.c` - replacement for OS's that don't have it.
+* `src/print.c` - print results, errors, warnings.
+* `src/readcdf.c` - CDF wrapper.
+* `src/readelf.[ch]` - Stand-alone elf parsing code.
+* `src/softmagic.c` - 2nd set of tests, based on /etc/magic
+* `src/mygetopt.h` - replacement for OS's that don't have it.
+* `src/strcasestr.c` - replacement for OS's that don't have it.
+* `src/strlcat.c` - replacement for OS's that don't have it.
+* `src/strlcpy.c` - replacement for OS's that don't have it.
+* `src/strndup.c` - replacement for OS's that don't have it.
+* `src/tar.h` - tar file definitions
+* `src/vasprintf.c` - for systems that don't have it.
+* `doc/file.man` - man page for the command
+* `doc/magic.man` - man page for the magic file, courtesy Guy Harris.
+ Install as magic.4 on USG and magic.5 on V7 or Berkeley; cf Makefile.
+
+Magdir - directory of /etc/magic pieces
+------------------------------------------------------------------------------
+
+If you submit a new magic entry please make sure you read the following
+guidelines:
+
+- Initial match is preferably at least 32 bits long, and is a _unique_ match
+- If this is not feasible, use additional check
+- Match of <= 16 bits are not accepted
+- Delay printing string as much as possible, don't print output too early
+- Avoid printf arbitrary byte as string, which can be a source of
+ crash and buffer overflow
+
+- Provide complete information with entry:
+ * One line short summary
+ * Optional long description
+ * File extension, if applicable
+ * Full name and contact method (for discussion when entry has problem)
+ * Further reference, such as documentation of format
+
+gpg for dummies:
+------------------------------------------------------------------------------
+
+```
+$ gpg --verify file-X.YY.tar.gz.asc file-X.YY.tar.gz
+gpg: assuming signed data in `file-X.YY.tar.gz'
+gpg: Signature made WWW MMM DD HH:MM:SS YYYY ZZZ using DSA key ID KKKKKKKK
+```
+
+To download the key:
+
+```
+$ gpg --keyserver hkp://keys.gnupg.net --recv-keys KKKKKKKK
+```
+------------------------------------------------------------------------------
+
+
+Parts of this software were developed at SoftQuad Inc., developers
+of SGML/HTML/XML publishing software, in Toronto, Canada.
+SoftQuad was swallowed up by Corel in 2002 and does not exist any longer.
diff --git a/contrib/libs/libmagic/config-linux.h b/contrib/libs/libmagic/config-linux.h
new file mode 100644
index 0000000000..bf766ac512
--- /dev/null
+++ b/contrib/libs/libmagic/config-linux.h
@@ -0,0 +1,519 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define in built-in ELF support is used */
+#define BUILTIN_ELF 1
+
+/* Enable bzlib compression support */
+/* #undef BZLIBSUPPORT */
+
+/* Define for ELF core file support */
+#define ELFCORE 1
+
+/* Define to 1 if you have the `asctime_r' function. */
+#define HAVE_ASCTIME_R 1
+
+/* Define to 1 if you have the `asprintf' function. */
+#define HAVE_ASPRINTF 1
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#define HAVE_BYTESWAP_H 1
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+/* #undef HAVE_BZLIB_H */
+
+/* Define to 1 if you have the `ctime_r' function. */
+#define HAVE_CTIME_R 1
+
+/* HAVE_DAYLIGHT */
+#define HAVE_DAYLIGHT 1
+
+/* Define to 1 if you have the declaration of `daylight', and to 0 if you
+ don't. */
+#define HAVE_DECL_DAYLIGHT 1
+
+/* Define to 1 if you have the declaration of `tzname', and to 0 if you don't.
+ */
+#define HAVE_DECL_TZNAME 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `dprintf' function. */
+#define HAVE_DPRINTF 1
+
+/* Define to 1 if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fmtcheck' function. */
+/* #undef HAVE_FMTCHECK */
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if you have the `freelocale' function. */
+#define HAVE_FREELOCALE 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `getline' function. */
+#define HAVE_GETLINE 1
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getopt_long' function. */
+#define HAVE_GETOPT_LONG 1
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* Define to 1 if the system has the type `intptr_t'. */
+#define HAVE_INTPTR_T 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `bz2' library (-lbz2). */
+/* #undef HAVE_LIBBZ2 */
+
+/* Define to 1 if you have the `gnurx' library (-lgnurx). */
+/* #undef HAVE_LIBGNURX */
+
+/* Define to 1 if you have the `lz' library (-llz). */
+/* #undef HAVE_LIBLZ */
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+/* #undef HAVE_LIBLZMA */
+
+/* Define to 1 if you have the `seccomp' library (-lseccomp). */
+/* #undef HAVE_LIBSECCOMP */
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+/* #undef HAVE_LIBZSTD */
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if you have the <lzlib.h> header file. */
+/* #undef HAVE_LZLIB_H */
+
+/* Define to 1 if you have the <lzma.h> header file. */
+/* #undef HAVE_LZMA_H */
+
+/* Define to 1 if mbrtowc and mbstate_t are properly declared. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if <wchar.h> declares mbstate_t. */
+#define HAVE_MBSTATE_T 1
+
+/* Define to 1 if you have the `memmem' function. */
+#define HAVE_MEMMEM 1
+
+/* Define to 1 if you have the <minix/config.h> header file. */
+/* #undef HAVE_MINIX_CONFIG_H */
+
+/* Define to 1 if you have the `mkostemp' function. */
+#define HAVE_MKOSTEMP 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have a working `mmap' system call. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the `newlocale' function. */
+#define HAVE_NEWLOCALE 1
+
+/* Define to 1 if you have the `pipe2' function. */
+#define HAVE_PIPE2 1
+
+/* Define to 1 if you have the `posix_spawnp' function. */
+#define HAVE_POSIX_SPAWNP 1
+
+/* Define to 1 if you have the `pread' function. */
+#define HAVE_PREAD 1
+
+/* Have sig_t type */
+#define HAVE_SIG_T 1
+
+/* Define to 1 if you have the <spawn.h> header file. */
+#define HAVE_SPAWN_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasestr' function. */
+#define HAVE_STRCASESTR 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlcat' function. */
+/* #undef HAVE_STRLCAT */
+
+/* Define to 1 if you have the `strlcpy' function. */
+/* #undef HAVE_STRLCPY */
+
+/* Define to 1 if you have the `strndup' function. */
+#define HAVE_STRNDUP 1
+
+/* Define to 1 if you have the `strtof' function. */
+#define HAVE_STRTOF 1
+
+/* HAVE_STRUCT_OPTION */
+#define HAVE_STRUCT_OPTION 1
+
+/* Define to 1 if `st_rdev' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_RDEV 1
+
+/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+
+/* Define to 1 if `tm_zone' is a member of `struct tm'. */
+#define HAVE_STRUCT_TM_TM_ZONE 1
+
+/* Define to 1 if you have the <sys/bswap.h> header file. */
+/* #undef HAVE_SYS_BSWAP_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+#define HAVE_SYS_SYSMACROS_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+/* #undef HAVE_SYS_UTIME_H */
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* HAVE_TM_ISDST */
+#define HAVE_TM_ISDST 1
+
+/* HAVE_TM_ZONE */
+#define HAVE_TM_ZONE 1
+
+/* HAVE_TZNAME */
+#define HAVE_TZNAME 1
+
+/* Define to 1 if the system has the type `uintptr_t'. */
+#define HAVE_UINTPTR_T 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `uselocale' function. */
+#define HAVE_USELOCALE 1
+
+/* Define to 1 if you have the `utime' function. */
+#define HAVE_UTIME 1
+
+/* Define to 1 if you have the `utimes' function. */
+#define HAVE_UTIMES 1
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the `vasprintf' function. */
+#define HAVE_VASPRINTF 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 or 0, depending whether the compiler supports simple visibility
+ declarations. */
+#define HAVE_VISIBILITY 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#define HAVE_WCTYPE_H 1
+
+/* Define to 1 if you have the `wcwidth' function. */
+#define HAVE_WCWIDTH 1
+
+/* Define to 1 if `fork' works. */
+#define HAVE_WORKING_FORK 1
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if you have the <xlocale.h> header file. */
+/* #undef HAVE_XLOCALE_H */
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#define HAVE_ZLIB_H 1
+
+/* Define to 1 if you have the <zstd_errors.h> header file. */
+/* #undef HAVE_ZSTD_ERRORS_H */
+
+/* Define to 1 if you have the <zstd.h> header file. */
+/* #undef HAVE_ZSTD_H */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Enable lzlib compression support */
+/* #undef LZLIBSUPPORT */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+ */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+ <sysmacros.h>. */
+#define MAJOR_IN_SYSMACROS 1
+
+/* Name of package */
+#define PACKAGE "file"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "christos@astron.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "file"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "file 5.45"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "file"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "5.45"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable general extensions on macOS. */
+#ifndef _DARWIN_C_SOURCE
+# define _DARWIN_C_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable X/Open compliant socket functions that do not require linking
+ with -lxnet on HP-UX 11.11. */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# define _HPUX_ALT_XOPEN_SOCKET_API 1
+#endif
+/* Identify the host operating system as Minix.
+ This macro does not affect the system headers' behavior.
+ A future release of Autoconf may stop defining this macro. */
+#ifndef _MINIX
+/* # undef _MINIX */
+#endif
+/* Enable general extensions on NetBSD.
+ Enable NetBSD compatibility extensions on Minix. */
+#ifndef _NETBSD_SOURCE
+# define _NETBSD_SOURCE 1
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+ Oddly enough, this does nothing on OpenBSD. */
+#ifndef _OPENBSD_SOURCE
+# define _OPENBSD_SOURCE 1
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_SOURCE
+/* # undef _POSIX_SOURCE */
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_1_SOURCE
+/* # undef _POSIX_1_SOURCE */
+#endif
+/* Enable POSIX-compatible threading on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# define __STDC_WANT_IEC_60559_BFP_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# define __STDC_WANT_IEC_60559_DFP_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# define __STDC_WANT_IEC_60559_TYPES_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
+#ifndef __STDC_WANT_LIB_EXT2__
+# define __STDC_WANT_LIB_EXT2__ 1
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009. */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# define __STDC_WANT_MATH_SPEC_FUNCS__ 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable X/Open extensions. Define to 500 only if necessary
+ to make mbstate_t available. */
+#ifndef _XOPEN_SOURCE
+/* # undef _XOPEN_SOURCE */
+#endif
+
+
+/* Version number of package */
+#define VERSION "5.45"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Enable xzlib compression support */
+/* #undef XZLIBSUPPORT */
+
+/* Enable zlib compression support */
+#define ZLIBSUPPORT 1
+
+/* Enable zstdlib compression support */
+/* #undef ZSTDLIBSUPPORT */
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef int32_t */
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef int64_t */
+
+/* Define to the type of a signed integer type wide enough to hold a pointer,
+ if such a type exists, and if the system does not define it. */
+/* #undef intptr_t */
+
+/* Define to a type if <wchar.h> does not define. */
+/* #undef mbstate_t */
+
+/* Define to `long int' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define as a signed integer type capable of holding a process identifier. */
+/* #undef pid_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
+
+/* Define to the type of an unsigned integer type wide enough to hold a
+ pointer, if such a type exists, and if the system does not define it. */
+/* #undef uintptr_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
diff --git a/contrib/libs/libmagic/config-osx.h b/contrib/libs/libmagic/config-osx.h
new file mode 100644
index 0000000000..de0960d03b
--- /dev/null
+++ b/contrib/libs/libmagic/config-osx.h
@@ -0,0 +1,9 @@
+#include "config-linux.h"
+
+#define HAVE_FMTCHECK 1
+#define HAVE_STRLCAT 1
+#define HAVE_STRLCPY 1
+#define HAVE_XLOCALE_H 1
+#undef HAVE_SYS_SYSMACROS_H
+#undef HAVE_PIPE2
+#undef HAVE_BYTESWAP_H
diff --git a/contrib/libs/libmagic/config.h b/contrib/libs/libmagic/config.h
new file mode 100644
index 0000000000..24eccf047e
--- /dev/null
+++ b/contrib/libs/libmagic/config.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#if defined(__APPLE__)
+# include "config-osx.h"
+#else
+# include "config-linux.h"
+#endif
diff --git a/contrib/libs/libmagic/file/0/ya.make b/contrib/libs/libmagic/file/0/ya.make
new file mode 100644
index 0000000000..f2b2d67f6a
--- /dev/null
+++ b/contrib/libs/libmagic/file/0/ya.make
@@ -0,0 +1,12 @@
+# Generated by devtools/yamaker.
+
+PROGRAM(file0)
+
+WITHOUT_LICENSE_TEXTS()
+
+PEERDIR(
+ contrib/libs/libmagic/src
+ contrib/libs/libmagic/src/file
+)
+
+END()
diff --git a/contrib/libs/libmagic/include/magic.h b/contrib/libs/libmagic/include/magic.h
new file mode 100644
index 0000000000..0c283e98d6
--- /dev/null
+++ b/contrib/libs/libmagic/include/magic.h
@@ -0,0 +1 @@
+#include "../src/magic.h" /* inclink generated by yamaker */
diff --git a/contrib/libs/libmagic/magic/Magdir/acorn b/contrib/libs/libmagic/magic/Magdir/acorn
new file mode 100644
index 0000000000..37a4ed79e5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/acorn
@@ -0,0 +1,102 @@
+
+#------------------------------------------------------------------------------
+# $File: acorn,v 1.8 2021/04/26 15:56:00 christos Exp $
+# acorn: file(1) magic for files found on Acorn systems
+#
+
+# RISC OS Chunk File Format
+# From RISC OS Programmer's Reference Manual, Appendix D
+# We guess the file type from the type of the first chunk.
+0 lelong 0xc3cbc6c5 RISC OS Chunk data
+>12 string OBJ_ \b, AOF object
+>12 string LIB_ \b, ALF library
+
+# RISC OS AIF, contains "SWI OS_Exit" at offset 16.
+16 lelong 0xef000011 RISC OS AIF executable
+
+# RISC OS Draw files
+# From RISC OS Programmer's Reference Manual, Appendix E
+0 string Draw RISC OS Draw file data
+
+# RISC OS new format font files
+# From RISC OS Programmer's Reference Manual, Appendix E
+0 string FONT\0 RISC OS outline font data,
+>5 byte x version %d
+0 string FONT\1 RISC OS 1bpp font data,
+>5 byte x version %d
+0 string FONT\4 RISC OS 4bpp font data
+>5 byte x version %d
+
+# RISC OS Music files
+# From RISC OS Programmer's Reference Manual, Appendix E
+0 string Maestro\r RISC OS music file
+>8 byte x version %d
+
+>8 byte x type %d
+
+# Digital Symphony data files
+# From: Bernard Jungen (bern8817@euphonynet.be)
+0 string \x02\x01\x13\x13\x13\x01\x0d\x10 Digital Symphony sound sample (RISC OS),
+>8 byte x version %d,
+>9 pstring x named "%s",
+>(9.b+19) byte =0 8-bit logarithmic
+>(9.b+19) byte =1 LZW-compressed linear
+>(9.b+19) byte =2 8-bit linear signed
+>(9.b+19) byte =3 16-bit linear signed
+>(9.b+19) byte =4 SigmaDelta-compressed linear
+>(9.b+19) byte =5 SigmaDelta-compressed logarithmic
+>(9.b+19) byte >5 unknown format
+
+0 string \x02\x01\x13\x13\x14\x12\x01\x0b Digital Symphony song (RISC OS),
+>8 byte x version %d,
+>9 byte =1 1 voice,
+>9 byte !1 %d voices,
+>10 leshort =1 1 track,
+>10 leshort !1 %d tracks,
+>12 leshort =1 1 pattern
+>12 leshort !1 %d patterns
+
+0 string \x02\x01\x13\x13\x10\x14\x12\x0e
+>9 byte =0 Digital Symphony sequence (RISC OS),
+>>8 byte x version %d,
+>>10 byte =1 1 line,
+>>10 byte !1 %d lines,
+>>11 leshort =1 1 position
+>>11 leshort !1 %d positions
+>9 byte =1 Digital Symphony pattern data (RISC OS),
+>>8 byte x version %d,
+>>10 leshort =1 1 pattern
+>>10 leshort !1 %d patterns
+
+# From: Joerg Jenderek
+# URL: https://www.kyzer.me.uk/pack/xad/#PackDir
+# reference: https://www.kyzer.me.uk/pack/xad/xad_PackDir.lha/PackDir.c
+# GRR: line below is too general as it matches also "Git pack" in ./revision
+0 string PACK\0
+# check for valid compression method 0-4
+>5 ulelong <5
+# https://www.riscosopen.org/wiki/documentation/show/Introduction%20To%20Filing%20Systems
+# To skip "Git pack" version 0 test for root directory object like
+# ADFS::RPC.$.websitezip.FONTFIX
+>>9 string >ADFS\ PackDir archive (RISC OS)
+# TrID labels above as "Acorn PackDir compressed Archive"
+# compression mode y (0 - 4) for GIF LZW with a maximum n bits
+# (y~n,0~12,1~13,2~14,3~15,4~16)
+>>>5 ulelong+12 x \b, LZW %u-bits compression
+# https://www.filebase.org.uk/filetypes
+# !Packdir compressed archive has three hexadecimal digits code 68E
+!:mime application/x-acorn-68E
+!:ext pkd/bin
+# null terminated root directory object like IDEFS::IDE-4.$.Apps.GRAPHICS.!XFMPdemo
+>>>9 string x \b, root "%s"
+# load address 0xFFFtttdd, ttt is the object filetype and dddddddddd is time
+>>>>&1 ulelong x \b, load address %#x
+# execution address 0xdddddddd dddddddddd is 40 bit unsigned centiseconds since 1.1.1900 UTC
+>>>>&5 ulelong x \b, exec address %#x
+# attributes (bits: 0~owner read,1~owner write,3~no delete,4~public read,5~public write)
+>>>>&9 ulelong x \b, attributes %#x
+# number of entries in this directory. for root dir 0
+#>>>&13 ulelong x \b, entries %#x
+# the entries start here with object name
+>>>>&17 string x \b, 1st object "%s"
+
diff --git a/contrib/libs/libmagic/magic/Magdir/adi b/contrib/libs/libmagic/magic/Magdir/adi
new file mode 100644
index 0000000000..2fe79d4431
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/adi
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: adi,v 1.4 2009/09/19 16:28:07 christos Exp $
+# adi: file(1) magic for ADi's objects
+# From Gregory McGarry <g.mcgarry@ieee.org>
+#
+0 leshort 0x521c COFF DSP21k
+>18 lelong &02 executable,
+>18 lelong ^02
+>>18 lelong &01 static object,
+>>18 lelong ^01 relocatable object,
+>18 lelong &010 stripped
+>18 lelong ^010 not stripped
diff --git a/contrib/libs/libmagic/magic/Magdir/adventure b/contrib/libs/libmagic/magic/Magdir/adventure
new file mode 100644
index 0000000000..bd7f863be2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/adventure
@@ -0,0 +1,122 @@
+
+#------------------------------------------------------------------------------
+# $File: adventure,v 1.18 2019/04/19 00:42:27 christos Exp $
+# adventure: file(1) magic for Adventure game files
+#
+# from Allen Garvin <earendil@faeryland.tamu-commerce.edu>
+# Edited by Dave Chapeskie <dchapes@ddm.on.ca> Jun 28, 1998
+# Edited by Chris Chittleborough <cchittleborough@yahoo.com.au>, March 2002
+#
+# ALAN
+# I assume there are other, lower versions, but these are the only ones I
+# saw in the archive.
+0 beshort 0x0206 ALAN game data
+>2 byte <10 version 2.6%d
+
+
+# Infocom (see z-machine)
+#------------------------------------------------------------------------------
+# Z-machine: file(1) magic for Z-machine binaries.
+# Sanity checks by David Griffith <dave@661.org>
+# Updated by Adam Buchbinder <adam.buchbinder@gmail.com>
+#
+#http://www.gnelson.demon.co.uk/zspec/sect11.html
+#https://www.jczorkmid.net/~jpenney/ZSpec11-latest.txt
+#https://en.wikipedia.org/wiki/Z-machine
+# The first byte is the Z-machine revision; it is always between 1 and 8. We
+# had false matches (for instance, inbig5.ocp from the Omega TeX extension as
+# well as an occasional MP3 file), so we sanity-check the version number.
+#
+# It might be possible to sanity-check the release number as well, as it seems
+# (at least in classic Infocom games) to always be a relatively small number,
+# always under 150 or so, but as this isn't rigorous, we'll wait on that until
+# it becomes clear that it's needed.
+#
+0 ubyte >0
+>0 ubyte <9
+>>16 belong&0xfe00f0f0 0x3030
+>>>0 ubyte < 10
+>>>>2 ubeshort x
+>>>>>18 regex [0-9][0-9][0-9][0-9][0-9][0-9]
+>>>>>>0 ubyte < 10 Infocom (Z-machine %d
+>>>>>>>2 ubeshort x \b, Release %d
+>>>>>>>>18 string >\0 \b, Serial %.6s
+>>>>>>>>18 string x \b)
+!:strength + 40
+!:mime application/x-zmachine
+
+#------------------------------------------------------------------------------
+# Glulx: file(1) magic for Glulx binaries.
+#
+# David Griffith <dave@661.org>
+# I haven't checked for false matches yet.
+#
+0 string Glul Glulx game data
+>4 beshort x (Version %d
+>>6 byte x \b.%d
+>>8 byte x \b.%d)
+>36 string Info Compiled by Inform
+!:mime application/x-glulx
+
+
+# For Quetzal and blorb magic see iff
+
+
+# TADS (Text Adventure Development System) version 2
+# All files are machine-independent (games compile to byte-code) and are tagged
+# with a version string of the form "V2.<digit>.<digit>\0".
+# Game files start with "TADS2 bin\n\r\032\0" then the compiler version.
+0 string TADS2\ bin TADS
+>9 belong !0x0A0D1A00 game data, CORRUPTED
+>9 belong 0x0A0D1A00
+>>13 string >\0 %s game data
+!:mime application/x-tads
+# Resource files start with "TADS2 rsc\n\r\032\0" then the compiler version.
+0 string TADS2\ rsc TADS
+>9 belong !0x0A0D1A00 resource data, CORRUPTED
+>9 belong 0x0A0D1A00
+>>13 string >\0 %s resource data
+!:mime application/x-tads
+# Some saved game files start with "TADS2 save/g\n\r\032\0", a little-endian
+# 2-byte length N, the N-char name of the game file *without* a NUL (darn!),
+# "TADS2 save\n\r\032\0" and the interpreter version.
+0 string TADS2\ save/g TADS
+>12 belong !0x0A0D1A00 saved game data, CORRUPTED
+>12 belong 0x0A0D1A00
+>>(16.s+32) string >\0 %s saved game data
+!:mime application/x-tads
+# Other saved game files start with "TADS2 save\n\r\032\0" and the interpreter
+# version.
+0 string TADS2\ save TADS
+>10 belong !0x0A0D1A00 saved game data, CORRUPTED
+>10 belong 0x0A0D1A00
+>>14 string >\0 %s saved game data
+!:mime application/x-tads
+
+# TADS (Text Adventure Development System) version 3
+# Game files start with "T3-image\015\012\032"
+0 string T3-image\015\012\032
+>11 leshort x TADS 3 game data (format version %d)
+# Saved game files start with "T3-state-v####\015\012\032"
+# where #### is a format version number
+0 string T3-state-v
+>14 string \015\012\032 TADS 3 saved game data (format version
+>>10 byte x %c
+>>11 byte x \b%c
+>>12 byte x \b%c
+>>13 byte x \b%c)
+!:mime application/x-t3vm-image
+
+# edited by David Griffith <dave@661.org>
+# Danny Milosavljevic <danny.milo@gmx.net>
+# These are ADRIFT (adventure game standard) game files, extension .taf
+# Checked from source at (http://www.adrift.co/) and various taf files
+# found at the Interactive Fiction Archive (https://ifarchive.org/)
+0 belong 0x3C423FC9
+>4 belong 0x6A87C2CF Adrift game file version
+>>8 belong 0x94453661 3.80
+>>8 belong 0x94453761 3.90
+>>8 belong 0x93453E61 4.0
+>>8 belong 0x92453E61 5.0
+>>8 default x unknown
+!:mime application/x-adrift
diff --git a/contrib/libs/libmagic/magic/Magdir/aes b/contrib/libs/libmagic/magic/Magdir/aes
new file mode 100644
index 0000000000..e5e1edcb13
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/aes
@@ -0,0 +1,29 @@
+
+#------------------------------------------------------------------------------
+# $File: aes,v 1.1 2020/08/18 21:20:22 christos Exp $
+#
+# aes: magic file for AES encrypted files
+
+# Summary: AES Crypt Encrypted Data File
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+# Reference: https://www.aescrypt.com/aes_file_format.html
+0 string AES
+>3 ubyte <3 AES encrypted data, version %u
+#!:mime application/aes
+!:mime application/x-aes-encrypted
+!:ext aes
+# For Version 2 the encrypted file can have text tags
+>>3 ubyte =2
+# length of an extension identifier and contents like: 0 24 33 38
+#>>5 ubeshort x \b, tag length %u
+#>>5 pstring/H x '%s'
+# standard extension tags like CREATED_BY
+>>>7 string CREATED_BY \b, created by
+# software product, manufacturer like "SharpAESCrypt v1.3.3.0" "aescrypt (Windows GUI) 3.10" ...
+>>>>&1 string x "%s"
+# TODO: more other tags
+# tag CREATED_DATE like YYYY-MM-DD
+# tag CREATED_TIME like HH:MM:SS
+#
+
diff --git a/contrib/libs/libmagic/magic/Magdir/algol68 b/contrib/libs/libmagic/magic/Magdir/algol68
new file mode 100644
index 0000000000..1ca1fad211
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/algol68
@@ -0,0 +1,35 @@
+
+#------------------------------------------------------------------------------
+# $File: algol68,v 1.6 2022/11/06 18:36:55 christos Exp $
+# algol68: file(1) magic for Algol 68 source
+#
+# URL: https://en.wikipedia.org/wiki/ALGOL_68
+# Reference: http://www.softwarepreservation.org/projects/ALGOL/report/Algol68_revised_report-AB.pdf
+# Update: Joerg Jenderek
+0 search/8192 (input,
+>0 use algol_68
+# graph_2d.a68
+0 regex/4006 \^PROC[[:space:]][a-zA-Z0-9_[:space:]]*[[:space:]]=
+>0 use algol_68
+0 regex/1024 \bMODE[\t\ ]
+>0 use algol_68
+0 regex/1024 \bMODE[\t\ ]
+>0 use algol_68
+0 regex/1024 \bREF[\t\ ]
+>0 use algol_68
+0 regex/1024 \bFLEX[\t\ ]\*\\[
+>0 use algol_68
+
+# display information like mime type and file name extension of Algol 68 source text
+0 name algol_68 Algol 68 source text
+!:mime text/x-Algol68
+# https://file-extension.net/seeker/file_extension_a68
+!:ext a68
+#!:ext a68/alg
+
+#0 regex [\t\ ]OD Algol 68 source text
+#>0 use algol_68
+#!:mime text/x-Algol68
+#0 regex [\t\ ]FI Algol 68 source text
+#>0 use algol_68
+#!:mime text/x-Algol68
diff --git a/contrib/libs/libmagic/magic/Magdir/allegro b/contrib/libs/libmagic/magic/Magdir/allegro
new file mode 100644
index 0000000000..b937c9cb02
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/allegro
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: allegro,v 1.4 2009/09/19 16:28:07 christos Exp $
+# allegro: file(1) magic for Allegro datafiles
+# Toby Deshane <hac@shoelace.digivill.net>
+#
+0 belong 0x736C6821 Allegro datafile (packed)
+0 belong 0x736C682E Allegro datafile (not packed/autodetect)
+0 belong 0x736C682B Allegro datafile (appended exe data)
diff --git a/contrib/libs/libmagic/magic/Magdir/alliant b/contrib/libs/libmagic/magic/Magdir/alliant
new file mode 100644
index 0000000000..962020238e
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/alliant
@@ -0,0 +1,18 @@
+
+#------------------------------------------------------------------------------
+# $File: alliant,v 1.7 2009/09/19 16:28:07 christos Exp $
+# alliant: file(1) magic for Alliant FX series a.out files
+#
+# If the FX series is the one that had a processor with a 68K-derived
+# instruction set, the "short" should probably become "beshort" and the
+# "long" should probably become "belong".
+# If it's the i860-based one, they should probably become either the
+# big-endian or little-endian versions, depending on the mode they ran
+# the 860 in....
+#
+0 short 0420 0420 Alliant virtual executable
+>2 short &0x0020 common library
+>16 long >0 not stripped
+0 short 0421 0421 Alliant compact executable
+>2 short &0x0020 common library
+>16 long >0 not stripped
diff --git a/contrib/libs/libmagic/magic/Magdir/amanda b/contrib/libs/libmagic/magic/Magdir/amanda
new file mode 100644
index 0000000000..e7fa539013
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/amanda
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: amanda,v 1.6 2017/03/17 21:35:28 christos Exp $
+# amanda: file(1) magic for amanda file format
+#
+0 string AMANDA:\ AMANDA
+>8 string TAPESTART\ DATE tape header file,
+>>23 string X
+>>>25 string >\ Unused %s
+>>23 string >\ DATE %s
+>8 string FILE\ dump file,
+>>13 string >\ DATE %s
diff --git a/contrib/libs/libmagic/magic/Magdir/amigaos b/contrib/libs/libmagic/magic/Magdir/amigaos
new file mode 100644
index 0000000000..fdd947fdf7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/amigaos
@@ -0,0 +1,218 @@
+
+#------------------------------------------------------------------------------
+# $File: amigaos,v 1.20 2021/09/20 00:42:19 christos Exp $
+# amigaos: file(1) magic for AmigaOS binary formats:
+
+#
+# From ignatios@cs.uni-bonn.de (Ignatios Souvatzis)
+#
+0 belong 0x000003fa AmigaOS shared library
+0 belong 0x000003f3 AmigaOS loadseg()ble executable/binary
+0 belong 0x000003e7 AmigaOS object/library data
+#
+0 beshort 0xe310 Amiga Workbench
+>2 beshort 1
+>>48 byte 1 disk icon
+>>48 byte 2 drawer icon
+>>48 byte 3 tool icon
+>>48 byte 4 project icon
+>>48 byte 5 garbage icon
+>>48 byte 6 device icon
+>>48 byte 7 kickstart icon
+>>48 byte 8 workbench application icon
+>2 beshort >1 icon, vers. %d
+#
+# various sound formats from the Amiga
+# G=F6tz Waschk <waschk@informatik.uni-rostock.de>
+#
+0 string FC14 Future Composer 1.4 Module sound file
+0 string SMOD Future Composer 1.3 Module sound file
+0 string AON4artofnoise Art Of Noise Module sound file
+1 string MUGICIAN/SOFTEYES Mugician Module sound file
+58 string SIDMON\ II\ -\ THE Sidmon 2.0 Module sound file
+0 string Synth4.0 Synthesis Module sound file
+0 string ARP. The Holy Noise Module sound file
+0 string BeEp\0 JamCracker Module sound file
+0 string COSO\0 Hippel-COSO Module sound file
+# Too simple (short, pure ASCII, deep), MPi
+#26 string V.3 Brian Postma's Soundmon Module sound file v3
+#26 string BPSM Brian Postma's Soundmon Module sound file v3
+#26 string V.2 Brian Postma's Soundmon Module sound file v2
+
+# The following are from: "Stefan A. Haubenthal" <polluks@web.de>
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Amiga_bitmap_font
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/f/font-amiga.trid.xml
+# https://wiki.amigaos.net/wiki/Graphics_Library_and_Text
+# fch_FileID=FCH_ID=0x0f00
+0 beshort 0x0f00
+# skip some AVM powerline firmware images by check for positive number of font elements
+# https://download.avm.de/fritzpowerline/fritzpowerline-1000e-t/other/fritz.os/fritz.powerline_1000ET_01_05.image
+>2 ubeshort >0 AmigaOS bitmap font
+#!:mime application/octet-stream
+!:mime font/x-amiga-font
+!:ext font
+# struct FontContents fch_FC; 1st fc_FileName [MAXFONTPATH=256]; ~ filename "/" fc_YSize
+# like: topazb/6 suits/8 Excel/9e emerald/17 Franklin/23 DIAMONDS/60.8C
+>>4 string x "%.256s"
+# fc_YSize ~number after slash in fc_FileName; like: 6 7 8 9 11 12 16 17 21 23 45 60
+>>260 beshort x \b, fc_YSize %u
+# fch_NumEntries; number of FontContents elements like:
+# 1 (often) 2 3 (IconCondensed.font tempfont.font) 4 (Franklin.font) 6 (mcoop.font)
+>>2 ubeshort >1 \b, %u elements
+#>>2 beshort x \b, %u element
+# plural s
+#>>2 beshort !1 \bs
+# like: 6 7 8 9 11 12 16 17 21 23 45 60
+#>>262 beshort x \b, FLAGS_STYLE
+>>2 beshort >1 \b, 2nd
+# 2nd fc_FileName like: Franklin/36
+>>>264 string x "%.256s"
+>>2 beshort >2 \b, 3rd
+# 3rd fc_FileName like: Franklin/18
+>>>524 string x "%.256s"
+# URL: http://fileformats.archiveteam.org/wiki/Amiga_bitmap_font
+# Reference: https://wiki.amigaos.net/wiki/Graphics_Library_and_Text
+# http://mark0.net/download/triddefs_xml.7z/defs/f/font-amiga-var2.trid.xml
+# Note: called by TrID "Amiga bitmap Font (var.2)"
+# fch_FileID=TFCH_ID=0x0f02
+0 beshort 0x0f02
+# skip possible misidentified foo by check for positive number of font elements
+>2 ubeshort >0 AmigaOS bitmap font (TFCH)
+#!:mime application/octet-stream
+!:mime font/x-amiga-font
+!:ext font
+# struct TFontContents fch_TFC[]; 1st tfc_FileName [254]; ~ filename "/" fc_YSize
+# like: Abbey/45 XScript/75 XTriumvirate/45
+>>4 string x "%.254s"
+# tfc_TagCount; including the TAG_END tag like: 4
+>>258 ubeshort x \b, tfc_TagCount %u
+# tfc_YSize ~number after slash in tfc_FileName; like: 45 75
+>>260 beshort x \b, tfc_YSize %u
+# tfc_Style; tfc_Flags like: 8022h 8222h
+#>>262 ubeshort x \b, FLAGS_STYLE %#x
+# fch_NumEntries; number of FontContents elements like: 1 (abbey.font) 2 (xscript.font xtriumvirate.font)
+>>2 ubeshort >1 \b, %u elements
+>>2 beshort >1 \b, 2nd
+# 2nd tfc_FileName like: XScript/45 XTriumvirate/30
+>>>264 string x "%.254s"
+0 beshort 0x0f03 AmigaOS outline font
+0 belong 0x80001001 AmigaOS outline tag
+0 string ##\ version catalog translation
+0 string EMOD\0 Amiga E module
+8 string ECXM\0 ECX module
+0 string/c @database AmigaGuide file
+
+# Amiga disk types
+# display information like volume name of root block on Amiga (floppy) disk
+0 name adf-rootblock
+# block primary type = T_HEADER (value 2)
+>0x000 ubelong !2 \b, type %u
+# header_key; unused in rootblock (value 0)
+>0x004 ubelong !0 \b, header_key %u
+# high_seq; unused (value 0)
+>0x008 ubelong !0 \b, high_seq %u
+# ht_size; hash table size; 0x48 for flopies
+>0x00c ubelong !0x48 \b, hash table size %#x
+# bm_flag; bitmap flag, -1 means VALID
+>0x138 belong !-1 \b, bitmap flag %#x
+# bm_ext; first bitmap extension block (Hard disks only)
+>0x1A0 ubelong !0 \b, bitmap extension block %#x
+# name_len; volume name length; diskname[30]; volume name
+>0x1B0 pstring >\0 \b, "%s"
+# first directory cache block for FFS; otherwise 0
+>0x1F8 ubelong !0 \b, directory cache block %#x
+# block secondary type = ST_ROOT (value 1)
+>0x1FC ubelong !1 \b, sec_type %#x
+#
+0 string RDSK Rigid Disk Block
+>160 string x on %.24s
+# URL: http://fileformats.archiveteam.org/wiki/ADF_(Amiga)
+# https://en.wikipedia.org/wiki/Amiga_Fast_File_System
+# Reference: http://lclevy.free.fr/adflib/adf_info.html
+# Update: Joerg Jenderek
+# Note: created by ADFOpus.exe
+# and verified by `unadf -l TURBO_SILVER_SV.ADF`
+0 string DOS
+# skip DOS Client Message Files like IPXODI.MSG DOSRQSTR.MSG
+>3 ubyte <8 Amiga
+# https://reposcope.com/mimetype/application/x-amiga-disk-format
+!:mime application/x-amiga-disk-format
+!:ext adf
+>>3 ubyte 0 DOS disk
+>>3 ubyte 1 FFS disk
+>>3 ubyte 2 Inter DOS disk
+>>3 ubyte 3 Inter FFS disk
+# For Fastdir mode the international mode is also enabled,
+>>3 ubyte 4 Fastdir DOS disk
+>>3 ubyte 5 Fastdir FFS dis
+# called by TrID "Amiga Disk image File (OFS+INTL+DIRC)"
+>>3 ubyte 6 Inter Fastdir DOS disk
+# called by TrID "Amiga Disk image File (FFS+INTL+DIRC)"
+>>3 ubyte 7 Inter Fastdir FFS disk
+# but according to Wikipedia variants with long name support
+#>>3 ubyte 6 long name DOS disk
+#>>3 ubyte 7 long name FFS disk
+# DOES NOT only work! Partly for file size ~< FILE_BYTES_MAX=1 MiB defined in ../../src/file.h
+#>>-0 offset x \b, %lld bytes
+# Correct file size, but next lines are NOT executed
+#>>-0 offset 901120 (DD 880 KiB floppy)
+# 880 KiB Double Density floppy disk by characteristic hash table size 0x48 and T_HEADER=2
+>>0x6E00C ubelong 0x48
+>>>0x6E000 ubelong 2 (DD 880 KiB)
+# 1760 KiB High Density floppy disk (1802240 bytes) by characteristic hash table size 0x48
+>>0xDC00C ubelong 0x48
+>>>0xDC000 ubelong 2 (HD 1760 KiB)
+# Chksum; special block checksum like: 0 0x44ccf4c0 0x51f32cac 0xe33d0e7d ...
+#>>4 ubelong x \b, CRC %#x
+# Rootblock: 0 880 (often for DD and HD) 1146049280 (IMAGINE_1_0_DISK_01.ADF TURBO_SILVER_SV.ADF)
+>>8 ubelong >0 \b, probably root block %d
+# bootblock code
+>>12 quad !0 \b, bootable
+# assembler instructions: lea exp(pc),a1; moveq 25h,d0; jsr -552(a6)
+>>>12 ubequad =0x43fa003e70254eae AmigaDOS 3.0
+>>>12 default x
+>>>>12 ubequad !0x43fa003e70254eae %#llx..
+# 880 KiB Double Density floppy disk (901120 bytes)
+>>0x6E00C ubelong 0x48
+>>>0x6E000 ubelong 2
+>>>>0x6E000 use adf-rootblock
+# 1760 KiB High Density floppy disk (1802240 bytes)
+>>0xDC00C ubelong 0x48
+>>>0xDC000 ubelong 2
+>>>>0xDC000 use adf-rootblock
+# 1 MiB hard disc by test for T_HEADER=2 and header_key=0=high_seq
+>>0x80000 ubelong 2
+>>>0x80004 quad 0
+>>>>0x80000 use adf-rootblock
+# 2 MiB hard disc; only works if in ../../src/file.h FILE_BYTES_MAX is raised to 2 MiB
+#>>0x100000 ubelong x 2 MiB TEST
+#>>0x100000 ubelong 2 \b, 2 MiB hard disc rootblock
+#>>>0x100000 use adf-rootblock
+0 string KICK Kickstart disk
+
+# From: Alex Beregszaszi <alex@fsn.hu>
+0 string LZX LZX compressed archive (Amiga)
+
+# From: Przemek Kramarczyk <pkramarczyk@gmail.com>
+0 string .KEY AmigaDOS script
+0 string .key AmigaDOS script
+
+# AMOS Basic file formats
+# https://www.exotica.org.uk/wiki/AMOS_file_formats
+0 string AMOS\040Basic\040 AMOS Basic source code
+>11 byte =0x56 \b, tested
+>11 byte =0x76 \b, untested
+0 string AMOS\040Pro AMOS Basic source code
+>11 byte =0x56 \b, tested
+>11 byte =0x76 \b, untested
+0 string AmSp AMOS Basic sprite bank
+>4 beshort x \b, %d sprites
+0 string AmIc AMOS Basic icon bank
+>4 beshort x \b, %d icons
+0 string AmBk AMOS Basic memory bank
+>4 beshort x \b, bank number %d
+>8 belong&0xFFFFFFF x \b, length %d
+>12 regex .{8} \b, type %s
+0 string AmBs AMOS Basic memory banks
+>4 beshort x \b, %d banks
diff --git a/contrib/libs/libmagic/magic/Magdir/android b/contrib/libs/libmagic/magic/Magdir/android
new file mode 100644
index 0000000000..8a2dedf3d2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/android
@@ -0,0 +1,259 @@
+
+#------------------------------------------------------------
+# $File: android,v 1.24 2023/02/20 16:51:59 christos Exp $
+# Various android related magic entries
+#------------------------------------------------------------
+
+# Dalvik .dex format. http://retrodev.com/android/dexformat.html
+# From <mkf@google.com> "Mike Fleming"
+# Fixed to avoid regexec 17 errors on some dex files
+# From <diff@lookout.com> "Tim Strazzere"
+0 string dex\n
+>0 regex dex\n[0-9]{2}\0 Dalvik dex file
+>4 string >000 version %s
+0 string dey\n
+>0 regex dey\n[0-9]{2}\0 Dalvik dex file (optimized for host)
+>4 string >000 version %s
+
+# Android bootimg format
+# From https://android.googlesource.com/\
+# platform/system/core/+/master/mkbootimg/bootimg.h
+# https://github.com/djrbliss/loki/blob/master/loki.h#L43
+0 string ANDROID! Android bootimg
+>1024 string LOKI \b, LOKI'd
+>>1028 lelong 0 \b (boot)
+>>1028 lelong 1 \b (recovery)
+>8 lelong >0 \b, kernel
+>>12 lelong >0 \b (%#x)
+>16 lelong >0 \b, ramdisk
+>>20 lelong >0 \b (%#x)
+>24 lelong >0 \b, second stage
+>>28 lelong >0 \b (%#x)
+>36 lelong >0 \b, page size: %d
+>38 string >0 \b, name: %s
+>64 string >0 \b, cmdline (%s)
+
+# Android Backup archive
+# From: Ariel Shkedi
+# Update: Joerg Jenderek
+# URL: https://github.com/android/platform_frameworks_base/blob/\
+# 0bacfd2ba68d21a68a3df345b830bc2a1e515b5a/services/java/com/\
+# android/server/BackupManagerService.java#L2367
+# Reference: https://sourceforge.net/projects/adbextractor/
+# android-backup-extractor/perl/backupencrypt.pl
+# Note: only unix line feeds "\n" found
+# After the header comes a tar file
+# If compressed, the entire tar file is compressed with JAVA deflate
+#
+# Include the version number hardcoded with the magic string to avoid
+# false positives
+0 string/b ANDROID\ BACKUP\n Android Backup
+# maybe look for some more characteristics like linefeed '\n' or version
+#>16 string \n
+# No mime-type defined officially
+!:mime application/x-google-ab
+!:ext ab
+# on 2nd line version (often 1, 2 on kitkat 4.4.3+, 4 on 7.1.2)
+>15 string >\0 \b, version %s
+# "1" on 3rd line means compressed
+>17 string 0\n \b, Not-Compressed
+>17 string 1\n \b, Compressed
+# The 4th line is encryption "none" or "AES-256"
+# any string as long as it's not the word none (which is matched below)
+>19 string none\n \b, Not-Encrypted
+# look for backup content after line with encryption info
+#>>19 search/7 \n
+# data part after header for not encrypted Android Backup
+#>>>&0 ubequad x \b, content %#16.16llx...
+# look for zlib compressed by ./compress after message with 1 space at end
+#>>>&0 indirect x \b; contains
+# look for tar archive block by ./archive for package name manifest
+>>288 string ustar \b; contains
+>>>31 use tar-file
+# look for zip/jar archive by ./archive ./zip after message with 1 space at end
+#>>2079 search/1025/s PK\003\004 \b; contains
+#>>>&0 indirect x
+>19 string !none
+>>19 regex/1l \^([^n\n]|n[^o]|no[^n]|non[^e]|none.+).* \b, Encrypted (%s)
+# Commented out because they don't seem useful to print
+# (but they are part of the header - the tar file comes after them):
+# The 5th line is User Password Salt (128 Hex)
+# string length too high with standard src configuration
+#>>>&1 string >\0 \b, PASSWORD salt: "%-128.128s"
+#>>>&1 regex/1l .* \b, Password salt: %s
+# The 6th line is Master Key Checksum Salt (128 Hex)
+#>>>>&1 regex/1l .* \b, Master salt: %s
+# The 7th line is Number of PBDKF2 Rounds (10000)
+#>>>>>&1 regex/1l .* \b, PBKDF2 rounds: %s
+# The 8th line is User key Initialization Vector (IV) (32 Hex)
+#>>>>>>&1 regex/1l .* \b, IV: %s
+#>>>>>>&1 regex/1l .* \b, IV: %s
+# The 9th line is Master IV+Key+Checksum (192 Hex)
+#>>>>>>>&1 regex/1l .* \b, Key: %s
+# look for new line separator char after line number 9
+#>>>0x204 ubyte 0x0a NL found
+#>>>>&1 ubequad x \b, Content magic %16.16llx
+
+# *.pit files by Joerg Jenderek
+# https://forum.xda-developers.com/showthread.php?p=9122369
+# https://forum.xda-developers.com/showthread.php?t=816449
+# Partition Information Table for Samsung's smartphone with Android
+# used by flash software Odin
+0 ulelong 0x12349876
+# 1st pit entry marker
+>0x01C ulequad&0xFFFFFFFCFFFFFFFC =0x0000000000000000
+# minimal 13 and maximal 18 PIT entries found
+>>4 ulelong <128 Partition Information Table for Samsung smartphone
+>>>4 ulelong x \b, %d entries
+# 1. pit entry
+>>>4 ulelong >0 \b; #1
+>>>0x01C use PIT-entry
+>>>4 ulelong >1 \b; #2
+>>>0x0A0 use PIT-entry
+>>>4 ulelong >2 \b; #3
+>>>0x124 use PIT-entry
+>>>4 ulelong >3 \b; #4
+>>>0x1A8 use PIT-entry
+>>>4 ulelong >4 \b; #5
+>>>0x22C use PIT-entry
+>>>4 ulelong >5 \b; #6
+>>>0x2B0 use PIT-entry
+>>>4 ulelong >6 \b; #7
+>>>0x334 use PIT-entry
+>>>4 ulelong >7 \b; #8
+>>>0x3B8 use PIT-entry
+>>>4 ulelong >8 \b; #9
+>>>0x43C use PIT-entry
+>>>4 ulelong >9 \b; #10
+>>>0x4C0 use PIT-entry
+>>>4 ulelong >10 \b; #11
+>>>0x544 use PIT-entry
+>>>4 ulelong >11 \b; #12
+>>>0x5C8 use PIT-entry
+>>>4 ulelong >12 \b; #13
+>>>>0x64C use PIT-entry
+# 14. pit entry
+>>>4 ulelong >13 \b; #14
+>>>>0x6D0 use PIT-entry
+>>>4 ulelong >14 \b; #15
+>>>0x754 use PIT-entry
+>>>4 ulelong >15 \b; #16
+>>>0x7D8 use PIT-entry
+>>>4 ulelong >16 \b; #17
+>>>0x85C use PIT-entry
+# 18. pit entry
+>>>4 ulelong >17 \b; #18
+>>>0x8E0 use PIT-entry
+
+0 name PIT-entry
+# garbage value implies end of pit entries
+>0x00 ulequad&0xFFFFFFFCFFFFFFFC =0x0000000000000000
+# skip empty partition name
+>>0x24 ubyte !0
+# partition name
+>>>0x24 string >\0 %-.32s
+# flags
+>>>0x0C ulelong&0x00000002 2 \b+RW
+# partition ID:
+# 0~IPL,MOVINAND,GANG;1~PIT,GPT;2~HIDDEN;3~SBL,HIDDEN;4~SBL2,HIDDEN;5~BOOT;6~kernel,RECOVER,misc;7~RECOVER
+# ;11~MODEM;20~efs;21~PARAM;22~FACTORY,SYSTEM;23~DBDATAFS,USERDATA;24~CACHE;80~BOOTLOADER;81~TZSW
+>>>0x08 ulelong x (%#x)
+# filename
+>>>0x44 string >\0 "%-.64s"
+#>>>0x18 ulelong >0
+# blocksize in 512 byte units ?
+#>>>>0x18 ulelong x \b, %db
+# partition size in blocks ?
+#>>>>0x22 ulelong x \b*%d
+
+# Android sparse img format
+# From https://android.googlesource.com/\
+# platform/system/core/+/master/libsparse/sparse_format.h
+0 lelong 0xed26ff3a Android sparse image
+>4 leshort x \b, version: %d
+>6 leshort x \b.%d
+>16 lelong x \b, Total of %d
+>12 lelong x \b %d-byte output blocks in
+>20 lelong x \b %d input chunks.
+
+# Android binary XML magic
+# In include/androidfw/ResourceTypes.h:
+# RES_XML_TYPE = 0x0003 followed by the size of the header (ResXMLTree_header),
+# which is 8 bytes (2 bytes type + 2 bytes header size + 4 bytes size).
+# The strength is increased to avoid misidentifying as Targa image data
+0 lelong 0x00080003 Android binary XML
+!:strength +1
+
+# Android cryptfs footer
+# From https://android.googlesource.com/\
+# platform/system/vold/+/refs/heads/master/cryptfs.h
+0 lelong 0xd0b5b1c4 Android cryptfs footer
+>4 leshort x \b, version: %d
+>6 leshort x \b.%d
+
+# Android Vdex format
+# From https://android.googlesource.com/\
+# platform/art/+/master/runtime/vdex_file.h
+0 string vdex Android vdex file,
+>4 string >000 verifier deps version: %s,
+>8 string >000 dex section version: %s,
+>12 lelong >0 number of dex files: %d,
+>16 lelong >0 verifier deps size: %d
+
+# Android Vdex format, dexfile is currently being updated
+# by android system
+# From https://android.googlesource.com/\
+# platform/art/+/master/dex2oat/dex2oat.cc
+0 string wdex Android vdex file, being processed by dex2oat,
+>4 string >000 verifier deps version: %s,
+>8 string >000 dex section version: %s,
+>12 lelong >0 number of dex files: %d,
+>16 lelong >0 verifier deps size: %d
+
+# Disassembled DEX files
+0 string/t .class\x20
+>&0 regex/512 \^\\.super\x20L.*;$ disassembled Android DEX Java class (smali/baksmali)
+!:ext smali
+
+# Android ART (baseline) profile + metadata: baseline.prof, baseline.profm
+# Reference: https://android.googlesource.com/platform/frameworks/support/\
+# +/refs/heads/androidx-main/profileinstaller/profileinstaller/\
+# src/main/java/androidx/profileinstaller/ProfileTranscoder.java
+# Reference: https://android.googlesource.com/platform/frameworks/support/\
+# +/refs/heads/androidx-main/profileinstaller/profileinstaller/\
+# src/main/java/androidx/profileinstaller/ProfileVersion.java
+0 string pro\x00
+>0 regex pro\x000[0-9][0-9]\x00 Android ART profile
+!:ext prof
+>>4 string 001\x00 \b, version 001 N
+>>4 string 005\x00 \b, version 005 O
+>>4 string 009\x00 \b, version 009 O MR1
+>>4 string 010\x00 \b, version 010 P
+>>4 string 015\x00 \b, version 015 S
+0 string prm\x00
+>0 regex prm\x000[0-9][0-9]\x00 Android ART profile metadata
+!:ext profm
+>>4 string 001\x00 \b, version 001 N
+>>4 string 002\x00 \b, version 002
+
+# Android package resource table (ARSC): resources.arsc
+# Reference: https://android.googlesource.com/platform/tools/base/\
+# +/refs/heads/mirror-goog-studio-main/apkparser/binary-resources/\
+# src/main/java/com/google/devrel/gmscore/tools/apk/arsc
+# 00: resource table type = 0x0002 (2) + header size = 12 (2)
+# 04: chunk size (4, skipped)
+# 08: #packages (4)
+0 ulelong 0x000c0002 Android package resource table (ARSC)
+!:ext arsc
+>8 ulelong !1 \b, %d packages
+# 12: string pool type = 0x0001 (2) + header size = 28 (2)
+# 16: chunk size (4, skipped)
+# 20: #strings (4), #styles (4), flags (4)
+>12 ulelong 0x001c0001
+>>20 ulelong !0 \b, %d string(s)
+>>24 ulelong !0 \b, %d style(s)
+>>28 ulelong &1 \b, sorted
+>>28 ulelong &256 \b, utf8
+
+# extracted APK Signing Block
+-16 string APK\x20Sig\x20Block\x2042 APK Signing Block
diff --git a/contrib/libs/libmagic/magic/Magdir/animation b/contrib/libs/libmagic/magic/Magdir/animation
new file mode 100644
index 0000000000..aab93ca34a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/animation
@@ -0,0 +1,1206 @@
+
+#------------------------------------------------------------------------------
+# $File: animation,v 1.94 2023/06/16 20:06:50 christos Exp $
+# animation: file(1) magic for animation/movie formats
+#
+# animation formats
+# MPEG, FLI, DL originally from vax@ccwf.cc.utexas.edu (VaX#n8)
+# FLC, SGI, Apple originally from Daniel Quinlan (quinlan@yggdrasil.com)
+
+# SGI and Apple formats
+0 string MOVI Silicon Graphics movie file
+!:mime video/x-sgi-movie
+4 string moov Apple QuickTime
+!:mime video/quicktime
+>12 string mvhd \b movie (fast start)
+>12 string mdra \b URL
+>12 string cmov \b movie (fast start, compressed header)
+>12 string rmra \b multiple URLs
+4 string mdat Apple QuickTime movie (unoptimized)
+!:mime video/quicktime
+4 string wide Apple QuickTime movie (unoptimized)
+!:mime video/quicktime
+#4 string skip Apple QuickTime movie (modified)
+#!:mime video/quicktime
+#4 string free Apple QuickTime movie (modified)
+#!:mime video/quicktime
+4 string idsc Apple QuickTime image (fast start)
+!:mime image/x-quicktime
+#4 string idat Apple QuickTime image (unoptimized)
+#!:mime image/x-quicktime
+4 string pckg Apple QuickTime compressed archive
+!:mime application/x-quicktime-player
+
+#### MP4 ####
+# https://www.ftyps.com/ with local additions
+# https://cconcolato.github.io/mp4ra/filetype.html
+4 string ftyp ISO Media
+# https://aeroquartet.com/wordpress/2016/03/05/3-xavc-s/
+>8 string XAVC \b, MPEG v4 system, Sony XAVC Codec
+!:mime video/mp4
+>>96 string x \b, Audio "%.4s"
+>>118 beshort x at %dHz
+>>140 string x \b, Video "%.4s"
+>>168 beshort x %d
+>>170 beshort x \bx%d
+>8 string 3g2 \b, MPEG v4 system, 3GPP2
+!:mime video/3gpp2
+>>11 byte 4 \b v4 (H.263/AMR GSM 6.10)
+>>11 byte 5 \b v5 (H.263/AMR GSM 6.10)
+>>11 byte 6 \b v6 (ITU H.264/AMR GSM 6.10)
+# https://www.3gpp2.org/Public_html/Specs/C.S0050-B_v1.0_070521.pdf
+# Section 8.1.1, corresponds to a, b, c
+>>11 byte 0x61 \b C.S0050-0 V1.0
+>>11 byte 0x62 \b C.S0050-0-A V1.0.0
+>>11 byte 0x63 \b C.S0050-0-B V1.0
+>8 string 3ge \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 6 \b, Release %d MBMS Extended Presentations
+>>11 byte 7 \b, Release %d MBMS Extended Presentations
+>>11 byte 9 \b, Release %d MBMS Extended Presentations
+>8 string 3gf \b, MPEG v4 system, 3GPP
+>>11 byte 9 \b, Release %d File-delivery profile
+>8 string 3gg \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 6 \b, Release %d General Profile
+>>11 byte 9 \b, Release %d General Profile
+>8 string 3gh \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 9 \b, Release %d Adaptive Streaming Profile
+>8 string 3gm \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 9 \b, Release %d Media Segment Profile
+>8 string 3gp \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 1 \b, Release %d (non existent)
+>>11 byte 2 \b, Release %d (non existent)
+>>11 byte 3 \b, Release %d (non existent)
+>>11 byte 4 \b, Release %d
+>>11 byte 5 \b, Release %d
+>>11 byte 6 \b, Release %d
+>>11 byte 7 \b, Release %d Streaming Servers
+>8 string 3gr \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 6 \b, Release %d Progressive Download Profile
+>>11 byte 9 \b, Release %d Progressive Download Profile
+>8 string 3gs \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 6 \b, Release %d Streaming Servers
+>>11 byte 7 \b, Release %d Streaming Servers
+>>11 byte 9 \b, Release %d Streaming Servers
+>8 string 3gt \b, MPEG v4 system, 3GPP
+!:mime video/3gpp
+>>11 byte 8 \b, Release %d Media Stream Recording Profile
+>>11 byte 9 \b, Release %d Media Stream Recording Profile
+>8 string ARRI \b, MPEG v4 system, ARRI Digital Camera
+!:mime video/mp4
+>8 string avc1 \b, MPEG v4 system, 3GPP JVT AVC [ISO 14496-12:2005]
+!:mime video/mp4
+>8 string bbxm \b, Blinkbox Master File: H.264 video/16-bit LE LPCM audio
+!:mime video/mp4
+>8 string/W qt \b, Apple QuickTime movie
+!:mime video/quicktime
+>8 string CAEP \b, Canon Digital Camera
+>8 string caqv \b, Casio Digital Camera
+>8 string CDes \b, Convergent Design
+>8 string caaa \b, CMAF Media Profile - AAC Adaptive Audio
+>8 string caac \b, CMAF Media Profile - AAC Core
+>8 string caqv \b, Casio Digital Camera Casio
+>8 string ccea \b, CMAF Supplemental Data - CEA-608/708
+>8 string ccff \b, Common container file format
+>8 string cfhd \b, CMAF Media Profile - AVC HD
+>8 string cfsd \b, CMAF Media Profile - AVC SD
+>8 string chd1 \b, CMAF Media Profile - HEVC HDR10
+>8 string chdf \b, CMAF Media Profile - AVC HDHF
+>8 string chhd \b, CMAF Media Profile - HEVC HHD8
+>8 string chh1 \b, CMAF Media Profile - HEVC HHD10
+>8 string clg1 \b, CMAF Media Profile - HEVC HLG10
+>8 string cmfc \b, CMAF Track Format
+>8 string cmff \b, CMAF Fragment Format
+>8 string cmfl \b, CMAF Chunk Format
+>8 string cmfs \b, CMAF Segment Format
+>8 string cud1 \b, CMAF Media Profile - HEVC UHD10
+>8 string cud8 \b, CMAF Media Profile - HEVC UHD8
+>8 string cwvt \b, CMAF Media Profile - WebVTT
+>8 string da0a \b, DMB MAF w/ MPEG Layer II aud, MOT slides, DLS, JPG/PNG/MNG
+>8 string da0b \b, DMB MAF, ext DA0A, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string da1a \b, DMB MAF audio with ER-BSAC audio, JPG/PNG/MNG images
+>8 string da1b \b, DMB MAF, ext da1a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string da2a \b, DMB MAF aud w/ HE-AAC v2 aud, MOT slides, DLS, JPG/PNG/MNG
+>8 string da2b \b, DMB MAF, ext da2a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string da3a \b, DMB MAF aud with HE-AAC aud, JPG/PNG/MNG images
+>8 string da3b \b, DMB MAF, ext da3a w/ BIFS, 3GPP, DID, TVA, REL, IPMP
+>8 string dash \b, MPEG v4 system, Dynamic Adaptive Streaming over HTTP
+!:mime video/mp4
+>8 string dby1 \b, MP4 files with Dolby content
+>8 string dsms \b, Media Segment DASH conformant
+>8 string dts1 \b, MP4 track file with audio codecs dtsc dtsh or dtse
+>8 string dts2 \b, MP4 track file with audio codec dtsx
+>8 string dts3 \b, MP4 track file with audio codec dtsy
+>8 string dxo$20 \b, DxO ONE camera
+>8 string dmb1 \b, DMB MAF supporting all the components defined in the spec
+>8 string dmpf \b, Digital Media Project
+>8 string drc1 \b, Dirac (wavelet compression), encap in ISO base media (MP4)
+>8 string dv1a \b, DMB MAF vid w/ AVC vid, ER-BSAC aud, BIFS, JPG/PNG/MNG, TS
+>8 string dv1b \b, DMB MAF, ext dv1a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string dv2a \b, DMB MAF vid w/ AVC vid, HE-AAC v2 aud, BIFS, JPG/PNG/MNG, TS
+>8 string dv2b \b, DMB MAF, ext dv2a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string dv3a \b, DMB MAF vid w/ AVC vid, HE-AAC aud, BIFS, JPG/PNG/MNG, TS
+>8 string dv3b \b, DMB MAF, ext dv3a, with 3GPP timed text, DID, TVA, REL, IPMP
+>8 string dvr1 \b, DVB (.DVB) over RTP
+!:mime video/vnd.dvb.file
+>8 string dvt1 \b, DVB (.DVB) over MPEG-2 Transport Stream
+>8 string emsg \b, Event message box present
+!:mime video/vnd.dvb.file
+>8 string F4V \b, Video for Adobe Flash Player 9+ (.F4V)
+!:mime video/mp4
+>8 string F4P \b, Protected Video for Adobe Flash Player 9+ (.F4P)
+!:mime video/mp4
+>8 string F4A \b, Audio for Adobe Flash Player 9+ (.F4A)
+!:mime audio/mp4
+>8 string F4B \b, Audio Book for Adobe Flash Player 9+ (.F4B)
+!:mime audio/mp4
+>8 string ifrm \b, Apple iFrame Specification, Version 8.1 Jan 2013
+>8 string im1i \b, CMAF Media Profile - IMSC1 Image
+>8 string im1t \b, CMAF Media Profile - IMSC1 Text
+>8 string isc2 \b, ISMACryp 2.0 Encrypted File
+# ?/enc-isoff-generic
+>8 string iso \b, MP4 Base Media
+!:mime video/mp4
+!:ext mp4
+>>11 string m v1 [ISO 14496-12:2003]
+>>11 string 2 v2 [ISO 14496-12:2005]
+>>11 string 4 v4
+>>11 string 5 v5
+>>11 string 6 v6
+>8 string isml \b, MP4 Base Media v2 [ISO 14496-12:2005]
+!:mime video/mp4
+>8 string J2P0 \b, JPEG2000 Profile 0
+>8 string J2P1 \b, JPEG2000 Profile 1
+>8 string/W jp2 \b, JPEG 2000
+!:mime image/jp2
+>8 string JP2 \b, JPEG 2000 Image (.JP2) [ISO 15444-1 ?]
+!:mime image/jp2
+>8 string JP20 \b, Unknown, from GPAC samples (prob non-existent)
+>8 string jpm \b, JPEG 2000 Compound Image (.JPM) [ISO 15444-6]
+!:mime image/jpm
+>8 string jpsi \b, The JPSearch data interchange format
+>8 string jpx \b, JPEG 2000 w/ extensions (.JPX) [ISO 15444-2]
+!:mime image/jpx
+>8 string KDDI \b, 3GPP2 EZmovie for KDDI 3G cellphones
+!:mime video/3gpp2
+>8 string LCAG \b, Leica digital camera
+>8 string lmsg \b, Last Media Segment indicator for ISO base media file format.
+>8 string M4A \b, Apple iTunes ALAC/AAC-LC (.M4A) Audio
+!:mime audio/x-m4a
+>8 string M4B \b, Apple iTunes ALAC/AAC-LC (.M4B) Audio Book
+!:mime audio/mp4
+>8 string M4P \b, Apple iTunes ALAC/AAC-LC (.M4P) AES Protected Audio
+!:mime video/mp4
+>8 string M4V \b, Apple iTunes Video (.M4V) Video
+!:mime video/x-m4v
+>8 string M4VH \b, Apple TV (.M4V)
+!:mime video/x-m4v
+>8 string M4VP \b, Apple iPhone (.M4V)
+!:mime video/x-m4v
+>8 string mj2s \b, Motion JPEG 2000 [ISO 15444-3] Simple Profile
+!:mime video/mj2
+>8 string mjp2 \b, Motion JPEG 2000 [ISO 15444-3] General Profile
+>8 string MFSM \b, Media File for Samsung video Metadata
+>8 string MGSV \b, Sony Home and Mobile Multimedia Platform (HMMP)
+!:mime video/mj2
+>8 string mmp4 \b, MPEG-4/3GPP Mobile Profile (.MP4 / .3GP) (for NTT)
+!:mime video/mp4
+>8 string mobi \b, MPEG-4, MOBI format
+!:mime video/mp4
+>8 string mp21 \b, MPEG-21 [ISO/IEC 21000-9]
+>8 string mp41 \b, MP4 v1 [ISO 14496-1:ch13]
+!:mime video/mp4
+>8 string mp42 \b, MP4 v2 [ISO 14496-14]
+!:mime video/mp4
+>8 string mp71 \b, MP4 w/ MPEG-7 Metadata [per ISO 14496-12]
+>8 string mp7t \b, MPEG v4 system, MPEG v7 XML
+>8 string mp7b \b, MPEG v4 system, MPEG v7 binary XML
+>8 string mpuf \b, Compliance with the MMT Processing Unit format
+>8 string msdh \b, Media Segment conforming to ISO base media file format.
+>8 string msix \b, Media Segment conforming to ISO base media file format.
+>8 string mmp4 \b, MPEG v4 system, 3GPP Mobile
+!:mime video/mp4
+>8 string MPPI \b, Photo Player, MAF [ISO/IEC 23000-3]
+>8 string mqt \b, Sony / Mobile QuickTime (.MQV) US Pat 7,477,830
+!:mime video/quicktime
+>8 string MSNV \b, MPEG-4 (.MP4) for SonyPSP
+!:mime audio/mp4
+>8 string NDAS \b, MP4 v2 [ISO 14496-14] Nero Digital AAC Audio
+!:mime audio/mp4
+>8 string NDSC \b, MPEG-4 (.MP4) Nero Cinema Profile
+!:mime video/mp4
+>8 string NDSH \b, MPEG-4 (.MP4) Nero HDTV Profile
+!:mime video/mp4
+>8 string NDSM \b, MPEG-4 (.MP4) Nero Mobile Profile
+!:mime video/mp4
+>8 string NDSP \b, MPEG-4 (.MP4) Nero Portable Profile
+!:mime video/mp4
+>8 string NDSS \b, MPEG-4 (.MP4) Nero Standard Profile
+!:mime video/mp4
+>8 string NDXC \b, H.264/MPEG-4 AVC (.MP4) Nero Cinema Profile
+!:mime video/mp4
+>8 string NDXH \b, H.264/MPEG-4 AVC (.MP4) Nero HDTV Profile
+!:mime video/mp4
+>8 string NDXM \b, H.264/MPEG-4 AVC (.MP4) Nero Mobile Profile
+!:mime video/mp4
+>8 string NDXP \b, H.264/MPEG-4 AVC (.MP4) Nero Portable Profile
+!:mime video/mp4
+>8 string NDXS \b, H.264/MPEG-4 AVC (.MP4) Nero Standard Profile
+>8 string niko \b, Nikon Digital Camera
+!:mime video/mp4
+>8 string odcf \b, OMA DCF DRM Format 2.0 (OMA-TS-DRM-DCF-V2_0-20060303-A)
+>8 string opf2 \b, OMA PDCF DRM Format 2.1 (OMA-TS-DRM-DCF-V2_1-20070724-C)
+>8 string opx2 \b, OMA PDCF DRM + XBS ext (OMA-TS-DRM_XBS-V1_0-20070529-C)
+>8 string pana \b, Panasonic Digital Camera
+>8 string piff \b, Protected Interoperable File Format
+>8 string pnvi ]b, Panasonic Video Intercom
+>8 string qt \b, Apple QuickTime (.MOV/QT)
+!:mime video/quicktime
+# HEIF image format
+# see https://nokiatech.github.io/heif/technical.html
+>8 string mif1 \b, HEIF Image
+!:mime image/heif
+>8 string msf1 \b, HEIF Image Sequence
+!:mime image/heif-sequence
+>8 string heic \b, HEIF Image HEVC Main or Main Still Picture Profile
+!:mime image/heic
+>8 string heix \b, HEIF Image HEVC Main 10 Profile
+!:mime image/heic
+>8 string hevc \b, HEIF Image Sequenz HEVC Main or Main Still Picture Profile
+!:mime image/heic-sequence
+>8 string hevx \b, HEIF Image Sequence HEVC Main 10 Profile
+!:mime image/heic-sequence
+# following HEIF brands are not mentioned in the heif technical info currently (Oct 2017)
+# but used in the reference implementation:
+# https://github.com/nokiatech/heif/blob/d5e9a21c8ba8df712bdf643021dd9f6518134776/Srcs/reader/hevcimagefilereader.cpp
+>8 string heim \b, HEIF Image L-HEVC
+!:mime image/heif
+>8 string heis \b, HEIF Image L-HEVC
+!:mime image/heif
+>8 string avic \b, HEIF Image AVC
+!:mime image/heif
+>8 string hevm \b, HEIF Image Sequence L-HEVC
+!:mime image/heif-sequence
+>8 string hevs \b, HEIF Image Sequence L-HEVC
+!:mime image/heif-sequence
+>8 string avcs \b, HEIF Image Sequence AVC
+!:mime image/heif-sequence
+# AVIF image format
+# see https://aomediacodec.github.io/av1-avif/
+>8 string avif \b, AVIF Image
+!:mime image/avif
+>8 string avis \b, AVIF Image Sequence
+!:mime image/avif
+>8 string risx \b, Representation Index Segment for MPEG-2 TS Segments
+>8 string ROSS \b, Ross Video
+>8 string sdv \b, SD Memory Card Video
+>8 string ssc1 \b, Samsung stereo, single stream (patent pending)
+>8 string ssc2 \b, Samsung stereo, dual stream (patent pending)
+>8 string SEAU \b, Sony Home and Mobile Multimedia Platform (HMMP)
+>8 string SEBK \b, Sony Home and Mobile Multimedia Platform (HMMP)
+>8 string senv \b, Video contents Sony Entertainment Network
+>8 string sims \b, Media Segment for Sub-Indexed Media Segment format
+>8 string sisx \b, Single Index Segment forindex MPEG-2 TS
+>8 string ssss \b, Subsegment Index Segment used to index MPEG-2 Segments
+>8 string uvvu \b, UltraViolet file brand for DECE Common Format
+
+# MPEG sequences
+# Scans for all common MPEG header start codes
+0 belong 0x00000001
+>4 byte&0x1F 0x07 JVT NAL sequence, H.264 video
+>>5 byte 66 \b, baseline
+>>5 byte 77 \b, main
+>>5 byte 88 \b, extended
+>>7 byte x \b @ L %u
+0 belong&0xFFFFFF00 0x00000100
+>3 byte 0xBA MPEG sequence
+!:mime video/mpeg
+# http://fileformats.archiveteam.org/wiki/Enhanced_VOB
+# https://reposcope.com/mimetype/video/mpeg
+!:ext vob/evo/mpg/mpeg
+>>4 byte &0x40 \b, v2, program multiplex
+>>4 byte ^0x40 \b, v1, system multiplex
+>3 byte 0xBB MPEG sequence, v1/2, multiplex (missing pack header)
+>3 byte&0x1F 0x07 MPEG sequence, H.264 video
+>>4 byte 66 \b, baseline
+>>4 byte 77 \b, main
+>>4 byte 88 \b, extended
+>>6 byte x \b @ L %u
+# GRR too general as it catches also FoxPro Memo example NG.FPT
+>3 byte 0xB0 MPEG sequence, v4
+# TODO: maybe this extra line exclude FoxPro Memo example NG.FPT starting with 000001b0 00000100 00000000
+#>>4 byte !0 MPEG sequence, v4
+!:mime video/mpeg4-generic
+>>5 belong 0x000001B5
+>>>9 byte &0x80
+>>>>10 byte&0xF0 16 \b, video
+>>>>10 byte&0xF0 32 \b, still texture
+>>>>10 byte&0xF0 48 \b, mesh
+>>>>10 byte&0xF0 64 \b, face
+>>>9 byte&0xF8 8 \b, video
+>>>9 byte&0xF8 16 \b, still texture
+>>>9 byte&0xF8 24 \b, mesh
+>>>9 byte&0xF8 32 \b, face
+>>4 byte 1 \b, simple @ L1
+>>4 byte 2 \b, simple @ L2
+>>4 byte 3 \b, simple @ L3
+>>4 byte 4 \b, simple @ L0
+>>4 byte 17 \b, simple scalable @ L1
+>>4 byte 18 \b, simple scalable @ L2
+>>4 byte 33 \b, core @ L1
+>>4 byte 34 \b, core @ L2
+>>4 byte 50 \b, main @ L2
+>>4 byte 51 \b, main @ L3
+>>4 byte 53 \b, main @ L4
+>>4 byte 66 \b, n-bit @ L2
+>>4 byte 81 \b, scalable texture @ L1
+>>4 byte 97 \b, simple face animation @ L1
+>>4 byte 98 \b, simple face animation @ L2
+>>4 byte 99 \b, simple face basic animation @ L1
+>>4 byte 100 \b, simple face basic animation @ L2
+>>4 byte 113 \b, basic animation text @ L1
+>>4 byte 114 \b, basic animation text @ L2
+>>4 byte 129 \b, hybrid @ L1
+>>4 byte 130 \b, hybrid @ L2
+>>4 byte 145 \b, advanced RT simple @ L!
+>>4 byte 146 \b, advanced RT simple @ L2
+>>4 byte 147 \b, advanced RT simple @ L3
+>>4 byte 148 \b, advanced RT simple @ L4
+>>4 byte 161 \b, core scalable @ L1
+>>4 byte 162 \b, core scalable @ L2
+>>4 byte 163 \b, core scalable @ L3
+>>4 byte 177 \b, advanced coding efficiency @ L1
+>>4 byte 178 \b, advanced coding efficiency @ L2
+>>4 byte 179 \b, advanced coding efficiency @ L3
+>>4 byte 180 \b, advanced coding efficiency @ L4
+>>4 byte 193 \b, advanced core @ L1
+>>4 byte 194 \b, advanced core @ L2
+>>4 byte 209 \b, advanced scalable texture @ L1
+>>4 byte 210 \b, advanced scalable texture @ L2
+>>4 byte 211 \b, advanced scalable texture @ L3
+>>4 byte 225 \b, simple studio @ L1
+>>4 byte 226 \b, simple studio @ L2
+>>4 byte 227 \b, simple studio @ L3
+>>4 byte 228 \b, simple studio @ L4
+>>4 byte 229 \b, core studio @ L1
+>>4 byte 230 \b, core studio @ L2
+>>4 byte 231 \b, core studio @ L3
+>>4 byte 232 \b, core studio @ L4
+>>4 byte 240 \b, advanced simple @ L0
+>>4 byte 241 \b, advanced simple @ L1
+>>4 byte 242 \b, advanced simple @ L2
+>>4 byte 243 \b, advanced simple @ L3
+>>4 byte 244 \b, advanced simple @ L4
+>>4 byte 245 \b, advanced simple @ L5
+>>4 byte 247 \b, advanced simple @ L3b
+>>4 byte 248 \b, FGS @ L0
+>>4 byte 249 \b, FGS @ L1
+>>4 byte 250 \b, FGS @ L2
+>>4 byte 251 \b, FGS @ L3
+>>4 byte 252 \b, FGS @ L4
+>>4 byte 253 \b, FGS @ L5
+>3 byte 0xB5 MPEG sequence, v4
+!:mime video/mpeg4-generic
+>>4 byte &0x80
+>>>5 byte&0xF0 16 \b, video (missing profile header)
+>>>5 byte&0xF0 32 \b, still texture (missing profile header)
+>>>5 byte&0xF0 48 \b, mesh (missing profile header)
+>>>5 byte&0xF0 64 \b, face (missing profile header)
+>>4 byte&0xF8 8 \b, video (missing profile header)
+>>4 byte&0xF8 16 \b, still texture (missing profile header)
+>>4 byte&0xF8 24 \b, mesh (missing profile header)
+>>4 byte&0xF8 32 \b, face (missing profile header)
+>3 byte 0xB3 MPEG sequence
+!:mime video/mpeg
+>>12 belong 0x000001B8 \b, v1, progressive Y'CbCr 4:2:0 video
+>>12 belong 0x000001B2 \b, v1, progressive Y'CbCr 4:2:0 video
+>>12 belong 0x000001B5 \b, v2,
+>>>16 byte&0x0F 1 \b HP
+>>>16 byte&0x0F 2 \b Spt
+>>>16 byte&0x0F 3 \b SNR
+>>>16 byte&0x0F 4 \b MP
+>>>16 byte&0x0F 5 \b SP
+>>>17 byte&0xF0 64 \b@HL
+>>>17 byte&0xF0 96 \b@H-14
+>>>17 byte&0xF0 128 \b@ML
+>>>17 byte&0xF0 160 \b@LL
+>>>17 byte &0x08 \b progressive
+>>>17 byte ^0x08 \b interlaced
+>>>17 byte&0x06 2 \b Y'CbCr 4:2:0 video
+>>>17 byte&0x06 4 \b Y'CbCr 4:2:2 video
+>>>17 byte&0x06 6 \b Y'CbCr 4:4:4 video
+>>11 byte &0x02
+>>>75 byte &0x01
+>>>>140 belong 0x000001B8 \b, v1, progressive Y'CbCr 4:2:0 video
+>>>>140 belong 0x000001B2 \b, v1, progressive Y'CbCr 4:2:0 video
+>>>>140 belong 0x000001B5 \b, v2,
+>>>>>144 byte&0x0F 1 \b HP
+>>>>>144 byte&0x0F 2 \b Spt
+>>>>>144 byte&0x0F 3 \b SNR
+>>>>>144 byte&0x0F 4 \b MP
+>>>>>144 byte&0x0F 5 \b SP
+>>>>>145 byte&0xF0 64 \b@HL
+>>>>>145 byte&0xF0 96 \b@H-14
+>>>>>145 byte&0xF0 128 \b@ML
+>>>>>145 byte&0xF0 160 \b@LL
+>>>>>145 byte &0x08 \b progressive
+>>>>>145 byte ^0x08 \b interlaced
+>>>>>145 byte&0x06 2 \b Y'CbCr 4:2:0 video
+>>>>>145 byte&0x06 4 \b Y'CbCr 4:2:2 video
+>>>>>145 byte&0x06 6 \b Y'CbCr 4:4:4 video
+>>76 belong 0x000001B8 \b, v1, progressive Y'CbCr 4:2:0 video
+>>76 belong 0x000001B2 \b, v1, progressive Y'CbCr 4:2:0 video
+>>76 belong 0x000001B5 \b, v2,
+>>>80 byte&0x0F 1 \b HP
+>>>80 byte&0x0F 2 \b Spt
+>>>80 byte&0x0F 3 \b SNR
+>>>80 byte&0x0F 4 \b MP
+>>>80 byte&0x0F 5 \b SP
+>>>81 byte&0xF0 64 \b@HL
+>>>81 byte&0xF0 96 \b@H-14
+>>>81 byte&0xF0 128 \b@ML
+>>>81 byte&0xF0 160 \b@LL
+>>>81 byte &0x08 \b progressive
+>>>81 byte ^0x08 \b interlaced
+>>>81 byte&0x06 2 \b Y'CbCr 4:2:0 video
+>>>81 byte&0x06 4 \b Y'CbCr 4:2:2 video
+>>>81 byte&0x06 6 \b Y'CbCr 4:4:4 video
+>>4 belong&0xFFFFFF00 0x78043800 \b, HD-TV 1920P
+>>>7 byte&0xF0 0x10 \b, 16:9
+>>4 belong&0xFFFFFF00 0x50002D00 \b, SD-TV 1280I
+>>>7 byte&0xF0 0x10 \b, 16:9
+>>4 belong&0xFFFFFF00 0x30024000 \b, PAL Capture
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 beshort&0xFFF0 0x2C00 \b, 4CIF
+>>>5 beshort&0x0FFF 0x01E0 \b NTSC
+>>>5 beshort&0x0FFF 0x0240 \b PAL
+>>>7 byte&0xF0 0x20 \b, 4:3
+>>>7 byte&0xF0 0x30 \b, 16:9
+>>>7 byte&0xF0 0x40 \b, 11:5
+>>>7 byte&0xF0 0x80 \b, PAL 4:3
+>>>7 byte&0xF0 0xC0 \b, NTSC 4:3
+>>4 belong&0xFFFFFF00 0x2801E000 \b, LD-TV 640P
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 belong&0xFFFFFF00 0x1400F000 \b, 320x240
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 belong&0xFFFFFF00 0x0F00A000 \b, 240x160
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 belong&0xFFFFFF00 0x0A007800 \b, 160x120
+>>>7 byte&0xF0 0x10 \b, 4:3
+>>4 beshort&0xFFF0 0x1600 \b, CIF
+>>>5 beshort&0x0FFF 0x00F0 \b NTSC
+>>>5 beshort&0x0FFF 0x0120 \b PAL
+>>>7 byte&0xF0 0x20 \b, 4:3
+>>>7 byte&0xF0 0x30 \b, 16:9
+>>>7 byte&0xF0 0x40 \b, 11:5
+>>>7 byte&0xF0 0x80 \b, PAL 4:3
+>>>7 byte&0xF0 0xC0 \b, NTSC 4:3
+>>>5 beshort&0x0FFF 0x0240 \b PAL 625
+>>>>7 byte&0xF0 0x20 \b, 4:3
+>>>>7 byte&0xF0 0x30 \b, 16:9
+>>>>7 byte&0xF0 0x40 \b, 11:5
+>>4 beshort&0xFFF0 0x2D00 \b, CCIR/ITU
+>>>5 beshort&0x0FFF 0x01E0 \b NTSC 525
+>>>5 beshort&0x0FFF 0x0240 \b PAL 625
+>>>7 byte&0xF0 0x20 \b, 4:3
+>>>7 byte&0xF0 0x30 \b, 16:9
+>>>7 byte&0xF0 0x40 \b, 11:5
+>>4 beshort&0xFFF0 0x1E00 \b, SVCD
+>>>5 beshort&0x0FFF 0x01E0 \b NTSC 525
+>>>5 beshort&0x0FFF 0x0240 \b PAL 625
+>>>7 byte&0xF0 0x20 \b, 4:3
+>>>7 byte&0xF0 0x30 \b, 16:9
+>>>7 byte&0xF0 0x40 \b, 11:5
+>>7 byte&0x0F 1 \b, 23.976 fps
+>>7 byte&0x0F 2 \b, 24 fps
+>>7 byte&0x0F 3 \b, 25 fps
+>>7 byte&0x0F 4 \b, 29.97 fps
+>>7 byte&0x0F 5 \b, 30 fps
+>>7 byte&0x0F 6 \b, 50 fps
+>>7 byte&0x0F 7 \b, 59.94 fps
+>>7 byte&0x0F 8 \b, 60 fps
+>>11 byte &0x04 \b, Constrained
+
+# MPEG ADTS Audio (*.mpx/mxa/aac)
+# from dreesen@math.fu-berlin.de
+# modified to fully support MPEG ADTS
+
+# MP3, M1A
+# modified by Joerg Jenderek
+# GRR the original test are too common for many DOS files
+# so don't accept as MP3 until we've tested the rate
+# But also beat GEMDOS fonts
+0 beshort&0xFFFE 0xFFFA
+# rates
+>2 byte&0xF0 !0
+>>2 byte&0xF0 !0xF0 MPEG ADTS, layer III, v1
+!:strength +20
+!:mime audio/mpeg
+>2 byte&0xF0 0x10 \b, 32 kbps
+>2 byte&0xF0 0x20 \b, 40 kbps
+>2 byte&0xF0 0x30 \b, 48 kbps
+>2 byte&0xF0 0x40 \b, 56 kbps
+>2 byte&0xF0 0x50 \b, 64 kbps
+>2 byte&0xF0 0x60 \b, 80 kbps
+>2 byte&0xF0 0x70 \b, 96 kbps
+>2 byte&0xF0 0x80 \b, 112 kbps
+>2 byte&0xF0 0x90 \b, 128 kbps
+>2 byte&0xF0 0xA0 \b, 160 kbps
+>2 byte&0xF0 0xB0 \b, 192 kbps
+>2 byte&0xF0 0xC0 \b, 224 kbps
+>2 byte&0xF0 0xD0 \b, 256 kbps
+>2 byte&0xF0 0xE0 \b, 320 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 44.1 kHz
+>2 byte&0x0C 0x04 \b, 48 kHz
+>2 byte&0x0C 0x08 \b, 32 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MP2, M1A
+0 beshort&0xFFFE 0xFFFC MPEG ADTS, layer II, v1
+!:mime audio/mpeg
+# rates
+>2 byte&0xF0 0x10 \b, 32 kbps
+>2 byte&0xF0 0x20 \b, 48 kbps
+>2 byte&0xF0 0x30 \b, 56 kbps
+>2 byte&0xF0 0x40 \b, 64 kbps
+>2 byte&0xF0 0x50 \b, 80 kbps
+>2 byte&0xF0 0x60 \b, 96 kbps
+>2 byte&0xF0 0x70 \b, 112 kbps
+>2 byte&0xF0 0x80 \b, 128 kbps
+>2 byte&0xF0 0x90 \b, 160 kbps
+>2 byte&0xF0 0xA0 \b, 192 kbps
+>2 byte&0xF0 0xB0 \b, 224 kbps
+>2 byte&0xF0 0xC0 \b, 256 kbps
+>2 byte&0xF0 0xD0 \b, 320 kbps
+>2 byte&0xF0 0xE0 \b, 384 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 44.1 kHz
+>2 byte&0x0C 0x04 \b, 48 kHz
+>2 byte&0x0C 0x08 \b, 32 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MPA, M1A
+# updated by Joerg Jenderek
+# GRR the original test are too common for many DOS files, so test 32 <= kbits <= 448
+# GRR this test is still too general as it catches a BOM of UTF-16 files (0xFFFE)
+# FIXME: Almost all little endian UTF-16 text with BOM are clobbered by these entries
+#0 beshort&0xFFFE 0xFFFE
+#>2 ubyte&0xF0 >0x0F
+#>>2 ubyte&0xF0 <0xE1 MPEG ADTS, layer I, v1
+## rate
+#>>>2 byte&0xF0 0x10 \b, 32 kbps
+#>>>2 byte&0xF0 0x20 \b, 64 kbps
+#>>>2 byte&0xF0 0x30 \b, 96 kbps
+#>>>2 byte&0xF0 0x40 \b, 128 kbps
+#>>>2 byte&0xF0 0x50 \b, 160 kbps
+#>>>2 byte&0xF0 0x60 \b, 192 kbps
+#>>>2 byte&0xF0 0x70 \b, 224 kbps
+#>>>2 byte&0xF0 0x80 \b, 256 kbps
+#>>>2 byte&0xF0 0x90 \b, 288 kbps
+#>>>2 byte&0xF0 0xA0 \b, 320 kbps
+#>>>2 byte&0xF0 0xB0 \b, 352 kbps
+#>>>2 byte&0xF0 0xC0 \b, 384 kbps
+#>>>2 byte&0xF0 0xD0 \b, 416 kbps
+#>>>2 byte&0xF0 0xE0 \b, 448 kbps
+## timing
+#>>>2 byte&0x0C 0x00 \b, 44.1 kHz
+#>>>2 byte&0x0C 0x04 \b, 48 kHz
+#>>>2 byte&0x0C 0x08 \b, 32 kHz
+## channels/options
+#>>>3 byte&0xC0 0x00 \b, Stereo
+#>>>3 byte&0xC0 0x40 \b, JntStereo
+#>>>3 byte&0xC0 0x80 \b, 2x Monaural
+#>>>3 byte&0xC0 0xC0 \b, Monaural
+##>1 byte ^0x01 \b, Data Verify
+##>2 byte &0x02 \b, Packet Pad
+##>2 byte &0x01 \b, Custom Flag
+##>3 byte &0x08 \b, Copyrighted
+##>3 byte &0x04 \b, Original Source
+##>3 byte&0x03 1 \b, NR: 50/15 ms
+##>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MP3, M2A
+0 beshort&0xFFFE 0xFFF2 MPEG ADTS, layer III, v2
+!:mime audio/mpeg
+# rate
+>2 byte&0xF0 0x10 \b, 8 kbps
+>2 byte&0xF0 0x20 \b, 16 kbps
+>2 byte&0xF0 0x30 \b, 24 kbps
+>2 byte&0xF0 0x40 \b, 32 kbps
+>2 byte&0xF0 0x50 \b, 40 kbps
+>2 byte&0xF0 0x60 \b, 48 kbps
+>2 byte&0xF0 0x70 \b, 56 kbps
+>2 byte&0xF0 0x80 \b, 64 kbps
+>2 byte&0xF0 0x90 \b, 80 kbps
+>2 byte&0xF0 0xA0 \b, 96 kbps
+>2 byte&0xF0 0xB0 \b, 112 kbps
+>2 byte&0xF0 0xC0 \b, 128 kbps
+>2 byte&0xF0 0xD0 \b, 144 kbps
+>2 byte&0xF0 0xE0 \b, 160 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 22.05 kHz
+>2 byte&0x0C 0x04 \b, 24 kHz
+>2 byte&0x0C 0x08 \b, 16 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MP2, M2A
+0 beshort&0xFFFE 0xFFF4 MPEG ADTS, layer II, v2
+!:mime audio/mpeg
+# rate
+>2 byte&0xF0 0x10 \b, 8 kbps
+>2 byte&0xF0 0x20 \b, 16 kbps
+>2 byte&0xF0 0x30 \b, 24 kbps
+>2 byte&0xF0 0x40 \b, 32 kbps
+>2 byte&0xF0 0x50 \b, 40 kbps
+>2 byte&0xF0 0x60 \b, 48 kbps
+>2 byte&0xF0 0x70 \b, 56 kbps
+>2 byte&0xF0 0x80 \b, 64 kbps
+>2 byte&0xF0 0x90 \b, 80 kbps
+>2 byte&0xF0 0xA0 \b, 96 kbps
+>2 byte&0xF0 0xB0 \b, 112 kbps
+>2 byte&0xF0 0xC0 \b, 128 kbps
+>2 byte&0xF0 0xD0 \b, 144 kbps
+>2 byte&0xF0 0xE0 \b, 160 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 22.05 kHz
+>2 byte&0x0C 0x04 \b, 24 kHz
+>2 byte&0x0C 0x08 \b, 16 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MPA, M2A
+0 beshort&0xFFFE 0xFFF6 MPEG ADTS, layer I, v2
+!:mime audio/mpeg
+# rate
+>2 byte&0xF0 0x10 \b, 32 kbps
+>2 byte&0xF0 0x20 \b, 48 kbps
+>2 byte&0xF0 0x30 \b, 56 kbps
+>2 byte&0xF0 0x40 \b, 64 kbps
+>2 byte&0xF0 0x50 \b, 80 kbps
+>2 byte&0xF0 0x60 \b, 96 kbps
+>2 byte&0xF0 0x70 \b, 112 kbps
+>2 byte&0xF0 0x80 \b, 128 kbps
+>2 byte&0xF0 0x90 \b, 144 kbps
+>2 byte&0xF0 0xA0 \b, 160 kbps
+>2 byte&0xF0 0xB0 \b, 176 kbps
+>2 byte&0xF0 0xC0 \b, 192 kbps
+>2 byte&0xF0 0xD0 \b, 224 kbps
+>2 byte&0xF0 0xE0 \b, 256 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 22.05 kHz
+>2 byte&0x0C 0x04 \b, 24 kHz
+>2 byte&0x0C 0x08 \b, 16 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# MP3, M25A
+0 beshort&0xFFFE 0xFFE2 MPEG ADTS, layer III, v2.5
+!:mime audio/mpeg
+# rate
+>2 byte&0xF0 0x10 \b, 8 kbps
+>2 byte&0xF0 0x20 \b, 16 kbps
+>2 byte&0xF0 0x30 \b, 24 kbps
+>2 byte&0xF0 0x40 \b, 32 kbps
+>2 byte&0xF0 0x50 \b, 40 kbps
+>2 byte&0xF0 0x60 \b, 48 kbps
+>2 byte&0xF0 0x70 \b, 56 kbps
+>2 byte&0xF0 0x80 \b, 64 kbps
+>2 byte&0xF0 0x90 \b, 80 kbps
+>2 byte&0xF0 0xA0 \b, 96 kbps
+>2 byte&0xF0 0xB0 \b, 112 kbps
+>2 byte&0xF0 0xC0 \b, 128 kbps
+>2 byte&0xF0 0xD0 \b, 144 kbps
+>2 byte&0xF0 0xE0 \b, 160 kbps
+# timing
+>2 byte&0x0C 0x00 \b, 11.025 kHz
+>2 byte&0x0C 0x04 \b, 12 kHz
+>2 byte&0x0C 0x08 \b, 8 kHz
+# channels/options
+>3 byte&0xC0 0x00 \b, Stereo
+>3 byte&0xC0 0x40 \b, JntStereo
+>3 byte&0xC0 0x80 \b, 2x Monaural
+>3 byte&0xC0 0xC0 \b, Monaural
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Packet Pad
+#>2 byte &0x01 \b, Custom Flag
+#>3 byte &0x08 \b, Copyrighted
+#>3 byte &0x04 \b, Original Source
+#>3 byte&0x03 1 \b, NR: 50/15 ms
+#>3 byte&0x03 3 \b, NR: CCIT J.17
+
+# AAC (aka MPEG-2 NBC audio) and MPEG-4 audio
+
+# Stored AAC streams (instead of the MP4 format)
+0 string ADIF MPEG ADIF, AAC
+!:mime audio/x-hx-aac-adif
+>4 byte &0x80
+>>13 byte &0x10 \b, VBR
+>>13 byte ^0x10 \b, CBR
+>>16 byte&0x1E 0x02 \b, single stream
+>>16 byte&0x1E 0x04 \b, 2 streams
+>>16 byte&0x1E 0x06 \b, 3 streams
+>>16 byte &0x08 \b, 4 or more streams
+>>16 byte &0x10 \b, 8 or more streams
+>>4 byte &0x80 \b, Copyrighted
+>>13 byte &0x40 \b, Original Source
+>>13 byte &0x20 \b, Home Flag
+>4 byte ^0x80
+>>4 byte &0x10 \b, VBR
+>>4 byte ^0x10 \b, CBR
+>>7 byte&0x1E 0x02 \b, single stream
+>>7 byte&0x1E 0x04 \b, 2 streams
+>>7 byte&0x1E 0x06 \b, 3 streams
+>>7 byte &0x08 \b, 4 or more streams
+>>7 byte &0x10 \b, 8 or more streams
+>>4 byte &0x40 \b, Original Stream(s)
+>>4 byte &0x20 \b, Home Source
+
+# Live or stored single AAC stream (used with MPEG-2 systems)
+0 beshort&0xFFF6 0xFFF0 MPEG ADTS, AAC
+!:mime audio/x-hx-aac-adts
+>1 byte &0x08 \b, v2
+>1 byte ^0x08 \b, v4
+# profile
+>>2 byte &0xC0 \b LTP
+>2 byte&0xc0 0x00 \b Main
+>2 byte&0xc0 0x40 \b LC
+>2 byte&0xc0 0x80 \b SSR
+# timing
+>2 byte&0x3c 0x00 \b, 96 kHz
+>2 byte&0x3c 0x04 \b, 88.2 kHz
+>2 byte&0x3c 0x08 \b, 64 kHz
+>2 byte&0x3c 0x0c \b, 48 kHz
+>2 byte&0x3c 0x10 \b, 44.1 kHz
+>2 byte&0x3c 0x14 \b, 32 kHz
+>2 byte&0x3c 0x18 \b, 24 kHz
+>2 byte&0x3c 0x1c \b, 22.05 kHz
+>2 byte&0x3c 0x20 \b, 16 kHz
+>2 byte&0x3c 0x24 \b, 12 kHz
+>2 byte&0x3c 0x28 \b, 11.025 kHz
+>2 byte&0x3c 0x2c \b, 8 kHz
+# channels
+>2 beshort&0x01c0 0x0040 \b, monaural
+>2 beshort&0x01c0 0x0080 \b, stereo
+>2 beshort&0x01c0 0x00c0 \b, stereo + center
+>2 beshort&0x01c0 0x0100 \b, stereo+center+LFE
+>2 beshort&0x01c0 0x0140 \b, surround
+>2 beshort&0x01c0 0x0180 \b, surround + LFE
+>2 beshort &0x01C0 \b, surround + side
+#>1 byte ^0x01 \b, Data Verify
+#>2 byte &0x02 \b, Custom Flag
+#>3 byte &0x20 \b, Original Stream
+#>3 byte &0x10 \b, Home Source
+#>3 byte &0x08 \b, Copyrighted
+
+# Live MPEG-4 audio streams (instead of RTP FlexMux)
+0 beshort&0xFFE0 0x56E0 MPEG-4 LOAS
+!:mime audio/x-mp4a-latm
+#>1 beshort&0x1FFF x \b, %hu byte packet
+>3 byte&0xE0 0x40
+>>4 byte&0x3C 0x04 \b, single stream
+>>4 byte&0x3C 0x08 \b, 2 streams
+>>4 byte&0x3C 0x0C \b, 3 streams
+>>4 byte &0x08 \b, 4 or more streams
+>>4 byte &0x20 \b, 8 or more streams
+>3 byte&0xC0 0
+>>4 byte&0x78 0x08 \b, single stream
+>>4 byte&0x78 0x10 \b, 2 streams
+>>4 byte&0x78 0x18 \b, 3 streams
+>>4 byte &0x20 \b, 4 or more streams
+>>4 byte &0x40 \b, 8 or more streams
+# This magic isn't strong enough (matches plausible ISO-8859-1 text)
+#0 beshort 0x4DE1 MPEG-4 LO-EP audio stream
+#!:mime audio/x-mp4a-latm
+
+# Summary: FLI animation format
+# Created by: Daniel Quinlan <quinlan@yggdrasil.com>
+# Modified by (1): Abel Cheung <abelcheung@gmail.com> (avoid over-generic detection)
+4 leshort 0xAF11
+# standard FLI always has 320x200 resolution and 8 bit color
+>8 leshort 320
+>>10 leshort 200
+>>>12 leshort 8 FLI animation, 320x200x8
+!:mime video/x-fli
+>>>>6 leshort x \b, %d frames
+# frame speed is multiple of 1/70s
+>>>>16 leshort x \b, %d/70s per frame
+
+# Summary: FLC animation format
+# Created by: Daniel Quinlan <quinlan@yggdrasil.com>
+# Modified by (1): Abel Cheung <abelcheung@gmail.com> (avoid over-generic detection)
+4 leshort 0xAF12
+# standard FLC always use 8 bit color
+>12 leshort 8 FLC animation
+!:mime video/x-flc
+>>8 leshort x \b, %d
+>>10 leshort x \bx%dx8
+>>6 uleshort x \b, %d frames
+>>16 uleshort x \b, %dms per frame
+
+# DL animation format
+# XXX - collision with most `mips' magic
+#
+# I couldn't find a real magic number for these, however, this
+# -appears- to work. Note that it might catch other files, too, so be
+# careful!
+#
+# Note that title and author appear in the two 20-byte chunks
+# at decimal offsets 2 and 22, respectively, but they are XOR'ed with
+# 255 (hex FF)! The DL format is really bad.
+#
+#0 byte 1 DL version 1, medium format (160x100, 4 images/screen)
+#!:mime video/x-unknown
+#>42 byte x - %d screens,
+#>43 byte x %d commands
+#0 byte 2 DL version 2
+#!:mime video/x-unknown
+#>1 byte 1 - large format (320x200,1 image/screen),
+#>1 byte 2 - medium format (160x100,4 images/screen),
+#>1 byte >2 - unknown format,
+#>42 byte x %d screens,
+#>43 byte x %d commands
+# Based on empirical evidence, DL version 3 have several nulls following the
+# \003. Most of them start with non-null values at hex offset 0x34 or so.
+#0 string \3\0\0\0\0\0\0\0\0\0\0\0 DL version 3
+
+# iso 13818 transport stream
+#
+# from Oskar Schirmer <schirmer@scara.com> Feb 3, 2001 (ISO 13818.1)
+# syncbyte 8 bit 0x47
+# error_ind 1 bit -
+# payload_start 1 bit 1
+# priority 1 bit -
+# PID 13 bit 0x0000
+# scrambling 2 bit -
+# adaptfld_ctrl 2 bit 1 or 3
+# conti_count 4 bit -
+0 belong&0xFF5FFF10 0x47400010
+>188 byte 0x47 MPEG transport stream data
+!:mime video/MP2T
+!:ext ts
+
+# Blu-ray disc Audio-Video MPEG-2 transport stream
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://en.wikipedia.org/wiki/MPEG_transport_stream
+# Note: similar to ISO 13818.1 but with 4 extra bytes per packets
+4 belong&0xFF5FFF10 =0x47400010
+>196 byte =0x47 BDAV MPEG-2 Transport Stream (M2TS)
+!:mime video/MP2T
+!:ext m2ts/mts
+
+# DIF digital video file format <mpruett@sgi.com>
+0 belong&0xffffff00 0x1f070000 DIF
+!:mime video/x-dv
+>4 byte &0x01 (DVCPRO) movie file
+>4 byte ^0x01 (DV) movie file
+>3 byte &0x80 (PAL)
+>3 byte ^0x80 (NTSC)
+
+# MNG Video Format, <URL:http://www.libpng.org/pub/mng/spec/>
+0 string \x8aMNG MNG video data,
+!:mime video/x-mng
+>4 belong !0x0d0a1a0a CORRUPTED,
+>4 belong 0x0d0a1a0a
+>>16 belong x %d x
+>>20 belong x %d
+
+# JNG Video Format, <URL:http://www.libpng.org/pub/mng/spec/>
+0 string \x8bJNG JNG video data,
+!:mime video/x-jng
+>4 belong !0x0d0a1a0a CORRUPTED,
+>4 belong 0x0d0a1a0a
+>>16 belong x %d x
+>>20 belong x %d
+
+# Vivo video (Wolfram Kleff)
+3 string \x0D\x0AVersion:Vivo Vivo video data
+
+# ABC (alembic.io 3d models)
+0 string 0gawa ABC 3d model
+
+#---------------------------------------------------------------------------
+# HVQM4: compressed movie format designed by Hudson for Nintendo GameCube
+# From Mark Sheppard <msheppard@climax.co.uk>, 2002-10-03
+#
+0 string HVQM4 %s
+>6 string >\0 v%s
+>0 byte x GameCube movie,
+>0x34 ubeshort x %d x
+>0x36 ubeshort x %d,
+>0x26 ubeshort x %dus,
+>0x42 ubeshort 0 no audio
+>0x42 ubeshort >0 %dHz audio
+
+# From: Stefan A. Haubenthal <polluks@sdf.lonestar.org>
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/VOB
+0 string DVDVIDEO-VTS Video title set,
+!:mime video/x-ifo
+!:ext ifo/bup
+>0x21 byte x v%x
+0 string DVDVIDEO-VMG Video manager,
+!:mime video/x-ifo
+!:ext ifo/bup
+>0x21 byte x v%x
+
+# From: Stefan A. Haubenthal <polluks@sdf.lonestar.org>
+0 string xMovieSetter MovieSetter movie
+0 string xSceneEditor MovieSetter movie
+
+# From: Behan Webster <behanw@websterwood.com>
+# NuppelVideo used by Mythtv (*.nuv)
+# Note: there are two identical stanzas here differing only in the
+# initial string matched. It used to be done with a regex, but we're
+# trying to get rid of those.
+0 string NuppelVideo MythTV NuppelVideo
+>12 string x v%s
+>20 lelong x (%d
+>24 lelong x \bx%d),
+>36 string P \bprogressive,
+>36 string I \binterlaced,
+>40 ledouble x \baspect:%.2f,
+>48 ledouble x \bfps:%.2f
+0 string MythTV MythTV NuppelVideo
+>12 string x v%s
+>20 lelong x (%d
+>24 lelong x \bx%d),
+>36 string P \bprogressive,
+>36 string I \binterlaced,
+>40 ledouble x \baspect:%.2f,
+>48 ledouble x \bfps:%.2f
+
+# MPEG file
+# MPEG sequences
+# FIXME: This section is from the old magic.mime file and needs
+# integrating with the rest
+#0 belong 0x000001BA
+#>4 byte &0x40
+#!:mime video/mp2p
+#>4 byte ^0x40
+#!:mime video/mpeg
+#0 belong 0x000001BB
+#!:mime video/mpeg
+#0 belong 0x000001B0
+#!:mime video/mp4v-es
+#0 belong 0x000001B5
+#!:mime video/mp4v-es
+#0 belong 0x000001B3
+#!:mime video/mpv
+#0 belong&0xFF5FFF10 0x47400010
+#!:mime video/mp2t
+#0 belong 0x00000001
+#>4 byte&0x1F 0x07
+#!:mime video/h264
+
+# Type: Bink Video
+# Extension: .bik
+# URL: https://wiki.multimedia.cx/index.php?title=Bink_Container
+# From: <hoehle@users.sourceforge.net> 2008-07-18
+0 name bik
+#>4 ulelong x size %d
+>20 ulelong x \b, %d
+>24 ulelong x \bx%d
+>8 ulelong x \b, %d frames
+>32 ulelong x at rate %d/
+>28 ulelong >1 \b%d
+>40 ulelong =0 \b, no audio
+>40 ulelong !0 \b, %d audio track
+>>40 ulelong !1 \bs
+# follow properties of the first audio track only
+>>48 uleshort x %dHz
+>>51 byte&0x20 0 mono
+>>51 byte&0x20 !0 stereo
+#>>51 byte&0x10 0 FFT
+#>>51 byte&0x10 !0 DCT
+
+0 string BIK
+>3 regex =[bdfghi] Bink Video rev.%s
+>>0 use bik
+
+0 string KB2
+>3 regex =[adfghi] Bink Video 2 rev.%s
+>>0 use bik
+
+# Type: NUT Container
+# URL: https://wiki.multimedia.cx/index.php?title=NUT
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string nut/multimedia\ container\0 NUT multimedia container
+
+# Type: Nullsoft Video (NSV)
+# URL: https://wiki.multimedia.cx/index.php?title=Nullsoft_Video
+# From: Mike Melanson <mike@multimedia.cx>
+0 string NSVf Nullsoft Video
+
+# Type: REDCode Video
+# URL: https://www.red.com/ ; https://wiki.multimedia.cx/index.php?title=REDCode
+# From: Mike Melanson <mike@multimedia.cx>
+4 string RED1 REDCode Video
+
+# Type: MTV Multimedia File
+# URL: https://wiki.multimedia.cx/index.php?title=MTV
+# From: Mike Melanson <mike@multimedia.cx>
+0 string AMVS MTV Multimedia File
+
+# Type: ARMovie
+# URL: https://wiki.multimedia.cx/index.php?title=ARMovie
+# From: Mike Melanson <mike@multimedia.cx>
+0 string ARMovie\012 ARMovie
+
+# Type: Interplay MVE Movie
+# URL: https://wiki.multimedia.cx/index.php?title=Interplay_MVE
+# From: Mike Melanson <mike@multimedia.cx>
+0 string Interplay\040MVE\040File\032 Interplay MVE Movie
+
+# Type: Windows Television DVR File
+# URL: https://wiki.multimedia.cx/index.php?title=WTV
+# From: Mike Melanson <mike@mutlimedia.cx>
+# This takes the form of a Windows-style GUID
+0 bequad 0xB7D800203749DA11
+>8 bequad 0xA64E0007E95EAD8D Windows Television DVR Media
+
+# Type: Sega FILM/CPK Multimedia
+# URL: https://wiki.multimedia.cx/index.php?title=Sega_FILM
+# From: Mike Melanson <mike@multimedia.cx>
+0 string FILM Sega FILM/CPK Multimedia,
+>32 belong x %d x
+>28 belong x %d
+
+# Type: Nintendo THP Multimedia
+# URL: https://wiki.multimedia.cx/index.php?title=THP
+# From: Mike Melanson <mike@multimedia.cx>
+0 string THP\0 Nintendo THP Multimedia
+
+# Type: BBC Dirac Video
+# URL: https://wiki.multimedia.cx/index.php?title=Dirac
+# From: Mike Melanson <mike@multimedia.cx>
+0 string BBCD BBC Dirac Video
+
+# Type: RAD Game Tools Smacker Multimedia
+# URL: https://wiki.multimedia.cx/index.php?title=Smacker
+# From: Mike Melanson <mike@multimedia.cx>
+0 string SMK RAD Game Tools Smacker Multimedia
+>3 byte x version %c,
+>4 lelong x %d x
+>8 lelong x %d,
+>12 lelong x %d frames
+
+# Material Exchange Format
+# More information:
+# https://en.wikipedia.org/wiki/Material_Exchange_Format
+# http://www.freemxf.org/
+0 string \x06\x0e\x2b\x34\x02\x05\x01\x01\x0d\x01\x02\x01\x01\x02 Material exchange container format
+!:ext mxf
+!:mime application/mxf
+
+# Recognize LucasArts Smush video files (cf.
+# https://wiki.multimedia.cx/index.php/Smush)
+0 string ANIM
+>8 string AHDR LucasArts Smush Animation Format (SAN) video
+0 string SANM
+>8 string SHDR LucasArts Smush v2 (SANM) video
+
+# Type: Scaleform video
+# Extension: .usm
+# URL: https://wiki.multimedia.cx/index.php/USM
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+0 string CRID
+>32 string @UTF Scaleform video
+
+# http://www.jerrysguide.com/tips/demystify-tvs-file-format.html
+0 string TVS\015\012
+>&0 string Version\040 TeamViewer Session File
+>>&0 string x \b, version %s
+
+# SER file format - simple uncompressed video format for astronomical use
+# Initially developed by Lucam Recorder,
+# as of 2021 maintained by Heiko Wilkens, Grischa Hahn
+# Typical extensions: .SER
+# http://www.grischa-hahn.homepage.t-online.de/astro/ser/SER%20Doc%20V3b.pdf
+0 string LUCAM-RECORDER SER video sequence
+!:ext ser
+>18 lelong 0 \b, bayer: mono
+>18 lelong 8 \b, bayer: RGGB
+>18 lelong 9 \b, bayer: GRBG
+>18 lelong 10 \b, bayer: GBRG
+>18 lelong 11 \b, bayer: BGGR
+>18 lelong 16 \b, bayer: CYYM
+>18 lelong 17 \b, bayer: YCMY
+>18 lelong 18 \b, bayer: YMCY
+>18 lelong 19 \b, bayer: MYYC
+>18 lelong 100 \b, bayer: RGB
+>18 lelong 101 \b, bayer: BGR
+>22 lelong 0 \b, big-endian
+>22 lelong 1 \b, little-endian
+>26 lelong x \b, width: %d
+>30 lelong x \b, height: %d
+>34 lelong x \b, %d bit
+>38 lelong x \b, frames: %d
+
+# https://wiki.multimedia.cx/index.php/Duck_IVF
+0 string DKIF Duck IVF video file
+!:mime video/x-ivf
+>4 leshort >0 \b, version %d
+>8 string x \b, codec %s
+>12 leshort x \b, %d
+>14 leshort x \bx%d
+>24 lelong >0 \b, %d frames
diff --git a/contrib/libs/libmagic/magic/Magdir/aout b/contrib/libs/libmagic/magic/Magdir/aout
new file mode 100644
index 0000000000..69b6ec60d8
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/aout
@@ -0,0 +1,46 @@
+
+#------------------------------------------------------------------------------
+# $File: aout,v 1.1 2013/01/09 22:37:23 christos Exp $
+# aout: file(1) magic for a.out executable/object/etc entries that
+# handle executables on multiple platforms.
+#
+
+#
+# Little-endian 32-bit-int a.out, merged from bsdi (for BSD/OS, from
+# BSDI), netbsd, and vax (for UNIX/32V and BSD)
+#
+# XXX - is there anything we can look at to distinguish BSD/OS 386 from
+# NetBSD 386 from various VAX binaries? The BSD/OS shared library flag
+# works only for binaries using shared libraries. Grabbing the entry
+# point from the a.out header, using it to find the first code executed
+# in the program, and looking at that might help.
+#
+0 lelong 0407 a.out little-endian 32-bit executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses BSD/OS shared libs)
+
+0 lelong 0410 a.out little-endian 32-bit pure executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses BSD/OS shared libs)
+
+0 lelong 0413 a.out little-endian 32-bit demand paged pure executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses BSD/OS shared libs)
+
+#
+# Big-endian 32-bit-int a.out, merged from sun (for old 68010 SunOS a.out),
+# mips (for old 68020(!) SGI a.out), and netbsd (for old big-endian a.out).
+#
+# XXX - is there anything we can look at to distinguish old SunOS 68010
+# from old 68020 IRIX from old NetBSD? Again, I guess we could look at
+# the first instruction or instructions in the program.
+#
+0 belong 0407 a.out big-endian 32-bit executable
+>16 belong >0 not stripped
+
+0 belong 0410 a.out big-endian 32-bit pure executable
+>16 belong >0 not stripped
+
+0 belong 0413 a.out big-endian 32-bit demand paged executable
+>16 belong >0 not stripped
+
diff --git a/contrib/libs/libmagic/magic/Magdir/apache b/contrib/libs/libmagic/magic/Magdir/apache
new file mode 100755
index 0000000000..d896b50551
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/apache
@@ -0,0 +1,28 @@
+
+#------------------------------------------------------------------------------
+# $File: apache,v 1.1 2017/04/11 14:52:15 christos Exp $
+# apache: file(1) magic for Apache Big Data formats
+
+# Avro files
+0 string Obj Apache Avro
+>3 byte x version %d
+
+# ORC files
+# Important information is in file footer, which we can't index to :(
+0 string ORC Apache ORC
+
+# Parquet files
+0 string PAR1 Apache Parquet
+
+# Hive RC files
+0 string RCF Apache Hive RC file
+>3 byte x version %d
+
+# Sequence files (and the careless first version of RC file)
+
+0 string SEQ
+>3 byte <6 Apache Hadoop Sequence file version %d
+>3 byte >6 Apache Hadoop Sequence file version %d
+>3 byte =6
+>>5 string org.apache.hadoop.hive.ql.io.RCFile$KeyBuffer Apache Hive RC file version 0
+>>3 default x Apache Hadoop Sequence file version 6
diff --git a/contrib/libs/libmagic/magic/Magdir/apl b/contrib/libs/libmagic/magic/Magdir/apl
new file mode 100644
index 0000000000..d717e377dc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/apl
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# $File: apl,v 1.6 2009/09/19 16:28:07 christos Exp $
+# apl: file(1) magic for APL (see also "pdp" and "vax" for other APL
+# workspaces)
+#
+0 long 0100554 APL workspace (Ken's original?)
diff --git a/contrib/libs/libmagic/magic/Magdir/apple b/contrib/libs/libmagic/magic/Magdir/apple
new file mode 100644
index 0000000000..547b0ac20a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/apple
@@ -0,0 +1,773 @@
+
+#------------------------------------------------------------------------------
+# $File: apple,v 1.48 2023/05/01 14:20:21 christos Exp $
+# apple: file(1) magic for Apple file formats
+#
+0 search/1/t FiLeStArTfIlEsTaRt binscii (apple ][) text
+0 string \x0aGL Binary II (apple ][) data
+0 string \x76\xff Squeezed (apple ][) data
+0 string NuFile NuFile archive (apple ][) data
+0 string N\xf5F\xe9l\xe5 NuFile archive (apple ][) data
+0 belong 0x00051600 AppleSingle encoded Macintosh file
+0 belong 0x00051607 AppleDouble encoded Macintosh file
+
+# Type: Apple Emulator A2R format
+# From: Greg Wildman <greg@apple2.org.za>
+# Ref: https://applesaucefdc.com/a2r2-reference/
+# Ref: https://applesaucefdc.com/a2r/
+0 string A2R
+>3 string \x31\xFF\x0A\x0D\x0A Applesauce A2R 1.x Disk Image
+>3 string \x32\xFF\x0A\x0D\x0A Applesauce A2R 2.x Disk Image
+>3 string \x33\xFF\x0A\x0D\x0A Applesauce A2R 3.x Disk Image
+>8 string INFO
+>>49 byte 01 \b, 5.25″ SS 40trk
+>>49 byte 02 \b, 3.5″ DS 80trk
+>>49 byte 03 \b, 5.25″ DS 80trk
+>>49 byte 04 \b, 5.25″ DS 40trk
+>>49 byte 05 \b, 3.5″ DS 80trk
+>>49 byte 06 \b, 8″ DS
+>>50 byte 01 \b, write protected
+>>51 byte 01 \b, cross track synchronized
+>>17 string/T x \b, %.32s
+
+# Type: Apple Emulator WOZ format
+# From: Greg Wildman <greg@apple2.org.za>
+# Ref: https://applesaucefdc.com/woz/reference/
+# Ref: https://applesaucefdc.com/woz/reference2/
+0 string WOZ
+>3 string \x31\xFF\x0A\x0D\x0A Apple ][ WOZ 1.0 Disk Image
+>3 string \x32\xFF\x0A\x0D\x0A Apple ][ WOZ 2.0 Disk Image
+>12 string INFO
+>>21 byte 01 \b, 5.25 inch
+>>21 byte 02 \b, 3.5 inch
+>>22 byte 01 \b, write protected
+>>23 byte 01 \b, cross track synchronized
+>>25 string/T x \b, %.32s
+
+# Type: Apple Macintosh Emulator MOOF format
+# From: Greg Wildman <greg@apple2.org.za>
+# Ref: https://applesaucefdc.com/moof-reference/
+0 string MOOF
+>4 string \xFF\x0A\x0D\x0A Apple Macintosh MOOF Disk Image
+>12 string INFO
+>>21 byte 01 \b, SSDD GCR (400K)
+>>21 byte 02 \b, DSDD GCR (800K)
+>>21 byte 03 \b, DSHD MFM (1.44M)
+>>22 byte 01 \b, write protected
+>>23 byte 01 \b, cross track synchronized
+>>25 string/T x \b, %.32s
+
+# Type: Apple Emulator disk images
+# From: Greg Wildman <greg@apple2.org.za>
+# ProDOS boot loader?
+0 string \x01\x38\xB0\x03\x4C Apple ProDOS Image
+# Detect Volume Directory block ($02)
+>0x400 string \x00\x00\x03\x00
+>>0x404 byte &0xF0
+>>>0x405 string x \b, Volume /%s
+>>>0x429 uleshort x \b, %u Blocks
+# ProDOS ordered ?
+>0xb00 string \x00\x00\x03\x00
+>>0xb04 byte &0xF0
+>>>0xb05 string x \b, Volume /%s
+>>>0xb29 uleshort x \b, %u Blocks
+#
+# Proboot HD
+0 string \x01\x8A\x48\xD8\x2C\x82\xC0\x8D\x0E\xC0\x8D\x0C Apple ProDOS ProBoot Image
+>0x400 string \x00\x00\x03\x00
+>>0x404 byte &0xF0
+>>>0x405 string x \b, Volume /%s
+>>>0x429 uleshort x \b, %u Blocks
+>0xb00 string \x00\x00\x03\x00
+>>0xb04 byte &0xF0
+>>>0xb05 string x \b, Volume /%s
+>>>0xb29 uleshort x \b, %u Blocks
+0 string \x01\xA8\x8A\x20\x7B\xF8\x29\x07\x09\xC0\x99\x30 Apple ProDOS ProBoot Image
+>0x400 string \x00\x00\x03\x00
+>>0x404 byte &0xF0
+>>>0x405 string x \b, Volume /%s
+>>>0x429 uleshort x \b, %u Blocks
+>0xb00 string \x00\x00\x03\x00
+>>0xb04 byte &0xF0
+>>>0xb05 string x \b, Volume /%s
+>>>0xb29 uleshort x \b, %u Blocks
+0 string \x01\x4A\xD0\x34\xE6\x3D\x8A\x20\x7B\xF8\x09\xC0 Apple ProDOS ProBoot Image
+>0x400 string \x00\x00\x03\x00
+>>0x404 byte &0xF0
+>>>0x405 string x \b, Volume /%s
+>>>0x429 uleshort x \b, %u Blocks
+>0xb00 string \x00\x00\x03\x00
+>>0xb04 byte &0xF0
+>>>0xb05 string x \b, Volume /%s
+>>>0xb29 uleshort x \b, %u Blocks
+#
+# ProDOS formatted
+0 string \x01\xBD\x88\xC0\x20\x2F\xFB\x20\x58\xFC\x20\x40 Apple ProDOS Unbootable Image
+>0x400 string \x00\x00\x03\x00
+>>0x404 byte &0xF0
+>>>0x405 string x \b, Volume /%s
+>>>0x429 uleshort x \b, %u Blocks
+>0xb00 string \x00\x00\x03\x00
+>>0xb04 byte &0xF0
+>>>0xb05 string x \b, Volume /%s
+>>>0xb29 uleshort x \b, %u Blocks
+0 string \x01\x38\xB0\x03\x4C\x1C\x09\x78\x86\x43\xC9\x03 Apple ProDOS Unbootable Image
+>0x400 string \x00\x00\x03\x00
+>>0x404 byte &0xF0
+>>>0x405 string x \b, Volume /%s
+>>>0x429 uleshort x \b, %u Blocks
+>0xb00 string \x00\x00\x03\x00
+>>0xb04 byte &0xF0
+>>>0xb05 string x \b, Volume /%s
+>>>0xb29 uleshort x \b, %u Blocks
+#
+# DOS3 boot loader
+0 string \x01\xA5\x27\xC9\x09\xD0
+>0x11001 byte 0x11
+>>0x11003 ubyte x Apple DOS 3.%u Image
+>>0x11006 ubyte x \b, Volume #%03u
+>>0x11034 ubyte x \b, %u Tracks
+>>0x11035 ubyte x \b, %u Sectors
+>>0x11036 uleshort x \b, %u bytes per sector
+#
+# DOS3 uninitialized disk
+0 string \x01\xA6\x2B\xBD\x88\xC0\x8A\x4A\x4A
+>0x11001 byte 0x11
+>>0x11003 ubyte x Apple DOS 3.%u Unbootable Image
+>>>0x11006 ubyte x \b, Volume #%03u
+>>>0x11034 ubyte x \b, %u Tracks
+>>>0x11035 ubyte x \b, %u Sectors
+>>>0x11036 uleshort x \b, %u bytes per sector
+#
+# Pascal boot loader?
+0 string \x01\xE0\x60\xF0\x03\x4C\xE3\x08\xAD
+>0xd6 pstring SYSTEM.APPLE
+>>0xb00 leshort 0x0000
+>>>0xb04 leshort 0x0000 Apple Pascal Image
+>>>>0xb06 pstring x \b, Volume %s:
+>>>>0xb0e leshort x \b, %u Blocks
+>>>>0xb10 leshort x \b, %u Files
+#
+# Diversi Dos boot loader?
+0 string \x01\xA8\xAD\x81\xC0\xEE\x09\x08\xAD
+>0x11001 string \x11\x0F\x03 Apple Diversi Dos Image
+>>0x11006 byte x \b, Volume %u
+>>0x11034 byte x \b, %u Tracks
+>>0x11035 byte x \b, %u Sectors
+>>0x11036 leshort x \b, %u bytes per sector
+
+# Type: Apple Emulator 2IMG format
+# From: Radek Vokal <rvokal@redhat.com>
+# Update: Greg Wildman <greg@apple2.org.za>
+0 string 2IMG Apple ][ 2IMG Disk Image
+>4 clear x
+>4 string XGS! \b, XGS
+>4 string CTKG \b, Catakig
+>4 string ShIm \b, Sheppy's ImageMaker
+>4 string SHEP \b, Sheppy's ImageMaker
+>4 string WOOF \b, Sweet 16
+>4 string B2TR \b, Bernie ][ the Rescue
+>4 string \!nfc \b, ASIMOV2
+>4 string \>BD\< \b, Brutal Deluxe's Cadius
+>4 string CdrP \b, CiderPress
+>4 string Vi][ \b, Virtual ][
+>4 string PRFS \b, ProFUSE
+>4 string FISH \b, FishWings
+>4 string RVLW \b, Revival for Windows
+>4 default x
+>>4 string x \b, Creator tag "%-4.4s"
+>0xc byte 00 \b, DOS 3.3 sector order
+>>0x10 byte 00 \b, Volume 254
+>>0x10 byte&0x7f x \b, Volume %u
+>0xc byte 01 \b, ProDOS sector order
+# Detect Volume Directory block ($02) + 2mg header offset
+>>0x440 string \x00\x00\x03\x00
+>>>0x444 byte &0xF0
+>>>>0x445 string x \b, Volume /%s
+>>>>0x469 uleshort x \b, %u Blocks
+>0xc byte 02 \b, NIB data
+
+# Type: Peter Ferrie QBoot
+# From: Greg Wildman <greg@apple2.org.za>
+# Ref: https://github.com/peterferrie/qboot
+0 string \x01\x4A\xA8\x69\x0F\x85\x27\xC9
+>8 string \x12\xF0\x10\xE6\x3D\x86\xDA\x8A Apple ][ QBoot Image
+
+# Type: Peter Ferrie 0Boot
+# From: Greg Wildman <greg@apple2.org.za>
+# Ref: https://github.com/peterferrie/0boot
+0 string \x01\x4A\xA8\x69\x0F\x85\x27\xC9
+>8 string \x12\xF0\x10\xE6\x3D\x86\xDA\x8A Apple ][ 0Boot Image
+
+# Different proprietary boot sectors
+0 string \x01\x0F\x21\x74\x00\x01\x6B\x00\x02\x30\x81\x5D Apple ][ Disk Image
+0 string \x01\x20\x58\xFC\xA2\x00\x8E\x78\x04\x8E\xF4\x03 Apple ][ Disk Image
+0 string \x01\x20\x58\xFC\xAD\x51\xC0\xAD\x54\xC0\xA6\x2B Apple ][ Disk Image
+0 string \x01\x20\x89\xFE\x20\x93\xFE\xA6\x2B\xBD\x88\xC0 Apple ][ Disk Image
+0 string \x01\x20\x93\xFE\x20\x89\xFE\x4C\x25\x08\x68\x85 Apple ][ Disk Image
+0 string \x01\x20\x93\xFE\x20\x89\xFE\x4C\x2D\x08\x68\x85 Apple ][ Disk Image
+0 string \x01\x38\x90\x2A\xC9\x01\xF0\x33\xA8\xC8\xC0\x10 Apple ][ Disk Image
+0 string \x01\x38\xB0\x03\x4C\x32\xA1\x87\x43\xC9\x03\x08 Apple ][ Disk Image
+0 string \x01\x4C\x04\x08\xA9\x2A\x8D\x02\x08\x86\x2B\xEE Apple ][ Disk Image
+0 string \x01\x4C\x60\x08\x09\xD0\x18\xA5\x2B\x4A\x4A\x4A Apple ][ Disk Image
+0 string \x01\x4C\x92\x08\x01\x08\xA2\x00\xB5\x00\x9D\x00 Apple ][ Disk Image
+0 string \x01\x4C\xB3\x08\x09\xD0\x18\xA5\x2B\x4A\x4A\x4A Apple ][ Disk Image
+0 string \x01\x8D\xFB\x03\x8E\xFC\x03\x8C\xFD\x03\x8A\x29 Apple ][ Disk Image
+0 string \x01\xA2\xFF\x9A\xD8\x20\x20\x08\x20\x34\x08\xAD Apple ][ Disk Image
+0 string \x01\xA5\x27\xBD\x88\xC0\x2C\x10\xC0\xA2\x00\xA9 Apple ][ Disk Image
+0 string \x01\xA5\x2B\xAE\x51\xC0\xEA\xAA\xBD\x88\xC0\x20 Apple ][ Disk Image
+0 string \x01\xA6\x27\xBD\x0B\x08\x48\xBD\x0A\x08\x48\x85 Apple ][ Disk Image
+0 string \x01\xA6\x2B\xBD\x88\xC0\x20\x58\xFC\xA9\x01\x85 Apple ][ Disk Image
+0 string \x01\xA6\x2B\xBD\x88\xC0\x20\x58\xFC\xA9\x25\x85 Apple ][ Disk Image
+0 string \x01\xA8\xC0\x0F\x90\x16\xF0\x12\xA0\xFF\x18\xAD Apple ][ Disk Image
+0 string \x01\xA9\x00\x85\xF0\xA9\x04\x85\xF1\xA0\x00\xA9 Apple ][ Disk Image
+0 string \x01\xA9\x5C\x8D\xF2\x03\xA9\xC6\x8D\xF3\x03\x49 Apple ][ Disk Image
+0 string \x01\xA9\x60\x8D\x01\x08\x20\x2F\xFB\x20\x58\xFC Apple ][ Disk Image
+0 string \x01\xA9\x60\x8D\x01\x08\x20\x49\x08\xA9\x0A\x85 Apple ][ Disk Image
+0 string \x01\xA9\x60\x8D\x01\x08\x2C\x82\xC0\xBD\x88\xC0 Apple ][ Disk Image
+0 string \x01\xA9\x60\x8D\x01\x08\x86\x43\x8A\x4A\x4A\x4A Apple ][ Disk Image
+0 string \x01\xA9\x60\x8D\x01\x08\xA2\x00\x86\xFF\xB5\x00 Apple ][ Disk Image
+0 string \x01\xA9\x60\x8D\x01\x08\xA2\x00\xB5\x00\x9D\x00 Apple ][ Disk Image
+0 string \x01\xA9\x60\x8D\x01\x08\xA9\xB2\x8D\xF2\x03\xA9 Apple ][ Disk Image
+0 string \x01\xA9\x60\x8D\x01\x08\xA9\xFF\x8D\xF3\x03\x8D Apple ][ Disk Image
+0 string \x01\xAC\x00\x08\xF0\x19\xB9\x30\x08\x85\x3D\xCE Apple ][ Disk Image
+0 string \x01\xAC\x23\x08\x30\x2E\xB9\x24\x08\x85\x3D\xCE Apple ][ Disk Image
+0 string \x01\xAD\x00\x08\xC9\x09\xB0\x20\x69\x02\x8D\x00 Apple ][ Disk Image
+0 string \x01\xB0\x00\xA9\x3C\x8D\x02\x08\x86\x2B\x8A\x4A Apple ][ Disk Image
+0 string \x01\xB0\x00\xA9\x3C\x8D\x02\x08\xA9\xF5\x8D\xF2 Apple ][ Disk Image
+0 string \x01\xB0\x00\xA9\x3F\x8D\x02\x08\x86\x2B\x8E\xF4 Apple ][ Disk Image
+0 string \x01\xB0\x00\xA9\x48\x8D\x02\x08\x86\x2B\x8E\xF4 Apple ][ Disk Image
+0 string \x01\xBD\x88\xC0\x8A\x4A\x4A\x4A\x4A\x09\xC0\x8D Apple ][ Disk Image
+0 string \x01\xBD\x88\xC0\x8A\x4A\x4A\x4A\x4A\x8D\x2F\x08 Apple ][ Disk Image
+0 string \x01\xD8\x2C\x81\xC0\xA9\x60\x4D\x58\xFF\xD0\xFE Apple ][ Disk Image
+0 string \x01\xD8\x78\xBD\x88\xC0\xA9\xFD\x85\x37\x85\x39 Apple ][ Disk Image
+0 string \x01\xE0\x60\xF0\x03\x4C\x16\x09\xAD\x00\x08\xC9 Apple ][ Disk Image
+0 string \x01\xE0\x60\xF0\x03\x4C\xCB\x08\xAD\x00\x08\xC9 Apple ][ Disk Image
+0 string \x01\xE0\x60\xF0\x03\x4C\xEE\x08\xAD\x00\x08\xC9 Apple ][ Disk Image
+0 string \x01\xE0\x60\xF0\x03\x4C\xEF\x08\xAD\x00\x08\xC9 Apple ][ Disk Image
+0 string \x01\xE0\x70\xB0\x04\xE0\x40\xB0\x39\xBD\x88\xC0 Apple ][ Disk Image
+0 string \x01\xEA\x8D\xF4\x03\xA9\x60\x9D\x88\xC0\x8D\x51 Apple ][ Disk Image
+
+# magic for Newton PDA package formats
+# from Ruda Moura <ruda@helllabs.org>
+0 string package0 Newton package, NOS 1.x,
+>12 belong &0x80000000 AutoRemove,
+>12 belong &0x40000000 CopyProtect,
+>12 belong &0x10000000 NoCompression,
+>12 belong &0x04000000 Relocation,
+>12 belong &0x02000000 UseFasterCompression,
+>16 belong x version %d
+
+0 string package1 Newton package, NOS 2.x,
+>12 belong &0x80000000 AutoRemove,
+>12 belong &0x40000000 CopyProtect,
+>12 belong &0x10000000 NoCompression,
+>12 belong &0x04000000 Relocation,
+>12 belong &0x02000000 UseFasterCompression,
+>16 belong x version %d
+
+0 string package4 Newton package,
+>8 byte 8 NOS 1.x,
+>8 byte 9 NOS 2.x,
+>12 belong &0x80000000 AutoRemove,
+>12 belong &0x40000000 CopyProtect,
+>12 belong &0x10000000 NoCompression,
+
+# The following entries for the Apple II are for files that have
+# been transferred as raw binary data from an Apple, without having
+# been encapsulated by any of the above archivers.
+#
+# In general, Apple II formats are hard to identify because Apple DOS
+# and especially Apple ProDOS have strong typing in the file system and
+# therefore programmers never felt much need to include type information
+# in the files themselves.
+#
+# Eric Fischer <enf@pobox.com>
+
+# AppleWorks word processor:
+# URL: https://en.wikipedia.org/wiki/AppleWorks
+# Reference: http://www.gno.org/pub/apple2/doc/apple/filetypes/ftn.1a.xxxx
+# Update: Joerg Jenderek
+# NOTE:
+# The "O" is really the magic number, but that's so common that it's
+# necessary to check the tab stops that follow it to avoid false positives.
+# and/or look for unused bits of booleans bytes like zoom, paginated, mail merge
+# the newer AppleWorks is from claris with extension CWK
+4 string O
+# test for unused bits of zoom- , paginated-boolean bytes
+>84 ubequad ^0x00Fe00000000Fe00
+# look for tabstop definitions "=" no tab, "|" no tab
+# "<" left tab,"^" center tab,">" right tab, "." decimal tab,
+# unofficial "!" other , "\x8a" other
+# official only if SFMinVers is nonzero
+>>5 regex/s [=.<>|!^\x8a]{79} AppleWorks Word Processor
+# AppleWorks Word Processor File (Apple II)
+# ./apple (version 5.25) labeled the entry as "AppleWorks word processor data"
+# application/x-appleworks is mime type for claris version with cwk extension
+!:mime application/x-appleworks3
+# http://home.earthlink.net/~hughhood/appleiiworksenvoy/
+# ('p' + 1-byte ProDOS File Type + 2-byte ProDOS Aux Type')
+# $70 $1A $F8 $FF is this the apple type ?
+#:apple pdosp^Z\xf8\xff
+!:ext awp
+# minimum version needed to read this files. SFMinVers (0 , 30~3.0 )
+>>>183 ubyte 30 3.0
+>>>183 ubyte !30
+>>>>183 ubyte !0 %#x
+# usual tabstop start sequence "=====<"
+>>>5 string x \b, tabstop ruler "%6.6s"
+# tabstop ruler
+#>>>5 string >\0 \b, tabstops "%-79s"
+# zoom switch
+>>>85 byte&0x01 >0 \b, zoomed
+# whether paginated
+>>>90 byte&0x01 >0 \b, paginated
+# contains any mail-merge commands
+>>>92 byte&0x01 >0 \b, with mail merge
+# left margin in 1/10 inches ( normally 0 or 10 )
+>>>91 ubyte >0
+>>>>91 ubyte x \b, %d/10 inch left margin
+
+# AppleWorks database:
+#
+# This isn't really a magic number, but it's the closest thing to one
+# that I could find. The 1 and 2 really mean "order in which you defined
+# categories" and "left to right, top to bottom," respectively; the D and R
+# mean that the cursor should move either down or right when you press Return.
+
+#30 string \x01D AppleWorks database data
+#30 string \x02D AppleWorks database data
+#30 string \x01R AppleWorks database data
+#30 string \x02R AppleWorks database data
+
+# AppleWorks spreadsheet:
+#
+# Likewise, this isn't really meant as a magic number. The R or C means
+# row- or column-order recalculation; the A or M means automatic or manual
+# recalculation.
+
+#131 string RA AppleWorks spreadsheet data
+#131 string RM AppleWorks spreadsheet data
+#131 string CA AppleWorks spreadsheet data
+#131 string CM AppleWorks spreadsheet data
+
+# Applesoft BASIC:
+#
+# This is incredibly sloppy, but will be true if the program was
+# written at its usual memory location of 2048 and its first line
+# number is less than 256. Yuck.
+# update by Joerg Jenderek at Feb 2013
+
+# GRR: this test is still too general as it catches also Gujin BOOT144.SYS (0xfa080000)
+#0 belong&0xff00ff 0x80000 Applesoft BASIC program data
+0 belong&0x00ff00ff 0x00080000
+# assuming that line number must be positive
+>2 leshort >0 Applesoft BASIC program data, first line number %d
+#>2 leshort x \b, first line number %d
+
+# ORCA/EZ assembler:
+#
+# This will not identify ORCA/M source files, since those have
+# some sort of date code instead of the two zero bytes at 6 and 7
+# XXX Conflicts with ELF
+#4 belong&0xff00ffff 0x01000000 ORCA/EZ assembler source data
+#>5 byte x \b, build number %d
+
+# Broderbund Fantavision
+#
+# I don't know what these values really mean, but they seem to recur.
+# Will they cause too many conflicts?
+
+# Probably :-)
+#2 belong&0xFF00FF 0x040008 Fantavision movie data
+
+# Some attempts at images.
+#
+# These are actually just bit-for-bit dumps of the frame buffer, so
+# there's really no reasonably way to distinguish them except for their
+# address (if preserved) -- 8192 or 16384 -- and their length -- 8192
+# or, occasionally, 8184.
+#
+# Nevertheless this will manage to catch a lot of images that happen
+# to have a solid-colored line at the bottom of the screen.
+
+# GRR: Magic too weak
+#8144 string \x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F Apple II image with white background
+#8144 string \x55\x2A\x55\x2A\x55\x2A\x55\x2A Apple II image with purple background
+#8144 string \x2A\x55\x2A\x55\x2A\x55\x2A\x55 Apple II image with green background
+#8144 string \xD5\xAA\xD5\xAA\xD5\xAA\xD5\xAA Apple II image with blue background
+#8144 string \xAA\xD5\xAA\xD5\xAA\xD5\xAA\xD5 Apple II image with orange background
+
+# Beagle Bros. Apple Mechanic fonts
+
+0 belong&0xFF00FFFF 0x6400D000 Apple Mechanic font
+
+# Apple Universal Disk Image Format (UDIF) - dmg files.
+# From Johan Gade.
+# These entries are disabled for now until we fix the following issues.
+#
+# Note there might be some problems with the "VAX COFF executable"
+# entry. Note this entry should be placed before the mac filesystem section,
+# particularly the "Apple Partition data" entry.
+#
+# The intended meaning of these tests is, that the file is only of the
+# specified type if both of the lines are correct - i.e. if the first
+# line matches and the second doesn't then it is not of that type.
+#
+#0 long 0x7801730d
+#>4 long 0x62626060 UDIF read-only zlib-compressed image (UDZO)
+#
+# Note that this entry is recognized correctly by the "Apple Partition
+# data" entry - however since this entry is more specific - this
+# information seems to be more useful.
+#0 long 0x45520200
+#>0x410 string disk\ image UDIF read/write image (UDRW)
+
+# From: Toby Peterson <toby@apple.com>
+# From https://www.nationalarchives.gov.uk/pronom/fmt/866
+0 string bplist00
+>8 search/500 WebMainResource Apple Safari Webarchive
+!:mime application/x-webarchive
+!:strength +50
+0 string bplist00 Apple binary property list
+!:mime application/x-bplist
+
+# Apple binary property list (bplist)
+# Assumes version bytes are hex.
+# Provides content hints for version 0 files. Assumes that the root
+# object is the first object (true for CoreFoundation implementation).
+# From: David Remahl <dremahl@apple.com>
+0 string bplist
+>6 byte x \bCoreFoundation binary property list data, version %#c
+>>7 byte x \b%c
+>6 string 00 \b
+>>8 byte&0xF0 0x00 \b
+>>>8 byte&0x0F 0x00 \b, root type: null
+>>>8 byte&0x0F 0x08 \b, root type: false boolean
+>>>8 byte&0x0F 0x09 \b, root type: true boolean
+>>8 byte&0xF0 0x10 \b, root type: integer
+>>8 byte&0xF0 0x20 \b, root type: real
+>>8 byte&0xF0 0x30 \b, root type: date
+>>8 byte&0xF0 0x40 \b, root type: data
+>>8 byte&0xF0 0x50 \b, root type: ascii string
+>>8 byte&0xF0 0x60 \b, root type: unicode string
+>>8 byte&0xF0 0x80 \b, root type: uid (CORRUPT)
+>>8 byte&0xF0 0xa0 \b, root type: array
+>>8 byte&0xF0 0xd0 \b, root type: dictionary
+
+# Apple/NeXT typedstream data
+# Serialization format used by NeXT and Apple for various
+# purposes in YellowStep/Cocoa, including some nib files.
+# From: David Remahl <dremahl@apple.com>
+2 string typedstream NeXT/Apple typedstream data, big endian
+>0 byte x \b, version %d
+>0 byte <5 \b
+>>13 byte 0x81 \b
+>>>14 ubeshort x \b, system %d
+2 string streamtyped NeXT/Apple typedstream data, little endian
+>0 byte x \b, version %d
+>0 byte <5 \b
+>>13 byte 0x81 \b
+>>>14 uleshort x \b, system %d
+
+#------------------------------------------------------------------------------
+# CAF: Apple CoreAudio File Format
+#
+# Container format for high-end audio purposes.
+# From: David Remahl <dremahl@apple.com>
+#
+0 string caff CoreAudio Format audio file
+>4 beshort <10 version %d
+>6 beshort x
+
+
+#------------------------------------------------------------------------------
+# Keychain database files
+0 string kych Mac OS X Keychain File
+
+#------------------------------------------------------------------------------
+# Code Signing related file types
+0 belong 0xfade0c00 Mac OS X Code Requirement
+>8 belong 1 (opExpr)
+>4 belong x - %d bytes
+
+0 belong 0xfade0c01 Mac OS X Code Requirement Set
+>8 belong >1 containing %d items
+>4 belong x - %d bytes
+
+0 belong 0xfade0c02 Mac OS X Code Directory
+>8 belong x version %x
+>12 belong >0 flags %#x
+>4 belong x - %d bytes
+
+0 belong 0xfade0cc0 Mac OS X Detached Code Signature (non-executable)
+>4 belong x - %d bytes
+
+0 belong 0xfade0cc1 Mac OS X Detached Code Signature
+>8 belong >1 (%d elements)
+>4 belong x - %d bytes
+
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# .vdi
+4 string innotek\ VirtualBox\ Disk\ Image %s
+
+# Apple disk partition stuff
+# URL: https://en.wikipedia.org/wiki/Apple_Partition_Map
+# Reference: https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/bootblock.h
+# Update: Joerg Jenderek
+# "ER" is APPLE_DRVR_MAP_MAGIC signature
+0 beshort 0x4552
+# display Apple Driver Map (strength=50) after Syslinux bootloader (71)
+#!:strength +0
+# strengthen the magic by looking for used blocksizes 512 2048
+>2 ubeshort&0xf1FF 0 Apple Driver Map
+# last 6 bytes for padding found are 0 or end with 55AAh marker for MBR hybrid
+#>>504 ubequad&0x0000FFffFFff0000 0
+!:mime application/x-apple-diskimage
+!:apple ????devr
+# https://en.wikipedia.org/wiki/Apple_Disk_Image
+!:ext dmg/iso
+# sbBlkSize for driver descriptor map 512 2048
+>>2 beshort x \b, blocksize %d
+# sbBlkCount sometimes garbish like
+# 0xb0200000 for unzlibed install_flash_player_19.0.0.245_osx.dmg
+# 0xf2720100 for bunziped Firefox 48.0-2.dmg
+# 0xeb02ffff for super_grub2_disk_hybrid_2.02s3.iso
+# 0x00009090 by syslinux-6.03/utils/isohybrid.c
+>>4 ubelong x \b, blockcount %u
+# following device/driver information not very useful
+# device type 0 1 (37008 garbage for super_grub2_disk_hybrid_2.02s3.iso)
+>>8 ubeshort x \b, devtype %u
+# device id 0 1 (37008 garbage for super_grub2_disk_hybrid_2.02s3.iso)
+>>10 ubeshort x \b, devid %u
+# driver data 0 (2425393296 garbage for super_grub2_disk_hybrid_2.02s3.iso)
+>>12 ubelong >0
+>>>12 ubelong x \b, driver data %u
+# number of driver descriptors sbDrvrCount <= 61
+# (37008 garbage for super_grub2_disk_hybrid_2.02s3.iso)
+>>16 ubeshort x \b, driver count %u
+# 61 * apple_drvr_descriptor[8]. information not very useful or same as in partition map
+# >>18 use apple-driver-map
+# >>26 use apple-driver-map
+# # ...
+# >>500 use apple-driver-map
+# number of partitions is always same in every partition (map block count)
+#>>0x0204 ubelong x \b, %u partitions
+>>0x0204 ubelong >0 \b, contains[@0x200]:
+>>>0x0200 use apple-apm
+>>0x0204 ubelong >1 \b, contains[@0x400]:
+>>>0x0400 use apple-apm
+>>0x0204 ubelong >2 \b, contains[@0x600]:
+>>>0x0600 use apple-apm
+>>0x0204 ubelong >3 \b, contains[@0x800]:
+>>>0x0800 use apple-apm
+>>0x0204 ubelong >4 \b, contains[@0xA00]:
+>>>0x0A00 use apple-apm
+>>0x0204 ubelong >5 \b, contains[@0xC00]:
+>>>0x0C00 use apple-apm
+>>0x0204 ubelong >6 \b, contains[@0xE00]:
+>>>0x0E00 use apple-apm
+>>0x0204 ubelong >7 \b, contains[@0x1000]:
+>>>0x1000 use apple-apm
+# display apple driver descriptor map (start-block, # blocks in sbBlkSize sizes, type)
+0 name apple-driver-map
+>0 ubequad !0
+# descBlock first block of driver
+>>0 ubelong x \b, driver start block %u
+# descSize driver size in blocks
+>>4 ubeshort x \b, size %u
+# descType driver system type 1 701h F8FFh FFFFh
+>>6 ubeshort x \b, type %#x
+
+# URL: https://en.wikipedia.org/wiki/Apple_Partition_Map
+# Reference: https://opensource.apple.com/source/IOStorageFamily/IOStorageFamily-116/IOApplePartitionScheme.h
+# Update: Joerg Jenderek
+# Yes, the 3rd and 4th bytes pmSigPad are reserved, but we use them to make the
+# magic stronger.
+# for apple partition map stored as a single file
+0 belong 0x504d0000
+# to display Apple Partition Map (strength=70) after Syslinux bootloader (71)
+#!:strength +0
+>0 use apple-apm
+# magic/Magdir/apple14.test, 365: Warning: Current entry does not yet have a description for adding a EXTENSION type
+# file: could not find any valid magic files!
+#!:ext bin
+# display apple partition map. Normally called after Apple driver map
+0 name apple-apm
+>0 belong 0x504d0000 Apple Partition Map
+# number of partitions
+>>4 ubelong x \b, map block count %u
+# logical block (512 bytes) start of partition
+>>8 ubelong x \b, start block %u
+>>12 ubelong x \b, block count %u
+>>16 string >0 \b, name %s
+>>48 string >0 \b, type %s
+# processor type dpme_process_id[16] e.g. "68000" "68020"
+>>120 string >0 \b, processor %s
+# A/UX boot arguments BootArgs[128]
+>>136 string >0 \b, boot arguments %s
+# status of partition dpme_flags
+>>88 belong & 1 \b, valid
+>>88 belong & 2 \b, allocated
+>>88 belong & 4 \b, in use
+>>88 belong & 8 \b, has boot info
+>>88 belong & 16 \b, readable
+>>88 belong & 32 \b, writable
+>>88 belong & 64 \b, pic boot code
+>>88 belong & 128 \b, chain compatible driver
+>>88 belong & 256 \b, real driver
+>>88 belong & 512 \b, chain driver
+# mount automatically at startup APPLE_PS_AUTO_MOUNT
+>>88 ubelong &0x40000000 \b, mount at startup
+# is the startup partition APPLE_PS_STARTUP
+>>88 ubelong &0x80000000 \b, is the startup partition
+
+#https://wiki.mozilla.org/DS_Store_File_Format
+#https://en.wikipedia.org/wiki/.DS_Store
+0 string \0\0\0\1Bud1\0 Apple Desktop Services Store
+
+# HFS/HFS+ Resource fork files (andrew.roazen@nau.edu Apr 13 2015)
+# Usually not in separate files, but have either filename rsrc with
+# no extension, or a filename corresponding to another file, with
+# extensions rsr/rsrc
+# URL: http://fileformats.archiveteam.org/wiki/Macintosh_resource_file
+# https://en.wikipedia.org/wiki/Resource_fork
+# Reference: https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
+# http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/MoreMacintoshToolbox.pdf
+# https://formats.kaitai.io/resource_fork/
+# Update: Joerg Jenderek
+# Note: verified often by command like `deark -m macrsrc Icon_.rsrc`
+# offset of resource data; usually starts at offset 0x0100
+0 string \000\000\001\000
+# skip NPETraceSession.etl with invalid "low" map offset 0
+>4 ubelong >0xFF
+# skip few Atari DEGAS Elite bitmap (eil2.pi1 nastro.pi1) with ivalid "high" 0x6550766 0x7510763 map length
+>>12 ubelong <0x8001
+# most examples with zeroed system reserved field
+>>>16 lelong =0
+>>>>0 use apple-rsr
+# few samples with not zeroed system reserved field like: Empty.rsrc.rsr OpenSans-CondBold.dfont
+>>>16 lelong !0
+# resource fork variant with not zeroed system reserved field and copy of header
+>>>>(4.L) ubelong 0x100
+# GRR: the line above only works if in ../../src/file.h FILE_BYTES_MAX is raised from 1 MiB above 0x6ab0f4 (HelveticaNeue.dfont)
+>>>>>0 use apple-rsr
+# data fork variant with not zeroed system reserved field and no copy of header
+>>>>(4.L) ubelong 0
+>>>>>0 use apple-rsr
+# Note: moved and merged from ./macintosh
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: https://en.wikipedia.org/wiki/Datafork_TrueType
+# Derived from the 'fondu' and 'ufond' source code (fondu.sf.net). 'sfnt' is
+# TrueType; 'POST' is PostScript. 'FONT' and 'NFNT' sometimes appear, but I
+# don't know what they mean.
+# display information about Mac OSX datafork font DFONT
+0 name apple-dfont
+>(4.L+30) ubelong x Mac OSX datafork font,
+# https://en.wikipedia.org/wiki/Datafork_TrueType
+!:mime application/x-dfont
+!:ext dfont
+# https://exiftool.org/TagNames/RSRC.html
+>(4.L+30) ubelong 0x73666e74 TrueType
+>(4.L+30) ubelong 0x464f4e54 'FONT'
+>(4.L+30) ubelong 0x4e464e54 'NFNT'
+>(4.L+30) ubelong 0x504f5354 PostScript
+>(4.L+30) ubelong 0x464f4e44 'FOND'
+>(4.L+30) ubelong 0x76657273 'vers'
+# display information about Macintosh resource
+0 name apple-rsr
+>(4.L+30) ubelong 0x73666e74
+>>0 use apple-dfont
+>(4.L+30) ubelong 0x464f4e54
+>>0 use apple-dfont
+>(4.L+30) ubelong 0x4e464e54
+>>0 use apple-dfont
+>(4.L+30) ubelong 0x504f5354
+>>0 use apple-dfont
+>(4.L+30) ubelong 0x464f4e44
+>>0 use apple-dfont
+>(4.L+30) ubelong 0x76657273
+>>0 use apple-dfont
+>(4.L+30) default x Apple HFS/HFS+ resource fork
+#!:mime application/octet-stream
+!:mime application/x-apple-rsr
+!:ext rsrc/rsr
+# offset to resource data; usually starts at offset 0x0100
+>0 ubelong !0x100 \b, data offset %#x
+# offset to resource map; positive but not nil like in NPETraceSession.etl
+>4 ubelong x \b, map offset %#x
+# length of resource map; positive with 32K limitation but not
+# nil like in NPETraceSession.etl or high like 0x7510763 in nastro.pi1
+>12 ubelong x \b, map length %#x
+# length of resource data; positive but not nil like in NPETraceSession.etl
+>8 ubelong x \b, data length %#x
+# reserved 112 bytes for system use; apparently often nil, but 8fd20000h in Empty.rsrc.rsr and 0x00768c2b in OpenSans-CondBold.dfont
+>16 ubelong !0 \b, at 16 %#8.8x
+# https://fontforge.org/docs/techref/macformats.html
+# jump to resource map
+# a copy of resource header or 16 bytes of zeros for data fork
+#>(4.L) ubelong x \b, DATA offset %#x
+#>(4.L+4) ubelong x \b, MAP offset %#x
+#>(4.L+8) ubelong x \b, DATA length %#x
+#>(4.L+12) ubelong x \b, MAP length %#x
+# nextResourceMap; handle to next resource map; used by the Resource Manager for internal bookkeeping; should be zero
+>(4.L+16) ubelong !0 \b, nextResourceMap %#x
+# fileRef; file reference number; used by the Resource Manager for internal bookkeeping; should be zero
+>(4.L+20) ubeshort !0 \b, fileRef %#x
+# attributes; Resource fork attributes (80h~read-only 40h~compression needed 20h~changed); other bits are reserved and should be zero
+>(4.L+22) ubeshort !0 \b, attributes %#x
+# typeListOffset; offset from resource map to start of type list like: 1Ch
+>(4.L+24) ubeshort x \b, list offset %#x
+# nameListOffset; offset from esource map to start of name list like: 32h 46h 56h (XLISP.RSR XLISPTIN.RSR) 13Eh (HelveticaNeue.dfont)
+>(4.L+26) ubeshort x \b, name offset %#x
+# typeCount; number of types in the map minus 1; If there are no resources, this is 0xFFFF
+>(4.L+28) beshort+1 >0 \b, %u type
+# plural s
+>>(4.L+28) beshort+1 >1 \bs
+# resource type list array; 1st resource type like: ALRT CODE FOND MPSR icns scsz
+>>(4.L+30) ubelong x \b, %#x
+>>(4.L+30) string x '%-.4s'
+# resourceCount; number of this type resources minus one. If there is one resource of this type, this is 0x0000
+>>(4.L+34) beshort+1 x * %d
+# resourceListOffset; offset from type list to resource list like: Ah 12h DAh
+>(4.L+36) ubeshort x resource offset %#x
+
+#https://en.wikipedia.org/wiki/AppleScript
+0 string FasdUAS AppleScript compiled
+
+# AppleWorks/ClarisWorks
+# https://github.com/joshenders/appleworks_format
+# http://fileformats.archiveteam.org/wiki/AppleWorks
+0 name appleworks
+>0 belong&0x00ffffff 0x07e100 AppleWorks CWK Document
+>0 belong&0x00ffffff 0x008803 ClarisWorks CWK Document
+>0 default x
+>>0 belong x AppleWorks/ClarisWorks CWK Document
+>0 byte x \b, version %d
+>30 beshort x \b, %d
+>32 beshort x \bx%d
+!:ext cwk
+
+4 string BOBO
+>0 byte >4
+>>12 belong 0
+>>>26 belong 0
+>>>>0 use appleworks
+>0 belong 0x0481ad00
+>>0 use appleworks
+
+# magic for Apple File System (APFS)
+# from Alex Myczko <alex@aiei.ch>
+32 string NXSB Apple File System (APFS)
+>36 ulelong x \b, blocksize %u
+
+# iTunes cover art (versions 1 and 2)
+4 string itch
+>24 string artw
+>>0x1e8 string data iTunes cover art
+>>>0x1ed string PNG (PNG)
+>>>0x1ec beshort 0xffd8 (JPEG)
+
+# MacPaint image
+65 string PNTGMPNT MacPaint image data
+#0 belong 2 MacPaint image data
diff --git a/contrib/libs/libmagic/magic/Magdir/application b/contrib/libs/libmagic/magic/Magdir/application
new file mode 100644
index 0000000000..f316608081
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/application
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# $File: application,v 1.1 2016/10/17 12:13:01 christos Exp $
+# application: file(1) magic for applications on small devices
+#
+# Pebble Application
+0 string PBLAPP\000\000 Pebble application
diff --git a/contrib/libs/libmagic/magic/Magdir/applix b/contrib/libs/libmagic/magic/Magdir/applix
new file mode 100644
index 0000000000..f3f362eec7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/applix
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: applix,v 1.5 2009/09/19 16:28:08 christos Exp $
+# applix: file(1) magic for Applixware
+# From: Peter Soos <sp@osb.hu>
+#
+0 string *BEGIN Applixware
+>7 string WORDS Words Document
+>7 string GRAPHICS Graphic
+>7 string RASTER Bitmap
+>7 string SPREADSHEETS Spreadsheet
+>7 string MACRO Macro
+>7 string BUILDER Builder Object
diff --git a/contrib/libs/libmagic/magic/Magdir/apt b/contrib/libs/libmagic/magic/Magdir/apt
new file mode 100644
index 0000000000..2d9f15901b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/apt
@@ -0,0 +1,52 @@
+
+#------------------------------------------------------------------------------
+# $File: apt,v 1.1 2016/10/17 19:51:57 christos Exp $
+# apt: file(1) magic for APT Cache files
+# <http://www.fifi.org/doc/libapt-pkg-doc/cache.html/ch2.html>
+# <https://anonscm.debian.org/cgit/apt/apt.git/tree/apt-pkg/pkgcache.h#n292>
+
+# before version 10 ("old format"), data was in arch-specific long/short
+
+# old format 64 bit
+0 name apt-cache-64bit-be
+>12 beshort 1 \b, dirty
+>40 bequad x \b, %llu packages
+>48 bequad x \b, %llu versions
+
+# old format 32 bit
+0 name apt-cache-32bit-be
+>8 beshort 1 \b, dirty
+>40 belong x \b, %u packages
+>44 belong x \b, %u versions
+
+# new format
+0 name apt-cache-be
+>6 byte 1 \b, dirty
+>24 belong x \b, %u packages
+>28 belong x \b, %u versions
+
+0 bequad 0x98FE76DC
+>8 ubeshort <10 APT cache data, version %u
+>>10 beshort x \b.%u, 64 bit big-endian
+>>0 use apt-cache-64bit-be
+
+0 lequad 0x98FE76DC
+>8 uleshort <10 APT cache data, version %u
+>>10 leshort x \b.%u, 64 bit little-endian
+>>0 use \^apt-cache-64bit-be
+
+0 belong 0x98FE76DC
+>4 ubeshort <10 APT cache data, version %u
+>>6 ubeshort x \b.%u, 32 bit big-endian
+>>0 use apt-cache-32bit-be
+>4 ubyte >9 APT cache data, version %u
+>>5 ubyte x \b.%u, big-endian
+>>0 use apt-cache-be
+
+0 lelong 0x98FE76DC
+>4 uleshort <10 APT cache data, version %u
+>>6 uleshort x \b.%u, 32 bit little-endian
+>>0 use \^apt-cache-32bit-be
+>4 ubyte >9 APT cache data, version %u
+>>5 ubyte x \b.%u, little-endian
+>>0 use \^apt-cache-be
diff --git a/contrib/libs/libmagic/magic/Magdir/archive b/contrib/libs/libmagic/magic/Magdir/archive
new file mode 100644
index 0000000000..6e1f9678e7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/archive
@@ -0,0 +1,2607 @@
+#------------------------------------------------------------------------------
+# $File: archive,v 1.193 2023/07/27 17:55:58 christos Exp $
+# archive: file(1) magic for archive formats (see also "msdos" for self-
+# extracting compressed archives)
+#
+# cpio, ar, arc, arj, hpack, lha/lharc, rar, squish, uc2, zip, zoo, etc.
+# pre-POSIX "tar" archives are also handled in the C code ../../src/is_tar.c.
+
+# POSIX tar archives
+# URL: https://en.wikipedia.org/wiki/Tar_(computing)
+# Reference: https://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5&manpath=FreeBSD+8-current
+# header mainly padded with nul bytes
+500 quad 0
+!:strength /2
+# filename or extended attribute printable strings in range space null til umlaut ue
+>0 ubeshort >0x1F00
+>>0 ubeshort <0xFCFD
+# last 4 header bytes often null but tar\0 in gtarfail2.tar gtarfail.tar-bad
+# at https://sourceforge.net/projects/s-tar/files/testscripts/
+>>>508 ubelong&0x8B9E8DFF 0
+# nul, space or ascii digit 0-7 at start of mode
+>>>>100 ubyte&0xC8 =0
+>>>>>101 ubyte&0xC8 =0
+# nul, space at end of check sum
+>>>>>>155 ubyte&0xDF =0
+# space or ascii digit 0 at start of check sum
+>>>>>>>148 ubyte&0xEF =0x20
+# FOR DEBUGGING:
+#>>>>>>>>0 regex \^[0-9]{2,4}[.](png|jpg|jpeg|tif|tiff|gif|bmp) NAME "%s"
+# check for 1st image main name with digits used for sorting
+# and for name extension case insensitive like: PNG JPG JPEG TIF TIFF GIF BMP
+>>>>>>>>0 regex \^[0-9]{2,4}[.](png|jpg|jpeg|tif|tiff|gif|bmp)
+>>>>>>>>>0 use tar-cbt
+# check for 1st member name with ovf suffix
+>>>>>>>>0 regex \^.{1,96}[.](ovf)
+>>>>>>>>>0 use tar-ova
+# if 1st member name without digits and without used image suffix and without *.ovf then it is a TAR archive
+>>>>>>>>0 default x
+>>>>>>>>>0 use tar-file
+# minimal check and then display tar archive information which can also be
+# embedded inside others like Android Backup, Clam AntiVirus database
+0 name tar-file
+>257 string !ustar
+# header padded with nuls
+>>257 ulong =0
+# GNU tar version 1.29 with non pax format option without refusing
+# creates misleading V7 header for Long path, Multi-volume, Volume type
+>>>156 ubyte 0x4c GNU tar archive
+!:mime application/x-gtar
+!:ext tar/gtar
+>>>156 ubyte 0x4d GNU tar archive
+!:mime application/x-gtar
+!:ext tar/gtar
+>>>156 ubyte 0x56 GNU tar archive
+!:mime application/x-gtar
+!:ext tar/gtar
+>>>156 default x tar archive (V7)
+!:mime application/x-tar
+!:ext tar
+# other stuff in padding
+# some implementations add new fields to the blank area at the end of the header record
+# created for example by DOS TAR 3.20g 1994 Tim V.Shapore with -j option
+>>257 ulong !0 tar archive (old)
+!:mime application/x-tar
+!:ext tar
+# magic in newer, GNU, posix variants
+>257 string =ustar
+# 2 last char of magic and UStar version because string expression does not work
+# 2 space characters followed by a null for GNU variant
+>>261 ubelong =0x72202000 POSIX tar archive (GNU)
+!:mime application/x-gtar
+!:ext tar/gtar
+# UStar version with ASCII "00"
+>>261 ubelong 0x72003030 POSIX
+# gLOBAL and ExTENSION type only found in POSIX.1-2001 format
+>>>156 ubyte 0x67 \b.1-2001
+>>>156 ubyte 0x78 \b.1-2001
+>>>156 ubyte x tar archive
+!:mime application/x-ustar
+!:ext tar/ustar
+# version with 2 binary nuls embedded in Android Backup like com.android.settings.ab
+>>261 ubelong 0x72000000 tar archive (ustar)
+!:mime application/x-ustar
+!:ext tar/ustar
+# not seen ustar variant with garbish version
+>>261 default x tar archive (unknown ustar)
+!:mime application/x-ustar
+!:ext tar/ustar
+# type flag of 1st tar archive member
+#>156 ubyte x \b, %c-type
+>156 ubyte x
+>>156 ubyte 0 \b, file
+>>156 ubyte 0x30 \b, file
+>>156 ubyte 0x31 \b, hard link
+>>156 ubyte 0x32 \b, symlink
+>>156 ubyte 0x33 \b, char device
+>>156 ubyte 0x34 \b, block device
+>>156 ubyte 0x35 \b, directory
+>>156 ubyte 0x36 \b, fifo
+>>156 ubyte 0x37 \b, reserved
+>>156 ubyte 0x4c \b, long path
+>>156 ubyte 0x4d \b, multi volume
+>>156 ubyte 0x56 \b, volume
+>>156 ubyte 0x67 \b, global
+>>156 ubyte 0x78 \b, extension
+>>156 default x \b, type
+>>>156 ubyte x '%c'
+# name[100]
+>0 string >\0 %-.60s
+# mode mainly stored as an octal number in ASCII null or space terminated
+>100 string >\0 \b, mode %-.7s
+# user id mainly as octal numbers in ASCII null or space terminated
+>108 string >\0 \b, uid %-.7s
+# group id mainly as octal numbers in ASCII null or space terminated
+>116 string >\0 \b, gid %-.7s
+# size mainly as octal number in ASCII
+>124 ubyte <0x38
+>>124 string >\0 \b, size %-.12s
+# coding indicated by setting the high-order bit of the leftmost byte
+>124 ubyte >0xEF \b, size 0x
+>>124 ubyte !0xff \b%2.2x
+>>125 ubyte !0xff \b%2.2x
+>>126 ubyte !0xff \b%2.2x
+>>127 ubyte !0xff \b%2.2x
+>>128 ubyte !0xff \b%2.2x
+>>129 ubyte !0xff \b%2.2x
+>>130 ubyte !0xff \b%2.2x
+>>131 ubyte !0xff \b%2.2x
+>>132 ubyte !0xff \b%2.2x
+>>133 ubyte !0xff \b%2.2x
+>>134 ubyte !0xff \b%2.2x
+>>135 ubyte !0xff \b%2.2x
+# seconds since 0:0:0 1 jan 1970 UTC as octal number mainly in ASCII null or space terminated
+>136 string >\0 \b, seconds %-.11s
+# header checksum stored as an octal number in ASCII null or space terminated
+#>148 string x \b, cksum %.7s
+# linkname[100]
+>157 string >\0 \b, linkname %-.40s
+# additional fields for ustar
+>257 string =ustar
+# owner user name null terminated
+>>265 string >\0 \b, user %-.32s
+# group name null terminated
+>>297 string >\0 \b, group %-.32s
+# device major minor if not zero
+>>329 ubequad&0xCFCFCFCFcFcFcFdf !0
+>>>329 string x \b, devmaj %-.7s
+>>337 ubequad&0xCFCFCFCFcFcFcFdf !0
+>>>337 string x \b, devmin %-.7s
+# prefix[155]
+>>345 string >\0 \b, prefix %-.155s
+# old non ustar/POSIX tar
+>257 string !ustar
+>>508 string =tar\0
+# padding[255] in old star
+>>>257 string >\0 \b, padding: %-.40s
+>>508 default x
+# padding[255] in old tar sometimes comment field
+>>>257 string >\0 \b, comment: %-.40s
+# Summary: Comic Book Archive *.CBT with TAR format
+# URL: https://en.wikipedia.org/wiki/Comic_book_archive
+# http://fileformats.archiveteam.org/wiki/Comic_Book_Archive
+# Note: there exist also RAR, ZIP, ACE and 7Z packed variants
+0 name tar-cbt
+>0 string x Comic Book archive, tar archive
+#!:mime application/x-tar
+!:mime application/vnd.comicbook
+#!:mime application/vnd.comicbook+tar
+!:ext cbt
+# name[100] probably like: 19.jpg 0001.png 0002.png
+# or maybe like ComicInfo.xml
+>0 string >\0 \b, 1st image %-.60s
+# Summary: Open Virtualization Format *.OVF with disk images and more packed as TAR archive *.OVA
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Open_Virtualization_Format
+# http://fileformats.archiveteam.org/wiki/OVF_(Open_Virtualization_Format)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/o/ova.trid.xml
+# Note: called "Open Virtualization Format package" by TrID
+# assuming *.ovf comes first
+0 name tar-ova
+>0 string x Open Virtualization Format Archive
+#!:mime application/x-ustar
+# http://extension.nirsoft.net/ova
+!:mime application/x-virtualbox-ova
+!:ext ova
+# assuming name[100] like: DOS-0.9.ovf FreeDOS_1.ovf Win98SE_DE.ovf
+>0 string >\0 \b, with %-.60s
+
+# Incremental snapshot gnu-tar format from:
+# https://www.gnu.org/software/tar/manual/html_node/Snapshot-Files.html
+0 string GNU\ tar- GNU tar incremental snapshot data
+>&0 regex [0-9]\\.[0-9]+-[0-9]+ version %s
+
+# cpio archives
+#
+# Yes, the top two "cpio archive" formats *are* supposed to just be "short".
+# The idea is to indicate archives produced on machines with the same
+# byte order as the machine running "file" with "cpio archive", and
+# to indicate archives produced on machines with the opposite byte order
+# from the machine running "file" with "byte-swapped cpio archive".
+#
+# The SVR4 "cpio(4)" hints that there are additional formats, but they
+# are defined as "short"s; I think all the new formats are
+# character-header formats and thus are strings, not numbers.
+# URL: http://fileformats.archiveteam.org/wiki/Cpio
+# https://en.wikipedia.org/wiki/Cpio
+# Reference: https://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt
+# Update: Joerg Jenderek
+#
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-cpio-bin.trid.xml
+# Note: called "CPIO archive (binary)" by TrID, "cpio/Binary LE" by 7-Zip and "CPIO" by DROID via PUID fmt/635
+0 short 070707
+# skip DROID fmt-635-signature-id-960.cpio by looking for pathname of 1st entry
+>26 string >\0 cpio archive
+!:mime application/x-cpio
+# https://download.opensuse.org/distribution/leap/15.4/iso/openSUSE-Leap-15.4-NET-x86_64-Media.iso
+# boot/x86_64/loader/bootlogo
+# message.cpi
+!:ext /cpio/cpi
+>>0 use cpio-bin
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-cpio-bin-sw.trid.xml
+# Note: called "CPIO archive (byte swapped binary)" by TrID and "Cpio/Binary BE" by 7-Zip
+0 short 0143561 byte-swapped cpio archive
+!:mime application/x-cpio # encoding: swapped
+# https://telparia.com/fileFormatSamples/archive/cpio/skeleton2.cpio
+!:ext cpio
+>0 use cpio-bin-be
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-cpio.trid.xml
+# Note: called "CPIO archive (portable)" by TrID, "cpio/Portable ASCII" by 7-Zip and "cpio/odc" by GNU cpio
+0 string 070707 ASCII cpio archive (pre-SVR4 or odc)
+!:mime application/x-cpio
+# https://telparia.com/fileFormatSamples/archive/cpio/ pthreads-1.60B5.osr5src.cpio cinema.cpi VOL.000.008 VOL.000.012
+!:ext cpio/cpi/008/012
+# Note: called "CPIO archive (portable)" by TrID, "cpio/New ASCII" by 7-Zip and "cpio/newc" by GNU cpio
+0 string 070701 ASCII cpio archive (SVR4 with no CRC)
+!:mime application/x-cpio
+# https://telparia.com/fileFormatSamples/archive/cpio/MainActor-2.06.3.cpio
+!:ext cpio
+# Note: called "CPIO archive (portable)" by TrID, "cpio/New CRC" by 7-Zip and "cpio/crc" by GNU cpio
+0 string 070702 ASCII cpio archive (SVR4 with CRC)
+!:mime application/x-cpio
+# http://ftp.gnu.org/gnu/tar/tar-1.27.cpio.gz
+# https://telparia.com/fileFormatSamples/archive/cpio/pcmcia
+!:ext /cpio
+# display information of old binary cpio archive
+# Note: verfied by 7-Zip `7z l -tcpio -slt *.cpio` and
+# `cpio -ivt --numeric-uid-gid --file=clam.bin-le.cpio`
+0 name cpio-bin
+# c_dev; device number; WHAT IS THAT?
+>2 uleshort x \b; device %u
+# c_ino; truncated inode number; use `ls --inode`
+>4 uleshort x \b, inode %u
+# c_mode; mode specifies permissions and file type like: ?622~?rw-r--r-- by `ls -l`
+>6 uleshort x \b, mode %o
+# c_uid; numeric user id; use `ls --numeric-uid-gid`
+>8 uleshort x \b, uid %u
+# c_gid; numeric group id
+>10 uleshort x \b, gid %u
+# c_nlink; links to this file; directories at least 2
+>12 uleshort >1 \b, %u links
+# c_rdev; device number for block and character entries; zero for all other entries by writers
+# like 0x0440 for /dev/ttyS0
+>14 uleshort >0 \b, device %#4.4x
+# c_mtime[2]; modification time in seconds since 1 January 1970; most-significant 16 bits first
+>16 medate x \b, modified %s
+# c_filesize[2]; size of pathname; most-significant 16 bits first like: 544
+>22 melong x \b, %u bytes
+# c_namesize; bytes in the pathname that follows the header like: 9
+#>20 uleshort x \b, namesize %u
+# pathname of entry like: "clam.exe"
+>26 string x "%s"
+# display information of old binary byte swapped cpio archive
+# Note: verfied by 7-Zip `7z l -tcpio -slt *.cpio` and
+# `LANGUAGE=C cpio -ivt --numeric-uid-gid --file=clam.bin-be.cpio`
+0 name cpio-bin-be
+>2 ubeshort x \b; device %u
+>4 ubeshort x \b, inode %u
+>6 ubeshort x \b, mode %o
+>8 ubeshort x \b, uid %u
+>10 ubeshort x \b, gid %u
+>12 ubeshort >1 \b, %u links
+>14 ubeshort >0 \b, device %#4.4x
+>16 bedate x \b, modified %s
+>22 ubelong x \b, %u bytes
+#>20 ubeshort x \b, namesize %u
+>26 string x "%s"
+
+#
+# Various archive formats used by various versions of the "ar"
+# command.
+#
+
+#
+# Original UNIX archive formats.
+# They were written with binary values in host byte order, and
+# the magic number was a host "int", which might have been 16 bits
+# or 32 bits. We don't say "PDP-11" or "VAX", as there might have
+# been ports to little-endian 16-bit-int or 32-bit-int platforms
+# (x86?) using some of those formats; if none existed, feel free
+# to use "PDP-11" for little-endian 16-bit and "VAX" for little-endian
+# 32-bit. There might have been big-endian ports of that sort as
+# well.
+#
+0 leshort 0177555 very old 16-bit-int little-endian archive
+0 beshort 0177555 very old 16-bit-int big-endian archive
+0 lelong 0177555 very old 32-bit-int little-endian archive
+0 belong 0177555 very old 32-bit-int big-endian archive
+
+0 leshort 0177545 old 16-bit-int little-endian archive
+>2 string __.SYMDEF random library
+0 beshort 0177545 old 16-bit-int big-endian archive
+>2 string __.SYMDEF random library
+0 lelong 0177545 old 32-bit-int little-endian archive
+>4 string __.SYMDEF random library
+0 belong 0177545 old 32-bit-int big-endian archive
+>4 string __.SYMDEF random library
+
+#
+# From "pdp" (but why a 4-byte quantity?)
+#
+0 lelong 0x39bed PDP-11 old archive
+0 lelong 0x39bee PDP-11 4.0 archive
+
+#
+# XXX - what flavor of APL used this, and was it a variant of
+# some ar archive format? It's similar to, but not the same
+# as, the APL workspace magic numbers in pdp.
+#
+0 long 0100554 apl workspace
+
+#
+# System V Release 1 portable(?) archive format.
+#
+0 string =<ar> System V Release 1 ar archive
+!:mime application/x-archive
+
+#
+# Debian package; it's in the portable archive format, and needs to go
+# before the entry for regular portable archives, as it's recognized as
+# a portable archive whose first member has a name beginning with
+# "debian".
+#
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Deb_(file_format)
+0 string =!<arch>\ndebian
+# https://manpages.debian.org/testing/dpkg/dpkg-split.1.en.html
+>14 string -split part of multipart Debian package
+!:mime application/vnd.debian.binary-package
+# udeb is used for stripped down deb file
+!:ext deb/udeb
+>14 string -binary Debian binary package
+!:mime application/vnd.debian.binary-package
+# For ipk packager see also https://en.wikipedia.org/wiki/Opkg
+!:ext deb/udeb/ipk
+# This should not happen
+>14 default x Unknown Debian package
+# NL terminated version; for most Debian cases this is 2.0 or 2.1 for split
+>68 string >\0 (format %s)
+#>68 string !2.0\n
+#>>68 string x (format %.3s)
+>68 string =2.0\n
+# 2nd archive name=control archive name like control.tar.gz or control.tar.xz
+# or control.tar.zst
+>>72 string >\0 \b, with %.15s
+# look for 3rd archive name=data archive name like data.tar.{gz,xz,bz2,lzma}
+>>0 search/0x93e4f data.tar. \b, data compression
+# the above line only works if FILE_BYTES_MAX in ../../src/file.h is raised
+# for example like libreoffice-dev-doc_1%3a5.2.7-1+rpi1+deb9u3_all.deb
+>>>&0 string x %.2s
+# skip space (0x20 BSD) and slash (0x2f System V) character marking end of name
+>>>&2 ubyte !0x20
+>>>>&-1 ubyte !0x2f
+# display 3rd character of file name extension like 2 of bz2 or m of lzma
+>>>>>&-1 ubyte x \b%c
+>>>>>>&0 ubyte !0x20
+>>>>>>>&-1 ubyte !0x2f
+# display 4th character of file name extension like a of lzma
+>>>>>>>>&-1 ubyte x \b%c
+# split debian package case
+>68 string =2.1\n
+# dpkg-1.18.25/dpkg-split/info.c
+# NL terminated ASCII package name like ckermit
+>>&0 string x \b, %s
+# NL terminated package version like 302-5.3
+>>>&1 string x %s
+# NL terminated MD5 checksum
+>>>>&1 string x \b, MD5 %s
+# NL terminated original package length
+>>>>>&1 string x \b, unsplitted size %s
+# NL terminated part length
+>>>>>>&1 string x \b, part length %s
+# NL terminated package part like n/m
+>>>>>>>&1 string x \b, part %s
+# NL terminated package architecture like armhf since dpkg 1.16.1 or later
+>>>>>>>>&1 string x \b, %s
+
+#
+# MIPS archive; they're in the portable archive format, and need to go
+# before the entry for regular portable archives, as it's recognized as
+# a portable archive whose first member has a name beginning with
+# "__________E".
+#
+0 string =!<arch>\n__________E MIPS archive
+!:mime application/x-archive
+>20 string U with MIPS Ucode members
+>21 string L with MIPSEL members
+>21 string B with MIPSEB members
+>19 string L and an EL hash table
+>19 string B and an EB hash table
+>22 string X -- out of date
+
+#
+# BSD/SVR2-and-later portable archive formats.
+#
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/AR
+# Reference: https://www.unix.com/man-page/opensolaris/3HEAD/ar.h/
+# Note: Mach-O universal binary in ./cafebabe is dependent
+# TODO: unify current ar archive, MIPS archive, Debian package
+# distinguish BSD, SVR; 32, 64 bit; HP from other 32-bit SVR;
+# *.ar packages from *.a libraries. handle empty archive
+0 string =!<arch>\n current ar archive
+# print first and possibly second ar_name[16] for debugging purpose
+#>8 string x \b, 1st "%.16s"
+#>68 string x \b, 2nd "%.16s"
+!:mime application/x-archive
+# a in most case for libraries; lib for Microsoft libraries; ar else cases
+!:ext a/lib/ar
+>8 string __.SYMDEF random library
+# first member with long marked name __.SYMDEF SORTED implies BSD library
+>68 string __.SYMDEF\ SORTED random library
+# Reference: https://parisc.wiki.kernel.org/images-parisc/b/b2/Rad_11_0_32.pdf
+# "archive file" entry moved from ./hp
+# LST header system_id 0210h~PA-RISC 1.1,... identifies the target architecture
+# LST header a_magic 0619h~relocatable library
+>68 belong 0x020b0619 - PA-RISC1.0 relocatable library
+>68 belong 0x02100619 - PA-RISC1.1 relocatable library
+>68 belong 0x02110619 - PA-RISC1.2 relocatable library
+>68 belong 0x02140619 - PA-RISC2.0 relocatable library
+#EOF for common ar archives
+
+#
+# "Thin" archive, as can be produced by GNU ar.
+#
+0 string =!<thin>\n thin archive with
+>68 belong 0 no symbol entries
+>68 belong 1 %d symbol entry
+>68 belong >1 %d symbol entries
+
+0 search/1 -h- Software Tools format archive text
+
+# ARC archiver, from Daniel Quinlan (quinlan@yggdrasil.com)
+#
+# The first byte is the magic (0x1a), byte 2 is the compression type for
+# the first file (0x01 through 0x09), and bytes 3 to 15 are the MS-DOS
+# filename of the first file (null terminated). Since some types collide
+# we only test some types on basis of frequency: 0x08 (83%), 0x09 (5%),
+# 0x02 (5%), 0x03 (3%), 0x04 (2%), 0x06 (2%). 0x01 collides with terminfo.
+0 lelong&0x8080ffff 0x0000081a ARC archive data, dynamic LZW
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000091a ARC archive data, squashed
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000021a ARC archive data, uncompressed
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000031a ARC archive data, packed
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000041a ARC archive data, squeezed
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000061a ARC archive data, crunched
+!:mime application/x-arc
+# [JW] stuff taken from idarc, obviously ARC successors:
+0 lelong&0x8080ffff 0x00000a1a PAK archive data
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000141a ARC+ archive data
+!:mime application/x-arc
+0 lelong&0x8080ffff 0x0000481a HYP archive data
+!:mime application/x-arc
+
+# Acorn archive formats (Disaster prone simpleton, m91dps@ecs.ox.ac.uk)
+# I can't create either SPARK or ArcFS archives so I have not tested this stuff
+# [GRR: the original entries collide with ARC, above; replaced with combined
+# version (not tested)]
+#0 byte 0x1a RISC OS archive (spark format)
+0 string \032archive RISC OS archive (ArcFS format)
+0 string Archive\000 RISC OS archive (ArcFS format)
+
+# All these were taken from idarc, many could not be verified. Unfortunately,
+# there were many low-quality sigs, i.e. easy to trigger false positives.
+# Please notify me of any real-world fishy/ambiguous signatures and I'll try
+# to get my hands on the actual archiver and see if I find something better. [JW]
+# probably many can be enhanced by finding some 0-byte or control char near the start
+
+# idarc calls this Crush/Uncompressed... *shrug*
+0 string CRUSH Crush archive data
+# Squeeze It (.sqz)
+0 string HLSQZ Squeeze It archive data
+# SQWEZ
+0 string SQWEZ SQWEZ archive data
+# HPack (.hpk)
+0 string HPAK HPack archive data
+# HAP
+0 string \x91\x33HF HAP archive data
+# MD/MDCD
+0 string MDmd MDCD archive data
+# LIM
+0 string LIM\x1a LIM archive data
+# SAR
+3 string LH5 SAR archive data
+# BSArc/BS2
+0 string \212\3SB\020\0 BSArc/BS2 archive data
+# Bethesda Softworks Archive (Oblivion)
+0 string BSA\0 BSArc archive data
+>4 lelong x version %d
+# MAR
+2 string =-ah MAR archive data
+# ACB
+#0 belong&0x00f800ff 0x00800000 ACB archive data
+# CPZ
+# TODO, this is what idarc says: 0 string \0\0\0 CPZ archive data
+# JRC
+0 string JRchive JRC archive data
+# Quantum
+0 string DS\0 Quantum archive data
+# ReSOF
+0 string PK\3\6 ReSOF archive data
+# QuArk
+0 string 7\4 QuArk archive data
+# YAC
+14 string YC YAC archive data
+# X1
+0 string X1 X1 archive data
+0 string XhDr X1 archive data
+# CDC Codec (.dqt)
+0 belong&0xffffe000 0x76ff2000 CDC Codec archive data
+# AMGC
+0 string \xad6" AMGC archive data
+# NuLIB
+0 string N\xc3\xb5F\xc3\xa9lx\xc3\xa5 NuLIB archive data
+# PakLeo
+0 string LEOLZW PAKLeo archive data
+# ChArc
+0 string SChF ChArc archive data
+# PSA
+0 string PSA PSA archive data
+# CrossePAC
+0 string DSIGDCC CrossePAC archive data
+# Freeze
+0 string \x1f\x9f\x4a\x10\x0a Freeze archive data
+# KBoom
+0 string \xc2\xa8MP\xc2\xa8 KBoom archive data
+# NSQ, must go after CDC Codec
+0 string \x76\xff NSQ archive data
+# DPA
+0 string Dirk\ Paehl DPA archive data
+# BA
+# TODO: idarc says "bytes 0-2 == bytes 3-5"
+# TTComp
+# URL: http://fileformats.archiveteam.org/wiki/TTComp_archive
+# Update: Joerg Jenderek
+# GRR: line below is too general as it matches also Panorama database "TCDB 2003-10 demo.pan", others
+0 string \0\6
+# look for first keyword of Panorama database *.pan
+>12 search/261 DESIGN
+# skip keyword with low entropy
+>12 default x
+# skip DOS 2.0 backup id file, sequence 6 with many nils like BACKUPID_xx6.@@@ handled by ./msdos
+>>8 quad !0
+>>>0 use ttcomp
+# variant ASCII, 4K dictionary (strength=48=50-2). With strength=49 wrong order! WHY?
+0 string \1\6
+# TODO:
+# skip VAX-order 68k Blit mpx/mux executable (strength=50) handled by ./blit
+!:strength -2
+>0 use ttcomp
+0 string \0\5
+# skip some DOS 2.0 backup id file, sequence 5 with many nils like BACKUPID_075.@@@ handled by ./msdos
+>8 quad !0
+>>0 use ttcomp
+0 string \1\5
+# TODO:
+# variant ASCII, 2K dictionary (strength=48=50-2). With strength=49 wrong order! WHY?
+# skip ctab data (strength=50) handled by ./ibm6000
+# skip locale data table (strength=50) handled by ./digital
+!:strength -2
+>0 use ttcomp
+0 string \0\4
+# skip many Maple help database *.hdb with version tag handled by ./maple
+>1028 string !version
+# skip veclib maple.hdb by looking for Mable keyword
+>>4 search/1091 Maple\040
+#>4 search/34090 Maple\040
+>>4 default x
+# skip DOS 2.0-3.2 backed up sequence 4 with many nils like LOTUS5.RAR handled by ./msdos
+# skip xBASE Compound Index file *.CDX with many nils
+>>>0x54 quad !0
+>>>>0 use ttcomp
+0 string \1\4
+# TODO:
+# skip shared library (strength=50) handled by ./ibm6000
+!:strength -2
+# skip Commodore PET BASIC programs (Mastermind.prg) with last 3 nil bytes (\0~end of line followed by 0000h line offset)
+#>-4 ubelong x LAST_BYTES=%8.8x
+>-4 ubelong&0x00FFffFF !0
+>>0 use ttcomp
+# display information of TTComp archive
+0 name ttcomp
+# (version 5.25) labeled the entry as "TTComp archive data"
+>0 ubyte x TTComp archive data
+!:mime application/x-compress-ttcomp
+# PBACKSCR.PI1
+!:ext $xe/$ts/pi1/__d
+# compression type: 0~binary compression 1~ASCII compression
+>0 ubyte 0 \b, binary
+>0 ubyte 1 \b, ASCII
+# size of the dictionary: 4~1024 bytes 5~2048 bytes 6~4096 bytes
+>1 ubyte 4 \b, 1K
+>1 ubyte 5 \b, 2K
+>1 ubyte 6 \b, 4K
+>1 ubyte x dictionary
+# https://mark0.net/forum/index.php?topic=848
+# last 3 bytes probably have only 8 possible bit sequences
+# xxxxxxxx 0000000x 11111111 ____FFh
+# xxxxxxxx 10000000 01111111 __807Fh
+# 0xxxxxxx 11000000 00111111 __C03Fh
+# 00xxxxxx 11100000 00011111 __E01Fh
+# 000xxxxx 11110000 00001111 __F00Fh
+# 0000xxxx 11111000 00000111 __F807h
+# 00000xxx 11111100 00000011 __FC03h
+# 000000xx 11111110 00000001 __FE01h
+# but for quickgif.__d 0A7DD4h
+#>-3 ubyte x \b, last 3 bytes 0x%2.2x
+#>-2 ubeshort x \b%4.4x
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Disk_Copy
+# reference: http://nulib.com/library/FTN.e00005.htm
+0x52 ubeshort 0x0100
+# test for disk image size equal or above 400k
+>0x40 ubelong >409599
+# test also for disk image size equal or below 1440k to skip
+# windows7en.mbr UNICODE.DAT
+#>>0x40 ubelong <1474561
+# test now for "low" disk image size equal or below 64 MiB to skip
+# windows7en.mbr (B441BBAAh) UNICODE.DAT (0400AF05h)
+>>0x40 ubelong <0x04000001
+# To skip Flags$StringJoiner.class with size 00106A61h test also for valid disk image sizes
+# 00064000 for 400k GCR disks dc42-400k-gcr.trid.xml
+# 000c8000 for 800k GCR disks dc42-800k-gcr.trid.xml
+# 000b4000 for 720k MFM disks dc42-720k-mfm.trid.xml
+# 00168000 for 1440k MFM disks dc42-1440k-mfm.trid.xml
+# https://lisaem.sunder.net/LisaProjectDocs.txt
+# 00500000 05M available
+# 00A00000 10M available
+# 01800000 24M possible
+# 02000000 32M uncertain
+# 04000000 64M uncertain
+>>>0x40 ubelong&0xf8003fFF 0
+# skip samples with invalid disk name length like:
+# 181 (biosmd80.rom) 202 (Flags$StringJoiner.class) 90 (UNICODE.DAT)
+>>>>0x0 ubyte <64
+>>>>>0 use dc42-floppy
+# display information of Apple DiskCopy 4.2 floppy image
+0 name dc42-floppy
+# disk name length; maximal 63
+#>0 ubyte x DISK NAME LENGTH %u
+# ASCII image pascal (maximal 63 bytes) name padded with NULs like:
+# "Microsoft Mail" "Disquette 2" "IIe Installer Disk"
+# "-lisaem.sunder.net hd-" (dc42-lisaem.trid.xml) "-not a Macintosh disk" (dc42-nonmac.trid.xml)
+>00 pstring/B x Apple DiskCopy 4.2 image %s
+#!:mime application/octet-stream
+!:mime application/x-dc42-floppy-image
+!:apple dCpydImg
+# probably also img like: "Utilitaires 2.img" "Installation 7.img"
+!:ext image/dc42/img
+# data size in bytes like: 409600 737280 819200 1474560
+>0x40 ubelong x \b, %u bytes
+# for debugging purpose size in hexadecimal
+#>0x40 ubelong x (%#8.8x)
+# tag size in bytes like: 0 (often) 2580h (PUID fmt/625) 4B00h (Microsoft Mail.image)
+>0x44 ubelong >0 \b, %#x tag size
+# data checksum
+#>0x48 ubelong x \b, %#x checksum
+# tag checksum
+#>0x4c ubelong x \b, %#x tag checksum
+# disk encoding like: 0 1 2 3 (PUID: fmt/625)
+>0x50 ubyte 0 \b, GCR CLV ssdd (400k)
+>0x50 ubyte 1 \b, GCR CLV dsdd (800k)
+>0x50 ubyte 2 \b, MFM CAV dsdd (720k)
+>0x50 ubyte 3 \b, MFM CAV dshd (1440k)
+>0x50 ubyte >3 \b, %#x encoding
+# format byte like: 12h (Lisa 400K) 24h (400K Macintosh) 96h (800K Apple II disk)
+# 2 (Mac 400k "Disquette Installation 13.image")
+# 22h (double-sided MFM or Mac 800k "Disco 12.image" "IIe Installer Disk.image")
+>0x51 ubyte x \b, %#x format
+#>0x54 ubequad x \b, data %#16.16llx
+# ESP, could this conflict with Easy Software Products' (e.g.ESP ghostscript) documentation?
+0 string ESP ESP archive data
+# ZPack
+0 string \1ZPK\1 ZPack archive data
+# Sky
+0 string \xbc\x40 Sky archive data
+# UFA
+0 string UFA UFA archive data
+# Dry
+0 string =-H2O DRY archive data
+# FoxSQZ
+0 string FOXSQZ FoxSQZ archive data
+# AR7
+0 string ,AR7 AR7 archive data
+# PPMZ
+0 string PPMZ PPMZ archive data
+# MS Compress
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/MS-DOS_installation_compression
+# Reference: https://hwiegman.home.xs4all.nl/fileformats/compress/szdd_kwaj_format.html
+# Note: use correct version of extracting tool like EXPAND, UNPACK, DECOMP or 7Z
+4 string \x88\xf0\x27
+# KWAJ variant
+>0 string KWAJ MS Compress archive data, KWAJ variant
+!:mime application/x-ms-compress-kwaj
+# extension not working in version 5.32
+# magic/Magdir/archive, 284: Warning: EXTENSION type ` ??_' has bad char '?'
+# file: line 284: Bad magic entry ' ??_'
+!:ext ??_
+# compression method (0-4)
+>>8 uleshort x \b, %u method
+# offset of compressed data
+>>10 uleshort x \b, %#x offset
+#>>(10.s) uleshort x
+#>>>&-6 string x \b, TEST extension %-.3s
+# header flags to mark header extensions
+>>12 uleshort >0 \b, %#x flags
+# 4 bytes: decompressed length of file
+>>12 uleshort &0x01
+>>>14 ulelong x \b, original size: %u bytes
+# 2 bytes: unknown purpose
+# 2 bytes: length of unknown data + mentioned bytes
+# 1-9 bytes: null-terminated file name
+# 1-4 bytes: null-terminated file extension
+>>12 uleshort &0x08
+>>>12 uleshort ^0x01
+>>>>12 uleshort ^0x02
+>>>>>12 uleshort ^0x04
+>>>>>>12 uleshort ^0x10
+>>>>>>>14 string x \b, %-.8s
+>>>>>>12 uleshort &0x10
+>>>>>>>14 string x \b, %-.8s
+>>>>>>>>&1 string x \b.%-.3s
+>>>>>12 uleshort &0x04
+>>>>>>12 uleshort ^0x10
+>>>>>>>(14.s) uleshort x
+>>>>>>>>&14 string x \b, %-.8s
+>>>>>>12 uleshort &0x10
+>>>>>>>(14.s) uleshort x
+>>>>>>>>&14 string x \b, %-.8s
+>>>>>>>>>&1 string x \b.%-.3s
+>>>>12 uleshort &0x02
+>>>>>12 uleshort ^0x04
+>>>>>>12 uleshort ^0x10
+>>>>>>>16 string x \b, %-.8s
+>>>>>>12 uleshort &0x10
+>>>>>>>16 string x \b, %-.8s
+>>>>>>>>&1 string x \b.%-.3s
+>>>>>12 uleshort &0x04
+>>>>>>12 uleshort ^0x10
+>>>>>>>(16.s) uleshort x
+>>>>>>>>&16 string x \b, %-.8s
+>>>>>>12 uleshort &0x10
+>>>>>>>(16.s) uleshort x
+>>>>>>>&16 string x %-.8s
+>>>>>>>>&1 string x \b.%-.3s
+>>>12 uleshort &0x01
+>>>>12 uleshort ^0x02
+>>>>>12 uleshort ^0x04
+>>>>>>12 uleshort ^0x10
+>>>>>>>18 string x \b, %-.8s
+>>>>>>12 uleshort &0x10
+>>>>>>>18 string x \b, %-.8s
+>>>>>>>>&1 string x \b.%-.3s
+>>>>>12 uleshort &0x04
+>>>>>>12 uleshort ^0x10
+>>>>>>>(18.s) uleshort x
+>>>>>>>>&18 string x \b, %-.8s
+>>>>>>12 uleshort &0x10
+>>>>>>>(18.s) uleshort x
+>>>>>>>>&18 string x \b, %-.8s
+>>>>>>>>>&1 string x \b.%-.3s
+>>>>12 uleshort &0x02
+>>>>>12 uleshort ^0x04
+>>>>>>12 uleshort ^0x10
+>>>>>>>20 string x \b, %-.8s
+>>>>>>12 uleshort &0x10
+>>>>>>>20 string x \b, %-.8s
+>>>>>>>>&1 string x \b.%-.3s
+>>>>>12 uleshort &0x04
+>>>>>>12 uleshort ^0x10
+>>>>>>>(20.s) uleshort x
+>>>>>>>>&20 string x \b, %-.8s
+>>>>>>12 uleshort &0x10
+>>>>>>>(20.s) uleshort x
+>>>>>>>>&20 string x \b, %-.8s
+>>>>>>>>>&1 string x \b.%-.3s
+# 2 bytes: length of data + mentioned bytes
+#
+# SZDD variant Haruhiko Okumura's LZSS or 7z type MsLZ
+# URL: http://fileformats.archiveteam.org/wiki/MS-DOS_installation_compression
+# Reference: http://www.cabextract.org.uk/libmspack/doc/szdd_kwaj_format.html
+# http://mark0.net/download/triddefs_xml.7z/defs/s/szdd.trid.xml
+# Note: called "Microsoft SZDD compressed (Haruhiko Okumura's LZSS)" by TrID
+# verfied by 7-Zip `7z l -tMsLZ -slt *.??_` as MsLZ
+# `deark -l -m lzss_oku -d2 setup-1-41.bin` as "LZSS.C by Haruhiko Okumura"
+>0 string SZDD MS Compress archive data, SZDD variant
+# 2nd part of signature
+#>>4 ubelong 0x88F02733 \b, SIGNATURE OK
+!:mime application/x-ms-compress-szdd
+!:ext ??_
+# The character missing from the end of the filename (0=unknown)
+>>9 string >\0 \b, %-.1s is last character of original name
+# https://www.betaarchive.com/forum/viewtopic.php?t=26161
+# Compression mode: "A" (0x41) found but sometimes "B" in Windows 3.1 builds 026 and 034e
+>>8 string !A \b, %-.1s method
+>>10 ulelong >0 \b, original size: %u bytes
+# Summary: InstallShield archive with SZDD compressed
+# URL: https://community.flexera.com/t5/InstallShield-Knowledge-Base/InstallShield-Redistributable-Files/ta-p/5647
+# From: Joerg Jenderek
+1 search/48/bs SZDD\x88\xF0\x27\x33 InstallShield archive
+#!:mime application/octet-stream
+!:mime application/x-installshield-compress-szdd
+!:ext ibt
+# name of compressed archive member like: setup.dl_ _setup7int.dl_ _setup2k.dl_ _igdi.dl_ cabinet.dl_
+>0 string x %s
+# name of uncompressed archive member like: setup.dll _Setup.dll IGdi.dll CABINET.DLL
+>>&1 string x (%s)
+# probably version like: 9.0.0.333 9.1.0.429 11.50.0.42618
+>>>&1 string x \b, version %s
+# SZDD member length like: 168048 169333 181842
+>>>>&1 string x \b, %s bytes
+# MS Compress archive data
+#>&0 string SZDD \b, SIGNATURE FOUND
+>&0 indirect x
+# QBasic SZDD variant
+3 string \x88\xf0\x27
+>0 string SZ\x20 MS Compress archive data, QBasic variant
+!:mime application/x-ms-compress-sz
+!:ext ??$
+>>8 ulelong >0 \b, original size: %u bytes
+
+# Summary: lzss compressed/EDI Pack
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/EDI_Install_packed_file
+# Note: called "EDI Install LZS compressed data" by TrID and verified by
+# command like `deark -l -m edi_pack -d2 BOOK01A.IC$` as "EDI Pack LZSS1"
+0 string EDILZSS
+>7 string 1
+# look for point character before orginal file name extension
+>>8 search/9/b .
+# check suffix of possible orginal file anme
+#>>>&0 ubelong x SUFFIX=%8.8x
+# samples without valid character after point in original file name field like: FENNEL.LZS PLANTAIN.LZS
+>>>&0 ubyte <0x20
+>>>>0 use edi-lzs
+# samples with valid character after point in original file name field
+>>>&0 ubyte >0x1F
+# check 2nd charcter of suffix
+#>>>>&0 ubyte x 2ND_SUFFIX=%x
+# sample with one valid character after point followed by \0 in original file name field like: SPELMATE.H$
+>>>>&0 ubyte =0
+>>>>>0 use edi-pack
+>>>>&0 ubyte >0x1F
+# check 3rd charcter of suffix
+#>>>>>&0 ubyte x 3RD_SUFFIX=%x
+# no sample with 2 valid characters after point followed by \0 in original file name field
+>>>>>&0 ubyte =0
+>>>>>>0 use edi-pack
+# samples with valid 3rd character after point in original file name field
+>>>>>&0 ubyte >0x1F
+# sample with 3 valid character after point followed by \0 in original file name field like: BOOK01A.IC$ CTL3D.DL$
+>>>>>>&0 ubyte =0
+>>>>>>>0 use edi-pack
+# sample with 3 valid character after point followed by no \0 in original file name field like: HERBTEXT.LZS
+>>>>>>&0 ubyte !0
+>>>>>>>0 use edi-lzs
+# no sample with invalid 3rd character after point in original file name field
+>>>>>&0 default x
+>>>>>>0 use edi-lzs
+# sample with invalid 2nd character after point in original file name field like: LACERATE.LZS SPLINTER.LZS
+>>>>&0 default x
+>>>>>0 use edi-lzs
+# sample without point character in original file name field like GUNSHOT.LZS
+>>8 default x
+>>>0 use edi-lzs
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/e/edi-lzss2.trid.xml
+# Note: called "EDI Install Pro LZSS2 compressed data" by TrID and verified by
+# command like `deark -l -m edi_pack -d2 4WAY.WA$` as "EDI Pack LZSS2"
+>7 string 2 EDI LZSS2 packed
+#!:mime application/octet-stream
+!:mime application/x-edi-pack-lzss
+# the name of a compressed file often ends in character '$' or '_'
+!:ext ??$/??_
+# original filename, NUL-terminated, padded to 13 bytes like: mci.vbx 4way.wav skymap.exe cmdialog.vbx
+>>8 string x "%-0.13s"
+# original file size, as a 4-byte integer.
+>>21 ulelong x \b, %u bytes
+# compressed data like: ff5249464606ec00 ff4d5aa601010000
+>>>25 ubequad x \b, data %#16.16llx...
+0 name edi-pack
+# Note: verified by command like `deark -l -d2 SPELMATE.H$` as "EDI Pack LZSS1"
+# original filename, NUL-terminated, padded to 13 bytes like: ctl3d.dll spelmate.h filemenu.rc owl.def index-it.exe
+# but not like \377Aloe.lzs\273 (HERBTEXT.LZS)
+>8 string x EDI LZSS packed "%-.13s"
+#!:mime application/octet-stream
+!:mime application/x-edi-pack-lzss
+# the name of a compressed file often ends in character '$' or '_'
+!:ext ??$/?$
+# compressed data like: f7000001eff02020 ff4d5aa900020000 ff2f2a207370656c
+>21 ubequad x \b, data %#16.16llx...
+# URL: http://fileformats.archiveteam.org/wiki/EDI_LZSSLib
+# Note: verified partly by command like `deark -l -m edi_pack -d2 GUNSHOT.LZS` as "EDI LZSSLib"
+0 name edi-lzs
+# Note: verified by command like `deark -l -d2 GUNSHOT.LZS` as "EDI LZSSLib"
+# no original filename looks like: \277BM\226.\0 \277BM.n\001 \277BM\226.\0 \277BM.g\001 \377Aloe.lzs\273
+>8 string x EDI LZSSLib packed
+#!:mime application/octet-stream
+!:mime application/x-edi-pack-lzss
+# The name of a compressed file ends with LZS suffix
+!:ext lzs
+# compressed data like: bf424df6e10100f3 ff416c6f652e6c7a ff416c6f652e6c7a
+>8 ubequad x \b, data %#16.16llx...
+
+# Summary: CAZIP compressed file
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/CAZIP
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/caz.trid.xml
+# Note: Format is distinct from CAZIPXP compressed
+0 string \x0D\x0A\x1ACAZIP CAZIP compressed file
+#!:mime application/octet-stream
+!:mime application/x-compress-cazip
+# like: BLINKER.WR_ CLIPDEFS._ CAOSETUP.EX_ CLIPPER.EX_ FILEIO.C_
+!:ext ??_/?_/_
+
+# Summary: FTCOMP compressed archive
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/FTCOMP
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-ftcomp.trid.xml
+# Note: called by TrID "FTCOMP compressed archive"
+# extracted by `unpack seahelp.hl_`
+24 string/b FTCOMP FTCOMP compressed archive
+#!:mime application/octet-stream
+!:mime application/x-compress-ftcomp
+!:ext ??_/??@/dll/drv/pk2/
+# probably A596FDFF magic at the beginning
+>0 ubelong !0xA596FDFF \b, at beginning %#x
+# probably original file name with directory like: \OS2\unpack.exe \SYSTEM\8514.DRV MAHJONGG.EXE
+>41 string x "%s"
+
+# MP3 (archiver, not lossy audio compression)
+0 string MP3\x1a MP3-Archiver archive data
+# ZET
+0 string OZ\xc3\x9d ZET archive data
+# TSComp
+0 string \x65\x5d\x13\x8c\x08\x01\x03\x00 TSComp archive data
+# ARQ
+0 string gW\4\1 ARQ archive data
+# Squash
+3 string OctSqu Squash archive data
+# Terse
+0 string \5\1\1\0 Terse archive data
+# UHarc
+0 string UHA UHarc archive data
+# ABComp
+0 string \2AB ABComp archive data
+0 string \3AB2 ABComp archive data
+# CMP
+0 string CO\0 CMP archive data
+# Splint
+0 string \x93\xb9\x06 Splint archive data
+# InstallShield
+0 string \x13\x5d\x65\x8c InstallShield Z archive Data
+# Gather
+1 string GTH Gather archive data
+# BOA
+0 string BOA BOA archive data
+# RAX
+0 string ULEB\xa RAX archive data
+# Xtreme
+0 string ULEB\0 Xtreme archive data
+# Pack Magic
+0 string @\xc3\xa2\1\0 Pack Magic archive data
+# BTS
+0 belong&0xfeffffff 0x1a034465 BTS archive data
+# ELI 5750
+0 string Ora\ ELI 5750 archive data
+# QFC
+0 string \x1aFC\x1a QFC archive data
+0 string \x1aQF\x1a QFC archive data
+# PRO-PACK https://www.segaretro.org/Rob_Northen_compression
+0 string RNC
+>3 byte 1 PRO-PACK archive data (compression 1)
+>3 byte 2 PRO-PACK archive data (compression 2)
+# 777
+0 string 777 777 archive data
+# LZS221
+0 string sTaC LZS221 archive data
+# HPA
+0 string HPA HPA archive data
+# Arhangel
+0 string LG Arhangel archive data
+# EXP1, uses bzip2
+0 string 0123456789012345BZh EXP1 archive data
+# IMP
+0 string IMP\xa IMP archive data
+# NRV
+0 string \x00\x9E\x6E\x72\x76\xFF NRV archive data
+# Squish
+0 string \x73\xb2\x90\xf4 Squish archive data
+# Par
+0 string PHILIPP Par archive data
+0 string PAR Par archive data
+# HIT
+0 string UB HIT archive data
+# SBX
+0 belong&0xfffff000 0x53423000 SBX archive data
+# NaShrink
+0 string NSK NaShrink archive data
+# SAPCAR
+0 string #\ CAR\ archive\ header SAPCAR archive data
+0 string CAR\ 2.00 SAPCAR archive data
+0 string CAR\ 2.01 SAPCAR archive data
+#!:mime application/octet-stream
+!:mime application/vnd.sar
+!:ext sar
+# Disintegrator
+0 string DST Disintegrator archive data
+# ASD
+0 string ASD ASD archive data
+# InstallShield CAB
+# Update: Joerg Jenderek at Nov 2021
+# URL: https://en.wikipedia.org/wiki/InstallShield
+# Reference: https://github.com/twogood/unshield/blob/master/lib/cabfile.h
+# Note: Not compatible with Microsoft CAB files
+# http://mark0.net/download/triddefs_xml.7z/defs/a/ark-cab-ishield.trid.xml
+# CAB_SIGNATURE 0x28635349
+0 string ISc( InstallShield
+#!:mime application/octet-stream
+!:mime application/x-installshield
+# http://mark0.net/download/triddefs_xml.7z/defs/a/ark-cab-ishield-hdr.trid.xml
+>16 ulelong !0 setup header
+# like: _SYS1.HDR _USER1.HDR data1.hdr
+!:ext hdr
+>16 ulelong =0 CAB
+# like: _SYS1.CAB _USER1.CAB DATA1.CAB data2.cab
+!:ext cab
+# https://github.com/twogood/unshield/blob/master/lib/helper.c
+# version like: 0x1005201 0x100600c 0x1007000 0x1009500
+# 0x2000578 0x20005dc 0x2000640 0x40007d0 0x4000834
+>4 ulelong x \b, version %#x
+# volume_info like: 0
+>8 ulelong !0 \b, volume_info %#x
+# cab_descriptor_offset like: 0x200
+>12 ulelong !0x200 \b, offset %#x
+#>0x200 ubequad x \b, at 0x200 %#16.16llx
+# cab_descriptor_size like: 0 (*.cab) BD5 C8B DA5 E2A E36 116C 251D 4DA9 56F0 5CC2 6E4B 777D 779E 1F7C2
+>16 ulelong !0 \b, descriptor size %#x
+# TOP4
+0 string T4\x1a TOP4 archive data
+# BatComp left out: sig looks like COM executable
+# so TODO: get real 4dos batcomp file and find sig
+# BlakHole
+0 string BH\5\7 BlakHole archive data
+# BIX
+0 string BIX0 BIX archive data
+# ChiefLZA
+0 string ChfLZ ChiefLZA archive data
+# Blink
+0 string Blink Blink archive data
+# Logitech Compress
+0 string \xda\xfa Logitech Compress archive data
+# ARS-Sfx (FIXME: really a SFX? then goto COM/EXE)
+1 string (C)\ STEPANYUK ARS-Sfx archive data
+# AKT/AKT32
+0 string AKT32 AKT32 archive data
+0 string AKT AKT archive data
+# NPack
+0 string MSTSM NPack archive data
+# PFT
+0 string \0\x50\0\x14 PFT archive data
+# SemOne
+0 string SEM SemOne archive data
+# PPMD
+0 string \x8f\xaf\xac\x84 PPMD archive data
+# FIZ
+0 string FIZ FIZ archive data
+# MSXiE
+0 belong&0xfffff0f0 0x4d530000 MSXiE archive data
+# DeepFreezer
+0 belong&0xfffffff0 0x797a3030 DeepFreezer archive data
+# DC
+0 string =<DC- DC archive data
+# TPac
+0 string \4TPAC\3 TPac archive data
+# Ai
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Ai_Archiver
+0 string Ai\1\1\0 Ai archive data
+#!:mime application/octet-stream
+!:mime application/x-compress-ai
+!:ext ai
+0 string Ai\1\0\0 Ai archive data
+#!:mime application/octet-stream
+!:mime application/x-compress-ai
+!:ext ai
+# Ai32
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-ai.trid.xml
+# Note: called "Ai Archivator compressed archive" by TrID
+0 string Ai\2\0 Ai32 archive data
+#!:mime application/octet-stream
+!:mime application/x-compress-ai
+!:ext ai
+# original file name
+>8 pstring/h x "%s"
+# according to TrID the next 3 bytes are nil
+>5 ubyte !0 \b, at 5 %#x
+>6 ubyte !0 \b, at 6 %#x
+>7 ubyte !0 \b, at 7 %#x
+# the fourth byte with value 0 is probably a flag for "non solid" mode
+#>3 ubyte =0x00 \b, unsolid mode
+0 string Ai\2\1 Ai32 archive data
+#!:mime application/octet-stream
+!:mime application/x-compress-ai
+!:ext ai
+# original file name
+>8 pstring/h x "%s"
+# the fourth byte with value 0x01 is probably a flag for "solid" mode; this is not the default
+>3 ubyte =0x01 \b, solid mode
+# SBC
+0 string SBC SBC archive data
+# Ybs
+0 string YBS Ybs archive data
+# DitPack
+0 string \x9e\0\0 DitPack archive data
+# DMS
+0 string DMS! DMS archive data
+# EPC
+0 string \x8f\xaf\xac\x8c EPC archive data
+# VSARC
+0 string VS\x1a VSARC archive data
+# PDZ
+0 string PDZ PDZ archive data
+# ReDuq
+0 string rdqx ReDuq archive data
+# GCA
+0 string GCAX GCA archive data
+# PPMN
+0 string pN PPMN archive data
+# WinImage
+3 string WINIMAGE WinImage archive data
+# Compressia
+0 string CMP0CMP Compressia archive data
+# UHBC
+0 string UHB UHBC archive data
+# WinHKI
+0 string \x61\x5C\x04\x05 WinHKI archive data
+# WWPack data file
+0 string WWP WWPack archive data
+# BSN (BSA, PTS-DOS)
+0 string \xffBSG BSN archive data
+1 string \xffBSG BSN archive data
+3 string \xffBSG BSN archive data
+1 string \0\xae\2 BSN archive data
+1 string \0\xae\3 BSN archive data
+1 string \0\xae\7 BSN archive data
+# AIN
+0 string \x33\x18 AIN archive data
+0 string \x33\x17 AIN archive data
+# XPA32 test moved and merged with XPA by Joerg Jenderek at Sep 2015
+# SZip (TODO: doesn't catch all versions)
+0 string SZ\x0a\4 SZip archive data
+# XPack DiskImage
+# *.XDI updated by Joerg Jenderek Sep 2015
+# ftp://ftp.sac.sk/pub/sac/pack/0index.txt
+# GRR: this test is still too general as it catches also text files starting with jm
+0 string jm
+# only found examples with this additional characteristic 2 bytes
+>2 string \x2\x4 Xpack DiskImage archive data
+#!:ext xdi
+# XPack Data
+# *.xpa updated by Joerg Jenderek Sep 2015
+# ftp://ftp.elf.stuba.sk/pub/pc/pack/
+0 string xpa XPA
+!:ext xpa
+# XPA32
+# ftp://ftp.elf.stuba.sk/pub/pc/pack/xpa32.zip
+# created by XPA32.EXE version 1.0.2 for Windows
+>0 string xpa\0\1 \b32 archive data
+# created by XPACK.COM version 1.67m or 1.67r with short 0x1800
+>3 ubeshort !0x0001 \bck archive data
+# XPack Single Data
+# changed by Joerg Jenderek Sep 2015 back to like in version 5.12
+# letter 'I'+ acute accent is equivalent to \xcd
+0 string \xcd\ jm Xpack single archive data
+#!:mime application/x-xpa-compressed
+!:ext xpa
+
+# TODO: missing due to unknown magic/magic at end of file:
+#DWC
+#ARG
+#ZAR
+#PC/3270
+#InstallIt
+#RKive
+#RK
+#XPack Diskimage
+
+# These were inspired by idarc, but actually verified
+# Dzip archiver (.dz)
+# Update: Joerg Jenderek
+# URL: http://speeddemosarchive.com/dzip/
+# reference: http://speeddemosarchive.com/dzip/dz29src.zip/main.c
+# GRR: line below is too general as it matches also ASCII texts like Doszip commander help dz.txt
+0 string DZ
+# latest version is 2.9 dated 7 may 2003
+>2 byte <4 Dzip archive data
+!:mime application/x-dzip
+!:ext dz
+>>2 byte x \b, version %i
+>>3 byte x \b.%i
+>>4 ulelong x \b, offset %#x
+>>8 ulelong x \b, %u files
+# ZZip archiver (.zz)
+0 string ZZ\ \0\0 ZZip archive data
+0 string ZZ0 ZZip archive data
+# PAQ archiver (.paq)
+0 string \xaa\x40\x5f\x77\x1f\xe5\x82\x0d PAQ archive data
+0 string PAQ PAQ archive data
+>3 byte&0xf0 0x30
+>>3 byte x (v%c)
+# JAR archiver (.j), this is the successor to ARJ, not Java's JAR (which is essentially ZIP)
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/JAR_(ARJ_Software)
+# reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-jar.trid.xml
+# https://www.sac.sk/download/pack/jar102x.exe/TECHNOTE.DOC
+# Note: called "JAR compressed archive" by TrID
+0xe string \x1aJar\x1b JAR (ARJ Software, Inc.) archive data
+#!:mime application/octet-stream
+!:mime application/x-compress-j
+>0 ulelong x \b, CRC32 %#x
+# standard suffix is ".j"; for multi volumes following order j01 j02 ... j99 100 ... 990
+!:ext j/j01/j02
+# URL: http://fileformats.archiveteam.org/wiki/JARCS
+# reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-jarcs.trid.xml
+# Note: called "JARCS compressed archive" by TrID
+0 string JARCS JAR (ARJ Software, Inc.) archive data
+#!:mime application/octet-stream
+!:mime application/x-compress-jar
+!:ext jar
+
+# ARJ archiver (jason@jarthur.Claremont.EDU)
+# URL: http://fileformats.archiveteam.org/wiki/ARJ
+# reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-arj.trid.xml
+# https://github.com/FarGroup/FarManager/
+# blob/master/plugins/multiarc/arc.doc/arj.txt
+# Note: called "ARJ compressed archive" by TrID and
+# "ARJ File Format" by DROID via PUID fmt/610
+# verified by `7z l -tarj PHRACK1.ARJ` and
+# `arj.exe l TEST-hk9.ARJ`
+0 leshort 0xea60
+# skip DROID fmt-610-signature-id-946.arj by check for valid file type of main header
+>0xA ubyte 2
+>>0 use arj-archive
+0 name arj-archive
+>0 leshort x ARJ archive
+!:mime application/x-arj
+# look for terminating 0-character of filename
+>0x26 search/1024 \0
+# file name extension is normally .arj but not for parts of multi volume
+#>>&-5 string x extension %.4s
+>>&-5 string/c .arj data
+!:ext arj
+>>&-5 default x
+# for multi volume first name is archive.arj then following parts archive.a01 archive.a02 ...
+>>>8 byte &0x04 data
+!:ext a01/a02
+# for SFX first name is archive.exe then following parts archive.e01 archive.e02 ...
+>>>8 byte ^0x04 data, SFX multi-volume
+!:ext e01/e02
+# basic header size like: 0x002b 0x002c 0x04e0 0x04e3 0x04e7
+#>2 uleshort x basic header size %#4.4x
+# next fragment content like: 0x0a200a003a8fc713 0x524a000010bb3471 0x524a0000c73c70f9
+#>(2.s) ubequad x NEXT FRAGMENT CONTENT %#16.16llx
+# first_hdr_size; seems to be same as basic header size
+#>2 uleshort x 1st header size %#x
+# archiver version number like: 3 4 6 11 102
+>5 byte x \b, v%d
+# minimum archiver version to extract like: 1
+>6 ubyte !1 \b, minimum %u to extract
+# FOR DEBUGGING
+#>8 byte x \b, FLAGS %#x
+# GARBLED_FLAG1; garble with password; g switch
+>8 byte &0x01 \b, password-protected
+# encryption version: 0~old 1~old 2~new 3~reserved 4~40 bit key GOST
+>>0x20 ubyte x (v%u)
+#>8 byte &0x02 \b, secured
+# ANSIPAGE_FLAG; indicates ANSI codepage used by ARJ32; hy switch
+>8 byte &0x02 \b, ANSI codepage
+# VOLUME_FLAG indicates presence of succeeding volume; but apparently not for SFX
+>8 byte &0x04 \b, multi-volume
+#>8 byte &0x08 \b, file-offset
+# ARJPROT_FLAG; build with data protection record; hk switch
+>8 byte &0x08 \b, recoverable
+# arj protection factor; maximal 10; switch hky -> factor=y+1
+>>0x22 byte x (factor %u)
+>8 byte &0x10 \b, slash-switched
+# BACKUP_FLAG; obsolete
+>8 byte &0x20 \b, backup
+# SECURED_FLAG;
+>8 byte &0x40 \b, secured,
+# ALTNAME_FLAG; indicates dual-name archive
+>8 byte &0x80 \b, dual-name
+# security version; 0~old 2~current
+>9 ubyte !0
+>>9 ubyte !2 \b, security version %u
+# file type; 2 in main header; 0~binary 1~7-bitText 2~comment 3~directory 4~VolumeLabel 5=ChapterLabel
+>0xA ubyte !2 \b, file type %u
+# date+time when original archive was created in MS-DOS format via ./msdos
+>0xC ulelong x \b, created
+>0xC use dos-date
+# or date and time by new internal function
+#>0xE lemsdosdate x %s
+#>0xC lemsdostime x %s
+# FOR DEBUGGING
+#>0x12 uleshort x RAW DATE %#4.4x
+#>0x10 uleshort x RAW TIME %#4.4x
+# date+time when archive was last modified; sometimes nil or
+# maybe wrong like in HP4DRVR.ARJ
+#>0x10 ulelong >0 \b, modified
+#>>0x10 use dos-date
+# or date and time by new internal function
+#>>0x12 lemsdosdate x %s
+#>>0x10 lemsdostime x %s
+# archive size (currently used only for secured archives); MAYBE?
+#>0x14 ulelong !0 \b, file size %u
+# security envelope file position; MAYBE?
+#>0x18 ulelong !0 \b, at %#x security envelope
+# filespec position in filename; WHAT IS THAT?
+#>0x1C uleshort >0 \b, filespec position %#x
+# length in bytes of security envelope data like: 2CAh 301h 364h 471h
+>0x1E uleshort !0 \b, security envelope length %#x
+# last chapter like: 0 1
+>0x21 ubyte !0 \b, last chapter %u
+# filename (null-terminated string); sometimes at 0x26 when 4 bytes for extra data
+>34 byte x \b, original name:
+# with extras data
+>34 byte <0x0B
+>>38 string x %s
+# without extras data
+>34 byte >0x0A
+>>34 string x %s
+# host OS: 0~MSDOS ... 11~WIN32
+>7 byte 0 \b, os: MS-DOS
+>7 byte 1 \b, os: PRIMOS
+>7 byte 2 \b, os: Unix
+>7 byte 3 \b, os: Amiga
+>7 byte 4 \b, os: Macintosh
+>7 byte 5 \b, os: OS/2
+>7 byte 6 \b, os: Apple ][ GS
+>7 byte 7 \b, os: Atari ST
+>7 byte 8 \b, os: NeXT
+>7 byte 9 \b, os: VAX/VMS
+>7 byte 10 \b, os: WIN95
+>7 byte 11 \b, os: WIN32
+# [JW] idarc says this is also possible
+2 leshort 0xea60 ARJ archive data
+#2 leshort 0xea60
+#>2 use arj-archive
+
+# HA archiver (Greg Roelofs, newt@uchicago.edu)
+# This is a really bad format. A file containing HAWAII will match this...
+#0 string HA HA archive data,
+#>2 leshort =1 1 file,
+#>2 leshort >1 %hu files,
+#>4 byte&0x0f =0 first is type CPY
+#>4 byte&0x0f =1 first is type ASC
+#>4 byte&0x0f =2 first is type HSC
+#>4 byte&0x0f =0x0e first is type DIR
+#>4 byte&0x0f =0x0f first is type SPECIAL
+# suggestion: at least identify small archives (<1024 files)
+0 belong&0xffff00fc 0x48410000 HA archive data
+>2 leshort =1 1 file,
+>2 leshort >1 %u files,
+>4 byte&0x0f =0 first is type CPY
+>4 byte&0x0f =1 first is type ASC
+>4 byte&0x0f =2 first is type HSC
+>4 byte&0x0f =0x0e first is type DIR
+>4 byte&0x0f =0x0f first is type SPECIAL
+
+# HPACK archiver (Peter Gutmann, pgut1@cs.aukuni.ac.nz)
+0 string HPAK HPACK archive data
+
+# JAM Archive volume format, by Dmitry.Kohmanyuk@UA.net
+0 string \351,\001JAM\ JAM archive,
+>7 string >\0 version %.4s
+>0x26 byte =0x27 -
+>>0x2b string >\0 label %.11s,
+>>0x27 lelong x serial %08x,
+>>0x36 string >\0 fstype %.8s
+
+# LHARC/LHA archiver (Greg Roelofs, newt@uchicago.edu)
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/LHA_(file_format)
+# Reference: https://web.archive.org/web/20021005080911/http://www.osirusoft.com/joejared/lzhformat.html
+#
+# check and display information of lharc (LHa,PMarc) file
+0 name lharc-file
+# check 1st character of method id like -lz4- -lh5- or -pm2-
+>2 string -
+# check 5th character of method id
+>>6 string -
+# check header level 0 1 2 3
+>>>20 ubyte <4
+# check 2nd, 3th and 4th character of method id
+>>>>3 regex \^(lh[0-9a-ex]|lz[s2-8]|pm[012]|pc1) \b
+!:mime application/x-lzh-compressed
+# creator type "LHA "
+!:apple ????LHA
+# display archive type name like "LHa/LZS archive data" or "LArc archive"
+>>>>>2 string -lz \b
+!:ext lzs
+# already known -lzs- -lz4- -lz5- with old names
+>>>>>>2 string -lzs LHa/LZS archive data
+>>>>>>3 regex \^lz[45] LHarc 1.x archive data
+# missing -lz?- with wikipedia names
+>>>>>>3 regex \^lz[2378] LArc archive
+# display archive type name like "LHa (2.x) archive data"
+>>>>>2 string -lh \b
+# already known -lh0- -lh1- -lh2- -lh3- -lh4- -lh5- -lh6- -lh7- -lhd- variants with old names
+>>>>>>3 regex \^lh[01] LHarc 1.x/ARX archive data
+# LHice archiver use ".ICE" as name extension instead usual one ".lzh"
+# FOOBAR archiver use ".foo" as name extension instead usual one
+# "Florian Orjanov's and Olga Bachetska's ARchiver" not found at the moment
+>>>>>>>2 string -lh1 \b
+!:ext lha/lzh/ice
+>>>>>>3 regex \^lh[23d] LHa 2.x? archive data
+>>>>>>3 regex \^lh[7] LHa (2.x)/LHark archive data
+>>>>>>3 regex \^lh[456] LHa (2.x) archive data
+>>>>>>>2 string -lh5 \b
+# https://en.wikipedia.org/wiki/BIOS
+# Some mainboard BIOS like Award use LHa compression. So archives with unusual extension are found like
+# bios.rom , kd7_v14.bin, 1010.004, ...
+!:ext lha/lzh/rom/bin
+# missing -lh?- variants (Joe Jared)
+>>>>>>3 regex \^lh[89a-ce] LHa (Joe Jared) archive
+# UNLHA32 2.67a
+>>>>>>2 string -lhx LHa (UNLHA32) archive
+# lha archives with standard file name extensions ".lha" ".lzh"
+>>>>>>3 regex !\^(lh1|lh5) \b
+!:ext lha/lzh
+# this should not happen if all -lh variants are described
+>>>>>>2 default x LHa (unknown) archive
+#!:ext lha
+# PMarc
+>>>>>3 regex \^pm[012] PMarc archive data
+!:ext pma
+# append method id without leading and trailing minus character
+>>>>>3 string x [%3.3s]
+>>>>>>0 use lharc-header
+#
+# check and display information of lharc header
+0 name lharc-header
+# header size 0x4 , 0x1b-0x61
+>0 ubyte x
+# compressed data size != compressed file size
+#>7 ulelong x \b, data size %d
+# attribute: 0x2~?? 0x10~symlink|target 0x20~normal
+#>19 ubyte x \b, 19_%#x
+# level identifier 0 1 2 3
+#>20 ubyte x \b, level %d
+# time stamp
+#>15 ubelong x DATE %#8.8x
+# OS ID for level 1
+>20 ubyte 1
+# 0x20 types find for *.rom files
+>>(21.b+24) ubyte <0x21 \b, %#x OS
+# ascii type like M for MSDOS
+>>(21.b+24) ubyte >0x20 \b, '%c' OS
+# OS ID for level 2
+>20 ubyte 2
+#>>23 ubyte x \b, OS ID %#x
+>>23 ubyte <0x21 \b, %#x OS
+>>23 ubyte >0x20 \b, '%c' OS
+# filename only for level 0 and 1
+>20 ubyte <2
+# length of filename
+>>21 ubyte >0 \b, with
+# filename
+>>>21 pstring x "%s"
+#
+#2 string -lh0- LHarc 1.x/ARX archive data [lh0]
+#!:mime application/x-lharc
+2 string -lh0-
+>0 use lharc-file
+#2 string -lh1- LHarc 1.x/ARX archive data [lh1]
+#!:mime application/x-lharc
+2 string -lh1-
+>0 use lharc-file
+# NEW -lz2- ... -lz8-
+2 string -lz2-
+>0 use lharc-file
+2 string -lz3-
+>0 use lharc-file
+2 string -lz4-
+>0 use lharc-file
+2 string -lz5-
+>0 use lharc-file
+2 string -lz7-
+>0 use lharc-file
+2 string -lz8-
+>0 use lharc-file
+# [never seen any but the last; -lh4- reported in comp.compression:]
+#2 string -lzs- LHa/LZS archive data [lzs]
+2 string -lzs-
+>0 use lharc-file
+# According to wikipedia and others such a version does not exist
+#2 string -lh\40- LHa 2.x? archive data [lh ]
+#2 string -lhd- LHa 2.x? archive data [lhd]
+2 string -lhd-
+>0 use lharc-file
+#2 string -lh2- LHa 2.x? archive data [lh2]
+2 string -lh2-
+>0 use lharc-file
+#2 string -lh3- LHa 2.x? archive data [lh3]
+2 string -lh3-
+>0 use lharc-file
+#2 string -lh4- LHa (2.x) archive data [lh4]
+2 string -lh4-
+>0 use lharc-file
+#2 string -lh5- LHa (2.x) archive data [lh5]
+2 string -lh5-
+>0 use lharc-file
+#2 string -lh6- LHa (2.x) archive data [lh6]
+2 string -lh6-
+>0 use lharc-file
+#2 string -lh7- LHa (2.x)/LHark archive data [lh7]
+2 string -lh7-
+# !:mime application/x-lha
+# >20 byte x - header level %d
+>0 use lharc-file
+# NEW -lh8- ... -lhe- , -lhx-
+2 string -lh8-
+>0 use lharc-file
+2 string -lh9-
+>0 use lharc-file
+2 string -lha-
+>0 use lharc-file
+2 string -lhb-
+>0 use lharc-file
+2 string -lhc-
+>0 use lharc-file
+2 string -lhe-
+>0 use lharc-file
+2 string -lhx-
+>0 use lharc-file
+# taken from idarc [JW]
+2 string -lZ PUT archive data
+# already done by LHarc magics
+# this should never happen if all sub types of LZS archive are identified
+#2 string -lz LZS archive data
+2 string -sw1- Swag archive data
+
+0 name rar-file-header
+>24 byte 15 \b, v1.5
+>24 byte 20 \b, v2.0
+>24 byte 29 \b, v4
+>15 byte 0 \b, os: MS-DOS
+>15 byte 1 \b, os: OS/2
+>15 byte 2 \b, os: Win32
+>15 byte 3 \b, os: Unix
+>15 byte 4 \b, os: Mac OS
+>15 byte 5 \b, os: BeOS
+
+0 name rar-archive-header
+>3 leshort&0x1ff >0 \b, flags:
+>>3 leshort &0x01 ArchiveVolume
+>>3 leshort &0x02 Commented
+>>3 leshort &0x04 Locked
+>>3 leshort &0x10 NewVolumeNaming
+>>3 leshort &0x08 Solid
+>>3 leshort &0x20 Authenticated
+>>3 leshort &0x40 RecoveryRecordPresent
+>>3 leshort &0x80 EncryptedBlockHeader
+>>3 leshort &0x100 FirstVolume
+
+# RAR (Roshal Archive) archive
+0 string Rar!\x1a\7\0 RAR archive data
+!:mime application/x-rar
+!:ext rar/cbr
+# file header
+>(0xc.l+9) byte 0x74
+>>(0xc.l+7) use rar-file-header
+# subblock seems to share information with file header
+>(0xc.l+9) byte 0x7a
+>>(0xc.l+7) use rar-file-header
+>9 byte 0x73
+>>7 use rar-archive-header
+
+0 string Rar!\x1a\7\1\0 RAR archive data, v5
+!:mime application/x-rar
+!:ext rar
+
+# Very old RAR archive
+# https://jasonblanks.com/wp-includes/images/papers/KnowyourarchiveRAR.pdf
+0 string RE\x7e\x5e RAR archive data (<v1.5)
+!:mime application/x-rar
+!:ext rar/cbr
+
+# SQUISH archiver (Greg Roelofs, newt@uchicago.edu)
+0 string SQSH squished archive data (Acorn RISCOS)
+
+# UC2 archiver (Greg Roelofs, newt@uchicago.edu)
+# [JW] see exe section for self-extracting version
+0 string UC2\x1a UC2 archive data
+
+# PKZIP multi-volume archive
+0 string PK\x07\x08PK\x03\x04 Zip multi-volume archive data, at least PKZIP v2.50 to extract
+!:mime application/zip
+!:ext zip/cbz
+
+# Android APK file (Zip archive)
+0 string PK\003\004
+!:strength +1
+# Starts with AndroidManifest.xml (file name length = 19)
+>26 uleshort 19
+>>30 string AndroidManifest.xml Android package (APK), with AndroidManifest.xml
+!:mime application/vnd.android.package-archive
+!:ext apk
+>>>-22 string PK\005\006
+>>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 \b, with APK Signing Block
+# Starts with META-INF/com/android/build/gradle/app-metadata.properties
+>26 uleshort 57
+>>30 string META-INF/com/android/build/gradle/
+>>>&0 string app-metadata.properties Android package (APK), with gradle app-metadata.properties
+!:mime application/vnd.android.package-archive
+!:ext apk
+>>>>-22 string PK\005\006
+>>>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 \b, with APK Signing Block
+# Starts with classes.dex (file name length = 11)
+>26 uleshort 11
+>>30 string classes.dex Android package (APK), with classes.dex
+!:mime application/vnd.android.package-archive
+!:ext apk
+>>>-22 string PK\005\006
+>>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 \b, with APK Signing Block
+# Starts with META-INF/MANIFEST.MF (file name length = 20)
+# NB: checks for resources.arsc, classes.dex, etc. as well to avoid matching JAR files
+>26 uleshort 20
+>>30 string META-INF/MANIFEST.MF
+# Contains resources.arsc (near the end, in the central directory)
+>>>-512 search resources.arsc Android package (APK), with MANIFEST.MF and resources.arsc
+!:mime application/vnd.android.package-archive
+!:ext apk
+>>>>-22 string PK\005\006
+>>>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 \b, with APK Signing Block
+>>>-512 default x
+# Contains classes.dex (near the end, in the central directory)
+>>>>-512 search classes.dex Android package (APK), with MANIFEST.MF and classes.dex
+!:mime application/vnd.android.package-archive
+!:ext apk
+>>>>>-22 string PK\005\006
+>>>>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 \b, with APK Signing Block
+>>>>-512 default x
+# Contains lib/armeabi (near the end, in the central directory)
+>>>>>-512 search lib/armeabi Android package (APK), with MANIFEST.MF and armeabi lib
+!:mime application/vnd.android.package-archive
+!:ext apk
+>>>>>>-22 string PK\005\006
+>>>>>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 \b, with APK Signing Block
+>>>>>-512 default x
+# Contains drawables (near the end, in the central directory)
+>>>>>>-512 search res/drawable Android package (APK), with MANIFEST.MF and drawables
+!:mime application/vnd.android.package-archive
+!:ext apk
+>>>>>>>-22 string PK\005\006
+>>>>>>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 \b, with APK Signing Block
+# It may or may not be an APK file, but it's definitely a Java JAR file
+>>>>>>-512 default x Java archive data (JAR)
+!:mime application/java-archive
+!:ext jar
+# Starts with zipflinger virtual entry (28 + 104 = 132 bytes)
+# See https://github.com/obfusk/apksigcopier/blob/666f5b7/apksigcopier/__init__.py#L230
+>4 string \x00\x00\x00\x00\x00\x00
+>>&0 string \x21\x08\x21\x02
+>>>&0 string \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+>>>>&0 string \x00\x00 Android package (APK), with zipflinger virtual entry
+!:mime application/vnd.android.package-archive
+!:ext apk
+>>>>>-22 string PK\005\006
+>>>>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 \b, with APK Signing Block
+# APK Signing Block
+>0 default x
+>>-22 string PK\005\006
+>>>(-6.l-16) string APK\x20Sig\x20Block\x2042 Android package (APK), with APK Signing Block
+!:mime application/vnd.android.package-archive
+!:ext apk
+
+# Zip archives (Greg Roelofs, c/o zip-bugs@wkuvx1.wku.edu)
+0 string PK\005\006 Zip archive data (empty)
+!:mime application/zip
+!:ext zip/cbz
+!:strength +1
+0 string PK\003\004
+!:strength +1
+
+# Specialised zip formats which start with a member named 'mimetype'
+# (stored uncompressed, with no 'extra field') containing the file's MIME type.
+# Check for have 8-byte name, 0-byte extra field, name "mimetype", and
+# contents starting with "application/":
+>26 string \x8\0\0\0mimetypeapplication/
+
+# KOffice / OpenOffice & StarOffice / OpenDocument formats
+# From: Abel Cheung <abel@oaka.org>
+
+# KOffice (1.2 or above) formats
+# (mimetype contains "application/vnd.kde.<SUBTYPE>")
+>>50 string vnd.kde. KOffice (>=1.2)
+>>>58 string karbon Karbon document
+>>>58 string kchart KChart document
+>>>58 string kformula KFormula document
+>>>58 string kivio Kivio document
+>>>58 string kontour Kontour document
+>>>58 string kpresenter KPresenter document
+>>>58 string kspread KSpread document
+>>>58 string kword KWord document
+
+# OpenOffice formats (for OpenOffice 1.x / StarOffice 6/7)
+# (mimetype contains "application/vnd.sun.xml.<SUBTYPE>")
+# URL: https://en.wikipedia.org/wiki/OpenOffice.org_XML
+# reference: http://fileformats.archiveteam.org/wiki/OpenOffice.org_XML
+>>50 string vnd.sun.xml. OpenOffice.org 1.x
+>>>62 string writer Writer
+>>>>68 byte !0x2e document
+!:mime application/vnd.sun.xml.writer
+!:ext sxw
+>>>>68 string .template template
+!:mime application/vnd.sun.xml.writer.template
+!:ext stw
+>>>>68 string .web Web template
+!:mime application/vnd.sun.xml.writer.web
+!:ext stw
+>>>>68 string .global global document
+!:mime application/vnd.sun.xml.writer.global
+!:ext sxg
+>>>62 string calc Calc
+>>>>66 byte !0x2e spreadsheet
+!:mime application/vnd.sun.xml.calc
+!:ext sxc
+>>>>66 string .template template
+!:mime application/vnd.sun.xml.calc.template
+!:ext stc
+>>>62 string draw Draw
+>>>>66 byte !0x2e document
+!:mime application/vnd.sun.xml.draw
+!:ext sxd
+>>>>66 string .template template
+!:mime application/vnd.sun.xml.draw.template
+!:ext std
+>>>62 string impress Impress
+>>>>69 byte !0x2e presentation
+!:mime application/vnd.sun.xml.impress
+!:ext sxi
+>>>>69 string .template template
+!:mime application/vnd.sun.xml.impress.template
+!:ext sti
+>>>62 string math Math document
+!:mime application/vnd.sun.xml.math
+!:ext sxm
+>>>62 string base Database file
+!:mime application/vnd.sun.xml.base
+!:ext sdb
+
+# URL: https://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/File_Format
+# From: Joerg Jenderek
+# Note: only few OXT samples are detected here by mimetype member
+# is used by OpenOffice and LibreOffice and probably also NeoOffice
+# verified by `unzip -Zv *.oxt` or `7z l -slt *.oxt`
+>>50 string vnd.openofficeorg. OpenOffice
+>>>68 string extension \b/LibreOffice Extension
+# http://extension.nirsoft.net/oxt
+!:mime application/vnd.openofficeorg.extension
+# like: Gallery-Puzzle.2.1.0.1.oxt
+!:ext oxt
+
+# OpenDocument formats (for OpenOffice 2.x / StarOffice >= 8)
+# URL: http://fileformats.archiveteam.org/wiki/OpenDocument
+# https://lists.oasis-open.org/archives/office/200505/msg00006.html
+# (mimetype contains "application/vnd.oasis.opendocument.<SUBTYPE>")
+>>50 string vnd.oasis.opendocument. OpenDocument
+>>>73 string text
+>>>>77 byte !0x2d Text
+!:mime application/vnd.oasis.opendocument.text
+!:ext odt
+>>>>77 string -template Text Template
+!:mime application/vnd.oasis.opendocument.text-template
+!:ext ott
+>>>>77 string -web HTML Document Template
+!:mime application/vnd.oasis.opendocument.text-web
+!:ext oth
+>>>>77 string -master
+>>>>>84 byte !0x2d Master Document
+!:mime application/vnd.oasis.opendocument.text-master
+!:ext odm
+>>>>>84 string -template Master Template
+!:mime application/vnd.oasis.opendocument.text-master-template
+!:ext otm
+>>>73 string graphics
+>>>>81 byte !0x2d Drawing
+!:mime application/vnd.oasis.opendocument.graphics
+!:ext odg
+>>>>81 string -template Drawing Template
+!:mime application/vnd.oasis.opendocument.graphics-template
+!:ext otg
+>>>73 string presentation
+>>>>85 byte !0x2d Presentation
+!:mime application/vnd.oasis.opendocument.presentation
+!:ext odp
+>>>>85 string -template Presentation Template
+!:mime application/vnd.oasis.opendocument.presentation-template
+!:ext otp
+>>>73 string spreadsheet
+>>>>84 byte !0x2d Spreadsheet
+!:mime application/vnd.oasis.opendocument.spreadsheet
+!:ext ods
+>>>>84 string -template Spreadsheet Template
+!:mime application/vnd.oasis.opendocument.spreadsheet-template
+!:ext ots
+>>>73 string chart
+>>>>78 byte !0x2d Chart
+!:mime application/vnd.oasis.opendocument.chart
+!:ext odc
+>>>>78 string -template Chart Template
+!:mime application/vnd.oasis.opendocument.chart-template
+!:ext otc
+>>>73 string formula
+>>>>80 byte !0x2d Formula
+!:mime application/vnd.oasis.opendocument.formula
+!:ext odf
+>>>>80 string -template Formula Template
+!:mime application/vnd.oasis.opendocument.formula-template
+!:ext otf
+# https://www.loc.gov/preservation/digital/formats/fdd/fdd000441.shtml
+>>>73 string database Database
+!:mime application/vnd.oasis.opendocument.database
+!:ext odb
+# Valid for LibreOffice Base 6.0.1.1 at least
+>>>73 string base Database
+# https://bugs.documentfoundation.org/show_bug.cgi?id=45854
+!:mime application/vnd.oasis.opendocument.base
+!:ext odb
+>>>73 string image
+>>>>78 byte !0x2d Image
+!:mime application/vnd.oasis.opendocument.image
+!:ext odi
+>>>>78 string -template Image Template
+!:mime application/vnd.oasis.opendocument.image-template
+!:ext oti
+
+# EPUB (OEBPS) books using OCF (OEBPS Container Format)
+# https://www.idpf.org/ocf/ocf1.0/download/ocf10.htm, section 4.
+# From: Ralf Brown <ralf.brown@gmail.com>
+>>50 string epub+zip EPUB document
+!:mime application/epub+zip
+
+# From: Hajin Jang <jb6804@naver.com>
+# hwpx (OWPML) document format follows OCF specification.
+# Hangul Word Processor 2010+ supports HWPX format.
+# URL: https://www.hancom.com/etc/hwpDownload.do
+# https://standard.go.kr/KSCI/standardIntro/getStandardSearchView.do?menuId=503&topMenuId=502&ksNo=KSX6101
+# https://e-ks.kr/streamdocs/view/sd;streamdocsId=72059197557727331
+>>50 string hwp+zip Hancom HWP (Hangul Word Processor) file, HWPX
+!:mime application/x-hwp+zip
+!:ext hwpx
+
+# From: Joerg Jenderek
+# URL: http://en.wikipedia.org/wiki/CorelDRAW
+# NOTE: version; til 2 WL-based; from 3 til 13 by ./riff; from 14 zip based
+>>50 string x-vnd.corel. Corel
+>>>62 string draw.document+zip Draw drawing, version 14-16
+!:mime application/x-vnd.corel.draw.document+zip
+!:ext cdr
+>>>62 string draw.template+zip Draw template, version 14-16
+!:mime application/x-vnd.corel.draw.template+zip
+!:ext cdrt
+>>>62 string zcf.draw.document+zip Draw drawing, version 17-22
+!:mime application/x-vnd.corel.zcf.draw.document+zip
+!:ext cdr
+>>>62 string zcf.draw.template+zip Draw template, version 17-22
+!:mime application/x-vnd.corel.zcf.draw.template+zip
+!:ext cdt/cdrt
+# URL: http://product.corel.com/help/CorelDRAW/540240626/Main/EN/Doc/CorelDRAW-Other-file-formats.html
+>>>62 string zcf.pattern+zip Draw pattern, version 22
+!:mime application/x-vnd.corel.zcf.pattern+zip
+!:ext pat
+# URL: https://en.wikipedia.org/wiki/Corel_Designer
+# Reference: http://fileformats.archiveteam.org/wiki/Corel_Designer
+# Note: called by TrID "Corel DESIGN graphics"
+>>>62 string designer.document+zip DESIGNER graphics, version 14-16
+!:mime application/x-vnd.corel.designer.document+zip
+!:ext des
+>>>62 string zcf.designer.document+zip DESIGNER graphics, version 17-21
+!:mime application/x-vnd.corel.zcf.designer.document+zip
+!:ext des
+# URL: http://product.corel.com/help/CorelDRAW/540223850/Main/EN/Documentation/
+# CorelDRAW-Corel-Symbol-Library-CSL.html
+>>>62 string symbol.library+zip Symbol Library, version 6-16.3
+!:mime application/x-vnd.corel.symbol.library+zip
+!:ext csl
+>>>62 string zcf.symbol.library+zip Symbol Library, version 17-22
+!:mime application/x-vnd.corel.zcf.symbol.library+zip
+!:ext csl
+
+# Catch other ZIP-with-mimetype formats
+# In a ZIP file, the bytes immediately after a member's contents are
+# always "PK". The 2 regex rules here print the "mimetype" member's
+# contents up to the first 'P'. Luckily, most MIME types don't contain
+# any capital 'P's. This is a kludge.
+# (mimetype contains "application/<OTHER>")
+>>50 default x Zip data
+>>>38 regex [!-OQ-~]+ (MIME type "%s"?)
+!:mime application/zip
+# (mimetype contents other than "application/*")
+>26 string \x8\0\0\0mimetype
+>>38 string !application/
+>>>38 regex [!-OQ-~]+ Zip data (MIME type "%s"?)
+!:mime application/zip
+
+# Java Jar files (see also APK files above)
+>(26.s+30) leshort 0xcafe Java archive data (JAR)
+!:mime application/java-archive
+!:ext jar
+
+# iOS App
+>(26.s+30) leshort !0xcafe
+>>26 string !\x8\0\0\0mimetype
+>>>30 string Payload/
+>>>>38 search/64 .app/ iOS App
+!:mime application/x-ios-app
+
+# Dup, see above.
+#>30 search/100/b application/epub+zip EPUB document
+#!:mime application/epub+zip
+
+# Generic zip archives (Greg Roelofs, c/o zip-bugs@wkuvx1.wku.edu)
+# Next line excludes specialized formats:
+>(26.s+30) leshort !0xcafe
+>>30 search/100/b !application/epub+zip
+>>>26 string !\x8\0\0\0mimetype Zip archive data
+!:mime application/zip
+>>>>4 beshort x \b, at least
+>>>>4 use zipversion
+>>>>4 beshort x to extract
+>>>>8 beshort x \b, compression method=
+>>>>8 use zipcompression
+>>>>0x161 string WINZIP \b, WinZIP self-extracting
+
+# StarView Metafile
+# From Pierre Ducroquet <pinaraf@pinaraf.info>
+0 string VCLMTF StarView MetaFile
+>6 beshort x \b, version %d
+>8 belong x \b, size %d
+
+# Zoo archiver
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Zoo_(file_format)
+# http://fileformats.archiveteam.org/wiki/Zoo
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-zoo-strict.trid.xml
+# http://distcache.freebsd.org/ports-distfiles/zoo-2.10pl1.tar.gz/zoo.h
+# Note: called "ZOO compressed archive (strict)" by TrID and "ZOO Compressed Archive" by DROID via PUID x-fmt/269
+# verified by command like `deark -m zoo -l -d2 WHRCGA.ZOO`
+20 lelong 0xfdc4a7dc
+# skip DROID x-fmt-269-signature-id-621.zoo by looking for valid major version to manipulate archive
+>32 byte >0 Zoo archive data
+!:mime application/x-zoo
+# bak is extension of backup-ed zoo
+!:ext zoo/bak
+# version in text form like: 1.50 2.00 2.10
+>>4 byte >48 \b, v%c.
+>>>6 byte >47 \b%c
+>>>>7 byte >47 \b%c
+# ZOO files typically start with "ZOO ?.?? Archive.", followed by the bytes 0x1a 0x0 0x0; not used by Zoo and they may be anything
+>>8 string !\040Archive.\032 \b, at 8
+>>>8 string x text "%0.10s"
+# major_ver.minor_ver; minimum version needed to manipulate archive like: 1.0 2.0
+>>32 byte >0 \b, modify: v%d
+>>>33 byte x \b.%d+
+# major_ver.minor_ver; minimum version needed to extract after modify like in old versions
+>>(24.l+28) ubyte x \b, extract: v%u
+>>(24.l+29) ubyte x \b.%u+
+# with zoo 2.00 additional fields have been added in the archive header
+>>32 byte >1
+# type; type of archive header like: 1 2
+>>>34 ubyte !1 \b, header type %u
+# acmt_pos; position of archive comment like: 6258 30599 61369 149501
+>>>35 lelong >0 \b, at %d
+# acmt_len; length of archive comment like: 258
+>>>>39 uleshort x %u bytes comment
+#>>>>(35.l) ubequad x COMMENT=%16.16llx
+# 1st character of comment maybe is CarriageReturn (0x0d)
+>>>>(35.l) ubyte <040
+# 2nd character of comment maybe is LineFeed (0x0a)
+>>>>>(35.l+1) ubyte <040
+# comment string after CRLF like "Anonymous ftp site garbo.uwasa.fi 128.214.87.1 moderated by"
+>>>>>>(35.l+2) string x %s
+# next character of remaining comment maybe is CarriageReturn (0x0d)
+>>>>>>>&0 ubyte <040
+>>>>>>>>&0 ubyte <040
+# 2nd comment part like: Timo Salmi ts@chyde.uwasa.fi PC directories and uploads\015\012Harri Valkama hv@chyde.uwasa.fi PC, Mac, Unix files, and upload
+>>>>>>>>>&0 string >037 %s
+# vdata; archive-level versioning byte like: 1 3
+>>>41 ubyte !1 \b, vdata %#x
+# zoo_start; pointer to 1st entry header
+>>24 lelong x \b; at %u
+# zoo_minus; zoo_start -1 for consistency checking
+#>>28 lelong x \b, zoo_minus %#x
+# zoo_tag; tag for check
+#>>(24.l+0) ulelong !0xfdc4a7dc \b, zoo_tag=%8.8x
+# type; type of directory entry like: 1 2
+>>(24.l+4) ubyte !2 type=%u
+# packing_method; 0~no packing 1~normal LZW 2~lzh
+>>(24.l+5) ubyte x method=
+>>>(24.l+5) ubyte 0 \bnot-compressed
+>>>(24.l+5) ubyte 1 \blzd
+>>>(24.l+5) ubyte 2 \blzh
+# next; position of next directory entry
+>>(24.l+6) ulelong x \b, next entry at %u
+# offset; position of file data for this entry
+#>>(24.l+10) ulelong x \b, data at %u
+# file_crc; CRC-16 of file data
+>>(24.l+18) uleshort x \b, CRC %#4.4x
+# comment; zero if none or points to entry comment like ADD9h (WHRCGA.ZOO)
+>>(24.l+32) lelong >0 \b, at %#x
+# cmt_size; if not 0 for none then length of entry comment like: 46
+>>>(24.l+36) uleshort >0 %u bytes comment
+# entry comment itself like: "CGA .GL file showing menu input from keyboard"
+>>>>(&-6.l) string x "%s"
+# org_size; original size of file
+>>(24.l+20) ulelong x \b, size %u
+# size_now; compressed size of file
+>>(24.l+24) ulelong x (%u compressed)
+# major_ver.minor_ver; minimum version needed to extract already done
+# deleted; will be 1 if deleted, 0 if not
+>>(24.l+30) ubyte =1 \b, deleted
+# struc; file structure if any; WHAT IS THAT?
+>>(24.l+31) ubyte !0 \b, structured
+# fname[13]; short/DOS file name like 12345678.012
+>>(24.l+38) string x \b, %0.13s
+# for directory entry type 2 with variable part
+>>(24.l+4) ubyte =2
+# var_dir_len; length of variable part of dir entry
+>>>(24.l+51) uleshort >0
+#>>>(24.l+51) uleshort >0 \b, variable part length %u
+# namlen; length of long filename
+#>>>>(24.l+56) ubyte x \b, namlen %u
+# dirlen; length of directory name
+#>>>>(24.l+57) ubyte x \b, dirlen %u
+# if file length positive then show long file name
+>>>>(24.l+56) ubyte >0
+# lfname[256]; long file name \0-terminated
+>>>>>(24.l+58) string x "%s"
+# if directory length positive then jump before file name field and then jump this addtional length plus 2 (\0-terminator + dirlen field) to following directory name
+>>>>(24.l+57) ubyte >0
+>>>>>(24.l+55) ubyte x
+# dirname[256]; directory name \0-terminated
+>>>>>>&(&0.b+2) string x in "%s"
+# dir_crc; CRC of directory entry
+#>>>(24.l+54) uleshort x \b, entry CRC %#4.4x
+# tz; timezone where file was archived; 7Fh~unknown 4~1.00hoursWestOfUTC 12 16 20~5.00hoursWestOfUTC -107~26.75hoursEastOfUTC -4~1.00hoursEastOfUTC
+>>>(24.l+53) byte !0x7f \b, time zone %d/4
+# date; last mod file date in DOS format
+>>>(24.l+14) lemsdosdate x \b, modified %s
+# time; last mod file time in DOS format
+>>>(24.l+16) lemsdostime x %s
+
+# Shell archives
+10 string #\ This\ is\ a\ shell\ archive shell archive text
+!:mime application/octet-stream
+
+#
+# LBR. NB: May conflict with the questionable
+# "binary Computer Graphics Metafile" format.
+#
+0 string \0\ \ \ \ \ \ \ \ \ \ \ \0\0 LBR archive data
+#
+# PMA (CP/M derivative of LHA)
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/LHA_(file_format)
+#
+#2 string -pm0- PMarc archive data [pm0]
+2 string -pm0-
+>0 use lharc-file
+#2 string -pm1- PMarc archive data [pm1]
+2 string -pm1-
+>0 use lharc-file
+#2 string -pm2- PMarc archive data [pm2]
+2 string -pm2-
+>0 use lharc-file
+2 string -pms- PMarc SFX archive (CP/M, DOS)
+#!:mime application/x-foobar-exec
+!:ext com
+5 string -pc1- PopCom compressed executable (CP/M)
+#!:mime application/x-
+#!:ext com
+
+# From Rafael Laboissiere <rafael@laboissiere.net>
+# The Project Revision Control System (see
+# http://prcs.sourceforge.net) generates a packaged project
+# file which is recognized by the following entry:
+0 leshort 0xeb81 PRCS packaged project
+
+# Microsoft cabinets
+# by David Necas (Yeti) <yeti@physics.muni.cz>
+#0 string MSCF\0\0\0\0 Microsoft cabinet file data,
+#>25 byte x v%d
+#>24 byte x \b.%d
+# MPi: All CABs have version 1.3, so this is pointless.
+# Better magic in debian-additions.
+
+# GTKtalog catalogs
+# by David Necas (Yeti) <yeti@physics.muni.cz>
+4 string gtktalog\ GTKtalog catalog data,
+>13 string 3 version 3
+>>14 beshort 0x677a (gzipped)
+>>14 beshort !0x677a (not gzipped)
+>13 string >3 version %s
+
+############################################################################
+# Parity archive reconstruction file, the 'par' file format now used on Usenet.
+0 string PAR\0 PARity archive data
+>48 leshort =0 - Index file
+>48 leshort >0 - file number %d
+
+# Felix von Leitner <felix-file@fefe.de>
+0 string d8:announce BitTorrent file
+!:mime application/x-bittorrent
+!:ext torrent
+# Durval Menezes, <jmgthbfile at durval dot com>
+0 string d13:announce-list BitTorrent file
+!:mime application/x-bittorrent
+!:ext torrent
+0 string d7:comment BitTorrent file
+!:mime application/x-bittorrent
+!:ext torrent
+0 string d4:info BitTorrent file
+!:mime application/x-bittorrent
+!:ext torrent
+
+# Atari MSA archive - Teemu Hukkanen <tjhukkan@iki.fi>
+# URL: http://fileformats.archiveteam.org/wiki/MSA_(Magic_Shadow_Archiver)
+# Reference: http://info-coach.fr/atari/documents/_mydoc/FD_Image_File_Format.pdf
+# http://mark0.net/download/triddefs_xml.7z/defs/m/msa.trid.xml
+# Update: Joerg Jenderek
+# Note: called by TrID "Atari MSA Disk Image" and verified by
+# command like `deark -l -m msa -d2 PDATS578.msa` as " Atari ST floppy disk image"
+# GRR: line below is too general as it matches setup.skin
+0 beshort 0x0e0f
+# skip foo setup.skin with unrealistic high number 52255 of sides by check for valid "low" value
+>4 ubeshort <2 Atari MSA archive data
+#!:mime application/octet-stream
+!:mime application/x-atari-msa
+!:ext msa
+# sectors per track like: 9 10
+>>2 beshort x \b, %d sectors per track
+# sides (0 or 1; add 1 to this to get correct number of sides)
+>>4 beshort 0 \b, 1 sided
+>>4 beshort 1 \b, 2 sided
+# starting track like: 0
+>>6 beshort x \b, starting track: %d
+# ending track like: 39 79 80 81
+>>8 beshort x \b, ending track: %d
+# tracks content
+#>>10 ubequad x \b, track content %#16.16llx
+
+# Alternate ZIP string (amc@arwen.cs.berkeley.edu)
+0 string PK00PK\003\004 Zip archive data
+!:mime application/zip
+!:ext zip/cbz
+
+# Recognize ZIP archives with prepended data by end-of-central-directory record
+# https://en.wikipedia.org/wiki/ZIP_(file_format)#End_of_central_directory_record_(EOCD)
+# by Michal Gorny <mgorny@gentoo.org>
+-2 uleshort 0
+>&-22 string PK\005\006
+# without #!
+>>0 string !#! Zip archive, with extra data prepended
+!:mime application/zip
+!:ext zip/cbz
+# with #!
+>>0 string/w #!\ a
+>>>&-1 string/T x %s script executable (Zip archive)
+
+# ACE archive (from http://www.wotsit.org/download.asp?f=ace)
+# by Stefan `Sec` Zehl <sec@42.org>
+7 string **ACE** ACE archive data
+!:mime application/x-ace-compressed
+!:ext ace
+>15 byte >0 version %d
+>16 byte =0x00 \b, from MS-DOS
+>16 byte =0x01 \b, from OS/2
+>16 byte =0x02 \b, from Win/32
+>16 byte =0x03 \b, from Unix
+>16 byte =0x04 \b, from MacOS
+>16 byte =0x05 \b, from WinNT
+>16 byte =0x06 \b, from Primos
+>16 byte =0x07 \b, from AppleGS
+>16 byte =0x08 \b, from Atari
+>16 byte =0x09 \b, from Vax/VMS
+>16 byte =0x0A \b, from Amiga
+>16 byte =0x0B \b, from Next
+>14 byte x \b, version %d to extract
+>5 leshort &0x0080 \b, multiple volumes,
+>>17 byte x \b (part %d),
+>5 leshort &0x0002 \b, contains comment
+>5 leshort &0x0200 \b, sfx
+>5 leshort &0x0400 \b, small dictionary
+>5 leshort &0x0800 \b, multi-volume
+>5 leshort &0x1000 \b, contains AV-String
+>>30 string \x16*UNREGISTERED\x20VERSION* (unregistered)
+>5 leshort &0x2000 \b, with recovery record
+>5 leshort &0x4000 \b, locked
+>5 leshort &0x8000 \b, solid
+# Date in MS-DOS format (whatever that is)
+#>18 lelong x Created on
+
+# sfArk : compression program for Soundfonts (sf2) by Dirk Jagdmann
+# <doj@cubic.org>
+0x1A string sfArk sfArk compressed Soundfont
+>0x15 string 2
+>>0x1 string >\0 Version %s
+>>0x2A string >\0 : %s
+
+# DR-DOS 7.03 Packed File *.??_
+# Reference: http://www.antonis.de/dos/dos-tuts/mpdostip/html/nwdostip.htm
+# Note: unpacked by PNUNPACK.EXE
+0 string Packed\ File\
+# by looking for Control-Z skip ASCII text starting with Packed File
+>0x18 ubyte 0x1a Personal NetWare Packed File
+!:mime application/x-novell-compress
+!:ext ??_
+>>12 string x \b, was "%.12s"
+# 1 or 2
+#>>0x19 ubyte x \b, at 0x19 %u
+>>0x1b ulelong x with %u bytes
+
+# EET archive
+# From: Tilman Sauerbeck <tilman@code-monkey.de>
+0 belong 0x1ee7ff00 EET archive
+!:mime application/x-eet
+
+# rzip archives
+0 string RZIP rzip compressed data
+>4 byte x - version %d
+>5 byte x \b.%d
+>6 belong x (%d bytes)
+
+# From: Joerg Jenderek
+# URL: https://help.foxitsoftware.com/kb/install-fzip-file.php
+# reference: http://mark0.net/download/triddefs_xml.7z/
+# defs/f/fzip.trid.xml
+# Note: unknown compression; No "PK" zip magic; normally in directory like
+# "%APPDATA%\Foxit Software\Addon\Foxit Reader\Install"
+0 ubequad 0x2506781901010000 Foxit add-on/update
+!:mime application/x-fzip
+!:ext fzip
+
+# From: "Robert Dale" <robdale@gmail.com>
+0 belong 123 dar archive,
+>4 belong x label "%.8x
+>>8 belong x %.8x
+>>>12 beshort x %.4x"
+>14 byte 0x54 end slice
+>14 beshort 0x4e4e multi-part
+>14 beshort 0x4e53 multi-part, with -S
+
+# Symbian installation files
+# https://www.thouky.co.uk/software/psifs/sis.html
+# http://developer.symbian.com/main/downloads/papers/SymbianOSv91/softwareinstallsis.pdf
+8 lelong 0x10000419 Symbian installation file
+!:mime application/vnd.symbian.install
+>4 lelong 0x1000006D (EPOC release 3/4/5)
+>4 lelong 0x10003A12 (EPOC release 6)
+0 lelong 0x10201A7A Symbian installation file (Symbian OS 9.x)
+!:mime x-epoc/x-sisx-app
+
+# From "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string MPQ\032 MoPaQ (MPQ) archive
+
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# .kgb
+0 string KGB_arch KGB Archiver file
+>10 string x with compression level %.1s
+
+# xar (eXtensible ARchiver) archive
+# URL: https://en.wikipedia.org/wiki/Xar_(archiver)
+# xar archive format: https://code.google.com/p/xar/
+# From: "David Remahl" <dremahl@apple.com>
+# Update: Joerg Jenderek
+# TODO: lzma compression; X509Data for pkg and xip
+# Note: verified by `xar --dump-header -f FullBundleUpdate.xar` or
+# 7z t -txar Xcode_10.2_beta_4.xip`
+0 string xar! xar archive
+!:mime application/x-xar
+# pkg for Mac OSX installer package like FullBundleUpdate.pkg
+# xip for signed Apple software like Xcode_10.2_beta_4.xip
+!:ext xar/pkg/xip
+# always 28 in older archives
+>4 ubeshort >28 \b, header size %u
+# currently there exit only version 1 since about 2014
+>6 ubeshort >1 version %u,
+>8 ubequad x compressed TOC: %llu,
+#>16 ubequad x uncompressed TOC: %llu,
+# cksum_alg 0-2 in older and also 3-4 in newer
+>24 belong 0 no checksum
+>24 belong 1 SHA-1 checksum
+>24 belong 2 MD5 checksum
+>24 belong 3 SHA-256 checksum
+>24 belong 4 SHA-512 checksum
+>24 belong >4 unknown %#x checksum
+#>24 belong >4 checksum
+# For no compression jump 0 bytes
+>24 belong 0
+>>0 ubyte x
+# jump more bytes forward by header size
+>>>&(4.S) ubyte x
+# jump more bytes forward by compressed table of contents size
+#>>>>&(8.Q) ubequad x \b, heap data %#llx
+>>>>&(8.Q) ubyte x
+# look for data by ./compress after message with 1 space at end
+>>>>>&-3 indirect x \b, contains
+# For SHA-1 jump 20 minus 2 bytes
+>24 belong 1
+>>18 ubyte x
+# jump more bytes forward by header size
+>>>&(4.S) ubyte x
+# jump more bytes forward by compressed table of contents size
+>>>>&(8.Q) ubyte x
+# data compressed by gzip, bzip, lzma or none
+>>>>>&-1 indirect x \b, contains
+# For SHA-256 jump 32 minus 2 bytes
+>24 belong 3
+>>30 ubyte x
+# jump more bytes forward by header size
+>>>&(4.S) ubyte x
+# jump more bytes forward by compressed table of contents size
+>>>>&(8.Q) ubyte x
+>>>>>&-1 indirect x \b, contains
+# For SHA-512 jump 64 minus 2 bytes
+>24 belong 4
+>>62 ubyte x
+# jump more bytes forward by header size
+>>>&(4.S) ubyte x
+# jump more bytes forward by compressed table of contents size
+>>>>&(8.Q) ubyte x
+>>>>>&-1 indirect x \b, contains
+
+# Type: Parity Archive
+# From: Daniel van Eeden <daniel_e@dds.nl>
+0 string PAR2 Parity Archive Volume Set
+
+# Bacula volume format. (Volumes always start with a block header.)
+# URL: https://bacula.org/3.0.x-manuals/en/developers/developers/Block_Header.html
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+12 string BB02 Bacula volume
+>20 bedate x \b, started %s
+
+# ePub is XHTML + XML inside a ZIP archive. The first member of the
+# archive must be an uncompressed file called 'mimetype' with contents
+# 'application/epub+zip'
+
+
+# From: "Michael Gorny" <mgorny@gentoo.org>
+# ZPAQ: http://mattmahoney.net/dc/zpaq.html
+0 string zPQ ZPAQ stream
+>3 byte x \b, level %d
+# From: Barry Carter <carter.barry@gmail.com>
+# https://encode.ru/threads/456-zpaq-updates/page32
+0 string 7kSt ZPAQ file
+
+# BBeB ebook, unencrypted (LRF format)
+# URL: https://www.sven.de/librie/Librie/LrfFormat
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string L\0R\0F\0\0\0 BBeB ebook data, unencrypted
+>8 beshort x \b, version %d
+>36 byte 1 \b, front-to-back
+>36 byte 16 \b, back-to-front
+>42 beshort x \b, (%dx,
+>44 beshort x %d)
+
+# Symantec GHOST image by Joerg Jenderek at May 2014
+# https://us.norton.com/ghost/
+# https://www.garykessler.net/library/file_sigs.html
+0 ubelong&0xFFFFf7f0 0xFEEF0100 Norton GHost image
+# *.GHO
+>2 ubyte&0x08 0x00 \b, first file
+# *.GHS or *.[0-9] with cns program option
+>2 ubyte&0x08 0x08 \b, split file
+# part of split index interesting for *.ghs
+>>4 ubyte x id=%#x
+# compression tag minus one equals numeric compression command line switch z[1-9]
+>3 ubyte 0 \b, no compression
+>3 ubyte 2 \b, fast compression (Z1)
+>3 ubyte 3 \b, medium compression (Z2)
+>3 ubyte >3
+>>3 ubyte <11 \b, compression (Z%d-1)
+>2 ubyte&0x08 0x00
+# ~ 30 byte password field only for *.gho
+>>12 ubequad !0 \b, password protected
+>>44 ubyte !1
+# 1~Image All, sector-by-sector only for *.gho
+>>>10 ubyte 1 \b, sector copy
+# 1~Image Boot track only for *.gho
+>>>43 ubyte 1 \b, boot track
+# 1~Image Disc only for *.gho implies Image Boot track and sector copy
+>>44 ubyte 1 \b, disc sector copy
+# optional image description only *.gho
+>>0xff string >\0 "%-.254s"
+# look for DOS sector end sequence
+>0xE08 search/7776 \x55\xAA
+>>&-512 indirect x \b; contains
+
+# Google Chrome extensions
+# https://developer.chrome.com/extensions/crx
+# https://developer.chrome.com/extensions/hosting
+0 string Cr24 Google Chrome extension
+!:mime application/x-chrome-extension
+>4 ulong x \b, version %u
+
+# SeqBox - Sequenced container
+# ext: sbx, seqbox
+# Marco Pontello marcopon@gmail.com
+# reference: https://github.com/MarcoPon/SeqBox
+0 string SBx SeqBox,
+>3 byte x version %d
+
+# LyNX archive
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Lynx_archive
+# Reference: http://ist.uwaterloo.ca/~schepers/formats/LNX.TXT
+# http://mark0.net/download/triddefs_xml.7z/defs/a/ark-lnx.trid.xml
+# Note: called "Lynx archive" by TrID and "Commodore C64 BASIC program" with "POKE 53280" by ./c64
+# TODO: merge and unify with Commodore C64 BASIC program
+56 string USE\040LYNX\040TO\040DISSOLVE\040THIS\040FILE LyNX archive
+# display "Lynx archive" (strength=330) before Commodore C64 BASIC program (strength=50) handled by ./c64
+#!:strength +0
+#!:mime application/octet-stream
+!:mime application/x-commodore-lnx
+!:ext lnx
+# afterwards look for BASIC tokenized GOTO (89h) 10, line terminator \0, end of programm tag \0\0 and CarriageReturn
+>86 search/10 \x8910\0\0\0\r \b,
+# for DEBUGGING
+#>>&0 string x STRING="%s"
+# number in ASCII of directory blocks with spaces on both sides like: 1 2 3 5
+>>&0 regex [0-9]{1,5} %s directory blocks
+# signature like: "*LYNX XII BY WILL CORLEY" " LYNX IX BY WILL CORLEY" "*LYNX BY CBMCONVERT 2.0*"
+>>>&2 regex [^\r]{1,24} \b, signature "%s"
+# number of files in ASCII surrounded by spaces and delimited by CR like: 2 3 6 13 69 144 (maximum?)
+>>>>&1 regex [0-9]{1,3} \b, %s files
+
+# From: Joerg Jenderek
+# URL: https://www.acronis.com/
+# Reference: https://en.wikipedia.org/wiki/TIB_(file_format)
+# Note: only tested with True Image 2013 Build 5962 and 2019 Build 14110
+0 ubequad 0xce24b9a220000000 Acronis True Image backup
+!:mime application/x-acronis-tib
+!:ext tib
+# 01000000
+#>20 ubelong x \b, at 20 %#x
+# 20000000
+#>28 ubelong x \b, at 28 %#x
+# strings like "Generic- SD/MMC 1.00" "Unknown Disk" "Msft Virtual Disk 1.0"
+# ???
+# strings like "\Device\0000011e" "\Device\0000015a"
+#>0 search/0x6852300/cs \\Device\\
+#>>&-1 pstring x \b, %s
+# "\Device\HarddiskVolume30" "\Device\HarddiskVolume39"
+#>>>&1 search/180/cs \\Device\\
+#>>>>&-1 pstring x \b, %s
+#>>>>>&0 search/29/cs \0\0\xc8\0
+# disk label
+#>>>>>>&10 lestring16 x \b, disk label %11.11s
+#>>>>>>&9 plestring16 x \b, disk label "%11.11s"
+#>>>>>>&10 ubequad x %16.16llx
+
+
+# Gentoo XPAK binary package
+# by Michal Gorny <mgorny@gentoo.org>
+# https://gitweb.gentoo.org/proj/portage.git/tree/man/xpak.5
+-4 string STOP
+>-16 string XPAKSTOP Gentoo binary package (XPAK)
+!:mime application/vnd.gentoo.xpak
+
+# From: Joerg Jenderek
+# URL: https://kodi.wiki/view/TexturePacker
+# Reference: https://mirrors.kodi.tv/releases/source/17.3-Krypton.tar.gz
+# /xbmc-Krypton/xbmc/guilib/XBTF.h
+# /xbmc-Krypton/xbmc/guilib/XBTF.cpp
+0 string XBTF
+# skip ASCII text by looking for terminating \0 of path
+>264 ubyte 0 XBMC texture package
+!:mime application/x-xbmc-xbt
+!:ext xbt
+# XBTF_VERSION 2
+>>4 string !2 \b, version %-.1s
+# nofFiles /xbmc-Krypton/xbmc/guilib/XBTFReader.cpp
+>>5 ulelong x \b, %u file
+# plural s
+>>5 ulelong >1 \bs
+# path[CXBTFFile[MaximumPathLength=256]
+>>9 string x \b, 1st %s
+
+# ALZIP archive
+# by Hyungjun Park <hyungjun.park@worksmobile.com>, Hajin Jang <hajin_jang@worksmobile.com>
+# http://kippler.com/win/unalz/
+# https://salsa.debian.org/l10n-korean-team/unalz
+0 string ALZ\001 ALZ archive data
+!:ext alz
+
+# https://cf-aldn.altools.co.kr/setup/EGG_Specification.zip
+0 string EGGA EGG archive data,
+!:ext egg
+>5 byte x version %u
+>4 byte x \b.%u
+>>0x0E ulelong =0x08E28222
+>>0x0E ulelong =0x24F5A262 \b, split
+>>0x0E ulelong =0x24E5A060 \b, solid
+>>0x0E default x \b, unknown
+
+# PAQ9A archive
+# URL: http://mattmahoney.net/dc/#paq9a
+# Note: Line 1186 of paq9a.cpp gives the magic bytes
+0 string pQ9\001 PAQ9A archive
+
+# From wof (wof@stachelkaktus.net)
+0 string Unison\ archive\ format Unison archive format
+
+# https://ankiweb.net
+30 string collection.anki2 Anki APKG file
+#!:ext .apkg
+
+# Synology archive (DiskStation Manager 7.0+)
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# Note: These archives are signed and encrypted.
+0 ulelong&0xFFFFFF00 0xEFBEAD00
+# MessagePack header (fixarray of 5 elements starting with a bin of 32 bytes)
+>8 ulelong&0x00FFFFFF 0x20C495 Synology archive
+!:ext spk
+# Extract some properties from MessagePack third item
+>>43 search/0x10000 package=
+>>>&0 string x \b, package %s
+>>43 search/0x10000 arch=
+>>>&0 string x %s
+>>43 search/0x10000 version=
+>>>&0 string x %s
+>>43 search/0x10000 create_time=
+>>>&0 string x \b, created on %s
+
+# MonoGame/XNA processed assets archive
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/MonoGame/MonoGame/blob/v3.8.1/MonoGame.Framework/Content/ContentManager.cs
+0 string XNB
+# XNB must be version 4 or 5
+>4 byte <6
+>>4 byte >3
+# Size must be positive
+>>>6 lelong >0 MonoGame/XNA processed assets
+!:ext xnb
+>>>>3 string =w \b, for Windows
+>>>>3 string =x \b, for Xbox360
+>>>>3 string =i \b, for iOS
+>>>>3 string =a \b, for Android
+>>>>3 string =d \b, for DesktopGL
+>>>>3 string =X \b, for MacOSX
+>>>>3 string =W \b, for WindowsStoreApp
+>>>>3 string =n \b, for NativeClient
+>>>>3 string =M \b, for WindowsPhone8
+>>>>3 string =r \b, for RaspberryPi
+>>>>3 string =P \b, for PlayStation4
+>>>>3 string =5 \b, for PlayStation5
+>>>>3 string =O \b, for XboxOne
+>>>>3 string =S \b, for Nintendo Switch
+>>>>3 string =G \b, for Google Stadia
+>>>>3 string =b \b, for WebAssembly and Bridge.NET
+>>>>3 string =m \b, for WindowsPhone7.0 (XNA)
+>>>>3 string =p \b, for PlayStationMobile
+>>>>3 string =v \b, for PSVita
+>>>>3 string =g \b, for Windows (OpenGL)
+>>>>3 string =l \b, for Linux
+>>>>4 byte x \b, version %d
+>>>>5 byte &0x80 \b, LZX compressed
+>>>>>10 lelong x \b, decompressed size: %d bytes
+>>>>5 byte &0x40 \b, LZ4 compressed
+>>>>>10 lelong x \b, decompressed size: %d bytes
+
+# Electron ASAR archive
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/electron/asar
+0 ulelong 4
+# Match JSON header start and end
+>16 string {"files":{"
+>>(12.l+12) string }}}} Electron ASAR archive
+!:ext asar
+>>>12 ulelong x \b, header length: %d bytes
diff --git a/contrib/libs/libmagic/magic/Magdir/aria b/contrib/libs/libmagic/magic/Magdir/aria
new file mode 100644
index 0000000000..c3a6bf57e4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/aria
@@ -0,0 +1,38 @@
+
+#------------------------------------------------------------------------------
+# URL: https://de.wikipedia.org/wiki/Aria_(Software)
+# Reference: https://github.com/aria2/aria2/blob/master/doc/manual-src/en/technical-notes.rst
+# From: Joerg Jenderek
+# Note: only version 1 suited
+# check for valid version one
+0 beshort 0x0001
+# skip most uncompressed DEGAS med-res bitmap *.PI2 and GEM bitmap (v1) *.IMG
+# by test for valid infoHashCheck extension
+>2 ubelong&0xffFFffFE 0x00000000
+# skip DEGAS med-res bitmap DIAGRAM1.PI2 by test for valid length of download
+>>(6.L+14) ubequad >0
+>>>0 use aria
+0 name aria
+# version; (0x0000) or (0x0001); for 0 all multi-byte are in host byte order. For 1 big endian
+>0 beshort x aria2 control file, version %u
+#!:mime application/octet-stream
+!:mime application/x-aria
+!:ext aria2
+# EXTension; if EXT[3]&1 == 1 checks whether saved InfoHash and current downloading the same; infoHashCheck extension
+>2 ubelong !0 \b, infoHashCheck %#x
+# info hash length like: 0 14h
+>6 ubelong !0 \b, %#x bytes info hash
+# info hash; BitTorrent InfoHash
+>>10 ubequad x %#16.16llx...
+# piece length; the length of the piece like: 400h 100000h
+>(6.L+10) ubelong x \b, piece length 0x%x
+# total length; the total length of the download
+>(6.L+14) ubequad x \b, total length %llu
+#>(6.L+14) ubequad x \b, total length %#llx
+# upload length; the uploaded length of download like: 0 400h
+>(6.L+22) ubequad !0 \b, upload length %#llx
+# bitfield length; the length of bitfield like: 4 6 Ah 10h 13h 167h
+>(6.L+30) ubelong x \b, %#x bytes bitfield
+# bitfield; bitfield which represents current download progress
+>(6.L+34) ubequad !0 %#llx...
+
diff --git a/contrib/libs/libmagic/magic/Magdir/arm b/contrib/libs/libmagic/magic/Magdir/arm
new file mode 100644
index 0000000000..c514320354
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/arm
@@ -0,0 +1,50 @@
+#------------------------------------------------------------------------------
+# $File: arm,v 1.3 2022/10/31 14:35:39 christos Exp $
+# arm: file(1) magic for ARM COFF
+#
+# https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+
+# Aarch64
+0 leshort 0xaa64
+# test for unused flag bits in f_flags
+>18 uleshort&0x8E80 0
+# use little endian variant of subroutine to
+# display name+variables+flags for common object formatted files
+>>0 use display-coff
+!:strength -10
+
+# ARM
+0 leshort 0x01c0
+# test for unused flag bits in f_flags
+>18 uleshort&0x8E80 0
+# use little endian variant of subroutine to
+# display name+variables+flags for common object formatted files
+>>0 use display-coff
+!:strength -10
+
+# ARM Thumb
+0 leshort 0x01c2
+# test for unused flag bits in f_flags
+>18 uleshort&0x8E80 0
+# use little endian variant of subroutine to
+# display name+variables+flags for common object formatted files
+>>0 use display-coff
+!:strength -10
+
+# ARMv7 Thumb
+0 leshort 0x01c4
+# test for unused flag bits in f_flags
+>18 uleshort&0x8E80 0
+# use little endian variant of subroutine to
+# display name+variables+flags for common object formatted files
+>>0 use display-coff
+!:strength -10
+
+# ARM64EC
+0 leshort 0xa641
+# test for unused flag bits in f_flags
+>18 uleshort&0x8E80 0
+# use little endian variant of subroutine to
+# display name+variables+flags for common object formatted files
+>>0 use display-coff
+!:strength -10
diff --git a/contrib/libs/libmagic/magic/Magdir/asf b/contrib/libs/libmagic/magic/Magdir/asf
new file mode 100644
index 0000000000..744a0afc2c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/asf
@@ -0,0 +1,132 @@
+
+#------------------------------------------------------------------------------
+# $File: asf,v 1.4 2022/10/31 13:22:26 christos Exp $
+# asf: file(1) magic for Microsoft Advanced Systems Format (ASF) files
+# http://www.staroceans.org/e-book/ASF_Specification.pdf
+
+0 name asf-name
+# ASF_Data_Object
+#>0 guid 75B22636-668E-11CF-A6D9-00AA0062CE6C
+#>16 lequad >0
+#>>(16.q) use asf-object
+# ASF_Simple_Index_Object
+>0 guid 33000890-E5B1-11CF-89F4-00A0C90349CB
+>0 guid D6E229D3-35DA-11D1-9034-00A0C90349BE ASF_Index_Object
+>0 guid FEB103F8-12AD-4C64-840F-2A1D2F7AD48C ASF_Media_Object_Index_Object
+>0 guid 3CB73FD0-0C4A-4803-953D-EDF7B6228F0C ASF_Timecode_Index_Object
+
+# ASF_File_Properties_Object
+>0 guid 8CABDCA1-A947-11CF-8EE4-00C00C205365
+
+# ASF_Stream_Properties_Object
+>0 guid B7DC0791-A9B7-11CF-8EE6-00C00C205365
+#>>56 lequad x Time Offset %lld
+#>>64 lelong x Type-Specific Data Length %d
+#>>68 lelong x Error Correction Data Length %d
+#>>72 leshort x Flags %#x
+#>>74 lelong x Reserved %x
+# ASF_Audio_Media
+>>24 guid F8699E40-5B4D-11CF-A8FD-00805F5C442B \b, Audio Media (
+>>>78 leshort x \bCodec Id %d
+>>>80 leshort x \b, Number of channels %d
+>>>82 lelong x \b, Samples Per Second %d
+>>>86 lelong x \b, Average Number of Bytes Per Second %d
+>>>90 lelong x \b, Block Alignment %d
+>>>94 leshort x \b, Bits Per Sample %d
+# ASF_Video_Media
+>>24 guid BC19EFC0-5B4D-11CF-A8FD-00805F5C442B \b, Video Media (
+>>>78 lelong x \bEncoded Image Width %d
+>>>82 lelong x \b, Encoded Image Height %d
+#>>>85 leshort x \b, Format Data Size %x
+>>>93 lelong x \b, Image Width %d
+>>>97 lelong x \b, Image Height %d
+#>>>101 leshort x \b, Reserved %#x
+>>>103 leshort x \b, Bits Per Pixel Count %d
+#>>>105 lelong x \b, Compression ID %d
+#>>>109 lelong x \b, Image Size %d
+#>>>113 lelong x \b, Horizontal Pixels Per Meter %d
+#>>>117 lelong x \b, Vertical Pixels Per Meter %d
+#>>>121 lelong x \b, Colors Used Count %d
+#>>>125 lelong x \b, Important Colors Count %d
+>>0 lelong x \b, Error correction type
+>>40 use asf-name
+>>0 lelong x \b)
+#ASF_Header_Extension_Object
+>0 guid 5FBF03B5-A92E-11CF-8EE3-00C00C205365
+# ASF_Codec_List_Object
+>0 guid 86D15240-311D-11D0-A3A4-00A0C90348F6
+>0 guid 1EFB1A30-0B62-11D0-A39B-00A0C90348F6 ASF_Script_Command_Object
+>0 guid F487CD01-A951-11CF-8EE6-00C00C205365 ASF_Marker_Object
+>0 guid D6E229DC-35DA-11D1-9034-00A0C90349BE ASF_Bitrate_Mutual_Exclusion_Object
+>0 guid 75B22635-668E-11CF-A6D9-00AA0062CE6C ASF_Error_Correction_Object
+# ASF_Content_Description_Object
+>0 guid 75B22633-668E-11CF-A6D9-00AA0062CE6C
+#>>24 leshort title length %d
+#>>26 leshort author length %d
+#>>28 leshort copyright length %d
+#>>30 leshort descriptor length %d
+#>>32 leshort rating length %d
+>0 guid D2D0A440-E307-11D2-97F0-00A0C95EA850 ASF_Extended_Content_Description_Object
+>0 guid 2211B3FA-BD23-11D2-B4B7-00A0C955FC6E ASF_Content_Branding_Object
+>0 guid 7BF875CE-468D-11D1-8D82-006097C9A2B2 ASF_Stream_Bitrate_Properties_Object
+>0 guid 2211B3FB-BD23-11D2-B4B7-00A0C955FC6E ASF_Content_Encryption_Object
+>0 guid 298AE614-2622-4C17-B935-DAE07EE9289C ASF_Extended_Content_Encryption_Object
+>0 guid 2211B3FC-BD23-11D2-B4B7-00A0C955FC6E ASF_Digital_Signature_Object
+# ASF_Padding_Object
+>0 guid 1806D474-CADF-4509-A4BA-9AABCB96AAE8
+>0 guid 14E6A5CB-C672-4332-8399-A96952065B5A ASF_Extended_Stream_Properties_Object
+>0 guid A08649CF-4775-4670-8A16-6E35357566CD ASF_Advanced_Mutual_Exclusion_Object
+>0 guid D1465A40-5A79-4338-B71B-E36B8FD6C249 ASF_Group_Mutual_Exclusion_Object
+>0 guid D4FED15B-88D3-454F-81F0-ED5C45999E24 ASF_Stream_Prioritization_Object
+>0 guid A69609E6-517B-11D2-B6AF-00C04FD908E9 ASF_Bandwidth_Sharing_Object
+>0 guid 7C4346A9-EFE0-4BFC-B229-393EDE415C85 ASF_Language_List_Object
+>0 guid C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA ASF_Metadata_Object
+>0 guid 44231C94-9498-49D1-A141-1D134E457054 ASF_Metadata_Library_Object
+>0 guid D6E229DF-35DA-11D1-9034-00A0C90349BE ASF_Index_Parameters_Object
+>0 guid 6B203BAD-3F11-48E4-ACA8-D7613DE2CFA7 ASF_Media_Object_Index_Parameters_Object
+>0 guid F55E496D-9797-4B5D-8C8B-604DFE9BFB24 ASF_Timecode_Index_Parameters_Object
+>0 guid 26F18B5D-4584-47EC-9F5F-0E651F0452C9 ASF_Compatibility_Object
+>0 guid 43058533-6981-49E6-9B74-AD12CB86D58C ASF_Advanced_Content_Encryption_Object
+>0 guid 59DACFC0-59E6-11D0-A3AC-00A0C90348F6 ASF_Command_Media
+>0 guid B61BE100-5B4E-11CF-A8FD-00805F5C442B ASF_JFIF_Media
+>0 guid 35907DE0-E415-11CF-A917-00805F5C442B ASF_Degradable_JPEG_Media
+>0 guid 91BD222C-F21C-497A-8B6D-5AA86BFC0185 ASF_File_Transfer_Media
+>0 guid 3AFB65E2-47EF-40F2-AC2C-70A90D71D343 ASF_Binary_Media
+>0 guid 776257D4-C627-41CB-8F81-7AC7FF1C40CC ASF_Web_Stream_Media_Subtype
+>0 guid DA1E6B13-8359-4050-B398-388E965BF00C ASF_Web_Stream_Format
+>0 guid 20FB5700-5B55-11CF-A8FD-00805F5C442B ASF_No_Error_Correction
+>0 guid BFC3CD50-618F-11CF-8BB2-00AA00B4E220 ASF_Audio_Spread
+>0 guid ABD3D211-A9BA-11cf-8EE6-00C00C205365 ASF_Reserved_1
+>0 guid 7A079BB6-DAA4-4e12-A5CA-91D38DC11A8D ASF_Content_Encryption_System_Windows_Media_DRM
+# _Network_Devices
+>0 guid 86D15241-311D-11D0-A3A4-00A0C90348F6 ASF_Reserved_2
+>0 guid 4B1ACBE3-100B-11D0-A39B-00A0C90348F6 ASF_Reserved_3
+>0 guid 4CFEDB20-75F6-11CF-9C0F-00A0C90349CB ASF_Reserved_4
+>0 guid D6E22A00-35DA-11D1-9034-00A0C90349BE ASF_Mutex_Language
+>0 guid D6E22A01-35DA-11D1-9034-00A0C90349BE ASF_Mutex_Bitrate
+>0 guid D6E22A02-35DA-11D1-9034-00A0C90349BE ASF_Mutex_Unknown
+>0 guid AF6060AA-5197-11D2-B6AF-00C04FD908E9 ASF_Bandwidth_Sharing_Exclusive
+>0 guid AF6060AB-5197-11D2-B6AF-00C04FD908E9 ASF_Bandwidth_Sharing_Partial
+>0 guid 399595EC-8667-4E2D-8FDB-98814CE76C1E ASF_Payload_Extension_System_Timecode
+>0 guid E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B ASF_Payload_Extension_System_File_Name
+>0 guid D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC ASF_Payload_Extension_System_Content_Type
+>0 guid 1B1EE554-F9EA-4BC8-821A-376B74E4C4B8 ASF_Payload_Extension_System_Pixel_Aspect_Ratio
+>0 guid C6BD9450-867F-4907-83A3-C77921B733AD ASF_Payload_Extension_System_Sample_Duration
+>0 guid 6698B84E-0AFA-4330-AEB2-1C0A98D7A44D ASF_Payload_Extension_System_Encryption_Sample_ID
+>0 guid 00E1AF06-7BEC-11D1-A582-00C04FC29CFB ASF_Payload_Extension_System_Degradable_JPEG
+
+0 name asf-object
+>0 use asf-name
+#>>16 lequad >0 (size %lld) [
+>>16 lequad >0
+>>>(16.q) use asf-object
+#>>16 lequad 0 ]
+
+# Microsoft Advanced Streaming Format (ASF) <mpruett@sgi.com>
+0 guid 75B22630-668E-11CF-A6D9-00AA0062CE6C Microsoft ASF
+!:mime video/x-ms-asf
+#>16 lequad >0 (size %lld
+#>>24 lelong x \b, %d header objects)
+>16 lequad >0
+>>30 use asf-object
+>>(16.q) use asf-object
diff --git a/contrib/libs/libmagic/magic/Magdir/assembler b/contrib/libs/libmagic/magic/Magdir/assembler
new file mode 100644
index 0000000000..805a326beb
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/assembler
@@ -0,0 +1,18 @@
+#------------------------------------------------------------------------------
+# $File: assembler,v 1.6 2013/12/11 14:14:20 christos Exp $
+# make: file(1) magic for assembler source
+#
+0 regex \^[\040\t]{0,50}\\.asciiz assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.byte assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.even assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.globl assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.text assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.file assembler source text
+!:mime text/x-asm
+0 regex \^[\040\t]{0,50}\\.type assembler source text
+!:mime text/x-asm
diff --git a/contrib/libs/libmagic/magic/Magdir/asterix b/contrib/libs/libmagic/magic/Magdir/asterix
new file mode 100644
index 0000000000..a9ea885cdb
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/asterix
@@ -0,0 +1,18 @@
+
+#------------------------------------------------------------------------------
+# $File: asterix,v 1.5 2009/09/19 16:28:08 christos Exp $
+# asterix: file(1) magic for Aster*x; SunOS 5.5.1 gave the 4-character
+# strings as "long" - we assume they're just strings:
+# From: guy@netapp.com (Guy Harris)
+#
+0 string *STA Aster*x
+>7 string WORD Words Document
+>7 string GRAP Graphic
+>7 string SPRE Spreadsheet
+>7 string MACR Macro
+0 string 2278 Aster*x Version 2
+>29 byte 0x36 Words Document
+>29 byte 0x35 Graphic
+>29 byte 0x32 Spreadsheet
+>29 byte 0x38 Macro
+
diff --git a/contrib/libs/libmagic/magic/Magdir/att3b b/contrib/libs/libmagic/magic/Magdir/att3b
new file mode 100644
index 0000000000..b83ae2ec08
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/att3b
@@ -0,0 +1,41 @@
+
+#------------------------------------------------------------------------------
+# $File: att3b,v 1.10 2017/03/17 21:35:28 christos Exp $
+# att3b: file(1) magic for AT&T 3B machines
+#
+# The `versions' should be un-commented if they work for you.
+# (Was the problem just one of endianness?)
+#
+# 3B20
+#
+# The 3B20 conflicts with SCCS.
+#0 beshort 0550 3b20 COFF executable
+#>12 belong >0 not stripped
+#>22 beshort >0 - version %d
+#0 beshort 0551 3b20 COFF executable (TV)
+#>12 belong >0 not stripped
+#>22 beshort >0 - version %d
+#
+# WE32K
+#
+0 beshort 0560 WE32000 COFF
+>18 beshort ^00000020 object
+>18 beshort &00000020 executable
+>12 belong >0 not stripped
+>18 beshort ^00010000 N/A on 3b2/300 w/paging
+>18 beshort &00020000 32100 required
+>18 beshort &00040000 and MAU hardware required
+>20 beshort 0407 (impure)
+>20 beshort 0410 (pure)
+>20 beshort 0413 (demand paged)
+>20 beshort 0443 (target shared library)
+>22 beshort >0 - version %d
+0 beshort 0561 WE32000 COFF executable (TV)
+>12 belong >0 not stripped
+#>18 beshort &00020000 - 32100 required
+#>18 beshort &00040000 and MAU hardware required
+#>22 beshort >0 - version %d
+#
+# core file for 3b2
+0 string \000\004\036\212\200 3b2 core file
+>364 string >\0 of '%s'
diff --git a/contrib/libs/libmagic/magic/Magdir/audio b/contrib/libs/libmagic/magic/Magdir/audio
new file mode 100644
index 0000000000..55c5cd0ad2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/audio
@@ -0,0 +1,1291 @@
+
+#------------------------------------------------------------------------------
+# $File: audio,v 1.127 2023/03/05 20:15:49 christos Exp $
+# audio: file(1) magic for sound formats (see also "iff")
+#
+# Jan Nicolai Langfeldt (janl@ifi.uio.no), Dan Quinlan (quinlan@yggdrasil.com),
+# and others
+#
+
+# Sun/NeXT audio data
+0 string .snd Sun/NeXT audio data:
+>12 belong 1 8-bit ISDN mu-law,
+!:mime audio/basic
+>12 belong 2 8-bit linear PCM [REF-PCM],
+!:mime audio/basic
+>12 belong 3 16-bit linear PCM,
+!:mime audio/basic
+>12 belong 4 24-bit linear PCM,
+!:mime audio/basic
+>12 belong 5 32-bit linear PCM,
+!:mime audio/basic
+>12 belong 6 32-bit IEEE floating point,
+!:mime audio/basic
+>12 belong 7 64-bit IEEE floating point,
+!:mime audio/basic
+>12 belong 8 Fragmented sample data,
+>12 belong 10 DSP program,
+>12 belong 11 8-bit fixed point,
+>12 belong 12 16-bit fixed point,
+>12 belong 13 24-bit fixed point,
+>12 belong 14 32-bit fixed point,
+>12 belong 18 16-bit linear with emphasis,
+>12 belong 19 16-bit linear compressed,
+>12 belong 20 16-bit linear with emphasis and compression,
+>12 belong 21 Music kit DSP commands,
+>12 belong 23 8-bit ISDN mu-law compressed (CCITT G.721 ADPCM voice enc.),
+!:mime audio/x-adpcm
+>12 belong 24 compressed (8-bit CCITT G.722 ADPCM)
+>12 belong 25 compressed (3-bit CCITT G.723.3 ADPCM),
+>12 belong 26 compressed (5-bit CCITT G.723.5 ADPCM),
+>12 belong 27 8-bit A-law (CCITT G.711),
+>20 belong 1 mono,
+>20 belong 2 stereo,
+>20 belong 4 quad,
+>16 belong >0 %d Hz
+
+# DEC systems (e.g. DECstation 5000) use a variant of the Sun/NeXT format
+# that uses little-endian encoding and has a different magic number
+0 lelong 0x0064732E DEC audio data:
+>12 lelong 1 8-bit ISDN mu-law,
+!:mime audio/x-dec-basic
+>12 lelong 2 8-bit linear PCM [REF-PCM],
+!:mime audio/x-dec-basic
+>12 lelong 3 16-bit linear PCM,
+!:mime audio/x-dec-basic
+>12 lelong 4 24-bit linear PCM,
+!:mime audio/x-dec-basic
+>12 lelong 5 32-bit linear PCM,
+!:mime audio/x-dec-basic
+>12 lelong 6 32-bit IEEE floating point,
+!:mime audio/x-dec-basic
+>12 lelong 7 64-bit IEEE floating point,
+!:mime audio/x-dec-basic
+>12 belong 8 Fragmented sample data,
+>12 belong 10 DSP program,
+>12 belong 11 8-bit fixed point,
+>12 belong 12 16-bit fixed point,
+>12 belong 13 24-bit fixed point,
+>12 belong 14 32-bit fixed point,
+>12 belong 18 16-bit linear with emphasis,
+>12 belong 19 16-bit linear compressed,
+>12 belong 20 16-bit linear with emphasis and compression,
+>12 belong 21 Music kit DSP commands,
+>12 lelong 23 8-bit ISDN mu-law compressed (CCITT G.721 ADPCM voice enc.),
+!:mime audio/x-dec-basic
+>12 belong 24 compressed (8-bit CCITT G.722 ADPCM)
+>12 belong 25 compressed (3-bit CCITT G.723.3 ADPCM),
+>12 belong 26 compressed (5-bit CCITT G.723.5 ADPCM),
+>12 belong 27 8-bit A-law (CCITT G.711),
+>20 lelong 1 mono,
+>20 lelong 2 stereo,
+>20 lelong 4 quad,
+>16 lelong >0 %d Hz
+
+# Creative Labs AUDIO stuff
+0 string MThd Standard MIDI data
+!:mime audio/midi
+>8 beshort x (format %d)
+>10 beshort x using %d track
+>10 beshort >1 \bs
+>12 beshort&0x7fff x at 1/%d
+>12 beshort&0x8000 >0 SMPTE
+
+0 string CTMF Creative Music (CMF) data
+!:mime audio/x-unknown
+0 string SBI SoundBlaster instrument data
+!:mime audio/x-unknown
+0 string Creative\ Voice\ File Creative Labs voice data
+!:mime audio/x-unknown
+# is this next line right? it came this way...
+>19 byte 0x1A
+>23 byte >0 - version %d
+>22 byte >0 \b.%d
+
+# first entry is also the string "NTRK"
+0 belong 0x4e54524b MultiTrack sound data
+>4 belong x - version %d
+
+# Extended MOD format (*.emd) (Greg Roelofs, newt@uchicago.edu); NOT TESTED
+# [based on posting 940824 by "Dirk/Elastik", husberg@lehtori.cc.tut.fi]
+0 string EMOD Extended MOD sound data,
+>4 byte&0xf0 x version %d
+>4 byte&0x0f x \b.%d,
+>45 byte x %d instruments
+>83 byte 0 (module)
+>83 byte 1 (song)
+
+# Real Audio (Magic .ra\0375)
+0 belong 0x2e7261fd RealAudio sound file
+!:mime audio/x-pn-realaudio
+0 string .RMF\0\0\0 RealMedia file
+!:mime application/vnd.rn-realmedia
+#video/x-pn-realvideo
+#video/vnd.rn-realvideo
+#application/vnd.rn-realmedia
+# sigh, there are many mimes for that but the above are the most common.
+
+# MTM/669/FAR/S3M/ULT/XM format checking [Aaron Eppert, aeppert@dialin.ind.net]
+# Oct 31, 1995
+# fixed by <doj@cubic.org> 2003-06-24
+# Too short...
+#0 string MTM MultiTracker Module sound file
+#0 string if Composer 669 Module sound data
+#0 string JN Composer 669 Module sound data (extended format)
+0 string MAS_U ULT(imate) Module sound data
+
+#0 string FAR Module sound data
+#>4 string >\15 Title: "%s"
+
+0x2c string SCRM ScreamTracker III Module sound data
+>0 string >\0 Title: "%s"
+!:mime audio/x-s3m
+
+# .stm before it got above .s3m extension
+0x16 string \!Scream\! ScreamTracker Module sound data
+>0 string >\0 Title: "%s"
+
+# Gravis UltraSound patches
+# From <ache@nagual.ru>
+
+0 string GF1PATCH110\0ID#000002\0 GUS patch
+0 string GF1PATCH100\0ID#000002\0 Old GUS patch
+
+# mime types according to http://www.geocities.com/nevilo/mod.htm:
+# audio/it .it
+# audio/x-zipped-it .itz
+# audio/xm fasttracker modules
+# audio/x-s3m screamtracker modules
+# audio/s3m screamtracker modules
+# audio/x-zipped-mod mdz
+# audio/mod mod
+# audio/x-mod All modules (mod, s3m, 669, mtm, med, xm, it, mdz, stm, itz, xmz, s3z)
+
+#
+# Taken from loader code from mikmod version 2.14
+# by Steve McIntyre (stevem@chiark.greenend.org.uk)
+# <doj@cubic.org> added title printing on 2003-06-24
+0 string MAS_UTrack_V00
+>14 string >/0 ultratracker V1.%.1s module sound data
+!:mime audio/x-mod
+#audio/x-tracker-module
+
+0 string UN05 MikMod UNI format module sound data
+
+0 string Extended\ Module: Fasttracker II module sound data
+!:mime audio/x-mod
+#audio/x-tracker-module
+>17 string >\0 Title: "%s"
+
+21 string/c =!SCREAM! Screamtracker 2 module sound data
+!:mime audio/x-mod
+#audio/x-screamtracker-module
+21 string BMOD2STM Screamtracker 2 module sound data
+!:mime audio/x-mod
+#audio/x-screamtracker-module
+
+1080 string \!PM! 4-channel Protracker module sound data
+!:mime audio/x-mod
+#audio/x-protracker-module
+>0 string >\0 Title: "%s"
+
+1080 string M.K. 4-channel Protracker module sound data
+!:mime audio/x-mod
+#audio/x-protracker-module
+>0 string >\0 Title: "%s"
+
+1080 string M!K! 4-channel Protracker module sound data
+!:mime audio/x-mod
+#audio/x-protracker-module
+>0 string >\0 Title: "%s"
+
+1080 string FLT4 4-channel Startracker module sound data
+!:mime audio/x-mod
+#audio/x-startracker-module
+>0 string >\0 Title: "%s"
+
+1080 string FLT8 8-channel Startracker module sound data
+!:mime audio/x-mod
+#audio/x-startracker-module
+>0 string >\0 Title: "%s"
+
+1080 string 4CHN 4-channel Fasttracker module sound data
+!:mime audio/x-mod
+#audio/x-fasttracker-module
+>0 string >\0 Title: "%s"
+
+1080 string 6CHN 6-channel Fasttracker module sound data
+!:mime audio/x-mod
+#audio/x-fasttracker-module
+>0 string >\0 Title: "%s"
+
+1080 string 8CHN 8-channel Fasttracker module sound data
+!:mime audio/x-mod
+#audio/x-fasttracker-module
+>0 string >\0 Title: "%s"
+
+1080 string CD81 8-channel Octalyser module sound data
+!:mime audio/x-mod
+#audio/x-octalysertracker-module
+>0 string >\0 Title: "%s"
+
+1080 string OKTA 8-channel Octalyzer module sound data
+!:mime audio/x-mod
+#audio/x-octalysertracker-module
+>0 string >\0 Title: "%s"
+
+# Not good enough.
+#1082 string CH
+#>1080 string >/0 %.2s-channel Fasttracker "oktalyzer" module sound data
+1080 string 16CN 16-channel Taketracker module sound data
+!:mime audio/x-mod
+#audio/x-taketracker-module
+>0 string >\0 Title: "%s"
+1080 string 32CN 32-channel Taketracker module sound data
+!:mime audio/x-mod
+#audio/x-taketracker-module
+>0 string >\0 Title: "%s"
+
+# TOC sound files -Trevor Johnson <trevor@jpj.net>
+#
+0 string TOC TOC sound file
+
+# sidfiles <pooka@iki.fi>
+# added name,author,(c) and new RSID type by <doj@cubic.org> 2003-06-24
+0 string SIDPLAY\ INFOFILE Sidplay info file
+
+0 string PSID PlaySID v2.2+ (AMIGA) sidtune
+>4 beshort >0 w/ header v%d,
+>14 beshort =1 single song,
+>14 beshort >1 %d songs,
+>16 beshort >0 default song: %d
+>0x16 string >\0 name: "%s"
+>0x36 string >\0 author: "%s"
+>0x56 string >\0 copyright: "%s"
+
+0 string RSID RSID sidtune PlaySID compatible
+>4 beshort >0 w/ header v%d,
+>14 beshort =1 single song,
+>14 beshort >1 %d songs,
+>16 beshort >0 default song: %d
+>0x16 string >\0 name: "%s"
+>0x36 string >\0 author: "%s"
+>0x56 string >\0 copyright: "%s"
+
+# IRCAM sound files - Michael Pruett <michael@68k.org>
+# http://www-mmsp.ece.mcgill.ca/documents/AudioFormats/IRCAM/IRCAM.html
+0 belong 0x64a30100 IRCAM file (VAX little-endian)
+0 belong 0x0001a364 IRCAM file (VAX big-endian)
+0 belong 0x64a30200 IRCAM file (Sun big-endian)
+0 belong 0x0002a364 IRCAM file (Sun little-endian)
+0 belong 0x64a30300 IRCAM file (MIPS little-endian)
+0 belong 0x0003a364 IRCAM file (MIPS big-endian)
+0 belong 0x64a30400 IRCAM file (NeXT big-endian)
+0 belong 0x64a30400 IRCAM file (NeXT big-endian)
+0 belong 0x0004a364 IRCAM file (NeXT little-endian)
+
+# NIST SPHERE <mpruett@sgi.com>
+0 string NIST_1A\n\ \ \ 1024\n NIST SPHERE file
+
+# Sample Vision <mpruett@sgi.com>
+0 string SOUND\ SAMPLE\ DATA\ Sample Vision file
+
+# Audio Visual Research <tonigonenstein@users.sourceforge.net>
+0 string 2BIT Audio Visual Research file,
+>12 beshort =0 mono,
+>12 beshort =-1 stereo,
+>14 beshort x %d bits
+>16 beshort =0 unsigned,
+>16 beshort =-1 signed,
+>22 belong&0x00ffffff x %d Hz,
+>18 beshort =0 no loop,
+>18 beshort =-1 loop,
+>21 ubyte <128 note %d,
+>22 byte =0 replay 5.485 KHz
+>22 byte =1 replay 8.084 KHz
+>22 byte =2 replay 10.971 KHz
+>22 byte =3 replay 16.168 KHz
+>22 byte =4 replay 21.942 KHz
+>22 byte =5 replay 32.336 KHz
+>22 byte =6 replay 43.885 KHz
+>22 byte =7 replay 47.261 KHz
+
+# SGI SoundTrack <mpruett@sgi.com>
+0 string _SGI_SoundTrack SGI SoundTrack project file
+# ID3 version 2 tags <waschk@informatik.uni-rostock.de>
+0 string ID3 Audio file with ID3 version 2
+>3 byte x \b.%d
+>4 byte x \b.%d
+>>5 byte &0x80 \b, unsynchronized frames
+>>5 byte &0x40 \b, extended header
+>>5 byte &0x20 \b, experimental
+>>5 byte &0x10 \b, footer present
+>(6.I+10) indirect x \b, contains:
+
+# NSF (NES sound file) magic
+0 string NESM\x1a NES Sound File
+>14 string >\0 ("%s" by
+>46 string >\0 %s, copyright
+>78 string >\0 %s),
+>5 byte x version %d,
+>6 byte x %d tracks,
+>122 byte&0x2 =1 dual PAL/NTSC
+>122 byte&0x1 =1 PAL
+>122 byte&0x1 =0 NTSC
+
+# NSFE (Extended NES sound file) magic
+# http://slickproductions.org/docs/NSF/nsfespec.txt
+# From: David Pflug <david@pflug.email>
+0 string NSFE Extended NES Sound File
+>48 search/0x1000 auth
+>>&0 string >\0 ("%s"
+>>>&1 string >\0 by %s
+>>>>&1 string >\0 \b, copyright %s
+>>>>>&1 string >\0 \b, ripped by %s
+>20 byte x \b), %d tracks,
+>18 byte&0x2 =1 dual PAL/NTSC
+>18 byte&0x2 =0
+>>18 byte&0x1 =1 PAL
+>>18 byte&0x1 =0 NTSC
+
+# Type: SNES SPC700 sound files
+# From: Josh Triplett <josh@freedesktop.org>
+0 string SNES-SPC700\ Sound\ File\ Data\ v SNES SPC700 sound file
+>&0 string 0.30 \b, version %s
+>>0x23 byte 0x1B \b, without ID666 tag
+>>0x23 byte 0x1A \b, with ID666 tag
+>>>0x2E string >\0 \b, song "%.32s"
+>>>0x4E string >\0 \b, game "%.32s"
+
+# Impulse tracker module (audio/x-it)
+0 string IMPM Impulse Tracker module sound data -
+!:mime audio/x-mod
+>4 string >\0 "%s"
+>40 leshort !0 compatible w/ITv%x
+>42 leshort !0 created w/ITv%x
+
+# Imago Orpheus module (audio/x-imf)
+60 string IM10 Imago Orpheus module sound data -
+>0 string >\0 "%s"
+
+# From <collver1@attbi.com>
+# These are the /etc/magic entries to decode modules, instruments, and
+# samples in Impulse Tracker's native format.
+
+0 string IMPS Impulse Tracker Sample
+>18 byte &2 16 bit
+>18 byte ^2 8 bit
+>18 byte &4 stereo
+>18 byte ^4 mono
+0 string IMPI Impulse Tracker Instrument
+>28 leshort !0 ITv%x
+>30 byte !0 %d samples
+
+# Yamaha TX Wave: file(1) magic for Yamaha TX Wave audio files
+# From <collver1@attbi.com>
+0 string LM8953 Yamaha TX Wave
+>22 byte 0x49 looped
+>22 byte 0xC9 non-looped
+>23 byte 1 33kHz
+>23 byte 2 50kHz
+>23 byte 3 16kHz
+
+# scream tracker: file(1) magic for Scream Tracker sample files
+#
+# From <collver1@attbi.com>
+76 string SCRS Scream Tracker Sample
+>0 byte 1 sample
+>0 byte 2 adlib melody
+>0 byte >2 adlib drum
+>31 byte &2 stereo
+>31 byte ^2 mono
+>31 byte &4 16bit little endian
+>31 byte ^4 8bit
+>30 byte 0 unpacked
+>30 byte 1 packed
+
+# audio
+# From: Cory Dikkers <cdikkers@swbell.net>
+0 string MMD0 MED music file, version 0
+0 string MMD1 OctaMED Pro music file, version 1
+0 string MMD3 OctaMED Soundstudio music file, version 3
+0 string OctaMEDCmpr OctaMED Soundstudio compressed file
+0 string MED MED_Song
+0 string SymM Symphonie SymMOD music file
+#
+# Track Length (TRL), Tracks (TRK), Samples (SMP), Subsongs (SS)
+# http://lclevy.free.fr/exotica/ahx/ahxformat.txt
+0 string THX AHX version
+>3 byte =0 1 module data
+>3 byte =1 2 module data
+>11 ubyte x TRK: %u
+>10 ubyte x TRL: %u
+>12 ubyte x SMP: %u
+>13 ubyte x SS: %u
+>(4.H) string x Title: "%.128s"
+
+# header is mostly AHX format
+0 string HVL
+>3 byte <2 Hively Tracker Song
+>3 byte =0 v1 module data
+>3 byte =1 v2 module data
+>11 ubyte x TRK: %u
+>10 ubyte x TRL: %u
+>12 ubyte x SMP: %u
+>13 ubyte x SS: %u
+>8 ubyte/4 =0 CHN: 4
+>8 ubyte/4 >0 CHN: 4+%u
+#>-0 offset <0xffff
+>(4.H) string x Title: "%.128s"
+
+#
+0 string OKTASONG Oktalyzer module data
+#
+0 string DIGI\ Booster\ module\0 %s
+>20 byte >0 %c
+>>21 byte >0 \b%c
+>>>22 byte >0 \b%c
+>>>>23 byte >0 \b%c
+>610 string >\0 \b, "%s"
+#
+0 string DBM0 DIGI Booster Pro Module
+>4 byte >0 V%X.
+>>5 byte x \b%02X
+>16 string >\0 \b, "%s"
+#
+0 string FTMN FaceTheMusic module
+>16 string >\0d \b, "%s"
+
+# From: <doj@cubic.org> 2003-06-24
+0 string AMShdr\32 Velvet Studio AMS Module v2.2
+0 string Extreme Extreme Tracker AMS Module v1.3
+0 string DDMF Xtracker DMF Module
+>4 byte x v%i
+>0xD string >\0 Title: "%s"
+>0x2B string >\0 Composer: "%s"
+0 string DSM\32 Dynamic Studio Module DSM
+0 string SONG DigiTrekker DTM Module
+0 string DMDL DigiTrakker MDL Module
+0 string PSM\32 Protracker Studio PSM Module
+44 string PTMF Poly Tracker PTM Module
+>0 string >\32 Title: "%s"
+0 string MT20 MadTracker 2.0 Module MT2
+0 string RAD\40by\40REALiTY!! RAD Adlib Tracker Module RAD
+0 string RTMM RTM Module
+0x426 string MaDoKaN96 XMS Adlib Module
+>0 string >\0 Composer: "%s"
+0 string AMF AMF Module
+>4 string >\0 Title: "%s"
+0 string MODINFO1 Open Cubic Player Module Information MDZ
+0 string Extended\40Instrument: Fast Tracker II Instrument
+
+# From: Takeshi Hamasaki <hma@syd.odn.ne.jp>
+# NOA Nancy Codec file
+0 string \210NOA\015\012\032 NOA Nancy Codec Movie file
+# Yamaha SMAF format
+0 string MMMD Yamaha SMAF file
+# Sharp Jisaku Melody format for PDC
+0 string \001Sharp\040JisakuMelody SHARP Cell-Phone ringing Melody
+>20 string Ver01.00 Ver. 1.00
+>>32 byte x , %d tracks
+
+# Free lossless audio codec <http://flac.sourceforge.net>
+# From: Przemyslaw Augustyniak <silvathraec@rpg.pl>
+0 string fLaC FLAC audio bitstream data
+!:mime audio/flac
+>4 byte&0x7f >0 \b, unknown version
+>4 byte&0x7f 0 \b
+# some common bits/sample values
+>>20 beshort&0x1f0 0x030 \b, 4 bit
+>>20 beshort&0x1f0 0x050 \b, 6 bit
+>>20 beshort&0x1f0 0x070 \b, 8 bit
+>>20 beshort&0x1f0 0x0b0 \b, 12 bit
+>>20 beshort&0x1f0 0x0f0 \b, 16 bit
+>>20 beshort&0x1f0 0x170 \b, 24 bit
+>>20 byte&0xe 0x0 \b, mono
+>>20 byte&0xe 0x2 \b, stereo
+>>20 byte&0xe 0x4 \b, 3 channels
+>>20 byte&0xe 0x6 \b, 4 channels
+>>20 byte&0xe 0x8 \b, 5 channels
+>>20 byte&0xe 0xa \b, 6 channels
+>>20 byte&0xe 0xc \b, 7 channels
+>>20 byte&0xe 0xe \b, 8 channels
+# sample rates derived from known oscillator frequencies;
+# 24.576 MHz (video/fs=48kHz), 22.5792 (audio/fs=44.1kHz) and
+# 16.384 (other/fs=32kHz).
+>>17 belong&0xfffff0 0x02b110 \b, 11.025 kHz
+>>17 belong&0xfffff0 0x03e800 \b, 16 kHz
+>>17 belong&0xfffff0 0x056220 \b, 22.05 kHz
+>>17 belong&0xfffff0 0x05dc00 \b, 24 kHz
+>>17 belong&0xfffff0 0x07d000 \b, 32 kHz
+>>17 belong&0xfffff0 0x0ac440 \b, 44.1 kHz
+>>17 belong&0xfffff0 0x0bb800 \b, 48 kHz
+>>17 belong&0xfffff0 0x0fa000 \b, 64 kHz
+>>17 belong&0xfffff0 0x158880 \b, 88.2 kHz
+>>17 belong&0xfffff0 0x177000 \b, 96 kHz
+>>17 belong&0xfffff0 0x1f4000 \b, 128 kHz
+>>17 belong&0xfffff0 0x2b1100 \b, 176.4 kHz
+>>17 belong&0xfffff0 0x2ee000 \b, 192 kHz
+>>17 belong&0xfffff0 0x3e8000 \b, 256 kHz
+>>17 belong&0xfffff0 0x562200 \b, 352.8 kHz
+>>17 belong&0xfffff0 0x5dc000 \b, 384 kHz
+>>21 byte&0xf >0 \b, >4G samples
+>>21 byte&0xf 0 \b
+>>>22 belong >0 \b, %u samples
+>>>22 belong 0 \b, length unknown
+
+# (ISDN) VBOX voice message file (Wolfram Kleff)
+0 string VBOX VBOX voice message data
+
+# ReBorn Song Files (.rbs)
+# David J. Singer <doc@deadvirgins.org.uk>
+8 string RB40 RBS Song file
+>29 string ReBorn created by ReBorn
+>37 string Propellerhead created by ReBirth
+
+# Synthesizer Generator and Kimwitu share their file format
+0 string A#S#C#S#S#L#V#3 Synthesizer Generator or Kimwitu data
+# Kimwitu++ uses a slightly different magic
+0 string A#S#C#S#S#L#HUB Kimwitu++ data
+
+# From "Simon Hosie
+0 string TFMX-SONG TFMX module sound data
+
+# Monkey's Audio compressed audio format (.ape)
+# From danny.milo@gmx.net (Danny Milosavljevic)
+# New version from Abel Cheung <abel (@) oaka.org>
+0 string MAC\040 Monkey's Audio compressed format
+!:mime audio/x-ape
+>4 uleshort >0x0F8B version %d
+>>(0x08.l) uleshort =1000 with fast compression
+>>(0x08.l) uleshort =2000 with normal compression
+>>(0x08.l) uleshort =3000 with high compression
+>>(0x08.l) uleshort =4000 with extra high compression
+>>(0x08.l) uleshort =5000 with insane compression
+>>(0x08.l+18) uleshort =1 \b, mono
+>>(0x08.l+18) uleshort =2 \b, stereo
+>>(0x08.l+20) ulelong x \b, sample rate %d
+>4 uleshort <0x0F8C version %d
+>>6 uleshort =1000 with fast compression
+>>6 uleshort =2000 with normal compression
+>>6 uleshort =3000 with high compression
+>>6 uleshort =4000 with extra high compression
+>>6 uleshort =5000 with insane compression
+>>10 uleshort =1 \b, mono
+>>10 uleshort =2 \b, stereo
+>>12 ulelong x \b, sample rate %d
+
+# adlib sound files
+# From: Alex Myczko <alex@aiei.ch>
+
+# https://github.com/rerrahkr/BambooTracker
+0 string BambooTracker BambooTracker
+>13 string Mod Module
+>13 string Ist Instrument
+>13 string Bnk Bank
+>22 byte x \b, version %u
+>21 byte x \b.%u
+>20 byte x \b.%u
+
+0 string CC2x CheeseCutter 2 song
+
+0 string RAWADATA RdosPlay RAW
+
+1068 string RoR AMUSIC Adlib Tracker
+
+0 string JCH EdLib
+
+0 string mpu401tr MPU-401 Trakker
+
+0 string SAdT Surprise! Adlib Tracker
+>4 byte x Version %d
+
+0 string XAD! eXotic ADlib
+
+0 string ofTAZ! eXtra Simple Music
+
+0 string FMK! FM Kingtracker Song
+
+0 string DFM DFM Song
+
+0 string \<CUD-FM-File\> CFF Song
+
+0 string _A2module A2M Song
+
+# Spectrum 128 tunes (.ay files).
+# From: Emanuel Haupt <ehaupt@critical.ch>
+0 string ZXAYEMUL Spectrum 128 tune
+
+0 string \0BONK BONK,
+#>5 byte x version %d
+>14 byte x %d channel(s),
+>15 byte =1 lossless,
+>15 byte =0 lossy,
+>16 byte x mid-side
+
+384 string LockStream LockStream Embedded file (mostly MP3 on old Nokia phones)
+
+# format VQF (proprietary codec for sound)
+# some infos on the header file available at :
+# http://www.twinvq.org/english/technology_format.html
+0 string TWIN97012000 VQF data
+>27 short 0 \b, Mono
+>27 short 1 \b, Stereo
+>31 short >0 \b, %d kbit/s
+>35 short >0 \b, %d kHz
+
+# Nelson A. de Oliveira (naoliv@gmail.com)
+# .eqf
+0 string Winamp\ EQ\ library\ file %s
+# it will match only versions like v<digit>.<digit>
+# Since I saw only eqf files with version v1.1 I think that it's OK
+>23 string x \b%.4s
+# .preset
+0 string [Equalizer\ preset] XMMS equalizer preset
+# .m3u
+0 search/1 #EXTM3U M3U playlist text
+# .pls
+0 search/1 [playlist] PLS playlist text
+# licq.conf
+1 string [licq] LICQ configuration file
+
+# Atari ST audio files by Dirk Jagdmann <doj@cubic.org>
+# NOTE: Most SNDH music is packed using ICE, which has
+# magic numbers "ICE!" and "Ice!". Some SNDH music is
+# not packed, so we check for both packed and unpacked.
+12 string SNDH SNDH Atari ST music
+0 belong&0xFFDFDFFF 0x49434521
+>14 search/40 NDH SNDH Atari ST music
+>14 search/40 TITL SNDH Atari ST music
+0 string SC68\ Music-file\ /\ (c)\ (BeN)jami sc68 Atari ST music
+
+# musepak support From: "Jiri Pejchal" <jiri.pejchal@gmail.com>
+0 string MP+ Musepack audio (MP+)
+!:mime audio/x-musepack
+>3 byte 255 \b, SV pre8
+>3 byte&0xF 0x6 \b, SV 6
+>3 byte&0xF 0x8 \b, SV 8
+>3 byte&0xF 0x7 \b, SV 7
+>>3 byte&0xF0 0x0 \b.0
+>>3 byte&0xF0 0x10 \b.1
+>>3 byte&0xF0 240 \b.15
+>>10 byte&0xF0 0x0 \b, no profile
+>>10 byte&0xF0 0x10 \b, profile 'Unstable/Experimental'
+>>10 byte&0xF0 0x50 \b, quality 0
+>>10 byte&0xF0 0x60 \b, quality 1
+>>10 byte&0xF0 0x70 \b, quality 2 (Telephone)
+>>10 byte&0xF0 0x80 \b, quality 3 (Thumb)
+>>10 byte&0xF0 0x90 \b, quality 4 (Radio)
+>>10 byte&0xF0 0xA0 \b, quality 5 (Standard)
+>>10 byte&0xF0 0xB0 \b, quality 6 (Xtreme)
+>>10 byte&0xF0 0xC0 \b, quality 7 (Insane)
+>>10 byte&0xF0 0xD0 \b, quality 8 (BrainDead)
+>>10 byte&0xF0 0xE0 \b, quality 9
+>>10 byte&0xF0 0xF0 \b, quality 10
+>>27 byte 0x0 \b, Buschmann 1.7.0-9, Klemm 0.90-1.05
+>>27 byte 102 \b, Beta 1.02
+>>27 byte 104 \b, Beta 1.04
+>>27 byte 105 \b, Alpha 1.05
+>>27 byte 106 \b, Beta 1.06
+>>27 byte 110 \b, Release 1.1
+>>27 byte 111 \b, Alpha 1.11
+>>27 byte 112 \b, Beta 1.12
+>>27 byte 113 \b, Alpha 1.13
+>>27 byte 114 \b, Beta 1.14
+>>27 byte 115 \b, Alpha 1.15
+
+0 string MPCK Musepack audio (MPCK)
+!:mime audio/x-musepack
+
+# IMY
+# from http://filext.com/detaillist.php?extdetail=IMY
+# https://cellphones.about.com/od/cellularfaqs/f/rf_imelody.htm
+# http://download.ncl.ie/doc/api/ie/ncl/media/music/IMelody.html
+# http://www.wx800.com/msg/download/irda/iMelody.pdf
+0 string BEGIN:IMELODY iMelody Ringtone Format
+
+# From: "Mateus Caruccio" <mateus@caruccio.com>
+# guitar pro v3,4,5 from http://filext.com/file-extension/gp3
+0 string \030FICHIER\ GUITAR\ PRO\ v3. Guitar Pro Ver. 3 Tablature
+
+# From: "Leslie P. Polzer" <leslie.polzer@gmx.net>
+60 string SONG SoundFX Module sound file
+
+# Type: Adaptive Multi-Rate Codec
+# URL: http://filext.com/detaillist.php?extdetail=AMR
+# From: Russell Coker <russell@coker.com.au>
+0 string #!AMR Adaptive Multi-Rate Codec (GSM telephony)
+!:mime audio/amr
+!:ext amr
+
+# Type: SuperCollider 3 Synth Definition File Format
+# From: Mario Lang <mlang@debian.org>
+0 string SCgf SuperCollider3 Synth Definition file,
+>4 belong x version %d
+
+# Type: True Audio Lossless Audio
+# URL: https://wiki.multimedia.cx/index.php?title=True_Audio
+# From: Mike Melanson <mike@multimedia.cx>
+0 string TTA1 True Audio Lossless Audio
+
+# Type: WavPack Lossless Audio
+# URL: https://wiki.multimedia.cx/index.php?title=WavPack
+# From: Mike Melanson <mike@multimedia.cx>
+0 string wvpk WavPack Lossless Audio
+
+# From Fabio R. Schmidlin <frs@pop.com.br>
+# VGM music file
+0 string Vgm\040
+>9 ubyte >0 VGM Video Game Music dump v
+!:mime audio/x-vgm
+!:ext vgm
+>>9 ubyte/16 >0 \b%d
+>>9 ubyte&0x0F x \b%d
+>>8 ubyte/16 x \b.%d
+>>8 ubyte&0x0F >0 \b%d
+#Get soundchips
+>>8 ubyte x \b, soundchip(s)=
+>>0x0C ulelong >0 SN76489 (PSG),
+>>0x10 ulelong >0 YM2413 (OPLL),
+>>0x2C ulelong >0 YM2612 (OPN2),
+>>0x30 ulelong >0 YM2151 (OPM),
+>>0x38 ulelong >0 Sega PCM,
+>>0x34 ulelong >0xC
+>>>0x40 ulelong >0 RF5C68 (PCM),
+>>0x34 ulelong >0x10
+>>>0x44 ulelong >0 YM2203 (OPN),
+>>0x34 ulelong >0x14
+>>>0x48 ulelong >0 YM2608 (OPNA),
+>>0x34 ulelong >0x18
+>>>0x4C lelong >0 YM2610 (OPNB),
+>>>0x4C lelong <0 YM2610B (OPNB+2FM),
+>>0x34 ulelong >0x1C
+>>>0x50 ulelong >0 YM3812 (OPL2),
+>>0x34 ulelong >0x20
+>>>0x54 ulelong >0 YM3526 (OPL),
+>>0x34 ulelong >0x24
+>>>0x58 ulelong >0 Y8950 (MSX-Audio),
+>>0x34 ulelong >0x28
+>>>0x5C ulelong >0 YMF262 (OPL3),
+>>0x34 ulelong >0x2C
+>>>0x60 ulelong >0 YMF278B (OPL4),
+>>0x34 ulelong >0x30
+>>>0x64 ulelong >0 YMF271 (OPX),
+>>0x34 ulelong >0x34
+>>>0x68 ulelong >0 YMZ280B (PCMD8),
+>>0x34 ulelong >0x38
+>>>0x6C ulelong >0 RF5C164 (PCM),
+>>0x34 ulelong >0x3C
+>>>0x70 ulelong >0 PWM,
+>>0x34 ulelong >0x40
+>>>0x74 ulelong >0
+>>>>0x78 ubyte 0x00 AY-3-8910,
+>>>>0x78 ubyte 0x01 AY-3-8912,
+>>>>0x78 ubyte 0x02 AY-3-8913,
+>>>>0x78 ubyte 0x03 AY-3-8930,
+>>>>0x78 ubyte 0x10 YM2149,
+>>>>0x78 ubyte 0x11 YM3439,
+>>>>0x78 ubyte 0x12 YMZ284,
+>>>>0x78 ubyte 0x13 YMZ294,
+# VGM 1.61
+>>0x34 ulelong >0x4C
+>>>0x80 ulelong >0 DMG,
+>>0x34 ulelong >0x50
+>>>0x84 lelong >0 NES APU,
+>>>0x84 lelong <0 NES APU with FDS,
+>>0x34 ulelong >0x54
+>>>0x88 ulelong >0 MultiPCM,
+>>0x34 ulelong >0x58
+>>>0x8C ulelong >0 uPD7759 (ADPCM Speech),
+>>0x34 ulelong >0x5C
+>>>0x90 ulelong >0 OKIM6258 (ADPCM Speech),
+>>0x34 ulelong >0x64
+>>>0x98 ulelong >0 OKIM6295 (ADPCM),
+>>0x34 ulelong >0x68
+>>>0x9C ulelong >0 K051649,
+>>0x34 ulelong >0x6C
+>>>0xA0 ulelong >0 K054539,
+>>0x34 ulelong >0x70
+>>>0xA4 ulelong >0 HuC6280,
+>>0x34 ulelong >0x74
+>>>0xA8 ulelong >0 C140,
+>>0x34 ulelong >0x78
+>>>0xAC ulelong >0 K053260,
+>>0x34 ulelong >0x7C
+>>>0xB0 ulelong >0 Pokey,
+>>0x34 ulelong >0x80
+>>>0xB4 ulelong >0 QSound,
+# VGM 1.71
+>>0x34 ulelong >0x84
+>>>0xB8 ulelong >0 SCSP,
+>>0x34 ulelong >0x8C
+>>>0xC0 ulelong >0 WonderSwan,
+>>0x34 ulelong >0x90
+>>>0xC4 ulelong >0 VSU,
+>>0x34 ulelong >0x94
+>>>0xC8 ulelong >0 SAA1099,
+>>0x34 ulelong >0x98
+>>>0xCC ulelong >0 ES5503 (DOC),
+>>0x34 ulelong >0x9C
+>>>0xD0 lelong >0 ES5505 (OTIS),
+>>>0xD0 lelong <0 ES5506 (OTTO),
+>>0x34 ulelong >0xA4
+>>>0xD8 ulelong >0 X1-010,
+>>0x34 ulelong >0xA8
+>>>0xDC ulelong >0 C352,
+>>0x34 ulelong >0xAC
+>>>0xE0 ulelong >0 GA20,
+
+# GVOX Encore file format
+# Since this is a proprietary file format and there is no publicly available
+# format specification, this is just based on induction
+#
+0 string SCOW
+>4 byte 0xc4 GVOX Encore music, version 5.0 or above
+>4 byte 0xc2 GVOX Encore music, version < 5.0
+
+0 string ZBOT
+>4 byte 0xc5 GVOX Encore music, version < 5.0
+
+# Summary: Garmin Voice Processing Module (WAVE audios)
+# From: Joerg Jenderek
+# URL: https://www.garmin.com/
+# Reference: http://www.poi-factory.com/node/19580
+# NOTE: there exist 2 other Garmin VPM formats
+0 string AUDIMG
+# skip text files starting with string "AUDIMG"
+>13 ubyte <13 Garmin Voice Processing Module
+!:mime audio/x-vpm-wav-garmin
+!:ext vpm
+# 3 bytes indicating the voice version (200,220)
+>>6 string x \b, version %3.3s
+# day of release (01-31)
+>>12 ubyte x \b, %.2d
+# month of release (01-12)
+>>13 ubyte x \b.%.2d
+# year of release (like 2006, 2007, 2008)
+>>14 uleshort x \b.%.4d
+# hour of release (0-23)
+>>11 ubyte x %.2d
+# minute of release (0-59)
+>>10 ubyte x \b:%.2d
+# second of release (0-59)
+>>9 ubyte x \b:%.2d
+# if you select a language like german on your garmin device
+# you can only select voice modules with corresponding language byte ID like 1
+>>18 ubyte x \b, language ID %d
+# structure for phrases/sentences?
+# number of voice sample in the 1st phrase?
+#>>19 uleshort x \b, %#x samples
+#>>>21 uleshort >0 \b, at %#4.4x
+#>>>(21.s) ubequad x %#llx
+# 2nd phrase?
+#>>23 uleshort x \b, %#x samples
+#>>>25 uleshort >0 \b, at %#4.4x
+#>>>(25.s) ubequad x %#llx
+# pointer to 1st audio WAV sample
+>>16 uleshort >0
+>>>(16.s) ulelong >0 \b, at %#x
+# WAV length
+# 1 space char after "bytes" to get phrase "bytes RIFF"
+>>>>(16.s+4) ulelong >0 %u bytes
+# look for magic
+>>>>>(&-8.l) string RIFF
+# determine type by ./riff
+>>>>>>&-4 indirect x
+# 2 - ~ 131 WAV samples following same way
+#
+# Summary: encrypted Garmin Voice Processing Module
+# From: Joerg Jenderek
+# URL: https://www.garmin.com/us/products/ontheroad/voicestudio
+# NOTE: Encrypted variant used in voices like DrNightmare, Elfred, Yeti.
+# There exist 2 other Garmin VPM formats
+0 ubequad 0xa141190fecc8ced6 Garmin Voice Processing Module (encrypted)
+!:mime audio/x-vpm-garmin
+!:ext vpm
+
+# From Martin Mueller Skarbiniks Pedersen
+0 string GDM
+>0x3 byte 0xFE General Digital Music.
+>0x4 string >\0 title: "%s"
+>0x24 string >\0 musician: "%s"
+>>0x44 beshort 0x0D0A
+>>>0x46 byte 0x1A
+>>>>0x47 string GMFS Version
+>>>>0x4B byte x %d.
+>>>>0x4C byte x \b%02d
+>>>>0x4D beshort 0x000 (2GDM v
+>>>>0x4F byte x \b%d.
+>>>>>0x50 byte x \b%d)
+
+0 string MTM Multitracker
+>0x3 byte/16 x Version %d.
+>0x3 byte&0x0F x \b%02d
+>>0x4 string >\0 title: "%s"
+
+0 string MO3
+>3 ubyte <6 MOdule with MP3
+>>3 byte 0 Version 0 (With MP3 and lossless)
+>>3 byte 1 Version 1 (With ogg and lossless)
+>>3 byte 3 Version 2.2
+>>3 byte 4 (With no LAME header)
+>>3 byte 5 Version 2.4
+
+0 string ADRVPACK AProSys module
+
+# ftp://ftp.modland.com/pub/documents/format_documentation/\
+# Art%20Of%20Noise%20(.aon).txt
+0 string AON
+>4 string "ArtOfNoise by Bastian Spiegel(twice/lego)"
+>0x2e string NAME Art of Noise Tracker Song
+>3 string <9
+>3 string 4 (4 voices)
+>3 string 8 (8 voices)
+>>0x36 string >\0 Title: "%s"
+
+0 string FAR
+>0x2c byte 0x0d
+>0x2d byte 0x0a
+>0x2e byte 0x1a
+>>0x3 byte 0xFE Farandole Tracker Song
+>>>0x31 byte/16 x Version %d.
+>>>0x31 byte&0x0F x \b%02d
+>>>>0x4 string >\0 \b, title: "%s"
+
+# magic for Klystrack, https://kometbomb.github.io/klystrack/
+# from Alex Myczko <alex@aiei.ch>
+0 string cyd!song Klystrack song
+>8 byte >0 \b, version %u
+>8 byte >26
+#>>9 byte x \b, channels %u
+#>>10 leshort x \b, time signature %u
+#>>12 leshort x \b, sequence step %u
+#>>14 byte x \b, instruments %u
+#>>15 leshort x \b, patterns %u
+#>>17 leshort x \b, sequences %u
+#>>19 leshort x \b, length %u
+#>>21 leshort x \b, loop point %u
+#>>23 byte x \b, master volume %u
+#>>24 byte x \b, song speed %u
+#>>25 byte x \b, song speed2 %u
+#>>26 byte x \b, song rate %u
+#>>27 belong x \b, flags %#x
+#>>31 byte x \b, multiplex period %u
+#>>32 byte x \b, pitch inaccuracy %u
+>>149 pstring x \b, title %s
+
+0 string cyd!inst Klystrack instrument
+
+# magic for WOPL instrument files, https://github.com/Wohlstand/OPL3BankEditor
+# see Specifications/WOPL-and-OPLI-Specification.txt
+
+0 string WOPL3-INST\0 WOPL instrument
+>11 leshort x \b, version %u
+0 string WOPL3-BANK\0 WOPL instrument bank
+>11 leshort x \b, version %u
+
+# AdLib/OPL instrument files. Format specifications on
+# http://www.shikadi.net/moddingwiki
+0 string Junglevision\ Patch\ File Junglevision instrument data
+0 string #OPL_II# DMX OP2 instrument data
+0 string IBK\x1a IBK instrument data
+0 string 2OP\x1a IBK instrument data, 2 operators
+0 string 4OP\x1a IBK instrument data, 4 operators
+2 string ADLIB- AdLib instrument data
+>0 byte x \b, version %u
+>1 byte x \b.%u
+
+# CRI ADX ADPCM audio
+# Used by various Sega games.
+# https://en.wikipedia.org/wiki/ADX_(file_format)
+# https://wiki.multimedia.cx/index.php/CRI_ADX_file
+# Added by David Korth <gerbilsoft@gerbilsoft.com>
+0x00 beshort 0x8000
+>(2.S-2) string (c)CRI CRI ADX ADPCM audio
+!:ext adx
+!:mime audio/x-adx
+!:strength +50
+>>0x12 byte x v%u
+>>0x04 byte 0x02 \b, pre-set prediction coefficients
+>>0x04 byte 0x03 \b, standard ADX
+>>0x04 byte 0x04 \b, exponential scale
+>>0x04 byte 0x10 \b, AHX (Dreamcast)
+>>0x04 byte 0x11 \b, AHX
+>>0x08 belong x \b, %u Hz
+>>0x12 byte 0x03
+>>>0x02 beshort >0x2B
+>>>>0x18 belong !0 \b, looping
+>>0x12 byte 0x04
+>>>0x02 beshort >0x37
+>>>>0x24 belong !0 \b, looping
+>>0x13 byte&0x08 0x08 \b, encrypted
+
+# Lossless audio (.la) (http://www.lossless-audio.com/)
+0 string LA
+>2 string 03 Lossless audio version 0.3
+>2 string 04 Lossless audio version 0.4
+
+# Sony PlayStation Audio (.xa)
+0 leshort 0x4158 Sony PlayStation Audio
+
+# Portable Sound Format
+# Used for audio rips for various consoles.
+# http://fileformats.archiveteam.org/wiki/Portable_Sound_Format
+# Added by David Korth <gerbilsoft@gerbilsoft.com>
+0 string PSF
+>3 byte 0x01
+>3 byte 0x02
+>3 byte 0x11
+>3 byte 0x12
+>3 byte 0x13
+>3 byte 0x21
+>3 byte 0x22
+>3 byte 0x23
+>3 byte 0x41
+>>0 string PSF Portable Sound Format
+!:mime audio/x-psf
+>>>3 byte 0x01 (Sony PlayStation)
+>>>3 byte 0x02 (Sony PlayStation 2)
+>>>3 byte 0x11 (Sega Saturn)
+>>>3 byte 0x12 (Sega Dreamcast)
+>>>3 byte 0x13 (Sega Mega Drive)
+>>>3 byte 0x21 (Nintendo 64)
+>>>3 byte 0x22 (Game Boy Advance)
+>>>3 byte 0x23 (Super NES)
+>>>3 byte 0x41 (Capcom QSound)
+
+# Atari 8-bit SAP audio format
+# http://asap.sourceforge.net/sap-format.html
+# Added by David Korth <gerbilsoft@gerbilsoft.com>
+0 string SAP\r\n Atari 8-bit SAP audio file
+!:mime audio/x-sap
+!:ext sap
+>5 search/1024 NAME
+>>&1 string x \b: %s
+>>5 search/1024 AUTHOR
+>>>&1 string x by %s
+
+# Nintendo Wii BRSTM audio format (fields)
+# NOTE: Assuming HEAD starts at 0x40.
+# FIXME: Replace 0x48 with HEAD offset plus 8.
+0 name nintendo-wii-brstm-fields
+>(0x10.L) string HEAD \b:
+>>(0x10.L+0x0C) belong x
+>>>(&-4.L+0x48) belong x
+>>>>&-4 byte 0 PCM, signed 8-bit,
+>>>>&-4 byte 1 PCM, signed 16-bit,
+>>>>&-4 byte 2 THP ADPCM,
+>>>>&-3 byte !0 looping,
+>>>>&-2 byte 1 mono
+>>>>&-2 byte 2 stereo
+>>>>&-2 byte 3 3 channels
+>>>>&-2 byte 4 quad
+>>>>&-2 byte >4 %u channels
+>>>>&0 beshort !0 %u Hz
+
+# Nintendo Wii BRSTM audio format
+# https://wiibrew.org/wiki/BRSTM_file
+# Added by David Korth <gerbilsoft@gerbilsoft.com>
+0 string RSTM Nintendo Wii BRSTM audio file
+!:mime audio/x-brstm
+!:ext brstm
+# Wii is big-endian, so default to BE.
+>4 beshort 0xFEFF
+>>0 use nintendo-wii-brstm-fields
+>4 leshort 0xFEFF
+>>0 use \^nintendo-wii-brstm-fields
+
+# Nintendo 3DS BCSTM audio format (fields)
+0 name nintendo-3ds-bcstm-fields
+>(0x18.l) string INFO \b:
+# INFO block: Stream information starts at 0x20 (minus 4 for the 'INFO' magic)
+>>&0x1C byte 0 PCM, signed 8-bit,
+>>&0x1C byte 1 PCM, signed 16-bit,
+>>&0x1C byte 2 DSP ADPCM,
+>>&0x1C byte 3 IMA ADPCM,
+>>&0x1D byte !0 looping,
+>>&0x1E byte 1 mono
+>>&0x1E byte 2 stereo
+>>&0x1E byte 3 3 channels
+>>&0x1E byte 4 quad
+>>&0x1E byte >4 %u channels
+>>&0x20 lelong !0 %u Hz
+
+# Nintendo 3DS BCSTM audio format
+# https://www.3dbrew.org/wiki/BCSTM
+# Added by David Korth <gerbilsoft@gerbilsoft.com>
+0 string CSTM Nintendo 3DS BCSTM audio file
+!:mime audio/x-bcstm
+!:ext bcstm
+# 3DS is little-endian, so default to LE.
+>4 leshort 0xFEFF
+>>0 use nintendo-3ds-bcstm-fields
+>4 beshort 0xFEFF
+>>0 use \^nintendo-3ds-bcstm-fields
+
+# Nintendo Wii U BFSTM audio format
+# http://mk8.tockdom.com/wiki/BFSTM_(File_Format)
+# NOTE: This format is very similar to BCSTM.
+# Added by David Korth <gerbilsoft@gerbilsoft.com>
+0 string FSTM Nintendo Wii U BFSTM audio file
+!:mime audio/x-bfstm
+!:ext bfstm
+# BFSTM is used on both Wii U (BE) and Switch (LE),
+# so default to LE.
+>4 leshort 0xFEFF
+>>0 use nintendo-3ds-bcstm-fields
+>4 beshort 0xFEFF
+>>0 use \^nintendo-3ds-bcstm-fields
+
+# Nintendo 3DS BCSTM audio format (fields)
+0 name nintendo-3ds-bcwav-fields
+>(0x18.l) string INFO \b:
+# INFO block (minus 4 for INFO magic)
+>>&0x4 byte 0 PCM, signed 8-bit,
+>>&0x4 byte 1 PCM, signed 16-bit,
+>>&0x4 byte 2 DSP ADPCM,
+>>&0x4 byte 3 IMA ADPCM,
+>>&0x5 byte !0 looping,
+>>&0x8 lelong x stereo
+>>&0x8 lelong !0 %u Hz
+
+# Nintendo 3DS BCWAV audio format
+# https://www.3dbrew.org/wiki/BCWAV
+# Added by David Korth <gerbilsoft@gerbilsoft.com>
+0 string CWAV Nintendo 3DS BCWAV audio file
+!:mime audio/x-bcwav
+!:ext bcwav
+# 3DS is little-endian, so default to LE.
+>4 leshort 0xFEFF
+>>0 use nintendo-3ds-bcwav-fields
+>4 beshort 0xFEFF
+>>0 use \^nintendo-3ds-bcwav-fields
+
+# Philips DSDIFF audio format (Direct Stream Digital Interchange File Format)
+# Used for DSD audio recordings and Super Audio CD (SACD) mastering annotations
+# https://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
+# From: Toni Ruottu <toni.ruottu@iki.fi>
+0 string FRM8
+12 string DSD\x20 DSDIFF audio bitstream data
+!:mime audio/x-dff
+!:ext dff
+
+# format version chunk
+>&0 string FVER
+# version 1
+>>&8 byte 1
+
+# v1 / sampling resolution ( 1 bit PDM only )
+>>>&0 string x \b, 1 bit
+
+# v1 / sound property chunk
+>>>&0 search/0xff PROP
+>>>>&8 string SND
+
+# v1 / sound property chunk / channel configuration chunk
+>>>>>&0 search/0xff CHNL
+>>>>>>&8 ubeshort 1 \b, mono
+>>>>>>&8 ubeshort 2
+>>>>>>>&0 string SLFTSRGT \b, stereo
+>>>>>>>&0 default x \b, 2 channels
+>>>>>>&8 ubeshort 3
+>>>>>>>&0 string SLFTSRGTLFE\x20 \b, 2.1 stereo
+>>>>>>>&0 string SLFTSRGTC\x20\x20\x20 \b, 3.0 stereo
+>>>>>>>&0 default x \b, 3 channels
+>>>>>>&8 ubeshort 4
+>>>>>>>&0 string MLFTMRGTLS\x20\x20RS\x20\x20 \b, 4.0 surround
+>>>>>>>&0 string SLFTSRGTC\x20\x20\x20LFE\x20 \b, 3.1 stereo
+>>>>>>>&0 default x \b, 4 channels
+>>>>>>&8 ubeshort 5
+>>>>>>>&0 string MLFTMRGTC\x20\x20\x20LS\x20\x20RS\x20\x20 \b, 5.0 surround
+>>>>>>>&0 string MLFTMRGTLFE\x20LS\x20\x20RS\x20\x20 \b, 4.1 surround
+>>>>>>>&0 default x \b, 5 channels
+>>>>>>&8 ubeshort 6
+>>>>>>>&0 string MLFTMRGTC\x20\x20\x20LFE\x20LS\x20\x20RS\x20\x20 \b, 5.1 surround
+>>>>>>>&0 default x \b, 6 channels
+>>>>>>&8 ubeshort >6 \b, %u channels
+
+# v1 / sound property chunk / sample rate chunk
+>>>>>&0 search/0xff FS\x20\x20
+>>>>>>&0 string x \b,
+>>>>>>&8 ubelong%44100 0
+>>>>>>>&-4 ubelong/44100 x "DSD %u"
+>>>>>>>&-4 ubelong x %u Hz
+
+# v1 / sound property chunk / compression type chunk
+>>>>>&0 search/0xff CMPR
+>>>>>>&8 string DSD\x20 \b, no compression
+>>>>>>&8 string DST\x20 \b, DST compression
+>>>>>>&8 default x \b, unknown compression
+
+# v1 / quest for metadata
+>>>&0 string x
+
+# v1 / quest for metadata / edited master information chunk
+>>>>&0 search DIIN
+>>>>>&0 ubequad >0 \b, "edited master" metadata
+
+# v1 / quest for metadata / ID3 chunk ( defacto standard )
+>>>>&0 search ID3\x20
+>>>>>&8 string ID3 \b, ID3 version 2
+>>>>>&0 byte x \b.%u
+>>>>>&1 byte x \b.%u
+
+# v1 / quest for metadata / failure ( possibly due to -P bytes=... being too low )
+>>>>&0 default x \b, ID3 missing (or unreachable)
+
+# version > 1 or 0
+>>&0 default x \b, unknown version
+
+# Sony DSF audio format (Direct Stream Digital Stream File)
+# Used for lossless digital storage of songs produced as DSD audio
+# Portable analog of a track stored on a Super Audio CD (SACD)
+# https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
+# From: Toni Ruottu <toni.ruottu@iki.fi>
+0 string DSD\x20 DSF audio bitstream data
+!:mime audio/x-dsf
+!:ext dsf
+
+# format chunk
+>28 string fmt\x20
+# version 1
+>>&8 ulelong 1
+
+# v1 / sampling resolution ( 1 bit PDM only )
+# NOTE: the spec incorrectly uses "bits per sample" instead of "bits per byte"
+>>>&0 string x \b, 1 bit
+
+# v1 / channel configuration
+>>>>&4 ulelong 1 \b, mono
+>>>>&4 ulelong 2 \b, stereo
+>>>>&4 ulelong 3 \b, 3.0 stereo
+>>>>&4 ulelong 4 \b, 4.0 surround
+>>>>&4 ulelong 5 \b, 3.1 stereo
+>>>>&4 ulelong 6 \b, 5.0 surround
+>>>>&4 ulelong 7 \b, 5.1 surround
+>>>>&0 default x
+>>>>>&4 ulelong x \b, %u channels
+
+# v1 / sample rate chunk
+>>>>&0 string x \b,
+>>>>&12 ulelong%44100 0
+>>>>>&-4 ulelong/44100 x "DSD %u"
+>>>>&12 ulelong x %u Hz
+
+# v1 / compression
+>>>>&0 string x
+>>>>>&0 ulelong 0 \b, no compression
+>>>>>&0 default x \b, unknown compression
+
+# v1 / embedded ID3v2 metadata
+>>>0 string x \b, ID3
+>>>>20 ulequad !0
+>>>>>(20.q) string ID3 version 2
+>>>>>>&0 byte x \b.%u
+>>>>>>&1 byte x \b.%u
+# unable to verify ID3 ( possibly due to -P bytes=... being too low )
+>>>>>&0 default x unreachable
+>>>>&0 default x missing
+
+# version > 1 or 0
+>>&0 default x \b, unknown version
diff --git a/contrib/libs/libmagic/magic/Magdir/avm b/contrib/libs/libmagic/magic/Magdir/avm
new file mode 100644
index 0000000000..86e96d110e
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/avm
@@ -0,0 +1,33 @@
+
+#------------------------------------------------------------------------------
+# $File: avm,v 1.1 2020/08/28 20:37:58 christos Exp $
+# avm: file(1) magic for avm files; this is not use
+
+# Summary: FRITZ!Box router configuration backup
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Fritz!Box
+# Reference: http://www.mengelke.de/Projekte/FritzBoxTools2
+# Note: only tested with models 4040 and 6490 Cable (lgi)
+0 string ****\ FRITZ!Box\ FRITZ!Box configuration backup
+#!:mime text/plain
+!:mime application/x-avm-export
+!:ext export
+# router model name like "4040" , "6490 Cable (lgi)" followed by " CONFIGURATION EXPORT"
+>15 string x of %-.4s
+# on 2nd line hashed password
+#>41 search/54 Password= \b, password
+# on 3rd line firmware version like: 141.06.24 141.06.50 141.07.10 ... 155.06.83
+>41 search/172 FirmwareVersion= \b, firmware version
+>>&0 string x %s
+# on 5th line oem like: avme lgi
+>41 search/285 OEM= \b, oem
+>>&0 string x %s
+# on 7th line language like: de en
+>41 search/305 Language= \b, language
+>>&0 string x %s
+# on 10th line cfg file name like: /var/tmp.cfg
+>41 search/349 tmp.cfg
+# on 11th line date inside c-comment like: Thu Jun 4 22:25:19 2015
+>>&4 string x \b, %s
+#
+
diff --git a/contrib/libs/libmagic/magic/Magdir/basis b/contrib/libs/libmagic/magic/Magdir/basis
new file mode 100644
index 0000000000..19dd463b41
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/basis
@@ -0,0 +1,18 @@
+
+#----------------------------------------------------------------
+# $File: basis,v 1.5 2019/04/19 00:42:27 christos Exp $
+# basis: file(1) magic for BBx/Pro5-files
+# Oliver Dammer <dammer@olida.de> 2005/11/07
+# https://www.basis.com business-basic-files.
+#
+0 string \074\074bbx\076\076 BBx
+>7 string \000 indexed file
+>7 string \001 serial file
+>7 string \002 keyed file
+>>13 short 0 (sort)
+>7 string \004 program
+>>18 byte x (LEVEL %d)
+>>>23 string >\000 psaved
+>7 string \006 mkeyed file
+>>13 short 0 (sort)
+>>8 string \000 (mkey)
diff --git a/contrib/libs/libmagic/magic/Magdir/beetle b/contrib/libs/libmagic/magic/Magdir/beetle
new file mode 100644
index 0000000000..94a835ccc4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/beetle
@@ -0,0 +1,7 @@
+#------------------------------------------------------------------------------
+# $File: beetle,v 1.2 2018/02/05 23:42:17 rrt Exp $
+# beetle: file(1) magic for Beetle VM object files
+# https://github.com/rrthomas/beetle/
+
+# Beetle object module
+0 string BEETLE\000 Beetle VM object file
diff --git a/contrib/libs/libmagic/magic/Magdir/ber b/contrib/libs/libmagic/magic/Magdir/ber
new file mode 100644
index 0000000000..15288c6824
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ber
@@ -0,0 +1,65 @@
+
+#------------------------------------------------------------------------------
+# $File: ber,v 1.2 2019/04/19 00:42:27 christos Exp $
+# ber: file(1) magic for several BER formats used in the mobile
+# telecommunications industry (Georg Sauthoff)
+
+# The file formats are standardized by the GSMA (GSM association).
+# They are specified via ASN.1 schemas and some prose. Basic encoding
+# rules (BER) is the used encoding. The formats are used for exchanging
+# call data records (CDRs) between mobile operators and associated
+# parties for roaming clearing purposes and fraud detection.
+
+# The magic file covers:
+
+# - TAP files (TD.57) - CDR batches and notifications
+# - RAP files (TD.32) - return batches and acknowledgements
+# - NRT files (TD.35) - CDR batches for 'near real time' processing
+
+#
+# TAP 3 Files
+# TAP -> Transferred Account Procedure
+# cf. https://www.gsma.com/newsroom/wp-content/uploads/TD.57-v32.31.pdf
+# TransferBatch short tag
+0 byte 0x61
+# BatchControlInfo short tag
+>&1 search/b5 \x64
+# Sender long tag #TAP 3.x (BER encoded)
+>>&1 search/b8 \x5f\x81\x44
+# <SpecificationVersionNumber>3</><ReleaseVersionNumber> block
+>>>&64 search/b64 \x5f\x81\x49\x01\x03\x5f\x81\x3d\x01
+>>>>&0 byte x TAP 3.%d Batch (TD.57, Transferred Account)
+
+# Notification short tag
+0 byte 0x62
+# Sender long tag
+>2 search/b8 \x5f\x81\x44
+# <SpecificationVersionNumber>3</><ReleaseVersionNumber> block
+>>&64 search/b64 \x5f\x81\x49\x01\x03\x5f\x81\x3d\x01
+>>>&0 byte x TAP 3.%d Notification (TD.57, Transferred Account)
+
+
+# NRT Files
+# NRT a.k.a. NRTRDE
+0 byte 0x61
+# <SpecificationVersionNumber>2</><ReleaseVersionNumber> block
+>&1 search/b8 \x5f\x29\x01\x02\x5f\x25\x01
+>>&0 byte x NRT 2.%d (TD.35, Near Real Time Roaming Data Exchange)
+
+# RAP Files
+# cf. https://www.gsma.com/newsroom/wp-content/uploads/TD.32-v6.11.pdf
+# Long ReturnBatch tag
+0 string \x7f\x84\x16
+# Long RapBatchControlInfo tag
+>&1 search/b8 \x7f\x84\x19
+# <SpecificationVersionNumber>3</><ReleaseVersionNumber> block
+>>&64 search/b64 \x5f\x81\x49\x01\x03\x5f\x81\x3d\x01
+# <RapSpecificationVersionNumber>1</><RapReleaseVersionNumber> block
+>>>&1 string/b \x5f\x84\x20\x01\x01\x5f\x84\x1f\x01
+>>>>&0 byte x RAP 1.%d Batch (TD.32, Returned Account Procedure),
+>>>&0 byte x TAP 3.%d
+
+# Long Acknowledgement tag
+0 string \x7f\x84\x17
+# Long Sender tag
+>&1 search/b5 \x5f\x81\x44 RAP Acknowledgement (TD.32, Returned Account Procedure)
diff --git a/contrib/libs/libmagic/magic/Magdir/bflt b/contrib/libs/libmagic/magic/Magdir/bflt
new file mode 100644
index 0000000000..c46b4dbb4b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/bflt
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: bflt,v 1.5 2014/04/30 21:41:02 christos Exp $
+# bFLT: file(1) magic for BFLT uclinux binary files
+#
+# From Philippe De Muyter <phdm@macqel.be>
+#
+0 string bFLT BFLT executable
+>4 belong x - version %d
+>4 belong 4
+>>36 belong&0x1 0x1 ram
+>>36 belong&0x2 0x2 gotpic
+>>36 belong&0x4 0x4 gzip
+>>36 belong&0x8 0x8 gzdata
diff --git a/contrib/libs/libmagic/magic/Magdir/bhl b/contrib/libs/libmagic/magic/Magdir/bhl
new file mode 100644
index 0000000000..6f57f03433
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/bhl
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# $File: bhl,v 1.1 2017/06/11 22:20:02 christos Exp $
+# BlockHashLoc
+# ext: bhl
+# Marco Pontello marcopon@gmail.com
+# reference: https://github.com/MarcoPon/BlockHashLoc
+0 string BlockHashLoc\x1a BlockHashLoc recovery info,
+>13 byte x version %d
+!:ext bhl
diff --git a/contrib/libs/libmagic/magic/Magdir/bioinformatics b/contrib/libs/libmagic/magic/Magdir/bioinformatics
new file mode 100644
index 0000000000..2966fa6e49
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/bioinformatics
@@ -0,0 +1,178 @@
+
+#------------------------------------------------------------------------------
+# $File: bioinformatics,v 1.5 2019/04/19 00:42:27 christos Exp $
+# bioinfomatics: file(1) magic for Bioinfomatics file formats
+
+###############################################################################
+# BGZF (Blocked GNU Zip Format) - gzip compatible, but also indexable
+# used by SAMtools bgzip/tabix (http://samtools.sourceforge.net/tabix.shtml)
+###############################################################################
+0 string \037\213
+>3 byte &0x04
+>>12 string BC
+>>>14 leshort &0x02 Blocked GNU Zip Format (BGZF; gzip compatible)
+>>>>16 leshort x \b, block length %d
+!:mime application/x-gzip
+
+
+###############################################################################
+# Tabix index file
+# used by SAMtools bgzip/tabix (http://samtools.sourceforge.net/tabix.shtml)
+###############################################################################
+0 string TBI\1 SAMtools TBI (Tabix index format)
+>0x04 lelong =1 \b, with %d reference sequence
+>0x04 lelong >1 \b, with %d reference sequences
+>0x08 lelong &0x10000 \b, using half-closed-half-open coordinates (BED style)
+>0x08 lelong ^0x10000
+>>0x08 lelong =0 \b, using closed and one based coordinates (GFF style)
+>>0x08 lelong =1 \b, using SAM format
+>>0x08 lelong =2 \b, using VCF format
+>0x0c lelong x \b, sequence name column: %d
+>0x10 lelong x \b, region start column: %d
+>0x08 lelong =0
+>>0x14 lelong x \b, region end column: %d
+>0x18 byte x \b, comment character: %c
+>0x1c lelong x \b, skip line count: %d
+
+
+###############################################################################
+# BAM (Binary Sequence Alignment/Map format)
+# used by SAMtools (http://samtools.sourceforge.net/SAM1.pdf)
+# data is normally present only within compressed BGZF blocks (CDATA), so use file -z to examine it
+###############################################################################
+0 string BAM\1 SAMtools BAM (Binary Sequence Alignment/Map)
+>0x04 lelong >0
+>>&0x00 regex =^[@]HD\t.*VN: \b, with SAM header
+>>>&0 regex =[0-9.]+ \b version %s
+>>&(0x04) lelong >0 \b, with %d reference sequences
+
+
+###############################################################################
+# BAI (BAM indexing format)
+# used by SAMtools (http://samtools.sourceforge.net/SAM1.pdf)
+###############################################################################
+0 string BAI\1 SAMtools BAI (BAM indexing format)
+>0x04 lelong >0 \b, with %d reference sequences
+
+
+###############################################################################
+# CRAM (Binary Sequence Alignment/Map format)
+###############################################################################
+0 string CRAM CRAM
+>0x04 byte >-1 version %d.
+>0x05 byte >-1 \b%d
+>0x06 string >\0 (identified as %s)
+
+
+###############################################################################
+# BCF (Binary Call Format), version 1
+# used by SAMtools & VCFtools (http://vcftools.sourceforge.net/bcf.pdf)
+# data is normally present only within compressed BGZF blocks (CDATA), so use file -z to examine it
+###############################################################################
+0 string BCF\4
+# length of seqnm data in bytes is positive
+>&0x00 lelong >0
+# length of smpl data in bytes is positive
+>>&(&-0x04) lelong >0 SAMtools BCF (Binary Call Format)
+# length of meta in bytes
+>>>&(&-0x04) lelong >0
+# have meta text string
+>>>>&0x00 search ##samtoolsVersion=
+>>>>>&0x00 string x \b, generated by SAMtools version %s
+
+
+###############################################################################
+# BCF (Binary Call Format), version 2.1
+# used by SAMtools (https://samtools.github.io/hts-specs/BCFv2_qref.pdf)
+# data is normally present only within compressed BGZF blocks (CDATA), so use file -z to examine it
+###############################################################################
+0 string BCF\2\1 Binary Call Format (BCF) version 2.1
+# length of header text
+>&0x00 lelong >0
+# have header string
+>>&0x00 search ##samtoolsVersion=
+>>>&0x00 string x \b, generated by SAMtools version %s
+
+
+###############################################################################
+# BCF (Binary Call Format), version 2.2
+# used by SAMtools (https://samtools.github.io/hts-specs/BCFv2_qref.pdf)
+# data is normally present only within compressed BGZF blocks (CDATA), so use file -z to examine it
+###############################################################################
+0 string BCF\2\2 Binary Call Format (BCF) version 2.2
+# length of header text
+>&0x00 lelong >0
+# have header string
+>>&0x00 search ##samtoolsVersion=
+>>>&0x00 string x \b, generated by SAMtools version %s
+
+###############################################################################
+# VCF (Variant Call Format)
+# used by VCFtools (http://vcftools.sourceforge.net/)
+###############################################################################
+0 search ##fileformat=VCFv Variant Call Format (VCF)
+>&0 string x \b version %s
+
+###############################################################################
+# FASTQ
+# used by MAQ (http://maq.sourceforge.net/fastq.shtml)
+###############################################################################
+# XXX Broken?
+# @<seqname>
+#0 regex =^@[A-Za-z0-9_.:-]+\?\n
+# <seq>
+#>&1 regex =^[A-Za-z\n.~]++
+# +[<seqname>]
+#>>&1 regex =^[A-Za-z0-9_.:-]*\?\n
+# <qual>
+#>>>&1 regex =^[!-~\n]+\n FASTQ
+
+###############################################################################
+# FASTA
+# used by FASTA (https://fasta.bioch.virginia.edu/fasta_www2/fasta_guide.pdf)
+###############################################################################
+#0 byte 0x3e
+# q>0 regex =^[>][!-~\t\ ]+$
+# Amino Acid codes: [A-IK-Z*-]+
+#>>1 regex !=[!-'Jj;:=?@^`|~\\] FASTA
+# IUPAC codes/gaps: [ACGTURYKMSWBDHVNX-]+
+# not in IUPAC codes/gaps: [EFIJLOPQZ]
+#>>>1 regex !=[EFIJLOPQZefijlopqz] \b, with IUPAC nucleotide codes
+#>>>1 regex =^[EFIJLOPQZefijlopqz]+$ \b, with Amino Acid codes
+
+###############################################################################
+# SAM (Sequence Alignment/Map format)
+# used by SAMtools (http://samtools.sourceforge.net/SAM1.pdf)
+###############################################################################
+# Short-cut version to recognise SAM files with (optional) header at beginning
+###############################################################################
+0 string @HD\t
+>4 search VN: Sequence Alignment/Map (SAM), with header
+>>&0 regex [0-9.]+ \b version %s
+###############################################################################
+# Longer version to recognise SAM alignment lines using (many) regexes
+###############################################################################
+# SAM Alignment QNAME
+0 regex =^[!-?A-~]{1,255}(\t[^\t]+){11}
+# SAM Alignment FLAG
+>0 regex =^([^\t]+\t){1}[0-9]{1,5}\t
+# SAM Alignment RNAME
+>>0 regex =^([^\t]+\t){2}\\*|[^*=]*\t
+# SAM Alignment POS
+>>>0 regex =^([^\t]+\t){3}[0-9]{1,9}\t
+# SAM Alignment MAPQ
+>>>>0 regex =^([^\t]+\t){4}[0-9]{1,3}\t
+# SAM Alignment CIGAR
+>>>>>0 regex =\t(\\*|([0-9]+[MIDNSHPX=])+)\t
+# SAM Alignment RNEXT
+>>>>>>0 regex =\t(\\*|=|[!-()+->?-~][!-~]*)\t
+# SAM Alignment PNEXT
+>>>>>>>0 regex =^([^\t]+\t){7}[0-9]{1,9}\t
+# SAM Alignment TLEN
+>>>>>>>>0 regex =\t[+-]{0,1}[0-9]{1,9}\t.*\t
+# SAM Alignment SEQ
+>>>>>>>>>0 regex =^([^\t]+\t){9}(\\*|[A-Za-z=.]+)\t
+# SAM Alignment QUAL
+>>>>>>>>>>0 regex =^([^\t]+\t){10}[!-~]+ Sequence Alignment/Map (SAM)
+>>>>>>>>>>>0 regex =^[@]HD\t.*VN: \b, with header
+>>>>>>>>>>>>&0 regex =[0-9.]+ \b version %s
diff --git a/contrib/libs/libmagic/magic/Magdir/biosig b/contrib/libs/libmagic/magic/Magdir/biosig
new file mode 100644
index 0000000000..7d41713f24
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/biosig
@@ -0,0 +1,154 @@
+
+##############################################################################
+#
+# Magic ids for biomedical signal file formats
+# Copyright (C) 2018 Alois Schloegl <alois.schloegl@gmail.com>
+#
+# The list has been derived from biosig projects
+# http://biosig.sourceforge.net
+# https://pub.ist.ac.at/~schloegl/matlab/eeg/
+# https://pub.ist.ac.at/~schloegl/biosig/TESTED
+#
+##############################################################################
+#
+0 string ABF\x20 Biosig/Axon Binary format
+!:mime biosig/abf2
+0 string ABF2\0\0 Biosig/Axon Binary format
+!:mime biosig/abf2
+#
+0 string ATES\x20MEDICA\x20SOFT.\x20EEG\x20for\x20Windows Biosig/ATES MEDICA SOFT. EEG for Windows
+!:mime biosig/ates
+#
+0 string ATF\x09 Biosig/Axon Text format
+!:mime biosig/atf
+#
+0 string ADU1 Biosig/Axona file format
+!:mime biosig/axona
+0 string ADU2 Biosig/Axona file format
+!:mime biosig/axona
+#
+0 string ALPHA-TRACE-MEDICAL Biosig/alpha trace
+!:mime biosig/alpha
+#
+0 string AxGr Biosig/AXG
+0 string axgx Biosig/AXG
+!:mime biosig/axg
+#
+0 string HeaderLen= Biosig/BCI2000
+0 string BCI2000V Biosig/BCI2000
+!:mime biosig/bci2000
+#
+### Specification: https://www.biosemi.com/faq/file_format.htm
+0 string \xffBIOSEMI Biosig/Biosemi data format
+!:mime biosig/bdf
+#
+0 string Brain\x20Vision\x20Data\x20Exchange\x20Header\x20File Biosig/Brainvision data file
+0 string Brain\x20Vision\x20V-Amp\x20Data\x20Header\x20File\x20Version Biosig/Brainvision V-Amp file
+0 string Brain\x20Vision\x20Data\x20Exchange\x20Marker\x20File,\x20Version Biosig/Brainvision Marker file
+!:mime biosig/brainvision
+#
+0 string CEDFILE Biosig/CFS: Cambridge Electronic devices File format
+!:mime biosig/ced
+#
+### Specification: https://www.edfplus.info/specs/index.html
+0 string 0\x20\x20\x20\x20\x20\x20\x20 Biosig/EDF: European Data format
+!:mime biosig/edf
+#
+### Specifications: https://arxiv.org/abs/cs/0608052
+0 string GDF Biosig/GDF: General data format for biosignals
+!:mime biosig/gdf
+#
+0 string DATA\0\0\0\0 Biosig/Heka Patchmaster
+0 string DAT1\0\0\0\0 Biosig/Heka Patchmaster
+0 string DAT2\0\0\0\0 Biosig/Heka Patchmaster
+!:mime biosig/heka
+#
+0 string (C)\x20CED\x2087 Biosig/CED SMR
+!:mime biosig/ced-smr
+#
+0 string CFWB\1\0\0\0 Biosig/CFWB
+!:mime biosig/cfwb
+#
+0 string DEMG Biosig/DEMG
+!:mime biosig/demg
+#
+0 string EBS\x94\x0a\x13\x1a\x0d Biosig/EBS
+!:mime biosig/ebs
+#
+0 string Embla\x20data\x20file Biosig/Embla
+!:mime biosig/embla
+#
+0 string Header\r\nFile Version Biosig/ETG4000
+!:mime biosig/etg4000
+#
+0 string GALILEO\x20EEG\x20TRACE\x20FILE Biosig/Galileo
+!:mime biosig/galileo
+#
+0 string IGOR Biosig/IgorPro ITX file
+!:mime biosig/igorpro
+#
+# Specification: http://www.ampsmedical.com/uploads/2017-12-7/The_ISHNE_Format.pdf
+0 string ISHNE1.0 Biosig/ISHNE
+!:mime biosig/ishne
+#
+# CEN/ISO 11073/22077 series, http://www.mfer.org/en/document.htm
+0 string @\x20\x20MFER\x20 Biosig/MFER
+0 string @\x20MFR\x20 Biosig/MFER
+!:mime biosig/mfer
+#
+0 string NEURALEV Biosig/NEV
+0 string N.EV.\0 Biosig/NEV
+!:mime biosig/nev
+#
+0 string NEX1 Biosig/NEX
+!:mime biosig/nex1
+#
+0 string PLEX Biosig/Plexon v1.0
+10 string PLEXON Biosig/Plexon v2.0
+!:mime biosig/plexon
+#
+0 string \x02\x27\x91\xC6 Biosig/RHD2000: Intan RHD2000 format
+#
+# Specification: CEN 1064:2005/ISO 11073:91064
+16 string SCPECG\0\0 Biosig/SCP-ECG format CEN 1064:2005/ISO 11073:91064
+!:mime biosig/scpecg
+#
+0 string IAvSFo Biosig/SIGIF
+!:mime biosig/sigif
+#
+0 string POLY\x20SAMPLE\x20FILEversion\x20 Biosig/TMS32
+!:mime biosig/tms32
+#
+0 string FileId=TMSi\x20PortiLab\x20sample\x20log\x20file\x0a\x0dVersion= Biosig/TMSiLOG
+!:mime biosig/tmsilog
+#
+4 string Synergy\0\48\49\50\46\48\48\51\46\48\48\48\46\48\48\48\0\28\0\0\0\2\0\0\0
+>63 string CRawDataElement
+>>85 string CRawDataBuffer Biosig/SYNERGY
+!:mime biosig/synergy
+#
+4 string \40\0\4\1\44\1\102\2\146\3\44\0\190\3 Biosig/UNIPRO
+!:mime biosig/unipro
+#
+0 string VER=9\r\nCTIME= Biosig/WCP
+!:mime biosig/wcp
+#
+0 string \xAF\xFE\xDA\xDA Biosig/Walter Graphtek
+0 string \xDA\xDA\xFE\xAF Biosig/Walter Graphtek
+0 string \x55\x55\xFE\xAF Biosig/Walter Graphtek
+!:mime biosig/walter-graphtek
+#
+0 string V3.0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
+>32 string [PatInfo] Biosig/Sigma
+!:mime biosig/sigma
+#
+0 string \067\069\078\013\010\0x1a\04\0x84 Biosig/File exchange format (FEF)
+!:mime biosig/fef
+0 string \67\69\78\0x13\0x10\0x1a\4\0x84 Biosig/File exchange format (FEF)
+!:mime biosig/fef
+#
+0 string \0\0\0\x64\0\0\0\x1f\0\0\0\x14\0\0\0\0\0\1
+>36 string \0\0\0\x65\0\0\0\3\0\0\0\4\0\0
+>>56 string \0\0\0\x6a\0\0\0\3\0\0\0\4\0\0\0\0\xff\xff\xff\xff\0\0 Biosig/FIFF
+!:mime biosig/fiff
+#
diff --git a/contrib/libs/libmagic/magic/Magdir/blackberry b/contrib/libs/libmagic/magic/Magdir/blackberry
new file mode 100644
index 0000000000..2e38a54f42
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/blackberry
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: blackberry,v 1.2 2017/03/17 21:35:28 christos Exp $
+# blackberry: file(1) magic for BlackBerry file formats
+#
+5 belong 0
+>8 belong 010010010 BlackBerry RIM ETP file
+>>22 string x \b for %s
diff --git a/contrib/libs/libmagic/magic/Magdir/blcr b/contrib/libs/libmagic/magic/Magdir/blcr
new file mode 100644
index 0000000000..d2f901ae92
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/blcr
@@ -0,0 +1,25 @@
+# Berkeley Lab Checkpoint Restart (BLCR) checkpoint context files
+# https://ftg.lbl.gov/checkpoint
+0 string C\0\0\0R\0\0\0 BLCR
+>16 lelong 1 x86
+>16 lelong 3 alpha
+>16 lelong 5 x86-64
+>16 lelong 7 ARM
+>8 lelong x context data (little endian, version %d)
+# Uncomment the following only of your "file" program supports "search"
+#>0 search/1024 VMA\06 for kernel
+#>>&1 byte x %d.
+#>>&2 byte x %d.
+#>>&3 byte x %d
+0 string \0\0\0C\0\0\0R BLCR
+>16 belong 2 SPARC
+>16 belong 4 ppc
+>16 belong 6 ppc64
+>16 belong 7 ARMEB
+>16 belong 8 SPARC64
+>8 belong x context data (big endian, version %d)
+# Uncomment the following only of your "file" program supports "search"
+#>0 search/1024 VMA\06 for kernel
+#>>&1 byte x %d.
+#>>&2 byte x \b%d.
+#>>&3 byte x \b%d
diff --git a/contrib/libs/libmagic/magic/Magdir/blender b/contrib/libs/libmagic/magic/Magdir/blender
new file mode 100644
index 0000000000..5a897113e0
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/blender
@@ -0,0 +1,50 @@
+
+#------------------------------------------------------------------------------
+# $File: blender,v 1.9 2022/12/21 15:53:27 christos Exp $
+# blender: file(1) magic for Blender 3D related files
+#
+# Native format rule v1.2. For questions use the developers list
+# https://lists.blender.org/mailman/listinfo/bf-committers
+# GLOB chunk was moved near start and provides subversion info since 2.42
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/BLEND
+# http://www.blender.org/
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/blend.trid.xml
+# http://formats.kaitai.io/blender_blend/index.html
+# Note: called "Blender 3D data" by TrID
+# and gzip compressed variant handled by ./compress
+0 string =BLENDER Blender3D,
+#!:mime application/octet-stream
+!:mime application/x-blender
+!:ext blend
+# no sample found with extension blender
+#!:ext blend/blender
+>7 string =_ saved as 32-bits
+>>8 string =v little endian
+>>>9 byte x with version %c.
+>>>10 byte x \b%c
+>>>11 byte x \b%c
+>>>0x40 string =GLOB \b.
+>>>>0x58 leshort x \b%.4d
+>>8 string =V big endian
+>>>9 byte x with version %c.
+>>>10 byte x \b%c
+>>>11 byte x \b%c
+>>>0x40 string =GLOB \b.
+>>>>0x58 beshort x \b%.4d
+>7 string =- saved as 64-bits
+>>8 string =v little endian
+>>9 byte x with version %c.
+>>10 byte x \b%c
+>>11 byte x \b%c
+>>0x44 string =GLOB \b.
+>>>0x60 leshort x \b%.4d
+>>8 string =V big endian
+>>>9 byte x with version %c.
+>>>10 byte x \b%c
+>>>11 byte x \b%c
+>>>0x44 string =GLOB \b.
+>>>>0x60 beshort x \b%.4d
+
+# Scripts that run in the embedded Python interpreter
+0 string #!BPY Blender3D BPython script
diff --git a/contrib/libs/libmagic/magic/Magdir/blit b/contrib/libs/libmagic/magic/Magdir/blit
new file mode 100644
index 0000000000..5ce7870706
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/blit
@@ -0,0 +1,24 @@
+
+#------------------------------------------------------------------------------
+# $File: blit,v 1.9 2021/07/03 14:01:46 christos Exp $
+# blit: file(1) magic for 68K Blit stuff as seen from 680x0 machine
+#
+# Note that this 0407 conflicts with several other a.out formats...
+#
+# XXX - should this be redone with "be" and "le", so that it works on
+# little-endian machines as well? If so, what's the deal with
+# "VAX-order" and "VAX-order2"?
+#
+#0 long 0407 68K Blit (standalone) executable
+#0 short 0407 VAX-order2 68K Blit (standalone) executable
+0 short 03401 VAX-order 68K Blit (standalone) executable
+0 long 0406 68k Blit mpx/mux executable
+0 short 0406 VAX-order2 68k Blit mpx/mux executable
+# GRR: line below is too general as it matches also TTComp archive, ASCII, 4K handled by ./archive
+0 short 03001 VAX-order 68k Blit mpx/mux executable
+# TODO:
+# skip TTComp archive, ASCII, 4K by looking for executable keyword like main
+#>0 search/5536 main\0 VAX-order 68k Blit mpx/mux executable
+# Need more values for WE32 DMD executables.
+# Note that 0520 is the same as COFF
+#0 short 0520 tty630 layers executable
diff --git a/contrib/libs/libmagic/magic/Magdir/bm b/contrib/libs/libmagic/magic/Magdir/bm
new file mode 100644
index 0000000000..a9a1d5bb3f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/bm
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# $File: bm,v 1.2 2021/03/14 16:56:51 christos Exp $
+# bm: file(1) magic for "Birtual Machine", cf. https://github.com/tsoding/bm
+
+0 string bm\001\244 Birtual Machine
+>4 leshort x \b, version %d
+>6 lelong x \b, program size %u
+>14 lelong x \b, memory size %u
+>22 lelong x \b, memory capacity %u
diff --git a/contrib/libs/libmagic/magic/Magdir/bout b/contrib/libs/libmagic/magic/Magdir/bout
new file mode 100644
index 0000000000..693cc2a4bd
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/bout
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# $File: bout,v 1.5 2009/09/19 16:28:08 christos Exp $
+# i80960 b.out objects and archives
+#
+0 long 0x10d i960 b.out relocatable object
+>16 long >0 not stripped
+#
+# b.out archive (hp-rt on i960)
+0 string =!<bout> b.out archive
+>8 string __.SYMDEF random library
diff --git a/contrib/libs/libmagic/magic/Magdir/bsdi b/contrib/libs/libmagic/magic/Magdir/bsdi
new file mode 100644
index 0000000000..8499b0c903
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/bsdi
@@ -0,0 +1,33 @@
+
+#------------------------------------------------------------------------------
+# $File: bsdi,v 1.7 2014/03/29 15:40:34 christos Exp $
+# bsdi: file(1) magic for BSD/OS (from BSDI) objects
+# Some object/executable formats use the same magic numbers as are used
+# in other OSes; those are handled by entries in aout.
+#
+
+0 lelong 0314 386 compact demand paged pure executable
+>16 lelong >0 not stripped
+>32 byte 0x6a (uses shared libs)
+
+# same as in SunOS 4.x, except for static shared libraries
+0 belong&077777777 0600413 SPARC demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+>36 belong 0xb4100001 (uses shared libs)
+
+0 belong&077777777 0600410 SPARC pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+>36 belong 0xb4100001 (uses shared libs)
+
+0 belong&077777777 0600407 SPARC
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+>36 belong 0xb4100001 (uses shared libs)
diff --git a/contrib/libs/libmagic/magic/Magdir/bsi b/contrib/libs/libmagic/magic/Magdir/bsi
new file mode 100644
index 0000000000..87e0fec76e
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/bsi
@@ -0,0 +1,10 @@
+# Chiasmus is an encryption standard developed by the German Federal
+# Office for Information Security (Bundesamt fuer Sicherheit in der
+# Informationstechnik).
+
+# https://www.bsi.bund.de/EN/Topics/OtherTopics/Chiasmus/Chiasmus_node.html
+0 string XIA1\r Chiasmus Encrypted data
+!:ext xia
+
+0 string XIS Chiasmus key
+!:ext xis
diff --git a/contrib/libs/libmagic/magic/Magdir/btsnoop b/contrib/libs/libmagic/magic/Magdir/btsnoop
new file mode 100644
index 0000000000..d72daad877
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/btsnoop
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: btsnoop,v 1.5 2009/09/19 16:28:08 christos Exp $
+# BTSnoop: file(1) magic for BTSnoop files
+#
+# From <marcel@holtmann.org>
+0 string btsnoop\0 BTSnoop
+>8 belong x version %d,
+>12 belong 1001 Unencapsulated HCI
+>12 belong 1002 HCI UART (H4)
+>12 belong 1003 HCI BCSP
+>12 belong 1004 HCI Serial (H5)
+>>12 belong x type %d
diff --git a/contrib/libs/libmagic/magic/Magdir/burp b/contrib/libs/libmagic/magic/Magdir/burp
new file mode 100644
index 0000000000..460d18c4c2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/burp
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------
+# $File: burp,v 1.1 2022/07/04 17:15:09 christos Exp $
+# Burp file, I don't know the version
+#------------------------------------------------------------
+# From wof (wof@stachelkaktus.net)
+0 bequad 0x6685828000000001 Burp project save file
diff --git a/contrib/libs/libmagic/magic/Magdir/bytecode b/contrib/libs/libmagic/magic/Magdir/bytecode
new file mode 100644
index 0000000000..dca961c264
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/bytecode
@@ -0,0 +1,41 @@
+
+#------------------------------------------------------------
+# $File: bytecode,v 1.5 2023/02/20 16:25:05 christos Exp $
+# magic for various bytecodes
+
+# From: Mikhail Gusarov <dottedmag@dottedmag.net>
+# NekoVM (https://nekovm.org/) bytecode
+0 string NEKO NekoVM bytecode
+>4 lelong x (%d global symbols,
+>8 lelong x %d global fields,
+>12 lelong x %d bytecode ops)
+!:mime application/x-nekovm-bytecode
+
+# https://www.iana.org/assignments/media-types/application/vnd.resilient.logic
+# From: Benedikt Muessig <benedikt@resilient-group.de>
+0 belong 0x07524c4d Resilient Logic bytecode
+!:mime application/vnd.resilient.logic
+>4 byte/16 x \b, version %d
+>4 byte&0x0f x \b.%d
+
+# Guile file magic from <dalepsmith@gmail.com>
+# https://www.gnu.org/s/guile/
+# https://git.savannah.gnu.org/gitweb/?p=guile.git;f=libguile/_scm.h;hb=HEAD#l250
+
+0 string GOOF---- Guile Object
+>8 string LE \b, little endian
+>8 string BE \b, big endian
+>11 string 4 \b, 32bit
+>11 string 8 \b, 64bit
+>13 regex .\\.. \b, bytecode v%s
+
+# Racket file magic
+# From: Haelwenn (lanodan) Monnier <contact+libmagic@hacktivis.me>
+# https://racket-lang.org/
+# https://github.com/racket/racket/blob/master/racket/src/expander/compile/write-linklet.rkt
+0 string #~
+>&0 pstring x
+>>&0 pstring racket
+>>>0 string #~ Racket bytecode
+>>>>&0 pstring x (version %s)
+
diff --git a/contrib/libs/libmagic/magic/Magdir/c-lang b/contrib/libs/libmagic/magic/Magdir/c-lang
new file mode 100644
index 0000000000..6e375a06a7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/c-lang
@@ -0,0 +1,110 @@
+#------------------------------------------------------------------------------
+# $File: c-lang,v 1.32 2023/06/16 19:57:19 christos Exp $
+# c-lang: file(1) magic for C and related languages programs
+#
+# The strength is to beat standard HTML
+
+# BCPL
+0 search/8192 "libhdr" BCPL source text
+!:mime text/x-bcpl
+0 search/8192 "LIBHDR" BCPL source text
+!:mime text/x-bcpl
+
+# C
+# Check for class if include is found, otherwise class is beaten by include because of lowered strength
+0 search/8192 #include
+>0 regex \^#include C
+>>0 regex \^class[[:space:]]+
+>>>&0 regex \\{[\.\*]\\}(;)?$ \b++
+>>&0 clear x source text
+!:strength + 15
+!:mime text/x-c
+0 search/8192 pragma
+>0 regex \^#[[:space:]]*pragma C source text
+!:mime text/x-c
+0 search/8192 endif
+>0 regex \^#[[:space:]]*(if\|ifn)def
+>>&0 regex \^#[[:space:]]*endif$ C source text
+!:mime text/x-c
+0 search/8192 define
+>0 regex \^#[[:space:]]*(if\|ifn)def
+>>&0 regex \^#[[:space:]]*define C source text
+!:mime text/x-c
+0 search/8192 char
+>0 regex \^[[:space:]]*char(\ \\*|\\*)(.+)(=.*)?;[[:space:]]*$ C source text
+!:mime text/x-c
+0 search/8192 double
+>0 regex \^[[:space:]]*double(\ \\*|\\*)(.+)(=.*)?;[[:space:]]*$ C source text
+!:mime text/x-c
+0 search/8192 extern
+>0 regex \^[[:space:]]*extern[[:space:]]+ C source text
+!:mime text/x-c
+0 search/8192 float
+>0 regex \^[[:space:]]*float(\ \\*|\\*)(.+)(=.*)?;[[:space:]]*$ C source text
+!:mime text/x-c
+0 search/8192 struct
+>0 regex \^struct[[:space:]]+ C source text
+!:mime text/x-c
+0 search/8192 union
+>0 regex \^union[[:space:]]+ C source text
+!:mime text/x-c
+0 search/8192 main(
+>&0 search/64 String Java source text
+!:mime text/x-java
+>&0 default x
+>>&0 regex \\)[[:space:]]*\\{ C source text
+!:mime text/x-c
+
+# C++
+# The strength of these rules is increased so they beat the C rules above
+0 search/8192 namespace
+>0 regex \^namespace[[:space:]]+[_[:alpha:]]{1,30}[[:space:]]*\\{ C++ source text
+!:strength + 30
+!:mime text/x-c++
+# using namespace [namespace] or using std::[lib]
+0 search/8192 using
+>0 regex \^using[[:space:]]+(namespace\ )?std(::)?[[:alpha:]]*[[:space:]]*; C++ source text
+!:strength + 30
+!:mime text/x-c++
+0 search/8192 template
+>0 regex \^[[:space:]]*template[[:space:]]*<.*>[[:space:]]*$ C++ source text
+!:strength + 30
+!:mime text/x-c++
+0 search/8192 virtual
+>0 regex \^[[:space:]]*virtual[[:space:]]+.*[};][[:space:]]*$ C++ source text
+!:strength + 30
+!:mime text/x-c++
+# But class alone is reduced to avoid beating php (Jens Schleusener)
+0 search/8192 class
+>0 regex \^[[:space:]]*class[[:space:]]+[[:digit:][:alpha:]:_]+[[:space:]]*\\{(.*[\n]*)*\\}(;)?$ C++ source text
+!:strength + 13
+!:mime text/x-c++
+0 search/8192 public
+>0 regex \^[[:space:]]*public: C++ source text
+!:strength + 30
+!:mime text/x-c++
+0 search/8192 private
+>0 regex \^[[:space:]]*private: C++ source text
+!:strength + 30
+!:mime text/x-c++
+0 search/8192 protected
+>0 regex \^[[:space:]]*protected: C++ source text
+!:strength + 30
+!:mime text/x-c++
+
+# Objective-C
+0 search/8192 #import
+>0 regex \^#import[[:space:]]+["<] Objective-C source text
+!:strength + 25
+!:mime text/x-objective-c
+
+# From: Mikhail Teterin <mi@aldan.algebra.com>
+0 string cscope cscope reference data
+>7 string x version %.2s
+# We skip the path here, because it is often long (so file will
+# truncate it) and mostly redundant.
+# The inverted index functionality was added some time between
+# versions 11 and 15, so look for -q if version is above 14:
+>7 string >14
+>>10 search/100 \ -q\ with inverted index
+>10 search/100 \ -c\ text (non-compressed)
diff --git a/contrib/libs/libmagic/magic/Magdir/c64 b/contrib/libs/libmagic/magic/Magdir/c64
new file mode 100644
index 0000000000..6c8732090f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/c64
@@ -0,0 +1,549 @@
+
+#------------------------------------------------------------------------------
+# $File: c64,v 1.14 2023/06/16 19:24:06 christos Exp $
+# c64: file(1) magic for various commodore 64 related files
+#
+# From: Dirk Jagdmann <doj@cubic.org>
+
+0x16500 belong 0x12014100 D64 Image
+0x16500 belong 0x12014180 D71 Image
+0x61800 belong 0x28034400 D81 Image
+0 belong 0x43154164 X64 Image
+
+# C64 (and other CBM) cartridges
+# Extended by David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://vice-emu.sourceforge.io/vice_17.html#SEC391
+
+0 string C64\40CARTRIDGE Commodore 64 cartridge
+>0x20 ubyte 0 \b,
+>0x20 ubyte !0
+>>0x20 string/T x \b: "%.32s",
+>0x16 beshort 0
+>>0x18 beshort 0x0000 16 KB game
+>>0x18 beshort 0x0001 8 KB game
+>>0x18 beshort 0x0100 UltiMax mode
+>>0x18 beshort 0x0101 RAM/disabled
+>0x16 beshort 1 Action Replay
+>0x16 beshort 2 KCS Power Cartridge
+>0x16 beshort 3 Final Cartridge III
+>0x16 beshort 4 Simons' BASIC
+>0x16 beshort 5 Ocean type 1
+>0x16 beshort 6 Expert Cartridge
+>0x16 beshort 7 Fun Play, Power Play
+>0x16 beshort 8 Super Games
+>0x16 beshort 9 Atomic Power
+>0x16 beshort 10 Epyx Fastload
+>0x16 beshort 11 Westermann Learning
+>0x16 beshort 12 Rex Utility
+>0x16 beshort 13 Final Cartridge I
+>0x16 beshort 14 Magic Formel
+>0x16 beshort 15 C64 Game System, System 3
+>0x16 beshort 16 Warp Speed
+>0x16 beshort 17 Dinamic
+>0x16 beshort 18 Zaxxon / Super Zaxxon (Sega)
+>0x16 beshort 19 Magic Desk, Domark, HES Australia
+>0x16 beshort 20 Super Snapshot V5
+>0x16 beshort 21 Comal-80
+>0x16 beshort 22 Structured BASIC
+>0x16 beshort 23 Ross
+>0x16 beshort 24 Dela EP64
+>0x16 beshort 25 Dela EP7x8
+>0x16 beshort 26 Dela EP256
+>0x16 beshort 27 Rex EP256
+>0x16 beshort 28 Mikro Assembler
+>0x16 beshort 29 Final Cartridge Plus
+>0x16 beshort 30 Action Replay 4
+>0x16 beshort 31 Stardos
+>0x16 beshort 32 EasyFlash
+>0x16 beshort 33 EasyFlash Xbank
+>0x16 beshort 34 Capture
+>0x16 beshort 35 Action Replay 3
+>0x16 beshort 36
+>>0x1A ubyte 1 Nordic Replay
+>>0x1A ubyte !1 Retro Replay
+>0x16 beshort 37 MMC64
+>0x16 beshort 38 MMC Replay
+>0x16 beshort 39 IDE64
+>0x16 beshort 40 Super Snapshot V4
+>0x16 beshort 41 IEEE-488
+>0x16 beshort 42 Game Killer
+>0x16 beshort 43 Prophet64
+>0x16 beshort 44 EXOS
+>0x16 beshort 45 Freeze Frame
+>0x16 beshort 46 Freeze Machine
+>0x16 beshort 47 Snapshot64
+>0x16 beshort 48 Super Explode V5.0
+>0x16 beshort 49 Magic Voice
+>0x16 beshort 50 Action Replay 2
+>0x16 beshort 51 MACH 5
+>0x16 beshort 52 Diashow-Maker
+>0x16 beshort 53 Pagefox
+>0x16 beshort 54 Kingsoft
+>0x16 beshort 55 Silverrock 128K Cartridge
+>0x16 beshort 56 Formel 64
+>0x16 beshort 57
+>>0x1A ubyte 1 Hucky
+>>0x1A ubyte !1 RGCD
+>0x16 beshort 58 RR-Net MK3
+>0x16 beshort 59 EasyCalc
+>0x16 beshort 60 GMod2
+>0x16 beshort 61 MAX Basic
+>0x16 beshort 62 GMod3
+>0x16 beshort 63 ZIPP-CODE 48
+>0x16 beshort 64 Blackbox V8
+>0x16 beshort 65 Blackbox V3
+>0x16 beshort 66 Blackbox V4
+>0x16 beshort 67 REX RAM-Floppy
+>0x16 beshort 68 BIS-Plus
+>0x16 beshort 69 SD-BOX
+>0x16 beshort 70 MultiMAX
+>0x16 beshort 71 Blackbox V9
+>0x16 beshort 72 Lt. Kernal Host Adaptor
+>0x16 beshort 73 RAMLink
+>0x16 beshort 74 H.E.R.O.
+>0x16 beshort 75 IEEE Flash! 64
+>0x16 beshort 76 Turtle Graphics II
+>0x16 beshort 77 Freeze Frame MK2
+
+0 string C128\40CARTRIDGE Commodore 128 cartridge
+>0x20 ubyte 0 \b,
+>0x20 ubyte !0
+>>0x20 string/T x \b: "%.32s",
+>0x16 beshort 0 generic cartridge
+>0x16 beshort 1 Warpspeed128
+>>0x1A ubyte 1 \b, REU support
+>>0x1A ubyte 2 \b, REU support, with I/O and ROM banking
+
+0 string CBM2\40CARTRIDGE Commodore CBM-II cartridge
+>0x20 ubyte !0
+>>0x20 string/T x \b: "%.32s"
+
+0 string VIC20\40CARTRIDGE Commodore VIC-20 cartridge
+>0x20 ubyte 0 \b,
+>0x20 ubyte !0
+>>0x20 string/T x \b: "%.32s",
+>0x16 beshort 0 generic cartridge
+>0x16 beshort 1 Mega-Cart
+>0x16 beshort 2 Behr Bonz
+>0x16 beshort 3 Vic Flash Plugin
+>0x16 beshort 4 UltiMem
+>0x16 beshort 5 Final Expansion
+
+0 string PLUS4\40CARTRIDGE Commodore 16/Plus4 cartridge
+>0x20 ubyte !0
+>>0x20 string/T x \b: "%.32s"
+
+
+# DreamLoad archives see:
+# https://www.lemon64.com/forum/viewtopic.php?t=37415\
+# &sid=494dc2ca91289e05dadf80a7f8a968fe (at the bottom).
+# https://www.c64-wiki.com/wiki/DreamLoad.
+# Example HVSC Commodore 64 music collection:
+# https://kohina.duckdns.org/HVSC/C64Music/10_Years_HVSC.dfi
+
+0 byte 0
+>1 string DREAMLOAD\40FILE\40ARCHIVE
+>>0x17 byte 0 DFI Image
+>>>0x1a leshort x version: %d.
+>>>0x18 leshort x \b%d
+>>>0x1c lelong x tracks: %d
+
+0 string GCR-1541 GCR Image
+>8 byte x version: %i
+>9 byte x tracks: %i
+
+9 string PSUR ARC archive (c64)
+2 string -LH1- LHA archive (c64)
+
+0 string C64File PC64 Emulator file
+>8 string >\0 "%s"
+0 string C64Image PC64 Freezer Image
+
+0 beshort 0x38CD C64 PCLink Image
+0 string CBM\144\0\0 Power 64 C64 Emulator Snapshot
+
+0 belong 0xFF424CFF WRAptor packer (c64)
+
+0 string C64S\x20tape\x20file T64 tape Image
+>32 leshort x Version:%#x
+>36 leshort !0 Entries:%i
+>40 string x Name:%.24s
+
+0 string C64\x20tape\x20image\x20file\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0 T64 tape Image
+>32 leshort x Version:%#x
+>36 leshort !0 Entries:%i
+>40 string x Name:%.24s
+
+0 string C64S\x20tape\x20image\x20file\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0 T64 tape Image
+>32 leshort x Version:%#x
+>36 leshort !0 Entries:%i
+>40 string x Name:%.24s
+
+# Raw tape file format (.tap files)
+# Esa Hyyti <esa@netlab.tkk.fi>
+0 string C64-TAPE-RAW C64 Raw Tape File (.tap),
+>0x0c byte x Version:%u,
+>0x10 lelong x Length:%u cycles
+
+# magic for Goattracker2, http://covertbitops.c64.org/
+# from Alex Myczko <alex@aiei.ch>
+0 string GTS5 GoatTracker 2 song
+>4 string >\0 \b, "%s"
+>36 string >\0 \b by %s
+>68 string >\0 \b (C) %s
+>100 byte >0 \b, %u subsong(s)
+
+# CBM BASIC (cc65 compiled)
+# Summary: binary executable or Basic program for Commodore C64 computers
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Commodore_BASIC_tokenized_file
+# Reference: https://www.c64-wiki.com/wiki/BASIC_token
+# https://github.com/thezerobit/bastext/blob/master/bastext.doc
+# http://mark0.net/download/triddefs_xml.7z/defs/p/prg-c64.trid.xml
+# TODO: unify Commodore BASIC/program sub routines
+# Note: "PUCrunch archive data" moved from ./archive and merged with c64-exe
+0 leshort 0x0801
+# display Commodore C64 BASIC program (strength=50) after "Lynx archive" (strength=330) handled by ./archive
+#!:strength +0
+# if first token is not SYS this implies BASIC program in most cases
+>6 ubyte !0x9e
+# but sELF-ExTRACTING-zIP executable unzp6420.prg contains SYS token at end of second BASIC line (at 0x35)
+>>23 search/30 \323ELF-E\330TRACTING-\332IP
+>>>0 use c64-exe
+>>23 default x
+>>>0 use c64-prg
+# if first token is SYS this implies binary executable
+>6 ubyte =0x9e
+>>0 use c64-exe
+# display information about C64 binary executable (memory address, line number, token)
+0 name c64-exe
+>0 uleshort x Commodore C64
+# http://a1bert.kapsi.fi/Dev/pucrunch/
+# start address 0801h; next offset 080bh; BASIC line number is 239=00EFh; BASIC instruction is SYS 2061
+# the above combination appartly also occur for other Commodore programs like: gunzip111.c64.prg
+# and there exist PUCrunch archive for other machines like C16 with other magics
+>0 string \x01\x08\x0b\x08\xef\x00\x9e\x32\x30\x36\x31 program, probably PUCrunch archive data
+!:mime application/x-compress-pucrunch
+!:ext prg/pck
+>0 string !\x01\x08\x0b\x08\xef\x00\x9e\x32\x30\x36\x31 program
+!:mime application/x-commodore-exec
+!:ext prg/
+# start address like: 801h
+>0 uleshort !0x0801 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x800) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# valid 2nd BASIC fragment found only in sELF-ExTRACTING-zIP executable unzp6420.prg
+>>23 search/30 \323ELF-E\330TRACTING-\332IP
+# jump again from beginning
+>>>(2.s-0x800) ubyte x
+>>>>&0 use basic-line
+# Zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# display information about tokenized C64 BASIC program (memory address, line number, token)
+0 name c64-prg
+>0 uleshort x Commodore C64 BASIC program
+!:mime application/x-commodore-basic
+# Tokenized BASIC programs were stored by Commodore as file type program "PRG" in separate field in directory structures.
+# So file name can have no suffix like in saveroms; When transferring to other platforms, they are often saved with .prg extensions.
+# BAS suffix is typically used for the BASIC source but also found in program pods.bas
+!:ext prg/bas/
+# start address like: 801h
+>0 uleshort !0x0801 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x0800) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# 2nd BASIC fragment
+>>&0 use basic-line
+# zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# Summary: binary executable or Basic program for Commodore C128 computers
+# URL: https://en.wikipedia.org/wiki/Commodore_128
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/prg-c128.trid.xml
+# From: Joerg Jenderek
+# Note: Commodore 128 BASIC 7.0 variant; there exist varaints with different start addresses
+0 leshort 0x1C01
+!:strength +1
+# GRR: line above with strength 51 (50+1) is too generic because it matches SVr3 curses screen image, big-endian with strength (50) handled by ./terminfo
+# probably skip SVr3 curses images with "invalid high" second line offset
+>2 uleshort <0x1D02
+# skip foo with "invalid low" second line offset
+>>2 uleshort >0x1C06
+# if first token is not SYS this implies BASIC program
+>>>6 ubyte !0x9e
+>>>>0 use c128-prg
+# if first token is SYS this implies binary executable
+>>>6 ubyte =0x9e
+>>>>0 use c128-exe
+# Summary: binary executable or Basic program for Commodore C128 computers
+# Note: Commodore 128 BASIC 7.1 extension by Rick Simon
+# start adress 132Dh
+#0 leshort 0x132D THIS_IS_C128_7.1
+#>0 use c128-prg
+# Summary: binary executable or Basic program for Commodore C128 computers
+# Note: Commodore 128 BASIC 7.0 saved with graphics mode enabled
+# start adress 4001h
+#0 leshort 0x4001 THIS_IS_C128_GRAPHIC
+#>0 use c128-prg
+# display information about tokenized C128 BASIC program (memory address, line number, token)
+0 name c128-prg
+>0 uleshort x Commodore C128 BASIC program
+!:mime application/x-commodore-basic
+!:ext prg
+# start address like: 1C01h
+>0 uleshort !0x1C01 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x1C00) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# 2nd BASIC fragment
+>>&0 use basic-line
+# Zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# display information about C128 program (memory address, line number, token)
+0 name c128-exe
+>0 uleshort x Commodore C128 program
+!:mime application/x-commodore-exec
+!:ext prg/
+# start address like: 1C01h
+>0 uleshort !0x1C01 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x1C00) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# no valid 2nd BASIC fragment in Commodore executables
+#>>&0 use basic-line
+# Zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# Summary: binary executable or Basic program for Commodore C16/VIC-20/Plus4 computers
+# URL: https://en.wikipedia.org/wiki/Commodore_Plus/4
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/prg-vic20.trid.xml
+# defs/p/prg-plus4.trid.xml
+# From: Joerg Jenderek
+# Note: there exist VIC-20 variants with different start address
+# GRR: line below is too generic because it matches Novell LANalyzer capture
+# with regular trace header record handled by ./sniffer
+0 leshort 0x1001
+# skip regular Novell LANalyzer capture (novell-2.tr1 novell-lanalyzer.tr1 novell-win10.tr1) with "invalid low" token value 54h
+>6 ubyte >0x7F
+# skip regular Novell LANalyzer capture (novell-2.tr1 novell-lanalyzer.tr1 novell-win10.tr1) with "invalid low" second line offset 4Ch
+#>>2 uleshort >0x1006 OFFSET_NOT_TOO_LOW
+# skip foo with "invalid high" second line offset but not for 0x123b (Minefield.prg)
+#>>>2 uleshort <0x1102 OFFSET_NOT_TOO_HIGH
+# if first token is not SYS this implies BASIC program
+>>6 ubyte !0x9e
+# valid second end of line separator implies BASIC program
+>>>(2.s-0x1000) ubyte =0
+>>>>0 use c16-prg
+# invalid second end of line separator !=0 implies binary executable like: Minefield.prg
+>>>(2.s-0x1000) ubyte !0
+>>>>0 use c16-exe
+# if first token is SYS this implies binary executable
+>>6 ubyte =0x9e
+>>>0 use c16-exe
+# display information about C16 program (memory address, line number, token)
+0 name c16-exe
+>0 uleshort x Commodore C16/VIC-20/Plus4 program
+!:mime application/x-commodore-exec
+!:ext prg/
+# start address like: 1001h
+>0 uleshort !0x1001 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x1000) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# no valid 2nd BASIC fragment in excutables
+#>>&0 use basic-line
+# Zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# display information about tokenized C16 BASIC program (memory address, line number, token)
+0 name c16-prg
+>0 uleshort x Commodore C16/VIC-20/Plus4 BASIC program
+!:mime application/x-commodore-basic
+!:ext prg
+# start address like: 1001h
+>0 uleshort !0x1001 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x1000) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# 2nd BASIC fragment
+>>&0 use basic-line
+# Zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# Summary: binary executable or Basic program for Commodore VIC-20 computer with 8K RAM expansion
+# URL: https://en.wikipedia.org/wiki/VIC-20
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/prg-vic20-8k.trid.xml
+# From: Joerg Jenderek
+# Note: Basic v2.0 with Basic v4.0 extension (VIC20); there exist VIC-20 variants with different start addresses
+# start adress 1201h
+0 leshort 0x1201
+# if first token is not SYS this implies BASIC program
+>6 ubyte !0x9e
+>>0 use vic-prg
+# if first token is SYS this implies binary executable
+>6 ubyte =0x9e
+>>0 use vic-exe
+# display information about Commodore VIC-20 BASIC+8K program (memory address, line number, token)
+0 name vic-prg
+>0 uleshort x Commodore VIC-20 +8K BASIC program
+!:mime application/x-commodore-basic
+!:ext prg
+# start address like: 1201h
+>0 uleshort !0x1201 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x1200) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# 2nd BASIC fragment
+>>&0 use basic-line
+# Zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# display information about Commodore VIC-20 +8K program (memory address, line number, token)
+0 name vic-exe
+>0 uleshort x Commodore VIC-20 +8K program
+!:mime application/x-commodore-exec
+!:ext prg/
+# start address like: 1201h
+>0 uleshort !0x1201 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x0400) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# no valid 2nd BASIC fragment in excutables
+#>>&0 use basic-line
+# Zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# Summary: binary executable or Basic program for Commodore PET computers
+# URL: https://en.wikipedia.org/wiki/Commodore_PET
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/prg-pet.trid.xml
+# From: Joerg Jenderek
+# start adress 0401h
+0 leshort 0x0401
+!:strength +1
+# GRR: line above with strength 51 (50+1) is too generic because it matches TTComp archive data, ASCII, 1K dictionary
+# (strength=48=50-2) handled by ./archive and shared library (strength=50) handled by ./ibm6000
+# skip TTComp archive data, ASCII, 1K dictionary ttcomp-ascii-1k.bin with "invalid high" second line offset 4162h
+>2 uleshort <0x0502
+# skip foo with "invalid low" second line offset
+#>>2 uleshort >0x0406 OFFSET_NOT_TOO_LOW
+# skip bar with "invalid end of line"
+#>>>(2.s-0x0400) ubyte =0 END_OF_LINE_OK
+# if first token is not SYS this implies BASIC program
+>>6 ubyte !0x9e
+>>>0 use pet-prg
+# if first token is SYS this implies binary executable
+>>6 ubyte =0x9e
+>>>0 use pet-exe
+# display information about Commodore PET BASIC program (memory address, line number, token)
+0 name pet-prg
+>0 uleshort x Commodore PET BASIC program
+!:mime application/x-commodore-basic
+!:ext prg
+# start address like: 0401h
+>0 uleshort !0x0401 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x0400) ubyte x
+# 2nd BASIC fragment
+>>&0 use basic-line
+# zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# display information about Commodore PET program (memory address, line number, token)
+0 name pet-exe
+>0 uleshort x Commodore PET program
+!:mime application/x-commodore-exec
+!:ext prg/
+# start address like: 0401h
+>0 uleshort !0x0401 \b, start address %#4.4x
+# 1st BASIC fragment
+>2 use basic-line
+# jump to 1 byte before next BASIC fragment; this must be zero-byte marking the end of line
+>(2.s-0x0400) ubyte x
+>>&-1 ubyte !0 \b, no EOL=%#x
+# no valid 2nd BASIC fragment in excutables
+#>>&0 use basic-line
+# Zero-byte marking the end of the BASIC line
+>-3 ubyte !0 \b, 3 last bytes %#2.2x
+# Two zero-bytes in place of the pointer to next BASIC line indicates the end of the program
+>>-2 ubeshort x \b%4.4x
+# display information about tokenized BASIC line (memory address, line number, Token)
+0 name basic-line
+# pointer to memory address of beginning of "next" BASIC line
+# greater then previous offset but maximal 100h difference
+>0 uleshort x \b, offset %#4.4x
+# offset 0x0000 indicates the end of BASIC program; so bytes afterwards may be some other data
+>0 uleshort 0
+# not line number but first 2 data bytes
+>>2 ubeshort x \b, data %#4.4x
+# not token but next 2 data bytes
+>>4 ubeshort x \b%4.4x
+# not token arguments but next data bytes
+>>6 ubequad x \b%16.16llx
+>>14 ubequad x \b%16.16llx...
+# like 0x0d20352020204c594e5820495820204259205749 "\r 5 LYNX IX BY WILL CORLEY" for LyNX archive Darkon.lnx handled by ./archive
+#>>3 string x "%-0.30s"
+>0 uleshort >0
+# BASIC line number with range from 0 to 65520; practice to increment numbers by some value (5, 10 or 100)
+>>2 uleshort x \b, line %u
+# https://www.c64-wiki.com/wiki/BASIC_token
+# The "high-bit" bytes from #128-#254 stood for the various BASIC commands and mathematical operators
+>>4 ubyte x \b, token (%#x)
+# https://www.c64-wiki.com/wiki/REM
+>>4 string \x8f REM
+# remark string like: ** SYNTHESIZER BY RICOCHET **
+>>>5 string >\0 %s
+#>>>>&1 uleshort x \b, NEXT OFFSET %#4.4x
+# https://www.c64-wiki.com/wiki/PRINT
+>>4 string \x99 PRINT
+# string like: "Hello world" "\021 \323ELF-E\330TRACTING-\332IP (64 ONLY)\016\231":\2362141
+>>>5 string x %s
+#>>>>&0 ubequad x AFTER_PRINT=%#16.16llx
+# https://www.c64-wiki.com/wiki/POKE
+>>4 string \x97 POKE
+# <Memory address>,<number>
+>>>5 regex \^[0-9,\040]+ %s
+# BASIC command delimiter colon (:=3Ah)
+>>>>&-2 ubyte =0x3A
+# after BASIC command delimiter colon remaining (<255) other tokenized BASIC commands
+>>>>>&0 string x "%s"
+# https://www.c64-wiki.com/wiki/SYS 0x9e=\236
+>>4 string \x9e SYS
+# SYS <Address> parameter is a 16-bit unsigned integer; in the range 0 - 65535
+>>>5 regex \^[0-9]{1,5} %s
+# maybe followed by spaces, "control-characters" or colon (:) followed by next commnds or in victracker.prg
+# (\302(43)\252256\254\302(44)\25236) /T.L.R/
+#>>>5 string x SYS_STRING="%s"
+# https://www.c64-wiki.com/wiki/GOSUB
+>>4 string \x8d GOSUB
+# <line>
+>>>5 string >\0 %s
diff --git a/contrib/libs/libmagic/magic/Magdir/cad b/contrib/libs/libmagic/magic/Magdir/cad
new file mode 100644
index 0000000000..0bead6eeb4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/cad
@@ -0,0 +1,437 @@
+
+#------------------------------------------------------------------------------
+# $File: cad,v 1.31 2022/12/09 15:36:23 christos Exp $
+# autocad: file(1) magic for cad files
+#
+
+# Microstation DGN/CIT Files (www.bentley.com)
+# Last updated July 29, 2005 by Lester Hightower
+# DGN is the default file extension of Microstation/Intergraph CAD files.
+# CIT is the proprietary raster format (similar to TIFF) used to attach
+# raster underlays to Microstation DGN (vector) drawings.
+#
+# http://www.wotsit.org/search.asp
+# https://filext.com/detaillist.php?extdetail=DGN
+# https://filext.com/detaillist.php?extdetail=CIT
+#
+# https://www.bentley.com/products/default.cfm?objectid=97F351F5-9C35-4E5E-89C2
+# 3F86C928&method=display&p_objectid=97F351F5-9C35-4E5E-89C280A93F86C928
+# https://www.bentley.com/products/default.cfm?objectid=A5C2FD43-3AC9-4C71-B682
+# 721C479F&method=display&p_objectid=A5C2FD43-3AC9-4C71-B682C7BE721C479F
+#
+# URL: https://en.wikipedia.org/wiki/MicroStation
+# reference: http://dgnlib.maptools.org/dgn.html
+# http://dgnlib.maptools.org/dl/ref18.pdf
+# Update: Joerg Jenderek
+# Note: verfied by command like `dgndump seed2d_b.dgn`
+# test for level 8 and type 5 or 9
+0 beshort&0x3F73 0x0801
+# level of element like 8
+#>0 ubyte&0x3F x \b, level %u
+#>0 ubyte &0x80 \b, complex
+#>0 ubyte &0x40 \b, reserved
+# type of element 9~TCB 8~Digitizer setup 5~Group Data Elements
+#>1 ubyte&0x7F x \b, type %u
+# words to follow in element: 17H~CEL library 2FEh~DGN 9FEh,DFEh~CIT
+#>2 uleshort x \b, words %#4.4x to follow
+# test for 3 reserved 0 bytes in CIT or "conversion" in ViewInfo structure (DGN CEL)
+#>508 ubelong x \b, RESERVED %8.8x
+>508 ubelong&0xFFffFF00 =0
+# test for level 8 and type 9 for INGR raster image
+>>0 beshort 0x0809
+# test for length of 1st element is multiple of blocks a 512 bytes
+>>>2 ubyte 0xfe
+>>>>0 use ingr-image
+# test for DGN or CEL by jump words (uleshort) forward to next element
+>(2.s*2) ulong x
+# 2nd element type: 8~Digitizer~DesiGNfile 1~library cell header other~CIT
+#>>&1 ubyte&0x7F x \b, 2nd type %u
+# DGN
+>>&1 ubyte&0x7F 8
+>>>2 uleshort =0x02FE Bentley/Intergraph Microstation CAD drawing
+!:mime application/x-bentley-dgn
+!:ext dgn
+# The 0x40 bit of this byte is 1 if the file is 3D, otherwise 0
+>>>>1214 ubyte &0x40 3D
+>>>>1214 ubyte ^0x40 2D
+# 2 chars for name of subunits like ft FT in IN mu m mm '\0 '\040
+>>>>1120 string x \b, units %-.2s
+# 2 chars for name of master unit like IN in ML SU tn th TH HU mm "\0 "\040 \0\0
+>>>>1122 string >\0 %-.2s
+#>>>>1120 ubelong x \b, units %#8.8x
+# element range low,high x y z like xlow=0 08010000h 01080000h
+#>>>>4 ubelong !0 \b, xlow %8.8x
+#>>>>8 ubelong !0 \b, ylow %8.8x
+#>>>>12 ubelong !0 \b, zlow %8.8x
+#>>>>16 ubelong !0 \b, xhigh %8.8x
+#>>>>20 ubelong !0 \b, yhigh %8.8x
+#>>>>24 ubelong !0 \b, zhigh %8.8x
+# graphic group number; all other elements in that group have same non-0 number
+#>>>>28 leshort x \b, grphgrp %#4.4x
+# words to optional attribute linkage
+#>>>>30 ubyte x \b, attindx \%o
+#>>>>31 ubyte x \b\%o
+# >>30 string \026\105 DGNFile
+# >>30 string \034\105 DGNFile
+# >>30 string \073\107 DGNFile
+# >>30 string \073\110 DGNFile
+# >>30 string \106\107 DGNFile
+# >>30 string \110\103 DGNFile
+# >>30 string \120\104 DGNFile
+# >>30 string \172\104 DGNFile
+# >>30 string \172\105 DGNFile
+# >>30 string \172\106 DGNFile
+# >>30 string \234\106 DGNFile
+# >>30 string \273\105 DGNFile
+# >>30 string \306\106 DGNFile
+# >>30 string \310\104 DGNFile
+# >>30 string \341\104 DGNFile
+# >>30 string \372\103 DGNFile
+# >>30 string \372\104 DGNFile
+# >>30 string \372\106 DGNFile
+# >>30 string \376\103 DGNFile
+# elements properties indicator
+#>>>>32 uleshort !0 \b, properties %#4.4x
+# class 0~Primary
+#>>>>>32 uleshort&0x000F !0 \b, class %#4.4x
+# Symbology
+#>>>>>34 uleshort x \b, Symbology %#4.4x
+# test for 2nd element type 1~library cell header
+>>&1 ubyte&0x7F 1
+# test for 1st element with level 8 and type 5 for cell library
+>>>0 beshort 0x0805 Bentley/Intergraph Microstation CAD cell library
+!:mime application/x-bentley-cel
+!:ext cel
+#
+# URL: http://fileformats.archiveteam.org/wiki/Intergraph_Raster
+# reference: https://web.archive.org/web/20140903185431/
+# http://oreilly.com/www/centers/gff/formats/ingr/index.htm
+# note: verfied by command like `nconvert -fullinfo LONGLAT.CIT`
+# display information for intergraph raster bitmap
+0 name ingr-image
+# in 5.37 "Microstation CITFile" "Bentley/Intergraph MicroStation CIT raster CAD"
+# DataTypeCode indicates format, depth of the pixel data and used compression
+>4 uleshort x Intergraph raster image
+>>4 uleshort 0x0009 \b, Run-Length Encoded 1-bit
+!:mime image/x-intergraph-rle
+!:ext rel
+>>4 uleshort 0x0018 \b, CCITT Group 4 1-bit
+!:mime image/x-intergraph-cit
+!:ext cit
+>>4 uleshort 27 \b, Adaptive RLE RGB
+!:mime image/x-intergraph-rgb
+!:ext rgb
+>>4 default x
+>>>4 uleshort x \b, Type %u
+!:mime image/x-intergraph
+# TODO:
+#>4 uleshort 0 \b, no data
+# ...
+#>4 uleshort 0x0045 \b, Continuous Tone CMKY (Uncompressed)
+# ApplicationType: 0~generic raster image 3~drawing, scanning
+# 8~I/IMAGE and MicroStation Imager 9~ModelView
+>6 uleshort !0 \b, ApplicationType %u
+#>6 uleshort x \b, ApplicationType %u
+# XViewOrigin; Raster grid data X origin
+#>8 ulequad !0 \b, XViewOrigin %llx
+# PixelsPerLine is the number of pixels in a scan line of bitmapp
+>184 ulelong x \b, %u x
+# NumberOfLines is height of the raster data in scanlines
+>188 ulelong x %u
+# DeviceResolution; resolution of scanning device
+# positive indicates number of micros between lines; negative indicates DPI
+#>192 leshort x \b, DeviceResolution %d
+# ScanlineOrient indicates the origin and the orientation of the scan lines
+#>194 ubyte x \b, ScanlineOrient %x
+>194 ubyte x \b, orientation
+>194 ubyte &0x01 right
+>194 ubyte ^0x01 left
+>194 ubyte &0x02 down
+>194 ubyte ^0x02 top
+>194 ubyte &0x04 horizontal
+>194 ubyte ^0x04 vertical
+# ScannableFlag; Scanline indexing method used
+#>195 ubyte !0 \b, ScannableFlag %#x
+# RotationAngle; Rotation angle of raster data
+#>196 ubequad !0 \b, RotationAngle %#llx
+# SkewAngle; Skew angle of raster data
+#>204 ubequad !0 \b, SkewAngle %llx
+# DataTypeModifier; Additional raster data format info
+#>212 uleshort !0 \b, DataTypeModifier %#4.4x
+# DesignFile[66]; Name of the design file
+>214 string >\0 \b, DesignFile %-.66s
+# DatabaseFile[66]; Name of the database file
+>280 string >\0 \b, DatabaseFile %-.66s
+# ParentGridFile[66]; Name of parent grid file
+>346 string >\0 \b, ParentGridFile %-.66s
+# FileDescription[80]; Text description of file and contents
+>412 string >\0 \b, FileDescription %-.80s
+# MinValue
+#>492 ubequad !0 \b, MinValue %#llx
+# MaxValue
+#>500 ubequad !0 \b, MaxValue %#llx
+# Reserved[3]; Unused (always 0)
+#>508 ubelong&0xFFffFF00 x \b, RESERVED %8.8x
+# GridFileVersion; Grid File Version like 2 3
+#>511 ubyte x \b, GridFileVersion %x
+
+# AutoCAD
+# Merge of the different contributions and updates from https://en.wikipedia.org/wiki/Dwg
+# and https://www.iana.org/assignments/media-types/image/vnd.dwg
+0 string MC0.0 DWG AutoDesk AutoCAD Release 1.0
+!:mime image/vnd.dwg
+0 string AC1.2 DWG AutoDesk AutoCAD Release 1.2
+!:mime image/vnd.dwg
+0 string AC1.3 DWG AutoDesk AutoCAD Release 1.3
+!:mime image/vnd.dwg
+0 string AC1.40 DWG AutoDesk AutoCAD Release 1.40
+!:mime image/vnd.dwg
+0 string AC1.50 DWG AutoDesk AutoCAD Release 2.05
+!:mime image/vnd.dwg
+0 string AC2.10 DWG AutoDesk AutoCAD Release 2.10
+!:mime image/vnd.dwg
+0 string AC2.21 DWG AutoDesk AutoCAD Release 2.21
+!:mime image/vnd.dwg
+0 string AC2.22 DWG AutoDesk AutoCAD Release 2.22
+!:mime image/vnd.dwg
+0 string AC1001 DWG AutoDesk AutoCAD Release 2.22
+!:mime image/vnd.dwg
+0 string AC1002 DWG AutoDesk AutoCAD Release 2.50
+!:mime image/vnd.dwg
+0 string AC1003 DWG AutoDesk AutoCAD Release 2.60
+!:mime image/vnd.dwg
+0 string AC1004 DWG AutoDesk AutoCAD Release 9
+!:mime image/vnd.dwg
+0 string AC1006 DWG AutoDesk AutoCAD Release 10
+!:mime image/vnd.dwg
+0 string AC1009 DWG AutoDesk AutoCAD Release 11/12
+!:mime image/vnd.dwg
+# AutoCAD DWG versions R13/R14 (www.autodesk.com)
+# Written December 01, 2003 by Lester Hightower
+# Based on the DWG File Format Specifications at http://www.opendwg.org/
+# AutoCad, from Nahuel Greco
+# AutoCAD DWG versions R12/R13/R14 (www.autodesk.com)
+0 string AC1012 DWG AutoDesk AutoCAD Release 13
+!:mime image/vnd.dwg
+0 string AC1013 DWG AutoDesk AutoCAD Release 13c3
+!:mime image/vnd.dwg
+0 string AC1014 DWG AutoDesk AutoCAD Release 14
+!:mime image/vnd.dwg
+0 string AC1015 DWG AutoDesk AutoCAD 2000
+!:mime image/vnd.dwg
+
+# A new version of AutoCAD DWG
+# Sergey Zaykov (mail_of_sergey@mail.ru, sergey_zaikov@rambler.ru,
+# ICQ 358572321)
+# From various sources like:
+# https://autodesk.blogs.com/between_the_lines/autocad-release-history.html
+0 string AC1018 DWG AutoDesk AutoCAD 2004/2005/2006
+!:mime image/vnd.dwg
+0 string AC1021 DWG AutoDesk AutoCAD 2007/2008/2009
+!:mime image/vnd.dwg
+0 string AC1024 DWG AutoDesk AutoCAD 2010/2011/2012
+!:mime image/vnd.dwg
+0 string AC1027 DWG AutoDesk AutoCAD 2013-2017
+!:mime image/vnd.dwg
+
+# From GNU LibreDWG
+0 string AC1032 DWG AutoDesk AutoCAD 2018/2019/2020
+!:mime image/vnd.dwg
+0 string AC1035 DWG AutoDesk AutoCAD 2021
+!:mime image/vnd.dwg
+
+# KOMPAS 2D drawing from ASCON
+# This is KOMPAS 2D drawing or fragment of drawing but is not detailed nor
+# gathered nor specification
+# ASCON https://ascon.net/main/ in English,
+# https://ascon.ru/ main site in Russian
+# Extension is CDW for drawing and FRW for fragment of drawing
+# Sergey Zaykov (mail_of_sergey@mail.ru, sergey_zaikov@rambler.ru,
+# ICQ 358572321, https://vkontakte.ru/id16076543)
+# From:
+# https://sd.ascon.ru/otrs/customer.pl?Action=CustomerFAQ&CategoryID=4&ItemID=292
+# (in russian) and my experiments
+0 string KF
+>2 belong 0x4E00000C Kompas drawing 12.0 SP1
+>2 belong 0x4D00000C Kompas drawing 12.0
+>2 belong 0x3200000B Kompas drawing 11.0 SP1
+>2 belong 0x3100000B Kompas drawing 11.0
+>2 belong 0x2310000A Kompas drawing 10.0 SP1
+>2 belong 0x2110000A Kompas drawing 10.0
+>2 belong 0x08000009 Kompas drawing 9.0 SP1
+>2 belong 0x05000009 Kompas drawing 9.0
+>2 belong 0x33010008 Kompas drawing 8+
+>2 belong 0x1A000008 Kompas drawing 8.0
+>2 belong 0x2C010107 Kompas drawing 7+
+>2 belong 0x05000007 Kompas drawing 7.0
+>2 belong 0x32000006 Kompas drawing 6+
+>2 belong 0x09000006 Kompas drawing 6.0
+>2 belong 0x5C009005 Kompas drawing 5.11R03
+>2 belong 0x54009005 Kompas drawing 5.11R02
+>2 belong 0x51009005 Kompas drawing 5.11R01
+>2 belong 0x22009005 Kompas drawing 5.10R03
+>2 belong 0x22009005 Kompas drawing 5.10R02 mar
+>2 belong 0x21009005 Kompas drawing 5.10R02 febr
+>2 belong 0x19009005 Kompas drawing 5.10R01
+>2 belong 0xF4008005 Kompas drawing 5.9R01.003
+>2 belong 0x1C008005 Kompas drawing 5.9R01.002
+>2 belong 0x11008005 Kompas drawing 5.8R01.003
+
+# CAD: file(1) magic for computer aided design files
+# Phillip Griffith <phillip dot griffith at gmail dot com>
+# AutoCAD magic taken from the Open Design Alliance's OpenDWG specifications.
+#
+
+# 3DS (3d Studio files)
+0 leshort 0x4d4d
+>6 leshort 0x2
+>>8 lelong 0xa
+>>>16 leshort 0x3d3d 3D Studio model
+# Beat sgi MMV
+!:strength +20
+!:mime image/x-3ds
+!:ext 3ds
+
+# MegaCAD 2D/3D drawing (.prt)
+# https://megacad.de/
+# From: Markus Heidelberg <markus.heidelberg@web.de>
+0 string MegaCad23\0 MegaCAD 2D/3D drawing
+
+# Hoops CAD files
+# https://docs.techsoft3d.com/visualize/3df/latest/build/general/hsf/\
+# HSF_architecture.html
+# Stephane Charette <stephane.charette@gmail.com>
+0 string ;;\040HSF\040V OpenHSF (Hoops Stream Format)
+>7 regex/9 V[.0-9]{4,5}\040 %s
+!:ext hsf
+
+# AutoCAD Drawing Exchange Format
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/DXF
+# https://en.wikipedia.org/wiki/AutoCAD_DXF
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/
+# dxf-var0.trid.xml dxf-var0u.trid.xml dxf-var2.trid.xml dxf-var2u.trid.xml
+# Note: called "AutoCAD Drawing eXchange Format" by TrID and
+# "Drawing Interchange File Format (ASCII)" by DROID
+# GRR: some samples does not match 1st test like: abydos.dxf
+0 regex \^[\ \t]*0\r?\000$
+>1 regex \^[\ \t]*SECTION\r?$
+>>2 regex \^[\ \t]*2\r?$
+# GRR: some samples without HEADER section like: airplan2.dxf
+>>>3 regex \^[\ \t]*HEADER\r?$ AutoCAD Drawing Exchange Format
+#!:mime application/x-dxf
+!:mime image/vnd.dxf
+!:ext dxf
+# DROID PUID fmt/64 fmt-64-signature-id-99.dxf
+>>>>&1 search/8192 MC0.0 \b, 1.0
+# DROID PUID fmt/65 fmt-65-signature-id-100.dxf
+>>>>&1 search/8192 AC1.2 \b, 1.2
+# DROID PUID fmt/66 fmt-66-signature-id-101.dxf
+>>>>&1 search/8192 AC1.3 \b, 1.3
+# DROID PUID fmt/67 fmt-67-signature-id-102.dxf
+>>>>&1 search/8192 AC1.40 \b, 1.4
+# DROID PUID fmt/68 fmt-68-signature-id-103.dxf
+>>>>&1 search/8192 AC1.50 \b, 2.0
+# DROID PUID fmt/69 fmt-69-signature-id-104.dxf
+>>>>&1 search/8192 AC2.10 \b, 2.1
+# DROID PUID fmt/70 fmt-70-signature-id-105.dxf
+>>>>&1 search/8192 AC2.21 \b, 2.2
+# DROID PUID fmt/71 fmt-71-signature-id-106.dxf
+>>>>&1 search/8192 AC1002 \b, 2.5
+# DROID PUID fmt/72 fmt-72-signature-id-107.dxf
+>>>>&1 search/8192 AC1003 \b, 2.6
+# DROID PUID fmt/73 fmt-73-signature-id-108.dxf
+>>>>&1 search/8192 AC1004 \b, R9
+>>>>&1 search/8192 AC1006 \b, R10
+# http://cd.textfiles.com/amigaenv/DXF/OBJEKTE/LASTMINUTE/apple.dxf
+#>>>>&1 search/8192 AC1008 \b, Rfoo
+>>>>&1 search/8192 AC1009 \b, R11/R12
+>>>>&1 search/8192 AC1012 \b, R13
+>>>>&1 search/8192 AC1013 \b, R13c3
+>>>>&1 search/8192 AC1014 \b, R14
+>>>>&1 search/8192 AC1015 \b, version 2000
+>>>>&1 search/8192 AC1018 \b, version 2004
+>>>>&1 search/8192 AC1021 \b, version 2007
+>>>>&1 search/8192 AC1024 \b, version 2010
+>>>>&1 search/8192 AC1027 \b, version 2013
+>>>>&1 search/8192 AC1032 \b, version 2018
+>>>>&1 search/8192 AC1035 \b, version 2021
+
+# The Sketchup 3D model format https://www.sketchup.com/
+0 string \xff\xfe\xff\x0e\x53\x00\x6b\x00\x65\x00\x74\x00\x63\x00\x68\x00\x55\x00\x70\x00\x20\x00\x4d\x00\x6f\x00\x64\x00\x65\x00\x6c\x00 SketchUp Model
+!:mime application/vnd.sketchup.skp
+!:ext skp
+
+4 regex/b P[0-9][0-9]\\.[0-9][0-9][0-9][0-9]\\.[0-9][0-9][0-9][0-9]\\.[0-9] NAXOS CAD System file from version %s
+!:strength +40
+
+# glTF (GL Transmission Format) - by the Khronos Group
+# Reference: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#glb-file-format-specification
+0 string glTF glTF binary model
+>4 ulelong x \b, version %d
+>8 ulelong x \b, length %d bytes
+!:mime model/gltf-binary
+!:ext glb
+
+# FBX (FilmBoX) - by Kaydara/Autodesk
+# Reference: https://code.blender.org/2013/08/fbx-binary-file-format-specification
+0 string Kaydara\ FBX\ Binary\ \ \0 Kaydara FBX model,
+>&2 ulelong x version %d
+!:ext fbx
+
+# PLY (Polygon File Format/Stanford Triangle Format) - by Greg Turk
+# Reference: https://web.archive.org/web/20161204152348/http://www.dcs.ed.ac.uk/teaching/cs4/www/graphics/Web/ply.html
+0 string ply\n PLY model,
+!:ext ply
+>4 string format\ ascii\ ASCII,
+>>&0 regex/6 [0-9.]+ version %s
+>4 string format\ binary binary,
+>>&0 string _little_endian\ little endian,
+>>>&0 regex/6 [0-9.]+ version %s
+>>&0 string _big_endian\ big endian,
+>>>&0 regex/6 [0-9.]+ version %s
+
+# VRML (Virtual Reality Modeling Language) - by the Web3D Consortium
+# From: Michel Briand <michelbriand@free.fr>
+# Reference: https://www.web3d.org/standards
+0 string/w #VRML\ V1.0\ ascii VRML 1 file
+!:mime model/vrml
+!:ext wrl
+0 string/w #VRML\ V2.0\ utf8 ISO/IEC 14772 VRML 97 file
+!:mime model/vrml
+!:ext wrl
+# X3D, VRML encoded
+0 string #X3D X3D (Extensible 3D) model, VRML format
+>4 string V
+>>5 regex/6 [0-9.]+ \b, version %s
+!:mime model/x3d+vrml
+!:ext x3dv
+
+## XML-based 3D CAD Formats
+# From: Michel Briand <michelbriand@free.fr>, Oliver Galvin <odg@riseup.net>
+0 string/w \<?xml\ version=
+!:strength + 5
+# X3D (Extensible 3D)
+# Schema: https://www.web3d.org/specifications/x3d-3.2.dtd
+# MIME Type: https://www.iana.org/assignments/media-types/model/x3d+xml
+# Example: https://www.web3d.org/x3d/content/examples/Basic/course/CreateX3DFromStringRandomSpheres.x3d
+>20 search/1000/w \<!DOCTYPE\ X3D X3D (Extensible 3D) model, XML document
+!:mime model/x3d+xml
+!:ext x3d
+# COLLADA (COLLAborative Design Activity) - by the Khronos Group
+# Schema: http://www.collada.org/2005/11/COLLADASchema
+# Reference: https://www.khronos.org/collada
+>20 search/1000/w \<COLLADA COLLADA model, XML document
+!:mime model/vnd.collada+xml
+!:ext dae
+# 3MF (3D Manufacturing Format) - by the 3MF Consortium
+# Schema: http://schemas.microsoft.com/3dmanufacturing/core/2015/02
+# Reference: https://3mf.io/specification
+>20 search/1000/w xmlns="http://schemas.microsoft.com/3dmanufacturing 3MF (3D Manufacturing Format) model, XML document
+!:mime model/3mf
+!:ext 3mf
+# AMF (Additive Manufacturing File)
+# Reference: https://www.astm.org/Standards/ISOASTM52915.htm
+>20 search/1000/w \<amf AMF (Additive Manufacturing Format) model, XML document
+!:mime application/x-amf
+!:ext amf
diff --git a/contrib/libs/libmagic/magic/Magdir/cafebabe b/contrib/libs/libmagic/magic/Magdir/cafebabe
new file mode 100644
index 0000000000..4f97cc0345
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/cafebabe
@@ -0,0 +1,107 @@
+
+#------------------------------------------------------------------------------
+# $File: cafebabe,v 1.28 2022/07/01 23:24:47 christos Exp $
+# Cafe Babes unite!
+#
+# Since Java bytecode and Mach-O universal binaries have the same magic number,
+# the test must be performed in the same "magic" sequence to get both right.
+# The long at offset 4 in a Mach-O universal binary tells the number of
+# architectures; the short at offset 4 in a Java bytecode file is the JVM minor
+# version and the short at offset 6 is the JVM major version. Since there are only
+# only 18 labeled Mach-O architectures at current, and the first released
+# Java class format was version 43.0, we can safely choose any number
+# between 18 and 39 to test the number of architectures against
+# (and use as a hack). Let's not use 18, because the Mach-O people
+# might add another one or two as time goes by...
+#
+### JAVA START ###
+# Reference: http://en.wikipedia.org/wiki/Java_class_file
+# Update: Joerg Jenderek
+0 belong 0xcafebabe
+>4 ubelong >30 compiled Java class data,
+!:mime application/x-java-applet
+#!:mime application/java-byte-code
+!:ext class
+>>6 ubeshort x version %d.
+>>4 ubeshort x \b%d
+# for debugging purpose version as hexadecimal to compare with Mach-O universal binary
+#>>4 ubelong x (%#8.8x)
+# Which is which?
+# https://docs.oracle.com/javase/specs/jvms/se6/html/ClassFile.doc.html
+#>>4 belong 0x002b (Java 0.?)
+#>>4 belong 0x032d (Java 1.0)
+#>>4 belong 0x032d (Java 1.1)
+>>4 belong 0x002e (Java 1.2)
+>>4 belong 0x002f (Java 1.3)
+>>4 belong 0x0030 (Java 1.4)
+>>4 belong 0x0031 (Java 1.5)
+>>4 belong 0x0032 (Java 1.6)
+>>4 belong 0x0033 (Java 1.7)
+>>4 belong 0x0034 (Java 1.8)
+>>4 belong 0x0035 (Java SE 9)
+>>4 belong 0x0036 (Java SE 10)
+>>4 belong 0x0037 (Java SE 11)
+>>4 belong 0x0038 (Java SE 12)
+>>4 belong 0x0039 (Java SE 13)
+>>4 belong 0x003A (Java SE 14)
+>>4 belong 0x003B (Java SE 15)
+>>4 belong 0x003C (Java SE 16)
+>>4 belong 0x003D (Java SE 17)
+>>4 belong 0x003E (Java SE 18)
+>>4 belong 0x003F (Java SE 19)
+>>4 belong 0x0040 (Java SE 20)
+# pool count unequal zero
+#>>8 beshort x \b, pool count %#x
+# pool table
+#>>10 ubequad x \b, pool %#16.16llx...
+
+0 belong 0xcafed00d JAR compressed with pack200,
+>5 byte x version %d.
+>4 byte x \b%d
+!:mime application/x-java-pack200
+
+
+0 belong 0xcafed00d JAR compressed with pack200,
+>5 byte x version %d.
+>4 byte x \b%d
+!:mime application/x-java-pack200
+
+### JAVA END ###
+### MACH-O START ###
+# URL: https://en.wikipedia.org/wiki/Mach-O
+
+0 name mach-o \b [
+# for debugging purpose CPU type as hexadecimal
+#>0 ubequad x CPU=%16.16llx
+# display CPU type as string like: i386 x86_64 ... armv7 armv7k ...
+>0 use mach-o-cpu \b
+# for debugging purpose print offset to 1st mach_header like:
+# 1000h 4000h seldom 2d000h 88000h 5b000h 10e000 h
+#>8 ubelong x at %#x offset
+>(8.L) indirect x \b:
+>0 belong x \b]
+
+# Reference: https://opensource.apple.com/source/cctools/cctools-949.0.1/
+# include/mach-o/fat.h
+# include/mach/machine.h
+0 belong 0xcafebabe
+>4 belong 1 Mach-O universal binary with 1 architecture:
+!:mime application/x-mach-binary
+>>8 use mach-o \b
+# nfat_arch; number of CPU architectures; highest is 18 for CPU_TYPE_POWERPC in 2020
+>4 ubelong >1
+>>4 ubelong <20 Mach-O universal binary with %d architectures:
+!:mime application/x-mach-binary
+>>>8 use mach-o \b
+>>>4 ubelong >1
+>>>>28 use mach-o \b
+>>>4 ubelong >2
+>>>>48 use mach-o \b
+>>>4 ubelong >3
+>>>>68 use mach-o \b
+>>>4 ubelong >4
+>>>>88 use mach-o \b
+>>>4 ubelong >5
+>>>>108 use mach-o \b
+
+### MACH-O END ###
diff --git a/contrib/libs/libmagic/magic/Magdir/cbor b/contrib/libs/libmagic/magic/Magdir/cbor
new file mode 100644
index 0000000000..c780dc6594
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/cbor
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# $File: cbor,v 1.1 2015/01/28 01:05:21 christos Exp $
+# cbor: file(1) magic for CBOR files as defined in RFC 7049
+
+0 string \xd9\xd9\xf7 Concise Binary Object Representation (CBOR) container
+!:mime application/cbor
+>3 ubyte <0x20 (positive integer)
+>3 ubyte <0x40
+>>3 ubyte >0x1f (negative integer)
+>3 ubyte <0x60
+>>3 ubyte >0x3f (byte string)
+>3 ubyte <0x80
+>>3 ubyte >0x5f (text string)
+>3 ubyte <0xa0
+>3 ubyte >0x7f (array)
+>3 ubyte <0xc0
+>>3 ubyte >0x9f (map)
+>3 ubyte <0xe0
+>>3 ubyte >0xbf (tagged)
+>3 ubyte >0xdf (other)
diff --git a/contrib/libs/libmagic/magic/Magdir/ccf b/contrib/libs/libmagic/magic/Magdir/ccf
new file mode 100644
index 0000000000..1d5ba19e00
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ccf
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: ccf,v 1.1 2022/02/15 12:57:45 christos Exp $
+# file(1) magic(5) data for Phillips remote controls
+
+# Exchange format for Philips Pronto universal infrared remote controls
+# A CCF file describes a learned/customized remote control,
+# i.e. it contains button UI and infrared pulse code definitions
+# (Georg Sauthoff)
+# http://files.remotecentral.com/download/45/pan-air-csakr.zip.html
+# https://github.com/gsauthof/pronto-ccf/blob/
+
+8 string @\xa5Z@_CCF
+>32 string CCF\x00 Philips Pronto IR remote control CCF
diff --git a/contrib/libs/libmagic/magic/Magdir/cddb b/contrib/libs/libmagic/magic/Magdir/cddb
new file mode 100644
index 0000000000..5d8a8517e2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/cddb
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: cddb,v 1.4 2009/09/19 16:28:08 christos Exp $
+# CDDB: file(1) magic for CDDB(tm) format CD text data files
+#
+# From <steve@gracenote.com>
+#
+# This is the /etc/magic entry to decode datafiles as used by
+# CDDB-enabled CD player applications.
+#
+
+0 search/1/w #\040xmcd CDDB(tm) format CD text data
diff --git a/contrib/libs/libmagic/magic/Magdir/chord b/contrib/libs/libmagic/magic/Magdir/chord
new file mode 100644
index 0000000000..00d0bec65a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/chord
@@ -0,0 +1,15 @@
+
+#------------------------------------------------------------------------------
+# $File: chord,v 1.5 2010/09/20 19:19:16 rrt Exp $
+# chord: file(1) magic for Chord music sheet typesetting utility input files
+#
+# From Philippe De Muyter <phdm@macqel.be>
+# File format is actually free, but many distributed files begin with `{title'
+#
+0 string {title Chord text file
+
+# Type: PowerTab file format
+# URL: http://www.power-tab.net/
+# From: Jelmer Vernooij <jelmer@samba.org>
+0 string ptab\003\000 Power-Tab v3 Tablature File
+0 string ptab\004\000 Power-Tab v4 Tablature File
diff --git a/contrib/libs/libmagic/magic/Magdir/cisco b/contrib/libs/libmagic/magic/Magdir/cisco
new file mode 100644
index 0000000000..0279bbb5b5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/cisco
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: cisco,v 1.4 2009/09/19 16:28:08 christos Exp $
+# cisco: file(1) magic for cisco Systems routers
+#
+# Most cisco file-formats are covered by the generic elf code
+#
+# Microcode files are non-ELF, 0x8501 conflicts with NetBSD/alpha.
+0 belong&0xffffff00 0x85011400 cisco IOS microcode
+>7 string >\0 for '%s'
+0 belong&0xffffff00 0x8501cb00 cisco IOS experimental microcode
+>7 string >\0 for '%s'
diff --git a/contrib/libs/libmagic/magic/Magdir/citrus b/contrib/libs/libmagic/magic/Magdir/citrus
new file mode 100644
index 0000000000..1801a55fa6
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/citrus
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: citrus,v 1.5 2021/01/04 19:48:31 christos Exp $
+# citrus locale declaration
+#
+
+0 string RuneCT Citrus locale declaration for LC_CTYPE
+0 string CtrsME Citrus locale declaration for LC_MESSAGES
+0 string CtrsMO Citrus locale declaration for LC_MONETARY
+0 string CtrsNU Citrus locale declaration for LC_NUMERIC
+0 string CtrsTI Citrus locale declaration for LC_TIME
+
diff --git a/contrib/libs/libmagic/magic/Magdir/clarion b/contrib/libs/libmagic/magic/Magdir/clarion
new file mode 100644
index 0000000000..9fa0049dab
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/clarion
@@ -0,0 +1,27 @@
+
+#------------------------------------------------------------------------------
+# $File: clarion,v 1.5 2014/04/30 21:41:02 christos Exp $
+# clarion: file(1) magic for # Clarion Personal/Professional Developer
+# (v2 and above)
+# From: Julien Blache <jb@jblache.org>
+
+# Database files
+# signature
+0 leshort 0x3343 Clarion Developer (v2 and above) data file
+# attributes
+>2 leshort &0x0001 \b, locked
+>2 leshort &0x0004 \b, encrypted
+>2 leshort &0x0008 \b, memo file exists
+>2 leshort &0x0010 \b, compressed
+>2 leshort &0x0040 \b, read only
+# number of records
+>5 lelong x \b, %d records
+
+# Memo files
+0 leshort 0x334d Clarion Developer (v2 and above) memo data
+
+# Key/Index files
+# No magic? :(
+
+# Help files
+0 leshort 0x49e0 Clarion Developer (v2 and above) help data
diff --git a/contrib/libs/libmagic/magic/Magdir/claris b/contrib/libs/libmagic/magic/Magdir/claris
new file mode 100644
index 0000000000..6a1b68fb22
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/claris
@@ -0,0 +1,48 @@
+
+#------------------------------------------------------------------------------
+# $File: claris,v 1.8 2016/07/18 19:23:38 christos Exp $
+# claris: file(1) magic for claris
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# Claris Works a word processor, etc.
+# Version 3.0
+
+# .pct claris works clip art files
+#0000000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
+#*
+#0001000 #010 250 377 377 377 377 000 213 000 230 000 021 002 377 014 000
+#null to byte 1000 octal
+514 string \377\377\377\377\000
+>0 string \0\0\0\0\0\0\0\0\0\0\0\0\0 Claris clip art
+514 string \377\377\377\377\001
+>0 string \0\0\0\0\0\0\0\0\0\0\0\0\0 Claris clip art
+
+# Claris works files
+# .cwk
+# Moved to Apple AppleWorks document
+#0 string \002\000\210\003\102\117\102\117\000\001\206 Claris works document
+# .plt
+0 string \020\341\000\000\010\010 Claris Works palette files .plt
+
+# .msp a dictionary file I am not sure about this I have only one .msp file
+0 string \002\271\262\000\040\002\000\164 Claris works dictionary
+
+# .usp are user dictionary bits
+# I am not sure about a magic header:
+#0000000 001 123 160 146 070 125 104 040 136 123 015 012 160 157 144 151
+# soh S p f 8 U D sp ^ S cr nl p o d i
+#0000020 141 164 162 151 163 164 040 136 123 015 012 144 151 166 040 043
+# a t r i s t sp ^ S cr nl d i v sp #
+
+# .mth Thesaurus
+# starts with \0 but no magic header
+
+# .chy Hyphenation file
+# I am not sure: 000 210 034 000 000
+
+# other claris files
+#./windows/claris/useng.ndx: data
+#./windows/claris/xtndtran.l32: data
+#./windows/claris/xtndtran.lst: data
+#./windows/claris/clworks.lbl: data
+#./windows/claris/clworks.prf: data
+#./windows/claris/userd.spl: data
diff --git a/contrib/libs/libmagic/magic/Magdir/clipper b/contrib/libs/libmagic/magic/Magdir/clipper
new file mode 100644
index 0000000000..484caeb89e
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/clipper
@@ -0,0 +1,65 @@
+
+#------------------------------------------------------------------------------
+# $File: clipper,v 1.9 2020/12/15 23:57:27 christos Exp $
+# clipper: file(1) magic for Intergraph (formerly Fairchild) Clipper.
+#
+# XXX - what byte order does the Clipper use?
+#
+# XXX - what's the "!" stuff:
+#
+# >18 short !074000,000000 C1 R1
+# >18 short !074000,004000 C2 R1
+# >18 short !074000,010000 C3 R1
+# >18 short !074000,074000 TEST
+#
+# I shall assume it's ANDing the field with the first value and
+# comparing it with the second, and rewrite it as:
+#
+# >18 short&074000 000000 C1 R1
+# >18 short&074000 004000 C2 R1
+# >18 short&074000 010000 C3 R1
+# >18 short&074000 074000 TEST
+#
+# as SVR3.1's "file" doesn't support anything of the "!074000,000000"
+# sort, nor does SunOS 4.x, so either it's something Intergraph added
+# in CLIX, or something AT&T added in SVR3.2 or later, or something
+# somebody else thought was a good idea; it's not documented in the
+# man page for this version of "magic", nor does it appear to be
+# implemented (at least not after I blew off the bogus code to turn
+# old-style "&"s into new-style "&"s, which just didn't work at all).
+#
+0 short 0575 CLIPPER COFF executable (VAX #)
+>20 short 0407 (impure)
+>20 short 0410 (5.2 compatible)
+>20 short 0411 (pure)
+>20 short 0413 (demand paged)
+>20 short 0443 (target shared library)
+>12 long >0 not stripped
+>22 short >0 - version %d
+0 short 0577 CLIPPER COFF executable
+>18 short&074000 000000 C1 R1
+>18 short&074000 004000 C2 R1
+>18 short&074000 010000 C3 R1
+>18 short&074000 074000 TEST
+>20 short 0407 (impure)
+>20 short 0410 (pure)
+>20 short 0411 (separate I&D)
+>20 short 0413 (paged)
+>20 short 0443 (target shared library)
+>12 long >0 not stripped
+>22 short >0 - version %d
+>48 long&01 01 alignment trap enabled
+>52 byte 1 -Ctnc
+>52 byte 2 -Ctsw
+>52 byte 3 -Ctpw
+>52 byte 4 -Ctcb
+>53 byte 1 -Cdnc
+>53 byte 2 -Cdsw
+>53 byte 3 -Cdpw
+>53 byte 4 -Cdcb
+>54 byte 1 -Csnc
+>54 byte 2 -Cssw
+>54 byte 3 -Cspw
+>54 byte 4 -Cscb
+#4 string pipe CLIPPER instruction trace
+#4 string prof CLIPPER instruction profile
diff --git a/contrib/libs/libmagic/magic/Magdir/clojure b/contrib/libs/libmagic/magic/Magdir/clojure
new file mode 100644
index 0000000000..1f1cddf9a2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/clojure
@@ -0,0 +1,30 @@
+#------------------------------------------------------------------------------
+# file: file(1) magic for Clojure
+# URL: https://clojure.org/
+# From: Jason Felice <jason.m.felice@gmail.com>
+
+0 string/w #!\ /usr/bin/clj Clojure script text executable
+!:mime text/x-clojure
+0 string/w #!\ /usr/local/bin/clj Clojure script text executable
+!:mime text/x-clojure
+0 string/w #!\ /usr/bin/clojure Clojure script text executable
+!:mime text/x-clojure
+0 string/w #!\ /usr/local/bin/clojure Clojure script text executable
+!:mime text/x-clojure
+0 string/W #!/usr/bin/env\ clj Clojure script text executable
+!:mime text/x-clojure
+0 string/W #!/usr/bin/env\ clojure Clojure script text executable
+!:mime text/x-clojure
+0 string/W #!\ /usr/bin/env\ clj Clojure script text executable
+!:mime text/x-clojure
+0 string/W #!\ /usr/bin/env\ clojure Clojure script text executable
+!:mime text/x-clojure
+
+0 regex \^\\\(ns[[:space:]]+[a-z] Clojure module source text
+!:mime text/x-clojure
+
+0 regex \^\\\(ns[[:space:]]+\\\^\\{: Clojure module source text
+!:mime text/x-clojure
+
+0 regex \^\\\(defn-?[[:space:]] Clojure module source text
+!:mime text/x-clojure
diff --git a/contrib/libs/libmagic/magic/Magdir/coff b/contrib/libs/libmagic/magic/Magdir/coff
new file mode 100644
index 0000000000..5123b7213c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/coff
@@ -0,0 +1,98 @@
+
+#------------------------------------------------------------------------------
+# $File: coff,v 1.7 2022/11/21 22:30:22 christos Exp $
+# coff: file(1) magic for Common Object Files not specific to known cpu types or manufactures
+#
+# COFF
+#
+# by Joerg Jenderek at Oct 2015, Feb 2021
+# https://en.wikipedia.org/wiki/COFF
+# https://de.wikipedia.org/wiki/Common_Object_File_Format
+# http://www.delorie.com/djgpp/doc/coff/filhdr.html
+
+# display name+variables+flags of Common Object Files Format (32bit)
+# Maybe used also in adi,att3b,clipper,hitachi-sh,hp,ibm6000,intel,
+# mips,motorola,msdos,osf1,sharc,varied.out,vax
+0 name display-coff
+# test for unused flag bits (0x8000,0x0800,0x0400,0x0200,x0080) in f_flags
+>18 uleshort&0x8E80 0
+# skip DOCTOR.DAILY READER.NDA REDBOX.ROOT by looking for positive number of sections
+>>2 uleshort >0
+# skip ega80woa.fnt svgafix.fnt HP3FNTS1.DAT HP3FNTS2.DAT INTRO.ACT LEARN.PIF by looking for low number of sections
+>>>2 uleshort <4207
+>>>>0 clear x
+# f_magic - magic number
+# DJGPP, 80386 COFF executable, MS Windows COFF Intel 80386 object file (./intel)
+>>>>0 uleshort 0x014C Intel 80386
+# Hitachi SH big-endian COFF (./hitachi-sh)
+>>>>0 uleshort 0x0500 Hitachi SH big-endian
+# Hitachi SH little-endian COFF (./hitachi-sh)
+>>>>0 uleshort 0x0550 Hitachi SH little-endian
+# executable (RISC System/6000 V3.1) or obj module (./ibm6000)
+#>>>>0 uleshort 0x01DF
+# MS Windows COFF Intel Itanium, AMD64
+# https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
+>>>>0 uleshort 0x0200 Intel ia64
+>>>>0 uleshort 0x8664 Intel amd64
+# ARM COFF (./arm)
+>>>>0 uleshort 0xaa64 Aarch64
+>>>>0 uleshort 0x01c0 ARM
+>>>>0 uleshort 0xa641 ARM64EC
+>>>>0 uleshort 0x01c2 ARM Thumb
+>>>>0 uleshort 0x01c4 ARMv7 Thumb
+# TODO for other COFFs
+#>>>>0 uleshort 0xABCD COFF_TEMPLATE
+>>>>0 default x
+>>>>>0 uleshort x type %#04x
+>>>>0 uleshort x COFF
+# F_EXEC flag bit
+>>>>18 leshort ^0x0002 object file
+!:mime application/x-coff
+!:ext o/obj/lib
+# no cof sample found
+#!:ext cof/o/obj/lib
+>>>>18 leshort &0x0002 executable
+#!:mime application/x-coffexec
+# F_RELFLG flag bit,static object
+>>>>18 leshort &0x0001 \b, no relocation info
+# F_LNNO flag bit
+>>>>18 leshort &0x0004 \b, no line number info
+# F_LSYMS flag bit
+>>>>18 leshort &0x0008 \b, stripped
+>>>>18 leshort ^0x0008 \b, not stripped
+# flags in other COFF versions
+#0x0010 F_FDPR_PROF
+#0x0020 F_FDPR_OPTI
+#0x0040 F_DSA
+# F_AR32WR flag bit
+#>>>>18 leshort &0x0100 \b, 32 bit little endian
+#0x1000 F_DYNLOAD
+#0x2000 F_SHROBJ
+#0x4000 F_LOADONLY
+# f_nscns - number of sections like: 1 2 3 4 5 7 8 9 11 12 15 16 19 20 21 22 26 30 36 40 42 56 80 89 96 124
+>>>>2 uleshort <2 \b, %u section
+>>>>2 uleshort >1 \b, %u sections
+# f_symptr - symbol table pointer, only for not stripped
+# like: 0 0x7c 0xf4 0x104 0x182 0x1c2 0x1c6 0x468 0x948 0x416e 0x149a6 0x1c9d8 0x23a68 0x35120 0x7afa0
+>>>>8 ulelong >0 \b, symbol offset=%#x
+# f_nsyms - number of symbols, only for not stripped
+# like: 0 2 7 9 10 11 20 35 41 63 71 80 105 146 153 158 170 208 294 572 831 1546
+>>>>12 ulelong >0 \b, %d symbols
+# f_opthdr - optional header size. An object file should have a value of 0
+>>>>16 uleshort >0 \b, optional header size %u
+# f_timdat - file time & date stamp only for little endian
+>>>>4 ledate >0 \b, created %s
+# at offset 20 can be optional header, extra bytes FILHSZ-20 because
+# do not rely on sizeof(FILHDR) to give the correct size for header.
+# or first section header
+# additional variables for other COFF files
+>>>>16 uleshort =0
+# first section name s_name[8] like: .text .data .debug$S .drectve .testseg
+>>>>>20 string x \b, 1st section name "%.8s"
+# >20 beshort 0407 (impure)
+# >20 beshort 0410 (pure)
+# >20 beshort 0413 (demand paged)
+# >20 beshort 0421 (standalone)
+# >22 leshort >0 - version %d
+# >168 string .lowmem Apple toolbox
+
diff --git a/contrib/libs/libmagic/magic/Magdir/commands b/contrib/libs/libmagic/magic/Magdir/commands
new file mode 100644
index 0000000000..6ad87fd757
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/commands
@@ -0,0 +1,201 @@
+
+#------------------------------------------------------------------------------
+# $File: commands,v 1.73 2022/11/06 18:39:23 christos Exp $
+# commands: file(1) magic for various shells and interpreters
+#
+#0 string/w : shell archive or script for antique kernel text
+0 string/fwt #!\ /bin/sh POSIX shell script text executable
+!:mime text/x-shellscript
+0 string/fwb #!\ /bin/sh POSIX shell script executable (binary data)
+!:mime text/x-shellscript
+>10 string #\040This\040script\040was\040generated\040using\040Makeself \b, self-executable archive
+>>53 string x \b, Makeself %s
+
+0 string/fwt #!\ /bin/csh C shell script text executable
+!:mime text/x-shellscript
+
+# korn shell magic, sent by George Wu, gwu@clyde.att.com
+0 string/fwt #!\ /bin/ksh Korn shell script text executable
+!:mime text/x-shellscript
+0 string/fwb #!\ /bin/ksh Korn shell script executable (binary data)
+!:mime text/x-shellscript
+
+0 string/fwt #!\ /bin/tcsh Tenex C shell script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/bin/tcsh Tenex C shell script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/local/tcsh Tenex C shell script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/local/bin/tcsh Tenex C shell script text executable
+!:mime text/x-shellscript
+
+#
+# zsh/ash/ae/nawk/gawk magic from cameron@cs.unsw.oz.au (Cameron Simpson)
+0 string/fwt #!\ /bin/zsh Paul Falstad's zsh script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/bin/zsh Paul Falstad's zsh script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/local/bin/zsh Paul Falstad's zsh script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/bin/env\ zsh Paul Falstad's zsh script text executable
+!:mime text/x-shellscript
+
+0 string/fwt #!\ /bin/ash Neil Brown's ash script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/bin/ash Neil Brown's ash script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/local/bin/ash Neil Brown's ash script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/local/bin/ae Neil Brown's ae script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /bin/nawk new awk script text executable
+!:mime text/x-nawk
+0 string/fwt #!\ /usr/bin/nawk new awk script text executable
+!:mime text/x-nawk
+0 string/fwt #!\ /usr/local/bin/nawk new awk script text executable
+!:mime text/x-nawk
+0 string/fwt #!\ /bin/gawk GNU awk script text executable
+!:mime text/x-gawk
+0 string/wt #!\ /usr/bin/gawk GNU awk script text executable
+!:mime text/x-gawk
+0 string/fwt #!\ /usr/local/bin/gawk GNU awk script text executable
+!:mime text/x-gawk
+#
+0 string/fwt #!\ /bin/awk awk script text executable
+!:mime text/x-awk
+0 string/fwt #!\ /usr/bin/awk awk script text executable
+!:mime text/x-awk
+0 regex/4096 =^[\040\t\f\r\n]{0,100}BEGIN[\040\t\f\r\n]{0,100}[{] awk or perl script text
+
+# AT&T Bell Labs' Plan 9 shell
+0 string/fwt #!\ /bin/rc Plan 9 rc shell script text executable
+
+# bash shell magic, from Peter Tobias (tobias@server.et-inf.fho-emden.de)
+0 string/fwt #!\ /bin/bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+0 string/fwb #!\ /bin/bash Bourne-Again shell script executable (binary data)
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/bin/bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+0 string/fwb #!\ /usr/bin/bash Bourne-Again shell script executable (binary data)
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/local/bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+0 string/fwb #!\ /usr/local/bash Bourne-Again shell script executable (binary data)
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/local/bin/bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+0 string/fwb #!\ /usr/local/bin/bash Bourne-Again shell script executable (binary data)
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/bin/env\ bash Bourne-Again shell script text executable
+!:mime text/x-shellscript
+
+# Fish shell magic
+# From: Benjamin Lowry <ben@ben.gmbh>
+0 string/fwt #!\ /usr/local/bin/fish fish shell script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/bin/fish fish shell script text executable
+!:mime text/x-shellscript
+0 string/fwt #!\ /usr/bin/env\ fish fish shell script text executable
+!:mime text/x-shellscript
+
+0 search/1/fwt #!\ /usr/bin/tclsh Tcl/Tk script text executable
+!:mime text/x-tcl
+
+0 search/1/fwt #!\ /usr/bin/texlua LuaTex script text executable
+!:mime text/x-luatex
+
+0 search/1/fwt #!\ /usr/bin/luatex LuaTex script text executable
+!:mime text/x-luatex
+
+0 search/1/fwt #!\ /usr/bin/stap Systemtap script text executable
+!:mime text/x-systemtap
+
+# From: Kylie McClain <kylie@somas.is>
+# Type: execline scripts
+# URL: https://skarnet.org/software/execline/
+0 string/fwt #!\ /command/execlineb execline script text executable
+!:mime text/x-execline
+0 string/fwt #!\ /bin/execlineb execline script text executable
+!:mime text/x-execline
+0 string/fwt #!\ /usr/bin/execlineb execline script text executable
+!:mime text/x-execline
+0 string/fwt #!\ /usr/bin/env\ execlineb execline script text executable
+!:mime text/x-execline
+
+0 string #!
+>0 regex \^#!.*/bin/execlineb([[:space:]].*)*$ execline script text executable
+!:mime text/x-execline
+
+# PHP scripts
+# Ulf Harnhammar <ulfh@update.uu.se>
+0 search/1/c =<?php PHP script text
+!:strength + 30
+!:mime text/x-php
+0 search/1 =<?\n PHP script text
+!:mime text/x-php
+0 search/1 =<?\r PHP script text
+!:mime text/x-php
+0 search/1/w #!\ /usr/local/bin/php PHP script text executable
+!:strength + 10
+!:mime text/x-php
+0 search/1/w #!\ /usr/bin/php PHP script text executable
+!:strength + 10
+!:mime text/x-php
+# Smarty compiled template, https://www.smarty.net/
+# Elan Ruusamae <glen@delfi.ee>
+0 string =<?php
+>5 regex [\ \n]
+>>6 string /*\ Smarty\ version Smarty compiled template
+>>>24 regex [0-9.]+ \b, version %s
+!:mime text/x-php
+
+0 string Zend\x00 PHP script Zend Optimizer data
+
+# From: Anatol Belski <ab@php.net>
+0 string OPCACHE
+>7 ubyte 0 PHP opcache filecache data
+
+0 search/64 --TEST--
+>16 search/64 --FILE--
+>24 search/8192 --EXPECT PHP core test
+!:ext phpt
+
+# https://www.php.net/manual/en/phar.fileformat.signature.php
+-4 string GBMB PHP phar archive
+>-8 ubyte 0x1 with MD5 signature
+!:ext phar
+>-8 ubyte 0x2 with SHA1 signature
+!:ext phar
+>-8 ubyte 0x3 with SHA256 signature
+!:ext phar
+>-8 ubyte 0x4 with SHA512 signature
+!:ext phar
+>-8 ubyte 0x10 with OpenSSL signature
+!:ext phar
+>-8 ubyte 0x11 with OpenSSL SHA256 signature
+!:ext phar
+>-8 ubyte 0x12 with OpenSSL SHA512 signature
+!:ext phar
+
+0 string/t $! DCL command file
+
+# Type: Pdmenu
+# URL: https://packages.debian.org/pdmenu
+# From: Edward Betts <edward@debian.org>
+0 string #!/usr/bin/pdmenu Pdmenu configuration file text
+
+# From Danny Weldon
+0 string \x0b\x13\x08\x00
+>0x04 uleshort <4 ksh byte-code version %d
+
+# From: arno <arenevier@fdn.fr>
+# mozilla xpconnect typelib
+# see https://www.mozilla.org/scriptable/typelib_file.html
+0 string XPCOM\nTypeLib\r\n\032 XPConnect Typelib
+>0x10 byte x version %d
+>>0x11 byte x \b.%d
+
+0 string/fwt #!\ /usr/bin/env\ runghc GHC script executable
+0 string/fwt #!\ /usr/bin/env\ runhaskell Haskell script executable
+0 string/fwt #!\ /usr/bin/env\ julia Julia script executable
diff --git a/contrib/libs/libmagic/magic/Magdir/communications b/contrib/libs/libmagic/magic/Magdir/communications
new file mode 100644
index 0000000000..8e1d908b67
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/communications
@@ -0,0 +1,22 @@
+
+#----------------------------------------------------------------------------
+# $File: communications,v 1.5 2009/09/19 16:28:08 christos Exp $
+# communication
+
+# TTCN is the Tree and Tabular Combined Notation described in ISO 9646-3.
+# It is used for conformance testing of communication protocols.
+# Added by W. Borgert <debacle@debian.org>.
+0 string $Suite TTCN Abstract Test Suite
+>&1 string $SuiteId
+>>&1 string >\n %s
+>&2 string $SuiteId
+>>&1 string >\n %s
+>&3 string $SuiteId
+>>&1 string >\n %s
+
+# MSC (message sequence charts) are a formal description technique,
+# described in ITU-T Z.120, mainly used for communication protocols.
+# Added by W. Borgert <debacle@debian.org>.
+0 string mscdocument Message Sequence Chart (document)
+0 string msc Message Sequence Chart (chart)
+0 string submsc Message Sequence Chart (subchart)
diff --git a/contrib/libs/libmagic/magic/Magdir/compress b/contrib/libs/libmagic/magic/Magdir/compress
new file mode 100644
index 0000000000..c3f93fa3be
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/compress
@@ -0,0 +1,461 @@
+#------------------------------------------------------------------------------
+# $File: compress,v 1.91 2023/06/16 19:37:47 christos Exp $
+# compress: file(1) magic for pure-compression formats (no archives)
+#
+# compress, gzip, pack, compact, huf, squeeze, crunch, freeze, yabba, etc.
+#
+# Formats for various forms of compressed data
+# Formats for "compress" proper have been moved into "compress.c",
+# because it tries to uncompress it to figure out what's inside.
+
+# standard unix compress
+0 string \037\235 compress'd data
+!:mime application/x-compress
+!:apple LZIVZIVU
+!:ext Z
+>2 byte&0x80 >0 block compressed
+>2 byte&0x1f x %d bits
+
+# gzip (GNU zip, not to be confused with Info-ZIP or PKWARE zip archiver)
+# URL: https://en.wikipedia.org/wiki/Gzip
+# Reference: https://tools.ietf.org/html/rfc1952
+# Update: Joerg Jenderek, Apr 2019, Dec 2022
+# Edited by Chris Chittleborough <cchittleborough@yahoo.com.au>, March 2002
+# * Original filename is only at offset 10 if "extra field" absent
+# * Produce shorter output - notably, only report compression methods
+# other than 8 ("deflate", the only method defined in RFC 1952).
+# Note: find defs -iname '*.trid.xml' -exec grep -q '<Bytes>1F8B08' {} \; -ls
+# TODO:
+# FBR Blueberry FlashBack screen Record https://www.flashbackrecorder.com/
+# KPR KOffice/Calligra KPresenter application/x-kpresenter
+# KPT KOffice/Calligra KPresenter template? application/x-kpresenter
+# SAV Diggles Saved Game File http://www.innonics.com
+# SAV FarCry (demo) saved game http://www.farcry-thegame.com
+# DAT ZOAGZIP game data format http://en.wikipedia.org/wiki/SD_Gundam_Capsule_Fighter
+0 string \037\213
+# to display gzip compressed (strength=100=2*50) before other (strength=50)?
+#!:strength * 2
+# no FNAME and FCOMMENT bit implies no file name/comment. That means only binary
+>3 byte&0x18 =0
+# For binary gzipped no ASCII text should occur
+# mcd-monu-cad.trid.xml
+>>10 string MCD Monu-Cad Drawing, Component or Font
+#>>36 string Created\ with\ MONU-CAD
+#!:mime application/octet-stream
+# http://fileformats.archiveteam.org/wiki/Monu-CAD
+# http://www.monucad.com/downloads/FullDemo-2005.EXE
+# /HANDS96.MCC Component
+# /DEMO_DD01.MCD Drawing
+# /MCALF020.FNT Font
+!:ext mcc/mcd/fnt
+# http://www.generalcadd.com
+>>10 string GXD General CADD, Drawing or Component
+#!:mime application/octet-stream
+# /gxc/BUILDINGEDGE.gxc Component
+# /gxd/HOCKETT-STPAUL-WRHSE.gxd Drawing
+# /gxd/POWERLAND-MILL-ADD-11.gxd Drawing v9.1.06
+!:ext gxc/gxd
+#>>>13 ubyte 0 \b, version 0
+>>>13 string 09 \b, version 9
+# other gzipped binary like gzipped tar, VirtualBox extension package,...
+>>10 default x gzip compressed data
+!:mime application/gzip
+>>>0 use gzip-info
+# size of the original (uncompressed) input data modulo 2^32
+# TODO: check for GXD MCD cad the reported size
+>>>-4 ulelong x \b, original size modulo 2^32 %u
+# gzipped TAR or VirtualBox extension package
+#!:mime application/x-compressed-tar
+#!:mime application/x-virtualbox-vbox-extpack
+# https://www.w3.org/TR/SVG/mimereg.html
+#!:mime image/svg+xml-compressed
+# zlib.3.gz
+# microcode-20180312.tgz
+# tpz same as tgz
+# lua-md5_1.2-1_i386_i486.ipk https://en.wikipedia.org/wiki/Opkg
+# Oracle_VM_VirtualBox_Extension_Pack-5.0.12-104815.vbox-extpack
+# trees.blend http://fileformats.archiveteam.org/wiki/BLEND
+# 2020-07-19-Note-16-24.xoj https://xournal.sourceforge.net/manual.html
+# MYgnucash-gz.gnucash https://wiki.gnucash.org/wiki/GnuCash_XML_format
+# text-rotate.dia https://en.wikipedia.org/wiki/Dia_(software)
+# MYrdata.RData https://en.wikipedia.org/wiki/R_(programming_language)
+!:ext gz/tgz/tpz/ipk/vbox-extpack/svgz/blend/dia/gnucash/rdata/xoj
+# FNAME/FCOMMENT bit implies file name/comment as iso-8859-1 text
+>3 byte&0x18 >0 gzip compressed data
+!:mime application/gzip
+# gzipped tar, gzipped Abiword document
+#!:mime application/x-compressed-tar
+#!:mime application/x-abiword-compressed
+#!:mime image/image/svg+xml-compressed
+# kleopatra_splashscreen.svgz gzipped .svg
+# RSI-Mega-Demo_Disk1.adz gzipped .adf http://fileformats.archiveteam.org/wiki/ADF_(Amiga)
+# PostbankTest.kmy gzipped XML https://docs.kde.org/stable5/en/kmymoney/kmymoney/details.formats.compressed.html
+# Logo.xcfgz gzipped .xcf http://fileformats.archiveteam.org/wiki/XCF
+!:ext gz/tgz/tpz/zabw/svgz/adz/kmy/xcfgz
+>>0 use gzip-info
+# size of the original (uncompressed) input data modulo 2^32
+>>-4 ulelong x \b, original size modulo 2^32 %u
+# display information of gzip compressed files
+0 name gzip-info
+#>2 byte x THIS iS GZIP
+>2 byte <8 \b, reserved method
+>2 byte >8 \b, unknown method
+>3 byte &0x01 \b, ASCII
+>3 byte &0x02 \b, has CRC
+>3 byte &0x04 \b, extra field
+>3 byte&0xC =0x08
+>>10 string x \b, was "%s"
+>3 byte &0x10 \b, has comment
+>3 byte &0x20 \b, encrypted
+>4 ledate >0 \b, last modified: %s
+>8 byte 2 \b, max compression
+>8 byte 4 \b, max speed
+>9 byte =0x00 \b, from FAT filesystem (MS-DOS, OS/2, NT)
+>9 byte =0x01 \b, from Amiga
+>9 byte =0x02 \b, from VMS
+>9 byte =0x03 \b, from Unix
+>9 byte =0x04 \b, from VM/CMS
+>9 byte =0x05 \b, from Atari
+>9 byte =0x06 \b, from HPFS filesystem (OS/2, NT)
+>9 byte =0x07 \b, from MacOS
+>9 byte =0x08 \b, from Z-System
+>9 byte =0x09 \b, from CP/M
+>9 byte =0x0A \b, from TOPS/20
+>9 byte =0x0B \b, from NTFS filesystem (NT)
+>9 byte =0x0C \b, from QDOS
+>9 byte =0x0D \b, from Acorn RISCOS
+# size of the original (uncompressed) input data modulo 2^32
+#>-4 ulelong x \b, original size modulo 2^32 %u
+#ERROR: line 114: non zero offset 1048572 at level 1
+
+# packed data, Huffman (minimum redundancy) codes on a byte-by-byte basis
+0 string \037\036 packed data
+!:mime application/octet-stream
+!:ext z
+>2 belong >1 \b, %d characters originally
+>2 belong =1 \b, %d character originally
+#
+# This magic number is byte-order-independent.
+0 short 0x1f1f old packed data
+!:mime application/octet-stream
+
+# XXX - why *two* entries for "compacted data", one of which is
+# byte-order independent, and one of which is byte-order dependent?
+#
+0 short 0x1fff compacted data
+!:mime application/octet-stream
+# This string is valid for SunOS (BE) and a matching "short" is listed
+# in the Ultrix (LE) magic file.
+0 string \377\037 compacted data
+!:mime application/octet-stream
+0 short 0145405 huf output
+!:mime application/octet-stream
+
+# bzip2
+0 string BZh bzip2 compressed data
+!:mime application/x-bzip2
+!:ext bz2
+>3 byte >47 \b, block size = %c00k
+
+# bzip a block-sorting file compressor
+# by Julian Seward <sewardj@cs.man.ac.uk> and others
+0 string BZ0 bzip compressed data
+!:mime application/x-bzip
+>3 byte >47 \b, block size = %c00k
+
+# lzip
+0 string LZIP lzip compressed data
+!:mime application/x-lzip
+!:ext lz
+>4 byte x \b, version: %d
+
+# squeeze and crunch
+# Michael Haardt <michael@cantor.informatik.rwth-aachen.de>
+0 beshort 0x76FF squeezed data,
+>4 string x original name %s
+0 beshort 0x76FE crunched data,
+>2 string x original name %s
+0 beshort 0x76FD LZH compressed data,
+>2 string x original name %s
+
+# Freeze
+0 string \037\237 frozen file 2.1
+0 string \037\236 frozen file 1.0 (or gzip 0.5)
+
+# SCO compress -H (LZH)
+0 string \037\240 SCO compress -H (LZH) data
+
+# European GSM 06.10 is a provisional standard for full-rate speech
+# transcoding, prI-ETS 300 036, which uses RPE/LTP (residual pulse
+# excitation/long term prediction) coding at 13 kbit/s.
+#
+# There's only a magic nibble (4 bits); that nibble repeats every 33
+# bytes. This isn't suited for use, but maybe we can use it someday.
+#
+# This will cause very short GSM files to be declared as data and
+# mismatches to be declared as data too!
+#0 byte&0xF0 0xd0 data
+#>33 byte&0xF0 0xd0
+#>66 byte&0xF0 0xd0
+#>99 byte&0xF0 0xd0
+#>132 byte&0xF0 0xd0 GSM 06.10 compressed audio
+
+# lzop from <markus.oberhumer@jk.uni-linz.ac.at>
+0 string \x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a lzop compressed data
+!:ext lzo
+>9 beshort <0x0940
+>>9 byte&0xf0 =0x00 - version 0.
+>>9 beshort&0x0fff x \b%03x,
+>>13 byte 1 LZO1X-1,
+>>13 byte 2 LZO1X-1(15),
+>>13 byte 3 LZO1X-999,
+## >>22 bedate >0 last modified: %s,
+>>14 byte =0x00 os: MS-DOS
+>>14 byte =0x01 os: Amiga
+>>14 byte =0x02 os: VMS
+>>14 byte =0x03 os: Unix
+>>14 byte =0x05 os: Atari
+>>14 byte =0x06 os: OS/2
+>>14 byte =0x07 os: MacOS
+>>14 byte =0x0A os: Tops/20
+>>14 byte =0x0B os: WinNT
+>>14 byte =0x0E os: Win32
+>9 beshort >0x0939
+>>9 byte&0xf0 =0x00 - version 0.
+>>9 byte&0xf0 =0x10 - version 1.
+>>9 byte&0xf0 =0x20 - version 2.
+>>9 beshort&0x0fff x \b%03x,
+>>15 byte 1 LZO1X-1,
+>>15 byte 2 LZO1X-1(15),
+>>15 byte 3 LZO1X-999,
+## >>25 bedate >0 last modified: %s,
+>>17 byte =0x00 os: MS-DOS
+>>17 byte =0x01 os: Amiga
+>>17 byte =0x02 os: VMS
+>>17 byte =0x03 os: Unix
+>>17 byte =0x05 os: Atari
+>>17 byte =0x06 os: OS/2
+>>17 byte =0x07 os: MacOS
+>>17 byte =0x0A os: Tops/20
+>>17 byte =0x0B os: WinNT
+>>17 byte =0x0E os: Win32
+
+# 4.3BSD-Quasijarus Strong Compression
+# https://minnie.tuhs.org/Quasijarus/compress.html
+0 string \037\241 Quasijarus strong compressed data
+
+# From: Cory Dikkers <cdikkers@swbell.net>
+0 string XPKF Amiga xpkf.library compressed data
+0 string PP11 Power Packer 1.1 compressed data
+0 string PP20 Power Packer 2.0 compressed data,
+>4 belong 0x09090909 fast compression
+>4 belong 0x090A0A0A mediocre compression
+>4 belong 0x090A0B0B good compression
+>4 belong 0x090A0C0C very good compression
+>4 belong 0x090A0C0D best compression
+
+# 7-zip archiver, from Thomas Klausner (wiz@danbala.tuwien.ac.at)
+# https://www.7-zip.org or DOC/7zFormat.txt
+#
+0 string 7z\274\257\047\034 7-zip archive data,
+>6 byte x version %d
+>7 byte x \b.%d
+!:mime application/x-7z-compressed
+!:ext 7z/cb7
+
+0 name lzma LZMA compressed data,
+!:mime application/x-lzma
+!:ext lzma
+>5 lequad =0xffffffffffffffff streamed
+>5 lequad !0xffffffffffffffff non-streamed, size %lld
+
+# Type: LZMA
+0 lelong&0xffffff =0x5d
+>12 leshort 0xff
+>>0 use lzma
+>12 leshort 0
+>>0 use lzma
+
+# http://tukaani.org/xz/xz-file-format.txt
+0 ustring \xFD7zXZ\x00 XZ compressed data, checksum
+!:strength * 2
+!:mime application/x-xz
+!:ext xz
+>7 byte&0xf 0x0 NONE
+>7 byte&0xf 0x1 CRC32
+>7 byte&0xf 0x4 CRC64
+>7 byte&0xf 0xa SHA-256
+
+# https://github.com/ckolivas/lrzip/blob/master/doc/magic.header.txt
+0 string LRZI LRZIP compressed data
+!:mime application/x-lrzip
+>4 byte x - version %d
+>5 byte x \b.%d
+>22 byte 1 \b, encrypted
+
+# https://fastcompression.blogspot.fi/2013/04/lz4-streaming-format-final.html
+0 lelong 0x184d2204 LZ4 compressed data (v1.4+)
+!:mime application/x-lz4
+!:ext lz4
+# Added by osm0sis@xda-developers.com
+0 lelong 0x184c2103 LZ4 compressed data (v1.0-v1.3)
+!:mime application/x-lz4
+0 lelong 0x184c2102 LZ4 compressed data (v0.1-v0.9)
+!:mime application/x-lz4
+
+# Zstandard/LZ4 skippable frames
+# https://github.com/facebook/zstd/blob/dev/zstd_compression_format.md
+0 lelong&0xFFFFFFF0 0x184D2A50
+>(4.l+8) indirect x
+
+# Zstandard Dictionary ID subroutine
+0 name zstd-dictionary-id
+# Single Segment = True
+>0 byte &0x20 \b, Dictionary ID:
+>>0 byte&0x03 0 None
+>>0 byte&0x03 1
+>>>1 byte x %u
+>>0 byte&0x03 2
+>>>1 leshort x %u
+>>0 byte&0x03 3
+>>>1 lelong x %u
+# Single Segment = False
+>0 byte ^0x20 \b, Dictionary ID:
+>>0 byte&0x03 0 None
+>>0 byte&0x03 1
+>>>2 byte x %u
+>>0 byte&0x03 2
+>>>2 leshort x %u
+>>0 byte&0x03 3
+>>>2 lelong x %u
+
+# Zstandard compressed data
+# https://github.com/facebook/zstd/blob/dev/zstd_compression_format.md
+0 lelong 0xFD2FB522 Zstandard compressed data (v0.2)
+!:mime application/zstd
+!:ext zst
+0 lelong 0xFD2FB523 Zstandard compressed data (v0.3)
+!:mime application/zstd
+!:ext zst
+0 lelong 0xFD2FB524 Zstandard compressed data (v0.4)
+!:mime application/zstd
+!:ext zst
+0 lelong 0xFD2FB525 Zstandard compressed data (v0.5)
+!:mime application/zstd
+!:ext zst
+0 lelong 0xFD2FB526 Zstandard compressed data (v0.6)
+!:mime application/zstd
+!:ext zst
+0 lelong 0xFD2FB527 Zstandard compressed data (v0.7)
+!:mime application/zstd
+!:ext zst
+>4 use zstd-dictionary-id
+0 lelong 0xFD2FB528 Zstandard compressed data (v0.8+)
+!:mime application/zstd
+!:ext zst
+>4 use zstd-dictionary-id
+
+# https://github.com/facebook/zstd/blob/dev/zstd_compression_format.md
+0 lelong 0xEC30A437 Zstandard dictionary
+!:mime application/x-std-dictionary
+>4 lelong x (ID %u)
+
+# AFX compressed files (Wolfram Kleff)
+2 string -afx- AFX compressed file data
+
+# Supplementary magic data for the file(1) command to support
+# rzip(1). The format is described in magic(5).
+#
+# Copyright (C) 2003 by Andrew Tridgell. You may do whatever you want with
+# this file.
+#
+0 string RZIP rzip compressed data
+>4 byte x - version %d
+>5 byte x \b.%d
+>6 belong x (%d bytes)
+
+0 string ArC\x01 FreeArc archive <http://freearc.org>
+
+# Type: DACT compressed files
+0 long 0x444354C3 DACT compressed data
+>4 byte >-1 (version %i.
+>5 byte >-1 %i.
+>6 byte >-1 %i)
+>7 long >0 , original size: %i bytes
+>15 long >30 , block size: %i bytes
+
+# Valve Pack (VPK) files
+0 lelong 0x55aa1234 Valve Pak file
+>0x4 lelong x \b, version %u
+>0x8 lelong x \b, %u entries
+
+# Snappy framing format
+# https://code.google.com/p/snappy/source/browse/trunk/framing_format.txt
+0 string \377\006\0\0sNaPpY snappy framed data
+!:mime application/x-snappy-framed
+
+# qpress, https://www.quicklz.com/
+0 string qpress10 qpress compressed data
+!:mime application/x-qpress
+
+# Zlib https://www.ietf.org/rfc/rfc6713.txt
+0 string/b x
+>0 beshort%31 =0
+>>0 byte&0xf =8
+>>>0 byte&0x80 =0 zlib compressed data
+!:mime application/zlib
+
+# BWC compression
+0 string BWC
+>3 byte 0 BWC compressed data
+
+# UCL compression
+0 bequad 0x00e955434cff011a UCL compressed data
+
+# Softlib archive
+0 string SLIB Softlib archive
+>4 leshort x \b, version %d
+>6 leshort x (contains %d files)
+
+# URL: https://github.com/lzfse/lzfse/blob/master/src/lzfse_internal.h#L276
+# From: Eric Hall <eric.hall@darkart.com>
+0 string bvx- lzfse encoded, no compression
+0 string bvx1 lzfse compressed, uncompressed tables
+0 string bvx2 lzfse compressed, compressed tables
+0 string bvxn lzfse encoded, lzvn compressed
+
+# pcxLib.exe compression program
+# http://www.shikadi.net/moddingwiki/PCX_Library
+0 string/b pcxLib
+>0x0A string/b Copyright\020(c)\020Genus\020Microprogramming,\020Inc. pcxLib compressed
+
+# https://support-docs.illumina.com/SW/ORA_Format_Specification/Content/SW/ORA/ORAFormatSpecification.htm
+0 uleshort 0x7c49
+>2 lelong 0x80 ORA FASTQ compressed file
+>>6 ulelong x \b, DNA size %u
+>>10 ulelong x \b, read names size %u
+>>14 ulelong x \b, quality buffer 1 size %u
+>>18 ulelong x \b, quality buffer 2 size %u
+>>22 ulelong x \b, sequence buffer size %u
+>>26 ulelong x \b, N-position buffer size %u
+>>30 ulelong x \b, crypto buffer size %u
+>>34 ulelong x \b, misc buffer 1 size %u
+>>38 ulelong x \b, misc buffer 2 size %u
+>>42 ulelong x \b, flags %#x
+>>46 lelong x \b, read size %d
+>>50 lelong x \b, number of reads %d
+>>54 leshort x \b, version %d
+
+# https://github.com/kspalaiologos/bzip3/blob/master/doc/file_format.md
+0 string/b BZ3v1 bzip3 compressed data
+>5 ulelong x \b, blocksize %u
+
+
+# https://support-docs.illumina.com/SW/ORA_Format_Specification/Content/\
+# SW/ORA/ORAFormatSpecification.htm
+# From Guillaume Rizk
+0 short =0x7C49 DRAGEN ORA file,
+>-261 short =0x7C49 with metadata:
+>-125 u8 x NB reads: %llu,
+>-109 u8 x NB bases: %llu.
+>-219 u4&0x02 2 File contains interleaved paired reads
diff --git a/contrib/libs/libmagic/magic/Magdir/console b/contrib/libs/libmagic/magic/Magdir/console
new file mode 100644
index 0000000000..0ed53fe34d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/console
@@ -0,0 +1,1226 @@
+
+#------------------------------------------------------------------------------
+# $File: console,v 1.72 2023/06/16 19:24:06 christos Exp $
+# Console game magic
+# Toby Deshane <hac@shoelace.digivill.net>
+
+# ines: file(1) magic for Marat's iNES Nintendo Entertainment System ROM dump format
+# Updated by David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://wiki.nesdev.com/w/index.php/INES
+# - https://wiki.nesdev.com/w/index.php/NES_2.0
+
+# Common header for iNES, NES 2.0, and Wii U iNES.
+0 name nes-rom-image-ines
+>7 byte&0x0C =0x8 (NES 2.0)
+>4 byte x \b: %ux16k PRG
+>5 byte x \b, %ux8k CHR
+>6 byte&0x08 =0x8 [4-Scr]
+>6 byte&0x09 =0x0 [H-mirror]
+>6 byte&0x09 =0x1 [V-mirror]
+>6 byte&0x02 =0x2 [SRAM]
+>6 byte&0x04 =0x4 [Trainer]
+>7 byte&0x03 =0x2 [PC10]
+>7 byte&0x03 =0x1 [VS]
+>>7 byte&0x0C =0x8
+# NES 2.0: VS PPU
+>>>13 byte&0x0F =0x0 \b, RP2C03B
+>>>13 byte&0x0F =0x1 \b, RP2C03G
+>>>13 byte&0x0F =0x2 \b, RP2C04-0001
+>>>13 byte&0x0F =0x3 \b, RP2C04-0002
+>>>13 byte&0x0F =0x4 \b, RP2C04-0003
+>>>13 byte&0x0F =0x5 \b, RP2C04-0004
+>>>13 byte&0x0F =0x6 \b, RP2C03B
+>>>13 byte&0x0F =0x7 \b, RP2C03C
+>>>13 byte&0x0F =0x8 \b, RP2C05-01
+>>>13 byte&0x0F =0x9 \b, RP2C05-02
+>>>13 byte&0x0F =0xA \b, RP2C05-03
+>>>13 byte&0x0F =0xB \b, RP2C05-04
+>>>13 byte&0x0F =0xC \b, RP2C05-05
+# TODO: VS protection hardware?
+>>7 byte x \b]
+# NES 2.0-specific flags.
+>7 byte&0x0C =0x8
+>>12 byte&0x03 =0x0 [NTSC]
+>>12 byte&0x03 =0x1 [PAL]
+>>12 byte&0x02 =0x2 [NTSC+PAL]
+
+# Standard iNES ROM header.
+0 string NES\x1A NES ROM image (iNES)
+!:mime application/x-nes-rom
+>0 use nes-rom-image-ines
+
+# Wii U Virtual Console iNES ROM header.
+0 belong 0x4E455300 NES ROM image (Wii U Virtual Console)
+!:mime application/x-nes-rom
+>0 use nes-rom-image-ines
+
+#------------------------------------------------------------------------------
+# unif: file(1) magic for UNIF-format Nintendo Entertainment System ROM images
+# Reference: https://wiki.nesdev.com/w/index.php/UNIF
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+#
+# NOTE: The UNIF format uses chunks instead of a fixed header,
+# so most of the data isn't easily parseable.
+#
+0 string UNIF
+>4 lelong <16 NES ROM image (UNIF v%d format)
+!:mime application/x-nes-rom
+
+#------------------------------------------------------------------------------
+# fds: file(1) magic for Famicom Disk System disk images
+# Reference: https://wiki.nesdev.com/w/index.php/Family_Computer_Disk_System#.FDS_format
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# TODO: Check "Disk info block" and get info from that in addition to the optional header.
+
+# Disk info block. (block 1)
+0 name nintendo-fds-disk-info-block
+>23 byte !1 FMC-
+>23 byte 1 FSC-
+>16 string x \b%.3s
+>15 ubyte x \b, mfr %02X
+>20 ubyte x (Rev.%02u)
+
+# Headered version.
+0 string FDS\x1A
+>0x11 string *NINTENDO-HVC* Famicom Disk System disk image:
+!:mime application/x-fds-disk
+>>0x10 use nintendo-fds-disk-info-block
+>4 byte 1 (%u side)
+>4 byte !1 (%u sides)
+
+# Unheadered version.
+1 string *NINTENDO-HVC* Famicom Disk System disk image:
+!:mime application/x-fds-disk
+>0 use nintendo-fds-disk-info-block
+
+#------------------------------------------------------------------------------
+# tnes: file(1) magic for TNES-format Nintendo Entertainment System ROM images
+# Used by Nintendo 3DS NES Virtual Console games.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+#
+0 string TNES NES ROM image (Nintendo 3DS Virtual Console)
+!:mime application/x-nes-rom
+>4 byte 100 \b: FDS,
+>>0x2010 use nintendo-fds-disk-info-block
+>4 byte !100 \b: TNES mapper %u
+>>5 byte x \b, %ux8k PRG
+>>6 byte x \b, %ux8k CHR
+>>7 byte&0x08 =1 [WRAM]
+>>8 byte&0x09 =1 [H-mirror]
+>>8 byte&0x09 =2 [V-mirror]
+>>8 byte&0x02 =3 [VRAM]
+
+#------------------------------------------------------------------------------
+# gameboy: file(1) magic for the Nintendo (Color) Gameboy raw ROM format
+# Reference: http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header
+#
+0x104 bequad 0xCEED6666CC0D000B Game Boy ROM image
+# TODO: application/x-gameboy-color-rom for GBC.
+!:mime application/x-gameboy-rom
+>0x143 byte&0x80 0x80
+>>0x134 string >\0 \b: "%.15s"
+>0x143 byte&0x80 !0x80
+>>0x134 string >\0 \b: "%.16s"
+>0x14c byte x (Rev.%02u)
+
+# Machine type. (SGB, CGB, SGB+CGB)
+# Old licensee code 0x33 is required for SGB, but not CGB.
+>0x14b byte 0x33
+>>0x146 byte 0x03
+>>>0x143 byte&0x80 0x80 [SGB+CGB]
+>>>0x143 byte&0x80 !0x80 [SGB]
+>>0x146 byte !0x03
+>>>0x143 byte&0xC0 0x80 [CGB]
+>>>0x143 byte&0xC0 0xC0 [CGB ONLY]
+>0x14b byte !0x33
+>>0x143 byte&0xC0 0x80 [CGB]
+>>0x143 byte&0xC0 0xC0 [CGB ONLY]
+
+# Mapper.
+>0x147 byte 0x00 [ROM ONLY]
+>0x147 byte 0x01 [MBC1]
+>0x147 byte 0x02 [MBC1+RAM]
+>0x147 byte 0x03 [MBC1+RAM+BATT]
+>0x147 byte 0x05 [MBC2]
+>0x147 byte 0x06 [MBC2+BATTERY]
+>0x147 byte 0x08 [ROM+RAM]
+>0x147 byte 0x09 [ROM+RAM+BATTERY]
+>0x147 byte 0x0B [MMM01]
+>0x147 byte 0x0C [MMM01+SRAM]
+>0x147 byte 0x0D [MMM01+SRAM+BATT]
+>0x147 byte 0x0F [MBC3+TIMER+BATT]
+>0x147 byte 0x10 [MBC3+TIMER+RAM+BATT]
+>0x147 byte 0x11 [MBC3]
+>0x147 byte 0x12 [MBC3+RAM]
+>0x147 byte 0x13 [MBC3+RAM+BATT]
+>0x147 byte 0x19 [MBC5]
+>0x147 byte 0x1A [MBC5+RAM]
+>0x147 byte 0x1B [MBC5+RAM+BATT]
+>0x147 byte 0x1C [MBC5+RUMBLE]
+>0x147 byte 0x1D [MBC5+RUMBLE+SRAM]
+>0x147 byte 0x1E [MBC5+RUMBLE+SRAM+BATT]
+>0x147 byte 0xFC [Pocket Camera]
+>0x147 byte 0xFD [Bandai TAMA5]
+>0x147 byte 0xFE [Hudson HuC-3]
+>0x147 byte 0xFF [Hudson HuC-1]
+
+# ROM size.
+>0x148 byte 0 \b, ROM: 256Kbit
+>0x148 byte 1 \b, ROM: 512Kbit
+>0x148 byte 2 \b, ROM: 1Mbit
+>0x148 byte 3 \b, ROM: 2Mbit
+>0x148 byte 4 \b, ROM: 4Mbit
+>0x148 byte 5 \b, ROM: 8Mbit
+>0x148 byte 6 \b, ROM: 16Mbit
+>0x148 byte 7 \b, ROM: 32Mbit
+>0x148 byte 0x52 \b, ROM: 9Mbit
+>0x148 byte 0x53 \b, ROM: 10Mbit
+>0x148 byte 0x54 \b, ROM: 12Mbit
+
+# RAM size.
+>0x149 byte 1 \b, RAM: 16Kbit
+>0x149 byte 2 \b, RAM: 64Kbit
+>0x149 byte 3 \b, RAM: 256Kbit
+>0x149 byte 4 \b, RAM: 1Mbit
+>0x149 byte 5 \b, RAM: 512Kbit
+
+#------------------------------------------------------------------------------
+# genesis: file(1) magic for various Sega Mega Drive / Genesis ROM image and disc formats
+# Updated by David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://www.retrodev.com/segacd.html
+# - http://devster.monkeeh.com/sega/32xguide1.txt
+#
+
+# Common Sega Mega Drive header format.
+# FIXME: Name fields are 48 bytes, but have spaces for padding instead of 00s.
+0 name sega-mega-drive-header
+# ROM title. (Use domestic if present; if not, use international.)
+>0x120 byte >0x20
+>>0x120 string >\0 \b: "%.16s"
+>0x120 byte <0x21
+>>0x150 string >\0 \b: "%.16s"
+# Other information.
+>0x180 string >\0 (%.14s
+>>0x110 string >\0 \b, %.16s
+>0x180 byte 0
+>>0x110 string >\0 (%.16s
+>0 byte x \b)
+
+# TODO: Check for 32X CD?
+# Sega Mega CD disc images: 2048-byte sectors.
+0 string SEGADISCSYSTEM\ \ Sega Mega CD disc image
+!:mime application/x-sega-cd-rom
+>0 use sega-mega-drive-header
+>0 byte x \b, 2048-byte sectors
+0 string SEGABOOTDISC\ \ \ \ Sega Mega CD disc image
+!:mime application/x-sega-cd-rom
+>0 use sega-mega-drive-header
+>0 byte x \b, 2048-byte sectors
+# Sega Mega CD disc images: 2352-byte sectors.
+0x10 string SEGADISCSYSTEM\ \ Sega Mega CD disc image
+!:mime application/x-sega-cd-rom
+>0x10 use sega-mega-drive-header
+>0 byte x \b, 2352-byte sectors
+0x10 string SEGABOOTDISC\ \ \ \ Sega Mega CD disc image
+!:mime application/x-sega-cd-rom
+>0x10 use sega-mega-drive-header
+>0 byte x \b, 2352-byte sectors
+
+# Sega Mega Drive: Identify the system ID.
+0x100 string SEGA
+>0x3C0 string MARS\ CHECK\ MODE Sega 32X ROM image
+!:mime application/x-genesis-32x-rom
+>>0 use sega-mega-drive-header
+>0x104 string \ PICO Sega Pico ROM image
+!:mime application/x-sega-pico-rom
+>>0 use sega-mega-drive-header
+>0x104 string TOYS\ PICO Sega Pico ROM image
+!:mime application/x-sega-pico-rom
+>>0 use sega-mega-drive-header
+>0x104 string \ TOYS\ PICO Sega Pico ROM image
+!:mime application/x-sega-pico-rom
+>>0 use sega-mega-drive-header
+>0x104 string \ IAC Sega Pico ROM image
+!:mime application/x-sega-pico-rom
+>>0 use sega-mega-drive-header
+>0x104 string \ TERA68K Sega Teradrive (68K) ROM image
+!:mime application/x-sega-teradrive-rom
+>>0 use sega-mega-drive-header
+>0x104 string \ TERA286 Sega Teradrive (286) ROM image
+!:mime application/x-sega-teradrive-rom
+>>0 use sega-mega-drive-header
+>0x180 string BR Sega Mega CD Boot ROM image
+!:mime application/x-genesis-rom
+>>0 use sega-mega-drive-header
+>0x104 default x Sega Mega Drive / Genesis ROM image
+!:mime application/x-genesis-rom
+>>0 use sega-mega-drive-header
+
+# Sega Mega Drive: Some ROMs have "SEGA" at 0x101, not 0x100.
+0x100 string \ SEGA Sega Mega Drive / Genesis ROM image
+>0 use sega-mega-drive-header
+
+# Sega Pico ROMs that don't start with "SEGA".
+0x100 string SAMSUNG\ PICO Samsung Pico ROM image
+!:mime application/x-sega-pico-rom
+>0 use sega-mega-drive-header
+0x100 string IMA\ IKUNOUJYUKU Samsung Pico ROM image
+!:mime application/x-sega-pico-rom
+>0 use sega-mega-drive-header
+0x100 string IMA IKUNOJYUKU Samsung Pico ROM image
+!:mime application/x-sega-pico-rom
+>0 use sega-mega-drive-header
+
+# Sega Picture Magic (modified 32X)
+0x100 string Picture\ Magic
+>0x3C0 string PICTURE MAGIC-01 Sega 32X ROM image
+!:mime application/x-genesis-32x-rom
+>>0 use sega-mega-drive-header
+
+#------------------------------------------------------------------------------
+# genesis: file(1) magic for the Super MegaDrive ROM dump format
+#
+
+# NOTE: Due to interleaving, we can't display anything
+# other than the copier header information.
+0 name sega-genesis-smd-header
+>0 byte x %dx16k blocks
+>2 byte 0 \b, last in series or standalone
+>2 byte >0 \b, split ROM
+
+# "Sega Genesis" header.
+0x280 string EAGN
+>8 beshort 0xAABB Sega Mega Drive / Genesis ROM image (SMD format):
+!:mime application/x-genesis-rom
+>>0 use sega-genesis-smd-header
+
+# "Sega Mega Drive" header.
+0x280 string EAMG
+>8 beshort 0xAABB Sega Mega Drive / Genesis ROM image (SMD format):
+!:mime application/x-genesis-rom
+>>0 use sega-genesis-smd-header
+
+#------------------------------------------------------------------------------
+# smsgg: file(1) magic for Sega Master System and Game Gear ROM images
+# Detects all Game Gear and export Sega Master System ROM images,
+# and some Japanese Sega Master System ROM images.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://www.smspower.org/Development/ROMHeader
+#
+
+# General SMS header rule.
+# The SMS boot ROM checks the header at three locations.
+0 name sega-master-system-rom-header
+# Machine type.
+>0x0F byte&0xF0 0x30 Sega Master System
+!:mime application/x-sms-rom
+>0x0F byte&0xF0 0x40 Sega Master System
+!:mime application/x-sms-rom
+>0x0F byte&0xF0 0x50 Sega Game Gear
+!:mime application/x-gamegear-rom
+>0x0F byte&0xF0 0x60 Sega Game Gear
+!:mime application/x-gamegear-rom
+>0x0F byte&0xF0 0x70 Sega Game Gear
+!:mime application/x-gamegear-rom
+>0x0F default x Sega Master System / Game Gear
+!:mime application/x-sms-rom
+>0 byte x ROM image:
+# Product code.
+>0x0E byte&0xF0 0x10 1
+>0x0E byte&0xF0 0x20 2
+>0x0E byte&0xF0 0x30 3
+>0x0E byte&0xF0 0x40 4
+>0x0E byte&0xF0 0x50 5
+>0x0E byte&0xF0 0x60 6
+>0x0E byte&0xF0 0x70 7
+>0x0E byte&0xF0 0x80 8
+>0x0E byte&0xF0 0x90 9
+>0x0E byte&0xF0 0xA0 10
+>0x0E byte&0xF0 0xB0 11
+>0x0E byte&0xF0 0xC0 12
+>0x0E byte&0xF0 0xD0 13
+>0x0E byte&0xF0 0xE0 14
+>0x0E byte&0xF0 0xF0 15
+# If the product code is 5 digits, we'll need to backspace here.
+>0x0E byte&0xF0 !0
+>>0x0C leshort x \b%04x
+>0x0E byte&0xF0 0
+>>0x0C leshort x %04x
+# Revision.
+>0x0E byte&0x0F x (Rev.%02d)
+# ROM size. (Used for the boot ROM checksum routine.)
+>0x0F byte&0x0F 0x0A (8 KB)
+>0x0F byte&0x0F 0x0B (16 KB)
+>0x0F byte&0x0F 0x0C (32 KB)
+>0x0F byte&0x0F 0x0D (48 KB)
+>0x0F byte&0x0F 0x0E (64 KB)
+>0x0F byte&0x0F 0x0F (128 KB)
+>0x0F byte&0x0F 0x00 (256 KB)
+>0x0F byte&0x0F 0x01 (512 KB)
+>0x0F byte&0x0F 0x02 (1 MB)
+
+# SMS/GG header locations.
+0x7FF0 string TMR\ SEGA
+>0x7FF0 use sega-master-system-rom-header
+0x3FF0 string TMR\ SEGA
+>0x3FF0 use sega-master-system-rom-header
+0x1FF0 string TMR\ SEGA
+>0x1FF0 use sega-master-system-rom-header
+
+#------------------------------------------------------------------------------
+# saturn: file(1) magic for the Sega Saturn disc image format.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+#
+
+# Common Sega Saturn disc header format.
+# NOTE: Title is 112 bytes, but we're only showing 32 due to space padding.
+# TODO: Release date, device information, region code, others?
+0 name sega-saturn-disc-header
+>0x60 string >\0 \b: "%.32s"
+>0x20 string >\0 (%.10s
+>>0x2A string >\0 \b, %.6s)
+>>0x2A byte 0 \b)
+
+# 2048-byte sector version.
+0 string SEGA\ SEGASATURN\ Sega Saturn disc image
+!:mime application/x-saturn-rom
+>0 use sega-saturn-disc-header
+>0 byte x (2048-byte sectors)
+# 2352-byte sector version.
+0x10 string SEGA\ SEGASATURN\ Sega Saturn disc image
+!:mime application/x-saturn-rom
+>0x10 use sega-saturn-disc-header
+>0 byte x (2352-byte sectors)
+
+#------------------------------------------------------------------------------
+# dreamcast: file(1) magic for the Sega Dreamcast disc image format.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://mc.pp.se/dc/ip0000.bin.html
+#
+
+# Common Sega Dreamcast disc header format.
+# NOTE: Title is 128 bytes, but we're only showing 32 due to space padding.
+# TODO: Release date, device information, region code, others?
+0 name sega-dreamcast-disc-header
+>0x80 string >\0 \b: "%.32s"
+>0x40 string >\0 (%.10s
+>>0x4A string >\0 \b, %.6s)
+>>0x4A byte 0 \b)
+
+# 2048-byte sector version.
+0 string SEGA\ SEGAKATANA\ Sega Dreamcast disc image
+!:mime application/x-dc-rom
+>0 use sega-dreamcast-disc-header
+>0 byte x (2048-byte sectors)
+# 2352-byte sector version.
+0x10 string SEGA\ SEGAKATANA\ Sega Dreamcast disc image
+!:mime application/x-dc-rom
+>0x10 use sega-dreamcast-disc-header
+>0 byte x (2352-byte sectors)
+
+#------------------------------------------------------------------------------
+# dreamcast: file(1) uncertain magic for the Sega Dreamcast VMU image format
+#
+0 belong 0x21068028 Sega Dreamcast VMU game image
+0 string LCDi Dream Animator file
+
+#------------------------------------------------------------------------------
+# z64: file(1) magic for the Z64 format N64 ROM dumps
+# Reference: http://forum.pj64-emu.com/showthread.php?t=2239
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+#
+0 bequad 0x803712400000000F Nintendo 64 ROM image
+!:mime application/x-n64-rom
+>0x20 string >\0 \b: "%.20s"
+>0x3B string x (%.4s
+>0x3F byte x \b, Rev.%02u)
+
+#------------------------------------------------------------------------------
+# v64: file(1) magic for the V64 format N64 ROM dumps
+# Same as z64 format, but with 16-bit byteswapping.
+#
+0 bequad 0x3780401200000F00 Nintendo 64 ROM image (V64)
+!:mime application/x-n64-rom
+
+#------------------------------------------------------------------------------
+# n64-swap2: file(1) magic for the swap2 format N64 ROM dumps
+# Same as z64 format, but with swapped 16-bit words.
+#
+0 bequad 0x12408037000F0000 Nintendo 64 ROM image (wordswapped)
+!:mime application/x-n64-rom
+
+#------------------------------------------------------------------------------
+# n64-le32: file(1) magic for the 32-bit byteswapped format N64 ROM dumps
+# Same as z64 format, but with 32-bit byteswapping.
+#
+0 bequad 0x401237800F000000 Nintendo 64 ROM image (32-bit byteswapped)
+!:mime application/x-n64-rom
+
+#------------------------------------------------------------------------------
+# gba: file(1) magic for the Nintendo Game Boy Advance raw ROM format
+# Reference: https://problemkaputt.de/gbatek.htm#gbacartridgeheader
+#
+# Original version from: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# Updated version from: David Korth <gerbilsoft@gerbilsoft.com>
+#
+4 bequad 0x24FFAE51699AA221 Game Boy Advance ROM image
+!:mime application/x-gba-rom
+>0xA0 string >\0 \b: "%.12s"
+>0xAC string x (%.6s
+>0xBC byte x \b, Rev.%02u)
+
+#------------------------------------------------------------------------------
+# nds: file(1) magic for the Nintendo DS(i) raw ROM format
+# Reference: https://problemkaputt.de/gbatek.htm#dscartridgeheader
+#
+# Original version from: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# Updated version from: David Korth <gerbilsoft@gerbilsoft.com>
+#
+0xC0 bequad 0x24FFAE51699AA221 Nintendo DS ROM image
+!:mime application/x-nintendo-ds-rom
+>0x00 string >\0 \b: "%.12s"
+>0x0C string x (%.6s
+>0x1E byte x \b, Rev.%02u)
+>0x12 byte 2 (DSi enhanced)
+>0x12 byte 3 (DSi only)
+# Secure Area check.
+>0x20 lelong <0x4000 (homebrew)
+>0x20 lelong >0x3FFF
+>>0x4000 lequad 0x0000000000000000 (multiboot)
+>>0x4000 lequad !0x0000000000000000
+>>>0x4000 lequad 0xE7FFDEFFE7FFDEFF (decrypted)
+>>>0x4000 lequad !0xE7FFDEFFE7FFDEFF
+>>>>0x1000 lequad 0x0000000000000000 (encrypted)
+>>>>0x1000 lequad !0x0000000000000000 (mask ROM)
+
+#------------------------------------------------------------------------------
+# nds_passme: file(1) magic for Nintendo DS ROM images for GBA cartridge boot.
+# This is also used for loading .nds files using the MSET exploit on 3DS.
+# Reference: https://github.com/devkitPro/ndstool/blob/master/source/ndscreate.cpp
+0xC0 bequad 0xC8604FE201708FE2 Nintendo DS Slot-2 ROM image (PassMe)
+!:mime application/x-nintendo-ds-rom
+
+#------------------------------------------------------------------------------
+# ngp: file(1) magic for the Neo Geo Pocket (Color) raw ROM format.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://neogpc.googlecode.com/svn-history/r10/trunk/src/core/neogpc.cpp
+# - https://www.devrs.com/ngp/files/ngpctech.txt
+#
+0x0A string BY\ SNK\ CORPORATION Neo Geo Pocket
+!:mime application/x-neo-geo-pocket-rom
+>0x23 byte 0x10 Color
+>0 byte x ROM image
+>0x24 string >\0 \b: "%.12s"
+>0x21 uleshort x \b, NEOP%04X
+>0x1F ubyte 0xFF (debug mode enabled)
+
+#------------------------------------------------------------------------------
+# msx: file(1) magic for MSX game cartridge dumps
+# Too simple - MPi
+#0 beshort 0x4142 MSX game cartridge dump
+
+#------------------------------------------------------------------------------
+# Sony Playstation executables (Adam Sjoegren <asjo@diku.dk>) :
+0 string PS-X\ EXE Sony Playstation executable
+>16 lelong x PC=%#08x,
+>20 lelong !0 GP=%#08x,
+>24 lelong !0 .text=[%#08x,
+>>28 lelong x \b%#x],
+>32 lelong !0 .data=[%#08x,
+>>36 lelong x \b%#x],
+>40 lelong !0 .bss=[%#08x,
+>>44 lelong x \b%#x],
+>48 lelong !0 Stack=%#08x,
+>48 lelong =0 No Stack!,
+>52 lelong !0 StackSize=%#x,
+#>76 string >\0 (%s)
+# Area:
+>113 string x (%s)
+
+# CPE executables
+0 string CPE CPE executable
+>3 byte x (version %d)
+
+# Sony PlayStation archive (PSARC)
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://www.psdevwiki.com/ps3/PlayStation_archive_(PSARC)
+0 string PSAR Sony PlayStation Archive
+!:ext psarc
+>4 ubeshort x \b, version %d.
+>6 ubeshort x \b%d
+>8 string zlib \b, zlib compression
+>8 string lzma \b, LZMA compression
+>28 ubeshort&2 0 \b, relative paths
+>28 ubeshort&2 2 \b, absolute paths
+>28 ubeshort&1 1 \b, ignore case
+
+#------------------------------------------------------------------------------
+# Microsoft Xbox executables .xbe (Esa Hyytia <ehyytia@cc.hut.fi>)
+0 string XBEH Microsoft Xbox executable
+!:mime audio/x-xbox-executable
+!:ext xbe
+# expect base address of 0x10000
+>0x0104 ulelong =0x10000
+>>(0x0118.l-0x0FFF4) lestring16 x \b: "%.40s"
+>>(0x0118.l-0x0FFF5) byte x (%c
+>>(0x0118.l-0x0FFF6) byte x \b%c-
+>>(0x0118.l-0x0FFF8) uleshort x \b%03u)
+>>(0x0118.l-0x0FF60) ulelong&0x80000007 0x80000007 \b, all regions
+>>(0x0118.l-0x0FF60) ulelong&0x80000007 !0x80000007
+>>>(0x0118.l-0x0FF60) ulelong >0 (regions:
+>>>>(0x0118.l-0x0FF60) ulelong &0x00000001 NA
+>>>>(0x0118.l-0x0FF60) ulelong &0x00000002 Japan
+>>>>(0x0118.l-0x0FF60) ulelong &0x00000004 Rest_of_World
+>>>>(0x0118.l-0x0FF60) ulelong &0x80000000 Manufacturer
+>>>(0x0118.l-0x0FF60) ulelong >0 \b)
+# probabilistic checks whether signed or not
+>0x0004 ulelong =0x0
+>>&2 ulelong =0x0
+>>>&2 ulelong =0x0 \b, not signed
+>0x0004 ulelong >0
+>>&2 ulelong >0
+>>>&2 ulelong >0 \b, signed
+
+# --------------------------------
+# Microsoft Xbox data file formats
+0 string XIP0 XIP, Microsoft Xbox data
+0 string XTF0 XTF, Microsoft Xbox data
+
+#------------------------------------------------------------------------------
+# Microsoft Xbox 360 executables (.xex)
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://free60project.github.io/wiki/XEX.html
+# - https://github.com/xenia-project/xenia/blob/HEAD/src/xenia/kernel/util/xex2_info.h
+
+# Title ID (part of Execution ID section)
+0 name xbox-360-xex-execution-id
+>(0.L+0xC) byte x (%c
+>(0.L+0xD) byte x \b%c
+>(0.L+0xE) beshort x \b-%04u, media ID:
+>(0.L) belong x %08X)
+
+# Region code (part of Security Info)
+0 name xbox-360-xex-region-code
+>0 ubelong 0xFFFFFFFF \b, all regions
+>0 ubelong !0xFFFFFFFF
+>>0 ubelong >0 (regions:
+>>0 ubelong&0x000000FF 0x000000FF USA
+>>0 ubelong&0x00000100 0x00000100 Japan
+>>0 ubelong&0x00000200 0x00000200 China
+>>0 ubelong&0x0000FC00 0x0000FC00 Asia
+>>0 ubelong&0x00FF0000 0x00FF0000 PAL
+>>0 ubelong&0x00FF0000 0x00FE0000 PAL [except AU/NZ]
+>>0 ubelong&0x00FF0000 0x00010000 AU/NZ
+>>0 ubelong&0xFF000000 0xFF000000 Other
+>>0 ubelong >0 \b)
+
+0 string XEX2 Microsoft Xbox 360 executable
+!:mime audio/x-xbox360-executable
+!:ext xex
+>0x18 search/0x100 \x00\x04\x00\x06
+>>&0 use xbox-360-xex-execution-id
+>(0x010.L+0x178) use xbox-360-xex-region-code
+
+0 string XEX1 Microsoft Xbox 360 executable (XEX1)
+!:mime audio/x-xbox360-executable
+!:ext xex
+>0x18 search/0x100 \x00\x04\x00\x06
+>>&0 use xbox-360-xex-execution-id
+>(0x010.L+0x154) use xbox-360-xex-region-code
+
+#------------------------------------------------------------------------------
+# Microsoft Xbox 360 packages
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://free60project.github.io/wiki/STFS.html
+# - https://github.com/xenia-project/xenia/blob/HEAD/src/xenia/kernel/util/xex2_info.h
+
+# TODO: More information for console-signed packages.
+
+0 name xbox-360-package
+>0x360 byte x (%c
+>0x361 byte x \b%c
+>0x362 beshort x \b-%04u, media ID:
+>0x354 belong x %08X)
+>0x344 belong x \b, content type:
+>>0x344 belong 0x1 Saved Game
+>>0x344 belong 0x2 Marketplace Content
+>>0x344 belong 0x3 Publisher
+>>0x344 belong 0x1000 Xbox 360 Title
+>>0x344 belong 0x2000 IPTV Pause Buffer
+>>0x344 belong 0x4000 Installed Game
+>>0x344 belong 0x5000 Original Xbox Game
+>>0x344 belong 0x9000 Avatar Item
+>>0x344 belong 0x10000 Profile
+>>0x344 belong 0x20000 Gamer Picture
+>>0x344 belong 0x30000 Theme
+>>0x344 belong 0x40000 Cache File
+>>0x344 belong 0x50000 Storage Download
+>>0x344 belong 0x60000 Xbox Saved Game
+>>0x344 belong 0x70000 Xbox Download
+>>0x344 belong 0x80000 Game Demo
+>>0x344 belong 0x90000 Video
+>>0x344 belong 0xA0000 Game
+>>0x344 belong 0xB0000 Installer
+>>0x344 belong 0xC0000 Game Trailer
+>>0x344 belong 0xD0000 Arcade Title
+>>0x344 belong 0xE0000 XNA
+>>0x344 belong 0xF0000 License Store
+>>0x344 belong 0x100000 Movie
+>>0x344 belong 0x200000 TV
+>>0x344 belong 0x300000 Music Video
+>>0x344 belong 0x400000 Game Video
+>>0x344 belong 0x500000 Podcast Video
+>>0x344 belong 0x600000 Viral Video
+>>0x344 belong 0x2000000 Community Game
+
+0 string CON\x20 Microsoft Xbox 360 package (console-signed)
+>0 use xbox-360-package
+0 string PIRS
+>0 belong 0 Microsoft Xbox 360 package (non-Xbox Live)
+>>0 use xbox-360-package
+0 string LIVE
+>0x104 belong 0 Microsoft Xbox 360 package (Xbox Live)
+>>0 use xbox-360-package
+
+# Atari Lynx cartridge dump (EXE/BLL header)
+# From: "Stefan A. Haubenthal" <polluks@sdf.lonestar.org>
+# Reference:
+# https://raw.githubusercontent.com/cc65/cc65/master/libsrc/lynx/exehdr.s
+# Double-check that the image type matches too, 0x8008 conflicts with
+# 8 character OMF-86 object file headers.
+0 beshort 0x8008
+>6 string BS93 Lynx homebrew cartridge
+!:mime application/x-atari-lynx-rom
+>>2 beshort x \b, RAM start $%04x
+# Update: Joerg Jenderek
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/l/lnx.trid.xml
+# Note: called "Atari Lynx ROM" by TrID
+0 string LYNX Lynx cartridge
+!:mime application/x-atari-lynx-rom
+!:ext lnx
+# bank 0 page size like: 128 256 512
+>4 leshort/4 >0 \b, bank 0 %dk
+>6 leshort/4 >0 \b, bank 1 %dk
+# 32 bytes cart name like: "jconnort.lyx" "viking~1.lyx" "Eye of the Beholder" "C:\EMU\LYNX\ROMS\ULTCHESS.LYX"
+>10 string >\0 \b, "%.32s"
+# 16 bytes manufacturer like: "Atari" "NuFX Inc." "Matthias Domin"
+>42 string >\0 \b, "%.16s"
+# version number
+#>8 leshort !1 \b, version number %u
+# rotation: 1~left Lexis (NA).lnx 2~right Centipede (Prototype).lnx
+>58 ubyte >0 \b, rotation %u
+# spare
+#>59 lelong !0 \b, spare %#x
+
+# Opera file system that is used on the 3DO console
+# From: Serge van den Boom <svdb@stack.nl>
+0 string \x01ZZZZZ\x01 3DO "Opera" file system
+
+# From: Alex Myczko <alex@aiei.ch>
+# From: David Pflug <david@pflug.email>
+# is the offset 12 or the offset 16 correct?
+# GBS (Game Boy Sound) magic
+# ftp://ftp.modland.com/pub/documents/format_documentation/\
+# Gameboy%20Sound%20System%20(.gbs).txt
+0 string GBS Nintendo Gameboy Music/Audio Data
+#12 string GameBoy\ Music\ Module Nintendo Gameboy Music Module
+>16 string >\0 ("%.32s" by
+>48 string >\0 %.32s, copyright
+>80 string >\0 %.32s),
+>3 byte x version %u,
+>4 byte x %u tracks
+
+# IPS Patch Files from: From: Thomas Klausner <tk@giga.or.at>
+# see https://zerosoft.zophar.net/ips.php
+0 string PATCH IPS patch file
+!:ext ips
+
+# BPS Patch Files - from: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://www.romhacking.net/documents/746/
+0 string BPS1 BPS patch file
+!:ext bps
+
+# APS Patch Files - from: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://github.com/btimofeev/UniPatcher/wiki/APS-(N64)
+0 string APS10 APS patch file
+!:ext aps
+>5 byte 0 \b, simple patch
+>5 byte 1 \b, N64-specific patch for
+>>58 byte x N%c
+>>59 byte x \b%c
+>>60 byte x \b%c
+>7 byte !0x20
+# FIXME: /T specifier isn't working with a fixed-length string.
+>>7 string x \b: "%.50s"
+
+# UPS Patch Files - from: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: http://fileformats.archiveteam.org/wiki/UPS_(binary_patch_format)
+0 string UPS1 UPS patch file
+!:ext ups
+
+# Playstations Patch Files from: From: Thomas Klausner <tk@giga.or.at>
+0 string PPF30 Playstation Patch File version 3.0
+>5 byte 0 \b, PPF 1.0 patch
+>5 byte 1 \b, PPF 2.0 patch
+>5 byte 2 \b, PPF 3.0 patch
+>>56 byte 0 \b, Imagetype BIN (any)
+>>56 byte 1 \b, Imagetype GI (PrimoDVD)
+>>57 byte 0 \b, Blockcheck disabled
+>>57 byte 1 \b, Blockcheck enabled
+>>58 byte 0 \b, Undo data not available
+>>58 byte 1 \b, Undo data available
+>6 string x \b, description: %s
+
+0 string PPF20 Playstation Patch File version 2.0
+>5 byte 0 \b, PPF 1.0 patch
+>5 byte 1 \b, PPF 2.0 patch
+>>56 lelong >0 \b, size of file to patch %d
+>6 string x \b, description: %s
+
+0 string PPF10 Playstation Patch File version 1.0
+>5 byte 0 \b, Simple Encoding
+>6 string x \b, description: %s
+
+# Compressed ISO disc image (used mostly by PSP, PS2 and MegaDrive)
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://en.wikipedia.org/wiki/.CSO
+# NOTE: This is NOT the same as Compact ISO or GameCube/Wii disc image,
+# though it has the same magic number.
+0 string CISO
+# Match CISO version 1 with ISO-9660 sector size
+>20 ubyte <2
+>>16 ulelong =2048 CSO v1 disk image
+!:mime application/x-compressed-iso
+!:ext ciso/cso
+>>>8 ulequad x \b, original size %llu bytes
+>>>16 ulelong x \b, datablock size %u bytes
+# Match CISO version 2
+>20 ubyte =2
+>>22 uleshort =0
+>>>4 ulelong =24 CSO v2 disk image
+!:mime application/x-compressed-iso
+!:ext ciso/cso
+>>>>8 ulequad x \b, original size %llu bytes
+>>>>16 ulelong x \b, datablock size %u bytes
+
+# From: Daniel Dawson <ddawson@icehouse.net>
+# SNES9x .smv "movie" file format.
+0 string SMV\x1A SNES9x input recording
+>0x4 lelong x \b, version %d
+# version 4 is latest so far
+>0x4 lelong <5
+>>0x8 ledate x \b, recorded at %s
+>>0xc lelong >0 \b, rerecorded %d times
+>>0x10 lelong x \b, %d frames long
+>>0x14 byte >0 \b, data for controller(s):
+>>>0x14 byte &0x1 #1
+>>>0x14 byte &0x2 #2
+>>>0x14 byte &0x4 #3
+>>>0x14 byte &0x8 #4
+>>>0x14 byte &0x10 #5
+>>0x15 byte ^0x1 \b, begins from snapshot
+>>0x15 byte &0x1 \b, begins from reset
+>>0x15 byte ^0x2 \b, NTSC standard
+>>0x15 byte &0x2 \b, PAL standard
+>>0x17 byte &0x1 \b, settings:
+# WIP1Timing not used as of version 4
+>>>0x4 lelong <4
+>>>>0x17 byte &0x2 WIP1Timing
+>>>0x17 byte &0x4 Left+Right
+>>>0x17 byte &0x8 VolumeEnvX
+>>>0x17 byte &0x10 FakeMute
+>>>0x17 byte &0x20 SyncSound
+# New flag as of version 4
+>>>0x4 lelong >3
+>>>>0x17 byte &0x80 NoCPUShutdown
+>>0x4 lelong <4
+>>>0x18 lelong >0x23
+>>>>0x20 leshort !0
+>>>>>0x20 lestring16 x \b, metadata: "%s"
+>>0x4 lelong >3
+>>>0x24 byte >0 \b, port 1:
+>>>>0x24 byte 1 joypad
+>>>>0x24 byte 2 mouse
+>>>>0x24 byte 3 SuperScope
+>>>>0x24 byte 4 Justifier
+>>>>0x24 byte 5 multitap
+>>>0x24 byte >0 \b, port 2:
+>>>>0x25 byte 1 joypad
+>>>>0x25 byte 2 mouse
+>>>>0x25 byte 3 SuperScope
+>>>>0x25 byte 4 Justifier
+>>>>0x25 byte 5 multitap
+>>>0x18 lelong >0x43
+>>>>0x40 leshort !0
+>>>>>0x40 lestring16 x \b, metadata: "%s"
+>>0x17 byte &0x40 \b, ROM:
+>>>(0x18.l-26) lelong x CRC32 %#08x
+>>>(0x18.l-23) string x "%s"
+
+# Type: scummVM savegame files
+# From: Sven Hartge <debian@ds9.argh.org>
+0 string SCVM ScummVM savegame
+>12 string >\0 "%s"
+
+#------------------------------------------------------------------------------
+# Nintendo GameCube / Wii file formats.
+#
+
+# Type: Nintendo GameCube/Wii common disc header data.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://wiibrew.org/wiki/Wii_Disc
+0 name nintendo-gcn-disc-common
+>0x20 string x "%.64s"
+>0x00 string x (%.6s
+>0x06 byte >0
+>>0x06 byte 1 \b, Disc 2
+>>0x06 byte 2 \b, Disc 3
+>>0x06 byte 3 \b, Disc 4
+>0x07 byte x \b, Rev.%02u)
+>0x18 belong 0x5D1C9EA3
+>>0x60 beshort 0x0101 \b (Unencrypted)
+>0x200 string NKIT \b (NKit compressed)
+
+
+# Type: Nintendo GameCube disc image
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://wiibrew.org/wiki/Wii_Disc
+0x1C belong 0xC2339F3D Nintendo GameCube disc image:
+!:mime application/x-gamecube-rom
+>0 use nintendo-gcn-disc-common
+
+# Type: Nintendo GameCube embedded disc image
+# Commonly found on demo discs.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: http://hitmen.c02.at/files/yagcd/yagcd/index.html#idx14.8
+0 belong 0xAE0F38A2
+>0x0C belong 0x00100000
+>>(8.L+0x1C) belong 0xC2339F3D Nintendo GameCube embedded disc image:
+!:mime application/x-gamecube-rom
+>>>(8.L) use nintendo-gcn-disc-common
+
+# Type: Nintendo Wii disc image
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://wiibrew.org/wiki/Wii_Disc
+0x18 belong 0x5D1C9EA3 Nintendo Wii disc image:
+>0 use nintendo-gcn-disc-common
+
+# Type: Nintendo Wii disc image (WBFS format)
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://wiibrew.org/wiki/Wii_Disc
+0 string WBFS
+>0x218 belong 0x5D1C9EA3 Nintendo Wii disc image (WBFS format):
+!:mime application/x-wii-rom
+>>0x200 use nintendo-gcn-disc-common
+
+# Type: Nintendo GameCube/Wii disc image (CISO format)
+# NOTE: This is NOT the same as Compact ISO or PSP CISO,
+# though it has the same magic number.
+0 string CISO
+# Other fields are used to determine what type of CISO this is:
+# - 0x04 == 0x00200000: GameCube/Wii CISO (block_size)
+# - 0x10 == 0x00000800: PSP CISO (ISO-9660 sector size)
+# - None of the above: Compact ISO.
+>4 lelong 0x200000
+>>8 byte 1
+>>>0x801C belong 0xC2339F3D Nintendo GameCube disc image (CISO format):
+!:mime application/x-wii-rom
+>>>>0x8000 use nintendo-gcn-disc-common
+>>>0x8018 belong 0x5D1C9EA3 Nintendo Wii disc image (CISO format):
+!:mime application/x-wii-rom
+>>>>0x8000 use nintendo-gcn-disc-common
+
+# Type: Nintendo GameCube/Wii disc image (GCZ format)
+# Due to zlib compression, we can't get the actual disc information.
+0 lelong 0xB10BC001
+>4 lelong 0 Nintendo GameCube disc image (GCZ format)
+!:mime application/x-gamecube-rom
+>4 lelong 1 Nintendo Wii disc image (GCZ format)
+!:mime application/x-wii-rom
+>4 default x Nintendo GameCube/Wii disc image (GCZ format)
+
+# Type: Nintendo GameCube/Wii disc image (WDF format)
+0 string WII\001DISC
+>8 belong 1
+# WDFv1
+>>0x54 belong 0xC2339F3D Nintendo GameCube disc image (WDFv1 format):
+!:mime application/x-gamecube-rom
+>>>0x38 use nintendo-gcn-disc-common
+>>0x58 belong 0x5D1C9EA3 Nintendo Wii disc image (WDFv1 format):
+!:mime application/x-wii-rom
+>>>0x38 use nintendo-gcn-disc-common
+>8 belong 2
+# WDFv2
+>>(12.L+0x1C) belong 0xC2339F3D Nintendo GameCube disc image (WDFv2 format):
+!:mime application/x-gamecube-rom
+>>>(12.L) use nintendo-gcn-disc-common
+>>(12.L+0x18) belong 0x5D1C9EA3 Nintendo Wii disc image (WDFv2 format):
+!:mime application/x-wii-rom
+>>>(12.L) use nintendo-gcn-disc-common
+
+# Type: Nintendo GameCube/Wii disc image (WIA format)
+0 string WIA\001 Nintendo
+>0x48 belong 1 GameCube
+!:mime application/x-gamecube-rom
+>0x48 belong 2 Wii
+!:mime application/x-wii-rom
+>0x48 default x GameCube/Wii
+>0x48 belong x disc image (WIA format):
+>>0x58 use nintendo-gcn-disc-common
+
+# Type: Nintendo GameCube/Wii disc image (with SDK header)
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://wiibrew.org/wiki/Wii_Disc
+0 belong 0xFFFF0000
+>0x18 belong 0x00000000
+>>0x1C belong 0x00000000
+>>>0x8018 belong 0x5D1C9EA3 Nintendo Wii SDK disc image:
+!:mime application/x-wii-rom
+>>>>0x8000 use nintendo-gcn-disc-common
+>>>0x801C belong 0xC2339F3D Nintendo GameCube SDK disc image:
+!:mime application/x-gamecube-rom
+>>>>0x8000 use nintendo-gcn-disc-common
+
+# Type: Nintendo GameCube/Wii disc image (RVZ format)
+0 string RVZ\001 Nintendo
+>0x48 belong 1 GameCube
+!:mime application/x-gamecube-rom
+>0x48 belong 2 Wii
+!:mime application/x-wii-rom
+>0x48 default x GameCube/Wii
+>0x48 belong x disc image (RVZ format):
+>>0x58 use nintendo-gcn-disc-common
+
+#------------------------------------------------------------------------------
+# Nintendo 3DS file formats.
+#
+
+# Type: Nintendo 3DS "NCSD" image. (game cards and eMMC)
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://www.3dbrew.org/wiki/NCSD
+0x100 string NCSD
+>0x118 lequad 0 Nintendo 3DS Game Card image
+# NCCH header for partition 0. (game data)
+>>0x1150 string >\0 \b: "%.16s"
+>>0x312 byte x (Rev.%02u)
+>>0x118C byte 2 (New3DS only)
+>>0x18D byte 0 (inner device)
+>>0x18D byte 1 (Card1)
+>>0x18D byte 2 (Card2)
+>>0x18D byte 3 (extended device)
+>0x118 bequad 0x0102020202000000 Nintendo 3DS eMMC dump (Old3DS)
+>0x118 bequad 0x0102020203000000 Nintendo 3DS eMMC dump (New3DS)
+
+# Nintendo 3DS version code.
+# Reference: https://www.3dbrew.org/wiki/Titles
+# Format: leshort containing three fields:
+# - 6-bit: Major
+# - 6-bit: Minor
+# - 4-bit: Revision
+# NOTE: Only supporting major/minor versions from 0-15 right now.
+# NOTE: Should be prefixed with "v".
+0 name nintendo-3ds-version-code
+# Raw version.
+>0 leshort x \b%u,
+# Major version.
+>0 leshort&0xFC00 0x0000 0
+>0 leshort&0xFC00 0x0400 1
+>0 leshort&0xFC00 0x0800 2
+>0 leshort&0xFC00 0x0C00 3
+>0 leshort&0xFC00 0x1000 4
+>0 leshort&0xFC00 0x1400 5
+>0 leshort&0xFC00 0x1800 6
+>0 leshort&0xFC00 0x1C00 7
+>0 leshort&0xFC00 0x2000 8
+>0 leshort&0xFC00 0x2400 9
+>0 leshort&0xFC00 0x2800 10
+>0 leshort&0xFC00 0x2C00 11
+>0 leshort&0xFC00 0x3000 12
+>0 leshort&0xFC00 0x3400 13
+>0 leshort&0xFC00 0x3800 14
+>0 leshort&0xFC00 0x3C00 15
+# Minor version.
+>0 leshort&0x03F0 0x0000 \b.0
+>0 leshort&0x03F0 0x0010 \b.1
+>0 leshort&0x03F0 0x0020 \b.2
+>0 leshort&0x03F0 0x0030 \b.3
+>0 leshort&0x03F0 0x0040 \b.4
+>0 leshort&0x03F0 0x0050 \b.5
+>0 leshort&0x03F0 0x0060 \b.6
+>0 leshort&0x03F0 0x0070 \b.7
+>0 leshort&0x03F0 0x0080 \b.8
+>0 leshort&0x03F0 0x0090 \b.9
+>0 leshort&0x03F0 0x00A0 \b.10
+>0 leshort&0x03F0 0x00B0 \b.11
+>0 leshort&0x03F0 0x00C0 \b.12
+>0 leshort&0x03F0 0x00D0 \b.13
+>0 leshort&0x03F0 0x00E0 \b.14
+>0 leshort&0x03F0 0x00F0 \b.15
+# Revision.
+>0 leshort&0x000F x \b.%u
+
+# Type: Nintendo 3DS "NCCH" container.
+# https://www.3dbrew.org/wiki/NCCH
+0x100 string NCCH Nintendo 3DS
+>0x18D byte&2 0 File Archive (CFA)
+>0x18D byte&2 2 Executable Image (CXI)
+>0x150 string >\0 \b: "%.16s"
+>0x18D byte 0x05
+>>0x10E leshort x (Old3DS System Update v
+>>0x10E use nintendo-3ds-version-code
+>>0x10E leshort x \b)
+>0x18D byte 0x15
+>>0x10E leshort x (New3DS System Update v
+>>0x10E use nintendo-3ds-version-code
+>>0x10E leshort x \b)
+>0x18D byte !0x05
+>>0x18D byte !0x15
+>>>0x112 byte x (v
+>>>0x112 use nintendo-3ds-version-code
+>>>0x112 byte x \b)
+>0x18C byte 2 (New3DS only)
+
+# Type: Nintendo 3DS "SMDH" file. (application description)
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://3dbrew.org/wiki/SMDH
+0 string SMDH Nintendo 3DS SMDH file
+>0x208 leshort !0
+>>0x208 lestring16 x \b: "%.128s"
+>>0x388 leshort !0
+>>>0x388 lestring16 x by %.128s
+>0x208 leshort 0
+>>0x008 leshort !0
+>>>0x008 lestring16 x \b: "%.128s"
+>>>0x188 leshort !0
+>>>>0x188 lestring16 x by %.128s
+
+# Type: Nintendo 3DS Homebrew Application.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://3dbrew.org/wiki/3DSX_Format
+0 string 3DSX Nintendo 3DS Homebrew Application (3DSX)
+
+# Type: Nintendo 3DS Banner Model Data.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://3dbrew.org/wiki/CBMD
+0 string CBMD\0\0\0\0 Nintendo 3DS Banner Model Data
+
+#------------------------------------------------------------------------------
+# a7800: file(1) magic for the Atari 7800 raw ROM format.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://sites.google.com/site/atari7800wiki/a78-header
+
+0 byte >0
+>0 byte <3
+>>1 string ATARI7800 Atari 7800 ROM image
+!:mime application/x-atari-7800-rom
+>>>0x11 string >\0 \b: "%.32s"
+# Display type.
+>>>0x39 byte 0 (NTSC)
+>>>0x39 byte 1 (PAL)
+>>>0x36 byte&1 1 (POKEY)
+
+#------------------------------------------------------------------------------
+# vectrex: file(1) magic for the GCE Vectrex raw ROM format.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: http://www.playvectrex.com/designit/chrissalo/hello1.htm
+#
+# NOTE: Title is terminated with 0x80, not 0.
+# The header is terminated with a 0, so that will
+# terminate the title as well.
+#
+0 string g\ GCE Vectrex ROM image
+>0x11 string >\0 \b: "%.16s"
+
+#------------------------------------------------------------------------------
+# amiibo: file(1) magic for Nintendo amiibo NFC dumps.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://www.3dbrew.org/wiki/Amiibo
+0x00 byte 0x04
+>0x0A beshort 0x0FE0
+>>0x0C belong 0xF110FFEE
+>>>0x208 beshort 0x0100
+>>>>0x020A byte 0x0F
+>>>>>0x020C bequad 0x000000045F000000
+>>>>>>0x5B byte 0x02
+>>>>>>>0x54 belong x Nintendo amiibo NFC dump - amiibo ID: %08X-
+>>>>>>>0x58 belong x \b%08X
+
+#------------------------------------------------------------------------------
+# Type: Nintendo Switch XCI (Game Cartridge Image)
+# From: Benjamin Lowry <ben@ben.gmbh>
+# Reference: https://switchbrew.org/wiki/Gamecard_Format
+0x100 string HEAD
+>0x10D byte 0xFA Nintendo Switch cartridge image (XCI), 1GB
+>0x10D byte 0xF8 Nintendo Switch cartridge image (XCI), 2GB
+>0x10D byte 0xF0 Nintendo Switch cartridge image (XCI), 4GB
+>0x10D byte 0xE0 Nintendo Switch cartridge image (XCI), 8GB
+>0x10D byte 0xE1 Nintendo Switch cartridge image (XCI), 16GB
+>0x10D byte 0xE2 Nintendo Switch cartridge image (XCI), 32GB
+
+#------------------------------------------------------------------------------
+# Type: Nintendo Switch Executable
+# From: Benjamin Lowry <ben@ben.gmbh>
+# Reference: https://switchbrew.org/wiki/NSO
+0x00 string NSO0 Nintendo Switch executable (NSO)
+
+#------------------------------------------------------------------------------
+# Type: Nintendo Switch PFS0
+# From: Benjamin Lowry <ben@ben.gmbh>
+# Reference: https://switchbrew.org/wiki/NCA_Format#PFS0
+0x00 string PFS0 Nintendo Switch partition filesystem (PFS0)
+>0x04 ulelong x \b, %d files
+
+#------------------------------------------------------------------------------
+# amiibo: file(1) magic for Nintendo Badge Arcade files.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://github.com/GerbilSoft/rom-properties/issues/92
+# - https://github.com/CaitSith2/BadgeArcadeTool
+# - https://github.com/TheMachinumps/Advanced-badge-editor
+
+# PRBS: Individual badge and/or mega badge.
+0 string PRBS
+>0x44 byte >0x20 Nintendo Badge Arcade
+>>0xB8 ulelong <2
+>>>0xBC ulelong <2 badge:
+>>>0xBC ulelong >1 Mega Badge
+>>>>0xB8 ulelong x (%ux
+>>>>0xBC ulelong x \b%u):
+>>0xB8 ulelong >1 Mega Badge
+>>>0xB8 ulelong x (%ux
+>>>0xBC ulelong x \b%u):
+>0x44 string x "%s"
+>0x3C ulelong x \b, badge ID: %u
+>0x74 byte >0x20
+>>0x74 string x \b, set: "%s"
+>0xA8 ulelong !0xFFFFFFFF
+>>0xA8 ulelong x \b, launch title ID: %08X
+>>0xA4 ulelong x \b-%08X
+
+# CABS: Badge set.
+0 string CABS
+>0x2C byte >0x20 Nintendo Badge Arcade badge set:
+>>0x2C string x "%.48s"
+>>0x24 ulelong x \b, set ID: %u
+
+#------------------------------------------------------------------------------
+# sufami: file(1) magic for Sufami Turbo ROM images.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://problemkaputt.de/fullsnes.htm#snescartsufamiturbominicartridgeadaptor
+0 string BANDAI\ SFC-ADX
+>0x10 string !SFC-ADX\ BACKUP Sufami Turbo ROM image:
+>>0x10 string/T x "%.14s"
+>>0x30 byte x \b, ID %02X
+>>0x31 byte x \b%02X
+>>0x32 byte x \b%02X
+>>0x33 ubyte >0 \b, series index %u
+>>0x34 ubyte 0 [SlowROM]
+>>0x34 ubyte 1 [FastROM]
+>>0x35 ubyte 1 [SRAM]
+>>0x35 ubyte 3 [Special]
diff --git a/contrib/libs/libmagic/magic/Magdir/convex b/contrib/libs/libmagic/magic/Magdir/convex
new file mode 100644
index 0000000000..6b28f768cc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/convex
@@ -0,0 +1,69 @@
+
+#------------------------------------------------------------------------------
+# $File: convex,v 1.8 2012/10/03 23:44:43 christos Exp $
+# convex: file(1) magic for Convex boxes
+#
+# Convexes are big-endian.
+#
+# /*\
+# * Below are the magic numbers and tests added for Convex.
+# * Added at beginning, because they are expected to be used most.
+# \*/
+0 belong 0507 Convex old-style object
+>16 belong >0 not stripped
+0 belong 0513 Convex old-style demand paged executable
+>16 belong >0 not stripped
+0 belong 0515 Convex old-style pre-paged executable
+>16 belong >0 not stripped
+0 belong 0517 Convex old-style pre-paged, non-swapped executable
+>16 belong >0 not stripped
+0 belong 0x011257 Core file
+#
+# The following are a series of dump format magic numbers. Each one
+# corresponds to a drastically different dump format. The first on is
+# the original dump format on a 4.1 BSD or earlier file system. The
+# second marks the change between the 4.1 file system and the 4.2 file
+# system. The Third marks the changing of the block size from 1K
+# to 2K to be compatible with an IDC file system. The fourth indicates
+# a dump that is dependent on Convex Storage Manager, because data in
+# secondary storage is not physically contained within the dump.
+# The restore program uses these number to determine how the data is
+# to be extracted.
+#
+24 belong =60013 dump format, 4.2 or 4.3 BSD (IDC compatible)
+24 belong =60014 dump format, Convex Storage Manager by-reference dump
+#
+# what follows is a bunch of bit-mask checks on the flags field of the opthdr.
+# If there is no `=' sign, assume just checking for whether the bit is set?
+#
+0 belong 0601 Convex SOFF
+>88 belong&0x000f0000 =0x00000000 c1
+>88 belong &0x00010000 c2
+>88 belong &0x00020000 c2mp
+>88 belong &0x00040000 parallel
+>88 belong &0x00080000 intrinsic
+>88 belong &0x00000001 demand paged
+>88 belong &0x00000002 pre-paged
+>88 belong &0x00000004 non-swapped
+>88 belong &0x00000008 POSIX
+#
+>84 belong &0x80000000 executable
+>84 belong &0x40000000 object
+>84 belong&0x20000000 =0 not stripped
+>84 belong&0x18000000 =0x00000000 native fpmode
+>84 belong&0x18000000 =0x10000000 ieee fpmode
+>84 belong&0x18000000 =0x18000000 undefined fpmode
+#
+0 belong 0605 Convex SOFF core
+#
+0 belong 0607 Convex SOFF checkpoint
+>88 belong&0x000f0000 =0x00000000 c1
+>88 belong &0x00010000 c2
+>88 belong &0x00020000 c2mp
+>88 belong &0x00040000 parallel
+>88 belong &0x00080000 intrinsic
+>88 belong &0x00000008 POSIX
+#
+>84 belong&0x18000000 =0x00000000 native fpmode
+>84 belong&0x18000000 =0x10000000 ieee fpmode
+>84 belong&0x18000000 =0x18000000 undefined fpmode
diff --git a/contrib/libs/libmagic/magic/Magdir/coverage b/contrib/libs/libmagic/magic/Magdir/coverage
new file mode 100644
index 0000000000..9f2c3dc91b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/coverage
@@ -0,0 +1,91 @@
+
+#------------------------------------------------------------------------------
+# $File: coverage,v 1.3 2021/02/23 00:51:10 christos Exp $
+# xoverage: file(1) magic for test coverage data
+
+# File formats used to store test coverage data
+# 2016-05-21, Georg Sauthoff <mail@georg.so>
+
+
+# - GCC gcno - written by GCC at compile time when compiling with
+# gcc -ftest-coverage
+# - GCC gcda - written by a program that was compiled with
+# gcc -fprofile-arcs
+# - LLVM raw profiles - generated by a program compiled with
+# clang -fprofile-instr-generate -fcoverage-mapping ...
+# - LLVM indexed profiles - generated by
+# llvm-profdata
+# - GCOV reports, i.e. the annotated source code
+# - LCOV trace files, i.e. aggregated GCC profiles
+#
+# GCC coverage tracefiles
+# .gcno file are created during compile time,
+# while data collected during runtime is stored in .gcda files
+# cf. gcov-io.h
+# https://gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/Gcov-Data-Files.html
+# Examples:
+# Fedora 23/x86-64/gcc-5.3.1: 6f 6e 63 67 52 33 30 35
+# Debian 8 PPC64/gcc-4.9.2 : 67 63 6e 6f 34 30 39 2a
+0 lelong 0x67636e6f GCC gcno coverage (-ftest-coverage),
+>&3 byte x version %c.
+>&1 byte x \b%c
+
+# big endian
+0 belong 0x67636e6f GCC gcno coverage (-ftest-coverage),
+>&0 byte x version %c.
+>&2 byte x \b%c (big-endian)
+
+# Examples:
+# Fedora 23/x86-64/gcc-5.3.1: 61 64 63 67 52 33 30 35
+# Debian 8 PPC64/gcc-4.9.2 : 67 63 64 61 34 30 39 2a
+0 lelong 0x67636461 GCC gcda coverage (-fprofile-arcs),
+>&3 byte x version %c.
+>&1 byte x \b%c
+
+# big endian
+0 belong 0x67636461 GCC gcda coverage (-fprofile-arcs),
+>&0 byte x version %c.
+>&2 byte x \b%c (big-endian)
+
+
+# LCOV tracefiles
+# cf. http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php
+0 string TN:
+>&0 search/64 \nSF:/ LCOV coverage tracefile
+
+
+# Coverage reports generated by gcov
+# i.e. source code annotated with coverage information
+0 string \x20\x20\x20\x20\x20\x20\x20\x20-:\x20\x20\x20\ 0:Source:
+>&0 search/128 \x20\x20\x20\x20\x20\x20\x20\x20-:\x20\x20\x20\ 0:Graph:
+>>&0 search/128 \x20\x20\x20\x20\x20\x20\x20\x20-:\x20\x20\x20\ 0:Data: GCOV coverage report
+
+
+# LLVM coverage files
+
+# raw data after running a program compiled with:
+# `clang -fprofile-instr-generate -fcoverage-mapping ...`
+# default name: default.profraw
+# magic is: \xFF lprofr \x81
+# cf. https://llvm.org/docs/doxygen/html/InstrProfData_8inc_source.html
+0 lequad 0xff6c70726f667281 LLVM raw profile data,
+>&0 byte x version %d
+
+# big endian
+0 bequad 0xff6c70726f667281 LLVM raw profile data,
+>&7 byte x version %d (big-endian)
+
+
+# LLVM indexed instruction profile (as generated by llvm-profdata)
+# magic is: reverse(\xFF lprofi \x81)
+# cf. https://llvm.org/docs/CoverageMappingFormat.html
+# https://llvm.org/docs/doxygen/html/namespacellvm_1_1IndexedInstrProf.html
+# https://llvm.org/docs/CommandGuide/llvm-cov.html
+# https://llvm.org/docs/CommandGuide/llvm-profdata.html
+0 lequad 0x8169666f72706cff LLVM indexed profile data,
+>&0 byte x version %d
+
+# big endian
+0 bequad 0x8169666f72706cff LLVM indexed profile data,
+>&7 byte x version %d (big-endian)
+
diff --git a/contrib/libs/libmagic/magic/Magdir/cracklib b/contrib/libs/libmagic/magic/Magdir/cracklib
new file mode 100644
index 0000000000..167659670d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/cracklib
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: cracklib,v 1.7 2009/09/19 16:28:08 christos Exp $
+# cracklib: file (1) magic for cracklib v2.7
+
+0 lelong 0x70775631 Cracklib password index, little endian
+>4 long >0 (%i words)
+>4 long 0 ("64-bit")
+>>8 long >-1 (%i words)
+0 belong 0x70775631 Cracklib password index, big endian
+>4 belong >-1 (%i words)
+# really bellong 0x0000000070775631
+0 search/1 \0\0\0\0pwV1 Cracklib password index, big endian ("64-bit")
+>12 belong >0 (%i words)
diff --git a/contrib/libs/libmagic/magic/Magdir/crypto b/contrib/libs/libmagic/magic/Magdir/crypto
new file mode 100644
index 0000000000..910df8dd49
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/crypto
@@ -0,0 +1,49 @@
+
+#------------------------------------------------------------------------------
+# $File: crypto,v 1.4 2023/07/17 16:41:48 christos Exp $
+# crypto: file(1) magic for crypto formats
+#
+# Bitcoin block files
+0 lelong 0xD9B4BEF9 Bitcoin
+>(4.l+40) lelong 0xD9B4BEF9 reverse block
+>>4 lelong x \b, size %u
+# normal block below
+>0 default x block
+>>4 lelong x \b, size %u
+>>8 lelong&0xE0000000 0x20000000
+>>>8 lelong x \b, BIP9 0x%x
+>>8 lelong&0xE0000000 !0x20000000
+>>>8 lelong x \b, version 0x%x
+>>76 ledate x \b, %s UTC
+# VarInt counter
+>>88 ubyte <0xfd \b, txcount %u
+>>88 ubyte 0xfd
+>>>89 leshort x \b, txcount %u
+>>88 ubyte 0xfe
+>>>89 lelong x \b, txcount %u
+>>88 ubyte 0xff
+>>>89 lequad x \b, txcount %llu
+!:ext dat
+# option to find more blocks in the file
+#>>(4.l+8) indirect x ;
+
+# LevelDB
+-8 lequad 0xdb4775248b80fb57 LevelDB table data
+
+# http://www.tarsnap.com/scrypt.html
+# see scryptenc_setup() in lib/scryptenc/scryptenc.c
+0 string scrypt\0 scrypt encrypted file
+>7 byte x \b, N=2**%d
+>8 belong x \b, r=%d
+>12 belong x \b, p=%d
+
+# https://age-encryption.org/
+# Only the first recipient is printed in detail to prevent repetitive output
+# in extreme cases ("ssh-rsa, ssh-rsa, ssh-rsa, ...").
+0 string age-encryption.org/v1\n age encrypted file
+>25 regex/128 \^[^\040]+ \b, %s recipient
+>>25 string scrypt
+>>>&0 regex/64 [0-9]+\$ (N=2**%s)
+>>&0 search/256 \n->\040 \b, among others
+
+0 string -----BEGIN\040AGE\040ENCRYPTED\040FILE----- age encrypted file, ASCII armored
diff --git a/contrib/libs/libmagic/magic/Magdir/ctags b/contrib/libs/libmagic/magic/Magdir/ctags
new file mode 100644
index 0000000000..f480d32338
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ctags
@@ -0,0 +1,6 @@
+
+# ----------------------------------------------------------------------------
+# $File: ctags,v 1.6 2009/09/19 16:28:08 christos Exp $
+# ctags: file (1) magic for Exuberant Ctags files
+# From: Alexander Mai <mai@migdal.ikp.physik.tu-darmstadt.de>
+0 search/1 =!_TAG Exuberant Ctags tag file text
diff --git a/contrib/libs/libmagic/magic/Magdir/ctf b/contrib/libs/libmagic/magic/Magdir/ctf
new file mode 100644
index 0000000000..d91684d18c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ctf
@@ -0,0 +1,23 @@
+
+#--------------------------------------------------------------
+# ctf: file(1) magic for CTF (Common Trace Format) trace files
+#
+# Specs. available here: <https://www.efficios.com/ctf>
+#--------------------------------------------------------------
+
+# CTF trace data
+0 lelong 0xc1fc1fc1 Common Trace Format (CTF) trace data (LE)
+0 belong 0xc1fc1fc1 Common Trace Format (CTF) trace data (BE)
+
+# CTF metadata (packetized)
+0 lelong 0x75d11d57 Common Trace Format (CTF) packetized metadata (LE)
+>35 byte x \b, v%d
+>36 byte x \b.%d
+0 belong 0x75d11d57 Common Trace Format (CTF) packetized metadata (BE)
+>35 byte x \b, v%d
+>36 byte x \b.%d
+
+# CTF metadata (plain text)
+0 string /*\x20CTF\x20 Common Trace Format (CTF) plain text metadata
+!:strength + 5 # this is to make sure we beat C
+>&0 regex [0-9]+\\.[0-9]+ \b, v%s
diff --git a/contrib/libs/libmagic/magic/Magdir/cubemap b/contrib/libs/libmagic/magic/Magdir/cubemap
new file mode 100644
index 0000000000..e2f87d8542
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/cubemap
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: cubemap,v 1.1 2012/06/06 13:03:20 christos Exp $
+# file(1) magic(5) data for cubemaps Martin Erik Werner <martinerikwerner@gmail.com>
+#
+0 string ACMP Map file for the AssaultCube FPS game
+0 string CUBE Map file for cube and cube2 engine games
+0 string MAPZ) Map file for the Blood Frontier/Red Eclipse FPS games
diff --git a/contrib/libs/libmagic/magic/Magdir/cups b/contrib/libs/libmagic/magic/Magdir/cups
new file mode 100644
index 0000000000..6dd14ac5a5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/cups
@@ -0,0 +1,56 @@
+
+#------------------------------------------------------------------------------
+# $File: cups,v 1.6 2019/04/19 00:42:27 christos Exp $
+# Cups: file(1) magic for the cups raster file format
+# From: Laurent Martelli <martellilaurent@gmail.com>
+# https://www.cups.org/documentation.php/spec-raster.html
+#
+
+0 name cups-le
+>280 lelong x \b, %d
+>284 lelong x \bx%d dpi
+>376 lelong x \b, %dx
+>380 lelong x \b%d pixels
+>388 lelong x %d bits/color
+>392 lelong x %d bits/pixel
+>400 lelong 0 ColorOrder=Chunky
+>400 lelong 1 ColorOrder=Banded
+>400 lelong 2 ColorOrder=Planar
+>404 lelong 0 ColorSpace=gray
+>404 lelong 1 ColorSpace=RGB
+>404 lelong 2 ColorSpace=RGBA
+>404 lelong 3 ColorSpace=black
+>404 lelong 4 ColorSpace=CMY
+>404 lelong 5 ColorSpace=YMC
+>404 lelong 6 ColorSpace=CMYK
+>404 lelong 7 ColorSpace=YMCK
+>404 lelong 8 ColorSpace=KCMY
+>404 lelong 9 ColorSpace=KCMYcm
+>404 lelong 10 ColorSpace=GMCK
+>404 lelong 11 ColorSpace=GMCS
+>404 lelong 12 ColorSpace=WHITE
+>404 lelong 13 ColorSpace=GOLD
+>404 lelong 14 ColorSpace=SILVER
+>404 lelong 15 ColorSpace=CIE XYZ
+>404 lelong 16 ColorSpace=CIE Lab
+>404 lelong 17 ColorSpace=RGBW
+>404 lelong 18 ColorSpace=sGray
+>404 lelong 19 ColorSpace=sRGB
+>404 lelong 20 ColorSpace=AdobeRGB
+
+# Cups Raster image format, Big Endian
+0 string RaS
+>3 string t Cups Raster version 1, Big Endian
+>3 string 2 Cups Raster version 2, Big Endian
+>3 string 3 Cups Raster version 3, Big Endian
+!:mime application/vnd.cups-raster
+>0 use \^cups-le
+
+
+# Cups Raster image format, Little Endian
+1 string SaR
+>0 string t Cups Raster version 1, Little Endian
+>0 string 2 Cups Raster version 2, Little Endian
+>0 string 3 Cups Raster version 3, Little Endian
+!:mime application/vnd.cups-raster
+>0 use cups-le
diff --git a/contrib/libs/libmagic/magic/Magdir/dact b/contrib/libs/libmagic/magic/Magdir/dact
new file mode 100644
index 0000000000..04627c9703
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/dact
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# $File: dact,v 1.4 2009/09/19 16:28:08 christos Exp $
+# dact: file(1) magic for DACT compressed files
+#
+0 long 0x444354C3 DACT compressed data
+>4 byte >-1 (version %i.
+>5 byte >-1 $BS%i.
+>6 byte >-1 $BS%i)
+>7 long >0 $BS, original size: %i bytes
+>15 long >30 $BS, block size: %i bytes
diff --git a/contrib/libs/libmagic/magic/Magdir/database b/contrib/libs/libmagic/magic/Magdir/database
new file mode 100644
index 0000000000..03ac4235f7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/database
@@ -0,0 +1,886 @@
+
+#------------------------------------------------------------------------------
+# $File: database,v 1.69 2023/01/12 00:14:04 christos Exp $
+# database: file(1) magic for various databases
+#
+# extracted from header/code files by Graeme Wilford (eep2gw@ee.surrey.ac.uk)
+#
+#
+# GDBM magic numbers
+# Will be maintained as part of the GDBM distribution in the future.
+# <downsj@teeny.org>
+0 belong 0x13579acd GNU dbm 1.x or ndbm database, big endian, 32-bit
+!:mime application/x-gdbm
+0 belong 0x13579ace GNU dbm 1.x or ndbm database, big endian, old
+!:mime application/x-gdbm
+0 belong 0x13579acf GNU dbm 1.x or ndbm database, big endian, 64-bit
+!:mime application/x-gdbm
+0 lelong 0x13579acd GNU dbm 1.x or ndbm database, little endian, 32-bit
+!:mime application/x-gdbm
+0 lelong 0x13579ace GNU dbm 1.x or ndbm database, little endian, old
+!:mime application/x-gdbm
+0 lelong 0x13579acf GNU dbm 1.x or ndbm database, little endian, 64-bit
+!:mime application/x-gdbm
+0 string GDBM GNU dbm 2.x database
+!:mime application/x-gdbm
+#
+# Berkeley DB
+#
+# Ian Darwin's file /etc/magic files: big/little-endian version.
+#
+# Hash 1.85/1.86 databases store metadata in network byte order.
+# Btree 1.85/1.86 databases store the metadata in host byte order.
+# Hash and Btree 2.X and later databases store the metadata in host byte order.
+
+0 long 0x00061561 Berkeley DB
+!:mime application/x-dbm
+>8 belong 4321
+>>4 belong >2 1.86
+>>4 belong <3 1.85
+>>4 belong >0 (Hash, version %d, native byte-order)
+>8 belong 1234
+>>4 belong >2 1.86
+>>4 belong <3 1.85
+>>4 belong >0 (Hash, version %d, little-endian)
+
+0 belong 0x00061561 Berkeley DB
+>8 belong 4321
+>>4 belong >2 1.86
+>>4 belong <3 1.85
+>>4 belong >0 (Hash, version %d, big-endian)
+>8 belong 1234
+>>4 belong >2 1.86
+>>4 belong <3 1.85
+>>4 belong >0 (Hash, version %d, native byte-order)
+
+0 long 0x00053162 Berkeley DB 1.85/1.86
+>4 long >0 (Btree, version %d, native byte-order)
+0 belong 0x00053162 Berkeley DB 1.85/1.86
+>4 belong >0 (Btree, version %d, big-endian)
+0 lelong 0x00053162 Berkeley DB 1.85/1.86
+>4 lelong >0 (Btree, version %d, little-endian)
+
+12 long 0x00061561 Berkeley DB
+>16 long >0 (Hash, version %d, native byte-order)
+12 belong 0x00061561 Berkeley DB
+>16 belong >0 (Hash, version %d, big-endian)
+12 lelong 0x00061561 Berkeley DB
+>16 lelong >0 (Hash, version %d, little-endian)
+
+12 long 0x00053162 Berkeley DB
+>16 long >0 (Btree, version %d, native byte-order)
+12 belong 0x00053162 Berkeley DB
+>16 belong >0 (Btree, version %d, big-endian)
+12 lelong 0x00053162 Berkeley DB
+>16 lelong >0 (Btree, version %d, little-endian)
+
+12 long 0x00042253 Berkeley DB
+>16 long >0 (Queue, version %d, native byte-order)
+12 belong 0x00042253 Berkeley DB
+>16 belong >0 (Queue, version %d, big-endian)
+12 lelong 0x00042253 Berkeley DB
+>16 lelong >0 (Queue, version %d, little-endian)
+
+# From Max Bowsher.
+12 long 0x00040988 Berkeley DB
+>16 long >0 (Log, version %d, native byte-order)
+12 belong 0x00040988 Berkeley DB
+>16 belong >0 (Log, version %d, big-endian)
+12 lelong 0x00040988 Berkeley DB
+>16 lelong >0 (Log, version %d, little-endian)
+
+#
+#
+# Round Robin Database Tool by Tobias Oetiker <oetiker@ee.ethz.ch>
+0 string/b RRD\0 RRDTool DB
+>4 string/b x version %s
+
+>>10 short !0 16bit aligned
+>>>10 bedouble 8.642135e+130 big-endian
+>>>>18 short x 32bit long (m68k)
+
+>>10 short 0
+>>>12 long !0 32bit aligned
+>>>>12 bedouble 8.642135e+130 big-endian
+>>>>>20 long 0 64bit long
+>>>>>20 long !0 32bit long
+>>>>12 ledouble 8.642135e+130 little-endian
+>>>>>24 long 0 64bit long
+>>>>>24 long !0 32bit long (i386)
+>>>>12 string \x43\x2b\x1f\x5b\x2f\x25\xc0\xc7 middle-endian
+>>>>>24 short !0 32bit long (arm)
+
+>>8 quad 0 64bit aligned
+>>>16 bedouble 8.642135e+130 big-endian
+>>>>24 long 0 64bit long (s390x)
+>>>>24 long !0 32bit long (hppa/mips/ppc/s390/SPARC)
+>>>16 ledouble 8.642135e+130 little-endian
+>>>>28 long 0 64bit long (alpha/amd64/ia64)
+>>>>28 long !0 32bit long (armel/mipsel)
+
+#----------------------------------------------------------------------
+# ROOT: file(1) magic for ROOT databases
+#
+0 string root\0 ROOT file
+>4 belong x Version %d
+>33 belong x (Compression: %d)
+
+# XXX: Weak magic.
+# Alex Ott <ott@jet.msk.su>
+## Paradox file formats
+#2 leshort 0x0800 Paradox
+#>0x39 byte 3 v. 3.0
+#>0x39 byte 4 v. 3.5
+#>0x39 byte 9 v. 4.x
+#>0x39 byte 10 v. 5.x
+#>0x39 byte 11 v. 5.x
+#>0x39 byte 12 v. 7.x
+#>>0x04 byte 0 indexed .DB data file
+#>>0x04 byte 1 primary index .PX file
+#>>0x04 byte 2 non-indexed .DB data file
+#>>0x04 byte 3 non-incrementing secondary index .Xnn file
+#>>0x04 byte 4 secondary index .Ynn file
+#>>0x04 byte 5 incrementing secondary index .Xnn file
+#>>0x04 byte 6 non-incrementing secondary index .XGn file
+#>>0x04 byte 7 secondary index .YGn file
+#>>>0x04 byte 8 incrementing secondary index .XGn file
+
+## XBase database files
+# updated by Joerg Jenderek at Feb 2013
+# https://www.dbase.com/Knowledgebase/INT/db7_file_fmt.htm
+# https://www.clicketyclick.dk/databases/xbase/format/dbf.html
+# inspect VVYYMMDD , where 1<= MM <= 12 and 1<= DD <= 31
+0 ubelong&0x0000FFFF <0x00000C20
+!:strength +10
+# skip Infocom game Z-machine
+>2 ubyte >0
+# skip Androids *.xml
+>>3 ubyte >0
+>>>3 ubyte <32
+# 1 < version VV
+>>>>0 ubyte >1
+# skip HELP.CA3 by test for reserved byte ( NULL )
+>>>>>27 ubyte 0
+# reserved bytes not always 0 ; also found 0x3901 (T4.DBF) ,0x7101 (T5.DBF,T6.DBF)
+#>>>>>30 ubeshort x 30NULL?%x
+# possible production flag,tag numbers(<=0x30),tag length(<=0x20), reserved (NULL)
+>>>>>>24 ubelong&0xffFFFFff >0x01302000
+# .DBF or .MDX
+>>>>>>24 ubelong&0xffFFFFff <0x01302001
+# for Xbase Database file (*.DBF) reserved (NULL) for multi-user
+>>>>>>>24 ubelong&0xffFFFFff =0
+# test for 2 reserved NULL bytes,transaction and encryption byte flag
+>>>>>>>>12 ubelong&0xFFFFfEfE 0
+# test for MDX flag
+>>>>>>>>>28 ubyte x
+>>>>>>>>>28 ubyte&0xf8 0
+# header size >= 32
+>>>>>>>>>>8 uleshort >31
+# skip PIC15736.PCX by test for language driver name or field name
+>>>>>>>>>>>32 ubyte >0
+#!:mime application/x-dbf; charset=unknown-8bit ??
+#!:mime application/x-dbase
+>>>>>>>>>>>>0 use xbase-type
+# database file
+>>>>>>>>>>>>28 ubyte&0x04 =0 \b DBF
+!:ext dbf
+>>>>>>>>>>>>28 ubyte&0x04 =4 \b DataBaseContainer
+!:ext dbc
+>>>>>>>>>>>>4 lelong 0 \b, no records
+>>>>>>>>>>>>4 lelong >0 \b, %d record
+# plural s appended
+>>>>>>>>>>>>>4 lelong >1 \bs
+# https://www.clicketyclick.dk/databases/xbase/format/dbf_check.html#CHECK_DBF
+# 1 <= record size <= 4000 (dBase 3,4) or 32 * KB (=0x8000)
+>>>>>>>>>>>>10 uleshort x * %d
+# file size = records * record size + header size
+>>>>>>>>>>>>1 ubyte x \b, update-date
+>>>>>>>>>>>>1 use xbase-date
+# https://msdn.microsoft.com/de-de/library/cc483186(v=vs.71).aspx
+#>>>>>>>>>>>>29 ubyte =0 \b, codepage ID=%#x
+# 2~cp850 , 3~cp1252 , 0x1b~?? ; what code page is 0x1b ?
+>>>>>>>>>>>>29 ubyte >0 \b, codepage ID=%#x
+#>>>>>>>>>>>>28 ubyte&0x01 0 \b, no index file
+# MDX or CDX index
+>>>>>>>>>>>>28 ubyte&0x01 1 \b, with index file .MDX
+>>>>>>>>>>>>28 ubyte&0x02 2 \b, with memo .FPT
+#>>>>>>>>>>>>28 ubyte&0x04 4 \b, DataBaseContainer
+# 1st record offset + 1 = header size
+>>>>>>>>>>>>8 uleshort >0
+>>>>>>>>>>>>(8.s+1) ubyte >0
+>>>>>>>>>>>>>8 uleshort >0 \b, at offset %d
+>>>>>>>>>>>>>(8.s+1) ubyte >0
+>>>>>>>>>>>>>>&-1 string >\0 1st record "%s"
+# for multiple index files (*.MDX) Production flag,tag numbers(<=0x30),tag length(<=0x20), reserved (NULL)
+>>>>>>>24 ubelong&0x0133f7ff >0
+# test for reserved NULL byte
+>>>>>>>>47 ubyte 0
+# test for valid TAG key format (0x10 or 0)
+>>>>>>>>>559 ubyte&0xeF 0
+# test MM <= 12
+>>>>>>>>>>45 ubeshort <0x0C20
+>>>>>>>>>>>45 ubyte >0
+>>>>>>>>>>>>46 ubyte <32
+>>>>>>>>>>>>>46 ubyte >0
+#!:mime application/x-mdx
+>>>>>>>>>>>>>>0 use xbase-type
+>>>>>>>>>>>>>>0 ubyte x \b MDX
+>>>>>>>>>>>>>>1 ubyte x \b, creation-date
+>>>>>>>>>>>>>>1 use xbase-date
+>>>>>>>>>>>>>>44 ubyte x \b, update-date
+>>>>>>>>>>>>>>44 use xbase-date
+# No.of tags in use (1,2,5,12)
+>>>>>>>>>>>>>>28 uleshort x \b, %d
+# No. of entries in tag (0x30)
+>>>>>>>>>>>>>>25 ubyte x \b/%d tags
+# Length of tag
+>>>>>>>>>>>>>>26 ubyte x * %d
+# 1st tag name_
+>>>>>>>>>>>>>548 string x \b, 1st tag "%.11s"
+# 2nd tag name
+#>>>>>>>>>>>>(26.b+548) string x \b, 2nd tag "%.11s"
+#
+# Print the xBase names of different version variants
+0 name xbase-type
+>0 ubyte <2
+# 1 < version
+>0 ubyte >1
+>>0 ubyte 0x02 FoxBase
+!:mime application/x-dbf
+# like: ACCESS.DBF USER.DBF dbase3date.dbf mitarbei.dbf produkte.dbf umlaut-test-v2.dbf
+# FoxBase+/dBaseIII+, no memo
+>>0 ubyte 0x03 FoxBase+/dBase III
+!:mime application/x-dbf
+# like: 92DATA.DBF MSCATLOG.DBF SYLLABI2.DBF SYLLABUS.DBF T4.DBF Teleadr.dbf us_city.dbf
+# dBASE IV no memo file
+>>0 ubyte 0x04 dBase IV
+!:mime application/x-dbf
+# like: Quattro-test11.dbf umlaut-test-v4.dbf
+# dBASE V no memo file
+>>0 ubyte 0x05 dBase V
+!:mime application/x-dbf
+# like: dbase4double.dbf Quattro-test2.dbf umlaut-test7.dbf
+!:ext dbf
+# probably Apollo Database Server 9.7? xBase (0x6)
+>>0 ubyte 0x06 Apollo
+!:mime application/x-dbf
+# like: ALIAS.DBF CRYPT.DBF PROCS.DBF USERS.DBF
+# https://docs.microsoft.com/en-us/previous-versions/visualstudio/foxpro/st4a0s68(v=vs.80)
+>>0 ubyte 0x2F FoxBase+/Dbase III plus, no memo
+!:mime application/x-dbf
+# no example
+>>0 ubyte 0x30 Visual FoxPro
+!:mime application/x-dbf
+# like: 26FRX.DBF 30DBC.DBF 30DBCPRO.DBF BEHINDSC.DBF USER_LEV.DBF
+# Microsoft Visual FoxPro Database Container File like: FOXPRO-DB-TEST.DBC TESTDATA.DBC TASTRADE.DBC
+>>0 ubyte 0x31 Visual FoxPro, autoincrement
+!:mime application/x-dbf
+# like: AI_Table.DBF dbase_31.dbf w_cityFoxpro.dbf
+# Visual FoxPro, with field type Varchar or Varbinary
+>>0 ubyte 0x32 Visual FoxPro, with field type Varchar
+!:mime application/x-dbf
+# like: dbase_32.dbf
+# dBASE IV SQL, no memo;dbv memo var size (Flagship)
+>>0 ubyte 0x43 dBase IV, with SQL table
+!:mime application/x-dbf
+# like: ASSEMBLY.DBF INVENTRY.DBF STAFF.DBF
+# https://docs.microsoft.com/en-us/previous-versions/visualstudio/foxpro/st4a0s68(v=vs.80)
+>>0 ubyte 0x62 dBase IV, with SQL table
+#!:mime application/x-dbf
+# no example
+# dBASE IV, with memo!!
+>>0 ubyte 0x7b dBase IV, with memo
+!:mime application/x-dbf
+# like: test3memo.DBF dbase5.DBF
+# https://docs.microsoft.com/en-us/previous-versions/visualstudio/foxpro/st4a0s68(v=vs.80)
+>>0 ubyte 0x82 dBase IV, with SQL system
+#!:mime application/x-dbf
+# no example
+# FoxBase+/dBaseIII+ with memo .DBT!
+>>0 ubyte 0x83 FoxBase+/dBase III, with memo .DBT
+!:mime application/x-dbf
+# like: T2.DBF t3.DBF biblio.dbf dbase_83.dbf dbase3dbt0_4.dbf fsadress.dbf stop.dbf
+# VISUAL OBJECTS (first 1.0 versions) for the Dbase III files (NTX clipper driver); memo file
+>>0 ubyte 0x87 VISUAL OBJECTS, with memo file
+!:mime application/x-dbf
+# like: ACCESS.DBF dbase3date.dbf dbase3float.dbf holdings.dbf mitarbei.dbf
+# https://docs.microsoft.com/en-us/previous-versions/visualstudio/foxpro/st4a0s68(v=vs.80)
+>>0 ubyte 0x8A FoxBase+/dBase III, with memo .DBT
+#!:mime application/x-dbf
+# no example
+# dBASE IV with memo!
+>>0 ubyte 0x8B dBase IV, with memo .DBT
+!:mime application/x-dbf
+# like: animals.dbf archive.dbf callin.dbf dbase_8b.dbf phnebook.dbf t6.dbf
+# dBase IV with SQL Table,no memo?
+>>0 ubyte 0x8E dBase IV, with SQL table
+!:mime application/x-dbf
+# like: dbase5.DBF test3memo.DBF test-memo.DBF
+# .dbv and .dbt memo (Flagship)?
+>>0 ubyte 0xB3 Flagship
+!:mime application/x-dbf
+# no example
+# https://docs.microsoft.com/en-us/previous-versions/visualstudio/foxpro/st4a0s68(v=vs.80)
+>>0 ubyte 0xCA dBase IV with memo .DBT
+#!:mime application/x-dbf
+# no example
+# dBASE IV with SQL table, with memo .DBT
+>>0 ubyte 0xCB dBase IV with SQL table, with memo .DBT
+!:mime application/x-dbf
+# like: dbase5.DBF test3memo.DBF test-memo.DBF
+# HiPer-Six format;Clipper SIX, with SMT memo file
+>>0 ubyte 0xE5 Clipper SIX with memo
+!:mime application/x-dbf
+# like: dbase5.DBF test3memo.DBF test-memo.DBF testClipper.dbf DATA.DBF
+# https://docs.microsoft.com/en-us/previous-versions/visualstudio/foxpro/st4a0s68(v=vs.80)
+>>0 ubyte 0xF4 dBase IV, with SQL table, with memo
+#!:mime application/x-dbf
+# no example
+>>0 ubyte 0xF5 FoxPro with memo
+!:mime application/x-dbf
+# like: CUSTOMER.DBF FOXUSER1.DBF Invoice.DBF NG.DBF OBJSAMP.DBF dbase_f5.dbf kunde.dbf
+# probably Apollo Database Server 9.7 with SQL and memo mask? xBase (0xF6)
+>>0 ubyte 0xF6 Apollo, with SQL table with memo
+!:mime application/x-dbf
+# like: SCRIPTS.DBF
+# https://docs.microsoft.com/en-us/previous-versions/visualstudio/foxpro/st4a0s68(v=vs.80)
+#>>0 ubyte 0xFA FoxPro 2.x, with memo
+#!:mime application/x-dbf
+# no example
+# unknown version (should not happen)
+>>0 default x xBase
+!:mime application/x-dbf
+>>>0 ubyte x (%#x)
+# flags in version byte
+# DBT flag (with dBASE III memo .DBT)!!
+# >>0 ubyte&0x80 >0 DBT_FLAG=%x
+# memo flag ??
+# >>0 ubyte&0x08 >0 MEMO_FLAG=%x
+# SQL flag ??
+# >>0 ubyte&0x70 >0 SQL_FLAG=%x
+# test and print the date of xBase .DBF .MDX
+0 name xbase-date
+# inspect YYMMDD , where 1<= MM <= 12 and 1<= DD <= 31
+>0 ubelong x
+>1 ubyte <13
+>>1 ubyte >0
+>>>2 ubyte >0
+>>>>2 ubyte <32
+>>>>>0 ubyte x
+# YY is interpreted as 20YY or 19YY
+>>>>>>0 ubyte <100 \b %.2d
+# YY is interpreted 1900+YY; TODO: display yy or 20yy instead 1YY
+>>>>>>0 ubyte >99 \b %d
+>>>>>1 ubyte x \b-%d
+>>>>>2 ubyte x \b-%d
+
+# dBase memo files .DBT or .FPT
+# https://msdn.microsoft.com/en-us/library/8599s21w(v=vs.80).aspx
+16 ubyte <4
+>16 ubyte !2
+>>16 ubyte !1
+# next free block index is positive
+>>>0 ulelong >0
+# skip many JPG. ZIP, BZ2 by test for reserved bytes NULL , 0|2 , 0|1 , low byte of block size
+>>>>17 ubelong&0xFFfdFEff 0x00000000
+# skip many RAR by test for low byte 0 ,high byte 0|2|even of block size, 0|a|e|d7 , 0|64h
+>>>>>20 ubelong&0xFF01209B 0x00000000
+# dBASE III
+>>>>>>16 ubyte 3
+# skip with invalid "low" 1st item "\0\0\0\0" StateRepository-Deployment.srd-shm "\001\010\0\0" gcry_cast5.mod
+>>>>>>>512 ubyte >040
+# skip with valid 1st item "rintf" keylayouts.mod
+# by looking for valid terminating character Ctrl-Z like in test.dbt
+>>>>>>>>513 search/3308 \032
+# skip GRUB plan9.mod with invalid second terminating character 007
+# by checking second terminating character Ctrl-Z like in test.dbt
+>>>>>>>>>&0 ubyte 032
+# dBASE III DBT with two Ctr-Z terminating characters
+>>>>>>>>>>0 use dbase3-memo-print
+# second terminating character \0 like in dbase-memo.dbt or GRUB nativedisk.mod
+>>>>>>>>>&0 ubyte 0
+# skip GRUB nativedisk.mod with grub_mod_init\0grub_mod_fini\0grub_fs_autoload_hook\0
+>>>>>>>>>>0x1ad string !grub_mod_init
+# like dbase-memo.dbt
+>>>>>>>>>>>0 use dbase3-memo-print
+# dBASE III DBT without version, dBASE IV DBT , FoxPro FPT , or many ZIP , DBF garbage
+>>>>>>16 ubyte 0
+# unusual dBASE III DBT like angest.dbt, dBASE IV DBT with block size 0 , FoxPro FPT , or garbage PCX DBF
+>>>>>>>20 uleshort 0
+# FoxPro FPT , unusual dBASE III DBT like biblio.dbt or garbage
+>>>>>>>>8 ulong =0
+>>>>>>>>>6 ubeshort >0
+# skip emacs.PIF
+>>>>>>>>>>4 ushort 0
+# check for valid FoxPro field type
+>>>>>>>>>>>512 ubelong <3
+# skip LXMDCLN4.OUT LXMDCLN6.OUT LXMDALG6.OUT with invalid blocksize 170=AAh
+>>>>>>>>>>>>6 ubeshort&0x002f 0
+>>>>>>>>>>>>>0 use foxpro-memo-print
+# dBASE III DBT , garbage
+# skip WORD1XW.DOC with improbably high free block index
+>>>>>>>>>0 ulelong <0x400000
+# skip WinStore.App.exe by looking for printable 2nd character of 1st memo item
+>>>>>>>>>>513 ubyte >037
+# skip DOS executables CPQ0TD.DRV E30ODI.COM IBM0MONO.DRV by looking for printable 1st character of 1st memo item
+>>>>>>>>>>>512 ubyte >037
+# skip few (14/758) Microsoft Event Trace Logs (boot_BASE+CSWITCH_1.etl DlTel-Merge.etl UpdateUx.006.etl) with invalid "high" 1st item \377\377
+>>>>>>>>>>>>512 ubyte <0377
+# skip some Commodore 64 Art Studio (Deep_Strike.aas dragon's_lair_ii.aas), some Atari DEGAS Elite bitmap (ELEPHANT.PC3 ST.PC2)
+# some probably old GRUB modules (part_sun.mod) and virtual-boy-wario-land.vb.
+# by looking for valid terminating character Ctrl-Z
+>>>>>>>>>>>>>513 search/523 \032
+# Atari DEGAS bitmap ST.PC2 with 0370 as second terminating character
+#>>>>>>>>>>>>>>&0 ubyte x 2ND_CHAR_IS=%o
+# dBASE III DBT with two Ctr-Z terminating characters like dbase3dbt0_1.dbt dbase_83.dbt
+>>>>>>>>>>>>>>&0 ubyte 032
+>>>>>>>>>>>>>>>0 use dbase3-memo-print
+# second terminating character \0 like in pcidump.mod or fsadress.dbt umlaut-dbf-cmd.dbt
+>>>>>>>>>>>>>>&0 ubyte 0
+# look for old GRUB module pcidump.mod with specific content "pcidump\0Show raw dump of the PCI configuration space"
+>>>>>>>>>>>>>>>514 search/0x11E pcidump\0Show
+# dBASE III DBT with Ctr-Z + \0 terminating characters like fsadress.dbt
+>>>>>>>>>>>>>>>514 default x
+# unusual dBASE III DBT like fsadress.dbt umlaut-dbf-cmd.dbt
+>>>>>>>>>>>>>>>>0 use dbase3-memo-print
+# dBASE III DBT like angest.dbt, or garbage PCX DBF
+>>>>>>>>8 ubelong !0
+# skip PCX and some DBF by test for for reserved NULL bytes
+>>>>>>>>>510 ubeshort 0
+# skip bad symples with improbably high free block index above 2 GiB file limit
+>>>>>>>>>>0 ulelong <0x400000
+# skip AI070GEP.EPS by printable 1st character of 1st memo item
+>>>>>>>>>>>512 ubyte >037
+# skip some Microsoft Visual C, OMF library like: BZ2.LIB WATTCPWL.LIB ZLIB.LIB
+>>>>>>>>>>>>512 ubyte <0200
+# skip gluon-ffhat-1.0-tp-link-tl-wr1043n-nd-v2-sysupgrade.bin by printable 2nd character
+>>>>>>>>>>>>>513 ubyte >037
+# skip few (8/758) Microsoft Event Trace Logs (WBEngine.3.etl Wifi.etl) with valid 1st item like
+# "9600.20369.amd64fre.winblue_ltsb_escrow.220427-1727"
+# "9600.19846.amd64fre.winblue_ltsb_escrow.200923-1735"
+# "10586.494.amd64fre.th2_release_sec.160630-1736"
+# by looking for valid terminating character Ctrl-Z
+>>>>>>>>>>>>>>513 search/0x11E \032
+# followed by second character Ctrl-Z implies typical DBT
+>>>>>>>>>>>>>>>&0 ubyte 032
+# examples like: angest.dbt
+>>>>>>>>>>>>>>>>0 use dbase3-memo-print
+>>>>>>>>>>>>>>>&0 ubyte 0
+# no example found here with terminating sequence CTRL-Z + \0
+>>>>>>>>>>>>>>>>0 use dbase3-memo-print
+# dBASE IV DBT with positive block size
+>>>>>>>20 uleshort >0
+# dBASE IV DBT with valid block length like 512, 1024
+# multiple of 2 in between 16 and 16 K ,implies upper and lower bits are zero
+# skip also 3600h 3E00h size
+>>>>>>>>20 uleshort&0xE00f 0
+>>>>>>>>>0 use dbase4-memo-print
+
+# Print the information of dBase III DBT memo file
+0 name dbase3-memo-print
+>0 ubyte x dBase III DBT
+!:mime application/x-dbt
+!:ext dbt
+# instead 3 as version number 0 for unusual examples like biblio.dbt
+>16 ubyte !3 \b, version number %u
+# Number of next available block for appending data
+#>0 lelong =0 \b, next free block index %u
+>0 lelong !0 \b, next free block index %u
+# no positive block length
+#>20 uleshort =0 \b, block length %u
+>20 uleshort !0 \b, block length %u
+# dBase III memo field terminated often by \032\032
+# like: "WHAT IS XBASE" test.dbt "Borges, Malte" biblio.dbt "First memo\032\032" T2.DBT
+>512 string >\0 \b, 1st item "%s"
+# For DEBUGGING
+#>512 ubelong x \b, 1ST item %#8.8x
+#>513 search/0x225 \032 FOUND_TERMINATOR
+#>>&0 ubyte 032 2xCTRL_Z
+# fsadress.dbt has 1 Ctrl-Z terminator followed by nil byte
+#>>&0 ubyte 0 1xCTRL_Z
+
+# https://www.clicketyclick.dk/databases/xbase/format/dbt.html
+# Print the information of dBase IV DBT memo file
+0 name dbase4-memo-print
+>0 lelong x dBase IV DBT
+!:mime application/x-dbt
+!:ext dbt
+# 8 character shorted main name of corresponding dBASE IV DBF file
+>8 ubelong >0x20000000
+# skip unusual like for angest.dbt
+>>20 uleshort >0
+>>>8 string >\0 \b of %-.8s.DBF
+# value 0 implies 512 as size
+#>4 ulelong =0 \b, blocks size %u
+# size of blocks not reliable like 0x2020204C in angest.dbt
+>4 ulelong !0
+>>4 ulelong&0x0000003f 0 \b, blocks size %u
+# dBase IV DBT with positive block length (found 512 , 1024)
+>20 uleshort >0 \b, block length %u
+# next available block
+#>0 lelong =0 \b, next free block index %u
+>0 lelong !0 \b, next free block index %u
+>20 uleshort >0
+>>(20.s) ubelong x
+>>>&-4 use dbase4-memofield-print
+# unusual dBase IV DBT without block length (implies 512 as length)
+>20 uleshort =0
+>>512 ubelong x
+>>>&-4 use dbase4-memofield-print
+# Print the information of dBase IV memo field
+0 name dbase4-memofield-print
+# free dBase IV memo field
+>0 ubelong !0xFFFF0800
+>>0 lelong x \b, next free block %u
+>>4 lelong x \b, next used block %u
+# used dBase IV memo field
+>0 ubelong =0xFFFF0800
+# length of memo field
+>>4 lelong x \b, field length %d
+>>>8 string >\0 \b, 1st used item "%s"
+# http://www.dbfree.org/webdocs/1-documentation/0018-developers_stuff_(advanced)/os_related_stuff/xbase_file_format.htm
+# Print the information of FoxPro FPT memo file
+0 name foxpro-memo-print
+>0 belong x FoxPro FPT
+!:mime application/x-fpt
+!:ext fpt
+# Size of blocks for FoxPro ( 64,256 ); probably a multiple of two
+>6 ubeshort x \b, blocks size %u
+# next available block
+#>0 belong =0 \b, next free block index %u
+>0 belong !0 \b, next free block index %u
+# field type ( 0~picture, 1~memo, 2~object )
+>512 ubelong <3 \b, field type %u
+# length of memo field
+>512 ubelong 1
+>>516 belong >0 \b, field length %d
+>>>520 string >\0 \b, 1st item "%s"
+
+# Summary: DBASE Compound Index file *.CDX and FoxPro index *.IDX
+# From: Joerg Jenderek
+# URL: https://www.clicketyclick.dk/databases/xbase/format/cdx.html
+# https://www.clicketyclick.dk/databases/xbase/format/idx.html
+# https://www.clicketyclick.dk/databases/xbase/format/idx_comp.html
+# Reference: https://mark0.net/download/triddefs_xml.7z/defs/s/sybase-ianywhere-cdx.trid.xml
+# https://mark0.net/download/triddefs_xml.7z/defs/c/cdx-vfp7.trid.xml
+# like: kunde.cdx
+0 ulelong 0x1C00
+>0 use xbase-index
+# like: SYLLABI2.CDX SYLLABUS.CDX
+0 ulelong 0x0800
+>0 use xbase-index
+# often in xBase index pointer to root node 400h
+0 ulelong 0x0400
+# skip most Maple help database *.hdb with version tag handled by ./maple
+>1028 string !version
+# skip Maple help database hsum.hdb checking for valid reserved area
+>>492 quad =0
+# skip remaining Maple help database *.hdb by checking key length
+#>>>12 uleshort !0x000F KEY_LENGTHVALID
+>>>0 use xbase-index
+# display information about dBase/FoxPro index
+0 name xbase-index
+>0 ulelong x xBase
+!:mime application/x-dbase-index
+>14 ubyte &0x40 compound index
+# DCX for FoxPro database index like: TESTDATA.DCX
+!:ext cdx/dcx
+>14 ubyte ^0x40 index
+# only 1 example like: TEST.IDX
+!:ext idx
+# pointer to root node like: 1C00h 800h often 400h
+>0 ulelong !0x400 \b, root pointer %#x
+# Pointer to free node list: often 0 but -1 if not present
+>4 ulelong !0 \b, free node pointer %#x
+# MAYBE number of pages in file (Foxbase, FoxPro 1.x) or
+# http://www.foxpert.com/foxpro/knowlbits/files/knowlbits_200708_1.HTM
+# Whenever Visual FoxPro updates the index file it increments this reserved field
+# Reserved for internal use like: 02000000h 03000000h 460c0000h 780f0000h 89000000h 9fdc0100h often 0
+>8 ulelong !0 \b, reserved counter %#x
+# length of key like: mostly 000Ah 0028h (TEST.IDX)
+>12 uleshort !0x000A \b, key length %#x
+# index options like: 24h E0h E8h
+# 1~a unique index 8~index has FOR clause 32~compact index format 64~compound index header
+# 16~Bit vector (SoftC) 128~Structure index (FoxPro)
+>14 ubyte x \b, index options (%#x
+>14 ubyte &0x01 \b, unique
+>14 ubyte &0x08 \b, has FOR clause
+>14 ubyte &0x10 \b, bit vector (SoftC)
+>14 ubyte &0x20 \b, compact format
+#>14 ubyte &0x40 \b, compound header
+>14 ubyte &0x80 \b, structure
+>14 ubyte x \b)
+# WHAT EXACTLY IS THAT? index signature like: 0 (sybase-ianywhere-cdx.trid.xml) 1 (cdx-vfp7.trid.xml)
+>15 ubyte !0 \b, index signature %u
+# reserved area (0-bytes) til about 500, but not for uncompressed Index files *.idx
+>16 quad !0 \b, at 16 reserved %#llx
+>492 quad !0 \b, at 492 reserved %#llx
+# for IDX variant
+#>14 ubyte ^0x40 IDX
+# for CDX variant
+>14 ubyte &0x40
+# Ascending or descending: 0~ascending 1~descending
+>>502 uleshort x \b, sort order %u
+# Total expression length (FoxPro 2) like: 0 1
+>>504 uleshort !0 \b, expression length %u
+# FOR expression pool length like: 1
+>>506 uleshort !1 \b, FOR expression pool length %#x
+# reserved for internal use like: 0
+>>508 uleshort !0 \b, at 0x508 reserved %#x
+# Key expression pool length like: 1
+>>510 uleshort !1 \b, key expression pool length %#x
+# 512 - 1023 Key & FOR expression pool (uncompiled)
+>>512 quad !0 \b, key expression pool %#llx
+#>>520 quad !0 \b, key expression pool %#llx
+
+# Summary: dBASE IV Printer Form *.PRF
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/.dbf#Other_file_types_found_in_dBASE
+# Reference: https://mark0.net/download/triddefs_xml.7z/defs/p/prf-dbase.trid.xml
+0 ubeshort 0x0400
+# skip some Xbase Index files *.ndx and Infocom (Z-machine 4) *.z4 handled by ./adventure
+# by looking for valid printer driver name extension
+>0x58 search/8 .PR2
+>>0 use xbase-prf
+# display information of dbase print form like printer driver *.PR2
+0 name xbase-prf dBase Printer Form
+!:mime application/x-dbase-prf
+!:ext prf
+# MAYBE version? like: 4~DBASE IV
+#>0 ubyte x \b, version %u
+# MAYBE flag like: 1~with output file name 0~not
+#>2 ubyte !0 \b, flag %u
+# optional printer text output file name like E:\DBASE\IV\T6.txt
+>3 string >\0 \b, output file %s
+# probably padding with nils til 0x53
+#>0x48 uquad !0 \b, at 0x48 padding %#llx
+# dBASE IV printer driver name like: Generic.PR2 ASCII.PR2
+>0x56 string >\0 \b, using printer driver %s
+# 2 is probably last character of previous dBASE printer driver name
+#>0x60 ubyte !0x32 \b, at 0x60 %#x
+# probably padding with nils til 0xa8
+#>0x61 uquad !0 \b, at 0x61 padding %#llx
+# unknown 0x03020300 0x03020100 at 0xa8
+>0xa8 ubelong x \b, at 0xa8 unknown %#8.8x
+# probably padding with nils til 0x2aa
+#>0x2a0 uquad !0 \b, at 0x2a0 padding %#llx
+# unknown 0x100ff7f01000001 at 0x2AB
+>0x2ab ubequad !0x100ff7f01000001 \b, at 0x2ab unknown %#llx
+# unknown 0x0042 at 0x2b3
+>0x2b3 ubeshort !0x0042 \b, at 0x2b3 unknown %#4.4x
+# unknown last 4 bytes at 0x2b6 like: 0 0x23
+>0x2b6 ubelong !0 \b, at 0x2b6 unknown %#8.8x
+
+# TODO:
+# DBASE index file *.NDX
+# dBASE compiled Format *.FMO
+# FoxPro Database memo file *.DCT
+# FoxPro Forms Memo *.SCT
+# FoxPro Generated Menu Program *.MPR
+# FoxPro Report *.FRX
+# FoxPro Report Memo *.FRT
+# Foxpro Generated Screen Program *.SPR
+# Foxpro memo *.PJT
+## End of XBase database stuff
+
+# MS Access database
+4 string Standard\ Jet\ DB Microsoft Access Database
+!:mime application/x-msaccess
+4 string Standard\ ACE\ DB Microsoft Access Database
+!:mime application/x-msaccess
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Extensible_Storage_Engine
+# Reference: https://github.com/libyal/libesedb/archive/master.zip
+# libesedb-master/documentation/
+# Extensible Storage Engine (ESE) Database File (EDB) format.asciidoc
+# Note: also known as "JET Blue". Used by numerous Windows components such as
+# Windows Search, Mail, Exchange and Active Directory.
+4 ubelong 0xefcdab89
+# unknown1
+>132 ubelong 0 Extensible storage engine
+!:mime application/x-ms-ese
+# file_type 0~database 1~stream
+>>12 ulelong 0 DataBase
+# Security DataBase (sdb)
+!:ext edb/sdb
+>>12 ulelong 1 STreaMing
+!:ext stm
+# format_version 620h
+>>8 uleshort x \b, version %#x
+>>10 uleshort >0 revision %#4.4x
+>>0 ubelong x \b, checksum %#8.8x
+# Page size 4096 8192 32768
+>>236 ulequad x \b, page size %lld
+# database_state
+>>52 ulelong 1 \b, JustCreated
+>>52 ulelong 2 \b, DirtyShutdown
+#>>52 ulelong 3 \b, CleanShutdown
+>>52 ulelong 4 \b, BeingConverted
+>>52 ulelong 5 \b, ForceDetach
+# Windows NT major version when the databases indexes were updated.
+>>216 ulelong x \b, Windows version %d
+# Windows NT minor version
+>>220 ulelong x \b.%d
+
+# From: Joerg Jenderek
+# URL: https://forensicswiki.org/wiki/Windows_Application_Compatibility
+# Note: files contain application compatibility fixes, application compatibility modes and application help messages.
+8 string sdbf
+>7 ubyte 0
+# TAG_TYPE_LIST+TAG_INDEXES
+>>12 uleshort 0x7802 Windows application compatibility Shim DataBase
+# version? 2 3
+#>>>0 ulelong x \b, version %d
+!:mime application/x-ms-sdb
+!:ext sdb
+
+# TDB database from Samba et al - Martin Pool <mbp@samba.org>
+0 string TDB\ file TDB database
+>32 lelong 0x2601196D version 6, little-endian
+>>36 lelong x hash size %d bytes
+
+# SE Linux policy database
+0 lelong 0xf97cff8c SE Linux policy
+>16 lelong x v%d
+>20 lelong 1 MLS
+>24 lelong x %d symbols
+>28 lelong x %d ocons
+
+# ICE authority file data (Wolfram Kleff)
+2 string ICE ICE authority data
+
+# X11 Xauthority file (Wolfram Kleff)
+10 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+11 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+12 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+13 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+14 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+15 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+16 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+17 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+18 string MIT-MAGIC-COOKIE-1 X11 Xauthority data
+
+# From: Maxime Henrion <mux@FreeBSD.org>
+# PostgreSQL's custom dump format, Maxime Henrion <mux@FreeBSD.org>
+0 string PGDMP PostgreSQL custom database dump
+>5 byte x - v%d
+>6 byte x \b.%d
+>5 beshort <0x101 \b-0
+>5 beshort >0x100
+>>7 byte x \b-%d
+
+# Type: Advanced Data Format (ADF) database
+# URL: https://www.grc.nasa.gov/WWW/cgns/adf/
+# From: Nicolas Chauvat <nicolas.chauvat@logilab.fr>
+0 string @(#)ADF\ Database CGNS Advanced Data Format
+
+# Tokyo Cabinet magic data
+# http://tokyocabinet.sourceforge.net/index.html
+0 string ToKyO\ CaBiNeT\n Tokyo Cabinet
+>14 string x \b (%s)
+>32 byte 0 \b, Hash
+!:mime application/x-tokyocabinet-hash
+>32 byte 1 \b, B+ tree
+!:mime application/x-tokyocabinet-btree
+>32 byte 2 \b, Fixed-length
+!:mime application/x-tokyocabinet-fixed
+>32 byte 3 \b, Table
+!:mime application/x-tokyocabinet-table
+>33 byte &1 \b, [open]
+>33 byte &2 \b, [fatal]
+>34 byte x \b, apow=%d
+>35 byte x \b, fpow=%d
+>36 byte &0x01 \b, [large]
+>36 byte &0x02 \b, [deflate]
+>36 byte &0x04 \b, [bzip]
+>36 byte &0x08 \b, [tcbs]
+>36 byte &0x10 \b, [excodec]
+>40 lequad x \b, bnum=%lld
+>48 lequad x \b, rnum=%lld
+>56 lequad x \b, fsiz=%lld
+
+# Type: QDBM Quick Database Manager
+# From: Benoit Sibaud <bsibaud@april.org>
+0 string \\[depot\\]\n\f Quick Database Manager, little endian
+0 string \\[DEPOT\\]\n\f Quick Database Manager, big endian
+
+# Type: TokyoCabinet database
+# URL: http://tokyocabinet.sourceforge.net/
+# From: Benoit Sibaud <bsibaud@april.org>
+0 string ToKyO\ CaBiNeT\n TokyoCabinet database
+>14 string x (version %s)
+
+# From: Stephane Blondon https://www.yaal.fr
+# Database file for Zope (done by FileStorage)
+0 string FS21 Zope Object Database File Storage v3 (data)
+0 string FS30 Zope Object Database File Storage v4 (data)
+
+# Cache file for the database of Zope (done by ClientStorage)
+0 string ZEC3 Zope Object Database Client Cache File (data)
+
+# IDA (Interactive Disassembler) database
+0 string IDA1 IDA (Interactive Disassembler) database
+
+# Hopper (reverse engineering tool) https://www.hopperapp.com/
+0 string hopperdb Hopper database
+
+# URL: https://en.wikipedia.org/wiki/Panorama_(database_engine)
+# Reference: http://www.provue.com/Panorama/
+# From: Joerg Jenderek
+# NOTE: test only versions 4 and 6.0 with Windows
+# length of Panorama database name
+5 ubyte >0
+# look after database name for "some" null bits
+>(5.B+7) ubelong&0xF3ffF000 0
+# look for first keyword
+>>&1 search/2 DESIGN Panorama database
+#!:mime application/x-panorama-database
+!:apple KASXZEPD
+!:ext pan
+# database name
+>>>5 pstring x \b, "%s"
+
+#
+#
+# askSam Database by Stefan A. Haubenthal <polluks@web.de>
+0 string askw40\0 askSam DB
+
+#
+#
+# MUIbase Database Tool by Stefan A. Haubenthal <polluks@web.de>
+0 string MBSTV\040 MUIbase DB
+>6 string x version %s
+
+#
+# CDB database
+0 string NBCDB\012 NetBSD Constant Database
+>7 byte x \b, version %d
+>8 string x \b, for '%s'
+>24 lelong x \b, datasize %d
+>28 lelong x \b, entries %d
+>32 lelong x \b, index %d
+>36 lelong x \b, seed %#x
+
+#
+# Redis RDB - https://redis.io/topics/persistence
+0 string REDIS Redis RDB file,
+>5 regex [0-9][0-9][0-9][0-9] version %s
+
+# Mork database.
+# Used by older versions of Mozilla Suite and Firefox,
+# and current versions of Thunderbird.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+0 string //\ <!--\ <mdb:mork:z\ v=" Mozilla Mork database
+>23 string x \b, version %.3s
+
+# URL: https://en.wikipedia.org/wiki/Management_Information_Format
+# Reference: https://www.dmtf.org/sites/default/files/standards/documents/DSP0005.pdf
+# From: Joerg Jenderek
+# Note: only tested with monitor asset reports of Dell Display Manager
+# skip start like Language=fr|CA|iso8859-1
+0 search/27/C Start\040Component DMI Management Information Format
+#!:mime text/plain
+!:mime text/x-dmtf-mif
+!:ext mif
+
diff --git a/contrib/libs/libmagic/magic/Magdir/dataone b/contrib/libs/libmagic/magic/Magdir/dataone
new file mode 100644
index 0000000000..566633eff2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/dataone
@@ -0,0 +1,47 @@
+
+#------------------------------------------------------------------------------
+# $File: dataone,v 1.3 2022/04/18 21:38:10 christos Exp $
+#
+# DataONE- files from Dave Vieglais <dave.vieglais@gmail.com> &
+# Pratik Shrivastava <pratikshrivastava23@gmail.com>
+#
+# file formats: https://cn.dataone.org/cn/v2/formats
+#------------------------------------------------------------------------------
+
+# EML (Ecological Metadata Language Format)
+0 string \<?xml\ version=
+>&0 regex/1024 eml-[0-9]\\.[0-9]\\.[0-9]+ eml://ecoinformatics.org/%s
+
+# onedcx (DataONE Dublin Core Extended v1.0)
+>&0 regex/1024 onedcx/v[0-9]\\.[0-9]+ https://ns.dataone.org/metadata/schema/onedcx/v1.0
+
+# FGDC-STD-001-1998 (Content Standard for Digital Geospatial Metadata,
+# version 001-1998)
+>&0 search/1024 fgdc FGDC-STD-001-1998
+
+# Mercury (Oak Ridge National Lab Mercury Metadata version 1.0)
+>&0 regex/1024 mercury/terms/v[0-9]\\.[0-9] https://purl.org/ornl/schema/mercury/terms/v1.0
+
+# ISOTC211 (Geographic MetaData (GMD) Extensible Markup Language)
+>&0 search/1024 isotc211
+>>&0 search/1024 eng;USA https://www.isotc211.org/2005/gmd
+
+# ISOTC211 (NOAA Variant Geographic MetaData (GMD) Extensible Markup Language)
+>>&0 regex/1024 gov\\.noaa\\.nodc:[0-9]+ https://www.isotc211.org/2005/gmd-noaa
+
+# ISOTC211 PANGAEA Variant Geographic MetaData (GMD) Extensible Markup Language
+>>&0 regex/1024 pangaea\\.dataset[0-9][0-9][0-9][0-9][0-9][0-9]+ https://www.isotc211.org/2005/gmd-pangaea
+!:mime text/xml
+
+
+# Object Reuse and Exchange Vocabulary
+0 string \<?xml\ version=
+>&0 search/1024 rdf
+>>&0 search/1024 openarchives https://www.openarchives.org/ore/terms
+!:mime application/rdf+xml
+
+
+# Dryad Metadata Application Profile Version 3.1
+0 string <DryadData
+>&0 regex/1024 dryad-bibo/v[0-9]\\.[0-9] https://datadryad.org/profile/v3.1
+!:mime text/xml
diff --git a/contrib/libs/libmagic/magic/Magdir/dbpf b/contrib/libs/libmagic/magic/Magdir/dbpf
new file mode 100644
index 0000000000..df07ff809a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/dbpf
@@ -0,0 +1,15 @@
+
+#------------------------------------------------------------------------------
+# $File: dbpf,v 1.3 2019/04/19 00:42:27 christos Exp $
+# dppf: Maxis Database Packed Files, the stored data file format used by all
+# Maxis games after the Sims: http://wiki.niotso.org/DBPF
+# https://www.wiki.sc4devotion.com/index.php?title=DBPF
+# 13 Oct 2017, Kip Warner <kip at thevertigo dot com>
+0 string DBPF Maxis Database Packed File
+>4 ulelong x \b, version: %u.
+>>8 ulelong x \b%u
+>>>36 ulelong x \b, files: %u
+>>24 ledate !0 \b, created: %s
+>>28 ledate !0 \b, modified: %s
+!:ext dbpf/package/dat/sc4
+!:mime application/x-maxis-dbpf
diff --git a/contrib/libs/libmagic/magic/Magdir/der b/contrib/libs/libmagic/magic/Magdir/der
new file mode 100644
index 0000000000..3bc2e38aa9
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/der
@@ -0,0 +1,146 @@
+#------------------------------------------------------------------------------
+# $File: der,v 1.6 2023/01/11 23:59:49 christos Exp $
+# der: file(1) magic for DER encoded files
+#
+
+# Certificate information piece
+0 name certinfo
+>0 der seq
+>>&0 der set
+>>>&0 der seq
+>>>>&0 der obj_id3=550406
+>>>>&0 der prt_str=x \b, countryName=%s
+>>&0 der set
+>>>&0 der seq
+>>>>&0 der obj_id3=550408
+>>>>&0 der utf8_str=x \b, stateOrProvinceName=%s
+>>&0 der set
+>>>&0 der seq
+>>>>&0 der obj_id3=55040a
+>>>>&0 der utf8_str=x \b, organizationName=%s
+>>&0 der set
+>>>&0 der seq
+>>>>&0 der obj_id3=550403
+>>>>&0 der utf8_str=x \b, commonName=%s
+>>&0 der seq
+
+# Certificate requests
+0 der seq
+>&0 der seq
+>>&0 der int1=00 DER Encoded Certificate request
+>>&0 use certinfo
+
+# Key Pairs
+0 der seq
+>&0 der int1=00
+>&0 der int65=x
+>&0 der int3=010001 DER Encoded Key Pair, 512 bits
+
+0 der seq
+>&0 der int1=00
+>&0 der int129=x
+>&0 der int3=010001 DER Encoded Key Pair, 1024 bits
+
+0 der seq
+>&0 der int1=00
+>&0 der int257=x
+>&0 der int3=010001 DER Encoded Key Pair, 2048 bits
+
+0 der seq
+>&0 der int1=00
+>&0 der int513=x
+>&0 der int3=010001 DER Encoded Key Pair, 4096 bits
+
+0 der seq
+>&0 der int1=00
+>&0 der int1025=x
+>&0 der int3=010001 DER Encoded Key Pair, 8192 bits
+
+0 der seq
+>&0 der int1=00
+>&0 der int2049=x
+>&0 der int3=010001 DER Encoded Key Pair, 16k bits
+
+0 der seq
+>&0 der int1=00
+>&0 der int4097=x
+>&0 der int3=010001 DER Encoded Key Pair, 32k bits
+
+# Certificates
+0 der seq
+>&0 der seq
+>>&0 der int2=0dfa DER Encoded Certificate, 512 bits
+>>&0 der int2=0dfb DER Encoded Certificate, 1024 bits
+>>&0 der int2=0dfc DER Encoded Certificate, 2048 bits
+>>&0 der int2=0dfd DER Encoded Certificate, 4096 bits
+>>&0 der int2=0dfe DER Encoded Certificate, 8192 bits
+>>&0 der int2=0dff DER Encoded Certificate, 16k bits
+>>&0 der int2=0e04 DER Encoded Certificate, 32k bits
+>>&0 der int2=x DER Encoded Certificate, ? bits (%s)
+>>&0 der seq
+>>>&0 der obj_id9=2a864886f70d010105 \b, sha1WithRSAEncryption
+>>>&0 der obj_id9=x \b, ? Encryption (%s)
+>>>&0 der null
+>>&0 der seq
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id3=550406
+>>>>>&0 der prt_str=x \b, countryName=%s
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id3=550408
+>>>>>&0 der prt_str=x \b, stateOrProvinceName=%s
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id3=550407
+>>>>>&0 der prt_str=x \b, localityName=%s
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id3=55040a
+>>>>>&0 der prt_str=x \b, organizationName=%s
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id3=55040b
+>>>>>&0 der prt_str=x \b, organizationUnitName=%s
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id3=550403
+>>>>>&0 der prt_str=x \b, commonName=%s
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id9=2a864886f70d010901
+>>>>>&0 der ia5_str=x \b, emailAddress=%s
+#>>&0 der seq
+#>>>&0 der utc_time=x \b, utcTime=%s
+#>>>&0 der utc_time=x \b, utcTime=%s
+>>&0 use certinfo
+
+0 der seq
+>&0 der seq
+>>&0 der eoc
+>>>&0 der int1=02 Certificate, Version=3
+>>>&0 der int1=x Certificate, Version=%s
+>>&0 der int9=x \b, Serial=%s
+>>&0 der seq
+>>>&0 der obj_id9=2a864886f70d01010b
+>>>&0 der null
+>>&0 der seq
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id3=550403
+>>>>>&0 der utf8_str=x \b, Issuer=%s
+#>>&0 der seq
+#>>>&0 der utc_time=x \b, not-valid-before=%s
+#>>>&0 der utc_time=x \b, not-valid-after=%s
+>>&0 der seq
+>>>&0 der set
+>>>>&0 der seq
+>>>>>&0 der obj_id3=550403
+>>>>>&0 der utf8_str=x \b, Subject=%s
+
+# PKCS#7 Signed Data (e.g. JAR Signature Block File)
+# OID 1.2.840.113549.1.7.2 (2a864886f70d010702)
+# Reference: https://www.rfc-editor.org/rfc/rfc2315
+0 der seq
+>&0 der obj_id9=2a864886f70d010702 DER Encoded PKCS#7 Signed Data
+!:ext RSA/DSA/EC
diff --git a/contrib/libs/libmagic/magic/Magdir/diamond b/contrib/libs/libmagic/magic/Magdir/diamond
new file mode 100644
index 0000000000..39d1ed6258
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/diamond
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: diamond,v 1.7 2009/09/19 16:28:08 christos Exp $
+# diamond: file(1) magic for Diamond system
+#
+# ... diamond is a multi-media mail and electronic conferencing system....
+#
+# XXX - I think it was either renamed Slate, or replaced by Slate....
+#
+# The full deal is too long...
+#0 string <list>\n<protocol\ bbn-multimedia-format> Diamond Multimedia Document
+0 string =<list>\n<protocol\ bbn-m Diamond Multimedia Document
diff --git a/contrib/libs/libmagic/magic/Magdir/dif b/contrib/libs/libmagic/magic/Magdir/dif
new file mode 100644
index 0000000000..9d7e5fd25b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/dif
@@ -0,0 +1,33 @@
+
+#------------------------------------------------------------------------------
+# $File: dif,v 1.1 2020/04/09 19:14:01 christos Exp $
+# dif: file(1) magic for DIF text files
+
+#------------------------------------------------------------------------------
+# From: Joerg Jenderek
+# URL: http://en.wikipedia.org/wiki/Data_Interchange_Format
+# http://fileformats.archiveteam.org/wiki/Data_Interchange_Format
+# Note: called by TrID "Data Interchange Format",
+# by DROID x-fmt/368 "VisiCalc Database"
+0 string TABLE
+# skip text starting with TABLE by looking for numeric version on 2nd line
+>6 search/2 0,
+# skip DROID x-fmt-41-signature-id-380.dif by looking for key word TUPLES at the beginning
+>>27 search/128 TUPLES Data Interchange Format
+# https://www.pcmatic.com/company/libraries/fileextension/detail.asp?ext=dif.html
+#!:mime application/x-dif-spreadsheet Gnumeric
+# https://github.com/LibreOffice/online/blob/master/discovery.xml
+#!:mime application/x-dif-document LibreOffice
+# https://www.wikidata.org/wiki/Wikidata:WikiProject_Informatics/File_formats/Lists/File_formats
+!:mime application/x-dif
+# https://extension.nirsoft.net/dif
+#!:mime application/vnd.ms-excel
+#!:mime text/plain
+!:ext dif
+# look for double quote 0x22 on 3rd line
+>>>10 search/3 "
+# skip if next character also double quote
+>>>>&0 ubyte !0x22 \b, generator or table name
+# comment like EXCEL, pwm enclosed in double quotes
+>>>>>&-2 string x %s
+
diff --git a/contrib/libs/libmagic/magic/Magdir/diff b/contrib/libs/libmagic/magic/Magdir/diff
new file mode 100644
index 0000000000..a6124e3f70
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/diff
@@ -0,0 +1,41 @@
+
+#------------------------------------------------------------------------------
+# $File: diff,v 1.17 2020/08/22 18:16:58 christos Exp $
+# diff: file(1) magic for diff(1) output
+#
+0 search/1 diff\040 diff output text
+!:mime text/x-diff
+0 search/1 ***\040
+>&0 search/1024 \n---\040 context diff output text
+!:mime text/x-diff
+0 search/1 Only\040in\040 diff output text
+!:mime text/x-diff
+0 search/1 Common\040subdirectories:\040 diff output text
+!:mime text/x-diff
+
+0 search/1 Index: RCS/CVS diff output text
+!:mime text/x-diff
+
+# bsdiff: file(1) magic for bsdiff(1) output
+0 string/b BSDIFF40 bsdiff(1) patch file
+
+
+# unified diff
+0 search/4096 ---\040
+>&0 search/1024 \n
+>>&0 search/1 +++\040
+>>>&0 search/1024 \n
+>>>>&0 search/1 @@ unified diff output text
+!:mime text/x-diff
+!:strength + 90
+
+# librsync -- the library for network deltas
+#
+# Copyright (C) 2001 by Martin Pool. You may do whatever you want with
+# this file.
+#
+0 belong 0x72730236 rdiff network-delta data
+
+0 belong 0x72730136 rdiff network-delta signature data
+>4 belong x (block length=%d,
+>8 belong x signature strength=%d)
diff --git a/contrib/libs/libmagic/magic/Magdir/digital b/contrib/libs/libmagic/magic/Magdir/digital
new file mode 100644
index 0000000000..b2753b9898
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/digital
@@ -0,0 +1,59 @@
+
+#------------------------------------------------------------------------------
+# $File: digital,v 1.12 2021/07/03 14:01:46 christos Exp $
+# Digital UNIX - Info
+#
+0 string =!<arch>\n________64E Alpha archive
+>22 string X -- out of date
+#
+
+0 leshort 0603
+>24 leshort 0410 COFF format alpha pure
+>24 leshort 0413 COFF format alpha demand paged
+>>22 leshort&030000 !020000 executable
+>>22 leshort&020000 !0 dynamically linked
+>>16 lelong !0 not stripped
+>>16 lelong 0 stripped
+>>27 byte x - version %d
+>>26 byte x \b.%d
+>>28 byte x \b-%d
+>24 leshort 0407 COFF format alpha object
+>>22 leshort&030000 020000 shared library
+>>27 byte x - version %d
+>>26 byte x \b.%d
+>>28 byte x \b-%d
+
+# Basic recognition of Digital UNIX core dumps - Mike Bremford <mike@opac.bl.uk>
+#
+# The actual magic number is just "Core", followed by a 2-byte version
+# number; however, treating any file that begins with "Core" as a Digital
+# UNIX core dump file may produce too many false hits, so we include one
+# byte of the version number as well; DU 5.0 appears only to be up to
+# version 2.
+#
+0 string Core\001 Alpha COFF format core dump (Digital UNIX)
+>24 string >\0 \b, from '%s'
+0 string Core\002 Alpha COFF format core dump (Digital UNIX)
+>24 string >\0 \b, from '%s'
+#
+# The next is incomplete, we could tell more about this format,
+# but its not worth it.
+0 leshort 0x188 Alpha compressed COFF
+0 leshort 0x18f Alpha u-code object
+#
+#
+# Some other interesting Digital formats,
+0 string \377\377\177 ddis/ddif
+0 string \377\377\174 ddis/dots archive
+0 string \377\377\176 ddis/dtif table data
+0 string \033c\033 LN03 output
+0 long 04553207 X image
+#
+0 string =!<PDF>!\n profiling data file
+#
+# Locale data tables (MIPS and Alpha).
+#
+# GRR: line below is too general as it matches also TTComp archive, ASCII, 2K handled by ./archive
+0 short 0x0501 locale data table
+>6 short 0x24 for MIPS
+>6 short 0x40 for Alpha
diff --git a/contrib/libs/libmagic/magic/Magdir/dolby b/contrib/libs/libmagic/magic/Magdir/dolby
new file mode 100644
index 0000000000..d73e7d35f9
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/dolby
@@ -0,0 +1,69 @@
+
+#------------------------------------------------------------------------------
+# $File: dolby,v 1.9 2019/04/19 00:42:27 christos Exp $
+# ATSC A/53 aka AC-3 aka Dolby Digital <ashitaka@gmx.at>
+# from https://www.atsc.org/standards/a_52a.pdf
+# corrections, additions, etc. are always welcome!
+#
+# syncword
+0 beshort 0x0b77 ATSC A/52 aka AC-3 aka Dolby Digital stream,
+# Proposed audio/ac3 RFC/4184
+!:mime audio/vnd.dolby.dd-raw
+# fscod
+>4 byte&0xc0 = 0x00 48 kHz,
+>4 byte&0xc0 = 0x40 44.1 kHz,
+>4 byte&0xc0 = 0x80 32 kHz,
+# is this one used for 96 kHz?
+>4 byte&0xc0 = 0xc0 reserved frequency,
+#
+>5 byte&0x07 = 0x00 \b, complete main (CM)
+>5 byte&0x07 = 0x01 \b, music and effects (ME)
+>5 byte&0x07 = 0x02 \b, visually impaired (VI)
+>5 byte&0x07 = 0x03 \b, hearing impaired (HI)
+>5 byte&0x07 = 0x04 \b, dialogue (D)
+>5 byte&0x07 = 0x05 \b, commentary (C)
+>5 byte&0x07 = 0x06 \b, emergency (E)
+>5 beshort&0x07e0 0x0720 \b, voiceover (VO)
+>5 beshort&0x07e0 >0x0720 \b, karaoke
+# acmod
+>6 byte&0xe0 = 0x00 1+1 front,
+>>6 byte&0x10 = 0x10 LFE on,
+>6 byte&0xe0 = 0x20 1 front/0 rear,
+>>6 byte&0x10 = 0x10 LFE on,
+>6 byte&0xe0 = 0x40 2 front/0 rear,
+# dsurmod (for stereo only)
+>>6 byte&0x18 = 0x00 Dolby Surround not indicated
+>>6 byte&0x18 = 0x08 not Dolby Surround encoded
+>>6 byte&0x18 = 0x10 Dolby Surround encoded
+>>6 byte&0x18 = 0x18 reserved Dolby Surround mode
+>>6 byte&0x04 = 0x04 LFE on,
+>6 byte&0xe0 = 0x60 3 front/0 rear,
+>>6 byte&0x04 = 0x04 LFE on,
+>6 byte&0xe0 = 0x80 2 front/1 rear,
+>>6 byte&0x04 = 0x04 LFE on,
+>6 byte&0xe0 = 0xa0 3 front/1 rear,
+>>6 byte&0x01 = 0x01 LFE on,
+>6 byte&0xe0 = 0xc0 2 front/2 rear,
+>>6 byte&0x04 = 0x04 LFE on,
+>6 byte&0xe0 = 0xe0 3 front/2 rear,
+>>6 byte&0x01 = 0x01 LFE on,
+#
+>4 byte&0x3e = 0x00 \b, 32 kbit/s
+>4 byte&0x3e = 0x02 \b, 40 kbit/s
+>4 byte&0x3e = 0x04 \b, 48 kbit/s
+>4 byte&0x3e = 0x06 \b, 56 kbit/s
+>4 byte&0x3e = 0x08 \b, 64 kbit/s
+>4 byte&0x3e = 0x0a \b, 80 kbit/s
+>4 byte&0x3e = 0x0c \b, 96 kbit/s
+>4 byte&0x3e = 0x0e \b, 112 kbit/s
+>4 byte&0x3e = 0x10 \b, 128 kbit/s
+>4 byte&0x3e = 0x12 \b, 160 kbit/s
+>4 byte&0x3e = 0x14 \b, 192 kbit/s
+>4 byte&0x3e = 0x16 \b, 224 kbit/s
+>4 byte&0x3e = 0x18 \b, 256 kbit/s
+>4 byte&0x3e = 0x1a \b, 320 kbit/s
+>4 byte&0x3e = 0x1c \b, 384 kbit/s
+>4 byte&0x3e = 0x1e \b, 448 kbit/s
+>4 byte&0x3e = 0x20 \b, 512 kbit/s
+>4 byte&0x3e = 0x22 \b, 576 kbit/s
+>4 byte&0x3e = 0x24 \b, 640 kbit/s
diff --git a/contrib/libs/libmagic/magic/Magdir/dump b/contrib/libs/libmagic/magic/Magdir/dump
new file mode 100644
index 0000000000..cc5644d3e1
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/dump
@@ -0,0 +1,96 @@
+
+#------------------------------------------------------------------------------
+# $File: dump,v 1.17 2018/06/26 01:07:17 christos Exp $
+# dump: file(1) magic for dump file format--for new and old dump filesystems
+#
+# We specify both byte orders in order to recognize byte-swapped dumps.
+#
+0 name new-dump-be
+>4 bedate x This dump %s,
+>8 bedate x Previous dump %s,
+>12 belong >0 Volume %d,
+>692 belong 0 Level zero, type:
+>692 belong >0 Level %d, type:
+>0 belong 1 tape header,
+>0 belong 2 beginning of file record,
+>0 belong 3 map of inodes on tape,
+>0 belong 4 continuation of file record,
+>0 belong 5 end of volume,
+>0 belong 6 map of inodes deleted,
+>0 belong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 belong >0 Flags %x
+
+0 name old-dump-be
+#>4 bedate x This dump %s,
+#>8 bedate x Previous dump %s,
+>12 belong >0 Volume %d,
+>692 belong 0 Level zero, type:
+>692 belong >0 Level %d, type:
+>0 belong 1 tape header,
+>0 belong 2 beginning of file record,
+>0 belong 3 map of inodes on tape,
+>0 belong 4 continuation of file record,
+>0 belong 5 end of volume,
+>0 belong 6 map of inodes deleted,
+>0 belong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 belong >0 Flags %x
+
+0 name ufs2-dump-be
+>896 beqdate x This dump %s,
+>904 beqdate x Previous dump %s,
+>12 belong >0 Volume %d,
+>692 belong 0 Level zero, type:
+>692 belong >0 Level %d, type:
+>0 belong 1 tape header,
+>0 belong 2 beginning of file record,
+>0 belong 3 map of inodes on tape,
+>0 belong 4 continuation of file record,
+>0 belong 5 end of volume,
+>0 belong 6 map of inodes deleted,
+>0 belong 7 end of medium (for floppy),
+>676 string >\0 Label %s,
+>696 string >\0 Filesystem %s,
+>760 string >\0 Device %s,
+>824 string >\0 Host %s,
+>888 belong >0 Flags %x
+
+24 belong 60012 new-fs dump file (big endian),
+>0 use new-dump-be
+
+24 belong 60011 old-fs dump file (big endian),
+>0 use old-dump-be
+
+24 lelong 60012 new-fs dump file (little endian),
+# to correctly recognize '*.mo' GNU message catalog (little endian)
+!:strength - 15
+>0 use \^new-dump-be
+
+24 lelong 60011 old-fs dump file (little endian),
+>0 use \^old-dump-be
+
+
+24 belong 0x19540119 new-fs dump file (ufs2, big endian),
+>0 use ufs2-dump-be
+
+24 lelong 0x19540119 new-fs dump file (ufs2, little endian),
+>0 use \^ufs2-dump-be
+
+18 leshort 60011 old-fs dump file (16-bit, assuming PDP-11 endianness),
+>2 medate x Previous dump %s,
+>6 medate x This dump %s,
+>10 leshort >0 Volume %d,
+>0 leshort 1 tape header.
+>0 leshort 2 beginning of file record.
+>0 leshort 3 map of inodes on tape.
+>0 leshort 4 continuation of file record.
+>0 leshort 5 end of volume.
+>0 leshort 6 map of inodes deleted.
+>0 leshort 7 end of medium (for floppy).
diff --git a/contrib/libs/libmagic/magic/Magdir/dwarfs b/contrib/libs/libmagic/magic/Magdir/dwarfs
new file mode 100644
index 0000000000..3700a33c5d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/dwarfs
@@ -0,0 +1,45 @@
+
+#------------------------------------------------------------------------------
+# $File: dwarfs,v 1.2 2023/05/23 13:37:32 christos Exp $
+# dwarfs: file(1) magic for DwarFS File System Image files
+# URL: https://github.com/mhx/dwarfs for details about DwarFS
+# From: Marcus Holland-Moritz <github@mhxnet.de>
+
+#### DwarFS Version Macro
+0 name dwarfsversion
+>&0 byte x \b, version %d
+>&1 byte x \b.%d
+
+#### DwarFS Compression Macro
+0 name dwarfscompression
+>&0 leshort =0 \b, uncompressed
+>&0 leshort =1 \b, LZMA compression
+>&0 leshort =2 \b, ZSTD compression
+>&0 leshort =3 \b, LZ4 compression
+>&0 leshort =4 \b, LZ4HC compression
+>&0 leshort =5 \b, BROTLI compression
+
+#### DwarFS files without header
+## We first check against a DWARFS magic at the start of the file, then
+## validate by checking the block count / section type to be all zeros
+## for the first block. Finally, we check that the *next* block also
+## has the correct DWARFS magic.
+0 string DWARFS
+>&0x2A string/b \0\0\0\0\0\0
+>>&(&0x02.q+0x0A) string DWARFS DwarFS File System Image
+>>>&0 use dwarfsversion
+>>&0 use dwarfscompression
+
+#### DwarFS files with header
+## We search for a DWARFS magic in the first 64k of the file (images with
+## headers longer than 64k won't be recognized), then validate by checking
+## the block count / section type to be all zeros for the first block.
+## Finally, we check that the *next* block also has the correct DWARFS magic.
+## If we find a DWARFS magic that doesn't pass validation, we continue with
+## an indirect match recursively.
+1 search/65536/b DWARFS
+>&0x2A string/b \0\0\0\0\0\0
+>>&(&0x02.q+0x0A) string DWARFS DwarFS File System Image (with header)
+>>>&0 use dwarfsversion
+>>&0 use dwarfscompression
+>&-1 indirect x
diff --git a/contrib/libs/libmagic/magic/Magdir/dyadic b/contrib/libs/libmagic/magic/Magdir/dyadic
new file mode 100644
index 0000000000..c57f81b7cb
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/dyadic
@@ -0,0 +1,61 @@
+
+#------------------------------------------------------------------------------
+# $File: dyadic,v 1.9 2019/04/19 00:42:27 christos Exp $
+# Dyadic: file(1) magic for Dyalog APL.
+#
+# updated by Joerg Jenderek at Oct 2013
+# https://en.wikipedia.org/wiki/Dyalog_APL
+# https://www.dyalog.com/
+# .DXV Dyalog APL External Variable
+# .DIN Dyalog APL Input Table
+# .DOT Dyalog APL Output Table
+# .DFT Dyalog APL Format File
+0 ubeshort&0xFF60 0xaa00
+# skip biblio.dbt
+>1 byte !4
+# real Dyalog APL have non zero version numbers like 7.3 or 13.4
+>>2 ubeshort >0x0000 Dyalog APL
+>>>1 byte 0x00 aplcore
+#>>>1 byte 0x00 incomplete workspace
+# *.DCF Dyalog APL Component File
+>>>1 byte 0x01 component file 32-bit non-journaled non-checksummed
+#>>>1 byte 0x01 component file
+>>>1 byte 0x02 external variable exclusive
+#>>>1 byte 0x02 external variable
+# *.DWS Dyalog APL Workspace
+>>>1 byte 0x03 workspace
+>>>>7 byte&0x28 0x00 32-bit
+>>>>7 byte&0x28 0x20 64-bit
+>>>>7 byte&0x0c 0x00 classic
+>>>>7 byte&0x0c 0x04 unicode
+>>>>7 byte&0x88 0x00 big-endian
+>>>>7 byte&0x88 0x80 little-endian
+>>>1 byte 0x06 external variable shared
+# *.DSE Dyalog APL Session , *.DLF Dyalog APL Session Log File
+>>>1 byte 0x07 session
+>>>1 byte 0x08 mapped file 32-bit
+>>>1 byte 0x09 component file 64-bit non-journaled non-checksummed
+>>>1 byte 0x0a mapped file 64-bit
+>>>1 byte 0x0b component file 32-bit level 1 journaled non-checksummed
+>>>1 byte 0x0c component file 64-bit level 1 journaled non-checksummed
+>>>1 byte 0x0d component file 32-bit level 1 journaled checksummed
+>>>1 byte 0x0e component file 64-bit level 1 journaled checksummed
+>>>1 byte 0x0f component file 32-bit level 2 journaled checksummed
+>>>1 byte 0x10 component file 64-bit level 2 journaled checksummed
+>>>1 byte 0x11 component file 32-bit level 3 journaled checksummed
+>>>1 byte 0x12 component file 64-bit level 3 journaled checksummed
+>>>1 byte 0x13 component file 32-bit non-journaled checksummed
+>>>1 byte 0x14 component file 64-bit non-journaled checksummed
+>>>1 byte 0x15 component file under construction
+>>>1 byte 0x16 DFS component file 64-bit level 1 journaled checksummed
+>>>1 byte 0x17 DFS component file 64-bit level 2 journaled checksummed
+>>>1 byte 0x18 DFS component file 64-bit level 3 journaled checksummed
+>>>1 byte 0x19 external workspace
+>>>1 byte 0x80 DDB
+>>>2 byte x version %d
+>>>3 byte x \b.%d
+#>>>2 byte x type %d
+#>>>3 byte x subtype %d
+
+# *.DXF Dyalog APL Transfer File
+0 short 0x6060 Dyalog APL transfer
diff --git a/contrib/libs/libmagic/magic/Magdir/ebml b/contrib/libs/libmagic/magic/Magdir/ebml
new file mode 100644
index 0000000000..d37b5c0b23
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ebml
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: ebml,v 1.2 2019/04/19 00:42:27 christos Exp $
+# ebml: file(1) magic for various Extensible Binary Meta Language
+# https://www.matroska.org/technical/specs/index.html#track
+0 belong 0x1a45dfa3 EBML file
+>4 search/b/100 \102\202
+>>&1 string x \b, creator %.8s
diff --git a/contrib/libs/libmagic/magic/Magdir/edid b/contrib/libs/libmagic/magic/Magdir/edid
new file mode 100644
index 0000000000..a17b6c4ea7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/edid
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# $File: edid,v 1.1 2019/03/28 12:36:01 christos Exp $
+# edid: file(1) magic for EDID dump files
+
+0 quad 0x00ffffffffffff00 Extended display identification data dump
+!:mime application/x-edid-dump
+>18 byte 0x01 Version 1
+>>19 byte <0x04 \b.%d
+>18 byte 0x02 Version 2
+>>19 byte 0x00 \b.0
diff --git a/contrib/libs/libmagic/magic/Magdir/editors b/contrib/libs/libmagic/magic/Magdir/editors
new file mode 100644
index 0000000000..48eaa116e3
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/editors
@@ -0,0 +1,43 @@
+
+#------------------------------------------------------------------------------
+# $File: editors,v 1.12 2020/10/11 20:28:07 christos Exp $
+# T602 editor documents
+# by David Necas <yeti@physics.muni.cz>
+0 string @CT\ T602 document data,
+>4 string 0 Kamenicky
+>4 string 1 CP 852
+>4 string 2 KOI8-CS
+>4 string >2 unknown encoding
+
+# Vi IMproved Encrypted file
+# by David Necas <yeti@physics.muni.cz>
+# updated by Osman Surkatty
+0 string VimCrypt~ Vim encrypted file data
+>9 string 01! with zip cryptmethod
+>9 string 02! with blowfish cryptmethod
+>9 string 03! with blowfish2 cryptmethod
+
+0 name vimnanoswap
+>67 byte 0
+>>107 byte 0
+#>>>2 string x %s swap file
+>>>24 ulelong x \b, pid %d
+>>>28 string >\0 \b, user %s
+>>>68 string >\0 \b, host %s
+>>>108 string >\0 \b, file %s
+>>>1007 byte 0x55 \b, modified
+
+# Vi IMproved Swap file
+# by Sven Wegener <swegener@gentoo.org>
+0 string b0VIM\ Vim swap file
+>&0 string >\0 \b, version %s
+>0 use vimnanoswap
+
+
+# Lock/swap file for several editors, at least
+# Vi IMproved and nano
+0 string b0nano Nano swap file
+>0 use vimnanoswap
+
+# kate (K Advanced Text Editor)
+0 string \x00\x00\x00\x12Kate\ Swap\ File\ 2.0\x00 Kate swap file
diff --git a/contrib/libs/libmagic/magic/Magdir/efi b/contrib/libs/libmagic/magic/Magdir/efi
new file mode 100644
index 0000000000..7760100b19
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/efi
@@ -0,0 +1,15 @@
+
+#------------------------------------------------------------------------------
+# $File: efi,v 1.5 2014/04/30 21:41:02 christos Exp $
+# efi: file(1) magic for Universal EFI binaries
+
+0 lelong 0x0ef1fab9
+>4 lelong 1 Universal EFI binary with 1 architecture
+>>&0 lelong 7 \b, i386
+>>&0 lelong 0x01000007 \b, x86_64
+>4 lelong 2 Universal EFI binary with 2 architectures
+>>&0 lelong 7 \b, i386
+>>&0 lelong 0x01000007 \b, x86_64
+>>&20 lelong 7 \b, i386
+>>&20 lelong 0x01000007 \b, x86_64
+>4 lelong >2 Universal EFI binary with %d architectures
diff --git a/contrib/libs/libmagic/magic/Magdir/elf b/contrib/libs/libmagic/magic/Magdir/elf
new file mode 100644
index 0000000000..d3ec0260af
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/elf
@@ -0,0 +1,379 @@
+
+#------------------------------------------------------------------------------
+# $File: elf,v 1.88 2023/01/08 17:09:18 christos Exp $
+# elf: file(1) magic for ELF executables
+#
+# We have to check the byte order flag to see what byte order all the
+# other stuff in the header is in.
+#
+# What're the correct byte orders for the nCUBE and the Fujitsu VPP500?
+#
+# https://www.sco.com/developers/gabi/latest/ch4.eheader.html
+#
+# Created by: unknown
+# Modified by (1): Daniel Quinlan <quinlan@yggdrasil.com>
+# Modified by (2): Peter Tobias <tobias@server.et-inf.fho-emden.de> (core support)
+# Modified by (3): Christian 'Dr. Disk' Hechelmann <drdisk@ds9.au.s.shuttle.de> (fix of core support)
+# Modified by (4): <gerardo.cacciari@gmail.com> (VMS Itanium)
+# Modified by (5): Matthias Urlichs <smurf@debian.org> (Listing of many architectures)
+
+0 name elf-mips
+>0 lelong&0xf0000000 0x00000000 MIPS-I
+>0 lelong&0xf0000000 0x10000000 MIPS-II
+>0 lelong&0xf0000000 0x20000000 MIPS-III
+>0 lelong&0xf0000000 0x30000000 MIPS-IV
+>0 lelong&0xf0000000 0x40000000 MIPS-V
+>0 lelong&0xf0000000 0x50000000 MIPS32
+>0 lelong&0xf0000000 0x60000000 MIPS64
+>0 lelong&0xf0000000 0x70000000 MIPS32 rel2
+>0 lelong&0xf0000000 0x80000000 MIPS64 rel2
+>0 lelong&0xf0000000 0x90000000 MIPS32 rel6
+>0 lelong&0xf0000000 0xa0000000 MIPS64 rel6
+
+0 name elf-sparc
+>0 lelong&0x00ffff00 0x00000100 V8+ Required,
+>0 lelong&0x00ffff00 0x00000200 Sun UltraSPARC1 Extensions Required,
+>0 lelong&0x00ffff00 0x00000400 HaL R1 Extensions Required,
+>0 lelong&0x00ffff00 0x00000800 Sun UltraSPARC3 Extensions Required,
+>0 lelong&0x3 0 total store ordering,
+>0 lelong&0x3 1 partial store ordering,
+>0 lelong&0x3 2 relaxed memory ordering,
+
+0 name elf-pa-risc
+>2 leshort 0x020b 1.0
+>2 leshort 0x0210 1.1
+>2 leshort 0x0214 2.0
+>0 leshort &0x0008 (LP64)
+
+0 name elf-riscv
+>0 lelong&0x00000001 0x00000001 RVC,
+>0 lelong&0x00000008 0x00000008 RVE,
+>0 lelong&0x00000006 0x00000000 soft-float ABI,
+>0 lelong&0x00000006 0x00000002 single-float ABI,
+>0 lelong&0x00000006 0x00000004 double-float ABI,
+>0 lelong&0x00000006 0x00000006 quad-float ABI,
+
+0 name elf-le
+>16 leshort 0 no file type,
+!:mime application/octet-stream
+>16 leshort 1 relocatable,
+!:mime application/x-object
+>16 leshort 2 executable,
+!:mime application/x-executable
+>16 leshort 3 ${x?pie executable:shared object},
+
+!:mime application/x-${x?pie-executable:sharedlib}
+>16 leshort 4 core file,
+!:mime application/x-coredump
+# OS-specific
+>7 byte 202
+>>16 leshort 0xFE01 executable,
+!:mime application/x-executable
+# Core file detection is not reliable.
+#>>>(0x38+0xcc) string >\0 of '%s'
+#>>>(0x38+0x10) lelong >0 (signal %d),
+>16 leshort &0xff00
+>>18 leshort !8 processor-specific,
+>>18 leshort 8
+>>>16 leshort 0xFF80 PlayStation 2 IOP module,
+!:mime application/x-sharedlib
+>>>16 leshort !0xFF80 processor-specific,
+>18 clear x
+>18 leshort 0 no machine,
+>18 leshort 1 AT&T WE32100,
+>18 leshort 2 SPARC,
+>18 leshort 3 Intel 80386,
+>18 leshort 4 Motorola m68k,
+>>4 byte 1
+>>>36 lelong &0x01000000 68000,
+>>>36 lelong &0x00810000 CPU32,
+>>>36 lelong 0 68020,
+>18 leshort 5 Motorola m88k,
+>18 leshort 6 Intel 80486,
+>18 leshort 7 Intel 80860,
+# The official e_machine number for MIPS is now #8, regardless of endianness.
+# The second number (#10) will be deprecated later. For now, we still
+# say something if #10 is encountered, but only gory details for #8.
+>18 leshort 8 MIPS,
+>>4 byte 1
+>>>36 lelong &0x20 N32
+>18 leshort 10 MIPS,
+>>4 byte 1
+>>>36 lelong &0x20 N32
+>18 leshort 8
+# only for 32-bit
+>>4 byte 1
+>>>36 use elf-mips
+# only for 64-bit
+>>4 byte 2
+>>>48 use elf-mips
+>18 leshort 9 Amdahl,
+>18 leshort 10 MIPS (deprecated),
+>18 leshort 11 RS6000,
+>18 leshort 15 PA-RISC,
+# only for 32-bit
+>>4 byte 1
+>>>36 use elf-pa-risc
+# only for 64-bit
+>>4 byte 2
+>>>48 use elf-pa-risc
+>18 leshort 16 nCUBE,
+>18 leshort 17 Fujitsu VPP500,
+>18 leshort 18 SPARC32PLUS,
+# only for 32-bit
+>>4 byte 1
+>>>36 use elf-sparc
+>18 leshort 19 Intel 80960,
+>18 leshort 20 PowerPC or cisco 4500,
+>18 leshort 21 64-bit PowerPC or cisco 7500,
+>>48 lelong 0 Unspecified or Power ELF V1 ABI,
+>>48 lelong 1 Power ELF V1 ABI,
+>>48 lelong 2 OpenPOWER ELF V2 ABI,
+>18 leshort 22 IBM S/390,
+>18 leshort 23 Cell SPU,
+>18 leshort 24 cisco SVIP,
+>18 leshort 25 cisco 7200,
+>18 leshort 36 NEC V800 or cisco 12000,
+>18 leshort 37 Fujitsu FR20,
+>18 leshort 38 TRW RH-32,
+>18 leshort 39 Motorola RCE,
+>18 leshort 40 ARM,
+>>4 byte 1
+>>>36 lelong&0xff000000 0x04000000 EABI4
+>>>36 lelong&0xff000000 0x05000000 EABI5
+>>>36 lelong &0x00800000 BE8
+>>>36 lelong &0x00400000 LE8
+>18 leshort 41 Alpha,
+>18 leshort 42 Renesas SH,
+>18 leshort 43 SPARC V9,
+>>4 byte 2
+>>>48 use elf-sparc
+>18 leshort 44 Siemens Tricore Embedded Processor,
+>18 leshort 45 Argonaut RISC Core, Argonaut Technologies Inc.,
+>18 leshort 46 Renesas H8/300,
+>18 leshort 47 Renesas H8/300H,
+>18 leshort 48 Renesas H8S,
+>18 leshort 49 Renesas H8/500,
+>18 leshort 50 IA-64,
+>18 leshort 51 Stanford MIPS-X,
+>18 leshort 52 Motorola Coldfire,
+>18 leshort 53 Motorola M68HC12,
+>18 leshort 54 Fujitsu MMA,
+>18 leshort 55 Siemens PCP,
+>18 leshort 56 Sony nCPU,
+>18 leshort 57 Denso NDR1,
+>18 leshort 58 Start*Core,
+>18 leshort 59 Toyota ME16,
+>18 leshort 60 ST100,
+>18 leshort 61 Tinyj emb.,
+>18 leshort 62 x86-64,
+>18 leshort 63 Sony DSP,
+>18 leshort 64 DEC PDP-10,
+>18 leshort 65 DEC PDP-11,
+>18 leshort 66 FX66,
+>18 leshort 67 ST9+ 8/16 bit,
+>18 leshort 68 ST7 8 bit,
+>18 leshort 69 MC68HC16,
+>18 leshort 70 MC68HC11,
+>18 leshort 71 MC68HC08,
+>18 leshort 72 MC68HC05,
+>18 leshort 73 SGI SVx or Cray NV1,
+>18 leshort 74 ST19 8 bit,
+>18 leshort 75 Digital VAX,
+>18 leshort 76 Axis cris,
+>18 leshort 77 Infineon 32-bit embedded,
+>18 leshort 78 Element 14 64-bit DSP,
+>18 leshort 79 LSI Logic 16-bit DSP,
+>18 leshort 80 MMIX,
+>18 leshort 81 Harvard machine-independent,
+>18 leshort 82 SiTera Prism,
+>18 leshort 83 Atmel AVR 8-bit,
+>18 leshort 84 Fujitsu FR30,
+>18 leshort 85 Mitsubishi D10V,
+>18 leshort 86 Mitsubishi D30V,
+>18 leshort 87 NEC v850,
+>18 leshort 88 Renesas M32R,
+>18 leshort 89 Matsushita MN10300,
+>18 leshort 90 Matsushita MN10200,
+>18 leshort 91 picoJava,
+>18 leshort 92 OpenRISC,
+>18 leshort 93 Synopsys ARCompact ARC700 cores,
+>18 leshort 94 Tensilica Xtensa,
+>18 leshort 95 Alphamosaic VideoCore,
+>18 leshort 96 Thompson Multimedia,
+>18 leshort 97 NatSemi 32k,
+>18 leshort 98 Tenor Network TPC,
+>18 leshort 99 Trebia SNP 1000,
+>18 leshort 100 STMicroelectronics ST200,
+>18 leshort 101 Ubicom IP2022,
+>18 leshort 102 MAX Processor,
+>18 leshort 103 NatSemi CompactRISC,
+>18 leshort 104 Fujitsu F2MC16,
+>18 leshort 105 TI msp430,
+>18 leshort 106 Analog Devices Blackfin,
+>18 leshort 107 S1C33 Family of Seiko Epson,
+>18 leshort 108 Sharp embedded,
+>18 leshort 109 Arca RISC,
+>18 leshort 110 PKU-Unity Ltd.,
+>18 leshort 111 eXcess: 16/32/64-bit,
+>18 leshort 112 Icera Deep Execution Processor,
+>18 leshort 113 Altera Nios II,
+>18 leshort 114 NatSemi CRX,
+>18 leshort 115 Motorola XGATE,
+>18 leshort 116 Infineon C16x/XC16x,
+>18 leshort 117 Renesas M16C series,
+>18 leshort 118 Microchip dsPIC30F,
+>18 leshort 119 Freescale RISC core,
+>18 leshort 120 Renesas M32C series,
+>18 leshort 131 Altium TSK3000 core,
+>18 leshort 132 Freescale RS08,
+>18 leshort 134 Cyan Technology eCOG2,
+>18 leshort 135 Sunplus S+core7 RISC,
+>18 leshort 136 New Japan Radio (NJR) 24-bit DSP,
+>18 leshort 137 Broadcom VideoCore III,
+>18 leshort 138 LatticeMico32,
+>18 leshort 139 Seiko Epson C17 family,
+>18 leshort 140 TI TMS320C6000 DSP family,
+>18 leshort 141 TI TMS320C2000 DSP family,
+>18 leshort 142 TI TMS320C55x DSP family,
+>18 leshort 144 TI Programmable Realtime Unit
+>18 leshort 160 STMicroelectronics 64bit VLIW DSP,
+>18 leshort 161 Cypress M8C,
+>18 leshort 162 Renesas R32C series,
+>18 leshort 163 NXP TriMedia family,
+>18 leshort 164 QUALCOMM DSP6,
+>18 leshort 165 Intel 8051 and variants,
+>18 leshort 166 STMicroelectronics STxP7x family,
+>18 leshort 167 Andes embedded RISC,
+>18 leshort 168 Cyan eCOG1X family,
+>18 leshort 169 Dallas MAXQ30,
+>18 leshort 170 New Japan Radio (NJR) 16-bit DSP,
+>18 leshort 171 M2000 Reconfigurable RISC,
+>18 leshort 172 Cray NV2 vector architecture,
+>18 leshort 173 Renesas RX family,
+>18 leshort 174 META,
+>18 leshort 175 MCST Elbrus,
+>18 leshort 176 Cyan Technology eCOG16 family,
+>18 leshort 177 NatSemi CompactRISC,
+>18 leshort 178 Freescale Extended Time Processing Unit,
+>18 leshort 179 Infineon SLE9X,
+>18 leshort 180 Intel L1OM,
+>18 leshort 181 Intel K1OM,
+>18 leshort 183 ARM aarch64,
+>18 leshort 185 Atmel 32-bit family,
+>18 leshort 186 STMicroeletronics STM8 8-bit,
+>18 leshort 187 Tilera TILE64,
+>18 leshort 188 Tilera TILEPro,
+>18 leshort 189 Xilinx MicroBlaze 32-bit RISC,
+>18 leshort 190 NVIDIA CUDA architecture,
+>18 leshort 191 Tilera TILE-Gx,
+>18 leshort 195 Synopsys ARCv2/HS3x/HS4x cores,
+>18 leshort 197 Renesas RL78 family,
+>18 leshort 199 Renesas 78K0R,
+>18 leshort 200 Freescale 56800EX,
+>18 leshort 201 Beyond BA1,
+>18 leshort 202 Beyond BA2,
+>18 leshort 203 XMOS xCORE,
+>18 leshort 204 Microchip 8-bit PIC(r),
+>18 leshort 210 KM211 KM32,
+>18 leshort 211 KM211 KMX32,
+>18 leshort 212 KM211 KMX16,
+>18 leshort 213 KM211 KMX8,
+>18 leshort 214 KM211 KVARC,
+>18 leshort 215 Paneve CDP,
+>18 leshort 216 Cognitive Smart Memory,
+>18 leshort 217 iCelero CoolEngine,
+>18 leshort 218 Nanoradio Optimized RISC,
+>18 leshort 219 CSR Kalimba architecture family
+>18 leshort 220 Zilog Z80
+>18 leshort 221 Controls and Data Services VISIUMcore processor
+>18 leshort 222 FTDI Chip FT32 high performance 32-bit RISC architecture
+>18 leshort 223 Moxie processor family
+>18 leshort 224 AMD GPU architecture
+>18 leshort 243 UCB RISC-V,
+# only for 32-bit
+>>4 byte 1
+>>>36 use elf-riscv
+# only for 64-bit
+>>4 byte 2
+>>>48 use elf-riscv
+>18 leshort 244 Lanai 32-bit processor,
+>18 leshort 245 CEVA Processor Architecture Family,
+>18 leshort 246 CEVA X2 Processor Family,
+>18 leshort 247 eBPF,
+>18 leshort 248 Graphcore Intelligent Processing Unit,
+>18 leshort 249 Imagination Technologies,
+>18 leshort 250 Netronome Flow Processor,
+>18 leshort 251 NEC Vector Engine,
+>18 leshort 252 C-SKY processor family,
+>18 leshort 253 Synopsys ARCv3 64-bit ISA/HS6x cores,
+>18 leshort 254 MOS Technology MCS 6502 processor,
+>18 leshort 255 Synopsys ARCv3 32-bit,
+>18 leshort 256 Kalray VLIW core of the MPPA family,
+>18 leshort 257 WDC 65816/65C816,
+>18 leshort 258 LoongArch,
+>18 leshort 259 ChipON KungFu32,
+>18 leshort 0x1057 AVR (unofficial),
+>18 leshort 0x1059 MSP430 (unofficial),
+>18 leshort 0x1223 Adapteva Epiphany (unofficial),
+>18 leshort 0x2530 Morpho MT (unofficial),
+>18 leshort 0x3330 FR30 (unofficial),
+>18 leshort 0x3426 OpenRISC (obsolete),
+>18 leshort 0x4688 Infineon C166 (unofficial),
+>18 leshort 0x5441 Cygnus FRV (unofficial),
+>18 leshort 0x5aa5 DLX (unofficial),
+>18 leshort 0x7650 Cygnus D10V (unofficial),
+>18 leshort 0x7676 Cygnus D30V (unofficial),
+>18 leshort 0x8217 Ubicom IP2xxx (unofficial),
+>18 leshort 0x8472 OpenRISC (obsolete),
+>18 leshort 0x9025 Cygnus PowerPC (unofficial),
+>18 leshort 0x9026 Alpha (unofficial),
+>18 leshort 0x9041 Cygnus M32R (unofficial),
+>18 leshort 0x9080 Cygnus V850 (unofficial),
+>18 leshort 0xa390 IBM S/390 (obsolete),
+>18 leshort 0xabc7 Old Xtensa (unofficial),
+>18 leshort 0xad45 xstormy16 (unofficial),
+>18 leshort 0xbaab Old MicroBlaze (unofficial),,
+>18 leshort 0xbeef Cygnus MN10300 (unofficial),
+>18 leshort 0xdead Cygnus MN10200 (unofficial),
+>18 leshort 0xf00d Toshiba MeP (unofficial),
+>18 leshort 0xfeb0 Renesas M32C (unofficial),
+>18 leshort 0xfeba Vitesse IQ2000 (unofficial),
+>18 leshort 0xfebb NIOS (unofficial),
+>18 leshort 0xfeed Moxie (unofficial),
+>18 default x
+>>18 leshort x *unknown arch %#x*
+>20 lelong 0 invalid version
+>20 lelong 1 version 1
+
+0 string \177ELF ELF
+!:strength *2
+>4 byte 0 invalid class
+>4 byte 1 32-bit
+>4 byte 2 64-bit
+>5 byte 0 invalid byte order
+>5 byte 1 LSB
+>>0 use elf-le
+>5 byte 2 MSB
+>>0 use \^elf-le
+>7 byte 0 (SYSV)
+>7 byte 1 (HP-UX)
+>7 byte 2 (NetBSD)
+>7 byte 3 (GNU/Linux)
+>7 byte 4 (GNU/Hurd)
+>7 byte 5 (86Open)
+>7 byte 6 (Solaris)
+>7 byte 7 (Monterey)
+>7 byte 8 (IRIX)
+>7 byte 9 (FreeBSD)
+>7 byte 10 (Tru64)
+>7 byte 11 (Novell Modesto)
+>7 byte 12 (OpenBSD)
+>7 byte 13 (OpenVMS)
+>7 byte 14 (HP NonStop Kernel)
+>7 byte 15 (AROS Research Operating System)
+>7 byte 16 (FenixOS)
+>7 byte 17 (Nuxi CloudABI)
+>7 byte 97 (ARM)
+>7 byte 202 (Cafe OS)
+>7 byte 255 (embedded)
diff --git a/contrib/libs/libmagic/magic/Magdir/encore b/contrib/libs/libmagic/magic/Magdir/encore
new file mode 100644
index 0000000000..287b388dba
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/encore
@@ -0,0 +1,22 @@
+
+#------------------------------------------------------------------------------
+# $File: encore,v 1.7 2014/04/30 21:41:02 christos Exp $
+# encore: file(1) magic for Encore machines
+#
+# XXX - needs to have the byte order specified (NS32K was little-endian,
+# dunno whether they run the 88K in little-endian mode or not).
+#
+0 short 0x154 Encore
+>20 short 0x107 executable
+>20 short 0x108 pure executable
+>20 short 0x10b demand-paged executable
+>20 short 0x10f unsupported executable
+>12 long >0 not stripped
+>22 short >0 - version %d
+>22 short 0 -
+#>4 date x stamp %s
+0 short 0x155 Encore unsupported executable
+>12 long >0 not stripped
+>22 short >0 - version %d
+>22 short 0 -
+#>4 date x stamp %s
diff --git a/contrib/libs/libmagic/magic/Magdir/epoc b/contrib/libs/libmagic/magic/Magdir/epoc
new file mode 100644
index 0000000000..6f4ab5fc38
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/epoc
@@ -0,0 +1,62 @@
+
+#------------------------------------------------------------------------------
+# $File: epoc,v 1.9 2013/12/21 14:28:15 christos Exp $
+# EPOC : file(1) magic for EPOC documents [Psion Series 5/Osaris/Geofox 1]
+# Stefan Praszalowicz <hpicollo@worldnet.fr> and Peter Breitenlohner <peb@mppmu.mpg.de>
+# Useful information for improving this file can be found at:
+# http://software.frodo.looijaard.name/psiconv/formats/Index.html
+#------------------------------------------------------------------------------
+0 lelong 0x10000037 Psion Series 5
+>4 lelong 0x10000039 font file
+>4 lelong 0x1000003A printer driver
+>4 lelong 0x1000003B clipboard
+>4 lelong 0x10000042 multi-bitmap image
+!:mime image/x-epoc-mbm
+>4 lelong 0x1000006A application information file
+>4 lelong 0x1000006D
+>>8 lelong 0x1000007D Sketch image
+!:mime image/x-epoc-sketch
+>>8 lelong 0x1000007E voice note
+>>8 lelong 0x1000007F Word file
+!:mime application/x-epoc-word
+>>8 lelong 0x10000085 OPL program (TextEd)
+!:mime application/x-epoc-opl
+>>8 lelong 0x10000087 Comms settings
+>>8 lelong 0x10000088 Sheet file
+!:mime application/x-epoc-sheet
+>>8 lelong 0x100001C4 EasyFax initialisation file
+>4 lelong 0x10000073 OPO module
+!:mime application/x-epoc-opo
+>4 lelong 0x10000074 OPL application
+!:mime application/x-epoc-app
+>4 lelong 0x1000008A exported multi-bitmap image
+>4 lelong 0x1000016D
+>>8 lelong 0x10000087 Comms names
+
+0 lelong 0x10000041 Psion Series 5 ROM multi-bitmap image
+
+0 lelong 0x10000050 Psion Series 5
+>4 lelong 0x1000006D database
+>>8 lelong 0x10000084 Agenda file
+!:mime application/x-epoc-agenda
+>>8 lelong 0x10000086 Data file
+!:mime application/x-epoc-data
+>>8 lelong 0x10000CEA Jotter file
+!:mime application/x-epoc-jotter
+>4 lelong 0x100000E4 ini file
+
+0 lelong 0x10000079 Psion Series 5 binary:
+>4 lelong 0x00000000 DLL
+>4 lelong 0x10000049 comms hardware library
+>4 lelong 0x1000004A comms protocol library
+>4 lelong 0x1000005D OPX
+>4 lelong 0x1000006C application
+>4 lelong 0x1000008D DLL
+>4 lelong 0x100000AC logical device driver
+>4 lelong 0x100000AD physical device driver
+>4 lelong 0x100000E5 file transfer protocol
+>4 lelong 0x100000E5 file transfer protocol
+>4 lelong 0x10000140 printer definition
+>4 lelong 0x10000141 printer definition
+
+0 lelong 0x1000007A Psion Series 5 executable
diff --git a/contrib/libs/libmagic/magic/Magdir/erlang b/contrib/libs/libmagic/magic/Magdir/erlang
new file mode 100644
index 0000000000..df7aa2aac8
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/erlang
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# $File: erlang,v 1.7 2019/04/19 00:42:27 christos Exp $
+# erlang: file(1) magic for Erlang JAM and BEAM files
+# URL: https://www.erlang.org/faq/x779.html#AEN812
+
+# OTP R3-R4
+0 string \0177BEAM! Old Erlang BEAM file
+>6 short >0 - version %d
+
+# OTP R5 and onwards
+0 string FOR1
+>8 string BEAM Erlang BEAM file
+
+# 4.2 version may have a copyright notice!
+4 string Tue\ Jan\ 22\ 14:32:44\ MET\ 1991 Erlang JAM file - version 4.2
+79 string Tue\ Jan\ 22\ 14:32:44\ MET\ 1991 Erlang JAM file - version 4.2
+
+4 string 1.0\ Fri\ Feb\ 3\ 09:55:56\ MET\ 1995 Erlang JAM file - version 4.3
+
+0 bequad 0x0000000000ABCDEF Erlang DETS file
diff --git a/contrib/libs/libmagic/magic/Magdir/espressif b/contrib/libs/libmagic/magic/Magdir/espressif
new file mode 100644
index 0000000000..a97c09301f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/espressif
@@ -0,0 +1,57 @@
+
+# $File: espressif,v 1.3 2021/04/26 15:56:00 christos Exp $
+# configuration dump of Tasmota firmware for ESP8266 based devices by Espressif
+# URL: https://github.com/arendst/Sonoff-Tasmota/
+# Reference: https://codeload.github.com/arendst/Sonoff-Tasmota/zip/release-6.2/
+# Sonoff-Tasmota-release-6.2.zip/Sonoff-Tasmota-release-6.2/sonoff/settings.h
+# From: Joerg Jenderek
+#
+# cfg_holder=4617=0x1209
+0 uleshort 4617
+# remaining settings normally 0x5A+offset XORed; free_1D5[20] empty since 5.12.0e
+>0x1D5 ubequad 0x2f30313233343536 configuration of Tasmota firmware (ESP8266)
+!:mime application/x-tasmota-dmp
+!:ext dmp
+# version like 6.2.1.0 ~ 0x06020100 XORed to 0x63666262
+>>11 ubyte^0x65 x \b, version %u
+>>10 ubyte^0x64 x \b.%u
+>>9 ubyte^0x63 x \b.%u
+>>8 ubyte^0x62 x \b.%u
+#>8 ubelong x (%#x)
+# hostname[33] XORed
+>>0x165 ubyte^0x1BF x \b, hostname %c
+>>0x166 ubyte^0x1C0 >037 \b%c
+>>0x167 ubyte^0x1C1 >037 \b%c
+>>0x168 ubyte^0x1C2 >037 \b%c
+>>0x169 ubyte^0x1C3 >037 \b%c
+>>0x16A ubyte^0x1C4 >037 \b%c
+>>0x16B ubyte^0x1C5 >037 \b%c
+>>0x16C ubyte^0x1C6 >037 \b%c
+>>0x16D ubyte^0x1C7 >037 \b%c
+>>0x16E ubyte^0x1C8 >037 \b%c
+>>0x16F ubyte^0x1C9 >037 \b%c
+>>0x170 ubyte^0x1CA >037 \b%c
+>>0x171 ubyte^0x1CB >037 \b%c
+>>0x172 ubyte^0x1CC >037 \b%c
+>>0x173 ubyte^0x1CD >037 \b%c
+>>0x174 ubyte^0x1CE >037 \b%c
+>>0x175 ubyte^0x1CF >037 \b%c
+>>0x176 ubyte^0x1D0 >037 \b%c
+>>0x177 ubyte^0x1D1 >037 \b%c
+>>0x178 ubyte^0x1D2 >037 \b%c
+>>0x179 ubyte^0x1D3 >037 \b%c
+>>0x17A ubyte^0x1D4 >037 \b%c
+>>0x17B ubyte^0x1D5 >037 \b%c
+>>0x17C ubyte^0x1D6 >037 \b%c
+>>0x17D ubyte^0x1D7 >037 \b%c
+>>0x17E ubyte^0x1D8 >037 \b%c
+>>0x17F ubyte^0x1D9 >037 \b%c
+>>0x180 ubyte^0x1DA >037 \b%c
+>>0x181 ubyte^0x1DB >037 \b%c
+>>0x182 ubyte^0x1DC >037 \b%c
+>>0x183 ubyte^0x1DD >037 \b%c
+>>0x184 ubyte^0x1DE >037 \b%c
+>>0x185 ubyte^0x1DF >037 \b%c
+#>>0x165 string x (%.33s)
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/esri b/contrib/libs/libmagic/magic/Magdir/esri
new file mode 100644
index 0000000000..e49a7ce407
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/esri
@@ -0,0 +1,28 @@
+
+#------------------------------------------------------------------------------
+# $File: esri,v 1.5 2019/04/19 00:42:27 christos Exp $
+# ESRI Shapefile format (.shp .shx .dbf=DBaseIII)
+# Based on info from
+# <URL:https://www.esri.com/library/whitepapers/pdfs/shapefile.pdf>
+0 belong 9994 ESRI Shapefile
+>4 belong =0
+>8 belong =0
+>12 belong =0
+>16 belong =0
+>20 belong =0
+>28 lelong x version %d
+>24 belong x length %d
+>32 lelong =0 type Null Shape
+>32 lelong =1 type Point
+>32 lelong =3 type PolyLine
+>32 lelong =5 type Polygon
+>32 lelong =8 type MultiPoint
+>32 lelong =11 type PointZ
+>32 lelong =13 type PolyLineZ
+>32 lelong =15 type PolygonZ
+>32 lelong =18 type MultiPointZ
+>32 lelong =21 type PointM
+>32 lelong =23 type PolyLineM
+>32 lelong =25 type PolygonM
+>32 lelong =28 type MultiPointM
+>32 lelong =31 type MultiPatch
diff --git a/contrib/libs/libmagic/magic/Magdir/etf b/contrib/libs/libmagic/magic/Magdir/etf
new file mode 100644
index 0000000000..707d23d3a4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/etf
@@ -0,0 +1,33 @@
+#------------------------------------------------------------------------------
+# $File: etf,v 1.1 2015/04/19 22:40:04 christos Exp $
+# elf: file(1) magic for Erlang External Term Format magic
+# http://erlang.org/doc/apps/erts/erl_ext_dist.html
+# This magic is too weak so it is not enabled by default
+0 byte 131
+>1 byte 80
+>>2 belong >0 Erlang External Term Format, compressed, original size = %d
+>1 byte 70 Erlang External Term Format, starts with NEW_FLOAT_EXT
+>1 byte 77 Erlang External Term Format, starts with BIT_BINARY_EXT
+>1 byte 97 Erlang External Term Format, starts with SMALL_INTEGER_EXT
+>1 byte 98 Erlang External Term Format, starts with INTEGER_EXT
+>1 byte 99 Erlang External Term Format, starts with FLOAT_EXT
+>1 byte 100 Erlang External Term Format, starts with ATOM_EXT
+>1 byte 101 Erlang External Term Format, starts with REFERENCE_EXT
+>1 byte 102 Erlang External Term Format, starts with PORT_EXT
+>1 byte 103 Erlang External Term Format, starts with PID_EXT
+>1 byte 104 Erlang External Term Format, starts with SMALL_TUPLE_EXT
+>1 byte 105 Erlang External Term Format, starts with LARGE_TUPLE_EXT
+>1 byte 106 Erlang External Term Format, starts with NIL_EXT
+>1 byte 107 Erlang External Term Format, starts with STRING_EXT
+>1 byte 108 Erlang External Term Format, starts with LIST_EXT
+>1 byte 109 Erlang External Term Format, starts with BINARY_EXT
+>1 byte 110 Erlang External Term Format, starts with SMALL_BIG_EXT
+>1 byte 111 Erlang External Term Format, starts with LARGE_BIG_EXT
+>1 byte 112 Erlang External Term Format, starts with NEW_FUN_EXT
+>1 byte 113 Erlang External Term Format, starts with EXPORT_EXT
+>1 byte 114 Erlang External Term Format, starts with NEW_REFERENCE_EXT
+>1 byte 115 Erlang External Term Format, starts with SMALL_ATOM_EXT
+>1 byte 116 Erlang External Term Format, starts with MAP_EXT
+>1 byte 117 Erlang External Term Format, starts with FUN_EXT
+>1 byte 118 Erlang External Term Format, starts with ATOM_UTF8_EXT
+>1 byte 119 Erlang External Term Format, starts with SMALL_ATOM_UTF8_EXT
diff --git a/contrib/libs/libmagic/magic/Magdir/fcs b/contrib/libs/libmagic/magic/Magdir/fcs
new file mode 100644
index 0000000000..613437f842
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/fcs
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: fcs,v 1.4 2009/09/19 16:28:09 christos Exp $
+# fcs: file(1) magic for FCS (Flow Cytometry Standard) data files
+# From Roger Leigh <roger@whinlatter.uklinux.net>
+0 string FCS1.0 Flow Cytometry Standard (FCS) data, version 1.0
+0 string FCS2.0 Flow Cytometry Standard (FCS) data, version 2.0
+0 string FCS3.0 Flow Cytometry Standard (FCS) data, version 3.0
+
diff --git a/contrib/libs/libmagic/magic/Magdir/filesystems b/contrib/libs/libmagic/magic/Magdir/filesystems
new file mode 100644
index 0000000000..cd72130516
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/filesystems
@@ -0,0 +1,2694 @@
+#------------------------------------------------------------------------------
+# $File: filesystems,v 1.158 2023/05/21 17:19:08 christos Exp $
+# filesystems: file(1) magic for different filesystems
+#
+0 name partid
+>0 ubyte 0x00 Unused
+>0 ubyte 0x01 12-bit FAT
+>0 ubyte 0x02 XENIX /
+>0 ubyte 0x03 XENIX /usr
+>0 ubyte 0x04 16-bit FAT, less than 32M
+>0 ubyte 0x05 extended partition
+>0 ubyte 0x06 16-bit FAT, more than 32M
+>0 ubyte 0x07 OS/2 HPFS, NTFS, QNX2, Adv. UNIX
+>0 ubyte 0x08 AIX or os, or etc.
+>0 ubyte 0x09 AIX boot partition or Coherent
+>0 ubyte 0x0a O/2 boot manager or Coherent swap
+>0 ubyte 0x0b 32-bit FAT
+>0 ubyte 0x0c 32-bit FAT, LBA-mapped
+>0 ubyte 0x0d 7XXX, LBA-mapped
+>0 ubyte 0x0e 16-bit FAT, LBA-mapped
+>0 ubyte 0x0f extended partition, LBA-mapped
+>0 ubyte 0x10 OPUS
+>0 ubyte 0x11 OS/2 DOS 12-bit FAT
+>0 ubyte 0x12 Compaq diagnostics
+>0 ubyte 0x14 OS/2 DOS 16-bit FAT <32M
+>0 ubyte 0x16 OS/2 DOS 16-bit FAT >=32M
+>0 ubyte 0x17 OS/2 hidden IFS
+>0 ubyte 0x18 AST Windows swapfile
+>0 ubyte 0x19 Willowtech Photon coS
+>0 ubyte 0x1b hidden win95 fat 32
+>0 ubyte 0x1c hidden win95 fat 32 lba
+>0 ubyte 0x1d hidden win95 fat 16 lba
+>0 ubyte 0x20 Willowsoft OFS1
+>0 ubyte 0x21 reserved
+>0 ubyte 0x23 reserved
+>0 ubyte 0x24 NEC DOS
+>0 ubyte 0x26 reserved
+>0 ubyte 0x31 reserved
+>0 ubyte 0x32 Alien Internet Services NOS
+>0 ubyte 0x33 reserved
+>0 ubyte 0x34 reserved
+>0 ubyte 0x35 JFS on OS2
+>0 ubyte 0x36 reserved
+>0 ubyte 0x38 Theos
+>0 ubyte 0x39 Plan 9, or Theos spanned
+>0 ubyte 0x3a Theos ver 4 4gb partition
+>0 ubyte 0x3b Theos ve 4 extended partition
+>0 ubyte 0x3c PartitionMagic recovery
+>0 ubyte 0x3d Hidden Netware
+>0 ubyte 0x40 VENIX 286 or LynxOS
+>0 ubyte 0x41 PReP
+>0 ubyte 0x42 linux swap sharing DRDOS disk
+>0 ubyte 0x43 linux sharing DRDOS disk
+>0 ubyte 0x44 GoBack change utility
+>0 ubyte 0x45 Boot US Boot manager
+>0 ubyte 0x46 EUMEL/Elan or Ergos 3
+>0 ubyte 0x47 EUMEL/Elan or Ergos 3
+>0 ubyte 0x48 EUMEL/Elan or Ergos 3
+>0 ubyte 0x4a ALFX/THIN filesystem for DOS
+>0 ubyte 0x4c Oberon partition
+>0 ubyte 0x4d QNX4.x
+>0 ubyte 0x4e QNX4.x 2nd part
+>0 ubyte 0x4f QNX4.x 3rd part
+>0 ubyte 0x50 DM (disk manager)
+>0 ubyte 0x51 DM6 Aux1 (or Novell)
+>0 ubyte 0x52 CP/M or Microport SysV/AT
+>0 ubyte 0x53 DM6 Aux3
+>0 ubyte 0x54 DM6 DDO
+>0 ubyte 0x55 EZ-Drive (disk manager)
+>0 ubyte 0x56 Golden Bow (disk manager)
+>0 ubyte 0x57 Drive PRO
+>0 ubyte 0x5c Priam Edisk (disk manager)
+>0 ubyte 0x61 SpeedStor
+>0 ubyte 0x63 GNU HURD or Mach or Sys V/386
+>0 ubyte 0x64 Novell Netware 2.xx or Speedstore
+>0 ubyte 0x65 Novell Netware 3.xx
+>0 ubyte 0x66 Novell 386 Netware
+>0 ubyte 0x67 Novell
+>0 ubyte 0x68 Novell
+>0 ubyte 0x69 Novell
+>0 ubyte 0x70 DiskSecure Multi-Boot
+>0 ubyte 0x71 reserved
+>0 ubyte 0x73 reserved
+>0 ubyte 0x74 reserved
+>0 ubyte 0x75 PC/IX
+>0 ubyte 0x76 reserved
+>0 ubyte 0x77 M2FS/M2CS partition
+>0 ubyte 0x78 XOSL boot loader filesystem
+>0 ubyte 0x80 MINIX until 1.4a
+>0 ubyte 0x81 MINIX since 1.4b
+>0 ubyte 0x82 Linux swap or Solaris
+>0 ubyte 0x83 Linux native
+>0 ubyte 0x84 OS/2 hidden C: drive
+>0 ubyte 0x85 Linux extended partition
+>0 ubyte 0x86 NT FAT volume set
+>0 ubyte 0x87 NTFS volume set or HPFS mirrored
+>0 ubyte 0x8a Linux Kernel AiR-BOOT partition
+>0 ubyte 0x8b Legacy Fault tolerant FAT32
+>0 ubyte 0x8c Legacy Fault tolerant FAT32 ext
+>0 ubyte 0x8d Hidden free FDISK FAT12
+>0 ubyte 0x8e Linux Logical Volume Manager
+>0 ubyte 0x90 Hidden free FDISK FAT16
+>0 ubyte 0x91 Hidden free FDISK DOS EXT
+>0 ubyte 0x92 Hidden free FDISK FAT16 Big
+>0 ubyte 0x93 Amoeba filesystem
+>0 ubyte 0x94 Amoeba bad block table
+>0 ubyte 0x95 MIT EXOPC native partitions
+>0 ubyte 0x97 Hidden free FDISK FAT32
+>0 ubyte 0x98 Datalight ROM-DOS Super-Boot
+>0 ubyte 0x99 Mylex EISA SCSI
+>0 ubyte 0x9a Hidden free FDISK FAT16 LBA
+>0 ubyte 0x9b Hidden free FDISK EXT LBA
+>0 ubyte 0x9f BSDI?
+>0 ubyte 0xa0 IBM Thinkpad hibernation
+>0 ubyte 0xa1 HP Volume expansion (SpeedStor)
+>0 ubyte 0xa3 HP Volume expansion (SpeedStor)
+>0 ubyte 0xa4 HP Volume expansion (SpeedStor)
+>0 ubyte 0xa5 386BSD partition type
+>0 ubyte 0xa6 OpenBSD partition type
+>0 ubyte 0xa7 NeXTSTEP 486
+>0 ubyte 0xa8 Apple UFS
+>0 ubyte 0xa9 NetBSD partition type
+>0 ubyte 0xaa Olivetty Fat12 1.44MB Service part
+>0 ubyte 0xab Apple Boot
+>0 ubyte 0xae SHAG OS filesystem
+>0 ubyte 0xaf Apple HFS
+>0 ubyte 0xb0 BootStar Dummy
+>0 ubyte 0xb1 reserved
+>0 ubyte 0xb3 reserved
+>0 ubyte 0xb4 reserved
+>0 ubyte 0xb6 reserved
+>0 ubyte 0xb7 BSDI BSD/386 filesystem
+>0 ubyte 0xb8 BSDI BSD/386 swap
+>0 ubyte 0xbb Boot Wizard Hidden
+>0 ubyte 0xbe Solaris 8 partition type
+>0 ubyte 0xbf Solaris partition type
+>0 ubyte 0xc0 CTOS
+>0 ubyte 0xc1 DRDOS/sec (FAT-12)
+>0 ubyte 0xc2 Hidden Linux
+>0 ubyte 0xc3 Hidden Linux swap
+>0 ubyte 0xc4 DRDOS/sec (FAT-16, < 32M)
+>0 ubyte 0xc5 DRDOS/sec (EXT)
+>0 ubyte 0xc6 DRDOS/sec (FAT-16, >= 32M)
+>0 ubyte 0xc7 Syrinx (Cyrnix?) or HPFS disabled
+>0 ubyte 0xc8 Reserved for DR-DOS 8.0+
+>0 ubyte 0xc9 Reserved for DR-DOS 8.0+
+>0 ubyte 0xca Reserved for DR-DOS 8.0+
+>0 ubyte 0xcb DR-DOS 7.04+ Secured FAT32 CHS
+>0 ubyte 0xcc DR-DOS 7.04+ Secured FAT32 LBA
+>0 ubyte 0xcd CTOS Memdump
+>0 ubyte 0xce DR-DOS 7.04+ FAT16X LBA
+>0 ubyte 0xcf DR-DOS 7.04+ EXT LBA
+>0 ubyte 0xd0 REAL/32 secure big partition
+>0 ubyte 0xd1 Old Multiuser DOS FAT12
+>0 ubyte 0xd4 Old Multiuser DOS FAT16 Small
+>0 ubyte 0xd5 Old Multiuser DOS Extended
+>0 ubyte 0xd6 Old Multiuser DOS FAT16 Big
+>0 ubyte 0xd8 CP/M 86
+>0 ubyte 0xdb CP/M or Concurrent CP/M
+>0 ubyte 0xdd Hidden CTOS Memdump
+>0 ubyte 0xde Dell PowerEdge Server utilities
+>0 ubyte 0xdf DG/UX virtual disk manager
+>0 ubyte 0xe0 STMicroelectronics ST AVFS
+>0 ubyte 0xe1 DOS access or SpeedStor 12-bit
+>0 ubyte 0xe3 DOS R/O or Storage Dimensions
+>0 ubyte 0xe4 SpeedStor 16-bit FAT < 1024 cyl.
+>0 ubyte 0xe5 reserved
+>0 ubyte 0xe6 reserved
+>0 ubyte 0xeb BeOS
+>0 ubyte 0xee GPT Protective MBR
+>0 ubyte 0xef EFI system partition
+>0 ubyte 0xf0 Linux PA-RISC boot loader
+>0 ubyte 0xf1 SpeedStor or Storage Dimensions
+>0 ubyte 0xf2 DOS 3.3+ Secondary
+>0 ubyte 0xf3 reserved
+>0 ubyte 0xf4 SpeedStor large partition
+>0 ubyte 0xf5 Prologue multi-volumen partition
+>0 ubyte 0xf6 reserved
+>0 ubyte 0xf9 pCache: ext2/ext3 persistent cache
+>0 ubyte 0xfa Bochs x86 emulator
+>0 ubyte 0xfb VMware File System
+>0 ubyte 0xfc VMware Swap
+>0 ubyte 0xfd Linux RAID partition persistent sb
+>0 ubyte 0xfe LANstep or IBM PS/2 IML
+>0 ubyte 0xff Xenix Bad Block Table
+
+0 string \366\366\366\366 PC formatted floppy with no filesystem
+# Sun disk labels
+# From /usr/include/sun/dklabel.h:
+0774 beshort 0xdabe
+# modified by Joerg Jenderek, because original test
+# succeeds for Cabinet archive dao360.dl_ with negative blocks
+>0770 long >0 Sun disk label
+>>0 string x '%s
+>>>31 string >\0 \b%s
+>>>>63 string >\0 \b%s
+>>>>>95 string >\0 \b%s
+>>0 string x \b'
+>>0734 short >0 %d rpm,
+>>0736 short >0 %d phys cys,
+>>0740 short >0 %d alts/cyl,
+>>0746 short >0 %d interleave,
+>>0750 short >0 %d data cyls,
+>>0752 short >0 %d alt cyls,
+>>0754 short >0 %d heads/partition,
+>>0756 short >0 %d sectors/track,
+>>0764 long >0 start cyl %d,
+>>0770 long x %d blocks
+# Is there a boot block written 1 sector in?
+>512 belong&077777777 0600407 \b, boot block present
+
+# Joerg Jenderek: Smart Boot Manager backup file is 25 (MSDOS) or 41 (LINUX) byte header + first sectors of disk
+# (http://btmgr.sourceforge.net/docs/user-guide-3.html)
+0 string SBMBAKUP_ Smart Boot Manager backup file
+>9 string x \b, version %-5.5s
+>>14 string =_
+>>>15 string x %-.1s
+>>>>16 string =_ \b.
+>>>>>17 string x \b%-.1s
+>>>>>>18 string =_ \b.
+>>>>>>>19 string x \b%-.1s
+>>>22 ubyte 0
+>>>>21 ubyte x \b, from drive %#x
+>>>22 ubyte >0
+>>>>21 string x \b, from drive %s
+>>>535 search/17 \x55\xAA
+>>>>&-512 indirect x \b; contains
+
+# updated by Joerg Jenderek at Nov 2012
+# DOS Emulator image is 128 byte, null right padded header + harddisc image
+0 string DOSEMU\0
+>0x27E leshort 0xAA55
+#offset is 128
+>>19 ubyte 128
+>>>(19.b-1) ubyte 0x0 DOS Emulator image
+>>>>7 ulelong >0 \b, %u heads
+>>>>11 ulelong >0 \b, %d sectors/track
+>>>>15 ulelong >0 \b, %d cylinders
+>>>>128 indirect x \b; contains
+
+# added by Joerg Jenderek at Nov 2012
+# http://www.thenakedpc.com/articles/v04/08/0408-05.html
+# Symantec (Peter Norton) Image.dat file consists of variable header, bootrecord, part of FAT and root directory data
+0 string PNCIHISK\0 Norton Utilities disc image data
+# real x86 boot sector with jump instruction
+>509 search/1026 \x55\xAA\xeb
+>>&-1 indirect x \b; contains
+# http://file-extension.net/seeker/file_extension_dat
+0 string PNCIUNDO Norton Disk Doctor UnDo file
+#
+
+# DOS/MBR boot sector updated by Joerg Jenderek at Sep 2007,May 2011,2013
+# for any allowed sector sizes
+30 search/481 \x55\xAA
+# to display DOS/MBR boot sector (40) before old one (strength=50+21),Syslinux bootloader (71),SYSLINUX MBR (37+36),NetBSD mbr (110),AdvanceMAME mbr (111)
+# DOS BPB information (70) and after DOS floppy (120) like in previous file version
+!:strength +65
+# for sector sizes < 512 Bytes
+>11 uleshort <512
+>>(11.s-2) uleshort 0xAA55 DOS/MBR boot sector
+# for sector sizes with 512 or more Bytes
+>0x1FE leshort 0xAA55 DOS/MBR boot sector
+
+# ExFAT
+3 string/w =EXFAT
+>0x1FE leshort 0xAA55
+>>0x6E ubyte 1
+>>>0x6F ubyte 0x80
+>>>0 ubyte 0xEB DOS/MBR boot sector,
+>>>0x69 ubyte x ExFAT Filesystem version %d.
+>>>0x68 ubyte x \b%d
+>>>0x6d ubyte x \b, (1<<%d) sectors per cluster
+>>>0x48 ulequad x \b, sectors %lld
+>>>0x64 ulelong x \b, serial number %#x
+
+# keep old DOS/MBR boot sector as dummy for mbr and bootloader displaying
+# only for sector sizes with 512 or more Bytes
+0x1FE leshort 0xAA55 DOS/MBR boot sector
+#
+# to display information (50) before DOS BPB (strength=70) and after DOS floppy (120) like in old file version
+!:strength +65
+>2 string OSBS OS/BS MBR
+# added by Joerg Jenderek at Feb 2013 according to https://thestarman.pcministry.com/asm/mbr/
+# and https://en.wikipedia.org/wiki/Master_Boot_Record
+# test for nearly all MS-DOS Master Boot Record initial program loader (IPL) is now done by
+# characteristic assembler instructions: xor ax,ax;mov ss,ax;mov sp,7c00
+>0 search/2 \x33\xc0\x8e\xd0\xbc\x00\x7c MS-MBR
+# Microsoft Windows 95A and early ( https://thestarman.pcministry.com/asm/mbr/STDMBR.htm )
+# assembler instructions: mov si,sp;push ax;pop es;push ax;pop ds;sti;cld
+>>8 ubequad 0x8bf45007501ffbfc
+# https://thestarman.pcministry.com/asm/mbr/200MBR.htm
+>>>0x16 ubyte 0xF3 \b,DOS 2
+>>>>219 regex Author\ -\ Author:
+# found "David Litton" , "A Pehrsson "
+>>>>>&0 string x "%s"
+>>>0x16 ubyte 0xF2
+# NEC MS-DOS 3.30 Rev. 3 . See https://thestarman.pcministry.com/asm/mbr/DOS33MBR.htm
+# assembler instructions: mov di,077c;cmp word ptrl[di],a55a;jnz
+>>>>0x22 ubequad 0xbf7c07813d5aa575 \b,NEC 3.3
+# version MS-DOS 3.30 til MS-Windows 95A (WinVer=4.00.1111)
+>>>>0x22 default x \b,D0S version 3.3-7.0
+# error messages are printed by assembler instructions: mov si,06nn;...;int 10 (0xBEnn06;...)
+# where nn is string offset varying for different languages
+# "Invalid partition table" nn=0x8b for english version
+>>>>>(0x49.b) string Invalid\ partition\ table english
+>>>>>(0x49.b) string Ung\201ltige\ Partitionstabelle german
+>>>>>(0x49.b) string Table\ de\ partition\ invalide french
+>>>>>(0x49.b) string Tabela\ de\ parti\207ao\ inv\240lida portuguese
+>>>>>(0x49.b) string Tabla\ de\ partici\242n\ no\ v\240lida spanish
+>>>>>(0x49.b) string Tavola\ delle\ partizioni\ non\ valida italian
+>>>>>0x49 ubyte >0 at offset %#x
+>>>>>>(0x49.b) string >\0 "%s"
+# "Error loading operating system" nn=0xa3 for english version
+# "Fehler beim Laden des Betriebssystems" nn=0xa7 for german version
+# "Erreur en chargeant syst\212me d'exploitation" nn=0xa7 for french version
+# "Erro na inicializa\207ao do sistema operacional" nn=0xa7 for portuguese Brazilian version
+# "Error al cargar sistema operativo" nn=0xa8 for spanish version
+# "Errore durante il caricamento del sistema operativo" nn=0xae for italian version
+>>>>>0x74 ubyte >0 at offset %#x
+>>>>>>(0x74.b) string >\0 "%s"
+# "Missing operating system" nn=0xc2 for english version
+# "Betriebssystem fehlt" nn=0xcd for german version
+# "Syst\212me d'exploitation absent" nn=0xd2 for french version
+# "Sistema operacional nao encontrado" nn=0xd4 for portuguese Brazilian version
+# "Falta sistema operativo" nn=0xca for spanish version
+# "Sistema operativo mancante" nn=0xe2 for italian version
+>>>>>0x79 ubyte >0 at offset %#x
+>>>>>>(0x79.b) string >\0 "%s"
+# Microsoft Windows 95B to XP (https://thestarman.pcministry.com/asm/mbr/95BMEMBR.htm)
+# assembler instructions: push ax;pop es;push ax;pop ds;cld;mov si,7c1b
+>>8 ubequad 0x5007501ffcbe1b7c
+# assembler instructions: rep;movsb;retf;mov si,07be;mov cl,04
+>>>24 ubequad 0xf3a4cbbebe07b104 9M
+# "Invalid partition table" nn=0x10F for english version
+# "Ung\201ltige Partitionstabelle" nn=0x10F for german version
+# "Table de partition erron\202e" nn=0x10F for french version
+# "\216\257\245\340\240\346\250\256\255\255\240\357 \341\250\341\342\245\254\240 \255\245 \255\240\251\244\245\255\240" nn=0x10F for russian version
+>>>>(0x3C.b+0x0FF) string Invalid\ partition\ table english
+>>>>(0x3C.b+0x0FF) string Ung\201ltige\ Partitionstabelle german
+>>>>(0x3C.b+0x0FF) string Table\ de\ partition\ erron\202e french
+>>>>(0x3C.b+0x0FF) string \215\245\257\340\240\242\250\253\354\255\240\357\ \342\240\241\253\250\346\240 russian
+>>>>0x3C ubyte x at offset %#x+0xFF
+>>>>(0x3C.b+0x0FF) string >\0 "%s"
+# "Error loading operating system" nn=0x127 for english version
+# "Fehler beim Laden des Betriebssystems" nn=0x12b for german version
+# "Erreur lors du chargement du syst\212me d'exploitation" nn=0x12a for french version
+# "\216\350\250\241\252\240 \257\340\250 \247\240\243\340\343\247\252\245 \256\257\245\340\240\346\250\256\255\255\256\251 \341\250\341\342\245\254\353" nn=0x12d for russian version
+>>>>0xBD ubyte x at offset 0x1%x
+>>>>(0xBD.b+0x100) string >\0 "%s"
+# "Missing operating system" nn=0x146 for english version
+# "Betriebssystem fehlt" nn=0x151 for german version
+# "Syst\212me d'exploitation manquant" nn=0x15e for french version
+# "\216\257\245\340\240\346\250\256\255\255\240\357 \341\250\341\342\245\254\240 \255\245 \255\240\251\244\245\255\240" nn=0x156 for russian version
+>>>>0xA9 ubyte x at offset 0x1%x
+>>>>(0xA9.b+0x100) string >\0 "%s"
+# https://thestarman.pcministry.com/asm/mbr/Win2kmbr.htm
+# assembler instructions: rep;movsb;retf;mov BP,07be;mov cl,04
+>>>24 ubequad 0xf3a4cbbdbe07b104 XP
+# where xxyyzz are lower bits from offsets of error messages varying for different languages
+>>>>0x1B4 ubelong&0x00FFFFFF 0x002c4463 english
+>>>>0x1B4 ubelong&0x00FFFFFF 0x002c486e german
+# "Invalid partition table" xx=0x12C for english version
+# "Ung\201ltige Partitionstabelle" xx=0x12C for german version
+>>>>0x1b5 ubyte >0 at offset 0x1%x
+>>>>(0x1b5.b+0x100) string >\0 "%s"
+# "Error loading operating system" yy=0x144 for english version
+# "Fehler beim Laden des Betriebssystems" yy=0x148 for german version
+>>>>0x1b6 ubyte >0 at offset 0x1%x
+>>>>(0x1b6.b+0x100) string >\0 "%s"
+# "Missing operating system" zz=0x163 for english version
+# "Betriebssystem nicht vorhanden" zz=0x16e for german version
+>>>>0x1b7 ubyte >0 at offset 0x1%x
+>>>>(0x1b7.b+0x100) string >\0 "%s"
+# Microsoft Windows Vista or 7
+# assembler instructions: ..;mov ds,ax;mov si,7c00;mov di,..00
+>>8 ubequad 0xc08ed8be007cbf00
+# Microsoft Windows Vista (https://thestarman.pcministry.com/asm/mbr/VistaMBR.htm)
+# assembler instructions: jnz 0729;cmp ebx,"TCPA"
+>>>0xEC ubequad 0x753b6681fb544350 Vista
+# where xxyyzz are lower bits from offsets of error messages varying for different languages
+>>>>0x1B4 ubelong&0x00FFFFFF 0x00627a99 english
+#>>>>0x1B4 ubelong&0x00FFFFFF ? german
+# "Invalid partition table" xx=0x162 for english version
+# "Ung\201ltige Partitionstabelle" xx=0x1?? for german version
+>>>>0x1b5 ubyte >0 at offset 0x1%x
+>>>>(0x1b5.b+0x100) string >\0 "%s"
+# "Error loading operating system" yy=0x17a for english version
+# "Fehler beim Laden des Betriebssystems" yy= 0x1?? for german version
+>>>>0x1b6 ubyte >0 at offset 0x1%x
+>>>>(0x1b6.b+0x100) string >\0 "%s"
+# "Missing operating system" zz=0x199 for english version
+# "Betriebssystem nicht vorhanden" zz=0x1?? for german version
+>>>>0x1b7 ubyte >0 at offset 0x1%x
+>>>>(0x1b7.b+0x100) string >\0 "%s"
+# Microsoft Windows 7 (https://thestarman.pcministry.com/asm/mbr/W7MBR.htm)
+# assembler instructions: cmp ebx,"TCPA";cmp
+>>>0xEC ubequad 0x6681fb5443504175 Windows 7
+# where xxyyzz are lower bits from offsets of error messages varying for different languages
+>>>>0x1B4 ubelong&0x00FFFFFF 0x00637b9a english
+#>>>>0x1B4 ubelong&0x00FFFFFF ? german
+# "Invalid partition table" xx=0x163 for english version
+# "Ung\201ltige Partitionstabelle" xx=0x1?? for german version
+>>>>0x1b5 ubyte >0 at offset 0x1%x
+>>>>(0x1b5.b+0x100) string >\0 "%s"
+# "Error loading operating system" yy=0x17b for english version
+# "Fehler beim Laden des Betriebssystems" yy=0x1?? for german version
+>>>>0x1b6 ubyte >0 at offset 0x1%x
+>>>>(0x1b6.b+0x100) string >\0 "%s"
+# "Missing operating system" zz=0x19a for english version
+# "Betriebssystem nicht vorhanden" zz=0x1?? for german version
+>>>>0x1b7 ubyte >0 at offset 0x1%x
+>>>>(0x1b7.b+0x100) string >\0 "%s"
+# https://thestarman.pcministry.com/asm/mbr/Win2kmbr.htm#DiskSigs
+# https://en.wikipedia.org/wiki/MBR_disk_signature#ID
+>>0x1b8 ulelong >0 \b, disk signature %#-.4x
+# driveID/timestamp for Win 95B,98,98SE and ME. See https://thestarman.pcministry.com/asm/mbr/mystery.htm
+>>0xDA uleshort 0
+>>>0xDC ulelong >0 \b, created
+# physical drive number (0x80-0xFF) when the Windows wrote that byte to the drive
+>>>>0xDC ubyte x with driveID %#x
+# hours, minutes and seconds
+>>>>0xDf ubyte x at %x
+>>>>0xDe ubyte x \b:%x
+>>>>0xDd ubyte x \b:%x
+# special case for Microsoft MS-DOS 3.21 spanish
+# assembler instructions: cli;mov $0x30,%ax;mov %ax,%ss;mov
+>0 ubequad 0xfab830008ed0bc00
+# assembler instructions: $0x1f00,%sp;mov $0x80cb,%di;add %cl,(%bx,%si);in (%dx),%ax;mov
+>>8 ubequad 0x1fbfcb800008ed8 MS-MBR,D0S version 3.21 spanish
+# Microsoft MBR IPL end
+
+# dr-dos with some upper-, lowercase variants
+>0x9D string Invalid\ partition\ table$
+>>181 string No\ Operating\ System$
+>>>201 string Operating\ System\ load\ error$ \b, DR-DOS MBR, Version 7.01 to 7.03
+>0x9D string Invalid\ partition\ table$
+>>181 string No\ operating\ system$
+>>>201 string Operating\ system\ load\ error$ \b, DR-DOS MBR, Version 7.01 to 7.03
+>342 string Invalid\ partition\ table$
+>>366 string No\ operating\ system$
+>>>386 string Operating\ system\ load\ error$ \b, DR-DOS MBR, version 7.01 to 7.03
+>295 string NEWLDR\0
+>>302 string Bad\ PT\ $
+>>>310 string No\ OS\ $
+>>>>317 string OS\ load\ err$
+>>>>>329 string Moved\ or\ missing\ IBMBIO.LDR\n\r
+>>>>>>358 string Press\ any\ key\ to\ continue.\n\r$
+>>>>>>>387 string Copyright\ (c)\ 1984,1998
+>>>>>>>>411 string Caldera\ Inc.\0 \b, DR-DOS MBR (IBMBIO.LDR)
+#
+# tests for different MS-DOS Master Boot Records (MBR) moved and merged
+#
+#>0x145 string Default:\ F \b, FREE-DOS MBR
+#>0x14B string Default:\ F \b, FREE-DOS 1.0 MBR
+>0x145 search/7 Default:\ F \b, FREE-DOS MBR
+#>>313 string F0\ .\ .\ .
+#>>>322 string disk\ 1
+#>>>>382 string FAT3
+>64 string no\ active\ partition\ found
+>>96 string read\ error\ while\ reading\ drive \b, FREE-DOS Beta 0.9 MBR
+# Ranish Partition Manager http://www.ranish.com/part/
+>387 search/4 \0\ Error!\r
+>>378 search/7 Virus!
+>>>397 search/4 Booting\040
+>>>>408 search/4 HD1/\0 \b, Ranish MBR (
+>>>>>416 string Writing\ changes... \b2.37
+>>>>>>438 ubyte x \b,%#x dots
+>>>>>>440 ubyte >0 \b,virus check
+>>>>>>441 ubyte >0 \b,partition %c
+#2.38,2.42,2.44
+>>>>>416 string !Writing\ changes... \b
+>>>>>>418 ubyte 1 \bvirus check,
+>>>>>>419 ubyte x \b%#x seconds
+>>>>>>420 ubyte&0x0F >0 \b,partition
+>>>>>>>420 ubyte&0x0F <5 \b %x
+>>>>>>>420 ubyte&0x0F 0Xf \b ask
+>>>>>420 ubyte x \b)
+#
+# SYSLINUX MBR moved
+# https://www.acronis.de/
+>362 string MBR\ Error\ \0\r
+>>376 string ress\ any\ key\ to\040
+>>>392 string boot\ from\ floppy...\0 \b, Acronis MBR
+# added by Joerg Jenderek
+# https://www.visopsys.org/
+# https://partitionlogic.org.uk/
+>309 string No\ bootable\ partition\ found\r
+>>339 string I/O\ Error\ reading\ boot\ sector\r \b, Visopsys MBR
+>349 string No\ bootable\ partition\ found\r
+>>379 string I/O\ Error\ reading\ boot\ sector\r \b, simple Visopsys MBR
+# bootloader, bootmanager
+>0x40 string SBML
+# label with 11 characters of FAT 12 bit filesystem
+>>43 string SMART\ BTMGR
+>>>430 string SBMK\ Bad!\r \b, Smart Boot Manager
+# OEM-ID not always "SBM"
+#>>>>3 strings SBM
+>>>>6 string >\0 \b, version %s
+>382 string XOSLLOADXCF \b, eXtended Operating System Loader
+>6 string LILO \b, LInux i386 boot LOader
+>>120 string LILO \b, version 22.3.4 SuSe
+>>172 string LILO \b, version 22.5.8 Debian
+# updated by Joerg Jenderek at Oct 2008
+# variables according to grub-0.97/stage1/stage1.S or
+# https://www.gnu.org/software/grub/manual/grub.html#Embedded-data
+# usual values are marked with comments to get only information of strange GRUB loaders
+>342 search/60 \0Geom\0
+#>0 ulelong x %x=0x009048EB , 0x2a9048EB 0
+>>0x41 ubyte <2
+>>>0x3E ubyte >2 \b; GRand Unified Bootloader
+# 0x3 for 0.5.95,0.93,0.94,0.96 0x4 for 1.90
+>>>>0x3E ubyte x \b, stage1 version %#x
+#If it is 0xFF, use a drive passed by BIOS
+>>>>0x40 ubyte <0xFF \b, boot drive %#x
+# in most case 0,1,0x2e for GRUB 0.5.95
+>>>>0x41 ubyte >0 \b, LBA flag %#x
+>>>>0x42 uleshort <0x8000 \b, stage2 address %#x
+#>>>>0x42 uleshort =0x8000 \b, stage2 address %#x (usual)
+>>>>0x42 uleshort >0x8000 \b, stage2 address %#x
+#>>>>0x44 ulelong =1 \b, 1st sector stage2 %#x (default)
+>>>>0x44 ulelong >1 \b, 1st sector stage2 %#x
+>>>>0x48 uleshort <0x800 \b, stage2 segment %#x
+#>>>>0x48 uleshort =0x800 \b, stage2 segment %#x (usual)
+>>>>0x48 uleshort >0x800 \b, stage2 segment %#x
+>>>>402 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>394 string stage1 \b, GRUB version 0.5.95
+>>>>382 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>376 string GRUB\ \0 \b, GRUB version 0.93 or 1.94
+>>>>383 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>377 string GRUB\ \0 \b, GRUB version 0.94
+>>>>385 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>379 string GRUB\ \0 \b, GRUB version 0.95 or 0.96
+>>>>391 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>>385 string GRUB\ \0 \b, GRUB version 0.97
+# unknown version
+>>>343 string Geom\0Read\0\ Error\0
+>>>>321 string Loading\ stage1.5 \b, GRUB version x.y
+>>>380 string Geom\0Hard\ Disk\0Read\0\ Error\0
+>>>>374 string GRUB\ \0 \b, GRUB version n.m
+# SYSLINUX bootloader moved
+>395 string chksum\0\ ERROR!\0 \b, Gujin bootloader
+# http://www.bcdwb.de/bcdw/index_e.htm
+>3 string BCDL
+>>498 string BCDL\ \ \ \ BIN \b, Bootable CD Loader (1.50Z)
+# mbr partition table entries updated by Joerg Jenderek at Sep 2013
+# skip Norton Utilities disc image data
+>3 string !IHISK
+# skip Linux style boot sector starting with assembler instructions mov 0x7c0,ax;
+>>0 belong !0xb8c0078e
+# not Linux kernel
+>>>514 string !HdrS
+# not BeOS
+>>>>422 string !Be\ Boot\ Loader
+# jump over BPB instruction implies DOS bootsector or AdvanceMAME mbr
+>>>>>0 ubelong&0xFD000000 =0xE9000000
+# AdvanceMAME mbr
+>>>>>>(1.b+2) ubequad 0xfa31c08ed88ec08e
+>>>>>>>446 use partition-table
+# mbr, Norton Utilities disc image data, or 2nd,etc. sector of x86 bootloader
+>>>>>0 ubelong&0xFD000000 !0xE9000000
+# skip FSInfosector
+>>>>>>0 string !RRaA
+# skip 3rd sector of MS x86 bootloader with assembler instructions cli;MOVZX EAX,BYTE PTR [BP+10];MOV ECX,
+# https://thestarman.pcministry.com/asm/mbr/MSWIN41.htm
+>>>>>>>0 ubequad !0xfa660fb64610668b
+# skip 13rd sector of MS x86 bootloader
+>>>>>>>>0 ubequad !0x660fb64610668b4e
+# skip sector starting with DOS new line
+>>>>>>>>>0 string !\r\n
+# allowed active flag 0,80h-FFh
+>>>>>>>>>>446 ubyte 0
+>>>>>>>>>>>446 use partition-table
+>>>>>>>>>>446 ubyte >0x7F
+>>>>>>>>>>>446 use partition-table
+# TODO: test for extended bootrecord (ebr) moved and merged with mbr partition table entries
+# mbr partition table entries end
+# https://www.acronis.de/
+#FAT label=ACRONIS\ SZ
+#OEM-ID=BOOTWIZ0
+>442 string Non-system\ disk,\040
+>>459 string press\ any\ key...\x7\0 \b, Acronis Startup Recovery Loader
+# updated by Joerg Jenderek at Nov 2012, Sep 2013
+# DOS names like F11.SYS or BOOTWIZ.SYS are 8 right space padded bytes+3 bytes
+# display 1 space
+>>>447 ubyte x \b
+>>>477 use DOS-filename
+#
+>185 string FDBOOT\ Version\040
+>>204 string \rNo\ Systemdisk.\040
+>>>220 string Booting\ from\ harddisk.\n\r
+>>>245 string Cannot\ load\ from\ harddisk.\n\r
+>>>>273 string Insert\ Systemdisk\040
+>>>>>291 string and\ press\ any\ key.\n\r \b, FDBOOT harddisk Bootloader
+>>>>>>200 string >\0 \b, version %-3s
+>242 string Bootsector\ from\ C.H.\ Hochst\204
+# http://freecode.com/projects/dosfstools dosfstools-n.m/src/mkdosfs.c
+# updated by Joerg Jenderek at Nov 2012. Use search directive with offset instead of string
+# skip name "C.H. Hochstaetter" partly because it is sometimes written without umlaut
+>242 search/127 Bootsector\ from\ C.H.\ Hochst
+>>278 search/127 No\ Systemdisk.\ Booting\ from\ harddisk
+# followed by variants with point,CR-NL or NL-CR
+>>>208 search/261 Cannot\ load\ from\ harddisk.
+# followed by variants CR-NL or NL-CR
+>>>>236 search/235 Insert\ Systemdisk\ and\ press\ any\ key.
+# followed by variants with point,CR-NL or NL-CR
+>>>>>180 search/96 Disk\ formatted\ with\ WinImage\ \b, WinImage harddisk Bootloader
+# followed by string like "6.50 (c) 1993-2004 Gilles Vollant"
+>>>>>>&0 string x \b, version %-4.4s
+>(1.b+2) ubyte 0xe
+>>(1.b+3) ubyte 0x1f
+>>>(1.b+4) ubyte 0xbe
+# message offset found at (1.b+5) is 0x77 for FAT32 or 0x5b for others
+>>>>(1.b+5) ubyte&0xd3 0x53
+>>>>>(1.b+6) ubyte 0x7c
+# assembler instructions: lodsb;and al,al;jz 0xb;push si;mov ah,
+>>>>>>(1.b+7) ubyte 0xac
+>>>>>>>(1.b+8) ubyte 0x22
+>>>>>>>>(1.b+9) ubyte 0xc0
+>>>>>>>>>(1.b+10) ubyte 0x74
+>>>>>>>>>>(1.b+11) ubyte 0x0b
+>>>>>>>>>>>(1.b+12) ubyte 0x56
+>>>>>>>>>>>>(1.b+13) ubyte 0xb4 \b, mkdosfs boot message display
+# FAT1X version
+>>>>>>>>>>>>>(1.b+5) ubyte 0x5b
+>>>>>>>>>>>>>>0x5b string >\0 "%-s"
+# FAT32 version
+>>>>>>>>>>>>>(1.b+5) ubyte 0x77
+>>>>>>>>>>>>>>0x77 string >\0 "%-s"
+>214 string Please\ try\ to\ install\ FreeDOS\ \b, DOS Emulator boot message display
+#>>244 string from\ dosemu-freedos-*-bin.tgz\r
+#>>>170 string Sorry,\ could\ not\ load\ an\040
+#>>>>195 string operating\ system.\r\n
+#
+>103 string This\ is\ not\ a\ bootable\ disk.\040
+>>132 string Please\ insert\ a\ bootable\040
+>>>157 string floppy\ and\r\n
+>>>>169 string press\ any\ key\ to\ try\ again...\r \b, FREE-DOS message display
+#
+>66 string Solaris\ Boot\ Sector
+>>99 string Incomplete\ MDBoot\ load.
+>>>89 string Version \b, Sun Solaris Bootloader
+>>>>97 byte x version %c
+#
+>408 string OS/2\ !!\ SYS01475\r\0
+>>429 string OS/2\ !!\ SYS02025\r\0
+>>>450 string OS/2\ !!\ SYS02027\r\0
+>>>469 string OS2BOOT\ \ \ \ \b, IBM OS/2 Warp bootloader
+#
+>409 string OS/2\ !!\ SYS01475\r\0
+>>430 string OS/2\ !!\ SYS02025\r\0
+>>>451 string OS/2\ !!\ SYS02027\r\0
+>>>470 string OS2BOOT\ \ \ \ \b, IBM OS/2 Warp Bootloader
+>112 string This\ disk\ is\ not\ bootable\r
+>>142 string If\ you\ wish\ to\ make\ it\ bootable
+>>>176 string run\ the\ DOS\ program\ SYS\040
+>>>200 string after\ the\r
+>>>>216 string system\ has\ been\ loaded\r\n
+>>>>>242 string Please\ insert\ a\ DOS\ diskette\040
+>>>>>271 string into\r\n\ the\ drive\ and\040
+>>>>>>292 string strike\ any\ key...\0 \b, IBM OS/2 Warp message display
+# XP
+>430 string NTLDR\ is\ missing\xFF\r\n
+>>449 string Disk\ error\xFF\r\n
+>>>462 string Press\ any\ key\ to\ restart\r \b, Microsoft Windows XP Bootloader
+# DOS names like NTLDR,CMLDR,$LDR$ are 8 right space padded bytes+3 bytes
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+#
+>>>>371 ubyte >0x20
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+>>>>>>376 ubyte&0xDF >0
+>>>>>>>376 string x \b.%-.3s
+#
+>430 string NTLDR\ nicht\ gefunden\xFF\r\n
+>>453 string Datentr\204gerfehler\xFF\r\n
+>>>473 string Neustart\ mit\ beliebiger\ Taste\r \b, Microsoft Windows XP Bootloader (german)
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+# offset variant
+>>>>379 string \0
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+#
+>430 string NTLDR\ fehlt\xFF\r\n
+>>444 string Datentr\204gerfehler\xFF\r\n
+>>>464 string Neustart\ mit\ beliebiger\ Taste\r \b, Microsoft Windows XP Bootloader (2.german)
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+# variant
+>>>>371 ubyte >0x20
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+>>>>>>376 ubyte&0xDF >0
+>>>>>>>376 string x \b.%-.3s
+#
+>430 string NTLDR\ fehlt\xFF\r\n
+>>444 string Medienfehler\xFF\r\n
+>>>459 string Neustart:\ Taste\ dr\201cken\r \b, Microsoft Windows XP Bootloader (3.german)
+>>>>371 ubyte >0x20
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+>>>>>>376 ubyte&0xDF >0
+>>>>>>>376 string x \b.%-.3s
+# variant
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+#
+>430 string Datentr\204ger\ entfernen\xFF\r\n
+>>454 string Medienfehler\xFF\r\n
+>>>469 string Neustart:\ Taste\ dr\201cken\r \b, Microsoft Windows XP Bootloader (4.german)
+>>>>379 string \0
+>>>>>368 ubyte&0xDF >0
+>>>>>>368 string x %-.5s
+>>>>>>>373 ubyte&0xDF >0
+>>>>>>>>373 string x \b%-.3s
+>>>>>>376 ubyte&0xDF >0
+>>>>>>>376 string x \b.%-.3s
+# variant
+>>>>417 ubyte&0xDF >0
+>>>>>417 string x %-.5s
+>>>>>>422 ubyte&0xDF >0
+>>>>>>>422 string x \b%-.3s
+>>>>>425 ubyte&0xDF >0
+>>>>>>425 string >\ \b.%-.3s
+#
+
+#>3 string NTFS\ \ \ \040
+>389 string Fehler\ beim\ Lesen\040
+>>407 string des\ Datentr\204gers
+>>>426 string NTLDR\ fehlt
+>>>>440 string NTLDR\ ist\ komprimiert
+>>>>>464 string Neustart\ mit\ Strg+Alt+Entf\r \b, Microsoft Windows XP Bootloader NTFS (german)
+#>3 string NTFS\ \ \ \040
+>313 string A\ disk\ read\ error\ occurred.\r
+>>345 string A\ kernel\ file\ is\ missing\040
+>>>370 string from\ the\ disk.\r
+>>>>484 string NTLDR\ is\ compressed
+>>>>>429 string Insert\ a\ system\ diskette\040
+>>>>>>454 string and\ restart\r\nthe\ system.\r \b, Microsoft Windows XP Bootloader NTFS
+# DOS loader variants different languages,offsets
+>472 ubyte&0xDF >0
+>>389 string Invalid\ system\ disk\xFF\r\n
+>>>411 string Disk\ I/O\ error
+>>>>428 string Replace\ the\ disk,\ and\040
+>>>>>455 string press\ any\ key \b, Microsoft Windows 98 Bootloader
+#IO.SYS
+>>>>>>472 ubyte&0xDF >0
+>>>>>>>472 string x \b %-.2s
+>>>>>>>>474 ubyte&0xDF >0
+>>>>>>>>>474 string x \b%-.5s
+>>>>>>>>>>479 ubyte&0xDF >0
+>>>>>>>>>>>479 string x \b%-.1s
+>>>>>>>480 ubyte&0xDF >0
+>>>>>>>>480 string x \b.%-.3s
+#MSDOS.SYS
+>>>>>>>483 ubyte&0xDF >0 \b+
+>>>>>>>>483 string x \b%-.5s
+>>>>>>>>>488 ubyte&0xDF >0
+>>>>>>>>>>488 string x \b%-.3s
+>>>>>>>>491 ubyte&0xDF >0
+>>>>>>>>>491 string x \b.%-.3s
+#
+>>390 string Invalid\ system\ disk\xFF\r\n
+>>>412 string Disk\ I/O\ error\xFF\r\n
+>>>>429 string Replace\ the\ disk,\ and\040
+>>>>>451 string then\ press\ any\ key\r \b, Microsoft Windows 98 Bootloader
+>>388 string Ungueltiges\ System\ \xFF\r\n
+>>>410 string E/A-Fehler\ \ \ \ \xFF\r\n
+>>>>427 string Datentraeger\ wechseln\ und\040
+>>>>>453 string Taste\ druecken\r \b, Microsoft Windows 95/98/ME Bootloader (german)
+#WINBOOT.SYS only not spaces (0xDF)
+>>>>>>497 ubyte&0xDF >0
+>>>>>>>497 string x %-.5s
+>>>>>>>>502 ubyte&0xDF >0
+>>>>>>>>>502 string x \b%-.1s
+>>>>>>>>>>503 ubyte&0xDF >0
+>>>>>>>>>>>503 string x \b%-.1s
+>>>>>>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>>>>>>504 string x \b%-.1s
+>>>>>>505 ubyte&0xDF >0
+>>>>>>>505 string x \b.%-.3s
+#IO.SYS
+>>>>>>472 ubyte&0xDF >0 or
+>>>>>>>472 string x \b %-.2s
+>>>>>>>>474 ubyte&0xDF >0
+>>>>>>>>>474 string x \b%-.5s
+>>>>>>>>>>479 ubyte&0xDF >0
+>>>>>>>>>>>479 string x \b%-.1s
+>>>>>>>480 ubyte&0xDF >0
+>>>>>>>>480 string x \b.%-.3s
+#MSDOS.SYS
+>>>>>>>483 ubyte&0xDF >0 \b+
+>>>>>>>>483 string x \b%-.5s
+>>>>>>>>>488 ubyte&0xDF >0
+>>>>>>>>>>488 string x \b%-.3s
+>>>>>>>>491 ubyte&0xDF >0
+>>>>>>>>>491 string x \b.%-.3s
+#
+>>390 string Ungueltiges\ System\ \xFF\r\n
+>>>412 string E/A-Fehler\ \ \ \ \xFF\r\n
+>>>>429 string Datentraeger\ wechseln\ und\040
+>>>>>455 string Taste\ druecken\r \b, Microsoft Windows 95/98/ME Bootloader (German)
+#WINBOOT.SYS only not spaces (0xDF)
+>>>>>>497 ubyte&0xDF >0
+>>>>>>>497 string x %-.7s
+>>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>>504 string x \b%-.1s
+>>>>>>505 ubyte&0xDF >0
+>>>>>>>505 string x \b.%-.3s
+#IO.SYS
+>>>>>>472 ubyte&0xDF >0 or
+>>>>>>>472 string x \b %-.2s
+>>>>>>>>474 ubyte&0xDF >0
+>>>>>>>>>474 string x \b%-.6s
+>>>>>>>480 ubyte&0xDF >0
+>>>>>>>>480 string x \b.%-.3s
+#MSDOS.SYS
+>>>>>>>483 ubyte&0xDF >0 \b+
+>>>>>>>>483 string x \b%-.5s
+>>>>>>>>>488 ubyte&0xDF >0
+>>>>>>>>>>488 string x \b%-.3s
+>>>>>>>>491 ubyte&0xDF >0
+>>>>>>>>>491 string x \b.%-.3s
+#
+>>389 string Ungueltiges\ System\ \xFF\r\n
+>>>411 string E/A-Fehler\ \ \ \ \xFF\r\n
+>>>>428 string Datentraeger\ wechseln\ und\040
+>>>>>454 string Taste\ druecken\r \b, Microsoft Windows 95/98/ME Bootloader (GERMAN)
+# DOS names like IO.SYS,WINBOOT.SYS,MSDOS.SYS,WINBOOT.INI are 8 right space padded bytes+3 bytes
+>>>>>>472 string x %-.2s
+>>>>>>>474 ubyte&0xDF >0
+>>>>>>>>474 string x \b%-.5s
+>>>>>>>>479 ubyte&0xDF >0
+>>>>>>>>>479 string x \b%-.1s
+>>>>>>480 ubyte&0xDF >0
+>>>>>>>480 string x \b.%-.3s
+>>>>>>483 ubyte&0xDF >0 \b+
+>>>>>>>483 string x \b%-.5s
+>>>>>>>488 ubyte&0xDF >0
+>>>>>>>>488 string x \b%-.2s
+>>>>>>>>490 ubyte&0xDF >0
+>>>>>>>>>490 string x \b%-.1s
+>>>>>>>491 ubyte&0xDF >0
+>>>>>>>>491 string x \b.%-.3s
+>479 ubyte&0xDF >0
+>>416 string Kein\ System\ oder\040
+>>>433 string Laufwerksfehler
+>>>>450 string Wechseln\ und\ Taste\ dr\201cken \b, Microsoft DOS Bootloader (german)
+#IO.SYS
+>>>>>479 string x \b %-.2s
+>>>>>>481 ubyte&0xDF >0
+>>>>>>>481 string x \b%-.6s
+>>>>>487 ubyte&0xDF >0
+>>>>>>487 string x \b.%-.3s
+#MSDOS.SYS
+>>>>>>490 ubyte&0xDF >0 \b+
+>>>>>>>490 string x \b%-.5s
+>>>>>>>>495 ubyte&0xDF >0
+>>>>>>>>>495 string x \b%-.3s
+>>>>>>>498 ubyte&0xDF >0
+>>>>>>>>498 string x \b.%-.3s
+#
+>376 search/41 Non-System\ disk\ or\040
+>>395 search/41 disk\ error\r
+>>>407 search/41 Replace\ and\040
+>>>>419 search/41 press\ \b,
+>>>>419 search/41 strike\ \b, old
+>>>>426 search/41 any\ key\ when\ ready\r MS or PC-DOS bootloader
+#449 Disk\ Boot\ failure\r MS 3.21
+#466 Boot\ Failure\r MS 3.30
+>>>>>468 search/18 \0
+#IO.SYS,IBMBIO.COM
+>>>>>>&0 string x \b %-.2s
+>>>>>>>&-20 ubyte&0xDF >0
+>>>>>>>>&-1 string x \b%-.4s
+>>>>>>>>>&-16 ubyte&0xDF >0
+>>>>>>>>>>&-1 string x \b%-.2s
+>>>>>>&8 ubyte&0xDF >0 \b.
+>>>>>>>&-1 string x \b%-.3s
+#MSDOS.SYS,IBMDOS.COM
+>>>>>>&11 ubyte&0xDF >0 \b+
+>>>>>>>&-1 string x \b%-.5s
+>>>>>>>>&-6 ubyte&0xDF >0
+>>>>>>>>>&-1 string x \b%-.1s
+>>>>>>>>>>&-5 ubyte&0xDF >0
+>>>>>>>>>>>&-1 string x \b%-.2s
+>>>>>>>&7 ubyte&0xDF >0 \b.
+>>>>>>>>&-1 string x \b%-.3s
+>441 string Cannot\ load\ from\ harddisk.\n\r
+>>469 string Insert\ Systemdisk\040
+>>>487 string and\ press\ any\ key.\n\r \b, MS (2.11) DOS bootloader
+#>43 string \224R-LOADER\ \ SYS =label
+>54 string SYS
+>>324 string VASKK
+>>>495 string NEWLDR\0 \b, DR-DOS Bootloader (LOADER.SYS)
+#
+>98 string Press\ a\ key\ to\ retry\0\r
+>>120 string Cannot\ find\ file\ \0\r
+>>>139 string Disk\ read\ error\0\r
+>>>>156 string Loading\ ...\0 \b, DR-DOS (3.41) Bootloader
+#DRBIOS.SYS
+>>>>>44 ubyte&0xDF >0
+>>>>>>44 string x \b %-.6s
+>>>>>>>50 ubyte&0xDF >0
+>>>>>>>>50 string x \b%-.2s
+>>>>>>52 ubyte&0xDF >0
+>>>>>>>52 string x \b.%-.3s
+#
+>70 string IBMBIO\ \ COM
+>>472 string Cannot\ load\ DOS!\040
+>>>489 string Any\ key\ to\ retry \b, DR-DOS Bootloader
+>>471 string Cannot\ load\ DOS\040
+>>487 string press\ key\ to\ retry \b, Open-DOS Bootloader
+#??
+>444 string KERNEL\ \ SYS
+>>314 string BOOT\ error! \b, FREE-DOS Bootloader
+>499 string KERNEL\ \ SYS
+>>305 string BOOT\ err!\0 \b, Free-DOS Bootloader
+>449 string KERNEL\ \ SYS
+>>319 string BOOT\ error! \b, FREE-DOS 0.5 Bootloader
+#
+>449 string Loading\ FreeDOS
+>>0x1AF ulelong >0 \b, FREE-DOS 0.95,1.0 Bootloader
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+#
+>331 string Error!.0 \b, FREE-DOS 1.0 bootloader
+#
+>125 string Loading\ FreeDOS...\r
+>>311 string BOOT\ error!\r \b, FREE-DOS bootloader
+>>>441 ubyte&0xDF >0
+>>>>441 string x \b %-.6s
+>>>>>447 ubyte&0xDF >0
+>>>>>>447 string x \b%-.1s
+>>>>>>>448 ubyte&0xDF >0
+>>>>>>>>448 string x \b%-.1s
+>>>>449 ubyte&0xDF >0
+>>>>>449 string x \b.%-.3s
+>124 string FreeDOS\0
+>>331 string \ err\0 \b, FREE-DOS BETa 0.9 Bootloader
+# DOS names like KERNEL.SYS,KERNEL16.SYS,KERNEL32.SYS,METAKERN.SYS are 8 right space padded bytes+3 bytes
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+>>333 string \ err\0 \b, FREE-DOS BEta 0.9 Bootloader
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+>>334 string \ err\0 \b, FREE-DOS Beta 0.9 Bootloader
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+>336 string Error!\040
+>>343 string Hit\ a\ key\ to\ reboot. \b, FREE-DOS Beta 0.9sr1 Bootloader
+>>>497 ubyte&0xDF >0
+>>>>497 string x \b %-.6s
+>>>>>503 ubyte&0xDF >0
+>>>>>>503 string x \b%-.1s
+>>>>>>>504 ubyte&0xDF >0
+>>>>>>>>504 string x \b%-.1s
+>>>>505 ubyte&0xDF >0
+>>>>>505 string x \b.%-.3s
+# added by Joerg Jenderek
+# https://www.visopsys.org/
+# https://partitionlogic.org.uk/
+# OEM-ID=Visopsys
+>478 ulelong 0
+>>(1.b+326) string I/O\ Error\ reading\040
+>>>(1.b+344) string Visopsys\ loader\r
+>>>>(1.b+361) string Press\ any\ key\ to\ continue.\r \b, Visopsys loader
+# http://alexfru.chat.ru/epm.html#bootprog
+>494 ubyte >0x4D
+>>495 string >E
+>>>495 string <S
+#OEM-ID is not reliable
+>>>>3 string BootProg
+# It just looks for a program file name at the root directory
+# and loads corresponding file with following execution.
+# DOS names like STARTUP.BIN,STARTUPC.COM,STARTUPE.EXE are 8 right space padded bytes+3 bytes
+>>>>499 ubyte&0xDF >0 \b, COM/EXE Bootloader
+>>>>>499 use DOS-filename
+#If the boot sector fails to read any other sector,
+#it prints a very short message ("RE") to the screen and hangs the computer.
+#If the boot sector fails to find needed program in the root directory,
+#it also hangs with another message ("NF").
+>>>>>492 string RENF \b, FAT (12 bit)
+>>>>>495 string RENF \b, FAT (16 bit)
+#If the boot sector fails to read any other sector,
+#it prints a very short message ("RE") to the screen and hangs the computer.
+# x86 bootloader end
+
+# added by Joerg Jenderek at Feb 2013 according to https://thestarman.pcministry.com/asm/mbr/MSWIN41.htm#FSINFO
+# and https://en.wikipedia.org/wiki/File_Allocation_Table#FS_Information_Sector
+>0 string RRaA
+>>0x1E4 string rrAa \b, FSInfosector
+#>>0x1FC uleshort =0 SHOULD BE ZERO
+>>>0x1E8 ulelong <0xffffffff \b, %u free clusters
+>>>0x1EC ulelong <0xffffffff \b, last allocated cluster %u
+
+# updated by Joerg Jenderek at Sep 2007
+>3 ubyte 0
+#no active flag
+>>446 ubyte 0
+# partition 1 not empty
+>>>450 ubyte >0
+# partitions 3,4 empty
+>>>>482 ubyte 0
+>>>>>498 ubyte 0
+# partition 2 ID=0,5,15
+>>>>>>466 ubyte <0x10
+>>>>>>>466 ubyte 0x05 \b, extended partition table
+>>>>>>>466 ubyte 0x0F \b, extended partition table (LBA)
+>>>>>>>466 ubyte 0x0 \b, extended partition table (last)
+
+# DOS x86 sector separated and moved from "DOS/MBR boot sector" by Joerg Jenderek at May 2011
+
+>0x200 lelong 0x82564557 \b, BSD disklabel
+
+# by Joerg Jenderek at Apr 2013
+# Print the DOS filenames from directory entry form with 8 right space padded bytes + 3 bytes for extension
+# like IO.SYS. MSDOS.SYS , KERNEL.SYS , DRBIO.SYS
+0 name DOS-filename
+# space=0x20 (00100000b) means empty
+>0 ubyte&0xDF >0
+>>0 ubyte x \b%c
+>>>1 ubyte&0xDF >0
+>>>>1 ubyte x \b%c
+>>>>>2 ubyte&0xDF >0
+>>>>>>2 ubyte x \b%c
+>>>>>>>3 ubyte&0xDF >0
+>>>>>>>>3 ubyte x \b%c
+>>>>>>>>>4 ubyte&0xDF >0
+>>>>>>>>>>4 ubyte x \b%c
+>>>>>>>>>>>5 ubyte&0xDF >0
+>>>>>>>>>>>>5 ubyte x \b%c
+>>>>>>>>>>>>>6 ubyte&0xDF >0
+>>>>>>>>>>>>>>6 ubyte x \b%c
+>>>>>>>>>>>>>>>7 ubyte&0xDF >0
+>>>>>>>>>>>>>>>>7 ubyte x \b%c
+# DOS filename extension
+>>8 ubyte&0xDF >0 \b.
+>>>8 ubyte x \b%c
+>>>>9 ubyte&0xDF >0
+>>>>>9 ubyte x \b%c
+>>>>>>10 ubyte&0xDF >0
+>>>>>>>10 ubyte x \b%c
+# Print 2 following DOS filenames from directory entry form
+# like IO.SYS+MSDOS.SYS or ibmbio.com+ibmdos.com
+0 name 2xDOS-filename
+# display 1 space
+>0 ubyte x \b
+>0 use DOS-filename
+>11 ubyte x \b+
+>11 use DOS-filename
+
+# https://en.wikipedia.org/wiki/Master_boot_record#PTE
+# display standard partition table
+0 name partition-table
+#>0 ubyte x PARTITION-TABLE
+# test and display 1st til 4th partition table entry
+>0 use partition-entry-test
+>16 use partition-entry-test
+>32 use partition-entry-test
+>48 use partition-entry-test
+# test for entry of partition table
+0 name partition-entry-test
+# partition type ID > 0
+>4 ubyte >0
+# active flag 0
+>>0 ubyte 0
+>>>0 use partition-entry
+# active flag 0x80, 0x81, ...
+>>0 ubyte >0x7F
+>>>0 use partition-entry
+# Print entry of partition table
+0 name partition-entry
+# partition type ID > 0
+>4 ubyte >0 \b; partition
+>>64 leshort 0xAA55 1
+>>48 leshort 0xAA55 2
+>>32 leshort 0xAA55 3
+>>16 leshort 0xAA55 4
+>>4 ubyte x : ID=%#x
+>>0 ubyte&0x80 0x80 \b, active
+>>0 ubyte >0x80 %#x
+>>1 ubyte x \b, start-CHS (
+>>1 use partition-chs
+>>5 ubyte x \b), end-CHS (
+>>5 use partition-chs
+>>8 ulelong x \b), startsector %u
+>>12 ulelong x \b, %u sectors
+# Print cylinder,head,sector (CHS) of partition entry
+0 name partition-chs
+# cylinder
+>1 ubyte x \b0x
+>1 ubyte&0xC0 0x40 \b1
+>1 ubyte&0xC0 0x80 \b2
+>1 ubyte&0xC0 0xC0 \b3
+>2 ubyte x \b%x
+# head
+>0 ubyte x \b,%u
+# sector
+>1 ubyte&0x3F x \b,%u
+
+# FATX
+0 string FATX FATX filesystem data
+
+# romfs filesystems - Juan Cespedes <cespedes@debian.org>
+0 string -rom1fs- romfs filesystem, version 1
+>8 belong x %d bytes,
+>16 string x named %s.
+
+# netboot image - Juan Cespedes <cespedes@debian.org>
+0 lelong 0x1b031336L Netboot image,
+>4 lelong&0xFFFFFF00 0
+>>4 lelong&0x100 0x000 mode 2
+>>4 lelong&0x100 0x100 mode 3
+>4 lelong&0xFFFFFF00 !0 unknown mode
+
+0x18b string OS/2 OS/2 Boot Manager
+
+# updated by Joerg Jenderek at Oct 2008 and Sep 2012
+# https://syslinux.zytor.com/iso.php
+# tested with versions 1.47,1.48,1.49,1.50,1.62,1.76,2.00,2.10;3.00,3.11,3.31,;3.70,3.71,3.73,3.75,3.80,3.82,3.84,3.86,4.01,4.03 and 4.05
+# assembler instructions: cli;jmp 0:7Cyy (yy=0x40,0x5e,0x6c,0x6e,0x77);nop;nop
+0 ulequad&0x909000007cc0eafa 0x909000007c40eafa
+>631 search/689 ISOLINUX\ isolinux Loader
+>>&0 string x (version %-4.4s)
+# https://syslinux.zytor.com/pxe.php
+# assembler instructions: jmp 7C05
+0 ulelong 0x007c05ea pxelinux loader (version 2.13 or older)
+# assembler instructions: pushfd;pushad
+0 ulelong 0x60669c66 pxelinux loader
+# assembler instructions: jmp 05
+0 ulelong 0xc00005ea pxelinux loader (version 3.70 or newer)
+# https://syslinux.zytor.com/wiki/index.php/SYSLINUX
+0 string LDLINUX\ SYS\ SYSLINUX loader
+>12 string x (older version %-4.4s)
+0 string \r\nSYSLINUX\ SYSLINUX loader
+>11 string x (version %-4.4s)
+# syslinux updated and separated from "DOS/MBR boot sector" by Joerg Jenderek at Sep 2012
+# assembler instructions: jmp yy (yy=0x3c,0x58);nop;"SYSLINUX"
+0 ulelong&0x80909bEB 0x009018EB
+# OEM-ID not always "SYSLINUX"
+>434 search/47 Boot\ failed
+# followed by \r\n\0 or :\
+>>482 search/132 \0LDLINUX\ SYS Syslinux bootloader (version 2.13 or older)
+>>1 ubyte 0x58 Syslinux bootloader (version 3.0-3.9)
+>459 search/30 Boot\ error\r\n\0
+>>1 ubyte 0x58 Syslinux bootloader (version 3.10 or newer)
+# SYSLINUX MBR updated and separated from "DOS/MBR boot sector" by Joerg Jenderek at Sep 2012
+# assembler instructions: mov di,0600h;mov cx,0100h
+16 search/4 \xbf\x00\x06\xb9\x00\x01
+# to display SYSLINUX MBR (36) before old DOS/MBR boot sector one with partition table (strength=50+21)
+!:strength +36
+>94 search/249 Missing\ operating\ system
+# followed by \r for versions older 3.35 , .\r for versions newer 3.52 and point for other
+# skip Ranish MBR
+>>408 search/4 HD1/\0
+>>408 default x
+>>>250 search/118 \0Operating\ system\ load SYSLINUX MBR
+# followed by "ing " or space
+>>>>292 search/98 error
+>>>>>&0 string \r (version 3.35 or older)
+>>>>>&0 string .\r (version 3.52 or newer)
+>>>>>&0 default x (version 3.36-3.51 )
+>368 search/106 \0Disk\ error\ on\ boot\r\n SYSLINUX GPT-MBR
+>>156 search/10 \0Boot\ partition\ not\ found\r\n
+>>>270 search/10 \0OS\ not\ bootable\r\n (version 3.86 or older)
+>>174 search/10 \0Missing\ OS\r\n
+>>>189 search/10 \0Multiple\ active\ partitions\r\n (version 4.00 or newer)
+# SYSLINUX END
+
+# NetBSD mbr variants (master-boot-code version 1.22) added by Joerg Jenderek at Nov 2012
+# assembler instructions: xor ax,ax;mov ax,ss;mov sp,0x7c00;mov ax,
+0 ubequad 0x31c08ed0bc007c8e
+# mbr_bootsel magic before partition table not reliable with small ipl fragments
+#>444 uleshort 0xb5e1
+>0004 uleshort x
+# ERRorTeXT
+>>181 search/166 Error\ \0\r\n NetBSD mbr
+# NT Drive Serial Number https://thestarman.pcministry.com/asm/mbr/Win2kmbr.htm#DS
+>>>0x1B8 ubelong >0 \b,Serial %#-.8x
+# BOOTSEL definitions contains assembler instructions: int 0x13;pop dx;push dx;push dx
+>>>0xbb search/71 \xcd\x13\x5a\x52\x52 \b,bootselector
+# BOOT_EXTENDED definitions contains assembler instructions:
+# xchg ecx,edx;addl ecx,edx;movw lba_info,si;movb 0x42,ah;pop dx;push dx;int 0x13
+>>>0x96 search/1 \x66\x87\xca\x66\x01\xca\x66\x89\x16\x3a\x07\xbe\x32\x07\xb4\x42\x5a\x52\xcd\x13 \b,boot extended
+# COM_PORT_VAL definitions contains assembler instructions: outb al,dx;add 5,dl;inb %dx;test 0x40,al
+>>>0x130 search/55 \xee\x80\xc2\x05\xec\xa8\x40 \b,serial IO
+# not TERSE_ERROR
+>>>196 search/106 No\ active\ partition\0
+>>>>&0 string Disk\ read\ error\0
+>>>>>&0 string No\ operating\ system\0 \b,verbose
+# not NO_CHS definitions contains assembler instructions: pop dx;push dx;movb $8,ah;int0x13
+>>>0x7d search/7 \x5a\x52\xb4\x08\xcd\x13 \b,CHS
+# not NO_LBA_CHECK definitions contains assembler instructions: movw 0x55aa,bx;movb 0x41,ah;pop dx;push dx;int 0x13
+>>>0xa4 search/84 \xbb\xaa\x55\xb4\x41\x5a\x52\xcd\x13 \b,LBA-check
+# assembler instructions: movw nametab,bx
+>>>0x26 search/21 \xBB\x94\x07
+# not NO_BANNER definitions contains assembler instructions: mov banner,si;call message_crlf
+>>>>&-9 ubequad&0xBE00f0E800febb94 0xBE0000E80000bb94
+>>>>>181 search/166 Error\ \0
+# "a: disk" , "Fn: diskn" or "NetBSD MBR boot"
+>>>>>>&3 string x \b,"%s"
+>>>446 use partition-table
+# Andrea Mazzoleni AdvanceCD mbr loader of http://advancemame.sourceforge.net/boot-readme.html
+# added by Joerg Jenderek at Nov 2012 for versions 1.3 - 1.4
+# assembler instructions: jmp short 0x58;nop;ASCII
+0 ubequad&0xeb58908000000000 0xeb58900000000000
+# assembler instructions: cli;xor ax,ax;mov ds,ax;mov es,ax;mov ss,
+>(1.b+2) ubequad 0xfa31c08ed88ec08e
+# Error messages at end of code
+>>376 string No\ operating\ system\r\n\0
+>>>398 string Disk\ error\r\n\0FDD\0HDD\0
+>>>>419 string \ EBIOS\r\n\0 AdvanceMAME mbr
+
+# Neil Turton mbr loader variant of https://www.chiark.greenend.org.uk/~neilt/mbr/
+# added by Joerg Jenderek at Mar 2011 for versions 1.0.0 - 1.1.11
+# for 1st version assembler instructions: cld;xor ax,ax;mov DS,ax;MOV ES,AX;mov SI,
+# or cld;xor ax,ax;mov SS,ax;XOR SP,SP;mov DS,
+0 ulequad&0xcE1b40D48EC031FC 0x8E0000D08EC031FC
+# pointer to the data starting with Neil Turton signature string
+>(0x1BC.s) string NDTmbr
+>>&-14 string 1234F\0 Turton mbr (
+# parameters also viewed by install-mbr --list
+>>>(0x1BC.s+7) ubyte x \b%u<=
+>>>(0x1BC.s+9) ubyte x \bVersion<=%u
+#>>>(0x1BC.s+8) ubyte x asm_flag_%x
+>>>(0x1BC.s+8) ubyte&1 1 \b,Y2K-Fix
+# variant used by testdisk of https://www.cgsecurity.org/wiki/Menu_MBRCode
+>>>(0x1BC.s+8) ubyte&2 2 \b,TestDisk
+#0x1~1,..,0x8~4,0x10~F,0x80~A enabled
+#>>>(0x1BC.s+10) ubyte x \b,flags %#x
+#0x0~1,0x1~2,...,0x3~4,0x4~F,0x7~D default boot
+#>>>(0x1BC.s+11) ubyte x \b,cfg_def %#x
+# for older versions
+>>>(0x1BC.s+9) ubyte <2
+#>>>>(0x1BC.s+12) ubyte 18 \b,%hhu/18 seconds
+>>>>(0x1BC.s+12) ubyte !18 \b,%u/18 seconds
+# floppy A: or B:
+>>>>(0x1BC.s+13) ubyte <2 \b,floppy %#x
+>>>>(0x1BC.s+13) ubyte >1
+# 1st hard disc
+#>>>>>(0x1BC.s+13) ubyte 0x80 \b,drive %#x
+# not 1st hard disc
+>>>>>(0x1BC.s+13) ubyte !0x80 \b,drive %#x
+# for version >= 2 maximal timeout can be 65534
+>>>(0x1BC.s+9) ubyte >1
+#>>>>(0x1BC.s+12) uleshort 18 \b,%u/18 seconds
+>>>>(0x1BC.s+12) uleshort !18 \b,%u/18 seconds
+# floppy A: or B:
+>>>>(0x1BC.s+14) ubyte <2 \b,floppy %#x
+>>>>(0x1BC.s+14) ubyte >1
+# 1st hard disc
+#>>>>>(0x1BC.s+14) ubyte 0x80 \b,drive %#x
+# not 1st hard disc
+>>>>>(0x1BC.s+14) ubyte !0x80 \b,drive %#x
+>>>0 ubyte x \b)
+
+# added by Joerg Jenderek
+# In the second sector (+0x200) are variables according to grub-0.97/stage2/asm.S or
+# grub-1.94/kern/i386/pc/startup.S
+# https://www.gnu.org/software/grub/manual/grub.html#Embedded-data
+# usual values are marked with comments to get only information of strange GRUB loaders
+0x200 uleshort 0x70EA
+# found only version 3.{1,2}
+>0x206 ubeshort >0x0300
+# GRUB version (0.5.)95,0.93,0.94,0.96,0.97 > "00"
+>>0x212 ubyte >0x29
+>>>0x213 ubyte >0x29
+# not iso9660_stage1_5
+#>>>0 ulelong&0x00BE5652 0x00BE5652
+>>>>0x213 ubyte >0x29 GRand Unified Bootloader
+# config_file for stage1_5 is 0xffffffff + default "/boot/grub/stage2"
+>>>>0x217 ubyte 0xFF stage1_5
+>>>>0x217 ubyte <0xFF stage2
+>>>>0x206 ubyte x \b version %u
+>>>>0x207 ubyte x \b.%u
+# module_size for 1.94
+>>>>0x208 ulelong <0xffffff \b, installed partition %u
+#>>>>0x208 ulelong =0xffffff \b, %lu (default)
+>>>>0x208 ulelong >0xffffff \b, installed partition %u
+# GRUB 0.5.95 unofficial
+>>>>0x20C ulelong&0x2E300000 0x2E300000
+# 0=stage2 1=ffs 2=e2fs 3=fat 4=minix 5=reiserfs
+>>>>>0x20C ubyte x \b, identifier %#x
+#>>>>>0x20D ubyte =0 \b, LBA flag %#x (default)
+>>>>>0x20D ubyte >0 \b, LBA flag %#x
+# GRUB version as string
+>>>>>0x20E string >\0 \b, GRUB version %-s
+# for stage1_5 is 0xffffffff + config_file "/boot/grub/stage2" default
+>>>>>>0x215 ulong 0xffffffff
+>>>>>>>0x219 string >\0 \b, configuration file %-s
+>>>>>>0x215 ulong !0xffffffff
+>>>>>>>0x215 string >\0 \b, configuration file %-s
+# newer GRUB versions
+>>>>0x20C ulelong&0x2E300000 !0x2E300000
+##>>>>>0x20C ulelong =0 \b, saved entry %d (usual)
+>>>>>0x20C ulelong >0 \b, saved entry %d
+# for 1.94 contains kernel image size
+# for 0.93,0.94,0.96,0.97
+# 0=stage2 1=ffs 2=e2fs 3=fat 4=minix 5=reiserfs 6=vstafs 7=jfs 8=xfs 9=iso9660 a=ufs2
+>>>>>0x210 ubyte x \b, identifier %#x
+# The flag for LBA forcing is in most cases 0
+#>>>>>0x211 ubyte =0 \b, LBA flag %#x (default)
+>>>>>0x211 ubyte >0 \b, LBA flag %#x
+# GRUB version as string
+>>>>>0x212 string >\0 \b, GRUB version %-s
+# for stage1_5 is 0xffffffff + config_file "/boot/grub/stage2" default
+>>>>>0x217 ulong 0xffffffff
+>>>>>>0x21b string >\0 \b, configuration file %-s
+>>>>>0x217 ulong !0xffffffff
+>>>>>>0x217 string >\0 \b, configuration file %-s
+
+# DOS x86 sector updated and separated from "DOS/MBR boot sector" by Joerg Jenderek at May 2011
+# JuMP short bootcodeoffset NOP assembler instructions will usually be EB xx 90
+# over BIOS parameter block (BPB)
+# https://thestarman.pcministry.com/asm/2bytejumps.htm#FWD
+# older drives may use Near JuMP instruction E9 xx xx
+# minimal short forward jump found 0x29 for bootloaders or 0x0
+# maximal short forward jump is 0x7f
+# OEM-ID is empty or contain readable bytes
+0 ulelong&0x804000E9 0x000000E9
+!:strength +60
+# mtools-3.9.8/msdos.h
+# usual values are marked with comments to get only information of strange FAT systems
+# valid sectorsize must be a power of 2 from 32 to 32768
+>11 uleshort&0x001f 0
+>>11 uleshort <32769
+>>>11 uleshort >31
+>>>>21 ubyte&0xf0 0xF0
+>>>>>0 ubyte 0xEB DOS/MBR boot sector
+>>>>>>1 ubyte x \b, code offset %#x+2
+>>>>>0 ubyte 0xE9
+>>>>>>1 uleshort x \b, code offset %#x+3
+>>>>>3 string >\0 \b, OEM-ID "%-.8s"
+#http://mirror.href.com/thestarman/asm/debug/debug2.htm#IHC
+>>>>>>8 string IHC \b cached by Windows 9M
+>>>>>11 uleshort >512 \b, Bytes/sector %u
+#>>>>>11 uleshort =512 \b, Bytes/sector %u=512 (usual)
+>>>>>11 uleshort <512 \b, Bytes/sector %u
+>>>>>13 ubyte >1 \b, sectors/cluster %u
+#>>>>>13 ubyte =1 \b, sectors/cluster %u (usual on Floppies)
+# for lazy FAT32 implementation like Transcend digital photo frame PF830
+>>>>>82 string/c fat32
+>>>>>>14 uleshort !32 \b, reserved sectors %u
+#>>>>>>14 uleshort =32 \b, reserved sectors %u (usual Fat32)
+>>>>>82 string/c !fat32
+>>>>>>14 uleshort >1 \b, reserved sectors %u
+#>>>>>>14 uleshort =1 \b, reserved sectors %u (usual FAT12,FAT16)
+#>>>>>>14 uleshort 0 \b, reserved sectors %u (usual NTFS)
+>>>>>16 ubyte >2 \b, FATs %u
+#>>>>>16 ubyte =2 \b, FATs %u (usual)
+>>>>>16 ubyte =1 \b, FAT %u
+>>>>>16 ubyte >0
+>>>>>17 uleshort >0 \b, root entries %u
+#>>>>>17 uleshort =0 \b, root entries %hu=0 (usual Fat32)
+>>>>>19 uleshort >0 \b, sectors %u (volumes <=32 MB)
+#>>>>>19 uleshort =0 \b, sectors %hu=0 (usual Fat32)
+>>>>>21 ubyte >0xF0 \b, Media descriptor %#x
+#>>>>>21 ubyte =0xF0 \b, Media descriptor %#x (usual floppy)
+>>>>>21 ubyte <0xF0 \b, Media descriptor %#x
+>>>>>22 uleshort >0 \b, sectors/FAT %u
+#>>>>>22 uleshort =0 \b, sectors/FAT %hu=0 (usual Fat32)
+>>>>>24 uleshort x \b, sectors/track %u
+>>>>>26 ubyte >2 \b, heads %u
+#>>>>>26 ubyte =2 \b, heads %u (usual floppy)
+>>>>>26 ubyte =1 \b, heads %u
+# valid only for sector sizes with more then 32 Bytes
+>>>>>11 uleshort >32
+# https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Extended_BIOS_Parameter_Block
+# skip for values 2,2Ah,70h,73h,DFh
+# and continue for extended boot signature values 0,28h,29h,80h
+>>>>>>38 ubyte&0x56 =0
+>>>>>>>28 ulelong >0 \b, hidden sectors %u
+#>>>>>>>28 ulelong =0 \b, hidden sectors %u (usual floppy)
+>>>>>>>32 ulelong >0 \b, sectors %u (volumes > 32 MB)
+#>>>>>>>32 ulelong =0 \b, sectors %u (volumes > 32 MB)
+# FAT<32 bit specific
+>>>>>>>82 string/c !fat32
+#>>>>>>>>36 ubyte 0x80 \b, physical drive %#x=0x80 (usual harddisk)
+#>>>>>>>>36 ubyte 0 \b, physical drive %#x=0 (usual floppy)
+>>>>>>>>36 ubyte !0x80
+>>>>>>>>>36 ubyte !0 \b, physical drive %#x
+# VGA-copy CRC or
+# in Windows NT bit 0 is a dirty flag to request chkdsk at boot time. bit 1 requests surface scan too
+>>>>>>>>37 ubyte >0 \b, reserved %#x
+#>>>>>>>>37 ubyte =0 \b, reserved %#x
+# extended boot signature value is 0x80 for NTFS, 0x28 or 0x29 for others
+>>>>>>>>38 ubyte !0x29 \b, dos < 4.0 BootSector (%#x)
+>>>>>>>>38 ubyte&0xFE =0x28
+>>>>>>>>>39 ulelong x \b, serial number %#x
+>>>>>>>>38 ubyte =0x29
+>>>>>>>>>43 string <NO\ NAME \b, label: "%11.11s"
+>>>>>>>>>43 string >NO\ NAME \b, label: "%11.11s"
+>>>>>>>>>43 string =NO\ NAME \b, unlabeled
+# there exist some old floppies without word FAT at offset 54
+# a word like "FATnm " is only a hint for a FAT size on nm-bits
+# Normally the number of clusters is calculated by the values of BPP.
+# if it is small enough FAT is 12 bit, if it is too big enough FAT is 32 bit,
+# otherwise FAT is 16 bit.
+# http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/determining-fat-widths.html
+>>>>>82 string/c !fat32
+>>>>>>54 string FAT12 \b, FAT (12 bit)
+>>>>>>54 string FAT16 \b, FAT (16 bit)
+>>>>>>54 default x
+# determinate FAT bit size by media descriptor
+# small floppies implies FAT12
+>>>>>>>21 ubyte <0xF0 \b, FAT (12 bit by descriptor)
+# with media descriptor F0h floppy or maybe superfloppy with FAT16
+>>>>>>>21 ubyte =0xF0
+# superfloppy (many sectors) implies FAT16
+>>>>>>>>32 ulelong >0xFFFF \b, FAT (16 bit by descriptor+sectors)
+# no superfloppy with media descriptor F0h implies FAT12
+>>>>>>>>32 default x \b, FAT (12 bit by descriptor+sectors)
+# with media descriptor F8h floppy or hard disc with FAT12 or FAT16
+>>>>>>>21 ubyte =0xF8
+# 360 KiB with media descriptor F8h, 9 sectors per track ,single sided floppy implies FAT12
+>>>>>>>>19 ubequad 0xd002f80300090001 \b, FAT (12 bit by descriptor+geometry)
+# hard disc with FAT12 or FAT16
+>>>>>>>>19 default x \b, FAT (1Y bit by descriptor)
+# with media descriptor FAh floppy, RAM disc with FAT12 or FAT16 or Tandy hard disc
+>>>>>>>21 ubyte =0xFA
+# 320 KiB with media descriptor FAh, 8 sectors per track ,single sided floppy implies FAT12
+>>>>>>>>19 ubequad 0x8002fa0200080001 \b, FAT (12 bit by descriptor+geometry)
+# RAM disc with FAT12 or FAT16 or Tandy hard disc
+>>>>>>>>19 default x \b, FAT (1Y bit by descriptor)
+# others are floppy
+>>>>>>>21 default x \b, FAT (12 bit by descriptor)
+# FAT32 bit specific
+>>>>>82 string/c fat32 \b, FAT (32 bit)
+>>>>>>36 ulelong x \b, sectors/FAT %u
+# https://technet.microsoft.com/en-us/library/cc977221.aspx
+>>>>>>40 uleshort >0 \b, extension flags %#x
+#>>>>>>40 uleshort =0 \b, extension flags %hu
+>>>>>>42 uleshort >0 \b, fsVersion %u
+#>>>>>>42 uleshort =0 \b, fsVersion %u (usual)
+>>>>>>44 ulelong >2 \b, rootdir cluster %u
+#>>>>>>44 ulelong =2 \b, rootdir cluster %u
+#>>>>>>44 ulelong =1 \b, rootdir cluster %u
+>>>>>>48 uleshort >1 \b, infoSector %u
+#>>>>>>48 uleshort =1 \b, infoSector %u (usual)
+>>>>>>48 uleshort <1 \b, infoSector %u
+# 0 or 0xFFFF instead of usual 6 means no backup sector
+>>>>>>50 uleshort =0xFFFF \b, no Backup boot sector
+>>>>>>50 uleshort =0 \b, no Backup boot sector
+#>>>>>>50 uleshort =6 \b, Backup boot sector %u (usual)
+>>>>>>50 default x
+>>>>>>>50 uleshort x \b, Backup boot sector %u
+# corrected by Joerg Jenderek at Feb 2011 according to https://thestarman.pcministry.com/asm/mbr/MSWIN41.htm#FSINFO
+>>>>>>52 ulelong >0 \b, reserved1 %#x
+>>>>>>56 ulelong >0 \b, reserved2 %#x
+>>>>>>60 ulelong >0 \b, reserved3 %#x
+# same structure as FAT1X
+#>>>>>>64 ubyte =0x80 \b, physical drive %#x=80 (usual harddisk)
+#>>>>>>64 ubyte =0 \b, physical drive %#x=0 (usual floppy)
+>>>>>>64 ubyte !0x80
+>>>>>>>64 ubyte >0 \b, physical drive %#x
+# in Windows NT bit 0 is a dirty flag to request chkdsk at boot time. bit 1 requests surface scan too
+>>>>>>65 ubyte >0 \b, reserved %#x
+>>>>>>66 ubyte !0x29 \b, dos < 4.0 BootSector (%#x)
+>>>>>>66 ubyte =0x29
+>>>>>>>67 ulelong x \b, serial number %#x
+>>>>>>>71 string <NO\ NAME \b, label: "%11.11s"
+>>>>>>>71 string >NO\ NAME \b, label: "%11.11s"
+>>>>>>>71 string =NO\ NAME \b, unlabeled
+# additional tests for floppy image added by Joerg Jenderek
+# no fixed disk
+>>>>>21 ubyte !0xF8
+# floppy media with 12 bit FAT
+>>>>>>54 string !FAT16
+# test for FAT after bootsector
+>>>>>>>(11.s) ulelong&0x00ffffF0 0x00ffffF0 \b, followed by FAT
+# floppy image
+!:mime application/x-ima
+# NTFS specific added by Joerg Jenderek at Mar 2011 according to https://thestarman.pcministry.com/asm/mbr/NTFSBR.htm
+# and http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/bios-parameter-block.html
+# 0 FATs
+>>>>>16 ubyte =0
+# 0 root entries
+>>>>>>17 uleshort =0
+# 0 DOS sectors
+>>>>>>>19 uleshort =0
+# 0 sectors/FAT
+# dos < 4.0 BootSector value found is 0x80
+#38 ubyte =0x80 \b, dos < 4.0 BootSector (%#x)
+>>>>>>>>22 uleshort =0 \b; NTFS
+>>>>>>>>>24 uleshort >0 \b, sectors/track %u
+>>>>>>>>>36 ulelong !0x800080 \b, physical drive %#x
+>>>>>>>>>40 ulequad >0 \b, sectors %lld
+>>>>>>>>>48 ulequad >0 \b, $MFT start cluster %lld
+>>>>>>>>>56 ulequad >0 \b, $MFTMirror start cluster %lld
+# Values 0 to 127 represent MFT record sizes of 0 to 127 clusters.
+# Values 128 to 255 represent MFT record sizes of 2^(256-N) bytes.
+>>>>>>>>>64 lelong <256
+>>>>>>>>>>64 lelong <128 \b, clusters/RecordSegment %d
+>>>>>>>>>>64 ubyte >127 \b, bytes/RecordSegment 2^(-1*%i)
+# Values 0 to 127 represent index block sizes of 0 to 127 clusters.
+# Values 128 to 255 represent index block sizes of 2^(256-N) byte
+>>>>>>>>>68 ulelong <256
+>>>>>>>>>>68 ulelong <128 \b, clusters/index block %d
+#>>>>>>>>>>68 ulelong >127 \b, bytes/index block 2^(256-%d)
+>>>>>>>>>>68 ubyte >127 \b, bytes/index block 2^(-1*%i)
+>>>>>>>>>72 ulequad x \b, serial number 0%llx
+>>>>>>>>>80 ulelong >0 \b, checksum %#x
+#>>>>>>>>>80 ulelong =0 \b, checksum %#x=0 (usual)
+# unicode loadername size jump
+>>>>>>>>>(0x200.s*2) ubyte x
+# in next sector loadername terminated by unicode CTRL-D and $
+>>>>>>>>>>&0x1FF ulequad&0x0000FFffFFffFF00 0x0000002400040000 \b; contains
+# if 2nd NTFS sectors is found then assume whole filesystem
+#!:mime application/x-raw-disk-image
+!:ext img/bin/ntfs
+>>>>>>>>>>>0x200 use ntfs-sector2
+
+# For 2nd NTFS sector added by Joerg Jenderek at Jan 2013, Mar 2019
+# https://thestarman.pcministry.com/asm/mbr/NTFSbrHexEd.htm
+# unused assembler instructions short JMP y2;NOP;NOP
+0x056 ulelong&0xFFFF0FFF 0x909002EB NTFS
+#!:mime application/octet-stream
+!:ext bin
+>0 use ntfs-sector2
+# https://memory.dataram.com/products-and-services/software/ramdisk
+# assembler instructions JMP C000;NOP
+0x056 ulelong 0x9000c0e9 NTFS
+#!:mime application/octet-stream
+!:ext bin
+>0 use ntfs-sector2
+# check for characteristics of second NTFS sector and then display loader name
+0 name ntfs-sector2
+# number of utf16 characters of loadername
+>0 uleshort <8
+# unused assembler instructions JMP y2;NOP;NOP or JMP C000;NOP
+>>0x056 ulelong&0xFF0000FD 0x900000E9
+# loadernames are NTLDR,CMLDR,PELDR,$LDR$ or BOOTMGR
+>>>0x002 lestring16 x bootstrap %-5.5s
+# check for 7 character length of loader name like BOOTMGR
+>>>0 uleshort 7
+>>>>0x0c lestring16 x \b%-2.2s
+### DOS,NTFS boot sectors end
+
+# ntfsclone-image is a special save format for NTFS volumes,
+# created and restored by the ntfsclone program
+0 string \0ntfsclone-image ntfsclone image,
+>0x10 byte x version %d.
+>0x11 byte x \b%d,
+>0x12 lelong x cluster size %d,
+>0x16 lequad x device size %lld,
+>0x1e lequad x %lld total clusters,
+>0x26 lequad x %lld clusters in use
+
+
+0 name ffsv1
+>8404 string x last mounted on %s,
+#>9504 ledate x last checked at %s,
+>8224 ledate x last written at %s,
+>8401 byte x clean flag %d,
+>8228 lelong x number of blocks %d,
+>8232 lelong x number of data blocks %d,
+>8236 lelong x number of cylinder groups %d,
+>8240 lelong x block size %d,
+>8244 lelong x fragment size %d,
+>8252 lelong x minimum percentage of free blocks %d,
+>8256 lelong x rotational delay %dms,
+>8260 lelong x disk rotational speed %drps,
+>8320 lelong 0 TIME optimization
+>8320 lelong 1 SPACE optimization
+
+9564 lelong 0x00011954 Unix Fast File system [v1] (little-endian),
+>0 use ffsv1
+
+9564 belong 0x00011954 Unix Fast File system [v1] (big-endian),
+>7168 belong 0x4c41424c Apple UFS Volume
+>>7186 string x named %s,
+>>7176 belong x volume label version %d,
+>>7180 bedate x created on %s,
+>0 use \^ffsv1
+
+0 name ffsv2
+>212 string x last mounted on %s,
+>680 string >\0 volume name %s,
+>1072 leqldate x last written at %s,
+>209 byte x clean flag %d,
+>210 byte x readonly flag %d,
+>1080 lequad x number of blocks %lld,
+>1088 lequad x number of data blocks %lld,
+>44 lelong x number of cylinder groups %d,
+>48 lelong x block size %d,
+>52 lelong x fragment size %d,
+>1196 lelong x average file size %d,
+>1200 lelong x average number of files in dir %d,
+>1104 lequad x pending blocks to free %lld,
+>1112 lelong x pending inodes to free %d,
+>712 lequad x system-wide uuid %0llx,
+>60 lelong x minimum percentage of free blocks %d,
+>128 lelong 0 TIME optimization
+>128 lelong 1 SPACE optimization
+
+42332 lelong 0x19012038 Unix Fast File system [v2ea] (little-endian)
+>40960 use ffsv2
+
+42332 lelong 0x19540119 Unix Fast File system [v2] (little-endian)
+>40960 use ffsv2
+
+42332 belong 0x19012038 Unix Fast File system [v2ea] (little-endian)
+>40960 use \^ffsv2
+
+42332 belong 0x19540119 Unix Fast File system [v2] (big-endian)
+>40960 use \^ffsv2
+
+66908 lelong 0x19012038 Unix Fast File system [v2ea] (little-endian)
+>65536 use ffsv2
+
+66908 lelong 0x19540119 Unix Fast File system [v2] (little-endian)
+>65536 use ffsv2
+
+66908 belong 0x19012038 Unix Fast File system [v2ea] (little-endian)
+>65536 use \^ffsv2
+
+66908 belong 0x19540119 Unix Fast File system [v2] (big-endian)
+>65536 use \^ffsv2
+
+0 ulequad 0xc8414d4dc5523031 HAMMER filesystem (little-endian),
+>0x90 lelong+1 x volume %d
+>0x94 lelong x (of %d),
+>0x50 string x name %s,
+>0x98 ulelong x version %u,
+>0xa0 ulelong x flags %#x
+
+0 ulequad 0x48414d3205172011 HAMMER2 filesystem (little-endian),
+>0x3b byte x volume %d,
+>0x28 ulequad/1073741824 x size %lluGB,
+>0x30 ulelong x version %u,
+>0x34 ulelong x flags %#x
+
+# ext2/ext3 filesystems - Andreas Dilger <adilger@dilger.ca>
+# ext4 filesystem - Eric Sandeen <sandeen@sandeen.net>
+# volume label and UUID Russell Coker
+# https://etbe.coker.com.au/2008/07/08/label-vs-uuid-vs-device/
+0x438 leshort 0xEF53 Linux
+>0x44c lelong x rev %d
+>0x43e leshort x \b.%d
+# No journal? ext2
+>0x45c lelong ^0x0000004 ext2 filesystem data
+>>0x43a leshort ^0x0000001 (mounted or unclean)
+# Has a journal? ext3 or ext4
+>0x45c lelong &0x0000004
+# and small INCOMPAT?
+>>0x460 lelong <0x0000040
+# and small RO_COMPAT?
+>>>0x464 lelong <0x0000008 ext3 filesystem data
+# else large RO_COMPAT?
+>>>0x464 lelong >0x0000007 ext4 filesystem data
+# else large INCOMPAT?
+>>0x460 lelong >0x000003f ext4 filesystem data
+>0x468 ubelong x \b, UUID=%08x
+>0x46c ubeshort x \b-%04x
+>0x46e ubeshort x \b-%04x
+>0x470 ubeshort x \b-%04x
+>0x472 ubelong x \b-%08x
+>0x476 ubeshort x \b%04x
+>0x478 string >0 \b, volume name "%s"
+# General flags for any ext* fs
+>0x460 lelong &0x0000004 (needs journal recovery)
+>0x43a leshort &0x0000002 (errors)
+# INCOMPAT flags
+>0x460 lelong &0x0000001 (compressed)
+#>0x460 lelong &0x0000002 (filetype)
+#>0x460 lelong &0x0000010 (meta bg)
+>0x460 lelong &0x0000040 (extents)
+>0x460 lelong &0x0000080 (64bit)
+#>0x460 lelong &0x0000100 (mmp)
+#>0x460 lelong &0x0000200 (flex bg)
+# RO_INCOMPAT flags
+#>0x464 lelong &0x0000001 (sparse super)
+>0x464 lelong &0x0000002 (large files)
+>0x464 lelong &0x0000008 (huge files)
+#>0x464 lelong &0x0000010 (gdt checksum)
+#>0x464 lelong &0x0000020 (many subdirs)
+#>0x463 lelong &0x0000040 (extra isize)
+
+# f2fs filesystem - Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
+0x400 lelong 0xF2F52010 F2FS filesystem
+>0x46c ubelong x \b, UUID=%08x
+>0x470 ubeshort x \b-%04x
+>0x472 ubeshort x \b-%04x
+>0x474 ubeshort x \b-%04x
+>0x476 ubelong x \b-%08x
+>0x47a ubeshort x \b%04x
+>0x147c lestring16 x \b, volume name "%s"
+
+# Minix filesystems - Juan Cespedes <cespedes@debian.org>
+0x410 leshort 0x137f
+!:strength / 2
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V1, 14 char names, %d zones
+>0x1e string minix \b, bootable
+0x410 beshort 0x137f
+!:strength / 2
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V1 (big endian), %d zones
+>0x1e string minix \b, bootable
+0x410 leshort 0x138f
+!:strength / 2
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V1, 30 char names, %d zones
+>0x1e string minix \b, bootable
+0x410 beshort 0x138f
+!:strength / 2
+>0x402 beshort < 100
+>0x402 beshort > -1 Minix filesystem, V1, 30 char names (big endian), %d zones
+>0x1e string minix \b, bootable
+# Weak Magic: this is $x
+#0x410 leshort 0x2468
+#>0x402 beshort < 100
+#>>0x402 beshort > -1 Minix filesystem, V2, 14 char names
+#>0x1e string minix \b, bootable
+#0x410 beshort 0x2468
+#>0x402 beshort < 100
+#>0x402 beshort > -1 Minix filesystem, V2 (big endian)
+#>0x1e string minix \b, bootable
+#0x410 leshort 0x2478
+#>0x402 beshort < 100
+#>0x402 beshort > -1 Minix filesystem, V2, 30 char names
+#>0x1e string minix \b, bootable
+#0x410 leshort 0x2478
+#>0x402 beshort < 100
+#>0x402 beshort > -1 Minix filesystem, V2, 30 char names
+#>0x1e string minix \b, bootable
+#0x410 beshort 0x2478
+#>0x402 beshort !0 Minix filesystem, V2, 30 char names (big endian)
+#>0x1e string minix \b, bootable
+# Weak Magic! this is MD
+#0x418 leshort 0x4d5a
+#>0x402 beshort <100
+#>>0x402 beshort > -1 Minix filesystem, V3, 60 char names
+
+# SGI disk labels - Nathan Scott <nathans@debian.org>
+0 belong 0x0BE5A941 SGI disk label (volume header)
+
+# SGI XFS filesystem - Nathan Scott <nathans@debian.org>
+0 belong 0x58465342 SGI XFS filesystem data
+>0x4 belong x (blksz %d,
+>0x68 beshort x inosz %d,
+>0x64 beshort ^0x2004 v1 dirs)
+>0x64 beshort &0x2004 v2 dirs)
+
+############################################################################
+# Minix-ST kernel floppy
+0x800 belong 0x46fc2700 Atari-ST Minix kernel image
+# https://en.wikipedia.org/wiki/BIOS_parameter_block
+# floppies with valid BPB and any instruction at beginning
+>19 string \240\005\371\005\0\011\0\2\0 \b, 720k floppy
+>19 string \320\002\370\005\0\011\0\1\0 \b, 360k floppy
+
+############################################################################
+# Hmmm, is this a better way of detecting _standard_ floppy images ?
+19 string \320\002\360\003\0\011\0\1\0 DOS floppy 360k
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+19 string \240\005\371\003\0\011\0\2\0 DOS floppy 720k
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+19 string \100\013\360\011\0\022\0\2\0 DOS floppy 1440k
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+
+19 string \240\005\371\005\0\011\0\2\0 DOS floppy 720k, IBM
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+19 string \100\013\371\005\0\011\0\2\0 DOS floppy 1440k, mkdosfs
+>0x1FE leshort 0xAA55 \b, DOS/MBR hard disk boot sector
+
+19 string \320\002\370\005\0\011\0\1\0 Atari-ST floppy 360k
+19 string \240\005\371\005\0\011\0\2\0 Atari-ST floppy 720k
+# | | | | |
+# | | | | heads
+# | | | sectors/track
+# | | sectors/FAT
+# | media descriptor
+# BPB: sectors
+
+# Valid media descriptor bytes for MS-DOS:
+#
+# Byte Capacity Media Size and Type
+# -------------------------------------------------
+#
+# F0 2.88 MB 3.5-inch, 2-sided, 36-sector
+# F0 1.44 MB 3.5-inch, 2-sided, 18-sector
+# F9 720K 3.5-inch, 2-sided, 9-sector
+# F9 1.2 MB 5.25-inch, 2-sided, 15-sector
+# FD 360K 5.25-inch, 2-sided, 9-sector
+# FF 320K 5.25-inch, 2-sided, 8-sector
+# FC 180K 5.25-inch, 1-sided, 9-sector
+# FE 160K 5.25-inch, 1-sided, 8-sector
+# FE 250K 8-inch, 1-sided, single-density
+# FD 500K 8-inch, 2-sided, single-density
+# FE 1.2 MB 8-inch, 2-sided, double-density
+# F8 ----- Fixed disk
+#
+# FC xxxK Apricot 70x1x9 boot disk.
+#
+# Originally a bitmap:
+# xxxxxxx0 Not two sided
+# xxxxxxx1 Double sided
+# xxxxxx0x Not 8 SPT
+# xxxxxx1x 8 SPT
+# xxxxx0xx Not Removable drive
+# xxxxx1xx Removable drive
+# 11111xxx Must be one.
+#
+# But now it's rather random:
+# 111111xx Low density disk
+# 00 SS, Not 8 SPT
+# 01 DS, Not 8 SPT
+# 10 SS, 8 SPT
+# 11 DS, 8 SPT
+#
+# 11111001 Double density 3 1/2 floppy disk, high density 5 1/4
+# 11110000 High density 3 1/2 floppy disk
+# 11111000 Hard disk any format
+#
+
+# all FAT12 (strength=70) floppies with sectorsize 512 added by Joerg Jenderek at Jun 2013
+# https://en.wikipedia.org/wiki/File_Allocation_Table#Exceptions
+# Too Weak.
+#512 ubelong&0xE0ffff00 0xE0ffff00
+# without valid Media descriptor in place of BPB, cases with are done at other places
+#>21 ubyte <0xE5 floppy with old FAT filesystem
+# but valid Media descriptor at begin of FAT
+#>>512 ubyte =0xed 720k
+#>>512 ubyte =0xf0 1440k
+#>>512 ubyte =0xf8 720k
+#>>512 ubyte =0xf9 1220k
+#>>512 ubyte =0xfa 320k
+#>>512 ubyte =0xfb 640k
+#>>512 ubyte =0xfc 180k
+# look like an old DOS directory entry
+#>>>0xA0E ubequad 0
+#>>>>0xA00 ubequad !0
+#!:mime application/x-ima
+#>>512 ubyte =0xfd
+# look for 2nd FAT at different location to distinguish between 360k and 500k
+#>>>0x600 ubelong&0xE0ffff00 0xE0ffff00 360k
+#>>>0x500 ubelong&0xE0ffff00 0xE0ffff00 500k
+#>>>0xA0E ubequad 0
+#!:mime application/x-ima
+#>>512 ubyte =0xfe
+#>>>0x400 ubelong&0xE0ffff00 0xE0ffff00 160k
+#>>>>0x60E ubequad 0
+#>>>>>0x600 ubequad !0
+#!:mime application/x-ima
+#>>>0xC00 ubelong&0xE0ffff00 0xE0ffff00 1200k
+#>>512 ubyte =0xff 320k
+#>>>0x60E ubequad 0
+#>>>>0x600 ubequad !0
+#!:mime application/x-ima
+#>>512 ubyte x \b, Media descriptor %#x
+# without x86 jump instruction
+#>>0 ulelong&0x804000E9 !0x000000E9
+# assembler instructions: CLI;MOV SP,1E7;MOV AX;07c0;MOV
+#>>>0 ubequad 0xfabce701b8c0078e \b, MS-DOS 1.12 bootloader
+# IOSYS.COM+MSDOS.COM
+#>>>>0xc4 use 2xDOS-filename
+#>>0 ulelong&0x804000E9 =0x000000E9
+# only x86 short jump instruction found
+#>>>0 ubyte =0xEB
+#>>>>1 ubyte x \b, code offset %#x+2
+# https://thestarman.pcministry.com/DOS/ibm100/Boot.htm
+# assembler instructions: CLI;MOV AX,CS;MOV DS,AX;MOV DX,0
+#>>>>(1.b+2) ubequad 0xfa8cc88ed8ba0000 \b, PC-DOS 1.0 bootloader
+# ibmbio.com+ibmdos.com
+#>>>>>0x176 use DOS-filename
+#>>>>>0x181 ubyte x \b+
+#>>>>>0x182 use DOS-filename
+# https://thestarman.pcministry.com/DOS/ibm110/Boot.htm
+# assembler instructions: CLI;MOV AX,CS;MOV DS,AX;XOR DX,DX;MOV
+#>>>>(1.b+2) ubequad 0xfa8cc88ed833d28e \b, PC-DOS 1.1 bootloader
+# ibmbio.com+ibmdos.com
+#>>>>>0x18b use DOS-filename
+#>>>>>0x196 ubyte x \b+
+#>>>>>0x197 use DOS-filename
+# https://en.wikipedia.org/wiki/Zenith_Data_Systems
+# assembler instructions: MOV BX,07c0;MOV SS,BX;MOV SP,01c6
+#>>>>(1.b+2) ubequad 0xbbc0078ed3bcc601 \b, Zenith Data Systems MS-DOS 1.25 bootloader
+# IO.SYS+MSDOS.SYS
+#>>>>>0x20 use 2xDOS-filename
+# https://en.wikipedia.org/wiki/Corona_Data_Systems
+# assembler instructions: MOV AX,CS;MOV DS,AX;CLI;MOV SS,AX;
+#>>>>(1.b+2) ubequad 0x8cc88ed8fa8ed0bc \b, MS-DOS 1.25 bootloader
+# IO.SYS+MSDOS.SYS
+#>>>>>0x69 use 2xDOS-filename
+# assembler instructions: CLI;PUSH CS;POP SS;MOV SP,7c00;
+#>>>>(1.b+2) ubequad 0xfa0e17bc007cb860 \b, MS-DOS 2.11 bootloader
+# defect IO.SYS+MSDOS.SYS ?
+#>>>>>0x162 use 2xDOS-filename
+
+0 name cdrom
+>38913 string !NSR0 ISO 9660 CD-ROM filesystem data
+!:mime application/x-iso9660-image
+!:ext iso/iso9660
+>38913 string NSR0 UDF filesystem data
+!:mime application/x-iso9660-image
+!:ext iso/udf
+>>38917 string 1 (version 1.0)
+>>38917 string 2 (version 1.5)
+>>38917 string 3 (version 2.0)
+>>38917 byte >0x33 (unknown version, ID %#X)
+>>38917 byte <0x31 (unknown version, ID %#X)
+# The next line is not necessary because the MBR staff is done looking for boot signature
+>0x1FE leshort 0xAA55 (DOS/MBR boot sector)
+# "application id" which appears to be used as a volume label
+>32808 string/T >\0 '%.32s'
+>34816 string \000CD001\001EL\ TORITO\ SPECIFICATION (bootable)
+37633 string CD001 ISO 9660 CD-ROM filesystem data (raw 2352 byte sectors)
+!:mime application/x-iso9660-image
+32777 string CDROM High Sierra CD-ROM filesystem data
+# "application id" which appears to be used as a volume label
+>32816 string/T >\0 '%.32s'
+
+
+# CDROM Filesystems
+# https://en.wikipedia.org/wiki/ISO_9660
+# Modified for UDF by gerardo.cacciari@gmail.com
+32769 string CD001
+# mime line at that position does not work
+# to display CD-ROM (70=81-11) after MBR (113=40+72+1), partition-table (71=50+21) and before Apple Driver Map (51)
+#!:strength -11
+# to display CD-ROM (114=81+33) before MBR (113=40+72+1), partition-table (71=50+21) and Apple Driver Map (51)
+!:strength +35
+>0 use cdrom
+
+# URL: https://en.wikipedia.org/wiki/NRG_(file_format)
+# Reference: https://dl.opendesktop.org/api/files/download/id/1460731811/
+# 11577-mount-iso-0.9.5.tar.bz2/mount-iso-0.9.5/install.sh
+# From: Joerg Jenderek
+# Note: Only for nero disc with once (DAO) type after 300 KB header
+339969 string CD001 Nero CD image at 0x4B000
+!:mime application/x-nrg
+!:ext nrg
+>307200 use cdrom
+
+# .cso files
+# Reference: https://pismotec.com/ciso/ciso.h
+# NOTE: There are two other formats with the same magic but
+# completely incompatible specifications:
+# - GameCube/Wii CISO: https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/DiscIO/CISOBlob.h
+# - PSP CISO: https://github.com/jamie/ciso/blob/master/ciso.h
+0 string CISO
+# Other fields are used to determine what type of CISO this is:
+# - 0x04 == 0x00200000: GameCube/Wii CISO (block_size)
+# - 0x10 == 0x00000800: PSP CISO (ISO-9660 sector size)
+# - 0x10 == 0x00004000: For >2GB files using maxcso...
+# https://github.com/unknownbrackets/maxcso/issues/26
+# - None of the above: Compact ISO.
+>4 lelong !0
+>>4 lelong !0x200000
+>>>16 lelong !0x800
+>>>>16 lelong !0x4000 Compressed ISO CD image
+
+# cramfs filesystem - russell@coker.com.au
+0 lelong 0x28cd3d45 Linux Compressed ROM File System data, little endian
+>4 lelong x size %u
+>8 lelong &1 version #2
+>8 lelong &2 sorted_dirs
+>8 lelong &4 hole_support
+>32 lelong x CRC %#x,
+>36 lelong x edition %u,
+>40 lelong x %u blocks,
+>44 lelong x %u files
+
+0 belong 0x28cd3d45 Linux Compressed ROM File System data, big endian
+>4 belong x size %u
+>8 belong &1 version #2
+>8 belong &2 sorted_dirs
+>8 belong &4 hole_support
+>32 belong x CRC %#x,
+>36 belong x edition %u,
+>40 belong x %u blocks,
+>44 belong x %u files
+
+# reiserfs - russell@coker.com.au
+0x10034 string ReIsErFs ReiserFS V3.5
+0x10034 string ReIsEr2Fs ReiserFS V3.6
+0x10034 string ReIsEr3Fs ReiserFS V3.6.19
+>0x1002c leshort x block size %d
+>0x10032 leshort &2 (mounted or unclean)
+>0x10000 lelong x num blocks %d
+>0x10040 lelong 1 tea hash
+>0x10040 lelong 2 yura hash
+>0x10040 lelong 3 r5 hash
+
+# EST flat binary format (which isn't, but anyway)
+# From: Mark Brown <broonie@sirena.org.uk>
+0 string ESTFBINR EST flat binary
+
+# Aculab VoIP firmware
+# From: Mark Brown <broonie@sirena.org.uk>
+0 string VoIP\ Startup\ and Aculab VoIP firmware
+>35 string x format %s
+
+# From: Mark Brown <broonie@sirena.org.uk> [old]
+# From: Behan Webster <behanw@websterwood.com>
+0 belong 0x27051956 u-boot legacy uImage,
+>32 string x %s,
+>28 byte 0 Invalid os/
+>28 byte 1 OpenBSD/
+>28 byte 2 NetBSD/
+>28 byte 3 FreeBSD/
+>28 byte 4 4.4BSD/
+>28 byte 5 Linux/
+>28 byte 6 SVR4/
+>28 byte 7 Esix/
+>28 byte 8 Solaris/
+>28 byte 9 Irix/
+>28 byte 10 SCO/
+>28 byte 11 Dell/
+>28 byte 12 NCR/
+>28 byte 13 LynxOS/
+>28 byte 14 VxWorks/
+>28 byte 15 pSOS/
+>28 byte 16 QNX/
+>28 byte 17 Firmware/
+>28 byte 18 RTEMS/
+>28 byte 19 ARTOS/
+>28 byte 20 Unity OS/
+>28 byte 21 INTEGRITY/
+>29 byte 0 \bInvalid CPU,
+>29 byte 1 \bAlpha,
+>29 byte 2 \bARM,
+>29 byte 3 \bIntel x86,
+>29 byte 4 \bIA64,
+>29 byte 5 \bMIPS,
+>29 byte 6 \bMIPS 64-bit,
+>29 byte 7 \bPowerPC,
+>29 byte 8 \bIBM S390,
+>29 byte 9 \bSuperH,
+>29 byte 10 \bSparc,
+>29 byte 11 \bSparc 64-bit,
+>29 byte 12 \bM68K,
+>29 byte 13 \bNios-32,
+>29 byte 14 \bMicroBlaze,
+>29 byte 15 \bNios-II,
+>29 byte 16 \bBlackfin,
+>29 byte 17 \bAVR32,
+>29 byte 18 \bSTMicroelectronics ST200,
+>29 byte 19 \bSandbox architecture,
+>29 byte 20 \bANDES Technology NDS32,
+>29 byte 21 \bOpenRISC 1000,
+>29 byte 22 \bARM 64-bit,
+>29 byte 23 \bDesignWare ARC,
+>29 byte 24 \bx86_64,
+>29 byte 25 \bXtensa,
+>29 byte 26 \bRISC-V,
+>30 byte 0 Invalid Image
+>30 byte 1 Standalone Program
+>30 byte 2 OS Kernel Image
+>30 byte 3 RAMDisk Image
+>30 byte 4 Multi-File Image
+>30 byte 5 Firmware Image
+>30 byte 6 Script File
+>30 byte 7 Filesystem Image (any type)
+>30 byte 8 Binary Flat Device Tree BLOB
+>31 byte 0 (Not compressed),
+>31 byte 1 (gzip),
+>31 byte 2 (bzip2),
+>31 byte 3 (lzma),
+>12 belong x %d bytes,
+>8 bedate x %s,
+>16 belong x Load Address: %#08X,
+>20 belong x Entry Point: %#08X,
+>4 belong x Header CRC: %#08X,
+>24 belong x Data CRC: %#08X
+
+# JFFS2 file system
+0 leshort 0x1984 Linux old jffs2 filesystem data little endian
+0 beshort 0x1984 Linux old jffs2 filesystem data big endian
+0 leshort 0x1985 Linux jffs2 filesystem data little endian
+0 beshort 0x1985 Linux jffs2 filesystem data big endian
+
+# Squashfs
+0 name squashfs
+>28 beshort x version %d.
+>30 beshort x \b%d,
+>20 beshort 0 uncompressed,
+>20 beshort 1 zlib
+>20 beshort 2 lzma
+>20 beshort 3 lzo
+>20 beshort 4 xz
+>20 beshort 5 lz4
+>20 beshort 6 zstd
+>20 beshort >0 compressed,
+>28 beshort <3
+>>8 belong x %d bytes,
+>28 beshort >2
+>>28 beshort <4
+>>>63 bequad x %lld bytes,
+>>28 beshort >3
+>>>40 bequad x %lld bytes,
+#>>67 belong x %d bytes,
+>4 belong x %d inodes,
+>28 beshort <2
+>>32 beshort x blocksize: %d bytes,
+>28 beshort >1
+>>28 beshort <4
+>>>51 belong x blocksize: %d bytes,
+>>28 beshort >3
+>>>12 belong x blocksize: %d bytes,
+>28 beshort <4
+>>39 bedate x created: %s
+>28 beshort >3
+>>8 bedate x created: %s
+
+0 string sqsh Squashfs filesystem, big endian,
+>0 use squashfs
+
+0 string hsqs Squashfs filesystem, little endian,
+>0 use \^squashfs
+
+# AFS Dump Magic
+# From: Ty Sarna <tsarna@sarna.org>
+0 string \x01\xb3\xa1\x13\x22 AFS Dump
+>&0 belong x (v%d)
+>>&0 byte 0x76
+>>>&0 belong x Vol %d,
+>>>>&0 byte 0x6e
+>>>>>&0 string x %s
+>>>>>>&1 byte 0x74
+>>>>>>>&0 beshort 2
+>>>>>>>>&4 bedate x on: %s
+>>>>>>>>&0 bedate =0 full dump
+>>>>>>>>&0 bedate !0 incremental since: %s
+
+#----------------------------------------------------------
+#delta ISO Daniel Novotny (dnovotny@redhat.com)
+0 string DISO Delta ISO data
+!:strength +50
+>4 belong x version %d
+
+# VMS backup savesets - gerardo.cacciari@gmail.com
+#
+4 string \x01\x00\x01\x00\x01\x00
+>(0.s+16) string \x01\x01
+>>&(&0.b+8) byte 0x42 OpenVMS backup saveset data
+>>>40 lelong x (block size %d,
+>>>49 string >\0 original name '%s',
+>>>2 short 1024 VAX generated)
+>>>2 short 2048 AXP generated)
+>>>2 short 4096 I64 generated)
+
+# Summary: Oracle Clustered Filesystem
+# Created by: Aaron Botsis <redhat@digitalmafia.org>
+8 string OracleCFS Oracle Clustered Filesystem,
+>4 long x rev %d
+>0 long x \b.%d,
+>560 string x label: %.64s,
+>136 string x mountpoint: %.128s
+
+# Summary: Oracle ASM tagged volume
+# Created by: Aaron Botsis <redhat@digitalmafia.org>
+32 string ORCLDISK Oracle ASM Volume,
+>40 string x Disk Name: %0.12s
+32 string ORCLCLRD Oracle ASM Volume (cleared),
+>40 string x Disk Name: %0.12s
+
+# Oracle Clustered Filesystem - Aaron Botsis <redhat@digitalmafia.org>
+8 string OracleCFS Oracle Clustered Filesystem,
+>4 long x rev %d
+>0 long x \b.%d,
+>560 string x label: %.64s,
+>136 string x mountpoint: %.128s
+
+# Oracle ASM tagged volume - Aaron Botsis <redhat@digitalmafia.org>
+32 string ORCLDISK Oracle ASM Volume,
+>40 string x Disk Name: %0.12s
+32 string ORCLCLRD Oracle ASM Volume (cleared),
+>40 string x Disk Name: %0.12s
+
+# Compaq/HP RILOE floppy image
+# From: Dirk Jagdmann <doj@cubic.org>
+0 string CPQRFBLO Compaq/HP RILOE floppy image
+
+#------------------------------------------------------------------------------
+# Files-11 On-Disk Structure (File system for various RSX-11 and VMS flavours).
+# These bits come from LBN 1 (home block) of ODS-1, ODS-2 and ODS-5 volumes,
+# which is mapped to VBN 2 of [000000]INDEXF.SYS;1 - gerardo.cacciari@gmail.com
+#
+1008 string DECFILE11 Files-11 On-Disk Structure
+>525 byte x (ODS-%d);
+>1017 string A RSX-11, VAX/VMS or OpenVMS VAX file system;
+>1017 string B
+>>525 byte 2 VAX/VMS or OpenVMS file system;
+>>525 byte 5 OpenVMS Alpha or Itanium file system;
+>984 string x volume label is '%-12.12s'
+
+# From: Thomas Klausner <wiz@NetBSD.org>
+# https://filext.com/file-extension/DAA
+# describes the daa file format. The magic would be:
+0 string DAA\x0\x0\x0\x0\x0 PowerISO Direct-Access-Archive
+
+# From Albert Cahalan <acahalan@gmail.com>
+# really le32 operation,destination,payloadsize (but quite predictable)
+# 01 00 00 00 00 00 00 c0 00 02 00 00
+0 string \1\0\0\0\0\0\0\300\0\2\0\0 Marvell Libertas firmware
+
+# From Eric Sandeen
+# GFS2
+0x10000 belong 0x01161970
+>0x10018 belong 0x0000051d GFS1 Filesystem
+>>0x10024 belong x (blocksize %d,
+>>0x10060 string >\0 lockproto %s)
+>0x10018 belong 0x00000709 GFS2 Filesystem
+>>0x10024 belong x (blocksize %d,
+>>0x10060 string >\0 lockproto %s)
+
+# Russell Coker <russell@coker.com.au>
+0x10040 string _BHRfS_M BTRFS Filesystem
+>0x1012b string >\0 label "%s",
+>0x10090 lelong x sectorsize %d,
+>0x10094 lelong x nodesize %d,
+>0x10098 lelong x leafsize %d,
+>0x10020 ubelong x UUID=%08x-
+>0x10024 ubeshort x \b%04x-
+>0x10026 ubeshort x \b%04x-
+>0x10028 ubeshort x \b%04x-
+>0x1002a ubeshort x \b%04x
+>0x1002c ubelong x \b%08x,
+>0x10078 lequad x %lld/
+>0x10070 lequad x \b%lld bytes used,
+>0x10088 lequad x %lld devices
+
+0 string btrfs-stream BTRFS stream file
+
+# dvdisaster's .ecc
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string *dvdisaster* dvdisaster error correction file
+
+# xfs metadump image
+# mb_magic XFSM at 0; superblock magic XFSB at 1 << mb_blocklog
+# but can we do the << ? For now it's always 512 (0x200) anyway.
+0 string XFSM
+>0x200 string XFSB XFS filesystem metadump image
+
+# Type: CROM filesystem
+# From: Werner Fink <werner@suse.de>
+0 string CROMFS CROMFS
+>6 string >\0 \b version %2.2s,
+>8 ulequad >0 \b block data at %lld,
+>16 ulequad >0 \b fblock table at %lld,
+>24 ulequad >0 \b inode table at %lld,
+>32 ulequad >0 \b root at %lld,
+>40 ulelong >0 \b fblock size = %d,
+>44 ulelong >0 \b block size = %d,
+>48 ulequad >0 \b bytes = %lld
+
+# Type: xfs metadump image
+# From: Daniel Novotny <dnovotny@redhat.com>
+# mb_magic XFSM at 0; superblock magic XFSB at 1 << mb_blocklog
+# but can we do the << ? For now it's always 512 (0x200) anyway.
+0 string XFSM
+>0x200 string XFSB XFS filesystem metadump image
+
+# Type: delta ISO
+# From: Daniel Novotny <dnovotny@redhat.com>
+0 string DISO Delta ISO data,
+>4 belong x version %d
+
+# JFS2 (Journaling File System) image. (Old JFS1 has superblock at 0x1000.)
+# See linux/fs/jfs/jfs_superblock.h for layout; see jfs_filsys.h for flags.
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0x8000 string JFS1
+# Because it's text-only magic, check a binary value (version) to be sure.
+# Should always be 2, but mkfs.jfs writes it as 1. Needs to be 2 or 1 to be
+# mountable.
+>&0 lelong <3 JFS2 filesystem image
+# Label is followed by a UUID; we have to limit string length to avoid
+# appending the UUID in the case of a 16-byte label.
+>>&144 regex [\x20-\x7E]{1,16} (label "%s")
+>>&0 lequad x \b, %lld blocks
+>>&8 lelong x \b, blocksize %d
+>>&32 lelong&0x00000006 >0 (dirty)
+>>&36 lelong >0 (compressed)
+
+# LFS
+0 lelong 0x070162 LFS filesystem image
+>4 lelong 1 version 1,
+>>8 lelong x \b blocks %u,
+>>12 lelong x \b blocks per segment %u,
+>4 lelong 2 version 2,
+>>8 lelong x \b fragments %u,
+>>12 lelong x \b bytes per segment %u,
+>16 lelong x \b disk blocks %u,
+>20 lelong x \b block size %u,
+>24 lelong x \b fragment size %u,
+>28 lelong x \b fragments per block %u,
+>32 lelong x \b start for free list %u,
+>36 lelong x \b number of free blocks %d,
+>40 lelong x \b number of files %u,
+>44 lelong x \b blocks available for writing %d,
+>48 lelong x \b inodes in cache %d,
+>52 lelong x \b inode file disk address %#x,
+>56 lelong x \b inode file inode number %u,
+>60 lelong x \b address of last segment written %#x,
+>64 lelong x \b address of next segment to write %#x,
+>68 lelong x \b address of current segment written %#x
+
+0 string td\000 floppy image data (TeleDisk, compressed)
+0 string TD\000 floppy image data (TeleDisk)
+
+0 string CQ\024 floppy image data (CopyQM,
+>16 leshort x %d sectors,
+>18 leshort x %d heads.)
+
+0 string ACT\020Apricot\020disk\020image\032\004 floppy image data (ApriDisk)
+
+# URL: http://fileformats.archiveteam.org/wiki/LoadDskF/SaveDskF
+# Update: Joerg Jenderek
+# Note: called "IBM SKF disk image" by TrID
+# verfied by 7-Zip `7z l -tFAT -slt *.dsk` and
+# `deark -l -m loaddskf 06200D19.DSK`
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dsk-skf-old.trid.xml
+0 beshort 0xAA58
+>0 use SaveDskF
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dsk-skf.trid.xml
+0 beshort 0xAA59
+>0 use SaveDskF
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dsk-skf-comp.trid.xml
+0 beshort 0xAA5A
+# skip foo by additional check for unused upper byte of media type in SaveDskF header
+#>3 ubyte =0
+# skip bar by additional check for valid "low" number of heads in SaveDskF header
+#>>26 uleshort <3
+# skip foo by additional check for unused double word field in SaveDskF header
+#>>>30 long =0
+#>>>>0 use SaveDskF
+>0 use SaveDskF
+# display information about IBM SaveDskF floppy disk images
+0 name SaveDskF
+# SaveDskF magic
+>0 beshort x floppy image data (IBM SaveDskF
+#!:mime application/octet-stream
+!:mime application/x-ibm-dsk
+!:ext dsk
+# also suffix with digit (1dk .2dk ...); NO example FOUND!
+#!:ext dsk/1dk/2dk
+>1 ubyte =0x58 \b, old)
+>1 ubyte =0x59 \b)
+>1 ubyte =0x5A \b, compressed)
+# media type; the first byte of the FAT like: 0xF0 (usual floppy) 0xF9 0xFE
+# https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system
+>2 ubyte !0xF0 \b, Media descriptor %#x
+# upper byte of media type is not used; so this seems to be nil
+>3 ubyte !0 \b, upper byte of media type %#x
+# sector size in bytes as in the BIOS parameter block like: 512 ; SAVEDSKF.EXE with other sizes produce garbage images
+>4 uleshort !512 \b, Bytes/sector %u
+# cluster mask; number of sectors per cluster, minus 1
+>6 uleshort+1 >1 \b, sectors/cluster %u
+#>6 uleshort+1 x \b, sectors/cluster %u
+# cluster shift; log2(cluster size / sector size) like: 0~1=ClusterSize/SectorSize
+>7 ubyte >0 \b, cluster shift %u
+#>7 ubyte x \b, cluster shift %u
+# reserved sectors; as in the BIOS parameter block like: 1 256 (2M256R-K.DSK)
+>8 uleshort >1 \b, reserved sectors %u
+#>8 uleshort x \b, reserved sectors %u
+# FAT copies; as in the BIOS parameter block like: 2 (usual) 1 (2-NK.DSK)
+>10 ubyte !2 \b, FAT
+# plural s
+>>10 ubyte >1 \bs
+>>10 ubyte x %u
+# root directory entries; as in the BIOS parameter block like: 224 (usual) 64 (H1-NK.DSK) 4096 (2-NK.DSK)
+>11 uleshort !224 \b, root entries %u
+# sector number of first cluster (count sectors used by boot sector, FATs and root directory) like: 7 10 29 33 288
+>13 uleshort !33 \b, 1st cluster at sector %u
+# number of clusters in image; empty clusters at the end are not saved and counted like: 2372 2848
+>15 uleshort x \b, %u clusters
+# sectors/FAT; as in the BIOS parameter block like: 1 (H1-NK.DSK) 7 9
+>17 ubyte !9 \b, sectors/FAT %u
+# sector number of root directory (ie, count of sectors used by boot sector and FATs) like: 3 (H1-NK.DSK) 9 10 15 19 274 (2M256R-K.DSK)
+>18 uleshort !19 \b, root directory at sector %u
+# checksum; sum of all bytes in the file
+>20 ulelong x \b, checksum %#8.8x
+# cylinders; number of cylinders like: 40 80
+>24 uleshort !80 \b, %u cylinders
+#>24 uleshort x \b, %u cylinders
+# heads; number of heads as in the BIOS parameter block like: 1 (H1-NK.DSK) 2
+>26 uleshort !2 \b, heads %u
+#>26 uleshort x \b, heads %u
+# sectors/track; number of sectors per track as in the BIOS parameter block like: 8 15 18 36
+>28 uleshort !18 \b, sectors/track %u
+#>28 uleshort x \b, sectors/track %u
+# unused double word field seems to be always like: 0
+>30 ulelong !0 \b, at 0x1E %#x
+# number of sectors in images like: 1017 2786 2880
+>34 uleshort x \b, sectors %u
+# if string is "printable" it can be a real comment
+>(36.s) ubyte !0x00
+# if 1st sector is far enough away (> 0x29) then there is space for comment part
+>>38 uleshort >41
+# offset to comment string like: 28h=40
+>>>36 uleshort x \b, at %#x
+# comment string terminated with \r\n\0
+>>>(36.s) string x "%s"
+# offset to the first sector like: 0 (If this is 0, assume it is 0x200) 29h=41 (DISPLAY3.DSK) 31h 43h 45h 46h 48h 50h 200h=512
+>38 uleshort !0 \b, 1st sector at %#x
+# FOR DEBUGGING!
+#>(38.s) ubelong x SECTOR CONTENT %x
+# not compressed floppy image implies readable DOS boot sector inside image
+>>1 ubyte !0x5A
+# when not compressed it is readable as DOS boot sector via ./filesystems
+#>>>(38.s) indirect x \b; contains
+>38 uleshort =0 \b, 1st sector at 0x200 (0)
+# maybe standard DOS boot sector; NO example FOUND HERE!
+#>>0x200 indirect x \b; contains
+
+0 string \074CPM_Disk\076 disk image data (YAZE)
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Central_Point_Software#cite_note-6
+# Reference: https://www.robcraig.com/download/transcopy-5-x-file-format
+# https://www.robcraig.com/download/transcopy-file-format-by-gene-thompson
+# http://mark0.net/download/triddefs_xml.7z/defs/t/tc-transcopy.trid.xml
+# TransCopy signature
+0 beshort 0x5AA5
+# skip Intel serial flash ROM with invalid 0 disk sides handled by ./intel
+>0x103 ubyte !0
+# skip Intel serial flash ROM with unlikely "high" start cylinder 100 handled by ./intel
+#>>0x101 ubyte <100 VALID_START_CYLINDER
+# skip Intel serial flash ROM with unlikely description handled by ./intel
+#>>>2 beshort !0xF00f VALID_DESCRIPTION
+# skip Intel serial flash ROM with invalid disk types 89h 88h handled by ./intel
+#>>>>0x100 byte !0x89 VALID_DISK_TYPE
+>>0 use tc-floppy
+# display information of Central Point Software (CPS) Option Board TransCopy floppy image
+0 name tc-floppy
+>0 beshort x TransCopy disk image
+#!:mime application/octet-stream
+!:mime application/x-floppy-image-tc
+# like: disk04.tc VOCALC2.TC WIZ5_A.tc WIZ2_720.IMG
+!:ext tc/img
+# 1st description (optional 0-terminated maximal 32) like:
+# "Project Workbench 2.20" "Visi On Calc" "Wizardry V Disk 1 of 3"
+>2 string >\0 %.32s
+# 2nd desc. (optional 0-terminated maximal 32) like:
+# "(1988)." "Advanced - Utility" 'Program Disk 2"
+>0x22 string >\0 "%.32s"
+# Looks like ascii (like MESSAGES) formatted with attribute bytes (190)?
+# not needed for disk copy
+#>>0x42 string x '%.190s'
+#>>0x88 lestring16 x "%.8s"
+# disktype: 2~MFM High Density 3~MFM Double Density 4~Apple II GCR 5~FM Single Density
+# 6~Commodore GCR 7~MFM Double Density 8~Commodore Amiga Ch~Atari FM FFh~Unknown
+>0x100 ubyte !0xFF \b, disk type %u
+# StartingCylinder like: 0
+>0x101 ubyte x \b, cylinder
+>0x101 ubyte !0 start=%u
+# EndingCylinder like: 40 (often) 41 79
+>0x102 ubyte x end=%u
+# NumberOfSides like: 2
+>0x103 ubyte !2 \b, %u sides
+# TrackIncrement like: 1
+>0x104 ubyte !1 \b, track increment %u
+# TrackPosTbl Track skew
+#>0x105 ubequad x \b, Track skew %#16.16llx
+# TrackOffsTbl
+#>0x305 ubequad x \b, TrackOffsTbl %#16.16llx
+# TrackLngthTbl
+#>0x505 ubequad x \b, TrackLngthTbl %#16.16llx
+# TrackTypeTable
+#>0x705 ubequad x \b, TrackTypeTable %#16.16llx
+# Address mark timing
+#>0x905 ubequad x \b, Address mark timing %#16.16llx
+# Track fragment
+#>0x2905 ubequad !0 \b, Track fragment %#16.16llx
+# Track data
+#>0x4000 ubequad !0 \b, Track data %#16.16llx
+
+# ReFS
+# Richard W.M. Jones <rjones@redhat.com>
+0 string \0\0\0ReFS\0 ReFS filesystem image
+
+# EFW encase image file format:
+# Gregoire Passault
+# http://www.forensicswiki.org/wiki/Encase_image_file_format
+0 string EVF\x09\x0d\x0a\xff\x00 EWF/Expert Witness/EnCase image file format
+
+# UBIfs
+# Linux kernel sources: fs/ubifs/ubifs-media.h
+0 lelong 0x06101831
+>0x16 leshort 0 UBIfs image
+>0x08 lequad x \b, sequence number %llu
+>0x10 leshort x \b, length %u
+>0x04 lelong x \b, CRC %#08x
+
+0 lelong 0x23494255
+>0x04 leshort <2
+>0x05 string \0\0\0
+>0x1c string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>0x04 leshort x UBI image, version %u
+
+# NEC PC-88 2D disk image
+# From Fabio R. Schmidlin <sd-snatcher@users.sourceforge.net>
+0x20 ulelong&0xFFFFFEFF 0x2A0
+>0x10 string \0\0\0\0\0\0\0\0\0\0
+>>0x280 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>>>0x1A ubyte&0xEF 0
+>>>>0x1B ubyte&0x8F 0
+>>>>>0x1B ubyte&70 <0x40
+>>>>>>0x1C ulelong >0x21
+>>>>>>>0 regex [[:print:]]* NEC PC-88 disk image, name=%s
+>>>>>>>>0x1B ubyte 0 \b, media=2D
+>>>>>>>>0x1B ubyte 0x10 \b, media=2DD
+>>>>>>>>0x1B ubyte 0x20 \b, media=2HD
+>>>>>>>>0x1B ubyte 0x30 \b, media=1D
+>>>>>>>>0x1B ubyte 0x40 \b, media=1DD
+>>>>>>>>0x1A ubyte 0x10 \b, write-protected
+
+# HDD Raw Copy Tool disk image, file extension: .imgc
+# From Benjamin Vanheuverzwijn <bvanheu@gmail.com>
+0 pstring HDD\ Raw\ Copy\ Tool %s
+>0x100 pstring x %s
+>0x200 pstring x - HD model: %s
+#>0x300 pstring x unknown %s
+>0x400 pstring x serial: %s
+#>0x500 pstring x unknown: %s
+!:ext imgc
+
+# http://martin.hinner.info/fs/bfs/bfs-structure.html
+0 lelong 0x1BADFACE SCO UnixWare BFS filesystem
+
+# https://arstechnica.com/information-technology/2018/07/the-beos-filesystem/
+32 lelong 0x42465331 BE/OS BFS1 filesystem
+>36 lelong x \b, byte order %d
+>40 lelong x \b, block size %d
+>44 lelong x \b, block shift %d
+>48 lequad x \b, total blocks %lld
+>56 lequad x \b, used blocks %lld
+
+
+0 name next
+>0 lelong x \b, size %d
+>4 string x \b, label %s
+
+# https://opensource.apple.com/source/IOStorageFamily/IOStorageFamily-44.3\
+# /IONeXTPartitionScheme.h
+0 string NeXT NeXT version 1 disklabel
+>12 use next
+0 string dlV1 NeXT version 2 disklabel
+>12 use next
+0 string dlV2 NeXT version 3 disklabel
+>12 use next
+
+# bcachefs
+# From: Thomas Weißschuh <thomas@t-8ch.de>
+
+0 name bcachefs-uuid
+>0 ubelong x \b%08x
+>4 ubeshort x \b-%04x
+>6 ubeshort x \b-%04x
+>8 ubeshort x \b-%04x
+>10 ubelong x \b-%08x
+>14 ubeshort x \b%04x
+
+0 name bcachefs bcachefs
+>0x68 lequad 8 \b, UUID=
+>>0x38 use bcachefs-uuid
+>>0x48 string >0 \b, label "%.32s"
+>>0x10 uleshort x \b, version %u
+>>0x12 uleshort x \b, min version %u
+>>0x7a byte x \b, device %d
+# assumes the first field is the members field
+>>0x2f4 ulelong 0x01 \b/UUID=
+>>>0x2f0 default x
+>>>&(0x07a.b*56) use bcachefs-uuid
+>>0x07b byte x \b, %d devices
+>>0x090 byte ^0x02 \b (unclean)
+
+0x1018 string \xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81
+>0x1000 use bcachefs
+
+0x1018 string \xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef
+>0x1000 use bcachefs
+
+# EROFS
+# https://kernel.googlesource.com/pub/scm/linux/kernel/git/xiang/erofs-utils/\
+# +/refs/heads/experimental/include/erofs_fs.h#12
+1024 lelong 0xE0F5E1E2 EROFS filesystem
+#>1028 lelong x \b, checksum=%#x
+>1032 lelong >0 \b, compat:
+>>1032 lelong &1 SB_CHKSUM
+>>1032 lelong &2 MTIME
+>1036 byte x \b, blocksize=%u
+>1037 byte x \b, exslots=%u
+#>1038 leshort x \b, root_nid=%d
+#>1040 lequad x \b, inodes=%ld
+#>1048 leldate x \b, build_time=%s
+#>1056 lelong x \b.%d
+#>1060 lelong x \b, blocks=%d
+#>1064 lelong x \b, metadata@%#x
+#>1068 lelong x \b, xattr@%#x
+>1072 guid x \b, uuid=%s
+>1088 string >0 \b, name=%s
+>1104 lelong >0 \b, incompat:
+>>1104 lelong &1 LZ4_0PADDING
+>>1104 lelong &2 BIG_PCLUSTER
+>>1104 lelong &4 CHUNKED_FILE
+>>1104 lelong &8 DEVICE_TABLE
+>>1104 lelong &16 ZTAILPACKING
+
+# YAFFS
+# The layout itself is undocumented, determined by the memory layout of the
+# reference implementation. This signature is derived from the
+# reference implementation code and generated test cases
+# We recognize the start of an object header defined by yaffs_obj_hdr:
+# (Note the values being encoded depending on platform endianess)
+
+# u32 type /* enum yaffs_obj_type, valid 1-5 */
+# u32 parent_obj_id; /* 1 for root objects we recognize */
+# u16 sum_no_longer_used; /* checksum of name. Not used by YAFFS and memset to 0xFF */
+# YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
+
+# mkyaffsimage always writes a root directory with empty name, then processing the target directory contents
+# mkyaffs2image directly proceeds to writing entries with the appropriate u32 YAFFS_OBJECT_TYPE (1-5 valid), each with parent id 1
+
+0 name yaffs
+>0 ulelong 1 \b, type file
+>0 ulelong 2 \b, type symlink
+>0 ulelong 3 \b, type root or directory
+>0 ulelong 4 \b, type hardlink
+>0 ulelong 5 \b, type special
+>0xA byte 0 \b, v1 root directory
+>0xA byte !0 \b, object entry
+>>0xA string x (name: "%s")
+
+# Little Endian: XX 00 00 00 01 00 00 00 FF FF YY
+# XX: 01 - 05 (object type)
+# YY: 00 for version 1 root directory, > 00 for version 2 (name data)
+0x1 string \x00\x00\x00\x01\x00\x00\x00\xFF\xFF
+>0 ulelong 0
+>0 ulelong >5
+>0 default x YAFFS filesystem root entry (little endian)
+>>0 use yaffs
+
+# Big Endian: 00 00 00 XX 00 00 00 01 FF FF YY
+# XX: 01 - 05 (object type)
+# YY: 00 for version 1 root directory, > 00 for version 2 (name data)
+0x4 string \x00\x00\x00\x01\xFF\xFF
+>0 string \x00\x00\x00
+>>0 ubelong 0
+>>0 ubelong >5
+>>0 default x YAFFS filesystem root entry (big endian)
+>>>0 use \^yaffs
diff --git a/contrib/libs/libmagic/magic/Magdir/finger b/contrib/libs/libmagic/magic/Magdir/finger
new file mode 100644
index 0000000000..ab43ac6f9d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/finger
@@ -0,0 +1,16 @@
+
+#------------------------------------------------------------------------------
+# $File: finger,v 1.3 2019/04/19 00:42:27 christos Exp $
+# fingerprint: file(1) magic for fingerprint data
+# XPM bitmaps)
+#
+
+# https://cgit.freedesktop.org/libfprint/libfprint/tree/libfprint/data.c
+
+0 string FP1 libfprint fingerprint data V1
+>3 beshort x \b, driver_id %x
+>5 belong x \b, devtype %x
+
+0 string FP2 libfprint fingerprint data V2
+>3 beshort x \b, driver_id %x
+>5 belong x \b, devtype %x
diff --git a/contrib/libs/libmagic/magic/Magdir/firmware b/contrib/libs/libmagic/magic/Magdir/firmware
new file mode 100644
index 0000000000..4835b12e8d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/firmware
@@ -0,0 +1,133 @@
+#------------------------------------------------------------------------------
+# $File: firmware,v 1.7 2023/03/11 18:52:03 christos Exp $
+# firmware: file(1) magic for firmware files
+#
+
+# https://github.com/MatrixEditor/frontier-smart-api/blob/main/docs/firmware-2.0.md#11-header-structure
+# examples: https://github.com/cweiske/frontier-silicon-firmwares
+0 lelong 0x00001176
+>4 lelong 0x7c Frontier Silicon firmware download
+>>8 lelong x \b, MeOS version %x
+>>12 string/32/T x \b, version %s
+>>40 string/64/T x \b, customization %s
+
+# HPE iLO firmware update image
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://www.sstic.org/2018/presentation/backdooring_your_server_through_its_bmc_the_hpe_ilo4_case/
+# iLO1 (ilo1*.bin) or iLO2 (ilo2_*.bin) images
+0 string \x20\x36\xc1\xce\x60\x37\x62\xf0\x3f\x06\xde\x00\x00\x03\x7f\x00
+>16 ubeshort =0xCFDD HPE iLO2 firmware update image
+>16 ubeshort =0x6444 HPE iLO1 firmware update image
+# iLO3 images (ilo3_*.bin) start directly with image name
+0 string iLO3\x20v\x20 HPE iLO3 firmware update image,
+>7 string x version %s
+# iLO4 images (ilo4_*.bin) start with a signature and a certificate
+0 string --=</Begin\x20HP\x20Signed
+>75 string label_HPBBatch
+>>5828 string iLO\x204
+>>>5732 string HPIMAGE\x00 HPE iLO4 firmware update image,
+>>>6947 string x version %s
+# iLO5 images (ilo5_*.bin) start with a signature
+>75 string label_HPE-HPB-BMC-ILO5-4096
+>>880 string HPIMAGE\x00 HPE iLO5 firmware update image,
+>>944 string x version %s
+
+# IBM POWER Secure Boot Container
+# from https://github.com/open-power/skiboot/blob/master/libstb/container.h
+0 belong 0x17082011 POWER Secure Boot Container,
+>4 beshort x version %u
+>6 bequad x container size %llu
+# These are always zero
+# >14 bequad x target HRMOR %llx
+# >22 bequad x stack pointer %llx
+>4096 ustring \xFD7zXZ\x00 XZ compressed
+0 belong 0x1bad1bad POWER boot firmware
+>256 belong 0x48002030 (PHYP entry point)
+
+# ARM Cortex-M vector table
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://developer.arm.com/documentation/100701/0200/Exception-properties
+# Match stack MSB
+3 byte 0x20
+# Function pointers must be in Thumb-mode and before 0x20000000 (4*5 bits match)
+>4 ulelong&0xE0000001 1
+>>8 ulelong&0xE0000001 1
+>>>12 ulelong&0xE0000001 1
+>>>>44 ulelong&0xE0000001 1
+>>>>>56 ulelong&0xE0000001 1
+# Match Cortex-M reserved sections (0x00000000 or 0xFFFFFFFF)
+>>>>>>28 ulelong+1 <2
+>>>>>>>32 ulelong+1 <2
+>>>>>>>>36 ulelong+1 <2
+>>>>>>>>>40 ulelong+1 <2
+>>>>>>>>>>52 ulelong+1 <2 ARM Cortex-M firmware
+>>>>>>>>>>>0 ulelong >0 \b, initial SP at 0x%08x
+>>>>>>>>>>>4 ulelong^1 x \b, reset at 0x%08x
+>>>>>>>>>>>8 ulelong^1 x \b, NMI at 0x%08x
+>>>>>>>>>>>12 ulelong^1 x \b, HardFault at 0x%08x
+>>>>>>>>>>>44 ulelong^1 x \b, SVCall at 0x%08x
+>>>>>>>>>>>56 ulelong^1 x \b, PendSV at 0x%08x
+
+# ESP-IDF partition table entry
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/espressif/esp-idf/blob/v5.0/components/esp_partition/include/esp_partition.h
+0 string \xAA\x50
+>2 ubyte <2 ESP-IDF partition table entry
+>>12 string/16 x \b, label: "%s"
+>>2 ubyte 0
+>>>3 ubyte 0x00 \b, factory app
+>>>3 ubyte 0x10 \b, OTA_0 app
+>>>3 ubyte 0x11 \b, OTA_1 app
+>>>3 ubyte 0x12 \b, OTA_2 app
+>>>3 ubyte 0x13 \b, OTA_3 app
+>>>3 ubyte 0x14 \b, OTA_4 app
+>>>3 ubyte 0x15 \b, OTA_5 app
+>>>3 ubyte 0x16 \b, OTA_6 app
+>>>3 ubyte 0x17 \b, OTA_7 app
+>>>3 ubyte 0x18 \b, OTA_8 app
+>>>3 ubyte 0x19 \b, OTA_9 app
+>>>3 ubyte 0x1A \b, OTA_10 app
+>>>3 ubyte 0x1B \b, OTA_11 app
+>>>3 ubyte 0x1C \b, OTA_12 app
+>>>3 ubyte 0x1D \b, OTA_13 app
+>>>3 ubyte 0x1E \b, OTA_14 app
+>>>3 ubyte 0x1F \b, OTA_15 app
+>>>3 ubyte 0x20 \b, test app
+>>2 ubyte 1
+>>>3 ubyte 0x00 \b, OTA selection data
+>>>3 ubyte 0x01 \b, PHY init data
+>>>3 ubyte 0x02 \b, NVS data
+>>>3 ubyte 0x03 \b, coredump data
+>>>3 ubyte 0x04 \b, NVS keys
+>>>3 ubyte 0x05 \b, emulated eFuse data
+>>>3 ubyte 0x06 \b, undefined data
+>>>3 ubyte 0x80 \b, ESPHTTPD partition
+>>>3 ubyte 0x81 \b, FAT partition
+>>>3 ubyte 0x82 \b, SPIFFS partition
+>>>3 ubyte 0xFF \b, any data
+>>4 ulelong x \b, offset: 0x%X
+>>8 ulelong x \b, size: 0x%X
+>>28 ulelong&0x1 1 \b, encrypted
+
+# ESP-IDF application image
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/espressif/esp-idf/blob/v5.0/components/bootloader_support/include/esp_app_format.h
+# Note: Concatenation of esp_image_header_t, esp_image_segment_header_t and esp_app_desc_t
+# First segment contains esp_app_desc_t
+0 ubyte 0xE9
+>32 ulelong 0xABCD5432 ESP-IDF application image
+>>12 uleshort 0x0000 for ESP32
+>>12 uleshort 0x0002 for ESP32-S2
+>>12 uleshort 0x0005 for ESP32-C3
+>>12 uleshort 0x0009 for ESP32-S3
+>>12 uleshort 0x000A for ESP32-H2 Beta1
+>>12 uleshort 0x000C for ESP32-C2
+>>12 uleshort 0x000D for ESP32-C6
+>>12 uleshort 0x000E for ESP32-H2 Beta2
+>>12 uleshort 0x0010 for ESP32-H2
+>>80 string/32 x \b, project name: "%s"
+>>48 string/32 x \b, version %s
+>>128 string/16 x \b, compiled on %s
+>>>112 string/16 x %s
+>>144 string/32 x \b, IDF version: %s
+>>4 ulelong x \b, entry address: 0x%08X
diff --git a/contrib/libs/libmagic/magic/Magdir/flash b/contrib/libs/libmagic/magic/Magdir/flash
new file mode 100644
index 0000000000..33b734499c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/flash
@@ -0,0 +1,62 @@
+
+#------------------------------------------------------------------------------
+# $File: flash,v 1.15 2019/04/19 00:42:27 christos Exp $
+# flash: file(1) magic for Macromedia Flash file format
+#
+# See
+#
+# https://www.macromedia.com/software/flash/open/
+# https://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/\
+# en/devnet/swf/pdf/swf-file-format-spec.pdf page 27
+#
+
+0 name swf-details
+
+>0 string F
+>>8 byte&0xfd 0x08 Macromedia Flash data
+!:mime application/x-shockwave-flash
+>>>3 byte x \b, version %d
+>>8 byte&0xfe 0x10 Macromedia Flash data
+!:mime application/x-shockwave-flash
+>>>3 byte x \b, version %d
+>>8 byte 0x18 Macromedia Flash data
+!:mime application/x-shockwave-flash
+>>>3 byte x \b, version %d
+>>8 beshort&0xff87 0x2000 Macromedia Flash data
+!:mime application/x-shockwave-flash
+>>>3 byte x \b, version %d
+>>8 beshort&0xffe0 0x3000 Macromedia Flash data
+!:mime application/x-shockwave-flash
+>>>3 byte x \b, version %d
+>>8 byte&0x7 0
+>>>8 ubyte >0x2f
+>>>>9 ubyte <0x20 Macromedia Flash data
+!:mime application/x-shockwave-flash
+>>>>>3 byte x \b, version %d
+
+>0 string C
+>>8 byte 0x78 Macromedia Flash data (compressed)
+!:mime application/x-shockwave-flash
+>>>3 byte x \b, version %d
+
+>0 string Z
+>>8 byte 0x5d Macromedia Flash data (lzma compressed)
+!:mime application/x-shockwave-flash
+>>>3 byte x \b, version %d
+
+
+1 string WS
+>4 ulelong >14
+>>3 ubyte !0
+>>>0 use swf-details
+
+# From: Cal Peake <cp@absolutedigital.net>
+0 string FLV\x01 Macromedia Flash Video
+!:mime video/x-flv
+
+#
+# Yosu Gomez
+0 string AGD2\xbe\xb8\xbb\xcd\x00 Macromedia Freehand 7 Document
+0 string AGD3\xbe\xb8\xbb\xcc\x00 Macromedia Freehand 8 Document
+# From Dave Wilson
+0 string AGD4\xbe\xb8\xbb\xcb\x00 Macromedia Freehand 9 Document
diff --git a/contrib/libs/libmagic/magic/Magdir/flif b/contrib/libs/libmagic/magic/Magdir/flif
new file mode 100644
index 0000000000..9406208f47
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/flif
@@ -0,0 +1,36 @@
+
+#------------------------------------------------------------------------------
+# $File: flif,v 1.1 2015/11/23 22:04:36 christos Exp $
+# flif: Magic data for file(1) command.
+# FLIF (Free Lossless Image Format)
+
+0 string FLIF FLIF
+>4 string <H image data
+>>6 beshort x \b, %u
+>>8 beshort x \bx%u
+>>5 string 1 \b, 8-bit/color,
+>>5 string 2 \b, 16-bit/color,
+>>4 string 1 \b, grayscale, non-interlaced
+>>4 string 3 \b, RGB, non-interlaced
+>>4 string 4 \b, RGBA, non-interlaced
+>>4 string A \b, grayscale
+>>4 string C \b, RGB, interlaced
+>>4 string D \b, RGBA, interlaced
+>4 string >H \b, animation data
+>>5 ubyte <255 \b, %i frames
+>>>7 beshort x \b, %u
+>>>9 beshort x \bx%u
+>>>6 string =1 \b, 8-bit/color
+>>>6 string =2 \b, 16-bit/color
+>>5 ubyte 0xFF
+>>>6 beshort x \b, %i frames,
+>>>9 beshort x \b, %u
+>>>11 beshort x \bx%u
+>>>8 string =1 \b, 8-bit/color
+>>>8 string =2 \b, 16-bit/color
+>>4 string =Q \b, grayscale, non-interlaced
+>>4 string =S \b, RGB, non-interlaced
+>>4 string =T \b, RGBA, non-interlaced
+>>4 string =a \b, grayscale
+>>4 string =c \b, RGB, interlaced
+>>4 string =d \b, RGBA, interlaced
diff --git a/contrib/libs/libmagic/magic/Magdir/fonts b/contrib/libs/libmagic/magic/Magdir/fonts
new file mode 100644
index 0000000000..17373b5a58
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/fonts
@@ -0,0 +1,449 @@
+
+#------------------------------------------------------------------------------
+# $File: fonts,v 1.51 2022/08/16 11:16:39 christos Exp $
+# fonts: file(1) magic for font data
+#
+0 search/1 FONT ASCII vfont text
+0 short 0436 Berkeley vfont data
+0 short 017001 byte-swapped Berkeley vfont data
+
+# PostScript fonts (must precede "printer" entries), quinlan@yggdrasil.com
+# Modified by: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/PostScript_fonts
+# http://fileformats.archiveteam.org/wiki/Adobe_Type_1
+# Reference: http://mark0.net/download/triddefs_xml.7z
+# defs/p/pfb.trid.xml
+# Note: PFB stands for Printer Font Binary
+0 string %!PS-AdobeFont-1. PostScript Type 1 font text
+#!:mime font/x-postscript-pfb
+#!:ext pfb
+>20 string >\0 (%s)
+# http://www.nationalarchives.gov.uk/pronom/fmt/525
+6 string %!PS-AdobeFont-1.
+# skip DROID fmt-525-signature-id-816.pfb by checking for content after header
+>24 ubyte x PostScript Type 1 font program data
+#!:mime application/octet-stream
+!:mime font/x-postscript-pfb
+!:ext pfb
+# often followed by colon (3Ah) and space (20h) and font name like: DarkGardenMK LetterGothic
+>>24 ubyte =0x3A
+>>>26 string >\0 (%s)
+# some times instead of colon %%CreationDate: and "font name" later
+>>24 ubyte !0x3A
+# font name directive followed by def like: c0633bt_.pfb
+>>>25 search/1247 /FontName\040/
+# show font name in parentheses like: Frankfurt Lithos CharterBT-BoldItalic Courier10PitchBT-Bold
+>>>>&0 regex [A-Za-z0-9-]+ (%s)
+# http://cd.textfiles.com/maxfonts/ATM/M/MIRROR__.PFB
+6 string %PS-AdobeFont-1. PostScript Type 1 font program data
+!:mime font/x-postscript-pfb
+!:ext pfb
+# font name like: Times-Mirror
+>25 string >\0 (%s)
+0 string %!FontType1 PostScript Type 1 font program data
+#!:mime font/x-postscript-pfb
+#!:ext pfb
+6 string %!FontType1 PostScript Type 1 font program data
+#!:mime application/octet-stream
+!:mime font/x-postscript-pfb
+!:ext pfb
+# font name like: CaslonOpenFace FetteFraktur Kaufmann Linotext MesozoicGothic Old-Town
+>23 string >\0 (%s)
+# http://cd.textfiles.com/maxfonts/ATM/P/PLAYBI.PFB
+230 string %!FontType1 PostScript Type 1 font program data
+!:mime font/x-postscript-pfb
+!:ext pfb
+# font name like: Playbill
+>247 string >\0 (%s)
+0 string %!PS-Adobe-3.0\ Resource-Font PostScript Type 1 font text
+#!:mime font/x-postscript-pfb
+#!:ext pfb
+
+# Summary: PostScript Type 1 Printer Font Metrics
+# URL: https://en.wikipedia.org/wiki/PostScript_fonts
+# Reference: https://partners.adobe.com/public/developer/en/font/5178.PFM.pdf
+# Modified by: Joerg Jenderek
+# Note: moved from ./msdos magic
+# dfVersion 256=0100h
+0 uleshort 0x0100
+# GRR: line above is too general as it catches also TrueType font,
+# raw G3 data FAX, WhatsApp encrypted and Panorama database
+# dfType 129=0081h
+>66 uleshort 0x0081
+# dfVertRes 300=012Ch not needed as additional test
+#>>70 uleshort 0x012c
+# dfHorizRes 300=012Ch
+#>>>72 uleshort 0x012c
+# dfDriverInfo points to postscript information section
+>>(101.l) string/c Postscript Printer Font Metrics
+# above labeled "PFM data" by ./msdos (version 5.28) or "Adobe Printer Font Metrics" by TrID
+!:mime application/x-font-pfm
+# AppleShare Print Server
+#!:apple ASPS????
+!:ext pfm
+# dfCopyright 60 byte null padded Copyright string. uncomment it to get old looking
+#>>>6 string >\060 - %-.60s
+# dfDriverInfo
+>>>139 ulelong >0
+# often abbreviated and same as filename
+>>>>(139.l) string x %s
+# dfSize
+>>>2 ulelong x \b, %d bytes
+# dfFace 210=D2h 9Eh
+>>>105 ulelong >0
+# Windows font name
+>>>>(105.l) string x \b, %s
+# dfItalic
+>>>80 ubyte 1 italic
+# dfUnderline
+>>>81 ubyte 1 underline
+# dfStrikeOut
+>>>82 ubyte 1 strikeout
+# dfWeight 400=0x0190 300=0x012c 500=0x01f4 600=0x0258 700=0x02bc
+>>>83 uleshort >699 bold
+# dfPitchAndFamily 16 17 48 49 64 65
+>>>90 ubyte 16 serif
+>>>90 ubyte 17 serif proportional
+#>>>90 ubyte 48 other
+>>>90 ubyte 49 proportional
+>>>90 ubyte 64 script
+>>>90 ubyte 65 script proportional
+
+# X11 font files in SNF (Server Natural Format) format
+# updated by Joerg Jenderek at Feb 2013 and Nov 2021
+# http://computer-programming-forum.com/51-perl/8f22fb96d2e34bab.htm
+# URL: http://fileformats.archiveteam.org/wiki/SNF
+# Reference: https://cgit.freedesktop.org/xorg/lib/libXfont/tree/src/bitmap/snfstr.h
+0 belong 00000004
+# version2 same as version1 in struct _snfFontInfo
+>104 belong 00000004 X11 SNF font data, MSB first
+# GRR: line above is too general as it catches also DEGAS low-res bitmap like:
+# http://cd.textfiles.com/geminiatari/FILES/GRAPHICS/ANIMAT/SPID_PAT/BIGSPID.PI1
+!:mime application/x-font-sfn
+!:ext snf
+# GRR: line below is too general as it catches also Xbase index file t3-CHAR.NDX
+0 lelong 00000004
+>104 lelong 00000004 X11 SNF font data, LSB first
+!:mime application/x-font-sfn
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/s/snf-x11-lsb.trid.xml
+!:ext snf
+
+# X11 Bitmap Distribution Format, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 search/1 STARTFONT\ X11 BDF font text
+
+# From: Joerg Jenderek
+# URL: https://grub.gibibit.com/New_font_format
+# Reference: util/grub-mkfont.c
+# include/grub/fontformat.h
+# FONT_FORMAT_SECTION_NAMES_FILE
+0 string FILE
+# FONT_FORMAT_PFF2_MAGIC
+>8 string PFF2
+# leng 4 only at the moment
+>>4 ubelong 4
+# FONT_FORMAT_SECTION_NAMES_FONT_NAME
+>>>12 string NAME GRUB2 font
+!:mime application/x-font-pf2
+!:ext pf2
+# length of font_name
+>>>>16 ubelong >0
+# font_name
+>>>>>20 string >\0 "%-s"
+
+# X11 fonts, from Daniel Quinlan (quinlan@yggdrasil.com)
+# PCF must come before SGI additions ("MIPSEL MIPS-II COFF" collides)
+0 string \001fcp X11 Portable Compiled Font data,
+>12 lelong ^0x08 bit: LSB,
+>12 lelong &0x08 bit: MSB,
+>12 lelong ^0x04 byte: LSB first
+>12 lelong &0x04 byte: MSB first
+0 string D1.0\015 X11 Speedo font data
+
+#------------------------------------------------------------------------------
+# FIGlet fonts and controlfiles
+# From figmagic supplied with Figlet version 2.2
+# "David E. O'Brien" <obrien@FreeBSD.ORG>
+0 string flf FIGlet font
+>3 string >2a version %-2.2s
+0 string flc FIGlet controlfile
+>3 string >2a version %-2.2s
+
+# libGrx graphics lib fonts, from Albert Cahalan (acahalan@cs.uml.edu)
+# Used with djgpp (DOS Gnu C++), sometimes Linux or Turbo C++
+0 belong 0x14025919 libGrx font data,
+>8 leshort x %dx
+>10 leshort x \b%d
+>40 string x %s
+# Misc. DOS VGA fonts, from Albert Cahalan (acahalan@cs.uml.edu)
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/CPI
+# Reference: http://www.delorie.com/djgpp/doc/rbinter/it/58/17.html
+0 belong 0xff464f4e DOS code page font data collection
+!:mime font/x-dos-cpi
+!:ext cpi
+0 string \x7fDRFONT DR-DOS code page font data collection
+!:mime font/x-drdos-cpi
+!:ext cpi
+7 belong 0x00454741 DOS code page font data
+7 belong 0x00564944 DOS code page font data (from Linux?)
+4098 string DOSFONT DOSFONT2 encrypted font data
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/GEM_bitmap_font
+# Reference: http://cd.textfiles.com/ataricompendium/BOOK/HTML/APPENDC.HTM#cnt
+#
+# usual case with lightening mask and skewing mask 5555h~UU
+#62 ulelong 0x55555555
+# skip cl8m8ocofedso.testfile by looking for face size lower/equal 72
+#>2 uleshort <73
+#>>0 use gdos-font
+# BOX18.GFT COWBOY30.GFT ROYALK30.GFT
+#62 ulelong 0
+# skip ISO 9660 CD-ROM ./filesystem by looking for low positive face size
+#>2 uleshort >2
+# skip DOS 2.0 backup id file ./msdos by looking for face size lower/equal 72
+#>>2 uleshort <73
+# skip MS oem.hlp, some Windows ICO ./msdos by looking for valid long name like WYE
+#>>>4 ulelong >0x001F1f1F
+# skip Microsoft WinWord 2.0 ./msdos by looking for positive offset to font data
+#>>>>76 ulelong >83
+#>>>>>0 use gdos-font
+0 name gdos-font
+>0 uleshort x GEM GDOS font
+!:mime application/x-font-gdos
+# also .eps found like AA070GEP.EPS AI360GEP.EPS
+!:ext fnt/gtf
+# font name like Big&Tall, Celtic #s, Courier, University Bold, WYE
+>4 string x %.32s
+# face size in points 3-72 SLSS03CG.FNT H1CELT72.FNT
+>2 uleshort x %u
+# face ID (must be unique)
+>0 uleshort x \b, ID %#4.4x
+# lowest character index in face (4 but usually 32 for disk-loaded fonts)
+#>36 uleshort !32 \b, unusual character index %u
+# width of the widest character like 0 8 10 12 16 24 32
+#>50 uleshort x \b, %u char width
+# width of the widest character cell like 8 11 12 14 15 16 33 67
+#>52 uleshort x \b, %u cell width
+# thickening size in pixel like 0 1 2 3 4 5 6 7 8
+#>58 uleshort x \b, %u thick
+# lightening mask to eliminate pixels, usually 5555h
+>62 uleshort !0x5555 \b, lightening mask %#x
+# skewing mask to determine when to perform additional rotation when skewing, usually 5555h
+>64 uleshort !0x5555 \b, skewing mask %#x
+# offset to optional horizontal offset table 0 58h~88 5eh 252h
+#>68 ulelong x \b, %#x horizontal table offset
+# offset of character offset table 54h for many *.GFT 55h 58h 5Eh 120h 1D4h 202h 220h
+#>72 ulelong x \b, %#x coffset
+# offset to font data like 116h 118h 158 20Ah 20Eh
+>76 ulelong x \b, %#x foffset
+# form width in bytes like 58 67 156 190 227 317 345
+#>80 uleshort x \b, %u fwidth
+# form height in bytes like 4 8 11 17 26 56 70 90 120 146 150
+#>82 uleshort x \b, %u fheight
+# pointer to the next font like 0 10000h 20000h 30000h 40000h 60000h 80000h E0000h D0000h
+#>84 ulelong x \b, %#x noffset
+
+# downloadable fonts for browser (prints type) anthon@mnt.org
+# https://tools.ietf.org/html/rfc3073
+0 string PFR1 Portable Font Resource font data (new)
+>102 string >0 \b: %s
+0 string PFR0 Portable Font Resource font data (old)
+>4 beshort >0 version %d
+
+# True Type fonts
+# Modified by: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/TrueType
+# Reference: https://developer.apple.com/fonts/TrueType-Reference-Manual/
+#
+# sfnt version "typ1" used by some Apple, but no example found
+0 string typ1
+>0 use sfnt-font
+>0 use sfnt-names
+# sfnt version "true" used by some Apple
+0 string true
+>0 use sfnt-font
+>0 use sfnt-names
+# GRR: below test is too general
+# sfnt version often 0x00010000
+0 string \000\001\000\000
+>0 use sfnt-font
+>0 use sfnt-names
+# validate and display sfnt font data like number of tables
+0 name sfnt-font
+# file 5.30 version assumes 00FFh as maximal number of tables
+#>4 ubeshort <0x0100
+# maximal 27 tables found like in Skia.ttf
+# 46 different table names mentioned on Apple specification
+# skip 1st sequence of DOS 2 backup with path separator (\~92 or /~47) misinterpreted as table number
+>4 ubeshort <47
+# skip bad examples with garbage table names like in a5.show HYPERC MAC
+# tag names consist of up to four characters padded with spaces at end like
+# BASE DSIG OS/2 Zapf acnt glyf cvt vmtx xref ...
+>>12 regex/4l \^[A-Za-z][A-Za-z][A-Za-z/][A-Za-z2\ ]
+#>>>0 ubelong x \b, sfnt version %#x
+>>>0 ubelong !0x4f54544f TrueType
+!:mime font/sfnt
+!:apple ????tfil
+# .ttf for TrueType font
+# EUDC.tte created by privat character editor %WINDIR%\system32\eudcedit.exe
+!:ext ttf/tte
+# sfnt version 4F54544Fh~OTTO
+>>>0 ubelong =0x4f54544f OpenType
+!:mime font/otf
+!:apple ????OTTO
+!:ext otf
+>>>0 ubelong x Font data
+# DSIG=44454947h table name implies a digitally signed font
+# search range = number of tables * 16 =< maximal number of tables * 16 = 27 * 16 = 432
+>>>12 search/432 DSIG \b, digitally signed
+>>>4 ubeshort x \b, %d tables
+# minimal 9 tables found like in NISC18030.ttf
+#>>>4 ubeshort <10 TMIN
+#>>>4 ubeshort >24 TBIG
+# table directory entries
+>>>12 string x \b, 1st "%4.4s"
+
+# search and display 1st name in sfnt font which is often copyright text
+# does not work inside font collections
+0 name sfnt-names
+# search for naming table
+>12 search/432/s name
+# biggest offset 0x0100bd28 like Windows10 Fonts\simsunb.ttf
+#>>>>&8 ubelong >0x0100bd27 BIGGEST OFFSET
+>>&8 ubelong >0x00100000
+# offset of name table
+>>>&-4 ubelong x \b, name offset %#x
+# GRR: pointer to name table only works if offset ~< FILE_BYTES_MAX = 100000h defined in src\file.h
+>>&8 ubelong <0x00100000
+>>>&-16 ubelong x
+# name table
+>>>>(&8.L) ubequad x
+# invalid format selector
+#>>>>>&-8 ubeshort !0 \b, invalid selector %x
+# minimal 3 name records found like in c:\Program Files (x86)\Tesseract-OCR\tessdata\pdf.ttf
+# maximal 1227 name records found like in Apple Chancery.ttf
+#>>>>>&-6 ubeshort <0x4 mincount
+#>>>>>&-6 ubeshort >130 maxcount
+>>>>>&-6 ubeshort x \b, %d names
+# offset to start of string storage from start of table
+#>>>>>&-4 ubeshort x \b, record offset %d
+# 1st name record
+# string offset from start of storage area
+#>>>>>&8 ubeshort x \b, string offset %d
+# string length
+#>>>>>&6 ubeshort x \b, string length %d
+# minimal name string 7 like in c:\Program Files (x86)\Kodi\addons\webinterface.default\lib\video-js\font\VideoJS.ttf
+# also found 0 like in SWZCONLN.TTF
+#>>>>>&6 ubeshort <8 MIN STRING
+# maximal name string 806 like in c:\Windows\Fonts\palabi.ttf
+#>>>>>&6 ubeshort >805 MAX STRING
+# platform identifier: 0~Apple Unicode, 1~Macintosh, 3~Microsoft
+#>>>>>&-2 ubeshort >3 BAD PLATFORM
+>>>>>&-2 ubeshort 0 \b, Unicode
+>>>>>&-2 ubeshort 1 \b, Macintosh
+>>>>>&-2 ubeshort 3 \b, Microsoft
+# languageID (0~english Macintosh, 0409h~english Microsoft, ...)
+>>>>>&2 ubeshort >0 \b, language %#x
+# name identifiers
+# often 0~copyright, 1~font, 2~font subfamily, 5~version, 13~license, 19~sample, ...
+>>>>>&4 ubeshort >0 \b, type %d string
+# platform specific encoding:
+# 0~undefined character set, 1~UGL set with Unicode, 3~Unicode 2.0 BMP only, 4~Unicode 2.0
+#>>>>>&0 ubeshort x \b, %d encoding
+>>>>>&0 ubeshort 0
+# handle only name string offset 0 because do not know how to add 2 relative offsets
+>>>>>>&6 ubeshort 0
+>>>>>>>&(&-14.S-18) ubyte !0
+# GRR: instead 806 only first MAXstring = 96 characters are displayed as defined in src\file.h
+# often copyright string that starts like \251 2006 The Monotype Corporation
+>>>>>>>>&-1 string x \b, %-11.96s
+# test for unicode string
+>>>>>>>&(&-14.S-18) ubyte 0
+>>>>>>>>&0 lestring16 x \b, %-11.96s
+# unicode encoding
+>>>>>&0 ubeshort >0
+>>>>>>&6 ubeshort 0
+>>>>>>>&(&-14.S-17) lestring16 x \b, %-11.96s
+
+0 string \007\001\001\000Copyright\ (c)\ 199 Adobe Multiple Master font
+0 string \012\001\001\000Copyright\ (c)\ 199 Adobe Multiple Master font
+
+# TrueType/OpenType font collections (.ttc)
+# URL: https://en.wikipedia.org/wiki/OpenType
+# https://www.microsoft.com/typography/otspec/otff.htm
+# Modified by: Joerg Jenderek
+# Note: container for TrueType, OpenType font
+0 string ttcf
+# skip ASCII text
+>4 ubyte 0
+# sfnt version often 0x00010000 of 1st table is TrueType
+>>(12.L) ubelong !0x4f54544f TrueType
+!:mime font/ttf
+!:apple ????tfil
+!:ext ttc
+# sfnt version 4F54544Fh~OTTO of 1st table is OpenType font
+>>(12.L) ubelong =0x4f54544f OpenType
+!:mime font/otf
+!:apple ????OTTO
+# no example found for otc
+!:ext ttc/otc
+>>4 ubyte x font collection data
+#!:mime font/collection
+# TCC version
+>>4 belong 0x00010000 \b, 1.0
+>>4 belong 0x00020000 \b, 2.0
+>>8 ubelong >0 \b, %d fonts
+# array offset size = fonts * offsetsize = fonts * 4
+>>(8.L*4) ubequad x
+# 0x44454947 = 'DSIG'
+>>>&4 belong 0x44534947 \b, digitally signed
+# offset to 1st font
+>>12 ubelong x \b, at %#x
+# point to 1st font that starts with sfnt version
+>>(12.L) use sfnt-font
+
+# Opentype font data from Avi Bercovich
+0 string OTTO OpenType font data
+!:mime application/vnd.ms-opentype
+
+# From: Alex Myczko <alex@aiei.ch>
+0 string SplineFontDB: Spline Font Database
+!:mime application/vnd.font-fontforge-sfd
+>14 string x version %s
+
+# EOT
+0x40 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>0x22 string LP Embedded OpenType (EOT)
+# workaround until there's lepstring16
+# >>0x52 lepstring16/h >\0 \b, %s family
+>>0x52 short !0
+>>>0x54 lestring16 x \b, %s family
+!:mime application/vnd.ms-fontobject
+
+# Web Open Font Format (.woff)
+0 name woff
+>4 belong 0x00010000 \b, TrueType
+>4 belong 0x4F54544F \b, CFF
+>4 belong 0x74727565 \b, TrueType
+>4 default x
+>>4 belong x \b, flavor %d
+>8 belong x \b, length %d
+#>12 beshort x \b, numTables %d
+#>14 beshort x \b, reserved %d
+#>16 belong x \b, totalSfntSize %d
+
+# https://www.w3.org/TR/WOFF/
+0 string wOFF Web Open Font Format
+!:mime font/woff
+>0 use woff
+>20 beshort x \b, version %d
+>22 beshort x \b.%d
+# https://www.w3.org/TR/WOFF2/
+0 string wOF2 Web Open Font Format (Version 2)
+!:mime font/woff2
+!:ext woff2
+>0 use woff
+#>20 belong x \b, totalCompressedSize %d
+>24 beshort x \b, version %d
+>26 beshort x \b.%d
diff --git a/contrib/libs/libmagic/magic/Magdir/forth b/contrib/libs/libmagic/magic/Magdir/forth
new file mode 100644
index 0000000000..34c918152a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/forth
@@ -0,0 +1,82 @@
+
+#------------------------------------------------------------------------------
+# $File: forth,v 1.4 2021/04/26 15:56:00 christos Exp $
+# forth: file(1) magic for various Forth environments
+# From: Lubomir Rintel <lkundrak@v3.sk>
+#
+
+# Has a FORTH stack diagram and something that looks very much like a FORTH
+# multi-line word definition. Probably a FORTH source.
+0 regex \[[:space:]]\\(([[:space:]].*)?\ --\ (.*[[:space:]])?\\)
+>0 regex \^:\[[:space:]]
+>>0 regex \^;$ FORTH program
+!:mime text/x-forth
+
+# Inline word definition complete with a stack diagram
+0 regex \^:[[:space:]].*[[:space:]]\\(([[:space:]].*)?\ --\ (.*[[:space:]])?\\)[[:space:]].*[[:space:]];$ FORTH program
+!:mime text/x-forth
+
+# Various dictionary images used by OpenFirware FORTH environment
+
+0 lelong 0xe1a00000
+>8 lelong 0xe1a00000
+# skip raspberry pi kernel image kernel7.img by checking for positive text length
+>>24 lelong >0 ARM OpenFirmware FORTH Dictionary,
+>>>24 lelong x Text length: %d bytes,
+>>>28 lelong x Data length: %d bytes,
+>>>32 lelong x Text Relocation Table length: %d bytes,
+>>>36 lelong x Data Relocation Table length: %d bytes,
+>>>40 lelong x Entry Point: %#08X,
+>>>44 lelong x BSS length: %d bytes
+
+0 string MP
+>28 lelong 1 x86 OpenFirmware FORTH Dictionary,
+>>4 leshort x %d blocks
+>>2 leshort x + %d bytes,
+>>6 leshort x %d relocations,
+>>8 leshort x Header length: %d paragraphs,
+>>10 leshort x Data Size: %d
+>>12 leshort x - %d 4K pages,
+>>14 lelong x Initial Stack Pointer: %#08X,
+>>20 lelong x Entry Point: %#08X,
+>>24 lelong x First Relocation Item: %d,
+>>26 lelong x Overlay Number: %d,
+>>18 leshort x Checksum: %#08X
+
+0 belong 0x48000020 PowerPC OpenFirmware FORTH Dictionary,
+>4 belong x Text length: %d bytes,
+>8 belong x Data length: %d bytes,
+>12 belong x BSS length: %d bytes,
+>16 belong x Symbol Table length: %d bytes,
+>20 belong x Entry Point: %#08X,
+>24 belong x Text Relocation Table length: %d bytes,
+>28 belong x Data Relocation Table length: %d bytes
+
+0 lelong 0x10000007 MIPS OpenFirmware FORTH Dictionary,
+>4 lelong x Text length: %d bytes,
+>8 lelong x Data length: %d bytes,
+>12 lelong x BSS length: %d bytes,
+>16 lelong x Symbol Table length: %d bytes,
+>20 lelong x Entry Point: %#08X,
+>24 lelong x Text Relocation Table length: %d bytes,
+>28 lelong x Data Relocation Table length: %d bytes
+
+# Dictionary images used by minimal C FORTH environments, any platform,
+# using native byte order.
+
+# Weak.
+#0 short 0x5820 cForth 16-bit Dictionary,
+#>2 short x Serial: %#08X,
+#>4 short x Dictionary Start: %#08X,
+#>6 short x Dictionary Size: %d bytes,
+#>8 short x User Area Start: %#08X,
+#>10 short x User Area Size: %d bytes,
+#>12 short x Entry Point: %#08X
+
+0 long 0x581120 cForth 32-bit Dictionary,
+>4 long x Serial: %#08X,
+>8 long x Dictionary Start: %#08X,
+>12 long x Dictionary Size: %d bytes,
+>16 long x User Area Start: %#08X,
+>20 long x User Area Size: %d bytes,
+>24 long x Entry Point: %#08X
diff --git a/contrib/libs/libmagic/magic/Magdir/fortran b/contrib/libs/libmagic/magic/Magdir/fortran
new file mode 100644
index 0000000000..6abc2f70cb
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/fortran
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: fortran,v 1.10 2015/11/05 18:47:16 christos Exp $
+# FORTRAN source
+# Check that the first 100 lines start with C or whitespace first.
+0 regex/100l !\^[^Cc\ \t].*$
+>0 regex/100l \^[Cc][\ \t] FORTRAN program text
+!:mime text/x-fortran
+!:strength - 5
diff --git a/contrib/libs/libmagic/magic/Magdir/frame b/contrib/libs/libmagic/magic/Magdir/frame
new file mode 100644
index 0000000000..c0fd840a46
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/frame
@@ -0,0 +1,62 @@
+
+#------------------------------------------------------------------------------
+# $File: frame,v 1.14 2019/11/25 00:31:30 christos Exp $
+# frame: file(1) magic for FrameMaker files
+#
+# This stuff came on a FrameMaker demo tape, most of which is
+# copyright, but this file is "published" as witness the following:
+#
+# Note that this is the Framemaker Maker Interchange Format, not the
+# Normal format which would be application/vnd.framemaker.
+#
+0 string \<MakerFile FrameMaker document
+!:mime application/x-mif
+>11 string 5.5 (5.5
+>11 string 5.0 (5.0
+>11 string 4.0 (4.0
+>11 string 3.0 (3.0
+>11 string 2.0 (2.0
+>11 string 1.0 (1.0
+>14 byte x %c)
+# URL: http://fileformats.archiveteam.org/wiki/Maker_Interchange_Format
+# Reference: https://help.adobe.com/en_US/framemaker/mifreference/mifref.pdf
+# Update: Joerg Jenderek 2019 Nov
+0 string \<MIFFile FrameMaker MIF (ASCII) file
+# https://www.iana.org/assignments/media-types/application/vnd.mif
+!:mime application/vnd.mif
+# mif most but also find bookTOC.framemif
+!:ext mif/framemif
+# followed by space~20h
+#>8 ubyte 0x20 \b, space before version
+# 3 characters of version number of the MIF language like 1.0, 2.0 ... 2015 ...
+>9 string x (%.3s
+# if not greater sign then display 4th character of version
+>12 ubyte =0x3e \b)
+>12 ubyte !0x3e \b%c)
+# comment starting with # shows the name+version number of generating program
+>13 search/3 #
+>>&0 string x "%s"
+0 search/1 \<MakerDictionary FrameMaker Dictionary text
+!:mime application/x-mif
+>17 string 3.0 (3.0)
+>17 string 2.0 (2.0)
+>17 string 1.0 (1.x)
+0 string \<MakerScreenFont FrameMaker Font file
+!:mime application/x-mif
+>17 string 1.01 (%s)
+0 string \<MML FrameMaker MML file
+!:mime application/x-mif
+0 string \<BookFile FrameMaker Book file
+!:mime application/x-mif
+>10 string 3.0 (3.0
+>10 string 2.0 (2.0
+>10 string 1.0 (1.0
+>13 byte x %c)
+# XXX - this book entry should be verified, if you find one, uncomment this
+#0 string \<Book\040 FrameMaker Book (ASCII) file
+#!:mime application/x-mif
+#>6 string 3.0 (3.0)
+#>6 string 2.0 (2.0)
+#>6 string 1.0 (1.0)
+0 string \<Maker\040Intermediate\040Print\040File FrameMaker IPL file
+!:mime application/x-mif
diff --git a/contrib/libs/libmagic/magic/Magdir/freebsd b/contrib/libs/libmagic/magic/Magdir/freebsd
new file mode 100644
index 0000000000..66aff6caf2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/freebsd
@@ -0,0 +1,164 @@
+
+#------------------------------------------------------------------------------
+# $File: freebsd,v 1.9 2022/01/19 12:44:13 christos Exp $
+# freebsd: file(1) magic for FreeBSD objects
+#
+# All new-style FreeBSD magic numbers are in host byte order (i.e.,
+# little-endian on x86).
+#
+# XXX - this comes from the file "freebsd" in a recent FreeBSD version of
+# "file"; it, and the NetBSD stuff in "netbsd", appear to use different
+# schemes for distinguishing between executable images, shared libraries,
+# and object files.
+#
+# FreeBSD says:
+#
+# Regardless of whether it's pure, demand-paged, or none of the
+# above:
+#
+# if the entry point is < 4096, then it's a shared library if
+# the "has run-time loader information" bit is set, and is
+# position-independent if the "is position-independent" bit
+# is set;
+#
+# if the entry point is >= 4096 (or >4095, same thing), then it's
+# an executable, and is dynamically-linked if the "has run-time
+# loader information" bit is set.
+#
+# On x86, NetBSD says:
+#
+# If it's neither pure nor demand-paged:
+#
+# if it has the "has run-time loader information" bit set, it's
+# a dynamically-linked executable;
+#
+# if it doesn't have that bit set, then:
+#
+# if it has the "is position-independent" bit set, it's
+# position-independent;
+#
+# if the entry point is non-zero, it's an executable, otherwise
+# it's an object file.
+#
+# If it's pure:
+#
+# if it has the "has run-time loader information" bit set, it's
+# a dynamically-linked executable, otherwise it's just an
+# executable.
+#
+# If it's demand-paged:
+#
+# if it has the "has run-time loader information" bit set,
+# then:
+#
+# if the entry point is < 4096, it's a shared library;
+#
+# if the entry point is = 4096 or > 4096 (i.e., >= 4096),
+# it's a dynamically-linked executable);
+#
+# if it doesn't have the "has run-time loader information" bit
+# set, then it's just an executable.
+#
+# (On non-x86, NetBSD does much the same thing, except that it uses
+# 8192 on 68K - except for "68k4k", which is presumably "68K with 4K
+# pages - SPARC, and MIPS, presumably because Sun-3's and Sun-4's
+# had 8K pages; dunno about MIPS.)
+#
+# I suspect the two will differ only in perverse and uninteresting cases
+# ("shared" libraries that aren't demand-paged and whose pages probably
+# won't actually be shared, executables with entry points <4096).
+#
+# I leave it to those more familiar with FreeBSD and NetBSD to figure out
+# what the right answer is (although using ">4095", FreeBSD-style, is
+# probably better than separately checking for "=4096" and ">4096",
+# NetBSD-style). (The old "netbsd" file analyzed FreeBSD demand paged
+# executables using the NetBSD technique.)
+#
+0 lelong&0377777777 041400407 FreeBSD/i386
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400410 FreeBSD/i386 pure
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400413 FreeBSD/i386 demand paged
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+0 lelong&0377777777 041400314 FreeBSD/i386 compact demand paged
+>20 lelong <4096
+>>3 byte&0xC0 &0x80 shared library
+>>3 byte&0xC0 0x40 PIC object
+>>3 byte&0xC0 0x00 object
+>20 lelong >4095
+>>3 byte&0x80 0x80 dynamically linked executable
+>>3 byte&0x80 0x00 executable
+>16 lelong >0 not stripped
+
+# XXX gross hack to identify core files
+# cores start with a struct tss; we take advantage of the following:
+# byte 7: highest byte of the kernel stack pointer, always 0xfe
+# 8/9: kernel (ring 0) ss value, always 0x0010
+# 10 - 27: ring 1 and 2 ss/esp, unused, thus always 0
+# 28: low order byte of the current PTD entry, always 0 since the
+# PTD is page-aligned
+#
+7 string \357\020\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 FreeBSD/i386 a.out core file
+>1039 string >\0 from '%s'
+
+# /var/run/ld.so.hints
+# What are you laughing about?
+0 lelong 011421044151 ld.so hints file (Little Endian
+>4 lelong >0 \b, version %d)
+>4 belong <1 \b)
+0 belong 011421044151 ld.so hints file (Big Endian
+>4 belong >0 \b, version %d)
+>4 belong <1 \b)
+
+#
+# Files generated by FreeBSD scrshot(1)/vidcontrol(1) utilities
+#
+0 string SCRSHOT_ scrshot(1) screenshot,
+>8 byte x version %d,
+>9 byte 2 %d bytes in header,
+>>10 byte x %d chars wide by
+>>11 byte x %d chars high
+
+#
+# FreeBSD kernel minidumps
+#
+0 string minidump\040FreeBSD/ FreeBSD kernel minidump
+# powerpc uses 32-byte magic, followed by 32-byte mmu kind, then version
+>17 string powerpc
+>>17 string >\0 for %s,
+>>>32 string >\0 %s,
+>>>>64 byte 0 big endian,
+>>>>>64 belong x version %d
+>>>>64 default x little endian,
+>>>>>64 lelong x version %d
+# all other architectures use 24-byte magic, followed by version
+>17 default x
+>>17 string >\0 for %s,
+>>>24 byte 0 big endian,
+>>>>24 belong x version %d
+>>>24 default x little endian,
+>>>>24 lelong x version %d
diff --git a/contrib/libs/libmagic/magic/Magdir/fsav b/contrib/libs/libmagic/magic/Magdir/fsav
new file mode 100644
index 0000000000..5c1d6e23dc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/fsav
@@ -0,0 +1,128 @@
+
+#------------------------------------------------------------------------------
+# $File: fsav,v 1.22 2021/04/26 15:56:00 christos Exp $
+# fsav: file(1) magic for datafellows fsav virus definition files
+# Anthon van der Neut (anthon@mnt.org)
+
+# ftp://ftp.f-prot.com/pub/{macrdef2.zip,nomacro.def}
+0 beshort 0x1575 fsav macro virus signatures
+>8 leshort >0 (%d-
+>11 byte >0 \b%02d-
+>10 byte >0 \b%02d)
+# ftp://ftp.f-prot.com/pub/sign.zip
+#10 ubyte <12
+#>9 ubyte <32
+#>>8 ubyte 0x0a
+#>>>12 ubyte 0x07
+#>>>>11 uleshort >0 fsav DOS/Windows virus signatures (%d-
+#>>>>10 byte 0 \b01-
+#>>>>10 byte 1 \b02-
+#>>>>10 byte 2 \b03-
+#>>>>10 byte 3 \b04-
+#>>>>10 byte 4 \b05-
+#>>>>10 byte 5 \b06-
+#>>>>10 byte 6 \b07-
+#>>>>10 byte 7 \b08-
+#>>>>10 byte 8 \b09-
+#>>>>10 byte 9 \b10-
+#>>>>10 byte 10 \b11-
+#>>>>10 byte 11 \b12-
+#>>>>9 ubyte >0 \b%02d)
+# ftp://ftp.f-prot.com/pub/sign2.zip
+#0 ubyte 0x62
+#>1 ubyte 0xF5
+#>>2 ubyte 0x1
+#>>>3 ubyte 0x1
+#>>>>4 ubyte 0x0e
+#>>>>>13 ubyte >0 fsav virus signatures
+#>>>>>>11 ubyte x size %#02x
+#>>>>>>12 ubyte x \b%02x
+#>>>>>>13 ubyte x \b%02x bytes
+
+# Joerg Jenderek: joerg dot jenderek at web dot de
+# clamav-0.100.2\docs\html\node60.html
+# https://github.com/vrtadmin/clamav-faq/raw/master/manual/clamdoc.pdf
+# ClamAV virus database files start with a 512 bytes colon separated header
+# ClamAV-VDB:buildDate:version:signaturesNumbers:functionalityLevelRequired:MD5:Signature:builder:buildTime
+# + gzipped (optional) tarball files
+# output can often be verified by `sigtool --info=FILE`
+0 string ClamAV-VDB: Clam AntiVirus
+# padding spaces implies database
+>511 ubyte =0x20 database
+!:mime application/x-clamav-database
+# empty build time
+>>10 string =:: (unsigned)
+# sigtool(1) man page
+!:ext cud
+# display some text to avoid error like:
+# Magdir/fsav, 78: Warning: Current entry does not yet have a description for adding a EXTENSION type
+# file: could not find any valid magic files! (No error)
+>>10 default x (with buildtime)
+#>>10 default x
+# clamtmp is used for temporarily database like update process
+# for pure tar database only cld extension found
+!:ext cld/cvd/clamtmp/cud
+>511 default x file
+!:mime application/x-clamav
+!:ext info
+>11 string >\0
+# buildDate empty or like "22 Mar 2017 12-57 -0400"; verified by `sigtool -i FILE`
+>>11 regex \^[^:]{0,23} \b, %s
+# version like 25170
+>>>&1 regex \^[^:]{1,6} \b, version %s
+# signaturesNumbers like 4566249
+>>>>&1 regex \^[^:]{1,10} \b, %s signatures
+# functionalityLevelRequired like 60
+>>>>>&1 regex \^[^:]{1,4} \b, level %s
+# X for nothing or MD5
+#>>>>>>&1 regex \^[^:]{1,32} \b, MD5 "%s"
+>>>>>>&1 regex \^[^:]{1,32}
+# X for nothing or digital signature starting like AIzk/LYbX
+#>>>>>>>&1 regex \^[^:]{1,255} \b, signature "%s"
+>>>>>>>&1 regex \^[^:]{1,255}
+# builder like neo
+>>>>>>>>&1 regex \^[^:]{1,32} \b, builder %s
+# buildTime like 1506611558
+#>>>>>>>>>&1 regex \^[^:]{1,10} \b, %s
+>>>>>>>>>&1 regex \^[^:]{1,10}
+# padding with spaces
+#>>>>>>>>>>&1 ubequad x \b, padding %#16.16llx
+>510 ubyte =0x20
+# inspect real database content
+#>>512 ubeshort x \b, database MAGIC %#x
+# ./archive handle pure tar archives
+>>1012 quad =0 \b, with
+>>>512 use tar-file
+# not pure tar
+>>1012 quad !0
+# one space at the end of text and then handles gzipped archives by ./compress
+>>>512 string \037\213 \b, with
+>>>>512 indirect x
+
+# Type: Grisoft AVG AntiVirus
+# From: David Newgas <david@newgas.net>
+0 string AVG7_ANTIVIRUS_VAULT_FILE AVG 7 Antivirus vault file data
+
+0 string X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR
+>33 string -STANDARD-ANTIVIRUS-TEST-FILE!$H+H* EICAR virus test files
+
+# From: Joerg Jenderek
+# URL: https://www.avira.com/
+# Note: found in directory %ProgramData%\Avira\Antivirus\INFECTED (Windows)
+# tested with version 15.0.43.23 at November 2019
+0 string AntiVir\ Qua Avira AntiVir quarantined
+!:mime application/x-avira-qua
+#!:mime application/octet-stream
+!:ext qua
+>156 string SUSPICIOUS_FILE
+# file path of suspicious file
+>>220 lestring16 x %s
+>156 string !SUSPICIOUS_FILE
+# file path of virus file
+>>228 lestring16 x %s
+# quarantined date
+>60 ldate x at %s
+# virus/danger name
+>156 string !SUSPICIOUS_FILE
+>>156 string x \b, category "%s"
+
diff --git a/contrib/libs/libmagic/magic/Magdir/fusecompress b/contrib/libs/libmagic/magic/Magdir/fusecompress
new file mode 100644
index 0000000000..165cf3c772
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/fusecompress
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: fusecompress,v 1.2 2011/08/08 09:05:55 christos Exp $
+# fusecompress: file(1) magic for fusecompress
+0 string \037\135\211 FuseCompress(ed) data
+>3 byte 0x00 (none format)
+>3 byte 0x01 (bz2 format)
+>3 byte 0x02 (gz format)
+>3 byte 0x03 (lzo format)
+>3 byte 0x04 (xor format)
+>3 byte >0x04 (unknown format)
+>4 long x uncompressed size: %d
diff --git a/contrib/libs/libmagic/magic/Magdir/games b/contrib/libs/libmagic/magic/Magdir/games
new file mode 100644
index 0000000000..0ccb4acff5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/games
@@ -0,0 +1,696 @@
+
+#------------------------------------------------------------------------------
+# $File: games,v 1.31 2023/03/29 22:57:27 christos Exp $
+# games: file(1) for games
+
+# Fabio Bonelli <fabiobonelli@libero.it>
+# Quake II - III data files
+0 string IDP2 Quake II 3D Model file,
+>20 long x %u skin(s),
+>8 long x (%u x
+>12 long x %u),
+>40 long x %u frame(s),
+>16 long x Frame size %u bytes,
+>24 long x %u vertices/frame,
+>28 long x %u texture coordinates,
+>32 long x %u triangles/frame
+
+0 string IBSP Quake
+>4 long 0x26 II Map file (BSP)
+>4 long 0x2E III Map file (BSP)
+
+0 string IDS2 Quake II SP2 sprite file
+
+#---------------------------------------------------------------------------
+# Doom and Quake
+# submitted by Nicolas Patrois
+
+0 string \xcb\x1dBoom\xe6\xff\x03\x01 Boom or linuxdoom demo
+# some doom lmp files don't match, I've got one beginning with \x6d\x02\x01\x01
+
+24 string LxD\ 203 Linuxdoom save
+>0 string x , name=%s
+>44 string x , world=%s
+
+# Quake
+
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/PAK
+# reference: https://quakewiki.org/wiki/.pak
+# GRR: line below is too general as it matches also Acorn PackDir compressed Archive
+# and Git pack ./revision
+0 string PACK
+# real Quake examples like pak0.pak have only some hundreds like 150 files
+# So test for few files
+>8 ulelong <0x01000000
+# in file version 5.32 test for null terminator is only true for
+# offset ~< FILE_BYTES_MAX = 1 MB defined in ../../src/file.h
+# look for null terminator of 1st entry name
+>>(4.l+55) ubyte 0 Quake I or II world or extension
+!:mime application/x-dzip
+!:ext pak
+#>>>8 ulelong x \b, table size %u
+# dividing this by entry size (64) gives number of files
+>>>8 ulelong/64 x \b, %u files
+# offset to the beginning of the file table
+>>>4 ulelong x \b, offset %#x
+# 1st file entry
+>>>(4.l) use pak-entry
+# 2nd file entry
+#>>>4 ulelong+64 x \b, offset %#x
+#>>>(4.l+64) use pak-entry
+#
+# display file table entry of Quake PAK archive
+0 name pak-entry
+# normally entry start after header which implies offset 12 or higher
+>56 ulelong >11
+# the offset from the beginning of pak to beginning of this entry file contents
+>>56 ulelong x at %#x
+# the size of file for this entry
+>>60 ulelong x %u bytes
+# 56 byte null-terminated entry name string includes path like maps/e1m1.bsp
+>>0 string x '%-.56s'
+# inspect entry content by jumping to entry offset
+>>(56) indirect x \b:
+
+#0 string -1\x0a Quake I demo
+#>30 string x version %.4s
+#>61 string x level %s
+
+#0 string 5\x0a Quake I save
+
+# The levels
+
+# Quake 1
+
+0 string 5\x0aIntroduction Quake I save: start Introduction
+0 string 5\x0athe_Slipgate_Complex Quake I save: e1m1 The slipgate complex
+0 string 5\x0aCastle_of_the_Damned Quake I save: e1m2 Castle of the damned
+0 string 5\x0athe_Necropolis Quake I save: e1m3 The necropolis
+0 string 5\x0athe_Grisly_Grotto Quake I save: e1m4 The grisly grotto
+0 string 5\x0aZiggurat_Vertigo Quake I save: e1m8 Ziggurat vertigo (secret)
+0 string 5\x0aGloom_Keep Quake I save: e1m5 Gloom keep
+0 string 5\x0aThe_Door_To_Chthon Quake I save: e1m6 The door to Chthon
+0 string 5\x0aThe_House_of_Chthon Quake I save: e1m7 The house of Chthon
+0 string 5\x0athe_Installation Quake I save: e2m1 The installation
+0 string 5\x0athe_Ogre_Citadel Quake I save: e2m2 The ogre citadel
+0 string 5\x0athe_Crypt_of_Decay Quake I save: e2m3 The crypt of decay (dopefish lives!)
+0 string 5\x0aUnderearth Quake I save: e2m7 Underearth (secret)
+0 string 5\x0athe_Ebon_Fortress Quake I save: e2m4 The ebon fortress
+0 string 5\x0athe_Wizard's_Manse Quake I save: e2m5 The wizard's manse
+0 string 5\x0athe_Dismal_Oubliette Quake I save: e2m6 The dismal oubliette
+0 string 5\x0aTermination_Central Quake I save: e3m1 Termination central
+0 string 5\x0aVaults_of_Zin Quake I save: e3m2 Vaults of Zin
+0 string 5\x0athe_Tomb_of_Terror Quake I save: e3m3 The tomb of terror
+0 string 5\x0aSatan's_Dark_Delight Quake I save: e3m4 Satan's dark delight
+0 string 5\x0athe_Haunted_Halls Quake I save: e3m7 The haunted halls (secret)
+0 string 5\x0aWind_Tunnels Quake I save: e3m5 Wind tunnels
+0 string 5\x0aChambers_of_Torment Quake I save: e3m6 Chambers of torment
+0 string 5\x0athe_Sewage_System Quake I save: e4m1 The sewage system
+0 string 5\x0aThe_Tower_of_Despair Quake I save: e4m2 The tower of despair
+0 string 5\x0aThe_Elder_God_Shrine Quake I save: e4m3 The elder god shrine
+0 string 5\x0athe_Palace_of_Hate Quake I save: e4m4 The palace of hate
+0 string 5\x0aHell's_Atrium Quake I save: e4m5 Hell's atrium
+0 string 5\x0athe_Nameless_City Quake I save: e4m8 The nameless city (secret)
+0 string 5\x0aThe_Pain_Maze Quake I save: e4m6 The pain maze
+0 string 5\x0aAzure_Agony Quake I save: e4m7 Azure agony
+0 string 5\x0aShub-Niggurath's_Pit Quake I save: end Shub-Niggurath's pit
+
+# Quake DeathMatch levels
+
+0 string 5\x0aPlace_of_Two_Deaths Quake I save: dm1 Place of two deaths
+0 string 5\x0aClaustrophobopolis Quake I save: dm2 Claustrophobopolis
+0 string 5\x0aThe_Abandoned_Base Quake I save: dm3 The abandoned base
+0 string 5\x0aThe_Bad_Place Quake I save: dm4 The bad place
+0 string 5\x0aThe_Cistern Quake I save: dm5 The cistern
+0 string 5\x0aThe_Dark_Zone Quake I save: dm6 The dark zone
+
+# Scourge of Armagon
+
+0 string 5\x0aCommand_HQ Quake I save: start Command HQ
+0 string 5\x0aThe_Pumping_Station Quake I save: hip1m1 The pumping station
+0 string 5\x0aStorage_Facility Quake I save: hip1m2 Storage facility
+0 string 5\x0aMilitary_Complex Quake I save: hip1m5 Military complex (secret)
+0 string 5\x0athe_Lost_Mine Quake I save: hip1m3 The lost mine
+0 string 5\x0aResearch_Facility Quake I save: hip1m4 Research facility
+0 string 5\x0aAncient_Realms Quake I save: hip2m1 Ancient realms
+0 string 5\x0aThe_Gremlin's_Domain Quake I save: hip2m6 The gremlin's domain (secret)
+0 string 5\x0aThe_Black_Cathedral Quake I save: hip2m2 The black cathedral
+0 string 5\x0aThe_Catacombs Quake I save: hip2m3 The catacombs
+0 string 5\x0athe_Crypt__ Quake I save: hip2m4 The crypt
+0 string 5\x0aMortum's_Keep Quake I save: hip2m5 Mortum's keep
+0 string 5\x0aTur_Torment Quake I save: hip3m1 Tur torment
+0 string 5\x0aPandemonium Quake I save: hip3m2 Pandemonium
+0 string 5\x0aLimbo Quake I save: hip3m3 Limbo
+0 string 5\x0athe_Edge_of_Oblivion Quake I save: hipdm1 The edge of oblivion (secret)
+0 string 5\x0aThe_Gauntlet Quake I save: hip3m4 The gauntlet
+0 string 5\x0aArmagon's_Lair Quake I save: hipend Armagon's lair
+
+# Malice
+
+0 string 5\x0aThe_Academy Quake I save: start The academy
+0 string 5\x0aThe_Lab Quake I save: d1 The lab
+0 string 5\x0aArea_33 Quake I save: d1b Area 33
+0 string 5\x0aSECRET_MISSIONS Quake I save: d3b Secret missions
+0 string 5\x0aThe_Hospital Quake I save: d10 The hospital (secret)
+0 string 5\x0aThe_Genetics_Lab Quake I save: d11 The genetics lab (secret)
+0 string 5\x0aBACK_2_MALICE Quake I save: d4b Back to Malice
+0 string 5\x0aArea44 Quake I save: d1c Area 44
+0 string 5\x0aTakahiro_Towers Quake I save: d2 Takahiro towers
+0 string 5\x0aA_Rat's_Life Quake I save: d3 A rat's life
+0 string 5\x0aInto_The_Flood Quake I save: d4 Into the flood
+0 string 5\x0aThe_Flood Quake I save: d5 The flood
+0 string 5\x0aNuclear_Plant Quake I save: d6 Nuclear plant
+0 string 5\x0aThe_Incinerator_Plant Quake I save: d7 The incinerator plant
+0 string 5\x0aThe_Foundry Quake I save: d7b The foundry
+0 string 5\x0aThe_Underwater_Base Quake I save: d8 The underwater base
+0 string 5\x0aTakahiro_Base Quake I save: d9 Takahiro base
+0 string 5\x0aTakahiro_Laboratories Quake I save: d12 Takahiro laboratories
+0 string 5\x0aStayin'_Alive Quake I save: d13 Stayin' alive
+0 string 5\x0aB.O.S.S._HQ Quake I save: d14 B.O.S.S. HQ
+0 string 5\x0aSHOWDOWN! Quake I save: d15 Showdown!
+
+# Malice DeathMatch levels
+
+0 string 5\x0aThe_Seventh_Precinct Quake I save: ddm1 The seventh precinct
+0 string 5\x0aSub_Station Quake I save: ddm2 Sub station
+0 string 5\x0aCrazy_Eights! Quake I save: ddm3 Crazy eights!
+0 string 5\x0aEast_Side_Invertationa Quake I save: ddm4 East side invertationa
+0 string 5\x0aSlaughterhouse Quake I save: ddm5 Slaughterhouse
+0 string 5\x0aDOMINO Quake I save: ddm6 Domino
+0 string 5\x0aSANDRA'S_LADDER Quake I save: ddm7 Sandra's ladder
+
+
+0 string MComprHD MAME CHD compressed hard disk image,
+>12 belong x version %u
+
+# MAME input recordings
+
+0 string MAMEINP\0 MAME input recording
+>8 leqdate x at %s,
+>16 leshort x format version %d.
+>18 leshort x \b%d,
+>20 string x %s driver,
+>32 string x %s
+
+# doom - submitted by Jon Dowland
+
+0 string =IWAD doom main IWAD data
+>4 lelong x containing %d lumps
+0 string =PWAD doom patch PWAD data
+>4 lelong x containing %d lumps
+
+# Build engine group files (Duke Nukem, Shadow Warrior, ...)
+# Extension: .grp
+# Created by: "Ganael Laplanche" <ganael.laplanche@martymac.org>
+0 string KenSilverman Build engine group file
+>12 lelong x containing %d files
+
+# Summary: Warcraft 3 save
+# Extension: .w3g
+# Created by: "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string Warcraft\ III\ recorded\ game %s
+
+
+# Summary: Warcraft 3 map
+# Extension: .w3m
+# Created by: "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string HM3W Warcraft III map file
+
+
+# Summary: SGF Smart Game Format
+# Extension: .sgf
+# Reference: https://www.red-bean.com/sgf/
+# Created by: Eduardo Sabbatella <eduardo_sabbatella@yahoo.com.ar>
+# Modified by (1): Abel Cheung (regex, more game format)
+# FIXME: Some games don't have GM (game type)
+0 regex \\(;.*GM\\[[0-9]{1,2}\\] Smart Game Format
+>2 search/0x200/b GM[
+>>&0 string 1] (Go)
+>>&0 string 2] (Othello)
+>>&0 string 3] (chess)
+>>&0 string 4] (Gomoku+Renju)
+>>&0 string 5] (Nine Men's Morris)
+>>&0 string 6] (Backgammon)
+>>&0 string 7] (Chinese chess)
+>>&0 string 8] (Shogi)
+>>&0 string 9] (Lines of Action)
+>>&0 string 10] (Ataxx)
+>>&0 string 11] (Hex)
+>>&0 string 12] (Jungle)
+>>&0 string 13] (Neutron)
+>>&0 string 14] (Philosopher's Football)
+>>&0 string 15] (Quadrature)
+>>&0 string 16] (Trax)
+>>&0 string 17] (Tantrix)
+>>&0 string 18] (Amazons)
+>>&0 string 19] (Octi)
+>>&0 string 20] (Gess)
+>>&0 string 21] (Twixt)
+>>&0 string 22] (Zertz)
+>>&0 string 23] (Plateau)
+>>&0 string 24] (Yinsh)
+>>&0 string 25] (Punct)
+>>&0 string 26] (Gobblet)
+>>&0 string 27] (hive)
+>>&0 string 28] (Exxit)
+>>&0 string 29] (Hnefatal)
+>>&0 string 30] (Kuba)
+>>&0 string 31] (Tripples)
+>>&0 string 32] (Chase)
+>>&0 string 33] (Tumbling Down)
+>>&0 string 34] (Sahara)
+>>&0 string 35] (Byte)
+>>&0 string 36] (Focus)
+>>&0 string 37] (Dvonn)
+>>&0 string 38] (Tamsk)
+>>&0 string 39] (Gipf)
+>>&0 string 40] (Kropki)
+
+##############################################
+# NetImmerse/Gamebryo game engine entries
+
+# Summary: Gamebryo game engine file
+# Extension: .nif, .kf
+# Created by: Abel Cheung <abelcheung@gmail.com>
+0 string Gamebryo\ File\ Format,\ Version\ Gamebryo game engine file
+>&0 regex [0-9a-z.]+ \b, version %s
+
+# Summary: Gamebryo game engine file
+# Extension: .kfm
+# Created by: Abel Cheung <abelcheung@gmail.com>
+0 string ;Gamebryo\ KFM\ File\ Version\ Gamebryo game engine animation File
+>&0 regex [0-9a-z.]+ \b, version %s
+
+# Summary: NetImmerse game engine file
+# Extension .nif
+# Created by: Abel Cheung <abelcheung@gmail.com>
+0 string NetImmerse\ File\ Format,\ Version
+>&0 string n\ NetImmerse game engine file
+>>&0 regex [0-9a-z.]+ \b, version %s
+
+# Type: SGF Smart Game Format
+# URL: https://www.red-bean.com/sgf/
+# From: Eduardo Sabbatella <eduardo_sabbatella@yahoo.com.ar>
+2 regex/c \\(;.*GM\\[[0-9]{1,2}\\] Smart Game Format
+>2 regex/c GM\\[1\\] - Go Game
+>2 regex/c GM\\[6\\] - BackGammon Game
+>2 regex/c GM\\[11\\] - Hex Game
+>2 regex/c GM\\[18\\] - Amazons Game
+>2 regex/c GM\\[19\\] - Octi Game
+>2 regex/c GM\\[20\\] - Gess Game
+>2 regex/c GM\\[21\\] - twix Game
+
+# Epic Games/Unreal Engine Package
+# URL: https://docs.unrealengine.com/udk/Three/ContentCooking.html
+# https://eliotvu.com/page/unreal-package-file-format
+# Little-endian version (such as x86 PC)
+0 lelong 0x9E2A83C1 Unreal Engine package (little-endian)
+!:ext xxx/tfc/upk/me1/u
+>4 uleshort !0 \b, version %u
+>>6 uleshort !0 \b/%03u
+>>0 use upk_header
+# Big-endian version (such as PS3)
+0 belong 0x9E2A83C1 Unreal Engine package (big-endian)
+!:ext xxx/tfc
+>6 ubeshort !0 \b, version %u
+>>4 ubeshort !0 \b/%03u
+>>0 use \^upk_header
+
+0 name upk_header
+# Identify game from version and licensee
+>4 ulelong 0x000002b2 (Alice Madness Returns)
+>4 ulelong 0x002f0313 (Aliens: Colonial Marines)
+>4 ulelong 0x005b021b (Alpha Protocol)
+>4 ulelong 0x0000032c (AntiChamber)
+>4 ulelong 0x00200223 (APB: All Points Bulletin)
+>4 ulelong 0x004b02d7 (Bioshock Infinite)
+>4 ulelong 0x00380340 (Borderlands 2)
+>4 ulelong 0x001d02e6 (Bulletstorm)
+>4 ulelong 0x00050240 (CrimeCraft)
+>4 ulelong 0x00000356 (Deadlight)
+>4 ulelong 0x001e0321 (Dishonored)
+>4 ulelong 0x000202a6 (Dungeon Defenders)
+>4 ulelong 0x000901ea (Gears of War)
+>4 ulelong 0x0000023f (Gears of War 2)
+>4 ulelong 0x0000033c (Gears of War 3)
+>4 ulelong 0x0000034e (Gears of War: Judgement)
+>4 ulelong 0x0004035c (Hawken)
+>4 ulelong 0x0001034a (Infinity Blade 2)
+>4 ulelong 0x00000350 (InMomentum)
+>4 ulelong 0x0015037D (Life Is Strange)
+>4 ulelong 0x000b01a5 (Medal of Honor: Airborne)
+>4 ulelong 0x002b0218 (Mirrors Edge)
+>4 ulelong 0x0000027e (Monday Night Combat)
+>4 ulelong 0x0000024b (MoonBase Alpha)
+>4 ulelong 0x002e01d8 (Mortal Kombat Komplete Edition 2605)
+>4 ulelong 0x0000035c (Painkiller HD)
+>4 ulelong 0x0000034d (Q.U.B.E)
+>4 ulelong 0x80660340 (Quantum Conundrum)
+>4 ulelong 0x0000035b (Ravaged)
+>4 ulelong 0x00150340 (Remember Me)
+>4 ulelong 0x00060171 (Roboblitz)
+>4 ulelong 0x00000325 (Rock of Ages)
+>4 ulelong 0x0000032a (Sanctum)
+>4 ulelong 0x00030248 (Saw)
+>4 ulelong 0x007e0248 (Singularity)
+>4 ulelong 0x00090388 (Soldier Front 2)
+>4 ulelong 0x000701e6 (Stargate Worlds)
+>4 ulelong 0x00000334 (Super Monday Night Combat)
+>4 ulelong 0x000002c2 (The Ball)
+>4 ulelong 0x000e0262 (The Exiled Realm of Arborea or TERA)
+>4 ulelong 0x0000035b (The Five Cores)
+>4 ulelong 0x00000349 (The Haunted: Hells Reach)
+>4 ulelong 0x00000354 (Unmechanical)
+>4 ulelong 0x035c0298 (Unreal Development Kit)
+>4 ulelong 0x00000200 (Unreal Tournament 3)
+>4 ulelong 0x0000032d (Waves)
+>4 ulelong 0x003b034d (XCOM: Enemy Unknown)
+# Newer versions insert more headers
+>4 ulelong&0xFFFF <249
+>>12 lelong !0 \b, names: %d
+>>28 lelong !0 \b, imports: %d
+>>20 lelong !0 \b, exports: %d
+>4 ulelong&0xFFFF >248
+>>12 belong&0xFF !0
+>>>12 string x \b, folder "%s"
+>>>>&5 lelong !0 \b, names: %d
+>>>>&21 lelong !0 \b, imports: %d
+>>>>&13 lelong !0 \b, exports: %d
+>>12 belong&0xFF 0
+>>>16 belong&0xFF !0
+>>>>16 string x \b, folder "%s"
+>>>>>&5 lelong !0 \b, names: %d
+>>>>>&21 lelong !0 \b, imports: %d
+>>>>>&13 lelong !0 \b, exports: %d
+>>>16 belong&0xFF 0
+>>>>20 string x \b, folder "%s"
+>>>>>&5 lelong !0 \b, names: %d
+>>>>>&21 lelong !0 \b, imports: %d
+>>>>>&13 lelong !0 \b, exports: %d
+
+0 string ESVG
+>4 lelong 0x00160000
+>10 string TOC\020 Empire Deluxe for DOS saved game
+
+# Sid Meier's Civilization V/VI
+# From: Benjamin Lowry <ben@ben.gmbh>
+0 string CIV5
+>4 byte 0x08 Sid Meier's Civilization V saved game,
+>>12 regex [0-9a-z.]+ saved by game version %s
+>4 byte 0x01 Sid Meier's Civilization V replay data,
+>>12 regex [0-9a-z.]+ saved by game version %s
+
+0 string CIV6 Sid Meier's Civilization VI saved game
+
+# https://syzygy-tables.info/
+# From Michel Van den Bergh
+0 string \327f\f\245 Syzygy DTZ tablebase
+!:mime application/syzygy
+0 string q\350#] Syzygy WDL tablebase
+!:mime application/syzygy
+
+##############################################################################
+# Grand Theft Auto (GTA) file formats.
+#
+# Summary:
+# Includes GTA-specific formats used in all games from 1997 to present. Games
+# and formats were created by Rockstar North, formerly DMA Design. Magic tests
+# were written based on a combination of official and community documentation.
+#
+# Created by: Oliver Galvin <odg@riseup.net>
+#
+# References:
+# * Classic GTA documentation and research:
+# <https://gitlab.com/classic-gta/gta-data>
+# * Official RenderWare documentation available from EA:
+# <https://github.com/electronicarts/RenderWare3Docs>
+# * Lots of community research in the GTAMods wiki:
+# <https://gtamods.com/wiki>
+
+# GTA 2D-Era data - 'Classic' top down games (1/L/2)
+
+## GTA text
+
+0 string \xbf\xf8\xbd\x49\x62\xbe GTA1 in-game text (FXT),
+0 string GBL GTA2 in-game text (GXT),
+>3 string E English,
+>>4 uleshort x version %d
+>3 string F French,
+>>4 uleshort x version %d
+>3 string G German,
+>>4 uleshort x version %d
+>3 string I Italian,
+>>4 uleshort x version %d
+>3 string S Spanish,
+>>4 uleshort x version %d
+>3 string J Japanese,
+>>4 uleshort x version %d
+
+## GTA maps
+
+0 ulelong 331 GTA1 map layout (CMP),
+>4 byte 1 Level 1
+>4 byte 2 Level 2
+>4 byte 3 Level 3
+0 string GBMP GTA2/GBH map layout (GMP),
+>4 uleshort x version %d
+0 string/t [MapFiles] GTA2 multiplayer map metadata (MMP)
+0 string/t MainOrBonus\ =\ MAIN GTA2 single player map listing (test1.seq)
+
+## GTA 2D sprites and textures
+
+0 ulelong 290 GTA1 style data (GRX), 8 bit editor graphics
+0 ulelong 325 GTA1 style data (GRY), 8 bit in-game graphics
+0 ulelong 336 GTA1 style data (G24), 24 bit in-game graphics
+0 string GBST GTA2/GBH style data (STY), in-game graphics,
+>4 uleshort x version %d
+
+## GTA audio index
+
+0 ulelong 0
+>4 ulelong <0x40000
+>>8 ulelong >4500
+>>>8 ulelong <45000 GTA audio index data (SDT)
+
+## GTA scripts
+
+0 ulelong 0x00080000
+>4 uleshort 0x0024 GTA2 binary main script (SCR)
+
+0 uleshort 0x063c GTA2 binary mission script (SCR), Residential area (ste)
+0 uleshort 0x055b GTA2 binary mission script (SCR), Downtown area (wil)
+0 uleshort 0x0469 GTA2 binary mission script (SCR), Industrial area (bil)
+
+0 string v9.6\0\0 GTA2 replay file (REP),
+>8 regex/30c [a-z0-9:\ ]+\0\0 created on %s
+
+# GTA 3D-Era (III/VC/SA/LCS/VCS) - used by the RenderWare engine by Criterion Games
+
+## GTA 3D models and textures - RenderWare binary streams
+
+8 ulelong 0x00000310 RenderWare data, v3.1.0.0, used in GTA III on PS2,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x0401ffff RenderWare data, v3.1.0.1, used in GTA III on PC/PS2,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x0800ffff RenderWare data, v3.2.0.0, used in GTA III on PC,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x0c00ffff RenderWare data, v3.3.0.0,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x0c02ffff RenderWare data, v3.3.0.2, used in GTA III PC and GTA VC PS2,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x1000ffff RenderWare data, v3.4.0.0,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x1003ffff RenderWare data, v3.4.0.3, used in GTA VC PC,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x1005ffff RenderWare data, v3.4.0.5, used in GTA III/VC on Android,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x1400ffff RenderWare data, v3.5.0.0, used in GTA III/VC on Xbox,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+8 ulelong 0x1803ffff RenderWare data, v3.6.0.3, used in GTA SA,
+>0 ulelong 0x00000016 texture archive (TXD)
+>0 ulelong 0x00000010 3D models (DFF)
+
+0 string COL RenderWare collision data (COL),
+>3 string L version 1, used in GTA III/VC/SA
+>3 string 2 version 2, used in GTA SA
+>3 string 3 version 3, used in GTA SA
+>3 string 4 version 4, used in GTA SA
+
+## GTA items and animations
+
+0 string/c #\ ipl\ generated\ from\ max\ file GTA Item Placement data (IPL), used in GTA III/VC
+0 string/b bnry GTA Item Placement data (IPL), used in GTA SA/IV,
+>4 ulelong x %d items
+
+0 string ANP GTA animation data (IFP),
+>3 string K version 1, used in GTA III/VC
+>3 string 3 version 2, used in GTA SA
+
+0 string GtaSA29 GTA Replay data (REP), used in GTA SA
+
+## GTA text
+
+0 string TKEY GTA in-game text (GXT), version 2, used in GTA III
+0 string TABL GTA in-game text (GXT), version 3, used in GTA VC/LS/VCS
+
+## GTA scripts
+
+0 string \x02\x00\x01 GTA script (SCM), used in GTA III/VC/SA
+
+## GTA archives
+
+0 string VER2 GTA archive (IMG), version 2, used in GTA SA,
+>4 ulelong x %d items
+
+# GTA HD-Era (IV/V) - used by the Rockstar Advanced Game Engine (RAGE)
+
+## GTA models and textures - RAGE resources
+# Note: GTA IV formats not yet documented - WAD, WBD, WBN, WHM, WPL
+
+0 ulelong 0x00695254 GTA Drawable data (WDR), model and weapon data, used in GTA IV
+0 ulelong 0x00695238 GTA Windows Frag Type (WFT), vehicle models, used in GTA IV
+0 ulelong 0x006953A4 GTA Ped and LOD models (WDD), used in GTA IV
+0 ulelong 0x00695384 GTA Windows Texture Dictionary (WTD), used in GTA IV
+
+## GTA text
+
+4 string TABL GTA in-game text (GXT),
+>0 uleshort x version %d, used in GTA SA/IV
+0 string 2GXT GTA in-game text (GXT2), used in GTA V
+
+## GTA scripts
+
+0 ulelong 0x0d524353 GTA script (SCO), unencrypted, used in GTA IV,
+>4 ulelong x %d code bytes,
+>>8 ulelong x %d static variables,
+>>>12 ulelong x %d global variables
+0 ulelong 0x0e726373 GTA script (SCO), encrypted, used in GTA IV
+>4 ulelong x %d code bytes,
+>>8 ulelong x %d static variables,
+>>>12 ulelong x %d global variables
+
+## GTA archives
+
+0 ulelong 0xa94e2a52 GTA archive (IMG),
+>4 ulelong x version %d, used in GTA IV,
+>>8 ulelong x %d items
+
+# RPF[0-8]
+0 ulelong&0xfffffff0 =0x52504630
+>0 ulelong&0xf <9 RAGE Package Format (RPF), version %d, used in
+>>0 ulelong&0xf =0 Rockstar Table Tennis,
+>>0 ulelong&0xf =1 *unknown*
+>>0 ulelong&0xf =2 GTA IV,
+>>0 ulelong&0xf =3 GTA IV Audio & Midnight Club: LA,
+>>0 ulelong&0xf =4 Max Payne 3,
+>>0 ulelong&0xf =5 *unknown*
+>>0 ulelong&0xf =6 RDR,
+>>0 ulelong&0xf =7 GTA V,
+>>0 ulelong&0xf =8 RDR 2,
+>>4 ulelong x %d bytes,
+>>>8 ulelong x %d entries
+
+# Blitz3D Model File Format
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/minetest/B3DExport/blob/master/B3DExport.py
+0 string BB3D
+>4 lelong >0
+>>8 lelong >0 Blitz3D Model
+!:ext b3d
+>>>8 lelong x \b, version %d
+
+# Minetest Schematic File Format
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/minetest/minetest/blob/5.6.1/src/mapgen/mg_schematic.h
+0 string MTSM Minetest Schematic
+!:ext mts
+>4 ubeshort x \b, version %d
+>6 ubeshort x \b, size [%d
+>8 ubeshort x \b, %d
+>10 ubeshort x \b, %d]
+
+# MagicaVoxel File Format
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/ephtracy/voxel-model/blob/ee2216c28a78ebb68691dc6cfa9c4ba429117ea2/MagicaVoxel-file-format-vox.txt
+# Note: This format is used in Veloren voxel RPG.
+0 string VOX\x20
+>4 lelong >0 MagicaVoxel model
+!:ext vox
+>>4 lelong x \b, version %d
+
+# Wwise SoundBank
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://wiki.xentax.com/index.php/Wwise_SoundBank_(*.bnk)
+0 string BKHD
+# Little-endian version (such as x86 PC)
+>4 ulelong <0x100 Wwise SoundBank (little-endian)
+!:ext bnk
+>>0 use wwise_bkhd
+# Big-endian version (such as PS3)
+>4 ubelong <0x100 Wwise SoundBank (big-endian)
+!:ext bnk
+>>0 use \^wwise_bkhd
+
+0 name wwise_bkhd
+>8 ulelong x \b, version %d
+>12 ulelong x \b, id %08X
+>16 ulelong =0x00 \b, SFX
+>16 ulelong =0x01 \b, arabic
+>16 ulelong =0x02 \b, bulgarian
+>16 ulelong =0x03 \b, chinese (HK)
+>16 ulelong =0x04 \b, chinese (PRC)
+>16 ulelong =0x05 \b, chinese (Taiwan)
+>16 ulelong =0x06 \b, czech
+>16 ulelong =0x07 \b, danish
+>16 ulelong =0x08 \b, dutch
+>16 ulelong =0x09 \b, english (Australia)
+>16 ulelong =0x0A \b, english (India)
+>16 ulelong =0x0B \b, english (UK)
+>16 ulelong =0x0C \b, english (US)
+>16 ulelong =0x0D \b, finnish
+>16 ulelong =0x0E \b, french (Canada)
+>16 ulelong =0x0F \b, french (France)
+>16 ulelong =0x10 \b, german
+>16 ulelong =0x11 \b, greek
+>16 ulelong =0x12 \b, hebrew
+>16 ulelong =0x13 \b, hungarian
+>16 ulelong =0x14 \b, indonesian
+>16 ulelong =0x15 \b, italian
+>16 ulelong =0x16 \b, japanese
+>16 ulelong =0x17 \b, korean
+>16 ulelong =0x18 \b, latin
+>16 ulelong =0x19 \b, norwegian
+>16 ulelong =0x1A \b, polish
+>16 ulelong =0x1B \b, portuguese (Brazil)
+>16 ulelong =0x1C \b, portuguese (Portugal)
+>16 ulelong =0x1D \b, romanian
+>16 ulelong =0x1E \b, russian
+>16 ulelong =0x1F \b, slovenian
+>16 ulelong =0x20 \b, spanish (Mexico)
+>16 ulelong =0x21 \b, spanish (Spain)
+>16 ulelong =0x22 \b, spanish (US)
+>16 ulelong =0x23 \b, swedish
+>16 ulelong =0x24 \b, turkish
+>16 ulelong =0x25 \b, ukrainian
+>16 ulelong =0x26 \b, vietnamese
+
+# Wwise Audio Package
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://wiki.xentax.com/index.php/Wwise_Audio_PCK
+0 string AKPK
+# Little-endian version (such as x86 PC)
+>8 ulelong <0x100 Wwise Audio Package (little-endian)
+!:ext pck
+# Big-endian version (such as PS3)
+>8 ubelong <0x100 Wwise Audio Package (big-endian)
+!:ext pck
diff --git a/contrib/libs/libmagic/magic/Magdir/gcc b/contrib/libs/libmagic/magic/Magdir/gcc
new file mode 100644
index 0000000000..ae98dc7dbc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gcc
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# $File: gcc,v 1.5 2016/07/01 23:31:13 christos Exp $
+# gcc: file(1) magic for GCC special files
+#
+0 string gpch GCC precompiled header
+
+# The version field is annoying. It's 3 characters, not zero-terminated.
+>5 byte x (version %c
+>6 byte x \b%c
+>7 byte x \b%c)
+
+# 67 = 'C', 111 = 'o', 43 = '+', 79 = 'O'
+>4 byte 67 for C
+>4 byte 111 for Objective-C
+>4 byte 43 for C++
+>4 byte 79 for Objective-C++
diff --git a/contrib/libs/libmagic/magic/Magdir/gconv b/contrib/libs/libmagic/magic/Magdir/gconv
new file mode 100644
index 0000000000..eec5ddcd7a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gconv
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# $File: gconv
+# gconv: file(1) magic for iconv/gconv module configuration cache
+#
+# Magic number defined in glibc/iconv/iconvconfig.h as GCONVCACHE_MAGIC
+#
+# From: Marek Cermak <macermak@redhat.com>
+#
+0 lelong 0x20010324 gconv module configuration cache data
diff --git a/contrib/libs/libmagic/magic/Magdir/gentoo b/contrib/libs/libmagic/magic/Magdir/gentoo
new file mode 100644
index 0000000000..f988047ad4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gentoo
@@ -0,0 +1,85 @@
+#------------------------------------------------------------------------------
+# $File: gentoo,v 1.5 2022/12/26 17:16:55 christos Exp $
+# gentoo: file(1) magic for gentoo specific formats
+#
+# Summary: Gentoo ebuild Manifest files (GLEP 74)
+# Reference: https://www.gentoo.org/glep/glep-0074.html
+# Submitted by: Michal Gorny <mgorny@gentoo.org>
+# Start by doing a fast check for the most common tags.
+0 string AUX
+>0 use gentoo-manifest
+0 string DATA
+>0 use gentoo-manifest
+0 string DIST
+>0 use gentoo-manifest
+0 string EBUILD
+>0 use gentoo-manifest
+0 string MANIFEST
+>0 use gentoo-manifest
+
+# Manifest can be PGP-signed.
+0 string -----BEGIN\040PGP\040SIGNED\040MESSAGE-----
+>34 search/32 \n\n
+>>&0 string AUX
+>>>&0 use gentoo-manifest
+>>&0 string DATA
+>>>&0 use gentoo-manifest
+>>&0 string DIST
+>>>&0 use gentoo-manifest
+>>&0 string EBUILD
+>>>&0 use gentoo-manifest
+>>&0 string MANIFEST
+>>>&0 use gentoo-manifest
+
+# Use a more detailed regex to verify that we were correct.
+# <tag> <filename> <size> <hash-name> <hash-value>...
+# (<tag>'s already been matched prior to calling)
+0 name gentoo-manifest
+>&0 regex [[:space:]]+[[:print:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[[:alnum:]]+[[:space:]]+[[:xdigit:]]{32} Gentoo Manifest (GLEP 74)
+!:mime application/vnd.gentoo.manifest
+
+# Summary: Gentoo ebuild and eclass files
+# Reference: https://projects.gentoo.org/pms/8/pms.html
+# Submitted by: Michal Gorny <mgorny@gentoo.org>
+0 search/512 EAPI=
+>0 regex .*\n[\040\t]*EAPI=["']? Gentoo ebuild
+>>&0 regex [[:alnum:]+_.-]+ \b, EAPI %s
+!:mime application/vnd.gentoo.ebuild
+
+0 search/512 @ECLASS:\040 Gentoo eclass
+>&0 string x %s
+!:mime application/vnd.gentoo.eclass
+
+# Summary: Gentoo supplementary package and category metadata files
+# Reference: https://www.gentoo.org/glep/glep-0068.html
+# Submitted by: Michal Gorny <mgorny@gentoo.org>
+0 string \<?xml
+>0 search/512 \<catmetadata Gentoo category metadata file
+!:mime application/vnd.gentoo.catmetadata+xml
+>0 search/512 \<pkgmetadata Gentoo package metadata file
+!:mime application/vnd.gentoo.pkgmetadata+xml
+
+# Summary: Gentoo GLEP 78 binary package
+# Reference: https://www.gentoo.org/glep/glep-0078.html
+# Note: assumes the strict format
+# Submitted by: Michal Gorny <mgorny@gentoo.org>
+
+# GPKG uses ustar (or ustar-compatible GNU format) that starts with
+# a <directory>/gpkg-1 file
+257 string ustar
+>0 search/100 /gpkg-1\0
+>>0 regex [^/]+ Gentoo GLEP 78 (GPKG) binary package for "%s"
+!:mime application/vnd.gentoo.gpkg
+!:ext tar
+# the logic below requires the gpkg-1 file to be empty
+>>>124 string 00000000000\0
+# determine the compression used by looking at the second member name
+>>>>512 search/100 .tar.
+>>>>>&0 string gz\0 using gzip compression
+>>>>>&0 string bz2\0 using bzip2 compression
+>>>>>&0 string lz\0 using lzip compression
+>>>>>&0 string lz4\0 using lz4 compression
+>>>>>&0 string lzo\0 using lzo compression
+>>>>>&0 string xz\0 using xz compression
+>>>>>&0 string zst\0 using zstd compression
+>>>>(636.o+1024) search/611 .sig\0 \b, signed
diff --git a/contrib/libs/libmagic/magic/Magdir/geo b/contrib/libs/libmagic/magic/Magdir/geo
new file mode 100644
index 0000000000..1fde25e57b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/geo
@@ -0,0 +1,166 @@
+
+#------------------------------------------------------------------------------
+# $File: geo,v 1.10 2022/10/31 13:22:26 christos Exp $
+# Geo- files from Kurt Schwehr <schwehr@ccom.unh.edu>
+
+######################################################################
+#
+# Acoustic Doppler Current Profilers (ADCP)
+#
+######################################################################
+
+0 beshort 0x7f7f RDI Acoustic Doppler Current Profiler (ADCP)
+
+######################################################################
+#
+# Metadata
+#
+######################################################################
+
+0 string Identification_Information FGDC ASCII metadata
+
+######################################################################
+#
+# Seimsic / Subbottom
+#
+######################################################################
+
+# Knudsen subbottom chirp profiler - Binary File Format: B9
+# KEB D409-03167 V1.75 Huffman
+0 string KEB\ Knudsen seismic KEL binary (KEB) -
+>4 regex [-A-Z0-9]+ Software: %s
+>>&1 regex V[0-9]+\\.[0-9]+ version %s
+
+######################################################################
+#
+# LIDAR - Laser altimetry or bathy
+#
+######################################################################
+
+
+# Caris LIDAR format for LADS comes as two parts... ascii location file and binary waveform data
+0 string HCA LADS Caris Ascii Format (CAF) bathymetric lidar
+>4 regex [0-9]+\\.[0-9]+ version %s
+
+0 string HCB LADS Caris Binary Format (CBF) bathymetric lidar waveform data
+>3 byte x version %d .
+>4 byte x %d
+
+
+######################################################################
+#
+# MULTIBEAM SONARS https://www.ldeo.columbia.edu/res/pi/MB-System/formatdoc/
+#
+######################################################################
+
+# GeoAcoustics - GeoSwath Plus
+# Update: Joerg Jenderek
+# URL: https://www.mbari.org/products/research-software/mb-system/
+# Reference: http://ccom.unh.edu/sites/default/files/news-and-events/conferences/auv-bootcamp/
+# GS%2B-6063-BB-GS%2B-Broadcast-Raw-Data-File-Format-Command-Specification.pdf
+# Note: All data is written using Intel 80x86 byte ordering (LSB to MSB)
+# raw_header_siz; file header size is 544 bytes
+4 beshort 0x2002
+# GRR: line above is too general as it matches also some Microsoft Event Trace Logs *.ETL
+# skip many (63/753) Microsoft Event Trace Logs (AMSITrace.etl lxcore_kernel.etl NotificationUxBroker.052.etl WindowsBackup.4.etl) with invalid "low" ping header size 0
+>6 leshort >0 GeoSwath RDF
+# skip foo samples with invalid "high" spare bytes
+#>>536 ulequad =0 OK_THIS_IS_GeoSwath_RDF
+#!:mime application/octet-stream
+!:mime application/x-geoswath-rdf
+# http://ccom.unh.edu/sites/default/files/news-and-events/conferences/auv-bootcamp/060116342.rdf
+!:ext rdf
+# filename; original file name like: "C:\GS+\Projects\Default\Raw Data Files\060116342.rdf"
+>>8 string x "%-.512s"
+# version[8]; recording software version number like: 3.16c
+>>527 string x \b, version %-.8s
+# creation; unsigned int file creation time; WHAT time format is this?
+>>0 ulelong x \b, creation time %#8.8x
+# raw_ping_header_size; size of ping header in bytes like: 64
+>>6 leshort !64 \b, ping header size %d
+# frequency; system frequency in hertz like: 500000
+>>520 lelong x \b, frequency %d
+# echo_type; Echosounder type index like: 1
+>>524 leshort x \b, echo type %#x
+# file_mode; file mode mask (0x00 bathy & sidescan, 0x80 bathy, 0x40 sidescan, 0x20 seismic)
+>>526 ubyte !0 \b, file mode %#2.2x
+# pps_mode; PPS synch mode like: 2
+>>535 byte x \b, pps mode %#x
+# char spare[8]; apparently zeroed
+>>536 ubequad !0 \b, spare %#16.16llx
+# Ping_number; 1st ping number like: 4944
+>>544 lelong x \b, 1st ping number %d
+
+0 string Start:- GeoSwatch auf text file
+
+# Seabeam 2100
+# mbsystem code mb41
+0 string SB2100 SeaBeam 2100 multibeam sonar
+0 string SB2100DR SeaBeam 2100 DR multibeam sonar
+0 string SB2100PR SeaBeam 2100 PR multibeam sonar
+
+# This corresponds to MB-System format 94, L-3/ELAC/SeaBeam XSE vendor
+# format. It is the format of our upgraded SeaBeam 2112 on R/V KNORR.
+0 string $HSF XSE multibeam
+
+# mb121 https://www.saic.com/maritime/gsf/
+8 string GSF-v SAIC generic sensor format (GSF) sonar data,
+>&0 regex [0-9]+\\.[0-9]+ version %s
+
+# MGD77 - https://www.ngdc.noaa.gov/mgg/dat/geodas/docs/mgd77.htm
+# mb161
+9 string MGD77 MGD77 Header, Marine Geophysical Data Exchange Format
+
+# MBSystem processing caches the mbinfo output
+1 string Swath\ Data\ File: mbsystem info cache
+
+# Caris John Hughes Clark format
+0 string HDCS Caris multibeam sonar related data
+1 string Start/Stop\ parameter\ header: Caris ASCII project summary
+
+######################################################################
+#
+# Visualization and 3D modeling
+#
+######################################################################
+
+# IVS - IVS3d.com Tagged Data Representation
+0 string %%\ TDR\ 2.0 IVS Fledermaus TDR file
+
+# http://www.ecma-international.org/publications/standards/Ecma-363.htm
+# 3D in PDFs
+0 string U3D ECMA-363, Universal 3D
+
+######################################################################
+#
+# Support files
+#
+######################################################################
+
+# https://midas.psi.ch/elog/
+0 string $@MID@$ elog journal entry
+
+# Geospatial Designs https://www.geospatialdesigns.com/surfer6_format.htm
+0 string DSBB Surfer 6 binary grid file
+>4 leshort x \b, %d
+>6 leshort x \bx%d
+>8 ledouble x \b, minx=%g
+>16 ledouble x \b, maxx=%g
+>24 ledouble x \b, miny=%g
+>32 ledouble x \b, maxy=%g
+>40 ledouble x \b, minz=%g
+>48 ledouble x \b, maxz=%g
+
+# magic for LAS format files
+# alex myczko <alex@aiei.ch>
+# https://www.asprs.org/wp-content/uploads/2010/12/LAS_1_3_r11.pdf
+0 string LASF LIDAR point data records
+>24 byte >0 \b, version %u
+>25 byte >0 \b.%u
+>26 string >\0 \b, SYSID %s
+>58 string >\0 \b, Generating Software %s
+
+# magic for PCD format files
+# alex myczko <alex@aiei.ch>
+# http://pointclouds.org/documentation/tutorials/pcd_file_format.php
+0 string #\ .PCD Point Cloud Data
diff --git a/contrib/libs/libmagic/magic/Magdir/geos b/contrib/libs/libmagic/magic/Magdir/geos
new file mode 100644
index 0000000000..66c2bd1a29
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/geos
@@ -0,0 +1,20 @@
+
+#------------------------------------------------------------------------------
+# $File: geos,v 1.4 2009/09/19 16:28:09 christos Exp $
+# GEOS files (Vidar Madsen, vidar@gimp.org)
+# semi-commonly used in embedded and handheld systems.
+0 belong 0xc745c153 GEOS
+>40 byte 1 executable
+>40 byte 2 VMFile
+>40 byte 3 binary
+>40 byte 4 directory label
+>40 byte <1 unknown
+>40 byte >4 unknown
+>4 string >\0 \b, name "%s"
+#>44 short x \b, version %d
+#>46 short x \b.%d
+#>48 short x \b, rev %d
+#>50 short x \b.%d
+#>52 short x \b, proto %d
+#>54 short x \br%d
+#>168 string >\0 \b, copyright "%s"
diff --git a/contrib/libs/libmagic/magic/Magdir/gimp b/contrib/libs/libmagic/magic/Magdir/gimp
new file mode 100644
index 0000000000..e763cbec83
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gimp
@@ -0,0 +1,77 @@
+
+#------------------------------------------------------------------------------
+# $File: gimp,v 1.10 2019/10/15 18:19:40 christos Exp $
+# GIMP Gradient: file(1) magic for the GIMP's gradient data files (.ggr)
+# by Federico Mena <federico@nuclecu.unam.mx>
+
+0 string/t GIMP\ Gradient GIMP gradient data
+#!:mime text/plain
+!:mime text/x-gimp-ggr
+!:ext ggr
+
+# GIMP palette (.gpl)
+# From: Markus Heidelberg <markus.heidelberg@web.de>
+0 string/t GIMP\ Palette GIMP palette data
+# URL: https://docs.gimp.org/en/gimp-concepts-palettes.html
+# Reference: http://fileformats.archiveteam.org/wiki/GIMP_Palette
+#!:mime text/plain
+!:mime text/x-gimp-gpl
+!:ext gpl
+
+#------------------------------------------------------------------------------
+# XCF: file(1) magic for the XCF image format used in the GIMP (.xcf) developed
+# by Spencer Kimball and Peter Mattis
+# ('Bucky' LaDieu, nega@vt.edu)
+
+# URL: https://en.wikipedia.org/wiki/XCF_(file_format)
+# Reference: https://gitlab.gnome.org/GNOME/gimp/blob/master/devel-docs/xcf.txt
+0 string gimp\ xcf GIMP XCF image data,
+!:mime image/x-xcf
+!:ext xcf
+>9 string file version 0,
+>9 string v version
+>>10 string >\0 %s,
+>14 belong x %u x
+>18 belong x %u,
+>22 belong 0 RGB Color
+>22 belong 1 Greyscale
+>22 belong 2 Indexed Color
+>22 belong >2 Unknown Image Type.
+
+#------------------------------------------------------------------------------
+# XCF: file(1) magic for the patterns used in the GIMP (.pat), developed
+# by Spencer Kimball and Peter Mattis
+# ('Bucky' LaDieu, nega@vt.edu)
+
+# Reference: http://fileformats.archiveteam.org/wiki/GIMP_Pattern
+20 string GPAT GIMP pattern data,
+>24 string x %s
+!:mime image/x-gimp-pat
+!:ext pat
+
+#------------------------------------------------------------------------------
+# XCF: file(1) magic for the brushes used in the GIMP (.gbr), developed
+# by Spencer Kimball and Peter Mattis
+# ('Bucky' LaDieu, nega@vt.edu)
+
+20 string GIMP GIMP brush data
+# Reference: http://fileformats.archiveteam.org/wiki/GIMP_Brush
+!:mime image/x-gimp-gbr
+# some sources also list gpb
+!:ext gbr
+
+# From: Joerg Jenderek
+# URL: https://docs.gimp.org/en/gimp-using-animated-brushes.html
+# Reference: http://fileformats.archiveteam.org/wiki/GIMP_Animated_Brush
+# share\gimp\2.0\brushes\Legacy\confetti.gih
+0 search/21/b \040ncells: GIMP animated brush data
+!:mime image/x-gimp-gih
+!:ext gih
+
+# GIMP Curves File
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+0 string #\040GIMP\040Curves\040File GIMP curve file
+#!:mime text/plain
+!:mime text/x-gimp-curve
+!:ext /txt
+
diff --git a/contrib/libs/libmagic/magic/Magdir/git b/contrib/libs/libmagic/magic/Magdir/git
new file mode 100644
index 0000000000..67eab32a66
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/git
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: git,v 1.2 2020/08/09 16:57:15 christos Exp $
+# git: file(1) magic for Git objects
+
+0 string blob\040
+>5 regex [0-9a-f]+ Git blob %s
+
+0 string tree\040
+>5 regex [0-9a-f]+ Git tree %s
+
+0 string commit\040
+>7 regex [0-9a-f]+ Git commit %s
diff --git a/contrib/libs/libmagic/magic/Magdir/glibc b/contrib/libs/libmagic/magic/Magdir/glibc
new file mode 100644
index 0000000000..3b856f3836
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/glibc
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# $File: glibc,v 1.1 2018/10/11 15:35:43 christos Exp $
+# glibc locale files
+#
+# https://sourceware.org/git/?p=glibc.git;f=locale/localeinfo.h;h=68822a63#l32
+
+0 belong 0x20070920 glibc locale file LC_CTYPE
+0 belong 0x14110320 glibc locale file LC_NUMERIC
+0 belong 0x17110320 glibc locale file LC_TIME
+0 belong 0x17100520 glibc locale file LC_COLLATE
+0 belong 0x11110320 glibc locale file LC_MONETARY
+0 belong 0x10110320 glibc locale file LC_MESSAGES
+0 belong 0x13110320 glibc locale file LC_ALL
+0 belong 0x12110320 glibc locale file LC_PAPER
+0 belong 0x1d110320 glibc locale file LC_NAME
+0 belong 0x1c110320 glibc locale file LC_ADDRESS
+0 belong 0x1f110320 glibc locale file LC_TELEPHONE
+0 belong 0x1e110320 glibc locale file LC_MEASUREMENT
+0 belong 0x19110320 glibc locale file LC_IDENTIFICATION
+
diff --git a/contrib/libs/libmagic/magic/Magdir/gnome b/contrib/libs/libmagic/magic/Magdir/gnome
new file mode 100644
index 0000000000..7a45d1d586
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gnome
@@ -0,0 +1,59 @@
+
+#------------------------------------------------------------------------------
+# $File: gnome,v 1.7 2020/06/23 16:17:08 christos Exp $
+# GNOME related files
+
+# Contributed by Josh Triplett
+# FIXME: Could be simplified if pstring supported two-byte counts
+0 string GnomeKeyring\n\r\0\n GNOME keyring
+>&0 ubyte 0 \b, major version 0
+>>&0 ubyte 0 \b, minor version 0
+>>>&0 ubyte 0 \b, crypto type 0 (AES)
+>>>&0 ubyte >0 \b, crypto type %u (unknown)
+>>>&1 ubyte 0 \b, hash type 0 (MD5)
+>>>&1 ubyte >0 \b, hash type %u (unknown)
+>>>&2 ubelong 0xFFFFFFFF \b, name NULL
+>>>&2 ubelong !0xFFFFFFFF
+>>>>&-4 ubelong >255 \b, name too long for file's pstring type
+>>>>&-4 ubelong <256
+>>>>>&-1 pstring x \b, name "%s"
+>>>>>>&0 ubeqdate x \b, last modified %s
+>>>>>>&8 ubeqdate x \b, created %s
+>>>>>>&16 ubelong &1
+>>>>>>>&0 ubelong x \b, locked if idle for %u seconds
+>>>>>>&16 ubelong ^1 \b, not locked if idle
+>>>>>>&24 ubelong x \b, hash iterations %u
+>>>>>>&28 ubequad x \b, salt %llu
+>>>>>>&52 ubelong x \b, %u item(s)
+
+# From: Alex Beregszaszi <alex@fsn.hu>
+4 string gtktalog GNOME Catalogue (gtktalog)
+>13 string >\0 version %s
+
+# Summary: GStreamer binary registry
+# Extension: .bin
+# Submitted by: Josh Triplett <josh@joshtriplett.org>
+0 belong 0xc0def00d GStreamer binary registry
+>4 string x \b, version %s
+
+# GVariant Database file
+# By Elan Ruusamae <glen@delfi.ee>
+# https://github.com/GNOME/gvdb/blob/master/gvdb-format.h
+# It's always "GVariant", it's byte swapped on incompatible archs
+# See https://github.com/GNOME/gvdb/blob/master/gvdb-builder.c
+# file_builder_serialise()
+# https://developer.gnome.org/glib/2.34/glib-GVariant.html#GVariant
+0 string GVariant GVariant Database file,
+# version is never filled. probably future extension
+>8 lelong x version %d
+# not sure are these usable, so commented out
+#>>16 lelong x start %d,
+#>>>20 lelong x end %d
+
+# G-IR database made by gobject-introspect toolset,
+# https://live.gnome.org/GObjectIntrospection
+0 string GOBJ\nMETADATA\r\n\032 G-IR binary database
+>16 byte x \b, v%d
+>17 byte x \b.%d
+>20 short x \b, %d entries
+>22 short x \b/%d local
diff --git a/contrib/libs/libmagic/magic/Magdir/gnu b/contrib/libs/libmagic/magic/Magdir/gnu
new file mode 100644
index 0000000000..761d657c4e
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gnu
@@ -0,0 +1,173 @@
+
+#------------------------------------------------------------------------------
+# $File: gnu,v 1.24 2021/04/26 15:56:00 christos Exp $
+# gnu: file(1) magic for various GNU tools
+#
+# GNU nlsutils message catalog file format
+#
+# GNU message catalog (.mo and .gmo files)
+
+# Update: Joerg Jenderek
+# URL: https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
+# Reference: ftp://ftp.gnu.org/pub/gnu/gettext/gettext-0.19.8.tar.gz/
+# gettext-0.19.8.1/gettext-runtime/intl/gmo.h
+# Note: maybe call it like "GNU translation gettext machine object"
+0 string \336\22\4\225 GNU message catalog (little endian),
+#0 ulelong 0x950412DE GNU-format message catalog data
+# TODO: write lines in such a way that code can also be called for big endian variant
+#>0 use gettext-object
+#0 name gettext-object
+>4 ulelong x revision
+!:mime application/x-gettext-translation
+# mo extension is also used for Easeus Partition Master PE32 executable module
+# like ConvertFatToNTFS.mo
+!:ext gmo/mo
+# only found three revision combinations 0.0 0.1 1.1 as unsigned 32-bit
+# major revision
+>4 ulelong/0xFFff x %u.
+# minor revision
+>4 ulelong&0x0000FFff x \b%u
+>>8 ulelong x \b, %u message
+# plural s
+>>8 ulelong >1 \bs
+# size of hashing table
+#>20 ulelong x \b, %u hash
+#>20 ulelong >1 \bes
+#>24 ulelong x at %#x
+# for revision x.0 offset of table with originals is 1Ch if directly after header
+>4 ulelong&0x0000FFff =0
+>>12 ulelong !0x1C \b, at %#x string table
+# but for x.1 table offset i found is 30h. That means directly after bigger header
+>4 ulelong&0x0000FFff >0
+>>12 ulelong !0x30 \b, at %#x string table
+# The following variables are only used in .mo files with minor revision >= 1
+# number of system dependent segments
+#>>28 ulelong x \b, %u segment
+#>>28 ulelong >1 \bs
+# offset of table describing system dependent segments
+#>>32 ulelong x at %#x
+# number of system dependent strings pairs
+>>36 ulelong x \b, %u sysdep message
+>>36 ulelong >1 \bs
+# offset of table with start offsets of original sysdep strings
+#>>40 ulelong x \b, at %#x sysdep strings
+# offset of table with start offsets of translated sysdep strings
+#>>44 ulelong x \b, at %#x sysdep translations
+# >>(44.l) ulelong x %#x chars
+# >>>&0 ulelong x at %#x
+# >>>>(&-4) string x "%s"
+# string table after big header
+#>>48 ubequad x \b, string table %#llx
+#
+# 0th string length seems to be always 0
+#>(12.l) ulelong x \b, %u chars
+#>>&0 ulelong x at %#x
+# if 1st string length positive inspect offset and string
+#>(12.l+8) ulelong >0 \b, %u chars
+#>>&0 ulelong x at %#x
+# if 2nd string length positive inspect offset and string
+# >(12.l+16) ulelong >0 \b, %u chars
+# >>&0 ulelong x at %#x
+# skip newline byte
+#>>>(&-4) ubyte =0x0A
+#>>>>&0 string x "%s"
+#>>>(&-4) ubyte !0x0A
+#>>>>&-1 string x '%s'
+# offset of table with translation strings
+#>16 ulelong x \b, at %#x translation table
+# check translation 0 length and offset
+>(16.l) ulelong >0
+>>&0 ulelong x
+# translation 0 seems to be often Project-Id with name and version
+>>>(&-4) string x \b, %s
+# trans. 1 with bytes >= 1 unlike icoutils-0.31.0\po\en@boldquot.gmo with 1 NL
+>(16.l+8) ulelong >1
+>>&0 ulelong x
+>>>(&-4) ubyte !0x0A
+>>>>&-1 string x '%s'
+# 1 New Line like in tar-1.29\po\de.gmo
+>>>(&-4) ubyte =0x0A
+>>>>&0 ubyte !0x0A
+>>>>>&-1 string x '%s'
+# 2nd New Line like in parted-3.1\po\de.gmo
+>>>>&0 ubyte =0x0A
+>>>>>&0 string x '%s'
+
+0 string \225\4\22\336 GNU message catalog (big endian),
+#0 ubelong 0x950412DE GNU-format message catalog data
+!:mime application/x-gettext-translation
+!:ext gmo/mo
+# TODO: for big endian use same code as for little endian
+#>0 use \^gettext-object
+# DEBUG code
+#>16 ubelong x \b, at %#x translation table
+#>(16.L) ubelong x %#x chars
+#>>&0 ubelong x at %#x
+# unexpected value HERE!
+#>>>(&-4) ubequad x %#llx
+#
+>4 beshort x revision %d.
+>6 beshort >0 \b%d,
+>>8 belong x %d messages,
+>>36 belong x %d sysdep messages
+>6 beshort =0 \b%d,
+>>8 belong x %d messages
+
+
+# GnuPG
+# The format is very similar to pgp
+0 string \001gpg GPG key trust database
+>4 byte x version %d
+# Note: magic.mime had 0x8501 for the next line instead of 0x8502
+0 beshort 0x8502 GPG encrypted data
+!:mime text/PGP # encoding: data
+
+# Update: Joerg Jenderek
+# Note: PGP and GPG use same data structure.
+# So recognition is now done by ./pgp with start test for byte 0x99
+# This magic is not particularly good, as the keyrings don't have true
+# magic. Nevertheless, it covers many keyrings.
+# 0 ubeshort-0x9901 <2
+# >3 byte 4
+# >>4 bedate x GPG key public ring, created %s
+# !:mime application/x-gnupg-keyring
+
+# Symmetric encryption
+0 leshort 0x0d8c
+>4 leshort 0x0203
+>>2 leshort 0x0204 GPG symmetrically encrypted data (3DES cipher)
+>>2 leshort 0x0304 GPG symmetrically encrypted data (CAST5 cipher)
+>>2 leshort 0x0404 GPG symmetrically encrypted data (BLOWFISH cipher)
+>>2 leshort 0x0704 GPG symmetrically encrypted data (AES cipher)
+>>2 leshort 0x0804 GPG symmetrically encrypted data (AES192 cipher)
+>>2 leshort 0x0904 GPG symmetrically encrypted data (AES256 cipher)
+>>2 leshort 0x0a04 GPG symmetrically encrypted data (TWOFISH cipher)
+>>2 leshort 0x0b04 GPG symmetrically encrypted data (CAMELLIA128 cipher)
+>>2 leshort 0x0c04 GPG symmetrically encrypted data (CAMELLIA192 cipher)
+>>2 leshort 0x0d04 GPG symmetrically encrypted data (CAMELLIA256 cipher)
+
+
+# GnuPG Keybox file
+# <https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=kbx/keybox-blob.c;hb=HEAD>
+# From: Philipp Hahn <hahn@univention.de>
+0 belong 32
+>4 byte 1
+>>8 string KBXf GPG keybox database
+>>>5 byte 1 version %d
+>>>16 bedate x \b, created-at %s
+>>>20 bedate x \b, last-maintained %s
+
+
+# From: James Youngman <jay@gnu.org>
+# gnu find magic
+0 string \0LOCATE GNU findutils locate database data
+>7 string >\0 \b, format %s
+>7 string 02 \b (frcode)
+
+# Files produced by GNU gettext
+
+# gettext message catalogue
+0 search/1024 \nmsgid
+>&0 search/1024 \nmsgstr GNU gettext message catalogue text
+!:strength +100
+!:mime text/x-po
diff --git a/contrib/libs/libmagic/magic/Magdir/gnumeric b/contrib/libs/libmagic/magic/Magdir/gnumeric
new file mode 100644
index 0000000000..928ad3eed1
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gnumeric
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: gnumeric,v 1.4 2009/09/19 16:28:09 christos Exp $
+# gnumeric: file(1) magic for Gnumeric spreadsheet
+# This entry is only semi-helpful, as Gnumeric compresses its files, so
+# they will ordinarily reported as "compressed", but at least -z helps
+39 string =<gmr:Workbook Gnumeric spreadsheet
+!:mime application/x-gnumeric
diff --git a/contrib/libs/libmagic/magic/Magdir/gpt b/contrib/libs/libmagic/magic/Magdir/gpt
new file mode 100644
index 0000000000..c2fd51c0dc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gpt
@@ -0,0 +1,240 @@
+
+#------------------------------------------------------------------------------
+# $File: gpt,v 1.5 2020/12/12 20:01:47 christos Exp $
+#
+# GPT Partition table patterns.
+# Author: Rogier Goossens (goossens.rogier@gmail.com)
+# Note that a GPT-formatted disk must contain an MBR as well.
+#
+
+# The initial segment (up to >>>>>>>>422) was copied from the X86
+# partition table code (aka MBR).
+# This is kept separate, so that MBR partitions are not reported as well.
+# (use -k if you do want them as well)
+
+# First, detect the MBR partition table
+# If more than one GPT protective MBR partition exists, don't print anything
+# (the other MBR detection code will then just print the MBR partition table)
+0x1FE leshort 0xAA55
+>3 string !MS
+>>3 string !SYSLINUX
+>>>3 string !MTOOL
+>>>>3 string !NEWLDR
+>>>>>5 string !DOS
+# not FAT (32 bit)
+>>>>>>82 string !FAT32
+#not Linux kernel
+>>>>>>>514 string !HdrS
+#not BeOS
+>>>>>>>>422 string !Be\ Boot\ Loader
+# GPT with protective MBR entry in partition 1 (only)
+>>>>>>>>>450 ubyte 0xee
+>>>>>>>>>>466 ubyte !0xee
+>>>>>>>>>>>482 ubyte !0xee
+>>>>>>>>>>>>498 ubyte !0xee
+#>>>>>>>>>>>>>446 use gpt-mbr-partition
+>>>>>>>>>>>>>(454.l*8192) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>0 ubyte x of 8192 bytes
+>>>>>>>>>>>>>(454.l*8192) string !EFI\ PART
+>>>>>>>>>>>>>>(454.l*4096) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>0 ubyte x of 4096 bytes
+>>>>>>>>>>>>>>(454.l*4096) string !EFI\ PART
+>>>>>>>>>>>>>>>(454.l*2048) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>0 ubyte x of 2048 bytes
+>>>>>>>>>>>>>>>(454.l*2048) string !EFI\ PART
+>>>>>>>>>>>>>>>>(454.l*1024) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>0 ubyte x of 1024 bytes
+>>>>>>>>>>>>>>>>(454.l*1024) string !EFI\ PART
+>>>>>>>>>>>>>>>>>(454.l*512) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>>0 ubyte x of 512 bytes
+# GPT with protective MBR entry in partition 2 (only)
+>>>>>>>>>450 ubyte !0xee
+>>>>>>>>>>466 ubyte 0xee
+>>>>>>>>>>>482 ubyte !0xee
+>>>>>>>>>>>>498 ubyte !0xee
+#>>>>>>>>>>>>>462 use gpt-mbr-partition
+>>>>>>>>>>>>>(470.l*8192) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>0 ubyte x of 8192 bytes
+>>>>>>>>>>>>>(470.l*8192) string !EFI\ PART
+>>>>>>>>>>>>>>(470.l*4096) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>0 ubyte x of 4096 bytes
+>>>>>>>>>>>>>>(470.l*4096) string !EFI\ PART
+>>>>>>>>>>>>>>>(470.l*2048) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>0 ubyte x of 2048 bytes
+>>>>>>>>>>>>>>>(470.l*2048) string !EFI\ PART
+>>>>>>>>>>>>>>>>(470.l*1024) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>0 ubyte x of 1024 bytes
+>>>>>>>>>>>>>>>>(470.l*1024) string !EFI\ PART
+>>>>>>>>>>>>>>>>>(470.l*512) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>>0 ubyte x of 512 bytes
+# GPT with protective MBR entry in partition 3 (only)
+>>>>>>>>>450 ubyte !0xee
+>>>>>>>>>>466 ubyte !0xee
+>>>>>>>>>>>482 ubyte 0xee
+>>>>>>>>>>>>498 ubyte !0xee
+#>>>>>>>>>>>>>478 use gpt-mbr-partition
+>>>>>>>>>>>>>(486.l*8192) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>0 ubyte x of 8192 bytes
+>>>>>>>>>>>>>(486.l*8192) string !EFI\ PART
+>>>>>>>>>>>>>>(486.l*4096) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>0 ubyte x of 4096 bytes
+>>>>>>>>>>>>>>(486.l*4096) string !EFI\ PART
+>>>>>>>>>>>>>>>(486.l*2048) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>0 ubyte x of 2048 bytes
+>>>>>>>>>>>>>>>(486.l*2048) string !EFI\ PART
+>>>>>>>>>>>>>>>>(486.l*1024) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>0 ubyte x of 1024 bytes
+>>>>>>>>>>>>>>>>(486.l*1024) string !EFI\ PART
+>>>>>>>>>>>>>>>>>(486.l*512) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>>0 ubyte x of 512 bytes
+# GPT with protective MBR entry in partition 4 (only)
+>>>>>>>>>450 ubyte !0xee
+>>>>>>>>>>466 ubyte !0xee
+>>>>>>>>>>>482 ubyte !0xee
+>>>>>>>>>>>>498 ubyte 0xee
+#>>>>>>>>>>>>>494 use gpt-mbr-partition
+>>>>>>>>>>>>>(502.l*8192) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>0 ubyte x of 8192 bytes
+>>>>>>>>>>>>>(502.l*8192) string !EFI\ PART
+>>>>>>>>>>>>>>(502.l*4096) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>0 ubyte x of 4096 bytes
+>>>>>>>>>>>>>>(502.l*4096) string !EFI\ PART
+>>>>>>>>>>>>>>>(502.l*2048) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>0 ubyte x of 2048 bytes
+>>>>>>>>>>>>>>>(502.l*2048) string !EFI\ PART
+>>>>>>>>>>>>>>>>(502.l*1024) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>0 ubyte x of 1024 bytes
+>>>>>>>>>>>>>>>>(502.l*1024) string !EFI\ PART
+>>>>>>>>>>>>>>>>>(502.l*512) string EFI\ PART GPT partition table
+>>>>>>>>>>>>>>>>>>0 use gpt-mbr-type
+>>>>>>>>>>>>>>>>>>&-8 use gpt-table
+>>>>>>>>>>>>>>>>>>0 ubyte x of 512 bytes
+
+# The following code does GPT detection and processing, including
+# sector size detection.
+# It has to be duplicated above because the top-level pattern
+# (i.e. not called using 'use') must print *something* for file
+# to count it as a match. Text only printed in named patterns is
+# not counted, and causes file to continue, and try and match
+# other patterns.
+#
+# Unfortunately, when assuming sector sizes >=16k, if the sector size
+# happens to be 512 instead, we may find confusing data after the GPT
+# table... If the GPT table has less than 128 entries, this may even
+# happen for assumed sector sizes as small as 4k
+# This could be solved by checking for the presence of the backup GPT
+# header as well, but that makes the logic extremely complex
+##0 name gpt-mbr-partition
+##>(8.l*8192) string EFI\ PART
+##>>(8.l*8192) use gpt-mbr-type
+##>>&-8 use gpt-table
+##>>0 ubyte x of 8192 bytes
+##>(8.l*8192) string !EFI\ PART
+##>>(8.l*4096) string EFI\ PART GPT partition table
+##>>>0 use gpt-mbr-type
+##>>>&-8 use gpt-table
+##>>>0 ubyte x of 4096 bytes
+##>>(8.l*4096) string !EFI\ PART
+##>>>(8.l*2048) string EFI\ PART GPT partition table
+##>>>>0 use gpt-mbr-type
+##>>>>&-8 use gpt-table
+##>>>>0 ubyte x of 2048 bytes
+##>>>(8.l*2048) string !EFI\ PART
+##>>>>(8.l*1024) string EFI\ PART GPT partition table
+##>>>>>0 use gpt-mbr-type
+##>>>>>&-8 use gpt-table
+##>>>>>0 ubyte x of 1024 bytes
+##>>>>(8.l*1024) string !EFI\ PART
+##>>>>>(8.l*512) string EFI\ PART GPT partition table
+##>>>>>>0 use gpt-mbr-type
+##>>>>>>&-8 use gpt-table
+##>>>>>>0 ubyte x of 512 bytes
+
+# Print details of MBR type for a GPT-disk
+# Calling code ensures that there is only one 0xee partition.
+0 name gpt-mbr-type
+# GPT with protective MBR entry in partition 1
+>450 ubyte 0xee
+>>454 ulelong 1
+>>>462 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>454 ulelong !1 \b (nonstandard: not at LBA 1)
+# GPT with protective MBR entry in partition 2
+>466 ubyte 0xee
+>>470 ulelong 1
+>>>478 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>>>>446 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>>478 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>470 ulelong !1 \b (nonstandard: not at LBA 1)
+# GPT with protective MBR entry in partition 3
+>482 ubyte 0xee
+>>486 ulelong 1
+>>>494 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>>>>446 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>>494 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>486 ulelong !1 \b (nonstandard: not at LBA 1)
+# GPT with protective MBR entry in partition 4
+>498 ubyte 0xee
+>>502 ulelong 1
+>>>446 string !\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \b (with hybrid MBR)
+>>502 ulelong !1 \b (nonstandard: not at LBA 1)
+
+# Print the information from a GPT partition table structure
+0 name gpt-table
+>10 uleshort x \b, version %u
+>8 uleshort x \b.%u
+>56 ulelong x \b, GUID: %08x
+>60 uleshort x \b-%04x
+>62 uleshort x \b-%04x
+>64 ubeshort x \b-%04x
+>66 ubeshort x \b-%04x
+>68 ubelong x \b%08x
+#>80 uleshort x \b, %d partition entries
+>32 ulequad+1 x \b, disk size: %lld sectors
+
+# In case a GPT data-structure is at LBA 0, report it as well
+# This covers systems which are not GPT-aware, and which show
+# and allow access to the protective partition. This code will
+# detect the contents of such a partition.
+0 string EFI\ PART GPT data structure (nonstandard: at LBA 0)
+>0 use gpt-table
+>0 ubyte x (sector size unknown)
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/gpu b/contrib/libs/libmagic/magic/Magdir/gpu
new file mode 100644
index 0000000000..36d712443b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gpu
@@ -0,0 +1,28 @@
+
+#------------------------------------------------------------------------------
+# $File: gpu,v 1.3 2021/04/26 15:56:00 christos Exp $
+# gpu: file(1) magic for GPU input files
+
+# Standard Portable Intermediate Representation (SPIR)
+# Documentation: https://www.khronos.org/spir
+# Typical file extension: .spv
+
+0 belong 0x07230203 Khronos SPIR-V binary, big-endian
+>4 belong x \b, version %#08x
+>8 belong x \b, generator %#08x
+
+0 lelong 0x07230203 Khronos SPIR-V binary, little-endian
+>4 lelong x \b, version %#08x
+>8 lelong x \b, generator %#08x
+
+# Vulkan Trace file
+# Documentation:
+# https://github.com/LunarG/VulkanTools/blob/master/vktrace/vktrace_common/\
+# vktrace_trace_packet_identifiers.h
+# Typical file extension: .vktrace
+
+8 lequad 0xABADD068ADEAFD0C Vulkan trace file, little-endian
+>0 leshort x \b, version %d
+
+8 bequad 0xABADD068ADEAFD0C Vulkan trace file, big-endian
+>0 beshort x \b, version %d
diff --git a/contrib/libs/libmagic/magic/Magdir/grace b/contrib/libs/libmagic/magic/Magdir/grace
new file mode 100644
index 0000000000..25bd759edc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/grace
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# $File: grace,v 1.4 2009/09/19 16:28:09 christos Exp $
+# ACE/gr and Grace type files - PLEASE DO NOT REMOVE THIS LINE
+#
+# ACE/gr binary
+0 string \000\000\0001\000\000\0000\000\000\0000\000\000\0002\000\000\0000\000\000\0000\000\000\0003 old ACE/gr binary file
+>39 byte >0 - version %c
+# ACE/gr ascii
+0 string #\ xvgr\ parameter\ file ACE/gr ascii file
+0 string #\ xmgr\ parameter\ file ACE/gr ascii file
+0 string #\ ACE/gr\ parameter\ file ACE/gr ascii file
+# Grace projects
+0 string #\ Grace\ project\ file Grace project file
+>23 string @version\ (version
+>>32 byte >0 %c
+>>33 string >\0 \b.%.2s
+>>35 string >\0 \b.%.2s)
+# ACE/gr fit description files
+0 string #\ ACE/gr\ fit\ description\ ACE/gr fit description file
+# end of ACE/gr and Grace type files - PLEASE DO NOT REMOVE THIS LINE
diff --git a/contrib/libs/libmagic/magic/Magdir/graphviz b/contrib/libs/libmagic/magic/Magdir/graphviz
new file mode 100644
index 0000000000..d8bf22db43
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/graphviz
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: graphviz,v 1.9 2019/04/30 04:01:40 christos Exp $
+# graphviz: file(1) magic for https://www.graphviz.org/
+
+# FIXME: These patterns match too generally. For example, the first
+# line matches a LaTeX file containing the word "graph" (with a {
+# following later) and the second line matches this file.
+#0 regex/100l [\r\n\t\ ]*graph[\r\n\t\ ]+.*\\{ graphviz graph text
+#!:mime text/vnd.graphviz
+#0 regex/100l [\r\n\t\ ]*digraph[\r\n\t\ ]+.*\\{ graphviz digraph text
+#!:mime text/vnd.graphviz
diff --git a/contrib/libs/libmagic/magic/Magdir/gringotts b/contrib/libs/libmagic/magic/Magdir/gringotts
new file mode 100644
index 0000000000..b67475406a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/gringotts
@@ -0,0 +1,48 @@
+
+#------------------------------------------------------------------------------
+# $File: gringotts,v 1.6 2017/03/17 21:35:28 christos Exp $
+# gringotts: file(1) magic for Gringotts
+# http://devel.pluto.linux.it/projects/Gringotts/
+# author: Germano Rizzo <mano@pluto.linux.it>
+#GRG3????Y
+0 string GRG Gringotts data file
+#file format 1
+>3 string 1 v.1, MCRYPT S2K, SERPENT crypt, SHA-256 hash, ZLib lvl.9
+#file format 2
+>3 string 2 v.2, MCRYPT S2K,
+>>8 byte&0x70 0x00 RIJNDAEL-128 crypt,
+>>8 byte&0x70 0x10 SERPENT crypt,
+>>8 byte&0x70 0x20 TWOFISH crypt,
+>>8 byte&0x70 0x30 CAST-256 crypt,
+>>8 byte&0x70 0x40 SAFER+ crypt,
+>>8 byte&0x70 0x50 LOKI97 crypt,
+>>8 byte&0x70 0x60 3DES crypt,
+>>8 byte&0x70 0x70 RIJNDAEL-256 crypt,
+>>8 byte&0x08 0x00 SHA1 hash,
+>>8 byte&0x08 0x08 RIPEMD-160 hash,
+>>8 byte&0x04 0x00 ZLib
+>>8 byte&0x04 0x04 BZip2
+>>8 byte&0x03 0x00 lvl.0
+>>8 byte&0x03 0x01 lvl.3
+>>8 byte&0x03 0x02 lvl.6
+>>8 byte&0x03 0x03 lvl.9
+#file format 3
+>3 string 3 v.3, OpenPGP S2K,
+>>8 byte&0x70 0x00 RIJNDAEL-128 crypt,
+>>8 byte&0x70 0x10 SERPENT crypt,
+>>8 byte&0x70 0x20 TWOFISH crypt,
+>>8 byte&0x70 0x30 CAST-256 crypt,
+>>8 byte&0x70 0x40 SAFER+ crypt,
+>>8 byte&0x70 0x50 LOKI97 crypt,
+>>8 byte&0x70 0x60 3DES crypt,
+>>8 byte&0x70 0x70 RIJNDAEL-256 crypt,
+>>8 byte&0x08 0x00 SHA1 hash,
+>>8 byte&0x08 0x08 RIPEMD-160 hash,
+>>8 byte&0x04 0x00 ZLib
+>>8 byte&0x04 0x04 BZip2
+>>8 byte&0x03 0x00 lvl.0
+>>8 byte&0x03 0x01 lvl.3
+>>8 byte&0x03 0x02 lvl.6
+>>8 byte&0x03 0x03 lvl.9
+#file format >3
+>3 string >3 v.%.1s (unknown details)
diff --git a/contrib/libs/libmagic/magic/Magdir/hardware b/contrib/libs/libmagic/magic/Magdir/hardware
new file mode 100644
index 0000000000..e92986c5a8
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/hardware
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: hardware,v 1.1 2018/08/02 06:32:52 christos Exp $
+# hardware magic
+
+# EDID
+# https://en.wikipedia.org/wiki/Extended_Display_Identification_Data
+0 string \x00\xFF\xFF\xFF\xFF\xFF\xFF\x00
+>19 byte x
+>>18 byte x EDID data, version %u.
+>>19 byte x \b%u
+#>>17 ubyte+1990 <255 \b, manufactured %u
diff --git a/contrib/libs/libmagic/magic/Magdir/hitachi-sh b/contrib/libs/libmagic/magic/Magdir/hitachi-sh
new file mode 100644
index 0000000000..f64489f7fc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/hitachi-sh
@@ -0,0 +1,30 @@
+
+#------------------------------------------------------------------------------
+# $File: hitachi-sh,v 1.10 2020/12/12 20:01:47 christos Exp $
+# hitach-sh: file(1) magic for Hitachi Super-H
+#
+# Super-H COFF
+#
+# updated by Joerg Jenderek at Oct 2015
+# https://en.wikipedia.org/wiki/COFF
+# https://de.wikipedia.org/wiki/Common_Object_File_Format
+# http://www.delorie.com/djgpp/doc/coff/filhdr.html
+# below test line conflicts with 2nd NTFS filesystem sector
+# 2nd NTFS filesystem sector often starts with 0x05004e00 for unicode string 5 NTLDR
+# and Portable Gaming Notation Compressed format (*.WID http://pgn.freeservers.com/)
+0 beshort 0x0500
+# test for unused flag bits (0x8000,0x0800,0x0400,0x0200,x0080) in f_flags
+>18 ubeshort&0x8E80 0
+# use big endian variant of subroutine to display name+variables+flags
+# for common object formatted files
+>>0 use \^display-coff
+!:strength -10
+
+0 leshort 0x0550
+# test for unused flag bits in f_flags
+>18 uleshort&0x8E80 0
+# use little endian variant of subroutine to
+# display name+variables+flags for common object formatted files
+>>0 use display-coff
+!:strength -10
+
diff --git a/contrib/libs/libmagic/magic/Magdir/hp b/contrib/libs/libmagic/magic/Magdir/hp
new file mode 100644
index 0000000000..d57169ee16
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/hp
@@ -0,0 +1,433 @@
+
+#------------------------------------------------------------------------------
+# $File: hp,v 1.25 2019/01/13 00:32:38 christos Exp $
+# hp: file(1) magic for Hewlett Packard machines (see also "printer")
+#
+# XXX - somebody should figure out whether any byte order needs to be
+# applied to the "TML" stuff; I'm assuming the Apollo stuff is
+# big-endian as it was mostly 68K-based.
+#
+# I think the 500 series was the old stack-based machines, running a
+# UNIX environment atop the "SUN kernel"; dunno whether it was
+# big-endian or little-endian.
+#
+# Daniel Quinlan (quinlan@yggdrasil.com): hp200 machines are 68010 based;
+# hp300 are 68020+68881 based; hp400 are also 68k. The following basic
+# HP magic is useful for reference, but using "long" magic is a better
+# practice in order to avoid collisions.
+#
+# Guy Harris (guy@netapp.com): some additions to this list came from
+# HP-UX 10.0's "/usr/include/sys/unistd.h" (68030, 68040, PA-RISC 1.1,
+# 1.2, and 2.0). The 1.2 and 2.0 stuff isn't in the HP-UX 10.0
+# "/etc/magic", though, except for the "archive file relocatable library"
+# stuff, and the 68030 and 68040 stuff isn't there at all - are they not
+# used in executables, or have they just not yet updated "/etc/magic"
+# completely?
+#
+# 0 beshort 200 hp200 (68010) BSD binary
+# 0 beshort 300 hp300 (68020+68881) BSD binary
+# 0 beshort 0x20c hp200/300 HP-UX binary
+# 0 beshort 0x20d hp400 (68030) HP-UX binary
+# 0 beshort 0x20e hp400 (68040?) HP-UX binary
+# 0 beshort 0x20b PA-RISC1.0 HP-UX binary
+# 0 beshort 0x210 PA-RISC1.1 HP-UX binary
+# 0 beshort 0x211 PA-RISC1.2 HP-UX binary
+# 0 beshort 0x214 PA-RISC2.0 HP-UX binary
+
+#
+# The "misc" stuff needs a byte order; the archives look suspiciously
+# like the old 177545 archives (0xff65 = 0177545).
+#
+#### Old Apollo stuff
+0 beshort 0627 Apollo m68k COFF executable
+>18 beshort ^040000 not stripped
+>22 beshort >0 - version %d
+0 beshort 0624 apollo a88k COFF executable
+>18 beshort ^040000 not stripped
+>22 beshort >0 - version %d
+0 long 01203604016 TML 0123 byte-order format
+0 long 01702407010 TML 1032 byte-order format
+0 long 01003405017 TML 2301 byte-order format
+0 long 01602007412 TML 3210 byte-order format
+#### PA-RISC 1.1
+0 belong 0x02100106 PA-RISC1.1 relocatable object
+0 belong 0x02100107 PA-RISC1.1 executable
+>168 belong &0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x02100108 PA-RISC1.1 shared executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0210010b PA-RISC1.1 demand-load executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0210010e PA-RISC1.1 shared library
+>96 belong >0 - not stripped
+
+0 belong 0x0210010d PA-RISC1.1 dynamic load library
+>96 belong >0 - not stripped
+
+#### PA-RISC 2.0
+0 belong 0x02140106 PA-RISC2.0 relocatable object
+
+0 belong 0x02140107 PA-RISC2.0 executable
+>168 belong &0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x02140108 PA-RISC2.0 shared executable
+>168 belong &0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0214010b PA-RISC2.0 demand-load executable
+>168 belong &0x00000004 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x0214010e PA-RISC2.0 shared library
+>96 belong >0 - not stripped
+
+0 belong 0x0214010d PA-RISC2.0 dynamic load library
+>96 belong >0 - not stripped
+
+#### 800
+0 belong 0x020b0106 PA-RISC1.0 relocatable object
+
+0 belong 0x020b0107 PA-RISC1.0 executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b0108 PA-RISC1.0 shared executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b010b PA-RISC1.0 demand-load executable
+>168 belong&0x4 0x4 dynamically linked
+>(144) belong 0x054ef630 dynamically linked
+>96 belong >0 - not stripped
+
+0 belong 0x020b010e PA-RISC1.0 shared library
+>96 belong >0 - not stripped
+
+0 belong 0x020b010d PA-RISC1.0 dynamic load library
+>96 belong >0 - not stripped
+
+#### 500
+0 long 0x02080106 HP s500 relocatable executable
+>16 long >0 - version %d
+
+0 long 0x02080107 HP s500 executable
+>16 long >0 - version %d
+
+0 long 0x02080108 HP s500 pure executable
+>16 long >0 - version %d
+
+#### 200
+0 belong 0x020c0108 HP s200 pure executable
+>4 beshort >0 - version %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c0107 HP s200 executable
+>4 beshort >0 - version %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c010b HP s200 demand-load executable
+>4 beshort >0 - version %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x40000000 dynamically linked
+>8 belong &0x20000000 debuggable
+>36 belong >0 not stripped
+
+0 belong 0x020c0106 HP s200 relocatable executable
+>4 beshort >0 - version %d
+>6 beshort >0 - highwater %d
+>8 belong &0x80000000 save fp regs
+>8 belong &0x20000000 debuggable
+>8 belong &0x10000000 PIC
+
+0 belong 0x020a0108 HP s200 (2.x release) pure executable
+>4 beshort >0 - version %d
+>36 belong >0 not stripped
+
+0 belong 0x020a0107 HP s200 (2.x release) executable
+>4 beshort >0 - version %d
+>36 belong >0 not stripped
+
+0 belong 0x020c010e HP s200 shared library
+>4 beshort >0 - version %d
+>6 beshort >0 - highwater %d
+>36 belong >0 not stripped
+
+0 belong 0x020c010d HP s200 dynamic load library
+>4 beshort >0 - version %d
+>6 beshort >0 - highwater %d
+>36 belong >0 not stripped
+
+#### MISC
+0 long 0x0000ff65 HP old archive
+0 long 0x020aff65 HP s200 old archive
+0 long 0x020cff65 HP s200 old archive
+0 long 0x0208ff65 HP s500 old archive
+
+0 long 0x015821a6 HP core file
+
+0 long 0x4da7eee8 HP-WINDOWS font
+>8 byte >0 - version %d
+0 string Bitmapfile HP Bitmapfile
+
+0 string IMGfile CIS compimg HP Bitmapfile
+# XXX - see "lif"
+#0 short 0x8000 lif file
+0 long 0x020c010c compiled Lisp
+
+0 string msgcat01 HP NLS message catalog,
+>8 long >0 %d messages
+
+# Summary: HP-48/49 calculator
+# Created by: phk@data.fls.dk
+# Modified by (1): AMAKAWA Shuhei <sa264@cam.ac.uk>
+# Modified by (2): Samuel Thibault <samuel.thibault@ens-lyon.org> (HP49 support)
+0 string HPHP HP
+>4 string 48 48 binary
+>4 string 49 49 binary
+>7 byte >64 - Rev %c
+>8 leshort 0x2911 (ADR)
+>8 leshort 0x2933 (REAL)
+>8 leshort 0x2955 (LREAL)
+>8 leshort 0x2977 (COMPLX)
+>8 leshort 0x299d (LCOMPLX)
+>8 leshort 0x29bf (CHAR)
+>8 leshort 0x29e8 (ARRAY)
+>8 leshort 0x2a0a (LNKARRAY)
+>8 leshort 0x2a2c (STRING)
+>8 leshort 0x2a4e (HXS)
+>8 leshort 0x2a74 (LIST)
+>8 leshort 0x2a96 (DIR)
+>8 leshort 0x2ab8 (ALG)
+>8 leshort 0x2ada (UNIT)
+>8 leshort 0x2afc (TAGGED)
+>8 leshort 0x2b1e (GROB)
+>8 leshort 0x2b40 (LIB)
+>8 leshort 0x2b62 (BACKUP)
+>8 leshort 0x2b88 (LIBDATA)
+>8 leshort 0x2d9d (PROG)
+>8 leshort 0x2dcc (CODE)
+>8 leshort 0x2e48 (GNAME)
+>8 leshort 0x2e6d (LNAME)
+>8 leshort 0x2e92 (XLIB)
+
+0 string %%HP: HP text
+>6 string T(0) - T(0)
+>6 string T(1) - T(1)
+>6 string T(2) - T(2)
+>6 string T(3) - T(3)
+>10 string A(D) A(D)
+>10 string A(R) A(R)
+>10 string A(G) A(G)
+>14 string F(.) F(.);
+>14 string F(,) F(,);
+
+
+# Summary: HP-38/39 calculator
+# Created by: Samuel Thibault <samuel.thibault@ens-lyon.org>
+0 string HP3
+>3 string 8 HP 38
+>3 string 9 HP 39
+>4 string Bin binary
+>4 string Asc ASCII
+>7 string A (Directory List)
+>7 string B (Zaplet)
+>7 string C (Note)
+>7 string D (Program)
+>7 string E (Variable)
+>7 string F (List)
+>7 string G (Matrix)
+>7 string H (Library)
+>7 string I (Target List)
+>7 string J (ASCII Vector specification)
+>7 string K (wildcard)
+
+# Summary: HP-38/39 calculator
+# Created by: Samuel Thibault <samuel.thibault@ens-lyon.org>
+0 string HP3
+>3 string 8 HP 38
+>3 string 9 HP 39
+>4 string Bin binary
+>4 string Asc ASCII
+>7 string A (Directory List)
+>7 string B (Zaplet)
+>7 string C (Note)
+>7 string D (Program)
+>7 string E (Variable)
+>7 string F (List)
+>7 string G (Matrix)
+>7 string H (Library)
+>7 string I (Target List)
+>7 string J (ASCII Vector specification)
+>7 string K (wildcard)
+
+# hpBSD magic numbers
+0 beshort 200 hp200 (68010) BSD
+>2 beshort 0407 impure binary
+>2 beshort 0410 read-only binary
+>2 beshort 0413 demand paged binary
+0 beshort 300 hp300 (68020+68881) BSD
+>2 beshort 0407 impure binary
+>2 beshort 0410 read-only binary
+>2 beshort 0413 demand paged binary
+#
+# From David Gero <dgero@nortelnetworks.com>
+# HP-UX 10.20 core file format from /usr/include/sys/core.h
+# Unfortunately, HP-UX uses corehead blocks without specifying the order
+# There are four we care about:
+# CORE_KERNEL, which starts with the string "HP-UX"
+# CORE_EXEC, which contains the name of the command
+# CORE_PROC, which contains the signal number that caused the core dump
+# CORE_FORMAT, which contains the version of the core file format (== 1)
+# The only observed order in real core files is KERNEL, EXEC, FORMAT, PROC
+# but we include all 6 variations of the order of the first 3, and
+# assume that PROC will always be last
+# Order 1: KERNEL, EXEC, FORMAT, PROC
+0x10 string HP-UX
+>0 belong 2
+>>0xC belong 0x3C
+>>>0x4C belong 0x100
+>>>>0x58 belong 0x44
+>>>>>0xA0 belong 1
+>>>>>>0xAC belong 4
+>>>>>>>0xB0 belong 1
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0x90 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 2: KERNEL, FORMAT, EXEC, PROC
+>>>0x4C belong 1
+>>>>0x58 belong 4
+>>>>>0x5C belong 1
+>>>>>>0x60 belong 0x100
+>>>>>>>0x6C belong 0x44
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0xA4 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 3: FORMAT, KERNEL, EXEC, PROC
+0x24 string HP-UX
+>0 belong 1
+>>0xC belong 4
+>>>0x10 belong 1
+>>>>0x14 belong 2
+>>>>>0x20 belong 0x3C
+>>>>>>0x60 belong 0x100
+>>>>>>>0x6C belong 0x44
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0xA4 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 4: EXEC, KERNEL, FORMAT, PROC
+0x64 string HP-UX
+>0 belong 0x100
+>>0xC belong 0x44
+>>>0x54 belong 2
+>>>>0x60 belong 0x3C
+>>>>>0xA0 belong 1
+>>>>>>0xAC belong 4
+>>>>>>>0xB0 belong 1
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0x44 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 5: FORMAT, EXEC, KERNEL, PROC
+0x78 string HP-UX
+>0 belong 1
+>>0xC belong 4
+>>>0x10 belong 1
+>>>>0x14 belong 0x100
+>>>>>0x20 belong 0x44
+>>>>>>0x68 belong 2
+>>>>>>>0x74 belong 0x3C
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0x58 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+# Order 6: EXEC, FORMAT, KERNEL, PROC
+>0 belong 0x100
+>>0xC belong 0x44
+>>>0x54 belong 1
+>>>>0x60 belong 4
+>>>>>0x64 belong 1
+>>>>>>0x68 belong 2
+>>>>>>>0x74 belong 0x2C
+>>>>>>>>0xB4 belong 4 core file
+>>>>>>>>>0x44 string >\0 from '%s'
+>>>>>>>>>0xC4 belong 3 - received SIGQUIT
+>>>>>>>>>0xC4 belong 4 - received SIGILL
+>>>>>>>>>0xC4 belong 5 - received SIGTRAP
+>>>>>>>>>0xC4 belong 6 - received SIGABRT
+>>>>>>>>>0xC4 belong 7 - received SIGEMT
+>>>>>>>>>0xC4 belong 8 - received SIGFPE
+>>>>>>>>>0xC4 belong 10 - received SIGBUS
+>>>>>>>>>0xC4 belong 11 - received SIGSEGV
+>>>>>>>>>0xC4 belong 12 - received SIGSYS
+>>>>>>>>>0xC4 belong 33 - received SIGXCPU
+>>>>>>>>>0xC4 belong 34 - received SIGXFSZ
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/human68k b/contrib/libs/libmagic/magic/Magdir/human68k
new file mode 100644
index 0000000000..707c74098b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/human68k
@@ -0,0 +1,26 @@
+
+#------------------------------------------------------------------------------
+# $File: human68k,v 1.6 2021/04/26 15:56:00 christos Exp $
+# human68k: file(1) magic for Human68k (X680x0 DOS) binary formats
+# Magic too short!
+#0 string HU Human68k
+#>68 string LZX LZX compressed
+#>>72 string >\0 (version %s)
+#>(8.L+74) string LZX LZX compressed
+#>>(8.L+78) string >\0 (version %s)
+#>60 belong >0 binded
+#>(8.L+66) string #HUPAIR hupair
+#>0 string HU X executable
+#>(8.L+74) string #LIBCV1 - linked PD LIBC ver 1
+#>4 belong >0 - base address %#x
+#>28 belong >0 not stripped
+#>32 belong >0 with debug information
+#0 beshort 0x601a Human68k Z executable
+#0 beshort 0x6000 Human68k object file
+#0 belong 0xd1000000 Human68k ar binary archive
+#0 belong 0xd1010000 Human68k ar ascii archive
+#0 beshort 0x0068 Human68k lib archive
+#4 string LZX Human68k LZX compressed
+#>8 string >\0 (version %s)
+#>4 string LZX R executable
+#2 string #HUPAIR Human68k hupair R executable
diff --git a/contrib/libs/libmagic/magic/Magdir/ibm370 b/contrib/libs/libmagic/magic/Magdir/ibm370
new file mode 100644
index 0000000000..dc976f8705
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ibm370
@@ -0,0 +1,52 @@
+
+#------------------------------------------------------------------------------
+# $File: ibm370,v 1.11 2021/03/14 16:51:45 christos Exp $
+# ibm370: file(1) magic for IBM 370 and compatibles.
+#
+# "ibm370" said that 0x15d == 0535 was "ibm 370 pure executable".
+# What the heck *is* "USS/370"?
+# AIX 4.1's "/etc/magic" has
+#
+# 0 short 0535 370 sysV executable
+# >12 long >0 not stripped
+# >22 short >0 - version %d
+# >30 long >0 - 5.2 format
+# 0 short 0530 370 sysV pure executable
+# >12 long >0 not stripped
+# >22 short >0 - version %d
+# >30 long >0 - 5.2 format
+#
+# instead of the "USS/370" versions of the same magic numbers.
+#
+0 beshort 0537 370 XA sysV executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+>30 belong >0 - 5.2 format
+0 beshort 0532 370 XA sysV pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+>30 belong >0 - 5.2 format
+0 beshort 054001 370 sysV pure executable
+>12 belong >0 not stripped
+0 beshort 055001 370 XA sysV pure executable
+>12 belong >0 not stripped
+0 beshort 056401 370 sysV executable
+>12 belong >0 not stripped
+0 beshort 057401 370 XA sysV executable
+>12 belong >0 not stripped
+0 beshort 0531 SVR2 executable (Amdahl-UTS)
+>12 belong >0 not stripped
+>24 belong >0 - version %d
+0 beshort 0534 SVR2 pure executable (Amdahl-UTS)
+>12 belong >0 not stripped
+>24 belong >0 - version %d
+0 beshort 0530 SVR2 pure executable (USS/370)
+>12 belong >0 not stripped
+>24 belong >0 - version %d
+0 beshort 0535 SVR2 executable (USS/370)
+>12 belong >0 not stripped
+>24 belong >0 - version %d
+
+# NETDATA (https://en.wikipedia.org/wiki/NETDATA)
+# -\INMR01 In EBCDIC
+0 string \x60\xe0\xc9\xd5\xd4\xd9\xf0\xf1 IBM NETDATA file
diff --git a/contrib/libs/libmagic/magic/Magdir/ibm6000 b/contrib/libs/libmagic/magic/Magdir/ibm6000
new file mode 100644
index 0000000000..724b64d3a5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ibm6000
@@ -0,0 +1,35 @@
+
+#------------------------------------------------------------------------------
+# $File: ibm6000,v 1.15 2021/07/03 14:01:46 christos Exp $
+# ibm6000: file(1) magic for RS/6000 and the RT PC.
+#
+0 beshort 0x01df executable (RISC System/6000 V3.1) or obj module
+>12 belong >0 not stripped
+# Breaks sun4 statically linked execs.
+#0 beshort 0x0103 executable (RT Version 2) or obj module
+#>2 byte 0x50 pure
+#>28 belong >0 not stripped
+#>6 beshort >0 - version %ld
+# GRR: line below is too general as it matches also TTComp archive, ASCII, 1K handled by ./archive
+0 beshort 0x0104 shared library
+# GRR: line below is too general as it matches also TTComp archive, ASCII, 2K handled by ./archive
+0 beshort 0x0105 ctab data
+0 beshort 0xfe04 structured file
+0 string 0xabcdef AIX message catalog
+0 belong 0x000001f9 AIX compiled message catalog
+0 string \<aiaff> archive
+0 string \<bigaf> archive (big format)
+0 belong 0x09006bea AIX backup/restore format file
+0 belong 0x09006fea AIX backup/restore format file
+
+0 beshort 0x01f7 64-bit XCOFF executable or object module
+>20 belong 0 not stripped
+# GRR: this test is still too general as it catches also many FATs of DOS filesystems
+4 belong &0x0feeddb0
+# real core dump could not be 32-bit and 64-bit together
+>7 byte&0x03 !3 AIX core file
+>>1 byte &0x01 fulldump
+>>7 byte &0x01 32-bit
+>>>0x6e0 string >\0 \b, %s
+>>7 byte &0x02 64-bit
+>>>0x524 string >\0 \b, %s
diff --git a/contrib/libs/libmagic/magic/Magdir/icc b/contrib/libs/libmagic/magic/Magdir/icc
new file mode 100644
index 0000000000..15fd76b8d5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/icc
@@ -0,0 +1,214 @@
+
+#------------------------------------------------------------------------------
+# $File: icc,v 1.7 2021/04/26 15:56:00 christos Exp $
+# icc: file(1) magic for International Color Consortium file formats
+
+#
+# Color profiles as per the ICC's "Image technology colour management -
+# Architecture, profile format, and data structure" specification.
+# See
+#
+# http://www.color.org/specification/ICC1v43_2010-12.pdf
+#
+# for Specification ICC.1:2010 (Profile version 4.3.0.0).
+# URL: http://fileformats.archiveteam.org/wiki/ICC_profile
+# Reference: http://www.color.org/iccmax/ICC.2-2016-7.pdf
+# Update: Joerg Jenderek
+#
+# Bytes 36 to 39 contain a generic profile file signature of "acsp";
+# bytes 40 to 43 "may be used to identify the primary platform/operating
+# system framework for which the profile was created".
+#
+# check and display ICC/ICM color profile
+0 name color-profile
+>36 string acsp
+# skip ASCII like Cognacspirit.txt by month <= 12
+>>26 ubeshort <13
+# platform/operating system. Only 5 mentioned
+
+#
+# This appears to be what's used for Apple ColorSync profiles.
+# Instead of adding that, Apple just changed the generic "acsp" entry
+# to be for "ColorSync ICC Color Profile" rather than "Kodak Color
+# Management System, ICC Profile".
+# Yes, it's "APPL", not "AAPL"; see the spec.
+>>>40 string APPL ColorSync
+
+# Microsoft ICM color profile
+>>>40 string MSFT Microsoft
+
+# Yes, that's a blank after "SGI".
+>>>40 string SGI\ SGI
+
+# XXX - is this what's used for the Sun KCMS or not? The standard file
+# uses just "acsp" for that, but Apple's file uses it for "ColorSync",
+# and there *is* an identified "primary platform" value of SUNW.
+>>>40 string SUNW Sun KCMS
+
+# 5th platform
+>>>40 string TGNT Taligent
+
+# remaining "l" "e" of "color profile" printed later to avoid error
+>>>40 string x color profi
+#>>>40 string x (%.4s)
+!:mime application/vnd.iccprofile
+# for "ICM" extension only versions 2.x and for Kodak "CC" 2.0 is found
+>>>8 ubyte =2
+# do not use empty message text to avoid error like
+# icc, 82: Warning: Current entry does not yet have a description for adding a EXTENSION type
+# file.exe: could not find any valid magic files!
+>>>>9 ubyte !0 \ble
+!:ext icc/icm
+# minor version
+>>>>9 ubyte =0 \bl
+# Kodak colour management system
+>>>>>4 string =KCMS \be
+!:ext icc/icm/cc
+>>>>>4 string !KCMS \be
+!:ext icc/icm
+>>>8 ubyte !2 \ble
+!:ext icc
+# Profile version major.4bit-minor.sub1.sub2 like 4.3.0.0 (04300000h)
+>>>8 ubyte x %u
+>>>9 ubyte/16 x \b.%u
+# reserved and shall be null but 205.205 in umx1220u.icm
+>>>10 ubyte >0 \b.%u
+>>>>11 ubyte >0 \b.%u
+# preferred colour management module like appl CCMS KCMS Lino UCCM "Win " "FF "
+# skip space like in brmsl08f.icm and null like in brmsl09f.icm, brmsl07f.icm
+>>>4 string >\ \b, type %.2s
+>>>>6 string >\ \b%.1s
+>>>>>7 string >\ \b%.1s
+# colour space "XYZ " "Lab " "RGB " CMYK GRAY ...
+>>>16 string x \b, %.3s
+>>>19 string >\ \b%.1s
+# Profile Connection Space (PCS) field usually "XYZ " or "Lab " but sometimes
+# null or CMYK like in ISOcoated_v2_to_PSOcoated_v3_DeviceLink.icc
+>>>20 string >\0 \b/%.3s
+>>>>23 string >\ \b%.1s
+# eleven device classes
+>>>12 string x \b-%.4s device
+# skip 00001964h in hpf69000.icc or 0h in XRDC50Q.ICM or " ROT" in brmsl05f.icm
+>>>52 string >\040
+# skip "none" model like in "Trinitron Compatible 9300K G2.2.icm"
+>>>>52 ubelong !0x6e6f6e65
+# device manufacturer field like "HP " "IBM " EPSO
+>>>>>48 string x \b, %.2s
+>>>>>50 string >\ \b%.1s
+>>>>>51 string >\ \b%.1s
+# model like "ADI " "A265" and skip 20000404h in IS330.icm for RICOH RUSSIAN-SC
+>>>>>52 string >\ \ \b/%.3s
+>>>>>>55 string >\ \b%.1s
+>>>>>52 string x model
+# creator (often same as manufacture) like HP SONY XROX or null like in A925A.icm
+>>>80 string >\0 by %.2s
+>>>>82 string >\ \b%.1s
+>>>>>83 string >\ \b%.1s
+# profile size
+>>>0 ubelong x \b, %u bytes
+# skip invalid date 0 like in linearSRGB.icc
+>>>24 ubequad !0
+# datetime dd-mm-yyyy hh:mm:ss
+>>>>28 ubeshort x \b, %u
+# month <= 12
+>>>>26 ubeshort x \b-%u
+# year
+>>>>24 ubeshort x \b-%u
+# do not display midnight time like in CNHP8308.ICC
+>>>>30 ubequad&0xFFffFFffFFff0000 !0
+# hour <= 24
+>>>>>30 ubeshort x %u
+# minutes <= 59
+>>>>>32 ubeshort x \b:%.2u
+# seconds <= 59
+>>>>>34 ubeshort x \b:%.2u
+# vendor specific flags like 2 in HPCLJ5.ICM
+>>>44 ubeshort >0 \b, %#x vendor flags
+# profile flags bits 0-2 of least 16 used by ICC
+#>>>44 ubelong >0 \b, %#x flags
+# icEmbeddedProfileTrue
+>>>44 ubelong &1 \b, embedded
+# icEmbeddedProfileFalse
+#>>>44 ubelong ^1 \b, not embedded
+# icUseWithEmbeddedDataOnly
+>>>44 ubelong &2 \b, dependently
+# icUseAnywhere
+#>>>44 ubelong ^2 \b, independently
+>>>44 ubelong &4 \b, MCS
+#>>>44 ubelong ^4 \b, no MCS
+# vendor specific device attributes 1~srgb.icc
+# E000D00h~CNB7QEDA.ICM C000A00h~CNB5FCAA.ICM 01040401h~CNB25PE3.ICM
+>>>56 ubelong >0 \b, %#x vendor attribute
+# ICC device attributes bits 0-7 used
+#>>>60 ubelong x \b, %#x attribute
+# http://www.color.org/icc34.h
+>>>60 ubelong &0x01 \b, transparent
+#>>>60 ubelong ^0x01 \b, reflective
+>>>60 ubelong &0x02 \b, matte
+#>>>60 ubelong ^0x02 \b, glossy
+>>>60 ubelong &0x04 \b, negative
+#>>>60 ubelong ^0x04 \b, positive
+>>>60 ubelong &0x08 \b, black&white
+#>>>60 ubelong ^0x08 \b, colour
+>>>60 ubelong &0x10 \b, non-paper
+#>>>60 ubelong ^0x10 \b, paper
+>>>60 ubelong &0x20 \b, non-textured
+#>>>60 ubelong ^0x20 \b, textured
+>>>60 ubelong &0x40 \b, non-isotropic
+#>>>60 ubelong ^0x40 \b, isotropic
+>>>60 ubelong &0x80 \b, self-luminous
+#>>>60 ubelong ^0x80 \b, non-self-luminous
+# rendering intent 0-3 but 7AEA5027h in EE051__1.ICM 6CB1BCh in EE061__1.ICM
+>>>64 ubelong >3 \b, %#x rendering intent
+#>>>64 ubelong =0 \b, perceptual
+>>>64 ubelong =1 \b, relative colorimetric
+>>>64 ubelong =2 \b, saturation
+>>>64 ubelong =3 \b, absolute colorimetric
+# PCS illuminant (3*s15Fixed16Numbers) often 0000f6d6 00010000 0000d32d
+>>>71 ubequad !0xd6000100000000d3 \b, PCS
+# usually X~0.9642*65536=63189.8112~63190=F6D5h ; but also found
+# often F6D6 in gt5000r.icm, F6B8 in kodakce.icm, F6CA in RSWOP.icm
+>>>>68 ubelong !0x0000f6d5 X=%#x
+# usually Y=1.0~00010000h but Y=0 in brmsl07f.icm
+>>>>72 ubelong !0x00010000 Y=%#x
+# usually Z~0.8249*65536=54060.6464~54061=D32Dh ; but also found
+# D2F7 in hp1200c.icm, often D32C in A925A.icm, D309 in RSWOP.icm , D2F8 in kodak_dc.icm
+>>>>76 ubelong !0x0000d32d Z=%#x
+# Profile ID. MD5 fingerprinting method as defined in Internet RFC 1321.
+>>>84 ubequad >0 \b, %#llx MD5
+# reserved in older versions should be zero but also found CDCDCDCDCDCDCDCD
+#>>100 ubequad x \b %#llx reserved
+# tag table
+# 6 <= tags count <= 43
+#>>>128 ubelong >43 \b, %u tags
+>>>128 ubelong x
+# shall contain the profileDescriptionTag "desc" , copyrightTag "cprt"
+# search range = tags count * 12 -8=< maximal tag count * 12 -8= 43 * 12 -8= 508
+>>>>132 search/508 cprt
+# but no copyright tag in linearSRGB.icc
+# beneath /System/Library/Frameworks/WebKit.framework/
+# Versions/A/Frameworks/WebCore.framework/Versions/A/Resources
+>>>>132 default x \b, no copyright tag
+# 1st tag
+#>>>132 string x \b, 1st tag %.4s
+#>>>136 ubelong x %#x offset
+#>>>140 ubelong x %#x len
+# 2nd tag,...
+# look also for profileDescriptionTag "desc"
+>>>132 search/508 desc
+# look further for TextDescriptionType "desc" signature
+>>>>(&0.L) string =desc
+>>>>>&4 pstring/l x "%s"
+# look alternative for multiLocalizedUnicodeType "mluc" signature like in VideoPAL.icc
+>>>>(&0.L) string =mluc
+>>>>>&(&8.L) ubequad x
+>>>>>>&4 bestring16 x '%s'
+
+# Any other profile.
+# XXX - should we use "acsp\0\0\0\0" for "no primary platform" profiles,
+# and use "acsp" for everything else and dump the "primary platform"
+# string in those cases?
+36 string acsp
+>0 use color-profile
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/iff b/contrib/libs/libmagic/magic/Magdir/iff
new file mode 100644
index 0000000000..258d16a4e1
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/iff
@@ -0,0 +1,80 @@
+
+#------------------------------------------------------------------------------
+# $File: iff,v 1.18 2022/03/21 19:57:18 christos Exp $
+# iff: file(1) magic for Interchange File Format (see also "audio" & "images")
+#
+# Daniel Quinlan (quinlan@yggdrasil.com) -- IFF was designed by Electronic
+# Arts for file interchange. It has also been used by Apple, SGI, and
+# especially Commodore-Amiga.
+#
+# IFF files begin with an 8 byte FORM header, followed by a 4 character
+# FORM type, which is followed by the first chunk in the FORM.
+
+0 string FORM IFF data
+#>4 belong x \b, FORM is %d bytes long
+# audio formats
+>8 string AIFF \b, AIFF audio
+!:mime audio/x-aiff
+>8 string AIFC \b, AIFF-C compressed audio
+!:mime audio/x-aiff
+>8 string 8SVX \b, 8SVX 8-bit sampled sound voice
+!:mime audio/x-aiff
+>8 string 16SV \b, 16SV 16-bit sampled sound voice
+>8 string SAMP \b, SAMP sampled audio
+>8 string MAUD \b, MAUD MacroSystem audio
+>8 string SMUS \b, SMUS simple music
+>8 string CMUS \b, CMUS complex music
+# image formats
+>8 string ILBMBMHD \b, ILBM interleaved image
+>>20 beshort x \b, %d x
+>>22 beshort x %d
+>8 string RGBN \b, RGBN 12-bit RGB image
+>8 string RGB8 \b, RGB8 24-bit RGB image
+>8 string DEEP \b, DEEP TVPaint/XiPaint image
+>8 string DR2D \b, DR2D 2-D object
+>8 string TDDD \b, TDDD 3-D rendering
+>8 string LWOB \b, LWOB 3-D object
+>8 string LWO2 \b, LWO2 3-D object, v2
+>8 string LWLO \b, LWLO 3-D layered object
+>8 string REAL \b, REAL Real3D rendering
+>8 string MC4D \b, MC4D MaxonCinema4D rendering
+>8 string ANIM \b, ANIM animation
+>8 string YAFA \b, YAFA animation
+>8 string SSA\ \b, SSA super smooth animation
+>8 string FANT \b, Fantavision animation
+>8 string ACBM \b, ACBM continuous image
+>8 string FAXX \b, FAXX fax image
+>8 string STFX \b, ST-Fax image
+>8 string IMAGIHDR \b, CD-i image
+# other formats
+>8 string FTXT \b, FTXT formatted text
+>8 string CTLG \b, CTLG message catalog
+>8 string PREF \b, PREF preferences
+>8 string DTYP \b, DTYP datatype description
+>8 string PTCH \b, PTCH binary patch
+>8 string AMFF \b, AMFF AmigaMetaFile format
+>8 string WZRD \b, WZRD StormWIZARD resource
+>8 string DOC\040 \b, DOC desktop publishing document
+>8 string SWRT \b, SWRT Final Copy/Writer document
+>8 string WORD \b, ProWrite document
+>8 string WTXT \b, WTXT Wordworth document
+>8 string WOWO \b, WOWO Wordworth document
+>8 string WVQA \b, Westwood Studios VQA Multimedia,
+>>24 leshort x %d video frames,
+>>26 leshort x %d x
+>>28 leshort x %d
+>8 string MOVE \b, Wing Commander III Video
+>>12 string _PC_ \b, PC version
+>>12 string 3DO_ \b, 3DO version
+
+# These go at the end of the iff rules
+#
+# David Griffith <dave@661.org>
+# I don't see why these might collide with anything else.
+#
+# Interactive Fiction related formats
+#
+>8 string IFRS \b, Blorb Interactive Fiction
+>>24 string Exec with executable chunk
+>8 string IFZS \b, Z-machine or Glulx saved game file (Quetzal)
+!:mime application/x-blorb
diff --git a/contrib/libs/libmagic/magic/Magdir/images b/contrib/libs/libmagic/magic/Magdir/images
new file mode 100644
index 0000000000..48e9f6dabf
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/images
@@ -0,0 +1,4219 @@
+
+#------------------------------------------------------------------------------
+# $File: images,v 1.243 2023/07/17 16:49:09 christos Exp $
+# images: file(1) magic for image formats (see also "iff", and "c-lang" for
+# XPM bitmaps)
+#
+# originally from jef@helios.ee.lbl.gov (Jef Poskanzer),
+# additions by janl@ifi.uio.no as well as others. Jan also suggested
+# merging several one- and two-line files into here.
+#
+# little magic: PCX (first byte is 0x0a)
+
+# Targa - matches `povray', `ppmtotga' and `xv' outputs
+# by Philippe De Muyter <phdm@macqel.be>
+# URL: http://justsolve.archiveteam.org/wiki/TGA
+# Reference: http://www.dca.fee.unicamp.br/~martino/disciplinas/ea978/tgaffs.pdf
+# Update: Joerg Jenderek
+# at 2, byte ImgType must be 1, 2, 3, 9, 10 or 11
+# ,32 or 33 (both not observed)
+# at 1, byte CoMapType must be 1 if ImgType is 1 or 9, 0 otherwise
+# or theoretically 2-128 reserved for use by Truevision or 128-255 may be used for developer applications
+# at 3, leshort Index is 0 for povray, ppmtotga and xv outputs
+# `xv' recognizes only a subset of the following (RGB with pixelsize = 24)
+# `tgatoppm' recognizes a superset (Index may be anything)
+#
+# test of Color Map Type 0~no 1~color map
+# and Image Type 1 2 3 9 10 11 32 33
+# and Color Map Entry Size 0 15 16 24 32
+0 ubequad&0x00FeC400000000C0 0
+# Conflict with MPEG sequences.
+!:strength -40
+# Prevent conflicts with CRI ADX.
+#>(2.S-2) belong !0x28632943
+# above line does not work for rgb32_top_left_rle.tga
+# skip some MPEG sequence *.vob and some CRI ADX audio with improbable interleave bits
+>17 ubyte&0xC0 !0xC0
+# skip more garbage like *.iso by looking for positive image type
+>>2 ubyte >0
+# skip some compiled terminfo like xterm+tmux by looking for image type less equal 33
+>>>2 ubyte <34
+# skip some MPEG sequence *.vob HV001T01.EVO winnicki.mpg with unacceptable alpha channel depth 11
+>>>>17 ubyte&0x0F !11
+# skip arches.3200 , Finder.Root , Slp.1 by looking for low pixel depth 1 8 15 16 24 32
+>>>>>16 ubyte 1
+>>>>>>0 use tga-image
+>>>>>16 ubyte 8
+>>>>>>0 use tga-image
+>>>>>16 ubyte 15
+>>>>>>0 use tga-image
+>>>>>16 ubyte 16
+>>>>>>0 use tga-image
+>>>>>16 ubyte 24
+>>>>>>0 use tga-image
+>>>>>16 ubyte 32
+>>>>>>0 use tga-image
+# display tga bitmap image information
+0 name tga-image
+>2 ubyte <34 Targa image data
+!:mime image/x-tga
+!:apple ????TPIC
+# normal extension .tga but some Truevision products used others:
+# tpic (Apple),icb (Image Capture Board),vda (Video Display Adapter),vst (NuVista),win (UNSURE about that)
+!:ext tga/tpic/icb/vda/vst
+# image type 1 2 3 9 10 11 32 33
+>2 ubyte&0xF7 1 - Map
+>2 ubyte&0xF7 2 - RGB
+# alpha channel
+>>17 ubyte&0x0F >0 \bA
+>2 ubyte&0xF7 3 - Mono
+# type not found, but by http://www.fileformat.info/format/tga/corion.htm
+# Compressed color-mapped data, using Huffman, Delta, and runlength encoding
+>2 ubyte 32 - Color
+# Compressed color-mapped data, using Huffman, Delta, and RLE. 4-pass quadtree- type process
+>2 ubyte 33 - Color
+# Color Map Type 0~no 1~color map
+>1 ubyte 1 (
+# first color map entry, 0 normal
+>>3 uleshort >0 \b%d-
+# color map length 0 2 1dh 3bh d9h 100h
+>>5 uleshort x \b%d)
+# 8~run length encoding bit
+>2 ubyte&0x08 8 - RLE
+# gimp can create big pictures!
+>12 uleshort >0 %d x
+>12 uleshort =0 65536 x
+# image height. 0 interpreted as 65536
+>14 uleshort >0 %d
+>14 uleshort =0 65536
+# Image Pixel depth 1 8 15 16 24 32
+>16 ubyte x x %d
+# X origin of image. 0 normal
+>8 uleshort >0 +%d
+# Y origin of image. 0 normal; positive for top
+>10 uleshort >0 +%d
+# Image descriptor: bits 3-0 give the alpha channel depth, bits 5-4 give direction
+# alpha depth like: 1 8
+>17 ubyte&0x0F >0 - %d-bit alpha
+# bits 5-4 give direction. normal bottom left
+>17 ubyte &0x20 - top
+#>17 ubyte ^0x20 - bottom
+>17 ubyte &0x10 - right
+#>17 ubyte ^0x10 - left
+# some info say other bits 6-7 should be zero
+# but data storage interleave by http://www.fileformat.info/format/tga/corion.htm
+# 00 - no interleave;01 - even/odd interleave; 10 - four way interleave; 11 - reserved
+#>17 ubyte&0xC0 0x00 - no interleave
+>17 ubyte&0xC0 0x40 - interleave
+>17 ubyte&0xC0 0x80 - four way interleave
+>17 ubyte&0xC0 0xC0 - reserved
+# positive length implies identification field
+>0 ubyte >0
+>>18 string x "%s"
+# last 18 bytes of newer tga file footer signature
+>18 search/4261301/s TRUEVISION-XFILE.\0
+# extension area offset if not 0
+>>&-8 ulelong >0
+# length of the extension area. normal 495 for version 2.0
+>>>(&-4.l) uleshort 0x01EF
+# AuthorName[41]
+>>>>&0 string >\0 - author "%-.40s"
+# Comment[324]=4 * 80 null terminated
+>>>>&41 string >\0 - comment "%-.80s"
+# date
+>>>>&365 ubequad&0xffffFFFFffff0000 !0
+# Day
+>>>>>&-6 uleshort x %d
+# Month
+>>>>>&-8 uleshort x \b-%d
+# Year
+>>>>>&-4 uleshort x \b-%d
+# time
+>>>>&371 ubequad&0xffffFFFFffff0000 !0
+# hour
+>>>>>&-8 uleshort x %d
+# minutes
+>>>>>&-6 uleshort x \b:%.2d
+# second
+>>>>>&-4 uleshort x \b:%.2d
+# JobName[41]
+>>>>&377 string >\0 - job "%-.40s"
+# JobHour Jobminute Jobsecond
+>>>>&418 ubequad&0xffffFFFFffff0000 !0
+>>>>>&-8 uleshort x %d
+>>>>>&-6 uleshort x \b:%.2d
+>>>>>&-4 uleshort x \b:%.2d
+# SoftwareId[41]
+>>>>&424 string >\0 - %-.40s
+# SoftwareVersionNumber
+>>>>&424 ubyte >0
+>>>>>&40 uleshort/100 x %d
+>>>>>&40 uleshort%100 x \b.%d
+# VersionLetter
+>>>>>&42 ubyte >0x20 \b%c
+# KeyColor
+>>>>&468 ulelong >0 - keycolor %#8.8x
+# Denominator of Pixel ratio. 0~no pixel aspect
+>>>>&474 uleshort >0
+# Numerator
+>>>>>&-4 uleshort >0 - aspect %d
+>>>>>&-2 uleshort x \b/%d
+# Denominator of Gamma ratio. 0~no Gamma value
+>>>>&478 uleshort >0
+# Numerator
+>>>>>&-4 uleshort >0 - gamma %d
+>>>>>&-2 uleshort x \b/%d
+# ColorOffset
+#>>>>&480 ulelong x - col offset %#8.8x
+# StampOffset
+#>>>>&484 ulelong x - stamp offset %#8.8x
+# ScanOffset
+#>>>>&488 ulelong x - scan offset %#8.8x
+# AttributesType
+#>>>>&492 ubyte x - Attributes %#x
+## EndOfTGA
+
+# PBMPLUS images
+# URL: https://en.wikipedia.org/wiki/Netpbm
+# The next byte following the magic is always whitespace.
+# adding 65 to strength so that Netpbm images comes before "x86 boot sector" or
+# "DOS/MBR boot sector" identified by ./filesystems
+0 name netpbm
+>3 regex/s =\^[0-9]{1,50}[\040\t\f\r\n]+[0-9]{1,50} Netpbm image data
+>>&0 regex =[0-9]{1,50} \b, size = %s x
+>>>&0 regex =[0-9]{1,50} \b %s
+
+0 search/1 P1
+# test for whitespace after 2 byte magic
+>2 regex/2 [\040\t\f\r\n]
+# skip DROID x-fmt-164-signature-id-583.pbm with ten 0 digits
+>>3 string !000000000
+>>>0 use netpbm
+>>>0 string x \b, bitmap
+!:strength + 65
+!:mime image/x-portable-bitmap
+!:ext pbm
+# check for character # starting a comment line
+>>>3 ubyte =0x23
+>>>>4 string x %s
+
+0 search/1 P2
+>0 regex/4 P2[\040\t\f\r\n]
+>>0 use netpbm
+>>0 string x \b, greymap
+!:strength + 65
+# american spelling gray
+!:mime image/x-portable-graymap
+!:ext pgm
+
+0 search/1 P3
+>0 regex/4 P3[\040\t\f\r\n]
+>>0 use netpbm
+>>0 string x \b, pixmap
+!:strength + 65
+!:mime image/x-portable-pixmap
+!:ext ppm
+
+0 string P4
+>0 regex/4 P4[\040\t\f\r\n]
+>>0 use netpbm
+>>0 string x \b, rawbits, bitmap
+!:strength + 65
+!:mime image/x-portable-bitmap
+!:ext pbm
+
+0 string P5
+>0 regex/4 P5[\040\t\f\r\n]
+>>0 use netpbm
+>>0 string x \b, rawbits, greymap
+!:strength + 65
+!:mime image/x-portable-greymap
+!:ext pgm
+
+0 string P6
+>0 regex/4 P6[\040\t\f\r\n]
+>>0 use netpbm
+>>0 string x \b, rawbits, pixmap
+!:strength + 65
+!:mime image/x-portable-pixmap
+!:ext ppm/pnm
+
+# URL: https://en.wikipedia.org/wiki/Netpbm#PAM_graphics_format
+# Reference: http://fileformats.archiveteam.org/wiki/Portable_Arbitrary_Map
+# Update: Joerg Jenderek
+0 string P7
+# skip DROID fmt-405-signature-id-589.pam by looking for character like New Line
+>2 ubyte !0xAB
+#>2 ubyte =0x0A
+>>3 search/256/b WIDTH Netpbm PAM image file, size =
+!:mime image/x-portable-arbitrarymap
+!:ext pam
+!:strength + 65
+>>>&1 string x %s
+>>>3 search/256/b HEIGHT x
+>>>>&1 string x %s
+# at offset 2 a New Line character (0xA) should appear
+>>>2 ubyte !0x0A \b, %#x at offset 2 instead new line
+
+# From: bryanh@giraffe-data.com (Bryan Henderson)
+0 string \117\072 Solitaire Image Recorder format
+>4 string \013 MGI Type 11
+>4 string \021 MGI Type 17
+0 string .MDA MicroDesign data
+>21 ubyte 48 version 2
+>21 ubyte 51 version 3
+0 string .MDP MicroDesign page data
+>21 ubyte 48 version 2
+>21 ubyte 51 version 3
+
+# NIFF (Navy Interchange File Format, a modification of TIFF) images
+# [GRR: this *must* go before TIFF]
+0 string IIN1 NIFF image data
+!:mime image/x-niff
+
+# Canon RAW version 1 (CRW) files are a type of Canon Image File Format
+# (CIFF) file. These are apparently all little-endian.
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: https://www.sno.phy.queensu.ca/~phil/exiftool/canon_raw.html
+0 string II\x1a\0\0\0HEAPCCDR Canon CIFF raw image data
+!:mime image/x-canon-crw
+>16 uleshort x \b, version %d.
+>14 uleshort x \b%d
+
+# Canon RAW version 2 (CR2) files are a kind of TIFF with an extra magic
+# number. Put this above the TIFF test to make sure we detect them.
+# These are apparently all little-endian.
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: https://libopenraw.freedesktop.org/wiki/Canon_CR2
+0 string II\x2a\0\x10\0\0\0CR Canon CR2 raw image data
+!:mime image/x-canon-cr2
+!:strength +80
+>10 ubyte x \b, version %d.
+>11 ubyte x \b%d
+
+# Fujifilm RAF RAW image files with embedded JPEG data and compressed
+# or uncompressed CFA RAW data. Byte order: Big Endian.
+# URL: https://libopenraw.freedesktop.org/formats/raf/
+# Useful info from http://fileformats.archiveteam.org/wiki/Fujifilm_RAF.
+# File extension: RAF
+# Works for both the FinePix S2 Pro and the X-T3. Anybody have some more Fuji
+# raw samples available?
+# -- David Dyer-Bennet <dd-b@dd-b.net> 9-Sep-2021
+0 string FUJIFILMCCD-RAW Fujifilm RAF raw image data
+!:mime image/x-fuji-raf
+!:ext raf
+>0x10 string x \b, format version %4.4s
+>0x1C string x \b, camera %s
+
+# Tag Image File Format, from Daniel Quinlan (quinlan@yggdrasil.com)
+# The second word of TIFF files is the TIFF version number, 42, which has
+# never changed. The TIFF specification recommends testing for it.
+0 string MM\x00\x2a TIFF image data, big-endian
+!:strength +70
+!:mime image/tiff
+!:ext tif/tiff
+>(4.L) use \^tiff_ifd
+0 string II\x2a\x00 TIFF image data, little-endian
+!:mime image/tiff
+!:strength +70
+!:ext tif/tiff
+>(4.l) use tiff_ifd
+
+0 name tiff_ifd
+>0 uleshort x \b, direntries=%d
+>2 use tiff_entry
+
+0 name tiff_entry
+# NewSubFileType
+>0 uleshort 0xfe
+>>12 use tiff_entry
+>0 uleshort 0x100
+>>4 ulelong 1
+>>>12 use tiff_entry
+>>>8 uleshort x \b, width=%d
+>0 uleshort 0x101
+>>4 ulelong 1
+>>>8 uleshort x \b, height=%d
+>>>12 use tiff_entry
+>0 uleshort 0x102
+>>8 uleshort x \b, bps=%d
+>>12 use tiff_entry
+>0 uleshort 0x103
+>>4 ulelong 1 \b, compression=
+>>>8 uleshort 1 \bnone
+>>>8 uleshort 2 \bhuffman
+>>>8 uleshort 3 \bbi-level group 3
+>>>8 uleshort 4 \bbi-level group 4
+>>>8 uleshort 5 \bLZW
+>>>8 uleshort 6 \bJPEG (old)
+>>>8 uleshort 7 \bJPEG
+>>>8 uleshort 8 \bdeflate
+>>>8 uleshort 9 \bJBIG, ITU-T T.85
+>>>8 uleshort 0xa \bJBIG, ITU-T T.43
+>>>8 uleshort 0x7ffe \bNeXT RLE 2-bit
+>>>8 uleshort 0x8005 \bPackBits (Macintosh RLE)
+>>>8 uleshort 0x8029 \bThunderscan RLE
+>>>8 uleshort 0x807f \bRasterPadding (CT or MP)
+>>>8 uleshort 0x8080 \bRLE (Line Work)
+>>>8 uleshort 0x8081 \bRLE (High-Res Cont-Tone)
+>>>8 uleshort 0x8082 \bRLE (Binary Line Work)
+>>>8 uleshort 0x80b2 \bDeflate (PKZIP)
+>>>8 uleshort 0x80b3 \bKodak DCS
+>>>8 uleshort 0x8765 \bJBIG
+>>>8 uleshort 0x8798 \bJPEG2000
+>>>8 uleshort 0x8799 \bNikon NEF Compressed
+>>>8 default x
+>>>>8 uleshort x \b(unknown %#x)
+>>>12 use tiff_entry
+>0 uleshort 0x106 \b, PhotometricInterpretation=
+>>8 clear x
+>>8 uleshort 0 \bWhiteIsZero
+>>8 uleshort 1 \bBlackIsZero
+>>8 uleshort 2 \bRGB
+>>8 uleshort 3 \bRGB Palette
+>>8 uleshort 4 \bTransparency Mask
+>>8 uleshort 5 \bCMYK
+>>8 uleshort 6 \bYCbCr
+>>8 uleshort 8 \bCIELab
+>>8 default x
+>>>8 uleshort x \b(unknown=%#x)
+>>12 use tiff_entry
+# FillOrder
+>0 uleshort 0x10a
+>>4 ulelong 1
+>>>12 use tiff_entry
+# DocumentName
+>0 uleshort 0x10d
+>>(8.l) string x \b, name=%s
+>>>12 use tiff_entry
+# ImageDescription
+>0 uleshort 0x10e
+>>(8.l) string x \b, description=%s
+>>>12 use tiff_entry
+# Make
+>0 uleshort 0x10f
+>>(8.l) string x \b, manufacturer=%s
+>>>12 use tiff_entry
+# Model
+>0 uleshort 0x110
+>>(8.l) string x \b, model=%s
+>>>12 use tiff_entry
+# StripOffsets
+>0 uleshort 0x111
+>>12 use tiff_entry
+# Orientation
+>0 uleshort 0x112 \b, orientation=
+>>8 uleshort 1 \bupper-left
+>>8 uleshort 3 \blower-right
+>>8 uleshort 6 \bupper-right
+>>8 uleshort 8 \blower-left
+>>8 uleshort 9 \bundefined
+>>8 default x
+>>>8 uleshort x \b[*%d*]
+>>12 use tiff_entry
+# XResolution
+>0 uleshort 0x11a
+>>8 ulelong x \b, xresolution=%d
+>>12 use tiff_entry
+# YResolution
+>0 uleshort 0x11b
+>>8 ulelong x \b, yresolution=%d
+>>12 use tiff_entry
+# ResolutionUnit
+>0 uleshort 0x128
+>>8 uleshort x \b, resolutionunit=%d
+>>12 use tiff_entry
+# Software
+>0 uleshort 0x131
+>>(8.l) string x \b, software=%s
+>>12 use tiff_entry
+# Datetime
+>0 uleshort 0x132
+>>(8.l) string x \b, datetime=%s
+>>12 use tiff_entry
+# HostComputer
+>0 uleshort 0x13c
+>>(8.l) string x \b, hostcomputer=%s
+>>12 use tiff_entry
+# WhitePoint
+>0 uleshort 0x13e
+>>12 use tiff_entry
+# PrimaryChromaticities
+>0 uleshort 0x13f
+>>12 use tiff_entry
+# YCbCrCoefficients
+>0 uleshort 0x211
+>>12 use tiff_entry
+# YCbCrPositioning
+>0 uleshort 0x213
+>>12 use tiff_entry
+# ReferenceBlackWhite
+>0 uleshort 0x214
+>>12 use tiff_entry
+# Copyright
+>0 uleshort 0x8298
+>>(8.l) string x \b, copyright=%s
+>>12 use tiff_entry
+# ExifOffset
+>0 uleshort 0x8769
+>>12 use tiff_entry
+# GPS IFD
+>0 uleshort 0x8825 \b, GPS-Data
+>>12 use tiff_entry
+
+#>0 uleshort x \b, unknown=%#x
+#>>12 use tiff_entry
+
+0 string MM\x00\x2b Big TIFF image data, big-endian
+!:mime image/tiff
+0 string II\x2b\x00 Big TIFF image data, little-endian
+!:mime image/tiff
+
+# PNG [Portable Network Graphics, or "PNG's Not GIF"] images
+# (Greg Roelofs, newt@uchicago.edu)
+# (Albert Cahalan, acahalan@cs.uml.edu)
+#
+# 137 P N G \r \n ^Z \n [4-byte length] I H D R [HEAD data] [HEAD crc] ...
+#
+
+# IHDR parser
+0 name png-ihdr
+>0 ubelong x \b, %d x
+>4 ubelong x %d,
+>8 ubyte x %d-bit
+>9 ubyte 0 grayscale,
+>9 ubyte 2 \b/color RGB,
+>9 ubyte 3 colormap,
+>9 ubyte 4 gray+alpha,
+>9 ubyte 6 \b/color RGBA,
+#>10 ubyte 0 deflate/32K,
+>12 ubyte 0 non-interlaced
+>12 ubyte 1 interlaced
+
+# Standard PNG image.
+0 string \x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0DIHDR PNG image data
+!:mime image/png
+!:ext png
+!:strength +10
+>16 use png-ihdr
+
+# Apple CgBI PNG image.
+0 string \x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x04CgBI
+>24 string \x00\x00\x00\x0DIHDR PNG image data (CgBI)
+!:mime image/png
+!:ext png
+!:strength +10
+>>32 use png-ihdr
+
+# possible GIF replacements; none yet released!
+# (Greg Roelofs, newt@uchicago.edu)
+#
+# GRR 950115: this was mine ("Zip GIF"):
+0 string GIF94z ZIF image (GIF+deflate alpha)
+!:mime image/x-unknown
+#
+# GRR 950115: this is Jeremy Wohl's Free Graphics Format (better):
+#
+0 string FGF95a FGF image (GIF+deflate beta)
+!:mime image/x-unknown
+#
+# GRR 950115: this is Thomas Boutell's Portable Bitmap Format proposal
+# (best; not yet implemented):
+#
+0 string PBF PBF image (deflate compression)
+!:mime image/x-unknown
+
+# GIF
+# Strength set up to beat 0x55AA DOS/MBR signature word lookups (+65)
+0 string GIF8 GIF image data
+!:strength +80
+!:mime image/gif
+!:apple 8BIMGIFf
+!:ext gif
+>4 string 7a \b, version 8%s,
+>4 string 9a \b, version 8%s,
+>6 uleshort >0 %d x
+>8 uleshort >0 %d
+#>10 ubyte &0x80 color mapped,
+#>10 ubyte&0x07 =0x00 2 colors
+#>10 ubyte&0x07 =0x01 4 colors
+#>10 ubyte&0x07 =0x02 8 colors
+#>10 ubyte&0x07 =0x03 16 colors
+#>10 ubyte&0x07 =0x04 32 colors
+#>10 ubyte&0x07 =0x05 64 colors
+#>10 ubyte&0x07 =0x06 128 colors
+#>10 ubyte&0x07 =0x07 256 colors
+
+# ITC (CMU WM) raster files. It is essentially a byte-reversed Sun raster,
+# 1 plane, no encoding.
+0 string \361\0\100\273 CMU window manager raster image data
+>4 ulelong >0 %d x
+>8 ulelong >0 %d,
+>12 ulelong >0 %d-bit
+
+# Magick Image File Format
+# URL: https://imagemagick.org/script/miff.php
+# Reference: http://fileformats.archiveteam.org/wiki/MIFF
+# Update: Joerg Jenderek
+# http://www.nationalarchives.gov.uk/pronom/fmt/930
+0 search/256/bc id=imagemagick
+# skip bad ASCII text by following new line~0x0A or space~0x20 character
+#>&0 ubyte x \b, next character %#x
+# called by TriD ImageMagick Machine independent File Format bitmap
+>&0 ubyte&0xD5 0 MIFF image data
+# https://reposcope.com/mimetype/image/miff
+#!:mime image/miff
+!:mime image/x-miff
+!:ext miff/mif
+# examples with standard file(1) magic
+#>>0 string =id=ImageMagick with standard magic
+# examples with unusual file(1) magic like
+>>0 string !id=ImageMagick starting with
+# start with comment (brace) like http://samples.fileformat.info/.../AQUARIUM.MIF
+>>>0 ubyte =0x7b comment
+# skip second character which is often a newline and show comment
+>>>>2 string x "%s"
+# does not start with comment, probably letters with other case like Id=ImageMagick
+# ImageMagick-7.0.9-2/Magick++/demo/smile_anim.miff
+>>>0 ubyte !0x7b
+>>>>0 string >\0 '%-.14s'
+# URL: https://imagemagick.org/
+# Reference: https://imagemagick.org/script/magick-vector-graphics.php
+# From: Joerg Jenderek
+# Note: all white-spaces between commands are ignored
+0 string push
+# skip some white spaces
+>5 search/3 graphic-context ImageMagick Vector Graphic
+# TODO: look for dangerous commands like CVE-2016-3715
+#!:mime text/plain
+!:mime image/x-mvg
+!:ext mvg
+
+# Artisan
+0 long 1123028772 Artisan image data
+>4 long 1 \b, rectangular 24-bit
+>4 long 2 \b, rectangular 8-bit with colormap
+>4 long 3 \b, rectangular 32-bit (24-bit with matte)
+
+# FIG (Facility for Interactive Generation of figures), an object-based format
+# URL: http://fileformats.archiveteam.org/wiki/Fig
+# https://en.wikipedia.org/wiki/Xfig
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/f/fig.trid.xml
+# https://web.archive.org/web/20070920204655/http://epb.lbl.gov/xfig/fig-format.html
+# Update: Joerg Jenderek
+# Note: called "FIG vector drawing" by TrID,
+# 4 byte magic is assumed to be always at offset 0 and
+# verified by `fig2mpdf -v bootloader.fig && file bootloader.pdf`
+#0 search/1/tb #FIG FIG image text
+# GRR: with --keep-going option the line above gives duplicate messages
+0 search/1/ts #FIG
+>&0 use image-xfig
+# binary data variant with non ASCII text characters like Control-A or °C in thermostat.fig
+0 search/1/bs #FIG
+>&0 use image-xfig
+# display XFIG image describing text, mime type, file name extension and version
+0 name image-xfig
+>8 ubyte x FIG image text
+#!:mime text/plain
+# https://reposcope.com/mimetype/image/x-xfig
+!:mime image/x-xfig
+!:ext fig
+# version string like: 1.4 2.1 3.1 3.2
+>5 string x \b, version %.3s
+# some times after version text like: "Produced by xfig version 3.2.5-alpha5"
+>8 ubyte >0x0D
+>>8 string x "%s"
+# should be point character (2Eh) of version string according to TrID
+#>6 ubyte !0x2E \b, at 6 %#x
+# caret character (23h) at the beginning in most or probably all examples
+#>0 ubyte !0x23 \b, starting with character %#x
+# URL: http://fileformats.archiveteam.org/wiki/DeskMate_Draw
+# http://en.wikipedia.org/wiki/Deskmate
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dm-fig.trid.xml
+# From: Joerg Jenderek
+# Note: called "DeskMate Draw drawing" by TrID
+0 string \x14FIG DeskMate Drawing
+#!:mime application/octet-stream
+!:mime image/x-deskmate-fig
+!:ext fig
+# TODO:
+# "Cabri 3D Figure" by TrID fig-cabri.trid.xml
+# "Playmation Figure" by TrID fig-playmation.trid.xml
+
+# PHIGS
+0 string ARF_BEGARF PHIGS clear text archive
+0 string @(#)SunPHIGS SunPHIGS
+# version number follows, in the form m.n
+>40 string SunBin binary
+>32 string archive archive
+
+# GKS (Graphics Kernel System)
+0 string GKSM GKS Metafile
+>24 string SunGKS \b, SunGKS
+
+# CGM image files
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/CGM
+# https://en.wikipedia.org/wiki/Computer_Graphics_Metafile
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/cgm-ct.trid.xml
+# http://standards.iso.org/ittf/PubliclyAvailableStandards/c032381_ISO_IEC_8632-4_1999(E).zip
+# Note: called "Computer Graphics Metafile (Clear Text)" by TrID and
+# "Computer Graphics Metafile ASCII" by DROID or CGM by XnView
+# verified by LibreOffice and partly by XnView `nconvert -info *.CGM`
+# According to TrID only letter B and M are always upcased and by DROID often only B is upcased for command BEGIN METAFILE
+0 string/c begmf
+# skip SOME DROID fmt-301-signature-id-359.cgm fmt-301-signature-id-361.cgm fmt-302-signature-id-364.cgm
+# fmt-302-signature-id-365.cgm x-fmt-142-signature-id-350.cgm x-fmt-142-signature-id-351.cgm
+>5 short !0
+# skip other versions of DROID fmt-301-signature-id-359.cgm fmt-301-signature-id-361.cgm fmt-302-signature-id-364.cgm
+# fmt-302-signature-id-365.cgm x-fmt-142-signature-id-350.cgm x-fmt-142-signature-id-351.cgm
+>>5 short !0xABab clear text Computer Graphics Metafile
+# https://reposcope.com/mimetype/image/cgm
+!:mime image/cgm
+!:ext cgm
+# SF:NAME like: 'metafile example';
+>>>5 string x %s
+# look for command METAFILE VERSION (MFVERSION <SOFTSEP> <I:VERSION>)
+>>>2 search/128/c mfversion
+#>>>>&0 ubyte x SOFTSEP=%#x
+# version like: 1 3 4
+>>>>&1 ubyte >0x31 \b, version %c
+# Summary: Computer Graphics Metafile (binary)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/cgm-bin.trid.xml
+# https://standards.iso.org/ittf/PubliclyAvailableStandards/c032380_ISO_IEC_8632-3_1999(E).zip
+# Note: called "Computer Graphics Metafile (binary)" by TrID and DROID or CGM by XnView
+# verified by LibreOffice and partly by XnView `nconvert -info *.CGM`
+# look for BEGIN METAFILE (element Class 0 and ID 1 and "random" Parameter) that is binary C C C C 0 0 0 0 0 0 1 P P P P P
+0 ubeshort&0xFFe0 0x0020
+# skip SOME DROID fmt-303-signature-id-368.cgm fmt-304-signature-id-369.cgm fmt-305-signature-id-370.cgm fmt-306-signature-id-371.cgm
+# with containing only 28 bytes
+>28 ubyte x
+# look for METAFILE VERSION (element class 1 and id 1 and parameter P1 with length 2) that is binary 0 0 0 1 i i i i i i 1 P P P 1 P
+# with "low" version; 2nd worst case argentin.cgm with parameter length 56
+# worst MS.CGM
+#>>2 search/73/b \x10\x22\0 binary Computer Graphics Metafile
+>>2 search/128/b \x10\x22\0 binary Computer Graphics Metafile
+!:mime image/cgm
+!:ext cgm
+# metafile 2 byte version number like: 1 (most) 2 3 4
+>>>&-1 ubeshort >1 \b, version %u
+# length number of 1st parameter octets in range 0 to 30 implies short command
+>>>0 ubeshort&0x001F <31 \b, parameter length %u
+# length of string like: 8 9 10 11 12 29
+#>>>>2 ubyte x \b, %u BYTES (SHORT)
+# string like: 'HiJaak 2' 'Example 1' 'sahara.cgm' 'MASTERCLIPS--Art Of Business '
+>>>>2 pstring >\0 '%s'
+# after 1st short command with even parameter length comes 2nd command like: 1022h 0010h (EAF00010.CGM 'HiJaak 2' FLOPPY2.CGM TIGER.CGM 'B:\TIGER.CGM')
+>>>>0 ubeshort&0x0001 =0
+>>>>>(2.b+3) ubeshort !0x1022 \b, 2nd command %#4.4x (short even)
+# after 1st short command with odd parameter length comes nil padding byte followed 2nd command like: 1022h
+>>>>0 ubeshort&0x0001 =1
+#>>>>>(2.b+3) ubyte !0 \b, PADDING %#x
+>>>>>(2.b+4) ubeshort !0x1022 \b, 2nd command %#4.4x (short odd)
+# 11111 binary (decimal 31) in the parameter field indicates that the command is in long-form
+>>>0 ubeshort&0x001F =0x1F
+# bit 15 is partition flag with 1 for 'not-last' partition and 0 for 'last' partition
+>>>>2 ubeshort&0x8000 !0 \b, partition flag %#4.4x
+# bits 0 to 14 is parameter list length; the number of following parameter octets; range 0 to 32767
+# length of 1st long command parameter like: 53
+>>>>2 ubeshort&0x7Fff x \b, parameter length %u (long)
+# The two header words are then followed by lenghth of 1st string like: 52
+#>>>>4 ubyte x \b, %u BYTES
+# string like: 'K:\PROJECTS\GRAPHICS\DWKS3.5\CLIPART\FLAGS\Italy.cgm'
+>>>>4 pstring/B x '%s'
+# odd long parameter length implies single null padding octet to start command on word boundary
+>>>>2 ubeshort&0x0001 =1
+# after 1st long command with odd parameter length comes nil padding byte followed by 2nd command like: 1022h
+#>>>>>(4.b+5) ubyte !0 \b, PADDING %#x
+>>>>>(4.b+6) ubeshort !0x1022 \b, 2nd command %#4.4x (long odd)
+# even long parameter length implies next command directly is following
+>>>>2 ubeshort&0x0001 =0
+# after 1st long command with even parameter length comes 2nd command like: 1022h 0x1054 (MS.CGM)
+>>>>>(4.b+5) ubeshort !0x1022 \b, 2nd command %#4.4x (long even)
+# look for END METAFILE (element class 0 and id 2 and 0 parameter) that is binary 0 0 0 0 i i i i i 1 i P P P P P
+>>>-2 ubeshort !0x0040 \b, NOT_FOUND_END_METAFILE
+
+# MGR bitmaps (Michael Haardt, u31b3hs@pool.informatik.rwth-aachen.de)
+0 string yz MGR bitmap, modern format, 8-bit aligned
+0 string zz MGR bitmap, old format, 1-bit deep, 16-bit aligned
+0 string xz MGR bitmap, old format, 1-bit deep, 32-bit aligned
+0 string yx MGR bitmap, modern format, squeezed
+
+# Fuzzy Bitmap (FBM) images
+0 string %bitmap\0 FBM image data
+>30 long 0x31 \b, mono
+>30 long 0x33 \b, color
+
+# facsimile data
+1 string PC\ Research,\ Inc group 3 fax data
+>29 ubyte 0 \b, normal resolution (204x98 DPI)
+>29 ubyte 1 \b, fine resolution (204x196 DPI)
+# From: Herbert Rosmanith <herp@wildsau.idv.uni.linz.at>
+0 string Sfff structured fax file
+
+# From: Joerg Jenderek <joerg.jen.der.ek@gmx.net>
+# URL: http://fileformats.archiveteam.org/wiki/Award_BIOS_logo
+# Note: verified by XnView command `nconvert -fullinfo *.EPA`
+0 string \x11\x06 Award BIOS Logo, 136 x 84
+!:mime image/x-award-bioslogo
+!:ext epa
+0 string \x11\x09 Award BIOS Logo, 136 x 126
+!:mime image/x-award-bioslogo
+!:ext epa
+# https://telparia.com/fileFormatSamples/image/epa/IO.EPA
+# Note: by bitmap-awbm-v1x1009.trid.xml called "Award BIOS logo bitmap (128x126) (v1)"
+# verified by RECOIL `recoil2png -o tmp.png IO.EPA; file tmp.png`
+0 string \x10\x09 Award BIOS Logo, 128 x 126
+!:mime image/x-award-bioslogo
+!:ext epa
+#0 string \x07\x1f BIOS Logo corrupted?
+# http://www.blackfiveservices.co.uk/awbmtools.shtml
+# http://biosgfx.narod.ru/v3/
+# http://biosgfx.narod.ru/abr-2/
+0 string AWBM
+# Note: by bitmap-awbm.trid.xml called "Award BIOS logo bitmap (v2)"
+>4 uleshort <1981 Award BIOS Logo, version 2
+#>4 uleshort <1981 Award BIOS bitmap
+!:mime image/x-award-bioslogo2
+#!:mime image/x-award-bmp
+!:ext epa/bmp
+# image width is a multiple of 4
+>>4 uleshort&0x0003 0
+>>>4 uleshort x \b, %d
+>>>6 uleshort x x %d
+>>4 uleshort&0x0003 >0 \b,
+>>>4 uleshort&0x0003 =1
+>>>>4 uleshort x %d+3
+>>>4 uleshort&0x0003 =2
+>>>>4 uleshort x %d+2
+>>>4 uleshort&0x0003 =3
+>>>>4 uleshort x %d+1
+>>>6 uleshort x x %d
+# at offset 8 starts imagedata followed by "RGB " marker
+
+# PC bitmaps (OS/2, Windows BMP files) (Greg Roelofs, newt@uchicago.edu)
+# https://en.wikipedia.org/wiki/BMP_file_format#DIB_header_.\
+# 28bitmap_information_header.29
+# Note: variant starting direct with DIB header see
+# http://fileformats.archiveteam.org/wiki/BMP
+# verified by ImageMagick version 6.8.9-8 command `identify *.dib`
+0 uleshort 40
+# skip bad samples like GAME by looking for valid number of color planes
+>12 uleshort 1 Device independent bitmap graphic
+!:mime image/x-ms-bmp
+!:apple ????BMPp
+!:ext dib
+>>4 ulelong x \b, %d x
+>>8 ulelong x %d x
+>>14 uleshort x %d
+# number of color planes (must be 1)
+#>>12 uleshort >1 \b, %u color planes
+# compression method: 0~no 1~RLE 8-bit/pixel 3~Huffman 1D
+#>>16 ulelong 3 \b, Huffman 1D compression
+>>16 ulelong >0 \b, %u compression
+# image size is the size of raw bitmap; a dummy 0 can be given for BI_RGB bitmaps
+>>20 ulelong x \b, image size %u
+# horizontal and vertical resolution of the image (pixel per metre, signed integer)
+>>24 ulelong >0 \b, resolution %d x
+>>>28 ulelong x %d px/m
+# number of colors in palette, or 0 to default to 2**n
+#>>32 ulelong >0 \b, %u colors
+# number of important colors used, or 0 when every color is important
+>>36 ulelong >0 \b, %u important colors
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/VBM_(VDC_BitMap)
+# Reference: http://csbruce.com/cbm/postings/csc19950906-1.txt
+# http://mark0.net/download/triddefs_xml.7z
+# defs/b/bitmap-vbm.trid.xml
+# defs/b/bitmap-vbm-v3.trid.xml
+# Note: called "VDC BitMap" by TrID
+# verified by RECOIL `recoil2png -o tmp.png coke_can.vbm; file tmp.png`
+# begin with a signature of 'B' 'M' 0xCB, followed by a version byte 2 or 3
+# Similar to the unrelated Windows BMP format
+# check for VDC bitmap and then display image dimension and version
+0 name bitmap-vbm
+>2 ubyte 0xCB VDC bitmap
+!:mime image/x-commodore-vbm
+# http://recoil.sourceforge.net/formats.html
+!:ext bm/vbm
+# the VBM format version number: 2 or 3
+>>3 ubyte x \b, version %u
+# width of the image in Hi/Lo format
+>>4 ubeshort x \b, %u
+# height of the image
+>>6 ubeshort x x %u
+# version 3 images have the following additional header information
+>>3 ubyte =3
+# data-encoding type: 0~uncompressed 1~RLE-compressed
+>>>8 ubyte 0 \b, uncompressed
+>>>8 ubyte 1 \b, RLE-compressed
+# byte code for general RLE repetitions
+#>>>9 ubyte x \b, RLE repetition code 0x%x
+# reserved := 0
+#>>>14 short >0 \b, reserved 0x%x
+# length of comment text; 0~no comment text
+#>>>16 ubeshort >0 \b, comment length %u
+>>>16 pstring/H >0 \b, comment "%s"
+#
+0 string BM
+# check for magic and version 2 of VDC bitmap or BMP with cbSize=715=CB02
+>2 ubeshort 0xCB02
+>>6 short =0
+>>>0 use bitmap-bmp
+# VDC bitmap height or maybe a few OS/2 BMP with nonzero "hotspot coordinates"
+>>6 short !0
+>>>0 use bitmap-vbm
+# check for magic and version 3 of VDC bitmap or BMP with cbSize=971=CB03
+>2 ubeshort 0xCB03
+# check for reserved value (=0) of VDC bitmap
+>>14 short =0
+>>>0 use bitmap-vbm
+# BMP with cbSize=????03CBh and dib header size != 0
+>>14 short !0
+>>>0 use bitmap-bmp
+# cbSize is size of header or file size of Windows BMP bitmap
+>2 default x
+>>0 use bitmap-bmp
+0 name bitmap-bmp
+>14 ulelong 12 PC bitmap, OS/2 1.x format
+!:mime image/bmp
+!:ext bmp
+>>18 uleshort x \b, %d x
+>>20 uleshort x %d
+# number of color planes (must be 1)
+#>>22 uleshort !1 \b, %u color planes
+# number of bits per pixel (color depth); found 4 8
+>>24 uleshort x x %d
+# x, y coordinates of the hotspot
+>>6 uleshort >0 \b, hotspot %ux
+>>>8 uleshort x \b%u
+# cbSize; size of file or header like 1Ah 228C8h
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize 0x%8.8x
+# offBits; offset to bitmap data like:
+>>10 ulelong x \b, bits offset %u
+# http://fileformats.archiveteam.org/wiki/BMP#OS.2F2_BMP_2.0 no examples found
+>14 ulelong 48 PC bitmap, OS/2 2.x format (DIB header size=48)
+>14 ulelong 24 PC bitmap, OS/2 2.x format (DIB header size=24)
+# http://entropymine.com/jason/bmpsuite/bmpsuite/q/pal8os2v2-16.bmp
+# Note: by bitmap-bmp-v2o.trid.xml called "Windows Bitmap (v2o)"
+>14 ulelong 16 PC bitmap, OS/2 2.x format (DIB header size=16)
+!:mime image/bmp
+!:apple ????BMPp
+!:ext bmp
+# image width and height fields are unsigned integers for OS/2
+>>18 ulelong x \b, %u x
+>>22 ulelong x %u
+# number of bits per pixel (color depth); found 8
+>>28 uleshort >1 x %u
+# x, y coordinates of the hotspot
+>>6 uleshort >0 \b, hotspot %ux
+>>>8 uleshort x \b%u
+# number of color planes (must be 1)
+#>>26 uleshort >1 \b, %u color planes
+# cbSize; size of file like: 241E
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize 0x%x
+# offBits; offset to bitmap data like: 41E
+>>10 ulelong x \b, bits offset %u
+#>>10 ulelong x \b, bits offset 0x%x
+>14 ulelong 64 PC bitmap, OS/2 2.x format
+!:mime image/bmp
+!:apple ????BMPp
+!:ext bmp
+# image width and height fields are unsigned integers for OS/2
+>>18 ulelong x \b, %u x
+>>22 ulelong x %u
+# number of bits per pixel (color depth); found 1 4 8
+>>28 uleshort >1 x %u
+# x, y coordinates of the hotspot
+>>6 uleshort >0 \b, hotspot %ux
+>>>8 uleshort x \b%u
+>>26 uleshort >1 \b, %u color planes
+# cbSize; size of file or headers
+>>2 ulelong x \b, cbSize %u
+# BMP with cbSize 000002CBh=715 or 000003CBh=971 maybe misinterpreted as VDC bitmap
+#>>2 ulelong x \b, cbSize %#x
+# offBits; offset to bitmap data like 56h 5Eh 8Eh 43Eh
+>>10 ulelong x \b, bits offset %u
+#>>10 ulelong x \b, bits offset %#x
+#>>(10.l) ubequad !0 \b, bits %#16.16llx
+# BITMAPV2INFOHEADER adds RGB bit masks
+>14 ulelong 52 PC bitmap, Adobe Photoshop
+!:mime image/bmp
+!:apple ????BMPp
+!:ext bmp
+>>18 ulelong x \b, %d x
+>>22 ulelong x %d x
+# number of bits per pixel (color depth); found 16 32
+>>28 uleshort x %d
+# x, y coordinates of the hotspot; should be zero for Windows variant
+>>6 uleshort >0 \b, hotspot %ux
+>>>8 uleshort x \b%u
+# cbSize; size of file like: 14A 7F42
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize 0x%x
+# offBits; offset to bitmap data like: 42h
+>>10 ulelong x \b, bits offset %u
+#>>10 ulelong x \b, bits offset 0x%x
+# BITMAPV3INFOHEADER adds alpha channel bit mask
+>14 ulelong 56 PC bitmap, Adobe Photoshop with alpha channel mask
+!:mime image/bmp
+!:apple ????BMPp
+!:ext bmp
+>>18 ulelong x \b, %d x
+>>22 ulelong x %d x
+# number of bits per pixel (color depth); found 16 32
+>>28 uleshort x %d
+# x, y coordinates of the hotspot; should be zero for Windows variant
+>>6 uleshort >0 \b, hotspot %ux
+>>>8 uleshort x \b%u
+# cbSize; size of file like: 4E 7F46 131DE 14046h
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize 0x%x
+# offBits; offset to bitmap data like: 46h
+>>10 ulelong x \b, bits offset %u
+#>>10 ulelong x \b, bits offset 0x%x
+>14 ulelong 40
+# jump 4 bytes before end of file/header to skip fmt-116-signature-id-118.dib
+# broken for large bitmaps
+#>>(2.l-4) ulong x PC bitmap, Windows 3.x format
+>>14 ulelong 40 PC bitmap, Windows 3.x format
+!:mime image/bmp
+!:apple ????BMPp
+>>>18 ulelong x \b, %d x
+>>>22 ulelong x %d
+# 320 x 400 https://en.wikipedia.org/wiki/LOGO.SYS
+>>>18 ulequad =0x0000019000000140 x
+!:ext bmp/sys
+>>>18 ulequad !0x0000019000000140
+# compression method 2~RLE 4-bit/pixel implies also extension rle
+>>>>30 ulelong 2 x
+!:ext bmp/rle
+# not RLE compressed and not 320x400 dimension
+>>>>30 default x
+# "small" dimensions like: 14x15 15x16 16x14 16x16 32x32
+# https://en.wikipedia.org/wiki/Favicon
+>>>>>18 ulequad&0xffFFffC0ffFFffC0 =0 x
+# https://www.politi-kdigital.de/favicon.ico
+# http://forum.rpc1.org/favicon.ico
+!:ext bmp/ico
+# "big" dimensions > 63
+>>>>>18 default x x
+!:ext bmp
+# number of bits per pixel (color depth); found 1 2 4 8 16 24 32
+>>>28 uleshort x %d
+# x, y coordinates of the hotspot; there is no hotspot in bitmaps, so values 0
+#>>>6 uleshort >0 \b, hotspot %ux
+#>>>>8 uleshort x \b%u
+# number of color planes (must be 1), except badplanes.bmp for testing
+#>>>26 uleshort >1 \b, %u color planes
+# compression method: 0~no 1~RLE 8-bit/pixel 2~RLE 4-bit/pixel 3~Huffman 1D 6~RGBA bit field masks
+#>>>30 ulelong 3 \b, Huffman 1D compression
+>>>30 ulelong >0 \b, %u compression
+# image size is the size of raw bitmap; a dummy 0 can be given for BI_RGB bitmaps
+>>>34 ulelong >0 \b, image size %u
+# horizontal and vertical resolution of the image (pixel per metre, signed integer)
+>>>38 ulelong >0 \b, resolution %d x
+>>>>42 ulelong x %d px/m
+# number of colors in palette 16 256, or 0 to default to 2**n
+#>>>46 ulelong >0 \b, %u colors
+# number of important colors used, or 0 when every color is important
+>>>50 ulelong >0 \b, %u important colors
+# cbSize; often size of file
+>>>2 ulelong x \b, cbSize %u
+#>>>2 ulelong x \b, cbSize %#x
+# offBits; offset to bitmap data like 36h 76h BEh 236h 406h 436h 4E6h
+>>>10 ulelong x \b, bits offset %u
+#>>>10 ulelong x \b, bits offset %#x
+#>>>(10.l) ubequad !0 \b, bits %#16.16llxd
+>14 ulelong 124 PC bitmap, Windows 98/2000 and newer format
+!:mime image/bmp
+!:ext bmp
+>>18 ulelong x \b, %d x
+>>22 ulelong x %d x
+# color planes; must be 1
+#>>>26 uleshort >1 \b, %u color planes
+# number of bits per pixel (color depth); found 4 8 16 24 32 1 (fmt-119-signature-id-121.bmp) 0 (rgb24jpeg.bmp rgb24png.bmp)
+>>28 uleshort x %d
+# x, y coordinates of the hotspot; should be zero for Windows variant
+>>6 uleshort >0 \b, hotspot %ux
+>>>8 uleshort x \b%u
+# cbSize; size of file like: 8E AA 48A 999 247A 4F02 7F8A 3F88E B216E 1D4C8A 100008A
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize 0x%x
+# offBits; offset to bitmap data like: 8A 47A ABABABAB (fmt-119-signature-id-121.bmp)
+>>10 ulelong x \b, bits offset %u
+#>>10 ulelong x \b, bits offset 0x%x
+>14 ulelong 108 PC bitmap, Windows 95/NT4 and newer format
+!:mime image/bmp
+!:ext bmp
+>>18 ulelong x \b, %d x
+>>22 ulelong x %d x
+# number of bits per pixel (color depth); found 8 24 32
+>>28 uleshort x %d
+# x, y coordinates of the hotspot; should be zero for Windows variant
+>>6 uleshort >0 \b, hotspot %ux
+>>>8 uleshort x \b%u
+# cbSize; size of file like: 82 8A 9A 9F86 1E07A 3007A 88B7A C007A
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize 0x%x
+# offBits; offset to bitmap data like: 7A 7E 46A
+>>10 ulelong x \b, bits offset %u
+#>>10 ulelong x \b, bits offset 0x%x
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/OS/2_Icon
+# Reference: http://www.fileformat.info
+# /format/os2bmp/spec/902d5c253f2a43ada39c2b81034f27fd/view.htm
+# Note: verified by command like `deark -l -d3 OS2MEMU.ICO`
+0 string IC
+# skip Lotus smart icon *.smi by looking for valid hotspot coordinates
+>6 ulelong&0xFF00FF00 =0 OS/2 icon
+# jump 4 bytes before end of header/file and test for accessibility
+#>>(2.l-4) ubelong x End of header is OK!
+!:mime image/x-os2-ico
+!:ext ico
+# cbSize; size of header or file in bytes like 1ah 120h 420h
+>>2 ulelong x \b, cbSize %u
+# xHotspot, yHotspot; coordinates of the hotspot for icons like 16 32
+>>6 uleshort x \b, hotspot %ux
+>>8 uleshort x \b%u
+# offBits; offset in bytes to the beginning of the bit-map pel data like 20h
+>>10 ulelong x \b, bits offset %u
+#>>(10.l) ubequad x \b, bits %#16.16llx
+#0 string PI PC pointer image data
+#0 string CI PC color icon data
+0 string CI
+# test also for valid dib header sizes 12 or 64
+>14 ulelong <65 OS/2
+# test also for valid hotspot coordinates
+#>>6 ulelong&0xFE00FE00 =0 OS/2
+!:mime image/x-os2-ico
+!:ext ico
+>>14 ulelong 12 1.x color icon
+# image width and height fields are unsigned integers for OS/2
+>>>18 uleshort x %u x
+# stored height = 2 * real height
+>>>20 uleshort/2 x %u
+# number of bits per pixel (color depth). Typical 32 24 16 8 4 but only 1 found
+>>>24 uleshort >1 x %u
+# color planes; must be 1
+#>>>22 uleshort >1 \b, %u color planes
+>>14 ulelong 64 2.x color icon
+# image width and height
+>>>18 ulelong x %u x
+# stored height = 2 * real height
+>>>22 ulelong/2 x %u
+# number of bits per pixel (color depth). only 1 found
+>>>28 uleshort >1 x %u
+#>>>26 uleshort >1 \b, %u color planes
+# compression method: 0~no 3~Huffman 1D
+>>>30 ulelong 3 \b, Huffman 1D compression
+#>>>30 ulelong >0 \b, %u compression
+# xHotspot, yHotspot; coordinates of the hotspot like 0 1 16 20 32 33 63 64
+>>6 uleshort x \b, hotspot %ux
+>>8 uleshort x \b%u
+# cbSize; size of header or maybe file in bytes like 1Ah 4Eh 84Eh
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize %x
+# offBits; offset to bitmap data (pixel array) like E4h 3Ah 66h 6Ah 33Ah 4A4h
+>>10 ulelong x \b, bits offset %u
+#>>10 ulelong x \b, bits offset %#x
+#>>(10.l) ubequad !0 \b, bits %#16.16llx
+# dib header size: 12~Ch~OS/2 1.x 64~40h~OS/2 2.x
+#>>14 ulelong x \b, dib header size %u
+#0 string CP PC color pointer image data
+# URL: http://fileformats.archiveteam.org/wiki/OS/2_Pointer
+# Reference: http://www.fileformat.info/format/os2bmp/egff.htm
+0 string CP
+# skip many Corel Photo-Paint image "CPT9FILE" by checking for positive bits offset
+>10 ulelong >0
+# skip CPU-Z Report by checking for valid dib header sizes 12 or 64
+>>14 ulelong =12
+>>>0 use os2-ptr
+>>14 ulelong =64
+>>>0 use os2-ptr
+# display information of OS/2 pointer bitmaps
+0 name os2-ptr
+>14 ulelong x OS/2
+# http://extension.nirsoft.net/PTR
+!:mime image/x-ibm-pointer
+!:ext ptr
+>>14 ulelong 12 1.x color pointer
+# image width and height fields are unsigned integers for OS/2
+>>>18 uleshort x %u x
+# stored height = 2 * real height
+>>>20 uleshort/2 x %u
+# number of bits per pixel (color depth). Typical 32 24 16 8 4 but only 1 found
+>>>24 uleshort >1 x %u
+# color planes; must be 1
+#>>>22 uleshort >1 \b, %u color planes
+>>14 ulelong 64 2.x color pointer
+# image width and height
+>>>18 ulelong x %u x
+# stored height = 2 * real height
+>>>22 ulelong/2 x %u
+# number of bits per pixel (color depth). only 1 found
+>>>28 uleshort >1 x %u
+#>>>26 uleshort >1 \b, %u color planes
+# compression method: 0~no 3~Huffman 1D
+>>>30 ulelong 3 \b, Huffman 1D compression
+#>>>30 ulelong >0 \b, %u compression
+# xHotspot, yHotspot; coordinates of the hotspot like 0 3 4 8 15 16 23 27 31
+>>6 uleshort x \b, hotspot %ux
+>>8 uleshort x \b%u
+# cbSize; size of header or maybe file in bytes like 1Ah 4Eh
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize %x
+# offBits; offset to bitmap data (pixel array) like 6Ah A4h E4h 4A4h
+>>10 ulelong x \b, bits offset %u
+#>>10 ulelong x \b, bits offset %#x
+#>>(10.l) ubequad !0 \b, bits %#16.16llx
+# dib header size: 12~Ch~OS/2 1.x 64~40h~OS/2 2.x
+#>>14 ulelong x \b, dib header size %u
+# Conflicts with other entries [BABYL]
+# URL: http://fileformats.archiveteam.org/wiki/BMP#OS.2F2_Bitmap_Array
+# Note: container for OS/2 icon "IC", color icon "CI", color pointer "CP" or bitmap "BM"
+#0 string BA PC bitmap array data
+0 string BA
+# skip old Emacs RMAIL BABYL ./mail.news by checking for low header size
+>2 ulelong <0x004c5942 OS/2 graphic array
+!:mime image/x-os2-graphics
+#!:apple ????BMPf
+# cbSize; size of header like 28h 5Ch
+>>2 ulelong x \b, cbSize %u
+#>>2 ulelong x \b, cbSize %#x
+# offNext; offset to data like 0 48h F2h 4Eh 64h C6h D2h D6h DAh E6h EAh 348h
+>>6 ulelong >0 \b, data offset %u
+#>>6 ulelong >0 \b, data offset %#x
+#>>(6.l) ubequad !0 \b, data %#16.16llx
+# dimensions of the intended device like 640 x 480 for VGA or 1024 x 768
+>>10 uleshort >0 \b, display %u
+>>>12 uleshort >0 x %u
+# usType of first array element
+#>>14 string x \b, usType %2.2s
+# 1 space char after "1st"
+# no *.bga examples found https://www.openwith.org/file-extensions/bga/1342
+>>14 string BM \b; 1st
+!:ext bmp/bga
+>>14 string CI \b; 1st
+!:ext ico
+>>14 string CP \b; 1st
+!:ext ico
+>>14 string IC \b; 1st
+!:ext ico
+# no white-black pointer found
+#>>14 string PT \b; 1st
+#!:ext
+>>14 indirect x
+
+# XPM icons (Greg Roelofs, newt@uchicago.edu)
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/XPM
+# Reference: http://www.x.org/docs/XPM/xpm.pdf
+# http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-xpm.trid.xml
+# Note: called "X PixMap bitmap" by TrID and "X-Windows Pixmap Image" by DROID via PUID x-fmt/208
+# starting with c comment like: logo.xpm
+0 string /*\040
+# 9 byte c-comment "/* XPM */" not at the beginning like: mozicon16.xpm mozicon50.xpm (thunderbird)
+>0 search/0xCE /*\ XPM\ */
+# skip DROID x-fmt-208-signature-id-620.xpm by looking for char array without explict length
+# and match mh-logo.xpm (emacs)
+>>&0 search/1249 []
+>>>0 use xpm-image
+# non standard because no 9 byte c-comment "/* XPM */" like: logo.xpm in qemu package
+>0 default x
+# words are separated by a white space which can be composed of space and tabulation characters
+>>0 search/0x52 static\040char\040
+# skip debug.c testmlc.c by looking for char array without explict length
+# https://www.clamav.net/downloads/production/clamav-0.104.2.tar.gz
+# clamav-0.104.2\libclammspack\mspack\debug.c
+>>>&0 search/64 []
+>>>>0 use xpm-image
+# display X pixmap image information
+0 name xpm-image
+>0 string x X pixmap image text
+#!:mime text/plain
+# https://reposcope.com/mimetype/image/x-xpixmap
+# alias
+#!:mime image/x-xpm
+!:mime image/x-xpixmap
+!:ext xpm
+# NO pm example found!
+#!:ext xpm/pm
+# look for start of character array at beginning of a line like: psetupl.xpm (OpenOffice 4.1.7)
+>0 search/0x406 \n"
+# DEBUG VALUES string
+#>>&0 string x '%s'
+# width with optional white space before like: 16 24 32 48 1280
+>>&0 regex/8 [0-9]{1,5} \b, %s
+# height with white space like: 15 16 17 24 32 48 1024
+>>>&0 regex/8 [0-9]{1,5} x %s
+# number of colors with white space like: 1 2 3 4 5 8 11 14 162 255 but unrelistic 4294967295 by hardcopy tool
+>>>>&0 regex/12 [0-9]{1,9} x %s
+# chars_per_pixel with white space like: 1 2
+>>>>>&0 regex/14 [0-9]{1,2} \b, %s chars/pixel
+# non standard because not starting with 9 byte c-comment "/* XPM */"
+>0 string !/*\ XPM\ */
+>>0 string x \b, 1st line "%s"
+
+# Utah Raster Toolkit RLE images (janl@ifi.uio.no)
+0 uleshort 0xcc52 RLE image data,
+>6 uleshort x %d x
+>8 uleshort x %d
+>2 uleshort >0 \b, lower left corner: %d
+>4 uleshort >0 \b, lower right corner: %d
+>10 ubyte&0x1 =0x1 \b, clear first
+>10 ubyte&0x2 =0x2 \b, no background
+>10 ubyte&0x4 =0x4 \b, alpha channel
+>10 ubyte&0x8 =0x8 \b, comment
+>11 ubyte >0 \b, %d color channels
+>12 ubyte >0 \b, %d bits per pixel
+>13 ubyte >0 \b, %d color map channels
+
+# image file format (Robert Potter, potter@cs.rochester.edu)
+0 string Imagefile\ version- iff image data
+# this adds the whole header (inc. version number), informative but longish
+>10 string >\0 %s
+
+# Sun raster images, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 ubelong 0x59a66a95 Sun raster image data
+>4 ubelong >0 \b, %d x
+>8 ubelong >0 %d,
+>12 ubelong >0 %d-bit,
+#>16 ubelong >0 %d bytes long,
+>20 ubelong 0 old format,
+#>20 ubelong 1 standard,
+>20 ubelong 2 compressed,
+>20 ubelong 3 RGB,
+>20 ubelong 4 TIFF,
+>20 ubelong 5 IFF,
+>20 ubelong 0xffff reserved for testing,
+>24 ubelong 0 no colormap
+>24 ubelong 1 RGB colormap
+>24 ubelong 2 raw colormap
+#>28 ubelong >0 colormap is %d bytes long
+
+# SGI image file format, from Daniel Quinlan (quinlan@yggdrasil.com)
+#
+# See
+# http://reality.sgi.com/grafica/sgiimage.html
+#
+0 ubeshort 474 SGI image data
+#>2 ubyte 0 \b, verbatim
+>2 ubyte 1 \b, RLE
+#>3 ubyte 1 \b, normal precision
+>3 ubyte 2 \b, high precision
+>4 ubeshort x \b, %d-D
+>6 ubeshort x \b, %d x
+>8 ubeshort x %d
+>10 ubeshort x \b, %d channel
+>10 ubeshort !1 \bs
+>80 string >0 \b, "%s"
+
+0 string IT01 FIT image data
+>4 ubelong x \b, %d x
+>8 ubelong x %d x
+>12 ubelong x %d
+#
+0 string IT02 FIT image data
+>4 ubelong x \b, %d x
+>8 ubelong x %d x
+>12 ubelong x %d
+#
+2048 string PCD_IPI Kodak Photo CD image pack file
+>0xe02 ubyte&0x03 0x00 , landscape mode
+>0xe02 ubyte&0x03 0x01 , portrait mode
+>0xe02 ubyte&0x03 0x02 , landscape mode
+>0xe02 ubyte&0x03 0x03 , portrait mode
+0 string PCD_OPA Kodak Photo CD overview pack file
+
+# FITS format. Jeff Uphoff <juphoff@tarsier.cv.nrao.edu>
+# FITS is the Flexible Image Transport System, the de facto standard for
+# data and image transfer, storage, etc., for the astronomical community.
+# (FITS floating point formats are big-endian.)
+0 string SIMPLE\ \ = FITS image data
+!:mime image/fits
+!:ext fits/fts
+>109 string 8 \b, 8-bit, character or unsigned binary integer
+>108 string 16 \b, 16-bit, two's complement binary integer
+>107 string \ 32 \b, 32-bit, two's complement binary integer
+>107 string -32 \b, 32-bit, floating point, single precision
+>107 string -64 \b, 64-bit, floating point, double precision
+
+# other images
+0 string This\ is\ a\ BitMap\ file Lisp Machine bit-array-file
+
+# From SunOS 5.5.1 "/etc/magic" - appeared right before Sun raster image
+# stuff.
+#
+0 ubeshort 0x1010 PEX Binary Archive
+
+# DICOM medical imaging data
+# URL: https://en.wikipedia.org/wiki/DICOM#Data_format
+# Note: "dcm" is the official file name extension
+# XnView mention also "dc3" and "acr" as file name extension
+128 string DICM DICOM medical imaging data
+!:mime application/dicom
+!:ext dcm/dicom/dic
+
+# XWD - X Window Dump file.
+# URL: http://fileformats.archiveteam.org/wiki/XWD
+# Reference: https://wiki.multimedia.cx/index.php?title=XWD
+# http://mark0.net/download/triddefs_xml.7z/defs/x/xdm-x11.trid.xml
+# Note: called "X-Windows Screen Dump (X11)" by TrID and
+# "X-Windows Screen Dump" version X11 by DROID via PUID fmt/483
+# verfied by XnView `nconvert -in xwd -info *`
+# and ImageMagick 6.9.11 `identify -verbose *` as XWD X Windows system window dump
+# and `xwud -in fig41.wxd -dumpheader`
+# As described in /usr/X11R6/include/X11/XWDFile.h
+# used by the xwd program.
+# Bradford Castalia, idaeim, 1/01
+# updated by Adam Buchbinder, 2/09 and Joerg Jenderek, May 2022
+# The following assumes version 7 of the format; the first long is the length
+# of the header, which is at least 25 4-byte longs, and the one at offset 8
+# is a constant which is always either 1 or 2. Offset 12 is the pixmap depth,
+# which is a maximum of 32.
+# Size of the entire file header (bytes) like: 100 104 105 106 107 109 110 113 114 115 118 172
+0 ubelong >99
+# pixmap_format; Pixmap format; 0~1-bit (XYBitmap) format 1~single-plane (XYPixmap) 2~bitmap with two or more planes (ZPixmap)
+>8 ubelong <3
+# pixmap_depth; Pixmap depth; value 1 - 32
+>>12 ubelong <33
+# file_version; XWD_FILE_VERSION=7
+>>>4 ubelong 7
+# skip DROID fmt-401-signature-id-618.xwd by test for existing border field
+>>>>96 ubelong x X-Window screen dump image data, version X11
+# ./images (version 1.205) labeled the above entry as "XWD X Window Dump image data"
+# https://reposcope.com/mimetype/image/x-xwindowdump
+!:mime image/x-xwindowdump
+#!:ext xwd
+!:ext xwd/dmp
+# https://www.xnview.com/en/image_formats/ NO example with x11 suffix FOUND!
+#!:ext xwd/dmp/x11
+# https://www.nationalarchives.gov.uk/PRONOM/fmt/401 NO example with xdm suffix FOUND!
+#!:ext xwd/dmp/x11/xmd
+# file comment if header > 100; so not in MARBLES.XWD and hardcopy-x-window-v11.xwd
+>>>>>0 ubelong >100
+# comment or windows name
+>>>>>>100 string >\0 \b, "%s"
+# pixmap_width; pixmap width like: 576 800 1014 1280 1419 NOT -1414812757=abABabABh
+>>>>>16 ubelong x \b, %dx
+# pixmap_height; pixmap height like: 449 454 600 704 720 1001 1024 NOT -1414812757=abABabABh
+>>>>>20 ubelong x \b%dx
+# pixmap_depth; pixmap depth
+>>>>>12 ubelong x \b%d
+# XOffset; Bitmap X offset; pixel numbers to ignore at the beginning of each scan-line
+#>>>>>24 ubelong x \b, %u ignore
+# ByteOrder; byte order of image data: 0~least significant byte first 1~most significant byte first
+>>>>>28 ubelong >0 \b, order %u
+# BitmapUnit; bitmap base data size unit in each scan line like: 8 16 32
+#>>>>>32 ubelong x \b, unit %u
+# BitmapBitOrder; bit-order of image data; apparently same as ByteOrder
+#>>>>>36 ubelong x \b, bit order %u
+# BitmapPad; number of padding bits added to each scan line like: 8 16 32
+#>>>>>40 ubelong x \b, pad %u
+# BitsPerPixel; Bits per pixel: 1~StaticGray and GrayScale 2-15~StaticColor and PseudoColor 16,24,32~TrueColor and DirectColor
+#>>>>>44 ubelong x \b, %u bits/pixel
+# BytesPerLine; size of each scan line in bytes
+#>>>>>48 ubelong x \b, %u bytes/line
+# VisualClass; class of the image: 0~StaticGray 1~GrayScale 2~StaticColor 3~PseudoColor 4~TrueColor 5~DirectColor
+#>>>>>52 ubelong x \b, %u Class
+# RedMask; red RGB mask values used by ZPixmaps like: 0 0xff0000
+#>>>>>56 ubelong !0 \b, %#x red
+# GreenMask; green mask like: 0
+#>>>>>60 ubelong !0 \b, %#x green
+# BlueMask; blue mask like: 0 0xff
+#>>>>>64 ubelong !0 \b, %#x blue
+# BitsPerRgb; Size of each color mask in bits like: 0 1 8 24
+#>>>>>68 ubelong x \b, %u bits/RGB
+# NumberOfColors; number of colors in image like: 256 4 2 0 (WHAT DOES THIS MEAN?)
+>>>>>72 ubelong x \b, %u colors
+# ColorMapEntries; number of entries in color map like: 256 16 2 0~no color map
+>>>>>76 ubelong x %u entries
+# WindowWidth; window width
+#>>>>>80 ubelong x \b, %u width
+# WindowHeight; window height
+#>>>>>84 ubelong x \b, %u height
+# WindowX; Window upper left X coordinate like: 0 24 32 80 237 290 422 466 568 (lenna.dmp)
+>>>>>88 ubelong !0 \b, x=%d
+# WindowY; Window upper left Y coordinate like: 0 8 18 26 60 73 107 (fig41.xwd) 128
+>>>>>92 ubelong !0 \b, y=%d
+# WindowBorderWidth; Window border width; apparently pixmap_width=WindowWidth+2*WindowBorderWidth
+# like: 1 (fig41.xwd) 2 (maze.dmp) 3 (lenna.dmp mandrill.dmp)
+>>>>>96 ubelong >0 \b, %u border
+# From: Joerg Jenderek
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/x/xdm-x10.trid.xml
+# Note: called "X-Windows Screen Dump (X10)" by TrID and
+# "X-Windows Screen Dump" version X10 by DROID via PUID x-fmt/300
+# verfied by XnView `nconvert -in xwd -info *`
+# HeaderSize is the size of the header in bytes; always 40 for X10 variant
+0 ubelong =0x000000028
+# FileVersion; always 6 for X10 variant
+>4 ubelong =6
+# skip DROID x-fmt-300-signature-id-619.xdm by test existing border field
+>>36 ubeshort x X-Window screen dump image data, version X10
+!:mime image/x-xwindowdump
+!:ext xwd
+# http://www.nationalarchives.gov.uk/pronom/fmt/401 NO example with xdm suffix FOUND!
+#!:ext xwd/xdm
+# PixmapWidth; pixmap width like: 127 1280
+>>>20 ubelong x \b, %d
+# PixmapHeight; pixmap height like: 64 1024
+>>>24 ubelong x \bx%d
+# DisplayPlanes; number of display planes like: 1 4 8
+>>>12 ubelong x \bx%u
+# DisplayType; display type like: 1 3
+#>>>8 ubelong x \b, type %u
+# PixmapFormat; pixmap format like: 1~bitmap with two or more planes (ZPixmap) 0~single-plane bitmap (XYBitmap)
+#>>>16 ubelong x \b, %u format
+# WindowWidth; window width; probably PixmapWidth=WindowWidth+2*WindowBorderWidth
+#>>>28 ubeshort x \b, width %u
+# WindowHeight; window height; probably PixmapWidth=PixmapHeight+2*WindowBorderWidth
+#>>>30 ubeshort x \b, height %u
+# WindowX; window upper left X coordinate like: 0
+>>>32 ubeshort !0 \b, x=%d
+# WindowY; window upper left Y coordinate like: 0
+>>>34 ubeshort !0 \b, y=%d
+# WindowBorderWidth; window border width like: 0
+>>>36 ubeshort !0 \b, %u border
+# WindowNumColors; Number of color entries in window like: 2 16 256
+#>>>38 ubeshort x \b, %u colors
+# if the image is a PseudoColor image, a color map immediately follows the header. X10COLORMAP[WindowNumColors];
+# EntryNumber; number of the color-map entry like: 0
+#>>>40 ubeshort x \b, colors #%u
+# Red; red-channel value
+#>>>42 ubeshort !0 \b, red %#x
+# Green; green-channel value
+#>>>44 ubeshort !0 \b, green %#x
+# Blue; blue-channel value
+#>>>46 ubeshort !0 \b, blue %#x
+# 2ND Entry like: 2
+#>>>48 ubeshort x \b, colors #%u
+
+# PDS - Planetary Data System
+# These files use Parameter Value Language in the header section.
+# Unfortunately, there is no certain magic, but the following
+# strings have been found to be most likely.
+0 string NJPL1I00 PDS (JPL) image data
+2 string NJPL1I PDS (JPL) image data
+0 string CCSD3ZF PDS (CCSD) image data
+2 string CCSD3Z PDS (CCSD) image data
+0 string PDS_ PDS image data
+0 string LBLSIZE= PDS (VICAR) image data
+
+# pM8x: ATARI STAD compressed bitmap format
+#
+# from Oskar Schirmer <schirmer@scara.com> Feb 2, 2001
+# p M 8 5/6 xx yy zz data...
+# Atari ST STAD bitmap is always 640x400, bytewise runlength compressed.
+# bytes either run horizontally (pM85) or vertically (pM86). yy is the
+# most frequent byte, xx and zz are runlength escape codes, where xx is
+# used for runs of yy.
+#
+0 string pM85 Atari ST STAD bitmap image data (hor)
+>5 ubyte 0x00 (white background)
+>5 ubyte 0xFF (black background)
+0 string pM86 Atari ST STAD bitmap image data (vert)
+>5 ubyte 0x00 (white background)
+>5 ubyte 0xFF (black background)
+
+# From: Alex Myczko <alex@aiei.ch>
+# https://www.atarimax.com/jindroush.atari.org/afmtatr.html
+0 uleshort 0x0296 Atari ATR image
+
+# URL: http://fileformats.archiveteam.org/wiki/DEGAS_image
+# Reference: https://wiki.multimedia.cx/index.php?title=Degas
+# From: Joerg Jenderek
+# http://mark0.net/download/triddefs_xml.7z/defs/b
+# bitmap-pi2-degas.trid.xml bitmap-pi3-degas.trid.xml
+# bitmap-pc1-degas.trid.xml bitmap-pc2-degas.trid.xml bitmap-pc3-degas.trid.xml
+# Note: verified by NetPBM `pi3topbm sigirl1.pi3 | file`
+# `deark -m degas -l -d2 ataribak.pi1`
+# XnView `nconvert -fullinfo *.p??`
+# DEGAS low-res uncompressed bitmap *.pi1
+0 beshort 0x0000
+# skip some ISO 9660 CD-ROM filesystems like plpbt.iso by test for 4 non black colors in palette entries
+>2 quad !0
+# skip g3test.g3 by test for unused bits of 2nd color entry
+>>4 ubeshort&0xF000 0
+#>>>0 beshort x 1ST_VALUE=%x
+#>>>-0 offset x FILE_SIZE=%lld
+# standard DEGAS low-res uncompressed bitmap *.pi1 with file size 32034
+>>>-0 offset =32034
+#>>>>0 beshort x 1st_VALUE=%x
+# like: 8ball.pi1 teddy.pi1 sonic01.pi1
+>>>>0 use degas-bitmap
+# about 61 DEGAS Elite low-res uncompressed bitmap *.pi1 with file size 32066
+>>>-0 offset =32066
+# like: spider.pi1 pinkgirl.pi1 frog3.pi1
+>>>>0 use degas-bitmap
+# about 55 DEGAS Elite low-res uncompressed bitmap *.pi1 with file size 32128
+>>>-0 offset =32128
+# like: mountain.pi1 bigspid.pi1 alf33.pi1
+>>>>0 use degas-bitmap
+# 1 DEGAS Elite low-res uncompressed bitmap *.pi1 with file size 44834
+>>>-0 offset =44834
+# like: kenshin.pi1
+>>>>0 use degas-bitmap
+# DEGAS mid-res uncompressed bitmap *.pi2 (strength=50) after GEM Images like:
+# BEETHVEN.IMG CHURCH.IMG GAMEOVR4.IMG TURKEY.IMG clinton.img
+0 beshort 0x0001
+#!:strength +0
+# skip many control files like gnucash-4.8.setup.exe.aria2 by test for non black in 4 palette entries
+>2 quad !0
+# skip control file load-v0001.aria2 and many GEM Image data like
+# GAMEOVR4.IMG BEETHVEN.IMG CHURCH.IMG TURKEY.IMG clinton.img
+# by test for valid file sizes
+# standard DEGAS mid-res uncompressed bitmap *.pi2 with file size 32034
+>>-0 offset =32034
+# (39/41) like: GEMINI03.PI2 ST_TOOLS.PI2 TBX_DEMO.PI2
+>>>0 use degas-bitmap
+# few DEGAS Elite mid-res uncompressed bitmap *.pi2 with file size 32066
+>>-0 offset =32066
+# (2/41) like: medres.pi2
+>>>0 use degas-bitmap
+# DEGAS high-res uncompressed bitmap *.pi3
+0 beshort 0x0002
+# skip Intel ia64 COFF msvcrt.lib by test for unused bits of 1st atari color palette entry
+>2 ubeshort&0xF000 0
+# skip few Adobe PhotoShop Brushes like Faux-Spitzen.abr by check
+# for invalid Adobe PhotoShop Brush UTF16-LE string length
+>>19 ubyte =0
+# many like: 4th_ofj2.pi3 GEMINI03.PI3 PEOPLE18.PI3 POWERFIX.PI3 abydos.pi3 highres.pi3 sigirl1.pi3 vanna5.pi3
+>>>0 use degas-bitmap
+# Adobe PhotoShop Brush UTF16-LE string length 15 "Gitter - klein " 8 "Kreis 1 "
+>>19 ubyte !0
+#>>19 ubyte !0 \b, NOTE LENGTH %u
+#>>>21 lestring16 x \b, BRUSH NOTE "%s"
+>>>(19.b*2) ubequad x
+# maybe last character of Adobe PhotoShop Brush UTF16-LE string and terminating nul char like
+# 006e0000 for n in "Faux-Spitzen.abr" 00310000 for 1 in "Verschiedene Spitzen.abr"
+# 00000000 "LEREDACT.PI3" 03730773 "TBX_DEMO.PI3"
+#>>>>&8 ubelong x \b, LAST CHAR+NIL %8.8x
+>>>>&8 ubelong&0xff00ffFF !0
+# skip many Adobe Photoshop Color swatch (ANPA-Farben.aco TOYO-Farbsystem.aco) with invalid 3rd color entry (1319 2201 2206 21f5 2480 24db 25fd)
+>>>>>6 ubeshort&0xF000 0
+# skip few Adobe Photoshop Color swatch (FOCOLTONE-Farben.aco "PANTONE process coated.aco") with invalid 4th color entry (ffff)
+>>>>>>8 ubeshort&0xF000 0
+# many DEGAS bitmap like: ARABDEMO.PI3 ELMRSESN.PI3 GEMVIEW.PI3 LEREDACT.PI3 PICCOLO.PI3 REPRO_JR.PI3 ST_TOOLS.PI3 TBX_DEMO.PI3 evgem7.pi3
+>>>>>>>0 use degas-bitmap
+# test for last character of Adobe PhotoShop Brush UTF16-LE string and terminating nul char
+>>>>&8 ubelong&0xff00ffFF =0
+# select last DEGAS bitmaps by invalid last char of brush note like BASICNES.PI3 DB_HELP.PI3 DB_WRITR.PI3 LEREDACT.PI3
+>>>>>&-4 ubelong&0x00FF0000 <0x00200000
+>>>>>>0 use degas-bitmap
+# last character of Adobe PhotoShop Brush UTF16-LE note
+#>>>>>&-4 ubelong&0x00FF0000 >0x001F0000 \b, THAT IS ABR
+# DEGAS low-res compressed bitmap *.pc1 like: BATTLSHP.PC1 GNUCHESS.PC1 MEDUSABL.PC1 MOONLORD.PC1 WILDROSE.PC1
+0 beshort 0x8000
+# skip lif files handled via ./lif by test for unused bits of 1st palette entry
+>2 ubeshort&0xF000 0
+# skip CRI ADX ADPCM audio (R04HT.adx R03T-15552.adx) with 44100 Hz misinterpreted as 5th color entry value AC44h
+>>10 ubeshort&0xF000 0
+# skip few (fmt-840-signature-id-1195.adx fmt-840-signature-id-1199.adx) by test for 4 first non black colors in palette entries
+>>>2 quad !0
+>>>>0 use degas-bitmap
+# DEGAS mid-res compressed bitmap *.pc2 like: abydos.pc2 ARTIS3.PC2 SMTHDRAW.PC2 STAR_2K.PC2 TX2_DEMO.PC2
+0 beshort 0x8001
+# skip many (1274/1369) PostScript Type 1 font (DarkGardenMK.pfb coupbi.pfb MONOBOLD.PFB) with invalid 1st atari color palette entry 5506 5b06 6906 7906 7e06 fb15
+>2 ubeshort&0xF000 0
+# skip some (95/1369) PostScript Type 1 font (fmt-525-signature-id-816.pfb LUXEMBRG.PFB) with invalid 3rd atari color palette entry 2521
+>>6 ubeshort&0xF000 0
+>>>0 use degas-bitmap
+# DEGAS high-res compressed bitmap *.pc3 like: abydos.pc3 COYOTE.PC3 ELEPHANT.PC3 TX2_DEMO.PC3 SMTHDRAW.PC3
+0 beshort 0x8002
+# skip some (36/212) Python Pickle (factor_cache.pickle environment.pickle) with invalid 1st atari color entry (2863 6363 7d71)
+>2 ubeshort&0xF000 0
+>>0 use degas-bitmap
+# display information of Atari DEGAS and DEGAS Elite bitmap images
+0 name degas-bitmap
+>0 ubyte x Atari DEGAS
+#!:mime application/octet-stream
+!:mime image/x-atari-degas
+# compressed
+>0 ubyte =0x80 Elite compressed
+# uncompressed
+#>0 ubyte =0x00 uncompressed
+#>0 ubyte =0x00 un.
+>0 ubyte =0x00
+# check for existence of footer for DEGAS Elite images
+>>32042 ubequad x Elite
+>0 beshort 0x0000 bitmap
+!:ext pi1
+>0 beshort 0x0001 bitmap
+!:ext pi2
+>0 beshort 0x0002 bitmap
+# no example with SUH extension found
+#!:ext pi3/suh
+!:ext pi3
+>0 beshort 0x8000 bitmap
+!:ext pc1
+>0 beshort 0x8001 bitmap
+!:ext pc2
+>0 beshort 0x8002 bitmap
+!:ext pc3
+# low resolution; 320x200, 16 colors
+>1 ubyte =0 320 x 200 x 16
+# medium resolution; 640x200, 4 colors
+>1 ubyte =1 640 x 200 x 4
+# high resolution; 640x400, 2 colors
+>1 ubyte =2 640 x 400 x 2
+# http://fileformats.archiveteam.org/wiki/Atari_ST_color_palette
+# hardware_palette[16]; 9 bit ?????RRR?GGG?BBB; 12 bit ????RRRRGGGGBBBB for Atari STE
+# for Atari DEGAS apparently no Spectrum 512 Enhanced 15 bit palette RGB?RRRRGGGGBBBB
+# Red Green Blue unused bit ? often 0 but not bilboule.pi1; color_value (examples or numbers)
+# 1st color palette entry like: 0777 (61) 0fff (LEREDACT.PI3) 0fcf (devgem7.pi3) 0001 (9)
+>2 ubeshort x \b, color palette %4.4x
+# 2nd palette entry like: 0000 (32) 0700 (38) 0f00 (LEREDACT.PI3 devgem7.pi3)
+>4 ubeshort x %4.4x
+# 3rd palette entry
+>6 ubeshort x %4.4x
+# 4th palette entry like: 0000 (72)
+>8 ubeshort x %4.4x
+# 5th palette entry
+>10 ubeshort x %4.4x
+>2 ubeshort x ...
+# 6th palette entry
+#>12 ubeshort x %4.4x
+# 7th palette entry like: 0000 (16) 0001 (ELMRSESN.PI3 elmrsesn.pi3) 0070 (51) 00f0 (BASICNES.PI3 LEREDACT.PI3) 00f8 (devgem7.pi3)
+#>14 ubeshort x %4.4x
+# 8th palette entry
+#>16 ubeshort x %4.4x
+# 9 palette entry
+#>18 ubeshort x %4.4x
+# 10 palette entry
+#>20 ubeshort x %4.4x
+# 11 palette entry
+#>22 ubeshort x %4.4x
+# 12 palette entry
+#>24 ubeshort x %4.4x
+# 13 palette entry
+#>26 ubeshort x %4.4x
+# 14th palette entry
+#>28 ubeshort x %4.4x
+# 15th palette entry
+#>30 ubeshort x %4.4x
+# 16th palette entry
+#>32 ubeshort x %4.4x
+# data[16000] for uncompressed images; pixel data
+#>34 ubequad x \b, DATA %#16.16llx...
+# footer for Elite variant images
+# https://www.fileformat.info/format/atari/egff.htm
+# https://pulkomandy.tk/projects/GrafX2/wiki/Develop/FileFormats/Atari
+# left_color_animation[4]; like: 0000000000000000 0000000100020003 fffafff000000030 (bigspid.pi1)
+#>32034 ubequad !0 \b, color animations %16.16llx (left)
+# right_color_animation[4]; like: 0000000000000000 0000000100020003
+#>>32042 ubequad !0 %16.16llx (right)
+# channel_direction[4]; 0~left 1~none 2~right like: 0001000100010001 0002000000010001 (cycle2.pi1)
+# sometimes unexpected like: feaafc0000000000 (bigspid.pi1)
+#>32050 ubequad !0 \b, channel directions %16.16llx
+# channel_delay[4]; 128 - channel delay, timebase 1/60 s
+#>32058 ubequad !0 \b, channel delays %16.16llx
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/GED
+# https://recoil.sourceforge.net/formats.html#Atari-8-bit
+# Reference: https://sourceforge.net/projects/recoil/files/recoil/6.3.4/recoil-6.3.4.tar.gz
+# recoil-6.3.4/recoil.c
+# http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-ged.trid.xml
+# Note: called "Atari GED bitmap" by TrID; file size 11302
+# and verified by RECOIL graphic tool
+0 string \xFF\xFF0SO\x7F Atari GED bitmap, 160x200
+#!:mime application/octet-stream
+!:mime image/x-atari-ged
+!:ext ged
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/ImageLab/PrintTechnic
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-b_w.trid.xml
+# Note: called "ImageLab bitmap" by TrID
+# verfied by XnView `nconvert -fullinfo "MAEDCHEN.B&W"`
+0 string B&W256 ImageLab bitmap
+!:mime image/x-ilab
+# https://www.xnview.com/de/image_formats/
+# GRR: add char & inside parse_ext in ../../src/apprentice.c to avoid in file version 5.40 error like:
+# Magdir\images, 1090: Warning: EXTENSION type ` b_w/b&w' has bad char '&'
+!:ext b_w/b&w
+# Width
+>6 ubeshort x \b, %u
+# Height
+>8 ubeshort x x %u
+
+# XXX:
+# This is bad magic 0x5249 == 'RI' conflicts with RIFF and other
+# magic.
+# SGI RICE image file <mpruett@sgi.com>
+#0 ubeshort 0x5249 RICE image
+#>2 ubeshort x v%d
+#>4 ubeshort x (%d x
+#>6 ubeshort x %d)
+#>8 ubeshort 0 8 bit
+#>8 ubeshort 1 10 bit
+#>8 ubeshort 2 12 bit
+#>8 ubeshort 3 13 bit
+#>10 ubeshort 0 4:2:2
+#>10 ubeshort 1 4:2:2:4
+#>10 ubeshort 2 4:4:4
+#>10 ubeshort 3 4:4:4:4
+#>12 ubeshort 1 RGB
+#>12 ubeshort 2 CCIR601
+#>12 ubeshort 3 RP175
+#>12 ubeshort 4 YUV
+
+# PCX image files
+# From: Dan Fandrich <dan@coneharvesters.com>
+# updated by Joerg Jenderek at Feb 2013 by https://de.wikipedia.org/wiki/PCX
+# https://web.archive.org/web/20100206055706/http://www.qzx.com/pc-gpe/pcx.txt
+# GRR: original test was still too general as it catches xbase examples T5.DBT,T6.DBT with 0xa000000
+# test for bytes 0x0a,version byte (0,2,3,4,5),compression byte flag(0,1), bit depth (>0) of PCX or T5.DBT,T6.DBT
+0 ubelong&0xffF8fe00 0x0a000000
+# for PCX bit depth > 0
+>3 ubyte >0
+# test for valid versions
+>>1 ubyte <6
+>>>1 ubyte !1 PCX
+!:mime image/x-pcx
+#!:mime image/pcx
+>>>>1 ubyte 0 ver. 2.5 image data
+>>>>1 ubyte 2 ver. 2.8 image data, with palette
+>>>>1 ubyte 3 ver. 2.8 image data, without palette
+>>>>1 ubyte 4 for Windows image data
+>>>>1 ubyte 5 ver. 3.0 image data
+>>>>4 uleshort x bounding box [%d,
+>>>>6 uleshort x %d] -
+>>>>8 uleshort x [%d,
+>>>>10 uleshort x %d],
+>>>>65 ubyte >1 %d planes each of
+>>>>3 ubyte x %d-bit
+>>>>68 ubyte 1 colour,
+>>>>68 ubyte 2 grayscale,
+# this should not happen
+>>>>68 default x image,
+>>>>12 uleshort >0 %d x
+>>>>>14 uleshort x %d dpi,
+>>>>2 ubyte 0 uncompressed
+>>>>2 ubyte 1 RLE compressed
+
+# Adobe Photoshop
+# From: Asbjoern Sloth Toennesen <asbjorn@lila.io>
+# URL: http://fileformats.archiveteam.org/wiki/PSD
+# Reference: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
+# Note: verfied by XnView `nconvert -fullinfo *.psd *.psb *.pdd`
+# and ImageMagick `identify -verbose *.pdd`
+0 string 8BPS
+# skip DROID x-fmt-92-signature-id-277.psd by checking valid width
+>18 ubelong >0 Adobe Photoshop
+!:mime image/vnd.adobe.photoshop
+!:apple ????8BPS
+# version: always equal to 1, but 2 for PSB
+>>4 beshort 1
+# URL: http://fileformats.archiveteam.org/wiki/PhotoDeluxe
+# EXTRAS/PHOTOS/DEMOPIX/ORIGINAL.PDD
+>>>34 search/0xC0d7 PHUT Image (PhotoDeluxe)
+!:ext pdd
+>>>34 default x Image
+!:ext psd
+# URL: http://fileformats.archiveteam.org/wiki/PSB
+>>4 beshort 2 Image (PSB)
+!:ext psb
+# width in pixels: 1-30000 1-300000 for PSB
+>>18 belong x \b, %d x
+>>14 belong x %d,
+# The color mode; 0~Bitmap 1~Grayscale 2~Indexed 3~RGB 4~CMYK 7~Multichannel 9~Duotone 9~Lab
+>>24 beshort 0 bitmap
+>>24 beshort 1 grayscale
+# the number of channels; range is 1 to 56
+>>>12 beshort 2 with alpha
+>>24 beshort 2 indexed
+>>24 beshort 3 RGB
+>>>12 beshort 4 \bA
+>>24 beshort 4 CMYK
+>>>12 beshort 5 \bA
+>>24 beshort 7 multichannel
+>>24 beshort 8 duotone
+>>24 beshort 9 lab
+>>12 beshort > 1
+>>>12 beshort x \b, %dx
+>>12 beshort 1 \b,
+>>22 beshort x %d-bit channel
+>>12 beshort > 1 \bs
+# 6 reserved bytes; must be zero, but spaces inside ImageMagick input.psd
+# https://download.imagemagick.org/ImageMagick/download/ImageMagick-7.0.11-11.zip
+# ImageMagick-7.0.11-11\PerlMagick\t\input.psd
+>>6 bequad&0xFFffFFffFFff0000 !0 \b, at offset 6
+>>>6 belong x 0x%8.8x
+>>>6 beshort x \b%4.4x
+
+# From: Joerg Jenderek
+# URL: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
+# http://fileformats.archiveteam.org/wiki/Photoshop
+# Reference: http://www.nomodes.com/aco.html
+# Note: registers as Photoshop.SwatchesFile for Photoshop.exe on Windows
+# check for valid versions like: 2 (newest) 1 (old) 0 (oldest no examples)
+0 ubeshort <3
+# skip few Atari DEGAS med-res bitmap (DIAGRAM1.PI2) and many ISO 9660 CD-ROM by check for invalid low color numbers (0)
+>2 ubeshort >0
+# skip few Targa (bmpsuite-15col.tga rgb24_top_left_colormap.tga) by check for invalid high color space ID (F0 1D)
+>>4 ubeshort <16
+# skip many (69/327) Targa image *.TGA by check of accessing near the ending of first color space section (size=nc*5*2)
+>>>(2.S*10) ubelong x
+# RGB branch for Adobe Photoshop Color swatch
+>>>>4 ubeshort =0
+# skip many (220/327) Targa by check of for invalid high RGB color z value (hexadecimal 2 3 2e03 4600 5e04 7502 8002 8b05 c700)
+>>>>>12 ubeshort =0
+# RGB branch for Adobe Photoshop Color swatch for older versions
+>>>>>>0 ubeshort <2
+>>>>>>>0 use adobe-aco
+# RGB branch for Adobe Photoshop Color swatch for newer version 2
+>>>>>>0 ubeshort =2
+# skip many (74/176) Atari DEGAS hi-res bitmap (*.PI3) by check for invalid low color name length (0)
+>>>>>>>16 ubeshort >0
+>>>>>>>>0 use adobe-aco
+# non RGB branch for Adobe Photoshop Color swatch
+>>>>4 ubeshort !0
+# non RGB branch for Adobe Photoshop Color swatch for older versions
+>>>>>0 ubeshort <2
+# skip many GEM Image (CHURCH.IMG TIGER.IMG) by check for invalid second high color space ID (55 114 143 157 256 288 450)
+>>>>>>14 ubeshort <16
+>>>>>>>0 use adobe-aco
+# non RGB branch for Adobe Photoshop Color swatch for newer version 2
+>>>>>0 ubeshort =2
+# skip few Atari DEGAS hi-res bitmap (pal1wb-blue.pi3) and few ABR by check for invalid "high" nil bytes (7) before color name length
+>>>>>>14 ubeshort =0
+>>>>>>>0 use adobe-aco
+# display Adobe Photoshop Color swatch file information (version, number of colors, color spaces, coordinates, names)
+0 name adobe-aco
+>0 ubeshort x Adobe Photoshop Color swatch, version %u
+#!:mime application/octet-stream
+!:mime application/x-adobe-aco
+!:apple ????8BCO
+!:ext aco
+>0 ubeshort <2
+>>(2.S*10) ubelong x
+# version 2 section after version 1 section
+>>>&0 ubeshort 2 and 2
+# nc; number of colors like: 20 50 86 88 126 204 300 1050 1137 1280 2092 3010 4096
+>2 ubeshort x \b, %u colors
+# maybe last 4 bytes of first section (probably y z color value) like: 0 0x66660000 0xfe700000 0xffff0000
+#>(2.S*10) ubelong x 1ST_SECTION_END=%#8.8x
+>0 ubeshort <2 \b; 1st
+# first older Adobe Photoshop Color entry
+>>4 use aco-color
+>>>2 ubeshort >1 \b; 2nd
+# second older Adobe Photoshop Color entry
+>>>>14 use aco-color
+>0 ubeshort =2 \b; 1st
+# first new Adobe Photoshop Color entry
+>>4 use aco-color-v2
+>>>2 ubeshort >1 \b; 2nd
+# jump first color name length words
+>>>>(16.S*2) ubequad x
+# second new Adobe Photoshop Color entry
+>>>>>&10 use aco-color-v2
+# display Adobe Photoshop Color entry (color space, color coordinates)
+0 name aco-color
+# each color spec entry occupies five words
+# color space: 0~RGB 1~HSB 2~CMYK 3~Pantone 4~Focoltone 5~Trumatch 6~Toyo 7~Lab 8~Grayscale 9?~wideCMYK 10~HKS ...
+#>0 ubeshort x COLOR_ENTRY
+>0 ubeshort 0 RGB
+>0 ubeshort 1 HSB
+>0 ubeshort 2 CMYK
+>0 ubeshort 3 Pantone
+>0 ubeshort 4 Focoltone
+>0 ubeshort 5 Trumatch
+>0 ubeshort 6 Toyo
+>0 ubeshort 7 Lab
+>0 ubeshort 8 Grayscale
+>0 ubeshort 9 wide CMYK
+>0 ubeshort 10 HKS
+# unofficial
+# >0 ubeshort 12 foo
+# >0 ubeshort 13 bar
+# >0 ubeshort 14 FOO
+# >0 ubeshort 15 BAR
+>0 ubeshort x space (%u)
+# color coordinate w
+>2 ubeshort x \b, w %#x
+# color coordinate x
+>4 ubeshort x \b, x %#x
+# color coordinate y
+>6 ubeshort x \b, y %#x
+# color coordinate z; zero for RGB space
+>8 ubeshort x \b, z %#x
+# display Adobe Photoshop Color entry version 2 (color space, color coordinates names)
+0 name aco-color-v2
+>0 use aco-color
+#>10 ubeshort x \b, NUL_BYTES %#x
+# color name length plus one (len+1) like: 7 8 9 13 14 15 16 17 22 26
+#>>12 ubeshort x \b, LENGTH %u
+>>12 ubeshort-1 x \b, %u chars
+# len words; UTF-16 representation of the color name like: "DIC 1s" "PANTONE Process Yellow PC"
+>>14 bestring16 x "%s"
+# followed by nil word
+
+# XV thumbnail indicator (ThMO)
+# URL: https://en.wikipedia.org/wiki/Xv_(software)
+# Reference: http://fileformats.archiveteam.org/wiki/XV_thumbnail
+# Update: Joerg Jenderek
+0 string P7\ 332 XV thumbnail image data
+#0 string P7\ 332 XV "thumbnail file" (icon) data
+!:mime image/x-xv-thumbnail
+# thumbnail .xvpic/foo.bar for graphic foo.bar
+!:ext p7/gif/tif/xpm/jpg
+
+# NITF is defined by United States MIL-STD-2500A
+0 string NITF National Imagery Transmission Format
+>25 string >\0 dated %.14s
+
+# GEM Image: Version 1, Headerlen 8 (Wolfram Kleff)
+# Format variations from: Bernd Nuernberger <bernd.nuernberger@web.de>
+# Update: Joerg Jenderek
+# See http://fileformats.archiveteam.org/wiki/GEM_Raster
+# For variations, also see:
+# https://www.seasip.info/Gem/ff_img.html (Ventura)
+# http://www.atari-wiki.com/?title=IMG_file (XIMG, STTT)
+# http://www.fileformat.info/format/gemraster/spec/index.htm (XIMG, STTT)
+# http://sylvana.net/1stguide/1STGUIDE.ENG (TIMG)
+0 beshort 0x0001
+# header_size
+>2 beshort 0x0008
+>>0 use gem_info
+>2 beshort 0x0009
+>>0 use gem_info
+# no example for NOSIG
+>2 beshort 24
+>>0 use gem_info
+# no example for HYPERPAINT
+>2 beshort 25
+>>0 use gem_info
+16 string XIMG\0
+>0 use gem_info
+# no example
+16 string STTT\0\x10
+>0 use gem_info
+# no example or description
+16 string TIMG\0
+>0 use gem_info
+
+0 name gem_info
+# version is 2 for some XIMG and 1 for all others
+>0 ubeshort <0x0003 GEM
+# https://www.snowstone.org.uk/riscos/mimeman/mimemap.txt
+!:mime image/x-gem
+# header_size 24 25 27 59 779 words for colored bitmaps
+>>2 ubeshort >9
+>>>16 string STTT\0\x10 STTT
+>>>16 string TIMG\0 TIMG
+# HYPERPAINT or NOSIG variant
+>>>16 string \0\x80
+>>>>2 ubeshort =24 NOSIG
+>>>>2 ubeshort !24 HYPERPAINT
+# NOSIG or XIMG variant
+>>>16 default x
+>>>>16 string !XIMG\0 NOSIG
+>>16 string =XIMG\0 XIMG Image data
+!:ext img/ximg
+# to avoid Warning: Current entry does not yet have a description for adding a EXTENSION type
+>>16 string !XIMG\0 Image data
+!:ext img
+# header_size is 9 for Ventura files and 8 for other GEM Paint files
+>>2 ubeshort 9 (Ventura)
+#>>2 ubeshort 8 (Paint)
+>>12 ubeshort x %d x
+>>14 ubeshort x %d,
+# 1 4 8
+>>4 ubeshort x %d planes,
+# in tenths of a millimetre
+>>8 ubeshort x %d x
+>>10 ubeshort x %d pixelsize
+# pattern_size 1-8. 2 for GEM Paint
+>>6 ubeshort !2 \b, pattern size %d
+
+# GEM Metafile (Wolfram Kleff)
+0 ulelong 0x0018FFFF GEM Metafile data
+>4 uleshort x version %d
+
+#
+# SMJPEG. A custom Motion JPEG format used by Loki Entertainment
+# Software Torbjorn Andersson <d91tan@Update.UU.SE>.
+#
+0 string \0\nSMJPEG SMJPEG
+>8 ubelong x %d.x data
+# According to the specification you could find any number of _TXT
+# headers here, but I can't think of any way of handling that. None of
+# the SMJPEG files I tried it on used this feature. Even if such a
+# file is encountered the output should still be reasonable.
+>16 string _SND \b,
+>>24 ubeshort >0 %d Hz
+>>26 ubyte 8 8-bit
+>>26 ubyte 16 16-bit
+>>28 string NONE uncompressed
+# >>28 string APCM ADPCM compressed
+>>27 ubyte 1 mono
+>>28 ubyte 2 stereo
+# Help! Isn't there any way to avoid writing this part twice?
+# Yes, use a name/use
+>>32 string _VID \b,
+# >>>48 string JFIF JPEG
+>>>40 ubelong >0 %d frames
+>>>44 ubeshort >0 (%d x
+>>>46 ubeshort >0 %d)
+>16 string _VID \b,
+# >>32 string JFIF JPEG
+>>24 ubelong >0 %d frames
+>>28 ubeshort >0 (%d x
+>>30 ubeshort >0 %d)
+
+0 string Paint\ Shop\ Pro\ Image\ File Paint Shop Pro Image File
+
+# taken from fkiss: (<yav@mte.biglobe.ne.jp> ?)
+0 string KiSS KISS/GS
+>4 ubyte 16 color
+>>5 ubyte x %d bit
+>>8 uleshort x %d colors
+>>10 uleshort x %d groups
+>4 ubyte 32 cell
+>>5 ubyte x %d bit
+>>8 uleshort x %d x
+>>10 uleshort x %d
+>>12 uleshort x +%d
+>>14 uleshort x +%d
+
+# Webshots (www.webshots.com), by John Harrison
+0 string C\253\221g\230\0\0\0 Webshots Desktop .wbz file
+
+# Hercules DASD image files
+# From Jan Jaeger <jj@septa.nl> and Jay Maynard <jaymaynard@gmail.com>
+0 string CKD_P370 Hercules CKD DASD image file
+>8 lelong x \b, %d heads per cylinder
+>12 lelong x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+
+0 string CKD_C370 Hercules compressed CKD DASD image file
+>8 lelong x \b, %d heads per cylinder
+>12 lelong x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+>552 lelong x \b, %d total cylinders
+>>557 byte 0 \b, no compression
+>>557 byte 1 \b, ZLIB compression
+>>557 byte 2 \b, BZ2 compression
+
+0 string CKD_S370 Hercules CKD DASD shadow file
+>8 lelong x \b, %d heads per cylinder
+>12 lelong x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+
+0 string CKD_P064 Hercules CKD64 DASD image file
+>8 lelong x \b, %d heads per cylinder
+>12 lelong x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+
+0 string CKD_C064 Hercules compressed CKD64 DASD image file
+>8 lelong x \b, %d heads per cylinder
+>12 lelong x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+>524 lelong x \b, %d total cylinders
+>>585 byte 0 \b, no compression
+>>585 byte 1 \b, ZLIB compression
+>>585 byte 2 \b, BZ2 compression
+
+0 string CKD_S064 Hercules CKD64 DASD shadow file
+>8 lelong x \b, %d heads per cylinder
+>12 lelong x \b, track size %d bytes
+>16 byte x \b, device type 33%2.2X
+
+# Squeak images and programs - etoffi@softhome.net
+0 string \146\031\0\0 Squeak image data
+0 search/1 'From\040Squeak Squeak program text
+
+# partimage: file(1) magic for PartImage files (experimental, incomplete)
+# Author: Hans-Joachim Baader <hjb@pro-linux.de>
+0 string PaRtImAgE-VoLuMe PartImage
+>0x0020 string 0.6.1 file version %s
+>>0x0060 ulelong >-1 volume %d
+#>>0x0064 8 byte identifier
+#>>0x007c reserved
+>>0x0200 string >\0 type %s
+>>0x1400 string >\0 device %s,
+>>0x1600 string >\0 original filename %s,
+# Some fields omitted
+>>0x2744 ulelong 0 not compressed
+>>0x2744 ulelong 1 gzip compressed
+>>0x2744 ulelong 2 bzip2 compressed
+>>0x2744 ulelong >2 compressed with unknown algorithm
+>0x0020 string >0.6.1 file version %s
+>0x0020 string <0.6.1 file version %s
+
+# DCX is multi-page PCX, using a simple header of up to 1024
+# offsets for the respective PCX components.
+# From: Joerg Wunsch <joerg_wunsch@uriah.heep.sax.de>
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/DCX
+0 ulelong 987654321 DCX multi-page
+# http://www.nationalarchives.gov.uk/pronom/x-fmt/348
+!:mime image/x-dcx
+!:ext dcx
+# The first file offset usually starts at file offset 0x1004
+# print 1 space after 0x100? offset and then handles PCX images by ./images
+>4 ulelong x \b, at %#x
+>(4.l) indirect x
+# possible 2nd PCX image
+#>8 ulelong !0 \b, at %#x
+#>>(8.l) indirect x
+# possible 3rd PCX image
+#>12 ulelong !0 \b, at %#x
+#>>(12.l) indirect x
+
+# Simon Walton <simonw@matteworld.com>
+# Kodak Cineon format for scanned negatives
+# http://www.kodak.com/US/en/motion/support/dlad/
+0 ulelong 0xd75f2a80 Cineon image data
+>200 ubelong >0 \b, %d x
+>204 ubelong >0 %d
+
+
+# Bio-Rad .PIC is an image format used by microscope control systems
+# and related image processing software used by biologists.
+# From: Vebjorn Ljosa <vebjorn@ljosa.com>
+# BOOL values are two-byte integers; use them to rule out false positives.
+# https://web.archive.org/web/20050317223257/www.cs.ubc.ca/spider/ladic/text/biorad.txt
+# Samples: https://www.loci.wisc.edu/software/sample-data
+14 uleshort <2
+>62 uleshort <2
+>>54 uleshort 12345 Bio-Rad .PIC Image File
+>>>0 uleshort >0 %d x
+>>>2 uleshort >0 %d,
+>>>4 uleshort =1 1 image in file
+>>>4 uleshort >1 %d images in file
+
+# From Jan "Yenya" Kasprzak <kas@fi.muni.cz>
+# The description of *.mrw format can be found at
+# http://www.dalibor.cz/minolta/raw_file_format.htm
+0 string \000MRM Minolta Dimage camera raw image data
+
+# Summary: DjVu image / document
+# Extension: .djvu
+# Reference: http://djvu.org/docs/DjVu3Spec.djvu
+# Submitted by: Stephane Loeuillet <stephane.loeuillet@tiscali.fr>
+# Modified by (1): Abel Cheung <abelcheung@gmail.com>
+0 string AT&TFORM
+>12 string DJVM DjVu multiple page document
+!:mime image/vnd.djvu
+>12 string DJVU DjVu image or single page document
+!:mime image/vnd.djvu
+>12 string DJVI DjVu shared document
+!:mime image/vnd.djvu
+>12 string THUM DjVu page thumbnails
+!:mime image/vnd.djvu
+
+# Originally by Marc Espie
+# Modified by Robert Minsk <robertminsk at yahoo.com>
+# https://www.openexr.com/openexrfilelayout.pdf
+0 ulelong 20000630 OpenEXR image data,
+!:mime image/x-exr
+>4 ulelong&0x000000ff x version %d,
+>4 ulelong ^0x00000200 storage: scanline
+>4 ulelong &0x00000200 storage: tiled
+>8 search/0x1000 compression\0 \b, compression:
+>>&16 ubyte 0 none
+>>&16 ubyte 1 rle
+>>&16 ubyte 2 zips
+>>&16 ubyte 3 zip
+>>&16 ubyte 4 piz
+>>&16 ubyte 5 pxr24
+>>&16 ubyte 6 b44
+>>&16 ubyte 7 b44a
+>>&16 ubyte 8 dwaa
+>>&16 ubyte 9 dwab
+>>&16 ubyte >9 unknown
+>8 search/0x1000 dataWindow\0 \b, dataWindow:
+>>&10 ulelong x (%d
+>>&14 ulelong x %d)-
+>>&18 ulelong x \b(%d
+>>&22 ulelong x %d)
+>8 search/0x1000 displayWindow\0 \b, displayWindow:
+>>&10 ulelong x (%d
+>>&14 ulelong x %d)-
+>>&18 ulelong x \b(%d
+>>&22 ulelong x %d)
+>8 search/0x1000 lineOrder\0 \b, lineOrder:
+>>&14 ubyte 0 increasing y
+>>&14 ubyte 1 decreasing y
+>>&14 ubyte 2 random y
+>>&14 ubyte >2 unknown
+
+# SMPTE Digital Picture Exchange Format, SMPTE DPX
+#
+# ANSI/SMPTE 268M-1994, SMPTE Standard for File Format for Digital
+# Moving-Picture Exchange (DPX), v1.0, 18 February 1994
+# Robert Minsk <robertminsk at yahoo.com>
+# Modified by Harry Mallon <hjmallon at gmail.com>
+0 string SDPX DPX image data, big-endian,
+!:mime image/x-dpx
+>0 use dpx_info
+0 string XPDS DPX image data, little-endian,
+!:mime image/x-dpx
+>0 use \^dpx_info
+
+0 name dpx_info
+>768 ubeshort <4
+>>772 ubelong x %dx
+>>776 ubelong x \b%d,
+>768 ubeshort >3
+>>776 ubelong x %dx
+>>772 ubelong x \b%d,
+>768 ubeshort 0 left to right/top to bottom
+>768 ubeshort 1 right to left/top to bottom
+>768 ubeshort 2 left to right/bottom to top
+>768 ubeshort 3 right to left/bottom to top
+>768 ubeshort 4 top to bottom/left to right
+>768 ubeshort 5 top to bottom/right to left
+>768 ubeshort 6 bottom to top/left to right
+>768 ubeshort 7 bottom to top/right to left
+
+# From: Tom Hilinski <tom.hilinski@comcast.net>
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/NetCDF
+# http://fileformats.archiveteam.org/wiki/NetCDF
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/n/netcdf.trid.xml
+# https://www.loc.gov/preservation/digital/formats/fdd/fdd000330.shtml
+# Note: called "NetCDF Network Common Data Form" by TrID and
+# "netCDF-3 Classic" by DROID via PUID fmt/282
+# https://www.unidata.ucar.edu/packages/netcdf/
+0 string CDF\001
+# skip DROID fmt-282-signature-id-298.nc by test for more content bytes
+>3 uleshort >0 NetCDF Data Format data
+#!:mime application/netcdf
+# https://reposcope.com/mimetype/application/x-netcdf
+!:mime application/x-netcdf
+!:ext nc
+# https://fileinfo.com/extension/cdf
+# https://www.file-extensions.org/cdf-file-extension-unidata-network-common-data-form
+# in 1994 changed from CDF to NC file extension avoid a clash with other file formats
+#!:ext nc/cdf
+# 64-bit offset netcdf Classic https://www.unidata.ucar.edu/software/netcdf/docs/file_format_specifications
+# Note: called "netCDF-3 64-bit" by DROID via PUID fmt/283
+0 string CDF\002
+# skip DROID fmt-283-signature-id-299.nc by test for more content bytes
+>3 uleshort >0 NetCDF Data Format data (64-bit offset)
+#!:mime application/netcdf
+!:mime application/x-netcdf
+!:ext nc
+
+# From: Michael Liu
+# https://en.wikipedia.org/wiki/Common_Data_Format
+0 ubelong 0xCDF30001 Common Data Format (Version 3 or later) data
+!:mime application/x-cdf
+
+0 ubelong 0xCDF26002 Common Data Format (Version 2.6 or 2.7) data
+!:mime application/x-cdf
+
+0 ubelong 0x0000FFFF Common Data Format (Version 2.5 or earlier) data
+!:mime application/x-cdf
+
+# Hierarchical Data Format, used to facilitate scientific data exchange
+# specifications at http://hdf.ncsa.uiuc.edu/
+# URL: http://fileformats.archiveteam.org/wiki/HDF
+# https://en.wikipedia.org/wiki/Hierarchical_Data_Format
+# Reference: https://portal.hdfgroup.org/download/attachments/52627880/HDF5_File_Format_Specification_Version-3.0.pdf
+0 ubelong 0x0e031301 Hierarchical Data Format (version 4) data
+!:mime application/x-hdf
+!:ext hdf/hdf4/h4
+0 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) data
+#!:mime application/x-hdf
+!:mime application/x-hdf5
+!:ext h5/hdf5/hdf/he5
+512 string \211HDF\r\n\032\n
+# skip Matlab v5 mat-file testhdf5_7.4_GLNX86.mat handled by ./mathematica
+>0 string !MATLAB Hierarchical Data Format (version 5) with 512 bytes user block
+#!:mime application/x-hdf
+!:mime application/x-hdf5
+!:ext h5/hdf5/hdf/he5
+1024 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) with 1k user block
+#!:mime application/x-hdf
+!:mime application/x-hdf5
+!:ext h5/hdf5/hdf/he5
+2048 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) with 2k user block
+#!:mime application/x-hdf
+!:mime application/x-hdf5
+!:ext h5/hdf5/hdf/he5
+4096 string \211HDF\r\n\032\n Hierarchical Data Format (version 5) with 4k user block
+#!:mime application/x-hdf
+!:mime application/x-hdf5
+!:ext h5/hdf5/hdf/he5
+
+# From: Tobias Burnus <burnus@net-b.de>
+# Xara (for a while: Corel Xara) is a graphic package, see
+# http://www.xara.com/ for Windows and as GPL application for Linux
+0 string XARA\243\243 Xara graphics file
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Corel_Gallery
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bmf-corel.trid.xml
+# Note: called "Corel Binary Material Format" by TrID and
+# "Corel Flow" by XnView
+0 string @CorelBMF\n\rCorel\040Corporation Corel GALLERY Clipart
+!:mime image/x-corel-bmf
+!:ext bmf
+
+# https://www.cartesianinc.com/Tech/
+# Reference: http://fileformats.archiveteam.org/wiki/Cartesian_Perceptual_Compression
+0 string CPC\262 Cartesian Perceptual Compression image
+!:mime image/x-cpi
+!:ext cpi/cpc
+
+# From Albert Cahalan <acahalan@gmail.com>
+# puredigital used it for the CVS disposable camcorder
+#8 lelong 4 ZBM bitmap image data
+#>4 uleshort x %u x
+#>6 uleshort x %u
+
+# From Albert Cahalan <acahalan@gmail.com>
+# uncompressed 5:6:5 HighColor image for OLPC XO firmware icons
+0 string C565 OLPC firmware icon image data
+>4 uleshort x %u x
+>6 uleshort x %u
+
+# Applied Images - Image files from Cytovision
+# Gustavo Junior Alves <gjalves@gjalves.com.br>
+0 string \xce\xda\xde\xfa Cytovision Metaphases file
+0 string \xed\xad\xef\xac Cytovision Karyotype file
+0 string \x0b\x00\x03\x00 Cytovision FISH Probe file
+0 string \xed\xfe\xda\xbe Cytovision FLEX file
+0 string \xed\xab\xed\xfe Cytovision FLEX file
+0 string \xad\xfd\xea\xad Cytovision RATS file
+
+# Wavelet Scalar Quantization format used in gray-scale fingerprint images
+# From Tano M Fotang <mfotang@quanteq.com>
+0 string \xff\xa0\xff\xa8\x00 Wavelet Scalar Quantization image data
+
+# Type: PCO B16 image files
+# URL: http://www.pco.de/fileadmin/user_upload/db/download/MA_CWDCOPIE_0412b.pdf
+# From: Florian Philipp <florian.philipp@binarywings.net>
+# Extension: .b16
+# Description: Pixel image format produced by PCO Camware, typically used
+# together with PCO cameras.
+# Note: Different versions exist for e.g. 8 bit and 16 bit images.
+# Documentation is incomplete.
+0 string/b PCO- PCO B16 image data
+>12 ulelong x \b, %dx
+>16 ulelong x \b%d
+>20 ulelong 0 \b, short header
+>20 ulelong -1 \b, extended header
+>>24 ulelong 0 \b, grayscale
+>>>36 ulelong 0 linear LUT
+>>>36 ulelong 1 logarithmic LUT
+>>>28 ulelong x [%d
+>>>32 ulelong x \b,%d]
+>>24 ulelong 1 \b, color
+>>>64 ulelong 0 linear LUT
+>>>64 ulelong 1 logarithmic LUT
+>>>40 ulelong x r[%d
+>>>44 ulelong x \b,%d]
+>>>48 ulelong x g[%d
+>>>52 ulelong x \b,%d]
+>>>56 ulelong x b[%d
+>>>60 ulelong x \b,%d]
+
+# Polar Monitor Bitmap (.pmb) used as logo for Polar Electro watches
+# From: Markus Heidelberg <markus.heidelberg at web.de>
+0 string/t [BitmapInfo2] Polar Monitor Bitmap text
+!:mime image/x-polar-monitor-bitmap
+
+# From: Rick Richardson <rickrich@gmail.com>
+# updated by: Joerg Jenderek
+# URL: http://techmods.net/nuvi/
+0 string GARMIN\ BITMAP\ 01 Garmin Bitmap file
+# extension is also used for
+# Sony SRF raw image (image/x-sony-srf)
+# SRF map
+# Terragen Surface Map (https://www.planetside.co.uk/terragen)
+# FileLocator Pro search criteria file (https://www.mythicsoft.com/filelocatorpro)
+!:ext srf
+#!:mime image/x-garmin-srf
+# version 1.00,2.00,2.10,2.40,2.50
+>0x2f string >0 \b, version %4.4s
+# width (2880,2881,3240)
+>0x55 uleshort >0 \b, %dx
+# height (80,90)
+>>0x53 uleshort x \b%d
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Imageiio/imaginfo_(Ulead)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/pe3.trid.xml
+# Note: called "Ulead Imageiio/Imaginfo thumbnail" by TrID
+0 string IIO1$ Ulead Photo Explorer 3
+#!:mime application/octet-stream
+!:mime image/x-ulead-pe3
+# IMAGEIIO.PE3
+!:ext pe3
+# look for DOS/Windows drive letter
+>5 search/192/s :\\
+# directory or full name of corresponding imaginfo.pe3 like: "T:\SAMPLES\TEXTURES\SKY_SNOW\IIOE371.TMP "S:\PI3\PIMPACT3\PROGRAMS\PATTERNS\imaginfo.pe3"
+>>&-1 string x "%s"
+# look for DOS/Windows network path if no drive letter part
+>5 default x
+>>5 search/192/s \x5c\x5c
+# full name of corresponding imaginfo.pe3 like: "\\Lionking\upi\SAMPLES\IMAGES\ANIMALS\imaginfo.pe3"
+>>>&0 string x "%s"
+# Type: Ulead Photo Explorer5 (.pe5)
+# URL: http://fileformats.archiveteam.org/wiki/Imageiio/imaginfo_(Ulead)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/pe4.trid.xml
+# From: Simon Horman <horms@debian.org>
+# Update: Joerg Jenderek
+# Note: some called "Ulead Imageiio/Imaginfo thumbnail" by TrID
+# and used in various Ulead applications
+0 string IIO2H Ulead Photo Explorer 4 or 5
+#!:mime application/octet-stream
+!:mime image/x-ulead-pe4
+# IMAGEIIO.PE4
+!:ext pe4/pe5
+# look in most samples for JPEG signature like: SAMPLES/IMAGES/SCENES/IMAGINFO.PE4
+>0x4c2 search/0xE02/s JFIF with JPEG image data
+>>&-6 use jpeg
+# near the end list of image names like: Img0001.pcd 1116012L.JPG NCARD4.TPL
+#
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/pe3-imaginfo.trid.xml
+11 string \001\0\0\0\0
+# check for version 3 part
+>19 string \0\001\0\003\0
+>>0 use ulead-imaginfo
+# From: Joerg Jenderek
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/pe4-imaginfo.trid.xml
+11 string \001\0\0\0\0
+# check for version 4 part
+>19 string \0\0\0\004\0
+>>0 use ulead-imaginfo
+# display information about Ulead Imaginfo thumbnail (version, directory, image extension)
+0 name ulead-imaginfo
+>22 ubyte x Ulead Imaginfo thumbnail
+#!:mime application/octet-stream
+!:mime image/x-ulead-imaginfo
+>22 ubyte =3 \b, version 3
+# IMAGINFO.PE3
+!:ext pe3
+>22 ubyte =4 \b, version 4
+# IMAGINFO.PE4
+!:ext pe4
+# MAYBE ALSO VERSION 5 ?
+#>22 ubyte =5 \b, version 5
+#!:ext pe5
+>22 ubyte x
+# look for DOS/Windows driver letter
+>>4 search/192/s :\x5c
+# skip f:\Programme\iPhoto Plus 4\Template\Business Cards\IMAGINFO.PE4
+# by looking for driver letter in range A-Z
+>>>&-1 ubyte >0x40
+# directory path like: "E:\iPE\CDSample\Images\Scenes" "D:\XmasCard\Samples" "C:\TEMP\PLANTS"
+>>>>&-5 pstring/l >0 \b, "%s"
+# look for DOS/Windows network path if no valid drive letter part
+>>>&-1 default x
+>>>>4 search/192/s \x5c\x5c
+# directory path like: "\\FSX\SYS\OPPS\IPE.ENG\TEMPLATE\BUSINESS" "\\Lionking\upi\SAMPLES\IMAGES\ANIMALS"
+>>>>>&-4 pstring/l >0 \b, "%s"
+# look for DOS/Windows network path if no drive letter part
+>>4 default x
+>>>4 search/192/s \x5c\x5c
+# directory path like: "\\FSX\SYS\opps\ipe.eng\samples" "\\DANIEL\IPE_CD\IPE.ITA"
+>>>>&-4 pstring/l >0 \b, "%s"
+# look for point character inside image names
+>56 search/38/s .
+# image name extension like: bmp jpg pcd tpl
+>>&1 string x with %-.3s images
+# Summary: Ulead Pattern image (Corel Corporation)
+# URL: https://en.wikipedia.org/wiki/Ulead_Systems
+# https://www.file-extensions.org/pst-file-extension-ulead-pattern-image-format
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/pst-ulead.trid.xml
+# From: Joerg Jenderek
+# Note: used also by CorelDraw Essentials 3 version 13.0.0.800
+# there seems to exist other versions
+0 ubelong 0xFFFF0100
+>8 search/21 PresetInfo Ulead pattern image
+#!:mime application/octet-stream
+!:mime image/x-ulead-pst
+!:ext pst
+# string length like: 16 18 19 21 24
+#>>4 uleshort x n=%u
+# like: BlendPresetInfo DropShadowPresetInfo FileNewPresetInfo VectorExtrudePresetInfo EnvelopePresetInfo ContourPresetInfo DistortionPresetInfo
+>>4 pstring/h x "%s"
+
+# Type: X11 cursor
+# URL: http://webcvs.freedesktop.org/mime/shared-mime-info/freedesktop.org.xml.in?view=markup
+# From: Mathias Brodala <info@noctus.net>
+0 string Xcur X11 cursor
+
+# Type: Olympus ORF raw images.
+# URL: https://libopenraw.freedesktop.org/wiki/Olympus_ORF
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string MMOR Olympus ORF raw image data, big-endian
+!:mime image/x-olympus-orf
+0 string IIRO Olympus ORF raw image data, little-endian
+!:mime image/x-olympus-orf
+0 string IIRS Olympus ORF raw image data, little-endian
+!:mime image/x-olympus-orf
+
+# Type: files used in modern AVCHD camcoders to store clip information
+# Extension: .cpi
+# From: Alexander Danilov <alexander.a.danilov@gmail.com>
+0 string HDMV0100 AVCHD Clip Information
+
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: http://local.wasp.uwa.edu.au/~pbourke/dataformats/pic/
+# Radiance HDR; usually has .pic or .hdr extension.
+0 string #?RADIANCE\n Radiance HDR image data
+!:mime image/vnd.radiance
+
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# URL: https://www.mpi-inf.mpg.de/resources/pfstools/pfs_format_spec.pdf
+# Used by the pfstools packages. The regex matches for the image size could
+# probably use some work. The MIME type is made up; if there's one in
+# actual common use, it should replace the one below.
+0 string PFS1\x0a PFS HDR image data
+#!mime image/x-pfs
+>1 regex [0-9]*\ \b, %s
+>>1 regex \ [0-9]{4} \bx%s
+
+# Type: Foveon X3F
+# URL: https://www.photofo.com/downloads/x3f-raw-format.pdf
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# Note that the MIME type isn't defined anywhere that I can find; if
+# there's a canonical type for this format, it should replace this one.
+0 string FOVb Foveon X3F raw image data
+!:mime image/x-x3f
+>6 uleshort x \b, version %d.
+>4 uleshort x \b%d
+>28 ulelong x \b, %dx
+>32 ulelong x \b%d
+
+# Paint.NET file
+# From Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string PDN3 Paint.NET image data
+!:mime image/x-paintnet
+
+# Not really an image.
+# From: "Tano M. Fotang" <mfotang@quanteq.com>
+0 string \x46\x4d\x52\x00 ISO/IEC 19794-2 Format Minutiae Record (FMR)
+
+# doc: https://www.shikino.co.jp/eng/products/images/FLOWER.jpg.zip
+# example: https://www.shikino.co.jp/eng/products/images/FLOWER.wdp.zip
+90 ubequad 0x574D50484F544F00 JPEG-XR Image
+>98 ubyte&0x08 =0x08 \b, hard tiling
+>99 ubyte&0x80 =0x80 \b, tiling present
+>99 ubyte&0x40 =0x40 \b, codestream present
+>99 ubyte&0x38 x \b, spatial xform=
+>99 ubyte&0x38 0x00 \bTL
+>99 ubyte&0x38 0x08 \bBL
+>99 ubyte&0x38 0x10 \bTR
+>99 ubyte&0x38 0x18 \bBR
+>99 ubyte&0x38 0x20 \bBT
+>99 ubyte&0x38 0x28 \bRB
+>99 ubyte&0x38 0x30 \bLT
+>99 ubyte&0x38 0x38 \bLB
+>100 ubyte&0x80 =0x80 \b, short header
+>>102 ubeshort+1 x \b, %d
+>>104 ubeshort+1 x \bx%d
+>100 ubyte&0x80 =0x00 \b, long header
+>>102 ubelong+1 x \b, %x
+>>106 ubelong+1 x \bx%x
+>101 ubeshort&0xf x \b, bitdepth=
+>>101 ubeshort&0xf 0x0 \b1-WHITE=1
+>>101 ubeshort&0xf 0x1 \b8
+>>101 ubeshort&0xf 0x2 \b16
+>>101 ubeshort&0xf 0x3 \b16-SIGNED
+>>101 ubeshort&0xf 0x4 \b16-FLOAT
+>>101 ubeshort&0xf 0x5 \b(reserved 5)
+>>101 ubeshort&0xf 0x6 \b32-SIGNED
+>>101 ubeshort&0xf 0x7 \b32-FLOAT
+>>101 ubeshort&0xf 0x8 \b5
+>>101 ubeshort&0xf 0x9 \b10
+>>101 ubeshort&0xf 0xa \b5-6-5
+>>101 ubeshort&0xf 0xb \b(reserved %d)
+>>101 ubeshort&0xf 0xc \b(reserved %d)
+>>101 ubeshort&0xf 0xd \b(reserved %d)
+>>101 ubeshort&0xf 0xe \b(reserved %d)
+>>101 ubeshort&0xf 0xf \b1-BLACK=1
+>101 ubeshort&0xf0 x \b, colorfmt=
+>>101 ubeshort&0xf0 0x00 \bYONLY
+>>101 ubeshort&0xf0 0x10 \bYUV240
+>>101 ubeshort&0xf0 0x20 \bYWV422
+>>101 ubeshort&0xf0 0x30 \bYWV444
+>>101 ubeshort&0xf0 0x40 \bCMYK
+>>101 ubeshort&0xf0 0x50 \bCMYKDIRECT
+>>101 ubeshort&0xf0 0x60 \bNCOMPONENT
+>>101 ubeshort&0xf0 0x70 \bRGB
+>>101 ubeshort&0xf0 0x80 \bRGBE
+>>101 ubeshort&0xf0 >0x80 \b(reserved %#x)
+
+# From: Johan van der Knijff <johan.vanderknijff@kb.nl>
+#
+# BPG (Better Portable Graphics) format
+# https://bellard.org/bpg/
+# http://fileformats.archiveteam.org/wiki/BPG
+#
+0 string \x42\x50\x47\xFB BPG (Better Portable Graphics)
+!:mime image/bpg
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Apple_Icon_Image_format
+0 string icns Mac OS X icon
+!:mime image/x-icns
+!:apple ????icns
+!:ext icns
+>4 ubelong >0
+# file size
+>>4 ubelong x \b, %d bytes
+# icon type
+>>8 string x \b, "%4.4s" type
+
+# TIM images
+# URL: http://fileformats.archiveteam.org/wiki/TIM_(PlayStation_graphics)
+# Reference: https://mrclick.zophar.net/TilEd/download/timgfx.txt
+# Update: Joerg Jenderek
+# Note: called as "PSX TIM *bpp bitmap" by bitmap-tim-*.trid.xml
+# verified as "TIM PSX" by XnView `nconvert -fullinfo *.tim` and
+# by RECOIL `recoil2png -o TMP.PNG input.tim; file TMP.PNG` and often
+# as "PSX TIM" by ImageMagick version 7.1.0-10 command `identify *.tim`
+# here signed integers are used but according to Kaitai unsigned
+0 ulelong 0x00000010
+# 32 Flag bits *cttt; c~CLUT flag t~type 000~4BPP 001~8BPP 010~16BPP 011~24BPP 100~Mixed
+#>4 ulelong x FLAGS=%#x
+# 12+Size of CLUT (2Ch for 4BPP; 20Ch 40Ch 60Ch 80Ch C0Ch for 8BPP) or
+# +image data size (800Ch 2000Ch 2580C for 16BPP) (02000003h for dBase memo test.dbt)
+#>8 ulelong x \b, 12+CLUT or data size=%#8.8x
+# CLUT or data size remainder is 12 (Ch), but 03 for dBase memo test.dbt
+#>8 ubyte&0x0F =0x0C \b, SIZE REMAINDER IS 12
+# skip dBase III memo test.dbt with invalid flags 22D10189h
+>4 ulelong&0xffFFffF0 =0 Sony PlayStation PSX image,
+# file (version 5.40) labeled the above entry as "TIM image"
+!:mime image/x-sony-tim
+!:ext tim
+#>>4 ulelong&0x00000007 x \b, BPP~%u
+# 4BPP and 8BPP examples exist with CLUT or without CLUT
+>>4 ulelong&0x07 0x0 4-Bit,
+>>4 ulelong&0x07 0x1 8-Bit,
+# 16BPP and 24BPP examples have no CLUT
+>>4 ulelong 0x2 15-Bit,
+>>4 ulelong 0x3 24-Bit,
+# no example
+>>4 ulelong&0x07 0x4 Mixed-Bit,
+# CLUT flag set
+>>4 ulelong &8
+# 12 + size of CLUT like: 1000Ch 800Ch 400Ch 40Ch and 2FEh (KAGE.TIM)
+#>>>(8.l+8) ulelong x \b, 12+CLUT SIZE=%#8.8x
+>>>(8.l+12) uleshort x Pixel at (%d,
+>>>(8.l+14) uleshort x \b%d) Size=
+# image width (to get actual width multiply by 4 for 4BPP and by 2 for 8BPP)
+>>>>4 ulelong 0x8
+>>>>>(8.l+16) uleshort*4 x \b%d
+>>>>4 ulelong 0x9
+>>>>>(8.l+16) uleshort*2 x \b%d
+# image height like: 32 64 128 144 160 208 256
+>>>(8.l+18) uleshort x \bx%d,
+>>>4 ulelong 0x8 16 CLUT Entries at
+>>>4 ulelong 0x9 256 CLUT Entries at
+>>>12 uleshort x (%d,
+>>>14 uleshort x \b%d)
+# no Color LookUp Table (CLUT)
+>>4 ulelong ^8
+# image origin X Y
+>>>12 uleshort x Pixel at (%d,
+>>>14 uleshort x \b%d) Size=
+# real image width = multiply by 4 (4BPP) 2 (8BPP) 1 (16BPP) 2/3 (24BPP)
+>>>>4 ulelong 0x0
+>>>>>16 uleshort*4 x \b%d
+>>>>4 ulelong 0x1
+>>>>>16 uleshort*2 x \b%d
+>>>>4 ulelong 0x2
+>>>>>16 uleshort x \b%d
+>>>>4 ulelong 0x3
+# GRR: NOT working
+#>>>>>16 uleshort*2/3 x \b%d
+>>>>>16 uleshort x \b2/3*%d
+# mixed format width not explained!
+>>>>4 ulelong 0x4
+>>>>>16 uleshort x \b%d
+# image height like: 64 240 256
+>>>18 uleshort x \bx%d
+# TIM image data
+
+# MDEC streams
+0 ulelong 0x80010160 MDEC video stream,
+>16 uleshort x %dx
+>18 uleshort x \b%d
+#>8 ulelong x %d frames
+#>4 uleshort x secCount=%d;
+#>6 uleshort x nSectors=%d;
+#>12 ulelong x frameSize=%d;
+
+# BS encoded bitstreams
+2 uleshort 0x3800 BS image,
+# GRR: the above line is also true for binary Computer Graphics Metafile SAB00012.CGM with long parameter length 56 (=38h)
+>6 uleshort x Version %d,
+>4 uleshort x Quantization %d,
+>0 uleshort x (Decompresses to %d words)
+
+# Type: farbfeld image.
+# Url: http://tools.suckless.org/farbfeld/
+# From: Ian D. Scott <ian@iandouglasscott.com>
+#
+0 string farbfeld farbfeld image data,
+>8 ubelong x %dx
+>12 ubelong x \b%d
+
+# Type: Microsoft DirectDraw Surface (DXGI formats)
+# URL: https://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/DDSFileReference/ddsfileformat.asp
+# From: Morten Hustveit <morten@debian.org>
+# Updated by: David Korth <gerbilsoft@gerbilsoft.com>
+0 name ms-directdraw-dx10
+>0 ulelong x \b, DXGI format:
+>0 ulelong 1 R32G32B32A32_TYPELESS
+>0 ulelong 2 R32G32B32A32_FLOAT
+>0 ulelong 3 R32G32B32A32_UINT
+>0 ulelong 4 R32G32B32A32_SINT
+>0 ulelong 5 R32G32B32_TYPELESS
+>0 ulelong 6 R32G32B32_FLOAT
+>0 ulelong 7 R32G32B32_UINT
+>0 ulelong 8 R32G32B32_SINT
+>0 ulelong 9 R16G16B16A16_TYPELESS
+>0 ulelong 10 R16G16B16A16_FLOAT
+>0 ulelong 11 R16G16B16A16_UNORM
+>0 ulelong 12 R16G16B16A16_UINT
+>0 ulelong 13 R16G16B16A16_SNORM
+>0 ulelong 14 R16G16B16A16_SINT
+>0 ulelong 15 R32G32_TYPELESS
+>0 ulelong 16 R32G32_FLOAT
+>0 ulelong 17 R32G32_UINT
+>0 ulelong 18 R32G32_SINT
+>0 ulelong 19 R32G8X24_TYPELESS
+>0 ulelong 20 D32_FLOAT_S8X24_UINT
+>0 ulelong 21 R32_FLOAT_X8X24_TYPELESS
+>0 ulelong 22 X32_TYPELESS_G8X24_UINT
+>0 ulelong 23 R10G10B10A2_TYPELESS
+>0 ulelong 24 R10G10B10A2_UNORM
+>0 ulelong 25 R10G10B10A2_UINT
+>0 ulelong 26 R11G11B10_FLOAT
+>0 ulelong 27 R8G8B8A8_TYPELESS
+>0 ulelong 28 R8G8B8A8_UNORM
+>0 ulelong 29 R8G8B8A8_UNORM_SRGB
+>0 ulelong 30 R8G8B8A8_UINT
+>0 ulelong 31 R8G8B8A8_SNORM
+>0 ulelong 32 R8G8B8A8_SINT
+>0 ulelong 33 R16G16_TYPELESS
+>0 ulelong 34 R16G16_FLOAT
+>0 ulelong 35 R16G16_UNORM
+>0 ulelong 36 R16G16_UINT
+>0 ulelong 37 R16G16_SNORM
+>0 ulelong 38 R16G16_SINT
+>0 ulelong 39 R32_TYPELESS
+>0 ulelong 40 D32_FLOAT
+>0 ulelong 41 R32_FLOAT
+>0 ulelong 42 R32_UINT
+>0 ulelong 43 R32_SINT
+>0 ulelong 44 R24G8_TYPELESS
+>0 ulelong 45 D24_UNORM_S8_UINT
+>0 ulelong 46 R24_UNORM_X8_TYPELESS
+>0 ulelong 47 X24_TYPELESS_G8_UINT
+>0 ulelong 48 R8G8_TYPELESS
+>0 ulelong 49 R8G8_UNORM
+>0 ulelong 50 R8G8_UINT
+>0 ulelong 51 R8G8_SNORM
+>0 ulelong 52 R8G8_SINT
+>0 ulelong 53 R16_TYPELESS
+>0 ulelong 54 R16_FLOAT
+>0 ulelong 55 D16_UNORM
+>0 ulelong 56 R16_UNORM
+>0 ulelong 57 R16_UINT
+>0 ulelong 58 R16_SNORM
+>0 ulelong 59 R16_SINT
+>0 ulelong 60 R8_TYPELESS
+>0 ulelong 61 R8_UNORM
+>0 ulelong 62 R8_UINT
+>0 ulelong 63 R8_SNORM
+>0 ulelong 64 R8_SINT
+>0 ulelong 65 A8_UNORM
+>0 ulelong 66 R1_UNORM
+>0 ulelong 67 R9G9B9E5_SHAREDEXP
+>0 ulelong 68 R8G8_B8G8_UNORM
+>0 ulelong 69 G8R8_G8B8_UNORM
+>0 ulelong 70 BC1_TYPELESS
+>0 ulelong 71 BC1_UNORM
+>0 ulelong 72 BC1_UNORM_SRGB
+>0 ulelong 73 BC2_TYPELESS
+>0 ulelong 74 BC2_UNORM
+>0 ulelong 75 BC2_UNORM_SRGB
+>0 ulelong 76 BC3_TYPELESS
+>0 ulelong 77 BC3_UNORM
+>0 ulelong 78 BC3_UNORM_SRGB
+>0 ulelong 79 BC4_TYPELESS
+>0 ulelong 80 BC4_UNORM
+>0 ulelong 81 BC4_SNORM
+>0 ulelong 82 BC5_TYPELESS
+>0 ulelong 83 BC5_UNORM
+>0 ulelong 84 BC5_SNORM
+>0 ulelong 85 B5G6R5_UNORM
+>0 ulelong 86 B5G5R5A1_UNORM
+>0 ulelong 87 B8G8R8A8_UNORM
+>0 ulelong 88 B8G8R8X8_UNORM
+>0 ulelong 89 R10G10B10_XR_BIAS_A2_UNORM
+>0 ulelong 90 B8G8R8A8_TYPELESS
+>0 ulelong 91 B8G8R8A8_UNORM_SRGB
+>0 ulelong 92 B8G8R8X8_TYPELESS
+>0 ulelong 93 B8G8R8X8_UNORM_SRGB
+>0 ulelong 94 BC6H_TYPELESS
+>0 ulelong 95 BC6H_UF16
+>0 ulelong 96 BC6H_SF16
+>0 ulelong 97 BC7_TYPELESS
+>0 ulelong 98 BC7_UNORM
+>0 ulelong 99 BC7_UNORM_SRGB
+>0 ulelong 100 AYUV
+>0 ulelong 101 Y410
+>0 ulelong 102 Y416
+>0 ulelong 103 NV12
+>0 ulelong 104 P010
+>0 ulelong 105 P016
+>0 ulelong 106 420_OPAQUE
+>0 ulelong 107 YUY2
+>0 ulelong 108 Y210
+>0 ulelong 109 Y216
+>0 ulelong 110 NV11
+>0 ulelong 111 AI44
+>0 ulelong 112 IA44
+>0 ulelong 113 P8
+>0 ulelong 114 A8P8
+>0 ulelong 115 B4G4R4A4_UNORM
+
+>0 ulelong 116 XBOX_R10G10B10_7E3_A2_FLOAT
+>0 ulelong 117 XBOX_R10G10B10_6E4_A2_FLOAT
+>0 ulelong 118 XBOX_D16_UNORM_S8_UINT
+>0 ulelong 119 XBOX_R16_UNORM_X8_TYPELESS
+>0 ulelong 120 XBOX_X16_TYPELESS_G8_UINT
+
+>0 ulelong 130 DXGI_FORMAT_P208
+>0 ulelong 131 DXGI_FORMAT_V208
+>0 ulelong 132 DXGI_FORMAT_V408
+
+>0 ulelong 133 ASTC_4X4_TYPELESS
+>0 ulelong 134 ASTC_4X4_UNORM
+>0 ulelong 135 ASTC_4X4_UNORM_SRGB
+>0 ulelong 137 ASTC_5X4_TYPELESS
+>0 ulelong 138 ASTC_5X4_UNORM
+>0 ulelong 139 ASTC_5X4_UNORM_SRGB
+>0 ulelong 141 ASTC_5X5_TYPELESS
+>0 ulelong 142 ASTC_5X5_UNORM
+>0 ulelong 143 ASTC_5X5_UNORM_SRGB
+>0 ulelong 145 ASTC_6X5_TYPELESS
+>0 ulelong 146 ASTC_6X5_UNORM
+>0 ulelong 147 ASTC_6X5_UNORM_SRGB
+>0 ulelong 149 ASTC_6X6_TYPELESS
+>0 ulelong 150 ASTC_6X6_UNORM
+>0 ulelong 151 ASTC_6X6_UNORM_SRGB
+>0 ulelong 153 ASTC_8X5_TYPELESS
+>0 ulelong 154 ASTC_8X5_UNORM
+>0 ulelong 155 ASTC_8X5_UNORM_SRGB
+>0 ulelong 157 ASTC_8X6_TYPELESS
+>0 ulelong 158 ASTC_8X6_UNORM
+>0 ulelong 159 ASTC_8X6_UNORM_SRGB
+>0 ulelong 161 ASTC_8X8_TYPELESS
+>0 ulelong 162 ASTC_8X8_UNORM
+>0 ulelong 163 ASTC_8X8_UNORM_SRGB
+>0 ulelong 165 ASTC_10X5_TYPELESS
+>0 ulelong 166 ASTC_10X5_UNORM
+>0 ulelong 167 ASTC_10X5_UNORM_SRGB
+>0 ulelong 169 ASTC_10X6_TYPELESS
+>0 ulelong 170 ASTC_10X6_UNORM
+>0 ulelong 171 ASTC_10X6_UNORM_SRGB
+>0 ulelong 173 ASTC_10X8_TYPELESS
+>0 ulelong 174 ASTC_10X8_UNORM
+>0 ulelong 175 ASTC_10X8_UNORM_SRGB
+>0 ulelong 177 ASTC_10X10_TYPELESS
+>0 ulelong 178 ASTC_10X10_UNORM
+>0 ulelong 179 ASTC_10X10_UNORM_SRGB
+>0 ulelong 181 ASTC_12X10_TYPELESS
+>0 ulelong 182 ASTC_12X10_UNORM
+>0 ulelong 183 ASTC_12X10_UNORM_SRGB
+>0 ulelong 185 ASTC_12X12_TYPELESS
+>0 ulelong 186 ASTC_12X12_UNORM
+>0 ulelong 187 ASTC_12X12_UNORM_SRGB
+
+>0 ulelong 190 XBOX_R10G10B10_SNORM_A2_UNORM
+>0 ulelong 189 XBOX_R4G4_UNORM
+>0 ulelong 0xFFFFFFFF DXGI_FORMAT_FORCE_UINT
+
+# Type: Microsoft DirectDraw Surface (common data)
+# URL: https://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/DDSFileReference/ddsfileformat.asp
+# From: Morten Hustveit <morten@debian.org>
+# Updated by: David Korth <gerbilsoft@gerbilsoft.com>
+0 name ms-directdraw-surface
+>0x10 ulelong x %u x
+>0x0C ulelong x %u
+# Color depth.
+>0x58 ulelong >0 \b, %u-bit color
+# Determine the pixel format.
+>0x50 ulelong&0x4 4
+# FIXME: Handle DX10 and XBOX formats.
+>>0x54 string DX10
+>>>0x80 use ms-directdraw-dx10
+>>0x54 string !DX10 \b, compressed using %.4s
+>0x50 ulelong&0x2 0x2 \b, alpha only
+>0x50 ulelong&0x200 0x200 \b, YUV
+>0x50 ulelong&0x20000 0x20000 \b, luminance
+# RGB pixel format
+>0x50 ulelong&0x40 0x40
+
+# Determine the RGB format using the color masks.
+# ulequad order: 0xGGGGGGGGRRRRRRRR, 0xAAAAAAAABBBBBBBB
+
+>>0x58 ulelong 16
+
+# NOTE: 15-bit color formats usually have 16-bit listed as the color depth.
+>>>0x5C ulequad 0x000003E000007C00
+>>>>0x64 ulequad 0x000000000000001F \b, RGB555
+>>>0x5C ulequad 0x000003E000001F00
+>>>>0x64 ulequad 0x000000000000007C \b, BGR555
+
+>>>0x5C ulequad 0x000007E00000F800
+>>>>0x64 ulequad 0x000000000000001F \b, RGB565
+>>>0x5C ulequad 0x000007E000001F00
+>>>>0x64 ulequad 0x00000000000000F8 \b, BGR565
+
+>>>0x5C ulequad 0x000000F000000F00
+>>>>0x64 ulequad 0x0000F0000000000F \b, ARGB4444
+>>>0x5C ulequad 0x000000F00000000F
+>>>>0x64 ulequad 0x0000F00000000F00 \b, ABGR4444
+
+>>>0x5C ulequad 0x00000F000000F000
+>>>>0x64 ulequad 0x0000000F000000F0 \b, RGBA4444
+>>>0x5C ulequad 0x00000F00000000F0
+>>>>0x64 ulequad 0x0000000F0000F000 \b, BGRA4444
+
+>>>0x5C ulequad 0x000000F000000F00
+>>>>0x64 ulequad 0x000000000000000F \b, xRGB4444
+>>>0x5C ulequad 0x000000F00000000F
+>>>>0x64 ulequad 0x0000000000000F00 \b, xBGR4444
+
+>>>0x5C ulequad 0x00000F000000F000
+>>>>0x64 ulequad 0x00000000000000F0 \b, RGBx4444
+>>>0x5C ulequad 0x00000F00000000F0
+>>>>0x64 ulequad 0x000000000000F000 \b, BGRx4444
+
+>>>0x5C ulequad 0x000003E000007C00
+>>>>0x64 ulequad 0x000080000000001F \b, ARGB1555
+>>>0x5C ulequad 0x000003E000001F00
+>>>>0x64 ulequad 0x000080000000007C \b, ABGR1555
+>>>0x5C ulequad 0x000007C00000F800
+>>>>0x64 ulequad 0x000000010000003E \b, RGBA5551
+>>>0x5C ulequad 0x000007C00000003E
+>>>>0x64 ulequad 0x000000010000F800 \b, BGRA5551
+
+>>88 ulelong 24
+>>>0x5C ulequad 0x0000FF0000FF0000
+>>>>0x64 ulequad 0x00000000000000FF \b, RGB888
+>>>0x5C ulequad 0x0000FF00000000FF
+>>>>0x64 ulequad 0x0000000000FF0000 \b, BGR888
+
+>>88 ulelong 32
+>>>0x5C ulequad 0x0000FF0000FF0000
+>>>>0x64 ulequad 0xFF000000000000FF \b, ARGB8888
+>>>0x5C ulequad 0x0000FF00000000FF
+>>>>0x64 ulequad 0xFF00000000FF0000 \b, ABGR8888
+
+>>>0x5C ulequad 0x00FF0000FF000000
+>>>>0x64 ulequad 0x000000FF0000FF00 \b, RGBA8888
+>>>0x5C ulequad 0x00FF00000000FF00
+>>>>0x64 ulequad 0x000000FFFF000000 \b, BGBA8888
+
+>>>0x5C ulequad 0x0000FF0000FF0000
+>>>>0x64 ulequad 0x00000000000000FF \b, xRGB8888
+>>>0x5C ulequad 0x0000FF00000000FF
+>>>>0x64 ulequad 0x0000000000FF0000 \b, xBGR8888
+
+>>>0x5C ulequad 0x00FF0000FF000000
+>>>>0x64 ulequad 0x000000000000FF00 \b, RGBx8888
+>>>0x5C ulequad 0x00FF00000000FF00
+>>>>0x64 ulequad 0x00000000FF000000 \b, BGBx8888
+
+# Less common 32-bit color formats.
+>>>0x5C ulequad 0xFFFF00000000FFFF
+>>>>0x64 ulequad 0x0000000000000000 \b, G16R16
+>>>0x5C ulequad 0x0000FFFFFFFF0000
+>>>>0x64 ulequad 0x0000000000000000 \b, R16G16
+
+>>>0x5C ulequad 0x000FFC003FF00000
+>>>>0x64 ulequad 0xC0000000000003FF \b, A2R10G10B10
+>>>0x5C ulequad 0x000FFC00000003FF
+>>>>0x64 ulequad 0xC00000003FF00000 \b, A2B10G10R10
+
+# Type: Microsoft DirectDraw Surface
+# URL: https://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/graphics/reference/DDSFileReference/ddsfileformat.asp
+# From: Morten Hustveit <morten@debian.org>
+# Updated by: David Korth <gerbilsoft@gerbilsoft.com>
+0 string/b DDS\040\174\000\000\000 Microsoft DirectDraw Surface (DDS):
+>0 use ms-directdraw-surface
+
+# Type: Sega PVR image.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://fabiensanglard.net/Mykaruga/tools/segaPVRFormat.txt
+# - https://github.com/yazgoo/pvrx2png
+# - https://github.com/nickworonekin/puyotools
+
+# Sega PVR header.
+0 name sega-pvr-image-header
+>0x0C uleshort x %u x
+>0x0E uleshort x %u
+# Image format.
+>0x08 ubyte 0 \b, ARGB1555
+>0x08 ubyte 1 \b, RGB565
+>0x08 ubyte 2 \b, ARGB4444
+>0x08 ubyte 3 \b, YUV442
+>0x08 ubyte 4 \b, Bump
+>0x08 ubyte 5 \b, 4bpp
+>0x08 ubyte 6 \b, 8bpp
+# Image data type.
+>0x09 ubyte 0x01 \b, square twiddled
+>0x09 ubyte 0x02 \b, square twiddled & mipmap
+>0x09 ubyte 0x03 \b, VQ
+>0x09 ubyte 0x04 \b, VQ & mipmap
+>0x09 ubyte 0x05 \b, 8-bit CLUT twiddled
+>0x09 ubyte 0x06 \b, 4-bit CLUT twiddled
+>0x09 ubyte 0x07 \b, 8-bit direct twiddled
+>0x09 ubyte 0x08 \b, 4-bit direct twiddled
+>0x09 ubyte 0x09 \b, rectangle
+>0x09 ubyte 0x0B \b, rectangular stride
+>0x09 ubyte 0x0D \b, rectangular twiddled
+>0x09 ubyte 0x10 \b, small VQ
+>0x09 ubyte 0x11 \b, small VQ & mipmap
+>0x09 ubyte 0x12 \b, square twiddled & mipmap
+
+# Sega PVR image.
+0 string PVRT
+>0x10 string DDS\040\174\000\000\000 Sega PVR (Xbox) image:
+>>0x20 use ms-directdraw-surface
+>0x10 ubelong !0x44445320 Sega PVR image:
+>>0 use sega-pvr-image-header
+
+# Sega PVR image with GBIX.
+0 string GBIX
+>0x10 string PVRT
+>>0x10 string DDS\040\174\000\000\000 Sega PVR (Xbox) image:
+>>>0x20 use ms-directdraw-surface
+>>0x10 ubelong !0x44445320 Sega PVR image:
+>>>0x10 use sega-pvr-image-header
+>>0x08 ulelong x \b, global index = %u
+
+# Sega GVR header.
+0 name sega-gvr-image-header
+>0x0C ubeshort x %u x
+>0x0E ubeshort x %u
+# Image data format.
+>0x0B ubyte 0 \b, I4
+>0x0B ubyte 1 \b, I8
+>0x0B ubyte 2 \b, IA4
+>0x0B ubyte 3 \b, IA8
+>0x0B ubyte 4 \b, RGB565
+>0x0B ubyte 5 \b, RGB5A3
+>0x0B ubyte 6 \b, ARGB8888
+>0x0B ubyte 8 \b, CI4
+>0x0B ubyte 9 \b, CI8
+>0x0B ubyte 14 \b, DXT1
+
+# Sega GVR image.
+0 string GVRT Sega GVR image:
+>0x10 use sega-gvr-image-header
+
+# Sega GVR image with GBIX.
+0 string GBIX
+>0x10 string GVRT Sega GVR image:
+>>0x10 use sega-gvr-image-header
+>>0x08 ubelong x \b, global index = %u
+
+# Sega GVR image with GCIX. (Wii)
+0 string GCIX
+>0x10 string GVRT Sega GVR image:
+>>0x10 use sega-gvr-image-header
+>>0x08 ubelong x \b, global index = %u
+
+# Light Field Picture
+# Documentation: http://optics.miloush.net/lytro/TheFileFormat.aspx
+# Typical file extensions: .lfp .lfr .lfx
+
+0 ubelong 0x894C4650
+>4 ubelong 0x0D0A1A0A
+>12 ubelong 0x00000000 Lytro Light Field Picture
+>8 ubelong x \b, version %d
+
+# Type: Vision Research Phantom CINE Format
+# URL: https://www.phantomhighspeed.com/
+# URL2: http://phantomhighspeed.force.com/vriknowledge/servlet/fileField?id=0BEU0000000Cfyk
+# From: Harry Mallon <hjmallon at gmail.com>
+#
+# This has a short "CI" code but the 44 is the size of the struct which is
+# stable
+0 string CI
+>2 uleshort 44 Vision Research CINE Video,
+>>4 uleshort 0 Grayscale,
+>>4 uleshort 1 JPEG Compressed,
+>>4 uleshort 2 RAW,
+>>6 uleshort x version %d,
+>>20 ulelong x %d frames,
+>>48 ulelong x %dx
+>>52 ulelong x \b%d
+
+# Type: ARRI Raw Image
+# Info: SMPTE RDD30:2014
+# From: Harry Mallon <hjmallon at gmail.com>
+0 string ARRI ARRI ARI image data,
+>4 ulelong 0x78563412 little-endian,
+>4 ulelong 0x12345678 big-endian,
+>12 ulelong x version %d,
+>20 ulelong x %dx
+>24 ulelong x \b%d
+
+# Type: Khronos KTX texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Reference: https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+
+# glEnum decoding.
+# NOTE: Only the most common formats are listed here.
+0 name khronos-ktx-glEnum
+>0 ulelong 0x1907 \b, RGB
+>0 ulelong 0x1908 \b, RGBA
+>0 ulelong 0x1909 \b, LUMINANCE
+>0 ulelong 0x190A \b, LUMINANCE_ALPHA
+>0 ulelong 0x80E1 \b, BGR
+>0 ulelong 0x80E2 \b, BGRA
+>0 ulelong 0x83A0 \b, RGB_S3TC
+>0 ulelong 0x83A1 \b, RGB4_S3TC
+>0 ulelong 0x83A2 \b, RGBA_S3TC
+>0 ulelong 0x83A3 \b, RGBA4_S3TC
+>0 ulelong 0x83A4 \b, RGBA_DXT5_S3TC
+>0 ulelong 0x83A5 \b, RGBA4_DXT5_S3TC
+>0 ulelong 0x83F0 \b, COMPRESSED_RGB_S3TC_DXT1_EXT
+>0 ulelong 0x83F1 \b, COMPRESSED_RGBA_S3TC_DXT1_EXT
+>0 ulelong 0x83F2 \b, COMPRESSED_RGBA_S3TC_DXT3_EXT
+>0 ulelong 0x83F3 \b, COMPRESSED_RGBA_S3TC_DXT5_EXT
+>0 ulelong 0x8D64 \b, ETC1_RGB8_OES
+>0 ulelong 0x9270 \b, COMPRESSED_R11_EAC
+>0 ulelong 0x9271 \b, COMPRESSED_SIGNED_R11_EAC
+>0 ulelong 0x9272 \b, COMPRESSED_RG11_EAC
+>0 ulelong 0x9273 \b, COMPRESSED_SIGNED_RG11_EAC
+>0 ulelong 0x9274 \b, COMPRESSED_RGB8_ETC2
+>0 ulelong 0x9275 \b, COMPRESSED_SRGB8_ETC2
+>0 ulelong 0x9276 \b, COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
+>0 ulelong 0x9277 \b, COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2
+>0 ulelong 0x9278 \b, COMPRESSED_RGBA2_ETC2_EAC
+>0 ulelong 0x9279 \b, COMPRESSED_SRGB8_ALPHA8_ETC2_EAC
+>0 ulelong 0x93B0 \b, COMPRESSED_RGBA_ASTC_4x4_KHR
+>0 ulelong 0x93B1 \b, COMPRESSED_RGBA_ASTC_5x4_KHR
+>0 ulelong 0x93B2 \b, COMPRESSED_RGBA_ASTC_5x5_KHR
+>0 ulelong 0x93B3 \b, COMPRESSED_RGBA_ASTC_6x5_KHR
+>0 ulelong 0x93B4 \b, COMPRESSED_RGBA_ASTC_6x6_KHR
+>0 ulelong 0x93B5 \b, COMPRESSED_RGBA_ASTC_8x5_KHR
+>0 ulelong 0x93B6 \b, COMPRESSED_RGBA_ASTC_8x6_KHR
+>0 ulelong 0x93B7 \b, COMPRESSED_RGBA_ASTC_8x8_KHR
+>0 ulelong 0x93B8 \b, COMPRESSED_RGBA_ASTC_10x5_KHR
+>0 ulelong 0x93B9 \b, COMPRESSED_RGBA_ASTC_10x6_KHR
+>0 ulelong 0x93BA \b, COMPRESSED_RGBA_ASTC_10x8_KHR
+>0 ulelong 0x93BB \b, COMPRESSED_RGBA_ASTC_10x10_KHR
+>0 ulelong 0x93BC \b, COMPRESSED_RGBA_ASTC_12x10_KHR
+>0 ulelong 0x93BD \b, COMPRESSED_RGBA_ASTC_12x12_KHR
+>0 ulelong 0x93D0 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR
+>0 ulelong 0x93D1 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR
+>0 ulelong 0x93D2 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR
+>0 ulelong 0x93D3 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR
+>0 ulelong 0x93D4 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR
+>0 ulelong 0x93D5 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR
+>0 ulelong 0x93D6 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR
+>0 ulelong 0x93D7 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR
+>0 ulelong 0x93D8 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR
+>0 ulelong 0x93D9 \b, COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR
+>0 ulelong 0x93DA \b, COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR
+>0 ulelong 0x93DB \b, COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR
+>0 ulelong 0x93DC \b, COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR
+>0 ulelong 0x93DD \b, COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR
+
+# Endian-specific KTX header.
+# TODO: glType (all textures I've seen so far are GL_UNSIGNED_BYTE)
+0 name khronos-ktx-endian-header
+>20 ulelong x \b, %u
+>24 ulelong >1 x %u
+>28 ulelong >1 x %u
+>8 ulelong >0
+>>8 use khronos-ktx-glEnum
+>8 ulelong 0
+>>12 use khronos-ktx-glEnum
+
+# Main KTX header.
+# Determine endianness, then check the rest of the header.
+0 string \xABKTX\ 11\xBB\r\n\x1A\n Khronos KTX texture
+>12 ulelong 0x04030201 (little-endian)
+>>16 use khronos-ktx-endian-header
+>12 ubelong 0x04030201 (big-endian)
+>>16 use \^khronos-ktx-endian-header
+
+# Type: Khronos KTX2 texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Based on draft19.
+# Reference: http://github.khronos.org/KTX-Specification/
+
+# Supercompression enum.
+0 name khronos-ktx2-supercompression
+>0 ulelong 1 BasisLZ
+>0 ulelong 2 Zstandard
+>0 ulelong 3 ZLIB
+
+# Vulkan format identifier.
+# NOTE: Formats prohibited from KTX2 are commented out.
+0 name khronos-ktx2-vkFormat
+>0 ulelong 0 UNDEFINED
+>0 ulelong 1 R4G4_UNORM_PACK8
+>0 ulelong 2 R4G4B4A4_UNORM_PACK16
+>0 ulelong 3 B4G4R4A4_UNORM_PACK16
+>0 ulelong 4 R5G6B5_UNORM_PACK16
+>0 ulelong 5 B5G6R5_UNORM_PACK16
+>0 ulelong 6 R5G5B5A1_UNORM_PACK16
+>0 ulelong 7 B5G5R5A1_UNORM_PACK16
+>0 ulelong 8 A1R5G5B5_UNORM_PACK16
+>0 ulelong 9 R8_UNORM
+>0 ulelong 10 R8_SNORM
+#>0 ulelong 11 R8_USCALED
+#>0 ulelong 12 R8_SSCALED
+>0 ulelong 13 R8_UINT
+>0 ulelong 14 R8_SINT
+>0 ulelong 15 R8_SRGB
+>0 ulelong 16 R8G8_UNORM
+>0 ulelong 17 R8G8_SNORM
+#>0 ulelong 18 R8G8_USCALED
+#>0 ulelong 19 R8G8_SSCALED
+>0 ulelong 20 R8G8_UINT
+>0 ulelong 21 R8G8_SINT
+>0 ulelong 22 R8G8_SRGB
+>0 ulelong 23 R8G8B8_UNORM
+>0 ulelong 24 R8G8B8_SNORM
+#>0 ulelong 25 R8G8B8_USCALED
+#>0 ulelong 26 R8G8B8_SSCALED
+>0 ulelong 27 R8G8B8_UINT
+>0 ulelong 28 R8G8B8_SINT
+>0 ulelong 29 R8G8B8_SRGB
+>0 ulelong 30 B8G8R8_UNORM
+>0 ulelong 31 B8G8R8_SNORM
+#>0 ulelong 32 B8G8R8_USCALED
+#>0 ulelong 33 B8G8R8_SSCALED
+>0 ulelong 34 B8G8R8_UINT
+>0 ulelong 35 B8G8R8_SINT
+>0 ulelong 36 B8G8R8_SRGB
+>0 ulelong 37 R8G8B8A8_UNORM
+>0 ulelong 38 R8G8B8A8_SNORM
+#>0 ulelong 39 R8G8B8A8_USCALED
+#>0 ulelong 40 R8G8B8A8_SSCALED
+>0 ulelong 41 R8G8B8A8_UINT
+>0 ulelong 42 R8G8B8A8_SINT
+>0 ulelong 43 R8G8B8A8_SRGB
+>0 ulelong 44 B8G8R8A8_UNORM
+>0 ulelong 45 B8G8R8A8_SNORM
+#>0 ulelong 46 B8G8R8A8_USCALED
+#>0 ulelong 47 B8G8R8A8_SSCALED
+>0 ulelong 48 B8G8R8A8_UINT
+>0 ulelong 49 B8G8R8A8_SINT
+>0 ulelong 50 B8G8R8A8_SRGB
+#>0 ulelong 51 A8B8G8R8_UNORM_PACK32
+#>0 ulelong 52 A8B8G8R8_SNORM_PACK32
+#>0 ulelong 53 A8B8G8R8_USCALED_PACK32
+#>0 ulelong 54 A8B8G8R8_SSCALED_PACK32
+#>0 ulelong 55 A8B8G8R8_UINT_PACK32
+#>0 ulelong 56 A8B8G8R8_SINT_PACK32
+#>0 ulelong 57 A8B8G8R8_SRGB_PACK32
+>0 ulelong 58 A2R10G10B10_UNORM_PACK32
+>0 ulelong 59 A2R10G10B10_SNORM_PACK32
+#>0 ulelong 60 A2R10G10B10_USCALED_PACK32
+#>0 ulelong 61 A2R10G10B10_SSCALED_PACK32
+>0 ulelong 62 A2R10G10B10_UINT_PACK32
+>0 ulelong 63 A2R10G10B10_SINT_PACK32
+>0 ulelong 64 A2B10G10R10_UNORM_PACK32
+>0 ulelong 65 A2B10G10R10_SNORM_PACK32
+#>0 ulelong 66 A2B10G10R10_USCALED_PACK32
+#>0 ulelong 67 A2B10G10R10_SSCALED_PACK32
+>0 ulelong 68 A2B10G10R10_UINT_PACK32
+>0 ulelong 69 A2B10G10R10_SINT_PACK32
+>0 ulelong 70 R16_UNORM
+>0 ulelong 71 R16_SNORM
+#>0 ulelong 72 R16_USCALED
+#>0 ulelong 73 R16_SSCALED
+>0 ulelong 74 R16_UINT
+>0 ulelong 75 R16_SINT
+>0 ulelong 76 R16_SFLOAT
+>0 ulelong 77 R16G16_UNORM
+>0 ulelong 78 R16G16_SNORM
+#>0 ulelong 79 R16G16_USCALED
+#>0 ulelong 80 R16G16_SSCALED
+>0 ulelong 81 R16G16_UINT
+>0 ulelong 82 R16G16_SINT
+>0 ulelong 83 R16G16_SFLOAT
+>0 ulelong 84 R16G16B16_UNORM
+>0 ulelong 85 R16G16B16_SNORM
+#>0 ulelong 86 R16G16B16_USCALED
+#>0 ulelong 87 R16G16B16_SSCALED
+>0 ulelong 88 R16G16B16_UINT
+>0 ulelong 89 R16G16B16_SINT
+>0 ulelong 90 R16G16B16_SFLOAT
+>0 ulelong 91 R16G16B16A16_UNORM
+>0 ulelong 92 R16G16B16A16_SNORM
+#>0 ulelong 93 R16G16B16A16_USCALED
+#>0 ulelong 94 R16G16B16A16_SSCALED
+>0 ulelong 95 R16G16B16A16_UINT
+>0 ulelong 96 R16G16B16A16_SINT
+>0 ulelong 97 R16G16B16A16_SFLOAT
+>0 ulelong 98 R32_UINT
+>0 ulelong 99 R32_SINT
+>0 ulelong 100 R32_SFLOAT
+>0 ulelong 101 R32G32_UINT
+>0 ulelong 102 R32G32_SINT
+>0 ulelong 103 R32G32_SFLOAT
+>0 ulelong 104 R32G32B32_UINT
+>0 ulelong 105 R32G32B32_SINT
+>0 ulelong 106 R32G32B32_SFLOAT
+>0 ulelong 107 R32G32B32A32_UINT
+>0 ulelong 108 R32G32B32A32_SINT
+>0 ulelong 109 R32G32B32A32_SFLOAT
+>0 ulelong 110 R64_UINT
+>0 ulelong 111 R64_SINT
+>0 ulelong 112 R64_SFLOAT
+>0 ulelong 113 R64G64_UINT
+>0 ulelong 114 R64G64_SINT
+>0 ulelong 115 R64G64_SFLOAT
+>0 ulelong 116 R64G64B64_UINT
+>0 ulelong 117 R64G64B64_SINT
+>0 ulelong 118 R64G64B64_SFLOAT
+>0 ulelong 119 R64G64B64A64_UINT
+>0 ulelong 120 R64G64B64A64_SINT
+>0 ulelong 121 R64G64B64A64_SFLOAT
+>0 ulelong 122 B10G11R11_UFLOAT_PACK32
+>0 ulelong 123 E5B9G9R9_UFLOAT_PACK32
+>0 ulelong 124 D16_UNORM
+>0 ulelong 125 X8_D24_UNORM_PACK32
+>0 ulelong 126 D32_SFLOAT
+>0 ulelong 127 S8_UINT
+>0 ulelong 128 D16_UNORM_S8_UINT
+>0 ulelong 129 D24_UNORM_S8_UINT
+>0 ulelong 130 D32_SFLOAT_S8_UINT
+
+>0 ulelong 131 BC1_RGB_UNORM_BLOCK
+>0 ulelong 132 BC1_RGB_SRGB_BLOCK
+>0 ulelong 133 BC1_RGBA_UNORM_BLOCK
+>0 ulelong 134 BC1_RGBA_SRGB_BLOCK
+>0 ulelong 135 BC2_UNORM_BLOCK
+>0 ulelong 136 BC2_SRGB_BLOCK
+>0 ulelong 137 BC3_UNORM_BLOCK
+>0 ulelong 138 BC3_SRGB_BLOCK
+>0 ulelong 139 BC4_UNORM_BLOCK
+>0 ulelong 140 BC4_SNORM_BLOCK
+>0 ulelong 141 BC5_UNORM_BLOCK
+>0 ulelong 142 BC5_SNORM_BLOCK
+>0 ulelong 143 BC6H_UFLOAT_BLOCK
+>0 ulelong 144 BC6H_SFLOAT_BLOCK
+>0 ulelong 145 BC7_UNORM_BLOCK
+>0 ulelong 146 BC7_SRGB_BLOCK
+
+>0 ulelong 147 ETC2_R8G8B8_UNORM_BLOCK
+>0 ulelong 148 ETC2_R8G8B8_SRGB_BLOCK
+>0 ulelong 149 ETC2_R8G8B8A1_UNORM_BLOCK
+>0 ulelong 150 ETC2_R8G8B8A1_SRGB_BLOCK
+>0 ulelong 151 ETC2_R8G8B8A8_UNORM_BLOCK
+>0 ulelong 152 ETC2_R8G8B8A8_SRGB_BLOCK
+
+>0 ulelong 153 EAC_R11_UNORM_BLOCK
+>0 ulelong 154 EAC_R11_SNORM_BLOCK
+>0 ulelong 155 EAC_R11G11_UNORM_BLOCK
+>0 ulelong 156 EAC_R11G11_SNORM_BLOCK
+
+>0 ulelong 157 ASTC_4x4_UNORM_BLOCK
+>0 ulelong 158 ASTC_4x4_SRGB_BLOCK
+>0 ulelong 159 ASTC_5x4_UNORM_BLOCK
+>0 ulelong 160 ASTC_5x4_SRGB_BLOCK
+>0 ulelong 161 ASTC_5x5_UNORM_BLOCK
+>0 ulelong 162 ASTC_5x5_SRGB_BLOCK
+>0 ulelong 163 ASTC_6x5_UNORM_BLOCK
+>0 ulelong 164 ASTC_6x5_SRGB_BLOCK
+>0 ulelong 165 ASTC_6x6_UNORM_BLOCK
+>0 ulelong 166 ASTC_6x6_SRGB_BLOCK
+>0 ulelong 167 ASTC_8x5_UNORM_BLOCK
+>0 ulelong 168 ASTC_8x5_SRGB_BLOCK
+>0 ulelong 169 ASTC_8x6_UNORM_BLOCK
+>0 ulelong 170 ASTC_8x6_SRGB_BLOCK
+>0 ulelong 171 ASTC_8x8_UNORM_BLOCK
+>0 ulelong 172 ASTC_8x8_SRGB_BLOCK
+>0 ulelong 173 ASTC_10x5_UNORM_BLOCK
+>0 ulelong 174 ASTC_10x5_SRGB_BLOCK
+>0 ulelong 175 ASTC_10x6_UNORM_BLOCK
+>0 ulelong 176 ASTC_10x6_SRGB_BLOCK
+>0 ulelong 177 ASTC_10x8_UNORM_BLOCK
+>0 ulelong 178 ASTC_10x8_SRGB_BLOCK
+>0 ulelong 179 ASTC_10x10_UNORM_BLOCK
+>0 ulelong 180 ASTC_10x10_SRGB_BLOCK
+>0 ulelong 181 ASTC_12x10_UNORM_BLOCK
+>0 ulelong 182 ASTC_12x10_SRGB_BLOCK
+>0 ulelong 183 ASTC_12x12_UNORM_BLOCK
+>0 ulelong 184 ASTC_12x12_SRGB_BLOCK
+
+>0 ulelong 1000156000 G8B8G8R8_422_UNORM
+>0 ulelong 1000156001 B8G8R8G8_422_UNORM
+>0 ulelong 1000156002 G8_B8_R8_3PLANE_420_UNORM
+>0 ulelong 1000156003 G8_B8R8_2PLANE_420_UNORM
+>0 ulelong 1000156004 G8_B8_R8_3PLANE_422_UNORM
+>0 ulelong 1000156005 G8_B8R8_2PLANE_422_UNORM
+>0 ulelong 1000156006 G8_B8_R8_3PLANE_444_UNORM
+>0 ulelong 1000156007 R10X6_UNORM_PACK16
+>0 ulelong 1000156008 R10X6G10X6_UNORM_2PACK16
+>0 ulelong 1000156009 R10X6G10X6B10X6A10X6_UNORM_4PACK16
+>0 ulelong 1000156010 G10X6B10X6G10X6R10X6_422_UNORM_4PACK16
+>0 ulelong 1000156011 B10X6G10X6R10X6G10X6_422_UNORM_4PACK16
+>0 ulelong 1000156012 G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16
+>0 ulelong 1000156013 G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16
+>0 ulelong 1000156014 G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16
+>0 ulelong 1000156015 G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16
+>0 ulelong 1000156016 G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16
+>0 ulelong 1000156017 R12X4_UNORM_PACK16
+>0 ulelong 1000156018 R12X4G12X4_UNORM_2PACK16
+>0 ulelong 1000156019 R12X4G12X4B12X4A12X4_UNORM_4PACK16
+>0 ulelong 1000156020 G12X4B12X4G12X4R12X4_422_UNORM_4PACK16
+>0 ulelong 1000156021 B12X4G12X4R12X4G12X4_422_UNORM_4PACK16
+>0 ulelong 1000156022 G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16
+>0 ulelong 1000156023 G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16
+>0 ulelong 1000156024 G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16
+>0 ulelong 1000156025 G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16
+>0 ulelong 1000156026 G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16
+>0 ulelong 1000156027 G16B16G16R16_422_UNORM
+>0 ulelong 1000156028 B16G16R16G16_422_UNORM
+>0 ulelong 1000156029 G16_B16_R16_3PLANE_420_UNORM
+>0 ulelong 1000156030 G16_B16R16_2PLANE_420_UNORM
+>0 ulelong 1000156031 G16_B16_R16_3PLANE_422_UNORM
+>0 ulelong 1000156032 G16_B16R16_2PLANE_422_UNORM
+>0 ulelong 1000156033 G16_B16_R16_3PLANE_444_UNORM
+
+>0 ulelong 1000054000 PVRTC1_2BPP_UNORM_BLOCK_IMG
+>0 ulelong 1000054001 PVRTC1_4BPP_UNORM_BLOCK_IMG
+>0 ulelong 1000054002 PVRTC2_2BPP_UNORM_BLOCK_IMG
+>0 ulelong 1000054003 PVRTC2_4BPP_UNORM_BLOCK_IMG
+>0 ulelong 1000054004 PVRTC1_2BPP_SRGB_BLOCK_IMG
+>0 ulelong 1000054005 PVRTC1_4BPP_SRGB_BLOCK_IMG
+>0 ulelong 1000054006 PVRTC2_2BPP_SRGB_BLOCK_IMG
+>0 ulelong 1000054007 PVRTC2_4BPP_SRGB_BLOCK_IMG
+
+>0 ulelong 1000066000 ASTC_4x4_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066001 ASTC_5x4_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066002 ASTC_5x5_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066003 ASTC_6x5_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066004 ASTC_6x6_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066005 ASTC_8x5_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066006 ASTC_8x6_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066007 ASTC_8x8_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066008 ASTC_10x5_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066009 ASTC_10x6_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066010 ASTC_10x8_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066011 ASTC_10x10_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066012 ASTC_12x10_SFLOAT_BLOCK_EXT
+>0 ulelong 1000066013 ASTC_12x12_SFLOAT_BLOCK_EXT
+
+# Main KTX2 header.
+0 string \xABKTX\ 20\xBB\r\n\x1A\n Khronos KTX2 texture
+>20 ulelong x \b, %u
+>24 ulelong >1 x %u
+>28 ulelong >1 x %u
+>32 ulelong >1 \b, %u layers
+>36 ulelong >1 \b, %u faces
+>40 ulelong >1 \b, %u mipmaps
+>44 ulelong >0 \b,
+>>44 use khronos-ktx2-supercompression
+>12 ulelong >0 \b,
+>>12 use khronos-ktx2-vkFormat
+
+# Type: Valve VTF texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://developer.valvesoftware.com/wiki/Valve_Texture_Format
+
+# VTF image formats.
+0 name vtf-image-format
+>0 ulelong 0 RGBA8888
+>0 ulelong 1 ABGR8888
+>0 ulelong 2 RGB888
+>0 ulelong 3 BGR888
+>0 ulelong 4 RGB565
+>0 ulelong 5 I8
+>0 ulelong 6 IA88
+>0 ulelong 7 P8
+>0 ulelong 8 A8
+>0 ulelong 9 RGB888 (bluescreen)
+>0 ulelong 10 BGR888 (bluescreen)
+>0 ulelong 11 ARGB8888
+>0 ulelong 12 BGRA8888
+>0 ulelong 13 DXT1
+>0 ulelong 14 DXT3
+>0 ulelong 15 DXT5
+>0 ulelong 16 BGRx8888
+>0 ulelong 17 BGR565
+>0 ulelong 18 BGRx5551
+>0 ulelong 19 BGRA4444
+>0 ulelong 20 DXT1+A1
+>0 ulelong 21 BGRA5551
+>0 ulelong 22 UV88
+>0 ulelong 23 UVWQ8888
+>0 ulelong 24 RGBA16161616F
+>0 ulelong 25 RGBA16161616
+>0 ulelong 26 UVLX8888
+
+# Main VTF header.
+0 string VTF\0 Valve Texture Format
+>4 ulelong x v%u
+>8 ulelong x \b.%u
+>0x10 uleshort x \b, %u
+>0x12 uleshort >1 x %u
+>4 lequad 0x0000000700000002
+>>0x3F uleshort >1 x %u
+>0x18 uleshort >1 \b, %u frames
+>0x38 ubyte x \b, mipmaps: %u
+>0x34 ulelong >-1 \b,
+>>0x34 use vtf-image-format
+
+# Type: Valve VTF3 (PS3) texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+0 string VTF3 Valve Texture Format (PS3)
+>0x14 ubeshort x \b, %u
+>0x16 ubeshort x \b x %u
+>0x10 ubelong&0x2000 0 \b, DXT1
+>0x10 ubelong&0x2000 0x2000 \b, DXT5
+
+# Type: ASTC texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://stackoverflow.com/questions/22600678/determine-internal-format-of-given-astc-compressed-image-through-its-header
+# - https://stackoverflow.com/a/22682244
+0 ulelong 0x5ca1ab13 ASTC
+>4 ubyte x %u
+>5 ubyte x \bx%u
+>6 ubyte >1 \bx%u
+# X, Y, and Z dimensions are stored as 24-bit LE.
+# Pretend it's 32-bit and mask off the high byte.
+>7 ulelong&0x00FFFFFF x texture, %u
+>10 ulelong&0x00FFFFFF x x %u
+>13 ulelong&0x00FFFFFF >1 x %u
+
+# Zebra Metafile graphic
+# http://www.fileformat.info/format/zbr/egff.htm
+0 ubeshort 0x9a02 Zebra Metafile graphic
+>2 uleshort 1 (version 1.x)
+>2 uleshort 2 (version 1.1x or 1.2x)
+>2 uleshort 3 (version 1.49)
+>2 uleshort 4 (version 1.50)
+>4 string x (comment = %s)
+
+# Microsoft Paint graphic
+# http://www.fileformat.info/format/mspaint/egff.htm
+0 string DanM icrosoft Paint image data (version 1.x)
+>4 uleshort x (%d
+>>6 uleshort x x %d)
+0 string LinS Microsoft Paint image data (version 2.0)
+>4 uleshort x (%d
+>>6 uleshort x x %d)
+
+# reMarkable tablet internal file format (https://www.remarkable.com/)
+# https://github.com/ax3l/lines-are-beautiful
+# https://plasma.ninja/blog/devices/remarkable/binary/format/2017/12/26/\
+# reMarkable-lines-file-format.html#what-to-do-next
+# from Axel Huebl
+0 string reMarkable
+>11 string lines
+>>17 string with
+>>>22 string selections
+>>>>33 string and
+>>>>>37 string layers
+>>>>>>43 ulelong x reMarkable tablet notebook lines, 1404 x 1872, %x page(s)
+
+# newer per-page files for the reMarkable
+0 string reMarkable
+>11 string .lines
+>>18 string file,
+>>>24 string version=
+>>>>32 ubyte x reMarkable tablet page (v%c), 1404 x 1872,
+>>>>>43 ulelong x %d layer(s)
+
+# Type: PVR3 texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - http://cdn.imgtec.com/sdk-documentation/PVR+File+Format.Specification.pdf
+
+# PVR3 pixel formats.
+0 name pvr3-pixel-format
+>0 ulelong 0 PVRTC 2bpp RGB
+>0 ulelong 1 PVRTC 2bpp RGBA
+>0 ulelong 2 PVRTC 4bpp RGB
+>0 ulelong 3 PVRTC 4bpp RGBA
+>0 ulelong 4 PVRTC-II 2bpp
+>0 ulelong 5 PVRTC-II 4bpp
+>0 ulelong 6 ETC1
+>0 ulelong 7 DXT1
+>0 ulelong 8 DXT2
+>0 ulelong 9 DXT3
+>0 ulelong 10 DXT4
+>0 ulelong 11 DXT5
+>0 ulelong 12 BC4
+>0 ulelong 13 BC5
+>0 ulelong 14 BC6
+>0 ulelong 15 BC7
+>0 ulelong 16 UYVY
+>0 ulelong 17 YUY2
+>0 ulelong 18 BW1bpp
+>0 ulelong 19 R9G9B9E5 Shared Exponent
+>0 ulelong 20 RGBG8888
+>0 ulelong 21 GRGB8888
+>0 ulelong 22 ETC2 RGB
+>0 ulelong 23 ETC2 RGBA
+>0 ulelong 24 ETC2 RGB A1
+>0 ulelong 25 EAC R11
+>0 ulelong 26 EAC RG11
+>0 ulelong 27 ASTC_4x4
+>0 ulelong 28 ASTC_5x4
+>0 ulelong 29 ASTC_5x5
+>0 ulelong 30 ASTC_6x5
+>0 ulelong 31 ASTC_6x6
+>0 ulelong 32 ASTC_8x5
+>0 ulelong 33 ASTC_8x6
+>0 ulelong 34 ASTC_8x8
+>0 ulelong 35 ASTC_10x5
+>0 ulelong 36 ASTC_10x6
+>0 ulelong 37 ASTC_10x8
+>0 ulelong 38 ASTC_10x10
+>0 ulelong 39 ASTC_12x10
+>0 ulelong 40 ASTC_12x12
+>0 ulelong 41 ASTC_3x3x3
+>0 ulelong 42 ASTC_4x3x3
+>0 ulelong 43 ASTC_4x4x3
+>0 ulelong 44 ASTC_4x4x4
+>0 ulelong 45 ASTC_5x4x4
+>0 ulelong 46 ASTC_5x5x4
+>0 ulelong 47 ASTC_5x5x5
+>0 ulelong 48 ASTC_6x5x5
+>0 ulelong 49 ASTC_6x6x5
+>0 ulelong 50 ASTC_6x6x6
+
+0 string PVR\x03 PowerVR 3.0 texture:
+>0x18 ulelong x %u x
+>0x1C ulelong x %u
+>0x20 ulelong >1 x %u
+>0x08 ubyte x \b,
+>0x0C ulelong 0
+>>0x08 use pvr3-pixel-format
+>0x0C ulelong !0
+>>0x08 ubyte !0 %c
+>>>0x0C ubyte !0 \b%u
+>>0x09 ubyte !0 \b%c
+>>>0x0D ubyte !0 \b%u
+>>0x0A ubyte !0 \b%c
+>>>0x0E ubyte !0 \b%u
+>>0x0B ubyte !0 \b%c
+>>>0x0F ubyte !0 \b%u
+>0x10 ulelong 1 \b, sRGB
+>0x04 ulelong&0x02 0x02 \b, premultiplied alpha
+
+0 string \x03RVP PowerVR 3.0 texture: BE,
+>0x18 ubelong x %u x
+>0x1C ubelong x %u
+>0x20 ubelong >1 x %u
+>0x08 ubyte x \b,
+>0x0C ubelong 0
+>>0x08 use pvr3-pixel-format
+>0x0C ubelong !0
+>>0x0B ubyte !0 %c
+>>>0x0F ubyte !0 \b%u
+>>0x0A ubyte !0 \b%c
+>>>0x0E ubyte !0 \b%u
+>>0x09 ubyte !0 \b%c
+>>>0x0D ubyte !0 \b%u
+>>0x08 ubyte !0 \b%c
+>>>0x0C ubyte !0 \b%u
+>0x10 ubelong 1 \b, sRGB
+>0x04 ubelong&0x02 0x02 \b, premultiplied alpha
+
+# Type: Microsoft Xbox XPR0 texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/blob/develop/src/core/hle/D3D8/XbD3D8Types.h
+
+# XPR pixel formats.
+0 name xbox-xpr-pixel-format
+>0 ubyte 0x00 L8
+>0 ubyte 0x01 AL8
+>0 ubyte 0x02 ARGB1555
+>0 ubyte 0x03 RGB555
+>0 ubyte 0x04 ARGB4444
+>0 ubyte 0x05 RGB565
+>0 ubyte 0x06 ARGB8888
+>0 ubyte 0x07 xRGB8888
+>0 ubyte 0x0B P8
+>0 ubyte 0x0C DXT1
+>0 ubyte 0x0E DXT2
+>0 ubyte 0x0F DXT4
+>0 ubyte 0x10 Linear ARGB1555
+>0 ubyte 0x11 Linear RGB565
+>0 ubyte 0x12 Linear ARGB8888
+>0 ubyte 0x13 Linear L8
+>0 ubyte 0x16 Linear R8B8
+>0 ubyte 0x17 Linear G8B8
+>0 ubyte 0x19 A8
+>0 ubyte 0x1A A8L8
+>0 ubyte 0x1B Linear AL8
+>0 ubyte 0x1C Linear RGB555
+>0 ubyte 0x1D Linear ARGB4444
+>0 ubyte 0x1E Linear xRGB8888
+>0 ubyte 0x1F Linear A8
+>0 ubyte 0x20 Linear A8L8
+>0 ubyte 0x24 YUY2
+>0 ubyte 0x25 UYVY
+>0 ubyte 0x27 L6V5U5
+>0 ubyte 0x28 V8U8
+>0 ubyte 0x29 R8B8
+>0 ubyte 0x2A D24S8
+>0 ubyte 0x2B F24S8
+>0 ubyte 0x2C D16
+>0 ubyte 0x2D F16
+>0 ubyte 0x2E Linear D24S8
+>0 ubyte 0x2F Linear F24S8
+>0 ubyte 0x30 Linear D16
+>0 ubyte 0x31 Linear F16
+>0 ubyte 0x32 L16
+>0 ubyte 0x33 V16U16
+>0 ubyte 0x35 Linear L16
+>0 ubyte 0x36 Linear V16U16
+>0 ubyte 0x37 Linear L6V5U5
+>0 ubyte 0x38 RGBA5551
+>0 ubyte 0x39 RGBA4444
+>0 ubyte 0x3A QWVU8888
+>0 ubyte 0x3B BGRA8888
+>0 ubyte 0x3C RGBA8888
+>0 ubyte 0x3D Linear RGBA5551
+>0 ubyte 0x3E Linear RGBA4444
+>0 ubyte 0x3F Linear ABGR8888
+>0 ubyte 0x40 Linear BGRA8888
+>0 ubyte 0x41 Linear RGBA8888
+>0 ubyte 0x64 Vertex Data
+
+0 string XPR0 Microsoft Xbox XPR0 texture
+>0x19 ubyte x \b, format:
+>>0x19 use xbox-xpr-pixel-format
+
+# ILDA Image Data Transfer Format
+# https://www.ilda.com/resources/StandardsDocs/ILDA_IDTF14_rev011.pdf
+#
+# Updated by Chuck Hein (laser@geekdude.com)
+#
+0 string ILDA ILDA Image Data Transfer Format
+>7 ubyte 0x00 3D Coordinates with Indexed Color
+>7 ubyte 0x01 2D Coordinates with Indexed Color
+>7 ubyte 0x02 Color Palette
+>7 ubyte 0x04 3D Coordinates with True Color
+>7 ubyte 0x05 2D Coordinates with True Color
+>8 string >0 \b, palette %s
+>16 string >0 \b, company %s
+>24 ubeshort >0 \b, number of records %d
+>>26 ubeshort x \b, palette number %d
+>>28 ubeshort >0 \b, number of frames %d
+>>30 ubyte >0 \b, projector number %d
+
+# Dropbox "lepton" compressed jpeg format
+# https://github.com/dropbox/lepton
+0 ubelong&0xfffff0ff 0xcf84005a Lepton image file
+>2 ubyte x (version %d)
+
+# Apple QuickTake camera raw images
+# https://en.wikipedia.org/wiki/Apple_QuickTake
+# dcraw can decode them
+0 name quicktake
+>4 ubelong 8
+>>544 ubeshort x \b, %dx
+>>546 ubeshort x \b%d
+>4 ubelong 4
+>>546 ubeshort x \b, %dx
+>>544 ubeshort x \b%d
+
+0 string qktk Apple QuickTake 100 Raw Image
+>0 use quicktake
+
+0 string qktn
+>4 ubyte 0 Apple QuickTake 150 Raw Image
+>4 ubyte >0 Apple QuickTake 200 Raw Image
+>0 use quicktake
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Corel_Photo-Paint_image
+# Reference: http://blog.argasinski.eu/wp-content/uploads/2011/08/cpt-specification-0.01.pdf
+0 string CPT
+>4 string FILE Corel Photo-Paint image, version
+# version like 7, 9 or 8
+>>3 ubyte x %c,
+!:mime image/x-corel-cpt
+!:ext cpt
+# if blocks_array_offset available jump blockNumber*8 bytes
+>>0x34 ulelong >0
+>>>(0x28.l*8) ubyte x
+# jump additional stored blocks_array_offset bytes forward to object block
+>>>>&(0x34.l-1) ulelong x %u
+# object height in pixels
+>>>>>&0 ulelong x x %u
+# if no blocks_array_offset available jump blockNumber*8 bytes
+>>0x34 ulelong =0
+>>>(0x28.l*8) ubyte x
+# jump additional 0x13C bytes forward to object block
+>>>>&0x13B ulelong x %u
+>>>>>&0 ulelong x x %u
+# image color model used
+>>0x8 ulelong x
+>>>0x8 ulelong 0x1 RGB 24 bits
+>>>0x8 ulelong 0x3 CMYK 24 bits
+>>>0x8 ulelong 0x5 greyscale 8 bits
+>>>0x8 ulelong 0x6 black and white 1 bit
+>>>0x8 ulelong 0xA RGB 8 bits
+# palette_length number of colors * 3 in case of 8-bit RGB paletted image
+# 0 otherwise. Allowed values: 0 or [1..256] * 3
+#>>0xC ulelong >0 \b, palette length %u
+>>>>0xC ulelong/3 <256 \b, %u colors
+>>>0x8 ulelong 0xB LAB
+>>>0x8 ulelong 0xC RGB 48 bits
+>>>0x8 ulelong 0xE greyscale 16 bits
+# this should not happen
+>>>0x8 default x color model
+>>>>0x8 ulelong x %#x
+# bit 1 in CPT file flags: UCS-2 file comment present
+>>0x31 ubyte &0x02
+# look for comment marker
+>>>0x100 search/0xc9d \4\2\0\0
+# UCS-2 file comment
+>>>>&0 lestring16 x "%s"
+# if no UCS-2 is present show ANSI file comment[112] if available
+>>0x31 ubyte&0x02 =0
+>>>0x3C string >\0 "%-.112s"
+# reserved seems to be always 0
+#>>0x10 ulelong >0 \b, reserved1 %u
+# horizontal real dpi = dpi_h * 25.4 / 10**6
+>>0x18 ulelong x \b, %u micro dots/mm
+# image vertical DPI in CPT DPI unit
+#>>0x1C ulelong x \b, %u micro dots/mm
+# reserved seems to be always 0
+#>>0x20 ulelong >0 \b, reserved2 %u
+#>>0x24 ulelong >0 \b, reserved3 %u
+# blocks_count; number of CPT_Block blocks. Allowed values: > 0
+>>0x28 ulelong x \b, %u block
+# plural s
+>>0x28 ulelong !1 \bs
+# CPT file flags
+# lower byte of CPT file flags: 0x94~CPT9FILE 0x01~often CPT7FILE 0x8C~CPT8FILE
+#>>0x30 ubyte x \b, lower flags %#x
+# upper byte of CPT file flags:
+#>>0x31 ubyte >0 \b, upper flags %#x
+# bit 2 in CPT file flags: unknown
+#>>0x31 ubyte &0x04 \b, with UNKNOWN
+# bits 3-7 in CPT file flags: unknown, seem to be often 0
+# show unusual flag combinations
+>>0x31 ubyte&0xFC >0
+>>>0x30 uleshort x \b, flags %#4.4x
+# reserved seems to be always 0
+#>>0x32 uleshort >0 \b, reserved4 %#x
+# blocks_array_offset is always 0 for CPT7 and CPT8 files created by PP7-PP8
+# typical values like: 13Ch 154h 43Ch 4F0h DA8h
+>>0x34 ulelong x \b, array offset %#x
+# reserved seems to be often 0
+>>0x38 ulelong >0 \b, reserved5 %#x
+# possible next master block
+#>>0x100 ubequad !0 \b, next block=%#llx...
+# bit 0: ICC profile block present
+>>0x31 ubyte &0x01 \b, with ICC profile
+# check for characteristic string acsp of color profile for DEBUGGING
+#>>>0x178 string x icc=%.4s
+# display ICC/ICM color profile by ./icc
+#>>>0x154 use color-profile
+
+# URL: http://fileformats.archiveteam.org/wiki/CorelDRAW
+# https://en.wikipedia.org/wiki/CorelDRAW
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/cdr-gen.trid.xml
+# Note: called "CorelDRAW drawing (generic)" by TrID
+# version til 2 WL-based; from version 3 til 13 handled by ./riff and from 14 zip based handled by ./archive
+0 ubelong&0xFFffF7ff 0x574C6500 Corel Draw Picture
+#!:mime image/x-coreldraw
+!:mime application/vnd.corel-draw
+!:ext cdr
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/cdr-corel-10.trid.xml
+# Note: called "CorelDRAW drawing (v1.0)" by TrID and
+# "CorelDraw Drawing" with version "1.0" by DROID via PUID fmt/467
+# only DROID fmt-467-signature-id-726.cdr example
+>2 ubyte 0x65 \b, version 1.0
+#>>4 ubelong !0x45000000 \b, at 4 %#8.8x
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/cdr-corel-20.trid.xml
+# Note: called "CorelDRAW drawing (v2.0)" by TrID and
+# "CorelDraw Drawing" with version "2.0" by DROID via PUID fmt/466
+>2 ubyte 0x6D \b, version 2.0
+# According to DROID 0xed080000 or 0x25050000
+#>>4 ubelong !0xed080000
+#>>>4 ubelong !0x25050000 \b, at 4 %#8.8x
+
+# Type: Crunch compressed texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://github.com/BinomialLLC/crunch/blob/44c8402e24441c7524ca364941fd224ab3b971e9/inc/crn_decomp.h#L267
+0 ubelong 0x4878004A Crunch compressed texture:
+>0x0C ubeshort x %u x
+>0x0E ubeshort x %u
+>0x12 ubyte 0 \b, DXT1
+>0x12 ubyte 1 \b, DXT3
+>0x12 ubyte 2 \b, DXT5
+>0x12 ubyte 3 \b, DXT5 CCxY
+>0x12 ubyte 4 \b, DXT5 xGxR
+>0x12 ubyte 5 \b, DXT5 xGBR
+>0x12 ubyte 6 \b, DXT5 AGBR
+>0x12 ubyte 7 \b, DXn XY
+>0x12 ubyte 8 \b, DXn YX
+>0x12 ubyte 9 \b, DXT5 Alpha
+>0x12 ubyte 10 \b, ETC1
+>0x10 ubyte >1 \b, %u images
+>0x11 ubyte >1 \b, %u faces
+# TODO: Flags at 0x13? (ubeshort)
+
+# Type: BasisLZ compressed texture.
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://github.com/BinomialLLC/basis_universal/blob/master/spec/basis_spec.txt
+0 uleshort 0x4273
+>0x04 uleshort 0x4D BasisLZ
+>>0x02 uleshort x v%x compressed texture:
+>>0x14 ubyte 0 ETC1S
+>>0x14 ubyte 1 UASTC 4x4
+>>0x0E ulelong&0xFFFFFF >1 \b, %u slices
+>>0x11 ulelong&0xFFFFFF >1 \b, %u images
+>>0x15 uleshort&0x02 2 \b, Y-flipped
+
+# MIME registration: https://www.iana.org/assignments/media-types/model/e57
+# Sample files: http://www.libe57.org/data.html
+# Reference implementation: http://www.libe57.org/
+# https://www.ri.cmu.edu/pub_files/2011/1/2011-huber-e57-v3.pdf
+0 string ASTM-E57 ASTM E57 three-dimensional model
+!:mime model/e57
+!:ext e57
+
+# QOI [Quite OK Image Format] images
+# (Horia Mihai David, mihaidavid@posteo.net)
+#
+# QOI format by Dominic Szablewski <http://phoboslab.org/>
+# <https://qoiformat.org/>
+#
+# Based on spec v1.0 (2022.01.05) <https://qoiformat.org/qoi-specification.pdf>
+
+0 string qoif QOI image data
+!:ext qoi
+!:mime image/x-qoi
+# See <https://github.com/phoboslab/qoi/issues/167>
+>4 ubelong x %ux
+>8 ubelong x \b%u,
+>>13 ubyte 0 s
+>>>12 ubyte 3 \bRGB
+>>>12 ubyte 4 \bRGBA
+>>>12 default x
+>>>>12 ubyte x \b*bad channels %u*
+>>>13 ubyte 0 (linear alpha)
+>>13 ubyte 1
+>>>12 ubyte 3 RGB
+>>>12 ubyte 4 RGBA
+>>>13 ubyte 1 (all channels linear)
+>>13 default x
+>>>13 ubyte x *bad colorspace %u*
+
+
+# Type: Godot 3, 4 texture (pixel format)
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+0 name godot-pixel-format
+>0 ulelong&0xFFFFF 0 L8
+>0 ulelong&0xFFFFF 1 LA8
+>0 ulelong&0xFFFFF 2 R8
+>0 ulelong&0xFFFFF 3 RG8
+>0 ulelong&0xFFFFF 4 RGB8
+>0 ulelong&0xFFFFF 5 RGBA8
+>0 ulelong&0xFFFFF 6 RGBA4444
+>0 ulelong&0xFFFFF 7 RGB565
+>0 ulelong&0xFFFFF 8 RF
+>0 ulelong&0xFFFFF 9 RGF
+>0 ulelong&0xFFFFF 10 RGBF
+>0 ulelong&0xFFFFF 11 RGBAF
+>0 ulelong&0xFFFFF 12 RH
+>0 ulelong&0xFFFFF 13 RGH
+>0 ulelong&0xFFFFF 14 RGBH
+>0 ulelong&0xFFFFF 15 RGBAH
+>0 ulelong&0xFFFFF 16 RGBE9995
+>0 ulelong&0xFFFFF 17 DXT1
+>0 ulelong&0xFFFFF 18 DXT3
+>0 ulelong&0xFFFFF 19 DXT5
+>0 ulelong&0xFFFFF 20 RGTC_R
+>0 ulelong&0xFFFFF 21 RGTC_RG
+>0 ulelong&0xFFFFF 22 BPTC_RGBA
+>0 ulelong&0xFFFFF 23 BPTC_RGBF
+>0 ulelong&0xFFFFF 24 BPTC_RGBFU
+>0 ulelong&0xFFFFF 25 PVRTC1_2
+>0 ulelong&0xFFFFF 26 PVRTC1_2A
+>0 ulelong&0xFFFFF 27 PVRTC1_4
+>0 ulelong&0xFFFFF 28 PVRTC1_4A
+>0 ulelong&0xFFFFF 29 ETC
+>0 ulelong&0xFFFFF 30 ETC2_R11
+>0 ulelong&0xFFFFF 31 ETC2_R11S
+>0 ulelong&0xFFFFF 32 ETC2_RG11
+>0 ulelong&0xFFFFF 33 ETC2_RG11S
+>0 ulelong&0xFFFFF 34 ETC2_RGB8
+>0 ulelong&0xFFFFF 35 ETC2_RGBA8
+>0 ulelong&0xFFFFF 36 ETC2_RGB8A1
+>0 ulelong&0xFFFFF 37 ASTC_8x8
+
+# Type: Godot 3, 4 texture (rescale display, width)
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Shows rescale value if it's not a power of 2.
+0 name godot-rescale-display-w
+>0 uleshort 0
+>0 uleshort 1
+>0 uleshort 2
+>0 uleshort 4
+>0 uleshort 8
+>0 uleshort 16
+>0 uleshort 32
+>0 uleshort 64
+>0 uleshort 128
+>0 uleshort 256
+>0 uleshort 512
+>0 uleshort 1024
+>0 uleshort 2048
+>0 uleshort 4096
+>0 uleshort 8192
+>0 uleshort 16384
+>0 uleshort 32768
+>0 default x
+>>0 uleshort x (rescale to %u x
+
+# Type: Godot 3, 4 texture (rescale display, height)
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Shows rescale value if it's not a power of 2.
+0 name godot-rescale-display-h
+>0 clear x
+>0 uleshort 0
+>0 uleshort 1
+>0 uleshort 2
+>0 uleshort 4
+>0 uleshort 8
+>0 uleshort 16
+>0 uleshort 32
+>0 uleshort 64
+>0 uleshort 128
+>0 uleshort 256
+>0 uleshort 512
+>0 uleshort 1024
+>0 uleshort 2048
+>0 uleshort 4096
+>0 uleshort 8192
+>0 uleshort 16384
+>0 uleshort 32768
+>0 default x
+>>0 uleshort x %u)
+
+# Type: Godot 3 texture
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://github.com/godotengine/godot/blob/3.3/core/image.h
+# - https://github.com/godotengine/godot/blob/3.3/scene/resources/texture.cpp
+# - https://github.com/godotengine/godot/blob/3.3/scene/resources/texture.h
+# TODO: Don't show "rescale to" if it matches the image size.
+0 string GDST Godot 3 texture:
+!:ext stex
+!:mime image/x-godot-stex
+>4 uleshort x %u x
+>8 uleshort x %u
+>6 uleshort 0 \b,
+>6 uleshort !0
+>>6 use godot-rescale-display-w
+>>10 use godot-rescale-display-h
+>>10 uleshort x \b,
+>16 ulelong&0x800000 !0 has mipmaps,
+>16 ulelong&0x100000 0x100000 lossless encoding
+>16 ulelong&0x200000 0x200000 lossy encoding
+>16 ulelong&0x300000 0
+>>16 use godot-pixel-format
+
+# Type: Godot 4 texture
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# References:
+# - https://github.com/godotengine/godot/blob/master/core/io/image.h
+# - https://github.com/godotengine/godot/blob/master/scene/resources/texture.cpp
+# - https://github.com/godotengine/godot/blob/master/scene/resources/texture.h
+# TODO: Don't show "rescale to" if it matches the image size.
+0 string GST2 Godot 4 texture
+!:ext stex
+!:mime image/x-godot-stex
+>4 ulelong x v%u:
+>0x28 uleshort x %u x
+>0x2A uleshort x %u
+>8 use godot-rescale-display-w
+>12 use godot-rescale-display-h
+>12 uleshort x \b,
+>0x2C ulelong >1 %u mipmaps,
+>0x30 use godot-pixel-format
+>0x24 ulelong 1 \b, embedded PNG image
+>0x24 ulelong 2 \b, embedded WebP image
+>0x24 ulelong 3 \b, Basis Universal
+
+# Summary: iCEDraw graphic *.IDF
+# URL: http://fileformats.archiveteam.org/wiki/ICEDraw
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/i/idf-icedraw.trid.xml
+# From: Joerg Jenderek
+# Note: called "iCEDraw graphic" by TrID, "iCEDraw text" by FFmpeg and "iCE Draw" by Ansilove
+# verified by FFmpeg command `ffprobe ICE-9605.IDF` and `ansilove -s SQ-FORCE.IDF`
+0 string \0041.4\0\0\0\0O\0 iCEDraw graphic
+#!:mime application/octet-stream
+!:mime image/x-idf
+!:ext idf
+
+# Type: ColoRIX VGA Paint Image File (.rix/.sci/.scX)
+# From: Eddy Jansson <github.com/eloj>
+# Reference: https://www.fileformat.info/format/rix/spec/
+#
+0 name rix-header
+>0 uleshort x \b, %u x
+>2 uleshort x %u
+# palette type:
+# .. if direct color, low bits encode bpp
+>4 ubyte&128 0
+>>4 ubyte&127 x \b %u bpp (direct color)
+# .. else palette
+>4 ubyte&128 128
+>>4 ubyte&7 0 \b x 2
+>>4 ubyte&7 1 \b x 4
+>>4 ubyte&7 2 \b x 8
+>>4 ubyte&7 3 \b x 16
+>>4 ubyte&7 4 \b x 32
+>>4 ubyte&7 5 \b x 64
+>>4 ubyte&7 6 \b x 128
+>>4 ubyte&7 7 \b x 256
+# storage type
+#>5 ubyte&15 0 \b, Linear
+>5 ubyte&15 1 \b, Planar (0213)
+>5 ubyte&15 2 \b, Planar
+>5 ubyte&15 3 \b, Text
+>5 ubyte&15 4 \b, Planar lines
+>5 ubyte&128 128 \b (compressed)
+>5 ubyte&64 64 \b (extension)
+>5 ubyte&32 32 \b (encrypted)
+
+0 string RIX3 ColoRIX Image
+>4 use rix-header
+
+0 string RIX7 ColoRIX Slideshow
+
+# http://fileformats.archiveteam.org/wiki/PaperPort_(MAX)
+0 string ViG Visioneer PaperPort
+>3 string Ae 2
+>3 string Be 2
+>3 string Cj 3-4
+>3 string Em 5-7
+>3 string Fk 8-12
+>3 default x MAX
diff --git a/contrib/libs/libmagic/magic/Magdir/inform b/contrib/libs/libmagic/magic/Magdir/inform
new file mode 100644
index 0000000000..fe518ece91
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/inform
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: inform,v 1.5 2009/09/19 16:28:09 christos Exp $
+# inform: file(1) magic for Inform interactive fiction language
+
+# URL: http://www.inform-fiction.org/
+# From: Reuben Thomas <rrt@sc3d.org>
+
+0 search/100/cW constant\ story Inform source text
diff --git a/contrib/libs/libmagic/magic/Magdir/intel b/contrib/libs/libmagic/magic/Magdir/intel
new file mode 100644
index 0000000000..5177fea457
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/intel
@@ -0,0 +1,310 @@
+
+#------------------------------------------------------------------------------
+# $File: intel,v 1.23 2022/10/31 13:22:26 christos Exp $
+# intel: file(1) magic for x86 Unix
+#
+# Various flavors of x86 UNIX executable/object (other than Xenix, which
+# is in "microsoft"). DOS is in "msdos"; the ambitious soul can do
+# Windows as well.
+#
+# Windows NT belongs elsewhere, as you need x86 and MIPS and Alpha and
+# whatever comes next (HP-PA Hummingbird?). OS/2 may also go elsewhere
+# as well, if, as, and when IBM makes it portable.
+#
+# The `versions' should be un-commented if they work for you.
+# (Was the problem just one of endianness?)
+#
+0 leshort 0502 basic-16 executable
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %d
+0 leshort 0503 basic-16 executable (TV)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %d
+0 leshort 0510 x86 executable
+>12 lelong >0 not stripped
+0 leshort 0511 x86 executable (TV)
+>12 lelong >0 not stripped
+0 leshort =0512 iAPX 286 executable small model (COFF)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %d
+0 leshort =0522 iAPX 286 executable large model (COFF)
+>12 lelong >0 not stripped
+#>22 leshort >0 - version %d
+# updated by Joerg Jenderek at Oct 2015
+# https://de.wikipedia.org/wiki/Common_Object_File_Format
+# http://www.delorie.com/djgpp/doc/coff/filhdr.html
+# ./msdos (version 5.25) labeled the next entry as "MS Windows COFF Intel 80386 object file"
+# ./intel (version 5.25) label labeled the next entry as "80386 COFF executable"
+# SGI labeled the next entry as "iAPX 386 executable" --Dan Quinlan
+0 leshort =0514
+# use subroutine to display name+flags+variables for common object formatted files
+>0 use display-coff
+#>12 lelong >0 not stripped
+# no hint found, that at offset 22 is version
+#>22 leshort >0 - version %d
+0 leshort 0x0200
+# no F_EXEC flag bit implies Intel ia64 COFF object file without optional header
+>18 leshort ^0x0002
+# skip some DEGAS high-res uncompressed bitmap *.pi3 handled by ./images like
+# GEMINI03.PI3 MODEM2.PI3 POWERFIX.PI3 sigirl1.pi3 vanna5.pi3
+# by test for valid starting character (often point 0x2E) of 1st section name
+>>20 ubyte >0x1F
+>>>0 use display-coff
+# F_EXEC flag bit implies Intel ia64 COFF executable
+>18 leshort &0x0002
+>>0 use display-coff
+0 leshort 0x8664
+>0 use display-coff
+
+# rom: file(1) magic for BIOS ROM Extensions found in intel machines
+# mapped into memory between 0xC0000 and 0xFFFFF
+# From: Alex Myczko <alex@aiei.ch>
+# updated by Joerg Jenderek
+# https://en.wikipedia.org/wiki/Option_ROM
+# URL: http://fileformats.archiveteam.org/wiki/BIOS
+# Reference: http://www.lejabeach.com/sisubb/BIOS_Disassembly_Ninjutsu_Uncovered.pdf
+0 beshort 0x55AA
+# skip misidentified raspberry pi pieeprom-*.bin by check for
+# unlikely high ROM size (0xF0*512=240*512) and not observed start instruction 0x0F
+>2 ubeshort !0xF00F
+# skip 2 byte sized eof.bin with start magic
+>>0 use rom-x86
+0 name rom-x86
+>0 beshort x BIOS (ia32) ROM Ext.
+#!:mime application/octet-stream
+!:mime application/x-ibm-rom
+!:ext rom/bin
+################################################################################
+# not Plug aNd Play ($PnP) like 00000000 (ide_xtp.bin kvmvapic.bin V7VGA.ROM) 000000fc (MCT-VGA.bin)
+# 55aaf00f (pieeprom-*.bin) 55aa40e9 (Trm3x5.bin) 24506f4f (sgabios-bin.rom)
+# 55aa4be9 (vgabios-stdvga.rom vgabios-cirrus-bin.rom vgabios-vmware-bin.rom)
+>(26.s) ubelong !0x24506e50
+#>(26.s) ubelong !0x24506e50 NOT PNP=%8.8x
+# also not PCI (PCIR) implies "old" ISA cards or foo like: 8a168404 (MCT-VGA.bin)
+# 55aaf00f (pieeprom*.bin)
+>>(24.s) ubelong !0x50434952
+#>>(24.s) ubelong !0x50434952 ISA CARD=%8.8x
+# "old" identification strings used in file version 5.41 and earlier
+# probably an USB controller
+>>>5 string USB USB
+# probably https://en.wikipedia.org/wiki/Preboot_Execution_Environment
+>>>7 string LDR UNDI image
+# probably another Adaptec SCSI controller
+>>>26 string Adaptec Adaptec
+# http://minuszerodegrees.net/rom/bin/adaptec_aha1542cp_bios_908501-00.bin
+# already done by PNP variant
+#>>>28 string Adaptec Adaptec
+# probably Promise SCSI controller
+>>>42 string PROMISE Promise
+# old test for IBM compatible Video cards; INTERNAL FACTS WHY IS THIS WORKING?
+>30 string IBM IBM comp. Video
+# display exact text for IBM compatible Video cards with longer text
+>>33 ubyte !0
+>>>30 string x "%s"
+# http://minuszerodegrees.net/rom/bin/unknown/MCT-VGA-16%20-%20TDVGA%203588%20BIOS%20Version%20V1.04A.zip
+# "IBM COMPATIBLETDVGA 3588 BIOS Version V1.04A2+" "MCT-VGA-16 - TDVGA 3588 BIOS Version V1.04A.bin"
+# "IBM VGA Compatible\001" NVidia44.bin
+# "IBM EGA ROM Video Seven BIOS Code, Version 1.04" V7VGA.ROM
+# "IBM" vgabios-stdvga.rom
+# "IBM" vgabios-vmware-bin.rom:
+# "IBM" vgabios-cirrus-bin.rom
+# "IBM" vgabios-virtio-bin.rom
+################################################################################
+# ROM size in 512B blocks must be interpreted as unsigned for ROM of network cards
+# like: efi-eepro100.rom efi-rtl8139.rom pxe-e1000.rom
+>2 ubyte x (%u*512)
+# file name file size calculated size remark
+# eof.bin 2 - with start magic nothing is shown here
+# orchid.bin 188 0 =0*512 on window 95 CD in Drivers\audio\orchid3d
+# multiboot.bin 1024 1024 =2*512 QEMU emulator
+# loader1.bin 512 2048 =4*512
+# ide_xtp.bin 8192 8192 =16*512
+# kvmvapic.bin 9216 9216 =18*512
+# V7VGA.ROM 18832 16384 =32*512
+# adaptec1542.bin 32768 16384 =32*512
+# MCT-VGA.bin 32768 24576 =48*512
+# 2975BIOS.BIN 32768 32256 =63*512
+# efi-e1000.rom 196608 64000 =125*512
+# efi-rtl8139.rom 176640 66048 =129*512
+# pieeprom*.bin 524288 122880 =240*512
+################################################################################
+# initialization vector with executable code; often near JuMP instruction E9 yy zz
+>3 ubyte =0xE9 jmp
+# jmp offset like: 008fh 0093h 009fh 00afh 0143h 3ad7h 5417h 54ech 594dh 895fh
+>>4 uleshort x %#4.4x
+# for initialization vector samples without 3 byte jump instruction
+>3 ubyte !0xE9 instruction
+# eb4b3734h NVidia44.bin
+# 00003234h V7VGA.ROM
+# 060e0731h kvmvapic.bin
+# cb000000h linuxboot-bin.rom
+# e80d0fcbh PXE-Intel.rom
+# b8004875h orchid.bin
+>>3 ubelong x %#8.8x
+# For misidentified raspberry pi pieeprom-*.bin like: 0xf00f
+#>2 ubeshort x \b, AT 2 %#4.4x
+################################################################################
+# new sections for BIOS (ia32) ROM Extension
+# 4 bytes ASCII Signature "$PnP" for Plug aNd Play expansion header
+>(26.s) string =$PnP \b;
+#>(26.s) string =$PnP FOUND $PnP
+# at 1Ah possible offset to expansion header structure; new for Plug aNd Play
+>>26 uleshort x at %#x PNP
+# Plug and Play vendor+device ID like: 0 0x000f1000 (2975BIOS.BIN) 0x31121095 (4243.bin) 0x04904215 (adaptec1542.bin)
+#>>(26.s+0x0A) ulelong !0 NOT-nullID=%8.8x
+>>(26.s+0x0A) uleshort !0
+# show PnP Vendor identification in human readable text form instead of numeric
+# For adaptec_ava1515_bios_585201-00.bin reverted endian! BUT IS THIS ALWAYS TRUE?
+>>>(26.s+0x0C) use \^PCI-vendor
+>>>(26.s+0x0A) ubeshort x device=%#4.4x
+# 3 byte Device type code; probably the same meaning as in PCI section?
+# OK for storage controller SCSI (2975BIOS.BIN adaptec1542.bin)
+# and network controller ethernet (efi-e1000.rom efi-rtl8139.rom)
+>>(26.s+0x12) use PCI-class
+# structure revision like: 01h
+>>(26.s+4) ubyte !1 \b, revision %u
+# PnP Header structure length in multiple of 16 bytes like: 2
+>>(26.s+5) uleshort !2 \b, length %u*16
+# offset to next header; 0 if none
+>>(26.s+7) uleshort !0 \b, at %#x next header
+# reserved byte; seems to be zero
+>>(26.s+8) ubyte !0 \b, reserved %#x
+# 8-bit checksum for this header; calculated and patched by patch2pnprom
+>>(26.s+9) ubyte !0 \b, CRC %#x
+# pointer to optional manufacturer string; like: 0 (4243.bin) 59h 5ch 60h c7h 14eh 27ch 296h 324h 3662h
+>>(26.s+0x0E) uleshort >0 \b, at %#x
+>>>(26.s+0x0C) uleshort x
+# manufacturer ASCII-Z string like "http://ipxe.org" "Plop - Elmar Hanlhofer www.plop.at" "QEMU"
+>>>>(&0.s) string x "%s"
+# pointer to optional product string; like: 0 (2975BIOS.BIN) 6ch 70h 7ch d9h 160h 281h 29bh 329h
+>>(26.s+0x10) uleshort >0 \b, at %#x
+>>>(26.s+0x0E) uleshort x
+# often human readable product ASCII-Z string like "iPXE" "Plop Boot Manager"
+# "multiboot loader" "Intel UNDI, PXE-2.0 (build 082)"
+>>>>(&0.s) string x "%s"
+# PnP Device indicators; contains bits that identify the device as being capable of bootable
+#>>(26.s+0x15) ubyte x \b, INDICATORS %#x
+# device is a display device
+>>(26.s+0x15) ubyte &0x01 \b, display
+# device is an input device
+>>(26.s+0x15) ubyte &0x02 \b, input
+# device is an IPL device
+>>(26.s+0x15) ubyte &0x04 \b, IPL
+#>>(26.s+0x15) ubyte &0x08 reserved
+# ROM is only required if this device is selected as a boot device
+>>(26.s+0x15) ubyte &0x10 \b, bootable
+# indicates ROM is read cacheable
+>>(26.s+0x15) ubyte &0x20 \b, cacheable
+# ROM may be shadowed in RAM
+>>(26.s+0x15) ubyte &0x40 \b, shadowable
+# ROM supports the device driver initialization model
+>>(26.s+0x15) ubyte &0x80 \b, InitialModel
+# boot connection vector; an offset to a routine that hook into INT 9h, INT 10h, or INT 13h
+# 0 means disabled 0x0429 (4650_sr5.bin) 0x0072 (adaptec1542.bin)
+>>(26.s+0x16) uleshort !0 \b, boot vector offset %#x
+# disconnect vector; offset to routine that do cleanup from an unsuccessful boot attempt
+>>(26.s+0x18) uleshort !0 \b, disconnect offset %#x
+# bootstrap entry point/vector (BEV); offset to a routine (like RPL) that hook into INT 19h
+# 0 means disabled 0x3c (multiboot.bin) 0x358 (efi-rtl8139.rom) 0xae7 (PXE-Intel.rom)
+>>(26.s+0x1A) uleshort !0 \b, bootstrap offset %#x
+# 2nd reserved area; seems to be zero
+>>(26.s+0x1C) uleshort !0 \b, 2nd reserved %#x
+# static resource information vector; 0 means disabled
+>>(26.s+0x1E) uleshort !0 \b, static offset %#4.4x
+################################################################################
+# 4 bytes ASCII Signature "PCIR" for PCI Data Structure
+#>(24.s) string =PCIR FOUND PCIR
+>(24.s) string =PCIR \b;
+# pointer to PCI data structure like: 1Ch 38h 104h 8E44h
+>>24 uleshort x at %#x PCI
+# Vendor identification (ID) https://pci-ids.ucw.cz/v2.2/pci.ids
+#>>(24.s+4) uleshort x ID=%4.4x
+# show Vendor identification in human readable text form instead of numeric
+>>(24.s+4) use PCI-vendor
+# device identification (ID)
+>>(24.s+6) uleshort x device=%#4.4x
+# Base+sub class code https://wiki.osdev.org/PCI
+>>(24.s+0x0D) use PCI-class
+# pointer to vital product data (VPD); 0 indicates no VPD; WHAT EXACTLY iS VPD?
+>>(24.s+8) uleshort !0 \b, at %#x VPD
+# PCI data structure length like: 24h 28h
+>>(24.s+0xA) uleshort >0x28 \b, length %u
+# PCI data structure revision like: 0 3
+>>(24.s+0xC) ubyte >0 \b, revision %u
+# image length (hexadecimal) in multiple of 512 bytes like: 54 56 68 6a 76 78 7c 7d 7e 7f 80 81 83
+# Apparently this gives the same information as given by byte at offset 2 but as 16-bit
+#>>(24.s+0x10) uleshort x \b, length %u*512
+# revision level of code/data like: 0 1 201h 502h
+>>(24.s+0xC) ubyte >1 \b, code revision %#x
+# code type: 0~Intel x86/PC-AT compatible 1~Open firmware standard for PCI42 FF~Reserved
+>>(24.s+0x14) ubyte >0 \b, code type %#x
+# last image indicator; bit 7 indicates "last image"; bits 0-6 are reserved
+>>(24.s+0x15) ubyte >0
+>>>(24.s+0x15) ubyte =0x80 \b, last ROM
+# THIS SHOULD NOT HAPPEN!
+>>>(24.s+0x15) ubyte !0x80 \b, indicator %x
+# 3rd reserved area; seems to be zero in most cases but not for
+# efi-e1000.rom efi-rtl8139.rom
+>>(24.s+0x16) ubeshort !0 \b, 3rd reserved %#x
+
+# Flash descriptors for Intel SPI flash roms.
+# From Dr. Jesus <j@hug.gs>
+0 lelong 0x0ff0a55a Intel serial flash for ICH/PCH ROM <= 5 or 3400 series A-step
+16 lelong 0x0ff0a55a Intel serial flash for PCH ROM
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface
+# Reference: https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf
+# Note: generated for example by `cat /sys/firmware/acpi/tables/DSDT MyDSDT.aml`
+0 string DSDT
+>0 use acpi-table
+# not tested or other file format
+0 string APIC
+>0 use acpi-table
+#0 string ASF!
+#>0 use acpi-table
+0 string FACP
+>0 use acpi-table
+#0 string FACS
+#>0 use acpi-table
+0 string MCFG
+>0 use acpi-table
+0 string SLIC
+>0 use acpi-table
+0 string SSDT
+>0 use acpi-table
+0 name acpi-table
+# skip ASCII text starting with DSDT by looking for valid "low" revision
+>8 ubyte <17 ACPI Machine Language file
+# assume that ACPI tables size are lower than 16 MiB
+#>4 ulelong <0x01000000
+# DSDT for Differentiated System Description Table
+>>0 string x '%.4s'
+#!:mime application/octet-stream
+!:mime application/x-intel-aml
+!:ext aml
+# the manufacture model ID like: VBOXBIOS BXDSDT
+>>16 string >\0 %.8s
+# OEM revision of DSDT for supplied OEM Table ID like: 0 1 2 20090511
+>>>24 ulelong x %x
+# OEM ID like: INTEL VBOX (VirtualBox) BXDSDT (qemu) MEDION or \030\001\0\0 for s3pt.aml
+>>10 ubyte >040 by %c
+>>>11 ubyte >040 \b%c
+>>>>12 ubyte >040 \b%c
+>>>>>13 ubyte >040 \b%c
+>>>>>>14 ubyte >040 \b%c
+>>>>>>>15 ubyte >040 \b%c
+# This field also sets the global integer width for the AML interpreter.
+# Values less than two will cause the interpreter to use 32-bit.
+# Values of two and greater will cause the interpreter to use full 64-bit.
+# 16 for asf!.aml, 67 fo rsdp.aml
+>>8 ubyte x \b, revision %u
+# length, in bytes, of the entire DSDT (including the header)
+>>4 ulelong x \b, %u bytes
+# entire table must sum to zero
+#>>9 ubyte x \b, checksum %#x
+# vendor ID for the ASL Compiler like: INTL MSFT ...
+>>28 string >\0 \b, created by %.4s
+# revision number of the ASL Compiler like: 20051117 20140724 20190703 20200110 ...
+>>>32 ulelong x %x
+
diff --git a/contrib/libs/libmagic/magic/Magdir/interleaf b/contrib/libs/libmagic/magic/Magdir/interleaf
new file mode 100644
index 0000000000..8e3aaf57da
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/interleaf
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: interleaf,v 1.10 2009/09/19 16:28:10 christos Exp $
+# interleaf: file(1) magic for InterLeaf TPS:
+#
+0 string =\210OPS Interleaf saved data
+0 string =<!OPS Interleaf document text
+>5 string ,\ Version\ = \b, version
+>>17 string >\0 %.3s
diff --git a/contrib/libs/libmagic/magic/Magdir/island b/contrib/libs/libmagic/magic/Magdir/island
new file mode 100644
index 0000000000..f40521a036
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/island
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# $File: island,v 1.5 2009/09/19 16:28:10 christos Exp $
+# island: file(1) magic for IslandWite/IslandDraw, from SunOS 5.5.1
+# "/etc/magic":
+# From: guy@netapp.com (Guy Harris)
+#
+4 string pgscriptver IslandWrite document
+13 string DrawFile IslandDraw document
+
diff --git a/contrib/libs/libmagic/magic/Magdir/ispell b/contrib/libs/libmagic/magic/Magdir/ispell
new file mode 100644
index 0000000000..57a6e9e789
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ispell
@@ -0,0 +1,63 @@
+
+#------------------------------------------------------------------------------
+# $File: ispell,v 1.8 2009/09/19 16:28:10 christos Exp $
+# ispell: file(1) magic for ispell
+#
+# Ispell 3.0 has a magic of 0x9601 and ispell 3.1 has 0x9602. This magic
+# will match 0x9600 through 0x9603 in *both* little endian and big endian.
+# (No other current magic entries collide.)
+#
+# Updated by Daniel Quinlan (quinlan@yggdrasil.com)
+#
+0 leshort&0xFFFC 0x9600 little endian ispell
+>0 byte 0 hash file (?),
+>0 byte 1 3.0 hash file,
+>0 byte 2 3.1 hash file,
+>0 byte 3 hash file (?),
+>2 leshort 0x00 8-bit, no capitalization, 26 flags
+>2 leshort 0x01 7-bit, no capitalization, 26 flags
+>2 leshort 0x02 8-bit, capitalization, 26 flags
+>2 leshort 0x03 7-bit, capitalization, 26 flags
+>2 leshort 0x04 8-bit, no capitalization, 52 flags
+>2 leshort 0x05 7-bit, no capitalization, 52 flags
+>2 leshort 0x06 8-bit, capitalization, 52 flags
+>2 leshort 0x07 7-bit, capitalization, 52 flags
+>2 leshort 0x08 8-bit, no capitalization, 128 flags
+>2 leshort 0x09 7-bit, no capitalization, 128 flags
+>2 leshort 0x0A 8-bit, capitalization, 128 flags
+>2 leshort 0x0B 7-bit, capitalization, 128 flags
+>2 leshort 0x0C 8-bit, no capitalization, 256 flags
+>2 leshort 0x0D 7-bit, no capitalization, 256 flags
+>2 leshort 0x0E 8-bit, capitalization, 256 flags
+>2 leshort 0x0F 7-bit, capitalization, 256 flags
+>4 leshort >0 and %d string characters
+0 beshort&0xFFFC 0x9600 big endian ispell
+>1 byte 0 hash file (?),
+>1 byte 1 3.0 hash file,
+>1 byte 2 3.1 hash file,
+>1 byte 3 hash file (?),
+>2 beshort 0x00 8-bit, no capitalization, 26 flags
+>2 beshort 0x01 7-bit, no capitalization, 26 flags
+>2 beshort 0x02 8-bit, capitalization, 26 flags
+>2 beshort 0x03 7-bit, capitalization, 26 flags
+>2 beshort 0x04 8-bit, no capitalization, 52 flags
+>2 beshort 0x05 7-bit, no capitalization, 52 flags
+>2 beshort 0x06 8-bit, capitalization, 52 flags
+>2 beshort 0x07 7-bit, capitalization, 52 flags
+>2 beshort 0x08 8-bit, no capitalization, 128 flags
+>2 beshort 0x09 7-bit, no capitalization, 128 flags
+>2 beshort 0x0A 8-bit, capitalization, 128 flags
+>2 beshort 0x0B 7-bit, capitalization, 128 flags
+>2 beshort 0x0C 8-bit, no capitalization, 256 flags
+>2 beshort 0x0D 7-bit, no capitalization, 256 flags
+>2 beshort 0x0E 8-bit, capitalization, 256 flags
+>2 beshort 0x0F 7-bit, capitalization, 256 flags
+>4 beshort >0 and %d string characters
+# ispell 4.0 hash files kromJx <kromJx@crosswinds.net>
+# Ispell 4.0
+0 string ISPL ispell
+>4 long x hash file version %d,
+>8 long x lexletters %d,
+>12 long x lexsize %d,
+>16 long x hashsize %d,
+>20 long x stblsize %d
diff --git a/contrib/libs/libmagic/magic/Magdir/isz b/contrib/libs/libmagic/magic/Magdir/isz
new file mode 100644
index 0000000000..4d9c030844
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/isz
@@ -0,0 +1,15 @@
+
+#------------------------------------------------------------------------------
+# $File: isz,v 1.5 2019/04/19 00:42:27 christos Exp $
+# ISO Zipped file format
+# https://www.ezbsystems.com/isz/iszspec.txt
+0 string IsZ! ISO Zipped file
+>4 byte x \b, header size %u
+>5 byte x \b, version %u
+>8 lelong x \b, serial %u
+#12 leshort x \b, sector size %u
+#>16 lelong x \b, total sectors %u
+>17 byte >0 \b, password protected
+#>24 lequad x \b, segment size %llu
+#>32 lelong x \b, blocks %u
+#>36 lelong x \b, block size %u
diff --git a/contrib/libs/libmagic/magic/Magdir/java b/contrib/libs/libmagic/magic/Magdir/java
new file mode 100644
index 0000000000..d361275535
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/java
@@ -0,0 +1,52 @@
+
+#------------------------------------------------------------
+# $File: java,v 1.22 2023/01/11 23:59:49 christos Exp $
+# Java ByteCode and Mach-O binaries (e.g., Mac OS X) use the
+# same magic number, 0xcafebabe, so they are both handled
+# in the entry called "cafebabe".
+#------------------------------------------------------------
+# Java serialization
+# From Martin Pool (m.pool@pharos.com.au)
+0 beshort 0xaced Java serialization data
+>2 beshort >0x0004 \b, version %d
+
+0 belong 0xfeedfeed Java KeyStore
+!:mime application/x-java-keystore
+0 belong 0xcececece Java JCE KeyStore
+!:mime application/x-java-jce-keystore
+
+# Java source
+0 regex \^import.*;$ Java source
+!:mime text/x-java
+
+# Java HPROF dumps
+# https://java.net/downloads/heap-snapshot/hprof-binary-format.html
+0 string JAVA\x20PROFILE\x201.0.
+>0x12 byte 0
+>>0x11 ubyte-0x31 <2 Java HPROF dump,
+>>>0x17 beqdate/1000 x created %s
+
+# Java jmod module
+# See https://hg.openjdk.java.net/jdk9/jdk9/jdk/file/tip/src/java.base/share/classes/jdk/internal/jmod/JmodFile.java
+# Grr. 2 byte magic "JM", really? In 2019?
+0 belong 0x4a4d0100 Java jmod module version 1.0
+!:mime application/x-java-jmod
+
+# Java jlinked image
+# See https://hg.openjdk.java.net/jdk9/jdk9/jdk/file/tip/src/java.base/share/native/libjimage/imageFile.hpp
+0 belong 0xcafedada Java module image (big endian)
+>4 beshort >0x00 \b, version %d
+>6 beshort x \b.%d
+!:mime application/x-java-image
+
+0 lelong 0xcafedada Java module image (little endian)
+>6 leshort >0x00 \b, version %d
+>4 leshort x \b.%d
+!:mime application/x-java-image
+
+# JAR Manifest & Signature File
+# Reference: https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html
+0 string/t Manifest-Version:\x201.0 JAR Manifest
+!:ext MF
+0 string/t Signature-Version:\x201.0 JAR Signature File
+!:ext SF
diff --git a/contrib/libs/libmagic/magic/Magdir/javascript b/contrib/libs/libmagic/magic/Magdir/javascript
new file mode 100644
index 0000000000..90a09cce46
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/javascript
@@ -0,0 +1,171 @@
+
+#------------------------------------------------------------------------------
+# $File: javascript,v 1.5 2023/01/12 00:02:16 christos Exp $
+# javascript: magic for javascript and node.js scripts.
+#
+0 string/tw #!/bin/node Node.js script executable
+!:mime application/javascript
+0 string/tw #!/usr/bin/node Node.js script executable
+!:mime application/javascript
+0 string/tw #!/bin/nodejs Node.js script executable
+!:mime application/javascript
+0 string/tw #!/usr/bin/nodejs Node.js script executable
+!:mime application/javascript
+0 string/t #!/usr/bin/env\ node Node.js script executable
+!:mime application/javascript
+0 string/t #!/usr/bin/env\ nodejs Node.js script executable
+!:mime application/javascript
+
+# JavaScript
+# The strength is increased to beat the C++ & HTML rules
+0 search "use\x20strict" JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 search 'use\x20strict' JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 regex module(\\.|\\[["'])exports.*= JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 regex \^(const|var|let).*=.*require\\( JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 regex \^export\x20(function|class|default|const|var|let|async)\x20 JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 regex \\((async\x20)?function[(\x20] JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 regex \^(import|export).*\x20from\x20 JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 regex \^(import|export)\x20["']\\./ JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 regex \^require\\(["'] JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+0 regex typeof.*[!=]== JavaScript source
+!:strength +30
+!:mime application/javascript
+!:ext js
+
+# React Native minified JavaScript
+0 search/128 __BUNDLE_START_TIME__= React Native minified JavaScript
+!:strength +30
+!:mime application/javascript
+!:ext bundle/jsbundle
+
+# Hermes by Facebook https://hermesengine.dev/
+# https://github.com/facebook/hermes/blob/master/include/hermes/\
+# BCGen/HBC/BytecodeFileFormat.h#L24
+0 lequad 0x1F1903C103BC1FC6 Hermes JavaScript bytecode
+>8 lelong x \b, version %d
+
+# v8 JavaScript engine bytecode
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://v8.dev/docs/ignition
+# Note: used in bytenode and NW.js protected source code
+# V8 bytecode extraction was added in NodeJS v5.7.0 (V8 4.6.85.31).
+# Version information is provided for some v8 versions found in NodeJS releases.
+2 uleshort =0xC0DE
+>0 ulelong^0xC0DE0000 >0
+# Reservation table starts at 40
+>>40 ulelong&0xFFFFFF00 =0x80000000
+# Stub keys present
+>>>24 ulelong >0
+>>>>0 ulelong^0xC0DE0000 x v8 bytecode, external reference table size: %u bytes,
+>>>>4 ulelong =0xEE4BF478 version 5.1.281.111,
+>>>>4 ulelong =0xC4A0100C version 5.5.372.43,
+>>>>8 ulelong x source size: %u bytes,
+>>>>12 ulelong x cpu features: %#08X,
+>>>>16 ulelong x flag hash: %#08X,
+>>>>20 ulelong x %u reservations,
+>>>>28 ulelong x payload size: %u bytes,
+>>>>32 ulelong x checksum1: %#08X,
+>>>>36 ulelong x checksum2: %#08X
+# No stub keys
+>>>24 ulelong =0
+>>>>0 ulelong^0xC0DE0000 x v8 bytecode, external reference table size: %u bytes,
+>>>>4 ulelong =0x54F0AD81 version 6.2.414.46,
+>>>>4 ulelong =0X7D1BF182 version 6.2.414.54,
+>>>>4 ulelong =0x35BA122E version 6.2.414.77,
+>>>>4 ulelong =0X9319F9C2 version 6.2.414.78,
+>>>>4 ulelong =0xB1240060 version 6.6.346.32,
+>>>>4 ulelong =0x2B757060 version 6.7.288.46,
+>>>>4 ulelong =0x09D147AA version 6.7.288.49,
+>>>>4 ulelong =0xF4D4F48A version 6.8.275.32,
+>>>>4 ulelong =0xD3961326 version 7.0.276.38,
+>>>>8 ulelong x source size: %u bytes,
+>>>>12 ulelong x cpu features: %#08X,
+>>>>16 ulelong x flag hash: %#08X,
+>>>>20 ulelong x %u reservations,
+>>>>28 ulelong x payload size: %u bytes,
+>>>>32 ulelong x checksum1: %#08X,
+>>>>36 ulelong x checksum2: %#08X
+# Reservation table starts at 32
+>>32 ulelong&0xFFFFFF00 =0x80000000
+# Second checksum present
+>>>28 ulelong >0
+>>>>0 ulelong^0xC0DE0000 x v8 bytecode, external reference table size: %u bytes,
+>>>>4 ulelong =0x21DDF627 version 7.4.288.21,
+>>>>4 ulelong =0x1FC9FE84 version 7.4.288.27,
+>>>>4 ulelong =0x60A99E8B version 7.5.288.22,
+>>>>4 ulelong =0x4F665E90 version 7.6.303.29,
+>>>>4 ulelong =0xC7ACFCDE version 7.7.299.11,
+>>>>4 ulelong =0x7F641D8F version 7.7.299.13,
+>>>>4 ulelong =0xFD9A4F2E version 7.8.279.17,
+>>>>4 ulelong =0x3A845324 version 7.8.279.23,
+>>>>4 ulelong =0xFF52FEAF version 7.9.317.25,
+>>>>8 ulelong x source size: %u bytes,
+>>>>12 ulelong x flag hash: %#08X,
+>>>>16 ulelong x %u reservations,
+>>>>20 ulelong x payload size: %u bytes,
+>>>>24 ulelong x checksum1: %#08X,
+>>>>28 ulelong x checksum2: %#08X
+# No second checksum
+>>>28 ulelong =0
+>>>>0 ulelong^0xC0DE0000 x v8 bytecode, external reference table size: %u bytes,
+>>>>4 ulelong =0x8725E0F8 version 8.1.307.30,
+>>>>4 ulelong =0x09ED1289 version 8.1.307.31,
+>>>>4 ulelong =0xA5728C87 version 8.3.110.9,
+>>>>4 ulelong =0xB45C5D30 version 8.4.371.23,
+>>>>4 ulelong =0xED9C278B version 8.4.371.19,
+>>>>4 ulelong =0xD27BFF42 version 8.6.395.16,
+>>>>8 ulelong x source size: %u bytes,
+>>>>12 ulelong x flag hash: %#08X,
+>>>>16 ulelong x %u reservations,
+>>>>20 ulelong x payload size: %u bytes,
+>>>>24 ulelong x payload checksum: %#08X
+# No reservation table and code starts at 24
+>>32 ulelong =0
+>>>0 ulelong^0xC0DE0000 x v8 bytecode, external reference table size: %u bytes,
+>>>4 ulelong =0x9A6F0B0F version 9.0.257.17,
+>>>4 ulelong =0x271D5D1E version 9.0.257.24,
+>>>4 ulelong =0x4EEA75DF version 9.0.257.25,
+>>>4 ulelong =0x80809479 version 9.1.269.36,
+>>>4 ulelong =0x55C46F65 version 9.1.269.38,
+>>>4 ulelong =0x8A9C758A version 9.2.230.21,
+>>>4 ulelong =0x9712F0E1 version 9.3.345.16,
+>>>4 ulelong =0x29593715 version 9.4.146.19,
+>>>4 ulelong =0xCD991825 version 9.4.146.24,
+>>>4 ulelong =0xACDD64EE version 9.4.146.26,
+>>>4 ulelong =0xC96B4CD5 version 9.5.172.21,
+>>>4 ulelong =0xBCCE4578 version 9.5.172.25,
+>>>4 ulelong =0xA2EEA077 version 9.6.180.15,
+>>>4 ulelong =0xFD350011 version 10.1.124.8,
+>>>4 ulelong =0xBEF4028F version 10.2.154.13,
+>>>4 ulelong =0xAF632352 version 10.2.154.4,
+>>>8 ulelong x source size: %u bytes,
+>>>12 ulelong x flag hash: %#08X,
+>>>16 ulelong x payload size: %u bytes,
+>>>20 ulelong x payload checksum: %#08X
diff --git a/contrib/libs/libmagic/magic/Magdir/jpeg b/contrib/libs/libmagic/magic/Magdir/jpeg
new file mode 100644
index 0000000000..9cebadad70
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/jpeg
@@ -0,0 +1,252 @@
+
+#------------------------------------------------------------------------------
+# $File: jpeg,v 1.38 2022/12/02 17:42:04 christos Exp $
+# JPEG images
+# SunOS 5.5.1 had
+#
+# 0 string \377\330\377\340 JPEG file
+# 0 string \377\330\377\356 JPG file
+#
+# both of which turn into "JPEG image data" here.
+#
+0 belong 0xffd8fff7 JPEG-LS image data
+!:mime image/jls
+!:ext jls
+>0 use jpeg
+
+0 belong&0xffffff00 0xffd8ff00 JPEG image data
+!:mime image/jpeg
+!:apple 8BIMJPEG
+!:strength *3
+!:ext jpeg/jpg/jpe/jfif
+>0 use jpeg
+
+0 name jpeg
+>6 string JFIF \b, JFIF standard
+# The following added by Erik Rossen <rossen@freesurf.ch> 1999-09-06
+# in a vain attempt to add image size reporting for JFIF. Note that these
+# tests are not fool-proof since some perfectly valid JPEGs are currently
+# impossible to specify in magic(4) format.
+# First, a little JFIF version info:
+>>11 byte x \b %d.
+>>12 byte x \b%02d
+# Next, the resolution or aspect ratio of the image:
+>>13 byte 0 \b, aspect ratio
+>>13 byte 1 \b, resolution (DPI)
+>>13 byte 2 \b, resolution (DPCM)
+>>14 beshort x \b, density %dx
+>>16 beshort x \b%d
+>>4 beshort x \b, segment length %d
+# Next, show thumbnail info, if it exists:
+>>18 byte !0 \b, thumbnail %dx
+>>>19 byte x \b%d
+>6 string Exif \b, Exif standard: [
+>>12 indirect/r x
+>>12 string x \b]
+
+# Jump to the first segment
+>(4.S+4) use jpeg_segment
+
+# This uses recursion...
+0 name jpeg_segment
+>0 beshort 0xFFFE
+# Recursion handled by FFE0
+#>>(2.S+2) use jpeg_segment
+>>2 pstring/HJ x \b, comment: "%s"
+
+>0 beshort 0xFFC0
+>>(2.S+2) use jpeg_segment
+>>4 byte x \b, baseline, precision %d
+>>7 beshort x \b, %dx
+>>5 beshort x \b%d
+>>9 byte x \b, components %d
+
+>0 beshort 0xFFC1
+>>(2.S+2) use jpeg_segment
+>>4 byte x \b, extended sequential, precision %d
+>>7 beshort x \b, %dx
+>>5 beshort x \b%d
+>>9 byte x \b, components %d
+
+>0 beshort 0xFFC2
+>>(2.S+2) use jpeg_segment
+>>4 byte x \b, progressive, precision %d
+>>7 beshort x \b, %dx
+>>5 beshort x \b%d
+>>9 byte x \b, components %d
+
+# Define Huffman Tables
+>0 beshort 0xFFC4
+>>(2.S+2) use jpeg_segment
+
+>0 beshort 0xFFE1
+# Recursion handled by FFE0
+#>>(2.S+2) use jpeg_segment
+>>4 string Exif \b, Exif Standard: [
+>>>10 indirect/r x
+>>>10 string x \b]
+
+# Application specific markers
+>0 beshort&0xFFE0 =0xFFE0
+>>(2.S+2) use jpeg_segment
+
+# DB: Define Quantization tables
+# DD: Define Restart interval [XXX: wrong here, it is 4 bytes]
+# D8: Start of image
+# D9: End of image
+# Dn: Restart
+>0 beshort&0xFFD0 =0xFFD0
+>>0 beshort&0xFFE0 !0xFFE0
+>>>(2.S+2) use jpeg_segment
+
+#>0 beshort x unknown %#x
+#>>(2.S+2) use jpeg_segment
+
+# HSI is Handmade Software's proprietary JPEG encoding scheme
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/HSI_JPEG
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-hsi1.trid.xml
+# Note: called by TrID "HSI JPEG bitmap"
+0 string hsi1 JPEG image data, HSI proprietary
+#!:mime application/octet-stream
+!:mime image/x-hsi
+!:ext hsi/jpg
+
+# From: David Santinoli <david@santinoli.com>
+0 string \x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A JPEG 2000
+# delete from ./animation (version 1.87) with jP (=6A50h) magic at offset 4
+# From: Johan van der Knijff <johan.vanderknijff@kb.nl>
+# Added sub-entries for JP2, JPX, JPM and MJ2 formats; added mimetypes
+# https://github.com/bitsgalore/jp2kMagic
+#
+# Now read value of 'Brand' field, which yields a few possibilities:
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/JP2
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-jpeg2k.trid.xml
+# Note: called by TrID "JPEG 2000 bitmap"
+>20 string \x6a\x70\x32\x20 Part 1 (JP2)
+# aliases image/jpeg2000, image/jpeg2000-image, image/x-jpeg2000-image
+!:mime image/jp2
+!:ext jp2
+# URL: http://fileformats.archiveteam.org/wiki/JPX
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-jpx.trid.xml
+# Note: called by TrID "JPEG 2000 eXtended bitmap"
+>20 string \x6a\x70\x78\x20 Part 2 (JPX)
+!:mime image/jpx
+!:ext jpf/jpx
+# URL: http://fileformats.archiveteam.org/wiki/JPM
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-jpm.trid.xml
+# Note: called by TrID "JPEG 2000 eXtended bitmap"
+>20 string \x6a\x70\x6d\x20 Part 6 (JPM)
+!:mime image/jpm
+!:ext jpm
+# URL: http://fileformats.archiveteam.org/wiki/MJ2
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/v/video-mj2.trid.xml
+# Note: called by TrID "Motion JPEG 2000 video"
+>20 string \x6d\x6a\x70\x32 Part 3 (MJ2)
+!:mime video/mj2
+!:ext mj2/mjp2
+
+# Type: JPEG 2000 codesream
+# From: Mathieu Malaterre <mathieu.malaterre@gmail.com>
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/JPEG_2000_codestream
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-jpc.trid.xml
+# Note: called by TrID "JPEG-2000 Code Stream bitmap"
+0 belong 0xff4fff51 JPEG 2000 codestream
+# value like: 0701h FF50h
+#>45 ubeshort x \b, at 45 %#4.4x
+#!:mime application/octet-stream
+# https://reposcope.com/mimetype/image/x-jp2-codestream
+!:mime image/x-jp2-codestream
+!:ext jpc/j2c/j2k
+# MAYBE also JHC like in byte_causal.jhc ?
+# WHAT IS THAT? DEAD ENTRY?
+#45 beshort 0xff52
+
+# JPEG extended range
+# URL: http://fileformats.archiveteam.org/wiki/JPEG_XR
+# Reference: https://www.itu.int/rec/T-REC-T.832
+# http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-wmp.trid.xml
+# Note: called by TrID "JPEG XR bitmap"
+0 string \x49\x49\xbc
+# FILE_VERSION_ID; shall be equal to 1; other values are reserved for future use
+>3 byte 1
+# FIRST_IFD_OFFSET; shall be an integer multiple of 2; so skip DROID fmt-590-signature-id-931.wdp
+>>4 lelong%2 0 JPEG-XR
+#!:mime image/vnd.ms-photo
+!:mime image/jxr
+# NO example for HDP !
+!:ext jxr/wdp/hdp
+# MAYBE also WMP ?
+#!:ext jxr/wdp/hdp/wmp
+# moved from ./images (version 1.205 ), merged and
+# partly verified by XnView `nconvert -info abydos.jxr FLOWER.wdp`
+# example: https://web.archive.org/web/20160403012904/
+# http://shikino.co.jp/solution/upfile/FLOWER.wdp.zip
+>90 bequad 0x574D50484F544F00
+>>98 byte&0x08 =0x08 \b, hard tiling
+>>99 byte&0x80 =0x80 \b, tiling present
+>>99 byte&0x40 =0x40 \b, codestream present
+>>99 byte&0x38 x \b, spatial xform=
+>>99 byte&0x38 0x00 \bTL
+>>99 byte&0x38 0x08 \bBL
+>>99 byte&0x38 0x10 \bTR
+>>99 byte&0x38 0x18 \bBR
+>>99 byte&0x38 0x20 \bBT
+>>99 byte&0x38 0x28 \bRB
+>>99 byte&0x38 0x30 \bLT
+>>99 byte&0x38 0x38 \bLB
+>>100 byte&0x80 =0x80 \b, short header
+>>>102 beshort+1 x \b, %d
+>>>104 beshort+1 x \bx%d
+>>100 byte&0x80 =0x00 \b, long header
+>>>102 belong+1 x \b, %x
+>>>106 belong+1 x \bx%x
+>>101 beshort&0xf x \b, bitdepth=
+>>>101 beshort&0xf 0x0 \b1-WHITE=1
+>>>101 beshort&0xf 0x1 \b8
+>>>101 beshort&0xf 0x2 \b16
+>>>101 beshort&0xf 0x3 \b16-SIGNED
+>>>101 beshort&0xf 0x4 \b16-FLOAT
+>>>101 beshort&0xf 0x5 \b(reserved 5)
+>>>101 beshort&0xf 0x6 \b32-SIGNED
+>>>101 beshort&0xf 0x7 \b32-FLOAT
+>>>101 beshort&0xf 0x8 \b5
+>>>101 beshort&0xf 0x9 \b10
+>>>101 beshort&0xf 0xa \b5-6-5
+>>>101 beshort&0xf 0xb \b(reserved %d)
+>>>101 beshort&0xf 0xc \b(reserved %d)
+>>>101 beshort&0xf 0xd \b(reserved %d)
+>>>101 beshort&0xf 0xe \b(reserved %d)
+>>>101 beshort&0xf 0xf \b1-BLACK=1
+>>101 beshort&0xf0 x \b, colorfmt=
+>>>101 beshort&0xf0 0x00 \bYONLY
+>>>101 beshort&0xf0 0x10 \bYUV240
+>>>101 beshort&0xf0 0x20 \bYWV422
+>>>101 beshort&0xf0 0x30 \bYWV444
+>>>101 beshort&0xf0 0x40 \bCMYK
+>>>101 beshort&0xf0 0x50 \bCMYKDIRECT
+>>>101 beshort&0xf0 0x60 \bNCOMPONENT
+>>>101 beshort&0xf0 0x70 \bRGB
+>>>101 beshort&0xf0 0x80 \bRGBE
+>>>101 beshort&0xf0 >0x80 \b(reserved %#x)
+
+# JPEG XL
+# From: Ian Tester
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/JPEG_XL
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-jxl.trid.xml
+# Note: called by TrID "JPEG XL bitmap"
+0 string \xff\x0a JPEG XL codestream
+!:mime image/jxl
+!:ext jxl
+
+# JPEG XL (transcoded JPEG file)
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/JPEG_XL
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-jxl-iso.trid.xml
+# Note: called by TrID "JPEG XL bitmap (ISOBMFF)"
+0 string \x00\x00\x00\x0cJXL\x20\x0d\x0a\x87\x0a JPEG XL container
+!:mime image/jxl
+!:ext jxl
diff --git a/contrib/libs/libmagic/magic/Magdir/karma b/contrib/libs/libmagic/magic/Magdir/karma
new file mode 100644
index 0000000000..938a51d5ed
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/karma
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: karma,v 1.8 2015/08/29 07:10:35 christos Exp $
+# karma: file(1) magic for Karma data files
+#
+# From <rgooch@atnf.csiro.au>
+
+0 string KarmaRHD\040Version Karma Data Structure Version
+>16 belong x %u
diff --git a/contrib/libs/libmagic/magic/Magdir/kde b/contrib/libs/libmagic/magic/Magdir/kde
new file mode 100644
index 0000000000..dda5819a9b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/kde
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# $File: kde,v 1.5 2010/11/25 15:00:12 christos Exp $
+# kde: file(1) magic for KDE
+
+0 string/t [KDE\ Desktop\ Entry] KDE desktop entry
+!:mime application/x-kdelnk
+0 string/t #\ KDE\ Config\ File KDE config file
+!:mime application/x-kdelnk
+0 string/t #\ xmcd xmcd database file for kscd
+!:mime text/x-xmcd
diff --git a/contrib/libs/libmagic/magic/Magdir/keepass b/contrib/libs/libmagic/magic/Magdir/keepass
new file mode 100644
index 0000000000..3d26efa5c5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/keepass
@@ -0,0 +1,20 @@
+
+#------------------------------------------------------------------------------
+# $File: keepass,v 1.2 2019/04/19 00:42:27 christos Exp $
+# keepass: file(1) magic for KeePass file
+#
+# Keepass Password Safe:
+# * original one: https://keepass.info/
+# * *nix port: https://www.keepassx.org/
+# * android port: https://code.google.com/p/keepassdroid/
+
+0 lelong 0x9AA2D903 Keepass password database
+>4 lelong 0xB54BFB65 1.x KDB
+>>48 lelong >0 \b, %d groups
+>>52 lelong >0 \b, %d entries
+>>8 lelong&0x0f 1 \b, SHA-256
+>>8 lelong&0x0f 2 \b, AES
+>>8 lelong&0x0f 4 \b, RC4
+>>8 lelong&0x0f 8 \b, Twofish
+>>120 lelong >0 \b, %d key transformation rounds
+>4 lelong 0xB54BFB67 2.x KDBX
diff --git a/contrib/libs/libmagic/magic/Magdir/kerberos b/contrib/libs/libmagic/magic/Magdir/kerberos
new file mode 100644
index 0000000000..df6dc52364
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/kerberos
@@ -0,0 +1,45 @@
+
+#------------------------------------------------------------------------------
+# $File: kerberos,v 1.3 2019/04/19 00:42:27 christos Exp $
+# kerberos: MIT kerberos file binary formats
+#
+
+# This magic entry is for demonstration purposes and could be improved
+# if the following features were implemented in file:
+#
+# Strings inside [[ .. ]] in the descriptions have special meanings and
+# are not printed.
+#
+# - Provide some form of iteration in number of components
+# [[${counter}=%d]] in the description
+# then append
+# [${counter}--] in the offset of the entries
+# - Provide a way to round the next offset
+# Add [R:4] after the offset?
+# - Provide a way to have optional entries
+# XXX: Syntax:
+# - Provide a way to "save" entries to print them later.
+# if the description is [[${name}=%s]], then nothing is
+# printed and a subsequent entry in the same magic file
+# can refer to ${name}
+# - Provide a way to format strings as hex values
+#
+# https://www.gnu.org/software/shishi/manual/html_node/\
+# The-Keytab-Binary-File-Format.html
+#
+
+0 name keytab_entry
+#>0 beshort x \b, size=%d
+#>2 beshort x \b, components=%d
+>4 pstring/H x \b, realm=%s
+>>&0 pstring/H x \b, principal=%s/
+>>>&0 pstring/H x \b%s
+>>>>&0 belong x \b, type=%d
+>>>>>&0 bedate x \b, date=%s
+>>>>>>&0 byte x \b, kvno=%u
+#>>>>>>>&0 pstring/H x
+#>>>>>>>>&0 belong x
+#>>>>>>>>>>&0 use keytab_entry
+
+0 belong 0x05020000 Kerberos Keytab file
+>4 use keytab_entry
diff --git a/contrib/libs/libmagic/magic/Magdir/kicad b/contrib/libs/libmagic/magic/Magdir/kicad
new file mode 100644
index 0000000000..212a550a49
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/kicad
@@ -0,0 +1,85 @@
+
+#------------------------------------------------------------------------------
+# $File: kicad,v 1.2 2020/05/06 14:03:28 christos Exp $
+# kicad: file(1) magic for KiCad files
+#
+# See
+#
+# http://kicad-pcb.org
+#
+
+# KiCad Schematic Document
+0 string (kicad_sch
+>10 byte 0x20 KiCad Schematic Document
+!:ext kicad_sch/kicad_sch-bak
+>>11 string (version
+>>>19 byte 0x20
+>>>>20 regex [0-9.]+ (Version %s)
+
+# KiCad Schematic Document (Legacy)
+0 string EESchema
+>8 byte 0x20
+>>9 string Schematic
+>>>18 byte 0x20 KiCad Schematic Document (Legacy)
+!:ext sch/bak
+>>>>24 string Version
+>>>>>31 byte 0x20
+>>>>>>32 string x (Version %s)
+
+# KiCad Symbol Library
+0 string (kicad_symbol_lib
+>17 byte 0x20 KiCad Symbol Library
+!:ext kicad_sym
+>>18 string (version
+>>>26 byte 0x20
+>>>>27 regex [0-9.]+ (Version %s)
+
+# KiCad Symbol Library (Legacy)
+0 string EESchema-LIBRARY
+>16 byte 0x20 KiCad Symbol Library (Legacy)
+!:ext lib
+>>17 string Version
+>>>24 byte 0x20
+>>>>25 string x (Version %s)
+
+# KiCad Symbol Library Documentation (Legacy)
+0 string EESchema-DOCLIB
+>15 byte 0x20 KiCad Symbol Library Documentation (Legacy)
+!:ext dcm
+>>17 string Version
+>>>24 byte 0x20
+>>>>25 string x (Version %s)
+
+# KiCad Board Layout
+0 string (kicad_pcb
+>10 byte 0x20 KiCad Board Layout
+!:ext kicad_pcb/kicad_pcb-bak
+>>11 string (version
+>>>19 byte 0x20
+>>>>20 regex [0-9.]+ (Version %s)
+
+# KiCad Footprint
+0 string (module
+>7 byte 0x20 KiCad Footprint
+!:ext kicad_mod
+
+# KiCad Footprint (Legacy)
+0 string PCBNEW-LibModule-V1 KiCad Footprint (Legacy)
+!:ext mod
+
+# KiCad Netlist
+0 string (export
+>7 byte 0x20 KiCad Netlist
+!:ext net
+
+# KiCad Symbol Library Table
+0 string (sym_lib_table
+>14 byte 0xA KiCad Symbol Library Table
+>14 byte 0xD KiCad Symbol Library Table
+>14 byte 0x20 KiCad Symbol Library Table
+
+# KiCad Footprint Library Table
+0 string (fp_lib_table
+>13 byte 0xA KiCad Footprint Library Table
+>13 byte 0xD KiCad Footprint Library Table
+>13 byte 0x20 KiCad Footprint Library Table
diff --git a/contrib/libs/libmagic/magic/Magdir/kml b/contrib/libs/libmagic/magic/Magdir/kml
new file mode 100644
index 0000000000..904f3b5d5f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/kml
@@ -0,0 +1,34 @@
+
+#------------------------------------------------------------------------------
+# $File: kml,v 1.6 2019/05/21 04:50:10 christos Exp $
+# Type: Google KML, formerly Keyhole Markup Language
+# Future development of this format has been handed
+# over to the Open Geospatial Consortium.
+# https://www.opengeospatial.org/standards/kml/
+# From: Asbjoern Sloth Toennesen <asbjorn@lila.io>
+0 string/t \<?xml
+>20 search/400 \ xmlns=
+>>&0 regex ['"]http://earth.google.com/kml Google KML document
+!:mime application/vnd.google-earth.kml+xml
+>>>&1 string 2.0' \b, version 2.0
+>>>&1 string 2.1' \b, version 2.1
+>>>&1 string 2.2' \b, version 2.2
+
+#------------------------------------------------------------------------------
+# Type: OpenGIS KML, formerly Keyhole Markup Language
+# This standard is maintained by the
+# Open Geospatial Consortium.
+# https://www.opengeospatial.org/standards/kml/
+# From: Asbjoern Sloth Toennesen <asbjorn@lila.io>
+>>&0 regex ['"]http://www.opengis.net/kml OpenGIS KML document
+!:mime application/vnd.google-earth.kml+xml
+>>>&1 string/t 2.2 \b, version 2.2
+
+#------------------------------------------------------------------------------
+# Type: Google KML Archive (ZIP based)
+# https://code.google.com/apis/kml/documentation/kml_tut.html
+# From: Asbjoern Sloth Toennesen <asbjorn@lila.io>
+0 string PK\003\004
+>4 byte 0x14
+>>30 string doc.kml Compressed Google KML Document, including resources.
+!:mime application/vnd.google-earth.kmz
diff --git a/contrib/libs/libmagic/magic/Magdir/lammps b/contrib/libs/libmagic/magic/Magdir/lammps
new file mode 100644
index 0000000000..5424383db8
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/lammps
@@ -0,0 +1,64 @@
+#------------------------------------------------------------------------------
+# $File: lammps,v 1.1 2021/03/14 16:24:18 christos Exp $
+#
+
+# Magic file patterns for use with file(1) for the
+# LAMMPS molecular dynamics simulation software.
+# https://lammps.sandia.gov
+#
+# Updated: 2021-03-14 by akohlmey@gmail.com
+
+# Binary restart file for the LAMMPS MD code
+0 string LammpS\ RestartT LAMMPS binary restart file
+>0x14 long x (rev %d),
+>>0x20 string x Version %s,
+>>>0x10 lelong 0x0001 Little Endian
+>>>0x10 lelong 0x1000 Big Endian
+
+# Atom style binary dump file for the LAMMPS MD code
+# written on a little endian machine
+0 lequad -8
+>0x08 string DUMPATOM LAMMPS atom style binary dump
+>>0x14 long x (rev %d),
+>>>0x10 lelong 0x0001 Little Endian,
+>>>>0x18 lequad x First time step: %lld
+
+# written on a big endian machine
+0 bequad -8
+>0x08 string DUMPATOM LAMMPS atom style binary dump
+>>0x14 belong x (rev %d),
+>>>0x10 lelong 0x1000 Big Endian,
+>>>>0x18 bequad x First time step: %lld
+
+# Atom style binary dump file for the LAMMPS MD code
+# written on a little endian machine
+0 lequad -10
+>0x08 string DUMPCUSTOM LAMMPS custom style binary dump
+>>0x16 lelong x (rev %d),
+>>>0x12 lelong 0x0001 Little Endian,
+>>>>0x1a lequad x First time step: %lld
+
+# written on a big endian machine
+0 bequad -10
+>0x08 string DUMPCUSTOM LAMMPS custom style binary dump
+>>0x16 belong x (rev %d),
+>>>0x12 lelong 0x1000 Big Endian,
+>>>>0x1a bequad x First time step: %lld
+
+# LAMMPS log file
+0 string LAMMPS\ ( LAMMPS log file
+>8 regex/16 [0-9]+\ [A-Za-z]+\ [0-9]+ written by version %s
+
+# Data file written either by LAMMPS, msi2lmp or VMD/TopoTools
+0 string LAMMPS\ data\ file LAMMPS data file
+>0x12 string CGCMM\ style written by TopoTools
+>0x12 string msi2lmp written by msi2lmp
+>0x11 string via\ write_data written by LAMMPS
+
+# LAMMPS data file written by OVITO
+0 string #\ LAMMPS\ data\ file LAMMPS data file
+>0x13 string written\ by\ OVITO written by OVITO
+
+# LAMMPS text mode dump file
+0 string ITEM:\ TIMESTEP LAMMPS text mode dump,
+>15 regex/16 [0-9]+ First time step: %s
diff --git a/contrib/libs/libmagic/magic/Magdir/lecter b/contrib/libs/libmagic/magic/Magdir/lecter
new file mode 100644
index 0000000000..6ae87c12c0
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/lecter
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# $File: lecter,v 1.4 2009/09/19 16:28:10 christos Exp $
+# DEC SRC Virtual Paper: Lectern files
+# Karl M. Hegbloom <karlheg@inetarena.com>
+0 string lect DEC SRC Virtual Paper Lectern file
diff --git a/contrib/libs/libmagic/magic/Magdir/lex b/contrib/libs/libmagic/magic/Magdir/lex
new file mode 100644
index 0000000000..cc9fac5e1f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/lex
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: lex,v 1.6 2009/09/19 16:28:10 christos Exp $
+# lex: file(1) magic for lex
+#
+# derived empirically, your offsets may vary!
+0 search/100 yyprevious C program text (from lex)
+>3 search/1 >\0 for %s
+# C program text from GNU flex, from Daniel Quinlan <quinlan@yggdrasil.com>
+0 search/100 generated\ by\ flex C program text (from flex)
+# lex description file, from Daniel Quinlan <quinlan@yggdrasil.com>
+0 search/1 %{ lex description text
diff --git a/contrib/libs/libmagic/magic/Magdir/lif b/contrib/libs/libmagic/magic/Magdir/lif
new file mode 100644
index 0000000000..3474a48d23
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/lif
@@ -0,0 +1,50 @@
+
+#------------------------------------------------------------------------------
+# $File: lif,v 1.11 2022/10/19 20:15:16 christos Exp $
+# lif: file(1) magic for lif
+#
+# (Daniel Quinlan <quinlan@yggdrasil.com>)
+#
+# Modified by: Joerg Jenderek
+# URL: https://www.hp9845.net/9845/projects/hpdir/
+# https://github.com/bug400/lifutils
+# Reference: https://www.hp9845.net/9845/downloads/manuals/LIF_excerpt_64941-90906_flpRef_Jan84.pdf
+# Note: called by TrID "HP Logical Interchange Format disk image"
+0 beshort 0x8000
+# GRR: line above is too general as it catches also compressed DEGAS low-res bitmap *.pc1
+# skip many compressed DEGAS low-res bitmap *.pc1 by test for unused bytes
+>14 beshort =0
+# skip MUNCHIE.PC1 BOARD.PC1 ENEMIES.PC1 by test for low version number
+>>20 ubeshort <0x0100
+# skip DROID fmt-840-signature-id-1195.adx fmt-840-signature-id-1199.adx by test for ASCII like volume name
+>>>2 ubelong >0x2020201F
+>>>>0 use lif-file
+0 name lif-file
+# LIF ID
+>0 beshort x lif file
+!:mime application/x-lif-disk
+# lif used by Tony Duell LIF utilities; enhanced version by Joachim Siebold use also dat; hpi used by hpdir
+!:ext lif/hpi/dat
+# volume label; A-Z 0-9 _ ; default are 6 spaces
+>2 string x "%.6s"
+#>2 ubelong x LABEL=%8.8x
+# version number; 0 for systems without extensions or 1 for model 64000
+>20 ubeshort x \b, version %u
+# LIF identifier; 010000 for system 3000
+>12 beshort !0x1000 \b, LIF identifier %#x
+# directory start address in units like: 2
+>8 ubelong x \b, directory
+>8 ubelong !2 start address %u
+# length of directory like: 2 4 7 10 12 14 (for model 64000) 16 18 20 24 30 50 57 77 80
+>16 ubelong x length %u
+# level 1 extensions
+>20 beshort =0
+>>24 ubequad !0 \b, for extensions %#llx...
+>20 beshort >0
+>>24 ubequad !0 \b, extensions %#llx...
+# word 21-126 reserved for extensions and future use; set to nil
+>42 ubequad !0 \b, RESERVED %#llx
+# lif first file name for standard directory; 0xffff... means uninitialized
+>8 ubelong 2
+>>512 string <\xff\xff \b, 1st file %-.10s
+
diff --git a/contrib/libs/libmagic/magic/Magdir/linux b/contrib/libs/libmagic/magic/Magdir/linux
new file mode 100644
index 0000000000..ae181148df
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/linux
@@ -0,0 +1,627 @@
+
+#------------------------------------------------------------------------------
+# $File: linux,v 1.85 2023/07/17 14:40:09 christos Exp $
+# linux: file(1) magic for Linux files
+#
+# Values for Linux/i386 binaries, from Daniel Quinlan <quinlan@yggdrasil.com>
+# The following basic Linux magic is useful for reference, but using
+# "long" magic is a better practice in order to avoid collisions.
+#
+# 2 leshort 100 Linux/i386
+# >0 leshort 0407 impure executable (OMAGIC)
+# >0 leshort 0410 pure executable (NMAGIC)
+# >0 leshort 0413 demand-paged executable (ZMAGIC)
+# >0 leshort 0314 demand-paged executable (QMAGIC)
+#
+0 lelong 0x00640107 Linux/i386 impure executable (OMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x00640108 Linux/i386 pure executable (NMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x0064010b Linux/i386 demand-paged executable (ZMAGIC)
+>16 lelong 0 \b, stripped
+0 lelong 0x006400cc Linux/i386 demand-paged executable (QMAGIC)
+>16 lelong 0 \b, stripped
+#
+0 string \007\001\000 Linux/i386 object file
+>20 lelong >0x1020 \b, DLL library
+# Linux-8086 stuff:
+0 string \01\03\020\04 Linux-8086 impure executable
+>28 long !0 not stripped
+0 string \01\03\040\04 Linux-8086 executable
+>28 long !0 not stripped
+#
+0 string \243\206\001\0 Linux-8086 object file
+#
+0 string \01\03\020\20 Minix-386 impure executable
+>28 long !0 not stripped
+0 string \01\03\040\20 Minix-386 executable
+>28 long !0 not stripped
+0 string \01\03\04\20 Minix-386 NSYM/GNU executable
+>28 long !0 not stripped
+# core dump file, from Bill Reynolds <bill@goshawk.lanl.gov>
+216 lelong 0421 Linux/i386 core file
+!:strength / 2
+>220 string >\0 of '%s'
+>200 lelong >0 (signal %d)
+#
+# LILO boot/chain loaders, from Daniel Quinlan <quinlan@yggdrasil.com>
+# this can be overridden by the DOS executable (COM) entry
+2 string LILO Linux/i386 LILO boot/chain loader
+#
+# Linux make config build file, from Ole Aamot <oka@oka.no>
+# Updated by Ken Sharp
+28 string make\ config Linux make config build file (old)
+49 search/70 Kernel\ Configuration Linux make config build file
+
+#
+# PSF fonts, from H. Peter Anvin <hpa@yggdrasil.com>
+# Updated by Adam Buchbinder <adam.buchbinder@gmail.com>
+# See: https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
+0 leshort 0x0436 Linux/i386 PC Screen Font v1 data,
+>2 byte&0x01 0 256 characters,
+>2 byte&0x01 !0 512 characters,
+>2 byte&0x02 0 no directory,
+>2 byte&0x02 !0 Unicode directory,
+>3 byte >0 8x%d
+0 string \x72\xb5\x4a\x86\x00\x00 Linux/i386 PC Screen Font v2 data,
+>16 lelong x %d characters,
+>12 lelong&0x01 0 no directory,
+>12 lelong&0x01 !0 Unicode directory,
+>28 lelong x %d
+>24 lelong x \bx%d
+
+# Linux swap and hibernate files
+# Linux kernel: include/linux/swap.h
+# util-linux: libblkid/src/superblocks/swap.c
+
+# format v0, unsupported since 2002
+0xff6 string SWAP-SPACE Linux old swap file, 4k page size
+0x1ff6 string SWAP-SPACE Linux old swap file, 8k page size
+0x3ff6 string SWAP-SPACE Linux old swap file, 16k page size
+0x7ff6 string SWAP-SPACE Linux old swap file, 32k page size
+0xfff6 string SWAP-SPACE Linux old swap file, 64k page size
+
+# format v1, supported since 1998
+0 name linux-swap
+>0x400 lelong 1 little endian, version %u,
+>>0x404 lelong x size %u pages,
+>>0x408 lelong x %u bad pages,
+>0x400 belong 1 big endian, version %u,
+>>0x404 belong x size %u pages,
+>>0x408 belong x %u bad pages,
+>0x41c string \0 no label,
+>0x41c string >\0 LABEL=%s,
+>0x40c ubelong x UUID=%08x
+>0x410 ubeshort x \b-%04x
+>0x412 ubeshort x \b-%04x
+>0x414 ubeshort x \b-%04x
+>0x416 ubelong x \b-%08x
+>0x41a ubeshort x \b%04x
+
+0xff6 string SWAPSPACE2 Linux swap file, 4k page size,
+>0 use linux-swap
+0x1ff6 string SWAPSPACE2 Linux swap file, 8k page size,
+>0 use linux-swap
+0x3ff6 string SWAPSPACE2 Linux swap file, 16k page size,
+>0 use linux-swap
+0x7ff6 string SWAPSPACE2 Linux swap file, 32k page size,
+>0 use linux-swap
+0xfff6 string SWAPSPACE2 Linux swap file, 64k page size,
+>0 use linux-swap
+
+0 name linux-hibernate
+>0 string S1SUSPEND \b, with SWSUSP1 image
+>0 string S2SUSPEND \b, with SWSUSP2 image
+>0 string ULSUSPEND \b, with uswsusp image
+>0 string LINHIB0001 \b, with compressed hibernate image
+>0 string \xed\xc3\x02\xe9\x98\x56\xe5\x0c \b, with tuxonice image
+>0 default x \b, with unknown hibernate image
+
+0xfec string SWAPSPACE2 Linux swap file, 4k page size,
+>0 use linux-swap
+>0xff6 use linux-hibernate
+0x1fec string SWAPSPACE2 Linux swap file, 8k page size,
+>0 use linux-swap
+>0x1ff6 use linux-hibernate
+0x3fec string SWAPSPACE2 Linux swap file, 16k page size,
+>0 use linux-swap
+>0x3ff6 use linux-hibernate
+0x7fec string SWAPSPACE2 Linux swap file, 32k page size,
+>0 use linux-swap
+>0x7ff6 use linux-hibernate
+0xffec string SWAPSPACE2 Linux swap file, 64k page size,
+>0 use linux-swap
+>0xfff6 use linux-hibernate
+
+#
+# Linux kernel boot images, from Albert Cahalan <acahalan@cs.uml.edu>
+# and others such as Axel Kohlmeyer <akohlmey@rincewind.chemie.uni-ulm.de>
+# and Nicolas Lichtmaier <nick@debian.org>
+# All known start with: b8 c0 07 8e d8 b8 00 90 8e c0 b9 00 01 29 f6 29
+# Linux kernel boot images (i386 arch) (Wolfram Kleff)
+# URL: https://www.kernel.org/doc/Documentation/x86/boot.txt
+514 string HdrS Linux kernel
+!:strength + 55
+# often no extension like in linux, vmlinuz, bzimage or memdisk but sometimes
+# Acronis Recovery kernel64.dat and Plop Boot Manager plpbtrom.bin
+# DamnSmallLinux 1.5 damnsmll.lnx
+!:ext /dat/bin/lnx
+>510 leshort 0xAA55 x86 boot executable
+>>518 leshort >0x1ff
+>>>529 byte 0 zImage,
+>>>529 byte 1 bzImage,
+>>>526 lelong >0
+>>>>(526.s+0x200) string >\0 version %s,
+>>498 leshort 1 RO-rootFS,
+>>498 leshort 0 RW-rootFS,
+>>508 leshort >0 root_dev %#X,
+>>502 leshort >0 swap_dev %#X,
+>>504 leshort >0 RAMdisksize %u KB,
+>>506 leshort 0xFFFF Normal VGA
+>>506 leshort 0xFFFE Extended VGA
+>>506 leshort 0xFFFD Prompt for Videomode
+>>506 leshort >0 Video mode %d
+# This also matches new kernels, which were caught above by "HdrS".
+0 belong 0xb8c0078e Linux kernel
+>0x1e3 string Loading version 1.3.79 or older
+>0x1e9 string Loading from prehistoric times
+
+# System.map files - Nicolas Lichtmaier <nick@debian.org>
+8 search/1 \ A\ _text Linux kernel symbol map text
+
+# LSM entries - Nicolas Lichtmaier <nick@debian.org>
+0 search/1 Begin3 Linux Software Map entry text
+0 search/1 Begin4 Linux Software Map entry text (new format)
+
+# From Matt Zimmerman, enhanced for v3 by Matthew Palmer
+0 belong 0x4f4f4f4d User-mode Linux COW file
+>4 belong <3 \b, version %d
+>>8 string >\0 \b, backing file %s
+>4 belong >2 \b, version %d
+>>32 string >\0 \b, backing file %s
+
+############################################################################
+# Linux kernel versions
+
+0 string \xb8\xc0\x07\x8e\xd8\xb8\x00\x90 Linux
+>497 leshort 0 x86 boot sector
+>>514 belong 0x8e of a kernel from the dawn of time!
+>>514 belong 0x908ed8b4 version 0.99-1.1.42
+>>514 belong 0x908ed8b8 for memtest86
+
+>497 leshort !0 x86 kernel
+>>504 leshort >0 RAMdisksize=%u KB
+>>502 leshort >0 swap=%#X
+>>508 leshort >0 root=%#X
+>>>498 leshort 1 \b-ro
+>>>498 leshort 0 \b-rw
+>>506 leshort 0xFFFF vga=normal
+>>506 leshort 0xFFFE vga=extended
+>>506 leshort 0xFFFD vga=ask
+>>506 leshort >0 vga=%d
+>>514 belong 0x908ed881 version 1.1.43-1.1.45
+>>514 belong 0x15b281cd
+>>>0xa8e belong 0x55AA5a5a version 1.1.46-1.2.13,1.3.0
+>>>0xa99 belong 0x55AA5a5a version 1.3.1,2
+>>>0xaa3 belong 0x55AA5a5a version 1.3.3-1.3.30
+>>>0xaa6 belong 0x55AA5a5a version 1.3.31-1.3.41
+>>>0xb2b belong 0x55AA5a5a version 1.3.42-1.3.45
+>>>0xaf7 belong 0x55AA5a5a version 1.3.46-1.3.72
+>>514 string HdrS
+>>>518 leshort >0x1FF
+>>>>529 byte 0 \b, zImage
+>>>>529 byte 1 \b, bzImage
+>>>>(526.s+0x200) string >\0 \b, version %s
+
+# Linux boot sector thefts.
+0 belong 0xb8c0078e Linux
+>0x1e6 belong 0x454c4b53 ELKS Kernel
+>0x1e6 belong !0x454c4b53 style boot sector
+
+############################################################################
+# Linux S390 kernel image
+# Created by: Jan Kaluza <jkaluza@redhat.com>
+8 string \x02\x00\x00\x18\x60\x00\x00\x50\x02\x00\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40\x40\x40\x40\x40 Linux S390
+>0x00010000 search/b/4096 \x00\x0a\x00\x00\x8b\xad\xcc\xcc
+# 64bit
+>>&0 string \xc1\x00\xef\xe3\xf0\x68\x00\x00 Z10 64bit kernel
+>>&0 string \xc1\x00\xef\xc3\x00\x00\x00\x00 Z9-109 64bit kernel
+>>&0 string \xc0\x00\x20\x00\x00\x00\x00\x00 Z990 64bit kernel
+>>&0 string \x00\x00\x00\x00\x00\x00\x00\x00 Z900 64bit kernel
+# 32bit
+>>&0 string \x81\x00\xc8\x80\x00\x00\x00\x00 Z10 32bit kernel
+>>&0 string \x81\x00\xc8\x80\x00\x00\x00\x00 Z9-109 32bit kernel
+>>&0 string \x80\x00\x20\x00\x00\x00\x00\x00 Z990 32bit kernel
+>>&0 string \x80\x00\x00\x00\x00\x00\x00\x00 Z900 32bit kernel
+
+############################################################################
+# Linux ARM compressed kernel image
+# From: Kevin Cernekee <cernekee@gmail.com>
+# Update: Joerg Jenderek
+0x24 lelong 0x016f2818 Linux kernel ARM boot executable zImage
+# There are three possible situations: LE, BE with LE bootloader and pure BE.
+# In order to aid telling these apart a new endian flag was added. In order
+# to support kernels before the flag and BE with LE bootloader was added we'll
+# do a negative check against the BE variant of the flag when we see a LE magic.
+>0x30 belong !0x04030201 (little-endian)
+# raspian "kernel7.img", Vu+ Ultimo4K "kernel_auto.bin"
+!:ext img/bin
+>0x30 belong 0x04030201 (big-endian)
+0x24 belong 0x016f2818 Linux kernel ARM boot executable zImage (big-endian)
+
+############################################################################
+# Linux AARCH64 kernel image
+0x38 lelong 0x644d5241 Linux kernel ARM64 boot executable Image
+>0x18 lelong ^1 \b, little-endian
+>0x18 lelong &1 \b, big-endian
+>0x18 lelong &2 \b, 4K pages
+>0x18 lelong &4 \b, 16K pages
+>0x18 lelong &6 \b, 32K pages
+
+############################################################################
+# Linux 8086 executable
+0 lelong&0xFF0000FF 0xC30000E9 Linux-Dev86 executable, headerless
+>5 string .
+>>4 string >\0 \b, libc version %s
+
+0 lelong&0xFF00FFFF 0x4000301 Linux-8086 executable
+>2 byte&0x01 !0 \b, unmapped zero page
+>2 byte&0x20 0 \b, impure
+>2 byte&0x20 !0
+>>2 byte&0x10 !0 \b, A_EXEC
+>2 byte&0x02 !0 \b, A_PAL
+>2 byte&0x04 !0 \b, A_NSYM
+>2 byte&0x08 !0 \b, A_STAND
+>2 byte&0x40 !0 \b, A_PURE
+>2 byte&0x80 !0 \b, A_TOVLY
+>28 long !0 \b, not stripped
+>37 string .
+>>36 string >\0 \b, libc version %s
+
+# 0 lelong&0xFF00FFFF 0x10000301 ld86 I80386 executable
+# 0 lelong&0xFF00FFFF 0xB000301 ld86 M68K executable
+# 0 lelong&0xFF00FFFF 0xC000301 ld86 NS16K executable
+# 0 lelong&0xFF00FFFF 0x17000301 ld86 SPARC executable
+
+# SYSLINUX boot logo files (from 'ppmtolss16' sources)
+# https://www.syslinux.org/wiki/index.php/SYSLINUX#Display_graphic_from_filename:
+# file extension .lss .16
+0 lelong =0x1413f33d SYSLINUX' LSS16 image data
+# syslinux-4.05/mime/image/x-lss16.xml
+!:mime image/x-lss16
+>4 leshort x \b, width %d
+>6 leshort x \b, height %d
+
+0 string OOOM User-Mode-Linux's Copy-On-Write disk image
+>4 belong x version %d
+
+# SE Linux policy database
+# From: Mike Frysinger <vapier@gentoo.org>
+0 lelong 0xf97cff8c SE Linux policy
+>16 lelong x v%d
+>20 lelong 1 MLS
+>24 lelong x %d symbols
+>28 lelong x %d ocons
+
+# Linux Logical Volume Manager (LVM)
+# Emmanuel VARAGNAT <emmanuel.varagnat@guzu.net>
+#
+# System ID, UUID and volume group name are 128 bytes long
+# but they should never be full and initialized with zeros...
+#
+# LVM1
+#
+0x0 string/b HM\001 LVM1 (Linux Logical Volume Manager), version 1
+>0x12c string/b >\0 , System ID: %s
+
+0x0 string/b HM\002 LVM1 (Linux Logical Volume Manager), version 2
+>0x12c string/b >\0 , System ID: %s
+
+# LVM2
+#
+# It seems that the label header can be in one the four first sector
+# of the disk... (from _find_labeller in lib/label/label.c of LVM2)
+#
+# 0x200 seems to be the common case
+0 name lvm2
+# display UUID in LVM format + display all 32 bytes (instead of max string length: 31)
+>0x0 string >\x2f \b, UUID: %.6s
+>0x6 string >\x2f \b-%.4s
+>0xa string >\x2f \b-%.4s
+>0xe string >\x2f \b-%.4s
+>0x12 string >\x2f \b-%.4s
+>0x16 string >\x2f \b-%.4s
+>0x1a string >\x2f \b-%.6s
+>0x20 lequad x \b, size: %lld
+
+
+# read the offset to add to the start of the header, and the header
+# start in 0x200
+0x218 string/b LVM2\ 001 LVM2 PV (Linux Logical Volume Manager)
+>&(&-12.l-0x20) use lvm2
+
+0x018 string/b LVM2\ 001 LVM2 PV (Linux Logical Volume Manager)
+>&(&-12.l-0x20) use lvm2
+
+0x418 string/b LVM2\ 001 LVM2 PV (Linux Logical Volume Manager)
+>&(&-12.l-0x20) use lvm2
+
+0x618 string/b LVM2\ 001 LVM2 PV (Linux Logical Volume Manager)
+>&(&-12.l-0x20) use lvm2
+
+# LVM snapshot
+# from Jason Farrel
+0 string SnAp LVM Snapshot (CopyOnWrite store)
+>4 lelong !0 - valid,
+>4 lelong 0 - invalid,
+>8 lelong x version %d,
+>12 lelong x chunk_size %d
+
+# SE Linux policy database
+0 lelong 0xf97cff8c SE Linux policy
+>16 lelong x v%d
+>20 lelong 1 MLS
+>24 lelong x %d symbols
+>28 lelong x %d ocons
+
+# Summary: Xen saved domain file
+# Created by: Radek Vokal <rvokal@redhat.com>
+0 string LinuxGuestRecord Xen saved domain
+>20 search/256 (name
+>>&1 string x (name %s)
+
+# Type: Xen, the virtual machine monitor
+# From: Radek Vokal <rvokal@redhat.com>
+0 string LinuxGuestRecord Xen saved domain
+#>2 regex \(name\ [^)]*\) %s
+>20 search/256 (name (name
+>>&1 string x %s...)
+
+# Systemd journald files
+# See https://www.freedesktop.org/wiki/Software/systemd/journal-files/.
+# From: Zbigniew Jedrzejewski-Szmek <zbyszek@in.waw.pl>
+# Update: Joerg Jenderek
+# URL: https://systemd.io/JOURNAL_FILE_FORMAT/
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/j/journal-sysd.trid.xml
+# Note: called "systemd journal" by TrID
+# verified by `journalctl --file=user-1000.journal`
+# check magic signature[8]
+0 string LPKSHHRH
+# check that state is one of known values
+# STATE_OFFLINE~0 STATE_ONLINE~1 STATE_ARCHIVED~2
+>16 ubyte&252 0
+# check that each half of three unique id128s is non-zero
+# file_id
+>>24 ubequad >0
+>>>32 ubequad >0
+# machine_id
+>>>>40 ubequad >0
+>>>>>48 ubequad >0
+# boot_id; last writer
+>>>>>>56 ubequad >0
+>>>>>>>64 ubequad >0 Journal file
+#!:mime application/octet-stream
+!:mime application/x-linux-journal
+# provide more info
+# head_entry_realtime; contains a POSIX timestamp stored in microseconds
+>>>>>>>>184 leqdate/1000000 !0 \b, %s
+>>>>>>>>184 leqdate 0 empty
+# If a file is closed after writing the state field should be set to STATE_OFFLINE
+>>>>>>>>16 ubyte 0 \b,
+# for offline and empty only journal~ extension found
+>>>>>>>>>184 leqdate 0 offline
+# https://man7.org/linux/man-pages/man8/systemd-journald.service.8.html
+# GRR: add char ~ inside parse_ext in ../../src/apprentice.c to avoid in file version 5.44 error like:
+# Magdir/linux, 463: Warning: EXTENSION type ` journal~' has bad char '~'
+!:ext journal~
+# for offline and non empty often *.journal~ but also user-1001.journal
+>>>>>>>>>184 leqdate !0 offline
+!:ext journal/journal~
+# if a file is opened for writing the state field should be set to STATE_ONLINE
+>>>>>>>>16 ubyte 1 \b,
+# for online and empty only journal~ extension found
+>>>>>>>>>184 leqdate 0 online
+# system@0005febee06e2ff2-f7ea54d10e4346ff.journal~
+!:ext journal~
+# for online and non empty only journal extension found
+>>>>>>>>>184 leqdate !0 online
+# system.journal user-1000.journal
+!:ext journal
+# after a file has been rotated it should be set to STATE_ARCHIVED
+>>>>>>>>16 ubyte 2 \b, archived
+!:ext journal
+# no *.journal~ found
+#!:ext journal/journal~
+# compatible_flags
+>>>>>>>>8 ulelong&1 1 \b, sealed
+# incompatible_flags; COMPRESSED_XZ~1 COMPRESSED_LZ4~2 KEYED_HASH~4 COMPRESSED_ZSTD~8 COMPACT~16
+#>>>>>>>>12 ulelong x FLAGS=%#x
+>>>>>>>>12 ulelong&1 1 \b, compressed
+>>>>>>>>12 ulelong&2 !0 \b, compressed lz4
+>>>>>>>>12 ulelong&4 !0 \b, keyed hash siphash24
+>>>>>>>>12 ulelong&8 !0 \b, compressed zstd
+>>>>>>>>12 ulelong&16 !0 \b, compact
+# uint8_t reserved[7]; apparently nil
+#>>17 long !0 \b, reserved %#8.8x
+# seqnum_id; like: 0 e623691afec94b5aa968ae2d726c49cc f98b2af481924b29 8d6816ca3639edc6
+#>>>>>>>>72 ubequad x \b, seqnum_id %#16.16llx
+#>>>>>>>>80 ubequad x b%16.16llx
+# header_size like: 100h
+>>>>>>>>88 ulequad !0x100h \b, header size %#llx
+# arena_size like: 0 7fff00h ffff00h 17fff00h
+#>>>>>>>>96 ulequad >0 \b, arena size %#llx
+# data_hash_table_offset like: 0 15f0h 15f0h
+#>>>>>>>>104 ulequad >0 \b, hash table offset %#llx
+# data_hash_table_size like: 0 38e380h
+#>>>>>>>>112 ulequad >0 \b, hash table size %#llx
+# field_hash_table_offset like: 0 110h
+#>>>>>>>>120 ulequad >0 \b, field hash table offset %#llx
+# field_hash_table_size like: 0 14d0h
+#>>>>>>>>128 ulequad >0 \b, field hash table size %#llx
+# tail_object_offset like: 0 43edd8h 511278h c68968h d487d0h efaa98h
+#>>>>>>>>136 ulequad >0 \b, tail object offset %#llx
+# n_objects like: 0 1032h 5a2eh 92bdh a8b5h aa75h 112adh 40c23h 4714eh
+#>>>>>>>>144 ulequad >0 \b, objects %#llx
+# n_entries like: 0 3aeh 235ah 2dc4h 3125h 16129h 187a1h
+>>>>>>>>152 ulequad >0 \b, entries %#llx
+# tail_entry_seqnum like: 0 1988h 16249h 24c12h 24c12h 41e64h 9fefdh
+#>>>>>>>>160 ulequad >0 \b, tail entry seqnum %#llx
+# head_entry_seqnum like: 0 1h 15dbh 6552h 213bfh 213bfh 3e672h 9a28ah
+#>>>>>>>>168 ulequad >0 \b, head entry seqnum %#llx
+# entry_array_offset like: 0 390058h 3909d8h 3909e0h
+#>>>>>>>>176 ulequad >0 \b, entry array offset %#llx
+
+# BCache backing and cache devices
+# From: Gabriel de Perthuis <g2p.code@gmail.com>
+0x1008 lequad 8
+>0x1018 string \xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81 BCache
+>>0x1010 ulequad 0 cache device
+>>0x1010 ulequad 1 backing device
+>>0x1010 ulequad 3 cache device
+>>0x1010 ulequad 4 backing device
+>>0x1048 string >0 \b, label "%.32s"
+>>0x1028 ubelong x \b, uuid %08x
+>>0x102c ubeshort x \b-%04x
+>>0x102e ubeshort x \b-%04x
+>>0x1030 ubeshort x \b-%04x
+>>0x1032 ubelong x \b-%08x
+>>0x1036 ubeshort x \b%04x
+>>0x1038 ubelong x \b, set uuid %08x
+>>0x103c ubeshort x \b-%04x
+>>0x103e ubeshort x \b-%04x
+>>0x1040 ubeshort x \b-%04x
+>>0x1042 ubelong x \b-%08x
+>>0x1046 ubeshort x \b%04x
+
+# Linux device tree:
+# File format description can be found in the Linux kernel sources at
+# Documentation/devicetree/booting-without-of.txt
+# From Christoph Biedl
+0 belong 0xd00dfeed
+# structure must be within blob, strings are omitted to handle devicetrees > 1M
+>&(8.L) byte x
+>>20 belong >1 Device Tree Blob version %d
+>>>4 belong x \b, size=%d
+>>>20 belong >1
+>>>>28 belong x \b, boot CPU=%d
+>>>20 belong >2
+>>>>32 belong x \b, string block size=%d
+>>>20 belong >16
+>>>>36 belong x \b, DT structure block size=%d
+
+# glibc locale archive as defined in glibc locale/locarchive.h
+0 lelong 0xde020109 locale archive
+>24 lelong x %d strings
+
+# Linux Software RAID (mdadm)
+# Russell Coker <russell@coker.com.au>
+0 name linuxraid
+>16 belong x UUID=%8x:
+>20 belong x \b%8x:
+>24 belong x \b%8x:
+>28 belong x \b%8x
+>32 string x name=%s
+>72 lelong x level=%d
+>92 lelong x disks=%d
+
+4096 lelong 0xa92b4efc Linux Software RAID
+>4100 lelong x version 1.2 (%d)
+>4096 use linuxraid
+
+0 lelong 0xa92b4efc Linux Software RAID
+>4 lelong x version 1.1 (%d)
+>0 use linuxraid
+
+# Summary: Database file for mlocate
+# Description: A database file as used by mlocate, a fast implementation
+# of locate/updatedb. It uses merging to reuse the existing
+# database and avoid rereading most of the filesystem. It's
+# the default version of locate on Arch Linux (and others).
+# File path: /var/lib/mlocate/mlocate.db by default (but configurable)
+# Site: https://fedorahosted.org/mlocate/
+# Format docs: https://linux.die.net/man/5/mlocate.db
+# Type: mlocate database file
+# URL: https://fedorahosted.org/mlocate/
+# From: Wander Nauta <info@wandernauta.nl>
+0 string \0mlocate mlocate database
+>12 byte x \b, version %d
+>13 byte 1 \b, require visibility
+>16 string x \b, root %s
+
+# Dump files for iproute2 tool. Generated by the "ip r|a save" command. URL:
+# https://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2
+# From: Pavel Emelyanov <xemul@parallels.com>
+0 lelong 0x45311224 iproute2 routes dump
+0 lelong 0x47361222 iproute2 addresses dump
+
+# Image and service files for CRIU tool.
+# URL: https://criu.org
+# From: Pavel Emelyanov <xemul@parallels.com>
+0 lelong 0x54564319 CRIU image file v1.1
+0 lelong 0x55105940 CRIU service file
+0 lelong 0x58313116 CRIU inventory
+
+# Kdump compressed dump files
+# https://github.com/makedumpfile/makedumpfile/blob/master/IMPLEMENTATION
+
+0 string KDUMP\x20\x20\x20 Kdump compressed dump
+>0 use kdump-compressed-dump
+
+0 name kdump-compressed-dump
+>8 long x v%d
+>12 string >\0 \b, system %s
+>77 string >\0 \b, node %s
+>142 string >\0 \b, release %s
+>207 string >\0 \b, version %s
+>272 string >\0 \b, machine %s
+>337 string >\0 \b, domain %s
+
+# Flattened format
+0 string makedumpfile
+>16 bequad 1
+>>0x1010 string KDUMP\x20\x20\x20 Flattened kdump compressed dump
+>>>0x1010 use kdump-compressed-dump
+
+# Device Tree files
+0 search/1024 /dts-v1/ Device Tree File (v1)
+# beat c code
+!:strength +14
+
+
+# e2fsck undo file
+# David Gilman <davidgilman1@gmail.com>
+0 string E2UNDO02 e2fsck undo file, version 2
+>44 lelong x \b, undo file is
+>>44 lelong&1 0 not finished
+>>44 lelong&1 1 finished
+>48 lelong x \b, undo file features:
+>>48 lelong&1 0 lacks filesystem offset
+>>48 lelong&1 1 has filesystem offset
+>>>64 lequad x at %#llx
+
+# ansible vault (does not really belong here)
+0 string $ANSIBLE_VAULT; Ansible Vault
+>&0 regex [0-9]+\\.[0-9]+ \b, version %s
+>>&0 string ;
+>>>&0 regex [A-Z0-9]+ \b, encryption %s
+
+# From: Joerg Jenderek
+# URL: https://www.gnu.org/software/grub
+# Reference: https://ftp.gnu.org/gnu/grub/grub-2.06.tar.gz
+# grub-2.06/include/grub/keyboard_layouts.h
+# grub-2.06/grub-core/commands/keylayouts.c
+# GRUB_KEYBOARD_LAYOUTS_FILEMAGIC
+0 string GRUBLAYO GRUB Keyboard
+!:mime application/x-grub-keyboard
+!:ext gkb
+# GRUB_KEYBOARD_LAYOUTS_VERSION like: 10
+>8 ulelong !10 \b, version %u
+# 4 grub_uint32_t grub_keyboard_layout[160]
+# for normal french keyboard this is letter a
+>92 ubyte !0x71
+>>92 ubyte >0x40 \b, english q is %c
+#>732 ubyte x \b, english Q is %c
+# for normal german keyboard this is letter z
+>124 ubyte !0x79
+>>124 ubyte >0x40 \b, english y is %c
+#>764 ubyte x \b, english Y is %c
diff --git a/contrib/libs/libmagic/magic/Magdir/lisp b/contrib/libs/libmagic/magic/Magdir/lisp
new file mode 100644
index 0000000000..c854fb7c74
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/lisp
@@ -0,0 +1,78 @@
+
+#------------------------------------------------------------------------------
+# $File: lisp,v 1.27 2020/08/14 19:23:39 christos Exp $
+# lisp: file(1) magic for lisp programs
+#
+# various lisp types, from Daniel Quinlan (quinlan@yggdrasil.com)
+
+# updated by Joerg Jenderek
+# GRR: This lot is too weak
+#0 string ;;
+# windows INF files often begin with semicolon and use CRLF as line end
+# lisp files are mainly created on unix system with LF as line end
+#>2 search/4096 !\r Lisp/Scheme program text
+#>2 search/4096 \r Windows INF file
+
+0 search/4096 (setq\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (defvar\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (defparam\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (defun\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (autoload\ Lisp/Scheme program text
+!:mime text/x-lisp
+0 search/4096 (custom-set-variables\ Lisp/Scheme program text
+!:mime text/x-lisp
+
+# URL: https://en.wikipedia.org/wiki/Emacs_Lisp
+# Reference: https://ftp.gnu.org/old-gnu/emacs/elisp-manual-18-1.03.tar.gz
+# Update: Joerg Jenderek
+# Emacs 18 - this is always correct, but not very magical.
+0 string \012(
+# look for emacs lisp keywords
+# GRR: split regex because it is too long or get error like
+# lisp, 36: Warning: cannot get string from `^(defun|defvar|defconst|defmacro|setq|fset|put|provide|require|'
+>&0 regex \^(defun|defvar|defconst|defmacro|setq|fset) Emacs v18 byte-compiled Lisp data
+!:mime application/x-elc
+# https://searchcode.com/codesearch/view/2173420/
+# not really pure text
+!:apple EMAxTEXT
+!:ext elc
+# remaining regex
+>&0 regex \^(put|provide|require|random) Emacs v18 byte-compiled Lisp data
+!:mime application/x-elc
+!:apple EMAxTEXT
+!:ext elc
+# missed cl.elc dbx.elc simple.elc look like normal lisp starting with ;;;
+
+# Emacs 19+ - ver. recognition added by Ian Springer
+# Also applies to XEmacs 19+ .elc files; could tell them apart with regexs
+# - Chris Chittleborough <cchittleborough@yahoo.com.au>
+# Update: Joerg Jenderek
+0 string ;ELC
+# version\0\0\0
+>4 byte >18 Emacs/XEmacs v%d byte-compiled Lisp data
+# why less than 32 ? does not make sense to me. GNU Emacs version is 24.5 at April 2015
+#>4 byte <32 Emacs/XEmacs v%d byte-compiled Lisp data
+!:mime application/x-elc
+!:apple EMAxTEXT
+!:ext elc
+
+# Files produced by GNU/Emacs pdumper
+0 string DUMPEDGNUEMACS GNU/Emacs pdumper image
+
+# Files produced by CLISP Common Lisp From: Bruno Haible <haible@ilog.fr>
+0 string (SYSTEM::VERSION\040' CLISP byte-compiled Lisp program (pre 2004-03-27)
+0 string (|SYSTEM|::|VERSION|\040' CLISP byte-compiled Lisp program text
+
+0 long 0x70768BD2 CLISP memory image data
+0 long 0xD28B7670 CLISP memory image data, other endian
+
+#.com and .bin for MIT scheme
+0 string \372\372\372\372 MIT scheme (library?)
+
+# From: David Allouche <david@allouche.net>
+0 search/1 \<TeXmacs| TeXmacs document text
+!:mime text/texmacs
diff --git a/contrib/libs/libmagic/magic/Magdir/llvm b/contrib/libs/libmagic/magic/Magdir/llvm
new file mode 100644
index 0000000000..6befe7a8bf
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/llvm
@@ -0,0 +1,22 @@
+
+#------------------------------------------------------------------------------
+# $File: llvm,v 1.10 2023/03/11 17:54:17 christos Exp $
+# llvm: file(1) magic for LLVM byte-codes
+# URL: https://llvm.org/docs/BitCodeFormat.html
+# From: Al Stone <ahs3@fc.hp.com>
+
+0 string llvm LLVM byte-codes, uncompressed
+0 string llvc0 LLVM byte-codes, null compression
+0 string llvc1 LLVM byte-codes, gzip compression
+0 string llvc2 LLVM byte-codes, bzip2 compression
+0 string CPCH LLVM Pre-compiled header file
+
+0 lelong 0x0b17c0de LLVM bitcode, wrapper
+# Are these Mach-O ABI values? They appear to be.
+>16 lelong 0x01000007 x86_64
+>16 lelong 0x00000007 i386
+>16 lelong 0x00000012 ppc
+>16 lelong 0x01000012 ppc64
+>16 lelong 0x0000000c arm
+
+0 string BC\xc0\xde LLVM IR bitcode
diff --git a/contrib/libs/libmagic/magic/Magdir/locoscript b/contrib/libs/libmagic/magic/Magdir/locoscript
new file mode 100644
index 0000000000..87771ccdf9
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/locoscript
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: locoscript,v 1.1 2021/01/03 20:56:25 christos Exp $
+# locoscript: file(1) magic for LocoScript documents and related files
+#
+# See http://fileformats.archiveteam.org/wiki/LocoScript
+0 string JOY\x01\x01 LocoScript 1 document
+0 string JOY\x01\x02 LocoScript 2 document
+0 string JOY\x01\x04 LocoScript 3 document
+0 string JOY\x01\x06 LocoScript 4 document
+0 string DOC\x01\x01 LocoScript PC document
+0 string DOC\x01\x03 LocoScript Professional document
diff --git a/contrib/libs/libmagic/magic/Magdir/lua b/contrib/libs/libmagic/magic/Magdir/lua
new file mode 100644
index 0000000000..ab17374534
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/lua
@@ -0,0 +1,31 @@
+
+#------------------------------------------------------------------------------
+# $File: lua,v 1.8 2020/10/08 23:23:56 christos Exp $
+# lua: file(1) magic for Lua scripting language
+# URL: https://www.lua.org/
+# From: Reuben Thomas <rrt@sc3d.org>, Seo Sanghyeon <tinuviel@sparcs.kaist.ac.kr>
+
+# Lua scripts
+0 search/1/w #!\ /usr/bin/lua Lua script text executable
+!:mime text/x-lua
+0 search/1/w #!\ /usr/local/bin/lua Lua script text executable
+!:mime text/x-lua
+0 search/1 #!/usr/bin/env\ lua Lua script text executable
+!:mime text/x-lua
+0 search/1 #!\ /usr/bin/env\ lua Lua script text executable
+!:mime text/x-lua
+
+# Lua bytecode
+0 string \033Lua Lua bytecode,
+# 2.4 uses 0x23 as its version byte because it shares the format
+# with 2.3 (which was never released publicly).
+>4 byte 0x23 version 2.4
+>4 byte 0x25 version 2.5/3.0
+>4 byte 0x31 version 3.1
+>4 byte 0x32 version 3.2
+>4 byte 0x40 version 4.0
+>4 byte 0x50 version 5.0
+>4 byte 0x51 version 5.1
+>4 byte 0x52 version 5.2
+>4 byte 0x53 version 5.3
+>4 byte 0x54 version 5.4
diff --git a/contrib/libs/libmagic/magic/Magdir/luks b/contrib/libs/libmagic/magic/Magdir/luks
new file mode 100644
index 0000000000..16042517a3
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/luks
@@ -0,0 +1,126 @@
+
+#------------------------------------------------------------------------------
+# $File: luks,v 1.5 2022/09/07 11:23:44 christos Exp $
+# luks: file(1) magic for Linux Unified Key Setup
+# URL: https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup
+# http://fileformats.archiveteam.org/wiki/LUKS
+# From: Anthon van der Neut <anthon@mnt.org>
+# Update: Joerg Jenderek
+# Note: verfied by command like `cryptsetup luksDump /dev/sda3`
+
+0 string LUKS\xba\xbe LUKS encrypted file,
+# https://reposcope.com/mimetype/application/x-raw-disk-image
+!:mime application/x-raw-disk-image
+#!:mime application/x-luks-volume
+# img is the generic extension; no suffix for partitions; luksVolumeHeaderBackUp via zuluCrypt
+!:ext /luks/img/luksVolumeHeaderBackUp
+# version like: 1 2
+>6 beshort x ver %d
+# test for version 1 variant
+>6 beshort 1
+>>0 use luks-v1
+# test for version 2 variant
+>6 beshort >1
+>>0 use luks-v2
+# Reference: https://mirrors.edge.kernel.org/pub/linux/utils/cryptsetup/LUKS_docs/on-disk-format.pdf
+# http://mark0.net/download/triddefs_xml.7z/defs/l/luks.trid.xml
+# display information about LUKS version 1
+0 name luks-v1
+# cipher-name like: aes twofish
+>8 string x [%s,
+# cipher-mode like: xts-plain64 cbc-essiv
+>40 string x %s,
+# hash specification like: sha256 sha1 ripemd160
+>72 string x %s]
+>168 string x UUID: %s
+# NEW PART!
+# payload-offset; start offset of the bulk data
+>104 ubelong x \b, at %#x data
+# key-bytes; number of key bytes; key-bytes*8=MK-bits
+>108 ubelong x \b, %u key bytes
+# mk-digest[20]; master key checksum from PBKDF2
+>112 ubequad x \b, MK digest %#16.16llx
+>>120 ubequad x \b%16.16llx
+>>128 ubelong x \b%8.8x
+# mk-digest-salt[32]; salt parameter for master key PBKDF2
+>132 ubequad x \b, MK salt %#16.16llx
+>>140 ubequad x \b%16.16llx
+>>148 ubequad x \b%16.16llx
+>>156 ubequad x \b%16.16llx
+# mk-digest-iter; iterations parameter for master key PBKDF2
+>164 ubelong x \b, %u MK iterations
+# key slot 1
+>208 ubelong =0x00AC71F3 \b; slot #0
+>>208 use luks-slot
+# key slot 2
+>256 ubelong =0x00AC71F3 \b; slot #1
+>>256 use luks-slot
+# key slot 3
+>304 ubelong =0x00AC71F3 \b; slot #2
+>>304 use luks-slot
+# key slot 4
+>352 ubelong =0x00AC71F3 \b; slot #3
+>>352 use luks-slot
+# key slot 5
+>400 ubelong =0x00AC71F3 \b; slot #4
+>>400 use luks-slot
+# key slot 6
+>448 ubelong =0x00AC71F3 \b; slot #5
+>>448 use luks-slot
+# key slot 7
+>496 ubelong =0x00AC71F3 \b; slot #6
+>>496 use luks-slot
+# key slot 8
+>544 ubelong =0x00AC71F3 \b; slot #7
+>>544 use luks-slot
+# Reference: https://gitlab.com/cryptsetup/LUKS2-docs/-/raw/master/luks2_doc_wip.pdf
+# http://mark0.net/download/triddefs_xml.7z/defs/l/luks2.trid.xml
+# display information about LUKS version 2
+0 name luks-v2
+# hdr_size; size including JSON area called Metadata area by cryptsetup with value like: 16384
+>8 ubequad x \b, header size %llu
+# possible check for MAGIC_2ND after header
+#>(8.Q) string SKUL\xba\xbe \b, 2nd_HEADER_OK
+# seqid; sequence ID, increased on update; called Epoch by cryptsetup with value like: 3 4 8 10
+>16 ubequad x \b, ID %llu
+# label[48]; optional ASCII label or empty; called Label by cryptsetup with value like: "LUKS2_EXT4_ROOT"
+>24 string >\0 \b, label %s
+# csum_alg[32]; checksum algorithm like: sha256 sha1 sha512 wirlpool ripemd160
+>72 string x \b, algo %s
+# salt[64]; salt , unique for every header
+>104 ubequad x \b, salt %#llx...
+# uuid[40]; UID of device as string like: 242256c6-396e-4a35-af5f-5b70cb7af9a7
+>168 string x \b, UUID: %-.40s
+# subsystem[48]; optional owner subsystem label or empty
+>208 string >\0 \b, sub label %-.48s
+# hdr_offset; offset from device start [ bytes ] like: 0
+>256 ubequad !0 \b, offset %llx
+# char _padding [184]; must be zeroed
+#>264 ubequad x \b, padding %#16.16llx
+#>440 ubequad x \b...%16.16llx
+# csum[64]; header checksum
+>448 ubequad x \b, crc %#llx...
+# char _padding4096 [7*512]; Padding , must be zeroed
+#>512 ubequad x \b, more padding %#16.16llx
+#>4088 ubequad x \b...%16.16llx
+# JSON text data terminated by the zero character; unused remainder empty and filled with zeroes like:
+# {"keyslots":{"0":{"type":"luks2","key_size":64,"af":{"type":"luks1","stripes":4000,"hash":"sha256"},"area":{"type":"raw","offse"
+>0x1000 string x \b, at 0x1000 %s
+#>0x1000 indirect x
+# display information (like active) about LUKS1 slot
+0 name luks-slot
+# state of keyslot; 0x00AC71F3~active 0x0000DEAD~inactive
+#>0 ubelong x \b, status %#8.8x
+>0 ubelong =0x00AC71F3 active
+>0 ubelong =0x0000DEAD inactive
+# iteration parameter for PBKDF2
+#>4 ubelong x \b, %u iterations
+# salt parameter for PBKDF2
+#>8 ubequad x \b, salt %#16.16llx
+#>>16 ubequad x \b%16.16llx
+#>>24 ubequad x \b%16.16llx
+#>>32 ubequad x \b%16.16llx
+# start sector of key material like: 8 0x200 0x3f8 0x5f0 0xdd0
+>40 ubelong x \b, %#x material offset
+# number of anti-forensic stripes like: 4000
+>44 ubelong !4000 \b, %u stripes
diff --git a/contrib/libs/libmagic/magic/Magdir/m4 b/contrib/libs/libmagic/magic/Magdir/m4
new file mode 100644
index 0000000000..587ebe80c6
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/m4
@@ -0,0 +1,11 @@
+#------------------------------------------------------------------------------
+# $File: m4,v 1.3 2019/02/27 16:46:23 christos Exp $
+# make: file(1) magic for M4 scripts
+#
+0 search/8192 dnl
+>0 regex \^dnl\ M4 macro processor script text
+!:mime text/x-m4
+0 search/8192 AC_DEFUN
+>0 regex \^AC_DEFUN\\(\\[ M4 macro processor script text
+!:strength + 15
+!:mime text/x-m4
diff --git a/contrib/libs/libmagic/magic/Magdir/mach b/contrib/libs/libmagic/magic/Magdir/mach
new file mode 100644
index 0000000000..7eb98ff34e
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mach
@@ -0,0 +1,303 @@
+
+#------------------------------------------------------------
+# $File: mach,v 1.29 2021/04/26 15:56:00 christos Exp $
+# Mach has two magic numbers, 0xcafebabe and 0xfeedface.
+# Unfortunately the first, cafebabe, is shared with
+# Java ByteCode, so they are both handled in the file "cafebabe".
+# The "feedface" ones are handled herein.
+#------------------------------------------------------------
+# if set, it's for the 64-bit version of the architecture
+# yes, this is separate from the low-order magic number bit
+# it's also separate from the "64-bit libraries" bit in the
+# upper 8 bits of the CPU subtype
+
+# Reference: https://opensource.apple.com/source/cctools/cctools-949.0.1/
+# include/mach-o/loader.h
+# display CPU type as string like: i386 x86_64 ... armv7 armv7k ...
+0 name mach-o-cpu
+>0 belong&0xff000000 0
+#
+# 32-bit ABIs.
+#
+# 1 vax
+>>0 belong&0x00ffffff 1
+>>>4 belong&0x00ffffff 0 vax
+>>>4 belong&0x00ffffff 1 vax11/780
+>>>4 belong&0x00ffffff 2 vax11/785
+>>>4 belong&0x00ffffff 3 vax11/750
+>>>4 belong&0x00ffffff 4 vax11/730
+>>>4 belong&0x00ffffff 5 uvaxI
+>>>4 belong&0x00ffffff 6 uvaxII
+>>>4 belong&0x00ffffff 7 vax8200
+>>>4 belong&0x00ffffff 8 vax8500
+>>>4 belong&0x00ffffff 9 vax8600
+>>>4 belong&0x00ffffff 10 vax8650
+>>>4 belong&0x00ffffff 11 vax8800
+>>>4 belong&0x00ffffff 12 uvaxIII
+>>>4 belong&0x00ffffff >12 vax subarchitecture=%d
+>>0 belong&0x00ffffff 2 romp
+>>0 belong&0x00ffffff 3 architecture=3
+>>0 belong&0x00ffffff 4 ns32032
+>>0 belong&0x00ffffff 5 ns32332
+>>0 belong&0x00ffffff 6 m68k
+# 7 x86
+>>0 belong&0x00ffffff 7
+>>>4 belong&0x0000000f 3 i386
+>>>4 belong&0x0000000f 4 i486
+>>>>4 belong&0x00fffff0 0
+>>>>4 belong&0x00fffff0 0x80 \bsx
+>>>4 belong&0x0000000f 5 i586
+>>>4 belong&0x0000000f 6
+>>>>4 belong&0x00fffff0 0 p6
+>>>>4 belong&0x00fffff0 0x10 pentium_pro
+>>>>4 belong&0x00fffff0 0x20 pentium_2_m0x20
+>>>>4 belong&0x00fffff0 0x30 pentium_2_m3
+>>>>4 belong&0x00fffff0 0x40 pentium_2_m0x40
+>>>>4 belong&0x00fffff0 0x50 pentium_2_m5
+>>>>4 belong&0x00fffff0 >0x50 pentium_2_m%#x
+>>>4 belong&0x0000000f 7 celeron
+>>>>4 belong&0x00fffff0 0x00 \b_m%#x
+>>>>4 belong&0x00fffff0 0x10 \b_m%#x
+>>>>4 belong&0x00fffff0 0x20 \b_m%#x
+>>>>4 belong&0x00fffff0 0x30 \b_m%#x
+>>>>4 belong&0x00fffff0 0x40 \b_m%#x
+>>>>4 belong&0x00fffff0 0x50 \b_m%#x
+>>>>4 belong&0x00fffff0 0x60
+>>>>4 belong&0x00fffff0 0x70 \b_mobile
+>>>>4 belong&0x00fffff0 >0x70 \b_m%#x
+>>>4 belong&0x0000000f 8 pentium_3
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 0x10 \b_m
+>>>>4 belong&0x00fffff0 0x20 \b_xeon
+>>>>4 belong&0x00fffff0 >0x20 \b_m%#x
+>>>4 belong&0x0000000f 9 pentiumM
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 >0x00 \b_m%#x
+>>>4 belong&0x0000000f 10 pentium_4
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 0x10 \b_m
+>>>>4 belong&0x00fffff0 >0x10 \b_m%#x
+>>>4 belong&0x0000000f 11 itanium
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 0x10 \b_2
+>>>>4 belong&0x00fffff0 >0x10 \b_m%#x
+>>>4 belong&0x0000000f 12 xeon
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 0x10 \b_mp
+>>>>4 belong&0x00fffff0 >0x10 \b_m%#x
+>>>4 belong&0x0000000f >12 ia32 family=%d
+>>>>4 belong&0x00fffff0 0x00
+>>>>4 belong&0x00fffff0 >0x00 model=%x
+>>0 belong&0x00ffffff 8 mips
+>>>4 belong&0x00ffffff 1 R2300
+>>>4 belong&0x00ffffff 2 R2600
+>>>4 belong&0x00ffffff 3 R2800
+>>>4 belong&0x00ffffff 4 R2000a
+>>>4 belong&0x00ffffff 5 R2000
+>>>4 belong&0x00ffffff 6 R3000a
+>>>4 belong&0x00ffffff 7 R3000
+>>>4 belong&0x00ffffff >7 subarchitecture=%d
+>>0 belong&0x00ffffff 9 ns32532
+>>0 belong&0x00ffffff 10 mc98000
+>>0 belong&0x00ffffff 11 hppa
+>>>4 belong&0x00ffffff 0 7100
+>>>4 belong&0x00ffffff 1 7100LC
+>>>4 belong&0x00ffffff >1 subarchitecture=%d
+>>0 belong&0x00ffffff 12 arm
+>>>4 belong&0x00ffffff 0
+>>>4 belong&0x00ffffff 1 subarchitecture=%d
+>>>4 belong&0x00ffffff 2 subarchitecture=%d
+>>>4 belong&0x00ffffff 3 subarchitecture=%d
+>>>4 belong&0x00ffffff 4 subarchitecture=%d
+>>>4 belong&0x00ffffff 5 \bv4t
+>>>4 belong&0x00ffffff 6 \bv6
+>>>4 belong&0x00ffffff 7 \bv5tej
+>>>4 belong&0x00ffffff 8 \bxscale
+>>>4 belong&0x00ffffff 9 \bv7
+>>>4 belong&0x00ffffff 10 \bv7f
+>>>4 belong&0x00ffffff 11 \bv7s
+>>>4 belong&0x00ffffff 12 \bv7k
+>>>4 belong&0x00ffffff 13 \bv8
+>>>4 belong&0x00ffffff 14 \bv6m
+>>>4 belong&0x00ffffff 15 \bv7m
+>>>4 belong&0x00ffffff 16 \bv7em
+>>>4 belong&0x00ffffff >16 subarchitecture=%d
+# 13 m88k
+>>0 belong&0x00ffffff 13
+>>>4 belong&0x00ffffff 0 mc88000
+>>>4 belong&0x00ffffff 1 mc88100
+>>>4 belong&0x00ffffff 2 mc88110
+>>>4 belong&0x00ffffff >2 mc88000 subarchitecture=%d
+>>0 belong&0x00ffffff 14 SPARC
+>>0 belong&0x00ffffff 15 i860g
+>>0 belong&0x00ffffff 16 alpha
+>>0 belong&0x00ffffff 17 rs6000
+>>0 belong&0x00ffffff 18 ppc
+>>>4 belong&0x00ffffff 0
+>>>4 belong&0x00ffffff 1 \b_601
+>>>4 belong&0x00ffffff 2 \b_602
+>>>4 belong&0x00ffffff 3 \b_603
+>>>4 belong&0x00ffffff 4 \b_603e
+>>>4 belong&0x00ffffff 5 \b_603ev
+>>>4 belong&0x00ffffff 6 \b_604
+>>>4 belong&0x00ffffff 7 \b_604e
+>>>4 belong&0x00ffffff 8 \b_620
+>>>4 belong&0x00ffffff 9 \b_750
+>>>4 belong&0x00ffffff 10 \b_7400
+>>>4 belong&0x00ffffff 11 \b_7450
+>>>4 belong&0x00ffffff 100 \b_970
+>>>4 belong&0x00ffffff >100 subarchitecture=%d
+>>0 belong&0x00ffffff >18 architecture=%d
+>0 belong&0xff000000 0x01000000
+#
+# 64-bit ABIs.
+#
+>>0 belong&0x00ffffff 0 64-bit architecture=%d
+>>0 belong&0x00ffffff 1 64-bit architecture=%d
+>>0 belong&0x00ffffff 2 64-bit architecture=%d
+>>0 belong&0x00ffffff 3 64-bit architecture=%d
+>>0 belong&0x00ffffff 4 64-bit architecture=%d
+>>0 belong&0x00ffffff 5 64-bit architecture=%d
+>>0 belong&0x00ffffff 6 64-bit architecture=%d
+>>0 belong&0x00ffffff 7 x86_64
+>>>4 belong&0x00ffffff 0 subarchitecture=%d
+>>>4 belong&0x00ffffff 1 subarchitecture=%d
+>>>4 belong&0x00ffffff 2 subarchitecture=%d
+>>>4 belong&0x00ffffff 3
+>>>4 belong&0x00ffffff 4 \b_arch1
+>>>4 belong&0x00ffffff 8 \b_haswell
+>>>4 belong&0x00ffffff >4 subarchitecture=%d
+>>0 belong&0x00ffffff 8 64-bit architecture=%d
+>>0 belong&0x00ffffff 9 64-bit architecture=%d
+>>0 belong&0x00ffffff 10 64-bit architecture=%d
+>>0 belong&0x00ffffff 11 64-bit architecture=%d
+>>0 belong&0x00ffffff 12 arm64
+>>>4 belong&0x00ffffff 0
+>>>4 belong&0x00ffffff 1 \bv8
+>>>4 belong&0x00ffffff 2 \be
+>>>>7 ubyte&0xff >0 (caps:
+>>>>7 ubyte&0xff <0x80 %#02x
+>>>>7 ubyte&0xc0 0x80 PAC
+>>>>>7 ubyte&0x3f x \b%02d
+>>>>7 ubyte&0xc0 0xc0 PAK
+>>>>>7 ubyte&0x3f x \b%02d
+>>>>7 ubyte&0xff x \b)
+>>>4 belong&0x00ffffff >2 subarchitecture=%d
+>>0 belong&0x00ffffff 13 64-bit architecture=%d
+>>0 belong&0x00ffffff 14 64-bit architecture=%d
+>>0 belong&0x00ffffff 15 64-bit architecture=%d
+>>0 belong&0x00ffffff 16 64-bit architecture=%d
+>>0 belong&0x00ffffff 17 64-bit architecture=%d
+>>0 belong&0x00ffffff 18 ppc64
+>>>4 belong&0x00ffffff 0
+>>>4 belong&0x00ffffff 1 \b_601
+>>>4 belong&0x00ffffff 2 \b_602
+>>>4 belong&0x00ffffff 3 \b_603
+>>>4 belong&0x00ffffff 4 \b_603e
+>>>4 belong&0x00ffffff 5 \b_603ev
+>>>4 belong&0x00ffffff 6 \b_604
+>>>4 belong&0x00ffffff 7 \b_604e
+>>>4 belong&0x00ffffff 8 \b_620
+>>>4 belong&0x00ffffff 9 \b_650
+>>>4 belong&0x00ffffff 10 \b_7400
+>>>4 belong&0x00ffffff 11 \b_7450
+>>>4 belong&0x00ffffff 100 \b_970
+>>>4 belong&0x00ffffff >100 subarchitecture=%d
+>>0 belong&0x00ffffff >18 64-bit architecture=%d
+>0 belong&0xff000000 0x02000000
+#
+# 64_32-bit ABIs.
+#
+>>0 belong&0x00ffffff 0 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 1 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 2 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 3 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 4 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 5 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 6 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 7 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 8 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 9 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 10 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 11 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 12 64_32-bit arm
+>>>4 belong&0x00ffffff 0
+>>>4 belong&0x00ffffff 1 \bv8
+>>>4 belong&0x00ffffff >1 subarchitecture=%d
+>>0 belong&0x00ffffff 13 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 14 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 15 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 16 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 17 64_32-bit architecture=%d
+>>0 belong&0x00ffffff 18 64_32-bit architecture=%d
+>>0 belong&0x00ffffff >18 64_32-bit architecture=%d
+
+0 name mach-o-be
+>0 byte 0xcf 64-bit
+>4 use mach-o-cpu
+>12 belong 1 object
+# GRR: Does not work for Mach-O with 2 architectures; instead display oo
+#!:ext o
+!:ext o/
+>12 belong 2 executable
+# the executables normally have no file extension like perl,
+# but exceptions like perl5.18 perl5.16
+!:ext 16/18/
+>12 belong 3 fixed virtual memory shared library
+>12 belong 4 core
+>12 belong 5 preload executable
+>12 belong 6 dynamically linked shared library
+# GRR: Does not work for Mach-O with 2 architectures; instead display dylibdylib
+#!:ext dylib
+!:ext dylib/
+>12 belong 7 dynamic linker
+>12 belong 8 bundle
+# normally name extension bundle; but exceptions like: AMDil_r700.dylib
+!:ext bundle/dylib/
+>12 belong 9 dynamically linked shared library stub
+>12 belong 10 dSYM companion file
+>12 belong 11 kext bundle
+>12 belong >11
+>>12 belong x filetype=%d
+>24 belong >0 \b, flags:<
+>>24 belong &0x00000001 \bNOUNDEFS
+>>24 belong &0x00000002 \b|INCRLINK
+>>24 belong &0x00000004 \b|DYLDLINK
+>>24 belong &0x00000008 \b|BINDATLOAD
+>>24 belong &0x00000010 \b|PREBOUND
+>>24 belong &0x00000020 \b|SPLIT_SEGS
+>>24 belong &0x00000040 \b|LAZY_INIT
+>>24 belong &0x00000080 \b|TWOLEVEL
+>>24 belong &0x00000100 \b|FORCE_FLAT
+>>24 belong &0x00000200 \b|NOMULTIDEFS
+>>24 belong &0x00000400 \b|NOFIXPREBINDING
+>>24 belong &0x00000800 \b|PREBINDABLE
+>>24 belong &0x00001000 \b|ALLMODSBOUND
+>>24 belong &0x00002000 \b|SUBSECTIONS_VIA_SYMBOLS
+>>24 belong &0x00004000 \b|CANONICAL
+>>24 belong &0x00008000 \b|WEAK_DEFINES
+>>24 belong &0x00010000 \b|BINDS_TO_WEAK
+>>24 belong &0x00020000 \b|ALLOW_STACK_EXECUTION
+>>24 belong &0x00040000 \b|ROOT_SAFE
+>>24 belong &0x00080000 \b|SETUID_SAFE
+>>24 belong &0x00100000 \b|NO_REEXPORTED_DYLIBS
+>>24 belong &0x00200000 \b|PIE
+>>24 belong &0x00400000 \b|DEAD_STRIPPABLE_DYLIB
+>>24 belong &0x00800000 \b|HAS_TLV_DESCRIPTORS
+>>24 belong &0x01000000 \b|NO_HEAP_EXECUTION
+>>24 belong &0x02000000 \b|APP_EXTENSION_SAFE
+>>24 belong &0x04000000 \b|NLIST_OUTOFSYNC_WITH_DYLDINFO
+>>24 belong &0x08000000 \b|SIM_SUPPORT
+>>24 belong &0x80000000 \b|DYLIB_IN_CACHE
+>>24 belong x \b>
+
+#
+0 lelong&0xfffffffe 0xfeedface Mach-O
+!:strength +1
+!:mime application/x-mach-binary
+>0 use \^mach-o-be
+
+0 belong&0xfffffffe 0xfeedface Mach-O
+!:strength +1
+!:mime application/x-mach-binary
+>0 use mach-o-be
diff --git a/contrib/libs/libmagic/magic/Magdir/macintosh b/contrib/libs/libmagic/magic/Magdir/macintosh
new file mode 100644
index 0000000000..a74aac487c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/macintosh
@@ -0,0 +1,505 @@
+
+#------------------------------------------------------------------------------
+# $File: macintosh,v 1.36 2022/12/06 18:45:20 christos Exp $
+# macintosh description
+#
+# BinHex is the Macintosh ASCII-encoded file format (see also "apple")
+# Daniel Quinlan, quinlan@yggdrasil.com
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/BinHex
+# Reference: http://fileformats.archiveteam.org/wiki/BinHex
+# Note: only tested with version 4.0 and hqx extension
+# Any text/binary before the characteristic comment sentence is to be ignored like in
+# http://ftp.vim.org/pub/ftp/ftp/infomac/disk/mac-update-40b7.hqx
+0 search/1602 (This\ file\
+>&0 use binhex
+# http://ftp.vim.org/pub/ftp/ftp/infomac/_Disk_&_File/zap-res-forks-101.hqx
+0 search/2652/b (This\ file\
+>&0 use binhex
+0 name binhex
+# keep split search string format similar like in version 5.37
+>0 string must\ be\ converted\ with\ BinHex\ BinHex binary text, version
+# http://www.macdisk.com/binhexen.php3
+!:apple BNHQTEXT
+# http://www.faqs.org/faqs/macintosh/comm-faq/part1/
+>>&0 string 1.0 1.0
+!:mime application/mac-binhex
+!:ext hex
+>>&0 string 2.0 2.0
+!:mime application/mac-binhex
+!:ext hcx
+# BinHex 3.0 never existed
+>>&0 string 4.0 4.0
+!:mime application/mac-binhex40
+!:ext hqx
+# BinHex 5.0 also MacBinary I
+>>&0 string 5.0 5.0
+!:mime application/mac-binhex40
+!:ext hqx
+# this should never happen
+>>&0 default x
+>>>&0 string x %.3s
+!:mime application/mac-binhex
+!:ext hqx
+
+# Stuffit archives are the de facto standard of compression for Macintosh
+# files obtained from most archives. (franklsm@tuns.ca)
+0 string SIT! StuffIt Archive (data)
+!:mime application/x-stuffit
+!:apple SIT!SIT!
+>2 string x : %s
+0 string SITD StuffIt Deluxe (data)
+>2 string x : %s
+0 string Seg StuffIt Deluxe Segment (data)
+>2 string x : %s
+
+# Newer StuffIt archives (grant@netbsd.org)
+0 string StuffIt StuffIt Archive
+!:mime application/x-stuffit
+!:apple SIT!SIT!
+#>162 string >0 : %s
+
+# Macintosh Applications and Installation binaries (franklsm@tuns.ca)
+# GRR: Too weak
+#0 string APPL Macintosh Application (data)
+#>2 string x \b: %s
+
+# Macintosh System files (franklsm@tuns.ca)
+# GRR: Too weak
+#0 string zsys Macintosh System File (data)
+#0 string FNDR Macintosh Finder (data)
+#0 string libr Macintosh Library (data)
+#>2 string x : %s
+#0 string shlb Macintosh Shared Library (data)
+#>2 string x : %s
+#0 string cdev Macintosh Control Panel (data)
+#>2 string x : %s
+#0 string INIT Macintosh Extension (data)
+#>2 string x : %s
+#0 string FFIL Macintosh Truetype Font (data)
+#>2 string x : %s
+#0 string LWFN Macintosh Postscript Font (data)
+#>2 string x : %s
+
+# Additional Macintosh Files (franklsm@tuns.ca)
+# GRR: Too weak
+#0 string PACT Macintosh Compact Pro Archive (data)
+#>2 string x : %s
+#0 string ttro Macintosh TeachText File (data)
+#>2 string x : %s
+#0 string TEXT Macintosh TeachText File (data)
+#>2 string x : %s
+#0 string PDF Macintosh PDF File (data)
+#>2 string x : %s
+
+# MacBinary format (Eric Fischer, enf@pobox.com)
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/MacBinary
+# http://fileformats.archiveteam.org/wiki/MacBinary
+# Reference: https://files.stairways.com/other/macbinaryii-standard-info.txt
+# Note: verified by macutils `macunpack -i -v BBEdit4.0.sit.bin` and
+# `deark -l -d -m macbinary G3FirmwareUpdate1.1.smi.bin`
+#
+# Unfortunately MacBinary doesn't really have a magic number prior
+# to the MacBinary III format.
+#
+
+# old version number, must be kept at zero for compatibility
+0 byte 0
+# length of filename (must be in the range 1-63)
+>1 ubyte >0
+# skip T.PIC.LZ INSTRUMENT.7T INVENTORY
+>>1 ubyte <64
+# skip Docs.MWII ReadMe.MacWrite "Notes (MacWrite II)"
+# by looking for printable characters at beginning of file name
+>>>2 ubelong >0x1F000000
+# zero fill, must be zero for compatibility
+>>>>74 byte 0
+# zero fill, must be zero for compatibility
+>>>>>82 byte 0
+# skip few DEGAS mid-res uncompressed bitmap (GEMINI03.PI2 CODE_RAM.PI2) with "too high" file names ffffff88 ffff4f00
+>>>>>>2 ubelong <0xffff0000
+# MacBinary I test for valid version numbers
+>>>>>>>122 ubeshort 0
+# additional check for undefined header fields in MacBinary I
+#>>>>>>>>101 ulong 0
+>>>>>>>>0 use mac-bin
+# MacBinary II the newer versions begins at 129
+>>>>>>>122 ubeshort 0x8181
+>>>>>>>>0 use mac-bin
+# MacBinary III with MacBinary II to read
+>>>>>>122 ubeshort 0x8281
+>>>>>>>0 use mac-bin
+
+# display information of MacBinary file
+0 name mac-bin
+>122 ubyte x MacBinary
+# versions for MacBinary II/III
+>122 ubyte 129 II
+>122 ubyte 130 III
+# only in MacBinary III
+>>102 string !mBIN with surprising version
+!:mime application/x-macbinary
+!:apple PSPTBINA
+!:ext bin/macbin
+# THIS SHOULD NEVER HAPPEN! Maybe another file type is misidentified as MacBinary
+#>1 ubyte >63 \b, name length %u too BIG!
+#>122 ubeshort x \b, version %#x
+# Finder flags if not 0
+# >73 byte !0 \b, flags 0x
+# >73 byte =0
+# >>101 byte !0 \b, flags 0x
+# # original Finder flags (Bits 8-15)
+# >73 byte !0 \b%x
+# # finder flags, bits 0-7
+# >101 byte !0 \b%x
+>73 byte &0x01 \b, inited
+>73 byte &0x02 \b, changed
+>73 byte &0x04 \b, busy
+>73 byte &0x08 \b, bozo
+>73 byte &0x10 \b, system
+>73 byte &0x20 \b, bundle
+>73 byte &0x40 \b, invisible
+>73 byte &0x80 \b, locked
+
+# 75 beshort # vertical posn in window
+#>75 beshort !0 \b, v.pos %u
+# 77 beshort # horiz posn in window
+#>77 beshort !0 \b, h.pos %u
+# 79 beshort # window or folder ID
+>79 ubeshort !0 \b, ID %#x
+# protected flag
+>81 byte !0 \b, protected %#x
+# length of comment after resource
+>99 ubeshort !0 \b, comment length %u
+# char. code of file name
+>106 ubyte !0 \b, char. code %#x
+# still more Finder flags
+>107 ubyte !0 \b, more flags %#x
+# length of total files when unpacked only used when pack and unpack on the fly
+>116 ubelong !0 \b, total length %u
+# 120 beshort # length of add'l header
+>120 ubeshort !0 \b, 2nd header length %u
+# 124 beshort # checksum
+#>124 ubeshort !0 \b, CRC %#x
+# creation date in seconds since MacOS epoch start. So 1 Jan 1970 ~ 7C25B080
+# few (31/1247) examples (hinkC4.0.sitx.bin InternetExplorer5.1.smi.bin G3FirmwareUpdate1.1.smi.bin Firewire2.3.3.smi.bin LR2image.bin) contain zeroed date fields
+>91 long !0
+>>91 beldate-0x7C25B080 x \b, %s
+# THIS SHOULD NEVER HAPPEN! Maybe another file type is misidentified or time overflow
+>91 ubelong <0x7c25b080 INVALID date
+# reported date seconds by deark
+#>91 ubelong x deark-DATE=%u
+# last modified date
+>95 long !0
+>>95 beldate-0x7C25B080 x \b, modified %s
+# Apple creator+typ if not null
+# file creator (normally expressed as four characters)
+>69 ulong !0 \b, creator
+# instead 4 character code display full creator name
+>>69 use apple-creator
+# file type (normally expressed as four characters)
+>65 ulong !0 \b, type
+>>65 use apple-type
+# length of data segment
+>83 ubelong !0 \b, %u bytes
+# filename (in the range 1-63)
+# like "BBEdit4.0.sit" "Archive.sitx" "MacPGP 2.2 (.sea)"
+>1 pstring x "%s"
+# print 1 space and then at offset 128 inspect data fork content if it has one
+>83 ubelong !0 \b
+>>128 indirect x
+# Afterwards resource fork if length of resource segment not zero
+>87 ubelong !0
+# calculate resource fork offset
+>>83 ubelong+128 x \b, at %#x
+# length of resource segment
+>>87 ubelong !0 %u bytes
+>>(83.S+128) ubequad x resource
+# further resource fork content inspection
+>>>&-8 indirect x
+
+# Apple Type/Creator Database
+# URL: https://en.wikipedia.org/wiki/Type_code
+# Reference: https://www.lacikam.co.il/tcdb/
+# https://www.macdisk.com/macsigen.php
+# Note: classic Mac OS files have two 4 character codes for type and creator.
+# Thereby the Finder attach documents types to applications.
+
+#>65 string x \b, type "%4.4s"
+
+# display information about apple type
+0 name apple-type
+>0 string 8BIM PhotoShop
+>0 string ALB3 PageMaker 3
+>0 string ALB4 PageMaker 4
+>0 string ALT3 PageMaker 3
+>0 string APPL application
+>0 string AWWP AppleWorks word processor
+>0 string CIRC simulated circuit
+>0 string DRWG MacDraw
+>0 string EPSF Encapsulated PostScript
+>0 string FFIL font suitcase
+>0 string FKEY function key
+>0 string FNDR Macintosh Finder
+>0 string GIFf GIF image
+>0 string Gzip GNU gzip
+>0 string INIT system extension
+>0 string LIB\ library
+>0 string LWFN PostScript font
+>0 string MSBC Microsoft BASIC
+>0 string PACT Compact Pro archive
+>0 string PDF\ Portable Document Format
+>0 string PICT picture
+>0 string PNTG MacPaint picture
+>0 string PREF preferences
+>0 string PROJ Think C project
+>0 string QPRJ Think Pascal project
+>0 string SCFL Defender scores
+>0 string SCRN startup screen
+>0 string SITD StuffIt Deluxe
+>0 string SPn3 SuperPaint
+>0 string STAK HyperCard stack
+>0 string Seg\ StuffIt segment
+>0 string TARF Unix tar archive
+>0 string TEXT ASCII
+>0 string TIFF TIFF image
+>0 string TOVF Eudora table of contents
+>0 string WDBN Microsoft Word word processor
+>0 string WORD MacWrite word processor
+>0 string XLS\ Microsoft Excel
+>0 string ZIVM compress (.Z)
+>0 string ZSYS Pre-System 7 system file
+>0 string acf3 Aldus FreeHand
+>0 string cdev control panel
+>0 string dfil Desk Accessory suitcase
+>0 string libr library
+>0 string nX^d WriteNow word processor
+>0 string nX^w WriteNow dictionary
+>0 string rsrc resource
+>0 string scbk Scrapbook
+>0 string shlb shared library
+>0 string ttro SimpleText read-only
+>0 string zsys system file
+
+# additional types added in Dec 2017
+>0 string BINA binary file
+>0 string BMPp BMP image
+>0 string JPEG JPEG image
+#>0 string W4BN Microsoft Word x.y word processor?
+# if type name is not known display 4 character identifier
+>0 default x
+>>0 string x '%4.4s'
+
+#>69 string x \b, creator "%4.4s"
+
+# Now Apple has no repository of registered Creator IDs any more. These are
+# just the ones that I happened to have files from and was able to identify.
+
+# display information about apple creator
+0 name apple-creator
+>0 string 8BIM Adobe Photoshop
+>0 string ALD3 PageMaker 3
+>0 string ALD4 PageMaker 4
+>0 string ALFA Alpha editor
+>0 string APLS Apple Scanner
+>0 string APSC Apple Scanner
+>0 string BRKL Brickles
+>0 string BTFT BitFont
+>0 string CCL2 Common Lisp 2
+>0 string CCL\ Common Lisp
+>0 string CDmo The Talking Moose
+>0 string CPCT Compact Pro
+>0 string CSOm Eudora
+>0 string DMOV Font/DA Mover
+>0 string DSIM DigSim
+>0 string EDIT Macintosh Edit
+>0 string ERIK Macintosh Finder
+>0 string EXTR self-extracting archive
+>0 string Gzip GNU gzip
+>0 string KAHL Think C
+>0 string LWFU LaserWriter Utility
+>0 string LZIV compress
+>0 string MACA MacWrite
+>0 string MACS Macintosh operating system
+>0 string MAcK MacKnowledge terminal emulator
+>0 string MLND Defender
+>0 string MPNT MacPaint
+>0 string MSBB Microsoft BASIC (binary)
+>0 string MSWD Microsoft Word
+>0 string NCSA NCSA Telnet
+>0 string PJMM Think Pascal
+>0 string PSAL Hunt the Wumpus
+#>0 string PSI2 Apple File Exchange
+>0 string R*ch BBEdit
+>0 string RMKR Resource Maker
+>0 string RSED Resource Editor
+>0 string Rich BBEdit
+>0 string SIT! StuffIt
+>0 string SPNT SuperPaint
+>0 string Unix NeXT Mac filesystem
+>0 string VIM! Vim editor
+>0 string WILD HyperCard
+>0 string XCEL Microsoft Excel
+>0 string aCa2 Fontographer
+>0 string aca3 Aldus FreeHand
+>0 string dosa Macintosh MS-DOS file system
+>0 string movr Font/DA Mover
+>0 string nX^n WriteNow
+>0 string pdos Apple ProDOS file system
+>0 string scbk Scrapbook
+>0 string ttxt SimpleText
+>0 string ufox Foreign File Access
+# additional creators added in Dec 2017
+# Claris/Apple Works
+>0 string BOBO Apple Works
+# CU-SeeMe_0.87b3_(68K).bin
+#>0 string CUce bar
+>0 string PSPT Apple File Exchange
+# Disk_Copy_4.2.sea.bin
+#>0 string NCse foo
+# probably StuffIt/Aladdin by Smith Micro Software, Inc.
+>0 string STi0 stuffit
+# MacGzip-1.1.3.sea.bin
+#>0 string aust bar
+# D-Disk_Copy_6.3.3.smi.bin
+>0 string oneb Disk Copy Self Mounting
+# if creator name is not known display 4 character identifier
+>0 default x
+>>0 string x '%4.4s'
+
+# sas magic from Bruce Foster (bef@nwu.edu)
+#
+#0 string SAS SAS
+#>8 string x %s
+0 string SAS SAS
+>24 string DATA data file
+>24 string CATALOG catalog
+>24 string INDEX data file index
+>24 string VIEW data view
+# sas 7+ magic from Reinhold Koch (reinhold.koch@roche.com)
+#
+0x54 string SAS SAS 7+
+>0x9C string DATA data file
+>0x9C string CATALOG catalog
+>0x9C string INDEX data file index
+>0x9C string VIEW data view
+
+# spss magic for SPSS system and portable files,
+# from Bruce Foster (bef@nwu.edu).
+
+0 long 0xc1e2c3c9 SPSS Portable File
+>40 string x %s
+
+0 string $FL2 SPSS System File
+>24 string x %s
+
+0 string $FL3 SPSS System File
+>24 string x %s
+
+# Macintosh filesystem data
+# From "Tom N Harris" <telliamed@mac.com>
+# Fixed HFS+ and Partition map magic: Ethan Benson <erbenson@alaska.net>
+# The MacOS epoch begins on 1 Jan 1904 instead of 1 Jan 1970, so these
+# entries depend on the data arithmetic added after v.35
+# There's also some Pascal strings in here, ditto...
+
+# The boot block signature, according to IM:Files, is
+# "for HFS volumes, this field always contains the value 0x4C4B."
+# But if this is true for MFS or HFS+ volumes, I don't know.
+# Alternatively, the boot block is supposed to be zeroed if it's
+# unused, so a simply >0 should suffice.
+
+0x400 beshort 0xD2D7 Macintosh MFS data
+>0 beshort 0x4C4B (bootable)
+>0x40a beshort &0x8000 (locked)
+>0x402 beldate-0x7C25B080 x created: %s,
+>0x406 beldate-0x7C25B080 >0 last backup: %s,
+>0x414 belong x block size: %d,
+>0x412 beshort x number of blocks: %d,
+>0x424 pstring x volume name: %s
+
+# *.hfs updated by Joerg Jenderek
+# https://en.wikipedia.org/wiki/Hierarchical_File_System
+# "BD" gives many false positives
+0x400 beshort 0x4244
+# ftp://ftp.mars.org/pub/hfs/hfsutils-3.2.6.tar.gz/hfsutils-3.2.6/libhfs/apple.h
+# first block of volume bit map (always 3)
+>0x40e ubeshort 0x0003
+# maximal length of volume name is 27
+>>0x424 ubyte <28 Macintosh HFS data
+!:mime application/x-apple-diskimage
+#!:apple hfsdINIT
+#!:apple MACSdisk
+# https://www.macdisk.com/macsigen.php
+#!:apple ddskdevi
+!:apple ????devi
+# https://en.wikipedia.org/wiki/Apple_Disk_Image
+!:ext hfs/dmg
+>>>0 beshort 0x4C4B (bootable)
+#>>>0 beshort 0x0000 (not bootable)
+>>>0x40a beshort &0x8000 (locked)
+>>>0x40a beshort ^0x0100 (mounted)
+>>>0x40a beshort &0x0200 (spared blocks)
+>>>0x40a beshort &0x0800 (unclean)
+>>>0x47C beshort 0x482B (Embedded HFS+ Volume)
+# https://www.epochconverter.com/
+# 0x7C245F00 seconds ~ 2082758400 ~ 01 Jan 2036 00:00:00 ~ 66 years to 1970
+# 0x7C25B080 seconds ~ 2082844800 ~ 02 Jan 2036 00:00:00
+# construct not working
+#>>>0x402 beldate-0x7C25B080 x created: %s,
+#>>>0x406 beldate-0x7C25B080 x last modified: %s,
+#>>>0x440 beldate-0x7C25B080 >0 last backup: %s,
+# found block sizes 200h,1200h,2800h
+>>>0x414 belong x block size: %d,
+>>>0x412 beshort x number of blocks: %d,
+>>>0x424 pstring x volume name: %s
+
+0 name hfsplus
+>&0 beshort x version %d data
+>0 beshort 0x4C4B (bootable)
+>0x404 belong ^0x00000100 (mounted)
+>&2 belong &0x00000200 (spared blocks)
+>&2 belong &0x00000800 (unclean)
+>&2 belong &0x00008000 (locked)
+>&6 string x last mounted by: '%.4s',
+# really, that should be treated as a belong and we print a string
+# based on the value. TN1150 only mentions '8.10' for "MacOS 8.1"
+>&14 beldate-0x7C25B080 x created: %s,
+# only the creation date is local time, all other timestamps in HFS+ are UTC.
+>&18 bedate-0x7C25B080 x last modified: %s,
+>&22 bedate-0x7C25B080 >0 last backup: %s,
+>&26 bedate-0x7C25B080 >0 last checked: %s,
+>&38 belong x block size: %d,
+>&42 belong x number of blocks: %d,
+>&46 belong x free blocks: %d
+
+0x400 beshort 0x482B Apple HFS Plus
+>&0 use hfsplus
+0x400 beshort 0x4858 Apple HFS Plus Extended
+>&0 use hfsplus
+
+## AFAIK, only the signature is different
+# same as Apple Partition Map
+# GRR: This magic is too weak, it is just "TS"
+#0x200 beshort 0x5453 Apple Old Partition data
+#>0x2 beshort x block size: %d,
+#>0x230 string x first type: %s,
+#>0x210 string x name: %s,
+#>0x254 belong x number of blocks: %d,
+#>0x400 beshort 0x504D
+#>>0x430 string x second type: %s,
+#>>0x410 string x name: %s,
+#>>0x454 belong x number of blocks: %d,
+#>>0x800 beshort 0x504D
+#>>>0x830 string x third type: %s,
+#>>>0x810 string x name: %s,
+#>>>0x854 belong x number of blocks: %d,
+#>>>0xa00 beshort 0x504D
+#>>>>0xa30 string x fourth type: %s,
+#>>>>0xa10 string x name: %s,
+#>>>>0xa54 belong x number of blocks: %d
+
+# From: Remi Mommsen <mommsen@slac.stanford.edu>
+0 string BOMStore Mac OS X bill of materials (BOM) file
+
diff --git a/contrib/libs/libmagic/magic/Magdir/macos b/contrib/libs/libmagic/magic/Magdir/macos
new file mode 100644
index 0000000000..0bacc13a48
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/macos
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# $File: macos,v 1.1 2012/12/21 16:41:07 christos Exp $
+# MacOS files
+#
+
+0 string book\0\0\0\0mark\0\0\0\0 MacOS Alias file
diff --git a/contrib/libs/libmagic/magic/Magdir/magic b/contrib/libs/libmagic/magic/Magdir/magic
new file mode 100644
index 0000000000..c8aa054b72
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/magic
@@ -0,0 +1,71 @@
+
+#------------------------------------------------------------------------------
+# $File: magic,v 1.11 2023/06/27 13:42:49 christos Exp $
+# magic: file(1) magic for magic files
+#
+# Update: Joerg Jenderek
+# skip Magicsee_R1.cfg found on retropie starting with # Magicsee R1 one-handed controller
+0 string/t #\ Magic\ magic text file for file(1) cmd
+#!:mime text/plain
+!:mime text/x-file
+# no suffix in ../Header
+!:ext /
+#
+# some samples start with a comment line
+0 ubyte =0x23
+# many samples start with separator line
+>4 string --------
+>>0 use magic-fragment
+# few samples with 1st comment line and without seperator comment line
+>4 default x
+# few sample with 1st comment line and without seperator comment line and regular expression like: sisu
+>>1 search/112 regex\x09
+>>>0 use magic-fragment
+>>1 default x
+# few samples with 1st comment line and without seperator comment line and string value like:
+# blcr bsi selinux ssh (file 3.34) digital gnu wordperfect
+>>>1 search/471 string\x09
+>>>>0 use magic-fragment
+>>>1 default x
+# few samples with 1st comment line and without seperator comment line and short value like:
+# (file 3.34) os9 osf1
+>>>>1 search/1716 short\x09
+>>>>>0 use magic-fragment
+# but many samples start with an empty first line
+0 ubyte =0x0A
+# many samples sttart with separator comment line
+>4 string --------
+>>0 use magic-fragment
+# few samples with 1st empty line and without seperator comment line like: biosig espressif
+>4 default x
+>>1 search/581 \041:mime
+>>>0 use magic-fragment
+# display information (lines) about magic text fragment
+0 name magic-fragment
+>0 string x magic text fragment for file(1) cmd
+!:mime text/x-file
+# most without suffix but mail.news varied.out varied.script
+!:ext /news/out/script
+# next lines are mainly for control reasons
+# some (34/339) samples start comment line
+>0 ubyte !0x0A
+>>0 string x \b, 1st line "%s"
+>>>&1 string x \b, 2nd line "%s"
+# but most (305/339) samples start with an empty first line
+>0 ubyte =0x0A
+>>1 string x \b, 2nd line "%s"
+>>>&1 string x \b, 3rd line "%s"
+#
+# URL: http://en.wikipedia.org/wiki/File_(command)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/m/mgc.trid.xml
+# Note: called "magic compiled data (LE)" by TrID
+0 lelong 0xF11E041C magic binary file for file(1) cmd
+#!:mime application/octet-stream
+!:mime application/x-file
+!:ext mgc
+>4 lelong x (version %d) (little endian)
+0 belong 0xF11E041C magic binary file for file(1) cmd
+#!:mime application/octet-stream
+!:mime application/x-file
+!:ext mgc
+>4 belong x (version %d) (big endian)
diff --git a/contrib/libs/libmagic/magic/Magdir/mail.news b/contrib/libs/libmagic/magic/Magdir/mail.news
new file mode 100644
index 0000000000..3ca3b405f6
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mail.news
@@ -0,0 +1,132 @@
+#------------------------------------------------------------------------------
+# $File: mail.news,v 1.30 2022/10/31 13:22:26 christos Exp $
+# mail.news: file(1) magic for mail and news
+#
+# Unfortunately, saved netnews also has From line added in some news software.
+#0 string From mail text
+0 string/t Relay-Version: old news text
+!:mime message/rfc822
+0 string/t #!\ rnews batched news text
+!:mime message/rfc822
+0 string/t N#!\ rnews mailed, batched news text
+!:mime message/rfc822
+0 string/t Forward\ to mail forwarding text
+!:mime message/rfc822
+0 string/t Pipe\ to mail piping text
+!:mime message/rfc822
+0 string/tc delivered-to: SMTP mail text
+!:mime message/rfc822
+0 string/tc return-path: SMTP mail text
+!:mime message/rfc822
+0 string/t Path: news text
+!:mime message/news
+0 string/t Xref: news text
+!:mime message/news
+0 string/t From: news or mail text
+!:mime message/rfc822
+0 string/t Date: news or mail text
+!:mime message/rfc822
+0 string/t Article saved news text
+!:mime message/news
+# Reference: http://quimby.gnus.org/notes/BABYL
+# Update: Joerg Jenderek
+# Note: used by Rmail in Emacs version 22 and before
+# is not text because of characters like Control-L Control-_
+0 string/b BABYL\ OPTIONS: Emacs RMAIL
+#0 string/t BABYL Emacs RMAIL text
+# https://reposcope.com/mimetype/message/x-gnu-rmail
+!:mime message/x-gnu-rmail
+# ~/RMAIL
+!:ext /
+0 string/t Received: RFC 822 mail text
+!:mime message/rfc822
+0 string/t MIME-Version: MIME entity text
+#0 string/t Content- MIME entity text
+
+# TNEF files...
+# URL: http://fileformats.archiveteam.org/wiki/Transport_Neutral_Encapsulation_Format
+# https://en.wikipedia.org/wiki/Transport_Neutral_Encapsulation_Format
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/t/tnef.trid.xml
+# https://interoperability.blob.core.windows.net/files/MS-OXTNEF/%5bMS-OXTNEF%5d-210817.pdf
+# Update: Joerg Jenderek
+# Note: moved and merged from ./msdos (version 1.154) there just called "TNEF"
+# partly verified by `tnef --list -v -f voice.tnef` and `ytnef -v triples.tnef`
+# TNEF magic From "Joomy" <joomy@se-ed.net>
+# TNEF_SIGNATURE
+0 lelong 0x223E9F78 Transport Neutral Encapsulation Format (TNEF)
+!:mime application/vnd.ms-tnef
+# winmail.dat or win.dat by Microsoft Outlook
+!:ext tnef/dat
+# https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxtnef/7fdb64ee-7f63-4d95-9af1-c672e7475c3a
+# LegacyKey
+#>4 uleshort x \b, key %#4.4x
+# attrLevelMessage; Level where attribute applies like: 1~attrLevelMessage 2~attrLevelAttachment
+>6 ubyte !1 \b, 1st level %#2.2x
+# other ID (like 02900000h) or TnefVersion ID (idTnefVersion=06900800h)
+>7 ubelong !0x06900800 \b, 1st id %#8.8x
+>7 ubelong =0x06900800
+# TnefVersion length like: 4
+>>11 ulelong !4 \b, TnefVersion length %x
+# TNEFVersionData; TnefVersion data like: 00010000h
+>>15 ulelong !0x00010000h \b, version %#8.8x
+# Checksum like: 1
+>>19 uleshort !1 \b, checksum %#4.4x
+# attrLevelMessage; level of attOemCodepage like: 1
+>>21 ubyte !1 \b, level %#2.2x
+# idOEMCodePage; OEMCodePage ID like: 07900600h
+>>22 ubelong =0x07900600 \b, OEM codepage
+# OEMCodePage length like: 8
+>>>26 ulelong =8
+# OEMCodePageData; PrimaryCodePage like: 1251 1252
+>>>>30 ulelong x %u
+# OEMCodePageData; SecondaryCodePage; unused and SHOULD contain zero
+>>>>34 ulelong !0 and %u
+# OEMCodePageData Checksum like: E7h E8h
+>>>>38 uleshort x (checksum %#x)
+# attrLevelMessage of attMessageClass like: 1
+>>40 ubyte !1 \b, level %u
+# idMessageClass; ID of attMessageClass like: 08800700h
+>>41 ubelong =0x08800700 \b, MessageAttribute
+# attMessageClass length like: 16 24 25
+#>>>45 ulelong x (length %u)
+# attMessageClass data like: "IPM.Microsoft Mail.Note" "IPM.Note.Portada Newseum"
+# "IPM.Appointment" "IPM.Note.Microsoft.Voicemail.UM.CA"
+>>>45 pstring/l x "%s"
+
+# From: Kevin Sullivan <ksulliva@psc.edu>
+0 string *mbx* MBX mail folder
+
+# From: Simon Matter <simon.matter@invoca.ch>
+0 string \241\002\213\015skiplist\ file\0\0\0 Cyrus skiplist DB
+0 string \241\002\213\015twoskip\ file\0\0\0\0 Cyrus twoskip DB
+
+# JAM(mbp) Fidonet message area databases
+# JHR file
+0 string JAM\0 JAM message area header file
+>12 leshort >0 (%d messages)
+
+# Squish Fidonet message area databases
+# SQD file (requires at least one message in the area)
+# XXX: Weak magic
+#256 leshort 0xAFAE4453 Squish message area data file
+#>4 leshort >0 (%d messages)
+
+#0 string \<!--\ MHonArc text/html; x-type=mhonarc
+
+# Cyrus: file(1) magic for compiled Cyrus sieve scripts
+# URL: https://www.cyrusimap.org/docs/cyrus-imapd/2.4.6/internal/bytecode.php
+# URL: http://git.cyrusimap.org/cyrus-imapd/tree/sieve/bytecode.h?h=master
+# From: Philipp Hahn <hahn@univention.de>
+
+# Compiled Cyrus sieve script
+0 string CyrSBytecode Cyrus sieve bytecode data,
+>12 belong =1 version 1, big-endian
+>12 lelong =1 version 1, little-endian
+>12 belong x version %d, network-endian
+
+# Dovecot mail server, version 2.2 and later.
+# Dovecot mailing list: dovecot@dovecot.org
+# File format spec: https://wiki.dovecot.org/Design/Dcrypt/#File_format
+# From: Stephen Gildea
+0 string CRYPTED\003\007 Dovecot encrypted message
+>9 byte x \b, dcrypt version %d
diff --git a/contrib/libs/libmagic/magic/Magdir/make b/contrib/libs/libmagic/magic/Magdir/make
new file mode 100644
index 0000000000..1abdf7a3ee
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/make
@@ -0,0 +1,21 @@
+#------------------------------------------------------------------------------
+# $File: make,v 1.5 2022/03/12 15:09:47 christos Exp $
+# make: file(1) magic for makefiles
+#
+# URL: https://en.wikipedia.org/wiki/Make_(software)
+0 regex/100l \^(CFLAGS|VPATH|LDFLAGS|all:|\\.PRECIOUS) makefile script text
+!:mime text/x-makefile
+!:strength -15
+# Update: Joerg Jenderek
+# Reference: https://www.freebsd.org/cgi/man.cgi?make(1)
+# exclude grub-core\lib\libgcrypt\mpi\Makefile.am with "#BEGIN_ASM_LIST"
+# by additional escaping point character
+# exclude MS Windows help file CoNtenT with ":include FOOBAR.CNT"
+# and NSIS script with "!include" by additional escaping point character
+0 regex/100l \^\\.(BEGIN|endif|include) BSD makefile script text
+!:mime text/x-makefile
+!:ext /mk
+!:strength -10
+0 regex/100l \^SUBDIRS[[:space:]]+= automake makefile script text
+!:mime text/x-makefile
+!:strength -15
diff --git a/contrib/libs/libmagic/magic/Magdir/map b/contrib/libs/libmagic/magic/Magdir/map
new file mode 100644
index 0000000000..2d56df0156
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/map
@@ -0,0 +1,413 @@
+
+
+#------------------------------------------------------------------------------
+# $File: map,v 1.10 2023/02/03 20:41:57 christos Exp $
+# map: file(1) magic for Map data
+#
+
+# Garmin .FIT files https://pub.ks-and-ks.ne.jp/cycling/edge500_fit.shtml
+8 string .FIT FIT Map data
+>15 byte 0
+>>35 belong x \b, unit id %d
+>>39 lelong x \b, serial %u
+# https://pub.ks-and-ks.ne.jp/cycling/edge500_fit.shtml
+# 20 years after unix epoch
+# TZ=GMT date -d '1989-12-31 0:00' +%s
+>>43 leldate+631065600 x \b, %s
+
+>>47 leshort x \b, manufacturer %d
+>>47 leshort 1 \b (garmin)
+>>49 leshort x \b, product %d
+>>53 byte x \b, type %d
+>>53 byte 1 \b (Device)
+>>53 byte 2 \b (Settings)
+>>53 byte 3 \b (Sports/Cycling)
+>>53 byte 4 \b (Activity)
+>>53 byte 8 \b (Elevations)
+>>53 byte 10 \b (Totals)
+
+# Summary: Garmin map
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Garmin_.img
+# Reference: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/IMG_File_Format
+# sourceforge.net/projects/garmin-img/files/IMG%20File%20Format/1.0/imgformat-1.0.pdf
+# GRR: similar to MBR boot sector handled by ./filesystems
+0x1FE leshort =0xAA55
+# look for valid map signature
+>0x13 string =IMG\0
+>>0 use garmin-map
+0 name garmin-map
+>0 ubyte x Garmin
+!:mime application/x-garmin-map
+# If non-zero, every byte of the entire .img file is to be XORed with this value
+>0 ubyte !0 \b, %#x XORed
+# goto block before FAT
+>(0x40.b*512) ubyte x
+# 1st fat name "DLLINFO TXT" only found for vpm
+>>&512 string =DLLINFO\ TXT map (Voice Processing)
+# there exist 2 other Garmin VPM formats; see ./audio
+!:ext vpm
+# Deutsch__Yannick_D4481-00_0210.vpm
+#>>>512 search/0x0116da60/s RIFF \b; with
+# determine type voice type by ./riff
+#>>>>&0 indirect x \b
+>>&512 string !DLLINFO\ TXT map
+!:ext img
+# 9 zeros
+>1 ubelong !0 \b, zeroes %#x
+# Map's version major
+>8 ubyte x v%u
+# Map's version minor
+>9 ubyte x \b.%.2u
+# Map description[20], 0x20 padded
+>0x49 string x %.20s
+# Map name, continued (0x20 padded, \0 terminated)
+>0x65 string >\ \b%.31s
+# Update year (+1900 for val >= 0x63, +2000 for val <= 0x62)
+>0xB ubyte x \b, updated
+>>0xB ubyte >0x62
+>>>0xB ubyte-100 x 20%.2u
+>>0xB ubyte <0x63
+>>>0xB ubyte x 20%.2u
+# Update month (0-11)
+>0xA ubyte x \b-%.2u
+# All zeroes
+>0xc uleshort !0 \b, zeroes %#x
+# Mapsource flag, 1 - file created by Mapsource, 0 - Garmin map visible in Basecamp and Homeport
+#>0xE ubyte !0 \b, Mapsource flag %#x
+>0xE ubyte 1 \b, Mapsource
+# Checksum, sum of all bytes modulo 256 should be 0
+#>0xF ubyte x \b, Checksum %#x
+# Signature: DSKIMG 0x00 or DSDIMG 0x00 for demo map
+>0x10 string !DSKIMG \b, signature "%.7s"
+>0x39 use garmin-date
+# Map file identifier like GARMIN\0
+>0x41 string !GARMIN \b, id "%.7s"
+# Block size exponent, E1; appears to always be 0x09; minimum block size 512 bytes
+>0x61 ubyte !0x09 \b, E1=%u
+# Block size exponent, E2 ; file blocksize=2**(E1+E2)
+>>0x62 ubyte x \b, E2=%u
+>0x61 ubyte =0x09 \b, blocksize
+>>0x62 ubyte 0 512
+>>0x62 ubyte 1 1024
+>>0x62 ubyte 2 2048
+>>0x62 ubyte 3 4096
+>>0x62 ubyte 4 8192
+>>0x62 ubyte 5 16384
+>>0x62 default x
+>>>0x62 ubyte x E2=%u
+# MBR signature
+>0x1FE leshort !0xAA55 \b, invalid MBR
+# 512 zeros
+>0x200 uquad !0 \b, zeroes %#llx
+# First sub-file offset (absolute); sometimes NO/UNKNOWN sub file!
+>0x40C ulelong >0 \b, at %#x
+# sub-file Header length
+#>>(0x40C.l) uleshort x \b, header len %#x
+>>(0x40C.l) uleshort x %u bytes
+# sub-file Type[10] like "GARMIN RGN" "GARMIN TRE", "GARMIN TYP", etc.
+>>(0x40C.l+2) ubyte >0x1F
+>>>(0x40C.l+2) ubyte <0xFF
+>>>>(0x40C.l+2) string x "%.10s"
+# 0x00 for most maps, 0x80 for locked maps (City Nav, City Select, etc.)
+>>>>(0x40C.l+13) ubyte >0 \b, locked %#x
+# Block sequence numbers like 0000 0100 0200 ... FFFF
+# >0x420 ubequad >0 \b, seq. %#16.16llx
+# >>0x428 ubequad >0 \b%16.16llx
+# >>>0x430 ubequad >0 \b%16.16llx
+# >>>>0x438 ubequad >0 \b%16.16llx
+# >>>>>0x440 ubequad >0 \b%16.16llx
+# >>>>>>0x448 ubequad >0 \b%16.16llx
+# >>>>>>>0x450 ubequad >0 \b%16.16llx
+# >>>>>>>>0x458 ubequad >0 \b%16.16llx
+# >>>>>>>>>0x460 ubequad >0 \b%16.16llx
+# >>>>>>>>>>0x468 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>0x470 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>0x478 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>0x480 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>0x488 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>>0x490 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>>>0x498 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>>>>0x4A0 ubequad >0 \b%16.16llx
+# >>>>>>>>>>>>>>>>>>0x4A8 ubequad >0 \b%16.16llx
+# look for end of FAT
+#>>0x420 search/512/s \xff\xff FAT END
+# Physical block number of FAT header
+#>0x40 ubyte x \b, FAT at phy. block %u
+>0x40 ubyte x
+>>(0x40.b*512) ubyte x
+# 1st FAT block
+>>>&511 use garmin-fat
+# 2nd FAT block
+>>>&1023 use garmin-fat
+# 3th FAT block
+>>>&1535 use garmin-fat
+# 4th FAT block
+>>>&2047 use garmin-fat
+# ... xth FAT block
+#
+# 314 zeros but not in vpm and also gmaptz.img
+>0x84 uquad !0 \b, at 0x84 %#llx
+# display FileAllocationTable block entry in garmin map
+0 name garmin-fat
+>0 ubyte x \b;
+# sub file part; 0x0003 seems to be garbage
+>0x10 uleshort !0 next %#4.4x
+>0x10 uleshort =0
+# fat flag 0~dummy block 1~true sub file
+>>0 ubyte !1 flag %u
+>>0 ubyte =1
+# sub-file name like MAKEGMAP 12345678
+>>>0x1 string x %.8s
+# sub-file typ like RGN TRE MDR LBL
+>>>0x9 string x \b.%.3s
+# size of sub file
+>>>0xC ulelong x \b, %u bytes
+# 32-bit block sequence numbers
+#>>>0x20 ubequad x \b, seq. %#16.16llx
+
+# display date stored inside Garmin maps like yyyy-mm-dd h:mm:ss
+0 name garmin-date
+# year like 2018
+>0 uleshort x \b, created %u
+# month (0-11)
+>2 ubyte x \b-%.2u
+# day (1-31)
+>3 ubyte x \b-%.2u
+# hour (0-23)
+>4 ubyte x %u
+# minute (0-59)
+>5 ubyte x \b:%.2u
+# second (0-59)
+>6 ubyte x \b:%.2u
+
+# Summary: Garmin Map subfiles
+# From: Joerg Jenderek
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/IMG_File_Format
+# Garmin Common Header
+2 string GARMIN\
+# skip ASCII text by checking for low header length
+>0 uleshort <0x1000 Garmin map,
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/GMP_Subfile_Format
+>>9 string GMP subtile
+!:mime application/x-garmin-gpm
+!:ext gmp
+# copyright message
+>>>(0.s) string x %s
+>>>0x0E use garmin-date
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/MDR_Subfile_Format
+# This contains the searchable address table used for finding routing destinations
+>>9 string MDR address table
+!:mime application/x-garmin-mdr
+!:ext mdr
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/NOD_Subfile_Format
+# http://svn.parabola.me.uk/display/trunk/doc/nod.txt
+# This contains the routing information
+>>9 string NOD routing
+!:mime application/x-garmin-nod
+!:ext nod
+>>>0x0E use garmin-date
+#>>>0x15 ulelong x \b, at %#x
+#>>>0x19 ulelong x %#x bytes NOD1
+#>>>0x25 ulelong x \b, at %#x
+#>>>0x29 ulelong x %#x bytes NOD2
+#>>>0x31 ulelong x \b, at %#x
+#>>>0x35 ulelong x %#x bytes NOD3
+# URL: http://www.pinns.co.uk/osm/net.html
+# routable highways (length, direction, allowed speed,house address information)
+>>9 string NET highways
+!:mime application/x-garmin-net
+!:ext net
+#>>>0x15 ulelong x \b, at %#x
+#>>>0x19 ulelong x %#x bytes NET1
+#>>>0x22 ulelong >0
+#>>>>0x1E ulelong x \b, at %#x
+#>>>>0x22 ulelong x %#x bytes NET2
+#>>>0x2B ulelong >0
+#>>>>0x27 ulelong x \b, at %#x
+#>>>>0x2B ulelong x %#x bytes NET3
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/LBL_Subfile_Format
+>>9 string LBL labels
+!:mime application/x-garmin-lbl
+!:ext lbl
+>>>(0.s) string x %s
+# Label coding type 6h 9h and ah
+>>>0x1E ubyte x \b, coding type %#x
+#>>>0x15 ulelong x \b, at %#x
+#>>>0x19 ulelong x %#x bytes LBL1
+#>>>0x1F ulelong x \b, at %#x
+#>>>0x23 ulelong x %#x bytes LBL2
+#>>>0x2D ulelong x \b, at %#x
+#>>>0x31 ulelong x %#x bytes LBL3
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/SRT_Subfile_Format
+# A lookup table of the chars in the map's codepage, and their collating sequence
+>>9 string SRT sort table
+!:mime application/x-garmin-srt
+!:ext srt
+>>>0x0E use garmin-date
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/TRE_Subfile_Format
+>>9 string TRE tree
+!:mime application/x-garmin-tre
+!:ext tre
+# title like City Nav Europe NTU 2019.2 Basemap
+# or OSM Street map
+>>>(0.s) string x %s
+# 2nd title like Copyright 1995-2018 by GARMIN Corporation.
+# or http://www.openstreetmap.org/
+>>>>&1 string x %s
+>>>0x0E use garmin-date
+#>>>0x21 ulelong x \b, at %#x
+#>>>0x25 ulelong x %#x bytes TRE1
+#>>>0x29 ulelong x \b, at %#x
+#>>>0x2D ulelong x %#x bytes TRE2
+#>>>0x31 ulelong x \b, at %#x
+#>>>0x35 ulelong x %#x bytes TRE3
+# Copyright record size
+#>>>0x39 uleshort x \b, copyright record size %u
+# Map ID
+>>>0x74 ulelong x \b, ID %#x
+# URL: https://www.gpspower.net/garmin-tutorials/353310-basecamp-installing-free-desktop-map.html
+# For road traffic information service (RDS/TMS/TMC). Commonly seen in City Navigator maps
+>>9 string TRF traffic,
+!:mime application/x-garmin-trf
+!:ext trf
+# city/region like Preitenegg
+>>>(0.s+1) string x 1st %s
+# highway part like L606/L148
+>>>>&1 string x %s
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/Format
+# Reference: http://www.pinns.co.uk/osm/typformat.html
+# customize the appearance of objects. For GPS and MapSource/Qlandkarte better looking maps
+>>9 string TYP types
+!:mime application/x-garmin-typ
+!:ext typ
+>>>0x0E use garmin-date
+# character set 1252 65001~UTF8
+>>>0x15 uleshort x \b, code page %u
+# POIs
+#>>>0x17 ulelong x \b, at %#x
+#>>>0x1B ulelong x %#x bytes TYP1
+# extra pois
+#>>>0x5B ulelong x \b, at %#x
+#>>>0x5F ulelong x %#x bytes TYP8
+# URL: https://wiki.openstreetmap.org/wiki/OSM_Map_On_Garmin/RGN_Subfile_Format
+# http://www.pinns.co.uk/osm/RGN.html
+# region data used by the Garmin software
+>>9 string RGN region
+!:mime application/x-garmin-rgn
+!:ext rgn
+# POIs,Indexed POIs,Polylines or Polygons or first map level
+#>>>0x15 ulelong x \b, at %#x
+#>>>0x19 ulelong x %#x bytes RGN1
+# polygons with extended types
+#>>>0x21 ulelong >0
+#>>>>0x1D ulelong x \b, at %#x
+#>>>>0x21 ulelong x %#x bytes RGN2
+# polylines with extended types
+#>>>0x3D ulelong >0
+#>>>>0x39 ulelong x \b, at %#x
+#>>>>0x3D ulelong x %#x bytes RGN3
+# extended POIs
+#>>>0x59 ulelong >0
+#>>>>0x55 ulelong x \b, at %#x
+#>>>>0x59 ulelong x %#x bytes RGN3
+#>>9 default x unknown map type
+# Header length; GMP:31h 35h 3Dh,MDR:11Eh 238h 2C4h 310h,NOD:3Fh 7Fh,NET:64h,
+# LBL:2A9h,SRT:1Dh 25h 27h,TRE:CFh 135h,TRF:5Ah,TYP:5Bh 6Eh 7Ch AEh,RGN:7Dh
+>>0 uleshort x \b, header length %#x
+
+# URL: https://www.memotech.franken.de/FileFormats/
+# Reference: https://www.memotech.franken.de/FileFormats/Garmin_RGN_Format.pdf
+# From: Joerg Jenderek
+0 string KpGr Garmin update
+# format version like: 0064h~1.0
+>0x4 uleshort !0x0064
+>>4 uleshort/100 x \b, version %u
+>>4 uleshort%100 x \b.%u
+# 1st Garmin entry
+>6 use garmin-entry
+# 2nd Garmin entry
+>(0x6.l+10) ubyte x
+>>&0 use garmin-entry
+# 3rd entry
+>(0x6.l+10) ubyte x
+>>&(&0.l+4) ubyte x
+>>>&0 use garmin-entry
+# look again at version to use default clause
+>0x4 uleshort x
+# test for region content by looking for
+# Garmin *.srf by ./images with normal builder name "SQA" or longer "hales"
+# 1 space after equal sign
+>>0x3a search/5/s GARMIN\ BITMAP \b=
+!:mime image/x-garmin-exe
+!:ext exe
+>>>&0 indirect x
+# if not bitmap *.srf then region; 1 space after equal sign
+>>0x3a default x \b=
+!:mime application/x-garmin-rgn
+!:ext rgn
+# recursiv embedded
+>>>0x3a search/5/s KpGrd
+>>>>&0 indirect x
+# look for ZIP or JAR archive by ./archive and ./zip
+>>>0x3a search/5/s PK\003\004
+>>>>&0 indirect x
+# TODO: other garmin RGN record content like foo
+#>>0x3a search/5/s bar BAR
+# display information of Garmin RGN record
+0 name garmin-entry
+# record length: 2 for Data, for Application often 1Bh sometimes 1Dh, "big" for Region
+#>0 ulelong x \b, length %#x
+# data record (ID='D') with version content like 0064h~1.0
+>4 ubyte =0x44
+>>5 uleshort !0x0064 \b; Data
+>>>5 uleshort/100 x \b, version %u
+>>>5 uleshort%100 x \b.%u
+# Application Record (ID='A')
+>4 ubyte =0x41 \b; App
+# version content like 00c8h~2.0
+>>5 uleshort !0x00C8
+>>>5 uleshort/100 x \b, version %u
+>>>5 uleshort%100 x \b.%u
+# builder name like: SQA sqa build hales
+>>7 string x \b, build by %s
+# build date like: Oct 25 1999, Oct 1 2008, Feb 23 2009, Dec 15 2009
+>>>&1 string x %s
+# build time like: 11:26:12, 11:45:54, 14:16:13, 18:23:01
+>>>>&1 string x %s
+# region record (ID='R')
+>4 ubyte =0x52 \b; Region
+# region ID:14~fw_all.bin: 78~ZIP, RGN or SRF bitmap; 148~ZIP or JAR; 249~display firmware; 251~WiFi or GCD firmware; 255~ZIP
+>>5 uleshort x ID=%u
+# delay in ms: like 0, 500
+>>7 ulelong !0 \b, %u ms
+# region size (is record length - 10)
+#>>11 ulelong x \b, length %#x
+# region content like:
+# "KpGr"~recursiv embedded,"GARMIN BITMAP"~Garmin Bitmap *.srf, "PK"~ZIP archive
+#>>15 string x \b, content "%s"
+>>15 ubequad x \b, content %#llx...
+# This does NOT WORK!
+#>>15 indirect x \b; contains
+>4 default x \b; other
+# garmin Record ID Identifies the record content like: D A R
+>>4 ubyte x ID '%c'
+
+# TOM TOM GPS watches ttbin files:
+# https://github.com/ryanbinns/ttwatch/tree/master/ttbin
+# From: Daniel Lenski
+0 byte 0x20
+>1 leshort 0x0007
+>>0x76 byte 0x20
+>>>0x77 leshort 0x0075 TomTom activity file, v7
+>>>>8 leldate x (%s,
+>>>>3 byte x device firmware %d.
+>>>>4 byte x \b%d.
+>>>>5 byte x \b%d,
+>>>>6 leshort x product ID %04d)
+
+# Garmin firmware:
+# https://www.memotech.franken.de/FileFormats/Garmin_GCD_Format.pdf
+# https://www.gpsrchive.com/GPSMAP/GPSMAP%2066sr/Firmware.html
+0 string GARMIN
+>6 uleshort 100 GARMIN firmware (version 1.0)
diff --git a/contrib/libs/libmagic/magic/Magdir/maple b/contrib/libs/libmagic/magic/Magdir/maple
new file mode 100644
index 0000000000..80cf9f29a1
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/maple
@@ -0,0 +1,109 @@
+
+#------------------------------------------------------------------------------
+# $File: maple,v 1.10 2021/08/30 13:31:25 christos Exp $
+# maple: file(1) magic for maple files
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# Maple V release 4, a multi-purpose math program
+#
+
+# maple library .lib
+# URL: https://en.wikipedia.org/wiki/Maple_(software)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/l/lib-maple-v-r4.trid.xml
+# Update: Joerg Jenderek
+0 string \000MVR4\nI Maple Vr4 library
+#!:mime application/octet-stream
+!:mime application/x-maple-lib
+!:ext lib
+
+# URL: https://en.wikipedia.org/wiki/Maple_(software)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/l/lib-maple-v-r5.trid.xml
+# From: Joerg Jenderek
+0 string \000MVR5\n Maple Vr5 library
+#!:mime application/octet-stream
+!:mime application/x-maple-lib
+!:ext lib
+
+# From: Joerg Jenderek
+0x400 string M7R0\nI Maple Vr7 library
+#!:mime application/octet-stream
+!:mime application/x-maple-lib
+!:ext lib
+# null terminated library name like: C:\Maple12/Cliffordlib\maple.lib ../Maplets/Tutors.lib
+>5 string x %s
+# probably library name padding with nil or points (0x2E)
+#>0xF8 uquad x \b, PADDING 0x%16.16llx
+# null terminated strings like: Exterior Clifford FunctionArithmetics
+# like: 1 20 40
+>0x115 ulelong x \b, %u string
+# plural s
+>0x115 ulelong >1 \bs
+>0x119 string x 1st '%s'
+# probably second name section padding with nil or points (0x2E)
+#>0x3F0 uquad x \b, 2nd PADDING 0x%16.16llx
+# line feed separated ASCII string with maximal 79 length
+#>0x407 string x \b, section "%s"
+>0x454 ubyte !0x0a \b, at 0x454 0x%x
+
+# .ind
+# no magic for these :-(
+# they are compiled indexes for maple files
+
+# .hdb
+# Update: Joerg Jenderek
+# URL: https://www.maplesoft.com/support/help/maple/view.aspx?path=Formats/HDB
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/h/hdb-maple.trid.xml
+# Note: This format was replaced in Maple 18 by the Maple Help format (*.help)
+0 string \000\004\000\000
+# skip xBASE Compound Index file *.CDX by looking for version
+>1028 string version Maple help database
+# length of string version
+#>>1024 ulelong !7 \b, at 0x400 unexpected %u
+#!:mime application/octet-stream
+!:mime application/x-maple-hdb
+!:ext hdb
+>1028 default x
+# skip more xBASE Compound Index file *.CDX by looking for keyword Maple
+# like hsum.hdb
+>>4 search/0xCC41 Maple Maple help database
+!:mime application/x-maple-hdb
+!:ext hdb
+
+# .mhp
+# this has the form <PACKAGE=name>
+0 string \<PACKAGE= Maple help file
+0 string \<HELP\ NAME= Maple help file
+0 string \n\<HELP\ NAME= Maple help file with extra carriage return at start (yuck)
+#0 string #\ Newton Maple help file, old style
+0 string #\ daub Maple help file, old style
+#0 string #=========== Maple help file, old style
+
+# .mws
+0 string \000\000\001\044\000\221 Maple worksheet
+#this is anomalous
+0 string WriteNow\000\002\000\001\000\000\000\000\100\000\000\000\000\000 Maple worksheet, but weird
+# this has the form {VERSION 2 3 "IBM INTEL NT" "2.3" }\n
+# that is {VERSION major_version miunor_version computer_type version_string}
+0 string {VERSION\ Maple worksheet
+>9 string >\0 version %.1s.
+>>11 string >\0 %.1s
+
+# .mps
+0 string \0\0\001$ Maple something
+# from byte 4 it is either 'nul E' or 'soh R'
+# I think 'nul E' means a file that was saved as a different name
+# a sort of revision marking
+# 'soh R' means new
+>4 string \000\105 An old revision
+>4 string \001\122 The latest save
+
+# .mpl
+# some of these are the same as .mps above
+#0000000 000 000 001 044 000 105 same as .mps
+#0000000 000 000 001 044 001 122 same as .mps
+
+0 string #\n##\ <SHAREFILE= Maple something
+0 string \n#\n##\ <SHAREFILE= Maple something
+0 string ##\ <SHAREFILE= Maple something
+0 string #\r##\ <SHAREFILE= Maple something
+0 string \r#\r##\ <SHAREFILE= Maple something
+0 string #\ \r##\ <DESCRIBE> Maple something anomalous.
diff --git a/contrib/libs/libmagic/magic/Magdir/marc21 b/contrib/libs/libmagic/magic/Magdir/marc21
new file mode 100644
index 0000000000..bb4998ec04
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/marc21
@@ -0,0 +1,30 @@
+#--------------------------------------------
+# marc21: file(1) magic for MARC 21 Format
+#
+# Kevin Ford (kefo@loc.gov)
+#
+# MARC21 formats are for the representation and communication
+# of bibliographic and related information in machine-readable
+# form. For more info, see https://www.loc.gov/marc/
+
+
+# leader position 20-21 must be 45
+# and 22-23 also 00 so far, but we check that later.
+20 string 45
+>0 search/2048 \x1e
+
+# leader starts with 5 digits, followed by codes specific to MARC format
+>>0 regex/1l (^[0-9]{5})[acdnp][^bhlnqsu-z] MARC21 Bibliographic
+!:mime application/marc
+>>0 regex/1l (^[0-9]{5})[acdnosx][z] MARC21 Authority
+!:mime application/marc
+>>0 regex/1l (^[0-9]{5})[cdn][uvxy] MARC21 Holdings
+!:mime application/marc
+>>0 regex/1l (^[0-9]{5})[acdn][w] MARC21 Classification
+!:mime application/marc
+>>0 regex/1l (^[0-9]{5})[cdn][q] MARC21 Community
+!:mime application/marc
+
+# leader position 22-23, should be "00" but is it?
+>>0 regex/1l (^.{21})([^0]{2}) (non-conforming)
+!:mime application/marc
diff --git a/contrib/libs/libmagic/magic/Magdir/mathcad b/contrib/libs/libmagic/magic/Magdir/mathcad
new file mode 100644
index 0000000000..b186641f7d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mathcad
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: mathcad,v 1.5 2009/09/19 16:28:10 christos Exp $
+# mathcad: file(1) magic for Mathcad documents
+# URL: http://www.mathsoft.com/
+# From: Josh Triplett <josh@freedesktop.org>
+
+0 string .MCAD\t Mathcad document
diff --git a/contrib/libs/libmagic/magic/Magdir/mathematica b/contrib/libs/libmagic/magic/Magdir/mathematica
new file mode 100644
index 0000000000..dda71e884e
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mathematica
@@ -0,0 +1,192 @@
+
+#------------------------------------------------------------------------------
+# $File: mathematica,v 1.17 2023/06/16 19:33:58 christos Exp $
+# mathematica: file(1) magic for mathematica files
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# Mathematica a multi-purpose math program
+# versions 2.2 and 3.0
+
+0 name wolfram
+>0 string x Mathematica notebook version 2.x
+!:ext mb
+!:mime application/vnd.wolfram.mathematica
+
+#mathematica .mb
+0 string \064\024\012\000\035\000\000\000
+>0 use wolfram
+0 string \064\024\011\000\035\000\000\000
+>0 use wolfram
+
+#
+0 search/1000 Content-type:\040application/mathematica Mathematica notebook version 2.x
+!:ext nb
+!:mime application/mathematica
+
+
+# .ma
+# multiple possibilities:
+
+0 string (*^\n\n::[\011frontEndVersion\ =
+#>41 string >\0 %s
+>0 use wolfram
+
+#0 string (*^\n\n::[\011palette
+
+#0 string (*^\n\n::[\011Information
+#>675 string >\0 %s #doesn't work well
+
+# there may be 'cr' instead of 'nl' in some does this matter?
+
+# generic:
+0 string (*^\r\r::[\011
+>0 use wolfram
+0 string (*^\r\n\r\n::[\011
+>0 use wolfram
+0 string (*^\015
+>0 use wolfram
+0 string (*^\n\r\n\r::[\011
+>0 use wolfram
+0 string (*^\r::[\011
+>0 use wolfram
+0 string (*^\r\n::[\011
+>0 use wolfram
+0 string (*^\n\n::[\011
+>0 use wolfram
+0 string (*^\n::[\011
+>0 use wolfram
+
+
+# Mathematica .mx files
+
+#0 string (*This\ is\ a\ Mathematica\ binary\ dump\ file.\ It\ can\ be\ loaded\ with\ Get.*) Mathematica binary file
+0 string (*This\ is\ a\ Mathematica\ binary\ Mathematica binary file
+#>71 string \000\010\010\010\010\000\000\000\000\000\000\010\100\010\000\000\000
+# >71... is optional
+>88 string >\0 from %s
+
+
+# Mathematica files PBF:
+# 115 115 101 120 102 106 000 001 000 000 000 203 000 001 000
+0 string MMAPBF\000\001\000\000\000\203\000\001\000 Mathematica PBF (fonts I think)
+
+# .ml files These are menu resources I think
+# these start with "[0-9][0-9][0-9]\ A~[0-9][0-9][0-9]\
+# how to put that into a magic rule?
+4 string \ A~ MAthematica .ml file
+
+# .nb files
+#too long 0 string (***********************************************************************\n\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Mathematica-Compatible Notebook Mathematica 3.0 notebook
+0 string (*********************** Mathematica 3.0 notebook
+
+# other (* matches it is a comment start in these langs
+# GRR: Too weak; also matches other languages e.g. ML
+#0 string (* Mathematica, or Pascal, Modula-2 or 3 code text
+
+#########################
+# MatLab v5
+# URL: http://fileformats.archiveteam.org/wiki/MAT
+# Reference: https://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf
+# first 116 bytes of header contain text in human-readable form
+0 string MATLAB Matlab v
+#>11 string/T x \b, at 11 "%.105s"
+#!:mime application/octet-stream
+!:mime application/x-matlab-data
+!:ext mat
+# https://de.mathworks.com/help/matlab/import_export/mat-file-versions.html
+# level of the MAT-file like: 5.0 7.0 or maybe 7.3
+#>7 string x LEVEL "%.3s"
+>7 ubyte =0x35 \b5 mat-file
+>7 ubyte !0x35
+>>7 string x \b%.3s mat-file
+>126 short 0x494d (big endian)
+>>124 beshort x version %#04x
+>126 short 0x4d49 (little endian)
+# 0x0100 for level 5.0 and 0x0200 for level 7.0
+>>124 leshort x version %#04x
+# test again so that default clause works
+>126 short x
+# created by MATLAB include Platform sometimes without leading comma (0x2C) or missing
+# like: GLNX86 PCWIN PCWIN64 SOL2 Windows\0407 nt posix
+>>20 search/2 Platform:\040 \b, platform
+>>>&0 string x %-0.2s
+>>>&2 ubyte !0x2C \b%c
+>>>>&0 ubyte !0x2C \b%c
+>>>>>&0 ubyte !0x2C \b%c
+>>>>>>&0 ubyte !0x2C \b%c
+>>>>>>>&0 ubyte !0x2C \b%c
+>>>>>>>>&0 ubyte !0x2C \b%c
+>>>>>>>>>&0 ubyte !0x2C \b%c
+# examples without Platform tag like one_by_zero_char.mat
+>>20 default x
+>>>11 string x "%s"
+# created by MATLAB include time like: Fri Feb 20 15:26:59 2009
+>34 search/9/c created\040on:\040 \b, created
+>>&0 string x %-.24s
+# MatLab v4
+# From: Joerg Jenderek
+# check for valid imaginary flag of Matlab matrix version 4
+13 ushort 0
+# check for valid ASCII matrix name
+>20 ubyte >0x1F
+# skip PreviousEntries.dat with "invalid high" name \304P\344@\001
+>>20 ubyte <0304
+# skip some Netwfw*.dat and $I3KREPH.dat by checking for non zero number of rows
+>>>4 ulong !0
+# skip some CD-ROM filesystem like test-hfs.iso by looking for valid big endian type flag
+>>>>0 ubelong&0xFFffFF00 0x00000300
+>>>>>0 use matlab4
+# no example for 8-bit and 16-bit integers matrix
+>>>>0 ubelong&0xFFffFF00 0x00000400
+>>>>>0 use matlab4
+# branch for Little-Endian variant of Matlab MATrix version 4
+# skip big endian variant by looking for valid low lttle endian type flag
+>>>>0 ulelong <53
+# skip tokens.dat and some Netwfw*.dat by check for valid imaginary flag value of MAT version 4
+>>>>>12 ulelong <2
+# no misidentified little endian MATrix example with "short" matrix name
+>>>>>>16 ulelong <3
+# skip radeon firmware BONAIRE_sdma.bin HAWAII_sdma.bin KABINI_sdma.bin KAVERI_sdma.bin MULLINS_sdma.bin
+# by check for non zero matrix name length
+>>>>>>>16 ubelong >0
+>>>>>>>>0 use \^matlab4
+# little endian MATrix with "long" matrix name or some misidentified samples
+>>>>>>16 ulelong >2
+# skip TileCacheLogo-*.dat with invalid 2nd character \001 of matrix name with length 96
+>>>>>>>21 ubyte >0x1F
+>>>>>>>>0 use \^matlab4
+# Note: called "MATLAB Mat File" with version "Level 4" by DROID via PUID fmt/1550
+# display information of Matlab v4 mat-file
+0 name matlab4 Matlab v4 mat-file
+#!:mime application/octet-stream
+!:mime application/x-matlab-data
+!:ext mat
+# 20-byte header with 5 long integers that contains information describing certain attributes of the Matrix
+# type flag decimal MOPT; maximal 4052=FD4h; maximal 52=34h for little endian
+#>0 ubelong x \b, type flag %u
+#>0 ubelong x (%#x)
+# M: 0~little endian 1~Big Endian 2~VAX D-float 3~VAX G-float 4~Cray
+#>0 ubelong/1000 x \b, M=%u
+>0 ubelong/1000 0 (little endian)
+>0 ubelong/1000 1 (big endian)
+>0 ubelong/1000 2 (VAX D-float)
+>0 ubelong/1000 3 (VAX G-float)
+>0 ubelong/1000 4 (Cray)
+# namlen; the length of the matrix name
+#>16 ubelong x \b, name length %u
+#>(16.L+19) ubyte x \b, TERMINATING NAME CHARACTER=%#x
+# nul terminated matrix name like: fit_params testmatrix testsparsecomplex teststringarray
+#>20 string x \b, MATRIX NAME="%s"
+#>21 ubyte x \b, MAYBE 2ND CHAR=%c
+>16 pstring/L x %s
+# T indicates the matrix type: 0~numeric 1~text 2~sparse
+#>0 ubelong%10 x \b, T=%u
+>0 ubelong%10 0 \b, numeric
+>0 ubelong%10 1 \b, text
+>0 ubelong%10 2 \b, sparse
+# mrows; number of rows in the matrix like: 1 3 8
+>4 ubelong x \b, rows %u
+# ncols; number of columns in the matrix like: 1 3 4 5 9 43
+>8 ubelong x \b, columns %u
+# imagf; imaginary flag; 1~matrix has an imaginary part 0~only real data
+>12 ubelong !0 \b, imaginary (%u)
+# real; Real part of the matrix consists of mrows * ncols numbers
diff --git a/contrib/libs/libmagic/magic/Magdir/matroska b/contrib/libs/libmagic/magic/Magdir/matroska
new file mode 100644
index 0000000000..271af556aa
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/matroska
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# $File: matroska,v 1.9 2019/04/19 00:42:27 christos Exp $
+# matroska: file(1) magic for Matroska files
+#
+# See https://www.matroska.org/
+#
+
+# EBML id:
+0 belong 0x1a45dfa3
+# DocType id:
+>4 search/4096 \x42\x82
+# DocType contents:
+>>&1 string webm WebM
+!:mime video/webm
+>>&1 string matroska Matroska data
+!:mime video/x-matroska
diff --git a/contrib/libs/libmagic/magic/Magdir/mcrypt b/contrib/libs/libmagic/magic/Magdir/mcrypt
new file mode 100644
index 0000000000..f2edd08912
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mcrypt
@@ -0,0 +1,52 @@
+
+#------------------------------------------------------------------------------
+# $File: mcrypt,v 1.6 2022/02/08 18:51:45 christos Exp $
+# Mavroyanopoulos Nikos <nmav@hellug.gr>
+# mcrypt: file(1) magic for mcrypt 2.2.x;
+# URL: https://en.wikipedia.org/wiki/Mcrypt
+# http://fileformats.archiveteam.org/wiki/MCrypt
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/n/nc-mcrypt.trid.xml
+# Update: Joerg Jenderek
+# Note: called by TrID "mcrypt encrypted (v2.5)"
+0 string \0m\3 mcrypt 2.5 encrypted data,
+#!:mime application/octet-stream
+!:mime application/x-crypt-nc
+!:ext nc
+>4 string >\0 algorithm: %s,
+>>&1 leshort >0 keysize: %d bytes,
+>>>&0 string >\0 mode: %s,
+
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/n/nc-mcrypt-22.trid.xml
+# Note: called by TrID "mcrypt encrypted (v2.2)"
+0 string \0m\2 mcrypt 2.2 encrypted data,
+#!:mime application/octet-stream
+!:mime application/x-crypt-nc
+# no example
+!:ext nc
+>3 byte 0 algorithm: blowfish-448,
+>3 byte 1 algorithm: DES,
+>3 byte 2 algorithm: 3DES,
+>3 byte 3 algorithm: 3-WAY,
+>3 byte 4 algorithm: GOST,
+>3 byte 6 algorithm: SAFER-SK64,
+>3 byte 7 algorithm: SAFER-SK128,
+>3 byte 8 algorithm: CAST-128,
+>3 byte 9 algorithm: xTEA,
+>3 byte 10 algorithm: TWOFISH-128,
+>3 byte 11 algorithm: RC2,
+>3 byte 12 algorithm: TWOFISH-192,
+>3 byte 13 algorithm: TWOFISH-256,
+>3 byte 14 algorithm: blowfish-128,
+>3 byte 15 algorithm: blowfish-192,
+>3 byte 16 algorithm: blowfish-256,
+>3 byte 100 algorithm: RC6,
+>3 byte 101 algorithm: IDEA,
+>4 byte 0 mode: CBC,
+>4 byte 1 mode: ECB,
+>4 byte 2 mode: CFB,
+>4 byte 3 mode: OFB,
+>4 byte 4 mode: nOFB,
+>5 byte 0 keymode: 8bit
+>5 byte 1 keymode: 4bit
+>5 byte 2 keymode: SHA-1 hash
+>5 byte 3 keymode: MD5 hash
diff --git a/contrib/libs/libmagic/magic/Magdir/measure b/contrib/libs/libmagic/magic/Magdir/measure
new file mode 100644
index 0000000000..42e7186484
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/measure
@@ -0,0 +1,44 @@
+
+#------------------------------------------------------------------------------
+# $File: measure,v 1.3 2021/03/25 17:30:10 christos Exp $
+# measure: file(1) magic for measurement data
+
+# DIY-Thermocam raw data
+0 name diy-thermocam-parser
+>0 beshort x scale %d-
+>2 beshort x \b%d,
+>4 lefloat x spot sensor temperature %f,
+>9 ubyte 0 unit celsius,
+>9 ubyte 1 unit fahrenheit,
+>8 ubyte x color scheme %d
+>10 ubyte 1 \b, show spot sensor
+>11 ubyte 1 \b, show scale bar
+>12 ubyte &1 \b, minimum point enabled
+>12 ubyte &2 \b, maximum point enabled
+>13 lefloat x \b, calibration: offset %f,
+>17 lefloat x slope %f
+
+0 name diy-thermocam-checker
+>9 ubyte <2
+>>10 ubyte <2
+>>>11 ubyte <2
+>>>>12 ubyte <4
+>>>>>17 lefloat >0.0001 DIY-Thermocam raw data
+
+# V2 and Leption 3.x:
+38408 ubyte <19
+>38400 use diy-thermocam-checker
+>>38400 default x (Lepton 3.x),
+>>>38400 use diy-thermocam-parser
+
+# V1 or Lepton 2.x
+9608 ubyte <19
+>9600 use diy-thermocam-checker
+>>9600 default x (Lepton 2.x),
+>>>9600 use diy-thermocam-parser
+
+# Becker & Hickl Photon Counting (PMS) data file
+# format documentation: https://www.becker-hickl.com/wp-content/uploads/2018/11/opm-pms400-v01.pdf (page 57)
+(0x02.l) string *IDENTIFICATION Becker & Hickl PMS Data File
+>0x12 short x (%d data blocks)
+!:ext sdt
diff --git a/contrib/libs/libmagic/magic/Magdir/mercurial b/contrib/libs/libmagic/magic/Magdir/mercurial
new file mode 100644
index 0000000000..b8f3cddb36
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mercurial
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: mercurial,v 1.5 2019/04/19 00:42:27 christos Exp $
+# mercurial: file(1) magic for Mercurial changeset bundles
+# https://www.selenic.com/mercurial/wiki/
+#
+# Jesse Glick (jesse.glick@sun.com)
+#
+
+0 string HG10 Mercurial changeset bundle
+>4 string UN (uncompressed)
+>4 string GZ (gzip compressed)
+>4 string BZ (bzip2 compressed)
diff --git a/contrib/libs/libmagic/magic/Magdir/metastore b/contrib/libs/libmagic/magic/Magdir/metastore
new file mode 100644
index 0000000000..e64e70440b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/metastore
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: metastore,v 1.3 2019/04/19 00:42:27 christos Exp $
+# metastore: file(1) magic for metastore files
+# From: Thomas Wissen
+# see https://david.hardeman.nu/software.php#metastore
+0 string MeTaSt00r3 Metastore data file,
+>10 bequad x version %0llx
diff --git a/contrib/libs/libmagic/magic/Magdir/meteorological b/contrib/libs/libmagic/magic/Magdir/meteorological
new file mode 100644
index 0000000000..725982f8d9
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/meteorological
@@ -0,0 +1,53 @@
+
+#------------------------------------------------------------------------------
+# $File: meteorological,v 1.4 2022/12/09 18:02:09 christos Exp $
+# rinex: file(1) magic for RINEX files
+# http://igscb.jpl.nasa.gov/igscb/data/format/rinex210.txt
+# ftp://cddis.gsfc.nasa.gov/pub/reports/formats/rinex300.pdf
+# data for testing: ftp://cddis.gsfc.nasa.gov/pub/gps/data
+60 string RINEX
+>80 search/256 XXRINEXB RINEX Data, GEO SBAS Broadcast
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/broadcast
+>80 search/256 XXRINEXD RINEX Data, Observation (Hatanaka comp)
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/observation
+>80 search/256 XXRINEXC RINEX Data, Clock
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/clock
+>80 search/256 XXRINEXH RINEX Data, GEO SBAS Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXG RINEX Data, GLONASS Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXL RINEX Data, Galileo Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXM RINEX Data, Meteorological
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/meteorological
+>80 search/256 XXRINEXN RINEX Data, Navigation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/navigation
+>80 search/256 XXRINEXO RINEX Data, Observation
+>>&32 string x \b, date %15.15s
+>>5 string x \b, version %6.6s
+!:mime rinex/observation
+
+# https://en.wikipedia.org/wiki/GRIB
+0 string GRIB
+>7 byte =1 Gridded binary (GRIB) version 1
+!:mime application/x-grib
+!:ext grb/grib
+>7 byte =2 Gridded binary (GRIB) version 2
+!:mime application/x-grib2
+!:ext grb2/grib2
diff --git a/contrib/libs/libmagic/magic/Magdir/microfocus b/contrib/libs/libmagic/magic/Magdir/microfocus
new file mode 100644
index 0000000000..93e39aa1bc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/microfocus
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# $File: microfocus,v 1.3 2019/04/19 00:42:27 christos Exp $
+# Micro Focus COBOL data files.
+
+# https://documentation.microfocus.com/help/index.jsp?topic=\
+# %2FGUID-0E0191D8-C39A-44D1-BA4C-D67107BAF784%2FHRFLRHFILE05.html
+# http://www.cobolproducts.com/datafile/data-viewer.html
+# https://github.com/miracle2k/mfcobol-export
+
+0 string \x30\x00\x00\x7C
+>36 string \x00\x3E Micro Focus File with Header (DAT)
+!:mime application/octet-stream
+
+0 string \x30\x7E\x00\x00
+>36 string \x00\x3E Micro Focus File with Header (DAT)
+!:mime application/octet-stream
+
+39 string \x02
+>136 string \x02\x02\x04\x04 Micro Focus Index File (IDX)
+!:mime application/octet-stream
diff --git a/contrib/libs/libmagic/magic/Magdir/mime b/contrib/libs/libmagic/magic/Magdir/mime
new file mode 100644
index 0000000000..57b2dd557b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mime
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: mime,v 1.8 2017/03/17 22:20:22 christos Exp $
+# mime: file(1) magic for MIME encoded files
+#
+0 string/t Content-Type:\040
+>14 string >\0 %s
+0 string/t Content-Type:
+>13 string >\0 %s
diff --git a/contrib/libs/libmagic/magic/Magdir/mips b/contrib/libs/libmagic/magic/Magdir/mips
new file mode 100644
index 0000000000..fe83614703
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mips
@@ -0,0 +1,120 @@
+
+#------------------------------------------------------------------------------
+# $File: mips,v 1.10 2014/04/30 21:41:02 christos Exp $
+# mips: file(1) magic for MIPS ECOFF and Ucode, as used in SGI IRIX
+# and DEC Ultrix
+#
+0 beshort 0x0160 MIPSEB ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x0162 MIPSEL-BE ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x6001 MIPSEB-LE ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x6201 MIPSEL ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+# MIPS 2 additions
+#
+0 beshort 0x0163 MIPSEB MIPS-II ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x0166 MIPSEL-BE MIPS-II ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x6301 MIPSEB-LE MIPS-II ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x6601 MIPSEL MIPS-II ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+# MIPS 3 additions
+#
+0 beshort 0x0140 MIPSEB MIPS-III ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x0142 MIPSEL-BE MIPS-III ECOFF executable
+>20 beshort 0407 (impure)
+>20 beshort 0410 (swapped)
+>20 beshort 0413 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>22 byte x - version %d
+>23 byte x \b.%d
+#
+0 beshort 0x4001 MIPSEB-LE MIPS-III ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x4201 MIPSEL MIPS-III ECOFF executable
+>20 beshort 03401 (impure)
+>20 beshort 04001 (swapped)
+>20 beshort 05401 (paged)
+>8 belong >0 not stripped
+>8 belong 0 stripped
+>23 byte x - version %d
+>22 byte x \b.%d
+#
+0 beshort 0x180 MIPSEB Ucode
+0 beshort 0x182 MIPSEL-BE Ucode
diff --git a/contrib/libs/libmagic/magic/Magdir/mirage b/contrib/libs/libmagic/magic/Magdir/mirage
new file mode 100644
index 0000000000..cdeb3fcbc2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mirage
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: mirage,v 1.7 2009/09/19 16:28:10 christos Exp $
+# mirage: file(1) magic for Mirage executables
+#
+# XXX - byte order?
+#
+0 long 31415 Mirage Assembler m.out executable
diff --git a/contrib/libs/libmagic/magic/Magdir/misctools b/contrib/libs/libmagic/magic/Magdir/misctools
new file mode 100644
index 0000000000..dc1542adac
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/misctools
@@ -0,0 +1,140 @@
+
+#-----------------------------------------------------------------------------
+# $File: misctools,v 1.21 2023/02/03 20:43:48 christos Exp $
+# misctools: file(1) magic for miscellaneous UNIX tools.
+#
+0 search/1 %%!! X-Post-It-Note text
+# URL: http://fileformats.archiveteam.org/wiki/ICalendar
+# https://en.wikipedia.org/wiki/ICalendar
+# Update: Joerg Jenderek
+# Reference: https://www.rfc-editor.org/rfc/rfc5545
+# http://mark0.net/download/triddefs_xml.7z/defs/v/vcs.trid.xml
+# Note: called "iCalendar - vCalendar" by TrID
+0 string/c BEGIN:vcalendar
+# skip DROID fmt-387-signature-id-572.vcs fmt-388-signature-id-573.ics
+# with invalid separator 0x0 or 0xAB instead of CarriageReturn (0x0D) or LineFeed (0x0A)
+>15 ubyte&0xF8 =0x08
+# look for VERSION keyword often on second line but sometimes later as in holidays_NRW_2014.ics
+>>0 search/188 VERSION
+# after VERSION keword :1.0 or often :2.0 but sometimes also ;VALUE=TEXT:2.0 like in Jewish religious Juish.ics
+# http://www.webcal.guru/de-DE/kalender_herunterladen?calendar_instance_id=217
+# \n\040:2.0 like in import-real-world-2004-11-19.ics found at
+# https://ftp.gnu.org/gnu/emacs/emacs-28.1.tar.xz
+# emacs-28.1/test/lisp/calendar/icalendar-resources/import-real-world-2004-11-19.ics
+#>>>&0 string x AFTER_VERSION=%.15s
+# Note: called "Internet Calendar and Scheduling format" by DROID via PUID fmt/388
+# skip optional verparam=;other-param like ;VALUE=TEXT and look for version 2.0 that implies iCalendar variant
+>>>&0 search/81 :2.0 iCalendar calendar
+# look for Free/Busy component
+>>>>15 search/278 :VFREEBUSY file, with Free/Busy component
+!:mime text/calendar
+!:apple ????iFBf
+# no real examples found but only example on Wikipedia page
+!:ext ifb
+# iCalendar calendar without Free/Busy component
+>>>>15 default x
+# look for ALARM component
+>>>>>15 search/154 :VALARM file, with ALARM component
+!:mime text/calendar
+!:apple ????iCal
+# found on macOS beneath /Users/$USER/Library/Calendars/ as EventAllDayAlarms.icsalarm or EventTimedAlarms.icsalarm
+# no isc examples found
+!:ext icsalarm/ics
+# iCalendar calendar without Free/Busy component and ALARM component
+>>>>>15 default x file
+!:mime text/calendar
+!:apple ????iCal
+# no examples found with .ical .icalender suffix
+!:ext ics
+# if no VERSION 2.0 is found then assume it is VERSION 1.0, that is older vCalendar
+# URL: http://fileformats.archiveteam.org/wiki/VCalendar
+# Note: called "VCalendar format" by DROID via fmt/387
+>>>&0 default x vCalendar calendar file
+# deprecated
+!:mime text/x-vcalendar
+!:ext vcs
+# GRR: without VERSION keyword violates specification but accepted by Thunderbird like
+# https://ftp.gnu.org/gnu/emacs/emacs-28.1.tar.xz
+# emacs-28.1/test/lisp/calendar/icalendar-resources/import-with-timezone.ics
+>>0 default x vCalendar calendar file, without VERSION
+!:mime text/x-vcalendar
+#!:mime text/calendar
+# no vcs example found
+!:ext ics/vcs
+# GRR: According to newest specification CarriageReturn (0xD) and LineFeed (0xA) should be used as separator but others accepted by Thunderbird
+# like CRLF,LF in Sport Today.vcs created by calendar plugin of TV-Browser https://enwiki.tvbrowser.org/index.php/Calendar_Export
+# or LF like https://www.schulferien.org/media/ical/deutschland/ferien_nordrhein-westfalen_2023.ics?k=foo
+>>15 ubeshort !0x0D0A \b, without CRLF
+
+# updated by Joerg Jenderek at Apr 2015, May 2021
+# https://en.wikipedia.org/wiki/VCard
+# URL: http://fileformats.archiveteam.org/wiki/VCard
+# https://datatracker.ietf.org/doc/html/rfc6350
+# the value is case-insensitive
+0 string/c begin:vcard
+# skip DROID fmt-395-signature-id-634.vcf
+>13 string !VERSION:END vCard visiting card
+# deprecated
+#!:mime text/x-vcard
+!:mime text/vcard
+!:apple ????vCrd
+!:ext vcf/vcard
+# VERSION must come right after BEGIN for 3.0 or 4.0 except in 2.1 , where it can be anywhere
+# Joerg_Jenderek_67.vcf
+>>12 search/0x113b4/c version:
+# VERSION 2.1 , 3.0 or 4.0
+>>>&0 string x \b, version %-.3s
+>>>&0 string !2.1
+>>>>13 string !VERSION: \b, 2nd line does not start with VERSION:
+# downcase violates RFC 6350, but some "bad" software produce such vcards
+>>0 string !BEGIN \b, not up case
+# http://ftp.mozilla.org/pub/thunderbird/candidates/
+# 78.10.1-candidates/build1/source/thunderbird-78.10.1.source.tar.xz
+# thunderbird-78.10.1/comm/mailnews/import/test/unit/resources/basic_vcard_addressbook.vcf
+>>11 beshort !0x0D0A \b, lines not separated by CRLF
+
+# Summary: Libtool library file
+# Extension: .la
+# Submitted by: Tomasz Trojanowski <tomek@uninet.com.pl>
+0 search/80 .la\ -\ a\ libtool\ library\ file libtool library file
+
+# Summary: Libtool object file
+# Extension: .lo
+# Submitted by: Abel Cheung <abelcheung@gmail.com>
+0 search/80 .lo\ -\ a\ libtool\ object\ file libtool object file
+
+# From: Daniel Novotny <dnovotny@redhat.com>
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Core_dump#User-mode_memory_dumps
+# Reference: https://msdn.microsoft.com/en-us/library/ms680378%28VS.85%29.aspx
+#
+# "Windows Minidump" by TrID
+# ./misctools (version 5.25) labeled the entry as "MDMP crash report data"
+0 string MDMP Mini DuMP crash report
+# https://filext.com/file-extension/DMP
+!:mime application/x-dmp
+!:ext dmp/mdmp
+# The high-order word is an internal value that is implementation specific.
+# The low-order word is MINIDUMP_VERSION 0xA793
+>4 ulelong&0x0000FFFF !0xA793 \b, version %#4.4x
+# NumberOfStreams 8,9,10,13
+>8 ulelong x \b, %d streams
+# StreamDirectoryRva 0x20
+>12 ulelong !0x20 \b, %#8.8x RVA
+# CheckSum 0
+>16 ulelong !0 \b, CheckSum %#8.8x
+# Reserved or TimeDateStamp
+>20 ledate x \b, %s
+# https://msdn.microsoft.com/en-us/library/windows/desktop/ms680519%28v=vs.85%29.aspx
+# Flags MINIDUMP_TYPE enumeration type 0 0x121 0x800
+>24 ulelong x \b, %#x type
+# >24 ulelong >0 \b; include
+# >>24 ulelong &0x00000001 \b data sections,
+# >>24 ulelong &0x00000020 \b list of unloaded modules,
+# >>24 ulelong &0x00000100 \b process and thread information,
+# >>24 ulelong &0x00000800 \b memory information,
+
+# Summary: abook addressbook file
+# Submitted by: Mark Schreiber <mark7@alumni.cmu.edu>
+0 string #\x20abook\x20addressbook\x20file abook address book
+!:mime application/x-abook-addressbook
diff --git a/contrib/libs/libmagic/magic/Magdir/mkid b/contrib/libs/libmagic/magic/Magdir/mkid
new file mode 100644
index 0000000000..faad3966c0
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mkid
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# $File: mkid,v 1.6 2009/09/19 16:28:10 christos Exp $
+# mkid: file(1) magic for mkid(1) databases
+#
+# ID is the binary tags database produced by mkid(1).
+#
+# XXX - byte order?
+#
+0 string \311\304 ID tags data
+>2 short >0 version %d
diff --git a/contrib/libs/libmagic/magic/Magdir/mlssa b/contrib/libs/libmagic/magic/Magdir/mlssa
new file mode 100644
index 0000000000..3c8875eb3d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mlssa
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: mlssa,v 1.4 2009/09/19 16:28:10 christos Exp $
+# mlssa: file(1) magic for MLSSA datafiles
+#
+0 lelong 0xffffabcd MLSSA datafile,
+>4 leshort x algorithm %d,
+>10 lelong x %d samples
diff --git a/contrib/libs/libmagic/magic/Magdir/mmdf b/contrib/libs/libmagic/magic/Magdir/mmdf
new file mode 100644
index 0000000000..5576a66277
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mmdf
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# $File: mmdf,v 1.6 2009/09/19 16:28:10 christos Exp $
+# mmdf: file(1) magic for MMDF mail files
+#
+0 string \001\001\001\001 MMDF mailbox
diff --git a/contrib/libs/libmagic/magic/Magdir/modem b/contrib/libs/libmagic/magic/Magdir/modem
new file mode 100644
index 0000000000..5d59401f6c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/modem
@@ -0,0 +1,92 @@
+
+#------------------------------------------------------------------------------
+# $File: modem,v 1.11 2022/10/19 20:15:16 christos Exp $
+# modem: file(1) magic for modem programs
+#
+# From: Florian La Roche <florian@knorke.saar.de>
+1 string PC\ Research,\ Inc Digifax-G3-File
+>29 byte 1 \b, fine resolution
+>29 byte 0 \b, normal resolution
+
+# Summary: CCITT Group 3 Facsimile in "raw" form (i.e. no header).
+# Modified by: Joerg Jenderek
+# URL: https://de.wikipedia.org/wiki/Fax
+# http://fileformats.archiveteam.org/wiki/CCITT_Group_3
+# Reference: https://web.archive.org/web/20020628195336/http://www.netnam.vn/unescocourse/computervision/104.htm
+# GRR: EOL of G3 is too general as it catches also TrueType fonts, Postscript PrinterFontMetric, others
+0 short 0x0100
+# 16 0-bits near beginning like True Type fonts *.ttf, Postscript PrinterFontMetric *.pfm, FTYPE.HYPERCARD, XFER
+>2 search/9 \0\0
+# maximal 7 0-bits for pixel sequences or 11 0-bits for EOL in G3
+>2 default x
+# skip IRCAM file (VAX big-endian) ./audio
+>>0 belong !0x0001a364
+# skip GEM Image data ./images
+>>>2 beshort !0x0008
+# look for first keyword of Panorama database *.pan
+>>>>11 search/262 \x06DESIGN
+# skip Panorama database
+>>>>11 default x
+# old Apple DreamWorld DreamGrafix *.3200 with keyword at end of g3 looking files
+>>>>>27118 search/1864 DreamWorld
+>>>>>27118 default x
+# skip MouseTrap/Mt.Defaults with file size 16 found on Golden Orchard Apple II CD Rom
+>>>>>>8 ubequad !0x2e01010454010203
+# skip PICTUREH.SML found on Golden Orchard Apple II CD Rom
+>>>>>>>8 ubequad !0x5dee74ad1aa56394
+# skip few (5/41) DEGAS mid-res bitmap (GEMINI01.PI2 GEMINI02.PI2 GEMINI03.PI2 CODE_RAM.PI2 TBX_DEMO.PI2)
+# with file size 32034
+>>>>>>>>-0 offset !32034 raw G3 (Group 3) FAX, byte-padded
+# version 5.25 labeled the entry above "raw G3 data, byte-padded"
+!:mime image/g3fax
+#!:apple ????TIFF
+!:ext g3
+# unusual image starting with black pixel
+#0 short 0x1300 raw G3 (Group 3) FAX
+0 short 0x1400
+# 16 0-bits near beginning like PicturePuzzler found on Golden Orchard Apple CD Rom
+>2 search/9 \0\0
+# maximal 7 0-bits for pixel sequences or 11 0-bits for EOL in G3
+>2 default x
+# skip some (84/1246) MacBinary II/III (Cyberdog2.068k.smi.bin FileMakerPro4.img.bin Hypercard1.25.image.bin UsbStorage1.3.5.smi.bin) with "non random" numbers by versions values 81h/82h + 81h
+>>122 ubeshort&0xFcFf !0x8081 raw G3 (Group 3) FAX
+# version 5.25 labeled the above entry as "raw G3 data"
+!:mime image/g3fax
+!:ext g3
+# unusual image with black pixel near beginning
+#0 short 0x1900 raw G3 (Group 3) FAX
+
+#
+# Magic data for vgetty voice formats
+# (Martin Seine & Marc Eberhard)
+
+#
+# raw modem data version 1
+#
+0 string RMD1 raw modem data
+>4 string >\0 (%s /
+>20 short >0 compression type %#04x)
+
+#
+# portable voice format 1
+#
+0 string PVF1\n portable voice format
+>5 string >\0 (binary %s)
+
+#
+# portable voice format 2
+#
+0 string PVF2\n portable voice format
+>5 string >\0 (ascii %s)
+
+# From: Bernd Nuernberger <bernd.nuernberger@web.de>
+# Brooktrout G3 fax data incl. 128 byte header
+# Common suffixes: 3??, BRK, BRT, BTR
+0 leshort 0x01bb
+>2 leshort 0x0100 Brooktrout 301 fax image,
+>>9 leshort x %d x
+>>0x2d leshort x %d
+>>6 leshort 200 \b, fine resolution
+>>6 leshort 100 \b, normal resolution
+>>11 byte 1 \b, G3 compression
+>>11 byte 2 \b, G32D compression
diff --git a/contrib/libs/libmagic/magic/Magdir/modulefile b/contrib/libs/libmagic/magic/Magdir/modulefile
new file mode 100644
index 0000000000..46c3baf2a0
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/modulefile
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: modulefile,v 1.1 2019/10/15 18:04:40 christos Exp $
+# modulefile: file(1) magic for user's environment modulefile
+# URL: http://modules.sourceforge.net/
+# Reference: https://modules.readthedocs.io/en/stable/modulefile.html
+# From: Xavier Delaruelle <xavier.delaruelle@cea.fr>
+0 string #%Module modulefile
+!:mime text/x-modulefile
diff --git a/contrib/libs/libmagic/magic/Magdir/motorola b/contrib/libs/libmagic/magic/Magdir/motorola
new file mode 100644
index 0000000000..af93720f29
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/motorola
@@ -0,0 +1,71 @@
+
+#------------------------------------------------------------------------------
+# $File: motorola,v 1.12 2021/04/26 15:56:00 christos Exp $
+# motorola: file(1) magic for Motorola 68K and 88K binaries
+#
+# 68K
+#
+0 beshort 0520 mc68k COFF
+>18 beshort ^00000020 object
+>18 beshort &00000020 executable
+>12 belong >0 not stripped
+>168 string .lowmem Apple toolbox
+>20 beshort 0407 (impure)
+>20 beshort 0410 (pure)
+>20 beshort 0413 (demand paged)
+>20 beshort 0421 (standalone)
+0 beshort 0521 mc68k executable (shared)
+>12 belong >0 not stripped
+0 beshort 0522 mc68k executable (shared demand paged)
+>12 belong >0 not stripped
+#
+# Motorola/UniSoft 68K Binary Compatibility Standard (BCS)
+#
+0 beshort 0554 68K BCS executable
+#
+# 88K
+#
+# Motorola/88Open BCS
+#
+0 beshort 0555 88K BCS executable
+#
+# Motorola S-Records, from Gerd Truschinski <gt@freebsd.first.gmd.de>
+0 string S0 Motorola S-Record; binary data in text format
+
+# ATARI ST relocatable PRG
+#
+# from Oskar Schirmer <schirmer@scara.com> Feb 3, 2001
+# (according to Roland Waldi, Oct 21, 1987)
+# besides the magic 0x601a, the text segment size is checked to be
+# not larger than 1 MB (which is a lot on ST).
+# The additional 0x601b distinction I took from Doug Lee's magic.
+0 belong&0xFFFFFFF0 0x601A0000 Atari ST M68K contiguous executable
+>2 belong x (txt=%d,
+>6 belong x dat=%d,
+>10 belong x bss=%d,
+>14 belong x sym=%d)
+0 belong&0xFFFFFFF0 0x601B0000 Atari ST M68K non-contig executable
+>2 belong x (txt=%d,
+>6 belong x dat=%d,
+>10 belong x bss=%d,
+>14 belong x sym=%d)
+
+# Atari ST/TT... program format (sent by Wolfram Kleff <kleff@cs.uni-bonn.de>)
+0 beshort 0x601A Atari 68xxx executable,
+>2 belong x text len %u,
+>6 belong x data len %u,
+>10 belong x BSS len %u,
+>14 belong x symboltab len %u,
+>18 belong 0
+>22 belong &0x01 fastload flag,
+>22 belong &0x02 may be loaded to alternate RAM,
+>22 belong &0x04 malloc may be from alternate RAM,
+>22 belong x flags: %#X,
+>26 beshort 0 no relocation tab
+>26 beshort !0 + relocation tab
+>30 string SFX [Self-Extracting LZH SFX archive]
+>38 string SFX [Self-Extracting LZH SFX archive]
+>44 string ZIP! [Self-Extracting ZIP SFX archive]
+
+0 beshort 0x0064 Atari 68xxx CPX file
+>8 beshort x (version %04x)
diff --git a/contrib/libs/libmagic/magic/Magdir/mozilla b/contrib/libs/libmagic/magic/Magdir/mozilla
new file mode 100644
index 0000000000..32f3bb7e9c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mozilla
@@ -0,0 +1,37 @@
+
+#------------------------------------------------------------------------------
+# $File: mozilla,v 1.12 2021/04/26 15:56:00 christos Exp $
+# mozilla: file(1) magic for Mozilla XUL fastload files
+# (XUL.mfasl and XPC.mfasl)
+# URL: https://www.mozilla.org/
+# From: Josh Triplett <josh@freedesktop.org>
+
+0 string XPCOM\nMozFASL\r\n\x1A Mozilla XUL fastload data
+# Probably the next magic line contains misspelled "mozLz40\0"
+0 string mozLz4a Mozilla lz4 compressed bookmark data
+# From: Joerg Jenderek
+# URL: https://lz4.github.io/lz4/
+# Reference: https://github.com/avih/dejsonlz4/archive/master.zip/
+# dejsonlz4-master\src\dejsonlz4.c
+# Note: mostly JSON compressed with a non-standard LZ4 header
+# can be unpacked by dejsonlz4 but not lz4 program.
+0 string mozLz40\0 Mozilla lz4 compressed data
+!:mime application/x-lz4+json
+# mozlz4 extension seems to be used for search/store, while jsonlz4 for bookmarks
+!:ext jsonlz4/mozlz4
+# decomp_size
+>8 ulelong x \b, originally %u bytes
+# lz4 data
+#>12 ubequad x \b, lz4 data %#16.16llx
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Firefox_4
+# Reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
+# Note: Most ZIP utilities are able to extract such archives
+# maybe only partly or after some warnings. Example:
+# zip -FF omni.ja --out omni.zip
+4 string PK\001\002 Mozilla archive omni.ja
+!:mime application/x-zip
+!:ext ja
+# TODO:
+#>4 use zip-dir-entry
diff --git a/contrib/libs/libmagic/magic/Magdir/msdos b/contrib/libs/libmagic/magic/Magdir/msdos
new file mode 100644
index 0000000000..aacf85946b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/msdos
@@ -0,0 +1,2304 @@
+
+#------------------------------------------------------------------------------
+# $File: msdos,v 1.169 2023/04/17 16:39:19 christos Exp $
+# msdos: file(1) magic for MS-DOS files
+#
+
+# .BAT files (Daniel Quinlan, quinlan@yggdrasil.com)
+# updated by Joerg Jenderek at Oct 2008,Apr 2011
+0 string/t @
+>1 string/cW \ echo\ off DOS batch file text
+!:mime text/x-msdos-batch
+!:ext bat
+>1 string/cW echo\ off DOS batch file text
+!:mime text/x-msdos-batch
+!:ext bat
+>1 string/cW rem DOS batch file text
+!:mime text/x-msdos-batch
+!:ext bat
+>1 string/cW set\ DOS batch file text
+!:mime text/x-msdos-batch
+!:ext bat
+
+
+# OS/2 batch files are REXX. the second regex is a bit generic, oh well
+# the matched commands seem to be common in REXX and uncommon elsewhere
+100 search/0xffff rxfuncadd
+>100 regex/c =^[\ \t]{0,10}call[\ \t]{1,10}rxfunc OS/2 REXX batch file text
+100 search/0xffff say
+>100 regex/c =^[\ \t]{0,10}say\ ['"] OS/2 REXX batch file text
+
+# updated by Joerg Jenderek at Oct 2015
+# https://de.wikipedia.org/wiki/Common_Object_File_Format
+# http://www.delorie.com/djgpp/doc/coff/filhdr.html
+# ./intel already labeled COFF type 0x14c=0514 as "80386 COFF executable"
+#0 leshort 0x14c MS Windows COFF Intel 80386 object file
+#>4 ledate x stamp %s
+0 leshort 0x166 MS Windows COFF MIPS R4000 object file
+#>4 ledate x stamp %s
+0 leshort 0x184 MS Windows COFF Alpha object file
+#>4 ledate x stamp %s
+0 leshort 0x268 MS Windows COFF Motorola 68000 object file
+#>4 ledate x stamp %s
+0 leshort 0x1f0 MS Windows COFF PowerPC object file
+#>4 ledate x stamp %s
+0 leshort 0x290 MS Windows COFF PA-RISC object file
+#>4 ledate x stamp %s
+
+# Tests for various EXE types.
+#
+# Many of the compressed formats were extracted from IDARC 1.23 source code.
+#
+# e_magic
+0 string/b MZ
+# TODO
+# FLT: Syntrillium CoolEdit Filter https://en.wikipedia.org/wiki/Adobe_Audition
+# FMX64:FileMaker Pro 64-bit plug-in https://en.wikipedia.org/wiki/FileMaker
+# FMX: FileMaker Pro 32-bit plug-in https://en.wikipedia.org/wiki/FileMaker
+# FOD: WIFE Font Driver
+# GAU: MS Flight Simulator Gauge
+# IFS: OS/2 Installable File System https://en.wikipedia.org/wiki/OS/2
+# MEXW32:MATLAB Windows 32bit compiled function https://en.wikipedia.org/wiki/MATLAB
+# MEXW64:MATLAB Windows 64bit compiled function https://en.wikipedia.org/wiki/MATLAB
+# MLL: Maya plug-in (generic) http://en.wikipedia.org/wiki/Autodesk_Maya
+# PFL: PhotoFilter plugin http://photofiltre.free.fr
+# 8*: PhotoShop plug-in (generic) http://www.adobe.com/products/photoshop/main.html
+# PLG: Aston Shell plugin http://www.astonshell.com/
+# QLB: Microsoft Basic Quick library https://en.wikipedia.org/wiki/QuickBASIC
+# SKL: WinLIFT skin http://www.zapsolution.com/winlift/index.htm
+# TBK: Asymetrix ToolBook application http://www.toolbook.com
+# TBP: The Bat! plugin http://www.ritlabs.com
+# UPC: Ultimate Paint Graphics Editor plugin http://ultimatepaint.j-t-l.com
+# XFM: Syntrillium Cool Edit Transform Effect bad http://www.cooledit.com
+# XPL: X-Plane plugin http://www.xsquawkbox.net/xpsdk/
+# ZAP: ZoneLabs Zone Alarm data http://www.zonelabs.com
+#
+# NEXT LINES FOR DEBUGGING!
+# e_cblp; bytes on last page of file
+# e_cp; pages in file
+#>4 uleshort x \b, e_cp 0x%x
+# e_lfanew; file address of new exe header
+#>0x3c ulelong x \b, e_lfanew 0x%x
+# e_lfarlc; address of relocation table
+#>0x18 uleshort x \b, e_lfarlc=0x%x
+# e_ovno; overlay number. If zero, this is the main executable foo
+#>0x1a uleshort !0 \b, e_ovno 0x%x
+#>0x1C ubequad !0 \b, e_res 0x%16.16llx
+# e_oemid; often 0
+#>0x24 uleshort !0 \b, e_oemid 0x%x
+# e_oeminfo; typically zeroes, but 13Dh (WORDSTAR.CNV WPFT5.CNV) 143h (WRITWIN.CNV)
+# 1A3h (DBASE.CNV LOTUS123.CNV RFTDCA.CNV WORDDOS.CNV WORDMAC.CNV WORDWIN1.CNVXLBIFF.CNV)
+#>0x26 uleshort !0 \b, e_oeminfo 0x%x
+# e_res2; typically zeroes, but 000006006F082D2Ah SCSICFG.EXE 00009A0300007C03h de.exe
+# 0000CA0000000002h country.exe dosxmgr.exe 421E0A00421EA823h QMC.EXE
+#>0x28 ubequad !0 \b, e_res2 0x%16.16llx
+# https://web.archive.org/web/20171116024937/http://www.ctyme.com/intr/rb-2939.htm#table1593
+# https://github.com/uxmal/reko/blob/master/src/ImageLoaders/MzExe/ExeImageLoader.cs
+# new exe header magic like: PE NE LE LX W3 W4
+# no examples found for ZM DL MP P2 P3
+#>(0x3c.l) string x \b, at [0x3c] %.2s
+#>(0x3c.l) ubelong x \b, at [0x3c] %#8.8x
+#>(0x3c.l+4) ubelong x \b, at [0x3c+4] %#8.8x
+#
+# Most non-DOS MZ-executable extensions have the relocation table more than 0x40 bytes into the file.
+# http://www.mitec.cz/Downloads/EXE.zip/EXE64.exe e_lfarlc=0x8ead
+# OS/2 ECS\INSTALL\DETECTEI\PCISCAN.EXE e_lfarlc=0x1c
+# some EFI apps Shell_Full.efi ext4_x64_signed.efi e_lfarlc=0
+# Icon library WORD60.ICL e_lfarlc=0
+# Microsoft compiled help format 2.0 WINWORD.DEV.HXS e_lfarlc=0
+>0x18 uleshort <0x40
+# check magic of new second header
+# NE executable with low e_lfarlc like: WORD60.ICL
+# ICL: Icons Library 16-bit http://fileformats.archiveteam.org/wiki/Icon_library
+>>(0x3c.l) string NE Windows Icons Library 16-bit
+!:mime image/x-ms-icl
+!:ext icl
+# handle LX executable with low e_lfarlc like: PCISCAN.EXE
+>>(0x3c.l) string LX
+>>>(0x3c.l) use lx-executable
+# skip Portable Executable (PE) with low e_lfarlc here, because handled later
+# like: ext4_x64_signed.efi Shell_Full.efi WINWORD.DEV.HXS
+>>(0x3c.l) string PE
+# not New Executable (NE) and not PE with low e_lfarlc like:
+# MACCNV55.EXE WORK_RTF.EXE TELE200.EXE NDD.EXE iflash.exe
+>>(0x3c.l) default x MS-DOS executable, MZ for MS-DOS
+!:mime application/x-dosexec
+# Windows and later versions of DOS will allow .EXEs to be named with a .COM
+# extension, mostly for compatibility's sake.
+# like: EDIT.COM 4DOS.COM CMD8086.COM CMD-FR.COM SYSLINUX.COM
+# URL: https://en.wikipedia.org/wiki/Personal_NetWare#VLM
+# Reference: https://mark0.net/download/triddefs_xml.7z/defs/e/exe-vlm-msg.trid.xml
+# also like: BGISRV.DRV
+!:ext exe/com/vlm/drv
+# These traditional tests usually work but not always. When test quality support is
+# implemented these can be turned on.
+#>>0x18 leshort 0x1c (Borland compiler)
+#>>0x18 leshort 0x1e (MS compiler)
+
+# Maybe it's a PE?
+# URL: http://fileformats.archiveteam.org/wiki/Portable_Executable
+# Reference: https://docs.microsoft.com/de-de/windows/win32/debug/pe-format
+>(0x3c.l) string PE\0\0 PE
+!:mime application/vnd.microsoft.portable-executable
+# https://docs.microsoft.com/de-de/windows/win32/debug/pe-format#characteristics
+# DLL Characteristics
+#>>(0x3c.l+22) uleshort x \b, CHARACTERISTICS %#4.4x,
+# 0x0200~IMAGE_FILE_DEBUG_STRIPPED Debugging information is removed from the image file
+# 0x1000~IMAGE_FILE_SYSTEM The image file is a system file, not a user program.
+# 0x2000~IMAGE_FILE_DLL The image file is a dynamic-link library (DLL)
+>>(0x3c.l+24) leshort 0x010b \b32 executable
+# https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#windows-subsystem
+#>>>(0x3c.l+92) leshort x \b, SUBSYSTEM %u
+>>(0x3c.l+24) leshort 0x020b \b32+ executable
+#>>>(0x3c.l+92) leshort x \b, SUBSYSTEM %u
+>>(0x3c.l+24) leshort 0x0107 ROM image
+>>(0x3c.l+24) default x Unknown PE signature
+>>>&0 leshort x %#x
+>>(0x3c.l+22) leshort&0x2000 >0 (DLL)
+# 0~IMAGE_SUBSYSTEM_UNKNOWN An unknown subsystem
+>>(0x3c.l+92) leshort 0 (
+# Summary: Microsoft compiled help *.HXS format 2.0
+# URL: https://en.wikipedia.org/wiki/Microsoft_Help_2
+# Reference: http://www.russotto.net/chm/itolitlsformat.html
+# https://mark0.net/download/triddefs_xml.7z/defs/h/hxs.trid.xml
+# Note: 2 PE sections (.rsrc, .its) implies Microsoft compiled help format; the .its section contains the help content ITOLITLS
+# verified by command like `pelook.exe -d WINWORD.HXS & pelook.exe -h WINWORD.HXS`
+>>>(0x3c.l+6) uleshort =2 \bMicrosoft compiled help format 2.0)
+!:ext hxs
+# 3 PE sections (.text, .reloc, .rsrc) implies some Control Panel Item like:
+# CPL: Control Panel item for WINE 1.7.28 https://www.winehq.org/
+>>>(0x3c.l+6) uleshort !2 \bControl Panel Item)
+!:ext cpl
+# 1~IMAGE_SUBSYSTEM_NATIVE device drivers and native Windows processes
+>>(0x3c.l+92) leshort 1
+# Native PEs include ntoskrnl.exe, hal.dll, smss.exe, autochk.exe, and all the
+# drivers in Windows/System32/drivers/*.sys.
+>>>(0x3c.l+22) leshort&0x2000 >0 (native)
+!:ext dll/sys
+>>>(0x3c.l+22) leshort&0x2000 0 (native)
+!:ext exe/sys
+# 2~IMAGE_SUBSYSTEM_WINDOWS_GUI The Windows graphical user interface (GUI) subsystem
+>>(0x3c.l+92) leshort 2
+>>>(0x3c.l+22) leshort&0x2000 >0 (GUI)
+# These could probably be at least partially distinguished from one another by
+# looking for specific exported functions.
+# CPL: Control Panel item
+# TLB: Type library
+# OCX: OLE/ActiveX control
+# ACM: Audio compression manager codec
+# AX: DirectShow source filter
+# IME: Input method editor
+!:ext dll/cpl/tlb/ocx/acm/ax/ime
+>>>(0x3c.l+22) leshort&0x2000 0 (GUI)
+# Screen savers typically include code from the scrnsave.lib static library, but
+# that's not guaranteed.
+!:ext exe/scr
+# 3~IMAGE_SUBSYSTEM_WINDOWS_CUI The Windows character subsystem
+>>(0x3c.l+92) leshort 3
+>>>(0x3c.l+22) leshort&0x2000 >0 (console)
+!:ext dll/cpl/tlb/ocx/acm/ax/ime
+>>>(0x3c.l+22) leshort&0x2000 0 (console)
+!:ext exe/com
+# NO Windows Subsystem number 4!
+>>(0x3c.l+92) leshort 4 (Unknown subsystem 4)
+# 5~IMAGE_SUBSYSTEM_OS2_CUI The OS/2 character subsystem
+>>(0x3c.l+92) leshort 5 (OS/2)
+# GRR: No examples found by Joerg Jenderek
+#!:ext foo-exe-os2
+# NO Windows Subsystem number 6!
+>>(0x3c.l+92) leshort 6 (Unknown subsystem 6)
+# 7~IMAGE_SUBSYSTEM_POSIX_CUI The Posix character subsystem
+>>(0x3c.l+92) leshort 7 (POSIX
+>>>(0x3c.l+22) leshort&0x2000 >0 \b)
+# like: PSXDLL.DLL
+!:ext dll
+>>>(0x3c.l+22) leshort&0x2000 0 \b)
+# like: PAX.EXE
+!:ext exe
+# 8~IMAGE_SUBSYSTEM_NATIVE_WINDOWS Native Win9x driver
+>>(0x3c.l+92) leshort 8 (Win9x)
+# GRR: No examples found by Joerg Jenderek
+#!:ext foo-exe-win98
+# 9~IMAGE_SUBSYSTEM_WINDOWS_CE_GUI Windows CE
+>>(0x3c.l+92) leshort 9 (Windows CE
+>>>(0x3c.l+22) leshort&0x2000 >0 \b)
+# like: MCS9900Ce50.dll Mosiisr99x.dll TMCGPS.DLL
+!:ext dll
+>>>(0x3c.l+22) leshort&0x2000 0 \b)
+# like: NNGStart.exe navigator.exe
+!:ext exe
+# 10~IMAGE_SUBSYSTEM_EFI_APPLICATION An Extensible Firmware Interface (EFI) application
+>>(0x3c.l+92) leshort 10 (EFI application)
+# like: bootmgfw.efi grub.efi gdisk_x64.efi Shell_Full.efi shim.efi syslinux.efi
+!:ext efi
+# 11~IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER An EFI driver with boot services
+>>(0x3c.l+92) leshort 11 (EFI boot service driver)
+# like: ext2_x64_signed.efi Fat_x64.efi iso9660_x64_signed.efi
+!:ext efi
+>>(0x3c.l+92) leshort 12 (EFI runtime driver)
+# no sample found
+!:ext efi
+# 13~IMAGE_SUBSYSTEM_EFI_ROM An EFI ROM image
+>>(0x3c.l+92) leshort 13 (EFI ROM)
+# no sample found
+!:ext efi
+# 14~IMAGE_SUBSYSTEM_XBOX XBOX
+>>(0x3c.l+92) leshort 14 (XBOX)
+#!:ext foo-xbox
+# NO Windows Subsystem number 15!
+>>(0x3c.l+92) leshort 15 (Unknown subsystem 15)
+# 16~IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION Windows boot application
+>>(0x3c.l+92) leshort 16 (Windows boot application
+>>>(0x3c.l+22) leshort&0x2000 >0 \b)
+# like: bootvhd.dll bootuwf.dll hvloader.dll tcbloader.dll bootspaces.dll
+!:ext dll
+>>>(0x3c.l+22) leshort&0x2000 0 \b)
+# like: bootmgr.efi memtest.efi shellx64.efi memtest.exe winload.exe winresume.exe bootvhd.dll hvloader.dll
+!:ext efi/exe
+# GRR: the next 2 lines are not executed!
+#>>(0x3c.l+92) default x (Unknown subsystem
+#>>>&0 leshort x %#x)
+>>(0x3c.l+92) leshort >16 (Unknown subsystem
+>>>&0 leshort x %#x)
+>>(0x3c.l+4) leshort 0x14c Intel 80386
+>>(0x3c.l+4) leshort 0x166 MIPS R4000
+>>(0x3c.l+4) leshort 0x168 MIPS R10000
+>>(0x3c.l+4) leshort 0x184 Alpha
+>>(0x3c.l+4) leshort 0x1a2 Hitachi SH3
+>>(0x3c.l+4) leshort 0x1a3 Hitachi SH3 DSP
+>>(0x3c.l+4) leshort 0x1a8 Hitachi SH5
+>>(0x3c.l+4) leshort 0x169 MIPS WCE v2
+>>(0x3c.l+4) leshort 0x1a6 Hitachi SH4
+>>(0x3c.l+4) leshort 0x1c0 ARM
+>>(0x3c.l+4) leshort 0x1c2 ARM Thumb
+>>(0x3c.l+4) leshort 0x1c4 ARMv7 Thumb
+>>(0x3c.l+4) leshort 0x1d3 Matsushita AM33
+>>(0x3c.l+4) leshort 0x1f0 PowerPC
+>>(0x3c.l+4) leshort 0x1f1 PowerPC with FPU
+>>(0x3c.l+4) leshort 0x1f2 PowerPC (big-endian)
+>>(0x3c.l+4) leshort 0x200 Intel Itanium
+>>(0x3c.l+4) leshort 0x266 MIPS16
+>>(0x3c.l+4) leshort 0x268 Motorola 68000
+>>(0x3c.l+4) leshort 0x290 PA-RISC
+>>(0x3c.l+4) leshort 0x366 MIPSIV
+>>(0x3c.l+4) leshort 0x466 MIPS16 with FPU
+>>(0x3c.l+4) leshort 0xebc EFI byte code
+>>(0x3c.l+4) leshort 0x5032 RISC-V 32-bit
+>>(0x3c.l+4) leshort 0x5064 RISC-V 64-bit
+>>(0x3c.l+4) leshort 0x5128 RISC-V 128-bit
+>>(0x3c.l+4) leshort 0x6232 LoongArch 32-bit
+>>(0x3c.l+4) leshort 0x6264 LoongArch 64-bit
+>>(0x3c.l+4) leshort 0x9041 Mitsubishi M32R
+>>(0x3c.l+4) leshort 0x8664 x86-64
+>>(0x3c.l+4) leshort 0xaa64 Aarch64
+>>(0x3c.l+4) leshort 0xc0ee MSIL
+# GRR: the next 2 lines are not executed!
+>>(0x3c.l+4) default x Unknown processor type
+>>>&0 leshort x %#x
+>>(0x3c.l+22) leshort&0x0200 >0 (stripped to external PDB)
+>>(0x3c.l+22) leshort&0x1000 >0 system file
+>>(0x3c.l+24) leshort 0x010b
+>>>(0x3c.l+232) lelong >0 Mono/.Net assembly
+>>(0x3c.l+24) leshort 0x020b
+>>>(0x3c.l+248) lelong >0 Mono/.Net assembly
+
+# hooray, there's a DOS extender using the PE format, with a valid PE
+# executable inside (which just prints a message and exits if run in win)
+>>(8.s*16) string 32STUB \b, 32rtm DOS extender
+>>(8.s*16) string !32STUB \b, for MS Windows
+>>(0x3c.l+0xf8) string UPX0 \b, UPX compressed
+>>(0x3c.l+0xf8) search/0x140 PEC2 \b, PECompact2 compressed
+>>(0x3c.l+0xf8) search/0x140 UPX2
+>>>(&0x10.l+(-4)) string PK\3\4 \b, ZIP self-extracting archive (Info-Zip)
+>>(0x3c.l+0xf8) search/0x140 .idata
+>>>(&0xe.l+(-4)) string PK\3\4 \b, ZIP self-extracting archive (Info-Zip)
+>>>(&0xe.l+(-4)) string ZZ0 \b, ZZip self-extracting archive
+>>>(&0xe.l+(-4)) string ZZ1 \b, ZZip self-extracting archive
+>>(0x3c.l+0xf8) search/0x140 .rsrc
+>>>(&0x0f.l+(-4)) string a\\\4\5 \b, WinHKI self-extracting archive
+>>>(&0x0f.l+(-4)) string Rar! \b, RAR self-extracting archive
+>>>(&0x0f.l+(-4)) search/0x3000 MSCF \b, InstallShield self-extracting archive
+>>>(&0x0f.l+(-4)) search/32 Nullsoft \b, Nullsoft Installer self-extracting archive
+>>(0x3c.l+0xf8) search/0x140 .data
+>>>(&0x0f.l) string WEXTRACT \b, MS CAB-Installer self-extracting archive
+>>(0x3c.l+0xf8) search/0x140 .petite\0 \b, Petite compressed
+>>>(0x3c.l+0xf7) byte x
+>>>>(&0x104.l+(-4)) string =!sfx! \b, ACE self-extracting archive
+>>(0x3c.l+0xf8) search/0x140 .WISE \b, WISE installer self-extracting archive
+>>(0x3c.l+0xf8) search/0x140 .dz\0\0\0 \b, Dzip self-extracting archive
+>>&(0x3c.l+0xf8) search/0x100 _winzip_ \b, ZIP self-extracting archive (WinZip)
+>>&(0x3c.l+0xf8) search/0x100 SharedD \b, Microsoft Installer self-extracting archive
+>>0x30 string Inno \b, InnoSetup self-extracting archive
+# NumberOfSections; Normal Dynamic Link libraries have a few sections for code, data and resource etc.
+# PE used as container have less sections
+>>(0x3c.l+6) leshort >1 \b, %u sections
+# do not display for 1 section to get output like in version 5.43 and to keep output columns low
+#>>(0x3c.l+6) leshort =1 \b, %u section
+
+# If the relocation table is 0x40 or more bytes into the file, it's definitely
+# not a DOS EXE.
+>0x18 uleshort >0x3f
+
+# Hmm, not a PE but the relocation table is too high for a traditional DOS exe,
+# must be one of the unusual subformats.
+>>(0x3c.l) string !PE\0\0 MS-DOS executable
+#!:mime application/x-dosexec
+
+>>(0x3c.l) string NE \b, NE
+#!:mime application/x-dosexec
+!:mime application/x-ms-ne-executable
+# FOR DEBUGGING!
+# Reference: https://wiki.osdev.org/NE
+# ProgFlags; Program flags, bitmapped
+#>>>(0x3c.l+0x0C) ubyte x \b, ProgFlags 0x%2.2x
+# >>>(0x3c.l+0x0c) ubyte&0x03 =0 \b, none
+# >>>(0x3c.l+0x0c) ubyte&0x03 =1 \b, single shared
+# >>>(0x3c.l+0x0c) ubyte&0x03 =2 \b, multiple
+# >>>(0x3c.l+0x0c) ubyte&0x03 =3 \b, (null)
+# >>>(0x3c.l+0x0c) ubyte &0x04 \b, Global initialization
+# >>>(0x3c.l+0x0c) ubyte &0x08 \b, Protected mode only
+# >>>(0x3c.l+0x0c) ubyte &0x10 \b, 8086 instructions
+# >>>(0x3c.l+0x0c) ubyte &0x20 \b, 80286 instructions
+# >>>(0x3c.l+0x0c) ubyte &0x40 \b, 80386 instructions
+# >>>(0x3c.l+0x0c) ubyte &0x80 \b, 80x87 instructions
+# ApplFlags; Application flags, bitmapped
+# https://www.fileformat.info/format/exe/corion-ne.htm
+#>>>(0x3c.l+0x0D) ubyte x \b, ApplFlags 0x%2.2x
+# Application type (bits 0-2); 1~Full screen (not aware of Windows/P.M. API)
+# 2~Compatible with Windows/P.M. API 3~Uses Windows/P.M. API
+#>>>(0x3c.l+0x0D) ubyte&0x07 =1 \b, Full screen
+#>>>(0x3c.l+0x0D) ubyte&0x07 =2 \b, Compatible with Windows/P.M. API
+#>>>(0x3c.l+0x0D) ubyte&0x07 =3 \b, use Windows/P.M. API
+# bit 7; DLL or driver (SS:SP info invalid, CS:IP points at FAR init routine called with AX handle
+#>>>(0x3c.l+0x0D) ubyte &0x80 \b, DLL or driver
+# AutoDataSegIndex; automatic data segment index like: 0 2 3 22
+# zero if the SINGLEDATA and MULTIPLEDATA bits are cleared
+#>>>(0x3c.l+0x0e) uleshort x \b, AutoDataSegIndex %u
+# InitHeapSize; intial local heap size like; 0 400h 1400h
+# zero if there is no local allocation
+#>>>(0x3c.l+0x10) uleshort !0 \b, InitHeapSize 0x%x
+# InitStackSize; inital stack size like: 0 10h A00h 7D0h A8Ch FA0h 1000h 1388h
+# 1400h (CBT) 1800h 2000h 2800h 2EE0h 2F3Ch 3258h 3E80h 4000h 4E20h 5000h 6000h
+# 6D60h 8000h 40000h
+# zero if the SS register value does not equal the DS register value
+#>>>(0x3c.l+0x12) uleshort !0 \b, InitStackSize 0x%x
+# EntryPoint; segment offset value of CS:IP like: 0 10000h 18A84h 11C1Ah 307F1h
+#>>>(0x3c.l+0x14) ulelong !0 \b, EntryPoint 0x%x
+# InitStack; specifies the segment offset value of stack pointer SS:SP
+# like: 0 20000h 160000h
+#>>>(0x3c.l+0x18) ulelong !0 \b, InitStack 0x%x
+# SegCount; number of segments in segment table like: 0 1 2 3 16h
+#>>>(0x3c.l+0x1C) uleshort x \b, SegCount 0x%x
+# ModRefs; number of module references (DLLs) like; 0 1 3
+#>>>(0x3c.l+0x1E) uleshort !0 \b, ModRefs %u
+# NoResNamesTabSiz; size in bytes of non-resident names table
+# like: Bh 16h B4h B9h 2Ch 18Fh 16AAh
+#>>>(0x3c.l+0x20) uleshort x \b, NoResNamesTabSiz 0x%x
+# SegTableOffset; offset of Segment table like: 40h
+#>>>(0x3c.l+0x22) uleshort !0x40 \b, SegTableOffset 0x%x
+# ResTableOffset; offset of resources table like: 40h 50h 58h F0h
+# 40h for most fonts likedos737.fon FMFONT.FOT but 60h for L1WBASE.FON
+#>>>(0x3c.l+0x24) uleshort x \b, ResTableOffset 0x%x
+# ResidNamTable; offset of resident names table
+# like: 58h 5Ch 60h 68h 74h 98h 2E3h 2E7h 2F0h
+#>>>(0x3c.l+0x26) uleshort x \b, ResidNamTable 0x%x
+# ImportNameTable; offset of imported names table (array of counted strings, terminated with string of length 00h)
+# like: 77h 7Eh 80h C6h A7h ACh 2F8h 3FFh
+#>>>(0x3c.l+0x2a) uleshort x \b, ImportNameTable 0x%x
+# OffStartNonResTab; offset from start of file to non-resident names table
+# like: 110h 11Dh 19Bh 1A5h 3F5h 4C8h 4EEh D93h
+#>>>(0x3c.l+0x2c) ulelong x \b, OffStartNonResTab 0x%x
+# MovEntryCount; number of movable entry points like: 0 4 5 6 16 17 24 312 355 446
+#>>>(0x3c.l+0x30) uleshort !0 \b, MovEntryCount %u
+# FileAlnSzShftCnt; log2 of the segment sector size; 4~16 0~9~512 (default)
+#>>>(0x3c.l+0x32) uleshort !9 \b, FileAlnSzShftCnt %u
+# nResTabEntries; number of resource table entries like: 0 2
+#>>>(0x3c.l+0x34) uleshort !0 \b, nResTabEntries %u
+# targOS; Target OS; 0~unknown~OS/2 1.0 or MS Windows 1-2
+# OS/2 1.0 like: DTM.DLL SHELL11F.EXE HELPMSG.EXE CREATEDD.EXE
+# or Windows 1.03 - 2.1 like: MSDOSD.EXE KARTEI.EXE KALENDER.EXE
+#>>>(0x3c.l+0x36) byte x TARGOS %x
+>>>(0x3c.l+0x36) byte 0 for OS/2 1.0 or MS Windows 1-2
+>>>(0x3c.l+0x36) byte 1 for OS/2 1.x
+>>>(0x3c.l+0x36) byte 2 for MS Windows 3.x
+>>>(0x3c.l+0x36) byte 3 for MS-DOS
+>>>(0x3c.l+0x36) byte 4 for Windows 386
+>>>(0x3c.l+0x36) byte 5 for Borland Operating System Services
+# http://downloads.sourceforge.net/dfendreloaded/D-Fend-Reloaded-1.4.4.zip
+# D-Fend Reloaded/VirtualHD/FREEDOS/DPMILD32.EXE
+# GRR: WHAT OS is this?
+#>>>(0x3c.l+0x36) byte 6 for TARGET SIX
+# https://en.wikipedia.org/wiki/Phar_Lap_(company)
+>>>(0x3c.l+0x36) byte 0x81 for MS-DOS, Phar Lap DOS extender, OS/2
+# like: CVP7.EXE
+>>>(0x3c.l+0x36) byte 0x82 for MS-DOS, Phar Lap DOS extender, Windows
+>>>(0x3c.l+0x36) default x
+>>>>(0x3c.l+0x36) ubyte x (unknown OS %#x)
+# expctwinver; expected Windows version (minor first) like:
+# 0.0~DTM.DLL 203.4~Windows 1.03 GDI.EXE 2.1~TTY.DRV 3.0~dos737.fon FMFONT.FOT THREED.VBX 3.10~GDI.EXE 4.0~(ME) VGAFULL.3GR
+>>>(0x3c.l+0x3F) ubyte x (%u
+>>>(0x3c.l+0x3E) ubyte x \b.%u)
+# OS2EXEFlags; other EXE flags
+# 0~Long filename support 1~2.x protected mode 4~2.x proportional fonts 8~Executable has gangload area
+#>>>(0x3c.l+0x37) byte !0 \b, OS2EXEFlags 0x%x
+# retThunkOffset; offset to return thunks or start of gangload area like: 0 34h 58h 246h
+#>>>(0x3c.l+0x38) uleshort !0 \b, retThunkOffset 0x%x
+# segrefthunksoff; offset to segment reference thunks or size of gangload area
+# like: 0 33Eh 39Ah AEEh
+#>>>(0x3c.l+0x3A) uleshort !0 \b, segrefthunksoff 0x%x
+# mincodeswap; minimum code swap area size like 0 620Ch
+#>>>(0x3c.l+0x3C) uleshort !0 \b, mincodeswap 0x%x
+>>>(0x3c.l+0x0c) leshort&0x8000 0x8000 (DLL or font)
+# DRV: Driver
+# 3GR: Grabber device driver
+# CPL: Control Panel Item
+# VBX: Visual Basic Extension https://en.wikipedia.org/wiki/Visual_Basic
+# FON: Bitmap font http://fileformats.archiveteam.org/wiki/FON
+# FOT: Font resource file
+# EXE: WINSPOOL.EXE USER.EXE krnl386.exe GDI.EXE
+# CNV: Microsoft Word text conversion https://www.file-extensions.org/cnv-file-extension-microsoft-word-text-conversion-data
+!:ext dll/drv/3gr/cpl/vbx/fon/fot
+>>>(0x3c.l+0x0c) leshort&0x8000 0 (EXE)
+!:ext exe/scr
+>>>&(&0x24.s-1) string ARJSFX \b, ARJ self-extracting archive
+>>>(0x3c.l+0x70) search/0x80 WinZip(R)\ Self-Extractor \b, ZIP self-extracting archive (WinZip)
+
+>>(0x3c.l) string LX\0\0 \b, LX
+!:mime application/x-dosexec
+>>>(0x3c.l+0x0a) leshort <1 (unknown OS)
+>>>(0x3c.l+0x0a) leshort 1 for OS/2
+>>>(0x3c.l+0x0a) leshort 2 for MS Windows
+>>>(0x3c.l+0x0a) leshort 3 for DOS
+>>>(0x3c.l+0x0a) leshort >3 (unknown OS)
+>>>(0x3c.l+0x10) lelong&0x28000 =0x8000 (DLL)
+>>>(0x3c.l+0x10) lelong&0x20000 >0 (device driver)
+>>>(0x3c.l+0x10) lelong&0x300 0x300 (GUI)
+>>>(0x3c.l+0x10) lelong&0x28300 <0x300 (console)
+>>>(0x3c.l+0x08) leshort 1 i80286
+>>>(0x3c.l+0x08) leshort 2 i80386
+>>>(0x3c.l+0x08) leshort 3 i80486
+>>>(8.s*16) string emx \b, emx
+>>>>&1 string x %s
+>>>&(&0x54.l-3) string arjsfx \b, ARJ self-extracting archive
+
+# MS Windows system file, supposedly a collection of LE executables
+# like vmm32.vxd WIN386.EXE
+>>(0x3c.l) string W3 \b, W3 for MS Windows
+#!:mime application/x-dosexec
+!:mime application/x-ms-w3-executable
+!:ext vxd/exe
+# W4 executable
+>>(0x3c.l) string W4 \b, W4 for MS Windows
+#!:mime application/x-dosexec
+!:mime application/x-ms-w4-executable
+# windows 98 VMM32.VXD
+!:ext vxd
+
+>>(0x3c.l) string LE\0\0 \b, LE executable
+!:mime application/x-dosexec
+>>>(0x3c.l+0x0a) leshort 1
+# some DOS extenders use LE files with OS/2 header
+>>>>0x240 search/0x100 DOS/4G for MS-DOS, DOS4GW DOS extender
+>>>>0x240 search/0x200 WATCOM\ C/C++ for MS-DOS, DOS4GW DOS extender
+>>>>0x440 search/0x100 CauseWay\ DOS\ Extender for MS-DOS, CauseWay DOS extender
+>>>>0x40 search/0x40 PMODE/W for MS-DOS, PMODE/W DOS extender
+>>>>0x40 search/0x40 STUB/32A for MS-DOS, DOS/32A DOS extender (stub)
+>>>>0x40 search/0x80 STUB/32C for MS-DOS, DOS/32A DOS extender (configurable stub)
+>>>>0x40 search/0x80 DOS/32A for MS-DOS, DOS/32A DOS extender (embedded)
+# this is a wild guess; hopefully it is a specific signature
+>>>>&0x24 lelong <0x50
+>>>>>(&0x4c.l) string \xfc\xb8WATCOM
+>>>>>>&0 search/8 3\xdbf\xb9 \b, 32Lite compressed
+# another wild guess: if real OS/2 LE executables exist, they probably have higher start EIP
+#>>>>(0x3c.l+0x1c) lelong >0x10000 for OS/2
+# fails with DOS-Extenders.
+>>>(0x3c.l+0x0a) leshort 2 for MS Windows
+>>>(0x3c.l+0x0a) leshort 3 for DOS
+>>>(0x3c.l+0x0a) leshort 4 for MS Windows (VxD)
+# VXD: VxD for Windows 95/98/Me
+# 386: VxD for Windows 2.10, 3.0, 3.1x
+# PDR: Port driver
+# MPD: Miniport driver (?)
+!:ext vxd/386/pdr/mpd
+>>>(&0x7c.l+0x26) string UPX \b, UPX compressed
+>>>&(&0x54.l-3) string UNACE \b, ACE self-extracting archive
+
+# looks like ASCII, probably some embedded copyright message.
+# and definitely not NE/LE/LX/PE
+>>0x3c lelong >0x20000000
+>>>(4.s*512) leshort !0x014c \b, MZ for MS-DOS
+!:mime application/x-dosexec
+!:ext exe/com
+# header data too small for extended executable
+>2 long !0
+>>0x18 uleshort <0x40
+>>>(4.s*512) leshort !0x014c
+
+>>>>&(2.s-514) string !LE
+>>>>>&-2 string !BW
+#>>>>>>(0x3c.l) string x \b, 2ND MAGIC %.2s
+# but some LX executable appear here also like: PCISCAN.EXE
+>>>>>>(0x3c.l) string !LX
+# because Portable Executable (PE) already done skip many here like:
+# xcopy32.exe stinger64.exe WimUtil.exe
+# NO such DOS examples found and
+# DOS examples seems to be already handled by e_lfarlc <0x40 like: CMD8086.COM CMD-FR.COM
+>>>>>>>(0x3c.l) string !PE \b, MZ for MS-DOS
+!:mime application/x-dosexec
+>>>>&(2.s-514) string LE \b, LE
+>>>>>0x240 search/0x100 DOS/4G for MS-DOS, DOS4GW DOS extender
+# educated guess since indirection is still not capable enough for complex offset
+# calculations (next embedded executable would be at &(&2*512+&0-2)
+# I suspect there are only LE executables in these multi-exe files
+>>>>&(2.s-514) string BW
+>>>>>0x240 search/0x100 DOS/4G \b, LE for MS-DOS, DOS4GW DOS extender (embedded)
+>>>>>0x240 search/0x100 !DOS/4G \b, BW collection for MS-DOS
+
+# This sequence skips to the first COFF segment, usually .text
+>(4.s*512) leshort 0x014c \b, COFF
+!:mime application/x-dosexec
+>>(8.s*16) string go32stub for MS-DOS, DJGPP go32 DOS extender
+>>(8.s*16) string emx
+>>>&1 string x for DOS, Win or OS/2, emx %s
+>>&(&0x42.l-3) byte x
+>>>&0x26 string UPX \b, UPX compressed
+# and yet another guess: small .text, and after large .data is unusual, could be 32lite
+>>&0x2c search/0xa0 .text
+>>>&0x0b lelong <0x2000
+>>>>&0 lelong >0x6000 \b, 32lite compressed
+
+>(8.s*16) string $WdX \b, WDos/X DOS extender
+
+# By now an executable type should have been printed out. The executable
+# may be a self-uncompressing archive, so look for evidence of that and
+# print it out.
+#
+# Some signatures below from Greg Roelofs, newt@uchicago.edu.
+#
+>0x35 string \x8e\xc0\xb9\x08\x00\xf3\xa5\x4a\x75\xeb\x8e\xc3\x8e\xd8\x33\xff\xbe\x30\x00\x05 \b, aPack compressed
+>0xe7 string LH/2\ Self-Extract \b, %s
+>0x1c string UC2X \b, UCEXE compressed
+>0x1c string WWP\ \b, WWPACK compressed
+>0x1c string RJSX \b, ARJ self-extracting archive
+>0x1c string diet \b, diet compressed
+>0x1c string LZ09 \b, LZEXE v0.90 compressed
+>0x1c string LZ91 \b, LZEXE v0.91 compressed
+>0x1c string tz \b, TinyProg compressed
+>0x1e string Copyright\ 1989-1990\ PKWARE\ Inc. Self-extracting PKZIP archive
+!:mime application/zip
+# Yes, this really is "Copr", not "Corp."
+>0x1e string PKLITE\ Copr. Self-extracting PKZIP archive
+!:mime application/zip
+# winarj stores a message in the stub instead of the sig in the MZ header
+>0x20 search/0xe0 aRJsfX \b, ARJ self-extracting archive
+>0x20 string AIN
+>>0x23 string 2 \b, AIN 2.x compressed
+>>0x23 string <2 \b, AIN 1.x compressed
+>>0x23 string >2 \b, AIN 1.x compressed
+>0x24 string LHa's\ SFX \b, LHa self-extracting archive
+!:mime application/x-lha
+>0x24 string LHA's\ SFX \b, LHa self-extracting archive
+!:mime application/x-lha
+>0x24 string \ $ARX \b, ARX self-extracting archive
+>0x24 string \ $LHarc \b, LHarc self-extracting archive
+>0x20 string SFX\ by\ LARC \b, LARC self-extracting archive
+>0x40 string aPKG \b, aPackage self-extracting archive
+>0x64 string W\ Collis\0\0 \b, Compack compressed
+>0x7a string Windows\ self-extracting\ ZIP \b, ZIP self-extracting archive
+>>&0xf4 search/0x140 \x0\x40\x1\x0
+>>>(&0.l+(4)) string MSCF \b, WinHKI CAB self-extracting archive
+>1638 string -lh5- \b, LHa self-extracting archive v2.13S
+>0x17888 string Rar! \b, RAR self-extracting archive
+
+# Skip to the end of the EXE. This will usually work fine in the PE case
+# because the MZ image is hardcoded into the toolchain and almost certainly
+# won't match any of these signatures.
+>(4.s*512) long x
+>>&(2.s-517) byte x
+>>>&0 string PK\3\4 \b, ZIP self-extracting archive
+>>>&0 string Rar! \b, RAR self-extracting archive
+>>>&0 string =!\x11 \b, AIN 2.x self-extracting archive
+>>>&0 string =!\x12 \b, AIN 2.x self-extracting archive
+>>>&0 string =!\x17 \b, AIN 1.x self-extracting archive
+>>>&0 string =!\x18 \b, AIN 1.x self-extracting archive
+>>>&7 search/400 **ACE** \b, ACE self-extracting archive
+>>>&0 search/0x480 UC2SFX\ Header \b, UC2 self-extracting archive
+
+# a few unknown ZIP sfxes, no idea if they are needed or if they are
+# already captured by the generic patterns above
+>(8.s*16) search/0x20 PKSFX \b, ZIP self-extracting archive (PKZIP)
+# TODO: how to add this? >FileSize-34 string Windows\ Self-Installing\ Executable \b, ZIP self-extracting archive
+#
+
+# TELVOX Teleinformatica CODEC self-extractor for OS/2:
+>49801 string \x79\xff\x80\xff\x76\xff \b, CODEC archive v3.21
+>>49824 leshort =1 \b, 1 file
+>>49824 leshort >1 \b, %u files
+
+# Summary: OS/2 LX Library and device driver (no DOS stub)
+# From: Joerg Jenderek
+# URL: http://en.wikipedia.org/wiki/EXE
+# Reference: http://www.textfiles.com/programming/FORMATS/lxexe.txt
+# https://github.com/open-watcom/open-watcom-v2/blob/master/bld/watcom/h/exeflat.h
+# Note: by dll-os2-no-dos-stub.trid.xml called "OS/2 Dynamic Link Library (no DOS stub)"
+# TODO: unify with DOS stub variant (MZ magic)
+0 string/b LX
+>2 ushort =0
+>>0 use lx-executable
+# no examples found for big endian variant
+>2 ushort =0x0101
+>>0 use \^lx-executable
+0 name lx-executable
+# similar looking like variant with MS-DOS stub (MZ magic): "MS-DOS executable, LX"
+#>0x00 uleshort x executable,
+# signature OSF_FLAT_LX_SIGNATURE~0x584C~LX OSF_FLAT_SIGNATURE~0x454C~LE
+>0x00 uleshort =0x584c LX
+>0x00 uleshort =0x454C LE
+>0x00 uleshort x executable
+#!:mime application/x-msdownload
+!:mime application/x-lx-executable
+!:ext exe
+# byte order: 00h~little-endian non-zero=1~big-endian
+#>0x02 ubyte =0 (little-endian)
+>0x02 ubyte !0 (big-endian)
+# FOR DEBUGGING!
+# word order: 00h~little-endian non-zero=1~big-endian
+#>0x03 ubyte =0 \b, little-endian word order
+#>0x03 ubyte !0 \b, big-endian word order
+# cpu_type; CPU type like: 1~286 2~386 3~486 4 20h~i860 21h~Intel N11 40h~MIPS R2000,R3000 41h~MIPS R6000 42h~MIPS R4000
+#>0x08 uleshort x \b, CPU %u
+# os_type; target operating system like: 0~unknown 1~OS/2 2~Windows 3~DOS 4.x 4~Windows 386
+#>0x0A leshort x \b, OS %u
+# flags; module type flags
+#>0x10 ulelong x \b, FLAGS %#8.8x
+# 00000002h ~Reserved for system use
+#>0x10 ulelong &0x00000002 \b, 2h reserved
+# OSF_INIT_INSTANCE=00000004h ~Per-Process Library Initialization; setting this bit for EXE file is invalid
+#>0x10 ulelong &0x00000004 \b, per-process library Initialization
+# OSF_INTERNAL_FIXUPS_DONE=00000010h ~Internal fixups for the module have been applied
+#>0x10 ulelong &0x00000010 \b, int. fixup
+# OSF_EXTERNAL_FIXUPS_DONE=00000020h ~External fixups for the module have been applied
+#>0x10 ulelong &0x00000020 \b, ext. fixup
+# OSF_NOT_PM_COMPATIBLE=00000100h ~Incompatible with PM windowing
+#>0x10 ulelong&0x00000100 =0x00000100 \b, incompatible with PM windowing
+# OSF_PM_COMPATIBLE=00000200h ~Compatible with PM windowing
+#>0x10 ulelong&0x00000200 =0x00000200 \b, compatible with PM windowing
+# bit 17; device driver
+#>0x10 ulelong&0x00020000 >0 \b, device driver
+# Per-process Library Termination; setting this bit for EXE file is invalid
+#>0x10 ulelong&0x40000000 =0x40000000 \b, per-process library termination
+>0x0a leshort 1 for OS/2
+# no example found
+>0x0a leshort 3 for DOS
+# http://www.ctyme.com/intr/rb-2939.htm#Table1610
+# library by module type mask 00038000h (bits 15-17);
+# 0h ~executable Program module
+>0x10 ulelong&0x00038000 =0x00000000 (program)
+#!:ext exe
+# OSF_IS_DLL=8000h ~Library module (DLL)
+>0x10 ulelong&0x00038000 >0x00000000
+# OSF_PHYS_DEVICE=00020000h ~device driver
+>>0x10 ulelong&0x00020000 >0 (device driver)
+!:ext sys
+# if not device driver it is library (DLL)
+>>0x10 ulelong&0x00020000 =0 (library)
+!:ext dll
+# bits 8-10; OSF_PM_APP=300h in flags ~Uses PM windowing API; either it is GUI or console
+>0x10 ulelong&0x00000300 =0x00000300 (GUI)
+>0x10 ulelong&0x00000300 !0x00000300 (console)
+# CPU type
+>0x08 uleshort 1 i80286
+# all inspected examples
+>0x08 uleshort 2 i80386
+>0x08 uleshort 3 i80486
+>0x08 uleshort 4 i80586
+# 21h Intel "N11" or compatible
+# 40h MIPS Mark I ( R2000, R3000) or compatible
+# 41h MIPS Mark II ( R6000 ) or compatible
+# 42h MIPS Mark III ( R4000 ) or compatible
+
+# added by Joerg Jenderek of https://www.freedos.org/software/?prog=kc
+# and https://www.freedos.org/software/?prog=kpdos
+# for FreeDOS files like KEYBOARD.SYS, KEYBRD2.SYS, KEYBRD3.SYS, *.KBD
+0 string/b KCF FreeDOS KEYBoard Layout collection
+# only version=0x100 found
+>3 uleshort x \b, version %#x
+# length of string containing author,info and special characters
+>6 ubyte >0
+#>>6 pstring x \b, name=%s
+>>7 string >\0 \b, author=%-.14s
+>>7 search/254 \xff \b, info=
+#>>>&0 string x \b%-s
+>>>&0 string x \b%-.15s
+# for FreeDOS *.KL files
+0 string/b KLF FreeDOS KEYBoard Layout file
+# only version=0x100 or 0x101 found
+>3 uleshort x \b, version %#x
+# stringlength
+>5 ubyte >0
+>>8 string x \b, name=%-.2s
+0 string \xffKEYB\ \ \ \0\0\0\0
+>12 string \0\0\0\0`\004\360 MS-DOS KEYBoard Layout file
+
+# DOS device driver updated by Joerg Jenderek at May 2011,Mar 2017,Aug 2020,Mar 2023
+# URL: http://fileformats.archiveteam.org/wiki/DOS_device_driver
+# Reference: http://www.delorie.com/djgpp/doc/rbinter/it/46/16.html
+# http://www.o3one.org/hwdocs/bios_doc/dosref22.html
+0 ulequad&0x07a0ffffffff 0xffffffff
+# skip OS/2 INI ./os2
+>4 ubelong !0x14000000
+#>>10 ubequad x MAYBE_DRIVER_NAME=%16.16llx
+# https://bugs.astron.com/view.php?id=434
+# skip OOXML document fragment 0000.dat where driver name is "empty" instead of "ASCII like"
+>>10 ubequad !0
+>>>0 use msdos-driver
+0 name msdos-driver DOS executable (
+#!:mime application/octet-stream
+!:mime application/x-dosdriver
+# also found FreeDOS print driver SPOOL.DEV and disc compression driver STACLOAD.BIN
+# and IBM Token-Ring adapter IBMTOK.DOS. Why and when DOS instead SYS is used?
+# PROTMAN.DOS ELNKPL.DOS
+!:ext sys/dev/bin/dos
+# 1 space char after "UPX compressed" to get phrase like "UPX compressed character device"
+>40 search/7 UPX! \bUPX compressed
+# DOS device driver attributes
+>4 uleshort&0x8000 0x0000 \bblock device driver
+# character device
+>4 uleshort&0x8000 0x8000 \b
+# 1 space char after "clock" to get phrase like "clock character device driver CLOCK$"
+>>4 uleshort&0x0008 0x0008 \bclock
+# fast video output by int 29h
+# 1 space char after "fast" to get phrase like "fast standard input/output character device driver"
+>>4 uleshort&0x0010 0x0010 \bfast
+# standard input/output device
+# 1 space char after "standard" to get phrase like "standard input/output character device driver"
+>>4 uleshort&0x0003 >0 \bstandard
+>>>4 uleshort&0x0001 0x0001 \binput
+>>>4 uleshort&0x0003 0x0003 \b/
+# 1 space char after "output" to get phrase like "input/output character device driver"
+>>>4 uleshort&0x0002 0x0002 \boutput
+>>4 uleshort&0x8000 0x8000 \bcharacter device driver
+>0 ubyte x
+# upx compressed device driver has garbage instead of real in name field of header
+>>40 search/7 UPX!
+>>40 default x
+# leading/trailing nulls, zeros or non ASCII characters in 8-byte name field at offset 10 are skipped
+# 1 space char before device driver name to get phrase like "device driver PROTMAN$" "device driver HP-150II" "device driver PC$MOUSE"
+>>>12 ubyte >0x23 \b
+>>>>10 ubyte >0x20
+>>>>>10 ubyte !0x2E
+>>>>>>10 ubyte !0x2A \b%c
+>>>>11 ubyte >0x20
+>>>>>11 ubyte !0x2E \b%c
+>>>>12 ubyte >0x20
+>>>>>12 ubyte !0x39
+>>>>>>12 ubyte !0x2E \b%c
+>>>13 ubyte >0x20
+>>>>13 ubyte !0x2E \b%c
+>>>>14 ubyte >0x20
+>>>>>14 ubyte !0x2E \b%c
+>>>>15 ubyte >0x20
+>>>>>15 ubyte !0x2E \b%c
+>>>>16 ubyte >0x20
+>>>>>16 ubyte !0x2E
+>>>>>>16 ubyte <0xCB \b%c
+>>>>17 ubyte >0x20
+>>>>>17 ubyte !0x2E
+>>>>>>17 ubyte <0x90 \b%c
+# some character device drivers like ASPICD.SYS, btcdrom.sys and Cr_atapi.sys contain only spaces or points in name field
+>>>12 ubyte <0x2F
+# they have their real name at offset 22
+# also block device drivers like DUMBDRV.SYS
+>>>>22 string >\056 %-.6s
+>4 uleshort&0x8000 0x0000
+# 32 bit sector addressing ( > 32 MB) for block devices
+>>4 uleshort&0x0002 0x0002 \b,32-bit sector-
+# support by driver functions 13h, 17h, 18h
+>4 uleshort&0x0040 0x0040 \b,IOCTL-
+# open, close, removable media support by driver functions 0Dh, 0Eh, 0Fh
+>4 uleshort&0x0800 0x0800 \b,close media-
+# output until busy support by int 10h for character device driver
+>4 uleshort&0x8000 0x8000
+>>4 uleshort&0x2000 0x2000 \b,until busy-
+# direct read/write support by driver functions 03h,0Ch
+>4 uleshort&0x4000 0x4000 \b,control strings-
+>4 uleshort&0x8000 0x8000
+>>4 uleshort&0x6840 >0 \bsupport
+>4 uleshort&0x8000 0x0000
+>>4 uleshort&0x4842 >0 \bsupport
+>0 ubyte x \b)
+>0 ulelong !0xffffffff with pointer %#x
+# DOS driver cmd640x.sys has 0x12 instead of 0xffffffff for pointer field to next device header
+0 ulequad 0x0513c00000000012
+>0 use msdos-driver
+# DOS drivers DC2975.SYS, DUMBDRV.SYS, ECHO.SYS has also none 0xffffffff for pointer field
+0 ulequad 0x32f28000ffff0016
+>0 use msdos-driver
+0 ulequad 0x007f00000000ffff
+>0 use msdos-driver
+# https://www.uwe-sieber.de/files/cfg_echo.zip
+0 ulequad 0x001600000000ffff
+>0 use msdos-driver
+# DOS drivers LS120.SYS, MKELS120.SYS use reserved bits of attribute field
+0 ulequad 0x0bf708c2ffffffff
+>0 use msdos-driver
+0 ulequad 0x07bd08c2ffffffff
+>0 use msdos-driver
+# 3Com EtherLink 3C501 CID\SERVER\IBMLS\IBM500D1\DLSNETDR.ZIP\ELNK.DOS
+0 ulequad 0x027ac0c0ffffffff
+>0 use msdos-driver
+# IBM Streamer CID\SERVER\IBMLS\IBM500D1\DLSNETDR.ZIP\IBMMPC.DOS
+0 ulequad 0x00228880ffffffff
+>0 use msdos-driver
+
+# updated by Joerg Jenderek
+# GRR: line below too general as it catches also
+# rt.lib DYADISKS.PIC and many more
+# start with assembler instruction MOV
+0 ubyte 0x8c
+# skip "AppleWorks word processor data" like ARTICLE.1 ./apple
+>4 string !O====
+# skip some unknown basic binaries like RocketRnger.SHR
+>>5 string !MAIN
+# skip "GPG symmetrically encrypted data" ./gnu
+# skip "PGP symmetric key encrypted data" ./pgp
+# openpgpdefs.h: fourth byte < 14 indicate cipher algorithm type
+>>>4 ubyte >13
+>>>>0 use msdos-com
+# the remaining files should be DOS *.COM executables
+# dosshell.COM 8cc0 2ea35f07 e85211 e88a11 b80058 cd
+# hmload.COM 8cc8 8ec0 bbc02b 89dc 83c30f c1eb04 b4
+# UNDELETE.COM 8cca 2e8916 6503 b430 cd21 8b 2e0200 8b
+# BOOTFIX.COM 8cca 2e8916 9603 b430 cd21 8b 2e0200 8b
+# RAWRITE3.COM 8cca 2e8916 d602 b430 cd21 8b 2e0200 8b
+# SHARE.COM 8cca 2e8916 d602 b430 cd21 8b 2e0200 8b
+# validchr.COM 8cca 2e8916 9603 b430 cd21 8b 2e028b1e
+# devload.COM 8cca 8916ad01 b430 cd21 8b2e0200 892e
+
+0 name msdos-com
+# URL: http://fileformats.archiveteam.org/wiki/DOS_executable_(.com)
+>0 byte x DOS executable (
+# DOS executable with JuMP 16-bit instruction
+>0 byte =0xE9
+# check for probably nil padding til offset 64 of Lotus driver name
+>>56 quad =0
+# check for "long" alphabetic Lotus driver name like:
+# Diablo "COMPAQ Text Display" "IBM Monochrome Display" "Plantronics ColorPlus"
+>>>24 regex =^[A-Z][A-Za-z\040]{5,21} \bLotus driver) %s
+!:mime application/x-dosexec
+# like: CPQ0TD.DRV IBM0MONO.DRV (Lotus 123 10a) SDIAB4.DRV SPL0CPLS.DRV (Lotus Symphony 2)
+!:ext drv
+# COM with nils like MODE.COM IBMDOS.COM (pcdos 3.31 ru Compaq) RSSTUB.COM (PC-DOS 2000 de) ACCESS.COM (Lotus Symphony 1)
+>>>24 default x \bCOM)
+!:mime application/x-dosexec
+!:ext com
+# DOS executable with JuMP 16-bit and without nil padding
+>>56 quad !0
+# https://wiki.syslinux.org/wiki/index.php?title=Doc/comboot
+# TODO: HOWTO distinguish COMboot from pure DOS executables?
+# look for unreliable Syslinux specific api call INTerrupt 22h for 16-bit COMBOOT program
+>>>1 search/0xc088 \xcd\x22 \bCOM or COMBOOT 16-bit)
+!:mime application/x-dosexec
+# like: sbm.cbt command.com (Windows XP) UNI2ASCI.COM (FreeDOS 1.2)
+!:ext com/cbt
+>>>1 default x \bCOM)
+!:mime application/x-dosexec
+!:ext com
+# DOS executable without JuMP 16-bit instruction
+>0 byte !0xE9
+# SCREATE.SYS https://en.wikipedia.org/wiki/Stac_Electronics
+>>10 string =?STACVOL \bSCREATE.SYS)
+!:mime application/x-dosexec
+!:ext sys
+# COM executable without JuMP 16-bit instruction and not SCREATE.SYS
+>>10 string !?STACVOL \bCOM)
+!:mime application/x-dosexec
+!:ext com
+>6 string SFX\ of\ LHarc \b, %s
+>0x1FE leshort 0xAA55 \b, boot code
+>85 string UPX \b, UPX compressed
+>4 string \ $ARX \b, ARX self-extracting archive
+>4 string \ $LHarc \b, LHarc self-extracting archive
+>0x20e string SFX\ by\ LARC \b, LARC self-extracting archive
+# like: E30ODI.COM MADGEODI.COM UNI2ASCI.COM RECOVER.COM (DOS 2) COMMAND.COM (DOS 2)
+>1 search/0xc088 \xcd\x22 \b, maybe with interrupt 22h
+>0 ubelong x \b, start instruction %#8.8x
+# show more instructions but not in samples like: rem.com (DJGPP)
+>4 ubelong x %8.8x
+
+# JMP 8bit
+0 byte 0xeb
+# byte 0xeb conflicts with magic leshort 0xn2eb of "SYMMETRY i386" handled by ./sequent
+# allow forward jumps only
+>1 byte >-1
+# that offset must be accessible
+# with hexadecimal values like: 0e 2e 50 8c 8d ba bc bd be e8 fb fc
+>>(1.b+2) byte x
+# if look like COM executable with x86 boot signature then this
+# implies FAT volume with x86 real mode code already handled by ./filesystems
+#
+# No x86 boot signature implies often DOS executable
+# check for unrealistic high number of FATs. Then it is an unusual disk image or often a DOS executable
+# like: FIXBIOS.COM (50 bytes)
+>>>16 ubyte >3
+# https://www.drivedroid.io/
+# skip MBR disk image drivedroid.img version 12 July 2013 by start message
+>>>>2 string !DriveDroid
+# ftp://old-dos.ru/OSCollect/OS/MS-DOS/Final Releases/
+# skip unusual floppy image disk1.img of MS-DOS 1.25 (Corona Data Systems OEM)
+# by check for characteristic message text near the beginning
+>>>>>15 string !Non\040System\040disk
+# "ftp://old-dos.ru/OSCollect/OS/BeOS/BeOS 4.0.rar"
+# skip BeOS 4 bootfloppy.img done as "Linux kernel x86 boot executable" by ./linux
+# by check for characteristic message text near the beginning
+>>>>>>6 string !read\040error\015
+# https://github.com/ventoy/Ventoy/releases/download/v1.0.78/ventoy-1.0.78-windows.zip
+# skip ventoy 1.0.78 boot_hybrid.img
+>>>>>>>24 string !\220\220\353I$\022\017
+# "ftp://old-dos.ru/OSCollect/OS/MS-DOS/Final Releases/PC-DOS 1.0 (5.25).rar"
+# skip unusual floppy image PCDOS100.IMG of DOS 1.0
+# by check for characteristic message text near the beginning
+>>>>>>>>9 string !7-May-81
+# "ftp://old-dos.ru/OSCollect/OS/BeOS/BeOS 5.0 Personal (BA).rar"
+# skip BeOS 5 floppy_1.44.00.ima done as "DOS/MBR boot sector" by ./filesystems
+# by check for characteristic message near the beginning
+>>>>>>>>>3 string !\370sdfS\270
+# like: FIXBIOS.COM (50 bytes)
+>>>>>>>>>>0 use msdos-com
+# check for unrealistic low number of FATs. Then it is an unusual FAT disk image or often a DOS executable
+# like: DEVICE.COM INSTALL.COM (GAG 4.10) WORD.COM (Word 1.15)
+>>>16 ubyte =0
+# if low FATs with x86 boot signature it can be unusual disk image like: boot.img (Ventoy 1.0.27) geodspms.img (Syslinux)
+>>>>0x1FE leshort =0xAA55
+>>>>0x1FE default x
+# https://thestarman.pcministry.com/tool/hxd/dimtut.htm
+# skip unusual floppy image TK-DOS11.img IBMDOS11.img of IBM DOS 1.10
+# by check for characteristic bootloader names near end of boot sector
+>>>>>395 string !ibmbio\040\040com
+>>>>>>0 use msdos-com
+# 8-bit jump with valid number of FAT implies FAT volume already handled by ./filesystems
+# like: balder.img
+>>>16 default x
+# skip disk images with boot signature at end of 1st sector
+# like: TDSK-64b.img
+>>>>(11.s-2) uleshort !0xAA55
+# skip unusual floppy image without boot signature like 360k-256.img (mtools 4.0.18)
+# by check for characteristic file system type text for FAT (12 bit or 16 bit)
+>>>>>54 string !FAT
+# "ftp://old-dos.ru/OSCollect/OS/MS-DOS/Final Releases/Microsoft MS-DOS 3.31 (Compaq OEM) (3.5).rar"
+# skip unusual floppy image Disk4.img without boot signature and file system type text
+# by check for characteristic OEM-ID text
+>>>>>>3 string !COMPAQ\040\040
+# no such DOS COM executables found
+>>>>>>>0 use msdos-com
+# JMP 16bit
+0 byte 0xe9
+# 16-bit offset; for DEBUGGING!; can be negative like: USBDRIVE.COM
+#>1 leshort x \b, OFFSET %d
+# forward jumps
+>1 leshort >-1
+# that offset must be accessible
+# with hexadecimal values like: 06 1e 0e 2e 60 8c 8d b4 ba be e8 fc
+>>(1.s+3) byte x
+# check for unrealistic high number of FATs. Then it is not a disk image and it is a DOS executable
+# like: CALLVER.COM CPUCACHE.COM K437_EUR.COM SHSUCDX.COM UMBFILL.COM (183 bytes)
+>>>16 ubyte >3
+>>>>0 use msdos-com
+# check for unrealistic low number of FATs. Then it is not a disk image and it is a DOS executable
+# like: GAG.COM DRMOUSE.COM NDN.COM CPQ0TD.DRV
+>>>16 ubyte =0
+>>>>0 use msdos-com
+# maybe disc image with valid number of FATs or DOS executable
+# like: IPXODI.COM PERUSE.COM TASKID.COM
+>>>16 default x
+# invalid low media descriptor. Then it is not a disk image and it is a DOS executable
+>>>>21 ubyte <0xE5
+>>>>>0 use msdos-com
+# valid media descriptor. Then it is maybe disk image or DOS executable
+>>>>21 ubyte >0xE4
+# invalid sectorsize not a power of 2 from 32-32768. Then it is not a disk image and it must be DOS executable
+# like: LEARN.COM (Word 1.15)
+>>>>>11 uleshort&0x001f !0
+>>>>>>0 use msdos-com
+# negative offset, must not lead into PSP
+# like: BASICA.COM (PC dos 3.20) FORMAT.COM SMC8100.COM WORD.COM (word4)
+# HIDSUPT1.COM USBDRIVE.COM USBSUPT1.COM USBUHCI.COM (FreeDOS USBDOS)
+>1 leshort <-259
+# that offset must be accessible
+# add 10000h to jump at end of 64 KiB segment, add 1 for jump instruction and 2 for 16-bit offset
+>>(1,s+65539) byte x
+# after jump next instruction for DEBUGGING!
+#>>>&-1 ubelong x \b, NEXT instruction %#8.8x
+>>>0 use msdos-com
+
+# updated by Joerg Jenderek at Oct 2008,2015,2022
+# following line is too general
+0 ubyte 0xb8
+# skip 2 linux kernels like memtest.bin with "\xb8\xc0\x07\x8e" in ./linux
+>0 string !\xb8\xc0\x07\x8e
+# modified by Joerg Jenderek
+# syslinux COM32 or COM32R executable
+>>1 lelong&0xFFFFFFFe 0x21CD4CFe COM executable (32-bit COMBOOT
+# https://www.syslinux.org/wiki/index.php/Comboot_API
+# Since version 5.00 c32 modules switched from the COM32 object format to ELF
+!:mime application/x-c32-comboot-syslinux-exec
+!:ext c32
+# https://syslinux.zytor.com/comboot.php
+# older syslinux version ( <4 )
+# (32-bit COMBOOT) programs *.C32 contain 32-bit code and run in flat-memory 32-bit protected mode
+# start with assembler instructions mov eax,21cd4cffh
+>>>1 lelong 0x21CD4CFf \b)
+# syslinux:doc/comboot.txt
+# A COM32R program must start with the byte sequence B8 FE 4C CD 21 (mov
+# eax,21cd4cfeh) as a magic number.
+# syslinux version (4.x)
+# "COM executable (COM32R)" or "Syslinux COM32 module" by TrID
+>>>1 lelong 0x21CD4CFe \b, relocatable)
+>>1 default x
+# look for interrupt instruction like in rem.com (DJGPP) LOADER.COM (DR-DOS 7.x)
+>>>3 search/118 \xCD
+# FOR DEBUGGING; possible hexadecimal interrupt number like: 10~BANNER.COM 13~bcdw_cl.com 15~poweroff.com (Syslinux)
+# 1A~BERNDPCI.COM 20~SETENHKB.COM 21~mostly 22~gfxboot.com (Syslinux) 2F~SHUTDOWN.COM (GEMSYS)
+#>>>>&0 ubyte x \b, INTERUPT %#x
+# few examples with interrupt 0x13 instruction
+>>>>&0 ubyte =0x13
+# FOR DEBUGGING!
+#>>>>>3 ubequad x \b, 2nd INSTRUCTION %#16.16llx
+# skip Gpt.com Mbr.com (edk2-UDK2018 bootsector) described as "DOS/MBR boot sector" by ./filesystems
+# by check for assembler instructions: mov es,ax ; mov ax,07c0h ; mov ds,ax
+>>>>>3 ubequad !0x8ec0b8c0078ed88d
+# few COM executables with interrupt 0x13 instruction like: Bootable CD Wizard executables bcdw_cl.com fdemuoff.com
+# http://bootcd.narod.ru/bcdw150z_en.zip
+>>>>>>0 use msdos-com
+# few examples with interrupt 0x16 instruction like flashimg.img
+>>>>&0 ubyte =0x16
+# skip Syslinux 3.71 flashimg.img done as "DOS/MBR boot sector" by ./filesystems
+# by check for assembler instructions: cmp ax 0xE4E4 (magic); jnz
+>>>>>8 ubelong !0x3DE4E475
+# no DOS executable with interrupt 0x16 found
+>>>>>>0 use msdos-com
+# most examples with interrupt instruction unequal 0x13 and 0x16
+>>>>&0 default x
+#>>>>>&-1 ubyte x \b, INTERUPT %#x
+# like: LOADER.COM SETENHKB.COM banner.com copybs.com gif2raw.com poweroff.com rem.com
+>>>>>0 use msdos-com
+# few COM executables without interrupt instruction like RESTART.COM (DOS 7.10) REBOOT.COM
+# or some EUC-KR text files or one Ulead Imaginfo thumbnail
+>>>3 default x
+# FOR DEBUGGING; 2nd instruction like 0x50 (RESTART.COM) 0x8e (REBOOT.COM)
+# or random like: 0x0 (IMAGINFO.PE3 sky_snow) 0xb1 (euckr_.txt)
+#>>>>3 ubyte x \b, 2nd INSTRUCTION %#x
+# skip 1 Ulead Imaginfo thumbnail (IMAGINFO.PE3 sky_snow)
+# inside SAMPLES/TEXTURES/SKY_SNOW
+# from https://archive.org/download/PI3CANON/PI3CANON.iso
+>>>>3 ubyte !0x0
+# skip some EUC-KR text files like: euckr_falsepositive.txt
+# https://bugs.astron.com/view.php?id=186
+>>>>>3 ubyte !0xb1
+# like: RESTART.COM (DOS 7.10) REBOOT.COM
+>>>>>>0 use msdos-com
+
+# URL: https://en.wikipedia.org/wiki/UPX
+# Reference: https://github.com/upx/upx/archive/v3.96.zip/upx-3.96/
+# src/stub/src/i086-dos16.com.S
+# Update: Joerg Jenderek
+# assembler instructions: cmp sp, offset sp_limit
+0 string/b \x81\xfc
+#>2 uleshort x \b, sp_limit=%#x
+# assembler instructions: jump above +2; int 0x20; mov cx, offset bytes_to_copy
+>4 string \x77\x02\xcd\x20\xb9
+#>9 uleshort x \b, [bytes_to_copy]=%#x
+# at different offsets assembler instructions: push di; jump decomp_start_n2b
+>0x1e search/3 \x57\xe9
+#>>&0 uleshort x \b, decomp_start_n2b=%#x
+# src/stub/src/include/header.S; UPX_MAGIC_LE32
+>>&2 string UPX! FREE-DOS executable (COM), UPX
+!:mime application/x-dosexec
+# UPX compressed *.CPI; See ./fonts
+>>>&21 string =FONT compressed DOS code page font
+!:ext cpx
+>>>&21 string !FONT compressed
+!:ext com
+# compressed size?
+#>>>&14 uleshort+152 x \b, %u bytes
+# uncompressed len
+>>>&12 uleshort x \b, uncompressed %u bytes
+252 string Must\ have\ DOS\ version DR-DOS executable (COM)
+!:mime application/x-dosexec
+!:ext com
+# GRR search is not working
+#2 search/28 \xcd\x21 COM executable for MS-DOS
+#WHICHFAT.cOM
+2 string \xcd\x21 COM executable for DOS
+!:mime application/x-dosexec
+!:ext com
+#DELTREE.cOM DELTREE2.cOM
+4 string \xcd\x21 COM executable for DOS
+!:mime application/x-dosexec
+!:ext com
+#IFMEMDSK.cOM ASSIGN.cOM COMP.cOM
+5 string \xcd\x21 COM executable for DOS
+!:mime application/x-dosexec
+!:ext com
+#DELTMP.COm HASFAT32.cOM
+7 string \xcd\x21
+>0 byte !0xb8 COM executable for DOS
+!:mime application/x-dosexec
+!:ext com
+#COMP.cOM MORE.COm
+10 string \xcd\x21
+>5 string !\xcd\x21 COM executable for DOS
+!:mime application/x-dosexec
+!:ext com
+#comecho.com
+13 string \xcd\x21 COM executable for DOS
+!:mime application/x-dosexec
+!:ext com
+#HELP.COm EDIT.coM
+18 string \xcd\x21
+# not printable before it?
+>17 byte >32
+>>17 byte <126
+>>17 default x COM executable for MS-DOS
+!:mime application/x-dosexec
+!:ext com
+#NWRPLTRM.COm
+23 string \xcd\x21 COM executable for MS-DOS
+!:mime application/x-dosexec
+!:ext com
+#LOADFIX.cOm LOADFIX.cOm
+30 string \xcd\x21 COM executable for MS-DOS
+!:mime application/x-dosexec
+!:ext com
+#syslinux.com 3.11
+70 string \xcd\x21 COM executable for DOS
+!:mime application/x-dosexec
+!:ext com
+# many compressed/converted COMs start with a copy loop instead of a jump
+0x6 search/0xa \xfc\x57\xf3\xa5\xc3 COM executable for MS-DOS
+!:mime application/x-dosexec
+!:ext com
+0x6 search/0xa \xfc\x57\xf3\xa4\xc3 COM executable for DOS
+!:mime application/x-dosexec
+!:ext com
+>0x18 search/0x10 \x50\xa4\xff\xd5\x73 \b, aPack compressed
+0x3c string W\ Collis\0\0 COM executable for MS-DOS, Compack compressed
+!:mime application/x-dosexec
+!:ext com
+# FIXME: missing diet .com compression
+
+# miscellaneous formats
+0 string/b LZ MS-DOS executable (built-in)
+#0 byte 0xf0 MS-DOS program library data
+#
+
+# AAF files:
+# <stuartc@rd.bbc.co.uk> Stuart Cunningham
+0 string/b \320\317\021\340\241\261\032\341AAFB\015\000OM\006\016\053\064\001\001\001\377 AAF legacy file using MS Structured Storage
+>30 byte 9 (512B sectors)
+>30 byte 12 (4kB sectors)
+0 string/b \320\317\021\340\241\261\032\341\001\002\001\015\000\002\000\000\006\016\053\064\003\002\001\001 AAF file using MS Structured Storage
+>30 byte 9 (512B sectors)
+>30 byte 12 (4kB sectors)
+
+# Popular applications
+#
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/DOC
+# Reference: https://web.archive.org/web/20170206041048/
+# http://www.msxnet.org/word2rtf/formats/ffh-dosword5
+# wIdent+dty
+0 belong 0x31be0000
+# skip droid skeleton like x-fmt-274-signature-id-488.doc
+>128 ubyte >0 Microsoft
+>>96 uleshort =0 Word
+!:mime application/msword
+!:apple MSWDWDBN
+# DCX is used in the Unix version.
+!:ext doc/dcx
+>>>0x6E ulequad =0 1.0-4.0
+>>>0x6E ulequad !0 5.0-6.0
+>>>0x6E ulequad x (DOS) Document
+# https://web.archive.org/web/20130831064118/http://msxnet.org/word2rtf/formats/write.txt
+>>96 uleshort !0 Write 3.0 (Windows) Document
+!:mime application/x-mswrite
+!:apple MSWDWDBN
+# sometimes also doc like in splitter.doc srchtest.doc
+!:ext wri/doc
+# wTool must be 0125400 octal
+#>>4 uleshort !0xAB00 \b, wTool %o
+# reserved; must be zero
+#>>6 ulelong !0 \b, reserved %u
+# block pointer to the block containing optional file manager information
+#>>0x1C uleshort x \b, at %#x info block
+# jump to File manager information block
+>>(0x1C.s*128) uleshort x
+# test for valid information start; maybe also 0012h
+>>>&-2 uleshort =0x0014
+# Document ASCIIZ name
+>>>>&0x12 string x %s
+# author name
+>>>>>&1 string x \b, author %s
+# reviser name
+>>>>>>&1 string x \b, reviser %s
+# keywords
+>>>>>>>&1 string x \b, keywords %s
+# comment
+>>>>>>>>&1 string x \b, comment %s
+# version number
+>>>>>>>>>&1 string x \b, version %s
+# date of last change MM/DD/YY
+>>>>>>>>>>&1 string x \b, %-.8s
+# creation date MM/DD/YY
+>>>>>>>>>>&9 string x created %-.8s
+# file name of print format like NORMAL.STY
+>>0x1E string >0 \b, formatted by %-.66s
+# count of pages in whole file for write variant; maybe some times wrong
+>>96 uleshort >0 \b, %u pages
+# name of the printer driver like HPLASMS
+>>0x62 string >0 \b, %-.8s printer
+# number of blocks used in the file; seems to be 0 for Word 4.0 and Write 3.0
+>>0x6A uleshort >0 \b, %u blocks
+# bit field for corrected text areas
+#>>0x6C uleshort x \b, %#x bit field
+# text of document; some times start with 4 non printable characters like CR LF
+>>128 ubyte x \b,
+>>>128 ubyte >0x1F
+>>>>128 string x %s
+>>>128 ubyte <0x20
+>>>>129 ubyte >0x1F
+>>>>>129 string x %s
+>>>>129 ubyte <0x20
+>>>>>130 ubyte >0x1F
+>>>>>>130 string x %s
+>>>>>130 ubyte <0x20
+>>>>>>131 ubyte >0x1F
+>>>>>>>131 string x %s
+>>>>>>131 ubyte <0x20
+>>>>>>>132 ubyte >0x1F
+>>>>>>>>132 string x %s
+>>>>>>>132 ubyte <0x20
+>>>>>>>>133 ubyte >0x1F
+>>>>>>>>>133 string x %s
+#
+0 string/b PO^Q` Microsoft Word 6.0 Document
+!:mime application/msword
+#
+4 long 0
+>0 belong 0xfe320000 Microsoft Word for Macintosh 1.0
+!:mime application/msword
+!:ext mcw
+>0 belong 0xfe340000 Microsoft Word for Macintosh 3.0
+!:mime application/msword
+!:ext mcw
+>0 belong 0xfe37001c Microsoft Word for Macintosh 4.0
+!:mime application/msword
+!:ext mcw
+>0 belong 0xfe370023 Microsoft Word for Macintosh 5.0
+!:mime application/msword
+!:ext mcw
+
+0 string/b \333\245-\0\0\0 Microsoft Word 2.0 Document
+!:mime application/msword
+!:ext doc
+# Note: seems already recognized as "OLE 2 Compound Document" in ./ole2compounddocs
+#512 string/b \354\245\301 Microsoft Word Document
+#!:mime application/msword
+
+#
+0 string/b \xDB\xA5\x2D\x00 Microsoft WinWord 2.0 Document
+!:mime application/msword
+#
+0 string/b \xDB\xA5\x2D\x00 Microsoft WinWord 2.0 Document
+!:mime application/msword
+
+#
+0 string/b \x09\x04\x06\x00\x00\x00\x10\x00 Microsoft Excel Worksheet
+!:mime application/vnd.ms-excel
+# https://www.macdisk.com/macsigen.php
+!:apple XCELXLS4
+!:ext xls
+#
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Lotus_1-2-3
+# Reference: http://www.aboutvb.de/bas/formate/pdf/wk3.pdf
+# Note: newer Lotus versions >2 use longer BOF record
+# record type (BeginningOfFile=0000h) + length (001Ah)
+0 belong 0x00001a00
+# reserved should be 0h but 8c0dh for TUTMAC.WK3, 5h for SAMPADNS.WK3, 1h for a_readme.wk3, 1eh for K&G86.WK3
+#>18 uleshort&0x73E0 0
+# Lotus Multi Byte Character Set (LMBCS=1-31)
+>20 ubyte >0
+>>20 ubyte <32 Lotus 1-2-3
+#!:mime application/x-123
+!:mime application/vnd.lotus-1-2-3
+!:apple ????L123
+# (version 5.26) labeled the entry as "Lotus 1-2-3 wk3 document data"
+>>>4 uleshort 0x1000 WorKsheet, version 3
+!:ext wk3
+# (version 5.26) labeled the entry as "Lotus 1-2-3 wk4 document data"
+>>>4 uleshort 0x1002 WorKsheet, version 4
+# also worksheet template 4 (.wt4)
+!:ext wk4/wt4
+# no example or documentation for wk5
+#>>4 uleshort 0x???? WorKsheet, version 4
+#!:ext wk5
+# only MacrotoScript.123 example
+>>>4 uleshort 0x1003 WorKsheet, version 97
+# also worksheet template Smartmaster (.12M)?
+!:ext 123
+# only Set_Y2K.123 example
+>>>4 uleshort 0x1005 WorKsheet, version 9.8 Millennium
+!:ext 123
+# no example for this version
+>>>4 uleshort 0x8001 FoRMatting data
+!:ext frm
+# (version 5.26) labeled the entry as "Lotus 1-2-3 fm3 or fmb document data"
+# TrID labeles the entry as "Formatting Data for Lotus 1-2-3 worksheet"
+>>>4 uleshort 0x8007 ForMatting data, version 3
+!:ext fm3
+>>>4 default x unknown
+# file revision sub code 0004h for worksheets
+>>>>6 uleshort =0x0004 worksheet
+!:ext wXX
+>>>>6 uleshort !0x0004 formatting data
+!:ext fXX
+# main revision number
+>>>>4 uleshort x \b, revision %#x
+>>>6 uleshort =0x0004 \b, cell range
+# active cellcoord range (start row, page,column ; end row, page, column)
+# start values normally 0~1st sheet A1
+>>>>8 ulelong !0
+>>>>>10 ubyte >0 \b%d*
+>>>>>8 uleshort x \b%d,
+>>>>>11 ubyte x \b%d-
+# end page mostly 0
+>>>>14 ubyte >0 \b%d*
+# end raw, column normally not 0
+>>>>12 uleshort x \b%d,
+>>>>15 ubyte x \b%d
+# Lotus Multi Byte Character Set (1~cp850,2~cp851,...,16~japan,...,31~??)
+>>>>20 ubyte >1 \b, character set %#x
+# flags
+>>>>21 ubyte x \b, flags %#x
+>>>6 uleshort !0x0004
+# record type (FONTNAME=00AEh)
+>>>>30 search/29 \0\xAE
+# variable length m (2) + entries (1) + ?? (1) + LCMBS string (n)
+>>>>>&4 string >\0 \b, 1st font "%s"
+#
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Lotus_1-2-3
+# Reference: http://www.schnarff.com/file-formats/lotus-1-2-3/WSFF2.TXT
+# Note: Used by both old Lotus 1-2-3 and Lotus Symphony (DOS) til version 2.x
+# record type (BeginningOfFile=0000h) + length (0002h)
+0 belong 0x00000200
+# GRR: line above is too general as it catches also MS Windows CURsor
+# to display MS Windows cursor (strength=70) before Lotus 1-2-3 (strength=70-1)
+!:strength -1
+# skip Windows cursors with image height <256 and keep Lotus with low opcode 0001-0083h
+>7 ubyte 0
+# skip Windows cursors with image width 256 and keep Lotus with positive opcode
+>>6 ubyte >0 Lotus
+# !:mime application/x-123
+!:mime application/vnd.lotus-1-2-3
+!:apple ????L123
+# revision number (0404h = 123 1A, 0405h = Lotus Symphony , 0406h = 123 2.x wk1 , 8006h = fmt , ...)
+# undocumented; (version 5.26) labeled the configurations as "Lotus 1-2-3"
+>>>4 uleshort 0x0007 1-2-3 CoNFiguration, version 2.x (PGRAPH.CNF)
+!:ext cnf
+>>>4 uleshort 0x0C05 1-2-3 CoNFiguration, version 2.4J
+!:ext cnf
+>>>4 uleshort 0x0801 1-2-3 CoNFiguration, version 1-2.1
+!:ext cnf
+>>>4 uleshort 0x0802 Symphony CoNFiguration
+!:ext cnf
+>>>4 uleshort 0x0804 1-2-3 CoNFiguration, version 2.2
+!:ext cnf
+>>>4 uleshort 0x080A 1-2-3 CoNFiguration, version 2.3-2.4
+!:ext cnf
+>>>4 uleshort 0x1402 1-2-3 CoNFiguration, version 3.x
+!:ext cnf
+>>>4 uleshort 0x1450 1-2-3 CoNFiguration, version 4.x
+!:ext cnf
+# (version 5.26) labeled the entry as "Lotus 123"
+# TrID labeles the entry as "Lotus 123 Worksheet (generic)"
+>>>4 uleshort 0x0404 1-2-3 WorKSheet, version 1
+# extension "wks" also for Microsoft Works document
+!:ext wks
+# (version 5.26) labeled the entry as "Lotus 123"
+# TrID labeles the entry as "Lotus 123 Worksheet (generic)"
+>>>4 uleshort 0x0405 Symphony WoRksheet, version 1.0
+!:ext wrk/wr1
+# (version 5.26) labeled the entry as "Lotus 1-2-3 wk1 document data"
+# TrID labeles the entry as "Lotus 123 Worksheet (V2)"
+>>>4 uleshort 0x0406 1-2-3/Symphony worksheet, version 2
+# Symphony (.wr1)
+!:ext wk1/wr1
+# no example for this japan version
+>>>4 uleshort 0x0600 1-2-3 WorKsheet, version 1.xJ
+!:ext wj1
+# no example or documentation for wk2
+#>>>4 uleshort 0x???? 1-2-3 WorKsheet, version 2
+#!:ext wk2
+# undocumented japan version
+>>>4 uleshort 0x0602 1-2-3 worksheet, version 2.4J
+!:ext wj3
+# (version 5.26) labeled the entry as "Lotus 1-2-3 fmt document data"
+>>>4 uleshort 0x8006 1-2-3 ForMaTting data, version 2.x
+# japan version 2.4J (fj3)
+!:ext fmt/fj3
+# no example for this version
+>>>4 uleshort 0x8007 1-2-3 FoRMatting data, version 2.0
+!:ext frm
+# (version 5.26) labeled the entry as "Lotus 1-2-3"
+>>>4 default x unknown worksheet or configuration
+!:ext cnf
+>>>>4 uleshort x \b, revision %#x
+# 2nd record for most worksheets describes cells range
+>>>6 use lotus-cells
+# 3rd record for most japan worksheets describes cells range
+>>>(8.s+10) use lotus-cells
+# check and then display Lotus worksheet cells range
+0 name lotus-cells
+# look for type (RANGE=0006h) + length (0008h) at record begin
+>0 ubelong 0x06000800 \b, cell range
+# cell range (start column, row, end column, row) start values normally 0,0~A1 cell
+>>4 ulong !0
+>>>4 uleshort x \b%d,
+>>>6 uleshort x \b%d-
+# end of cell range
+>>8 uleshort x \b%d,
+>>10 uleshort x \b%d
+# EndOfLotus123
+0 string/b WordPro\0 Lotus WordPro
+!:mime application/vnd.lotus-wordpro
+0 string/b WordPro\r\373 Lotus WordPro
+!:mime application/vnd.lotus-wordpro
+
+
+# Summary: Script used by InstallScield to uninstall applications
+# Extension: .isu
+# Submitted by: unknown
+# Modified by (1): Abel Cheung <abelcheung@gmail.com> (replace useless entry)
+0 string \x71\xa8\x00\x00\x01\x02
+>12 string Stirling\ Technologies, InstallShield Uninstall Script
+
+# Winamp .avs
+#0 string Nullsoft\ AVS\ Preset\ \060\056\061\032 A plug in for Winamp ms-windows Freeware media player
+0 string/b Nullsoft\ AVS\ Preset\ Winamp plug in
+
+# Windows Metafile .WMF
+# URL: http://fileformats.archiveteam.org/wiki/Windows_Metafile
+# http://en.wikipedia.org/wiki/Windows_Metafile
+# Reference: https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/%5bMS-WMF%5d.pdf
+# http://mark0.net/download/triddefs_xml.7z/defs/w/wmf.trid.xml
+# Note: called "Windows Metafile" by TrID and
+# verified by ImageMagick `identify -verbose *.wmf` as WMF (Windows Meta File)
+# META_PLACEABLE Record (Aldus Placeable Metafile signature)
+0 string/b \327\315\306\232
+# Note: called "Windows Metafile Image with Placeable File Header" by DROID via PUID x-fmt/119
+# and verified by XnView `nconvert -info abydos.wmf SPA_FLAG.wmf hardcopy-windows-meta.wmf` as "Windows Placeable metafile"
+# skip failed libreoffice-7.3.2.2 ofz35149-1.wmf with invalid version 2020h and exttextout-2.wmf with invalid version 3a02h
+# and x-fmt-119-signature-id-609.wmf without version instead of 0100h=METAVERSION100 or 0300h=METAVERSION300
+>26 uleshort&0xFDff =0x0100 Windows metafile
+# HWmf; resource handle to the metafile; When the metafile is on disk, this field MUST contain 0
+# seems to be always true but in failed samples 2020h ofz35149-1.wmf 56f8h exttextout-2.wmf
+>>4 uleshort !0 \b, resource handle %#x
+# BoundingBox; the rectangle in the playback context measured in logical units for displaying
+# sometimes useful like: hardcopy-windows-meta.wmf (0,0 / 1280,1024)
+# but garbage in x-fmt-119-signature-id-609.wmf (-21589,-21589 / -21589,-21589)
+#>>6 ubequad x \b, bounding box %#16.16llx
+# Left; x-coordinate of the upper-left corner of the rectangle
+>>6 leshort x \b, bounding box (%d
+# Top; y-coordinate upper-left corner
+>>8 leshort x \b,%d
+# Right; x-coordinate lower-right corner
+>>10 leshort x / %d
+# Bottom; y-coordinate lower-right corner
+>>12 leshort x \b,%d)
+# Inch; number of logical units per inch like: 72 96 575 576 1000 1200 1439 1440 2540
+>>14 uleshort x \b, dpi %u
+# Reserved; field is not used and MUST be set to 0; but ababababh in x-fmt-119-signature-id-609.wmf
+>>16 ulelong !0 \b, reserved %#x
+# Checksum; checksum for the previous 10 words
+>>20 uleshort x \b, checksum %#x
+# META_HEADER Record after META_PLACEABLE Record
+>>22 use wmf-head
+# GRR: no example for type 2 (DISKMETAFILE) variant found under few thousands WMF
+0 string/b \002\000\011\000 Windows metafile
+>0 use wmf-head
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/w/wmf-16.trid.xml
+# Note: called "Windows Metafile (old Win 3.x format)" by TrID and
+# "Windows Metafile Image without Placeable File Header" by DROID via PUID x-fmt/119
+# verified by XnView `nconvert -info *.wmf` as Windows metafile
+# variant with type=1=MEMORYMETAFILE and valid HeaderSize 9
+0 string/b \001\000\011\000
+# skip DROID x-fmt-119-signature-id-1228.wmf by looking for content after header (18 bytes=2*011)
+>18 ulelong >0 Windows metafile
+# GRR: in version 5.44 unequal and not endian variant not working!
+#>18 ulelong !0 THIS_SHOULD_NOT_HAPPEN
+#>18 long !0 THIS_SHOULD_NOT_HAPPEN
+>>0 use wmf-head
+# display information of Windows metafile header (type, size, objects)
+0 name wmf-head
+# MetafileType: 0001h=MEMORYMETAFILE~Metafile is stored in memory 0002h=DISKMETAFILE~Metafile is stored on disk
+>0 uleshort !0x0001 \b, type %#x
+# HeaderSize; the number of WORDs in header record; seems to be always 9 (18 bytes)
+>2 uleshort*2 !18 \b, header size %u
+# MetafileVersion: 0100h=METAVERSION100~DIBs (device-independent bitmaps) not supported 0300h=METAVERSION300~DIBs are supported
+# but in failed samples 2020h ofz35149-1.wmf 3a02h exttextout-2.wmf
+>4 uleshort =0x0100 \b, DIBs not supported
+>4 uleshort =0x0300
+#>4 uleshort =0x0300 \b, DIBs supported
+# this should not happen!
+>4 default x \b, version
+>>4 uleshort x %#x
+# Size; the number of WORDs in the entire metafile
+>6 ulelong x \b, size %u words
+#>6 ulelong*2 x \b, size %u bytes
+!:mime image/wmf
+!:ext wmf
+# NumberOfObjects: the number of graphics objects like: 0 hardcopy-windows-meta.wmf 1 2 3 4 5 6 7 8 9 12 13 14 16 17 20 27 110 PERSGRID.WMF
+>10 uleshort x \b, %u objects
+# MaxRecord: the size of the largest record in the metafile in WORDs like: 78h b0h 1f4h 310h 63fh 1e0022h 3fcc21h
+>12 ulelong x \b, largest record size %#x
+# NumberOfMembers: It SHOULD be 0x0000, but 5 TestBitBltStretchBlt.wmf 13 TestPalette.wmf and in failed samples 4254 bitcount-1.wmf 8224 ofz5942-1.wmf 56832 exttextout-2.wmf
+>16 uleshort !0 \b, %u members
+
+#tz3 files whatever that is (MS Works files)
+0 string/b \003\001\001\004\070\001\000\000 tz3 ms-works file
+0 string/b \003\002\001\004\070\001\000\000 tz3 ms-works file
+0 string/b \003\003\001\004\070\001\000\000 tz3 ms-works file
+
+# PGP sig files .sig
+#0 string \211\000\077\003\005\000\063\237\127 065 to \027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\065\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\066\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\067\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\070\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\077\003\005\000\063\237\127\071\027\266\151\064\005\045\101\233\021\002 PGP sig
+0 string \211\000\225\003\005\000\062\122\207\304\100\345\042 PGP sig
+
+# windows zips files .dmf
+0 string/b MDIF\032\000\010\000\000\000\372\046\100\175\001\000\001\036\001\000 MS Windows special zipped file
+
+# Windows icons
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/CUR_(file_format)
+# Note: similar to Windows CURsor. container for BMP (only DIB part) or PNG
+0 belong 0x00000100
+>9 byte 0
+>>0 byte x
+>>0 use cur-ico-dir
+>9 ubyte 0xff
+>>0 byte x
+>>0 use cur-ico-dir
+# displays number of icons and information for icon or cursor
+0 name cur-ico-dir
+# skip some Lotus 1-2-3 worksheets, CYCLE.PIC and keep Windows cursors with
+# 1st data offset = dir header size + n * dir entry size = 6 + n * 10h = ?6h
+>18 ulelong &0x00000006
+# skip remaining worksheets, because valid only for DIB image (40) or PNG image (\x89PNG)
+>>(18.l) ulelong x MS Windows
+>>>0 ubelong 0x00000100 icon resource
+# https://www.iana.org/assignments/media-types/image/vnd.microsoft.icon
+!:mime image/vnd.microsoft.icon
+#!:mime image/x-icon
+!:ext ico
+>>>>4 uleshort x - %d icon
+# plural s
+>>>>4 uleshort >1 \bs
+# 1st icon
+>>>>0x06 use ico-entry
+# 2nd icon
+>>>>4 uleshort >1
+>>>>>0x16 use ico-entry
+>>>0 ubelong 0x00000200 cursor resource
+#!:mime image/x-cur
+!:mime image/x-win-bitmap
+!:ext cur
+>>>>4 uleshort x - %d icon
+>>>>4 uleshort >1 \bs
+# 1st cursor
+>>>>0x06 use cur-entry
+#>>>>0x16 use cur-entry
+# display information of one cursor entry
+0 name cur-entry
+>0 use cur-ico-entry
+>4 uleshort x \b, hotspot @%dx
+>6 uleshort x \b%d
+# display information of one icon entry
+0 name ico-entry
+>0 use cur-ico-entry
+# normally 0 1 but also found 14
+>4 uleshort >1 \b, %d planes
+# normally 0 1 but also found some 3, 4, some 6, 8, 24, many 32, two 256
+>6 uleshort >1 \b, %d bits/pixel
+# display shared information of cursor or icon entry
+0 name cur-ico-entry
+>0 byte =0 \b, 256x
+>0 byte !0 \b, %dx
+>1 byte =0 \b256
+>1 byte !0 \b%d
+# number of colors in palette
+>2 ubyte !0 \b, %d colors
+# reserved 0 FFh
+#>3 ubyte x \b, reserved %x
+#>8 ulelong x \b, image size %d
+# offset of PNG or DIB image
+#>12 ulelong x \b, offset %#x
+# PNG header (\x89PNG)
+>(12.l) ubelong =0x89504e47
+# 1 space char after "with" to get phrase "with PNG image" by magic in ./images
+>>&-4 indirect x \b with
+# DIB image
+>(12.l) ubelong !0x89504e47
+#>>&-4 use dib-image
+
+# Windows non-animated cursors
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/CUR_(file_format)
+# Note: similar to Windows ICOn. container for BMP ( only DIB part)
+# GRR: line below is too general as it catches also Lotus 1-2-3 files
+0 belong 0x00000200
+>9 byte 0
+>>0 use cur-ico-dir
+>9 ubyte 0xff
+>>0 use cur-ico-dir
+
+# .chr files
+0 string/b PK\010\010BGI Borland font
+>4 string >\0 %s
+# then there is a copyright notice
+
+
+# .bgi files
+0 string/b pk\010\010BGI Borland device
+>4 string >\0 %s
+# then there is a copyright notice
+
+
+# Windows Recycle Bin record file (named INFO2)
+# By Abel Cheung (abelcheung AT gmail dot com)
+# Version 4 always has 280 bytes (0x118) per record, version 5 has 800 bytes
+# Since Vista uses another structure, INFO2 structure probably won't change
+# anymore. Detailed analysis in:
+# http://www.cybersecurityinstitute.biz/downloads/INFO2.pdf
+0 lelong 0x00000004
+>12 lelong 0x00000118 Windows Recycle Bin INFO2 file (Win98 or below)
+
+0 lelong 0x00000005
+>12 lelong 0x00000320 Windows Recycle Bin INFO2 file (Win2k - WinXP)
+
+# From Doug Lee via a FreeBSD pr
+9 string GERBILDOC First Choice document
+9 string GERBILDB First Choice database
+9 string GERBILCLIP First Choice database
+0 string GERBIL First Choice device file
+9 string RABBITGRAPH RabbitGraph file
+0 string DCU1 Borland Delphi .DCU file
+0 string =!<spell> MKS Spell hash list (old format)
+0 string =!<spell2> MKS Spell hash list
+# Too simple - MPi
+#0 string AH Halo(TM) bitmapped font file
+0 lelong 0x08086b70 TurboC BGI file
+0 lelong 0x08084b50 TurboC Font file
+
+# Debian#712046: The magic below identifies "Delphi compiled form data".
+# An additional source of information is available at:
+# http://www.woodmann.com/fravia/dafix_t1.htm
+0 string TPF0
+>4 pstring >\0 Delphi compiled form '%s'
+
+# tests for DBase files moved, updated and merged to database
+
+0 string PMCC Windows 3.x .GRP file
+1 string RDC-meg MegaDots
+>8 byte >0x2F version %c
+>9 byte >0x2F \b.%c file
+
+# .PIF files added by Joerg Jenderek from https://smsoft.ru/en/pifdoc.htm
+# only for windows versions equal or greater 3.0
+0x171 string MICROSOFT\ PIFEX\0 Windows Program Information File
+!:mime application/x-dosexec
+!:ext pif
+#>2 string >\0 \b, Title:%.30s
+>0x24 string >\0 \b for %.63s
+>0x65 string >\0 \b, directory=%.64s
+>0xA5 string >\0 \b, parameters=%.64s
+#>0x181 leshort x \b, offset %x
+#>0x183 leshort x \b, offsetdata %x
+#>0x185 leshort x \b, section length %x
+>0x187 search/0xB55 WINDOWS\ VMM\ 4.0\0
+>>&0x5e ubyte >0
+>>>&-1 string <PIFMGR.DLL \b, icon=%s
+#>>>&-1 string PIFMGR.DLL \b, icon=%s
+>>>&-1 string >PIFMGR.DLL \b, icon=%s
+>>&0xF0 ubyte >0
+>>>&-1 string <Terminal \b, font=%.32s
+#>>>&-1 string =Terminal \b, font=%.32s
+>>>&-1 string >Terminal \b, font=%.32s
+>>&0x110 ubyte >0
+>>>&-1 string <Lucida\ Console \b, TrueTypeFont=%.32s
+#>>>&-1 string =Lucida\ Console \b, TrueTypeFont=%.32s
+>>>&-1 string >Lucida\ Console \b, TrueTypeFont=%.32s
+#>0x187 search/0xB55 WINDOWS\ 286\ 3.0\0 \b, Windows 3.X standard mode-style
+#>0x187 search/0xB55 WINDOWS\ 386\ 3.0\0 \b, Windows 3.X enhanced mode-style
+>0x187 search/0xB55 WINDOWS\ NT\ \ 3.1\0 \b, Windows NT-style
+#>0x187 search/0xB55 WINDOWS\ NT\ \ 4.0\0 \b, Windows NT-style
+>0x187 search/0xB55 CONFIG\ \ SYS\ 4.0\0 \b +CONFIG.SYS
+#>>&06 string x \b:%s
+>0x187 search/0xB55 AUTOEXECBAT\ 4.0\0 \b +AUTOEXEC.BAT
+#>>&06 string x \b:%s
+
+# Norton Guide (.NG , .HLP) files added by Joerg Jenderek from source NG2HTML.C
+# of http://www.davep.org/norton-guides/ng2h-105.tgz
+# https://en.wikipedia.org/wiki/Norton_Guides
+0 string NG\0\001
+# only value 0x100 found at offset 2
+>2 ulelong 0x00000100 Norton Guide
+!:mime application/x-norton-guide
+# often like NORTON.NG but some times like NC.HLP
+!:ext ng/hlp
+# Title[40]
+>>8 string >\0 "%-.40s"
+#>>6 uleshort x \b, MenuCount=%u
+# szCredits[5][66]
+>>48 string >\0 \b, %-.66s
+>>114 string >\0 %-.66s
+
+# URL: https://en.wikipedia.org/wiki/Norton_Commander
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/m/msg-nc-eng.trid.xml
+# From: Joerg Jenderek
+# Note: Message file is used by executable with same main name.
+# Only tested with version 5.50 (english) and 2.01 (Windows)
+0 string Abort
+# \0 or i
+#>5 ubyte x %x
+# skip ASCII Abort text by looking for error message like in NCVIEW.MSG
+>6 search/7089 Non-DOS\ disk Norton Commander module message
+!:mime application/x-norton-msg
+!:ext msg
+
+# URL: http://www.antonis.de/dos/dos-tuts/mpdostip/html/nwdostip.htm
+# Reference: https://mark0.net/download/triddefs_xml.7z/defs/m/msg-netware-dos.trid.xml
+# From: Joerg Jenderek
+0 string DOS\ Client\ Message\ File: Novell DOS client message
+#!:mime application/octet-stream
+#!:mime application/x-novell-msg
+!:ext msg
+# look for second letter instead space character
+>26 ubyte >0x20
+# digit 1 or often main or program name like: IPXODI.COM TASKID pnwtrap DOSRqstr
+>>25 ubyte !0x20 %c
+>>>26 ubyte !0x20 \b%c
+>>>>27 ubyte !0x20 \b%c
+>>>>>28 ubyte !0x20 \b%c
+>>>>>>29 ubyte !0x20 \b%c
+>>>>>>>30 ubyte !0x20 \b%c
+>>>>>>>>31 ubyte !0x20 \b%c
+>>>>>>>>>32 ubyte !0x20 \b%c
+>>>>>>>>>>33 ubyte !0x20 \b%c
+>>>>>>>>>>>34 ubyte !0x20 \b%c
+>>>>>>>>>>>>35 ubyte !0x20 \b%c
+>>>>>>>>>>>>>36 ubyte !0x20 \b%c
+# followed by string like: 0 v.10 V1.20
+#
+# followed by ,\040Tran
+>28 search/14 ,\040Tran
+# probably translated version string like: 0 v1.00
+>>&0 string x \b, tran version %s
+# followed by Ctrl-J Ctrl-Z
+>>>&0 ubyte !0xa \b, terminated by %#2.2x
+>>>>&0 ubyte x \b%2.2x
+# Ctrl-Z
+>0x65 ubyte !0x1A \b, at 0x65 %#x
+# one
+>0x66 ubyte !0x01 \b, at 0x66 %#x
+# URL: https://en.wikipedia.org/wiki/NetWare
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dat-novell-msg.trid.xml
+# ftp://ftp.iitb.ac.in/LDP/en/NLM-HOWTO/NLM-HOWTO-single.html
+# From: Joerg Jenderek
+0 string Novell\ Message\ Librarian\ Data\ File Novell message librarian data
+#>35 string Version\ 1.00
+#>49 string COPYRIGHT\ (c)\ 1985\ by\ Novell,\ Inc.
+#>83 string \ \ All\ Rights\ Reserved
+#!:mime application/octet-stream
+#!:mime application/x-novell-msg
+!:ext msg
+#!:ext msg/dat
+# 4DOS help (.HLP) files added by Joerg Jenderek from source TPHELP.PAS
+# of https://www.4dos.info/
+# pointer,HelpID[8]=4DHnnnmm
+0 ulelong 0x48443408 4DOS help file
+>4 string x \b, version %-4.4s
+
+# old binary Microsoft (.HLP) files added by Joerg Jenderek from http://file-extension.net/seeker/file_extension_hlp
+0 ulequad 0x3a000000024e4c MS Advisor help file
+
+# HtmlHelp files (.chm)
+0 string/b ITSF\003\000\000\000\x60\000\000\000 MS Windows HtmlHelp Data
+!:mime application/vnd.ms-htmlhelp
+!:ext chm
+
+# GFA-BASIC (Wolfram Kleff)
+2 string/b GFA-BASIC3 GFA-BASIC 3 data
+
+#------------------------------------------------------------------------------
+# From Stuart Caie <kyzer@4u.net> (developer of cabextract)
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Cabinet_(file_format)
+# Reference: https://msdn.microsoft.com/en-us/library/bb267310.aspx
+# Note: verified by `7z l *.cab`
+# Microsoft Cabinet files
+0 string/b MSCF\0\0\0\0 Microsoft Cabinet archive data
+#
+# https://support.microsoft.com/en-us/help/973559/frequently-asked-questions-about-the-microsoft-support-diagnostic-tool
+# CAB with *.{diagcfg,diagpkg} is used by Microsoft Support Diagnostic Tool MSDT.EXE
+# because some archive does not have *.diag* as 1st or 2nd archive member like
+# O15CTRRemove.diagcab or AzureStorageAnalyticsLogs_global.DiagCab
+# brute looking after header for filenames with diagcfg or diagpkg extension in CFFILE section
+>0x2c search/980/c .diag \b, Diagnostic
+!:mime application/vnd.ms-cab-compressed
+!:ext diagcab
+# http://fileformats.archiveteam.org/wiki/PUZ
+# Microsoft Publisher version about 2003 has a "Pack and Go" feature that
+# bundles a Publisher document *PNG.pub with all links into a CAB
+>0x2c search/300/c png.pub\0 \b, Publisher Packed and Go
+!:mime application/vnd.ms-cab-compressed
+!:ext puz
+# ppz variant with Microsoft PowerPoint Viewer ppview32.exe to play PowerPoint presentation
+>0x2c search/17/c ppview32.exe\0 \b, PowerPoint Viewer Packed and Go
+!:mime application/vnd.ms-powerpoint
+#!:mime application/mspowerpoint
+!:ext ppz
+# URL: https://en.wikipedia.org/wiki/Windows_Desktop_Gadgets
+# Reference: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/sidebar/
+# http://win10gadgets.com/download/273/ All_CPU_Meter1.zip/All_CPU_Meter_V4.7.3.gadget
+>0x2c search/968/c gadget.xml \b, Windows Desktop Gadget
+#!:mime application/vnd.ms-cab-compressed
+# http://extension.nirsoft.net/gadget
+!:mime application/x-windows-gadget
+!:ext gadget
+# http://www.incredimail.com/
+# IncrediMail CAB contains an initialisation file "content.ini" like in im2.ims
+>0x2c search/3369/c content.ini\0 \b, IncrediMail
+!:mime application/x-incredimail
+# member Flavor.htm implies IncrediMail ecard like in tell_a_friend.imf
+>>0x2c search/83/c Flavor.htm\0 ecard
+!:ext imf
+# member Macromedia Flash data *.swf implies IncrediMail skin like in im2.ims
+>>0x2c search/211/c .swf\0 skin
+!:ext ims
+# member anim.im3 implies IncrediMail animation like in letter_fold.ima
+>>0x2c search/92/c anim.im3\0 animation
+!:ext ima
+# other IncrediMail cab archive
+>>0x2c default x
+>>>0x2c search/116/c thumb ecard, image, notifier or skin
+!:ext imf/imi/imn/ims
+# http://file-extension.net/seeker/file_extension_ime
+>>>0x2c default x emoticons or sound
+!:ext ime/imw
+# no Diagnostic, Packed and Go, Windows Desktop Gadget, IncrediMail
+>0x2c default x
+# look for 1st member name
+>>(16.l+16) ubyte x
+# From: Joerg Jenderek
+# URL: https://docs.microsoft.com/en-us/windows-hardware/drivers/install/building-device-metadata-packages
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/devicemetadata-ms.trid.xml
+>>>&-1 string PackageInfo.xml \b, Device Metadata Package
+!:mime application/vnd.ms-cab-compressed
+!:ext devicemetadata-ms
+# https://en.wikipedia.org/wiki/SNP_file_format
+>>>&-1 string/c _accrpt_.snp \b, Access report snapshot
+!:mime application/msaccess
+!:ext snp
+# https://en.wikipedia.org/wiki/Microsoft_InfoPath
+>>>&-1 string manifest.xsf \b, InfoPath Form Template
+!:mime application/vnd.ms-cab-compressed
+#!:mime application/vnd.ms-infopath
+!:ext xsn
+# https://www.cabextract.org.uk/wince_cab_format/
+# extension of DOS 8+3 name with ".000" of 1st archive member name implies Windows CE installer
+>>>&7 string =.000 \b, WinCE install
+!:mime application/vnd.ms-cab-compressed
+!:ext cab
+
+# https://support.microsoft.com/kb/934307/en-US
+# All inspected MSU contain a file with name WSUSSCAN.cab
+# that is called "Windows Update meta data" by Microsoft
+>>>&-1 string/c wsusscan.cab \b, Microsoft Standalone Update
+!:mime application/vnd.ms-cab-compressed
+!:ext msu
+>>>&-1 default x
+# look at point character of 1st archive member name for file name extension
+# GRR: search range is maybe too large and match point else where like in EN600x64.cab!
+>>>>&-1 search/255 .
+# http://www.pptfaq.com/FAQ00164_What_is_a_PPZ_file-.htm
+# PPZ were created using Pack & Go feature of PowerPoint versions 97 - 2002
+# packs optional files, a PowerPoint presentation *.ppt with optional PLAYLIST.LST to CAB
+>>>>>&0 string/c ppt\0
+>>>>>>28 uleshort >1 \b, PowerPoint Packed and Go
+!:mime application/vnd.ms-powerpoint
+#!:mime application/mspowerpoint
+!:ext ppz
+# or POWERPNT.PPT packed as POWERPNT.PP_ found on Windows 2000,XP setup CD in directory i386
+>>>>>>28 uleshort =1 \b, one packed PowerPoint
+!:mime application/vnd.ms-cab-compressed
+!:ext pp_
+# https://msdn.microsoft.com/en-us/library/windows/desktop/bb773190(v=vs.85).aspx
+# first member *.theme implies Windows 7 Theme Pack like in CommunityShowcaseAqua3.themepack
+# or Windows 8 Desktop Theme Pack like in PanoramicGlaciers.deskthemepack
+>>>>>&0 string/c theme \b, Windows
+!:mime application/x-windows-themepack
+# https://www.drewkeller.com/content/using-theme-both-windows-7-and-windows-8
+# 1st member Panoramic.theme or Panoramas.theme implies Windows 8-10 Theme Pack
+# with MTSM=RJSPBS in [MasterThemeSelector] inside *.theme
+>>>>>>(16.l+16) string =Panoram 8
+!:ext deskthemepack
+>>>>>>(16.l+16) string !Panoram 7 or 8
+!:ext themepack/deskthemepack
+>>>>>>(16.l+16) ubyte x Theme Pack
+# URL: https://en.wikipedia.org/wiki/Microsoft_OneNote#File_format
+# http://fileformats.archiveteam.org/wiki/OneNote
+# Reference: https://mark0.net/download/triddefs_xml.7z/defs/o/onepkg.trid.xml
+# 1st member name like: "Class Notes.one" "test-onenote.one" "Open Notebook.onetoc2" "Editor Öffnen.onetoc2"
+>>>>>&0 string/c one \b, OneNote Package
+!:mime application/msonenote
+!:ext onepkg
+>>>>>&0 default x
+# look for null terminator of 1st member name
+>>>>>>&0 search/255 \0
+# 2nd member name WSUSSCAN.cab like in Microsoft-Windows-MediaFeaturePack-OOB-Package.msu
+>>>>>>>&16 string/c wsusscan.cab \b, Microsoft Standalone Update
+!:mime application/vnd.ms-cab-compressed
+!:ext msu
+>>>>>>>&16 default x
+# archive with more then one file need some output in version 5.32 to avoid error message like
+# Magdir/msdos, 1138: Warning: Current entry does not yet have a description for adding a MIME type
+# Magdir/msdos, 1139: Warning: Current entry does not yet have a description for adding a EXTENSION type
+# file: could not find any valid magic files!
+>>>>>>>>28 uleshort >1 \b, many
+!:mime application/vnd.ms-cab-compressed
+!:ext cab
+# remaining archives with just one file
+>>>>>>>>28 uleshort =1
+# neither extra bytes nor cab chain implies Windows 2000,XP setup files in directory i386
+>>>>>>>>>30 uleshort =0x0000 \b, Windows 2000/XP setup
+# cut of last char of source extension and add underscore to generate extension
+# TERMCAP._ ... FXSCOUNT.H_ ... L3CODECA.AC_ ... NPDRMV2.ZI_
+!:mime application/vnd.ms-cab-compressed
+!:ext _/?_/??_
+# archive need some output like "single" in version 5.32 to avoid error messages
+>>>>>>>>>30 uleshort !0x0000 \b, single
+!:mime application/vnd.ms-cab-compressed
+!:ext cab
+# first archive name without point character
+>>>>&-1 default x
+>>>>>28 uleshort =1 \b, single
+!:mime application/vnd.ms-cab-compressed
+# on XP_CD\I386\ like: NETWORKS._ PROTOCOL._ QUOTES._ SERVICES._
+!:ext _
+>>>>>28 uleshort >1 \b, many
+!:mime application/vnd.ms-cab-compressed
+# like: HP Envy 6000 printer driver packages Full_x86.cab Full_x64.cab
+!:ext cab
+# TODO: additional extensions like
+# .xtp InfoPath Template Part
+# .lvf Logitech Video Effects Face Accessory
+>8 ulelong x \b, %u bytes
+>28 uleshort 1 \b, 1 file
+>28 uleshort >1 \b, %u files
+# Reserved fields, set to zero
+#>4 belong !0 \b, reserved1 %x
+#>12 belong !0 \b, reserved2 %x
+# offset of the first CFFILE entry coffFiles: minimal 2Ch
+>16 ulelong x \b, at %#x
+>(16.l) use cab-file
+# at least also 2nd member
+>28 uleshort >1
+>>(16.l+16) ubyte x
+>>>&0 search/255 \0
+# second member info
+>>>>&0 use cab-file
+#>20 belong !0 \b, reserved %x
+# Cabinet file format version. Currently, versionMajor = 1 and versionMinor = 3
+>24 ubeshort !0x0301 \b version %#x
+# number of CFFOLDER entries
+>26 uleshort >1 \b, %u cffolders
+# cabinet file option indicators 1~PREVIOUS, 2~NEXT, 4~reserved fields
+# only found for flags 0 1 2 3 4 not 7
+>30 uleshort >0 \b, flags %#x
+# Cabinet files have a 16-bit cabinet setID field that is designed for application use.
+# default is zero, however, the -i option of cabarc can be used to set this field
+>32 uleshort >0 \b, ID %u
+# iCabinet is number of this cabinet file in a set, where 0 for the first cabinet
+#>34 uleshort x \b, iCabinet %u
+# add one for display because humans start numbering by 1 and also fit to name of disk szDisk*
+>34 uleshort+1 x \b, number %u
+>30 uleshort &0x0004 \b, extra bytes
+# cbCFHeader optional size of per-cabinet reserved area 14h 1800h
+>>36 uleshort >0 %u in head
+# cbCFFolder is optional size of per-folder reserved area
+>>38 ubyte >0 %u in folder
+# cbCFData is optional size of per-datablock reserved area
+>>39 ubyte >0 %u in data block
+# optional per-cabinet reserved area abReserve[cbCFHeader]
+>>36 uleshort >0
+# 1st CFFOLDER after reserved area in header
+>>>(36.s+40) use cab-folder
+# no reserved area in header
+>30 uleshort ^0x0004
+# no previous and next cab archive
+>>30 uleshort =0x0000
+>>>36 use cab-folder
+# only previous cab archive
+>>30 uleshort =0x0001 \b, previous
+>>>36 use cab-anchor
+# only next cab archive
+>>30 uleshort =0x0002 \b, next
+>>>36 use cab-anchor
+# previous+next cab archive
+# can not use sub routine cab-anchor to display previous and next cabinet together
+#>>>36 use cab-anchor
+#>>>>&0 use cab-anchor
+>>30 uleshort =0x0003 \b, previous
+>>>36 string x %s
+# optional name of previous disk szDisk*
+>>>>&1 string x disk %s
+>>>>>&1 string x \b, next %s
+# optional name of previous disk szDisk*
+>>>>>>&1 string x disk %s
+>>>>>>>&1 use cab-folder
+# display filename and disk name of previous or next cabinet
+0 name cab-anchor
+# optional name of previous/next cabinet file szCabinet*[255]
+>&0 string x %s
+# optional name of previous/next disk szDisk*[255]
+>>&1 string x disk %s
+# display folder structure CFFOLDER information like compression of cabinet
+0 name cab-folder
+# offset of the CFDATA block in this folder
+#>0 ulelong x \b, coffCabStart %#x
+# number of CFDATA blocks in folder
+>4 uleshort x \b, %u datablock
+# plural s
+>4 uleshort >1 \bs
+# compression typeCompress: 0~None 1~MSZIP 0x1503~LZX:21 0x1003~LZX:16 0x0f03~LZX:15
+>6 uleshort x \b, %#x compression
+# optional per-folder reserved area
+#>8 ubequad x \b, abReserve %#llx
+# display member structure CFFILE information like member name of cabinet
+0 name cab-file
+# cbFile is uncompressed size of file in bytes
+#>0 ulelong x \b, cbFile %u
+# uoffFolderStart is uncompressed offset of file in folder
+#>4 ulelong >0 \b, uoffFolderStart %#x
+# iFolder is index into the CFFOLDER area. 0 indicates first folder in cabinet
+# define ifoldCONTINUED_FROM_PREV (0xFFFD)
+# define ifoldCONTINUED_TO_NEXT (0xFFFE)
+# define ifoldCONTINUED_PREV_AND_NEXT (0xFFFF)
+>8 uleshort >0 \b, iFolder %#x
+# date stamp for file
+>10 lemsdosdate x last modified %s
+# time stamp for file
+>12 lemsdostime x %s
+# attribs is attribute flags for file
+# define _A_RDONLY (0x01) file is read-only
+# define _A_HIDDEN (0x02) file is hidden
+# define _A_SYSTEM (0x04) file is a system file
+# define _A_ARCH (0x20) file modified since last backup
+# example http://sebastien.kirche.free.fr/pebuilder_plugins/depends.cab
+# define _A_EXEC (0x40) run after extraction
+# define _A_NAME_IS_UTF (0x80) szName[] contains UTF
+# define UNKNOWN (0x0100) undocumented or accident
+#>14 uleshort x \b, attribs %#x
+>14 uleshort >0 +
+>>14 uleshort &0x0001 \bR
+>>14 uleshort &0x0002 \bH
+>>14 uleshort &0x0004 \bS
+>>14 uleshort &0x0020 \bA
+>>14 uleshort &0x0040 \bX
+>>14 uleshort &0x0080 \bUtf
+# unknown 0x0100 flag found on one XP_CD:\I386\DRIVER.CAB
+>>14 uleshort &0x0100 \b?
+# szName is name of archive member
+>16 string x "%s"
+# next archive member name if more files
+#>>&17 string >\0 \b, NEXT NAME %-.50s
+
+# InstallShield Cabinet files
+0 string/b ISc( InstallShield Cabinet archive data
+>5 byte&0xf0 =0x60 version 6,
+>5 byte&0xf0 !0x60 version 4/5,
+>(12.l+40) lelong x %u files
+
+# Windows CE package files
+0 string/b MSCE\0\0\0\0 Microsoft WinCE install header
+>20 lelong 0 \b, architecture-independent
+>20 lelong 103 \b, Hitachi SH3
+>20 lelong 104 \b, Hitachi SH4
+>20 lelong 0xA11 \b, StrongARM
+>20 lelong 4000 \b, MIPS R4000
+>20 lelong 10003 \b, Hitachi SH3
+>20 lelong 10004 \b, Hitachi SH3E
+>20 lelong 10005 \b, Hitachi SH4
+>20 lelong 70001 \b, ARM 7TDMI
+>52 leshort 1 \b, 1 file
+>52 leshort >1 \b, %u files
+>56 leshort 1 \b, 1 registry entry
+>56 leshort >1 \b, %u registry entries
+
+
+# Windows Enhanced Metafile (EMF)
+# See msdn.microsoft.com/archive/en-us/dnargdi/html/msdn_enhmeta.asp
+# for further information.
+0 ulelong 1
+>40 string \ EMF Windows Enhanced Metafile (EMF) image data
+>>44 ulelong x version %#x
+
+
+0 string/b \224\246\056 Microsoft Word Document
+!:mime application/msword
+
+# From: "Nelson A. de Oliveira" <naoliv@gmail.com>
+# Magic type for Dell's BIOS .hdr files
+# Dell's .hdr
+0 string/b $RBU
+>23 string Dell %s system BIOS
+>5 byte 2
+>>48 byte x version %d.
+>>49 byte x \b%d.
+>>50 byte x \b%d
+>5 byte <2
+>>48 string x version %.3s
+
+# Type: Microsoft Document Imaging Format (.mdi)
+# URL: https://en.wikipedia.org/wiki/Microsoft_Document_Imaging_Format
+# From: Daniele Sempione <scrows@oziosi.org>
+# Too weak (EP)
+#0 short 0x5045 Microsoft Document Imaging Format
+
+# MS eBook format (.lit)
+0 string/b ITOLITLS Microsoft Reader eBook Data
+>8 lelong x \b, version %u
+!:mime application/x-ms-reader
+
+# Windows CE Binary Image Data Format
+# From: Dr. Jesus <j@hug.gs>
+0 string/b B000FF\n Windows Embedded CE binary image
+
+# The second byte of these signatures is a file version; I don't know what,
+# if anything, produced files with version numbers 0-2.
+# From: John Elliott <johne@seasip.demon.co.uk>
+0 string \xfc\x03\x00 Mallard BASIC program data (v1.11)
+0 string \xfc\x04\x00 Mallard BASIC program data (v1.29+)
+0 string \xfc\x03\x01 Mallard BASIC protected program data (v1.11)
+0 string \xfc\x04\x01 Mallard BASIC protected program data (v1.29+)
+
+0 string MIOPEN Mallard BASIC Jetsam data
+0 string Jetsam0 Mallard BASIC Jetsam index data
+
+# DOS backup 2.0 to 3.2
+# URL: http://fileformats.archiveteam.org/wiki/BACKUP_(MS-DOS)
+# Reference: http://www.ibiblio.org/pub/micro/pc-stuff/freedos/files/dos/restore/brtecdoc.htm
+# backupid.@@@
+
+# plausibility check for date
+0x3 ushort >1979
+>0x5 ubyte-1 <31
+>>0x6 ubyte-1 <12
+# actually 121 nul bytes
+>>>0x7 string \0\0\0\0\0\0\0\0
+>>>>0x1 ubyte x DOS 2.0 backup id file, sequence %d
+#!:mime application/octet-stream
+!:ext @@@
+>>>>0x0 ubyte 0xff \b, last disk
+
+# backed up file
+
+# skip some AppleWorks word like Tomahawk.Awp, WIN98SE-DE.vhd
+# by looking for trailing nul of maximal file name string
+0x52 ubyte 0
+# test for flag byte: FFh~complete file, 00h~split file
+# FFh -127 = -1 -127 = -128
+# 00h -127 = 0 -127 = -127
+>0 byte-127 <-126
+# plausibility check for file name length
+>>0x53 ubyte-1 <78
+# looking for terminating nul of file name string
+>>>(0x53.b+4) ubyte 0
+# looking if last char of string is valid DOS file name
+>>>>(0x53.b+3) ubyte >0x1F
+# actually 44 nul bytes
+# but sometimes garbage according to Ralf Quint. So can not be used as test
+#>0x54 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+# first char of full file name is DOS (5Ch) or UNIX (2Fh) path separator
+# only DOS variant found. UNIX variant according to V32SLASH.TXT in archive PD0315.EXE
+>>>>>5 ubyte&0x8C 0x0C
+# ./msdos (version 5.30) labeled the entry as
+# "DOS 2.0 backed up file %s, split file, sequence %d" or
+# "DOS 2.0 backed up file %s, complete file"
+>>>>>>0 ubyte x DOS 2.0-3.2 backed up
+#>>>>>>0 ubyte 0xff complete
+>>>>>>0 ubyte 0
+>>>>>>>1 uleshort x sequence %d of
+# full file name with path but without drive letter and colon stored from 0x05 til 0x52
+>>>>>>0x5 string x file %s
+#!:mime application/octet-stream
+# backup name is original filename
+#!:ext doc/exe/rar/zip
+#!:ext *
+# magic/Magdir/msdos, 1169: Warning: EXTENSION type ` *' has bad char '*'
+# file: line 1169: Bad magic entry ' *'
+# after header original file content
+>>>>>>128 indirect x \b;
+
+
+# DOS backup 3.3 to 5.x
+
+# CONTROL.nnn files
+0 string \x8bBACKUP\x20
+# actually 128 nul bytes
+>0xa string \0\0\0\0\0\0\0\0
+>>0x9 ubyte x DOS 3.3 backup control file, sequence %d
+>>0x8a ubyte 0xff \b, last disk
+
+# NB: The BACKUP.nnn files consist of the files backed up,
+# concatenated.
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/MS-DOS_date/time
+# Reference: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime
+# Note: DOS date+time format is different from formats such as Unix epoch
+# bit encoded; uses year values relative to 1980 and 2 second precision
+0 name dos-date
+# HHHHHMMMMMMSSSSS bit encoded Hour (0-23) Minute (0-59) SecondPart (*2)
+#>0 uleshort x RAW TIME [%#4.4x]
+# hour part
+#>0 uleshort/2048 x hour [%u]
+# YYYYYMMMMDDDDD bit encoded YearPart (+1980) Month (1-12) Day (1-31)
+#>2 uleshort x RAW DATE [%#4.4x]
+# day part
+>2 uleshort&0x001F x %u
+#>2 uleshort/16 x MONTH PART [%#x]
+# GRR: not working
+#>2 uleshort/16 &0x000F MONTH [%u]
+#>2 uleshort&0x01E0 x MONTH PART [%#4.4x]
+>2 uleshort&0x01E0 =0x0020 jan
+>2 uleshort&0x01E0 =0x0040 feb
+>2 uleshort&0x01E0 =0x0060 mar
+>2 uleshort&0x01E0 =0x0080 apr
+>2 uleshort&0x01E0 =0x00A0 may
+>2 uleshort&0x01E0 =0x00C0 jun
+>2 uleshort&0x01E0 =0x00E0 jul
+>2 uleshort&0x01E0 =0x0100 aug
+>2 uleshort&0x01E0 =0x0120 sep
+>2 uleshort&0x01E0 =0x0140 oct
+>2 uleshort&0x01E0 =0x0160 nov
+>2 uleshort&0x01E0 =0x0180 dec
+# year part
+>2 uleshort/512 x 1980+%u
+#
diff --git a/contrib/libs/libmagic/magic/Magdir/msooxml b/contrib/libs/libmagic/magic/Magdir/msooxml
new file mode 100644
index 0000000000..905017eb91
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/msooxml
@@ -0,0 +1,68 @@
+
+#------------------------------------------------------------------------------
+# $File: msooxml,v 1.19 2023/03/14 19:46:15 christos Exp $
+# msooxml: file(1) magic for Microsoft Office XML
+# From: Ralf Brown <ralf.brown@gmail.com>
+
+# .docx, .pptx, and .xlsx are XML plus other files inside a ZIP
+# archive. The first member file is normally "[Content_Types].xml".
+# but some libreoffice generated files put this later. Perhaps skip
+# the "[Content_Types].xml" test?
+# Since MSOOXML doesn't have anything like the uncompressed "mimetype"
+# file of ePub or OpenDocument, we'll have to scan for a filename
+# which can distinguish between the three types
+
+0 name msooxml
+>0 string word/ Microsoft Word 2007+
+!:mime application/vnd.openxmlformats-officedocument.wordprocessingml.document
+!:ext docx
+>0 string ppt/ Microsoft PowerPoint 2007+
+!:mime application/vnd.openxmlformats-officedocument.presentationml.presentation
+!:ext pptx
+>0 string xl/ Microsoft Excel 2007+
+!:mime application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
+!:ext xlsx
+>0 string visio/ Microsoft Visio 2013+
+!:mime application/vnd.ms-visio.drawing.main+xml
+>0 string AppManifest.xaml Microsoft Silverlight Application
+!:mime application/x-silverlight-app
+
+# start by checking for ZIP local file header signature
+0 string PK\003\004
+!:strength +10
+# make sure the first file is correct
+>0x1E use msooxml
+>0x1E default x
+>>0x1E regex \\[Content_Types\\]\\.xml|_rels/\\.rels|docProps|customXml
+# skip to the second local file header
+# since some documents include a 520-byte extra field following the file
+# header, we need to scan for the next header
+>>>(18.l+49) search/6000 PK\003\004
+# now skip to the *third* local file header; again, we need to scan due to a
+# 520-byte extra field following the file header
+>>>>&26 search/6000 PK\003\004
+# and check the subdirectory name to determine which type of OOXML
+# file we have. Correct the mimetype with the registered ones:
+# https://technet.microsoft.com/en-us/library/cc179224.aspx
+>>>>>&26 use msooxml
+>>>>>&26 default x
+# OpenOffice/Libreoffice orders ZIP entry differently, so check the 4th file
+>>>>>>&26 search/6000 PK\003\004
+>>>>>>>&26 use msooxml
+# Some OOXML generators add an extra customXml directory. Check another file.
+>>>>>>>&26 default x
+>>>>>>>>&26 search/6000 PK\003\004
+>>>>>>>>>&26 use msooxml
+>>>>>>>>>&26 default x Microsoft OOXML
+>>>>>>>&26 default x Microsoft OOXML
+>>>>>&26 default x Microsoft OOXML
+>>0x1E regex \\[trash\\]
+>>>&26 search/6000 PK\003\004
+>>>>&26 search/6000 PK\003\004
+>>>>>&26 use msooxml
+>>>>>&26 default x
+>>>>>>&26 search/6000 PK\003\004
+>>>>>>>&26 use msooxml
+>>>>>>>&26 default x Microsoft OOXML
+>>>>>>&26 default x Microsoft OOXML
+>>>>>&26 default x Microsoft OOXML
diff --git a/contrib/libs/libmagic/magic/Magdir/msvc b/contrib/libs/libmagic/magic/Magdir/msvc
new file mode 100644
index 0000000000..fbfa4f266f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/msvc
@@ -0,0 +1,222 @@
+
+#------------------------------------------------------------------------------
+# $File: msvc,v 1.11 2022/01/17 17:17:30 christos Exp $
+# msvc: file(1) magic for msvc
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# Microsoft visual C
+#
+# I have version 1.0
+
+# .aps
+0 string HWB\000\377\001\000\000\000 Microsoft Visual C .APS file
+
+# .ide
+#too long 0 string \102\157\162\154\141\156\144\040\103\053\053\040\120\162\157\152\145\143\164\040\106\151\154\145\012\000\032\000\002\000\262\000\272\276\372\316 MSVC .ide
+0 string \102\157\162\154\141\156\144\040\103\053\053\040\120\162\157 MSVC .ide
+
+# .res
+0 string \000\000\000\000\040\000\000\000\377 MSVC .res
+0 string \377\003\000\377\001\000\020\020\350 MSVC .res
+0 string \377\003\000\377\001\000\060\020\350 MSVC .res
+
+#.lib
+# URL: https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B
+# http://fileformats.archiveteam.org/wiki/Microsoft_Library
+# http://fileformats.archiveteam.org/wiki/OMF
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/l/lib-msvc.trid.xml
+# https://pierrelib.pagesperso-orange.fr/exec_formats/OMF_v1.1.pdf
+# Update: Joerg Jenderek
+#0 string \360\015\000\000 Microsoft Visual C library
+#0 string \360\075\000\000 Microsoft Visual C library
+#0 string \360\175\000\000 Microsoft Visual C library
+# test for RecordType~LibraryHeaderRecord=0xF0 + RecordLength=???Dh + dictionary offset is multiple of 0x200
+0 ubelong&0xFF0f80ff =0xF00d0000
+# Microsoft Visual C library (strength=70) before MIDI SysEx messages (strength=50) handled by ./sysex
+#!:strength +0
+# test for valid 2nd RecordType~Translator Header Record=THEADR=80h or LHEADR=82h
+>(1.s+3) ubyte&0xFD =0x80
+>>0 use omf-lib
+# display information about Microsoft Visual C/OMF library
+0 name omf-lib
+# RecordType~LibraryHeaderRecord=0xF0
+#>0 byte 0xF0 Microsoft Visual C library
+# the above description was used in file version 5.41
+>0 byte 0xF0 Microsoft Visual C/OMF library
+#>0 byte 0xF0 relocatable Object Module Format (OMF) libray
+#!:mime application/octet-stream
+!:mime application/x-omf-lib
+!:ext lib
+# 1st record data length like 13=0Dh 29=1Dh 61=3Dh 125=7Dh 509=01FDh ... 32765=7FFDh
+#>1 uleshort x \b, 1st record data length %u
+#>1 uleshort x \b, 1st record data length %#x
+# 2**4=16 <= RecordLength+3 = PageSize = 2**n {16 32 512 no examples 64 128 256 1024 2048 ...32768} <= 2**15=32768
+>1 uleshort+3 x \b, page size %u
+# dictionary offset like: 400h 600h a00h c00h 1200h 1800h 2400h 5600h 12800h 19200h 28a00h
+>3 ulelong x \b, at %#x dictionary
+# dictionary block a 512 bytes; the first 37 bytes correspond to the 37 buckets
+#>(3.l) ubequad x (%#16.16llx...)
+# dictionary size; length in 512-byte blocks; a prime number? like:
+# 1 2 3 4 5 6 7 9 11 13 15 16 18 21 22 23 24 25 31 50 53 89 101 117 277
+>7 uleshort x with %u block
+# plurals s
+>7 uleshort >1 \bs
+# If dictionary byte 38 (FFLAG) has the value 255, there is no space left
+>(3.l+37) ubyte <0xFF (FFLAG=%#x)
+>(3.l+37) ubyte =0xFF (FFLAG=full)
+# dictionary entry; length byte of following symbol, the following text bytes of symbol, two bytes specifies the page number
+# like: dbfntx1! DBFNTX.LIB zlibCompileFlags_ ZLIB.LIB atoi! mwlibc.lib
+>(3.l+38) pstring x 1st entry %s
+# like: 1 33 41 47 458 8783
+>>&0 uleshort x in page %u
+# library flags; 0 or 1, but WHAT IS 0x4d in MOUSE.LIB ?
+>9 ubyte >1 \b, flags %#x
+>9 ubyte =1 case sensitive
+# In the library after header comes first object module with a Library Module Header Record (LHEADR=82h)
+# but in examples Translator Header Record (THEADR=80h) which is handled identically
+>(1.s+3) ubyte x \b, 2nd record
+>(1.s+3) ubyte !0x80 (type %#x)
+#>(1.s+4) uleshort x \b, 2nd record data length %u
+# Module name often source name like "dos\crt0.asm" in mlibce.lib or "QB4UTIL.ASM" in QB4UTIL.LIB
+# or "C:\Documents and Settings\Allan Campbell\My Documents\FDOSBoot\zlib\zutil.c" in ZLIB.LIB
+# or title like "87INIT" in FP87.LIB or "ACOSASIN" in MATHC.LIB or "Copyright" in calc-bcc.lib
+>(1.s+6) pstring x "%s"
+# 2nd record checksum
+#>>&0 ubyte x checksum %#x
+# 3rd RecordType: 96h~LNAMES 88h~COMENT
+>>&1 ubyte x \b, 3rd record
+>>&1 ubyte !0x88
+>>>&-1 ubyte !0x96
+# 3rd unusual record type
+>>>>&-1 ubyte x (type %#x)
+# 3rd record is a List of Names Record (LNAMES=96h)
+>>&1 ubyte =0x96 LNAMES
+# LNAMES record length like: 2 15 19
+#>>>&0 uleshort x \b, LNAMES record length %u
+>>>&0 uleshort >2
+# 1st LNAME string length; null is valid; maximal 255
+#>>>>&0 ubyte x 1st LNAME length %u
+>>>>&0 ubyte =0
+# 2nd LNAME length like: 4 7 8 17 31
+#>>>>>&0 ubyte x 2nd LNAME length %u
+# name used for segment, class, group, overlay, etc like:
+# CODE (mwlibc.lib) _TEXT32 (JMPPM32.LIB) _OVLCODE (WOVL.LIB)
+>>>>>&0 pstring x %s
+# 3rd LNAME length like: 4 5
+#>>>>>>&0 ubyte x 3rd LNAME length %u
+# like: DATA (mwlibc.lib) CODE (JMPPM32.LIB) _TEXT (EMU87.LIB)
+>>>>>>&0 pstring x %s
+# maybe 4th LNAME length like: 4 6
+>>>>>>>&0 ubyte <44
+# like: DATA (DEBUG.LIB) DGROUP (mwlibc.lib MOUSE.LIB)
+>>>>>>>>&-1 pstring x %s
+# 3rd record is a COMMENT (Including all comment class extensions)
+>>&1 ubyte =0x88 COMMENT
+# comment record length like: 3 FLIB7M.LIB 1Bh 1Eh 23h 27h 2Bh 30h freetype-bcc.lib
+#>>>&0 uleshort x \b, record length %#x
+# real comment length = record length - 1 (comment type) - 1 (comment Class) - 1 (checksum) -1 (char count)
+# like: 2 LIBFL.LIB 4 "UUID" 5 "dscap" 6 "int386" 7 "qb4util" 8 "AMSGEXIT" 16 REXX.LIB 20 27 35 44 freetype-bcc.lib
+#>>>>&-2 uleshort-4 >0 \b, comment length %u
+# check that record contain at least comment type (1 byte), comment class (1 byte), checksum (1 byte)
+# probably always true
+>>>&0 uleshort >2
+# comment type: 80h~NP~no purge bit 40h~NL~no list bit
+#>>>>&0 ubyte !0 Type %#x
+>>>>&0 ubyte &0x80 Preserved
+# no example
+>>>>&0 ubyte &0x40 NoList
+# comment class like: 0~Translator A0~OMF extensions A3~LIBMOD A1~New OMF extensions AA~UNKNOWN
+>>>>&1 ubyte x class=%#x
+# check that comment record contains at least real content
+>>>>&-2 uleshort >3
+# Translator comment record (0); it may name the source language or translator
+>>>>>&1 ubyte =0 Translator
+#>>>>>>&0 ubyte x Translator length %u
+# like: "TC86 Borland Turbo C 2.01 " (GEMS.LIB) "TC86 Borland Turbo C++ 3.00" (CATDB.LIB)
+>>>>>>&0 pstring x "%s"
+# OMF extensions comment record (A0); first byte of commentary string identifies subtype
+>>>>>&1 ubyte =0xA0 OMF extensions
+# A0 subtype like: 1~IMPDEF
+>>>>>>&0 ubyte !1 subtype %#x
+# Import Definition Record (Comment Class A0, Subtype 01~IMPDEF)
+>>>>>>&0 ubyte 1 IMPDEF
+# ordinal flag; determines form of Entry Ident field. If nonzero (seems to be 1) Entry is ordinal
+>>>>>>>&0 ubyte !0 ordinal
+# like: IMPORT.LIB DOSCALLS.LIB mlibw.lib mwinlibc.lib REXX.LIB
+>>>>>>>>&-1 ubyte >1 %u
+# Internal Name in count, char string format; module name for the imported symbol
+# like: 7 "REXXSAA" 9 11 13 14 15 16 20 21 26 "_Z10_clip_linePdS_S_S_dddd"
+#>>>>>>>&1 ubyte x internal name length %u
+# internal module name like: _DllGetVersion DllGetVersion BezierTerminationTest Copyright
+>>>>>>>&1 pstring x %s
+# module name in count, char string format; DLL name that supplies a matching export symbol
+# like: jpeg62.dll (jpeg-bcc.lib) unrar3.dll (unrar-bcc.lib) REXX (REXX.LIB)
+>>>>>>>>&0 pstring x exported by %s
+# Entry Ident; 16-bit if ordinal flag != 0 or imported name in count, char string format if ordinal flag = 0
+# like: \0 (calc-bcc.lib) DllGetVersion (libtiff-bcc.lib) UTF8ToHtml (libxml2-bcc.lib) xslAddCall (libxslt-bcc.lib)
+#>>>>>>>>>&0 pstring >\0 entry ident %s
+# "New OMF" extensions comment (A1); indicate version of symbolic debug information
+# like: LIBFL.LIB
+>>>>>&1 ubyte =0xA1 New OMF extensions
+# symbolic debug information version n
+>>>>>>&0 ubyte x n=%u
+# symbolic debug information style like: HL~IBM PM Debugger style (LIBFL.LIB) DX~AIX style CV~Microsoft symbol and type style
+>>>>>>>&0 string HL IBM style
+>>>>>>>&0 string DX AIX style
+>>>>>>>&0 string CV Microsoft style
+# LIBMOD comment record (A3) used only by the librarian
+# Microsoft extension added for LIB version 3.07 in macro assembler (MASM 5.0)
+>>>>>&1 ubyte =0xA3 LIBMOD
+# The A3 LIBMOD record contains only the ASCII string of the module name in count char format
+#>>>>>>&0 ubyte x LIBMOD length %u
+# LIBMOD comment record module name without path and extension like:
+# qb4util (QB4UTIL.LIB) affaldiv (libh.lib) crt0 (slibc.lib) clipper (DDDRAWS.LIB) dinpdev (DINPUTS.LIB) UUID (UUID.LIB)
+>>>>>>&0 pstring x %s
+# GRR: WHAT iS THAT? AA foo comment record
+#>>>>>&1 ubyte =0xAA AA-comment
+# like: OS220
+#>>>>>>&0 string x what=%-5.5s
+#
+
+#.pch
+0 string DTJPCH0\000\022\103\006\200 Microsoft Visual C .pch
+
+# Summary: Symbol Table / Debug info used by Microsoft compilers
+# URL: https://en.wikipedia.org/wiki/Program_database
+# Reference: https://code.google.com/p/pdbparser/wiki/MSF_Format
+# Update: Joerg Jenderek
+# Note: test only for Windows XP+SP3 x86 , 8.1 x64 arm and 10.1 x86
+# info does only applies partly for older files like msvbvm50.pdb about year 2001
+0 string Microsoft\ C/C++\040
+# "Microsoft Program DataBase" by TrID
+>24 search/14 \r\n\x1A MSVC program database
+!:mime application/x-ms-pdb
+!:ext pdb
+# "MSF 7.00" "program database 2.00" for msvbvm50.pdb
+>>16 regex \([0-9.]+\) ver %s
+#>>>0x38 search/128123456 /LinkInfo \b with linkinfo
+# "MSF 7.00" variant
+>>0x1e leshort 0
+# PageSize 400h 1000h
+>>>0x20 lelong x \b, %d
+# Page Count
+>>>0x28 lelong x \b*%d bytes
+# "program database 2.00" variant
+>>0x1e leshort !0
+# PageSize 400h
+>>>0x2c lelong x \b, %d
+# Page Count for msoo-dll.pdb 4379h
+>>>0x32 leshort x \b*%d bytes
+
+# Reference: https://github.com/Microsoft/vstest/pull/856/commits/fdc7a9f074ca5a8dfeec83b1be9162bf0cf4000d
+0 string/c bsjb\001\000\001\000\000\000\000\000\f\000\000\000pdb\ v1.0 Microsoft Roslyn C# debugging symbols version 1.0
+
+#.sbr
+0 string \000\002\000\007\000 MSVC .sbr
+>5 string >\0 %s
+
+#.bsc
+0 string \002\000\002\001 MSVC .bsc
+
+#.wsp
+0 string 1.00\ .0000.0000\000\003 MSVC .wsp version 1.0000.0000
+# these seem to start with the version and contain menus
diff --git a/contrib/libs/libmagic/magic/Magdir/msx b/contrib/libs/libmagic/magic/Magdir/msx
new file mode 100644
index 0000000000..60e16569e2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/msx
@@ -0,0 +1,309 @@
+
+#------------------------------------------------------------------------------
+# msx: file(1) magic for the MSX Home Computer
+# v1.3
+# Fabio R. Schmidlin <sd-snatcher@users.sourceforge.net>
+
+############## MSX Music file formats ##############
+
+# Gigamix MGSDRV music file
+0 string/b MGS MSX Gigamix MGSDRV3 music file,
+>6 ubeshort 0x0D0A
+>>3 byte x \bv%c
+>>4 byte x \b.%c
+>>5 byte x \b%c
+>>8 string >\0 \b, title: %s
+
+1 string/b mgs2\ MSX Gigamix MGSDRV2 music file
+>6 uleshort 0x80
+>>0x2E uleshort 0
+>>>0x30 string >\0 \b, title: %s
+
+# KSS music file
+0 string/b KSCC KSS music file v1.03
+>0xE byte 0
+>>0xF byte&0x02 0 \b, soundchips: AY-3-8910, SCC(+)
+>>0xF byte&0x02 2 \b, soundchip(s): SN76489
+>>>0xF byte&0x04 4 stereo
+>>0xF byte&0x01 1 \b, YM2413
+>>0xF byte&0x08 8 \b, Y8950
+
+0 string/b KSSX KSS music file v1.20
+>0xE byte&0xEF 0
+>>0xF byte&0x40 0x00 \b, 60Hz
+>>0xF byte&0x40 0x40 \b, 50Hz
+>>0xF byte&0x02 0 \b, soundchips: AY-3-8910, SCC(+)
+>>0xF byte&0x02 0x02 \b, soundchips: SN76489
+>>>0xF byte&0x04 0x04 stereo
+>>0xF byte&0x01 0x01 \b,
+>>>0xF byte&0x18 0x00 \bYM2413
+>>>0xF byte&0x18 0x08 \bYM2413, Y8950
+>>>0xF byte&0x18 0x18 \bYM2413+Y8950 pseudostereo
+>>0xF byte&0x18 0x10 \b, Majyutsushi DAC
+
+# Moonblaster for Moonsound
+0 string/b MBMS
+>4 byte 0x10 MSX Moonblaster for MoonSound music
+
+# Music Player K-kaz
+0 string/b MPK MSX Music Player K-kaz song
+>6 ubeshort 0x0D0A
+>>3 byte x v%c
+>>4 byte x \b.%c
+>>5 byte x \b%c
+
+# I don't know why these don't work
+#0 search/0xFFFF \r\n.FM9
+#>0 search/0xFFFF \r\n#FORMAT MSX Music Player K-kaz source MML file
+#0 search/0xFFFF \r\nFM1\ \=
+#>0 search/0xFFFF \r\nPSG1\=
+#>>0 search/0xFFFF \r\nSCC1\= MSX MuSiCa MML source file
+
+# OPX Music file
+0x35 beshort 0x0d0a
+>0x7B beshort 0x0d0a
+>>0x7D byte 0x1a
+>>>0x87 uleshort 0 MSX OPX Music file
+>>>>0x86 byte 0 v1.5
+>>>>>0 string >\32 \b, title: %s
+>>>>0x86 byte 1 v2.4
+>>>>>0 string >\32 \b, title: %s
+
+# SCMD music file
+0x8B string/b SCMD
+>0xCE uleshort 0 MSX SCMD Music file
+#>>-2 uleshort 0x6a71 ; The file must end with this value. How to code this here?
+>>0x8F string >\0 \b, title: %s
+
+0 search/0xFFFF \r\n@title
+>&0 search/0xFFFF \r\n@m=[ MSX SCMD source MML file
+
+
+############## MSX image file formats ##############
+
+# MSX raw VRAM dump
+0 ubyte 0xFE
+>1 uleshort 0
+>>5 uleshort 0
+>>>3 uleshort 0x37FF MSX SC2/GRP raw image
+>>>3 uleshort 0x6A00 MSX Graph Saurus SR5 raw image
+>>>3 uleshort >0x769E
+>>>>3 uleshort <0x8000 MSX GE5/GE6 raw image
+>>>>>3 uleshort 0x7FFF \b, with sprite patterns
+>>>3 uleshort 0xD3FF MSX screen 7-12 raw image
+>>>3 uleshort 0xD400 MSX Graph Saurus SR7/SR8/SRS raw image
+
+# Graph Saurus compressed images
+0 ubyte 0xFD
+>1 uleshort 0
+>>5 uleshort 0
+>>>3 uleshort >0x013D MSX Graph Saurus compressed image
+
+# MSX G9B image file
+0 string/b G9B
+>1 uleshort 11
+>>3 uleshort >10
+>>>5 ubyte >0 MSX G9B image, depth=%d
+>>>>8 uleshort x \b, %dx
+>>>>10 uleshort x \b%d
+>>>>5 ubyte <9
+>>>>>6 ubyte 0
+>>>>>>7 ubyte x \b, codec=%d RGB color palettes
+>>>>>6 ubyte 64 \b, codec=RGB fixed color
+>>>>>6 ubyte 128 \b, codec=YJK
+>>>>>6 ubyte 192 \b, codec=YUV
+>>>>5 ubyte >8 codec=RGB fixed color
+>>>>12 ubyte 0 \b, raw
+>>>>12 ubyte 1 \b, bitbuster compression
+
+############## Other MSX file formats ##############
+
+# MSX internal ROMs
+0 ubeshort 0xF3C3
+>2 uleshort <0x4000
+>>8 ubyte 0xC3
+>>>9 uleshort <0x4000
+>>>>0x0B ubeshort 0x00C3
+>>>>>0x0D uleshort <0x4000
+>>>>>>0x0F ubeshort 0x00C3
+>>>>>>>0x11 uleshort <0x4000
+>>>>>>>>0x13 ubeshort 0x00C3
+>>>>>>>>>0x15 uleshort <0x4000
+>>>>>>>>>>0x50 ubyte 0xC3
+>>>>>>>>>>>0x51 uleshort <0x4000
+>>>>>>>>>>>>(9.s) ubyte 0xC3
+>>>>>>>>>>>>>&0 uleshort >0x4000
+>>>>>>>>>>>>>>&0 ubyte 0xC3 MSX BIOS+BASIC
+>>>>>>>>>>>>>>>0x002D ubyte+1 <3 \b. version=MSX%d
+>>>>>>>>>>>>>>>0x002D ubyte 2 \b, version=MSX2+
+>>>>>>>>>>>>>>>0x002D ubyte 3 \b, version=MSX Turbo-R
+>>>>>>>>>>>>>>>0x002D ubyte >3 \b, version=Unknown MSX %d version
+>>>>>>>>>>>>>>>0x0006 ubyte x \b, VDP.DR=%#2x
+>>>>>>>>>>>>>>>0x0007 ubyte x \b, VDP.DW=%#2x
+>>>>>>>>>>>>>>>0x002B ubyte&0xF 0 \b, charset=Japanese
+>>>>>>>>>>>>>>>0x002B ubyte&0xF 1 \b, charset=International
+>>>>>>>>>>>>>>>0x002B ubyte&0xF 2 \b, charset=Korean
+>>>>>>>>>>>>>>>0x002B ubyte&0xF >2 \b, charset=Unknown id:%d
+>>>>>>>>>>>>>>>0x002B ubyte&0x70 0x00 \b, date format=Y-M-D
+>>>>>>>>>>>>>>>0x002B ubyte&0x70 0x10 \b, date format=M-D-Y
+>>>>>>>>>>>>>>>0x002B ubyte&0x70 0x20 \b, date format=D-M-Y
+>>>>>>>>>>>>>>>0x002B ubyte&0x80 0x00 \b, vfreq=60Hz
+>>>>>>>>>>>>>>>0x002B ubyte&0x80 0x80 \b, vfreq=50Hz
+>>>>>>>>>>>>>>>0x002C ubyte&0x0F 0 \b, keyboard=Japanese
+>>>>>>>>>>>>>>>0x002C ubyte&0x0F 1 \b, keyboard=International
+>>>>>>>>>>>>>>>0x002C ubyte&0x0F 2 \b, keyboard=French
+>>>>>>>>>>>>>>>0x002C ubyte&0x0F 3 \b, keyboard=UK
+>>>>>>>>>>>>>>>0x002C ubyte&0x0F 4 \b, keyboard=German
+>>>>>>>>>>>>>>>0x002C ubyte&0x0F 5 \b, keyboard=Unknown id:%d
+>>>>>>>>>>>>>>>0x002C ubyte&0x0F 6 \b, keyboard=Spanish
+>>>>>>>>>>>>>>>0x002C ubyte&0x0F >6 \b, keyboard=Unknown id:%d
+>>>>>>>>>>>>>>>0x002C ubyte&0xF0 0x00 \b, basic=Japanese
+>>>>>>>>>>>>>>>0x002C ubyte&0xF0 0x10 \b, basic=International
+>>>>>>>>>>>>>>>0x002C ubyte&0xF0 >0x10 \b, basic=Unknown id:%d
+>>>>>>>>>>>>>>>0x002E ubyte&1 1 \b, built-in MIDI
+
+
+0 string/b CD
+>2 uleshort >0x10
+>>2 uleshort <0x4000
+>>>4 uleshort <0x4000
+>>>>6 uleshort <0x4000
+>>>>>8 ubyte 0xC3
+>>>>>>9 uleshort <0x4000
+>>>>>>>0x10 ubyte 0xC3
+>>>>>>>>0x11 uleshort <0x4000
+>>>>>>>>>0x14 ubyte 0xC3
+>>>>>>>>>>0x15 uleshort <0x4000 MSX2/2+/TR SubROM
+
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>0x5F0 ubequad 0x8282828244380000
+>>0x150 ubyte 0x38
+>>>0x170 string \20\20\20
+>>>>0x1E32 string ())
+>>>>>0x2130 ubequad 0xA5A5594924231807
+>>>>>0x2138 ubequad 0x4A4A3424488830C0 MSX Kanji Font
+
+
+
+# MSX extension ROMs
+0 string/b AB
+>2 uleshort 0x0010 MSX ROM
+>>2 uleshort x \b, init=%#4x
+>>4 uleshort >0 \b, stahdl=%#4x
+>>6 uleshort >0 \b, devhdl=%#4x
+>>8 uleshort >0 \b, bas=%#4x
+>2 uleshort 0x4010 MSX ROM
+>>2 uleshort x \b, init=%#04x
+>>4 uleshort >0 \b, stahdl=%#04x
+>>6 uleshort >0 \b, devhdl=%#04x
+>>8 uleshort >0 \b, bas=%#04x
+>2 uleshort 0x8010 MSX ROM
+>>2 uleshort x \b, init=%#04x
+>>4 uleshort >0 \b, stahdl=%#04x
+>>6 uleshort >0 \b, devhdl=%#04x
+>>8 uleshort >0 \b, bas=%#04x
+0 string/b AB\0\0
+>6 uleshort 0
+>>4 uleshort >0x400F MSX-BASIC extension ROM
+>>>4 uleshort >0 \b, stahdl=%#04x
+>>>6 uleshort >0 \b, devhdl=%#04x
+>>>0x1C string OPLL \b, MSX-Music
+>>>>0x18 string PAC2 \b (external)
+>>>>0x18 string APRL \b (internal)
+
+0 string/b AB\0\0\0\0
+>6 uleshort >0x400F MSX device BIOS
+>>6 uleshort >0 \b, devhdl=%#04x
+
+
+0 string/b AB
+#>2 string 5JSuperLAYDOCK MSX Super Laydock ROM
+#>3 string @HYDLIDE3MSX MSX Hydlide-3 ROM
+#>3 string @3\x80IA862 Golvellius MSX1 ROM
+>2 uleshort >15
+>>2 uleshort <0xC000
+>>>8 string \0\0\0\0\0\0\0\0
+>>>>(2.s&0x3FFF) uleshort >0 MSX ROM
+>>>>>0x10 string YZ\0\0\0\0 Konami Game Master 2 MSX ROM
+>>>>>0x10 string CD \b, Konami RC-
+>>>>>>0x12 ubyte x \b%d
+>>>>>>0x13 ubyte/16 x \b%d
+>>>>>>0x13 ubyte&0xF x \b%d
+>>>>>0x10 string EF \b, Konami RC-
+>>>>>>0x12 ubyte x \b%d
+>>>>>>0x13 ubyte/16 x \b%d
+>>>>>>0x13 ubyte&0xF x \b%d
+>>>>>2 uleshort x \b, init=%#04x
+>>>>>4 uleshort >0 \b, stahdl=%#04x
+>>>>>6 uleshort >0 \b, devhdl=%#04x
+>>>>>8 uleshort >0 \b, bas=%#04x
+>>>2 uleshort 0
+>>>>4 uleshort 0
+>>>>>6 uleshort 0
+>>>>>>8 uleshort >0 MSX BASIC program in ROM, bas=%#04x
+
+0x4000 string/b AB
+>0x4002 uleshort >0x400F
+>>0x400A string \0\0\0\0\0\0 MSX ROM with nonstandard page order
+>>>0x4002 uleshort x \b, init=%#04x
+>>>0x4004 uleshort >0 \b, stahdl=%#04x
+>>>0x4006 uleshort >0 \b, devhdl=%#04x
+>>>0x4008 uleshort >0 \b, bas=%#04x
+
+0x8000 string/b AB
+>0x8002 uleshort >0x400F
+>>0x800A string \0\0\0\0\0\0 MSX ROM with nonstandard page order
+>>>0x8002 uleshort x \b, init=%#04x
+>>>0x8004 uleshort >0 \b, stahdl=%#04x
+>>>0x8006 uleshort >0 \b, devhdl=%#04x
+>>>0x8008 uleshort >0 \b, bas=%#04x
+
+
+0x3C000 string/b AB
+>0x3C008 string \0\0\0\0\0\0\0\0 MSX MegaROM with nonstandard page order
+>>0x3C002 uleshort x \b, init=%#04x
+>>0x3C004 uleshort >0 \b, stahdl=%#04x
+>>0x3C006 uleshort >0 \b, devhdl=%#04x
+>>0x3C008 uleshort >0 \b, bas=%#04x
+
+# MSX BIN file
+#0 byte 0xFE
+#>1 uleshort >0x8000
+#>>3 uleshort >0x8004
+#>>>5 uleshort >0x8000 MSX BIN file
+
+# MSX-BASIC file
+0 byte 0xFF
+>3 uleshort 0x000A
+>>1 uleshort >0x8000 MSX-BASIC program
+
+# MSX .CAS file
+0 string/b \x1F\xA6\xDE\xBA\xCC\x13\x7D\x74 MSX cassette archive
+
+# Mega-Assembler file
+0 byte 0xFE
+>1 uleshort 0x0001
+>>5 uleshort 0xffff
+>>>6 byte 0x0A MSX Mega-Assembler source
+
+# Execrom Patchfile
+0 string ExecROM\ patchfile\x1A MSX ExecROM patchfile
+>0x12 ubyte/16 x v%d
+>0x12 ubyte&0xF x \b.%d
+>0x13 ubyte x \b, contains %d patches
+
+# Konami's King's Valley-2 custom stage (ELG file)
+4 uleshort 0x0900
+>0xF byte 1
+>>0x14 byte 0
+>>>0x1E string \040\040\040
+>>>>0x23 byte 1
+>>>>>0x25 byte 0
+>>>>>>0x15 string >\x30
+>>>>>>>0x15 string <\x5A Konami King's Valley-2 custom stage, title: "%-8.8s"
+>>>>>>>>0x1D byte <32 \b, theme: %d
+
+# Metal Gear 1 savegame
+#0x4F string \x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF
+#>>0x60 string \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF
+#>>>0x7B string \0x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00 Metal Gear 1 savegame
diff --git a/contrib/libs/libmagic/magic/Magdir/mup b/contrib/libs/libmagic/magic/Magdir/mup
new file mode 100644
index 0000000000..05b9471b07
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/mup
@@ -0,0 +1,24 @@
+
+# ------------------------------------------------------------------------
+# $File: mup,v 1.5 2017/03/17 21:35:28 christos Exp $
+# mup: file(1) magic for Mup (Music Publisher) input file.
+#
+# From: Abel Cheung <abel (@) oaka.org>
+#
+# NOTE: This header is mainly proposed in the Arkkra mailing list,
+# and is not a mandatory header because of old mup input file
+# compatibility. Noteedit also use mup format, but is not forcing
+# user to use any header as well.
+#
+0 search/1 //!Mup Mup music publication program input text
+>6 string -Arkkra (Arkkra)
+>>13 string -
+>>>16 string .
+>>>>14 string x \b, need V%.4s
+>>>15 string .
+>>>>14 string x \b, need V%.3s
+>6 string -
+>>9 string .
+>>>7 string x \b, need V%.4s
+>>8 string .
+>>>7 string x \b, need V%.3s
diff --git a/contrib/libs/libmagic/magic/Magdir/music b/contrib/libs/libmagic/magic/Magdir/music
new file mode 100644
index 0000000000..ad8da65938
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/music
@@ -0,0 +1,17 @@
+#------------------------------------------------------------------------------
+# $File: music,v 1.1 2011/11/25 03:28:17 christos Exp $
+# music: file (1) magic for music formats
+
+# BWW format used by Bagpipe Music Writer Gold by Robert MacNeil Musicworks
+# and Bagpipe Writer by Doug Wickstrom
+#
+0 string Bagpipe Bagpipe
+>8 string Reader Reader
+>>15 string >\0 (version %.3s)
+>8 string Music\ Writer Music Writer
+>>20 string :
+>>>21 string >\0 (version %.3s)
+>>21 string Gold Gold
+>>>25 string :
+>>>>26 string >\0 (version %.3s)
+
diff --git a/contrib/libs/libmagic/magic/Magdir/nasa b/contrib/libs/libmagic/magic/Magdir/nasa
new file mode 100644
index 0000000000..de3545f808
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/nasa
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# nasa: file(1) magic
+
+# From: Barry Carter <carter.barry@gmail.com>
+0 string DAF/SPK NASA SPICE file (binary format)
+0 string DAFETF\ NAIF\ DAF\ ENCODED NASA SPICE file (transfer format)
diff --git a/contrib/libs/libmagic/magic/Magdir/natinst b/contrib/libs/libmagic/magic/Magdir/natinst
new file mode 100644
index 0000000000..7a55ddecdf
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/natinst
@@ -0,0 +1,24 @@
+
+#-----------------------------------------------------------------------------
+# $File: natinst,v 1.6 2014/06/03 19:17:27 christos Exp $
+# natinst: file(1) magic for National Instruments Code Files
+
+#
+# From <egamez@fcfm.buap.mx> Enrique Gamez-Flores
+# version 1
+# Many formats still missing, we use, for the moment LabVIEW
+# We guess VXI format file. VISA, LabWindowsCVI, BridgeVIEW, etc, are missing
+#
+0 string RSRC National Instruments,
+# Check if it's a LabVIEW File
+>8 string LV LabVIEW File,
+# Check which kind of file it is
+>>10 string SB Code Resource File, data
+>>10 string IN Virtual Instrument Program, data
+>>10 string AR VI Library, data
+# This is for Menu Libraries
+>8 string LMNULBVW Portable File Names, data
+# This is for General Resources
+>8 string rsc Resources File, data
+# This is for VXI Package
+0 string VMAP National Instruments, VXI File, data
diff --git a/contrib/libs/libmagic/magic/Magdir/ncr b/contrib/libs/libmagic/magic/Magdir/ncr
new file mode 100644
index 0000000000..21b09aba5c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ncr
@@ -0,0 +1,49 @@
+
+#------------------------------------------------------------------------------
+# $File: ncr,v 1.8 2014/04/30 21:41:02 christos Exp $
+# ncr: file(1) magic for NCR Tower objects
+#
+# contributed by
+# Michael R. Wayne *** TMC & Associates *** INTERNET: wayne@ford-vax.arpa
+# uucp: {philabs | pyramid} !fmsrl7!wayne OR wayne@fmsrl7.UUCP
+#
+0 beshort 000610 Tower/XP rel 2 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000615 Tower/XP rel 2 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000620 Tower/XP rel 3 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000625 Tower/XP rel 3 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000630 Tower32/600/400 68020 object
+>12 belong >0 not stripped
+>20 beshort 0407 executable
+>20 beshort 0410 pure executable
+>22 beshort >0 - version %d
+0 beshort 000640 Tower32/800 68020
+>18 beshort &020000 w/68881 object
+>18 beshort &040000 compatible object
+>18 beshort &060000 object
+>20 beshort 0407 executable
+>20 beshort 0413 pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
+0 beshort 000645 Tower32/800 68010
+>18 beshort &040000 compatible object
+>18 beshort &060000 object
+>20 beshort 0407 executable
+>20 beshort 0413 pure executable
+>12 belong >0 not stripped
+>22 beshort >0 - version %d
diff --git a/contrib/libs/libmagic/magic/Magdir/netbsd b/contrib/libs/libmagic/magic/Magdir/netbsd
new file mode 100644
index 0000000000..77e64f0b2e
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/netbsd
@@ -0,0 +1,251 @@
+
+#------------------------------------------------------------------------------
+# $File: netbsd,v 1.26 2019/01/01 03:11:23 christos Exp $
+# netbsd: file(1) magic for NetBSD objects
+#
+# All new-style magic numbers are in network byte order.
+# The old-style magic numbers are indistinguishable from the same magic
+# numbers used in other systems, and are handled, for all those systems,
+# in aout.
+#
+
+0 name netbsd-detail
+>20 lelong x @%#x
+>4 lelong >0 \b+T=%d
+>8 lelong >0 \b+D=%d
+>12 lelong >0 \b+B=%d
+>16 lelong >0 \b+S=%d
+>24 lelong >0 \b+TR=%d
+>28 lelong >0 \b+TD=%d
+
+0 name netbsd-4096
+>0 byte &0x80
+>>20 lelong <4096 shared library
+>>20 lelong =4096 dynamically linked executable
+>>20 lelong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+
+0 name netbsd-8192
+>0 byte &0x80
+>>20 lelong <8192 shared library
+>>20 lelong =8192 dynamically linked executable
+>>20 lelong >8192 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+>0 use netbsd-detail
+
+0 name netbsd-normal
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80
+>>0 byte &0x40 position independent
+>>20 lelong !0 executable
+>>20 lelong =0 object file
+>16 lelong >0 not stripped
+>0 use netbsd-detail
+
+0 name netbsd-pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 lelong >0 not stripped
+>0 use netbsd-detail
+
+0 name netbsd-core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+0 belong&0377777777 041400413 a.out NetBSD/i386 demand paged
+>0 use netbsd-4096
+
+0 belong&0377777777 041400410 a.out NetBSD/i386 pure
+>0 use netbsd-pure
+
+0 belong&0377777777 041400407 a.out NetBSD/i386
+>0 use netbsd-normal
+
+0 belong&0377777777 041400507 a.out NetBSD/i386 core
+>0 use netbsd-core
+
+0 belong&0377777777 041600413 a.out NetBSD/m68k demand paged
+>0 use \^netbsd-8192
+
+0 belong&0377777777 041600410 a.out NetBSD/m68k pure
+>0 use \^netbsd-pure
+
+0 belong&0377777777 041600407 a.out NetBSD/m68k
+>0 use \^netbsd-normal
+
+0 belong&0377777777 041600507 a.out NetBSD/m68k core
+>0 use \^netbsd-core
+
+0 belong&0377777777 042000413 a.out NetBSD/m68k4k demand paged
+>0 use \^netbsd-4096
+
+0 belong&0377777777 042000410 a.out NetBSD/m68k4k pure
+>0 use \^netbsd-pure
+
+0 belong&0377777777 042000407 a.out NetBSD/m68k4k
+>0 use \^netbsd-normal
+
+0 belong&0377777777 042000507 a.out NetBSD/m68k4k core
+>0 use \^netbsd-core
+
+0 belong&0377777777 042200413 a.out NetBSD/ns32532 demand paged
+>0 use netbsd-4096
+
+0 belong&0377777777 042200410 a.out NetBSD/ns32532 pure
+>0 use netbsd-pure
+
+0 belong&0377777777 042200407 a.out NetBSD/ns32532
+>0 use netbsd-normal
+
+0 belong&0377777777 042200507 a.out NetBSD/ns32532 core
+>0 use netbsd-core
+
+0 belong&0377777777 045200507 a.out NetBSD/powerpc core
+>0 use netbsd-core
+
+0 belong&0377777777 042400413 a.out NetBSD/SPARC demand paged
+>0 use \^netbsd-8192
+
+0 belong&0377777777 042400410 a.out NetBSD/SPARC pure
+>0 use \^netbsd-pure
+
+0 belong&0377777777 042400407 a.out NetBSD/SPARC
+>0 use \^netbsd-normal
+
+0 belong&0377777777 042400507 a.out NetBSD/SPARC core
+>0 use \^netbsd-core
+
+0 belong&0377777777 042600413 a.out NetBSD/pmax demand paged
+>0 use netbsd-4096
+
+0 belong&0377777777 042600410 a.out NetBSD/pmax pure
+>0 use \^netbsd-pure
+
+0 belong&0377777777 042600407 a.out NetBSD/pmax
+>0 use netbsd-normal
+
+0 belong&0377777777 042600507 a.out NetBSD/pmax core
+>0 use netbsd-core
+
+0 belong&0377777777 043000413 a.out NetBSD/vax 1k demand paged
+>0 use netbsd-4096
+
+0 belong&0377777777 043000410 a.out NetBSD/vax 1k pure
+>0 use netbsd-pure
+
+0 belong&0377777777 043000407 a.out NetBSD/vax 1k
+>0 use netbsd-normal
+
+0 belong&0377777777 043000507 a.out NetBSD/vax 1k core
+>0 use netbsd-core
+
+0 belong&0377777777 045400413 a.out NetBSD/vax 4k demand paged
+>0 use netbsd-4096
+
+0 belong&0377777777 045400410 a.out NetBSD/vax 4k pure
+>0 use netbsd-pure
+
+0 belong&0377777777 045400407 a.out NetBSD/vax 4k
+>0 use netbsd-normal
+
+0 belong&0377777777 045400507 a.out NetBSD/vax 4k core
+>0 use netbsd-core
+
+# NetBSD/alpha does not support (and has never supported) a.out objects,
+# so no rules are provided for them. NetBSD/alpha ELF objects are
+# dealt with in "elf".
+0 lelong 0x00070185 ECOFF NetBSD/alpha binary
+>10 leshort 0x0001 not stripped
+>10 leshort 0x0000 stripped
+0 belong&0377777777 043200507 a.out NetBSD/alpha core
+>12 string >\0 from '%s'
+>32 lelong !0 (signal %d)
+
+0 belong&0377777777 043400413 a.out NetBSD/mips demand paged
+>0 use \^netbsd-8192
+
+>16 belong >0 not stripped
+0 belong&0377777777 043400410 a.out NetBSD/mips pure
+>0 use netbsd-pure
+
+0 belong&0377777777 043400407 a.out NetBSD/mips
+>0 use netbsd-normal
+
+0 belong&0377777777 043400507 a.out NetBSD/mips core
+>0 use netbsd-core
+
+0 belong&0377777777 043600413 a.out NetBSD/arm32 demand paged
+>0 use netbsd-4096
+
+0 belong&0377777777 043600410 a.out NetBSD/arm32 pure
+>0 use netbsd-pure
+
+0 belong&0377777777 043600407 a.out NetBSD/arm32
+>0 use netbsd-normal
+
+# NetBSD/arm26 has always used ELF objects, but it shares a core file
+# format with NetBSD/arm32.
+0 belong&0377777777 043600507 a.out NetBSD/arm core
+>0 use netbsd-core
+
+# Kernel core dump format
+0 belong&0x0000ffff 0x00008fca NetBSD kernel core file
+>0 belong&0x03ff0000 0x00000000 \b, Unknown
+>0 belong&0x03ff0000 0x00010000 \b, sun 68010/68020
+>0 belong&0x03ff0000 0x00020000 \b, sun 68020
+>0 belong&0x03ff0000 0x00640000 \b, 386 PC
+>0 belong&0x03ff0000 0x00860000 \b, i386 BSD
+>0 belong&0x03ff0000 0x00870000 \b, m68k BSD (8K pages)
+>0 belong&0x03ff0000 0x00880000 \b, m68k BSD (4K pages)
+>0 belong&0x03ff0000 0x00890000 \b, ns32532 BSD
+>0 belong&0x03ff0000 0x008a0000 \b, SPARC/32 BSD
+>0 belong&0x03ff0000 0x008b0000 \b, pmax BSD
+>0 belong&0x03ff0000 0x008c0000 \b, vax BSD (1K pages)
+>0 belong&0x03ff0000 0x008d0000 \b, alpha BSD
+>0 belong&0x03ff0000 0x008e0000 \b, mips BSD (Big Endian)
+>0 belong&0x03ff0000 0x008f0000 \b, arm6 BSD
+>0 belong&0x03ff0000 0x00900000 \b, m68k BSD (2K pages)
+>0 belong&0x03ff0000 0x00910000 \b, sh3 BSD
+>0 belong&0x03ff0000 0x00950000 \b, ppc BSD (Big Endian)
+>0 belong&0x03ff0000 0x00960000 \b, vax BSD (4K pages)
+>0 belong&0x03ff0000 0x00970000 \b, mips1 BSD
+>0 belong&0x03ff0000 0x00980000 \b, mips2 BSD
+>0 belong&0x03ff0000 0x00990000 \b, m88k BSD
+>0 belong&0x03ff0000 0x00920000 \b, parisc BSD
+>0 belong&0x03ff0000 0x009b0000 \b, sh5/64 BSD
+>0 belong&0x03ff0000 0x009c0000 \b, SPARC/64 BSD
+>0 belong&0x03ff0000 0x009d0000 \b, amd64 BSD
+>0 belong&0x03ff0000 0x009e0000 \b, sh5/32 BSD
+>0 belong&0x03ff0000 0x009f0000 \b, ia64 BSD
+>0 belong&0x03ff0000 0x00b70000 \b, aarch64 BSD
+>0 belong&0x03ff0000 0x00b80000 \b, or1k BSD
+>0 belong&0x03ff0000 0x00b90000 \b, Risk-V BSD
+>0 belong&0x03ff0000 0x00c80000 \b, hp200 BSD
+>0 belong&0x03ff0000 0x012c0000 \b, hp300 BSD
+>0 belong&0x03ff0000 0x020b0000 \b, hp800 HP-UX
+>0 belong&0x03ff0000 0x020c0000 \b, hp200/hp300 HP-UX
+>0 belong&0xfc000000 0x04000000 \b, CPU
+>0 belong&0xfc000000 0x08000000 \b, DATA
+>0 belong&0xfc000000 0x10000000 \b, STACK
+>4 leshort x \b, (headersize = %d
+>6 leshort x \b, segmentsize = %d
+>8 lelong x \b, segments = %d)
+
+# little endian only for now.
+0 name ktrace
+>4 leshort 7
+>>6 leshort <3 NetBSD ktrace file version %d
+>>>12 string x from %s
+>>>56 string x \b, emulation %s
+>>>8 lelong <65536 \b, pid=%d
+
+56 string netbsd
+>0 use ktrace
+56 string linux
+>0 use ktrace
+56 string sunos
+>0 use ktrace
+56 string hpux
+>0 use ktrace
diff --git a/contrib/libs/libmagic/magic/Magdir/netscape b/contrib/libs/libmagic/magic/Magdir/netscape
new file mode 100644
index 0000000000..0e1ca61334
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/netscape
@@ -0,0 +1,26 @@
+
+#------------------------------------------------------------------------------
+# $File: netscape,v 1.8 2017/03/17 21:35:28 christos Exp $
+# netscape: file(1) magic for Netscape files
+# "H. Nanosecond" <aldomel@ix.netcom.com>
+# version 3 and 4 I think
+#
+
+# Netscape Address book .nab
+0 string \000\017\102\104\000\000\000\000\000\000\001\000\000\000\000\002\000\000\000\002\000\000\004\000 Netscape Address book
+
+# Netscape Communicator address book
+0 string \000\017\102\111 Netscape Communicator address book
+
+# .snm Caches
+0 string #\ Netscape\ folder\ cache Netscape folder cache
+0 string \000\036\204\220\000 Netscape folder cache
+# .n2p
+# Net 2 Phone
+#0 string 123\130\071\066\061\071\071\071\060\070\061\060\061\063\060
+0 string SX961999 Net2phone
+
+#
+#This is files ending in .art, FIXME add more rules
+0 string JG\004\016\0\0\0\0 AOL ART image
+0 string JG\003\016\0\0\0\0 AOL ART image
diff --git a/contrib/libs/libmagic/magic/Magdir/netware b/contrib/libs/libmagic/magic/Magdir/netware
new file mode 100644
index 0000000000..089a243644
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/netware
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# $File: netware,v 1.5 2020/09/04 16:30:51 christos Exp $
+# netware: file(1) magic for NetWare Loadable Modules (NLMs)
+# From: Mads Martin Joergensen <mmj@suse.de>
+# URL: https://en.wikipedia.org/wiki/NetWare_Loadable_Module
+
+0 string NetWare\ Loadable\ Module NetWare Loadable Module
+#!:mime application/octet-stream
+!:ext nlm
+
diff --git a/contrib/libs/libmagic/magic/Magdir/news b/contrib/libs/libmagic/magic/Magdir/news
new file mode 100644
index 0000000000..eea8aed765
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/news
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: news,v 1.6 2009/09/19 16:28:11 christos Exp $
+# news: file(1) magic for SunOS NeWS fonts (not "news" as in "netnews")
+#
+0 string StartFontMetrics ASCII font metrics
+0 string StartFont ASCII font bits
+0 belong 0x137A2944 NeWS bitmap font
+0 belong 0x137A2947 NeWS font family
+0 belong 0x137A2950 scalable OpenFont binary
+0 belong 0x137A2951 encrypted scalable OpenFont binary
+8 belong 0x137A2B45 X11/NeWS bitmap font
+8 belong 0x137A2B48 X11/NeWS font family
diff --git a/contrib/libs/libmagic/magic/Magdir/nifty b/contrib/libs/libmagic/magic/Magdir/nifty
new file mode 100644
index 0000000000..151d869872
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/nifty
@@ -0,0 +1,202 @@
+
+#------------------------------------------------------------------------------
+# $File: nifty,v 1.1 2022/02/14 16:51:15 christos Exp $
+# file(1) magic for the NIfTI file format
+
+# Type: NIfTI, Neuroimaging file format
+# URL: https://nifti.nimh.nih.gov/
+# From: Yann Leprince <yann.leprince@cea.fr>, 2022
+
+344 string n+1\0 NIfTI-1 neuroimaging data,
+!:mime image/x.nifti
+!:ext nii
+>0 use nifti1
+344 string ni1\0 NIfTI-1 neuroimaging data header,
+!:mime image/x.nifti
+!:ext hdr
+>0 use nifti1
+
+4 string n+2\0\r\n\032\n NIfTI-2 neuroimaging data,
+!:mime image/x.nifti
+!:ext nii
+>0 use nifti2
+4 string ni2\0\r\n\032\n NIfTI-2 neuroimaging data header,
+!:mime image/x.nifti
+!:ext hdr
+>0 use nifti2
+
+# Main subroutine for NIfTI-1
+0 name nifti1
+>0 clear x
+>0 lelong =348 little endian
+>>70 use nifti-datatype-le
+>>112 lefloat !0 with scaling
+>>0 use nifti1-dim-le
+>>252 leshort >0 \b, with qform
+>>>252 use xform-code-nifti1-le
+>>254 leshort >0 \b, with sform
+>>>254 use xform-code-nifti1-le
+>>136 string >\0 \b, description: %s
+>0 belong =348 big endian
+>>70 use \^nifti-datatype-le
+>>112 befloat !0 with scaling
+>>0 use \^nifti1-dim-le
+>>252 beshort >0 \b, with qform
+>>>252 use \^xform-code-nifti1-le
+>>254 beshort >0 \b, with sform
+>>>254 use \^xform-code-nifti1-le
+>>136 string >\0 \b, description: %s
+>0 default x
+>>0 long x invalid sizeof_hdr=%d
+
+# Main subroutine for NIfTI-2
+0 name nifti2
+>0 clear x
+>0 lelong =540 little endian
+>>12 use nifti-datatype-le
+>>176 lefloat !0 with scaling
+>>0 use nifti2-dim-le
+>>344 lelong >0 \b, with qform
+>>>344 use xform-code-nifti2-le
+>>348 lelong >0 \b, with sform
+>>>348 use xform-code-nifti2-le
+>>240 string >\0 \b, description: %s
+>0 belong =540 big endian
+>>12 use \^nifti-datatype-le
+>>176 befloat !0 with scaling
+>>0 use \^nifti2-dim-le
+>>344 lelong >0 \b, with qform
+>>>344 use \^xform-code-nifti2-le
+>>348 lelong >0 \b, with sform
+>>>348 use \^xform-code-nifti2-le
+>>240 string >\0 \b, description: %s
+>0 default x
+>>0 long x invalid sizeof_hdr=%d
+
+
+# Other subroutines for details of NIfTI files
+
+0 name nifti-datatype-le
+>0 clear x
+>0 leshort =1 \b, binary datatype
+>0 leshort =2 \b, uint8 datatype
+>0 leshort =4 \b, int16 datatype
+>0 leshort =8 \b, int32 datatype
+>0 leshort =16 \b, float32 datatype
+>0 leshort =32 \b, complex64 datatype
+>0 leshort =64 \b, float64 datatype
+>0 leshort =128 \b, RGB24 datatype
+>0 leshort =256 \b, int8 datatype
+>0 leshort =512 \b, uint16 datatype
+>0 leshort =768 \b, uint32 datatype
+>0 leshort =1024 \b, int64 datatype
+>0 leshort =1280 \b, uint64 datatype
+>0 leshort =1536 \b, float128 datatype
+>0 leshort =1792 \b, complex128 datatype
+>0 leshort =2048 \b, complex256 datatype
+>0 leshort =2304 \b, RGBA32 datatype
+>0 default x
+>>0 leshort x \b, unknown datatype 0x%x
+>>2 leshort x (%d bits/pixel)
+
+0 name nifti1-dim-le
+>0 clear x
+>40 leshort <0 \b, INVALID dim[0]=%d
+>40 leshort >7 \b, INVALID dim[0]=%d
+>0 default x
+>>40 leshort x \b, %d-dimensional (size
+>>42 leshort x %d
+>>40 leshort >1
+>>>44 leshort x \bx%d
+>>40 leshort >2
+>>>46 leshort x \bx%d
+>>40 leshort >3
+>>>48 leshort x \bx%d
+>>40 leshort >4
+>>>50 leshort x \bx%d
+>>40 leshort >5
+>>>52 leshort x \bx%d
+>>40 leshort >6
+>>>54 leshort x \bx%d
+>>80 lefloat x \b, voxel size %f
+>>40 leshort >1
+>>>84 lefloat x x %f
+>>40 leshort >2
+>>>88 lefloat x x %f
+>>123 use nifti1-xyz-unit
+>>40 leshort >3
+>>>92 lefloat x x %f
+>>>123 use nifti1-t-unit
+>>40 leshort x \b)
+
+0 name nifti2-dim-le
+>0 clear x
+>16 lequad <0 \b, INVALID dim[0]=%lld
+>16 lequad >7 \b, INVALID dim[0]=%lld
+>0 default x
+>>16 lequad x \b, %lld-dimensional (size
+>>24 lequad x %lld
+>>16 lequad >1
+>>>32 lequad x \bx%lld
+>>16 lequad >2
+>>>40 lequad x \bx%lld
+>>16 lequad >3
+>>>48 lequad x \bx%lld
+>>16 lequad >4
+>>>56 lequad x \bx%lld
+>>16 lequad >5
+>>>64 lequad x \bx%lld
+>>16 lequad >6
+>>>72 lequad x \bx%lld,
+>>112 ledouble x \b, voxel size %f
+>>16 lequad >1
+>>>120 ledouble x x %f
+>>16 lequad >2
+>>>128 ledouble x x %f
+>>500 use nifti2-xyz-unit
+>>16 lequad >3
+>>>136 ledouble x x %f
+>>>500 use nifti2-t-unit
+>>16 lequad x \b)
+
+0 name xform-code-nifti1-le
+>0 leshort =1 to scanner-based coordinates
+>0 leshort =2 to aligned coordinates
+>0 leshort =3 to Talairach coordinates
+>0 leshort =4 to MNI152 coordinates
+>0 leshort =5 to template coordinates
+
+0 name xform-code-nifti2-le
+>0 lelong =1 to scanner-based coordinates
+>0 lelong =2 to aligned coordinates
+>0 lelong =3 to Talairach coordinates
+>0 lelong =4 to MNI152 coordinates
+>0 lelong =5 to template coordinates
+
+0 name nifti1-xyz-unit
+>0 byte &0x01
+>>0 byte ^0x02 m
+>>0 byte &0x02 micron
+>0 byte ^0x01
+>>0 byte &0x02 mm
+
+0 name nifti1-t-unit
+>0 byte &0x08
+>>0 byte ^0x10 s
+>>0 byte &0x10 ms
+>0 byte ^0x08
+>>0 byte &0x10 microsecond
+
+0 name nifti2-xyz-unit
+>0 lelong &0x01
+>>0 lelong ^0x02 m
+>>0 lelong &0x02 micron
+>0 lelong ^0x01
+>>0 lelong &0x02 mm
+
+0 name nifti2-t-unit
+>0 lelong &0x08
+>>0 lelong ^0x10 s
+>>0 lelong &0x10 ms
+>0 lelong ^0x08
+>>0 lelong &0x10 microsecond
diff --git a/contrib/libs/libmagic/magic/Magdir/nim-lang b/contrib/libs/libmagic/magic/Magdir/nim-lang
new file mode 100644
index 0000000000..bc2cf987c7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/nim-lang
@@ -0,0 +1,29 @@
+
+#------------------------------------------------------------------------------
+# $File: nim-lang,v 1.3 2021/07/06 12:34:06 christos Exp $
+# nim-lang: file(1) magic for nim
+# URL: https://nim-lang.org/
+
+0 search/8192 import
+>&0 search/64 os
+>>&0 use nim1
+>&0 default x
+>>&0 search/64 osproc
+>>>&0 use nim1
+>>&0 default x
+>>>&0 search/64 strutils
+>>>>&0 use nim1
+
+0 name nim1
+>&0 search/8192 proc
+>>&0 use nim2
+>&0 default x
+>>&0 search/8192 template
+>>>&0 use nim2
+>>&0 default x
+>>>&0 search/8192 let
+>>>>&0 use nim2
+
+0 name nim2
+>&0 search/8192 when Nim source code
+!:ext nim
diff --git a/contrib/libs/libmagic/magic/Magdir/nitpicker b/contrib/libs/libmagic/magic/Magdir/nitpicker
new file mode 100644
index 0000000000..bea96c3e74
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/nitpicker
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: nitpicker,v 1.8 2019/04/19 00:42:27 christos Exp $
+# nitpicker: file(1) magic for Flowfiles.
+# From: Christian Jachmann <C.Jachmann@gmx.net> https://www.nitpicker.de
+0 string NPFF NItpicker Flow File
+>4 byte x V%d.
+>5 byte x %d
+>6 bedate x started: %s
+>10 bedate x stopped: %s
+>14 belong x Bytes: %u
+>18 belong x Bytes1: %u
+>22 belong x Flows: %u
+>26 belong x Pkts: %u
diff --git a/contrib/libs/libmagic/magic/Magdir/numpy b/contrib/libs/libmagic/magic/Magdir/numpy
new file mode 100644
index 0000000000..c1520dd5df
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/numpy
@@ -0,0 +1,9 @@
+
+#------------------------------------------------------------------------------
+# $File: numpy,v 1.1 2019/05/09 16:24:36 christos Exp $
+# numpy: file(1) magic for NumPy array binary serialization format
+# Reference: https://docs.scipy.org/doc/numpy/reference/generated/numpy.lib.format.html
+0 string \x93NUMPY NumPy array,
+>6 ubyte x version %d
+>7 ubyte x \b.%d,
+>8 uleshort x header length %d
diff --git a/contrib/libs/libmagic/magic/Magdir/oasis b/contrib/libs/libmagic/magic/Magdir/oasis
new file mode 100644
index 0000000000..45ad6d137d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/oasis
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: oasis,v 1.2 2014/06/03 19:17:27 christos Exp $
+# OASIS
+# Summary: OASIS stream file
+# Long description: Open Artwork System Interchange Standard
+# File extension: .oas
+# Full name: Ben Cowley (bcowley@broadcom.com)
+# Philip Dixon (pdixon@broadcom.com)
+# Reference: http://www.wrcad.com/oasis/oasis-3626-042303-draft.pdf
+# (see page 3)
+0 string %SEMI-OASIS\r\n OASIS Stream file
diff --git a/contrib/libs/libmagic/magic/Magdir/ocaml b/contrib/libs/libmagic/magic/Magdir/ocaml
new file mode 100644
index 0000000000..3ec3100c6d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ocaml
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: ocaml,v 1.5 2010/09/20 18:55:20 rrt Exp $
+# ocaml: file(1) magic for Objective Caml files.
+0 string Caml1999 OCaml
+>8 string X exec file
+>8 string I interface file (.cmi)
+>8 string O object file (.cmo)
+>8 string A library file (.cma)
+>8 string Y native object file (.cmx)
+>8 string Z native library file (.cmxa)
+>8 string M abstract syntax tree implementation file
+>8 string N abstract syntax tree interface file
+>9 string >\0 (Version %3.3s)
diff --git a/contrib/libs/libmagic/magic/Magdir/octave b/contrib/libs/libmagic/magic/Magdir/octave
new file mode 100644
index 0000000000..49ea3e73e0
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/octave
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# $File: octave,v 1.4 2009/09/19 16:28:11 christos Exp $
+# octave binary data file(1) magic, from Dirk Eddelbuettel <edd@debian.org>
+0 string Octave-1-L Octave binary data (little endian)
+0 string Octave-1-B Octave binary data (big endian)
diff --git a/contrib/libs/libmagic/magic/Magdir/ole2compounddocs b/contrib/libs/libmagic/magic/Magdir/ole2compounddocs
new file mode 100644
index 0000000000..2c451a9ab5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ole2compounddocs
@@ -0,0 +1,760 @@
+
+#------------------------------------------------------------------------------
+# $File: ole2compounddocs,v 1.26 2023/05/15 16:46:12 christos Exp $
+# Microsoft OLE 2 Compound Documents : file(1) magic for Microsoft Structured
+# storage (https://en.wikipedia.org/wiki/Compound_File_Binary_Format)
+# Additional tests for OLE 2 Compound Documents should be under this recipe.
+# reference: https://www.openoffice.org/sc/compdocfileformat.pdf
+
+0 string \320\317\021\340\241\261\032\341
+# https://digital-preservation.github.io/droid/
+# skip droid skeleton like fmt-39-signature-id-128.doc by valid version
+>0x1A ushort !0xABAB OLE 2 Compound Document
+#>0x1C uleshort x \b, endnian %#4.4x
+# big endian not tested
+>>0x1C ubeshort =0xfffe \b, big-endian
+>>>546 string jbjb : Microsoft Word Document
+!:mime application/msword
+!:apple MSWDWDBN
+!:ext doc
+# Byte Order 0xFFFE means little-endian found in real world applications
+#>>0x1C uleshort =0xfffe \b, little-endian
+>>0x1C uleshort =0xfffe
+# From: Joerg Jenderek
+# Major Version 3 or 4
+>>>0x1A uleshort x \b, v%u
+# Minor Version 32h=50 3Bh=59 3Eh=62
+>>>0x18 uleshort x \b.%u
+# SecID of first sector of the directory stream is often 1 but high like 3144h
+>>>48 ulelong x \b, SecID %#x
+# Sector Shift Exponent in short-stream container stream: 6~64 bytes
+>>>32 uleshort !6 \b, exponent of short stream %u
+# total number of sectors used for the FAT
+>>>44 ulelong >1 \b, %u FAT sectors
+# SecID of first sector of the short-sector allocation table (Mini FAT)
+# or -2 (End Of ChainSecID) if not extant
+>>>60 ulelong !0xffFFffFE \b, Mini FAT start sector %#x
+# total number of sectors used for the short-sector allocation table
+>>>64 ulelong !1 \b, %u Mini FAT sector
+# plural s
+>>>>64 ulelong >1 \bs
+# SecID of first sector of the master sector allocation table (DIFAT)
+# or -2 (End Of Chain SecID) if no additional sectors used
+>>>68 ulelong !0xffFFffFE \b, DIFAT start sector %#x
+# total number of sectors used for the master sector allocation table (DIFAT)
+>>>72 ulelong >0 \b, %u DIFAT sectors
+# First part of the master sector allocation table (DIFAT) containing 109 SecIDs
+#>>>76 ubequad x \b, DIFAT=%#16.16llx
+#>>>84 ubequad x \b%16.16llx...
+# pointer to root entry only works with standard configuration for SecID ~< 800h
+# Red-Carpet-presentation-1.0-1.sdd sg10.sdv 2000_GA_Annual_Review_Data.xls
+# "ORLEN Factbook 2017.xls" XnView_metadata.doc
+# "Barham, Lisa - Die Shopping-Prinzessinnen.doc" then not recognized
+>>>48 ulelong >0x800 too big for FILE_BYTES_MAX = 1 MiB
+# Sector Shift Exponent 9~512 for major version 3 or C~4096 for major version 4
+>>>0x1E uleshort 0xc \b, blocksize 4096
+# jump to one block (4096 bytes per block) before root storage block
+>>>>(48.l*4096) ubyte x
+>>>>>&4095 use ole2-directory
+#>>>0x1E uleshort 9 \b, blocksize 512
+>>>0x1E uleshort 9
+# jump to one block (512 bytes per block) before root storage block
+# in 5.37 only true for offset ~< FILE_BYTES_MAX=7 MiB defined in ../../src/file.h
+>>>>(48.l*512) ubyte x
+>>>>>&511 use ole2-directory
+# check directory entry structure and display types by GUID
+0 name ole2-directory
+# directory entry name like "Root Entry"
+#>0 lestring16 x \b, 1st %.10s
+# type of the entry; 5~Root storage
+#>66 ubyte x \b, type %x
+# node colour of the entry: 00H ~ Red 01H ~ Black
+#>67 ubyte x \b, color %x
+# the DirIDs of the child nodes. Should both be -1 in the root storage entry
+#>68 bequad !0xffffffffffffffff \b, DirIDs %llx
+# NEXT lines for DEBUGGING
+# second directory entry name like VisioDocument Control000
+#>128 lestring16 x \b, 2nd %.20s
+# third directory entry like WordDocument
+#>256 lestring16 x \b, 3rd %.20s
+# forth
+#>384 lestring16 x \b, 4th %.10s
+# 5th
+#>512 lestring16 x \b, 5th %.10s
+# 6th
+#>640 lestring16 x \b, 6th %.10s
+# 7th
+#>768 lestring16 x \b, 7th %.10s
+# https://wikileaks.org/ciav7p1/cms/page_13762814.html
+# https://m.blog.naver.com/superman4u/40047693679
+# https://misc.daniel-marschall.de/projects/guid_analysis/guid.txt
+# https://toolslick.com/conversion/data/guid
+#>80 ubequad !0 \b, clsid %#16.16llx
+#>>88 ubequad x \b%16.16llx
+# test for "Root Entry" inside directory by type 5 value
+>66 ubyte 5
+# look for CLSID GUID 0
+>>88 ubequad 0x0
+>>>80 ubequad 0x0
+# - Microstation V8 DGN files (www.bentley.com)
+# URL: https://en.wikipedia.org/wiki/MicroStation
+# Last update on 10/23/2006 by Lester Hightower
+# 07/24/2019 by Joerg Jenderek
+# Second directory entry name like Dgn~H Dgn~S
+>>>>128 lestring16 Dgn~ : Microstation V8 CAD
+#!:mime application/x-ole-storage
+!:mime application/x-bentley-dgn
+# http://www.q-cad.com/files/samples_cad_files/1344468165.dgn
+!:ext dgn
+#
+# URL: http://fileformats.archiveteam.org/wiki/WordPerfect
+# Second directory entry name PerfectOffice_
+>>>>128 lestring16 PerfectOffice_ : WordPerfect 7-X3 presentations Master, Document or Graphic
+!:mime application/vnd.wordperfect
+# https://www.macdisk.com/macsigen.php "WPC2" for Wordperfect 2 *.wpd
+!:apple ????WPC7
+!:ext mst/wpd/wpg
+#
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Works_Word_Processor
+# Second directory entry name MatOST_
+>>>>128 lestring16 MatOST : Microsoft Works 3.0 document
+!:mime application/vnd.ms-works
+!:apple ????AWWP
+!:ext wps
+#
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Works_Spreadsheet
+# 3rd directory entry name WksSSWorkBook
+>>>>256 lestring16 WksSSWorkBook : Microsoft Works 6-9 spreadsheet
+!:mime application/vnd.ms-works
+!:apple ????AWSS
+!:ext xlr
+#
+# URL: http://fileformats.archiveteam.org/wiki/XLS
+# what is the difference to {00020820-0000-0000-c000-000000000046} ?
+# Second directory entry name Workbook
+>>>>128 lestring16 Workbook
+>>>>>256 lestring16 !WksSSWorkBook : Microsoft Excel 97-2003 worksheet 0 clsid
+!:mime application/vnd.ms-excel
+# https://www.macdisk.com/macsigen.php XLS5 for Excel 5
+!:apple ????XLS9
+!:ext xls
+#
+# URL: http://fileformats.archiveteam.org/wiki/PPT
+# Second directory entry name Object1 Object12 Object35
+>>>>128 lestring16 Object : Microsoft PowerPoint 4 presentation
+!:mime application/vnd.ms-powerpoint
+# https://www.macdisk.com/macsigen.php
+!:apple ????PPT3
+!:ext ppt
+#
+# URL: https://www.msoutlook.info/question/164
+# Second directory entry name __CollDataStm
+>>>>128 lestring16 __CollDataStm : Microsoft Outlook Send Receive Settings
+#!:mime application/vnd.ms-outlook
+!:mime application/x-ms-srs
+# %APPDATA%\Microsoft\Outlook\Outlook.srs
+!:ext srs
+#
+# URL: https://www.file-extensions.org/cag-file-extension
+# Second directory entry name Category
+>>>>128 lestring16 Category : Microsoft Clip Art Gallery
+#!:mime application/x-ole-storage
+!:mime application/x-ms-cag
+!:apple MScgCGdb
+!:ext cag/
+#
+# URL: https://www.filesuffix.com/de/extension/rra
+# 3rd directory entry name StrIndex_StringTable
+>>>>256 lestring16 StrIndex_StringTable : Windows temporarily installer
+#!:mime application/x-ole-storage
+!:mime application/x-ms-rra
+!:ext rra
+#
+# URL: https://www.forensicswiki.org/wiki/Jump_Lists
+# 3rd directory entry name DestList
+>>>>256 lestring16 DestList : Windows jump list
+#!:mime application/x-ole-storage
+!:mime application/x-ms-jumplist
+# %APPDATA%\Microsoft\Windows\Recent\AutomaticDestinations\*.automaticDestinations-ms
+!:ext automaticDestinations-ms
+#
+# URL: https://en.wikipedia.org/wiki/Windows_thumbnail_cache
+# Second directory entry name 256_
+>>>>128 lestring16 256_ : Windows thumbnail database 256
+#!:mime application/x-ole-storage
+!:mime application/x-ms-thumbnail
+# Thumbs.db
+!:ext db
+>>>>128 lestring16 96_ : Windows thumbnail database 96
+!:mime application/x-ms-thumbnail
+!:ext db
+# 3rd directory entry name Catalog_
+>>>>256 lestring16 Catalog : Windows thumbnail database
+!:mime application/x-ms-thumbnail
+!:ext db
+#
+# URL: https://support.microsoft.com/en-us/help/300887/how-to-use-system-information-msinfo32-command-line-tool-switches
+# Note: older Microsoft Systeminfo (MSInfo Configuration File of msinfo32); newer use xml based
+# Second directory entry name Control000
+>>>>128 lestring16 Control000 : Microsoft old Systeminfo
+#!:mime application/x-ole-storage
+!:mime application/x-ms-info
+!:ext nfo
+#
+# From: Joerg Jenderek
+# URL: https://learn.microsoft.com/en-us/sysinternals/downloads/autoruns
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/arn-autoruns-v14.trid.xml
+# Note: older versions til 13 about middle 2021 handled by ./windows
+# called "Sysinternals Autoruns data (v14)" by TrID
+# second, third and fourth directory entry name like Header Items 0
+>>>>128 lestring16 Header : Microsoft sysinternals AutoRuns data, version 14
+#!:mime application/x-ole-storage
+!:mime application/x-ms-arn
+# like: MyHOSTNAME.arn
+!:ext arn
+#
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Microsoft_Access
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/m/mdz.trid.xml
+# http://fileformats.archiveteam.org/wiki/Microsoft_Compound_File
+# Note: only version foo tested and called "Microsoft Access Wizard template" by TrID
+# Fourth directory entry name TemplateID
+>>>>384 lestring16 TemplateID : Microsoft Access wizard template
+# Second directory entry name like \005SummaryInformation and 3rd name like \005DocumentSummaryInformation
+#!:mime application/x-ole-storage
+#!:mime application/vnd.ms-office
+#!:mime application/vnd.ms-access
+#!:mime application/msaccess
+!:mime application/x-ms-mdz
+# http://extension.nirsoft.net/mdz
+!:ext mdz
+#
+# URL: http://fileformats.archiveteam.org/wiki/Corel_Print_House
+# Second directory entry name Thumbnail
+>>>>128 lestring16 Thumbnail : Corel PrintHouse image
+#!:mime application/x-ole-storage
+!:mime application/x-corel-cph
+!:ext cph
+# 3rd directory entry name Thumbnail
+>>>>256 lestring16 Thumbnail : Corel PrintHouse image
+!:mime application/x-corel-cph
+!:ext cph
+# URL: http://fileformats.archiveteam.org/wiki/Corel_Gallery
+# Note: format since Gallery 2; sometimes called Corel Multimedia Manager Album
+# third directory entry name _INFO_
+>>>>256 lestring16 _INFO_ : Corel Gallery
+# second directory entry name _ITEM_ or _DATA_
+# later directory entry names: _ALBUM_ _THUMBNAIL_
+#!:mime application/x-ole-storage
+!:mime application/x-corel-gal
+!:ext gal
+#
+# From: Joerg Jenderek
+# URL: https://archive.org/details/iPhoto-Plus-4
+# https://filext.com/file-extension/TPL
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/t/tpl-ulead.trid.xml
+# Note: found in Template sub directory in program directory of software iPhoto Plus version 4
+# second, third and fourth directory entry name like TplHeader TplMainImage TplPreview
+>>>>128 lestring16 TplHeader : Ulead iPhoto Template
+#!:mime application/x-ole-storage
+!:mime image/x-ulead-tpl
+# https://www.file-extensions.org/tpl-file-extension-ulead-photo-express-template
+!:ext tpl
+#
+# URL: https://en.wikipedia.org/wiki/Hangul_(word_processor)
+# https://www.hancom.com/etc/hwpDownload.do
+# Note: "HWP Document File" signature found in FileHeader
+# Hangul Word Processor WORDIAN, 2002 and later is using HWP 5.0 format.
+# Second directory entry name FileHeader hint for Thinkfree Office document
+>>>>128 lestring16 FileHeader : Hancom HWP (Hangul Word Processor) file, version 5.0
+#!:mime application/haansofthwp
+!:mime application/x-hwp
+# https://example-files.online-convert.com/document/hwp/example.hwp
+!:ext hwp
+#
+# URL: https://ask.libreoffice.org/en/question/26303/creating-new-themes-for-the-gallery-not-functioning/
+# Second directory entry name like dd2000 dd2001 dd2036 dd2060 dd2083
+>>>>128 lestring16 dd2 : StarOffice Gallery view
+#!:mime application/x-ole-storage
+!:mime application/x-star-sdv
+!:ext sdv
+# URL: https://en.wikipedia.org/wiki/SoftMaker_Office
+# second directory entry name Current User
+>>>>128 lestring16 Current\ User : SoftMaker
+# third directory entry name SMNativeObjData
+>>>>>256 lestring16 SMNativeObjData
+# 5th directory entry name PowerPoint
+>>>>>>512 lestring16 PowerPoint PowerPoint presentation or template
+!:mime application/vnd.ms-powerpoint
+!:ext ppt/pps/pot
+# 4th directory entry name PowerPoint
+>>>>>384 lestring16 PowerPoint Presentations or template
+# http://extension.nirsoft.net/prv
+!:mime application/vnd.softmaker.presentations
+!:ext prd/prv
+# third directory entry name like Current User
+>>>>256 lestring16 Current\ User : SoftMaker
+# 5th directory entry name PowerPoint
+>>>>>512 lestring16 PowerPoint Presentations or template
+# http://extension.nirsoft.net/prd
+!:mime application/vnd.softmaker.presentations
+!:ext prd/prv
+# 2nd directory entry name Pictures
+>>>>>>128 lestring16 Pictures with pictures
+#
+# URL: http://fileformats.archiveteam.org/wiki/PageMaker
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p
+# pagemaker-generic.trid.xml
+# pagemaker-pm6.trid.xml
+# pagemaker-pm65.trid.xml
+# pmd-pm7.trid.xml
+# From: Joerg Jenderek
+# Note: since version 6 embedd as stream with PageMaker name the "old" format handled by ./wordprocessors
+# verified by Michal Mutl Structured Storage Viewer `SSView.exe brochus.pt6`
+# Second directory entry name PageMaker
+>>>>128 lestring16 PageMaker :
+# look for magic of "old" PageMaker like in 02TEMPLT.T65
+>>>>>0 search/0xa900/s \0\0\0\0\0\0\xff\x99
+# GRR: jump to PageMaker stream and inspect it by sub routine PageMaker of ./wordprocessors failed with wrong version!
+#>>>>>>&0 use PageMaker
+# THIS WORKS PARTLY!
+>>>>>>&0 indirect x
+# remaining null clsid
+>>>>128 default x
+>>>>>0 use ole2-unknown
+# look for CLSID where "second" part is 0
+>>>80 ubequad !0x0
+#
+# Summary: Family Tree Maker
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Family_Tree_Maker
+# https://en.wikipedia.org/wiki/Family_Tree_Maker
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/f/ftw.trid.xml
+# Note called "Family Tree Maker Family Tree" by TrID and
+# "FamilyTree Maker Database" with version "1-4" by DROID via PUID fmt/1352
+# tested only with version 2.0
+# verified by Michal Mutl Structured Storage Viewer `SSView.exe my.ftw`
+# newer versions are SQLite based and handled by ./sql
+# directory names like: IND.DB AUX.DB GENERAL.DB NAME.NDX BIRTH.NDX EXTRA.DB
+>>>>80 ubequad 0x5702000000000000 : Family Tree Maker Windows database, version 1-4
+# look for "File Format (C) Copyright 1993 Banner Blue Software Inc. - All Rights Reserved" in GENERAL.DB
+#>>>>>0 search/0x5460c/s F\0i\0l\0e\0\040\0F\0o\0r\0m\0a\0t\0\040\0(\0C\0)\0 \b, VERSION
+# GRR: jump to version value like 2 does not work!
+#>>>>>>&-8 ubyte x %u
+#!:mime application/x-ole-storage
+!:mime application/x-fmt
+# FBK is used for backup of FTW
+!:ext ftw/fbk
+#
+>>>>80 default x
+>>>>>0 use ole2-unknown
+# look for known clsid GUID
+# - Visio documents
+# URL: http://fileformats.archiveteam.org/wiki/Visio
+# Last update on 10/23/2006 by Lester Hightower, 07/20/2019 by Joerg Jenderek
+>>88 ubequad 0xc000000000000046
+>>>80 ubequad 0x131a020000000000 : Microsoft Visio 2000-2002 Document, stencil or template
+!:mime application/vnd.visio
+# VSD~Drawing VSS~Stencil VST~Template
+!:ext vsd/vss/vst
+>>>80 ubequad 0x141a020000000000 : Microsoft Visio 2003-2010 Document, stencil or template
+!:mime application/vnd.visio
+!:ext vsd/vss/vst
+#
+# URL: http://fileformats.archiveteam.org/wiki/Windows_Installer
+# https://en.wikipedia.org/wiki/Windows_Installer#ICE_validation
+# Update: Joerg Jenderek
+# Windows Installer Package *.MSI or validation module *.CUB
+>>>80 ubequad 0x84100c0000000000 : Microsoft Windows Installer Package or validation module
+!:mime application/x-msi
+#!:mime application/x-ms-win-installer
+# https://learn.microsoft.com/en-us/windows/win32/msi/internal-consistency-evaluators-ices
+# cub is used for validation module like: Vstalogo.cub XPlogo.cub darice.cub logo.cub mergemod.cub
+#!:mime application/x-ms-cub
+!:ext msi/cub
+# From: Joerg Jenderek
+# URL: http://en.wikipedia.org/wiki/Windows_Installer
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/m/mst.trid.xml
+# called "Windows SDK Setup Transform script" by TrID
+>>>80 ubequad 0x82100c0000000000 : Microsoft Windows Installer transform script
+#!:mime application/x-ole-storage
+!:mime application/x-ms-mst
+!:ext mst
+>>>80 ubequad 0x86100c0000000000 : Microsoft Windows Installer Patch
+# ??
+!:mime application/x-wine-extension-msp
+#!:mime application/x-ms-msp
+!:ext msp
+#
+# URL: http://fileformats.archiveteam.org/wiki/DOC
+>>>80 ubequad 0x0009020000000000 : Microsoft Word 6-95 document or template
+!:mime application/msword
+# for template MSWDW8TN
+!:apple MSWDWDBN
+!:ext doc/dot
+>>>80 ubequad 0x0609020000000000 : Microsoft Word 97-2003 document or template
+!:mime application/msword
+!:apple MSWDWDBN
+# dot for template; no extension on Macintosh
+!:ext doc/dot/
+#
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Works_Word_Processor
+>>>80 ubequad 0x0213020000000000 : Microsoft Works 3-4 document or template
+!:mime application/vnd.ms-works
+!:apple ????AWWP
+# ps for template https://filext.com/file-extension/PS bps for backup
+!:ext wps/ps/bps
+#
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Works_Database
+>>>80 ubequad 0x0313020000000000 : Microsoft Works 3-4 database or template
+!:mime application/vnd.ms-works-db
+# https://www.macdisk.com/macsigen.php
+!:apple ????AWDB
+# db for template www.file-extensions.org/db-file-extension-microsoft-works-data bdb for backup
+!:ext wdb/db/bdb
+#
+# URL: https://en.wikipedia.org/wiki/Microsoft_Excel
+>>>80 ubequad 0x1008020000000000 : Microsoft Excel 5-95 worksheet, addin or template
+!:mime application/vnd.ms-excel
+# https://www.macdisk.com/macsigen.php
+!:apple ????XLS5
+# worksheet/addin/template/no extension on Macintosh
+!:ext xls/xla/xlt/
+#
+>>>80 ubequad 0x2008020000000000 : Microsoft Excel 97-2003
+!:mime application/vnd.ms-excel
+# https://www.macdisk.com/macsigen.php XLS5 for Excel 5
+!:apple ????XLS9
+# 3rd directory entry name
+>>>>256 lestring16 _VBA_PROJECT_CUR addin
+!:ext xla/
+# 4th directory entry name
+>>>>384 lestring16 _VBA_PROJECT_CUR addin
+!:ext xla
+#!:ext xla/
+>>>>256 default x worksheet or template
+!:ext xls/xlt
+#!:ext xls/xlt/
+#
+# URL: http://fileformats.archiveteam.org/wiki/OLE2
+>>>80 ubequad 0x0b0d020000000000 : Microsoft Outlook 97-2003 item
+#>>>80 ubequad 0x0b0d020000000000 : Microsoft Outlook 97-2003 Message
+#!:mime application/vnd.ms-outlook
+!:mime application/x-ms-msg
+!:ext msg
+# URL: https://wiki.fileformat.com/email/oft/
+>>>80 ubequad 0x46f0060000000000 : Microsoft Outlook 97-2003 item template
+#!:mime application/vnd.ms-outlook
+!:mime application/x-ms-oft
+!:ext oft
+#
+# URL: http://fileformats.archiveteam.org/wiki/PPT
+>>>80 ubequad 0x5148040000000000 : Microsoft PowerPoint 4.0 presentation
+!:mime application/vnd.ms-powerpoint
+# https://www.macdisk.com/macsigen.php
+!:apple ????PPT3
+!:ext ppt
+# Summary: "newer" Greenstreet Art drawing
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/GST_ART
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/art-gst-docfile.trid.xml
+# Note: called like "Greenstreet Art drawing" by TrID
+# Note: CONTENT stream contains binary part of older versions with phrase GST:ART at offset 16
+# verified by Michal Mutl Structured Storage Viewer `SSView.exe BCARD2.ART`
+>>>80 ubequad 0x602c020000000000 : Greenstreet Art drawing
+#!:mime application/x-ole-storage
+!:mime image/x-greenstreet-art
+!:ext art
+>>>80 default x
+>>>>0 use ole2-unknown
+#??
+# URL: http://www.checkfilename.com/view-details/Microsoft-Works/RespageIndex/0/sTab/2/
+>>88 ubequad 0xa29a00aa004a1a72 : Microsoft
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Works_Word_Processor
+>>>80 ubequad 0xc2dbcd28e20ace11 Works 4 document
+!:mime application/vnd.ms-works
+!:apple ????AWWP
+!:ext wps
+#
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Works_Database
+>>>80 ubequad 0xc3dbcd28e20ace11 Works 4 database
+!:mime application/vnd.ms-works-db
+!:apple ????AWDB
+!:ext wdb/bdb
+#??
+>>88 ubequad 0xa40700c04fb932ba : Microsoft
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Works_Word_Processor
+>>>80 ubequad 0xb25aa40e0a9ed111 Works 5-6 document
+!:mime application/vnd.ms-works
+!:apple ????AWWP
+!:ext wps
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Microsoft_Works
+# Reference: http://fileformats.archiveteam.org/wiki/Microsoft_Compound_File
+# Note: probably version 6 and 7
+# organize pictures like JPFG images in streams __cf1 with names like
+# 001.JPG, 002.JPG ... in streams __fname
+>>88 ubequad 0xa1c800c04f612452 : Microsoft
+>>>80 ubequad 0xc0c7266eb98cd311 Works portfolio
+# 2nd directory entry name PfOrder, 3rd __LastID and 4th __SizeUsed
+#!:mime application/x-ole-storage
+# https://www.iana.org/assignments/media-types/application/vnd.ms-works
+!:mime application/vnd.ms-works
+# https://extension.nirsoft.net/wsb
+# like: wsbsamp.wsb WORKS2003_CD:\MSWorks\Common\Sammlung.wsb
+!:ext wsb
+#??
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Publisher
+>>88 ubequad 0x00c0000000000046 : Microsoft
+>>>80 ubequad 0x0112020000000000 Publisher
+!:mime application/vnd.ms-publisher
+!:ext pub
+#
+# URL: http://fileformats.archiveteam.org/wiki/PPT
+#??
+>>88 ubequad 0xa90300aa00510ea3 : Microsoft
+>>>80 ubequad 0x70ae7bea3bfbcd11 PowerPoint 95 presentation
+!:mime application/vnd.ms-powerpoint
+# https://www.macdisk.com/macsigen.php
+!:apple ????PPT3
+!:ext ppt/pot
+#??
+>>88 ubequad 0x86ea00aa00b929e8 : Microsoft
+>>>80 ubequad 0x108d81649b4fcf11 PowerPoint 97-2003 presentation or template
+!:mime application/vnd.ms-powerpoint
+!:apple ????PPT3
+# /autostart/template
+!:ext ppt/pps/pot
+# From: Joerg Jenderek
+# URL: https://www.file-extensions.org/ppa-file-extension
+# https://en.wikipedia.org/wiki/Microsoft_PowerPoint#cite_note-231
+# Reference: http://fileformats.archiveteam.org/wiki/Microsoft_Compound_File
+>>88 ubequad 0x871800aa0060263b : Microsoft
+# only version 8 (97) tested; PowerPoint 4.0 to 11.0 (2004) (Wikipedia); 97 to 2003 (file-extensions.org)
+>>>80 ubequad 0xf04672810a72cf11 PowerPoint Addin or Wizard
+# second, third and fourth directory entry name like VBA PROJECT PROJECTwm
+# http://extension.nirsoft.net/pwz
+!:mime application/vnd.ms-powerpoint
+# like: BSHPPT97.PPA "AutoContent Wizard.pwz"
+!:ext ppa/pwz
+#
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/AWD_(At_Work_Document)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/awd-fax.trid.xml
+# Note: called "Microsoft At Work Fax document" by TrID
+>>88 ubequad 0xb29400dd010f2bf9 : Microsoft
+>>>80 ubequad 0x801cb0023de01a10 At Work fax Document
+#!:mime application/x-ole-storage
+!:mime image/x-ms-awd
+!:ext awd
+#
+# URL: https://en.wikipedia.org/wiki/Microsoft_Project
+#??
+>>88 ubequad 0xbe1100c04fb6faf1 : Microsoft
+>>>80 ubequad 0x3a8fb774c8c8d111 Project
+!:mime application/vnd.ms-project
+!:ext mpp
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Microsoft_Office_shared_tools#Binder
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/o/obd.trid.xml
+# http://fileformats.archiveteam.org/wiki/Microsoft_Compound_File
+# Note: only version 8 tested and called "Office Binder Document" by TrID and
+# "Microsoft Office Binder File for Windows" version 97-2000 by DROID fmt/240
+>>88 ubequad 0xb21c00aa004ba90b : Microsoft
+>>>80 ubequad 0x0004855964661b10 Office Binder Document, Template or wizard
+# second directory entry name like Binder
+# https://www.file-extensions.org/obd-file-extension
+#!:mime application/vnd.ms-binder
+!:mime application/x-msbinder
+# obt for template; obz for Microsoft Office Binder wizard
+!:ext obd/obt/obz
+#
+# URL: http://fileformats.archiveteam.org/wiki/WordPerfect
+# Reference: http://fileformats.archiveteam.org/wiki/Microsoft_Compound_File
+# https://github.com/OneWingedShark/WordPerfect/
+# blob/master/doc/SDK_Help/FileFormats/WPFF_DocumentStructure.htm
+# From: Joerg Jenderek
+# Note: internal version x.2 or 2.2 like in embedded ole6-PerfectOffice_MAIN.wpd
+# 3rd directory entry name PerfectOffice_OBJECT and 2nd PerfectOffice_MAIN,
+# which contains WordPerfect document \xffWPC signature handled by ./wordprocessors
+>>88 ubequad 0x19370000929679cd : WordPerfect 7
+>>>80 ubequad 0xff739851ad2d2002 Document
+!:mime application/vnd.wordperfect
+#!:apple ????WPC?
+# https://fossies.org/linux/wp2latex/test/ole6.wpd
+!:ext wpd
+#>>>>0 search/0xc01/s \xffWPC \b, WPC SIGNATURE
+# inspect embedded WordPerfect document by ./wordprocessors with 1 space at end
+#>>>>>&0 indirect x \b; contains
+# GRR: the above expression does not work correctly
+#
+# URL: http://fileformats.archiveteam.org/wiki/SHW_(Corel)
+#???
+>>88 ubequad 0x99ae04021c007002 : WordPerfect
+>>>80 ubequad 0x62fe2e4099191b10 7-X3 presentation
+!:mime application/x-corelpresentations
+#!:mime application/x-shw-viewer
+#!:mime image/x-presentations
+!:ext shw
+#
+# URL: http://www.checkfilename.com/view-details/WordPerfect-Office-X3/RespageIndex/0/sTab/2/
+>>>80 ubequad 0x60fe2e4099191b10 9 Graphic
+#!:mime application/x-wpg
+#!:mime image/x-wordperfect-graphics
+!:mime image/x-wpg
+# https://www.macdisk.com/macsigen.php "WPC2" for Wordperfect 2 *.wpd
+!:apple ????WPC9
+!:ext wpg
+#
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/CorelCAD
+# https://en.wikipedia.org/wiki/CorelCAD
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/ccd-corelcad.trid.xml
+# Note: called "CorelCAD Drawing" by TrID and CorelCAD
+# directory entry names like Contents ViewInfo CustomViewDescriptions LayerInfo
+>>88 ubequad 0xbe26db67235e2689 : Corel
+>>>80 ubequad 0x20f414de1cacce11 \bCAD Drawing or Template
+#!:mime application/x-ole-storage
+!:mime application/x-corel-cad
+# CCT for CorelCAD Template
+!:ext ccd/cct
+#
+# URL: http://fileformats.archiveteam.org/wiki/StarOffice_binary_formats
+>>88 ubequad 0x996104021c007002 : StarOffice
+>>>80 ubequad 0x407e5cdc5cb31b10 StarWriter 3.0 document or template
+# https://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html
+!:mime application/x-starwriter
+!:ext sdw/vor
+#
+>>>80 ubequad 0xa03f543fa6b61b10 StarCalc 3.0 spreadsheet or template
+!:mime application/x-starcalc
+!:ext sdc/vor
+#
+>>>80 ubequad 0xe0aa10af6db31b10 StarDraw 3.0 drawing or template
+!:mime application/x-starimpress
+#!:mime application/x-stardraw
+# sda ??
+!:ext sdd/sda/vor
+#??
+>>88 ubequad 0x89cb008029e4b0b1 : StarOffice
+>>>80 ubequad 0x41d461633542d011 StarCalc 4.0 spreadsheet or template
+!:mime application/x-starcalc
+!:ext sdc/vor
+#
+>>>80 ubequad 0x61b8a5c6d685d111 StarCalc 5.0 spreadsheet or template
+!:mime application/vnd.stardivision.cal
+!:ext sdc/vor
+#
+>>>80 ubequad 0xc03c2d011642d011 StarImpress 4.0 presentation or template
+!:mime application/x-starimpress
+!:ext sdd/vor
+#??
+>>88 ubequad 0xb12a04021c007002 : StarOffice
+>>>80 ubequad 0x600459d4fd351c10 StarMath 3.0
+!:mime application/x-starmath
+!:ext smf
+#??
+>>88 ubequad 0x8e2c00001b4cc711 : StarOffice
+>>>80 ubequad 0xe0999cfb6d2c1c10 StarChart 3.0
+!:mime application/x-starchart
+!:ext sds
+#??
+>>88 ubequad 0xa45e00a0249d57b1 : StarOffice
+>>>80 ubequad 0xb0e9048b0e42d011 StarWriter 4.0 document or template
+!:mime application/x-starwriter
+!:ext sdw/vor
+#??
+>>88 ubequad 0x89ca008029e4b0b1 : StarOffice
+>>>80 ubequad 0xe1b7b3022542d011 StarMath 4.0
+!:mime application/x-starmath
+!:ext smf
+#
+>>>80 ubequad 0xe0b7b3022542d011 StarChart 4.0
+!:mime application/x-starchart
+!:ext sds
+#??
+>>88 ubequad 0xa53f00a0249d57b1 : StarOffice
+>>>80 ubequad 0x70c90a340de3d011 Master 4.0 document
+!:mime application/x-starwriter-global
+!:ext sgl
+#??
+>>88 ubequad 0x89d0008029e4b0b1 : StarOffice
+>>>80 ubequad 0x40e6b5ffde85d111 StarMath 5.0
+!:mime application/vnd.stardivision.math
+!:ext smf
+#
+>>>80 ubequad 0xa005892ebd85d111 StarDraw 5.0 drawing or template
+!:mime application/vnd.stardivision.draw
+!:ext sda/vor
+#
+>>>80 ubequad 0x21725c56bc85d111 StarImpress 5.0 presentation or template
+!:mime application/vnd.stardivision.impress
+# sda is used for what?
+!:ext sdd/vor/sda
+#
+>>>80 ubequad 0x214388bfdd85d111 StarChart 5.0
+!:mime application/vnd.stardivision.chart
+!:ext sds
+# ??
+>>88 ubequad 0xaab4006097da561a : StarOffice
+>>>80 ubequad 0xd1f90cc2ae85d111 StarWriter 5.0 document or template
+!:mime application/vnd.stardivision.writer
+!:ext sdw/vor
+#
+>>>80 ubequad 0xd3f90cc2ae85d111 Master 5.0 document
+!:mime application/vnd.stardivision.writer-global
+!:ext sgl
+#??
+# URL: http://fileformats.archiveteam.org/wiki/FlashPix
+>>88 ubequad 0x855300aa00a1f95b : Kodak
+>>>80 ubequad 0x0067615654c1ce11 FlashPIX Image
+!:mime image/vnd.fpx
+!:apple ????FPix
+!:ext fpx
+# URL: https://en.wikipedia.org/wiki/SoftMaker_Office
+>>88 ubequad 0x95f600a0cc3cca14 : PlanMaker
+>>>80 ubequad 0x9174088a6452d411 document or template
+!:mime application/vnd.softmaker.planmaker
+# pmv for template https://www.file-extensions.org/pmv-file-extension
+!:ext pmd/pmv
+# URL: http://fileformats.archiveteam.org/wiki/MAX_(3ds_Max)
+# https://en.wikipedia.org/wiki/Autodesk_3ds_Max
+# Reference: http://fileformats.archiveteam.org/wiki/Microsoft_Compound_File
+# Note: called "3D Studio Max Scene" by TrID and "3DS Max" by DROID and
+# "3DSMax thumbnail" by XnView and verfied by `nconvert -info A380.max`
+# applies only to "newer" versions (about 2008-2020)
+>>88 ubequad 0x9fed04143144cc1e : Autodesk
+>>>80 ubequad 0x7b8cdd1cc081a045 3ds Max
+#!:mime application/x-ole-storage
+!:mime model/x-autodesk-max
+# like: https://static.free3d.com/models/dropbox/dropbox/sq/A380.7z/A380.max
+!:ext max
+# also chr for character file according to DROID https://www.nationalarchives.gov.uk/PRONOM/fmt/978
+#!:ext max/chr
+# remaining non null clsid
+>>88 default x
+>>>0 use ole2-unknown
+# display information about directory for not detected CDF files
+0 name ole2-unknown
+>80 ubequad x : UNKNOWN
+# https://reposcope.com/mimetype/application/x-ole-storage
+!:mime application/x-ole-storage
+# according to file version 5.41 with -e soft option
+#!:mime application/CDFV2
+#!:ext ???
+>80 ubequad !0 \b, clsid %#16.16llx
+>>88 ubequad x \b%16.16llx
+# converted hexadecimal format to standard GUUID notation
+>>80 guid x {%s}
+# second directory entry name like VisioDocument Control000
+>128 lestring16 x with names %.20s
+# third directory entry like WordDocument Preview.dib
+>256 lestring16 x %.20s
+# forth like \005SummaryInformation
+>384 lestring16 x %.25s
+# 5th
+>512 lestring16 x %.10s
+# 6th
+>640 lestring16 x %.10s
+# 7th
+>768 lestring16 x %.10s
diff --git a/contrib/libs/libmagic/magic/Magdir/olf b/contrib/libs/libmagic/magic/Magdir/olf
new file mode 100644
index 0000000000..6ae3fc04e5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/olf
@@ -0,0 +1,98 @@
+
+#------------------------------------------------------------------------------
+# $File: olf,v 1.4 2009/09/19 16:28:11 christos Exp $
+# olf: file(1) magic for OLF executables
+#
+# We have to check the byte order flag to see what byte order all the
+# other stuff in the header is in.
+#
+# MIPS R3000 may also be for MIPS R2000.
+# What're the correct byte orders for the nCUBE and the Fujitsu VPP500?
+#
+# Created by Erik Theisen <etheisen@openbsd.org>
+# Based on elf from Daniel Quinlan <quinlan@yggdrasil.com>
+0 string \177OLF OLF
+>4 byte 0 invalid class
+>4 byte 1 32-bit
+>4 byte 2 64-bit
+>7 byte 0 invalid os
+>7 byte 1 OpenBSD
+>7 byte 2 NetBSD
+>7 byte 3 FreeBSD
+>7 byte 4 4.4BSD
+>7 byte 5 Linux
+>7 byte 6 SVR4
+>7 byte 7 esix
+>7 byte 8 Solaris
+>7 byte 9 Irix
+>7 byte 10 SCO
+>7 byte 11 Dell
+>7 byte 12 NCR
+>5 byte 0 invalid byte order
+>5 byte 1 LSB
+>>16 leshort 0 no file type,
+>>16 leshort 1 relocatable,
+>>16 leshort 2 executable,
+>>16 leshort 3 shared object,
+# Core handling from Peter Tobias <tobias@server.et-inf.fho-emden.de>
+# corrections by Christian 'Dr. Disk' Hechelmann <drdisk@ds9.au.s.shuttle.de>
+>>16 leshort 4 core file
+>>>(0x38+0xcc) string >\0 of '%s'
+>>>(0x38+0x10) lelong >0 (signal %d),
+>>16 leshort &0xff00 processor-specific,
+>>18 leshort 0 no machine,
+>>18 leshort 1 AT&T WE32100 - invalid byte order,
+>>18 leshort 2 SPARC - invalid byte order,
+>>18 leshort 3 Intel 80386,
+>>18 leshort 4 Motorola 68000 - invalid byte order,
+>>18 leshort 5 Motorola 88000 - invalid byte order,
+>>18 leshort 6 Intel 80486,
+>>18 leshort 7 Intel 80860,
+>>18 leshort 8 MIPS R3000_BE - invalid byte order,
+>>18 leshort 9 Amdahl - invalid byte order,
+>>18 leshort 10 MIPS R3000_LE,
+>>18 leshort 11 RS6000 - invalid byte order,
+>>18 leshort 15 PA-RISC - invalid byte order,
+>>18 leshort 16 nCUBE,
+>>18 leshort 17 VPP500,
+>>18 leshort 18 SPARC32PLUS,
+>>18 leshort 20 PowerPC,
+>>18 leshort 0x9026 Alpha,
+>>20 lelong 0 invalid version
+>>20 lelong 1 version 1
+>>36 lelong 1 MathCoPro/FPU/MAU Required
+>8 string >\0 (%s)
+>5 byte 2 MSB
+>>16 beshort 0 no file type,
+>>16 beshort 1 relocatable,
+>>16 beshort 2 executable,
+>>16 beshort 3 shared object,
+>>16 beshort 4 core file,
+>>>(0x38+0xcc) string >\0 of '%s'
+>>>(0x38+0x10) belong >0 (signal %d),
+>>16 beshort &0xff00 processor-specific,
+>>18 beshort 0 no machine,
+>>18 beshort 1 AT&T WE32100,
+>>18 beshort 2 SPARC,
+>>18 beshort 3 Intel 80386 - invalid byte order,
+>>18 beshort 4 Motorola 68000,
+>>18 beshort 5 Motorola 88000,
+>>18 beshort 6 Intel 80486 - invalid byte order,
+>>18 beshort 7 Intel 80860,
+>>18 beshort 8 MIPS R3000_BE,
+>>18 beshort 9 Amdahl,
+>>18 beshort 10 MIPS R3000_LE - invalid byte order,
+>>18 beshort 11 RS6000,
+>>18 beshort 15 PA-RISC,
+>>18 beshort 16 nCUBE,
+>>18 beshort 17 VPP500,
+>>18 beshort 18 SPARC32PLUS,
+>>18 beshort 20 PowerPC or cisco 4500,
+>>18 beshort 21 cisco 7500,
+>>18 beshort 24 cisco SVIP,
+>>18 beshort 25 cisco 7200,
+>>18 beshort 36 cisco 12000,
+>>18 beshort 0x9026 Alpha,
+>>20 belong 0 invalid version
+>>20 belong 1 version 1
+>>36 belong 1 MathCoPro/FPU/MAU Required
diff --git a/contrib/libs/libmagic/magic/Magdir/openfst b/contrib/libs/libmagic/magic/Magdir/openfst
new file mode 100644
index 0000000000..8df9b56b85
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/openfst
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# $File: openfst,v 1.1 2019/09/30 15:58:24 christos Exp $
+# openfs: file(1) magic for OpenFST (Weighted finite-state tranducer library)
+
+0 long 0x7eb2fdd6 OpenFst binary FST data
+>&0 pstring/l x \b, fst type: %s
+>>&0 pstring/l x \b, arc type: %s
+>>>&0 long x \b, version: %d
+>>>>&20 quad x \b, num states: %lld
+>>>>>&0 quad >0 \b, num arcs: %lld
+
+0 long 0x56515c OpenFst binary FAR data, far type: stlist
+>4 long x \b, version: %d
+
+0 long 0x7eb2f35c OpenFst binary FAR data, far type: sttable
+>4 long x \b, version: %d
diff --git a/contrib/libs/libmagic/magic/Magdir/opentimestamps b/contrib/libs/libmagic/magic/Magdir/opentimestamps
new file mode 100644
index 0000000000..f2f0e3ec11
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/opentimestamps
@@ -0,0 +1,16 @@
+
+#------------------------------------------------------------
+# $File: opentimestamps,v 1.1 2019/05/27 01:27:31 christos Exp $
+# OpenTimestamps related magic entries
+# https://opentimestamps.org/
+# https://en.wikipedia.org/wiki/OpenTimestamps
+# "Emanuele Cisbani" <emanuele.cisbani@gmail.com>
+#------------------------------------------------------------
+
+# OpenTimestamps Proof .ots format.
+# Magic is defined here:
+# https://github.com/opentimestamps/python-opentimestamps/\
+# blob/master/opentimestamps/core/timestamp.py#L273
+
+0 string \x00\x4f\x70\x65\x6e\x54\x69\x6d\x65\x73\x74\x61\x6d\x70\x73\x00 OpenTimestamps
+>16 string \x00\x50\x72\x6f\x6f\x66\x00\xbf\x89\xe2\xe8\x84\xe8\x92\x94\x01 Proof
diff --git a/contrib/libs/libmagic/magic/Magdir/oric b/contrib/libs/libmagic/magic/Magdir/oric
new file mode 100644
index 0000000000..38c02c5751
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/oric
@@ -0,0 +1,16 @@
+
+#------------------------------------------------------------------------------
+# $File: oric,v 1.2 2022/04/25 17:28:20 christos Exp $
+# Oric tape files
+# From: Stefan A. Haubenthal <polluks@sdf.lonestar.org>
+# References:
+# http://fileformats.archiveteam.org/wiki/TAP_(Oric)
+# http://fileformats.archiveteam.org/wiki/DSK_(Oric)
+0 string \x16\x16\x16\x24 Oric tape,
+>6 byte =0x00 BASIC,
+>6 byte =0x80 memory block,
+>7 byte >0x00 autorun,
+>13 string x "%.15s"
+
+0 string ORICDISK Oric Image
+0 string MFM_DISK Oric Image
diff --git a/contrib/libs/libmagic/magic/Magdir/os2 b/contrib/libs/libmagic/magic/Magdir/os2
new file mode 100644
index 0000000000..cb43e999f6
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/os2
@@ -0,0 +1,186 @@
+
+#------------------------------------------------------------------------------
+# $File: os2,v 1.14 2022/03/21 21:25:50 christos Exp $
+# os2: file(1) magic for OS/2 files
+#
+
+# Provided 1998/08/22 by
+# David Mediavilla <davidme.news@REMOVEIFNOTSPAMusa.net>
+1 search/100 InternetShortcut MS Windows 95 Internet shortcut text
+!:mime application/x-mswinurl
+!:ext url
+>17 search/100 URL= (URL=<
+>>&0 string x \b%s>)
+
+# OS/2 URL objects
+# Provided 1998/08/22 by
+# David Mediavilla <davidme.news@REMOVEIFNOTSPAMusa.net>
+#0 string http: OS/2 URL object text
+#>5 string >\ (WWW) <http:%s>
+#0 string mailto: OS/2 URL object text
+#>7 string >\ (email) <%s>
+#0 string news: OS/2 URL object text
+#>5 string >\ (Usenet) <%s>
+#0 string ftp: OS/2 URL object text
+#>4 string >\ (FTP) <ftp:%s>
+#0 string file: OS/2 URL object text
+#>5 string >\ (Local file) <%s>
+
+# >>>>> OS/2 INF/HLP <<<<< (source: Daniel Dissett ddissett@netcom.com)
+# URL: http://fileformats.archiveteam.org/wiki/INF/HLP_(OS/2)
+# Reference: http://www.edm2.com/0308/inf.html
+# Carl Hauser (chauser.parc@xerox.com) and
+# Marcus Groeber (marcusg@ph-cip.uni-koeln.de)
+# list the following header format in inf02a.doc:
+#
+# int16 ID; // ID magic word (5348h = "HS")
+# int8 unknown1; // unknown purpose, could be third letter of ID
+# int8 flags; // probably a flag word...
+# // bit 0: set if INF style file
+# // bit 4: set if HLP style file
+# // patching this byte allows reading HLP files
+# // using the VIEW command, while help files
+# // seem to work with INF settings here as well.
+# int16 hdrsize; // total size of header
+# int16 unknown2; // unknown purpose
+#
+0 string HSP\x01\x9b\x00 OS/2 INF
+!:mime application/x-os2-inf
+!:ext inf
+>107 string >0 (%s)
+0 string HSP\x10\x9b\x00 OS/2 HLP
+!:mime application/x-os2-hlp
+!:ext hlp
+>107 string >0 (%s)
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/MSG_(OS/2)
+# Reference: https://github.com/OS2World/UTIL-SYSTEM-MKMSGF/blob/master/mkmsgf.h
+# Note: created by MKMSGF.EXE. Text source can be recreated by E_MSGF
+# example like OS001H.MSG
+0 string \xffMKMSGF\0 OS/2 help message
+!:mime application/x-os2-msg
+!:ext msg
+# identifier[3] like: DOS NET REX SYS ...
+>8 string x '%.3s'
+# msgnumber: number of messages
+>11 uleshort x \b, %u messages
+# firstmsgnumber; number of the first message like: some times 0 often 1 169 1000 3502
+>13 uleshort >1 \b, 1st number %u
+# offset16bit; 1~Index table has 16-bit offsets (files<64k) 0~Index table has 32-bit offsets
+>15 ubyte =0 \b, 32-bit
+#>15 ubyte =1 \b, 16-bit
+# version; file version: 2~new 0~old
+>16 uleshort !2 \b, version %u
+# indextaboffset; offset of index table: 1F~after header 0~no index table for version 0?
+>18 uleshort >0
+>>18 uleshort !0x1f \b, at %#x index
+# 32-bit offset
+>>15 ubyte =0
+# offset with message table
+>>>(18.s) ulelong x \b, at %#x
+# 1st message
+# http://www.os2museum.com/files/docs/os210ptk/os2-1.0-ptk-tools-1988.pdf
+# message type: E~Error H~Help I~Information P~Prompt W~Warning ?
+>>>>(&-4.l) ubyte x %c-type
+>>>>>&0 string x %s
+# 16-bit offset
+>>15 ubyte =1
+# msgnum; message number
+>>>(18.s) uleshort x \b, number %u
+# msgindex; offset of message from begin of file
+>>>(18.s+2) uleshort x at %#x
+# message type E H I P W ?
+>>>>(&-2.s) ubyte x %c-type
+# skip newline carriage return
+>>>>>&0 ubeshort =0x0D0a
+>>>>>>&0 string x %s
+>>>>>&0 ubeshort !0x0D0a
+>>>>>>&-2 string x %s
+# for version 0 index table apparently at offset 1F
+>16 uleshort 0
+>>15 ubyte 1
+# 1st message 16-bit
+>>>0x1F uleshort x \b, at %#x
+# message type: E~Error H~Help I~Information P~Prompt W~Warning ?
+>>>>(0x1F.s) ubyte x %c-type
+>>>>>&0 string x %s
+# 2nd message 16-bit
+>>>0x21 uleshort x \b, at %#x
+>>>>(0x21.s) ubyte x %c-type
+>>>>>&0 string x %s
+# 3rd message 16-bit
+>>>0x23 uleshort x \b, at %#x
+>>>>(0x23.s) ubyte x %c-type
+>>>>>&0 string x %s
+# version 0 32-bit
+>>15 ubyte 0
+# 1st message 32-bit
+>>>0x1f ulelong x \b, at %#x
+>>>>(0x1F.l) ubyte x %c-type
+>>>>>&0 string x %s
+# 2nd message 32-bit
+>>>0x23 ulelong x \b, at %#x
+>>>>(0x23.l) ubyte x %c-type
+>>>>>&0 string x %s
+# 3rd message 32-bit
+>>>0x27 ulelong x \b, AT %#x
+>>>>(0x27.l) ubyte x %c-type
+>>>>>&0 string x %s
+# countryinfo; offset of country info block: 0 for version 0
+>20 uleshort !0 \b, at %#x countryinfo
+# nextcoutryinfo
+>>22 uleshort >0 \b, at %#x next
+# reserved[5]; Must be 0
+>>25 ulelong !0 \b, RESERVED %#x
+>>(20.s) use os2-msg-info
+# display country info block of MKMSGF message file
+0 name os2-msg-info
+# bytesperchar; bytes per char: 1~SBCS 2~DBCS
+>0 ubyte >1 \b, %u bytes/char
+# reserved; Not known
+>1 uleshort !0 \b, reserved %#x
+# langfamilyID; language family ID like: 0~? 1~Arabic ... 7~German ... 9~English ... 34~Slovene
+>3 uleshort >0 \b, language %u
+# langversionID; like: 7_1~German 7_2~Swiss German 12_1~French 12_3~Canadian French
+>>5 uleshort x \b_%u
+# langfamilyID too high. This should not happen
+>3 uleshort >34 (invalid language)
+# codepagesnumber; number of codepages like: 1 2 ... 16
+>7 uleshort x \b, %u code page
+# plural s
+>7 uleshort >1 \bs
+# too many number of codepages. This should not happen
+>7 uleshort >16 (Too many)
+# codepages[16]; codepages list like 437 850 ...
+>7 uleshort <17
+# 1st code page
+>>9 uleshort >0 %u
+# possible 2nd code page number
+>>>7 uleshort >1
+>>>>11 uleshort x %u
+# filename[260]; name of file like: dbaseos2.msg dde4c01e.msg os2ldr.mgr xdfh.msg ...
+>41 string x \b, %s
+
+# OS/2 INI (this is a guess)
+0 string \xff\xff\xff\xff\x14\0\0\0 OS/2 INI
+!:mime application/x-os2-ini
+!:ext ini
+
+# From: Joerg Jenderek
+# URL: http://warpin.netlabs.org/
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/ark-wpi.trid.xml
+# Note: called by TrID "WarpIN Installer"
+# probably magic at the beginning
+0 ubelong =0x770402BE WarpIN Installer
+#>4 ubelong =0x03000000
+#!:mime application/octet-stream
+!:mime application/x-os2-wpi
+!:ext wpi
+# creator program name like: "reserved" or "WIC x.y.z"
+>0x106 string x \b, created by %s
+# name like: "reserved" or "OS/2 Netlabs"
+>0x146 string x \b, '%s'
+# name like: "N/A" "http://warpin.netlabs.org"
+>0x186 string x \b, URL %s
+
diff --git a/contrib/libs/libmagic/magic/Magdir/os400 b/contrib/libs/libmagic/magic/Magdir/os400
new file mode 100644
index 0000000000..6a05f083eb
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/os400
@@ -0,0 +1,39 @@
+
+#------------------------------------------------------------------------------
+# $File: os400,v 1.5 2009/09/19 16:28:11 christos Exp $
+# os400: file(1) magic for IBM OS/400 files
+#
+# IBM OS/400 (i5/OS) Save file (SAVF) - gerardo.cacciari@gmail.com
+# In spite of its quite variable format (due to internal memory page
+# length differences between CISC and RISC versions of the OS) the
+# SAVF structure hasn't suitable offsets to identify the catalog
+# header in the first descriptor where there are some useful infos,
+# so we must search in a somewhat large area for a particular string
+# that represents the EBCDIC encoding of 'QSRDSSPC' (save/restore
+# descriptor space) preceded by a two byte constant.
+#
+1090 search/7393 \x19\xDB\xD8\xE2\xD9\xC4\xE2\xE2\xD7\xC3 IBM OS/400 save file data
+>&212 byte 0x01 \b, created with SAVOBJ
+>&212 byte 0x02 \b, created with SAVLIB
+>&212 byte 0x07 \b, created with SAVCFG
+>&212 byte 0x08 \b, created with SAVSECDTA
+>&212 byte 0x0A \b, created with SAVSECDTA
+>&212 byte 0x0B \b, created with SAVDLO
+>&212 byte 0x0D \b, created with SAVLICPGM
+>&212 byte 0x11 \b, created with SAVCHGOBJ
+>&213 byte 0x44 \b, at least V5R4 to open
+>&213 byte 0x43 \b, at least V5R3 to open
+>&213 byte 0x42 \b, at least V5R2 to open
+>&213 byte 0x41 \b, at least V5R1 to open
+>&213 byte 0x40 \b, at least V4R5 to open
+>&213 byte 0x3F \b, at least V4R4 to open
+>&213 byte 0x3E \b, at least V4R3 to open
+>&213 byte 0x3C \b, at least V4R2 to open
+>&213 byte 0x3D \b, at least V4R1M4 to open
+>&213 byte 0x3B \b, at least V4R1 to open
+>&213 byte 0x3A \b, at least V3R7 to open
+>&213 byte 0x35 \b, at least V3R6 to open
+>&213 byte 0x36 \b, at least V3R2 to open
+>&213 byte 0x34 \b, at least V3R1 to open
+>&213 byte 0x31 \b, at least V3R0M5 to open
+>&213 byte 0x30 \b, at least V2R3 to open
diff --git a/contrib/libs/libmagic/magic/Magdir/os9 b/contrib/libs/libmagic/magic/Magdir/os9
new file mode 100644
index 0000000000..74b47f3585
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/os9
@@ -0,0 +1,80 @@
+
+#------------------------------------------------------------------------------
+# $File: os9,v 1.8 2017/03/17 21:35:28 christos Exp $
+#
+# Copyright (c) 1996 Ignatios Souvatzis. All rights reserved.
+#
+# 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.
+#
+# 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.
+#
+#
+#
+# OS9/6809 module descriptions:
+#
+0 beshort 0x87CD OS9/6809 module:
+#
+>6 byte&0x0f 0x00 non-executable
+>6 byte&0x0f 0x01 machine language
+>6 byte&0x0f 0x02 BASIC I-code
+>6 byte&0x0f 0x03 Pascal P-code
+>6 byte&0x0f 0x04 C I-code
+>6 byte&0x0f 0x05 COBOL I-code
+>6 byte&0x0f 0x06 Fortran I-code
+#
+>6 byte&0xf0 0x10 program executable
+>6 byte&0xf0 0x20 subroutine
+>6 byte&0xf0 0x30 multi-module
+>6 byte&0xf0 0x40 data module
+#
+>6 byte&0xf0 0xC0 system module
+>6 byte&0xf0 0xD0 file manager
+>6 byte&0xf0 0xE0 device driver
+>6 byte&0xf0 0xF0 device descriptor
+#
+# OS9/m68k stuff (to be continued)
+#
+0 beshort 0x4AFC OS9/68K module:
+#
+# attr
+>0x14 byte&0x80 0x80 re-entrant
+>0x14 byte&0x40 0x40 ghost
+>0x14 byte&0x20 0x20 system-state
+#
+# lang:
+#
+>0x13 byte 1 machine language
+>0x13 byte 2 BASIC I-code
+>0x13 byte 3 Pascal P-code
+>0x13 byte 4 C I-code
+>0x13 byte 5 COBOL I-code
+>0x13 byte 6 Fortran I-code
+#
+#
+# type:
+#
+>0x12 byte 1 program executable
+>0x12 byte 2 subroutine
+>0x12 byte 3 multi-module
+>0x12 byte 4 data module
+>0x12 byte 11 trap library
+>0x12 byte 12 system module
+>0x12 byte 13 file manager
+>0x12 byte 14 device driver
+>0x12 byte 15 device descriptor
diff --git a/contrib/libs/libmagic/magic/Magdir/osf1 b/contrib/libs/libmagic/magic/Magdir/osf1
new file mode 100644
index 0000000000..4e9147196d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/osf1
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# $File: osf1,v 1.7 2009/09/19 16:28:11 christos Exp $
+#
+# Mach magic number info
+#
+0 long 0xefbe OSF/Rose object
+# I386 magic number info
+#
+0 short 0565 i386 COFF object
diff --git a/contrib/libs/libmagic/magic/Magdir/palm b/contrib/libs/libmagic/magic/Magdir/palm
new file mode 100644
index 0000000000..5d2b913c35
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/palm
@@ -0,0 +1,156 @@
+
+#------------------------------------------------------------------------------
+# $File: palm,v 1.15 2021/12/16 21:50:06 christos Exp $
+# palm: file(1) magic for PalmOS {.prc,.pdb}: applications, docfiles, and hacks
+#
+# Brian Lalor <blalor@hcirisc.cs.binghamton.edu>
+
+# These are weak, byte 59 is not guaranteed to be 0 and there are
+# 8 character identifiers at byte 60, one I found for appl is BIGb.
+# What are the possibilities and where is this documented?
+
+# The common header format for PalmOS .pdb/.prc files is
+# {
+# char name[ 32 ];
+# Word attributes;
+# Word version;
+# DWord creationDate;
+# DWord modificationDate;
+# DWord lastBackupDate;
+# DWord modificationNumber;
+# DWord appInfoID;
+# DWord sortInfoID;
+# char type[4];
+# char creator[4];
+# DWord uniqueIDSeed;
+# RecordListType recordList;
+# };
+#
+# Datestamps are unsigned seconds since the MacOS epoch (Jan 1, 1904),
+# or Unix/POSIX time + 2082844800.
+
+0 name aportisdoc
+# date is supposed to be big-endian seconds since 1 Jan 1904, but many
+# files contain the timestamp in little-endian or a completely
+# nonsensical value...
+#>36 bedate-2082844800 >0 \b, created %s
+# compression: 1=uncomp, 2=orig, 0x4448=HuffDic
+>(78.L) beshort =1 \b, uncompressed
+# compressed
+>(78.L) beshort >1
+>>(78.L+4) belong x \b, %d bytes uncompressed
+
+# appl
+#60 string appl PalmOS application
+#>0 string >\0 "%s"
+
+# HACK
+#60 string HACK HackMaster hack
+#>0 string >\0 "%s"
+
+# iSiloX e-book
+60 string SDocSilX iSiloX E-book
+>0 string >\0 "%s"
+
+# Mobipocket (www.mobipocket.com), donated by Carl Witty
+# expanded by Ralf Brown
+60 string BOOKMOBI Mobipocket E-book
+!:mime application/x-mobipocket-ebook
+# MobiPocket stores a full title, pointed at by the belong at offset
+# 0x54 in its header at (78.L), with length given by the belong at
+# offset 0x58.
+# there's no guarantee that the title string is null-terminated, but
+# we currently can't specify a variable-length string where the length
+# field is not at the start of the string; in practice, the data
+# following the string always seems to start with a zero byte
+>(78.L) belong x
+>>&(&0x50.L-4) string >\0 "%s"
+>0 use aportisdoc
+>>(78.L+0x68) belong >0 \b, version %d
+>>(78.L+0x1C) belong !0 \b, codepage %d
+>>(78.L+0x0C) beshort >0 \b, encrypted (type %d)
+
+# AportisDoc/PalmDOC
+60 string TEXtREAd AportisDoc/PalmDOC E-book
+>0 string >\0 "%s"
+>0 use aportisdoc
+
+# Variety of PalmOS document types
+# Michael-John Turner <mj@debian.org>
+# Thanks to Hasan Umit Ezerce <humit@tr-net.net.tr> for his DocType
+60 string BVokBDIC BDicty PalmOS document
+>0 string >\0 "%s"
+60 string DB99DBOS DB PalmOS document
+>0 string >\0 "%s"
+60 string vIMGView FireViewer/ImageViewer PalmOS document
+>0 string >\0 "%s"
+60 string PmDBPmDB HanDBase PalmOS document
+>0 string >\0 "%s"
+60 string InfoINDB InfoView PalmOS document
+>0 string >\0 "%s"
+60 string ToGoToGo iSilo PalmOS document
+>0 string >\0 "%s"
+60 string JfDbJBas JFile PalmOS document
+>0 string >\0 "%s"
+60 string JfDbJFil JFile Pro PalmOS document
+>0 string >\0 "%s"
+60 string DATALSdb List PalmOS document
+>0 string >\0 "%s"
+60 string Mdb1Mdb1 MobileDB PalmOS document
+>0 string >\0 "%s"
+60 string PNRdPPrs PeanutPress PalmOS document
+>0 string >\0 "%s"
+60 string DataPlkr Plucker PalmOS document
+>0 string >\0 "%s"
+60 string DataSprd QuickSheet PalmOS document
+>0 string >\0 "%s"
+60 string SM01SMem SuperMemo PalmOS document
+>0 string >\0 "%s"
+60 string TEXtTlDc TealDoc PalmOS document
+>0 string >\0 "%s"
+60 string InfoTlIf TealInfo PalmOS document
+>0 string >\0 "%s"
+60 string DataTlMl TealMeal PalmOS document
+>0 string >\0 "%s"
+60 string DataTlPt TealPaint PalmOS document
+>0 string >\0 "%s"
+60 string dataTDBP ThinkDB PalmOS document
+>0 string >\0 "%s"
+60 string TdatTide Tides PalmOS document
+>0 string >\0 "%s"
+60 string ToRaTRPW TomeRaider PalmOS document
+>0 string >\0 "%s"
+
+# A GutenPalm zTXT etext for use on Palm Pilots (http://gutenpalm.sf.net)
+# For version 1.xx zTXTs, outputs version and numbers of bookmarks and
+# annotations.
+# For other versions, just outputs version.
+#
+60 string zTXT A GutenPalm zTXT e-book
+>0 string >\0 "%s"
+>(0x4E.L) byte 0
+>>(0x4E.L+1) byte x (v0.%02d)
+>(0x4E.L) byte 1
+>>(0x4E.L+1) byte x (v1.%02d)
+>>>(0x4E.L+10) beshort >0
+>>>>(0x4E.L+10) beshort <2 - 1 bookmark
+>>>>(0x4E.L+10) beshort >1 - %d bookmarks
+>>>(0x4E.L+14) beshort >0
+>>>>(0x4E.L+14) beshort <2 - 1 annotation
+>>>>(0x4E.L+14) beshort >1 - %d annotations
+>(0x4E.L) byte >1 (v%d.
+>>(0x4E.L+1) byte x %02d)
+
+# Palm OS .prc file types
+60 string libr
+# flags, only bit 0 or bit 6
+# https://en.wikipedia.org/wiki/PRC_%28Palm_OS%29
+# https://web.mit.edu/tytso/www/pilot/prc-format.html
+>0x20 beshort&0xffbe 0
+>>0 string >\0 Palm OS dynamic library data "%s"
+60 string ptch Palm OS operating system patch data
+>0 string >\0 "%s"
+
+# Mobipocket (www.mobipocket.com), donated by Carl Witty
+60 string BOOKMOBI Mobipocket E-book
+>0 string >\0 "%s"
diff --git a/contrib/libs/libmagic/magic/Magdir/parix b/contrib/libs/libmagic/magic/Magdir/parix
new file mode 100644
index 0000000000..ba5cbf5fe8
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/parix
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: parix,v 1.5 2020/03/08 22:18:32 christos Exp $
+#
+# Parix COFF executables
+# From: Ignatios Souvatzis <ignatios@cs.uni-bonn.de>
+#
+0 beshort&0xefff 0x8ACE PARIX
+>0 byte&0xf0 0x80 T800
+>0 byte&0xf0 0x90 T9000
+>19 byte&0x02 0x02 executable
+>19 byte&0x02 0x00 object
+>19 byte&0x0c 0x00 not stripped
diff --git a/contrib/libs/libmagic/magic/Magdir/parrot b/contrib/libs/libmagic/magic/Magdir/parrot
new file mode 100644
index 0000000000..b2a56c817a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/parrot
@@ -0,0 +1,22 @@
+#------------------------------------------------------------------------------
+# $File: parrot,v 1.2 2019/04/19 00:42:27 christos Exp $
+# parrot: file(1) magic for Parrot Virtual Machine
+# URL: https://www.lua.org/
+# From: Lubomir Rintel <lkundrak@v3.sk>
+
+# Compiled Parrot byte code
+0 string \376PBC\r\n\032\n Parrot bytecode
+>64 byte x %d.
+>72 byte x \b%d,
+>8 byte >0 %d byte words,
+>16 byte 0 little-endian,
+>16 byte 1 big-endian,
+>32 byte 0 IEEE-754 8 byte double floats,
+>32 byte 1 x86 12 byte long double floats,
+>32 byte 2 IEEE-754 16 byte long double floats,
+>32 byte 3 MIPS 16 byte long double floats,
+>32 byte 4 AIX 16 byte long double floats,
+>32 byte 5 4-byte floats,
+>40 byte x Parrot %d.
+>48 byte x \b%d.
+>56 byte x \b%d
diff --git a/contrib/libs/libmagic/magic/Magdir/pascal b/contrib/libs/libmagic/magic/Magdir/pascal
new file mode 100644
index 0000000000..6168802456
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pascal
@@ -0,0 +1,39 @@
+#------------------------------------------------------------------------------
+# $File: pascal,v 1.4 2022/07/30 16:53:06 christos Exp $
+# pascal: file(1) magic for Pascal source
+#
+0 search/8192 (input, Pascal source text
+!:mime text/x-pascal
+#0 regex \^program Pascal source text
+#!:mime text/x-pascal
+#0 regex \^record Pascal source text
+#!:mime text/x-pascal
+
+# Free Pascal
+0 string PPU Pascal unit
+>3 string x \b, version %s
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Dan_Bricklin
+0 string/b Type
+# URL: https://dl.winworldpc.com/Dan%20Bricklins%20Demo%20II%20Version%202%20Manual.7z
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dbd-v2.trid.xml
+>4 string D2 Dan Bricklin's Demo 2 demo
+#!:mime application/octet-stream
+!:ext dbd
+# URL: https://muhaz.org/turbo-pascal-download-details.html
+# From: Joerg Jenderek
+# Note: used by Turbo Pascal 5.5 TOUR.EXE
+>4 string T2 Turbo Pascal TOUR data
+#!:mime application/octet-stream
+!:mime application/x-borland-cbt
+!:ext cbt
+# WHAT iS THAT?
+#>4 string \040P Dan Bricklin's Demo 2 foo
+#!:mime application/octet-stream
+# _PPRINT.SG2 _PASCII.SG2
+#!:ext sg2
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dbd-gen.trid.xml
+>4 default x Dan Bricklin's Demo demo (generic)
+#!:mime application/octet-stream
+!:ext dbd
diff --git a/contrib/libs/libmagic/magic/Magdir/pbf b/contrib/libs/libmagic/magic/Magdir/pbf
new file mode 100644
index 0000000000..0ab7a88101
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pbf
@@ -0,0 +1,11 @@
+
+#------------------------------------------------------------------------------
+# $File: pbf,v 1.3 2019/04/19 00:42:27 christos Exp $
+# file(1) magic(5) data for OpenStreetMap
+
+# OpenStreetMap Protocolbuffer Binary Format (.osm.pbf)
+# https://wiki.openstreetmap.org/wiki/PBF_Format
+# From: Markus Heidelberg <markus.heidelberg@web.de>
+0 belong&0xfffffff0 0
+>4 beshort 0x0A09
+>>6 string OSMHeader OpenStreetMap Protocolbuffer Binary Format
diff --git a/contrib/libs/libmagic/magic/Magdir/pbm b/contrib/libs/libmagic/magic/Magdir/pbm
new file mode 100644
index 0000000000..40ecf49114
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pbm
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: pbm,v 1.6 2009/09/19 16:28:11 christos Exp $
+# pbm: file(1) magic for Portable Bitmap files
+#
+# XXX - byte order?
+#
+0 short 0x2a17 "compact bitmap" format (Poskanzer)
diff --git a/contrib/libs/libmagic/magic/Magdir/pc88 b/contrib/libs/libmagic/magic/Magdir/pc88
new file mode 100644
index 0000000000..03822f5027
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pc88
@@ -0,0 +1,24 @@
+#------------------------------------------------------------------------------
+# pc88: file(1) magic for the NEC Home Computer
+# v1.0
+# Fabio R. Schmidlin <sd-snatcher@users.sourceforge.net>
+
+# PC88 2D disk image
+0x20 ulelong&0xFFFFFEFF 0x2A0
+>0x10 string \0\0\0\0\0\0\0\0\0\0
+>>0x280 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
+>>>0x1A ubyte&0xEF 0
+>>>>0x1B ubyte&0x8F 0
+>>>>>0x1B ubyte&70 <0x40
+>>>>>>0x1C ulelong >0x21
+>>>>>>>0 regex [[:print:]]* NEC PC-88 disk image, name=%s
+>>>>>>>>0x1B ubyte 0 \b, media=2D
+>>>>>>>>0x1B ubyte 0x10 \b, media=2DD
+>>>>>>>>0x1B ubyte 0x20 \b, media=2HD
+>>>>>>>>0x1B ubyte 0x30 \b, media=1D
+>>>>>>>>0x1B ubyte 0x40 \b, media=1DD
+>>>>>>>>0x1A ubyte 0x10 \b, write-protected
+
+
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/pc98 b/contrib/libs/libmagic/magic/Magdir/pc98
new file mode 100644
index 0000000000..e8f6b8a57a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pc98
@@ -0,0 +1,77 @@
+#------------------------------------------------------------------------------
+# pc98: file(1) magic for the MSX Home Computer
+# v1.0
+# Fabio R. Schmidlin <sd-snatcher@users.sourceforge.net>
+
+# Maki-chan v1 Graphic format
+# The image resolution should be X=(44.L - 40.L) and Y=(46.L - 42.L), but I couldn't find a way to do so
+# http://www.jisyo.com/viewer/faq/maki_tech.htm
+0 string/b MAKI01 Maki-chan v1.
+>6 ubyte|0x20 x \b%c image
+>8 ubelong >0x40404040 \b, system ID:
+>>8 byte x %c
+>>9 byte x \b%c
+>>10 byte x \b%c
+>>11 byte x \b%c
+>44 ubeshort x \b, %dx
+>46 ubeshort x \b%d
+>38 ubeshort&2 0 \b, 16 paletted RGB colors
+>38 ubeshort&2 2 \b, 8 fixed RGB colors
+>38 ubeshort&1 1 \b, 2:1 dot aspect ratio
+
+# Maki-chan v2 Graphic format
+# http://www.jisyo.com/viewer/faq/mag_tech.htm
+# https://mooncore.eu/bunny/txt/makichan.htm
+# http://metanest.jp/mag/mag.xhtml
+0 string/b MAKI02\ \ Maki-chan v2 image,
+>8 byte x system ID: %c
+>9 byte x \b%c
+>10 byte x \b%c
+>11 byte x \b%c,
+>13 search/0x200 \x1A
+#Maki-chan video modes are a bit messy and seems to have been expanded over the years without too much planing:
+#1) When offset1(ubeshort) !=0x0344:
+# 1.1) And offset3(ubyte).b7=0:
+# - b0=pixel aspect ratio: 1=2:1 (note: this ignores that the machine's 1:1 pixel aspect ratio isn't really 1:1)
+# - b1=number of colors: 0=16 colors, 1=8 colors
+# - b2=Palette or fixed colors flag (called "analog" and "digital" in the doc): 0=Paletted, 1=Fixed colors encoded directly in the pixel data
+# 1.2) And offset3(ubyte).B7=1:
+# - b0=256 paletted colors
+# - b1=256 fixed colors using the MSX SCR8 palette
+#2) When offset1(ubeshort) =0x0344:
+# - 256x212 image with 19268 YJK colors. The usual resolution and color information fields from the file must be ignored
+>>&1 ubeshort 0x0344 256x212, 19268 fixed YJK colors
+>>&1 ubeshort !0x0344
+>>>&5 uleshort+1 x %dx
+>>>&7 uleshort+1 x \b%d,
+>>>&0 ubyte&0x86 0x00 16 paletted RGB colors
+>>>&0 ubyte&0x86 0x02 8 paletted RGB colors
+>>>&0 ubyte&0x86 0x04 16 fixed RGB colors
+>>>&0 ubyte&0x86 0x06 8 fixed RGB colors
+>>>&0 ubyte&0x81 0x80 256 paletted RGB colors
+>>>&0 ubyte&0x81 0x81 256 fixed MSX-SCR8 colors
+>>>&0 ubyte&0x01 1 \b, 2:1 dot aspect ratio
+
+# XLD4 (Q4) picture
+11 string/b MAJYO XLD4(Q4) picture
+
+# Yanagisawa Pi picture
+#0 string Pi\x1A\0 Yanagisawa Pi picture
+#>3 search/0x200 \x04
+0 string Pi
+>2 search/0x200 \x1A
+>>&0 ubyte 0
+>>>&3 ubyte 4 Yanagisawa Pi 16 color picture,
+>>>&4 byte x system ID: %c
+>>>&5 byte x \b%c
+>>>&6 byte x \b%c
+>>>&7 byte x \b%c,
+>>>&10 ubeshort x %dx
+>>>&12 ubeshort x \b%d
+>>>&3 ubyte 8 Yanagisawa Pi 256 color picture
+>>>&4 byte x system ID: %c
+>>>&5 byte x \b%c
+>>>&6 byte x \b%c
+>>>&7 byte x \b%c,
+>>>&10 ubeshort x %dx
+>>>&12 ubeshort x \b%d
diff --git a/contrib/libs/libmagic/magic/Magdir/pci_ids b/contrib/libs/libmagic/magic/Magdir/pci_ids
new file mode 100644
index 0000000000..34bc2e2f8a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pci_ids
@@ -0,0 +1,116 @@
+
+#------------------------------------------------------------------------------
+# $File: pci_ids,v 1.1 2022/04/02 14:47:42 christos Exp $
+# pci.ids: file(1) magic for PCI specific informations
+#
+
+# Vendor identification (ID) https://pci-ids.ucw.cz/v2.2/pci.ids
+# show hexadecimal PCI vendor identification in human readable text form
+0 name PCI-vendor
+# ID vendor name
+#>0 uleshort =0x0f00 fOO
+>0 uleshort =0x1000 Broadcom
+>0 uleshort =0x1002 AMD/ATI
+>0 uleshort =0x1013 Cirrus Logic
+>0 uleshort =0x1014 IBM
+>0 uleshort =0x1022 AMD
+>0 uleshort =0x1050 Winbond
+>0 uleshort =0x105a Promise
+>0 uleshort =0x1095 Silicon
+>0 uleshort =0x10EC Realtek
+>0 uleshort =0x10de NVIDIA
+>0 uleshort =0x1106 VIA
+# Woodward McCoach, Inc.
+>0 uleshort =0x1231 Woodward
+#
+>0 uleshort =0x1234 Bochs
+>0 uleshort =0x15ad VMware
+>0 uleshort =0x1af4 Virtio
+>0 uleshort =0x1b36 QEMU
+>0 uleshort =0x1de1 Tekram
+# maybe also Promise?
+#>0 uleshort =0x4289 Promise
+#>0 uleshort =0x66a1 FOO
+>0 uleshort =0x8086 Intel
+>0 uleshort =0x9004 Adaptec
+# also Adaptec; but no example
+>0 uleshort =0x9005 Adaptec
+# for unknown/missing manufactors
+>0 default x UNKNOWN
+>>0 uleshort x (%#4.4x)
+
+# https://blog.ladsai.com/pci-configuration-space-class-code.html
+# Base class code https://wiki.osdev.org/PCI
+# show hexadecimal PCI class+sub+ProgIF identification in human readable text form
+0 name PCI-class
+#>0 ubyte x CLASS=%x
+>0 ubyte x
+# Device was built prior definition of the class code field
+>>0 ubyte 0x00 PRIOR
+# Any device except for VGA-Compatible devices like: 2975BIOS.BIN Trm3x5.bin
+# BUT also NVidia44.bin vgabios-stdvga-bin.rom
+#>>>0 ubyte 0x00 NOT VGA
+# VGA-Compatible Device; NO EXAMPLE found here!!
+#>>>0 ubyte 0x01 VGA
+# like 4243.bin
+#>>>0 ubyte 0x04 SUB_CLASS_4
+>>0 ubyte 0x01 storage controller
+# device sub-type and its definition is dependent upon the base-type code
+>>>1 ubyte 0x00 SCSI
+>>>1 ubyte 0x01 IDE
+>>>1 ubyte 0x02 Floppy
+>>>1 ubyte 0x03 IPI
+>>>0 ubyte 0x04 RAID
+>>>1 ubyte 0x05 ATA
+>>>1 ubyte 0x06 SATA
+>>>1 ubyte 0x07 SAS
+>>>1 ubyte 0x08 NVM
+# 4650_sr5.bin "PROMISE" "FT TX4650 Ary X"
+>>>1 ubyte 0x80 OTHER
+>>0 ubyte 0x02 network controller
+>>>1 ubyte 0x00 ethernet
+>>>1 ubyte 0x01 token ring
+>>>1 ubyte 0x02 FDDI
+>>>1 ubyte 0x03 ATM
+>>>1 ubyte 0x04 ISDN
+>>>1 ubyte 0x05 WorldFip
+# PICMG 2.14 Multi Computing
+>>>1 ubyte 0x06 PICMG
+>>>1 ubyte 0x80 OTHER
+>>0 ubyte 0x03 display controller
+>>0 ubyte 0x04 multimedia controller
+>>0 ubyte 0x05 memory controller
+>>0 ubyte 0x06 bridge device
+# Simple Communication Controllers
+>>0 ubyte 0x07 communication controller
+# Base System Peripherals
+>>0 ubyte 0x08 base peripheral
+# Input Devices
+>>0 ubyte 0x09 input device
+# Docking Stations
+>>0 ubyte 0x0A docking station
+>>0 ubyte 0x0B processor
+>>0 ubyte 0x0C serial bus controller
+>>0 ubyte 0x0D wireless controller
+# Intelligent I/O Controllers
+>>0 ubyte 0x0E I/O controller
+# Satellite Communication Controllers
+>>0 ubyte 0x0F satellite controller
+# Encryption/Decryption Controllers
+>>0 ubyte 0x10 encryption controller
+# Data Acquisition and Signal Processing Controllers
+>>0 ubyte 0x11 signal controller
+# Processing Accelerator
+>>0 ubyte 0x12 processing accelerator
+# Non-Essential Instrumentation
+>>0 ubyte 0x13 non-essential
+# reserved or unassigned
+>>0 default x
+# device does not fit any defined class; Unassigned Class (Vendor specific)
+>>>0 ubyte 0xFF UNASSIGNED
+# THIS SHOULD NOT HAPPEN! BUT CLASS=8f for Promise 4650_sr5.bin 8660_sr5.bin
+>>>0 default x RESERVED
+>>>>0 ubyte x (%#x)
+# Prog IF of PCI class code?
+# defines the specific device programming interface
+>2 ubyte >0 \b, ProgIF=%u
diff --git a/contrib/libs/libmagic/magic/Magdir/pcjr b/contrib/libs/libmagic/magic/Magdir/pcjr
new file mode 100644
index 0000000000..c3ab7a25fd
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pcjr
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: pcjr,v 1.1 2021/01/09 15:09:58 christos Exp $
+# pcjr: file(1) magic for PCjr Cartridge image file format
+# From: Francis Laniel <laniel_francis@privacyrequired.com>
+0 string PCjr
+>0x80 beshort 0x55aa PCjr Cartridge image
+>0x200 beshort 0x55aa PCjr Cartridge image
diff --git a/contrib/libs/libmagic/magic/Magdir/pdf b/contrib/libs/libmagic/magic/Magdir/pdf
new file mode 100644
index 0000000000..7a99d8d3cf
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pdf
@@ -0,0 +1,51 @@
+
+#------------------------------------------------------------------------------
+# $File: pdf,v 1.18 2023/07/17 15:57:18 christos Exp $
+# pdf: file(1) magic for Portable Document Format
+#
+
+0 name pdf
+>8 search /Count
+>>&0 regex [0-9]+ \b, %s page(s)
+>8 search/512 /Filter/FlateDecode/ (zip deflate encoded)
+
+0 string %PDF- PDF document
+!:mime application/pdf
+!:strength +60
+!:ext pdf
+>5 byte x \b, version %c
+>7 byte x \b.%c
+>0 use pdf
+
+0 string \012%PDF- PDF document
+!:mime application/pdf
+!:strength +60
+!:ext pdf
+>6 byte x \b, version %c
+>8 byte x \b.%c
+>0 use pdf
+
+0 string \xef\xbb\xbf%PDF- PDF document (UTF-8)
+!:mime application/pdf
+!:strength +60
+!:ext pdf
+>6 byte x \b, version %c
+>8 byte x \b.%c
+>0 use pdf
+
+# From: Nick Schmalenberger <nick@schmalenberger.us>
+# Forms Data Format
+0 string %FDF- FDF document
+!:mime application/vnd.fdf
+!:strength +60
+!:ext pdf
+>5 byte x \b, version %c
+>7 byte x \b.%c
+
+0 search/1024 %PDF- PDF document
+!:mime application/pdf
+!:strength +60
+!:ext pdf
+>&0 byte x \b, version %c
+>&2 byte x \b.%c
+>0 use pdf
diff --git a/contrib/libs/libmagic/magic/Magdir/pdp b/contrib/libs/libmagic/magic/Magdir/pdp
new file mode 100644
index 0000000000..2d18b62df5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pdp
@@ -0,0 +1,42 @@
+
+#------------------------------------------------------------------------------
+# $File: pdp,v 1.11 2017/03/17 21:35:28 christos Exp $
+# pdp: file(1) magic for PDP-11 executable/object and APL workspace
+#
+0 lelong 0101555 PDP-11 single precision APL workspace
+0 lelong 0101554 PDP-11 double precision APL workspace
+#
+# PDP-11 a.out
+#
+0 leshort 0407 PDP-11 executable
+>8 leshort >0 not stripped
+>15 byte >0 - version %d
+
+# updated by Joerg Jenderek at Mar 2013
+# GRR: line below too general as it catches also Windows precompiled setup information *.PNF
+0 leshort 0401
+# skip *.PNF with WinDirPathOffset 58h
+>68 ulelong !0x00000058 PDP-11 UNIX/RT ldp
+# skip *.PNF with high byte of InfVersionDatumCount zero
+#>>15 byte !0 PDP-11 UNIX/RT ldp
+0 leshort 0405 PDP-11 old overlay
+
+0 leshort 0410 PDP-11 pure executable
+>8 leshort >0 not stripped
+>15 byte >0 - version %d
+
+0 leshort 0411 PDP-11 separate I&D executable
+>8 leshort >0 not stripped
+>15 byte >0 - version %d
+
+0 leshort 0437 PDP-11 kernel overlay
+
+# These last three are derived from 2.11BSD file(1)
+0 leshort 0413 PDP-11 demand-paged pure executable
+>8 leshort >0 not stripped
+
+0 leshort 0430 PDP-11 overlaid pure executable
+>8 leshort >0 not stripped
+
+0 leshort 0431 PDP-11 overlaid separate executable
+>8 leshort >0 not stripped
diff --git a/contrib/libs/libmagic/magic/Magdir/perl b/contrib/libs/libmagic/magic/Magdir/perl
new file mode 100644
index 0000000000..4a3756a483
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/perl
@@ -0,0 +1,100 @@
+#------------------------------------------------------------------------------
+# $File: perl,v 1.27 2023/07/17 16:01:36 christos Exp $
+# perl: file(1) magic for Larry Wall's perl language.
+#
+# The `eval' lines recognizes an outrageously clever hack.
+# Keith Waclena <keith@cerberus.uchicago.edu>
+# Send additions to <perl5-porters@perl.org>
+0 search/1024 eval\ "exec\ perl Perl script text
+!:mime text/x-perl
+0 search/1024 eval\ "exec\ /bin/perl Perl script text
+!:mime text/x-perl
+0 search/1024 eval\ "exec\ /usr/bin/perl Perl script text
+!:mime text/x-perl
+0 search/1024 eval\ "exec\ /usr/local/bin/perl Perl script text
+!:mime text/x-perl
+0 search/1024 eval\ 'exec\ perl Perl script text
+!:mime text/x-perl
+0 search/1024 eval\ 'exec\ /bin/perl Perl script text
+!:mime text/x-perl
+0 search/1024 eval\ 'exec\ /usr/bin/perl Perl script text
+!:mime text/x-perl
+0 search/1024 eval\ 'exec\ /usr/local/bin/perl Perl script text
+!:mime text/x-perl
+0 search/1024 eval\ '(exit\ $?0)'\ &&\ eval\ 'exec Perl script text
+!:mime text/x-perl
+0 string #!/usr/bin/env\ perl Perl script text executable
+!:mime text/x-perl
+0 string #!\ /usr/bin/env\ perl Perl script text executable
+!:mime text/x-perl
+0 string #!
+>0 regex \^#!.*/bin/perl([[:space:]].*)*$ Perl script text executable
+!:mime text/x-perl
+
+# by Dmitry V. Levin and Alexey Tourbin
+# check the first line
+0 search/8192 package
+>0 regex \^package[[:space:]]+[0-9A-Za-z_:]+[[:space:]]*([[:space:]]v?[0-9][0-9.]*)?[[:space:]]*; Perl5 module source text
+!:strength + 40
+# not 'p', check other lines
+0 search/8192 !p
+>0 regex \^package[[:space:]]+[0-9A-Za-z_:]+[[:space:]]*([[:space:]]v?[0-9][0-9.]*)?[[:space:]]*;
+>>0 regex \^1[[:space:]]*;|\^(use|sub|my)[[:space:]].*[(;{=] Perl5 module source text
+!:strength + 75
+
+# Perl POD documents
+# From: Tom Hukins <tom@eborcom.com>
+0 search/1024/W \=pod\n Perl POD document text
+0 search/1024/W \n\=pod\n Perl POD document text
+0 search/1024/W \=head1\ Perl POD document text
+0 search/1024/W \n\=head1\ Perl POD document text
+0 search/1024/W \=head2\ Perl POD document text
+0 search/1024/W \n\=head2\ Perl POD document text
+0 search/1024/W \=encoding\ Perl POD document text
+0 search/1024/W \n\=encoding\ Perl POD document text
+
+
+# Perl Storable data files.
+0 string perl-store perl Storable (v0.6) data
+>4 byte >0 (net-order %d)
+>>4 byte &01 (network-ordered)
+>>4 byte =3 (major 1)
+>>4 byte =2 (major 1)
+
+0 string pst0 perl Storable (v0.7) data
+>4 byte >0
+>>4 byte &01 (network-ordered)
+>>4 byte =5 (major 2)
+>>4 byte =4 (major 2)
+>>5 byte >0 (minor %d)
+
+# This is Debian #742949 by Zefram <zefram@fysh.org>:
+# -----------------------------------------------------------
+# The Perl module Hash::SharedMem
+# <https://metacpan.org/release/Hash-SharedMem> defines a file format
+# for a key/value store. Details of the file format are in the "DESIGN"
+# file in the module distribution. Magic:
+0 bequad =0xa58afd185cbf5af7 Hash::SharedMem master file, big-endian
+>8 bequad <0x1000000
+>>15 byte >2 \b, line size 2^%d byte
+>>14 byte >2 \b, page size 2^%d byte
+>>13 byte &1
+>>>13 byte >1 \b, max fanout %d
+0 lequad =0xa58afd185cbf5af7 Hash::SharedMem master file, little-endian
+>8 lequad <0x1000000
+>>8 byte >2 \b, line size 2^%d byte
+>>9 byte >2 \b, page size 2^%d byte
+>>10 byte &1
+>>>10 byte >1 \b, max fanout %d
+0 bequad =0xc693dac5ed5e47c2 Hash::SharedMem data file, big-endian
+>8 bequad <0x1000000
+>>15 byte >2 \b, line size 2^%d byte
+>>14 byte >2 \b, page size 2^%d byte
+>>13 byte &1
+>>>13 byte >1 \b, max fanout %d
+0 lequad =0xc693dac5ed5e47c2 Hash::SharedMem data file, little-endian
+>8 lequad <0x1000000
+>>8 byte >2 \b, line size 2^%d byte
+>>9 byte >2 \b, page size 2^%d byte
+>>10 byte &1
+>>>10 byte >1 \b, max fanout %d
diff --git a/contrib/libs/libmagic/magic/Magdir/pgf b/contrib/libs/libmagic/magic/Magdir/pgf
new file mode 100644
index 0000000000..8318ce1338
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pgf
@@ -0,0 +1,52 @@
+
+#------------------------------------------------------------------------------
+# $File: pgf,v 1.3 2021/02/23 00:51:10 christos Exp $
+# pgf: file(1) magic for Progressive Graphics File (PGF)
+#
+# <http://www.libpgf.org/uploads/media/PGF_Details_01.pdf>
+# 2013 by Philipp Hahn <pmhahn debian org>
+0 string PGF Progressive Graphics image data,
+!:mime image/x-pgf
+>3 string 2 version %s,
+>3 string 4 version %s,
+>3 string 5 version %s,
+>3 string 6 version %s,
+# PGFPreHeader
+#>>4 lelong x header size %d,
+# PGFHeader
+>>8 lelong x %d x
+>>12 lelong x %d,
+>>16 byte x %d levels,
+>>17 byte x compression level %d,
+>>18 byte x %d bpp,
+>>19 byte x %d channels,
+>>20 clear x
+>>20 byte 0 bitmap,
+>>20 byte 1 gray scale,
+>>20 byte 2 indexed color,
+>>20 byte 3 RGB color,
+>>20 byte 4 CMYK color,
+>>20 byte 5 HSL color,
+>>20 byte 6 HSB color,
+>>20 byte 7 multi-channel,
+>>20 byte 8 duo tone,
+>>20 byte 9 LAB color,
+>>20 byte 10 gray scale 16,
+>>20 byte 11 RGB color 48,
+>>20 byte 12 LAB color 48,
+>>20 byte 13 CMYK color 64,
+>>20 byte 14 deep multi-channel,
+>>20 byte 15 duo tone 16,
+>>20 byte 17 RGBA color,
+>>20 byte 18 gray scale 32,
+>>20 byte 19 RGB color 12,
+>>20 byte 20 RGB color 16,
+>>20 byte 255 unknown format,
+>>20 default x format
+>>>20 byte x \b %d,
+>>21 byte x %d bpc
+# PGFPostHeader
+# Level-Sizes
+#>>(4.l+4) lelong x level 0 size: %d
+#>>(4.l+8) lelong x level 1 size: %d
+#>>(4.l+12) lelong x level 2 size: %d
diff --git a/contrib/libs/libmagic/magic/Magdir/pgp b/contrib/libs/libmagic/magic/Magdir/pgp
new file mode 100644
index 0000000000..d81883868b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pgp
@@ -0,0 +1,581 @@
+
+#------------------------------------------------------------------------------
+# $File: pgp,v 1.25 2021/04/26 15:56:00 christos Exp $
+# pgp: file(1) magic for Pretty Good Privacy
+
+# Handling of binary PGP keys is in pgp-binary-keys.
+# see https://lists.gnupg.org/pipermail/gnupg-devel/1999-September/016052.html
+#
+0 beshort 0xa600 PGP encrypted data
+#!:mime application/pgp-encrypted
+#0 string -----BEGIN\040PGP text/PGP armored data
+!:mime text/PGP # encoding: armored data
+#>15 string PUBLIC\040KEY\040BLOCK- public key block
+#>15 string MESSAGE- message
+#>15 string SIGNED\040MESSAGE- signed message
+#>15 string PGP\040SIGNATURE- signature
+
+# Update: Joerg Jenderek
+# URL: http://en.wikipedia.org/wiki/Pretty_Good_Privacy
+# Reference: https://reposcope.com/mimetype/application/pgp-keys
+2 string ---BEGIN\040PGP\040PRIVATE\040KEY\040BLOCK- PGP private key block
+#!:mime text/PGP
+!:mime application/pgp-keys
+!:ext asc
+2 string ---BEGIN\040PGP\040PUBLIC\040KEY\040BLOCK- PGP public key block
+!:mime application/pgp-keys
+!:ext asc
+>10 search/100 \n\n
+>>&0 use pgp
+0 string -----BEGIN\040PGP\040MESSAGE- PGP message
+# https://reposcope.com/mimetype/application/pgp-encrypted
+#!:mime application/pgp
+!:mime application/pgp-encrypted
+!:ext asc
+#!:ext asc/pgp/gpg
+>10 search/100 \n\n
+>>&0 use pgp
+# Reference: https://www.gnupg.org/gph/en/manual/x135.html
+0 string -----BEGIN\040PGP\040SIGNED\040MESSAGE- PGP signed message
+#!:mime text/plain
+!:mime text/PGP
+#!:mime application/pgp
+!:ext asc
+0 string -----BEGIN\040PGP\040SIGNATURE- PGP signature
+# https://reposcope.com/mimetype/application/pgp-signature
+!:mime application/pgp-signature
+!:ext asc
+>10 search/100 \n\n
+>>&0 use pgp
+
+# Decode the type of the packet based on it's base64 encoding.
+# Idea from Mark Martinec
+# The specification is in RFC 4880, section 4.2 and 4.3:
+# https://tools.ietf.org/html/rfc4880#section-4.2
+
+0 name pgp
+>0 byte 0x67 Reserved (old)
+>0 byte 0x68 Public-Key Encrypted Session Key (old)
+>0 byte 0x69 Signature (old)
+>0 byte 0x6a Symmetric-Key Encrypted Session Key (old)
+>0 byte 0x6b One-Pass Signature (old)
+>0 byte 0x6c Secret-Key (old)
+>0 byte 0x6d Public-Key (old)
+>0 byte 0x6e Secret-Subkey (old)
+>0 byte 0x6f Compressed Data (old)
+>0 byte 0x70 Symmetrically Encrypted Data (old)
+>0 byte 0x71 Marker (old)
+>0 byte 0x72 Literal Data (old)
+>0 byte 0x73 Trust (old)
+>0 byte 0x74 User ID (old)
+>0 byte 0x75 Public-Subkey (old)
+>0 byte 0x76 Unused (old)
+>0 byte 0x77
+>>1 byte&0xc0 0x00 Reserved
+>>1 byte&0xc0 0x40 Public-Key Encrypted Session Key
+>>1 byte&0xc0 0x80 Signature
+>>1 byte&0xc0 0xc0 Symmetric-Key Encrypted Session Key
+>0 byte 0x78
+>>1 byte&0xc0 0x00 One-Pass Signature
+>>1 byte&0xc0 0x40 Secret-Key
+>>1 byte&0xc0 0x80 Public-Key
+>>1 byte&0xc0 0xc0 Secret-Subkey
+>0 byte 0x79
+>>1 byte&0xc0 0x00 Compressed Data
+>>1 byte&0xc0 0x40 Symmetrically Encrypted Data
+>>1 byte&0xc0 0x80 Marker
+>>1 byte&0xc0 0xc0 Literal Data
+>0 byte 0x7a
+>>1 byte&0xc0 0x00 Trust
+>>1 byte&0xc0 0x40 User ID
+>>1 byte&0xc0 0x80 Public-Subkey
+>>1 byte&0xc0 0xc0 Unused [z%x]
+>0 byte 0x30
+>>1 byte&0xc0 0x00 Unused [0%x]
+>>1 byte&0xc0 0x40 User Attribute
+>>1 byte&0xc0 0x80 Sym. Encrypted and Integrity Protected Data
+>>1 byte&0xc0 0xc0 Modification Detection Code
+
+# magic signatures to detect PGP crypto material (from stef)
+# detects and extracts metadata from:
+# - symmetric encrypted packet header
+# - RSA (e=65537) secret (sub-)keys
+
+# 1024b RSA encrypted data
+
+0 string \x84\x8c\x03 PGP RSA encrypted session key -
+>3 belong x keyid: %08X
+>7 belong x %08X
+>11 byte 0x01 RSA (Encrypt or Sign) 1024b
+>11 byte 0x02 RSA Encrypt-Only 1024b
+>12 string \x04\x00
+>12 string \x03\xff
+>12 string \x03\xfe
+>12 string \x03\xfd
+>12 string \x03\xfc
+>12 string \x03\xfb
+>12 string \x03\xfa
+>12 string \x03\xf9
+>142 byte 0xd2 .
+
+# 2048b RSA encrypted data
+
+0 string \x85\x01\x0c\x03 PGP RSA encrypted session key -
+>4 belong x keyid: %08X
+>8 belong x %08X
+>12 byte 0x01 RSA (Encrypt or Sign) 2048b
+>12 byte 0x02 RSA Encrypt-Only 2048b
+>13 string \x08\x00
+>13 string \x07\xff
+>13 string \x07\xfe
+>13 string \x07\xfd
+>13 string \x07\xfc
+>13 string \x07\xfb
+>13 string \x07\xfa
+>13 string \x07\xf9
+>271 byte 0xd2 .
+
+# 3072b RSA encrypted data
+
+0 string \x85\x01\x8c\x03 PGP RSA encrypted session key -
+>4 belong x keyid: %08X
+>8 belong x %08X
+>12 byte 0x01 RSA (Encrypt or Sign) 3072b
+>12 byte 0x02 RSA Encrypt-Only 3072b
+>13 string \x0c\x00
+>13 string \x0b\xff
+>13 string \x0b\xfe
+>13 string \x0b\xfd
+>13 string \x0b\xfc
+>13 string \x0b\xfb
+>13 string \x0b\xfa
+>13 string \x0b\xf9
+>399 byte 0xd2 .
+
+# 4096b RSA encrypted data
+
+0 string \x85\x02\x0c\x03 PGP RSA encrypted session key -
+>4 belong x keyid: %08X
+>8 belong x %08X
+>12 byte 0x01 RSA (Encrypt or Sign) 4096b
+>12 byte 0x02 RSA Encrypt-Only 4096b
+>13 string \x10\x00
+>13 string \x0f\xff
+>13 string \x0f\xfe
+>13 string \x0f\xfd
+>13 string \x0f\xfc
+>13 string \x0f\xfb
+>13 string \x0f\xfa
+>13 string \x0f\xf9
+>527 byte 0xd2 .
+
+# 8192b RSA encrypted data
+
+0 string \x85\x04\x0c\x03 PGP RSA encrypted session key -
+>4 belong x keyid: %08X
+>8 belong x %08X
+>12 byte 0x01 RSA (Encrypt or Sign) 8192b
+>12 byte 0x02 RSA Encrypt-Only 8192b
+>13 string \x20\x00
+>13 string \x1f\xff
+>13 string \x1f\xfe
+>13 string \x1f\xfd
+>13 string \x1f\xfc
+>13 string \x1f\xfb
+>13 string \x1f\xfa
+>13 string \x1f\xf9
+>1039 byte 0xd2 .
+
+# 1024b Elgamal encrypted data
+
+0 string \x85\x01\x0e\x03 PGP Elgamal encrypted session key -
+>4 belong x keyid: %08X
+>8 belong x %08X
+>12 byte 0x10 Elgamal Encrypt-Only 1024b.
+>13 string \x04\x00
+>13 string \x03\xff
+>13 string \x03\xfe
+>13 string \x03\xfd
+>13 string \x03\xfc
+>13 string \x03\xfb
+>13 string \x03\xfa
+>13 string \x03\xf9
+
+# 2048b Elgamal encrypted data
+
+0 string \x85\x02\x0e\x03 PGP Elgamal encrypted session key -
+>4 belong x keyid: %08X
+>8 belong x %08X
+>12 byte 0x10 Elgamal Encrypt-Only 2048b.
+>13 string \x08\x00
+>13 string \x07\xff
+>13 string \x07\xfe
+>13 string \x07\xfd
+>13 string \x07\xfc
+>13 string \x07\xfb
+>13 string \x07\xfa
+>13 string \x07\xf9
+
+# 3072b Elgamal encrypted data
+
+0 string \x85\x03\x0e\x03 PGP Elgamal encrypted session key -
+>4 belong x keyid: %08X
+>8 belong x %08X
+>12 byte 0x10 Elgamal Encrypt-Only 3072b.
+>13 string \x0c\x00
+>13 string \x0b\xff
+>13 string \x0b\xfe
+>13 string \x0b\xfd
+>13 string \x0b\xfc
+>13 string \x0b\xfb
+>13 string \x0b\xfa
+>13 string \x0b\xf9
+
+# crypto algo mapper
+
+0 name crypto
+>0 byte 0x00 Plaintext or unencrypted data
+>0 byte 0x01 IDEA
+>0 byte 0x02 TripleDES
+>0 byte 0x03 CAST5 (128 bit key)
+>0 byte 0x04 Blowfish (128 bit key, 16 rounds)
+>0 byte 0x07 AES with 128-bit key
+>0 byte 0x08 AES with 192-bit key
+>0 byte 0x09 AES with 256-bit key
+>0 byte 0x0a Twofish with 256-bit key
+
+# hash algo mapper
+
+0 name hash
+>0 byte 0x01 MD5
+>0 byte 0x02 SHA-1
+>0 byte 0x03 RIPE-MD/160
+>0 byte 0x08 SHA256
+>0 byte 0x09 SHA384
+>0 byte 0x0a SHA512
+>0 byte 0x0b SHA224
+
+# display public key algorithms as human readable text
+0 name key_algo
+>0 byte 0x01 RSA (Encrypt or Sign)
+# keep old look of version 5.28 without parentheses
+>0 byte 0x02 RSA Encrypt-Only
+>0 byte 0x03 RSA (Sign-Only)
+>0 byte 16 ElGamal (Encrypt-Only)
+>0 byte 17 DSA
+>0 byte 18 Elliptic Curve
+>0 byte 19 ECDSA
+>0 byte 20 ElGamal (Encrypt or Sign)
+>0 byte 21 Diffie-Hellman
+>0 default x
+>>0 ubyte <22 unknown (pub %d)
+# this should never happen
+>>0 ubyte >21 invalid (%d)
+
+# pgp symmetric encrypted data
+
+0 byte 0x8c PGP symmetric key encrypted data -
+>1 byte 0x0d
+>1 byte 0x0c
+>2 byte 0x04
+>3 use crypto
+>4 byte 0x01 salted -
+>>5 use hash
+>>14 byte 0xd2 .
+>>14 byte 0xc9 .
+>4 byte 0x03 salted & iterated -
+>>5 use hash
+>>15 byte 0xd2 .
+>>15 byte 0xc9 .
+
+# encrypted keymaterial needs s2k & can be checksummed/hashed
+
+0 name chkcrypto
+>0 use crypto
+>1 byte 0x00 Simple S2K
+>1 byte 0x01 Salted S2K
+>1 byte 0x03 Salted&Iterated S2K
+>2 use hash
+
+# all PGP keys start with this prolog
+# containing version, creation date, and purpose
+
+0 name keyprolog
+>0 byte 0x04
+>1 beldate x created on %s -
+>5 byte 0x01 RSA (Encrypt or Sign)
+>5 byte 0x02 RSA Encrypt-Only
+
+# end of secret keys known signature
+# contains e=65537 and the prolog to
+# the encrypted parameters
+
+0 name keyend
+>0 string \x00\x11\x01\x00\x01 e=65537
+>5 use crypto
+>5 byte 0xff checksummed
+>>6 use chkcrypto
+>5 byte 0xfe hashed
+>>6 use chkcrypto
+
+# PGP secret keys contain also the public parts
+# these vary by bitsize of the key
+
+0 name x1024
+>0 use keyprolog
+>6 string \x03\xfe
+>6 string \x03\xff
+>6 string \x04\x00
+>136 use keyend
+
+0 name x2048
+>0 use keyprolog
+>6 string \x80\x00
+>6 string \x07\xfe
+>6 string \x07\xff
+>264 use keyend
+
+0 name x3072
+>0 use keyprolog
+>6 string \x0b\xfe
+>6 string \x0b\xff
+>6 string \x0c\x00
+>392 use keyend
+
+0 name x4096
+>0 use keyprolog
+>6 string \x10\x00
+>6 string \x0f\xfe
+>6 string \x0f\xff
+>520 use keyend
+
+# \x00|\x1f[\xfe\xff]).{1024})'
+0 name x8192
+>0 use keyprolog
+>6 string \x20\x00
+>6 string \x1f\xfe
+>6 string \x1f\xff
+>1032 use keyend
+
+# depending on the size of the pkt
+# we branch into the proper key size
+# signatures defined as x{keysize}
+
+0 name pgpkey
+>0 string \x01\xd8 1024b
+>>2 use x1024
+>0 string \x01\xeb 1024b
+>>2 use x1024
+>0 string \x01\xfb 1024b
+>>2 use x1024
+>0 string \x01\xfd 1024b
+>>2 use x1024
+>0 string \x01\xf3 1024b
+>>2 use x1024
+>0 string \x01\xee 1024b
+>>2 use x1024
+>0 string \x01\xfe 1024b
+>>2 use x1024
+>0 string \x01\xf4 1024b
+>>2 use x1024
+>0 string \x02\x0d 1024b
+>>2 use x1024
+>0 string \x02\x03 1024b
+>>2 use x1024
+>0 string \x02\x05 1024b
+>>2 use x1024
+>0 string \x02\x15 1024b
+>>2 use x1024
+>0 string \x02\x00 1024b
+>>2 use x1024
+>0 string \x02\x10 1024b
+>>2 use x1024
+>0 string \x02\x04 1024b
+>>2 use x1024
+>0 string \x02\x06 1024b
+>>2 use x1024
+>0 string \x02\x16 1024b
+>>2 use x1024
+>0 string \x03\x98 2048b
+>>2 use x2048
+>0 string \x03\xab 2048b
+>>2 use x2048
+>0 string \x03\xbb 2048b
+>>2 use x2048
+>0 string \x03\xbd 2048b
+>>2 use x2048
+>0 string \x03\xcd 2048b
+>>2 use x2048
+>0 string \x03\xb3 2048b
+>>2 use x2048
+>0 string \x03\xc3 2048b
+>>2 use x2048
+>0 string \x03\xc5 2048b
+>>2 use x2048
+>0 string \x03\xd5 2048b
+>>2 use x2048
+>0 string \x03\xae 2048b
+>>2 use x2048
+>0 string \x03\xbe 2048b
+>>2 use x2048
+>0 string \x03\xc0 2048b
+>>2 use x2048
+>0 string \x03\xd0 2048b
+>>2 use x2048
+>0 string \x03\xb4 2048b
+>>2 use x2048
+>0 string \x03\xc4 2048b
+>>2 use x2048
+>0 string \x03\xc6 2048b
+>>2 use x2048
+>0 string \x03\xd6 2048b
+>>2 use x2048
+>0 string \x05X 3072b
+>>2 use x3072
+>0 string \x05k 3072b
+>>2 use x3072
+>0 string \x05{ 3072b
+>>2 use x3072
+>0 string \x05} 3072b
+>>2 use x3072
+>0 string \x05\x8d 3072b
+>>2 use x3072
+>0 string \x05s 3072b
+>>2 use x3072
+>0 string \x05\x83 3072b
+>>2 use x3072
+>0 string \x05\x85 3072b
+>>2 use x3072
+>0 string \x05\x95 3072b
+>>2 use x3072
+>0 string \x05n 3072b
+>>2 use x3072
+>0 string \x05\x7e 3072b
+>>2 use x3072
+>0 string \x05\x80 3072b
+>>2 use x3072
+>0 string \x05\x90 3072b
+>>2 use x3072
+>0 string \x05t 3072b
+>>2 use x3072
+>0 string \x05\x84 3072b
+>>2 use x3072
+>0 string \x05\x86 3072b
+>>2 use x3072
+>0 string \x05\x96 3072b
+>>2 use x3072
+>0 string \x07[ 4096b
+>>2 use x4096
+>0 string \x07\x18 4096b
+>>2 use x4096
+>0 string \x07+ 4096b
+>>2 use x4096
+>0 string \x07; 4096b
+>>2 use x4096
+>0 string \x07= 4096b
+>>2 use x4096
+>0 string \x07M 4096b
+>>2 use x4096
+>0 string \x073 4096b
+>>2 use x4096
+>0 string \x07C 4096b
+>>2 use x4096
+>0 string \x07E 4096b
+>>2 use x4096
+>0 string \x07U 4096b
+>>2 use x4096
+>0 string \x07. 4096b
+>>2 use x4096
+>0 string \x07> 4096b
+>>2 use x4096
+>0 string \x07@ 4096b
+>>2 use x4096
+>0 string \x07P 4096b
+>>2 use x4096
+>0 string \x074 4096b
+>>2 use x4096
+>0 string \x07D 4096b
+>>2 use x4096
+>0 string \x07F 4096b
+>>2 use x4096
+>0 string \x07V 4096b
+>>2 use x4096
+>0 string \x0e[ 8192b
+>>2 use x8192
+>0 string \x0e\x18 8192b
+>>2 use x8192
+>0 string \x0e+ 8192b
+>>2 use x8192
+>0 string \x0e; 8192b
+>>2 use x8192
+>0 string \x0e= 8192b
+>>2 use x8192
+>0 string \x0eM 8192b
+>>2 use x8192
+>0 string \x0e3 8192b
+>>2 use x8192
+>0 string \x0eC 8192b
+>>2 use x8192
+>0 string \x0eE 8192b
+>>2 use x8192
+>0 string \x0eU 8192b
+>>2 use x8192
+>0 string \x0e. 8192b
+>>2 use x8192
+>0 string \x0e> 8192b
+>>2 use x8192
+>0 string \x0e@ 8192b
+>>2 use x8192
+>0 string \x0eP 8192b
+>>2 use x8192
+>0 string \x0e4 8192b
+>>2 use x8192
+>0 string \x0eD 8192b
+>>2 use x8192
+>0 string \x0eF 8192b
+>>2 use x8192
+>0 string \x0eV 8192b
+>>2 use x8192
+
+# PGP RSA (e=65537) secret (sub-)key header
+
+0 byte 0x97 PGP Secret Sub-key -
+>1 use pgpkey
+0 byte 0x9d
+# Update: Joerg Jenderek
+# secret subkey packet (tag 7) with same structure as secret key packet (tag 5)
+# skip Fetus.Sys16 CALIBUS.MAIN OrbFix.Sys16.Ex by looking for positive len
+>1 ubeshort >0
+#>1 ubeshort x \b, body length %#x
+# next packet type often 88h,89h~(tag 2)~Signature Packet
+#>>(1.S+3) ubyte x \b, next packet type %#x
+# skip Dragon.SHR DEMO.INIT by looking for positive version
+>>3 ubyte >0
+# skip BUISSON.13 GUITAR1 by looking for low version number
+>>>3 ubyte <5 PGP Secret Sub-key
+# sub-key are normally part of secret key. So it does not occur as standalone file
+#!:ext bin
+# version 2,3~old 4~new . Comment following line for version 5.28 look
+>>>>3 ubyte x (v%d)
+>>>>3 ubyte x -
+# old versions 2 or 3 but no real example found
+>>>>3 ubyte <4
+# 2 byte for key bits in version 5.28 look
+>>>>>11 ubeshort x %db
+>>>>>4 beldate x created on %s -
+# old versions use 2 additional bytes after time stamp
+#>>>>>8 ubeshort x %#x
+# display key algorithm 1~RSA Encrypt|Sign - 21~Diffie-Hellman
+>>>>>10 use key_algo
+>>>>>(11.S/8) ubequad x
+# look after first key
+>>>>>>&5 use keyend
+# new version
+>>>>3 ubyte >3
+>>>>>9 ubeshort x %db
+>>>>>4 beldate x created on %s -
+# display key algorithm
+>>>>>8 use key_algo
+>>>>>(9.S/8) ubequad x
+# look after first key for something like s2k
+>>>>>>&3 use keyend
diff --git a/contrib/libs/libmagic/magic/Magdir/pgp-binary-keys b/contrib/libs/libmagic/magic/Magdir/pgp-binary-keys
new file mode 100644
index 0000000000..1ce76d907b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pgp-binary-keys
@@ -0,0 +1,388 @@
+
+#------------------------------------------------------------------------------
+# $File: pgp-binary-keys,v 1.2 2021/04/26 15:56:00 christos Exp $
+# pgp-binary-keys: This file handles pgp binary keys.
+#
+# An PGP certificate or message doesn't have a fixed header. Instead,
+# they are sequences of packets:
+#
+# https://tools.ietf.org/html/rfc4880#section-4.3
+#
+# whose order conforms to a grammar:
+#
+# https://tools.ietf.org/html/rfc4880#section-11
+#
+# Happily most packets have a few fields that are constrained, which
+# allow us to fingerprint them with relatively high certainty.
+#
+# A PGP packet is described by a single byte: the so-called CTB. The
+# high-bit is always set. If bit 6 is set, then it is a so-called
+# new-style CTB; if bit 6 is clear, then it is a so-called old-style
+# CTB. Old-style CTBs have only four bits of type information; bits
+# 1-0 are used to describe the length. New-style CTBs have 6 bits of
+# type information.
+#
+# Following the CTB is the packet's length in bytes. If we blindly
+# advance the file cursor by this amount past the end of the length
+# information we come to the next packet.
+#
+# Data Structures
+# ===============
+#
+# New Style CTB
+# -------------
+#
+# https://tools.ietf.org/html/rfc4880#section-4.2.2
+#
+# 76543210
+# ||\----/
+# || tag
+# |always 1
+# always 1
+#
+# Tag bits 7 and 6 set
+# 0 0xC0 -- Reserved - a packet tag MUST NOT have this value
+# 1 0xC1 -- Public-Key Encrypted Session Key Packet
+# 2 0xC2 -- Signature Packet
+# 3 0xC3 -- Symmetric-Key Encrypted Session Key Packet
+# 4 0xC4 -- One-Pass Signature Packet
+# 5 0xC5 -- Secret-Key Packet
+# 6 0xC6 -- Public-Key Packet
+# 7 0xC7 -- Secret-Subkey Packet
+# 8 0xC8 -- Compressed Data Packet
+# 9 0xC9 -- Symmetrically Encrypted Data Packet
+# 10 0xCA -- Marker Packet
+# 11 0xCB -- Literal Data Packet
+# 12 0xCC -- Trust Packet
+# 13 0xCD -- User ID Packet
+# 14 0xCE -- Public-Subkey Packet
+# 17 0xD1 -- User Attribute Packet
+# 18 0xD2 -- Sym. Encrypted and Integrity Protected Data Packet
+# 19 0xD3 -- Modification Detection Code Packet
+# 60 to 63 -- Private or Experimental Values
+#
+# The CTB is followed by the length header, which is densely encoded:
+#
+# if length[0] is:
+# 0..191: one byte length (length[0])
+# 192..223: two byte length ((length[0] - 192) * 256 + length[2] + 192
+# 224..254: four byte length (big endian interpretation of length[1..5])
+# 255: partial body encoding
+#
+# The partial body encoding is similar to HTTP's chunk encoding. It
+# is only allowed for container packets (SEIP, Compressed Data and
+# Literal).
+#
+# Old Style CTB
+# -------------
+#
+# https://tools.ietf.org/html/rfc4880#section-4.2.1
+#
+# CTB:
+#
+# 76543210
+# ||\--/\/
+# || | length encoding
+# || tag
+# |always 0
+# always 1
+#
+# Tag:
+#
+# Tag bit 7 set, bits 6, 1, 0 clear
+# 0 0x80 -- Reserved - a packet tag MUST NOT have this value
+# 1 0x84 -- Public-Key Encrypted Session Key Packet
+# 2 0x88 -- Signature Packet
+# 3 0x8C -- Symmetric-Key Encrypted Session Key Packet
+# 4 0x90 -- One-Pass Signature Packet
+# 5 0x94 -- Secret-Key Packet
+# 6 0x98 -- Public-Key Packet
+# 7 0x9C -- Secret-Subkey Packet
+# 8 0xA0 -- Compressed Data Packet
+# 9 0xA4 -- Symmetrically Encrypted Data Packet
+# 10 0xA8 -- Marker Packet
+# 11 0xAC -- Literal Data Packet
+# 12 0xB0 -- Trust Packet
+# 13 0xB4 -- User ID Packet
+# 14 0xB8 -- Public-Subkey Packet
+#
+# Length encoding:
+#
+# Value
+# 0 1 byte length (following byte is the length)
+# 1 2 byte length (following two bytes are the length)
+# 2 4 byte length (following four bytes are the length)
+# 3 indeterminate length: natural end of packet, e.g., EOF
+#
+# An indeterminate length is only allowed for container packets
+# (SEIP, Compressed Data and Literal).
+#
+# Certificates
+# ------------
+#
+# We check the first three packets to determine if a sequence of
+# OpenPGP packets is likely to be a certificate. The grammar allows
+# the following prefixes:
+#
+# [Primary Key] [SIG] (EOF or another certificate)
+# [Primary Key] [SIG] [User ID] [SIG]...
+# [Primary Key] [SIG] [User Attribute] [SIG]...
+# [Primary Key] [SIG] [Subkey] [SIG]...
+# [Primary Key] [User ID] [SIG]...
+# [Primary Key] [User Attribute] [SIG]...
+# [Primary Key] [Subkey] [SIG]...
+#
+# Any number of marker packets are also allowed between each packet,
+# but they are not normally used and we don't currently check for
+# them.
+#
+# The keys and subkeys may be public or private.
+#
+
+# Key packets and signature packets are versioned. There are two
+# packet versions that we need to worry about in practice: v3 and v4.
+# v4 packets were introduced in RFC 2440, which was published in 1998.
+# It also deprecated v3 packets. There are no actively used v3
+# certificates (GnuPG removed the code to support them in November
+# 2014). But there are v3 keys lying around and it is useful to
+# identify them. The next version of OpenPGP will introduce v5 keys.
+# The document has not yet been standardized so changes are still
+# possible. But, for our purposes, it appears that v5 data structures
+# will be identical to v4 data structures modulo the version number.
+#
+# https://tools.ietf.org/html/rfc2440
+# https://lists.gnupg.org/pipermail/gnupg-announce/2014q4/000358.html
+# https://www.ietf.org/id/draft-ietf-openpgp-rfc4880bis-09.html#name-key-material-packet
+
+
+
+
+# The first packet has to be a public key or a secret key.
+#
+# New-Style Public Key
+0 ubyte =0xC6 OpenPGP Public Key
+>&0 use primary_key_length_new
+# New-Style Secret Key
+0 ubyte =0xC5 OpenPGP Secret Key
+>&0 use primary_key_length_new
+# Old-Style Public Key
+0 ubyte&0xFC =0x98 OpenPGP Public Key
+>&-1 use primary_key_length_old
+# Old-Style Secret Key
+0 ubyte&0xFC =0x94 OpenPGP Secret Key
+>&-1 use primary_key_length_old
+
+# Parse the length, check the packet's body and finally advance to the
+# next packet.
+
+# There are 4 different new-style length encodings, but the partial
+# body encoding is only acceptable for the SEIP, Compressed Data, and
+# Literal packets, which isn't valid for any packets in a certificate
+# so we ignore it.
+0 name primary_key_length_new
+>&0 ubyte <192
+#>>&0 ubyte x (1 byte length encoding, %d bytes)
+>>&0 use pgp_binary_key_pk_check
+>>>&(&-1.B) use sig_or_component_1
+>&0 ubyte >191
+>>&-1 ubyte <225
+# offset = ((offset[0] - 192) << 8) + offset[1] + 192 (for the length header)
+# raw - (192 * 256 - 192)
+# = 48960
+#>>>&0 ubeshort x (2 byte length encoding, %d bytes)
+>>>&1 use pgp_binary_key_pk_check
+>>>>&(&-2.S-48960) use sig_or_component_1
+>&0 ubyte =255
+#>>&0 belong x (5 byte length encoding, %d bytes)
+>>&4 use pgp_binary_key_pk_check
+>>>&(&-4.L) use sig_or_component_1
+# Partial body encoding (only valid for container packets).
+# >&0 ubyte >224
+# >>&0 ubyte <255 partial body encoding
+
+# There are 4 different old-style length encodings, but the
+# indeterminate length encoding is only acceptable for the SEIP,
+# Compressed Data, and Literal packets, which isn't valid for any
+# packets in a certificate.
+0 name primary_key_length_old
+#>&0 ubyte x (ctb: %x)
+>&0 ubyte&0x3 =0
+#>>&0 ubyte x (1 byte length encoding, %d bytes)
+>>&1 use pgp_binary_key_pk_check
+>>>&(&-1.B) use sig_or_component_1
+>&0 ubyte&0x3 =1
+#>>&0 ubeshort x (2 byte length encoding, %d bytes)
+>>&2 use pgp_binary_key_pk_check
+>>>&(&-2.S) use sig_or_component_1
+>&0 ubyte&0x3 =2
+#>>&0 ubelong x (4 byte length encoding, %d bytes)
+>>&4 use pgp_binary_key_pk_check
+>>>&(&-4.L) use sig_or_component_1
+
+# Check the Key.
+#
+# https://tools.ietf.org/html/rfc4880#section-5.5.2
+0 name pgp_binary_key_pk_check
+# Valid versions are: 2, 3, 4. 5 is proposed in RFC 4880bis.
+# Anticipate a v6 / v7 format that like v5 is compatible with v4.
+# key format in a decade or so :D.
+>&0 ubyte >1
+>>&-1 ubyte <8
+>>>&-1 byte x Version %d
+# Check that keys were created after 1990.
+# (1990 - 1970) * 365.2524 * 24 * 60 * 60 = 631156147
+>>>&0 bedate >631156147 \b, Created %s
+>>>>&-5 ubyte >3
+>>>>>&4 use pgp_binary_key_algo
+>>>>&-5 ubyte <4
+>>>>>&6 use pgp_binary_key_algo
+
+# Print out the key's algorithm and the number of bits, if this is
+# relevant (ECC keys are a fixed size).
+0 name pgp_binary_key_algo
+>0 clear x
+>&0 ubyte =1 \b, RSA (Encrypt or Sign,
+>>&0 ubeshort x \b %d bits)
+>&0 ubyte =2 \b, RSA (Encrypt,
+>>&0 ubeshort x \b %d bits)
+>&0 ubyte =3 \b, RSA (Sign,
+>>&0 ubeshort x \b %d bits)
+>&0 ubyte =16 \b, El Gamal (Encrypt,
+>>&0 ubeshort x \b %d bits)
+>&0 ubyte =17 \b, DSA
+>>&0 ubeshort x \b (%d bits)
+>&0 ubyte =18 \b, ECDH
+>&0 ubyte =19 \b, ECDSA
+>&0 ubyte =20 \b, El Gamal (Encrypt or Sign,
+>>&0 ubeshort x \b %d bits)
+>&0 ubyte =22 \b, EdDSA
+>&0 default x
+>>&0 ubyte x \b, Unknown Algorithm (%#x)
+
+# Match all possible second packets.
+0 name sig_or_component_1
+#>0 ubyte x (ctb: %x)
+>&0 ubyte =0xC2
+>>0 ubyte x \b; Signature
+>>&0 use sig_or_component_1_length_new
+>&0 ubyte =0xCD
+>>0 ubyte x \b; User ID
+>>&0 use sig_or_component_1_length_new
+>&0 ubyte =0xCE
+>>0 ubyte x \b; Public Subkey
+>>&0 use sig_or_component_1_length_new
+>&0 ubyte =0xC7
+>>0 ubyte x \b; Secret Subkey
+>>&0 use sig_or_component_1_length_new
+>&0 ubyte =0xD1
+>>0 ubyte x \b; User Attribute
+>>&0 use sig_or_component_1_length_new
+>&0 ubyte&0xFC =0x88
+>>0 ubyte x \b; Signature
+>>&-1 use sig_or_component_1_length_old
+>&0 ubyte&0xFC =0xB4
+>>0 ubyte x \b; User ID
+>>&-1 use sig_or_component_1_length_old
+>&0 ubyte&0xFC =0xB8
+>>0 ubyte x \b; Public Subkey
+>>&-1 use sig_or_component_1_length_old
+>&0 ubyte&0xFC =0x9C
+>>0 ubyte x \b; Secret Subkey
+>>&-1 use sig_or_component_1_length_old
+
+# Copy of 'primary_key_length_new', but calls cert_packet_3.
+0 name sig_or_component_1_length_new
+>&0 ubyte <192
+#>>&0 ubyte x (1 byte new length encoding, %d bytes)
+>>&(&-1.B) use cert_packet_3
+>&0 ubyte >191
+>>&-1 ubyte <225
+# offset = ((offset[0] - 192) << 8) + offset[1] + 192 + 1 (for the length header)
+# raw - (192 * 256 - 192 - 1)
+# = 48959
+#>>>&-1 ubeshort x (2 byte new length encoding, %d bytes)
+>>>&(&-1.S-48959) use cert_packet_3
+>&0 ubyte =255
+#>>&0 belong x (5 byte new length encoding, %d bytes)
+>>&(&-4.L) use cert_packet_3
+# Partial body encoding (only valid for container packets).
+# >&0 ubyte >224
+# >>&0 ubyte <255 partial body encoding
+
+0 name sig_or_component_1_length_old
+#>&0 ubyte x (ctb: %x)
+>&0 ubyte&0x3 =0
+#>>&0 ubyte x (1 byte old length encoding, %d bytes)
+>>&(&0.B+1) use cert_packet_3
+>&0 ubyte&0x3 =1
+#>>&0 ubeshort x (2 byte old length encoding, %d bytes)
+>>&(&0.S+2) use cert_packet_3
+>&0 ubyte&0x3 =2
+#>>&0 ubelong x (4 byte old length encoding, %d bytes)
+>>&(&0.L+4) use cert_packet_3
+
+# Copy of above.
+0 name cert_packet_3
+#>0 ubyte x (ctb: %x)
+>&0 ubyte =0xC2
+>>0 ubyte x \b; Signature
+>>&0 use cert_packet_3_length_new
+>&0 ubyte =0xCD
+>>0 ubyte x \b; User ID
+>>&0 use cert_packet_3_length_new
+>&0 ubyte =0xCE
+>>0 ubyte x \b; Public Subkey
+>>&0 use cert_packet_3_length_new
+>&0 ubyte =0xC7
+>>0 ubyte x \b; Secret Subkey
+>>&0 use cert_packet_3_length_new
+>&0 ubyte =0xD1
+>>0 ubyte x \b; User Attribute
+>>&0 use cert_packet_3_length_new
+>&0 ubyte&0xFC =0x88
+>>0 ubyte x \b; Signature
+>>&-1 use cert_packet_3_length_old
+>&0 ubyte&0xFC =0xB4
+>>0 ubyte x \b; User ID
+>>&-1 use cert_packet_3_length_old
+>&0 ubyte&0xFC =0xB8
+>>0 ubyte x \b; Public Subkey
+>>&-1 use cert_packet_3_length_old
+>&0 ubyte&0xFC =0x9C
+>>0 ubyte x \b; Secret Subkey
+>>&-1 use cert_packet_3_length_old
+
+# Copy of above.
+0 name cert_packet_3_length_new
+>&0 ubyte <192
+#>>&0 ubyte x (1 byte new length encoding, %d bytes)
+>>&(&-1.B) use pgp_binary_keys_end
+>&0 ubyte >191
+>>&-1 ubyte <225
+# offset = ((offset[0] - 192) << 8) + offset[1] + 192 + 1 (for the length header)
+# raw - (192 * 256 - 192 - 1)
+# = 48959
+#>>>&-1 ubeshort x (2 byte new length encoding, %d bytes)
+>>>&(&-1.S-48959) use pgp_binary_keys_end
+>&0 ubyte =255
+#>>&0 belong x (5 byte new length encoding, %d bytes)
+>>&(&-4.L) use pgp_binary_keys_end
+
+0 name cert_packet_3_length_old
+#>&0 ubyte x (ctb: %x)
+>&0 ubyte&0x3 =0
+#>>&0 ubyte x (1 byte old length encoding, %d bytes)
+>>&(&0.B+1) use pgp_binary_keys_end
+>&0 ubyte&0x3 =1
+#>>&0 ubeshort x (2 byte old length encoding, %d bytes)
+>>&(&0.S+2) use pgp_binary_keys_end
+>&0 ubyte&0x3 =2
+#>>&0 ubelong x (4 byte old length encoding, %d bytes)
+>>&(&0.L+4) use pgp_binary_keys_end
+
+# We managed to parse the first three packets of the certificate. Declare
+# victory.
+0 name pgp_binary_keys_end
+>0 byte x \b; OpenPGP Certificate
+!:mime application/pgp-keys
+!:ext pgp/gpg/pkr/asd
diff --git a/contrib/libs/libmagic/magic/Magdir/pkgadd b/contrib/libs/libmagic/magic/Magdir/pkgadd
new file mode 100644
index 0000000000..7dfb28691d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pkgadd
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# $File: pkgadd,v 1.6 2009/09/19 16:28:11 christos Exp $
+# pkgadd: file(1) magic for SysV R4 PKG Datastreams
+#
+0 string #\ PaCkAgE\ DaTaStReAm pkg Datastream (SVR4)
+!:mime application/x-svr4-package
diff --git a/contrib/libs/libmagic/magic/Magdir/plan9 b/contrib/libs/libmagic/magic/Magdir/plan9
new file mode 100644
index 0000000000..db068479c2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/plan9
@@ -0,0 +1,25 @@
+
+#------------------------------------------------------------------------------
+# $File: plan9,v 1.6 2021/07/30 12:25:13 christos Exp $
+# plan9: file(1) magic for AT&T Bell Labs' Plan 9 executables and object files
+# From: "Stefan A. Haubenthal" <polluks@web.de>
+#
+0 belong 0x00000107 Plan 9 executable, Motorola 68k
+0 belong 0x00000197 Plan 9 executable, AT&T Hobbit
+0 belong 0x000001EB Plan 9 executable, Intel 386
+0 belong 0x00000247 Plan 9 executable, Intel 960
+0 belong 0x000002AB Plan 9 executable, SPARC
+0 belong 0x00000407 Plan 9 executable, MIPS R3000
+0 belong 0x0000048B Plan 9 executable, AT&T DSP 3210
+0 belong 0x00000517 Plan 9 executable, MIPS R4000 BE
+0 belong 0x000005AB Plan 9 executable, AMD 29000
+0 belong 0x00000647 Plan 9 executable, ARM 7-something
+0 belong 0x000006EB Plan 9 executable, PowerPC
+0 belong 0x00000797 Plan 9 executable, MIPS R4000 LE
+0 belong 0x0000084B Plan 9 executable, DEC Alpha
+
+0 belong 0x3A11013C Plan 9 object file, MIPS R3000
+0 belong 0x430D013C Plan 9 object file, AT&T Hobbit
+0 belong 0x4D013201 Plan 9 object file, Motorola 68k
+0 belong 0x7410013C Plan 9 object file, SPARC
+0 belong 0x7E004501 Plan 9 object file, Intel 386
diff --git a/contrib/libs/libmagic/magic/Magdir/playdate b/contrib/libs/libmagic/magic/Magdir/playdate
new file mode 100644
index 0000000000..77f8c68937
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/playdate
@@ -0,0 +1,57 @@
+
+#------------------------------------------------------------------------------
+# $File: playdate,v 1.1 2022/11/04 13:34:48 christos Exp $
+#
+# Various native file formats for the Playdate portable video game console.
+#
+# These are unofficially documented at
+# https://github.com/jaames/playdate-reverse-engineering
+#
+# The SDK is a source for many test files, and can be used to
+# create others. https://play.date/dev/
+
+
+# pdi: static image
+0 string Playdate\ IMG Playdate image data
+>12 belong&0x80 0x80 (compressed)
+>>20 lelong x %d x
+>>24 lelong x %d
+>12 belong&0x80 0x00 (uncompressed)
+>>16 leshort x %d x
+>>18 leshort x %d
+
+# pdt: multiple static images
+0 string Playdate\ IMT Playdate image data set
+>12 belong&0x80 0x80 (compressed)
+>>20 lelong x %d x
+>>24 lelong x %d,
+>>28 lelong x %d cells
+>12 belong&0x80 0x00 (uncompressed)
+>>20 lelong x tile grid %d x
+>>24 lelong x %d
+
+# pds: string tables
+0 string Playdate\ STR Playdate localization strings
+>12 belong&0x80 0x80 (compressed)
+>12 belong&0x80 0x00 (uncompressed)
+
+# pda: audio
+0 string Playdate\ AUD Playdate audio file
+>12 lelong&0xffffff x %d Hz,
+>15 byte 0 unsigned, 8-bit PCM, 1 channel
+>15 byte 1 unsigned, 8-bit PCM, 2 channel
+>15 byte 2 signed, 16-bit little-endian PCM, 1 channel
+>15 byte 3 signed, 16-bit little-endian PCM, 1 channel
+>15 byte 4 4-bit ADPCM, 1 channel
+>15 byte 5 4-bit ADPCM, 2 channel
+
+# pda: video
+0 string Playdate\ VID Playdate video file
+>24 leshort x %d x
+>26 leshort x %d,
+>16 leshort x %d frames,
+>20 lefloat x %.2f FPS
+
+# pdz: executable package
+# Not a lot we can do, as it's a stream of entries with no summary information.
+0 string Playdate\ PDZ Playdate executable package
diff --git a/contrib/libs/libmagic/magic/Magdir/plus5 b/contrib/libs/libmagic/magic/Magdir/plus5
new file mode 100644
index 0000000000..795cca1f11
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/plus5
@@ -0,0 +1,18 @@
+
+#------------------------------------------------------------------------------
+# $File: plus5,v 1.6 2009/09/19 16:28:11 christos Exp $
+# plus5: file(1) magic for Plus Five's UNIX MUMPS
+#
+# XXX - byte order? Paging Hokey....
+#
+0 short 0x259 mumps avl global
+>2 byte >0 (V%d)
+>6 byte >0 with %d byte name
+>7 byte >0 and %d byte data cells
+0 short 0x25a mumps blt global
+>2 byte >0 (V%d)
+>8 short >0 - %d byte blocks
+>15 byte 0x00 - P/D format
+>15 byte 0x01 - P/K/D format
+>15 byte 0x02 - K/D format
+>15 byte >0x02 - Bad Flags
diff --git a/contrib/libs/libmagic/magic/Magdir/pmem b/contrib/libs/libmagic/magic/Magdir/pmem
new file mode 100644
index 0000000000..c0ead7316b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pmem
@@ -0,0 +1,46 @@
+
+#------------------------------------------------------------------------------
+# $File: pmem,v 1.4 2021/04/26 15:56:00 christos Exp $
+# pmem: file(1) magic for Persistent Memory Development Kit pool files
+#
+0 string PMEM
+>4 string POOLSET Persistent Memory Poolset file
+>>11 search REPLICA with replica
+>4 regex LOG|BLK|OBJ Persistent Memory Pool file, type: %s,
+>>8 lelong >0 version: %#x,
+>>12 lelong x compat: %#x,
+>>16 lelong x incompat: %#x,
+>>20 lelong x ro_compat: %#x,
+
+
+>>120 leqldate x crtime: %s,
+>>128 lequad x alignment_desc: %#016llx,
+
+>>136 clear x
+>>136 byte 2 machine_class: 64-bit,
+>>136 default x machine_class: unknown
+>>>136 byte x (%#d),
+
+>>137 clear x
+>>137 byte 1 data: little-endian,
+>>137 byte 2 data: big-endian,
+>>137 default x data: unknown
+>>>137 byte x (%#d),
+
+>>138 byte !0 reserved[0]: %d,
+>>139 byte !0 reserved[1]: %d,
+>>140 byte !0 reserved[2]: %d,
+>>141 byte !0 reserved[3]: %d,
+
+>>142 clear x
+>>142 leshort 62 machine: x86_64
+>>142 leshort 183 machine: aarch64
+>>142 default x machine: unknown
+>>>142 leshort x (%#d)
+
+>4 string BLK
+>>4096 lelong x \b, blk.bsize: %d
+
+>4 string OBJ
+>>4096 string >0 \b, obj.layout: '%s'
+>>4096 string <0 \b, obj.layout: NULL
diff --git a/contrib/libs/libmagic/magic/Magdir/polyml b/contrib/libs/libmagic/magic/Magdir/polyml
new file mode 100644
index 0000000000..1cc01093e4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/polyml
@@ -0,0 +1,23 @@
+
+#------------------------------------------------------------------------------
+# $File: polyml,v 1.2 2019/04/19 00:42:27 christos Exp $
+# polyml: file(1) magic for PolyML
+#
+# PolyML
+# MPEG, FLI, DL originally from vax@ccwf.cc.utexas.edu (VaX#n8)
+# FLC, SGI, Apple originally from Daniel Quinlan (quinlan@yggdrasil.com)
+
+# [0]: https://www.polyml.org/
+# [1]: https://github.com/polyml/polyml/blob/master/\
+# libpolyml/savestate.cpp#L146-L147
+# [2]: https://github.com/polyml/polyml/blob/master/\
+# libpolyml/savestate.cpp#L1262-L1263
+
+# Type: Poly/ML saved data
+# From: Matthew Fernandez <matthew.fernandez@gmail.com>
+
+0 string POLYSAVE Poly/ML saved state
+>8 long x version %u
+
+0 string POLYMODU Poly/ML saved module
+>8 long x version %u
diff --git a/contrib/libs/libmagic/magic/Magdir/printer b/contrib/libs/libmagic/magic/Magdir/printer
new file mode 100644
index 0000000000..b45a2025ec
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/printer
@@ -0,0 +1,278 @@
+
+#------------------------------------------------------------------------------
+# $File: printer,v 1.34 2023/06/16 19:27:12 christos Exp $
+# printer: file(1) magic for printer-formatted files
+#
+
+# PostScript, updated by Daniel Quinlan (quinlan@yggdrasil.com)
+0 string %! PostScript document text
+!:mime application/postscript
+!:apple ASPSTEXT
+>2 string PS-Adobe- conforming
+>>11 string >\0 DSC level %.3s
+>>>15 string EPS \b, type %s
+>>>15 string Query \b, type %s
+>>>15 string ExitServer \b, type %s
+>>>15 search/1000 %%LanguageLevel:\040
+>>>>&0 string >\0 \b, Level %s
+# Some PCs have the annoying habit of adding a ^D as a document separator
+0 string \004%! PostScript document text
+!:mime application/postscript
+!:apple ASPSTEXT
+>3 string PS-Adobe- conforming
+>>12 string >\0 DSC level %.3s
+>>>16 string EPS \b, type %s
+>>>16 string Query \b, type %s
+>>>16 string ExitServer \b, type %s
+>>>16 search/1000 %%LanguageLevel:\040
+>>>>&0 string >\0 \b, Level %s
+0 string \033%-12345X%!PS PostScript document
+
+# DOS EPS Binary File Header
+# From: Ed Sznyter <ews@Black.Market.NET>
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Encapsulated_PostScript
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/eps-adobe.trid.xml
+# Note: called "Encapsulated PostScript binary" by TrID and
+# verified partly by ImageMagick `identify -verbose *` as EPT (Encapsulated PostScript with TIFF preview)
+0 belong 0xC5D0D3C6
+# skip DROID fmt-122-signature-id-174.eps fmt-123-signature-id-178.eps fmt-124-signature-id-180.eps
+# by looking for content after header
+# GRR: in version 5.44 unequal and not endian variant not working!
+>32 ulelong >0 DOS EPS Binary File
+!:mime image/x-eps
+# TODO: check that "long" is false on big endian machines
+# Postscript often (850/857) comes after header; so values like: 30 32 or 2788 10644 43350 71828
+>>4 long >0 at byte %d
+# 1 space char after length value to get phrase like "length 263893 PostScript document text"
+>>>8 long >0 length %d
+# PostScript document text handled by ./printer
+>>>>(4.l) indirect x
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/e/eps-wmf.trid.xml
+# Note: called "Encapsulated PostScript binary (with WMF preview)" by TrID
+# verified partly by XnView `nconvert -info *.EP?` as TIFF epsp
+>>>>12 long >0 at byte %d
+!:ext eps
+# GRR: in file version 5.44 calling indirect of ./msdos produce phrase like "length 452\012- Windows metafile"
+>>>>16 long >0 length %d
+# Windows metafile data handled by ./msdos
+>>>>>(12.l) indirect x
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/e/eps-tiff.trid.xml
+# Note: called "Encapsulated PostScript binary (with TIFF preview)" by TrID
+>>>>20 long >0 at byte %d
+# For the variant with the TIFF preview image sometimes the file extension ept is used
+!:ext eps/ept
+# GRR: in file version 5.44 calling indirect of ./images produce phrase like "length 43320\012- TIFF image data,"
+>>>>>24 long >0 length %d
+# TIFF image data handled by ./images
+>>>>>>(20.l) indirect x
+
+# Summary: Adobe's PostScript Printer Description File
+# Extension: .ppd
+# Reference: https://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf, Section 3.8
+# Submitted by: Yves Arrouye <arrouye@marin.fdn.fr>
+#
+0 string *PPD-Adobe:\x20 PPD file
+>&0 string x \b, version %s
+!:ext ppd
+!:mime application/vnd.cups-ppd
+
+# HP Printer Job Language
+0 string \033%-12345X@PJL HP Printer Job Language data
+# HP Printer Job Language
+# The header found on Win95 HP plot files is the "Silliest Thing possible"
+# (TM)
+# Every driver puts the language at some random position, with random case
+# (LANGUAGE and Language)
+# For example the LaserJet 5L driver puts the "PJL ENTER LANGUAGE" in line 10
+# From: Uwe Bonnes <bon@elektron.ikp.physik.th-darmstadt.de>
+#
+0 string \033%-12345X@PJL HP Printer Job Language data
+>&0 string >\0 %s
+>>&0 string >\0 %s
+>>>&0 string >\0 %s
+>>>>&0 string >\0 %s
+#>15 string \ ENTER\ LANGUAGE\ =
+#>31 string PostScript PostScript
+
+# From: Stefan Thurner <thurners@nicsys.de>
+0 string \033%-12345X@PJL
+>&0 search/10000 %! PJL encapsulated PostScript document text
+
+# Rick Richardson <rickrich@gmail.com>
+
+# For Fuji-Xerox Printers - HBPL stands for Host Based Printer Language
+# For Oki Data Printers - HIPERC
+# For Konica Minolta Printers - LAVAFLOW
+# For Samsung Printers - QPDL
+# For HP Printers - ZJS stands for Zenographics ZJStream
+0 string \033%-12345X@PJL HP Printer Job Language data
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=HBPL - HBPL
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=HIPERC - Oki Data HIPERC
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=LAVAFLOW - Konica Minolta LAVAFLOW
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=QPDL - Samsung QPDL
+>0 search/10000 @PJL\ ENTER\ LANGUAGE\ =\ QPDL - Samsung QPDL
+>0 search/10000 @PJL\ ENTER\ LANGUAGE=ZJS - HP ZJS
+# Summary: Hewlett-Packard printer firmware update
+# From: Joerg Jenderek
+# URL: https://support.hp.com/us-en/drivers/selfservice/hp-envy-6000e-all-in-one-printer-series/2100187505/model/2100187513
+# Note: firmware update tested with ENVY 6000 All-in-One Printer
+0 string @PJL\ ENTER\ LANGUAGE=FWUPDATE2 HP Printer firmware update
+#!:mime application/octet-stream
+#!:mime application/x-hp-firmware
+# https://ftp.hp.com/pub/softlib/software13/printers/en6000/2214/EN6000_2214B.exe
+# vasari_base_dist_pp1_001.2214B_nonassert_appsigned_lbi_rootfs_secure_signed.ful2
+!:ext ful2
+
+# HP Printer Control Language, Daniel Quinlan (quinlan@yggdrasil.com)
+0 string \033E\033 HP PCL printer data
+>3 string \&l0A - default page size
+>3 string \&l1A - US executive page size
+>3 string \&l2A - US letter page size
+>3 string \&l3A - US legal page size
+>3 string \&l26A - A4 page size
+>3 string \&l80A - Monarch envelope size
+>3 string \&l81A - No. 10 envelope size
+>3 string \&l90A - Intl. DL envelope size
+>3 string \&l91A - Intl. C5 envelope size
+>3 string \&l100A - Intl. B5 envelope size
+>3 string \&l-81A - No. 10 envelope size (landscape)
+>3 string \&l-90A - Intl. DL envelope size (landscape)
+
+# IMAGEN printer-ready files:
+0 string @document( Imagen printer
+# this only works if "language xxx" is first item in Imagen header.
+>10 string language\ impress (imPRESS data)
+>10 string language\ daisy (daisywheel text)
+>10 string language\ diablo (daisywheel text)
+>10 string language\ printer (line printer emulation)
+>10 string language\ tektronix (Tektronix 4014 emulation)
+# Add any other languages that your Imagen uses - remember
+# to keep the word `text' if the file is human-readable.
+# [GRR 950115: missing "postscript" or "ultrascript" (whatever it was called)]
+#
+# Now magic for IMAGEN font files...
+0 string Rast RST-format raster font data
+>45 string >0 face %s
+# From Jukka Ukkonen
+0 string \033[K\002\0\0\017\033(a\001\0\001\033(g Canon Bubble Jet BJC formatted data
+
+# From <mike@flyn.org>
+# These are the /etc/magic entries to decode data sent to an Epson printer.
+0 string \x1B\x40\x1B\x28\x52\x08\x00\x00REMOTE1P Epson Stylus Color 460 data
+
+
+#------------------------------------------------------------------------------
+# zenographics: file(1) magic for Zenographics ZjStream printer data
+# Rick Richardson <rickrich@gmail.com>
+0 string JZJZ
+>0x12 string ZZ Zenographics ZjStream printer data (big-endian)
+0 string ZJZJ
+>0x12 string ZZ Zenographics ZjStream printer data (little-endian)
+
+
+#------------------------------------------------------------------------------
+# Oak Technologies printer stream
+# Rick Richardson <rickrich@gmail.com>
+0 string OAK
+>0x07 byte 0
+>0x0b byte 0 Oak Technologies printer stream
+
+# This would otherwise be recognized as PostScript - nick@debian.org
+0 string %!VMF SunClock's Vector Map Format data
+
+#------------------------------------------------------------------------------
+# HP LaserJet 1000 series downloadable firmware file
+0 string \xbe\xefABCDEFGH HP LaserJet 1000 series downloadable firmware
+
+# From: Paolo <oopla@users.sf.net>
+# Epson ESC/Page, ESC/PageColor
+0 string \x1b\x01@EJL Epson ESC/Page language printer data
+
+# Summary: Hewlett-Packard Graphics Language
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/HP-GL
+# https://en.wikipedia.org/wiki/HPGL
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/h/hpg.trid.xml
+# Note: called "Hewlett-Packard Graphics Language" by TrID and
+# "Hewlett Packard Graphics Language" by DROID via PUID x-fmt/293 and
+# HPGL by XnView command `nconvert -info *`
+# initialize, start a plotting job
+0 string IN;
+>0 use hpgl
+# fill.plt
+0 string INPS
+>0 use hpgl
+# http://ftp.funet.fi/index/graphics/packages/hpgl2ps/hpgl2ps.tar.Z/hpgl2ps/test1.hpgl
+0 string DF;
+>0 use hpgl
+# http://ftp.funet.fi/index/graphics/packages/hpgl2ps/hpgl2ps.tar.Z/hpgl2ps/test3.hpgl
+# Select Pen n; If no pen number or 0, the controller performs an end of file command; n in range between -32767 and 32768 like: 6
+0 string SP
+# skip text Linux-syscall-note inside qemu sources starting with SPDX-Exception-Identifier: Linux-syscall-note
+# by checking for valid Pen number
+>2 regex \^([0-9]{1,5})
+#>2 regex \^([0-9]{1,5}) PEN_NUMBER=%s
+>>0 use hpgl
+# charsize.hp pages.hp set the scaling points (P1 and P2) to their default positions
+0 string IP0
+>0 use hpgl
+# ci.hp
+0 string CO\040
+>0 use hpgl
+# iw.hp 286x192.5_lh.hpg 286x192.5_lq.hpg
+0 string PS\040
+>0 use hpgl
+# thick.hp
+0 string PS9
+>0 use hpgl
+# ul.hp
+0 string PS4
+>0 use hpgl
+# la.hp
+0 string BP
+>0 use hpgl
+# miter.hp
+# Plot Absolute x,y{,x,y{...}}; x and y in range between -32767 and 32768 like: PA4000,3000;
+0 string PA
+# skip shell scripts test_msa_run_32r5eb.sh test_msa_run_32r5eb.sh with variable PATH_TO_QEMU
+# by checking for valid x coordinate
+>2 regex \^([-]{0,1}[0-9]{1,5})
+#>2 regex \^([-]{0,1}[0-9]{1,5}) COORDINATE=%s
+>>0 use hpgl
+# pw.hpg number of pens x
+0 string NP
+>0 use hpgl
+# win_1.hp
+#0 string \003INCA WHAT_IS_THAT
+#>0 use hpgl
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/h/hpgl2.trid.xml
+# Note: called "Hewlett-Packard Graphics Language 2" by TrID
+0 string \033%-1B Hewlett-Packard Graphics Language 2
+!:mime application/vnd.hp-HPGL
+# like: dt.plt
+!:ext plt
+#!:ext plt/gl2/hpg2/spl
+# remaining part after escsape sequnce
+>5 string x with "%-.10s"
+# display Hewlett-Packard Graphics Language vector graphic information
+0 name hpgl
+>0 string x Hewlett-Packard Graphics Language
+#!:mime vector/x-hpgl
+# https://www.iana.org/assignments/media-types/application/vnd.hp-HPGL
+!:mime application/vnd.hp-HPGL
+# no example with HPL suffix found
+!:ext hpgl/hpg/hp/plt
+# like: "IN;" "DF;IN;LT;PU1000,1000;PD2000,10" "SP6;DI0,1;SR0.70,1.90;SC0,800,"
+# "CO Concentric circles drawn with different linewidths;"
+>0 string x \b, starting with "%-.54s"
+# continue but not for 1 long line without CR or LF
+>>&0 ubyte <0x0E
+#>>&0 ubyte <0x0E TERMINATOR=%x
+# second line after 1 terminator character
+>>>&0 string >\r with "%-.10s"
+# next character again CR or LF
+>>>&0 ubyte <0x0E
+#>>>&0 ubyte <0x0E 2ND_CHARACTER=%x
+# second line after 2 terminator characters
+>>>>&0 string >\r with "%-.10s"
diff --git a/contrib/libs/libmagic/magic/Magdir/project b/contrib/libs/libmagic/magic/Magdir/project
new file mode 100644
index 0000000000..9180b57d63
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/project
@@ -0,0 +1,10 @@
+
+#------------------------------------------------------------------------------
+# $File: project,v 1.5 2017/03/17 21:35:28 christos Exp $
+# project: file(1) magic for Project management
+#
+# Magic strings for ftnchek project files. Alexander Mai
+0 string FTNCHEK_\ P project file for ftnchek
+>10 string 1 version 2.7
+>10 string 2 version 2.8 to 2.10
+>10 string 3 version 2.11 or later
diff --git a/contrib/libs/libmagic/magic/Magdir/psdbms b/contrib/libs/libmagic/magic/Magdir/psdbms
new file mode 100644
index 0000000000..3eec965731
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/psdbms
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: psdbms,v 1.8 2017/03/17 21:35:28 christos Exp $
+# psdbms: file(1) magic for psdatabase
+#
+# Update: Joerg Jenderek
+# GRR: line below too general as it catches also some Panorama database *.pan ,
+# AppleWorks word processor
+0 belong&0xff00ffff 0x56000000
+# assume version starts with digit
+>1 regex/s =^[0-9] ps database
+>>1 string >\0 version %s
+# kernel name
+>>4 string >\0 from kernel %s
diff --git a/contrib/libs/libmagic/magic/Magdir/psl b/contrib/libs/libmagic/magic/Magdir/psl
new file mode 100644
index 0000000000..0296540ce5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/psl
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: psl,v 1.3 2019/04/19 00:42:27 christos Exp $
+# psl: file(1) magic for Public Suffix List representations
+# From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+# URL: https://publicsuffix.org
+# see also: https://thread.gmane.org/gmane.network.dns.libpsl.bugs/162/focus=166
+
+0 search/512 \n\n//\ ===BEGIN\ ICANN\ DOMAINS===\n\n Public Suffix List data
+
+0 string .DAFSA@PSL_
+>15 string \n Public Suffix List data (optimized)
+>>11 byte >0x2f
+>>>11 byte <0x3a (Version %c)
diff --git a/contrib/libs/libmagic/magic/Magdir/pulsar b/contrib/libs/libmagic/magic/Magdir/pulsar
new file mode 100644
index 0000000000..7cb6f18bda
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pulsar
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: pulsar,v 1.5 2009/09/19 16:28:12 christos Exp $
+# pulsar: file(1) magic for Pulsar POP3 daemon binary files
+#
+# http://pulsar.sourceforge.net
+# mailto:rok.papez@lugos.si
+#
+
+0 belong 0x1ee7f11e Pulsar POP3 daemon mailbox cache file.
+>4 ubelong x Version: %d.
+>8 ubelong x \b%d
+
diff --git a/contrib/libs/libmagic/magic/Magdir/puzzle b/contrib/libs/libmagic/magic/Magdir/puzzle
new file mode 100644
index 0000000000..ac983f32b8
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/puzzle
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# $File: puzzle,v 1.2 2021/10/07 15:40:40 christos Exp $
+# wsdl: Magic for various puzzles
+
+# PUZ crossword puzzles from Alan De Smet
+# Test files can be found at
+# https://theworld.com/~wij/puzzles/wij-themed.html or using the
+# "Universal" or "WS Journal" links on the right side of
+# https://www.cruciverb.com/ .
+
+2 string ACROSS&DOWN PUZ crossword puzzle
+>0x2c byte x %d x
+>0x2d byte x %d,
+>0x2e leshort x %d clues,
+>0x1e leshort 0x0000 plain text solution
+>0x1e leshort !0x0000 scrambled solution
diff --git a/contrib/libs/libmagic/magic/Magdir/pwsafe b/contrib/libs/libmagic/magic/Magdir/pwsafe
new file mode 100644
index 0000000000..549093f143
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pwsafe
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: pwsafe,v 1.2 2019/04/19 00:42:27 christos Exp $
+# pwsafe: file(1) magic for passwordsafe file
+#
+# Password Safe
+# http://passwordsafe.sourceforge.net/
+# file format specs
+# https://passwordsafe.svn.sourceforge.net/viewvc/passwordsafe/trunk/pwsafe/pwsafe/docs/formatV3.txt
+# V2 https://passwordsafe.svn.sourceforge.net/viewvc/passwordsafe/trunk/pwsafe/pwsafe/docs/formatV2.txt
+# V1 https://passwordsafe.svn.sourceforge.net/viewvc/passwordsafe/trunk/pwsafe/pwsafe/docs/notes.txt
+# V2 and V1 have no easy identifier that I can find
+# .psafe3
+0 string PWS3 Password Safe V3 database
diff --git a/contrib/libs/libmagic/magic/Magdir/pyramid b/contrib/libs/libmagic/magic/Magdir/pyramid
new file mode 100644
index 0000000000..ee47c80767
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/pyramid
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: pyramid,v 1.7 2009/09/19 16:28:12 christos Exp $
+# pyramid: file(1) magic for Pyramids
+#
+# XXX - byte order?
+#
+0 long 0x50900107 Pyramid 90x family executable
+0 long 0x50900108 Pyramid 90x family pure executable
+>16 long >0 not stripped
+0 long 0x5090010b Pyramid 90x family demand paged pure executable
+>16 long >0 not stripped
diff --git a/contrib/libs/libmagic/magic/Magdir/python b/contrib/libs/libmagic/magic/Magdir/python
new file mode 100644
index 0000000000..00d90d1238
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/python
@@ -0,0 +1,305 @@
+
+#------------------------------------------------------------------------------
+# $File: python,v 1.45 2022/07/24 23:59:37 christos Exp $
+# python: file(1) magic for python
+#
+# Outlook puts """ too for urgent messages
+# From: David Necas <yeti@physics.muni.cz>
+# often the module starts with a multiline string
+0 string/t """ Python script text executable
+# MAGIC as specified in Python/import.c (1.0 to 3.7)
+# and in Lib/importlib/_bootstrap_external.py (3.5+)
+# two bytes of magic followed by "\r\n" in little endian order
+0 belong 0x02099900 python 1.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x03099900 python 1.1/1.2 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x892e0d0a python 1.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x04170d0a python 1.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x994e0d0a python 1.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xfcc40d0a python 1.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xfdc40d0a python 1.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x87c60d0a python 2.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x88c60d0a python 2.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2aeb0d0a python 2.1 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2beb0d0a python 2.1 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2ded0d0a python 2.2 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2eed0d0a python 2.2 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x3bf20d0a python 2.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x3cf20d0a python 2.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x45f20d0a python 2.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x59f20d0a python 2.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x63f20d0a python 2.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x6df20d0a python 2.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x6ef20d0a python 2.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x77f20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x81f20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x8bf20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x8cf20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x95f20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x9ff20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xa9f20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xb3f20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xb4f20d0a python 2.5 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xc7f20d0a python 2.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xd1f20d0a python 2.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xd2f20d0a python 2.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xdbf20d0a python 2.7 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xe5f20d0a python 2.7 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xeff20d0a python 2.7 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xf9f20d0a python 2.7 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x03f30d0a python 2.7 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x04f30d0a python 2.7 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x0af30d0a PyPy2.7 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xb80b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xc20b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xcc0b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xd60b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xe00b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xea0b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xf40b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xf50b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xff0b0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x090c0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x130c0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x1d0c0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x1f0c0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x270c0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x3b0c0d0a python 3.0 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x450c0d0a python 3.1 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x4f0c0d0a python 3.1 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x580c0d0a python 3.2 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x620c0d0a python 3.2 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x6c0c0d0a python 3.2 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x760c0d0a python 3.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x800c0d0a python 3.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x8a0c0d0a python 3.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x940c0d0a python 3.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x9e0c0d0a python 3.3 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xb20c0d0a python 3.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xbc0c0d0a python 3.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xc60c0d0a python 3.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xd00c0d0a python 3.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xda0c0d0a python 3.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xe40c0d0a python 3.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xee0c0d0a python 3.4 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0xf80c0d0a python 3.5.1- byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x020d0d0a python 3.5.1- byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x0c0d0d0a python 3.5.1- byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x160d0d0a python 3.5.1- byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x170d0d0a python 3.5.2+ byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x200d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x210d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2a0d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2b0d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2c0d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2d0d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x2f0d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x300d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x310d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x320d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x330d0d0a python 3.6 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x3e0d0d0a python 3.7 byte-compiled
+!:mime application/x-bytecode.python
+0 belong 0x3f0d0d0a python 3.7 byte-compiled
+!:mime application/x-bytecode.python
+
+# magic 3392+ implements PEP 552: Deterministic pycs
+0 name pyc-pep552
+# the flag field determines how .pyc validity is checked
+>4 ulelong&1 0 timestamp-based,
+>>8 uledate x .py timestamp: %s UTC,
+>>12 ulelong x .py size: %d bytes
+>4 ulelong&1 !0 hash-based, check-source flag
+>>4 ulelong&2 0 unset,
+>>4 ulelong&2 !0 set,
+>>8 ulequad x hash: 0x%llx
+
+# uleshort magic followed by \x0d\0xa
+2 string \x0d\x0a
+# extra check: only two bits of flag field are currently used
+>4 ulelong <0x4
+# \x0d as part of magic should suffice till Python 3.14 (magic 3600)
+>>1 ubyte 0x0d Byte-compiled Python module for
+!:mime application/x-bytecode.python
+# now look at the magic number to determine the version
+>>>0 uleshort <3400 CPython 3.7,
+>>>0 default x
+>>>>0 uleshort <3420 CPython 3.8,
+>>>>0 default x
+>>>>>0 uleshort <3430 CPython 3.9,
+>>>>>0 default x
+>>>>>>0 uleshort <3450 CPython 3.10,
+>>>>>>0 default x
+>>>>>>>0 uleshort <3500 CPython 3.11,
+>>>>>>>0 default x CPython 3.12 or newer,
+>>>0 use pyc-pep552
+>>0 uleshort 240 Byte-compiled Python module for PyPy3.7,
+!:mime application/x-bytecode.python
+>>>0 use pyc-pep552
+>>0 uleshort 256 Byte-compiled Python module for PyPy3.8,
+!:mime application/x-bytecode.python
+>>>0 use pyc-pep552
+>>0 uleshort 336 Byte-compiled Python module for PyPy3.9,
+!:mime application/x-bytecode.python
+>>>0 use pyc-pep552
+
+0 search/1/w #!\040/usr/bin/python Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+0 search/1/w #!\040/usr/local/bin/python Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+0 search/10/w #!\040/usr/bin/env\040python Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+
+
+# from module.submodule import func1, func2
+0 search/8192 import
+>0 regex \^from[\040\t]+([A-Za-z0-9_]|\\.)+[\040\t]+import.*$ Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+
+# def __init__ (self, ...):
+0 search/4096 def\ __init__
+>&0 search/64 self Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+
+# if __name__ == "__main__":
+0 search/4096 if\ __name__
+>&0 search/64 '__main__' Python script text executable
+>&0 search/64 "__main__" Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+
+# import module [as abrev]
+0 search/8192 import
+>0 regex \^import\ [_[:alpha:]]+\ as\ [[:alpha:]][[:space:]]*$ Python script text executable
+!:mime text/x-script.python
+
+# comments
+#0 search/4096 '''
+#>&0 regex .*'''$ Python script text executable
+#!:mime text/x-script.python
+
+#0 search/4096 """
+#>&0 regex .*"""$ Python script text executable
+#!:mime text/x-script.python
+
+# try:
+# except: or finally:
+# block
+0 search/4096 try:
+>&0 regex \^[[:space:]]*except.*:$ Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+>&0 search/4096 finally: Python script text executable
+!:mime text/x-script.python
+
+# class name[(base classes,)]: [pass]
+0 search/8192 class
+>0 regex \^class\ [_[:alpha:]]+(\\(.*\\))?(\ )*:([\ \t]+pass)?$ Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+
+# def name(*args, **kwargs):
+0 search/8192 def\
+>0 regex \^[[:space:]]{0,50}def\ {1,50}[_a-zA-Z]{1,100}
+>>&0 regex \\(([[:alpha:]*_,\ ]){0,255}\\):$ Python script text executable
+!:strength + 15
+!:mime text/x-script.python
+
+# https://numpy.org/devdocs/reference/generated/numpy.lib.format.html
+0 string \223NUMPY NumPy data file
+!:mime application/x-numpy-data
+>6 byte x \b, version %d
+>7 byte x \b.%d
+#>8 leshort x \b, header length=%d
+>10 string x \b, description %s
diff --git a/contrib/libs/libmagic/magic/Magdir/qt b/contrib/libs/libmagic/magic/Magdir/qt
new file mode 100644
index 0000000000..68085f2892
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/qt
@@ -0,0 +1,30 @@
+
+#------------------------------------------------------------------------------
+# $File: qt,v 1.4 2022/11/11 14:50:23 christos Exp $
+# qt: file(1) magic for Qt
+
+# https://doc.qt.io/qt-5/resources.html
+0 string \<!DOCTYPE\040RCC\> Qt Resource Collection file
+
+# https://qt.gitorious.org/qt/qtbase/source/\
+# 5367fa356233da4c0f28172a8f817791525f5457:\
+# src/tools/rcc/rcc.cpp#L840
+0 string qres\0\0 Qt Binary Resource file
+0 search/1024 The\040Resource\040Compiler\040for\040Qt Qt C-code resource file
+
+# https://qt.gitorious.org/qt/qtbase/source/\
+# 5367fa356233da4c0f28172a8f817791525f5457:\
+# src/corelib/kernel/qtranslator.cpp#L62
+0 string \x3c\xb8\x64\x18\xca\xef\x9c\x95
+>8 string \xcd\x21\x1c\xbf\x60\xa1\xbd\xdd Qt Translation file
+
+
+# Qt V4 Javascript engine compiled unit
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/qt/qtdeclarative/blob/v6.4.0/src/qml/common/qv4compileddata_p.h
+0 string qv4cdata QV4 compiled unit
+!:ext qmlc
+>8 ulelong x \b, version %d
+>12 byte x \b, Qt %d
+>13 byte x \b.%d
+>14 byte x \b.%d
diff --git a/contrib/libs/libmagic/magic/Magdir/revision b/contrib/libs/libmagic/magic/Magdir/revision
new file mode 100644
index 0000000000..824220a3d2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/revision
@@ -0,0 +1,66 @@
+
+#------------------------------------------------------------------------------
+# $File: revision,v 1.11 2019/04/19 00:42:27 christos Exp $
+# file(1) magic for revision control files
+# From Hendrik Scholz <hendrik@scholz.net>
+0 string/t /1\ :pserver: cvs password text file
+
+# Conary changesets
+# From: Jonathan Smith <smithj@rpath.com>
+0 belong 0xea3f81bb Conary changeset data
+
+# Type: Git bundles (git-bundle)
+# From: Josh Triplett <josh@freedesktop.org>
+0 string #\ v2\ git\ bundle\n Git bundle
+
+# Type: Git pack
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Git
+# reference: https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt
+# The actual magic is 'PACK', but that clashes with Doom/Quake packs. However,
+# those have a little-endian offset immediately following the magic 'PACK',
+# the first byte of which is never 0, while the first byte of the Git pack
+# version, since it's a tiny number stored in big-endian format, is always 0.
+0 string PACK
+# GRR: line above is too general as it matches also PackDir archive ./acorn
+# test for major version. Git 2017 accepts version number 2 or 3
+>4 ubelong <9
+# Acorn PackDir with method 0 compression has root like ADFS::HardDisc4.$.AsylumSrc
+# or SystemDevice::foobar
+>>9 search/13 ::
+# but in git binary
+>>9 default x Git pack
+!:mime application/x-git
+!:ext pack
+# 4 GB limit implies unsigned integer
+>>>4 ubelong x \b, version %u
+>>>8 ubelong x \b, %u objects
+
+# Type: Git pack index
+# From: Adam Buchbinder <adam.buchbinder@gmail.com>
+0 string \377tOc Git pack index
+>4 belong =2 \b, version 2
+
+# Type: Git index file
+# From: Frederic Briare <fbriere@fbriere.net>
+0 string DIRC Git index
+>4 belong >0 \b, version %d
+>>8 belong >0 \b, %d entries
+
+# Type: Mercurial bundles
+# From: Seo Sanghyeon <tinuviel@sparcs.kaist.ac.kr>
+0 string HG10 Mercurial bundle,
+>4 string UN uncompressed
+>4 string BZ bzip2 compressed
+
+# Type: Subversion (SVN) dumps
+# From: Uwe Zeisberger <zeisberg@informatik.uni-freiburg.de>
+0 string SVN-fs-dump-format-version: Subversion dumpfile
+>28 string >\0 (version: %s)
+
+# Type: Bazaar revision bundles and merge requests
+# URL: https://www.bazaar-vcs.org/
+# From: Jelmer Vernooij <jelmer@samba.org>
+0 string #\ Bazaar\ revision\ bundle\ v Bazaar Bundle
+0 string #\ Bazaar\ merge\ directive\ format Bazaar merge directive
diff --git a/contrib/libs/libmagic/magic/Magdir/riff b/contrib/libs/libmagic/magic/Magdir/riff
new file mode 100644
index 0000000000..422a0d7941
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/riff
@@ -0,0 +1,841 @@
+
+#------------------------------------------------------------------------------
+# $File: riff,v 1.45 2022/07/24 23:47:49 christos Exp $
+# riff: file(1) magic for RIFF format
+# See
+#
+# https://www.seanet.com/users/matts/riffmci/riffmci.htm
+# http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf
+# https://www.iana.org/assignments/wave-avi-codec-registry/wave-avi-codec-registry.xml
+#
+
+# audio format tag. Assume limits: max 1024 bit, 128 channels, 1 MHz
+0 name riff-wave
+>0 leshort 0x01 \b, Microsoft PCM
+>>14 leshort >0
+>>>14 leshort <1024 \b, %d bit
+>0 leshort 0x02 \b, Microsoft ADPCM
+>0 leshort 0x03 \b, IEEE Float
+>0 leshort 0x04 \b, Compaq VSELP
+>0 leshort 0x05 \b, IBM CVSD
+>0 leshort 0x06 \b, ITU G.711 A-law
+>0 leshort 0x07 \b, ITU G.711 mu-law
+>0 leshort 0x08 \b, Microsoft DTS
+>0 leshort 0x10 \b, OKI ADPCM
+>0 leshort 0x11 \b, IMA ADPCM
+>0 leshort 0x12 \b, MediaSpace ADPCM
+>0 leshort 0x13 \b, Sierra ADPCM
+>0 leshort 0x14 \b, ITU G.723 ADPCM (Yamaha)
+>0 leshort 0x15 \b, DSP Solutions DIGISTD
+>0 leshort 0x16 \b, DSP Solutions DIGIFIX
+>0 leshort 0x17 \b, Dialogic OKI ADPCM
+>0 leshort 0x18 \b, MediaVision ADPCM
+>0 leshort 0x19 \b, HP CU
+>0 leshort 0x20 \b, Yamaha ADPCM
+>0 leshort 0x21 \b, Speech Compression SONARC
+>0 leshort 0x22 \b, DSP Group True Speech
+>0 leshort 0x23 \b, Echo Speech EchoSC1
+>0 leshort 0x24 \b, AudioFile AF36
+>0 leshort 0x25 \b, APTX
+>0 leshort 0x26 \b, AudioFile AF10
+>0 leshort 0x27 \b, Prosody 1612
+>0 leshort 0x28 \b, LRC
+>0 leshort 0x30 \b, Dolby AC2
+>0 leshort 0x31 \b, GSM 6.10
+>0 leshort 0x32 \b, MSN Audio
+>0 leshort 0x33 \b, Antex ADPCME
+>0 leshort 0x34 \b, Control Res VQLPC
+>0 leshort 0x35 \b, Digireal
+>0 leshort 0x36 \b, DigiADPCM
+>0 leshort 0x37 \b, Control Res CR10
+>0 leshort 0x38 \b, NMS VBXADPCM
+>0 leshort 0x39 \b, Roland RDAC
+>0 leshort 0x3A \b, Echo Speech EchoSC3
+>0 leshort 0x3B \b, Rockwell ADPCM
+>0 leshort 0x3C \b, Rockwell Digitalk
+>0 leshort 0x3D \b, Xebec
+>0 leshort 0x40 \b, ITU G.721 ADPCM
+>0 leshort 0x41 \b, ITU G.728 CELP
+>0 leshort 0x42 \b, MSG723
+>0 leshort 0x50 \b, MPEG
+>0 leshort 0x52 \b, RT24
+>0 leshort 0x53 \b, PAC
+>0 leshort 0x55 \b, MPEG Layer 3
+>0 leshort 0x59 \b, Lucent G.723
+>0 leshort 0x60 \b, Cirrus
+>0 leshort 0x61 \b, ESPCM
+>0 leshort 0x62 \b, Voxware
+>0 leshort 0x63 \b, Canopus Atrac
+>0 leshort 0x64 \b, ITU G.726 ADPCM
+>0 leshort 0x65 \b, ITU G.722 ADPCM
+>0 leshort 0x66 \b, DSAT
+>0 leshort 0x67 \b, DSAT Display
+>0 leshort 0x69 \b, Voxware Byte Aligned
+>0 leshort 0x70 \b, Voxware AC8
+>0 leshort 0x71 \b, Voxware AC10
+>0 leshort 0x72 \b, Voxware AC16
+>0 leshort 0x73 \b, Voxware AC20
+>0 leshort 0x74 \b, Voxware MetaVoice
+>0 leshort 0x75 \b, Voxware MetaSound
+>0 leshort 0x76 \b, Voxware RT29HW
+>0 leshort 0x77 \b, Voxware VR12
+>0 leshort 0x78 \b, Voxware VR18
+>0 leshort 0x79 \b, Voxware TQ40
+>0 leshort 0x80 \b, Softsound
+>0 leshort 0x81 \b, Voxware TQ60
+>0 leshort 0x82 \b, MSRT24
+>0 leshort 0x83 \b, ITU G.729A
+>0 leshort 0x84 \b, MVI MV12
+>0 leshort 0x85 \b, DF G.726
+>0 leshort 0x86 \b, DF GSM610
+>0 leshort 0x88 \b, ISIAudio
+>0 leshort 0x89 \b, Onlive
+>0 leshort 0x91 \b, SBC24
+>0 leshort 0x92 \b, Dolby AC3 S/PDIF
+>0 leshort 0x97 \b, ZyXEL ADPCM
+>0 leshort 0x98 \b, Philips LPCBB
+>0 leshort 0x99 \b, Packed
+>0 leshort 0x100 \b, Rhetorex ADPCM
+>0 leshort 0x101 \b, BeCubed Software IRAT
+>0 leshort 0x111 \b, Vivo G.723
+>0 leshort 0x112 \b, Vivo Siren
+>0 leshort 0x123 \b, Digital G.723
+>0 leshort 0x200 \b, Creative ADPCM
+>0 leshort 0x202 \b, Creative FastSpeech8
+>0 leshort 0x203 \b, Creative FastSpeech10
+>0 leshort 0x220 \b, Quarterdeck
+>0 leshort 0x300 \b, FM Towns Snd
+>0 leshort 0x400 \b, BTV Digital
+>0 leshort 0x680 \b, VME VMPCM
+>0 leshort 0x1000 \b, OLIGSM
+>0 leshort 0x1001 \b, OLIADPCM
+>0 leshort 0x1002 \b, OLICELP
+>0 leshort 0x1003 \b, OLISBC
+>0 leshort 0x1004 \b, OLIOPR
+>0 leshort 0x1100 \b, LH Codec
+>0 leshort 0x1400 \b, Norris
+>0 leshort 0x1401 \b, ISIAudio
+>0 leshort 0x1500 \b, Soundspace Music Compression
+>0 leshort 0x2000 \b, AC3 DVM
+>0 leshort 0x2001 \b, DTS
+>2 leshort =1 \b, mono
+>2 leshort =2 \b, stereo
+>2 leshort >2
+>>2 leshort <128 \b, %d channels
+>4 lelong >0
+>>4 lelong <1000000 %d Hz
+
+# try to find "fmt "
+0 name riff-walk
+>0 string fmt\x20
+>>4 lelong >15
+>>>8 use riff-wave
+>0 string LIST
+>>&(4.l+4) use riff-walk
+>0 string DISP
+>>&(4.l+4) use riff-walk
+>0 string bext
+>>&(4.l+4) use riff-walk
+>0 string Fake
+>>&(4.l+4) use riff-walk
+>0 string fact
+>>&(4.l+4) use riff-walk
+>0 string VP8
+>>11 byte 0x9d
+>>>12 byte 0x01
+>>>>13 byte 0x2a \b, VP8 encoding
+>>>>>14 leshort&0x3fff x \b, %d
+>>>>>16 leshort&0x3fff x \bx%d, Scaling:
+>>>>>14 leshort&0xc000 0x0000 \b [none]
+>>>>>14 leshort&0xc000 0x1000 \b [5/4]
+>>>>>14 leshort&0xc000 0x2000 \b [5/3]
+>>>>>14 leshort&0xc000 0x3000 \b [2]
+>>>>>14 leshort&0xc000 0x0000 \bx[none]
+>>>>>14 leshort&0xc000 0x1000 \bx[5/4]
+>>>>>14 leshort&0xc000 0x2000 \bx[5/3]
+>>>>>14 leshort&0xc000 0x3000 \bx[2]
+>>>>>15 byte&0x80 =0x00 \b, YUV color
+>>>>>15 byte&0x80 =0x80 \b, bad color specification
+>>>>>15 byte&0x40 =0x40 \b, no clamping required
+>>>>>15 byte&0x40 =0x00 \b, decoders should clamp
+#>0 string x we got %s
+#>>&(4.l+4) use riff-walk
+
+# RecorderGear TR500 call recorder digits (BCD)
+0 name tr500-call-recorder-digits
+>0 byte&0xF0 0x00 \b0
+>0 byte&0xF0 0x10 \b1
+>0 byte&0xF0 0x20 \b2
+>0 byte&0xF0 0x30 \b3
+>0 byte&0xF0 0x40 \b4
+>0 byte&0xF0 0x50 \b5
+>0 byte&0xF0 0x60 \b6
+>0 byte&0xF0 0x70 \b7
+>0 byte&0xF0 0x80 \b8
+>0 byte&0xF0 0x90 \b9
+>0 byte&0xF0 0xb0 \b*
+>0 byte&0xF0 0xc0 \b#
+>0 byte&0x0F 0 \b0
+>0 byte&0x0F 1 \b1
+>0 byte&0x0F 2 \b2
+>0 byte&0x0F 3 \b3
+>0 byte&0x0F 4 \b4
+>0 byte&0x0F 5 \b5
+>0 byte&0x0F 6 \b6
+>0 byte&0x0F 7 \b7
+>0 byte&0x0F 8 \b8
+>0 byte&0x0F 9 \b9
+>0 byte&0x0F 0xb \b*
+>0 byte&0x0F 0xc \b#
+
+# TR500 call recorder extended header
+# From: David Korth <gerbilsoft@gerbilsoft.com>
+# Contains dialed/incoming phone number and timestamp.
+# TODO: Verify byte 15.
+0 name tr500-call-recorder-header
+>15 byte 2 (outgoing call:
+>15 byte 4 (incoming call:
+>1 byte 0xFF \bno number
+>1 byte !0xFF
+>>1 use tr500-call-recorder-digits
+>>2 byte !0xFF
+>>>2 use tr500-call-recorder-digits
+>>3 byte !0xFF
+>>>3 use tr500-call-recorder-digits
+>>4 byte !0xFF
+>>>4 use tr500-call-recorder-digits
+>>5 byte !0xFF
+>>>5 use tr500-call-recorder-digits
+>>6 byte !0xFF
+>>>6 use tr500-call-recorder-digits
+>>7 byte !0xFF
+>>>7 use tr500-call-recorder-digits
+>>8 byte !0xFF
+>>>8 use tr500-call-recorder-digits
+>9 byte x \b, 20%02x
+>10 byte x \b/%02x
+>11 byte x \b/%02x
+>12 byte x %02x
+>13 byte x \b:%02x
+>14 byte x \b:%02x)
+
+# AVI section extended by Patrik Radman <patrik+file-magic@iki.fi>
+#
+0 string RIFF RIFF (little-endian) data
+# RIFF Palette format
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Resource_Interchange_File_Format
+# Reference: https://worms2d.info/Palette_file
+# WAVE/AVI codec registry: https://www.iana.org/assignments/wave-avi-codec-registry/wave-avi-codec-registry.xml
+>8 string PAL\ \b, palette
+!:mime application/x-riff
+# color palette by Microsoft Corporation
+!:ext pal
+# file size = chunk size + 8 in most cases
+>>4 ulelong+8 x \b, %u bytes
+# Extended PAL Format
+>>12 string plth \b, extended
+# Simple PAL Format
+>>12 string data
+# data chunk size = color entries * 4 + 4 + sometimes extra (4) appended bytes
+>>>16 ulelong x \b, data size %u
+# palVersion is always 0x0300
+#>>>20 leshort x \b, version %#4.4x
+# palNumEntries specifies the number of palette color entries
+>>>22 uleshort x \b, %u entries
+# after palPalEntry sized (number of color entries * 4 ) vector
+>>>(22.s*4) ubequad x
+# jump relative 22 ( 8 + 16) bytes forward points after end of file or to
+# appended extra bytes like in http://safecolours.rigdenage.com/set(ms).zip/Protan(MS).pal
+>>>>&16 ubelong x \b, extra bytes
+>>>>>&-4 ubelong >0 %#8.8x
+# RIFF Device Independent Bitmap format
+# URL: http://fileformats.archiveteam.org/wiki/RDIB
+>8 string RDIB \b, device-independent bitmap
+!:ext rdi/dib
+>>16 string BM
+>>>30 leshort 12 \b, OS/2 1.x format
+>>>>34 leshort x \b, %d x
+>>>>36 leshort x %d
+>>>30 leshort 64 \b, OS/2 2.x format
+>>>>34 leshort x \b, %d x
+>>>>36 leshort x %d
+>>>30 leshort 40 \b, Windows 3.x format
+>>>>34 lelong x \b, %d x
+>>>>38 lelong x %d x
+>>>>44 leshort x %d
+# RIFF MIDI format
+# URL: http://fileformats.archiveteam.org/wiki/RIFF_MIDI
+>8 string RMID \b, MIDI
+# http://extension.nirsoft.net/rmi
+!:mime audio/mid
+#!:mime audio/x-rmid
+!:ext rmi
+# RIFF Multimedia Movie File format
+# URL: http://fileformats.archiveteam.org/wiki/RIFF_Multimedia_Movie
+>8 string RMMP \b, multimedia movie
+!:mime video/x-mmm
+!:ext mmm
+# RIFF wrapper for MP3
+>8 string RMP3 \b, MPEG Layer 3 audio
+#!:mime audio/x-rmp3
+# Microsoft WAVE format (*.wav)
+# URL: http://fileformats.archiveteam.org/wiki/WAV
+>8 string WAVE \b, WAVE audio
+#!:mime audio/vnd.wave
+!:mime audio/x-wav
+# https://www.macdisk.com/macsigen.php
+#!:apple ????WAVE
+!:ext wav/wave
+>>12 string >\0
+>>>12 use riff-walk
+# TR500 call recorder extended header
+>>16 ulelong 0x1E4
+>>>20 leshort 0x11
+>>>>256 byte 4
+>>>>>256 use tr500-call-recorder-header
+# Update: Joerg Jenderek
+# lower case for Corel Draw version 8 Bidi
+>8 string/c cdr
+# skip Corel CCX Clipart
+>>8 string !CDRXcont
+# Corel Draw Picture
+>>>0 use corel-draw
+# URL: http://fileformats.archiveteam.org/wiki/CCX_(Corel)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/ccx-corel.trid.xml
+>>8 string =CDRXcont \b, Corel Clipart
+!:mime application/x-corel-ccx
+!:ext ccx
+# 3rd chunk data {Corel\040Binary\040Meta\040File}
+#>>>20 string x \b, 3rd '%-s'
+>>>4 ulelong+8 x \b, %u bytes
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/CorelDRAW
+# Reference: http://fileformats.archiveteam.org/wiki/CorelDRAW
+# Picture templates created by newer software start with RIFF type CDT
+>8 string CDT
+>>0 use corel-draw
+# Picture templates with version 4.4
+>8 string CDST
+>>0 use corel-draw
+# pattern created by newer software start with RIFF type PAT
+>8 string PAT
+>>0 use corel-draw
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Corel_Designer
+# Reference: http://fileformats.archiveteam.org/wiki/Corel_Designer
+>8 string DES
+>>8 string !DESC
+>>>0 use corel-des
+# Corel Draw templates with version 12.5 or Corel Designer illustration 12
+>>8 string =DESC
+# MORE TESTS NEEDED HERE!
+#>>>0 use corel-des
+#>>>0 use corel-draw
+>8 string NUNDROOT \b, Steinberg CuBase
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/MIDI_Instrument_Definition_File
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/i/idf.trid.xml
+# ftp://curscott.servebeer.com/Download/Apps/_Microsoft/
+# Visual%20Studio%206.0%20Professional%20MSDN/
+# SAMPLES/VC98/SDK/GRAPHICS/AUDIO/IDFEDIT/GLOBALS.H
+# Note: called "MIDI Instrument Definition File" by TrID
+>8 string IDF\ LIST \b, MIDI Instrument Definition File
+!:mime audio/x-idf
+!:ext idf
+# 3rd chunk size like: 254 284 286 670
+#>>0x10 ulelong x \b, 3th SIZE %u
+# for debugging purpose display next chunk like: MMAPhdr
+#>>0x14 string x \b, 4th "%-8.8s"
+#>>0x1C ulelong x \b, 4th SIZE 0x%x
+# probably MIDI instrument name like: "Universal-MIDI-Instrument" "instrument name" "General MIDI"
+>>0x30 string x "%s"
+# look for inst TAG
+>>0x31 search/256 inst by
+# probably manufacture name like: "Unspecified Company" "NVidia Corporation"
+>>>&0x24 string x "%s"
+# AVI == Audio Video Interleave
+# Reference: http://fileformats.archiveteam.org/wiki/AVI
+>8 string AVI\040 \b, AVI
+# https://reposcope.com/mimetype/video/x-msvideo
+!:mime video/x-msvideo
+# https://www.iana.org/assignments/wave-avi-codec-registry/wave-avi-codec-registry.xml
+#!:mime video/vnd.avi
+!:ext avi/divx
+>>12 string LIST
+>>>20 string hdrlavih
+>>>>&36 lelong x \b, %u x
+>>>>&40 lelong x %u,
+>>>>&4 lelong >1000000 <1 fps,
+>>>>&4 lelong 1000000 1.00 fps,
+>>>>&4 lelong 500000 2.00 fps,
+>>>>&4 lelong 333333 3.00 fps,
+>>>>&4 lelong 250000 4.00 fps,
+>>>>&4 lelong 200000 5.00 fps,
+>>>>&4 lelong 166667 6.00 fps,
+>>>>&4 lelong 142857 7.00 fps,
+>>>>&4 lelong 125000 8.00 fps,
+>>>>&4 lelong 111111 9.00 fps,
+>>>>&4 lelong 100000 10.00 fps,
+# ]9.9,10.1[
+>>>>&4 lelong <101010
+>>>>>&-4 lelong >99010
+>>>>>>&-4 lelong !100000 ~10 fps,
+>>>>&4 lelong 83333 12.00 fps,
+# ]11.9,12.1[
+>>>>&4 lelong <84034
+>>>>>&-4 lelong >82645
+>>>>>>&-4 lelong !83333 ~12 fps,
+>>>>&4 lelong 66667 15.00 fps,
+# ]14.9,15.1[
+>>>>&4 lelong <67114
+>>>>>&-4 lelong >66225
+>>>>>>&-4 lelong !66667 ~15 fps,
+>>>>&4 lelong 50000 20.00 fps,
+>>>>&4 lelong 41708 23.98 fps,
+>>>>&4 lelong 41667 24.00 fps,
+# ]23.9,24.1[
+>>>>&4 lelong <41841
+>>>>>&-4 lelong >41494
+>>>>>>&-4 lelong !41708
+>>>>>>>&-4 lelong !41667 ~24 fps,
+>>>>&4 lelong 40000 25.00 fps,
+# ]24.9,25.1[
+>>>>&4 lelong <40161
+>>>>>&-4 lelong >39841
+>>>>>>&-4 lelong !40000 ~25 fps,
+>>>>&4 lelong 33367 29.97 fps,
+>>>>&4 lelong 33333 30.00 fps,
+# ]29.9,30.1[
+>>>>&4 lelong <33445
+>>>>>&-4 lelong >33223
+>>>>>>&-4 lelong !33367
+>>>>>>>&-4 lelong !33333 ~30 fps,
+>>>>&4 lelong <32224 >30 fps,
+##>>>>&4 lelong x (%lu)
+##>>>>&20 lelong x %lu frames,
+# Note: The tests below assume that the AVI has 1 or 2 streams,
+# "vids" optionally followed by "auds".
+# (Should cover 99.9% of all AVIs.)
+# assuming avih length = 56
+>>>88 string LIST
+>>>>96 string strlstrh
+>>>>>108 string vids video:
+>>>>>>&0 lelong 0 uncompressed
+# skip past vids strh
+>>>>>>(104.l+108) string strf
+>>>>>>>(104.l+132) lelong 1 RLE 8bpp
+>>>>>>>(104.l+132) string/c anim Intel RDX
+>>>>>>>(104.l+132) string/c aur2 AuraVision Aura 2
+>>>>>>>(104.l+132) string/c aura AuraVision Aura
+>>>>>>>(104.l+132) string/c bt20 Brooktree MediaStream
+>>>>>>>(104.l+132) string/c btcv Brooktree Composite Video
+>>>>>>>(104.l+132) string/c cc12 Intel YUV12
+>>>>>>>(104.l+132) string/c cdvc Canopus DV
+>>>>>>>(104.l+132) string/c cham Winnov Caviara Cham
+>>>>>>>(104.l+132) string/c cljr Proprietary YUV 4 pixels
+>>>>>>>(104.l+132) string/c cmyk Common Data Format in Printing
+>>>>>>>(104.l+132) string/c cpla Weitek 4:2:0 YUV Planar
+>>>>>>>(104.l+132) string/c cvid Cinepak
+>>>>>>>(104.l+132) string/c cwlt Microsoft Color WLT DIB
+>>>>>>>(104.l+132) string/c cyuv Creative Labs YUV
+>>>>>>>(104.l+132) string/c d261 H.261
+>>>>>>>(104.l+132) string/c d263 H.263
+>>>>>>>(104.l+132) string/c duck TrueMotion 1.0
+>>>>>>>(104.l+132) string/c dve2 DVE-2 Videoconferencing
+>>>>>>>(104.l+132) string/c fljp Field Encoded Motion JPEG
+>>>>>>>(104.l+132) string/c fvf1 Fractal Video Frame
+>>>>>>>(104.l+132) string/c gwlt Microsoft Greyscale WLT DIB
+>>>>>>>(104.l+132) string/c h260 H.260
+>>>>>>>(104.l+132) string/c h261 H.261
+>>>>>>>(104.l+132) string/c h262 H.262
+>>>>>>>(104.l+132) string/c h263 H.263
+>>>>>>>(104.l+132) string/c h264 H.264
+>>>>>>>(104.l+132) string/c h265 H.265
+>>>>>>>(104.l+132) string/c h266 H.266
+>>>>>>>(104.l+132) string/c h267 H.267
+>>>>>>>(104.l+132) string/c h268 H.268
+>>>>>>>(104.l+132) string/c h269 H.269
+>>>>>>>(104.l+132) string/c i263 Intel I.263
+>>>>>>>(104.l+132) string/c i420 Intel Indeo 4
+>>>>>>>(104.l+132) string/c ian Intel RDX
+>>>>>>>(104.l+132) string/c iclb CellB Videoconferencing Codec
+>>>>>>>(104.l+132) string/c ilvc Intel Layered Video
+>>>>>>>(104.l+132) string/c ilvr ITU-T H.263+
+>>>>>>>(104.l+132) string/c iraw Intel YUV Uncompressed
+>>>>>>>(104.l+132) string/c iv30 Intel Indeo 3
+>>>>>>>(104.l+132) string/c iv31 Intel Indeo 3.1
+>>>>>>>(104.l+132) string/c iv32 Intel Indeo 3.2
+>>>>>>>(104.l+132) string/c iv33 Intel Indeo 3.3
+>>>>>>>(104.l+132) string/c iv34 Intel Indeo 3.4
+>>>>>>>(104.l+132) string/c iv35 Intel Indeo 3.5
+>>>>>>>(104.l+132) string/c iv36 Intel Indeo 3.6
+>>>>>>>(104.l+132) string/c iv37 Intel Indeo 3.7
+>>>>>>>(104.l+132) string/c iv38 Intel Indeo 3.8
+>>>>>>>(104.l+132) string/c iv39 Intel Indeo 3.9
+>>>>>>>(104.l+132) string/c iv40 Intel Indeo 4.0
+>>>>>>>(104.l+132) string/c iv41 Intel Indeo 4.1
+>>>>>>>(104.l+132) string/c iv42 Intel Indeo 4.2
+>>>>>>>(104.l+132) string/c iv43 Intel Indeo 4.3
+>>>>>>>(104.l+132) string/c iv44 Intel Indeo 4.4
+>>>>>>>(104.l+132) string/c iv45 Intel Indeo 4.5
+>>>>>>>(104.l+132) string/c iv46 Intel Indeo 4.6
+>>>>>>>(104.l+132) string/c iv47 Intel Indeo 4.7
+>>>>>>>(104.l+132) string/c iv48 Intel Indeo 4.8
+>>>>>>>(104.l+132) string/c iv49 Intel Indeo 4.9
+>>>>>>>(104.l+132) string/c iv50 Intel Indeo 5.0
+>>>>>>>(104.l+132) string/c mpeg MPEG 1 Video Frame
+>>>>>>>(104.l+132) string/c mjpg Motion JPEG
+>>>>>>>(104.l+132) string/c mp42 Microsoft MPEG-4 v2
+>>>>>>>(104.l+132) string/c mp43 Microsoft MPEG-4 v3
+>>>>>>>(104.l+132) string/c mrca MR Codec
+>>>>>>>(104.l+132) string/c mrle Run Length Encoding
+>>>>>>>(104.l+132) string/c msvc Microsoft Video 1
+>>>>>>>(104.l+132) string/c phmo Photomotion
+>>>>>>>(104.l+132) string/c qpeq QPEG 1.1 Format Video
+>>>>>>>(104.l+132) string/c rgbt RGBT
+>>>>>>>(104.l+132) string/c rle4 Run Length Encoded 4
+>>>>>>>(104.l+132) string/c rle8 Run Length Encoded 8
+>>>>>>>(104.l+132) string/c rt21 Intel Indeo 2.1
+>>>>>>>(104.l+132) string/c rvx Intel RDX
+>>>>>>>(104.l+132) string/c sdcc Sun Digital Camera Codec
+>>>>>>>(104.l+132) string/c sfmc Crystal Net SFM Codec
+>>>>>>>(104.l+132) string/c smsc SMSC
+>>>>>>>(104.l+132) string/c smsd SMSD
+>>>>>>>(104.l+132) string/c splc Splash Studios ACM Audio Codec
+>>>>>>>(104.l+132) string/c sqz2 Microsoft VXtreme Video Codec
+>>>>>>>(104.l+132) string/c sv10 Sorenson Video R1
+>>>>>>>(104.l+132) string/c tlms TeraLogic Motion Intraframe Codec A
+>>>>>>>(104.l+132) string/c tlst TeraLogic Motion Intraframe Codec B
+>>>>>>>(104.l+132) string/c tm20 TrueMotion 2.0
+>>>>>>>(104.l+132) string/c tmic TeraLogic Motion Intraframe Codec 2
+>>>>>>>(104.l+132) string/c tmot TrueMotion Video Compression
+>>>>>>>(104.l+132) string/c tr20 TrueMotion RT 2.0
+>>>>>>>(104.l+132) string/c ulti Ultimotion
+>>>>>>>(104.l+132) string/c uyvy UYVY 4:2:2 byte ordering
+>>>>>>>(104.l+132) string/c v422 24-bit YUV 4:2:2 format
+>>>>>>>(104.l+132) string/c v655 16-bit YUV 4:2:2 format
+>>>>>>>(104.l+132) string/c vcr1 ATI VCR 1.0
+>>>>>>>(104.l+132) string/c vcr2 ATI VCR 2.0
+>>>>>>>(104.l+132) string/c vcr3 ATI VCR 3.0
+>>>>>>>(104.l+132) string/c vcr4 ATI VCR 4.0
+>>>>>>>(104.l+132) string/c vcr5 ATI VCR 5.0
+>>>>>>>(104.l+132) string/c vcr6 ATI VCR 6.0
+>>>>>>>(104.l+132) string/c vcr7 ATI VCR 7.0
+>>>>>>>(104.l+132) string/c vcr8 ATI VCR 8.0
+>>>>>>>(104.l+132) string/c vcr9 ATI VCR 9.0
+>>>>>>>(104.l+132) string/c vdct Video Maker Pro DIB
+>>>>>>>(104.l+132) string/c vids YUV 4:2:2 CCIR 601 for V422
+>>>>>>>(104.l+132) string/c vivo Vivo H.263
+>>>>>>>(104.l+132) string/c vixl VIXL
+>>>>>>>(104.l+132) string/c vlv1 VLCAP.DRV
+>>>>>>>(104.l+132) string/c wbvc W9960
+>>>>>>>(104.l+132) string/c x263 mmioFOURCC('X','2','6','3')
+>>>>>>>(104.l+132) string/c xlv0 XL Video Decoder
+>>>>>>>(104.l+132) string/c y211 YUV 2:1:1 Packed
+>>>>>>>(104.l+132) string/c y411 YUV 4:1:1 Packed
+>>>>>>>(104.l+132) string/c y41b YUV 4:1:1 Planar
+>>>>>>>(104.l+132) string/c y41p PC1 4:1:1
+>>>>>>>(104.l+132) string/c y41t PC1 4:1:1 with transparency
+>>>>>>>(104.l+132) string/c y42b YUV 4:2:2 Planar
+>>>>>>>(104.l+132) string/c y42t PC1 4:2:2 with transparency
+>>>>>>>(104.l+132) string/c yc12 Intel YUV12 Codec
+>>>>>>>(104.l+132) string/c yuv8 Winnov Caviar YUV8
+>>>>>>>(104.l+132) string/c yuv9 YUV9
+>>>>>>>(104.l+132) string/c yuy2 YUY2 4:2:2 byte ordering packed
+>>>>>>>(104.l+132) string/c yuyv BI_YUYV, Canopus
+>>>>>>>(104.l+132) string/c fmp4 FFMpeg MPEG-4
+>>>>>>>(104.l+132) string/c div3 DivX 3
+>>>>>>>>112 string/c div3 Low-Motion
+>>>>>>>>112 string/c div4 Fast-Motion
+>>>>>>>(104.l+132) string/c divx DivX 4
+>>>>>>>(104.l+132) string/c dx50 DivX 5
+>>>>>>>(104.l+132) string/c xvid XviD
+>>>>>>>(104.l+132) string/c h264 H.264
+>>>>>>>(104.l+132) string/c wmv3 Windows Media Video 9
+>>>>>>>(104.l+132) string/c h264 X.264 or H.264
+>>>>>>>(104.l+132) lelong 0
+##>>>>>>>(104.l+132) string x (%.4s)
+# skip past first (video) LIST
+>>>>(92.l+96) string LIST
+>>>>>(92.l+104) string strlstrh
+>>>>>>(92.l+116) string auds \b, audio:
+# auds strh length = 56:
+>>>>>>>(92.l+172) string strf
+>>>>>>>>(92.l+180) leshort 0x0001 uncompressed PCM
+>>>>>>>>(92.l+180) leshort 0x0002 ADPCM
+>>>>>>>>(92.l+180) leshort 0x0006 aLaw
+>>>>>>>>(92.l+180) leshort 0x0007 uLaw
+>>>>>>>>(92.l+180) leshort 0x0050 MPEG-1 Layer 1 or 2
+>>>>>>>>(92.l+180) leshort 0x0055 MPEG-1 Layer 3
+>>>>>>>>(92.l+180) leshort 0x2000 Dolby AC3
+>>>>>>>>(92.l+180) leshort 0x0161 DivX
+##>>>>>>>>(92.l+180) leshort x (%#.4x)
+>>>>>>>>(92.l+182) leshort 1 (mono,
+>>>>>>>>(92.l+182) leshort 2 (stereo,
+>>>>>>>>(92.l+182) leshort >2 (%d channels,
+>>>>>>>>(92.l+184) lelong x %d Hz)
+# auds strh length = 64:
+>>>>>>>(92.l+180) string strf
+>>>>>>>>(92.l+188) leshort 0x0001 uncompressed PCM
+>>>>>>>>(92.l+188) leshort 0x0002 ADPCM
+>>>>>>>>(92.l+188) leshort 0x0055 MPEG-1 Layer 3
+>>>>>>>>(92.l+188) leshort 0x2000 Dolby AC3
+>>>>>>>>(92.l+188) leshort 0x0161 DivX
+##>>>>>>>>(92.l+188) leshort x (%#.4x)
+>>>>>>>>(92.l+190) leshort 1 (mono,
+>>>>>>>>(92.l+190) leshort 2 (stereo,
+>>>>>>>>(92.l+190) leshort >2 (%d channels,
+>>>>>>>>(92.l+192) lelong x %d Hz)
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/VDR_(VirtualDub)
+# Reference: http://sourceforge.net/projects/virtualdub/files/virtualdub-win/
+# 1.10.4.35491/VirtualDub-1.10.4-src.7z/src/vdremote/Main.cpp
+# VirtualDub link handler
+>8 string VDRM \b, VirtualDub link
+!:mime video/x-vdr
+!:ext vdr
+>>12 string PATH \b, PATH
+# remote-path to video file
+>>16 pstring/l x %s
+# Animated Cursor format
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Windows_Animated_Cursor
+# Reference: https://www.gdgsoft.com/anituner/help/aniformat.htm
+>8 string ACON \b, animated cursor
+!:mime application/x-navi-animation
+# http://extension.nirsoft.net/ani
+#!:mime image/ani
+!:ext ani
+# INAM tag followed by length of title
+>>24 string INAM
+>>>28 pstring/l x "%s"
+# IART tag followed by length of author
+>>>(28.l+32) ubelong 0x49415254
+>>>>&0 pstring/l x %s
+# SoundFont 2 <mpruett@sgi.com>
+# URL: http://fileformats.archiveteam.org/wiki/SoundFont_2.0
+>8 string sfbk \b, SoundFont/Bank
+!:mime audio/x-sfbk
+!:ext sf2
+# MPEG-1 wrapped in a RIFF, apparently
+# URL: http://file.fyicenter.com/17_Video_.DAT_File_Extension_for_VCD_Files.html
+>8 string CDXA \b, wrapped MPEG-1 (CDXA)
+!:mime video/x-cdxa
+!:ext mpg/dat
+# URL: http://fileformats.archiveteam.org/wiki/4X_IMA_ADPCM
+>8 string 4XMV \b, 4X Movie file
+!:mime video/x-4xmv
+!:ext 4xm/4xa
+# AMV-type AVI file: https://wiki.multimedia.cx/index.php?title=AMV
+>8 string AMV\040 \b, AMV
+# http://fileformats.archiveteam.org/wiki/MTV_Video_(.AMV)
+!:mime video/x-amv
+!:ext amv
+#!:ext amv/mtv
+# URL: http://fileformats.archiveteam.org/wiki/WebP
+>8 string WEBP \b, Web/P image
+!:mime image/webp
+!:ext webp
+!:strength + 50
+>>12 use riff-walk
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/RIFF_MIDS
+>8 string MIDS \b, MIDI Stream
+!:mime audio/x-mids
+!:ext mds
+# From: Joerg Jenderek
+# URL: http://mark0.net/soft-trid-e.html
+# Reference: http://fileformats.archiveteam.org/wiki/Trd_(TRID)
+>8 string TRID \b, TrID defs package
+!:mime application/x-trid-trd
+!:ext trd
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/CorelDRAW
+# Reference: http://fileformats.archiveteam.org/wiki/CorelDRAW
+# Note: Since version 3 CorelDraw Pictures are RIFF based
+# but data chunks remain proprietary.
+# Since version 14 til 15 packed as "content/riffData.cdr" and
+# since 16 "content/root.dat" in ZIP container
+# TODO: distinguish templates with version 12.5 from Designer illustration 12
+# display information of RIFF based Corel Draw pictures, templates and patterns
+0 name corel-draw
+# display second chunk for debugging
+#>8 string x \b, [8]=%.8s
+>0 string x \b, Corel Draw
+#!:mime image/x-coreldraw
+!:mime application/vnd.corel-draw
+# used by newer pictures templates
+>>8 string CDT
+# used by templates with newer versions since 16
+>>>12 string =fver Picture template (root.dat)
+!:ext dat
+# used by templates with older versions with vrsn tag
+>>>12 string !fver
+# used by templates with older versions 14-15
+>>>>11 string >E Picture template (riffData.cdr)
+!:ext cdr
+# used by templates with older versions 11-13
+>>>>11 string <F Picture template
+!:ext cdt/cdrt
+# used by older templates with version 4.4
+>>8 string CDST Picture template
+!:ext cdt
+# used by templates with version 12.5
+>>8 string DESC Picture template
+!:ext cdt
+# used by newer patterns with version 22
+>>8 string PAT Pattern
+!:ext dat
+# remaining older templates, patterns, drawings
+>>8 default x
+# pattern with old version 4.y
+>>>26 ulelong =0x0000206C Pattern
+!:ext pat
+# pattern with newer versions
+>>>26 ulelong =0x00000D2C Pattern
+!:ext pat
+# remaining older templates or pictures
+>>>26 default x
+# used by older versions 5 - 15
+>>>>12 string =vrsn
+# 4th chunk size unequal 282Ch only found for CDR
+>>>>>26 ulelong !0x0000282c Picture
+!:ext cdr
+>>>>>26 default x Picture or template
+!:ext cdr/cdt
+# used by newer versions since 16
+>>>>12 string =fver Picture (root.dat)
+!:ext dat
+# version marked by 1 ASCII char: space~3, ... , F~15, ... , N~22, ... R~22 template
+>11 string x \b, version
+>11 string >\040 '%-.1s'
+>0 use corel-version
+>4 ulelong+8 x \b, %u bytes
+#
+# display numeric version of RIFF based Corel after 3rd RIFF tag
+0 name corel-version
+# for debugging purpose; vrsn for short content; fver for 16 byte size
+#>12 string x \b, TAG "%-4.4s"
+# 1st data chunk length 2 implies short content version
+>16 ulelong 2
+# vrsn chunk short content interpreted by MajorVersion * 100 + MinorVersion
+>>20 uleshort/100 x %u
+>>20 uleshort%100 >0 \b.%u
+# for debugging purpose display next chunk like: DISP LIST
+#>>22 string x \b, 4th "%-4.4s"
+#>>26 ulelong x \b, 4th SIZE %#x
+# for debugging purpose display 5th chunk like: LIST DISP ccmm osfp
+#>>(26.l+30) string x \b, 5th "%-4.4s"
+# 1st data chunk length 10h implies 16 byte content with version info
+>16 ulelong 0x10
+>>34 ubyte x %u
+>>>33 ubyte >0 \b.%u
+# display information of RIFF based Corel Design formats
+0 name corel-des
+# display second chunk for debugging
+#>8 string x \b, [8]=%.8s
+>12 string x \b, Corel DESIGNER
+!:mime image/x-corel-des
+#!:mime application/x-vnd.corel.designer.document
+# used by Corel Designer with newer versions since 16
+>12 string =fver graphics (root.dat)
+!:ext dat
+# used by Corel Designer templates with older versions with vrsn tag
+>12 string !fver
+# used by Corel Designer with versions 14-15
+>>11 string >D graphics (riffData.cdr)
+!:ext cdr
+# used by Corel Designer with versions 10-12
+>>11 string <E graphics
+!:ext des
+# version indicated by last ASCII char of second chunk tag
+>11 string x \b, version '%-.1s'
+# but vrsn short content is not always version indicator
+# exceptions: 'A'~11.4 'B'~12 'C'~12.5
+>11 string >D
+>>0 use corel-version
+# for debugging purpose display next chunk like: DISP LIST
+#>>22 string x \b, 4th "%-4.4s"
+#>>26 ulelong x \b, 4th SIZE %#x
+# for debugging purpose display 5th chunk like: LIST osfp
+#>>(26.l+30) string x \b, 5th "%-4.4s"
+>4 ulelong+8 x \b, %u bytes
+
+#
+# XXX - some of the below may only appear in little-endian form.
+#
+# Also "MV93" appears to be for one form of Macromedia Director
+# files, and "GDMF" appears to be another multimedia format.
+#
+0 string RIFX RIFF (big-endian) data
+# RIFF Palette format
+>8 string PAL \b, palette
+>>16 beshort x \b, version %d
+>>18 beshort x \b, %d entries
+# RIFF Device Independent Bitmap format
+>8 string RDIB \b, device-independent bitmap
+>>16 string BM
+>>>30 beshort 12 \b, OS/2 1.x format
+>>>>34 beshort x \b, %d x
+>>>>36 beshort x %d
+>>>30 beshort 64 \b, OS/2 2.x format
+>>>>34 beshort x \b, %d x
+>>>>36 beshort x %d
+>>>30 beshort 40 \b, Windows 3.x format
+>>>>34 belong x \b, %d x
+>>>>38 belong x %d x
+>>>>44 beshort x %d
+# RIFF MIDI format
+>8 string RMID \b, MIDI
+# RIFF Multimedia Movie File format
+>8 string RMMP \b, multimedia movie
+# Microsoft WAVE format (*.wav)
+>8 string WAVE \b, WAVE audio
+>>20 leshort 1 \b, Microsoft PCM
+>>>34 leshort >0 \b, %d bit
+>>22 beshort =1 \b, mono
+>>22 beshort =2 \b, stereo
+>>22 beshort >2 \b, %d channels
+>>24 belong >0 %d Hz
+# Corel Draw Picture big endian not tested by real examples
+#>8 string CDRA \b, Corel Draw Picture
+#>8 string CDR6 \b, Corel Draw Picture, version 6
+>8 string CDR
+>>0 use \^corel-draw
+
+# AVI == Audio Video Interleave
+>8 string AVI\040 \b, AVI
+# Animated Cursor format
+>8 string ACON \b, animated cursor
+# Notation Interchange File Format (big-endian only)
+>8 string NIFF \b, Notation Interchange File Format
+# SoundFont 2 <mpruett@sgi.com>
+>8 string sfbk SoundFont/Bank
+
+#------------------------------------------------------------------------------
+# Sony Wave64
+# see http://www.vcs.de/fileadmin/user_upload/MBS/PDF/Whitepaper/Informations_about_Sony_Wave64.pdf
+# 128 bit RIFF-GUID { 66666972-912E-11CF-A5D6-28DB04C10000 } in little-endian
+0 string riff\x2E\x91\xCF\x11\xA5\xD6\x28\xDB\x04\xC1\x00\x00 Sony Wave64 RIFF data
+# 128 bit + total file size (64 bits) so 24 bytes
+# then WAVE-GUID { 65766177-ACF3-11D3-8CD1-00C04F8EDB8A }
+>24 string wave\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A \b, WAVE 64 audio
+!:mime audio/x-w64
+# FMT-GUID { 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A }
+>>40 search/256 fmt\x20\xF3\xAC\xD3\x11\x8C\xD1\x00\xC0\x4F\x8E\xDB\x8A \b
+>>>&10 leshort =1 \b, mono
+>>>&10 leshort =2 \b, stereo
+>>>&10 leshort >2 \b, %d channels
+>>>&12 lelong >0 %d Hz
+
+#------------------------------------------------------------------------------
+# MBWF/RF64
+# see EBU TECH 3306 https://tech.ebu.ch/docs/tech/tech3306-2009.pdf
+0 string RF64\xff\xff\xff\xffWAVEds64 MBWF/RF64 audio
+!:mime audio/x-wav
+>40 search/256 fmt\x20 \b
+>>&6 leshort =1 \b, mono
+>>&6 leshort =2 \b, stereo
+>>&6 leshort >2 \b, %d channels
+>>&8 lelong >0 %d Hz
diff --git a/contrib/libs/libmagic/magic/Magdir/ringdove b/contrib/libs/libmagic/magic/Magdir/ringdove
new file mode 100644
index 0000000000..38dd4bfe66
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ringdove
@@ -0,0 +1,45 @@
+#------------------------------------------------------------------------------
+# $File: ringdove,v 1.1 2022/08/16 12:04:30 christos Exp $
+# ringdove: file(1) magic for RingdoveEDA data files
+
+# librnd and global
+0 regex/128l ha:rnd-menu-v[0-9]+[\ \t\r\n]*[{] librnd menu system (lihata)
+0 regex/128l ha:rnd-menu-patch-v[0-9]+[\ \t\r\n]*[{] librnd menu patch (lihata)
+0 regex/128l ha:coraleda-project-v[0-9]+[\ \t\r\n]*[{] CoralEDA/Ringdove project file (lihata)
+0 regex/128l ha:ringdove-project-v[0-9]+[\ \t\r\n]*[{] Ringdove project file (lihata)
+
+# pcb-rnd
+0 regex/128l ha:pcb-rnd-board-v[0-9]+[\ \t\r\n]*[{] pcb-rnd board file (lihata)
+0 regex/128l li:pcb-rnd-subcircuit-v[0-9]+[\ \t\r\n]*[{] pcb-rnd subcircuit/footprint file (lihata)
+0 regex/128l ha:pcb-rnd-buffer-v[0-9]+[\ \t\r\n]*[{] pcb-rnd paste buffer content (lihata)
+0 regex/128l li:pcb-rnd-conf-v[0-9]+[\ \t\r\n]*[{] pcb-rnd configuration (lihata)
+0 regex/128l ha:pcb-rnd-drc-query-v[0-9]+[\ \t\r\n]*[{] pcb-rnd drc query string (lihata)
+0 regex/128l li:pcb-rnd-font-v[0-9]+[\ \t\r\n]*[{] pcb-rnd vector font (lihata)
+0 regex/128l ha:pcb-rnd-log-v[0-9]+[\ \t\r\n]*[{] pcb-rnd message log dump (lihata)
+0 regex/128l ha:pcb-rnd-padstack-v[0-9]+[\ \t\r\n]*[{] pcb-rnd padstack (lihata)
+0 regex/128l li:pcb-rnd-view-list-v[0-9]+[\ \t\r\n]*[{] pcb-rnd view list (lihata)
+0 regex/128l li:view-list-v[0-9]+[\ \t\r\n]*[{] pcb-rnd view list (lihata)
+0 search Netlist(Freeze) pcb-rnd or gEDA/PCB netlist forward annotation action script
+
+# sch-rnd (cschem data model)
+0 regex/128l li:cschem-buffer-v[0-9]+[\ \t\r\n]*[{] sch-rnd/cschem buffer content (lihata)
+0 regex/128l li:sch-rnd-conf-v[0-9]+[\ \t\r\n]*[{] sch-rnd configuration (lihata)
+0 regex/128l ha:std_devmap.v[0-9]+[\ \t\r\n]*[{] sch-rnd devmap (device mapping; lihata)
+0 regex/128l li:cschem-group-v[0-9]+[\ \t\r\n]*[{] sch-rnd/cschem group or symbol (lihata)
+0 regex/128l ha:cschem-sheet-v[0-9]+[\ \t\r\n]*[{] sch-rnd/cschem schematic sheet (lihata)
+
+# tEDAx (modular format)
+0 regex/1l tEDAx[\ \t\r\n]v tEDAx (Trivial EDA eXchange)
+>0 regex begin\ symbol\ v with schematic symbol
+>0 regex begin\ board\ v with Printed Circuit Board
+>0 regex begin\ route_req\ v with PCB routing request
+>0 regex begin\ route_res\ v with PCB routing result
+>0 regex begin\ camv_layer\ v with camv-rnd exported layer
+>0 regex begin\ netlist\ v with netlist
+>0 regex begin\ backann\ v with Ringdove EDA back annotation
+>0 regex begin\ footprint\ v with PCB footprint
+>0 regex begin\ drc\ v with PCB DRC script
+>0 regex begin\ drc_query_rule\ v with pcb-rnd drc_query rules
+>0 regex begin\ drc_query_def\ v with pcb-rnd drc_query value/config definitions
+>0 regex begin\ etest\ v with PCB electric test
+
diff --git a/contrib/libs/libmagic/magic/Magdir/rpi b/contrib/libs/libmagic/magic/Magdir/rpi
new file mode 100644
index 0000000000..0d213b5357
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/rpi
@@ -0,0 +1,52 @@
+
+#------------------------------------------------------------------------------
+# $File: rpi,v 1.3 2022/04/02 14:39:34 christos Exp $
+# rpi: file(1) magic for Raspberry Pi images
+-44 lelong 0
+>4 lelong 0
+>>8 lelong 1
+>>12 lelong 4
+>>>16 string 283x
+>>>>20 lelong 1
+>>>>>24 lelong 4
+>>>>>>28 string DTOK
+>>>>>>>32 lelong 44
+>>>>>>>>36 lelong 4
+>>>>>>>>>40 string RPTL Raspberry PI kernel image
+
+-56 lelong 0
+>4 lelong 0
+>>8 lelong 1
+>>12 lelong 4
+>>>16 string 283x
+>>>>20 lelong 1
+>>>>>24 lelong 4
+>>>>>>28 string DTOK
+>>>>>>>32 lelong 1
+>>>>>>>>36 lelong 4
+>>>>>>>>>40 string DDTK8
+>>>>>>>>>>48 lelong 4
+>>>>>>>>>>>52 string RPTL Raspberry PI kernel image
+
+# From: Joerg Jenderek
+# URL: https://www.raspberrypi.com/documentation/computers/raspberry-pi.html
+# #raspberry-pi-4-boot-eeprom
+# Reference: https://github.com/raspberrypi/rpi-eeprom/blob/master/rpi-eeprom-config
+# Note: start with same magic as for BIOS (ia32) ROM Extension handled by ./intel
+# masked with MAGIC_MASK and then compared with MAGIC
+0 belong&0xFFffF00F 0x55aaF00F Raspberry PI EEPROM
+#!:mime application/octet-stream
+!:mime application/x-raspberry-eeprom
+# like: pieeprom-2020-09-03.bin
+!:ext bin
+# a 32 bit offset to the next section like: 000184d4 000184c8 00018534 ... 0000bb84 0000bbd4 0000bbd4
+>4 ubelong x \b, offset %8.8x
+#>(4.L) ubelong x NEXT=%8.8x
+# self.length
+>8 ubelong !0 \b, length %x
+# self.filename
+>12 string >0 \b, "%s"
+# length is zero
+>8 ubelong =0
+# if length is zero then 2nd section magic here can be zero; this means sections parsing done
+>>8 ubelong !0 \b, 2nd MAGIC=%8.8x
diff --git a/contrib/libs/libmagic/magic/Magdir/rpm b/contrib/libs/libmagic/magic/Magdir/rpm
new file mode 100644
index 0000000000..9a795f841a
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/rpm
@@ -0,0 +1,45 @@
+
+#------------------------------------------------------------------------------
+# $File: rpm,v 1.12 2013/01/11 16:45:23 christos Exp $
+#
+# RPM: file(1) magic for Red Hat Packages Erik Troan (ewt@redhat.com)
+#
+0 belong 0xedabeedb RPM
+!:mime application/x-rpm
+>4 byte x v%d
+>5 byte x \b.%d
+>6 beshort 1 src
+>6 beshort 0 bin
+>>8 beshort 1 i386/x86_64
+>>8 beshort 2 Alpha/Sparc64
+>>8 beshort 3 Sparc
+>>8 beshort 4 MIPS
+>>8 beshort 5 PowerPC
+>>8 beshort 6 68000
+>>8 beshort 7 SGI
+>>8 beshort 8 RS6000
+>>8 beshort 9 IA64
+>>8 beshort 10 Sparc64
+>>8 beshort 11 MIPSel
+>>8 beshort 12 ARM
+>>8 beshort 13 MiNT
+>>8 beshort 14 S/390
+>>8 beshort 15 S/390x
+>>8 beshort 16 PowerPC64
+>>8 beshort 17 SuperH
+>>8 beshort 18 Xtensa
+>>8 beshort 255 noarch
+
+#delta RPM Daniel Novotny (dnovotny@redhat.com)
+0 string drpm Delta RPM
+!:mime application/x-rpm
+>12 string x %s
+>>8 beshort 11 MIPSel
+>>8 beshort 12 ARM
+>>8 beshort 13 MiNT
+>>8 beshort 14 S/390
+>>8 beshort 15 S/390x
+>>8 beshort 16 PowerPC64
+>>8 beshort 17 SuperH
+>>8 beshort 18 Xtensa
+>>10 string x %s
diff --git a/contrib/libs/libmagic/magic/Magdir/rpmsg b/contrib/libs/libmagic/magic/Magdir/rpmsg
new file mode 100644
index 0000000000..cbbbb2bc4f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/rpmsg
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# $File: rpmsg,v 1.1 2019/04/19 00:40:47 christos Exp $
+# rpmsg: file(1) magic for restricted-permission messages (or "rights-protected" messages)
+# see https://en.wikipedia.org/wiki/Rpmsg
+
+0 string \x76\xe8\x04\x60\xc4\x11\xe3\x86 rpmsg Restricted Permission Message
diff --git a/contrib/libs/libmagic/magic/Magdir/rst b/contrib/libs/libmagic/magic/Magdir/rst
new file mode 100644
index 0000000000..0df15b8fa5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/rst
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: rst,v 1.4 2023/07/27 18:26:32 christos Exp $
+# rst: ReStructuredText http://docutils.sourceforge.net/rst.html
+0 search/256 \=\=
+!:strength + 30
+>&0 regex/256 \^[\=]+$
+>>&0 search/512 :Author: ReStructuredText file
+>>&0 search/512 \012Authors: ReStructuredText file
+>>&0 search/512 \012Author: ReStructuredText file
+>>&0 default x
+>>>&0 regex/512 \^\\.\\.[A-Za-z] ReStructuredText file
+!:ext rst
diff --git a/contrib/libs/libmagic/magic/Magdir/rtf b/contrib/libs/libmagic/magic/Magdir/rtf
new file mode 100644
index 0000000000..48a1f28af4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/rtf
@@ -0,0 +1,94 @@
+
+#------------------------------------------------------------------------------
+# $File: rtf,v 1.9 2020/12/12 20:01:47 christos Exp $
+# rtf: file(1) magic for Rich Text Format (RTF)
+#
+# Duncan P. Simpson, D.P.Simpson@dcs.warwick.ac.uk
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Rich_Text_Format
+# Reference: http://www.snake.net/software/RTF/RTF-Spec-1.7.rtf
+# http://www.kleinlercher.at/tools/Windows_Protocols/Word2007RTFSpec9.pdf
+0 string {\\rtf
+# skip DROID fmt-355-signature-id-522.rtf by looking for valid version
+>5 ubyte !0xAB
+# skip also \ in DROID fmt-50-signature-id-158.rtf by looking for valid version
+>>5 ubyte !0x5C Rich Text Format data
+!:mime text/rtf
+!:apple ????RTF
+!:ext rtf
+>>>0 use rtf-info
+# display information like version, language and code page of RTF
+0 name rtf-info
+# 1 mostly, 2 for newer Pocket Word documents, space for test like fdo78502.rtf, { for some urtf
+>5 ubyte !0x7b \b, version %c
+# The word for character set must precede any text or most other control words
+>6 string \\mac \b, Apple Macintosh
+>6 string \\pc
+# control word \pca
+>>9 ubyte =0x61 \b, IBM PS/2, code page 850
+>>9 ubyte !0x61 \b, IBM PC, code page 437
+# unknown character set or ANSI later after control words like
+# \adeflang1025 \info \title \author \category \manager
+# "Burow, Steffanie - Im Tal des Schneeleoparden.rtf"
+#>6 search/105 \\ansi \b, ANSI
+>6 search/502 \\ansi \b, ANSI
+>6 default x \b, unknown character set
+# look for explicit codepage keyword
+# "Burow, Steffanie - Im Tal des Schneeleoparden.rtf"
+#>5 search/110 \\ansicpg
+>5 search/500 \\ansicpg
+# skip unknown or buggy codepage string 0 like in fdo78502.rtf
+>>&0 ubyte !0x30 \b, code page
+# codepage string: 437~United States IBM, ..., 1252~WesternEuropean, ..., 57011~Punjabi
+>>>&-1 string x %-.3s
+# skip space or \ and display possible 4th digit of code page string
+>>>&2 ubyte >0x2F
+>>>>&-1 ubyte <0x3A \b%c
+# possible 5th digit of code page string
+>>>>>&0 ubyte >0x2F
+>>>>>>&-1 ubyte <0x3A \b%c
+# look again at version byte to use default clause
+>5 ubyte x
+# Default language ID for South Asian/Middle Eastern text
+# language ID: 1025, ..., 1065~Persian, ..., 2057~English_UnitedKingdom, ..., 58380~French_NorthAfrica
+# Readme-0.72-Persian.rtf
+#>6 search/1 \\adeflang \b, default middle east language ID
+>>6 search/497 \\adeflang \b, default middle east language ID
+# https://docs.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a
+>>>&0 string x %.4s
+# skip \ and NL and show possible 5th digit of language string
+>>>&4 ubyte >0x2F
+>>>>&-1 ubyte <0x3A \b%c
+# else look for default language to be used when the \plain control word is encountered
+>>6 default x
+# "Burow, Steffanie - Im Tal des Schneeleoparden.rtf"
+#>>>6 search/127 \\deflang
+>>>6 search/505 \\deflang
+>>>>&0 string >0 \b, default language ID %-.4s
+# possible 5th digit of language string
+>>>>&4 ubyte >0x2F
+>>>>>&-1 ubyte <0x3A \b%c
+
+# Reference: http://latex2rtf.sourceforge.net/rtfspec_63.html
+# Note: no real world example found
+0 string {\\urtf Rich Text Format unicoded data
+!:mime text/rtf
+#!:apple ????RTF
+!:ext rtf
+>1 use rtf-info
+
+# URL: https://en.wikipedia.org/wiki/Microsoft_Word
+# Reference: http://fileformats.archiveteam.org/wiki/Microsoft_Word
+# Note: called by TrID "Pocket Word document"
+# by PlanMaker "Pocket Word-Handheld PC" for pwd
+# by PlanMaker "Pocket Word-Pocket PC" for psw
+0 string {\\pwd Pocket Word document or template
+# by SoftMaker Office http://extension.nirsoft.net/pwd
+#!:mime application/msword
+# https://reposcope.com/mimetype/application/x-pocket-word
+!:mime application/x-pocket-word
+# PWD for Handheld PC variant and PSW for Pocket PC variant
+# PWT for template
+!:ext pwd/psw/pwt
+>0 use rtf-info
+
diff --git a/contrib/libs/libmagic/magic/Magdir/ruby b/contrib/libs/libmagic/magic/Magdir/ruby
new file mode 100644
index 0000000000..9e67a3e22d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ruby
@@ -0,0 +1,55 @@
+
+#------------------------------------------------------------------------------
+# $File: ruby,v 1.10 2019/07/21 09:40:17 christos Exp $
+# ruby: file(1) magic for Ruby scripting language
+# URL: https://www.ruby-lang.org/
+# From: Reuben Thomas <rrt@sc3d.org>
+
+# Ruby scripts
+0 search/1/w #!\ /usr/bin/ruby Ruby script text executable
+!:strength + 15
+!:mime text/x-ruby
+0 search/1/w #!\ /usr/local/bin/ruby Ruby script text executable
+!:strength + 15
+!:mime text/x-ruby
+0 search/1 #!/usr/bin/env\ ruby Ruby script text executable
+!:strength + 15
+!:mime text/x-ruby
+0 search/1 #!\ /usr/bin/env\ ruby Ruby script text executable
+!:strength + 15
+!:mime text/x-ruby
+
+# What looks like ruby, but does not have a shebang
+# (modules and such)
+# From: Lubomir Rintel <lkundrak@v3.sk>
+0 search/8192 require
+>0 regex \^[[:space:]]*require[[:space:]]'[A-Za-z_/.]+'
+>>0 regex def\ [a-z]|\ do$
+>>>&0 regex \^[[:space:]]*end([[:space:]]+[;#].*)?$ Ruby script text
+!:strength + 30
+!:mime text/x-ruby
+0 regex \^[[:space:]]*(class|module)[[:space:]][A-Z]
+>0 regex (modul|includ)e\ [A-Z]|def\ [a-z]
+>>&0 regex \^[[:space:]]*end([[:space:]]+[;#].*)?$ Ruby script text
+!:strength + 30
+!:mime text/x-ruby
+# Classes with no modules or defs, beats simple ASCII
+0 regex \^[[:space:]]*(class|module)[[:space:]][A-Z]
+>&0 regex \^[[:space:]]*end([[:space:]]+[;#if].*)?$ Ruby script text
+!:strength + 10
+!:mime text/x-ruby
+# Looks for function definition to balance python magic
+# def name (args)
+# end
+0 search/8192 def\
+>0 regex \^[[:space:]]*def\ [a-z]|def\ [[:alpha:]]+::[a-z]
+>>&0 regex \^[[:space:]]*end([[:space:]]+[;#].*)?$ Ruby script text
+!:strength + 10
+!:mime text/x-ruby
+
+0 search/8192 require
+>0 regex \^[[:space:]]*require[[:space:]]'[A-Za-z_/.]+' Ruby script text
+!:mime text/x-ruby
+0 search/8192 include
+>0 regex \^[[:space:]]*include\ ([A-Z]+[a-z]*(::))+ Ruby script text
+!:mime text/x-ruby
diff --git a/contrib/libs/libmagic/magic/Magdir/rust b/contrib/libs/libmagic/magic/Magdir/rust
new file mode 100644
index 0000000000..b1bbd9d970
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/rust
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# $File: rust,v 1.2 2022/11/18 15:58:15 christos Exp $
+# Magic for Rust and related languages programs
+#
+
+# Rust compiler metadata
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/rust-lang/rust/blob/1.64.0/compiler/rustc_metadata/src/rmeta/mod.rs
+0 string rust\x00\x00\x00
+>12 string \014rustc\x20 Rust compiler metadata
+!:ext rmeta
+>>7 byte x \b, version %d
+
+# Rust incremental compilation metadata
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/rust-lang/rust/blob/1.64.0/compiler/rustc_incremental/src/persist/file_format.rs
+0 string RSIC
+>4 uleshort =0 Rust incremental compilation metadata
+!:ext bin
+>>6 pstring x \b, rustc %s
diff --git a/contrib/libs/libmagic/magic/Magdir/sc b/contrib/libs/libmagic/magic/Magdir/sc
new file mode 100644
index 0000000000..dc6d6c83d7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sc
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# $File: sc,v 1.6 2009/09/19 16:28:12 christos Exp $
+# sc: file(1) magic for "sc" spreadsheet
+#
+38 string Spreadsheet sc spreadsheet file
+!:mime application/x-sc
diff --git a/contrib/libs/libmagic/magic/Magdir/sccs b/contrib/libs/libmagic/magic/Magdir/sccs
new file mode 100644
index 0000000000..04e7929921
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sccs
@@ -0,0 +1,24 @@
+
+#------------------------------------------------------------------------------
+# $File: sccs,v 1.8 2020/06/20 21:32:52 christos Exp $
+# sccs: file(1) magic for SCCS archives
+#
+# SCCS v4 archive structure:
+# \001h01207
+# \001s 00276/00000/00000
+# \001d D 1.1 87/09/23 08:09:20 ian 1 0
+# \001c date and time created 87/09/23 08:09:20 by ian
+# \001e
+# \001u
+# \001U
+# ... etc.
+# Now '\001h' happens to be the same as the 3B20's a.out magic number (0550).
+# *Sigh*. And these both came from various parts of the USG.
+# Maybe we should just switch everybody from SCCS to RCS!
+# Further, you can't just say '\001h0', because the five-digit number
+# is a checksum that could (presumably) have any leading digit,
+# Fortunately we have regular expression matching:
+0 string \001h
+>2 regex [0-9][0-9][0-9][0-9][0-9]$
+>>8 string \001s\040 SCCS v4 archive data
+>2 string V6,sum= SCCS v6 archive data
diff --git a/contrib/libs/libmagic/magic/Magdir/scientific b/contrib/libs/libmagic/magic/Magdir/scientific
new file mode 100644
index 0000000000..d52d6aeb01
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/scientific
@@ -0,0 +1,144 @@
+
+#------------------------------------------------------------------------------
+# $File: scientific,v 1.14 2023/04/29 17:28:09 christos Exp $
+# scientific: file(1) magic for scientific formats
+#
+# From: Joe Krahn <krahn@niehs.nih.gov>
+
+########################################################
+# CCP4 data and plot files:
+0 string MTZ\040 MTZ reflection file
+
+92 string PLOT%%84 Plot84 plotting file
+>52 byte 1 , Little-endian
+>55 byte 1 , Big-endian
+
+########################################################
+# Electron density MAP/MASK formats
+
+0 string EZD_MAP NEWEZD Electron Density Map
+109 string MAP\040( Old EZD Electron Density Map
+
+0 string/c :-)\040Origin BRIX Electron Density Map
+>170 string >0 , Sigma:%.12s
+#>4 string >0 %.178s
+#>4 addr x %.178s
+
+7 string 18\040!NTITLE XPLOR ASCII Electron Density Map
+9 string \040!NTITLE\012\040REMARK CNS ASCII electron density map
+
+208 string MAP\040 CCP4 Electron Density Map
+# Assumes same stamp for float and double (normal case)
+>212 byte 17 \b, Big-endian
+>212 byte 34 \b, VAX format
+>212 byte 68 \b, Little-endian
+>212 byte 85 \b, Convex native
+
+############################################################
+# X-Ray Area Detector images
+0 string R-AXIS4\ \ \ R-Axis Area Detector Image:
+>796 lelong <20 Little-endian, IP #%d,
+>>768 lelong >0 Size=%dx
+>>772 lelong >0 \b%d
+>796 belong <20 Big-endian, IP #%d,
+>>768 belong >0 Size=%dx
+>>772 belong >0 \b%d
+
+0 string RAXIS\ \ \ \ \ R-Axis Area Detector Image, Win32:
+>796 lelong <20 Little-endian, IP #%d,
+>>768 lelong >0 Size=%dx
+>>772 lelong >0 \b%d
+>796 belong <20 Big-endian, IP #%d,
+>>768 belong >0 Size=%dx
+>>772 belong >0 \b%d
+
+
+1028 string MMX\000\000\000\000\000\000\000\000\000\000\000\000\000 MAR Area Detector Image,
+>1072 ulong >1 Compressed(%d),
+>1100 ulong >1 %d headers,
+>1104 ulong >0 %d x
+>1108 ulong >0 %d,
+>1120 ulong >0 %d bits/pixel
+
+# Type: GEDCOM genealogical (family history) data
+# From: Giuseppe Bilotta
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/GEDCOM
+# https://en.wikipedia.org/wiki/GEDCOM
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/g/
+# ged.trid.xml ged-utf8.trid.xml ged-utf16.trid.xml
+# Note: called "GEDCOM Family History" by TrID and "Genealogical Data Communication (GEDCOM) Format" by DROID via PUID fmt/851
+0 search/1/c 0\ HEAD GEDCOM genealogy text
+#!:mime text/plain
+#!:mime application/x-gedcom
+# https://www.iana.org/assignments/media-types/text/vnd.familysearch.gedcom
+!:mime text/vnd.familysearch.gedcom
+!:ext ged
+# no gedcom sample found and ged suffix also used for other formats
+#!:ext ged/gedcom
+>&0 search 1\ GEDC
+>>&0 search 2\ VERS version
+# 4 5.0 5.3 5.4 5.5 5.5.1 5.5.5 5.6 7.0 or no version
+>>>&1 string >\0 %s
+# From: Phil Endecott <phil05@chezphil.org>
+# 0\040HEAD as UTF-16 big endian without BOM
+0 string \000\060\000\040\000\110\000\105\000\101\000\104 GEDCOM genealogy text
+!:mime text/vnd.familysearch.gedcom
+!:ext ged
+# look for VERS tag encoded as UTF-16 big endian
+>12 search/0x65 V\0E\0R\0S version
+# version like: 5.5.1
+>>&2 bestring16 x %s
+>>0 string x \b, UTF-16 (without BOM) big-endian text
+# 0\040HEAD as UTF-16 little endian without BOM
+0 string \060\000\040\000\110\000\105\000\101\000\104\000 GEDCOM genealogy text
+!:mime text/vnd.familysearch.gedcom
+!:ext ged
+# look for VERS tag encoded as UTF-16 lttle endian
+>12 search/0x65 V\0E\0R\0S version
+# version like: 5.5.1
+>>&3 lestring16 x %s
+>>2 string x \b, UTF-16 (without BOM) little-endian text
+# Note: UTF-16 with BOM variants already described above by first test as "GEDCOM genealogy text"
+# 0\040HEAD as UTF-16 big endian with BOM
+#0 string \376\377\000\060\000\040\000\110\000\105\000\101\000\104 GEDCOM data
+# 0\040HEAD as UTF-16 little endian with BOM
+#0 string \377\376\060\000\040\000\110\000\105\000\101\000\104\000 GEDCOM data
+
+# PDB: Protein Data Bank files
+# Adam Buchbinder <adam.buchbinder@gmail.com>
+#
+# https://www.wwpdb.org/documentation/format32/sect2.html
+# https://www.ch.ic.ac.uk/chemime/
+#
+# The PDB file format is fixed-field, 80 columns. From the spec:
+#
+# COLS DATA
+# 1 - 6 "HEADER"
+# 11 - 50 String(40)
+# 51 - 59 Date
+# 63 - 66 IDcode
+#
+# Thus, positions 7-10, 60-62 and 67-80 are spaces. The Date must be in the
+# format DD-MMM-YY, e.g., 01-JAN-70, and the IDcode consists of numbers and
+# uppercase letters. However, examples have been seen without the date string,
+# e.g., the example on the chemime site.
+0 string HEADER\ \ \ \040
+>&0 regex/1l \^.{40}
+>>&0 regex/1l [0-9]{2}-[A-Z]{3}-[0-9]{2}\ {3}
+>>>&0 regex/1ls [A-Z0-9]{4}.{14}$
+>>>>&0 regex/1l [A-Z0-9]{4} Protein Data Bank data, ID Code %s
+!:mime chemical/x-pdb
+>>>>0 regex/1l [0-9]{2}-[A-Z]{3}-[0-9]{2} \b, %s
+
+# Type: GDSII Stream file
+0 belong 0x00060002 GDSII Stream file
+>4 byte 0x00
+>>5 byte x version %d.0
+>4 byte >0x00 version %d
+>>5 byte x \b.%d
+
+# Type: LXT (interLaced eXtensible Trace)
+# chrysn <chrysn@fsfe.org>
+0 beshort 0x0138 interLaced eXtensible Trace (LXT) file
+>2 beshort >0 (Version %u)
diff --git a/contrib/libs/libmagic/magic/Magdir/securitycerts b/contrib/libs/libmagic/magic/Magdir/securitycerts
new file mode 100644
index 0000000000..8785dd883f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/securitycerts
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# $File: securitycerts,v 1.4 2009/09/19 16:28:12 christos Exp $
+0 search/1 -----BEGIN\ CERTIFICATE------ RFC1421 Security Certificate text
+0 search/1 -----BEGIN\ NEW\ CERTIFICATE RFC1421 Security Certificate Signing Request text
+0 belong 0xedfeedfe Sun 'jks' Java Keystore File data
diff --git a/contrib/libs/libmagic/magic/Magdir/selinux b/contrib/libs/libmagic/magic/Magdir/selinux
new file mode 100644
index 0000000000..89d5f53629
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/selinux
@@ -0,0 +1,24 @@
+# Type: SE Linux policy modules *.pp reference policy
+# for Fedora 5 to 9, RHEL5, and Debian Etch and Lenny.
+# URL: https://doc.coker.com.au/computers/selinux-magic
+# From: Russell Coker <russell@coker.com.au>
+
+0 lelong 0xf97cff8f SE Linux modular policy
+>4 lelong x version %d,
+>8 lelong x %d sections,
+>>(12.l) lelong 0xf97cff8d
+>>>(12.l+27) lelong x mod version %d,
+>>>(12.l+31) lelong 0 Not MLS,
+>>>(12.l+31) lelong 1 MLS,
+>>>(12.l+23) lelong 2
+>>>>(12.l+47) string >\0 module name %s
+>>>(12.l+23) lelong 1 base
+
+1 string policy_module( SE Linux policy module source
+2 string policy_module( SE Linux policy module source
+
+0 string ##\ <summary> SE Linux policy interface source
+
+#0 search gen_context( SE Linux policy file contexts
+
+#0 search gen_sens( SE Linux policy MLS constraints source
diff --git a/contrib/libs/libmagic/magic/Magdir/sendmail b/contrib/libs/libmagic/magic/Magdir/sendmail
new file mode 100644
index 0000000000..6808dbfd33
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sendmail
@@ -0,0 +1,37 @@
+
+#------------------------------------------------------------------------------
+# $File: sendmail,v 1.12 2022/10/31 13:22:26 christos Exp $
+# sendmail: file(1) magic for sendmail config files
+#
+# XXX - byte order?
+#
+# Update: Joerg Jenderek
+# GRR: this test is too general as it catches also
+# READ.ME.FIRST.AWP Sendmail frozen configuration
+# - version ====|====|====|====|====|====|====|====|====|====|====|====|===
+# Email_23_f217153422.ts Sendmail frozen configuration
+# - version \330jK\354
+0 byte 046
+# https://www.sendmail.com/sm/open_source/docs/older_release_notes/
+# freezed configuration file (dbm format?) created from sendmail.cf with -bz
+# by older sendmail. til version 8.6 support for frozen configuration files is removed
+# valid version numbers look like "7.14.4" and should be similar to output of commands
+# "sendmail -d0 -bt < /dev/null |grep -i Version" or "egrep '^DZ' /etc/sendmail.cf"
+>16 regex/s =^[0-78][0-9.]{4} Sendmail frozen configuration
+# normally only /etc/sendmail.fc or /var/adm/sendmail/sendmail.fc
+!:ext fc
+>>16 string >\0 - version %s
+0 short 0x271c
+# look for valid version number
+>16 regex/s =^[0-78][0-9.]{4} Sendmail frozen configuration
+!:ext fc
+>>16 string >\0 - version %s
+
+#------------------------------------------------------------------------------
+# sendmail: file(1) magic for sendmail m4(1) files
+#
+# From Hendrik Scholz <hendrik@scholz.net>
+# i.e. files in /usr/share/sendmail/cf/
+#
+0 string divert(-1)\n sendmail m4 text file
+
diff --git a/contrib/libs/libmagic/magic/Magdir/sequent b/contrib/libs/libmagic/magic/Magdir/sequent
new file mode 100644
index 0000000000..da38de65af
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sequent
@@ -0,0 +1,42 @@
+
+#------------------------------------------------------------------------------
+# $File: sequent,v 1.14 2019/04/19 00:42:27 christos Exp $
+# sequent: file(1) magic for Sequent machines
+#
+# Sequent information updated by Don Dwiggins <atsun!dwiggins>.
+# For Sequent's multiprocessor systems (incomplete).
+0 lelong 0x00ea BALANCE NS32000 .o
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 lelong 0x10ea BALANCE NS32000 executable (0 @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 lelong 0x20ea BALANCE NS32000 executable (invalid @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 lelong 0x30ea BALANCE NS32000 standalone executable
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+#
+# Symmetry information added by Jason Merrill <jason@jarthur.claremont.edu>.
+# Symmetry magic nums will not be reached if DOS COM comes before them;
+# byte 0xeb is matched before these get a chance.
+0 leshort 0x12eb SYMMETRY i386 .o
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 leshort 0x22eb SYMMETRY i386 executable (0 @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+0 leshort 0x32eb SYMMETRY i386 executable (invalid @ 0)
+>16 lelong >0 not stripped
+>124 lelong >0 version %d
+# https://en.wikipedia.org/wiki/Sequent_Computer_Systems
+# below test line conflicts with MS-DOS 2.11 floppies and Acronis loader
+#0 leshort 0x42eb SYMMETRY i386 standalone executable
+0 leshort 0x42eb
+# skip unlike negative version
+>124 lelong >-1
+# assuming version 28867614 is very low probable
+>>124 lelong !28867614 SYMMETRY i386 standalone executable
+>>>16 lelong >0 not stripped
+>>>124 lelong >0 version %d
diff --git a/contrib/libs/libmagic/magic/Magdir/sereal b/contrib/libs/libmagic/magic/Magdir/sereal
new file mode 100644
index 0000000000..ead78d5d35
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sereal
@@ -0,0 +1,35 @@
+
+#------------------------------------------------------------------------------
+# $File: sereal,v 1.3 2015/02/05 19:14:45 christos Exp $
+# sereal: file(1) magic the Sereal binary serialization format
+#
+# From: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+#
+# See the specification of the format at
+# https://github.com/Sereal/Sereal/blob/master/sereal_spec.pod#document-header-format
+#
+# I'd have liked to do the byte&0xF0 matching against 0, 1, 2 ... by
+# doing (byte&0xF0)>>4 here, but unfortunately that's not
+# supported. So when we print out a message about an unknown format
+# we'll print out e.g. 0x30 instead of the more human-readable
+# 0x30>>4.
+#
+# See https://github.com/Sereal/Sereal/commit/35372ae01d in the
+# Sereal.git repository for test Sereal data.
+0 name sereal
+>4 byte&0x0F x (version %d,
+>4 byte&0xF0 0x00 uncompressed)
+>4 byte&0xF0 0x10 compressed with non-incremental Snappy)
+>4 byte&0xF0 0x20 compressed with incremental Snappy)
+>4 byte&0xF0 >0x20 unknown subformat, flag: %d>>4)
+
+0 string/b \=srl Sereal data packet
+!:mime application/sereal
+>&0 use sereal
+0 string/b \=\xF3rl Sereal data packet
+!:mime application/sereal
+>&0 use sereal
+0 string/b \=\xC3\xB3rl Sereal data packet, UTF-8 encoded
+!:mime application/sereal
+>&0 use sereal
+
diff --git a/contrib/libs/libmagic/magic/Magdir/sgi b/contrib/libs/libmagic/magic/Magdir/sgi
new file mode 100644
index 0000000000..fe532e0010
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sgi
@@ -0,0 +1,144 @@
+
+#------------------------------------------------------------------------------
+# $File: sgi,v 1.24 2021/09/13 13:23:53 christos Exp $
+# sgi: file(1) magic for Silicon Graphics operating systems and applications
+#
+# Executable images are handled either in aout (for old-style a.out
+# files for 68K; they are indistinguishable from other big-endian 32-bit
+# a.out files) or in mips (for MIPS ECOFF and Ucode files)
+#
+
+# kbd file definitions
+0 string kbd!map kbd map file
+>8 byte >0 Ver %d:
+>10 short >0 with %d table(s)
+
+0 beshort 0x8765 disk quotas file
+
+0 beshort 0x0506 IRIS Showcase file
+>2 byte 0x49 -
+>3 byte x - version %d
+0 beshort 0x0226 IRIS Showcase template
+>2 byte 0x63 -
+>3 byte x - version %d
+0 belong 0x5343464d IRIS Showcase file
+>4 byte x - version %d
+0 belong 0x5443464d IRIS Showcase template
+>4 byte x - version %d
+0 belong 0xdeadbabe IRIX Parallel Arena
+>8 belong >0 - version %d
+
+# core files
+#
+# 32bit core file
+0 belong 0xdeadadb0 IRIX core dump
+>4 belong 1 of
+>16 string >\0 '%s'
+# 64bit core file
+0 belong 0xdeadad40 IRIX 64-bit core dump
+>4 belong 1 of
+>16 string >\0 '%s'
+# N32bit core file
+0 belong 0xbabec0bb IRIX N32 core dump
+>4 belong 1 of
+>16 string >\0 '%s'
+# New style crash dump file
+0 string \x43\x72\x73\x68\x44\x75\x6d\x70 IRIX vmcore dump of
+>36 string >\0 '%s'
+
+# Trusted IRIX info
+0 string SGIAUDIT SGI Audit file
+>8 byte x - version %d
+>9 byte x \b.%d
+#
+0 string WNGZWZSC Wingz compiled script
+0 string WNGZWZSS Wingz spreadsheet
+0 string WNGZWZHP Wingz help file
+#
+0 string #Inventor\040V IRIS Inventor 1.0 file
+0 string #Inventor\040V2 Open Inventor 2.0 file
+# GLF is OpenGL stream encoding
+0 string glfHeadMagic(); GLF_TEXT
+4 belong 0x7d000000 GLF_BINARY_LSB_FIRST
+!:strength -30
+4 belong 0x0000007d GLF_BINARY_MSB_FIRST
+!:strength -30
+# GLS is OpenGL stream encoding; GLS is the successor of GLF
+0 string glsBeginGLS( GLS_TEXT
+4 belong 0x10000000 GLS_BINARY_LSB_FIRST
+!:strength -30
+4 belong 0x00000010 GLS_BINARY_MSB_FIRST
+!:strength -30
+
+# Performance Co-Pilot file types
+0 string PmNs PCP compiled namespace (V.0)
+0 string PmN PCP compiled namespace
+>3 string >\0 (V.%1.1s)
+3 belong 0x84500526 PCP archive
+>7 byte x (V.%d)
+>20 belong -2 temporal index
+>20 belong -1 metadata
+>20 belong 0 log volume #0
+>20 belong >0 log volume #%d
+>24 string >\0 host: %s
+3 belong 0x28500526 PCP archive
+>7 byte x (V.%d)
+>24 belong -2 temporal index
+>24 belong -1 metadata
+>24 belong 0 log volume #0
+>24 belong >0 log volume #%d
+>36 string >\0 host: %s
+0 string PCPFolio PCP
+>9 string Version: Archive Folio
+>18 string >\0 (V.%s)
+0 string #pmchart PCP pmchart view
+>9 string Version
+>17 string >\0 (V%-3.3s)
+0 string #kmchart PCP pmchart view
+>9 string Version
+>17 string >\0 (V.%s)
+0 string pmview PCP pmview config
+>7 string Version
+>15 string >\0 (V%-3.3s)
+0 string #pmlogger PCP pmlogger config
+>10 string Version
+>18 string >\0 (V%1.1s)
+0 string #pmdahotproc PCP pmdahotproc config
+>13 string Version
+>21 string >\0 (V%-3.3s)
+0 string PcPh PCP Help
+>4 string 1 Index
+>4 string 2 Text
+>5 string >\0 (V.%1.1s)
+0 string #pmieconf-rules PCP pmieconf rules
+>16 string >\0 (V.%1.1s)
+3 string pmieconf-pmie PCP pmie config
+>17 string >\0 (V.%1.1s)
+0 string #pmlogconf-setup PCP pmlogconf config
+>17 string >\0 (V.%1.1s)
+1 string pmlogconf PCP pmlogger config
+>11 string >\0 (V.%1.1s)
+0 string MMV PCP memory mapped values
+>4 long x (V.%d)
+
+# SpeedShop data files
+0 lelong 0x13130303 SpeedShop data file
+
+# mdbm files
+0 lelong 0x01023962 mdbm file, version 0 (obsolete)
+0 string mdbm mdbm file,
+>5 byte x version %d,
+>6 byte x 2^%d pages,
+>7 byte x pagesize 2^%d,
+>17 byte x hash %d,
+>11 byte x dataformat %d
+
+# Alias Maya files
+0 string/t //Maya\040ASCII Alias Maya Ascii File,
+>13 string >\0 version %s
+8 string MAYAFOR4 Alias Maya Binary File,
+>32 string >\0 version %s scene
+8 string MayaFOR4 Alias Maya Binary File,
+>32 string >\0 version %s scene
+8 string CIMG Alias Maya Image File
+8 string DEEP Alias Maya Image File
diff --git a/contrib/libs/libmagic/magic/Magdir/sgml b/contrib/libs/libmagic/magic/Magdir/sgml
new file mode 100644
index 0000000000..9cd38e09b7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sgml
@@ -0,0 +1,163 @@
+
+#------------------------------------------------------------------------------
+# $File: sgml,v 1.48 2023/01/18 16:10:21 christos Exp $
+# Type: SVG Vectorial Graphics
+# From: Noel Torres <tecnico@ejerciciosresueltos.com>
+0 string \<?xml\ version=
+>14 regex ['"\ \t]*[0-9.]+['"\ \t]*
+>>19 search/4096 \<svg SVG Scalable Vector Graphics image
+!:strength + 50
+!:mime image/svg+xml
+!:ext svg
+>>19 search/4096 \<gnc-v2 GnuCash file
+!:mime application/x-gnucash
+0 string \<svg SVG Scalable Vector Graphics image
+!:strength + 50
+!:mime image/svg+xml
+!:ext svg
+
+# Sitemap file
+0 string/t \<?xml\ version=
+>14 regex ['"\ \t]*[0-9.]+['"\ \t]*
+>>19 search/4096 \<urlset XML Sitemap document text
+!:mime application/xml-sitemap
+
+# OpenStreetMap XML (.osm)
+# https://wiki.openstreetmap.org/wiki/OSM_XML
+# From: Markus Heidelberg <markus.heidelberg@web.de>
+0 string \<?xml\ version=
+>14 regex ['"\ \t]*[0-9.]+['"\ \t]*
+>>19 search/4096 \<osm OpenStreetMap XML data
+
+# xhtml
+0 string/t \<?xml\ version="
+>19 search/4096/cWbt \<!doctype\ html XHTML document text
+>>15 string >\0 (version %.3s)
+!:mime text/html
+0 string/t \<?xml\ version='
+>19 search/4096/cWbt \<!doctype\ html XHTML document text
+>>15 string >\0 (version %.3s)
+!:mime text/html
+0 string/t \<?xml\ version="
+>19 search/4096/cWbt \<html broken XHTML document text
+>>15 string >\0 (version %.3s)
+!:mime text/html
+
+#------------------------------------------------------------------------------
+# sgml: file(1) magic for Standard Generalized Markup Language
+# HyperText Markup Language (HTML) is an SGML document type,
+# from Daniel Quinlan (quinlan@yggdrasil.com)
+# adapted to string extensions by Anthon van der Neut <anthon@mnt.org)
+0 search/4096/cWt \<!doctype\ html HTML document text
+!:mime text/html
+!:strength + 5
+
+# avoid misdetection as JavaScript
+0 string/cWt \<!doctype\ html HTML document text
+!:mime text/html
+0 string/ct \<html> HTML document text
+!:mime text/html
+0 string/ct \<!--
+>&0 search/4096/cWt \<!doctype\ html HTML document text
+!:mime text/html
+>&0 search/4096/ct \<html> HTML document text
+!:mime text/html
+
+# SVG document
+# https://www.w3.org/TR/SVG/single-page.html
+0 search/4096/cWbt \<!doctype\ svg SVG XML document
+!:mime image/svg+xml
+!:strength + 15
+
+0 search/4096/cwt \<head\> HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cWt \<head\ HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cwt \<title\> HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cWt \<title\ HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cwt \<html\> HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cWt \<html\ HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cwt \<script\> HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cWt \<script\ HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cwt \<style\> HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cWt \<style\ HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cwt \<table\> HTML document text
+!:mime text/html
+!:strength + 15
+0 search/4096/cWt \<table\ HTML document text
+!:mime text/html
+!:strength + 15
+
+0 search/4096/cwt \<a\ href= HTML document text
+!:mime text/html
+!:strength + 15
+
+# Extensible markup language (XML), a subset of SGML
+# from Marc Prud'hommeaux (marc@apocalypse.org)
+0 search/1/cwt \<?xml XML document text
+!:mime text/xml
+!:strength + 15
+0 string/t \<?xml\ version\ " XML
+!:mime text/xml
+!:strength + 15
+0 string/t \<?xml\ version=" XML
+!:mime text/xml
+!:strength + 15
+>15 string/t >\0 %.3s document text
+>>23 search/1 \<xsl:stylesheet (XSL stylesheet)
+>>24 search/1 \<xsl:stylesheet (XSL stylesheet)
+0 string/t \<?xml\ version=' XML
+!:mime text/xml
+!:strength + 15
+>15 string/t >\0 %.3s document text
+>>23 search/1 \<xsl:stylesheet (XSL stylesheet)
+>>24 search/1 \<xsl:stylesheet (XSL stylesheet)
+0 search/1/wt \<?XML broken XML document text
+!:mime text/xml
+!:strength - 10
+
+
+# SGML, mostly from rph@sq
+0 search/4096/cwt \<!doctype exported SGML document text
+0 search/4096/cwt \<!subdoc exported SGML subdocument text
+0 search/4096/cwt \<!-- exported SGML document text
+!:strength - 10
+
+# Web browser cookie files
+# (Mozilla, Galeon, Netscape 4, Konqueror..)
+# Ulf Harnhammar <ulfh@update.uu.se>
+0 search/1 #\ HTTP\ Cookie\ File Web browser cookie text
+0 search/1 #\ Netscape\ HTTP\ Cookie\ File Netscape cookie text
+0 search/1 #\ KDE\ Cookie\ File Konqueror cookie text
+
+# XML-based format representing braille pages in a digital format.
+#
+# Specification:
+# http://files.pef-format.org/specifications/pef-2008-1/pef-specification.html
+#
+# Simon Aittamaa <simon.aittamaa@gmail.com>
+0 string \<?xml\ version=
+>14 regex ['"\ \t]*[0-9.]+['"\ \t]*
+>>19 search/4096 \<pef Portable Embosser Format
+!:mime application/x-pef+xml
+
+# https://www.qgis.org/en/site/
+0 string \<!DOCTYPE\040qgis QGIS XML document
diff --git a/contrib/libs/libmagic/magic/Magdir/sharc b/contrib/libs/libmagic/magic/Magdir/sharc
new file mode 100644
index 0000000000..e54088bc8f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sharc
@@ -0,0 +1,23 @@
+
+#------------------------------------------------------------------------
+# $File: sharc,v 1.8 2017/03/17 21:35:28 christos Exp $
+# file(1) magic for sharc files
+#
+# SHARC DSP, MIDI SysEx and RiscOS filetype definitions added by
+# FutureGroove Music (dsp@futuregroove.de)
+
+#------------------------------------------------------------------------
+#0 string Draw RiscOS Drawfile
+#0 string PACK RiscOS PackdDir archive
+
+#------------------------------------------------------------------------
+# SHARC DSP stuff (based on the FGM SHARC DSP SDK)
+
+#0 string =! Assembler source
+#0 string Analog ADi asm listing file
+0 string .SYSTEM SHARC architecture file
+0 string .system SHARC architecture file
+
+0 leshort 0x521C SHARC COFF binary
+>2 leshort >1 , %d sections
+>>12 lelong >0 , not stripped
diff --git a/contrib/libs/libmagic/magic/Magdir/sinclair b/contrib/libs/libmagic/magic/Magdir/sinclair
new file mode 100644
index 0000000000..608d779d7b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sinclair
@@ -0,0 +1,40 @@
+
+#------------------------------------------------------------------------------
+# $File: sinclair,v 1.7 2021/04/27 20:35:51 christos Exp $
+# sinclair: file(1) sinclair QL
+
+# additions to /etc/magic by Thomas M. Ott (ThMO)
+
+# Sinclair QL floppy disk formats (ThMO)
+0 string =QL5 QL disk dump data,
+>3 string =A 720 KB,
+>3 string =B 1.44 MB,
+>3 string =C 3.2 MB,
+>4 string >\0 label:%.10s
+
+# Sinclair QL OS dump (ThMO)
+0 belong =0x30000
+>49124 belong <47104
+>>49128 belong <47104
+>>>49132 belong <47104
+>>>>49136 belong <47104 QL OS dump data,
+>>>>>49148 string >\0 type %.3s,
+>>>>>49142 string >\0 version %.4s
+
+# Sinclair QL firmware executables (ThMO)
+0 string NqNqNq`\004 QL firmware executable (BCPL)
+
+# Sinclair QL libraries (was ThMO)
+0 beshort 0xFB01 QDOS object
+>2 pstring x '%s'
+
+# Sinclair QL executables (was ThMO)
+4 belong 0x4AFB QDOS executable
+>9 pstring x '%s'
+6 beshort 0x4AFB QDOS executable
+>9 pstring x '%s'
+
+# Sinclair QL ROM (ThMO)
+0 belong =0x4AFB0001 QL plugin-ROM data,
+>9 pstring =\0 un-named
+>9 pstring >\0 named: %s
diff --git a/contrib/libs/libmagic/magic/Magdir/sisu b/contrib/libs/libmagic/magic/Magdir/sisu
new file mode 100644
index 0000000000..ba7104fa16
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sisu
@@ -0,0 +1,18 @@
+# Type: SiSU Markup Language
+# URL: http://www.sisudoc.org/
+# From: Ralph Amissah <ralph.amissah@gmail.com>
+
+0 regex \^%?[\ \t]*SiSU[\ \t]+insert SiSU text insert
+>5 regex [0-9.]+ %s
+
+0 regex \^%[\ \t]+SiSU[\ \t]+master SiSU text master
+>5 regex [0-9.]+ %s
+
+0 regex \^%?[\ \t]*SiSU[\ \t]+text SiSU text
+>5 regex [0-9.]+ %s
+
+0 regex \^%?[\ \t]*SiSU[\ \t][0-9.]+ SiSU text
+>5 regex [0-9.]+ %s
+
+0 regex \^%*[\ \t]*sisu-[0-9.]+ SiSU text
+>5 regex [0-9.]+ %s
diff --git a/contrib/libs/libmagic/magic/Magdir/sketch b/contrib/libs/libmagic/magic/Magdir/sketch
new file mode 100644
index 0000000000..ee731ddd52
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sketch
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# $File: sketch,v 1.5 2017/03/17 21:35:28 christos Exp $
+# Sketch Drawings: http://sketch.sourceforge.net/
+# From: Edwin Mons <e@ik.nu>
+0 search/1 ##Sketch Sketch document text
diff --git a/contrib/libs/libmagic/magic/Magdir/smalltalk b/contrib/libs/libmagic/magic/Magdir/smalltalk
new file mode 100644
index 0000000000..9ff2c6b0c0
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/smalltalk
@@ -0,0 +1,25 @@
+
+#-----------------------------------------------
+# $File: smalltalk,v 1.5 2009/09/19 16:28:12 christos Exp $
+# GNU Smalltalk image, starting at version 1.6.2
+# From: catull_us@yahoo.com
+#
+0 string GSTIm\0\0 GNU SmallTalk
+# little-endian
+>7 byte&1 =0 LE image version
+>>10 byte x %d.
+>>9 byte x \b%d.
+>>8 byte x \b%d
+#>>12 lelong x , data: %ld
+#>>16 lelong x , table: %ld
+#>>20 lelong x , memory: %ld
+# big-endian
+>7 byte&1 =1 BE image version
+>>8 byte x %d.
+>>9 byte x \b%d.
+>>10 byte x \b%d
+#>>12 belong x , data: %ld
+#>>16 belong x , table: %ld
+#>>20 belong x , memory: %ld
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/smile b/contrib/libs/libmagic/magic/Magdir/smile
new file mode 100644
index 0000000000..d196de50d1
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/smile
@@ -0,0 +1,34 @@
+
+#------------------------------------------------------------------------------
+# $File: smile,v 1.1 2011/08/17 17:37:18 christos Exp $
+# smile: file(1) magic for Smile serialization
+#
+# The Smile serialization format uses a 4-byte header:
+#
+# Constant byte #0: 0x3A (ASCII ':')
+# Constant byte #1: 0x29 (ASCII ')')
+# Constant byte #2: 0x0A (ASCII linefeed, '\n')
+# Variable byte #3, consisting of bits:
+# Bits 4-7 (4 MSB): 4-bit version number
+# Bits 3: Reserved
+# Bit 2 (mask 0x04): Whether raw binary (unescaped 8-bit) values may be present in content
+# Bit 1 (mask 0x02): Whether shared String value checking was enabled during encoding, default false
+# Bit 0 (mask 0x01): Whether shared property name checking was enabled during encoding, default true
+#
+# Reference: http://wiki.fasterxml.com/SmileFormatSpec
+# Created by: Pierre-Alexandre Meyer <pierre@mouraf.org>
+
+# Detection
+0 string :)\n Smile binary data
+
+# Versioning
+>3 byte&0xF0 x version %d:
+
+# Properties
+>3 byte&0x04 0x04 binary raw,
+>3 byte&0x04 0x00 binary encoded,
+>3 byte&0x02 0x02 shared String values enabled,
+>3 byte&0x02 0x00 shared String values disabled,
+>3 byte&0x01 0x01 shared field names enabled
+>3 byte&0x01 0x00 shared field names disabled
+
diff --git a/contrib/libs/libmagic/magic/Magdir/sniffer b/contrib/libs/libmagic/magic/Magdir/sniffer
new file mode 100644
index 0000000000..751d197376
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sniffer
@@ -0,0 +1,482 @@
+
+#------------------------------------------------------------------------------
+# $File: sniffer,v 1.34 2022/12/14 18:27:36 christos Exp $
+# sniffer: file(1) magic for packet capture files
+#
+# From: guy@alum.mit.edu (Guy Harris)
+#
+
+#
+# Microsoft Network Monitor 1.x capture files.
+#
+0 string RTSS NetMon capture file
+>5 byte x - version %d
+>4 byte x \b.%d
+>6 leshort 0 (Unknown)
+>6 leshort 1 (Ethernet)
+>6 leshort 2 (Token Ring)
+>6 leshort 3 (FDDI)
+>6 leshort 4 (ATM)
+>6 leshort >4 (type %d)
+
+#
+# Microsoft Network Monitor 2.x capture files.
+#
+0 string GMBU NetMon capture file
+>5 byte x - version %d
+>4 byte x \b.%d
+>6 leshort 0 (Unknown)
+>6 leshort 1 (Ethernet)
+>6 leshort 2 (Token Ring)
+>6 leshort 3 (FDDI)
+>6 leshort 4 (ATM)
+>6 leshort 5 (IP-over-IEEE 1394)
+>6 leshort 6 (802.11)
+>6 leshort 7 (Raw IP)
+>6 leshort 8 (Raw IP)
+>6 leshort 9 (Raw IP)
+>6 leshort >9 (type %d)
+
+#
+# Network General Sniffer capture files.
+# Sorry, make that "Network Associates Sniffer capture files."
+# Sorry, make that "Network General old DOS Sniffer capture files."
+#
+0 string TRSNIFF\040data\040\040\040\040\032 Sniffer capture file
+>33 byte 2 (compressed)
+>23 leshort x - version %d
+>25 leshort x \b.%d
+>32 byte 0 (Token Ring)
+>32 byte 1 (Ethernet)
+>32 byte 2 (ARCNET)
+>32 byte 3 (StarLAN)
+>32 byte 4 (PC Network broadband)
+>32 byte 5 (LocalTalk)
+>32 byte 6 (Znet)
+>32 byte 7 (Internetwork Analyzer)
+>32 byte 9 (FDDI)
+>32 byte 10 (ATM)
+
+#
+# Cinco Networks NetXRay capture files.
+# Sorry, make that "Network General Sniffer Basic capture files."
+# Sorry, make that "Network Associates Sniffer Basic capture files."
+# Sorry, make that "Network Associates Sniffer Basic, and Windows
+# Sniffer Pro", capture files."
+# Sorry, make that "Network General Sniffer capture files."
+# Sorry, make that "NetScout Sniffer capture files."
+#
+0 string XCP\0 NetXRay capture file
+>4 string >\0 - version %s
+>44 leshort 0 (Ethernet)
+>44 leshort 1 (Token Ring)
+>44 leshort 2 (FDDI)
+>44 leshort 3 (WAN)
+>44 leshort 8 (ATM)
+>44 leshort 9 (802.11)
+
+#
+# "libpcap" capture files.
+# https://www.tcpdump.org/manpages/pcap-savefile.5.html
+# (We call them "tcpdump capture file(s)" for now, as "tcpdump" is
+# the main program that uses that format, but there are other programs
+# that use "libpcap", or that use the same capture file format.)
+#
+0 name pcap-be
+>4 beshort x - version %d
+>6 beshort x \b.%d
+# clear that continuation level match
+>20 clear x
+>20 belong&0x03FFFFFF 0 (No link-layer encapsulation
+>20 belong&0x03FFFFFF 1 (Ethernet
+>20 belong&0x03FFFFFF 2 (3Mb Ethernet
+>20 belong&0x03FFFFFF 3 (AX.25
+>20 belong&0x03FFFFFF 4 (ProNET
+>20 belong&0x03FFFFFF 5 (CHAOS
+>20 belong&0x03FFFFFF 6 (Token Ring
+>20 belong&0x03FFFFFF 7 (BSD ARCNET
+>20 belong&0x03FFFFFF 8 (SLIP
+>20 belong&0x03FFFFFF 9 (PPP
+>20 belong&0x03FFFFFF 10 (FDDI
+>20 belong&0x03FFFFFF 11 (RFC 1483 ATM
+>20 belong&0x03FFFFFF 12 (Raw IP
+>20 belong&0x03FFFFFF 13 (BSD/OS SLIP
+>20 belong&0x03FFFFFF 14 (BSD/OS PPP
+>20 belong&0x03FFFFFF 19 (Linux ATM Classical IP
+>20 belong&0x03FFFFFF 50 (PPP or Cisco HDLC
+>20 belong&0x03FFFFFF 51 (PPP-over-Ethernet
+>20 belong&0x03FFFFFF 99 (Symantec Enterprise Firewall
+>20 belong&0x03FFFFFF 100 (RFC 1483 ATM
+>20 belong&0x03FFFFFF 101 (Raw IP
+>20 belong&0x03FFFFFF 102 (BSD/OS SLIP
+>20 belong&0x03FFFFFF 103 (BSD/OS PPP
+>20 belong&0x03FFFFFF 104 (BSD/OS Cisco HDLC
+>20 belong&0x03FFFFFF 105 (802.11
+>20 belong&0x03FFFFFF 106 (Linux Classical IP over ATM
+>20 belong&0x03FFFFFF 107 (Frame Relay
+>20 belong&0x03FFFFFF 108 (OpenBSD loopback
+>20 belong&0x03FFFFFF 109 (OpenBSD IPsec encrypted
+>20 belong&0x03FFFFFF 112 (Cisco HDLC
+>20 belong&0x03FFFFFF 113 (Linux cooked v1
+>20 belong&0x03FFFFFF 114 (LocalTalk
+>20 belong&0x03FFFFFF 117 (OpenBSD PFLOG
+>20 belong&0x03FFFFFF 119 (802.11 with Prism header
+>20 belong&0x03FFFFFF 122 (RFC 2625 IP over Fibre Channel
+>20 belong&0x03FFFFFF 123 (SunATM
+>20 belong&0x03FFFFFF 127 (802.11 with radiotap header
+>20 belong&0x03FFFFFF 129 (Linux ARCNET
+>20 belong&0x03FFFFFF 130 (Juniper Multi-Link PPP
+>20 belong&0x03FFFFFF 131 (Juniper Multi-Link Frame Relay
+>20 belong&0x03FFFFFF 132 (Juniper Encryption Services PIC
+>20 belong&0x03FFFFFF 133 (Juniper GGSN PIC
+>20 belong&0x03FFFFFF 134 (Juniper FRF.16 Frame Relay
+>20 belong&0x03FFFFFF 135 (Juniper ATM2 PIC
+>20 belong&0x03FFFFFF 136 (Juniper Advanced Services PIC
+>20 belong&0x03FFFFFF 137 (Juniper ATM1 PIC
+>20 belong&0x03FFFFFF 138 (Apple IP over IEEE 1394
+>20 belong&0x03FFFFFF 139 (SS7 MTP2 with pseudo-header
+>20 belong&0x03FFFFFF 140 (SS7 MTP2
+>20 belong&0x03FFFFFF 141 (SS7 MTP3
+>20 belong&0x03FFFFFF 142 (SS7 SCCP
+>20 belong&0x03FFFFFF 143 (DOCSIS
+>20 belong&0x03FFFFFF 144 (Linux IrDA
+>20 belong&0x03FFFFFF 147 (Private use 0
+>20 belong&0x03FFFFFF 148 (Private use 1
+>20 belong&0x03FFFFFF 149 (Private use 2
+>20 belong&0x03FFFFFF 150 (Private use 3
+>20 belong&0x03FFFFFF 151 (Private use 4
+>20 belong&0x03FFFFFF 152 (Private use 5
+>20 belong&0x03FFFFFF 153 (Private use 6
+>20 belong&0x03FFFFFF 154 (Private use 7
+>20 belong&0x03FFFFFF 155 (Private use 8
+>20 belong&0x03FFFFFF 156 (Private use 9
+>20 belong&0x03FFFFFF 157 (Private use 10
+>20 belong&0x03FFFFFF 158 (Private use 11
+>20 belong&0x03FFFFFF 159 (Private use 12
+>20 belong&0x03FFFFFF 160 (Private use 13
+>20 belong&0x03FFFFFF 161 (Private use 14
+>20 belong&0x03FFFFFF 162 (Private use 15
+>20 belong&0x03FFFFFF 163 (802.11 with AVS header
+>20 belong&0x03FFFFFF 164 (Juniper Passive Monitor PIC
+>20 belong&0x03FFFFFF 165 (BACnet MS/TP
+>20 belong&0x03FFFFFF 166 (PPPD
+>20 belong&0x03FFFFFF 167 (Juniper PPPoE
+>20 belong&0x03FFFFFF 168 (Juniper PPPoE/ATM
+>20 belong&0x03FFFFFF 169 (GPRS LLC
+>20 belong&0x03FFFFFF 170 (GPF-T
+>20 belong&0x03FFFFFF 171 (GPF-F
+>20 belong&0x03FFFFFF 174 (Juniper PIC Peer
+>20 belong&0x03FFFFFF 175 (Ethernet with Endace ERF header
+>20 belong&0x03FFFFFF 176 (Packet-over-SONET with Endace ERF header
+>20 belong&0x03FFFFFF 177 (Linux LAPD
+>20 belong&0x03FFFFFF 178 (Juniper Ethernet
+>20 belong&0x03FFFFFF 179 (Juniper PPP
+>20 belong&0x03FFFFFF 180 (Juniper Frame Relay
+>20 belong&0x03FFFFFF 181 (Juniper C-HDLC
+>20 belong&0x03FFFFFF 182 (FRF.16 Frame Relay
+>20 belong&0x03FFFFFF 183 (Juniper Voice PIC
+>20 belong&0x03FFFFFF 184 (Arinc 429
+>20 belong&0x03FFFFFF 185 (Arinc 653 Interpartition Communication
+>20 belong&0x03FFFFFF 186 (USB with FreeBSD header
+>20 belong&0x03FFFFFF 187 (Bluetooth HCI H4
+>20 belong&0x03FFFFFF 188 (802.16 MAC Common Part Sublayer
+>20 belong&0x03FFFFFF 189 (Linux USB
+>20 belong&0x03FFFFFF 190 (Controller Area Network (CAN) v. 2.0B
+>20 belong&0x03FFFFFF 191 (802.15.4 with Linux padding
+>20 belong&0x03FFFFFF 192 (PPI
+>20 belong&0x03FFFFFF 193 (802.16 MAC Common Part Sublayer plus radiotap header
+>20 belong&0x03FFFFFF 194 (Juniper Integrated Service Module
+>20 belong&0x03FFFFFF 195 (802.15.4 with FCS
+>20 belong&0x03FFFFFF 196 (SITA
+>20 belong&0x03FFFFFF 197 (Endace ERF
+>20 belong&0x03FFFFFF 198 (Ethernet with u10 Networks pseudo-header
+>20 belong&0x03FFFFFF 199 (IPMB
+>20 belong&0x03FFFFFF 200 (Juniper Secure Tunnel
+>20 belong&0x03FFFFFF 201 (Bluetooth HCI H4 with pseudo-header
+>20 belong&0x03FFFFFF 202 (AX.25 with KISS header
+>20 belong&0x03FFFFFF 203 (LAPD
+>20 belong&0x03FFFFFF 204 (PPP with direction pseudo-header
+>20 belong&0x03FFFFFF 205 (Cisco HDLC with direction pseudo-header
+>20 belong&0x03FFFFFF 206 (Frame Relay with direction pseudo-header
+>20 belong&0x03FFFFFF 209 (Linux IPMB
+>20 belong&0x03FFFFFF 215 (802.15.4 with non-ASK PHY header
+>20 belong&0x03FFFFFF 216 (Linux evdev events
+>20 belong&0x03FFFFFF 219 (MPLS with label as link-layer header
+>20 belong&0x03FFFFFF 220 (Memory-mapped Linux USB
+>20 belong&0x03FFFFFF 221 (DECT
+>20 belong&0x03FFFFFF 222 (AOS Space Data Link protocol
+>20 belong&0x03FFFFFF 223 (Wireless HART
+>20 belong&0x03FFFFFF 224 (Fibre Channel FC-2
+>20 belong&0x03FFFFFF 225 (Fibre Channel FC-2 with frame delimiters
+>20 belong&0x03FFFFFF 226 (Solaris IPNET
+>20 belong&0x03FFFFFF 227 (SocketCAN
+>20 belong&0x03FFFFFF 228 (Raw IPv4
+>20 belong&0x03FFFFFF 229 (Raw IPv6
+>20 belong&0x03FFFFFF 230 (802.15.4 without FCS
+>20 belong&0x03FFFFFF 231 (D-Bus messages
+>20 belong&0x03FFFFFF 232 (Juniper Virtual Server
+>20 belong&0x03FFFFFF 233 (Juniper SRX E2E
+>20 belong&0x03FFFFFF 234 (Juniper Fibre Channel
+>20 belong&0x03FFFFFF 235 (DVB-CI
+>20 belong&0x03FFFFFF 236 (MUX27010
+>20 belong&0x03FFFFFF 237 (STANAG 5066 D_PDUs
+>20 belong&0x03FFFFFF 238 (Juniper ATM CEMIC
+>20 belong&0x03FFFFFF 239 (Linux netfilter log messages
+>20 belong&0x03FFFFFF 240 (Hilscher netAnalyzer
+>20 belong&0x03FFFFFF 241 (Hilscher netAnalyzer with delimiters
+>20 belong&0x03FFFFFF 242 (IP-over-Infiniband
+>20 belong&0x03FFFFFF 243 (MPEG-2 Transport Stream packets
+>20 belong&0x03FFFFFF 244 (ng4t ng40
+>20 belong&0x03FFFFFF 245 (NFC LLCP
+>20 belong&0x03FFFFFF 246 (Packet filter state syncing
+>20 belong&0x03FFFFFF 247 (InfiniBand
+>20 belong&0x03FFFFFF 248 (SCTP
+>20 belong&0x03FFFFFF 249 (USB with USBPcap header
+>20 belong&0x03FFFFFF 250 (Schweitzer Engineering Laboratories RTAC packets
+>20 belong&0x03FFFFFF 251 (Bluetooth Low Energy air interface
+>20 belong&0x03FFFFFF 252 (Wireshark Upper PDU export
+>20 belong&0x03FFFFFF 253 (Linux netlink
+>20 belong&0x03FFFFFF 254 (Bluetooth Linux Monitor
+>20 belong&0x03FFFFFF 255 (Bluetooth Basic Rate/Enhanced Data Rate baseband packets
+>20 belong&0x03FFFFFF 256 (Bluetooth Low Energy air interface with pseudo-header
+>20 belong&0x03FFFFFF 257 (PROFIBUS data link layer
+>20 belong&0x03FFFFFF 258 (Apple DLT_PKTAP
+>20 belong&0x03FFFFFF 259 (Ethernet with 802.3 Clause 65 EPON preamble
+>20 belong&0x03FFFFFF 260 (IPMI trace packets
+>20 belong&0x03FFFFFF 261 (Z-Wave RF profile R1 and R2 packets
+>20 belong&0x03FFFFFF 262 (Z-Wave RF profile R3 packets
+>20 belong&0x03FFFFFF 263 (WattStopper Digital Lighting Mngmt/Legrand Nitoo Open Proto
+>20 belong&0x03FFFFFF 264 (ISO 14443 messages
+>20 belong&0x03FFFFFF 265 (IEC 62106 Radio Data System groups
+>20 belong&0x03FFFFFF 266 (USB with Darwin header
+>20 belong&0x03FFFFFF 267 (OpenBSD DLT_OPENFLOW
+>20 belong&0x03FFFFFF 268 (IBM SDLC frames
+>20 belong&0x03FFFFFF 269 (TI LLN sniffer frames
+>20 belong&0x03FFFFFF 271 (Linux vsock
+>20 belong&0x03FFFFFF 272 (Nordic Semiconductor Bluetooth LE sniffer frames
+>20 belong&0x03FFFFFF 273 (Excentis XRA-31 DOCSIS 3.1 RF sniffer frames
+>20 belong&0x03FFFFFF 274 (802.3br mPackets
+>20 belong&0x03FFFFFF 275 (DisplayPort AUX channel monitoring data
+>20 belong&0x03FFFFFF 276 (Linux cooked v2
+>20 belong&0x03FFFFFF 278 (OpenVizsla USB
+>20 belong&0x03FFFFFF 279 (Elektrobit High Speed Capture and Replay (EBHSCR)
+>20 belong&0x03FFFFFF 281 (Broadcom tag
+>20 belong&0x03FFFFFF 282 (Broadcom tag (prepended)
+>20 belong&0x03FFFFFF 283 (802.15.4 with TAP
+>20 belong&0x03FFFFFF 284 (Marvell DSA
+>20 belong&0x03FFFFFF 285 (Marvell EDSA
+>20 belong&0x03FFFFFF 286 (ELEE lawful intercept
+>20 belong&0x03FFFFFF 287 (Z-Wave serial
+>20 belong&0x03FFFFFF 288 (USB 2.0
+>20 belong&0x03FFFFFF 289 (ATSC ALP
+>20 belong&0x03FFFFFF 290 (Event Tracing for Windows
+>20 belong&0x03FFFFFF 291 (Hilscher netANALYZER NG pseudo-footer
+>20 belong&0x03FFFFFF 292 (ZBOSS NCP protocol with pseudo-header
+>20 belong&0x03FFFFFF 293 (Low-Speed USB 2.0/1.1/1.0
+>20 belong&0x03FFFFFF 294 (Full-Speed USB 2.0/1.1/1.0
+>20 belong&0x03FFFFFF 295 (High-Speed USB 2.0
+# print default match
+>20 default x
+>>20 belong x (linktype#%u
+>16 belong x \b, capture length %u)
+
+# packets time stamps in seconds and microseconds.
+0 ubelong 0xa1b2c3d4 pcap capture file, microseconds ts (big-endian)
+!:mime application/vnd.tcpdump.pcap
+>0 use pcap-be
+0 ulelong 0xa1b2c3d4 pcap capture file, microsecond ts (little-endian)
+!:mime application/vnd.tcpdump.pcap
+>0 use \^pcap-be
+
+# packets time stamps in seconds and nanoseconds.
+0 ubelong 0xa1b23c4d pcap capture file, nanosecond ts (big-endian)
+!:mime application/vnd.tcpdump.pcap
+>0 use pcap-be
+0 ulelong 0xa1b23c4d pcap capture file, nanosecond ts (little-endian)
+!:mime application/vnd.tcpdump.pcap
+>0 use \^pcap-be
+
+#
+# "libpcap"-with-Alexey-Kuznetsov's-patches capture files.
+#
+0 ubelong 0xa1b2cd34 pcap capture file, microsecond ts, extensions (big-endian)
+>0 use pcap-be
+0 ulelong 0xa1b2cd34 pcap capture file, microsecond ts, extensions (little-endian)
+>0 use \^pcap-be
+
+#
+# "pcapng" capture files.
+# https://github.com/pcapng/pcapng
+# Pcapng files can contain multiple sections. Printing the endianness,
+# snaplen, or other information from the first SHB may be misleading.
+#
+0 ubelong 0x0a0d0d0a
+>8 ubelong 0x1a2b3c4d pcapng capture file
+>>12 beshort x - version %d
+>>14 beshort x \b.%d
+0 ulelong 0x0a0d0d0a
+>8 ulelong 0x1a2b3c4d pcapng capture file
+>>12 leshort x - version %d
+>>14 leshort x \b.%d
+
+#
+# AIX "iptrace" capture files.
+#
+0 string iptrace\0401.0 AIX iptrace capture file
+0 string iptrace\0402.0 AIX iptrace capture file
+
+#
+# Novell LANalyzer capture files.
+# URL: http://www.blacksheepnetworks.com/security/info/nw/lan/trace.txt
+# Reference: https://github.com/wireshark/wireshark/blob/master/wiretap/lanalyzer.c
+# Update: Joerg Jenderek
+#
+# regular trace header record (RT_HeaderRegular)
+0 leshort 0x1001
+# GRR: line above is too generic because it matches Commodore Plus/4 BASIC V3.5
+# and VIC-20 BASIC V2 program
+# skip many Commodore Basic program (Microzodiac.prg Minefield.prg Vic-tac-toe.prg breakvic_joy.prg)
+# with invalid second record type 0 instead of "Trace receive channel name record"
+>(2.s+4) leshort =0x1006h
+>>0 use novell-lanalyzer
+# cyclic trace header record (RT_HeaderCyclic)
+0 leshort 0x1007
+>0 use novell-lanalyzer
+0 name novell-lanalyzer
+>0 leshort x Novell LANalyzer capture file
+# https://reposcope.com/mimetype/application/x-lanalyzer
+!:mime application/x-lanalyzer
+# maybe also TR2 .. TR9 TRA .. TRZ
+!:ext tr1
+# version like: 1.5
+>4 ubyte x \b, version %u
+# minor version; one byte identifying the trace file minor version number
+>5 ubyte x \b.%u
+# Trace header record type like: 1001~regular or 1007~cyclic
+>0 leshort !0x1001 \b, record type %4.4x
+# record_length[2] is the length of the data part of 1st reorcd (without "type" and "length" fields) like: 4Ch
+>2 leshort x \b, record length %#x
+# second record type like: 1006h~Trace receive channel name record
+>(2.s+4) leshort !0x1006h \b, 2nd record type %#4.4x
+>(2.s+6) leshort x \b, 2nd record length %#x
+# each channel name is a null-terminated, eight-byte ASCII string like: Channel1
+>(2.s+8) string x \b, names %.9s
+# 2nd channel name like: Channel2
+>(2.s+17) string x %.9s ...
+
+#
+# HP-UX "nettl" capture files.
+# URL: https://nixdoc.net/man-pages/HP-UX/man1m/nettl.1m.html
+# Reference: https://github.com/wireshark/wireshark/blob/master/wiretap/nettl.c
+# Update: Joerg Jenderek
+# Note: Wireshark fills "meta information header fields" with "dummy" values
+# nettl_magic_hpux9[12]; for HP-UX 9.x not tested
+0 string \x00\x00\x00\x01\x00\x00\x00\x00\x00\x07\xD0\x00 HP/UX 9.x nettl capture file
+!:mime application/x-nettl
+!:ext trc0/trc1
+# nettl_magic_hpux10[12]; for HP-UX 10.x and 11.x
+0 string \x54\x52\x00\x64\x00 HP/UX nettl capture file
+# https://reposcope.com/mimetype/application/x-nettl
+!:mime application/x-nettl
+# maybe also TRC000 TRC001 TRC002 ...
+!:ext trc0/trc1
+# file_name[56]; maybe also like /tmp/raw.tr.TRC000
+>12 string !/tmp/wireshark.TRC000
+>>12 string x "%-.56s"
+# tz[20]; like UTC
+>68 string !UTC \b, tz
+>>68 string x %-.20s
+# host_name[9];
+>88 string >\0 \b, host %-.9s
+# os_vers[9]; like B.11.11
+>97 string !B.11.11 \b, os
+>>97 string x %-.9s
+# os_v; like 55h
+>>106 ubyte x (%#x)
+# xxa[8]; like 0
+>107 ubequad !0 \b, xxa=%#16.16llx
+# model[11] like: 9000/800
+>115 string !9000/800 \b, model
+>>115 string x %-.11s
+# unknown; probably just padding to 128 bytes like: 0406h
+>126 ubeshort !0x0406h \b, at 126 %#4.4x
+
+#
+# RADCOM WAN/LAN Analyzer capture files.
+#
+0 string \x42\xd2\x00\x34\x12\x66\x22\x88 RADCOM WAN/LAN Analyzer capture file
+
+#
+# NetStumbler log files. Not really packets, per se, but about as
+# close as you can get. These are log files from NetStumbler, a
+# Windows program, that scans for 802.11b networks.
+#
+0 string NetS NetStumbler log file
+>8 lelong x \b, %d stations found
+
+#
+# *Peek tagged capture files.
+#
+0 string \177ver EtherPeek/AiroPeek/OmniPeek capture file
+
+#
+# Visual Networks traffic capture files.
+#
+0 string \x05VNF Visual Networks traffic capture file
+
+#
+# Network Instruments Observer capture files.
+#
+0 string ObserverPktBuffe Network Instruments Observer capture file
+
+#
+# Files from Accellent Group's 5View products.
+#
+# URL: http://www.infovista.com
+# Reference: http://mark0.net/download/triddefs_xml.7z
+# defs/0/5vw.trid.xml
+# https://2.na.dl.wireshark.org/src/wireshark-3.6.2.tar.xz
+# wireshark-3.6.2/wiretap/5views.c
+# Update: Joerg Jenderek
+# Note: called "5View capture" by TrID and
+# "Wireshark capture file" on Windows or
+# "Packet Capture (Accellent/InfoVista 5view)" by shared MIME-info database
+# verified/falsified by `wireshark *.5vw`
+0 string \xaa\xaa\xaa\xaa
+# skip misidentified boot/x86_64/loader/kroete.dat on Suse LEAP DVD
+# by check for valid record version
+>8 ulelong =0x00010000
+>>0 use 5view-le
+0 name 5view-le
+# t_5VW_Info_Header.Signature = CST_5VW_INFO_HEADER_KEY = 0xAAAAAAAAU
+>0 ulelong x 5View capture file
+# https://reposcope.com/mimetype/application/x-5view
+!:mime application/x-5view
+!:ext 5vw
+# size of header in bytes (included signature and reserved fields); probably always 20h
+>4 ulelong !0x00000020 \b, header size %#x
+# version of header record; apparently always CST_5VW_INFO_RECORD_VERSION=0x00010000U
+>8 ulelong !0x00010000 \b, record version %#x
+# DataSize; total size of data without header like: 18h
+>12 ulelong x \b, record size %#x
+# filetype; type of the capture file like: 18001000h
+>16 ulelong x \b, file type %#8.8x
+# Reserved[3]; reserved for future use; apparently zero
+>20 quad !0 \b, Reserved %#llx
+# look for record header key CST_5VW_RECORDS_HEADER_KEY of structure t_5VW_TimeStamped_Header
+>0x20 search/0xB8/b \xEE\xEE\x33\x33 \b; record
+# HeaderSize; actual size of this header in bytes like: 32 24h
+>>&0 uleshort x size %#x
+# HeaderType; exact type of this header; probably always 0x4000
+>>&2 uleshort !0x4000 \b, header type %#x
+# RecType; type of record like: 80000000h
+>>&4 ulelong x \b, record type %#x
+# RecSubType; subtype of record like: 0
+>>&8 ulelong !0 \b, subtype %#x
+# RecSize; Size of one record like: 5Ch
+>>&12 ulelong x \b, RecSize %#x
+# RecNb; Number of records like: 1
+>>&16 ulelong >1 \b, %#x records
+# Timestamp Utc
+#>>&20 ulelong x \b, RAW TIME %#8.8x
+>>&20 date x \b, Time-stamp %s
diff --git a/contrib/libs/libmagic/magic/Magdir/softquad b/contrib/libs/libmagic/magic/Magdir/softquad
new file mode 100644
index 0000000000..28f03b9b78
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/softquad
@@ -0,0 +1,40 @@
+
+#------------------------------------------------------------------------------
+# $File: softquad,v 1.14 2022/10/28 17:19:54 christos Exp $
+# softquad: file(1) magic for SoftQuad Publishing Software
+# URL: https://en.wikipedia.org/wiki/SoftQuad_Software
+#
+# Author/Editor and RulesBuilder
+#
+# XXX - byte order?
+#
+0 string \<!SQ\ DTD> Compiled SGML rules file
+>9 string >\0 Type %s
+0 string \<!SQ\ A/E> A/E SGML Document binary
+>9 string >\0 Type %s
+0 string \<!SQ\ STS> A/E SGML binary styles file
+>9 string >\0 Type %s
+0 short 0xc0de Compiled PSI (v1) data
+0 short 0xc0da Compiled PSI (v2) data
+>3 string >\0 (%s)
+# Binary sqtroff font/desc files...
+# GRR: the line below is also true for 5View capture file handled by ./sniffer
+0 short 0125252
+# skip 5View capture file with "invalid" version AAAAh
+>2 short >0 SoftQuad DESC or font file binary - version %d
+# Bitmaps...
+0 search/1 SQ\ BITMAP1 SoftQuad Raster Format text
+#0 string SQ\ BITMAP2 SoftQuad Raster Format data
+# sqtroff intermediate language (replacement for ditroff int. lang.)
+0 string X\ SoftQuad troff Context intermediate
+>2 string 495 for AT&T 495 laser printer
+>2 string hp for Hewlett-Packard LaserJet
+>2 string impr for IMAGEN imPRESS
+>2 string ps for PostScript
+
+# From: Michael Piefel <piefel@debian.org>
+# sqtroff intermediate language (replacement for ditroff int. lang.)
+0 string X\ 495 SoftQuad troff Context intermediate for AT&T 495 laser printer
+0 string X\ hp SoftQuad troff Context intermediate for HP LaserJet
+0 string X\ impr SoftQuad troff Context intermediate for IMAGEN imPRESS
+0 string X\ ps SoftQuad troff Context intermediate for PostScript
diff --git a/contrib/libs/libmagic/magic/Magdir/sosi b/contrib/libs/libmagic/magic/Magdir/sosi
new file mode 100644
index 0000000000..88ecc512ba
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sosi
@@ -0,0 +1,40 @@
+
+#------------------------------------------------------------------------------
+# $File: sosi,v 1.2 2021/02/23 00:51:10 christos Exp $
+# SOSI
+# Summary: Systematic Organization of Spatial Information
+# Long description: Norwegian text based map format
+# File extension: .sos
+# Full name: Petter Reinholdtsen (pere@hungry.com)
+# Reference: https://en.wikipedia.org/wiki/SOSI
+#
+# Example SOSI files available from
+# https://trac.osgeo.org/gdal/ticket/3638
+# https://nedlasting.geonorge.no/geonorge/Basisdata/N50Kartdata/SOSI/
+# https://nedlasting.geonorge.no/geonorge/Samferdsel/Elveg/SOSI/
+#
+# Start with optional comments (from "!" to the next line end)
+# followed by ".HODE" and end with "\n.SLUTT" followed by an optional
+# separator (any number of " ", "\t", "\n" or "\r"), might have BOM at
+# the start and following ".HODE" near the start there is "..OMR=C3=85DE"
+# (either UTF-8, ISO-8859-1 or some 7 bit Norwegian charset based on
+# ASCII) , "..TRANSPAR", "..TEGNSETT " followed by the charset and a
+# separator, as well as "..SOSI-VERSJON " followed by the format
+# version and a separator.
+#
+# FIXME figure out how to accept any of [space], [tab], [newline] and
+# [carriage return] as separators, not only line end.
+
+# Not searching for full "OMR=C3=85DE" to match also for non-UTF-8
+# character sets
+0 search ..OMR
+>0 search ..TRANSPAR
+>>0 search .HODE SOSI map data
+>>>&0 search ..SOSI-VERSJON
+>>>>&1 string x \b, version %s
+# FIXME could not figure out way to make a match for .SLUTT at the end required
+#>-7 string \n.SLUTT slutt
+#>-8 string \n.SLUTT\n slutt-nl
+#>-9 string \n.SLUTT\r\n slutt-crnl2
+!:mime text/vnd.sosi
+!:ext sos
diff --git a/contrib/libs/libmagic/magic/Magdir/spec b/contrib/libs/libmagic/magic/Magdir/spec
new file mode 100644
index 0000000000..c504b1fd19
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/spec
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# $File: spec,v 1.4 2009/09/19 16:28:12 christos Exp $
+# spec: file(1) magic for SPEC raw results (*.raw, *.rsf)
+#
+# Cloyce D. Spradling <cloyce@headgear.org>
+
+0 string spec SPEC
+>4 string .cpu CPU
+>>8 string <: \b%.4s
+>>12 string . raw result text
+
+17 string version=SPECjbb SPECjbb
+>32 string <: \b%.4s
+>>37 string <: v%.4s raw result text
+
+0 string BEGIN\040SPECWEB SPECweb
+>13 string <: \b%.2s
+>>15 string _SSL \b_SSL
+>>>20 string <: v%.4s raw result text
+>>16 string <: v%.4s raw result text
diff --git a/contrib/libs/libmagic/magic/Magdir/spectrum b/contrib/libs/libmagic/magic/Magdir/spectrum
new file mode 100644
index 0000000000..cf14551b4d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/spectrum
@@ -0,0 +1,184 @@
+
+#------------------------------------------------------------------------------
+# $File: spectrum,v 1.10 2023/05/08 01:33:36 christos Exp $
+# spectrum: file(1) magic for Spectrum emulator files.
+#
+# John Elliott <jce@seasip.demon.co.uk>
+
+#
+# Spectrum +3DOS header
+#
+0 string PLUS3DOS\032 Spectrum +3 data
+>15 byte 0 - BASIC program
+>15 byte 1 - number array
+>15 byte 2 - character array
+>15 byte 3 - memory block
+>>16 belong 0x001B0040 (screen)
+>15 byte 4 - Tasword document
+>15 string TAPEFILE - ZXT tapefile
+#
+# Tape file. This assumes the .TAP starts with a Spectrum-format header,
+# which nearly all will.
+#
+# Update: Sanity-check string contents to be printable.
+# -Adam Buchbinder <adam.buchbinder@gmail.com>
+# Update: Joerg Jenderek 2023 May
+# URL: http://fileformats.archiveteam.org/wiki/TAP_(ZX_Spectrum)
+# Reference: http://web.archive.org/web/20110711141601/http://www.zxmodules.de/fileformats/tapformat.html
+# http://mark0.net/download/triddefs_xml.7z/defs/t/tap-zx.trid.xml
+# Note: called "ZX Spectrum Tape image" by TrID and "TAP (ZX Spectrum)" by DROID via PUID fmt/801
+# verified by fuse-emulator-utils `tzxlist EXAMPLES.TAP`
+#
+# headers length 19=023 and flag byte 0 indicating a standard ROM loading header
+0 string \023\000\000
+>4 string >\0
+# skip {85CEE8D6-0F90-4492-B484-98E38862B28D}.2.ver0x0000000000000004.db {DDF571F2-BE98-426D-8288-1A9A39C3FDA2}.2.ver0x0000000000000001.db
+# inside c:\ProgramData\Microsoft\Windows\Caches according to TrID and DROID
+>>23 ubyte =0xFF
+# skip DROID fmt-801-signature-id-1166.tap with invalid name \253\253\253\253\253\253\253\253\253\253
+# which looks like: "TF COPY II" "screen " "\023\001TF" " 1943 "
+>>>4 string <\177 Spectrum .TAP data "%-10.10s"
+#!:mime application/octet-stream
+!:mime application/x-spectrum-tap
+!:ext tap
+>>>>3 byte 0 - BASIC program
+# autostart line; 0..9999 are valid; 32768 means "no auto-loading"
+>>>>>16 uleshort x \b, autostart line %u
+# program length; length of BASIC program
+>>>>>18 uleshort x \b, program length %u
+>>>>3 byte 1 - number array
+>>>>3 byte 2 - character array
+>>>>3 byte 3 - memory block
+# length of the following data 1B00h=6912 and start address 4000h=16384 in case of a SCREEN$ header
+>>>>>14 belong 0x001B0040 (screen)
+# unused 32768=8000h
+>>>>>18 uleshort !32768 \b, unused %u
+# zxlength; length of the following data after the header
+>>>>14 uleshort x \b, data length %u
+#>>14 uleshort x \b, data length %#x
+# checksum byte; simply all bytes (including flag byte) XORed
+#>>>>20 ubyte x \b, checksum %#x
+
+# The following three blocks are from pak21-spectrum@srcf.ucam.org
+# TZX tape images
+# Update: Joerg Jenderek 2023 May
+# URL: http://fileformats.archiveteam.org/wiki/TZX
+# Reference: https://worldofspectrum.net/TZXformat.html
+# http://mark0.net/download/triddefs_xml.7z/defs/t/tzx.trid.xml
+# Note: called "ZX Spectrum Tape image" by TrID and "TZX Format" by DROID via PUID fmt/1000
+0 string ZXTape!\x1a Spectrum .TZX data
+#!:mime application/octet-stream
+!:mime application/x-spectrum-tzx
+# CDT is used for Amstrad tapes
+!:ext tzx/cdt
+>8 byte x version %d
+>9 byte x \b.%d
+# ID of first block
+>10 ubyte x \b; ID %#x
+# turbo speed data block
+>10 ubyte =0x11 (turbo)
+# length of PILOT tone (number of pulses)
+>>21 uleshort x \b, %u pilot pulses
+# length of PILOT pulse
+>>11 uleshort x with %u tstates
+# length of SYNC first pulse
+>>13 uleshort x \b, %u and
+# length of SYNC second pulse
+>>15 uleshort x %u sync tstates
+# length of ZERO bit pulse
+>>17 uleshort x \b, %u zero tstates
+# length of ONE bit pulse
+>>19 uleshort x \b, %u one tstates
+# used bits in the last byte
+>>23 ubyte x \b, use %u bit
+# plural s
+>>23 ubyte >1 \bs
+# pause after this block in milliseconds
+>>24 uleshort x \b, %u ms pause
+# BYTE[3]; length of data that follow
+>>26 ulelong&0x00FFffFF x \b, %u data bytes
+>10 ubyte =0x20 (pause)
+# pause duration in milliseconds
+>>11 uleshort x %u ms
+# text description
+>10 ubyte =0x30 (text)
+# length of the text description
+#>>11 ubyte x L=%u
+>>11 pstring x "%s"
+# archive text description in ASCII format
+>10 ubyte =0x32 (archive info)
+# length of archive text
+>>11 uleshort x \b, %#x bytes
+# number of text strings
+>>13 ubyte x with %u (type) text parts
+# text type identification byte: 0~title 1~publisher 2~author 3~year 4~language 5~type 6~price 7~protection 8~origin ff~comment
+>>14 byte <9 (%d)
+>>>14 byte >-2
+# length of text string
+#>>>>15 ubyte x L=%u
+>>>>15 pstring x %s
+# 2nd possible text description
+>>>>>&0 byte <9 (%d)
+>>>>>>&-1 byte >-2
+>>>>>>>&0 pstring x %s
+# 3rd possible text description
+>>>>>>>>&0 byte <9 (%d)
+>>>>>>>>>&-1 byte >-2
+>>>>>>>>>>&0 pstring x %s
+# 4th possible text description
+>>>>>>>>>>>&0 byte <9 (%d)
+>>>>>>>>>>>>&-1 byte >-2
+>>>>>>>>>>>>>&0 pstring x %s
+# 5th possible text description
+>>>>>>>>>>>>>>&0 byte <9 (%d)
+>>>>>>>>>>>>>>>&-1 byte >-2
+>>>>>>>>>>>>>>>>&0 pstring x %s
+# 6th possible text description
+>>>>>>>>>>>>>>>>>&0 byte <9 (%d)
+>>>>>>>>>>>>>>>>>>&-1 byte >-2
+>>>>>>>>>>>>>>>>>>>&0 pstring x %s
+# 7th possible text description
+>>>>>>>>>>>>>>>>>>>>&0 byte <9 (%d)
+>>>>>>>>>>>>>>>>>>>>>&-1 byte >-2
+>>>>>>>>>>>>>>>>>>>>>>&0 pstring x %s
+
+# RZX input recording files
+0 string RZX! Spectrum .RZX data
+>4 byte x version %d
+>5 byte x \b.%d
+
+# Floppy disk images
+0 string MV\ -\ CPCEMU\ Disk-Fil Amstrad/Spectrum .DSK data
+0 string MV\ -\ CPC\ format\ Dis Amstrad/Spectrum DU54 .DSK data
+0 string EXTENDED\ CPC\ DSK\ Fil Amstrad/Spectrum Extended .DSK data
+0 string SINCLAIR Spectrum .SCL Betadisk image
+
+# Hard disk images
+0 string RS-IDE\x1a Spectrum .HDF hard disk image
+>7 byte x \b, version %#02x
+
+# SZX snapshots (fuse and spectaculator)
+# Martin M. S. Pedersen <martin@linux.com>
+# http://www.spectaculator.com/docs/zx-state/header.shtml
+#
+0 string ZXST zx-state snapshot
+>4 byte x version %d
+>5 byte x \b.%d
+>>6 byte 0 16k ZX Spectrum
+>>6 byte 1 48k ZX Spectrum/ZX Spectrum+
+>>6 byte 2 ZX Spectrum 128
+>>6 byte 3 ZX Spectrum +2
+>>6 byte 4 ZX Spectrum +2A/+2B
+>>6 byte 5 ZX Spectrum +3
+>>6 byte 6 ZX Spectrum +3e
+>>6 byte 7 Pentagon 128
+>>6 byte 8 Timex Sinclair TC2048
+>>6 byte 9 Timex Sinclair TC2068
+>>6 byte 10 Scorpion ZS-256
+>>6 byte 11 ZX Spectrum SE
+>>6 byte 12 Timex Sinclair TS2068
+>>6 byte 13 Pentagon 512
+>>6 byte 14 Pentagon 1024
+>>6 byte 15 48k ZX Spectrum (NTSC)
+>>6 byte 16 ZX Spectrum 12Ke
+>>>7 byte 1 (alternate timings)
diff --git a/contrib/libs/libmagic/magic/Magdir/sql b/contrib/libs/libmagic/magic/Magdir/sql
new file mode 100644
index 0000000000..00f36179f8
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sql
@@ -0,0 +1,288 @@
+
+#------------------------------------------------------------------------------
+# $File: sql,v 1.26 2023/04/29 17:26:58 christos Exp $
+# sql: file(1) magic for SQL files
+#
+# From: "Marty Leisner" <mleisner@eng.mc.xerox.com>
+# Recognize some MySQL files.
+# Elan Ruusamae <glen@delfi.ee>, added MariaDB signatures
+# from https://bazaar.launchpad.net/~maria-captains/maria/5.5/view/head:/support-files/magic
+#
+0 beshort 0xfe01 MySQL table definition file
+>2 byte x Version %d
+>3 byte 0 \b, type UNKNOWN
+>3 byte 1 \b, type DIAM_ISAM
+>3 byte 2 \b, type HASH
+>3 byte 3 \b, type MISAM
+>3 byte 4 \b, type PISAM
+>3 byte 5 \b, type RMS_ISAM
+>3 byte 6 \b, type HEAP
+>3 byte 7 \b, type ISAM
+>3 byte 8 \b, type MRG_ISAM
+>3 byte 9 \b, type MYISAM
+>3 byte 10 \b, type MRG_MYISAM
+>3 byte 11 \b, type BERKELEY_DB
+>3 byte 12 \b, type INNODB
+>3 byte 13 \b, type GEMINI
+>3 byte 14 \b, type NDBCLUSTER
+>3 byte 15 \b, type EXAMPLE_DB
+>3 byte 16 \b, type CSV_DB
+>3 byte 17 \b, type FEDERATED_DB
+>3 byte 18 \b, type BLACKHOLE_DB
+>3 byte 19 \b, type PARTITION_DB
+>3 byte 20 \b, type BINLOG
+>3 byte 21 \b, type SOLID
+>3 byte 22 \b, type PBXT
+>3 byte 23 \b, type TABLE_FUNCTION
+>3 byte 24 \b, type MEMCACHE
+>3 byte 25 \b, type FALCON
+>3 byte 26 \b, type MARIA
+>3 byte 27 \b, type PERFORMANCE_SCHEMA
+>3 byte 127 \b, type DEFAULT
+>0x0033 ulong x \b, MySQL version %d
+0 belong&0xffffff00 0xfefe0500 MySQL ISAM index file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0600 MySQL ISAM compressed data file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0700 MySQL MyISAM index file
+>3 byte x Version %d
+>14 beshort x \b, %d key parts
+>16 beshort x \b, %d unique key parts
+>18 byte x \b, %d keys
+>28 bequad x \b, %lld records
+>36 bequad x \b, %lld deleted records
+0 belong&0xffffff00 0xfefe0800 MySQL MyISAM compressed data file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0900 MySQL Maria index file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0a00 MySQL Maria compressed data file
+>3 byte x Version %d
+0 belong&0xffffff00 0xfefe0c00
+>4 string MACF MySQL Maria control file
+>>3 byte x Version %d
+0 string \376bin MySQL replication log,
+>9 long x server id %d
+>8 byte 1
+>>13 long 69 \b, MySQL V3.2.3
+>>>19 string x \b, server version %s
+>>13 long 75 \b, MySQL V4.0.2-V4.1
+>>>25 string x \b, server version %s
+>8 byte 15 MySQL V5+,
+>>25 string x server version %s
+>4 string MARIALOG MySQL Maria transaction log file
+>>3 byte x Version %d
+
+#------------------------------------------------------------------------------
+# iRiver H Series database file
+# From Ken Guest <ken@linux.ie>
+# As observed from iRivNavi.iDB and unencoded firmware
+#
+0 string iRivDB iRiver Database file
+>11 string >\0 Version %s
+>39 string iHP-100 [H Series]
+
+#------------------------------------------------------------------------------
+# SQLite database files
+# Ken Guest <ken@linux.ie>, Ty Sarna, Zack Weinberg
+#
+# Version 1 used GDBM internally; its files cannot be distinguished
+# from other GDBM files.
+#
+# Update: Joerg Jenderek
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/s/sqlite-2x.trid.xml
+# Note: called "SQLite 2.x database" by TrID and "SQLite Database File Format" version 2 by DROID via PUID fmt/1135
+# Version 2 used this format:
+0 string **\ This\ file\ contains\ an\ SQLite SQLite 2.x database
+!:mime application/x-sqlite2
+# FileAttributesStore.db test.sqlite2
+!:ext sqlite/sqlite2/db
+
+# URL: https://en.wikipedia.org/wiki/SQLite
+# Reference: https://www.sqlite.org/fileformat.html
+# Update: Joerg Jenderek
+# Version 3 of SQLite allows applications to embed their own "user version"
+# number in the database at offset 60. Later, SQLite added an "application id"
+# at offset 68 that is preferred over "user version" for indicating the
+# associated application.
+#
+0 string SQLite\ format\ 3
+# skip DROID fmt-729-signature-id-1053.sqlite by checking for valid page size
+>16 ubeshort >0 SQLite 3.x
+# deprecated
+#!:mime application/x-sqlite3
+!:mime application/vnd.sqlite3
+# seldom found extension sqlite3 like in SyncData.sqlite3
+# db
+# db3 like: AddrBook.db3 cgipcrvp.db3
+# https://www.maplesoft.com/support/help/Maple/view.aspx?path=worksheet%2freference%2fhelpdatabase
+# help is used for newer Maple help database
+# SQLite database weewx.sdb used by weather software weewx
+# https://www.weewx.com/docs/usersguide.htm
+# Avira Antivir use extension "dbe" like in avevtdb.dbe, avguard_tchk.dbe
+# Unfortunately extension sqlite also used for other databases starting with string
+# "TTCONTAINER" like in tracks.sqlite contentconsumer.sqlite contentproducerrepository.sqlite
+# and with string "ZV-zlib" in like extra.sqlite
+>>68 belong !0x5CDE09EF database
+!:ext sqlite/sqlite3/db/db3/dbe/sdb/help
+>>68 belong =0x5CDE09EF database
+# maple is used for Maple Workbook
+!:ext maple
+>>60 belong =0x5f4d544e (Monotone source repository)
+# if no known user version then check for Application IDs with default clause
+>>60 belong !0x5f4d544e
+# The "Application ID" set by PRAGMA application_id
+>>>68 belong =0x0f055112 (Fossil checkout)
+>>>68 belong =0x0f055113 (Fossil global configuration)
+>>>68 belong =0x0f055111 (Fossil repository)
+>>>68 belong =0x42654462 (Bentley Systems BeSQLite Database)
+>>>68 belong =0x42654c6e (Bentley Systems Localization File)
+>>>68 belong =0x47504b47 (OGC GeoPackage file)
+# https://www.sqlite.org/src/artifact?ci=trunk&filename=magic.txt
+>>>68 belong =0x47503130 (OGC GeoPackage version 1.0 file)
+>>>68 belong =0x45737269 (Esri Spatially-Enabled Database)
+>>>68 belong =0x4d504258 (MBTiles tileset)
+# https://www.maplesoft.com/support/help/errors/view.aspx?path=Formats/Maple
+>>>68 belong =0x5CDE09EF (Maple Workbook)
+# unknown application ID
+>>>68 default x
+>>>>68 belong !0 \b, application id %u
+# The "user version" as read and set by the user_version pragma like:
+# 1 2 4 5 7 9 10 25 36 43 53 400 416 131073 131074 131075
+>>60 belong !0 \b, user version %d
+# SQLITE_VERSION_NUMBER like: 0 3008011 3016002 3007014 3017000 3022000 3028000 3031001
+>>96 belong x \b, last written using SQLite version %d
+# database page size in bytes; a power of two between 512 and 32768, or 1 for 65536
+# like: 512 1024 often 4096 32768
+>>16 ubeshort !4096 \b, page size %u
+# File format write version. 1 for legacy; 2 for WAL; 0 for corruptDB.sqlite
+>>18 ubyte !1 \b, writer version %u
+# File format read version. 1 for legacy; 2 for WAL; 4 for corruptDB.sqlite
+>>19 ubyte !1 \b, read version %u
+# Bytes of unused "reserved" space at the end of each page. Usually 0
+>>20 ubyte !0 \b, unused bytes %u
+# maximum embedded payload fraction. Must be 64; 1 for corruptDB.sqlite
+>>21 ubyte !64 \b, maximum payload %u
+# Minimum embedded payload fraction. Must be 32; 1 for corruptDB.sqlite
+>>22 ubyte !32 \b, minimum payload %u
+# Leaf payload fraction. Must be 32; 0 for corruptDB.sqlite
+>>23 ubyte !32 \b, leaf payload %u
+# file change counter
+>>24 ubelong x \b, file counter %u
+# Size of the database file in pages
+>>28 ubelong x \b, database pages %u
+# page number of the first freelist trunk page like: 0 2 3 4 5 9
+# 10 13 14 15 16 17 18 19 23 36 39 46 50 136 190 217 307 505 516 561 883 1659
+>>32 ubelong !0 \b, 1st free page %u
+# total number of freelist pages
+>>36 ubelong !0 \b, free pages %u
+# The schema cookie like: 2 3 4 6 7 9 A D E F 13 14 1C 25 2A 2F 33 44 4B 53 5A 5F 62 86 87 8F 91 A8
+>>40 ubelong x \b, cookie %#x
+# the schema format number. Supported formats are 1 2 3 and often 4
+# 3328 for corruptDB.sqlite and 0 for 512 byte storage.sqlite (TorBrowser Firefox Thunderbird)
+>>44 ubelong x \b, schema %u
+# Suggested cache size like: 0 2000
+>>48 ubelong !0 \b, cache page size %u
+# The page number of the largest root b-tree page when in auto-vacuum or incremental-vacuum modes, or zero otherwise.
+>>52 ubelong !0 \b, largest root page %u
+# The database text encoding; a value of 1 means UTF-8; 2 means UTF-16le; 3 means UTF-16be
+#>>56 ubelong x \b, encoding %u
+>>56 ubelong x
+>>>56 ubelong =1 \b, UTF-8
+>>>56 ubelong =2 \b, UTF-16 little endian
+>>>56 ubelong =3 \b, UTF-16 big endian
+# 0 for corruptDB.sqlite and for storage.sqlite with database pages 1 (TorBrowser Firefox Thunderbird)
+# https://mozilla.github.io/firefox-browser-architecture/text/0010-firefox-data-stores.html
+>>>56 default x
+>>>>56 ubelong x \b, unknown %#x encoding
+# True (non-zero) for incremental-vacuum mode; false (zero) otherwiseqy
+>>64 ubelong !0 \b, vacuum mode %u
+# Reserved for expansion. Must be zero
+>>72 uquad !0 \b, reserved %#llx
+# The version-valid-for number like:
+# 1 2 3 4 C F 68h 95h 266h A99h 3DCDh B7CEh
+>>92 ubelong x \b, version-valid-for %u
+
+# SQLite Write-Ahead Log from SQLite version >= 3.7.0
+# https://www.sqlite.org/fileformat.html#walformat
+0 belong&0xfffffffe 0x377f0682 SQLite Write-Ahead Log,
+!:ext sqlite-wal/db-wal
+>4 belong x version %d
+# Summary: SQLite Write-Ahead-Log index (shared memory)
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/SQLite
+# Reference: http://www.sqlite.org/draft/walformat.html#walidxfmt
+# iVersion; WAL-index format version number; always 3007000=2DE218h
+0 ulelong 0x002DE218
+>0 use shm-le
+# big endian variant not tested
+0 ubelong 0x002DE218
+>0 use \^shm-le
+# show information about SQLite Write-Ahead-Log shared memory
+0 name shm-le
+>0 ulelong x SQLite Write-Ahead Log shared memory
+#!:mime application/octet-stream
+!:mime application/vnd.sqlite3
+# db3-shm Acronis BackupAndRecovery F4CEEE47-042C-4828-95A0-DE44EC267A28.db3-shm
+# dbx-shm probably Dropbox filecache.dbx-shm
+# aup3-shm Audacity project tada.aup3-shm
+# srd-shm Microsoft Windows StateRepository service StateRepository-Deployment.srd-shm StateRepository-Machine.srd-shm:
+!:ext sqlite-shm/db-shm/db3-shm/dbx-shm/aup3-shm/srd-shm
+# unused padding space; must be zero
+>4 ulelong !0 \b, unused %x
+# iChange; unsigned integer counter, incremented with each transaction
+>8 ulelong x \b, counter %u
+# isInit; the "isInit" flag; 1 when the shm file has been initialized
+>12 ubyte !1 \b, not initialized %u
+# bigEndCksum; true if the WAL file uses big-ending checksums; 0 if the WAL uses little-endian checksums
+>13 ubyte !0 \b, checksum type %u
+# szPage; database page size in bytes, or 1 if the page size is 65536
+>14 uleshort !1 \b, page size %u
+>14 uleshort =1 \b, page size 65536
+# mxFrame; number of valid and committed frames in the WAL file
+>16 ulelong x \b, %u frames
+# nPage; size of the database file in pages
+>20 ulelong x \b, %u pages
+# aFrameCksum; checksum of the last frame in the WAL file
+>24 ulelong x \b, frame checksum %#x
+# aSalt; two salt value copied from the WAL file header in the byte-order of the WAL file; might be different from machine byte-order
+>32 ulequad x \b, salt %#llx
+# aCksum; checksum over bytes 0 through 39 of this header
+>40 ulelong x \b, header checksum %#x
+# a copy of bytes 0 through 47 of header
+>48 ulelong !3007000 \b, iversion %u
+# nBackfill; number of WAL frames that have already been backfilled into the database by prior checkpoints
+>96 ulelong !0 \b, %u backfilled
+# nBackfillAttempted; number of WAL frames that have attempted to be backfilled
+>>128 ulelong x (%u attempts)
+# read-mark[0..4]; five "read marks"; each read mark is a 32-bit unsigned integer
+>100 ulelong !0 \b, read-mark[0] %#x
+>104 ulelong x \b, read-mark[1] %#x
+>108 ulelong !0xffffffff \b, read-mark[2] %#x
+>112 ulelong !0xffffffff \b, read-mark[3] %#x
+>116 ulelong !0xffffffff \b, read-mark[4] %#x
+# unused space set aside for 8 file locks
+>120 ulequad !0 \b, space %#llx
+# unused space reserved for further expansion
+>132 ulelong !0 \b, reserved %#x
+
+# SQLite Rollback Journal
+# https://www.sqlite.org/fileformat.html#rollbackjournal
+0 string \xd9\xd5\x05\xf9\x20\xa1\x63\xd7 SQLite Rollback Journal
+
+# Panasonic channel list database svl.bin or svl.db added by Joerg Jenderek
+# https://github.com/PredatH0r/ChanSort
+0 string PSDB\0 Panasonic channel list DataBase
+!:ext db/bin
+#!:mime application/x-db-svl-panasonic
+>126 string SQLite\ format\ 3
+#!:mime application/x-panasonic-sqlite3
+>>&-15 indirect x \b; contains
+
+# H2 Database from https://www.h2database.com/
+0 string --\ H2\ 0.5/B\ --\ \n H2 Database file
+
+# DuckDB database file from https://duckdb.org
+8 string DUCK DuckDB database file
+>12 lequad x \b, version %lld
+#>20 lequad x \b, flags %#llx
+#>28 lequad x \b, flags %#llx
diff --git a/contrib/libs/libmagic/magic/Magdir/ssh b/contrib/libs/libmagic/magic/Magdir/ssh
new file mode 100644
index 0000000000..56b28a8488
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ssh
@@ -0,0 +1,42 @@
+# Type: OpenSSH key files
+# From: Nicolas Collignon <tsointsoin@gmail.com>
+
+0 string SSH\040PRIVATE\040KEY OpenSSH RSA1 private key,
+>28 string >\0 version %s
+0 string -----BEGIN\040OPENSSH\040PRIVATE\040KEY----- OpenSSH private key
+# https://www.rfc-editor.org/rfc/rfc5958
+0 string -----BEGIN\040PRIVATE\040KEY----- OpenSSH private key (no password)
+0 string -----BEGIN\040ENCRYPTED\040PRIVATE\040KEY----- OpenSSH private key (with password)
+
+0 string ssh-dss\040 OpenSSH DSA public key
+0 string ssh-rsa\040 OpenSSH RSA public key
+0 string ecdsa-sha2-nistp256 OpenSSH ECDSA public key
+0 string ecdsa-sha2-nistp384 OpenSSH ECDSA public key
+0 string ecdsa-sha2-nistp521 OpenSSH ECDSA public key
+0 string ssh-ed25519 OpenSSH ED25519 public key
+
+0 string SSHKRL\n\0
+>8 ubelong 1 OpenSSH key/certificate revocation list, format %u
+>>12 ubequad x \b, version %llx
+>>>20 beqdate x \b, generated %s
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/PuTTY
+# Reference: https://the.earth.li/~sgtatham/putty/latest/putty-0.73.tar.gz
+# /sshpubk.c
+0 string PuTTY-User-Key-File- PuTTY Private Key File
+#!:mime text/plain
+# https://github.com/github/putty/blob/master/windows/installer.wxs
+!:mime application/x-putty-private-key
+!:ext ppk
+# version 1 or 2
+>20 string x \b, version %.1s
+# name of the algorithm like: ssh-dss ssh-rsa ecdsa-sha2-nistp256 ssh-ed25519
+>23 string x \b, algorithm %s
+# next line says "Encryption: " plus an encryption type like aes256-cbc or none
+>32 search/13 Encryption:\040 \b, Encryption
+>>&0 string x %s
+# next line says "Comment: " plus the comment string
+>>>&0 search/3 Comment:\040
+>>>>&0 string x "%s"
+
diff --git a/contrib/libs/libmagic/magic/Magdir/ssl b/contrib/libs/libmagic/magic/Magdir/ssl
new file mode 100644
index 0000000000..2309392393
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ssl
@@ -0,0 +1,20 @@
+
+#------------------------------------------------------------------------------
+# $File: ssl,v 1.5 2017/12/29 04:00:07 christos Exp $
+# ssl: file(1) magic for SSL file formats
+
+# Type: OpenSSL certificates/key files
+# From: Nicolas Collignon <tsointsoin@gmail.com>
+
+0 string -----BEGIN\040CERTIFICATE----- PEM certificate
+0 string -----BEGIN\040CERTIFICATE\040REQ PEM certificate request
+0 string -----BEGIN\040RSA\040PRIVATE PEM RSA private key
+0 string -----BEGIN\040DSA\040PRIVATE PEM DSA private key
+0 string -----BEGIN\040EC\040PRIVATE PEM EC private key
+0 string -----BEGIN\040ECDSA\040PRIVATE PEM ECDSA private key
+
+# From Luc Gommans
+# OpenSSL enc file (recognized by a magic string preceding the password's salt)
+0 string Salted__ openssl enc'd data with salted password
+# Using the -a or -base64 option, OpenSSL will base64-encode the data.
+0 string U2FsdGVkX1 openssl enc'd data with salted password, base64 encoded
diff --git a/contrib/libs/libmagic/magic/Magdir/statistics b/contrib/libs/libmagic/magic/Magdir/statistics
new file mode 100644
index 0000000000..ca9f8591b6
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/statistics
@@ -0,0 +1,45 @@
+
+#------------------------------------------------------------------------------
+# $File: statistics,v 1.3 2022/03/24 15:48:58 christos Exp $
+# statistics: file(1) magic for statistics related software
+#
+
+# From Remy Rampin
+
+# Stata is a statistical software tool that was created in 1985. While I
+# don't personally use it, data files in its native (proprietary) format
+# are common (.dta files).
+#
+# Because they are so common, especially in statistical and social
+# sciences, Stata files and SPSS files can be opened by a lot of modern
+# software, for example Python's pandas package provides built-in
+# support for them (read_stata() and read_spss()).
+#
+# I noticed that the magic database includes an entry for SPSS files but
+# not Stata files. Stata files for Stata 13 and newer (formats 117, 118,
+# and 119) always begin with the string "<stata_dta><header>" as per
+# https://www.stata.com/help.cgi?dta#definition
+#
+# The format version number always follows, for example:
+# <stata_dta><header><release>117</release>
+# <stata_dta><header><release>118</release>
+#
+# Therefore the following line would do the trick:
+# 0 string <stata_dta><header> Stata Data File
+#
+# (I'm sure the version number could be captured as well but I did not
+# manage this without a regex)
+#
+# Unfortunately the previous formats (created by Stata before 13, which
+# was released 2013) are harder to recognize. Format 115 starts with the
+# four bytes 0x73010100 or 0x73020100, format 114 with 0x72010100 or
+# 0x72020100, format 113 with 0x71010101 or 0x71020101.
+#
+# For additional reference, the Library of Congress website has an entry
+# for the Stata Data File Format 118:
+# https://www.loc.gov/preservation/digital/formats/fdd/fdd000471.shtml
+#
+# Example of those files can be found on Zenodo:
+# https://zenodo.org/search?page=1&size=20&q=&file_type=dta
+0 string \<stata_dta\>\<header\>\<release\> Stata Data File
+>&0 regex [0-9]+ (Release %s)
diff --git a/contrib/libs/libmagic/magic/Magdir/subtitle b/contrib/libs/libmagic/magic/Magdir/subtitle
new file mode 100644
index 0000000000..cfbe293d59
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/subtitle
@@ -0,0 +1,38 @@
+
+#------------------------------------------------------------------------------
+# $File: subtitle,v 1.2 2022/09/07 11:29:09 christos Exp $
+# subtitle: file(1) magic for subtitles files
+
+# EBU-STL
+# https://tech.ebu.ch/docs/tech/tech3264.pdf
+3 string STL EBU-STL subtitles
+>6 regex =^[0-9][0-9] \b, rate %s
+>>8 string .01 \b, v1
+!:mime application/x-ebu-stl
+>>>16 regex =^[^\ ]{0,32} \b, title "%s"
+>>>>224 regex =^[0-9]{2} \b, created %-.2s
+>>>>>&0 regex =^[0-9]{2} \b-%-.2s
+>>>>>>&0 regex =^[0-9]{2} \b-%-.2s
+!:ext stl
+
+# SubRip (srt) subtitles
+0 regex/20 =^1[\r\n]+0[01]:[0-9]{2}:[0-9]{2},[0-9]{3}\040--> SubRip
+!:mime application/x-subrip
+!:ext srt
+
+# WebVTT subtitles
+# https://www.w3.org/TR/webvtt1/
+0 string/t WEBVTT
+>&0 regex/255 =[0-9]{2}:[0-9]{2}\\.[0-9]{3}\040--> WebVTT subtitles
+!:mime text/vtt
+!:ext vtt
+
+# XML TTML subtitles
+# https://www.w3.org/TR/ttml2/
+0 string/t \<?xml
+>20 search/400 \020xmlns=
+>>&0 regex ['"]http://www.w3.org/ns/ttml TTML subtitles
+!:mime application/ttml+xml
+# Augment strength to beat plain XML
+!:strength * 3
+!:ext ttml
diff --git a/contrib/libs/libmagic/magic/Magdir/sun b/contrib/libs/libmagic/magic/Magdir/sun
new file mode 100644
index 0000000000..df83834d2d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sun
@@ -0,0 +1,141 @@
+
+#------------------------------------------------------------------------------
+# $File: sun,v 1.28 2019/04/19 00:42:27 christos Exp $
+# sun: file(1) magic for Sun machines
+#
+# Values for big-endian Sun (MC680x0, SPARC) binaries on pre-5.x
+# releases. (5.x uses ELF.) Entries for executables without an
+# architecture type, used before the 68020-based Sun-3's came out,
+# are in aout, as they're indistinguishable from other big-endian
+# 32-bit a.out files.
+#
+0 belong&077777777 0600413 a.out SunOS SPARC demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0600410 a.out SunOS SPARC pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0600407 a.out SunOS SPARC
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0400413 a.out SunOS mc68020 demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0400410 a.out SunOS mc68020 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0400407 a.out SunOS mc68020
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0200413 a.out SunOS mc68010 demand paged
+>0 byte &0x80
+>>20 belong <4096 shared library
+>>20 belong =4096 dynamically linked executable
+>>20 belong >4096 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0200410 a.out SunOS mc68010 pure
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+0 belong&077777777 0200407 a.out SunOS mc68010
+>0 byte &0x80 dynamically linked executable
+>0 byte ^0x80 executable
+>16 belong >0 not stripped
+
+#
+# Core files. "SPARC 4.x BCP" means "core file from a SunOS 4.x SPARC
+# binary executed in compatibility mode under SunOS 5.x".
+#
+0 belong 0x080456 SunOS core file
+>4 belong 432 (SPARC)
+>>132 string >\0 from '%s'
+>>116 belong =3 (quit)
+>>116 belong =4 (illegal instruction)
+>>116 belong =5 (trace trap)
+>>116 belong =6 (abort)
+>>116 belong =7 (emulator trap)
+>>116 belong =8 (arithmetic exception)
+>>116 belong =9 (kill)
+>>116 belong =10 (bus error)
+>>116 belong =11 (segmentation violation)
+>>116 belong =12 (bad argument to system call)
+>>116 belong =29 (resource lost)
+>>120 belong x (T=%dK,
+>>124 belong x D=%dK,
+>>128 belong x S=%dK)
+>4 belong 826 (68K)
+>>128 string >\0 from '%s'
+>4 belong 456 (SPARC 4.x BCP)
+>>152 string >\0 from '%s'
+# Sun SunPC
+0 long 0xfa33c08e SunPC 4.0 Hard Disk
+0 string #SUNPC_CONFIG SunPC 4.0 Properties Values
+# Sun snoop (see RFC 1761, which describes the capture file format,
+# RFC 3827, which describes some additional datalink types, and
+# https://www.iana.org/assignments/snoop-datalink-types/snoop-datalink-types.xml,
+# which is the IANA registry of Snoop datalink types)
+#
+0 string snoop Snoop capture file
+>8 belong >0 - version %d
+>12 belong 0 (IEEE 802.3)
+>12 belong 1 (IEEE 802.4)
+>12 belong 2 (IEEE 802.5)
+>12 belong 3 (IEEE 802.6)
+>12 belong 4 (Ethernet)
+>12 belong 5 (HDLC)
+>12 belong 6 (Character synchronous)
+>12 belong 7 (IBM channel-to-channel adapter)
+>12 belong 8 (FDDI)
+>12 belong 9 (Other)
+>12 belong 10 (type %d)
+>12 belong 11 (type %d)
+>12 belong 12 (type %d)
+>12 belong 13 (type %d)
+>12 belong 14 (type %d)
+>12 belong 15 (type %d)
+>12 belong 16 (Fibre Channel)
+>12 belong 17 (ATM)
+>12 belong 18 (ATM Classical IP)
+>12 belong 19 (type %d)
+>12 belong 20 (type %d)
+>12 belong 21 (type %d)
+>12 belong 22 (type %d)
+>12 belong 23 (type %d)
+>12 belong 24 (type %d)
+>12 belong 25 (type %d)
+>12 belong 26 (IP over Infiniband)
+>12 belong >26 (type %d)
+
+#---------------------------------------------------------------------------
+# The following entries have been tested by Duncan Laurie <duncan@sun.com> (a
+# lead Sun/Cobalt developer) who agrees that they are good and worthy of
+# inclusion.
+
+# Boot ROM images for Sun/Cobalt Linux server appliances
+0 string Cobalt\ Networks\ Inc.\nFirmware\ v Paged COBALT boot rom
+>38 string x V%.4s
+
+# New format for Sun/Cobalt boot ROMs is annoying, it stores the version code
+# at the very end where file(1) can't get it.
+0 string CRfs COBALT boot rom data (Flat boot rom or file system)
diff --git a/contrib/libs/libmagic/magic/Magdir/svf b/contrib/libs/libmagic/magic/Magdir/svf
new file mode 100644
index 0000000000..b0d5c980f9
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/svf
@@ -0,0 +1,5 @@
+# $File: svf,v 1.2 2023/05/23 13:37:32 christos Exp $
+#
+# file(1) magic(5) data for SmartVersion files with the .svf extension.
+
+0 string DFS\ File\x0D\x0Ahttp://www.difstream.com\x0D\x0A SmartVersion binary patch file
diff --git a/contrib/libs/libmagic/magic/Magdir/sylk b/contrib/libs/libmagic/magic/Magdir/sylk
new file mode 100644
index 0000000000..f497c05bb2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sylk
@@ -0,0 +1,36 @@
+
+#------------------------------------------------------------------------------
+# $File: sylk,v 1.1 2020/04/05 22:18:34 christos Exp $
+# sylk: file(1) magic for SYLK text files
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/SYmbolic_LinK_%28SYLK%29
+# http://fileformats.archiveteam.org/wiki/SYLK
+# Note: called by TrID "SYLK - SYmbolic LinK data",
+# by DROID "Microsoft Symbolic Link (SYLK) File"
+# by FreeDesktop.org "spreadsheet interchange document"
+0 string ID;P
+# skip short DROID x-fmt-106-signature-id-603.slk
+>7 ubyte >0 spreadsheet interchange document
+# https://reposcope.com/mimetype/text/spreadsheet
+#!:mime text/spreadsheet
+# https://reposcope.com/mimetype/application/x-sylk by Gnumeric
+!:mime application/x-sylk
+!:ext slk/sylk
+>>4 ubyte >037 \b, created by
+# Gnumeric, pmw~PlanMaker, CALCOOO32~LibreOffice OpenOffice, SCALC3~StarOffice
+# MP~Multiplan, XL~Excel WXL~Excel Windows
+>>>4 string Gnumeric Gnumeric
+>>>4 string pmw PlanMaker
+>>>4 string CALCOOO32 Libre/OpenOffice Calc
+>>>4 string SCALC3 StarOffice Calc
+>>>4 string XL Excel
+# Excel, version probably running on Windows
+>>>4 string WXL Excel
+# not tested
+>>>4 string MP Multiplan
+# unknown spreadsheet software
+>>>4 default x
+>>>>4 string x %s
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/symbos b/contrib/libs/libmagic/magic/Magdir/symbos
new file mode 100644
index 0000000000..c97a42e0c7
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/symbos
@@ -0,0 +1,42 @@
+
+#------------------------------------------------------------------------------
+# msx: file(1) magic for the SymbOS operating system
+# http://www.symbos.de
+# Fabio R. Schmidlin <frs@pop.com.br>
+
+# SymbOS EXE file
+0x30 string SymExe SymbOS executable
+>0x36 ubyte x v%c
+>0x37 ubyte x \b.%c
+>0xF string x \b, name: %s
+
+# SymbOS DOX document
+0 string INFOq\0 SymbOS DOX document
+
+# Symbos driver
+0 string SMD1 SymbOS driver
+>19 byte x \b, name: %c
+>20 byte x \b%c
+>21 byte x \b%c
+>22 byte x \b%c
+>23 byte x \b%c
+>24 byte x \b%c
+>25 byte x \b%c
+>26 byte x \b%c
+>27 byte x \b%c
+>28 byte x \b%c
+>29 byte x \b%c
+>30 byte x \b%c
+>31 byte x \b%c
+
+# Symbos video
+0 string SymVid SymbOS video
+>6 ubyte x v%c
+>7 ubyte x \b.%c
+
+# Soundtrakker 128 ST2 music
+0 byte 0
+>0xC string \x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x40\x00 Soundtrakker 128 ST2 music,
+>>1 string x name: %s
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/sysex b/contrib/libs/libmagic/magic/Magdir/sysex
new file mode 100644
index 0000000000..d02389d9a4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/sysex
@@ -0,0 +1,429 @@
+
+#------------------------------------------------------------------------
+# $File: sysex,v 1.12 2022/10/31 13:22:26 christos Exp $
+# sysex: file(1) magic for MIDI sysex files
+#
+# GRR: original 1 byte test at offset was too general as it catches also many FATs of DOS filesystems
+# where real SYStem EXclusive messages at offset 1 are limited to seven bits
+# https://en.wikipedia.org/wiki/MIDI
+# test for StartSysEx byte and upper unsed bit of vendor ID
+0 ubeshort&0xFF80 0xF000
+# MIDI System Exclusive (SysEx) messages (strength=50) after Microsoft Visual C library (strength=70)
+#!:strength +0
+# skip Microsoft Visual C library with page size 16 misidentified as ADA and
+# page size 32 misidentified as Inventronics by looking for terminating End Of eXclusive byte (EOX)
+>2 search/12 \xF7
+>>0 use midi-sysex
+# display information about MIDI System Exclusive (SysEx) messages
+0 name midi-sysex
+# https://fileinfo.com/extension/syx
+>1 ubyte x MIDI audio System Exclusive (SysEx) message -
+# Note: file (version 5.41) labeled the above entry as "SysEx File"
+#!:mime application/octet-stream
+!:mime audio/x-syx
+# https://onsongapp.com/docs/features/formats/sysex
+!:ext syx/sysex
+# https://www.midi.org/specifications-old/item/manufacturer-id-numbers
+# https://raw.githubusercontent.com/insolace/MIDI-Sysex-MFG-IDs/master/Sysex%20ID%20Tables/MIDI%20Sysex%20MFG%20IDs.csv
+# SysEx manufacturer ID; originally one byte, but now 0 is used as an escapement to reach the next two
+# North American Group
+#>1 byte 0x01 Sequential
+>1 byte 0x01 Sequential Circuits
+>1 byte 0x02 IDP
+#>1 byte 0x03 OctavePlateau
+>1 byte 0x03 Voyetra Turtle Beach
+>1 byte 0x04 Moog
+#>1 byte 0x05 Passport
+>1 byte 0x05 Passport Designs
+#>1 byte 0x06 Lexicon
+>1 byte 0x06 Lexicon Inc.
+>1 byte 0x07 Kurzweil/Future Retro
+>>3 byte 0x77 777
+>>4 byte 0x00 Bank
+>>4 byte 0x01 Song
+>>5 byte 0x0f 16
+>>5 byte 0x0e 15
+>>5 byte 0x0d 14
+>>5 byte 0x0c 13
+>>5 byte 0x0b 12
+>>5 byte 0x0a 11
+>>5 byte 0x09 10
+>>5 byte 0x08 9
+>>5 byte 0x07 8
+>>5 byte 0x06 7
+>>5 byte 0x05 6
+>>5 byte 0x04 5
+>>5 byte 0x03 4
+>>5 byte 0x02 3
+>>5 byte 0x01 2
+>>5 byte 0x00 1
+>>5 byte 0x10 (ALL)
+>>2 byte x \b, Channel %d
+>1 byte 0x08 Fender
+#>1 byte 0x09 Gulbransen
+>1 byte 0x09 MIDI9
+#>1 byte 0x0a AKG
+>1 byte 0x0a AKG Acoustics
+>1 byte 0x0b Voyce
+>1 byte 0x0c Waveframe
+# not ADA programming language
+#>1 byte 0x0d ADA
+>1 byte 0x0d ADA Signal Processors Inc.
+#>1 byte 0x0e Garfield
+>1 byte 0x0e Garfield Electronics
+>1 byte 0x0f Ensoniq
+>1 byte 0x10 Oberheim
+>>2 byte 0x06 Matrix 6 series
+>>3 byte 0x0A Dump (All)
+>>3 byte 0x01 Dump (Bank)
+>>4 belong 0x0002040E Matrix 1000
+>>>11 byte <2 User bank %d
+>>>11 byte >1 Preset bank %d
+>1 byte 0x11 Apple
+>1 byte 0x12 GreyMatter
+>1 byte 0x14 PalmTree
+>1 byte 0x15 JLCooper
+>1 byte 0x16 Lowrey
+>1 byte 0x17 AdamsSmith
+>1 byte 0x18 E-mu
+#>1 byte 0x19 Harmony
+>1 byte 0x19 Harmony Systems
+>1 byte 0x1a ART
+>1 byte 0x1b Baldwin
+>1 byte 0x1c Eventide
+>1 byte 0x1d Inventronics
+>1 byte 0x1f Clarity
+
+# European Group
+#>1 byte 0x21 SIEL
+>1 byte 0x21 Proel Labs (SIEL)
+>1 byte 0x22 Synthaxe
+>1 byte 0x24 Hohner
+>1 byte 0x25 Twister
+#>1 byte 0x26 Solton
+>1 byte 0x26 Ketron s.r.l.
+>1 byte 0x27 Jellinghaus
+>1 byte 0x28 Southworth
+>1 byte 0x29 PPG
+>1 byte 0x2a JEN
+#>1 byte 0x2b SSL
+>1 byte 0x2b Solid State Logic Organ Systems
+#>1 byte 0x2c AudioVertrieb
+>1 byte 0x2c Audio Veritrieb-P. Struven
+
+>1 byte 0x2f ELKA
+>>3 byte 0x09 EK-44
+
+>1 byte 0x30 Dynacord
+#>1 byte 0x31 Jomox
+>1 byte 0x31 Viscount International Spa
+>1 byte 0x33 Clavia
+>1 byte 0x39 Soundcraft
+# Some Waldorf info from http://Stromeko.Synth.net/Downloads#WaldorfDocs
+>1 byte 0x3e Waldorf
+>>2 byte 0x00 microWave
+>>2 byte 0x0E microwave2 / XT
+>>2 byte 0x0F Q / Q+
+>>3 byte =0 (default id)
+>>3 byte >0 (
+>>>3 byte <0x7F \bdevice %d)
+>>>3 byte =0x7F \bbroadcast id)
+>>3 byte 0x7f Microwave I
+>>>4 byte 0x00 SNDR (Sound Request)
+>>>4 byte 0x10 SNDD (Sound Dump)
+>>>4 byte 0x20 SNDP (Sound Parameter Change)
+>>>4 byte 0x30 SNDQ (Sound Parameter Inquiry)
+>>>4 byte 0x70 BOOT (Sound Reserved)
+>>>4 byte 0x01 MULR (Multi Request)
+>>>4 byte 0x11 MULD (Multi Dump)
+>>>4 byte 0x21 MULP (Multi Parameter Change)
+>>>4 byte 0x31 MULQ (Multi Parameter Inquiry)
+>>>4 byte 0x71 OS (Multi Reserved)
+>>>4 byte 0x02 DRMR (Drum Map Request)
+>>>4 byte 0x12 DRMD (Drum Map Dump)
+>>>4 byte 0x22 DRMP (Drum Map Parameter Change)
+>>>4 byte 0x32 DRMQ (Drum Map Parameter Inquiry)
+>>>4 byte 0x72 BIN (Drum Map Reserved)
+>>>4 byte 0x03 PATR (Sequencer Pattern Request)
+>>>4 byte 0x13 PATD (Sequencer Pattern Dump)
+>>>4 byte 0x23 PATP (Sequencer Pattern Parameter Change)
+>>>4 byte 0x33 PATQ (Sequencer Pattern Parameter Inquiry)
+>>>4 byte 0x73 AFM (Sequencer Pattern Reserved)
+>>>4 byte 0x04 GLBR (Global Parameter Request)
+>>>4 byte 0x14 GLBD (Global Parameter Dump)
+>>>4 byte 0x24 GLBP (Global Parameter Parameter Change)
+>>>4 byte 0x34 GLBQ (Global Parameter Parameter Inquiry)
+>>>4 byte 0x07 MODR (Mode Parameter Request)
+>>>4 byte 0x17 MODD (Mode Parameter Dump)
+>>>4 byte 0x27 MODP (Mode Parameter Parameter Change)
+>>>4 byte 0x37 MODQ (Mode Parameter Parameter Inquiry)
+>>2 byte 0x10 microQ
+>>>4 byte 0x00 SNDR (Sound Request)
+>>>4 byte 0x10 SNDD (Sound Dump)
+>>>4 byte 0x20 SNDP (Sound Parameter Change)
+>>>4 byte 0x30 SNDQ (Sound Parameter Inquiry)
+>>>4 byte 0x70 (Sound Reserved)
+>>>4 byte 0x01 MULR (Multi Request)
+>>>4 byte 0x11 MULD (Multi Dump)
+>>>4 byte 0x21 MULP (Multi Parameter Change)
+>>>4 byte 0x31 MULQ (Multi Parameter Inquiry)
+>>>4 byte 0x71 OS (Multi Reserved)
+>>>4 byte 0x02 DRMR (Drum Map Request)
+>>>4 byte 0x12 DRMD (Drum Map Dump)
+>>>4 byte 0x22 DRMP (Drum Map Parameter Change)
+>>>4 byte 0x32 DRMQ (Drum Map Parameter Inquiry)
+>>>4 byte 0x72 BIN (Drum Map Reserved)
+>>>4 byte 0x04 GLBR (Global Parameter Request)
+>>>4 byte 0x14 GLBD (Global Parameter Dump)
+>>>4 byte 0x24 GLBP (Global Parameter Parameter Change)
+>>>4 byte 0x34 GLBQ (Global Parameter Parameter Inquiry)
+>>2 byte 0x11 rackAttack
+>>>4 byte 0x00 SNDR (Sound Parameter Request)
+>>>4 byte 0x10 SNDD (Sound Parameter Dump)
+>>>4 byte 0x20 SNDP (Sound Parameter Parameter Change)
+>>>4 byte 0x30 SNDQ (Sound Parameter Parameter Inquiry)
+>>>4 byte 0x01 PRGR (Program Parameter Request)
+>>>4 byte 0x11 PRGD (Program Parameter Dump)
+>>>4 byte 0x21 PRGP (Program Parameter Parameter Change)
+>>>4 byte 0x31 PRGQ (Program Parameter Parameter Inquiry)
+>>>4 byte 0x71 OS (Program Parameter Reserved)
+>>>4 byte 0x03 PATR (Pattern Parameter Request)
+>>>4 byte 0x13 PATD (Pattern Parameter Dump)
+>>>4 byte 0x23 PATP (Pattern Parameter Parameter Change)
+>>>4 byte 0x33 PATQ (Pattern Parameter Parameter Inquiry)
+>>>4 byte 0x04 GLBR (Global Parameter Request)
+>>>4 byte 0x14 GLBD (Global Parameter Dump)
+>>>4 byte 0x24 GLBP (Global Parameter Parameter Change)
+>>>4 byte 0x34 GLBQ (Global Parameter Parameter Inquiry)
+>>>4 byte 0x05 EFXR (FX Parameter Request)
+>>>4 byte 0x15 EFXD (FX Parameter Dump)
+>>>4 byte 0x25 EFXP (FX Parameter Parameter Change)
+>>>4 byte 0x35 EFXQ (FX Parameter Parameter Inquiry)
+>>>4 byte 0x07 MODR (Mode Command Request)
+>>>4 byte 0x17 MODD (Mode Command Dump)
+>>>4 byte 0x27 MODP (Mode Command Parameter Change)
+>>>4 byte 0x37 MODQ (Mode Command Parameter Inquiry)
+>>2 byte 0x03 Wave
+>>>4 byte 0x00 SBPR (Soundprogram)
+>>>4 byte 0x01 SAPR (Performance)
+>>>4 byte 0x02 SWAVE (Wave)
+>>>4 byte 0x03 SWTBL (Wave control table)
+>>>4 byte 0x04 SVT (Velocity Curve)
+>>>4 byte 0x05 STT (Tuning Table)
+>>>4 byte 0x06 SGLB (Global Parameters)
+>>>4 byte 0x07 SARRMAP (Performance Program Change Map)
+>>>4 byte 0x08 SBPRMAP (Sound Program Change Map)
+>>>4 byte 0x09 SBPRPAR (Sound Parameter)
+>>>4 byte 0x0A SARRPAR (Performance Parameter)
+>>>4 byte 0x0B SINSPAR (Instrument/External Parameter)
+>>>4 byte 0x0F SBULK (Bulk Switch on/off)
+
+# Japanese Group
+>1 byte 0x40 Kawai
+>>3 byte 0x20 K1
+>>3 byte 0x22 K4
+
+>1 byte 0x41 Roland
+>>3 byte 0x14 D-50
+>>3 byte 0x2b U-220
+>>3 byte 0x02 TR-707
+
+>1 byte 0x42 Korg
+>>3 byte 0x19 M1
+
+>1 byte 0x43 Yamaha
+>1 byte 0x44 Casio
+>1 byte 0x46 Kamiya
+>1 byte 0x47 Akai
+#>1 byte 0x48 Victor
+>1 byte 0x48 Victor Company of Japan. Ltd.
+>1 byte 0x49 Mesosha
+>1 byte 0x4b Fujitsu
+>1 byte 0x4c Sony
+>1 byte 0x4e Teac
+>1 byte 0x50 Matsushita
+>1 byte 0x51 Fostex
+#>1 byte 0x52 Zoom
+>1 byte 0x52 Zoom Corporation
+>1 byte 0x54 Matsushita
+>1 byte 0x57 Acoustic tech. lab.
+# https://www.midi.org/techspecs/manid.php
+>1 belong&0xffffff00 0x00007400 Ta Horng
+>1 belong&0xffffff00 0x00007500 e-Tek
+>1 belong&0xffffff00 0x00007600 E-Voice
+>1 belong&0xffffff00 0x00007700 Midisoft
+>1 belong&0xffffff00 0x00007800 Q-Sound
+>1 belong&0xffffff00 0x00007900 Westrex
+>1 belong&0xffffff00 0x00007a00 Nvidia*
+>1 belong&0xffffff00 0x00007b00 ESS
+>1 belong&0xffffff00 0x00007c00 Mediatrix
+>1 belong&0xffffff00 0x00007d00 Brooktree
+>1 belong&0xffffff00 0x00007e00 Otari
+>1 belong&0xffffff00 0x00007f00 Key Electronics
+>1 belong&0xffffff00 0x00010000 Shure
+>1 belong&0xffffff00 0x00010100 AuraSound
+>1 belong&0xffffff00 0x00010200 Crystal
+>1 belong&0xffffff00 0x00010300 Rockwell
+>1 belong&0xffffff00 0x00010400 Silicon Graphics
+>1 belong&0xffffff00 0x00010500 Midiman
+>1 belong&0xffffff00 0x00010600 PreSonus
+>1 belong&0xffffff00 0x00010800 Topaz
+>1 belong&0xffffff00 0x00010900 Cast Lightning
+>1 belong&0xffffff00 0x00010a00 Microsoft
+>1 belong&0xffffff00 0x00010b00 Sonic Foundry
+>1 belong&0xffffff00 0x00010c00 Line 6
+>1 belong&0xffffff00 0x00010d00 Beatnik Inc.
+>1 belong&0xffffff00 0x00010e00 Van Koerving
+>1 belong&0xffffff00 0x00010f00 Altech Systems
+>1 belong&0xffffff00 0x00011000 S & S Research
+>1 belong&0xffffff00 0x00011100 VLSI Technology
+>1 belong&0xffffff00 0x00011200 Chromatic
+>1 belong&0xffffff00 0x00011300 Sapphire
+>1 belong&0xffffff00 0x00011400 IDRC
+>1 belong&0xffffff00 0x00011500 Justonic Tuning
+>1 belong&0xffffff00 0x00011600 TorComp
+>1 belong&0xffffff00 0x00011700 Newtek Inc.
+>1 belong&0xffffff00 0x00011800 Sound Sculpture
+>1 belong&0xffffff00 0x00011900 Walker Technical
+>1 belong&0xffffff00 0x00011a00 Digital Harmony
+>1 belong&0xffffff00 0x00011b00 InVision
+>1 belong&0xffffff00 0x00011c00 T-Square
+>1 belong&0xffffff00 0x00011d00 Nemesys
+>1 belong&0xffffff00 0x00011e00 DBX
+>1 belong&0xffffff00 0x00011f00 Syndyne
+>1 belong&0xffffff00 0x00012000 Bitheadz
+>1 belong&0xffffff00 0x00012100 Cakewalk
+>1 belong&0xffffff00 0x00012200 Staccato
+>1 belong&0xffffff00 0x00012300 National Semicon.
+>1 belong&0xffffff00 0x00012400 Boom Theory
+>1 belong&0xffffff00 0x00012500 Virtual DSP Corp
+>1 belong&0xffffff00 0x00012600 Antares
+>1 belong&0xffffff00 0x00012700 Angel Software
+>1 belong&0xffffff00 0x00012800 St Louis Music
+>1 belong&0xffffff00 0x00012900 Lyrrus dba G-VOX
+>1 belong&0xffffff00 0x00012a00 Ashley Audio
+>1 belong&0xffffff00 0x00012b00 Vari-Lite
+>1 belong&0xffffff00 0x00012c00 Summit Audio
+>1 belong&0xffffff00 0x00012d00 Aureal Semicon.
+>1 belong&0xffffff00 0x00012e00 SeaSound
+>1 belong&0xffffff00 0x00012f00 U.S. Robotics
+>1 belong&0xffffff00 0x00013000 Aurisis
+>1 belong&0xffffff00 0x00013100 Nearfield Multimedia
+>1 belong&0xffffff00 0x00013200 FM7 Inc.
+>1 belong&0xffffff00 0x00013300 Swivel Systems
+>1 belong&0xffffff00 0x00013400 Hyperactive
+>1 belong&0xffffff00 0x00013500 MidiLite
+>1 belong&0xffffff00 0x00013600 Radical
+>1 belong&0xffffff00 0x00013700 Roger Linn
+>1 belong&0xffffff00 0x00013800 Helicon
+>1 belong&0xffffff00 0x00013900 Event
+>1 belong&0xffffff00 0x00013a00 Sonic Network
+>1 belong&0xffffff00 0x00013b00 Realtime Music
+>1 belong&0xffffff00 0x00013c00 Apogee Digital
+
+>1 belong&0xffffff00 0x00202b00 Medeli Electronics
+>1 belong&0xffffff00 0x00202c00 Charlie Lab
+>1 belong&0xffffff00 0x00202d00 Blue Chip Music
+>1 belong&0xffffff00 0x00202e00 BEE OH Corp
+>1 belong&0xffffff00 0x00202f00 LG Semicon America
+>1 belong&0xffffff00 0x00203000 TESI
+>1 belong&0xffffff00 0x00203100 EMAGIC
+>1 belong&0xffffff00 0x00203200 Behringer
+>1 belong&0xffffff00 0x00203300 Access Music
+>1 belong&0xffffff00 0x00203400 Synoptic
+>1 belong&0xffffff00 0x00203500 Hanmesoft Corp
+>1 belong&0xffffff00 0x00203600 Terratec
+>1 belong&0xffffff00 0x00203700 Proel SpA
+>1 belong&0xffffff00 0x00203800 IBK MIDI
+>1 belong&0xffffff00 0x00203900 IRCAM
+>1 belong&0xffffff00 0x00203a00 Propellerhead Software
+>1 belong&0xffffff00 0x00203b00 Red Sound Systems
+>1 belong&0xffffff00 0x00203c00 Electron ESI AB
+>1 belong&0xffffff00 0x00203d00 Sintefex Audio
+>1 belong&0xffffff00 0x00203e00 Music and More
+>1 belong&0xffffff00 0x00203f00 Amsaro
+>1 belong&0xffffff00 0x00204000 CDS Advanced Technology
+>1 belong&0xffffff00 0x00204100 Touched by Sound
+>1 belong&0xffffff00 0x00204200 DSP Arts
+>1 belong&0xffffff00 0x00204300 Phil Rees Music
+>1 belong&0xffffff00 0x00204400 Stamer Musikanlagen GmbH
+>1 belong&0xffffff00 0x00204500 Soundart
+>1 belong&0xffffff00 0x00204600 C-Mexx Software
+>1 belong&0xffffff00 0x00204700 Klavis Tech.
+>1 belong&0xffffff00 0x00204800 Noteheads AB
+
+# Update: Joerg Jenderek; January 2022
+>1 byte 0x00 ID EXTENSIONS
+>1 byte 0x13 Digidesign Inc.
+>1 byte 0x1e Key Concepts
+>1 byte 0x20 Passac
+>1 byte 0x23 Stepp
+>1 byte 0x2d Neve
+>1 byte 0x2e Soundtracs Ltd.
+>1 byte 0x32 Drawmer
+>1 byte 0x34 Audio Architecture
+>1 byte 0x35 Generalmusic Corp SpA
+>1 byte 0x36 Cheetah Marketing
+>1 byte 0x37 C.T.M.
+>1 byte 0x38 Simmons UK
+>1 byte 0x3a Steinberg
+>1 byte 0x3b Wersi GmbH
+>1 byte 0x3c AVAB Niethammer AB
+>1 byte 0x3d Digigram
+>1 byte 0x3f Quasimidi
+#
+>1 byte 0x40 Kawai Musical Instruments MFG. CO. Ltd
+#>1 byte 0x45 foo
+#>1 byte 0x4a foo
+#>1 byte 0x4d foo
+#>1 byte 0x4f foo
+#>1 byte 0x53 foo
+>1 byte 0x55 Suzuki Musical Instruments MFG. Co. Ltd.
+>1 byte 0x56 Fuji Sound Corporation Ltd.
+#>1 byte 0x58 foo
+>1 byte 0x59 Faith. Inc.
+>1 byte 0x5a Internet Corporation
+#>1 byte 0x5b foo
+>1 byte 0x5c Seekers Co. Ltd.
+#>1 byte 0x5d foo
+#>1 byte 0x5e foo
+>1 byte 0x5f SD Card Association
+# Reserved for other uses for 60H to 7FH
+# URL: https://www.philscomputerlab.com/roland-midi-emulator-project-20.html
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/s/syx--midiemu.trid.xml
+# Note: called by TrID "MIDI Emulator Project SysEx preset command"
+>1 byte 0x66 MIDI Emulator
+# https://electronicmusic.fandom.com/wiki/List_of_MIDI_Manufacturer_IDs
+# Educational, prototyping, test, private use and experimentation
+>1 byte 0x7D PROTOTYPING
+# universal non-real-time (sample dump, tuning table, etc.)
+>1 byte 0x7E UNIVERSAL
+# universal real time (MIDI time code, MIDI Machine control, etc.)
+>1 byte 0x7F universal real time
+# display information about End Of eXclusive byte (EOX=F7)
+#>2 ubyte 0xF7 \b, at 2 EOX
+#>3 ubyte 0xF7 \b, at 3 EOX
+# https://tttapa.github.io/Control-Surface-doc/new-input/Doxygen/d2/d93/SysEx-Send-Receive_8ino-example.html
+>4 ubyte 0xF7 \b, at 4 EOX
+# http://www.1manband.nl/tutorials2/sysex.htm
+>5 ubyte 0xF7 \b, at 5 EOX
+# http://www.somascape.org/midi/tech/mfile.html#sysex
+>6 ubyte 0xF7 \b, at 6 EOX
+#
+>7 ubyte 0xF7 \b, at 7 EOX
+# https://webmidijs.org/forum/discussion/34/how-to-send-or-receive-system-exclusive-messages
+>8 ubyte 0xF7 \b, at 8 EOX
+#
+>9 ubyte 0xF7 \b, at 9 EOX
+# https://www.chd-el.cz/wp-content/uploads/845010_syxcom.pdf
+>10 ubyte 0xF7 \b, at 10 EOX
+# https://stackoverflow.com/questions/52906076/handling-midi-the-input-of-multiple-system-exclusive-messages-in-vb
+>11 ubyte 0xF7 \b, at 11 EOX
+# https://www.2writers.com/eddie/TutSysEx.htm
+>12 ubyte 0xF7 \b, at 12 EOX
+>13 ubyte 0xF7 \b, at 13 EOX
+# http://www.chromakinetics.com/handsonic/rolSysEx.htm
+>14 ubyte 0xF7 \b, at 14 EOX
+#>15 ubyte 0xF7 \b, at 15 EOX
+
+0 string T707 Roland TR-707 Data
diff --git a/contrib/libs/libmagic/magic/Magdir/tcl b/contrib/libs/libmagic/magic/Magdir/tcl
new file mode 100644
index 0000000000..edc3ec42b4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/tcl
@@ -0,0 +1,29 @@
+#------------------------------------------------------------------------------
+# file: file(1) magic for Tcl scripting language
+# URL: https://www.tcl.tk/
+# From: gustaf neumann
+
+# Tcl scripts
+0 search/1/w #!\ /usr/bin/tcl Tcl script text executable
+!:mime text/x-tcl
+0 search/1/w #!\ /usr/local/bin/tcl Tcl script text executable
+!:mime text/x-tcl
+0 search/1 #!/usr/bin/env\ tcl Tcl script text executable
+!:mime text/x-tcl
+0 search/1 #!\ /usr/bin/env\ tcl Tcl script text executable
+!:mime text/x-tcl
+0 search/1/w #!\ /usr/bin/wish Tcl/Tk script text executable
+!:mime text/x-tcl
+0 search/1/w #!\ /usr/local/bin/wish Tcl/Tk script text executable
+!:mime text/x-tcl
+0 search/1 #!/usr/bin/env\ wish Tcl/Tk script text executable
+!:mime text/x-tcl
+0 search/1 #!\ /usr/bin/env\ wish Tcl/Tk script text executable
+!:mime text/x-tcl
+
+# check the first line
+0 search/1 package\ req
+>0 regex \^package[\ \t]+req Tcl script
+# not 'p', check other lines
+0 search/1 !p
+>0 regex \^package[\ \t]+req Tcl script
diff --git a/contrib/libs/libmagic/magic/Magdir/teapot b/contrib/libs/libmagic/magic/Magdir/teapot
new file mode 100644
index 0000000000..b6577b6f28
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/teapot
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# $File: teapot,v 1.4 2009/09/19 16:28:12 christos Exp $
+# teapot: file(1) magic for "teapot" spreadsheet
+#
+0 string #!teapot\012xdr teapot work sheet (XDR format)
diff --git a/contrib/libs/libmagic/magic/Magdir/terminfo b/contrib/libs/libmagic/magic/Magdir/terminfo
new file mode 100644
index 0000000000..41704eb559
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/terminfo
@@ -0,0 +1,63 @@
+
+#------------------------------------------------------------------------------
+# $File: terminfo,v 1.13 2022/11/21 22:25:37 christos Exp $
+# terminfo: file(1) magic for terminfo
+#
+# URL: https://invisible-island.net/ncurses/man/term.5.html
+# URL: https://invisible-island.net/ncurses/man/scr_dump.5.html
+#
+# Workaround for Targa image type by Joerg Jenderek
+# GRR: line below too general as it catches also
+# Targa image type 1 with 26 long identification field
+# and HELP.DSK
+0 string \032\001
+# 5th character of terminal name list, but not Targa image pixel size (15 16 24 32)
+>16 ubyte >32
+# namelist, if more than 1 separated by "|" like "st|stterm| simpleterm 0.4.1"
+>>12 regex \^[a-zA-Z0-9][a-zA-Z0-9.][^|]* Compiled terminfo entry "%-s"
+!:mime application/x-terminfo
+# no extension
+#!:ext
+#
+#------------------------------------------------------------------------------
+# The following was added for ncurses6 development:
+#------------------------------------------------------------------------------
+#
+0 string \036\002
+# imitate the legacy compiled-format, to get the entry-name printed
+>16 ubyte >32
+# namelist, if more than 1 separated by "|" like "st|stterm| simpleterm 0. 4.1"
+>>12 regex \^[a-zA-Z0-9][a-zA-Z0-9.][^|]* Compiled 32-bit terminfo entry "%-s"
+!:mime application/x-terminfo2
+#
+# While the compiled terminfo uses little-endian format regardless of
+# platform, SystemV screen dumps do not. They came later, and that detail was
+# overlooked.
+#
+# AIX and HPUX use the SVr4 big-endian format
+# Solaris uses the SVr3 formats (sparc and x86 differ endian-ness)
+0 beshort 0433 SVr2 curses screen image, big-endian
+# GRR: line below too general as it catches Commodore C128 program (crc32.prg XLINK.PRG) with start address 1C01h handled by ./c64
+0 beshort 0434 SVr3 curses screen image, big-endian
+0 beshort 0435 SVr4 curses screen image, big-endian
+#
+0 leshort 0433 SVr2 curses screen image, little-endian
+0 leshort 0434 SVr3 curses screen image, little-endian
+0 leshort 0435 SVr4 curses screen image, little-endian
+#
+# Rather than SVr4, Solaris "xcurses" writes this header:
+0 regex \^MAX=[0-9]+,[0-9]+$
+>1 regex \^BEG=[0-9]+,[0-9]+$
+>2 regex \^SCROLL=[0-9]+,[0-9]+$
+>3 regex \^VMIN=[0-9]+$
+>4 regex \^VTIME=[0-9]+$
+>5 regex \^FLAGS=0x[[:xdigit:]]+$
+>6 regex \^FG=[0-9],[0-9]+$
+>7 regex \^BG=[0-9]+,[0-9]+, Solaris xcurses screen image
+#
+# ncurses5 (and before) did not use a magic number, making screen dumps "data".
+# ncurses6 (2015) uses this format, ignoring byte-order
+0 string \210\210\210\210ncurses ncurses6 screen image
+#
+# PDCurses added this in 2005
+0 string PDC\001 PDCurses screen image
diff --git a/contrib/libs/libmagic/magic/Magdir/tex b/contrib/libs/libmagic/magic/Magdir/tex
new file mode 100644
index 0000000000..e66f8ffdce
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/tex
@@ -0,0 +1,141 @@
+
+#------------------------------------------------------------------------------
+# $File: tex,v 1.22 2022/12/21 16:50:04 christos Exp $
+# tex: file(1) magic for TeX files
+#
+# XXX - needs byte-endian stuff (big-endian and little-endian DVI?)
+#
+# From <conklin@talisman.kaleida.com>
+
+# Although we may know the offset of certain text fields in TeX DVI
+# and font files, we can't use them reliably because they are not
+# zero terminated. [but we do anyway, christos]
+0 string \367\002
+>(14.b+15) string \213
+>>14 pstring >\0 TeX DVI file (%s)
+!:mime application/x-dvi
+0 string \367\203 TeX generic font data
+0 string \367\131 TeX packed font data
+>3 string >\0 (%s)
+0 string \367\312
+>(2.b+11) string \363 TeX virtual font data
+0 search/1 This\ is\ TeX, TeX transcript text
+0 search/1 This\ is\ METAFONT, METAFONT transcript text
+
+# There is no way to detect TeX Font Metric (*.tfm) files without
+# breaking them apart and reading the data. The following patterns
+# match most *.tfm files generated by METAFONT or afm2tfm.
+2 string \000\021 TeX font metric data
+!:mime application/x-tex-tfm
+>33 string >\0 (%s)
+2 string \000\022 TeX font metric data
+!:mime application/x-tex-tfm
+>33 string >\0 (%s)
+
+# Texinfo and GNU Info, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 search/1 \\input\ texinfo Texinfo source text
+!:mime text/x-texinfo
+0 search/1 This\ is\ Info\ file GNU Info text
+!:mime text/x-info
+
+# TeX documents, from Daniel Quinlan (quinlan@yggdrasil.com)
+0 search/4096 \\input TeX document text
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\begin LaTeX document text
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\section LaTeX document text
+!:mime text/x-tex
+!:strength + 18
+0 search/4096 \\setlength LaTeX document text
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\documentstyle LaTeX document text
+!:mime text/x-tex
+!:strength + 18
+0 search/4096 \\chapter LaTeX document text
+!:mime text/x-tex
+!:strength + 18
+0 search/4096 \\documentclass LaTeX 2e document text
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\relax LaTeX auxiliary file
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 \\contentsline LaTeX table of contents
+!:mime text/x-tex
+!:strength + 15
+0 search/4096 %\ -*-latex-*- LaTeX document text
+!:mime text/x-tex
+
+# Tex document, from Hendrik Scholz <hendrik@scholz.net>
+0 search/1 \\ifx TeX document text
+
+# Index and glossary files
+0 search/4096 \\indexentry LaTeX raw index file
+0 search/4096 \\begin{theindex} LaTeX sorted index
+0 search/4096 \\glossaryentry LaTeX raw glossary
+0 search/4096 \\begin{theglossary} LaTeX sorted glossary
+0 search/4096 This\ is\ makeindex Makeindex log file
+
+# End of TeX
+
+#------------------------------------------------------------------------------
+# file(1) magic for BibTex text files
+# From Hendrik Scholz <hendrik@scholz.net>
+
+0 search/1/c @article{ BibTeX text file
+0 search/1/c @book{ BibTeX text file
+0 search/1/c @inbook{ BibTeX text file
+0 search/1/c @incollection{ BibTeX text file
+0 search/1/c @inproceedings{ BibTeX text file
+0 search/1/c @manual{ BibTeX text file
+0 search/1/c @misc{ BibTeX text file
+0 search/1/c @preamble{ BibTeX text file
+0 search/1/c @phdthesis{ BibTeX text file
+0 search/1/c @techreport{ BibTeX text file
+0 search/1/c @unpublished{ BibTeX text file
+
+73 search/1 %%%\ \ BibTeX-file{ BibTex text file (with full header)
+
+73 search/1 %%%\ \ @BibTeX-style-file{ BibTeX style text file (with full header)
+
+0 search/1 %\ BibTeX\ standard\ bibliography\ BibTeX standard bibliography style text file
+
+0 search/1 %\ BibTeX\ ` BibTeX custom bibliography style text file
+
+0 search/1 @c\ @mapfile{ TeX font aliases text file
+
+0 string #LyX LyX document text
+
+# ConTeXt documents
+# https://wiki.contextgarden.net/
+0 search/4096 \\setupcolors[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\definecolor[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupinteraction[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\useURL[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setuppapersize[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setuplayout[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupfooter[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupfootertexts[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setuppagenumbering[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupbodyfont[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setuphead[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupitemize[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupwhitespace[ ConTeXt document text
+!:strength + 15
+0 search/4096 \\setupindenting[ ConTeXt document text
+!:strength + 15
diff --git a/contrib/libs/libmagic/magic/Magdir/tgif b/contrib/libs/libmagic/magic/Magdir/tgif
new file mode 100644
index 0000000000..e80b3a76cb
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/tgif
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# $File: tgif,v 1.7 2010/09/20 19:03:46 rrt Exp $
+# file(1) magic for tgif(1) files
+# From Hendrik Scholz <hendrik@scholz.net>
+0 string %TGIF\ Tgif file version
+>6 string x %s
diff --git a/contrib/libs/libmagic/magic/Magdir/ti-8x b/contrib/libs/libmagic/magic/Magdir/ti-8x
new file mode 100644
index 0000000000..b05c5c9c3f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/ti-8x
@@ -0,0 +1,239 @@
+
+#------------------------------------------------------------------------------
+# $File: ti-8x,v 1.8 2020/02/12 22:13:01 christos Exp $
+# ti-8x: file(1) magic for the TI-8x and TI-9x Graphing Calculators.
+#
+# From: Ryan McGuire (rmcguire@freenet.columbus.oh.us).
+#
+# Update: Romain Lievin (roms@lpg.ticalc.org).
+#
+# NOTE: This list is not complete.
+# Files for the TI-80 and TI-81 are pretty rare. I'm not going to put the
+# program/group magic numbers in here because I cannot find any.
+0 string **TI80** TI-80 Graphing Calculator File.
+0 string **TI81** TI-81 Graphing Calculator File.
+#
+# Magic Numbers for the TI-73
+#
+0 string **TI73** TI-73 Graphing Calculator
+>0x00003B byte 0x00 (real number)
+>0x00003B byte 0x01 (list)
+>0x00003B byte 0x02 (matrix)
+>0x00003B byte 0x03 (equation)
+>0x00003B byte 0x04 (string)
+>0x00003B byte 0x05 (program)
+>0x00003B byte 0x06 (assembly program)
+>0x00003B byte 0x07 (picture)
+>0x00003B byte 0x08 (gdb)
+>0x00003B byte 0x0C (complex number)
+>0x00003B byte 0x0F (window settings)
+>0x00003B byte 0x10 (zoom)
+>0x00003B byte 0x11 (table setup)
+>0x00003B byte 0x13 (backup)
+
+# Magic Numbers for the TI-82
+#
+0 string **TI82** TI-82 Graphing Calculator
+>0x00003B byte 0x00 (real)
+>0x00003B byte 0x01 (list)
+>0x00003B byte 0x02 (matrix)
+>0x00003B byte 0x03 (Y-variable)
+>0x00003B byte 0x05 (program)
+>0x00003B byte 0x06 (protected prgm)
+>0x00003B byte 0x07 (picture)
+>0x00003B byte 0x08 (gdb)
+>0x00003B byte 0x0B (window settings)
+>0x00003B byte 0x0C (window settings)
+>0x00003B byte 0x0D (table setup)
+>0x00003B byte 0x0E (screenshot)
+>0x00003B byte 0x0F (backup)
+#
+# Magic Numbers for the TI-83
+#
+0 string **TI83** TI-83 Graphing Calculator
+>0x00003B byte 0x00 (real)
+>0x00003B byte 0x01 (list)
+>0x00003B byte 0x02 (matrix)
+>0x00003B byte 0x03 (Y-variable)
+>0x00003B byte 0x04 (string)
+>0x00003B byte 0x05 (program)
+>0x00003B byte 0x06 (protected prgm)
+>0x00003B byte 0x07 (picture)
+>0x00003B byte 0x08 (gdb)
+>0x00003B byte 0x0B (window settings)
+>0x00003B byte 0x0C (window settings)
+>0x00003B byte 0x0D (table setup)
+>0x00003B byte 0x0E (screenshot)
+>0x00003B byte 0x13 (backup)
+#
+# Magic Numbers for the TI-83+
+#
+0 string **TI83F* TI-83+ Graphing Calculator
+>0x00003B byte 0x00 (real number)
+>0x00003B byte 0x01 (list)
+>0x00003B byte 0x02 (matrix)
+>0x00003B byte 0x03 (equation)
+>0x00003B byte 0x04 (string)
+>0x00003B byte 0x05 (program)
+>0x00003B byte 0x06 (assembly program)
+>0x00003B byte 0x07 (picture)
+>0x00003B byte 0x08 (gdb)
+>0x00003B byte 0x0C (complex number)
+>0x00003B byte 0x0F (window settings)
+>0x00003B byte 0x10 (zoom)
+>0x00003B byte 0x11 (table setup)
+>0x00003B byte 0x13 (backup)
+>0x00003B byte 0x15 (application variable)
+>0x00003B byte 0x17 (group of variable)
+
+#
+# Magic Numbers for the TI-85
+#
+0 string **TI85** TI-85 Graphing Calculator
+>0x00003B byte 0x00 (real number)
+>0x00003B byte 0x01 (complex number)
+>0x00003B byte 0x02 (real vector)
+>0x00003B byte 0x03 (complex vector)
+>0x00003B byte 0x04 (real list)
+>0x00003B byte 0x05 (complex list)
+>0x00003B byte 0x06 (real matrix)
+>0x00003B byte 0x07 (complex matrix)
+>0x00003B byte 0x08 (real constant)
+>0x00003B byte 0x09 (complex constant)
+>0x00003B byte 0x0A (equation)
+>0x00003B byte 0x0C (string)
+>0x00003B byte 0x0D (function GDB)
+>0x00003B byte 0x0E (polar GDB)
+>0x00003B byte 0x0F (parametric GDB)
+>0x00003B byte 0x10 (diffeq GDB)
+>0x00003B byte 0x11 (picture)
+>0x00003B byte 0x12 (program)
+>0x00003B byte 0x13 (range)
+>0x00003B byte 0x17 (window settings)
+>0x00003B byte 0x18 (window settings)
+>0x00003B byte 0x19 (window settings)
+>0x00003B byte 0x1A (window settings)
+>0x00003B byte 0x1B (zoom)
+>0x00003B byte 0x1D (backup)
+>0x00003B byte 0x1E (unknown)
+>0x00003B byte 0x2A (equation)
+>0x000032 string ZS4 - ZShell Version 4 File.
+>0x000032 string ZS3 - ZShell Version 3 File.
+#
+# Magic Numbers for the TI-86
+#
+0 string **TI86** TI-86 Graphing Calculator
+>0x00003B byte 0x00 (real number)
+>0x00003B byte 0x01 (complex number)
+>0x00003B byte 0x02 (real vector)
+>0x00003B byte 0x03 (complex vector)
+>0x00003B byte 0x04 (real list)
+>0x00003B byte 0x05 (complex list)
+>0x00003B byte 0x06 (real matrix)
+>0x00003B byte 0x07 (complex matrix)
+>0x00003B byte 0x08 (real constant)
+>0x00003B byte 0x09 (complex constant)
+>0x00003B byte 0x0A (equation)
+>0x00003B byte 0x0C (string)
+>0x00003B byte 0x0D (function GDB)
+>0x00003B byte 0x0E (polar GDB)
+>0x00003B byte 0x0F (parametric GDB)
+>0x00003B byte 0x10 (diffeq GDB)
+>0x00003B byte 0x11 (picture)
+>0x00003B byte 0x12 (program)
+>0x00003B byte 0x13 (range)
+>0x00003B byte 0x17 (window settings)
+>0x00003B byte 0x18 (window settings)
+>0x00003B byte 0x19 (window settings)
+>0x00003B byte 0x1A (window settings)
+>0x00003B byte 0x1B (zoom)
+>0x00003B byte 0x1D (backup)
+>0x00003B byte 0x1E (unknown)
+>0x00003B byte 0x2A (equation)
+#
+# Magic Numbers for the TI-89
+#
+0 string **TI89** TI-89 Graphing Calculator
+>0x000048 byte 0x00 (expression)
+>0x000048 byte 0x04 (list)
+>0x000048 byte 0x06 (matrix)
+>0x000048 byte 0x0A (data)
+>0x000048 byte 0x0B (text)
+>0x000048 byte 0x0C (string)
+>0x000048 byte 0x0D (graphic data base)
+>0x000048 byte 0x0E (figure)
+>0x000048 byte 0x10 (picture)
+>0x000048 byte 0x12 (program)
+>0x000048 byte 0x13 (function)
+>0x000048 byte 0x14 (macro)
+>0x000048 byte 0x1C (zipped)
+>0x000048 byte 0x21 (assembler)
+#
+# Magic Numbers for the TI-92
+#
+0 string **TI92** TI-92 Graphing Calculator
+>0x000048 byte 0x00 (expression)
+>0x000048 byte 0x04 (list)
+>0x000048 byte 0x06 (matrix)
+>0x000048 byte 0x0A (data)
+>0x000048 byte 0x0B (text)
+>0x000048 byte 0x0C (string)
+>0x000048 byte 0x0D (graphic data base)
+>0x000048 byte 0x0E (figure)
+>0x000048 byte 0x10 (picture)
+>0x000048 byte 0x12 (program)
+>0x000048 byte 0x13 (function)
+>0x000048 byte 0x14 (macro)
+>0x000048 byte 0x1D (backup)
+#
+# Magic Numbers for the TI-92+/V200
+#
+0 string **TI92P* TI-92+/V200 Graphing Calculator
+>0x000048 byte 0x00 (expression)
+>0x000048 byte 0x04 (list)
+>0x000048 byte 0x06 (matrix)
+>0x000048 byte 0x0A (data)
+>0x000048 byte 0x0B (text)
+>0x000048 byte 0x0C (string)
+>0x000048 byte 0x0D (graphic data base)
+>0x000048 byte 0x0E (figure)
+>0x000048 byte 0x10 (picture)
+>0x000048 byte 0x12 (program)
+>0x000048 byte 0x13 (function)
+>0x000048 byte 0x14 (macro)
+>0x000048 byte 0x1C (zipped)
+>0x000048 byte 0x21 (assembler)
+#
+# Magic Numbers for the TI-73/83+/89/92+/V200 FLASH upgrades
+#
+#0x0000016 string Advanced TI-XX Graphing Calculator (FLASH)
+0 string **TIFL** TI-XX Graphing Calculator (FLASH)
+>8 byte >0 - Revision %d
+>>9 byte x \b.%d,
+>12 byte >0 Revision date %02x
+>>13 byte x \b/%02x
+>>14 beshort x \b/%04x,
+>17 string >/0 name: '%s',
+>48 byte 0x74 device: TI-73,
+>48 byte 0x73 device: TI-83+,
+>48 byte 0x98 device: TI-89,
+>48 byte 0x88 device: TI-92+,
+>49 byte 0x23 type: OS upgrade,
+>49 byte 0x24 type: application,
+>49 byte 0x25 type: certificate,
+>49 byte 0x3e type: license,
+>74 lelong >0 size: %d bytes
+
+# VTi & TiEmu skins (TI Graphing Calculators).
+# From: Romain Lievin (roms@lpg.ticalc.org).
+# Magic Numbers for the VTi skins
+0 string VTI Virtual TI skin
+>3 string v - Version
+>>4 byte >0 \b %c
+>>6 byte x \b.%c
+# Magic Numbers for the TiEmu skins
+0 string TiEmu TiEmu skin
+>6 string v - Version
+>>7 byte >0 \b %c
+>>9 byte x \b.%c
+>>10 byte x \b%c
diff --git a/contrib/libs/libmagic/magic/Magdir/timezone b/contrib/libs/libmagic/magic/Magdir/timezone
new file mode 100644
index 0000000000..84e9081667
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/timezone
@@ -0,0 +1,42 @@
+
+#------------------------------------------------------------------------------
+# $File: timezone,v 1.13 2021/07/21 17:57:20 christos Exp $
+# timezone: file(1) magic for timezone data
+#
+# from Daniel Quinlan (quinlan@yggdrasil.com)
+# this should work on Linux, SunOS, and maybe others
+# Added new official magic number for recent versions of the Olson code
+0 name timezone
+>4 byte 0 \b, old version
+>4 byte >0 \b, version %c
+>20 belong 0 \b, no gmt time flags
+>20 belong 1 \b, 1 gmt time flag
+>20 belong >1 \b, %d gmt time flags
+>24 belong 0 \b, no std time flags
+>24 belong 1 \b, 1 std time flag
+>24 belong >1 \b, %d std time flags
+>28 belong 0 \b, no leap seconds
+>28 belong 1 \b, 1 leap second
+>28 belong >1 \b, %d leap seconds
+>32 belong 0 \b, no transition times
+>32 belong 1 \b, 1 transition time
+>32 belong >1 \b, %d transition times
+>36 belong 0 \b, no local time types
+>36 belong 1 \b, 1 local time type
+>36 belong >1 \b, %d local time types
+>40 belong 0 \b, no abbreviation chars
+>40 belong 1 \b, 1 abbreviation char
+>40 belong >1 \b, %d abbreviation chars
+
+0 string TZif timezone data
+>51 string TZif \b(slim)
+>>51 use timezone
+>51 default x \b(fat)
+>>0 use timezone
+
+0 string \0\0\0\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\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0 old timezone data
+0 string \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0 old timezone data
diff --git a/contrib/libs/libmagic/magic/Magdir/tplink b/contrib/libs/libmagic/magic/Magdir/tplink
new file mode 100644
index 0000000000..1b4ef0f336
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/tplink
@@ -0,0 +1,95 @@
+
+#------------------------------------------------------------------------------
+# $File: tplink,v 1.8 2023/05/15 16:41:02 christos Exp $
+# tplink: File magic for openwrt firmware files
+
+# URL: https://wiki.openwrt.org/doc/techref/header
+# Reference: https://git.openwrt.org/?p=openwrt.git;a=blob;f=tools/firmware-utils/src/mktplinkfw.c
+# http://mark0.net/download/triddefs_xml.7z/defs/b/bin-tplink-v1.trid.xml
+# Note: called "TP-Link router firmware (v1)" by TrID
+# From: Joerg Jenderek
+# check for valid header version 1 or 2
+0 ulelong <3
+>0 ulelong !0
+# test for header padding with nulls
+>>0x100 long 0
+# skip Norton Commander Cleanup Utility NCCLEAN.INI by looking for valid vendor name
+>>>4 ubelong >0x1F000000
+# skip user.dbt by looking for positive hardware id
+>>>>0x40 ubeshort >0
+# skip cversions.1.db cversions.2.db cversions.3.db inside
+# c:\ProgramData\Microsoft\Windows\Caches
+# with invalid vendor names \240\0\0\0 \140\0\0\0 \040\0\0\0
+>>>>>5 short !0
+>>>>>>0 use firmware-tplink
+
+0 name firmware-tplink
+>0 ubyte x firmware
+!:mime application/x-tplink-bin
+# like: TL-WR1043ND-V1-FW0.0.3-stripped.bin gluon-ffrefugee-0.9.2-tp-link-archer-c5-v1-sysupgrade.bin
+!:ext bin
+# hardware id like 10430001 07410001 09410004 09410006
+>0x40 ubeshort x %x
+>0x42 ubeshort x v%x
+# hardware revision like 1
+>0x44 ubelong !1 (revision %u)
+# vendor_name[24] like OpenWrt or TP-LINK Technologies
+>4 string x %.24s
+# fw_version[36] like r49389 or ver. 1.0
+>0x1c string x %.36s
+# header version 1 or 2
+>0 ubyte !1 V%X
+# ver_hi.ver_mid.ver_lo
+>0x98 long !0 \b, version
+>>0x98 ubeshort x %u
+>>0x9A ubeshort x \b.%u
+>>0x9C ubeshort x \b.%u
+# region code 0~universal 1~US
+>0x48 ubelong x
+#>>0x48 ubelong 0 (universal)
+>>0x48 ubelong 1 (US)
+>>0x48 ubelong >1 (region %u)
+# total length of the firmware. not always true
+>0x7C ubelong x \b, %u bytes or less
+# unknown 1
+>0x48 ubelong !0 \b, UNKNOWN1 %#x
+# md5sum1[16]
+#>0x4c ubequad x \b, MD5 %llx
+#>>0x54 ubequad x \b%llx
+# unknown 2
+>0x5c ubelong !0 \b, UNKNOWN2 %#x
+# md5sum2[16]
+#>0x60 ubequad !0 \b, 2nd MD5 %llx
+#>>0x68 ubequad x \b%llx
+# unknown 3
+>0x70 ubelong !0 \b, UNKNOWN3 %#x
+# kernel load address
+#>0x74 ubelong x \b, %#x load
+# kernel entry point
+#>0x78 ubelong x \b, %#x entry
+# kernel data offset. 200h means direct after header
+>0x80 ubelong x \b, at %#x
+# kernel data length and 1 space
+>0x84 ubelong x %u bytes
+# look for kernel type (gzip compressed vmlinux.bin by ./compress)
+>(0x80.L) indirect x
+# root file system data offset
+# WRONG in 5.35 with above indirect expression
+>0x88 ubelong x \b, at %#x
+# rootfs data length and 1 space
+>0x8C ubelong x %u bytes
+# in 5.32 only true for offset ~< FILE_BYTES_MAX=9 MB defined in ../../src/file.h
+>(0x88.L) indirect x
+# 'qshs' for wr940nv1_en_3_13_7_up(111228).bin
+#>(0x88.L) string x \b, file system '%.4s'
+#>(0x88.L) ubequad x \b, file system %#llx
+# bootloader data offset
+>0x90 ubelong !0 \b, at %#x
+# bootloader data length only reasonable if bootloader offset not null
+>>0x94 ubelong !0 %u bytes
+# pad[354] should be 354 null bytes.
+#>0x9E ubequad !0 \b, padding %#llx
+# But at 0x120 18 non null bytes in examples like
+# wr940nv4_eu_3_16_9_up_boot(160620).bin
+# wr940nv6_us_3_18_1_up_boot(171030).bin
+#>0x120 ubequad !0 \b, other padding %#llx
diff --git a/contrib/libs/libmagic/magic/Magdir/troff b/contrib/libs/libmagic/magic/Magdir/troff
new file mode 100644
index 0000000000..301a40bc34
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/troff
@@ -0,0 +1,44 @@
+
+#------------------------------------------------------------------------------
+# $File: troff,v 1.14 2023/06/01 16:00:46 christos Exp $
+# troff: file(1) magic for *roff
+#
+# updated by Daniel Quinlan (quinlan@yggdrasil.com)
+
+# troff input
+0 search/1 .\\" troff or preprocessor input text
+!:strength +12
+!:mime text/troff
+0 search/1 '\\" troff or preprocessor input text
+!:strength +12
+!:mime text/troff
+0 search/1 '.\\" troff or preprocessor input text
+!:strength +12
+!:mime text/troff
+0 search/1 \\" troff or preprocessor input text
+!:strength +12
+!:mime text/troff
+#0 search/1 ''' troff or preprocessor input text
+#!:mime text/troff
+0 regex/20l \^\\.[A-Za-z][A-Za-z0-9][\ \t] troff or preprocessor input text
+!:strength +12
+!:mime text/troff
+0 regex/20l \^\\.[A-Za-z][A-Za-z0-9]$ troff or preprocessor input text
+!:strength +12
+!:mime text/troff
+
+# ditroff intermediate output text
+0 search/1 x\ T ditroff output text
+>4 search/1 cat for the C/A/T phototypesetter
+>4 search/1 ps for PostScript
+>4 search/1 dvi for DVI
+>4 search/1 ascii for ASCII
+>4 search/1 lj4 for LaserJet 4
+>4 search/1 latin1 for ISO 8859-1 (Latin 1)
+>4 search/1 X75 for xditview at 75dpi
+>>7 search/1 -12 (12pt)
+>4 search/1 X100 for xditview at 100dpi
+>>8 search/1 -12 (12pt)
+
+# output data formats
+0 string \100\357 very old (C/A/T) troff output data
diff --git a/contrib/libs/libmagic/magic/Magdir/tuxedo b/contrib/libs/libmagic/magic/Magdir/tuxedo
new file mode 100644
index 0000000000..191501decf
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/tuxedo
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: tuxedo,v 1.4 2009/09/19 16:28:13 christos Exp $
+# tuxedo: file(1) magic for BEA TUXEDO data files
+#
+# from Ian Springer <ispringer@hotmail.com>
+#
+0 string \0\0\1\236\0\0\0\0\0\0\0\0\0\0\0\0 BEA TUXEDO DES mask data
diff --git a/contrib/libs/libmagic/magic/Magdir/typeset b/contrib/libs/libmagic/magic/Magdir/typeset
new file mode 100644
index 0000000000..e99fe3731b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/typeset
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# $File: typeset,v 1.8 2009/09/19 16:28:13 christos Exp $
+# typeset: file(1) magic for other typesetting
+#
+0 string Interpress/Xerox Xerox InterPress data
+>16 string / (version
+>>17 string >\0 %s)
diff --git a/contrib/libs/libmagic/magic/Magdir/uf2 b/contrib/libs/libmagic/magic/Magdir/uf2
new file mode 100644
index 0000000000..49a86d7640
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/uf2
@@ -0,0 +1,72 @@
+
+#------------------------------------------------------------------------------
+# $File: uf2,v 1.3 2021/04/28 01:00:31 christos Exp $
+# uf2: file(1) magic for UF2 firmware image files
+#
+# https://github.com/microsoft/uf2
+#
+# Created by Blake Ramsdell <blaker@gmail.com>
+
+0 string UF2\n UF2 firmware image
+!:ext uf2
+# This is for checking the other magic numbers, do we want to do that?
+#>4 lelong 0x9E5D5157 howdy
+#>>508 lelong 0x0AB16F30 doody
+>8 lelong &0x0001 \b, not main flash
+>8 lelong &0x1000 \b, file container
+>8 lelong &0x2000 \b, family
+
+# To update the UF2 family data, use this fine command
+#
+# families=`curl \
+# https://raw.githubusercontent.com/microsoft/uf2/master/utils/uf2families.json \
+# | jq -r '.[] | ">>28\tlelong\t\(.id)\t\(.description)"' | sort -n -k 3` && \
+# perl -0777 -i -pe \
+# "s/(### BEGIN UF2 FAMILIES\\n).*(\\n### END UF2 FAMILIES)/\$1$families\$2/s" \
+# uf2
+
+### BEGIN UF2 FAMILIES
+>>28 lelong 0x00ff6919 ST STM32L4xx
+>>28 lelong 0x04240bdf ST STM32L5xx
+>>28 lelong 0x16573617 Microchip (Atmel) ATmega32
+>>28 lelong 0x1851780a Microchip (Atmel) SAML21
+>>28 lelong 0x1b57745f Nordic NRF52
+>>28 lelong 0x1c5f21b0 ESP32
+>>28 lelong 0x1e1f432d ST STM32L1xx
+>>28 lelong 0x202e3a91 ST STM32L0xx
+>>28 lelong 0x21460ff0 ST STM32WLxx
+>>28 lelong 0x2abc77ec NXP LPC55xx
+>>28 lelong 0x300f5633 ST STM32G0xx
+>>28 lelong 0x31d228c6 GD32F350
+>>28 lelong 0x4c71240a ST STM32G4xx
+>>28 lelong 0x4fb2d5bd NXP i.MX RT10XX
+>>28 lelong 0x53b80f00 ST STM32F7xx
+>>28 lelong 0x55114460 Microchip (Atmel) SAMD51
+>>28 lelong 0x57755a57 ST STM32F401
+>>28 lelong 0x5a18069b Cypress FX2
+>>28 lelong 0x5d1a0a2e ST STM32F2xx
+>>28 lelong 0x5ee21072 ST STM32F103
+>>28 lelong 0x647824b6 ST STM32F0xx
+>>28 lelong 0x68ed2b88 Microchip (Atmel) SAMD21
+>>28 lelong 0x6b846188 ST STM32F3xx
+>>28 lelong 0x6d0922fa ST STM32F407
+>>28 lelong 0x6db66082 ST STM32H7xx
+>>28 lelong 0x70d16653 ST STM32WBxx
+>>28 lelong 0x7eab61ed ESP8266
+>>28 lelong 0x7f83e793 NXP KL32L2x
+>>28 lelong 0x8fb060fe ST STM32F407VG
+>>28 lelong 0xada52840 Nordic NRF52840
+>>28 lelong 0xbfdd4eee ESP32-S2
+>>28 lelong 0xc47e5767 ESP32-S3
+>>28 lelong 0xd42ba06c ESP32-C3
+>>28 lelong 0xe48bff56 Raspberry Pi RP2040
+### END UF2 FAMILIES
+
+>>28 default x
+>>>28 lelong x %#08x
+>8 lelong&0x2000 0 \b, file size
+>>28 lelong x %#08x
+>8 lelong &0x4000 \b, MD5 checksum present
+>8 lelong &0x8000 \b, extension tags present
+>12 lelong x \b, address %#08x
+>24 lelong x \b, %u total blocks
diff --git a/contrib/libs/libmagic/magic/Magdir/unicode b/contrib/libs/libmagic/magic/Magdir/unicode
new file mode 100644
index 0000000000..7ca61bacbe
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/unicode
@@ -0,0 +1,15 @@
+
+#------------------------------------------------------------------------------
+# $File: unicode,v 1.7 2019/02/19 20:34:42 christos Exp $
+# Unicode: BOM prefixed text files - Adrian Havill <havill@turbolinux.co.jp>
+# These types are recognised in file_ascmagic so these encodings can be
+# treated by text patterns. Missing types are already dealt with internally.
+#
+0 string +/v8 Unicode text, UTF-7
+0 string +/v9 Unicode text, UTF-7
+0 string +/v+ Unicode text, UTF-7
+0 string +/v/ Unicode text, UTF-7
+0 string \335\163\146\163 Unicode text, UTF-8-EBCDIC
+0 string \000\000\376\377 Unicode text, UTF-32, big-endian
+0 string \377\376\000\000 Unicode text, UTF-32, little-endian
+0 string \016\376\377 Unicode text, SCSU (Standard Compression Scheme for Unicode)
diff --git a/contrib/libs/libmagic/magic/Magdir/unisig b/contrib/libs/libmagic/magic/Magdir/unisig
new file mode 100644
index 0000000000..6212c3871f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/unisig
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: unisig,v 1.1 2020/04/09 19:05:44 christos Exp $
+# unisig: file(1) magic for files carrying a uniform signature (Unisig)
+# From: Lassi Kortela, John Cowan
+# URL: https://github.com/unisig
+#
+0 string \xDC\xDC\x0D\x0A\x1A\x0A\x00 Unisig:
+>7 ubyte =0 UUID
+>>8 guid x %s
+>7 ubyte >0 URI
+>>7 pstring x %s
diff --git a/contrib/libs/libmagic/magic/Magdir/unknown b/contrib/libs/libmagic/magic/Magdir/unknown
new file mode 100644
index 0000000000..578a8ea06d
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/unknown
@@ -0,0 +1,34 @@
+
+#------------------------------------------------------------------------------
+# $File: unknown,v 1.8 2013/01/09 22:37:24 christos Exp $
+# unknown: file(1) magic for unknown machines
+#
+# 0x107 is 0407, 0x108 is 0410, and 0x109 is 0411; those are all PDP-11
+# (executable, pure, and split I&D, respectively), but the PDP-11 version
+# doesn't have the "version %ld", which may be a bogus COFFism (I don't
+# think there was ever COFF for the PDP-11).
+#
+# 0x10B is 0413; that's VAX demand-paged, but this is a short, not a
+# long, as it would be on a VAX. In any case, that could collide with
+# VAX demand-paged files, as the magic number is little-endian on those
+# binaries, so the first 16 bits of the file would contain 0x10B.
+#
+# Therefore, those entries are commented out.
+#
+# 0x10C is 0414 and 0x10E is 0416; those *are* unknown.
+#
+#0 short 0x107 unknown machine executable
+#>8 short >0 not stripped
+#>15 byte >0 - version %ld
+#0 short 0x108 unknown pure executable
+#>8 short >0 not stripped
+#>15 byte >0 - version %ld
+#0 short 0x109 PDP-11 separate I&D
+#>8 short >0 not stripped
+#>15 byte >0 - version %ld
+#0 short 0x10b unknown pure executable
+#>8 short >0 not stripped
+#>15 byte >0 - version %ld
+0 long 0x10c unknown demand paged pure executable
+>16 long >0 not stripped
+0 long 0x10e unknown readable demand paged pure executable
diff --git a/contrib/libs/libmagic/magic/Magdir/usd b/contrib/libs/libmagic/magic/Magdir/usd
new file mode 100644
index 0000000000..356cdf7675
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/usd
@@ -0,0 +1,21 @@
+
+#------------------------------------------------------------------------------
+# $File: usd,v 1.2 2020/05/21 22:17:00 christos Exp $
+#
+# From Christian Schmidbauer
+#
+# https://github.com/PixarAnimationStudios/USD
+
+# USD crate file
+# https://github.com/PixarAnimationStudios/USD/blob/ebac0a8b6703f4fa1c27115f1f013bb9819662f4/pxr/usd/usd/crateFile.h#L441-L450
+0 string PXR-USDC USD crate
+>8 byte x \b, version %x.
+>9 byte x \b%x.
+>10 byte x \b%x
+!:ext usd
+
+# USD ASCII file
+0 string #usda\040 USD ASCII
+>6 string x \b, version %s
+!:mime text/plain
+!:ext usd
diff --git a/contrib/libs/libmagic/magic/Magdir/uterus b/contrib/libs/libmagic/magic/Magdir/uterus
new file mode 100644
index 0000000000..4b9e768b64
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/uterus
@@ -0,0 +1,16 @@
+
+#------------------------------------------------------------------------------
+# $File: uterus,v 1.4 2022/10/31 13:22:26 christos Exp $
+# file(1) magic for uterus files
+# http://freecode.com/projects/uterus
+#
+0 string UTE+ uterus file
+>4 string v \b, version
+>5 byte x %c
+>6 string . \b.
+>7 byte x \b%c
+>8 string \<\> \b, big-endian
+>>16 belong >0 \b, slut size %u
+>8 string \>\< \b, little-endian
+>>16 lelong >0 \b, slut size %u
+>10 byte &8 \b, compressed
diff --git a/contrib/libs/libmagic/magic/Magdir/uuencode b/contrib/libs/libmagic/magic/Magdir/uuencode
new file mode 100644
index 0000000000..df70dc5319
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/uuencode
@@ -0,0 +1,28 @@
+
+#------------------------------------------------------------------------------
+# $File: uuencode,v 1.9 2021/11/13 17:48:10 christos Exp $
+# uuencode: file(1) magic for ASCII-encoded files
+#
+
+# The first line of xxencoded files is identical to that in uuencoded files,
+# but the first character in most subsequent lines is 'h' instead of 'M'.
+# (xxencoding uses lowercase letters in place of most of uuencode's
+# punctuation and survives BITNET gateways better.)
+0 regex/1024 \^begin\040[0-7]{3}\040
+>&0 regex/256 [\012\015]+M[\040-\140]{60}[\012\015]+ uuencoded text
+>&0 regex/256 [\012\015]+h[0-9A-Za-z\053\055]{60}[\012\015]+ xxencoded text
+>&0 default x uuencoded or xxencoded text
+>&0 string >\0 \b, file name "%s"
+
+# btoa(1) is an alternative to uuencode that requires less space.
+0 search/1 xbtoa\ Begin btoa'd text
+
+# ship(1) is another, much cooler alternative to uuencode.
+# Greg Roelofs, newt@uchicago.edu
+0 search/1 $\012ship ship'd binary text
+
+# bencode(8) is used to encode compressed news batches (Bnews/Cnews only?)
+# Greg Roelofs, newt@uchicago.edu
+0 search/1 Decode\ the\ following\ with\ bdeco bencoded News text
+
+# GRR: handle BASE64
diff --git a/contrib/libs/libmagic/magic/Magdir/vacuum-cleaner b/contrib/libs/libmagic/magic/Magdir/vacuum-cleaner
new file mode 100644
index 0000000000..eef78f21ef
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/vacuum-cleaner
@@ -0,0 +1,54 @@
+
+#------------------------------------------------------------------------------
+# $File: vacuum-cleaner,v 1.1 2015/11/14 13:38:35 christos Exp $
+# vacuum cleaner magic by Thomas M. Ott (ThMO)
+#
+# navigation map for LG robot vacuum cleaner models VR62xx, VR64xx, VR63xx
+# file: MAPDATAyyyymmddhhmmss_xxxxxx_cc.blk
+# -> yyyymmdd: year, month, day of cleaning
+# -> hhmmss: hour, minute, second of cleaning
+# -> xxxxxx: 6 digits
+# -> cc: cleaning runs counter
+# size: 136044 bytes
+#
+# struct maphdr {
+# int32_t map_cnt; /* 0: single map */
+# int32_t min_ceil; /* 4: 100 mm == 10 cm == min. ceil */
+# int32_t max_ceil; /* 8: 10000 mm == 100 m == max. ceil */
+# int32_t max_climb; /* 12: 50 mm = 5 cm == max. height to climb */
+# int32_t unknown; /* 16: 50000 ??? */
+# int32_t cell_bytes; /* 20: # of bytes for cells per block */
+# int32_t block_max; /* 24: 1000 == max. # of blocks */
+# int32_t route_max; /* 28: 1000 == max. # of routes */
+# int32_t used_blocks; /* 32: 5/45/33/... == # of block entries used! */
+# int32_t cell_dim; /* 36: 10 == cell dimension */
+# int32_t clock_tick; /* 40: 100 == clock ticks */
+# #if 0
+# struct { /* 44: 1000 blocks for 10x10 cells */
+# int32_t yoffset;
+# int32_t xoffset;
+# int32_t posxy;
+# int32_t timecode;
+# } blocks[ 1000];
+# char cells[ 1000* 100]; /* 16044: 1000 10x10 cells */
+# int16_t routes[ 1000* 10]; /* 116044: 1000 10-routes */
+# #endif
+# };
+
+0 lelong =1
+>4 lelong =100
+>>8 lelong =10000
+>>>12 lelong =50
+>>>>16 lelong =50000
+>>>>>20 lelong =100
+>>>>>>24 lelong =1000
+>>>>>>>28 lelong =1000
+>>>>>>>>36 lelong =10
+>>>>>>>>>40 lelong =100
+>>>>>>>>>>32 lelong x LG robot VR6[234]xx %dm^2 navigation
+>>>>>>>>>>136040 lelong =-1 reuse map data
+>>>>>>>>>>136040 lelong =0 map data
+>>>>>>>>>>136040 lelong >0 spurious map data
+>>>>>>>>>>136040 lelong <-1 spurious map data
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/varied.out b/contrib/libs/libmagic/magic/Magdir/varied.out
new file mode 100644
index 0000000000..01caf07faf
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/varied.out
@@ -0,0 +1,46 @@
+
+#------------------------------------------------------------------------------
+# $File: varied.out,v 1.23 2014/04/30 21:41:02 christos Exp $
+# varied.out: file(1) magic for various USG systems
+#
+# Herewith many of the object file formats used by USG systems.
+# Most have been moved to files for a particular processor,
+# and deleted if they duplicate other entries.
+#
+0 short 0610 Perkin-Elmer executable
+# AMD 29K
+0 beshort 0572 amd 29k coff noprebar executable
+0 beshort 01572 amd 29k coff prebar executable
+0 beshort 0160007 amd 29k coff archive
+# Cray
+6 beshort 0407 unicos (cray) executable
+# Ultrix 4.3
+596 string \130\337\377\377 Ultrix core file
+>600 string >\0 from '%s'
+# BeOS and MAcOS PEF executables
+# From: hplus@zilker.net (Jon Watte)
+0 string Joy!peffpwpc header for PowerPC PEF executable
+#
+# ava assembler/linker Uros Platise <uros.platise@ijs.si>
+0 string avaobj AVR assembler object code
+>7 string >\0 version '%s'
+# gnu gmon magic From: Eugen Dedu <dedu@ese-metz.fr>
+0 string gmon GNU prof performance data
+>4 long x - version %d
+# From: Dave Pearson <davep@davep.org>
+# Harbour <URL:http://harbour-project.org/> HRB files.
+0 string \xc0HRB Harbour HRB file
+>4 leshort x version %d
+# Harbour HBV files
+0 string \xc0HBV Harbour variable dump file
+>4 leshort x version %d
+
+# From: Alex Beregszaszi <alex@fsn.hu>
+# 0 string exec BugOS executable
+# 0 string pack BugOS archive
+
+# From: Jason Spence <jspence@lightconsulting.com>
+# Generated by the "examples" in STM's ST40 devkit, and derived code.
+0 lelong 0x13a9f17e ST40 component image format
+>4 string >\0 \b, name '%s'
+
diff --git a/contrib/libs/libmagic/magic/Magdir/varied.script b/contrib/libs/libmagic/magic/Magdir/varied.script
new file mode 100644
index 0000000000..74b1b2276c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/varied.script
@@ -0,0 +1,21 @@
+#------------------------------------------------------------------------------
+# $File: varied.script,v 1.15 2022/10/18 13:01:30 christos Exp $
+# varied.script: file(1) magic for various interpreter scripts
+
+0 string/wt #!\ a
+>&-1 string/T x %s script text executable
+!:strength / 3
+
+0 string/wb #!\ a
+>&-1 string/T x %s script executable (binary data)
+!:strength / 3
+
+
+# using env
+0 string/wt #!\ /usr/bin/env a
+>15 string/T >\0 %s script text executable
+!:strength / 6
+
+0 string/wb #!\ /usr/bin/env a
+>15 string/T >\0 %s script executable (binary data)
+!:strength / 6
diff --git a/contrib/libs/libmagic/magic/Magdir/vax b/contrib/libs/libmagic/magic/Magdir/vax
new file mode 100644
index 0000000000..f3deffa59f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/vax
@@ -0,0 +1,32 @@
+
+#------------------------------------------------------------------------------
+# $File: vax,v 1.10 2019/10/04 18:07:46 christos Exp $
+# vax: file(1) magic for VAX executable/object and APL workspace
+#
+0 lelong 0101557 VAX single precision APL workspace
+0 lelong 0101556 VAX double precision APL workspace
+
+#
+# VAX a.out (BSD; others collide with 386 and other 32-bit little-endian
+# executables, and are handled in aout)
+#
+0 lelong 0420 a.out VAX demand paged (first page unmapped) pure executable
+>16 lelong >0 not stripped
+
+#
+# VAX COFF
+#
+# The `versions' were commented out, but have been un-commented out.
+# (Was the problem just one of endianness?)
+#
+0 leshort 0570
+>2 uleshort <100 VAX COFF executable, sections %d
+>>4 ledate x \b, created %s
+>>12 lelong >0 \b, not stripped
+>>22 leshort >0 \b, version %d
+
+0 leshort 0575
+>2 uleshort <100 VAX COFF pure executable, sections %d
+>>4 ledate x \b, created %s
+>>12 lelong >0 \b, not stripped
+>>22 leshort >0 \b, version %d
diff --git a/contrib/libs/libmagic/magic/Magdir/vicar b/contrib/libs/libmagic/magic/Magdir/vicar
new file mode 100644
index 0000000000..59d843d7ca
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/vicar
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# $File: vicar,v 1.4 2009/09/19 16:28:13 christos Exp $
+# vicar: file(1) magic for VICAR files.
+#
+# From: Ossama Othman <othman@astrosun.tn.cornell.edu
+# VICAR is JPL's in-house spacecraft image processing program
+# VICAR image
+0 string LBLSIZE= VICAR image data
+>32 string BYTE \b, 8 bits = VAX byte
+>32 string HALF \b, 16 bits = VAX word = Fortran INTEGER*2
+>32 string FULL \b, 32 bits = VAX longword = Fortran INTEGER*4
+>32 string REAL \b, 32 bits = VAX longword = Fortran REAL*4
+>32 string DOUB \b, 64 bits = VAX quadword = Fortran REAL*8
+>32 string COMPLEX \b, 64 bits = VAX quadword = Fortran COMPLEX*8
+# VICAR label file
+43 string SFDU_LABEL VICAR label file
diff --git a/contrib/libs/libmagic/magic/Magdir/virtual b/contrib/libs/libmagic/magic/Magdir/virtual
new file mode 100644
index 0000000000..3372020421
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/virtual
@@ -0,0 +1,307 @@
+
+#------------------------------------------------------------------------------
+# $File: virtual,v 1.17 2022/08/23 08:00:54 christos Exp $
+# From: James Nobis <quel@quelrod.net>
+# Microsoft hard disk images for:
+# Virtual Server
+# Virtual PC
+# VirtualBox
+# URL: http://fileformats.archiveteam.org/wiki/VHD_(Virtual_Hard_Disk)
+# Reference: https://download.microsoft.com/download/f/f/e/ffef50a5-07dd-4cf8-aaa3-442c0673a029/
+# Virtual%20Hard%20Disk%20Format%20Spec_10_18_06.doc
+0 string conectix Microsoft Disk Image, Virtual Server or Virtual PC
+# alternative shorter names
+#0 string conectix Microsoft Virtual Hard Disk image
+#0 string conectix Microsoft Virtual HD image
+!:mime application/x-virtualbox-vhd
+!:ext vhd
+# Features is a bit field used to indicate specific feature support
+#>8 ubelong !0x00000002 \b, Features %#x
+# Reserved. This bit must always be set to 1.
+#>8 ubelong &0x00000002 \b, Reserved %#x
+# File Format Version for the current specification 0x00010000
+#>12 ubelong !0x00010000 \b, Version %#8.8x
+# Data Offset only found 0x200
+#>16 ubequad !0x200 \b, Data Offset %#llx
+#>16 ubequad x \b, at %#llx
+# Dynamic Disk Header cookie like cxsparse
+#>(16.Q) string x "%-.8s"
+# This field contains a Unicode string (UTF-16) of the parent hard disk filename
+#>(16.Q+64) ubequad x \b, parent name %#llx
+# Creator Application
+# vpc~Microsoft Virtual PC, vs~Microsoft Virtual Server, vbox~VirtualBox, d2v~disk2vhd
+>28 string x \b, Creator %-4.4s
+# Creator Version: 0x00010000~Virtual Server 2004, 0x00050000~Virtual PC 2004
+# holds the major/minor version of the application that created the image
+>32 ubeshort x %x
+>34 ubeshort x \b.%x
+#>32 ubelong x \b, Version %#8.8x
+# Creator Host OS: 0x5769326B~Windows (Wi2k), 0x4D616320~Macintosh (Mac)
+>36 ubelong x (
+>>36 ubelong 0x5769326B \bW2k
+>>36 ubelong 0x4D616320 \bMac
+>>36 default x \b0x
+>>>36 ubelong x \b%8.8x
+# creation Time in seconds since 1 Jan 2000 UTC~946684800 sec. since Unix Epoch
+>24 bedate+946684800 x \b) %s
+# Original Size
+#>40 ubequad x \b, o.-Size %#llx
+# Current Size is same as original size, but change when disk is expanded
+#>48 ubequad x \b, Size %#llx
+>48 ubequad x \b, %llu bytes
+# Disk Geometry: cylinder, heads, and sectors/track for hard disk
+#>56 ubeshort x \b, Cylinder %#x
+>56 ubeshort x \b, CHS %u
+# Heads
+#>58 ubyte x \b, Heads %#x
+>58 ubyte x \b/%u
+# Sectors per track
+#>59 ubyte x \b, Sectors %#x
+>59 ubyte x \b/%u
+# Disk Type: 3~Dynamic hard disk
+>60 ubelong !0x3 \b, type %#x
+# Checksum
+#>64 ubelong x \b, cksum %#x
+# universally unique identifier (UUID) to associate a parent with its differencing image
+#>68 ubequad x \b, id %#16.16llx
+#>76 ubequad x \b-%16.16llx
+# Saved State: 1~Saved State
+>84 ubyte !0 \b, State %#x
+# Reserved 427 bytes with nils
+#>85 ubequad !0 \b, Reserved %#16.16llx
+
+# From: Joerg Jenderek
+# URL: https://msdn.microsoft.com/en-us/library/mt740058.aspx
+# Reference: https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/
+# MS-VHDX/[MS-VHDX].pdf
+# Note: extends the VHD format with new capabilities, such as a 16TB maximum size
+# TODO: find and display values like virtual size, disk size, cluster_size, etc
+# display id in GUID format
+#
+# VHDX_FILE_IDENTIFIER signature 0x656C696678646876
+0 string vhdxfile
+# VHDX_HEADER signature. 1 header is stored at offset 64KB and the other at 128KB
+>0x10000 string head Microsoft Disk Image eXtended
+#>0x20000 string head \b, 2nd header
+#!:mime application/x-virtualbox-vhdx
+!:ext vhdx
+# Creator[256] like "QEMU v3.0.0", "Microsoft Windows 6.3.9600.18512"
+>>8 lestring16 x \b, by %.256s
+# The Checksum field is a CRC-32C hash over the entire 4 KB structure
+#>>0x10004 ulelong x \b, CRC %#x
+# SequenceNumber
+>>0x10008 ulequad x \b, sequence %#llx
+# FileWriteGuid
+#>>0x10010 ubequad x \b, file id %#llx
+#>>>0x10018 ubequad x \b-%llx
+# DataWriteGuid
+#>>0x10020 ubequad x \b, data id %#llx
+#>>>0x10028 ubequad x \b-%llx
+# LogGuid. If this field is zero, then the log is empty or has no valid entries
+>>0x10030 ubequad >0 \b, log id %#llx
+>>>0x10038 ubequad x \b-%llx
+# LogVersion. If not 0 there is a log to replay
+>>0x10040 uleshort >0 \b, LogVersion %#x
+# Version. This field must be set to 1
+>>0x10042 uleshort !1 \b, Version %#x
+# LogLength must be multiples of 1 MB
+>>0x10044 ulelong/1048576 >1 \b, LogLength %u MB
+# LogOffset (normally 0x100000 when log direct after header); multiples of 1 MB
+>>0x10048 ulequad !0x100000 \b, LogOffset %#llx
+# Log Entry Signature must be 0x65676F6C~loge
+>>(0x10048.q) ulelong !0x65676F6C \b, NO Log Signature
+>>(0x10048.q) ulelong =0x65676F6C \b; LOG
+# Log Entry Checksum
+#>>>(0x10048.q+4) ulelong x \b, Log CRC %#x
+# Log Entry Length must be a multiple of 4 KB
+>>>(0x10048.q+8) ulelong/1024 >4 \b, EntryLength %u KB
+# Log Entry Tail must be a multiple of 4 KB
+#>>>(0x10048.q+12) ulelong x \b, Tail %#x
+# Log Entry SequenceNumber
+#>>>(0x10048.q+16) ulequad x \b, # %#llx
+# Log Entry DescriptorCount may be zero. only 4 bytes in other docs instead 8
+#>>>(0x10048.q+24) ulelong x \b, DescriptorCount %#llx
+# Log Entry Reserved must be set to 0
+>>>(0x10048.q+28) ulelong !0 \b, Reserved %#x
+# Log Entry LogGuid
+#>>>(0x10048.q+32) ubequad x \b, Log id %#llx
+#>>>(0x10048.q+40) ubequad x \b-%llx
+# Log Entry FlushedFileOffset should VHDX size when entry is written.
+#>>>(0x10048.q+48) ulequad x \b, FlushedFileOffset %llu
+# Log Entry LastFileOffset
+#>>>(0x10048.q+56) ulequad x \b, LastFileOffset %llu
+# filling
+#>>>(0x10048.q+64) ulequad >0 \b, filling %llx
+# Reserved[4016]
+#>>0x10050 ulequad >0 \b, Reserved %#llx
+# VHDX_REGION_TABLE_HEADER Signature 0x69676572~regi at offset 192 KB and 256 KB
+>0x30000 ulelong !0x69676572 \b, 1st region INVALID
+>0x30000 ulelong =0x69676572 \b; region
+# region Checksum. CRC-32C hash over the entire 64-KB table
+#>>0x30004 ulelong x \b, CRC %#x
+# The EntryCount specifies number of valid entries; Found 2; This must be =< 2047.
+>>0x30008 ulelong x \b, %u entries
+# reserved must be zero
+#>>0x3000C ulelong !0 \b, RESERVED %#x
+# Region Table Entry starts with identifier for the object. often BAT id
+>>0x30010 use vhdx-id
+# FileOffset
+>>0x30020 ulequad x \b, at %#llx
+# Length. Specifies the length of the object within the file
+#>>0x30028 ulelong x \b, Length %#x
+# 1 means region entry is required. if region not recognized, then REFUSE to load VHDX
+>>0x3002C ulelong x \b, Required %u
+# 2nd region entry often metadata id
+>>0x30030 use vhdx-id
+# 2nd entry FileOffset
+>>0x30040 ulequad x \b, at %#llx
+# 1 means region entry is required. if region not recognized, then REFUSE to load VHDX
+>>0x3004C ulelong x \b, Required %u
+# 2nd region
+>>0x40000 ulelong !0x69676572 \b, 2nd region INVALID
+# check in vhdx images for known id and show names instead hexadecimal
+0 name vhdx-id
+# https://www.windowstricks.in/online-windows-guid-converter
+# 2DC27766-F623-4200-9D64-115E9BFD4A08 BAT GUID
+# 6677C22D23F600429D64115E9BFD4A08 BAT ID
+>0 ubequad =0x6677C22D23F60042
+>>8 ubequad =0x9D64115E9BFD4A08 \b, id BAT
+# no BAT id
+>>8 default x
+>>>0 use vhdx-id-hex
+# 8B7CA206-4790-4B9A-B8FE-575F050F886E Metadata region GUID
+# 06A27C8B90479A4BB8FE575F050F886E Metadata region ID
+>0 ubequad =0x06A27C8B90479A4B
+>>8 ubequad =0xB8FE575F050F886E \b, id Metadata
+# no Metadata id
+>>8 default x
+>>>0 use vhdx-id-hex
+# 2FA54224-CD1B-4876-B211-5DBED83BF4B8 Virtual Disk Size GUID
+# 2442A52F1BCD7648B2115DBED83BF4B8 Virtual Disk Size ID
+# value "virtual size" can be verified by command `qemu-img info `
+>0 ubequad =0x2442A52F1BCD7648
+>>8 ubequad =0xB2115DBED83BF4B8 \b, id vsize
+# no Virtual Disk Size ID
+>>8 default x
+>>>0 use vhdx-id-hex
+# other ids
+>0 default x
+>>0 use vhdx-id-hex
+# in vhdx images show id as hexadecimal
+0 name vhdx-id-hex
+>0 ubequad x \b, ID %#16.16llx
+>8 ubequad x \b-%16.16llx
+#
+# libvirt
+# From: Philipp Hahn <hahn@univention.de>
+0 string LibvirtQemudSave Libvirt QEMU Suspend Image
+>0x10 lelong x \b, version %u
+>0x14 lelong x \b, XML length %u
+>0x18 lelong 1 \b, running
+>0x1c lelong 1 \b, compressed
+
+0 string LibvirtQemudPart Libvirt QEMU partial Suspend Image
+# From: Alex Beregszaszi <alex@fsn.hu>
+0 string/b COWD VMWare3
+>4 byte 3 disk image
+>>32 lelong x (%d/
+>>36 lelong x \b%d/
+>>40 lelong x \b%d)
+>4 byte 2 undoable disk image
+>>32 string >\0 (%s)
+
+0 string/b VMDK VMware4 disk image
+0 string/b KDMV VMware4 disk image
+
+#--------------------------------------------------------------------
+# Qemu Emulator Images
+# Lines written by Friedrich Schwittay (f.schwittay@yousable.de)
+# Updated by Adam Buchbinder (adam.buchbinder@gmail.com)
+# Made by reading sources, reading documentation, and doing trial and error
+# on existing QCOW files
+0 string/b QFI\xFB QEMU QCOW Image
+!:mime application/x-qemu-disk
+
+# Uncomment the following line to display Magic (only used for debugging
+# this magic number)
+#>0 string/b x , Magic: %s
+
+# There are currently 2 Versions: "1" and "2".
+# https://www.gnome.org/~markmc/qcow-image-format-version-1.html
+>4 belong x (v%d)
+
+# Using the existence of the Backing File Offset to determine whether
+# to read Backing File Information
+>>12 belong >0 \b, has backing file (
+# Note that this isn't a null-terminated string; the length is actually
+# (16.L). Assuming a null-terminated string happens to work usually, but it
+# may spew junk until it reaches a \0 in some cases.
+>>>(12.L) string >\0 \bpath %s
+
+# Modification time of the Backing File
+# Really useful if you want to know if your backing
+# file is still usable together with this image
+>>>>20 bedate >0 \b, mtime %s)
+>>>>20 default x \b)
+
+# Size is stored in bytes in a big-endian u64.
+>>24 bequad x \b, %lld bytes
+
+# 1 for AES encryption, 0 for none.
+>>36 belong 1 \b, AES-encrypted
+
+# https://www.gnome.org/~markmc/qcow-image-format.html
+>4 belong 2 (v2)
+# Using the existence of the Backing File Offset to determine whether
+# to read Backing File Information
+>>8 bequad >0 \b, has backing file
+# Note that this isn't a null-terminated string; the length is actually
+# (16.L). Assuming a null-terminated string happens to work usually, but it
+# may spew junk until it reaches a \0 in some cases. Also, since there's no
+# .Q modifier, we just use the bottom four bytes as an offset. Note that if
+# the file is over 4G, and the backing file path is stored after the first 4G,
+# the wrong filename will be printed. (This should be (8.Q), when that syntax
+# is introduced.)
+>>>(12.L) string >\0 (path %s)
+>>24 bequad x \b, %lld bytes
+>>32 belong 1 \b, AES-encrypted
+
+>4 belong 3 (v3)
+# Using the existence of the Backing File Offset to determine whether
+# to read Backing File Information
+>>8 bequad >0 \b, has backing file
+# Note that this isn't a null-terminated string; the length is actually
+# (16.L). Assuming a null-terminated string happens to work usually, but it
+# may spew junk until it reaches a \0 in some cases. Also, since there's no
+# .Q modifier, we just use the bottom four bytes as an offset. Note that if
+# the file is over 4G, and the backing file path is stored after the first 4G,
+# the wrong filename will be printed. (This should be (8.Q), when that syntax
+# is introduced.)
+>>>(12.L) string >\0 (path %s)
+>>24 bequad x \b, %lld bytes
+>>32 belong 1 \b, AES-encrypted
+
+>4 default x (unknown version)
+
+0 string/b QEVM QEMU suspend to disk image
+
+# QEMU QED Image
+# https://wiki.qemu.org/Features/QED/Specification
+0 string/b QED\0 QEMU QED Image
+
+# VDI Image
+# Sun xVM VirtualBox Disk Image
+# From: Richard W.M. Jones <rich@annexia.org>
+# VirtualBox Disk Image
+0x40 ulelong 0xbeda107f VirtualBox Disk Image
+>0x44 uleshort >0 \b, major %u
+>0x46 uleshort >0 \b, minor %u
+>0 string >\0 (%s)
+>368 lequad x \b, %lld bytes
+
+0 string/b Bochs\ Virtual\ HD\ Image Bochs disk image,
+>32 string x type %s,
+>48 string x subtype %s
+
+0 lelong 0x02468ace Bochs Sparse disk image
+
diff --git a/contrib/libs/libmagic/magic/Magdir/virtutech b/contrib/libs/libmagic/magic/Magdir/virtutech
new file mode 100644
index 0000000000..410ab9ee4b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/virtutech
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: virtutech,v 1.4 2009/09/19 16:28:13 christos Exp $
+# Virtutech Compressed Random Access File Format
+#
+# From <gustav@virtutech.com>
+0 string \211\277\036\203 Virtutech CRAFF
+>4 belong x v%d
+>20 belong 0 uncompressed
+>20 belong 1 bzipp2ed
+>20 belong 2 gzipped
+>24 belong 0 not clean
diff --git a/contrib/libs/libmagic/magic/Magdir/visx b/contrib/libs/libmagic/magic/Magdir/visx
new file mode 100644
index 0000000000..fe5c827d94
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/visx
@@ -0,0 +1,32 @@
+
+#------------------------------------------------------------------------------
+# $File: visx,v 1.5 2009/09/19 16:28:13 christos Exp $
+# visx: file(1) magic for Visx format files
+#
+0 short 0x5555 VISX image file
+>2 byte 0 (zero)
+>2 byte 1 (unsigned char)
+>2 byte 2 (short integer)
+>2 byte 3 (float 32)
+>2 byte 4 (float 64)
+>2 byte 5 (signed char)
+>2 byte 6 (bit-plane)
+>2 byte 7 (classes)
+>2 byte 8 (statistics)
+>2 byte 10 (ascii text)
+>2 byte 15 (image segments)
+>2 byte 100 (image set)
+>2 byte 101 (unsigned char vector)
+>2 byte 102 (short integer vector)
+>2 byte 103 (float 32 vector)
+>2 byte 104 (float 64 vector)
+>2 byte 105 (signed char vector)
+>2 byte 106 (bit plane vector)
+>2 byte 121 (feature vector)
+>2 byte 122 (feature vector library)
+>2 byte 124 (chain code)
+>2 byte 126 (bit vector)
+>2 byte 130 (graph)
+>2 byte 131 (adjacency graph)
+>2 byte 132 (adjacency graph library)
+>2 string .VISIX (ascii text)
diff --git a/contrib/libs/libmagic/magic/Magdir/vms b/contrib/libs/libmagic/magic/Magdir/vms
new file mode 100644
index 0000000000..56d57ae932
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/vms
@@ -0,0 +1,30 @@
+
+#------------------------------------------------------------------------------
+# $File: vms,v 1.10 2017/03/17 21:35:28 christos Exp $
+# vms: file(1) magic for VMS executables (experimental)
+#
+# VMS .exe formats, both VAX and AXP (Greg Roelofs, newt@uchicago.edu)
+
+# GRR 950122: I'm just guessing on these, based on inspection of the headers
+# of three executables each for Alpha and VAX architectures. The VAX files
+# all had headers similar to this:
+#
+# 00000 b0 00 30 00 44 00 60 00 00 00 00 00 30 32 30 35 ..0.D.`.....0205
+# 00010 01 01 00 00 ff ff ff ff ff ff ff ff 00 00 00 00 ................
+#
+0 string \xb0\0\x30\0 VMS VAX executable
+>44032 string PK\003\004 \b, Info-ZIP SFX archive v5.12 w/decryption
+#
+# The AXP files all looked like this, except that the byte at offset 0x22
+# was 06 in some of them and 07 in others:
+#
+# 00000 03 00 00 00 00 00 00 00 ec 02 00 00 10 01 00 00 ................
+# 00010 68 00 00 00 98 00 00 00 b8 00 00 00 00 00 00 00 h...............
+# 00020 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+# 00030 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
+# 00040 00 00 00 00 ff ff ff ff ff ff ff ff 02 00 00 00 ................
+#
+# GRR this test is still too general as it catches example adressen.dbt
+0 belong 0x03000000
+>8 ubelong 0xec020000 VMS Alpha executable
+>>75264 string PK\003\004 \b, Info-ZIP SFX archive v5.12 w/decryption
diff --git a/contrib/libs/libmagic/magic/Magdir/vmware b/contrib/libs/libmagic/magic/Magdir/vmware
new file mode 100644
index 0000000000..cd1a9d9576
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/vmware
@@ -0,0 +1,6 @@
+
+#------------------------------------------------------------------------------
+# $File: vmware,v 1.8 2017/03/17 21:35:28 christos Exp $
+# VMware specific files (deducted from version 1.1 and log file entries)
+# Anthon van der Neut (anthon@mnt.org)
+0 belong 0x4d52564e VMware nvram
diff --git a/contrib/libs/libmagic/magic/Magdir/vorbis b/contrib/libs/libmagic/magic/Magdir/vorbis
new file mode 100644
index 0000000000..49e75cb2d2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/vorbis
@@ -0,0 +1,155 @@
+
+#------------------------------------------------------------------------------
+# $File: vorbis,v 1.26 2020/08/22 18:30:55 christos Exp $
+# vorbis: file(1) magic for Ogg/Vorbis files
+#
+# From Felix von Leitner <leitner@fefe.de>
+# Extended by Beni Cherniavsky <cben@crosswinds.net>
+# Further extended by Greg Wooledge <greg@wooledge.org>
+#
+# Most (everything but the number of channels and bitrate) is commented
+# out with `##' as it's not interesting to the average user. The most
+# probable things advanced users would want to uncomment are probably
+# the number of comments and the encoder version.
+#
+# FIXME: The first match has been made a search, so that it can skip
+# over prepended ID3 tags. This will work for MIME type detection, but
+# won't work for detecting other properties of the file (they all need
+# to be made relative to the search). In any case, if the file has ID3
+# tags, the ID3 information will be printed, not the Ogg information,
+# so until that's fixed, this doesn't matter.
+# FIXME[2]: Disable the above for now, since search assumes text mode.
+#
+# --- Ogg Framing ---
+#0 search/1000 OggS Ogg data
+0 string OggS Ogg data
+>4 byte !0 UNKNOWN REVISION %u
+##>4 byte 0 revision 0
+>4 byte 0
+##>>14 lelong x (Serial %lX)
+# non-Vorbis content: FLAC (Free Lossless Audio Codec, http://flac.sourceforge.net)
+>>28 string \x7fFLAC \b, FLAC audio
+# non-Vorbis content: Theora
+!:mime audio/ogg
+>>28 string \x80theora \b, Theora video
+!:mime video/ogg
+# non-Vorbis content: Kate
+>>28 string \x80kate\0\0\0\0 \b, Kate (Karaoke and Text)
+!:mime application/ogg
+>>>37 ubyte x v%u
+>>>38 ubyte x \b.%u,
+>>>40 byte 0 utf8 encoding,
+>>>40 byte !0 unknown character encoding,
+>>>60 string >\0 language %s,
+>>>60 string \0 no language set,
+>>>76 string >\0 category %s
+>>>76 string \0 no category set
+# non-Vorbis content: Skeleton
+>>28 string fishead\0 \b, Skeleton
+!:mime video/ogg
+>>>36 leshort x v%u
+>>>40 leshort x \b.%u
+# non-Vorbis content: Speex
+>>28 string Speex\ \ \ \b, Speex audio
+!:mime audio/ogg
+# non-Vorbis content: OGM
+>>28 string \x01video\0\0\0 \b, OGM video
+!:mime video/ogg
+>>>37 string/c div3 (DivX 3)
+>>>37 string/c divx (DivX 4)
+>>>37 string/c dx50 (DivX 5)
+>>>37 string/c xvid (XviD)
+# --- First vorbis packet - general header ---
+>>28 string \x01vorbis \b, Vorbis audio,
+!:mime audio/ogg
+>>>35 lelong !0 UNKNOWN VERSION %u,
+##>>>35 lelong 0 version 0,
+>>>35 lelong 0
+>>>>39 ubyte 1 mono,
+>>>>39 ubyte 2 stereo,
+>>>>39 ubyte >2 %u channels,
+>>>>40 lelong x %u Hz
+# Minimal, nominal and maximal bitrates specified when encoding
+>>>>48 string <\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff \b,
+# The above tests if at least one of these is specified:
+>>>>>52 lelong !-1
+# Vorbis RC2 has a bug which puts -1000 in the min/max bitrate fields
+# instead of -1.
+# Vorbis 1.0 uses 0 instead of -1.
+>>>>>>52 lelong !0
+>>>>>>>52 lelong !-1000
+>>>>>>>>52 lelong x <%u
+>>>>>48 lelong !-1
+>>>>>>48 lelong x ~%u
+>>>>>44 lelong !-1
+>>>>>>44 lelong !-1000
+>>>>>>>44 lelong !0
+>>>>>>>>44 lelong x >%u
+>>>>>48 string <\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff bps
+# -- Second vorbis header packet - the comments
+# A kludge to read the vendor string. It's a counted string, not a
+# zero-terminated one, so file(1) can't read it in a generic way.
+# libVorbis is the only one existing currently, so I detect specifically
+# it. The interesting value is the cvs date (8 digits decimal).
+# Post-RC1 Ogg files have the second header packet (and thus the version)
+# in a different place, so we must use an indirect offset.
+>>>(84.b+85) string \x03vorbis
+>>>>(84.b+96) string/c Xiphophorus\ libVorbis\ I \b, created by: Xiphophorus libVorbis I
+>>>>>(84.b+120) string >00000000
+# Map to beta version numbers:
+>>>>>>(84.b+120) string <20000508 (<beta1, prepublic)
+>>>>>>(84.b+120) string 20000508 (1.0 beta 1 or beta 2)
+>>>>>>(84.b+120) string >20000508
+>>>>>>>(84.b+120) string <20001031 (beta2-3)
+>>>>>>(84.b+120) string 20001031 (1.0 beta 3)
+>>>>>>(84.b+120) string >20001031
+>>>>>>>(84.b+120) string <20010225 (beta3-4)
+>>>>>>(84.b+120) string 20010225 (1.0 beta 4)
+>>>>>>(84.b+120) string >20010225
+>>>>>>>(84.b+120) string <20010615 (beta4-RC1)
+>>>>>>(84.b+120) string 20010615 (1.0 RC1)
+>>>>>>(84.b+120) string 20010813 (1.0 RC2)
+>>>>>>(84.b+120) string 20010816 (RC2 - Garf tuned v1)
+>>>>>>(84.b+120) string 20011014 (RC2 - Garf tuned v2)
+>>>>>>(84.b+120) string 20011217 (1.0 RC3)
+>>>>>>(84.b+120) string 20011231 (1.0 RC3)
+# Some pre-1.0 CVS snapshots still had "Xiphphorus"...
+>>>>>>(84.b+120) string >20011231 (pre-1.0 CVS)
+# For the 1.0 release, Xiphophorus is replaced by Xiph.Org
+>>>>(84.b+96) string/c Xiph.Org\ libVorbis\ I \b, created by: Xiph.Org libVorbis I
+>>>>>(84.b+117) string >00000000
+>>>>>>(84.b+117) string <20020717 (pre-1.0 CVS)
+>>>>>>(84.b+117) string 20020717 (1.0)
+>>>>>>(84.b+117) string 20030909 (1.0.1)
+>>>>>>(84.b+117) string 20040629 (1.1.0 RC1)
+>>>>>>(84.b+117) string 20050304 (1.1.2)
+>>>>>>(84.b+117) string 20070622 (1.2.0)
+>>>>>>(84.b+117) string 20090624 (1.2.2)
+>>>>>>(84.b+117) string 20090709 (1.2.3)
+>>>>>>(84.b+117) string 20100325 (1.3.1)
+>>>>>>(84.b+117) string 20101101 (1.3.2)
+>>>>>>(84.b+117) string 20120203 (1.3.3)
+>>>>>>(84.b+117) string 20140122 (1.3.4)
+>>>>>>(84.b+117) string 20150105 (1.3.5)
+
+# non-Vorbis content: Opus https://tools.ietf.org/html/rfc7845#section-5
+>>28 string OpusHead \b, Opus audio,
+!:mime audio/ogg
+>>>36 ubyte >0x0F UNKNOWN VERSION %u,
+>>>36 ubyte&0x0F !0 version 0.%u,
+>>>>46 ubyte >1
+>>>>>46 ubyte !255 unknown channel mapping family %u,
+>>>>>37 ubyte x %u channels
+>>>>46 ubyte 0
+>>>>>37 ubyte 1 mono
+>>>>>37 ubyte 2 stereo
+>>>>46 ubyte 1
+>>>>>37 ubyte 1 mono
+>>>>>37 ubyte 2 stereo
+>>>>>37 ubyte 3 linear surround
+>>>>>37 ubyte 4 quadraphonic
+>>>>>37 ubyte 5 5.0 surround
+>>>>>37 ubyte 6 5.1 surround
+>>>>>37 ubyte 7 6.1 surround
+>>>>>37 ubyte 8 7.1 surround
+>>>>40 lelong !0 \b, %u Hz (Input Sample Rate) \ No newline at end of file
diff --git a/contrib/libs/libmagic/magic/Magdir/vxl b/contrib/libs/libmagic/magic/Magdir/vxl
new file mode 100644
index 0000000000..0fdc68a7f0
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/vxl
@@ -0,0 +1,14 @@
+
+#------------------------------------------------------------------------------
+# $File: vxl,v 1.4 2009/09/19 16:28:13 christos Exp $
+# VXL: file(1) magic for VXL binary IO data files
+#
+# from Ian Scott <scottim@sf.net>
+#
+# VXL is a collection of C++ libraries for Computer Vision.
+# See the vsl chapter in the VXL Book for more info
+# http://www.isbe.man.ac.uk/public_vxl_doc/books/vxl/book.html
+# http:/vxl.sf.net
+
+2 lelong 0x472b2c4e VXL data file,
+>0 leshort >0 schema version no %d
diff --git a/contrib/libs/libmagic/magic/Magdir/warc b/contrib/libs/libmagic/magic/Magdir/warc
new file mode 100644
index 0000000000..5942867ddf
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/warc
@@ -0,0 +1,16 @@
+
+#------------------------------------------------------------------------------
+# $File: warc,v 1.4 2019/04/19 00:42:27 christos Exp $
+# warc: file(1) magic for WARC files
+
+0 string WARC/ WARC Archive
+>5 string x version %.4s
+!:mime application/warc
+
+#------------------------------------------------------------------------------
+# Arc File Format from Internet Archive
+# see https://www.archive.org/web/researcher/ArcFileFormat.php
+0 string filedesc:// Internet Archive File
+!:mime application/x-ia-arc
+>11 search/256 \x0A \b
+>>&0 ubyte >0 \b version %c
diff --git a/contrib/libs/libmagic/magic/Magdir/weak b/contrib/libs/libmagic/magic/Magdir/weak
new file mode 100644
index 0000000000..6dc1793c92
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/weak
@@ -0,0 +1,16 @@
+
+#------------------------------------------------------------------------------
+# weak: file(1) magic for very weak magic entries, disabled by default
+#
+# These entries are so weak that they might interfere identification of
+# other formats. Example include:
+# - Only identify for 1 or 2 bytes
+# - Match against very wide range of values
+# - Match against generic word in some spoken languages (e.g. English)
+
+# Summary: Computer Graphics Metafile
+# Extension: .cgm
+#0 beshort&0xffe0 0x0020 binary Computer Graphics Metafile
+#0 beshort 0x3020 character Computer Graphics Metafile
+
+#0 string =!! Bennet Yee's "face" format
diff --git a/contrib/libs/libmagic/magic/Magdir/web b/contrib/libs/libmagic/magic/Magdir/web
new file mode 100644
index 0000000000..a0d26e67fb
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/web
@@ -0,0 +1,18 @@
+
+#------------------------------------------------------------------------------
+# $File: web,v 1.2 2022/10/29 16:02:37 christos Exp $
+
+# http://www.rdfhdt.org/
+# From Christoph Biedl
+# http://www.rdfhdt.org/hdt-internals/
+# https://github.com/rdfhdt/hdt-cpp
+
+0 string $HDT\x01 HDT file (binary compressed indexed RDF triples) type 1
+!:mime application/vnd.hdt
+!:ext hdt
+
+0 string [Adblock\040Plus Adblock Plus
+>&1 regex [0-9.]+ %s
+>1 string x rules file
+>10 search/100 Version:
+>>&1 regex [0-9]+ \b, version %s
diff --git a/contrib/libs/libmagic/magic/Magdir/webassembly b/contrib/libs/libmagic/magic/Magdir/webassembly
new file mode 100644
index 0000000000..469b45e22b
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/webassembly
@@ -0,0 +1,17 @@
+#------------------------------------------------------------------------------
+# $File: webassembly,v 1.4 2022/08/16 11:16:39 christos Exp $
+# webassembly: file(1) magic for WebAssembly modules
+#
+# WebAssembly is a virtual architecture developed by a W3C Community
+# Group at https://webassembly.org/. The file extension is .wasm, and
+# the MIME type is application/wasm.
+#
+# https://webassembly.org/docs/binary-encoding/ is the main
+# document describing the binary format.
+# From: Pip Cet <pipcet@gmail.com> and Joel Martin
+
+0 string \0asm WebAssembly (wasm) binary module
+>4 lelong =1 version %#x (MVP)
+!:mime application/wasm
+!:ext wasm
+>4 lelong >1 version %#x
diff --git a/contrib/libs/libmagic/magic/Magdir/windows b/contrib/libs/libmagic/magic/Magdir/windows
new file mode 100644
index 0000000000..f58ce3e5a5
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/windows
@@ -0,0 +1,1822 @@
+
+#------------------------------------------------------------------------------
+# $File: windows,v 1.63 2023/07/17 16:56:13 christos Exp $
+# windows: file(1) magic for Microsoft Windows
+#
+# This file is mainly reserved for files where programs
+# using them are run almost always on MS Windows 3.x or
+# above, or files only used exclusively in Windows OS,
+# where there is no better category to allocate for.
+# For example, even though WinZIP almost run on Windows
+# only, it is better to treat them as "archive" instead.
+# For format usable in DOS, such as generic executable
+# format, please specify under "msdos" file.
+#
+
+
+# Summary: Outlook Express DBX file
+# Created by: Christophe Monniez
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Outlook_Express_Database
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dbx.trid.xml
+# https://sourceforge.net/projects/ol2mbox/files/LibDBX/
+# v1.0.4/libdbx_1.0.4.tar.gz/FILE-FORMAT
+# Note: called "Outlook Express Database" by TrID and DROID via PUID fmt/838 fmt/839
+# and partly verified by `undbx --verbosity 4 Posteingang.dbx`
+0 string \xCF\xAD\x12\xFE
+# skip DROID fmt-838-signature-id-1193.dbx fmt-839-signature-id-1194.dbx by check for valid file size
+>0x7C ulelong >0 MS Outlook Express DBX file
+#!:mime application/octet-stream
+#!:mime application/vnd.ms-outlook
+!:mime application/x-ms-dbx
+!:ext dbx
+>>4 byte =0xC5 \b, message database
+>>4 byte =0xC6 \b, folder database
+>>4 byte =0xC7 \b, account information
+>>4 byte =0x30 \b, offline database
+# version like: 5.2 5.5 (typical)
+>>20 ulequad !0x0000000500000005 \b, version
+# major version
+>>>24 ulelong x %u
+# minor version
+>>>20 ulelong x \b.%u
+# CLSID: 6F74FDC5-E366-11d1-9A4E-00C04FA309D4~Message 6F74FDC6-E366-11D1-9A4E-00C04FA309D4~Folder
+# 26FE9D30-1A8F-11D2-AABF-006097D474C4~offline
+#>>4 guid x \b, CLSID %s
+# file size; total size of file; sometimes real size a little bit higher
+>>0x7C ulelong x \b, ~ %u bytes
+# highest Email ID; the next email will have a number one higher than this
+>>0x5c ulelong x \b, highest ID %#x
+# item count; number of items stored in this DBX file
+>>0xC4 ulelong x \b, %u item
+# plural s
+>>0xC4 ulelong !1 \bs
+# index pointer; file offset pointing to a page of Data Indexes
+>>0xE4 ulelong >0 \b, index pointer %#x
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Nickfile
+# https://www.nirsoft.net/utils/outlook_nk2_edit.html
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/n/nk2.trid.xml
+# https://github.com/libyal/libnk2/blob/main/documentation
+# Nickfile%20(NK2)%20format.asciidoc
+# Note: called "Outlook Nickfile" by TrID & TestDisk and
+# "Outlook Nickname File" by Microsoft Outlook and
+# "Outlook AutoComplete File" by Nirsoft NK2Edit
+# partly verfied by NK2Edit Raw Text Edit Mode
+0 ubelong 0x0DF0ADBA MS Outlook Nickfile
+#!:mime application/octet-stream
+#!:mime application/vnd.ms-outlook
+!:mime application/x-ms-nickfile
+!:ext nk2/dat/bak
+# nick is used by "older" Outlook; dat is used by "newer" Outlook (probably 2010 - 2016); bak is used for backup
+#!:ext nick/nk2/dat/bak
+# Unknown; probably a version indicator like: 0000000Ah 0000000Ch
+>4 ulelong x \b, probably version %u
+# Unknown2; probably a version indicator like: 1 0
+>8 ulelong x \b.%u
+# number of rows (nickname or alias items) in file
+>12 ulelong x \b, %u items
+# number of item entries/columns/properties value like: 17h
+>16 ulelong x \b, %u entries
+# value type/property tag: 001Fh~4 bytes for data size of UTF-16 LE string
+>20 uleshort x \b, value type %#4.4x
+# entry type/property identifier: 6001h~PR_DOTSTUFF_STATE/PR_NICK_NAME_W
+>22 uleshort x \b, entry type %#4.4x
+# Reserved like: 0013FD90h
+#>24 ulelong x \b, reserved %#8.8x
+# value data array/Irrelevant Union like: 0000000004E31A80h
+#>28 ulequad x \b, data %#16.16llx
+# UTF-16
+>20 uleshort =0x001F
+# unicode string bytes like: 2Ch
+>>36 ulelong x \b, %u bytes
+# unicode string value PT_UNICODE like: janesmith@contoso.org
+>>40 lestring16 x "%s"
+
+# Summary: Windows crash dump
+# Created by: Andreas Schuster (https://computer.forensikblog.de/)
+# https://web.archive.org/web/20101125060849/https://computer.forensikblog.de/en/2008/02/64bit_magic.html
+# Modified by (1): Abel Cheung (Avoid match with first 4 bytes only)
+# Modified by (2): Joerg Jenderek (addtional fields, extension, URL)
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dmp.trid.xml
+# https://gitlab.com/qemu-project/qemu/-/blob/master/include/qemu/win_dump_defs.h
+# Note: called "Windows memory dump" by TrID
+# and verified by like Windows Kit `Dumpchk.exe 043022-18703-01.dmp`
+# and partly by NirSoft `BlueScreenView.exe 043022-18703-01.dmp`
+# char Signature[4]
+0 string PAGE
+# char ValidDump[4]
+>4 string DUMP MS Windows 32bit crash dump
+#!:mime application/octet-stream
+!:mime application/x-ms-dmp
+# like: Mini111013-01.dmp
+!:ext dmp
+# major version like: 15
+>>8 ulelong x \b, version %u
+# minor version like: 2600
+>>12 ulelong x \b.%u
+# DirectoryTableBase like: 709000
+#>>16 ulelong x \b, DirectoryTableBase %#x
+# PfnDatabase like: 805620c8
+#>>20 ulelong x \b, PfnDatabase %#x
+# PsLoadedModuleList like: 8055d720
+#>>24 ulelong x \b, PsLoadedModuleList %#x
+# PsActiveProcessHead like:805638b8
+#>>28 ulelong x \b, PsActiveProcessHead %#x
+# MachineImageType like: 14c (intel x86)
+>>32 ulelong !0x14c \b, MachineImageType %#x
+# NumberProcessors like: 2
+>>36 ulelong x \b, %u processors
+# BugcheckCode like: e2
+#>>40 ulelong x \b, BugcheckCode %#x
+# BugcheckParameter1 like: 0
+#>>44 ulelong x \b, BugcheckParameter1 %#x
+# BugcheckParameter2 like: 0
+#>>48 ulelong x \b, BugcheckParameter2 %#x
+# BugcheckParameter3 like: 0
+#>>52 ulelong x \b, BugcheckParameter3 %#x
+# BugcheckParameter4 like: 0
+#>>56 ulelong x \b, BugcheckParameter4 %#x
+# VersionUser[32]; like "PAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGE" ""
+#>>60 string x \b, VersionUser "%.32s"
+# uint32_t reserved0 like: 45474101
+#>>92 ulelong x \b, reserved0 %#x
+>>0x05c byte 0 \b, no PAE
+>>0x05c byte 1 \b, PAE
+# KdDebuggerDataBlock like: 8054d2e0
+#>>96 ulelong x \b, KdDebuggerDataBlock %#x
+# uint8_t PhysicalMemoryBlockBuffer[700]
+# WinDumpPhyMemDesc32 NumberOfRuns like: 45474150
+#>>100 ulelong x \b, NumberOfRuns %#x
+# WinDumpPhyMemDesc32 uint32_t NumberOfPages like: 1162297680
+#>>104 ulelong x \b, NumberOfPages %#x
+# WinDumpPhyMemRun32 Run[86]; 688 bytes
+#>>108 ulelong x \b, BasePage %#x
+#>>112 ulelong x \b, PageCount %#x
+# uint8_t reserved1[3200]
+#>>800 string x \b, reserved "%s"
+#>>4000 ulelong x \b, RequiredDumpSpace %#x
+# uint8_t reserved2[92];
+#>>4004 string x \b, reserved2 "%s"
+>>0xf88 lelong 1 \b, full dump
+>>0xf88 lelong 2 \b, kernel dump
+>>0xf88 lelong 3 \b, small dump
+# like: 4
+>>0xf88 lelong >3 \b, dump type (%#x)
+# WinDumpPhyMemDesc32 uint32_t NumberOfPages like: 1162297680
+# GRR: IS THIS TRUE? VALUE IS SOMETIMES VERY HIGH!
+#>>104 ulelong x \b, NumberOfPages %#x
+>>0x068 lelong x \b, %d pages
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/d/dmp-64.trid.xml113o
+# Note: called "Windows 64bit Memory Dump" by TrID
+# char ValidDump[4]
+>4 string DU64 MS Windows 64bit crash dump
+#!:mime application/octet-stream
+!:mime application/x-ms-dmp
+# like: c:\Windows\Minidump\020322-18890-01.dmp c:\Windows\MEMORY.DMP
+!:ext dmp
+# major version like: 15
+>>8 ulelong x \b, version %u
+# minor version like: 9600 19041 22621
+>>12 ulelong x \b.%u
+# DirectoryTableBase like: 001ab000
+#>>16 ulequad x \b, DirectoryTableBase %#llx
+# PfnDatabase like: fffffa8000000000
+#>>24 ulequad x \b, PfnDatabase %#llx
+# PsLoadedModuleList like: fffff800c553f650
+#>>32 ulequad x \b, PsLoadedModuleList %#llx
+# PsActiveProcessHead like: fffff800c5525400
+#>>40 ulequad x \b, PsActiveProcessHead %#llx
+# MachineImageType like: 00008664
+>>48 ulelong !0x8664 \b, MachineImageType %#x
+# NumberProcessors like: 2 4
+>>52 ulelong x \b, %u processors
+# BugcheckCode like: 1000007e
+#>>56 ulelong x \b, BugcheckCode %#x
+# unused0
+#>>60 ulelong x \b, unused0 %#x
+# BugcheckParameter1 like: ffffffffc0000005
+#>>64 ulequad x \b, BugcheckParameter1 %#llx
+# BugcheckParameter2 like: fffff801abb2158f
+#>>72 ulequad x \b, BugcheckParameter2 %#llx
+# BugcheckParameter3 like: ffffd000290d4288
+#>>80 ulequad x \b, BugcheckParameter3 %#llx
+# BugcheckParameter4 like: ffffd000290d3aa0
+#>>88 ulequad x \b, BugcheckParameter4 %#llx
+# VersionUser[32]; like "" "PAGEPAGEPAGEPAGEPAGEPAGEPAGEPAGE" ""
+#>>96 string x \b, VersionUser "%.32s"
+# KdDebuggerDataBlock like: fffff800c550c530
+#>>128 ulequad x \b, KdDebuggerDataBlock %#llx
+# uint8_t PhysicalMemoryBlockBuffer[704]
+# WinDumpPhyMemDesc64 NumberOfRuns like: 6 7 0x45474150
+#>>136 ulelong x \b, NumberOfRuns %#x
+# WinDumpPhyMemDesc64 unused like: 0 0x45474150
+#>>140 ulelong x \b, unused %#x
+# WinDumpPhyMemRun64 Run[43] BasePage like: 1
+#>>152 ulequad x \b, BasePage %#llx
+# WinDumpPhyMemRun64 Run[43] PageCount like: 57h
+#>>160 ulequad x \b, PageCount %#llx
+# uint8_t ContextBuffer[3000] like: "" "\001" "\0207J\266\001\340\377\377&8\007\312"
+#>>840 string x \b, ContextBuffer "%s"
+# WinDumpExceptionRecord ExceptionCode
+#>>3840 ulelong x \b, ExceptionCode %#x
+# WinDumpExceptionRecord ExceptionFlags
+#>>3844 ulelong x \b, ExceptionFlags %#x
+# WinDumpExceptionRecord ExceptionRecord
+#>>3848 ulequad x \b, ExceptionRecord %#llx
+# WinDumpExceptionRecord ExceptionAddress
+#>>3856 ulequad x \b, ExceptionAddress %#llx
+# WinDumpExceptionRecord NumberParameters
+#>>3864 ulelong x \b, NumberParameters %#x
+# WinDumpExceptionRecord unused
+#>>3868 ulelong x \b, unsed %#x
+# WinDumpExceptionRecord ExceptionInformation[15]
+#>>3872 ulequad x \b, ExceptionInformation[0] %#llx
+# https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/memory-dump-file-options
+# but DumpType like: 4~small 5~full (MEMORY.DMP) 6~kernel (MEMORY.DMP)
+>>0xf98 ulelong x \b,
+>>>0xf98 lelong 5 full dump
+>>>0xf98 lelong 6 kernel dump
+>>>0xf98 lelong 4 small dump
+# This probably never occur
+>>>0xf98 default x DumpType
+>>>>0xf98 ulelong x (%#x)
+# WinDumpPhyMemDesc64 uint64_t NumberOfPages like: 3142425 8341923 8366500 1162297680 4992030524978970960
+# GRR: IS THIS TRUE? VALUE IS SOMETIMES VERY HIGH!
+>>0x090 lequad x \b, %lld pages
+
+# Summary: Vista Event Log
+# Created by: Andreas Schuster (https://computer.forensikblog.de/)
+# Update: Joerg Jenderek
+# URL: https://github.com/libyal/libevtx/blob/main/documentation/Windows%20XML%20Event%20Log%20(EVTX).asciidoc
+# Reference (1): https://web.archive.org/web/20110803085000/
+# https://computer.forensikblog.de/en/2007/05/some_magic.html
+# http://mark0.net/download/triddefs_xml.7z/defs/e/evtx.trid.xml
+# Note: called "Vista Event Log" by TrID and "Event Log" by Windows
+# verified partly by `wevtutil.exe gli /lf:true dumpfile.evtx`
+0 string ElfFile\0 MS Windows
+#!:mime application/octet-stream
+!:mime application/x-ms-evtx
+!:ext evtx
+# Major+Minor format version: 3.1~Vista and later 3.2~Windows 10 (2004) and later
+>0x24 ulelong =0x00030001 Vista-8.1 Event Log
+>0x24 ulelong !0x00030001 10-11 Event Log, version
+>>0x26 uleshort x %u
+>>0x24 uleshort x \b.%u
+>0x2a leshort x \b, %d chunks
+>>0x10 lelong x \b (no. %d in use)
+>0x18 lelong >1 \b, next record no. %d
+>0x18 lelong =1 \b, empty
+>0x78 lelong &1 \b, DIRTY
+>0x78 lelong &2 \b, FULL
+
+# Summary: Windows Event Trace Log
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/ETL
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/e/etl.trid.xml
+# https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/etw/tracelog/trace_logfile_header.htm
+# Note: called "Window tracing/diagnostic binary log" by TrID
+# verified by `tracerpt.EXE Wifi.etl -of EVTX`
+# and by etl-parser `etl2xml --input AMSITrace.etl --output AMSITrace.xml`
+# Every ETL file begins with a WMI_BUFFER_HEADER, a SYSTEM_TRACE_HEADER and a TRACE_LOGFILE_HEADER
+0 ubyte 0
+# look for corresponding encoded as UTF-16 file name extension like in: boot_BASE+CSWITCH_1.etl
+>0 search/0x699087/b .\0e\0t\0l\0\0\0
+# GRR: line above only works if in ../../src/file.h FILE_BYTES_MAX is raised above 699086h (6,59 MiB)
+>>0 use trace-etl
+# display information of Windows Performance Analyzer Trace File (file name)
+0 name trace-etl
+>0 ubyte x Windows Event Trace Log
+#!:mime application/x-ms-etl
+# http://extension.nirsoft.net/etl
+!:mime application/etl
+!:ext etl
+# look for DOS drive letter part of log file name like: PhotosAppTracing_startedInBGMode.etl
+>0 search/0x2b4/sb :\0\x5c\0
+# like: "c:\Windows\Logs\NetSetup\service.0.etl" "C:\Windows\System32\LogFiles\WMI\Wifi.etl"
+>>&-2 lestring16 x "%s"
+
+# Summary: Windows System Deployment Image
+# Created by: Joerg Jenderek
+# URL: http://en.wikipedia.org/wiki/System_Deployment_Image
+# Reference: http://skolk.livejournal.com/1320.html
+0 string $SDI
+>4 string 0001 System Deployment Image
+!:mime application/x-ms-sdi
+#!:mime application/octet-stream
+# \Boot\boot.sdi
+!:ext sdi
+# MDBtype: 0~Unspecified 1~RAM 2~ROM
+>>8 ulequad !0 \b, MDBtype %#llx
+# BootCodeOffset
+>>16 ulequad !0 \b, BootCodeOffset %#llx
+# BootCodeSize
+>>24 ulequad !0 \b, BootCodeSize %#llx
+# VendorID
+>>32 ulequad !0 \b, VendorID %#llx
+# DeviceID
+>>40 ulequad !0 \b, DeviceID %#llx
+# DeviceModel
+>>48 ulequad !0 \b, DeviceModel %#llx
+>>>56 ulequad !0 \b%llx
+# DeviceRole
+>>64 ulequad !0 \b, DeviceRole %#llx
+# Reserved1; reserved fields and gaps between BLOBs are padded with \0
+#>>72 ulequad !0 \b, Reserved1 %#llx
+# RuntimeGUID
+>>80 ulequad !0 \b, RuntimeGUID %#llx
+>>>88 ulequad !0 \b%llx
+# RuntimeOEMrev
+>>96 ulequad !0 \b, RuntimeOEMrev %#llx
+# Reserved2
+#>>104 ulequad !0 \b, Reserved2 %#llx
+# BLOB alignment value in pages, as specified in sdimgr /pack: 1~4K 2~8k
+>>112 ulequad !0 \b, PageAlignment %llu
+# Reserved3[48]
+#>>120 ulequad !0 \b, Reserved3 %#llx
+# SDI checksum 39h
+>>0x1f8 ulequad x \b, checksum %#llx
+# BLOBtype[8] \0-padded: PART, WIM , BOOT, LOAD, DISK
+>>0x400 string >\0 \b, type %-3.8s
+# 0~non-filesystem 7~NTFS 6~BIGFAT
+>>>0x420 ulequad !0 (%#llx)
+# ATTRibutes
+>>>0x408 ulequad !0 %#llx attributes
+# Offset
+>>>0x410 ulequad x at %#llx
+# print 1 space after size and then handles NTFS boot sector by ./filesystems
+>>>0x418 ulequad >0 %llu bytes
+>>>>(0x410.l) indirect x
+# 2nd BLOB: WIM
+>>0x440 string >\0 \b, type %-3.8s
+>>>0x428 ulequad !0 (%#llx)
+# ATTRibutes
+>>>0x448 ulequad !0 %#llx attributes
+# Offset
+>>>0x450 ulequad x at %#llx
+>>>0x458 ulequad >0 %llu bytes
+>>>>(0x450.l) indirect x
+# 3rd BLOB
+>>0x480 string >\0 \b, type %-3.8s
+
+# Summary: Windows boot status log BOOTSTAT.DAT
+# From: Joerg Jenderek
+# Reference: https://www.geoffchappell.com/notes/windows/boot/bsd.htm
+# Note: mainly refers to older Windows Vista, sometimes
+# BOOTSTAT.DAT only contains nulls or invalid data
+# checking for valid version below 5
+0 ulelong <5
+# skip many ISO images by checking for valid 64 KiB file size
+>8 ulelong =0x00010000
+>>0 use bootstat-dat
+# display information of BOOTSTAT.DAT
+0 name bootstat-dat
+>0 ulelong x Windows boot log
+#!:mime application/octet-stream
+!:mime application/x-ms-dat
+# BOOTSTAT.DAT in BOOT subdirectory
+!:ext dat
+# apparently a version number: 2 for older like Vista, 3, 4 Windows 10
+>0 ulelong >2 \b, version %u
+# apparently the size of the header: often 10h in older Windows, 14h, 18h
+>4 ulelong !0x10 \b, header size %#x
+#>4 ulelong !0x10 \b, header size %u
+# apparently the size of the file: always 0x00010000~64KiB
+# the file is acceptable to BOOTMGR only if it is exactly 64 KiB
+>8 ulelong !0x00010000 \b, file size %#x
+# size of valid data, in bytes: C8h 50h 172h 5D5Ch
+>0xc ulelong x \b, %#x valid bytes
+# skip header and jump to first bootstat entry and display information
+>(0x4.l-1) ubyte x
+>>&0 use bootstat-entry
+# jump to first entry again because pointer are bad after "use"
+>(0x4.l-1) ubyte x
+# by 1st entry size jump to 2nd entry and display information
+>>&(&0x18.l-1) ubyte x
+>>>&0 use bootstat-entry
+# jump to possible 3rd boot entry and display information
+# >(0x4.l-1) ubyte x
+# >>&(&0x18.l-1) ubyte x
+# >>>&(&0x18.l-1) ubyte x
+# >>>>&0 use bootstat-entry
+# display BOOTSTAT.DAT entry
+0 name bootstat-entry
+#>0x00 ubequad x \b, ENTRY %16.16llx
+# size of entry, in bytes: 40h(init) 78h(launced) 9Ch
+#>0x18 ulelong x \b; entry size %u
+>0x18 ulelong x \b; entry size %#x
+# time stamp, in seconds
+>0x00 ulelong x \b, %#x seconds
+# always zero, significance unknown
+>0x04 ulelong !0 \b, not null %u
+# GUID of event source; but empty if event source is BOOTMGR
+>0x08 ubequad !0 \b, GUID %#16.16llx
+>>0x10 ubequad x \b%16.16llx
+# severity code: 1~informational 3~errors
+>0x1C ulelong !1 \b, severity %#x
+# apparently a version number: 2
+>0x20 ulelong !2 \b, version %u
+# event identifier 1~log file initialised 11h~boot application launched
+#>0x24 ulelong x \b, event %#x
+>0x24 ulelong !1
+>>0x24 ulelong !0x11 \b, event %#x
+# entry data; size depends on event identifier
+#>0x28 ubequad x \b, data %#16.16llx
+>0x24 ulelong =0x1 \b, Init
+# always 0, significance unknown
+>>0x34 uleshort !0 \b, not null %u
+# always 7, significance unknown
+>>0x36 uleshort !7 \b, not seven %u
+# year
+>>0x28 uleshort x %u
+# month
+>>0x2A uleshort x \b-%u
+# day
+>>0x2C uleshort x \b-%u
+# hour
+>>0x2E uleshort x %u
+# minute
+>>0x30 uleshort x \b:%u
+# second
+>>0x32 uleshort x \b:%u
+# boot application launched
+>0x24 ulelong =0x11 \b, launched
+# type of start: 0 normally, 1 or 2 maybe in a recovery sequence
+>>0x38 uleshort !0 \b, type %u
+# pathname of boot application, as null-terminated Unicode string; typically
+# \Windows\system32\winload.exe \Windows\system32\winload.efi
+>>0x3C lestring16 x %s
+
+# Summary: Windows Error Report text files
+# URL: https://en.wikipedia.org/wiki/Windows_Error_Reporting
+# Reference: https://www.nirsoft.net/utils/app_crash_view.html
+# Created by: Joerg Jenderek
+# Note: in directories %ProgramData%\Microsoft\Windows\WER\{ReportArchive,ReportQueue}
+# %LOCALAPPDATA%\Microsoft\Windows\WER\{ReportArchive,ReportQueue}
+0 lestring16 Version=
+>22 lestring16 EventType Windows Error Report
+!:mime text/plain
+# Report.wer
+!:ext wer
+
+# Summary: Windows 3.1 group files
+# Extension: .grp
+# Created by: unknown
+0 string \120\115\103\103 MS Windows 3.1 group files
+
+
+# Summary: Old format help files
+# URL: https://en.wikipedia.org/wiki/WinHelp
+# Reference: https://www.oocities.org/mwinterhoff/helpfile.htm
+# Update: Joerg Jenderek
+# Created by: Dirk Jagdmann <doj@cubic.org>
+#
+# check and then display version and date inside MS Windows HeLP file fragment
+0 name help-ver-date
+# look for Magic of SYSTEMHEADER
+>0 leshort 0x036C
+# version Major 1 for right file fragment
+>>4 leshort 1 Windows
+# print non empty string above to avoid error message
+# Warning: Current entry does not yet have a description for adding a MIME type
+!:mime application/winhelp
+!:ext hlp
+# version Minor of help file format is hint for windows version
+>>>2 leshort 0x0F 3.x
+>>>2 leshort 0x15 3.0
+>>>2 leshort 0x21 3.1
+>>>2 leshort 0x27 x.y
+>>>2 leshort 0x33 95
+>>>2 default x y.z
+>>>>2 leshort x %#x
+# to complete message string like "MS Windows 3.x help file"
+>>>2 leshort x help
+# GenDate often older than file creation date
+>>>6 ldate x \b, %s
+#
+# Magic for HeLP files
+0 lelong 0x00035f3f
+# ./windows (version 5.25) labeled the entry as "MS Windows 3.x help file"
+# file header magic 0x293B at DirectoryStart+9
+>(4.l+9) uleshort 0x293B MS
+# look for @VERSION bmf.. like IBMAVW.ANN
+>>0xD4 string =\x62\x6D\x66\x01\x00 Windows help annotation
+!:mime application/x-winhelp
+!:ext ann
+>>0xD4 string !\x62\x6D\x66\x01\x00
+# "GID Help index" by TrID
+>>>(4.l+0x65) string =|Pete Windows help Global Index
+!:mime application/x-winhelp
+!:ext gid
+# HeLP Bookmark or
+# "Windows HELP File" by TrID
+>>>(4.l+0x65) string !|Pete
+# maybe there exist a cleaner way to detect HeLP fragments
+# brute search for Magic 0x036C with matching Major maximal 7 iterations
+# discapp.hlp
+>>>>16 search/0x49AF/s \x6c\x03
+>>>>>&0 use help-ver-date
+>>>>>&4 leshort !1
+# putty.hlp
+>>>>>>&0 search/0x69AF/s \x6c\x03
+>>>>>>>&0 use help-ver-date
+>>>>>>>&4 leshort !1
+>>>>>>>>&0 search/0x49AF/s \x6c\x03
+>>>>>>>>>&0 use help-ver-date
+>>>>>>>>>&4 leshort !1
+>>>>>>>>>>&0 search/0x49AF/s \x6c\x03
+>>>>>>>>>>>&0 use help-ver-date
+>>>>>>>>>>>&4 leshort !1
+>>>>>>>>>>>>&0 search/0x49AF/s \x6c\x03
+>>>>>>>>>>>>>&0 use help-ver-date
+>>>>>>>>>>>>>&4 leshort !1
+>>>>>>>>>>>>>>&0 search/0x49AF/s \x6c\x03
+>>>>>>>>>>>>>>>&0 use help-ver-date
+>>>>>>>>>>>>>>>&4 leshort !1
+>>>>>>>>>>>>>>>>&0 search/0x49AF/s \x6c\x03
+# GCC.HLP is detected after 7 iterations
+>>>>>>>>>>>>>>>>>&0 use help-ver-date
+# this only happens if bigger hlp file is detected after used search iterations
+>>>>>>>>>>>>>>>>>&4 leshort !1 Windows y.z help
+!:mime application/winhelp
+!:ext hlp
+# repeat search again or following default line does not work
+>>>>16 search/0x49AF/s \x6c\x03
+# remaining files should be HeLP Bookmark WinHlp32.BMK (XP 32-bit) or WinHlp32 (Windows 8.1 64-bit)
+>>>>16 default x Windows help Bookmark
+!:mime application/x-winhelp
+!:ext bmk
+## FirstFreeBlock normally FFFFFFFFh 10h for *ANN
+##>>8 lelong x \b, FirstFreeBlock %#8.8x
+# EntireFileSize
+>>12 lelong x \b, %d bytes
+## ReservedSpace normally 042Fh AFh for *.ANN
+#>>(4.l) lelong x \b, ReservedSpace %#8.8x
+## UsedSpace normally 0426h A6h for *.ANN
+#>>(4.l+4) lelong x \b, UsedSpace %#8.8x
+## FileFlags normally 04...
+#>>(4.l+5) lelong x \b, FileFlags %#8.8x
+## file header magic 0x293B
+#>>(4.l+9) uleshort x \b, file header magic %#4.4x
+## file header Flags 0x0402
+#>>(4.l+11) uleshort x \b, file header Flags %#4.4x
+## file header PageSize 0400h 80h for *.ANN
+#>>(4.l+13) uleshort x \b, PageSize %#4.4x
+## Structure[16] z4
+#>>(4.l+15) string >\0 \b, Structure_"%-.16s"
+## MustBeZero 0
+#>>(4.l+31) uleshort x \b, MustBeZero %#4.4x
+## PageSplits
+#>>(4.l+33) uleshort x \b, PageSplits %#4.4x
+## RootPage
+#>>(4.l+35) uleshort x \b, RootPage %#4.4x
+## MustBeNegOne 0xffff
+#>>(4.l+37) uleshort x \b, MustBeNegOne %#4.4x
+## TotalPages 1
+#>>(4.l+39) uleshort x \b, TotalPages %#4.4x
+## NLevels 0x0001
+#>>(4.l+41) uleshort x \b, NLevels %#4.4x
+## TotalBtreeEntries
+#>>(4.l+43) ulelong x \b, TotalBtreeEntries %#8.8x
+## pages of the B+ tree
+#>>(4.l+47) ubequad x \b, PageStart %#16.16llx
+
+# start with colon or semicolon for comment line like Back2Life.cnt
+0 regex \^(:|;)
+# look for first keyword Base
+>0 search/45 :Base
+>>&0 use cnt-name
+# only solution to search again from beginning , because relative offsets changes when use is called
+>0 search/45 :Base
+>0 default x
+# look for other keyword Title like in putty.cnt
+>>0 search/45 :Title
+>>>&0 use cnt-name
+#
+# display mime type and name of Windows help Content source
+0 name cnt-name
+# skip space at beginning
+>0 string \040
+# name without extension and greater character or name with hlp extension
+>>1 regex/c \^([^\xd>]*|.*\\.hlp) MS Windows help file Content, based "%s"
+!:mime text/plain
+!:apple ????TEXT
+!:ext cnt
+#
+# Windows creates a full text search from hlp file, if the user clicks the "Find" tab and enables keyword indexing
+0 string tfMR MS Windows help Full Text Search index
+!:mime application/x-winhelp-fts
+!:ext fts
+>16 string >\0 for "%s"
+
+# Summary: Hyper terminal
+# Created by: unknown
+# Update: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/HyperACCESS
+# https://www.hilgraeve.com/hyperterminal/
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/h/ht.trid.xml
+# Note: called "HyperTerminal data file" by TrID and "HyperTerminal File" on English Windows
+0 string HyperTerminal\040
+>14 string 1.0\ --\ HyperTerminal\ data\ file MS Windows HyperTerminal profile
+#!:mime application/octet-stream
+!:mime application/x-ms-ht
+!:ext ht
+
+# https://ithreats.files.wordpress.com/2009/05/\040
+# lnk_the_windows_shortcut_file_format.pdf
+# Summary: Windows shortcut
+# Created by: unknown
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Windows_Shortcut
+# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/l/lnk-shortcut.trid.xml
+# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-SHLLINK/%5bMS-SHLLINK%5d.pdf
+# Note: called "Windows Shortcut" by TrID, "Microsoft Windows Shortcut" by DROID via PUID x-fmt/428 and "Windows shortcut file" by ./msdos (v 1.158)
+# partly verified by command like `lnkinfo AOL.lnk`
+# 'L' + GUUID
+# HeaderSize + LinkCLSID 00021401-0000-0000-C000-000000000046
+0 string \114\0\0\0\001\024\002\0\0\0\0\0\300\0\0\0\0\0\0\106 MS Windows shortcut
+!:mime application/x-ms-shortcut
+!:ext lnk
+# LinkFlags
+# HasLinkTargetIDList; if set a LinkTargetIDList structure MUST follow the ShellLinkHeader; If is not set, structure MUST NOT be present
+>20 lelong&1 1 \b, Item id list present
+# HasLinkInfo; if set a LinkInfo structure MUST follow the ShellLinkHeader or LinkTargetIDList; If is not set, structure MUST NOT be present
+>20 lelong&2 2 \b, Points to a file or directory
+>20 lelong&4 4 \b, Has Description string
+>20 lelong&8 8 \b, Has Relative path
+>20 lelong&16 16 \b, Has Working directory
+>20 lelong&32 32 \b, Has command line arguments
+>20 lelong&64 64 \b, Icon
+# IconIndex
+>>56 lelong x \b number=%d
+# IsUnicode; If set then StringData section contains Unicode-encoded strings
+>20 lelong&128 128 \b, Unicoded
+# ForceNoLinkInfo; LinkInfo structure is ignored
+>20 lelong&256 256 \b, NoLinkInfo
+# HasExpString; with an EnvironmentVariableDataBlock
+>20 lelong&512 512 \b, HasEnvironment
+# look for BlockSize 314h and EnvironmentVariableDataBlock BlockSignature A0000001h
+>>76 search/1972 \x14\x03\x00\x00\x01\x00\x00\xa0
+# TargetAnsi (260 bytes); NULL-terminated path to environment variable encoded with system default code page
+#>>>&0 string x '%s'
+# TargetUnicode (520 bytes): optional NULL-terminated path to same environment variable Unicode encoded
+# like: "%windir%\system32\calc.exe"
+>>>&260 lestring16 x "%s"
+# RunInSeparateProcess; run in a separate virtual machine when launching a 16-bit application; no examples found
+>20 lelong&1024 1024 \b, RunInSeparateProcess
+# Unused1; undefined and MUST be ignored
+#>20 lelong&2048 2048 \b, Unused1
+# HasDarwinID; with a DarwinDataBlock
+>20 lelong&4096 4096 \b, HasDarwinID
+# look for BlockSize 314h and DarwinDataBlock BlockSignature A0000006h
+>>76 search/1972 \x14\x03\x00\x00\x06\x00\x00\xa0
+# DarwinDataAnsi (260 bytes); NULL-terminated application identifier encoded with system default code page; SHOULD be ignored
+#>>>&0 string x '%s'
+# DarwinDataUnicode (520 bytes); NULL-terminated application identifier Unicode encoded
+>>>&260 lestring16 x "%s"
+# RunAsUser; target application is run as a different user
+>20 lelong&8192 8192 \b, RunAsUser
+# HasExpIcon; with an IconEnvironmentDataBlock
+>20 lelong&16384 16384 \b, HasExpIcon
+# look for BlockSize 314h and IconEnvironmentDataBlock BlockSignature A0000007h
+>>76 search/1972 \x14\x03\x00\x00\x07\x00\x00\xa0
+# TargetAnsi (260 bytes); NULL-terminated path to environment icon variable encoded with system default code page
+#>>>&0 string x '%s'
+# TargetUnicode (520 bytes); optional NULL-terminated path to same icon environment variable Unicode encoded
+# like: "%SystemDrive%\Program Files\YaCy\addon\YaCy.ico"
+>>>&260 lestring16 x "%s"
+# NoPidlAlias; represented in the shell namespace; no examples found
+>20 lelong&32768 32768 \b, NoPidlAlias
+# Unused2; undefined and MUST be ignored
+#>20 lelong&65536 65536 \b, Unused2
+# RunWithShimLayer; with a ShimDataBlock; no examples found
+>20 lelong&131072 131072 \b, RunWithShimLayer
+# ForceNoLinkTrack; TrackerDataBlock is ignored; no examples found
+>20 lelong&262144 262144 \b, ForceNoLinkTrack
+>20 lelong&262144 0
+# look for BlockSize 60h, TrackerDataBlock BlockSignature A0000003h, it length 58h and Version 0
+>>76 search/1972 \x60\x00\x00\x00\x03\x00\x00\xa0\x58\x00\x00\x00\0\0\0\0
+# MachineID (16 bytes); a NULL-terminated NetBIOS name encoded with system default code page of the machine
+>>>&0 string x \b, MachineID %0.16s
+# Droid (32 bytes)
+#
+# DroidBirth (32 bytes)
+#
+# EnableTargetMetadata; collect target properties and store in PropertyStoreDataBlock
+>20 lelong&524288 524288 \b, EnableTargetMetadata
+# look for BlockSize >= Ch, PropertyStoreDataBlock BlockSignature A0000009h
+#>>76 search/1972 \x00\x00\x09\x00\x00\xa0
+# PropertyStore (variable)
+#
+# DisableLinkPathTracking; EnvironmentVariableDataBlock is ignored; no examples found
+>20 lelong&1048576 1048576 \b, DisableLinkPathTracking
+# DisableKnownFolderTracking; SpecialFolderDataBlock and KnownFolderDataBlock are ignored and not saved
+>20 lelong&2097152 2097152 \b, DisableKnownFolderTracking
+>20 lelong&2097152 0
+# look for BlockSize 1Ch and KnownFolderDataBlock BlockSignature A000000Bh
+>>76 search/1972 \x1c\x00\x00\x00\x0B\x00\x00\xa0
+# https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/known-folder-guids-for-file-dialog-custom-places
+# KnownFolderID specifies the folder GUID ID
+# ProgramFiles 905E63B6-C1BF-494E-B29C-65B732D3D21A
+# ProgramFilesX86 7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E
+>>>&0 guid x KnownFolderID %s
+# DisableKnownFolderAlias; unaliased form of the known folder IDList SHOULD be used; no examples found
+>20 lelong&4194304 4194304 \b, DisableKnownFolderAlias
+# AllowLinkToLink; link that references another link is enabled; no examples found
+>20 lelong&8388608 8388608 \b, AllowLinkToLink
+# UnaliasOnSave; unaliased form of that known folder or the target IDList SHOULD be used; no examples found
+>20 lelong&16777216 16777216 \b, UnaliasOnSave
+# PreferEnvironmentPath; path specified in the EnvironmentVariableDataBlock SHOULD be used
+>20 lelong&33554432 33554432 \b, PreferEnvironmentPath
+# KeepLocalIDListForUNCTarget; UNC name SHOULD be stored in local path IDList in PropertyStoreDataBlock; no examples found
+>20 lelong&67108864 67108864 \b, KeepLocalIDListForUNCTarget
+# FileAttributes
+>24 lelong&1 1 \b, Read-Only
+>24 lelong&2 2 \b, Hidden
+>24 lelong&4 4 \b, System
+# Reserved1; MUST be zero
+>24 lelong&8 8 \b, Reserved1
+>24 lelong&16 16 \b, Directory
+>24 lelong&32 32 \b, Archive
+# Reserved2; MUST be zero
+>24 lelong&64 64 \b, Reserved2
+>24 lelong&128 128 \b, Normal
+>24 lelong&256 256 \b, Temporary
+# no examples found
+>24 lelong&512 512 \b, Sparse
+# no examples found
+>24 lelong&1024 1024 \b, Reparse point
+>24 lelong&2048 2048 \b, Compressed
+>24 lelong&4096 4096 \b, Offline
+# FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; contents need to be indexed
+>24 lelong&8192 8192 \b, NeedIndexed
+# FILE_ATTRIBUTE_ENCRYPTED; file or directory is encrypted
+>24 lelong&16384 16384 \b, Encrypted
+# value zero means there is no time set on the target
+>28 leqwdate !0 \b, ctime=%s
+# Access time of target in UTC
+>36 leqwdate !0 \b, atime=%s
+# write time of target in UTC
+>44 leqwdate !0 \b, mtime=%s
+# FileSize; 32 bit size of target in bytes
+>52 lelong x \b, length=%u, window=
+# ShowCommand; 1~SW_SHOWNORMAL 3~SW_SHOWMAXIMIZED HerzlichMEDION.lnk 7~SW_SHOWMINNOACTIVE YaCy.lnk Privoxy.lnk; All other values like 2 MUST be treated as SW_SHOWNORMAL
+#>60 lelong x ShowCommand=%#x
+>60 lelong x
+>>60 lelong 3 \bshowmaximized
+>>60 lelong 7 \bshowminnoactive
+>>60 default x \bnormal
+# Hotkey
+>64 uleshort >0 \b, hot key
+# 41h~A 42h~B ...
+>>64 ubyte x %c
+# modifier keys: 0x01~HOTKEYF_SHIFT 0x02~HOTKEYF_CONTROL 0x04~HOTKEYF_ALT
+>>65 ubyte&1 1 \b+SHIFT
+>>65 ubyte&2 2 \b+CONTROL
+>>65 ubyte&4 4 \b+ALT
+# Reserved; MUST be zero
+#>66 uleshort !0 \b, reserved %#x
+# Reserved2; MUST be zero
+#>68 ulelong !0 \b, reserved2 %#x
+# Reserved3; MUST be zero
+#>72 ulelong !0 \b, reserved3 %#x
+# optional LINKTARGET_IDLIST if LinkFlags bit HasLinkTargetIDList is set
+>20 lelong&1 1
+# IDListSize; size of IDList
+>>76 uleshort x \b, IDListSize %#4.4x
+# 1st item
+>>78 use lnk-item
+# 2nd possible item
+>>(78.s+78) uleshort >0
+>>>(78.s+78) use lnk-item
+# 3rd possible item
+>>>&(&-2.s-2) uleshort >0
+>>>>&-2 use lnk-item
+# 4th possible item
+>>>>&(&-2.s-2) uleshort >0
+>>>>>&-2 use lnk-item
+# Because HasLinkInfo is set, a LinkInfo structure follows
+>20 lelong&2 2
+# if no LINKTARGET_IDLIST (no HasLinkTargetIDList) then direct after header; no example found
+>>20 lelong&1 =0
+>>>76 use lnk-info
+# if LINKTARGET_IDLIST (HasLinkTargetIDList) then after LINKTARGET_IDLIST by addtional IDListSize bytes
+>>20 lelong&1 =1
+>>>76 uleshort >0
+#>>>>(76.s+78) use lnk-info
+>>>>(76.s+78) ubelong x
+# move pointer to beginnig of LinkInfo structure
+>>>>>&-8 ubelong x
+#>>>>>>&16 ulelong x \b, LocalBasePathOffset=%#8.8x
+>>>>>>&(&16.l) string x \b, LocalBasePath "%s"
+# check and then display link item (size,data)
+0 name lnk-item
+# size value 0x0000 means TerminalID; indicates the end of the item IDs list
+>0 uleshort >0
+#>>0 uleshort x \b, ItemIDSize %#4.4x
+# item Data
+#>>2 ubequad x \b, Item data=%#16.16llx
+#>>2 ubyte x \b, Item type=%#x
+>>2 ubyte =0x1f \b, Root folder
+# like: "26EE0668-A00A-44D7-9371-BEB064C98683" Control Panel
+# "20D04FE0-3AEA-1069-A2D8-08002B30309D" My Computer
+# "871C5380-42A0-1069-A2EA-08002B30309D" Internet Explorer
+>>>4 guid x "%s"
+>>2 ubyte =0x2f \b, Volume
+# like: "C:\" "D:\"
+>>>3 string x "%s"
+# Control panel category
+#>>2 ubyte foo \b, Control panel category
+# display LinkInfo structure (size,flags,offsets)
+0 name lnk-info
+# LinkInfoSize; size of the LinkInfo structure
+>0 ulelong x \b, LinkInfoSize %#x
+# LinkInfoHeaderSize; if 1C no optional fields; >=24 optional fields are specified
+>4 ulelong x \b, LinkInfoHeaderSize %#x
+# LinkInfoFlags;
+#>8 ulelong x \b, LinkInfoFlags=%#x
+>8 ulelong&1 1 \b, VolumeIDAndLocalBasePath
+# VolumeIDOffset; location of the VolumeID field (VolumeIDSize DriveType DriveSerialNumber VolumeLabelOffset ... ) inside LinkInfo structure
+>>12 ulelong x \b, VolumeIDOffset %#x
+# LocalBasePathOffset; location of LocalBasePath field like "C:\test\a.txt" inside LinkInfo structure
+>>16 ulelong x \b, LocalBasePathOffset %#x
+# LocalBasePathOffsetUnicode; location of the LocalBasePathUnicode field inside LinkInfo structure
+>>4 ulelong >23
+>>>28 ulelong x \b, LocalBasePathOffsetUnicode %#x
+>8 ulelong&2 2 \b, CommonNetworkRelativeLinkAndPathSuffix
+# CommonNetworkRelativeLinkOffset; location of the CommonNetworkRelativeLink field inside LinkInfo structure
+>>20 ulelong x \b, CommonNetworkRelativeLinkOffset %#x
+# CommonPathSuffixOffset; location of CommonPathSuffix field
+>24 ulelong x \b, CommonPathSuffixOffset %#x
+# CommonPathSuffixOffsetUnicode; location of CommonPathSuffixUnicode field inside LinkInfo structure
+>4 ulelong >23
+>>32 ulelong x \b, CommonPathSuffixOffsetUnicode %#x
+
+# Summary: Outlook Personal Folders
+# Created by: unknown
+# Update: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/Personal_Folder_File
+# https://en.wikipedia.org/wiki/Personal_Storage_Table
+# Reference: https://interoperability.blob.core.windows.net/files/MS-PST/%5bMS-PST%5d.pdf
+# http://mark0.net/download/triddefs_xml.7z/defs/p/pab.trid.xml
+# dwMagic !BDN
+0 lelong 0x4E444221
+# skip DROID x-fmt-75-signature-id-472.pab x-fmt-248-signature-id-260.pst x-fmt-249-signature-id-261.pst
+# by check for existance of bPlatformCreate value
+>14 ubyte x Microsoft Outlook
+#!:mime application/octet-stream
+# NOT official registered !
+!:mime application/vnd.ms-outlook
+# dwCRCPartial; 32-bit cyclic redundancy check (CRC) value of followin 471 bytes; zero for 64-bit
+#>>4 ulelong !0 \b, CRC %#x
+# wMagicClient; AB (4142h) is used for PAB files; SM (534Dh) is used for PST files; SO (534Fh) is used for OST files
+#>>8 leshort x \b, wMagicClient=%#x
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/pab.trid.xml
+# Note: called "Microsoft Personal Address Book" by TrID and
+# "Microsoft Outlook Personal Address Book" by DROID via x-fmt/75
+>>8 leshort 0x4142 Personal Address Book
+#!:mime application/x-ms-pab
+!:ext pab
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/pst.trid.xml
+# http://mark0.net/download/triddefs_xml.7z/defs/p/pst-unicode.trid.xml
+# Note: called "Microsoft OutLook Personal Folder" by TrID and
+# by DROID via x-fmt/248 for ANSI and via x-fmt/249 for Unicode
+#>>8 leshort 0x4D53 \b, PST~
+# called "Microsoft Outlook email folder" in ./windows version 1.37 and older
+>>8 leshort 0x4D53 Personal Storage
+#!:mime application/x-ms-pst
+!:ext pst
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/o/ost.trid.xml
+# Note: called "Outlook Exchange Offline Storage" by TrID
+>>8 leshort 0x4F53 Offline Storage
+#!:mime application/x-ms-ost
+!:ext ost
+# wVer; file format version. 14 or 15 if the file is ANSI; > 21 or 23(=17h) if Unicode; 37 for written by Outlook with WIP
+>>10 uleshort x (
+# probably NO intermediate versions exist
+>>10 leshort <0x10 \b<=2002, ANSI,
+>>10 leshort >0x14 \b>=2003, Unicode,
+>>10 uleshort x version %u)
+# wVerClient; client file format version like: 19 22
+#>>12 uleshort x \b, wVerClient=%u
+# bPlatformCreate; This value MUST be set to 1 but also found 2
+>>14 ubyte >1 \b, bPlatformCreate=%u
+# bPlatformAccess; This value MUST be set to 1 but also found 2
+>>15 ubyte >1 \b, bPlatformAccess=%u
+# dwReserved1; SHOULD ignore and NOT modify this value; SHOULD initialize to zero
+>>16 ulelong !0 \b, dwReserved1=%#x
+# dwReserved2; SHOULD ignore and NOT modify this value; SHOULD initialize to zero
+>>20 ulelong !0 \b, dwReserved2=%#x
+# ANSI 32-bit variant Outlook 1997-2002
+>>10 uleshort <16
+# bidNextB; next BlockID (ANSI 4 bytes)
+#>>>24 ulelong !0 \b, bidNextB=%#x
+# bidNextP; Next available back BlockID pointer
+#>>>28 ulelong !0 \b, bidNextP=%#x
+# dwUnique; value monotonically increased when modifying PST; so CRC is changing
+>>>32 ulelong !0 \b, dwUnique=%#x
+# rgnid[128]; A fixed array of 32 NodeIDs, each corresponding to one of the 32 possible NID_TYPEs
+#>>>36 ubequad x \b, rgnid=%#llx...
+# dwReserved; Implementations SHOULD ignore this value and SHOULD NOT modify it; Initialized zero
+>>>164 ulelong !0 \b, dwReserved=%#x
+# ibFileEof; the size of the PST file, in bytes (ANSI 4 bytes)
+>>>168 ulelong x \b, %u bytes
+# ibAMapLast; offset to the last AMap page
+#>>>172 ulelong x \b, ibAMapLast=%#x
+# bSentinel; MUST be set to 0x80
+>>>460 ubyte !0x80 \b, bSentinel=%#x
+# bCryptMethod: 0~No encryption 1~encryption with permutation 2~encryption with cyclic 16~encryption with Windows Information Protection (WIP)
+>>>461 ubyte >0 \b, bCryptMethod=%u
+# UNICODE 64-bit variant Outlook 2003-2007
+>>10 uleshort >20
+# bidUnused; Unused 8 bytes padding (Unicode only); sometimes like: 0x0000000100000004
+>>>24 ulequad !0x0000000100000004 \b, bidUnused=%#16.16llx
+# dwUnique; value monotonically increased when modifying PST; so CRC is changing
+>>>40 ulelong !0 \b, dwUnique=%#x
+# rgnid[] (128 bytes): A fixed array of 32 NIDs, each corresponding to one of the 32 possible
+#>>>44 ubequad x \b, rgnid=%#llx...
+# ibFileEof; the size of the PST file, in bytes (Unicode 8 bytes)
+>>>184 ulequad x \b, %llu bytes
+# bSentinel; MUST be set to 0x80
+>>>512 ubyte !0x80 \b, bSentinel=%#x
+# bCryptMethod; Encryption type like: 0 1 2 16
+>>>513 ubyte >0 \b, bCryptMethod=%u
+# dwCRC; 32-bit CRC of the of the previous 516 bytes
+>>>524 ulelong x \b, CRC32 %#x
+
+
+# Summary: Windows help cache
+# Created by: unknown
+0 string \164\146\115\122\012\000\000\000\001\000\000\000 MS Windows help cache
+
+
+# Summary: IE cache file
+# Created by: Christophe Monniez
+0 string Client\ UrlCache\ MMF Internet Explorer cache file
+>20 string >\0 version %s
+
+
+# Summary: Registry files
+# Created by: unknown
+# Modified by (1): Joerg Jenderek
+0 string regf MS Windows registry file, NT/2000 or above
+0 string CREG MS Windows 95/98/ME registry file
+0 string SHCC3 MS Windows 3.1 registry file
+
+
+# Summary: Windows Registry text
+# URL: https://en.wikipedia.org/wiki/Windows_Registry#.REG_files
+# Reference: http://fileformats.archiveteam.org/wiki/Windows_Registry
+# Submitted by: Abel Cheung <abelcheung@gmail.com>
+# Update: Joerg Jenderek
+# Windows 3-9X variant
+0 string REGEDIT
+# skip ASCII text like "REGEDITor.txt" but match
+# L1WMAP.REG with only 1 CRNL or org.gnome.gnumeric.reg with 2 NL
+>7 search/3 \n Windows Registry text
+!:mime text/x-ms-regedit
+!:ext reg
+# Windows 9X variant
+>>0 string REGEDIT4 (Win95 or above)
+# Windows 2K ANSI variant
+0 string Windows\ Registry\ Editor\
+>&0 string Version\ 5.00\r\n\r\n Windows Registry text (Win2K or above)
+!:mime text/x-ms-regedit
+!:ext reg
+# Windows 2K UTF-16 variant
+2 lestring16 Windows\ Registry\ Editor\
+>0x32 lestring16 Version\ 5.00\r\n\r\n Windows Registry little-endian text (Win2K or above)
+# relative offset not working
+#>&0 lestring16 Version\ 5.00\r\n\r\n Windows Registry little-endian text (Win2K or above)
+!:mime text/x-ms-regedit
+!:ext reg
+# WINE variant
+# URL: https://en.wikipedia.org/wiki/Wine_(software)
+# Reference: https://www.winehq.org/pipermail/wine-cvs/2005-October/018763.html
+# Note: WINE use text based registry (system.reg,user.reg,userdef.reg)
+# instead binary hiv structure like Windows
+0 string WINE\ REGISTRY\ Version\ WINE registry text
+# version 2
+>&0 string x \b, version %s
+!:mime text/x-wine-extension-reg
+!:ext reg
+
+# Windows *.INF *.INI files updated by Joerg Jenderek at Apr 2013, Feb 2018
+# empty ,comment , section
+# PR/383: remove unicode BOM because it is not portable across regex impls
+#0 regex/s \\`(\\r\\n|;|[[])
+# empty line CRLF
+0 ubeshort 0x0D0A
+>0 use ini-file
+# comment line starting with semicolon
+0 string ;
+# look for phrase of Windows policy ADMinistrative template (with starting remark)
+# like: WINDOW_95_CD/TOOLS/RESKIT/netadmin/poledit/conf.adm
+>1 search/3548 END\040CATEGORY
+# ADM with remark (by adm-rem.trid.xml) already done by generic ASCII variant
+# if no Windows policy ADMinistrative template then Windows INItialization
+>1 default x
+>>0 use ini-file
+# section line starting with left bracket
+0 string [
+>0 use ini-file
+# check and then display Windows INItialization configuration
+0 name ini-file
+# look for left bracket in section line
+>0 search/8192 [
+# https://en.wikipedia.org/wiki/Autorun.inf
+# https://msdn.microsoft.com/en-us/library/windows/desktop/cc144200.aspx
+# space after right bracket
+# or AutoRun.Amd64 for 64 bit systems
+# or only NL separator
+>>&0 regex/c \^autorun
+# but sometimes total commander directory tree file "treeinfo.wc" with lines like
+# [AUTORUN]
+# [boot]
+>>>&0 string =]\r\n[ Total commander directory treeinfo.wc
+!:mime text/plain
+!:ext wc
+# From: Pal Tamas <folti@balabit.hu>
+# Autorun File
+>>>&0 string !]\r\n[ Microsoft Windows Autorun file
+!:mime application/x-setupscript
+!:ext inf
+# https://msdn.microsoft.com/en-us/library/windows/hardware/ff549520(v=vs.85).aspx
+# version strings ASCII coded case-independent for Windows setup information script file
+>>&0 regex/c \^(version|strings)] Windows setup INFormation
+!:mime application/x-setupscript
+#!:mime application/x-wine-extension-inf
+!:ext inf
+# NETCRC.INF OEMCPL.INF
+>>&0 regex/c \^(WinsockCRCList|OEMCPL)] Windows setup INFormation
+!:mime application/x-setupscript
+!:ext inf
+# http://www.winfaq.de/faq_html/Content/tip2500/onlinefaq.php?h=tip2653.htm
+# https://msdn.microsoft.com/en-us/library/windows/desktop/cc144102.aspx
+# .ShellClassInfo DeleteOnCopy LocalizedFileNames ASCII coded case-independent
+>>&0 regex/1024c \^(\\.ShellClassInfo|DeleteOnCopy|LocalizedFileNames)] Windows desktop.ini
+!:mime application/x-wine-extension-ini
+#!:mime text/plain
+# https://support.microsoft.com/kb/84709/
+>>&0 regex/c \^don't\ load] Windows CONTROL.INI
+!:mime application/x-wine-extension-ini
+!:ext ini
+>>&0 regex/c \^(ndishlp\\$|protman\\$|NETBEUI\\$)] Windows PROTOCOL.INI
+!:mime application/x-wine-extension-ini
+!:ext ini
+# https://technet.microsoft.com/en-us/library/cc722567.aspx
+# http://www.winfaq.de/faq_html/Content/tip0000/onlinefaq.php?h=tip0137.htm
+>>&0 regex/c \^(windows|Compatibility|embedding)] Windows WIN.INI
+!:mime application/x-wine-extension-ini
+!:ext ini
+# https://en.wikipedia.org/wiki/SYSTEM.INI
+>>&0 regex/c \^(boot|386enh|drivers)] Windows SYSTEM.INI
+!:mime application/x-wine-extension-ini
+!:ext ini
+# http://www.mdgx.com/newtip6.htm
+>>&0 regex/c \^SafeList] Windows IOS.INI
+!:mime application/x-wine-extension-ini
+!:ext ini
+# https://en.wikipedia.org/wiki/NTLDR Windows Boot Loader information
+>>&0 regex/c \^boot\x20loader] Windows boot.ini
+!:mime application/x-wine-extension-ini
+!:ext ini
+# https://en.wikipedia.org/wiki/CONFIG.SYS
+>>&0 regex/c \^menu] MS-DOS CONFIG.SYS
+# @CONFIG.UI configuration file of previous DOS version saved by Caldera OPENDOS INSTALL.EXE
+# CONFIG.PSS saved version of file CONFIG.SYS created by %WINDIR%\SYSTEM\MSCONFIG.EXE
+# CONFIG.TSH renamed file CONFIG.SYS.BAT by %WINDIR%\SYSTEM\MSCONFIG.EXE
+# dos and w40 used in dual booting scene
+!:ext sys/dos/w40
+# https://support.microsoft.com/kb/118579/
+>>&0 regex/c \^Paths]\r\n MS-DOS MSDOS.SYS
+!:ext sys/dos
+# http://chmspec.nongnu.org/latest/INI.html#HHP
+>>&0 regex/c \^options]\r\n Microsoft HTML Help Project
+!:mime text/plain
+!:ext hhp
+# From: Joerg Jenderek
+# URL: https://documentation.basis.com/BASISHelp/WebHelp/b3odbc/ODBC_Driver/obdcdriv_character_translation.htm
+# Reference: https://www.garykessler.net/library/file_sigs.html
+# http://mark0.net/download/triddefs_xml.7z/defs/c/cpx.trid.xml
+# Note: stored in directory %WINDIR%\SysWOW64 or %WINDIR%\system
+# second word often Latin but sometimes Cyrillic like in 12510866.CPX
+>>&0 regex/c \^Windows\ (Latin|Cyrillic) Windows codepage translator
+#!:mime text/plain
+!:mime text/x-ms-cpx
+# like: 12510866.CPX
+!:ext cpx
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/File_Explorer
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/s/scf-exp.trid.xml,scf-exp-old.trid.xml
+# Note: called "Windows Explorer Command Shell File" by TrID and "File Explorer Command" by Windows via SHCmdFile
+>>&0 regex/c \^Shell]\r\n Windows Explorer Shell Command File
+#!:mime text/plain
+!:mime text/x-ms-scf
+# like: channels.scf desktop.scf explorer.scf "Desktop anzeigen.scf"
+!:ext scf
+# look for icon file directive maybe pointing to malicious file
+>>>1 search/128 IconFile= \b, icon
+>>>>&0 string x "%s"
+# From: Joerg Jenderek
+# URL: http://en.wikipedia.org/wiki/VIA_Technologies
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/s/scf-via.trid.xml
+# Note: called "VIA setup configuration file" by TrID
+>>&0 regex/c \^SCF]\r\n VIA setup configuration
+#!:mime text/plain
+!:mime text/x-via-scf
+# like: SETUP.SCF
+!:ext scf
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/InstallShield
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/l/lid-is.trid.xml
+# Note: contain also 3 keywords like: count Default key0
+>>&0 regex/c \^Languages] InstallShield Language Identifier
+#!:mime text/plain
+!:mime text/x-installshield-lid
+# like: SETUP.LID
+!:ext lid
+# From: Joerg Jenderek
+# URL: https://www.file-extensions.org/tag-file-extension
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/t/taginfo.trid.xml
+# Note: contain also keywords like: Application Category Company Misc Version
+>>&0 regex/c \^TagInfo] TagInfo
+#!:mime text/plain
+#!:mime text/prs.lines.tag
+!:mime text/x-ms-tag
+# like: DATA.TAG
+!:ext tag
+# URL: https://en.wikipedia.org/wiki/Flatpak
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/f/flatpakref.trid.xml
+# Note: called "Flatpack Reference" by TrID
+>>&0 string Flatpak\ Ref] Flatpak repository reference
+#!:mime text/plain
+# https://reposcope.com/mimetype/application/vnd.flatpak.ref
+!:mime application/vnd.flatpak.ref
+!:ext flatpakref
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/CloneCD
+# Reference: https://en.wikipedia.org/wiki/CloneCD_Control_File
+# http://mark0.net/download/triddefs_xml.7z/defs/c/cdimage-clonecd-cue.trid.xml
+# Note: called "CloneCD CDImage (description)" by TrID and "CloneCD Control File" by DROID via PUID fmt/1760
+>>&0 string CloneCD] CloneCD CD-image Description
+#!:mime text/plain
+!:mime text/x-ccd
+!:ext ccd
+# unknown keyword after opening bracket
+>>&0 default x
+#>>>&0 string/c x UNKNOWN [%s
+# look for left bracket of second section
+>>>&0 search/8192 [
+# version Strings FileIdentification
+>>>>&0 string/c version Windows setup INFormation
+!:mime application/x-setupscript
+!:ext inf
+# From: Joerg Jenderek
+# URL: https://cdrtfe.sourceforge.io/
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/c/cfp-cdrtfe.trid.xml
+>>>>&0 string FileExplorer] cdrtfe Project
+!:mime text/x-cfp
+!:ext cfp
+# https://en.wikipedia.org/wiki/Initialization_file Windows Initialization File or other
+>>>>&0 default x
+>>>>>&0 ubyte x
+# characters, digits, underscore and white space followed by right bracket
+# terminated by CR implies section line to skip BOOTLOG.TXT DETLOG.TXT
+>>>>>>&-1 regex/T \^([A-Za-z0-9_\(\)\ ]+)\]\r Generic INItialization configuration [%-.40s
+# NETDEF.INF multiarc.ini
+#!:mime application/x-setupscript
+!:mime application/x-wine-extension-ini
+#!:mime text/plain
+!:ext ini/inf
+# samples with only 1 and unknown section name
+# XXX: matches a file containing '[1] 2'
+#>>>&0 default x Generic INItialization configuration
+#>>>>0 string x \b, 1st line "%s"
+# UTF-16 BOM
+0 ubeshort =0xFFFE
+# look for phrase of Windows policy ADMinistrative template (UTF-16 by adm-uni.trid.xml)
+# like: wuau.adm
+>2 search/0x384A E\0N\0D\0\040\0C\0A\0T\0E\0G\0O\0R\0Y\0
+>>0 use windows-adm
+# if no Windows policy ADMinistrative template then Windows INFormation
+>2 default x
+# UTF-16 BOM followed by CR~0D00 , comment~semicolon~3B00 , section~bracket~5B00
+>>0 ubelong&0xFFff89FF =0xFFFE0900
+# look for left bracket in section line
+>>>2 search/8192 [
+# keyword without 1st letter which is maybe up-/down-case
+>>>>&3 lestring16 ersion] Windows setup INFormation
+!:mime application/x-setupscript
+# like: hdaudio.inf iscsi.inf spaceport.inf tpm.inf usbhub3.inf UVncVirtualDisplay.inf
+!:ext inf
+>>>>&3 lestring16 trings] Windows setup INFormation
+!:mime application/x-setupscript
+# like: arduino_gemma.inf iis.inf MSM8960.inf
+!:ext inf
+>>>>&3 lestring16 ourceDisksNames] Windows setup INFormation
+!:mime application/x-setupscript
+# like: atiixpag.inf mdmnokia.inf netefe32.inf rdpbus.inf
+!:ext inf
+# netnwcli.inf start with ;---[ NetNWCli.INX ]
+>>>>&3 default x
+# look for NL followed by left bracket
+>>>>>&0 search/8192 \x0A\x00\x5b
+# like: defltwk.inf netvwifibus.inf WSDPrint.inf
+>>>>>>&3 lestring16 ersion] Windows setup INFormation
+!:mime application/x-setupscript
+!:ext inf
+
+# Summary: Windows Policy ADMinistrative template
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Administrative_Template
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/adm.trid.xml
+# Note: typically stored in directory like: %WINDIR%\system32\GroupPolicy\ADM
+# worst case ASCII variant starting with remark line like: inetset.adm
+0 search/0x4E CLASS\040
+>&0 string MACHINE
+>>0 use windows-adm
+>&0 string USER
+>>0 use windows-adm
+# display information about Windows policy ADMinistrative template
+0 name windows-adm Windows Policy Administrative Template
+!:mime text/x-ms-adm
+!:ext adm
+# UTF-16 BOM implies UTF-16 encoded ADM (by adm-uni.trid.xml)
+>0 ubeshort =0xFFFE
+>>2 lestring16 x \b, 1st line "%s"
+# look for UTF-16 encoded CarriageReturn LineFeed
+>>>2 search/0x3A \r\0\n\0
+>>>>&0 lestring16 x \b, 2nd line "%s"
+# no UTF-16 BOM implies "ASCII" encoded ADM (by adm.trid.xml)
+>0 ubeshort !0xFFFE
+>>0 string x \b, 1st line "%s"
+#>>>&0 ubequad x \b, 2ND %16.16llx
+# 2nd line empty
+>>>&2 beshort =0x0D0A
+>>>>&0 beshort !0x0D0A \b, 3th line
+>>>>>&-2 string x "%s"
+# 2nd line with content
+>>>&2 beshort !0x0D0A \b, 2nd line
+>>>>&-2 string x "%s"
+
+# Windows Precompiled INF files *.PNF added by Joerg Jenderek at Mar 2013 of _PNF_HEADER inf.h
+# http://read.pudn.com/downloads3/sourcecode/windows/248345/win2k/private/windows/setup/setupapi/inf.h__.htm
+# URL: http://fileformats.archiveteam.org/wiki/INF_(Windows)
+# Reference: http://en.verysource.com/code/10350344_1/inf.h.html
+# Note: stored in %Windir%\Inf %Windir%\System32\DriverStore\FileRepository
+# check for valid major and minor versions: 101h - 303h
+0 leshort&0xFcFc =0x0000
+# GRR: line above (strength 50) is too general as it catches also "PDP-11 UNIX/RT ldp" ./pdp
+>0 leshort&0x0303 !0x0000
+# test for valid InfStyles: 1 2
+>>2 uleshort >0
+>>>2 uleshort <3
+# look for colon in WinDirPath after PNF header
+#>>>>0x59 search/18 :
+# skip few Adobe Photoshop Color swatch ("Mac OS.aco" TRUMATCH-Farben.aco Windows.aco) and some
+# Targa image (money-256.tga XING_B_UCM8.tga x-fmt-367-signature-id-604.tga) with "invalid low section name" \0
+>>>>(20.l) ubelong >0x40004000
+>>>>>0 use PreCompiledInf
+0 name PreCompiledInf
+>0 uleshort x Windows Precompiled iNF
+!:mime application/x-pnf
+!:ext pnf
+# major version 1 for older Windows like XP and 3 since about Windows Vista
+# 101h~95-XP; 301h~Windows Vista-7 ; 302h~Windows 10 14393; 303h~Windows 10 18362-Windows11
+>1 ubyte x \b, version %u
+>0 ubyte x \b.%u
+>0 uleshort =0x0101 (Windows
+>>4 ulelong&0x00000001 !0x00000001 95-98)
+>>4 ulelong&0x00000001 =0x00000001 XP)
+>0 uleshort =0x0301 (Windows Vista-8.1)
+>0 uleshort =0x0302 (Windows 10 older)
+>0 uleshort =0x0303 (Windows 10-11)
+# 1 ,2 (windows 98 SE)
+>2 uleshort !2 \b, InfStyle %u
+# PNF_FLAG_IS_UNICODE 0x00000001
+# PNF_FLAG_HAS_STRINGS 0x00000002
+# PNF_FLAG_SRCPATH_IS_URL 0x00000004
+# PNF_FLAG_HAS_VOLATILE_DIRIDS 0x00000008
+# PNF_FLAG_INF_VERIFIED 0x00000010
+# PNF_FLAG_INF_DIGITALLY_SIGNED 0x00000020
+# UNKNOWN8 0x00000080
+# UNKNOWN 0x00000100
+# UNKNOWN1 0x01000000
+# UNKNOWN2 0x02000000
+>4 ulelong&0x03000180 >0 \b, flags
+>>4 ulelong x %#x
+>4 ulelong&0x00000001 0x00000001 \b, unicoded
+>4 ulelong&0x00000002 0x00000002 \b, has strings
+>4 ulelong&0x00000004 0x00000004 \b, src URL
+>4 ulelong&0x00000008 0x00000008 \b, volatile dir ids
+>4 ulelong&0x00000010 0x00000010 \b, verified
+>4 ulelong&0x00000020 0x00000020 \b, digitally signed
+# >4 ulelong&0x00000080 0x00000080 \b, UNKNOWN8
+# >4 ulelong&0x00000100 0x00000100 \b, UNKNOWN
+# >4 ulelong&0x01000000 0x01000000 \b, UNKNOWN1
+# >4 ulelong&0x02000000 0x02000000 \b, UNKNOWN2
+#>8 ulelong x \b, InfSubstValueListOffset %#x
+# many 0, 1 lmouusb.PNF, 2 linkfx10.PNF , f webfdr16.PNF
+# , 6 bth.PNF, 9 usbport.PNF, d netnwifi.PNF, 10h nettcpip.PNF
+#>12 uleshort x \b, InfSubstValueCount %#x
+# only < 9 found: 8 hcw85b64.PNF
+#>14 uleshort x \b, InfVersionDatumCount %#x
+# only found values lower 0x0000ffff ??
+#>16 ulelong x \b, InfVersionDataSize %#x
+# only found positive values lower 0x00ffFFff for InfVersionDataOffset
+>20 ulelong x \b, at %#x
+>4 ulelong&0x00000001 =0x00000001
+# case independent: CatalogFile Class DriverVer layoutfile LayoutFile SetupClass signature Signature
+>>(20.l) lestring16 x "%s"
+>4 ulelong&0x00000001 !0x00000001
+>>(20.l) string x "%s"
+# FILETIME is number of 100-nanosecond intervals since 1 January 1601
+#>24 ulequad x \b, InfVersionLastWriteTime %16.16llx
+>24 qwdate x \b, InfVersionLastWriteTime %s
+# for Windows 98, XP
+>0 uleshort <0x0102
+# only found values lower 0x00ffFFff
+# often 70 but also 78h for corelist.PNF
+# >>32 ulelong x \b, StringTableBlockOffset %#x
+# >>36 ulelong x \b, StringTableBlockSize %#x
+# >>40 ulelong x \b, InfSectionCount %#x
+# >>44 ulelong x \b, InfSectionBlockOffset %#x
+# >>48 ulelong x \b, InfSectionBlockSize %#x
+# >>52 ulelong x \b, InfLineBlockOffset %#x
+# >>56 ulelong x \b, InfLineBlockSize %#x
+# >>60 ulelong x \b, InfValueBlockOffset %#x
+# >>64 ulelong x \b, InfValueBlockSize %#x
+# WinDirPathOffset
+# like 58h, which means direct after PNF header
+#>>68 ulelong x \b, at %#x
+>>68 ulelong x
+>>>4 ulelong&0x00000001 =0x00000001
+#>>>>(68.l) ubequad =0x43003a005c005700
+# normally unicoded C:\Windows
+#>>>>>(68.l) lestring16 x \b, WinDirPath "%s"
+>>>>(68.l) ubequad !0x43003a005c005700
+>>>>>(68.l) lestring16 x \b, WinDirPath "%s"
+>>>4 ulelong&0x00000001 !0x00000001
+# normally ASCII C:\WINDOWS
+#>>>>(68.l) string =C:\\WINDOWS \b, WinDirPath "%s"
+>>>>(68.l) string !C:\\WINDOWS
+>>>>>(68.l) string x \b, WinDirPath "%s"
+# found OsLoaderPathOffset values often 0 , once 70h corelist.PNF, once 68h ASCII machine.PNF
+>>>72 ulelong >0 \b,
+>>>>4 ulelong&0x00000001 =0x00000001
+>>>>>(72.l) lestring16 x OsLoaderPath "%s"
+>>>>4 ulelong&0x00000001 !0x00000001
+# seldom C:\ instead empty
+>>>>>(72.l) string x OsLoaderPath "%s"
+# 1fdh
+#>>>76 uleshort x \b, StringTableHashBucketCount %#x
+# https://docs.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a
+# only 407h found
+>>>78 uleshort !0x409 \b, LanguageID %x
+#>>>78 uleshort =0x409 \b, LanguageID %x
+# InfSourcePathOffset often 0
+>>>80 ulelong >0 \b, at %#x
+>>>>4 ulelong&0x00000001 =0x00000001
+>>>>>(80.l) lestring16 x SourcePath "%s"
+>>>>4 ulelong&0x00000001 !0x00000001
+>>>>>(80.l) string >\0 SourcePath "%s"
+# OriginalInfNameOffset often 0
+>>>84 ulelong >0 \b, at %#x
+>>>>4 ulelong&0x00000001 =0x00000001
+>>>>>(84.l) lestring16 x InfName "%s"
+>>>>4 ulelong&0x00000001 !0x00000001
+>>>>>(84.l) string >\0 InfName "%s"
+
+# for newer Windows like Vista, 7 , 8.1 , 10
+>0 uleshort >0x0101
+>>80 ulelong x \b, at %#x WinDirPath
+>>>4 ulelong&0x00000001 0x00000001
+# normally unicoded C:\Windows
+#>>>>(80.l) ubequad =0x43003a005c005700
+#>>>>>(80.l) lestring16 x "%s"
+>>>>(80.l) ubequad !0x43003a005c005700
+>>>>>(80.l) lestring16 x "%s"
+# language id: 0 407h~german 409h~English_US
+>>90 uleshort !0x409 \b, LanguageID %x
+#>>90 uleshort =0x409 \b, LanguageID %x
+>>92 ulelong >0 \b, at %#x
+>>>4 ulelong&0x00000001 0x00000001
+# language string like: de-DE en-US
+>>>>(92.l) lestring16 x language %s
+
+# Summary: backup file created with utility like NTBACKUP.EXE shipped with Windows NT/2K/XP/2003
+# Extension: .bkf
+# Created by: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/NTBackup
+# Reference: http://laytongraphics.com/mtf/MTF_100a.PDF
+# Descriptor BloCK name of Microsoft Tape Format
+0 string TAPE
+# Format Logical Address is zero
+>20 ulequad 0
+# Reserved for MBC is zero
+>>28 uleshort 0
+# Control Block ID is zero
+>>>36 ulelong 0
+# BIT4-BIT15, BIT18-BIT31 of block attributes are unused
+>>>>4 ulelong&0xFFfcFFe0 0 Windows NTbackup archive
+#!:mime application/x-ntbackup
+!:ext bkf
+# OS ID
+>>>>>10 ubyte 1 \b NetWare
+>>>>>10 ubyte 13 \b NetWare SMS
+>>>>>10 ubyte 14 \b NT
+>>>>>10 ubyte 24 \b 3
+>>>>>10 ubyte 25 \b OS/2
+>>>>>10 ubyte 26 \b 95
+>>>>>10 ubyte 27 \b Macintosh
+>>>>>10 ubyte 28 \b UNIX
+# OS Version (2)
+#>>>>>11 ubyte x OS V=%x
+# MTF_CONTINUATION Media Sequence Number > 1
+#>>>>>4 ulelong&0x00000001 !0 \b, continued
+# MTF_COMPRESSION
+>>>>>4 ulelong&0x00000004 !0 \b, compressed
+# MTF_EOS_AT_EOM End Of Medium was hit during end of set processing
+>>>>>4 ulelong&0x00000008 !0 \b, End Of Medium hit
+>>>>>4 ulelong&0x00020000 0
+# MTF_SET_MAP_EXISTS A Media Based Catalog Set Map may exist on tape
+>>>>>>4 ulelong&0x00010000 !0 \b, with catalog
+# MTF_FDD_ALLOWED However File/Directory Detail can only exist if a Set Map is also present
+>>>>>4 ulelong&0x00020000 !0 \b, with file catalog
+# Offset To First Event 238h,240h,28Ch
+#>>>>>8 uleshort x \b, event offset %4.4x
+# Displayable Size (20e0230h 20e024ch 20e0224h)
+#>>>>>8 ulequad x dis. size %16.16llx
+# Media Family ID (455288C4h 4570BD1Ah 45708F2Fh 4570BBF5h)
+#>>>>>52 ulelong x family ID %8.8x
+# TAPE Attributes (3)
+#>>>>>56 ulelong x TAPE %8.8x
+# Media Sequence Number
+>>>>>60 uleshort >1 \b, sequence %u
+# Password Encryption Algorithm (3)
+>>>>>62 uleshort >0 \b, %#x encrypted
+# Soft Filemark Block Size * 512 (2)
+#>>>>>64 uleshort =2 \b, soft size %u*512
+>>>>>64 uleshort !2 \b, soft size %u*512
+# Media Based Catalog Type (1,2)
+#>>>>>66 uleshort x \b, catalog type %4.4x
+# size of Media Name (66,68,6Eh)
+>>>>>68 uleshort >0
+# offset of Media Name (5Eh)
+>>>>>>70 uleshort >0
+# 0~, 1~ANSI, 2~UNICODE
+>>>>>>>48 ubyte 1
+# size terminated ansi coded string normally followed by "MTF Media Label"
+>>>>>>>>(70.s) string >\0 \b, name: %s
+>>>>>>>48 ubyte 2
+# Not null, but size terminated unicoded string
+>>>>>>>>(70.s) lestring16 x \b, name: %s
+# size of Media Label (104h)
+>>>>>72 uleshort >0
+# offset of Media Label (C4h,C6h,CCh)
+>>>>>74 uleshort >0
+>>>>>>48 ubyte 1
+#Tag|Version|Vendor|Vendor ID|Creation Time Stamp|Cartridge Label|Side|Media ID|Media Domain ID|Vendor Specific fields
+>>>>>>>(74.s) string >\0 \b, label: %s
+>>>>>>48 ubyte 2
+>>>>>>>(74.s) lestring16 x \b, label: %s
+# size of password name (0,1Ch)
+#>>>>>76 uleshort >0 \b, password size %4.4x
+# Software Vendor ID (CBEh)
+>>>>>86 uleshort x \b, software (%#x)
+# size of Software Name (6Eh)
+>>>>>80 uleshort >0
+# offset of Software Name (1C8h,1CAh,1D0h)
+>>>>>>82 uleshort >0
+# 1~ANSI, 2~UNICODE
+>>>>>>>48 ubyte 1
+>>>>>>>>(82.s) string >\0 \b: %s
+>>>>>>>48 ubyte 2
+# size terminated unicoded coded string normally followed by "SPAD"
+>>>>>>>>(82.s) lestring16 x \b: %s
+# Format Logical Block Size (512,1024)
+#>>>>>84 uleshort =1024 \b, block size %u
+>>>>>84 uleshort !1024 \b, block size %u
+# Media Date of MTF_DATE_TIME type with 5 bytes
+#>>>>>>88 ubequad x DATE %16.16llx
+# MTF Major Version (1)
+#>>>>>>93 ubyte x \b, MFT version %x
+#
+
+# URL: https://en.wikipedia.org/wiki/PaintShop_Pro
+# Reference: https://www.cryer.co.uk/file-types/p/pal.htm
+# Created by: Joerg Jenderek
+# Note: there exist other color palette formats also with .pal extension
+0 string JASC-PAL\r\n PaintShop Pro color palette
+#!:mime text/plain
+# PspPalette extension is used by newer (probably 8) PaintShopPro versions
+!:ext pal/PspPalette
+# 2nd line contains palette file version. For example "0100"
+>10 string !0100 \b, version %.4s
+# third line contains the number of colours: 16 256 ...
+>16 string x \b, %.3s colors
+
+# URL: https://en.wikipedia.org/wiki/Innosetup
+# Reference: https://github.com/jrsoftware/issrc/blob/master/Projects/Undo.pas
+# Created by: Joerg Jenderek
+# Note: created by like "InnoSetup self-extracting archive" inside ./msdos
+# TrID labeles the entry as "Inno Setup Uninstall Log"
+# TUninstallLogID
+0 string Inno\ Setup\ Uninstall\ Log\ (b) InnoSetup Log
+!:mime application/x-innosetup
+# unins000.dat, unins001.dat, ...
+!:ext dat
+# " 64-bit" variant
+>0x1c string >\0 \b%.7s
+# AppName[0x80] like "Minimal SYStem", ClamWin Free Antivirus , ...
+>0xc0 string x %s
+# AppId[0x80] is similar to AppName or
+# GUID like {4BB0DCDC-BC24-49EC-8937-72956C33A470} start with left brace
+>0x40 ubyte 0x7b
+>>0x40 string x %-.38s
+# do not know how this log version correlates to program version
+>0x140 ulelong x \b, version %#x
+# NumRecs
+#>0x144 ulelong x \b, %#4.4x records
+# EndOffset means files size
+>0x148 ulelong x \b, %u bytes
+# Flags 5 25h 35h
+#>0x14c ulelong x \b, flags %8.8x
+# Reserved: array[0..26] of Longint
+# the non Unicode HighestSupportedVersion may never become greater than or equal to 1000
+>0x140 ulelong <1000
+# hostname
+>>0x1d6 pstring x \b, %s
+# user name
+>>>&0 pstring x \b\%s
+# directory like C:\Program Files (x86)\GnuWin32
+>>>>&0 pstring x \b, "%s"
+# version 1000 or higher implies unicode
+>0x140 ulelong >999
+# hostname
+>>0x1db lestring16 x \b, %-.9s
+# utf string variant with prepending fe??ffFFff
+>>0x1db search/43 \xFF\xFF\xFF
+# user name
+>>>&0 lestring16 x \b\%-.9s
+>>>&0 search/43 \xFF\xFF\xFF
+# directory like C:\Program Files\GIMP 2
+>>>>&0 lestring16 x \b, %-.42s
+
+# URL: https://jrsoftware.org/ishelp/index.php?topic=setup_signeduninstaller
+# Reference:https://github.com/jrsoftware/issrc/blob/main/Projects/Struct.pas
+# From: Joerg Jenderek
+0 string Inno\ Setup\ Messages\ (
+# null padded til 0x40 boundary
+>0x38 quad 0 InnoSetup messages
+!:mime application/x-innosetup-msg
+# unins000.msg, unins001.msg, ...
+!:ext msg
+# version like 5.1.1 5.1.11 5.5.0 5.5.3 6.0.0
+>>0x15 string x \b, version %.5s
+# look for 6th char of version string or terminating right parentheses
+>>>0x1a ubyte !0x29 \b%c
+# NumMessages
+>>0x40 ulelong x \b, %u messages
+# TotalSize: Cardinal;
+#>>0x44 ulelong x \b, TotalSize %u
+# NotTotalSize: Cardinal;
+#>>0x48 ulelong x \b, NotTotalSize %u
+# CRCMessages: Longint;
+#>>0x4C ulelong x \b, CRC %#x
+>>0x40 ulelong x
+# (u) after version means unicoded messages
+>>>0x1c search/2 (u) (UTF-16),
+>>>>0x50 lestring16 x %s
+# ASCII coded message
+>>>0x1c default x (ASCII),
+>>>>0x50 string x %s
+
+# Windows Imaging (WIM) Image
+# Update: Joerg Jenderek at Mar 2019, 2021
+# URL: https://en.wikipedia.org/wiki/Windows_Imaging_Format
+# http://fileformats.archiveteam.org/wiki/Windows_Imaging_Format
+# Reference: https://download.microsoft.com/download/f/e/f/
+# fefdc36e-392d-4678-9e4e-771ffa2692ab/Windows%20Imaging%20File%20Format.rtf
+# Note: verified by like `7z t boot.wim` `wiminfo install.esd --header`
+0 string MSWIM\000\000\000
+>0 use wim-archive
+# https://wimlib.net/man1/wimoptimize.html
+0 string WLPWM\000\000\000
+>0 use wim-archive
+0 name wim-archive
+# _WIMHEADER_V1_PACKED ImageTag[8]
+>0 string x Windows imaging
+!:mime application/x-ms-wim
+# TO avoid in file version 5.36 error like
+# Magdir/windows, 760: Warning: Current entry does not yet have a description
+# file: could not find any valid magic files! (No error)
+# split WIM
+>16 ulelong &0x00000008 (SWM
+!:ext swm
+# usPartNumber; 1, unless the file was split into multiple parts
+>>40 uleshort x \b %u
+# usTotalParts; The total number of WIM file parts in a spanned set
+>>42 uleshort x \b of %u) image
+# non split WIM
+>16 ulelong ^0x00000008
+# https://wimlib.net/man1/wimmount.html
+# solid WIMs; version 3584; usually contain LZMS-compressed and the .esd extension
+>>12 ulelong 3584 (ESD) image
+!:ext esd
+>>12 ulelong !3584 (
+# look for archive member RunTime.xml like in Microsoft.Windows.Cosa.Desktop.Client.ppkg
+>>>156 search/68233/s RunTime.xml \bWindows provisioning package)
+!:ext ppkg
+# if is is not a Windows provisioning package, then it is a WIM
+>>>156 default x \bWIM) image
+# second disk image part created by Microsoft's RecoveryDrive.exe has name Reconstruct.WIM2
+!:ext wim/wim2
+>0 string/b WLPWM\000\000\000 \b, wimlib pipable format
+# cbSize size of the WIM header in bytes like 208
+#>8 ulelong x \b, headersize %u
+# dwVersion version of the WIM file 00010d00h~1.13 00000e00h~0.14
+>14 uleshort x v%u
+>13 ubyte x \b.%u
+# dwImageCount; The number of images contained in the WIM file
+>44 ulelong >1 \b, %u images
+# dwBootIndex
+# 1-based index of the bootable image of the WIM, or 0 if no image is bootable
+>0x78 ulelong >0 \b, bootable no. %u
+# dwFlags
+#>16 ulelong x \b, flags %#8.8x
+#define FLAG_HEADER_COMPRESSION 0x00000002
+#define FLAG_HEADER_READONLY 0x00000004
+#define FLAG_HEADER_SPANNED 0x00000008
+#define FLAG_HEADER_RESOURCE_ONLY 0x00000010
+#define FLAG_HEADER_METADATA_ONLY 0x00000020
+#define FLAG_HEADER_WRITE_IN_PROGRESS 0x00000040
+#define FLAG_HEADER_RP_FIX 0x00000080 reparse point fixup
+#define FLAG_HEADER_COMPRESS_RESERVED 0x00010000
+#define FLAG_HEADER_COMPRESS_XPRESS 0x00020000
+#define FLAG_HEADER_COMPRESS_LZX 0x00040000
+#define FLAG_HEADER_COMPRESS_LZMS 0x00080000
+#define FLAG_HEADER_COMPRESS_XPRESS2 0x00100000 wimlib-1.13.0\include\wimlib\header.h
+# XPRESS, with small chunk size
+>16 ulelong &0x00100000 \b, XPRESS2
+>16 ulelong &0x00080000 \b, LZMS
+>16 ulelong &0x00040000 \b, LZX
+>16 ulelong &0x00020000 \b, XPRESS
+>16 ulelong &0x00000002 compressed
+>16 ulelong &0x00000004 \b, read only
+>16 ulelong &0x00000010 \b, resource only
+>16 ulelong &0x00000020 \b, metadata only
+>16 ulelong &0x00000080 \b, reparse point fixup
+#>16 ulelong &0x00010000 \b, RESERVED
+# dwCompressionSize; Uncompressed chunk size for resources or 0 if uncompressed
+#>20 ulelong >0 \b, chunk size %u bytes
+# gWIMGuid
+#>24 ubequad x \b, GUID %#16.16llx
+#>>32 ubequad x \b%16.16llx
+# rhOffsetTable; the location of the resource lookup table
+# wim_reshdr_disk[24]= u8 size_in_wim[7] + u8 flags + le64 offset_in_wim + le64 uncompressed_size
+#>48 ubequad x \b, rhOffsetTable %#16.16llx
+# rhXmlData; the location of the XML data
+#>0x50 ulelong x \b, at %#8.8x
+# NOT WORKING \xff\xfe<\0W\0I\0M\0
+#>(0x50.l) ubequad x \b, xml=%16.16llx
+# rhBootMetadata; the location of the metadata resource
+#>0x60 ubequad x \b, rhBootMetadata %#16.16llx
+# rhIntegrity; the location of integrity table used to verify files
+#>0x7c ubequad x \b, rhIntegrity %#16.16llx
+# Unused[60]
+#>148 ubequad !0 \b,unused %#16.16llx
+#
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Windows_Easy_Transfer
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/m/mig.trid.xml
+# Note: called "Windows Easy Transfer migration data" by TrID,
+# "Migration Store" or "EasyTransfer file" by Microsoft
+0 string 1giM Windows Easy Transfer migration data
+#!:mime application/octet-stream
+!:mime application/x-ms-mig
+!:ext mig
+>0x18 string =MRTS without password
+# data offset with 1 space at end
+>>0x1c ulelong+0x38 x \b, at %#x
+# look for zlib compressed data by ./compress
+>>(0x1c.l+0x38) ubyte x
+>>>&-1 indirect x
+# in password protected examples MRTS comes some bytes further
+>0x18 string !MRTS with password
+# look for first MRTS tag
+>0x18 search/29/b MRTS
+# probably first file name length like 178, ...
+#>>&0 ulelong x \b, 1st length %u
+# URL like File\C:\Users\nutzer\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\desktop.ini
+>>&20 lestring16 x \b, 1st %-s
+
+# Microsoft SYLK
+# https://en.wikipedia.org/wiki/SYmbolic_LinK_(SYLK)
+# https://outflank.nl/upload/sylksum.txt
+0 string ID;P Microsoft SYLK program
+>4 string >0 \b, created by %s
+!:ext slk/sylk
+
+# Summary: Windows Performance Monitor Alert
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/Performance_Monitor
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p/pma.trid.xml
+# Note: called "Windows Performance Monitor Alert" by TrID
+0 ubelong =0xDC058340
+>4 ubyte =0 Windows Performance Monitor Alert
+#!:mime application/octet-stream
+# https://www.thoughtco.com/mime-types-by-content-type-3469108
+# https://filext.com/file-extension/PAM
+!:mime application/x-perfmon
+#!:mime application/x-ms-pma
+!:ext pma
+# metric type like: "BrowserMetrics" "CrashpadMetrics" "SetupMetrics"
+>>80 string x \b, "%s"
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/InstallShield
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/i/ins.trid.xml
+# Note: contain also keywords like: BATCH_INSTALL ISVERSION LOGHANDLE SRCDIR SRCDISK WINDIR WINSYSDISK
+0 ubelong 0xB8C90C00 InstallShield Script
+#!:mime application/octet-stream
+!:mime application/x-installshield-ins
+# like test.ins Setup.ins
+!:ext ins
+# UNKNOWN like: 160034121de07e00 1600341260befe00 16003412e0783700
+# 5000010021083f00 50000100b0335600 50000100cbfdf800 50000100dfbc4700
+#>4 ubequad x \b, at 4 %#16.16llx
+# copyright text like: "Stirling Technologies, Inc. (c) 1990-1994"
+# "InstallSHIELD Software Corporation (c) 1990-1997"
+>13 pstring/h x "%s"
+# look for specific ASCII variable names
+>1 search/0x121/s SRCDIR \b, variable names:
+# 1st like: SRCDIR
+>>&-4 leshort x #%u
+>>&-2 pstring/h x %s
+# 2nd like: SRCDISK
+>>>&0 leshort x #%u
+>>>&2 pstring/h x %s
+# 3rd like: TARGETDISK
+>>>>&0 leshort x #%u
+>>>>&2 pstring/h x %s
+# 4th like: TARGETDIR
+#>>>>>&0 leshort x #%u
+#>>>>>&2 pstring/h x %s
+# 5th like: WINDIR
+#>>>>>>&0 leshort x #%u
+#>>>>>>&2 pstring/h x %s
+# 6th like: WINDISK
+#>>>>>>>&0 leshort x #%u
+#>>>>>>>&2 pstring/h x %s
+# 7th like: WINSYSDIR
+#>>>>>>>>&0 leshort x #%u
+#>>>>>>>>&2 pstring/h x %s
+# ... LOGHANDLE
+>0 ubelong x ...
+#
+
+# Summary: Microsoft Remote Desktop Protocol connection
+# From: Joerg Jenderek
+# URL: https://learn.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/rdp-files
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/r/rdp.trid.xml
+# Note: called "Remote Desktop Connection Settings" by TrID
+0 string screen\040mode\040id:i: Remote Desktop Protocol connection
+#!:mime text/plain
+!:mime text/x-ms-rdp
+!:ext rdp
+# Screen mode: 1~session appear in a window 2~session appear full screen
+>17 string 1 \b, window mode
+>17 string 2 \b, full screen mode
+
+0 guid 7B5C52E4-D88C-4DA7-AEB1-5378D02996D3 Microsoft OneNote
+!:ext one
+!:mime application/onenote
+0 guid 43FF2FA1-EFD9-4C76-9EE2-10EA5722765F Microsoft OneNote Revision Store File
+
+# Microsoft XAML Binary Format
+# From: Alexandre Iooss <erdnaxe@crans.org>
+# URL: https://github.com/WalkingCat/XbfDump/blob/8832d2ffcaa738434d803fefa2ba99d3af37ed29/xbf_data.h
+0 string XBF\0
+>12 ulelong <0xFF
+>>16 ulelong <0xFF Microsoft XAML Binary Format
+!:ext xbf
+>>>12 ulelong x %d
+>>>16 ulelong x \b.%d
+>>>4 ulelong x \b, metadata size: %d bytes
+>>>8 ulelong x \b, node size: %d bytes
+
+# Metaswitch MetaView Service Assurance Server exports
+0 string MetaView\x20Service\x20Assurance\x20Export\x20File MetaView SAS export
+>39 string Version\x20
+>>47 byte x \b, version %c
+
+# Active Directory Group Policy Registry Policy File Format
+# From: Yuuta Liang <yuuta@yuuta.moe>
+# URL: https://learn.microsoft.com/en-us/previous-versions/windows/desktop/policy/registry-policy-file-format
+0 string PReg
+>4 lelong x Group Policy Registry Policy, Version=%d
diff --git a/contrib/libs/libmagic/magic/Magdir/wireless b/contrib/libs/libmagic/magic/Magdir/wireless
new file mode 100644
index 0000000000..badb73bb85
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/wireless
@@ -0,0 +1,7 @@
+
+#------------------------------------------------------------------------------
+# $File: wireless,v 1.2 2009/09/19 16:28:13 christos Exp $
+# wireless-regdb: file(1) magic for CRDA wireless-regdb file format
+#
+0 string RGDB CRDA wireless regulatory database file
+>4 belong 19 (Version 1)
diff --git a/contrib/libs/libmagic/magic/Magdir/wordprocessors b/contrib/libs/libmagic/magic/Magdir/wordprocessors
new file mode 100644
index 0000000000..3a2e1ceaa8
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/wordprocessors
@@ -0,0 +1,630 @@
+
+#------------------------------------------------------------------------------
+# $File: wordprocessors,v 1.34 2023/01/24 20:13:40 christos Exp $
+# wordprocessors: file(1) magic fo word processors.
+#
+####### PWP file format used on Smith Corona Personal Word Processors:
+2 string \040\040\040\040\040\040\040\040\040\040\040ML4D\040'92 Smith Corona PWP
+>24 byte 2 \b, single spaced
+>24 byte 3 \b, 1.5 spaced
+>24 byte 4 \b, double spaced
+>25 byte 0x42 \b, letter
+>25 byte 0x54 \b, legal
+>26 byte 0x46 \b, A4
+
+# URL: http://fileformats.archiveteam.org/wiki/Microsoft_Works_Word_Processor
+# reference: http://mark0.net/download/triddefs_xml.7z
+# /defs/w/wps-works-dos.trid.xml
+# From: Joerg Jenderek
+# Note: older non OLE 2 Compound based versions
+0 ubeshort =0x01FE
+>112 ubeshort =0x0100 Microsoft Works 1-3 (DOS) or 2 (Windows) document
+# title like THE GREAT KHAN GAME
+>>0x100 string x %s
+!:mime application/vnd-ms-works
+#!:mime application/x-msworks
+# https://www.macdisk.com/macsigen.php
+!:apple ????AWWP
+!:ext wps
+
+# Corel/WordPerfect
+# URL: https://en.wikipedia.org/wiki/WordPerfect
+# Reference: https://github.com/OneWingedShark/WordPerfect/blob/master/doc/SDK_Help/FileFormats/WPFF_DocumentStructure.htm
+# http://mark0.net/download/triddefs_xml.7z/defs/w/wp-generic.trid.xml
+0 string \xffWPC
+# WordPerfect
+>8 byte 1
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/w/wpm-macro.trid.xml
+# Note: there exist other macro variants
+>>9 byte 1 WordPerfect macro
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-wpm
+# like: ALTD.WPM ENDFOOT.WPM FOOTEND.WPM LABELS.WPM REVEALTX.WPM
+!:ext wpm
+# Note: used in WordPerfect 5.1; there exist other FIL variants
+>>9 byte 2 WordPerfect help file
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-help
+# like: WPHELP.FIL
+!:ext fil
+# pointer to document area like: 10h
+>>>4 ulelong !0x10 \b, at %#x document area
+>>9 byte 3 WordPerfect keyboard file
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-keyboard
+!:ext wpk
+# no document area, so point to end of file; so this is file size like: 23381 2978 32835 3355 3775 919
+>>>4 ulelong x \b, %u bytes
+>>9 byte 4 WordPerfect VAX keyboard definition
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-keyboard
+#!:ext foo
+# URL: http://fileformats.archiveteam.org/wiki/WordPerfect
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/w/wpd-doc-gen.trid.xml
+>>9 byte 10 WordPerfect document
+# https://www.iana.org/assignments/media-types/application/vnd.wordperfect
+!:mime application/vnd.wordperfect
+#!:apple ????WPC2
+# TODO: distinguish different suffix
+!:ext wpd/wpt/wkb/icr/tut/sty/tst/crs
+>>9 byte 11 WordPerfect dictionary
+>>9 byte 12 WordPerfect thesaurus
+>>9 byte 13 WordPerfect block
+>>9 byte 14 WordPerfect rectangular block
+>>9 byte 15 WordPerfect column block
+>>9 byte 16 WordPerfect printer data
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-prs
+# like: STANDARD.PRS WORKBOOK.PRS
+!:ext prs
+# like: "Standard Printer" "Workbook Printer"
+>>>0x64 pstring/B >A "%s"
+#>>9 byte 18 WordPerfect Prefix information file
+# printer resource .ALL
+>>9 byte 19 WordPerfect printer data
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-all
+!:ext all
+# display Resource
+>>9 byte 20 WordPerfect driver resource data
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-drs
+# like: WPSMALL.DRS
+!:ext drs
+# pointer to index area with string "smalldrs" like: 46h
+>>>4 uleshort !0x46 \b, at %#x index area
+>>9 byte 21 WordPerfect Overlay file
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-fil
+# like: WP.FIL
+!:ext fil
+# URL: http://fileformats.archiveteam.org/wiki/WordPerfect_Graphics
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/b/bitmap-wpg.trid.xml
+# Note: called "WordPerfect Graphics bitmap" by TrID and
+# "WordPerfect Graphics Metafile" by DROID via x-fmt/395 fmt/1042
+# "WPG (Word Perfect Graphics)" by ImageMagick `identify -verbose BUTTRFLY.WPG`
+>>9 byte 22 WordPerfect graphic image
+# TODO: skip DROID x-fmt-395-signature-id-132.wpg by check for existing document area
+#>>>4 ulelong >15 WordPerfect_graphic_OK
+#!:mime application/octet-stream
+# http://extension.nirsoft.net/wpg
+!:mime image/x-wordperfect-graphics
+# https://reposcope.com/mimetype/application/x-wpg
+#!:mime application/x-wpg
+# like: BUTTRFLY.WPG STAR-5.WPG input.wpg WORDPFCT.WPG
+!:ext wpg
+# pointer to document area like: 10h 1Ah
+>>>4 ulelong !0x1A \b, at %#x document area
+>>9 byte 23 WordPerfect hyphenation code
+>>9 byte 24 WordPerfect hyphenation data
+>>9 byte 25 WordPerfect macro resource data
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-mrs
+# like: WP.MRS
+!:ext mrs
+>>9 byte 27 WordPerfect hyphenation lex
+>>9 byte 29 WordPerfect wordlist
+>>9 byte 30 WordPerfect equation resource data
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-qrs
+# like: WQ.QRS wpDE.qrs wpen.qrs
+!:ext qrs
+# jump to document area with some marker and equation
+>>>(4.l) ubyte x
+# equation like: "Fraction: x OVER y"
+>>>>&1 string >A (...%-.19s...)
+# pointer to document area like: 17C4h
+>>>4 ulelong x \b, at %#x document area
+#>>9 byte 31 reserved
+#>>9 byte 32 WordPerfect VAX .SET
+>>9 byte 33 WordPerfect spell rules
+>>9 byte 34 WordPerfect dictionary rules
+#>>9 byte 35 reserved
+# video resource device driver
+# Note: filetype 26 for VRS and filetype 36 for WPD apparently is wrong
+>>9 byte 36 WordPerfect Video Resource
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-vrs
+# like: STANDARD.VRS
+!:ext vrs
+# like: "IBM CGA (& compatibles)"
+>>>0x20 string >A "%.23s"
+>>9 byte 39 WordPerfect spell rules (Microlytics)
+#>>9 byte 40 reserved
+>>9 byte 41 WordPerfect Install options
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-ins
+# like: WP51.INS
+!:ext ins
+# probably default directory name like: "C:\WP51\"
+>>>0x12 string >A "%.8s"
+# maybe mouse driver for WP5.1
+>>9 byte 42 WordPerfect Resource
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-irs
+# like: STANDARD.IRS
+!:ext irs
+# like: "Mouse Driver (MOUSE.COM)"
+>>>0x28 string >A "%.24s"
+>>9 byte 43 WordPerfect settings file
+# maybe Macintosh WP2.0 document
+>>9 byte 44 WordPerfect 3.5 document
+!:mime application/vnd.wordperfect
+!:apple ????WPD3
+# like: WP3.wpd
+!:ext wpd
+>>9 byte 45 WordPerfect 4.2 document
+# External spell code module (WP5.1)
+#>>9 byte 46 WordPerfect external spell
+# external spell dictionary .LEX
+#>>9 byte 47 WordPerfect external spell dictionary
+# Macintosh SOFT graphics file (SOFT (Sequential Object Format)
+#>>9 byte 48 WordPerfect SOFT graphics
+#>>9 byte 49 reserved
+#>>9 byte 50 reserved
+# WPWin 5.1 Application Resource Library added for WPWin 5.1
+#>>9 byte 51 WordPerfect application resource library
+>>9 byte 69 WordPerfect dialog file
+# From: Joerg Jenderek
+# Note: found in sub directory WritingTools inside WordPerfect 2021 program directory
+>>9 byte 70 WordPerfect Writing Tools
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-cbt
+# like: Wt13cbede.cbt Wt13cbeit.cbt Wt13cbefr.cbt WT21cbede.cbt Wt13cbeEN.CBD WT21cbeEN.CBD
+!:ext cbd/cbt
+>>9 byte 76 WordPerfect button bar
+>>9 default x
+>>>9 byte x Corel WordPerfect: Unknown filetype %d
+# Corel Shell
+>8 byte 2
+>>9 byte 1 Corel shell macro
+>>9 byte 10 Corel shell definition
+>>9 default x
+>>>9 byte x Corel Shell: Unknown filetype %d
+# Corel Notebook
+>8 byte 3
+>>9 byte 1 Corel Notebook macro
+>>9 byte 2 Corel Notebook help file
+>>9 byte 3 Corel Notebook keyboard file
+>>9 byte 10 Corel Notebook definition
+>>9 default x
+>>>9 byte x Corel Notebook: Unknown filetype %d
+# Corel Calculator
+>8 byte 4
+>>9 byte 2 Corel Calculator help file
+>>9 default x
+>>>9 byte x Corel Calculator: Unknown filetype %d
+# Corel File Manager
+>8 byte 5
+>>9 default x
+>>>9 byte x Corel File Manager: Unknown filetype %d
+# Corel Calendar
+>8 byte 6
+>>9 byte 2 Corel Calendar help file
+>>9 byte 10 Corel Calendar data file
+>>9 default x
+>>>9 byte x Corel Calendar: Unknown filetype %d
+# Corel Program Editor/Ed Editor
+>8 byte 7
+>>9 byte 1 Corel Editor macro
+>>9 byte 2 Corel Editor help file
+>>9 byte 3 Corel Editor keyboard file
+>>9 byte 25 Corel Editor macro resource file
+>>9 default x
+>>>9 byte x Corel Program Editor/Ed Editor: Unknown filetype %d
+# Corel Macro Editor
+>8 byte 8
+>>9 byte 1 Corel Macro editor macro
+>>9 byte 2 Corel Macro editor help file
+>>9 byte 3 Corel Macro editor keyboard file
+>>9 default x
+>>>9 byte x Corel Macro Editor: Unknown filetype %d
+# Corel Plan Perfect
+>8 byte 9
+>>9 default x
+>>>9 byte x Corel Plan Perfect: Unknown filetype %d
+# Corel DataPerfect
+>8 byte 10
+# CHECK: Don't these belong into product 9?
+>>9 byte 1 Corel PlanPerfect macro
+>>9 byte 2 Corel PlanPerfect help file
+>>9 byte 3 Corel PlanPerfect keyboard file
+>>9 byte 10 Corel PlanPerfect worksheet
+>>9 byte 15 Corel PlanPerfect printer definition
+>>9 byte 18 Corel PlanPerfect graphic definition
+>>9 byte 19 Corel PlanPerfect data
+>>9 byte 20 Corel PlanPerfect temporary printer
+>>9 byte 25 Corel PlanPerfect macro resource data
+>>9 default x
+>>>9 byte x Corel DataPerfect: Unknown filetype %d
+# Corel Mail
+>8 byte 11
+>>9 byte 2 Corel Mail help file
+>>9 byte 5 Corel Mail distribution list
+>>9 byte 10 Corel Mail out box
+>>9 byte 11 Corel Mail in box
+>>9 byte 20 Corel Mail users archived mailbox
+>>9 byte 21 Corel Mail archived message database
+>>9 byte 22 Corel Mail archived attachments
+>>9 default x
+>>>9 byte x Corel Mail: Unknown filetype %d
+# Corel Printer
+>8 byte 12
+>>9 byte 11 Corel Printer temporary file
+>>9 default x
+>>>9 byte x Corel Printer: Unknown filetype %d
+# Corel Scheduler
+>8 byte 13
+>>9 byte 2 Corel Scheduler help file
+>>9 byte 10 Corel Scheduler in file
+>>9 byte 11 Corel Scheduler out file
+>>9 default x
+>>>9 byte x Corel Scheduler: Unknown filetype %d
+# Corel WordPerfect Office
+>8 byte 14
+>>9 byte 10 Corel GroupWise settings file
+>>9 byte 17 Corel GroupWise directory services
+>>9 byte 43 Corel GroupWise settings file
+>>9 default x
+>>>9 byte x Corel WordPerfect Office: Unknown filetype %d
+# Corel DrawPerfect
+# URL: http://fileformats.archiveteam.org/wiki/Corel_Presentations
+# Update: Joerg Jenderek
+>8 byte 15
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/s/shw-wp-2.trid.xml
+# Note: called "WordPerfect Presentations (v2)" by TrID and
+# "Corel Presentation" with version "7-8-9" by DROID via PUID fmt/877
+>>9 byte 10 WordPerfect Presentation
+#!:mime application/octet-stream
+#!:mime application/vnd.wordperfect
+!:mime application/x-drawperfect-shw
+# like: BENEFITS.SHW chartbar.shw chartbul.shw chartgal.shw chartorg.shw fig-demo.shw figurgal.shw mastrgal.shw scuba.shw tutorial.shw
+!:ext shw
+# pointer to document area like: 10h
+>>>4 ulelong !0x10 \b, at %#x document area
+# according to TrID this is nil
+>>>12 ulelong !0 \b, at 0xC %#x
+# search for embedded WP file like in tutorial.shw
+#>>>16 search/638/sb \xffWPC WPC_MAGIC_FOUND
+# GRR: indirect call leads to recursion! WHY?
+#>>>>&0 indirect x \b; contains
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/s/shw-wp-3.trid.xml
+# Note: called "WordPerfect/Corel Presentations (v3)" by TrID and
+# "Corel Presentation" with version "3" by DROID via PUID fmt/878
+>>9 byte 15 Corel Presentation
+#!:mime application/octet-stream
+#!:mime application/vnd.wordperfect
+!:mime application/x-drawperfect-shw
+# like: FIG_ANIM.SHW presenta.shw
+!:ext shw
+# pointer to document area like: 1ah
+>>>4 ulelong !0x1a \b, at %#x document area
+# according to TrID this is nil
+>>>12 ulelong !0 \b, at 0xC %#x
+# reserved like: 3
+>>>16 ulelong !0x3 \b, at 0x10 %#x
+# file size, not including pad characters at EOF
+>>>0x14 ulelong x \b, %u bytes
+# search for embedded WP file like in foo
+#>>>24 search/638/sb \xffWPC WPC_MAGIC_FOUND
+# GRR: indirect call leads to recursion! WHY?
+#>>>>&0 indirect x \b; contains
+# embedded inside Compound Document variant handled by ./ole2compounddocs
+>>9 byte 16 Corel Presentation (embeded)
+#!:mime application/octet-stream
+#!:mime application/vnd.wordperfect
+!:mime application/x-corelpresentations
+# like: PerfectOffice_MAIN
+!:ext /
+# pointer to document area like: 1ah
+>>>4 ulelong !0x1a \b, at %#x document area
+>>>12 ulelong !0 \b, at 0xC %#x
+# reserved like: 3
+>>>16 ulelong !0x3 \b, at 0x10 %#x
+# file size, not including pad characters at EOF
+>>>0x14 ulelong x \b, %u bytes
+# search for embedded WP file
+#>>>24 search/638/sb \xffWPC WPC_MAGIC_FOUND
+# GRR: indirect call leads to recursion! WHY?
+#>>>>&0 indirect x \b; contains
+>>9 default x
+>>>9 byte x Corel DrawPerfect: Unknown filetype %d
+# Corel LetterPerfect
+>8 byte 16
+>>9 default x
+>>>9 byte x Corel LetterPerfect: Unknown filetype %d
+# Corel Terminal
+>8 byte 17
+>>9 byte 10 Corel Terminal resource data
+>>9 byte 11 Corel Terminal resource data
+>>9 byte 43 Corel Terminal resource data
+>>9 default x
+>>>9 byte x Corel Terminal: Unknown filetype %d
+# Corel loadable file
+>8 byte 18
+>>9 byte 10 Corel loadable file
+>>9 byte 11 Corel GUI loadable text
+>>9 byte 12 Corel graphics resource data
+>>9 byte 13 Corel printer settings file
+>>9 byte 14 Corel port definition file
+>>9 byte 15 Corel print queue parameters
+>>9 byte 16 Corel compressed file
+>>9 default x
+>>>9 byte x Corel loadable file: Unknown filetype %d
+>>15 byte 0 \b, optimized for Intel
+>>15 byte 1 \b, optimized for Non-Intel
+# Network service
+>8 byte 20
+>>9 byte 10 Corel Network service msg file
+>>9 byte 11 Corel Network service msg file
+>>9 byte 12 Corel Async gateway login msg
+>>9 byte 14 Corel GroupWise message file
+>>9 default x
+>>>9 byte x Corel Network service: Unknown filetype %d
+# GroupWise
+>8 byte 31
+>>9 byte 20 GroupWise admin domain database
+>>9 byte 21 GroupWise admin host database
+>>9 byte 23 GroupWise admin remote host database
+>>9 byte 24 GroupWise admin ADS deferment data file
+>>9 default x
+>>>9 byte x GroupWise: Unknown filetype %d
+# Corel Writing Tools WT*.*
+# From: Joerg Jenderek
+# URL: https://support.corel.com/hc/en-us/articles/215876258-Writing-Tools-Spell-Check-Dictionary-does-not-work-in-WordPerfect-X5
+# http://wordperfect.helpmax.net/en/editing-and-formatting-documents/using-the-writing-tools/working-with-user-word-lists/
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/u/uwl-wp.trid.xml
+>8 byte 32
+>>9 byte 10 Corel Writing Tools User Word List
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-wordlist
+# personal user word list UWL under user directory like: WTDE.UWL WTUS.UWL WT21DE.UWL WT21US.UWL WT13DE.UWL ...
+# and "template" SAV/HWL variant under program directory like: wt13en.hwl Wt13de.sav Wt13it.sav wt13ru.sav WT21us.sav Wtcz.sav ...
+!:ext uwl/hwl/sav
+# jump to document area with some marker and word list
+>>>(4.l) ubyte x
+# look for beginning of word list starting mostly with letter a as UTF-16 like: Wt13es.sav
+# but not found in russian wt13ru.sav
+>>>>&0 search/91/sb a\0
+# word list starting like: "acsesory\022accessory.\001\026acomodate\026accommodate4\001"
+>>>>>&0 lestring16 x (...%-.33s...)
+# pointer to document area like: 200h
+>>>4 ulelong !0x200 \b, at %#x document area
+# file size, not including pad characters at EOF
+>>>0x14 uleshort x \b, %u bytes
+# IntelliTAG
+>8 byte 33
+>>9 byte 10 IntelliTAG (SGML) compiled DTD
+>>9 default x
+>>>9 byte x IntelliTAG: Unknown filetype %d
+# Summary: Corel WordPerfect WritingTools advise part
+# From: Joerg Jenderek
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/a/adv-wp.trid.xml
+>8 byte 34
+>>9 byte 11 Corel WordPerfect dictionary advise
+#!:mime application/octet-stream
+!:mime application/x-wordperfect-adv
+#!:mime application/vnd.wordperfect.adv
+# like: WT21de.adv Wt13de.adv Wt13es.adv Wt13fr.adv wt13us.adv
+!:ext adv
+# advise text part often start with tag like: 580A
+#>>>(16.s) ubequad x ADVISE PART %#llx
+# part of advise text like: "This is too informal for most writing."
+>>>(16.s+16) string x (...%-.33s...)
+# everything else
+>8 default x
+>>8 byte x Unknown Corel/Wordperfect product %d,
+>>>9 byte x file type %d
+>10 byte 0 \b, v5.
+# version of WP file; 2.1~WP 8.0
+# major version of WP file like: 1 2
+>10 byte !0 \b, v%d.
+# minor version of WP file like: 0 1
+>11 byte x \b%d
+
+# Hancom HWP (Hangul Word Processor)
+# Hangul Word Processor 3.0 through 97 used HWP 3.0 format.
+# URL: https://www.hancom.com/etc/hwpDownload.do
+0 string HWP\ Document\ File Hancom HWP (Hangul Word Processor) file, version 3.0
+!:ext hwp
+
+# CosmicBook, from Benoit Rouits
+0 string CSBK Ted Neslson's CosmicBook hypertext file
+
+2 string EYWR AmigaWriter file
+
+# chi: file(1) magic for ChiWriter files
+0 string \\1cw\ ChiWriter file
+>5 string >\0 version %s
+0 string \\1cw ChiWriter file
+
+# Quark Express from https://www.garykessler.net/library/file_sigs.html
+2 string IIXPR3 Intel Quark Express Document (English)
+2 string IIXPRa Intel Quark Express Document (Korean)
+2 string MMXPR3 Motorola Quark Express Document (English)
+!:mime application/x-quark-xpress-3
+2 string MMXPRa Motorola Quark Express Document (Korean)
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/PageMaker
+# https://en.wikipedia.org/wiki/Adobe_PageMaker
+# Reference: http://mark0.net/download/triddefs_xml.7z/defs/p
+# pm4-pagemaker.trid.xml
+# pm5-pagemaker.trid.xml
+# Note: since version 6 in 1995 called Adobe PageMaker and
+# embedded in Compound Document handled by ./ole2compounddocs
+# mainly tested little endian variant
+4 ubelong =0x0000FF99
+>0 use PageMaker
+# big endian variant
+4 ubelong =0x000099FF
+>0 use \^PageMaker
+# display information of Aldus/Adobe PageMaker document/publication
+0 name PageMaker
+>110 uleshort <0x0600 Aldus
+>110 uleshort >0x05FF Adobe
+>110 uleshort x PageMaker
+# "MP" marker for newer version 4 and above according to TrID
+#>108 string x \b, MARKER "%.2s"
+# http://www.nationalarchives.gov.uk/pronom/fmt/876
+!:mime application/vnd.pagemaker
+#!:mime application/x-pagemaker
+# different file name extensions are used depending on version
+# older version like 3
+>110 uleshort/256 =0 document
+# https://www.macdisk.com/macsigen.php
+!:apple ALB3ALD3
+# PT3 for template and no example for PageMaker document/publication with PM3 extension
+!:ext pm3/pt3
+>110 uleshort/256 =4 document
+!:apple ALD4ALB4
+# no example for PT4 template
+!:ext pm4/pt4
+>110 uleshort/256 =5 document
+!:apple ALD5ALB5
+# no example for PT5 template
+!:ext pm5/pt5
+>110 uleshort =0x0600 document
+!:apple ALD6ALB6
+# PT6 for template
+!:ext pm6/pt6
+# HOWTO to distinguish version 7 from 6.5 ?
+>110 uleshort =0x0632 document
+!:apple AD65AB65
+# no example for T65 template
+!:ext p65/t65/pmd/pmt
+# version 7 with PMT extension for template
+#!:ext pmd/pmt
+#!:apple ????PUBF
+# endian marker FF 99 for little endian
+>6 ubyte =0xFF \b, little-endian
+>6 ubyte =0x99 \b, big-endian
+# newer numeric version like: 4 5 6 6.50
+#>110 uleshort x \b, VERSION=%#x
+>110 uleshort >0x03FF
+>>110 uleshort/256 x \b, version %u
+>>110 uleshort%256 >0 \b.%u
+# older version like 3
+>110 uleshort <0x0400 \b, maybe version 3
+
+# adobe indesign (document, whatever...) from querkan
+0 belong 0x0606edf5 Adobe InDesign
+>16 string DOCUMENT Document
+
+#------------------------------------------------------------------------------
+# ichitaro456: file(1) magic for Just System Word Processor Ichitaro
+#
+# Contributor kenzo-:
+# Reversed-engineered JS Ichitaro magic numbers
+#
+
+0 string DOC
+>43 byte 0x14 Just System Word Processor Ichitaro v4
+!:mime application/x-ichitaro4
+>144 string JDASH application/x-ichitaro4
+
+0 string DOC
+>43 byte 0x15 Just System Word Processor Ichitaro v5
+!:mime application/x-ichitaro5
+
+0 string DOC
+>43 byte 0x16 Just System Word Processor Ichitaro v6
+!:mime application/x-ichitaro6
+
+# Type: Freemind mindmap documents
+# From: Jamie Thompson <debian-bugs@jamie-thompson.co.uk>
+0 string/w \<map\ version Freemind document
+!:mime application/x-freemind
+
+# Type: Freeplane mindmap documents
+# From: Felix Natter <fnatter@gmx.net>
+0 string/w \<map\ version="freeplane Freeplane document
+!:mime application/x-freeplane
+
+# Type: Scribus
+# From: Werner Fink <werner@suse.de>
+0 string \<SCRIBUSUTF8\ Version Scribus Document
+0 string \<SCRIBUSUTF8NEW\ Version Scribus Document
+!:mime application/x-scribus
+
+# help files .hlp compiled from html and used by gfxboot added by Joerg Jenderek
+# markups page=0x04,label=0x12, followed by strings like "opt" or "main" and title=0x14
+0 ulelong&0x8080FFFF 0x00001204 gfxboot compiled html help file
+
+# From: Joerg Jenderek
+# URL: https://en.wikipedia.org/wiki/StarOffice
+# Reference: http://mark0.net/download/triddefs_xml.7z
+# /defs/t/thm-staroffice.trid.xml
+# Note: used in Star-, Open- and Libre-Office
+# named as soffice.StarConfigFile.6 or OpenOffice.org configuration by others
+0 ubeshort 0x0400
+# non nil gap
+#>(2.s+8) ubequad x \b, gap %#16.16llx
+# test for null value in gap after theme name maybe unreliable
+#>(2.s+9) ubyte 0 \b, 0-byte
+# look for keyword GALRESRV near the end
+# "C:\Program Files (x86)\StarOffice6.0\share\gallery\sg27.thm" Navigation, 238 objects
+#>0 search/8415 GALRESRV \b, GALRESRV found
+# "neues thema6.thm" MorePictures, 315 objects
+#>0 search/19299 GALRESRV \b, GALRESRV FOUND
+#>2 uleshort x \b, name length %u
+# skip file2147.chk by check for positive name length like for sg16.thm "3D"
+>2 uleshort >0
+# skip dBase printer form T6.PRF with misidentified gallery
+# name :\DBASE\IV\T6.txts by check for 1st object name or RESRV keyword
+# https://www.clicketyclick.dk/databases/xbase/xbase/dbase_ex.zip
+# template/t6/with_data/T6.PRF
+# by first char of object name or RESRV part of keyword GALRESRV
+>>(2.s+13) ubyte >0x1F StarOffice Gallery theme
+!:mime application/x-stargallery-thm
+# thm is also used for JPEG thumbnail images
+!:ext thm
+# gallery name often 1 word like: 3D sounds Diagrams Flussdiagramme Fotos
+# or like private://gallery/hidden/imgppt "Cisco - WAN - LAN"
+>>>2 pstring/h x %s
+# number of objects
+>>>(2.s+4) ulelong x \b, %u object
+# plural s
+>>>(2.s+4) ulelong !1 \bs
+# if available then display first object name
+>>>(2.s+4) ulelong >0
+# partial file name, URL or internal name like "dd2*" of 1st object or RESRV
+>>>>(2.s+11) pstring/h x \b, 1st %s
+
+# From: Joerg Jenderek
+# URL: http://fileformats.archiveteam.org/wiki/StarOffice_Gallery
+# Note: used in Star-, Open- and Libre-Office and found in directories like
+# %APPDATA%\Roaming\LibreOffice\4\user\gallery
+# $HOME/.config/libreoffice/4/user/gallery
+0 string SGA3 StarOffice Gallery thumbnails
+# Unknown like 0x04000?0001000142
+#>4 ubequad x \b, UNKNOWN %#16.16llx
+#!:mime application/x-sdg
+!:mime application/x-stargallery-sdg
+!:ext sdg
+# display image magic for debugging purpose like 'BM'
+# looking like PC bitmap, Windows 3.x format with unknown compression
+#>11 string x \b, image magic '%-.2s'
+# inspect 1st GALLERY thumbnail magic by ./images with 1 space at end
+#>11 indirect x \b; contains
+
diff --git a/contrib/libs/libmagic/magic/Magdir/wsdl b/contrib/libs/libmagic/magic/Magdir/wsdl
new file mode 100644
index 0000000000..1c9e60aaa2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/wsdl
@@ -0,0 +1,23 @@
+
+#------------------------------------------------------------------------------
+# $File: wsdl,v 1.6 2021/04/26 15:56:00 christos Exp $
+# wsdl: PHP WSDL Cache, https://www.php.net/manual/en/book.soap.php
+# Cache format extracted from source:
+# https://svn.php.net/viewvc/php/php-src/trunk/ext/soap/php_sdl.c?revision=HEAD&view=markup
+# Requires file >= 5.05
+# By Elan Ruusamae <glen@delfi.ee>, Patryk Zawadzki <patrys@pld-linux.org>, 2010-2011
+0 string wsdl PHP WSDL cache,
+>4 byte x version %#02x
+>6 ledate x \b, created %s
+
+# uri
+>10 lelong <0x7fffffff
+>>10 pstring/l x \b, uri: "%s"
+
+# source
+>>>&0 lelong <0x7fffffff
+>>>>&-4 pstring/l x \b, source: "%s"
+
+# target_ns
+>>>>>&0 lelong <0x7fffffff
+>>>>>>&-4 pstring/l x \b, target_ns: "%s"
diff --git a/contrib/libs/libmagic/magic/Magdir/x68000 b/contrib/libs/libmagic/magic/Magdir/x68000
new file mode 100644
index 0000000000..927b96dea2
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/x68000
@@ -0,0 +1,25 @@
+#------------------------------------------------------------------------------
+# x68000: file(1) magic for the Sharp Home Computer
+# v1.0
+# Fabio R. Schmidlin <sd-snatcher@users.sourceforge.net>
+
+# Yanagisawa PIC picture
+0 string PIC
+>3 search/0x200 \x1A
+>>&0 search/0x200 \x0
+>>>&0 ubyte 0 Yanagisawa PIC image file,
+>>>>&0 ubyte&15 0 model: X68000,
+>>>>&0 ubyte&15 1 model: PC-88VA,
+>>>>&0 ubyte&15 2 model: FM-TOWNS,
+>>>>&0 ubyte&15 3 model: MAC,
+>>>>&0 ubyte&15 15 model: Generic,
+>>>>&3 ubeshort x %dx
+>>>>&5 ubeshort x \b%d,
+>>>>&1 ubeshort 4 colors: 16
+>>>>&1 ubeshort 8 colors: 256
+>>>>&1 ubeshort 12 colors: 4096
+>>>>&1 ubeshort 15 colors: 32768
+>>>>&1 ubeshort 16 colors: 65536
+>>>>&1 ubeshort >16 colors: %d-bit
+
+
diff --git a/contrib/libs/libmagic/magic/Magdir/xdelta b/contrib/libs/libmagic/magic/Magdir/xdelta
new file mode 100644
index 0000000000..fde1d26e13
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/xdelta
@@ -0,0 +1,13 @@
+
+#------------------------------------------------------------------------------
+# $File: xdelta,v 1.5 2011/08/08 09:01:05 christos Exp $
+# file(1) magic(5) data for xdelta Josh MacDonald <jmacd@CS.Berkeley.EDU>
+#
+0 string %XDELTA% XDelta binary patch file 0.14
+0 string %XDZ000% XDelta binary patch file 0.18
+0 string %XDZ001% XDelta binary patch file 0.20
+0 string %XDZ002% XDelta binary patch file 1.0
+0 string %XDZ003% XDelta binary patch file 1.0.4
+0 string %XDZ004% XDelta binary patch file 1.1
+
+0 string \xD6\xC3\xC4\x00 VCDIFF binary diff
diff --git a/contrib/libs/libmagic/magic/Magdir/xenix b/contrib/libs/libmagic/magic/Magdir/xenix
new file mode 100644
index 0000000000..fc8027b746
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/xenix
@@ -0,0 +1,106 @@
+
+#------------------------------------------------------------------------------
+# $File: xenix,v 1.15 2022/10/19 20:15:16 christos Exp $
+# xenix: file(1) magic for Microsoft Xenix
+#
+# "Middle model" stuff, and "Xenix 8086 relocatable or 80286 small
+# model" lifted from "magic.xenix", with comment "derived empirically;
+# treat as folklore until proven"
+#
+# "small model", "large model", "huge model" stuff lifted from XXX
+#
+# XXX - "x.out" collides with PDP-11 archives
+#
+0 string core core file (Xenix)
+# URL: http://www.polarhome.com/service/man/?qf=86rel&tf=2&of=Xenix
+# http://fileformats.archiveteam.org/wiki/OMF
+# Reference: http://www.azillionmonkeys.com/qed/Omfg.pdf
+# Update: Joerg Jenderek
+# recordtype~TranslatorHEADerRecord
+0 byte 0x80
+# GRR: line above is too general as it catches also Extensible storage engine DataBase,
+# all lif files like forth.lif hpcc88.lif lex90b.lif ( See ./lif)
+# and all compressed DEGAS low-res bitmaps like: MUNCHIE.PC1 PIDER1.PC1
+# skip examples like GENA.SND Switch.Snd by looking for record length maximal 1024-3
+>1 uleshort <1022
+# skip examples like GAME.PICTURE Strange.Pic by looking for positive record length
+>>1 uleshort >0
+# skip examples like Xtable.Data FRACTAL.GEN SHR.VIEW by looking for positive string length
+>>>3 ubyte >0
+# skip examples like OMBRE.6 with "UUUUUU" name by looking for valid high second record type
+>>>>(1.s+3) ubyte >0x6D
+# skip few Atari DEGAS bitmap TPDEMO.PC2 RECIPE.PC2 with invalid "high" second record type FEh FFh
+>>>>>(1.s+3) ubyte <0xF2 8086 relocatable (Microsoft)
+#!:mime application/octet-stream
+!:mime application/x-object
+!:ext obj/o/a
+# T-module name often source name like "hello.c" or "jmppm32.asm" in JMPPM32.OBJ or
+# "kbhit" in KBHITS.OBJ or "CAUSEWAY_KERNAL" in CWAPI.OBJ
+>>>>>>3 pstring x \b, "%s"
+# data length probably lower 256 according to TrID obj_omf.trid.xml
+>>>>>>1 uleshort x \b, 1st record data length %u
+# checksum
+#>>>>>>(3.b+4) ubyte x \b, checksum %#2.2x
+# second recordtype: 96h~LNAMES 88h~COMENT 8CH~EXTDEF
+# highest F1h~Library End Record
+>>>>>>(1.s+3) ubyte x \b, 2nd record type %#x
+>>>>>>(1.s+4) uleshort x \b, 2nd record data length %u
+0 leshort 0xff65 x.out
+>2 string __.SYMDEF randomized
+>0 byte x archive
+0 leshort 0x206 Microsoft a.out
+>8 leshort 1 Middle model
+>0x1e leshort &0x10 overlay
+>0x1e leshort &0x2 separate
+>0x1e leshort &0x4 pure
+>0x1e leshort &0x800 segmented
+>0x1e leshort &0x400 standalone
+>0x1e leshort &0x8 fixed-stack
+>0x1c byte &0x80 byte-swapped
+>0x1c byte &0x40 word-swapped
+>0x10 lelong >0 not-stripped
+>0x1e leshort ^0xc000 pre-SysV
+>0x1e leshort &0x4000 V2.3
+>0x1e leshort &0x8000 V3.0
+>0x1c byte &0x4 86
+>0x1c byte &0xb 186
+>0x1c byte &0x9 286
+>0x1c byte &0xa 386
+>0x1f byte <0x040 small model
+>0x1f byte =0x048 large model
+>0x1f byte =0x049 huge model
+>0x1e leshort &0x1 executable
+>0x1e leshort ^0x1 object file
+>0x1e leshort &0x40 Large Text
+>0x1e leshort &0x20 Large Data
+>0x1e leshort &0x120 Huge Objects Enabled
+>0x10 lelong >0 not stripped
+
+0 leshort 0x140 old Microsoft 8086 x.out
+>0x3 byte &0x4 separate
+>0x3 byte &0x2 pure
+>0 byte &0x1 executable
+>0 byte ^0x1 relocatable
+>0x14 lelong >0 not stripped
+
+0 lelong 0x206 b.out
+>0x1e leshort &0x10 overlay
+>0x1e leshort &0x2 separate
+>0x1e leshort &0x4 pure
+>0x1e leshort &0x800 segmented
+>0x1e leshort &0x400 standalone
+>0x1e leshort &0x1 executable
+>0x1e leshort ^0x1 object file
+>0x1e leshort &0x4000 V2.3
+>0x1e leshort &0x8000 V3.0
+>0x1c byte &0x4 86
+>0x1c byte &0xb 186
+>0x1c byte &0x9 286
+>0x1c byte &0x29 286
+>0x1c byte &0xa 386
+>0x1e leshort &0x4 Large Text
+>0x1e leshort &0x2 Large Data
+>0x1e leshort &0x102 Huge Objects Enabled
+
+0 leshort 0x580 XENIX 8086 relocatable or 80286 small model
+# GRR: line above is too general as it catches also all 8086 relocatable (Microsoft) with 1st record data length 5 C0M.OBJ C0T.OBJ C0S.OBJ
diff --git a/contrib/libs/libmagic/magic/Magdir/xilinx b/contrib/libs/libmagic/magic/Magdir/xilinx
new file mode 100644
index 0000000000..fd1467813c
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/xilinx
@@ -0,0 +1,58 @@
+
+#------------------------------------------------------------------------------
+# $File: xilinx,v 1.10 2022/12/18 14:59:32 christos Exp $
+# This is Aaron's attempt at a MAGIC file for Xilinx .bit files.
+# Xilinx-Magic@RevRagnarok.com
+# Got the info from FPGA-FAQ 0026
+#
+# Rewritten to use pstring/H instead of hardcoded lengths by O. Freyermuth,
+# fixes at least reading of bitfiles from Spartan 2, 3, 6.
+# http://www.fpga-faq.com/FAQ_Pages/0026_Tell_me_about_bit_files.htm
+#
+# First there is the sync header and its length
+0 beshort 0x0009
+>2 belong =0x0ff00ff0
+>>&0 belong =0x0ff00ff0
+>>>&0 byte =0x00
+>>>&1 beshort =0x0001
+>>>&3 string a Xilinx BIT data
+# Next is a Pascal-style string with the NCD name. We want to capture that.
+>>>>&0 pstring/H x - from %s
+# And then 'b'
+>>>>>&1 string b
+# Then the model / part number:
+>>>>>>&0 pstring/H x - for %s
+# Then 'c'
+>>>>>>>&1 string c
+# Then the build-date
+>>>>>>>>&0 pstring/H x - built %s
+# Then 'd'
+>>>>>>>>>&1 string d
+# Then the build-time
+>>>>>>>>>>&0 pstring/H x \b(%s)
+# Then 'e'
+>>>>>>>>>>>&1 string e
+# And length of data
+>>>>>>>>>>>>&0 belong x - data length %#x
+
+# Raw bitstream files
+0 long 0xffffffff
+>&0 belong 0xaa995566 Xilinx RAW bitstream (.BIN)
+
+# AXLF (xclbin) files used by AMD/Xilinx accelerators.
+# The file format is defined by XRT source tree:
+# https://github.com/Xilinx/XRT/blob/master/src/runtime_src/core/include/xclbin.h
+# Display file size, creation date, accelerator shell name, xclbin uuid and
+# number of sections.
+
+0 string xclbin2 AMD/Xilinx accelerator AXLF (xclbin) file
+>0x130 lequad x \b, %lld bytes
+>0x138 leqdate x \b, created %s
+>0x160 string >0 \b, shell "%.64s"
+>0x1a0 ubelong x \b, uuid %08x
+>0x1a4 ubeshort x \b-%04x
+>0x1a6 ubeshort x \b-%04x
+>0x1a8 ubeshort x \b-%04x
+>0x1aa ubelong x \b-%08x
+>0x1ae ubeshort x \b%04x
+>0x1c0 lelong x \b, %d sections \ No newline at end of file
diff --git a/contrib/libs/libmagic/magic/Magdir/xo65 b/contrib/libs/libmagic/magic/Magdir/xo65
new file mode 100644
index 0000000000..f7b555f59f
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/xo65
@@ -0,0 +1,37 @@
+
+#------------------------------------------------------------------------------
+# $File: xo65,v 1.5 2022/07/17 15:36:20 christos Exp $
+# https://cc65.github.io/doc/sim65.html
+# xo65 object files
+# From: "Ullrich von Bassewitz" <uz@cc65.org>
+#
+0 string \x55\x7A\x6E\x61 xo65 object,
+>4 leshort x version %d,
+>6 leshort&0x0001 =0x0001 with debug info
+>6 leshort&0x0001 =0x0000 no debug info
+
+# xo65 library files
+0 string \x6E\x61\x55\x7A xo65 library,
+>4 leshort x version %d
+
+# o65 object files
+0 string \x01\x00\x6F\x36\x35 o65
+>6 leshort&0x1000 =0x0000 executable,
+>6 leshort&0x1000 =0x1000 object,
+>5 byte x version %d,
+>6 leshort&0x8000 =0x8000 65816,
+>6 leshort&0x8000 =0x0000 6502,
+>6 leshort&0x2000 =0x2000 32 bit,
+>6 leshort&0x2000 =0x0000 16 bit,
+>6 leshort&0x4000 =0x4000 page reloc,
+>6 leshort&0x4000 =0x0000 byte reloc,
+>6 leshort&0x0003 =0x0000 alignment 1
+>6 leshort&0x0003 =0x0001 alignment 2
+>6 leshort&0x0003 =0x0002 alignment 4
+>6 leshort&0x0003 =0x0003 alignment 256
+
+# sim65 executable files
+0 string \x73\x69\x6d\x36\x35 sim65 executable,
+>5 byte x version %d,
+>6 leshort&0x0000 =0x0000 6502
+>6 leshort&0x0001 =0x0001 65C02
diff --git a/contrib/libs/libmagic/magic/Magdir/xwindows b/contrib/libs/libmagic/magic/Magdir/xwindows
new file mode 100644
index 0000000000..d8c08c8702
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/xwindows
@@ -0,0 +1,43 @@
+
+#------------------------------------------------------------------------------
+# $File: xwindows,v 1.13 2022/03/24 15:48:58 christos Exp $
+# xwindows: file(1) magic for various X/Window system file formats.
+
+# Compiled X Keymap
+# XKM (compiled X keymap) files (including version and byte ordering)
+1 string mkx Compiled XKB Keymap: lsb,
+>0 byte >0 version %d
+>0 byte =0 obsolete
+0 string xkm Compiled XKB Keymap: msb,
+>3 byte >0 version %d
+>3 byte =0 obsolete
+
+# xfsdump archive
+0 string xFSdump0 xfsdump archive
+>8 belong x (version %d)
+
+# Jaleo XFS files
+0 long 395726 Jaleo XFS file
+>4 long x - version %d
+>8 long x - [%d -
+>20 long x \b%dx
+>24 long x \b%dx
+>28 long 1008 \bYUV422]
+>28 long 1000 \bRGB24]
+
+# Xcursor data
+# X11 mouse cursor format defined in libXcursor, see
+# https://www.x.org/archive/X11R6.8.1/doc/Xcursor.3.html
+# https://cgit.freedesktop.org/xorg/lib/libXcursor/tree/include/X11/Xcursor/Xcursor.h
+0 string Xcur Xcursor data
+!:mime image/x-xcursor
+>10 leshort x version %d
+>>8 leshort x \b.%d
+
+# X bitmap https://en.wikipedia.org/wiki/X_BitMap
+0 search/2048 #define\040
+>&0 regex [a-zA-Z0-9]+_width\040 xbm image
+>>&0 regex [0-9]+ (%sx
+>>>&0 string \n#define\040
+>>>>&0 regex [a-zA-Z0-9]+_height\040
+>>>>>&0 regex [0-9]+ \b%s)
diff --git a/contrib/libs/libmagic/magic/Magdir/yara b/contrib/libs/libmagic/magic/Magdir/yara
new file mode 100644
index 0000000000..6156cc63bc
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/yara
@@ -0,0 +1,17 @@
+
+
+#------------------------------------------------------------------------------
+# $File: yara,v 1.4 2021/04/26 15:56:00 christos Exp $
+# yara: file(1) magic for https://virustotal.github.io/yara/
+#
+
+0 string YARA
+>4 lelong >2047
+>8 byte <20 YARA 3.x compiled rule set
+# version
+>>8 clear x
+>>8 byte 6 created with version 3.3.0
+>>8 byte 8 created with version 3.4.0
+>>8 byte 11 created with version 3.5.0
+>>8 default x
+>>>8 byte x development version %#02x
diff --git a/contrib/libs/libmagic/magic/Magdir/zfs b/contrib/libs/libmagic/magic/Magdir/zfs
new file mode 100644
index 0000000000..5cb0fdd180
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/zfs
@@ -0,0 +1,96 @@
+#------------------------------------------------------------------------------
+# zfs: file(1) magic for ZFS dumps
+#
+# From <rea-fbsd@codelabs.ru>
+# ZFS dump header has the following structure (as per zfs_ioctl.h
+# in FreeBSD with drr_type is set to DRR_BEGIN)
+#
+# enum {
+# DRR_BEGIN, DRR_OBJECT, DRR_FREEOBJECTS,
+# DRR_WRITE, DRR_FREE, DRR_END,
+# } drr_type;
+# uint32_t drr_pad;
+# uint64_t drr_magic;
+# uint64_t drr_version;
+# uint64_t drr_creation_time;
+# dmu_objset_type_t drr_type;
+# uint32_t drr_pad;
+# uint64_t drr_toguid;
+# uint64_t drr_fromguid;
+# char drr_toname[MAXNAMELEN];
+#
+# Backup magic is 0x00000002f5bacbac (quad word)
+# The drr_type is defined as
+# typedef enum dmu_objset_type {
+# DMU_OST_NONE,
+# DMU_OST_META,
+# DMU_OST_ZFS,
+# DMU_OST_ZVOL,
+# DMU_OST_OTHER, /* For testing only! */
+# DMU_OST_ANY, /* Be careful! */
+# DMU_OST_NUMTYPES
+# } dmu_objset_type_t;
+#
+# Almost all uint64_t fields are printed as the 32-bit ones (with high
+# 32 bits zeroed), because there is no simple way to print them as the
+# full 64-bit values.
+
+# Big-endian values
+8 string \000\000\000\002\365\272\313\254 ZFS snapshot (big-endian machine),
+>20 belong x version %u,
+>32 belong 0 type: NONE,
+>32 belong 1 type: META,
+>32 belong 2 type: ZFS,
+>32 belong 3 type: ZVOL,
+>32 belong 4 type: OTHER,
+>32 belong 5 type: ANY,
+>32 belong >5 type: UNKNOWN (%u),
+>40 byte x destination GUID: %02X
+>41 byte x %02X
+>42 byte x %02X
+>43 byte x %02X
+>44 byte x %02X
+>45 byte x %02X
+>46 byte x %02X
+>47 byte x %02X,
+>48 ulong >0
+>>52 ulong >0
+>>>48 byte x source GUID: %02X
+>>>49 byte x %02X
+>>>50 byte x %02X
+>>>51 byte x %02X
+>>>52 byte x %02X
+>>>53 byte x %02X
+>>>54 byte x %02X
+>>>55 byte x %02X,
+>56 string >\0 name: '%s'
+
+# Little-endian values
+8 string \254\313\272\365\002\000\000\000 ZFS snapshot (little-endian machine),
+>16 lelong x version %u,
+>32 lelong 0 type: NONE,
+>32 lelong 1 type: META,
+>32 lelong 2 type: ZFS,
+>32 lelong 3 type: ZVOL,
+>32 lelong 4 type: OTHER,
+>32 lelong 5 type: ANY,
+>32 lelong >5 type: UNKNOWN (%u),
+>47 byte x destination GUID: %02X
+>46 byte x %02X
+>45 byte x %02X
+>44 byte x %02X
+>43 byte x %02X
+>42 byte x %02X
+>41 byte x %02X
+>40 byte x %02X,
+>48 ulong >0
+>>52 ulong >0
+>>>55 byte x source GUID: %02X
+>>>54 byte x %02X
+>>>53 byte x %02X
+>>>52 byte x %02X
+>>>51 byte x %02X
+>>>50 byte x %02X
+>>>49 byte x %02X
+>>>48 byte x %02X,
+>56 string >\0 name: '%s'
diff --git a/contrib/libs/libmagic/magic/Magdir/zilog b/contrib/libs/libmagic/magic/Magdir/zilog
new file mode 100644
index 0000000000..1c861fb283
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/zilog
@@ -0,0 +1,12 @@
+
+#------------------------------------------------------------------------------
+# $File: zilog,v 1.7 2009/09/19 16:28:13 christos Exp $
+# zilog: file(1) magic for Zilog Z8000.
+#
+# Was it big-endian or little-endian? My Product Specification doesn't
+# say.
+#
+0 long 0xe807 object file (z8000 a.out)
+0 long 0xe808 pure object file (z8000 a.out)
+0 long 0xe809 separate object file (z8000 a.out)
+0 long 0xe805 overlay object file (z8000 a.out)
diff --git a/contrib/libs/libmagic/magic/Magdir/zip b/contrib/libs/libmagic/magic/Magdir/zip
new file mode 100644
index 0000000000..abf5284776
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/zip
@@ -0,0 +1,126 @@
+#------------------------------------------------------------------------------
+# $File: zip,v 1.8 2021/10/24 15:53:56 christos Exp $
+# zip: file(1) magic for zip files; this is not use
+# Note the version of magic in archive is currently stronger, this is
+# just an example until negative offsets are supported better
+# Note: All fields unless otherwise noted are unsigned!
+
+# Zip Central Directory record
+0 name zipcd
+>0 string PK\001\002 Zip archive data
+!:mime application/zip
+# no "made by" in local file header with PK\3\4 magic
+>>4 leshort x \b, made by
+>>4 use zipversion
+>>4 use ziphost
+# inside ./archive 1.151 called "at least" zipversion "to extract"
+>>6 leshort x \b, extract using at least
+>>6 use zipversion
+# This is DOS date like: ledate 21:00:48 19 Dec 2001 != DOS 00:00 1 Jan 2010 ~ 0000213C
+>>12 ulelong x \b, last modified
+>>14 lemsdosdate x \b, last modified %s
+>>12 lemsdostime x %s
+# uncompressed size of 1st entry; FFffFFff means real value stored in ZIP64 record
+>>24 ulelong !0xFFffFFff \b, uncompressed size %u
+# inside ./archive 1.151 called "compression method="zipcompression
+>>10 leshort x \b, method=
+>>10 use zipcompression
+
+# URL: https://en.wikipedia.org/wiki/Zip_(file_format)
+# reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT (Version: 6.3.9)
+# Zip known compressions
+0 name zipcompression
+>0 leshort 0 \bstore
+>0 leshort 1 \bShrinking
+>0 leshort 6 \bImploding
+>0 leshort 7 \bTokenizing
+>0 leshort 8 \bdeflate
+>0 leshort 9 \bdeflate64
+>0 leshort 10 \bLibrary imploding
+#>0 leshort 11 \bReserved by PKWARE
+>0 leshort 12 \bbzip2
+#>0 leshort 13 \bReserved by PKWARE
+>0 leshort 14 \blzma
+#>0 leshort 15 \bReserved by PKWARE
+>0 leshort 16 \bCMPSC (IBM z/OS)
+#>0 leshort 17 \bReserved by PKWARE
+>0 leshort 18 \bIBM TERSE
+>0 leshort 19 \bIBM LZ77 (z/Architecture)
+>0 leshort 20 \bZstd (deprecated)
+>0 leshort 93 \bZstd
+>0 leshort 94 \bMP3
+>0 leshort 95 \bxz
+>0 leshort 96 \bJpeg
+>0 leshort 97 \bWavPack
+>0 leshort 98 \bPPMd
+>0 leshort 99 \bAES Encrypted
+>0 default x
+>>0 leshort x \b[%#x]
+
+# Zip known versions
+0 name zipversion
+# The lower byte indicates the ZIP version of this file. The value/10 indicates
+# the major version number, and the value mod 10 is the minor version number.
+>0 ubyte/10 x v%u
+>0 ubyte%10 x \b.%u
+# >0 leshort 0x09 v0.9
+# >0 leshort 0x0a v1.0
+# >0 leshort 0x0b v1.1
+# >0 leshort 0x14 v2.0
+# >0 leshort 0x15 v2.1
+# >0 leshort 0x19 v2.5
+# >0 leshort 0x1b v2.7
+# >0 leshort 0x2d v4.5
+# >0 leshort 0x2e v4.6
+# >0 leshort 0x32 v5.0
+# >0 leshort 0x33 v5.1
+# >0 leshort 0x34 v5.2
+# >0 leshort 0x3d v6.1
+# >0 leshort 0x3e v6.2
+# >0 leshort 0x3f v6.3
+# >0 default x
+# >>0 leshort x v?[%#x]
+
+# display compatible host system name of ZIP archive
+0 name ziphost
+# The upper byte indicates the compatibility of the file attribute information.
+# If the file is compatible with MS-DOS (v 2.04g) then this value will be zero.
+#>1 ubyte 0 DOS
+>1 ubyte 1 Amiga
+>1 ubyte 2 OpenVMS
+>1 ubyte 3 UNIX
+>1 ubyte 4 VM/CMS
+>1 ubyte 6 OS/2
+>1 ubyte 7 Macintosh
+>1 ubyte 11 MVS
+>1 ubyte 13 Acorn Risc
+>1 ubyte 16 BeOS
+>1 ubyte 17 Tandem
+# 9 untested
+>1 ubyte 5 Atari ST
+>1 ubyte 8 Z-System
+>1 ubyte 9 CP/M
+>1 ubyte 10 Windows NTFS
+>1 ubyte 12 VSE
+>1 ubyte 14 VFAT
+>1 ubyte 15 alternate MVS
+>1 ubyte 18 OS/400
+>1 ubyte 19 OS X
+# unused
+#>1 ubyte >19 unused %#x
+
+# Zip End Of Central Directory record
+# GRR: wrong for ZIP with comment archive
+-22 string PK\005\006
+#>4 uleshort !0xFFff \b, %u disks
+#>6 uleshort !0xFFff \b, central directory disk %u
+#>8 uleshort !0xFFff \b, %u central directories on this disk
+#>10 uleshort !0xFFff \b, %u central directories
+#>12 ulelong !0xFFffFFff \b, %u central directory bytes
+# offset of central directory
+#>16 ulelong x \b, central directory offset %#x
+>(16.l) use zipcd
+# archive comment length n
+#>>20 uleshort >0 \b, comment length %u
+# archive comment
+>>20 pstring/l >0 \b, %s
diff --git a/contrib/libs/libmagic/magic/Magdir/zyxel b/contrib/libs/libmagic/magic/Magdir/zyxel
new file mode 100644
index 0000000000..d3a43e4878
--- /dev/null
+++ b/contrib/libs/libmagic/magic/Magdir/zyxel
@@ -0,0 +1,17 @@
+
+#------------------------------------------------------------------------------
+# $File: zyxel,v 1.6 2009/09/19 16:28:13 christos Exp $
+# zyxel: file(1) magic for ZyXEL modems
+#
+# From <rob@pe1chl.ampr.org>
+# These are the /etc/magic entries to decode datafiles as used for the
+# ZyXEL U-1496E DATA/FAX/VOICE modems. (This header conforms to a
+# ZyXEL-defined standard)
+
+0 string ZyXEL\002 ZyXEL voice data
+>10 byte 0 - CELP encoding
+>10 byte&0x0B 1 - ADPCM2 encoding
+>10 byte&0x0B 2 - ADPCM3 encoding
+>10 byte&0x0B 3 - ADPCM4 encoding
+>10 byte&0x0B 8 - New ADPCM3 encoding
+>10 byte&0x04 4 with resync
diff --git a/contrib/libs/libmagic/magic/ya.make b/contrib/libs/libmagic/magic/ya.make
new file mode 100644
index 0000000000..c8fe646fd4
--- /dev/null
+++ b/contrib/libs/libmagic/magic/ya.make
@@ -0,0 +1,44 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(BSD-2-Clause)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+SRCDIR(contrib/libs/libmagic/magic/Magdir)
+
+RESOURCE(
+ Magdir.mgc /magic/magic.mgc
+)
+
+RUN_PROGRAM(
+ contrib/libs/libmagic/file/0 -C -m $CURDIR/Magdir
+ CWD $BINDIR
+ OUT Magdir.mgc
+ IN acorn adi adventure aes algol68 allegro alliant amanda amigaos android animation aout apache apl apple
+ application applix apt archive aria arm asf assembler asterix att3b audio avm basis beetle ber bflt
+ bhl bioinformatics biosig blackberry blcr blender blit bm bout bsdi bsi btsnoop burp bytecode c-lang
+ c64 cad cafebabe cbor ccf cddb chord cisco citrus clarion claris clipper clojure coff commands
+ communications compress console convex coverage cracklib crypto ctags ctf cubemap cups dact database
+ dataone dbpf der diamond dif diff digital dolby dump dwarfs dyadic ebml edid editors efi elf encore
+ epoc erlang espressif esri etf fcs filesystems finger firmware flash flif fonts forth fortran frame
+ freebsd fsav fusecompress games gcc gconv gentoo geo geos gimp git glibc gnome gnu gnumeric gpt gpu
+ grace graphviz gringotts hardware hitachi-sh hp human68k ibm370 ibm6000 icc iff images inform intel
+ interleaf island ispell isz java javascript jpeg karma kde keepass kerberos kicad kml lammps lecter
+ lex lif linux lisp llvm locoscript lua luks m4 mach macintosh macos magic mail.news make map maple
+ marc21 mathcad mathematica matroska mcrypt measure mercurial metastore meteorological microfocus mime
+ mips mirage misctools mkid mlssa mmdf modem modulefile motorola mozilla msdos msooxml msvc msx mup
+ music nasa natinst ncr netbsd netscape netware news nifty nim-lang nitpicker numpy oasis ocaml octave
+ ole2compounddocs olf openfst opentimestamps oric os2 os400 os9 osf1 palm parix parrot pascal pbf pbm
+ pc88 pc98 pci_ids pcjr pdf pdp perl pgf pgp pgp-binary-keys pkgadd plan9 playdate plus5 pmem polyml
+ printer project psdbms psl pulsar puzzle pwsafe pyramid python qt revision riff ringdove rpi rpm rpmsg
+ rst rtf ruby rust sc sccs scientific securitycerts selinux sendmail sequent sereal sgi sgml sharc
+ sinclair sisu sketch smalltalk smile sniffer softquad sosi spec spectrum sql ssh ssl statistics
+ subtitle sun svf sylk symbos sysex tcl teapot terminfo tex tgif ti-8x timezone tplink troff tuxedo
+ typeset uf2 unicode unisig unknown usd uterus uuencode vacuum-cleaner varied.out varied.script vax
+ vicar virtual virtutech visx vms vmware vorbis vxl warc weak web webassembly windows wireless
+ wordprocessors wsdl x68000 xdelta xenix xilinx xo65 xwindows yara zfs zilog zip zyxel
+)
+
+END()
diff --git a/contrib/libs/libmagic/src/apprentice.c b/contrib/libs/libmagic/src/apprentice.c
new file mode 100644
index 0000000000..ca22186695
--- /dev/null
+++ b/contrib/libs/libmagic/src/apprentice.c
@@ -0,0 +1,3743 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * apprentice - make one pass through /etc/magic, learning its secrets.
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: apprentice.c,v 1.342 2023/07/17 14:38:35 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef QUICK
+#include <sys/mman.h>
+#endif
+#include <dirent.h>
+#include <limits.h>
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+#ifdef HAVE_SYS_BSWAP_H
+#error #include <sys/bswap.h>
+#endif
+
+
+#define EATAB {while (isascii(CAST(unsigned char, *l)) && \
+ isspace(CAST(unsigned char, *l))) ++l;}
+#define LOWCASE(l) (isupper(CAST(unsigned char, l)) ? \
+ tolower(CAST(unsigned char, l)) : (l))
+/*
+ * Work around a bug in headers on Digital Unix.
+ * At least confirmed for: OSF1 V4.0 878
+ */
+#if defined(__osf__) && defined(__DECC)
+#ifdef MAP_FAILED
+#undef MAP_FAILED
+#endif
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED (void *) -1
+#endif
+
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#define ALLOC_CHUNK CAST(size_t, 10)
+#define ALLOC_INCR CAST(size_t, 200)
+
+#define MAP_TYPE_USER 0
+#define MAP_TYPE_MALLOC 1
+#define MAP_TYPE_MMAP 2
+
+struct magic_entry {
+ struct magic *mp;
+ uint32_t cont_count;
+ uint32_t max_count;
+};
+
+struct magic_entry_set {
+ struct magic_entry *me;
+ uint32_t count;
+ uint32_t max;
+};
+
+struct magic_map {
+ void *p;
+ size_t len;
+ int type;
+ struct magic *magic[MAGIC_SETS];
+ uint32_t nmagic[MAGIC_SETS];
+};
+
+int file_formats[FILE_NAMES_SIZE];
+const size_t file_nformats = FILE_NAMES_SIZE;
+const char *file_names[FILE_NAMES_SIZE];
+const size_t file_nnames = FILE_NAMES_SIZE;
+
+file_private int getvalue(struct magic_set *ms, struct magic *, const char **, int);
+file_private int hextoint(int);
+file_private const char *getstr(struct magic_set *, struct magic *, const char *,
+ int);
+file_private int parse(struct magic_set *, struct magic_entry *, const char *,
+ size_t, int);
+file_private void eatsize(const char **);
+file_private int apprentice_1(struct magic_set *, const char *, int);
+file_private ssize_t apprentice_magic_strength_1(const struct magic *);
+file_private int apprentice_sort(const void *, const void *);
+file_private void apprentice_list(struct mlist *, int );
+file_private struct magic_map *apprentice_load(struct magic_set *,
+ const char *, int);
+file_private struct mlist *mlist_alloc(void);
+file_private void mlist_free_all(struct magic_set *);
+file_private void mlist_free(struct mlist *);
+file_private void byteswap(struct magic *, uint32_t);
+file_private void bs1(struct magic *);
+
+#if defined(HAVE_BYTESWAP_H)
+#define swap2(x) bswap_16(x)
+#define swap4(x) bswap_32(x)
+#define swap8(x) bswap_64(x)
+#elif defined(HAVE_SYS_BSWAP_H)
+#define swap2(x) bswap16(x)
+#define swap4(x) bswap32(x)
+#define swap8(x) bswap64(x)
+#else
+file_private uint16_t swap2(uint16_t);
+file_private uint32_t swap4(uint32_t);
+file_private uint64_t swap8(uint64_t);
+#endif
+
+file_private char *mkdbname(struct magic_set *, const char *, int);
+file_private struct magic_map *apprentice_buf(struct magic_set *, struct magic *,
+ size_t);
+file_private struct magic_map *apprentice_map(struct magic_set *, const char *);
+file_private int check_buffer(struct magic_set *, struct magic_map *, const char *);
+file_private void apprentice_unmap(struct magic_map *);
+file_private int apprentice_compile(struct magic_set *, struct magic_map *,
+ const char *);
+file_private int check_format_type(const char *, int, const char **);
+file_private int check_format(struct magic_set *, struct magic *);
+file_private int get_op(char);
+file_private int parse_mime(struct magic_set *, struct magic_entry *, const char *,
+ size_t);
+file_private int parse_strength(struct magic_set *, struct magic_entry *,
+ const char *, size_t);
+file_private int parse_apple(struct magic_set *, struct magic_entry *, const char *,
+ size_t);
+file_private int parse_ext(struct magic_set *, struct magic_entry *, const char *,
+ size_t);
+
+
+file_private size_t magicsize = sizeof(struct magic);
+
+file_private const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
+
+file_private struct {
+ const char *name;
+ size_t len;
+ int (*fun)(struct magic_set *, struct magic_entry *, const char *,
+ size_t);
+} bang[] = {
+#define DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name }
+ DECLARE_FIELD(mime),
+ DECLARE_FIELD(apple),
+ DECLARE_FIELD(ext),
+ DECLARE_FIELD(strength),
+#undef DECLARE_FIELD
+ { NULL, 0, NULL }
+};
+
+#ifdef COMPILE_ONLY
+
+int main(int, char *[]);
+
+int
+main(int argc, char *argv[])
+{
+ int ret;
+ struct magic_set *ms;
+ char *progname;
+
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ if (argc != 2) {
+ (void)fprintf(stderr, "Usage: %s file\n", progname);
+ return 1;
+ }
+
+ if ((ms = magic_open(MAGIC_CHECK)) == NULL) {
+ (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
+ return 1;
+ }
+ ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0;
+ if (ret == 1)
+ (void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms));
+ magic_close(ms);
+ return ret;
+}
+#endif /* COMPILE_ONLY */
+
+struct type_tbl_s {
+ const char name[16];
+ const size_t len;
+ const int type;
+ const int format;
+};
+
+/*
+ * XXX - the actual Single UNIX Specification says that "long" means "long",
+ * as in the C data type, but we treat it as meaning "4-byte integer".
+ * Given that the OS X version of file 5.04 did the same, I guess that passes
+ * the actual test; having "long" be dependent on how big a "long" is on
+ * the machine running "file" is silly.
+ */
+static const struct type_tbl_s type_tbl[] = {
+# define XX(s) s, (sizeof(s) - 1)
+# define XX_NULL "", 0
+ { XX("invalid"), FILE_INVALID, FILE_FMT_NONE },
+ { XX("byte"), FILE_BYTE, FILE_FMT_NUM },
+ { XX("short"), FILE_SHORT, FILE_FMT_NUM },
+ { XX("default"), FILE_DEFAULT, FILE_FMT_NONE },
+ { XX("long"), FILE_LONG, FILE_FMT_NUM },
+ { XX("string"), FILE_STRING, FILE_FMT_STR },
+ { XX("date"), FILE_DATE, FILE_FMT_STR },
+ { XX("beshort"), FILE_BESHORT, FILE_FMT_NUM },
+ { XX("belong"), FILE_BELONG, FILE_FMT_NUM },
+ { XX("bedate"), FILE_BEDATE, FILE_FMT_STR },
+ { XX("leshort"), FILE_LESHORT, FILE_FMT_NUM },
+ { XX("lelong"), FILE_LELONG, FILE_FMT_NUM },
+ { XX("ledate"), FILE_LEDATE, FILE_FMT_STR },
+ { XX("pstring"), FILE_PSTRING, FILE_FMT_STR },
+ { XX("ldate"), FILE_LDATE, FILE_FMT_STR },
+ { XX("beldate"), FILE_BELDATE, FILE_FMT_STR },
+ { XX("leldate"), FILE_LELDATE, FILE_FMT_STR },
+ { XX("regex"), FILE_REGEX, FILE_FMT_STR },
+ { XX("bestring16"), FILE_BESTRING16, FILE_FMT_STR },
+ { XX("lestring16"), FILE_LESTRING16, FILE_FMT_STR },
+ { XX("search"), FILE_SEARCH, FILE_FMT_STR },
+ { XX("medate"), FILE_MEDATE, FILE_FMT_STR },
+ { XX("meldate"), FILE_MELDATE, FILE_FMT_STR },
+ { XX("melong"), FILE_MELONG, FILE_FMT_NUM },
+ { XX("quad"), FILE_QUAD, FILE_FMT_QUAD },
+ { XX("lequad"), FILE_LEQUAD, FILE_FMT_QUAD },
+ { XX("bequad"), FILE_BEQUAD, FILE_FMT_QUAD },
+ { XX("qdate"), FILE_QDATE, FILE_FMT_STR },
+ { XX("leqdate"), FILE_LEQDATE, FILE_FMT_STR },
+ { XX("beqdate"), FILE_BEQDATE, FILE_FMT_STR },
+ { XX("qldate"), FILE_QLDATE, FILE_FMT_STR },
+ { XX("leqldate"), FILE_LEQLDATE, FILE_FMT_STR },
+ { XX("beqldate"), FILE_BEQLDATE, FILE_FMT_STR },
+ { XX("float"), FILE_FLOAT, FILE_FMT_FLOAT },
+ { XX("befloat"), FILE_BEFLOAT, FILE_FMT_FLOAT },
+ { XX("lefloat"), FILE_LEFLOAT, FILE_FMT_FLOAT },
+ { XX("double"), FILE_DOUBLE, FILE_FMT_DOUBLE },
+ { XX("bedouble"), FILE_BEDOUBLE, FILE_FMT_DOUBLE },
+ { XX("ledouble"), FILE_LEDOUBLE, FILE_FMT_DOUBLE },
+ { XX("leid3"), FILE_LEID3, FILE_FMT_NUM },
+ { XX("beid3"), FILE_BEID3, FILE_FMT_NUM },
+ { XX("indirect"), FILE_INDIRECT, FILE_FMT_NUM },
+ { XX("qwdate"), FILE_QWDATE, FILE_FMT_STR },
+ { XX("leqwdate"), FILE_LEQWDATE, FILE_FMT_STR },
+ { XX("beqwdate"), FILE_BEQWDATE, FILE_FMT_STR },
+ { XX("name"), FILE_NAME, FILE_FMT_NONE },
+ { XX("use"), FILE_USE, FILE_FMT_NONE },
+ { XX("clear"), FILE_CLEAR, FILE_FMT_NONE },
+ { XX("der"), FILE_DER, FILE_FMT_STR },
+ { XX("guid"), FILE_GUID, FILE_FMT_STR },
+ { XX("offset"), FILE_OFFSET, FILE_FMT_QUAD },
+ { XX("bevarint"), FILE_BEVARINT, FILE_FMT_STR },
+ { XX("levarint"), FILE_LEVARINT, FILE_FMT_STR },
+ { XX("msdosdate"), FILE_MSDOSDATE, FILE_FMT_STR },
+ { XX("lemsdosdate"), FILE_LEMSDOSDATE, FILE_FMT_STR },
+ { XX("bemsdosdate"), FILE_BEMSDOSDATE, FILE_FMT_STR },
+ { XX("msdostime"), FILE_MSDOSTIME, FILE_FMT_STR },
+ { XX("lemsdostime"), FILE_LEMSDOSTIME, FILE_FMT_STR },
+ { XX("bemsdostime"), FILE_BEMSDOSTIME, FILE_FMT_STR },
+ { XX("octal"), FILE_OCTAL, FILE_FMT_STR },
+ { XX_NULL, FILE_INVALID, FILE_FMT_NONE },
+};
+
+/*
+ * These are not types, and cannot be preceded by "u" to make them
+ * unsigned.
+ */
+static const struct type_tbl_s special_tbl[] = {
+ { XX("der"), FILE_DER, FILE_FMT_STR },
+ { XX("name"), FILE_NAME, FILE_FMT_STR },
+ { XX("use"), FILE_USE, FILE_FMT_STR },
+ { XX("octal"), FILE_OCTAL, FILE_FMT_STR },
+ { XX_NULL, FILE_INVALID, FILE_FMT_NONE },
+};
+# undef XX
+# undef XX_NULL
+
+file_private int
+get_type(const struct type_tbl_s *tbl, const char *l, const char **t)
+{
+ const struct type_tbl_s *p;
+
+ for (p = tbl; p->len; p++) {
+ if (strncmp(l, p->name, p->len) == 0) {
+ if (t)
+ *t = l + p->len;
+ break;
+ }
+ }
+ return p->type;
+}
+
+file_private off_t
+maxoff_t(void) {
+ if (/*CONSTCOND*/sizeof(off_t) == sizeof(int))
+ return CAST(off_t, INT_MAX);
+ if (/*CONSTCOND*/sizeof(off_t) == sizeof(long))
+ return CAST(off_t, LONG_MAX);
+ return 0x7fffffff;
+}
+
+file_private int
+get_standard_integer_type(const char *l, const char **t)
+{
+ int type;
+
+ if (isalpha(CAST(unsigned char, l[1]))) {
+ switch (l[1]) {
+ case 'C':
+ /* "dC" and "uC" */
+ type = FILE_BYTE;
+ break;
+ case 'S':
+ /* "dS" and "uS" */
+ type = FILE_SHORT;
+ break;
+ case 'I':
+ case 'L':
+ /*
+ * "dI", "dL", "uI", and "uL".
+ *
+ * XXX - the actual Single UNIX Specification says
+ * that "L" means "long", as in the C data type,
+ * but we treat it as meaning "4-byte integer".
+ * Given that the OS X version of file 5.04 did
+ * the same, I guess that passes the actual SUS
+ * validation suite; having "dL" be dependent on
+ * how big a "long" is on the machine running
+ * "file" is silly.
+ */
+ type = FILE_LONG;
+ break;
+ case 'Q':
+ /* "dQ" and "uQ" */
+ type = FILE_QUAD;
+ break;
+ default:
+ /* "d{anything else}", "u{anything else}" */
+ return FILE_INVALID;
+ }
+ l += 2;
+ } else if (isdigit(CAST(unsigned char, l[1]))) {
+ /*
+ * "d{num}" and "u{num}"; we only support {num} values
+ * of 1, 2, 4, and 8 - the Single UNIX Specification
+ * doesn't say anything about whether arbitrary
+ * values should be supported, but both the Solaris 10
+ * and OS X Mountain Lion versions of file passed the
+ * Single UNIX Specification validation suite, and
+ * neither of them support values bigger than 8 or
+ * non-power-of-2 values.
+ */
+ if (isdigit(CAST(unsigned char, l[2]))) {
+ /* Multi-digit, so > 9 */
+ return FILE_INVALID;
+ }
+ switch (l[1]) {
+ case '1':
+ type = FILE_BYTE;
+ break;
+ case '2':
+ type = FILE_SHORT;
+ break;
+ case '4':
+ type = FILE_LONG;
+ break;
+ case '8':
+ type = FILE_QUAD;
+ break;
+ default:
+ /* XXX - what about 3, 5, 6, or 7? */
+ return FILE_INVALID;
+ }
+ l += 2;
+ } else {
+ /*
+ * "d" or "u" by itself.
+ */
+ type = FILE_LONG;
+ ++l;
+ }
+ if (t)
+ *t = l;
+ return type;
+}
+
+file_private void
+init_file_tables(void)
+{
+ static int done = 0;
+ const struct type_tbl_s *p;
+
+ if (done)
+ return;
+ done++;
+
+ for (p = type_tbl; p->len; p++) {
+ assert(p->type < FILE_NAMES_SIZE);
+ file_names[p->type] = p->name;
+ file_formats[p->type] = p->format;
+ }
+ assert(p - type_tbl == FILE_NAMES_SIZE);
+}
+
+file_private int
+add_mlist(struct mlist *mlp, struct magic_map *map, size_t idx)
+{
+ struct mlist *ml;
+
+ mlp->map = NULL;
+ if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL)
+ return -1;
+
+ ml->map = idx == 0 ? map : NULL;
+ ml->magic = map->magic[idx];
+ ml->nmagic = map->nmagic[idx];
+ if (ml->nmagic) {
+ ml->magic_rxcomp = CAST(file_regex_t **,
+ calloc(ml->nmagic, sizeof(*ml->magic_rxcomp)));
+ if (ml->magic_rxcomp == NULL) {
+ free(ml);
+ return -1;
+ }
+ } else
+ ml->magic_rxcomp = NULL;
+ mlp->prev->next = ml;
+ ml->prev = mlp->prev;
+ ml->next = mlp;
+ mlp->prev = ml;
+ return 0;
+}
+
+/*
+ * Handle one file or directory.
+ */
+file_private int
+apprentice_1(struct magic_set *ms, const char *fn, int action)
+{
+ struct magic_map *map;
+#ifndef COMPILE_ONLY
+ size_t i;
+#endif
+
+ if (magicsize != FILE_MAGICSIZE) {
+ file_error(ms, 0, "magic element size %lu != %lu",
+ CAST(unsigned long, sizeof(*map->magic[0])),
+ CAST(unsigned long, FILE_MAGICSIZE));
+ return -1;
+ }
+
+ if (action == FILE_COMPILE) {
+ map = apprentice_load(ms, fn, action);
+ if (map == NULL)
+ return -1;
+ return apprentice_compile(ms, map, fn);
+ }
+
+#ifndef COMPILE_ONLY
+ map = apprentice_map(ms, fn);
+ if (map == NULL) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(NULL, "using regular magic file `%s'", fn);
+ map = apprentice_load(ms, fn, action);
+ if (map == NULL)
+ return -1;
+ }
+
+ for (i = 0; i < MAGIC_SETS; i++) {
+ if (add_mlist(ms->mlist[i], map, i) == -1) {
+ /* failed to add to any list, free explicitly */
+ if (i == 0)
+ apprentice_unmap(map);
+ else
+ mlist_free_all(ms);
+ file_oomem(ms, sizeof(*ms->mlist[0]));
+ return -1;
+ }
+ }
+
+ if (action == FILE_LIST) {
+ for (i = 0; i < MAGIC_SETS; i++) {
+ printf("Set %" SIZE_T_FORMAT "u:\nBinary patterns:\n",
+ i);
+ apprentice_list(ms->mlist[i], BINTEST);
+ printf("Text patterns:\n");
+ apprentice_list(ms->mlist[i], TEXTTEST);
+ }
+ }
+ return 0;
+#else
+ return 0;
+#endif /* COMPILE_ONLY */
+}
+
+file_protected void
+file_ms_free(struct magic_set *ms)
+{
+ size_t i;
+ if (ms == NULL)
+ return;
+ for (i = 0; i < MAGIC_SETS; i++)
+ mlist_free(ms->mlist[i]);
+ free(ms->o.pbuf);
+ free(ms->o.buf);
+ free(ms->c.li);
+#ifdef USE_C_LOCALE
+ freelocale(ms->c_lc_ctype);
+#endif
+ free(ms);
+}
+
+file_protected struct magic_set *
+file_ms_alloc(int flags)
+{
+ struct magic_set *ms;
+ size_t i, len;
+
+ if ((ms = CAST(struct magic_set *, calloc(CAST(size_t, 1u),
+ sizeof(*ms)))) == NULL)
+ return NULL;
+
+ if (magic_setflags(ms, flags) == -1) {
+ errno = EINVAL;
+ goto free;
+ }
+
+ ms->o.buf = ms->o.pbuf = NULL;
+ ms->o.blen = 0;
+ len = (ms->c.len = 10) * sizeof(*ms->c.li);
+
+ if ((ms->c.li = CAST(struct level_info *, malloc(len))) == NULL)
+ goto free;
+
+ ms->event_flags = 0;
+ ms->error = -1;
+ for (i = 0; i < MAGIC_SETS; i++)
+ ms->mlist[i] = NULL;
+ ms->file = "unknown";
+ ms->line = 0;
+ ms->indir_max = FILE_INDIR_MAX;
+ ms->name_max = FILE_NAME_MAX;
+ ms->elf_shnum_max = FILE_ELF_SHNUM_MAX;
+ ms->elf_shsize_max = FILE_ELF_SHSIZE_MAX;
+ ms->elf_phnum_max = FILE_ELF_PHNUM_MAX;
+ ms->elf_notes_max = FILE_ELF_NOTES_MAX;
+ ms->regex_max = FILE_REGEX_MAX;
+ ms->bytes_max = FILE_BYTES_MAX;
+ ms->encoding_max = FILE_ENCODING_MAX;
+#ifdef USE_C_LOCALE
+ ms->c_lc_ctype = newlocale(LC_CTYPE_MASK, "C", 0);
+ assert(ms->c_lc_ctype != NULL);
+#endif
+ return ms;
+free:
+ free(ms);
+ return NULL;
+}
+
+file_private void
+apprentice_unmap(struct magic_map *map)
+{
+ size_t i;
+ char *p;
+ if (map == NULL)
+ return;
+
+ switch (map->type) {
+ case MAP_TYPE_USER:
+ break;
+ case MAP_TYPE_MALLOC:
+ p = CAST(char *, map->p);
+ for (i = 0; i < MAGIC_SETS; i++) {
+ char *b = RCAST(char *, map->magic[i]);
+ if (p != NULL && b >= p && b <= p + map->len)
+ continue;
+ free(b);
+ }
+ free(p);
+ break;
+#ifdef QUICK
+ case MAP_TYPE_MMAP:
+ if (map->p && map->p != MAP_FAILED)
+ (void)munmap(map->p, map->len);
+ break;
+#endif
+ default:
+ fprintf(stderr, "Bad map type %d", map->type);
+ abort();
+ }
+ free(map);
+}
+
+file_private struct mlist *
+mlist_alloc(void)
+{
+ struct mlist *mlist;
+ if ((mlist = CAST(struct mlist *, calloc(1, sizeof(*mlist)))) == NULL) {
+ return NULL;
+ }
+ mlist->next = mlist->prev = mlist;
+ return mlist;
+}
+
+file_private void
+mlist_free_all(struct magic_set *ms)
+{
+ size_t i;
+
+ for (i = 0; i < MAGIC_SETS; i++) {
+ mlist_free(ms->mlist[i]);
+ ms->mlist[i] = NULL;
+ }
+}
+
+file_private void
+mlist_free_one(struct mlist *ml)
+{
+ size_t i;
+
+ if (ml->map)
+ apprentice_unmap(CAST(struct magic_map *, ml->map));
+
+ for (i = 0; i < ml->nmagic; ++i) {
+ if (ml->magic_rxcomp[i]) {
+ file_regfree(ml->magic_rxcomp[i]);
+ free(ml->magic_rxcomp[i]);
+ ml->magic_rxcomp[i] = NULL;
+ }
+ }
+ free(ml->magic_rxcomp);
+ ml->magic_rxcomp = NULL;
+ free(ml);
+}
+
+file_private void
+mlist_free(struct mlist *mlist)
+{
+ struct mlist *ml, *next;
+
+ if (mlist == NULL)
+ return;
+
+ for (ml = mlist->next; ml != mlist;) {
+ next = ml->next;
+ mlist_free_one(ml);
+ ml = next;
+ }
+ mlist_free_one(mlist);
+}
+
+#ifndef COMPILE_ONLY
+/* void **bufs: an array of compiled magic files */
+file_protected int
+buffer_apprentice(struct magic_set *ms, struct magic **bufs,
+ size_t *sizes, size_t nbufs)
+{
+ size_t i, j;
+ struct magic_map *map;
+
+ if (nbufs == 0)
+ return -1;
+
+ (void)file_reset(ms, 0);
+
+ init_file_tables();
+
+ for (i = 0; i < MAGIC_SETS; i++) {
+ mlist_free(ms->mlist[i]);
+ if ((ms->mlist[i] = mlist_alloc()) == NULL) {
+ file_oomem(ms, sizeof(*ms->mlist[0]));
+ goto fail;
+ }
+ }
+
+ for (i = 0; i < nbufs; i++) {
+ map = apprentice_buf(ms, bufs[i], sizes[i]);
+ if (map == NULL)
+ goto fail;
+
+ for (j = 0; j < MAGIC_SETS; j++) {
+ if (add_mlist(ms->mlist[j], map, j) == -1) {
+ file_oomem(ms, sizeof(*ms->mlist[0]));
+ goto fail;
+ }
+ }
+ }
+
+ return 0;
+fail:
+ mlist_free_all(ms);
+ return -1;
+}
+#endif
+
+/* const char *fn: list of magic files and directories */
+file_protected int
+file_apprentice(struct magic_set *ms, const char *fn, int action)
+{
+ char *p, *mfn;
+ int fileerr, errs = -1;
+ size_t i, j;
+
+ (void)file_reset(ms, 0);
+
+ if ((fn = magic_getpath(fn, action)) == NULL)
+ return -1;
+
+ init_file_tables();
+
+ if ((mfn = strdup(fn)) == NULL) {
+ file_oomem(ms, strlen(fn));
+ return -1;
+ }
+
+ for (i = 0; i < MAGIC_SETS; i++) {
+ mlist_free(ms->mlist[i]);
+ if ((ms->mlist[i] = mlist_alloc()) == NULL) {
+ file_oomem(ms, sizeof(*ms->mlist[0]));
+ for (j = 0; j < i; j++) {
+ mlist_free(ms->mlist[j]);
+ ms->mlist[j] = NULL;
+ }
+ free(mfn);
+ return -1;
+ }
+ }
+ fn = mfn;
+
+ while (fn) {
+ p = CCAST(char *, strchr(fn, PATHSEP));
+ if (p)
+ *p++ = '\0';
+ if (*fn == '\0')
+ break;
+ fileerr = apprentice_1(ms, fn, action);
+ errs = MAX(errs, fileerr);
+ fn = p;
+ }
+
+ free(mfn);
+
+ if (errs == -1) {
+ for (i = 0; i < MAGIC_SETS; i++) {
+ mlist_free(ms->mlist[i]);
+ ms->mlist[i] = NULL;
+ }
+ file_error(ms, 0, "could not find any valid magic files!");
+ return -1;
+ }
+
+#if 0
+ /*
+ * Always leave the database loaded
+ */
+ if (action == FILE_LOAD)
+ return 0;
+
+ for (i = 0; i < MAGIC_SETS; i++) {
+ mlist_free(ms->mlist[i]);
+ ms->mlist[i] = NULL;
+ }
+#endif
+
+ switch (action) {
+ case FILE_LOAD:
+ case FILE_COMPILE:
+ case FILE_CHECK:
+ case FILE_LIST:
+ return 0;
+ default:
+ file_error(ms, 0, "Invalid action %d", action);
+ return -1;
+ }
+}
+
+/*
+ * Compute the real length of a magic expression, for the purposes
+ * of determining how "strong" a magic expression is (approximating
+ * how specific its matches are):
+ * - magic characters count 0 unless escaped.
+ * - [] expressions count 1
+ * - {} expressions count 0
+ * - regular characters or escaped magic characters count 1
+ * - 0 length expressions count as one
+ */
+file_private size_t
+nonmagic(const char *str)
+{
+ const char *p;
+ size_t rv = 0;
+
+ for (p = str; *p; p++)
+ switch (*p) {
+ case '\\': /* Escaped anything counts 1 */
+ if (!*++p)
+ p--;
+ rv++;
+ continue;
+ case '?': /* Magic characters count 0 */
+ case '*':
+ case '.':
+ case '+':
+ case '^':
+ case '$':
+ continue;
+ case '[': /* Bracketed expressions count 1 the ']' */
+ while (*p && *p != ']')
+ p++;
+ p--;
+ continue;
+ case '{': /* Braced expressions count 0 */
+ while (*p && *p != '}')
+ p++;
+ if (!*p)
+ p--;
+ continue;
+ default: /* Anything else counts 1 */
+ rv++;
+ continue;
+ }
+
+ return rv == 0 ? 1 : rv; /* Return at least 1 */
+}
+
+
+file_private size_t
+typesize(int type)
+{
+ switch (type) {
+ case FILE_BYTE:
+ return 1;
+
+ case FILE_SHORT:
+ case FILE_LESHORT:
+ case FILE_BESHORT:
+ case FILE_MSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ case FILE_MSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ return 2;
+
+ case FILE_LONG:
+ case FILE_LELONG:
+ case FILE_BELONG:
+ case FILE_MELONG:
+ return 4;
+
+ case FILE_DATE:
+ case FILE_LEDATE:
+ case FILE_BEDATE:
+ case FILE_MEDATE:
+ case FILE_LDATE:
+ case FILE_LELDATE:
+ case FILE_BELDATE:
+ case FILE_MELDATE:
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ case FILE_BEID3:
+ case FILE_LEID3:
+ return 4;
+
+ case FILE_QUAD:
+ case FILE_BEQUAD:
+ case FILE_LEQUAD:
+ case FILE_QDATE:
+ case FILE_LEQDATE:
+ case FILE_BEQDATE:
+ case FILE_QLDATE:
+ case FILE_LEQLDATE:
+ case FILE_BEQLDATE:
+ case FILE_QWDATE:
+ case FILE_LEQWDATE:
+ case FILE_BEQWDATE:
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ case FILE_OFFSET:
+ case FILE_BEVARINT:
+ case FILE_LEVARINT:
+ return 8;
+
+ case FILE_GUID:
+ return 16;
+
+ default:
+ return FILE_BADSIZE;
+ }
+}
+
+/*
+ * Get weight of this magic entry, for sorting purposes.
+ */
+file_private ssize_t
+apprentice_magic_strength_1(const struct magic *m)
+{
+#define MULT 10U
+ size_t ts, v;
+ ssize_t val = 2 * MULT; /* baseline strength */
+
+ switch (m->type) {
+ case FILE_DEFAULT: /* make sure this sorts last */
+ if (m->factor_op != FILE_FACTOR_OP_NONE) {
+ file_magwarn(NULL, "Usupported factor_op in default %d",
+ m->factor_op);
+ }
+ return 0;
+
+ case FILE_BYTE:
+ case FILE_SHORT:
+ case FILE_LESHORT:
+ case FILE_BESHORT:
+ case FILE_LONG:
+ case FILE_LELONG:
+ case FILE_BELONG:
+ case FILE_MELONG:
+ case FILE_DATE:
+ case FILE_LEDATE:
+ case FILE_BEDATE:
+ case FILE_MEDATE:
+ case FILE_LDATE:
+ case FILE_LELDATE:
+ case FILE_BELDATE:
+ case FILE_MELDATE:
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ case FILE_QUAD:
+ case FILE_BEQUAD:
+ case FILE_LEQUAD:
+ case FILE_QDATE:
+ case FILE_LEQDATE:
+ case FILE_BEQDATE:
+ case FILE_QLDATE:
+ case FILE_LEQLDATE:
+ case FILE_BEQLDATE:
+ case FILE_QWDATE:
+ case FILE_LEQWDATE:
+ case FILE_BEQWDATE:
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ case FILE_BEVARINT:
+ case FILE_LEVARINT:
+ case FILE_GUID:
+ case FILE_BEID3:
+ case FILE_LEID3:
+ case FILE_OFFSET:
+ case FILE_MSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ case FILE_MSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ ts = typesize(m->type);
+ if (ts == FILE_BADSIZE) {
+ (void)fprintf(stderr, "Bad size for type %d\n",
+ m->type);
+ abort();
+ }
+ val += ts * MULT;
+ break;
+
+ case FILE_PSTRING:
+ case FILE_STRING:
+ case FILE_OCTAL:
+ val += m->vallen * MULT;
+ break;
+
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ val += m->vallen * MULT / 2;
+ break;
+
+ case FILE_SEARCH:
+ if (m->vallen == 0)
+ break;
+ val += m->vallen * MAX(MULT / m->vallen, 1);
+ break;
+
+ case FILE_REGEX:
+ v = nonmagic(m->value.s);
+ val += v * MAX(MULT / v, 1);
+ break;
+
+ case FILE_INDIRECT:
+ case FILE_NAME:
+ case FILE_USE:
+ case FILE_CLEAR:
+ break;
+
+ case FILE_DER:
+ val += MULT;
+ break;
+
+ default:
+ (void)fprintf(stderr, "Bad type %d\n", m->type);
+ abort();
+ }
+
+ switch (m->reln) {
+ case 'x': /* matches anything penalize */
+ case '!': /* matches almost anything penalize */
+ val = 0;
+ break;
+
+ case '=': /* Exact match, prefer */
+ val += MULT;
+ break;
+
+ case '>':
+ case '<': /* comparison match reduce strength */
+ val -= 2 * MULT;
+ break;
+
+ case '^':
+ case '&': /* masking bits, we could count them too */
+ val -= MULT;
+ break;
+
+ default:
+ (void)fprintf(stderr, "Bad relation %c\n", m->reln);
+ abort();
+ }
+
+ return val;
+}
+
+
+/*ARGSUSED*/
+file_protected size_t
+file_magic_strength(const struct magic *m,
+ size_t nmagic __attribute__((__unused__)))
+{
+ ssize_t val = apprentice_magic_strength_1(m);
+
+#ifdef notyet
+ if (m->desc[0] == '\0') {
+ size_t i;
+ /*
+ * Magic entries with no description get their continuations
+ * added
+ */
+ for (i = 1; m[i].cont_level != 0 && i < MIN(nmagic, 3); i++) {
+ ssize_t v = apprentice_magic_strength_1(&m[i]) >>
+ (i + 1);
+ val += v;
+ if (m[i].desc[0] != '\0')
+ break;
+ }
+ }
+#endif
+
+ switch (m->factor_op) {
+ case FILE_FACTOR_OP_NONE:
+ break;
+ case FILE_FACTOR_OP_PLUS:
+ val += m->factor;
+ break;
+ case FILE_FACTOR_OP_MINUS:
+ val -= m->factor;
+ break;
+ case FILE_FACTOR_OP_TIMES:
+ val *= m->factor;
+ break;
+ case FILE_FACTOR_OP_DIV:
+ val /= m->factor;
+ break;
+ default:
+ (void)fprintf(stderr, "Bad factor_op %u\n", m->factor_op);
+ abort();
+ }
+
+ if (val <= 0) /* ensure we only return 0 for FILE_DEFAULT */
+ val = 1;
+
+#ifndef notyet
+ /*
+ * Magic entries with no description get a bonus because they depend
+ * on subsequent magic entries to print something.
+ */
+ if (m->desc[0] == '\0')
+ val++;
+#endif
+
+ return val;
+}
+
+/*
+ * Sort callback for sorting entries by "strength" (basically length)
+ */
+file_private int
+apprentice_sort(const void *a, const void *b)
+{
+ const struct magic_entry *ma = CAST(const struct magic_entry *, a);
+ const struct magic_entry *mb = CAST(const struct magic_entry *, b);
+ size_t sa = file_magic_strength(ma->mp, ma->cont_count);
+ size_t sb = file_magic_strength(mb->mp, mb->cont_count);
+ if (sa == sb)
+ return 0;
+ else if (sa > sb)
+ return -1;
+ else
+ return 1;
+}
+
+/*
+ * Shows sorted patterns list in the order which is used for the matching
+ */
+file_private void
+apprentice_list(struct mlist *mlist, int mode)
+{
+ uint32_t magindex, descindex, mimeindex, lineindex;
+ struct mlist *ml;
+ for (ml = mlist->next; ml != mlist; ml = ml->next) {
+ for (magindex = 0; magindex < ml->nmagic; magindex++) {
+ struct magic *m = &ml->magic[magindex];
+ if ((m->flag & mode) != mode) {
+ /* Skip sub-tests */
+ while (magindex + 1 < ml->nmagic &&
+ ml->magic[magindex + 1].cont_level != 0)
+ ++magindex;
+ continue; /* Skip to next top-level test*/
+ }
+
+ /*
+ * Try to iterate over the tree until we find item with
+ * description/mimetype.
+ */
+ lineindex = descindex = mimeindex = magindex;
+ for (; magindex + 1 < ml->nmagic &&
+ ml->magic[magindex + 1].cont_level != 0;
+ magindex++) {
+ uint32_t mi = magindex + 1;
+ if (*ml->magic[descindex].desc == '\0'
+ && *ml->magic[mi].desc)
+ descindex = mi;
+ if (*ml->magic[mimeindex].mimetype == '\0'
+ && *ml->magic[mi].mimetype)
+ mimeindex = mi;
+ }
+
+ printf("Strength = %3" SIZE_T_FORMAT "u@%u: %s [%s]\n",
+ file_magic_strength(m, ml->nmagic - magindex),
+ ml->magic[lineindex].lineno,
+ ml->magic[descindex].desc,
+ ml->magic[mimeindex].mimetype);
+ }
+ }
+}
+
+file_private void
+set_test_type(struct magic *mstart, struct magic *m)
+{
+ switch (m->type) {
+ case FILE_BYTE:
+ case FILE_SHORT:
+ case FILE_LONG:
+ case FILE_DATE:
+ case FILE_BESHORT:
+ case FILE_BELONG:
+ case FILE_BEDATE:
+ case FILE_LESHORT:
+ case FILE_LELONG:
+ case FILE_LEDATE:
+ case FILE_LDATE:
+ case FILE_BELDATE:
+ case FILE_LELDATE:
+ case FILE_MEDATE:
+ case FILE_MELDATE:
+ case FILE_MELONG:
+ case FILE_QUAD:
+ case FILE_LEQUAD:
+ case FILE_BEQUAD:
+ case FILE_QDATE:
+ case FILE_LEQDATE:
+ case FILE_BEQDATE:
+ case FILE_QLDATE:
+ case FILE_LEQLDATE:
+ case FILE_BEQLDATE:
+ case FILE_QWDATE:
+ case FILE_LEQWDATE:
+ case FILE_BEQWDATE:
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ case FILE_BEVARINT:
+ case FILE_LEVARINT:
+ case FILE_DER:
+ case FILE_GUID:
+ case FILE_OFFSET:
+ case FILE_MSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ case FILE_MSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ case FILE_OCTAL:
+ mstart->flag |= BINTEST;
+ break;
+ case FILE_STRING:
+ case FILE_PSTRING:
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ /* Allow text overrides */
+ if (mstart->str_flags & STRING_TEXTTEST)
+ mstart->flag |= TEXTTEST;
+ else
+ mstart->flag |= BINTEST;
+ break;
+ case FILE_REGEX:
+ case FILE_SEARCH:
+ /* Check for override */
+ if (mstart->str_flags & STRING_BINTEST)
+ mstart->flag |= BINTEST;
+ if (mstart->str_flags & STRING_TEXTTEST)
+ mstart->flag |= TEXTTEST;
+
+ if (mstart->flag & (TEXTTEST|BINTEST))
+ break;
+
+ /* binary test if pattern is not text */
+ if (file_looks_utf8(m->value.us, CAST(size_t, m->vallen), NULL,
+ NULL) <= 0)
+ mstart->flag |= BINTEST;
+ else
+ mstart->flag |= TEXTTEST;
+ break;
+ case FILE_DEFAULT:
+ /* can't deduce anything; we shouldn't see this at the
+ top level anyway */
+ break;
+ case FILE_INVALID:
+ default:
+ /* invalid search type, but no need to complain here */
+ break;
+ }
+}
+
+file_private int
+addentry(struct magic_set *ms, struct magic_entry *me,
+ struct magic_entry_set *mset)
+{
+ size_t i = me->mp->type == FILE_NAME ? 1 : 0;
+ if (mset[i].me == NULL || mset[i].count == mset[i].max) {
+ struct magic_entry *mp;
+
+ size_t incr = mset[i].max + ALLOC_INCR;
+ if ((mp = CAST(struct magic_entry *,
+ realloc(mset[i].me, sizeof(*mp) * incr))) ==
+ NULL) {
+ file_oomem(ms, sizeof(*mp) * incr);
+ return -1;
+ }
+ (void)memset(&mp[mset[i].count], 0, sizeof(*mp) *
+ ALLOC_INCR);
+ mset[i].me = mp;
+ mset[i].max = CAST(uint32_t, incr);
+ assert(mset[i].max == incr);
+ }
+ mset[i].me[mset[i].count++] = *me;
+ memset(me, 0, sizeof(*me));
+ return 0;
+}
+
+/*
+ * Load and parse one file.
+ */
+file_private void
+load_1(struct magic_set *ms, int action, const char *fn, int *errs,
+ struct magic_entry_set *mset)
+{
+ size_t lineno = 0, llen = 0;
+ char *line = NULL;
+ ssize_t len;
+ struct magic_entry me;
+
+ FILE *f = fopen(ms->file = fn, "r");
+ if (f == NULL) {
+ if (errno != ENOENT)
+ file_error(ms, errno, "cannot read magic file `%s'",
+ fn);
+ (*errs)++;
+ return;
+ }
+
+ memset(&me, 0, sizeof(me));
+ /* read and parse this file */
+ for (ms->line = 1; (len = getline(&line, &llen, f)) != -1;
+ ms->line++) {
+ if (len == 0) /* null line, garbage, etc */
+ continue;
+ if (line[len - 1] == '\n') {
+ lineno++;
+ line[len - 1] = '\0'; /* delete newline */
+ }
+ switch (line[0]) {
+ case '\0': /* empty, do not parse */
+ case '#': /* comment, do not parse */
+ continue;
+ case '!':
+ if (line[1] == ':') {
+ size_t i;
+
+ for (i = 0; bang[i].name != NULL; i++) {
+ if (CAST(size_t, len - 2) > bang[i].len &&
+ memcmp(bang[i].name, line + 2,
+ bang[i].len) == 0)
+ break;
+ }
+ if (bang[i].name == NULL) {
+ file_error(ms, 0,
+ "Unknown !: entry `%s'", line);
+ (*errs)++;
+ continue;
+ }
+ if (me.mp == NULL) {
+ file_error(ms, 0,
+ "No current entry for :!%s type",
+ bang[i].name);
+ (*errs)++;
+ continue;
+ }
+ if ((*bang[i].fun)(ms, &me,
+ line + bang[i].len + 2,
+ len - bang[i].len - 2) != 0) {
+ (*errs)++;
+ continue;
+ }
+ continue;
+ }
+ /*FALLTHROUGH*/
+ default:
+ again:
+ switch (parse(ms, &me, line, lineno, action)) {
+ case 0:
+ continue;
+ case 1:
+ (void)addentry(ms, &me, mset);
+ goto again;
+ default:
+ (*errs)++;
+ break;
+ }
+ }
+ }
+ if (me.mp)
+ (void)addentry(ms, &me, mset);
+ free(line);
+ (void)fclose(f);
+}
+
+/*
+ * parse a file or directory of files
+ * const char *fn: name of magic file or directory
+ */
+file_private int
+cmpstrp(const void *p1, const void *p2)
+{
+ return strcmp(*RCAST(char *const *, p1), *RCAST(char *const *, p2));
+}
+
+
+file_private uint32_t
+set_text_binary(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
+ uint32_t starttest)
+{
+ static const char text[] = "text";
+ static const char binary[] = "binary";
+ static const size_t len = sizeof(text);
+
+ uint32_t i = starttest;
+
+ do {
+ set_test_type(me[starttest].mp, me[i].mp);
+ if ((ms->flags & MAGIC_DEBUG) == 0)
+ continue;
+ (void)fprintf(stderr, "%s%s%s: %s\n",
+ me[i].mp->mimetype,
+ me[i].mp->mimetype[0] == '\0' ? "" : "; ",
+ me[i].mp->desc[0] ? me[i].mp->desc : "(no description)",
+ me[i].mp->flag & BINTEST ? binary : text);
+ if (me[i].mp->flag & BINTEST) {
+ char *p = strstr(me[i].mp->desc, text);
+ if (p && (p == me[i].mp->desc ||
+ isspace(CAST(unsigned char, p[-1]))) &&
+ (p + len - me[i].mp->desc == MAXstring
+ || (p[len] == '\0' ||
+ isspace(CAST(unsigned char, p[len])))))
+ (void)fprintf(stderr, "*** Possible "
+ "binary test for text type\n");
+ }
+ } while (++i < nme && me[i].mp->cont_level != 0);
+ return i;
+}
+
+file_private void
+set_last_default(struct magic_set *ms, struct magic_entry *me, uint32_t nme)
+{
+ uint32_t i;
+ for (i = 0; i < nme; i++) {
+ if (me[i].mp->cont_level == 0 &&
+ me[i].mp->type == FILE_DEFAULT) {
+ while (++i < nme)
+ if (me[i].mp->cont_level == 0)
+ break;
+ if (i != nme) {
+ /* XXX - Ugh! */
+ ms->line = me[i].mp->lineno;
+ file_magwarn(ms,
+ "level 0 \"default\" did not sort last");
+ }
+ return;
+ }
+ }
+}
+
+file_private int
+coalesce_entries(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
+ struct magic **ma, uint32_t *nma)
+{
+ uint32_t i, mentrycount = 0;
+ size_t slen;
+
+ for (i = 0; i < nme; i++)
+ mentrycount += me[i].cont_count;
+
+ if (mentrycount == 0) {
+ *ma = NULL;
+ *nma = 0;
+ return 0;
+ }
+
+ slen = sizeof(**ma) * mentrycount;
+ if ((*ma = CAST(struct magic *, malloc(slen))) == NULL) {
+ file_oomem(ms, slen);
+ return -1;
+ }
+
+ mentrycount = 0;
+ for (i = 0; i < nme; i++) {
+ (void)memcpy(*ma + mentrycount, me[i].mp,
+ me[i].cont_count * sizeof(**ma));
+ mentrycount += me[i].cont_count;
+ }
+ *nma = mentrycount;
+ return 0;
+}
+
+file_private void
+magic_entry_free(struct magic_entry *me, uint32_t nme)
+{
+ uint32_t i;
+ if (me == NULL)
+ return;
+ for (i = 0; i < nme; i++)
+ free(me[i].mp);
+ free(me);
+}
+
+file_private struct magic_map *
+apprentice_load(struct magic_set *ms, const char *fn, int action)
+{
+ int errs = 0;
+ uint32_t i, j;
+ size_t files = 0, maxfiles = 0;
+ char **filearr = NULL, *mfn;
+ struct stat st;
+ struct magic_map *map;
+ struct magic_entry_set mset[MAGIC_SETS];
+ DIR *dir;
+ struct dirent *d;
+
+ memset(mset, 0, sizeof(mset));
+ ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */
+
+
+ if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL)
+ {
+ file_oomem(ms, sizeof(*map));
+ return NULL;
+ }
+ map->type = MAP_TYPE_MALLOC;
+
+ /* print silly verbose header for USG compat. */
+ if (action == FILE_CHECK)
+ (void)fprintf(stderr, "%s\n", usg_hdr);
+
+ /* load directory or file */
+ if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) {
+ dir = opendir(fn);
+ if (!dir) {
+ errs++;
+ goto out;
+ }
+ while ((d = readdir(dir)) != NULL) {
+ if (d->d_name[0] == '.')
+ continue;
+ if (asprintf(&mfn, "%s/%s", fn, d->d_name) < 0) {
+ file_oomem(ms,
+ strlen(fn) + strlen(d->d_name) + 2);
+ errs++;
+ closedir(dir);
+ goto out;
+ }
+ if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) {
+ free(mfn);
+ continue;
+ }
+ if (files >= maxfiles) {
+ size_t mlen;
+ char **nfilearr;
+ maxfiles = (maxfiles + 1) * 2;
+ mlen = maxfiles * sizeof(*filearr);
+ if ((nfilearr = CAST(char **,
+ realloc(filearr, mlen))) == NULL) {
+ file_oomem(ms, mlen);
+ free(mfn);
+ closedir(dir);
+ errs++;
+ goto out;
+ }
+ filearr = nfilearr;
+ }
+ filearr[files++] = mfn;
+ }
+ closedir(dir);
+ if (filearr) {
+ qsort(filearr, files, sizeof(*filearr), cmpstrp);
+ for (i = 0; i < files; i++) {
+ load_1(ms, action, filearr[i], &errs, mset);
+ free(filearr[i]);
+ }
+ free(filearr);
+ filearr = NULL;
+ }
+ } else
+ load_1(ms, action, fn, &errs, mset);
+ if (errs)
+ goto out;
+
+ for (j = 0; j < MAGIC_SETS; j++) {
+ /* Set types of tests */
+ for (i = 0; i < mset[j].count; ) {
+ if (mset[j].me[i].mp->cont_level != 0) {
+ i++;
+ continue;
+ }
+ i = set_text_binary(ms, mset[j].me, mset[j].count, i);
+ }
+ if (mset[j].me)
+ qsort(mset[j].me, mset[j].count, sizeof(*mset[0].me),
+ apprentice_sort);
+
+ /*
+ * Make sure that any level 0 "default" line is last
+ * (if one exists).
+ */
+ set_last_default(ms, mset[j].me, mset[j].count);
+
+ /* coalesce per file arrays into a single one, if needed */
+ if (mset[j].count == 0)
+ continue;
+
+ if (coalesce_entries(ms, mset[j].me, mset[j].count,
+ &map->magic[j], &map->nmagic[j]) == -1) {
+ errs++;
+ goto out;
+ }
+ }
+
+out:
+ free(filearr);
+ for (j = 0; j < MAGIC_SETS; j++)
+ magic_entry_free(mset[j].me, mset[j].count);
+
+ if (errs) {
+ apprentice_unmap(map);
+ return NULL;
+ }
+ return map;
+}
+
+/*
+ * extend the sign bit if the comparison is to be signed
+ */
+file_protected uint64_t
+file_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
+{
+ if (!(m->flag & UNSIGNED)) {
+ switch(m->type) {
+ /*
+ * Do not remove the casts below. They are
+ * vital. When later compared with the data,
+ * the sign extension must have happened.
+ */
+ case FILE_BYTE:
+ v = CAST(signed char, v);
+ break;
+ case FILE_SHORT:
+ case FILE_BESHORT:
+ case FILE_LESHORT:
+ v = CAST(short, v);
+ break;
+ case FILE_DATE:
+ case FILE_BEDATE:
+ case FILE_LEDATE:
+ case FILE_MEDATE:
+ case FILE_LDATE:
+ case FILE_BELDATE:
+ case FILE_LELDATE:
+ case FILE_MELDATE:
+ case FILE_LONG:
+ case FILE_BELONG:
+ case FILE_LELONG:
+ case FILE_MELONG:
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ case FILE_MSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ case FILE_MSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ v = CAST(int32_t, v);
+ break;
+ case FILE_QUAD:
+ case FILE_BEQUAD:
+ case FILE_LEQUAD:
+ case FILE_QDATE:
+ case FILE_QLDATE:
+ case FILE_QWDATE:
+ case FILE_BEQDATE:
+ case FILE_BEQLDATE:
+ case FILE_BEQWDATE:
+ case FILE_LEQDATE:
+ case FILE_LEQLDATE:
+ case FILE_LEQWDATE:
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ case FILE_OFFSET:
+ case FILE_BEVARINT:
+ case FILE_LEVARINT:
+ v = CAST(int64_t, v);
+ break;
+ case FILE_STRING:
+ case FILE_PSTRING:
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ case FILE_REGEX:
+ case FILE_SEARCH:
+ case FILE_DEFAULT:
+ case FILE_INDIRECT:
+ case FILE_NAME:
+ case FILE_USE:
+ case FILE_CLEAR:
+ case FILE_DER:
+ case FILE_GUID:
+ case FILE_OCTAL:
+ break;
+ default:
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "cannot happen: m->type=%d\n",
+ m->type);
+ return FILE_BADSIZE;
+ }
+ }
+ return v;
+}
+
+file_private int
+string_modifier_check(struct magic_set *ms, struct magic *m)
+{
+ if ((ms->flags & MAGIC_CHECK) == 0)
+ return 0;
+
+ if ((m->type != FILE_REGEX || (m->str_flags & REGEX_LINE_COUNT) == 0) &&
+ (m->type != FILE_PSTRING && (m->str_flags & PSTRING_LEN) != 0)) {
+ file_magwarn(ms,
+ "'/BHhLl' modifiers are only allowed for pascal strings\n");
+ return -1;
+ }
+ switch (m->type) {
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ if (m->str_flags != 0) {
+ file_magwarn(ms,
+ "no modifiers allowed for 16-bit strings\n");
+ return -1;
+ }
+ break;
+ case FILE_STRING:
+ case FILE_PSTRING:
+ if ((m->str_flags & REGEX_OFFSET_START) != 0) {
+ file_magwarn(ms,
+ "'/%c' only allowed on regex and search\n",
+ CHAR_REGEX_OFFSET_START);
+ return -1;
+ }
+ break;
+ case FILE_SEARCH:
+ if (m->str_range == 0) {
+ file_magwarn(ms,
+ "missing range; defaulting to %d\n",
+ STRING_DEFAULT_RANGE);
+ m->str_range = STRING_DEFAULT_RANGE;
+ return -1;
+ }
+ break;
+ case FILE_REGEX:
+ if ((m->str_flags & STRING_COMPACT_WHITESPACE) != 0) {
+ file_magwarn(ms, "'/%c' not allowed on regex\n",
+ CHAR_COMPACT_WHITESPACE);
+ return -1;
+ }
+ if ((m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) != 0) {
+ file_magwarn(ms, "'/%c' not allowed on regex\n",
+ CHAR_COMPACT_OPTIONAL_WHITESPACE);
+ return -1;
+ }
+ break;
+ default:
+ file_magwarn(ms, "coding error: m->type=%d\n",
+ m->type);
+ return -1;
+ }
+ return 0;
+}
+
+file_private int
+get_op(char c)
+{
+ switch (c) {
+ case '&':
+ return FILE_OPAND;
+ case '|':
+ return FILE_OPOR;
+ case '^':
+ return FILE_OPXOR;
+ case '+':
+ return FILE_OPADD;
+ case '-':
+ return FILE_OPMINUS;
+ case '*':
+ return FILE_OPMULTIPLY;
+ case '/':
+ return FILE_OPDIVIDE;
+ case '%':
+ return FILE_OPMODULO;
+ default:
+ return -1;
+ }
+}
+
+#ifdef ENABLE_CONDITIONALS
+file_private int
+get_cond(const char *l, const char **t)
+{
+ static const struct cond_tbl_s {
+ char name[8];
+ size_t len;
+ int cond;
+ } cond_tbl[] = {
+ { "if", 2, COND_IF },
+ { "elif", 4, COND_ELIF },
+ { "else", 4, COND_ELSE },
+ { "", 0, COND_NONE },
+ };
+ const struct cond_tbl_s *p;
+
+ for (p = cond_tbl; p->len; p++) {
+ if (strncmp(l, p->name, p->len) == 0 &&
+ isspace(CAST(unsigned char, l[p->len]))) {
+ if (t)
+ *t = l + p->len;
+ break;
+ }
+ }
+ return p->cond;
+}
+
+file_private int
+check_cond(struct magic_set *ms, int cond, uint32_t cont_level)
+{
+ int last_cond;
+ last_cond = ms->c.li[cont_level].last_cond;
+
+ switch (cond) {
+ case COND_IF:
+ if (last_cond != COND_NONE && last_cond != COND_ELIF) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "syntax error: `if'");
+ return -1;
+ }
+ last_cond = COND_IF;
+ break;
+
+ case COND_ELIF:
+ if (last_cond != COND_IF && last_cond != COND_ELIF) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "syntax error: `elif'");
+ return -1;
+ }
+ last_cond = COND_ELIF;
+ break;
+
+ case COND_ELSE:
+ if (last_cond != COND_IF && last_cond != COND_ELIF) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "syntax error: `else'");
+ return -1;
+ }
+ last_cond = COND_NONE;
+ break;
+
+ case COND_NONE:
+ last_cond = COND_NONE;
+ break;
+ }
+
+ ms->c.li[cont_level].last_cond = last_cond;
+ return 0;
+}
+#endif /* ENABLE_CONDITIONALS */
+
+file_private int
+parse_indirect_modifier(struct magic_set *ms, struct magic *m, const char **lp)
+{
+ const char *l = *lp;
+
+ while (!isspace(CAST(unsigned char, *++l)))
+ switch (*l) {
+ case CHAR_INDIRECT_RELATIVE:
+ m->str_flags |= INDIRECT_RELATIVE;
+ break;
+ default:
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "indirect modifier `%c' "
+ "invalid", *l);
+ *lp = l;
+ return -1;
+ }
+ *lp = l;
+ return 0;
+}
+
+file_private void
+parse_op_modifier(struct magic_set *ms, struct magic *m, const char **lp,
+ int op)
+{
+ const char *l = *lp;
+ char *t;
+ uint64_t val;
+
+ ++l;
+ m->mask_op |= op;
+ val = CAST(uint64_t, strtoull(l, &t, 0));
+ l = t;
+ m->num_mask = file_signextend(ms, m, val);
+ eatsize(&l);
+ *lp = l;
+}
+
+file_private int
+parse_string_modifier(struct magic_set *ms, struct magic *m, const char **lp)
+{
+ const char *l = *lp;
+ char *t;
+ int have_range = 0;
+
+ while (!isspace(CAST(unsigned char, *++l))) {
+ switch (*l) {
+ case '0': case '1': case '2':
+ case '3': case '4': case '5':
+ case '6': case '7': case '8':
+ case '9':
+ if (have_range && (ms->flags & MAGIC_CHECK))
+ file_magwarn(ms, "multiple ranges");
+ have_range = 1;
+ m->str_range = CAST(uint32_t, strtoul(l, &t, 0));
+ if (m->str_range == 0)
+ file_magwarn(ms, "zero range");
+ l = t - 1;
+ break;
+ case CHAR_COMPACT_WHITESPACE:
+ m->str_flags |= STRING_COMPACT_WHITESPACE;
+ break;
+ case CHAR_COMPACT_OPTIONAL_WHITESPACE:
+ m->str_flags |= STRING_COMPACT_OPTIONAL_WHITESPACE;
+ break;
+ case CHAR_IGNORE_LOWERCASE:
+ m->str_flags |= STRING_IGNORE_LOWERCASE;
+ break;
+ case CHAR_IGNORE_UPPERCASE:
+ m->str_flags |= STRING_IGNORE_UPPERCASE;
+ break;
+ case CHAR_REGEX_OFFSET_START:
+ m->str_flags |= REGEX_OFFSET_START;
+ break;
+ case CHAR_BINTEST:
+ m->str_flags |= STRING_BINTEST;
+ break;
+ case CHAR_TEXTTEST:
+ m->str_flags |= STRING_TEXTTEST;
+ break;
+ case CHAR_TRIM:
+ m->str_flags |= STRING_TRIM;
+ break;
+ case CHAR_FULL_WORD:
+ m->str_flags |= STRING_FULL_WORD;
+ break;
+ case CHAR_PSTRING_1_LE:
+#define SET_LENGTH(a) m->str_flags = (m->str_flags & ~PSTRING_LEN) | (a)
+ if (m->type != FILE_PSTRING)
+ goto bad;
+ SET_LENGTH(PSTRING_1_LE);
+ break;
+ case CHAR_PSTRING_2_BE:
+ if (m->type != FILE_PSTRING)
+ goto bad;
+ SET_LENGTH(PSTRING_2_BE);
+ break;
+ case CHAR_PSTRING_2_LE:
+ if (m->type != FILE_PSTRING)
+ goto bad;
+ SET_LENGTH(PSTRING_2_LE);
+ break;
+ case CHAR_PSTRING_4_BE:
+ if (m->type != FILE_PSTRING)
+ goto bad;
+ SET_LENGTH(PSTRING_4_BE);
+ break;
+ case CHAR_PSTRING_4_LE:
+ switch (m->type) {
+ case FILE_PSTRING:
+ case FILE_REGEX:
+ break;
+ default:
+ goto bad;
+ }
+ SET_LENGTH(PSTRING_4_LE);
+ break;
+ case CHAR_PSTRING_LENGTH_INCLUDES_ITSELF:
+ if (m->type != FILE_PSTRING)
+ goto bad;
+ m->str_flags |= PSTRING_LENGTH_INCLUDES_ITSELF;
+ break;
+ default:
+ bad:
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "string modifier `%c' "
+ "invalid", *l);
+ goto out;
+ }
+ /* allow multiple '/' for readability */
+ if (l[1] == '/' && !isspace(CAST(unsigned char, l[2])))
+ l++;
+ }
+ if (string_modifier_check(ms, m) == -1)
+ goto out;
+ *lp = l;
+ return 0;
+out:
+ *lp = l;
+ return -1;
+}
+
+/*
+ * parse one line from magic file, put into magic[index++] if valid
+ */
+file_private int
+parse(struct magic_set *ms, struct magic_entry *me, const char *line,
+ size_t lineno, int action)
+{
+#ifdef ENABLE_CONDITIONALS
+ static uint32_t last_cont_level = 0;
+#endif
+ size_t i;
+ struct magic *m;
+ const char *l = line;
+ char *t;
+ int op;
+ uint32_t cont_level;
+ int32_t diff;
+
+ cont_level = 0;
+
+ /*
+ * Parse the offset.
+ */
+ while (*l == '>') {
+ ++l; /* step over */
+ cont_level++;
+ }
+#ifdef ENABLE_CONDITIONALS
+ if (cont_level == 0 || cont_level > last_cont_level)
+ if (file_check_mem(ms, cont_level) == -1)
+ return -1;
+ last_cont_level = cont_level;
+#endif
+ if (cont_level != 0) {
+ if (me->mp == NULL) {
+ file_magerror(ms, "No current entry for continuation");
+ return -1;
+ }
+ if (me->cont_count == 0) {
+ file_magerror(ms, "Continuations present with 0 count");
+ return -1;
+ }
+ m = &me->mp[me->cont_count - 1];
+ diff = CAST(int32_t, cont_level) - CAST(int32_t, m->cont_level);
+ if (diff > 1)
+ file_magwarn(ms, "New continuation level %u is more "
+ "than one larger than current level %u", cont_level,
+ m->cont_level);
+ if (me->cont_count == me->max_count) {
+ struct magic *nm;
+ size_t cnt = me->max_count + ALLOC_CHUNK;
+ if ((nm = CAST(struct magic *, realloc(me->mp,
+ sizeof(*nm) * cnt))) == NULL) {
+ file_oomem(ms, sizeof(*nm) * cnt);
+ return -1;
+ }
+ me->mp = nm;
+ me->max_count = CAST(uint32_t, cnt);
+ }
+ m = &me->mp[me->cont_count++];
+ (void)memset(m, 0, sizeof(*m));
+ m->cont_level = cont_level;
+ } else {
+ static const size_t len = sizeof(*m) * ALLOC_CHUNK;
+ if (me->mp != NULL)
+ return 1;
+ if ((m = CAST(struct magic *, malloc(len))) == NULL) {
+ file_oomem(ms, len);
+ return -1;
+ }
+ me->mp = m;
+ me->max_count = ALLOC_CHUNK;
+ (void)memset(m, 0, sizeof(*m));
+ m->factor_op = FILE_FACTOR_OP_NONE;
+ m->cont_level = 0;
+ me->cont_count = 1;
+ }
+ m->lineno = CAST(uint32_t, lineno);
+
+ if (*l == '&') { /* m->cont_level == 0 checked below. */
+ ++l; /* step over */
+ m->flag |= OFFADD;
+ }
+ if (*l == '(') {
+ ++l; /* step over */
+ m->flag |= INDIR;
+ if (m->flag & OFFADD)
+ m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
+
+ if (*l == '&') { /* m->cont_level == 0 checked below */
+ ++l; /* step over */
+ m->flag |= OFFADD;
+ }
+ }
+ /* Indirect offsets are not valid at level 0. */
+ if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD))) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "relative offset at level 0");
+ return -1;
+ }
+
+ /* get offset, then skip over it */
+ if (*l == '-') {
+ ++l; /* step over */
+ m->flag |= OFFNEGATIVE;
+ }
+ m->offset = CAST(int32_t, strtol(l, &t, 0));
+ if (l == t) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "offset `%s' invalid", l);
+ return -1;
+ }
+
+ l = t;
+
+ if (m->flag & INDIR) {
+ m->in_type = FILE_LONG;
+ m->in_offset = 0;
+ m->in_op = 0;
+ /*
+ * read [.,lbs][+-]nnnnn)
+ */
+ if (*l == '.' || *l == ',') {
+ if (*l == ',')
+ m->in_op |= FILE_OPSIGNED;
+ l++;
+ switch (*l) {
+ case 'l':
+ m->in_type = FILE_LELONG;
+ break;
+ case 'L':
+ m->in_type = FILE_BELONG;
+ break;
+ case 'm':
+ m->in_type = FILE_MELONG;
+ break;
+ case 'h':
+ case 's':
+ m->in_type = FILE_LESHORT;
+ break;
+ case 'H':
+ case 'S':
+ m->in_type = FILE_BESHORT;
+ break;
+ case 'c':
+ case 'b':
+ case 'C':
+ case 'B':
+ m->in_type = FILE_BYTE;
+ break;
+ case 'e':
+ case 'f':
+ case 'g':
+ m->in_type = FILE_LEDOUBLE;
+ break;
+ case 'E':
+ case 'F':
+ case 'G':
+ m->in_type = FILE_BEDOUBLE;
+ break;
+ case 'i':
+ m->in_type = FILE_LEID3;
+ break;
+ case 'I':
+ m->in_type = FILE_BEID3;
+ break;
+ case 'o':
+ m->in_type = FILE_OCTAL;
+ break;
+ case 'q':
+ m->in_type = FILE_LEQUAD;
+ break;
+ case 'Q':
+ m->in_type = FILE_BEQUAD;
+ break;
+ default:
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms,
+ "indirect offset type `%c' invalid",
+ *l);
+ return -1;
+ }
+ l++;
+ }
+
+ if (*l == '~') {
+ m->in_op |= FILE_OPINVERSE;
+ l++;
+ }
+ if ((op = get_op(*l)) != -1) {
+ m->in_op |= op;
+ l++;
+ }
+ if (*l == '(') {
+ m->in_op |= FILE_OPINDIRECT;
+ l++;
+ }
+ if (isdigit(CAST(unsigned char, *l)) || *l == '-') {
+ m->in_offset = CAST(int32_t, strtol(l, &t, 0));
+ if (l == t) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms,
+ "in_offset `%s' invalid", l);
+ return -1;
+ }
+ l = t;
+ }
+ if (*l++ != ')' ||
+ ((m->in_op & FILE_OPINDIRECT) && *l++ != ')')) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms,
+ "missing ')' in indirect offset");
+ return -1;
+ }
+ }
+ EATAB;
+
+#ifdef ENABLE_CONDITIONALS
+ m->cond = get_cond(l, &l);
+ if (check_cond(ms, m->cond, cont_level) == -1)
+ return -1;
+
+ EATAB;
+#endif
+
+ /*
+ * Parse the type.
+ */
+ if (*l == 'u') {
+ /*
+ * Try it as a keyword type prefixed by "u"; match what
+ * follows the "u". If that fails, try it as an SUS
+ * integer type.
+ */
+ m->type = get_type(type_tbl, l + 1, &l);
+ if (m->type == FILE_INVALID) {
+ /*
+ * Not a keyword type; parse it as an SUS type,
+ * 'u' possibly followed by a number or C/S/L.
+ */
+ m->type = get_standard_integer_type(l, &l);
+ }
+ /* It's unsigned. */
+ if (m->type != FILE_INVALID)
+ m->flag |= UNSIGNED;
+ } else {
+ /*
+ * Try it as a keyword type. If that fails, try it as
+ * an SUS integer type if it begins with "d" or as an
+ * SUS string type if it begins with "s". In any case,
+ * it's not unsigned.
+ */
+ m->type = get_type(type_tbl, l, &l);
+ if (m->type == FILE_INVALID) {
+ /*
+ * Not a keyword type; parse it as an SUS type,
+ * either 'd' possibly followed by a number or
+ * C/S/L, or just 's'.
+ */
+ if (*l == 'd')
+ m->type = get_standard_integer_type(l, &l);
+ else if (*l == 's'
+ && !isalpha(CAST(unsigned char, l[1]))) {
+ m->type = FILE_STRING;
+ ++l;
+ }
+ }
+ }
+
+ if (m->type == FILE_INVALID) {
+ /* Not found - try it as a special keyword. */
+ m->type = get_type(special_tbl, l, &l);
+ }
+
+ if (m->type == FILE_INVALID) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "type `%s' invalid", l);
+ return -1;
+ }
+
+ if (m->type == FILE_NAME && cont_level != 0) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "`name%s' entries can only be "
+ "declared at top level", l);
+ return -1;
+ }
+
+ /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
+ /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
+
+ m->mask_op = 0;
+ if (*l == '~') {
+ if (!IS_STRING(m->type))
+ m->mask_op |= FILE_OPINVERSE;
+ else if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "'~' invalid for string types");
+ ++l;
+ }
+ m->str_range = 0;
+ m->str_flags = m->type == FILE_PSTRING ? PSTRING_1_LE : 0;
+ if ((op = get_op(*l)) != -1) {
+ if (IS_STRING(m->type)) {
+ int r;
+
+ if (op != FILE_OPDIVIDE) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms,
+ "invalid string/indirect op: "
+ "`%c'", *t);
+ return -1;
+ }
+
+ if (m->type == FILE_INDIRECT)
+ r = parse_indirect_modifier(ms, m, &l);
+ else
+ r = parse_string_modifier(ms, m, &l);
+ if (r == -1)
+ return -1;
+ } else
+ parse_op_modifier(ms, m, &l, op);
+ }
+
+ /*
+ * We used to set mask to all 1's here, instead let's just not do
+ * anything if mask = 0 (unless you have a better idea)
+ */
+ EATAB;
+
+ switch (*l) {
+ case '>':
+ case '<':
+ m->reln = *l;
+ ++l;
+ if (*l == '=') {
+ if (ms->flags & MAGIC_CHECK) {
+ file_magwarn(ms, "%c= not supported",
+ m->reln);
+ return -1;
+ }
+ ++l;
+ }
+ break;
+ /* Old-style anding: "0 byte &0x80 dynamically linked" */
+ case '&':
+ case '^':
+ case '=':
+ m->reln = *l;
+ ++l;
+ if (*l == '=') {
+ /* HP compat: ignore &= etc. */
+ ++l;
+ }
+ break;
+ case '!':
+ m->reln = *l;
+ ++l;
+ break;
+ default:
+ m->reln = '='; /* the default relation */
+ if (*l == 'x' && ((isascii(CAST(unsigned char, l[1])) &&
+ isspace(CAST(unsigned char, l[1]))) || !l[1])) {
+ m->reln = *l;
+ ++l;
+ }
+ break;
+ }
+ /*
+ * Grab the value part, except for an 'x' reln.
+ */
+ if (m->reln != 'x' && getvalue(ms, m, &l, action))
+ return -1;
+
+ /*
+ * TODO finish this macro and start using it!
+ * #define offsetcheck {if (offset > ms->bytes_max -1)
+ * magwarn("offset too big"); }
+ */
+
+ /*
+ * Now get last part - the description
+ */
+ EATAB;
+ if (l[0] == '\b') {
+ ++l;
+ m->flag |= NOSPACE;
+ } else if ((l[0] == '\\') && (l[1] == 'b')) {
+ ++l;
+ ++l;
+ m->flag |= NOSPACE;
+ }
+ for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); )
+ continue;
+ if (i == sizeof(m->desc)) {
+ m->desc[sizeof(m->desc) - 1] = '\0';
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "description `%s' truncated", m->desc);
+ }
+
+ /*
+ * We only do this check while compiling, or if any of the magic
+ * files were not compiled.
+ */
+ if (ms->flags & MAGIC_CHECK) {
+ if (check_format(ms, m) == -1)
+ return -1;
+ }
+#ifndef COMPILE_ONLY
+ if (action == FILE_CHECK) {
+ file_mdump(m);
+ }
+#endif
+ m->mimetype[0] = '\0'; /* initialise MIME type to none */
+ return 0;
+}
+
+/*
+ * parse a STRENGTH annotation line from magic file, put into magic[index - 1]
+ * if valid
+ */
+/*ARGSUSED*/
+file_private int
+parse_strength(struct magic_set *ms, struct magic_entry *me, const char *line,
+ size_t len __attribute__((__unused__)))
+{
+ const char *l = line;
+ char *el;
+ unsigned long factor;
+ char sbuf[512];
+ struct magic *m = &me->mp[0];
+
+ if (m->factor_op != FILE_FACTOR_OP_NONE) {
+ file_magwarn(ms,
+ "Current entry already has a strength type: %c %d",
+ m->factor_op, m->factor);
+ return -1;
+ }
+ if (m->type == FILE_NAME) {
+ file_magwarn(ms, "%s: Strength setting is not supported in "
+ "\"name\" magic entries",
+ file_printable(ms, sbuf, sizeof(sbuf), m->value.s,
+ sizeof(m->value.s)));
+ return -1;
+ }
+ EATAB;
+ switch (*l) {
+ case FILE_FACTOR_OP_NONE:
+ break;
+ case FILE_FACTOR_OP_PLUS:
+ case FILE_FACTOR_OP_MINUS:
+ case FILE_FACTOR_OP_TIMES:
+ case FILE_FACTOR_OP_DIV:
+ m->factor_op = *l++;
+ break;
+ default:
+ file_magwarn(ms, "Unknown factor op `%c'", *l);
+ return -1;
+ }
+ EATAB;
+ factor = strtoul(l, &el, 0);
+ if (factor > 255) {
+ file_magwarn(ms, "Too large factor `%lu'", factor);
+ goto out;
+ }
+ if (*el && !isspace(CAST(unsigned char, *el))) {
+ file_magwarn(ms, "Bad factor `%s'", l);
+ goto out;
+ }
+ m->factor = CAST(uint8_t, factor);
+ if (m->factor == 0 && m->factor_op == FILE_FACTOR_OP_DIV) {
+ file_magwarn(ms, "Cannot have factor op `%c' and factor %u",
+ m->factor_op, m->factor);
+ goto out;
+ }
+ return 0;
+out:
+ m->factor_op = FILE_FACTOR_OP_NONE;
+ m->factor = 0;
+ return -1;
+}
+
+file_private int
+goodchar(unsigned char x, const char *extra)
+{
+ return (isascii(x) && isalnum(x)) || strchr(extra, x);
+}
+
+file_private int
+parse_extra(struct magic_set *ms, struct magic_entry *me, const char *line,
+ size_t llen, off_t off, size_t len, const char *name, const char *extra,
+ int nt)
+{
+ size_t i;
+ const char *l = line;
+ struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
+ char *buf = CAST(char *, CAST(void *, m)) + off;
+
+ if (buf[0] != '\0') {
+ len = nt ? strlen(buf) : len;
+ file_magwarn(ms, "Current entry already has a %s type "
+ "`%.*s', new type `%s'", name, CAST(int, len), buf, l);
+ return -1;
+ }
+
+ if (*m->desc == '\0') {
+ file_magwarn(ms, "Current entry does not yet have a "
+ "description for adding a %s type", name);
+ return -1;
+ }
+
+ EATAB;
+ for (i = 0; *l && i < llen && i < len && goodchar(*l, extra);
+ buf[i++] = *l++)
+ continue;
+
+ if (i == len && *l) {
+ if (nt)
+ buf[len - 1] = '\0';
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "%s type `%s' truncated %"
+ SIZE_T_FORMAT "u", name, line, i);
+ } else {
+ if (!isspace(CAST(unsigned char, *l)) && !goodchar(*l, extra))
+ file_magwarn(ms, "%s type `%s' has bad char '%c'",
+ name, line, *l);
+ if (nt)
+ buf[i] = '\0';
+ }
+
+ if (i > 0)
+ return 0;
+
+ file_magerror(ms, "Bad magic entry '%s'", line);
+ return -1;
+}
+
+/*
+ * Parse an Apple CREATOR/TYPE annotation from magic file and put it into
+ * magic[index - 1]
+ */
+file_private int
+parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line,
+ size_t len)
+{
+ return parse_extra(ms, me, line, len,
+ CAST(off_t, offsetof(struct magic, apple)),
+ sizeof(me->mp[0].apple), "APPLE", "!+-./?", 0);
+}
+
+/*
+ * Parse a comma-separated list of extensions
+ */
+file_private int
+parse_ext(struct magic_set *ms, struct magic_entry *me, const char *line,
+ size_t len)
+{
+ return parse_extra(ms, me, line, len,
+ CAST(off_t, offsetof(struct magic, ext)),
+ sizeof(me->mp[0].ext), "EXTENSION", ",!+-/@?_$&~", 0);
+ /* & for b&w */
+ /* ~ for journal~ */
+}
+
+/*
+ * parse a MIME annotation line from magic file, put into magic[index - 1]
+ * if valid
+ */
+file_private int
+parse_mime(struct magic_set *ms, struct magic_entry *me, const char *line,
+ size_t len)
+{
+ return parse_extra(ms, me, line, len,
+ CAST(off_t, offsetof(struct magic, mimetype)),
+ sizeof(me->mp[0].mimetype), "MIME", "+-/.$?:{}", 1);
+}
+
+file_private int
+check_format_type(const char *ptr, int type, const char **estr)
+{
+ int quad = 0, h;
+ size_t len, cnt;
+ if (*ptr == '\0') {
+ /* Missing format string; bad */
+ *estr = "missing format spec";
+ return -1;
+ }
+
+ switch (file_formats[type]) {
+ case FILE_FMT_QUAD:
+ quad = 1;
+ /*FALLTHROUGH*/
+ case FILE_FMT_NUM:
+ if (quad == 0) {
+ switch (type) {
+ case FILE_BYTE:
+ h = 2;
+ break;
+ case FILE_SHORT:
+ case FILE_BESHORT:
+ case FILE_LESHORT:
+ h = 1;
+ break;
+ case FILE_LONG:
+ case FILE_BELONG:
+ case FILE_LELONG:
+ case FILE_MELONG:
+ case FILE_LEID3:
+ case FILE_BEID3:
+ case FILE_INDIRECT:
+ h = 0;
+ break;
+ default:
+ fprintf(stderr, "Bad number format %d", type);
+ abort();
+ }
+ } else
+ h = 0;
+ while (*ptr && strchr("-.#", *ptr) != NULL)
+ ptr++;
+#define CHECKLEN() do { \
+ for (len = cnt = 0; isdigit(CAST(unsigned char, *ptr)); ptr++, cnt++) \
+ len = len * 10 + (*ptr - '0'); \
+ if (cnt > 5 || len > 1024) \
+ goto toolong; \
+} while (/*CONSTCOND*/0)
+
+ CHECKLEN();
+ if (*ptr == '.')
+ ptr++;
+ CHECKLEN();
+ if (quad) {
+ if (*ptr++ != 'l')
+ goto invalid;
+ if (*ptr++ != 'l')
+ goto invalid;
+ }
+
+ switch (*ptr++) {
+#ifdef STRICT_FORMAT /* "long" formats are int formats for us */
+ /* so don't accept the 'l' modifier */
+ case 'l':
+ switch (*ptr++) {
+ case 'i':
+ case 'd':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ if (h == 0)
+ return 0;
+ /*FALLTHROUGH*/
+ default:
+ goto invalid;
+ }
+
+ /*
+ * Don't accept h and hh modifiers. They make writing
+ * magic entries more complicated, for very little benefit
+ */
+ case 'h':
+ if (h-- <= 0)
+ goto invalid;
+ switch (*ptr++) {
+ case 'h':
+ if (h-- <= 0)
+ goto invalid;
+ switch (*ptr++) {
+ case 'i':
+ case 'd':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ return 0;
+ default:
+ goto invalid;
+ }
+ case 'i':
+ case 'd':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ if (h == 0)
+ return 0;
+ /*FALLTHROUGH*/
+ default:
+ goto invalid;
+ }
+#endif
+ case 'c':
+ if (h == 2)
+ return 0;
+ goto invalid;
+ case 'i':
+ case 'd':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+#ifdef STRICT_FORMAT
+ if (h == 0)
+ return 0;
+ /*FALLTHROUGH*/
+#else
+ return 0;
+#endif
+ default:
+ goto invalid;
+ }
+
+ case FILE_FMT_FLOAT:
+ case FILE_FMT_DOUBLE:
+ if (*ptr == '-')
+ ptr++;
+ if (*ptr == '.')
+ ptr++;
+ CHECKLEN();
+ if (*ptr == '.')
+ ptr++;
+ CHECKLEN();
+ switch (*ptr++) {
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ return 0;
+
+ default:
+ goto invalid;
+ }
+
+
+ case FILE_FMT_STR:
+ if (*ptr == '-')
+ ptr++;
+ while (isdigit(CAST(unsigned char, *ptr)))
+ ptr++;
+ if (*ptr == '.') {
+ ptr++;
+ while (isdigit(CAST(unsigned char , *ptr)))
+ ptr++;
+ }
+
+ switch (*ptr++) {
+ case 's':
+ return 0;
+ default:
+ goto invalid;
+ }
+
+ default:
+ /* internal error */
+ fprintf(stderr, "Bad file format %d", type);
+ abort();
+ }
+invalid:
+ *estr = "not valid";
+ return -1;
+toolong:
+ *estr = "too long";
+ return -1;
+}
+
+/*
+ * Check that the optional printf format in description matches
+ * the type of the magic.
+ */
+file_private int
+check_format(struct magic_set *ms, struct magic *m)
+{
+ char *ptr;
+ const char *estr;
+
+ for (ptr = m->desc; *ptr; ptr++)
+ if (*ptr == '%')
+ break;
+ if (*ptr == '\0') {
+ /* No format string; ok */
+ return 1;
+ }
+
+ assert(file_nformats == file_nnames);
+
+ if (m->type >= file_nformats) {
+ file_magwarn(ms, "Internal error inconsistency between "
+ "m->type and format strings");
+ return -1;
+ }
+ if (file_formats[m->type] == FILE_FMT_NONE) {
+ file_magwarn(ms, "No format string for `%s' with description "
+ "`%s'", m->desc, file_names[m->type]);
+ return -1;
+ }
+
+ ptr++;
+ if (check_format_type(ptr, m->type, &estr) == -1) {
+ /*
+ * TODO: this error message is unhelpful if the format
+ * string is not one character long
+ */
+ file_magwarn(ms, "Printf format is %s for type "
+ "`%s' in description `%s'", estr,
+ file_names[m->type], m->desc);
+ return -1;
+ }
+
+ for (; *ptr; ptr++) {
+ if (*ptr == '%') {
+ file_magwarn(ms,
+ "Too many format strings (should have at most one) "
+ "for `%s' with description `%s'",
+ file_names[m->type], m->desc);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Read a numeric value from a pointer, into the value union of a magic
+ * pointer, according to the magic type. Update the string pointer to point
+ * just after the number read. Return 0 for success, non-zero for failure.
+ */
+file_private int
+getvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
+{
+ char *ep;
+ uint64_t ull;
+ int y;
+
+ switch (m->type) {
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ case FILE_STRING:
+ case FILE_PSTRING:
+ case FILE_REGEX:
+ case FILE_SEARCH:
+ case FILE_NAME:
+ case FILE_USE:
+ case FILE_DER:
+ case FILE_OCTAL:
+ *p = getstr(ms, m, *p, action == FILE_COMPILE);
+ if (*p == NULL) {
+ if (ms->flags & MAGIC_CHECK)
+ file_magwarn(ms, "cannot get string from `%s'",
+ m->value.s);
+ return -1;
+ }
+ if (m->type == FILE_REGEX) {
+ file_regex_t rx;
+ int rc =
+ file_regcomp(ms, &rx, m->value.s, REG_EXTENDED);
+ if (rc == 0) {
+ file_regfree(&rx);
+ }
+ return rc ? -1 : 0;
+ }
+ return 0;
+ default:
+ if (m->reln == 'x')
+ return 0;
+ break;
+ }
+
+ switch (m->type) {
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ errno = 0;
+#ifdef HAVE_STRTOF
+ m->value.f = strtof(*p, &ep);
+#else
+ m->value.f = (float)strtod(*p, &ep);
+#endif
+ if (errno == 0)
+ *p = ep;
+ return 0;
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ errno = 0;
+ m->value.d = strtod(*p, &ep);
+ if (errno == 0)
+ *p = ep;
+ return 0;
+ case FILE_GUID:
+ if (file_parse_guid(*p, m->value.guid) == -1)
+ return -1;
+ *p += FILE_GUID_SIZE - 1;
+ return 0;
+ default:
+ errno = 0;
+ ull = CAST(uint64_t, strtoull(*p, &ep, 0));
+ m->value.q = file_signextend(ms, m, ull);
+ if (*p == ep) {
+ file_magwarn(ms, "Unparsable number `%s'", *p);
+ return -1;
+ } else {
+ size_t ts = typesize(m->type);
+ uint64_t x;
+ const char *q;
+
+ if (ts == FILE_BADSIZE) {
+ file_magwarn(ms,
+ "Expected numeric type got `%s'",
+ type_tbl[m->type].name);
+ return -1;
+ }
+ for (q = *p; isspace(CAST(unsigned char, *q)); q++)
+ continue;
+ if (*q == '-' && ull != UINT64_MAX)
+ ull = -CAST(int64_t, ull);
+ switch (ts) {
+ case 1:
+ x = CAST(uint64_t, ull & ~0xffULL);
+ y = (x & ~0xffULL) != ~0xffULL;
+ break;
+ case 2:
+ x = CAST(uint64_t, ull & ~0xffffULL);
+ y = (x & ~0xffffULL) != ~0xffffULL;
+ break;
+ case 4:
+ x = CAST(uint64_t, ull & ~0xffffffffULL);
+ y = (x & ~0xffffffffULL) != ~0xffffffffULL;
+ break;
+ case 8:
+ x = 0;
+ y = 0;
+ break;
+ default:
+ fprintf(stderr, "Bad width %zu", ts);
+ abort();
+ }
+ if (x && y) {
+ file_magwarn(ms, "Overflow for numeric"
+ " type `%s' value %#" PRIx64,
+ type_tbl[m->type].name, ull);
+ return -1;
+ }
+ }
+ if (errno == 0) {
+ *p = ep;
+ eatsize(p);
+ }
+ return 0;
+ }
+}
+
+/*
+ * Convert a string containing C character escapes. Stop at an unescaped
+ * space or tab.
+ * Copy the converted version to "m->value.s", and the length in m->vallen.
+ * Return updated scan pointer as function result. Warn if set.
+ */
+file_private const char *
+getstr(struct magic_set *ms, struct magic *m, const char *s, int warn)
+{
+ const char *origs = s;
+ char *p = m->value.s;
+ size_t plen = sizeof(m->value.s);
+ char *origp = p;
+ char *pmax = p + plen - 1;
+ int c;
+ int val;
+ size_t bracket_nesting = 0;
+
+ while ((c = *s++) != '\0') {
+ if (isspace(CAST(unsigned char, c)))
+ break;
+ if (p >= pmax) {
+ file_error(ms, 0, "string too long: `%s'", origs);
+ return NULL;
+ }
+ if (c != '\\') {
+ if (c == '[') {
+ bracket_nesting++;
+ }
+ if (c == ']' && bracket_nesting > 0) {
+ bracket_nesting--;
+ }
+ *p++ = CAST(char, c);
+ continue;
+ }
+ switch(c = *s++) {
+
+ case '\0':
+ if (warn)
+ file_magwarn(ms, "incomplete escape");
+ s--;
+ goto out;
+ case '.':
+ if (m->type == FILE_REGEX &&
+ bracket_nesting == 0 && warn) {
+ file_magwarn(ms, "escaped dot ('.') found, "
+ "use \\\\. instead");
+ }
+ warn = 0; /* already did */
+ /*FALLTHROUGH*/
+ case '\t':
+ if (warn) {
+ file_magwarn(ms,
+ "escaped tab found, use \\\\t instead");
+ warn = 0; /* already did */
+ }
+ /*FALLTHROUGH*/
+ default:
+ if (warn) {
+ if (isprint(CAST(unsigned char, c))) {
+ /* Allow escaping of
+ * ``relations'' */
+ if (strchr("<>&^=!", c) == NULL
+ && (m->type != FILE_REGEX ||
+ strchr("[]().*?^$|{}", c)
+ == NULL)) {
+ file_magwarn(ms, "no "
+ "need to escape "
+ "`%c'", c);
+ }
+ } else {
+ file_magwarn(ms,
+ "unknown escape sequence: "
+ "\\%03o", c);
+ }
+ }
+ /*FALLTHROUGH*/
+ /* space, perhaps force people to use \040? */
+ case ' ':
+#if 0
+ /*
+ * Other things people escape, but shouldn't need to,
+ * so we disallow them
+ */
+ case '\'':
+ case '"':
+ case '?':
+#endif
+ /* Relations */
+ case '>':
+ case '<':
+ case '&':
+ case '^':
+ case '=':
+ case '!':
+ /* and backslash itself */
+ case '\\':
+ *p++ = CAST(char, c);
+ break;
+
+ case 'a':
+ *p++ = '\a';
+ break;
+
+ case 'b':
+ *p++ = '\b';
+ break;
+
+ case 'f':
+ *p++ = '\f';
+ break;
+
+ case 'n':
+ *p++ = '\n';
+ break;
+
+ case 'r':
+ *p++ = '\r';
+ break;
+
+ case 't':
+ *p++ = '\t';
+ break;
+
+ case 'v':
+ *p++ = '\v';
+ break;
+
+ /* \ and up to 3 octal digits */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = c - '0';
+ c = *s++; /* try for 2 */
+ if (c >= '0' && c <= '7') {
+ val = (val << 3) | (c - '0');
+ c = *s++; /* try for 3 */
+ if (c >= '0' && c <= '7')
+ val = (val << 3) | (c-'0');
+ else
+ --s;
+ }
+ else
+ --s;
+ *p++ = CAST(char, val);
+ break;
+
+ /* \x and up to 2 hex digits */
+ case 'x':
+ val = 'x'; /* Default if no digits */
+ c = hextoint(*s++); /* Get next char */
+ if (c >= 0) {
+ val = c;
+ c = hextoint(*s++);
+ if (c >= 0)
+ val = (val << 4) + c;
+ else
+ --s;
+ } else
+ --s;
+ *p++ = CAST(char, val);
+ break;
+ }
+ }
+ --s;
+out:
+ *p = '\0';
+ m->vallen = CAST(unsigned char, (p - origp));
+ if (m->type == FILE_PSTRING) {
+ size_t l = file_pstring_length_size(ms, m);
+ if (l == FILE_BADSIZE)
+ return NULL;
+ m->vallen += CAST(unsigned char, l);
+ }
+ return s;
+}
+
+
+/* Single hex char to int; -1 if not a hex char. */
+file_private int
+hextoint(int c)
+{
+ if (!isascii(CAST(unsigned char, c)))
+ return -1;
+ if (isdigit(CAST(unsigned char, c)))
+ return c - '0';
+ if ((c >= 'a') && (c <= 'f'))
+ return c + 10 - 'a';
+ if (( c>= 'A') && (c <= 'F'))
+ return c + 10 - 'A';
+ return -1;
+}
+
+
+/*
+ * Print a string containing C character escapes.
+ */
+file_protected void
+file_showstr(FILE *fp, const char *s, size_t len)
+{
+ char c;
+
+ for (;;) {
+ if (len == FILE_BADSIZE) {
+ c = *s++;
+ if (c == '\0')
+ break;
+ }
+ else {
+ if (len-- == 0)
+ break;
+ c = *s++;
+ }
+ if (c >= 040 && c <= 0176) /* TODO isprint && !iscntrl */
+ (void) fputc(c, fp);
+ else {
+ (void) fputc('\\', fp);
+ switch (c) {
+ case '\a':
+ (void) fputc('a', fp);
+ break;
+
+ case '\b':
+ (void) fputc('b', fp);
+ break;
+
+ case '\f':
+ (void) fputc('f', fp);
+ break;
+
+ case '\n':
+ (void) fputc('n', fp);
+ break;
+
+ case '\r':
+ (void) fputc('r', fp);
+ break;
+
+ case '\t':
+ (void) fputc('t', fp);
+ break;
+
+ case '\v':
+ (void) fputc('v', fp);
+ break;
+
+ default:
+ (void) fprintf(fp, "%.3o", c & 0377);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * eatsize(): Eat the size spec from a number [eg. 10UL]
+ */
+file_private void
+eatsize(const char **p)
+{
+ const char *l = *p;
+
+ if (LOWCASE(*l) == 'u')
+ l++;
+
+ switch (LOWCASE(*l)) {
+ case 'l': /* long */
+ case 's': /* short */
+ case 'h': /* short */
+ case 'b': /* char/byte */
+ case 'c': /* char/byte */
+ l++;
+ /*FALLTHROUGH*/
+ default:
+ break;
+ }
+
+ *p = l;
+}
+
+/*
+ * handle a buffer containing a compiled file.
+ */
+file_private struct magic_map *
+apprentice_buf(struct magic_set *ms, struct magic *buf, size_t len)
+{
+ struct magic_map *map;
+
+ if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) {
+ file_oomem(ms, sizeof(*map));
+ return NULL;
+ }
+ map->len = len;
+ map->p = buf;
+ map->type = MAP_TYPE_USER;
+ if (check_buffer(ms, map, "buffer") != 0) {
+ apprentice_unmap(map);
+ return NULL;
+ }
+ return map;
+}
+
+/*
+ * handle a compiled file.
+ */
+
+file_private struct magic_map *
+apprentice_map(struct magic_set *ms, const char *fn)
+{
+ int fd;
+ struct stat st;
+ char *dbname = NULL;
+ struct magic_map *map;
+ struct magic_map *rv = NULL;
+
+ fd = -1;
+ if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) {
+ file_oomem(ms, sizeof(*map));
+ goto error;
+ }
+ map->type = MAP_TYPE_USER; /* unspecified */
+
+ if (strncmp(fn, "res@", 4) == 0) {
+ map->type = MAP_TYPE_MALLOC;
+ _magic_read_res(fn + 4, &map->p, &map->len);
+ if (map->p == NULL) {
+ file_error(ms, 0, "cannot read_res %s", fn);
+ goto error;
+ }
+ if (check_buffer(ms, map, fn) != 0) {
+ goto error;
+ }
+ return map;
+ }
+
+ dbname = mkdbname(ms, fn, 0);
+ if (dbname == NULL)
+ goto error;
+
+ if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1)
+ goto error;
+
+ if (fstat(fd, &st) == -1) {
+ file_error(ms, errno, "cannot stat `%s'", dbname);
+ goto error;
+ }
+ if (st.st_size < 8 || st.st_size > maxoff_t()) {
+ file_error(ms, 0, "file `%s' is too %s", dbname,
+ st.st_size < 8 ? "small" : "large");
+ goto error;
+ }
+
+ map->len = CAST(size_t, st.st_size);
+#ifdef QUICK
+ map->type = MAP_TYPE_MMAP;
+ if ((map->p = mmap(0, CAST(size_t, st.st_size), PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_FILE, fd, CAST(off_t, 0))) == MAP_FAILED) {
+ file_error(ms, errno, "cannot map `%s'", dbname);
+ goto error;
+ }
+#else
+ map->type = MAP_TYPE_MALLOC;
+ if ((map->p = CAST(void *, malloc(map->len))) == NULL) {
+ file_oomem(ms, map->len);
+ goto error;
+ }
+ if (read(fd, map->p, map->len) != (ssize_t)map->len) {
+ file_badread(ms);
+ goto error;
+ }
+#endif
+ (void)close(fd);
+ fd = -1;
+
+ if (check_buffer(ms, map, dbname) != 0) {
+ goto error;
+ }
+#ifdef QUICK
+ if (mprotect(map->p, CAST(size_t, st.st_size), PROT_READ) == -1) {
+ file_error(ms, errno, "cannot mprotect `%s'", dbname);
+ goto error;
+ }
+#endif
+
+ free(dbname);
+ return map;
+
+error:
+ if (fd != -1)
+ (void)close(fd);
+ apprentice_unmap(map);
+ free(dbname);
+ return rv;
+}
+
+file_private int
+check_buffer(struct magic_set *ms, struct magic_map *map, const char *dbname)
+{
+ uint32_t *ptr;
+ uint32_t entries, nentries;
+ uint32_t version;
+ int i, needsbyteswap;
+
+ ptr = CAST(uint32_t *, map->p);
+ if (*ptr != MAGICNO) {
+ if (swap4(*ptr) != MAGICNO) {
+ file_error(ms, 0, "bad magic in `%s'", dbname);
+ return -1;
+ }
+ needsbyteswap = 1;
+ } else
+ needsbyteswap = 0;
+ if (needsbyteswap)
+ version = swap4(ptr[1]);
+ else
+ version = ptr[1];
+ if (version != VERSIONNO) {
+ file_error(ms, 0, "File %s supports only version %d magic "
+ "files. `%s' is version %d", VERSION,
+ VERSIONNO, dbname, version);
+ return -1;
+ }
+ entries = CAST(uint32_t, map->len / sizeof(struct magic));
+ if ((entries * sizeof(struct magic)) != map->len) {
+ file_error(ms, 0, "Size of `%s' %" SIZE_T_FORMAT "u is not "
+ "a multiple of %" SIZE_T_FORMAT "u",
+ dbname, map->len, sizeof(struct magic));
+ return -1;
+ }
+ map->magic[0] = CAST(struct magic *, map->p) + 1;
+ nentries = 0;
+ for (i = 0; i < MAGIC_SETS; i++) {
+ if (needsbyteswap)
+ map->nmagic[i] = swap4(ptr[i + 2]);
+ else
+ map->nmagic[i] = ptr[i + 2];
+ if (i != MAGIC_SETS - 1)
+ map->magic[i + 1] = map->magic[i] + map->nmagic[i];
+ nentries += map->nmagic[i];
+ }
+ if (entries != nentries + 1) {
+ file_error(ms, 0, "Inconsistent entries in `%s' %u != %u",
+ dbname, entries, nentries + 1);
+ return -1;
+ }
+ if (needsbyteswap)
+ for (i = 0; i < MAGIC_SETS; i++)
+ byteswap(map->magic[i], map->nmagic[i]);
+ return 0;
+}
+
+/*
+ * handle an mmaped file.
+ */
+file_private int
+apprentice_compile(struct magic_set *ms, struct magic_map *map, const char *fn)
+{
+ static const size_t nm = sizeof(*map->nmagic) * MAGIC_SETS;
+ static const size_t m = sizeof(**map->magic);
+ int fd = -1;
+ size_t len;
+ char *dbname;
+ int rv = -1;
+ uint32_t i;
+ union {
+ struct magic m;
+ uint32_t h[2 + MAGIC_SETS];
+ } hdr;
+
+ dbname = mkdbname(ms, fn, 1);
+
+ if (dbname == NULL)
+ goto out;
+
+ if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1)
+ {
+ file_error(ms, errno, "cannot open `%s'", dbname);
+ goto out;
+ }
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.h[0] = MAGICNO;
+ hdr.h[1] = VERSIONNO;
+ memcpy(hdr.h + 2, map->nmagic, nm);
+
+ if (write(fd, &hdr, sizeof(hdr)) != CAST(ssize_t, sizeof(hdr))) {
+ file_error(ms, errno, "error writing `%s'", dbname);
+ goto out2;
+ }
+
+ for (i = 0; i < MAGIC_SETS; i++) {
+ len = m * map->nmagic[i];
+ if (write(fd, map->magic[i], len) != CAST(ssize_t, len)) {
+ file_error(ms, errno, "error writing `%s'", dbname);
+ goto out2;
+ }
+ }
+
+ rv = 0;
+out2:
+ if (fd != -1)
+ (void)close(fd);
+out:
+ apprentice_unmap(map);
+ free(dbname);
+ return rv;
+}
+
+file_private const char ext[] = ".mgc";
+/*
+ * make a dbname
+ */
+file_private char *
+mkdbname(struct magic_set *ms, const char *fn, int strip)
+{
+ const char *p, *q;
+ char *buf;
+
+ if (strip) {
+ if ((p = strrchr(fn, '/')) != NULL)
+ fn = ++p;
+ }
+
+ for (q = fn; *q; q++)
+ continue;
+ /* Look for .mgc */
+ for (p = ext + sizeof(ext) - 1; p >= ext && q >= fn; p--, q--)
+ if (*p != *q)
+ break;
+
+ /* Did not find .mgc, restore q */
+ if (p >= ext)
+ while (*q)
+ q++;
+
+ q++;
+ /* Compatibility with old code that looked in .mime */
+ if (ms->flags & MAGIC_MIME) {
+ if (asprintf(&buf, "%.*s.mime%s", CAST(int, q - fn), fn, ext)
+ < 0)
+ return NULL;
+ if (access(buf, R_OK) != -1) {
+ ms->flags &= MAGIC_MIME_TYPE;
+ return buf;
+ }
+ free(buf);
+ }
+ if (asprintf(&buf, "%.*s%s", CAST(int, q - fn), fn, ext) < 0)
+ return NULL;
+
+ /* Compatibility with old code that looked in .mime */
+ if (strstr(fn, ".mime") != NULL)
+ ms->flags &= MAGIC_MIME_TYPE;
+ return buf;
+}
+
+/*
+ * Byteswap an mmap'ed file if needed
+ */
+file_private void
+byteswap(struct magic *magic, uint32_t nmagic)
+{
+ uint32_t i;
+ for (i = 0; i < nmagic; i++)
+ bs1(&magic[i]);
+}
+
+#if !defined(HAVE_BYTESWAP_H) && !defined(HAVE_SYS_BSWAP_H)
+/*
+ * swap a short
+ */
+file_private uint16_t
+swap2(uint16_t sv)
+{
+ uint16_t rv;
+ uint8_t *s = RCAST(uint8_t *, RCAST(void *, &sv));
+ uint8_t *d = RCAST(uint8_t *, RCAST(void *, &rv));
+ d[0] = s[1];
+ d[1] = s[0];
+ return rv;
+}
+
+/*
+ * swap an int
+ */
+file_private uint32_t
+swap4(uint32_t sv)
+{
+ uint32_t rv;
+ uint8_t *s = RCAST(uint8_t *, RCAST(void *, &sv));
+ uint8_t *d = RCAST(uint8_t *, RCAST(void *, &rv));
+ d[0] = s[3];
+ d[1] = s[2];
+ d[2] = s[1];
+ d[3] = s[0];
+ return rv;
+}
+
+/*
+ * swap a quad
+ */
+file_private uint64_t
+swap8(uint64_t sv)
+{
+ uint64_t rv;
+ uint8_t *s = RCAST(uint8_t *, RCAST(void *, &sv));
+ uint8_t *d = RCAST(uint8_t *, RCAST(void *, &rv));
+# if 0
+ d[0] = s[3];
+ d[1] = s[2];
+ d[2] = s[1];
+ d[3] = s[0];
+ d[4] = s[7];
+ d[5] = s[6];
+ d[6] = s[5];
+ d[7] = s[4];
+# else
+ d[0] = s[7];
+ d[1] = s[6];
+ d[2] = s[5];
+ d[3] = s[4];
+ d[4] = s[3];
+ d[5] = s[2];
+ d[6] = s[1];
+ d[7] = s[0];
+# endif
+ return rv;
+}
+#endif
+
+file_protected uintmax_t
+file_varint2uintmax_t(const unsigned char *us, int t, size_t *l)
+{
+ uintmax_t x = 0;
+ const unsigned char *c;
+ if (t == FILE_LEVARINT) {
+ for (c = us; *c; c++) {
+ if ((*c & 0x80) == 0)
+ break;
+ }
+ if (l)
+ *l = c - us + 1;
+ for (; c >= us; c--) {
+ x |= *c & 0x7f;
+ x <<= 7;
+ }
+ } else {
+ for (c = us; *c; c++) {
+ x |= *c & 0x7f;
+ if ((*c & 0x80) == 0)
+ break;
+ x <<= 7;
+ }
+ if (l)
+ *l = c - us + 1;
+ }
+ return x;
+}
+
+
+/*
+ * byteswap a single magic entry
+ */
+file_private void
+bs1(struct magic *m)
+{
+ m->cont_level = swap2(m->cont_level);
+ m->offset = swap4(CAST(uint32_t, m->offset));
+ m->in_offset = swap4(CAST(uint32_t, m->in_offset));
+ m->lineno = swap4(CAST(uint32_t, m->lineno));
+ if (IS_STRING(m->type)) {
+ m->str_range = swap4(m->str_range);
+ m->str_flags = swap4(m->str_flags);
+ }
+ else {
+ m->value.q = swap8(m->value.q);
+ m->num_mask = swap8(m->num_mask);
+ }
+}
+
+file_protected size_t
+file_pstring_length_size(struct magic_set *ms, const struct magic *m)
+{
+ switch (m->str_flags & PSTRING_LEN) {
+ case PSTRING_1_LE:
+ return 1;
+ case PSTRING_2_LE:
+ case PSTRING_2_BE:
+ return 2;
+ case PSTRING_4_LE:
+ case PSTRING_4_BE:
+ return 4;
+ default:
+ file_error(ms, 0, "corrupt magic file "
+ "(bad pascal string length %d)",
+ m->str_flags & PSTRING_LEN);
+ return FILE_BADSIZE;
+ }
+}
+file_protected size_t
+file_pstring_get_length(struct magic_set *ms, const struct magic *m,
+ const char *ss)
+{
+ size_t len = 0;
+ const unsigned char *s = RCAST(const unsigned char *, ss);
+ unsigned int s3, s2, s1, s0;
+
+ switch (m->str_flags & PSTRING_LEN) {
+ case PSTRING_1_LE:
+ len = *s;
+ break;
+ case PSTRING_2_LE:
+ s0 = s[0];
+ s1 = s[1];
+ len = (s1 << 8) | s0;
+ break;
+ case PSTRING_2_BE:
+ s0 = s[0];
+ s1 = s[1];
+ len = (s0 << 8) | s1;
+ break;
+ case PSTRING_4_LE:
+ s0 = s[0];
+ s1 = s[1];
+ s2 = s[2];
+ s3 = s[3];
+ len = (s3 << 24) | (s2 << 16) | (s1 << 8) | s0;
+ break;
+ case PSTRING_4_BE:
+ s0 = s[0];
+ s1 = s[1];
+ s2 = s[2];
+ s3 = s[3];
+ len = (s0 << 24) | (s1 << 16) | (s2 << 8) | s3;
+ break;
+ default:
+ file_error(ms, 0, "corrupt magic file "
+ "(bad pascal string length %d)",
+ m->str_flags & PSTRING_LEN);
+ return FILE_BADSIZE;
+ }
+
+ if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF) {
+ size_t l = file_pstring_length_size(ms, m);
+ if (l == FILE_BADSIZE)
+ return l;
+ len -= l;
+ }
+
+ return len;
+}
+
+file_protected int
+file_magicfind(struct magic_set *ms, const char *name, struct mlist *v)
+{
+ uint32_t i, j;
+ struct mlist *mlist, *ml;
+
+ mlist = ms->mlist[1];
+
+ for (ml = mlist->next; ml != mlist; ml = ml->next) {
+ struct magic *ma = ml->magic;
+ for (i = 0; i < ml->nmagic; i++) {
+ if (ma[i].type != FILE_NAME)
+ continue;
+ if (strcmp(ma[i].value.s, name) == 0) {
+ v->magic = &ma[i];
+ v->magic_rxcomp = &(ml->magic_rxcomp[i]);
+ for (j = i + 1; j < ml->nmagic; j++)
+ if (ma[j].cont_level == 0)
+ break;
+ v->nmagic = j - i;
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
diff --git a/contrib/libs/libmagic/src/apptype.c b/contrib/libs/libmagic/src/apptype.c
new file mode 100644
index 0000000000..9473627567
--- /dev/null
+++ b/contrib/libs/libmagic/src/apptype.c
@@ -0,0 +1,169 @@
+/*
+ * Adapted from: apptype.c, Written by Eberhard Mattes and put into the
+ * file_public domain
+ *
+ * Notes: 1. Qualify the filename so that DosQueryAppType does not do extraneous
+ * searches.
+ *
+ * 2. DosQueryAppType will return FAPPTYP_DOS on a file ending with ".com"
+ * (other than an OS/2 exe or Win exe with this name). Eberhard Mattes
+ * remarks Tue, 6 Apr 93: Moreover, it reports the type of the (new and very
+ * bug ridden) Win Emacs as "OS/2 executable".
+ *
+ * 3. apptype() uses the filename if given, otherwise a tmp file is created with
+ * the contents of buf. If buf is not the complete file, apptype can
+ * incorrectly identify the exe type. The "-z" option of "file" is the reason
+ * for this ugly code.
+ */
+
+/*
+ * amai: Darrel Hankerson did the changes described here.
+ *
+ * It remains to check the validity of comments (2.) since it's referred to an
+ * "old" OS/2 version.
+ *
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: apptype.c,v 1.17 2022/12/26 17:31:14 christos Exp $")
+#endif /* lint */
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __EMX__
+#include <io.h>
+#define INCL_DOSSESMGR
+#define INCL_DOSERRORS
+#define INCL_DOSFILEMGR
+#include <os2.h>
+typedef ULONG APPTYPE;
+
+file_protected int
+file_os2_apptype(struct magic_set *ms, const char *fn, const void *buf,
+ size_t nb)
+{
+ APPTYPE rc, type;
+ char path[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR],
+ fname[_MAX_FNAME], ext[_MAX_EXT];
+ char *filename;
+ FILE *fp;
+
+ if (fn)
+ filename = strdup(fn);
+ else if ((filename = tempnam("./", "tmp")) == NULL) {
+ file_error(ms, errno, "cannot create tempnam");
+ return -1;
+ }
+ /* qualify the filename to prevent extraneous searches */
+ _splitpath(filename, drive, dir, fname, ext);
+ (void)sprintf(path, "%s%s%s%s", drive,
+ (*dir == '\0') ? "./" : dir,
+ fname,
+ (*ext == '\0') ? "." : ext);
+
+ if (fn == NULL) {
+ if ((fp = fopen(path, "wb")) == NULL) {
+ file_error(ms, errno, "cannot open tmp file `%s'", path);
+ return -1;
+ }
+ if (fwrite(buf, 1, nb, fp) != nb) {
+ file_error(ms, errno, "cannot write tmp file `%s'",
+ path);
+ (void)fclose(fp);
+ return -1;
+ }
+ (void)fclose(fp);
+ }
+ rc = DosQueryAppType((unsigned char *)path, &type);
+
+ if (fn == NULL) {
+ unlink(path);
+ free(filename);
+ }
+#if 0
+ if (rc == ERROR_INVALID_EXE_SIGNATURE)
+ printf("%s: not an executable file\n", fname);
+ else if (rc == ERROR_FILE_NOT_FOUND)
+ printf("%s: not found\n", fname);
+ else if (rc == ERROR_ACCESS_DENIED)
+ printf("%s: access denied\n", fname);
+ else if (rc != 0)
+ printf("%s: error code = %lu\n", fname, rc);
+ else
+#else
+
+ /*
+ * for our purpose here it's sufficient to just ignore the error and
+ * return w/o success (=0)
+ */
+
+ if (rc)
+ return (0);
+
+#endif
+
+ if (type & FAPPTYP_32BIT)
+ if (file_printf(ms, "32-bit ") == -1)
+ return -1;
+ if (type & FAPPTYP_PHYSDRV) {
+ if (file_printf(ms, "physical device driver") == -1)
+ return -1;
+ } else if (type & FAPPTYP_VIRTDRV) {
+ if (file_printf(ms, "virtual device driver") == -1)
+ return -1;
+ } else if (type & FAPPTYP_DLL) {
+ if (type & FAPPTYP_PROTDLL)
+ if (file_printf(ms, "file_protected ") == -1)
+ return -1;
+ if (file_printf(ms, "DLL") == -1)
+ return -1;
+ } else if (type & (FAPPTYP_WINDOWSREAL | FAPPTYP_WINDOWSPROT)) {
+ if (file_printf(ms, "Windows executable") == -1)
+ return -1;
+ } else if (type & FAPPTYP_DOS) {
+ /*
+ * The API routine is partially broken on filenames ending
+ * ".com".
+ */
+ if (stricmp(ext, ".com") == 0)
+ if (strncmp((const char *)buf, "MZ", 2))
+ return (0);
+ if (file_printf(ms, "DOS executable") == -1)
+ return -1;
+ /* ---------------------------------------- */
+ /* Might learn more from the magic(4) entry */
+ if (file_printf(ms, ", magic(4)-> ") == -1)
+ return -1;
+ return (0);
+ /* ---------------------------------------- */
+ } else if (type & FAPPTYP_BOUND) {
+ if (file_printf(ms, "bound executable") == -1)
+ return -1;
+ } else if ((type & 7) == FAPPTYP_WINDOWAPI) {
+ if (file_printf(ms, "PM executable") == -1)
+ return -1;
+ } else if (file_printf(ms, "OS/2 executable") == -1)
+ return -1;
+
+ switch (type & (FAPPTYP_NOTWINDOWCOMPAT |
+ FAPPTYP_WINDOWCOMPAT |
+ FAPPTYP_WINDOWAPI)) {
+ case FAPPTYP_NOTWINDOWCOMPAT:
+ if (file_printf(ms, " [NOTWINDOWCOMPAT]") == -1)
+ return -1;
+ break;
+ case FAPPTYP_WINDOWCOMPAT:
+ if (file_printf(ms, " [WINDOWCOMPAT]") == -1)
+ return -1;
+ break;
+ case FAPPTYP_WINDOWAPI:
+ if (file_printf(ms, " [WINDOWAPI]") == -1)
+ return -1;
+ break;
+ }
+ return 1;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/ascmagic.c b/contrib/libs/libmagic/src/ascmagic.c
new file mode 100644
index 0000000000..2d61267923
--- /dev/null
+++ b/contrib/libs/libmagic/src/ascmagic.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * ASCII magic -- try to detect text encoding.
+ *
+ * Extensively modified by Eric Fischer <enf@pobox.com> in July, 2000,
+ * to handle character codes other than ASCII on a unified basis.
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: ascmagic.c,v 1.116 2023/05/21 16:08:50 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#define MAXLINELEN 300 /* longest sane line length */
+#define ISSPC(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n' \
+ || (x) == 0x85 || (x) == '\f')
+
+file_private unsigned char *encode_utf8(unsigned char *, size_t, file_unichar_t *,
+ size_t);
+file_private size_t trim_nuls(const unsigned char *, size_t);
+
+/*
+ * Undo the NUL-termination kindly provided by process()
+ * but leave at least one byte to look at
+ */
+file_private size_t
+trim_nuls(const unsigned char *buf, size_t nbytes)
+{
+ while (nbytes > 1 && buf[nbytes - 1] == '\0')
+ nbytes--;
+
+ return nbytes;
+}
+
+file_protected int
+file_ascmagic(struct magic_set *ms, const struct buffer *b, int text)
+{
+ file_unichar_t *ubuf = NULL;
+ size_t ulen = 0;
+ int rv = 1;
+ struct buffer bb;
+
+ const char *code = NULL;
+ const char *code_mime = NULL;
+ const char *type = NULL;
+
+ bb = *b;
+ bb.flen = trim_nuls(CAST(const unsigned char *, b->fbuf), b->flen);
+ /*
+ * Avoid trimming at an odd byte if the original buffer was evenly
+ * sized; this avoids losing the last character on UTF-16 LE text
+ */
+ if ((bb.flen & 1) && !(b->flen & 1))
+ bb.flen++;
+
+ /* If file doesn't look like any sort of text, give up. */
+ if (file_encoding(ms, &bb, &ubuf, &ulen, &code, &code_mime,
+ &type) == 0)
+ rv = 0;
+ else
+ rv = file_ascmagic_with_encoding(ms, &bb,
+ ubuf, ulen, code, type, text);
+
+ free(ubuf);
+
+ return rv;
+}
+
+file_protected int
+file_ascmagic_with_encoding(struct magic_set *ms, const struct buffer *b,
+ file_unichar_t *ubuf, size_t ulen, const char *code, const char *type,
+ int text)
+{
+ struct buffer bb;
+ const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
+ size_t nbytes = b->flen;
+ unsigned char *utf8_buf = NULL, *utf8_end;
+ size_t mlen, i, len;
+ int rv = -1;
+ int mime = ms->flags & MAGIC_MIME;
+ int need_separator = 0;
+
+ const char *subtype = NULL;
+
+ int has_escapes = 0;
+ int has_backspace = 0;
+ int seen_cr = 0;
+
+ size_t n_crlf = 0;
+ size_t n_lf = 0;
+ size_t n_cr = 0;
+ size_t n_nel = 0;
+ int executable = 0;
+
+ size_t last_line_end = CAST(size_t, -1);
+ size_t has_long_lines = 0;
+
+ nbytes = trim_nuls(buf, nbytes);
+
+ /* If we have fewer than 2 bytes, give up. */
+ if (nbytes <= 1) {
+ rv = 0;
+ goto done;
+ }
+
+ if (ulen > 0 && (ms->flags & MAGIC_NO_CHECK_SOFT) == 0) {
+ /* Convert ubuf to UTF-8 and try text soft magic */
+ /* malloc size is a conservative overestimate; could be
+ improved, or at least realloced after conversion. */
+ mlen = ulen * 6;
+ if ((utf8_buf = CAST(unsigned char *, malloc(mlen))) == NULL) {
+ file_oomem(ms, mlen);
+ goto done;
+ }
+ if ((utf8_end = encode_utf8(utf8_buf, mlen, ubuf, ulen))
+ == NULL) {
+ rv = 0;
+ goto done;
+ }
+ buffer_init(&bb, b->fd, &b->st, utf8_buf,
+ CAST(size_t, utf8_end - utf8_buf));
+
+ if ((rv = file_softmagic(ms, &bb, NULL, NULL,
+ TEXTTEST, text)) == 0)
+ rv = -1;
+ else
+ need_separator = 1;
+ buffer_fini(&bb);
+ if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))) {
+ rv = rv == -1 ? 0 : 1;
+ goto done;
+ }
+ }
+
+ if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))) {
+ rv = 0;
+ goto done;
+ }
+
+ /* Now try to discover other details about the file. */
+ for (i = 0; i < ulen; i++) {
+ if (ubuf[i] == '\n') {
+ if (seen_cr)
+ n_crlf++;
+ else
+ n_lf++;
+ last_line_end = i;
+ } else if (seen_cr)
+ n_cr++;
+
+ seen_cr = (ubuf[i] == '\r');
+ if (seen_cr)
+ last_line_end = i;
+
+ if (ubuf[i] == 0x85) { /* X3.64/ECMA-43 "next line" character */
+ n_nel++;
+ last_line_end = i;
+ }
+
+ /* If this line is _longer_ than MAXLINELEN, remember it. */
+ if (i > last_line_end + MAXLINELEN) {
+ size_t ll = i - last_line_end;
+ if (ll > has_long_lines)
+ has_long_lines = ll;
+ }
+
+ if (ubuf[i] == '\033')
+ has_escapes = 1;
+ if (ubuf[i] == '\b')
+ has_backspace = 1;
+ }
+
+ if (strcmp(type, "binary") == 0) {
+ rv = 0;
+ goto done;
+ }
+ len = file_printedlen(ms);
+ if (mime) {
+ if ((mime & MAGIC_MIME_TYPE) != 0) {
+ if (len) {
+ /*
+ * Softmagic printed something, we
+ * are either done, or we need a separator
+ */
+ if ((ms->flags & MAGIC_CONTINUE) == 0) {
+ rv = 1;
+ goto done;
+ }
+ if (need_separator && file_separator(ms) == -1)
+ goto done;
+ }
+ if (file_printf(ms, "text/plain") == -1)
+ goto done;
+ }
+ } else {
+ if (len) {
+ switch (file_replace(ms, " text$", ", ")) {
+ case 0:
+ switch (file_replace(ms, " text executable$",
+ ", ")) {
+ case 0:
+ if (file_printf(ms, ", ") == -1)
+ goto done;
+ break;
+ case -1:
+ goto done;
+ default:
+ executable = 1;
+ break;
+ }
+ break;
+ case -1:
+ goto done;
+ default:
+ break;
+ }
+ }
+
+ if (file_printf(ms, "%s", code) == -1)
+ goto done;
+
+ if (subtype) {
+ if (file_printf(ms, " %s", subtype) == -1)
+ goto done;
+ }
+
+ if (file_printf(ms, " %s", type) == -1)
+ goto done;
+
+ if (executable)
+ if (file_printf(ms, " executable") == -1)
+ goto done;
+
+ if (has_long_lines)
+ if (file_printf(ms, ", with very long lines (%"
+ SIZE_T_FORMAT "u)", has_long_lines) == -1)
+ goto done;
+
+ /*
+ * Only report line terminators if we find one other than LF,
+ * or if we find none at all.
+ */
+ if ((n_crlf == 0 && n_cr == 0 && n_nel == 0 && n_lf == 0) ||
+ (n_crlf != 0 || n_cr != 0 || n_nel != 0)) {
+ if (file_printf(ms, ", with") == -1)
+ goto done;
+
+ if (n_crlf == 0 && n_cr == 0 &&
+ n_nel == 0 && n_lf == 0) {
+ if (file_printf(ms, " no") == -1)
+ goto done;
+ } else {
+ if (n_crlf) {
+ if (file_printf(ms, " CRLF") == -1)
+ goto done;
+ if (n_cr || n_lf || n_nel)
+ if (file_printf(ms, ",") == -1)
+ goto done;
+ }
+ if (n_cr) {
+ if (file_printf(ms, " CR") == -1)
+ goto done;
+ if (n_lf || n_nel)
+ if (file_printf(ms, ",") == -1)
+ goto done;
+ }
+ if (n_lf) {
+ if (file_printf(ms, " LF") == -1)
+ goto done;
+ if (n_nel)
+ if (file_printf(ms, ",") == -1)
+ goto done;
+ }
+ if (n_nel)
+ if (file_printf(ms, " NEL") == -1)
+ goto done;
+ }
+
+ if (file_printf(ms, " line terminators") == -1)
+ goto done;
+ }
+
+ if (has_escapes)
+ if (file_printf(ms, ", with escape sequences") == -1)
+ goto done;
+ if (has_backspace)
+ if (file_printf(ms, ", with overstriking") == -1)
+ goto done;
+ }
+ rv = 1;
+done:
+ free(utf8_buf);
+
+ return rv;
+}
+
+/*
+ * Encode Unicode string as UTF-8, returning pointer to character
+ * after end of string, or NULL if an invalid character is found.
+ */
+file_private unsigned char *
+encode_utf8(unsigned char *buf, size_t len, file_unichar_t *ubuf, size_t ulen)
+{
+ size_t i;
+ unsigned char *end = buf + len;
+
+ for (i = 0; i < ulen; i++) {
+ if (ubuf[i] <= 0x7f) {
+ if (end - buf < 1)
+ return NULL;
+ *buf++ = CAST(unsigned char, ubuf[i]);
+ continue;
+ }
+ if (ubuf[i] <= 0x7ff) {
+ if (end - buf < 2)
+ return NULL;
+ *buf++ = CAST(unsigned char, (ubuf[i] >> 6) + 0xc0);
+ goto out1;
+ }
+ if (ubuf[i] <= 0xffff) {
+ if (end - buf < 3)
+ return NULL;
+ *buf++ = CAST(unsigned char, (ubuf[i] >> 12) + 0xe0);
+ goto out2;
+ }
+ if (ubuf[i] <= 0x1fffff) {
+ if (end - buf < 4)
+ return NULL;
+ *buf++ = CAST(unsigned char, (ubuf[i] >> 18) + 0xf0);
+ goto out3;
+ }
+ if (ubuf[i] <= 0x3ffffff) {
+ if (end - buf < 5)
+ return NULL;
+ *buf++ = CAST(unsigned char, (ubuf[i] >> 24) + 0xf8);
+ goto out4;
+ }
+ if (ubuf[i] <= 0x7fffffff) {
+ if (end - buf < 6)
+ return NULL;
+ *buf++ = CAST(unsigned char, (ubuf[i] >> 30) + 0xfc);
+ goto out5;
+ }
+ /* Invalid character */
+ return NULL;
+ out5: *buf++ = CAST(unsigned char, ((ubuf[i] >> 24) & 0x3f) + 0x80);
+ out4: *buf++ = CAST(unsigned char, ((ubuf[i] >> 18) & 0x3f) + 0x80);
+ out3: *buf++ = CAST(unsigned char, ((ubuf[i] >> 12) & 0x3f) + 0x80);
+ out2: *buf++ = CAST(unsigned char, ((ubuf[i] >> 6) & 0x3f) + 0x80);
+ out1: *buf++ = CAST(unsigned char, ((ubuf[i] >> 0) & 0x3f) + 0x80);
+ }
+
+ return buf;
+}
diff --git a/contrib/libs/libmagic/src/buffer.c b/contrib/libs/libmagic/src/buffer.c
new file mode 100644
index 0000000000..598db1471d
--- /dev/null
+++ b/contrib/libs/libmagic/src/buffer.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) Christos Zoulas 2017.
+ * All Rights Reserved.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: buffer.c,v 1.13 2023/07/02 12:48:39 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+void
+buffer_init(struct buffer *b, int fd, const struct stat *st, const void *data,
+ size_t len)
+{
+ b->fd = fd;
+ if (st)
+ memcpy(&b->st, st, sizeof(b->st));
+ else if (b->fd == -1 || fstat(b->fd, &b->st) == -1)
+ memset(&b->st, 0, sizeof(b->st));
+ b->fbuf = data;
+ b->flen = len;
+ b->eoff = 0;
+ b->ebuf = NULL;
+ b->elen = 0;
+}
+
+void
+buffer_fini(struct buffer *b)
+{
+ free(b->ebuf);
+ b->ebuf = NULL;
+ b->elen = 0;
+}
+
+int
+buffer_fill(const struct buffer *bb)
+{
+ struct buffer *b = CCAST(struct buffer *, bb);
+
+ if (b->elen != 0)
+ return b->elen == FILE_BADSIZE ? -1 : 0;
+
+ if (!S_ISREG(b->st.st_mode))
+ goto out;
+
+ b->elen = CAST(size_t, b->st.st_size) < b->flen ?
+ CAST(size_t, b->st.st_size) : b->flen;
+ if (b->elen == 0) {
+ free(b->ebuf);
+ b->ebuf = NULL;
+ return 0;
+ }
+ if ((b->ebuf = malloc(b->elen)) == NULL)
+ goto out;
+
+ b->eoff = b->st.st_size - b->elen;
+ if (pread(b->fd, b->ebuf, b->elen, b->eoff) == -1) {
+ free(b->ebuf);
+ b->ebuf = NULL;
+ goto out;
+ }
+
+ return 0;
+out:
+ b->elen = FILE_BADSIZE;
+ return -1;
+}
diff --git a/contrib/libs/libmagic/src/cdf.c b/contrib/libs/libmagic/src/cdf.c
new file mode 100644
index 0000000000..fb3cf7cb59
--- /dev/null
+++ b/contrib/libs/libmagic/src/cdf.c
@@ -0,0 +1,1676 @@
+/*-
+ * Copyright (c) 2008 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+/*
+ * Parse Composite Document Files, the format used in Microsoft Office
+ * document files before they switched to zipped XML.
+ * Info from: http://sc.openoffice.org/compdocfileformat.pdf
+ *
+ * N.B. This is the "Composite Document File" format, and not the
+ * "Compound Document Format", nor the "Channel Definition Format".
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: cdf.c,v 1.123 2022/09/24 20:30:13 christos Exp $")
+#endif
+
+#include <assert.h>
+#ifdef CDF_DEBUG
+#include <err.h>
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <limits.h>
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+#ifdef HAVE_SYS_BSWAP_H
+#error #include <sys/bswap.h>
+#endif
+
+#ifndef EFTYPE
+#define EFTYPE EINVAL
+#endif
+
+#ifndef SIZE_T_MAX
+#define SIZE_T_MAX CAST(size_t, ~0ULL)
+#endif
+
+#include "cdf.h"
+
+#ifdef CDF_DEBUG
+#define DPRINTF(a) printf a, fflush(stdout)
+#else
+#define DPRINTF(a)
+#endif
+
+static union {
+ char s[4];
+ uint32_t u;
+} cdf_bo;
+
+#define NEED_SWAP (cdf_bo.u == CAST(uint32_t, 0x01020304))
+
+#define CDF_TOLE8(x) \
+ (CAST(uint64_t, NEED_SWAP ? _cdf_tole8(x) : CAST(uint64_t, x)))
+#define CDF_TOLE4(x) \
+ (CAST(uint32_t, NEED_SWAP ? _cdf_tole4(x) : CAST(uint32_t, x)))
+#define CDF_TOLE2(x) \
+ (CAST(uint16_t, NEED_SWAP ? _cdf_tole2(x) : CAST(uint16_t, x)))
+#define CDF_TOLE(x) (/*CONSTCOND*/sizeof(x) == 2 ? \
+ CDF_TOLE2(CAST(uint16_t, x)) : \
+ (/*CONSTCOND*/sizeof(x) == 4 ? \
+ CDF_TOLE4(CAST(uint32_t, x)) : \
+ CDF_TOLE8(CAST(uint64_t, x))))
+#define CDF_GETUINT32(x, y) cdf_getuint32(x, y)
+
+#define CDF_MALLOC(n) cdf_malloc(__FILE__, __LINE__, (n))
+#define CDF_REALLOC(p, n) cdf_realloc(__FILE__, __LINE__, (p), (n))
+#define CDF_CALLOC(n, u) cdf_calloc(__FILE__, __LINE__, (n), (u))
+
+
+/*ARGSUSED*/
+static void *
+cdf_malloc(const char *file __attribute__((__unused__)),
+ size_t line __attribute__((__unused__)), size_t n)
+{
+ DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u\n",
+ file, line, __func__, n));
+ if (n == 0)
+ n++;
+ return malloc(n);
+}
+
+/*ARGSUSED*/
+static void *
+cdf_realloc(const char *file __attribute__((__unused__)),
+ size_t line __attribute__((__unused__)), void *p, size_t n)
+{
+ DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u\n",
+ file, line, __func__, n));
+ return realloc(p, n);
+}
+
+/*ARGSUSED*/
+static void *
+cdf_calloc(const char *file __attribute__((__unused__)),
+ size_t line __attribute__((__unused__)), size_t n, size_t u)
+{
+ DPRINTF(("%s,%" SIZE_T_FORMAT "u: %s %" SIZE_T_FORMAT "u %"
+ SIZE_T_FORMAT "u\n", file, line, __func__, n, u));
+ if (n == 0)
+ n++;
+ return calloc(n, u);
+}
+
+#if defined(HAVE_BYTESWAP_H)
+# define _cdf_tole2(x) bswap_16(x)
+# define _cdf_tole4(x) bswap_32(x)
+# define _cdf_tole8(x) bswap_64(x)
+#elif defined(HAVE_SYS_BSWAP_H)
+# define _cdf_tole2(x) bswap16(x)
+# define _cdf_tole4(x) bswap32(x)
+# define _cdf_tole8(x) bswap64(x)
+#else
+/*
+ * swap a short
+ */
+static uint16_t
+_cdf_tole2(uint16_t sv)
+{
+ uint16_t rv;
+ uint8_t *s = RCAST(uint8_t *, RCAST(void *, &sv));
+ uint8_t *d = RCAST(uint8_t *, RCAST(void *, &rv));
+ d[0] = s[1];
+ d[1] = s[0];
+ return rv;
+}
+
+/*
+ * swap an int
+ */
+static uint32_t
+_cdf_tole4(uint32_t sv)
+{
+ uint32_t rv;
+ uint8_t *s = RCAST(uint8_t *, RCAST(void *, &sv));
+ uint8_t *d = RCAST(uint8_t *, RCAST(void *, &rv));
+ d[0] = s[3];
+ d[1] = s[2];
+ d[2] = s[1];
+ d[3] = s[0];
+ return rv;
+}
+
+/*
+ * swap a quad
+ */
+static uint64_t
+_cdf_tole8(uint64_t sv)
+{
+ uint64_t rv;
+ uint8_t *s = RCAST(uint8_t *, RCAST(void *, &sv));
+ uint8_t *d = RCAST(uint8_t *, RCAST(void *, &rv));
+ d[0] = s[7];
+ d[1] = s[6];
+ d[2] = s[5];
+ d[3] = s[4];
+ d[4] = s[3];
+ d[5] = s[2];
+ d[6] = s[1];
+ d[7] = s[0];
+ return rv;
+}
+#endif
+
+/*
+ * grab a uint32_t from a possibly unaligned address, and return it in
+ * the native host order.
+ */
+static uint32_t
+cdf_getuint32(const uint8_t *p, size_t offs)
+{
+ uint32_t rv;
+ (void)memcpy(&rv, p + offs * sizeof(uint32_t), sizeof(rv));
+ return CDF_TOLE4(rv);
+}
+
+#define CDF_UNPACK(a) \
+ (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a)
+#define CDF_UNPACKA(a) \
+ (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a)
+
+uint16_t
+cdf_tole2(uint16_t sv)
+{
+ return CDF_TOLE2(sv);
+}
+
+uint32_t
+cdf_tole4(uint32_t sv)
+{
+ return CDF_TOLE4(sv);
+}
+
+uint64_t
+cdf_tole8(uint64_t sv)
+{
+ return CDF_TOLE8(sv);
+}
+
+void
+cdf_swap_header(cdf_header_t *h)
+{
+ size_t i;
+
+ h->h_magic = CDF_TOLE8(h->h_magic);
+ h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]);
+ h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]);
+ h->h_revision = CDF_TOLE2(h->h_revision);
+ h->h_version = CDF_TOLE2(h->h_version);
+ h->h_byte_order = CDF_TOLE2(h->h_byte_order);
+ h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2);
+ h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2);
+ h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat);
+ h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory);
+ h->h_min_size_standard_stream =
+ CDF_TOLE4(h->h_min_size_standard_stream);
+ h->h_secid_first_sector_in_short_sat =
+ CDF_TOLE4(CAST(uint32_t, h->h_secid_first_sector_in_short_sat));
+ h->h_num_sectors_in_short_sat =
+ CDF_TOLE4(h->h_num_sectors_in_short_sat);
+ h->h_secid_first_sector_in_master_sat =
+ CDF_TOLE4(CAST(uint32_t, h->h_secid_first_sector_in_master_sat));
+ h->h_num_sectors_in_master_sat =
+ CDF_TOLE4(h->h_num_sectors_in_master_sat);
+ for (i = 0; i < __arraycount(h->h_master_sat); i++) {
+ h->h_master_sat[i] =
+ CDF_TOLE4(CAST(uint32_t, h->h_master_sat[i]));
+ }
+}
+
+void
+cdf_unpack_header(cdf_header_t *h, char *buf)
+{
+ size_t i;
+ size_t len = 0;
+
+ CDF_UNPACK(h->h_magic);
+ CDF_UNPACKA(h->h_uuid);
+ CDF_UNPACK(h->h_revision);
+ CDF_UNPACK(h->h_version);
+ CDF_UNPACK(h->h_byte_order);
+ CDF_UNPACK(h->h_sec_size_p2);
+ CDF_UNPACK(h->h_short_sec_size_p2);
+ CDF_UNPACKA(h->h_unused0);
+ CDF_UNPACK(h->h_num_sectors_in_sat);
+ CDF_UNPACK(h->h_secid_first_directory);
+ CDF_UNPACKA(h->h_unused1);
+ CDF_UNPACK(h->h_min_size_standard_stream);
+ CDF_UNPACK(h->h_secid_first_sector_in_short_sat);
+ CDF_UNPACK(h->h_num_sectors_in_short_sat);
+ CDF_UNPACK(h->h_secid_first_sector_in_master_sat);
+ CDF_UNPACK(h->h_num_sectors_in_master_sat);
+ for (i = 0; i < __arraycount(h->h_master_sat); i++)
+ CDF_UNPACK(h->h_master_sat[i]);
+}
+
+void
+cdf_swap_dir(cdf_directory_t *d)
+{
+ d->d_namelen = CDF_TOLE2(d->d_namelen);
+ d->d_left_child = CDF_TOLE4(CAST(uint32_t, d->d_left_child));
+ d->d_right_child = CDF_TOLE4(CAST(uint32_t, d->d_right_child));
+ d->d_storage = CDF_TOLE4(CAST(uint32_t, d->d_storage));
+ d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]);
+ d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]);
+ d->d_flags = CDF_TOLE4(d->d_flags);
+ d->d_created = CDF_TOLE8(CAST(uint64_t, d->d_created));
+ d->d_modified = CDF_TOLE8(CAST(uint64_t, d->d_modified));
+ d->d_stream_first_sector = CDF_TOLE4(
+ CAST(uint32_t, d->d_stream_first_sector));
+ d->d_size = CDF_TOLE4(d->d_size);
+}
+
+void
+cdf_swap_class(cdf_classid_t *d)
+{
+ d->cl_dword = CDF_TOLE4(d->cl_dword);
+ d->cl_word[0] = CDF_TOLE2(d->cl_word[0]);
+ d->cl_word[1] = CDF_TOLE2(d->cl_word[1]);
+}
+
+void
+cdf_unpack_dir(cdf_directory_t *d, char *buf)
+{
+ size_t len = 0;
+
+ CDF_UNPACKA(d->d_name);
+ CDF_UNPACK(d->d_namelen);
+ CDF_UNPACK(d->d_type);
+ CDF_UNPACK(d->d_color);
+ CDF_UNPACK(d->d_left_child);
+ CDF_UNPACK(d->d_right_child);
+ CDF_UNPACK(d->d_storage);
+ CDF_UNPACKA(d->d_storage_uuid);
+ CDF_UNPACK(d->d_flags);
+ CDF_UNPACK(d->d_created);
+ CDF_UNPACK(d->d_modified);
+ CDF_UNPACK(d->d_stream_first_sector);
+ CDF_UNPACK(d->d_size);
+ CDF_UNPACK(d->d_unused0);
+}
+
+int
+cdf_zero_stream(cdf_stream_t *scn)
+{
+ scn->sst_len = 0;
+ scn->sst_dirlen = 0;
+ scn->sst_ss = 0;
+ free(scn->sst_tab);
+ scn->sst_tab = NULL;
+ return -1;
+}
+
+static size_t
+cdf_check_stream(const cdf_stream_t *sst, const cdf_header_t *h)
+{
+ size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
+ CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
+ assert(ss == sst->sst_ss);
+ return sst->sst_ss;
+}
+
+static int
+cdf_check_stream_offset(const cdf_stream_t *sst, const cdf_header_t *h,
+ const void *p, size_t tail, int line)
+{
+ const char *b = RCAST(const char *, sst->sst_tab);
+ const char *e = RCAST(const char *, p) + tail;
+ size_t ss = cdf_check_stream(sst, h);
+ /*LINTED*/(void)&line;
+ if (e >= b && CAST(size_t, e - b) <= ss * sst->sst_len)
+ return 0;
+ DPRINTF(("%d: offset begin %p < end %p || %" SIZE_T_FORMAT "u"
+ " > %" SIZE_T_FORMAT "u [%" SIZE_T_FORMAT "u %"
+ SIZE_T_FORMAT "u]\n", line, b, e, (size_t)(e - b),
+ ss * sst->sst_len, ss, sst->sst_len));
+ errno = EFTYPE;
+ return -1;
+}
+
+static ssize_t
+cdf_read(const cdf_info_t *info, off_t off, void *buf, size_t len)
+{
+ size_t siz = CAST(size_t, off + len);
+
+ if (CAST(off_t, off + len) != CAST(off_t, siz))
+ goto out;
+
+ if (info->i_buf != NULL && info->i_len >= siz) {
+ (void)memcpy(buf, &info->i_buf[off], len);
+ return CAST(ssize_t, len);
+ }
+
+ if (info->i_fd == -1)
+ goto out;
+
+ if (pread(info->i_fd, buf, len, off) != CAST(ssize_t, len))
+ return -1;
+
+ return CAST(ssize_t, len);
+out:
+ errno = EINVAL;
+ return -1;
+}
+
+int
+cdf_read_header(const cdf_info_t *info, cdf_header_t *h)
+{
+ char buf[512];
+
+ (void)memcpy(cdf_bo.s, "\01\02\03\04", 4);
+ if (cdf_read(info, CAST(off_t, 0), buf, sizeof(buf)) == -1)
+ return -1;
+ cdf_unpack_header(h, buf);
+ cdf_swap_header(h);
+ if (h->h_magic != CDF_MAGIC) {
+ DPRINTF(("Bad magic %#" INT64_T_FORMAT "x != %#"
+ INT64_T_FORMAT "x\n",
+ (unsigned long long)h->h_magic,
+ (unsigned long long)CDF_MAGIC));
+ goto out;
+ }
+ if (h->h_sec_size_p2 > 20) {
+ DPRINTF(("Bad sector size %hu\n", h->h_sec_size_p2));
+ goto out;
+ }
+ if (h->h_short_sec_size_p2 > 20) {
+ DPRINTF(("Bad short sector size %hu\n",
+ h->h_short_sec_size_p2));
+ goto out;
+ }
+ return 0;
+out:
+ errno = EFTYPE;
+ return -1;
+}
+
+
+ssize_t
+cdf_read_sector(const cdf_info_t *info, void *buf, size_t offs, size_t len,
+ const cdf_header_t *h, cdf_secid_t id)
+{
+ size_t ss = CDF_SEC_SIZE(h);
+ size_t pos;
+
+ if (SIZE_T_MAX / ss < CAST(size_t, id))
+ return -1;
+
+ pos = CDF_SEC_POS(h, id);
+ assert(ss == len);
+ return cdf_read(info, CAST(off_t, pos), RCAST(char *, buf) + offs, len);
+}
+
+ssize_t
+cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs,
+ size_t len, const cdf_header_t *h, cdf_secid_t id)
+{
+ size_t ss = CDF_SHORT_SEC_SIZE(h);
+ size_t pos;
+
+ if (SIZE_T_MAX / ss < CAST(size_t, id))
+ return -1;
+
+ pos = CDF_SHORT_SEC_POS(h, id);
+ assert(ss == len);
+ if (pos + len > CDF_SEC_SIZE(h) * sst->sst_len) {
+ DPRINTF(("Out of bounds read %" SIZE_T_FORMAT "u > %"
+ SIZE_T_FORMAT "u\n",
+ pos + len, CDF_SEC_SIZE(h) * sst->sst_len));
+ goto out;
+ }
+ (void)memcpy(RCAST(char *, buf) + offs,
+ RCAST(const char *, sst->sst_tab) + pos, len);
+ return len;
+out:
+ errno = EFTYPE;
+ return -1;
+}
+
+/*
+ * Read the sector allocation table.
+ */
+int
+cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat)
+{
+ size_t i, j, k;
+ size_t ss = CDF_SEC_SIZE(h);
+ cdf_secid_t *msa, mid, sec;
+ size_t nsatpersec = (ss / sizeof(mid)) - 1;
+
+ for (i = 0; i < __arraycount(h->h_master_sat); i++)
+ if (h->h_master_sat[i] == CDF_SECID_FREE)
+ break;
+
+#define CDF_SEC_LIMIT (UINT32_MAX / (64 * ss))
+ if ((nsatpersec > 0 &&
+ h->h_num_sectors_in_master_sat > CDF_SEC_LIMIT / nsatpersec) ||
+ i > CDF_SEC_LIMIT) {
+ DPRINTF(("Number of sectors in master SAT too big %u %"
+ SIZE_T_FORMAT "u\n", h->h_num_sectors_in_master_sat, i));
+ errno = EFTYPE;
+ return -1;
+ }
+
+ sat->sat_len = h->h_num_sectors_in_master_sat * nsatpersec + i;
+ DPRINTF(("sat_len = %" SIZE_T_FORMAT "u ss = %" SIZE_T_FORMAT "u\n",
+ sat->sat_len, ss));
+ if ((sat->sat_tab = CAST(cdf_secid_t *, CDF_CALLOC(sat->sat_len, ss)))
+ == NULL)
+ return -1;
+
+ for (i = 0; i < __arraycount(h->h_master_sat); i++) {
+ if (h->h_master_sat[i] < 0)
+ break;
+ if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
+ h->h_master_sat[i]) != CAST(ssize_t, ss)) {
+ DPRINTF(("Reading sector %d", h->h_master_sat[i]));
+ goto out1;
+ }
+ }
+
+ if ((msa = CAST(cdf_secid_t *, CDF_CALLOC(1, ss))) == NULL)
+ goto out1;
+
+ mid = h->h_secid_first_sector_in_master_sat;
+ for (j = 0; j < h->h_num_sectors_in_master_sat; j++) {
+ if (mid < 0)
+ goto out;
+ if (j >= CDF_LOOP_LIMIT) {
+ DPRINTF(("Reading master sector loop limit"));
+ goto out3;
+ }
+ if (cdf_read_sector(info, msa, 0, ss, h, mid) !=
+ CAST(ssize_t, ss)) {
+ DPRINTF(("Reading master sector %d", mid));
+ goto out2;
+ }
+ for (k = 0; k < nsatpersec; k++, i++) {
+ sec = CDF_TOLE4(CAST(uint32_t, msa[k]));
+ if (sec < 0)
+ goto out;
+ if (i >= sat->sat_len) {
+ DPRINTF(("Out of bounds reading MSA %"
+ SIZE_T_FORMAT "u >= %" SIZE_T_FORMAT "u",
+ i, sat->sat_len));
+ goto out3;
+ }
+ if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
+ sec) != CAST(ssize_t, ss)) {
+ DPRINTF(("Reading sector %d",
+ CDF_TOLE4(msa[k])));
+ goto out2;
+ }
+ }
+ mid = CDF_TOLE4(CAST(uint32_t, msa[nsatpersec]));
+ }
+out:
+ sat->sat_len = i;
+ free(msa);
+ return 0;
+out3:
+ errno = EFTYPE;
+out2:
+ free(msa);
+out1:
+ free(sat->sat_tab);
+ return -1;
+}
+
+size_t
+cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size)
+{
+ size_t i, j;
+ cdf_secid_t maxsector = CAST(cdf_secid_t, (sat->sat_len * size)
+ / sizeof(maxsector));
+
+ DPRINTF(("Chain:"));
+ if (sid == CDF_SECID_END_OF_CHAIN) {
+ /* 0-length chain. */
+ DPRINTF((" empty\n"));
+ return 0;
+ }
+
+ for (j = i = 0; sid >= 0; i++, j++) {
+ DPRINTF((" %d", sid));
+ if (j >= CDF_LOOP_LIMIT) {
+ DPRINTF(("Counting chain loop limit"));
+ goto out;
+ }
+ if (sid >= maxsector) {
+ DPRINTF(("Sector %d >= %d\n", sid, maxsector));
+ goto out;
+ }
+ sid = CDF_TOLE4(CAST(uint32_t, sat->sat_tab[sid]));
+ }
+ if (i == 0) {
+ DPRINTF((" none, sid: %d\n", sid));
+ goto out;
+
+ }
+ DPRINTF(("\n"));
+ return i;
+out:
+ errno = EFTYPE;
+ return CAST(size_t, -1);
+}
+
+int
+cdf_read_long_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, cdf_secid_t sid, size_t len, cdf_stream_t *scn)
+{
+ size_t ss = CDF_SEC_SIZE(h), i, j;
+ ssize_t nr;
+ scn->sst_tab = NULL;
+ scn->sst_len = cdf_count_chain(sat, sid, ss);
+ scn->sst_dirlen = MAX(h->h_min_size_standard_stream, len);
+ scn->sst_ss = ss;
+
+ if (sid == CDF_SECID_END_OF_CHAIN || len == 0)
+ return cdf_zero_stream(scn);
+
+ if (scn->sst_len == CAST(size_t, -1))
+ goto out;
+
+ scn->sst_tab = CDF_CALLOC(scn->sst_len, ss);
+ if (scn->sst_tab == NULL)
+ return cdf_zero_stream(scn);
+
+ for (j = i = 0; sid >= 0; i++, j++) {
+ if (j >= CDF_LOOP_LIMIT) {
+ DPRINTF(("Read long sector chain loop limit"));
+ goto out;
+ }
+ if (i >= scn->sst_len) {
+ DPRINTF(("Out of bounds reading long sector chain "
+ "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
+ scn->sst_len));
+ goto out;
+ }
+ if ((nr = cdf_read_sector(info, scn->sst_tab, i * ss, ss, h,
+ sid)) != CAST(ssize_t, ss)) {
+ if (i == scn->sst_len - 1 && nr > 0) {
+ /* Last sector might be truncated */
+ return 0;
+ }
+ DPRINTF(("Reading long sector chain %d", sid));
+ goto out;
+ }
+ sid = CDF_TOLE4(CAST(uint32_t, sat->sat_tab[sid]));
+ }
+ return 0;
+out:
+ errno = EFTYPE;
+ return cdf_zero_stream(scn);
+}
+
+int
+cdf_read_short_sector_chain(const cdf_header_t *h,
+ const cdf_sat_t *ssat, const cdf_stream_t *sst,
+ cdf_secid_t sid, size_t len, cdf_stream_t *scn)
+{
+ size_t ss = CDF_SHORT_SEC_SIZE(h), i, j;
+ scn->sst_tab = NULL;
+ scn->sst_len = cdf_count_chain(ssat, sid, CDF_SEC_SIZE(h));
+ scn->sst_dirlen = len;
+ scn->sst_ss = ss;
+
+ if (scn->sst_len == CAST(size_t, -1))
+ goto out;
+
+ scn->sst_tab = CDF_CALLOC(scn->sst_len, ss);
+ if (scn->sst_tab == NULL)
+ return cdf_zero_stream(scn);
+
+ for (j = i = 0; sid >= 0; i++, j++) {
+ if (j >= CDF_LOOP_LIMIT) {
+ DPRINTF(("Read short sector chain loop limit"));
+ goto out;
+ }
+ if (i >= scn->sst_len) {
+ DPRINTF(("Out of bounds reading short sector chain "
+ "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n",
+ i, scn->sst_len));
+ goto out;
+ }
+ if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h,
+ sid) != CAST(ssize_t, ss)) {
+ DPRINTF(("Reading short sector chain %d", sid));
+ goto out;
+ }
+ sid = CDF_TOLE4(CAST(uint32_t, ssat->sat_tab[sid]));
+ }
+ return 0;
+out:
+ errno = EFTYPE;
+ return cdf_zero_stream(scn);
+}
+
+int
+cdf_read_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
+ cdf_secid_t sid, size_t len, cdf_stream_t *scn)
+{
+
+ if (len < h->h_min_size_standard_stream && sst->sst_tab != NULL)
+ return cdf_read_short_sector_chain(h, ssat, sst, sid, len,
+ scn);
+ else
+ return cdf_read_long_sector_chain(info, h, sat, sid, len, scn);
+}
+
+int
+cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, cdf_dir_t *dir)
+{
+ size_t i, j;
+ size_t ss = CDF_SEC_SIZE(h), ns, nd;
+ char *buf;
+ cdf_secid_t sid = h->h_secid_first_directory;
+
+ ns = cdf_count_chain(sat, sid, ss);
+ if (ns == CAST(size_t, -1))
+ return -1;
+
+ nd = ss / CDF_DIRECTORY_SIZE;
+
+ dir->dir_len = ns * nd;
+ dir->dir_tab = CAST(cdf_directory_t *,
+ CDF_CALLOC(dir->dir_len, sizeof(dir->dir_tab[0])));
+ if (dir->dir_tab == NULL)
+ return -1;
+
+ if ((buf = CAST(char *, CDF_MALLOC(ss))) == NULL) {
+ free(dir->dir_tab);
+ return -1;
+ }
+
+ for (j = i = 0; i < ns; i++, j++) {
+ if (j >= CDF_LOOP_LIMIT) {
+ DPRINTF(("Read dir loop limit"));
+ goto out;
+ }
+ if (cdf_read_sector(info, buf, 0, ss, h, sid) !=
+ CAST(ssize_t, ss)) {
+ DPRINTF(("Reading directory sector %d", sid));
+ goto out;
+ }
+ for (j = 0; j < nd; j++) {
+ cdf_unpack_dir(&dir->dir_tab[i * nd + j],
+ &buf[j * CDF_DIRECTORY_SIZE]);
+ }
+ sid = CDF_TOLE4(CAST(uint32_t, sat->sat_tab[sid]));
+ }
+ if (NEED_SWAP)
+ for (i = 0; i < dir->dir_len; i++)
+ cdf_swap_dir(&dir->dir_tab[i]);
+ free(buf);
+ return 0;
+out:
+ free(dir->dir_tab);
+ free(buf);
+ errno = EFTYPE;
+ return -1;
+}
+
+
+int
+cdf_read_ssat(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, cdf_sat_t *ssat)
+{
+ size_t i, j;
+ size_t ss = CDF_SEC_SIZE(h);
+ cdf_secid_t sid = h->h_secid_first_sector_in_short_sat;
+
+ ssat->sat_tab = NULL;
+ ssat->sat_len = cdf_count_chain(sat, sid, ss);
+ if (ssat->sat_len == CAST(size_t, -1))
+ goto out;
+
+ ssat->sat_tab = CAST(cdf_secid_t *, CDF_CALLOC(ssat->sat_len, ss));
+ if (ssat->sat_tab == NULL)
+ goto out1;
+
+ for (j = i = 0; sid >= 0; i++, j++) {
+ if (j >= CDF_LOOP_LIMIT) {
+ DPRINTF(("Read short sat sector loop limit"));
+ goto out;
+ }
+ if (i >= ssat->sat_len) {
+ DPRINTF(("Out of bounds reading short sector chain "
+ "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
+ ssat->sat_len));
+ goto out;
+ }
+ if (cdf_read_sector(info, ssat->sat_tab, i * ss, ss, h, sid) !=
+ CAST(ssize_t, ss)) {
+ DPRINTF(("Reading short sat sector %d", sid));
+ goto out1;
+ }
+ sid = CDF_TOLE4(CAST(uint32_t, sat->sat_tab[sid]));
+ }
+ return 0;
+out:
+ errno = EFTYPE;
+out1:
+ free(ssat->sat_tab);
+ return -1;
+}
+
+int
+cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, const cdf_dir_t *dir, cdf_stream_t *scn,
+ const cdf_directory_t **root)
+{
+ size_t i;
+ const cdf_directory_t *d;
+
+ *root = NULL;
+ for (i = 0; i < dir->dir_len; i++)
+ if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE)
+ break;
+
+ /* If the it is not there, just fake it; some docs don't have it */
+ if (i == dir->dir_len) {
+ DPRINTF(("Cannot find root storage dir\n"));
+ goto out;
+ }
+ d = &dir->dir_tab[i];
+ *root = d;
+
+ /* If the it is not there, just fake it; some docs don't have it */
+ if (d->d_stream_first_sector < 0) {
+ DPRINTF(("No first secror in dir\n"));
+ goto out;
+ }
+
+ return cdf_read_long_sector_chain(info, h, sat,
+ d->d_stream_first_sector, d->d_size, scn);
+out:
+ scn->sst_tab = NULL;
+ (void)cdf_zero_stream(scn);
+ return 0;
+}
+
+static int
+cdf_namecmp(const char *d, const uint16_t *s, size_t l)
+{
+ for (; l--; d++, s++)
+ if (*d != CDF_TOLE2(*s))
+ return CAST(unsigned char, *d) - CDF_TOLE2(*s);
+ return 0;
+}
+
+int
+cdf_read_doc_summary_info(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
+ const cdf_dir_t *dir, cdf_stream_t *scn)
+{
+ return cdf_read_user_stream(info, h, sat, ssat, sst, dir,
+ "\05DocumentSummaryInformation", scn);
+}
+
+int
+cdf_read_summary_info(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
+ const cdf_dir_t *dir, cdf_stream_t *scn)
+{
+ return cdf_read_user_stream(info, h, sat, ssat, sst, dir,
+ "\05SummaryInformation", scn);
+}
+
+int
+cdf_read_user_stream(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
+ const cdf_dir_t *dir, const char *name, cdf_stream_t *scn)
+{
+ const cdf_directory_t *d;
+ int i = cdf_find_stream(dir, name, CDF_DIR_TYPE_USER_STREAM);
+
+ if (i <= 0) {
+ memset(scn, 0, sizeof(*scn));
+ return -1;
+ }
+
+ d = &dir->dir_tab[i - 1];
+ return cdf_read_sector_chain(info, h, sat, ssat, sst,
+ d->d_stream_first_sector, d->d_size, scn);
+}
+
+int
+cdf_find_stream(const cdf_dir_t *dir, const char *name, int type)
+{
+ size_t i, name_len = strlen(name) + 1;
+
+ for (i = dir->dir_len; i > 0; i--)
+ if (dir->dir_tab[i - 1].d_type == type &&
+ cdf_namecmp(name, dir->dir_tab[i - 1].d_name, name_len)
+ == 0)
+ break;
+ if (i > 0)
+ return CAST(int, i);
+
+ DPRINTF(("Cannot find type %d `%s'\n", type, name));
+ errno = ESRCH;
+ return 0;
+}
+
+#define CDF_SHLEN_LIMIT (UINT32_MAX / 64)
+#define CDF_PROP_LIMIT (UINT32_MAX / (64 * sizeof(cdf_property_info_t)))
+
+static const void *
+cdf_offset(const void *p, size_t l)
+{
+ return CAST(const void *, CAST(const uint8_t *, p) + l);
+}
+
+static const uint8_t *
+cdf_get_property_info_pos(const cdf_stream_t *sst, const cdf_header_t *h,
+ const uint8_t *p, const uint8_t *e, size_t i)
+{
+ size_t tail = (i << 1) + 1;
+ size_t ofs;
+
+ if (p >= e) {
+ DPRINTF(("Past end %p < %p\n", e, p));
+ return NULL;
+ }
+
+ if (cdf_check_stream_offset(sst, h, p, (tail + 1) * sizeof(uint32_t),
+ __LINE__) == -1)
+ return NULL;
+
+ ofs = CDF_GETUINT32(p, tail);
+ if (ofs < 2 * sizeof(uint32_t)) {
+ DPRINTF(("Offset too small %zu\n", ofs));
+ return NULL;
+ }
+
+ ofs -= 2 * sizeof(uint32_t);
+ if (ofs > CAST(size_t, e - p)) {
+ DPRINTF(("Offset too big %zu %td\n", ofs, e - p));
+ return NULL;
+ }
+
+ return CAST(const uint8_t *, cdf_offset(CAST(const void *, p), ofs));
+}
+
+static cdf_property_info_t *
+cdf_grow_info(cdf_property_info_t **info, size_t *maxcount, size_t incr)
+{
+ cdf_property_info_t *inp;
+ size_t newcount = *maxcount + incr;
+
+ if (newcount > CDF_PROP_LIMIT) {
+ DPRINTF(("exceeded property limit %" SIZE_T_FORMAT "u > %"
+ SIZE_T_FORMAT "u\n", newcount, CDF_PROP_LIMIT));
+ goto out;
+ }
+ inp = CAST(cdf_property_info_t *,
+ CDF_REALLOC(*info, newcount * sizeof(*inp)));
+ if (inp == NULL)
+ goto out;
+
+ *info = inp;
+ *maxcount = newcount;
+ return inp;
+out:
+ free(*info);
+ *maxcount = 0;
+ *info = NULL;
+ return NULL;
+}
+
+static int
+cdf_copy_info(cdf_property_info_t *inp, const void *p, const void *e,
+ size_t len)
+{
+ if (inp->pi_type & CDF_VECTOR)
+ return 0;
+
+ if (CAST(size_t, CAST(const char *, e) - CAST(const char *, p)) < len)
+ return 0;
+
+ (void)memcpy(&inp->pi_val, p, len);
+
+ switch (len) {
+ case 2:
+ inp->pi_u16 = CDF_TOLE2(inp->pi_u16);
+ break;
+ case 4:
+ inp->pi_u32 = CDF_TOLE4(inp->pi_u32);
+ break;
+ case 8:
+ inp->pi_u64 = CDF_TOLE8(inp->pi_u64);
+ break;
+ default:
+ abort();
+ }
+ return 1;
+}
+
+int
+cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h,
+ uint32_t offs, cdf_property_info_t **info, size_t *count, size_t *maxcount)
+{
+ const cdf_section_header_t *shp;
+ cdf_section_header_t sh;
+ const uint8_t *p, *q, *e;
+ size_t i, o4, nelements, j, slen, left;
+ cdf_property_info_t *inp;
+
+ if (offs > UINT32_MAX / 4) {
+ errno = EFTYPE;
+ goto out;
+ }
+ shp = CAST(const cdf_section_header_t *,
+ cdf_offset(sst->sst_tab, offs));
+ if (cdf_check_stream_offset(sst, h, shp, sizeof(*shp), __LINE__) == -1)
+ goto out;
+ sh.sh_len = CDF_TOLE4(shp->sh_len);
+ if (sh.sh_len > CDF_SHLEN_LIMIT) {
+ errno = EFTYPE;
+ goto out;
+ }
+
+ if (cdf_check_stream_offset(sst, h, shp, sh.sh_len, __LINE__) == -1)
+ goto out;
+
+ sh.sh_properties = CDF_TOLE4(shp->sh_properties);
+ DPRINTF(("section len: %u properties %u\n", sh.sh_len,
+ sh.sh_properties));
+ if (sh.sh_properties > CDF_PROP_LIMIT)
+ goto out;
+ inp = cdf_grow_info(info, maxcount, sh.sh_properties);
+ if (inp == NULL)
+ goto out;
+ inp += *count;
+ *count += sh.sh_properties;
+ p = CAST(const uint8_t *, cdf_offset(sst->sst_tab, offs + sizeof(sh)));
+ e = CAST(const uint8_t *, cdf_offset(shp, sh.sh_len));
+ if (p >= e || cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1)
+ goto out;
+
+ for (i = 0; i < sh.sh_properties; i++) {
+ if ((q = cdf_get_property_info_pos(sst, h, p, e, i)) == NULL)
+ goto out;
+ inp[i].pi_id = CDF_GETUINT32(p, i << 1);
+ left = CAST(size_t, e - q);
+ if (left < sizeof(uint32_t)) {
+ DPRINTF(("short info (no type)_\n"));
+ goto out;
+ }
+ inp[i].pi_type = CDF_GETUINT32(q, 0);
+ DPRINTF(("%" SIZE_T_FORMAT "u) id=%#x type=%#x offs=%#tx,%#x\n",
+ i, inp[i].pi_id, inp[i].pi_type, q - p, offs));
+ if (inp[i].pi_type & CDF_VECTOR) {
+ if (left < sizeof(uint32_t) * 2) {
+ DPRINTF(("missing CDF_VECTOR length\n"));
+ goto out;
+ }
+ nelements = CDF_GETUINT32(q, 1);
+ if (nelements > CDF_ELEMENT_LIMIT || nelements == 0) {
+ DPRINTF(("CDF_VECTOR with nelements == %"
+ SIZE_T_FORMAT "u\n", nelements));
+ goto out;
+ }
+ slen = 2;
+ } else {
+ nelements = 1;
+ slen = 1;
+ }
+ o4 = slen * sizeof(uint32_t);
+ if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED))
+ goto unknown;
+ switch (inp[i].pi_type & CDF_TYPEMASK) {
+ case CDF_NULL:
+ case CDF_EMPTY:
+ break;
+ case CDF_SIGNED16:
+ if (!cdf_copy_info(&inp[i], &q[o4], e, sizeof(int16_t)))
+ goto unknown;
+ break;
+ case CDF_SIGNED32:
+ case CDF_BOOL:
+ case CDF_UNSIGNED32:
+ case CDF_FLOAT:
+ if (!cdf_copy_info(&inp[i], &q[o4], e, sizeof(int32_t)))
+ goto unknown;
+ break;
+ case CDF_SIGNED64:
+ case CDF_UNSIGNED64:
+ case CDF_DOUBLE:
+ case CDF_FILETIME:
+ if (!cdf_copy_info(&inp[i], &q[o4], e, sizeof(int64_t)))
+ goto unknown;
+ break;
+ case CDF_LENGTH32_STRING:
+ case CDF_LENGTH32_WSTRING:
+ if (nelements > 1) {
+ size_t nelem = inp - *info;
+ inp = cdf_grow_info(info, maxcount, nelements);
+ if (inp == NULL)
+ goto out;
+ inp += nelem;
+ }
+ for (j = 0; j < nelements && i < sh.sh_properties;
+ j++, i++)
+ {
+ uint32_t l;
+
+ if (o4 + sizeof(uint32_t) > left)
+ goto out;
+
+ l = CDF_GETUINT32(q, slen);
+ o4 += sizeof(uint32_t);
+ if (o4 + l > left)
+ goto out;
+
+ inp[i].pi_str.s_len = l;
+ inp[i].pi_str.s_buf = CAST(const char *,
+ CAST(const void *, &q[o4]));
+
+ DPRINTF(("o=%" SIZE_T_FORMAT "u l=%d(%"
+ SIZE_T_FORMAT "u), t=%" SIZE_T_FORMAT
+ "u s=%.*s\n", o4, l,
+ CDF_ROUND(l, sizeof(l)),
+ left, (int)l, inp[i].pi_str.s_buf));
+
+ if (l & 1)
+ l++;
+
+ slen += l >> 1;
+ o4 = slen * sizeof(uint32_t);
+ }
+ i--;
+ break;
+ case CDF_CLIPBOARD:
+ if (inp[i].pi_type & CDF_VECTOR)
+ goto unknown;
+ break;
+ default:
+ unknown:
+ memset(&inp[i].pi_val, 0, sizeof(inp[i].pi_val));
+ DPRINTF(("Don't know how to deal with %#x\n",
+ inp[i].pi_type));
+ break;
+ }
+ }
+ return 0;
+out:
+ free(*info);
+ *info = NULL;
+ *count = 0;
+ *maxcount = 0;
+ errno = EFTYPE;
+ return -1;
+}
+
+int
+cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h,
+ cdf_summary_info_header_t *ssi, cdf_property_info_t **info, size_t *count)
+{
+ size_t maxcount;
+ const cdf_summary_info_header_t *si =
+ CAST(const cdf_summary_info_header_t *, sst->sst_tab);
+ const cdf_section_declaration_t *sd =
+ CAST(const cdf_section_declaration_t *, RCAST(const void *,
+ RCAST(const char *, sst->sst_tab)
+ + CDF_SECTION_DECLARATION_OFFSET));
+
+ if (cdf_check_stream_offset(sst, h, si, sizeof(*si), __LINE__) == -1 ||
+ cdf_check_stream_offset(sst, h, sd, sizeof(*sd), __LINE__) == -1)
+ return -1;
+ ssi->si_byte_order = CDF_TOLE2(si->si_byte_order);
+ ssi->si_os_version = CDF_TOLE2(si->si_os_version);
+ ssi->si_os = CDF_TOLE2(si->si_os);
+ ssi->si_class = si->si_class;
+ cdf_swap_class(&ssi->si_class);
+ ssi->si_count = CDF_TOLE4(si->si_count);
+ *count = 0;
+ maxcount = 0;
+ *info = NULL;
+ if (cdf_read_property_info(sst, h, CDF_TOLE4(sd->sd_offset), info,
+ count, &maxcount) == -1)
+ return -1;
+ return 0;
+}
+
+
+#define extract_catalog_field(t, f, l) \
+ if (b + l + sizeof(cep->f) > eb) { \
+ cep->ce_namlen = 0; \
+ break; \
+ } \
+ memcpy(&cep->f, b + (l), sizeof(cep->f)); \
+ ce[i].f = CAST(t, CDF_TOLE(cep->f))
+
+int
+cdf_unpack_catalog(const cdf_header_t *h, const cdf_stream_t *sst,
+ cdf_catalog_t **cat)
+{
+ size_t ss = cdf_check_stream(sst, h);
+ const char *b = CAST(const char *, sst->sst_tab);
+ const char *nb, *eb = b + ss * sst->sst_len;
+ size_t nr, i, j, k;
+ cdf_catalog_entry_t *ce;
+ uint16_t reclen;
+ const uint16_t *np;
+
+ for (nr = 0;; nr++) {
+ memcpy(&reclen, b, sizeof(reclen));
+ reclen = CDF_TOLE2(reclen);
+ if (reclen == 0)
+ break;
+ b += reclen;
+ if (b > eb)
+ break;
+ }
+ if (nr == 0)
+ return -1;
+ nr--;
+ *cat = CAST(cdf_catalog_t *,
+ CDF_MALLOC(sizeof(cdf_catalog_t) + nr * sizeof(*ce)));
+ if (*cat == NULL)
+ return -1;
+ ce = (*cat)->cat_e;
+ memset(ce, 0, nr * sizeof(*ce));
+ b = CAST(const char *, sst->sst_tab);
+ for (j = i = 0; i < nr; b += reclen) {
+ cdf_catalog_entry_t *cep = &ce[j];
+ uint16_t rlen;
+
+ extract_catalog_field(uint16_t, ce_namlen, 0);
+ extract_catalog_field(uint16_t, ce_num, 4);
+ extract_catalog_field(uint64_t, ce_timestamp, 8);
+ reclen = cep->ce_namlen;
+
+ if (reclen < 14) {
+ cep->ce_namlen = 0;
+ continue;
+ }
+
+ cep->ce_namlen = __arraycount(cep->ce_name) - 1;
+ rlen = reclen - 14;
+ if (cep->ce_namlen > rlen)
+ cep->ce_namlen = rlen;
+
+ np = CAST(const uint16_t *, CAST(const void *, (b + 16)));
+ nb = CAST(const char *, CAST(const void *,
+ (np + cep->ce_namlen)));
+ if (nb > eb) {
+ cep->ce_namlen = 0;
+ break;
+ }
+
+ for (k = 0; k < cep->ce_namlen; k++)
+ cep->ce_name[k] = np[k]; /* XXX: CDF_TOLE2? */
+ cep->ce_name[cep->ce_namlen] = 0;
+ j = i;
+ i++;
+ }
+ (*cat)->cat_num = j;
+ return 0;
+}
+
+int
+cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id)
+{
+ return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-"
+ "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0],
+ id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0],
+ id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4],
+ id->cl_six[5]);
+}
+
+static const struct {
+ uint32_t v;
+ const char *n;
+} vn[] = {
+ { CDF_PROPERTY_CODE_PAGE, "Code page" },
+ { CDF_PROPERTY_TITLE, "Title" },
+ { CDF_PROPERTY_SUBJECT, "Subject" },
+ { CDF_PROPERTY_AUTHOR, "Author" },
+ { CDF_PROPERTY_KEYWORDS, "Keywords" },
+ { CDF_PROPERTY_COMMENTS, "Comments" },
+ { CDF_PROPERTY_TEMPLATE, "Template" },
+ { CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" },
+ { CDF_PROPERTY_REVISION_NUMBER, "Revision Number" },
+ { CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" },
+ { CDF_PROPERTY_LAST_PRINTED, "Last Printed" },
+ { CDF_PROPERTY_CREATE_TIME, "Create Time/Date" },
+ { CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" },
+ { CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" },
+ { CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" },
+ { CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" },
+ { CDF_PROPERTY_THUMBNAIL, "Thumbnail" },
+ { CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" },
+ { CDF_PROPERTY_SECURITY, "Security" },
+ { CDF_PROPERTY_LOCALE_ID, "Locale ID" },
+};
+
+int
+cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(vn); i++)
+ if (vn[i].v == p)
+ return snprintf(buf, bufsiz, "%s", vn[i].n);
+ return snprintf(buf, bufsiz, "%#x", p);
+}
+
+int
+cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts)
+{
+ int len = 0;
+ int days, hours, mins, secs;
+
+ ts /= CDF_TIME_PREC;
+ secs = CAST(int, ts % 60);
+ ts /= 60;
+ mins = CAST(int, ts % 60);
+ ts /= 60;
+ hours = CAST(int, ts % 24);
+ ts /= 24;
+ days = CAST(int, ts);
+
+ if (days) {
+ len += snprintf(buf + len, bufsiz - len, "%dd+", days);
+ if (CAST(size_t, len) >= bufsiz)
+ return len;
+ }
+
+ if (days || hours) {
+ len += snprintf(buf + len, bufsiz - len, "%.2d:", hours);
+ if (CAST(size_t, len) >= bufsiz)
+ return len;
+ }
+
+ len += snprintf(buf + len, bufsiz - len, "%.2d:", mins);
+ if (CAST(size_t, len) >= bufsiz)
+ return len;
+
+ len += snprintf(buf + len, bufsiz - len, "%.2d", secs);
+ return len;
+}
+
+char *
+cdf_u16tos8(char *buf, size_t len, const uint16_t *p)
+{
+ size_t i;
+ for (i = 0; i < len && p[i]; i++)
+ buf[i] = CAST(char, p[i]);
+ buf[i] = '\0';
+ return buf;
+}
+
+#ifdef CDF_DEBUG
+void
+cdf_dump_header(const cdf_header_t *h)
+{
+ size_t i;
+
+#define DUMP(a, b) (void)fprintf(stderr, "%40.40s = " a "\n", # b, h->h_ ## b)
+#define DUMP2(a, b) (void)fprintf(stderr, "%40.40s = " a " (" a ")\n", # b, \
+ h->h_ ## b, 1 << h->h_ ## b)
+ DUMP("%d", revision);
+ DUMP("%d", version);
+ DUMP("%#x", byte_order);
+ DUMP2("%d", sec_size_p2);
+ DUMP2("%d", short_sec_size_p2);
+ DUMP("%d", num_sectors_in_sat);
+ DUMP("%d", secid_first_directory);
+ DUMP("%d", min_size_standard_stream);
+ DUMP("%d", secid_first_sector_in_short_sat);
+ DUMP("%d", num_sectors_in_short_sat);
+ DUMP("%d", secid_first_sector_in_master_sat);
+ DUMP("%d", num_sectors_in_master_sat);
+ for (i = 0; i < __arraycount(h->h_master_sat); i++) {
+ if (h->h_master_sat[i] == CDF_SECID_FREE)
+ break;
+ (void)fprintf(stderr, "%35.35s[%.3" SIZE_T_FORMAT "u] = %d\n",
+ "master_sat", i, h->h_master_sat[i]);
+ }
+}
+
+void
+cdf_dump_sat(const char *prefix, const cdf_sat_t *sat, size_t size)
+{
+ size_t i, j, s = size / sizeof(cdf_secid_t);
+
+ for (i = 0; i < sat->sat_len; i++) {
+ (void)fprintf(stderr, "%s[%" SIZE_T_FORMAT "u]:\n%.6"
+ SIZE_T_FORMAT "u: ", prefix, i, i * s);
+ for (j = 0; j < s; j++) {
+ (void)fprintf(stderr, "%5d, ",
+ CDF_TOLE4(sat->sat_tab[s * i + j]));
+ if ((j + 1) % 10 == 0)
+ (void)fprintf(stderr, "\n%.6" SIZE_T_FORMAT
+ "u: ", i * s + j + 1);
+ }
+ (void)fprintf(stderr, "\n");
+ }
+}
+
+void
+cdf_dump(const void *v, size_t len)
+{
+ size_t i, j;
+ const unsigned char *p = v;
+ char abuf[16];
+
+ (void)fprintf(stderr, "%.4x: ", 0);
+ for (i = 0, j = 0; i < len; i++, p++) {
+ (void)fprintf(stderr, "%.2x ", *p);
+ abuf[j++] = isprint(*p) ? *p : '.';
+ if (j == 16) {
+ j = 0;
+ abuf[15] = '\0';
+ (void)fprintf(stderr, "%s\n%.4" SIZE_T_FORMAT "x: ",
+ abuf, i + 1);
+ }
+ }
+ (void)fprintf(stderr, "\n");
+}
+
+void
+cdf_dump_stream(const cdf_stream_t *sst)
+{
+ size_t ss = sst->sst_ss;
+ cdf_dump(sst->sst_tab, ss * sst->sst_len);
+}
+
+void
+cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h,
+ const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
+ const cdf_dir_t *dir)
+{
+ size_t i, j;
+ cdf_directory_t *d;
+ char name[__arraycount(d->d_name)];
+ cdf_stream_t scn;
+ struct timespec ts;
+
+ static const char *types[] = { "empty", "user storage",
+ "user stream", "lockbytes", "property", "root storage" };
+
+ for (i = 0; i < dir->dir_len; i++) {
+ char buf[26];
+ d = &dir->dir_tab[i];
+ for (j = 0; j < sizeof(name); j++)
+ name[j] = (char)CDF_TOLE2(d->d_name[j]);
+ (void)fprintf(stderr, "Directory %" SIZE_T_FORMAT "u: %s\n",
+ i, name);
+ if (d->d_type < __arraycount(types))
+ (void)fprintf(stderr, "Type: %s\n", types[d->d_type]);
+ else
+ (void)fprintf(stderr, "Type: %d\n", d->d_type);
+ (void)fprintf(stderr, "Color: %s\n",
+ d->d_color ? "black" : "red");
+ (void)fprintf(stderr, "Left child: %d\n", d->d_left_child);
+ (void)fprintf(stderr, "Right child: %d\n", d->d_right_child);
+ (void)fprintf(stderr, "Flags: %#x\n", d->d_flags);
+ cdf_timestamp_to_timespec(&ts, d->d_created);
+ (void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec, buf));
+ cdf_timestamp_to_timespec(&ts, d->d_modified);
+ (void)fprintf(stderr, "Modified %s",
+ cdf_ctime(&ts.tv_sec, buf));
+ (void)fprintf(stderr, "Stream %d\n", d->d_stream_first_sector);
+ (void)fprintf(stderr, "Size %d\n", d->d_size);
+ switch (d->d_type) {
+ case CDF_DIR_TYPE_USER_STORAGE:
+ (void)fprintf(stderr, "Storage: %d\n", d->d_storage);
+ break;
+ case CDF_DIR_TYPE_USER_STREAM:
+ if (sst == NULL)
+ break;
+ if (cdf_read_sector_chain(info, h, sat, ssat, sst,
+ d->d_stream_first_sector, d->d_size, &scn) == -1) {
+ warn("Can't read stream for %s at %d len %d",
+ name, d->d_stream_first_sector, d->d_size);
+ break;
+ }
+ cdf_dump_stream(&scn);
+ free(scn.sst_tab);
+ break;
+ default:
+ break;
+ }
+
+ }
+}
+
+void
+cdf_dump_property_info(const cdf_property_info_t *info, size_t count)
+{
+ cdf_timestamp_t tp;
+ struct timespec ts;
+ char buf[64];
+ size_t i, j;
+
+ for (i = 0; i < count; i++) {
+ cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
+ (void)fprintf(stderr, "%" SIZE_T_FORMAT "u) %s: ", i, buf);
+ switch (info[i].pi_type) {
+ case CDF_NULL:
+ break;
+ case CDF_SIGNED16:
+ (void)fprintf(stderr, "signed 16 [%hd]\n",
+ info[i].pi_s16);
+ break;
+ case CDF_SIGNED32:
+ (void)fprintf(stderr, "signed 32 [%d]\n",
+ info[i].pi_s32);
+ break;
+ case CDF_UNSIGNED32:
+ (void)fprintf(stderr, "unsigned 32 [%u]\n",
+ info[i].pi_u32);
+ break;
+ case CDF_FLOAT:
+ (void)fprintf(stderr, "float [%g]\n",
+ info[i].pi_f);
+ break;
+ case CDF_DOUBLE:
+ (void)fprintf(stderr, "double [%g]\n",
+ info[i].pi_d);
+ break;
+ case CDF_LENGTH32_STRING:
+ (void)fprintf(stderr, "string %u [%.*s]\n",
+ info[i].pi_str.s_len,
+ info[i].pi_str.s_len, info[i].pi_str.s_buf);
+ break;
+ case CDF_LENGTH32_WSTRING:
+ (void)fprintf(stderr, "string %u [",
+ info[i].pi_str.s_len);
+ for (j = 0; j < info[i].pi_str.s_len - 1; j++)
+ (void)fputc(info[i].pi_str.s_buf[j << 1], stderr);
+ (void)fprintf(stderr, "]\n");
+ break;
+ case CDF_FILETIME:
+ tp = info[i].pi_tp;
+ if (tp < 1000000000000000LL) {
+ cdf_print_elapsed_time(buf, sizeof(buf), tp);
+ (void)fprintf(stderr, "timestamp %s\n", buf);
+ } else {
+ char tbuf[26];
+ cdf_timestamp_to_timespec(&ts, tp);
+ (void)fprintf(stderr, "timestamp %s",
+ cdf_ctime(&ts.tv_sec, tbuf));
+ }
+ break;
+ case CDF_CLIPBOARD:
+ (void)fprintf(stderr, "CLIPBOARD %u\n", info[i].pi_u32);
+ break;
+ default:
+ DPRINTF(("Don't know how to deal with %#x\n",
+ info[i].pi_type));
+ break;
+ }
+ }
+}
+
+
+void
+cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst)
+{
+ char buf[128];
+ cdf_summary_info_header_t ssi;
+ cdf_property_info_t *info;
+ size_t count;
+
+ (void)&h;
+ if (cdf_unpack_summary_info(sst, h, &ssi, &info, &count) == -1)
+ return;
+ (void)fprintf(stderr, "Endian: %#x\n", ssi.si_byte_order);
+ (void)fprintf(stderr, "Os Version %d.%d\n", ssi.si_os_version & 0xff,
+ ssi.si_os_version >> 8);
+ (void)fprintf(stderr, "Os %d\n", ssi.si_os);
+ cdf_print_classid(buf, sizeof(buf), &ssi.si_class);
+ (void)fprintf(stderr, "Class %s\n", buf);
+ (void)fprintf(stderr, "Count %d\n", ssi.si_count);
+ cdf_dump_property_info(info, count);
+ free(info);
+}
+
+
+void
+cdf_dump_catalog(const cdf_header_t *h, const cdf_stream_t *sst)
+{
+ cdf_catalog_t *cat;
+ cdf_unpack_catalog(h, sst, &cat);
+ const cdf_catalog_entry_t *ce = cat->cat_e;
+ struct timespec ts;
+ char tbuf[64], sbuf[256];
+ size_t i;
+
+ printf("Catalog:\n");
+ for (i = 0; i < cat->cat_num; i++) {
+ cdf_timestamp_to_timespec(&ts, ce[i].ce_timestamp);
+ printf("\t%d %s %s", ce[i].ce_num,
+ cdf_u16tos8(sbuf, ce[i].ce_namlen, ce[i].ce_name),
+ cdf_ctime(&ts.tv_sec, tbuf));
+ }
+ free(cat);
+}
+
+#endif
+
+#ifdef TEST
+int
+main(int argc, char *argv[])
+{
+ int i;
+ cdf_header_t h;
+ cdf_sat_t sat, ssat;
+ cdf_stream_t sst, scn;
+ cdf_dir_t dir;
+ cdf_info_t info;
+ const cdf_directory_t *root;
+#ifdef __linux__
+#define getprogname() __progname
+ extern char *__progname;
+#endif
+ if (argc < 2) {
+ (void)fprintf(stderr, "Usage: %s <filename>\n", getprogname());
+ return -1;
+ }
+
+ info.i_buf = NULL;
+ info.i_len = 0;
+ for (i = 1; i < argc; i++) {
+ if ((info.i_fd = open(argv[1], O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "Cannot open `%s'", argv[1]);
+
+ if (cdf_read_header(&info, &h) == -1)
+ err(EXIT_FAILURE, "Cannot read header");
+#ifdef CDF_DEBUG
+ cdf_dump_header(&h);
+#endif
+
+ if (cdf_read_sat(&info, &h, &sat) == -1)
+ err(EXIT_FAILURE, "Cannot read sat");
+#ifdef CDF_DEBUG
+ cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
+#endif
+
+ if (cdf_read_ssat(&info, &h, &sat, &ssat) == -1)
+ err(EXIT_FAILURE, "Cannot read ssat");
+#ifdef CDF_DEBUG
+ cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
+#endif
+
+ if (cdf_read_dir(&info, &h, &sat, &dir) == -1)
+ err(EXIT_FAILURE, "Cannot read dir");
+
+ if (cdf_read_short_stream(&info, &h, &sat, &dir, &sst, &root)
+ == -1)
+ err(EXIT_FAILURE, "Cannot read short stream");
+#ifdef CDF_DEBUG
+ cdf_dump_stream(&sst);
+#endif
+
+#ifdef CDF_DEBUG
+ cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
+#endif
+
+
+ if (cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
+ &scn) == -1)
+ warn("Cannot read summary info");
+#ifdef CDF_DEBUG
+ else
+ cdf_dump_summary_info(&h, &scn);
+#endif
+ if (cdf_read_user_stream(&info, &h, &sat, &ssat, &sst,
+ &dir, "Catalog", &scn) == -1)
+ warn("Cannot read catalog");
+#ifdef CDF_DEBUG
+ else
+ cdf_dump_catalog(&h, &scn);
+#endif
+
+ (void)close(info.i_fd);
+ }
+
+ return 0;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/cdf.h b/contrib/libs/libmagic/src/cdf.h
new file mode 100644
index 0000000000..05056668fb
--- /dev/null
+++ b/contrib/libs/libmagic/src/cdf.h
@@ -0,0 +1,353 @@
+/*-
+ * Copyright (c) 2008 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+/*
+ * Parse Composite Document Files, the format used in Microsoft Office
+ * document files before they switched to zipped XML.
+ * Info from: http://sc.openoffice.org/compdocfileformat.pdf
+ *
+ * N.B. This is the "Composite Document File" format, and not the
+ * "Compound Document Format", nor the "Channel Definition Format".
+ */
+
+#ifndef _H_CDF_
+#define _H_CDF_
+
+#ifdef WIN32
+#include <winsock2.h>
+#define timespec timeval
+#define tv_nsec tv_usec
+#endif
+#ifdef __DJGPP__
+#define timespec timeval
+#define tv_nsec tv_usec
+#endif
+
+typedef int32_t cdf_secid_t;
+
+#define CDF_LOOP_LIMIT 10000
+#define CDF_ELEMENT_LIMIT 100000
+
+#define CDF_SECID_NULL 0
+#define CDF_SECID_FREE -1
+#define CDF_SECID_END_OF_CHAIN -2
+#define CDF_SECID_SECTOR_ALLOCATION_TABLE -3
+#define CDF_SECID_MASTER_SECTOR_ALLOCATION_TABLE -4
+
+typedef struct {
+ uint64_t h_magic;
+#define CDF_MAGIC 0xE11AB1A1E011CFD0LL
+ uint64_t h_uuid[2];
+ uint16_t h_revision;
+ uint16_t h_version;
+ uint16_t h_byte_order;
+ uint16_t h_sec_size_p2;
+ uint16_t h_short_sec_size_p2;
+ uint8_t h_unused0[10];
+ uint32_t h_num_sectors_in_sat;
+ uint32_t h_secid_first_directory;
+ uint8_t h_unused1[4];
+ uint32_t h_min_size_standard_stream;
+ cdf_secid_t h_secid_first_sector_in_short_sat;
+ uint32_t h_num_sectors_in_short_sat;
+ cdf_secid_t h_secid_first_sector_in_master_sat;
+ uint32_t h_num_sectors_in_master_sat;
+ cdf_secid_t h_master_sat[436/4];
+} cdf_header_t;
+
+#define CDF_SEC_SIZE(h) CAST(size_t, 1 << (h)->h_sec_size_p2)
+#define CDF_SEC_POS(h, secid) (CDF_SEC_SIZE(h) + (secid) * CDF_SEC_SIZE(h))
+#define CDF_SHORT_SEC_SIZE(h) CAST(size_t, 1 << (h)->h_short_sec_size_p2)
+#define CDF_SHORT_SEC_POS(h, secid) ((secid) * CDF_SHORT_SEC_SIZE(h))
+
+typedef int32_t cdf_dirid_t;
+#define CDF_DIRID_NULL -1
+
+typedef int64_t cdf_timestamp_t;
+#define CDF_BASE_YEAR 1601
+#define CDF_TIME_PREC 10000000
+
+typedef struct {
+ uint16_t d_name[32];
+ uint16_t d_namelen;
+ uint8_t d_type;
+#define CDF_DIR_TYPE_EMPTY 0
+#define CDF_DIR_TYPE_USER_STORAGE 1
+#define CDF_DIR_TYPE_USER_STREAM 2
+#define CDF_DIR_TYPE_LOCKBYTES 3
+#define CDF_DIR_TYPE_PROPERTY 4
+#define CDF_DIR_TYPE_ROOT_STORAGE 5
+ uint8_t d_color;
+#define CDF_DIR_COLOR_READ 0
+#define CDF_DIR_COLOR_BLACK 1
+ cdf_dirid_t d_left_child;
+ cdf_dirid_t d_right_child;
+ cdf_dirid_t d_storage;
+ uint64_t d_storage_uuid[2];
+ uint32_t d_flags;
+ cdf_timestamp_t d_created;
+ cdf_timestamp_t d_modified;
+ cdf_secid_t d_stream_first_sector;
+ uint32_t d_size;
+ uint32_t d_unused0;
+} cdf_directory_t;
+
+#define CDF_DIRECTORY_SIZE 128
+
+typedef struct {
+ cdf_secid_t *sat_tab;
+ size_t sat_len;
+} cdf_sat_t;
+
+typedef struct {
+ cdf_directory_t *dir_tab;
+ size_t dir_len;
+} cdf_dir_t;
+
+typedef struct {
+ void *sst_tab;
+ size_t sst_len; /* Number of sectors */
+ size_t sst_dirlen; /* Directory sector size */
+ size_t sst_ss; /* Sector size */
+} cdf_stream_t;
+
+typedef struct {
+ uint32_t cl_dword;
+ uint16_t cl_word[2];
+ uint8_t cl_two[2];
+ uint8_t cl_six[6];
+} cdf_classid_t;
+
+typedef struct {
+ uint16_t si_byte_order;
+ uint16_t si_zero;
+ uint16_t si_os_version;
+ uint16_t si_os;
+ cdf_classid_t si_class;
+ uint32_t si_count;
+} cdf_summary_info_header_t;
+
+#define CDF_SECTION_DECLARATION_OFFSET 0x1c
+
+typedef struct {
+ cdf_classid_t sd_class;
+ uint32_t sd_offset;
+} cdf_section_declaration_t;
+
+typedef struct {
+ uint32_t sh_len;
+ uint32_t sh_properties;
+} cdf_section_header_t;
+
+typedef struct {
+ uint32_t pi_id;
+ uint32_t pi_type;
+ union {
+ uint16_t _pi_u16;
+ int16_t _pi_s16;
+ uint32_t _pi_u32;
+ int32_t _pi_s32;
+ uint64_t _pi_u64;
+ int64_t _pi_s64;
+ cdf_timestamp_t _pi_tp;
+ float _pi_f;
+ double _pi_d;
+ struct {
+ uint32_t s_len;
+ const char *s_buf;
+ } _pi_str;
+ } pi_val;
+#define pi_u64 pi_val._pi_u64
+#define pi_s64 pi_val._pi_s64
+#define pi_u32 pi_val._pi_u32
+#define pi_s32 pi_val._pi_s32
+#define pi_u16 pi_val._pi_u16
+#define pi_s16 pi_val._pi_s16
+#define pi_f pi_val._pi_f
+#define pi_d pi_val._pi_d
+#define pi_tp pi_val._pi_tp
+#define pi_str pi_val._pi_str
+} cdf_property_info_t;
+
+#define CDF_ROUND(val, by) (((val) + (by) - 1) & ~((by) - 1))
+
+/* Variant type definitions */
+#define CDF_EMPTY 0x00000000
+#define CDF_NULL 0x00000001
+#define CDF_SIGNED16 0x00000002
+#define CDF_SIGNED32 0x00000003
+#define CDF_FLOAT 0x00000004
+#define CDF_DOUBLE 0x00000005
+#define CDF_CY 0x00000006
+#define CDF_DATE 0x00000007
+#define CDF_BSTR 0x00000008
+#define CDF_DISPATCH 0x00000009
+#define CDF_ERROR 0x0000000a
+#define CDF_BOOL 0x0000000b
+#define CDF_VARIANT 0x0000000c
+#define CDF_UNKNOWN 0x0000000d
+#define CDF_DECIMAL 0x0000000e
+#define CDF_SIGNED8 0x00000010
+#define CDF_UNSIGNED8 0x00000011
+#define CDF_UNSIGNED16 0x00000012
+#define CDF_UNSIGNED32 0x00000013
+#define CDF_SIGNED64 0x00000014
+#define CDF_UNSIGNED64 0x00000015
+#define CDF_INT 0x00000016
+#define CDF_UINT 0x00000017
+#define CDF_VOID 0x00000018
+#define CDF_HRESULT 0x00000019
+#define CDF_PTR 0x0000001a
+#define CDF_SAFEARRAY 0x0000001b
+#define CDF_CARRAY 0x0000001c
+#define CDF_USERDEFINED 0x0000001d
+#define CDF_LENGTH32_STRING 0x0000001e
+#define CDF_LENGTH32_WSTRING 0x0000001f
+#define CDF_FILETIME 0x00000040
+#define CDF_BLOB 0x00000041
+#define CDF_STREAM 0x00000042
+#define CDF_STORAGE 0x00000043
+#define CDF_STREAMED_OBJECT 0x00000044
+#define CDF_STORED_OBJECT 0x00000045
+#define CDF_BLOB_OBJECT 0x00000046
+#define CDF_CLIPBOARD 0x00000047
+#define CDF_CLSID 0x00000048
+#define CDF_VECTOR 0x00001000
+#define CDF_ARRAY 0x00002000
+#define CDF_BYREF 0x00004000
+#define CDF_RESERVED 0x00008000
+#define CDF_ILLEGAL 0x0000ffff
+#define CDF_ILLEGALMASKED 0x00000fff
+#define CDF_TYPEMASK 0x00000fff
+
+#define CDF_PROPERTY_CODE_PAGE 0x00000001
+#define CDF_PROPERTY_TITLE 0x00000002
+#define CDF_PROPERTY_SUBJECT 0x00000003
+#define CDF_PROPERTY_AUTHOR 0x00000004
+#define CDF_PROPERTY_KEYWORDS 0x00000005
+#define CDF_PROPERTY_COMMENTS 0x00000006
+#define CDF_PROPERTY_TEMPLATE 0x00000007
+#define CDF_PROPERTY_LAST_SAVED_BY 0x00000008
+#define CDF_PROPERTY_REVISION_NUMBER 0x00000009
+#define CDF_PROPERTY_TOTAL_EDITING_TIME 0x0000000a
+#define CDF_PROPERTY_LAST_PRINTED 0X0000000b
+#define CDF_PROPERTY_CREATE_TIME 0x0000000c
+#define CDF_PROPERTY_LAST_SAVED_TIME 0x0000000d
+#define CDF_PROPERTY_NUMBER_OF_PAGES 0x0000000e
+#define CDF_PROPERTY_NUMBER_OF_WORDS 0x0000000f
+#define CDF_PROPERTY_NUMBER_OF_CHARACTERS 0x00000010
+#define CDF_PROPERTY_THUMBNAIL 0x00000011
+#define CDF_PROPERTY_NAME_OF_APPLICATION 0x00000012
+#define CDF_PROPERTY_SECURITY 0x00000013
+#define CDF_PROPERTY_LOCALE_ID 0x80000000
+
+typedef struct {
+ int i_fd;
+ const unsigned char *i_buf;
+ size_t i_len;
+} cdf_info_t;
+
+
+typedef struct {
+ uint16_t ce_namlen;
+ uint32_t ce_num;
+ uint64_t ce_timestamp;
+ uint16_t ce_name[256];
+} cdf_catalog_entry_t;
+
+typedef struct {
+ size_t cat_num;
+ cdf_catalog_entry_t cat_e[1];
+} cdf_catalog_t;
+
+struct timespec;
+int cdf_timestamp_to_timespec(struct timespec *, cdf_timestamp_t);
+int cdf_timespec_to_timestamp(cdf_timestamp_t *, const struct timespec *);
+int cdf_read_header(const cdf_info_t *, cdf_header_t *);
+void cdf_swap_header(cdf_header_t *);
+void cdf_unpack_header(cdf_header_t *, char *);
+void cdf_swap_dir(cdf_directory_t *);
+void cdf_unpack_dir(cdf_directory_t *, char *);
+void cdf_swap_class(cdf_classid_t *);
+ssize_t cdf_read_sector(const cdf_info_t *, void *, size_t, size_t,
+ const cdf_header_t *, cdf_secid_t);
+ssize_t cdf_read_short_sector(const cdf_stream_t *, void *, size_t, size_t,
+ const cdf_header_t *, cdf_secid_t);
+int cdf_read_sat(const cdf_info_t *, cdf_header_t *, cdf_sat_t *);
+size_t cdf_count_chain(const cdf_sat_t *, cdf_secid_t, size_t);
+int cdf_read_long_sector_chain(const cdf_info_t *, const cdf_header_t *,
+ const cdf_sat_t *, cdf_secid_t, size_t, cdf_stream_t *);
+int cdf_read_short_sector_chain(const cdf_header_t *, const cdf_sat_t *,
+ const cdf_stream_t *, cdf_secid_t, size_t, cdf_stream_t *);
+int cdf_read_sector_chain(const cdf_info_t *, const cdf_header_t *,
+ const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *, cdf_secid_t,
+ size_t, cdf_stream_t *);
+int cdf_read_dir(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *,
+ cdf_dir_t *);
+int cdf_read_ssat(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *,
+ cdf_sat_t *);
+int cdf_read_short_stream(const cdf_info_t *, const cdf_header_t *,
+ const cdf_sat_t *, const cdf_dir_t *, cdf_stream_t *,
+ const cdf_directory_t **);
+int cdf_read_property_info(const cdf_stream_t *, const cdf_header_t *, uint32_t,
+ cdf_property_info_t **, size_t *, size_t *);
+int cdf_read_user_stream(const cdf_info_t *, const cdf_header_t *,
+ const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *,
+ const cdf_dir_t *, const char *, cdf_stream_t *);
+int cdf_find_stream(const cdf_dir_t *, const char *, int);
+int cdf_zero_stream(cdf_stream_t *);
+int cdf_read_doc_summary_info(const cdf_info_t *, const cdf_header_t *,
+ const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *,
+ const cdf_dir_t *, cdf_stream_t *);
+int cdf_read_summary_info(const cdf_info_t *, const cdf_header_t *,
+ const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *,
+ const cdf_dir_t *, cdf_stream_t *);
+int cdf_unpack_summary_info(const cdf_stream_t *, const cdf_header_t *,
+ cdf_summary_info_header_t *, cdf_property_info_t **, size_t *);
+int cdf_unpack_catalog(const cdf_header_t *, const cdf_stream_t *,
+ cdf_catalog_t **);
+int cdf_print_classid(char *, size_t, const cdf_classid_t *);
+int cdf_print_property_name(char *, size_t, uint32_t);
+int cdf_print_elapsed_time(char *, size_t, cdf_timestamp_t);
+uint16_t cdf_tole2(uint16_t);
+uint32_t cdf_tole4(uint32_t);
+uint64_t cdf_tole8(uint64_t);
+char *cdf_ctime(const time_t *, char *);
+char *cdf_u16tos8(char *, size_t, const uint16_t *);
+
+#ifdef CDF_DEBUG
+void cdf_dump_header(const cdf_header_t *);
+void cdf_dump_sat(const char *, const cdf_sat_t *, size_t);
+void cdf_dump(const void *, size_t);
+void cdf_dump_stream(const cdf_stream_t *);
+void cdf_dump_dir(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *,
+ const cdf_sat_t *, const cdf_stream_t *, const cdf_dir_t *);
+void cdf_dump_property_info(const cdf_property_info_t *, size_t);
+void cdf_dump_summary_info(const cdf_header_t *, const cdf_stream_t *);
+void cdf_dump_catalog(const cdf_header_t *, const cdf_stream_t *);
+#endif
+
+
+#endif /* _H_CDF_ */
diff --git a/contrib/libs/libmagic/src/cdf_time.c b/contrib/libs/libmagic/src/cdf_time.c
new file mode 100644
index 0000000000..56eda5ecda
--- /dev/null
+++ b/contrib/libs/libmagic/src/cdf_time.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (c) 2008 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: cdf_time.c,v 1.24 2023/07/17 15:54:44 christos Exp $")
+#endif
+
+#include <time.h>
+#ifdef TEST
+#include <err.h>
+#endif
+#include <string.h>
+
+#include "cdf.h"
+
+#define isleap(y) ((((y) % 4) == 0) && \
+ ((((y) % 100) != 0) || (((y) % 400) == 0)))
+
+static const int mdays[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * Return the number of days between jan 01 1601 and jan 01 of year.
+ */
+static int
+cdf_getdays(int year)
+{
+ int days = 0;
+ int y;
+
+ for (y = CDF_BASE_YEAR; y < year; y++)
+ days += isleap(y) + 365;
+
+ return days;
+}
+
+/*
+ * Return the day within the month
+ */
+static int
+cdf_getday(int year, int days)
+{
+ size_t m;
+
+ for (m = 0; m < __arraycount(mdays); m++) {
+ int sub = mdays[m] + (m == 1 && isleap(year));
+ if (days < sub)
+ return days;
+ days -= sub;
+ }
+ return days;
+}
+
+/*
+ * Return the 0...11 month number.
+ */
+static int
+cdf_getmonth(int year, int days)
+{
+ size_t m;
+
+ for (m = 0; m < __arraycount(mdays); m++) {
+ days -= mdays[m];
+ if (m == 1 && isleap(year))
+ days--;
+ if (days <= 0)
+ return CAST(int, m);
+ }
+ return CAST(int, m);
+}
+
+int
+cdf_timestamp_to_timespec(struct timespec *ts, cdf_timestamp_t t)
+{
+ struct tm tm;
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+ static char UTC[] = "UTC";
+#endif
+ int rdays;
+
+ /* Unit is 100's of nanoseconds */
+ ts->tv_nsec = (t % CDF_TIME_PREC) * 100;
+
+ t /= CDF_TIME_PREC;
+ tm.tm_sec = CAST(int, t % 60);
+ t /= 60;
+
+ tm.tm_min = CAST(int, t % 60);
+ t /= 60;
+
+ tm.tm_hour = CAST(int, t % 24);
+ t /= 24;
+
+ /* XXX: Approx */
+ tm.tm_year = CAST(int, CDF_BASE_YEAR + (t / 365));
+
+ rdays = cdf_getdays(tm.tm_year);
+ t -= rdays - 1;
+ tm.tm_mday = cdf_getday(tm.tm_year, CAST(int, t));
+ tm.tm_mon = cdf_getmonth(tm.tm_year, CAST(int, t));
+ tm.tm_wday = 0;
+ tm.tm_yday = 0;
+ tm.tm_isdst = 0;
+#ifdef HAVE_STRUCT_TM_TM_GMTOFF
+ tm.tm_gmtoff = 0;
+#endif
+#ifdef HAVE_STRUCT_TM_TM_ZONE
+ tm.tm_zone = UTC;
+#endif
+ tm.tm_year -= 1900;
+ ts->tv_sec = mktime(&tm);
+ if (ts->tv_sec == -1) {
+ errno = EINVAL;
+ return -1;
+ }
+ return 0;
+}
+
+int
+/*ARGSUSED*/
+cdf_timespec_to_timestamp(cdf_timestamp_t *t, const struct timespec *ts)
+{
+#ifndef __lint__
+ (void)&t;
+ (void)&ts;
+#endif
+#ifdef notyet
+ struct tm tm;
+ if (gmtime_r(&ts->ts_sec, &tm) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ *t = (ts->ts_nsec / 100) * CDF_TIME_PREC;
+ *t += tm.tm_sec;
+ *t += tm.tm_min * 60;
+ *t += tm.tm_hour * 60 * 60;
+ *t += tm.tm_mday * 60 * 60 * 24;
+#endif
+ return 0;
+}
+
+char *
+cdf_ctime(const time_t *sec, char *buf)
+{
+ char *ptr = *sec > MAX_CTIME ? NULL : ctime_r(sec, buf);
+ if (ptr != NULL)
+ return buf;
+#ifdef WIN32
+ (void)snprintf(buf, 26, "*Bad* 0x%16.16I64x\n",
+ CAST(long long, *sec));
+#else
+ (void)snprintf(buf, 26, "*Bad* %#16.16" INT64_T_FORMAT "x\n",
+ CAST(long long, *sec));
+#endif
+ return buf;
+}
+
+
+#ifdef TEST_TIME
+int
+main(int argc, char *argv[])
+{
+ struct timespec ts;
+ char buf[25];
+ static const cdf_timestamp_t tst = 0x01A5E403C2D59C00ULL;
+ static const char *ref = "Sat Apr 23 01:30:00 1977";
+ char *p, *q;
+
+ cdf_timestamp_to_timespec(&ts, tst);
+ p = cdf_ctime(&ts.tv_sec, buf);
+ if ((q = strchr(p, '\n')) != NULL)
+ *q = '\0';
+ if (strcmp(ref, p) != 0)
+ errx(1, "Error date %s != %s\n", ref, p);
+ return 0;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/compress.c b/contrib/libs/libmagic/src/compress.c
new file mode 100644
index 0000000000..36cf85f455
--- /dev/null
+++ b/contrib/libs/libmagic/src/compress.c
@@ -0,0 +1,1227 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * compress routines:
+ * zmagic() - returns 0 if not recognized, uncompresses and prints
+ * information if recognized
+ * uncompress(method, old, n, newch) - uncompress old into new,
+ * using method, return sizeof new
+ */
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: compress.c,v 1.157 2023/05/21 15:59:58 christos Exp $")
+#endif
+
+#include "magic.h"
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SPAWN_H
+#include <spawn.h>
+#endif
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <signal.h>
+#ifndef HAVE_SIG_T
+typedef void (*sig_t)(int);
+#endif /* HAVE_SIG_T */
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if defined(HAVE_SYS_TIME_H)
+#include <sys/time.h>
+#endif
+
+#if defined(HAVE_ZLIB_H) && defined(ZLIBSUPPORT)
+#define BUILTIN_DECOMPRESS
+#include <zlib.h>
+#endif
+
+#if defined(HAVE_BZLIB_H) && defined(BZLIBSUPPORT)
+#define BUILTIN_BZLIB
+#error #include <bzlib.h>
+#endif
+
+#if defined(HAVE_LZMA_H) && defined(XZLIBSUPPORT)
+#define BUILTIN_XZLIB
+#error #include <lzma.h>
+#endif
+
+#if defined(HAVE_ZSTD_H) && defined(ZSTDLIBSUPPORT)
+#define BUILTIN_ZSTDLIB
+#error #include <zstd.h>
+#error #include <zstd_errors.h>
+#endif
+
+#if defined(HAVE_LZLIB_H) && defined(LZLIBSUPPORT)
+#define BUILTIN_LZLIB
+#error #include <lzlib.h>
+#endif
+
+#ifdef DEBUG
+int tty = -1;
+#define DPRINTF(...) do { \
+ if (tty == -1) \
+ tty = open("/dev/tty", O_RDWR); \
+ if (tty == -1) \
+ abort(); \
+ dprintf(tty, __VA_ARGS__); \
+} while (/*CONSTCOND*/0)
+#else
+#define DPRINTF(...)
+#endif
+
+#ifdef ZLIBSUPPORT
+/*
+ * The following python code is not really used because ZLIBSUPPORT is only
+ * defined if we have a built-in zlib, and the built-in zlib handles that.
+ * That is not true for android where we have zlib.h and not -lz.
+ */
+static const char zlibcode[] =
+ "import sys, zlib; sys.stdout.write(zlib.decompress(sys.stdin.read()))";
+
+static const char *zlib_args[] = { "python", "-c", zlibcode, NULL };
+
+static int
+zlibcmp(const unsigned char *buf)
+{
+ unsigned short x = 1;
+ unsigned char *s = CAST(unsigned char *, CAST(void *, &x));
+
+ if ((buf[0] & 0xf) != 8 || (buf[0] & 0x80) != 0)
+ return 0;
+ if (s[0] != 1) /* endianness test */
+ x = buf[0] | (buf[1] << 8);
+ else
+ x = buf[1] | (buf[0] << 8);
+ if (x % 31)
+ return 0;
+ return 1;
+}
+#endif
+
+static int
+lzmacmp(const unsigned char *buf)
+{
+ if (buf[0] != 0x5d || buf[1] || buf[2])
+ return 0;
+ if (buf[12] && buf[12] != 0xff)
+ return 0;
+ return 1;
+}
+
+#define gzip_flags "-cd"
+#define lzip_flags gzip_flags
+
+static const char *gzip_args[] = {
+ "gzip", gzip_flags, NULL
+};
+static const char *uncompress_args[] = {
+ "uncompress", "-c", NULL
+};
+static const char *bzip2_args[] = {
+ "bzip2", "-cd", NULL
+};
+static const char *lzip_args[] = {
+ "lzip", lzip_flags, NULL
+};
+static const char *xz_args[] = {
+ "xz", "-cd", NULL
+};
+static const char *lrzip_args[] = {
+ "lrzip", "-qdf", "-", NULL
+};
+static const char *lz4_args[] = {
+ "lz4", "-cd", NULL
+};
+static const char *zstd_args[] = {
+ "zstd", "-cd", NULL
+};
+
+#define do_zlib NULL
+#define do_bzlib NULL
+
+file_private const struct {
+ union {
+ const char *magic;
+ int (*func)(const unsigned char *);
+ } u;
+ int maglen;
+ const char **argv;
+ void *unused;
+} compr[] = {
+#define METH_FROZEN 2
+#define METH_BZIP 7
+#define METH_XZ 9
+#define METH_LZIP 8
+#define METH_ZSTD 12
+#define METH_LZMA 13
+#define METH_ZLIB 14
+ { { .magic = "\037\235" }, 2, gzip_args, NULL }, /* 0, compressed */
+ /* Uncompress can get stuck; so use gzip first if we have it
+ * Idea from Damien Clark, thanks! */
+ { { .magic = "\037\235" }, 2, uncompress_args, NULL },/* 1, compressed */
+ { { .magic = "\037\213" }, 2, gzip_args, do_zlib },/* 2, gzipped */
+ { { .magic = "\037\236" }, 2, gzip_args, NULL }, /* 3, frozen */
+ { { .magic = "\037\240" }, 2, gzip_args, NULL }, /* 4, SCO LZH */
+ /* the standard pack utilities do not accept standard input */
+ { { .magic = "\037\036" }, 2, gzip_args, NULL }, /* 5, packed */
+ { { .magic = "PK\3\4" }, 4, gzip_args, NULL }, /* 6, pkziped */
+ /* ...only first file examined */
+ { { .magic = "BZh" }, 3, bzip2_args, do_bzlib },/* 7, bzip2-ed */
+ { { .magic = "LZIP" }, 4, lzip_args, NULL }, /* 8, lzip-ed */
+ { { .magic = "\3757zXZ\0" },6, xz_args, NULL }, /* 9, XZ Util */
+ { { .magic = "LRZI" }, 4, lrzip_args, NULL }, /* 10, LRZIP */
+ { { .magic = "\004\"M\030" },4, lz4_args, NULL }, /* 11, LZ4 */
+ { { .magic = "\x28\xB5\x2F\xFD" }, 4, zstd_args, NULL },/* 12, zstd */
+ { { .func = lzmacmp }, -13, xz_args, NULL }, /* 13, lzma */
+#ifdef ZLIBSUPPORT
+ { { .func = zlibcmp }, -2, zlib_args, NULL }, /* 14, zlib */
+#endif
+};
+
+#define OKDATA 0
+#define NODATA 1
+#define ERRDATA 2
+
+file_private ssize_t swrite(int, const void *, size_t);
+#if HAVE_FORK
+file_private size_t ncompr = __arraycount(compr);
+file_private int uncompressbuf(int, size_t, size_t, int, const unsigned char *,
+ unsigned char **, size_t *);
+#ifdef BUILTIN_DECOMPRESS
+file_private int uncompresszlib(const unsigned char *, unsigned char **, size_t,
+ size_t *, int);
+file_private int uncompressgzipped(const unsigned char *, unsigned char **, size_t,
+ size_t *, int);
+#endif
+#ifdef BUILTIN_BZLIB
+file_private int uncompressbzlib(const unsigned char *, unsigned char **, size_t,
+ size_t *, int);
+#endif
+#ifdef BUILTIN_XZLIB
+file_private int uncompressxzlib(const unsigned char *, unsigned char **, size_t,
+ size_t *, int);
+#endif
+#ifdef BUILTIN_ZSTDLIB
+file_private int uncompresszstd(const unsigned char *, unsigned char **, size_t,
+ size_t *, int);
+#endif
+#ifdef BUILTIN_LZLIB
+file_private int uncompresslzlib(const unsigned char *, unsigned char **, size_t,
+ size_t *, int);
+#endif
+
+static int makeerror(unsigned char **, size_t *, const char *, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+file_private const char *methodname(size_t);
+
+file_private int
+format_decompression_error(struct magic_set *ms, size_t i, unsigned char *buf)
+{
+ unsigned char *p;
+ int mime = ms->flags & MAGIC_MIME;
+
+ if (!mime)
+ return file_printf(ms, "ERROR:[%s: %s]", methodname(i), buf);
+
+ for (p = buf; *p; p++)
+ if (!isalnum(*p))
+ *p = '-';
+
+ return file_printf(ms, "application/x-decompression-error-%s-%s",
+ methodname(i), buf);
+}
+
+file_protected int
+file_zmagic(struct magic_set *ms, const struct buffer *b, const char *name)
+{
+ unsigned char *newbuf = NULL;
+ size_t i, nsz;
+ char *rbuf;
+ file_pushbuf_t *pb;
+ int urv, prv, rv = 0;
+ int mime = ms->flags & MAGIC_MIME;
+ int fd = b->fd;
+ const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
+ size_t nbytes = b->flen;
+ int sa_saved = 0;
+ struct sigaction sig_act;
+
+ if ((ms->flags & MAGIC_COMPRESS) == 0)
+ return 0;
+
+ for (i = 0; i < ncompr; i++) {
+ int zm;
+ if (nbytes < CAST(size_t, abs(compr[i].maglen)))
+ continue;
+ if (compr[i].maglen < 0) {
+ zm = (*compr[i].u.func)(buf);
+ } else {
+ zm = memcmp(buf, compr[i].u.magic,
+ CAST(size_t, compr[i].maglen)) == 0;
+ }
+
+ if (!zm)
+ continue;
+
+ /* Prevent SIGPIPE death if child dies unexpectedly */
+ if (!sa_saved) {
+ //We can use sig_act for both new and old, but
+ struct sigaction new_act;
+ memset(&new_act, 0, sizeof(new_act));
+ new_act.sa_handler = SIG_IGN;
+ sa_saved = sigaction(SIGPIPE, &new_act, &sig_act) != -1;
+ }
+
+ nsz = nbytes;
+ free(newbuf);
+ urv = uncompressbuf(fd, ms->bytes_max, i,
+ (ms->flags & MAGIC_NO_COMPRESS_FORK), buf, &newbuf, &nsz);
+ DPRINTF("uncompressbuf = %d, %s, %" SIZE_T_FORMAT "u\n", urv,
+ (char *)newbuf, nsz);
+ switch (urv) {
+ case OKDATA:
+ case ERRDATA:
+ ms->flags &= ~MAGIC_COMPRESS;
+ if (urv == ERRDATA)
+ prv = format_decompression_error(ms, i, newbuf);
+ else
+ prv = file_buffer(ms, -1, NULL, name, newbuf,
+ nsz);
+ if (prv == -1)
+ goto error;
+ rv = 1;
+ if ((ms->flags & MAGIC_COMPRESS_TRANSP) != 0)
+ goto out;
+ if (mime != MAGIC_MIME && mime != 0)
+ goto out;
+ if ((file_printf(ms,
+ mime ? " compressed-encoding=" : " (")) == -1)
+ goto error;
+ if ((pb = file_push_buffer(ms)) == NULL)
+ goto error;
+ /*
+ * XXX: If file_buffer fails here, we overwrite
+ * the compressed text. FIXME.
+ */
+ if (file_buffer(ms, -1, NULL, NULL, buf, nbytes) == -1)
+ {
+ if (file_pop_buffer(ms, pb) != NULL)
+ abort();
+ goto error;
+ }
+ if ((rbuf = file_pop_buffer(ms, pb)) != NULL) {
+ if (file_printf(ms, "%s", rbuf) == -1) {
+ free(rbuf);
+ goto error;
+ }
+ free(rbuf);
+ }
+ if (!mime && file_printf(ms, ")") == -1)
+ goto error;
+ /*FALLTHROUGH*/
+ case NODATA:
+ break;
+ default:
+ abort();
+ /*NOTREACHED*/
+ error:
+ rv = -1;
+ break;
+ }
+ }
+out:
+ DPRINTF("rv = %d\n", rv);
+
+ if (sa_saved && sig_act.sa_handler != SIG_IGN)
+ (void)sigaction(SIGPIPE, &sig_act, NULL);
+
+ free(newbuf);
+ ms->flags |= MAGIC_COMPRESS;
+ DPRINTF("Zmagic returns %d\n", rv);
+ return rv;
+}
+#endif
+/*
+ * `safe' write for sockets and pipes.
+ */
+file_private ssize_t
+swrite(int fd, const void *buf, size_t n)
+{
+ ssize_t rv;
+ size_t rn = n;
+
+ do
+ switch (rv = write(fd, buf, n)) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ return -1;
+ default:
+ n -= rv;
+ buf = CAST(const char *, buf) + rv;
+ break;
+ }
+ while (n > 0);
+ return rn;
+}
+
+
+/*
+ * `safe' read for sockets and pipes.
+ */
+file_protected ssize_t
+sread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__)))
+{
+ ssize_t rv;
+#if defined(FIONREAD) && !defined(__MINGW32__)
+ int t = 0;
+#endif
+ size_t rn = n;
+
+ if (fd == STDIN_FILENO)
+ goto nocheck;
+
+#if defined(FIONREAD) && !defined(__MINGW32__)
+ if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
+#ifdef FD_ZERO
+ ssize_t cnt;
+ for (cnt = 0;; cnt++) {
+ fd_set check;
+ struct timeval tout = {0, 100 * 1000};
+ int selrv;
+
+ FD_ZERO(&check);
+ FD_SET(fd, &check);
+
+ /*
+ * Avoid soft deadlock: do not read if there
+ * is nothing to read from sockets and pipes.
+ */
+ selrv = select(fd + 1, &check, NULL, NULL, &tout);
+ if (selrv == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ } else if (selrv == 0 && cnt >= 5) {
+ return 0;
+ } else
+ break;
+ }
+#endif
+ (void)ioctl(fd, FIONREAD, &t);
+ }
+
+ if (t > 0 && CAST(size_t, t) < n) {
+ n = t;
+ rn = n;
+ }
+#endif
+
+nocheck:
+ do
+ switch ((rv = read(fd, buf, n))) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ return -1;
+ case 0:
+ return rn - n;
+ default:
+ n -= rv;
+ buf = CAST(char *, CCAST(void *, buf)) + rv;
+ break;
+ }
+ while (n > 0);
+ return rn;
+}
+
+file_protected int
+file_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
+ size_t nbytes)
+{
+ char buf[4096];
+ ssize_t r;
+ int tfd;
+
+#ifdef WIN32
+ const char *t;
+ buf[0] = '\0';
+ if ((t = getenv("TEMP")) != NULL)
+ (void)strlcpy(buf, t, sizeof(buf));
+ else if ((t = getenv("TMP")) != NULL)
+ (void)strlcpy(buf, t, sizeof(buf));
+ else if ((t = getenv("TMPDIR")) != NULL)
+ (void)strlcpy(buf, t, sizeof(buf));
+ if (buf[0] != '\0')
+ (void)strlcat(buf, "/", sizeof(buf));
+ (void)strlcat(buf, "file.XXXXXX", sizeof(buf));
+#else
+ (void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof(buf));
+#endif
+#ifndef HAVE_MKSTEMP
+ {
+ char *ptr = mktemp(buf);
+ tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
+ r = errno;
+ (void)unlink(ptr);
+ errno = r;
+ }
+#else
+ {
+ int te;
+ mode_t ou = umask(0);
+ tfd = mkstemp(buf);
+ (void)umask(ou);
+ te = errno;
+ (void)unlink(buf);
+ errno = te;
+ }
+#endif
+ if (tfd == -1) {
+ file_error(ms, errno,
+ "cannot create temporary file for pipe copy");
+ return -1;
+ }
+
+ if (swrite(tfd, startbuf, nbytes) != CAST(ssize_t, nbytes))
+ r = 1;
+ else {
+ while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
+ if (swrite(tfd, buf, CAST(size_t, r)) != r)
+ break;
+ }
+
+ switch (r) {
+ case -1:
+ file_error(ms, errno, "error copying from pipe to temp file");
+ return -1;
+ case 0:
+ break;
+ default:
+ file_error(ms, errno, "error while writing to temp file");
+ return -1;
+ }
+
+ /*
+ * We duplicate the file descriptor, because fclose on a
+ * tmpfile will delete the file, but any open descriptors
+ * can still access the phantom inode.
+ */
+ if ((fd = dup2(tfd, fd)) == -1) {
+ file_error(ms, errno, "could not dup descriptor for temp file");
+ return -1;
+ }
+ (void)close(tfd);
+ if (lseek(fd, CAST(off_t, 0), SEEK_SET) == CAST(off_t, -1)) {
+ file_badseek(ms);
+ return -1;
+ }
+ return fd;
+}
+#if HAVE_FORK
+#ifdef BUILTIN_DECOMPRESS
+
+#define FHCRC (1 << 1)
+#define FEXTRA (1 << 2)
+#define FNAME (1 << 3)
+#define FCOMMENT (1 << 4)
+
+
+file_private int
+uncompressgzipped(const unsigned char *old, unsigned char **newch,
+ size_t bytes_max, size_t *n, int extra __attribute__((__unused__)))
+{
+ unsigned char flg;
+ size_t data_start = 10;
+
+ if (*n < 4) {
+ goto err;
+ }
+
+ flg = old[3];
+
+ if (flg & FEXTRA) {
+ if (data_start + 1 >= *n)
+ goto err;
+ data_start += 2 + old[data_start] + old[data_start + 1] * 256;
+ }
+ if (flg & FNAME) {
+ while(data_start < *n && old[data_start])
+ data_start++;
+ data_start++;
+ }
+ if (flg & FCOMMENT) {
+ while(data_start < *n && old[data_start])
+ data_start++;
+ data_start++;
+ }
+ if (flg & FHCRC)
+ data_start += 2;
+
+ if (data_start >= *n)
+ goto err;
+
+ *n -= data_start;
+ old += data_start;
+ return uncompresszlib(old, newch, bytes_max, n, 0);
+err:
+ return makeerror(newch, n, "File too short");
+}
+
+file_private int
+uncompresszlib(const unsigned char *old, unsigned char **newch,
+ size_t bytes_max, size_t *n, int zlib)
+{
+ int rc;
+ z_stream z;
+
+ DPRINTF("builtin zlib decompression\n");
+ z.next_in = CCAST(Bytef *, old);
+ z.avail_in = CAST(uint32_t, *n);
+ z.next_out = *newch;
+ z.avail_out = CAST(unsigned int, bytes_max);
+ z.zalloc = Z_NULL;
+ z.zfree = Z_NULL;
+ z.opaque = Z_NULL;
+
+ /* LINTED bug in header macro */
+ rc = zlib ? inflateInit(&z) : inflateInit2(&z, -15);
+ if (rc != Z_OK)
+ goto err;
+
+ rc = inflate(&z, Z_SYNC_FLUSH);
+ if (rc != Z_OK && rc != Z_STREAM_END) {
+ inflateEnd(&z);
+ goto err;
+ }
+
+ *n = CAST(size_t, z.total_out);
+ rc = inflateEnd(&z);
+ if (rc != Z_OK)
+ goto err;
+
+ /* let's keep the nul-terminate tradition */
+ (*newch)[*n] = '\0';
+
+ return OKDATA;
+err:
+ return makeerror(newch, n, "%s", z.msg ? z.msg : zError(rc));
+}
+#endif
+
+#ifdef BUILTIN_BZLIB
+file_private int
+uncompressbzlib(const unsigned char *old, unsigned char **newch,
+ size_t bytes_max, size_t *n, int extra __attribute__((__unused__)))
+{
+ int rc;
+ bz_stream bz;
+
+ DPRINTF("builtin bzlib decompression\n");
+ memset(&bz, 0, sizeof(bz));
+ rc = BZ2_bzDecompressInit(&bz, 0, 0);
+ if (rc != BZ_OK)
+ goto err;
+
+ bz.next_in = CCAST(char *, RCAST(const char *, old));
+ bz.avail_in = CAST(uint32_t, *n);
+ bz.next_out = RCAST(char *, *newch);
+ bz.avail_out = CAST(unsigned int, bytes_max);
+
+ rc = BZ2_bzDecompress(&bz);
+ if (rc != BZ_OK && rc != BZ_STREAM_END) {
+ BZ2_bzDecompressEnd(&bz);
+ goto err;
+ }
+
+ /* Assume byte_max is within 32bit */
+ /* assert(bz.total_out_hi32 == 0); */
+ *n = CAST(size_t, bz.total_out_lo32);
+ rc = BZ2_bzDecompressEnd(&bz);
+ if (rc != BZ_OK)
+ goto err;
+
+ /* let's keep the nul-terminate tradition */
+ (*newch)[*n] = '\0';
+
+ return OKDATA;
+err:
+ return makeerror(newch, n, "bunzip error %d", rc);
+}
+#endif
+
+#ifdef BUILTIN_XZLIB
+file_private int
+uncompressxzlib(const unsigned char *old, unsigned char **newch,
+ size_t bytes_max, size_t *n, int extra __attribute__((__unused__)))
+{
+ int rc;
+ lzma_stream xz;
+
+ DPRINTF("builtin xzlib decompression\n");
+ memset(&xz, 0, sizeof(xz));
+ rc = lzma_auto_decoder(&xz, UINT64_MAX, 0);
+ if (rc != LZMA_OK)
+ goto err;
+
+ xz.next_in = CCAST(const uint8_t *, old);
+ xz.avail_in = CAST(uint32_t, *n);
+ xz.next_out = RCAST(uint8_t *, *newch);
+ xz.avail_out = CAST(unsigned int, bytes_max);
+
+ rc = lzma_code(&xz, LZMA_RUN);
+ if (rc != LZMA_OK && rc != LZMA_STREAM_END) {
+ lzma_end(&xz);
+ goto err;
+ }
+
+ *n = CAST(size_t, xz.total_out);
+
+ lzma_end(&xz);
+
+ /* let's keep the nul-terminate tradition */
+ (*newch)[*n] = '\0';
+
+ return OKDATA;
+err:
+ return makeerror(newch, n, "unxz error %d", rc);
+}
+#endif
+
+#ifdef BUILTIN_ZSTDLIB
+file_private int
+uncompresszstd(const unsigned char *old, unsigned char **newch,
+ size_t bytes_max, size_t *n, int extra __attribute__((__unused__)))
+{
+ size_t rc;
+ ZSTD_DStream *zstd;
+ ZSTD_inBuffer in;
+ ZSTD_outBuffer out;
+
+ DPRINTF("builtin zstd decompression\n");
+ if ((zstd = ZSTD_createDStream()) == NULL) {
+ return makeerror(newch, n, "No ZSTD decompression stream, %s",
+ strerror(errno));
+ }
+
+ rc = ZSTD_DCtx_reset(zstd, ZSTD_reset_session_only);
+ if (ZSTD_isError(rc))
+ goto err;
+
+ in.src = CCAST(const void *, old);
+ in.size = *n;
+ in.pos = 0;
+ out.dst = RCAST(void *, *newch);
+ out.size = bytes_max;
+ out.pos = 0;
+
+ rc = ZSTD_decompressStream(zstd, &out, &in);
+ if (ZSTD_isError(rc))
+ goto err;
+
+ *n = out.pos;
+
+ ZSTD_freeDStream(zstd);
+
+ /* let's keep the nul-terminate tradition */
+ (*newch)[*n] = '\0';
+
+ return OKDATA;
+err:
+ ZSTD_freeDStream(zstd);
+ return makeerror(newch, n, "zstd error %d", ZSTD_getErrorCode(rc));
+}
+#endif
+
+#ifdef BUILTIN_LZLIB
+file_private int
+uncompresslzlib(const unsigned char *old, unsigned char **newch,
+ size_t bytes_max, size_t *n, int extra __attribute__((__unused__)))
+{
+ enum LZ_Errno err;
+ size_t old_remaining = *n;
+ size_t new_remaining = bytes_max;
+ size_t total_read = 0;
+ unsigned char *bufp;
+ struct LZ_Decoder *dec;
+
+ bufp = *newch;
+
+ DPRINTF("builtin lzlib decompression\n");
+ dec = LZ_decompress_open();
+ if (!dec) {
+ return makeerror(newch, n, "unable to allocate LZ_Decoder");
+ }
+ if (LZ_decompress_errno(dec) != LZ_ok)
+ goto err;
+
+ for (;;) {
+ // LZ_decompress_read() stops at member boundaries, so we may
+ // have more than one successful read after writing all data
+ // we have.
+ if (old_remaining > 0) {
+ int wr = LZ_decompress_write(dec, old, old_remaining);
+ if (wr < 0)
+ goto err;
+ old_remaining -= wr;
+ old += wr;
+ }
+
+ int rd = LZ_decompress_read(dec, bufp, new_remaining);
+ if (rd > 0) {
+ new_remaining -= rd;
+ bufp += rd;
+ total_read += rd;
+ }
+
+ if (rd < 0 || LZ_decompress_errno(dec) != LZ_ok)
+ goto err;
+ if (new_remaining == 0)
+ break;
+ if (old_remaining == 0 && rd == 0)
+ break;
+ }
+
+ LZ_decompress_close(dec);
+ *n = total_read;
+
+ /* let's keep the nul-terminate tradition */
+ *bufp = '\0';
+
+ return OKDATA;
+err:
+ err = LZ_decompress_errno(dec);
+ LZ_decompress_close(dec);
+ return makeerror(newch, n, "lzlib error: %s", LZ_strerror(err));
+}
+#endif
+
+
+static int
+makeerror(unsigned char **buf, size_t *len, const char *fmt, ...)
+{
+ char *msg;
+ va_list ap;
+ int rv;
+
+ DPRINTF("Makeerror %s\n", fmt);
+ free(*buf);
+ va_start(ap, fmt);
+ rv = vasprintf(&msg, fmt, ap);
+ va_end(ap);
+ if (rv < 0) {
+ DPRINTF("Makeerror failed");
+ *buf = NULL;
+ *len = 0;
+ return NODATA;
+ }
+ *buf = RCAST(unsigned char *, msg);
+ *len = strlen(msg);
+ return ERRDATA;
+}
+
+static void
+closefd(int *fd, size_t i)
+{
+ if (fd[i] == -1)
+ return;
+ (void) close(fd[i]);
+ fd[i] = -1;
+}
+
+static void
+closep(int *fd)
+{
+ size_t i;
+ for (i = 0; i < 2; i++)
+ closefd(fd, i);
+}
+
+static void
+movedesc(void *v, int i, int fd)
+{
+ if (fd == i)
+ return; /* "no dup was necessary" */
+#ifdef HAVE_POSIX_SPAWNP
+ posix_spawn_file_actions_t *fa = RCAST(posix_spawn_file_actions_t *, v);
+ posix_spawn_file_actions_adddup2(fa, fd, i);
+ posix_spawn_file_actions_addclose(fa, fd);
+#else
+ if (dup2(fd, i) == -1) {
+ DPRINTF("dup(%d, %d) failed (%s)\n", fd, i, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ close(v ? fd : fd);
+#endif
+}
+
+static void
+closedesc(void *v, int fd)
+{
+#ifdef HAVE_POSIX_SPAWNP
+ posix_spawn_file_actions_t *fa = RCAST(posix_spawn_file_actions_t *, v);
+ posix_spawn_file_actions_addclose(fa, fd);
+#else
+ close(v ? fd : fd);
+#endif
+}
+
+static void
+handledesc(void *v, int fd, int fdp[3][2])
+{
+ if (fd != -1) {
+ (void) lseek(fd, CAST(off_t, 0), SEEK_SET);
+ movedesc(v, STDIN_FILENO, fd);
+ } else {
+ movedesc(v, STDIN_FILENO, fdp[STDIN_FILENO][0]);
+ if (fdp[STDIN_FILENO][1] > 2)
+ closedesc(v, fdp[STDIN_FILENO][1]);
+ }
+
+ file_clear_closexec(STDIN_FILENO);
+
+///FIXME: if one of the fdp[i][j] is 0 or 1, this can bomb spectacularly
+ movedesc(v, STDOUT_FILENO, fdp[STDOUT_FILENO][1]);
+ if (fdp[STDOUT_FILENO][0] > 2)
+ closedesc(v, fdp[STDOUT_FILENO][0]);
+
+ file_clear_closexec(STDOUT_FILENO);
+
+ movedesc(v, STDERR_FILENO, fdp[STDERR_FILENO][1]);
+ if (fdp[STDERR_FILENO][0] > 2)
+ closedesc(v, fdp[STDERR_FILENO][0]);
+
+ file_clear_closexec(STDERR_FILENO);
+}
+
+static pid_t
+writechild(int fd, const void *old, size_t n)
+{
+ pid_t pid;
+
+ /*
+ * fork again, to avoid blocking because both
+ * pipes filled
+ */
+ pid = fork();
+ if (pid == -1) {
+ DPRINTF("Fork failed (%s)\n", strerror(errno));
+ return -1;
+ }
+ if (pid == 0) {
+ /* child */
+ if (swrite(fd, old, n) != CAST(ssize_t, n)) {
+ DPRINTF("Write failed (%s)\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ exit(EXIT_SUCCESS);
+ }
+ /* parent */
+ return pid;
+}
+
+static ssize_t
+filter_error(unsigned char *ubuf, ssize_t n)
+{
+ char *p;
+ char *buf;
+
+ ubuf[n] = '\0';
+ buf = RCAST(char *, ubuf);
+ while (isspace(CAST(unsigned char, *buf)))
+ buf++;
+ DPRINTF("Filter error[[[%s]]]\n", buf);
+ if ((p = strchr(CAST(char *, buf), '\n')) != NULL)
+ *p = '\0';
+ if ((p = strchr(CAST(char *, buf), ';')) != NULL)
+ *p = '\0';
+ if ((p = strrchr(CAST(char *, buf), ':')) != NULL) {
+ ++p;
+ while (isspace(CAST(unsigned char, *p)))
+ p++;
+ n = strlen(p);
+ memmove(ubuf, p, CAST(size_t, n + 1));
+ }
+ DPRINTF("Filter error after[[[%s]]]\n", (char *)ubuf);
+ if (islower(*ubuf))
+ *ubuf = toupper(*ubuf);
+ return n;
+}
+
+file_private const char *
+methodname(size_t method)
+{
+ switch (method) {
+#ifdef BUILTIN_DECOMPRESS
+ case METH_FROZEN:
+ case METH_ZLIB:
+ return "zlib";
+#endif
+#ifdef BUILTIN_BZLIB
+ case METH_BZIP:
+ return "bzlib";
+#endif
+#ifdef BUILTIN_XZLIB
+ case METH_XZ:
+ case METH_LZMA:
+ return "xzlib";
+#endif
+#ifdef BUILTIN_ZSTDLIB
+ case METH_ZSTD:
+ return "zstd";
+#endif
+#ifdef BUILTIN_LZLIB
+ case METH_LZIP:
+ return "lzlib";
+#endif
+ default:
+ return compr[method].argv[0];
+ }
+}
+
+file_private int (*
+getdecompressor(size_t method))(const unsigned char *, unsigned char **, size_t,
+ size_t *, int)
+{
+ switch (method) {
+#ifdef BUILTIN_DECOMPRESS
+ case METH_FROZEN:
+ return uncompressgzipped;
+ case METH_ZLIB:
+ return uncompresszlib;
+#endif
+#ifdef BUILTIN_BZLIB
+ case METH_BZIP:
+ return uncompressbzlib;
+#endif
+#ifdef BUILTIN_XZLIB
+ case METH_XZ:
+ case METH_LZMA:
+ return uncompressxzlib;
+#endif
+#ifdef BUILTIN_ZSTDLIB
+ case METH_ZSTD:
+ return uncompresszstd;
+#endif
+#ifdef BUILTIN_LZLIB
+ case METH_LZIP:
+ return uncompresslzlib;
+#endif
+ default:
+ return NULL;
+ }
+}
+
+file_private int
+uncompressbuf(int fd, size_t bytes_max, size_t method, int nofork,
+ const unsigned char *old, unsigned char **newch, size_t* n)
+{
+ int fdp[3][2];
+ int status, rv, w;
+ pid_t pid;
+ pid_t writepid = -1;
+ size_t i;
+ ssize_t r, re;
+ char *const *args;
+#ifdef HAVE_POSIX_SPAWNP
+ posix_spawn_file_actions_t fa;
+#endif
+ int (*decompress)(const unsigned char *, unsigned char **,
+ size_t, size_t *, int) = getdecompressor(method);
+
+ *newch = CAST(unsigned char *, malloc(bytes_max + 1));
+ if (*newch == NULL)
+ return makeerror(newch, n, "No buffer, %s", strerror(errno));
+
+ if (decompress) {
+ if (nofork) {
+ return makeerror(newch, n,
+ "Fork is required to uncompress, but disabled");
+ }
+ return (*decompress)(old, newch, bytes_max, n, 1);
+ }
+
+ (void)fflush(stdout);
+ (void)fflush(stderr);
+
+ for (i = 0; i < __arraycount(fdp); i++)
+ fdp[i][0] = fdp[i][1] = -1;
+
+ /*
+ * There are multithreaded users who run magic_file()
+ * from dozens of threads. If two parallel magic_file() calls
+ * analyze two large compressed files, both will spawn
+ * an uncompressing child here, which writes out uncompressed data.
+ * We read some portion, then close the pipe, then waitpid() the child.
+ * If uncompressed data is larger, child should get EPIPE and exit.
+ * However, with *parallel* calls OTHER child may unintentionally
+ * inherit pipe fds, thus keeping pipe open and making writes in
+ * our child block instead of failing with EPIPE!
+ * (For the bug to occur, two threads must mutually inherit their pipes,
+ * and both must have large outputs. Thus it happens not that often).
+ * To avoid this, be sure to create pipes with O_CLOEXEC.
+ */
+ if ((fd == -1 && file_pipe_closexec(fdp[STDIN_FILENO]) == -1) ||
+ file_pipe_closexec(fdp[STDOUT_FILENO]) == -1 ||
+ file_pipe_closexec(fdp[STDERR_FILENO]) == -1) {
+ closep(fdp[STDIN_FILENO]);
+ closep(fdp[STDOUT_FILENO]);
+ return makeerror(newch, n, "Cannot create pipe, %s",
+ strerror(errno));
+ }
+
+ args = RCAST(char *const *, RCAST(intptr_t, compr[method].argv));
+#ifdef HAVE_POSIX_SPAWNP
+ posix_spawn_file_actions_init(&fa);
+
+ handledesc(&fa, fd, fdp);
+
+ DPRINTF("Executing %s\n", compr[method].argv[0]);
+ status = posix_spawnp(&pid, compr[method].argv[0], &fa, NULL,
+ args, NULL);
+
+ posix_spawn_file_actions_destroy(&fa);
+
+ if (status == -1) {
+ return makeerror(newch, n, "Cannot posix_spawn `%s', %s",
+ compr[method].argv[0], strerror(errno));
+ }
+#else
+ /* For processes with large mapped virtual sizes, vfork
+ * may be _much_ faster (10-100 times) than fork.
+ */
+ pid = vfork();
+ if (pid == -1) {
+ return makeerror(newch, n, "Cannot vfork, %s",
+ strerror(errno));
+ }
+ if (pid == 0) {
+ /* child */
+ /* Note: we are after vfork, do not modify memory
+ * in a way which confuses parent. In particular,
+ * do not modify fdp[i][j].
+ */
+ handledesc(NULL, fd, fdp);
+ DPRINTF("Executing %s\n", compr[method].argv[0]);
+
+ (void)execvp(compr[method].argv[0], args);
+ dprintf(STDERR_FILENO, "exec `%s' failed, %s",
+ compr[method].argv[0], strerror(errno));
+ _exit(EXIT_FAILURE); /* _exit(), not exit(), because of vfork */
+ }
+#endif
+ /* parent */
+ /* Close write sides of child stdout/err pipes */
+ for (i = 1; i < __arraycount(fdp); i++)
+ closefd(fdp[i], 1);
+ /* Write the buffer data to child stdin, if we don't have fd */
+ if (fd == -1) {
+ closefd(fdp[STDIN_FILENO], 0);
+ writepid = writechild(fdp[STDIN_FILENO][1], old, *n);
+ if (writepid == (pid_t)-1) {
+ rv = makeerror(newch, n, "Write to child failed, %s",
+ strerror(errno));
+ DPRINTF("Write to child failed\n");
+ goto err;
+ }
+ closefd(fdp[STDIN_FILENO], 1);
+ }
+
+ rv = OKDATA;
+ r = sread(fdp[STDOUT_FILENO][0], *newch, bytes_max, 0);
+ DPRINTF("read got %zd\n", r);
+ if (r < 0) {
+ rv = ERRDATA;
+ DPRINTF("Read stdout failed %d (%s)\n", fdp[STDOUT_FILENO][0],
+ strerror(errno));
+ goto err;
+ }
+ if (CAST(size_t, r) == bytes_max) {
+ /*
+ * close fd so that the child exits with sigpipe and ignore
+ * errors, otherwise we risk the child blocking and never
+ * exiting.
+ */
+ DPRINTF("Closing stdout for bytes_max\n");
+ closefd(fdp[STDOUT_FILENO], 0);
+ goto ok;
+ }
+ if ((re = sread(fdp[STDERR_FILENO][0], *newch, bytes_max, 0)) > 0) {
+ DPRINTF("Got stuff from stderr %s\n", *newch);
+ rv = ERRDATA;
+ r = filter_error(*newch, r);
+ goto ok;
+ }
+ if (re == 0)
+ goto ok;
+ rv = makeerror(newch, n, "Read stderr failed, %s",
+ strerror(errno));
+ goto err;
+ok:
+ *n = r;
+ /* NUL terminate, as every buffer is handled here. */
+ (*newch)[*n] = '\0';
+err:
+ closefd(fdp[STDIN_FILENO], 1);
+ closefd(fdp[STDOUT_FILENO], 0);
+ closefd(fdp[STDERR_FILENO], 0);
+
+ w = waitpid(pid, &status, 0);
+wait_err:
+ if (w == -1) {
+ rv = makeerror(newch, n, "Wait failed, %s", strerror(errno));
+ DPRINTF("Child wait return %#x\n", status);
+ } else if (!WIFEXITED(status)) {
+ DPRINTF("Child not exited (%#x)\n", status);
+ } else if (WEXITSTATUS(status) != 0) {
+ DPRINTF("Child exited (%#x)\n", WEXITSTATUS(status));
+ }
+ if (writepid > 0) {
+ /* _After_ we know decompressor has exited, our input writer
+ * definitely will exit now (at worst, writing fails in it,
+ * since output fd is closed now on the reading size).
+ */
+ w = waitpid(writepid, &status, 0);
+ writepid = -1;
+ goto wait_err;
+ }
+
+ closefd(fdp[STDIN_FILENO], 0); //why? it is already closed here!
+ DPRINTF("Returning %p n=%" SIZE_T_FORMAT "u rv=%d\n", *newch, *n, rv);
+
+ return rv;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/der.c b/contrib/libs/libmagic/src/der.c
new file mode 100644
index 0000000000..3a036517d0
--- /dev/null
+++ b/contrib/libs/libmagic/src/der.c
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 2016 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+/*
+ * DER (Distinguished Encoding Rules) Parser
+ *
+ * Sources:
+ * https://en.wikipedia.org/wiki/X.690
+ * http://fm4dd.com/openssl/certexamples.htm
+ * http://blog.engelke.com/2014/10/17/parsing-ber-and-der-encoded-asn-1-objects/
+ */
+#ifndef TEST_DER
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: der.c,v 1.27 2022/09/24 20:30:13 christos Exp $")
+#endif
+#else
+#define SIZE_T_FORMAT "z"
+#define CAST(a, b) ((a)(b))
+#endif
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef TEST_DER
+#include "magic.h"
+#include "der.h"
+#else
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <err.h>
+#endif
+
+#define DER_BAD CAST(uint32_t, -1)
+
+#define DER_CLASS_UNIVERSAL 0
+#define DER_CLASS_APPLICATION 1
+#define DER_CLASS_CONTEXT 2
+#define DER_CLASS_PRIVATE 3
+#if defined(DEBUG_DER) || defined(TEST_DER)
+static const char der_class[] = "UACP";
+#endif
+
+#define DER_TYPE_PRIMITIVE 0
+#define DER_TYPE_CONSTRUCTED 1
+#if defined(DEBUG_DER) || defined(TEST_DER)
+static const char der_type[] = "PC";
+#endif
+
+#define DER_TAG_EOC 0x00
+#define DER_TAG_BOOLEAN 0x01
+#define DER_TAG_INTEGER 0x02
+#define DER_TAG_BIT STRING 0x03
+#define DER_TAG_OCTET_STRING 0x04
+#define DER_TAG_NULL 0x05
+#define DER_TAG_OBJECT_IDENTIFIER 0x06
+#define DER_TAG_OBJECT_DESCRIPTOR 0x07
+#define DER_TAG_EXTERNAL 0x08
+#define DER_TAG_REAL 0x09
+#define DER_TAG_ENUMERATED 0x0a
+#define DER_TAG_EMBEDDED_PDV 0x0b
+#define DER_TAG_UTF8_STRING 0x0c
+#define DER_TAG_RELATIVE_OID 0x0d
+#define DER_TAG_TIME 0x0e
+#define DER_TAG_RESERVED_2 0x0f
+#define DER_TAG_SEQUENCE 0x10
+#define DER_TAG_SET 0x11
+#define DER_TAG_NUMERIC_STRING 0x12
+#define DER_TAG_PRINTABLE_STRING 0x13
+#define DER_TAG_T61_STRING 0x14
+#define DER_TAG_VIDEOTEX_STRING 0x15
+#define DER_TAG_IA5_STRING 0x16
+#define DER_TAG_UTCTIME 0x17
+#define DER_TAG_GENERALIZED_TIME 0x18
+#define DER_TAG_GRAPHIC_STRING 0x19
+#define DER_TAG_VISIBLE_STRING 0x1a
+#define DER_TAG_GENERAL_STRING 0x1b
+#define DER_TAG_UNIVERSAL_STRING 0x1c
+#define DER_TAG_CHARACTER_STRING 0x1d
+#define DER_TAG_BMP_STRING 0x1e
+#define DER_TAG_DATE 0x1f
+#define DER_TAG_TIME_OF_DAY 0x20
+#define DER_TAG_DATE_TIME 0x21
+#define DER_TAG_DURATION 0x22
+#define DER_TAG_OID_IRI 0x23
+#define DER_TAG_RELATIVE_OID_IRI 0x24
+#define DER_TAG_LAST 0x25
+
+static const char *der__tag[] = {
+ "eoc", "bool", "int", "bit_str", "octet_str",
+ "null", "obj_id", "obj_desc", "ext", "real",
+ "enum", "embed", "utf8_str", "rel_oid", "time",
+ "res2", "seq", "set", "num_str", "prt_str",
+ "t61_str", "vid_str", "ia5_str", "utc_time", "gen_time",
+ "gr_str", "vis_str", "gen_str", "univ_str", "char_str",
+ "bmp_str", "date", "tod", "datetime", "duration",
+ "oid-iri", "rel-oid-iri",
+};
+
+#ifdef DEBUG_DER
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+
+#ifdef TEST_DER
+static uint8_t
+getclass(uint8_t c)
+{
+ return c >> 6;
+}
+
+static uint8_t
+gettype(uint8_t c)
+{
+ return (c >> 5) & 1;
+}
+#endif
+
+static uint32_t
+gettag(const uint8_t *c, size_t *p, size_t l)
+{
+ uint32_t tag;
+
+ if (*p >= l)
+ return DER_BAD;
+
+ tag = c[(*p)++] & 0x1f;
+
+ if (tag != 0x1f)
+ return tag;
+
+ if (*p >= l)
+ return DER_BAD;
+
+ while (c[*p] >= 0x80) {
+ tag = tag * 128 + c[(*p)++] - 0x80;
+ if (*p >= l)
+ return DER_BAD;
+ }
+ return tag;
+}
+
+/*
+ * Read the length of a DER tag from the input.
+ *
+ * `c` is the input, `p` is an output parameter that specifies how much of the
+ * input we consumed, and `l` is the maximum input length.
+ *
+ * Returns the length, or DER_BAD if the end of the input is reached or the
+ * length exceeds the remaining input.
+ */
+static uint32_t
+getlength(const uint8_t *c, size_t *p, size_t l)
+{
+ uint8_t digits, i;
+ size_t len;
+ int is_onebyte_result;
+
+ if (*p >= l) {
+ DPRINTF(("%s:[1] %zu >= %zu\n", __func__, *p, l));
+ return DER_BAD;
+ }
+
+ /*
+ * Digits can either be 0b0 followed by the result, or 0b1
+ * followed by the number of digits of the result. In either case,
+ * we verify that we can read so many bytes from the input.
+ */
+ is_onebyte_result = (c[*p] & 0x80) == 0;
+ digits = c[(*p)++] & 0x7f;
+ if (*p + digits >= l) {
+ DPRINTF(("%s:[2] %zu + %u >= %zu\n", __func__, *p, digits, l));
+ return DER_BAD;
+ }
+
+ if (is_onebyte_result)
+ return digits;
+
+ /*
+ * Decode len. We've already verified that we're allowed to read
+ * `digits` bytes.
+ */
+ len = 0;
+ for (i = 0; i < digits; i++)
+ len = (len << 8) | c[(*p)++];
+
+ if (len > UINT32_MAX - *p || *p + len > l) {
+ DPRINTF(("%s:[3] bad len %zu + %zu >= %zu\n",
+ __func__, *p, len, l));
+ return DER_BAD;
+ }
+ return CAST(uint32_t, len);
+}
+
+static const char *
+der_tag(char *buf, size_t len, uint32_t tag)
+{
+ if (tag < DER_TAG_LAST)
+ strlcpy(buf, der__tag[tag], len);
+ else
+ snprintf(buf, len, "%#x", tag);
+ return buf;
+}
+
+#ifndef TEST_DER
+static int
+der_data(char *buf, size_t blen, uint32_t tag, const void *q, uint32_t len)
+{
+ uint32_t i;
+ const uint8_t *d = CAST(const uint8_t *, q);
+ switch (tag) {
+ case DER_TAG_PRINTABLE_STRING:
+ case DER_TAG_UTF8_STRING:
+ case DER_TAG_IA5_STRING:
+ return snprintf(buf, blen, "%.*s", len, RCAST(const char *, q));
+ case DER_TAG_UTCTIME:
+ if (len < 12)
+ break;
+ return snprintf(buf, blen,
+ "20%c%c-%c%c-%c%c %c%c:%c%c:%c%c GMT", d[0], d[1], d[2],
+ d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11]);
+ default:
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ uint32_t z = i << 1;
+ if (z < blen - 2)
+ snprintf(buf + z, blen - z, "%.2x", d[i]);
+ }
+ return len * 2;
+}
+
+int32_t
+der_offs(struct magic_set *ms, struct magic *m, size_t nbytes)
+{
+ const uint8_t *b = RCAST(const uint8_t *, ms->search.s);
+ size_t offs = 0, len = ms->search.s_len ? ms->search.s_len : nbytes;
+
+ if (gettag(b, &offs, len) == DER_BAD) {
+ DPRINTF(("%s: bad tag 1\n", __func__));
+ return -1;
+ }
+ DPRINTF(("%s1: %u %" SIZE_T_FORMAT "u %d\n", __func__, ms->offset,
+ offs, m->offset));
+
+ uint32_t tlen = getlength(b, &offs, len);
+ if (tlen == DER_BAD) {
+ DPRINTF(("%s: bad tag 2\n", __func__));
+ return -1;
+ }
+ DPRINTF(("%s2: %u %" SIZE_T_FORMAT "u %u\n", __func__, ms->offset,
+ offs, tlen));
+
+ offs += ms->offset + m->offset;
+ DPRINTF(("cont_level = %d\n", m->cont_level));
+#ifdef DEBUG_DER
+ size_t i;
+ for (i = 0; i < m->cont_level; i++)
+ printf("cont_level[%" SIZE_T_FORMAT "u] = %d\n", i,
+ ms->c.li[i].off);
+#endif
+ if (m->cont_level != 0) {
+ if (offs + tlen > nbytes)
+ return -1;
+ ms->c.li[m->cont_level - 1].off = CAST(int, offs + tlen);
+ DPRINTF(("cont_level[%u] = %d\n", m->cont_level - 1,
+ ms->c.li[m->cont_level - 1].off));
+ }
+ return CAST(int32_t, offs);
+}
+
+int
+der_cmp(struct magic_set *ms, struct magic *m)
+{
+ const uint8_t *b = RCAST(const uint8_t *, ms->search.s);
+ const char *s = m->value.s;
+ size_t offs = 0, len = ms->search.s_len;
+ uint32_t tag, tlen;
+ char buf[128];
+
+ DPRINTF(("%s: compare %zu bytes\n", __func__, len));
+
+ tag = gettag(b, &offs, len);
+ if (tag == DER_BAD) {
+ DPRINTF(("%s: bad tag 1\n", __func__));
+ return -1;
+ }
+
+ DPRINTF(("%s1: %d %" SIZE_T_FORMAT "u %d\n", __func__, ms->offset,
+ offs, m->offset));
+
+ tlen = getlength(b, &offs, len);
+ if (tlen == DER_BAD) {
+ DPRINTF(("%s: bad tag 2\n", __func__));
+ return -1;
+ }
+
+ der_tag(buf, sizeof(buf), tag);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "%s: tag %p got=%s exp=%s\n", __func__, b,
+ buf, s);
+ size_t slen = strlen(buf);
+
+ if (strncmp(buf, s, slen) != 0) {
+ DPRINTF(("%s: no string match %s != %s\n", __func__, buf, s));
+ return 0;
+ }
+
+ s += slen;
+
+again:
+ switch (*s) {
+ case '\0':
+ DPRINTF(("%s: EOF match\n", __func__));
+ return 1;
+ case '=':
+ s++;
+ goto val;
+ default:
+ if (!isdigit(CAST(unsigned char, *s))) {
+ DPRINTF(("%s: no digit %c\n", __func__, *s));
+ return 0;
+ }
+
+ slen = 0;
+ do
+ slen = slen * 10 + *s - '0';
+ while (isdigit(CAST(unsigned char, *++s)));
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "%s: len %" SIZE_T_FORMAT "u %u\n",
+ __func__, slen, tlen);
+ if (tlen != slen) {
+ DPRINTF(("%s: len %u != %zu\n", __func__, tlen, slen));
+ return 0;
+ }
+ goto again;
+ }
+val:
+ DPRINTF(("%s: before data %" SIZE_T_FORMAT "u %u\n", __func__, offs,
+ tlen));
+ der_data(buf, sizeof(buf), tag, b + offs, tlen);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "%s: data %s %s\n", __func__, buf, s);
+ if (strcmp(buf, s) != 0 && strcmp("x", s) != 0) {
+ DPRINTF(("%s: no string match %s != %s\n", __func__, buf, s));
+ return 0;
+ }
+ strlcpy(ms->ms_value.s, buf, sizeof(ms->ms_value.s));
+ DPRINTF(("%s: complete match\n", __func__));
+ return 1;
+}
+#endif
+
+#ifdef TEST_DER
+static void
+printtag(uint32_t tag, const void *q, uint32_t len)
+{
+ const uint8_t *d = q;
+ switch (tag) {
+ case DER_TAG_PRINTABLE_STRING:
+ case DER_TAG_UTF8_STRING:
+ case DER_TAG_IA5_STRING:
+ case DER_TAG_UTCTIME:
+ printf("%.*s\n", len, (const char *)q);
+ return;
+ default:
+ break;
+ }
+
+ for (uint32_t i = 0; i < len; i++)
+ printf("%.2x", d[i]);
+ printf("\n");
+}
+
+static void
+printdata(size_t level, const void *v, size_t x, size_t l)
+{
+ const uint8_t *p = v, *ep = p + l;
+ size_t ox;
+ char buf[128];
+
+ while (p + x < ep) {
+ const uint8_t *q;
+ uint8_t c = getclass(p[x]);
+ uint8_t t = gettype(p[x]);
+ ox = x;
+// if (x != 0)
+// printf("%.2x %.2x %.2x\n", p[x - 1], p[x], p[x + 1]);
+ uint32_t tag = gettag(p, &x, ep - p + x);
+ if (p + x >= ep)
+ break;
+ uint32_t len = getlength(p, &x, ep - p + x);
+
+ printf("%" SIZE_T_FORMAT "u %" SIZE_T_FORMAT "u-%"
+ SIZE_T_FORMAT "u %c,%c,%s,%u:", level, ox, x,
+ der_class[c], der_type[t],
+ der_tag(buf, sizeof(buf), tag), len);
+ q = p + x;
+ if (p + len > ep)
+ errx(EXIT_FAILURE, "corrupt der");
+ printtag(tag, q, len);
+ if (t != DER_TYPE_PRIMITIVE)
+ printdata(level + 1, p, x, len + x);
+ x += len;
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ struct stat st;
+ size_t l;
+ void *p;
+
+ if ((fd = open(argv[1], O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "open `%s'", argv[1]);
+ if (fstat(fd, &st) == -1)
+ err(EXIT_FAILURE, "stat `%s'", argv[1]);
+ l = (size_t)st.st_size;
+ if ((p = mmap(NULL, l, PROT_READ, MAP_FILE, fd, 0)) == MAP_FAILED)
+ err(EXIT_FAILURE, "mmap `%s'", argv[1]);
+
+ printdata(0, p, 0, l);
+ munmap(p, l);
+ return 0;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/der.h b/contrib/libs/libmagic/src/der.h
new file mode 100644
index 0000000000..3333239201
--- /dev/null
+++ b/contrib/libs/libmagic/src/der.h
@@ -0,0 +1,28 @@
+/*-
+ * Copyright (c) 2016 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+extern int der_offs(struct magic_set *, struct magic *, size_t);
+extern int der_cmp(struct magic_set *, struct magic *);
diff --git a/contrib/libs/libmagic/src/elfclass.h b/contrib/libs/libmagic/src/elfclass.h
new file mode 100644
index 0000000000..936d8dc912
--- /dev/null
+++ b/contrib/libs/libmagic/src/elfclass.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) Christos Zoulas 2008.
+ * All Rights Reserved.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+ if (nbytes <= sizeof(elfhdr))
+ return 0;
+
+ u.l = 1;
+ (void)memcpy(&elfhdr, buf, sizeof elfhdr);
+ swap = (u.c[sizeof(int32_t) - 1] + 1) != elfhdr.e_ident[EI_DATA];
+
+ type = elf_getu16(swap, elfhdr.e_type);
+ notecount = ms->elf_notes_max;
+ switch (type) {
+#ifdef ELFCORE
+ case ET_CORE:
+ phnum = elf_getu16(swap, elfhdr.e_phnum);
+ if (phnum > ms->elf_phnum_max)
+ return toomany(ms, "program headers", phnum);
+ flags |= FLAGS_IS_CORE;
+ if (dophn_core(ms, clazz, swap, fd,
+ CAST(off_t, elf_getu(swap, elfhdr.e_phoff)), phnum,
+ CAST(size_t, elf_getu16(swap, elfhdr.e_phentsize)),
+ fsize, &flags, &notecount) == -1)
+ return -1;
+ break;
+#endif
+ case ET_EXEC:
+ case ET_DYN:
+ phnum = elf_getu16(swap, elfhdr.e_phnum);
+ if (phnum > ms->elf_phnum_max)
+ return toomany(ms, "program", phnum);
+ shnum = elf_getu16(swap, elfhdr.e_shnum);
+ if (shnum > ms->elf_shnum_max)
+ return toomany(ms, "section", shnum);
+ if (dophn_exec(ms, clazz, swap, fd,
+ CAST(off_t, elf_getu(swap, elfhdr.e_phoff)), phnum,
+ CAST(size_t, elf_getu16(swap, elfhdr.e_phentsize)),
+ fsize, shnum, &flags, &notecount) == -1)
+ return -1;
+ /*FALLTHROUGH*/
+ case ET_REL:
+ shnum = elf_getu16(swap, elfhdr.e_shnum);
+ if (shnum > ms->elf_shnum_max)
+ return toomany(ms, "section headers", shnum);
+ if (doshn(ms, clazz, swap, fd,
+ CAST(off_t, elf_getu(swap, elfhdr.e_shoff)), shnum,
+ CAST(size_t, elf_getu16(swap, elfhdr.e_shentsize)),
+ fsize, elf_getu16(swap, elfhdr.e_machine),
+ CAST(int, elf_getu16(swap, elfhdr.e_shstrndx)),
+ &flags, &notecount) == -1)
+ return -1;
+ break;
+
+ default:
+ break;
+ }
+ if (notecount == 0)
+ return toomany(ms, "notes", ms->elf_notes_max);
+ return 1;
diff --git a/contrib/libs/libmagic/src/encoding.c b/contrib/libs/libmagic/src/encoding.c
new file mode 100644
index 0000000000..9dbb9dd93e
--- /dev/null
+++ b/contrib/libs/libmagic/src/encoding.c
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * Encoding -- determine the character encoding of a text file.
+ *
+ * Joerg Wunsch <joerg@freebsd.org> wrote the original support for 8-bit
+ * international characters.
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: encoding.c,v 1.42 2022/12/26 17:31:14 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+file_private int looks_ascii(const unsigned char *, size_t, file_unichar_t *,
+ size_t *);
+file_private int looks_utf8_with_BOM(const unsigned char *, size_t, file_unichar_t *,
+ size_t *);
+file_private int looks_utf7(const unsigned char *, size_t, file_unichar_t *,
+ size_t *);
+file_private int looks_ucs16(const unsigned char *, size_t, file_unichar_t *,
+ size_t *);
+file_private int looks_ucs32(const unsigned char *, size_t, file_unichar_t *,
+ size_t *);
+file_private int looks_latin1(const unsigned char *, size_t, file_unichar_t *,
+ size_t *);
+file_private int looks_extended(const unsigned char *, size_t, file_unichar_t *,
+ size_t *);
+file_private void from_ebcdic(const unsigned char *, size_t, unsigned char *);
+
+#ifdef DEBUG_ENCODING
+#define DPRINTF(a) printf a
+#else
+#define DPRINTF(a)
+#endif
+
+/*
+ * Try to determine whether text is in some character code we can
+ * identify. Each of these tests, if it succeeds, will leave
+ * the text converted into one-file_unichar_t-per-character Unicode in
+ * ubuf, and the number of characters converted in ulen.
+ */
+file_protected int
+file_encoding(struct magic_set *ms, const struct buffer *b,
+ file_unichar_t **ubuf, size_t *ulen, const char **code,
+ const char **code_mime, const char **type)
+{
+ const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
+ size_t nbytes = b->flen;
+ size_t mlen;
+ int rv = 1, ucs_type;
+ file_unichar_t *udefbuf;
+ size_t udeflen;
+
+ if (ubuf == NULL)
+ ubuf = &udefbuf;
+ if (ulen == NULL)
+ ulen = &udeflen;
+
+ *type = "text";
+ *ulen = 0;
+ *code = "unknown";
+ *code_mime = "binary";
+
+ if (nbytes > ms->encoding_max)
+ nbytes = ms->encoding_max;
+
+ mlen = (nbytes + 1) * sizeof((*ubuf)[0]);
+ *ubuf = CAST(file_unichar_t *, calloc(CAST(size_t, 1), mlen));
+ if (*ubuf == NULL) {
+ file_oomem(ms, mlen);
+ goto done;
+ }
+ if (looks_ascii(buf, nbytes, *ubuf, ulen)) {
+ if (looks_utf7(buf, nbytes, *ubuf, ulen) > 0) {
+ DPRINTF(("utf-7 %" SIZE_T_FORMAT "u\n", *ulen));
+ *code = "Unicode text, UTF-7";
+ *code_mime = "utf-7";
+ } else {
+ DPRINTF(("ascii %" SIZE_T_FORMAT "u\n", *ulen));
+ *code = "ASCII";
+ *code_mime = "us-ascii";
+ }
+ } else if (looks_utf8_with_BOM(buf, nbytes, *ubuf, ulen) > 0) {
+ DPRINTF(("utf8/bom %" SIZE_T_FORMAT "u\n", *ulen));
+ *code = "Unicode text, UTF-8 (with BOM)";
+ *code_mime = "utf-8";
+ } else if (file_looks_utf8(buf, nbytes, *ubuf, ulen) > 1) {
+ DPRINTF(("utf8 %" SIZE_T_FORMAT "u\n", *ulen));
+ *code = "Unicode text, UTF-8";
+ *code_mime = "utf-8";
+ } else if ((ucs_type = looks_ucs32(buf, nbytes, *ubuf, ulen)) != 0) {
+ if (ucs_type == 1) {
+ *code = "Unicode text, UTF-32, little-endian";
+ *code_mime = "utf-32le";
+ } else {
+ *code = "Unicode text, UTF-32, big-endian";
+ *code_mime = "utf-32be";
+ }
+ DPRINTF(("ucs32 %" SIZE_T_FORMAT "u\n", *ulen));
+ } else if ((ucs_type = looks_ucs16(buf, nbytes, *ubuf, ulen)) != 0) {
+ if (ucs_type == 1) {
+ *code = "Unicode text, UTF-16, little-endian";
+ *code_mime = "utf-16le";
+ } else {
+ *code = "Unicode text, UTF-16, big-endian";
+ *code_mime = "utf-16be";
+ }
+ DPRINTF(("ucs16 %" SIZE_T_FORMAT "u\n", *ulen));
+ } else if (looks_latin1(buf, nbytes, *ubuf, ulen)) {
+ DPRINTF(("latin1 %" SIZE_T_FORMAT "u\n", *ulen));
+ *code = "ISO-8859";
+ *code_mime = "iso-8859-1";
+ } else if (looks_extended(buf, nbytes, *ubuf, ulen)) {
+ DPRINTF(("extended %" SIZE_T_FORMAT "u\n", *ulen));
+ *code = "Non-ISO extended-ASCII";
+ *code_mime = "unknown-8bit";
+ } else {
+ unsigned char *nbuf;
+
+ mlen = (nbytes + 1) * sizeof(nbuf[0]);
+ if ((nbuf = CAST(unsigned char *, malloc(mlen))) == NULL) {
+ file_oomem(ms, mlen);
+ goto done;
+ }
+ from_ebcdic(buf, nbytes, nbuf);
+
+ if (looks_ascii(nbuf, nbytes, *ubuf, ulen)) {
+ DPRINTF(("ebcdic %" SIZE_T_FORMAT "u\n", *ulen));
+ *code = "EBCDIC";
+ *code_mime = "ebcdic";
+ } else if (looks_latin1(nbuf, nbytes, *ubuf, ulen)) {
+ DPRINTF(("ebcdic/international %" SIZE_T_FORMAT "u\n",
+ *ulen));
+ *code = "International EBCDIC";
+ *code_mime = "ebcdic";
+ } else { /* Doesn't look like text at all */
+ DPRINTF(("binary\n"));
+ rv = 0;
+ *type = "binary";
+ }
+ free(nbuf);
+ }
+
+ done:
+ if (ubuf == &udefbuf)
+ free(udefbuf);
+
+ return rv;
+}
+
+/*
+ * This table reflects a particular philosophy about what constitutes
+ * "text," and there is room for disagreement about it.
+ *
+ * Version 3.31 of the file command considered a file to be ASCII if
+ * each of its characters was approved by either the isascii() or
+ * isalpha() function. On most systems, this would mean that any
+ * file consisting only of characters in the range 0x00 ... 0x7F
+ * would be called ASCII text, but many systems might reasonably
+ * consider some characters outside this range to be alphabetic,
+ * so the file command would call such characters ASCII. It might
+ * have been more accurate to call this "considered textual on the
+ * local system" than "ASCII."
+ *
+ * It considered a file to be "International language text" if each
+ * of its characters was either an ASCII printing character (according
+ * to the real ASCII standard, not the above test), a character in
+ * the range 0x80 ... 0xFF, or one of the following control characters:
+ * backspace, tab, line feed, vertical tab, form feed, carriage return,
+ * escape. No attempt was made to determine the language in which files
+ * of this type were written.
+ *
+ *
+ * The table below considers a file to be ASCII if all of its characters
+ * are either ASCII printing characters (again, according to the X3.4
+ * standard, not isascii()) or any of the following controls: bell,
+ * backspace, tab, line feed, form feed, carriage return, esc, nextline.
+ *
+ * I include bell because some programs (particularly shell scripts)
+ * use it literally, even though it is rare in normal text. I exclude
+ * vertical tab because it never seems to be used in real text. I also
+ * include, with hesitation, the X3.64/ECMA-43 control nextline (0x85),
+ * because that's what the dd EBCDIC->ASCII table maps the EBCDIC newline
+ * character to. It might be more appropriate to include it in the 8859
+ * set instead of the ASCII set, but it's got to be included in *something*
+ * we recognize or EBCDIC files aren't going to be considered textual.
+ * Some old Unix source files use SO/SI (^N/^O) to shift between Greek
+ * and Latin characters, so these should possibly be allowed. But they
+ * make a real mess on VT100-style displays if they're not paired properly,
+ * so we are probably better off not calling them text.
+ *
+ * A file is considered to be ISO-8859 text if its characters are all
+ * either ASCII, according to the above definition, or printing characters
+ * from the ISO-8859 8-bit extension, characters 0xA0 ... 0xFF.
+ *
+ * Finally, a file is considered to be international text from some other
+ * character code if its characters are all either ISO-8859 (according to
+ * the above definition) or characters in the range 0x80 ... 0x9F, which
+ * ISO-8859 considers to be control characters but the IBM PC and Macintosh
+ * consider to be printing characters.
+ */
+
+#define F 0 /* character never appears in text */
+#define T 1 /* character appears in plain ASCII text */
+#define I 2 /* character appears in ISO-8859 text */
+#define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */
+
+file_private char text_chars[256] = {
+ /* BEL BS HT LF VT FF CR */
+ F, F, F, F, F, F, F, T, T, T, T, T, T, T, F, F, /* 0x0X */
+ /* ESC */
+ F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */
+ /* NEL */
+ X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */
+ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I /* 0xfX */
+};
+
+#define LOOKS(NAME, COND) \
+file_private int \
+looks_ ## NAME(const unsigned char *buf, size_t nbytes, file_unichar_t *ubuf, \
+ size_t *ulen) \
+{ \
+ size_t i; \
+\
+ *ulen = 0; \
+\
+ for (i = 0; i < nbytes; i++) { \
+ int t = text_chars[buf[i]]; \
+\
+ if (COND) \
+ return 0; \
+\
+ ubuf[(*ulen)++] = buf[i]; \
+ } \
+ return 1; \
+}
+
+LOOKS(ascii, t != T)
+LOOKS(latin1, t != T && t != I)
+LOOKS(extended, t != T && t != I && t != X)
+
+/*
+ * Decide whether some text looks like UTF-8. Returns:
+ *
+ * -1: invalid UTF-8
+ * 0: uses odd control characters, so doesn't look like text
+ * 1: 7-bit text
+ * 2: definitely UTF-8 text (valid high-bit set bytes)
+ *
+ * If ubuf is non-NULL on entry, text is decoded into ubuf, *ulen;
+ * ubuf must be big enough!
+ */
+
+// from: https://golang.org/src/unicode/utf8/utf8.go
+
+#define XX 0xF1 // invalid: size 1
+#define AS 0xF0 // ASCII: size 1
+#define S1 0x02 // accept 0, size 2
+#define S2 0x13 // accept 1, size 3
+#define S3 0x03 // accept 0, size 3
+#define S4 0x23 // accept 2, size 3
+#define S5 0x34 // accept 3, size 4
+#define S6 0x04 // accept 0, size 4
+#define S7 0x44 // accept 4, size 4
+
+#define LOCB 0x80
+#define HICB 0xBF
+
+// first is information about the first byte in a UTF-8 sequence.
+static const uint8_t first[] = {
+ // 1 2 3 4 5 6 7 8 9 A B C D E F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x00-0x0F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x10-0x1F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x20-0x2F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x30-0x3F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x40-0x4F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x50-0x5F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x60-0x6F
+ AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, AS, // 0x70-0x7F
+ // 1 2 3 4 5 6 7 8 9 A B C D E F
+ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0x80-0x8F
+ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0x90-0x9F
+ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0xA0-0xAF
+ XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0xB0-0xBF
+ XX, XX, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, // 0xC0-0xCF
+ S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, S1, // 0xD0-0xDF
+ S2, S3, S3, S3, S3, S3, S3, S3, S3, S3, S3, S3, S3, S4, S3, S3, // 0xE0-0xEF
+ S5, S6, S6, S6, S7, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0xF0-0xFF
+};
+
+// acceptRange gives the range of valid values for the second byte in a UTF-8
+// sequence.
+struct accept_range {
+ uint8_t lo; // lowest value for second byte.
+ uint8_t hi; // highest value for second byte.
+} accept_ranges[16] = {
+// acceptRanges has size 16 to avoid bounds checks in the code that uses it.
+ { LOCB, HICB },
+ { 0xA0, HICB },
+ { LOCB, 0x9F },
+ { 0x90, HICB },
+ { LOCB, 0x8F },
+};
+
+file_protected int
+file_looks_utf8(const unsigned char *buf, size_t nbytes, file_unichar_t *ubuf,
+ size_t *ulen)
+{
+ size_t i;
+ int n;
+ file_unichar_t c;
+ int gotone = 0, ctrl = 0;
+
+ if (ubuf)
+ *ulen = 0;
+
+ for (i = 0; i < nbytes; i++) {
+ if ((buf[i] & 0x80) == 0) { /* 0xxxxxxx is plain ASCII */
+ /*
+ * Even if the whole file is valid UTF-8 sequences,
+ * still reject it if it uses weird control characters.
+ */
+
+ if (text_chars[buf[i]] != T)
+ ctrl = 1;
+
+ if (ubuf)
+ ubuf[(*ulen)++] = buf[i];
+ } else if ((buf[i] & 0x40) == 0) { /* 10xxxxxx never 1st byte */
+ return -1;
+ } else { /* 11xxxxxx begins UTF-8 */
+ int following;
+ uint8_t x = first[buf[i]];
+ const struct accept_range *ar =
+ &accept_ranges[(unsigned int)x >> 4];
+ if (x == XX)
+ return -1;
+
+ if ((buf[i] & 0x20) == 0) { /* 110xxxxx */
+ c = buf[i] & 0x1f;
+ following = 1;
+ } else if ((buf[i] & 0x10) == 0) { /* 1110xxxx */
+ c = buf[i] & 0x0f;
+ following = 2;
+ } else if ((buf[i] & 0x08) == 0) { /* 11110xxx */
+ c = buf[i] & 0x07;
+ following = 3;
+ } else if ((buf[i] & 0x04) == 0) { /* 111110xx */
+ c = buf[i] & 0x03;
+ following = 4;
+ } else if ((buf[i] & 0x02) == 0) { /* 1111110x */
+ c = buf[i] & 0x01;
+ following = 5;
+ } else
+ return -1;
+
+ for (n = 0; n < following; n++) {
+ i++;
+ if (i >= nbytes)
+ goto done;
+
+ if (n == 0 &&
+ (buf[i] < ar->lo || buf[i] > ar->hi))
+ return -1;
+
+ if ((buf[i] & 0x80) == 0 || (buf[i] & 0x40))
+ return -1;
+
+ c = (c << 6) + (buf[i] & 0x3f);
+ }
+
+ if (ubuf)
+ ubuf[(*ulen)++] = c;
+ gotone = 1;
+ }
+ }
+done:
+ return ctrl ? 0 : (gotone ? 2 : 1);
+}
+
+/*
+ * Decide whether some text looks like UTF-8 with BOM. If there is no
+ * BOM, return -1; otherwise return the result of looks_utf8 on the
+ * rest of the text.
+ */
+file_private int
+looks_utf8_with_BOM(const unsigned char *buf, size_t nbytes,
+ file_unichar_t *ubuf, size_t *ulen)
+{
+ if (nbytes > 3 && buf[0] == 0xef && buf[1] == 0xbb && buf[2] == 0xbf)
+ return file_looks_utf8(buf + 3, nbytes - 3, ubuf, ulen);
+ else
+ return -1;
+}
+
+file_private int
+looks_utf7(const unsigned char *buf, size_t nbytes, file_unichar_t *ubuf,
+ size_t *ulen)
+{
+ if (nbytes > 4 && buf[0] == '+' && buf[1] == '/' && buf[2] == 'v')
+ switch (buf[3]) {
+ case '8':
+ case '9':
+ case '+':
+ case '/':
+ if (ubuf)
+ *ulen = 0;
+ return 1;
+ default:
+ return -1;
+ }
+ else
+ return -1;
+}
+
+#define UCS16_NOCHAR(c) ((c) >= 0xfdd0 && (c) <= 0xfdef)
+#define UCS16_HISURR(c) ((c) >= 0xd800 && (c) <= 0xdbff)
+#define UCS16_LOSURR(c) ((c) >= 0xdc00 && (c) <= 0xdfff)
+
+file_private int
+looks_ucs16(const unsigned char *bf, size_t nbytes, file_unichar_t *ubf,
+ size_t *ulen)
+{
+ int bigend;
+ uint32_t hi;
+ size_t i;
+
+ if (nbytes < 2)
+ return 0;
+
+ if (bf[0] == 0xff && bf[1] == 0xfe)
+ bigend = 0;
+ else if (bf[0] == 0xfe && bf[1] == 0xff)
+ bigend = 1;
+ else
+ return 0;
+
+ *ulen = 0;
+ hi = 0;
+
+ for (i = 2; i + 1 < nbytes; i += 2) {
+ uint32_t uc;
+
+ if (bigend)
+ uc = CAST(uint32_t,
+ bf[i + 1] | (CAST(file_unichar_t, bf[i]) << 8));
+ else
+ uc = CAST(uint32_t,
+ bf[i] | (CAST(file_unichar_t, bf[i + 1]) << 8));
+
+ uc &= 0xffff;
+
+ switch (uc) {
+ case 0xfffe:
+ case 0xffff:
+ return 0;
+ default:
+ if (UCS16_NOCHAR(uc))
+ return 0;
+ break;
+ }
+ if (hi) {
+ if (!UCS16_LOSURR(uc))
+ return 0;
+ uc = 0x10000 + 0x400 * (hi - 1) + (uc - 0xdc00);
+ hi = 0;
+ }
+ if (uc < 128 && text_chars[CAST(size_t, uc)] != T)
+ return 0;
+ ubf[(*ulen)++] = uc;
+ if (UCS16_HISURR(uc))
+ hi = uc - 0xd800 + 1;
+ if (UCS16_LOSURR(uc))
+ return 0;
+ }
+
+ return 1 + bigend;
+}
+
+file_private int
+looks_ucs32(const unsigned char *bf, size_t nbytes, file_unichar_t *ubf,
+ size_t *ulen)
+{
+ int bigend;
+ size_t i;
+
+ if (nbytes < 4)
+ return 0;
+
+ if (bf[0] == 0xff && bf[1] == 0xfe && bf[2] == 0 && bf[3] == 0)
+ bigend = 0;
+ else if (bf[0] == 0 && bf[1] == 0 && bf[2] == 0xfe && bf[3] == 0xff)
+ bigend = 1;
+ else
+ return 0;
+
+ *ulen = 0;
+
+ for (i = 4; i + 3 < nbytes; i += 4) {
+ /* XXX fix to properly handle chars > 65536 */
+
+ if (bigend)
+ ubf[(*ulen)++] = CAST(file_unichar_t, bf[i + 3])
+ | (CAST(file_unichar_t, bf[i + 2]) << 8)
+ | (CAST(file_unichar_t, bf[i + 1]) << 16)
+ | (CAST(file_unichar_t, bf[i]) << 24);
+ else
+ ubf[(*ulen)++] = CAST(file_unichar_t, bf[i + 0])
+ | (CAST(file_unichar_t, bf[i + 1]) << 8)
+ | (CAST(file_unichar_t, bf[i + 2]) << 16)
+ | (CAST(file_unichar_t, bf[i + 3]) << 24);
+
+ if (ubf[*ulen - 1] == 0xfffe)
+ return 0;
+ if (ubf[*ulen - 1] < 128 &&
+ text_chars[CAST(size_t, ubf[*ulen - 1])] != T)
+ return 0;
+ }
+
+ return 1 + bigend;
+}
+#undef F
+#undef T
+#undef I
+#undef X
+
+/*
+ * This table maps each EBCDIC character to an (8-bit extended) ASCII
+ * character, as specified in the rationale for the dd(1) command in
+ * draft 11.2 (September, 1991) of the POSIX P1003.2 standard.
+ *
+ * Unfortunately it does not seem to correspond exactly to any of the
+ * five variants of EBCDIC documented in IBM's _Enterprise Systems
+ * Architecture/390: Principles of Operation_, SA22-7201-06, Seventh
+ * Edition, July, 1999, pp. I-1 - I-4.
+ *
+ * Fortunately, though, all versions of EBCDIC, including this one, agree
+ * on most of the printing characters that also appear in (7-bit) ASCII.
+ * Of these, only '|', '!', '~', '^', '[', and ']' are in question at all.
+ *
+ * Fortunately too, there is general agreement that codes 0x00 through
+ * 0x3F represent control characters, 0x41 a nonbreaking space, and the
+ * remainder printing characters.
+ *
+ * This is sufficient to allow us to identify EBCDIC text and to distinguish
+ * between old-style and internationalized examples of text.
+ */
+
+file_private unsigned char ebcdic_to_ascii[] = {
+ 0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31,
+128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7,
+144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26,
+' ', 160, 161, 162, 163, 164, 165, 166, 167, 168, 213, '.', '<', '(', '+', '|',
+'&', 169, 170, 171, 172, 173, 174, 175, 176, 177, '!', '$', '*', ')', ';', '~',
+'-', '/', 178, 179, 180, 181, 182, 183, 184, 185, 203, ',', '%', '_', '>', '?',
+186, 187, 188, 189, 190, 191, 192, 193, 194, '`', ':', '#', '@', '\'','=', '"',
+195, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 196, 197, 198, 199, 200, 201,
+202, 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', '^', 204, 205, 206, 207, 208,
+209, 229, 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 210, 211, 212, '[', 214, 215,
+216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, ']', 230, 231,
+'{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 232, 233, 234, 235, 236, 237,
+'}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 238, 239, 240, 241, 242, 243,
+'\\',159, 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 244, 245, 246, 247, 248, 249,
+'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 250, 251, 252, 253, 254, 255
+};
+
+#ifdef notdef
+/*
+ * The following EBCDIC-to-ASCII table may relate more closely to reality,
+ * or at least to modern reality. It comes from
+ *
+ * http://ftp.s390.ibm.com/products/oe/bpxqp9.html
+ *
+ * and maps the characters of EBCDIC code page 1047 (the code used for
+ * Unix-derived software on IBM's 390 systems) to the corresponding
+ * characters from ISO 8859-1.
+ *
+ * If this table is used instead of the above one, some of the special
+ * cases for the NEL character can be taken out of the code.
+ */
+
+file_private unsigned char ebcdic_1047_to_8859[] = {
+0x00,0x01,0x02,0x03,0x9C,0x09,0x86,0x7F,0x97,0x8D,0x8E,0x0B,0x0C,0x0D,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x9D,0x0A,0x08,0x87,0x18,0x19,0x92,0x8F,0x1C,0x1D,0x1E,0x1F,
+0x80,0x81,0x82,0x83,0x84,0x85,0x17,0x1B,0x88,0x89,0x8A,0x8B,0x8C,0x05,0x06,0x07,
+0x90,0x91,0x16,0x93,0x94,0x95,0x96,0x04,0x98,0x99,0x9A,0x9B,0x14,0x15,0x9E,0x1A,
+0x20,0xA0,0xE2,0xE4,0xE0,0xE1,0xE3,0xE5,0xE7,0xF1,0xA2,0x2E,0x3C,0x28,0x2B,0x7C,
+0x26,0xE9,0xEA,0xEB,0xE8,0xED,0xEE,0xEF,0xEC,0xDF,0x21,0x24,0x2A,0x29,0x3B,0x5E,
+0x2D,0x2F,0xC2,0xC4,0xC0,0xC1,0xC3,0xC5,0xC7,0xD1,0xA6,0x2C,0x25,0x5F,0x3E,0x3F,
+0xF8,0xC9,0xCA,0xCB,0xC8,0xCD,0xCE,0xCF,0xCC,0x60,0x3A,0x23,0x40,0x27,0x3D,0x22,
+0xD8,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0xAB,0xBB,0xF0,0xFD,0xFE,0xB1,
+0xB0,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0xAA,0xBA,0xE6,0xB8,0xC6,0xA4,
+0xB5,0x7E,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0xA1,0xBF,0xD0,0x5B,0xDE,0xAE,
+0xAC,0xA3,0xA5,0xB7,0xA9,0xA7,0xB6,0xBC,0xBD,0xBE,0xDD,0xA8,0xAF,0x5D,0xB4,0xD7,
+0x7B,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0xAD,0xF4,0xF6,0xF2,0xF3,0xF5,
+0x7D,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0xB9,0xFB,0xFC,0xF9,0xFA,0xFF,
+0x5C,0xF7,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0xB2,0xD4,0xD6,0xD2,0xD3,0xD5,
+0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0xB3,0xDB,0xDC,0xD9,0xDA,0x9F
+};
+#endif
+
+/*
+ * Copy buf[0 ... nbytes-1] into out[], translating EBCDIC to ASCII.
+ */
+file_private void
+from_ebcdic(const unsigned char *buf, size_t nbytes, unsigned char *out)
+{
+ size_t i;
+
+ for (i = 0; i < nbytes; i++) {
+ out[i] = ebcdic_to_ascii[buf[i]];
+ }
+}
diff --git a/contrib/libs/libmagic/src/file.c b/contrib/libs/libmagic/src/file.c
new file mode 100644
index 0000000000..c20391317a
--- /dev/null
+++ b/contrib/libs/libmagic/src/file.c
@@ -0,0 +1,859 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * file - find type of a file or files - main program.
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: file.c,v 1.215 2023/05/21 17:08:34 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef RESTORE_TIME
+# if (__COHERENT__ >= 0x420)
+# include <sys/utime.h>
+# else
+# ifdef USE_UTIMES
+# include <sys/time.h>
+# else
+# include <utime.h>
+# endif
+# endif
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for read() */
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+#ifdef HAVE_WCTYPE_H
+#include <wctype.h>
+#endif
+#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) && \
+ defined(HAVE_WCTYPE_H)
+#define FILE_WIDE_SUPPORT
+#else
+#include <ctype.h>
+#endif
+
+#if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
+# include <getopt.h>
+# ifndef HAVE_GETOPT_LONG
+int getopt_long(int, char * const *, const char *,
+ const struct option *, int *);
+# endif
+# else
+# error #include "mygetopt.h"
+#endif
+
+#ifdef S_IFLNK
+# define IFLNK_h "h"
+# define IFLNK_L "L"
+#else
+# define IFLNK_h ""
+# define IFLNK_L ""
+#endif
+
+#define FILE_FLAGS "bcCdE" IFLNK_h "ik" IFLNK_L "lNnprsSvzZ0"
+#define OPTSTRING "bcCde:Ef:F:hiklLm:nNpP:rsSvzZ0"
+
+# define USAGE \
+ "Usage: %s [-" FILE_FLAGS "] [--apple] [--extension] [--mime-encoding]\n" \
+ " [--mime-type] [-e <testname>] [-F <separator>] " \
+ " [-f <namefile>]\n" \
+ " [-m <magicfiles>] [-P <parameter=value>] [--exclude-quiet]\n" \
+ " <file> ...\n" \
+ " %s -C [-m <magicfiles>]\n" \
+ " %s [--help]\n"
+
+file_private int /* Global command-line options */
+ bflag = 0, /* brief output format */
+ nopad = 0, /* Don't pad output */
+ nobuffer = 0, /* Do not buffer stdout */
+ nulsep = 0; /* Append '\0' to the separator */
+
+file_private const char *separator = ":"; /* Default field separator */
+file_private const struct option long_options[] = {
+#define OPT_HELP 1
+#define OPT_APPLE 2
+#define OPT_EXTENSIONS 3
+#define OPT_MIME_TYPE 4
+#define OPT_MIME_ENCODING 5
+#define OPT_EXCLUDE_QUIET 6
+#define OPT(shortname, longname, opt, def, doc) \
+ {longname, opt, NULL, shortname},
+#define OPT_LONGONLY(longname, opt, def, doc, id) \
+ {longname, opt, NULL, id},
+#include "file_opts.h"
+#undef OPT
+#undef OPT_LONGONLY
+ {0, 0, NULL, 0}
+ };
+
+file_private const struct {
+ const char *name;
+ int value;
+} nv[] = {
+ { "apptype", MAGIC_NO_CHECK_APPTYPE },
+ { "ascii", MAGIC_NO_CHECK_ASCII },
+ { "cdf", MAGIC_NO_CHECK_CDF },
+ { "compress", MAGIC_NO_CHECK_COMPRESS },
+ { "csv", MAGIC_NO_CHECK_CSV },
+ { "elf", MAGIC_NO_CHECK_ELF },
+ { "encoding", MAGIC_NO_CHECK_ENCODING },
+ { "soft", MAGIC_NO_CHECK_SOFT },
+ { "tar", MAGIC_NO_CHECK_TAR },
+ { "json", MAGIC_NO_CHECK_JSON },
+ { "simh", MAGIC_NO_CHECK_SIMH },
+ { "text", MAGIC_NO_CHECK_TEXT }, /* synonym for ascii */
+ { "tokens", MAGIC_NO_CHECK_TOKENS }, /* OBSOLETE: ignored for backwards compatibility */
+};
+
+file_private struct {
+ const char *name;
+ size_t value;
+ size_t def;
+ const char *desc;
+ int tag;
+ int set;
+} pm[] = {
+ { "bytes", 0, FILE_BYTES_MAX, "max bytes to look inside file",
+ MAGIC_PARAM_BYTES_MAX, 0 },
+ { "elf_notes", 0, FILE_ELF_NOTES_MAX, "max ELF notes processed",
+ MAGIC_PARAM_ELF_NOTES_MAX, 0 },
+ { "elf_phnum", 0, FILE_ELF_PHNUM_MAX, "max ELF prog sections processed",
+ MAGIC_PARAM_ELF_PHNUM_MAX, 0 },
+ { "elf_shnum", 0, FILE_ELF_SHNUM_MAX, "max ELF sections processed",
+ MAGIC_PARAM_ELF_SHNUM_MAX, 0 },
+ { "elf_shsize", 0, FILE_ELF_SHSIZE_MAX, "max ELF section size",
+ MAGIC_PARAM_ELF_SHSIZE_MAX, 0 },
+ { "encoding", 0, FILE_ENCODING_MAX, "max bytes to scan for encoding",
+ MAGIC_PARAM_ENCODING_MAX, 0 },
+ { "indir", 0, FILE_INDIR_MAX, "recursion limit for indirection",
+ MAGIC_PARAM_INDIR_MAX, 0 },
+ { "name", 0, FILE_NAME_MAX, "use limit for name/use magic",
+ MAGIC_PARAM_NAME_MAX, 0 },
+ { "regex", 0, FILE_REGEX_MAX, "length limit for REGEX searches",
+ MAGIC_PARAM_REGEX_MAX, 0 },
+};
+
+file_private int posixly;
+
+#ifdef __dead
+__dead
+#endif
+file_private void usage(void);
+file_private void docprint(const char *, int);
+#ifdef __dead
+__dead
+#endif
+file_private void help(void);
+
+file_private int unwrap(struct magic_set *, const char *);
+file_private int process(struct magic_set *ms, const char *, int);
+file_private struct magic_set *load(const char *, int);
+file_private void setparam(const char *);
+file_private void applyparam(magic_t);
+
+
+/*
+ * main - parse arguments and handle options
+ */
+int
+main(int argc, char *argv[])
+{
+ int c;
+ size_t i, j, wid, nw;
+ int action = 0, didsomefiles = 0, errflg = 0;
+ int flags = 0, e = 0;
+#ifdef HAVE_LIBSECCOMP
+ int sandbox = 1;
+#endif
+ struct magic_set *magic = NULL;
+ int longindex;
+ const char *magicfile = NULL; /* where the magic is */
+ char *progname;
+
+ /* makes islower etc work for other langs */
+ (void)setlocale(LC_CTYPE, "");
+
+#ifdef __EMX__
+ /* sh-like wildcard expansion! Shouldn't hurt at least ... */
+ _wildcard(&argc, &argv);
+#endif
+
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ file_setprogname(progname);
+
+
+#ifdef S_IFLNK
+ posixly = getenv("POSIXLY_CORRECT") != NULL;
+ flags |= posixly ? MAGIC_SYMLINK : 0;
+#endif
+ while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
+ &longindex)) != -1)
+ switch (c) {
+ case OPT_HELP:
+ help();
+ break;
+ case OPT_APPLE:
+ flags |= MAGIC_APPLE;
+ break;
+ case OPT_EXTENSIONS:
+ flags |= MAGIC_EXTENSION;
+ break;
+ case OPT_MIME_TYPE:
+ flags |= MAGIC_MIME_TYPE;
+ break;
+ case OPT_MIME_ENCODING:
+ flags |= MAGIC_MIME_ENCODING;
+ break;
+ case '0':
+ nulsep++;
+ break;
+ case 'b':
+ bflag++;
+ break;
+ case 'c':
+ action = FILE_CHECK;
+ break;
+ case 'C':
+ action = FILE_COMPILE;
+ break;
+ case 'd':
+ flags |= MAGIC_DEBUG|MAGIC_CHECK;
+ break;
+ case 'E':
+ flags |= MAGIC_ERROR;
+ break;
+ case 'e':
+ case OPT_EXCLUDE_QUIET:
+ for (i = 0; i < __arraycount(nv); i++)
+ if (strcmp(nv[i].name, optarg) == 0)
+ break;
+
+ if (i == __arraycount(nv)) {
+ if (c != OPT_EXCLUDE_QUIET)
+ errflg++;
+ } else
+ flags |= nv[i].value;
+ break;
+
+ case 'f':
+ if(action)
+ usage();
+ if (magic == NULL)
+ if ((magic = load(magicfile, flags)) == NULL)
+ return 1;
+ applyparam(magic);
+ e |= unwrap(magic, optarg);
+ ++didsomefiles;
+ break;
+ case 'F':
+ separator = optarg;
+ break;
+ case 'i':
+ flags |= MAGIC_MIME;
+ break;
+ case 'k':
+ flags |= MAGIC_CONTINUE;
+ break;
+ case 'l':
+ action = FILE_LIST;
+ break;
+ case 'm':
+ magicfile = optarg;
+ break;
+ case 'n':
+ ++nobuffer;
+ break;
+ case 'N':
+ ++nopad;
+ break;
+#if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
+ case 'p':
+ flags |= MAGIC_PRESERVE_ATIME;
+ break;
+#endif
+ case 'P':
+ setparam(optarg);
+ break;
+ case 'r':
+ flags |= MAGIC_RAW;
+ break;
+ case 's':
+ flags |= MAGIC_DEVICES;
+ break;
+ case 'S':
+#ifdef HAVE_LIBSECCOMP
+ sandbox = 0;
+#endif
+ break;
+ case 'v':
+ if (magicfile == NULL)
+ magicfile = magic_getpath(magicfile, action);
+ (void)fprintf(stdout, "%s-%s\n", file_getprogname(),
+ VERSION);
+ (void)fprintf(stdout, "magic file from %s\n",
+ magicfile);
+#ifdef HAVE_LIBSECCOMP
+ (void)fprintf(stdout, "seccomp support included\n");
+#endif
+ return 0;
+ case 'z':
+ flags |= MAGIC_COMPRESS;
+ break;
+
+ case 'Z':
+ flags |= MAGIC_COMPRESS|MAGIC_COMPRESS_TRANSP;
+ break;
+#ifdef S_IFLNK
+ case 'L':
+ flags |= MAGIC_SYMLINK;
+ break;
+ case 'h':
+ flags &= ~MAGIC_SYMLINK;
+ break;
+#endif
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+
+ if (errflg) {
+ usage();
+ }
+ if (e)
+ return e;
+
+#ifdef HAVE_LIBSECCOMP
+#if 0
+ if (sandbox && enable_sandbox_basic() == -1)
+#else
+ if (sandbox && enable_sandbox_full() == -1)
+#endif
+ file_err(EXIT_FAILURE, "SECCOMP initialisation failed");
+ if (sandbox)
+ flags |= MAGIC_NO_COMPRESS_FORK;
+#endif /* HAVE_LIBSECCOMP */
+
+ if (MAGIC_VERSION != magic_version())
+ file_warnx("Compiled magic version [%d] "
+ "does not match with shared library magic version [%d]\n",
+ MAGIC_VERSION, magic_version());
+
+ switch(action) {
+ case FILE_CHECK:
+ case FILE_COMPILE:
+ case FILE_LIST:
+ /*
+ * Don't try to check/compile ~/.magic unless we explicitly
+ * ask for it.
+ */
+ magic = magic_open(flags|MAGIC_CHECK);
+ if (magic == NULL) {
+ file_warn("Can't create magic");
+ return 1;
+ }
+
+
+ switch(action) {
+ case FILE_CHECK:
+ c = magic_check(magic, magicfile);
+ break;
+ case FILE_COMPILE:
+ c = magic_compile(magic, magicfile);
+ break;
+ case FILE_LIST:
+ c = magic_list(magic, magicfile);
+ break;
+ default:
+ abort();
+ }
+ if (c == -1) {
+ file_warnx("%s", magic_error(magic));
+ e = 1;
+ goto out;
+ }
+ goto out;
+ default:
+ if (magic == NULL)
+ if ((magic = load(magicfile, flags)) == NULL)
+ return 1;
+ applyparam(magic);
+ }
+
+ if (optind == argc) {
+ if (!didsomefiles)
+ usage();
+ goto out;
+ }
+
+ for (wid = 0, j = CAST(size_t, optind); j < CAST(size_t, argc);
+ j++) {
+ nw = file_mbswidth(magic, argv[j]);
+ if (nw > wid)
+ wid = nw;
+ }
+
+ /*
+ * If bflag is only set twice, set it depending on
+ * number of files [this is undocumented, and subject to change]
+ */
+ if (bflag == 2) {
+ bflag = optind >= argc - 1;
+ }
+ for (; optind < argc; optind++)
+ e |= process(magic, argv[optind], wid);
+
+out:
+ if (!nobuffer)
+ e |= fflush(stdout) != 0;
+
+ if (magic)
+ magic_close(magic);
+ return e;
+}
+
+file_private void
+applyparam(magic_t magic)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(pm); i++) {
+ if (!pm[i].set)
+ continue;
+ if (magic_setparam(magic, pm[i].tag, &pm[i].value) == -1)
+ file_err(EXIT_FAILURE, "Can't set %s", pm[i].name);
+ }
+}
+
+file_private void
+setparam(const char *p)
+{
+ size_t i;
+ char *s;
+
+ if ((s = CCAST(char *, strchr(p, '='))) == NULL)
+ goto badparm;
+
+ for (i = 0; i < __arraycount(pm); i++) {
+ if (strncmp(p, pm[i].name, s - p) != 0)
+ continue;
+ pm[i].value = atoi(s + 1);
+ pm[i].set = 1;
+ return;
+ }
+badparm:
+ file_errx(EXIT_FAILURE, "Unknown param %s", p);
+}
+
+file_private struct magic_set *
+/*ARGSUSED*/
+load(const char *magicfile, int flags)
+{
+ struct magic_set *magic = magic_open(flags);
+ const char *e;
+
+ if (magic == NULL) {
+ file_warn("Can't create magic");
+ return NULL;
+ }
+ if (magic_load(magic, magicfile) == -1) {
+ file_warn("%s", magic_error(magic));
+ magic_close(magic);
+ return NULL;
+ }
+ if ((e = magic_error(magic)) != NULL)
+ file_warn("%s", e);
+ return magic;
+}
+
+/*
+ * unwrap -- read a file of filenames, do each one.
+ */
+file_private int
+unwrap(struct magic_set *ms, const char *fn)
+{
+ FILE *f;
+ ssize_t len;
+ char *line = NULL;
+ size_t llen = 0;
+ int wid = 0, cwid;
+ int e = 0;
+ size_t fi = 0, fimax = 0;
+ char **flist = NULL;
+
+ if (strcmp("-", fn) == 0)
+ f = stdin;
+ else {
+ if ((f = fopen(fn, "r")) == NULL) {
+ file_warn("Cannot open `%s'", fn);
+ return 1;
+ }
+ }
+
+ while ((len = getline(&line, &llen, f)) > 0) {
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ cwid = file_mbswidth(ms, line);
+ if (nobuffer) {
+ e |= process(ms, line, cwid);
+ free(line);
+ line = NULL;
+ llen = 0;
+ continue;
+ }
+ if (cwid > wid)
+ wid = cwid;
+ if (fi >= fimax) {
+ fimax += 100;
+ char **nf = CAST(char **,
+ realloc(flist, fimax * sizeof(*flist)));
+ if (nf == NULL) {
+ file_err(EXIT_FAILURE,
+ "Cannot allocate memory for file list");
+ }
+ flist = nf;
+ }
+ flist[fi++] = line;
+ line = NULL;
+ llen = 0;
+ }
+
+ if (!nobuffer) {
+ fimax = fi;
+ for (fi = 0; fi < fimax; fi++) {
+ e |= process(ms, flist[fi], wid);
+ free(flist[fi]);
+ }
+ }
+ free(flist);
+
+ if (f != stdin)
+ (void)fclose(f);
+ return e;
+}
+
+file_private void
+file_octal(unsigned char c)
+{
+ (void)putc('\\', stdout);
+ (void)putc(((c >> 6) & 7) + '0', stdout);
+ (void)putc(((c >> 3) & 7) + '0', stdout);
+ (void)putc(((c >> 0) & 7) + '0', stdout);
+}
+
+file_private void
+fname_print(const char *inname)
+{
+ size_t n = strlen(inname);
+#ifdef FILE_WIDE_SUPPORT
+ mbstate_t state;
+ wchar_t nextchar;
+ size_t bytesconsumed;
+
+
+ (void)memset(&state, 0, sizeof(state));
+ while (n > 0) {
+ bytesconsumed = mbrtowc(&nextchar, inname, n, &state);
+ if (bytesconsumed == CAST(size_t, -1) ||
+ bytesconsumed == CAST(size_t, -2)) {
+ nextchar = *inname++;
+ n--;
+ (void)memset(&state, 0, sizeof(state));
+ file_octal(CAST(unsigned char, nextchar));
+ continue;
+ }
+ inname += bytesconsumed;
+ n -= bytesconsumed;
+ if (iswprint(nextchar)) {
+ printf("%lc", (wint_t)nextchar);
+ continue;
+ }
+ /* XXX: What if it is > 255? */
+ file_octal(CAST(unsigned char, nextchar));
+ }
+#else
+ size_t i;
+ for (i = 0; i < n; i++) {
+ unsigned char c = CAST(unsigned char, inname[i]);
+ if (isprint(c)) {
+ (void)putc(c, stdout);
+ continue;
+ }
+ file_octal(c);
+ }
+#endif
+}
+
+/*
+ * Called for each input file on the command line (or in a list of files)
+ */
+file_private int
+process(struct magic_set *ms, const char *inname, int wid)
+{
+ const char *type, c = nulsep > 1 ? '\0' : '\n';
+ int std_in = strcmp(inname, "-") == 0;
+ int haderror = 0;
+
+ if (wid > 0 && !bflag) {
+ const char *pname = std_in ? "/dev/stdin" : inname;
+ if ((ms->flags & MAGIC_RAW) == 0)
+ fname_print(pname);
+ else
+ (void)printf("%s", pname);
+ if (nulsep)
+ (void)putc('\0', stdout);
+ if (nulsep < 2) {
+ (void)printf("%s", separator);
+ (void)printf("%*s ", CAST(int, nopad ? 0
+ : (wid - file_mbswidth(ms, inname))), "");
+ }
+ }
+
+ type = magic_file(ms, std_in ? NULL : inname);
+
+ if (type == NULL) {
+ haderror |= printf("ERROR: %s%c", magic_error(ms), c);
+ } else {
+ haderror |= printf("%s%c", type, c) < 0;
+ }
+ if (nobuffer)
+ haderror |= fflush(stdout) != 0;
+ return haderror || type == NULL;
+}
+
+file_protected size_t
+file_mbswidth(struct magic_set *ms, const char *s)
+{
+ size_t width = 0;
+#ifdef FILE_WIDE_SUPPORT
+ size_t bytesconsumed, n;
+ mbstate_t state;
+ wchar_t nextchar;
+
+ (void)memset(&state, 0, sizeof(state));
+ n = strlen(s);
+
+ while (n > 0) {
+ bytesconsumed = mbrtowc(&nextchar, s, n, &state);
+ if (bytesconsumed == CAST(size_t, -1) ||
+ bytesconsumed == CAST(size_t, -2)) {
+ nextchar = *s;
+ bytesconsumed = 1;
+ (void)memset(&state, 0, sizeof(state));
+ width += 4;
+ } else {
+ int w = wcwidth(nextchar);
+ width += ((ms->flags & MAGIC_RAW) != 0
+ || iswprint(nextchar)) ? (w > 0 ? w : 1) : 4;
+ }
+
+ s += bytesconsumed, n -= bytesconsumed;
+ }
+#else
+ for (; *s; s++) {
+ width += (ms->flags & MAGIC_RAW) != 0
+ || isprint(CAST(unsigned char, *s)) ? 1 : 4;
+ }
+#endif
+ return width;
+}
+
+file_private void
+usage(void)
+{
+ const char *pn = file_getprogname();
+ (void)fprintf(stderr, USAGE, pn, pn, pn);
+ exit(EXIT_FAILURE);
+}
+
+file_private void
+defprint(int def)
+{
+ if (!def)
+ return;
+ if (((def & 1) && posixly) || ((def & 2) && !posixly))
+ (void)fprintf(stdout, " (default)");
+ (void)putc('\n', stdout);
+}
+
+file_private void
+docprint(const char *opts, int def)
+{
+ size_t i;
+ int comma, pad;
+ char *sp, *p;
+
+ p = CCAST(char *, strchr(opts, '%'));
+ if (p == NULL) {
+ (void)fprintf(stdout, "%s", opts);
+ defprint(def);
+ return;
+ }
+
+ for (sp = p - 1; sp > opts && *sp == ' '; sp--)
+ continue;
+
+ (void)printf("%.*s", CAST(int, p - opts), opts);
+ pad = (int)CAST(int, p - sp - 1);
+
+ switch (*++p) {
+ case 'e':
+ comma = 0;
+ for (i = 0; i < __arraycount(nv); i++) {
+ (void)printf("%s%s", comma++ ? ", " : "", nv[i].name);
+ if (i && i % 5 == 0 && i != __arraycount(nv) - 1) {
+ (void)printf(",\n%*s", pad, "");
+ comma = 0;
+ }
+ }
+ break;
+ case 'P':
+ for (i = 0; i < __arraycount(pm); i++) {
+ (void)printf("%9s %7zu %s", pm[i].name, pm[i].def,
+ pm[i].desc);
+ if (i != __arraycount(pm) - 1)
+ (void)printf("\n%*s", pad, "");
+ }
+ break;
+ default:
+ file_errx(EXIT_FAILURE, "Unknown escape `%c' in long options",
+ *p);
+ break;
+ }
+ (void)printf("%s", opts + (p - opts) + 1);
+
+}
+
+file_private void
+help(void)
+{
+ (void)fputs(
+"Usage: file [OPTION...] [FILE...]\n"
+"Determine type of FILEs.\n"
+"\n", stdout);
+#define OPT(shortname, longname, opt, def, doc) \
+ (void)printf(" -%c, --" longname, shortname), \
+ docprint(doc, def);
+#define OPT_LONGONLY(longname, opt, def, doc, id) \
+ (void)printf(" --" longname), \
+ docprint(doc, def);
+#include "file_opts.h"
+#undef OPT
+#undef OPT_LONGONLY
+ (void)printf("\nReport bugs to https://bugs.astron.com/\n");
+ exit(EXIT_SUCCESS);
+}
+
+file_private const char *file_progname;
+
+file_protected void
+file_setprogname(const char *progname)
+{
+ file_progname = progname;
+}
+
+file_protected const char *
+file_getprogname(void)
+{
+ return file_progname;
+}
+
+file_protected void
+file_err(int e, const char *fmt, ...)
+{
+ va_list ap;
+ int se = errno;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", file_progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (se)
+ (void)fprintf(stderr, " (%s)\n", strerror(se));
+ else
+ fputc('\n', stderr);
+ exit(e);
+}
+
+file_protected void
+file_errx(int e, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", file_progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(e);
+}
+
+file_protected void
+file_warn(const char *fmt, ...)
+{
+ va_list ap;
+ int se = errno;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", file_progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (se)
+ (void)fprintf(stderr, " (%s)\n", strerror(se));
+ else
+ fputc('\n', stderr);
+ errno = se;
+}
+
+file_protected void
+file_warnx(const char *fmt, ...)
+{
+ va_list ap;
+ int se = errno;
+
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "%s: ", file_progname);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ errno = se;
+}
diff --git a/contrib/libs/libmagic/src/file.h b/contrib/libs/libmagic/src/file.h
new file mode 100644
index 0000000000..2e0494d2fd
--- /dev/null
+++ b/contrib/libs/libmagic/src/file.h
@@ -0,0 +1,729 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * file.h - definitions for file(1) program
+ * @(#)$File: file.h,v 1.247 2023/07/27 19:40:22 christos Exp $
+ */
+
+#ifndef __file_h__
+#define __file_h__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
+#ifdef _WIN32
+# ifdef PRIu32
+# ifdef _WIN64
+# define SIZE_T_FORMAT PRIu64
+# else
+# define SIZE_T_FORMAT PRIu32
+# endif
+# define INT64_T_FORMAT PRIi64
+# define INTMAX_T_FORMAT PRIiMAX
+# else
+# ifdef _WIN64
+# define SIZE_T_FORMAT "I64"
+# else
+# define SIZE_T_FORMAT ""
+# endif
+# define INT64_T_FORMAT "I64"
+# define INTMAX_T_FORMAT "I64"
+# endif
+#else
+# define SIZE_T_FORMAT "z"
+# define INT64_T_FORMAT "ll"
+# define INTMAX_T_FORMAT "j"
+#endif
+
+#include <stdio.h> /* Include that here, to make sure __P gets defined */
+#include <errno.h>
+#include <fcntl.h> /* For open and flags */
+#include <regex.h>
+#include <time.h>
+#include <sys/types.h>
+#ifndef WIN32
+#include <sys/param.h>
+#endif
+/* Do this here and now, because struct stat gets re-defined on solaris */
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <locale.h>
+#if defined(HAVE_XLOCALE_H)
+#include <xlocale.h>
+#endif
+
+#define ENABLE_CONDITIONALS
+
+#ifndef MAGIC
+#define MAGIC "/etc/magic"
+#endif
+
+#if defined(__EMX__) || defined (WIN32)
+#define PATHSEP ';'
+#else
+#define PATHSEP ':'
+#endif
+
+#define file_private static
+
+#if HAVE_VISIBILITY && !defined(WIN32)
+#define file_public __attribute__ ((__visibility__("default")))
+#ifndef file_protected
+#define file_protected __attribute__ ((__visibility__("hidden")))
+#endif
+#else
+#define file_public
+#ifndef file_protected
+#define file_protected
+#endif
+#endif
+
+#ifndef __arraycount
+#define __arraycount(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+#ifndef __GNUC_PREREQ__
+#ifdef __GNUC__
+#define __GNUC_PREREQ__(x, y) \
+ ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \
+ (__GNUC__ > (x)))
+#else
+#define __GNUC_PREREQ__(x, y) 0
+#endif
+#endif
+
+#ifndef __GNUC__
+#ifndef __attribute__
+#define __attribute__(a)
+#endif
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+
+#ifndef FD_CLOEXEC
+# define FD_CLOEXEC 1
+#endif
+
+
+/*
+ * Dec 31, 23:59:59 9999
+ * we need to make sure that we don't exceed 9999 because some libc
+ * implementations like muslc crash otherwise
+ */
+#define MAX_CTIME CAST(time_t, 0x3afff487cfULL)
+
+#define FILE_BADSIZE CAST(size_t, ~0ul)
+#define MAXDESC 64 /* max len of text description/MIME type */
+#define MAXMIME 80 /* max len of text MIME type */
+#define MAXstring 128 /* max len of "string" types */
+
+#define MAGICNO 0xF11E041C
+#define VERSIONNO 18
+#define FILE_MAGICSIZE 376
+
+#define FILE_GUID_SIZE sizeof("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
+
+#define FILE_LOAD 0
+#define FILE_CHECK 1
+#define FILE_COMPILE 2
+#define FILE_LIST 3
+
+typedef regex_t file_regex_t;
+
+struct buffer {
+ int fd;
+ struct stat st;
+ const void *fbuf;
+ size_t flen;
+ off_t eoff;
+ void *ebuf;
+ size_t elen;
+};
+
+union VALUETYPE {
+ uint8_t b;
+ uint16_t h;
+ uint32_t l;
+ uint64_t q;
+ uint8_t hs[2]; /* 2 bytes of a fixed-endian "short" */
+ uint8_t hl[4]; /* 4 bytes of a fixed-endian "long" */
+ uint8_t hq[8]; /* 8 bytes of a fixed-endian "quad" */
+ char s[MAXstring]; /* the search string or regex pattern */
+ unsigned char us[MAXstring];
+ uint64_t guid[2];
+ float f;
+ double d;
+};
+
+struct magic {
+ /* Word 1 */
+ uint16_t cont_level; /* level of ">" */
+ uint8_t flag;
+#define INDIR 0x01 /* if '(...)' appears */
+#define OFFADD 0x02 /* if '>&' or '>...(&' appears */
+#define INDIROFFADD 0x04 /* if '>&(' appears */
+#define UNSIGNED 0x08 /* comparison is unsigned */
+#define NOSPACE 0x10 /* suppress space character before output */
+#define BINTEST 0x20 /* test is for a binary type (set only
+ for top-level tests) */
+#define TEXTTEST 0x40 /* for passing to file_softmagic */
+#define OFFNEGATIVE 0x80 /* relative to the end of file */
+
+ uint8_t factor;
+
+ /* Word 2 */
+ uint8_t reln; /* relation (0=eq, '>'=gt, etc) */
+ uint8_t vallen; /* length of string value, if any */
+ uint8_t type; /* comparison type (FILE_*) */
+ uint8_t in_type; /* type of indirection */
+#define FILE_INVALID 0
+#define FILE_BYTE 1
+#define FILE_SHORT 2
+#define FILE_DEFAULT 3
+#define FILE_LONG 4
+#define FILE_STRING 5
+#define FILE_DATE 6
+#define FILE_BESHORT 7
+#define FILE_BELONG 8
+#define FILE_BEDATE 9
+#define FILE_LESHORT 10
+#define FILE_LELONG 11
+#define FILE_LEDATE 12
+#define FILE_PSTRING 13
+#define FILE_LDATE 14
+#define FILE_BELDATE 15
+#define FILE_LELDATE 16
+#define FILE_REGEX 17
+#define FILE_BESTRING16 18
+#define FILE_LESTRING16 19
+#define FILE_SEARCH 20
+#define FILE_MEDATE 21
+#define FILE_MELDATE 22
+#define FILE_MELONG 23
+#define FILE_QUAD 24
+#define FILE_LEQUAD 25
+#define FILE_BEQUAD 26
+#define FILE_QDATE 27
+#define FILE_LEQDATE 28
+#define FILE_BEQDATE 29
+#define FILE_QLDATE 30
+#define FILE_LEQLDATE 31
+#define FILE_BEQLDATE 32
+#define FILE_FLOAT 33
+#define FILE_BEFLOAT 34
+#define FILE_LEFLOAT 35
+#define FILE_DOUBLE 36
+#define FILE_BEDOUBLE 37
+#define FILE_LEDOUBLE 38
+#define FILE_BEID3 39
+#define FILE_LEID3 40
+#define FILE_INDIRECT 41
+#define FILE_QWDATE 42
+#define FILE_LEQWDATE 43
+#define FILE_BEQWDATE 44
+#define FILE_NAME 45
+#define FILE_USE 46
+#define FILE_CLEAR 47
+#define FILE_DER 48
+#define FILE_GUID 49
+#define FILE_OFFSET 50
+#define FILE_BEVARINT 51
+#define FILE_LEVARINT 52
+#define FILE_MSDOSDATE 53
+#define FILE_LEMSDOSDATE 54
+#define FILE_BEMSDOSDATE 55
+#define FILE_MSDOSTIME 56
+#define FILE_LEMSDOSTIME 57
+#define FILE_BEMSDOSTIME 58
+#define FILE_OCTAL 59
+#define FILE_NAMES_SIZE 60 /* size of array to contain all names */
+
+#define IS_STRING(t) \
+ ((t) == FILE_STRING || \
+ (t) == FILE_PSTRING || \
+ (t) == FILE_BESTRING16 || \
+ (t) == FILE_LESTRING16 || \
+ (t) == FILE_REGEX || \
+ (t) == FILE_SEARCH || \
+ (t) == FILE_INDIRECT || \
+ (t) == FILE_NAME || \
+ (t) == FILE_USE || \
+ (t) == FILE_OCTAL)
+
+#define FILE_FMT_NONE 0
+#define FILE_FMT_NUM 1 /* "cduxXi" */
+#define FILE_FMT_STR 2 /* "s" */
+#define FILE_FMT_QUAD 3 /* "ll" */
+#define FILE_FMT_FLOAT 4 /* "eEfFgG" */
+#define FILE_FMT_DOUBLE 5 /* "eEfFgG" */
+
+ /* Word 3 */
+ uint8_t in_op; /* operator for indirection */
+ uint8_t mask_op; /* operator for mask */
+#ifdef ENABLE_CONDITIONALS
+ uint8_t cond; /* conditional type */
+#else
+ uint8_t dummy;
+#endif
+ uint8_t factor_op;
+#define FILE_FACTOR_OP_PLUS '+'
+#define FILE_FACTOR_OP_MINUS '-'
+#define FILE_FACTOR_OP_TIMES '*'
+#define FILE_FACTOR_OP_DIV '/'
+#define FILE_FACTOR_OP_NONE '\0'
+
+#define FILE_OPS "&|^+-*/%"
+#define FILE_OPAND 0
+#define FILE_OPOR 1
+#define FILE_OPXOR 2
+#define FILE_OPADD 3
+#define FILE_OPMINUS 4
+#define FILE_OPMULTIPLY 5
+#define FILE_OPDIVIDE 6
+#define FILE_OPMODULO 7
+#define FILE_OPS_MASK 0x07 /* mask for above ops */
+#define FILE_UNUSED_1 0x08
+#define FILE_UNUSED_2 0x10
+#define FILE_OPSIGNED 0x20
+#define FILE_OPINVERSE 0x40
+#define FILE_OPINDIRECT 0x80
+
+#ifdef ENABLE_CONDITIONALS
+#define COND_NONE 0
+#define COND_IF 1
+#define COND_ELIF 2
+#define COND_ELSE 3
+#endif /* ENABLE_CONDITIONALS */
+
+ /* Word 4 */
+ int32_t offset; /* offset to magic number */
+ /* Word 5 */
+ int32_t in_offset; /* offset from indirection */
+ /* Word 6 */
+ uint32_t lineno; /* line number in magic file */
+ /* Word 7,8 */
+ union {
+ uint64_t _mask; /* for use with numeric and date types */
+ struct {
+ uint32_t _count; /* repeat/line count */
+ uint32_t _flags; /* modifier flags */
+ } _s; /* for use with string types */
+ } _u;
+#define num_mask _u._mask
+#define str_range _u._s._count
+#define str_flags _u._s._flags
+ /* Words 9-24 */
+ union VALUETYPE value; /* either number or string */
+ /* Words 25-40 */
+ char desc[MAXDESC]; /* description */
+ /* Words 41-60 */
+ char mimetype[MAXMIME]; /* MIME type */
+ /* Words 61-62 */
+ char apple[8]; /* APPLE CREATOR/TYPE */
+ /* Words 63-78 */
+ char ext[64]; /* Popular extensions */
+};
+
+#define BIT(A) (1 << (A))
+#define STRING_COMPACT_WHITESPACE BIT(0)
+#define STRING_COMPACT_OPTIONAL_WHITESPACE BIT(1)
+#define STRING_IGNORE_LOWERCASE BIT(2)
+#define STRING_IGNORE_UPPERCASE BIT(3)
+#define REGEX_OFFSET_START BIT(4)
+#define STRING_TEXTTEST BIT(5)
+#define STRING_BINTEST BIT(6)
+#define PSTRING_1_BE BIT(7)
+#define PSTRING_1_LE BIT(7)
+#define PSTRING_2_BE BIT(8)
+#define PSTRING_2_LE BIT(9)
+#define PSTRING_4_BE BIT(10)
+#define PSTRING_4_LE BIT(11)
+#define REGEX_LINE_COUNT BIT(11)
+#define PSTRING_LEN \
+ (PSTRING_1_BE|PSTRING_2_LE|PSTRING_2_BE|PSTRING_4_LE|PSTRING_4_BE)
+#define PSTRING_LENGTH_INCLUDES_ITSELF BIT(12)
+#define STRING_TRIM BIT(13)
+#define STRING_FULL_WORD BIT(14)
+#define CHAR_COMPACT_WHITESPACE 'W'
+#define CHAR_COMPACT_OPTIONAL_WHITESPACE 'w'
+#define CHAR_IGNORE_LOWERCASE 'c'
+#define CHAR_IGNORE_UPPERCASE 'C'
+#define CHAR_REGEX_OFFSET_START 's'
+#define CHAR_TEXTTEST 't'
+#define CHAR_TRIM 'T'
+#define CHAR_FULL_WORD 'f'
+#define CHAR_BINTEST 'b'
+#define CHAR_PSTRING_1_BE 'B'
+#define CHAR_PSTRING_1_LE 'B'
+#define CHAR_PSTRING_2_BE 'H'
+#define CHAR_PSTRING_2_LE 'h'
+#define CHAR_PSTRING_4_BE 'L'
+#define CHAR_PSTRING_4_LE 'l'
+#define CHAR_PSTRING_LENGTH_INCLUDES_ITSELF 'J'
+#define STRING_IGNORE_CASE (STRING_IGNORE_LOWERCASE|STRING_IGNORE_UPPERCASE)
+#define STRING_DEFAULT_RANGE 100
+
+#define INDIRECT_RELATIVE BIT(0)
+#define CHAR_INDIRECT_RELATIVE 'r'
+
+/* list of magic entries */
+struct mlist {
+ struct magic *magic; /* array of magic entries */
+ file_regex_t **magic_rxcomp; /* array of compiled regexps */
+ size_t nmagic; /* number of entries in array */
+ void *map; /* internal resources used by entry */
+ struct mlist *next, *prev;
+};
+
+#ifdef __cplusplus
+#define CAST(T, b) static_cast<T>(b)
+#define RCAST(T, b) reinterpret_cast<T>(b)
+#define CCAST(T, b) const_cast<T>(b)
+#else
+#define CAST(T, b) ((T)(b))
+#define RCAST(T, b) ((T)(uintptr_t)(b))
+#define CCAST(T, b) ((T)(uintptr_t)(b))
+#endif
+
+struct level_info {
+ int32_t off;
+ int got_match;
+#ifdef ENABLE_CONDITIONALS
+ int last_match;
+ int last_cond; /* used for error checking by parse() */
+#endif
+};
+
+struct cont {
+ size_t len;
+ struct level_info *li;
+};
+
+#define MAGIC_SETS 2
+
+struct magic_set {
+ struct mlist *mlist[MAGIC_SETS]; /* list of regular entries */
+ struct cont c;
+ struct out {
+ char *buf; /* Accumulation buffer */
+ size_t blen; /* Length of buffer */
+ char *pbuf; /* Printable buffer */
+ } o;
+ uint32_t offset; /* a copy of m->offset while we */
+ /* are working on the magic entry */
+ uint32_t eoffset; /* offset from end of file */
+ int error;
+ int flags; /* Control magic tests. */
+ int event_flags; /* Note things that happened. */
+#define EVENT_HAD_ERR 0x01
+ const char *file;
+ size_t line; /* current magic line number */
+ mode_t mode; /* copy of current stat mode */
+
+ /* data for searches */
+ struct {
+ const char *s; /* start of search in original source */
+ size_t s_len; /* length of search region */
+ size_t offset; /* starting offset in source: XXX - should this be off_t? */
+ size_t rm_len; /* match length */
+ } search;
+
+ /* FIXME: Make the string dynamically allocated so that e.g.
+ strings matched in files can be longer than MAXstring */
+ union VALUETYPE ms_value; /* either number or string */
+ uint16_t indir_max;
+ uint16_t name_max;
+ uint16_t elf_shnum_max;
+ uint16_t elf_phnum_max;
+ uint16_t elf_notes_max;
+ uint16_t regex_max;
+ size_t bytes_max; /* number of bytes to read from file */
+ size_t encoding_max; /* bytes to look for encoding */
+ size_t elf_shsize_max;
+#ifndef FILE_BYTES_MAX
+# define FILE_BYTES_MAX (7 * 1024 * 1024)/* how much of the file to look at */
+#endif /* above 0x6ab0f4 map offset for HelveticaNeue.dfont */
+#define FILE_ELF_NOTES_MAX 256
+#define FILE_ELF_PHNUM_MAX 2048
+#define FILE_ELF_SHNUM_MAX 32768
+#define FILE_ELF_SHSIZE_MAX (128 * 1024 * 1024)
+#define FILE_INDIR_MAX 50
+#define FILE_NAME_MAX 50
+#define FILE_REGEX_MAX 8192
+#define FILE_ENCODING_MAX (64 * 1024)
+#if defined(HAVE_NEWLOCALE) && defined(HAVE_USELOCALE) && defined(HAVE_FREELOCALE)
+#define USE_C_LOCALE
+ locale_t c_lc_ctype;
+#define file_locale_used
+#else
+#define file_locale_used __attribute__((__unused__))
+#endif
+};
+
+/* Type for Unicode characters */
+typedef unsigned long file_unichar_t;
+
+struct stat;
+#define FILE_T_LOCAL 1
+#define FILE_T_WINDOWS 2
+file_protected const char *file_fmtdatetime(char *, size_t, uint64_t, int);
+file_protected const char *file_fmtdate(char *, size_t, uint16_t);
+file_protected const char *file_fmttime(char *, size_t, uint16_t);
+file_protected const char *file_fmtvarint(char *, size_t, const unsigned char *,
+ int);
+file_protected const char *file_fmtnum(char *, size_t, const char *, int);
+file_protected struct magic_set *file_ms_alloc(int);
+file_protected void file_ms_free(struct magic_set *);
+file_protected int file_default(struct magic_set *, size_t);
+file_protected int file_buffer(struct magic_set *, int, struct stat *,
+ const char *, const void *, size_t);
+file_protected int file_fsmagic(struct magic_set *, const char *,
+ struct stat *);
+file_protected int file_pipe2file(struct magic_set *, int, const void *,
+ size_t);
+file_protected int file_vprintf(struct magic_set *, const char *, va_list)
+ __attribute__((__format__(__printf__, 2, 0)));
+file_protected int file_separator(struct magic_set *);
+file_protected char *file_copystr(char *, size_t, size_t, const char *);
+file_protected int file_checkfmt(char *, size_t, const char *);
+file_protected size_t file_printedlen(const struct magic_set *);
+file_protected int file_print_guid(char *, size_t, const uint64_t *);
+file_protected int file_parse_guid(const char *, uint64_t *);
+file_protected int file_replace(struct magic_set *, const char *, const char *);
+file_protected int file_printf(struct magic_set *, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+file_protected int file_reset(struct magic_set *, int);
+file_protected int file_tryelf(struct magic_set *, const struct buffer *);
+file_protected int file_trycdf(struct magic_set *, const struct buffer *);
+#if HAVE_FORK
+file_protected int file_zmagic(struct magic_set *, const struct buffer *,
+ const char *);
+#endif
+file_protected int file_ascmagic(struct magic_set *, const struct buffer *,
+ int);
+file_protected int file_ascmagic_with_encoding(struct magic_set *,
+ const struct buffer *, file_unichar_t *, size_t, const char *, const char *, int);
+file_protected int file_encoding(struct magic_set *, const struct buffer *,
+ file_unichar_t **, size_t *, const char **, const char **, const char **);
+file_protected int file_is_json(struct magic_set *, const struct buffer *);
+file_protected int file_is_csv(struct magic_set *, const struct buffer *, int,
+ const char *);
+file_protected int file_is_simh(struct magic_set *, const struct buffer *);
+file_protected int file_is_tar(struct magic_set *, const struct buffer *);
+file_protected int file_softmagic(struct magic_set *, const struct buffer *,
+ uint16_t *, uint16_t *, int, int);
+file_protected int file_apprentice(struct magic_set *, const char *, int);
+file_protected size_t file_magic_strength(const struct magic *, size_t);
+file_protected int buffer_apprentice(struct magic_set *, struct magic **,
+ size_t *, size_t);
+file_protected int file_magicfind(struct magic_set *, const char *,
+ struct mlist *);
+file_protected uint64_t file_signextend(struct magic_set *, struct magic *,
+ uint64_t);
+file_protected uintmax_t file_varint2uintmax_t(const unsigned char *, int,
+ size_t *);
+
+file_protected void file_badread(struct magic_set *);
+file_protected void file_badseek(struct magic_set *);
+file_protected void file_oomem(struct magic_set *, size_t);
+file_protected void file_error(struct magic_set *, int, const char *, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+file_protected void file_magerror(struct magic_set *, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+file_protected void file_magwarn(struct magic_set *, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+file_protected void file_mdump(struct magic *);
+file_protected void file_showstr(FILE *, const char *, size_t);
+file_protected size_t file_mbswidth(struct magic_set *, const char *);
+file_protected const char *file_getbuffer(struct magic_set *);
+file_protected ssize_t sread(int, void *, size_t, int);
+file_protected int file_check_mem(struct magic_set *, unsigned int);
+file_protected int file_looks_utf8(const unsigned char *, size_t,
+ file_unichar_t *, size_t *);
+file_protected size_t file_pstring_length_size(struct magic_set *,
+ const struct magic *);
+file_protected size_t file_pstring_get_length(struct magic_set *,
+ const struct magic *, const char *);
+file_protected char * file_printable(struct magic_set *, char *, size_t,
+ const char *, size_t);
+#ifdef __EMX__
+file_protected int file_os2_apptype(struct magic_set *, const char *,
+ const void *, size_t);
+#endif /* __EMX__ */
+file_protected int file_pipe_closexec(int *);
+file_protected int file_clear_closexec(int);
+file_protected char *file_strtrim(char *);
+
+file_protected void buffer_init(struct buffer *, int, const struct stat *,
+ const void *, size_t);
+file_protected void buffer_fini(struct buffer *);
+file_protected int buffer_fill(const struct buffer *);
+
+
+
+file_protected int file_regcomp(struct magic_set *, file_regex_t *,
+ const char *, int);
+file_protected int file_regexec(struct magic_set *, file_regex_t *,
+ const char *, size_t, regmatch_t *, int);
+file_protected void file_regfree(file_regex_t *);
+
+typedef struct {
+ char *buf;
+ size_t blen;
+ uint32_t offset;
+} file_pushbuf_t;
+
+file_protected file_pushbuf_t *file_push_buffer(struct magic_set *);
+file_protected char *file_pop_buffer(struct magic_set *, file_pushbuf_t *);
+
+#ifndef COMPILE_ONLY
+extern const char *file_names[];
+extern const size_t file_nnames;
+#endif
+
+#ifndef HAVE_PREAD
+ssize_t pread(int, void *, size_t, off_t);
+#endif
+#ifndef HAVE_VASPRINTF
+int vasprintf(char **, const char *, va_list);
+#endif
+#ifndef HAVE_ASPRINTF
+int asprintf(char **, const char *, ...);
+#endif
+#ifndef HAVE_DPRINTF
+int dprintf(int, const char *, ...);
+#endif
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#endif
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *, const char *, size_t);
+#endif
+#ifndef HAVE_STRCASESTR
+char *strcasestr(const char *, const char *);
+#endif
+#ifndef HAVE_GETLINE
+ssize_t getline(char **, size_t *, FILE *);
+ssize_t getdelim(char **, size_t *, int, FILE *);
+#endif
+#ifndef HAVE_CTIME_R
+char *ctime_r(const time_t *, char *);
+#endif
+#ifndef HAVE_ASCTIME_R
+char *asctime_r(const struct tm *, char *);
+#endif
+#ifndef HAVE_GMTIME_R
+struct tm *gmtime_r(const time_t *, struct tm *);
+#endif
+#ifndef HAVE_LOCALTIME_R
+struct tm *localtime_r(const time_t *, struct tm *);
+#endif
+#ifndef HAVE_FMTCHECK
+const char *fmtcheck(const char *, const char *)
+ __attribute__((__format_arg__(2)));
+#endif
+
+#ifdef HAVE_LIBSECCOMP
+// basic filter
+// this mode should not interfere with normal operations
+// only some dangerous syscalls are blacklisted
+int enable_sandbox_basic(void);
+
+// enhanced filter
+// this mode allows only the necessary syscalls used during normal operation
+// extensive testing required !!!
+int enable_sandbox_full(void);
+#endif
+
+file_protected const char *file_getprogname(void);
+file_protected void file_setprogname(const char *);
+file_protected void file_err(int, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3), __noreturn__));
+file_protected void file_errx(int, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3), __noreturn__));
+file_protected void file_warn(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+file_protected void file_warnx(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+
+#if defined(HAVE_MMAP) && defined(HAVE_SYS_MMAN_H) && !defined(QUICK)
+#define QUICK
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_NONBLOCK
+#define O_NONBLOCK 0
+#endif
+
+#ifndef __cplusplus
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define FILE_RCSID(id) \
+static const char rcsid[] __attribute__((__used__)) = id;
+#else
+#define FILE_RCSID(id) \
+static const char *rcsid(const char *p) { \
+ return rcsid(p = id); \
+}
+#endif
+#else
+#define FILE_RCSID(id)
+#endif
+#ifndef __RCSID
+#define __RCSID(a)
+#endif
+
+#endif /* __file_h__ */
diff --git a/contrib/libs/libmagic/src/file/ya.make b/contrib/libs/libmagic/src/file/ya.make
new file mode 100644
index 0000000000..edd4ded276
--- /dev/null
+++ b/contrib/libs/libmagic/src/file/ya.make
@@ -0,0 +1,34 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+WITHOUT_LICENSE_TEXTS()
+
+LICENSE(BSD-2-Clause)
+
+PEERDIR(
+ contrib/libs/libmagic/src
+)
+
+ADDINCL(
+ contrib/libs/libmagic
+ contrib/libs/libmagic/src
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DHAVE_CONFIG_H
+ -DMAGIC=\"res@/magic/magic.mgc\"
+)
+
+SRCDIR(contrib/libs/libmagic/src)
+
+SRCS(
+ file.c
+ seccomp.c
+)
+
+END()
diff --git a/contrib/libs/libmagic/src/file_opts.h b/contrib/libs/libmagic/src/file_opts.h
new file mode 100644
index 0000000000..3aed7dea7e
--- /dev/null
+++ b/contrib/libs/libmagic/src/file_opts.h
@@ -0,0 +1,89 @@
+/*
+ * Table of command-line options
+ *
+ * The first column specifies the short name, if any, or 0 if none.
+ * The second column specifies the long name.
+ * The third column specifies whether it takes a parameter.
+ * The fourth column specifies whether is marked as "default"
+ * if POSIXLY_CORRECT is defined: 1,
+ * if POSIXLY_CORRECT is not defined: 2.
+ * The fifth column is the documentation.
+ *
+ * N.B. The long options' order must correspond to the code in file.c,
+ * and OPTSTRING must be kept up-to-date with the short options.
+ * Pay particular attention to the numbers of long-only options in the
+ * switch statement!
+ */
+
+OPT_LONGONLY("help", 0, 0,
+ " display this help and exit\n", OPT_HELP)
+OPT('v', "version", 0, 0,
+ " output version information and exit\n")
+OPT('m', "magic-file", 1, 0,
+ " LIST use LIST as a colon-separated list of magic\n"
+ " number files\n")
+OPT('z', "uncompress", 0, 0,
+ " try to look inside compressed files\n")
+OPT('Z', "uncompress-noreport", 0, 0,
+ " only print the contents of compressed files\n")
+OPT('b', "brief", 0, 0,
+ " do not prepend filenames to output lines\n")
+OPT('c', "checking-printout", 0, 0,
+ " print the parsed form of the magic file, use in\n"
+ " conjunction with -m to debug a new magic file\n"
+ " before installing it\n")
+OPT('e', "exclude", 1, 0,
+ " TEST exclude TEST from the list of test to be\n"
+ " performed for file. Valid tests are:\n"
+ " %e\n")
+OPT_LONGONLY("exclude-quiet", 1, 0,
+ " TEST like exclude, but ignore unknown tests\n", OPT_EXCLUDE_QUIET)
+OPT('f', "files-from", 1, 0,
+ " FILE read the filenames to be examined from FILE\n")
+OPT('F', "separator", 1, 0,
+ " STRING use string as separator instead of `:'\n")
+OPT('i', "mime", 0, 0,
+ " output MIME type strings (--mime-type and\n"
+ " --mime-encoding)\n")
+OPT_LONGONLY("apple", 0, 0,
+ " output the Apple CREATOR/TYPE\n", OPT_APPLE)
+OPT_LONGONLY("extension", 0, 0,
+ " output a slash-separated list of extensions\n", OPT_EXTENSIONS)
+OPT_LONGONLY("mime-type", 0, 0,
+ " output the MIME type\n", OPT_MIME_TYPE)
+OPT_LONGONLY("mime-encoding", 0, 0,
+ " output the MIME encoding\n", OPT_MIME_ENCODING)
+OPT('k', "keep-going", 0, 0,
+ " don't stop at the first match\n")
+OPT('l', "list", 0, 0,
+ " list magic strength\n")
+#ifdef S_IFLNK
+OPT('L', "dereference", 0, 1,
+ " follow symlinks (default if POSIXLY_CORRECT is set)")
+OPT('h', "no-dereference", 0, 2,
+ " don't follow symlinks (default if POSIXLY_CORRECT is not set)")
+#endif
+OPT('n', "no-buffer", 0, 0,
+ " do not buffer output\n")
+OPT('N', "no-pad", 0, 0,
+ " do not pad output\n")
+OPT('0', "print0", 0, 0,
+ " terminate filenames with ASCII NUL\n")
+#if defined(HAVE_UTIME) || defined(HAVE_UTIMES)
+OPT('p', "preserve-date", 0, 0,
+ " preserve access times on files\n")
+#endif
+OPT('P', "parameter", 1, 0,
+ " set file engine parameter limits\n"
+ " %P\n")
+OPT('r', "raw", 0, 0,
+ " don't translate unprintable chars to \\ooo\n")
+OPT('s', "special-files", 0, 0,
+ " treat special (block/char devices) files as\n"
+ " ordinary ones\n")
+OPT('S', "no-sandbox", 0, 0,
+ " disable system call sandboxing\n")
+OPT('C', "compile", 0, 0,
+ " compile file specified by -m\n")
+OPT('d', "debug", 0, 0,
+ " print debugging messages\n")
diff --git a/contrib/libs/libmagic/src/fmtcheck.c b/contrib/libs/libmagic/src/fmtcheck.c
new file mode 100644
index 0000000000..868d06fd95
--- /dev/null
+++ b/contrib/libs/libmagic/src/fmtcheck.c
@@ -0,0 +1,254 @@
+/* $NetBSD: fmtcheck.c,v 1.8 2008/04/28 20:22:59 martin Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Allen Briggs.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#include "file.h"
+#ifndef lint
+FILE_RCSID("@(#)$File: fmtcheck.c,v 1.6 2022/09/24 20:30:13 christos Exp $")
+#endif /* lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+enum __e_fmtcheck_types {
+ FMTCHECK_START,
+ FMTCHECK_SHORT,
+ FMTCHECK_INT,
+ FMTCHECK_LONG,
+ FMTCHECK_QUAD,
+ FMTCHECK_SHORTPOINTER,
+ FMTCHECK_INTPOINTER,
+ FMTCHECK_LONGPOINTER,
+ FMTCHECK_QUADPOINTER,
+ FMTCHECK_DOUBLE,
+ FMTCHECK_LONGDOUBLE,
+ FMTCHECK_STRING,
+ FMTCHECK_WIDTH,
+ FMTCHECK_PRECISION,
+ FMTCHECK_DONE,
+ FMTCHECK_UNKNOWN
+};
+typedef enum __e_fmtcheck_types EFT;
+
+#define RETURN(pf,f,r) do { \
+ *(pf) = (f); \
+ return r; \
+ } /*NOTREACHED*/ /*CONSTCOND*/ while (0)
+
+static EFT
+get_next_format_from_precision(const char **pf)
+{
+ int sh, lg, quad, longdouble;
+ const char *f;
+
+ sh = lg = quad = longdouble = 0;
+
+ f = *pf;
+ switch (*f) {
+ case 'h':
+ f++;
+ sh = 1;
+ break;
+ case 'l':
+ f++;
+ if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
+ if (*f == 'l') {
+ f++;
+ quad = 1;
+ } else {
+ lg = 1;
+ }
+ break;
+ case 'q':
+ f++;
+ quad = 1;
+ break;
+ case 'L':
+ f++;
+ longdouble = 1;
+ break;
+#ifdef WIN32
+ case 'I':
+ f++;
+ if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
+ if (*f == '3' && f[1] == '2') {
+ f += 2;
+ } else if (*f == '6' && f[1] == '4') {
+ f += 2;
+ quad = 1;
+ }
+#ifdef _WIN64
+ else {
+ quad = 1;
+ }
+#endif
+ break;
+#endif
+ default:
+ break;
+ }
+ if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
+ if (strchr("diouxX", *f)) {
+ if (longdouble)
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ if (lg)
+ RETURN(pf,f,FMTCHECK_LONG);
+ if (quad)
+ RETURN(pf,f,FMTCHECK_QUAD);
+ RETURN(pf,f,FMTCHECK_INT);
+ }
+ if (*f == 'n') {
+ if (longdouble)
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ if (sh)
+ RETURN(pf,f,FMTCHECK_SHORTPOINTER);
+ if (lg)
+ RETURN(pf,f,FMTCHECK_LONGPOINTER);
+ if (quad)
+ RETURN(pf,f,FMTCHECK_QUADPOINTER);
+ RETURN(pf,f,FMTCHECK_INTPOINTER);
+ }
+ if (strchr("DOU", *f)) {
+ if (sh + lg + quad + longdouble)
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ RETURN(pf,f,FMTCHECK_LONG);
+ }
+ if (strchr("eEfg", *f)) {
+ if (longdouble)
+ RETURN(pf,f,FMTCHECK_LONGDOUBLE);
+ if (sh + lg + quad)
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ RETURN(pf,f,FMTCHECK_DOUBLE);
+ }
+ if (*f == 'c') {
+ if (sh + lg + quad + longdouble)
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ RETURN(pf,f,FMTCHECK_INT);
+ }
+ if (*f == 's') {
+ if (sh + lg + quad + longdouble)
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ RETURN(pf,f,FMTCHECK_STRING);
+ }
+ if (*f == 'p') {
+ if (sh + lg + quad + longdouble)
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ RETURN(pf,f,FMTCHECK_LONG);
+ }
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ /*NOTREACHED*/
+}
+
+static EFT
+get_next_format_from_width(const char **pf)
+{
+ const char *f;
+
+ f = *pf;
+ if (*f == '.') {
+ f++;
+ if (*f == '*') {
+ RETURN(pf,f,FMTCHECK_PRECISION);
+ }
+ /* eat any precision (empty is allowed) */
+ while (isdigit((unsigned char)*f)) f++;
+ if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN);
+ }
+ RETURN(pf,f,get_next_format_from_precision(pf));
+ /*NOTREACHED*/
+}
+
+static EFT
+get_next_format(const char **pf, EFT eft)
+{
+ int infmt;
+ const char *f;
+
+ if (eft == FMTCHECK_WIDTH) {
+ (*pf)++;
+ return get_next_format_from_width(pf);
+ } else if (eft == FMTCHECK_PRECISION) {
+ (*pf)++;
+ return get_next_format_from_precision(pf);
+ }
+
+ f = *pf;
+ infmt = 0;
+ while (!infmt) {
+ f = strchr(f, '%');
+ if (f == NULL)
+ RETURN(pf,f,FMTCHECK_DONE);
+ f++;
+ if (!*f)
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ if (*f != '%')
+ infmt = 1;
+ else
+ f++;
+ }
+
+ /* Eat any of the flags */
+ while (*f && (strchr("#0- +", *f)))
+ f++;
+
+ if (*f == '*') {
+ RETURN(pf,f,FMTCHECK_WIDTH);
+ }
+ /* eat any width */
+ while (isdigit((unsigned char)*f)) f++;
+ if (!*f) {
+ RETURN(pf,f,FMTCHECK_UNKNOWN);
+ }
+
+ RETURN(pf,f,get_next_format_from_width(pf));
+ /*NOTREACHED*/
+}
+
+const char *
+fmtcheck(const char *f1, const char *f2)
+{
+ const char *f1p, *f2p;
+ EFT f1t, f2t;
+
+ if (!f1) return f2;
+
+ f1p = f1;
+ f1t = FMTCHECK_START;
+ f2p = f2;
+ f2t = FMTCHECK_START;
+ while ((f1t = get_next_format(&f1p, f1t)) != FMTCHECK_DONE) {
+ if (f1t == FMTCHECK_UNKNOWN)
+ return f2;
+ f2t = get_next_format(&f2p, f2t);
+ if (f1t != f2t)
+ return f2;
+ }
+ return f1;
+}
diff --git a/contrib/libs/libmagic/src/fsmagic.c b/contrib/libs/libmagic/src/fsmagic.c
new file mode 100644
index 0000000000..5a13dbda50
--- /dev/null
+++ b/contrib/libs/libmagic/src/fsmagic.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * fsmagic - magic based on filesystem info - directory, special files, etc.
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: fsmagic.c,v 1.85 2022/12/26 17:31:14 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+/* Since major is a function on SVR4, we cannot use `ifndef major'. */
+#ifdef MAJOR_IN_MKDEV
+# include <sys/mkdev.h>
+# define HAVE_MAJOR
+#endif
+#ifdef HAVE_SYS_SYSMACROS_H
+# include <sys/sysmacros.h>
+#endif
+#ifdef MAJOR_IN_SYSMACROS
+# define HAVE_MAJOR
+#endif
+#if defined(major) && !defined(HAVE_MAJOR)
+/* Might be defined in sys/types.h. */
+# define HAVE_MAJOR
+#endif
+#ifdef WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+#ifndef HAVE_MAJOR
+# define major(dev) (((dev) >> 8) & 0xff)
+# define minor(dev) ((dev) & 0xff)
+#endif
+#undef HAVE_MAJOR
+#ifdef S_IFLNK
+file_private int
+bad_link(struct magic_set *ms, int err, char *buf)
+{
+ int mime = ms->flags & MAGIC_MIME;
+ if ((mime & MAGIC_MIME_TYPE) &&
+ file_printf(ms, "inode/symlink")
+ == -1)
+ return -1;
+ else if (!mime) {
+ if (ms->flags & MAGIC_ERROR) {
+ file_error(ms, err,
+ "broken symbolic link to %s", buf);
+ return -1;
+ }
+ if (file_printf(ms, "broken symbolic link to %s", buf) == -1)
+ return -1;
+ }
+ return 1;
+}
+#endif
+file_private int
+handle_mime(struct magic_set *ms, int mime, const char *str)
+{
+ if ((mime & MAGIC_MIME_TYPE)) {
+ if (file_printf(ms, "inode/%s", str) == -1)
+ return -1;
+ if ((mime & MAGIC_MIME_ENCODING) && file_printf(ms,
+ "; charset=") == -1)
+ return -1;
+ }
+ if ((mime & MAGIC_MIME_ENCODING) && file_printf(ms, "binary") == -1)
+ return -1;
+ return 0;
+}
+
+file_protected int
+file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb)
+{
+ int ret, did = 0;
+ int mime = ms->flags & MAGIC_MIME;
+ int silent = ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION);
+#ifdef S_IFLNK
+ char buf[BUFSIZ+4];
+ ssize_t nch;
+ struct stat tstatbuf;
+#endif
+
+ if (fn == NULL)
+ return 0;
+
+#define COMMA (did++ ? ", " : "")
+ /*
+ * Fstat is cheaper but fails for files you don't have read perms on.
+ * On 4.2BSD and similar systems, use lstat() to identify symlinks.
+ */
+#ifdef S_IFLNK
+ if ((ms->flags & MAGIC_SYMLINK) == 0)
+ ret = lstat(fn, sb);
+ else
+#endif
+ ret = stat(fn, sb); /* don't merge into if; see "ret =" above */
+
+#ifdef WIN32
+ {
+ HANDLE hFile = CreateFile((LPCSTR)fn, 0, FILE_SHARE_DELETE |
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0,
+ NULL);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ /*
+ * Stat failed, but we can still open it - assume it's
+ * a block device, if nothing else.
+ */
+ if (ret) {
+ sb->st_mode = S_IFBLK;
+ ret = 0;
+ }
+ switch (GetFileType(hFile)) {
+ case FILE_TYPE_CHAR:
+ sb->st_mode |= S_IFCHR;
+ sb->st_mode &= ~S_IFREG;
+ break;
+ case FILE_TYPE_PIPE:
+ sb->st_mode |= S_IFIFO;
+ sb->st_mode &= ~S_IFREG;
+ break;
+ }
+ CloseHandle(hFile);
+ }
+ }
+#endif
+
+ if (ret) {
+ if (ms->flags & MAGIC_ERROR) {
+ file_error(ms, errno, "cannot stat `%s'", fn);
+ return -1;
+ }
+ if (file_printf(ms, "cannot open `%s' (%s)",
+ fn, strerror(errno)) == -1)
+ return -1;
+ return 0;
+ }
+
+ ret = 1;
+ if (!mime && !silent) {
+#ifdef S_ISUID
+ if (sb->st_mode & S_ISUID)
+ if (file_printf(ms, "%ssetuid", COMMA) == -1)
+ return -1;
+#endif
+#ifdef S_ISGID
+ if (sb->st_mode & S_ISGID)
+ if (file_printf(ms, "%ssetgid", COMMA) == -1)
+ return -1;
+#endif
+#ifdef S_ISVTX
+ if (sb->st_mode & S_ISVTX)
+ if (file_printf(ms, "%ssticky", COMMA) == -1)
+ return -1;
+#endif
+ }
+
+ switch (sb->st_mode & S_IFMT) {
+ case S_IFDIR:
+ if (mime) {
+ if (handle_mime(ms, mime, "directory") == -1)
+ return -1;
+ } else if (silent) {
+ } else if (file_printf(ms, "%sdirectory", COMMA) == -1)
+ return -1;
+ break;
+#ifdef S_IFCHR
+ case S_IFCHR:
+ /*
+ * If -s has been specified, treat character special files
+ * like ordinary files. Otherwise, just report that they
+ * are block special files and go on to the next file.
+ */
+ if ((ms->flags & MAGIC_DEVICES) != 0) {
+ ret = 0;
+ break;
+ }
+ if (mime) {
+ if (handle_mime(ms, mime, "chardevice") == -1)
+ return -1;
+ } else if (silent) {
+ } else {
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+# ifdef dv_unit
+ if (file_printf(ms, "%scharacter special (%d/%d/%d)",
+ COMMA, major(sb->st_rdev), dv_unit(sb->st_rdev),
+ dv_subunit(sb->st_rdev)) == -1)
+ return -1;
+# else
+ if (file_printf(ms, "%scharacter special (%ld/%ld)",
+ COMMA, (long)major(sb->st_rdev),
+ (long)minor(sb->st_rdev)) == -1)
+ return -1;
+# endif
+#else
+ if (file_printf(ms, "%scharacter special", COMMA) == -1)
+ return -1;
+#endif
+ }
+ break;
+#endif
+#ifdef S_IFBLK
+ case S_IFBLK:
+ /*
+ * If -s has been specified, treat block special files
+ * like ordinary files. Otherwise, just report that they
+ * are block special files and go on to the next file.
+ */
+ if ((ms->flags & MAGIC_DEVICES) != 0) {
+ ret = 0;
+ break;
+ }
+ if (mime) {
+ if (handle_mime(ms, mime, "blockdevice") == -1)
+ return -1;
+ } else if (silent) {
+ } else {
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+# ifdef dv_unit
+ if (file_printf(ms, "%sblock special (%d/%d/%d)",
+ COMMA, major(sb->st_rdev), dv_unit(sb->st_rdev),
+ dv_subunit(sb->st_rdev)) == -1)
+ return -1;
+# else
+ if (file_printf(ms, "%sblock special (%ld/%ld)",
+ COMMA, (long)major(sb->st_rdev),
+ (long)minor(sb->st_rdev)) == -1)
+ return -1;
+# endif
+#else
+ if (file_printf(ms, "%sblock special", COMMA) == -1)
+ return -1;
+#endif
+ }
+ break;
+#endif
+ /* TODO add code to handle V7 MUX and Blit MUX files */
+#ifdef S_IFIFO
+ case S_IFIFO:
+ if((ms->flags & MAGIC_DEVICES) != 0)
+ break;
+ if (mime) {
+ if (handle_mime(ms, mime, "fifo") == -1)
+ return -1;
+ } else if (silent) {
+ } else if (file_printf(ms, "%sfifo (named pipe)", COMMA) == -1)
+ return -1;
+ break;
+#endif
+#ifdef S_IFDOOR
+ case S_IFDOOR:
+ if (mime) {
+ if (handle_mime(ms, mime, "door") == -1)
+ return -1;
+ } else if (silent) {
+ } else if (file_printf(ms, "%sdoor", COMMA) == -1)
+ return -1;
+ break;
+#endif
+#ifdef S_IFLNK
+ case S_IFLNK:
+ if ((nch = readlink(fn, buf, BUFSIZ-1)) <= 0) {
+ if (ms->flags & MAGIC_ERROR) {
+ file_error(ms, errno, "unreadable symlink `%s'",
+ fn);
+ return -1;
+ }
+ if (mime) {
+ if (handle_mime(ms, mime, "symlink") == -1)
+ return -1;
+ } else if (silent) {
+ } else if (file_printf(ms,
+ "%sunreadable symlink `%s' (%s)", COMMA, fn,
+ strerror(errno)) == -1)
+ return -1;
+ break;
+ }
+ buf[nch] = '\0'; /* readlink(2) does not do this */
+
+ /* If broken symlink, say so and quit early. */
+#ifdef __linux__
+ /*
+ * linux procfs/devfs makes symlinks like pipe:[3515864880]
+ * that we can't stat their readlink output, so stat the
+ * original filename instead.
+ */
+ if (stat(fn, &tstatbuf) < 0)
+ return bad_link(ms, errno, buf);
+#else
+ if (*buf == '/') {
+ if (stat(buf, &tstatbuf) < 0)
+ return bad_link(ms, errno, buf);
+ } else {
+ char *tmp;
+ char buf2[BUFSIZ+BUFSIZ+4];
+
+ if ((tmp = CCAST(char *, strrchr(fn, '/'))) == NULL) {
+ tmp = buf; /* in current directory anyway */
+ } else {
+ if (tmp - fn + 1 > BUFSIZ) {
+ if (ms->flags & MAGIC_ERROR) {
+ file_error(ms, 0,
+ "path too long: `%s'", buf);
+ return -1;
+ }
+ if (mime) {
+ if (handle_mime(ms, mime,
+ "x-path-too-long") == -1)
+ return -1;
+ } else if (silent) {
+ } else if (file_printf(ms,
+ "%spath too long: `%s'", COMMA,
+ fn) == -1)
+ return -1;
+ break;
+ }
+ /* take dir part */
+ (void)strlcpy(buf2, fn, sizeof buf2);
+ buf2[tmp - fn + 1] = '\0';
+ /* plus (rel) link */
+ (void)strlcat(buf2, buf, sizeof buf2);
+ tmp = buf2;
+ }
+ if (stat(tmp, &tstatbuf) < 0)
+ return bad_link(ms, errno, buf);
+ }
+#endif
+
+ /* Otherwise, handle it. */
+ if ((ms->flags & MAGIC_SYMLINK) != 0) {
+ const char *p;
+ ms->flags &= MAGIC_SYMLINK;
+ p = magic_file(ms, buf);
+ ms->flags |= MAGIC_SYMLINK;
+ if (p == NULL)
+ return -1;
+ } else { /* just print what it points to */
+ if (mime) {
+ if (handle_mime(ms, mime, "symlink") == -1)
+ return -1;
+ } else if (silent) {
+ } else if (file_printf(ms, "%ssymbolic link to %s",
+ COMMA, buf) == -1)
+ return -1;
+ }
+ break;
+#endif
+#ifdef S_IFSOCK
+#ifndef __COHERENT__
+ case S_IFSOCK:
+ if (mime) {
+ if (handle_mime(ms, mime, "socket") == -1)
+ return -1;
+ } else if (silent) {
+ } else if (file_printf(ms, "%ssocket", COMMA) == -1)
+ return -1;
+ break;
+#endif
+#endif
+ case S_IFREG:
+ /*
+ * regular file, check next possibility
+ *
+ * If stat() tells us the file has zero length, report here that
+ * the file is empty, so we can skip all the work of opening and
+ * reading the file.
+ * But if the -s option has been given, we skip this
+ * optimization, since on some systems, stat() reports zero
+ * size for raw disk partitions. (If the block special device
+ * really has zero length, the fact that it is empty will be
+ * detected and reported correctly when we read the file.)
+ */
+ if ((ms->flags & MAGIC_DEVICES) == 0 && sb->st_size == 0) {
+ if (mime) {
+ if (handle_mime(ms, mime, "x-empty") == -1)
+ return -1;
+ } else if (silent) {
+ } else if (file_printf(ms, "%sempty", COMMA) == -1)
+ return -1;
+ break;
+ }
+ ret = 0;
+ break;
+
+ default:
+ file_error(ms, 0, "invalid mode 0%o", sb->st_mode);
+ return -1;
+ /*NOTREACHED*/
+ }
+
+ if (!silent && !mime && did && ret == 0) {
+ if (file_printf(ms, " ") == -1)
+ return -1;
+ }
+ /*
+ * If we were looking for extensions or apple (silent) it is not our
+ * job to print here, so don't count this as a match.
+ */
+ if (ret == 1 && silent)
+ return 0;
+ return ret;
+}
diff --git a/contrib/libs/libmagic/src/funcs.c b/contrib/libs/libmagic/src/funcs.c
new file mode 100644
index 0000000000..af15d69886
--- /dev/null
+++ b/contrib/libs/libmagic/src/funcs.c
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) Christos Zoulas 2003.
+ * All Rights Reserved.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: funcs.c,v 1.140 2023/05/21 17:08:34 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for pipe2() */
+#endif
+#if defined(HAVE_WCHAR_H)
+#include <wchar.h>
+#endif
+#if defined(HAVE_WCTYPE_H)
+#include <wctype.h>
+#endif
+#include <limits.h>
+
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)~0)
+#endif
+
+file_protected char *
+file_copystr(char *buf, size_t blen, size_t width, const char *str)
+{
+ if (blen == 0)
+ return buf;
+ if (width >= blen)
+ width = blen - 1;
+ memcpy(buf, str, width);
+ buf[width] = '\0';
+ return buf;
+}
+
+file_private void
+file_clearbuf(struct magic_set *ms)
+{
+ free(ms->o.buf);
+ ms->o.buf = NULL;
+ ms->o.blen = 0;
+}
+
+file_private int
+file_checkfield(char *msg, size_t mlen, const char *what, const char **pp)
+{
+ const char *p = *pp;
+ int fw = 0;
+
+ while (*p && isdigit((unsigned char)*p))
+ fw = fw * 10 + (*p++ - '0');
+
+ *pp = p;
+
+ if (fw < 1024)
+ return 1;
+ if (msg)
+ snprintf(msg, mlen, "field %s too large: %d", what, fw);
+
+ return 0;
+}
+
+file_protected int
+file_checkfmt(char *msg, size_t mlen, const char *fmt)
+{
+ const char *p;
+ for (p = fmt; *p; p++) {
+ if (*p != '%')
+ continue;
+ if (*++p == '%')
+ continue;
+ // Skip uninteresting.
+ while (strchr("#0.'+- ", *p) != NULL)
+ p++;
+ if (*p == '*') {
+ if (msg)
+ snprintf(msg, mlen, "* not allowed in format");
+ return -1;
+ }
+
+ if (!file_checkfield(msg, mlen, "width", &p))
+ return -1;
+
+ if (*p == '.') {
+ p++;
+ if (!file_checkfield(msg, mlen, "precision", &p))
+ return -1;
+ }
+
+ if (!isalpha((unsigned char)*p)) {
+ if (msg)
+ snprintf(msg, mlen, "bad format char: %c", *p);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Like printf, only we append to a buffer.
+ */
+file_protected int
+file_vprintf(struct magic_set *ms, const char *fmt, va_list ap)
+{
+ int len;
+ char *buf, *newstr;
+ char tbuf[1024];
+
+ if (ms->event_flags & EVENT_HAD_ERR)
+ return 0;
+
+ if (file_checkfmt(tbuf, sizeof(tbuf), fmt)) {
+ file_clearbuf(ms);
+ file_error(ms, 0, "Bad magic format `%s' (%s)", fmt, tbuf);
+ return -1;
+ }
+
+ len = vasprintf(&buf, fmt, ap);
+ if (len < 0 || (size_t)len > 1024 || len + ms->o.blen > 1024 * 1024) {
+ size_t blen = ms->o.blen;
+ free(buf);
+ file_clearbuf(ms);
+ file_error(ms, 0, "Output buffer space exceeded %d+%"
+ SIZE_T_FORMAT "u", len, blen);
+ return -1;
+ }
+
+ if (ms->o.buf != NULL) {
+ len = asprintf(&newstr, "%s%s", ms->o.buf, buf);
+ free(buf);
+ if (len < 0)
+ goto out;
+ free(ms->o.buf);
+ buf = newstr;
+ }
+ ms->o.buf = buf;
+ ms->o.blen = len;
+ return 0;
+out:
+ file_clearbuf(ms);
+ file_error(ms, errno, "vasprintf failed");
+ return -1;
+}
+
+file_protected int
+file_printf(struct magic_set *ms, const char *fmt, ...)
+{
+ int rv;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rv = file_vprintf(ms, fmt, ap);
+ va_end(ap);
+ return rv;
+}
+
+/*
+ * error - print best error message possible
+ */
+/*VARARGS*/
+__attribute__((__format__(__printf__, 3, 0)))
+file_private void
+file_error_core(struct magic_set *ms, int error, const char *f, va_list va,
+ size_t lineno)
+{
+ /* Only the first error is ok */
+ if (ms->event_flags & EVENT_HAD_ERR)
+ return;
+ if (lineno != 0) {
+ file_clearbuf(ms);
+ (void)file_printf(ms, "line %" SIZE_T_FORMAT "u:", lineno);
+ }
+ if (ms->o.buf && *ms->o.buf)
+ (void)file_printf(ms, " ");
+ (void)file_vprintf(ms, f, va);
+ if (error > 0)
+ (void)file_printf(ms, " (%s)", strerror(error));
+ ms->event_flags |= EVENT_HAD_ERR;
+ ms->error = error;
+}
+
+/*VARARGS*/
+file_protected void
+file_error(struct magic_set *ms, int error, const char *f, ...)
+{
+ va_list va;
+ va_start(va, f);
+ file_error_core(ms, error, f, va, 0);
+ va_end(va);
+}
+
+/*
+ * Print an error with magic line number.
+ */
+/*VARARGS*/
+file_protected void
+file_magerror(struct magic_set *ms, const char *f, ...)
+{
+ va_list va;
+ va_start(va, f);
+ file_error_core(ms, 0, f, va, ms->line);
+ va_end(va);
+}
+
+file_protected void
+file_oomem(struct magic_set *ms, size_t len)
+{
+ file_error(ms, errno, "cannot allocate %" SIZE_T_FORMAT "u bytes",
+ len);
+}
+
+file_protected void
+file_badseek(struct magic_set *ms)
+{
+ file_error(ms, errno, "error seeking");
+}
+
+file_protected void
+file_badread(struct magic_set *ms)
+{
+ file_error(ms, errno, "error reading");
+}
+
+#ifndef COMPILE_ONLY
+#define FILE_SEPARATOR "\n- "
+
+file_protected int
+file_separator(struct magic_set *ms)
+{
+ return file_printf(ms, FILE_SEPARATOR);
+}
+
+static void
+trim_separator(struct magic_set *ms)
+{
+ size_t l;
+
+ if (ms->o.buf == NULL)
+ return;
+
+ l = strlen(ms->o.buf);
+ if (l < sizeof(FILE_SEPARATOR))
+ return;
+
+ l -= sizeof(FILE_SEPARATOR) - 1;
+ if (strcmp(ms->o.buf + l, FILE_SEPARATOR) != 0)
+ return;
+
+ ms->o.buf[l] = '\0';
+}
+
+static int
+checkdone(struct magic_set *ms, int *rv)
+{
+ if ((ms->flags & MAGIC_CONTINUE) == 0)
+ return 1;
+ if (file_separator(ms) == -1)
+ *rv = -1;
+ return 0;
+}
+
+file_protected int
+file_default(struct magic_set *ms, size_t nb)
+{
+ if (ms->flags & MAGIC_MIME) {
+ if ((ms->flags & MAGIC_MIME_TYPE) &&
+ file_printf(ms, "application/%s",
+ nb ? "octet-stream" : "x-empty") == -1)
+ return -1;
+ return 1;
+ }
+ if (ms->flags & MAGIC_APPLE) {
+ if (file_printf(ms, "UNKNUNKN") == -1)
+ return -1;
+ return 1;
+ }
+ if (ms->flags & MAGIC_EXTENSION) {
+ if (file_printf(ms, "???") == -1)
+ return -1;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * The magic detection functions return:
+ * 1: found
+ * 0: not found
+ * -1: error
+ */
+/*ARGSUSED*/
+file_protected int
+file_buffer(struct magic_set *ms, int fd, struct stat *st,
+ const char *inname __attribute__ ((__unused__)),
+ const void *buf, size_t nb)
+{
+ int m = 0, rv = 0, looks_text = 0;
+ const char *code = NULL;
+ const char *code_mime = "binary";
+ const char *def = "data";
+ const char *ftype = NULL;
+ char *rbuf = NULL;
+ struct buffer b;
+
+ buffer_init(&b, fd, st, buf, nb);
+ ms->mode = b.st.st_mode;
+
+ if (nb == 0) {
+ def = "empty";
+ goto simple;
+ } else if (nb == 1) {
+ def = "very short file (no magic)";
+ goto simple;
+ }
+
+ if ((ms->flags & MAGIC_NO_CHECK_ENCODING) == 0) {
+ looks_text = file_encoding(ms, &b, NULL, 0,
+ &code, &code_mime, &ftype);
+ }
+
+#ifdef __EMX__
+ if ((ms->flags & MAGIC_NO_CHECK_APPTYPE) == 0 && inname) {
+ m = file_os2_apptype(ms, inname, &b);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try os2_apptype %d]\n", m);
+ switch (m) {
+ case -1:
+ return -1;
+ case 0:
+ break;
+ default:
+ return 1;
+ }
+ }
+#endif
+#if HAVE_FORK
+ /* try compression stuff */
+ if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) == 0) {
+ m = file_zmagic(ms, &b, inname);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try zmagic %d]\n", m);
+ if (m) {
+ goto done_encoding;
+ }
+ }
+#endif
+ /* Check if we have a tar file */
+ if ((ms->flags & MAGIC_NO_CHECK_TAR) == 0) {
+ m = file_is_tar(ms, &b);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try tar %d]\n", m);
+ if (m) {
+ if (checkdone(ms, &rv))
+ goto done;
+ }
+ }
+
+ /* Check if we have a JSON file */
+ if ((ms->flags & MAGIC_NO_CHECK_JSON) == 0) {
+ m = file_is_json(ms, &b);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try json %d]\n", m);
+ if (m) {
+ if (checkdone(ms, &rv))
+ goto done;
+ }
+ }
+
+ /* Check if we have a CSV file */
+ if ((ms->flags & MAGIC_NO_CHECK_CSV) == 0) {
+ m = file_is_csv(ms, &b, looks_text, code);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try csv %d]\n", m);
+ if (m) {
+ if (checkdone(ms, &rv))
+ goto done;
+ }
+ }
+
+ /* Check if we have a SIMH tape file */
+ if ((ms->flags & MAGIC_NO_CHECK_SIMH) == 0) {
+ m = file_is_simh(ms, &b);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try simh %d]\n", m);
+ if (m) {
+ if (checkdone(ms, &rv))
+ goto done;
+ }
+ }
+
+ /* Check if we have a CDF file */
+ if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) {
+ m = file_trycdf(ms, &b);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try cdf %d]\n", m);
+ if (m) {
+ if (checkdone(ms, &rv))
+ goto done;
+ }
+ }
+#ifdef BUILTIN_ELF
+ if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && nb > 5 && fd != -1) {
+ file_pushbuf_t *pb;
+ /*
+ * We matched something in the file, so this
+ * *might* be an ELF file, and the file is at
+ * least 5 bytes long, so if it's an ELF file
+ * it has at least one byte past the ELF magic
+ * number - try extracting information from the
+ * ELF headers that cannot easily be extracted
+ * with rules in the magic file. We we don't
+ * print the information yet.
+ */
+ if ((pb = file_push_buffer(ms)) == NULL)
+ return -1;
+
+ rv = file_tryelf(ms, &b);
+ rbuf = file_pop_buffer(ms, pb);
+ if (rv == -1) {
+ free(rbuf);
+ rbuf = NULL;
+ }
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try elf %d]\n", m);
+ }
+#endif
+
+ /* try soft magic tests */
+ if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) {
+ m = file_softmagic(ms, &b, NULL, NULL, BINTEST, looks_text);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try softmagic %d]\n", m);
+ if (m == 1 && rbuf) {
+ if (file_printf(ms, "%s", rbuf) == -1)
+ goto done;
+ }
+ if (m) {
+ if (checkdone(ms, &rv))
+ goto done;
+ }
+ }
+
+ /* try text properties */
+ if ((ms->flags & MAGIC_NO_CHECK_TEXT) == 0) {
+
+ m = file_ascmagic(ms, &b, looks_text);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void)fprintf(stderr, "[try ascmagic %d]\n", m);
+ if (m) {
+ goto done;
+ }
+ }
+
+simple:
+ /* give up */
+ if (m == 0) {
+ m = 1;
+ rv = file_default(ms, nb);
+ if (rv == 0)
+ if (file_printf(ms, "%s", def) == -1)
+ rv = -1;
+ }
+ done:
+ trim_separator(ms);
+ if ((ms->flags & MAGIC_MIME_ENCODING) != 0) {
+ if (ms->flags & MAGIC_MIME_TYPE)
+ if (file_printf(ms, "; charset=") == -1)
+ rv = -1;
+ if (file_printf(ms, "%s", code_mime) == -1)
+ rv = -1;
+ }
+#if HAVE_FORK
+ done_encoding:
+#endif
+ free(rbuf);
+ buffer_fini(&b);
+ if (rv)
+ return rv;
+
+ return m;
+}
+#endif
+
+file_protected int
+file_reset(struct magic_set *ms, int checkloaded)
+{
+ if (checkloaded && ms->mlist[0] == NULL) {
+ file_error(ms, 0, "no magic files loaded");
+ return -1;
+ }
+ file_clearbuf(ms);
+ if (ms->o.pbuf) {
+ free(ms->o.pbuf);
+ ms->o.pbuf = NULL;
+ }
+ ms->event_flags &= ~EVENT_HAD_ERR;
+ ms->error = -1;
+ return 0;
+}
+
+#define OCTALIFY(n, o) \
+ /*LINTED*/ \
+ (void)(*(n)++ = '\\', \
+ *(n)++ = ((CAST(uint32_t, *(o)) >> 6) & 3) + '0', \
+ *(n)++ = ((CAST(uint32_t, *(o)) >> 3) & 7) + '0', \
+ *(n)++ = ((CAST(uint32_t, *(o)) >> 0) & 7) + '0', \
+ (o)++)
+
+file_protected const char *
+file_getbuffer(struct magic_set *ms)
+{
+ char *pbuf, *op, *np;
+ size_t psize, len;
+
+ if (ms->event_flags & EVENT_HAD_ERR)
+ return NULL;
+
+ if (ms->flags & MAGIC_RAW)
+ return ms->o.buf;
+
+ if (ms->o.buf == NULL)
+ return NULL;
+
+ /* * 4 is for octal representation, + 1 is for NUL */
+ len = strlen(ms->o.buf);
+ if (len > (SIZE_MAX - 1) / 4) {
+ file_oomem(ms, len);
+ return NULL;
+ }
+ psize = len * 4 + 1;
+ if ((pbuf = CAST(char *, realloc(ms->o.pbuf, psize))) == NULL) {
+ file_oomem(ms, psize);
+ return NULL;
+ }
+ ms->o.pbuf = pbuf;
+
+#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
+ {
+ mbstate_t state;
+ wchar_t nextchar;
+ int mb_conv = 1;
+ size_t bytesconsumed;
+ char *eop;
+ (void)memset(&state, 0, sizeof(mbstate_t));
+
+ np = ms->o.pbuf;
+ op = ms->o.buf;
+ eop = op + len;
+
+ while (op < eop) {
+ bytesconsumed = mbrtowc(&nextchar, op,
+ CAST(size_t, eop - op), &state);
+ if (bytesconsumed == CAST(size_t, -1) ||
+ bytesconsumed == CAST(size_t, -2)) {
+ mb_conv = 0;
+ break;
+ }
+
+ if (iswprint(nextchar)) {
+ (void)memcpy(np, op, bytesconsumed);
+ op += bytesconsumed;
+ np += bytesconsumed;
+ } else {
+ while (bytesconsumed-- > 0)
+ OCTALIFY(np, op);
+ }
+ }
+ *np = '\0';
+
+ /* Parsing succeeded as a multi-byte sequence */
+ if (mb_conv != 0)
+ return ms->o.pbuf;
+ }
+#endif
+
+ for (np = ms->o.pbuf, op = ms->o.buf; *op;) {
+ if (isprint(CAST(unsigned char, *op))) {
+ *np++ = *op++;
+ } else {
+ OCTALIFY(np, op);
+ }
+ }
+ *np = '\0';
+ return ms->o.pbuf;
+}
+
+file_protected int
+file_check_mem(struct magic_set *ms, unsigned int level)
+{
+ size_t len;
+
+ if (level >= ms->c.len) {
+ len = (ms->c.len = 20 + level) * sizeof(*ms->c.li);
+ ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ?
+ malloc(len) :
+ realloc(ms->c.li, len));
+ if (ms->c.li == NULL) {
+ file_oomem(ms, len);
+ return -1;
+ }
+ }
+ ms->c.li[level].got_match = 0;
+#ifdef ENABLE_CONDITIONALS
+ ms->c.li[level].last_match = 0;
+ ms->c.li[level].last_cond = COND_NONE;
+#endif /* ENABLE_CONDITIONALS */
+ return 0;
+}
+
+file_protected size_t
+file_printedlen(const struct magic_set *ms)
+{
+ return ms->o.blen;
+}
+
+file_protected int
+file_replace(struct magic_set *ms, const char *pat, const char *rep)
+{
+ file_regex_t rx;
+ int rc, rv = -1;
+
+ rc = file_regcomp(ms, &rx, pat, REG_EXTENDED);
+ if (rc == 0) {
+ regmatch_t rm;
+ int nm = 0;
+ while (file_regexec(ms, &rx, ms->o.buf, 1, &rm, 0) == 0) {
+ ms->o.buf[rm.rm_so] = '\0';
+ if (file_printf(ms, "%s%s", rep,
+ rm.rm_eo != 0 ? ms->o.buf + rm.rm_eo : "") == -1)
+ goto out;
+ nm++;
+ }
+ rv = nm;
+ }
+out:
+ file_regfree(&rx);
+ return rv;
+}
+
+file_private int
+check_regex(struct magic_set *ms, const char *pat)
+{
+ char sbuf[512];
+ unsigned char oc = '\0';
+ const char *p;
+
+ for (p = pat; *p; p++) {
+ unsigned char c = *p;
+ // Avoid repetition
+ if (c == oc && strchr("?*+{", c) != NULL) {
+ size_t len = strlen(pat);
+ file_magwarn(ms,
+ "repetition-operator operand `%c' "
+ "invalid in regex `%s'", c,
+ file_printable(ms, sbuf, sizeof(sbuf), pat, len));
+ return -1;
+ }
+ oc = c;
+ if (isprint(c) || isspace(c) || c == '\b'
+ || c == 0x8a) // XXX: apple magic fixme
+ continue;
+ size_t len = strlen(pat);
+ file_magwarn(ms,
+ "non-ascii characters in regex \\%#o `%s'",
+ c, file_printable(ms, sbuf, sizeof(sbuf), pat, len));
+ return -1;
+ }
+ return 0;
+}
+
+file_protected int
+file_regcomp(struct magic_set *ms file_locale_used, file_regex_t *rx,
+ const char *pat, int flags)
+{
+ if (check_regex(ms, pat) == -1)
+ return -1;
+
+#ifdef USE_C_LOCALE
+ locale_t old = uselocale(ms->c_lc_ctype);
+ assert(old != NULL);
+#else
+ char old[1024];
+ strlcpy(old, setlocale(LC_CTYPE, NULL), sizeof(old));
+ (void)setlocale(LC_CTYPE, "C");
+#endif
+ int rc;
+ rc = regcomp(rx, pat, flags);
+
+#ifdef USE_C_LOCALE
+ uselocale(old);
+#else
+ (void)setlocale(LC_CTYPE, old);
+#endif
+ if (rc > 0 && (ms->flags & MAGIC_CHECK)) {
+ char errmsg[512], buf[512];
+
+ (void)regerror(rc, rx, errmsg, sizeof(errmsg));
+ file_magerror(ms, "regex error %d for `%s', (%s)", rc,
+ file_printable(ms, buf, sizeof(buf), pat, strlen(pat)),
+ errmsg);
+ }
+ return rc;
+}
+
+/*ARGSUSED*/
+file_protected int
+file_regexec(struct magic_set *ms file_locale_used, file_regex_t *rx,
+ const char *str, size_t nmatch, regmatch_t* pmatch, int eflags)
+{
+#ifdef USE_C_LOCALE
+ locale_t old = uselocale(ms->c_lc_ctype);
+ assert(old != NULL);
+#else
+ char old[1024];
+ strlcpy(old, setlocale(LC_CTYPE, NULL), sizeof(old));
+ (void)setlocale(LC_CTYPE, "C");
+#endif
+ int rc;
+ /* XXX: force initialization because glibc does not always do this */
+ if (nmatch != 0)
+ memset(pmatch, 0, nmatch * sizeof(*pmatch));
+ rc = regexec(rx, str, nmatch, pmatch, eflags);
+#ifdef USE_C_LOCALE
+ uselocale(old);
+#else
+ (void)setlocale(LC_CTYPE, old);
+#endif
+ return rc;
+}
+
+file_protected void
+file_regfree(file_regex_t *rx)
+{
+ regfree(rx);
+}
+
+file_protected file_pushbuf_t *
+file_push_buffer(struct magic_set *ms)
+{
+ file_pushbuf_t *pb;
+
+ if (ms->event_flags & EVENT_HAD_ERR)
+ return NULL;
+
+ if ((pb = (CAST(file_pushbuf_t *, malloc(sizeof(*pb))))) == NULL)
+ return NULL;
+
+ pb->buf = ms->o.buf;
+ pb->blen = ms->o.blen;
+ pb->offset = ms->offset;
+
+ ms->o.buf = NULL;
+ ms->o.blen = 0;
+ ms->offset = 0;
+
+ return pb;
+}
+
+file_protected char *
+file_pop_buffer(struct magic_set *ms, file_pushbuf_t *pb)
+{
+ char *rbuf;
+
+ if (ms->event_flags & EVENT_HAD_ERR) {
+ free(pb->buf);
+ free(pb);
+ return NULL;
+ }
+
+ rbuf = ms->o.buf;
+
+ ms->o.buf = pb->buf;
+ ms->o.blen = pb->blen;
+ ms->offset = pb->offset;
+
+ free(pb);
+ return rbuf;
+}
+
+/*
+ * convert string to ascii printable format.
+ */
+file_protected char *
+file_printable(struct magic_set *ms, char *buf, size_t bufsiz,
+ const char *str, size_t slen)
+{
+ char *ptr, *eptr = buf + bufsiz - 1;
+ const unsigned char *s = RCAST(const unsigned char *, str);
+ const unsigned char *es = s + slen;
+
+ for (ptr = buf; ptr < eptr && s < es && *s; s++) {
+ if ((ms->flags & MAGIC_RAW) != 0 || isprint(*s)) {
+ *ptr++ = *s;
+ continue;
+ }
+ if (ptr >= eptr - 3)
+ break;
+ *ptr++ = '\\';
+ *ptr++ = ((CAST(unsigned int, *s) >> 6) & 7) + '0';
+ *ptr++ = ((CAST(unsigned int, *s) >> 3) & 7) + '0';
+ *ptr++ = ((CAST(unsigned int, *s) >> 0) & 7) + '0';
+ }
+ *ptr = '\0';
+ return buf;
+}
+
+struct guid {
+ uint32_t data1;
+ uint16_t data2;
+ uint16_t data3;
+ uint8_t data4[8];
+};
+
+file_protected int
+file_parse_guid(const char *s, uint64_t *guid)
+{
+ struct guid *g = CAST(struct guid *, CAST(void *, guid));
+#ifndef WIN32
+ return sscanf(s,
+ "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
+ &g->data1, &g->data2, &g->data3, &g->data4[0], &g->data4[1],
+ &g->data4[2], &g->data4[3], &g->data4[4], &g->data4[5],
+ &g->data4[6], &g->data4[7]) == 11 ? 0 : -1;
+#else
+ /* MS-Windows runtime doesn't support %hhx, except under
+ non-default __USE_MINGW_ANSI_STDIO. */
+ uint16_t data16[8];
+ int rv = sscanf(s, "%8x-%4hx-%4hx-%2hx%2hx-%2hx%2hx%2hx%2hx%2hx%2hx",
+ &g->data1, &g->data2, &g->data3, &data16[0], &data16[1],
+ &data16[2], &data16[3], &data16[4], &data16[5],
+ &data16[6], &data16[7]) == 11 ? 0 : -1;
+ int i;
+ for (i = 0; i < 8; i++)
+ g->data4[i] = data16[i];
+ return rv;
+#endif
+}
+
+file_protected int
+file_print_guid(char *str, size_t len, const uint64_t *guid)
+{
+ const struct guid *g = CAST(const struct guid *,
+ CAST(const void *, guid));
+
+#ifndef WIN32
+ return snprintf(str, len, "%.8X-%.4hX-%.4hX-%.2hhX%.2hhX-"
+ "%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX",
+ g->data1, g->data2, g->data3, g->data4[0], g->data4[1],
+ g->data4[2], g->data4[3], g->data4[4], g->data4[5],
+ g->data4[6], g->data4[7]);
+#else
+ return snprintf(str, len, "%.8X-%.4hX-%.4hX-%.2hX%.2hX-"
+ "%.2hX%.2hX%.2hX%.2hX%.2hX%.2hX",
+ g->data1, g->data2, g->data3, g->data4[0], g->data4[1],
+ g->data4[2], g->data4[3], g->data4[4], g->data4[5],
+ g->data4[6], g->data4[7]);
+#endif
+}
+
+file_protected int
+file_pipe_closexec(int *fds)
+{
+#ifdef __MINGW32__
+ return 0;
+#elif defined(HAVE_PIPE2)
+ return pipe2(fds, O_CLOEXEC);
+#else
+ if (pipe(fds) == -1)
+ return -1;
+# ifdef F_SETFD
+ (void)fcntl(fds[0], F_SETFD, FD_CLOEXEC);
+ (void)fcntl(fds[1], F_SETFD, FD_CLOEXEC);
+# endif
+ return 0;
+#endif
+}
+
+file_protected int
+file_clear_closexec(int fd) {
+#ifdef F_SETFD
+ return fcntl(fd, F_SETFD, 0);
+#else
+ return 0;
+#endif
+}
+
+file_protected char *
+file_strtrim(char *str)
+{
+ char *last;
+
+ while (isspace(CAST(unsigned char, *str)))
+ str++;
+ last = str;
+ while (*last)
+ last++;
+ --last;
+ while (isspace(CAST(unsigned char, *last)))
+ last--;
+ *++last = '\0';
+ return str;
+}
diff --git a/contrib/libs/libmagic/src/is_csv.c b/contrib/libs/libmagic/src/is_csv.c
new file mode 100644
index 0000000000..7b95e3b851
--- /dev/null
+++ b/contrib/libs/libmagic/src/is_csv.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 2019 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/*
+ * Parse CSV object serialization format (RFC-4180, RFC-7111)
+ */
+
+#ifndef TEST
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: is_csv.c,v 1.13 2023/07/17 16:08:17 christos Exp $")
+#endif
+
+#include <string.h>
+#include "magic.h"
+#else
+#include <sys/types.h>
+#endif
+
+
+#ifdef DEBUG
+#include <stdio.h>
+#define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+/*
+ * if CSV_LINES == 0:
+ * check all the lines in the buffer
+ * otherwise:
+ * check only up-to the number of lines specified
+ *
+ * the last line count is always ignored if it does not end in CRLF
+ */
+#ifndef CSV_LINES
+#define CSV_LINES 10
+#endif
+
+static int csv_parse(const unsigned char *, const unsigned char *);
+
+static const unsigned char *
+eatquote(const unsigned char *uc, const unsigned char *ue)
+{
+ int quote = 0;
+
+ while (uc < ue) {
+ unsigned char c = *uc++;
+ if (c != '"') {
+ // We already got one, done.
+ if (quote) {
+ return --uc;
+ }
+ continue;
+ }
+ if (quote) {
+ // quote-quote escapes
+ quote = 0;
+ continue;
+ }
+ // first quote
+ quote = 1;
+ }
+ return ue;
+}
+
+static int
+csv_parse(const unsigned char *uc, const unsigned char *ue)
+{
+ size_t nf = 0, tf = 0, nl = 0;
+
+ while (uc < ue) {
+ switch (*uc++) {
+ case '"':
+ // Eat until the matching quote
+ uc = eatquote(uc, ue);
+ break;
+ case ',':
+ nf++;
+ break;
+ case '\n':
+ DPRINTF("%zu %zu %zu\n", nl, nf, tf);
+ nl++;
+#if CSV_LINES
+ if (nl == CSV_LINES)
+ return tf != 0 && tf == nf;
+#endif
+ if (tf == 0) {
+ // First time and no fields, give up
+ if (nf == 0)
+ return 0;
+ // First time, set the number of fields
+ tf = nf;
+ } else if (tf != nf) {
+ // Field number mismatch, we are done.
+ return 0;
+ }
+ nf = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ return tf && nl >= 2;
+}
+
+#ifndef TEST
+int
+file_is_csv(struct magic_set *ms, const struct buffer *b, int looks_text,
+ const char *code)
+{
+ const unsigned char *uc = CAST(const unsigned char *, b->fbuf);
+ const unsigned char *ue = uc + b->flen;
+ int mime = ms->flags & MAGIC_MIME;
+
+ if (!looks_text)
+ return 0;
+
+ if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
+ return 0;
+
+ if (!csv_parse(uc, ue))
+ return 0;
+
+ if (mime == MAGIC_MIME_ENCODING)
+ return 1;
+
+ if (mime) {
+ if (file_printf(ms, "text/csv") == -1)
+ return -1;
+ return 1;
+ }
+
+ if (file_printf(ms, "CSV %s%stext", code ? code : "",
+ code ? " " : "") == -1)
+ return -1;
+
+ return 1;
+}
+
+#else
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <err.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ struct stat st;
+ unsigned char *p;
+
+ if ((fd = open(argv[1], O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "Can't open `%s'", argv[1]);
+
+ if (fstat(fd, &st) == -1)
+ err(EXIT_FAILURE, "Can't stat `%s'", argv[1]);
+
+ if ((p = CAST(char *, malloc(st.st_size))) == NULL)
+ err(EXIT_FAILURE, "Can't allocate %jd bytes",
+ (intmax_t)st.st_size);
+ if (read(fd, p, st.st_size) != st.st_size)
+ err(EXIT_FAILURE, "Can't read %jd bytes",
+ (intmax_t)st.st_size);
+ printf("is csv %d\n", csv_parse(p, p + st.st_size));
+ return 0;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/is_json.c b/contrib/libs/libmagic/src/is_json.c
new file mode 100644
index 0000000000..eca2a49ee7
--- /dev/null
+++ b/contrib/libs/libmagic/src/is_json.c
@@ -0,0 +1,500 @@
+/*-
+ * Copyright (c) 2018 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/*
+ * Parse JSON object serialization format (RFC-7159)
+ */
+
+#ifndef TEST
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: is_json.c,v 1.30 2022/09/27 19:12:40 christos Exp $")
+#endif
+
+#include "magic.h"
+#else
+#include <stdio.h>
+#include <stddef.h>
+#endif
+#include <string.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#define DPRINTF(a, b, c) \
+ printf("%*s%s [%.2x/%c] %.*s\n", (int)lvl, "", (a), *(b), *(b), \
+ (int)(b - c), (const char *)(c))
+#define __file_debugused
+#else
+#define DPRINTF(a, b, c) do { } while (/*CONSTCOND*/0)
+#define __file_debugused __attribute__((__unused__))
+#endif
+
+#define JSON_ARRAY 0
+#define JSON_CONSTANT 1
+#define JSON_NUMBER 2
+#define JSON_OBJECT 3
+#define JSON_STRING 4
+#define JSON_ARRAYN 5
+#define JSON_MAX 6
+
+/*
+ * if JSON_COUNT != 0:
+ * count all the objects, require that we have the whole data file
+ * otherwise:
+ * stop if we find an object or an array
+ */
+#ifndef JSON_COUNT
+#define JSON_COUNT 0
+#endif
+
+static int json_parse(const unsigned char **, const unsigned char *, size_t *,
+ size_t);
+
+static int
+json_isspace(const unsigned char uc)
+{
+ switch (uc) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+json_isdigit(unsigned char uc)
+{
+ switch (uc) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+json_isxdigit(unsigned char uc)
+{
+ if (json_isdigit(uc))
+ return 1;
+ switch (uc) {
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static const unsigned char *
+json_skip_space(const unsigned char *uc, const unsigned char *ue)
+{
+ while (uc < ue && json_isspace(*uc))
+ uc++;
+ return uc;
+}
+
+/*ARGSUSED*/
+static int
+json_parse_string(const unsigned char **ucp, const unsigned char *ue,
+ size_t lvl __file_debugused)
+{
+ const unsigned char *uc = *ucp;
+ size_t i;
+
+ DPRINTF("Parse string: ", uc, *ucp);
+ while (uc < ue) {
+ switch (*uc++) {
+ case '\0':
+ goto out;
+ case '\\':
+ if (uc == ue)
+ goto out;
+ switch (*uc++) {
+ case '\0':
+ goto out;
+ case '"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ continue;
+ case 'u':
+ if (ue - uc < 4) {
+ uc = ue;
+ goto out;
+ }
+ for (i = 0; i < 4; i++)
+ if (!json_isxdigit(*uc++))
+ goto out;
+ continue;
+ default:
+ goto out;
+ }
+ case '"':
+ DPRINTF("Good string: ", uc, *ucp);
+ *ucp = uc;
+ return 1;
+ default:
+ continue;
+ }
+ }
+out:
+ DPRINTF("Bad string: ", uc, *ucp);
+ *ucp = uc;
+ return 0;
+}
+
+static int
+json_parse_array(const unsigned char **ucp, const unsigned char *ue,
+ size_t *st, size_t lvl)
+{
+ const unsigned char *uc = *ucp;
+
+ DPRINTF("Parse array: ", uc, *ucp);
+ while (uc < ue) {
+ uc = json_skip_space(uc, ue);
+ if (uc == ue)
+ goto out;
+ if (*uc == ']')
+ goto done;
+ if (!json_parse(&uc, ue, st, lvl + 1))
+ goto out;
+ if (uc == ue)
+ goto out;
+ switch (*uc) {
+ case ',':
+ uc++;
+ continue;
+ case ']':
+ done:
+ st[JSON_ARRAYN]++;
+ DPRINTF("Good array: ", uc, *ucp);
+ *ucp = uc + 1;
+ return 1;
+ default:
+ goto out;
+ }
+ }
+out:
+ DPRINTF("Bad array: ", uc, *ucp);
+ *ucp = uc;
+ return 0;
+}
+
+static int
+json_parse_object(const unsigned char **ucp, const unsigned char *ue,
+ size_t *st, size_t lvl)
+{
+ const unsigned char *uc = *ucp;
+ DPRINTF("Parse object: ", uc, *ucp);
+ while (uc < ue) {
+ uc = json_skip_space(uc, ue);
+ if (uc == ue)
+ goto out;
+ if (*uc == '}') {
+ uc++;
+ goto done;
+ }
+ if (*uc++ != '"') {
+ DPRINTF("not string", uc, *ucp);
+ goto out;
+ }
+ DPRINTF("next field", uc, *ucp);
+ if (!json_parse_string(&uc, ue, lvl)) {
+ DPRINTF("not string", uc, *ucp);
+ goto out;
+ }
+ uc = json_skip_space(uc, ue);
+ if (uc == ue)
+ goto out;
+ if (*uc++ != ':') {
+ DPRINTF("not colon", uc, *ucp);
+ goto out;
+ }
+ if (!json_parse(&uc, ue, st, lvl + 1)) {
+ DPRINTF("not json", uc, *ucp);
+ goto out;
+ }
+ if (uc == ue)
+ goto out;
+ switch (*uc++) {
+ case ',':
+ continue;
+ case '}': /* { */
+ done:
+ DPRINTF("Good object: ", uc, *ucp);
+ *ucp = uc;
+ return 1;
+ default:
+ DPRINTF("not more", uc, *ucp);
+ *ucp = uc - 1;
+ goto out;
+ }
+ }
+out:
+ DPRINTF("Bad object: ", uc, *ucp);
+ *ucp = uc;
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+json_parse_number(const unsigned char **ucp, const unsigned char *ue,
+ size_t lvl __file_debugused)
+{
+ const unsigned char *uc = *ucp;
+ int got = 0;
+
+ DPRINTF("Parse number: ", uc, *ucp);
+ if (uc == ue)
+ return 0;
+ if (*uc == '-')
+ uc++;
+
+ for (; uc < ue; uc++) {
+ if (!json_isdigit(*uc))
+ break;
+ got = 1;
+ }
+ if (uc == ue)
+ goto out;
+ if (*uc == '.')
+ uc++;
+ for (; uc < ue; uc++) {
+ if (!json_isdigit(*uc))
+ break;
+ got = 1;
+ }
+ if (uc == ue)
+ goto out;
+ if (got && (*uc == 'e' || *uc == 'E')) {
+ uc++;
+ got = 0;
+ if (uc == ue)
+ goto out;
+ if (*uc == '+' || *uc == '-')
+ uc++;
+ for (; uc < ue; uc++) {
+ if (!json_isdigit(*uc))
+ break;
+ got = 1;
+ }
+ }
+out:
+ if (!got)
+ DPRINTF("Bad number: ", uc, *ucp);
+ else
+ DPRINTF("Good number: ", uc, *ucp);
+ *ucp = uc;
+ return got;
+}
+
+/*ARGSUSED*/
+static int
+json_parse_const(const unsigned char **ucp, const unsigned char *ue,
+ const char *str, size_t len, size_t lvl __file_debugused)
+{
+ const unsigned char *uc = *ucp;
+
+ DPRINTF("Parse const: ", uc, *ucp);
+ *ucp += --len - 1;
+ if (*ucp > ue)
+ *ucp = ue;
+ for (; uc < ue && --len;) {
+ if (*uc++ != *++str) {
+ DPRINTF("Bad const: ", uc, *ucp);
+ return 0;
+ }
+ }
+ DPRINTF("Good const: ", uc, *ucp);
+ return 1;
+}
+
+static int
+json_parse(const unsigned char **ucp, const unsigned char *ue,
+ size_t *st, size_t lvl)
+{
+ const unsigned char *uc, *ouc;
+ int rv = 0;
+ int t;
+
+ ouc = uc = json_skip_space(*ucp, ue);
+ if (uc == ue)
+ goto out;
+
+ // Avoid recursion
+ if (lvl > 500) {
+ DPRINTF("Too many levels", uc, *ucp);
+ return 0;
+ }
+#if JSON_COUNT
+ /* bail quickly if not counting */
+ if (lvl > 1 && (st[JSON_OBJECT] || st[JSON_ARRAYN]))
+ return 1;
+#endif
+
+ DPRINTF("Parse general: ", uc, *ucp);
+ switch (*uc++) {
+ case '"':
+ rv = json_parse_string(&uc, ue, lvl + 1);
+ t = JSON_STRING;
+ break;
+ case '[':
+ rv = json_parse_array(&uc, ue, st, lvl + 1);
+ t = JSON_ARRAY;
+ break;
+ case '{': /* '}' */
+ rv = json_parse_object(&uc, ue, st, lvl + 1);
+ t = JSON_OBJECT;
+ break;
+ case 't':
+ rv = json_parse_const(&uc, ue, "true", sizeof("true"), lvl + 1);
+ t = JSON_CONSTANT;
+ break;
+ case 'f':
+ rv = json_parse_const(&uc, ue, "false", sizeof("false"),
+ lvl + 1);
+ t = JSON_CONSTANT;
+ break;
+ case 'n':
+ rv = json_parse_const(&uc, ue, "null", sizeof("null"), lvl + 1);
+ t = JSON_CONSTANT;
+ break;
+ default:
+ --uc;
+ rv = json_parse_number(&uc, ue, lvl + 1);
+ t = JSON_NUMBER;
+ break;
+ }
+ if (rv)
+ st[t]++;
+ uc = json_skip_space(uc, ue);
+out:
+ DPRINTF("End general: ", uc, *ucp);
+ *ucp = uc;
+ if (lvl == 0) {
+ if (!rv)
+ return 0;
+ if (uc == ue)
+ return (st[JSON_ARRAYN] || st[JSON_OBJECT]) ? 1 : 0;
+ if (*ouc == *uc && json_parse(&uc, ue, st, 1))
+ return (st[JSON_ARRAYN] || st[JSON_OBJECT]) ? 2 : 0;
+ else
+ return 0;
+ }
+ return rv;
+}
+
+#ifndef TEST
+int
+file_is_json(struct magic_set *ms, const struct buffer *b)
+{
+ const unsigned char *uc = CAST(const unsigned char *, b->fbuf);
+ const unsigned char *ue = uc + b->flen;
+ size_t st[JSON_MAX];
+ int mime = ms->flags & MAGIC_MIME;
+ int jt;
+
+
+ if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
+ return 0;
+
+ memset(st, 0, sizeof(st));
+
+ if ((jt = json_parse(&uc, ue, st, 0)) == 0)
+ return 0;
+
+ if (mime == MAGIC_MIME_ENCODING)
+ return 1;
+ if (mime) {
+ if (file_printf(ms, "application/%s",
+ jt == 1 ? "json" : "x-ndjson") == -1)
+ return -1;
+ return 1;
+ }
+ if (file_printf(ms, "%sJSON text data",
+ jt == 1 ? "" : "New Line Delimited ") == -1)
+ return -1;
+#if JSON_COUNT
+#define P(n) st[n], st[n] > 1 ? "s" : ""
+ if (file_printf(ms, " (%" SIZE_T_FORMAT "u object%s, %" SIZE_T_FORMAT
+ "u array%s, %" SIZE_T_FORMAT "u string%s, %" SIZE_T_FORMAT
+ "u constant%s, %" SIZE_T_FORMAT "u number%s, %" SIZE_T_FORMAT
+ "u >1array%s)",
+ P(JSON_OBJECT), P(JSON_ARRAY), P(JSON_STRING), P(JSON_CONSTANT),
+ P(JSON_NUMBER), P(JSON_ARRAYN))
+ == -1)
+ return -1;
+#endif
+ return 1;
+}
+
+#else
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <err.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ struct stat st;
+ unsigned char *p;
+ size_t stats[JSON_MAX];
+
+ if ((fd = open(argv[1], O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "Can't open `%s'", argv[1]);
+
+ if (fstat(fd, &st) == -1)
+ err(EXIT_FAILURE, "Can't stat `%s'", argv[1]);
+
+ if ((p = CAST(char *, malloc(st.st_size))) == NULL)
+ err(EXIT_FAILURE, "Can't allocate %jd bytes",
+ (intmax_t)st.st_size);
+ if (read(fd, p, st.st_size) != st.st_size)
+ err(EXIT_FAILURE, "Can't read %jd bytes",
+ (intmax_t)st.st_size);
+ memset(stats, 0, sizeof(stats));
+ printf("is json %d\n", json_parse((const unsigned char **)&p,
+ p + st.st_size, stats, 0));
+ return 0;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/is_simh.c b/contrib/libs/libmagic/src/is_simh.c
new file mode 100644
index 0000000000..4e78173fc4
--- /dev/null
+++ b/contrib/libs/libmagic/src/is_simh.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 2023 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/*
+ * Parse SIM-H tape files
+ * http://simh.trailing-edge.com/docs/simh_magtape.pdf
+ */
+
+#ifndef TEST
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: is_simh.c,v 1.10 2023/07/27 19:39:55 christos Exp $")
+#endif
+
+#include <string.h>
+#include <stddef.h>
+#include "magic.h"
+#else
+#include <stdint.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stddef.h>
+#define CAST(a, b) (a)(b)
+#endif
+
+
+#ifdef DEBUG
+#include <stdio.h>
+#define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+/*
+ * if SIMH_TAPEMARKS == 0:
+ * check all the records and tapemarks
+ * otherwise:
+ * check only up-to the number of tapemarks specified
+ */
+#ifndef SIMH_TAPEMARKS
+#define SIMH_TAPEMARKS 10
+#endif
+
+typedef union {
+ char s[4];
+ uint32_t u;
+} myword;
+
+static myword simh_bo;
+
+#define NEED_SWAP (simh_bo.u == CAST(uint32_t, 0x01020304))
+
+/*
+ * swap an int
+ */
+static uint32_t
+swap4(uint32_t sv)
+{
+ myword d, s;
+ s.u = sv;
+ d.s[0] = s.s[3];
+ d.s[1] = s.s[2];
+ d.s[2] = s.s[1];
+ d.s[3] = s.s[0];
+ return d.u;
+}
+
+
+static uint32_t
+getlen(const unsigned char **uc)
+{
+ uint32_t n;
+ memcpy(&n, *uc, sizeof(n));
+ *uc += sizeof(n);
+ if (NEED_SWAP)
+ n = swap4(n);
+ if (n == 0xffffffff) /* check for End of Medium */
+ return n;
+ n &= 0x00ffffff; /* keep only the record len */
+ if (n & 1)
+ n++;
+ return n;
+}
+
+static int
+simh_parse(const unsigned char *uc, const unsigned char *ue)
+{
+ uint32_t nbytes, cbytes;
+ const unsigned char *orig_uc = uc;
+ size_t nt = 0, nr = 0;
+
+ (void)memcpy(simh_bo.s, "\01\02\03\04", 4);
+
+ while (ue - uc >= CAST(ptrdiff_t, sizeof(nbytes))) {
+ nbytes = getlen(&uc);
+ if ((nt > 0 || nr > 0) && nbytes == 0xFFFFFFFF)
+ /* EOM after at least one record or tapemark */
+ break;
+ if (nbytes == 0) {
+ nt++; /* count tapemarks */
+#if SIMH_TAPEMARKS
+ if (nt == SIMH_TAPEMARKS)
+ break;
+#endif
+ continue;
+ }
+ /* handle a data record */
+ uc += nbytes;
+ if (ue - uc < CAST(ptrdiff_t, sizeof(nbytes)))
+ break;
+ cbytes = getlen(&uc);
+ if (nbytes != cbytes)
+ return 0;
+ nr++;
+ }
+ if (nt * sizeof(uint32_t) == CAST(size_t, uc - orig_uc))
+ return 0; /* All examined data was tapemarks (0) */
+ if (nr == 0) /* No records */
+ return 0;
+ return 1;
+}
+
+#ifndef TEST
+int
+file_is_simh(struct magic_set *ms, const struct buffer *b)
+{
+ const unsigned char *uc = CAST(const unsigned char *, b->fbuf);
+ const unsigned char *ue = uc + b->flen;
+ int mime = ms->flags & MAGIC_MIME;
+
+ if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
+ return 0;
+
+ if (!simh_parse(uc, ue))
+ return 0;
+
+ if (mime == MAGIC_MIME_ENCODING)
+ return 1;
+
+ if (mime) {
+ if (file_printf(ms, "application/SIMH-tape-data") == -1)
+ return -1;
+ return 1;
+ }
+
+ if (file_printf(ms, "SIMH tape data") == -1)
+ return -1;
+
+ return 1;
+}
+
+#else
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <err.h>
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ struct stat st;
+ unsigned char *p;
+
+ if ((fd = open(argv[1], O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "Can't open `%s'", argv[1]);
+
+ if (fstat(fd, &st) == -1)
+ err(EXIT_FAILURE, "Can't stat `%s'", argv[1]);
+
+ if ((p = CAST(char *, malloc(st.st_size))) == NULL)
+ err(EXIT_FAILURE, "Can't allocate %jd bytes",
+ (intmax_t)st.st_size);
+ if (read(fd, p, st.st_size) != st.st_size)
+ err(EXIT_FAILURE, "Can't read %jd bytes",
+ (intmax_t)st.st_size);
+ printf("is simh %d\n", simh_parse(p, p + st.st_size));
+ return 0;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/is_tar.c b/contrib/libs/libmagic/src/is_tar.c
new file mode 100644
index 0000000000..fa83e1e241
--- /dev/null
+++ b/contrib/libs/libmagic/src/is_tar.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * is_tar() -- figure out whether file is a tar archive.
+ *
+ * Stolen (by the author!) from the file_public domain tar program:
+ * Public Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
+ *
+ * @(#)list.c 1.18 9/23/86 Public Domain - gnu
+ *
+ * Comments changed and some code/comments reformatted
+ * for file command by Ian Darwin.
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: is_tar.c,v 1.50 2022/12/26 17:31:14 christos Exp $")
+#endif
+
+#include "magic.h"
+#include <string.h>
+#include <ctype.h>
+#include "tar.h"
+
+#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
+
+file_private int is_tar(const unsigned char *, size_t);
+file_private int from_oct(const char *, size_t); /* Decode octal number */
+
+static const char tartype[][32] = { /* should be equal to messages */
+ "tar archive", /* found in ../magic/Magdir/archive */
+ "POSIX tar archive",
+ "POSIX tar archive (GNU)", /* */
+};
+
+file_protected int
+file_is_tar(struct magic_set *ms, const struct buffer *b)
+{
+ const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
+ size_t nbytes = b->flen;
+ /*
+ * Do the tar test first, because if the first file in the tar
+ * archive starts with a dot, we can confuse it with an nroff file.
+ */
+ int tar;
+ int mime = ms->flags & MAGIC_MIME;
+
+ if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0)
+ return 0;
+
+ tar = is_tar(buf, nbytes);
+ if (tar < 1 || tar > 3)
+ return 0;
+
+ if (mime == MAGIC_MIME_ENCODING)
+ return 1;
+
+ if (file_printf(ms, "%s", mime ? "application/x-tar" :
+ tartype[tar - 1]) == -1)
+ return -1;
+
+ return 1;
+}
+
+/*
+ * Return
+ * 0 if the checksum is bad (i.e., probably not a tar archive),
+ * 1 for old UNIX tar file,
+ * 2 for Unix Std (POSIX) tar file,
+ * 3 for GNU tar file.
+ */
+file_private int
+is_tar(const unsigned char *buf, size_t nbytes)
+{
+ static const char gpkg_match[] = "/gpkg-1";
+
+ const union record *header = RCAST(const union record *,
+ RCAST(const void *, buf));
+ size_t i;
+ int sum, recsum;
+ const unsigned char *p, *ep;
+ const char *nulp;
+
+ if (nbytes < sizeof(*header))
+ return 0;
+
+ /* If the file looks like Gentoo GLEP 78 binary package (GPKG),
+ * don't waste time on further checks and fall back to magic rules.
+ */
+ nulp = CAST(const char *,
+ memchr(header->header.name, 0, sizeof(header->header.name)));
+ if (nulp != NULL && nulp >= header->header.name + sizeof(gpkg_match) &&
+ memcmp(nulp - sizeof(gpkg_match) + 1, gpkg_match,
+ sizeof(gpkg_match)) == 0)
+ return 0;
+
+ recsum = from_oct(header->header.chksum, sizeof(header->header.chksum));
+
+ sum = 0;
+ p = header->charptr;
+ ep = header->charptr + sizeof(*header);
+ while (p < ep)
+ sum += *p++;
+
+ /* Adjust checksum to count the "chksum" field as blanks. */
+ for (i = 0; i < sizeof(header->header.chksum); i++)
+ sum -= header->header.chksum[i];
+ sum += ' ' * sizeof(header->header.chksum);
+
+ if (sum != recsum)
+ return 0; /* Not a tar archive */
+
+ if (strncmp(header->header.magic, GNUTMAGIC,
+ sizeof(header->header.magic)) == 0)
+ return 3; /* GNU Unix Standard tar archive */
+
+ if (strncmp(header->header.magic, TMAGIC,
+ sizeof(header->header.magic)) == 0)
+ return 2; /* Unix Standard tar archive */
+
+ return 1; /* Old fashioned tar archive */
+}
+
+
+/*
+ * Quick and dirty octal conversion.
+ *
+ * Result is -1 if the field is invalid (all blank, or non-octal).
+ */
+file_private int
+from_oct(const char *where, size_t digs)
+{
+ int value;
+
+ if (digs == 0)
+ return -1;
+
+ while (isspace(CAST(unsigned char, *where))) { /* Skip spaces */
+ where++;
+ if (digs-- == 0)
+ return -1; /* All blank field */
+ }
+ value = 0;
+ while (digs > 0 && isodigit(*where)) { /* Scan til non-octal */
+ value = (value << 3) | (*where++ - '0');
+ digs--;
+ }
+
+ if (digs > 0 && *where && !isspace(CAST(unsigned char, *where)))
+ return -1; /* Ended on non-(space/NUL) */
+
+ return value;
+}
diff --git a/contrib/libs/libmagic/src/magic.c b/contrib/libs/libmagic/src/magic.c
new file mode 100644
index 0000000000..e8fbd594db
--- /dev/null
+++ b/contrib/libs/libmagic/src/magic.c
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) Christos Zoulas 2003.
+ * All Rights Reserved.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#ifdef WIN32
+#include <windows.h>
+#include <shlwapi.h>
+#endif
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: magic.c,v 1.121 2023/02/09 17:45:19 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef QUICK
+#include <sys/mman.h>
+#endif
+#include <limits.h> /* for PIPE_BUF */
+
+#if defined(HAVE_UTIMES)
+# include <sys/time.h>
+#elif defined(HAVE_UTIME)
+# if defined(HAVE_SYS_UTIME_H)
+# include <sys/utime.h>
+# elif defined(HAVE_UTIME_H)
+# include <utime.h>
+# endif
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for read() */
+#endif
+
+#ifndef PIPE_BUF
+/* Get the PIPE_BUF from pathconf */
+#ifdef _PC_PIPE_BUF
+#define PIPE_BUF pathconf(".", _PC_PIPE_BUF)
+#else
+#define PIPE_BUF 512
+#endif
+#endif
+
+file_private void close_and_restore(const struct magic_set *, const char *, int,
+ const struct stat *);
+file_private int unreadable_info(struct magic_set *, mode_t, const char *);
+file_private const char* get_default_magic(void);
+#ifndef COMPILE_ONLY
+file_private const char *file_or_fd(struct magic_set *, const char *, int);
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+#ifdef WIN32
+/* HINSTANCE of this shared library. Needed for get_default_magic() */
+static HINSTANCE _w32_dll_instance = NULL;
+
+static void
+_w32_append_path(char **hmagicpath, const char *fmt, ...)
+{
+ char *tmppath;
+ char *newpath;
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (vasprintf(&tmppath, fmt, ap) < 0) {
+ va_end(ap);
+ return;
+ }
+ va_end(ap);
+
+ if (access(tmppath, R_OK) == -1)
+ goto out;
+
+ if (*hmagicpath == NULL) {
+ *hmagicpath = tmppath;
+ return;
+ }
+
+ if (asprintf(&newpath, "%s%c%s", *hmagicpath, PATHSEP, tmppath) < 0)
+ goto out;
+
+ free(*hmagicpath);
+ free(tmppath);
+ *hmagicpath = newpath;
+ return;
+out:
+ free(tmppath);
+}
+
+static void
+_w32_get_magic_relative_to(char **hmagicpath, HINSTANCE module)
+{
+ static const char *trypaths[] = {
+ "%s/share/misc/magic.mgc",
+ "%s/magic.mgc",
+ };
+ LPSTR dllpath;
+ size_t sp;
+
+ dllpath = calloc(MAX_PATH + 1, sizeof(*dllpath));
+
+ if (!GetModuleFileNameA(module, dllpath, MAX_PATH))
+ goto out;
+
+ PathRemoveFileSpecA(dllpath);
+
+ if (module) {
+ char exepath[MAX_PATH];
+ GetModuleFileNameA(NULL, exepath, MAX_PATH);
+ PathRemoveFileSpecA(exepath);
+ if (stricmp(exepath, dllpath) == 0)
+ goto out;
+ }
+
+ sp = strlen(dllpath);
+ if (sp > 3 && stricmp(&dllpath[sp - 3], "bin") == 0) {
+ _w32_append_path(hmagicpath,
+ "%s/../share/misc/magic.mgc", dllpath);
+ goto out;
+ }
+
+ for (sp = 0; sp < __arraycount(trypaths); sp++)
+ _w32_append_path(hmagicpath, trypaths[sp], dllpath);
+out:
+ free(dllpath);
+}
+
+#ifndef BUILD_AS_WINDOWS_STATIC_LIBARAY
+/* Placate GCC by offering a sacrificial previous prototype */
+BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID);
+
+BOOL WINAPI
+DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
+ LPVOID lpvReserved __attribute__((__unused__)))
+{
+ if (fdwReason == DLL_PROCESS_ATTACH)
+ _w32_dll_instance = hinstDLL;
+ return 1;
+}
+#endif
+#endif
+
+file_private const char *
+get_default_magic(void)
+{
+ static const char hmagic[] = "/.magic/magic.mgc";
+ static char *default_magic;
+ char *home, *hmagicpath;
+
+#ifndef WIN32
+ struct stat st;
+
+ if (default_magic) {
+ free(default_magic);
+ default_magic = NULL;
+ }
+ if ((home = getenv("HOME")) == NULL)
+ return MAGIC;
+
+ if (asprintf(&hmagicpath, "%s/.magic.mgc", home) < 0)
+ return MAGIC;
+ if (stat(hmagicpath, &st) == -1) {
+ free(hmagicpath);
+ if (asprintf(&hmagicpath, "%s/.magic", home) < 0)
+ return MAGIC;
+ if (stat(hmagicpath, &st) == -1)
+ goto out;
+ if (S_ISDIR(st.st_mode)) {
+ free(hmagicpath);
+ if (asprintf(&hmagicpath, "%s/%s", home, hmagic) < 0)
+ return MAGIC;
+ if (access(hmagicpath, R_OK) == -1)
+ goto out;
+ }
+ }
+
+ if (asprintf(&default_magic, "%s:%s", hmagicpath, MAGIC) < 0)
+ goto out;
+ free(hmagicpath);
+ return default_magic;
+out:
+ default_magic = NULL;
+ free(hmagicpath);
+ return MAGIC;
+#else
+ hmagicpath = NULL;
+
+ if (default_magic) {
+ free(default_magic);
+ default_magic = NULL;
+ }
+
+ /* Before anything else, try to get a magic file from user HOME */
+ if ((home = getenv("HOME")) != NULL)
+ _w32_append_path(&hmagicpath, "%s%s", home, hmagic);
+
+ /* First, try to get a magic file from user-application data */
+ if ((home = getenv("LOCALAPPDATA")) != NULL)
+ _w32_append_path(&hmagicpath, "%s%s", home, hmagic);
+
+ /* Second, try to get a magic file from the user profile data */
+ if ((home = getenv("USERPROFILE")) != NULL)
+ _w32_append_path(&hmagicpath,
+ "%s/Local Settings/Application Data%s", home, hmagic);
+
+ /* Third, try to get a magic file from Common Files */
+ if ((home = getenv("COMMONPROGRAMFILES")) != NULL)
+ _w32_append_path(&hmagicpath, "%s%s", home, hmagic);
+
+ /* Fourth, try to get magic file relative to exe location */
+ _w32_get_magic_relative_to(&hmagicpath, NULL);
+
+ /* Fifth, try to get magic file relative to dll location */
+ _w32_get_magic_relative_to(&hmagicpath, _w32_dll_instance);
+
+ /* Avoid MAGIC constant - it likely points to a file within MSys tree */
+ default_magic = hmagicpath;
+ return default_magic;
+#endif
+}
+
+file_public const char *
+magic_getpath(const char *magicfile, int action)
+{
+ if (magicfile != NULL)
+ return magicfile;
+
+ magicfile = getenv("MAGIC");
+ if (magicfile != NULL)
+ return magicfile;
+
+ return MAGIC;
+}
+
+file_public struct magic_set *
+magic_open(int flags)
+{
+ return file_ms_alloc(flags);
+}
+
+file_private int
+unreadable_info(struct magic_set *ms, mode_t md, const char *file)
+{
+ if (file) {
+ /* We cannot open it, but we were able to stat it. */
+ if (access(file, W_OK) == 0)
+ if (file_printf(ms, "writable, ") == -1)
+ return -1;
+#ifndef WIN32
+ if (access(file, X_OK) == 0)
+ if (file_printf(ms, "executable, ") == -1)
+ return -1;
+#else
+ /* X_OK doesn't work well on MS-Windows */
+ {
+ const char *p = strrchr(file, '.');
+ if (p && (stricmp(p, ".exe")
+ || stricmp(p, ".dll")
+ || stricmp(p, ".bat")
+ || stricmp(p, ".cmd")))
+ if (file_printf(ms, "writable, ") == -1)
+ return -1;
+ }
+#endif
+ }
+ if (S_ISREG(md))
+ if (file_printf(ms, "regular file, ") == -1)
+ return -1;
+ if (file_printf(ms, "no read permission") == -1)
+ return -1;
+ return 0;
+}
+
+file_public void
+magic_close(struct magic_set *ms)
+{
+ if (ms == NULL)
+ return;
+ file_ms_free(ms);
+}
+
+/*
+ * load a magic file
+ */
+file_public int
+magic_load(struct magic_set *ms, const char *magicfile)
+{
+ if (ms == NULL)
+ return -1;
+ return file_apprentice(ms, magicfile, FILE_LOAD);
+}
+
+#ifndef COMPILE_ONLY
+/*
+ * Install a set of compiled magic buffers.
+ */
+file_public int
+magic_load_buffers(struct magic_set *ms, void **bufs, size_t *sizes,
+ size_t nbufs)
+{
+ if (ms == NULL)
+ return -1;
+ return buffer_apprentice(ms, RCAST(struct magic **, bufs),
+ sizes, nbufs);
+}
+#endif
+
+file_public int
+magic_compile(struct magic_set *ms, const char *magicfile)
+{
+ if (ms == NULL)
+ return -1;
+ return file_apprentice(ms, magicfile, FILE_COMPILE);
+}
+
+file_public int
+magic_check(struct magic_set *ms, const char *magicfile)
+{
+ if (ms == NULL)
+ return -1;
+ return file_apprentice(ms, magicfile, FILE_CHECK);
+}
+
+file_public int
+magic_list(struct magic_set *ms, const char *magicfile)
+{
+ if (ms == NULL)
+ return -1;
+ return file_apprentice(ms, magicfile, FILE_LIST);
+}
+
+file_private void
+close_and_restore(const struct magic_set *ms, const char *name, int fd,
+ const struct stat *sb)
+{
+ if (fd == STDIN_FILENO || name == NULL)
+ return;
+ (void) close(fd);
+
+ if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) {
+ /*
+ * Try to restore access, modification times if read it.
+ * This is really *bad* because it will modify the status
+ * time of the file... And of course this will affect
+ * backup programs
+ */
+#ifdef HAVE_UTIMES
+ struct timeval utsbuf[2];
+ (void)memset(utsbuf, 0, sizeof(utsbuf));
+ utsbuf[0].tv_sec = sb->st_atime;
+ utsbuf[1].tv_sec = sb->st_mtime;
+
+ (void) utimes(name, utsbuf); /* don't care if loses */
+#elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H)
+ struct utimbuf utbuf;
+
+ (void)memset(&utbuf, 0, sizeof(utbuf));
+ utbuf.actime = sb->st_atime;
+ utbuf.modtime = sb->st_mtime;
+ (void) utime(name, &utbuf); /* don't care if loses */
+#endif
+ }
+}
+
+#ifndef COMPILE_ONLY
+
+/*
+ * find type of descriptor
+ */
+file_public const char *
+magic_descriptor(struct magic_set *ms, int fd)
+{
+ if (ms == NULL)
+ return NULL;
+ return file_or_fd(ms, NULL, fd);
+}
+
+/*
+ * find type of named file
+ */
+file_public const char *
+magic_file(struct magic_set *ms, const char *inname)
+{
+ if (ms == NULL)
+ return NULL;
+ return file_or_fd(ms, inname, STDIN_FILENO);
+}
+
+file_private const char *
+file_or_fd(struct magic_set *ms, const char *inname, int fd)
+{
+ int rv = -1;
+ unsigned char *buf;
+ struct stat sb;
+ ssize_t nbytes = 0; /* number of bytes read from a datafile */
+ int ispipe = 0;
+ int okstat = 0;
+ off_t pos = CAST(off_t, -1);
+
+ if (file_reset(ms, 1) == -1)
+ goto out;
+
+ /*
+ * one extra for terminating '\0', and
+ * some overlapping space for matches near EOF
+ */
+#define SLOP (1 + sizeof(union VALUETYPE))
+ if ((buf = CAST(unsigned char *, malloc(ms->bytes_max + SLOP))) == NULL)
+ return NULL;
+
+ switch (file_fsmagic(ms, inname, &sb)) {
+ case -1: /* error */
+ goto done;
+ case 0: /* nothing found */
+ break;
+ default: /* matched it and printed type */
+ rv = 0;
+ goto done;
+ }
+
+#ifdef WIN32
+ /* Place stdin in binary mode, so EOF (Ctrl+Z) doesn't stop early. */
+ if (fd == STDIN_FILENO)
+ _setmode(STDIN_FILENO, O_BINARY);
+#endif
+ if (inname != NULL) {
+ int flags = O_RDONLY|O_BINARY|O_NONBLOCK|O_CLOEXEC;
+ errno = 0;
+ if ((fd = open(inname, flags)) < 0) {
+ okstat = stat(inname, &sb) == 0;
+#ifdef WIN32
+ /*
+ * Can't stat, can't open. It may have been opened in
+ * fsmagic, so if the user doesn't have read permission,
+ * allow it to say so; otherwise an error was probably
+ * displayed in fsmagic.
+ */
+ if (!okstat && errno == EACCES) {
+ sb.st_mode = S_IFBLK;
+ okstat = 1;
+ }
+#endif
+ if (okstat &&
+ unreadable_info(ms, sb.st_mode, inname) == -1)
+ goto done;
+ rv = 0;
+ goto done;
+ }
+#if O_CLOEXEC == 0 && defined(F_SETFD)
+ (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+ }
+
+ if (fd != -1) {
+ okstat = fstat(fd, &sb) == 0;
+ if (okstat && S_ISFIFO(sb.st_mode))
+ ispipe = 1;
+ if (inname == NULL)
+ pos = lseek(fd, CAST(off_t, 0), SEEK_CUR);
+ }
+
+ /*
+ * try looking at the first ms->bytes_max bytes
+ */
+ if (ispipe) {
+ if (fd != -1) {
+ ssize_t r = 0;
+
+ while ((r = sread(fd, RCAST(void *, &buf[nbytes]),
+ CAST(size_t, ms->bytes_max - nbytes), 1)) > 0) {
+ nbytes += r;
+ if (r < PIPE_BUF) break;
+ }
+ }
+
+ if (nbytes == 0 && inname) {
+ /* We can not read it, but we were able to stat it. */
+ if (unreadable_info(ms, sb.st_mode, inname) == -1)
+ goto done;
+ rv = 0;
+ goto done;
+ }
+
+ } else if (fd != -1) {
+ /* Windows refuses to read from a big console buffer. */
+ size_t howmany =
+#ifdef WIN32
+ _isatty(fd) ? 8 * 1024 :
+#endif
+ ms->bytes_max;
+ if ((nbytes = read(fd, RCAST(void *, buf), howmany)) == -1) {
+ if (inname == NULL && fd != STDIN_FILENO)
+ file_error(ms, errno, "cannot read fd %d", fd);
+ else
+ file_error(ms, errno, "cannot read `%s'",
+ inname == NULL ? "/dev/stdin" : inname);
+ goto done;
+ }
+ }
+
+ (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */
+ if (file_buffer(ms, fd, okstat ? &sb : NULL, inname, buf, CAST(size_t, nbytes)) == -1)
+ goto done;
+ rv = 0;
+done:
+ free(buf);
+ if (fd != -1) {
+ if (pos != CAST(off_t, -1))
+ (void)lseek(fd, pos, SEEK_SET);
+ close_and_restore(ms, inname, fd, &sb);
+ }
+out:
+ return rv == 0 ? file_getbuffer(ms) : NULL;
+}
+
+
+file_public const char *
+magic_buffer(struct magic_set *ms, const void *buf, size_t nb)
+{
+ if (ms == NULL)
+ return NULL;
+ if (file_reset(ms, 1) == -1)
+ return NULL;
+ /*
+ * The main work is done here!
+ * We have the file name and/or the data buffer to be identified.
+ */
+ if (file_buffer(ms, -1, NULL, NULL, buf, nb) == -1) {
+ return NULL;
+ }
+ return file_getbuffer(ms);
+}
+#endif
+
+file_public const char *
+magic_error(struct magic_set *ms)
+{
+ if (ms == NULL)
+ return "Magic database is not open";
+ return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL;
+}
+
+file_public int
+magic_errno(struct magic_set *ms)
+{
+ if (ms == NULL)
+ return EINVAL;
+ return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0;
+}
+
+file_public int
+magic_getflags(struct magic_set *ms)
+{
+ if (ms == NULL)
+ return -1;
+
+ return ms->flags;
+}
+
+file_public int
+magic_setflags(struct magic_set *ms, int flags)
+{
+ if (ms == NULL)
+ return -1;
+#if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES)
+ if (flags & MAGIC_PRESERVE_ATIME)
+ return -1;
+#endif
+ ms->flags = flags;
+ return 0;
+}
+
+file_public int
+magic_version(void)
+{
+ return MAGIC_VERSION;
+}
+
+file_public int
+magic_setparam(struct magic_set *ms, int param, const void *val)
+{
+ if (ms == NULL)
+ return -1;
+ switch (param) {
+ case MAGIC_PARAM_INDIR_MAX:
+ ms->indir_max = CAST(uint16_t, *CAST(const size_t *, val));
+ return 0;
+ case MAGIC_PARAM_NAME_MAX:
+ ms->name_max = CAST(uint16_t, *CAST(const size_t *, val));
+ return 0;
+ case MAGIC_PARAM_ELF_PHNUM_MAX:
+ ms->elf_phnum_max = CAST(uint16_t, *CAST(const size_t *, val));
+ return 0;
+ case MAGIC_PARAM_ELF_SHNUM_MAX:
+ ms->elf_shnum_max = CAST(uint16_t, *CAST(const size_t *, val));
+ return 0;
+ case MAGIC_PARAM_ELF_SHSIZE_MAX:
+ ms->elf_shsize_max = *CAST(const size_t *, val);
+ return 0;
+ case MAGIC_PARAM_ELF_NOTES_MAX:
+ ms->elf_notes_max = CAST(uint16_t, *CAST(const size_t *, val));
+ return 0;
+ case MAGIC_PARAM_REGEX_MAX:
+ ms->regex_max = CAST(uint16_t, *CAST(const size_t *, val));
+ return 0;
+ case MAGIC_PARAM_BYTES_MAX:
+ ms->bytes_max = *CAST(const size_t *, val);
+ return 0;
+ case MAGIC_PARAM_ENCODING_MAX:
+ ms->encoding_max = *CAST(const size_t *, val);
+ return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+file_public int
+magic_getparam(struct magic_set *ms, int param, void *val)
+{
+ if (ms == NULL)
+ return -1;
+ switch (param) {
+ case MAGIC_PARAM_INDIR_MAX:
+ *CAST(size_t *, val) = ms->indir_max;
+ return 0;
+ case MAGIC_PARAM_NAME_MAX:
+ *CAST(size_t *, val) = ms->name_max;
+ return 0;
+ case MAGIC_PARAM_ELF_PHNUM_MAX:
+ *CAST(size_t *, val) = ms->elf_phnum_max;
+ return 0;
+ case MAGIC_PARAM_ELF_SHNUM_MAX:
+ *CAST(size_t *, val) = ms->elf_shnum_max;
+ return 0;
+ case MAGIC_PARAM_ELF_SHSIZE_MAX:
+ *CAST(size_t *, val) = ms->elf_shsize_max;
+ return 0;
+ case MAGIC_PARAM_ELF_NOTES_MAX:
+ *CAST(size_t *, val) = ms->elf_notes_max;
+ return 0;
+ case MAGIC_PARAM_REGEX_MAX:
+ *CAST(size_t *, val) = ms->regex_max;
+ return 0;
+ case MAGIC_PARAM_BYTES_MAX:
+ *CAST(size_t *, val) = ms->bytes_max;
+ return 0;
+ case MAGIC_PARAM_ENCODING_MAX:
+ *CAST(size_t *, val) = ms->encoding_max;
+ return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
diff --git a/contrib/libs/libmagic/src/magic.h b/contrib/libs/libmagic/src/magic.h
new file mode 100644
index 0000000000..29961f3936
--- /dev/null
+++ b/contrib/libs/libmagic/src/magic.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) Christos Zoulas 2003.
+ * All Rights Reserved.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+#ifndef _MAGIC_H
+#define _MAGIC_H
+
+#include <sys/types.h>
+
+#define MAGIC_NONE 0x0000000 /* No flags */
+#define MAGIC_DEBUG 0x0000001 /* Turn on debugging */
+#define MAGIC_SYMLINK 0x0000002 /* Follow symlinks */
+#define MAGIC_COMPRESS 0x0000004 /* Check inside compressed files */
+#define MAGIC_DEVICES 0x0000008 /* Look at the contents of devices */
+#define MAGIC_MIME_TYPE 0x0000010 /* Return the MIME type */
+#define MAGIC_CONTINUE 0x0000020 /* Return all matches */
+#define MAGIC_CHECK 0x0000040 /* Print warnings to stderr */
+#define MAGIC_PRESERVE_ATIME 0x0000080 /* Restore access time on exit */
+#define MAGIC_RAW 0x0000100 /* Don't convert unprintable chars */
+#define MAGIC_ERROR 0x0000200 /* Handle ENOENT etc as real errors */
+#define MAGIC_MIME_ENCODING 0x0000400 /* Return the MIME encoding */
+#define MAGIC_MIME (MAGIC_MIME_TYPE|MAGIC_MIME_ENCODING)
+#define MAGIC_APPLE 0x0000800 /* Return the Apple creator/type */
+#define MAGIC_EXTENSION 0x1000000 /* Return a /-separated list of
+ * extensions */
+#define MAGIC_COMPRESS_TRANSP 0x2000000 /* Check inside compressed files
+ * but not report compression */
+#define MAGIC_NO_COMPRESS_FORK 0x4000000 /* Don't allow decompression that
+ * needs to fork */
+#define MAGIC_NODESC (MAGIC_EXTENSION|MAGIC_MIME|MAGIC_APPLE)
+
+#define MAGIC_NO_CHECK_COMPRESS 0x0001000 /* Don't check for compressed files */
+#define MAGIC_NO_CHECK_TAR 0x0002000 /* Don't check for tar files */
+#define MAGIC_NO_CHECK_SOFT 0x0004000 /* Don't check magic entries */
+#define MAGIC_NO_CHECK_APPTYPE 0x0008000 /* Don't check application type */
+#define MAGIC_NO_CHECK_ELF 0x0010000 /* Don't check for elf details */
+#define MAGIC_NO_CHECK_TEXT 0x0020000 /* Don't check for text files */
+#define MAGIC_NO_CHECK_CDF 0x0040000 /* Don't check for cdf files */
+#define MAGIC_NO_CHECK_CSV 0x0080000 /* Don't check for CSV files */
+#define MAGIC_NO_CHECK_TOKENS 0x0100000 /* Don't check tokens */
+#define MAGIC_NO_CHECK_ENCODING 0x0200000 /* Don't check text encodings */
+#define MAGIC_NO_CHECK_JSON 0x0400000 /* Don't check for JSON files */
+#define MAGIC_NO_CHECK_SIMH 0x0800000 /* Don't check for SIMH tape files */
+
+/* No built-in tests; only consult the magic file */
+#define MAGIC_NO_CHECK_BUILTIN ( \
+ MAGIC_NO_CHECK_COMPRESS | \
+ MAGIC_NO_CHECK_TAR | \
+/* MAGIC_NO_CHECK_SOFT | */ \
+ MAGIC_NO_CHECK_APPTYPE | \
+ MAGIC_NO_CHECK_ELF | \
+ MAGIC_NO_CHECK_TEXT | \
+ MAGIC_NO_CHECK_CSV | \
+ MAGIC_NO_CHECK_CDF | \
+ MAGIC_NO_CHECK_TOKENS | \
+ MAGIC_NO_CHECK_ENCODING | \
+ MAGIC_NO_CHECK_JSON | \
+ MAGIC_NO_CHECK_SIMH | \
+ 0 \
+)
+
+#define MAGIC_SNPRINTB "\177\020\
+b\0debug\0\
+b\1symlink\0\
+b\2compress\0\
+b\3devices\0\
+b\4mime_type\0\
+b\5continue\0\
+b\6check\0\
+b\7preserve_atime\0\
+b\10raw\0\
+b\11error\0\
+b\12mime_encoding\0\
+b\13apple\0\
+b\14no_check_compress\0\
+b\15no_check_tar\0\
+b\16no_check_soft\0\
+b\17no_check_sapptype\0\
+b\20no_check_elf\0\
+b\21no_check_text\0\
+b\22no_check_cdf\0\
+b\23no_check_csv\0\
+b\24no_check_tokens\0\
+b\25no_check_encoding\0\
+b\26no_check_json\0\
+b\27no_check_simh\0\
+b\30extension\0\
+b\31transp_compression\0\
+"
+
+/* Defined for backwards compatibility (renamed) */
+#define MAGIC_NO_CHECK_ASCII MAGIC_NO_CHECK_TEXT
+
+/* Defined for backwards compatibility; do nothing */
+#define MAGIC_NO_CHECK_FORTRAN 0x000000 /* Don't check ascii/fortran */
+#define MAGIC_NO_CHECK_TROFF 0x000000 /* Don't check ascii/troff */
+
+#define MAGIC_VERSION 545 /* This implementation */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct magic_set *magic_t;
+magic_t magic_open(int);
+void magic_close(magic_t);
+
+const char *magic_getpath(const char *, int);
+const char *magic_file(magic_t, const char *);
+const char *magic_descriptor(magic_t, int);
+const char *magic_buffer(magic_t, const void *, size_t);
+
+const char *magic_error(magic_t);
+int magic_getflags(magic_t);
+int magic_setflags(magic_t, int);
+
+int magic_version(void);
+int magic_load(magic_t, const char *);
+int magic_load_buffers(magic_t, void **, size_t *, size_t);
+
+int magic_compile(magic_t, const char *);
+int magic_check(magic_t, const char *);
+int magic_list(magic_t, const char *);
+int magic_errno(magic_t);
+
+#define MAGIC_PARAM_INDIR_MAX 0
+#define MAGIC_PARAM_NAME_MAX 1
+#define MAGIC_PARAM_ELF_PHNUM_MAX 2
+#define MAGIC_PARAM_ELF_SHNUM_MAX 3
+#define MAGIC_PARAM_ELF_NOTES_MAX 4
+#define MAGIC_PARAM_REGEX_MAX 5
+#define MAGIC_PARAM_BYTES_MAX 6
+#define MAGIC_PARAM_ENCODING_MAX 7
+#define MAGIC_PARAM_ELF_SHSIZE_MAX 8
+
+int magic_setparam(magic_t, int, const void *);
+int magic_getparam(magic_t, int, void *);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* _MAGIC_H */
diff --git a/contrib/libs/libmagic/src/print.c b/contrib/libs/libmagic/src/print.c
new file mode 100644
index 0000000000..9ab383aad0
--- /dev/null
+++ b/contrib/libs/libmagic/src/print.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * print.c - debugging printout routines
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: print.c,v 1.99 2023/07/17 16:40:57 christos Exp $")
+#endif /* lint */
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+
+#include "cdf.h"
+
+#ifndef COMPILE_ONLY
+file_protected void
+file_mdump(struct magic *m)
+{
+ static const char optyp[] = { FILE_OPS };
+ char tbuf[256];
+
+ (void) fprintf(stderr, "%u: %.*s %d", m->lineno,
+ (m->cont_level & 7) + 1, ">>>>>>>>", m->offset);
+
+ if (m->flag & INDIR) {
+ (void) fprintf(stderr, "(%s,",
+ /* Note: type is unsigned */
+ (m->in_type < file_nnames) ? file_names[m->in_type] :
+ "*bad in_type*");
+ if (m->in_op & FILE_OPINVERSE)
+ (void) fputc('~', stderr);
+ (void) fprintf(stderr, "%c%d),",
+ (CAST(size_t, m->in_op & FILE_OPS_MASK) <
+ __arraycount(optyp)) ?
+ optyp[m->in_op & FILE_OPS_MASK] : '?', m->in_offset);
+ }
+ (void) fprintf(stderr, " %s%s", (m->flag & UNSIGNED) ? "u" : "",
+ /* Note: type is unsigned */
+ (m->type < file_nnames) ? file_names[m->type] : "*bad type");
+ if (m->mask_op & FILE_OPINVERSE)
+ (void) fputc('~', stderr);
+
+ if (IS_STRING(m->type)) {
+ if (m->str_flags) {
+ (void) fputc('/', stderr);
+ if (m->str_flags & STRING_COMPACT_WHITESPACE)
+ (void) fputc(CHAR_COMPACT_WHITESPACE, stderr);
+ if (m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE)
+ (void) fputc(CHAR_COMPACT_OPTIONAL_WHITESPACE,
+ stderr);
+ if (m->str_flags & STRING_IGNORE_LOWERCASE)
+ (void) fputc(CHAR_IGNORE_LOWERCASE, stderr);
+ if (m->str_flags & STRING_IGNORE_UPPERCASE)
+ (void) fputc(CHAR_IGNORE_UPPERCASE, stderr);
+ if (m->str_flags & REGEX_OFFSET_START)
+ (void) fputc(CHAR_REGEX_OFFSET_START, stderr);
+ if (m->str_flags & STRING_TEXTTEST)
+ (void) fputc(CHAR_TEXTTEST, stderr);
+ if (m->str_flags & STRING_BINTEST)
+ (void) fputc(CHAR_BINTEST, stderr);
+ if (m->str_flags & PSTRING_1_BE)
+ (void) fputc(CHAR_PSTRING_1_BE, stderr);
+ if (m->str_flags & PSTRING_2_BE)
+ (void) fputc(CHAR_PSTRING_2_BE, stderr);
+ if (m->str_flags & PSTRING_2_LE)
+ (void) fputc(CHAR_PSTRING_2_LE, stderr);
+ if (m->str_flags & PSTRING_4_BE)
+ (void) fputc(CHAR_PSTRING_4_BE, stderr);
+ if (m->str_flags & PSTRING_4_LE)
+ (void) fputc(CHAR_PSTRING_4_LE, stderr);
+ if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF)
+ (void) fputc(
+ CHAR_PSTRING_LENGTH_INCLUDES_ITSELF,
+ stderr);
+ }
+ if (m->str_range)
+ (void) fprintf(stderr, "/%u", m->str_range);
+ }
+ else {
+ if (CAST(size_t, m->mask_op & FILE_OPS_MASK) <
+ __arraycount(optyp))
+ (void) fputc(optyp[m->mask_op & FILE_OPS_MASK], stderr);
+ else
+ (void) fputc('?', stderr);
+
+ if (m->num_mask) {
+ (void) fprintf(stderr, "%.8llx",
+ CAST(unsigned long long, m->num_mask));
+ }
+ }
+ (void) fprintf(stderr, ",%c", m->reln);
+
+ if (m->reln != 'x') {
+ switch (m->type) {
+ case FILE_BYTE:
+ case FILE_SHORT:
+ case FILE_LONG:
+ case FILE_LESHORT:
+ case FILE_LELONG:
+ case FILE_MELONG:
+ case FILE_BESHORT:
+ case FILE_BELONG:
+ case FILE_INDIRECT:
+ (void) fprintf(stderr, "%d", CAST(int32_t, m->value.l));
+ break;
+ case FILE_BEQUAD:
+ case FILE_LEQUAD:
+ case FILE_QUAD:
+ case FILE_OFFSET:
+ (void) fprintf(stderr, "%" INT64_T_FORMAT "d",
+ CAST(long long, m->value.q));
+ break;
+ case FILE_PSTRING:
+ case FILE_STRING:
+ case FILE_REGEX:
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ case FILE_SEARCH:
+ file_showstr(stderr, m->value.s,
+ CAST(size_t, m->vallen));
+ break;
+ case FILE_DATE:
+ case FILE_LEDATE:
+ case FILE_BEDATE:
+ case FILE_MEDATE:
+ (void)fprintf(stderr, "%s,",
+ file_fmtdatetime(tbuf, sizeof(tbuf), m->value.l, 0));
+ break;
+ case FILE_LDATE:
+ case FILE_LELDATE:
+ case FILE_BELDATE:
+ case FILE_MELDATE:
+ (void)fprintf(stderr, "%s,",
+ file_fmtdatetime(tbuf, sizeof(tbuf), m->value.l,
+ FILE_T_LOCAL));
+ break;
+ case FILE_QDATE:
+ case FILE_LEQDATE:
+ case FILE_BEQDATE:
+ (void)fprintf(stderr, "%s,",
+ file_fmtdatetime(tbuf, sizeof(tbuf), m->value.q, 0));
+ break;
+ case FILE_QLDATE:
+ case FILE_LEQLDATE:
+ case FILE_BEQLDATE:
+ (void)fprintf(stderr, "%s,",
+ file_fmtdatetime(tbuf, sizeof(tbuf), m->value.q,
+ FILE_T_LOCAL));
+ break;
+ case FILE_QWDATE:
+ case FILE_LEQWDATE:
+ case FILE_BEQWDATE:
+ (void)fprintf(stderr, "%s,",
+ file_fmtdatetime(tbuf, sizeof(tbuf), m->value.q,
+ FILE_T_WINDOWS));
+ break;
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ (void) fprintf(stderr, "%G", m->value.f);
+ break;
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ (void) fprintf(stderr, "%G", m->value.d);
+ break;
+ case FILE_LEVARINT:
+ case FILE_BEVARINT:
+ (void)fprintf(stderr, "%s", file_fmtvarint(
+ tbuf, sizeof(tbuf), m->value.us, m->type));
+ break;
+ case FILE_MSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ (void)fprintf(stderr, "%s,",
+ file_fmtdate(tbuf, sizeof(tbuf), m->value.h));
+ break;
+ case FILE_MSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ (void)fprintf(stderr, "%s,",
+ file_fmttime(tbuf, sizeof(tbuf), m->value.h));
+ break;
+ case FILE_OCTAL:
+ (void)fprintf(stderr, "%s",
+ file_fmtnum(tbuf, sizeof(tbuf), m->value.s, 8));
+ break;
+ case FILE_DEFAULT:
+ /* XXX - do anything here? */
+ break;
+ case FILE_USE:
+ case FILE_NAME:
+ case FILE_DER:
+ (void) fprintf(stderr, "'%s'", m->value.s);
+ break;
+ case FILE_GUID:
+ (void) file_print_guid(tbuf, sizeof(tbuf),
+ m->value.guid);
+ (void) fprintf(stderr, "%s", tbuf);
+ break;
+
+ default:
+ (void) fprintf(stderr, "*bad type %d*", m->type);
+ break;
+ }
+ }
+ (void) fprintf(stderr, ",\"%s\"]\n", m->desc);
+}
+#endif
+
+/*VARARGS*/
+file_protected void
+file_magwarn(struct magic_set *ms, const char *f, ...)
+{
+ va_list va;
+
+ /* cuz we use stdout for most, stderr here */
+ (void) fflush(stdout);
+
+ if (ms && ms->file)
+ (void) fprintf(stderr, "%s, %lu: ", ms->file,
+ CAST(unsigned long, ms->line));
+ (void) fprintf(stderr, "Warning: ");
+ va_start(va, f);
+ (void) vfprintf(stderr, f, va);
+ va_end(va);
+ (void) fputc('\n', stderr);
+}
+
+file_protected const char *
+file_fmtvarint(char *buf, size_t blen, const unsigned char *us, int t)
+{
+ snprintf(buf, blen, "%jd", CAST(intmax_t,
+ file_varint2uintmax_t(us, t, NULL)));
+ return buf;
+}
+
+file_protected const char *
+file_fmtdatetime(char *buf, size_t bsize, uint64_t v, int flags)
+{
+ char *pp;
+ time_t t;
+ struct tm *tm, tmz;
+
+ if (flags & FILE_T_WINDOWS) {
+ struct timespec ts;
+ cdf_timestamp_to_timespec(&ts, CAST(cdf_timestamp_t, v));
+ t = ts.tv_sec;
+ } else {
+ // XXX: perhaps detect and print something if overflow
+ // on 32 bit time_t?
+ t = CAST(time_t, v);
+ }
+
+ if (t > MAX_CTIME)
+ goto out;
+
+ if (flags & FILE_T_LOCAL) {
+ tm = localtime_r(&t, &tmz);
+ } else {
+ tm = gmtime_r(&t, &tmz);
+ }
+ if (tm == NULL)
+ goto out;
+ pp = asctime_r(tm, buf);
+
+ if (pp == NULL)
+ goto out;
+ pp[strcspn(pp, "\n")] = '\0';
+ return pp;
+out:
+ strlcpy(buf, "*Invalid datetime*", bsize);
+ return buf;
+}
+
+/*
+ * https://docs.microsoft.com/en-us/windows/win32/api/winbase/\
+ * nf-winbase-dosdatetimetofiletime?redirectedfrom=MSDN
+ */
+file_protected const char *
+file_fmtdate(char *buf, size_t bsize, uint16_t v)
+{
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_mday = v & 0x1f;
+ tm.tm_mon = ((v >> 5) & 0xf) - 1;
+ tm.tm_year = (v >> 9) + 80;
+
+ if (strftime(buf, bsize, "%a, %b %d %Y", &tm) == 0)
+ goto out;
+
+ return buf;
+out:
+ strlcpy(buf, "*Invalid date*", bsize);
+ return buf;
+}
+
+file_protected const char *
+file_fmttime(char *buf, size_t bsize, uint16_t v)
+{
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_sec = (v & 0x1f) * 2;
+ tm.tm_min = ((v >> 5) & 0x3f);
+ tm.tm_hour = (v >> 11);
+
+ if (strftime(buf, bsize, "%T", &tm) == 0)
+ goto out;
+
+ return buf;
+out:
+ strlcpy(buf, "*Invalid time*", bsize);
+ return buf;
+
+}
+
+file_protected const char *
+file_fmtnum(char *buf, size_t blen, const char *us, int base)
+{
+ char *endptr;
+ unsigned long long val;
+
+ errno = 0;
+ val = strtoull(us, &endptr, base);
+ if (*endptr || errno) {
+bad: strlcpy(buf, "*Invalid number*", blen);
+ return buf;
+ }
+
+ if (snprintf(buf, blen, "%llu", val) < 0)
+ goto bad;
+ return buf;
+}
diff --git a/contrib/libs/libmagic/src/readcdf.c b/contrib/libs/libmagic/src/readcdf.c
new file mode 100644
index 0000000000..30c3d24667
--- /dev/null
+++ b/contrib/libs/libmagic/src/readcdf.c
@@ -0,0 +1,682 @@
+/*-
+ * Copyright (c) 2008, 2016 Christos Zoulas
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: readcdf.c,v 1.80 2023/01/24 20:13:40 christos Exp $")
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "cdf.h"
+#include "magic.h"
+
+#define NOTMIME(ms) (((ms)->flags & MAGIC_MIME) == 0)
+
+static const struct nv {
+ const char *pattern;
+ const char *mime;
+} app2mime[] = {
+ { "Word", "msword", },
+ { "Excel", "vnd.ms-excel", },
+ { "Powerpoint", "vnd.ms-powerpoint", },
+ { "Crystal Reports", "x-rpt", },
+ { "Advanced Installer", "vnd.ms-msi", },
+ { "InstallShield", "vnd.ms-msi", },
+ { "Microsoft Patch Compiler", "vnd.ms-msi", },
+ { "NAnt", "vnd.ms-msi", },
+ { "Windows Installer", "vnd.ms-msi", },
+ { NULL, NULL, },
+}, name2mime[] = {
+ { "Book", "vnd.ms-excel", },
+ { "Workbook", "vnd.ms-excel", },
+ { "WordDocument", "msword", },
+ { "PowerPoint", "vnd.ms-powerpoint", },
+ { "DigitalSignature", "vnd.ms-msi", },
+ { NULL, NULL, },
+}, name2desc[] = {
+ { "Book", "Microsoft Excel", },
+ { "Workbook", "Microsoft Excel", },
+ { "WordDocument", "Microsoft Word", },
+ { "PowerPoint", "Microsoft PowerPoint", },
+ { "DigitalSignature", "Microsoft Installer", },
+ { NULL, NULL, },
+};
+
+static const struct cv {
+ uint64_t clsid[2];
+ const char *mime;
+} clsid2mime[] = {
+ {
+ { 0x00000000000c1084ULL, 0x46000000000000c0ULL },
+ "x-msi",
+ },
+ { { 0, 0 },
+ NULL,
+ },
+}, clsid2desc[] = {
+ {
+ { 0x00000000000c1084ULL, 0x46000000000000c0ULL },
+ "MSI Installer",
+ },
+ { { 0, 0 },
+ NULL,
+ },
+};
+
+file_private const char *
+cdf_clsid_to_mime(const uint64_t clsid[2], const struct cv *cv)
+{
+ size_t i;
+ for (i = 0; cv[i].mime != NULL; i++) {
+ if (clsid[0] == cv[i].clsid[0] && clsid[1] == cv[i].clsid[1])
+ return cv[i].mime;
+ }
+#ifdef CDF_DEBUG
+ fprintf(stderr, "unknown mime %" PRIx64 ", %" PRIx64 "\n", clsid[0],
+ clsid[1]);
+#endif
+ return NULL;
+}
+
+file_private const char *
+cdf_app_to_mime(const char *vbuf, const struct nv *nv)
+{
+ size_t i;
+ const char *rv = NULL;
+#ifdef USE_C_LOCALE
+ locale_t old_lc_ctype, c_lc_ctype;
+
+ c_lc_ctype = newlocale(LC_CTYPE_MASK, "C", 0);
+ assert(c_lc_ctype != NULL);
+ old_lc_ctype = uselocale(c_lc_ctype);
+ assert(old_lc_ctype != NULL);
+#else
+ char *old_lc_ctype = setlocale(LC_CTYPE, NULL);
+ assert(old_lc_ctype != NULL);
+ old_lc_ctype = strdup(old_lc_ctype);
+ assert(old_lc_ctype != NULL);
+ (void)setlocale(LC_CTYPE, "C");
+#endif
+ for (i = 0; nv[i].pattern != NULL; i++)
+ if (strcasestr(vbuf, nv[i].pattern) != NULL) {
+ rv = nv[i].mime;
+ break;
+ }
+#ifdef CDF_DEBUG
+ fprintf(stderr, "unknown app %s\n", vbuf);
+#endif
+#ifdef USE_C_LOCALE
+ (void)uselocale(old_lc_ctype);
+ freelocale(c_lc_ctype);
+#else
+ (void)setlocale(LC_CTYPE, old_lc_ctype);
+ free(old_lc_ctype);
+#endif
+ return rv;
+}
+
+file_private int
+cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info,
+ size_t count, const cdf_directory_t *root_storage)
+{
+ size_t i;
+ cdf_timestamp_t tp;
+ struct timespec ts;
+ char buf[64];
+ const char *str = NULL;
+ const char *s, *e;
+ int len;
+
+ if (!NOTMIME(ms) && root_storage)
+ str = cdf_clsid_to_mime(root_storage->d_storage_uuid,
+ clsid2mime);
+
+ for (i = 0; i < count; i++) {
+ cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
+ switch (info[i].pi_type) {
+ case CDF_NULL:
+ break;
+ case CDF_SIGNED16:
+ if (NOTMIME(ms) && file_printf(ms, ", %s: %hd", buf,
+ info[i].pi_s16) == -1)
+ return -1;
+ break;
+ case CDF_SIGNED32:
+ if (NOTMIME(ms) && file_printf(ms, ", %s: %d", buf,
+ info[i].pi_s32) == -1)
+ return -1;
+ break;
+ case CDF_UNSIGNED32:
+ if (NOTMIME(ms) && file_printf(ms, ", %s: %u", buf,
+ info[i].pi_u32) == -1)
+ return -1;
+ break;
+ case CDF_FLOAT:
+ if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf,
+ info[i].pi_f) == -1)
+ return -1;
+ break;
+ case CDF_DOUBLE:
+ if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf,
+ info[i].pi_d) == -1)
+ return -1;
+ break;
+ case CDF_LENGTH32_STRING:
+ case CDF_LENGTH32_WSTRING:
+ len = info[i].pi_str.s_len;
+ if (len > 1) {
+ char vbuf[1024];
+ size_t j, k = 1;
+
+ if (info[i].pi_type == CDF_LENGTH32_WSTRING)
+ k++;
+ s = info[i].pi_str.s_buf;
+ e = info[i].pi_str.s_buf + len;
+ for (j = 0; s < e && j < sizeof(vbuf)
+ && len--; s += k) {
+ if (*s == '\0')
+ break;
+ if (isprint(CAST(unsigned char, *s)))
+ vbuf[j++] = *s;
+ }
+ if (j == sizeof(vbuf))
+ --j;
+ vbuf[j] = '\0';
+ if (NOTMIME(ms)) {
+ if (vbuf[0]) {
+ if (file_printf(ms, ", %s: %s",
+ buf, vbuf) == -1)
+ return -1;
+ }
+ } else if (str == NULL && info[i].pi_id ==
+ CDF_PROPERTY_NAME_OF_APPLICATION) {
+ str = cdf_app_to_mime(vbuf, app2mime);
+ }
+ }
+ break;
+ case CDF_FILETIME:
+ tp = info[i].pi_tp;
+ if (tp != 0) {
+ char tbuf[64];
+ if (tp < 1000000000000000LL) {
+ cdf_print_elapsed_time(tbuf,
+ sizeof(tbuf), tp);
+ if (NOTMIME(ms) && file_printf(ms,
+ ", %s: %s", buf, tbuf) == -1)
+ return -1;
+ } else {
+ char *c, *ec;
+ cdf_timestamp_to_timespec(&ts, tp);
+ c = cdf_ctime(&ts.tv_sec, tbuf);
+ if (c != NULL &&
+ (ec = strchr(c, '\n')) != NULL)
+ *ec = '\0';
+
+ if (NOTMIME(ms) && file_printf(ms,
+ ", %s: %s", buf, c) == -1)
+ return -1;
+ }
+ }
+ break;
+ case CDF_CLIPBOARD:
+ break;
+ default:
+ return -1;
+ }
+ }
+ if (ms->flags & MAGIC_MIME_TYPE) {
+ if (str == NULL)
+ return 0;
+ if (file_printf(ms, "application/%s", str) == -1)
+ return -1;
+ }
+ return 1;
+}
+
+file_private int
+cdf_file_catalog(struct magic_set *ms, const cdf_header_t *h,
+ const cdf_stream_t *sst)
+{
+ cdf_catalog_t *cat;
+ size_t i;
+ char buf[256];
+ cdf_catalog_entry_t *ce;
+
+ if (NOTMIME(ms)) {
+ if (file_printf(ms, "Microsoft Thumbs.db [") == -1)
+ return -1;
+ if (cdf_unpack_catalog(h, sst, &cat) == -1)
+ return -1;
+ ce = cat->cat_e;
+ /* skip first entry since it has a , or paren */
+ for (i = 1; i < cat->cat_num; i++)
+ if (file_printf(ms, "%s%s",
+ cdf_u16tos8(buf, ce[i].ce_namlen, ce[i].ce_name),
+ i == cat->cat_num - 1 ? "]" : ", ") == -1) {
+ free(cat);
+ return -1;
+ }
+ free(cat);
+ } else if (ms->flags & MAGIC_MIME_TYPE) {
+ if (file_printf(ms, "application/CDFV2") == -1)
+ return -1;
+ }
+ return 1;
+}
+
+file_private int
+cdf_file_summary_info(struct magic_set *ms, const cdf_header_t *h,
+ const cdf_stream_t *sst, const cdf_directory_t *root_storage)
+{
+ cdf_summary_info_header_t si;
+ cdf_property_info_t *info;
+ size_t count;
+ int m;
+
+ if (cdf_unpack_summary_info(sst, h, &si, &info, &count) == -1)
+ return -1;
+
+ if (NOTMIME(ms)) {
+ const char *str;
+
+ if (file_printf(ms, "Composite Document File V2 Document")
+ == -1)
+ return -1;
+
+ if (file_printf(ms, ", %s Endian",
+ si.si_byte_order == 0xfffe ? "Little" : "Big") == -1)
+ return -2;
+ switch (si.si_os) {
+ case 2:
+ if (file_printf(ms, ", Os: Windows, Version %d.%d",
+ si.si_os_version & 0xff,
+ CAST(uint32_t, si.si_os_version) >> 8) == -1)
+ return -2;
+ break;
+ case 1:
+ if (file_printf(ms, ", Os: MacOS, Version %d.%d",
+ CAST(uint32_t, si.si_os_version) >> 8,
+ si.si_os_version & 0xff) == -1)
+ return -2;
+ break;
+ default:
+ if (file_printf(ms, ", Os %d, Version: %d.%d", si.si_os,
+ si.si_os_version & 0xff,
+ CAST(uint32_t, si.si_os_version) >> 8) == -1)
+ return -2;
+ break;
+ }
+ if (root_storage) {
+ str = cdf_clsid_to_mime(root_storage->d_storage_uuid,
+ clsid2desc);
+ if (str) {
+ if (file_printf(ms, ", %s", str) == -1)
+ return -2;
+ }
+ }
+ }
+
+ m = cdf_file_property_info(ms, info, count, root_storage);
+ free(info);
+
+ return m == -1 ? -2 : m;
+}
+
+#ifdef notdef
+file_private char *
+format_clsid(char *buf, size_t len, const uint64_t uuid[2]) {
+ snprintf(buf, len, "%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4"
+ PRIx64 "-%.12" PRIx64,
+ (uuid[0] >> 32) & (uint64_t)0x000000000ffffffffULL,
+ (uuid[0] >> 16) & (uint64_t)0x0000000000000ffffULL,
+ (uuid[0] >> 0) & (uint64_t)0x0000000000000ffffULL,
+ (uuid[1] >> 48) & (uint64_t)0x0000000000000ffffULL,
+ (uuid[1] >> 0) & (uint64_t)0x0000fffffffffffffULL);
+ return buf;
+}
+#endif
+
+file_private int
+cdf_file_catalog_info(struct magic_set *ms, const cdf_info_t *info,
+ const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat,
+ const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn)
+{
+ int i;
+
+ if ((i = cdf_read_user_stream(info, h, sat, ssat, sst,
+ dir, "Catalog", scn)) == -1)
+ return i;
+#ifdef CDF_DEBUG
+ cdf_dump_catalog(h, scn);
+#endif
+ if ((i = cdf_file_catalog(ms, h, scn)) == -1)
+ return -1;
+ return i;
+}
+
+file_private int
+cdf_check_summary_info(struct magic_set *ms, const cdf_info_t *info,
+ const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat,
+ const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn,
+ const cdf_directory_t *root_storage, const char **expn)
+{
+ int i;
+ const char *str = NULL;
+ cdf_directory_t *d;
+ char name[__arraycount(d->d_name)];
+ size_t j, k;
+
+#ifdef CDF_DEBUG
+ cdf_dump_summary_info(h, scn);
+#endif
+ if ((i = cdf_file_summary_info(ms, h, scn, root_storage)) < 0) {
+ *expn = "Can't expand summary_info";
+ return i;
+ }
+ if (i == 1)
+ return i;
+ for (j = 0; str == NULL && j < dir->dir_len; j++) {
+ d = &dir->dir_tab[j];
+ for (k = 0; k < sizeof(name); k++)
+ name[k] = CAST(char, cdf_tole2(d->d_name[k]));
+ str = cdf_app_to_mime(name,
+ NOTMIME(ms) ? name2desc : name2mime);
+ }
+ if (NOTMIME(ms)) {
+ if (str != NULL) {
+ if (file_printf(ms, "%s", str) == -1)
+ return -1;
+ i = 1;
+ }
+ } else if (ms->flags & MAGIC_MIME_TYPE) {
+ if (str == NULL)
+ str = "vnd.ms-office";
+ if (file_printf(ms, "application/%s", str) == -1)
+ return -1;
+ i = 1;
+ }
+ if (i <= 0) {
+ i = cdf_file_catalog_info(ms, info, h, sat, ssat, sst,
+ dir, scn);
+ }
+ return i;
+}
+
+file_private struct sinfo {
+ const char *name;
+ const char *mime;
+ const char *sections[5];
+ const int types[5];
+} sectioninfo[] = {
+ { "Encrypted", "encrypted",
+ {
+ "EncryptedPackage", "EncryptedSummary",
+ NULL, NULL, NULL,
+ },
+ {
+ CDF_DIR_TYPE_USER_STREAM,
+ CDF_DIR_TYPE_USER_STREAM,
+ 0, 0, 0,
+
+ },
+ },
+ { "QuickBooks", "quickbooks",
+ {
+#if 0
+ "TaxForms", "PDFTaxForms", "modulesInBackup",
+#endif
+ "mfbu_header", NULL, NULL, NULL, NULL,
+ },
+ {
+#if 0
+ CDF_DIR_TYPE_USER_STORAGE,
+ CDF_DIR_TYPE_USER_STORAGE,
+ CDF_DIR_TYPE_USER_STREAM,
+#endif
+ CDF_DIR_TYPE_USER_STREAM,
+ 0, 0, 0, 0
+ },
+ },
+ { "Microsoft Excel", "vnd.ms-excel",
+ {
+ "Book", "Workbook", NULL, NULL, NULL,
+ },
+ {
+ CDF_DIR_TYPE_USER_STREAM,
+ CDF_DIR_TYPE_USER_STREAM,
+ 0, 0, 0,
+ },
+ },
+ { "Microsoft Word", "msword",
+ {
+ "WordDocument", NULL, NULL, NULL, NULL,
+ },
+ {
+ CDF_DIR_TYPE_USER_STREAM,
+ 0, 0, 0, 0,
+ },
+ },
+ { "Microsoft PowerPoint", "vnd.ms-powerpoint",
+ {
+ "PowerPoint", NULL, NULL, NULL, NULL,
+ },
+ {
+ CDF_DIR_TYPE_USER_STREAM,
+ 0, 0, 0, 0,
+ },
+ },
+ { "Microsoft Outlook Message", "vnd.ms-outlook",
+ {
+ "__properties_version1.0",
+ "__recip_version1.0_#00000000",
+ NULL, NULL, NULL,
+ },
+ {
+ CDF_DIR_TYPE_USER_STREAM,
+ CDF_DIR_TYPE_USER_STORAGE,
+ 0, 0, 0,
+ },
+ },
+};
+
+file_private int
+cdf_file_dir_info(struct magic_set *ms, const cdf_dir_t *dir)
+{
+ size_t sd, j;
+
+ for (sd = 0; sd < __arraycount(sectioninfo); sd++) {
+ const struct sinfo *si = &sectioninfo[sd];
+ for (j = 0; si->sections[j]; j++) {
+ if (cdf_find_stream(dir, si->sections[j], si->types[j])
+ > 0)
+ break;
+#ifdef CDF_DEBUG
+ fprintf(stderr, "Can't read %s\n", si->sections[j]);
+#endif
+ }
+ if (si->sections[j] == NULL)
+ continue;
+ if (NOTMIME(ms)) {
+ if (file_printf(ms, "CDFV2 %s", si->name) == -1)
+ return -1;
+ } else if (ms->flags & MAGIC_MIME_TYPE) {
+ if (file_printf(ms, "application/%s", si->mime) == -1)
+ return -1;
+ }
+ return 1;
+ }
+ return -1;
+}
+
+file_protected int
+file_trycdf(struct magic_set *ms, const struct buffer *b)
+{
+ int fd = b->fd;
+ const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
+ size_t nbytes = b->flen;
+ cdf_info_t info;
+ cdf_header_t h;
+ cdf_sat_t sat, ssat;
+ cdf_stream_t sst, scn;
+ cdf_dir_t dir;
+ int i;
+ const char *expn = "";
+ const cdf_directory_t *root_storage;
+
+ scn.sst_tab = NULL;
+ info.i_fd = fd;
+ info.i_buf = buf;
+ info.i_len = nbytes;
+ if (ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))
+ return 0;
+ if (cdf_read_header(&info, &h) == -1)
+ return 0;
+#ifdef CDF_DEBUG
+ cdf_dump_header(&h);
+#endif
+
+ if ((i = cdf_read_sat(&info, &h, &sat)) == -1) {
+ expn = "Can't read SAT";
+ goto out0;
+ }
+#ifdef CDF_DEBUG
+ cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
+#endif
+
+ if ((i = cdf_read_ssat(&info, &h, &sat, &ssat)) == -1) {
+ expn = "Can't read SSAT";
+ goto out1;
+ }
+#ifdef CDF_DEBUG
+ cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
+#endif
+
+ if ((i = cdf_read_dir(&info, &h, &sat, &dir)) == -1) {
+ expn = "Can't read directory";
+ goto out2;
+ }
+
+ if ((i = cdf_read_short_stream(&info, &h, &sat, &dir, &sst,
+ &root_storage)) == -1) {
+ expn = "Cannot read short stream";
+ goto out3;
+ }
+#ifdef CDF_DEBUG
+ cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
+#endif
+#ifdef notdef
+ if (root_storage) {
+ if (NOTMIME(ms)) {
+ char clsbuf[128];
+ if (file_printf(ms, "CLSID %s, ",
+ format_clsid(clsbuf, sizeof(clsbuf),
+ root_storage->d_storage_uuid)) == -1)
+ return -1;
+ }
+ }
+#endif
+
+ if (cdf_read_user_stream(&info, &h, &sat, &ssat, &sst, &dir,
+ "FileHeader", &scn) != -1) {
+#define HWP5_SIGNATURE "HWP Document File"
+ if (scn.sst_len * scn.sst_ss >= sizeof(HWP5_SIGNATURE) - 1
+ && memcmp(scn.sst_tab, HWP5_SIGNATURE,
+ sizeof(HWP5_SIGNATURE) - 1) == 0) {
+ if (NOTMIME(ms)) {
+ if (file_printf(ms,
+ "Hancom HWP (Hangul Word Processor) file, version 5.0") == -1)
+ return -1;
+ } else if (ms->flags & MAGIC_MIME_TYPE) {
+ if (file_printf(ms, "application/x-hwp") == -1)
+ return -1;
+ }
+ i = 1;
+ goto out5;
+ } else {
+ cdf_zero_stream(&scn);
+ }
+ }
+
+ if ((i = cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
+ &scn)) == -1) {
+ if (errno != ESRCH) {
+ expn = "Cannot read summary info";
+ }
+ } else {
+ i = cdf_check_summary_info(ms, &info, &h,
+ &sat, &ssat, &sst, &dir, &scn, root_storage, &expn);
+ cdf_zero_stream(&scn);
+ }
+ if (i <= 0) {
+ if ((i = cdf_read_doc_summary_info(&info, &h, &sat, &ssat,
+ &sst, &dir, &scn)) == -1) {
+ if (errno != ESRCH) {
+ expn = "Cannot read summary info";
+ }
+ } else {
+ i = cdf_check_summary_info(ms, &info, &h, &sat, &ssat,
+ &sst, &dir, &scn, root_storage, &expn);
+ }
+ }
+ if (i <= 0) {
+ i = cdf_file_dir_info(ms, &dir);
+ if (i < 0)
+ expn = "Cannot read section info";
+ }
+out5:
+ cdf_zero_stream(&scn);
+ cdf_zero_stream(&sst);
+out3:
+ free(dir.dir_tab);
+out2:
+ free(ssat.sat_tab);
+out1:
+ free(sat.sat_tab);
+out0:
+ /* If we handled it already, return */
+ if (i != -1)
+ return i;
+ /* Provide a default handler */
+ if (NOTMIME(ms)) {
+ if (file_printf(ms,
+ "Composite Document File V2 Document") == -1)
+ return -1;
+ if (*expn)
+ if (file_printf(ms, ", %s", expn) == -1)
+ return -1;
+ } else if (ms->flags & MAGIC_MIME_TYPE) {
+ /* https://reposcope.com/mimetype/application/x-ole-storage */
+ if (file_printf(ms, "application/x-ole-storage") == -1)
+ return -1;
+ }
+ return 1;
+}
diff --git a/contrib/libs/libmagic/src/readelf.c b/contrib/libs/libmagic/src/readelf.c
new file mode 100644
index 0000000000..a2a66ddd72
--- /dev/null
+++ b/contrib/libs/libmagic/src/readelf.c
@@ -0,0 +1,1899 @@
+/*
+ * Copyright (c) Christos Zoulas 2003.
+ * All Rights Reserved.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: readelf.c,v 1.190 2023/07/27 19:39:06 christos Exp $")
+#endif
+
+#ifdef BUILTIN_ELF
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "readelf.h"
+#include "magic.h"
+
+#ifdef ELFCORE
+file_private int dophn_core(struct magic_set *, int, int, int, off_t, int,
+ size_t, off_t, int *, uint16_t *);
+#endif
+file_private int dophn_exec(struct magic_set *, int, int, int, off_t, int,
+ size_t, off_t, int, int *, uint16_t *);
+file_private int doshn(struct magic_set *, int, int, int, off_t, int, size_t,
+ off_t, int, int, int *, uint16_t *);
+file_private size_t donote(struct magic_set *, void *, size_t, size_t, int,
+ int, size_t, int *, uint16_t *, int, off_t, int, off_t);
+
+#define ELF_ALIGN(a) ((((a) + align - 1) / align) * align)
+
+#define isquote(c) (strchr("'\"`", (c)) != NULL)
+
+file_private uint16_t getu16(int, uint16_t);
+file_private uint32_t getu32(int, uint32_t);
+file_private uint64_t getu64(int, uint64_t);
+
+#define SIZE_UNKNOWN CAST(off_t, -1)
+
+file_private int
+toomany(struct magic_set *ms, const char *name, uint16_t num)
+{
+ if (ms->flags & MAGIC_MIME)
+ return 1;
+ if (file_printf(ms, ", too many %s (%u)", name, num) == -1)
+ return -1;
+ return 1;
+}
+
+file_private uint16_t
+getu16(int swap, uint16_t value)
+{
+ union {
+ uint16_t ui;
+ char c[2];
+ } retval, tmpval;
+
+ if (swap) {
+ tmpval.ui = value;
+
+ retval.c[0] = tmpval.c[1];
+ retval.c[1] = tmpval.c[0];
+
+ return retval.ui;
+ } else
+ return value;
+}
+
+file_private uint32_t
+getu32(int swap, uint32_t value)
+{
+ union {
+ uint32_t ui;
+ char c[4];
+ } retval, tmpval;
+
+ if (swap) {
+ tmpval.ui = value;
+
+ retval.c[0] = tmpval.c[3];
+ retval.c[1] = tmpval.c[2];
+ retval.c[2] = tmpval.c[1];
+ retval.c[3] = tmpval.c[0];
+
+ return retval.ui;
+ } else
+ return value;
+}
+
+file_private uint64_t
+getu64(int swap, uint64_t value)
+{
+ union {
+ uint64_t ui;
+ char c[8];
+ } retval, tmpval;
+
+ if (swap) {
+ tmpval.ui = value;
+
+ retval.c[0] = tmpval.c[7];
+ retval.c[1] = tmpval.c[6];
+ retval.c[2] = tmpval.c[5];
+ retval.c[3] = tmpval.c[4];
+ retval.c[4] = tmpval.c[3];
+ retval.c[5] = tmpval.c[2];
+ retval.c[6] = tmpval.c[1];
+ retval.c[7] = tmpval.c[0];
+
+ return retval.ui;
+ } else
+ return value;
+}
+
+#define elf_getu16(swap, value) getu16(swap, value)
+#define elf_getu32(swap, value) getu32(swap, value)
+#define elf_getu64(swap, value) getu64(swap, value)
+
+#define xsh_addr (clazz == ELFCLASS32 \
+ ? CAST(void *, &sh32) \
+ : CAST(void *, &sh64))
+#define xsh_sizeof (clazz == ELFCLASS32 \
+ ? sizeof(sh32) \
+ : sizeof(sh64))
+#define xsh_size CAST(size_t, (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, sh32.sh_size) \
+ : elf_getu64(swap, sh64.sh_size)))
+#define xsh_offset CAST(off_t, (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, sh32.sh_offset) \
+ : elf_getu64(swap, sh64.sh_offset)))
+#define xsh_type (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, sh32.sh_type) \
+ : elf_getu32(swap, sh64.sh_type))
+#define xsh_name (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, sh32.sh_name) \
+ : elf_getu32(swap, sh64.sh_name))
+
+#define xph_addr (clazz == ELFCLASS32 \
+ ? CAST(void *, &ph32) \
+ : CAST(void *, &ph64))
+#define xph_sizeof (clazz == ELFCLASS32 \
+ ? sizeof(ph32) \
+ : sizeof(ph64))
+#define xph_type (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, ph32.p_type) \
+ : elf_getu32(swap, ph64.p_type))
+#define xph_offset CAST(off_t, (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, ph32.p_offset) \
+ : elf_getu64(swap, ph64.p_offset)))
+#define xph_align CAST(size_t, (clazz == ELFCLASS32 \
+ ? CAST(off_t, (ph32.p_align ? \
+ elf_getu32(swap, ph32.p_align) : 4))\
+ : CAST(off_t, (ph64.p_align ? \
+ elf_getu64(swap, ph64.p_align) : 4))))
+#define xph_vaddr CAST(size_t, (clazz == ELFCLASS32 \
+ ? CAST(off_t, (ph32.p_vaddr ? \
+ elf_getu32(swap, ph32.p_vaddr) : 4))\
+ : CAST(off_t, (ph64.p_vaddr ? \
+ elf_getu64(swap, ph64.p_vaddr) : 4))))
+#define xph_filesz CAST(size_t, (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, ph32.p_filesz) \
+ : elf_getu64(swap, ph64.p_filesz)))
+#define xph_memsz CAST(size_t, ((clazz == ELFCLASS32 \
+ ? elf_getu32(swap, ph32.p_memsz) \
+ : elf_getu64(swap, ph64.p_memsz))))
+#define xnh_addr (clazz == ELFCLASS32 \
+ ? CAST(void *, &nh32) \
+ : CAST(void *, &nh64))
+#define xnh_sizeof (clazz == ELFCLASS32 \
+ ? sizeof(nh32) \
+ : sizeof(nh64))
+#define xnh_type (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, nh32.n_type) \
+ : elf_getu32(swap, nh64.n_type))
+#define xnh_namesz (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, nh32.n_namesz) \
+ : elf_getu32(swap, nh64.n_namesz))
+#define xnh_descsz (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, nh32.n_descsz) \
+ : elf_getu32(swap, nh64.n_descsz))
+
+#define xdh_addr (clazz == ELFCLASS32 \
+ ? CAST(void *, &dh32) \
+ : CAST(void *, &dh64))
+#define xdh_sizeof (clazz == ELFCLASS32 \
+ ? sizeof(dh32) \
+ : sizeof(dh64))
+#define xdh_tag (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, dh32.d_tag) \
+ : elf_getu64(swap, dh64.d_tag))
+#define xdh_val (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, dh32.d_un.d_val) \
+ : elf_getu64(swap, dh64.d_un.d_val))
+
+#define xcap_addr (clazz == ELFCLASS32 \
+ ? CAST(void *, &cap32) \
+ : CAST(void *, &cap64))
+#define xcap_sizeof (clazz == ELFCLASS32 \
+ ? sizeof(cap32) \
+ : sizeof(cap64))
+#define xcap_tag (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, cap32.c_tag) \
+ : elf_getu64(swap, cap64.c_tag))
+#define xcap_val (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, cap32.c_un.c_val) \
+ : elf_getu64(swap, cap64.c_un.c_val))
+
+#define xauxv_addr (clazz == ELFCLASS32 \
+ ? CAST(void *, &auxv32) \
+ : CAST(void *, &auxv64))
+#define xauxv_sizeof (clazz == ELFCLASS32 \
+ ? sizeof(auxv32) \
+ : sizeof(auxv64))
+#define xauxv_type (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, auxv32.a_type) \
+ : elf_getu64(swap, auxv64.a_type))
+#define xauxv_val (clazz == ELFCLASS32 \
+ ? elf_getu32(swap, auxv32.a_v) \
+ : elf_getu64(swap, auxv64.a_v))
+
+#define prpsoffsets(i) (clazz == ELFCLASS32 \
+ ? prpsoffsets32[i] \
+ : prpsoffsets64[i])
+
+#ifdef ELFCORE
+/*
+ * Try larger offsets first to avoid false matches
+ * from earlier data that happen to look like strings.
+ */
+static const size_t prpsoffsets32[] = {
+#ifdef USE_NT_PSINFO
+ 104, /* SunOS 5.x (command line) */
+ 88, /* SunOS 5.x (short name) */
+#endif /* USE_NT_PSINFO */
+
+ 100, /* SunOS 5.x (command line) */
+ 84, /* SunOS 5.x (short name) */
+
+ 44, /* Linux (command line) */
+ 28, /* Linux (short name) */
+
+ 48, /* Linux PowerPC (command line) */
+ 32, /* Linux PowerPC (short name) */
+
+ 8, /* FreeBSD */
+};
+
+static const size_t prpsoffsets64[] = {
+#ifdef USE_NT_PSINFO
+ 152, /* SunOS 5.x (command line) */
+ 136, /* SunOS 5.x (short name) */
+#endif /* USE_NT_PSINFO */
+
+ 136, /* SunOS 5.x, 64-bit (command line) */
+ 120, /* SunOS 5.x, 64-bit (short name) */
+
+ 56, /* Linux (command line) */
+ 40, /* Linux (tested on core from 2.4.x, short name) */
+
+ 16, /* FreeBSD, 64-bit */
+};
+
+#define NOFFSETS32 __arraycount(prpsoffsets32)
+#define NOFFSETS64 __arraycount(prpsoffsets64)
+
+#define NOFFSETS (clazz == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
+
+/*
+ * Look through the program headers of an executable image, searching
+ * for a PT_NOTE section of type NT_PRPSINFO, with a name "CORE" or
+ * "FreeBSD"; if one is found, try looking in various places in its
+ * contents for a 16-character string containing only printable
+ * characters - if found, that string should be the name of the program
+ * that dropped core. Note: right after that 16-character string is,
+ * at least in SunOS 5.x (and possibly other SVR4-flavored systems) and
+ * Linux, a longer string (80 characters, in 5.x, probably other
+ * SVR4-flavored systems, and Linux) containing the start of the
+ * command line for that program.
+ *
+ * SunOS 5.x core files contain two PT_NOTE sections, with the types
+ * NT_PRPSINFO (old) and NT_PSINFO (new). These structs contain the
+ * same info about the command name and command line, so it probably
+ * isn't worthwhile to look for NT_PSINFO, but the offsets are provided
+ * above (see USE_NT_PSINFO), in case we ever decide to do so. The
+ * NT_PRPSINFO and NT_PSINFO sections are always in order and adjacent;
+ * the SunOS 5.x file command relies on this (and prefers the latter).
+ *
+ * The signal number probably appears in a section of type NT_PRSTATUS,
+ * but that's also rather OS-dependent, in ways that are harder to
+ * dissect with heuristics, so I'm not bothering with the signal number.
+ * (I suppose the signal number could be of interest in situations where
+ * you don't have the binary of the program that dropped core; if you
+ * *do* have that binary, the debugger will probably tell you what
+ * signal it was.)
+ */
+
+#define OS_STYLE_SVR4 0
+#define OS_STYLE_FREEBSD 1
+#define OS_STYLE_NETBSD 2
+
+file_private const char os_style_names[][8] = {
+ "SVR4",
+ "FreeBSD",
+ "NetBSD",
+};
+
+#define FLAGS_CORE_STYLE 0x0003
+
+#define FLAGS_DID_CORE 0x0004
+#define FLAGS_DID_OS_NOTE 0x0008
+#define FLAGS_DID_BUILD_ID 0x0010
+#define FLAGS_DID_CORE_STYLE 0x0020
+#define FLAGS_DID_NETBSD_PAX 0x0040
+#define FLAGS_DID_NETBSD_MARCH 0x0080
+#define FLAGS_DID_NETBSD_CMODEL 0x0100
+#define FLAGS_DID_NETBSD_EMULATION 0x0200
+#define FLAGS_DID_NETBSD_UNKNOWN 0x0400
+#define FLAGS_IS_CORE 0x0800
+#define FLAGS_DID_AUXV 0x1000
+
+file_private int
+dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
+ int num, size_t size, off_t fsize, int *flags, uint16_t *notecount)
+{
+ Elf32_Phdr ph32;
+ Elf64_Phdr ph64;
+ size_t offset, len;
+ unsigned char nbuf[BUFSIZ];
+ ssize_t bufsize;
+ off_t ph_off = off, offs;
+ int ph_num = num;
+
+ if (ms->flags & MAGIC_MIME)
+ return 0;
+
+ if (num == 0) {
+ if (file_printf(ms, ", no program header") == -1)
+ return -1;
+ return 0;
+ }
+ if (size != xph_sizeof) {
+ if (file_printf(ms, ", corrupted program header size") == -1)
+ return -1;
+ return 0;
+ }
+
+ /*
+ * Loop through all the program headers.
+ */
+ for ( ; num; num--) {
+ if (pread(fd, xph_addr, xph_sizeof, off) <
+ CAST(ssize_t, xph_sizeof)) {
+ if (file_printf(ms,
+ ", can't read elf program headers at %jd",
+ (intmax_t)off) == -1)
+ return -1;
+ return 0;
+ }
+ off += size;
+
+ if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
+ /* Perhaps warn here */
+ continue;
+ }
+
+ if (xph_type != PT_NOTE)
+ continue;
+
+ /*
+ * This is a PT_NOTE section; loop through all the notes
+ * in the section.
+ */
+ len = xph_filesz < sizeof(nbuf) ? xph_filesz : sizeof(nbuf);
+ offs = xph_offset;
+ if ((bufsize = pread(fd, nbuf, len, offs)) == -1) {
+ if (file_printf(ms, " can't read note section at %jd",
+ (intmax_t)offs) == -1)
+ return -1;
+ return 0;
+ }
+ offset = 0;
+ for (;;) {
+ if (offset >= CAST(size_t, bufsize))
+ break;
+ offset = donote(ms, nbuf, offset, CAST(size_t, bufsize),
+ clazz, swap, 4, flags, notecount, fd, ph_off,
+ ph_num, fsize);
+ if (offset == 0)
+ break;
+
+ }
+ }
+ return 0;
+}
+#endif
+
+static int
+do_note_netbsd_version(struct magic_set *ms, int swap, void *v)
+{
+ uint32_t desc;
+ memcpy(&desc, v, sizeof(desc));
+ desc = elf_getu32(swap, desc);
+
+ if (file_printf(ms, ", for NetBSD") == -1)
+ return -1;
+ /*
+ * The version number used to be stuck as 199905, and was thus
+ * basically content-free. Newer versions of NetBSD have fixed
+ * this and now use the encoding of __NetBSD_Version__:
+ *
+ * MMmmrrpp00
+ *
+ * M = major version
+ * m = minor version
+ * r = release ["",A-Z,Z[A-Z] but numeric]
+ * p = patchlevel
+ */
+ if (desc > 100000000U) {
+ uint32_t ver_patch = (desc / 100) % 100;
+ uint32_t ver_rel = (desc / 10000) % 100;
+ uint32_t ver_min = (desc / 1000000) % 100;
+ uint32_t ver_maj = desc / 100000000;
+
+ if (file_printf(ms, " %u.%u", ver_maj, ver_min) == -1)
+ return -1;
+ if (ver_maj >= 9) {
+ ver_patch += 100 * ver_rel;
+ ver_rel = 0;
+ }
+ if (ver_rel == 0 && ver_patch != 0) {
+ if (file_printf(ms, ".%u", ver_patch) == -1)
+ return -1;
+ } else if (ver_rel != 0) {
+ while (ver_rel > 26) {
+ if (file_printf(ms, "Z") == -1)
+ return -1;
+ ver_rel -= 26;
+ }
+ if (file_printf(ms, "%c", 'A' + ver_rel - 1) == -1)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+do_note_freebsd_version(struct magic_set *ms, int swap, void *v)
+{
+ uint32_t desc;
+
+ memcpy(&desc, v, sizeof(desc));
+ desc = elf_getu32(swap, desc);
+ if (file_printf(ms, ", for FreeBSD") == -1)
+ return -1;
+
+ /*
+ * Contents is __FreeBSD_version, whose relation to OS
+ * versions is defined by a huge table in the Porter's
+ * Handbook. This is the general scheme:
+ *
+ * Releases:
+ * Mmp000 (before 4.10)
+ * Mmi0p0 (before 5.0)
+ * Mmm0p0
+ *
+ * Development branches:
+ * Mmpxxx (before 4.6)
+ * Mmp1xx (before 4.10)
+ * Mmi1xx (before 5.0)
+ * M000xx (pre-M.0)
+ * Mmm1xx
+ *
+ * M = major version
+ * m = minor version
+ * i = minor version increment (491000 -> 4.10)
+ * p = patchlevel
+ * x = revision
+ *
+ * The first release of FreeBSD to use ELF by default
+ * was version 3.0.
+ */
+ if (desc == 460002) {
+ if (file_printf(ms, " 4.6.2") == -1)
+ return -1;
+ } else if (desc < 460100) {
+ if (file_printf(ms, " %d.%d", desc / 100000,
+ desc / 10000 % 10) == -1)
+ return -1;
+ if (desc / 1000 % 10 > 0)
+ if (file_printf(ms, ".%d", desc / 1000 % 10) == -1)
+ return -1;
+ if ((desc % 1000 > 0) || (desc % 100000 == 0))
+ if (file_printf(ms, " (%d)", desc) == -1)
+ return -1;
+ } else if (desc < 500000) {
+ if (file_printf(ms, " %d.%d", desc / 100000,
+ desc / 10000 % 10 + desc / 1000 % 10) == -1)
+ return -1;
+ if (desc / 100 % 10 > 0) {
+ if (file_printf(ms, " (%d)", desc) == -1)
+ return -1;
+ } else if (desc / 10 % 10 > 0) {
+ if (file_printf(ms, ".%d", desc / 10 % 10) == -1)
+ return -1;
+ }
+ } else {
+ if (file_printf(ms, " %d.%d", desc / 100000,
+ desc / 1000 % 100) == -1)
+ return -1;
+ if ((desc / 100 % 10 > 0) ||
+ (desc % 100000 / 100 == 0)) {
+ if (file_printf(ms, " (%d)", desc) == -1)
+ return -1;
+ } else if (desc / 10 % 10 > 0) {
+ if (file_printf(ms, ".%d", desc / 10 % 10) == -1)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+file_private int
+/*ARGSUSED*/
+do_bid_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
+ int swap __attribute__((__unused__)), uint32_t namesz, uint32_t descsz,
+ size_t noff, size_t doff, int *flags)
+{
+ if (namesz == 4 && strcmp(RCAST(char *, &nbuf[noff]), "GNU") == 0 &&
+ type == NT_GNU_BUILD_ID && (descsz >= 4 && descsz <= 20)) {
+ uint8_t desc[20];
+ const char *btype;
+ uint32_t i;
+ *flags |= FLAGS_DID_BUILD_ID;
+ switch (descsz) {
+ case 8:
+ btype = "xxHash";
+ break;
+ case 16:
+ btype = "md5/uuid";
+ break;
+ case 20:
+ btype = "sha1";
+ break;
+ default:
+ btype = "unknown";
+ break;
+ }
+ if (file_printf(ms, ", BuildID[%s]=", btype) == -1)
+ return -1;
+ memcpy(desc, &nbuf[doff], descsz);
+ for (i = 0; i < descsz; i++)
+ if (file_printf(ms, "%02x", desc[i]) == -1)
+ return -1;
+ return 1;
+ }
+ if (namesz == 4 && strcmp(RCAST(char *, &nbuf[noff]), "Go") == 0 &&
+ type == NT_GO_BUILD_ID && descsz < 128) {
+ char buf[256];
+ if (file_printf(ms, ", Go BuildID=%s",
+ file_copystr(buf, sizeof(buf), descsz,
+ RCAST(const char *, &nbuf[doff]))) == -1)
+ return -1;
+ return 1;
+ }
+ return 0;
+}
+
+file_private int
+do_os_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
+ int swap, uint32_t namesz, uint32_t descsz,
+ size_t noff, size_t doff, int *flags)
+{
+ const char *name = RCAST(const char *, &nbuf[noff]);
+
+ if (namesz == 5 && strcmp(name, "SuSE") == 0 &&
+ type == NT_GNU_VERSION && descsz == 2) {
+ *flags |= FLAGS_DID_OS_NOTE;
+ if (file_printf(ms, ", for SuSE %d.%d", nbuf[doff],
+ nbuf[doff + 1]) == -1)
+ return -1;
+ return 1;
+ }
+
+ if (namesz == 4 && strcmp(name, "GNU") == 0 &&
+ type == NT_GNU_VERSION && descsz == 16) {
+ uint32_t desc[4];
+ memcpy(desc, &nbuf[doff], sizeof(desc));
+
+ *flags |= FLAGS_DID_OS_NOTE;
+ if (file_printf(ms, ", for GNU/") == -1)
+ return -1;
+ switch (elf_getu32(swap, desc[0])) {
+ case GNU_OS_LINUX:
+ if (file_printf(ms, "Linux") == -1)
+ return -1;
+ break;
+ case GNU_OS_HURD:
+ if (file_printf(ms, "Hurd") == -1)
+ return -1;
+ break;
+ case GNU_OS_SOLARIS:
+ if (file_printf(ms, "Solaris") == -1)
+ return -1;
+ break;
+ case GNU_OS_KFREEBSD:
+ if (file_printf(ms, "kFreeBSD") == -1)
+ return -1;
+ break;
+ case GNU_OS_KNETBSD:
+ if (file_printf(ms, "kNetBSD") == -1)
+ return -1;
+ break;
+ default:
+ if (file_printf(ms, "<unknown>") == -1)
+ return -1;
+ }
+ if (file_printf(ms, " %d.%d.%d", elf_getu32(swap, desc[1]),
+ elf_getu32(swap, desc[2]), elf_getu32(swap, desc[3])) == -1)
+ return -1;
+ return 1;
+ }
+
+ if (namesz == 7 && strcmp(name, "NetBSD") == 0) {
+ if (type == NT_NETBSD_VERSION && descsz == 4) {
+ *flags |= FLAGS_DID_OS_NOTE;
+ if (do_note_netbsd_version(ms, swap, &nbuf[doff]) == -1)
+ return -1;
+ return 1;
+ }
+ }
+
+ if (namesz == 8 && strcmp(name, "FreeBSD") == 0) {
+ if (type == NT_FREEBSD_VERSION && descsz == 4) {
+ *flags |= FLAGS_DID_OS_NOTE;
+ if (do_note_freebsd_version(ms, swap, &nbuf[doff])
+ == -1)
+ return -1;
+ return 1;
+ }
+ }
+
+ if (namesz == 8 && strcmp(name, "OpenBSD") == 0 &&
+ type == NT_OPENBSD_VERSION && descsz == 4) {
+ *flags |= FLAGS_DID_OS_NOTE;
+ if (file_printf(ms, ", for OpenBSD") == -1)
+ return -1;
+ /* Content of note is always 0 */
+ return 1;
+ }
+
+ if (namesz == 10 && strcmp(name, "DragonFly") == 0 &&
+ type == NT_DRAGONFLY_VERSION && descsz == 4) {
+ uint32_t desc;
+ *flags |= FLAGS_DID_OS_NOTE;
+ if (file_printf(ms, ", for DragonFly") == -1)
+ return -1;
+ memcpy(&desc, &nbuf[doff], sizeof(desc));
+ desc = elf_getu32(swap, desc);
+ if (file_printf(ms, " %d.%d.%d", desc / 100000,
+ desc / 10000 % 10, desc % 10000) == -1)
+ return -1;
+ return 1;
+ }
+ return 0;
+}
+
+file_private int
+do_pax_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
+ int swap, uint32_t namesz, uint32_t descsz,
+ size_t noff, size_t doff, int *flags)
+{
+ const char *name = RCAST(const char *, &nbuf[noff]);
+
+ if (namesz == 4 && strcmp(name, "PaX") == 0 &&
+ type == NT_NETBSD_PAX && descsz == 4) {
+ static const char *pax[] = {
+ "+mprotect",
+ "-mprotect",
+ "+segvguard",
+ "-segvguard",
+ "+ASLR",
+ "-ASLR",
+ };
+ uint32_t desc;
+ size_t i;
+ int did = 0;
+
+ *flags |= FLAGS_DID_NETBSD_PAX;
+ memcpy(&desc, &nbuf[doff], sizeof(desc));
+ desc = elf_getu32(swap, desc);
+
+ if (desc && file_printf(ms, ", PaX: ") == -1)
+ return -1;
+
+ for (i = 0; i < __arraycount(pax); i++) {
+ if (((1 << CAST(int, i)) & desc) == 0)
+ continue;
+ if (file_printf(ms, "%s%s", did++ ? "," : "",
+ pax[i]) == -1)
+ return -1;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+file_private int
+do_core_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
+ int swap, uint32_t namesz, uint32_t descsz,
+ size_t noff, size_t doff, int *flags, size_t size, int clazz)
+{
+#ifdef ELFCORE
+ char buf[256];
+ const char *name = RCAST(const char *, &nbuf[noff]);
+
+ int os_style = -1;
+ /*
+ * Sigh. The 2.0.36 kernel in Debian 2.1, at
+ * least, doesn't correctly implement name
+ * sections, in core dumps, as specified by
+ * the "Program Linking" section of "UNIX(R) System
+ * V Release 4 Programmer's Guide: ANSI C and
+ * Programming Support Tools", because my copy
+ * clearly says "The first 'namesz' bytes in 'name'
+ * contain a *null-terminated* [emphasis mine]
+ * character representation of the entry's owner
+ * or originator", but the 2.0.36 kernel code
+ * doesn't include the terminating null in the
+ * name....
+ */
+ if ((namesz == 4 && strncmp(name, "CORE", 4) == 0) ||
+ (namesz == 5 && strcmp(name, "CORE") == 0)) {
+ os_style = OS_STYLE_SVR4;
+ }
+
+ if ((namesz == 8 && strcmp(name, "FreeBSD") == 0)) {
+ os_style = OS_STYLE_FREEBSD;
+ }
+
+ if ((namesz >= 11 && strncmp(name, "NetBSD-CORE", 11)
+ == 0)) {
+ os_style = OS_STYLE_NETBSD;
+ }
+
+ if (os_style != -1 && (*flags & FLAGS_DID_CORE_STYLE) == 0) {
+ if (file_printf(ms, ", %s-style", os_style_names[os_style])
+ == -1)
+ return -1;
+ *flags |= FLAGS_DID_CORE_STYLE;
+ *flags |= os_style;
+ }
+
+ switch (os_style) {
+ case OS_STYLE_NETBSD:
+ if (type == NT_NETBSD_CORE_PROCINFO) {
+ char sbuf[512];
+ struct NetBSD_elfcore_procinfo pi;
+ memset(&pi, 0, sizeof(pi));
+ memcpy(&pi, nbuf + doff, MIN(descsz, sizeof(pi)));
+
+ if (file_printf(ms, ", from '%.31s', pid=%u, uid=%u, "
+ "gid=%u, nlwps=%u, lwp=%u (signal %u/code %u)",
+ file_printable(ms, sbuf, sizeof(sbuf),
+ RCAST(char *, pi.cpi_name), sizeof(pi.cpi_name)),
+ elf_getu32(swap, CAST(uint32_t, pi.cpi_pid)),
+ elf_getu32(swap, pi.cpi_euid),
+ elf_getu32(swap, pi.cpi_egid),
+ elf_getu32(swap, pi.cpi_nlwps),
+ elf_getu32(swap, CAST(uint32_t, pi.cpi_siglwp)),
+ elf_getu32(swap, pi.cpi_signo),
+ elf_getu32(swap, pi.cpi_sigcode)) == -1)
+ return -1;
+
+ *flags |= FLAGS_DID_CORE;
+ return 1;
+ }
+ break;
+
+ case OS_STYLE_FREEBSD:
+ if (type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) {
+ size_t argoff, pidoff;
+
+ if (clazz == ELFCLASS32)
+ argoff = 4 + 4 + 17;
+ else
+ argoff = 4 + 4 + 8 + 17;
+ if (file_printf(ms, ", from '%.80s'", nbuf + doff +
+ argoff) == -1)
+ return -1;
+ pidoff = argoff + 81 + 2;
+ if (doff + pidoff + 4 <= size) {
+ if (file_printf(ms, ", pid=%u",
+ elf_getu32(swap, *RCAST(uint32_t *, (nbuf +
+ doff + pidoff)))) == -1)
+ return -1;
+ }
+ *flags |= FLAGS_DID_CORE;
+ }
+ break;
+
+ default:
+ if (type == NT_PRPSINFO && *flags & FLAGS_IS_CORE) {
+ size_t i, j;
+ unsigned char c;
+ /*
+ * Extract the program name. We assume
+ * it to be 16 characters (that's what it
+ * is in SunOS 5.x and Linux).
+ *
+ * Unfortunately, it's at a different offset
+ * in various OSes, so try multiple offsets.
+ * If the characters aren't all printable,
+ * reject it.
+ */
+ for (i = 0; i < NOFFSETS; i++) {
+ unsigned char *cname, *cp;
+ size_t reloffset = prpsoffsets(i);
+ size_t noffset = doff + reloffset;
+ size_t k;
+ for (j = 0; j < 16; j++, noffset++,
+ reloffset++) {
+ /*
+ * Make sure we're not past
+ * the end of the buffer; if
+ * we are, just give up.
+ */
+ if (noffset >= size)
+ goto tryanother;
+
+ /*
+ * Make sure we're not past
+ * the end of the contents;
+ * if we are, this obviously
+ * isn't the right offset.
+ */
+ if (reloffset >= descsz)
+ goto tryanother;
+
+ c = nbuf[noffset];
+ if (c == '\0') {
+ /*
+ * A '\0' at the
+ * beginning is
+ * obviously wrong.
+ * Any other '\0'
+ * means we're done.
+ */
+ if (j == 0)
+ goto tryanother;
+ else
+ break;
+ } else {
+ /*
+ * A nonprintable
+ * character is also
+ * wrong.
+ */
+ if (!isprint(c) || isquote(c))
+ goto tryanother;
+ }
+ }
+ /*
+ * Well, that worked.
+ */
+
+ /*
+ * Try next offsets, in case this match is
+ * in the middle of a string.
+ */
+ for (k = i + 1 ; k < NOFFSETS; k++) {
+ size_t no;
+ int adjust = 1;
+ if (prpsoffsets(k) >= prpsoffsets(i))
+ continue;
+ /*
+ * pr_fname == pr_psargs - 16 &&
+ * non-nul-terminated fname (qemu)
+ */
+ if (prpsoffsets(k) ==
+ prpsoffsets(i) - 16 && j == 16)
+ continue;
+ for (no = doff + prpsoffsets(k);
+ no < doff + prpsoffsets(i); no++)
+ adjust = adjust
+ && isprint(nbuf[no]);
+ if (adjust)
+ i = k;
+ }
+
+ cname = CAST(unsigned char *,
+ &nbuf[doff + prpsoffsets(i)]);
+ for (cp = cname; cp < nbuf + size && *cp
+ && isprint(*cp); cp++)
+ continue;
+ /*
+ * Linux apparently appends a space at the end
+ * of the command line: remove it.
+ */
+ while (cp > cname && isspace(cp[-1]))
+ cp--;
+ if (file_printf(ms, ", from '%s'",
+ file_copystr(buf, sizeof(buf),
+ CAST(size_t, cp - cname),
+ RCAST(char *, cname))) == -1)
+ return -1;
+ *flags |= FLAGS_DID_CORE;
+ return 1;
+
+ tryanother:
+ ;
+ }
+ }
+ break;
+ }
+#endif
+ return 0;
+}
+
+file_private off_t
+get_offset_from_virtaddr(struct magic_set *ms, int swap, int clazz, int fd,
+ off_t off, int num, off_t fsize, uint64_t virtaddr)
+{
+ Elf32_Phdr ph32;
+ Elf64_Phdr ph64;
+
+ /*
+ * Loop through all the program headers and find the header with
+ * virtual address in which the "virtaddr" belongs to.
+ */
+ for ( ; num; num--) {
+ if (pread(fd, xph_addr, xph_sizeof, off) <
+ CAST(ssize_t, xph_sizeof)) {
+ if (file_printf(ms,
+ ", can't read elf program header at %jd",
+ (intmax_t)off) == -1)
+ return -1;
+ return 0;
+
+ }
+ off += xph_sizeof;
+
+ if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
+ /* Perhaps warn here */
+ continue;
+ }
+
+ if (virtaddr >= xph_vaddr && virtaddr < xph_vaddr + xph_filesz)
+ return xph_offset + (virtaddr - xph_vaddr);
+ }
+ return 0;
+}
+
+file_private size_t
+get_string_on_virtaddr(struct magic_set *ms,
+ int swap, int clazz, int fd, off_t ph_off, int ph_num,
+ off_t fsize, uint64_t virtaddr, char *buf, ssize_t buflen)
+{
+ char *bptr;
+ off_t offset;
+
+ if (buflen == 0)
+ return 0;
+
+ offset = get_offset_from_virtaddr(ms, swap, clazz, fd, ph_off, ph_num,
+ fsize, virtaddr);
+ if (offset < 0 ||
+ (buflen = pread(fd, buf, CAST(size_t, buflen), offset)) <= 0) {
+ (void)file_printf(ms, ", can't read elf string at %jd",
+ (intmax_t)offset);
+ return 0;
+ }
+
+ buf[buflen - 1] = '\0';
+
+ /* We expect only printable characters, so return if buffer contains
+ * non-printable character before the '\0' or just '\0'. */
+ for (bptr = buf; *bptr && isprint(CAST(unsigned char, *bptr)); bptr++)
+ continue;
+ if (*bptr != '\0')
+ return 0;
+
+ return bptr - buf;
+}
+
+
+/*ARGSUSED*/
+file_private int
+do_auxv_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type,
+ int swap, uint32_t namesz __attribute__((__unused__)),
+ uint32_t descsz __attribute__((__unused__)),
+ size_t noff __attribute__((__unused__)), size_t doff,
+ int *flags, size_t size __attribute__((__unused__)), int clazz,
+ int fd, off_t ph_off, int ph_num, off_t fsize)
+{
+#ifdef ELFCORE
+ Aux32Info auxv32;
+ Aux64Info auxv64;
+ size_t elsize = xauxv_sizeof;
+ const char *tag;
+ int is_string;
+ size_t nval, off;
+
+ if ((*flags & (FLAGS_IS_CORE|FLAGS_DID_CORE_STYLE)) !=
+ (FLAGS_IS_CORE|FLAGS_DID_CORE_STYLE))
+ return 0;
+
+ switch (*flags & FLAGS_CORE_STYLE) {
+ case OS_STYLE_SVR4:
+ if (type != NT_AUXV)
+ return 0;
+ break;
+#ifdef notyet
+ case OS_STYLE_NETBSD:
+ if (type != NT_NETBSD_CORE_AUXV)
+ return 0;
+ break;
+ case OS_STYLE_FREEBSD:
+ if (type != NT_FREEBSD_PROCSTAT_AUXV)
+ return 0;
+ break;
+#endif
+ default:
+ return 0;
+ }
+
+ *flags |= FLAGS_DID_AUXV;
+
+ nval = 0;
+ for (off = 0; off + elsize <= descsz; off += elsize) {
+ memcpy(xauxv_addr, &nbuf[doff + off], xauxv_sizeof);
+ /* Limit processing to 50 vector entries to prevent DoS */
+ if (nval++ >= 50) {
+ file_error(ms, 0, "Too many ELF Auxv elements");
+ return 1;
+ }
+
+ switch(xauxv_type) {
+ case AT_LINUX_EXECFN:
+ is_string = 1;
+ tag = "execfn";
+ break;
+ case AT_LINUX_PLATFORM:
+ is_string = 1;
+ tag = "platform";
+ break;
+ case AT_LINUX_UID:
+ is_string = 0;
+ tag = "real uid";
+ break;
+ case AT_LINUX_GID:
+ is_string = 0;
+ tag = "real gid";
+ break;
+ case AT_LINUX_EUID:
+ is_string = 0;
+ tag = "effective uid";
+ break;
+ case AT_LINUX_EGID:
+ is_string = 0;
+ tag = "effective gid";
+ break;
+ default:
+ is_string = 0;
+ tag = NULL;
+ break;
+ }
+
+ if (tag == NULL)
+ continue;
+
+ if (is_string) {
+ char buf[256];
+ ssize_t buflen;
+ buflen = get_string_on_virtaddr(ms, swap, clazz, fd,
+ ph_off, ph_num, fsize, xauxv_val, buf, sizeof(buf));
+
+ if (buflen == 0)
+ continue;
+
+ if (file_printf(ms, ", %s: '%s'", tag, buf) == -1)
+ return -1;
+ } else {
+ if (file_printf(ms, ", %s: %d", tag,
+ CAST(int, xauxv_val)) == -1)
+ return -1;
+ }
+ }
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+file_private size_t
+dodynamic(struct magic_set *ms, void *vbuf, size_t offset, size_t size,
+ int clazz, int swap, int *pie, size_t *need)
+{
+ Elf32_Dyn dh32;
+ Elf64_Dyn dh64;
+ unsigned char *dbuf = CAST(unsigned char *, vbuf);
+
+ if (xdh_sizeof + offset > size) {
+ /*
+ * We're out of note headers.
+ */
+ return xdh_sizeof + offset;
+ }
+
+ memcpy(xdh_addr, &dbuf[offset], xdh_sizeof);
+ offset += xdh_sizeof;
+
+ switch (xdh_tag) {
+ case DT_FLAGS_1:
+ *pie = 1;
+ if (xdh_val & DF_1_PIE)
+ ms->mode |= 0111;
+ else
+ ms->mode &= ~0111;
+ break;
+ case DT_NEEDED:
+ (*need)++;
+ break;
+ default:
+ break;
+ }
+ return offset;
+}
+
+
+file_private size_t
+donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size,
+ int clazz, int swap, size_t align, int *flags, uint16_t *notecount,
+ int fd, off_t ph_off, int ph_num, off_t fsize)
+{
+ Elf32_Nhdr nh32;
+ Elf64_Nhdr nh64;
+ size_t noff, doff;
+ uint32_t namesz, descsz;
+ char buf[256];
+ unsigned char *nbuf = CAST(unsigned char *, vbuf);
+
+ if (*notecount == 0)
+ return 0;
+ --*notecount;
+
+ if (xnh_sizeof + offset > size) {
+ /*
+ * We're out of note headers.
+ */
+ return xnh_sizeof + offset;
+ }
+ /*XXX: GCC */
+ memset(&nh32, 0, sizeof(nh32));
+ memset(&nh64, 0, sizeof(nh64));
+
+ memcpy(xnh_addr, &nbuf[offset], xnh_sizeof);
+ offset += xnh_sizeof;
+
+ namesz = xnh_namesz;
+ descsz = xnh_descsz;
+
+ if ((namesz == 0) && (descsz == 0)) {
+ /*
+ * We're out of note headers.
+ */
+ return (offset >= size) ? offset : size;
+ }
+
+ if (namesz & 0x80000000) {
+ (void)file_printf(ms, ", bad note name size %#lx",
+ CAST(unsigned long, namesz));
+ return 0;
+ }
+
+ if (descsz & 0x80000000) {
+ (void)file_printf(ms, ", bad note description size %#lx",
+ CAST(unsigned long, descsz));
+ return 0;
+ }
+
+ noff = offset;
+ doff = ELF_ALIGN(offset + namesz);
+
+ if (offset + namesz > size) {
+ /*
+ * We're past the end of the buffer.
+ */
+ return doff;
+ }
+
+ offset = ELF_ALIGN(doff + descsz);
+ if (doff + descsz > size) {
+ /*
+ * We're past the end of the buffer.
+ */
+ return (offset >= size) ? offset : size;
+ }
+
+
+ if ((*flags & FLAGS_DID_OS_NOTE) == 0) {
+ if (do_os_note(ms, nbuf, xnh_type, swap,
+ namesz, descsz, noff, doff, flags))
+ return offset;
+ }
+
+ if ((*flags & FLAGS_DID_BUILD_ID) == 0) {
+ if (do_bid_note(ms, nbuf, xnh_type, swap,
+ namesz, descsz, noff, doff, flags))
+ return offset;
+ }
+
+ if ((*flags & FLAGS_DID_NETBSD_PAX) == 0) {
+ if (do_pax_note(ms, nbuf, xnh_type, swap,
+ namesz, descsz, noff, doff, flags))
+ return offset;
+ }
+
+ if ((*flags & FLAGS_DID_CORE) == 0) {
+ if (do_core_note(ms, nbuf, xnh_type, swap,
+ namesz, descsz, noff, doff, flags, size, clazz))
+ return offset;
+ }
+
+ if ((*flags & FLAGS_DID_AUXV) == 0) {
+ if (do_auxv_note(ms, nbuf, xnh_type, swap,
+ namesz, descsz, noff, doff, flags, size, clazz,
+ fd, ph_off, ph_num, fsize))
+ return offset;
+ }
+
+ if (namesz == 7 && strcmp(RCAST(char *, &nbuf[noff]), "NetBSD") == 0) {
+ int descw, flag;
+ const char *str, *tag;
+ if (descsz > 100)
+ descsz = 100;
+ switch (xnh_type) {
+ case NT_NETBSD_VERSION:
+ return offset;
+ case NT_NETBSD_MARCH:
+ flag = FLAGS_DID_NETBSD_MARCH;
+ tag = "compiled for";
+ break;
+ case NT_NETBSD_CMODEL:
+ flag = FLAGS_DID_NETBSD_CMODEL;
+ tag = "compiler model";
+ break;
+ case NT_NETBSD_EMULATION:
+ flag = FLAGS_DID_NETBSD_EMULATION;
+ tag = "emulation:";
+ break;
+ default:
+ if (*flags & FLAGS_DID_NETBSD_UNKNOWN)
+ return offset;
+ *flags |= FLAGS_DID_NETBSD_UNKNOWN;
+ if (file_printf(ms, ", note=%u", xnh_type) == -1)
+ return offset;
+ return offset;
+ }
+
+ if (*flags & flag)
+ return offset;
+ str = RCAST(const char *, &nbuf[doff]);
+ descw = CAST(int, descsz);
+ *flags |= flag;
+ file_printf(ms, ", %s: %s", tag,
+ file_copystr(buf, sizeof(buf), descw, str));
+ return offset;
+ }
+
+ return offset;
+}
+
+/* SunOS 5.x hardware capability descriptions */
+typedef struct cap_desc {
+ uint64_t cd_mask;
+ const char *cd_name;
+} cap_desc_t;
+
+static const cap_desc_t cap_desc_sparc[] = {
+ { AV_SPARC_MUL32, "MUL32" },
+ { AV_SPARC_DIV32, "DIV32" },
+ { AV_SPARC_FSMULD, "FSMULD" },
+ { AV_SPARC_V8PLUS, "V8PLUS" },
+ { AV_SPARC_POPC, "POPC" },
+ { AV_SPARC_VIS, "VIS" },
+ { AV_SPARC_VIS2, "VIS2" },
+ { AV_SPARC_ASI_BLK_INIT, "ASI_BLK_INIT" },
+ { AV_SPARC_FMAF, "FMAF" },
+ { AV_SPARC_FJFMAU, "FJFMAU" },
+ { AV_SPARC_IMA, "IMA" },
+ { 0, NULL }
+};
+
+static const cap_desc_t cap_desc_386[] = {
+ { AV_386_FPU, "FPU" },
+ { AV_386_TSC, "TSC" },
+ { AV_386_CX8, "CX8" },
+ { AV_386_SEP, "SEP" },
+ { AV_386_AMD_SYSC, "AMD_SYSC" },
+ { AV_386_CMOV, "CMOV" },
+ { AV_386_MMX, "MMX" },
+ { AV_386_AMD_MMX, "AMD_MMX" },
+ { AV_386_AMD_3DNow, "AMD_3DNow" },
+ { AV_386_AMD_3DNowx, "AMD_3DNowx" },
+ { AV_386_FXSR, "FXSR" },
+ { AV_386_SSE, "SSE" },
+ { AV_386_SSE2, "SSE2" },
+ { AV_386_PAUSE, "PAUSE" },
+ { AV_386_SSE3, "SSE3" },
+ { AV_386_MON, "MON" },
+ { AV_386_CX16, "CX16" },
+ { AV_386_AHF, "AHF" },
+ { AV_386_TSCP, "TSCP" },
+ { AV_386_AMD_SSE4A, "AMD_SSE4A" },
+ { AV_386_POPCNT, "POPCNT" },
+ { AV_386_AMD_LZCNT, "AMD_LZCNT" },
+ { AV_386_SSSE3, "SSSE3" },
+ { AV_386_SSE4_1, "SSE4.1" },
+ { AV_386_SSE4_2, "SSE4.2" },
+ { 0, NULL }
+};
+
+file_private int
+doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num,
+ size_t size, off_t fsize, int mach, int strtab, int *flags,
+ uint16_t *notecount)
+{
+ Elf32_Shdr sh32;
+ Elf64_Shdr sh64;
+ int stripped = 1, has_debug_info = 0;
+ size_t nbadcap = 0;
+ void *nbuf;
+ off_t noff, coff, name_off, offs;
+ uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilities */
+ uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilities */
+ char name[50];
+ ssize_t namesize;
+
+ if (ms->flags & MAGIC_MIME)
+ return 0;
+
+ if (num == 0) {
+ if (file_printf(ms, ", no section header") == -1)
+ return -1;
+ return 0;
+ }
+ if (size != xsh_sizeof) {
+ if (file_printf(ms, ", corrupted section header size") == -1)
+ return -1;
+ return 0;
+ }
+
+ /* Read offset of name section to be able to read section names later */
+ offs = CAST(off_t, (off + size * strtab));
+ if (pread(fd, xsh_addr, xsh_sizeof, offs) < CAST(ssize_t, xsh_sizeof)) {
+ if (file_printf(ms, ", missing section headers at %jd",
+ (intmax_t)offs) == -1)
+ return -1;
+ return 0;
+ }
+ name_off = xsh_offset;
+
+ if (fsize != SIZE_UNKNOWN && fsize < name_off) {
+ if (file_printf(ms, ", too large section header offset %jd",
+ (intmax_t)name_off) == -1)
+ return -1;
+ return 0;
+ }
+
+ for ( ; num; num--) {
+ /* Read the name of this section. */
+ offs = name_off + xsh_name;
+ if ((namesize = pread(fd, name, sizeof(name) - 1, offs))
+ == -1) {
+ if (file_printf(ms,
+ ", can't read name of elf section at %jd",
+ (intmax_t)offs) == -1)
+ return -1;
+ return 0;
+ }
+ name[namesize] = '\0';
+ if (strcmp(name, ".debug_info") == 0) {
+ has_debug_info = 1;
+ stripped = 0;
+ }
+
+ if (pread(fd, xsh_addr, xsh_sizeof, off) <
+ CAST(ssize_t, xsh_sizeof)) {
+ if (file_printf(ms, ", can't read elf section at %jd",
+ (intmax_t)off) == -1)
+ return -1;
+ return 0;
+ }
+ off += size;
+
+ /* Things we can determine before we seek */
+ switch (xsh_type) {
+ case SHT_SYMTAB:
+#if 0
+ case SHT_DYNSYM:
+#endif
+ stripped = 0;
+ break;
+ default:
+ if (fsize != SIZE_UNKNOWN && xsh_offset > fsize) {
+ /* Perhaps warn here */
+ continue;
+ }
+ break;
+ }
+
+
+ /* Things we can determine when we seek */
+ switch (xsh_type) {
+ case SHT_NOTE:
+ if (CAST(uintmax_t, (xsh_size + xsh_offset)) >
+ CAST(uintmax_t, fsize)) {
+ if (file_printf(ms,
+ ", note offset/size %#" INTMAX_T_FORMAT
+ "x+%#" INTMAX_T_FORMAT "x exceeds"
+ " file size %#" INTMAX_T_FORMAT "x",
+ CAST(uintmax_t, xsh_offset),
+ CAST(uintmax_t, xsh_size),
+ CAST(uintmax_t, fsize)) == -1)
+ return -1;
+ return 0;
+ }
+ if (xsh_size > ms->elf_shsize_max) {
+ file_error(ms, errno, "Note section size too "
+ "big (%ju > %zu)", (uintmax_t)xsh_size,
+ ms->elf_shsize_max);
+ return -1;
+ }
+ if ((nbuf = malloc(xsh_size)) == NULL) {
+ file_error(ms, errno, "Cannot allocate memory"
+ " for note");
+ return -1;
+ }
+ offs = xsh_offset;
+ if (pread(fd, nbuf, xsh_size, offs) <
+ CAST(ssize_t, xsh_size)) {
+ free(nbuf);
+ if (file_printf(ms,
+ ", can't read elf note at %jd",
+ (intmax_t)offs) == -1)
+ return -1;
+ return 0;
+ }
+
+ noff = 0;
+ for (;;) {
+ if (noff >= CAST(off_t, xsh_size))
+ break;
+ noff = donote(ms, nbuf, CAST(size_t, noff),
+ xsh_size, clazz, swap, 4, flags, notecount,
+ fd, 0, 0, 0);
+ if (noff == 0)
+ break;
+ }
+ free(nbuf);
+ break;
+ case SHT_SUNW_cap:
+ switch (mach) {
+ case EM_SPARC:
+ case EM_SPARCV9:
+ case EM_IA_64:
+ case EM_386:
+ case EM_AMD64:
+ break;
+ default:
+ goto skip;
+ }
+
+ if (nbadcap > 5)
+ break;
+ if (lseek(fd, xsh_offset, SEEK_SET)
+ == CAST(off_t, -1)) {
+ file_badseek(ms);
+ return -1;
+ }
+ coff = 0;
+ for (;;) {
+ Elf32_Cap cap32;
+ Elf64_Cap cap64;
+ char cbuf[/*CONSTCOND*/
+ MAX(sizeof(cap32), sizeof(cap64))];
+ if ((coff += xcap_sizeof) >
+ CAST(off_t, xsh_size))
+ break;
+ if (read(fd, cbuf, CAST(size_t, xcap_sizeof)) !=
+ CAST(ssize_t, xcap_sizeof)) {
+ file_badread(ms);
+ return -1;
+ }
+ if (cbuf[0] == 'A') {
+#ifdef notyet
+ char *p = cbuf + 1;
+ uint32_t len, tag;
+ memcpy(&len, p, sizeof(len));
+ p += 4;
+ len = getu32(swap, len);
+ if (memcmp("gnu", p, 3) != 0) {
+ if (file_printf(ms,
+ ", unknown capability %.3s", p)
+ == -1)
+ return -1;
+ break;
+ }
+ p += strlen(p) + 1;
+ tag = *p++;
+ memcpy(&len, p, sizeof(len));
+ p += 4;
+ len = getu32(swap, len);
+ if (tag != 1) {
+ if (file_printf(ms, ", unknown gnu"
+ " capability tag %d", tag)
+ == -1)
+ return -1;
+ break;
+ }
+ // gnu attributes
+#endif
+ break;
+ }
+ memcpy(xcap_addr, cbuf, xcap_sizeof);
+ switch (xcap_tag) {
+ case CA_SUNW_NULL:
+ break;
+ case CA_SUNW_HW_1:
+ cap_hw1 |= xcap_val;
+ break;
+ case CA_SUNW_SF_1:
+ cap_sf1 |= xcap_val;
+ break;
+ default:
+ if (file_printf(ms,
+ ", with unknown capability "
+ "%#" INT64_T_FORMAT "x = %#"
+ INT64_T_FORMAT "x",
+ CAST(unsigned long long, xcap_tag),
+ CAST(unsigned long long, xcap_val))
+ == -1)
+ return -1;
+ if (nbadcap++ > 2)
+ coff = xsh_size;
+ break;
+ }
+ }
+ /*FALLTHROUGH*/
+ skip:
+ default:
+ break;
+ }
+ }
+
+ if (has_debug_info) {
+ if (file_printf(ms, ", with debug_info") == -1)
+ return -1;
+ }
+ if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1)
+ return -1;
+ if (cap_hw1) {
+ const cap_desc_t *cdp;
+ switch (mach) {
+ case EM_SPARC:
+ case EM_SPARC32PLUS:
+ case EM_SPARCV9:
+ cdp = cap_desc_sparc;
+ break;
+ case EM_386:
+ case EM_IA_64:
+ case EM_AMD64:
+ cdp = cap_desc_386;
+ break;
+ default:
+ cdp = NULL;
+ break;
+ }
+ if (file_printf(ms, ", uses") == -1)
+ return -1;
+ if (cdp) {
+ while (cdp->cd_name) {
+ if (cap_hw1 & cdp->cd_mask) {
+ if (file_printf(ms,
+ " %s", cdp->cd_name) == -1)
+ return -1;
+ cap_hw1 &= ~cdp->cd_mask;
+ }
+ ++cdp;
+ }
+ if (cap_hw1)
+ if (file_printf(ms,
+ " unknown hardware capability %#"
+ INT64_T_FORMAT "x",
+ CAST(unsigned long long, cap_hw1)) == -1)
+ return -1;
+ } else {
+ if (file_printf(ms,
+ " hardware capability %#" INT64_T_FORMAT "x",
+ CAST(unsigned long long, cap_hw1)) == -1)
+ return -1;
+ }
+ }
+ if (cap_sf1) {
+ if (cap_sf1 & SF1_SUNW_FPUSED) {
+ if (file_printf(ms,
+ (cap_sf1 & SF1_SUNW_FPKNWN)
+ ? ", uses frame pointer"
+ : ", not known to use frame pointer") == -1)
+ return -1;
+ }
+ cap_sf1 &= ~SF1_SUNW_MASK;
+ if (cap_sf1)
+ if (file_printf(ms,
+ ", with unknown software capability %#"
+ INT64_T_FORMAT "x",
+ CAST(unsigned long long, cap_sf1)) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Look through the program headers of an executable image, to determine
+ * if it is statically or dynamically linked. If it has a dynamic section,
+ * it is pie, and does not have an interpreter or needed libraries, we
+ * call it static pie.
+ */
+file_private int
+dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
+ int num, size_t size, off_t fsize, int sh_num, int *flags,
+ uint16_t *notecount)
+{
+ Elf32_Phdr ph32;
+ Elf64_Phdr ph64;
+ const char *linking_style;
+ unsigned char nbuf[BUFSIZ];
+ char ibuf[BUFSIZ];
+ char interp[BUFSIZ];
+ ssize_t bufsize;
+ size_t offset, align, need = 0;
+ int pie = 0, dynamic = 0;
+
+ if (num == 0) {
+ if (file_printf(ms, ", no program header") == -1)
+ return -1;
+ return 0;
+ }
+ if (size != xph_sizeof) {
+ if (file_printf(ms, ", corrupted program header size") == -1)
+ return -1;
+ return 0;
+ }
+
+ interp[0] = '\0';
+ for ( ; num; num--) {
+ int doread;
+ if (pread(fd, xph_addr, xph_sizeof, off) <
+ CAST(ssize_t, xph_sizeof)) {
+ if (file_printf(ms,
+ ", can't read elf program headers at %jd",
+ (intmax_t)off) == -1)
+ return -1;
+ return 0;
+ }
+
+ off += size;
+ bufsize = 0;
+ align = 4;
+
+ /* Things we can determine before we seek */
+ switch (xph_type) {
+ case PT_DYNAMIC:
+ doread = 1;
+ break;
+ case PT_NOTE:
+ if (sh_num) /* Did this through section headers */
+ continue;
+ if (((align = xph_align) & 0x80000000UL) != 0 ||
+ align < 4) {
+ if (file_printf(ms,
+ ", invalid note alignment %#lx",
+ CAST(unsigned long, align)) == -1)
+ return -1;
+ align = 4;
+ }
+ /*FALLTHROUGH*/
+ case PT_INTERP:
+ doread = 1;
+ break;
+ default:
+ doread = 0;
+ if (fsize != SIZE_UNKNOWN && xph_offset > fsize) {
+ /* Maybe warn here? */
+ continue;
+ }
+ break;
+ }
+
+ if (doread) {
+ size_t len = xph_filesz < sizeof(nbuf) ? xph_filesz
+ : sizeof(nbuf);
+ off_t offs = xph_offset;
+ bufsize = pread(fd, nbuf, len, offs);
+ if (bufsize == -1) {
+ if (file_printf(ms,
+ ", can't read section at %jd",
+ (intmax_t)offs) == -1)
+ return -1;
+ return 0;
+ }
+ }
+
+ /* Things we can determine when we seek */
+ switch (xph_type) {
+ case PT_DYNAMIC:
+ dynamic = 1;
+ offset = 0;
+ // Let DF_1 determine if we are PIE or not.
+ ms->mode &= ~0111;
+ for (;;) {
+ if (offset >= CAST(size_t, bufsize))
+ break;
+ offset = dodynamic(ms, nbuf, offset,
+ CAST(size_t, bufsize), clazz, swap,
+ &pie, &need);
+ if (offset == 0)
+ break;
+ }
+ if (ms->flags & MAGIC_MIME)
+ continue;
+ break;
+
+ case PT_INTERP:
+ need++;
+ if (ms->flags & MAGIC_MIME)
+ continue;
+ if (bufsize && nbuf[0]) {
+ nbuf[bufsize - 1] = '\0';
+ memcpy(interp, nbuf, CAST(size_t, bufsize));
+ } else
+ strlcpy(interp, "*empty*", sizeof(interp));
+ break;
+ case PT_NOTE:
+ if (ms->flags & MAGIC_MIME)
+ return 0;
+ /*
+ * This is a PT_NOTE section; loop through all the notes
+ * in the section.
+ */
+ offset = 0;
+ for (;;) {
+ if (offset >= CAST(size_t, bufsize))
+ break;
+ offset = donote(ms, nbuf, offset,
+ CAST(size_t, bufsize), clazz, swap, align,
+ flags, notecount, fd, 0, 0, 0);
+ if (offset == 0)
+ break;
+ }
+ break;
+ default:
+ if (ms->flags & MAGIC_MIME)
+ continue;
+ break;
+ }
+ }
+ if (ms->flags & MAGIC_MIME)
+ return 0;
+ if (dynamic) {
+ if (pie && need == 0)
+ linking_style = "static-pie";
+ else
+ linking_style = "dynamically";
+ } else {
+ linking_style = "statically";
+ }
+ if (file_printf(ms, ", %s linked", linking_style) == -1)
+ return -1;
+ if (interp[0])
+ if (file_printf(ms, ", interpreter %s", file_printable(ms,
+ ibuf, sizeof(ibuf), interp, sizeof(interp))) == -1)
+ return -1;
+ return 0;
+}
+
+
+file_protected int
+file_tryelf(struct magic_set *ms, const struct buffer *b)
+{
+ int fd = b->fd;
+ const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
+ size_t nbytes = b->flen;
+ union {
+ int32_t l;
+ char c[sizeof(int32_t)];
+ } u;
+ int clazz;
+ int swap;
+ struct stat st;
+ const struct stat *stp;
+ off_t fsize;
+ int flags = 0;
+ Elf32_Ehdr elf32hdr;
+ Elf64_Ehdr elf64hdr;
+ uint16_t type, phnum, shnum, notecount;
+
+ if (ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))
+ return 0;
+ /*
+ * ELF executables have multiple section headers in arbitrary
+ * file locations and thus file(1) cannot determine it from easily.
+ * Instead we traverse thru all section headers until a symbol table
+ * one is found or else the binary is stripped.
+ * Return immediately if it's not ELF (so we avoid pipe2file unless
+ * needed).
+ */
+ if (buf[EI_MAG0] != ELFMAG0
+ || (buf[EI_MAG1] != ELFMAG1 && buf[EI_MAG1] != OLFMAG1)
+ || buf[EI_MAG2] != ELFMAG2 || buf[EI_MAG3] != ELFMAG3)
+ return 0;
+
+ /*
+ * If we cannot seek, it must be a pipe, socket or fifo.
+ */
+ if((lseek(fd, CAST(off_t, 0), SEEK_SET) == CAST(off_t, -1))
+ && (errno == ESPIPE))
+ fd = file_pipe2file(ms, fd, buf, nbytes);
+
+ if (fd == -1) {
+ file_badread(ms);
+ return -1;
+ }
+
+ stp = &b->st;
+ /*
+ * b->st.st_size != 0 if previous fstat() succeeded,
+ * which is likely, we can avoid extra stat() call.
+ */
+ if (b->st.st_size == 0) {
+ stp = &st;
+ if (fstat(fd, &st) == -1) {
+ file_badread(ms);
+ return -1;
+ }
+ }
+ if (S_ISREG(stp->st_mode) || stp->st_size != 0)
+ fsize = stp->st_size;
+ else
+ fsize = SIZE_UNKNOWN;
+
+ clazz = buf[EI_CLASS];
+
+ switch (clazz) {
+ case ELFCLASS32:
+#undef elf_getu
+#define elf_getu(a, b) elf_getu32(a, b)
+#undef elfhdr
+#define elfhdr elf32hdr
+#include "elfclass.h"
+ case ELFCLASS64:
+#undef elf_getu
+#define elf_getu(a, b) elf_getu64(a, b)
+#undef elfhdr
+#define elfhdr elf64hdr
+#include "elfclass.h"
+ default:
+ if (file_printf(ms, ", unknown class %d", clazz) == -1)
+ return -1;
+ break;
+ }
+ return 0;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/readelf.h b/contrib/libs/libmagic/src/readelf.h
new file mode 100644
index 0000000000..809d3f7573
--- /dev/null
+++ b/contrib/libs/libmagic/src/readelf.h
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) Christos Zoulas 2003.
+ * All Rights Reserved.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * @(#)Id: readelf.h,v 1.9 2002/05/16 18:45:56 christos Exp
+ *
+ * Provide elf data structures for non-elf machines, allowing file
+ * non-elf hosts to determine if an elf binary is stripped.
+ * Note: cobbled from the linux header file, with modifications
+ */
+#ifndef __fake_elf_h__
+#define __fake_elf_h__
+
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+typedef uint32_t Elf32_Addr;
+typedef uint32_t Elf32_Off;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Word;
+typedef uint8_t Elf32_Char;
+
+typedef uint64_t Elf64_Addr;
+typedef uint64_t Elf64_Off;
+typedef uint64_t Elf64_Xword;
+typedef uint16_t Elf64_Half;
+typedef uint32_t Elf64_Word;
+typedef uint8_t Elf64_Char;
+
+#define EI_NIDENT 16
+
+typedef struct {
+ Elf32_Word a_type; /* 32-bit id */
+ Elf32_Word a_v; /* 32-bit id */
+} Aux32Info;
+
+typedef struct {
+ Elf64_Xword a_type; /* 64-bit id */
+ Elf64_Xword a_v; /* 64-bit id */
+} Aux64Info;
+
+#define AT_NULL 0 /* end of vector */
+#define AT_IGNORE 1 /* entry should be ignored */
+#define AT_EXECFD 2 /* file descriptor of program */
+#define AT_PHDR 3 /* program headers for program */
+#define AT_PHENT 4 /* size of program header entry */
+#define AT_PHNUM 5 /* number of program headers */
+#define AT_PAGESZ 6 /* system page size */
+#define AT_BASE 7 /* base address of interpreter */
+#define AT_FLAGS 8 /* flags */
+#define AT_ENTRY 9 /* entry point of program */
+#define AT_LINUX_NOTELF 10 /* program is not ELF */
+#define AT_LINUX_UID 11 /* real uid */
+#define AT_LINUX_EUID 12 /* effective uid */
+#define AT_LINUX_GID 13 /* real gid */
+#define AT_LINUX_EGID 14 /* effective gid */
+#define AT_LINUX_PLATFORM 15 /* string identifying CPU for optimizations */
+#define AT_LINUX_HWCAP 16 /* arch dependent hints at CPU capabilities */
+#define AT_LINUX_CLKTCK 17 /* frequency at which times() increments */
+/* AT_* values 18 through 22 are reserved */
+#define AT_LINUX_SECURE 23 /* secure mode boolean */
+#define AT_LINUX_BASE_PLATFORM 24 /* string identifying real platform, may
+ * differ from AT_PLATFORM. */
+#define AT_LINUX_RANDOM 25 /* address of 16 random bytes */
+#define AT_LINUX_HWCAP2 26 /* extension of AT_HWCAP */
+#define AT_LINUX_EXECFN 31 /* filename of program */
+
+typedef struct {
+ Elf32_Char e_ident[EI_NIDENT];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry; /* Entry point */
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct {
+ Elf64_Char e_ident[EI_NIDENT];
+ Elf64_Half e_type;
+ Elf64_Half e_machine;
+ Elf64_Word e_version;
+ Elf64_Addr e_entry; /* Entry point */
+ Elf64_Off e_phoff;
+ Elf64_Off e_shoff;
+ Elf64_Word e_flags;
+ Elf64_Half e_ehsize;
+ Elf64_Half e_phentsize;
+ Elf64_Half e_phnum;
+ Elf64_Half e_shentsize;
+ Elf64_Half e_shnum;
+ Elf64_Half e_shstrndx;
+} Elf64_Ehdr;
+
+/* e_type */
+#define ET_REL 1
+#define ET_EXEC 2
+#define ET_DYN 3
+#define ET_CORE 4
+
+/* e_machine (used only for SunOS 5.x hardware capabilities) */
+#define EM_SPARC 2
+#define EM_386 3
+#define EM_SPARC32PLUS 18
+#define EM_SPARCV9 43
+#define EM_IA_64 50
+#define EM_AMD64 62
+
+/* sh_type */
+#define SHT_SYMTAB 2
+#define SHT_NOTE 7
+#define SHT_DYNSYM 11
+#define SHT_SUNW_cap 0x6ffffff5 /* SunOS 5.x hw/sw capabilities */
+
+/* elf type */
+#define ELFDATANONE 0 /* e_ident[EI_DATA] */
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+/* elf class */
+#define ELFCLASSNONE 0
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+
+/* magic number */
+#define EI_MAG0 0 /* e_ident[] indexes */
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_PAD 7
+
+#define ELFMAG0 0x7f /* EI_MAG */
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFMAG "\177ELF"
+
+#define OLFMAG1 'O'
+#define OLFMAG "\177OLF"
+
+typedef struct {
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+} Elf32_Phdr;
+
+typedef struct {
+ Elf64_Word p_type;
+ Elf64_Word p_flags;
+ Elf64_Off p_offset;
+ Elf64_Addr p_vaddr;
+ Elf64_Addr p_paddr;
+ Elf64_Xword p_filesz;
+ Elf64_Xword p_memsz;
+ Elf64_Xword p_align;
+} Elf64_Phdr;
+
+#define PT_NULL 0 /* p_type */
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+#define PT_INTERP 3
+#define PT_NOTE 4
+#define PT_SHLIB 5
+#define PT_PHDR 6
+#define PT_NUM 7
+
+typedef struct {
+ Elf32_Word sh_name;
+ Elf32_Word sh_type;
+ Elf32_Word sh_flags;
+ Elf32_Addr sh_addr;
+ Elf32_Off sh_offset;
+ Elf32_Word sh_size;
+ Elf32_Word sh_link;
+ Elf32_Word sh_info;
+ Elf32_Word sh_addralign;
+ Elf32_Word sh_entsize;
+} Elf32_Shdr;
+
+typedef struct {
+ Elf64_Word sh_name;
+ Elf64_Word sh_type;
+ Elf64_Off sh_flags;
+ Elf64_Addr sh_addr;
+ Elf64_Off sh_offset;
+ Elf64_Off sh_size;
+ Elf64_Word sh_link;
+ Elf64_Word sh_info;
+ Elf64_Off sh_addralign;
+ Elf64_Off sh_entsize;
+} Elf64_Shdr;
+
+#define NT_NETBSD_CORE_PROCINFO 1
+#define NT_NETBSD_CORE_AUXV 2
+
+struct NetBSD_elfcore_procinfo {
+ /* Version 1 fields start here. */
+ uint32_t cpi_version; /* our version */
+ uint32_t cpi_cpisize; /* sizeof(this struct) */
+ uint32_t cpi_signo; /* killing signal */
+ uint32_t cpi_sigcode; /* signal code */
+ uint32_t cpi_sigpend[4]; /* pending signals */
+ uint32_t cpi_sigmask[4]; /* blocked signals */
+ uint32_t cpi_sigignore[4]; /* ignored signals */
+ uint32_t cpi_sigcatch[4]; /* caught signals */
+ int32_t cpi_pid; /* process ID */
+ int32_t cpi_ppid; /* parent process ID */
+ int32_t cpi_pgrp; /* process group ID */
+ int32_t cpi_sid; /* session ID */
+ uint32_t cpi_ruid; /* real user ID */
+ uint32_t cpi_euid; /* effective user ID */
+ uint32_t cpi_svuid; /* saved user ID */
+ uint32_t cpi_rgid; /* real group ID */
+ uint32_t cpi_egid; /* effective group ID */
+ uint32_t cpi_svgid; /* saved group ID */
+ uint32_t cpi_nlwps; /* number of LWPs */
+ int8_t cpi_name[32]; /* copy of p->p_comm */
+ /* Add version 2 fields below here. */
+ int32_t cpi_siglwp; /* LWP target of killing signal */
+};
+
+/* Note header in a PT_NOTE section */
+typedef struct elf_note {
+ Elf32_Word n_namesz; /* Name size */
+ Elf32_Word n_descsz; /* Content size */
+ Elf32_Word n_type; /* Content type */
+} Elf32_Nhdr;
+
+typedef struct {
+ Elf64_Word n_namesz;
+ Elf64_Word n_descsz;
+ Elf64_Word n_type;
+} Elf64_Nhdr;
+
+/* Notes used in ET_CORE */
+#define NT_PRSTATUS 1
+#define NT_PRFPREG 2
+#define NT_PRPSINFO 3
+#define NT_PRXREG 4
+#define NT_TASKSTRUCT 4
+#define NT_PLATFORM 5
+#define NT_AUXV 6
+
+/* Note types used in executables */
+/* NetBSD executables (name = "NetBSD") */
+#define NT_NETBSD_VERSION 1
+#define NT_NETBSD_EMULATION 2
+#define NT_FREEBSD_VERSION 1
+#define NT_OPENBSD_VERSION 1
+#define NT_DRAGONFLY_VERSION 1
+/*
+ * GNU executables (name = "GNU")
+ * word[0]: GNU OS tags
+ * word[1]: major version
+ * word[2]: minor version
+ * word[3]: tiny version
+ */
+#define NT_GNU_VERSION 1
+
+/* GNU OS tags */
+#define GNU_OS_LINUX 0
+#define GNU_OS_HURD 1
+#define GNU_OS_SOLARIS 2
+#define GNU_OS_KFREEBSD 3
+#define GNU_OS_KNETBSD 4
+
+/*
+ * GNU Hardware capability information
+ * word[0]: Number of entries
+ * word[1]: Bitmask of enabled entries
+ * Followed by a byte id, and a NUL terminated string per entry
+ */
+#define NT_GNU_HWCAP 2
+
+/*
+ * GNU Build ID generated by ld
+ * 160 bit SHA1 [default]
+ * 128 bit md5 or uuid
+ */
+#define NT_GNU_BUILD_ID 3
+
+/*
+ * NetBSD-specific note type: PaX.
+ * There should be 1 NOTE per executable.
+ * name: PaX\0
+ * namesz: 4
+ * desc:
+ * word[0]: capability bitmask
+ * descsz: 4
+ */
+#define NT_NETBSD_PAX 3
+#define NT_NETBSD_PAX_MPROTECT 0x01 /* Force enable Mprotect */
+#define NT_NETBSD_PAX_NOMPROTECT 0x02 /* Force disable Mprotect */
+#define NT_NETBSD_PAX_GUARD 0x04 /* Force enable Segvguard */
+#define NT_NETBSD_PAX_NOGUARD 0x08 /* Force disable Servguard */
+#define NT_NETBSD_PAX_ASLR 0x10 /* Force enable ASLR */
+#define NT_NETBSD_PAX_NOASLR 0x20 /* Force disable ASLR */
+
+/*
+ * NetBSD-specific note type: MACHINE_ARCH.
+ * There should be 1 NOTE per executable.
+ * name: NetBSD\0
+ * namesz: 7
+ * desc: string
+ * descsz: variable
+ */
+#define NT_NETBSD_MARCH 5
+
+/*
+ * NetBSD-specific note type: COMPILER MODEL.
+ * There should be 1 NOTE per executable.
+ * name: NetBSD\0
+ * namesz: 7
+ * desc: string
+ * descsz: variable
+ */
+#define NT_NETBSD_CMODEL 6
+
+/*
+ * Golang-specific note type
+ * name: Go\0\0
+ * namesz: 4
+ * desc: base-64 build id.
+ * descsz: < 128
+ */
+#define NT_GO_BUILD_ID 4
+
+/*
+ * FreeBSD specific notes
+ */
+#define NT_FREEBSD_PROCSTAT_AUXV 16
+
+#if !defined(ELFSIZE) && defined(ARCH_ELFSIZE)
+#define ELFSIZE ARCH_ELFSIZE
+#endif
+/* SunOS 5.x hardware/software capabilities */
+typedef struct {
+ Elf32_Word c_tag;
+ union {
+ Elf32_Word c_val;
+ Elf32_Addr c_ptr;
+ } c_un;
+} Elf32_Cap;
+
+typedef struct {
+ Elf64_Xword c_tag;
+ union {
+ Elf64_Xword c_val;
+ Elf64_Addr c_ptr;
+ } c_un;
+} Elf64_Cap;
+
+/* SunOS 5.x hardware/software capability tags */
+#define CA_SUNW_NULL 0
+#define CA_SUNW_HW_1 1
+#define CA_SUNW_SF_1 2
+
+/* SunOS 5.x software capabilities */
+#define SF1_SUNW_FPKNWN 0x01
+#define SF1_SUNW_FPUSED 0x02
+#define SF1_SUNW_MASK 0x03
+
+/* SunOS 5.x hardware capabilities: sparc */
+#define AV_SPARC_MUL32 0x0001
+#define AV_SPARC_DIV32 0x0002
+#define AV_SPARC_FSMULD 0x0004
+#define AV_SPARC_V8PLUS 0x0008
+#define AV_SPARC_POPC 0x0010
+#define AV_SPARC_VIS 0x0020
+#define AV_SPARC_VIS2 0x0040
+#define AV_SPARC_ASI_BLK_INIT 0x0080
+#define AV_SPARC_FMAF 0x0100
+#define AV_SPARC_FJFMAU 0x4000
+#define AV_SPARC_IMA 0x8000
+
+/* SunOS 5.x hardware capabilities: 386 */
+#define AV_386_FPU 0x00000001
+#define AV_386_TSC 0x00000002
+#define AV_386_CX8 0x00000004
+#define AV_386_SEP 0x00000008
+#define AV_386_AMD_SYSC 0x00000010
+#define AV_386_CMOV 0x00000020
+#define AV_386_MMX 0x00000040
+#define AV_386_AMD_MMX 0x00000080
+#define AV_386_AMD_3DNow 0x00000100
+#define AV_386_AMD_3DNowx 0x00000200
+#define AV_386_FXSR 0x00000400
+#define AV_386_SSE 0x00000800
+#define AV_386_SSE2 0x00001000
+#define AV_386_PAUSE 0x00002000
+#define AV_386_SSE3 0x00004000
+#define AV_386_MON 0x00008000
+#define AV_386_CX16 0x00010000
+#define AV_386_AHF 0x00020000
+#define AV_386_TSCP 0x00040000
+#define AV_386_AMD_SSE4A 0x00080000
+#define AV_386_POPCNT 0x00100000
+#define AV_386_AMD_LZCNT 0x00200000
+#define AV_386_SSSE3 0x00400000
+#define AV_386_SSE4_1 0x00800000
+#define AV_386_SSE4_2 0x01000000
+
+/*
+ * Dynamic Section structure array
+ */
+typedef struct {
+ Elf32_Word d_tag; /* entry tag value */
+ union {
+ Elf32_Addr d_ptr;
+ Elf32_Word d_val;
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct {
+ Elf64_Xword d_tag; /* entry tag value */
+ union {
+ Elf64_Addr d_ptr;
+ Elf64_Xword d_val;
+ } d_un;
+} Elf64_Dyn;
+
+/* d_tag */
+#define DT_NULL 0 /* Marks end of dynamic array */
+#define DT_NEEDED 1 /* Name of needed library (DT_STRTAB offset) */
+#define DT_PLTRELSZ 2 /* Size, in bytes, of relocations in PLT */
+#define DT_PLTGOT 3 /* Address of PLT and/or GOT */
+#define DT_HASH 4 /* Address of symbol hash table */
+#define DT_STRTAB 5 /* Address of string table */
+#define DT_SYMTAB 6 /* Address of symbol table */
+#define DT_RELA 7 /* Address of Rela relocation table */
+#define DT_RELASZ 8 /* Size, in bytes, of DT_RELA table */
+#define DT_RELAENT 9 /* Size, in bytes, of one DT_RELA entry */
+#define DT_STRSZ 10 /* Size, in bytes, of DT_STRTAB table */
+#define DT_SYMENT 11 /* Size, in bytes, of one DT_SYMTAB entry */
+#define DT_INIT 12 /* Address of initialization function */
+#define DT_FINI 13 /* Address of termination function */
+#define DT_SONAME 14 /* Shared object name (DT_STRTAB offset) */
+#define DT_RPATH 15 /* Library search path (DT_STRTAB offset) */
+#define DT_SYMBOLIC 16 /* Start symbol search within local object */
+#define DT_REL 17 /* Address of Rel relocation table */
+#define DT_RELSZ 18 /* Size, in bytes, of DT_REL table */
+#define DT_RELENT 19 /* Size, in bytes, of one DT_REL entry */
+#define DT_PLTREL 20 /* Type of PLT relocation entries */
+#define DT_DEBUG 21 /* Used for debugging; unspecified */
+#define DT_TEXTREL 22 /* Relocations might modify non-writable seg */
+#define DT_JMPREL 23 /* Address of relocations associated with PLT */
+#define DT_BIND_NOW 24 /* Process all relocations at load-time */
+#define DT_INIT_ARRAY 25 /* Address of initialization function array */
+#define DT_FINI_ARRAY 26 /* Size, in bytes, of DT_INIT_ARRAY array */
+#define DT_INIT_ARRAYSZ 27 /* Address of termination function array */
+#define DT_FINI_ARRAYSZ 28 /* Size, in bytes, of DT_FINI_ARRAY array*/
+#define DT_RUNPATH 29 /* overrides DT_RPATH */
+#define DT_FLAGS 30 /* Encodes ORIGIN, SYMBOLIC, TEXTREL, BIND_NOW, STATIC_TLS */
+#define DT_ENCODING 31 /* ??? */
+#define DT_PREINIT_ARRAY 32 /* Address of pre-init function array */
+#define DT_PREINIT_ARRAYSZ 33 /* Size, in bytes, of DT_PREINIT_ARRAY array */
+#define DT_NUM 34
+
+#define DT_LOOS 0x60000000 /* Operating system specific range */
+#define DT_VERSYM 0x6ffffff0 /* Symbol versions */
+#define DT_FLAGS_1 0x6ffffffb /* ELF dynamic flags */
+#define DT_VERDEF 0x6ffffffc /* Versions defined by file */
+#define DT_VERDEFNUM 0x6ffffffd /* Number of versions defined by file */
+#define DT_VERNEED 0x6ffffffe /* Versions needed by file */
+#define DT_VERNEEDNUM 0x6fffffff /* Number of versions needed by file */
+#define DT_HIOS 0x6fffffff
+#define DT_LOPROC 0x70000000 /* Processor-specific range */
+#define DT_HIPROC 0x7fffffff
+
+/* Flag values for DT_FLAGS */
+#define DF_ORIGIN 0x00000001 /* uses $ORIGIN */
+#define DF_SYMBOLIC 0x00000002 /* */
+#define DF_TEXTREL 0x00000004 /* */
+#define DF_BIND_NOW 0x00000008 /* */
+#define DF_STATIC_TLS 0x00000010 /* */
+
+/* Flag values for DT_FLAGS_1 */
+#define DF_1_NOW 0x00000001 /* Same as DF_BIND_NOW */
+#define DF_1_GLOBAL 0x00000002 /* Unused */
+#define DF_1_GROUP 0x00000004 /* Is member of group */
+#define DF_1_NODELETE 0x00000008 /* Cannot be deleted from process */
+#define DF_1_LOADFLTR 0x00000010 /* Immediate loading of filters */
+#define DF_1_INITFIRST 0x00000020 /* init/fini takes priority */
+#define DF_1_NOOPEN 0x00000040 /* Do not allow loading on dlopen() */
+#define DF_1_ORIGIN 0x00000080 /* Require $ORIGIN processing */
+#define DF_1_DIRECT 0x00000100 /* Enable direct bindings */
+#define DF_1_INTERPOSE 0x00000400 /* Is an interposer */
+#define DF_1_NODEFLIB 0x00000800 /* Ignore default library search path */
+#define DF_1_NODUMP 0x00001000 /* Cannot be dumped with dldump(3C) */
+#define DF_1_CONFALT 0x00002000 /* Configuration alternative */
+#define DF_1_ENDFILTEE 0x00004000 /* Filtee ends filter's search */
+#define DF_1_DISPRELDNE 0x00008000 /* Did displacement relocation */
+#define DF_1_DISPRELPND 0x00010000 /* Pending displacement relocation */
+#define DF_1_NODIRECT 0x00020000 /* Has non-direct bindings */
+#define DF_1_IGNMULDEF 0x00040000 /* Used internally */
+#define DF_1_NOKSYMS 0x00080000 /* Used internally */
+#define DF_1_NOHDR 0x00100000 /* Used internally */
+#define DF_1_EDITED 0x00200000 /* Has been modified since build */
+#define DF_1_NORELOC 0x00400000 /* Used internally */
+#define DF_1_SYMINTPOSE 0x00800000 /* Has individual symbol interposers */
+#define DF_1_GLOBAUDIT 0x01000000 /* Require global auditing */
+#define DF_1_SINGLETON 0x02000000 /* Has singleton symbols */
+#define DF_1_STUB 0x04000000 /* Stub */
+#define DF_1_PIE 0x08000000 /* Position Independent Executable */
+
+#endif
diff --git a/contrib/libs/libmagic/src/res.cpp b/contrib/libs/libmagic/src/res.cpp
new file mode 100644
index 0000000000..99a5af6ee5
--- /dev/null
+++ b/contrib/libs/libmagic/src/res.cpp
@@ -0,0 +1,7 @@
+#include <library/cpp/resource/resource.h>
+
+extern "C" void _magic_read_res(const char* res, void** out, size_t* size) {
+ TString s;
+ if (NResource::FindExact(res, &s) && (*size = s.size()) && (*out = malloc(*size)))
+ memcpy(*out, s.data(), *size);
+}
diff --git a/contrib/libs/libmagic/src/seccomp.c b/contrib/libs/libmagic/src/seccomp.c
new file mode 100644
index 0000000000..d2ce43c585
--- /dev/null
+++ b/contrib/libs/libmagic/src/seccomp.c
@@ -0,0 +1,290 @@
+/*
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * libseccomp hooks.
+ */
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: seccomp.c,v 1.25 2022/12/26 18:57:29 christos Exp $")
+#endif /* lint */
+
+#if HAVE_LIBSECCOMP
+#error #include <seccomp.h> /* libseccomp */
+#include <sys/prctl.h> /* prctl */
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define DENY_RULE(call) \
+ do \
+ if (seccomp_rule_add (ctx, SCMP_ACT_KILL, SCMP_SYS(call), 0) == -1) \
+ goto out; \
+ while (/*CONSTCOND*/0)
+#define ALLOW_RULE(call) \
+ do \
+ if (seccomp_rule_add (ctx, SCMP_ACT_ALLOW, SCMP_SYS(call), 0) == -1) \
+ goto out; \
+ while (/*CONSTCOND*/0)
+
+#define ALLOW_IOCTL_RULE(param) \
+ do \
+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1, \
+ SCMP_CMP(1, SCMP_CMP_EQ, (scmp_datum_t)param, \
+ (scmp_datum_t)0)) == -1) \
+ goto out; \
+ while (/*CONSTCOND*/0)
+
+static scmp_filter_ctx ctx;
+
+int
+enable_sandbox_basic(void)
+{
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
+ return -1;
+
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1)
+ return -1;
+
+ // initialize the filter
+ ctx = seccomp_init(SCMP_ACT_ALLOW);
+ if (ctx == NULL)
+ return 1;
+
+ DENY_RULE(_sysctl);
+ DENY_RULE(acct);
+ DENY_RULE(add_key);
+ DENY_RULE(adjtimex);
+ DENY_RULE(chroot);
+ DENY_RULE(clock_adjtime);
+ DENY_RULE(create_module);
+ DENY_RULE(delete_module);
+ DENY_RULE(fanotify_init);
+ DENY_RULE(finit_module);
+ DENY_RULE(get_kernel_syms);
+ DENY_RULE(get_mempolicy);
+ DENY_RULE(init_module);
+ DENY_RULE(io_cancel);
+ DENY_RULE(io_destroy);
+ DENY_RULE(io_getevents);
+ DENY_RULE(io_setup);
+ DENY_RULE(io_submit);
+ DENY_RULE(ioperm);
+ DENY_RULE(iopl);
+ DENY_RULE(ioprio_set);
+ DENY_RULE(kcmp);
+#ifdef __NR_kexec_file_load
+ DENY_RULE(kexec_file_load);
+#endif
+ DENY_RULE(kexec_load);
+ DENY_RULE(keyctl);
+ DENY_RULE(lookup_dcookie);
+ DENY_RULE(mbind);
+ DENY_RULE(nfsservctl);
+ DENY_RULE(migrate_pages);
+ DENY_RULE(modify_ldt);
+ DENY_RULE(mount);
+ DENY_RULE(move_pages);
+ DENY_RULE(name_to_handle_at);
+ DENY_RULE(open_by_handle_at);
+ DENY_RULE(perf_event_open);
+ DENY_RULE(pivot_root);
+ DENY_RULE(process_vm_readv);
+ DENY_RULE(process_vm_writev);
+ DENY_RULE(ptrace);
+ DENY_RULE(reboot);
+ DENY_RULE(remap_file_pages);
+ DENY_RULE(request_key);
+ DENY_RULE(set_mempolicy);
+ DENY_RULE(swapoff);
+ DENY_RULE(swapon);
+ DENY_RULE(sysfs);
+ DENY_RULE(syslog);
+ DENY_RULE(tuxcall);
+ DENY_RULE(umount2);
+ DENY_RULE(uselib);
+ DENY_RULE(vmsplice);
+
+ // blocking dangerous syscalls that file should not need
+ DENY_RULE (execve);
+ DENY_RULE (socket);
+ // ...
+
+
+ // applying filter...
+ if (seccomp_load (ctx) == -1)
+ goto out;
+ // free ctx after the filter has been loaded into the kernel
+ seccomp_release(ctx);
+ return 0;
+
+out:
+ seccomp_release(ctx);
+ return -1;
+}
+
+
+int
+enable_sandbox_full(void)
+{
+
+ // prevent child processes from getting more priv e.g. via setuid,
+ // capabilities, ...
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
+ return -1;
+
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1)
+ return -1;
+
+ // initialize the filter
+ ctx = seccomp_init(SCMP_ACT_KILL);
+ if (ctx == NULL)
+ return -1;
+
+ ALLOW_RULE(access);
+ ALLOW_RULE(brk);
+ ALLOW_RULE(close);
+ ALLOW_RULE(dup2);
+ ALLOW_RULE(exit);
+ ALLOW_RULE(exit_group);
+#ifdef __NR_faccessat
+ ALLOW_RULE(faccessat);
+#endif
+ ALLOW_RULE(fcntl);
+ ALLOW_RULE(fcntl64);
+#ifdef __NR_fstat
+ ALLOW_RULE(fstat);
+#endif
+ ALLOW_RULE(fstat64);
+#ifdef __NR_fstatat64
+ ALLOW_RULE(fstatat64);
+#endif
+ ALLOW_RULE(futex);
+ ALLOW_RULE(getdents);
+#ifdef __NR_getdents64
+ ALLOW_RULE(getdents64);
+#endif
+#ifdef FIONREAD
+ // called in src/compress.c under sread
+ ALLOW_IOCTL_RULE(FIONREAD);
+#endif
+#ifdef TIOCGWINSZ
+ // musl libc may call ioctl TIOCGWINSZ on stdout
+ ALLOW_IOCTL_RULE(TIOCGWINSZ);
+#endif
+#ifdef TCGETS
+ // glibc may call ioctl TCGETS on stdout on physical terminal
+ ALLOW_IOCTL_RULE(TCGETS);
+#endif
+ ALLOW_RULE(lseek);
+ ALLOW_RULE(_llseek);
+ ALLOW_RULE(lstat);
+ ALLOW_RULE(lstat64);
+ ALLOW_RULE(madvise);
+ ALLOW_RULE(mmap);
+ ALLOW_RULE(mmap2);
+ ALLOW_RULE(mprotect);
+ ALLOW_RULE(mremap);
+ ALLOW_RULE(munmap);
+#ifdef __NR_newfstatat
+ ALLOW_RULE(newfstatat);
+#endif
+ ALLOW_RULE(open);
+ ALLOW_RULE(openat);
+ ALLOW_RULE(pread64);
+ ALLOW_RULE(read);
+ ALLOW_RULE(readlink);
+#ifdef __NR_readlinkat
+ ALLOW_RULE(readlinkat);
+#endif
+ ALLOW_RULE(rt_sigaction);
+ ALLOW_RULE(rt_sigprocmask);
+ ALLOW_RULE(rt_sigreturn);
+ ALLOW_RULE(select);
+ ALLOW_RULE(stat);
+ ALLOW_RULE(statx);
+ ALLOW_RULE(stat64);
+ ALLOW_RULE(sysinfo);
+ ALLOW_RULE(umask); // Used in file_pipe2file()
+ ALLOW_RULE(getpid); // Used by glibc in file_pipe2file()
+ ALLOW_RULE(unlink);
+ ALLOW_RULE(utimes);
+ ALLOW_RULE(write);
+ ALLOW_RULE(writev);
+
+
+#if 0
+ // needed by valgrind
+ ALLOW_RULE(gettid);
+ ALLOW_RULE(rt_sigtimedwait);
+#endif
+
+#if 0
+ /* special restrictions for socket, only allow AF_UNIX/AF_LOCAL */
+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, AF_UNIX)) == -1)
+ goto out;
+
+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, AF_LOCAL)) == -1)
+ goto out;
+
+
+ /* special restrictions for open, prevent opening files for writing */
+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1,
+ SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY | O_RDWR, 0)) == -1)
+ goto out;
+
+ if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1,
+ SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY)) == -1)
+ goto out;
+
+ if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1,
+ SCMP_CMP(1, SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR)) == -1)
+ goto out;
+
+
+ /* allow stderr */
+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
+ SCMP_CMP(0, SCMP_CMP_EQ, 2)) == -1)
+ goto out;
+#endif
+
+ // applying filter...
+ if (seccomp_load(ctx) == -1)
+ goto out;
+ // free ctx after the filter has been loaded into the kernel
+ seccomp_release(ctx);
+ return 0;
+
+out:
+ // something went wrong
+ seccomp_release(ctx);
+ return -1;
+}
+#endif
diff --git a/contrib/libs/libmagic/src/softmagic.c b/contrib/libs/libmagic/src/softmagic.c
new file mode 100644
index 0000000000..ea466ecd00
--- /dev/null
+++ b/contrib/libs/libmagic/src/softmagic.c
@@ -0,0 +1,2522 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * softmagic - interpret variable magic from MAGIC
+ */
+
+#include "file.h"
+
+#ifndef lint
+FILE_RCSID("@(#)$File: softmagic.c,v 1.345 2023/07/02 12:48:39 christos Exp $")
+#endif /* lint */
+
+#include "magic.h"
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <time.h>
+#include "der.h"
+
+file_private int match(struct magic_set *, struct magic *, file_regex_t **, size_t,
+ const struct buffer *, size_t, int, int, int, uint16_t *,
+ uint16_t *, int *, int *, int *, int *, int *);
+file_private int mget(struct magic_set *, struct magic *, const struct buffer *,
+ const unsigned char *, size_t,
+ size_t, unsigned int, int, int, int, uint16_t *,
+ uint16_t *, int *, int *, int *, int *, int *);
+file_private int msetoffset(struct magic_set *, struct magic *, struct buffer *,
+ const struct buffer *, size_t, unsigned int);
+file_private int magiccheck(struct magic_set *, struct magic *, file_regex_t **);
+file_private int mprint(struct magic_set *, struct magic *);
+file_private int moffset(struct magic_set *, struct magic *, const struct buffer *,
+ int32_t *);
+file_private void mdebug(uint32_t, const char *, size_t);
+file_private int mcopy(struct magic_set *, union VALUETYPE *, int, int,
+ const unsigned char *, uint32_t, size_t, struct magic *);
+file_private int mconvert(struct magic_set *, struct magic *, int);
+file_private int print_sep(struct magic_set *, int);
+file_private int handle_annotation(struct magic_set *, struct magic *, int);
+file_private int cvt_8(union VALUETYPE *, const struct magic *);
+file_private int cvt_16(union VALUETYPE *, const struct magic *);
+file_private int cvt_32(union VALUETYPE *, const struct magic *);
+file_private int cvt_64(union VALUETYPE *, const struct magic *);
+
+#define OFFSET_OOB(n, o, i) ((n) < CAST(uint32_t, (o)) || (i) > ((n) - (o)))
+#define BE64(p) ( \
+ (CAST(uint64_t, (p)->hq[0])<<56)| \
+ (CAST(uint64_t, (p)->hq[1])<<48)| \
+ (CAST(uint64_t, (p)->hq[2])<<40)| \
+ (CAST(uint64_t, (p)->hq[3])<<32)| \
+ (CAST(uint64_t, (p)->hq[4])<<24)| \
+ (CAST(uint64_t, (p)->hq[5])<<16)| \
+ (CAST(uint64_t, (p)->hq[6])<<8)| \
+ (CAST(uint64_t, (p)->hq[7])))
+#define LE64(p) ( \
+ (CAST(uint64_t, (p)->hq[7])<<56)| \
+ (CAST(uint64_t, (p)->hq[6])<<48)| \
+ (CAST(uint64_t, (p)->hq[5])<<40)| \
+ (CAST(uint64_t, (p)->hq[4])<<32)| \
+ (CAST(uint64_t, (p)->hq[3])<<24)| \
+ (CAST(uint64_t, (p)->hq[2])<<16)| \
+ (CAST(uint64_t, (p)->hq[1])<<8)| \
+ (CAST(uint64_t, (p)->hq[0])))
+#define LE32(p) ( \
+ (CAST(uint32_t, (p)->hl[3])<<24)| \
+ (CAST(uint32_t, (p)->hl[2])<<16)| \
+ (CAST(uint32_t, (p)->hl[1])<<8)| \
+ (CAST(uint32_t, (p)->hl[0])))
+#define BE32(p) ( \
+ (CAST(uint32_t, (p)->hl[0])<<24)| \
+ (CAST(uint32_t, (p)->hl[1])<<16)| \
+ (CAST(uint32_t, (p)->hl[2])<<8)| \
+ (CAST(uint32_t, (p)->hl[3])))
+#define ME32(p) ( \
+ (CAST(uint32_t, (p)->hl[1])<<24)| \
+ (CAST(uint32_t, (p)->hl[0])<<16)| \
+ (CAST(uint32_t, (p)->hl[3])<<8)| \
+ (CAST(uint32_t, (p)->hl[2])))
+
+#define BE16(p) ((CAST(uint16_t, (p)->hs[0])<<8)|(CAST(uint16_t, (p)->hs[1])))
+#define LE16(p) ((CAST(uint16_t, (p)->hs[1])<<8)|(CAST(uint16_t, (p)->hs[0])))
+#define SEXT(s,v,p) ((s) ? \
+ CAST(intmax_t, CAST(int##v##_t, p)) : \
+ CAST(intmax_t, CAST(uint##v##_t, p)))
+
+/*
+ * softmagic - lookup one file in parsed, in-memory copy of database
+ * Passed the name and FILE * of one file to be typed.
+ */
+/*ARGSUSED1*/ /* nbytes passed for regularity, maybe need later */
+file_protected int
+file_softmagic(struct magic_set *ms, const struct buffer *b,
+ uint16_t *indir_count, uint16_t *name_count, int mode, int text)
+{
+ struct mlist *ml;
+ int rv = 0, printed_something = 0, need_separator = 0, firstline = 1;
+ uint16_t nc, ic;
+
+ if (name_count == NULL) {
+ nc = 0;
+ name_count = &nc;
+ }
+ if (indir_count == NULL) {
+ ic = 0;
+ indir_count = &ic;
+ }
+
+ for (ml = ms->mlist[0]->next; ml != ms->mlist[0]; ml = ml->next) {
+ int ret = match(ms, ml->magic, ml->magic_rxcomp, ml->nmagic, b,
+ 0, mode, text, 0, indir_count, name_count,
+ &printed_something, &need_separator, &firstline,
+ NULL, NULL);
+ switch (ret) {
+ case -1:
+ return ret;
+ case 0:
+ continue;
+ default:
+ if ((ms->flags & MAGIC_CONTINUE) == 0)
+ return ret;
+ rv = ret;
+ break;
+ }
+ }
+
+ return rv;
+}
+
+#define FILE_FMTDEBUG
+#ifdef FILE_FMTDEBUG
+#define F(a, b, c) file_fmtcheck((a), (b), (c), __FILE__, __LINE__)
+
+file_private const char * __attribute__((__format_arg__(3)))
+file_fmtcheck(struct magic_set *ms, const char *desc, const char *def,
+ const char *file, size_t line)
+{
+ const char *ptr;
+
+ if (strchr(desc, '%') == NULL)
+ return desc;
+
+ ptr = fmtcheck(desc, def);
+ if (ptr == def)
+ file_magerror(ms,
+ "%s, %" SIZE_T_FORMAT "u: format `%s' does not match"
+ " with `%s'", file, line, desc, def);
+ return ptr;
+}
+#else
+#define F(a, b, c) fmtcheck((b), (c))
+#endif
+
+/*
+ * Go through the whole list, stopping if you find a match. Process all
+ * the continuations of that match before returning.
+ *
+ * We support multi-level continuations:
+ *
+ * At any time when processing a successful top-level match, there is a
+ * current continuation level; it represents the level of the last
+ * successfully matched continuation.
+ *
+ * Continuations above that level are skipped as, if we see one, it
+ * means that the continuation that controls them - i.e, the
+ * lower-level continuation preceding them - failed to match.
+ *
+ * Continuations below that level are processed as, if we see one,
+ * it means we've finished processing or skipping higher-level
+ * continuations under the control of a successful or unsuccessful
+ * lower-level continuation, and are now seeing the next lower-level
+ * continuation and should process it. The current continuation
+ * level reverts to the level of the one we're seeing.
+ *
+ * Continuations at the current level are processed as, if we see
+ * one, there's no lower-level continuation that may have failed.
+ *
+ * If a continuation matches, we bump the current continuation level
+ * so that higher-level continuations are processed.
+ */
+file_private int
+match(struct magic_set *ms, struct magic *magic, file_regex_t **magic_rxcomp,
+ size_t nmagic, const struct buffer *b, size_t offset, int mode, int text,
+ int flip, uint16_t *indir_count, uint16_t *name_count,
+ int *printed_something, int *need_separator, int *firstline,
+ int *returnval, int *found_match)
+{
+ uint32_t magindex = 0;
+ unsigned int cont_level = 0;
+ int found_matchv = 0; /* if a match is found it is set to 1*/
+ int returnvalv = 0, e;
+ struct buffer bb;
+ int print = (ms->flags & MAGIC_NODESC) == 0;
+
+ /*
+ * returnval can be 0 if a match is found, but there was no
+ * annotation to be printed.
+ */
+ if (returnval == NULL)
+ returnval = &returnvalv;
+ if (found_match == NULL)
+ found_match = &found_matchv;
+
+ if (file_check_mem(ms, cont_level) == -1)
+ return -1;
+
+ for (magindex = 0; magindex < nmagic; magindex++) {
+ int flush = 0;
+ struct magic *m = &magic[magindex];
+ file_regex_t **m_rxcomp = &magic_rxcomp[magindex];
+
+ if (m->type != FILE_NAME)
+ if ((IS_STRING(m->type) &&
+#define FLT (STRING_BINTEST | STRING_TEXTTEST)
+ ((text && (m->str_flags & FLT) == STRING_BINTEST) ||
+ (!text && (m->str_flags & FLT) == STRING_TEXTTEST))) ||
+ (m->flag & mode) != mode) {
+flush:
+ /* Skip sub-tests */
+ while (magindex < nmagic - 1 &&
+ magic[magindex + 1].cont_level != 0)
+ magindex++;
+ cont_level = 0;
+ continue; /* Skip to next top-level test*/
+ }
+
+ if (msetoffset(ms, m, &bb, b, offset, cont_level) == -1)
+ goto flush;
+ ms->line = m->lineno;
+
+ /* if main entry matches, print it... */
+ switch (mget(ms, m, b, CAST(const unsigned char *, bb.fbuf),
+ bb.flen, offset, cont_level,
+ mode, text, flip, indir_count, name_count,
+ printed_something, need_separator, firstline, returnval,
+ found_match))
+ {
+ case -1:
+ return -1;
+ case 0:
+ flush = m->reln != '!';
+ break;
+ default:
+ if (m->type == FILE_INDIRECT) {
+ *found_match = 1;
+ *returnval = 1;
+ }
+
+ switch (magiccheck(ms, m, m_rxcomp)) {
+ case -1:
+ return -1;
+ case 0:
+ flush++;
+ break;
+ default:
+ flush = 0;
+ break;
+ }
+ break;
+ }
+ if (flush) {
+ /*
+ * main entry didn't match,
+ * flush its continuations
+ */
+ goto flush;
+ }
+
+ if ((e = handle_annotation(ms, m, *firstline)) != 0)
+ {
+ *found_match = 1;
+ *need_separator = 1;
+ *printed_something = 1;
+ *returnval = 1;
+ *firstline = 0;
+ return e;
+ }
+
+ /*
+ * If we are going to print something, we'll need to print
+ * a blank before we print something else.
+ */
+ if (*m->desc) {
+ *found_match = 1;
+ if (print) {
+ *returnval = 1;
+ *need_separator = 1;
+ *printed_something = 1;
+ if (print_sep(ms, *firstline) == -1)
+ return -1;
+ if (mprint(ms, m) == -1)
+ return -1;
+ }
+ }
+
+ switch (moffset(ms, m, &bb, &ms->c.li[cont_level].off)) {
+ case -1:
+ case 0:
+ goto flush;
+ default:
+ break;
+ }
+
+ /* and any continuations that match */
+ if (file_check_mem(ms, ++cont_level) == -1)
+ return -1;
+
+ while (magindex + 1 < nmagic &&
+ magic[magindex + 1].cont_level != 0) {
+ m = &magic[++magindex];
+ m_rxcomp = &magic_rxcomp[magindex];
+ ms->line = m->lineno; /* for messages */
+
+ if (cont_level < m->cont_level)
+ continue;
+ if (cont_level > m->cont_level) {
+ /*
+ * We're at the end of the level
+ * "cont_level" continuations.
+ */
+ cont_level = m->cont_level;
+ }
+ if (msetoffset(ms, m, &bb, b, offset, cont_level) == -1)
+ goto flush;
+ if (m->flag & OFFADD) {
+ if (cont_level == 0) {
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr,
+ "direct *zero*"
+ " cont_level\n");
+ return 0;
+ }
+ ms->offset +=
+ ms->c.li[cont_level - 1].off;
+ }
+
+#ifdef ENABLE_CONDITIONALS
+ if (m->cond == COND_ELSE ||
+ m->cond == COND_ELIF) {
+ if (ms->c.li[cont_level].last_match == 1)
+ continue;
+ }
+#endif
+ switch (mget(ms, m, b, CAST(const unsigned char *,
+ bb.fbuf), bb.flen, offset,
+ cont_level, mode, text, flip, indir_count,
+ name_count, printed_something, need_separator,
+ firstline, returnval, found_match)) {
+ case -1:
+ return -1;
+ case 0:
+ if (m->reln != '!')
+ continue;
+ flush = 1;
+ break;
+ default:
+ if (m->type == FILE_INDIRECT) {
+ *found_match = 1;
+ *returnval = 1;
+ }
+ flush = 0;
+ break;
+ }
+
+ switch (flush ? 1 : magiccheck(ms, m, m_rxcomp)) {
+ case -1:
+ return -1;
+ case 0:
+#ifdef ENABLE_CONDITIONALS
+ ms->c.li[cont_level].last_match = 0;
+#endif
+ break;
+ default:
+#ifdef ENABLE_CONDITIONALS
+ ms->c.li[cont_level].last_match = 1;
+#endif
+ if (m->type == FILE_CLEAR)
+ ms->c.li[cont_level].got_match = 0;
+ else if (ms->c.li[cont_level].got_match) {
+ if (m->type == FILE_DEFAULT)
+ break;
+ } else
+ ms->c.li[cont_level].got_match = 1;
+
+ if ((e = handle_annotation(ms, m, *firstline))
+ != 0) {
+ *found_match = 1;
+ *need_separator = 1;
+ *printed_something = 1;
+ *returnval = 1;
+ return e;
+ }
+ if (*m->desc) {
+ *found_match = 1;
+ }
+ if (print && *m->desc) {
+ *returnval = 1;
+ /*
+ * This continuation matched. Print
+ * its message, with a blank before it
+ * if the previous item printed and
+ * this item isn't empty.
+ */
+ /*
+ * If we are going to print something,
+ * make sure that we have a separator
+ * first.
+ */
+ if (!*printed_something) {
+ *printed_something = 1;
+ if (print_sep(ms, *firstline)
+ == -1)
+ return -1;
+ }
+ /* space if previous printed */
+ if (*need_separator
+ && (m->flag & NOSPACE) == 0) {
+ if (file_printf(ms, " ") == -1)
+ return -1;
+ }
+ if (mprint(ms, m) == -1)
+ return -1;
+ *need_separator = 1;
+ }
+
+ switch (moffset(ms, m, &bb,
+ &ms->c.li[cont_level].off)) {
+ case -1:
+ case 0:
+ cont_level--;
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If we see any continuations
+ * at a higher level,
+ * process them.
+ */
+ if (file_check_mem(ms, ++cont_level) == -1)
+ return -1;
+ break;
+ }
+ }
+ if (*printed_something) {
+ *firstline = 0;
+ }
+ if (*found_match) {
+ if ((ms->flags & MAGIC_CONTINUE) == 0)
+ return *returnval;
+ // So that we print a separator
+ *printed_something = 0;
+ *firstline = 0;
+ }
+ cont_level = 0;
+ }
+ return *returnval;
+}
+
+file_private int
+check_fmt(struct magic_set *ms, const char *fmt)
+{
+ file_regex_t rx;
+ int rc, rv = -1;
+ const char* pat = "%[-0-9\\.]*s";
+
+ if (strchr(fmt, '%') == NULL)
+ return 0;
+
+ rc = file_regcomp(ms, &rx, pat, REG_EXTENDED|REG_NOSUB);
+ if (rc == 0) {
+ rc = file_regexec(ms, &rx, fmt, 0, 0, 0);
+ rv = !rc;
+ }
+ file_regfree(&rx);
+ return rv;
+}
+
+#if !defined(HAVE_STRNDUP) || defined(__aiws__) || defined(_AIX)
+# if defined(__aiws__) || defined(_AIX)
+# define strndup aix_strndup /* aix is broken */
+# endif
+char *strndup(const char *, size_t);
+
+char *
+strndup(const char *str, size_t n)
+{
+ size_t len;
+ char *copy;
+
+ for (len = 0; len < n && str[len]; len++)
+ continue;
+ if ((copy = CAST(char *, malloc(len + 1))) == NULL)
+ return NULL;
+ (void)memcpy(copy, str, len);
+ copy[len] = '\0';
+ return copy;
+}
+#endif /* HAVE_STRNDUP */
+
+static int
+varexpand(struct magic_set *ms, char *buf, size_t len, const char *str)
+{
+ const char *ptr, *sptr, *e, *t, *ee, *et;
+ size_t l;
+
+ for (sptr = str; (ptr = strstr(sptr, "${")) != NULL;) {
+ l = CAST(size_t, ptr - sptr);
+ if (l >= len)
+ return -1;
+ memcpy(buf, sptr, l);
+ buf += l;
+ len -= l;
+ ptr += 2;
+ if (!*ptr || ptr[1] != '?')
+ return -1;
+ for (et = t = ptr + 2; *et && *et != ':'; et++)
+ continue;
+ if (*et != ':')
+ return -1;
+ for (ee = e = et + 1; *ee && *ee != '}'; ee++)
+ continue;
+ if (*ee != '}')
+ return -1;
+ switch (*ptr) {
+ case 'x':
+ if (ms->mode & 0111) {
+ ptr = t;
+ l = et - t;
+ } else {
+ ptr = e;
+ l = ee - e;
+ }
+ break;
+ default:
+ return -1;
+ }
+ if (l >= len)
+ return -1;
+ memcpy(buf, ptr, l);
+ buf += l;
+ len -= l;
+ sptr = ee + 1;
+ }
+
+ l = strlen(sptr);
+ if (l >= len)
+ return -1;
+
+ memcpy(buf, sptr, l);
+ buf[l] = '\0';
+ return 0;
+}
+
+
+file_private int
+mprint(struct magic_set *ms, struct magic *m)
+{
+ uint64_t v;
+ float vf;
+ double vd;
+ char buf[128], tbuf[26], sbuf[512], ebuf[512];
+ const char *desc;
+ union VALUETYPE *p = &ms->ms_value;
+
+ if (varexpand(ms, ebuf, sizeof(ebuf), m->desc) == -1)
+ desc = m->desc;
+ else
+ desc = ebuf;
+
+#define PRINTER(value, format, stype, utype) \
+ v = file_signextend(ms, m, CAST(uint64_t, value)); \
+ switch (check_fmt(ms, desc)) { \
+ case -1: \
+ return -1; \
+ case 1: \
+ if (m->flag & UNSIGNED) { \
+ (void)snprintf(buf, sizeof(buf), "%" format "u", \
+ CAST(utype, v)); \
+ } else { \
+ (void)snprintf(buf, sizeof(buf), "%" format "d", \
+ CAST(stype, v)); \
+ } \
+ if (file_printf(ms, F(ms, desc, "%s"), buf) == -1) \
+ return -1; \
+ break; \
+ default: \
+ if (m->flag & UNSIGNED) { \
+ if (file_printf(ms, F(ms, desc, "%" format "u"), \
+ CAST(utype, v)) == -1) \
+ return -1; \
+ } else { \
+ if (file_printf(ms, F(ms, desc, "%" format "d"), \
+ CAST(stype, v)) == -1) \
+ return -1; \
+ } \
+ break; \
+ } \
+ break
+
+ switch (m->type) {
+ case FILE_BYTE:
+ PRINTER(p->b, "", int8_t, uint8_t);
+
+ case FILE_SHORT:
+ case FILE_BESHORT:
+ case FILE_LESHORT:
+ PRINTER(p->h, "", int16_t, uint16_t);
+
+ case FILE_LONG:
+ case FILE_BELONG:
+ case FILE_LELONG:
+ case FILE_MELONG:
+ PRINTER(p->l, "", int32_t, uint32_t);
+
+ case FILE_QUAD:
+ case FILE_BEQUAD:
+ case FILE_LEQUAD:
+ case FILE_OFFSET:
+ PRINTER(p->q, INT64_T_FORMAT, long long, unsigned long long);
+
+ case FILE_STRING:
+ case FILE_PSTRING:
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ if (m->reln == '=' || m->reln == '!') {
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_printable(ms, sbuf, sizeof(sbuf), m->value.s,
+ sizeof(m->value.s))) == -1)
+ return -1;
+ }
+ else {
+ char *str = p->s;
+
+ /* compute t before we mangle the string? */
+
+ if (*m->value.s == '\0')
+ str[strcspn(str, "\r\n")] = '\0';
+
+ if (m->str_flags & STRING_TRIM)
+ str = file_strtrim(str);
+
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_printable(ms, sbuf, sizeof(sbuf), str,
+ sizeof(p->s) - (str - p->s))) == -1)
+ return -1;
+
+ if (m->type == FILE_PSTRING) {
+ size_t l = file_pstring_length_size(ms, m);
+ if (l == FILE_BADSIZE)
+ return -1;
+ }
+ }
+ break;
+
+ case FILE_DATE:
+ case FILE_BEDATE:
+ case FILE_LEDATE:
+ case FILE_MEDATE:
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_fmtdatetime(tbuf, sizeof(tbuf), p->l, 0)) == -1)
+ return -1;
+ break;
+
+ case FILE_LDATE:
+ case FILE_BELDATE:
+ case FILE_LELDATE:
+ case FILE_MELDATE:
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_fmtdatetime(tbuf, sizeof(tbuf), p->l, FILE_T_LOCAL))
+ == -1)
+ return -1;
+ break;
+
+ case FILE_QDATE:
+ case FILE_BEQDATE:
+ case FILE_LEQDATE:
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_fmtdatetime(tbuf, sizeof(tbuf), p->q, 0)) == -1)
+ return -1;
+ break;
+
+ case FILE_QLDATE:
+ case FILE_BEQLDATE:
+ case FILE_LEQLDATE:
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_fmtdatetime(tbuf, sizeof(tbuf), p->q, FILE_T_LOCAL)) == -1)
+ return -1;
+ break;
+
+ case FILE_QWDATE:
+ case FILE_BEQWDATE:
+ case FILE_LEQWDATE:
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_fmtdatetime(tbuf, sizeof(tbuf), p->q, FILE_T_WINDOWS))
+ == -1)
+ return -1;
+ break;
+
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ vf = p->f;
+ switch (check_fmt(ms, desc)) {
+ case -1:
+ return -1;
+ case 1:
+ (void)snprintf(buf, sizeof(buf), "%g", vf);
+ if (file_printf(ms, F(ms, desc, "%s"), buf) == -1)
+ return -1;
+ break;
+ default:
+ if (file_printf(ms, F(ms, desc, "%g"), vf) == -1)
+ return -1;
+ break;
+ }
+ break;
+
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ vd = p->d;
+ switch (check_fmt(ms, desc)) {
+ case -1:
+ return -1;
+ case 1:
+ (void)snprintf(buf, sizeof(buf), "%g", vd);
+ if (file_printf(ms, F(ms, desc, "%s"), buf) == -1)
+ return -1;
+ break;
+ default:
+ if (file_printf(ms, F(ms, desc, "%g"), vd) == -1)
+ return -1;
+ break;
+ }
+ break;
+
+ case FILE_SEARCH:
+ case FILE_REGEX: {
+ char *cp, *scp;
+ int rval;
+
+ cp = strndup(RCAST(const char *, ms->search.s),
+ ms->search.rm_len);
+ if (cp == NULL) {
+ file_oomem(ms, ms->search.rm_len);
+ return -1;
+ }
+ scp = (m->str_flags & STRING_TRIM) ? file_strtrim(cp) : cp;
+
+ rval = file_printf(ms, F(ms, desc, "%s"), file_printable(ms,
+ sbuf, sizeof(sbuf), scp, ms->search.rm_len));
+ free(cp);
+
+ if (rval == -1)
+ return -1;
+ break;
+ }
+
+ case FILE_DEFAULT:
+ case FILE_CLEAR:
+ if (file_printf(ms, "%s", m->desc) == -1)
+ return -1;
+ break;
+
+ case FILE_INDIRECT:
+ case FILE_USE:
+ case FILE_NAME:
+ break;
+ case FILE_DER:
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_printable(ms, sbuf, sizeof(sbuf), ms->ms_value.s,
+ sizeof(ms->ms_value.s))) == -1)
+ return -1;
+ break;
+ case FILE_GUID:
+ (void) file_print_guid(buf, sizeof(buf), ms->ms_value.guid);
+ if (file_printf(ms, F(ms, desc, "%s"), buf) == -1)
+ return -1;
+ break;
+ case FILE_MSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_fmtdate(tbuf, sizeof(tbuf), p->h)) == -1)
+ return -1;
+ break;
+ case FILE_MSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ if (file_printf(ms, F(ms, desc, "%s"),
+ file_fmttime(tbuf, sizeof(tbuf), p->h)) == -1)
+ return -1;
+ break;
+ case FILE_OCTAL:
+ file_fmtnum(buf, sizeof(buf), m->value.s, 8);
+ if (file_printf(ms, F(ms, desc, "%s"), buf) == -1)
+ return -1;
+ break;
+ default:
+ file_magerror(ms, "invalid m->type (%d) in mprint()", m->type);
+ return -1;
+ }
+ return 0;
+}
+
+file_private int
+moffset(struct magic_set *ms, struct magic *m, const struct buffer *b,
+ int32_t *op)
+{
+ size_t nbytes = b->flen;
+ int32_t o;
+
+ switch (m->type) {
+ case FILE_BYTE:
+ o = CAST(int32_t, (ms->offset + sizeof(char)));
+ break;
+
+ case FILE_SHORT:
+ case FILE_BESHORT:
+ case FILE_LESHORT:
+ case FILE_MSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_MSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ o = CAST(int32_t, (ms->offset + sizeof(short)));
+ break;
+
+ case FILE_LONG:
+ case FILE_BELONG:
+ case FILE_LELONG:
+ case FILE_MELONG:
+ o = CAST(int32_t, (ms->offset + sizeof(int32_t)));
+ break;
+
+ case FILE_QUAD:
+ case FILE_BEQUAD:
+ case FILE_LEQUAD:
+ o = CAST(int32_t, (ms->offset + sizeof(int64_t)));
+ break;
+
+ case FILE_STRING:
+ case FILE_PSTRING:
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ case FILE_OCTAL:
+ if (m->reln == '=' || m->reln == '!') {
+ o = ms->offset + m->vallen;
+ } else {
+ union VALUETYPE *p = &ms->ms_value;
+
+ if (*m->value.s == '\0')
+ p->s[strcspn(p->s, "\r\n")] = '\0';
+ o = CAST(uint32_t, (ms->offset + strlen(p->s)));
+ if (m->type == FILE_PSTRING) {
+ size_t l = file_pstring_length_size(ms, m);
+ if (l == FILE_BADSIZE)
+ return -1;
+ o += CAST(uint32_t, l);
+ }
+ }
+ break;
+
+ case FILE_DATE:
+ case FILE_BEDATE:
+ case FILE_LEDATE:
+ case FILE_MEDATE:
+ o = CAST(int32_t, (ms->offset + sizeof(uint32_t)));
+ break;
+
+ case FILE_LDATE:
+ case FILE_BELDATE:
+ case FILE_LELDATE:
+ case FILE_MELDATE:
+ o = CAST(int32_t, (ms->offset + sizeof(uint32_t)));
+ break;
+
+ case FILE_QDATE:
+ case FILE_BEQDATE:
+ case FILE_LEQDATE:
+ o = CAST(int32_t, (ms->offset + sizeof(uint64_t)));
+ break;
+
+ case FILE_QLDATE:
+ case FILE_BEQLDATE:
+ case FILE_LEQLDATE:
+ o = CAST(int32_t, (ms->offset + sizeof(uint64_t)));
+ break;
+
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ o = CAST(int32_t, (ms->offset + sizeof(float)));
+ break;
+
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ o = CAST(int32_t, (ms->offset + sizeof(double)));
+ break;
+
+ case FILE_REGEX:
+ if ((m->str_flags & REGEX_OFFSET_START) != 0)
+ o = CAST(int32_t, ms->search.offset);
+ else
+ o = CAST(int32_t,
+ (ms->search.offset + ms->search.rm_len));
+ break;
+
+ case FILE_SEARCH:
+ if ((m->str_flags & REGEX_OFFSET_START) != 0)
+ o = CAST(int32_t, ms->search.offset);
+ else
+ o = CAST(int32_t, (ms->search.offset + m->vallen));
+ break;
+
+ case FILE_CLEAR:
+ case FILE_DEFAULT:
+ case FILE_INDIRECT:
+ case FILE_OFFSET:
+ case FILE_USE:
+ o = ms->offset;
+ break;
+
+ case FILE_DER:
+ o = der_offs(ms, m, nbytes);
+ if (o == -1 || CAST(size_t, o) > nbytes) {
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ (void)fprintf(stderr,
+ "Bad DER offset %d nbytes=%"
+ SIZE_T_FORMAT "u", o, nbytes);
+ }
+ *op = 0;
+ return 0;
+ }
+ break;
+
+ case FILE_GUID:
+ o = CAST(int32_t, (ms->offset + 2 * sizeof(uint64_t)));
+ break;
+
+ default:
+ o = 0;
+ break;
+ }
+
+ if (CAST(size_t, o) > nbytes) {
+#if 0
+ file_error(ms, 0, "Offset out of range %" SIZE_T_FORMAT
+ "u > %" SIZE_T_FORMAT "u", (size_t)o, nbytes);
+#endif
+ return -1;
+ }
+ *op = o;
+ return 1;
+}
+
+file_private uint32_t
+cvt_id3(struct magic_set *ms, uint32_t v)
+{
+ v = ((((v >> 0) & 0x7f) << 0) |
+ (((v >> 8) & 0x7f) << 7) |
+ (((v >> 16) & 0x7f) << 14) |
+ (((v >> 24) & 0x7f) << 21));
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "id3 offs=%u\n", v);
+ return v;
+}
+
+file_private int
+cvt_flip(int type, int flip)
+{
+ if (flip == 0)
+ return type;
+ switch (type) {
+ case FILE_BESHORT:
+ return FILE_LESHORT;
+ case FILE_BELONG:
+ return FILE_LELONG;
+ case FILE_BEDATE:
+ return FILE_LEDATE;
+ case FILE_BELDATE:
+ return FILE_LELDATE;
+ case FILE_BEQUAD:
+ return FILE_LEQUAD;
+ case FILE_BEQDATE:
+ return FILE_LEQDATE;
+ case FILE_BEQLDATE:
+ return FILE_LEQLDATE;
+ case FILE_BEQWDATE:
+ return FILE_LEQWDATE;
+ case FILE_LESHORT:
+ return FILE_BESHORT;
+ case FILE_LELONG:
+ return FILE_BELONG;
+ case FILE_LEDATE:
+ return FILE_BEDATE;
+ case FILE_LELDATE:
+ return FILE_BELDATE;
+ case FILE_LEQUAD:
+ return FILE_BEQUAD;
+ case FILE_LEQDATE:
+ return FILE_BEQDATE;
+ case FILE_LEQLDATE:
+ return FILE_BEQLDATE;
+ case FILE_LEQWDATE:
+ return FILE_BEQWDATE;
+ case FILE_BEFLOAT:
+ return FILE_LEFLOAT;
+ case FILE_LEFLOAT:
+ return FILE_BEFLOAT;
+ case FILE_BEDOUBLE:
+ return FILE_LEDOUBLE;
+ case FILE_LEDOUBLE:
+ return FILE_BEDOUBLE;
+ default:
+ return type;
+ }
+}
+#define DO_CVT(fld, type) \
+ if (m->num_mask) \
+ switch (m->mask_op & FILE_OPS_MASK) { \
+ case FILE_OPAND: \
+ p->fld &= CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPOR: \
+ p->fld |= CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPXOR: \
+ p->fld ^= CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPADD: \
+ p->fld += CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPMINUS: \
+ p->fld -= CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPMULTIPLY: \
+ p->fld *= CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPDIVIDE: \
+ if (CAST(type, m->num_mask) == 0) \
+ return -1; \
+ p->fld /= CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPMODULO: \
+ if (CAST(type, m->num_mask) == 0) \
+ return -1; \
+ p->fld %= CAST(type, m->num_mask); \
+ break; \
+ } \
+ if (m->mask_op & FILE_OPINVERSE) \
+ p->fld = ~p->fld \
+
+file_private int
+cvt_8(union VALUETYPE *p, const struct magic *m)
+{
+ DO_CVT(b, uint8_t);
+ return 0;
+}
+
+file_private int
+cvt_16(union VALUETYPE *p, const struct magic *m)
+{
+ DO_CVT(h, uint16_t);
+ return 0;
+}
+
+file_private int
+cvt_32(union VALUETYPE *p, const struct magic *m)
+{
+ DO_CVT(l, uint32_t);
+ return 0;
+}
+
+file_private int
+cvt_64(union VALUETYPE *p, const struct magic *m)
+{
+ DO_CVT(q, uint64_t);
+ return 0;
+}
+
+#define DO_CVT2(fld, type) \
+ if (m->num_mask) \
+ switch (m->mask_op & FILE_OPS_MASK) { \
+ case FILE_OPADD: \
+ p->fld += CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPMINUS: \
+ p->fld -= CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPMULTIPLY: \
+ p->fld *= CAST(type, m->num_mask); \
+ break; \
+ case FILE_OPDIVIDE: \
+ if (CAST(type, m->num_mask) == 0) \
+ return -1; \
+ p->fld /= CAST(type, m->num_mask); \
+ break; \
+ } \
+
+file_private int
+cvt_float(union VALUETYPE *p, const struct magic *m)
+{
+ DO_CVT2(f, float);
+ return 0;
+}
+
+file_private int
+cvt_double(union VALUETYPE *p, const struct magic *m)
+{
+ DO_CVT2(d, double);
+ return 0;
+}
+
+/*
+ * Convert the byte order of the data we are looking at
+ * While we're here, let's apply the mask operation
+ * (unless you have a better idea)
+ */
+file_private int
+mconvert(struct magic_set *ms, struct magic *m, int flip)
+{
+ union VALUETYPE *p = &ms->ms_value;
+
+ switch (cvt_flip(m->type, flip)) {
+ case FILE_BYTE:
+ if (cvt_8(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_SHORT:
+ case FILE_MSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_MSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ if (cvt_16(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_LONG:
+ case FILE_DATE:
+ case FILE_LDATE:
+ if (cvt_32(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_QUAD:
+ case FILE_QDATE:
+ case FILE_QLDATE:
+ case FILE_QWDATE:
+ case FILE_OFFSET:
+ if (cvt_64(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_STRING:
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ case FILE_OCTAL: {
+ /* Null terminate and eat *trailing* return */
+ p->s[sizeof(p->s) - 1] = '\0';
+ return 1;
+ }
+ case FILE_PSTRING: {
+ char *ptr1, *ptr2;
+ size_t len, sz = file_pstring_length_size(ms, m);
+ if (sz == FILE_BADSIZE)
+ return 0;
+ ptr1 = p->s;
+ ptr2 = ptr1 + sz;
+ len = file_pstring_get_length(ms, m, ptr1);
+ if (len == FILE_BADSIZE)
+ return 0;
+ sz = sizeof(p->s) - sz; /* maximum length of string */
+ if (len >= sz) {
+ /*
+ * The size of the pascal string length (sz)
+ * is 1, 2, or 4. We need at least 1 byte for NUL
+ * termination, but we've already truncated the
+ * string by p->s, so we need to deduct sz.
+ * Because we can use one of the bytes of the length
+ * after we shifted as NUL termination.
+ */
+ len = sz;
+ }
+ while (len--)
+ *ptr1++ = *ptr2++;
+ *ptr1 = '\0';
+ return 1;
+ }
+ case FILE_BESHORT:
+ p->h = CAST(short, BE16(p));
+ if (cvt_16(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_BELONG:
+ case FILE_BEDATE:
+ case FILE_BELDATE:
+ p->l = CAST(int32_t, BE32(p));
+ if (cvt_32(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_BEQUAD:
+ case FILE_BEQDATE:
+ case FILE_BEQLDATE:
+ case FILE_BEQWDATE:
+ p->q = CAST(uint64_t, BE64(p));
+ if (cvt_64(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_LESHORT:
+ p->h = CAST(short, LE16(p));
+ if (cvt_16(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_LELONG:
+ case FILE_LEDATE:
+ case FILE_LELDATE:
+ p->l = CAST(int32_t, LE32(p));
+ if (cvt_32(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_LEQUAD:
+ case FILE_LEQDATE:
+ case FILE_LEQLDATE:
+ case FILE_LEQWDATE:
+ p->q = CAST(uint64_t, LE64(p));
+ if (cvt_64(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_MELONG:
+ case FILE_MEDATE:
+ case FILE_MELDATE:
+ p->l = CAST(int32_t, ME32(p));
+ if (cvt_32(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_FLOAT:
+ if (cvt_float(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_BEFLOAT:
+ p->l = BE32(p);
+ if (cvt_float(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_LEFLOAT:
+ p->l = LE32(p);
+ if (cvt_float(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_DOUBLE:
+ if (cvt_double(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_BEDOUBLE:
+ p->q = BE64(p);
+ if (cvt_double(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_LEDOUBLE:
+ p->q = LE64(p);
+ if (cvt_double(p, m) == -1)
+ goto out;
+ return 1;
+ case FILE_REGEX:
+ case FILE_SEARCH:
+ case FILE_DEFAULT:
+ case FILE_CLEAR:
+ case FILE_NAME:
+ case FILE_USE:
+ case FILE_DER:
+ case FILE_GUID:
+ return 1;
+ default:
+ file_magerror(ms, "invalid type %d in mconvert()", m->type);
+ return 0;
+ }
+out:
+ file_magerror(ms, "zerodivide in mconvert()");
+ return 0;
+}
+
+
+file_private void
+mdebug(uint32_t offset, const char *str, size_t len)
+{
+ (void) fprintf(stderr, "mget/%" SIZE_T_FORMAT "u @%d: ", len, offset);
+ file_showstr(stderr, str, len);
+ (void) fputc('\n', stderr);
+ (void) fputc('\n', stderr);
+}
+
+file_private int
+mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir,
+ const unsigned char *s, uint32_t offset, size_t nbytes, struct magic *m)
+{
+ size_t size = sizeof(*p);
+ /*
+ * Note: FILE_SEARCH and FILE_REGEX do not actually copy
+ * anything, but setup pointers into the source
+ */
+ if (indir == 0) {
+ switch (type) {
+ case FILE_DER:
+ case FILE_SEARCH:
+ if (offset > nbytes)
+ offset = CAST(uint32_t, nbytes);
+ ms->search.s = RCAST(const char *, s) + offset;
+ ms->search.s_len = nbytes - offset;
+ ms->search.offset = offset;
+ return 0;
+
+ case FILE_REGEX: {
+ const char *b;
+ const char *c;
+ const char *last; /* end of search region */
+ const char *buf; /* start of search region */
+ const char *end;
+ size_t lines, linecnt, bytecnt;
+
+ if (s == NULL || nbytes < offset) {
+ ms->search.s_len = 0;
+ ms->search.s = NULL;
+ return 0;
+ }
+
+ if (m->str_flags & REGEX_LINE_COUNT) {
+ linecnt = m->str_range;
+ bytecnt = linecnt * 80;
+ } else {
+ linecnt = 0;
+ bytecnt = m->str_range;
+ }
+
+ if (bytecnt == 0 || bytecnt > nbytes - offset)
+ bytecnt = nbytes - offset;
+ if (bytecnt > ms->regex_max)
+ bytecnt = ms->regex_max;
+
+ buf = RCAST(const char *, s) + offset;
+ end = last = RCAST(const char *, s) + bytecnt + offset;
+ /* mget() guarantees buf <= last */
+ for (lines = linecnt, b = buf; lines && b < end &&
+ ((b = CAST(const char *,
+ memchr(c = b, '\n', CAST(size_t, (end - b)))))
+ || (b = CAST(const char *,
+ memchr(c, '\r', CAST(size_t, (end - c))))));
+ lines--, b++) {
+ if (b < end - 1 && b[0] == '\r' && b[1] == '\n')
+ b++;
+ if (b < end - 1 && b[0] == '\n')
+ b++;
+ last = b;
+ }
+ if (lines)
+ last = end;
+
+ ms->search.s = buf;
+ ms->search.s_len = last - buf;
+ ms->search.offset = offset;
+ ms->search.rm_len = 0;
+ return 0;
+ }
+ case FILE_BESTRING16:
+ case FILE_LESTRING16: {
+ const unsigned char *src = s + offset;
+ const unsigned char *esrc = s + nbytes;
+ char *dst = p->s;
+ char *edst = &p->s[sizeof(p->s) - 1];
+
+ if (type == FILE_BESTRING16)
+ src++;
+
+ /* check that offset is within range */
+ if (offset >= nbytes)
+ break;
+ for (/*EMPTY*/; src < esrc; src += 2, dst++) {
+ if (dst < edst)
+ *dst = *src;
+ else
+ break;
+ if (*dst == '\0') {
+ if (type == FILE_BESTRING16 ?
+ *(src - 1) != '\0' :
+ ((src + 1 < esrc) &&
+ *(src + 1) != '\0'))
+ *dst = ' ';
+ }
+ }
+ *edst = '\0';
+ return 0;
+ }
+ case FILE_STRING: /* XXX - these two should not need */
+ case FILE_PSTRING: /* to copy anything, but do anyway. */
+ if (m->str_range != 0 && m->str_range < sizeof(*p))
+ size = m->str_range;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (type == FILE_OFFSET) {
+ (void)memset(p, '\0', sizeof(*p));
+ p->q = offset;
+ return 0;
+ }
+
+ if (offset >= nbytes) {
+ (void)memset(p, '\0', sizeof(*p));
+ return 0;
+ }
+ if (nbytes - offset < size)
+ nbytes = nbytes - offset;
+ else
+ nbytes = size;
+
+ (void)memcpy(p, s + offset, nbytes);
+
+ /*
+ * the usefulness of padding with zeroes eludes me, it
+ * might even cause problems
+ */
+ if (nbytes < sizeof(*p))
+ (void)memset(RCAST(char *, RCAST(void *, p)) + nbytes, '\0',
+ sizeof(*p) - nbytes);
+ return 0;
+}
+
+file_private int
+do_ops(struct magic_set *ms, struct magic *m, uint32_t *rv, intmax_t lhs,
+ intmax_t off)
+{
+ intmax_t offset;
+ // On purpose not INTMAX_MAX
+ if (lhs >= UINT_MAX || lhs <= INT_MIN ||
+ off >= UINT_MAX || off <= INT_MIN) {
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "lhs/off overflow %jd %jd\n", lhs, off);
+ return 1;
+ }
+
+ if (off) {
+ switch (m->in_op & FILE_OPS_MASK) {
+ case FILE_OPAND:
+ offset = lhs & off;
+ break;
+ case FILE_OPOR:
+ offset = lhs | off;
+ break;
+ case FILE_OPXOR:
+ offset = lhs ^ off;
+ break;
+ case FILE_OPADD:
+ offset = lhs + off;
+ break;
+ case FILE_OPMINUS:
+ offset = lhs - off;
+ break;
+ case FILE_OPMULTIPLY:
+ offset = lhs * off;
+ break;
+ case FILE_OPDIVIDE:
+ offset = lhs / off;
+ break;
+ case FILE_OPMODULO:
+ offset = lhs % off;
+ break;
+ }
+ } else
+ offset = lhs;
+ if (m->in_op & FILE_OPINVERSE)
+ offset = ~offset;
+ if (offset >= UINT_MAX) {
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "offset overflow %jd\n", offset);
+ return 1;
+ }
+ *rv = CAST(uint32_t, offset);
+ return 0;
+}
+
+file_private int
+msetoffset(struct magic_set *ms, struct magic *m, struct buffer *bb,
+ const struct buffer *b, size_t o, unsigned int cont_level)
+{
+ int32_t offset;
+ if (m->flag & OFFNEGATIVE) {
+ offset = -m->offset;
+ if (cont_level > 0) {
+ if (m->flag & (OFFADD|INDIROFFADD))
+ goto normal;
+#if 0
+ file_error(ms, 0, "negative offset %d at continuation"
+ "level %u", m->offset, cont_level);
+ return -1;
+#endif
+ }
+ if (buffer_fill(b) == -1)
+ return -1;
+ if (o != 0) {
+ // Not yet!
+ file_magerror(ms, "non zero offset %" SIZE_T_FORMAT
+ "u at level %u", o, cont_level);
+ return -1;
+ }
+ if (CAST(size_t, m->offset) > b->elen)
+ return -1;
+ buffer_init(bb, -1, NULL, b->ebuf, b->elen);
+ ms->eoffset = ms->offset = CAST(int32_t, b->elen - m->offset);
+ } else {
+ offset = m->offset;
+ if (cont_level == 0) {
+normal:
+ // XXX: Pass real fd, then who frees bb?
+ buffer_init(bb, -1, NULL, b->fbuf, b->flen);
+ ms->offset = offset;
+ ms->eoffset = 0;
+ } else {
+ ms->offset = ms->eoffset + offset;
+ }
+ }
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ fprintf(stderr, "bb=[%p,%" SIZE_T_FORMAT "u,%"
+ SIZE_T_FORMAT "u], %d [b=%p,%"
+ SIZE_T_FORMAT "u,%" SIZE_T_FORMAT "u], [o=%#x, c=%d]\n",
+ bb->fbuf, bb->flen, bb->elen, ms->offset, b->fbuf,
+ b->flen, b->elen, offset, cont_level);
+ }
+ return 0;
+}
+
+file_private int
+save_cont(struct magic_set *ms, struct cont *c)
+{
+ size_t len;
+ *c = ms->c;
+ len = c->len * sizeof(*c->li);
+ ms->c.li = CAST(struct level_info *, malloc(len));
+ if (ms->c.li == NULL) {
+ ms->c = *c;
+ return -1;
+ }
+ memcpy(ms->c.li, c->li, len);
+ return 0;
+}
+
+file_private void
+restore_cont(struct magic_set *ms, struct cont *c)
+{
+ free(ms->c.li);
+ ms->c = *c;
+}
+
+file_private int
+mget(struct magic_set *ms, struct magic *m, const struct buffer *b,
+ const unsigned char *s, size_t nbytes, size_t o, unsigned int cont_level,
+ int mode, int text, int flip, uint16_t *indir_count, uint16_t *name_count,
+ int *printed_something, int *need_separator, int *firstline, int *returnval,
+ int *found_match)
+{
+ uint32_t eoffset, offset = ms->offset;
+ struct buffer bb;
+ intmax_t lhs;
+ file_pushbuf_t *pb;
+ int rv, oneed_separator, in_type, nfound_match;
+ char *rbuf;
+ union VALUETYPE *p = &ms->ms_value;
+ struct mlist ml, *mlp;
+ struct cont c;
+
+ if (*indir_count >= ms->indir_max) {
+ file_error(ms, 0, "indirect count (%hu) exceeded",
+ *indir_count);
+ return -1;
+ }
+
+ if (*name_count >= ms->name_max) {
+ file_error(ms, 0, "name use count (%hu) exceeded",
+ *name_count);
+ return -1;
+ }
+
+
+
+ if (mcopy(ms, p, m->type, m->flag & INDIR, s,
+ CAST(uint32_t, offset + o), CAST(uint32_t, nbytes), m) == -1)
+ return -1;
+
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ fprintf(stderr, "mget(type=%d, flag=%#x, offset=%u, o=%"
+ SIZE_T_FORMAT "u, " "nbytes=%" SIZE_T_FORMAT
+ "u, il=%hu, nc=%hu)\n",
+ m->type, m->flag, offset, o, nbytes,
+ *indir_count, *name_count);
+ mdebug(offset, RCAST(char *, RCAST(void *, p)),
+ sizeof(union VALUETYPE));
+#ifndef COMPILE_ONLY
+ file_mdump(m);
+#endif
+ }
+
+ if (m->flag & INDIR) {
+ intmax_t off = m->in_offset;
+ const int sgn = m->in_op & FILE_OPSIGNED;
+ if (m->in_op & FILE_OPINDIRECT) {
+ const union VALUETYPE *q = CAST(const union VALUETYPE *,
+ RCAST(const void *, s + offset + off));
+ int op;
+ switch (op = cvt_flip(m->in_type, flip)) {
+ case FILE_BYTE:
+ if (OFFSET_OOB(nbytes, offset + off, 1))
+ return 0;
+ off = SEXT(sgn,8,q->b);
+ break;
+ case FILE_SHORT:
+ if (OFFSET_OOB(nbytes, offset + off, 2))
+ return 0;
+ off = SEXT(sgn,16,q->h);
+ break;
+ case FILE_BESHORT:
+ if (OFFSET_OOB(nbytes, offset + off, 2))
+ return 0;
+ off = SEXT(sgn,16,BE16(q));
+ break;
+ case FILE_LESHORT:
+ if (OFFSET_OOB(nbytes, offset + off, 2))
+ return 0;
+ off = SEXT(sgn,16,LE16(q));
+ break;
+ case FILE_LONG:
+ if (OFFSET_OOB(nbytes, offset + off, 4))
+ return 0;
+ off = SEXT(sgn,32,q->l);
+ break;
+ case FILE_BELONG:
+ case FILE_BEID3:
+ if (OFFSET_OOB(nbytes, offset + off, 4))
+ return 0;
+ off = SEXT(sgn,32,BE32(q));
+ break;
+ case FILE_LEID3:
+ case FILE_LELONG:
+ if (OFFSET_OOB(nbytes, offset + off, 4))
+ return 0;
+ off = SEXT(sgn,32,LE32(q));
+ break;
+ case FILE_MELONG:
+ if (OFFSET_OOB(nbytes, offset + off, 4))
+ return 0;
+ off = SEXT(sgn,32,ME32(q));
+ break;
+ case FILE_BEQUAD:
+ if (OFFSET_OOB(nbytes, offset + off, 8))
+ return 0;
+ off = SEXT(sgn,64,BE64(q));
+ break;
+ case FILE_LEQUAD:
+ if (OFFSET_OOB(nbytes, offset + off, 8))
+ return 0;
+ off = SEXT(sgn,64,LE64(q));
+ break;
+ case FILE_OCTAL:
+ if (OFFSET_OOB(nbytes, offset, m->vallen))
+ return 0;
+ off = SEXT(sgn,64,strtoull(p->s, NULL, 8));
+ break;
+ default:
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "bad op=%d\n", op);
+ return 0;
+ }
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "indirect offs=%jd\n", off);
+ }
+ switch (in_type = cvt_flip(m->in_type, flip)) {
+ case FILE_BYTE:
+ if (OFFSET_OOB(nbytes, offset, 1))
+ return 0;
+ if (do_ops(ms, m, &offset, SEXT(sgn,8,p->b), off))
+ return 0;
+ break;
+ case FILE_BESHORT:
+ if (OFFSET_OOB(nbytes, offset, 2))
+ return 0;
+ if (do_ops(ms, m, &offset, SEXT(sgn,16,BE16(p)), off))
+ return 0;
+ break;
+ case FILE_LESHORT:
+ if (OFFSET_OOB(nbytes, offset, 2))
+ return 0;
+ if (do_ops(ms, m, &offset, SEXT(sgn,16,LE16(p)), off))
+ return 0;
+ break;
+ case FILE_SHORT:
+ if (OFFSET_OOB(nbytes, offset, 2))
+ return 0;
+ if (do_ops(ms, m, &offset, SEXT(sgn,16,p->h), off))
+ return 0;
+ break;
+ case FILE_BELONG:
+ case FILE_BEID3:
+ if (OFFSET_OOB(nbytes, offset, 4))
+ return 0;
+ lhs = BE32(p);
+ if (in_type == FILE_BEID3)
+ lhs = cvt_id3(ms, CAST(uint32_t, lhs));
+ if (do_ops(ms, m, &offset, SEXT(sgn,32,lhs), off))
+ return 0;
+ break;
+ case FILE_LELONG:
+ case FILE_LEID3:
+ if (OFFSET_OOB(nbytes, offset, 4))
+ return 0;
+ lhs = LE32(p);
+ if (in_type == FILE_LEID3)
+ lhs = cvt_id3(ms, CAST(uint32_t, lhs));
+ if (do_ops(ms, m, &offset, SEXT(sgn,32,lhs), off))
+ return 0;
+ break;
+ case FILE_MELONG:
+ if (OFFSET_OOB(nbytes, offset, 4))
+ return 0;
+ if (do_ops(ms, m, &offset, SEXT(sgn,32,ME32(p)), off))
+ return 0;
+ break;
+ case FILE_LONG:
+ if (OFFSET_OOB(nbytes, offset, 4))
+ return 0;
+ if (do_ops(ms, m, &offset, SEXT(sgn,32,p->l), off))
+ return 0;
+ break;
+ case FILE_LEQUAD:
+ if (OFFSET_OOB(nbytes, offset, 8))
+ return 0;
+ if (do_ops(ms, m, &offset, SEXT(sgn,64,LE64(p)), off))
+ return 0;
+ break;
+ case FILE_BEQUAD:
+ if (OFFSET_OOB(nbytes, offset, 8))
+ return 0;
+ if (do_ops(ms, m, &offset, SEXT(sgn,64,BE64(p)), off))
+ return 0;
+ break;
+ case FILE_OCTAL:
+ if (OFFSET_OOB(nbytes, offset, m->vallen))
+ return 0;
+ if(do_ops(ms, m, &offset,
+ SEXT(sgn,64,strtoull(p->s, NULL, 8)), off))
+ return 0;
+ break;
+ default:
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "bad in_type=%d\n", in_type);
+ return 0;
+ }
+
+ if (m->flag & INDIROFFADD) {
+ if (cont_level == 0) {
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr,
+ "indirect *zero* cont_level\n");
+ return 0;
+ }
+ offset += ms->c.li[cont_level - 1].off;
+ if (offset == 0) {
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr,
+ "indirect *zero* offset\n");
+ return 0;
+ }
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "indirect +offs=%u\n", offset);
+ }
+ if (mcopy(ms, p, m->type, 0, s, offset, nbytes, m) == -1)
+ return -1;
+ ms->offset = offset;
+
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ mdebug(offset, RCAST(char *, RCAST(void *, p)),
+ sizeof(union VALUETYPE));
+#ifndef COMPILE_ONLY
+ file_mdump(m);
+#endif
+ }
+ }
+
+ /* Verify we have enough data to match magic type */
+ switch (m->type) {
+ case FILE_BYTE:
+ if (OFFSET_OOB(nbytes, offset, 1))
+ return 0;
+ break;
+
+ case FILE_SHORT:
+ case FILE_BESHORT:
+ case FILE_LESHORT:
+ if (OFFSET_OOB(nbytes, offset, 2))
+ return 0;
+ break;
+
+ case FILE_LONG:
+ case FILE_BELONG:
+ case FILE_LELONG:
+ case FILE_MELONG:
+ case FILE_DATE:
+ case FILE_BEDATE:
+ case FILE_LEDATE:
+ case FILE_MEDATE:
+ case FILE_LDATE:
+ case FILE_BELDATE:
+ case FILE_LELDATE:
+ case FILE_MELDATE:
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ if (OFFSET_OOB(nbytes, offset, 4))
+ return 0;
+ break;
+
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ if (OFFSET_OOB(nbytes, offset, 8))
+ return 0;
+ break;
+
+ case FILE_GUID:
+ if (OFFSET_OOB(nbytes, offset, 16))
+ return 0;
+ break;
+
+ case FILE_STRING:
+ case FILE_PSTRING:
+ case FILE_SEARCH:
+ case FILE_OCTAL:
+ if (OFFSET_OOB(nbytes, offset, m->vallen))
+ return 0;
+ break;
+
+ case FILE_REGEX:
+ if (nbytes < offset)
+ return 0;
+ break;
+
+ case FILE_INDIRECT:
+ if (m->str_flags & INDIRECT_RELATIVE)
+ offset += CAST(uint32_t, o);
+ if (offset == 0)
+ return 0;
+
+ if (nbytes < offset)
+ return 0;
+
+ if ((pb = file_push_buffer(ms)) == NULL)
+ return -1;
+
+ (*indir_count)++;
+ bb = *b;
+ bb.fbuf = s + offset;
+ bb.flen = nbytes - offset;
+ bb.ebuf = NULL;
+ bb.elen = 0;
+ rv = -1;
+ for (mlp = ms->mlist[0]->next; mlp != ms->mlist[0];
+ mlp = mlp->next)
+ {
+ if ((rv = match(ms, mlp->magic, mlp->magic_rxcomp,
+ mlp->nmagic, &bb, 0, BINTEST, text, 0, indir_count,
+ name_count, printed_something, need_separator,
+ firstline, NULL, NULL)) != 0)
+ break;
+ }
+ buffer_fini(&bb);
+
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ fprintf(stderr, "indirect @offs=%u[%d]\n", offset, rv);
+
+ rbuf = file_pop_buffer(ms, pb);
+ if (rbuf == NULL && ms->event_flags & EVENT_HAD_ERR)
+ return -1;
+
+ if (rv == 1) {
+ if ((ms->flags & MAGIC_NODESC) == 0 &&
+ file_printf(ms, F(ms, m->desc, "%u"), offset) == -1)
+ {
+ free(rbuf);
+ return -1;
+ }
+ if (file_printf(ms, "%s", rbuf) == -1) {
+ free(rbuf);
+ return -1;
+ }
+ }
+ free(rbuf);
+ return rv;
+
+ case FILE_USE:
+ if (nbytes < offset)
+ return 0;
+ rbuf = m->value.s;
+ if (*rbuf == '^') {
+ rbuf++;
+ flip = !flip;
+ }
+ if (file_magicfind(ms, rbuf, &ml) == -1) {
+ file_error(ms, 0, "cannot find entry `%s'", rbuf);
+ return -1;
+ }
+ if (save_cont(ms, &c) == -1) {
+ file_error(ms, errno, "can't allocate continuation");
+ return -1;
+ }
+
+ oneed_separator = *need_separator;
+ if (m->flag & NOSPACE)
+ *need_separator = 0;
+
+ nfound_match = 0;
+ (*name_count)++;
+ eoffset = ms->eoffset;
+ rv = match(ms, ml.magic, ml.magic_rxcomp, ml.nmagic, b,
+ offset + o, mode, text, flip, indir_count, name_count,
+ printed_something, need_separator, firstline, returnval,
+ &nfound_match);
+ ms->ms_value.q = nfound_match;
+ (*name_count)--;
+ *found_match |= nfound_match;
+
+ restore_cont(ms, &c);
+
+ if (rv != 1)
+ *need_separator = oneed_separator;
+ ms->offset = offset;
+ ms->eoffset = eoffset;
+ return rv || *found_match;
+
+ case FILE_NAME:
+ if (ms->flags & MAGIC_NODESC)
+ return 1;
+ if (file_printf(ms, "%s", m->desc) == -1)
+ return -1;
+ return 1;
+ case FILE_DER:
+ case FILE_DEFAULT: /* nothing to check */
+ case FILE_CLEAR:
+ default:
+ break;
+ }
+ if (!mconvert(ms, m, flip))
+ return 0;
+ return 1;
+}
+
+file_private uint64_t
+file_strncmp(const char *s1, const char *s2, size_t len, size_t maxlen,
+ uint32_t flags)
+{
+ /*
+ * Convert the source args to unsigned here so that (1) the
+ * compare will be unsigned as it is in strncmp() and (2) so
+ * the ctype functions will work correctly without extra
+ * casting.
+ */
+ const unsigned char *a = RCAST(const unsigned char *, s1);
+ const unsigned char *b = RCAST(const unsigned char *, s2);
+ uint32_t ws = flags & (STRING_COMPACT_WHITESPACE |
+ STRING_COMPACT_OPTIONAL_WHITESPACE);
+ const unsigned char *eb = b + (ws ? maxlen : len);
+ uint64_t v;
+
+ /*
+ * What we want here is v = strncmp(s1, s2, len),
+ * but ignoring any nulls.
+ */
+ v = 0;
+ len++;
+ if (0L == flags) { /* normal string: do it fast */
+ while (--len > 0)
+ if ((v = *b++ - *a++) != '\0')
+ break;
+ }
+ else { /* combine the others */
+ while (--len > 0) {
+ if (b >= eb) {
+ v = 1;
+ break;
+ }
+ if ((flags & STRING_IGNORE_LOWERCASE) &&
+ islower(*a)) {
+ if ((v = tolower(*b++) - *a++) != '\0')
+ break;
+ }
+ else if ((flags & STRING_IGNORE_UPPERCASE) &&
+ isupper(*a)) {
+ if ((v = toupper(*b++) - *a++) != '\0')
+ break;
+ }
+ else if ((flags & STRING_COMPACT_WHITESPACE) &&
+ isspace(*a)) {
+ a++;
+ if (isspace(*b)) {
+ b++;
+ if (!isspace(*a))
+ while (b < eb && isspace(*b))
+ b++;
+ }
+ else {
+ v = 1;
+ break;
+ }
+ }
+ else if ((flags & STRING_COMPACT_OPTIONAL_WHITESPACE) &&
+ isspace(*a)) {
+ a++;
+ while (b < eb && isspace(*b))
+ b++;
+ }
+ else {
+ if ((v = *b++ - *a++) != '\0')
+ break;
+ }
+ }
+ if (len == 0 && v == 0 && (flags & STRING_FULL_WORD)) {
+ if (*b && !isspace(*b))
+ v = 1;
+ }
+ }
+ return v;
+}
+
+file_private uint64_t
+file_strncmp16(const char *a, const char *b, size_t len, size_t maxlen,
+ uint32_t flags)
+{
+ /*
+ * XXX - The 16-bit string compare probably needs to be done
+ * differently, especially if the flags are to be supported.
+ * At the moment, I am unsure.
+ */
+ flags = 0;
+ return file_strncmp(a, b, len, maxlen, flags);
+}
+
+file_private file_regex_t *
+alloc_regex(struct magic_set *ms, struct magic *m)
+{
+ int rc;
+ file_regex_t *rx = CAST(file_regex_t *, malloc(sizeof(*rx)));
+
+ if (rx == NULL) {
+ file_error(ms, errno, "can't allocate %" SIZE_T_FORMAT
+ "u bytes", sizeof(*rx));
+ return NULL;
+ }
+
+ rc = file_regcomp(ms, rx, m->value.s, REG_EXTENDED | REG_NEWLINE |
+ ((m->str_flags & STRING_IGNORE_CASE) ? REG_ICASE : 0));
+ if (rc == 0)
+ return rx;
+
+ free(rx);
+ return NULL;
+}
+
+file_private int
+magiccheck(struct magic_set *ms, struct magic *m, file_regex_t **m_cache)
+{
+ uint64_t l = m->value.q;
+ uint64_t v;
+ float fl, fv;
+ double dl, dv;
+ int matched;
+ union VALUETYPE *p = &ms->ms_value;
+
+ switch (m->type) {
+ case FILE_BYTE:
+ v = p->b;
+ break;
+
+ case FILE_SHORT:
+ case FILE_BESHORT:
+ case FILE_LESHORT:
+ case FILE_MSDOSDATE:
+ case FILE_LEMSDOSDATE:
+ case FILE_BEMSDOSDATE:
+ case FILE_MSDOSTIME:
+ case FILE_LEMSDOSTIME:
+ case FILE_BEMSDOSTIME:
+ v = p->h;
+ break;
+
+ case FILE_LONG:
+ case FILE_BELONG:
+ case FILE_LELONG:
+ case FILE_MELONG:
+ case FILE_DATE:
+ case FILE_BEDATE:
+ case FILE_LEDATE:
+ case FILE_MEDATE:
+ case FILE_LDATE:
+ case FILE_BELDATE:
+ case FILE_LELDATE:
+ case FILE_MELDATE:
+ v = p->l;
+ break;
+
+ case FILE_QUAD:
+ case FILE_LEQUAD:
+ case FILE_BEQUAD:
+ case FILE_QDATE:
+ case FILE_BEQDATE:
+ case FILE_LEQDATE:
+ case FILE_QLDATE:
+ case FILE_BEQLDATE:
+ case FILE_LEQLDATE:
+ case FILE_QWDATE:
+ case FILE_BEQWDATE:
+ case FILE_LEQWDATE:
+ case FILE_OFFSET:
+ v = p->q;
+ break;
+
+ case FILE_FLOAT:
+ case FILE_BEFLOAT:
+ case FILE_LEFLOAT:
+ fl = m->value.f;
+ fv = p->f;
+ switch (m->reln) {
+ case 'x':
+ matched = 1;
+ break;
+
+ case '!':
+ matched = isunordered(fl, fv) ? 1 : fv != fl;
+ break;
+
+ case '=':
+ matched = isunordered(fl, fv) ? 0 : fv == fl;
+ break;
+
+ case '>':
+ matched = isgreater(fv, fl);
+ break;
+
+ case '<':
+ matched = isless(fv, fl);
+ break;
+
+ default:
+ file_magerror(ms, "cannot happen with float: "
+ "invalid relation `%c'", m->reln);
+ return -1;
+ }
+ return matched;
+
+ case FILE_DOUBLE:
+ case FILE_BEDOUBLE:
+ case FILE_LEDOUBLE:
+ dl = m->value.d;
+ dv = p->d;
+ switch (m->reln) {
+ case 'x':
+ matched = 1;
+ break;
+
+ case '!':
+ matched = isunordered(dv, dl) ? 1 : dv != dl;
+ break;
+
+ case '=':
+ matched = isunordered(dv, dl) ? 0 : dv == dl;
+ break;
+
+ case '>':
+ matched = isgreater(dv, dl);
+ break;
+
+ case '<':
+ matched = isless(dv, dl);
+ break;
+
+ default:
+ file_magerror(ms, "cannot happen with double: "
+ "invalid relation `%c'", m->reln);
+ return -1;
+ }
+ return matched;
+
+ case FILE_DEFAULT:
+ case FILE_CLEAR:
+ l = 0;
+ v = 0;
+ break;
+
+ case FILE_STRING:
+ case FILE_PSTRING:
+ case FILE_OCTAL:
+ l = 0;
+ v = file_strncmp(m->value.s, p->s, CAST(size_t, m->vallen),
+ sizeof(p->s), m->str_flags);
+ break;
+
+ case FILE_BESTRING16:
+ case FILE_LESTRING16:
+ l = 0;
+ v = file_strncmp16(m->value.s, p->s, CAST(size_t, m->vallen),
+ sizeof(p->s), m->str_flags);
+ break;
+
+ case FILE_SEARCH: { /* search ms->search.s for the string m->value.s */
+ size_t slen;
+ size_t idx;
+
+ if (ms->search.s == NULL)
+ return 0;
+
+ slen = MIN(m->vallen, sizeof(m->value.s));
+ l = 0;
+ v = 0;
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ size_t xlen = ms->search.s_len > 100 ? 100
+ : ms->search.s_len;
+
+ fprintf(stderr, "search: [");
+ file_showstr(stderr, ms->search.s, xlen);
+ fprintf(stderr, "%s] for [", ms->search.s_len == xlen
+ ? "" : "...");
+ file_showstr(stderr, m->value.s, slen);
+ }
+#ifdef HAVE_MEMMEM
+ if (slen > 0 && m->str_flags == 0) {
+ const char *found;
+ idx = m->str_range + slen;
+ if (m->str_range == 0 || ms->search.s_len < idx)
+ idx = ms->search.s_len;
+ found = CAST(const char *, memmem(ms->search.s, idx,
+ m->value.s, slen));
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ fprintf(stderr, "] %sfound\n",
+ found ? "" : "not ");
+ }
+ if (!found) {
+ v = 1;
+ break;
+ }
+ idx = found - ms->search.s;
+ ms->search.offset += idx;
+ ms->search.rm_len = ms->search.s_len - idx;
+ break;
+ }
+#endif
+
+ for (idx = 0; m->str_range == 0 || idx < m->str_range; idx++) {
+ if (slen + idx > ms->search.s_len) {
+ v = 1;
+ break;
+ }
+
+ v = file_strncmp(m->value.s, ms->search.s + idx, slen,
+ ms->search.s_len - idx, m->str_flags);
+ if (v == 0) { /* found match */
+ ms->search.offset += idx;
+ ms->search.rm_len = ms->search.s_len - idx;
+ break;
+ }
+ }
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ fprintf(stderr, "] %sfound\n", v == 0 ? "" : "not ");
+ }
+ break;
+ }
+ case FILE_REGEX: {
+ int rc;
+ file_regex_t *rx = *m_cache;
+ const char *search;
+ regmatch_t pmatch;
+ size_t slen = ms->search.s_len;
+ char *copy;
+
+ if (ms->search.s == NULL)
+ return 0;
+
+ if (rx == NULL) {
+ rx = *m_cache = alloc_regex(ms, m);
+ if (rx == NULL)
+ return -1;
+ }
+ l = 0;
+ if (slen != 0) {
+ copy = CAST(char *, malloc(slen));
+ if (copy == NULL) {
+ file_error(ms, errno,
+ "can't allocate %" SIZE_T_FORMAT "u bytes",
+ slen);
+ return -1;
+ }
+ memcpy(copy, ms->search.s, slen);
+ copy[--slen] = '\0';
+ search = copy;
+ } else {
+ search = CCAST(char *, "");
+ copy = NULL;
+ }
+ rc = file_regexec(ms, rx, RCAST(const char *, search),
+ 1, &pmatch, 0);
+ free(copy);
+ switch (rc) {
+ case 0:
+ ms->search.s += CAST(int, pmatch.rm_so);
+ ms->search.offset += CAST(size_t, pmatch.rm_so);
+ ms->search.rm_len = CAST(size_t,
+ pmatch.rm_eo - pmatch.rm_so);
+ v = 0;
+ break;
+
+ case REG_NOMATCH:
+ v = 1;
+ break;
+
+ default:
+ return -1;
+ }
+ break;
+ }
+ case FILE_USE:
+ return ms->ms_value.q != 0;
+ case FILE_NAME:
+ case FILE_INDIRECT:
+ return 1;
+ case FILE_DER:
+ matched = der_cmp(ms, m);
+ if (matched == -1) {
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ (void) fprintf(stderr,
+ "EOF comparing DER entries\n");
+ }
+ return 0;
+ }
+ return matched;
+ case FILE_GUID:
+ l = 0;
+ v = memcmp(m->value.guid, p->guid, sizeof(p->guid));
+ break;
+ default:
+ file_magerror(ms, "invalid type %d in magiccheck()", m->type);
+ return -1;
+ }
+
+ v = file_signextend(ms, m, v);
+
+ switch (m->reln) {
+ case 'x':
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "%" INT64_T_FORMAT
+ "u == *any* = 1", CAST(unsigned long long, v));
+ matched = 1;
+ break;
+
+ case '!':
+ matched = v != l;
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "%" INT64_T_FORMAT "u != %"
+ INT64_T_FORMAT "u = %d",
+ CAST(unsigned long long, v),
+ CAST(unsigned long long, l), matched);
+ break;
+
+ case '=':
+ matched = v == l;
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "%" INT64_T_FORMAT "u == %"
+ INT64_T_FORMAT "u = %d",
+ CAST(unsigned long long, v),
+ CAST(unsigned long long, l), matched);
+ break;
+
+ case '>':
+ if (m->flag & UNSIGNED) {
+ matched = v > l;
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "%" INT64_T_FORMAT
+ "u > %" INT64_T_FORMAT "u = %d",
+ CAST(unsigned long long, v),
+ CAST(unsigned long long, l), matched);
+ }
+ else {
+ matched = CAST(int64_t, v) > CAST(int64_t, l);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "%" INT64_T_FORMAT
+ "d > %" INT64_T_FORMAT "d = %d",
+ CAST(long long, v),
+ CAST(long long, l), matched);
+ }
+ break;
+
+ case '<':
+ if (m->flag & UNSIGNED) {
+ matched = v < l;
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "%" INT64_T_FORMAT
+ "u < %" INT64_T_FORMAT "u = %d",
+ CAST(unsigned long long, v),
+ CAST(unsigned long long, l), matched);
+ }
+ else {
+ matched = CAST(int64_t, v) < CAST(int64_t, l);
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "%" INT64_T_FORMAT
+ "d < %" INT64_T_FORMAT "d = %d",
+ CAST(long long, v),
+ CAST(long long, l), matched);
+ }
+ break;
+
+ case '&':
+ matched = (v & l) == l;
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "((%" INT64_T_FORMAT "x & %"
+ INT64_T_FORMAT "x) == %" INT64_T_FORMAT
+ "x) = %d", CAST(unsigned long long, v),
+ CAST(unsigned long long, l),
+ CAST(unsigned long long, l),
+ matched);
+ break;
+
+ case '^':
+ matched = (v & l) != l;
+ if ((ms->flags & MAGIC_DEBUG) != 0)
+ (void) fprintf(stderr, "((%" INT64_T_FORMAT "x & %"
+ INT64_T_FORMAT "x) != %" INT64_T_FORMAT
+ "x) = %d", CAST(unsigned long long, v),
+ CAST(unsigned long long, l),
+ CAST(unsigned long long, l), matched);
+ break;
+
+ default:
+ file_magerror(ms, "cannot happen: invalid relation `%c'",
+ m->reln);
+ return -1;
+ }
+ if ((ms->flags & MAGIC_DEBUG) != 0) {
+ (void) fprintf(stderr, " strength=%zu\n",
+ file_magic_strength(m, 1));
+ }
+
+ return matched;
+}
+
+file_private int
+handle_annotation(struct magic_set *ms, struct magic *m, int firstline)
+{
+ if ((ms->flags & MAGIC_APPLE) && m->apple[0]) {
+ if (print_sep(ms, firstline) == -1)
+ return -1;
+ if (file_printf(ms, "%.8s", m->apple) == -1)
+ return -1;
+ return 1;
+ }
+ if ((ms->flags & MAGIC_EXTENSION) && m->ext[0]) {
+ if (print_sep(ms, firstline) == -1)
+ return -1;
+ if (file_printf(ms, "%s", m->ext) == -1)
+ return -1;
+ return 1;
+ }
+ if ((ms->flags & MAGIC_MIME_TYPE) && m->mimetype[0]) {
+ char buf[1024];
+ const char *p;
+ if (print_sep(ms, firstline) == -1)
+ return -1;
+ if (varexpand(ms, buf, sizeof(buf), m->mimetype) == -1)
+ p = m->mimetype;
+ else
+ p = buf;
+ if (file_printf(ms, "%s", p) == -1)
+ return -1;
+ return 1;
+ }
+ return 0;
+}
+
+file_private int
+print_sep(struct magic_set *ms, int firstline)
+{
+ if (firstline)
+ return 0;
+ /*
+ * we found another match
+ * put a newline and '-' to do some simple formatting
+ */
+ return file_separator(ms);
+}
diff --git a/contrib/libs/libmagic/src/tar.h b/contrib/libs/libmagic/src/tar.h
new file mode 100644
index 0000000000..ced4f39398
--- /dev/null
+++ b/contrib/libs/libmagic/src/tar.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) Ian F. Darwin 1986-1995.
+ * Software written by Ian F. Darwin and others;
+ * maintained 1995-present by Christos Zoulas and others.
+ *
+ * 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 immediately at the beginning of the file, without modification,
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+/*
+ * Header file for file_public domain tar (tape archive) program.
+ *
+ * @(#)tar.h 1.20 86/10/29 Public Domain.
+ *
+ * Created 25 August 1985 by John Gilmore, ihnp4!hoptoad!gnu.
+ *
+ * $File: tar.h,v 1.16 2022/12/26 17:31:14 christos Exp $ # checkin only
+ */
+
+/*
+ * Header block on tape.
+ *
+ * I'm going to use traditional DP naming conventions here.
+ * A "block" is a big chunk of stuff that we do I/O on.
+ * A "record" is a piece of info that we care about.
+ * Typically many "record"s fit into a "block".
+ */
+#define RECORDSIZE 512
+#define NAMSIZ 100
+#define TUNMLEN 32
+#define TGNMLEN 32
+
+union record {
+ unsigned char charptr[RECORDSIZE];
+ struct header {
+ char name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[NAMSIZ];
+ char magic[8];
+ char uname[TUNMLEN];
+ char gname[TGNMLEN];
+ char devmajor[8];
+ char devminor[8];
+ } header;
+};
+
+/* The magic field is filled with this if uname and gname are valid. */
+#define TMAGIC "ustar" /* 5 chars and a null */
+#define GNUTMAGIC "ustar " /* 7 chars and a null */
diff --git a/contrib/libs/libmagic/src/ya.make b/contrib/libs/libmagic/src/ya.make
new file mode 100644
index 0000000000..56433e73c2
--- /dev/null
+++ b/contrib/libs/libmagic/src/ya.make
@@ -0,0 +1,60 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(
+ BSD-2-Clause AND
+ Bsd-Simplified-Darwin AND
+ Public-Domain
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+PEERDIR(
+ contrib/libs/libc_compat
+ contrib/libs/zlib
+ library/cpp/resource
+)
+
+ADDINCL(
+ contrib/libs/libmagic
+ contrib/libs/libmagic/src
+)
+
+NO_COMPILER_WARNINGS()
+
+CFLAGS(
+ -DHAVE_CONFIG_H
+ -DMAGIC=\"res@/magic/magic.mgc\"
+)
+
+SRCS(
+ apprentice.c
+ apptype.c
+ ascmagic.c
+ buffer.c
+ cdf.c
+ cdf_time.c
+ compress.c
+ der.c
+ encoding.c
+ fmtcheck.c
+ fsmagic.c
+ funcs.c
+ is_csv.c
+ is_json.c
+ is_simh.c
+ is_tar.c
+ magic.c
+ print.c
+ readcdf.c
+ readelf.c
+ res.cpp
+ softmagic.c
+)
+
+END()
+
+RECURSE(
+ file
+)
diff --git a/contrib/libs/libmagic/ya.make b/contrib/libs/libmagic/ya.make
new file mode 100644
index 0000000000..fb83b2a780
--- /dev/null
+++ b/contrib/libs/libmagic/ya.make
@@ -0,0 +1,28 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+LIBRARY()
+
+LICENSE(Bsd-Simplified-Darwin)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(5.45)
+
+ORIGINAL_SOURCE(https://github.com/file/file/archive/FILE5_45.tar.gz)
+
+PEERDIR(
+ contrib/libs/libmagic/magic
+ contrib/libs/libmagic/src
+)
+
+ADDINCL(
+ GLOBAL contrib/libs/libmagic/include
+)
+
+END()
+
+RECURSE(
+ file
+ magic
+ src
+)
diff --git a/contrib/libs/pcre2/AUTHORS b/contrib/libs/pcre2/AUTHORS
new file mode 100644
index 0000000000..11ef898b25
--- /dev/null
+++ b/contrib/libs/pcre2/AUTHORS
@@ -0,0 +1,36 @@
+THE MAIN PCRE2 LIBRARY CODE
+---------------------------
+
+Written by: Philip Hazel
+Email local part: Philip.Hazel
+Email domain: gmail.com
+
+Retired from University of Cambridge Computing Service,
+Cambridge, England.
+
+Copyright (c) 1997-2022 University of Cambridge
+All rights reserved
+
+
+PCRE2 JUST-IN-TIME COMPILATION SUPPORT
+--------------------------------------
+
+Written by: Zoltan Herczeg
+Email local part: hzmester
+Emain domain: freemail.hu
+
+Copyright(c) 2010-2022 Zoltan Herczeg
+All rights reserved.
+
+
+STACK-LESS JUST-IN-TIME COMPILER
+--------------------------------
+
+Written by: Zoltan Herczeg
+Email local part: hzmester
+Emain domain: freemail.hu
+
+Copyright(c) 2009-2022 Zoltan Herczeg
+All rights reserved.
+
+####
diff --git a/contrib/libs/pcre2/COPYING b/contrib/libs/pcre2/COPYING
new file mode 100644
index 0000000000..c233950f6f
--- /dev/null
+++ b/contrib/libs/pcre2/COPYING
@@ -0,0 +1,5 @@
+PCRE2 LICENCE
+
+Please see the file LICENCE in the PCRE2 distribution for licensing details.
+
+End
diff --git a/contrib/libs/pcre2/ChangeLog b/contrib/libs/pcre2/ChangeLog
new file mode 100644
index 0000000000..2b100361e8
--- /dev/null
+++ b/contrib/libs/pcre2/ChangeLog
@@ -0,0 +1,2827 @@
+Change Log for PCRE2 - see also the Git log
+-------------------------------------------
+
+
+Version 10.42 11-December-2022
+------------------------------
+
+1. Change 19 of 10.41 wasn't quite right; it put the definition of a default,
+empty value for PCRE2_CALL_CONVENTION in src/pcre2posix.c instead of
+src/pcre2posix.h, which meant that programs that included pcre2posix.h but not
+pcre2.h failed to compile.
+
+2. To catch similar issues to the above in future, a new small test program
+that includes pcre2posix.h but not pcre2.h has been added to the test suite.
+
+3. When the -S option of pcre2test was used to set a stack size greater than
+the allowed maximum, the error message displayed the hard limit incorrectly.
+This was pointed out on GitHub pull request #171, but the suggested patch
+didn't cope with all cases. Some further modification was required.
+
+4. Supplying an ovector count of more than 65535 to pcre2_match_data_create()
+caused a crash because the field in the match data block is only 16 bits. A
+maximum of 65535 is now silently applied.
+
+5. Merged @carenas patch #175 which fixes #86 - segfault on aarch64 (ARM),
+
+
+Version 10.41 06-December-2022
+------------------------------
+
+1. Add fflush() before and after a fork callout in pcre2grep to get its output
+to be the same on all systems. (There were previously ordering differences in
+Alpine Linux).
+
+2. Merged patch from @carenas (GitHub #110) for pthreads support in CMake.
+
+3. SSF scorecards grumbled about possible overflow in an expression in
+pcre2test. It never would have overflowed in practice, but some casts have been
+added and at the some time there's been some tidying of fprints that output
+size_t values.
+
+4. PR #94 showed up an unused enum in pcre2_convert.c, which is now removed.
+
+5. Minor code re-arrangement to remove gcc warning about realloc() in
+pcre2test.
+
+6. Change a number of int variables that hold buffer and line lengths in
+pcre2grep to PCRE2_SIZE (aka size_t).
+
+7. Added an #ifdef to cut out a call to PRIV(jit_free) when JIT is not
+supported (even though that function would do nothing in that case) at the
+request of a user who doesn't even want to link with pcre_jit_compile.o. Also
+tidied up an untidy #ifdef arrangement in pcre2test.
+
+8. Fixed an issue in the backtracking optimization of character repeats in
+JIT. Furthermore optimize star repetitions, not just plus repetitions.
+
+9. Removed the use of an initial backtracking frames vector on the system stack
+in pcre2_match() so that it now always uses the heap. (In a multi-thread
+environment with very small stacks there had been an issue.) This also is
+tidier for JIT matching, which didn't need that vector. The heap vector is now
+remembered in the match data block and re-used if that block itself is re-used.
+It is freed with the match data block.
+
+10. Adjusted the find_limits code in pcre2test to work with change 9 above.
+
+11. Added find_limits_noheap to pcre2test, because the heap limits are now
+different in different environments and so cannot be included in the standard
+tests.
+
+12. Created a test for pcre2_match() heap processing that is not part of the
+tests run by 'make check', but can be run manually. The current output is from
+a 64-bit system.
+
+13. Implemented -Z aka --null in pcre2grep.
+
+14. A minor change to pcre2test and the addition of several new pcre2grep tests
+have improved LCOV coverage statistics. At the same time, code in pcre2grep and
+elsewhere that can never be obeyed in normal testing has been excluded from
+coverage.
+
+15. Fixed a bug in pcre2grep that could cause an extra newline to be written
+after output generaed by --output.
+
+16. If a file has a .bz2 extension but is not in fact compressed, pcre2grep
+should process it as a plain text file. A bug stopped this happening; now fixed
+and added to the tests.
+
+17. When pcre2grep was running not in UTF mode, if a string specified by
+--output or obtained from a callout in a pattern contained a character (byte)
+greater than 127, it was incorrectly output in UTF-8 format.
+
+18. Added some casts after warnings from Clang sanitize.
+
+19. Merged patch from cbouc (GitHub #139): 4 function prototypes were missing
+PCRE2_CALL_CONVENTION in src/pcre2posix.h. All function prototypes returning
+pointers had out of place PCRE2_CALL_CONVENTION in src/pcre2.h.*. These
+produced errors when building for Windows with #define PCRE2_CALL_CONVENTION
+__stdcall.
+
+20. A negative repeat value in a pcre2test subject line was not being
+diagnosed, leading to infinite looping.
+
+21. Updated RunGrepTest to discard the warning that Bash now gives when setting
+LC_CTYPE to a bad value (because older versions didn't).
+
+22. Updated pcre2grep so that it behaves like GNU grep when matching more than
+one pattern and a later pattern matches at an earlier point in the subject when
+the matched substrings are being identified by colour or by offsets.
+
+23. Updated the PrepareRelease script so that the man page that it makes for
+the pcre2demo demonstration program is more standard and does not cause errors
+when processed by lexgrog or mandb -c (GitHub issue #160).
+
+24. The JIT compiler was updated.
+
+
+Version 10.40 15-April-2022
+---------------------------
+
+1. Merged patch from @carenas (GitHub #35, 7db87842) to fix pcre2grep incorrect
+handling of multiple passes.
+
+2. Merged patch from @carenas (GitHub #36, dae47509) to fix portability issue
+in pcre2grep with buffered fseek(stdin).
+
+3. Merged patch from @carenas (GitHub #37, acc520924) to fix tests when -S is
+not supported.
+
+4. Revert an unintended change in JIT repeat detection.
+
+5. Merged patch from @carenas (GitHub #52, b037bfa1) to fix build on GNU Hurd.
+
+6. Merged documentation and comments patches from @carenas (GitHub #47).
+
+7. Merged patch from @carenas (GitHub #49) to remove obsolete JFriedl test code
+from pcre2grep.
+
+8. Merged patch from @carenas (GitHub #48) to fix CMake install issue #46.
+
+9. Merged patch from @carenas (GitHub #53) fixing NULL checks in matching and
+substituting.
+
+10. Add null_subject and null_replacement modifiers to pcre2test.
+
+11. Add check for NULL subject to POSIX regexec() function.
+
+12. Add check for NULL replacement to pcre2_substitute().
+
+13. For the subject arguments of pcre2_match(), pcre2_dfa_match(), and
+pcre2_substitute(), and the replacement argument of the latter, if the pointer
+is NULL and the length is zero, treat as an empty string. Apparently a number
+of applications treat NULL/0 in this way.
+
+14. Added support for Bidi_Class and a number of binary Unicode properties,
+including Bidi_Control.
+
+15. Fix some minor issues raised by clang sanitize.
+
+16. Very minor code speed up for maximizing character property matches.
+
+17. A number of changes to script matching for \p and \P:
+
+ (a) Script extensions for a character are now coded as a bitmap instead of
+ a list of script numbers, which should be faster and does not need a
+ loop.
+
+ (b) Added the syntax \p{script:xxx} and \p{script_extensions:xxx} (synonyms
+ sc and scx).
+
+ (c) Changed \p{scriptname} from being the same as \p{sc:scriptname} to being
+ the same as \p{scx:scriptname} because this change happened in Perl at
+ release 5.26.
+
+ (d) The standard Unicode 4-letter abbreviations for script names are now
+ recognized.
+
+ (e) In accordance with Unicode and Perl's "loose matching" rules, spaces,
+ hyphens, and underscores are ignored in property names, which are then
+ matched independent of case.
+
+18. The Python scripts in the maint directory have been refactored. There are
+now three scripts that generate pcre2_ucd.c, pcre2_ucp.h, and pcre2_ucptables.c
+(which is #included by pcre2_tables.c). The data lists that used to be
+duplicated are now held in a single common Python module.
+
+19. On CHERI, and thus Arm's Morello prototype, pointers are represented as
+hardware capabilities, which consist of both an integer address and additional
+metadata, meaning they are twice the size of the platform's size_t type, i.e.
+16 bytes on a 64-bit system. The ovector member of heapframe happens to only be
+8 byte aligned, and so computing frame_size ended up with a multiple of 8 but
+not 16. Whilst the first frame was always suitably aligned, this then
+misaligned the frame that follows, resulting in an alignment fault when storing
+a pointer to Fecode at the start of match. Patch to fix this issue by Jessica
+Clarke PR#72.
+
+20. Added -LP and -LS listing options to pcre2test.
+
+21. A user discovered that the library names in CMakeLists.txt for MSVC
+debugger (PDB) files were incorrect - perhaps never tried for PCRE2?
+
+22. An item such as [Aa] is optimized into a caseless single character match.
+When this was quantified (e.g. [Aa]{2}) and was also the last literal item in a
+pattern, the optimizing "must be present for a match" character check was not
+being flagged as caseless, causing some matches that should have succeeded to
+fail.
+
+23. Fixed a unicode property matching issue in JIT. The character was not
+fully read in caseless matching.
+
+24. Fixed an issue affecting recursions in JIT caused by duplicated data
+transfers.
+
+25. Merged patch from @carenas (GitHub #96) which fixes some problems with
+pcre2test and readline/readedit:
+
+ * Use the right header for libedit in FreeBSD with autoconf
+ * Really allow libedit with cmake
+ * Avoid using readline headers with libedit
+
+
+Version 10.39 29-October-2021
+-----------------------------
+
+1. Fix incorrect detection of alternatives in first character search in JIT.
+
+2. Merged patch from @carenas (GitHub #28):
+
+ Visual Studio 2013 includes support for %zu and %td, so let newer
+ versions of it avoid the fallback, and while at it, make sure that
+ the first check is for DISABLE_PERCENT_ZT so it will be always
+ honoured if chosen.
+
+ prtdiff_t is signed, so use a signed type instead, and make sure
+ that an appropriate width is chosen if pointers are 64bit wide and
+ long is not (ex: Windows 64bit).
+
+ IMHO removing the cast (and therefore the possibilty of truncation)
+ make the code cleaner and the fallback is likely portable enough
+ with all 64-bit POSIX systems doing LP64 except for Windows.
+
+3. Merged patch from @carenas (GitHub #29) to update to Unicode 14.0.0.
+
+4. Merged patch from @carenas (GitHub #30):
+
+ * Cleanup: remove references to no longer used stdint.h
+
+ Since 19c50b9d (Unconditionally use inttypes.h instead of trying for stdint.h
+ (simplification) and remove the now unnecessary inclusion in
+ pcre2_internal.h., 2018-11-14), stdint.h is no longer used.
+
+ Remove checks for it in autotools and CMake and document better the expected
+ build failures for systems that might have stdint.h (C99) and not inttypes.h
+ (from POSIX), like old Windows.
+
+ * Cleanup: remove detection for inttypes.h which is a hard dependency
+
+ CMake checks for standard headers are not meant to be used for hard
+ dependencies, so will prevent a possible fallback to work.
+
+ Alternatively, the header could be checked to make the configuration fail
+ instead of breaking the build, but that was punted, as it was missing anyway
+ from autotools.
+
+5. Merged patch from @carenas (GitHub #32):
+
+ * jit: allow building with ancient MSVC versions
+
+ Visual Studio older than 2013 fails to build with JIT enabled, because it is
+ unable to parse non C89 compatible syntax, with mixed declarations and code.
+ While most recent compilers wouldn't even report this as a warning since it
+ is valid C99, it could be also made visible by adding to gcc/clang the
+ -Wdeclaration-after-statement flag at build time.
+
+ Move the code below the affected definitions.
+
+ * pcre2grep: avoid mixing declarations with code
+
+ Since d5a61ee8 (Patch to detect (and ignore) symlink loops in pcre2grep,
+ 2021-08-28), code will fail to build in a strict C89 compiler.
+
+ Reformat slightly to make it C89 compatible again.
+
+
+Version 10.38 01-October-2021
+-----------------------------
+
+1. Fix invalid single character repetition issues in JIT when the repetition
+is inside a capturing bracket and the bracket is preceded by character
+literals.
+
+2. Installed revised CMake configuration files provided by Jan-Willem Blokland.
+This extends the CMake build system to build both static and shared libraries
+in one go, builds the static library with PIC, and exposes PCRE2 libraries
+using the CMake config files. JWB provided these notes:
+
+- Introduced CMake variable BUILD_STATIC_LIBS to build the static library.
+
+- Make a small modification to config-cmake.h.in by removing the PCRE2_STATIC
+ variable. Added PCRE2_STATIC variable to the static build using the
+ target_compile_definitions() function.
+
+- Extended the CMake config files.
+
+ - Introduced CMake variable PCRE2_USE_STATIC_LIBS to easily switch between
+ the static and shared libraries.
+
+ - Added the PCRE_STATIC variable to the target compile definitions for the
+ import of the static library.
+
+Building static and shared libraries using MSVC results in a name clash of
+the libraries. Both static and shared library builds create, for example, the
+file pcre2-8.lib. Therefore, I decided to change the static library names by
+adding "-static". For example, pcre2-8.lib has become pcre2-8-static.lib.
+[Comment by PH: this is MSVC-specific. It doesn't happen on Linux.]
+
+3. Increased the minimum release number for CMake to 3.0.0 because older than
+2.8.12 is deprecated (it was set to 2.8.5) and causes warnings. Even 3.0.0 is
+quite old; it was released in 2014.
+
+4. Implemented a modified version of Thomas Tempelmann's pcre2grep patch for
+detecting symlink loops. This is dependent on the availability of realpath(),
+which is now tested for in ./configure and CMakeLists.txt.
+
+5. Implemented a modified version of Thomas Tempelmann's patch for faster
+case-independent "first code unit" searches for unanchored patterns in 8-bit
+mode in the interpreters. Instead of just remembering whether one case matched
+or not, it remembers the position of a previous match so as to avoid
+unnecessary repeated searching.
+
+6. Perl now locks out \K in lookarounds, so PCRE2 now does the same by default.
+However, just in case anybody was relying on the old behaviour, there is an
+option called PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK that enables the old behaviour.
+An option has also been added to pcre2grep to enable this.
+
+7. Re-enable a JIT optimization which was unintentionally disabled in 10.35.
+
+8. There is a loop counter to catch excessively crazy patterns when checking
+the lengths of lookbehinds at compile time. This was incorrectly getting reset
+whenever a lookahead was processed, leading to some fuzzer-generated patterns
+taking a very long time to compile when (?|) was present in the pattern,
+because (?|) disables caching of group lengths.
+
+
+Version 10.37 26-May-2021
+-------------------------
+
+1. Change RunGrepTest to use tr instead of sed when testing with binary
+zero bytes, because sed varies a lot from system to system and has problems
+with binary zeros. This is from Bugzilla #2681. Patch from Jeremie
+Courreges-Anglas via Nam Nguyen. This fixes RunGrepTest for OpenBSD. Later:
+it broke it for at least one version of Solaris, where tr can't handle binary
+zeros. However, that system had /usr/xpg4/bin/tr installed, which works OK, so
+RunGrepTest now checks for that command and uses it if found.
+
+2. Compiling with gcc 10.2's -fanalyzer option showed up a hypothetical problem
+with a NULL dereference. I don't think this case could ever occur in practice,
+but I have put in a check in order to get rid of the compiler error.
+
+3. An alternative patch for CMakeLists.txt because 10.36 #4 breaks CMake on
+Windows. Patch from email@cs-ware.de fixes bugzilla #2688.
+
+4. Two bugs related to over-large numbers have been fixed so the behaviour is
+now the same as Perl.
+
+ (a) A pattern such as /\214748364/ gave an overflow error instead of being
+ treated as the octal number \214 followed by literal digits.
+
+ (b) A sequence such as {65536 that has no terminating } so is not a
+ quantifier was nevertheless complaining that a quantifier number was too big.
+
+5. A run of autoconf suggested that configure.ac was out-of-date with respect
+to the lastest autoconf. Running autoupdate made some valid changes, some valid
+suggestions, and also some invalid changes, which were fixed by hand. Autoconf
+now runs clean and the resulting "configure" seems to work, so I hope nothing
+is broken. Later: the requirement for autoconf 2.70 broke some automatic test
+robots. It doesn't seem to be necessary: trying a reduction to 2.60.
+
+6. The pattern /a\K.(?0)*/ when matched against "abac" by the interpreter gave
+the answer "bac", whereas Perl and JIT both yield "c". This was because the
+effect of \K was not propagating back from the full pattern recursion. Other
+recursions such as /(a\K.(?1)*)/ did not have this problem.
+
+7. Restore single character repetition optimization in JIT. Currently fewer
+character repetitions are optimized than in 10.34.
+
+8. When the names of the functions in the POSIX wrapper were changed to
+pcre2_regcomp() etc. (see change 10.33 #4 below), functions with the original
+names were left in the library so that pre-compiled programs would still work.
+However, this has proved troublesome when programs link with several libraries,
+some of which use PCRE2 via the POSIX interface while others use a native POSIX
+library. For this reason, the POSIX function names are removed in this release.
+The macros in pcre2posix.h should ensure that re-compiling fixes any programs
+that haven't been compiled since before 10.33.
+
+
+Version 10.36 04-December-2020
+------------------------------
+
+1. Add CET_CFLAGS so that when Intel CET is enabled, pass -mshstk to
+compiler. This fixes https://bugs.exim.org/show_bug.cgi?id=2578. Patch for
+Makefile.am and configure.ac by H.J. Lu. Equivalent patch for CMakeLists.txt
+invented by PH.
+
+2. Fix inifinite loop when a single byte newline is searched in JIT when
+invalid utf8 mode is enabled.
+
+3. Updated CMakeLists.txt with patch from Wolfgang Stöggl (Bugzilla #2584):
+
+ - Include GNUInstallDirs and use ${CMAKE_INSTALL_LIBDIR} instead of hardcoded
+ lib. This allows differentiation between lib and lib64.
+ CMAKE_INSTALL_LIBDIR is used for installation of libraries and also for
+ pkgconfig file generation.
+
+ - Add the version of PCRE2 to the configuration summary like ./configure
+ does.
+
+ - Fix typo: MACTHED_STRING->MATCHED_STRING
+
+4. Updated CMakeLists.txt with another patch from Wolfgang Stöggl (Bugzilla
+#2588):
+
+ - Add escaped double quotes around include directory in CMakeLists.txt to
+ allow spaces in directory names.
+
+ - This fixes a cmake error, if the path of the pcre2 source contains a space.
+
+5. Updated CMakeLists.txt with a patch from B. Scott Michel: CMake's
+documentation suggests using CHECK_SYMBOL_EXISTS over CHECK_FUNCTION_EXIST.
+Moreover, these functions come from specific header files, which need to be
+specified (and, thankfully, are the same on both the Linux and WinXX
+platforms.)
+
+6. Added a (uint32_t) cast to prevent a compiler warning in pcre2_compile.c.
+
+7. Applied a patch from Wolfgang Stöggl (Bugzilla #2600) to fix postfix for
+debug Windows builds using CMake. This also updated configure so that it
+generates *.pc files and pcre2-config with the same content, as in the past.
+
+8. If a pattern ended with (?(VERSION=n.d where n is any number but d is just a
+single digit, the code unit beyond d was being read (i.e. there was a read
+buffer overflow). Fixes ClusterFuzz 23779.
+
+9. After the rework in r1235, certain character ranges were incorrectly
+handled by an optimization in JIT. Furthermore a wrong offset was used to
+read a value from a buffer which could lead to memory overread.
+
+10. Unnoticed for many years was the fact that delimiters other than / in the
+testinput1 and testinput4 files could cause incorrect behaviour when these
+files were processed by perltest.sh. There were several tests that used quotes
+as delimiters, and it was just luck that they didn't go wrong with perltest.sh.
+All the patterns in testinput1 and testinput4 now use / as their delimiter.
+This fixes Bugzilla #2641.
+
+11. Perl has started to give an error for \K within lookarounds (though there
+are cases where it doesn't). PCRE2 still allows this, so the tests that include
+this case have been moved from test 1 to test 2.
+
+12. Further to 10 above, pcre2test has been updated to detect and grumble if a
+delimiter other than / is used after #perltest.
+
+13. Fixed a bug with PCRE2_MATCH_INVALID_UTF in 8-bit mode when PCRE2_CASELESS
+was set and PCRE2_NO_START_OPTIMIZE was not set. The optimization for finding
+the start of a match was not resetting correctly after a failed match on the
+first valid fragment of the subject, possibly causing incorrect "no match"
+returns on subsequent fragments. For example, the pattern /A/ failed to match
+the subject \xe5A. Fixes Bugzilla #2642.
+
+14. Fixed a bug in character set matching when JIT is enabled and both unicode
+scripts and unicode classes are present at the same time.
+
+15. Added GNU grep's -m (aka --max-count) option to pcre2grep.
+
+16. Refactored substitution processing in pcre2grep strings, both for the -O
+option and when dealing with callouts. There is now a single function that
+handles $ expansion in all cases (instead of multiple copies of almost
+identical code). This means that the same escape sequences are available
+everywhere, which was not previously the case. At the same time, the escape
+sequences $x{...} and $o{...} have been introduced, to allow for characters
+whose code points are greater than 255 in Unicode mode.
+
+17. Applied the patch from Bugzilla #2628 to RunGrepTest. This does an explicit
+test for a version of sed that can handle binary zero, instead of assuming that
+any Linux version will work. Later: replaced $(...) by `...` because not all
+shells recognize the former.
+
+18. Fixed a word boundary check bug in JIT when partial matching is enabled.
+
+19. Fix ARM64 compilation warning in JIT. Patch by Carlo.
+
+20. A bug in the RunTest script meant that if the first part of test 2 failed,
+the failure was not reported.
+
+21. Test 2 was failing when run from a directory other than the source
+directory. This failure was previously missed in RunTest because of 20 above.
+Fixes added to both RunTest and RunTest.bat.
+
+22. Patch to CMakeLists.txt from Daniel to fix problem with testing under
+Windows.
+
+
+Version 10.35 09-May-2020
+---------------------------
+
+1. Use PCRE2_MATCH_EMPTY flag to detect empty matches in JIT.
+
+2. Fix ARMv5 JIT improper handling of labels right after a constant pool.
+
+3. A JIT bug is fixed which allowed to read the fields of the compiled
+pattern before its existence is checked.
+
+4. Back in the PCRE1 day, capturing groups that contained recursive back
+references to themselves were made atomic (version 8.01, change 18) because
+after the end a repeated group, the captured substrings had their values from
+the final repetition, not from an earlier repetition that might be the
+destination of a backtrack. This feature was documented, and was carried over
+into PCRE2. However, it has now been realized that the major refactoring that
+was done for 10.30 has made this atomicizing unnecessary, and it is confusing
+when users are unaware of it, making some patterns appear not to be working as
+expected. Capture values of recursive back references in repeated groups are
+now correctly backtracked, so this unnecessary restriction has been removed.
+
+5. Added PCRE2_SUBSTITUTE_LITERAL.
+
+6. Avoid some VS compiler warnings.
+
+7. Added PCRE2_SUBSTITUTE_MATCHED.
+
+8. Added (?* and (?<* as synonyms for (*napla: and (*naplb: to match another
+regex engine. The Perl regex folks are aware of this usage and have made a note
+about it.
+
+9. When an assertion is repeated, PCRE2 used to limit the maximum repetition to
+1, believing that repeating an assertion is pointless. However, if a positive
+assertion contains capturing groups, repetition can be useful. In any case, an
+assertion could always be wrapped in a repeated group. The only restriction
+that is now imposed is that an unlimited maximum is changed to one more than
+the minimum.
+
+10. Fix *THEN verbs in lookahead assertions in JIT.
+
+11. Added PCRE2_SUBSTITUTE_REPLACEMENT_ONLY.
+
+12. The JIT stack should be freed when the low-level stack allocation fails.
+
+13. In pcre2grep, if the final line in a scanned file is output but does not
+end with a newline sequence, add a newline according to the --newline setting.
+
+14. (?(DEFINE)...) groups were not being handled correctly when checking for
+the fixed length of a lookbehind assertion. Such a group within a lookbehind
+should be skipped, as it does not contribute to the length of the group.
+Instead, the (DEFINE) group was being processed, and if at the end of the
+lookbehind, that end was not correctly recognized. Errors such as "lookbehind
+assertion is not fixed length" and also "internal error: bad code value in
+parsed_skip()" could result.
+
+15. Put a limit of 1000 on recursive calls in pcre2_study() when searching
+nested groups for starting code units, in order to avoid stack overflow issues.
+If the limit is reached, it just gives up trying for this optimization.
+
+16. The control verb chain list must always be restored when exiting from a
+recurse function in JIT.
+
+17. Fix a crash which occurs when the character type of an invalid UTF
+character is decoded in JIT.
+
+18. Changes in many areas of the code so that when Unicode is supported and
+PCRE2_UCP is set without PCRE2_UTF, Unicode character properties are used for
+upper/lower case computations on characters whose code points are greater than
+127.
+
+19. The function for checking UTF-16 validity was returning an incorrect offset
+for the start of the error when a high surrogate was not followed by a valid
+low surrogate. This caused incorrect behaviour, for example when
+PCRE2_MATCH_INVALID_UTF was set and a match started immediately following the
+invalid high surrogate, such as /aa/ matching "\x{d800}aa".
+
+20. If a DEFINE group immediately preceded a lookbehind assertion, the pattern
+could be mis-compiled and therefore not match correctly. This is the example
+that found this: /(?(DEFINE)(?<foo>bar))(?<![-a-z0-9])word/ which failed to
+match "word" because the "move back" value was set to zero.
+
+21. Following a request from a user, some extensions and tidies to the
+character tables handling have been done:
+
+ (a) The dftables auxiliary program is renamed pcre2_dftables, but it is still
+ not installed for public use.
+
+ (b) There is now a -b option for pcre2_dftables, which causes the tables to
+ be written in binary. There is also a -help option.
+
+ (c) PCRE2_CONFIG_TABLES_LENGTH is added to pcre2_config() so that an
+ application that wants to save tables in binary knows how long they are.
+
+22. Changed setting of CMAKE_MODULE_PATH in CMakeLists.txt from SET to
+LIST(APPEND...) to allow a setting from the command line to be included.
+
+23. Updated to Unicode 13.0.0.
+
+24. CMake build now checks for secure_getenv() and strerror(). Patch by Carlo.
+
+25. Avoid using [-1] as a suffix in pcre2test because it can provoke a compiler
+warning.
+
+26. Added tests for __attribute__((uninitialized)) to both the configure and
+CMake build files, and then applied this attribute to the variable called
+stack_frames_vector[] in pcre2_match(). When implemented, this disables
+automatic initialization (a facility in clang), which can take time on big
+variables.
+
+27. Updated CMakeLists.txt (patches by Uwe Korn) to add support for
+pcre2-config, the libpcre*.pc files, SOVERSION, VERSION and the
+MACHO_*_VERSIONS settings for CMake builds.
+
+28. Another patch to CMakeLists.txt to check for mkostemp (configure already
+does). Patch by Carlo Marcelo Arenas Belon.
+
+29. Check for the existence of memfd_create in both CMake and configure
+configurations. Patch by Carlo Marcelo Arenas Belon.
+
+30. Restrict the configuration setting for the SELinux compatible execmem
+allocator (change 10.30/44) to Linux and NetBSD.
+
+
+Version 10.34 21-November-2019
+------------------------------
+
+1. The maximum number of capturing subpatterns is 65535 (documented), but no
+check on this was ever implemented. This omission has been rectified; it fixes
+ClusterFuzz 14376.
+
+2. Improved the invalid utf32 support of the JIT compiler. Now it correctly
+detects invalid characters in the 0xd800-0xdfff range.
+
+3. Fix minor typo bug in JIT compile when \X is used in a non-UTF string.
+
+4. Add support for matching in invalid UTF strings to the pcre2_match()
+interpreter, and integrate with the existing JIT support via the new
+PCRE2_MATCH_INVALID_UTF compile-time option.
+
+5. Give more error detail for invalid UTF-8 when detected in pcre2grep.
+
+6. Add support for invalid UTF-8 to pcre2grep.
+
+7. Adjust the limit for "must have" code unit searching, in particular,
+increase it substantially for non-anchored patterns.
+
+8. Allow (*ACCEPT) to be quantified, because an ungreedy quantifier with a zero
+minimum is potentially useful.
+
+9. Some changes to the way the minimum subject length is handled:
+
+ * When PCRE2_NO_START_OPTIMIZE is set, no minimum length is computed;
+ pcre2test now omits this item instead of showing a value of zero.
+
+ * An incorrect minimum length could be calculated for a pattern that
+ contained (*ACCEPT) inside a qualified group whose minimum repetition was
+ zero, for example /A(?:(*ACCEPT))?B/, which incorrectly computed a minimum
+ of 2. The minimum length scan no longer happens for a pattern that
+ contains (*ACCEPT).
+
+ * When no minimum length is set by the normal scan, but a first and/or last
+ code unit is recorded, set the minimum to 1 or 2 as appropriate.
+
+ * When a pattern contains multiple groups with the same number, a back
+ reference cannot know which one to scan for a minimum length. This used to
+ cause the minimum length finder to give up with no result. Now it treats
+ such references as not adding to the minimum length (which it should have
+ done all along).
+
+ * Furthermore, the above action now happens only if the back reference is to
+ a group that exists more than once in a pattern instead of any back
+ reference in a pattern with duplicate numbers.
+
+10. A (*MARK) value inside a successful condition was not being returned by the
+interpretive matcher (it was returned by JIT). This bug has been mended.
+
+11. A bug in pcre2grep meant that -o without an argument (or -o0) didn't work
+if the pattern had more than 32 capturing parentheses. This is fixed. In
+addition (a) the default limit for groups requested by -o<n> has been raised to
+50, (b) the new --om-capture option changes the limit, (c) an error is raised
+if -o asks for a group that is above the limit.
+
+12. The quantifier {1} was always being ignored, but this is incorrect when it
+is made possessive and applied to an item in parentheses, because a
+parenthesized item may contain multiple branches or other backtracking points,
+for example /(a|ab){1}+c/ or /(a+){1}+a/.
+
+13. For partial matches, pcre2test was always showing the maximum lookbehind
+characters, flagged with "<", which is misleading when the lookbehind didn't
+actually look behind the start (because it was later in the pattern). Showing
+all consulted preceding characters for partial matches is now controlled by the
+existing "allusedtext" modifier and, as for complete matches, this facility is
+available only for non-JIT matching, because JIT does not maintain the first
+and last consulted characters.
+
+14. DFA matching (using pcre2_dfa_match()) was not recognising a partial match
+if the end of the subject was encountered in a lookahead (conditional or
+otherwise), an atomic group, or a recursion.
+
+15. Give error if pcre2test -t, -T, -tm or -TM is given an argument of zero.
+
+16. Check for integer overflow when computing lookbehind lengths. Fixes
+Clusterfuzz issue 15636.
+
+17. Implemented non-atomic positive lookaround assertions.
+
+18. If a lookbehind contained a lookahead that contained another lookbehind
+within it, the nested lookbehind was not correctly processed. For example, if
+/(?<=(?=(?<=a)))b/ was matched to "ab" it gave no match instead of matching
+"b".
+
+19. Implemented pcre2_get_match_data_size().
+
+20. Two alterations to partial matching:
+
+ (a) The definition of a partial match is slightly changed: if a pattern
+ contains any lookbehinds, an empty partial match may be given, because this
+ is another situation where adding characters to the current subject can
+ lead to a full match. Example: /c*+(?<=[bc])/ with subject "ab".
+
+ (b) Similarly, if a pattern could match an empty string, an empty partial
+ match may be given. Example: /(?![ab]).*/ with subject "ab". This case
+ applies only to PCRE2_PARTIAL_HARD.
+
+ (c) An empty string partial hard match can be returned for \z and \Z as it
+ is documented that they shouldn't match.
+
+21. A branch that started with (*ACCEPT) was not being recognized as one that
+could match an empty string.
+
+22. Corrected pcre2_set_character_tables() tables data type: was const unsigned
+char * instead of const uint8_t *, as generated by pcre2_maketables().
+
+23. Upgraded to Unicode 12.1.0.
+
+24. Add -jitfast command line option to pcre2test (to make all the jit options
+available directly).
+
+25. Make pcre2test -C show if libreadline or libedit is supported.
+
+26. If the length of one branch of a group exceeded 65535 (the maximum value
+that is remembered as a minimum length), the whole group's length was
+incorrectly recorded as 65535, leading to incorrect "no match" when start-up
+optimizations were in force.
+
+27. The "rightmost consulted character" value was not always correct; in
+particular, if a pattern ended with a negative lookahead, characters that were
+inspected in that lookahead were not included.
+
+28. Add the pcre2_maketables_free() function.
+
+29. The start-up optimization that looks for a unique initial matching
+code unit in the interpretive engines uses memchr() in 8-bit mode. When the
+search is caseless, it was doing so inefficiently, which ended up slowing down
+the match drastically when the subject was very long. The revised code (a)
+remembers if one case is not found, so it never repeats the search for that
+case after a bumpalong and (b) when one case has been found, it searches only
+up to that position for an earlier occurrence of the other case. This fix
+applies to both interpretive pcre2_match() and to pcre2_dfa_match().
+
+30. While scanning to find the minimum length of a group, if any branch has
+minimum length zero, there is no need to scan any subsequent branches (a small
+compile-time performance improvement).
+
+31. Installed a .gitignore file on a user's suggestion. When using the svn
+repository with git (through git svn) this helps keep it tidy.
+
+32. Add underflow check in JIT which may occur when the value of subject
+string pointer is close to 0.
+
+33. Arrange for classes such as [Aa] which contain just the two cases of the
+same character, to be treated as a single caseless character. This causes the
+first and required code unit optimizations to kick in where relevant.
+
+34. Improve the bitmap of starting bytes for positive classes that include wide
+characters, but no property types, in UTF-8 mode. Previously, on encountering
+such a class, the bits for all bytes greater than \xc4 were set, thus
+specifying any character with codepoint >= 0x100. Now the only bits that are
+set are for the relevant bytes that start the wide characters. This can give a
+noticeable performance improvement.
+
+35. If the bitmap of starting code units contains only 1 or 2 bits, replace it
+with a single starting code unit (1 bit) or a caseless single starting code
+unit if the two relevant characters are case-partners. This is particularly
+relevant to the 8-bit library, though it applies to all. It can give a
+performance boost for patterns such as [Ww]ord and (word|WORD). However, this
+optimization doesn't happen if there is a "required" code unit of the same
+value (because the search for a "required" code unit starts at the match start
+for non-unique first code unit patterns, but after a unique first code unit,
+and patterns such as a*a need the former action).
+
+36. Small patch to pcre2posix.c to set the erroroffset field to -1 immediately
+after a successful compile, instead of at the start of matching to avoid a
+sanitizer complaint (regexec is supposed to be thread safe).
+
+37. Add NEON vectorization to JIT to speed up matching of first character and
+pairs of characters on ARM64 CPUs.
+
+38. If a non-ASCII character was the first in a starting assertion in a
+caseless match, the "first code unit" optimization did not get the casing
+right, and the assertion failed to match a character in the other case if it
+did not start with the same code unit.
+
+39. Fixed the incorrect computation of jump sizes on x86 CPUs in JIT. A masking
+operation was incorrectly removed in r1136. Reported by Ralf Junker.
+
+
+Version 10.33 16-April-2019
+---------------------------
+
+1. Added "allvector" to pcre2test to make it easy to check the part of the
+ovector that shouldn't be changed, in particular after substitute and failed or
+partial matches.
+
+2. Fix subject buffer overread in JIT when UTF is disabled and \X or \R has
+a greater than 1 fixed quantifier. This issue was found by Yunho Kim.
+
+3. Added support for callouts from pcre2_substitute(). After 10.33-RC1, but
+prior to release, fixed a bug that caused a crash if pcre2_substitute() was
+called with a NULL match context.
+
+4. The POSIX functions are now all called pcre2_regcomp() etc., with wrapper
+functions that use the standard POSIX names. However, in pcre2posix.h the POSIX
+names are defined as macros. This should help avoid linking with the wrong
+library in some environments while still exporting the POSIX names for
+pre-existing programs that use them. (The Debian alternative names are also
+defined as macros, but not documented.)
+
+5. Fix an xclass matching issue in JIT.
+
+6. Implement PCRE2_EXTRA_ESCAPED_CR_IS_LF (see Bugzilla 2315).
+
+7. Implement the Perl 5.28 experimental alphabetic names for atomic groups and
+lookaround assertions, for example, (*pla:...) and (*atomic:...). These are
+characterized by a lower case letter following (* and to simplify coding for
+this, the character tables created by pcre2_maketables() were updated to add a
+new "is lower case letter" bit. At the same time, the now unused "is
+hexadecimal digit" bit was removed. The default tables in
+src/pcre2_chartables.c.dist are updated.
+
+8. Implement the new Perl "script run" features (*script_run:...) and
+(*atomic_script_run:...) aka (*sr:...) and (*asr:...).
+
+9. Fixed two typos in change 22 for 10.21, which added special handling for
+ranges such as a-z in EBCDIC environments. The original code probably never
+worked, though there were no bug reports.
+
+10. Implement PCRE2_COPY_MATCHED_SUBJECT for pcre2_match() (including JIT via
+pcre2_match()) and pcre2_dfa_match(), but *not* the pcre2_jit_match() fast
+path. Also, when a match fails, set the subject field in the match data to NULL
+for tidiness - none of the substring extractors should reference this after
+match failure.
+
+11. If a pattern started with a subroutine call that had a quantifier with a
+minimum of zero, an incorrect "match must start with this character" could be
+recorded. Example: /(?&xxx)*ABC(?<xxx>XYZ)/ would (incorrectly) expect 'A' to
+be the first character of a match.
+
+12. The heap limit checking code in pcre2_dfa_match() could suffer from
+overflow if the heap limit was set very large. This could cause incorrect "heap
+limit exceeded" errors.
+
+13. Add "kibibytes" to the heap limit output from pcre2test -C to make the
+units clear.
+
+14. Add a call to pcre2_jit_free_unused_memory() in pcre2grep, for tidiness.
+
+15. Updated the VMS-specific code in pcre2test on the advice of a VMS user.
+
+16. Removed the unnecessary inclusion of stdint.h (or inttypes.h) from
+pcre2_internal.h as it is now included by pcre2.h. Also, change 17 for 10.32
+below was unnecessarily complicated, as inttypes.h is a Standard C header,
+which is defined to be a superset of stdint.h. Instead of conditionally
+including stdint.h or inttypes.h, pcre2.h now unconditionally includes
+inttypes.h. This supports environments that do not have stdint.h but do have
+inttypes.h, which are known to exist. A note in the autotools documentation
+says (November 2018) that there are none known that are the other way round.
+
+17. Added --disable-percent-zt to "configure" (and equivalent to CMake) to
+forcibly disable the use of %zu and %td in formatting strings because there is
+at least one version of VMS that claims to be C99 but does not support these
+modifiers.
+
+18. Added --disable-pcre2grep-callout-fork, which restricts the callout support
+in pcre2grep to the inbuilt echo facility. This may be useful in environments
+that do not support fork().
+
+19. Fix two instances of <= 0 being applied to unsigned integers (the VMS
+compiler complains).
+
+20. Added "fork" support for VMS to pcre2grep, for running an external program
+via a string callout.
+
+21. Improve MAP_JIT flag usage on MacOS. Patch by Rich Siegel.
+
+22. If a pattern started with (*MARK), (*COMMIT), (*PRUNE), (*SKIP), or (*THEN)
+followed by ^ it was not recognized as anchored.
+
+23. The RunGrepTest script used to cut out the test of NUL characters for
+Solaris and MacOS as printf and sed can't handle them. It seems that the *BSD
+systems can't either. I've inverted the test so that only those OS that are
+known to work (currently only Linux) try to run this test.
+
+24. Some tests in RunGrepTest appended to testtrygrep from two different file
+descriptors instead of redirecting stderr to stdout. This worked on Linux, but
+it was reported not to on other systems, causing the tests to fail.
+
+25. In the RunTest script, make the test for stack setting use the same value
+for the stack as it needs for -bigstack.
+
+26. Insert a cast in pcre2_dfa_match.c to suppress a compiler warning.
+
+26. With PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL set, escape sequences such as \s
+which are valid in character classes, but not as the end of ranges, were being
+treated as literals. An example is [_-\s] (but not [\s-_] because that gave an
+error at the *start* of a range). Now an "invalid range" error is given
+independently of PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL.
+
+27. Related to 26 above, PCRE2_BAD_ESCAPE_IS_LITERAL was affecting known escape
+sequences such as \eX when they appeared invalidly in a character class. Now
+the option applies only to unrecognized or malformed escape sequences.
+
+28. Fix word boundary in JIT compiler. Patch by Mike Munday.
+
+29. The pcre2_dfa_match() function was incorrectly handling conditional version
+tests such as (?(VERSION>=0)...) when the version test was true. Incorrect
+processing or a crash could result.
+
+30. When PCRE2_UTF is set, allow non-ASCII letters and decimal digits in group
+names, as Perl does. There was a small bug in this new code, found by
+ClusterFuzz 12950, fixed before release.
+
+31. Implemented PCRE2_EXTRA_ALT_BSUX to support ECMAScript 6's \u{hhh}
+construct.
+
+32. Compile \p{Any} to be the same as . in DOTALL mode, so that it benefits
+from auto-anchoring if \p{Any}* starts a pattern.
+
+33. Compile invalid UTF check in JIT test when only pcre32 is enabled.
+
+34. For some time now, CMake has been warning about the setting of policy
+CMP0026 to "OLD" in CmakeLists.txt, and hinting that the feature might be
+removed in a future version. A request for CMake expertise on the list produced
+no result, so I have now hacked CMakeLists.txt along the lines of some changes
+I found on the Internet. The new code no longer needs the policy setting, and
+it appears to work fine on Linux.
+
+35. Setting --enable-jit=auto for an out-of-tree build failed because the
+source directory wasn't in the search path for AC_TRY_COMPILE always. Patch
+from Ross Burton.
+
+36. Disable SSE2 JIT optimizations in x86 CPUs when SSE2 is not available.
+Patch by Guillem Jover.
+
+37. Changed expressions such as 1<<10 to 1u<<10 in many places because compiler
+warnings were reported.
+
+38. Using the clang compiler with sanitizing options causes runtime complaints
+about truncation for statements such as x = ~x when x is an 8-bit value; it
+seems to compute ~x as a 32-bit value. Changing such statements to x = 255 ^ x
+gets rid of the warnings. There were also two missing casts in pcre2test.
+
+
+Version 10.32 10-September-2018
+-------------------------------
+
+1. When matching using the the REG_STARTEND feature of the POSIX API with a
+non-zero starting offset, unset capturing groups with lower numbers than a
+group that did capture something were not being correctly returned as "unset"
+(that is, with offset values of -1).
+
+2. When matching using the POSIX API, pcre2test used to omit listing unset
+groups altogether. Now it shows those that come before any actual captures as
+"<unset>", as happens for non-POSIX matching.
+
+3. Running "pcre2test -C" always stated "\R matches CR, LF, or CRLF only",
+whatever the build configuration was. It now correctly says "\R matches all
+Unicode newlines" in the default case when --enable-bsr-anycrlf has not been
+specified. Similarly, running "pcre2test -C bsr" never produced the result
+ANY.
+
+4. Matching the pattern /(*UTF)\C[^\v]+\x80/ against an 8-bit string containing
+multi-code-unit characters caused bad behaviour and possibly a crash. This
+issue was fixed for other kinds of repeat in release 10.20 by change 19, but
+repeating character classes were overlooked.
+
+5. pcre2grep now supports the inclusion of binary zeros in patterns that are
+read from files via the -f option.
+
+6. A small fix to pcre2grep to avoid compiler warnings for -Wformat-overflow=2.
+
+7. Added --enable-jit=auto support to configure.ac.
+
+8. Added some dummy variables to the heapframe structure in 16-bit and 32-bit
+modes for the benefit of m68k, where pointers can be 16-bit aligned. The
+dummies force 32-bit alignment and this ensures that the structure is a
+multiple of PCRE2_SIZE, a requirement that is tested at compile time. In other
+architectures, alignment requirements take care of this automatically.
+
+9. When returning an error from pcre2_pattern_convert(), ensure the error
+offset is set zero for early errors.
+
+10. A number of patches for Windows support from Daniel Richard G:
+
+ (a) List of error numbers in Runtest.bat corrected (it was not the same as in
+ Runtest).
+
+ (b) pcre2grep snprintf() workaround as used elsewhere in the tree.
+
+ (c) Support for non-C99 snprintf() that returns -1 in the overflow case.
+
+11. Minor tidy of pcre2_dfa_match() code.
+
+12. Refactored pcre2_dfa_match() so that the internal recursive calls no longer
+use the stack for local workspace and local ovectors. Instead, an initial block
+of stack is reserved, but if this is insufficient, heap memory is used. The
+heap limit parameter now applies to pcre2_dfa_match().
+
+13. If a "find limits" test of DFA matching in pcre2test resulted in too many
+matches for the ovector, no matches were displayed.
+
+14. Removed an occurrence of ctrl/Z from test 6 because Windows treats it as
+EOF. The test looks to have come from a fuzzer.
+
+15. If PCRE2 was built with a default match limit a lot greater than the
+default default of 10 000 000, some JIT tests of the match limit no longer
+failed. All such tests now set 10 000 000 as the upper limit.
+
+16. Another Windows related patch for pcregrep to ensure that WIN32 is
+undefined under Cygwin.
+
+17. Test for the presence of stdint.h and inttypes.h in configure and CMake and
+include whichever exists (stdint preferred) instead of unconditionally
+including stdint. This makes life easier for old and non-standard systems.
+
+18. Further changes to improve portability, especially to old and or non-
+standard systems:
+
+ (a) Put all printf arguments in RunGrepTest into single, not double, quotes,
+ and use \0 not \x00 for binary zero.
+
+ (b) Avoid the use of C++ (i.e. BCPL) // comments.
+
+ (c) Parameterize the use of %zu in pcre2test to make it like %td. For both of
+ these now, if using MSVC or a standard C before C99, %lu is used with a
+ cast if necessary.
+
+19. Applied a contributed patch to CMakeLists.txt to increase the stack size
+when linking pcre2test with MSVC. This gets rid of a stack overflow error in
+the standard set of tests.
+
+20. Output a warning in pcre2test when ignoring the "altglobal" modifier when
+it is given with the "replace" modifier.
+
+21. In both pcre2test and pcre2_substitute(), with global matching, a pattern
+that matched an empty string, but never at the starting match offset, was not
+handled in a Perl-compatible way. The pattern /(<?=\G.)/ is an example of such
+a pattern. Because \G is in a lookbehind assertion, there has to be a
+"bumpalong" before there can be a match. The automatic "advance by one
+character after an empty string match" rule is therefore inappropriate. A more
+complicated algorithm has now been implemented.
+
+22. When checking to see if a lookbehind is of fixed length, lookaheads were
+correctly ignored, but qualifiers on lookaheads were not being ignored, leading
+to an incorrect "lookbehind assertion is not fixed length" error.
+
+23. The VERSION condition test was reading fractional PCRE2 version numbers
+such as the 04 in 10.04 incorrectly and hence giving wrong results.
+
+24. Updated to Unicode version 11.0.0. As well as the usual addition of new
+scripts and characters, this involved re-jigging the grapheme break property
+algorithm because Unicode has changed the way emojis are handled.
+
+25. Fixed an obscure bug that struck when there were two atomic groups not
+separated by something with a backtracking point. There could be an incorrect
+backtrack into the first of the atomic groups. A complicated example is
+/(?>a(*:1))(?>b)(*SKIP:1)x|.*/ matched against "abc", where the *SKIP
+shouldn't find a MARK (because is in an atomic group), but it did.
+
+26. Upgraded the perltest.sh script: (1) #pattern lines can now be used to set
+a list of modifiers for all subsequent patterns - only those that the script
+recognizes are meaningful; (2) #subject lines can be used to set or unset a
+default "mark" modifier; (3) Unsupported #command lines give a warning when
+they are ignored; (4) Mark data is output only if the "mark" modifier is
+present.
+
+27. (*ACCEPT:ARG), (*FAIL:ARG), and (*COMMIT:ARG) are now supported.
+
+28. A (*MARK) name was not being passed back for positive assertions that were
+terminated by (*ACCEPT).
+
+29. Add support for \N{U+dddd}, but only in Unicode mode.
+
+30. Add support for (?^) for unsetting all imnsx options.
+
+31. The PCRE2_EXTENDED (/x) option only ever discarded space characters whose
+code point was less than 256 and that were recognized by the lookup table
+generated by pcre2_maketables(), which uses isspace() to identify white space.
+Now, when Unicode support is compiled, PCRE2_EXTENDED also discards U+0085,
+U+200E, U+200F, U+2028, and U+2029, which are additional characters defined by
+Unicode as "Pattern White Space". This makes PCRE2 compatible with Perl.
+
+32. In certain circumstances, option settings within patterns were not being
+correctly processed. For example, the pattern /((?i)A)(?m)B/ incorrectly
+matched "ab". (The (?m) setting lost the fact that (?i) should be reset at the
+end of its group during the parse process, but without another setting such as
+(?m) the compile phase got it right.) This bug was introduced by the
+refactoring in release 10.23.
+
+33. PCRE2 uses bcopy() if available when memmove() is not, and it used just to
+define memmove() as function call to bcopy(). This hasn't been tested for a
+long time because in pcre2test the result of memmove() was being used, whereas
+bcopy() doesn't return a result. This feature is now refactored always to call
+an emulation function when there is no memmove(). The emulation makes use of
+bcopy() when available.
+
+34. When serializing a pattern, set the memctl, executable_jit, and tables
+fields (that is, all the fields that contain pointers) to zeros so that the
+result of serializing is always the same. These fields are re-set when the
+pattern is deserialized.
+
+35. In a pattern such as /[^\x{100}-\x{ffff}]*[\x80-\xff]/ which has a repeated
+negative class with no characters less than 0x100 followed by a positive class
+with only characters less than 0x100, the first class was incorrectly being
+auto-possessified, causing incorrect match failures.
+
+36. Removed the character type bit ctype_meta, which dates from PCRE1 and is
+not used in PCRE2.
+
+37. Tidied up unnecessarily complicated macros used in the escapes table.
+
+38. Since 10.21, the new testoutput8-16-4 file has accidentally been omitted
+from distribution tarballs, owing to a typo in Makefile.am which had
+testoutput8-16-3 twice. Now fixed.
+
+39. If the only branch in a conditional subpattern was anchored, the whole
+subpattern was treated as anchored, when it should not have been, since the
+assumed empty second branch cannot be anchored. Demonstrated by test patterns
+such as /(?(1)^())b/ or /(?(?=^))b/.
+
+40. A repeated conditional subpattern that could match an empty string was
+always assumed to be unanchored. Now it it checked just like any other
+repeated conditional subpattern, and can be found to be anchored if the minimum
+quantifier is one or more. I can't see much use for a repeated anchored
+pattern, but the behaviour is now consistent.
+
+41. Minor addition to pcre2_jit_compile.c to avoid static analyzer complaint
+(for an event that could never occur but you had to have external information
+to know that).
+
+42. If before the first match in a file that was being searched by pcre2grep
+there was a line that was sufficiently long to cause the input buffer to be
+expanded, the variable holding the location of the end of the previous match
+was being adjusted incorrectly, and could cause an overflow warning from a code
+sanitizer. However, as the value is used only to print pending "after" lines
+when the next match is reached (and there are no such lines in this case) this
+bug could do no damage.
+
+
+Version 10.31 12-February-2018
+------------------------------
+
+1. Fix typo (missing ]) in VMS code in pcre2test.c.
+
+2. Replace the replicated code for matching extended Unicode grapheme sequences
+(which got a lot more complicated by change 10.30/49) by a single subroutine
+that is called by both pcre2_match() and pcre2_dfa_match().
+
+3. Add idempotent guard to pcre2_internal.h.
+
+4. Add new pcre2_config() options: PCRE2_CONFIG_NEVER_BACKSLASH_C and
+PCRE2_CONFIG_COMPILED_WIDTHS.
+
+5. Cut out \C tests in the JIT regression tests when NEVER_BACKSLASH_C is
+defined (e.g. by --enable-never-backslash-C).
+
+6. Defined public names for all the pcre2_compile() error numbers, and used
+the public names in pcre2_convert.c.
+
+7. Fixed a small memory leak in pcre2test (convert contexts).
+
+8. Added two casts to compile.c and one to match.c to avoid compiler warnings.
+
+9. Added code to pcre2grep when compiled under VMS to set the symbol
+PCRE2GREP_RC to the exit status, because VMS does not distinguish between
+exit(0) and exit(1).
+
+10. Added the -LM (list modifiers) option to pcre2test. Also made -C complain
+about a bad option only if the following argument item does not start with a
+hyphen.
+
+11. pcre2grep was truncating components of file names to 128 characters when
+processing files with the -r option, and also (some very odd code) truncating
+path names to 512 characters. There is now a check on the absolute length of
+full path file names, which may be up to 2047 characters long.
+
+12. When an assertion contained (*ACCEPT) it caused all open capturing groups
+to be closed (as for a non-assertion ACCEPT), which was wrong and could lead to
+misbehaviour for subsequent references to groups that started outside the
+assertion. ACCEPT in an assertion now closes only those groups that were
+started within that assertion. Fixes oss-fuzz issues 3852 and 3891.
+
+13. Multiline matching in pcre2grep was misbehaving if the pattern matched
+within a line, and then matched again at the end of the line and over into
+subsequent lines. Behaviour was different with and without colouring, and
+sometimes context lines were incorrectly printed and/or line endings were lost.
+All these issues should now be fixed.
+
+14. If --line-buffered was specified for pcre2grep when input was from a
+compressed file (.gz or .bz2) a segfault occurred. (Line buffering should be
+ignored for compressed files.)
+
+15. Although pcre2_jit_match checks whether the pattern is compiled
+in a given mode, it was also expected that at least one mode is available.
+This is fixed and pcre2_jit_match returns with PCRE2_ERROR_JIT_BADOPTION
+when the pattern is not optimized by JIT at all.
+
+16. The line number and related variables such as match counts in pcre2grep
+were all int variables, causing overflow when files with more than 2147483647
+lines were processed (assuming 32-bit ints). They have all been changed to
+unsigned long ints.
+
+17. If a backreference with a minimum repeat count of zero was first in a
+pattern, apart from assertions, an incorrect first matching character could be
+recorded. For example, for the pattern /(?=(a))\1?b/, "b" was incorrectly set
+as the first character of a match.
+
+18. Characters in a leading positive assertion are considered for recording a
+first character of a match when the rest of the pattern does not provide one.
+However, a character in a non-assertive group within a leading assertion such
+as in the pattern /(?=(a))\1?b/ caused this process to fail. This was an
+infelicity rather than an outright bug, because it did not affect the result of
+a match, just its speed. (In fact, in this case, the starting 'a' was
+subsequently picked up in the study.)
+
+19. A minor tidy in pcre2_match(): making all PCRE2_ERROR_ returns use "return"
+instead of "RRETURN" saves unwinding the backtracks in these cases (only one
+didn't).
+
+20. Allocate a single callout block on the stack at the start of pcre2_match()
+and set its never-changing fields once only. Do the same for pcre2_dfa_match().
+
+21. Save the extra compile options (set in the compile context) with the
+compiled pattern (they were not previously saved), add PCRE2_INFO_EXTRAOPTIONS
+to retrieve them, and update pcre2test to show them.
+
+22. Added PCRE2_CALLOUT_STARTMATCH and PCRE2_CALLOUT_BACKTRACK bits to a new
+field callout_flags in callout blocks. The bits are set by pcre2_match(), but
+not by JIT or pcre2_dfa_match(). Their settings are shown in pcre2test callouts
+if the callout_extra subject modifier is set. These bits are provided to help
+with tracking how a backtracking match is proceeding.
+
+23. Updated the pcre2demo.c demonstration program, which was missing the extra
+code for -g that handles the case when \K in an assertion causes the match to
+end at the original start point. Also arranged for it to detect when \K causes
+the end of a match to be before its start.
+
+24. Similar to 23 above, strange things (including loops) could happen in
+pcre2grep when \K was used in an assertion when --colour was used or in
+multiline mode. The "end at original start point" bug is fixed, and if the end
+point is found to be before the start point, they are swapped.
+
+25. When PCRE2_FIRSTLINE without PCRE2_NO_START_OPTIMIZE was used in non-JIT
+matching (both pcre2_match() and pcre2_dfa_match()) and the matched string
+started with the first code unit of a newline sequence, matching failed because
+it was not tried at the newline.
+
+26. Code for giving up a non-partial match after failing to find a starting
+code unit anywhere in the subject was missing when searching for one of a
+number of code units (the bitmap case) in both pcre2_match() and
+pcre2_dfa_match(). This was a missing optimization rather than a bug.
+
+27. Tidied up the ACROSSCHAR macro to be like FORWARDCHAR and BACKCHAR, using a
+pointer argument rather than a code unit value. This should not have affected
+the generated code.
+
+28. The JIT compiler has been updated.
+
+29. Avoid pointer overflow for unset captures in pcre2_substring_list_get().
+This could not actually cause a crash because it was always used in a memcpy()
+call with zero length.
+
+30. Some internal structures have a variable-length ovector[] as their last
+element. Their actual memory is obtained dynamically, giving an ovector of
+appropriate length. However, they are defined in the structure as
+ovector[NUMBER], where NUMBER is large so that array bound checkers don't
+grumble. The value of NUMBER was 10000, but a fuzzer exceeded 5000 capturing
+groups, making the ovector larger than this. The number has been increased to
+131072, which allows for the maximum number of captures (65535) plus the
+overall match. This fixes oss-fuzz issue 5415.
+
+31. Auto-possessification at the end of a capturing group was dependent on what
+follows the group (e.g. /(a+)b/ would auto-possessify the a+) but this caused
+incorrect behaviour when the group was called recursively from elsewhere in the
+pattern where something different might follow. This bug is an unforseen
+consequence of change #1 for 10.30 - the implementation of backtracking into
+recursions. Iterators at the ends of capturing groups are no longer considered
+for auto-possessification if the pattern contains any recursions. Fixes
+Bugzilla #2232.
+
+
+Version 10.30 14-August-2017
+----------------------------
+
+1. The main interpreter, pcre2_match(), has been refactored into a new version
+that does not use recursive function calls (and therefore the stack) for
+remembering backtracking positions. This makes --disable-stack-for-recursion a
+NOOP. The new implementation allows backtracking into recursive group calls in
+patterns, making it more compatible with Perl, and also fixes some other
+hard-to-do issues such as #1887 in Bugzilla. The code is also cleaner because
+the old code had a number of fudges to try to reduce stack usage. It seems to
+run no slower than the old code.
+
+A number of bugs in the refactored code were subsequently fixed during testing
+before release, but after the code was made available in the repository. These
+bugs were never in fully released code, but are noted here for the record.
+
+ (a) If a pattern had fewer capturing parentheses than the ovector supplied in
+ the match data block, a memory error (detectable by ASAN) occurred after
+ a match, because the external block was being set from non-existent
+ internal ovector fields. Fixes oss-fuzz issue 781.
+
+ (b) A pattern with very many capturing parentheses (when the internal frame
+ size was greater than the initial frame vector on the stack) caused a
+ crash. A vector on the heap is now set up at the start of matching if the
+ vector on the stack is not big enough to handle at least 10 frames.
+ Fixes oss-fuzz issue 783.
+
+ (c) Handling of (*VERB)s in recursions was wrong in some cases.
+
+ (d) Captures in negative assertions that were used as conditions were not
+ happening if the assertion matched via (*ACCEPT).
+
+ (e) Mark values were not being passed out of recursions.
+
+ (f) Refactor some code in do_callout() to avoid picky compiler warnings about
+ negative indices. Fixes oss-fuzz issue 1454.
+
+ (g) Similarly refactor the way the variable length ovector is addressed for
+ similar reasons. Fixes oss-fuzz issue 1465.
+
+2. Now that pcre2_match() no longer uses recursive function calls (see above),
+the "match limit recursion" value seems misnamed. It still exists, and limits
+the depth of tree that is searched. To avoid future confusion, it has been
+renamed as "depth limit" in all relevant places (--with-depth-limit,
+(*LIMIT_DEPTH), pcre2_set_depth_limit(), etc) but the old names are still
+available for backwards compatibility.
+
+3. Hardened pcre2test so as to reduce the number of bugs reported by fuzzers:
+
+ (a) Check for malloc failures when getting memory for the ovector (POSIX) or
+ the match data block (non-POSIX).
+
+4. In the 32-bit library in non-UTF mode, an attempt to find a Unicode property
+for a character with a code point greater than 0x10ffff (the Unicode maximum)
+caused a crash.
+
+5. If a lookbehind assertion that contained a back reference to a group
+appearing later in the pattern was compiled with the PCRE2_ANCHORED option,
+undefined actions (often a segmentation fault) could occur, depending on what
+other options were set. An example assertion is (?<!\1(abc)) where the
+reference \1 precedes the group (abc). This fixes oss-fuzz issue 865.
+
+6. Added the PCRE2_INFO_FRAMESIZE item to pcre2_pattern_info() and arranged for
+pcre2test to use it to output the frame size when the "framesize" modifier is
+given.
+
+7. Reworked the recursive pattern matching in the JIT compiler to follow the
+interpreter changes.
+
+8. When the zero_terminate modifier was specified on a pcre2test subject line
+for global matching, unpredictable things could happen. For example, in UTF-8
+mode, the pattern //g,zero_terminate read random memory when matched against an
+empty string with zero_terminate. This was a bug in pcre2test, not the library.
+
+9. Moved some Windows-specific code in pcre2grep (introduced in 10.23/13) out
+of the section that is compiled when Unix-style directory scanning is
+available, and into a new section that is always compiled for Windows.
+
+10. In pcre2test, explicitly close the file after an error during serialization
+or deserialization (the "load" or "save" commands).
+
+11. Fix memory leak in pcre2_serialize_decode() when the input is invalid.
+
+12. Fix potential NULL dereference in pcre2_callout_enumerate() if called with
+a NULL pattern pointer when Unicode support is available.
+
+13. When the 32-bit library was being tested by pcre2test, error messages that
+were longer than 64 code units could cause a buffer overflow. This was a bug in
+pcre2test.
+
+14. The alternative matching function, pcre2_dfa_match() misbehaved if it
+encountered a character class with a possessive repeat, for example [a-f]{3}+.
+
+15. The depth (formerly recursion) limit now applies to DFA matching (as
+of 10.23/36); pcre2test has been upgraded so that \=find_limits works with DFA
+matching to find the minimum value for this limit.
+
+16. Since 10.21, if pcre2_match() was called with a null context, default
+memory allocation functions were used instead of whatever was used when the
+pattern was compiled.
+
+17. Changes to the pcre2test "memory" modifier on a subject line. These apply
+only to pcre2_match():
+
+ (a) Warn if null_context is set on both pattern and subject, because the
+ memory details cannot then be shown.
+
+ (b) Remember (up to a certain number of) memory allocations and their
+ lengths, and list only the lengths, so as to be system-independent.
+ (In practice, the new interpreter never has more than 2 blocks allocated
+ simultaneously.)
+
+18. Make pcre2test detect an error return from pcre2_get_error_message(), give
+a message, and abandon the run (this would have detected #13 above).
+
+19. Implemented PCRE2_ENDANCHORED.
+
+20. Applied Jason Hood's patches (slightly modified) to pcre2grep, to implement
+the --output=text (-O) option and the inbuilt callout echo.
+
+21. Extend auto-anchoring etc. to ignore groups with a zero qualifier and
+single-branch conditions with a false condition (e.g. DEFINE) at the start of a
+branch. For example, /(?(DEFINE)...)^A/ and /(...){0}^B/ are now flagged as
+anchored.
+
+22. Added an explicit limit on the amount of heap used by pcre2_match(), set by
+pcre2_set_heap_limit() or (*LIMIT_HEAP=xxx). Upgraded pcre2test to show the
+heap limit along with other pattern information, and to find the minimum when
+the find_limits modifier is set.
+
+23. Write to the last 8 bytes of the pcre2_real_code structure when a compiled
+pattern is set up so as to initialize any padding the compiler might have
+included. This avoids valgrind warnings when a compiled pattern is copied, in
+particular when it is serialized.
+
+24. Remove a redundant line of code left in accidentally a long time ago.
+
+25. Remove a duplication typo in pcre2_tables.c
+
+26. Correct an incorrect cast in pcre2_valid_utf.c
+
+27. Update pcre2test, remove some unused code in pcre2_match(), and upgrade the
+tests to improve coverage.
+
+28. Some fixes/tidies as a result of looking at Coverity Scan output:
+
+ (a) Typo: ">" should be ">=" in opcode check in pcre2_auto_possess.c.
+ (b) Added some casts to avoid "suspicious implicit sign extension".
+ (c) Resource leaks in pcre2test in rare error cases.
+ (d) Avoid warning for never-use case OP_TABLE_LENGTH which is just a fudge
+ for checking at compile time that tables are the right size.
+ (e) Add missing "fall through" comment.
+
+29. Implemented PCRE2_EXTENDED_MORE and related /xx and (?xx) features.
+
+30. Implement (?n: for PCRE2_NO_AUTO_CAPTURE, because Perl now has this.
+
+31. If more than one of "push", "pushcopy", or "pushtablescopy" were set in
+pcre2test, a crash could occur.
+
+32. Make -bigstack in RunTest allocate a 64MiB stack (instead of 16MiB) so
+that all the tests can run with clang's sanitizing options.
+
+33. Implement extra compile options in the compile context and add the first
+one: PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES.
+
+34. Implement newline type PCRE2_NEWLINE_NUL.
+
+35. A lookbehind assertion that had a zero-length branch caused undefined
+behaviour when processed by pcre2_dfa_match(). This is oss-fuzz issue 1859.
+
+36. The match limit value now also applies to pcre2_dfa_match() as there are
+patterns that can use up a lot of resources without necessarily recursing very
+deeply. (Compare item 10.23/36.) This should fix oss-fuzz #1761.
+
+37. Implement PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL.
+
+38. Fix returned offsets from regexec() when REG_STARTEND is used with a
+starting offset greater than zero.
+
+39. Implement REG_PEND (GNU extension) for the POSIX wrapper.
+
+40. Implement the subject_literal modifier in pcre2test, and allow jitstack on
+pattern lines.
+
+41. Implement PCRE2_LITERAL and use it to support REG_NOSPEC.
+
+42. Implement PCRE2_EXTRA_MATCH_LINE and PCRE2_EXTRA_MATCH_WORD for the benefit
+of pcre2grep.
+
+43. Re-implement pcre2grep's -F, -w, and -x options using PCRE2_LITERAL,
+PCRE2_EXTRA_MATCH_WORD, and PCRE2_EXTRA_MATCH_LINE. This fixes two bugs:
+
+ (a) The -F option did not work for fixed strings containing \E.
+ (b) The -w option did not work for patterns with multiple branches.
+
+44. Added configuration options for the SELinux compatible execmem allocator in
+JIT.
+
+45. Increased the limit for searching for a "must be present" code unit in
+subjects from 1000 to 2000 for 8-bit searches, since they use memchr() and are
+much faster.
+
+46. Arrange for anchored patterns to record and use "first code unit" data,
+because this can give a fast "no match" without searching for a "required code
+unit". Previously only non-anchored patterns did this.
+
+47. Upgraded the Unicode tables from Unicode 8.0.0 to Unicode 10.0.0.
+
+48. Add the callout_no_where modifier to pcre2test.
+
+49. Update extended grapheme breaking rules to the latest set that are in
+Unicode Standard Annex #29.
+
+50. Added experimental foreign pattern conversion facilities
+(pcre2_pattern_convert() and friends).
+
+51. Change the macro FWRITE, used in pcre2grep, to FWRITE_IGNORE because FWRITE
+is defined in a system header in cygwin. Also modified some of the #ifdefs in
+pcre2grep related to Windows and Cygwin support.
+
+52. Change 3(g) for 10.23 was a bit too zealous. If a hyphen that follows a
+character class is the last character in the class, Perl does not give a
+warning. PCRE2 now also treats this as a literal.
+
+53. Related to 52, though PCRE2 was throwing an error for [[:digit:]-X] it was
+not doing so for [\d-X] (and similar escapes), as is documented.
+
+54. Fixed a MIPS issue in the JIT compiler reported by Joshua Kinard.
+
+55. Fixed a "maybe uninitialized" warning for class_uchardata in \p handling in
+pcre2_compile() which could never actually trigger (code should have been cut
+out when Unicode support is disabled).
+
+
+Version 10.23 14-February-2017
+------------------------------
+
+1. Extended pcre2test with the utf8_input modifier so that it is able to
+generate all possible 16-bit and 32-bit code unit values in non-UTF modes.
+
+2. In any wide-character mode (8-bit UTF or any 16-bit or 32-bit mode), without
+PCRE2_UCP set, a negative character type such as \D in a positive class should
+cause all characters greater than 255 to match, whatever else is in the class.
+There was a bug that caused this not to happen if a Unicode property item was
+added to such a class, for example [\D\P{Nd}] or [\W\pL].
+
+3. There has been a major re-factoring of the pcre2_compile.c file. Most syntax
+checking is now done in the pre-pass that identifies capturing groups. This has
+reduced the amount of duplication and made the code tidier. While doing this,
+some minor bugs and Perl incompatibilities were fixed, including:
+
+ (a) \Q\E in the middle of a quantifier such as A+\Q\E+ is now ignored instead
+ of giving an invalid quantifier error.
+
+ (b) {0} can now be used after a group in a lookbehind assertion; previously
+ this caused an "assertion is not fixed length" error.
+
+ (c) Perl always treats (?(DEFINE) as a "define" group, even if a group with
+ the name "DEFINE" exists. PCRE2 now does likewise.
+
+ (d) A recursion condition test such as (?(R2)...) must now refer to an
+ existing subpattern.
+
+ (e) A conditional recursion test such as (?(R)...) misbehaved if there was a
+ group whose name began with "R".
+
+ (f) When testing zero-terminated patterns under valgrind, the terminating
+ zero is now marked "no access". This catches bugs that would otherwise
+ show up only with non-zero-terminated patterns.
+
+ (g) A hyphen appearing immediately after a POSIX character class (for example
+ /[[:ascii:]-z]/) now generates an error. Perl does accept this as a
+ literal, but gives a warning, so it seems best to fail it in PCRE.
+
+ (h) An empty \Q\E sequence may appear after a callout that precedes an
+ assertion condition (it is, of course, ignored).
+
+One effect of the refactoring is that some error numbers and messages have
+changed, and the pattern offset given for compiling errors is not always the
+right-most character that has been read. In particular, for a variable-length
+lookbehind assertion it now points to the start of the assertion. Another
+change is that when a callout appears before a group, the "length of next
+pattern item" that is passed now just gives the length of the opening
+parenthesis item, not the length of the whole group. A length of zero is now
+given only for a callout at the end of the pattern. Automatic callouts are no
+longer inserted before and after explicit callouts in the pattern.
+
+A number of bugs in the refactored code were subsequently fixed during testing
+before release, but after the code was made available in the repository. Many
+of the bugs were discovered by fuzzing testing. Several of them were related to
+the change from assuming a zero-terminated pattern (which previously had
+required non-zero terminated strings to be copied). These bugs were never in
+fully released code, but are noted here for the record.
+
+ (a) An overall recursion such as (?0) inside a lookbehind assertion was not
+ being diagnosed as an error.
+
+ (b) In utf mode, the length of a *MARK (or other verb) name was being checked
+ in characters instead of code units, which could lead to bad code being
+ compiled, leading to unpredictable behaviour.
+
+ (c) In extended /x mode, characters whose code was greater than 255 caused
+ a lookup outside one of the global tables. A similar bug existed for wide
+ characters in *VERB names.
+
+ (d) The amount of memory needed for a compiled pattern was miscalculated if a
+ lookbehind contained more than one toplevel branch and the first branch
+ was of length zero.
+
+ (e) In UTF-8 or UTF-16 modes with PCRE2_EXTENDED (/x) set and a non-zero-
+ terminated pattern, if a # comment ran on to the end of the pattern, one
+ or more code units past the end were being read.
+
+ (f) An unterminated repeat at the end of a non-zero-terminated pattern (e.g.
+ "{2,2") could cause reading beyond the pattern.
+
+ (g) When reading a callout string, if the end delimiter was at the end of the
+ pattern one further code unit was read.
+
+ (h) An unterminated number after \g' could cause reading beyond the pattern.
+
+ (i) An insufficient memory size was being computed for compiling with
+ PCRE2_AUTO_CALLOUT.
+
+ (j) A conditional group with an assertion condition used more memory than was
+ allowed for it during parsing, so too many of them could therefore
+ overrun a buffer.
+
+ (k) If parsing a pattern exactly filled the buffer, the internal test for
+ overrun did not check when the final META_END item was added.
+
+ (l) If a lookbehind contained a subroutine call, and the called group
+ contained an option setting such as (?s), and the PCRE2_ANCHORED option
+ was set, unpredictable behaviour could occur. The underlying bug was
+ incorrect code and insufficient checking while searching for the end of
+ the called subroutine in the parsed pattern.
+
+ (m) Quantifiers following (*VERB)s were not being diagnosed as errors.
+
+ (n) The use of \Q...\E in a (*VERB) name when PCRE2_ALT_VERBNAMES and
+ PCRE2_AUTO_CALLOUT were both specified caused undetermined behaviour.
+
+ (o) If \Q was preceded by a quantified item, and the following \E was
+ followed by '?' or '+', and there was at least one literal character
+ between them, an internal error "unexpected repeat" occurred (example:
+ /.+\QX\E+/).
+
+ (p) A buffer overflow could occur while sorting the names in the group name
+ list (depending on the order in which the names were seen).
+
+ (q) A conditional group that started with a callout was not doing the right
+ check for a following assertion, leading to compiling bad code. Example:
+ /(?(C'XX))?!XX/
+
+ (r) If a character whose code point was greater than 0xffff appeared within
+ a lookbehind that was within another lookbehind, the calculation of the
+ lookbehind length went wrong and could provoke an internal error.
+
+ (t) The sequence \E- or \Q\E- after a POSIX class in a character class caused
+ an internal error. Now the hyphen is treated as a literal.
+
+4. Back references are now permitted in lookbehind assertions when there are
+no duplicated group numbers (that is, (?| has not been used), and, if the
+reference is by name, there is only one group of that name. The referenced
+group must, of course be of fixed length.
+
+5. pcre2test has been upgraded so that, when run under valgrind with valgrind
+support enabled, reading past the end of the pattern is detected, both when
+compiling and during callout processing.
+
+6. \g{+<number>} (e.g. \g{+2} ) is now supported. It is a "forward back
+reference" and can be useful in repetitions (compare \g{-<number>} ). Perl does
+not recognize this syntax.
+
+7. Automatic callouts are no longer generated before and after callouts in the
+pattern.
+
+8. When pcre2test was outputing information from a callout, the caret indicator
+for the current position in the subject line was incorrect if it was after an
+escape sequence for a character whose code point was greater than \x{ff}.
+
+9. Change 19 for 10.22 had a typo (PCRE_STATIC_RUNTIME should be
+PCRE2_STATIC_RUNTIME). Fix from David Gaussmann.
+
+10. Added --max-buffer-size to pcre2grep, to allow for automatic buffer
+expansion when long lines are encountered. Original patch by Dmitry
+Cherniachenko.
+
+11. If pcre2grep was compiled with JIT support, but the library was compiled
+without it (something that neither ./configure nor CMake allow, but it can be
+done by editing config.h), pcre2grep was giving a JIT error. Now it detects
+this situation and does not try to use JIT.
+
+12. Added some "const" qualifiers to variables in pcre2grep.
+
+13. Added Dmitry Cherniachenko's patch for colouring output in Windows
+(untested by me). Also, look for GREP_COLOUR or GREP_COLOR if the environment
+variables PCRE2GREP_COLOUR and PCRE2GREP_COLOR are not found.
+
+14. Add the -t (grand total) option to pcre2grep.
+
+15. A number of bugs have been mended relating to match start-up optimizations
+when the first thing in a pattern is a positive lookahead. These all applied
+only when PCRE2_NO_START_OPTIMIZE was *not* set:
+
+ (a) A pattern such as (?=.*X)X$ was incorrectly optimized as if it needed
+ both an initial 'X' and a following 'X'.
+ (b) Some patterns starting with an assertion that started with .* were
+ incorrectly optimized as having to match at the start of the subject or
+ after a newline. There are cases where this is not true, for example,
+ (?=.*[A-Z])(?=.{8,16})(?!.*[\s]) matches after the start in lines that
+ start with spaces. Starting .* in an assertion is no longer taken as an
+ indication of matching at the start (or after a newline).
+
+16. The "offset" modifier in pcre2test was not being ignored (as documented)
+when the POSIX API was in use.
+
+17. Added --enable-fuzz-support to "configure", causing an non-installed
+library containing a test function that can be called by fuzzers to be
+compiled. A non-installed binary to run the test function locally, called
+pcre2fuzzcheck is also compiled.
+
+18. A pattern with PCRE2_DOTALL (/s) set but not PCRE2_NO_DOTSTAR_ANCHOR, and
+which started with .* inside a positive lookahead was incorrectly being
+compiled as implicitly anchored.
+
+19. Removed all instances of "register" declarations, as they are considered
+obsolete these days and in any case had become very haphazard.
+
+20. Add strerror() to pcre2test for failed file opening.
+
+21. Make pcre2test -C list valgrind support when it is enabled.
+
+22. Add the use_length modifier to pcre2test.
+
+23. Fix an off-by-one bug in pcre2test for the list of names for 'get' and
+'copy' modifiers.
+
+24. Add PCRE2_CALL_CONVENTION into the prototype declarations in pcre2.h as it
+is apparently needed there as well as in the function definitions. (Why did
+nobody ask for this in PCRE1?)
+
+25. Change the _PCRE2_H and _PCRE2_UCP_H guard macros in the header files to
+PCRE2_H_IDEMPOTENT_GUARD and PCRE2_UCP_H_IDEMPOTENT_GUARD to be more standard
+compliant and unique.
+
+26. pcre2-config --libs-posix was listing -lpcre2posix instead of
+-lpcre2-posix. Also, the CMake build process was building the library with the
+wrong name.
+
+27. In pcre2test, give some offset information for errors in hex patterns.
+This uses the C99 formatting sequence %td, except for MSVC which doesn't
+support it - %lu is used instead.
+
+28. Implemented pcre2_code_copy_with_tables(), and added pushtablescopy to
+pcre2test for testing it.
+
+29. Fix small memory leak in pcre2test.
+
+30. Fix out-of-bounds read for partial matching of /./ against an empty string
+when the newline type is CRLF.
+
+31. Fix a bug in pcre2test that caused a crash when a locale was set either in
+the current pattern or a previous one and a wide character was matched.
+
+32. The appearance of \p, \P, or \X in a substitution string when
+PCRE2_SUBSTITUTE_EXTENDED was set caused a segmentation fault (NULL
+dereference).
+
+33. If the starting offset was specified as greater than the subject length in
+a call to pcre2_substitute() an out-of-bounds memory reference could occur.
+
+34. When PCRE2 was compiled to use the heap instead of the stack for recursive
+calls to match(), a repeated minimizing caseless back reference, or a
+maximizing one where the two cases had different numbers of code units,
+followed by a caseful back reference, could lose the caselessness of the first
+repeated back reference (example: /(Z)(a)\2{1,2}?(?-i)\1X/i should match ZaAAZX
+but didn't).
+
+35. When a pattern is too complicated, PCRE2 gives up trying to find a minimum
+matching length and just records zero. Typically this happens when there are
+too many nested or recursive back references. If the limit was reached in
+certain recursive cases it failed to be triggered and an internal error could
+be the result.
+
+36. The pcre2_dfa_match() function now takes note of the recursion limit for
+the internal recursive calls that are used for lookrounds and recursions within
+the pattern.
+
+37. More refactoring has got rid of the internal could_be_empty_branch()
+function (around 400 lines of code, including comments) by keeping track of
+could-be-emptiness as the pattern is compiled instead of scanning compiled
+groups. (This would have been much harder before the refactoring of #3 above.)
+This lifts a restriction on the number of branches in a group (more than about
+1100 would give "pattern is too complicated").
+
+38. Add the "-ac" command line option to pcre2test as a synonym for "-pattern
+auto_callout".
+
+39. In a library with Unicode support, incorrect data was compiled for a
+pattern with PCRE2_UCP set without PCRE2_UTF if a class required all wide
+characters to match (for example, /[\s[:^ascii:]]/).
+
+40. The callout_error modifier has been added to pcre2test to make it possible
+to return PCRE2_ERROR_CALLOUT from a callout.
+
+41. A minor change to pcre2grep: colour reset is now "<esc>[0m" instead of
+"<esc>[00m".
+
+42. The limit in the auto-possessification code that was intended to catch
+overly-complicated patterns and not spend too much time auto-possessifying was
+being reset too often, resulting in very long compile times for some patterns.
+Now such patterns are no longer completely auto-possessified.
+
+43. Applied Jason Hood's revised patch for RunTest.bat.
+
+44. Added a new Windows script RunGrepTest.bat, courtesy of Jason Hood.
+
+45. Minor cosmetic fix to pcre2test: move a variable that is not used under
+Windows into the "not Windows" code.
+
+46. Applied Jason Hood's patches to upgrade pcre2grep under Windows and tidy
+some of the code:
+
+ * normalised the Windows condition by ensuring WIN32 is defined;
+ * enables the callout feature under Windows;
+ * adds globbing (Microsoft's implementation expands quoted args),
+ using a tweaked opendirectory;
+ * implements the is_*_tty functions for Windows;
+ * --color=always will write the ANSI sequences to file;
+ * add sequences 4 (underline works on Win10) and 5 (blink as bright
+ background, relatively standard on DOS/Win);
+ * remove the (char *) casts for the now-const strings;
+ * remove GREP_COLOUR (grep's command line allowed the 'u', but not
+ the environment), parsing GREP_COLORS instead;
+ * uses the current colour if not set, rather than black;
+ * add print_match for the undefined case;
+ * fixes a typo.
+
+In addition, colour settings containing anything other than digits and
+semicolon are ignored, and the colour controls are no longer output for empty
+strings.
+
+47. Detecting patterns that are too large inside the length-measuring loop
+saves processing ridiculously long patterns to their end.
+
+48. Ignore PCRE2_CASELESS when processing \h, \H, \v, and \V in classes as it
+just wastes time. In the UTF case it can also produce redundant entries in
+XCLASS lists caused by characters with multiple other cases and pairs of
+characters in the same "not-x" sublists.
+
+49. A pattern such as /(?=(a\K))/ can report the end of the match being before
+its start; pcre2test was not handling this correctly when using the POSIX
+interface (it was OK with the native interface).
+
+50. In pcre2grep, ignore all JIT compile errors. This means that pcre2grep will
+continue to work, falling back to interpretation if anything goes wrong with
+JIT.
+
+51. Applied patches from Christian Persch to configure.ac to make use of the
+AC_USE_SYSTEM_EXTENSIONS macro and to test for functions used by the JIT
+modules.
+
+52. Minor fixes to pcre2grep from Jason Hood:
+ * fixed some spacing;
+ * Windows doesn't usually use single quotes, so I've added a define
+ to use appropriate quotes [in an example];
+ * LC_ALL was displayed as "LCC_ALL";
+ * numbers 11, 12 & 13 should end in "th";
+ * use double quotes in usage message.
+
+53. When autopossessifying, skip empty branches without recursion, to reduce
+stack usage for the benefit of clang with -fsanitize-address, which uses huge
+stack frames. Example pattern: /X?(R||){3335}/. Fixes oss-fuzz issue 553.
+
+54. A pattern with very many explicit back references to a group that is a long
+way from the start of the pattern could take a long time to compile because
+searching for the referenced group in order to find the minimum length was
+being done repeatedly. Now up to 128 group minimum lengths are cached and the
+attempt to find a minimum length is abandoned if there is a back reference to a
+group whose number is greater than 128. (In that case, the pattern is so
+complicated that this optimization probably isn't worth it.) This fixes
+oss-fuzz issue 557.
+
+55. Issue 32 for 10.22 below was not correctly fixed. If pcre2grep in multiline
+mode with --only-matching matched several lines, it restarted scanning at the
+next line instead of moving on to the end of the matched string, which can be
+several lines after the start.
+
+56. Applied Jason Hood's new patch for RunGrepTest.bat that updates it in line
+with updates to the non-Windows version.
+
+
+
+Version 10.22 29-July-2016
+--------------------------
+
+1. Applied Jason Hood's patches to RunTest.bat and testdata/wintestoutput3
+to fix problems with running the tests under Windows.
+
+2. Implemented a facility for quoting literal characters within hexadecimal
+patterns in pcre2test, to make it easier to create patterns with just a few
+non-printing characters.
+
+3. Binary zeros are not supported in pcre2test input files. It now detects them
+and gives an error.
+
+4. Updated the valgrind parameters in RunTest: (a) changed smc-check=all to
+smc-check=all-non-file; (b) changed obj:* in the suppression file to obj:??? so
+that it matches only unknown objects.
+
+5. Updated the maintenance script maint/ManyConfigTests to make it easier to
+select individual groups of tests.
+
+6. When the POSIX wrapper function regcomp() is called, the REG_NOSUB option
+used to set PCRE2_NO_AUTO_CAPTURE when calling pcre2_compile(). However, this
+disables the use of back references (and subroutine calls), which are supported
+by other implementations of regcomp() with RE_NOSUB. Therefore, REG_NOSUB no
+longer causes PCRE2_NO_AUTO_CAPTURE to be set, though it still ignores nmatch
+and pmatch when regexec() is called.
+
+7. Because of 6 above, pcre2test has been modified with a new modifier called
+posix_nosub, to call regcomp() with REG_NOSUB. Previously the no_auto_capture
+modifier had this effect. That option is now ignored when the POSIX API is in
+use.
+
+8. Minor tidies to the pcre2demo.c sample program, including more comments
+about its 8-bit-ness.
+
+9. Detect unmatched closing parentheses and give the error in the pre-scan
+instead of later. Previously the pre-scan carried on and could give a
+misleading incorrect error message. For example, /(?J)(?'a'))(?'a')/ gave a
+message about invalid duplicate group names.
+
+10. It has happened that pcre2test was accidentally linked with another POSIX
+regex library instead of libpcre2-posix. In this situation, a call to regcomp()
+(in the other library) may succeed, returning zero, but of course putting its
+own data into the regex_t block. In one example the re_pcre2_code field was
+left as NULL, which made pcre2test think it had not got a compiled POSIX regex,
+so it treated the next line as another pattern line, resulting in a confusing
+error message. A check has been added to pcre2test to see if the data returned
+from a successful call of regcomp() are valid for PCRE2's regcomp(). If they
+are not, an error message is output and the pcre2test run is abandoned. The
+message points out the possibility of a mis-linking. Hopefully this will avoid
+some head-scratching the next time this happens.
+
+11. A pattern such as /(?<=((?C)0))/, which has a callout inside a lookbehind
+assertion, caused pcre2test to output a very large number of spaces when the
+callout was taken, making the program appearing to loop.
+
+12. A pattern that included (*ACCEPT) in the middle of a sufficiently deeply
+nested set of parentheses of sufficient size caused an overflow of the
+compiling workspace (which was diagnosed, but of course is not desirable).
+
+13. Detect missing closing parentheses during the pre-pass for group
+identification.
+
+14. Changed some integer variable types and put in a number of casts, following
+a report of compiler warnings from Visual Studio 2013 and a few tests with
+gcc's -Wconversion (which still throws up a lot).
+
+15. Implemented pcre2_code_copy(), and added pushcopy and #popcopy to pcre2test
+for testing it.
+
+16. Change 66 for 10.21 introduced the use of snprintf() in PCRE2's version of
+regerror(). When the error buffer is too small, my version of snprintf() puts a
+binary zero in the final byte. Bug #1801 seems to show that other versions do
+not do this, leading to bad output from pcre2test when it was checking for
+buffer overflow. It no longer assumes a binary zero at the end of a too-small
+regerror() buffer.
+
+17. Fixed typo ("&&" for "&") in pcre2_study(). Fortunately, this could not
+actually affect anything, by sheer luck.
+
+18. Two minor fixes for MSVC compilation: (a) removal of apparently incorrect
+"const" qualifiers in pcre2test and (b) defining snprintf as _snprintf for
+older MSVC compilers. This has been done both in src/pcre2_internal.h for most
+of the library, and also in src/pcre2posix.c, which no longer includes
+pcre2_internal.h (see 24 below).
+
+19. Applied Chris Wilson's patch (Bugzilla #1681) to CMakeLists.txt for MSVC
+static compilation. Subsequently applied Chris Wilson's second patch, putting
+the first patch under a new option instead of being unconditional when
+PCRE_STATIC is set.
+
+20. Updated pcre2grep to set stdout as binary when run under Windows, so as not
+to convert \r\n at the ends of reflected lines into \r\r\n. This required
+ensuring that other output that is written to stdout (e.g. file names) uses the
+appropriate line terminator: \r\n for Windows, \n otherwise.
+
+21. When a line is too long for pcre2grep's internal buffer, show the maximum
+length in the error message.
+
+22. Added support for string callouts to pcre2grep (Zoltan's patch with PH
+additions).
+
+23. RunTest.bat was missing a "set type" line for test 22.
+
+24. The pcre2posix.c file was including pcre2_internal.h, and using some
+"private" knowledge of the data structures. This is unnecessary; the code has
+been re-factored and no longer includes pcre2_internal.h.
+
+25. A racing condition is fixed in JIT reported by Mozilla.
+
+26. Minor code refactor to avoid "array subscript is below array bounds"
+compiler warning.
+
+27. Minor code refactor to avoid "left shift of negative number" warning.
+
+28. Add a bit more sanity checking to pcre2_serialize_decode() and document
+that it expects trusted data.
+
+29. Fix typo in pcre2_jit_test.c
+
+30. Due to an oversight, pcre2grep was not making use of JIT when available.
+This is now fixed.
+
+31. The RunGrepTest script is updated to use the valgrind suppressions file
+when testing with JIT under valgrind (compare 10.21/51 below). The suppressions
+file is updated so that is now the same as for PCRE1: it suppresses the
+Memcheck warnings Addr16 and Cond in unknown objects (that is, JIT-compiled
+code). Also changed smc-check=all to smc-check=all-non-file as was done for
+RunTest (see 4 above).
+
+32. Implemented the PCRE2_NO_JIT option for pcre2_match().
+
+33. Fix typo that gave a compiler error when JIT not supported.
+
+34. Fix comment describing the returns from find_fixedlength().
+
+35. Fix potential negative index in pcre2test.
+
+36. Calls to pcre2_get_error_message() with error numbers that are never
+returned by PCRE2 functions were returning empty strings. Now the error code
+PCRE2_ERROR_BADDATA is returned. A facility has been added to pcre2test to
+show the texts for given error numbers (i.e. to call pcre2_get_error_message()
+and display what it returns) and a few representative error codes are now
+checked in RunTest.
+
+37. Added "&& !defined(__INTEL_COMPILER)" to the test for __GNUC__ in
+pcre2_match.c, in anticipation that this is needed for the same reason it was
+recently added to pcrecpp.cc in PCRE1.
+
+38. Using -o with -M in pcre2grep could cause unnecessary repeated output when
+the match extended over a line boundary, as it tried to find more matches "on
+the same line" - but it was already over the end.
+
+39. Allow \C in lookbehinds and DFA matching in UTF-32 mode (by converting it
+to the same code as '.' when PCRE2_DOTALL is set).
+
+40. Fix two clang compiler warnings in pcre2test when only one code unit width
+is supported.
+
+41. Upgrade RunTest to automatically re-run test 2 with a large (64MiB) stack
+if it fails when running the interpreter with a 16MiB stack (and if changing
+the stack size via pcre2test is possible). This avoids having to manually set a
+large stack size when testing with clang.
+
+42. Fix register overwite in JIT when SSE2 acceleration is enabled.
+
+43. Detect integer overflow in pcre2test pattern and data repetition counts.
+
+44. In pcre2test, ignore "allcaptures" after DFA matching.
+
+45. Fix unaligned accesses on x86. Patch by Marc Mutz.
+
+46. Fix some more clang compiler warnings.
+
+
+Version 10.21 12-January-2016
+-----------------------------
+
+1. Improve matching speed of patterns starting with + or * in JIT.
+
+2. Use memchr() to find the first character in an unanchored match in 8-bit
+mode in the interpreter. This gives a significant speed improvement.
+
+3. Removed a redundant copy of the opcode_possessify table in the
+pcre2_auto_possessify.c source.
+
+4. Fix typos in dftables.c for z/OS.
+
+5. Change 36 for 10.20 broke the handling of [[:>:]] and [[:<:]] in that
+processing them could involve a buffer overflow if the following character was
+an opening parenthesis.
+
+6. Change 36 for 10.20 also introduced a bug in processing this pattern:
+/((?x)(*:0))#(?'/. Specifically: if a setting of (?x) was followed by a (*MARK)
+setting (which (*:0) is), then (?x) did not get unset at the end of its group
+during the scan for named groups, and hence the external # was incorrectly
+treated as a comment and the invalid (?' at the end of the pattern was not
+diagnosed. This caused a buffer overflow during the real compile. This bug was
+discovered by Karl Skomski with the LLVM fuzzer.
+
+7. Moved the pcre2_find_bracket() function from src/pcre2_compile.c into its
+own source module to avoid a circular dependency between src/pcre2_compile.c
+and src/pcre2_study.c
+
+8. A callout with a string argument containing an opening square bracket, for
+example /(?C$[$)(?<]/, was incorrectly processed and could provoke a buffer
+overflow. This bug was discovered by Karl Skomski with the LLVM fuzzer.
+
+9. The handling of callouts during the pre-pass for named group identification
+has been tightened up.
+
+10. The quantifier {1} can be ignored, whether greedy, non-greedy, or
+possessive. This is a very minor optimization.
+
+11. A possessively repeated conditional group that could match an empty string,
+for example, /(?(R))*+/, was incorrectly compiled.
+
+12. The Unicode tables have been updated to Unicode 8.0.0 (thanks to Christian
+Persch).
+
+13. An empty comment (?#) in a pattern was incorrectly processed and could
+provoke a buffer overflow. This bug was discovered by Karl Skomski with the
+LLVM fuzzer.
+
+14. Fix infinite recursion in the JIT compiler when certain patterns such as
+/(?:|a|){100}x/ are analysed.
+
+15. Some patterns with character classes involving [: and \\ were incorrectly
+compiled and could cause reading from uninitialized memory or an incorrect
+error diagnosis. Examples are: /[[:\\](?<[::]/ and /[[:\\](?'abc')[a:]. The
+first of these bugs was discovered by Karl Skomski with the LLVM fuzzer.
+
+16. Pathological patterns containing many nested occurrences of [: caused
+pcre2_compile() to run for a very long time. This bug was found by the LLVM
+fuzzer.
+
+17. A missing closing parenthesis for a callout with a string argument was not
+being diagnosed, possibly leading to a buffer overflow. This bug was found by
+the LLVM fuzzer.
+
+18. A conditional group with only one branch has an implicit empty alternative
+branch and must therefore be treated as potentially matching an empty string.
+
+19. If (?R was followed by - or + incorrect behaviour happened instead of a
+diagnostic. This bug was discovered by Karl Skomski with the LLVM fuzzer.
+
+20. Another bug that was introduced by change 36 for 10.20: conditional groups
+whose condition was an assertion preceded by an explicit callout with a string
+argument might be incorrectly processed, especially if the string contained \Q.
+This bug was discovered by Karl Skomski with the LLVM fuzzer.
+
+21. Compiling PCRE2 with the sanitize options of clang showed up a number of
+very pedantic coding infelicities and a buffer overflow while checking a UTF-8
+string if the final multi-byte UTF-8 character was truncated.
+
+22. For Perl compatibility in EBCDIC environments, ranges such as a-z in a
+class, where both values are literal letters in the same case, omit the
+non-letter EBCDIC code points within the range.
+
+23. Finding the minimum matching length of complex patterns with back
+references and/or recursions can take a long time. There is now a cut-off that
+gives up trying to find a minimum length when things get too complex.
+
+24. An optimization has been added that speeds up finding the minimum matching
+length for patterns containing repeated capturing groups or recursions.
+
+25. If a pattern contained a back reference to a group whose number was
+duplicated as a result of appearing in a (?|...) group, the computation of the
+minimum matching length gave a wrong result, which could cause incorrect "no
+match" errors. For such patterns, a minimum matching length cannot at present
+be computed.
+
+26. Added a check for integer overflow in conditions (?(<digits>) and
+(?(R<digits>). This omission was discovered by Karl Skomski with the LLVM
+fuzzer.
+
+27. Fixed an issue when \p{Any} inside an xclass did not read the current
+character.
+
+28. If pcre2grep was given the -q option with -c or -l, or when handling a
+binary file, it incorrectly wrote output to stdout.
+
+29. The JIT compiler did not restore the control verb head in case of *THEN
+control verbs. This issue was found by Karl Skomski with a custom LLVM fuzzer.
+
+30. The way recursive references such as (?3) are compiled has been re-written
+because the old way was the cause of many issues. Now, conversion of the group
+number into a pattern offset does not happen until the pattern has been
+completely compiled. This does mean that detection of all infinitely looping
+recursions is postponed till match time. In the past, some easy ones were
+detected at compile time. This re-writing was done in response to yet another
+bug found by the LLVM fuzzer.
+
+31. A test for a back reference to a non-existent group was missing for items
+such as \987. This caused incorrect code to be compiled. This issue was found
+by Karl Skomski with a custom LLVM fuzzer.
+
+32. Error messages for syntax errors following \g and \k were giving inaccurate
+offsets in the pattern.
+
+33. Improve the performance of starting single character repetitions in JIT.
+
+34. (*LIMIT_MATCH=) now gives an error instead of setting the value to 0.
+
+35. Error messages for syntax errors in *LIMIT_MATCH and *LIMIT_RECURSION now
+give the right offset instead of zero.
+
+36. The JIT compiler should not check repeats after a {0,1} repeat byte code.
+This issue was found by Karl Skomski with a custom LLVM fuzzer.
+
+37. The JIT compiler should restore the control chain for empty possessive
+repeats. This issue was found by Karl Skomski with a custom LLVM fuzzer.
+
+38. A bug which was introduced by the single character repetition optimization
+was fixed.
+
+39. Match limit check added to recursion. This issue was found by Karl Skomski
+with a custom LLVM fuzzer.
+
+40. Arrange for the UTF check in pcre2_match() and pcre2_dfa_match() to look
+only at the part of the subject that is relevant when the starting offset is
+non-zero.
+
+41. Improve first character match in JIT with SSE2 on x86.
+
+42. Fix two assertion fails in JIT. These issues were found by Karl Skomski
+with a custom LLVM fuzzer.
+
+43. Correct the setting of CMAKE_C_FLAGS in CMakeLists.txt (patch from Roy Ivy
+III).
+
+44. Fix bug in RunTest.bat for new test 14, and adjust the script for the added
+test (there are now 20 in total).
+
+45. Fixed a corner case of range optimization in JIT.
+
+46. Add the ${*MARK} facility to pcre2_substitute().
+
+47. Modifier lists in pcre2test were splitting at spaces without the required
+commas.
+
+48. Implemented PCRE2_ALT_VERBNAMES.
+
+49. Fixed two issues in JIT. These were found by Karl Skomski with a custom
+LLVM fuzzer.
+
+50. The pcre2test program has been extended by adding the #newline_default
+command. This has made it possible to run the standard tests when PCRE2 is
+compiled with either CR or CRLF as the default newline convention. As part of
+this work, the new command was added to several test files and the testing
+scripts were modified. The pcre2grep tests can now also be run when there is no
+LF in the default newline convention.
+
+51. The RunTest script has been modified so that, when JIT is used and valgrind
+is specified, a valgrind suppressions file is set up to ignore "Invalid read of
+size 16" errors because these are false positives when the hardware supports
+the SSE2 instruction set.
+
+52. It is now possible to have comment lines amid the subject strings in
+pcre2test (and perltest.sh) input.
+
+53. Implemented PCRE2_USE_OFFSET_LIMIT and pcre2_set_offset_limit().
+
+54. Add the null_context modifier to pcre2test so that calling pcre2_compile()
+and the matching functions with NULL contexts can be tested.
+
+55. Implemented PCRE2_SUBSTITUTE_EXTENDED.
+
+56. In a character class such as [\W\p{Any}] where both a negative-type escape
+("not a word character") and a property escape were present, the property
+escape was being ignored.
+
+57. Fixed integer overflow for patterns whose minimum matching length is very,
+very large.
+
+58. Implemented --never-backslash-C.
+
+59. Change 55 above introduced a bug by which certain patterns provoked the
+erroneous error "\ at end of pattern".
+
+60. The special sequences [[:<:]] and [[:>:]] gave rise to incorrect compiling
+errors or other strange effects if compiled in UCP mode. Found with libFuzzer
+and AddressSanitizer.
+
+61. Whitespace at the end of a pcre2test pattern line caused a spurious error
+message if there were only single-character modifiers. It should be ignored.
+
+62. The use of PCRE2_NO_AUTO_CAPTURE could cause incorrect compilation results
+or segmentation errors for some patterns. Found with libFuzzer and
+AddressSanitizer.
+
+63. Very long names in (*MARK) or (*THEN) etc. items could provoke a buffer
+overflow.
+
+64. Improve error message for overly-complicated patterns.
+
+65. Implemented an optional replication feature for patterns in pcre2test, to
+make it easier to test long repetitive patterns. The tests for 63 above are
+converted to use the new feature.
+
+66. In the POSIX wrapper, if regerror() was given too small a buffer, it could
+misbehave.
+
+67. In pcre2_substitute() in UTF mode, the UTF validity check on the
+replacement string was happening before the length setting when the replacement
+string was zero-terminated.
+
+68. In pcre2_substitute() in UTF mode, PCRE2_NO_UTF_CHECK can be set for the
+second and subsequent calls to pcre2_match().
+
+69. There was no check for integer overflow for a replacement group number in
+pcre2_substitute(). An added check for a number greater than the largest group
+number in the pattern means this is not now needed.
+
+70. The PCRE2-specific VERSION condition didn't work correctly if only one
+digit was given after the decimal point, or if more than two digits were given.
+It now works with one or two digits, and gives a compile time error if more are
+given.
+
+71. In pcre2_substitute() there was the possibility of reading one code unit
+beyond the end of the replacement string.
+
+72. The code for checking a subject's UTF-32 validity for a pattern with a
+lookbehind involved an out-of-bounds pointer, which could potentially cause
+trouble in some environments.
+
+73. The maximum lookbehind length was incorrectly calculated for patterns such
+as /(?<=(a)(?-1))x/ which have a recursion within a backreference.
+
+74. Give an error if a lookbehind assertion is longer than 65535 code units.
+
+75. Give an error in pcre2_substitute() if a match ends before it starts (as a
+result of the use of \K).
+
+76. Check the length of subpattern names and the names in (*MARK:xx) etc.
+dynamically to avoid the possibility of integer overflow.
+
+77. Implement pcre2_set_max_pattern_length() so that programs can restrict the
+size of patterns that they are prepared to handle.
+
+78. (*NO_AUTO_POSSESS) was not working.
+
+79. Adding group information caching improves the speed of compiling when
+checking whether a group has a fixed length and/or could match an empty string,
+especially when recursion or subroutine calls are involved. However, this
+cannot be used when (?| is present in the pattern because the same number may
+be used for groups of different sizes. To catch runaway patterns in this
+situation, counts have been introduced to the functions that scan for empty
+branches or compute fixed lengths.
+
+80. Allow for the possibility of the size of the nest_save structure not being
+a factor of the size of the compiling workspace (it currently is).
+
+81. Check for integer overflow in minimum length calculation and cap it at
+65535.
+
+82. Small optimizations in code for finding the minimum matching length.
+
+83. Lock out configuring for EBCDIC with non-8-bit libraries.
+
+84. Test for error code <= 0 in regerror().
+
+85. Check for too many replacements (more than INT_MAX) in pcre2_substitute().
+
+86. Avoid the possibility of computing with an out-of-bounds pointer (though
+not dereferencing it) while handling lookbehind assertions.
+
+87. Failure to get memory for the match data in regcomp() is now given as a
+regcomp() error instead of waiting for regexec() to pick it up.
+
+88. In pcre2_substitute(), ensure that CRLF is not split when it is a valid
+newline sequence.
+
+89. Paranoid check in regcomp() for bad error code from pcre2_compile().
+
+90. Run test 8 (internal offsets and code sizes) for link sizes 3 and 4 as well
+as for link size 2.
+
+91. Document that JIT has a limit on pattern size, and give more information
+about JIT compile failures in pcre2test.
+
+92. Implement PCRE2_INFO_HASBACKSLASHC.
+
+93. Re-arrange valgrind support code in pcre2test to avoid spurious reports
+with JIT (possibly caused by SSE2?).
+
+94. Support offset_limit in JIT.
+
+95. A sequence such as [[:punct:]b] that is, a POSIX character class followed
+by a single ASCII character in a class item, was incorrectly compiled in UCP
+mode. The POSIX class got lost, but only if the single character followed it.
+
+96. [:punct:] in UCP mode was matching some characters in the range 128-255
+that should not have been matched.
+
+97. If [:^ascii:] or [:^xdigit:] are present in a non-negated class, all
+characters with code points greater than 255 are in the class. When a Unicode
+property was also in the class (if PCRE2_UCP is set, escapes such as \w are
+turned into Unicode properties), wide characters were not correctly handled,
+and could fail to match.
+
+98. In pcre2test, make the "startoffset" modifier a synonym of "offset",
+because it sets the "startoffset" parameter for pcre2_match().
+
+99. If PCRE2_AUTO_CALLOUT was set on a pattern that had a (?# comment between
+an item and its qualifier (for example, A(?#comment)?B) pcre2_compile()
+misbehaved. This bug was found by the LLVM fuzzer.
+
+100. The error for an invalid UTF pattern string always gave the code unit
+offset as zero instead of where the invalidity was found.
+
+101. Further to 97 above, negated classes such as [^[:^ascii:]\d] were also not
+working correctly in UCP mode.
+
+102. Similar to 99 above, if an isolated \E was present between an item and its
+qualifier when PCRE2_AUTO_CALLOUT was set, pcre2_compile() misbehaved. This bug
+was found by the LLVM fuzzer.
+
+103. The POSIX wrapper function regexec() crashed if the option REG_STARTEND
+was set when the pmatch argument was NULL. It now returns REG_INVARG.
+
+104. Allow for up to 32-bit numbers in the ordin() function in pcre2grep.
+
+105. An empty \Q\E sequence between an item and its qualifier caused
+pcre2_compile() to misbehave when auto callouts were enabled. This bug
+was found by the LLVM fuzzer.
+
+106. If both PCRE2_ALT_VERBNAMES and PCRE2_EXTENDED were set, and a (*MARK) or
+other verb "name" ended with whitespace immediately before the closing
+parenthesis, pcre2_compile() misbehaved. Example: /(*:abc )/, but only when
+both those options were set.
+
+107. In a number of places pcre2_compile() was not handling NULL characters
+correctly, and pcre2test with the "bincode" modifier was not always correctly
+displaying fields containing NULLS:
+
+ (a) Within /x extended #-comments
+ (b) Within the "name" part of (*MARK) and other *verbs
+ (c) Within the text argument of a callout
+
+108. If a pattern that was compiled with PCRE2_EXTENDED started with white
+space or a #-type comment that was followed by (?-x), which turns off
+PCRE2_EXTENDED, and there was no subsequent (?x) to turn it on again,
+pcre2_compile() assumed that (?-x) applied to the whole pattern and
+consequently mis-compiled it. This bug was found by the LLVM fuzzer. The fix
+for this bug means that a setting of any of the (?imsxJU) options at the start
+of a pattern is no longer transferred to the options that are returned by
+PCRE2_INFO_ALLOPTIONS. In fact, this was an anachronism that should have
+changed when the effects of those options were all moved to compile time.
+
+109. An escaped closing parenthesis in the "name" part of a (*verb) when
+PCRE2_ALT_VERBNAMES was set caused pcre2_compile() to malfunction. This bug
+was found by the LLVM fuzzer.
+
+110. Implemented PCRE2_SUBSTITUTE_UNSET_EMPTY, and updated pcre2test to make it
+possible to test it.
+
+111. "Harden" pcre2test against ridiculously large values in modifiers and
+command line arguments.
+
+112. Implemented PCRE2_SUBSTITUTE_UNKNOWN_UNSET and PCRE2_SUBSTITUTE_OVERFLOW_
+LENGTH.
+
+113. Fix printing of *MARK names that contain binary zeroes in pcre2test.
+
+
+Version 10.20 30-June-2015
+--------------------------
+
+1. Callouts with string arguments have been added.
+
+2. Assertion code generator in JIT has been optimized.
+
+3. The invalid pattern (?(?C) has a missing assertion condition at the end. The
+pcre2_compile() function read past the end of the input before diagnosing an
+error. This bug was discovered by the LLVM fuzzer.
+
+4. Implemented pcre2_callout_enumerate().
+
+5. Fix JIT compilation of conditional blocks whose assertion is converted to
+(*FAIL). E.g: /(?(?!))/.
+
+6. The pattern /(?(?!)^)/ caused references to random memory. This bug was
+discovered by the LLVM fuzzer.
+
+7. The assertion (?!) is optimized to (*FAIL). This was not handled correctly
+when this assertion was used as a condition, for example (?(?!)a|b). In
+pcre2_match() it worked by luck; in pcre2_dfa_match() it gave an incorrect
+error about an unsupported item.
+
+8. For some types of pattern, for example /Z*(|d*){216}/, the auto-
+possessification code could take exponential time to complete. A recursion
+depth limit of 1000 has been imposed to limit the resources used by this
+optimization. This infelicity was discovered by the LLVM fuzzer.
+
+9. A pattern such as /(*UTF)[\S\V\H]/, which contains a negated special class
+such as \S in non-UCP mode, explicit wide characters (> 255) can be ignored
+because \S ensures they are all in the class. The code for doing this was
+interacting badly with the code for computing the amount of space needed to
+compile the pattern, leading to a buffer overflow. This bug was discovered by
+the LLVM fuzzer.
+
+10. A pattern such as /((?2)+)((?1))/ which has mutual recursion nested inside
+other kinds of group caused stack overflow at compile time. This bug was
+discovered by the LLVM fuzzer.
+
+11. A pattern such as /(?1)(?#?'){8}(a)/ which had a parenthesized comment
+between a subroutine call and its quantifier was incorrectly compiled, leading
+to buffer overflow or other errors. This bug was discovered by the LLVM fuzzer.
+
+12. The illegal pattern /(?(?<E>.*!.*)?)/ was not being diagnosed as missing an
+assertion after (?(. The code was failing to check the character after (?(?<
+for the ! or = that would indicate a lookbehind assertion. This bug was
+discovered by the LLVM fuzzer.
+
+13. A pattern such as /X((?2)()*+){2}+/ which has a possessive quantifier with
+a fixed maximum following a group that contains a subroutine reference was
+incorrectly compiled and could trigger buffer overflow. This bug was discovered
+by the LLVM fuzzer.
+
+14. Negative relative recursive references such as (?-7) to non-existent
+subpatterns were not being diagnosed and could lead to unpredictable behaviour.
+This bug was discovered by the LLVM fuzzer.
+
+15. The bug fixed in 14 was due to an integer variable that was unsigned when
+it should have been signed. Some other "int" variables, having been checked,
+have either been changed to uint32_t or commented as "must be signed".
+
+16. A mutual recursion within a lookbehind assertion such as (?<=((?2))((?1)))
+caused a stack overflow instead of the diagnosis of a non-fixed length
+lookbehind assertion. This bug was discovered by the LLVM fuzzer.
+
+17. The use of \K in a positive lookbehind assertion in a non-anchored pattern
+(e.g. /(?<=\Ka)/) could make pcre2grep loop.
+
+18. There was a similar problem to 17 in pcre2test for global matches, though
+the code there did catch the loop.
+
+19. If a greedy quantified \X was preceded by \C in UTF mode (e.g. \C\X*),
+and a subsequent item in the pattern caused a non-match, backtracking over the
+repeated \X did not stop, but carried on past the start of the subject, causing
+reference to random memory and/or a segfault. There were also some other cases
+where backtracking after \C could crash. This set of bugs was discovered by the
+LLVM fuzzer.
+
+20. The function for finding the minimum length of a matching string could take
+a very long time if mutual recursion was present many times in a pattern, for
+example, /((?2){73}(?2))((?1))/. A better mutual recursion detection method has
+been implemented. This infelicity was discovered by the LLVM fuzzer.
+
+21. Implemented PCRE2_NEVER_BACKSLASH_C.
+
+22. The feature for string replication in pcre2test could read from freed
+memory if the replication required a buffer to be extended, and it was not
+working properly in 16-bit and 32-bit modes. This issue was discovered by a
+fuzzer: see http://lcamtuf.coredump.cx/afl/.
+
+23. Added the PCRE2_ALT_CIRCUMFLEX option.
+
+24. Adjust the treatment of \8 and \9 to be the same as the current Perl
+behaviour.
+
+25. Static linking against the PCRE2 library using the pkg-config module was
+failing on missing pthread symbols.
+
+26. If a group that contained a recursive back reference also contained a
+forward reference subroutine call followed by a non-forward-reference
+subroutine call, for example /.((?2)(?R)\1)()/, pcre2_compile() failed to
+compile correct code, leading to undefined behaviour or an internally detected
+error. This bug was discovered by the LLVM fuzzer.
+
+27. Quantification of certain items (e.g. atomic back references) could cause
+incorrect code to be compiled when recursive forward references were involved.
+For example, in this pattern: /(?1)()((((((\1++))\x85)+)|))/. This bug was
+discovered by the LLVM fuzzer.
+
+28. A repeated conditional group whose condition was a reference by name caused
+a buffer overflow if there was more than one group with the given name. This
+bug was discovered by the LLVM fuzzer.
+
+29. A recursive back reference by name within a group that had the same name as
+another group caused a buffer overflow. For example: /(?J)(?'d'(?'d'\g{d}))/.
+This bug was discovered by the LLVM fuzzer.
+
+30. A forward reference by name to a group whose number is the same as the
+current group, for example in this pattern: /(?|(\k'Pm')|(?'Pm'))/, caused a
+buffer overflow at compile time. This bug was discovered by the LLVM fuzzer.
+
+31. Fix -fsanitize=undefined warnings for left shifts of 1 by 31 (it treats 1
+as an int; fixed by writing it as 1u).
+
+32. Fix pcre2grep compile when -std=c99 is used with gcc, though it still gives
+a warning for "fileno" unless -std=gnu99 us used.
+
+33. A lookbehind assertion within a set of mutually recursive subpatterns could
+provoke a buffer overflow. This bug was discovered by the LLVM fuzzer.
+
+34. Give an error for an empty subpattern name such as (?'').
+
+35. Make pcre2test give an error if a pattern that follows #forbud_utf contains
+\P, \p, or \X.
+
+36. The way named subpatterns are handled has been refactored. There is now a
+pre-pass over the regex which does nothing other than identify named
+subpatterns and count the total captures. This means that information about
+named patterns is known before the rest of the compile. In particular, it means
+that forward references can be checked as they are encountered. Previously, the
+code for handling forward references was contorted and led to several errors in
+computing the memory requirements for some patterns, leading to buffer
+overflows.
+
+37. There was no check for integer overflow in subroutine calls such as (?123).
+
+38. The table entry for \l in EBCDIC environments was incorrect, leading to its
+being treated as a literal 'l' instead of causing an error.
+
+39. If a non-capturing group containing a conditional group that could match
+an empty string was repeated, it was not identified as matching an empty string
+itself. For example: /^(?:(?(1)x|)+)+$()/.
+
+40. In an EBCDIC environment, pcretest was mishandling the escape sequences
+\a and \e in test subject lines.
+
+41. In an EBCDIC environment, \a in a pattern was converted to the ASCII
+instead of the EBCDIC value.
+
+42. The handling of \c in an EBCDIC environment has been revised so that it is
+now compatible with the specification in Perl's perlebcdic page.
+
+43. Single character repetition in JIT has been improved. 20-30% speedup
+was achieved on certain patterns.
+
+44. The EBCDIC character 0x41 is a non-breaking space, equivalent to 0xa0 in
+ASCII/Unicode. This has now been added to the list of characters that are
+recognized as white space in EBCDIC.
+
+45. When PCRE2 was compiled without Unicode support, the use of \p and \P gave
+an error (correctly) when used outside a class, but did not give an error
+within a class.
+
+46. \h within a class was incorrectly compiled in EBCDIC environments.
+
+47. JIT should return with error when the compiled pattern requires
+more stack space than the maximum.
+
+48. Fixed a memory leak in pcre2grep when a locale is set.
+
+
+Version 10.10 06-March-2015
+---------------------------
+
+1. When a pattern is compiled, it remembers the highest back reference so that
+when matching, if the ovector is too small, extra memory can be obtained to
+use instead. A conditional subpattern whose condition is a check on a capture
+having happened, such as, for example in the pattern /^(?:(a)|b)(?(1)A|B)/, is
+another kind of back reference, but it was not setting the highest
+backreference number. This mattered only if pcre2_match() was called with an
+ovector that was too small to hold the capture, and there was no other kind of
+back reference (a situation which is probably quite rare). The effect of the
+bug was that the condition was always treated as FALSE when the capture could
+not be consulted, leading to a incorrect behaviour by pcre2_match(). This bug
+has been fixed.
+
+2. Functions for serialization and deserialization of sets of compiled patterns
+have been added.
+
+3. The value that is returned by PCRE2_INFO_SIZE has been corrected to remove
+excess code units at the end of the data block that may occasionally occur if
+the code for calculating the size over-estimates. This change stops the
+serialization code copying uninitialized data, to which valgrind objects. The
+documentation of PCRE2_INFO_SIZE was incorrect in stating that the size did not
+include the general overhead. This has been corrected.
+
+4. All code units in every slot in the table of group names are now set, again
+in order to avoid accessing uninitialized data when serializing.
+
+5. The (*NO_JIT) feature is implemented.
+
+6. If a bug that caused pcre2_compile() to use more memory than allocated was
+triggered when using valgrind, the code in (3) above passed a stupidly large
+value to valgrind. This caused a crash instead of an "internal error" return.
+
+7. A reference to a duplicated named group (either a back reference or a test
+for being set in a conditional) that occurred in a part of the pattern where
+PCRE2_DUPNAMES was not set caused the amount of memory needed for the pattern
+to be incorrectly calculated, leading to overwriting.
+
+8. A mutually recursive set of back references such as (\2)(\1) caused a
+segfault at compile time (while trying to find the minimum matching length).
+The infinite loop is now broken (with the minimum length unset, that is, zero).
+
+9. If an assertion that was used as a condition was quantified with a minimum
+of zero, matching went wrong. In particular, if the whole group had unlimited
+repetition and could match an empty string, a segfault was likely. The pattern
+(?(?=0)?)+ is an example that caused this. Perl allows assertions to be
+quantified, but not if they are being used as conditions, so the above pattern
+is faulted by Perl. PCRE2 has now been changed so that it also rejects such
+patterns.
+
+10. The error message for an invalid quantifier has been changed from "nothing
+to repeat" to "quantifier does not follow a repeatable item".
+
+11. If a bad UTF string is compiled with NO_UTF_CHECK, it may succeed, but
+scanning the compiled pattern in subsequent auto-possessification can get out
+of step and lead to an unknown opcode. Previously this could have caused an
+infinite loop. Now it generates an "internal error" error. This is a tidyup,
+not a bug fix; passing bad UTF with NO_UTF_CHECK is documented as having an
+undefined outcome.
+
+12. A UTF pattern containing a "not" match of a non-ASCII character and a
+subroutine reference could loop at compile time. Example: /[^\xff]((?1))/.
+
+13. The locale test (RunTest 3) has been upgraded. It now checks that a locale
+that is found in the output of "locale -a" can actually be set by pcre2test
+before it is accepted. Previously, in an environment where a locale was listed
+but would not set (an example does exist), the test would "pass" without
+actually doing anything. Also the fr_CA locale has been added to the list of
+locales that can be used.
+
+14. Fixed a bug in pcre2_substitute(). If a replacement string ended in a
+capturing group number without parentheses, the last character was incorrectly
+literally included at the end of the replacement string.
+
+15. A possessive capturing group such as (a)*+ with a minimum repeat of zero
+failed to allow the zero-repeat case if pcre2_match() was called with an
+ovector too small to capture the group.
+
+16. Improved error message in pcre2test when setting the stack size (-S) fails.
+
+17. Fixed two bugs in CMakeLists.txt: (1) Some lines had got lost in the
+transfer from PCRE1, meaning that CMake configuration failed if "build tests"
+was selected. (2) The file src/pcre2_serialize.c had not been added to the list
+of PCRE2 sources, which caused a failure to build pcre2test.
+
+18. Fixed typo in pcre2_serialize.c (DECL instead of DEFN) that causes problems
+only on Windows.
+
+19. Use binary input when reading back saved serialized patterns in pcre2test.
+
+20. Added RunTest.bat for running the tests under Windows.
+
+21. "make distclean" was not removing config.h, a file that may be created for
+use with CMake.
+
+22. A pattern such as "((?2){0,1999}())?", which has a group containing a
+forward reference repeated a large (but limited) number of times within a
+repeated outer group that has a zero minimum quantifier, caused incorrect code
+to be compiled, leading to the error "internal error: previously-checked
+referenced subpattern not found" when an incorrect memory address was read.
+This bug was reported as "heap overflow", discovered by Kai Lu of Fortinet's
+FortiGuard Labs. (Added 24-March-2015: CVE-2015-2325 was given to this.)
+
+23. A pattern such as "((?+1)(\1))/" containing a forward reference subroutine
+call within a group that also contained a recursive back reference caused
+incorrect code to be compiled. This bug was reported as "heap overflow",
+discovered by Kai Lu of Fortinet's FortiGuard Labs. (Added 24-March-2015:
+CVE-2015-2326 was given to this.)
+
+24. Computing the size of the JIT read-only data in advance has been a source
+of various issues, and new ones are still appear unfortunately. To fix
+existing and future issues, size computation is eliminated from the code,
+and replaced by on-demand memory allocation.
+
+25. A pattern such as /(?i)[A-`]/, where characters in the other case are
+adjacent to the end of the range, and the range contained characters with more
+than one other case, caused incorrect behaviour when compiled in UTF mode. In
+that example, the range a-j was left out of the class.
+
+
+Version 10.00 05-January-2015
+-----------------------------
+
+Version 10.00 is the first release of PCRE2, a revised API for the PCRE
+library. Changes prior to 10.00 are logged in the ChangeLog file for the old
+API, up to item 20 for release 8.36.
+
+The code of the library was heavily revised as part of the new API
+implementation. Details of each and every modification were not individually
+logged. In addition to the API changes, the following changes were made. They
+are either new functionality, or bug fixes and other noticeable changes of
+behaviour that were implemented after the code had been forked.
+
+1. Including Unicode support at build time is now enabled by default, but it
+can optionally be disabled. It is not enabled by default at run time (no
+change).
+
+2. The test program, now called pcre2test, was re-specified and almost
+completely re-written. Its input is not compatible with input for pcretest.
+
+3. Patterns may start with (*NOTEMPTY) or (*NOTEMPTY_ATSTART) to set the
+PCRE2_NOTEMPTY or PCRE2_NOTEMPTY_ATSTART options for every subject line that is
+matched by that pattern.
+
+4. For the benefit of those who use PCRE2 via some other application, that is,
+not writing the function calls themselves, it is possible to check the PCRE2
+version by matching a pattern such as /(?(VERSION>=10)yes|no)/ against a
+string such as "yesno".
+
+5. There are case-equivalent Unicode characters whose encodings use different
+numbers of code units in UTF-8. U+023A and U+2C65 are one example. (It is
+theoretically possible for this to happen in UTF-16 too.) If a backreference to
+a group containing one of these characters was greedily repeated, and during
+the match a backtrack occurred, the subject might be backtracked by the wrong
+number of code units. For example, if /^(\x{23a})\1*(.)/ is matched caselessly
+(and in UTF-8 mode) against "\x{23a}\x{2c65}\x{2c65}\x{2c65}", group 2 should
+capture the final character, which is the three bytes E2, B1, and A5 in UTF-8.
+Incorrect backtracking meant that group 2 captured only the last two bytes.
+This bug has been fixed; the new code is slower, but it is used only when the
+strings matched by the repetition are not all the same length.
+
+6. A pattern such as /()a/ was not setting the "first character must be 'a'"
+information. This applied to any pattern with a group that matched no
+characters, for example: /(?:(?=.)|(?<!x))a/.
+
+7. When an (*ACCEPT) is triggered inside capturing parentheses, it arranges for
+those parentheses to be closed with whatever has been captured so far. However,
+it was failing to mark any other groups between the highest capture so far and
+the currrent group as "unset". Thus, the ovector for those groups contained
+whatever was previously there. An example is the pattern /(x)|((*ACCEPT))/ when
+matched against "abcd".
+
+8. The pcre2_substitute() function has been implemented.
+
+9. If an assertion used as a condition was quantified with a minimum of zero
+(an odd thing to do, but it happened), SIGSEGV or other misbehaviour could
+occur.
+
+10. The PCRE2_NO_DOTSTAR_ANCHOR option has been implemented.
+
+****
diff --git a/contrib/libs/pcre2/INSTALL b/contrib/libs/pcre2/INSTALL
new file mode 100644
index 0000000000..e82fd21de2
--- /dev/null
+++ b/contrib/libs/pcre2/INSTALL
@@ -0,0 +1,368 @@
+Installation Instructions
+*************************
+
+ Copyright (C) 1994-1996, 1999-2002, 2004-2017, 2020-2021 Free
+Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+ Briefly, the shell command './configure && make && make install'
+should configure, build, and install this package. The following
+more-detailed instructions are generic; see the 'README' file for
+instructions specific to this package. Some packages provide this
+'INSTALL' file but do not implement all of the features documented
+below. The lack of an optional feature in a given package is not
+necessarily a bug. More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+ The 'configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a 'Makefile' in each directory of the package.
+It may also create one or more '.h' files containing system-dependent
+definitions. Finally, it creates a shell script 'config.status' that
+you can run in the future to recreate the current configuration, and a
+file 'config.log' containing compiler output (useful mainly for
+debugging 'configure').
+
+ It can also use an optional file (typically called 'config.cache' and
+enabled with '--cache-file=config.cache' or simply '-C') that saves the
+results of its tests to speed up reconfiguring. Caching is disabled by
+default to prevent problems with accidental use of stale cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how 'configure' could check whether to do them, and mail
+diffs or instructions to the address given in the 'README' so they can
+be considered for the next release. If you are using the cache, and at
+some point 'config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file 'configure.ac' (or 'configure.in') is used to create
+'configure' by a program called 'autoconf'. You need 'configure.ac' if
+you want to change it or regenerate 'configure' using a newer version of
+'autoconf'.
+
+ The simplest way to compile this package is:
+
+ 1. 'cd' to the directory containing the package's source code and type
+ './configure' to configure the package for your system.
+
+ Running 'configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type 'make' to compile the package.
+
+ 3. Optionally, type 'make check' to run any self-tests that come with
+ the package, generally using the just-built uninstalled binaries.
+
+ 4. Type 'make install' to install the programs and any data files and
+ documentation. When installing into a prefix owned by root, it is
+ recommended that the package be configured and built as a regular
+ user, and only the 'make install' phase executed with root
+ privileges.
+
+ 5. Optionally, type 'make installcheck' to repeat any self-tests, but
+ this time using the binaries in their final installed location.
+ This target does not install anything. Running this target as a
+ regular user, particularly if the prior 'make install' required
+ root privileges, verifies that the installation completed
+ correctly.
+
+ 6. You can remove the program binaries and object files from the
+ source code directory by typing 'make clean'. To also remove the
+ files that 'configure' created (so you can compile the package for
+ a different kind of computer), type 'make distclean'. There is
+ also a 'make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 7. Often, you can also type 'make uninstall' to remove the installed
+ files again. In practice, not all packages have tested that
+ uninstallation works correctly, even though it is required by the
+ GNU Coding Standards.
+
+ 8. Some packages, particularly those that use Automake, provide 'make
+ distcheck', which can by used by developers to test that all other
+ targets like 'make install' and 'make uninstall' work correctly.
+ This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the 'configure' script does not know about. Run './configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give 'configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here is
+an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU 'make'. 'cd' to the
+directory where you want the object files and executables to go and run
+the 'configure' script. 'configure' automatically checks for the source
+code in the directory that 'configure' is in and in '..'. This is known
+as a "VPATH" build.
+
+ With a non-GNU 'make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use 'make distclean' before
+reconfiguring for another architecture.
+
+ On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple '-arch' options to the
+compiler but only a single '-arch' option to the preprocessor. Like
+this:
+
+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CPP="gcc -E" CXXCPP="g++ -E"
+
+ This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the 'lipo' tool if you have problems.
+
+Installation Names
+==================
+
+ By default, 'make install' installs the package's commands under
+'/usr/local/bin', include files under '/usr/local/include', etc. You
+can specify an installation prefix other than '/usr/local' by giving
+'configure' the option '--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like '--bindir=DIR' to specify different values for particular
+kinds of files. Run 'configure --help' for a list of the directories
+you can set and what kinds of files go in them. In general, the default
+for these options is expressed in terms of '${prefix}', so that
+specifying just '--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+ The most portable way to affect installation locations is to pass the
+correct locations to 'configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+'make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+ The first method involves providing an override variable for each
+affected directory. For example, 'make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+'${prefix}'. Any directories that were specified during 'configure',
+but not in terms of '${prefix}', must each be overridden at install time
+for the entire installation to be relocated. The approach of makefile
+variable overrides for each directory variable is required by the GNU
+Coding Standards, and ideally causes no recompilation. However, some
+platforms have known limitations with the semantics of shared libraries
+that end up requiring recompilation when using this method, particularly
+noticeable in packages that use GNU Libtool.
+
+ The second method involves providing the 'DESTDIR' variable. For
+example, 'make install DESTDIR=/alternate/directory' will prepend
+'/alternate/directory' before all installation names. The approach of
+'DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters. On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of '${prefix}'
+at 'configure' time.
+
+Optional Features
+=================
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving 'configure' the
+option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
+
+ Some packages pay attention to '--enable-FEATURE' options to
+'configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to '--with-PACKAGE' options, where PACKAGE
+is something like 'gnu-as' or 'x' (for the X Window System). The
+'README' should mention any '--enable-' and '--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, 'configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the 'configure' options '--x-includes=DIR' and
+'--x-libraries=DIR' to specify their locations.
+
+ Some packages offer the ability to configure how verbose the
+execution of 'make' will be. For these packages, running './configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with 'make V=1'; while running './configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with 'make V=0'.
+
+Particular systems
+==================
+
+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC
+is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+ HP-UX 'make' updates targets which have the same timestamps as their
+prerequisites, which makes it generally unusable when shipped generated
+files such as 'configure' are involved. Use GNU 'make' instead.
+
+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its '<wchar.h>' header file. The option '-nodtk' can be used as a
+workaround. If GNU CC is not installed, it is therefore recommended to
+try
+
+ ./configure CC="cc"
+
+and if that doesn't work, try
+
+ ./configure CC="cc -nodtk"
+
+ On Solaris, don't put '/usr/ucb' early in your 'PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in '/usr/bin'. So, if you need '/usr/ucb'
+in your 'PATH', put it _after_ '/usr/bin'.
+
+ On Haiku, software installed for all users goes in '/boot/common',
+not '/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+ There may be some features 'configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, 'configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+'--build=TYPE' option. TYPE can either be a short name for the system
+type, such as 'sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file 'config.sub' for the possible values of each field. If
+'config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option '--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with '--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for 'configure' scripts to share,
+you can create a site shell script called 'config.site' that gives
+default values for variables like 'CC', 'cache_file', and 'prefix'.
+'configure' looks for 'PREFIX/share/config.site' if it exists, then
+'PREFIX/etc/config.site' if it exists. Or, you can set the
+'CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all 'configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to 'configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the 'configure' command line, using 'VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified 'gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
+Autoconf limitation. Until the limitation is lifted, you can use this
+workaround:
+
+ CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+'configure' Invocation
+======================
+
+ 'configure' recognizes the following options to control how it
+operates.
+
+'--help'
+'-h'
+ Print a summary of all of the options to 'configure', and exit.
+
+'--help=short'
+'--help=recursive'
+ Print a summary of the options unique to this package's
+ 'configure', and exit. The 'short' variant lists options used only
+ in the top level, while the 'recursive' variant lists options also
+ present in any nested packages.
+
+'--version'
+'-V'
+ Print the version of Autoconf used to generate the 'configure'
+ script, and exit.
+
+'--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally 'config.cache'. FILE defaults to '/dev/null' to
+ disable caching.
+
+'--config-cache'
+'-C'
+ Alias for '--cache-file=config.cache'.
+
+'--quiet'
+'--silent'
+'-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to '/dev/null' (any error
+ messages will still be shown).
+
+'--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ 'configure' can determine that directory automatically.
+
+'--prefix=DIR'
+ Use DIR as the installation prefix. *note Installation Names:: for
+ more details, including other options available for fine-tuning the
+ installation locations.
+
+'--no-create'
+'-n'
+ Run the configure checks, but stop before creating any output
+ files.
+
+'configure' also accepts some other, not widely useful, options. Run
+'configure --help' for more details.
diff --git a/contrib/libs/pcre2/NEWS b/contrib/libs/pcre2/NEWS
new file mode 100644
index 0000000000..97da19e071
--- /dev/null
+++ b/contrib/libs/pcre2/NEWS
@@ -0,0 +1,436 @@
+News about PCRE2 releases
+-------------------------
+
+
+Version 10.42 11-December-2022
+------------------------------
+
+This is an unexpectedly early release to fix a problem that was introduced in
+10.41. ChangeLog number 19 (GitHub #139) added the default definition of
+PCRE2_CALL_CONVENTION to pcre2posix.c instead of pcre2posix.h, which meant that
+programs including pcre2posix.h but not pcre2.h couldn't compile. A new test
+that checks this case has been added.
+
+A couple of other minor issues are also fixed, and a patch for an intermittent
+JIT fault is also included. See ChangeLog and the Git log.
+
+
+Version 10.41 06-December-2022
+------------------------------
+
+This is another mainly bug-fixing and code-tidying release. There is one
+significant upgrade to pcre2grep: it now behaves like GNU grep when matching
+more than one pattern and a later pattern matches at an earlier point in the
+subject when the matched substrings are being identified by colour or by
+offsets.
+
+
+Version 10.40 15-April-2022
+---------------------------
+
+This is mostly a bug-fixing and code-tidying release. However, there are some
+extensions to Unicode property handling:
+
+* Added support for Bidi_Class and a number of binary Unicode properties,
+including Bidi_Control.
+
+* A number of changes to script matching for \p and \P:
+
+ (a) Script extensions for a character are now coded as a bitmap instead of
+ a list of script numbers, which should be faster and does not need a
+ loop.
+
+ (b) Added the syntax \p{script:xxx} and \p{script_extensions:xxx} (synonyms
+ sc and scx).
+
+ (c) Changed \p{scriptname} from being the same as \p{sc:scriptname} to being
+ the same as \p{scx:scriptname} because this change happened in Perl at
+ release 5.26.
+
+ (d) The standard Unicode 4-letter abbreviations for script names are now
+ recognized.
+
+ (e) In accordance with Unicode and Perl's "loose matching" rules, spaces,
+ hyphens, and underscores are ignored in property names, which are then
+ matched independent of case.
+
+As always, see ChangeLog for a list of all changes (also the Git log).
+
+
+Version 10.39 29-October-2021
+-----------------------------
+
+This release is happening soon after 10.38 because the bug fix is important.
+
+1. Fix incorrect detection of alternatives in first character search in JIT.
+
+2. Update to Unicode 14.0.0.
+
+3. Some code cleanups (see ChangeLog).
+
+
+Version 10.38 01-October-2021
+-----------------------------
+
+As well as some bug fixes and tidies (as always, see ChangeLog for details),
+the documentation is updated to list the new URLs, following the move of the
+source repository to GitHub and the mailing list to Google Groups.
+
+* The CMake build system can now build both static and shared libraries in one
+go.
+
+* Following Perl's lead, \K is now locked out in lookaround assertions by
+default, but an option is provided to re-enable the previous behaviour.
+
+
+Version 10.37 26-May-2021
+-------------------------
+
+A few more bug fixes and tidies. The only change of real note is the removal of
+the actual POSIX names regcomp etc. from the POSIX wrapper library because
+these have caused issues for some applications (see 10.33 #2 below).
+
+
+Version 10.36 04-December-2020
+------------------------------
+
+Again, mainly bug fixes and tidies. The only enhancements are the addition of
+GNU grep's -m (aka --max-count) option to pcre2grep, and also unifying the
+handling of substitution strings for both -O and callouts in pcre2grep, with
+the addition of $x{...} and $o{...} to allow for characters whose code points
+are greater than 255 in Unicode mode.
+
+NOTE: there is an outstanding issue with JIT support for MacOS on arm64
+hardware. For details, please see Bugzilla issue #2618.
+
+
+Version 10.35 15-April-2020
+---------------------------
+
+Bugfixes, tidies, and a few new enhancements.
+
+1. Capturing groups that contain recursive backreferences to themselves are no
+longer automatically atomic, because the restriction is no longer necessary
+as a result of the 10.30 restructuring.
+
+2. Several new options for pcre2_substitute().
+
+3. When Unicode is supported and PCRE2_UCP is set without PCRE2_UTF, Unicode
+character properties are used for upper/lower case computations on characters
+whose code points are greater than 127.
+
+4. The character tables (for low-valued characters) can now more easily be
+saved and restored in binary.
+
+5. Updated to Unicode 13.0.0.
+
+
+Version 10.34 21-November-2019
+------------------------------
+
+Another release with a few enhancements as well as bugfixes and tidies. The
+main new features are:
+
+1. There is now some support for matching in invalid UTF strings.
+
+2. Non-atomic positive lookarounds are implemented in the pcre2_match()
+interpreter, but not in JIT.
+
+3. Added two new functions: pcre2_get_match_data_size() and
+pcre2_maketables_free().
+
+4. Upgraded to Unicode 12.1.0.
+
+
+Version 10.33 16-April-2019
+---------------------------
+
+Yet more bugfixes, tidies, and a few enhancements, summarized here (see
+ChangeLog for the full list):
+
+1. Callouts from pcre2_substitute() are now available.
+
+2. The POSIX functions are now all called pcre2_regcomp() etc., with wrapper
+functions that use the standard POSIX names. However, in pcre2posix.h the POSIX
+names are defined as macros. This should help avoid linking with the wrong
+library in some environments, while still exporting the POSIX names for
+pre-existing programs that use them.
+
+3. Some new options:
+
+ (a) PCRE2_EXTRA_ESCAPED_CR_IS_LF makes \r behave as \n.
+
+ (b) PCRE2_EXTRA_ALT_BSUX enables support for ECMAScript 6's \u{hh...}
+ construct.
+
+ (c) PCRE2_COPY_MATCHED_SUBJECT causes a copy of a matched subject to be
+ made, instead of just remembering a pointer.
+
+4. Some new Perl features:
+
+ (a) Perl 5.28's experimental alphabetic names for atomic groups and
+ lookaround assertions, for example, (*pla:...) and (*atomic:...).
+
+ (b) The new Perl "script run" features (*script_run:...) and
+ (*atomic_script_run:...) aka (*sr:...) and (*asr:...).
+
+ (c) When PCRE2_UTF is set, allow non-ASCII letters and decimal digits in
+ capture group names.
+
+5. --disable-percent-zt disables the use of %zu and %td in formatting strings
+in pcre2test. They were already automatically disabled for VC and older C
+compilers.
+
+6. Some changes related to callouts in pcre2grep:
+
+ (a) Support for running an external program under VMS has been added, in
+ addition to Windows and fork() support.
+
+ (b) --disable-pcre2grep-callout-fork restricts the callout support in
+ to the inbuilt echo facility.
+
+
+Version 10.32 10-September-2018
+-------------------------------
+
+This is another mainly bugfix and tidying release with a few minor
+enhancements. These are the main ones:
+
+1. pcre2grep now supports the inclusion of binary zeros in patterns that are
+read from files via the -f option.
+
+2. ./configure now supports --enable-jit=auto, which automatically enables JIT
+if the hardware supports it.
+
+3. In pcre2_dfa_match(), internal recursive calls no longer use the stack for
+local workspace and local ovectors. Instead, an initial block of stack is
+reserved, but if this is insufficient, heap memory is used. The heap limit
+parameter now applies to pcre2_dfa_match().
+
+4. Updated to Unicode version 11.0.0.
+
+5. (*ACCEPT:ARG), (*FAIL:ARG), and (*COMMIT:ARG) are now supported.
+
+6. Added support for \N{U+dddd}, but only in Unicode mode.
+
+7. Added support for (?^) to unset all imnsx options.
+
+
+Version 10.31 12-February-2018
+------------------------------
+
+This is mainly a bugfix and tidying release (see ChangeLog for full details).
+However, there are some minor enhancements.
+
+1. New pcre2_config() options: PCRE2_CONFIG_NEVER_BACKSLASH_C and
+PCRE2_CONFIG_COMPILED_WIDTHS.
+
+2. New pcre2_pattern_info() option PCRE2_INFO_EXTRAOPTIONS to retrieve the
+extra compile time options.
+
+3. There are now public names for all the pcre2_compile() error numbers.
+
+4. Added PCRE2_CALLOUT_STARTMATCH and PCRE2_CALLOUT_BACKTRACK bits to a new
+field callout_flags in callout blocks.
+
+
+Version 10.30 14-August-2017
+----------------------------
+
+The full list of changes that includes bugfixes and tidies is, as always, in
+ChangeLog. These are the most important new features:
+
+1. The main interpreter, pcre2_match(), has been refactored into a new version
+that does not use recursive function calls (and therefore the system stack) for
+remembering backtracking positions. This makes --disable-stack-for-recursion a
+NOOP. The new implementation allows backtracking into recursive group calls in
+patterns, making it more compatible with Perl, and also fixes some other
+previously hard-to-do issues. For patterns that have a lot of backtracking, the
+heap is now used, and there is an explicit limit on the amount, settable by
+pcre2_set_heap_limit() or (*LIMIT_HEAP=xxx). The "recursion limit" is retained,
+but is renamed as "depth limit" (though the old names remain for
+compatibility).
+
+There is also a change in the way callouts from pcre2_match() are handled. The
+offset_vector field in the callout block is no longer a pointer to the
+actual ovector that was passed to the matching function in the match data
+block. Instead it points to an internal ovector of a size large enough to hold
+all possible captured substrings in the pattern.
+
+2. The new option PCRE2_ENDANCHORED insists that a pattern match must end at
+the end of the subject.
+
+3. The new option PCRE2_EXTENDED_MORE implements Perl's /xx feature, and
+pcre2test is upgraded to support it. Setting within the pattern by (?xx) is
+also supported.
+
+4. (?n) can be used to set PCRE2_NO_AUTO_CAPTURE, because Perl now has this.
+
+5. Additional compile options in the compile context are now available, and the
+first two are: PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES and
+PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL.
+
+6. The newline type PCRE2_NEWLINE_NUL is now available.
+
+7. The match limit value now also applies to pcre2_dfa_match() as there are
+patterns that can use up a lot of resources without necessarily recursing very
+deeply.
+
+8. The option REG_PEND (a GNU extension) is now available for the POSIX
+wrapper. Also there is a new option PCRE2_LITERAL which is used to support
+REG_NOSPEC.
+
+9. PCRE2_EXTRA_MATCH_LINE and PCRE2_EXTRA_MATCH_WORD are implemented for the
+benefit of pcre2grep, and pcre2grep's -F, -w, and -x options are re-implemented
+using PCRE2_LITERAL, PCRE2_EXTRA_MATCH_WORD, and PCRE2_EXTRA_MATCH_LINE. This
+is tidier and also fixes some bugs.
+
+10. The Unicode tables are upgraded from Unicode 8.0.0 to Unicode 10.0.0.
+
+11. There are some experimental functions for converting foreign patterns
+(globs and POSIX patterns) into PCRE2 patterns.
+
+
+Version 10.23 14-February-2017
+------------------------------
+
+1. ChangeLog has the details of a lot of bug fixes and tidies.
+
+2. There has been a major re-factoring of the pcre2_compile.c file. Most syntax
+checking is now done in the pre-pass that identifies capturing groups. This has
+reduced the amount of duplication and made the code tidier. While doing this,
+some minor bugs and Perl incompatibilities were fixed (see ChangeLog for
+details.)
+
+3. Back references are now permitted in lookbehind assertions when there are
+no duplicated group numbers (that is, (?| has not been used), and, if the
+reference is by name, there is only one group of that name. The referenced
+group must, of course be of fixed length.
+
+4. \g{+<number>} (e.g. \g{+2} ) is now supported. It is a "forward back
+reference" and can be useful in repetitions (compare \g{-<number>} ). Perl does
+not recognize this syntax.
+
+5. pcre2grep now automatically expands its buffer up to a maximum set by
+--max-buffer-size.
+
+6. The -t option (grand total) has been added to pcre2grep.
+
+7. A new function called pcre2_code_copy_with_tables() exists to copy a
+compiled pattern along with a private copy of the character tables that is
+uses.
+
+8. A user supplied a number of patches to upgrade pcre2grep under Windows and
+tidy the code.
+
+9. Several updates have been made to pcre2test and test scripts (see
+ChangeLog).
+
+
+Version 10.22 29-July-2016
+--------------------------
+
+1. ChangeLog has the details of a number of bug fixes.
+
+2. The POSIX wrapper function regcomp() did not used to support back references
+and subroutine calls if called with the REG_NOSUB option. It now does.
+
+3. A new function, pcre2_code_copy(), is added, to make a copy of a compiled
+pattern.
+
+4. Support for string callouts is added to pcre2grep.
+
+5. Added the PCRE2_NO_JIT option to pcre2_match().
+
+6. The pcre2_get_error_message() function now returns with a negative error
+code if the error number it is given is unknown.
+
+7. Several updates have been made to pcre2test and test scripts (see
+ChangeLog).
+
+
+Version 10.21 12-January-2016
+-----------------------------
+
+1. Many bugs have been fixed. A large number of them were provoked only by very
+strange pattern input, and were discovered by fuzzers. Some others were
+discovered by code auditing. See ChangeLog for details.
+
+2. The Unicode tables have been updated to Unicode version 8.0.0.
+
+3. For Perl compatibility in EBCDIC environments, ranges such as a-z in a
+class, where both values are literal letters in the same case, omit the
+non-letter EBCDIC code points within the range.
+
+4. There have been a number of enhancements to the pcre2_substitute() function,
+giving more flexibility to replacement facilities. It is now also possible to
+cause the function to return the needed buffer size if the one given is too
+small.
+
+5. The PCRE2_ALT_VERBNAMES option causes the "name" parts of special verbs such
+as (*THEN:name) to be processed for backslashes and to take note of
+PCRE2_EXTENDED.
+
+6. PCRE2_INFO_HASBACKSLASHC makes it possible for a client to find out if a
+pattern uses \C, and --never-backslash-C makes it possible to compile a version
+PCRE2 in which the use of \C is always forbidden.
+
+7. A limit to the length of pattern that can be handled can now be set by
+calling pcre2_set_max_pattern_length().
+
+8. When matching an unanchored pattern, a match can be required to begin within
+a given number of code units after the start of the subject by calling
+pcre2_set_offset_limit().
+
+9. The pcre2test program has been extended to test new facilities, and it can
+now run the tests when LF on its own is not a valid newline sequence.
+
+10. The RunTest script has also been updated to enable more tests to be run.
+
+11. There have been some minor performance enhancements.
+
+
+Version 10.20 30-June-2015
+--------------------------
+
+1. Callouts with string arguments and the pcre2_callout_enumerate() function
+have been implemented.
+
+2. The PCRE2_NEVER_BACKSLASH_C option, which locks out the use of \C, is added.
+
+3. The PCRE2_ALT_CIRCUMFLEX option lets ^ match after a newline at the end of a
+subject in multiline mode.
+
+4. The way named subpatterns are handled has been refactored. The previous
+approach had several bugs.
+
+5. The handling of \c in EBCDIC environments has been changed to conform to the
+perlebcdic document. This is an incompatible change.
+
+6. Bugs have been mended, many of them discovered by fuzzers.
+
+
+Version 10.10 06-March-2015
+---------------------------
+
+1. Serialization and de-serialization functions have been added to the API,
+making it possible to save and restore sets of compiled patterns, though
+restoration must be done in the same environment that was used for compilation.
+
+2. The (*NO_JIT) feature has been added; this makes it possible for a pattern
+creator to specify that JIT is not to be used.
+
+3. A number of bugs have been fixed. In particular, bugs that caused building
+on Windows using CMake to fail have been mended.
+
+
+Version 10.00 05-January-2015
+-----------------------------
+
+Version 10.00 is the first release of PCRE2, a revised API for the PCRE
+library. Changes prior to 10.00 are logged in the ChangeLog file for the old
+API, up to item 20 for release 8.36. New programs are recommended to use the
+new library. Programs that use the original (PCRE1) API will need changing
+before linking with the new library.
+
+****
diff --git a/contrib/libs/pcre2/README b/contrib/libs/pcre2/README
new file mode 100644
index 0000000000..c88acff8ba
--- /dev/null
+++ b/contrib/libs/pcre2/README
@@ -0,0 +1,924 @@
+README file for PCRE2 (Perl-compatible regular expression library)
+------------------------------------------------------------------
+
+PCRE2 is a re-working of the original PCRE1 library to provide an entirely new
+API. Since its initial release in 2015, there has been further development of
+the code and it now differs from PCRE1 in more than just the API. There are new
+features, and the internals have been improved. The original PCRE1 library is
+now obsolete and no longer maintained. The latest release of PCRE2 is available
+in .tar.gz, tar.bz2, or .zip form from this GitHub repository:
+
+https://github.com/PCRE2Project/pcre2/releases
+
+There is a mailing list for discussion about the development of PCRE2 at
+pcre2-dev@googlegroups.com. You can subscribe by sending an email to
+pcre2-dev+subscribe@googlegroups.com.
+
+You can access the archives and also subscribe or manage your subscription
+here:
+
+https://groups.google.com/g/pcre2-dev
+
+Please read the NEWS file if you are upgrading from a previous release. The
+contents of this README file are:
+
+ The PCRE2 APIs
+ Documentation for PCRE2
+ Contributions by users of PCRE2
+ Building PCRE2 on non-Unix-like systems
+ Building PCRE2 without using autotools
+ Building PCRE2 using autotools
+ Retrieving configuration information
+ Shared libraries
+ Cross-compiling using autotools
+ Making new tarballs
+ Testing PCRE2
+ Character tables
+ File manifest
+
+
+The PCRE2 APIs
+--------------
+
+PCRE2 is written in C, and it has its own API. There are three sets of
+functions, one for the 8-bit library, which processes strings of bytes, one for
+the 16-bit library, which processes strings of 16-bit values, and one for the
+32-bit library, which processes strings of 32-bit values. Unlike PCRE1, there
+are no C++ wrappers.
+
+The distribution does contain a set of C wrapper functions for the 8-bit
+library that are based on the POSIX regular expression API (see the pcre2posix
+man page). These are built into a library called libpcre2-posix. Note that this
+just provides a POSIX calling interface to PCRE2; the regular expressions
+themselves still follow Perl syntax and semantics. The POSIX API is restricted,
+and does not give full access to all of PCRE2's facilities.
+
+The header file for the POSIX-style functions is called pcre2posix.h. The
+official POSIX name is regex.h, but I did not want to risk possible problems
+with existing files of that name by distributing it that way. To use PCRE2 with
+an existing program that uses the POSIX API, pcre2posix.h will have to be
+renamed or pointed at by a link (or the program modified, of course). See the
+pcre2posix documentation for more details.
+
+
+Documentation for PCRE2
+-----------------------
+
+If you install PCRE2 in the normal way on a Unix-like system, you will end up
+with a set of man pages whose names all start with "pcre2". The one that is
+just called "pcre2" lists all the others. In addition to these man pages, the
+PCRE2 documentation is supplied in two other forms:
+
+ 1. There are files called doc/pcre2.txt, doc/pcre2grep.txt, and
+ doc/pcre2test.txt in the source distribution. The first of these is a
+ concatenation of the text forms of all the section 3 man pages except the
+ listing of pcre2demo.c and those that summarize individual functions. The
+ other two are the text forms of the section 1 man pages for the pcre2grep
+ and pcre2test commands. These text forms are provided for ease of scanning
+ with text editors or similar tools. They are installed in
+ <prefix>/share/doc/pcre2, where <prefix> is the installation prefix
+ (defaulting to /usr/local).
+
+ 2. A set of files containing all the documentation in HTML form, hyperlinked
+ in various ways, and rooted in a file called index.html, is distributed in
+ doc/html and installed in <prefix>/share/doc/pcre2/html.
+
+
+Building PCRE2 on non-Unix-like systems
+---------------------------------------
+
+For a non-Unix-like system, please read the file NON-AUTOTOOLS-BUILD, though if
+your system supports the use of "configure" and "make" you may be able to build
+PCRE2 using autotools in the same way as for many Unix-like systems.
+
+PCRE2 can also be configured using CMake, which can be run in various ways
+(command line, GUI, etc). This creates Makefiles, solution files, etc. The file
+NON-AUTOTOOLS-BUILD has information about CMake.
+
+PCRE2 has been compiled on many different operating systems. It should be
+straightforward to build PCRE2 on any system that has a Standard C compiler and
+library, because it uses only Standard C functions.
+
+
+Building PCRE2 without using autotools
+--------------------------------------
+
+The use of autotools (in particular, libtool) is problematic in some
+environments, even some that are Unix or Unix-like. See the NON-AUTOTOOLS-BUILD
+file for ways of building PCRE2 without using autotools.
+
+
+Building PCRE2 using autotools
+------------------------------
+
+The following instructions assume the use of the widely used "configure; make;
+make install" (autotools) process.
+
+If you have downloaded and unpacked a PCRE2 release tarball, run the
+"configure" command from the PCRE2 directory, with your current directory set
+to the directory where you want the files to be created. This command is a
+standard GNU "autoconf" configuration script, for which generic instructions
+are supplied in the file INSTALL.
+
+The files in the GitHub repository do not contain "configure". If you have
+downloaded the PCRE2 source files from GitHub, before you can run "configure"
+you must run the shell script called autogen.sh. This runs a number of
+autotools to create a "configure" script (you must of course have the autotools
+commands installed in order to do this).
+
+Most commonly, people build PCRE2 within its own distribution directory, and in
+this case, on many systems, just running "./configure" is sufficient. However,
+the usual methods of changing standard defaults are available. For example:
+
+CFLAGS='-O2 -Wall' ./configure --prefix=/opt/local
+
+This command specifies that the C compiler should be run with the flags '-O2
+-Wall' instead of the default, and that "make install" should install PCRE2
+under /opt/local instead of the default /usr/local.
+
+If you want to build in a different directory, just run "configure" with that
+directory as current. For example, suppose you have unpacked the PCRE2 source
+into /source/pcre2/pcre2-xxx, but you want to build it in
+/build/pcre2/pcre2-xxx:
+
+cd /build/pcre2/pcre2-xxx
+/source/pcre2/pcre2-xxx/configure
+
+PCRE2 is written in C and is normally compiled as a C library. However, it is
+possible to build it as a C++ library, though the provided building apparatus
+does not have any features to support this.
+
+There are some optional features that can be included or omitted from the PCRE2
+library. They are also documented in the pcre2build man page.
+
+. By default, both shared and static libraries are built. You can change this
+ by adding one of these options to the "configure" command:
+
+ --disable-shared
+ --disable-static
+
+ (See also "Shared libraries on Unix-like systems" below.)
+
+. By default, only the 8-bit library is built. If you add --enable-pcre2-16 to
+ the "configure" command, the 16-bit library is also built. If you add
+ --enable-pcre2-32 to the "configure" command, the 32-bit library is also
+ built. If you want only the 16-bit or 32-bit library, use --disable-pcre2-8
+ to disable building the 8-bit library.
+
+. If you want to include support for just-in-time (JIT) compiling, which can
+ give large performance improvements on certain platforms, add --enable-jit to
+ the "configure" command. This support is available only for certain hardware
+ architectures. If you try to enable it on an unsupported architecture, there
+ will be a compile time error. If in doubt, use --enable-jit=auto, which
+ enables JIT only if the current hardware is supported.
+
+. If you are enabling JIT under SELinux environment you may also want to add
+ --enable-jit-sealloc, which enables the use of an executable memory allocator
+ that is compatible with SELinux. Warning: this allocator is experimental!
+ It does not support fork() operation and may crash when no disk space is
+ available. This option has no effect if JIT is disabled.
+
+. If you do not want to make use of the default support for UTF-8 Unicode
+ character strings in the 8-bit library, UTF-16 Unicode character strings in
+ the 16-bit library, or UTF-32 Unicode character strings in the 32-bit
+ library, you can add --disable-unicode to the "configure" command. This
+ reduces the size of the libraries. It is not possible to configure one
+ library with Unicode support, and another without, in the same configuration.
+ It is also not possible to use --enable-ebcdic (see below) with Unicode
+ support, so if this option is set, you must also use --disable-unicode.
+
+ When Unicode support is available, the use of a UTF encoding still has to be
+ enabled by setting the PCRE2_UTF option at run time or starting a pattern
+ with (*UTF). When PCRE2 is compiled with Unicode support, its input can only
+ either be ASCII or UTF-8/16/32, even when running on EBCDIC platforms.
+
+ As well as supporting UTF strings, Unicode support includes support for the
+ \P, \p, and \X sequences that recognize Unicode character properties.
+ However, only a subset of Unicode properties are supported; see the
+ pcre2pattern man page for details. Escape sequences such as \d and \w in
+ patterns do not by default make use of Unicode properties, but can be made to
+ do so by setting the PCRE2_UCP option or starting a pattern with (*UCP).
+
+. You can build PCRE2 to recognize either CR or LF or the sequence CRLF, or any
+ of the preceding, or any of the Unicode newline sequences, or the NUL (zero)
+ character as indicating the end of a line. Whatever you specify at build time
+ is the default; the caller of PCRE2 can change the selection at run time. The
+ default newline indicator is a single LF character (the Unix standard). You
+ can specify the default newline indicator by adding --enable-newline-is-cr,
+ --enable-newline-is-lf, --enable-newline-is-crlf,
+ --enable-newline-is-anycrlf, --enable-newline-is-any, or
+ --enable-newline-is-nul to the "configure" command, respectively.
+
+. By default, the sequence \R in a pattern matches any Unicode line ending
+ sequence. This is independent of the option specifying what PCRE2 considers
+ to be the end of a line (see above). However, the caller of PCRE2 can
+ restrict \R to match only CR, LF, or CRLF. You can make this the default by
+ adding --enable-bsr-anycrlf to the "configure" command (bsr = "backslash R").
+
+. In a pattern, the escape sequence \C matches a single code unit, even in a
+ UTF mode. This can be dangerous because it breaks up multi-code-unit
+ characters. You can build PCRE2 with the use of \C permanently locked out by
+ adding --enable-never-backslash-C (note the upper case C) to the "configure"
+ command. When \C is allowed by the library, individual applications can lock
+ it out by calling pcre2_compile() with the PCRE2_NEVER_BACKSLASH_C option.
+
+. PCRE2 has a counter that limits the depth of nesting of parentheses in a
+ pattern. This limits the amount of system stack that a pattern uses when it
+ is compiled. The default is 250, but you can change it by setting, for
+ example,
+
+ --with-parens-nest-limit=500
+
+. PCRE2 has a counter that can be set to limit the amount of computing resource
+ it uses when matching a pattern. If the limit is exceeded during a match, the
+ match fails. The default is ten million. You can change the default by
+ setting, for example,
+
+ --with-match-limit=500000
+
+ on the "configure" command. This is just the default; individual calls to
+ pcre2_match() or pcre2_dfa_match() can supply their own value. There is more
+ discussion in the pcre2api man page (search for pcre2_set_match_limit).
+
+. There is a separate counter that limits the depth of nested backtracking
+ (pcre2_match()) or nested function calls (pcre2_dfa_match()) during a
+ matching process, which indirectly limits the amount of heap memory that is
+ used, and in the case of pcre2_dfa_match() the amount of stack as well. This
+ counter also has a default of ten million, which is essentially "unlimited".
+ You can change the default by setting, for example,
+
+ --with-match-limit-depth=5000
+
+ There is more discussion in the pcre2api man page (search for
+ pcre2_set_depth_limit).
+
+. You can also set an explicit limit on the amount of heap memory used by
+ the pcre2_match() and pcre2_dfa_match() interpreters:
+
+ --with-heap-limit=500
+
+ The units are kibibytes (units of 1024 bytes). This limit does not apply when
+ the JIT optimization (which has its own memory control features) is used.
+ There is more discussion on the pcre2api man page (search for
+ pcre2_set_heap_limit).
+
+. In the 8-bit library, the default maximum compiled pattern size is around
+ 64 kibibytes. You can increase this by adding --with-link-size=3 to the
+ "configure" command. PCRE2 then uses three bytes instead of two for offsets
+ to different parts of the compiled pattern. In the 16-bit library,
+ --with-link-size=3 is the same as --with-link-size=4, which (in both
+ libraries) uses four-byte offsets. Increasing the internal link size reduces
+ performance in the 8-bit and 16-bit libraries. In the 32-bit library, the
+ link size setting is ignored, as 4-byte offsets are always used.
+
+. For speed, PCRE2 uses four tables for manipulating and identifying characters
+ whose code point values are less than 256. By default, it uses a set of
+ tables for ASCII encoding that is part of the distribution. If you specify
+
+ --enable-rebuild-chartables
+
+ a program called pcre2_dftables is compiled and run in the default C locale
+ when you obey "make". It builds a source file called pcre2_chartables.c. If
+ you do not specify this option, pcre2_chartables.c is created as a copy of
+ pcre2_chartables.c.dist. See "Character tables" below for further
+ information.
+
+. It is possible to compile PCRE2 for use on systems that use EBCDIC as their
+ character code (as opposed to ASCII/Unicode) by specifying
+
+ --enable-ebcdic --disable-unicode
+
+ This automatically implies --enable-rebuild-chartables (see above). However,
+ when PCRE2 is built this way, it always operates in EBCDIC. It cannot support
+ both EBCDIC and UTF-8/16/32. There is a second option, --enable-ebcdic-nl25,
+ which specifies that the code value for the EBCDIC NL character is 0x25
+ instead of the default 0x15.
+
+. If you specify --enable-debug, additional debugging code is included in the
+ build. This option is intended for use by the PCRE2 maintainers.
+
+. In environments where valgrind is installed, if you specify
+
+ --enable-valgrind
+
+ PCRE2 will use valgrind annotations to mark certain memory regions as
+ unaddressable. This allows it to detect invalid memory accesses, and is
+ mostly useful for debugging PCRE2 itself.
+
+. In environments where the gcc compiler is used and lcov is installed, if you
+ specify
+
+ --enable-coverage
+
+ the build process implements a code coverage report for the test suite. The
+ report is generated by running "make coverage". If ccache is installed on
+ your system, it must be disabled when building PCRE2 for coverage reporting.
+ You can do this by setting the environment variable CCACHE_DISABLE=1 before
+ running "make" to build PCRE2. There is more information about coverage
+ reporting in the "pcre2build" documentation.
+
+. When JIT support is enabled, pcre2grep automatically makes use of it, unless
+ you add --disable-pcre2grep-jit to the "configure" command.
+
+. There is support for calling external programs during matching in the
+ pcre2grep command, using PCRE2's callout facility with string arguments. This
+ support can be disabled by adding --disable-pcre2grep-callout to the
+ "configure" command. There are two kinds of callout: one that generates
+ output from inbuilt code, and another that calls an external program. The
+ latter has special support for Windows and VMS; otherwise it assumes the
+ existence of the fork() function. This facility can be disabled by adding
+ --disable-pcre2grep-callout-fork to the "configure" command.
+
+. The pcre2grep program currently supports only 8-bit data files, and so
+ requires the 8-bit PCRE2 library. It is possible to compile pcre2grep to use
+ libz and/or libbz2, in order to read .gz and .bz2 files (respectively), by
+ specifying one or both of
+
+ --enable-pcre2grep-libz
+ --enable-pcre2grep-libbz2
+
+ Of course, the relevant libraries must be installed on your system.
+
+. The default starting size (in bytes) of the internal buffer used by pcre2grep
+ can be set by, for example:
+
+ --with-pcre2grep-bufsize=51200
+
+ The value must be a plain integer. The default is 20480. The amount of memory
+ used by pcre2grep is actually three times this number, to allow for "before"
+ and "after" lines. If very long lines are encountered, the buffer is
+ automatically enlarged, up to a fixed maximum size.
+
+. The default maximum size of pcre2grep's internal buffer can be set by, for
+ example:
+
+ --with-pcre2grep-max-bufsize=2097152
+
+ The default is either 1048576 or the value of --with-pcre2grep-bufsize,
+ whichever is the larger.
+
+. It is possible to compile pcre2test so that it links with the libreadline
+ or libedit libraries, by specifying, respectively,
+
+ --enable-pcre2test-libreadline or --enable-pcre2test-libedit
+
+ If this is done, when pcre2test's input is from a terminal, it reads it using
+ the readline() function. This provides line-editing and history facilities.
+ Note that libreadline is GPL-licenced, so if you distribute a binary of
+ pcre2test linked in this way, there may be licensing issues. These can be
+ avoided by linking with libedit (which has a BSD licence) instead.
+
+ Enabling libreadline causes the -lreadline option to be added to the
+ pcre2test build. In many operating environments with a sytem-installed
+ readline library this is sufficient. However, in some environments (e.g. if
+ an unmodified distribution version of readline is in use), it may be
+ necessary to specify something like LIBS="-lncurses" as well. This is
+ because, to quote the readline INSTALL, "Readline uses the termcap functions,
+ but does not link with the termcap or curses library itself, allowing
+ applications which link with readline the option to choose an appropriate
+ library." If you get error messages about missing functions tgetstr, tgetent,
+ tputs, tgetflag, or tgoto, this is the problem, and linking with the ncurses
+ library should fix it.
+
+. The C99 standard defines formatting modifiers z and t for size_t and
+ ptrdiff_t values, respectively. By default, PCRE2 uses these modifiers in
+ environments other than Microsoft Visual Studio versions earlier than 2013
+ when __STDC_VERSION__ is defined and has a value greater than or equal to
+ 199901L (indicating C99). However, there is at least one environment that
+ claims to be C99 but does not support these modifiers. If
+ --disable-percent-zt is specified, no use is made of the z or t modifiers.
+ Instead of %td or %zu, %lu is used, with a cast for size_t values.
+
+. There is a special option called --enable-fuzz-support for use by people who
+ want to run fuzzing tests on PCRE2. At present this applies only to the 8-bit
+ library. If set, it causes an extra library called libpcre2-fuzzsupport.a to
+ be built, but not installed. This contains a single function called
+ LLVMFuzzerTestOneInput() whose arguments are a pointer to a string and the
+ length of the string. When called, this function tries to compile the string
+ as a pattern, and if that succeeds, to match it. This is done both with no
+ options and with some random options bits that are generated from the string.
+ Setting --enable-fuzz-support also causes a binary called pcre2fuzzcheck to
+ be created. This is normally run under valgrind or used when PCRE2 is
+ compiled with address sanitizing enabled. It calls the fuzzing function and
+ outputs information about what it is doing. The input strings are specified
+ by arguments: if an argument starts with "=" the rest of it is a literal
+ input string. Otherwise, it is assumed to be a file name, and the contents
+ of the file are the test string.
+
+. Releases before 10.30 could be compiled with --disable-stack-for-recursion,
+ which caused pcre2_match() to use individual blocks on the heap for
+ backtracking instead of recursive function calls (which use the stack). This
+ is now obsolete because pcre2_match() was refactored always to use the heap
+ (in a much more efficient way than before). This option is retained for
+ backwards compatibility, but has no effect other than to output a warning.
+
+The "configure" script builds the following files for the basic C library:
+
+. Makefile the makefile that builds the library
+. src/config.h build-time configuration options for the library
+. src/pcre2.h the public PCRE2 header file
+. pcre2-config script that shows the building settings such as CFLAGS
+ that were set for "configure"
+. libpcre2-8.pc )
+. libpcre2-16.pc ) data for the pkg-config command
+. libpcre2-32.pc )
+. libpcre2-posix.pc )
+. libtool script that builds shared and/or static libraries
+
+Versions of config.h and pcre2.h are distributed in the src directory of PCRE2
+tarballs under the names config.h.generic and pcre2.h.generic. These are
+provided for those who have to build PCRE2 without using "configure" or CMake.
+If you use "configure" or CMake, the .generic versions are not used.
+
+The "configure" script also creates config.status, which is an executable
+script that can be run to recreate the configuration, and config.log, which
+contains compiler output from tests that "configure" runs.
+
+Once "configure" has run, you can run "make". This builds whichever of the
+libraries libpcre2-8, libpcre2-16 and libpcre2-32 are configured, and a test
+program called pcre2test. If you enabled JIT support with --enable-jit, another
+test program called pcre2_jit_test is built as well. If the 8-bit library is
+built, libpcre2-posix, pcre2posix_test, and the pcre2grep command are also
+built. Running "make" with the -j option may speed up compilation on
+multiprocessor systems.
+
+The command "make check" runs all the appropriate tests. Details of the PCRE2
+tests are given below in a separate section of this document. The -j option of
+"make" can also be used when running the tests.
+
+You can use "make install" to install PCRE2 into live directories on your
+system. The following are installed (file names are all relative to the
+<prefix> that is set when "configure" is run):
+
+ Commands (bin):
+ pcre2test
+ pcre2grep (if 8-bit support is enabled)
+ pcre2-config
+
+ Libraries (lib):
+ libpcre2-8 (if 8-bit support is enabled)
+ libpcre2-16 (if 16-bit support is enabled)
+ libpcre2-32 (if 32-bit support is enabled)
+ libpcre2-posix (if 8-bit support is enabled)
+
+ Configuration information (lib/pkgconfig):
+ libpcre2-8.pc
+ libpcre2-16.pc
+ libpcre2-32.pc
+ libpcre2-posix.pc
+
+ Header files (include):
+ pcre2.h
+ pcre2posix.h
+
+ Man pages (share/man/man{1,3}):
+ pcre2grep.1
+ pcre2test.1
+ pcre2-config.1
+ pcre2.3
+ pcre2*.3 (lots more pages, all starting "pcre2")
+
+ HTML documentation (share/doc/pcre2/html):
+ index.html
+ *.html (lots more pages, hyperlinked from index.html)
+
+ Text file documentation (share/doc/pcre2):
+ AUTHORS
+ COPYING
+ ChangeLog
+ LICENCE
+ NEWS
+ README
+ pcre2.txt (a concatenation of the man(3) pages)
+ pcre2test.txt the pcre2test man page
+ pcre2grep.txt the pcre2grep man page
+ pcre2-config.txt the pcre2-config man page
+
+If you want to remove PCRE2 from your system, you can run "make uninstall".
+This removes all the files that "make install" installed. However, it does not
+remove any directories, because these are often shared with other programs.
+
+
+Retrieving configuration information
+------------------------------------
+
+Running "make install" installs the command pcre2-config, which can be used to
+recall information about the PCRE2 configuration and installation. For example:
+
+ pcre2-config --version
+
+prints the version number, and
+
+ pcre2-config --libs8
+
+outputs information about where the 8-bit library is installed. This command
+can be included in makefiles for programs that use PCRE2, saving the programmer
+from having to remember too many details. Run pcre2-config with no arguments to
+obtain a list of possible arguments.
+
+The pkg-config command is another system for saving and retrieving information
+about installed libraries. Instead of separate commands for each library, a
+single command is used. For example:
+
+ pkg-config --libs libpcre2-16
+
+The data is held in *.pc files that are installed in a directory called
+<prefix>/lib/pkgconfig.
+
+
+Shared libraries
+----------------
+
+The default distribution builds PCRE2 as shared libraries and static libraries,
+as long as the operating system supports shared libraries. Shared library
+support relies on the "libtool" script which is built as part of the
+"configure" process.
+
+The libtool script is used to compile and link both shared and static
+libraries. They are placed in a subdirectory called .libs when they are newly
+built. The programs pcre2test and pcre2grep are built to use these uninstalled
+libraries (by means of wrapper scripts in the case of shared libraries). When
+you use "make install" to install shared libraries, pcre2grep and pcre2test are
+automatically re-built to use the newly installed shared libraries before being
+installed themselves. However, the versions left in the build directory still
+use the uninstalled libraries.
+
+To build PCRE2 using static libraries only you must use --disable-shared when
+configuring it. For example:
+
+./configure --prefix=/usr/gnu --disable-shared
+
+Then run "make" in the usual way. Similarly, you can use --disable-static to
+build only shared libraries.
+
+
+Cross-compiling using autotools
+-------------------------------
+
+You can specify CC and CFLAGS in the normal way to the "configure" command, in
+order to cross-compile PCRE2 for some other host. However, you should NOT
+specify --enable-rebuild-chartables, because if you do, the pcre2_dftables.c
+source file is compiled and run on the local host, in order to generate the
+inbuilt character tables (the pcre2_chartables.c file). This will probably not
+work, because pcre2_dftables.c needs to be compiled with the local compiler,
+not the cross compiler.
+
+When --enable-rebuild-chartables is not specified, pcre2_chartables.c is
+created by making a copy of pcre2_chartables.c.dist, which is a default set of
+tables that assumes ASCII code. Cross-compiling with the default tables should
+not be a problem.
+
+If you need to modify the character tables when cross-compiling, you should
+move pcre2_chartables.c.dist out of the way, then compile pcre2_dftables.c by
+hand and run it on the local host to make a new version of
+pcre2_chartables.c.dist. See the pcre2build section "Creating character tables
+at build time" for more details.
+
+
+Making new tarballs
+-------------------
+
+The command "make dist" creates three PCRE2 tarballs, in tar.gz, tar.bz2, and
+zip formats. The command "make distcheck" does the same, but then does a trial
+build of the new distribution to ensure that it works.
+
+If you have modified any of the man page sources in the doc directory, you
+should first run the PrepareRelease script before making a distribution. This
+script creates the .txt and HTML forms of the documentation from the man pages.
+
+
+Testing PCRE2
+-------------
+
+To test the basic PCRE2 library on a Unix-like system, run the RunTest script.
+There is another script called RunGrepTest that tests the pcre2grep command.
+When the 8-bit library is built, a test program for the POSIX wrapper, called
+pcre2posix_test, is compiled, and when JIT support is enabled, a test program
+called pcre2_jit_test is built. The scripts and the program tests are all run
+when you obey "make check". For other environments, see the instructions in
+NON-AUTOTOOLS-BUILD.
+
+The RunTest script runs the pcre2test test program (which is documented in its
+own man page) on each of the relevant testinput files in the testdata
+directory, and compares the output with the contents of the corresponding
+testoutput files. RunTest uses a file called testtry to hold the main output
+from pcre2test. Other files whose names begin with "test" are used as working
+files in some tests.
+
+Some tests are relevant only when certain build-time options were selected. For
+example, the tests for UTF-8/16/32 features are run only when Unicode support
+is available. RunTest outputs a comment when it skips a test.
+
+Many (but not all) of the tests that are not skipped are run twice if JIT
+support is available. On the second run, JIT compilation is forced. This
+testing can be suppressed by putting "-nojit" on the RunTest command line.
+
+The entire set of tests is run once for each of the 8-bit, 16-bit and 32-bit
+libraries that are enabled. If you want to run just one set of tests, call
+RunTest with either the -8, -16 or -32 option.
+
+If valgrind is installed, you can run the tests under it by putting "-valgrind"
+on the RunTest command line. To run pcre2test on just one or more specific test
+files, give their numbers as arguments to RunTest, for example:
+
+ RunTest 2 7 11
+
+You can also specify ranges of tests such as 3-6 or 3- (meaning 3 to the
+end), or a number preceded by ~ to exclude a test. For example:
+
+ Runtest 3-15 ~10
+
+This runs tests 3 to 15, excluding test 10, and just ~13 runs all the tests
+except test 13. Whatever order the arguments are in, the tests are always run
+in numerical order.
+
+You can also call RunTest with the single argument "list" to cause it to output
+a list of tests.
+
+The test sequence starts with "test 0", which is a special test that has no
+input file, and whose output is not checked. This is because it will be
+different on different hardware and with different configurations. The test
+exists in order to exercise some of pcre2test's code that would not otherwise
+be run.
+
+Tests 1 and 2 can always be run, as they expect only plain text strings (not
+UTF) and make no use of Unicode properties. The first test file can be fed
+directly into the perltest.sh script to check that Perl gives the same results.
+The only difference you should see is in the first few lines, where the Perl
+version is given instead of the PCRE2 version. The second set of tests check
+auxiliary functions, error detection, and run-time flags that are specific to
+PCRE2. It also uses the debugging flags to check some of the internals of
+pcre2_compile().
+
+If you build PCRE2 with a locale setting that is not the standard C locale, the
+character tables may be different (see next paragraph). In some cases, this may
+cause failures in the second set of tests. For example, in a locale where the
+isprint() function yields TRUE for characters in the range 128-255, the use of
+[:isascii:] inside a character class defines a different set of characters, and
+this shows up in this test as a difference in the compiled code, which is being
+listed for checking. For example, where the comparison test output contains
+[\x00-\x7f] the test might contain [\x00-\xff], and similarly in some other
+cases. This is not a bug in PCRE2.
+
+Test 3 checks pcre2_maketables(), the facility for building a set of character
+tables for a specific locale and using them instead of the default tables. The
+script uses the "locale" command to check for the availability of the "fr_FR",
+"french", or "fr" locale, and uses the first one that it finds. If the "locale"
+command fails, or if its output doesn't include "fr_FR", "french", or "fr" in
+the list of available locales, the third test cannot be run, and a comment is
+output to say why. If running this test produces an error like this:
+
+ ** Failed to set locale "fr_FR"
+
+it means that the given locale is not available on your system, despite being
+listed by "locale". This does not mean that PCRE2 is broken. There are three
+alternative output files for the third test, because three different versions
+of the French locale have been encountered. The test passes if its output
+matches any one of them.
+
+Tests 4 and 5 check UTF and Unicode property support, test 4 being compatible
+with the perltest.sh script, and test 5 checking PCRE2-specific things.
+
+Tests 6 and 7 check the pcre2_dfa_match() alternative matching function, in
+non-UTF mode and UTF-mode with Unicode property support, respectively.
+
+Test 8 checks some internal offsets and code size features, but it is run only
+when Unicode support is enabled. The output is different in 8-bit, 16-bit, and
+32-bit modes and for different link sizes, so there are different output files
+for each mode and link size.
+
+Tests 9 and 10 are run only in 8-bit mode, and tests 11 and 12 are run only in
+16-bit and 32-bit modes. These are tests that generate different output in
+8-bit mode. Each pair are for general cases and Unicode support, respectively.
+
+Test 13 checks the handling of non-UTF characters greater than 255 by
+pcre2_dfa_match() in 16-bit and 32-bit modes.
+
+Test 14 contains some special UTF and UCP tests that give different output for
+different code unit widths.
+
+Test 15 contains a number of tests that must not be run with JIT. They check,
+among other non-JIT things, the match-limiting features of the interpretive
+matcher.
+
+Test 16 is run only when JIT support is not available. It checks that an
+attempt to use JIT has the expected behaviour.
+
+Test 17 is run only when JIT support is available. It checks JIT complete and
+partial modes, match-limiting under JIT, and other JIT-specific features.
+
+Tests 18 and 19 are run only in 8-bit mode. They check the POSIX interface to
+the 8-bit library, without and with Unicode support, respectively.
+
+Test 20 checks the serialization functions by writing a set of compiled
+patterns to a file, and then reloading and checking them.
+
+Tests 21 and 22 test \C support when the use of \C is not locked out, without
+and with UTF support, respectively. Test 23 tests \C when it is locked out.
+
+Tests 24 and 25 test the experimental pattern conversion functions, without and
+with UTF support, respectively.
+
+Test 26 checks Unicode property support using tests that are generated
+automatically from the Unicode data tables.
+
+
+Character tables
+----------------
+
+For speed, PCRE2 uses four tables for manipulating and identifying characters
+whose code point values are less than 256. By default, a set of tables that is
+built into the library is used. The pcre2_maketables() function can be called
+by an application to create a new set of tables in the current locale. This are
+passed to PCRE2 by calling pcre2_set_character_tables() to put a pointer into a
+compile context.
+
+The source file called pcre2_chartables.c contains the default set of tables.
+By default, this is created as a copy of pcre2_chartables.c.dist, which
+contains tables for ASCII coding. However, if --enable-rebuild-chartables is
+specified for ./configure, a new version of pcre2_chartables.c is built by the
+program pcre2_dftables (compiled from pcre2_dftables.c), which uses the ANSI C
+character handling functions such as isalnum(), isalpha(), isupper(),
+islower(), etc. to build the table sources. This means that the default C
+locale that is set for your system will control the contents of these default
+tables. You can change the default tables by editing pcre2_chartables.c and
+then re-building PCRE2. If you do this, you should take care to ensure that the
+file does not get automatically re-generated. The best way to do this is to
+move pcre2_chartables.c.dist out of the way and replace it with your customized
+tables.
+
+When the pcre2_dftables program is run as a result of specifying
+--enable-rebuild-chartables, it uses the default C locale that is set on your
+system. It does not pay attention to the LC_xxx environment variables. In other
+words, it uses the system's default locale rather than whatever the compiling
+user happens to have set. If you really do want to build a source set of
+character tables in a locale that is specified by the LC_xxx variables, you can
+run the pcre2_dftables program by hand with the -L option. For example:
+
+ ./pcre2_dftables -L pcre2_chartables.c.special
+
+The second argument names the file where the source code for the tables is
+written. The first two 256-byte tables provide lower casing and case flipping
+functions, respectively. The next table consists of a number of 32-byte bit
+maps which identify certain character classes such as digits, "word"
+characters, white space, etc. These are used when building 32-byte bit maps
+that represent character classes for code points less than 256. The final
+256-byte table has bits indicating various character types, as follows:
+
+ 1 white space character
+ 2 letter
+ 4 lower case letter
+ 8 decimal digit
+ 16 alphanumeric or '_'
+
+You can also specify -b (with or without -L) when running pcre2_dftables. This
+causes the tables to be written in binary instead of as source code. A set of
+binary tables can be loaded into memory by an application and passed to
+pcre2_compile() in the same way as tables created dynamically by calling
+pcre2_maketables(). The tables are just a string of bytes, independent of
+hardware characteristics such as endianness. This means they can be bundled
+with an application that runs in different environments, to ensure consistent
+behaviour.
+
+See also the pcre2build section "Creating character tables at build time".
+
+
+File manifest
+-------------
+
+The distribution should contain the files listed below.
+
+(A) Source files for the PCRE2 library functions and their headers are found in
+ the src directory:
+
+ src/pcre2_dftables.c auxiliary program for building pcre2_chartables.c
+ when --enable-rebuild-chartables is specified
+
+ src/pcre2_chartables.c.dist a default set of character tables that assume
+ ASCII coding; unless --enable-rebuild-chartables is
+ specified, used by copying to pcre2_chartables.c
+
+ src/pcre2posix.c )
+ src/pcre2_auto_possess.c )
+ src/pcre2_compile.c )
+ src/pcre2_config.c )
+ src/pcre2_context.c )
+ src/pcre2_convert.c )
+ src/pcre2_dfa_match.c )
+ src/pcre2_error.c )
+ src/pcre2_extuni.c )
+ src/pcre2_find_bracket.c )
+ src/pcre2_jit_compile.c )
+ src/pcre2_jit_match.c ) sources for the functions in the library,
+ src/pcre2_jit_misc.c ) and some internal functions that they use
+ src/pcre2_maketables.c )
+ src/pcre2_match.c )
+ src/pcre2_match_data.c )
+ src/pcre2_newline.c )
+ src/pcre2_ord2utf.c )
+ src/pcre2_pattern_info.c )
+ src/pcre2_script_run.c )
+ src/pcre2_serialize.c )
+ src/pcre2_string_utils.c )
+ src/pcre2_study.c )
+ src/pcre2_substitute.c )
+ src/pcre2_substring.c )
+ src/pcre2_tables.c )
+ src/pcre2_ucd.c )
+ src/pcre2_ucptables.c )
+ src/pcre2_valid_utf.c )
+ src/pcre2_xclass.c )
+
+ src/pcre2_printint.c debugging function that is used by pcre2test,
+ src/pcre2_fuzzsupport.c function for (optional) fuzzing support
+
+ src/config.h.in template for config.h, when built by "configure"
+ src/pcre2.h.in template for pcre2.h when built by "configure"
+ src/pcre2posix.h header for the external POSIX wrapper API
+ src/pcre2_internal.h header for internal use
+ src/pcre2_intmodedep.h a mode-specific internal header
+ src/pcre2_jit_neon_inc.h header used by JIT
+ src/pcre2_jit_simd_inc.h header used by JIT
+ src/pcre2_ucp.h header for Unicode property handling
+
+ sljit/* source files for the JIT compiler
+
+(B) Source files for programs that use PCRE2:
+
+ src/pcre2demo.c simple demonstration of coding calls to PCRE2
+ src/pcre2grep.c source of a grep utility that uses PCRE2
+ src/pcre2test.c comprehensive test program
+ src/pcre2_jit_test.c JIT test program
+ src/pcre2posix_test.c POSIX wrapper API test program
+
+(C) Auxiliary files:
+
+ 132html script to turn "man" pages into HTML
+ AUTHORS information about the author of PCRE2
+ ChangeLog log of changes to the code
+ CleanTxt script to clean nroff output for txt man pages
+ Detrail script to remove trailing spaces
+ HACKING some notes about the internals of PCRE2
+ INSTALL generic installation instructions
+ LICENCE conditions for the use of PCRE2
+ COPYING the same, using GNU's standard name
+ Makefile.in ) template for Unix Makefile, which is built by
+ ) "configure"
+ Makefile.am ) the automake input that was used to create
+ ) Makefile.in
+ NEWS important changes in this release
+ NON-AUTOTOOLS-BUILD notes on building PCRE2 without using autotools
+ PrepareRelease script to make preparations for "make dist"
+ README this file
+ RunTest a Unix shell script for running tests
+ RunGrepTest a Unix shell script for pcre2grep tests
+ aclocal.m4 m4 macros (generated by "aclocal")
+ config.guess ) files used by libtool,
+ config.sub ) used only when building a shared library
+ configure a configuring shell script (built by autoconf)
+ configure.ac ) the autoconf input that was used to build
+ ) "configure" and config.h
+ depcomp ) script to find program dependencies, generated by
+ ) automake
+ doc/*.3 man page sources for PCRE2
+ doc/*.1 man page sources for pcre2grep and pcre2test
+ doc/index.html.src the base HTML page
+ doc/html/* HTML documentation
+ doc/pcre2.txt plain text version of the man pages
+ doc/pcre2test.txt plain text documentation of test program
+ install-sh a shell script for installing files
+ libpcre2-8.pc.in template for libpcre2-8.pc for pkg-config
+ libpcre2-16.pc.in template for libpcre2-16.pc for pkg-config
+ libpcre2-32.pc.in template for libpcre2-32.pc for pkg-config
+ libpcre2-posix.pc.in template for libpcre2-posix.pc for pkg-config
+ ltmain.sh file used to build a libtool script
+ missing ) common stub for a few missing GNU programs while
+ ) installing, generated by automake
+ mkinstalldirs script for making install directories
+ perltest.sh Script for running a Perl test program
+ pcre2-config.in source of script which retains PCRE2 information
+ testdata/testinput* test data for main library tests
+ testdata/testoutput* expected test results
+ testdata/grep* input and output for pcre2grep tests
+ testdata/* other supporting test files
+
+(D) Auxiliary files for cmake support
+
+ cmake/COPYING-CMAKE-SCRIPTS
+ cmake/FindPackageHandleStandardArgs.cmake
+ cmake/FindEditline.cmake
+ cmake/FindReadline.cmake
+ CMakeLists.txt
+ config-cmake.h.in
+
+(E) Auxiliary files for building PCRE2 "by hand"
+
+ src/pcre2.h.generic ) a version of the public PCRE2 header file
+ ) for use in non-"configure" environments
+ src/config.h.generic ) a version of config.h for use in non-"configure"
+ ) environments
+
+Philip Hazel
+Email local part: Philip.Hazel
+Email domain: gmail.com
+Last updated: 10 December 2022
diff --git a/contrib/libs/pcre2/README.md b/contrib/libs/pcre2/README.md
new file mode 100644
index 0000000000..d3fff179e3
--- /dev/null
+++ b/contrib/libs/pcre2/README.md
@@ -0,0 +1,56 @@
+# PCRE2 - Perl-Compatible Regular Expressions
+
+The PCRE2 library is a set of C functions that implement regular expression
+pattern matching using the same syntax and semantics as Perl 5. PCRE2 has its
+own native API, as well as a set of wrapper functions that correspond to the
+POSIX regular expression API. The PCRE2 library is free, even for building
+proprietary software. It comes in three forms, for processing 8-bit, 16-bit,
+or 32-bit code units, in either literal or UTF encoding.
+
+PCRE2 was first released in 2015 to replace the API in the original PCRE
+library, which is now obsolete and no longer maintained. As well as a more
+flexible API, the code of PCRE2 has been much improved since the fork.
+
+## Download
+
+As well as downloading from the
+[GitHub site](https://github.com/PCRE2Project/pcre2), you can download PCRE2
+or the older, unmaintained PCRE1 library from an
+[*unofficial* mirror](https://sourceforge.net/projects/pcre/files/) at SourceForge.
+
+You can check out the PCRE2 source code via Git or Subversion:
+
+ git clone https://github.com/PCRE2Project/pcre2.git
+ svn co https://github.com/PCRE2Project/pcre2.git
+
+## Contributed Ports
+
+If you just need the command-line PCRE2 tools on Windows, precompiled binary
+versions are available at this
+[Rexegg page](http://www.rexegg.com/pcregrep-pcretest.html).
+
+A PCRE2 port for z/OS, a mainframe operating system which uses EBCDIC as its
+default character encoding, can be found at
+[http://www.cbttape.org](http://www.cbttape.org/) (File 939).
+
+## Documentation
+
+You can read the PCRE2 documentation
+[here](https://PCRE2Project.github.io/pcre2/doc/html/index.html).
+
+Comparisons to Perl's regular expression semantics can be found in the
+community authored Wikipedia entry for PCRE.
+
+There is a curated summary of changes for each PCRE release, copies of
+documentation from older releases, and other useful information from the third
+party authored
+[RexEgg PCRE Documentation and Change Log page](http://www.rexegg.com/pcre-documentation.html).
+
+## Contact
+
+To report a problem with the PCRE2 library, or to make a feature request, please
+use the PCRE2 GitHub issues tracker. There is a mailing list for discussion of
+ PCRE2 issues and development at pcre2-dev@googlegroups.com, which is where any
+announcements will be made. You can browse the
+[list archives](https://groups.google.com/g/pcre2-dev).
+
diff --git a/contrib/libs/pcre2/pcre2.h b/contrib/libs/pcre2/pcre2.h
new file mode 100644
index 0000000000..1cbecd0e86
--- /dev/null
+++ b/contrib/libs/pcre2/pcre2.h
@@ -0,0 +1,993 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* This is the public header file for the PCRE library, second API, to be
+#included by applications that call PCRE2 functions.
+
+ Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef PCRE2_H_IDEMPOTENT_GUARD
+#define PCRE2_H_IDEMPOTENT_GUARD
+
+/* The current PCRE version information. */
+
+#define PCRE2_MAJOR 10
+#define PCRE2_MINOR 42
+#define PCRE2_PRERELEASE
+#define PCRE2_DATE 2022-12-11
+
+/* When an application links to a PCRE DLL in Windows, the symbols that are
+imported have to be identified as such. When building PCRE2, the appropriate
+export setting is defined in pcre2_internal.h, which includes this file. So we
+don't change existing definitions of PCRE2_EXP_DECL. */
+
+#if defined(_WIN32) && !defined(PCRE2_STATIC)
+# ifndef PCRE2_EXP_DECL
+# define PCRE2_EXP_DECL extern __declspec(dllimport)
+# endif
+#endif
+
+/* By default, we use the standard "extern" declarations. */
+
+#ifndef PCRE2_EXP_DECL
+# ifdef __cplusplus
+# define PCRE2_EXP_DECL extern "C"
+# else
+# define PCRE2_EXP_DECL extern
+# endif
+#endif
+
+/* When compiling with the MSVC compiler, it is sometimes necessary to include
+a "calling convention" before exported function names. (This is secondhand
+information; I know nothing about MSVC myself). For example, something like
+
+ void __cdecl function(....)
+
+might be needed. In order so make this easy, all the exported functions have
+PCRE2_CALL_CONVENTION just before their names. It is rarely needed; if not
+set, we ensure here that it has no effect. */
+
+#ifndef PCRE2_CALL_CONVENTION
+#define PCRE2_CALL_CONVENTION
+#endif
+
+/* Have to include limits.h, stdlib.h, and inttypes.h to ensure that size_t and
+uint8_t, UCHAR_MAX, etc are defined. Some systems that do have inttypes.h do
+not have stdint.h, which is why we use inttypes.h, which according to the C
+standard is a superset of stdint.h. If inttypes.h is not available the build
+will break and the relevant values must be provided by some other means. */
+
+#include <limits.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+/* Allow for C++ users compiling this directly. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following option bits can be passed to pcre2_compile(), pcre2_match(),
+or pcre2_dfa_match(). PCRE2_NO_UTF_CHECK affects only the function to which it
+is passed. Put these bits at the most significant end of the options word so
+others can be added next to them */
+
+#define PCRE2_ANCHORED 0x80000000u
+#define PCRE2_NO_UTF_CHECK 0x40000000u
+#define PCRE2_ENDANCHORED 0x20000000u
+
+/* The following option bits can be passed only to pcre2_compile(). However,
+they may affect compilation, JIT compilation, and/or interpretive execution.
+The following tags indicate which:
+
+C alters what is compiled by pcre2_compile()
+J alters what is compiled by pcre2_jit_compile()
+M is inspected during pcre2_match() execution
+D is inspected during pcre2_dfa_match() execution
+*/
+
+#define PCRE2_ALLOW_EMPTY_CLASS 0x00000001u /* C */
+#define PCRE2_ALT_BSUX 0x00000002u /* C */
+#define PCRE2_AUTO_CALLOUT 0x00000004u /* C */
+#define PCRE2_CASELESS 0x00000008u /* C */
+#define PCRE2_DOLLAR_ENDONLY 0x00000010u /* J M D */
+#define PCRE2_DOTALL 0x00000020u /* C */
+#define PCRE2_DUPNAMES 0x00000040u /* C */
+#define PCRE2_EXTENDED 0x00000080u /* C */
+#define PCRE2_FIRSTLINE 0x00000100u /* J M D */
+#define PCRE2_MATCH_UNSET_BACKREF 0x00000200u /* C J M */
+#define PCRE2_MULTILINE 0x00000400u /* C */
+#define PCRE2_NEVER_UCP 0x00000800u /* C */
+#define PCRE2_NEVER_UTF 0x00001000u /* C */
+#define PCRE2_NO_AUTO_CAPTURE 0x00002000u /* C */
+#define PCRE2_NO_AUTO_POSSESS 0x00004000u /* C */
+#define PCRE2_NO_DOTSTAR_ANCHOR 0x00008000u /* C */
+#define PCRE2_NO_START_OPTIMIZE 0x00010000u /* J M D */
+#define PCRE2_UCP 0x00020000u /* C J M D */
+#define PCRE2_UNGREEDY 0x00040000u /* C */
+#define PCRE2_UTF 0x00080000u /* C J M D */
+#define PCRE2_NEVER_BACKSLASH_C 0x00100000u /* C */
+#define PCRE2_ALT_CIRCUMFLEX 0x00200000u /* J M D */
+#define PCRE2_ALT_VERBNAMES 0x00400000u /* C */
+#define PCRE2_USE_OFFSET_LIMIT 0x00800000u /* J M D */
+#define PCRE2_EXTENDED_MORE 0x01000000u /* C */
+#define PCRE2_LITERAL 0x02000000u /* C */
+#define PCRE2_MATCH_INVALID_UTF 0x04000000u /* J M D */
+
+/* An additional compile options word is available in the compile context. */
+
+#define PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES 0x00000001u /* C */
+#define PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL 0x00000002u /* C */
+#define PCRE2_EXTRA_MATCH_WORD 0x00000004u /* C */
+#define PCRE2_EXTRA_MATCH_LINE 0x00000008u /* C */
+#define PCRE2_EXTRA_ESCAPED_CR_IS_LF 0x00000010u /* C */
+#define PCRE2_EXTRA_ALT_BSUX 0x00000020u /* C */
+#define PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK 0x00000040u /* C */
+
+/* These are for pcre2_jit_compile(). */
+
+#define PCRE2_JIT_COMPLETE 0x00000001u /* For full matching */
+#define PCRE2_JIT_PARTIAL_SOFT 0x00000002u
+#define PCRE2_JIT_PARTIAL_HARD 0x00000004u
+#define PCRE2_JIT_INVALID_UTF 0x00000100u
+
+/* These are for pcre2_match(), pcre2_dfa_match(), pcre2_jit_match(), and
+pcre2_substitute(). Some are allowed only for one of the functions, and in
+these cases it is noted below. Note that PCRE2_ANCHORED, PCRE2_ENDANCHORED and
+PCRE2_NO_UTF_CHECK can also be passed to these functions (though
+pcre2_jit_match() ignores the latter since it bypasses all sanity checks). */
+
+#define PCRE2_NOTBOL 0x00000001u
+#define PCRE2_NOTEOL 0x00000002u
+#define PCRE2_NOTEMPTY 0x00000004u /* ) These two must be kept */
+#define PCRE2_NOTEMPTY_ATSTART 0x00000008u /* ) adjacent to each other. */
+#define PCRE2_PARTIAL_SOFT 0x00000010u
+#define PCRE2_PARTIAL_HARD 0x00000020u
+#define PCRE2_DFA_RESTART 0x00000040u /* pcre2_dfa_match() only */
+#define PCRE2_DFA_SHORTEST 0x00000080u /* pcre2_dfa_match() only */
+#define PCRE2_SUBSTITUTE_GLOBAL 0x00000100u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_EXTENDED 0x00000200u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_UNSET_EMPTY 0x00000400u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_UNKNOWN_UNSET 0x00000800u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_OVERFLOW_LENGTH 0x00001000u /* pcre2_substitute() only */
+#define PCRE2_NO_JIT 0x00002000u /* Not for pcre2_dfa_match() */
+#define PCRE2_COPY_MATCHED_SUBJECT 0x00004000u
+#define PCRE2_SUBSTITUTE_LITERAL 0x00008000u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_MATCHED 0x00010000u /* pcre2_substitute() only */
+#define PCRE2_SUBSTITUTE_REPLACEMENT_ONLY 0x00020000u /* pcre2_substitute() only */
+
+/* Options for pcre2_pattern_convert(). */
+
+#define PCRE2_CONVERT_UTF 0x00000001u
+#define PCRE2_CONVERT_NO_UTF_CHECK 0x00000002u
+#define PCRE2_CONVERT_POSIX_BASIC 0x00000004u
+#define PCRE2_CONVERT_POSIX_EXTENDED 0x00000008u
+#define PCRE2_CONVERT_GLOB 0x00000010u
+#define PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR 0x00000030u
+#define PCRE2_CONVERT_GLOB_NO_STARSTAR 0x00000050u
+
+/* Newline and \R settings, for use in compile contexts. The newline values
+must be kept in step with values set in config.h and both sets must all be
+greater than zero. */
+
+#define PCRE2_NEWLINE_CR 1
+#define PCRE2_NEWLINE_LF 2
+#define PCRE2_NEWLINE_CRLF 3
+#define PCRE2_NEWLINE_ANY 4
+#define PCRE2_NEWLINE_ANYCRLF 5
+#define PCRE2_NEWLINE_NUL 6
+
+#define PCRE2_BSR_UNICODE 1
+#define PCRE2_BSR_ANYCRLF 2
+
+/* Error codes for pcre2_compile(). Some of these are also used by
+pcre2_pattern_convert(). */
+
+#define PCRE2_ERROR_END_BACKSLASH 101
+#define PCRE2_ERROR_END_BACKSLASH_C 102
+#define PCRE2_ERROR_UNKNOWN_ESCAPE 103
+#define PCRE2_ERROR_QUANTIFIER_OUT_OF_ORDER 104
+#define PCRE2_ERROR_QUANTIFIER_TOO_BIG 105
+#define PCRE2_ERROR_MISSING_SQUARE_BRACKET 106
+#define PCRE2_ERROR_ESCAPE_INVALID_IN_CLASS 107
+#define PCRE2_ERROR_CLASS_RANGE_ORDER 108
+#define PCRE2_ERROR_QUANTIFIER_INVALID 109
+#define PCRE2_ERROR_INTERNAL_UNEXPECTED_REPEAT 110
+#define PCRE2_ERROR_INVALID_AFTER_PARENS_QUERY 111
+#define PCRE2_ERROR_POSIX_CLASS_NOT_IN_CLASS 112
+#define PCRE2_ERROR_POSIX_NO_SUPPORT_COLLATING 113
+#define PCRE2_ERROR_MISSING_CLOSING_PARENTHESIS 114
+#define PCRE2_ERROR_BAD_SUBPATTERN_REFERENCE 115
+#define PCRE2_ERROR_NULL_PATTERN 116
+#define PCRE2_ERROR_BAD_OPTIONS 117
+#define PCRE2_ERROR_MISSING_COMMENT_CLOSING 118
+#define PCRE2_ERROR_PARENTHESES_NEST_TOO_DEEP 119
+#define PCRE2_ERROR_PATTERN_TOO_LARGE 120
+#define PCRE2_ERROR_HEAP_FAILED 121
+#define PCRE2_ERROR_UNMATCHED_CLOSING_PARENTHESIS 122
+#define PCRE2_ERROR_INTERNAL_CODE_OVERFLOW 123
+#define PCRE2_ERROR_MISSING_CONDITION_CLOSING 124
+#define PCRE2_ERROR_LOOKBEHIND_NOT_FIXED_LENGTH 125
+#define PCRE2_ERROR_ZERO_RELATIVE_REFERENCE 126
+#define PCRE2_ERROR_TOO_MANY_CONDITION_BRANCHES 127
+#define PCRE2_ERROR_CONDITION_ASSERTION_EXPECTED 128
+#define PCRE2_ERROR_BAD_RELATIVE_REFERENCE 129
+#define PCRE2_ERROR_UNKNOWN_POSIX_CLASS 130
+#define PCRE2_ERROR_INTERNAL_STUDY_ERROR 131
+#define PCRE2_ERROR_UNICODE_NOT_SUPPORTED 132
+#define PCRE2_ERROR_PARENTHESES_STACK_CHECK 133
+#define PCRE2_ERROR_CODE_POINT_TOO_BIG 134
+#define PCRE2_ERROR_LOOKBEHIND_TOO_COMPLICATED 135
+#define PCRE2_ERROR_LOOKBEHIND_INVALID_BACKSLASH_C 136
+#define PCRE2_ERROR_UNSUPPORTED_ESCAPE_SEQUENCE 137
+#define PCRE2_ERROR_CALLOUT_NUMBER_TOO_BIG 138
+#define PCRE2_ERROR_MISSING_CALLOUT_CLOSING 139
+#define PCRE2_ERROR_ESCAPE_INVALID_IN_VERB 140
+#define PCRE2_ERROR_UNRECOGNIZED_AFTER_QUERY_P 141
+#define PCRE2_ERROR_MISSING_NAME_TERMINATOR 142
+#define PCRE2_ERROR_DUPLICATE_SUBPATTERN_NAME 143
+#define PCRE2_ERROR_INVALID_SUBPATTERN_NAME 144
+#define PCRE2_ERROR_UNICODE_PROPERTIES_UNAVAILABLE 145
+#define PCRE2_ERROR_MALFORMED_UNICODE_PROPERTY 146
+#define PCRE2_ERROR_UNKNOWN_UNICODE_PROPERTY 147
+#define PCRE2_ERROR_SUBPATTERN_NAME_TOO_LONG 148
+#define PCRE2_ERROR_TOO_MANY_NAMED_SUBPATTERNS 149
+#define PCRE2_ERROR_CLASS_INVALID_RANGE 150
+#define PCRE2_ERROR_OCTAL_BYTE_TOO_BIG 151
+#define PCRE2_ERROR_INTERNAL_OVERRAN_WORKSPACE 152
+#define PCRE2_ERROR_INTERNAL_MISSING_SUBPATTERN 153
+#define PCRE2_ERROR_DEFINE_TOO_MANY_BRANCHES 154
+#define PCRE2_ERROR_BACKSLASH_O_MISSING_BRACE 155
+#define PCRE2_ERROR_INTERNAL_UNKNOWN_NEWLINE 156
+#define PCRE2_ERROR_BACKSLASH_G_SYNTAX 157
+#define PCRE2_ERROR_PARENS_QUERY_R_MISSING_CLOSING 158
+/* Error 159 is obsolete and should now never occur */
+#define PCRE2_ERROR_VERB_ARGUMENT_NOT_ALLOWED 159
+#define PCRE2_ERROR_VERB_UNKNOWN 160
+#define PCRE2_ERROR_SUBPATTERN_NUMBER_TOO_BIG 161
+#define PCRE2_ERROR_SUBPATTERN_NAME_EXPECTED 162
+#define PCRE2_ERROR_INTERNAL_PARSED_OVERFLOW 163
+#define PCRE2_ERROR_INVALID_OCTAL 164
+#define PCRE2_ERROR_SUBPATTERN_NAMES_MISMATCH 165
+#define PCRE2_ERROR_MARK_MISSING_ARGUMENT 166
+#define PCRE2_ERROR_INVALID_HEXADECIMAL 167
+#define PCRE2_ERROR_BACKSLASH_C_SYNTAX 168
+#define PCRE2_ERROR_BACKSLASH_K_SYNTAX 169
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_LOOKBEHINDS 170
+#define PCRE2_ERROR_BACKSLASH_N_IN_CLASS 171
+#define PCRE2_ERROR_CALLOUT_STRING_TOO_LONG 172
+#define PCRE2_ERROR_UNICODE_DISALLOWED_CODE_POINT 173
+#define PCRE2_ERROR_UTF_IS_DISABLED 174
+#define PCRE2_ERROR_UCP_IS_DISABLED 175
+#define PCRE2_ERROR_VERB_NAME_TOO_LONG 176
+#define PCRE2_ERROR_BACKSLASH_U_CODE_POINT_TOO_BIG 177
+#define PCRE2_ERROR_MISSING_OCTAL_OR_HEX_DIGITS 178
+#define PCRE2_ERROR_VERSION_CONDITION_SYNTAX 179
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_AUTO_POSSESS 180
+#define PCRE2_ERROR_CALLOUT_NO_STRING_DELIMITER 181
+#define PCRE2_ERROR_CALLOUT_BAD_STRING_DELIMITER 182
+#define PCRE2_ERROR_BACKSLASH_C_CALLER_DISABLED 183
+#define PCRE2_ERROR_QUERY_BARJX_NEST_TOO_DEEP 184
+#define PCRE2_ERROR_BACKSLASH_C_LIBRARY_DISABLED 185
+#define PCRE2_ERROR_PATTERN_TOO_COMPLICATED 186
+#define PCRE2_ERROR_LOOKBEHIND_TOO_LONG 187
+#define PCRE2_ERROR_PATTERN_STRING_TOO_LONG 188
+#define PCRE2_ERROR_INTERNAL_BAD_CODE 189
+#define PCRE2_ERROR_INTERNAL_BAD_CODE_IN_SKIP 190
+#define PCRE2_ERROR_NO_SURROGATES_IN_UTF16 191
+#define PCRE2_ERROR_BAD_LITERAL_OPTIONS 192
+#define PCRE2_ERROR_SUPPORTED_ONLY_IN_UNICODE 193
+#define PCRE2_ERROR_INVALID_HYPHEN_IN_OPTIONS 194
+#define PCRE2_ERROR_ALPHA_ASSERTION_UNKNOWN 195
+#define PCRE2_ERROR_SCRIPT_RUN_NOT_AVAILABLE 196
+#define PCRE2_ERROR_TOO_MANY_CAPTURES 197
+#define PCRE2_ERROR_CONDITION_ATOMIC_ASSERTION_EXPECTED 198
+#define PCRE2_ERROR_BACKSLASH_K_IN_LOOKAROUND 199
+
+
+/* "Expected" matching error codes: no match and partial match. */
+
+#define PCRE2_ERROR_NOMATCH (-1)
+#define PCRE2_ERROR_PARTIAL (-2)
+
+/* Error codes for UTF-8 validity checks */
+
+#define PCRE2_ERROR_UTF8_ERR1 (-3)
+#define PCRE2_ERROR_UTF8_ERR2 (-4)
+#define PCRE2_ERROR_UTF8_ERR3 (-5)
+#define PCRE2_ERROR_UTF8_ERR4 (-6)
+#define PCRE2_ERROR_UTF8_ERR5 (-7)
+#define PCRE2_ERROR_UTF8_ERR6 (-8)
+#define PCRE2_ERROR_UTF8_ERR7 (-9)
+#define PCRE2_ERROR_UTF8_ERR8 (-10)
+#define PCRE2_ERROR_UTF8_ERR9 (-11)
+#define PCRE2_ERROR_UTF8_ERR10 (-12)
+#define PCRE2_ERROR_UTF8_ERR11 (-13)
+#define PCRE2_ERROR_UTF8_ERR12 (-14)
+#define PCRE2_ERROR_UTF8_ERR13 (-15)
+#define PCRE2_ERROR_UTF8_ERR14 (-16)
+#define PCRE2_ERROR_UTF8_ERR15 (-17)
+#define PCRE2_ERROR_UTF8_ERR16 (-18)
+#define PCRE2_ERROR_UTF8_ERR17 (-19)
+#define PCRE2_ERROR_UTF8_ERR18 (-20)
+#define PCRE2_ERROR_UTF8_ERR19 (-21)
+#define PCRE2_ERROR_UTF8_ERR20 (-22)
+#define PCRE2_ERROR_UTF8_ERR21 (-23)
+
+/* Error codes for UTF-16 validity checks */
+
+#define PCRE2_ERROR_UTF16_ERR1 (-24)
+#define PCRE2_ERROR_UTF16_ERR2 (-25)
+#define PCRE2_ERROR_UTF16_ERR3 (-26)
+
+/* Error codes for UTF-32 validity checks */
+
+#define PCRE2_ERROR_UTF32_ERR1 (-27)
+#define PCRE2_ERROR_UTF32_ERR2 (-28)
+
+/* Miscellaneous error codes for pcre2[_dfa]_match(), substring extraction
+functions, context functions, and serializing functions. They are in numerical
+order. Originally they were in alphabetical order too, but now that PCRE2 is
+released, the numbers must not be changed. */
+
+#define PCRE2_ERROR_BADDATA (-29)
+#define PCRE2_ERROR_MIXEDTABLES (-30) /* Name was changed */
+#define PCRE2_ERROR_BADMAGIC (-31)
+#define PCRE2_ERROR_BADMODE (-32)
+#define PCRE2_ERROR_BADOFFSET (-33)
+#define PCRE2_ERROR_BADOPTION (-34)
+#define PCRE2_ERROR_BADREPLACEMENT (-35)
+#define PCRE2_ERROR_BADUTFOFFSET (-36)
+#define PCRE2_ERROR_CALLOUT (-37) /* Never used by PCRE2 itself */
+#define PCRE2_ERROR_DFA_BADRESTART (-38)
+#define PCRE2_ERROR_DFA_RECURSE (-39)
+#define PCRE2_ERROR_DFA_UCOND (-40)
+#define PCRE2_ERROR_DFA_UFUNC (-41)
+#define PCRE2_ERROR_DFA_UITEM (-42)
+#define PCRE2_ERROR_DFA_WSSIZE (-43)
+#define PCRE2_ERROR_INTERNAL (-44)
+#define PCRE2_ERROR_JIT_BADOPTION (-45)
+#define PCRE2_ERROR_JIT_STACKLIMIT (-46)
+#define PCRE2_ERROR_MATCHLIMIT (-47)
+#define PCRE2_ERROR_NOMEMORY (-48)
+#define PCRE2_ERROR_NOSUBSTRING (-49)
+#define PCRE2_ERROR_NOUNIQUESUBSTRING (-50)
+#define PCRE2_ERROR_NULL (-51)
+#define PCRE2_ERROR_RECURSELOOP (-52)
+#define PCRE2_ERROR_DEPTHLIMIT (-53)
+#define PCRE2_ERROR_RECURSIONLIMIT (-53) /* Obsolete synonym */
+#define PCRE2_ERROR_UNAVAILABLE (-54)
+#define PCRE2_ERROR_UNSET (-55)
+#define PCRE2_ERROR_BADOFFSETLIMIT (-56)
+#define PCRE2_ERROR_BADREPESCAPE (-57)
+#define PCRE2_ERROR_REPMISSINGBRACE (-58)
+#define PCRE2_ERROR_BADSUBSTITUTION (-59)
+#define PCRE2_ERROR_BADSUBSPATTERN (-60)
+#define PCRE2_ERROR_TOOMANYREPLACE (-61)
+#define PCRE2_ERROR_BADSERIALIZEDDATA (-62)
+#define PCRE2_ERROR_HEAPLIMIT (-63)
+#define PCRE2_ERROR_CONVERT_SYNTAX (-64)
+#define PCRE2_ERROR_INTERNAL_DUPMATCH (-65)
+#define PCRE2_ERROR_DFA_UINVALID_UTF (-66)
+
+
+/* Request types for pcre2_pattern_info() */
+
+#define PCRE2_INFO_ALLOPTIONS 0
+#define PCRE2_INFO_ARGOPTIONS 1
+#define PCRE2_INFO_BACKREFMAX 2
+#define PCRE2_INFO_BSR 3
+#define PCRE2_INFO_CAPTURECOUNT 4
+#define PCRE2_INFO_FIRSTCODEUNIT 5
+#define PCRE2_INFO_FIRSTCODETYPE 6
+#define PCRE2_INFO_FIRSTBITMAP 7
+#define PCRE2_INFO_HASCRORLF 8
+#define PCRE2_INFO_JCHANGED 9
+#define PCRE2_INFO_JITSIZE 10
+#define PCRE2_INFO_LASTCODEUNIT 11
+#define PCRE2_INFO_LASTCODETYPE 12
+#define PCRE2_INFO_MATCHEMPTY 13
+#define PCRE2_INFO_MATCHLIMIT 14
+#define PCRE2_INFO_MAXLOOKBEHIND 15
+#define PCRE2_INFO_MINLENGTH 16
+#define PCRE2_INFO_NAMECOUNT 17
+#define PCRE2_INFO_NAMEENTRYSIZE 18
+#define PCRE2_INFO_NAMETABLE 19
+#define PCRE2_INFO_NEWLINE 20
+#define PCRE2_INFO_DEPTHLIMIT 21
+#define PCRE2_INFO_RECURSIONLIMIT 21 /* Obsolete synonym */
+#define PCRE2_INFO_SIZE 22
+#define PCRE2_INFO_HASBACKSLASHC 23
+#define PCRE2_INFO_FRAMESIZE 24
+#define PCRE2_INFO_HEAPLIMIT 25
+#define PCRE2_INFO_EXTRAOPTIONS 26
+
+/* Request types for pcre2_config(). */
+
+#define PCRE2_CONFIG_BSR 0
+#define PCRE2_CONFIG_JIT 1
+#define PCRE2_CONFIG_JITTARGET 2
+#define PCRE2_CONFIG_LINKSIZE 3
+#define PCRE2_CONFIG_MATCHLIMIT 4
+#define PCRE2_CONFIG_NEWLINE 5
+#define PCRE2_CONFIG_PARENSLIMIT 6
+#define PCRE2_CONFIG_DEPTHLIMIT 7
+#define PCRE2_CONFIG_RECURSIONLIMIT 7 /* Obsolete synonym */
+#define PCRE2_CONFIG_STACKRECURSE 8 /* Obsolete */
+#define PCRE2_CONFIG_UNICODE 9
+#define PCRE2_CONFIG_UNICODE_VERSION 10
+#define PCRE2_CONFIG_VERSION 11
+#define PCRE2_CONFIG_HEAPLIMIT 12
+#define PCRE2_CONFIG_NEVER_BACKSLASH_C 13
+#define PCRE2_CONFIG_COMPILED_WIDTHS 14
+#define PCRE2_CONFIG_TABLES_LENGTH 15
+
+
+/* Types for code units in patterns and subject strings. */
+
+typedef uint8_t PCRE2_UCHAR8;
+typedef uint16_t PCRE2_UCHAR16;
+typedef uint32_t PCRE2_UCHAR32;
+
+typedef const PCRE2_UCHAR8 *PCRE2_SPTR8;
+typedef const PCRE2_UCHAR16 *PCRE2_SPTR16;
+typedef const PCRE2_UCHAR32 *PCRE2_SPTR32;
+
+/* The PCRE2_SIZE type is used for all string lengths and offsets in PCRE2,
+including pattern offsets for errors and subject offsets after a match. We
+define special values to indicate zero-terminated strings and unset offsets in
+the offset vector (ovector). */
+
+#define PCRE2_SIZE size_t
+#define PCRE2_SIZE_MAX SIZE_MAX
+#define PCRE2_ZERO_TERMINATED (~(PCRE2_SIZE)0)
+#define PCRE2_UNSET (~(PCRE2_SIZE)0)
+
+/* Generic types for opaque structures and JIT callback functions. These
+declarations are defined in a macro that is expanded for each width later. */
+
+#define PCRE2_TYPES_LIST \
+struct pcre2_real_general_context; \
+typedef struct pcre2_real_general_context pcre2_general_context; \
+\
+struct pcre2_real_compile_context; \
+typedef struct pcre2_real_compile_context pcre2_compile_context; \
+\
+struct pcre2_real_match_context; \
+typedef struct pcre2_real_match_context pcre2_match_context; \
+\
+struct pcre2_real_convert_context; \
+typedef struct pcre2_real_convert_context pcre2_convert_context; \
+\
+struct pcre2_real_code; \
+typedef struct pcre2_real_code pcre2_code; \
+\
+struct pcre2_real_match_data; \
+typedef struct pcre2_real_match_data pcre2_match_data; \
+\
+struct pcre2_real_jit_stack; \
+typedef struct pcre2_real_jit_stack pcre2_jit_stack; \
+\
+typedef pcre2_jit_stack *(*pcre2_jit_callback)(void *);
+
+
+/* The structures for passing out data via callout functions. We use structures
+so that new fields can be added on the end in future versions, without changing
+the API of the function, thereby allowing old clients to work without
+modification. Define the generic versions in a macro; the width-specific
+versions are generated from this macro below. */
+
+/* Flags for the callout_flags field. These are cleared after a callout. */
+
+#define PCRE2_CALLOUT_STARTMATCH 0x00000001u /* Set for each bumpalong */
+#define PCRE2_CALLOUT_BACKTRACK 0x00000002u /* Set after a backtrack */
+
+#define PCRE2_STRUCTURE_LIST \
+typedef struct pcre2_callout_block { \
+ uint32_t version; /* Identifies version of block */ \
+ /* ------------------------ Version 0 ------------------------------- */ \
+ uint32_t callout_number; /* Number compiled into pattern */ \
+ uint32_t capture_top; /* Max current capture */ \
+ uint32_t capture_last; /* Most recently closed capture */ \
+ PCRE2_SIZE *offset_vector; /* The offset vector */ \
+ PCRE2_SPTR mark; /* Pointer to current mark or NULL */ \
+ PCRE2_SPTR subject; /* The subject being matched */ \
+ PCRE2_SIZE subject_length; /* The length of the subject */ \
+ PCRE2_SIZE start_match; /* Offset to start of this match attempt */ \
+ PCRE2_SIZE current_position; /* Where we currently are in the subject */ \
+ PCRE2_SIZE pattern_position; /* Offset to next item in the pattern */ \
+ PCRE2_SIZE next_item_length; /* Length of next item in the pattern */ \
+ /* ------------------- Added for Version 1 -------------------------- */ \
+ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \
+ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \
+ PCRE2_SPTR callout_string; /* String compiled into pattern */ \
+ /* ------------------- Added for Version 2 -------------------------- */ \
+ uint32_t callout_flags; /* See above for list */ \
+ /* ------------------------------------------------------------------ */ \
+} pcre2_callout_block; \
+\
+typedef struct pcre2_callout_enumerate_block { \
+ uint32_t version; /* Identifies version of block */ \
+ /* ------------------------ Version 0 ------------------------------- */ \
+ PCRE2_SIZE pattern_position; /* Offset to next item in the pattern */ \
+ PCRE2_SIZE next_item_length; /* Length of next item in the pattern */ \
+ uint32_t callout_number; /* Number compiled into pattern */ \
+ PCRE2_SIZE callout_string_offset; /* Offset to string within pattern */ \
+ PCRE2_SIZE callout_string_length; /* Length of string compiled into pattern */ \
+ PCRE2_SPTR callout_string; /* String compiled into pattern */ \
+ /* ------------------------------------------------------------------ */ \
+} pcre2_callout_enumerate_block; \
+\
+typedef struct pcre2_substitute_callout_block { \
+ uint32_t version; /* Identifies version of block */ \
+ /* ------------------------ Version 0 ------------------------------- */ \
+ PCRE2_SPTR input; /* Pointer to input subject string */ \
+ PCRE2_SPTR output; /* Pointer to output buffer */ \
+ PCRE2_SIZE output_offsets[2]; /* Changed portion of the output */ \
+ PCRE2_SIZE *ovector; /* Pointer to current ovector */ \
+ uint32_t oveccount; /* Count of pairs set in ovector */ \
+ uint32_t subscount; /* Substitution number */ \
+ /* ------------------------------------------------------------------ */ \
+} pcre2_substitute_callout_block;
+
+
+/* List the generic forms of all other functions in macros, which will be
+expanded for each width below. Start with functions that give general
+information. */
+
+#define PCRE2_GENERAL_INFO_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION pcre2_config(uint32_t, void *);
+
+
+/* Functions for manipulating contexts. */
+
+#define PCRE2_GENERAL_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_general_context *PCRE2_CALL_CONVENTION \
+ pcre2_general_context_copy(pcre2_general_context *); \
+PCRE2_EXP_DECL pcre2_general_context *PCRE2_CALL_CONVENTION \
+ pcre2_general_context_create(void *(*)(PCRE2_SIZE, void *), \
+ void (*)(void *, void *), void *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_general_context_free(pcre2_general_context *);
+
+#define PCRE2_COMPILE_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_compile_context *PCRE2_CALL_CONVENTION \
+ pcre2_compile_context_copy(pcre2_compile_context *); \
+PCRE2_EXP_DECL pcre2_compile_context *PCRE2_CALL_CONVENTION \
+ pcre2_compile_context_create(pcre2_general_context *);\
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_compile_context_free(pcre2_compile_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_bsr(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_character_tables(pcre2_compile_context *, const uint8_t *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_compile_extra_options(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_max_pattern_length(pcre2_compile_context *, PCRE2_SIZE); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_newline(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_parens_nest_limit(pcre2_compile_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_compile_recursion_guard(pcre2_compile_context *, \
+ int (*)(uint32_t, void *), void *);
+
+#define PCRE2_MATCH_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_match_context *PCRE2_CALL_CONVENTION \
+ pcre2_match_context_copy(pcre2_match_context *); \
+PCRE2_EXP_DECL pcre2_match_context *PCRE2_CALL_CONVENTION \
+ pcre2_match_context_create(pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_match_context_free(pcre2_match_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_callout(pcre2_match_context *, \
+ int (*)(pcre2_callout_block *, void *), void *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_substitute_callout(pcre2_match_context *, \
+ int (*)(pcre2_substitute_callout_block *, void *), void *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_depth_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_heap_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_match_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_offset_limit(pcre2_match_context *, PCRE2_SIZE); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_recursion_limit(pcre2_match_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_recursion_memory_management(pcre2_match_context *, \
+ void *(*)(PCRE2_SIZE, void *), void (*)(void *, void *), void *);
+
+#define PCRE2_CONVERT_CONTEXT_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_convert_context *PCRE2_CALL_CONVENTION \
+ pcre2_convert_context_copy(pcre2_convert_context *); \
+PCRE2_EXP_DECL pcre2_convert_context *PCRE2_CALL_CONVENTION \
+ pcre2_convert_context_create(pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_convert_context_free(pcre2_convert_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_glob_escape(pcre2_convert_context *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_set_glob_separator(pcre2_convert_context *, uint32_t);
+
+
+/* Functions concerned with compiling a pattern to PCRE internal code. */
+
+#define PCRE2_COMPILE_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \
+ pcre2_compile(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, \
+ pcre2_compile_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_code_free(pcre2_code *); \
+PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \
+ pcre2_code_copy(const pcre2_code *); \
+PCRE2_EXP_DECL pcre2_code *PCRE2_CALL_CONVENTION \
+ pcre2_code_copy_with_tables(const pcre2_code *);
+
+
+/* Functions that give information about a compiled pattern. */
+
+#define PCRE2_PATTERN_INFO_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_pattern_info(const pcre2_code *, uint32_t, void *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_callout_enumerate(const pcre2_code *, \
+ int (*)(pcre2_callout_enumerate_block *, void *), void *);
+
+
+/* Functions for running a match and inspecting the result. */
+
+#define PCRE2_MATCH_FUNCTIONS \
+PCRE2_EXP_DECL pcre2_match_data *PCRE2_CALL_CONVENTION \
+ pcre2_match_data_create(uint32_t, pcre2_general_context *); \
+PCRE2_EXP_DECL pcre2_match_data *PCRE2_CALL_CONVENTION \
+ pcre2_match_data_create_from_pattern(const pcre2_code *, \
+ pcre2_general_context *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_dfa_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \
+ uint32_t, pcre2_match_data *, pcre2_match_context *, int *, PCRE2_SIZE); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \
+ uint32_t, pcre2_match_data *, pcre2_match_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_match_data_free(pcre2_match_data *); \
+PCRE2_EXP_DECL PCRE2_SPTR PCRE2_CALL_CONVENTION \
+ pcre2_get_mark(pcre2_match_data *); \
+PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \
+ pcre2_get_match_data_size(pcre2_match_data *); \
+PCRE2_EXP_DECL uint32_t PCRE2_CALL_CONVENTION \
+ pcre2_get_ovector_count(pcre2_match_data *); \
+PCRE2_EXP_DECL PCRE2_SIZE *PCRE2_CALL_CONVENTION \
+ pcre2_get_ovector_pointer(pcre2_match_data *); \
+PCRE2_EXP_DECL PCRE2_SIZE PCRE2_CALL_CONVENTION \
+ pcre2_get_startchar(pcre2_match_data *);
+
+
+/* Convenience functions for handling matched substrings. */
+
+#define PCRE2_SUBSTRING_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_copy_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR *, \
+ PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_copy_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR *, \
+ PCRE2_SIZE *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_substring_free(PCRE2_UCHAR *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_get_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_UCHAR **, \
+ PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_get_bynumber(pcre2_match_data *, uint32_t, PCRE2_UCHAR **, \
+ PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_length_byname(pcre2_match_data *, PCRE2_SPTR, PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_length_bynumber(pcre2_match_data *, uint32_t, PCRE2_SIZE *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_nametable_scan(const pcre2_code *, PCRE2_SPTR, PCRE2_SPTR *, \
+ PCRE2_SPTR *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_number_from_name(const pcre2_code *, PCRE2_SPTR); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_substring_list_free(PCRE2_SPTR *); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substring_list_get(pcre2_match_data *, PCRE2_UCHAR ***, PCRE2_SIZE **);
+
+/* Functions for serializing / deserializing compiled patterns. */
+
+#define PCRE2_SERIALIZE_FUNCTIONS \
+PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \
+ pcre2_serialize_encode(const pcre2_code **, int32_t, uint8_t **, \
+ PCRE2_SIZE *, pcre2_general_context *); \
+PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \
+ pcre2_serialize_decode(pcre2_code **, int32_t, const uint8_t *, \
+ pcre2_general_context *); \
+PCRE2_EXP_DECL int32_t PCRE2_CALL_CONVENTION \
+ pcre2_serialize_get_number_of_codes(const uint8_t *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_serialize_free(uint8_t *);
+
+
+/* Convenience function for match + substitute. */
+
+#define PCRE2_SUBSTITUTE_FUNCTION \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_substitute(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \
+ uint32_t, pcre2_match_data *, pcre2_match_context *, PCRE2_SPTR, \
+ PCRE2_SIZE, PCRE2_UCHAR *, PCRE2_SIZE *);
+
+
+/* Functions for converting pattern source strings. */
+
+#define PCRE2_CONVERT_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_pattern_convert(PCRE2_SPTR, PCRE2_SIZE, uint32_t, PCRE2_UCHAR **, \
+ PCRE2_SIZE *, pcre2_convert_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_converted_pattern_free(PCRE2_UCHAR *);
+
+
+/* Functions for JIT processing */
+
+#define PCRE2_JIT_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_jit_compile(pcre2_code *, uint32_t); \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_jit_match(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, \
+ uint32_t, pcre2_match_data *, pcre2_match_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_jit_free_unused_memory(pcre2_general_context *); \
+PCRE2_EXP_DECL pcre2_jit_stack *PCRE2_CALL_CONVENTION \
+ pcre2_jit_stack_create(PCRE2_SIZE, PCRE2_SIZE, pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_jit_stack_assign(pcre2_match_context *, pcre2_jit_callback, void *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_jit_stack_free(pcre2_jit_stack *);
+
+
+/* Other miscellaneous functions. */
+
+#define PCRE2_OTHER_FUNCTIONS \
+PCRE2_EXP_DECL int PCRE2_CALL_CONVENTION \
+ pcre2_get_error_message(int, PCRE2_UCHAR *, PCRE2_SIZE); \
+PCRE2_EXP_DECL const uint8_t *PCRE2_CALL_CONVENTION \
+ pcre2_maketables(pcre2_general_context *); \
+PCRE2_EXP_DECL void PCRE2_CALL_CONVENTION \
+ pcre2_maketables_free(pcre2_general_context *, const uint8_t *);
+
+/* Define macros that generate width-specific names from generic versions. The
+three-level macro scheme is necessary to get the macros expanded when we want
+them to be. First we get the width from PCRE2_LOCAL_WIDTH, which is used for
+generating three versions of everything below. After that, PCRE2_SUFFIX will be
+re-defined to use PCRE2_CODE_UNIT_WIDTH, for use when macros such as
+pcre2_compile are called by application code. */
+
+#define PCRE2_JOIN(a,b) a ## b
+#define PCRE2_GLUE(a,b) PCRE2_JOIN(a,b)
+#define PCRE2_SUFFIX(a) PCRE2_GLUE(a,PCRE2_LOCAL_WIDTH)
+
+
+/* Data types */
+
+#define PCRE2_UCHAR PCRE2_SUFFIX(PCRE2_UCHAR)
+#define PCRE2_SPTR PCRE2_SUFFIX(PCRE2_SPTR)
+
+#define pcre2_code PCRE2_SUFFIX(pcre2_code_)
+#define pcre2_jit_callback PCRE2_SUFFIX(pcre2_jit_callback_)
+#define pcre2_jit_stack PCRE2_SUFFIX(pcre2_jit_stack_)
+
+#define pcre2_real_code PCRE2_SUFFIX(pcre2_real_code_)
+#define pcre2_real_general_context PCRE2_SUFFIX(pcre2_real_general_context_)
+#define pcre2_real_compile_context PCRE2_SUFFIX(pcre2_real_compile_context_)
+#define pcre2_real_convert_context PCRE2_SUFFIX(pcre2_real_convert_context_)
+#define pcre2_real_match_context PCRE2_SUFFIX(pcre2_real_match_context_)
+#define pcre2_real_jit_stack PCRE2_SUFFIX(pcre2_real_jit_stack_)
+#define pcre2_real_match_data PCRE2_SUFFIX(pcre2_real_match_data_)
+
+
+/* Data blocks */
+
+#define pcre2_callout_block PCRE2_SUFFIX(pcre2_callout_block_)
+#define pcre2_callout_enumerate_block PCRE2_SUFFIX(pcre2_callout_enumerate_block_)
+#define pcre2_substitute_callout_block PCRE2_SUFFIX(pcre2_substitute_callout_block_)
+#define pcre2_general_context PCRE2_SUFFIX(pcre2_general_context_)
+#define pcre2_compile_context PCRE2_SUFFIX(pcre2_compile_context_)
+#define pcre2_convert_context PCRE2_SUFFIX(pcre2_convert_context_)
+#define pcre2_match_context PCRE2_SUFFIX(pcre2_match_context_)
+#define pcre2_match_data PCRE2_SUFFIX(pcre2_match_data_)
+
+
+/* Functions: the complete list in alphabetical order */
+
+#define pcre2_callout_enumerate PCRE2_SUFFIX(pcre2_callout_enumerate_)
+#define pcre2_code_copy PCRE2_SUFFIX(pcre2_code_copy_)
+#define pcre2_code_copy_with_tables PCRE2_SUFFIX(pcre2_code_copy_with_tables_)
+#define pcre2_code_free PCRE2_SUFFIX(pcre2_code_free_)
+#define pcre2_compile PCRE2_SUFFIX(pcre2_compile_)
+#define pcre2_compile_context_copy PCRE2_SUFFIX(pcre2_compile_context_copy_)
+#define pcre2_compile_context_create PCRE2_SUFFIX(pcre2_compile_context_create_)
+#define pcre2_compile_context_free PCRE2_SUFFIX(pcre2_compile_context_free_)
+#define pcre2_config PCRE2_SUFFIX(pcre2_config_)
+#define pcre2_convert_context_copy PCRE2_SUFFIX(pcre2_convert_context_copy_)
+#define pcre2_convert_context_create PCRE2_SUFFIX(pcre2_convert_context_create_)
+#define pcre2_convert_context_free PCRE2_SUFFIX(pcre2_convert_context_free_)
+#define pcre2_converted_pattern_free PCRE2_SUFFIX(pcre2_converted_pattern_free_)
+#define pcre2_dfa_match PCRE2_SUFFIX(pcre2_dfa_match_)
+#define pcre2_general_context_copy PCRE2_SUFFIX(pcre2_general_context_copy_)
+#define pcre2_general_context_create PCRE2_SUFFIX(pcre2_general_context_create_)
+#define pcre2_general_context_free PCRE2_SUFFIX(pcre2_general_context_free_)
+#define pcre2_get_error_message PCRE2_SUFFIX(pcre2_get_error_message_)
+#define pcre2_get_mark PCRE2_SUFFIX(pcre2_get_mark_)
+#define pcre2_get_match_data_size PCRE2_SUFFIX(pcre2_get_match_data_size_)
+#define pcre2_get_ovector_pointer PCRE2_SUFFIX(pcre2_get_ovector_pointer_)
+#define pcre2_get_ovector_count PCRE2_SUFFIX(pcre2_get_ovector_count_)
+#define pcre2_get_startchar PCRE2_SUFFIX(pcre2_get_startchar_)
+#define pcre2_jit_compile PCRE2_SUFFIX(pcre2_jit_compile_)
+#define pcre2_jit_match PCRE2_SUFFIX(pcre2_jit_match_)
+#define pcre2_jit_free_unused_memory PCRE2_SUFFIX(pcre2_jit_free_unused_memory_)
+#define pcre2_jit_stack_assign PCRE2_SUFFIX(pcre2_jit_stack_assign_)
+#define pcre2_jit_stack_create PCRE2_SUFFIX(pcre2_jit_stack_create_)
+#define pcre2_jit_stack_free PCRE2_SUFFIX(pcre2_jit_stack_free_)
+#define pcre2_maketables PCRE2_SUFFIX(pcre2_maketables_)
+#define pcre2_maketables_free PCRE2_SUFFIX(pcre2_maketables_free_)
+#define pcre2_match PCRE2_SUFFIX(pcre2_match_)
+#define pcre2_match_context_copy PCRE2_SUFFIX(pcre2_match_context_copy_)
+#define pcre2_match_context_create PCRE2_SUFFIX(pcre2_match_context_create_)
+#define pcre2_match_context_free PCRE2_SUFFIX(pcre2_match_context_free_)
+#define pcre2_match_data_create PCRE2_SUFFIX(pcre2_match_data_create_)
+#define pcre2_match_data_create_from_pattern PCRE2_SUFFIX(pcre2_match_data_create_from_pattern_)
+#define pcre2_match_data_free PCRE2_SUFFIX(pcre2_match_data_free_)
+#define pcre2_pattern_convert PCRE2_SUFFIX(pcre2_pattern_convert_)
+#define pcre2_pattern_info PCRE2_SUFFIX(pcre2_pattern_info_)
+#define pcre2_serialize_decode PCRE2_SUFFIX(pcre2_serialize_decode_)
+#define pcre2_serialize_encode PCRE2_SUFFIX(pcre2_serialize_encode_)
+#define pcre2_serialize_free PCRE2_SUFFIX(pcre2_serialize_free_)
+#define pcre2_serialize_get_number_of_codes PCRE2_SUFFIX(pcre2_serialize_get_number_of_codes_)
+#define pcre2_set_bsr PCRE2_SUFFIX(pcre2_set_bsr_)
+#define pcre2_set_callout PCRE2_SUFFIX(pcre2_set_callout_)
+#define pcre2_set_character_tables PCRE2_SUFFIX(pcre2_set_character_tables_)
+#define pcre2_set_compile_extra_options PCRE2_SUFFIX(pcre2_set_compile_extra_options_)
+#define pcre2_set_compile_recursion_guard PCRE2_SUFFIX(pcre2_set_compile_recursion_guard_)
+#define pcre2_set_depth_limit PCRE2_SUFFIX(pcre2_set_depth_limit_)
+#define pcre2_set_glob_escape PCRE2_SUFFIX(pcre2_set_glob_escape_)
+#define pcre2_set_glob_separator PCRE2_SUFFIX(pcre2_set_glob_separator_)
+#define pcre2_set_heap_limit PCRE2_SUFFIX(pcre2_set_heap_limit_)
+#define pcre2_set_match_limit PCRE2_SUFFIX(pcre2_set_match_limit_)
+#define pcre2_set_max_pattern_length PCRE2_SUFFIX(pcre2_set_max_pattern_length_)
+#define pcre2_set_newline PCRE2_SUFFIX(pcre2_set_newline_)
+#define pcre2_set_parens_nest_limit PCRE2_SUFFIX(pcre2_set_parens_nest_limit_)
+#define pcre2_set_offset_limit PCRE2_SUFFIX(pcre2_set_offset_limit_)
+#define pcre2_set_substitute_callout PCRE2_SUFFIX(pcre2_set_substitute_callout_)
+#define pcre2_substitute PCRE2_SUFFIX(pcre2_substitute_)
+#define pcre2_substring_copy_byname PCRE2_SUFFIX(pcre2_substring_copy_byname_)
+#define pcre2_substring_copy_bynumber PCRE2_SUFFIX(pcre2_substring_copy_bynumber_)
+#define pcre2_substring_free PCRE2_SUFFIX(pcre2_substring_free_)
+#define pcre2_substring_get_byname PCRE2_SUFFIX(pcre2_substring_get_byname_)
+#define pcre2_substring_get_bynumber PCRE2_SUFFIX(pcre2_substring_get_bynumber_)
+#define pcre2_substring_length_byname PCRE2_SUFFIX(pcre2_substring_length_byname_)
+#define pcre2_substring_length_bynumber PCRE2_SUFFIX(pcre2_substring_length_bynumber_)
+#define pcre2_substring_list_get PCRE2_SUFFIX(pcre2_substring_list_get_)
+#define pcre2_substring_list_free PCRE2_SUFFIX(pcre2_substring_list_free_)
+#define pcre2_substring_nametable_scan PCRE2_SUFFIX(pcre2_substring_nametable_scan_)
+#define pcre2_substring_number_from_name PCRE2_SUFFIX(pcre2_substring_number_from_name_)
+
+/* Keep this old function name for backwards compatibility */
+#define pcre2_set_recursion_limit PCRE2_SUFFIX(pcre2_set_recursion_limit_)
+
+/* Keep this obsolete function for backwards compatibility: it is now a noop. */
+#define pcre2_set_recursion_memory_management PCRE2_SUFFIX(pcre2_set_recursion_memory_management_)
+
+/* Now generate all three sets of width-specific structures and function
+prototypes. */
+
+#define PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS \
+PCRE2_TYPES_LIST \
+PCRE2_STRUCTURE_LIST \
+PCRE2_GENERAL_INFO_FUNCTIONS \
+PCRE2_GENERAL_CONTEXT_FUNCTIONS \
+PCRE2_COMPILE_CONTEXT_FUNCTIONS \
+PCRE2_CONVERT_CONTEXT_FUNCTIONS \
+PCRE2_CONVERT_FUNCTIONS \
+PCRE2_MATCH_CONTEXT_FUNCTIONS \
+PCRE2_COMPILE_FUNCTIONS \
+PCRE2_PATTERN_INFO_FUNCTIONS \
+PCRE2_MATCH_FUNCTIONS \
+PCRE2_SUBSTRING_FUNCTIONS \
+PCRE2_SERIALIZE_FUNCTIONS \
+PCRE2_SUBSTITUTE_FUNCTION \
+PCRE2_JIT_FUNCTIONS \
+PCRE2_OTHER_FUNCTIONS
+
+#define PCRE2_LOCAL_WIDTH 8
+PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
+#undef PCRE2_LOCAL_WIDTH
+
+#define PCRE2_LOCAL_WIDTH 16
+PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
+#undef PCRE2_LOCAL_WIDTH
+
+#define PCRE2_LOCAL_WIDTH 32
+PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
+#undef PCRE2_LOCAL_WIDTH
+
+/* Undefine the list macros; they are no longer needed. */
+
+#undef PCRE2_TYPES_LIST
+#undef PCRE2_STRUCTURE_LIST
+#undef PCRE2_GENERAL_INFO_FUNCTIONS
+#undef PCRE2_GENERAL_CONTEXT_FUNCTIONS
+#undef PCRE2_COMPILE_CONTEXT_FUNCTIONS
+#undef PCRE2_CONVERT_CONTEXT_FUNCTIONS
+#undef PCRE2_MATCH_CONTEXT_FUNCTIONS
+#undef PCRE2_COMPILE_FUNCTIONS
+#undef PCRE2_PATTERN_INFO_FUNCTIONS
+#undef PCRE2_MATCH_FUNCTIONS
+#undef PCRE2_SUBSTRING_FUNCTIONS
+#undef PCRE2_SERIALIZE_FUNCTIONS
+#undef PCRE2_SUBSTITUTE_FUNCTION
+#undef PCRE2_JIT_FUNCTIONS
+#undef PCRE2_OTHER_FUNCTIONS
+#undef PCRE2_TYPES_STRUCTURES_AND_FUNCTIONS
+
+/* PCRE2_CODE_UNIT_WIDTH must be defined. If it is 8, 16, or 32, redefine
+PCRE2_SUFFIX to use it. If it is 0, undefine the other macros and make
+PCRE2_SUFFIX a no-op. Otherwise, generate an error. */
+
+#undef PCRE2_SUFFIX
+#ifndef PCRE2_CODE_UNIT_WIDTH
+#error PCRE2_CODE_UNIT_WIDTH must be defined before including pcre2.h.
+#error Use 8, 16, or 32; or 0 for a multi-width application.
+#else /* PCRE2_CODE_UNIT_WIDTH is defined */
+#if PCRE2_CODE_UNIT_WIDTH == 8 || \
+ PCRE2_CODE_UNIT_WIDTH == 16 || \
+ PCRE2_CODE_UNIT_WIDTH == 32
+#define PCRE2_SUFFIX(a) PCRE2_GLUE(a, PCRE2_CODE_UNIT_WIDTH)
+#elif PCRE2_CODE_UNIT_WIDTH == 0
+#undef PCRE2_JOIN
+#undef PCRE2_GLUE
+#define PCRE2_SUFFIX(a) a
+#else
+#error PCRE2_CODE_UNIT_WIDTH must be 0, 8, 16, or 32.
+#endif
+#endif /* PCRE2_CODE_UNIT_WIDTH is defined */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PCRE2_H_IDEMPOTENT_GUARD */
+
+/* End of pcre2.h */
diff --git a/contrib/libs/pcre2/src/pcre2_auto_possess.c b/contrib/libs/pcre2/src/pcre2_auto_possess.c
new file mode 100644
index 0000000000..e8338690ca
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_auto_possess.c
@@ -0,0 +1,1365 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains functions that scan a compiled pattern and change
+repeats into possessive repeats where possible. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Tables for auto-possessification *
+*************************************************/
+
+/* This table is used to check whether auto-possessification is possible
+between adjacent character-type opcodes. The left-hand (repeated) opcode is
+used to select the row, and the right-hand opcode is use to select the column.
+A value of 1 means that auto-possessification is OK. For example, the second
+value in the first row means that \D+\d can be turned into \D++\d.
+
+The Unicode property types (\P and \p) have to be present to fill out the table
+because of what their opcode values are, but the table values should always be
+zero because property types are handled separately in the code. The last four
+columns apply to items that cannot be repeated, so there is no need to have
+rows for them. Note that OP_DIGIT etc. are generated only when PCRE_UCP is
+*not* set. When it is set, \d etc. are converted into OP_(NOT_)PROP codes. */
+
+#define APTROWS (LAST_AUTOTAB_LEFT_OP - FIRST_AUTOTAB_OP + 1)
+#define APTCOLS (LAST_AUTOTAB_RIGHT_OP - FIRST_AUTOTAB_OP + 1)
+
+static const uint8_t autoposstab[APTROWS][APTCOLS] = {
+/* \D \d \S \s \W \w . .+ \C \P \p \R \H \h \V \v \X \Z \z $ $M */
+ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \D */
+ { 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \d */
+ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \S */
+ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \s */
+ { 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \W */
+ { 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1 }, /* \w */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0 }, /* .+ */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, /* \C */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* \P */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* \p */
+ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, /* \R */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 }, /* \H */
+ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0 }, /* \h */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, /* \V */
+ { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0 }, /* \v */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 } /* \X */
+};
+
+#ifdef SUPPORT_UNICODE
+/* This table is used to check whether auto-possessification is possible
+between adjacent Unicode property opcodes (OP_PROP and OP_NOTPROP). The
+left-hand (repeated) opcode is used to select the row, and the right-hand
+opcode is used to select the column. The values are as follows:
+
+ 0 Always return FALSE (never auto-possessify)
+ 1 Character groups are distinct (possessify if both are OP_PROP)
+ 2 Check character categories in the same group (general or particular)
+ 3 TRUE if the two opcodes are not the same (PROP vs NOTPROP)
+
+ 4 Check left general category vs right particular category
+ 5 Check right general category vs left particular category
+
+ 6 Left alphanum vs right general category
+ 7 Left space vs right general category
+ 8 Left word vs right general category
+
+ 9 Right alphanum vs left general category
+ 10 Right space vs left general category
+ 11 Right word vs left general category
+
+ 12 Left alphanum vs right particular category
+ 13 Left space vs right particular category
+ 14 Left word vs right particular category
+
+ 15 Right alphanum vs left particular category
+ 16 Right space vs left particular category
+ 17 Right word vs left particular category
+*/
+
+static const uint8_t propposstab[PT_TABSIZE][PT_TABSIZE] = {
+/* ANY LAMP GC PC SC SCX ALNUM SPACE PXSPACE WORD CLIST UCNC BIDICL BOOL */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_ANY */
+ { 0, 3, 0, 0, 0, 0, 3, 1, 1, 0, 0, 0, 0, 0 }, /* PT_LAMP */
+ { 0, 0, 2, 4, 0, 0, 9, 10, 10, 11, 0, 0, 0, 0 }, /* PT_GC */
+ { 0, 0, 5, 2, 0, 0, 15, 16, 16, 17, 0, 0, 0, 0 }, /* PT_PC */
+ { 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_SC */
+ { 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_SCX */
+ { 0, 3, 6, 12, 0, 0, 3, 1, 1, 0, 0, 0, 0, 0 }, /* PT_ALNUM */
+ { 0, 1, 7, 13, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0 }, /* PT_SPACE */
+ { 0, 1, 7, 13, 0, 0, 1, 3, 3, 1, 0, 0, 0, 0 }, /* PT_PXSPACE */
+ { 0, 0, 8, 14, 0, 0, 0, 1, 1, 3, 0, 0, 0, 0 }, /* PT_WORD */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_CLIST */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, /* PT_UCNC */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* PT_BIDICL */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } /* PT_BOOL */
+};
+
+/* This table is used to check whether auto-possessification is possible
+between adjacent Unicode property opcodes (OP_PROP and OP_NOTPROP) when one
+specifies a general category and the other specifies a particular category. The
+row is selected by the general category and the column by the particular
+category. The value is 1 if the particular category is not part of the general
+category. */
+
+static const uint8_t catposstab[7][30] = {
+/* Cc Cf Cn Co Cs Ll Lm Lo Lt Lu Mc Me Mn Nd Nl No Pc Pd Pe Pf Pi Po Ps Sc Sk Sm So Zl Zp Zs */
+ { 0, 0, 0, 0, 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 }, /* C */
+ { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* L */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* M */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* N */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 }, /* P */
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1 }, /* S */
+ { 1, 1, 1, 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, 0, 0 } /* Z */
+};
+
+/* This table is used when checking ALNUM, (PX)SPACE, SPACE, and WORD against
+a general or particular category. The properties in each row are those
+that apply to the character set in question. Duplication means that a little
+unnecessary work is done when checking, but this keeps things much simpler
+because they can all use the same code. For more details see the comment where
+this table is used.
+
+Note: SPACE and PXSPACE used to be different because Perl excluded VT from
+"space", but from Perl 5.18 it's included, so both categories are treated the
+same here. */
+
+static const uint8_t posspropstab[3][4] = {
+ { ucp_L, ucp_N, ucp_N, ucp_Nl }, /* ALNUM, 3rd and 4th values redundant */
+ { ucp_Z, ucp_Z, ucp_C, ucp_Cc }, /* SPACE and PXSPACE, 2nd value redundant */
+ { ucp_L, ucp_N, ucp_P, ucp_Po } /* WORD */
+};
+#endif /* SUPPORT_UNICODE */
+
+
+
+#ifdef SUPPORT_UNICODE
+/*************************************************
+* Check a character and a property *
+*************************************************/
+
+/* This function is called by compare_opcodes() when a property item is
+adjacent to a fixed character.
+
+Arguments:
+ c the character
+ ptype the property type
+ pdata the data for the type
+ negated TRUE if it's a negated property (\P or \p{^)
+
+Returns: TRUE if auto-possessifying is OK
+*/
+
+static BOOL
+check_char_prop(uint32_t c, unsigned int ptype, unsigned int pdata,
+ BOOL negated)
+{
+BOOL ok;
+const uint32_t *p;
+const ucd_record *prop = GET_UCD(c);
+
+switch(ptype)
+ {
+ case PT_LAMP:
+ return (prop->chartype == ucp_Lu ||
+ prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt) == negated;
+
+ case PT_GC:
+ return (pdata == PRIV(ucp_gentype)[prop->chartype]) == negated;
+
+ case PT_PC:
+ return (pdata == prop->chartype) == negated;
+
+ case PT_SC:
+ return (pdata == prop->script) == negated;
+
+ case PT_SCX:
+ ok = (pdata == prop->script
+ || MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), pdata) != 0);
+ return ok == negated;
+
+ /* These are specials */
+
+ case PT_ALNUM:
+ return (PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N) == negated;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included, which
+ means that Perl space and POSIX space are now identical. PCRE was changed
+ at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ return negated;
+
+ default:
+ return (PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == negated;
+ }
+ break; /* Control never reaches here */
+
+ case PT_WORD:
+ return (PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE) == negated;
+
+ case PT_CLIST:
+ p = PRIV(ucd_caseless_sets) + prop->caseset;
+ for (;;)
+ {
+ if (c < *p) return !negated;
+ if (c == *p++) return negated;
+ }
+ break; /* Control never reaches here */
+
+ /* Haven't yet thought these through. */
+
+ case PT_BIDICL:
+ return FALSE;
+
+ case PT_BOOL:
+ return FALSE;
+ }
+
+return FALSE;
+}
+#endif /* SUPPORT_UNICODE */
+
+
+
+/*************************************************
+* Base opcode of repeated opcodes *
+*************************************************/
+
+/* Returns the base opcode for repeated single character type opcodes. If the
+opcode is not a repeated character type, it returns with the original value.
+
+Arguments: c opcode
+Returns: base opcode for the type
+*/
+
+static PCRE2_UCHAR
+get_repeat_base(PCRE2_UCHAR c)
+{
+return (c > OP_TYPEPOSUPTO)? c :
+ (c >= OP_TYPESTAR)? OP_TYPESTAR :
+ (c >= OP_NOTSTARI)? OP_NOTSTARI :
+ (c >= OP_NOTSTAR)? OP_NOTSTAR :
+ (c >= OP_STARI)? OP_STARI :
+ OP_STAR;
+}
+
+
+/*************************************************
+* Fill the character property list *
+*************************************************/
+
+/* Checks whether the code points to an opcode that can take part in auto-
+possessification, and if so, fills a list with its properties.
+
+Arguments:
+ code points to start of expression
+ utf TRUE if in UTF mode
+ ucp TRUE if in UCP mode
+ fcc points to the case-flipping table
+ list points to output list
+ list[0] will be filled with the opcode
+ list[1] will be non-zero if this opcode
+ can match an empty character string
+ list[2..7] depends on the opcode
+
+Returns: points to the start of the next opcode if *code is accepted
+ NULL if *code is not accepted
+*/
+
+static PCRE2_SPTR
+get_chr_property_list(PCRE2_SPTR code, BOOL utf, BOOL ucp, const uint8_t *fcc,
+ uint32_t *list)
+{
+PCRE2_UCHAR c = *code;
+PCRE2_UCHAR base;
+PCRE2_SPTR end;
+uint32_t chr;
+
+#ifdef SUPPORT_UNICODE
+uint32_t *clist_dest;
+const uint32_t *clist_src;
+#else
+(void)utf; /* Suppress "unused parameter" compiler warnings */
+(void)ucp;
+#endif
+
+list[0] = c;
+list[1] = FALSE;
+code++;
+
+if (c >= OP_STAR && c <= OP_TYPEPOSUPTO)
+ {
+ base = get_repeat_base(c);
+ c -= (base - OP_STAR);
+
+ if (c == OP_UPTO || c == OP_MINUPTO || c == OP_EXACT || c == OP_POSUPTO)
+ code += IMM2_SIZE;
+
+ list[1] = (c != OP_PLUS && c != OP_MINPLUS && c != OP_EXACT &&
+ c != OP_POSPLUS);
+
+ switch(base)
+ {
+ case OP_STAR:
+ list[0] = OP_CHAR;
+ break;
+
+ case OP_STARI:
+ list[0] = OP_CHARI;
+ break;
+
+ case OP_NOTSTAR:
+ list[0] = OP_NOT;
+ break;
+
+ case OP_NOTSTARI:
+ list[0] = OP_NOTI;
+ break;
+
+ case OP_TYPESTAR:
+ list[0] = *code;
+ code++;
+ break;
+ }
+ c = list[0];
+ }
+
+switch(c)
+ {
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_ANYNL:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ case OP_EXTUNI:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_DOLL:
+ case OP_DOLLM:
+ return code;
+
+ case OP_CHAR:
+ case OP_NOT:
+ GETCHARINCTEST(chr, code);
+ list[2] = chr;
+ list[3] = NOTACHAR;
+ return code;
+
+ case OP_CHARI:
+ case OP_NOTI:
+ list[0] = (c == OP_CHARI) ? OP_CHAR : OP_NOT;
+ GETCHARINCTEST(chr, code);
+ list[2] = chr;
+
+#ifdef SUPPORT_UNICODE
+ if (chr < 128 || (chr < 256 && !utf && !ucp))
+ list[3] = fcc[chr];
+ else
+ list[3] = UCD_OTHERCASE(chr);
+#elif defined SUPPORT_WIDE_CHARS
+ list[3] = (chr < 256) ? fcc[chr] : chr;
+#else
+ list[3] = fcc[chr];
+#endif
+
+ /* The othercase might be the same value. */
+
+ if (chr == list[3])
+ list[3] = NOTACHAR;
+ else
+ list[4] = NOTACHAR;
+ return code;
+
+#ifdef SUPPORT_UNICODE
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (code[0] != PT_CLIST)
+ {
+ list[2] = code[0];
+ list[3] = code[1];
+ return code + 2;
+ }
+
+ /* Convert only if we have enough space. */
+
+ clist_src = PRIV(ucd_caseless_sets) + code[1];
+ clist_dest = list + 2;
+ code += 2;
+
+ do {
+ if (clist_dest >= list + 8)
+ {
+ /* Early return if there is not enough space. This should never
+ happen, since all clists are shorter than 5 character now. */
+ list[2] = code[0];
+ list[3] = code[1];
+ return code;
+ }
+ *clist_dest++ = *clist_src;
+ }
+ while(*clist_src++ != NOTACHAR);
+
+ /* All characters are stored. The terminating NOTACHAR is copied from the
+ clist itself. */
+
+ list[0] = (c == OP_PROP) ? OP_CHAR : OP_NOT;
+ return code;
+#endif
+
+ case OP_NCLASS:
+ case OP_CLASS:
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ if (c == OP_XCLASS)
+ end = code + GET(code, 0) - 1;
+ else
+#endif
+ end = code + 32 / sizeof(PCRE2_UCHAR);
+
+ switch(*end)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSQUERY:
+ list[1] = TRUE;
+ end++;
+ break;
+
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ end++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ list[1] = (GET2(end, 1) == 0);
+ end += 1 + 2 * IMM2_SIZE;
+ break;
+ }
+ list[2] = (uint32_t)(end - code);
+ return end;
+ }
+
+return NULL; /* Opcode not accepted */
+}
+
+
+
+/*************************************************
+* Scan further character sets for match *
+*************************************************/
+
+/* Checks whether the base and the current opcode have a common character, in
+which case the base cannot be possessified.
+
+Arguments:
+ code points to the byte code
+ utf TRUE in UTF mode
+ ucp TRUE in UCP mode
+ cb compile data block
+ base_list the data list of the base opcode
+ base_end the end of the base opcode
+ rec_limit points to recursion depth counter
+
+Returns: TRUE if the auto-possessification is possible
+*/
+
+static BOOL
+compare_opcodes(PCRE2_SPTR code, BOOL utf, BOOL ucp, const compile_block *cb,
+ const uint32_t *base_list, PCRE2_SPTR base_end, int *rec_limit)
+{
+PCRE2_UCHAR c;
+uint32_t list[8];
+const uint32_t *chr_ptr;
+const uint32_t *ochr_ptr;
+const uint32_t *list_ptr;
+PCRE2_SPTR next_code;
+#ifdef SUPPORT_WIDE_CHARS
+PCRE2_SPTR xclass_flags;
+#endif
+const uint8_t *class_bitset;
+const uint8_t *set1, *set2, *set_end;
+uint32_t chr;
+BOOL accepted, invert_bits;
+BOOL entered_a_group = FALSE;
+
+if (--(*rec_limit) <= 0) return FALSE; /* Recursion has gone too deep */
+
+/* Note: the base_list[1] contains whether the current opcode has a greedy
+(represented by a non-zero value) quantifier. This is a different from
+other character type lists, which store here that the character iterator
+matches to an empty string (also represented by a non-zero value). */
+
+for(;;)
+ {
+ /* All operations move the code pointer forward.
+ Therefore infinite recursions are not possible. */
+
+ c = *code;
+
+ /* Skip over callouts */
+
+ if (c == OP_CALLOUT)
+ {
+ code += PRIV(OP_lengths)[c];
+ continue;
+ }
+
+ if (c == OP_CALLOUT_STR)
+ {
+ code += GET(code, 1 + 2*LINK_SIZE);
+ continue;
+ }
+
+ /* At the end of a branch, skip to the end of the group. */
+
+ if (c == OP_ALT)
+ {
+ do code += GET(code, 1); while (*code == OP_ALT);
+ c = *code;
+ }
+
+ /* Inspect the next opcode. */
+
+ switch(c)
+ {
+ /* We can always possessify a greedy iterator at the end of the pattern,
+ which is reached after skipping over the final OP_KET. A non-greedy
+ iterator must never be possessified. */
+
+ case OP_END:
+ return base_list[1] != 0;
+
+ /* When an iterator is at the end of certain kinds of group we can inspect
+ what follows the group by skipping over the closing ket. Note that this
+ does not apply to OP_KETRMAX or OP_KETRMIN because what follows any given
+ iteration is variable (could be another iteration or could be the next
+ item). As these two opcodes are not listed in the next switch, they will
+ end up as the next code to inspect, and return FALSE by virtue of being
+ unsupported. */
+
+ case OP_KET:
+ case OP_KETRPOS:
+ /* The non-greedy case cannot be converted to a possessive form. */
+
+ if (base_list[1] == 0) return FALSE;
+
+ /* If the bracket is capturing it might be referenced by an OP_RECURSE
+ so its last iterator can never be possessified if the pattern contains
+ recursions. (This could be improved by keeping a list of group numbers that
+ are called by recursion.) */
+
+ switch(*(code - GET(code, 1)))
+ {
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ if (cb->had_recurse) return FALSE;
+ break;
+
+ /* A script run might have to backtrack if the iterated item can match
+ characters from more than one script. So give up unless repeating an
+ explicit character. */
+
+ case OP_SCRIPT_RUN:
+ if (base_list[0] != OP_CHAR && base_list[0] != OP_CHARI)
+ return FALSE;
+ break;
+
+ /* Atomic sub-patterns and assertions can always auto-possessify their
+ last iterator. However, if the group was entered as a result of checking
+ a previous iterator, this is not possible. */
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ONCE:
+ return !entered_a_group;
+
+ /* Non-atomic assertions - don't possessify last iterator. This needs
+ more thought. */
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ return FALSE;
+ }
+
+ /* Skip over the bracket and inspect what comes next. */
+
+ code += PRIV(OP_lengths)[c];
+ continue;
+
+ /* Handle cases where the next item is a group. */
+
+ case OP_ONCE:
+ case OP_BRA:
+ case OP_CBRA:
+ next_code = code + GET(code, 1);
+ code += PRIV(OP_lengths)[c];
+
+ /* Check each branch. We have to recurse a level for all but the last
+ branch. */
+
+ while (*next_code == OP_ALT)
+ {
+ if (!compare_opcodes(code, utf, ucp, cb, base_list, base_end, rec_limit))
+ return FALSE;
+ code = next_code + 1 + LINK_SIZE;
+ next_code += GET(next_code, 1);
+ }
+
+ entered_a_group = TRUE;
+ continue;
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+
+ next_code = code + 1;
+ if (*next_code != OP_BRA && *next_code != OP_CBRA &&
+ *next_code != OP_ONCE) return FALSE;
+
+ do next_code += GET(next_code, 1); while (*next_code == OP_ALT);
+
+ /* The bracket content will be checked by the OP_BRA/OP_CBRA case above. */
+
+ next_code += 1 + LINK_SIZE;
+ if (!compare_opcodes(next_code, utf, ucp, cb, base_list, base_end,
+ rec_limit))
+ return FALSE;
+
+ code += PRIV(OP_lengths)[c];
+ continue;
+
+ /* The next opcode does not need special handling; fall through and use it
+ to see if the base can be possessified. */
+
+ default:
+ break;
+ }
+
+ /* We now have the next appropriate opcode to compare with the base. Check
+ for a supported opcode, and load its properties. */
+
+ code = get_chr_property_list(code, utf, ucp, cb->fcc, list);
+ if (code == NULL) return FALSE; /* Unsupported */
+
+ /* If either opcode is a small character list, set pointers for comparing
+ characters from that list with another list, or with a property. */
+
+ if (base_list[0] == OP_CHAR)
+ {
+ chr_ptr = base_list + 2;
+ list_ptr = list;
+ }
+ else if (list[0] == OP_CHAR)
+ {
+ chr_ptr = list + 2;
+ list_ptr = base_list;
+ }
+
+ /* Character bitsets can also be compared to certain opcodes. */
+
+ else if (base_list[0] == OP_CLASS || list[0] == OP_CLASS
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ /* In 8 bit, non-UTF mode, OP_CLASS and OP_NCLASS are the same. */
+ || (!utf && (base_list[0] == OP_NCLASS || list[0] == OP_NCLASS))
+#endif
+ )
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (base_list[0] == OP_CLASS || (!utf && base_list[0] == OP_NCLASS))
+#else
+ if (base_list[0] == OP_CLASS)
+#endif
+ {
+ set1 = (uint8_t *)(base_end - base_list[2]);
+ list_ptr = list;
+ }
+ else
+ {
+ set1 = (uint8_t *)(code - list[2]);
+ list_ptr = base_list;
+ }
+
+ invert_bits = FALSE;
+ switch(list_ptr[0])
+ {
+ case OP_CLASS:
+ case OP_NCLASS:
+ set2 = (uint8_t *)
+ ((list_ptr == list ? code : base_end) - list_ptr[2]);
+ break;
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ xclass_flags = (list_ptr == list ? code : base_end) - list_ptr[2] + LINK_SIZE;
+ if ((*xclass_flags & XCL_HASPROP) != 0) return FALSE;
+ if ((*xclass_flags & XCL_MAP) == 0)
+ {
+ /* No bits are set for characters < 256. */
+ if (list[1] == 0) return (*xclass_flags & XCL_NOT) == 0;
+ /* Might be an empty repeat. */
+ continue;
+ }
+ set2 = (uint8_t *)(xclass_flags + 1);
+ break;
+#endif
+
+ case OP_NOT_DIGIT:
+ invert_bits = TRUE;
+ /* Fall through */
+ case OP_DIGIT:
+ set2 = (uint8_t *)(cb->cbits + cbit_digit);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ invert_bits = TRUE;
+ /* Fall through */
+ case OP_WHITESPACE:
+ set2 = (uint8_t *)(cb->cbits + cbit_space);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ invert_bits = TRUE;
+ /* Fall through */
+ case OP_WORDCHAR:
+ set2 = (uint8_t *)(cb->cbits + cbit_word);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ /* Because the bit sets are unaligned bytes, we need to perform byte
+ comparison here. */
+
+ set_end = set1 + 32;
+ if (invert_bits)
+ {
+ do
+ {
+ if ((*set1++ & ~(*set2++)) != 0) return FALSE;
+ }
+ while (set1 < set_end);
+ }
+ else
+ {
+ do
+ {
+ if ((*set1++ & *set2++) != 0) return FALSE;
+ }
+ while (set1 < set_end);
+ }
+
+ if (list[1] == 0) return TRUE;
+ /* Might be an empty repeat. */
+ continue;
+ }
+
+ /* Some property combinations also acceptable. Unicode property opcodes are
+ processed specially; the rest can be handled with a lookup table. */
+
+ else
+ {
+ uint32_t leftop, rightop;
+
+ leftop = base_list[0];
+ rightop = list[0];
+
+#ifdef SUPPORT_UNICODE
+ accepted = FALSE; /* Always set in non-unicode case. */
+ if (leftop == OP_PROP || leftop == OP_NOTPROP)
+ {
+ if (rightop == OP_EOD)
+ accepted = TRUE;
+ else if (rightop == OP_PROP || rightop == OP_NOTPROP)
+ {
+ int n;
+ const uint8_t *p;
+ BOOL same = leftop == rightop;
+ BOOL lisprop = leftop == OP_PROP;
+ BOOL risprop = rightop == OP_PROP;
+ BOOL bothprop = lisprop && risprop;
+
+ /* There's a table that specifies how each combination is to be
+ processed:
+ 0 Always return FALSE (never auto-possessify)
+ 1 Character groups are distinct (possessify if both are OP_PROP)
+ 2 Check character categories in the same group (general or particular)
+ 3 Return TRUE if the two opcodes are not the same
+ ... see comments below
+ */
+
+ n = propposstab[base_list[2]][list[2]];
+ switch(n)
+ {
+ case 0: break;
+ case 1: accepted = bothprop; break;
+ case 2: accepted = (base_list[3] == list[3]) != same; break;
+ case 3: accepted = !same; break;
+
+ case 4: /* Left general category, right particular category */
+ accepted = risprop && catposstab[base_list[3]][list[3]] == same;
+ break;
+
+ case 5: /* Right general category, left particular category */
+ accepted = lisprop && catposstab[list[3]][base_list[3]] == same;
+ break;
+
+ /* This code is logically tricky. Think hard before fiddling with it.
+ The posspropstab table has four entries per row. Each row relates to
+ one of PCRE's special properties such as ALNUM or SPACE or WORD.
+ Only WORD actually needs all four entries, but using repeats for the
+ others means they can all use the same code below.
+
+ The first two entries in each row are Unicode general categories, and
+ apply always, because all the characters they include are part of the
+ PCRE character set. The third and fourth entries are a general and a
+ particular category, respectively, that include one or more relevant
+ characters. One or the other is used, depending on whether the check
+ is for a general or a particular category. However, in both cases the
+ category contains more characters than the specials that are defined
+ for the property being tested against. Therefore, it cannot be used
+ in a NOTPROP case.
+
+ Example: the row for WORD contains ucp_L, ucp_N, ucp_P, ucp_Po.
+ Underscore is covered by ucp_P or ucp_Po. */
+
+ case 6: /* Left alphanum vs right general category */
+ case 7: /* Left space vs right general category */
+ case 8: /* Left word vs right general category */
+ p = posspropstab[n-6];
+ accepted = risprop && lisprop ==
+ (list[3] != p[0] &&
+ list[3] != p[1] &&
+ (list[3] != p[2] || !lisprop));
+ break;
+
+ case 9: /* Right alphanum vs left general category */
+ case 10: /* Right space vs left general category */
+ case 11: /* Right word vs left general category */
+ p = posspropstab[n-9];
+ accepted = lisprop && risprop ==
+ (base_list[3] != p[0] &&
+ base_list[3] != p[1] &&
+ (base_list[3] != p[2] || !risprop));
+ break;
+
+ case 12: /* Left alphanum vs right particular category */
+ case 13: /* Left space vs right particular category */
+ case 14: /* Left word vs right particular category */
+ p = posspropstab[n-12];
+ accepted = risprop && lisprop ==
+ (catposstab[p[0]][list[3]] &&
+ catposstab[p[1]][list[3]] &&
+ (list[3] != p[3] || !lisprop));
+ break;
+
+ case 15: /* Right alphanum vs left particular category */
+ case 16: /* Right space vs left particular category */
+ case 17: /* Right word vs left particular category */
+ p = posspropstab[n-15];
+ accepted = lisprop && risprop ==
+ (catposstab[p[0]][base_list[3]] &&
+ catposstab[p[1]][base_list[3]] &&
+ (base_list[3] != p[3] || !risprop));
+ break;
+ }
+ }
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ accepted = leftop >= FIRST_AUTOTAB_OP && leftop <= LAST_AUTOTAB_LEFT_OP &&
+ rightop >= FIRST_AUTOTAB_OP && rightop <= LAST_AUTOTAB_RIGHT_OP &&
+ autoposstab[leftop - FIRST_AUTOTAB_OP][rightop - FIRST_AUTOTAB_OP];
+
+ if (!accepted) return FALSE;
+
+ if (list[1] == 0) return TRUE;
+ /* Might be an empty repeat. */
+ continue;
+ }
+
+ /* Control reaches here only if one of the items is a small character list.
+ All characters are checked against the other side. */
+
+ do
+ {
+ chr = *chr_ptr;
+
+ switch(list_ptr[0])
+ {
+ case OP_CHAR:
+ ochr_ptr = list_ptr + 2;
+ do
+ {
+ if (chr == *ochr_ptr) return FALSE;
+ ochr_ptr++;
+ }
+ while(*ochr_ptr != NOTACHAR);
+ break;
+
+ case OP_NOT:
+ ochr_ptr = list_ptr + 2;
+ do
+ {
+ if (chr == *ochr_ptr)
+ break;
+ ochr_ptr++;
+ }
+ while(*ochr_ptr != NOTACHAR);
+ if (*ochr_ptr == NOTACHAR) return FALSE; /* Not found */
+ break;
+
+ /* Note that OP_DIGIT etc. are generated only when PCRE2_UCP is *not*
+ set. When it is set, \d etc. are converted into OP_(NOT_)PROP codes. */
+
+ case OP_DIGIT:
+ if (chr < 256 && (cb->ctypes[chr] & ctype_digit) != 0) return FALSE;
+ break;
+
+ case OP_NOT_DIGIT:
+ if (chr > 255 || (cb->ctypes[chr] & ctype_digit) == 0) return FALSE;
+ break;
+
+ case OP_WHITESPACE:
+ if (chr < 256 && (cb->ctypes[chr] & ctype_space) != 0) return FALSE;
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (chr > 255 || (cb->ctypes[chr] & ctype_space) == 0) return FALSE;
+ break;
+
+ case OP_WORDCHAR:
+ if (chr < 255 && (cb->ctypes[chr] & ctype_word) != 0) return FALSE;
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (chr > 255 || (cb->ctypes[chr] & ctype_word) == 0) return FALSE;
+ break;
+
+ case OP_HSPACE:
+ switch(chr)
+ {
+ HSPACE_CASES: return FALSE;
+ default: break;
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ switch(chr)
+ {
+ HSPACE_CASES: break;
+ default: return FALSE;
+ }
+ break;
+
+ case OP_ANYNL:
+ case OP_VSPACE:
+ switch(chr)
+ {
+ VSPACE_CASES: return FALSE;
+ default: break;
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ switch(chr)
+ {
+ VSPACE_CASES: break;
+ default: return FALSE;
+ }
+ break;
+
+ case OP_DOLL:
+ case OP_EODN:
+ switch (chr)
+ {
+ case CHAR_CR:
+ case CHAR_LF:
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ return FALSE;
+ }
+ break;
+
+ case OP_EOD: /* Can always possessify before \z */
+ break;
+
+#ifdef SUPPORT_UNICODE
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (!check_char_prop(chr, list_ptr[2], list_ptr[3],
+ list_ptr[0] == OP_NOTPROP))
+ return FALSE;
+ break;
+#endif
+
+ case OP_NCLASS:
+ if (chr > 255) return FALSE;
+ /* Fall through */
+
+ case OP_CLASS:
+ if (chr > 255) break;
+ class_bitset = (uint8_t *)
+ ((list_ptr == list ? code : base_end) - list_ptr[2]);
+ if ((class_bitset[chr >> 3] & (1u << (chr & 7))) != 0) return FALSE;
+ break;
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ if (PRIV(xclass)(chr, (list_ptr == list ? code : base_end) -
+ list_ptr[2] + LINK_SIZE, utf)) return FALSE;
+ break;
+#endif
+
+ default:
+ return FALSE;
+ }
+
+ chr_ptr++;
+ }
+ while(*chr_ptr != NOTACHAR);
+
+ /* At least one character must be matched from this opcode. */
+
+ if (list[1] == 0) return TRUE;
+ }
+
+/* Control never reaches here. There used to be a fail-save return FALSE; here,
+but some compilers complain about an unreachable statement. */
+}
+
+
+
+/*************************************************
+* Scan compiled regex for auto-possession *
+*************************************************/
+
+/* Replaces single character iterations with their possessive alternatives
+if appropriate. This function modifies the compiled opcode! Hitting a
+non-existent opcode may indicate a bug in PCRE2, but it can also be caused if a
+bad UTF string was compiled with PCRE2_NO_UTF_CHECK. The rec_limit catches
+overly complicated or large patterns. In these cases, the check just stops,
+leaving the remainder of the pattern unpossessified.
+
+Arguments:
+ code points to start of the byte code
+ cb compile data block
+
+Returns: 0 for success
+ -1 if a non-existant opcode is encountered
+*/
+
+int
+PRIV(auto_possessify)(PCRE2_UCHAR *code, const compile_block *cb)
+{
+PCRE2_UCHAR c;
+PCRE2_SPTR end;
+PCRE2_UCHAR *repeat_opcode;
+uint32_t list[8];
+int rec_limit = 1000; /* Was 10,000 but clang+ASAN uses a lot of stack. */
+BOOL utf = (cb->external_options & PCRE2_UTF) != 0;
+BOOL ucp = (cb->external_options & PCRE2_UCP) != 0;
+
+for (;;)
+ {
+ c = *code;
+
+ if (c >= OP_TABLE_LENGTH) return -1; /* Something gone wrong */
+
+ if (c >= OP_STAR && c <= OP_TYPEPOSUPTO)
+ {
+ c -= get_repeat_base(c) - OP_STAR;
+ end = (c <= OP_MINUPTO) ?
+ get_chr_property_list(code, utf, ucp, cb->fcc, list) : NULL;
+ list[1] = c == OP_STAR || c == OP_PLUS || c == OP_QUERY || c == OP_UPTO;
+
+ if (end != NULL && compare_opcodes(end, utf, ucp, cb, list, end,
+ &rec_limit))
+ {
+ switch(c)
+ {
+ case OP_STAR:
+ *code += OP_POSSTAR - OP_STAR;
+ break;
+
+ case OP_MINSTAR:
+ *code += OP_POSSTAR - OP_MINSTAR;
+ break;
+
+ case OP_PLUS:
+ *code += OP_POSPLUS - OP_PLUS;
+ break;
+
+ case OP_MINPLUS:
+ *code += OP_POSPLUS - OP_MINPLUS;
+ break;
+
+ case OP_QUERY:
+ *code += OP_POSQUERY - OP_QUERY;
+ break;
+
+ case OP_MINQUERY:
+ *code += OP_POSQUERY - OP_MINQUERY;
+ break;
+
+ case OP_UPTO:
+ *code += OP_POSUPTO - OP_UPTO;
+ break;
+
+ case OP_MINUPTO:
+ *code += OP_POSUPTO - OP_MINUPTO;
+ break;
+ }
+ }
+ c = *code;
+ }
+ else if (c == OP_CLASS || c == OP_NCLASS || c == OP_XCLASS)
+ {
+#ifdef SUPPORT_WIDE_CHARS
+ if (c == OP_XCLASS)
+ repeat_opcode = code + GET(code, 1);
+ else
+#endif
+ repeat_opcode = code + 1 + (32 / sizeof(PCRE2_UCHAR));
+
+ c = *repeat_opcode;
+ if (c >= OP_CRSTAR && c <= OP_CRMINRANGE)
+ {
+ /* The return from get_chr_property_list() will never be NULL when
+ *code (aka c) is one of the three class opcodes. However, gcc with
+ -fanalyzer notes that a NULL return is possible, and grumbles. Hence we
+ put in a check. */
+
+ end = get_chr_property_list(code, utf, ucp, cb->fcc, list);
+ list[1] = (c & 1) == 0;
+
+ if (end != NULL &&
+ compare_opcodes(end, utf, ucp, cb, list, end, &rec_limit))
+ {
+ switch (c)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ *repeat_opcode = OP_CRPOSSTAR;
+ break;
+
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ *repeat_opcode = OP_CRPOSPLUS;
+ break;
+
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ *repeat_opcode = OP_CRPOSQUERY;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ *repeat_opcode = OP_CRPOSRANGE;
+ break;
+ }
+ }
+ }
+ c = *code;
+ }
+
+ switch(c)
+ {
+ case OP_END:
+ return 0;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
+ break;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSUPTO:
+ if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)
+ code += 2;
+ break;
+
+ case OP_CALLOUT_STR:
+ code += GET(code, 1 + 2*LINK_SIZE);
+ break;
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ code += GET(code, 1);
+ break;
+#endif
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ code += code[1];
+ break;
+ }
+
+ /* Add in the fixed length from the table */
+
+ code += PRIV(OP_lengths)[c];
+
+ /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be
+ followed by a multi-byte character. The length in the table is a minimum, so
+ we have to arrange to skip the extra code units. */
+
+#ifdef MAYBE_UTF_MULTI
+ if (utf) switch(c)
+ {
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);
+ break;
+ }
+#else
+ (void)(utf); /* Keep compiler happy by referencing function argument */
+#endif /* SUPPORT_WIDE_CHARS */
+ }
+}
+
+/* End of pcre2_auto_possess.c */
diff --git a/contrib/libs/pcre2/src/pcre2_chartables.c b/contrib/libs/pcre2/src/pcre2_chartables.c
new file mode 100644
index 0000000000..4506b0d5ee
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_chartables.c
@@ -0,0 +1,202 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* This file was automatically written by the pcre2_dftables auxiliary
+program. It contains character tables that are used when no external
+tables are passed to PCRE2 by the application that calls it. The tables
+are used only for characters whose code values are less than 256. */
+
+/* This set of tables was written in the C locale. */
+
+/* The pcre2_ftables program (which is distributed with PCRE2) can be used
+to build alternative versions of this file. This is necessary if you are
+running in an EBCDIC environment, or if you want to default to a different
+encoding, for example ISO-8859-1. When pcre2_dftables is run, it creates
+these tables in the "C" locale by default. This happens automatically if
+PCRE2 is configured with --enable-rebuild-chartables. However, you can run
+pcre2_dftables manually with the -L option to build tables using the LC_ALL
+locale. */
+
+/* The following #include is present because without it gcc 4.x may remove
+the array definition from the final binary if PCRE2 is built into a static
+library and dead code stripping is activated. This leads to link errors.
+Pulling in the header ensures that the array gets flagged as "someone
+outside this compilation unit might reference this" and so it will always
+be supplied to the linker. */
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+const uint8_t PRIV(default_tables)[] = {
+
+/* This table is a lower casing table. */
+
+ 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, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,
+ 120,121,122, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,
+ 120,121,122,123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,
+ 136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,
+ 152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,
+ 168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,
+ 184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,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,
+
+/* This table is a case flipping table. */
+
+ 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, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,
+ 120,121,122, 91, 92, 93, 94, 95,
+ 96, 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,123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,
+ 136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,
+ 152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,
+ 168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,
+ 184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,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,
+
+/* This table contains bit maps for various character classes. Each map is 32
+bytes long and the bits run from the least significant end of each byte. The
+classes that have their own maps are: space, xdigit, digit, upper, lower, word,
+graph, print, punct, and cntrl. Other classes are built from combinations. */
+
+ 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, /* space */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* xdigit */
+ 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* digit */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* upper */
+ 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* lower */
+ 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, /* word */
+ 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, /* graph */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, /* print */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, /* punct */
+ 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, /* cntrl */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+
+/* This table identifies various classes of character by individual bits:
+ 0x01 white space character
+ 0x02 letter
+ 0x04 lower case letter
+ 0x08 decimal digit
+ 0x10 alphanumeric or '_'
+*/
+
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */
+ 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */
+ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */
+ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, /* 0 - 7 */
+ 0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */
+ 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* @ - G */
+ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */
+ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */
+ 0x12,0x12,0x12,0x00,0x00,0x00,0x00,0x10, /* X - _ */
+ 0x00,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* ` - g */
+ 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* h - o */
+ 0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, /* p - w */
+ 0x16,0x16,0x16,0x00,0x00,0x00,0x00,0x00, /* x -127 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
+
+/* End of pcre2_chartables.c */
diff --git a/contrib/libs/pcre2/src/pcre2_compile.c b/contrib/libs/pcre2/src/pcre2_compile.c
new file mode 100644
index 0000000000..4caeecf6a4
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_compile.c
@@ -0,0 +1,10631 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#define NLBLOCK cb /* Block containing newline information */
+#define PSSTART start_pattern /* Field containing processed string start */
+#define PSEND end_pattern /* Field containing processed string end */
+
+#include "pcre2_internal.h"
+
+/* In rare error cases debugging might require calling pcre2_printint(). */
+
+#if 0
+#ifdef EBCDIC
+#define PRINTABLE(c) ((c) >= 64 && (c) < 255)
+#else
+#define PRINTABLE(c) ((c) >= 32 && (c) < 127)
+#endif
+#error #include "pcre2_printint.c"
+#define DEBUG_CALL_PRINTINT
+#endif
+
+/* Other debugging code can be enabled by these defines. */
+
+/* #define DEBUG_SHOW_CAPTURES */
+/* #define DEBUG_SHOW_PARSED */
+
+/* There are a few things that vary with different code unit sizes. Handle them
+by defining macros in order to minimize #if usage. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define STRING_UTFn_RIGHTPAR STRING_UTF8_RIGHTPAR, 5
+#define XDIGIT(c) xdigitab[c]
+
+#else /* Either 16-bit or 32-bit */
+#define XDIGIT(c) (MAX_255(c)? xdigitab[c] : 0xff)
+
+#if PCRE2_CODE_UNIT_WIDTH == 16
+#define STRING_UTFn_RIGHTPAR STRING_UTF16_RIGHTPAR, 6
+
+#else /* 32-bit */
+#define STRING_UTFn_RIGHTPAR STRING_UTF32_RIGHTPAR, 6
+#endif
+#endif
+
+/* Macros to store and retrieve a PCRE2_SIZE value in the parsed pattern, which
+consists of uint32_t elements. Assume that if uint32_t can't hold it, two of
+them will be able to (i.e. assume a 64-bit world). */
+
+#if PCRE2_SIZE_MAX <= UINT32_MAX
+#define PUTOFFSET(s,p) *p++ = s
+#define GETOFFSET(s,p) s = *p++
+#define GETPLUSOFFSET(s,p) s = *(++p)
+#define READPLUSOFFSET(s,p) s = p[1]
+#define SKIPOFFSET(p) p++
+#define SIZEOFFSET 1
+#else
+#define PUTOFFSET(s,p) \
+ { *p++ = (uint32_t)(s >> 32); *p++ = (uint32_t)(s & 0xffffffff); }
+#define GETOFFSET(s,p) \
+ { s = ((PCRE2_SIZE)p[0] << 32) | (PCRE2_SIZE)p[1]; p += 2; }
+#define GETPLUSOFFSET(s,p) \
+ { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; p += 2; }
+#define READPLUSOFFSET(s,p) \
+ { s = ((PCRE2_SIZE)p[1] << 32) | (PCRE2_SIZE)p[2]; }
+#define SKIPOFFSET(p) p += 2
+#define SIZEOFFSET 2
+#endif
+
+/* Macros for manipulating elements of the parsed pattern vector. */
+
+#define META_CODE(x) (x & 0xffff0000u)
+#define META_DATA(x) (x & 0x0000ffffu)
+#define META_DIFF(x,y) ((x-y)>>16)
+
+/* Function definitions to allow mutual recursion */
+
+#ifdef SUPPORT_UNICODE
+static unsigned int
+ add_list_to_class_internal(uint8_t *, PCRE2_UCHAR **, uint32_t,
+ compile_block *, const uint32_t *, unsigned int);
+#endif
+
+static int
+ compile_regex(uint32_t, PCRE2_UCHAR **, uint32_t **, int *, uint32_t,
+ uint32_t *, uint32_t *, uint32_t *, uint32_t *, branch_chain *,
+ compile_block *, PCRE2_SIZE *);
+
+static int
+ get_branchlength(uint32_t **, int *, int *, parsed_recurse_check *,
+ compile_block *);
+
+static BOOL
+ set_lookbehind_lengths(uint32_t **, int *, int *, parsed_recurse_check *,
+ compile_block *);
+
+static int
+ check_lookbehinds(uint32_t *, uint32_t **, parsed_recurse_check *,
+ compile_block *, int *);
+
+
+/*************************************************
+* Code parameters and static tables *
+*************************************************/
+
+#define MAX_GROUP_NUMBER 65535u
+#define MAX_REPEAT_COUNT 65535u
+#define REPEAT_UNLIMITED (MAX_REPEAT_COUNT+1)
+
+/* COMPILE_WORK_SIZE specifies the size of stack workspace, which is used in
+different ways in the different pattern scans. The parsing and group-
+identifying pre-scan uses it to handle nesting, and needs it to be 16-bit
+aligned for this. Having defined the size in code units, we set up
+C16_WORK_SIZE as the number of elements in the 16-bit vector.
+
+During the first compiling phase, when determining how much memory is required,
+the regex is partly compiled into this space, but the compiled parts are
+discarded as soon as they can be, so that hopefully there will never be an
+overrun. The code does, however, check for an overrun, which can occur for
+pathological patterns. The size of the workspace depends on LINK_SIZE because
+the length of compiled items varies with this.
+
+In the real compile phase, this workspace is not currently used. */
+
+#define COMPILE_WORK_SIZE (3000*LINK_SIZE) /* Size in code units */
+
+#define C16_WORK_SIZE \
+ ((COMPILE_WORK_SIZE * sizeof(PCRE2_UCHAR))/sizeof(uint16_t))
+
+/* A uint32_t vector is used for caching information about the size of
+capturing groups, to improve performance. A default is created on the stack of
+this size. */
+
+#define GROUPINFO_DEFAULT_SIZE 256
+
+/* The overrun tests check for a slightly smaller size so that they detect the
+overrun before it actually does run off the end of the data block. */
+
+#define WORK_SIZE_SAFETY_MARGIN (100)
+
+/* This value determines the size of the initial vector that is used for
+remembering named groups during the pre-compile. It is allocated on the stack,
+but if it is too small, it is expanded, in a similar way to the workspace. The
+value is the number of slots in the list. */
+
+#define NAMED_GROUP_LIST_SIZE 20
+
+/* The pre-compiling pass over the pattern creates a parsed pattern in a vector
+of uint32_t. For short patterns this lives on the stack, with this size. Heap
+memory is used for longer patterns. */
+
+#define PARSED_PATTERN_DEFAULT_SIZE 1024
+
+/* Maximum length value to check against when making sure that the variable
+that holds the compiled pattern length does not overflow. We make it a bit less
+than INT_MAX to allow for adding in group terminating code units, so that we
+don't have to check them every time. */
+
+#define OFLOW_MAX (INT_MAX - 20)
+
+/* Code values for parsed patterns, which are stored in a vector of 32-bit
+unsigned ints. Values less than META_END are literal data values. The coding
+for identifying the item is in the top 16-bits, leaving 16 bits for the
+additional data that some of them need. The META_CODE, META_DATA, and META_DIFF
+macros are used to manipulate parsed pattern elements.
+
+NOTE: When these definitions are changed, the table of extra lengths for each
+code (meta_extra_lengths, just below) must be updated to remain in step. */
+
+#define META_END 0x80000000u /* End of pattern */
+
+#define META_ALT 0x80010000u /* alternation */
+#define META_ATOMIC 0x80020000u /* atomic group */
+#define META_BACKREF 0x80030000u /* Back ref */
+#define META_BACKREF_BYNAME 0x80040000u /* \k'name' */
+#define META_BIGVALUE 0x80050000u /* Next is a literal > META_END */
+#define META_CALLOUT_NUMBER 0x80060000u /* (?C with numerical argument */
+#define META_CALLOUT_STRING 0x80070000u /* (?C with string argument */
+#define META_CAPTURE 0x80080000u /* Capturing parenthesis */
+#define META_CIRCUMFLEX 0x80090000u /* ^ metacharacter */
+#define META_CLASS 0x800a0000u /* start non-empty class */
+#define META_CLASS_EMPTY 0x800b0000u /* empty class */
+#define META_CLASS_EMPTY_NOT 0x800c0000u /* negative empty class */
+#define META_CLASS_END 0x800d0000u /* end of non-empty class */
+#define META_CLASS_NOT 0x800e0000u /* start non-empty negative class */
+#define META_COND_ASSERT 0x800f0000u /* (?(?assertion)... */
+#define META_COND_DEFINE 0x80100000u /* (?(DEFINE)... */
+#define META_COND_NAME 0x80110000u /* (?(<name>)... */
+#define META_COND_NUMBER 0x80120000u /* (?(digits)... */
+#define META_COND_RNAME 0x80130000u /* (?(R&name)... */
+#define META_COND_RNUMBER 0x80140000u /* (?(Rdigits)... */
+#define META_COND_VERSION 0x80150000u /* (?(VERSION<op>x.y)... */
+#define META_DOLLAR 0x80160000u /* $ metacharacter */
+#define META_DOT 0x80170000u /* . metacharacter */
+#define META_ESCAPE 0x80180000u /* \d and friends */
+#define META_KET 0x80190000u /* closing parenthesis */
+#define META_NOCAPTURE 0x801a0000u /* no capture parens */
+#define META_OPTIONS 0x801b0000u /* (?i) and friends */
+#define META_POSIX 0x801c0000u /* POSIX class item */
+#define META_POSIX_NEG 0x801d0000u /* negative POSIX class item */
+#define META_RANGE_ESCAPED 0x801e0000u /* range with at least one escape */
+#define META_RANGE_LITERAL 0x801f0000u /* range defined literally */
+#define META_RECURSE 0x80200000u /* Recursion */
+#define META_RECURSE_BYNAME 0x80210000u /* (?&name) */
+#define META_SCRIPT_RUN 0x80220000u /* (*script_run:...) */
+
+/* These must be kept together to make it easy to check that an assertion
+is present where expected in a conditional group. */
+
+#define META_LOOKAHEAD 0x80230000u /* (?= */
+#define META_LOOKAHEADNOT 0x80240000u /* (?! */
+#define META_LOOKBEHIND 0x80250000u /* (?<= */
+#define META_LOOKBEHINDNOT 0x80260000u /* (?<! */
+
+/* These cannot be conditions */
+
+#define META_LOOKAHEAD_NA 0x80270000u /* (*napla: */
+#define META_LOOKBEHIND_NA 0x80280000u /* (*naplb: */
+
+/* These must be kept in this order, with consecutive values, and the _ARG
+versions of COMMIT, PRUNE, SKIP, and THEN immediately after their non-argument
+versions. */
+
+#define META_MARK 0x80290000u /* (*MARK) */
+#define META_ACCEPT 0x802a0000u /* (*ACCEPT) */
+#define META_FAIL 0x802b0000u /* (*FAIL) */
+#define META_COMMIT 0x802c0000u /* These */
+#define META_COMMIT_ARG 0x802d0000u /* pairs */
+#define META_PRUNE 0x802e0000u /* must */
+#define META_PRUNE_ARG 0x802f0000u /* be */
+#define META_SKIP 0x80300000u /* kept */
+#define META_SKIP_ARG 0x80310000u /* in */
+#define META_THEN 0x80320000u /* this */
+#define META_THEN_ARG 0x80330000u /* order */
+
+/* These must be kept in groups of adjacent 3 values, and all together. */
+
+#define META_ASTERISK 0x80340000u /* * */
+#define META_ASTERISK_PLUS 0x80350000u /* *+ */
+#define META_ASTERISK_QUERY 0x80360000u /* *? */
+#define META_PLUS 0x80370000u /* + */
+#define META_PLUS_PLUS 0x80380000u /* ++ */
+#define META_PLUS_QUERY 0x80390000u /* +? */
+#define META_QUERY 0x803a0000u /* ? */
+#define META_QUERY_PLUS 0x803b0000u /* ?+ */
+#define META_QUERY_QUERY 0x803c0000u /* ?? */
+#define META_MINMAX 0x803d0000u /* {n,m} repeat */
+#define META_MINMAX_PLUS 0x803e0000u /* {n,m}+ repeat */
+#define META_MINMAX_QUERY 0x803f0000u /* {n,m}? repeat */
+
+#define META_FIRST_QUANTIFIER META_ASTERISK
+#define META_LAST_QUANTIFIER META_MINMAX_QUERY
+
+/* This is a special "meta code" that is used only to distinguish (*asr: from
+(*sr: in the table of aphabetic assertions. It is never stored in the parsed
+pattern because (*asr: is turned into (*sr:(*atomic: at that stage. There is
+therefore no need for it to have a length entry, so use a high value. */
+
+#define META_ATOMIC_SCRIPT_RUN 0x8fff0000u
+
+/* Table of extra lengths for each of the meta codes. Must be kept in step with
+the definitions above. For some items these values are a basic length to which
+a variable amount has to be added. */
+
+static unsigned char meta_extra_lengths[] = {
+ 0, /* META_END */
+ 0, /* META_ALT */
+ 0, /* META_ATOMIC */
+ 0, /* META_BACKREF - more if group is >= 10 */
+ 1+SIZEOFFSET, /* META_BACKREF_BYNAME */
+ 1, /* META_BIGVALUE */
+ 3, /* META_CALLOUT_NUMBER */
+ 3+SIZEOFFSET, /* META_CALLOUT_STRING */
+ 0, /* META_CAPTURE */
+ 0, /* META_CIRCUMFLEX */
+ 0, /* META_CLASS */
+ 0, /* META_CLASS_EMPTY */
+ 0, /* META_CLASS_EMPTY_NOT */
+ 0, /* META_CLASS_END */
+ 0, /* META_CLASS_NOT */
+ 0, /* META_COND_ASSERT */
+ SIZEOFFSET, /* META_COND_DEFINE */
+ 1+SIZEOFFSET, /* META_COND_NAME */
+ 1+SIZEOFFSET, /* META_COND_NUMBER */
+ 1+SIZEOFFSET, /* META_COND_RNAME */
+ 1+SIZEOFFSET, /* META_COND_RNUMBER */
+ 3, /* META_COND_VERSION */
+ 0, /* META_DOLLAR */
+ 0, /* META_DOT */
+ 0, /* META_ESCAPE - more for ESC_P, ESC_p, ESC_g, ESC_k */
+ 0, /* META_KET */
+ 0, /* META_NOCAPTURE */
+ 1, /* META_OPTIONS */
+ 1, /* META_POSIX */
+ 1, /* META_POSIX_NEG */
+ 0, /* META_RANGE_ESCAPED */
+ 0, /* META_RANGE_LITERAL */
+ SIZEOFFSET, /* META_RECURSE */
+ 1+SIZEOFFSET, /* META_RECURSE_BYNAME */
+ 0, /* META_SCRIPT_RUN */
+ 0, /* META_LOOKAHEAD */
+ 0, /* META_LOOKAHEADNOT */
+ SIZEOFFSET, /* META_LOOKBEHIND */
+ SIZEOFFSET, /* META_LOOKBEHINDNOT */
+ 0, /* META_LOOKAHEAD_NA */
+ SIZEOFFSET, /* META_LOOKBEHIND_NA */
+ 1, /* META_MARK - plus the string length */
+ 0, /* META_ACCEPT */
+ 0, /* META_FAIL */
+ 0, /* META_COMMIT */
+ 1, /* META_COMMIT_ARG - plus the string length */
+ 0, /* META_PRUNE */
+ 1, /* META_PRUNE_ARG - plus the string length */
+ 0, /* META_SKIP */
+ 1, /* META_SKIP_ARG - plus the string length */
+ 0, /* META_THEN */
+ 1, /* META_THEN_ARG - plus the string length */
+ 0, /* META_ASTERISK */
+ 0, /* META_ASTERISK_PLUS */
+ 0, /* META_ASTERISK_QUERY */
+ 0, /* META_PLUS */
+ 0, /* META_PLUS_PLUS */
+ 0, /* META_PLUS_QUERY */
+ 0, /* META_QUERY */
+ 0, /* META_QUERY_PLUS */
+ 0, /* META_QUERY_QUERY */
+ 2, /* META_MINMAX */
+ 2, /* META_MINMAX_PLUS */
+ 2 /* META_MINMAX_QUERY */
+};
+
+/* Types for skipping parts of a parsed pattern. */
+
+enum { PSKIP_ALT, PSKIP_CLASS, PSKIP_KET };
+
+/* Macro for setting individual bits in class bitmaps. It took some
+experimenting to figure out how to stop gcc 5.3.0 from warning with
+-Wconversion. This version gets a warning:
+
+ #define SETBIT(a,b) a[(b)/8] |= (uint8_t)(1u << ((b)&7))
+
+Let's hope the apparently less efficient version isn't actually so bad if the
+compiler is clever with identical subexpressions. */
+
+#define SETBIT(a,b) a[(b)/8] = (uint8_t)(a[(b)/8] | (1u << ((b)&7)))
+
+/* Values and flags for the unsigned xxcuflags variables that accompany xxcu
+variables, which are concerned with first and required code units. A value
+greater than or equal to REQ_NONE means "no code unit set"; otherwise the
+matching xxcu variable is set, and the low valued bits are relevant. */
+
+#define REQ_UNSET 0xffffffffu /* Not yet found anything */
+#define REQ_NONE 0xfffffffeu /* Found not fixed character */
+#define REQ_CASELESS 0x00000001u /* Code unit in xxcu is caseless */
+#define REQ_VARY 0x00000002u /* Code unit is followed by non-literal */
+
+/* These flags are used in the groupinfo vector. */
+
+#define GI_SET_FIXED_LENGTH 0x80000000u
+#define GI_NOT_FIXED_LENGTH 0x40000000u
+#define GI_FIXED_LENGTH_MASK 0x0000ffffu
+
+/* This simple test for a decimal digit works for both ASCII/Unicode and EBCDIC
+and is fast (a good compiler can turn it into a subtraction and unsigned
+comparison). */
+
+#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9)
+
+/* Table to identify hex digits. The tables in chartables are dependent on the
+locale, and may mark arbitrary characters as digits. We want to recognize only
+0-9, a-z, and A-Z as hex digits, which is why we have a private table here. It
+costs 256 bytes, but it is a lot faster than doing character value tests (at
+least in some simple cases I timed), and in some applications one wants PCRE2
+to compile efficiently as well as match efficiently. The value in the table is
+the binary hex digit value, or 0xff for non-hex digits. */
+
+/* This is the "normal" case, for ASCII systems, and EBCDIC systems running in
+UTF-8 mode. */
+
+#ifndef EBCDIC
+static const uint8_t xdigitab[] =
+ {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - ' */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ( - / */
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 */
+ 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff, /* 8 - ? */
+ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* @ - G */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H - O */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* P - W */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* X - _ */
+ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* ` - g */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h - o */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* p - w */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* x -127 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 128-135 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 136-143 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144-151 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 152-159 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160-167 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 168-175 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 176-183 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 192-199 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 2ff-207 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 208-215 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 216-223 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 224-231 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 232-239 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 240-247 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};/* 248-255 */
+
+#else
+
+/* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */
+
+static const uint8_t xdigitab[] =
+ {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 0- 7 0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 8- 15 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 16- 23 10 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 24- 31 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 32- 39 20 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 40- 47 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 48- 55 30 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 56- 63 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - 71 40 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 72- | */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* & - 87 50 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 88- 95 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* - -103 60 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 104- ? */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 112-119 70 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 120- " */
+ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* 128- g 80 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* h -143 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 144- p 90 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* q -159 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 160- x A0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* y -175 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* ^ -183 B0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 184-191 */
+ 0xff,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xff, /* { - G C0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* H -207 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* } - P D0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Q -223 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* \ - X E0 */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* Y -239 */
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0 - 7 F0 */
+ 0x08,0x09,0xff,0xff,0xff,0xff,0xff,0xff};/* 8 -255 */
+#endif /* EBCDIC */
+
+
+/* Table for handling alphanumeric escaped characters. Positive returns are
+simple data values; negative values are for special things like \d and so on.
+Zero means further processing is needed (for things like \x), or the escape is
+invalid. */
+
+/* This is the "normal" table for ASCII systems or for EBCDIC systems running
+in UTF-8 mode. It runs from '0' to 'z'. */
+
+#ifndef EBCDIC
+#define ESCAPES_FIRST CHAR_0
+#define ESCAPES_LAST CHAR_z
+#define UPPER_CASE(c) (c-32)
+
+static const short int escapes[] = {
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ CHAR_COLON, CHAR_SEMICOLON,
+ CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN,
+ CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK,
+ CHAR_COMMERCIAL_AT, -ESC_A,
+ -ESC_B, -ESC_C,
+ -ESC_D, -ESC_E,
+ 0, -ESC_G,
+ -ESC_H, 0,
+ 0, -ESC_K,
+ 0, 0,
+ -ESC_N, 0,
+ -ESC_P, -ESC_Q,
+ -ESC_R, -ESC_S,
+ 0, 0,
+ -ESC_V, -ESC_W,
+ -ESC_X, 0,
+ -ESC_Z, CHAR_LEFT_SQUARE_BRACKET,
+ CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET,
+ CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE,
+ CHAR_GRAVE_ACCENT, CHAR_BEL,
+ -ESC_b, 0,
+ -ESC_d, CHAR_ESC,
+ CHAR_FF, 0,
+ -ESC_h, 0,
+ 0, -ESC_k,
+ 0, 0,
+ CHAR_LF, 0,
+ -ESC_p, 0,
+ CHAR_CR, -ESC_s,
+ CHAR_HT, 0,
+ -ESC_v, -ESC_w,
+ 0, 0,
+ -ESC_z
+};
+
+#else
+
+/* This is the "abnormal" table for EBCDIC systems without UTF-8 support.
+It runs from 'a' to '9'. For some minimal testing of EBCDIC features, the code
+is sometimes compiled on an ASCII system. In this case, we must not use CHAR_a
+because it is defined as 'a', which of course picks up the ASCII value. */
+
+#if 'a' == 0x81 /* Check for a real EBCDIC environment */
+#define ESCAPES_FIRST CHAR_a
+#define ESCAPES_LAST CHAR_9
+#define UPPER_CASE(c) (c+64)
+#else /* Testing in an ASCII environment */
+#define ESCAPES_FIRST ((unsigned char)'\x81') /* EBCDIC 'a' */
+#define ESCAPES_LAST ((unsigned char)'\xf9') /* EBCDIC '9' */
+#define UPPER_CASE(c) (c-32)
+#endif
+
+static const short int escapes[] = {
+/* 80 */ CHAR_BEL, -ESC_b, 0, -ESC_d, CHAR_ESC, CHAR_FF, 0,
+/* 88 */ -ESC_h, 0, 0, '{', 0, 0, 0, 0,
+/* 90 */ 0, 0, -ESC_k, 0, 0, CHAR_LF, 0, -ESC_p,
+/* 98 */ 0, CHAR_CR, 0, '}', 0, 0, 0, 0,
+/* A0 */ 0, '~', -ESC_s, CHAR_HT, 0, -ESC_v, -ESC_w, 0,
+/* A8 */ 0, -ESC_z, 0, 0, 0, '[', 0, 0,
+/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-',
+/* C0 */ '{', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G,
+/* C8 */ -ESC_H, 0, 0, 0, 0, 0, 0, 0,
+/* D0 */ '}', 0, -ESC_K, 0, 0, -ESC_N, 0, -ESC_P,
+/* D8 */ -ESC_Q, -ESC_R, 0, 0, 0, 0, 0, 0,
+/* E0 */ '\\', 0, -ESC_S, 0, 0, -ESC_V, -ESC_W, -ESC_X,
+/* E8 */ 0, -ESC_Z, 0, 0, 0, 0, 0, 0,
+/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* F8 */ 0, 0
+};
+
+/* We also need a table of characters that may follow \c in an EBCDIC
+environment for characters 0-31. */
+
+static unsigned char ebcdic_escape_c[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
+
+#endif /* EBCDIC */
+
+
+/* Table of special "verbs" like (*PRUNE). This is a short table, so it is
+searched linearly. Put all the names into a single string, in order to reduce
+the number of relocations when a shared library is dynamically linked. The
+string is built from string macros so that it works in UTF-8 mode on EBCDIC
+platforms. */
+
+typedef struct verbitem {
+ unsigned int len; /* Length of verb name */
+ uint32_t meta; /* Base META_ code */
+ int has_arg; /* Argument requirement */
+} verbitem;
+
+static const char verbnames[] =
+ "\0" /* Empty name is a shorthand for MARK */
+ STRING_MARK0
+ STRING_ACCEPT0
+ STRING_F0
+ STRING_FAIL0
+ STRING_COMMIT0
+ STRING_PRUNE0
+ STRING_SKIP0
+ STRING_THEN;
+
+static const verbitem verbs[] = {
+ { 0, META_MARK, +1 }, /* > 0 => must have an argument */
+ { 4, META_MARK, +1 },
+ { 6, META_ACCEPT, -1 }, /* < 0 => Optional argument, convert to pre-MARK */
+ { 1, META_FAIL, -1 },
+ { 4, META_FAIL, -1 },
+ { 6, META_COMMIT, 0 },
+ { 5, META_PRUNE, 0 }, /* Optional argument; bump META code if found */
+ { 4, META_SKIP, 0 },
+ { 4, META_THEN, 0 }
+};
+
+static const int verbcount = sizeof(verbs)/sizeof(verbitem);
+
+/* Verb opcodes, indexed by their META code offset from META_MARK. */
+
+static const uint32_t verbops[] = {
+ OP_MARK, OP_ACCEPT, OP_FAIL, OP_COMMIT, OP_COMMIT_ARG, OP_PRUNE,
+ OP_PRUNE_ARG, OP_SKIP, OP_SKIP_ARG, OP_THEN, OP_THEN_ARG };
+
+/* Table of "alpha assertions" like (*pla:...), similar to the (*VERB) table. */
+
+typedef struct alasitem {
+ unsigned int len; /* Length of name */
+ uint32_t meta; /* Base META_ code */
+} alasitem;
+
+static const char alasnames[] =
+ STRING_pla0
+ STRING_plb0
+ STRING_napla0
+ STRING_naplb0
+ STRING_nla0
+ STRING_nlb0
+ STRING_positive_lookahead0
+ STRING_positive_lookbehind0
+ STRING_non_atomic_positive_lookahead0
+ STRING_non_atomic_positive_lookbehind0
+ STRING_negative_lookahead0
+ STRING_negative_lookbehind0
+ STRING_atomic0
+ STRING_sr0
+ STRING_asr0
+ STRING_script_run0
+ STRING_atomic_script_run;
+
+static const alasitem alasmeta[] = {
+ { 3, META_LOOKAHEAD },
+ { 3, META_LOOKBEHIND },
+ { 5, META_LOOKAHEAD_NA },
+ { 5, META_LOOKBEHIND_NA },
+ { 3, META_LOOKAHEADNOT },
+ { 3, META_LOOKBEHINDNOT },
+ { 18, META_LOOKAHEAD },
+ { 19, META_LOOKBEHIND },
+ { 29, META_LOOKAHEAD_NA },
+ { 30, META_LOOKBEHIND_NA },
+ { 18, META_LOOKAHEADNOT },
+ { 19, META_LOOKBEHINDNOT },
+ { 6, META_ATOMIC },
+ { 2, META_SCRIPT_RUN }, /* sr = script run */
+ { 3, META_ATOMIC_SCRIPT_RUN }, /* asr = atomic script run */
+ { 10, META_SCRIPT_RUN }, /* script run */
+ { 17, META_ATOMIC_SCRIPT_RUN } /* atomic script run */
+};
+
+static const int alascount = sizeof(alasmeta)/sizeof(alasitem);
+
+/* Offsets from OP_STAR for case-independent and negative repeat opcodes. */
+
+static uint32_t chartypeoffset[] = {
+ OP_STAR - OP_STAR, OP_STARI - OP_STAR,
+ OP_NOTSTAR - OP_STAR, OP_NOTSTARI - OP_STAR };
+
+/* Tables of names of POSIX character classes and their lengths. The names are
+now all in a single string, to reduce the number of relocations when a shared
+library is dynamically loaded. The list of lengths is terminated by a zero
+length entry. The first three must be alpha, lower, upper, as this is assumed
+for handling case independence. The indices for graph, print, and punct are
+needed, so identify them. */
+
+static const char posix_names[] =
+ STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0
+ STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0
+ STRING_graph0 STRING_print0 STRING_punct0 STRING_space0
+ STRING_word0 STRING_xdigit;
+
+static const uint8_t posix_name_lengths[] = {
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 };
+
+#define PC_GRAPH 8
+#define PC_PRINT 9
+#define PC_PUNCT 10
+
+/* Table of class bit maps for each POSIX class. Each class is formed from a
+base map, with an optional addition or removal of another map. Then, for some
+classes, there is some additional tweaking: for [:blank:] the vertical space
+characters are removed, and for [:alpha:] and [:alnum:] the underscore
+character is removed. The triples in the table consist of the base map offset,
+second map offset or -1 if no second map, and a non-negative value for map
+addition or a negative value for map subtraction (if there are two maps). The
+absolute value of the third field has these meanings: 0 => no tweaking, 1 =>
+remove vertical space characters, 2 => remove underscore. */
+
+static const int posix_class_maps[] = {
+ cbit_word, cbit_digit, -2, /* alpha */
+ cbit_lower, -1, 0, /* lower */
+ cbit_upper, -1, 0, /* upper */
+ cbit_word, -1, 2, /* alnum - word without underscore */
+ cbit_print, cbit_cntrl, 0, /* ascii */
+ cbit_space, -1, 1, /* blank - a GNU extension */
+ cbit_cntrl, -1, 0, /* cntrl */
+ cbit_digit, -1, 0, /* digit */
+ cbit_graph, -1, 0, /* graph */
+ cbit_print, -1, 0, /* print */
+ cbit_punct, -1, 0, /* punct */
+ cbit_space, -1, 0, /* space */
+ cbit_word, -1, 0, /* word - a Perl extension */
+ cbit_xdigit,-1, 0 /* xdigit */
+};
+
+#ifdef SUPPORT_UNICODE
+
+/* The POSIX class Unicode property substitutes that are used in UCP mode must
+be in the order of the POSIX class names, defined above. */
+
+static int posix_substitutes[] = {
+ PT_GC, ucp_L, /* alpha */
+ PT_PC, ucp_Ll, /* lower */
+ PT_PC, ucp_Lu, /* upper */
+ PT_ALNUM, 0, /* alnum */
+ -1, 0, /* ascii, treat as non-UCP */
+ -1, 1, /* blank, treat as \h */
+ PT_PC, ucp_Cc, /* cntrl */
+ PT_PC, ucp_Nd, /* digit */
+ PT_PXGRAPH, 0, /* graph */
+ PT_PXPRINT, 0, /* print */
+ PT_PXPUNCT, 0, /* punct */
+ PT_PXSPACE, 0, /* space */ /* Xps is POSIX space, but from 8.34 */
+ PT_WORD, 0, /* word */ /* Perl and POSIX space are the same */
+ -1, 0 /* xdigit, treat as non-UCP */
+};
+#define POSIX_SUBSIZE (sizeof(posix_substitutes) / (2*sizeof(uint32_t)))
+#endif /* SUPPORT_UNICODE */
+
+/* Masks for checking option settings. When PCRE2_LITERAL is set, only a subset
+are allowed. */
+
+#define PUBLIC_LITERAL_COMPILE_OPTIONS \
+ (PCRE2_ANCHORED|PCRE2_AUTO_CALLOUT|PCRE2_CASELESS|PCRE2_ENDANCHORED| \
+ PCRE2_FIRSTLINE|PCRE2_LITERAL|PCRE2_MATCH_INVALID_UTF| \
+ PCRE2_NO_START_OPTIMIZE|PCRE2_NO_UTF_CHECK|PCRE2_USE_OFFSET_LIMIT|PCRE2_UTF)
+
+#define PUBLIC_COMPILE_OPTIONS \
+ (PUBLIC_LITERAL_COMPILE_OPTIONS| \
+ PCRE2_ALLOW_EMPTY_CLASS|PCRE2_ALT_BSUX|PCRE2_ALT_CIRCUMFLEX| \
+ PCRE2_ALT_VERBNAMES|PCRE2_DOLLAR_ENDONLY|PCRE2_DOTALL|PCRE2_DUPNAMES| \
+ PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MATCH_UNSET_BACKREF| \
+ PCRE2_MULTILINE|PCRE2_NEVER_BACKSLASH_C|PCRE2_NEVER_UCP| \
+ PCRE2_NEVER_UTF|PCRE2_NO_AUTO_CAPTURE|PCRE2_NO_AUTO_POSSESS| \
+ PCRE2_NO_DOTSTAR_ANCHOR|PCRE2_UCP|PCRE2_UNGREEDY)
+
+#define PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS \
+ (PCRE2_EXTRA_MATCH_LINE|PCRE2_EXTRA_MATCH_WORD)
+
+#define PUBLIC_COMPILE_EXTRA_OPTIONS \
+ (PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS| \
+ PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES|PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL| \
+ PCRE2_EXTRA_ESCAPED_CR_IS_LF|PCRE2_EXTRA_ALT_BSUX| \
+ PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK)
+
+/* Compile time error code numbers. They are given names so that they can more
+easily be tracked. When a new number is added, the tables called eint1 and
+eint2 in pcre2posix.c may need to be updated, and a new error text must be
+added to compile_error_texts in pcre2_error.c. Also, the error codes in
+pcre2.h.in must be updated - their values are exactly 100 greater than these
+values. */
+
+enum { ERR0 = COMPILE_ERROR_BASE,
+ ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10,
+ ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20,
+ ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, ERR30,
+ ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40,
+ ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, ERR50,
+ ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, ERR60,
+ ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, ERR70,
+ ERR71, ERR72, ERR73, ERR74, ERR75, ERR76, ERR77, ERR78, ERR79, ERR80,
+ ERR81, ERR82, ERR83, ERR84, ERR85, ERR86, ERR87, ERR88, ERR89, ERR90,
+ ERR91, ERR92, ERR93, ERR94, ERR95, ERR96, ERR97, ERR98, ERR99 };
+
+/* This is a table of start-of-pattern options such as (*UTF) and settings such
+as (*LIMIT_MATCH=nnnn) and (*CRLF). For completeness and backward
+compatibility, (*UTFn) is supported in the relevant libraries, but (*UTF) is
+generic and always supported. */
+
+enum { PSO_OPT, /* Value is an option bit */
+ PSO_FLG, /* Value is a flag bit */
+ PSO_NL, /* Value is a newline type */
+ PSO_BSR, /* Value is a \R type */
+ PSO_LIMH, /* Read integer value for heap limit */
+ PSO_LIMM, /* Read integer value for match limit */
+ PSO_LIMD }; /* Read integer value for depth limit */
+
+typedef struct pso {
+ const uint8_t *name;
+ uint16_t length;
+ uint16_t type;
+ uint32_t value;
+} pso;
+
+/* NB: STRING_UTFn_RIGHTPAR contains the length as well */
+
+static pso pso_list[] = {
+ { (uint8_t *)STRING_UTFn_RIGHTPAR, PSO_OPT, PCRE2_UTF },
+ { (uint8_t *)STRING_UTF_RIGHTPAR, 4, PSO_OPT, PCRE2_UTF },
+ { (uint8_t *)STRING_UCP_RIGHTPAR, 4, PSO_OPT, PCRE2_UCP },
+ { (uint8_t *)STRING_NOTEMPTY_RIGHTPAR, 9, PSO_FLG, PCRE2_NOTEMPTY_SET },
+ { (uint8_t *)STRING_NOTEMPTY_ATSTART_RIGHTPAR, 17, PSO_FLG, PCRE2_NE_ATST_SET },
+ { (uint8_t *)STRING_NO_AUTO_POSSESS_RIGHTPAR, 16, PSO_OPT, PCRE2_NO_AUTO_POSSESS },
+ { (uint8_t *)STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR, 18, PSO_OPT, PCRE2_NO_DOTSTAR_ANCHOR },
+ { (uint8_t *)STRING_NO_JIT_RIGHTPAR, 7, PSO_FLG, PCRE2_NOJIT },
+ { (uint8_t *)STRING_NO_START_OPT_RIGHTPAR, 13, PSO_OPT, PCRE2_NO_START_OPTIMIZE },
+ { (uint8_t *)STRING_LIMIT_HEAP_EQ, 11, PSO_LIMH, 0 },
+ { (uint8_t *)STRING_LIMIT_MATCH_EQ, 12, PSO_LIMM, 0 },
+ { (uint8_t *)STRING_LIMIT_DEPTH_EQ, 12, PSO_LIMD, 0 },
+ { (uint8_t *)STRING_LIMIT_RECURSION_EQ, 16, PSO_LIMD, 0 },
+ { (uint8_t *)STRING_CR_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_CR },
+ { (uint8_t *)STRING_LF_RIGHTPAR, 3, PSO_NL, PCRE2_NEWLINE_LF },
+ { (uint8_t *)STRING_CRLF_RIGHTPAR, 5, PSO_NL, PCRE2_NEWLINE_CRLF },
+ { (uint8_t *)STRING_ANY_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_ANY },
+ { (uint8_t *)STRING_NUL_RIGHTPAR, 4, PSO_NL, PCRE2_NEWLINE_NUL },
+ { (uint8_t *)STRING_ANYCRLF_RIGHTPAR, 8, PSO_NL, PCRE2_NEWLINE_ANYCRLF },
+ { (uint8_t *)STRING_BSR_ANYCRLF_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_ANYCRLF },
+ { (uint8_t *)STRING_BSR_UNICODE_RIGHTPAR, 12, PSO_BSR, PCRE2_BSR_UNICODE }
+};
+
+/* This table is used when converting repeating opcodes into possessified
+versions as a result of an explicit possessive quantifier such as ++. A zero
+value means there is no possessified version - in those cases the item in
+question must be wrapped in ONCE brackets. The table is truncated at OP_CALLOUT
+because all relevant opcodes are less than that. */
+
+static const uint8_t opcode_possessify[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 15 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 - 31 */
+
+ 0, /* NOTI */
+ OP_POSSTAR, 0, /* STAR, MINSTAR */
+ OP_POSPLUS, 0, /* PLUS, MINPLUS */
+ OP_POSQUERY, 0, /* QUERY, MINQUERY */
+ OP_POSUPTO, 0, /* UPTO, MINUPTO */
+ 0, /* EXACT */
+ 0, 0, 0, 0, /* POS{STAR,PLUS,QUERY,UPTO} */
+
+ OP_POSSTARI, 0, /* STARI, MINSTARI */
+ OP_POSPLUSI, 0, /* PLUSI, MINPLUSI */
+ OP_POSQUERYI, 0, /* QUERYI, MINQUERYI */
+ OP_POSUPTOI, 0, /* UPTOI, MINUPTOI */
+ 0, /* EXACTI */
+ 0, 0, 0, 0, /* POS{STARI,PLUSI,QUERYI,UPTOI} */
+
+ OP_NOTPOSSTAR, 0, /* NOTSTAR, NOTMINSTAR */
+ OP_NOTPOSPLUS, 0, /* NOTPLUS, NOTMINPLUS */
+ OP_NOTPOSQUERY, 0, /* NOTQUERY, NOTMINQUERY */
+ OP_NOTPOSUPTO, 0, /* NOTUPTO, NOTMINUPTO */
+ 0, /* NOTEXACT */
+ 0, 0, 0, 0, /* NOTPOS{STAR,PLUS,QUERY,UPTO} */
+
+ OP_NOTPOSSTARI, 0, /* NOTSTARI, NOTMINSTARI */
+ OP_NOTPOSPLUSI, 0, /* NOTPLUSI, NOTMINPLUSI */
+ OP_NOTPOSQUERYI, 0, /* NOTQUERYI, NOTMINQUERYI */
+ OP_NOTPOSUPTOI, 0, /* NOTUPTOI, NOTMINUPTOI */
+ 0, /* NOTEXACTI */
+ 0, 0, 0, 0, /* NOTPOS{STARI,PLUSI,QUERYI,UPTOI} */
+
+ OP_TYPEPOSSTAR, 0, /* TYPESTAR, TYPEMINSTAR */
+ OP_TYPEPOSPLUS, 0, /* TYPEPLUS, TYPEMINPLUS */
+ OP_TYPEPOSQUERY, 0, /* TYPEQUERY, TYPEMINQUERY */
+ OP_TYPEPOSUPTO, 0, /* TYPEUPTO, TYPEMINUPTO */
+ 0, /* TYPEEXACT */
+ 0, 0, 0, 0, /* TYPEPOS{STAR,PLUS,QUERY,UPTO} */
+
+ OP_CRPOSSTAR, 0, /* CRSTAR, CRMINSTAR */
+ OP_CRPOSPLUS, 0, /* CRPLUS, CRMINPLUS */
+ OP_CRPOSQUERY, 0, /* CRQUERY, CRMINQUERY */
+ OP_CRPOSRANGE, 0, /* CRRANGE, CRMINRANGE */
+ 0, 0, 0, 0, /* CRPOS{STAR,PLUS,QUERY,RANGE} */
+
+ 0, 0, 0, /* CLASS, NCLASS, XCLASS */
+ 0, 0, /* REF, REFI */
+ 0, 0, /* DNREF, DNREFI */
+ 0, 0 /* RECURSE, CALLOUT */
+};
+
+
+#ifdef DEBUG_SHOW_PARSED
+/*************************************************
+* Show the parsed pattern for debugging *
+*************************************************/
+
+/* For debugging the pre-scan, this code, which outputs the parsed data vector,
+can be enabled. */
+
+static void show_parsed(compile_block *cb)
+{
+uint32_t *pptr = cb->parsed_pattern;
+
+for (;;)
+ {
+ int max, min;
+ PCRE2_SIZE offset;
+ uint32_t i;
+ uint32_t length;
+ uint32_t meta_arg = META_DATA(*pptr);
+
+ fprintf(stderr, "+++ %02d %.8x ", (int)(pptr - cb->parsed_pattern), *pptr);
+
+ if (*pptr < META_END)
+ {
+ if (*pptr > 32 && *pptr < 128) fprintf(stderr, "%c", *pptr);
+ pptr++;
+ }
+
+ else switch (META_CODE(*pptr++))
+ {
+ default:
+ fprintf(stderr, "**** OOPS - unknown META value - giving up ****\n");
+ return;
+
+ case META_END:
+ fprintf(stderr, "META_END\n");
+ return;
+
+ case META_CAPTURE:
+ fprintf(stderr, "META_CAPTURE %d", meta_arg);
+ break;
+
+ case META_RECURSE:
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "META_RECURSE %d %zd", meta_arg, offset);
+ break;
+
+ case META_BACKREF:
+ if (meta_arg < 10)
+ offset = cb->small_ref_offset[meta_arg];
+ else
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "META_BACKREF %d %zd", meta_arg, offset);
+ break;
+
+ case META_ESCAPE:
+ if (meta_arg == ESC_P || meta_arg == ESC_p)
+ {
+ uint32_t ptype = *pptr >> 16;
+ uint32_t pvalue = *pptr++ & 0xffff;
+ fprintf(stderr, "META \\%c %d %d", (meta_arg == ESC_P)? 'P':'p',
+ ptype, pvalue);
+ }
+ else
+ {
+ uint32_t cc;
+ /* There's just one escape we might have here that isn't negated in the
+ escapes table. */
+ if (meta_arg == ESC_g) cc = CHAR_g;
+ else for (cc = ESCAPES_FIRST; cc <= ESCAPES_LAST; cc++)
+ {
+ if (meta_arg == (uint32_t)(-escapes[cc - ESCAPES_FIRST])) break;
+ }
+ if (cc > ESCAPES_LAST) cc = CHAR_QUESTION_MARK;
+ fprintf(stderr, "META \\%c", cc);
+ }
+ break;
+
+ case META_MINMAX:
+ min = *pptr++;
+ max = *pptr++;
+ if (max != REPEAT_UNLIMITED)
+ fprintf(stderr, "META {%d,%d}", min, max);
+ else
+ fprintf(stderr, "META {%d,}", min);
+ break;
+
+ case META_MINMAX_QUERY:
+ min = *pptr++;
+ max = *pptr++;
+ if (max != REPEAT_UNLIMITED)
+ fprintf(stderr, "META {%d,%d}?", min, max);
+ else
+ fprintf(stderr, "META {%d,}?", min);
+ break;
+
+ case META_MINMAX_PLUS:
+ min = *pptr++;
+ max = *pptr++;
+ if (max != REPEAT_UNLIMITED)
+ fprintf(stderr, "META {%d,%d}+", min, max);
+ else
+ fprintf(stderr, "META {%d,}+", min);
+ break;
+
+ case META_BIGVALUE: fprintf(stderr, "META_BIGVALUE %.8x", *pptr++); break;
+ case META_CIRCUMFLEX: fprintf(stderr, "META_CIRCUMFLEX"); break;
+ case META_COND_ASSERT: fprintf(stderr, "META_COND_ASSERT"); break;
+ case META_DOLLAR: fprintf(stderr, "META_DOLLAR"); break;
+ case META_DOT: fprintf(stderr, "META_DOT"); break;
+ case META_ASTERISK: fprintf(stderr, "META *"); break;
+ case META_ASTERISK_QUERY: fprintf(stderr, "META *?"); break;
+ case META_ASTERISK_PLUS: fprintf(stderr, "META *+"); break;
+ case META_PLUS: fprintf(stderr, "META +"); break;
+ case META_PLUS_QUERY: fprintf(stderr, "META +?"); break;
+ case META_PLUS_PLUS: fprintf(stderr, "META ++"); break;
+ case META_QUERY: fprintf(stderr, "META ?"); break;
+ case META_QUERY_QUERY: fprintf(stderr, "META ??"); break;
+ case META_QUERY_PLUS: fprintf(stderr, "META ?+"); break;
+
+ case META_ATOMIC: fprintf(stderr, "META (?>"); break;
+ case META_NOCAPTURE: fprintf(stderr, "META (?:"); break;
+ case META_LOOKAHEAD: fprintf(stderr, "META (?="); break;
+ case META_LOOKAHEADNOT: fprintf(stderr, "META (?!"); break;
+ case META_LOOKAHEAD_NA: fprintf(stderr, "META (*napla:"); break;
+ case META_SCRIPT_RUN: fprintf(stderr, "META (*sr:"); break;
+ case META_KET: fprintf(stderr, "META )"); break;
+ case META_ALT: fprintf(stderr, "META | %d", meta_arg); break;
+
+ case META_CLASS: fprintf(stderr, "META ["); break;
+ case META_CLASS_NOT: fprintf(stderr, "META [^"); break;
+ case META_CLASS_END: fprintf(stderr, "META ]"); break;
+ case META_CLASS_EMPTY: fprintf(stderr, "META []"); break;
+ case META_CLASS_EMPTY_NOT: fprintf(stderr, "META [^]"); break;
+
+ case META_RANGE_LITERAL: fprintf(stderr, "META - (literal)"); break;
+ case META_RANGE_ESCAPED: fprintf(stderr, "META - (escaped)"); break;
+
+ case META_POSIX: fprintf(stderr, "META_POSIX %d", *pptr++); break;
+ case META_POSIX_NEG: fprintf(stderr, "META_POSIX_NEG %d", *pptr++); break;
+
+ case META_ACCEPT: fprintf(stderr, "META (*ACCEPT)"); break;
+ case META_FAIL: fprintf(stderr, "META (*FAIL)"); break;
+ case META_COMMIT: fprintf(stderr, "META (*COMMIT)"); break;
+ case META_PRUNE: fprintf(stderr, "META (*PRUNE)"); break;
+ case META_SKIP: fprintf(stderr, "META (*SKIP)"); break;
+ case META_THEN: fprintf(stderr, "META (*THEN)"); break;
+
+ case META_OPTIONS: fprintf(stderr, "META_OPTIONS 0x%02x", *pptr++); break;
+
+ case META_LOOKBEHIND:
+ fprintf(stderr, "META (?<= %d offset=", meta_arg);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_LOOKBEHIND_NA:
+ fprintf(stderr, "META (*naplb: %d offset=", meta_arg);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_LOOKBEHINDNOT:
+ fprintf(stderr, "META (?<! %d offset=", meta_arg);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_CALLOUT_NUMBER:
+ fprintf(stderr, "META (?C%d) next=%d/%d", pptr[2], pptr[0],
+ pptr[1]);
+ pptr += 3;
+ break;
+
+ case META_CALLOUT_STRING:
+ {
+ uint32_t patoffset = *pptr++; /* Offset of next pattern item */
+ uint32_t patlength = *pptr++; /* Length of next pattern item */
+ fprintf(stderr, "META (?Cstring) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd next=%d/%d", offset, patoffset, patlength);
+ }
+ break;
+
+ case META_RECURSE_BYNAME:
+ fprintf(stderr, "META (?(&name) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_BACKREF_BYNAME:
+ fprintf(stderr, "META_BACKREF_BYNAME length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_COND_NUMBER:
+ fprintf(stderr, "META_COND_NUMBER %d offset=", pptr[SIZEOFFSET]);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ pptr++;
+ break;
+
+ case META_COND_DEFINE:
+ fprintf(stderr, "META (?(DEFINE) offset=");
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_COND_VERSION:
+ fprintf(stderr, "META (?(VERSION%s", (*pptr++ == 0)? "=" : ">=");
+ fprintf(stderr, "%d.", *pptr++);
+ fprintf(stderr, "%d)", *pptr++);
+ break;
+
+ case META_COND_NAME:
+ fprintf(stderr, "META (?(<name>) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_COND_RNAME:
+ fprintf(stderr, "META (?(R&name) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ /* This is kept as a name, because it might be. */
+
+ case META_COND_RNUMBER:
+ fprintf(stderr, "META (?(Rnumber) length=%d offset=", *pptr++);
+ GETOFFSET(offset, pptr);
+ fprintf(stderr, "%zd", offset);
+ break;
+
+ case META_MARK:
+ fprintf(stderr, "META (*MARK:");
+ goto SHOWARG;
+
+ case META_COMMIT_ARG:
+ fprintf(stderr, "META (*COMMIT:");
+ goto SHOWARG;
+
+ case META_PRUNE_ARG:
+ fprintf(stderr, "META (*PRUNE:");
+ goto SHOWARG;
+
+ case META_SKIP_ARG:
+ fprintf(stderr, "META (*SKIP:");
+ goto SHOWARG;
+
+ case META_THEN_ARG:
+ fprintf(stderr, "META (*THEN:");
+ SHOWARG:
+ length = *pptr++;
+ for (i = 0; i < length; i++)
+ {
+ uint32_t cc = *pptr++;
+ if (cc > 32 && cc < 128) fprintf(stderr, "%c", cc);
+ else fprintf(stderr, "\\x{%x}", cc);
+ }
+ fprintf(stderr, ") length=%u", length);
+ break;
+ }
+ fprintf(stderr, "\n");
+ }
+return;
+}
+#endif /* DEBUG_SHOW_PARSED */
+
+
+
+/*************************************************
+* Copy compiled code *
+*************************************************/
+
+/* Compiled JIT code cannot be copied, so the new compiled block has no
+associated JIT data. */
+
+PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION
+pcre2_code_copy(const pcre2_code *code)
+{
+PCRE2_SIZE* ref_count;
+pcre2_code *newcode;
+
+if (code == NULL) return NULL;
+newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data);
+if (newcode == NULL) return NULL;
+memcpy(newcode, code, code->blocksize);
+newcode->executable_jit = NULL;
+
+/* If the code is one that has been deserialized, increment the reference count
+in the decoded tables. */
+
+if ((code->flags & PCRE2_DEREF_TABLES) != 0)
+ {
+ ref_count = (PCRE2_SIZE *)(code->tables + TABLES_LENGTH);
+ (*ref_count)++;
+ }
+
+return newcode;
+}
+
+
+
+/*************************************************
+* Copy compiled code and character tables *
+*************************************************/
+
+/* Compiled JIT code cannot be copied, so the new compiled block has no
+associated JIT data. This version of code_copy also makes a separate copy of
+the character tables. */
+
+PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION
+pcre2_code_copy_with_tables(const pcre2_code *code)
+{
+PCRE2_SIZE* ref_count;
+pcre2_code *newcode;
+uint8_t *newtables;
+
+if (code == NULL) return NULL;
+newcode = code->memctl.malloc(code->blocksize, code->memctl.memory_data);
+if (newcode == NULL) return NULL;
+memcpy(newcode, code, code->blocksize);
+newcode->executable_jit = NULL;
+
+newtables = code->memctl.malloc(TABLES_LENGTH + sizeof(PCRE2_SIZE),
+ code->memctl.memory_data);
+if (newtables == NULL)
+ {
+ code->memctl.free((void *)newcode, code->memctl.memory_data);
+ return NULL;
+ }
+memcpy(newtables, code->tables, TABLES_LENGTH);
+ref_count = (PCRE2_SIZE *)(newtables + TABLES_LENGTH);
+*ref_count = 1;
+
+newcode->tables = newtables;
+newcode->flags |= PCRE2_DEREF_TABLES;
+return newcode;
+}
+
+
+
+/*************************************************
+* Free compiled code *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_code_free(pcre2_code *code)
+{
+PCRE2_SIZE* ref_count;
+
+if (code != NULL)
+ {
+#ifdef SUPPORT_JIT
+ if (code->executable_jit != NULL)
+ PRIV(jit_free)(code->executable_jit, &code->memctl);
+#endif
+
+ if ((code->flags & PCRE2_DEREF_TABLES) != 0)
+ {
+ /* Decoded tables belong to the codes after deserialization, and they must
+ be freed when there are no more references to them. The *ref_count should
+ always be > 0. */
+
+ ref_count = (PCRE2_SIZE *)(code->tables + TABLES_LENGTH);
+ if (*ref_count > 0)
+ {
+ (*ref_count)--;
+ if (*ref_count == 0)
+ code->memctl.free((void *)code->tables, code->memctl.memory_data);
+ }
+ }
+
+ code->memctl.free(code, code->memctl.memory_data);
+ }
+}
+
+
+
+/*************************************************
+* Read a number, possibly signed *
+*************************************************/
+
+/* This function is used to read numbers in the pattern. The initial pointer
+must be the sign or first digit of the number. When relative values (introduced
+by + or -) are allowed, they are relative group numbers, and the result must be
+greater than zero.
+
+Arguments:
+ ptrptr points to the character pointer variable
+ ptrend points to the end of the input string
+ allow_sign if < 0, sign not allowed; if >= 0, sign is relative to this
+ max_value the largest number allowed
+ max_error the error to give for an over-large number
+ intptr where to put the result
+ errcodeptr where to put an error code
+
+Returns: TRUE - a number was read
+ FALSE - errorcode == 0 => no number was found
+ errorcode != 0 => an error occurred
+*/
+
+static BOOL
+read_number(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, int32_t allow_sign,
+ uint32_t max_value, uint32_t max_error, int *intptr, int *errorcodeptr)
+{
+int sign = 0;
+uint32_t n = 0;
+PCRE2_SPTR ptr = *ptrptr;
+BOOL yield = FALSE;
+
+*errorcodeptr = 0;
+
+if (allow_sign >= 0 && ptr < ptrend)
+ {
+ if (*ptr == CHAR_PLUS)
+ {
+ sign = +1;
+ max_value -= allow_sign;
+ ptr++;
+ }
+ else if (*ptr == CHAR_MINUS)
+ {
+ sign = -1;
+ ptr++;
+ }
+ }
+
+if (ptr >= ptrend || !IS_DIGIT(*ptr)) return FALSE;
+while (ptr < ptrend && IS_DIGIT(*ptr))
+ {
+ n = n * 10 + *ptr++ - CHAR_0;
+ if (n > max_value)
+ {
+ *errorcodeptr = max_error;
+ goto EXIT;
+ }
+ }
+
+if (allow_sign >= 0 && sign != 0)
+ {
+ if (n == 0)
+ {
+ *errorcodeptr = ERR26; /* +0 and -0 are not allowed */
+ goto EXIT;
+ }
+
+ if (sign > 0) n += allow_sign;
+ else if ((int)n > allow_sign)
+ {
+ *errorcodeptr = ERR15; /* Non-existent subpattern */
+ goto EXIT;
+ }
+ else n = allow_sign + 1 - n;
+ }
+
+yield = TRUE;
+
+EXIT:
+*intptr = n;
+*ptrptr = ptr;
+return yield;
+}
+
+
+
+/*************************************************
+* Read repeat counts *
+*************************************************/
+
+/* Read an item of the form {n,m} and return the values if non-NULL pointers
+are supplied. Repeat counts must be less than 65536 (MAX_REPEAT_COUNT); a
+larger value is used for "unlimited". We have to use signed arguments for
+read_number() because it is capable of returning a signed value.
+
+Arguments:
+ ptrptr points to pointer to character after'{'
+ ptrend pointer to end of input
+ minp if not NULL, pointer to int for min
+ maxp if not NULL, pointer to int for max (-1 if no max)
+ returned as -1 if no max
+ errorcodeptr points to error code variable
+
+Returns: FALSE if not a repeat quantifier, errorcode set zero
+ FALSE on error, with errorcode set non-zero
+ TRUE on success, with pointer updated to point after '}'
+*/
+
+static BOOL
+read_repeat_counts(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *minp,
+ uint32_t *maxp, int *errorcodeptr)
+{
+PCRE2_SPTR p;
+BOOL yield = FALSE;
+BOOL had_comma = FALSE;
+int32_t min = 0;
+int32_t max = REPEAT_UNLIMITED; /* This value is larger than MAX_REPEAT_COUNT */
+
+/* Check the syntax */
+
+*errorcodeptr = 0;
+for (p = *ptrptr;; p++)
+ {
+ uint32_t c;
+ if (p >= ptrend) return FALSE;
+ c = *p;
+ if (IS_DIGIT(c)) continue;
+ if (c == CHAR_RIGHT_CURLY_BRACKET) break;
+ if (c == CHAR_COMMA)
+ {
+ if (had_comma) return FALSE;
+ had_comma = TRUE;
+ }
+ else return FALSE;
+ }
+
+/* The only error from read_number() is for a number that is too big. */
+
+p = *ptrptr;
+if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &min, errorcodeptr))
+ goto EXIT;
+
+if (*p == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ p++;
+ max = min;
+ }
+else
+ {
+ if (*(++p) != CHAR_RIGHT_CURLY_BRACKET)
+ {
+ if (!read_number(&p, ptrend, -1, MAX_REPEAT_COUNT, ERR5, &max,
+ errorcodeptr))
+ goto EXIT;
+ if (max < min)
+ {
+ *errorcodeptr = ERR4;
+ goto EXIT;
+ }
+ }
+ p++;
+ }
+
+yield = TRUE;
+if (minp != NULL) *minp = (uint32_t)min;
+if (maxp != NULL) *maxp = (uint32_t)max;
+
+/* Update the pattern pointer */
+
+EXIT:
+*ptrptr = p;
+return yield;
+}
+
+
+
+/*************************************************
+* Handle escapes *
+*************************************************/
+
+/* This function is called when a \ has been encountered. It either returns a
+positive value for a simple escape such as \d, or 0 for a data character, which
+is placed in chptr. A backreference to group n is returned as negative n. On
+entry, ptr is pointing at the character after \. On exit, it points after the
+final code unit of the escape sequence.
+
+This function is also called from pcre2_substitute() to handle escape sequences
+in replacement strings. In this case, the cb argument is NULL, and in the case
+of escapes that have further processing, only sequences that define a data
+character are recognised. The isclass argument is not relevant; the options
+argument is the final value of the compiled pattern's options.
+
+Arguments:
+ ptrptr points to the input position pointer
+ ptrend points to the end of the input
+ chptr points to a returned data character
+ errorcodeptr points to the errorcode variable (containing zero)
+ options the current options bits
+ isclass TRUE if inside a character class
+ cb compile data block or NULL when called from pcre2_substitute()
+
+Returns: zero => a data character
+ positive => a special escape sequence
+ negative => a numerical back reference
+ on error, errorcodeptr is set non-zero
+*/
+
+int
+PRIV(check_escape)(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, uint32_t *chptr,
+ int *errorcodeptr, uint32_t options, uint32_t extra_options, BOOL isclass,
+ compile_block *cb)
+{
+BOOL utf = (options & PCRE2_UTF) != 0;
+PCRE2_SPTR ptr = *ptrptr;
+uint32_t c, cc;
+int escape = 0;
+int i;
+
+/* If backslash is at the end of the string, it's an error. */
+
+if (ptr >= ptrend)
+ {
+ *errorcodeptr = ERR1;
+ return 0;
+ }
+
+GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
+*errorcodeptr = 0; /* Be optimistic */
+
+/* Non-alphanumerics are literals, so we just leave the value in c. An initial
+value test saves a memory lookup for code points outside the alphanumeric
+range. */
+
+if (c < ESCAPES_FIRST || c > ESCAPES_LAST) {} /* Definitely literal */
+
+/* Otherwise, do a table lookup. Non-zero values need little processing here. A
+positive value is a literal value for something like \n. A negative value is
+the negation of one of the ESC_ macros that is passed back for handling by the
+calling function. Some extra checking is needed for \N because only \N{U+dddd}
+is supported. If the value is zero, further processing is handled below. */
+
+else if ((i = escapes[c - ESCAPES_FIRST]) != 0)
+ {
+ if (i > 0)
+ {
+ c = (uint32_t)i;
+ if (c == CHAR_CR && (extra_options & PCRE2_EXTRA_ESCAPED_CR_IS_LF) != 0)
+ c = CHAR_LF;
+ }
+ else /* Negative table entry */
+ {
+ escape = -i; /* Else return a special escape */
+ if (cb != NULL && (escape == ESC_P || escape == ESC_p || escape == ESC_X))
+ cb->external_flags |= PCRE2_HASBKPORX; /* Note \P, \p, or \X */
+
+ /* Perl supports \N{name} for character names and \N{U+dddd} for numerical
+ Unicode code points, as well as plain \N for "not newline". PCRE does not
+ support \N{name}. However, it does support quantification such as \N{2,3},
+ so if \N{ is not followed by U+dddd we check for a quantifier. */
+
+ if (escape == ESC_N && ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET)
+ {
+ PCRE2_SPTR p = ptr + 1;
+
+ /* \N{U+ can be handled by the \x{ code. However, this construction is
+ not valid in EBCDIC environments because it specifies a Unicode
+ character, not a codepoint in the local code. For example \N{U+0041}
+ must be "A" in all environments. Also, in Perl, \N{U+ forces Unicode
+ casing semantics for the entire pattern, so allow it only in UTF (i.e.
+ Unicode) mode. */
+
+ if (ptrend - p > 1 && *p == CHAR_U && p[1] == CHAR_PLUS)
+ {
+#ifdef EBCDIC
+ *errorcodeptr = ERR93;
+#else
+ if (utf)
+ {
+ ptr = p + 1;
+ escape = 0; /* Not a fancy escape after all */
+ goto COME_FROM_NU;
+ }
+ else *errorcodeptr = ERR93;
+#endif
+ }
+
+ /* Give an error if what follows is not a quantifier, but don't override
+ an error set by the quantifier reader (e.g. number overflow). */
+
+ else
+ {
+ if (!read_repeat_counts(&p, ptrend, NULL, NULL, errorcodeptr) &&
+ *errorcodeptr == 0)
+ *errorcodeptr = ERR37;
+ }
+ }
+ }
+ }
+
+/* Escapes that need further processing, including those that are unknown, have
+a zero entry in the lookup table. When called from pcre2_substitute(), only \c,
+\o, and \x are recognized (\u and \U can never appear as they are used for case
+forcing). */
+
+else
+ {
+ int s;
+ PCRE2_SPTR oldptr;
+ BOOL overflow;
+ BOOL alt_bsux =
+ ((options & PCRE2_ALT_BSUX) | (extra_options & PCRE2_EXTRA_ALT_BSUX)) != 0;
+
+ /* Filter calls from pcre2_substitute(). */
+
+ if (cb == NULL)
+ {
+ if (c != CHAR_c && c != CHAR_o && c != CHAR_x)
+ {
+ *errorcodeptr = ERR3;
+ return 0;
+ }
+ alt_bsux = FALSE; /* Do not modify \x handling */
+ }
+
+ switch (c)
+ {
+ /* A number of Perl escapes are not handled by PCRE. We give an explicit
+ error. */
+
+ case CHAR_F:
+ case CHAR_l:
+ case CHAR_L:
+ *errorcodeptr = ERR37;
+ break;
+
+ /* \u is unrecognized when neither PCRE2_ALT_BSUX nor PCRE2_EXTRA_ALT_BSUX
+ is set. Otherwise, \u must be followed by exactly four hex digits or, if
+ PCRE2_EXTRA_ALT_BSUX is set, by any number of hex digits in braces.
+ Otherwise it is a lowercase u letter. This gives some compatibility with
+ ECMAScript (aka JavaScript). */
+
+ case CHAR_u:
+ if (!alt_bsux) *errorcodeptr = ERR37; else
+ {
+ uint32_t xc;
+
+ if (ptr >= ptrend) break;
+ if (*ptr == CHAR_LEFT_CURLY_BRACKET &&
+ (extra_options & PCRE2_EXTRA_ALT_BSUX) != 0)
+ {
+ PCRE2_SPTR hptr = ptr + 1;
+ cc = 0;
+
+ while (hptr < ptrend && (xc = XDIGIT(*hptr)) != 0xff)
+ {
+ if ((cc & 0xf0000000) != 0) /* Test for 32-bit overflow */
+ {
+ *errorcodeptr = ERR77;
+ ptr = hptr; /* Show where */
+ break; /* *hptr != } will cause another break below */
+ }
+ cc = (cc << 4) | xc;
+ hptr++;
+ }
+
+ if (hptr == ptr + 1 || /* No hex digits */
+ hptr >= ptrend || /* Hit end of input */
+ *hptr != CHAR_RIGHT_CURLY_BRACKET) /* No } terminator */
+ break; /* Hex escape not recognized */
+
+ c = cc; /* Accept the code point */
+ ptr = hptr + 1;
+ }
+
+ else /* Must be exactly 4 hex digits */
+ {
+ if (ptrend - ptr < 4) break; /* Less than 4 chars */
+ if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */
+ if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */
+ cc = (cc << 4) | xc;
+ if ((xc = XDIGIT(ptr[2])) == 0xff) break; /* Not a hex digit */
+ cc = (cc << 4) | xc;
+ if ((xc = XDIGIT(ptr[3])) == 0xff) break; /* Not a hex digit */
+ c = (cc << 4) | xc;
+ ptr += 4;
+ }
+
+ if (utf)
+ {
+ if (c > 0x10ffffU) *errorcodeptr = ERR77;
+ else
+ if (c >= 0xd800 && c <= 0xdfff &&
+ (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)
+ *errorcodeptr = ERR73;
+ }
+ else if (c > MAX_NON_UTF_CHAR) *errorcodeptr = ERR77;
+ }
+ break;
+
+ /* \U is unrecognized unless PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set,
+ in which case it is an upper case letter. */
+
+ case CHAR_U:
+ if (!alt_bsux) *errorcodeptr = ERR37;
+ break;
+
+ /* In a character class, \g is just a literal "g". Outside a character
+ class, \g must be followed by one of a number of specific things:
+
+ (1) A number, either plain or braced. If positive, it is an absolute
+ backreference. If negative, it is a relative backreference. This is a Perl
+ 5.10 feature.
+
+ (2) Perl 5.10 also supports \g{name} as a reference to a named group. This
+ is part of Perl's movement towards a unified syntax for back references. As
+ this is synonymous with \k{name}, we fudge it up by pretending it really
+ was \k{name}.
+
+ (3) For Oniguruma compatibility we also support \g followed by a name or a
+ number either in angle brackets or in single quotes. However, these are
+ (possibly recursive) subroutine calls, _not_ backreferences. We return
+ the ESC_g code.
+
+ Summary: Return a negative number for a numerical back reference, ESC_k for
+ a named back reference, and ESC_g for a named or numbered subroutine call.
+ */
+
+ case CHAR_g:
+ if (isclass) break;
+
+ if (ptr >= ptrend)
+ {
+ *errorcodeptr = ERR57;
+ break;
+ }
+
+ if (*ptr == CHAR_LESS_THAN_SIGN || *ptr == CHAR_APOSTROPHE)
+ {
+ escape = ESC_g;
+ break;
+ }
+
+ /* If there is a brace delimiter, try to read a numerical reference. If
+ there isn't one, assume we have a name and treat it as \k. */
+
+ if (*ptr == CHAR_LEFT_CURLY_BRACKET)
+ {
+ PCRE2_SPTR p = ptr + 1;
+ if (!read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s,
+ errorcodeptr))
+ {
+ if (*errorcodeptr == 0) escape = ESC_k; /* No number found */
+ break;
+ }
+ if (p >= ptrend || *p != CHAR_RIGHT_CURLY_BRACKET)
+ {
+ *errorcodeptr = ERR57;
+ break;
+ }
+ ptr = p + 1;
+ }
+
+ /* Read an undelimited number */
+
+ else
+ {
+ if (!read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &s,
+ errorcodeptr))
+ {
+ if (*errorcodeptr == 0) *errorcodeptr = ERR57; /* No number found */
+ break;
+ }
+ }
+
+ if (s <= 0)
+ {
+ *errorcodeptr = ERR15;
+ break;
+ }
+
+ escape = -s;
+ break;
+
+ /* The handling of escape sequences consisting of a string of digits
+ starting with one that is not zero is not straightforward. Perl has changed
+ over the years. Nowadays \g{} for backreferences and \o{} for octal are
+ recommended to avoid the ambiguities in the old syntax.
+
+ Outside a character class, the digits are read as a decimal number. If the
+ number is less than 10, or if there are that many previous extracting left
+ brackets, it is a back reference. Otherwise, up to three octal digits are
+ read to form an escaped character code. Thus \123 is likely to be octal 123
+ (cf \0123, which is octal 012 followed by the literal 3).
+
+ Inside a character class, \ followed by a digit is always either a literal
+ 8 or 9 or an octal number. */
+
+ case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5:
+ case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9:
+
+ if (!isclass)
+ {
+ oldptr = ptr;
+ ptr--; /* Back to the digit */
+
+ /* As we know we are at a digit, the only possible error from
+ read_number() is a number that is too large to be a group number. In this
+ case we fall through handle this as not a group reference. If we have
+ read a small enough number, check for a back reference.
+
+ \1 to \9 are always back references. \8x and \9x are too; \1x to \7x
+ are octal escapes if there are not that many previous captures. */
+
+ if (read_number(&ptr, ptrend, -1, INT_MAX/10 - 1, 0, &s, errorcodeptr) &&
+ (s < 10 || oldptr[-1] >= CHAR_8 || s <= (int)cb->bracount))
+ {
+ if (s > (int)MAX_GROUP_NUMBER) *errorcodeptr = ERR61;
+ else escape = -s; /* Indicates a back reference */
+ break;
+ }
+
+ ptr = oldptr; /* Put the pointer back and fall through */
+ }
+
+ /* Handle a digit following \ when the number is not a back reference, or
+ we are within a character class. If the first digit is 8 or 9, Perl used to
+ generate a binary zero and then treat the digit as a following literal. At
+ least by Perl 5.18 this changed so as not to insert the binary zero. */
+
+ if (c >= CHAR_8) break;
+
+ /* Fall through */
+
+ /* \0 always starts an octal number, but we may drop through to here with a
+ larger first octal digit. The original code used just to take the least
+ significant 8 bits of octal numbers (I think this is what early Perls used
+ to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode,
+ but no more than 3 octal digits. */
+
+ case CHAR_0:
+ c -= CHAR_0;
+ while(i++ < 2 && ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7)
+ c = c * 8 + *ptr++ - CHAR_0;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (!utf && c > 0xff) *errorcodeptr = ERR51;
+#endif
+ break;
+
+ /* \o is a relatively new Perl feature, supporting a more general way of
+ specifying character codes in octal. The only supported form is \o{ddd}. */
+
+ case CHAR_o:
+ if (ptr >= ptrend || *ptr++ != CHAR_LEFT_CURLY_BRACKET)
+ {
+ ptr--;
+ *errorcodeptr = ERR55;
+ }
+ else if (ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET)
+ *errorcodeptr = ERR78;
+ else
+ {
+ c = 0;
+ overflow = FALSE;
+ while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7)
+ {
+ cc = *ptr++;
+ if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (c >= 0x20000000l) { overflow = TRUE; break; }
+#endif
+ c = (c << 3) + (cc - CHAR_0);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (c > (utf ? 0x10ffffU : 0xffU)) { overflow = TRUE; break; }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ if (c > (utf ? 0x10ffffU : 0xffffU)) { overflow = TRUE; break; }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ if (utf && c > 0x10ffffU) { overflow = TRUE; break; }
+#endif
+ }
+ if (overflow)
+ {
+ while (ptr < ptrend && *ptr >= CHAR_0 && *ptr <= CHAR_7) ptr++;
+ *errorcodeptr = ERR34;
+ }
+ else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ if (utf && c >= 0xd800 && c <= 0xdfff &&
+ (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)
+ {
+ ptr--;
+ *errorcodeptr = ERR73;
+ }
+ }
+ else
+ {
+ ptr--;
+ *errorcodeptr = ERR64;
+ }
+ }
+ break;
+
+ /* When PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set, \x must be followed
+ by two hexadecimal digits. Otherwise it is a lowercase x letter. */
+
+ case CHAR_x:
+ if (alt_bsux)
+ {
+ uint32_t xc;
+ if (ptrend - ptr < 2) break; /* Less than 2 characters */
+ if ((cc = XDIGIT(ptr[0])) == 0xff) break; /* Not a hex digit */
+ if ((xc = XDIGIT(ptr[1])) == 0xff) break; /* Not a hex digit */
+ c = (cc << 4) | xc;
+ ptr += 2;
+ }
+
+ /* Handle \x in Perl's style. \x{ddd} is a character code which can be
+ greater than 0xff in UTF-8 or non-8bit mode, but only if the ddd are hex
+ digits. If not, { used to be treated as a data character. However, Perl
+ seems to read hex digits up to the first non-such, and ignore the rest, so
+ that, for example \x{zz} matches a binary zero. This seems crazy, so PCRE
+ now gives an error. */
+
+ else
+ {
+ if (ptr < ptrend && *ptr == CHAR_LEFT_CURLY_BRACKET)
+ {
+#ifndef EBCDIC
+ COME_FROM_NU:
+#endif
+ if (++ptr >= ptrend || *ptr == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ *errorcodeptr = ERR78;
+ break;
+ }
+ c = 0;
+ overflow = FALSE;
+
+ while (ptr < ptrend && (cc = XDIGIT(*ptr)) != 0xff)
+ {
+ ptr++;
+ if (c == 0 && cc == 0) continue; /* Leading zeroes */
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (c >= 0x10000000l) { overflow = TRUE; break; }
+#endif
+ c = (c << 4) | cc;
+ if ((utf && c > 0x10ffffU) || (!utf && c > MAX_NON_UTF_CHAR))
+ {
+ overflow = TRUE;
+ break;
+ }
+ }
+
+ if (overflow)
+ {
+ while (ptr < ptrend && XDIGIT(*ptr) != 0xff) ptr++;
+ *errorcodeptr = ERR34;
+ }
+ else if (ptr < ptrend && *ptr++ == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ if (utf && c >= 0xd800 && c <= 0xdfff &&
+ (extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) == 0)
+ {
+ ptr--;
+ *errorcodeptr = ERR73;
+ }
+ }
+
+ /* If the sequence of hex digits does not end with '}', give an error.
+ We used just to recognize this construct and fall through to the normal
+ \x handling, but nowadays Perl gives an error, which seems much more
+ sensible, so we do too. */
+
+ else
+ {
+ ptr--;
+ *errorcodeptr = ERR67;
+ }
+ } /* End of \x{} processing */
+
+ /* Read a up to two hex digits after \x */
+
+ else
+ {
+ c = 0;
+ if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */
+ ptr++;
+ c = cc;
+ if (ptr >= ptrend || (cc = XDIGIT(*ptr)) == 0xff) break; /* Not a hex digit */
+ ptr++;
+ c = (c << 4) | cc;
+ } /* End of \xdd handling */
+ } /* End of Perl-style \x handling */
+ break;
+
+ /* The handling of \c is different in ASCII and EBCDIC environments. In an
+ ASCII (or Unicode) environment, an error is given if the character
+ following \c is not a printable ASCII character. Otherwise, the following
+ character is upper-cased if it is a letter, and after that the 0x40 bit is
+ flipped. The result is the value of the escape.
+
+ In an EBCDIC environment the handling of \c is compatible with the
+ specification in the perlebcdic document. The following character must be
+ a letter or one of small number of special characters. These provide a
+ means of defining the character values 0-31.
+
+ For testing the EBCDIC handling of \c in an ASCII environment, recognize
+ the EBCDIC value of 'c' explicitly. */
+
+#if defined EBCDIC && 'a' != 0x81
+ case 0x83:
+#else
+ case CHAR_c:
+#endif
+ if (ptr >= ptrend)
+ {
+ *errorcodeptr = ERR2;
+ break;
+ }
+ c = *ptr;
+ if (c >= CHAR_a && c <= CHAR_z) c = UPPER_CASE(c);
+
+ /* Handle \c in an ASCII/Unicode environment. */
+
+#ifndef EBCDIC /* ASCII/UTF-8 coding */
+ if (c < 32 || c > 126) /* Excludes all non-printable ASCII */
+ {
+ *errorcodeptr = ERR68;
+ break;
+ }
+ c ^= 0x40;
+
+ /* Handle \c in an EBCDIC environment. The special case \c? is converted to
+ 255 (0xff) or 95 (0x5f) if other characters suggest we are using the
+ POSIX-BC encoding. (This is the way Perl indicates that it handles \c?.)
+ The other valid sequences correspond to a list of specific characters. */
+
+#else
+ if (c == CHAR_QUESTION_MARK)
+ c = ('\\' == 188 && '`' == 74)? 0x5f : 0xff;
+ else
+ {
+ for (i = 0; i < 32; i++)
+ {
+ if (c == ebcdic_escape_c[i]) break;
+ }
+ if (i < 32) c = i; else *errorcodeptr = ERR68;
+ }
+#endif /* EBCDIC */
+
+ ptr++;
+ break;
+
+ /* Any other alphanumeric following \ is an error. Perl gives an error only
+ if in warning mode, but PCRE doesn't have a warning mode. */
+
+ default:
+ *errorcodeptr = ERR3;
+ *ptrptr = ptr - 1; /* Point to the character at fault */
+ return 0;
+ }
+ }
+
+/* Set the pointer to the next character before returning. */
+
+*ptrptr = ptr;
+*chptr = c;
+return escape;
+}
+
+
+
+#ifdef SUPPORT_UNICODE
+/*************************************************
+* Handle \P and \p *
+*************************************************/
+
+/* This function is called after \P or \p has been encountered, provided that
+PCRE2 is compiled with support for UTF and Unicode properties. On entry, the
+contents of ptrptr are pointing after the P or p. On exit, it is left pointing
+after the final code unit of the escape sequence.
+
+Arguments:
+ ptrptr the pattern position pointer
+ negptr a boolean that is set TRUE for negation else FALSE
+ ptypeptr an unsigned int that is set to the type value
+ pdataptr an unsigned int that is set to the detailed property value
+ errorcodeptr the error code variable
+ cb the compile data
+
+Returns: TRUE if the type value was found, or FALSE for an invalid type
+*/
+
+static BOOL
+get_ucp(PCRE2_SPTR *ptrptr, BOOL *negptr, uint16_t *ptypeptr,
+ uint16_t *pdataptr, int *errorcodeptr, compile_block *cb)
+{
+PCRE2_UCHAR c;
+PCRE2_SIZE i, bot, top;
+PCRE2_SPTR ptr = *ptrptr;
+PCRE2_UCHAR name[50];
+PCRE2_UCHAR *vptr = NULL;
+uint16_t ptscript = PT_NOTSCRIPT;
+
+if (ptr >= cb->end_pattern) goto ERROR_RETURN;
+c = *ptr++;
+*negptr = FALSE;
+
+/* \P or \p can be followed by a name in {}, optionally preceded by ^ for
+negation. */
+
+if (c == CHAR_LEFT_CURLY_BRACKET)
+ {
+ if (ptr >= cb->end_pattern) goto ERROR_RETURN;
+
+ if (*ptr == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ *negptr = TRUE;
+ ptr++;
+ }
+
+ for (i = 0; i < (int)(sizeof(name) / sizeof(PCRE2_UCHAR)) - 1; i++)
+ {
+ if (ptr >= cb->end_pattern) goto ERROR_RETURN;
+ c = *ptr++;
+ while (c == '_' || c == '-' || isspace(c))
+ {
+ if (ptr >= cb->end_pattern) goto ERROR_RETURN;
+ c = *ptr++;
+ }
+ if (c == CHAR_NUL) goto ERROR_RETURN;
+ if (c == CHAR_RIGHT_CURLY_BRACKET) break;
+ name[i] = tolower(c);
+ if ((c == ':' || c == '=') && vptr == NULL) vptr = name + i;
+ }
+
+ if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN;
+ name[i] = 0;
+ }
+
+/* If { doesn't follow \p or \P there is just one following character, which
+must be an ASCII letter. */
+
+else if (MAX_255(c) && (cb->ctypes[c] & ctype_letter) != 0)
+ {
+ name[0] = tolower(c);
+ name[1] = 0;
+ }
+else goto ERROR_RETURN;
+
+*ptrptr = ptr;
+
+/* If the property contains ':' or '=' we have class name and value separately
+specified. The following are supported:
+
+ . Bidi_Class (synonym bc), for which the property names are "bidi<name>".
+ . Script (synonym sc) for which the property name is the script name
+ . Script_Extensions (synonym scx), ditto
+
+As this is a small number, we currently just check the names directly. If this
+grows, a sorted table and a switch will be neater.
+
+For both the script properties, set a PT_xxx value so that (1) they can be
+distinguished and (2) invalid script names that happen to be the name of
+another property can be diagnosed. */
+
+if (vptr != NULL)
+ {
+ int offset = 0;
+ PCRE2_UCHAR sname[8];
+
+ *vptr = 0; /* Terminate property name */
+ if (PRIV(strcmp_c8)(name, STRING_bidiclass) == 0 ||
+ PRIV(strcmp_c8)(name, STRING_bc) == 0)
+ {
+ offset = 4;
+ sname[0] = CHAR_b;
+ sname[1] = CHAR_i; /* There is no strcpy_c8 function */
+ sname[2] = CHAR_d;
+ sname[3] = CHAR_i;
+ }
+
+ else if (PRIV(strcmp_c8)(name, STRING_script) == 0 ||
+ PRIV(strcmp_c8)(name, STRING_sc) == 0)
+ ptscript = PT_SC;
+
+ else if (PRIV(strcmp_c8)(name, STRING_scriptextensions) == 0 ||
+ PRIV(strcmp_c8)(name, STRING_scx) == 0)
+ ptscript = PT_SCX;
+
+ else
+ {
+ *errorcodeptr = ERR47;
+ return FALSE;
+ }
+
+ /* Adjust the string in name[] as needed */
+
+ memmove(name + offset, vptr + 1, (name + i - vptr)*sizeof(PCRE2_UCHAR));
+ if (offset != 0) memmove(name, sname, offset*sizeof(PCRE2_UCHAR));
+ }
+
+/* Search for a recognized property using binary chop. */
+
+bot = 0;
+top = PRIV(utt_size);
+
+while (bot < top)
+ {
+ int r;
+ i = (bot + top) >> 1;
+ r = PRIV(strcmp_c8)(name, PRIV(utt_names) + PRIV(utt)[i].name_offset);
+
+ /* When a matching property is found, some extra checking is needed when the
+ \p{xx:yy} syntax is used and xx is either sc or scx. */
+
+ if (r == 0)
+ {
+ *pdataptr = PRIV(utt)[i].value;
+ if (vptr == NULL || ptscript == PT_NOTSCRIPT)
+ {
+ *ptypeptr = PRIV(utt)[i].type;
+ return TRUE;
+ }
+
+ switch (PRIV(utt)[i].type)
+ {
+ case PT_SC:
+ *ptypeptr = PT_SC;
+ return TRUE;
+
+ case PT_SCX:
+ *ptypeptr = ptscript;
+ return TRUE;
+ }
+
+ break; /* Non-script found */
+ }
+
+ if (r > 0) bot = i + 1; else top = i;
+ }
+
+*errorcodeptr = ERR47; /* Unrecognized property */
+return FALSE;
+
+ERROR_RETURN: /* Malformed \P or \p */
+*errorcodeptr = ERR46;
+*ptrptr = ptr;
+return FALSE;
+}
+#endif
+
+
+
+/*************************************************
+* Check for POSIX class syntax *
+*************************************************/
+
+/* This function is called when the sequence "[:" or "[." or "[=" is
+encountered in a character class. It checks whether this is followed by a
+sequence of characters terminated by a matching ":]" or ".]" or "=]". If we
+reach an unescaped ']' without the special preceding character, return FALSE.
+
+Originally, this function only recognized a sequence of letters between the
+terminators, but it seems that Perl recognizes any sequence of characters,
+though of course unknown POSIX names are subsequently rejected. Perl gives an
+"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE
+didn't consider this to be a POSIX class. Likewise for [:1234:].
+
+The problem in trying to be exactly like Perl is in the handling of escapes. We
+have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX
+class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code
+below handles the special cases \\ and \], but does not try to do any other
+escape processing. This makes it different from Perl for cases such as
+[:l\ower:] where Perl recognizes it as the POSIX class "lower" but PCRE does
+not recognize "l\ower". This is a lesser evil than not diagnosing bad classes
+when Perl does, I think.
+
+A user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not.
+It seems that the appearance of a nested POSIX class supersedes an apparent
+external class. For example, [:a[:digit:]b:] matches "a", "b", ":", or
+a digit. This is handled by returning FALSE if the start of a new group with
+the same terminator is encountered, since the next closing sequence must close
+the nested group, not the outer one.
+
+In Perl, unescaped square brackets may also appear as part of class names. For
+example, [:a[:abc]b:] gives unknown POSIX class "[:abc]b:]". However, for
+[:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not
+seem right at all. PCRE does not allow closing square brackets in POSIX class
+names.
+
+Arguments:
+ ptr pointer to the character after the initial [ (colon, dot, equals)
+ ptrend pointer to the end of the pattern
+ endptr where to return a pointer to the terminating ':', '.', or '='
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+check_posix_syntax(PCRE2_SPTR ptr, PCRE2_SPTR ptrend, PCRE2_SPTR *endptr)
+{
+PCRE2_UCHAR terminator; /* Don't combine these lines; the Solaris cc */
+terminator = *ptr++; /* compiler warns about "non-constant" initializer. */
+
+for (; ptrend - ptr >= 2; ptr++)
+ {
+ if (*ptr == CHAR_BACKSLASH &&
+ (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET || ptr[1] == CHAR_BACKSLASH))
+ ptr++;
+
+ else if ((*ptr == CHAR_LEFT_SQUARE_BRACKET && ptr[1] == terminator) ||
+ *ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE;
+
+ else if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ *endptr = ptr;
+ return TRUE;
+ }
+ }
+
+return FALSE;
+}
+
+
+
+/*************************************************
+* Check POSIX class name *
+*************************************************/
+
+/* This function is called to check the name given in a POSIX-style class entry
+such as [:alnum:].
+
+Arguments:
+ ptr points to the first letter
+ len the length of the name
+
+Returns: a value representing the name, or -1 if unknown
+*/
+
+static int
+check_posix_name(PCRE2_SPTR ptr, int len)
+{
+const char *pn = posix_names;
+int yield = 0;
+while (posix_name_lengths[yield] != 0)
+ {
+ if (len == posix_name_lengths[yield] &&
+ PRIV(strncmp_c8)(ptr, pn, (unsigned int)len) == 0) return yield;
+ pn += posix_name_lengths[yield] + 1;
+ yield++;
+ }
+return -1;
+}
+
+
+
+/*************************************************
+* Read a subpattern or VERB name *
+*************************************************/
+
+/* This function is called from parse_regex() below whenever it needs to read
+the name of a subpattern or a (*VERB) or an (*alpha_assertion). The initial
+pointer must be to the character before the name. If that character is '*' we
+are reading a verb or alpha assertion name. The pointer is updated to point
+after the name, for a VERB or alpha assertion name, or after tha name's
+terminator for a subpattern name. Returning both the offset and the name
+pointer is redundant information, but some callers use one and some the other,
+so it is simplest just to return both.
+
+Arguments:
+ ptrptr points to the character pointer variable
+ ptrend points to the end of the input string
+ utf true if the input is UTF-encoded
+ terminator the terminator of a subpattern name must be this
+ offsetptr where to put the offset from the start of the pattern
+ nameptr where to put a pointer to the name in the input
+ namelenptr where to put the length of the name
+ errcodeptr where to put an error code
+ cb pointer to the compile data block
+
+Returns: TRUE if a name was read
+ FALSE otherwise, with error code set
+*/
+
+static BOOL
+read_name(PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend, BOOL utf, uint32_t terminator,
+ PCRE2_SIZE *offsetptr, PCRE2_SPTR *nameptr, uint32_t *namelenptr,
+ int *errorcodeptr, compile_block *cb)
+{
+PCRE2_SPTR ptr = *ptrptr;
+BOOL is_group = (*ptr != CHAR_ASTERISK);
+
+if (++ptr >= ptrend) /* No characters in name */
+ {
+ *errorcodeptr = is_group? ERR62: /* Subpattern name expected */
+ ERR60; /* Verb not recognized or malformed */
+ goto FAILED;
+ }
+
+*nameptr = ptr;
+*offsetptr = (PCRE2_SIZE)(ptr - cb->start_pattern);
+
+/* In UTF mode, a group name may contain letters and decimal digits as defined
+by Unicode properties, and underscores, but must not start with a digit. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && is_group)
+ {
+ uint32_t c, type;
+
+ GETCHAR(c, ptr);
+ type = UCD_CHARTYPE(c);
+
+ if (type == ucp_Nd)
+ {
+ *errorcodeptr = ERR44;
+ goto FAILED;
+ }
+
+ for(;;)
+ {
+ if (type != ucp_Nd && PRIV(ucp_gentype)[type] != ucp_L &&
+ c != CHAR_UNDERSCORE) break;
+ ptr++;
+ FORWARDCHARTEST(ptr, ptrend);
+ if (ptr >= ptrend) break;
+ GETCHAR(c, ptr);
+ type = UCD_CHARTYPE(c);
+ }
+ }
+else
+#else
+(void)utf; /* Avoid compiler warning */
+#endif /* SUPPORT_UNICODE */
+
+/* Handle non-group names and group names in non-UTF modes. A group name must
+not start with a digit. If either of the others start with a digit it just
+won't be recognized. */
+
+ {
+ if (is_group && IS_DIGIT(*ptr))
+ {
+ *errorcodeptr = ERR44;
+ goto FAILED;
+ }
+
+ while (ptr < ptrend && MAX_255(*ptr) && (cb->ctypes[*ptr] & ctype_word) != 0)
+ {
+ ptr++;
+ }
+ }
+
+/* Check name length */
+
+if (ptr > *nameptr + MAX_NAME_SIZE)
+ {
+ *errorcodeptr = ERR48;
+ goto FAILED;
+ }
+*namelenptr = (uint32_t)(ptr - *nameptr);
+
+/* Subpattern names must not be empty, and their terminator is checked here.
+(What follows a verb or alpha assertion name is checked separately.) */
+
+if (is_group)
+ {
+ if (ptr == *nameptr)
+ {
+ *errorcodeptr = ERR62; /* Subpattern name expected */
+ goto FAILED;
+ }
+ if (ptr >= ptrend || *ptr != (PCRE2_UCHAR)terminator)
+ {
+ *errorcodeptr = ERR42;
+ goto FAILED;
+ }
+ ptr++;
+ }
+
+*ptrptr = ptr;
+return TRUE;
+
+FAILED:
+*ptrptr = ptr;
+return FALSE;
+}
+
+
+
+/*************************************************
+* Manage callouts at start of cycle *
+*************************************************/
+
+/* At the start of a new item in parse_regex() we are able to record the
+details of the previous item in a prior callout, and also to set up an
+automatic callout if enabled. Avoid having two adjacent automatic callouts,
+which would otherwise happen for items such as \Q that contribute nothing to
+the parsed pattern.
+
+Arguments:
+ ptr current pattern pointer
+ pcalloutptr points to a pointer to previous callout, or NULL
+ auto_callout TRUE if auto_callouts are enabled
+ parsed_pattern the parsed pattern pointer
+ cb compile block
+
+Returns: possibly updated parsed_pattern pointer.
+*/
+
+static uint32_t *
+manage_callouts(PCRE2_SPTR ptr, uint32_t **pcalloutptr, BOOL auto_callout,
+ uint32_t *parsed_pattern, compile_block *cb)
+{
+uint32_t *previous_callout = *pcalloutptr;
+
+if (previous_callout != NULL) previous_callout[2] = (uint32_t)(ptr -
+ cb->start_pattern - (PCRE2_SIZE)previous_callout[1]);
+
+if (!auto_callout) previous_callout = NULL; else
+ {
+ if (previous_callout == NULL ||
+ previous_callout != parsed_pattern - 4 ||
+ previous_callout[3] != 255)
+ {
+ previous_callout = parsed_pattern; /* Set up new automatic callout */
+ parsed_pattern += 4;
+ previous_callout[0] = META_CALLOUT_NUMBER;
+ previous_callout[2] = 0;
+ previous_callout[3] = 255;
+ }
+ previous_callout[1] = (uint32_t)(ptr - cb->start_pattern);
+ }
+
+*pcalloutptr = previous_callout;
+return parsed_pattern;
+}
+
+
+
+/*************************************************
+* Parse regex and identify named groups *
+*************************************************/
+
+/* This function is called first of all. It scans the pattern and does two
+things: (1) It identifies capturing groups and makes a table of named capturing
+groups so that information about them is fully available to both the compiling
+scans. (2) It writes a parsed version of the pattern with comments omitted and
+escapes processed into the parsed_pattern vector.
+
+Arguments:
+ ptr points to the start of the pattern
+ options compiling dynamic options (may change during the scan)
+ has_lookbehind points to a boolean, set TRUE if a lookbehind is found
+ cb pointer to the compile data block
+
+Returns: zero on success or a non-zero error code, with the
+ error offset placed in the cb field
+*/
+
+/* A structure and some flags for dealing with nested groups. */
+
+typedef struct nest_save {
+ uint16_t nest_depth;
+ uint16_t reset_group;
+ uint16_t max_group;
+ uint16_t flags;
+ uint32_t options;
+} nest_save;
+
+#define NSF_RESET 0x0001u
+#define NSF_CONDASSERT 0x0002u
+#define NSF_ATOMICSR 0x0004u
+
+/* Options that are changeable within the pattern must be tracked during
+parsing. Some (e.g. PCRE2_EXTENDED) are implemented entirely during parsing,
+but all must be tracked so that META_OPTIONS items set the correct values for
+the main compiling phase. */
+
+#define PARSE_TRACKED_OPTIONS (PCRE2_CASELESS|PCRE2_DOTALL|PCRE2_DUPNAMES| \
+ PCRE2_EXTENDED|PCRE2_EXTENDED_MORE|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE| \
+ PCRE2_UNGREEDY)
+
+/* States used for analyzing ranges in character classes. The two OK values
+must be last. */
+
+enum { RANGE_NO, RANGE_STARTED, RANGE_OK_ESCAPED, RANGE_OK_LITERAL };
+
+/* Only in 32-bit mode can there be literals > META_END. A macro encapsulates
+the storing of literal values in the main parsed pattern, where they can always
+be quantified. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+#define PARSED_LITERAL(c, p) \
+ { \
+ if (c >= META_END) *p++ = META_BIGVALUE; \
+ *p++ = c; \
+ okquantifier = TRUE; \
+ }
+#else
+#define PARSED_LITERAL(c, p) *p++ = c; okquantifier = TRUE;
+#endif
+
+/* Here's the actual function. */
+
+static int parse_regex(PCRE2_SPTR ptr, uint32_t options, BOOL *has_lookbehind,
+ compile_block *cb)
+{
+uint32_t c;
+uint32_t delimiter;
+uint32_t namelen;
+uint32_t class_range_state;
+uint32_t *verblengthptr = NULL; /* Value avoids compiler warning */
+uint32_t *verbstartptr = NULL;
+uint32_t *previous_callout = NULL;
+uint32_t *parsed_pattern = cb->parsed_pattern;
+uint32_t *parsed_pattern_end = cb->parsed_pattern_end;
+uint32_t meta_quantifier = 0;
+uint32_t add_after_mark = 0;
+uint32_t extra_options = cb->cx->extra_options;
+uint16_t nest_depth = 0;
+int after_manual_callout = 0;
+int expect_cond_assert = 0;
+int errorcode = 0;
+int escape;
+int i;
+BOOL inescq = FALSE;
+BOOL inverbname = FALSE;
+BOOL utf = (options & PCRE2_UTF) != 0;
+BOOL auto_callout = (options & PCRE2_AUTO_CALLOUT) != 0;
+BOOL isdupname;
+BOOL negate_class;
+BOOL okquantifier = FALSE;
+PCRE2_SPTR thisptr;
+PCRE2_SPTR name;
+PCRE2_SPTR ptrend = cb->end_pattern;
+PCRE2_SPTR verbnamestart = NULL; /* Value avoids compiler warning */
+named_group *ng;
+nest_save *top_nest, *end_nests;
+
+/* Insert leading items for word and line matching (features provided for the
+benefit of pcre2grep). */
+
+if ((extra_options & PCRE2_EXTRA_MATCH_LINE) != 0)
+ {
+ *parsed_pattern++ = META_CIRCUMFLEX;
+ *parsed_pattern++ = META_NOCAPTURE;
+ }
+else if ((extra_options & PCRE2_EXTRA_MATCH_WORD) != 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + ESC_b;
+ *parsed_pattern++ = META_NOCAPTURE;
+ }
+
+/* If the pattern is actually a literal string, process it separately to avoid
+cluttering up the main loop. */
+
+if ((options & PCRE2_LITERAL) != 0)
+ {
+ while (ptr < ptrend)
+ {
+ if (parsed_pattern >= parsed_pattern_end)
+ {
+ errorcode = ERR63; /* Internal error (parsed pattern overflow) */
+ goto FAILED;
+ }
+ thisptr = ptr;
+ GETCHARINCTEST(c, ptr);
+ if (auto_callout)
+ parsed_pattern = manage_callouts(thisptr, &previous_callout,
+ auto_callout, parsed_pattern, cb);
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ goto PARSED_END;
+ }
+
+/* Process a real regex which may contain meta-characters. */
+
+top_nest = NULL;
+end_nests = (nest_save *)(cb->start_workspace + cb->workspace_size);
+
+/* The size of the nest_save structure might not be a factor of the size of the
+workspace. Therefore we must round down end_nests so as to correctly avoid
+creating a nest_save that spans the end of the workspace. */
+
+end_nests = (nest_save *)((char *)end_nests -
+ ((cb->workspace_size * sizeof(PCRE2_UCHAR)) % sizeof(nest_save)));
+
+/* PCRE2_EXTENDED_MORE implies PCRE2_EXTENDED */
+
+if ((options & PCRE2_EXTENDED_MORE) != 0) options |= PCRE2_EXTENDED;
+
+/* Now scan the pattern */
+
+while (ptr < ptrend)
+ {
+ int prev_expect_cond_assert;
+ uint32_t min_repeat = 0, max_repeat = 0;
+ uint32_t set, unset, *optset;
+ uint32_t terminator;
+ uint32_t prev_meta_quantifier;
+ BOOL prev_okquantifier;
+ PCRE2_SPTR tempptr;
+ PCRE2_SIZE offset;
+
+ if (parsed_pattern >= parsed_pattern_end)
+ {
+ errorcode = ERR63; /* Internal error (parsed pattern overflow) */
+ goto FAILED;
+ }
+
+ if (nest_depth > cb->cx->parens_nest_limit)
+ {
+ errorcode = ERR19;
+ goto FAILED; /* Parentheses too deeply nested */
+ }
+
+ /* Get next input character, save its position for callout handling. */
+
+ thisptr = ptr;
+ GETCHARINCTEST(c, ptr);
+
+ /* Copy quoted literals until \E, allowing for the possibility of automatic
+ callouts, except when processing a (*VERB) "name". */
+
+ if (inescq)
+ {
+ if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E)
+ {
+ inescq = FALSE;
+ ptr++; /* Skip E */
+ }
+ else
+ {
+ if (expect_cond_assert > 0) /* A literal is not allowed if we are */
+ { /* expecting a conditional assertion, */
+ ptr--; /* but an empty \Q\E sequence is OK. */
+ errorcode = ERR28;
+ goto FAILED;
+ }
+ if (inverbname)
+ { /* Don't use PARSED_LITERAL() because it */
+#if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */
+ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;
+#endif
+ *parsed_pattern++ = c;
+ }
+ else
+ {
+ if (after_manual_callout-- <= 0)
+ parsed_pattern = manage_callouts(thisptr, &previous_callout,
+ auto_callout, parsed_pattern, cb);
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ meta_quantifier = 0;
+ }
+ continue; /* Next character */
+ }
+
+ /* If we are processing the "name" part of a (*VERB:NAME) item, all
+ characters up to the closing parenthesis are literals except when
+ PCRE2_ALT_VERBNAMES is set. That causes backslash interpretation, but only \Q
+ and \E and escaped characters are allowed (no character types such as \d). If
+ PCRE2_EXTENDED is also set, we must ignore white space and # comments. Do
+ this by not entering the special (*VERB:NAME) processing - they are then
+ picked up below. Note that c is a character, not a code unit, so we must not
+ use MAX_255 to test its size because MAX_255 tests code units and is assumed
+ TRUE in 8-bit mode. */
+
+ if (inverbname &&
+ (
+ /* EITHER: not both options set */
+ ((options & (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) !=
+ (PCRE2_EXTENDED | PCRE2_ALT_VERBNAMES)) ||
+#ifdef SUPPORT_UNICODE
+ /* OR: character > 255 AND not Unicode Pattern White Space */
+ (c > 255 && (c|1) != 0x200f && (c|1) != 0x2029) ||
+#endif
+ /* OR: not a # comment or isspace() white space */
+ (c < 256 && c != CHAR_NUMBER_SIGN && (cb->ctypes[c] & ctype_space) == 0
+#ifdef SUPPORT_UNICODE
+ /* and not CHAR_NEL when Unicode is supported */
+ && c != CHAR_NEL
+#endif
+ )))
+ {
+ PCRE2_SIZE verbnamelength;
+
+ switch(c)
+ {
+ default: /* Don't use PARSED_LITERAL() because it */
+#if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */
+ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;
+#endif
+ *parsed_pattern++ = c;
+ break;
+
+ case CHAR_RIGHT_PARENTHESIS:
+ inverbname = FALSE;
+ /* This is the length in characters */
+ verbnamelength = (PCRE2_SIZE)(parsed_pattern - verblengthptr - 1);
+ /* But the limit on the length is in code units */
+ if (ptr - verbnamestart - 1 > (int)MAX_MARK)
+ {
+ ptr--;
+ errorcode = ERR76;
+ goto FAILED;
+ }
+ *verblengthptr = (uint32_t)verbnamelength;
+
+ /* If this name was on a verb such as (*ACCEPT) which does not continue,
+ a (*MARK) was generated for the name. We now add the original verb as the
+ next item. */
+
+ if (add_after_mark != 0)
+ {
+ *parsed_pattern++ = add_after_mark;
+ add_after_mark = 0;
+ }
+ break;
+
+ case CHAR_BACKSLASH:
+ if ((options & PCRE2_ALT_VERBNAMES) != 0)
+ {
+ escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,
+ cb->cx->extra_options, FALSE, cb);
+ if (errorcode != 0) goto FAILED;
+ }
+ else escape = 0; /* Treat all as literal */
+
+ switch(escape)
+ {
+ case 0: /* Don't use PARSED_LITERAL() because it */
+#if PCRE2_CODE_UNIT_WIDTH == 32 /* sets okquantifier. */
+ if (c >= META_END) *parsed_pattern++ = META_BIGVALUE;
+#endif
+ *parsed_pattern++ = c;
+ break;
+
+ case ESC_Q:
+ inescq = TRUE;
+ break;
+
+ case ESC_E: /* Ignore */
+ break;
+
+ default:
+ errorcode = ERR40; /* Invalid in verb name */
+ goto FAILED;
+ }
+ }
+ continue; /* Next character in pattern */
+ }
+
+ /* Not a verb name character. At this point we must process everything that
+ must not change the quantification state. This is mainly comments, but we
+ handle \Q and \E here as well, so that an item such as A\Q\E+ is treated as
+ A+, as in Perl. An isolated \E is ignored. */
+
+ if (c == CHAR_BACKSLASH && ptr < ptrend)
+ {
+ if (*ptr == CHAR_Q || *ptr == CHAR_E)
+ {
+ inescq = *ptr == CHAR_Q;
+ ptr++;
+ continue;
+ }
+ }
+
+ /* Skip over whitespace and # comments in extended mode. Note that c is a
+ character, not a code unit, so we must not use MAX_255 to test its size
+ because MAX_255 tests code units and is assumed TRUE in 8-bit mode. The
+ whitespace characters are those designated as "Pattern White Space" by
+ Unicode, which are the isspace() characters plus CHAR_NEL (newline), which is
+ U+0085 in Unicode, plus U+200E, U+200F, U+2028, and U+2029. These are a
+ subset of space characters that match \h and \v. */
+
+ if ((options & PCRE2_EXTENDED) != 0)
+ {
+ if (c < 256 && (cb->ctypes[c] & ctype_space) != 0) continue;
+#ifdef SUPPORT_UNICODE
+ if (c == CHAR_NEL || (c|1) == 0x200f || (c|1) == 0x2029) continue;
+#endif
+ if (c == CHAR_NUMBER_SIGN)
+ {
+ while (ptr < ptrend)
+ {
+ if (IS_NEWLINE(ptr)) /* For non-fixed-length newline cases, */
+ { /* IS_NEWLINE sets cb->nllen. */
+ ptr += cb->nllen;
+ break;
+ }
+ ptr++;
+#ifdef SUPPORT_UNICODE
+ if (utf) FORWARDCHARTEST(ptr, ptrend);
+#endif
+ }
+ continue; /* Next character in pattern */
+ }
+ }
+
+ /* Skip over bracketed comments */
+
+ if (c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 2 &&
+ ptr[0] == CHAR_QUESTION_MARK && ptr[1] == CHAR_NUMBER_SIGN)
+ {
+ while (++ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS);
+ if (ptr >= ptrend)
+ {
+ errorcode = ERR18; /* A special error for missing ) in a comment */
+ goto FAILED; /* to make it easier to debug. */
+ }
+ ptr++;
+ continue; /* Next character in pattern */
+ }
+
+ /* If the next item is not a quantifier, fill in length of any previous
+ callout and create an auto callout if required. */
+
+ if (c != CHAR_ASTERISK && c != CHAR_PLUS && c != CHAR_QUESTION_MARK &&
+ (c != CHAR_LEFT_CURLY_BRACKET ||
+ (tempptr = ptr,
+ !read_repeat_counts(&tempptr, ptrend, NULL, NULL, &errorcode))))
+ {
+ if (after_manual_callout-- <= 0)
+ parsed_pattern = manage_callouts(thisptr, &previous_callout, auto_callout,
+ parsed_pattern, cb);
+ }
+
+ /* If expect_cond_assert is 2, we have just passed (?( and are expecting an
+ assertion, possibly preceded by a callout. If the value is 1, we have just
+ had the callout and expect an assertion. There must be at least 3 more
+ characters in all cases. When expect_cond_assert is 2, we know that the
+ current character is an opening parenthesis, as otherwise we wouldn't be
+ here. However, when it is 1, we need to check, and it's easiest just to check
+ always. Note that expect_cond_assert may be negative, since all callouts just
+ decrement it. */
+
+ if (expect_cond_assert > 0)
+ {
+ BOOL ok = c == CHAR_LEFT_PARENTHESIS && ptrend - ptr >= 3 &&
+ (ptr[0] == CHAR_QUESTION_MARK || ptr[0] == CHAR_ASTERISK);
+ if (ok)
+ {
+ if (ptr[0] == CHAR_ASTERISK) /* New alpha assertion format, possibly */
+ {
+ ok = MAX_255(ptr[1]) && (cb->ctypes[ptr[1]] & ctype_lcletter) != 0;
+ }
+ else switch(ptr[1]) /* Traditional symbolic format */
+ {
+ case CHAR_C:
+ ok = expect_cond_assert == 2;
+ break;
+
+ case CHAR_EQUALS_SIGN:
+ case CHAR_EXCLAMATION_MARK:
+ break;
+
+ case CHAR_LESS_THAN_SIGN:
+ ok = ptr[2] == CHAR_EQUALS_SIGN || ptr[2] == CHAR_EXCLAMATION_MARK;
+ break;
+
+ default:
+ ok = FALSE;
+ }
+ }
+
+ if (!ok)
+ {
+ ptr--; /* Adjust error offset */
+ errorcode = ERR28;
+ goto FAILED;
+ }
+ }
+
+ /* Remember whether we are expecting a conditional assertion, and set the
+ default for this item. */
+
+ prev_expect_cond_assert = expect_cond_assert;
+ expect_cond_assert = 0;
+
+ /* Remember quantification status for the previous significant item, then set
+ default for this item. */
+
+ prev_okquantifier = okquantifier;
+ prev_meta_quantifier = meta_quantifier;
+ okquantifier = FALSE;
+ meta_quantifier = 0;
+
+ /* If the previous significant item was a quantifier, adjust the parsed code
+ if there is a following modifier. The base meta value is always followed by
+ the PLUS and QUERY values, in that order. We do this here rather than after
+ reading a quantifier so that intervening comments and /x whitespace can be
+ ignored without having to replicate code. */
+
+ if (prev_meta_quantifier != 0 && (c == CHAR_QUESTION_MARK || c == CHAR_PLUS))
+ {
+ parsed_pattern[(prev_meta_quantifier == META_MINMAX)? -3 : -1] =
+ prev_meta_quantifier + ((c == CHAR_QUESTION_MARK)?
+ 0x00020000u : 0x00010000u);
+ continue; /* Next character in pattern */
+ }
+
+
+ /* Process the next item in the main part of a pattern. */
+
+ switch(c)
+ {
+ default: /* Non-special character */
+ PARSED_LITERAL(c, parsed_pattern);
+ break;
+
+
+ /* ---- Escape sequence ---- */
+
+ case CHAR_BACKSLASH:
+ tempptr = ptr;
+ escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,
+ cb->cx->extra_options, FALSE, cb);
+ if (errorcode != 0)
+ {
+ ESCAPE_FAILED:
+ if ((extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0)
+ goto FAILED;
+ ptr = tempptr;
+ if (ptr >= ptrend) c = CHAR_BACKSLASH; else
+ {
+ GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
+ }
+ escape = 0; /* Treat as literal character */
+ }
+
+ /* The escape was a data escape or literal character. */
+
+ if (escape == 0)
+ {
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+
+ /* The escape was a back (or forward) reference. We keep the offset in
+ order to give a more useful diagnostic for a bad forward reference. For
+ references to groups numbered less than 10 we can't use more than two items
+ in parsed_pattern because they may be just two characters in the input (and
+ in a 64-bit world an offset may need two elements). So for them, the offset
+ of the first occurrent is held in a special vector. */
+
+ else if (escape < 0)
+ {
+ offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 1);
+ escape = -escape;
+ *parsed_pattern++ = META_BACKREF | (uint32_t)escape;
+ if (escape < 10)
+ {
+ if (cb->small_ref_offset[escape] == PCRE2_UNSET)
+ cb->small_ref_offset[escape] = offset;
+ }
+ else
+ {
+ PUTOFFSET(offset, parsed_pattern);
+ }
+ okquantifier = TRUE;
+ }
+
+ /* The escape was a character class such as \d etc. or other special
+ escape indicator such as \A or \X. Most of them generate just a single
+ parsed item, but \P and \p are followed by a 16-bit type and a 16-bit
+ value. They are supported only when Unicode is available. The type and
+ value are packed into a single 32-bit value so that the whole sequences
+ uses only two elements in the parsed_vector. This is because the same
+ coding is used if \d (for example) is turned into \p{Nd} when PCRE2_UCP is
+ set.
+
+ There are also some cases where the escape sequence is followed by a name:
+ \k{name}, \k<name>, and \k'name' are backreferences by name, and \g<name>
+ and \g'name' are subroutine calls by name; \g{name} is a synonym for
+ \k{name}. Note that \g<number> and \g'number' are handled by check_escape()
+ and returned as a negative value (handled above). A name is coded as an
+ offset into the pattern and a length. */
+
+ else switch (escape)
+ {
+ case ESC_C:
+#ifdef NEVER_BACKSLASH_C
+ errorcode = ERR85;
+ goto ESCAPE_FAILED;
+#else
+ if ((options & PCRE2_NEVER_BACKSLASH_C) != 0)
+ {
+ errorcode = ERR83;
+ goto ESCAPE_FAILED;
+ }
+#endif
+ okquantifier = TRUE;
+ *parsed_pattern++ = META_ESCAPE + escape;
+ break;
+
+ case ESC_X:
+#ifndef SUPPORT_UNICODE
+ errorcode = ERR45; /* Supported only with Unicode support */
+ goto ESCAPE_FAILED;
+#endif
+ case ESC_H:
+ case ESC_h:
+ case ESC_N:
+ case ESC_R:
+ case ESC_V:
+ case ESC_v:
+ okquantifier = TRUE;
+ *parsed_pattern++ = META_ESCAPE + escape;
+ break;
+
+ default: /* \A, \B, \b, \G, \K, \Z, \z cannot be quantified. */
+ *parsed_pattern++ = META_ESCAPE + escape;
+ break;
+
+ /* Escapes that change in UCP mode. Note that PCRE2_UCP will never be set
+ without Unicode support because it is checked when pcre2_compile() is
+ called. */
+
+ case ESC_d:
+ case ESC_D:
+ case ESC_s:
+ case ESC_S:
+ case ESC_w:
+ case ESC_W:
+ okquantifier = TRUE;
+ if ((options & PCRE2_UCP) == 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + escape;
+ }
+ else
+ {
+ *parsed_pattern++ = META_ESCAPE +
+ ((escape == ESC_d || escape == ESC_s || escape == ESC_w)?
+ ESC_p : ESC_P);
+ switch(escape)
+ {
+ case ESC_d:
+ case ESC_D:
+ *parsed_pattern++ = (PT_PC << 16) | ucp_Nd;
+ break;
+
+ case ESC_s:
+ case ESC_S:
+ *parsed_pattern++ = PT_SPACE << 16;
+ break;
+
+ case ESC_w:
+ case ESC_W:
+ *parsed_pattern++ = PT_WORD << 16;
+ break;
+ }
+ }
+ break;
+
+ /* Unicode property matching */
+
+ case ESC_P:
+ case ESC_p:
+#ifdef SUPPORT_UNICODE
+ {
+ BOOL negated;
+ uint16_t ptype = 0, pdata = 0;
+ if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb))
+ goto ESCAPE_FAILED;
+ if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P;
+ *parsed_pattern++ = META_ESCAPE + escape;
+ *parsed_pattern++ = (ptype << 16) | pdata;
+ okquantifier = TRUE;
+ }
+#else
+ errorcode = ERR45;
+ goto ESCAPE_FAILED;
+#endif
+ break; /* End \P and \p */
+
+ /* When \g is used with quotes or angle brackets as delimiters, it is a
+ numerical or named subroutine call, and control comes here. When used
+ with brace delimiters it is a numberical back reference and does not come
+ here because check_escape() returns it directly as a reference. \k is
+ always a named back reference. */
+
+ case ESC_g:
+ case ESC_k:
+ if (ptr >= ptrend || (*ptr != CHAR_LEFT_CURLY_BRACKET &&
+ *ptr != CHAR_LESS_THAN_SIGN && *ptr != CHAR_APOSTROPHE))
+ {
+ errorcode = (escape == ESC_g)? ERR57 : ERR69;
+ goto ESCAPE_FAILED;
+ }
+ terminator = (*ptr == CHAR_LESS_THAN_SIGN)?
+ CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)?
+ CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET;
+
+ /* For a non-braced \g, check for a numerical recursion. */
+
+ if (escape == ESC_g && terminator != CHAR_RIGHT_CURLY_BRACKET)
+ {
+ PCRE2_SPTR p = ptr + 1;
+
+ if (read_number(&p, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i,
+ &errorcode))
+ {
+ if (p >= ptrend || *p != terminator)
+ {
+ errorcode = ERR57;
+ goto ESCAPE_FAILED;
+ }
+ ptr = p;
+ goto SET_RECURSION;
+ }
+ if (errorcode != 0) goto ESCAPE_FAILED;
+ }
+
+ /* Not a numerical recursion */
+
+ if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,
+ &errorcode, cb)) goto ESCAPE_FAILED;
+
+ /* \k and \g when used with braces are back references, whereas \g used
+ with quotes or angle brackets is a recursion */
+
+ *parsed_pattern++ =
+ (escape == ESC_k || terminator == CHAR_RIGHT_CURLY_BRACKET)?
+ META_BACKREF_BYNAME : META_RECURSE_BYNAME;
+ *parsed_pattern++ = namelen;
+
+ PUTOFFSET(offset, parsed_pattern);
+ okquantifier = TRUE;
+ break; /* End special escape processing */
+ }
+ break; /* End escape sequence processing */
+
+
+ /* ---- Single-character special items ---- */
+
+ case CHAR_CIRCUMFLEX_ACCENT:
+ *parsed_pattern++ = META_CIRCUMFLEX;
+ break;
+
+ case CHAR_DOLLAR_SIGN:
+ *parsed_pattern++ = META_DOLLAR;
+ break;
+
+ case CHAR_DOT:
+ *parsed_pattern++ = META_DOT;
+ okquantifier = TRUE;
+ break;
+
+
+ /* ---- Single-character quantifiers ---- */
+
+ case CHAR_ASTERISK:
+ meta_quantifier = META_ASTERISK;
+ goto CHECK_QUANTIFIER;
+
+ case CHAR_PLUS:
+ meta_quantifier = META_PLUS;
+ goto CHECK_QUANTIFIER;
+
+ case CHAR_QUESTION_MARK:
+ meta_quantifier = META_QUERY;
+ goto CHECK_QUANTIFIER;
+
+
+ /* ---- Potential {n,m} quantifier ---- */
+
+ case CHAR_LEFT_CURLY_BRACKET:
+ if (!read_repeat_counts(&ptr, ptrend, &min_repeat, &max_repeat,
+ &errorcode))
+ {
+ if (errorcode != 0) goto FAILED; /* Error in quantifier. */
+ PARSED_LITERAL(c, parsed_pattern); /* Not a quantifier */
+ break; /* No more quantifier processing */
+ }
+ meta_quantifier = META_MINMAX;
+ /* Fall through */
+
+
+ /* ---- Quantifier post-processing ---- */
+
+ /* Check that a quantifier is allowed after the previous item. */
+
+ CHECK_QUANTIFIER:
+ if (!prev_okquantifier)
+ {
+ errorcode = ERR9;
+ goto FAILED_BACK;
+ }
+
+ /* Most (*VERB)s are not allowed to be quantified, but an ungreedy
+ quantifier can be useful for (*ACCEPT) - meaning "succeed on backtrack", a
+ sort of negated (*COMMIT). We therefore allow (*ACCEPT) to be quantified by
+ wrapping it in non-capturing brackets, but we have to allow for a preceding
+ (*MARK) for when (*ACCEPT) has an argument. */
+
+ if (parsed_pattern[-1] == META_ACCEPT)
+ {
+ uint32_t *p;
+ for (p = parsed_pattern - 1; p >= verbstartptr; p--) p[1] = p[0];
+ *verbstartptr = META_NOCAPTURE;
+ parsed_pattern[1] = META_KET;
+ parsed_pattern += 2;
+ }
+
+ /* Now we can put the quantifier into the parsed pattern vector. At this
+ stage, we have only the basic quantifier. The check for a following + or ?
+ modifier happens at the top of the loop, after any intervening comments
+ have been removed. */
+
+ *parsed_pattern++ = meta_quantifier;
+ if (c == CHAR_LEFT_CURLY_BRACKET)
+ {
+ *parsed_pattern++ = min_repeat;
+ *parsed_pattern++ = max_repeat;
+ }
+ break;
+
+
+ /* ---- Character class ---- */
+
+ case CHAR_LEFT_SQUARE_BRACKET:
+ okquantifier = TRUE;
+
+ /* In another (POSIX) regex library, the ugly syntax [[:<:]] and [[:>:]] is
+ used for "start of word" and "end of word". As these are otherwise illegal
+ sequences, we don't break anything by recognizing them. They are replaced
+ by \b(?=\w) and \b(?<=\w) respectively. Sequences like [a[:<:]] are
+ erroneous and are handled by the normal code below. */
+
+ if (ptrend - ptr >= 6 &&
+ (PRIV(strncmp_c8)(ptr, STRING_WEIRD_STARTWORD, 6) == 0 ||
+ PRIV(strncmp_c8)(ptr, STRING_WEIRD_ENDWORD, 6) == 0))
+ {
+ *parsed_pattern++ = META_ESCAPE + ESC_b;
+
+ if (ptr[2] == CHAR_LESS_THAN_SIGN)
+ {
+ *parsed_pattern++ = META_LOOKAHEAD;
+ }
+ else
+ {
+ *parsed_pattern++ = META_LOOKBEHIND;
+ *has_lookbehind = TRUE;
+
+ /* The offset is used only for the "non-fixed length" error; this won't
+ occur here, so just store zero. */
+
+ PUTOFFSET((PCRE2_SIZE)0, parsed_pattern);
+ }
+
+ if ((options & PCRE2_UCP) == 0)
+ *parsed_pattern++ = META_ESCAPE + ESC_w;
+ else
+ {
+ *parsed_pattern++ = META_ESCAPE + ESC_p;
+ *parsed_pattern++ = PT_WORD << 16;
+ }
+ *parsed_pattern++ = META_KET;
+ ptr += 6;
+ break;
+ }
+
+ /* PCRE supports POSIX class stuff inside a class. Perl gives an error if
+ they are encountered at the top level, so we'll do that too. */
+
+ if (ptr < ptrend && (*ptr == CHAR_COLON || *ptr == CHAR_DOT ||
+ *ptr == CHAR_EQUALS_SIGN) &&
+ check_posix_syntax(ptr, ptrend, &tempptr))
+ {
+ errorcode = (*ptr-- == CHAR_COLON)? ERR12 : ERR13;
+ goto FAILED;
+ }
+
+ /* Process a regular character class. If the first character is '^', set
+ the negation flag. If the first few characters (either before or after ^)
+ are \Q\E or \E or space or tab in extended-more mode, we skip them too.
+ This makes for compatibility with Perl. */
+
+ negate_class = FALSE;
+ while (ptr < ptrend)
+ {
+ GETCHARINCTEST(c, ptr);
+ if (c == CHAR_BACKSLASH)
+ {
+ if (ptr < ptrend && *ptr == CHAR_E) ptr++;
+ else if (ptrend - ptr >= 3 &&
+ PRIV(strncmp_c8)(ptr, STR_Q STR_BACKSLASH STR_E, 3) == 0)
+ ptr += 3;
+ else
+ break;
+ }
+ else if ((options & PCRE2_EXTENDED_MORE) != 0 &&
+ (c == CHAR_SPACE || c == CHAR_HT)) /* Note: just these two */
+ continue;
+ else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT)
+ negate_class = TRUE;
+ else break;
+ }
+
+ /* Now the real contents of the class; c has the first "real" character.
+ Empty classes are permitted only if the option is set. */
+
+ if (c == CHAR_RIGHT_SQUARE_BRACKET &&
+ (cb->external_options & PCRE2_ALLOW_EMPTY_CLASS) != 0)
+ {
+ *parsed_pattern++ = negate_class? META_CLASS_EMPTY_NOT : META_CLASS_EMPTY;
+ break; /* End of class processing */
+ }
+
+ /* Process a non-empty class. */
+
+ *parsed_pattern++ = negate_class? META_CLASS_NOT : META_CLASS;
+ class_range_state = RANGE_NO;
+
+ /* In an EBCDIC environment, Perl treats alphabetic ranges specially
+ because there are holes in the encoding, and simply using the range A-Z
+ (for example) would include the characters in the holes. This applies only
+ to ranges where both values are literal; [\xC1-\xE9] is different to [A-Z]
+ in this respect. In order to accommodate this, we keep track of whether
+ character values are literal or not, and a state variable for handling
+ ranges. */
+
+ /* Loop for the contents of the class */
+
+ for (;;)
+ {
+ BOOL char_is_literal = TRUE;
+
+ /* Inside \Q...\E everything is literal except \E */
+
+ if (inescq)
+ {
+ if (c == CHAR_BACKSLASH && ptr < ptrend && *ptr == CHAR_E)
+ {
+ inescq = FALSE; /* Reset literal state */
+ ptr++; /* Skip the 'E' */
+ goto CLASS_CONTINUE;
+ }
+ goto CLASS_LITERAL;
+ }
+
+ /* Skip over space and tab (only) in extended-more mode. */
+
+ if ((options & PCRE2_EXTENDED_MORE) != 0 &&
+ (c == CHAR_SPACE || c == CHAR_HT))
+ goto CLASS_CONTINUE;
+
+ /* Handle POSIX class names. Perl allows a negation extension of the
+ form [:^name:]. A square bracket that doesn't match the syntax is
+ treated as a literal. We also recognize the POSIX constructions
+ [.ch.] and [=ch=] ("collating elements") and fault them, as Perl
+ 5.6 and 5.8 do. */
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET &&
+ ptrend - ptr >= 3 &&
+ (*ptr == CHAR_COLON || *ptr == CHAR_DOT ||
+ *ptr == CHAR_EQUALS_SIGN) &&
+ check_posix_syntax(ptr, ptrend, &tempptr))
+ {
+ BOOL posix_negate = FALSE;
+ int posix_class;
+
+ /* Perl treats a hyphen before a POSIX class as a literal, not the
+ start of a range. However, it gives a warning in its warning mode. PCRE
+ does not have a warning mode, so we give an error, because this is
+ likely an error on the user's part. */
+
+ if (class_range_state == RANGE_STARTED)
+ {
+ errorcode = ERR50;
+ goto FAILED;
+ }
+
+ if (*ptr != CHAR_COLON)
+ {
+ errorcode = ERR13;
+ goto FAILED_BACK;
+ }
+
+ if (*(++ptr) == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ posix_negate = TRUE;
+ ptr++;
+ }
+
+ posix_class = check_posix_name(ptr, (int)(tempptr - ptr));
+ if (posix_class < 0)
+ {
+ errorcode = ERR30;
+ goto FAILED;
+ }
+ ptr = tempptr + 2;
+
+ /* Perl treats a hyphen after a POSIX class as a literal, not the
+ start of a range. However, it gives a warning in its warning mode
+ unless the hyphen is the last character in the class. PCRE does not
+ have a warning mode, so we give an error, because this is likely an
+ error on the user's part. */
+
+ if (ptr < ptrend - 1 && *ptr == CHAR_MINUS &&
+ ptr[1] != CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ errorcode = ERR50;
+ goto FAILED;
+ }
+
+ /* Set "a hyphen is not the start of a range" for the -] case, and also
+ in case the POSIX class is followed by \E or \Q\E (possibly repeated -
+ fuzzers do that kind of thing) and *then* a hyphen. This causes that
+ hyphen to be treated as a literal. I don't think it's worth setting up
+ special apparatus to do otherwise. */
+
+ class_range_state = RANGE_NO;
+
+ /* When PCRE2_UCP is set, some of the POSIX classes are converted to
+ use Unicode properties \p or \P or, in one case, \h or \H. The
+ substitutes table has two values per class, containing the type and
+ value of a \p or \P item. The special cases are specified with a
+ negative type: a non-zero value causes \h or \H to be used, and a zero
+ value falls through to behave like a non-UCP POSIX class. */
+
+#ifdef SUPPORT_UNICODE
+ if ((options & PCRE2_UCP) != 0)
+ {
+ int ptype = posix_substitutes[2*posix_class];
+ int pvalue = posix_substitutes[2*posix_class + 1];
+ if (ptype >= 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_P : ESC_p);
+ *parsed_pattern++ = (ptype << 16) | pvalue;
+ goto CLASS_CONTINUE;
+ }
+
+ if (pvalue != 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + (posix_negate? ESC_H : ESC_h);
+ goto CLASS_CONTINUE;
+ }
+
+ /* Fall through */
+ }
+#endif /* SUPPORT_UNICODE */
+
+ /* Non-UCP POSIX class */
+
+ *parsed_pattern++ = posix_negate? META_POSIX_NEG : META_POSIX;
+ *parsed_pattern++ = posix_class;
+ }
+
+ /* Handle potential start of range */
+
+ else if (c == CHAR_MINUS && class_range_state >= RANGE_OK_ESCAPED)
+ {
+ *parsed_pattern++ = (class_range_state == RANGE_OK_LITERAL)?
+ META_RANGE_LITERAL : META_RANGE_ESCAPED;
+ class_range_state = RANGE_STARTED;
+ }
+
+ /* Handle a literal character */
+
+ else if (c != CHAR_BACKSLASH)
+ {
+ CLASS_LITERAL:
+ if (class_range_state == RANGE_STARTED)
+ {
+ if (c == parsed_pattern[-2]) /* Optimize one-char range */
+ parsed_pattern--;
+ else if (parsed_pattern[-2] > c) /* Check range is in order */
+ {
+ errorcode = ERR8;
+ goto FAILED_BACK;
+ }
+ else
+ {
+ if (!char_is_literal && parsed_pattern[-1] == META_RANGE_LITERAL)
+ parsed_pattern[-1] = META_RANGE_ESCAPED;
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ class_range_state = RANGE_NO;
+ }
+ else /* Potential start of range */
+ {
+ class_range_state = char_is_literal?
+ RANGE_OK_LITERAL : RANGE_OK_ESCAPED;
+ PARSED_LITERAL(c, parsed_pattern);
+ }
+ }
+
+ /* Handle escapes in a class */
+
+ else
+ {
+ tempptr = ptr;
+ escape = PRIV(check_escape)(&ptr, ptrend, &c, &errorcode, options,
+ cb->cx->extra_options, TRUE, cb);
+
+ if (errorcode != 0)
+ {
+ if ((extra_options & PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL) == 0)
+ goto FAILED;
+ ptr = tempptr;
+ if (ptr >= ptrend) c = CHAR_BACKSLASH; else
+ {
+ GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */
+ }
+ escape = 0; /* Treat as literal character */
+ }
+
+ switch(escape)
+ {
+ case 0: /* Escaped character code point is in c */
+ char_is_literal = FALSE;
+ goto CLASS_LITERAL;
+
+ case ESC_b:
+ c = CHAR_BS; /* \b is backspace in a class */
+ char_is_literal = FALSE;
+ goto CLASS_LITERAL;
+
+ case ESC_Q:
+ inescq = TRUE; /* Enter literal mode */
+ goto CLASS_CONTINUE;
+
+ case ESC_E: /* Ignore orphan \E */
+ goto CLASS_CONTINUE;
+
+ case ESC_B: /* Always an error in a class */
+ case ESC_R:
+ case ESC_X:
+ errorcode = ERR7;
+ ptr--;
+ goto FAILED;
+ }
+
+ /* The second part of a range can be a single-character escape
+ sequence (detected above), but not any of the other escapes. Perl
+ treats a hyphen as a literal in such circumstances. However, in Perl's
+ warning mode, a warning is given, so PCRE now faults it, as it is
+ almost certainly a mistake on the user's part. */
+
+ if (class_range_state == RANGE_STARTED)
+ {
+ errorcode = ERR50;
+ goto FAILED; /* Not CLASS_ESCAPE_FAILED; always an error */
+ }
+
+ /* Of the remaining escapes, only those that define characters are
+ allowed in a class. None may start a range. */
+
+ class_range_state = RANGE_NO;
+ switch(escape)
+ {
+ case ESC_N:
+ errorcode = ERR71;
+ goto FAILED;
+
+ case ESC_H:
+ case ESC_h:
+ case ESC_V:
+ case ESC_v:
+ *parsed_pattern++ = META_ESCAPE + escape;
+ break;
+
+ /* These escapes are converted to Unicode property tests when
+ PCRE2_UCP is set. */
+
+ case ESC_d:
+ case ESC_D:
+ case ESC_s:
+ case ESC_S:
+ case ESC_w:
+ case ESC_W:
+ if ((options & PCRE2_UCP) == 0)
+ {
+ *parsed_pattern++ = META_ESCAPE + escape;
+ }
+ else
+ {
+ *parsed_pattern++ = META_ESCAPE +
+ ((escape == ESC_d || escape == ESC_s || escape == ESC_w)?
+ ESC_p : ESC_P);
+ switch(escape)
+ {
+ case ESC_d:
+ case ESC_D:
+ *parsed_pattern++ = (PT_PC << 16) | ucp_Nd;
+ break;
+
+ case ESC_s:
+ case ESC_S:
+ *parsed_pattern++ = PT_SPACE << 16;
+ break;
+
+ case ESC_w:
+ case ESC_W:
+ *parsed_pattern++ = PT_WORD << 16;
+ break;
+ }
+ }
+ break;
+
+ /* Explicit Unicode property matching */
+
+ case ESC_P:
+ case ESC_p:
+#ifdef SUPPORT_UNICODE
+ {
+ BOOL negated;
+ uint16_t ptype = 0, pdata = 0;
+ if (!get_ucp(&ptr, &negated, &ptype, &pdata, &errorcode, cb))
+ goto FAILED;
+ if (negated) escape = (escape == ESC_P)? ESC_p : ESC_P;
+ *parsed_pattern++ = META_ESCAPE + escape;
+ *parsed_pattern++ = (ptype << 16) | pdata;
+ }
+#else
+ errorcode = ERR45;
+ goto FAILED;
+#endif
+ break; /* End \P and \p */
+
+ default: /* All others are not allowed in a class */
+ errorcode = ERR7;
+ ptr--;
+ goto FAILED;
+ }
+
+ /* Perl gives a warning unless a following hyphen is the last character
+ in the class. PCRE throws an error. */
+
+ if (ptr < ptrend - 1 && *ptr == CHAR_MINUS &&
+ ptr[1] != CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ errorcode = ERR50;
+ goto FAILED;
+ }
+ }
+
+ /* Proceed to next thing in the class. */
+
+ CLASS_CONTINUE:
+ if (ptr >= ptrend)
+ {
+ errorcode = ERR6; /* Missing terminating ']' */
+ goto FAILED;
+ }
+ GETCHARINCTEST(c, ptr);
+ if (c == CHAR_RIGHT_SQUARE_BRACKET && !inescq) break;
+ } /* End of class-processing loop */
+
+ /* -] at the end of a class is a literal '-' */
+
+ if (class_range_state == RANGE_STARTED)
+ {
+ parsed_pattern[-1] = CHAR_MINUS;
+ class_range_state = RANGE_NO;
+ }
+
+ *parsed_pattern++ = META_CLASS_END;
+ break; /* End of character class */
+
+
+ /* ---- Opening parenthesis ---- */
+
+ case CHAR_LEFT_PARENTHESIS:
+ if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+
+ /* If ( is not followed by ? it is either a capture or a special verb or an
+ alpha assertion or a positive non-atomic lookahead. */
+
+ if (*ptr != CHAR_QUESTION_MARK)
+ {
+ const char *vn;
+
+ /* Handle capturing brackets (or non-capturing if auto-capture is turned
+ off). */
+
+ if (*ptr != CHAR_ASTERISK)
+ {
+ nest_depth++;
+ if ((options & PCRE2_NO_AUTO_CAPTURE) == 0)
+ {
+ if (cb->bracount >= MAX_GROUP_NUMBER)
+ {
+ errorcode = ERR97;
+ goto FAILED;
+ }
+ cb->bracount++;
+ *parsed_pattern++ = META_CAPTURE | cb->bracount;
+ }
+ else *parsed_pattern++ = META_NOCAPTURE;
+ }
+
+ /* Do nothing for (* followed by end of pattern or ) so it gives a "bad
+ quantifier" error rather than "(*MARK) must have an argument". */
+
+ else if (ptrend - ptr <= 1 || (c = ptr[1]) == CHAR_RIGHT_PARENTHESIS)
+ break;
+
+ /* Handle "alpha assertions" such as (*pla:...). Most of these are
+ synonyms for the historical symbolic assertions, but the script run and
+ non-atomic lookaround ones are new. They are distinguished by starting
+ with a lower case letter. Checking both ends of the alphabet makes this
+ work in all character codes. */
+
+ else if (CHMAX_255(c) && (cb->ctypes[c] & ctype_lcletter) != 0)
+ {
+ uint32_t meta;
+
+ vn = alasnames;
+ if (!read_name(&ptr, ptrend, utf, 0, &offset, &name, &namelen,
+ &errorcode, cb)) goto FAILED;
+ if (ptr >= ptrend || *ptr != CHAR_COLON)
+ {
+ errorcode = ERR95; /* Malformed */
+ goto FAILED;
+ }
+
+ /* Scan the table of alpha assertion names */
+
+ for (i = 0; i < alascount; i++)
+ {
+ if (namelen == alasmeta[i].len &&
+ PRIV(strncmp_c8)(name, vn, namelen) == 0)
+ break;
+ vn += alasmeta[i].len + 1;
+ }
+
+ if (i >= alascount)
+ {
+ errorcode = ERR95; /* Alpha assertion not recognized */
+ goto FAILED;
+ }
+
+ /* Check for expecting an assertion condition. If so, only atomic
+ lookaround assertions are valid. */
+
+ meta = alasmeta[i].meta;
+ if (prev_expect_cond_assert > 0 &&
+ (meta < META_LOOKAHEAD || meta > META_LOOKBEHINDNOT))
+ {
+ errorcode = (meta == META_LOOKAHEAD_NA || meta == META_LOOKBEHIND_NA)?
+ ERR98 : ERR28; /* (Atomic) assertion expected */
+ goto FAILED;
+ }
+
+ /* The lookaround alphabetic synonyms can mostly be handled by jumping
+ to the code that handles the traditional symbolic forms. */
+
+ switch(meta)
+ {
+ default:
+ errorcode = ERR89; /* Unknown code; should never occur because */
+ goto FAILED; /* the meta values come from a table above. */
+
+ case META_ATOMIC:
+ goto ATOMIC_GROUP;
+
+ case META_LOOKAHEAD:
+ goto POSITIVE_LOOK_AHEAD;
+
+ case META_LOOKAHEAD_NA:
+ goto POSITIVE_NONATOMIC_LOOK_AHEAD;
+
+ case META_LOOKAHEADNOT:
+ goto NEGATIVE_LOOK_AHEAD;
+
+ case META_LOOKBEHIND:
+ case META_LOOKBEHINDNOT:
+ case META_LOOKBEHIND_NA:
+ *parsed_pattern++ = meta;
+ ptr--;
+ goto POST_LOOKBEHIND;
+
+ /* The script run facilities are handled here. Unicode support is
+ required (give an error if not, as this is a security issue). Always
+ record a META_SCRIPT_RUN item. Then, for the atomic version, insert
+ META_ATOMIC and remember that we need two META_KETs at the end. */
+
+ case META_SCRIPT_RUN:
+ case META_ATOMIC_SCRIPT_RUN:
+#ifdef SUPPORT_UNICODE
+ *parsed_pattern++ = META_SCRIPT_RUN;
+ nest_depth++;
+ ptr++;
+ if (meta == META_ATOMIC_SCRIPT_RUN)
+ {
+ *parsed_pattern++ = META_ATOMIC;
+ if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);
+ else if (++top_nest >= end_nests)
+ {
+ errorcode = ERR84;
+ goto FAILED;
+ }
+ top_nest->nest_depth = nest_depth;
+ top_nest->flags = NSF_ATOMICSR;
+ top_nest->options = options & PARSE_TRACKED_OPTIONS;
+ }
+ break;
+#else /* SUPPORT_UNICODE */
+ errorcode = ERR96;
+ goto FAILED;
+#endif
+ }
+ }
+
+
+ /* ---- Handle (*VERB) and (*VERB:NAME) ---- */
+
+ else
+ {
+ vn = verbnames;
+ if (!read_name(&ptr, ptrend, utf, 0, &offset, &name, &namelen,
+ &errorcode, cb)) goto FAILED;
+ if (ptr >= ptrend || (*ptr != CHAR_COLON &&
+ *ptr != CHAR_RIGHT_PARENTHESIS))
+ {
+ errorcode = ERR60; /* Malformed */
+ goto FAILED;
+ }
+
+ /* Scan the table of verb names */
+
+ for (i = 0; i < verbcount; i++)
+ {
+ if (namelen == verbs[i].len &&
+ PRIV(strncmp_c8)(name, vn, namelen) == 0)
+ break;
+ vn += verbs[i].len + 1;
+ }
+
+ if (i >= verbcount)
+ {
+ errorcode = ERR60; /* Verb not recognized */
+ goto FAILED;
+ }
+
+ /* An empty argument is treated as no argument. */
+
+ if (*ptr == CHAR_COLON && ptr + 1 < ptrend &&
+ ptr[1] == CHAR_RIGHT_PARENTHESIS)
+ ptr++; /* Advance to the closing parens */
+
+ /* Check for mandatory non-empty argument; this is (*MARK) */
+
+ if (verbs[i].has_arg > 0 && *ptr != CHAR_COLON)
+ {
+ errorcode = ERR66;
+ goto FAILED;
+ }
+
+ /* Remember where this verb, possibly with a preceding (*MARK), starts,
+ for handling quantified (*ACCEPT). */
+
+ verbstartptr = parsed_pattern;
+ okquantifier = (verbs[i].meta == META_ACCEPT);
+
+ /* It appears that Perl allows any characters whatsoever, other than a
+ closing parenthesis, to appear in arguments ("names"), so we no longer
+ insist on letters, digits, and underscores. Perl does not, however, do
+ any interpretation within arguments, and has no means of including a
+ closing parenthesis. PCRE supports escape processing but only when it
+ is requested by an option. We set inverbname TRUE here, and let the
+ main loop take care of this so that escape and \x processing is done by
+ the main code above. */
+
+ if (*ptr++ == CHAR_COLON) /* Skip past : or ) */
+ {
+ /* Some optional arguments can be treated as a preceding (*MARK) */
+
+ if (verbs[i].has_arg < 0)
+ {
+ add_after_mark = verbs[i].meta;
+ *parsed_pattern++ = META_MARK;
+ }
+
+ /* The remaining verbs with arguments (except *MARK) need a different
+ opcode. */
+
+ else
+ {
+ *parsed_pattern++ = verbs[i].meta +
+ ((verbs[i].meta != META_MARK)? 0x00010000u:0);
+ }
+
+ /* Set up for reading the name in the main loop. */
+
+ verblengthptr = parsed_pattern++;
+ verbnamestart = ptr;
+ inverbname = TRUE;
+ }
+ else /* No verb "name" argument */
+ {
+ *parsed_pattern++ = verbs[i].meta;
+ }
+ } /* End of (*VERB) handling */
+ break; /* Done with this parenthesis */
+ } /* End of groups that don't start with (? */
+
+
+ /* ---- Items starting (? ---- */
+
+ /* The type of item is determined by what follows (?. Handle (?| and option
+ changes under "default" because both need a new block on the nest stack.
+ Comments starting with (?# are handled above. Note that there is some
+ ambiguity about the sequence (?- because if a digit follows it's a relative
+ recursion or subroutine call whereas otherwise it's an option unsetting. */
+
+ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+
+ switch(*ptr)
+ {
+ default:
+ if (*ptr == CHAR_MINUS && ptrend - ptr > 1 && IS_DIGIT(ptr[1]))
+ goto RECURSION_BYNUMBER; /* The + case is handled by CHAR_PLUS */
+
+ /* We now have either (?| or a (possibly empty) option setting,
+ optionally followed by a non-capturing group. */
+
+ nest_depth++;
+ if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);
+ else if (++top_nest >= end_nests)
+ {
+ errorcode = ERR84;
+ goto FAILED;
+ }
+ top_nest->nest_depth = nest_depth;
+ top_nest->flags = 0;
+ top_nest->options = options & PARSE_TRACKED_OPTIONS;
+
+ /* Start of non-capturing group that resets the capture count for each
+ branch. */
+
+ if (*ptr == CHAR_VERTICAL_LINE)
+ {
+ top_nest->reset_group = (uint16_t)cb->bracount;
+ top_nest->max_group = (uint16_t)cb->bracount;
+ top_nest->flags |= NSF_RESET;
+ cb->external_flags |= PCRE2_DUPCAPUSED;
+ *parsed_pattern++ = META_NOCAPTURE;
+ ptr++;
+ }
+
+ /* Scan for options imnsxJU to be set or unset. */
+
+ else
+ {
+ BOOL hyphenok = TRUE;
+ uint32_t oldoptions = options;
+
+ top_nest->reset_group = 0;
+ top_nest->max_group = 0;
+ set = unset = 0;
+ optset = &set;
+
+ /* ^ at the start unsets imnsx and disables the subsequent use of - */
+
+ if (ptr < ptrend && *ptr == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ options &= ~(PCRE2_CASELESS|PCRE2_MULTILINE|PCRE2_NO_AUTO_CAPTURE|
+ PCRE2_DOTALL|PCRE2_EXTENDED|PCRE2_EXTENDED_MORE);
+ hyphenok = FALSE;
+ ptr++;
+ }
+
+ while (ptr < ptrend && *ptr != CHAR_RIGHT_PARENTHESIS &&
+ *ptr != CHAR_COLON)
+ {
+ switch (*ptr++)
+ {
+ case CHAR_MINUS:
+ if (!hyphenok)
+ {
+ errorcode = ERR94;
+ ptr--; /* Correct the offset */
+ goto FAILED;
+ }
+ optset = &unset;
+ hyphenok = FALSE;
+ break;
+
+ case CHAR_J: /* Record that it changed in the external options */
+ *optset |= PCRE2_DUPNAMES;
+ cb->external_flags |= PCRE2_JCHANGED;
+ break;
+
+ case CHAR_i: *optset |= PCRE2_CASELESS; break;
+ case CHAR_m: *optset |= PCRE2_MULTILINE; break;
+ case CHAR_n: *optset |= PCRE2_NO_AUTO_CAPTURE; break;
+ case CHAR_s: *optset |= PCRE2_DOTALL; break;
+ case CHAR_U: *optset |= PCRE2_UNGREEDY; break;
+
+ /* If x appears twice it sets the extended extended option. */
+
+ case CHAR_x:
+ *optset |= PCRE2_EXTENDED;
+ if (ptr < ptrend && *ptr == CHAR_x)
+ {
+ *optset |= PCRE2_EXTENDED_MORE;
+ ptr++;
+ }
+ break;
+
+ default:
+ errorcode = ERR11;
+ ptr--; /* Correct the offset */
+ goto FAILED;
+ }
+ }
+
+ /* If we are setting extended without extended-more, ensure that any
+ existing extended-more gets unset. Also, unsetting extended must also
+ unset extended-more. */
+
+ if ((set & (PCRE2_EXTENDED|PCRE2_EXTENDED_MORE)) == PCRE2_EXTENDED ||
+ (unset & PCRE2_EXTENDED) != 0)
+ unset |= PCRE2_EXTENDED_MORE;
+
+ options = (options | set) & (~unset);
+
+ /* If the options ended with ')' this is not the start of a nested
+ group with option changes, so the options change at this level.
+ In this case, if the previous level set up a nest block, discard the
+ one we have just created. Otherwise adjust it for the previous level.
+ If the options ended with ':' we are starting a non-capturing group,
+ possibly with an options setting. */
+
+ if (ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+ if (*ptr++ == CHAR_RIGHT_PARENTHESIS)
+ {
+ nest_depth--; /* This is not a nested group after all. */
+ if (top_nest > (nest_save *)(cb->start_workspace) &&
+ (top_nest-1)->nest_depth == nest_depth) top_nest--;
+ else top_nest->nest_depth = nest_depth;
+ }
+ else *parsed_pattern++ = META_NOCAPTURE;
+
+ /* If nothing changed, no need to record. */
+
+ if (options != oldoptions)
+ {
+ *parsed_pattern++ = META_OPTIONS;
+ *parsed_pattern++ = options;
+ }
+ } /* End options processing */
+ break; /* End default case after (? */
+
+
+ /* ---- Python syntax support ---- */
+
+ case CHAR_P:
+ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+
+ /* (?P<name> is the same as (?<name>, which defines a named group. */
+
+ if (*ptr == CHAR_LESS_THAN_SIGN)
+ {
+ terminator = CHAR_GREATER_THAN_SIGN;
+ goto DEFINE_NAME;
+ }
+
+ /* (?P>name) is the same as (?&name), which is a recursion or subroutine
+ call. */
+
+ if (*ptr == CHAR_GREATER_THAN_SIGN) goto RECURSE_BY_NAME;
+
+ /* (?P=name) is the same as \k<name>, a back reference by name. Anything
+ else after (?P is an error. */
+
+ if (*ptr != CHAR_EQUALS_SIGN)
+ {
+ errorcode = ERR41;
+ goto FAILED;
+ }
+ if (!read_name(&ptr, ptrend, utf, CHAR_RIGHT_PARENTHESIS, &offset, &name,
+ &namelen, &errorcode, cb)) goto FAILED;
+ *parsed_pattern++ = META_BACKREF_BYNAME;
+ *parsed_pattern++ = namelen;
+ PUTOFFSET(offset, parsed_pattern);
+ okquantifier = TRUE;
+ break; /* End of (?P processing */
+
+
+ /* ---- Recursion/subroutine calls by number ---- */
+
+ case CHAR_R:
+ i = 0; /* (?R) == (?R0) */
+ ptr++;
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR58;
+ goto FAILED;
+ }
+ goto SET_RECURSION;
+
+ /* An item starting (?- followed by a digit comes here via the "default"
+ case because (?- followed by a non-digit is an options setting. */
+
+ case CHAR_PLUS:
+ if (ptrend - ptr < 2 || !IS_DIGIT(ptr[1]))
+ {
+ errorcode = ERR29; /* Missing number */
+ goto FAILED;
+ }
+ /* Fall through */
+
+ case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4:
+ case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9:
+ RECURSION_BYNUMBER:
+ if (!read_number(&ptr, ptrend,
+ (IS_DIGIT(*ptr))? -1:(int)(cb->bracount), /* + and - are relative */
+ MAX_GROUP_NUMBER, ERR61,
+ &i, &errorcode)) goto FAILED;
+ if (i < 0) /* NB (?0) is permitted */
+ {
+ errorcode = ERR15; /* Unknown group */
+ goto FAILED_BACK;
+ }
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ goto UNCLOSED_PARENTHESIS;
+
+ SET_RECURSION:
+ *parsed_pattern++ = META_RECURSE | (uint32_t)i;
+ offset = (PCRE2_SIZE)(ptr - cb->start_pattern);
+ ptr++;
+ PUTOFFSET(offset, parsed_pattern);
+ okquantifier = TRUE;
+ break; /* End of recursive call by number handling */
+
+
+ /* ---- Recursion/subroutine calls by name ---- */
+
+ case CHAR_AMPERSAND:
+ RECURSE_BY_NAME:
+ if (!read_name(&ptr, ptrend, utf, CHAR_RIGHT_PARENTHESIS, &offset, &name,
+ &namelen, &errorcode, cb)) goto FAILED;
+ *parsed_pattern++ = META_RECURSE_BYNAME;
+ *parsed_pattern++ = namelen;
+ PUTOFFSET(offset, parsed_pattern);
+ okquantifier = TRUE;
+ break;
+
+ /* ---- Callout with numerical or string argument ---- */
+
+ case CHAR_C:
+ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+
+ /* If the previous item was a condition starting (?(? an assertion,
+ optionally preceded by a callout, is expected. This is checked later on,
+ during actual compilation. However we need to identify this kind of
+ assertion in this pass because it must not be qualified. The value of
+ expect_cond_assert is set to 2 after (?(? is processed. We decrement it
+ for a callout - still leaving a positive value that identifies the
+ assertion. Multiple callouts or any other items will make it zero or
+ less, which doesn't matter because they will cause an error later. */
+
+ expect_cond_assert = prev_expect_cond_assert - 1;
+
+ /* If previous_callout is not NULL, it means this follows a previous
+ callout. If it was a manual callout, do nothing; this means its "length
+ of next pattern item" field will remain zero. If it was an automatic
+ callout, abolish it. */
+
+ if (previous_callout != NULL && (options & PCRE2_AUTO_CALLOUT) != 0 &&
+ previous_callout == parsed_pattern - 4 &&
+ parsed_pattern[-1] == 255)
+ parsed_pattern = previous_callout;
+
+ /* Save for updating next pattern item length, and skip one item before
+ completing. */
+
+ previous_callout = parsed_pattern;
+ after_manual_callout = 1;
+
+ /* Handle a string argument; specific delimiter is required. */
+
+ if (*ptr != CHAR_RIGHT_PARENTHESIS && !IS_DIGIT(*ptr))
+ {
+ PCRE2_SIZE calloutlength;
+ PCRE2_SPTR startptr = ptr;
+
+ delimiter = 0;
+ for (i = 0; PRIV(callout_start_delims)[i] != 0; i++)
+ {
+ if (*ptr == PRIV(callout_start_delims)[i])
+ {
+ delimiter = PRIV(callout_end_delims)[i];
+ break;
+ }
+ }
+ if (delimiter == 0)
+ {
+ errorcode = ERR82;
+ goto FAILED;
+ }
+
+ *parsed_pattern = META_CALLOUT_STRING;
+ parsed_pattern += 3; /* Skip pattern info */
+
+ for (;;)
+ {
+ if (++ptr >= ptrend)
+ {
+ errorcode = ERR81;
+ ptr = startptr; /* To give a more useful message */
+ goto FAILED;
+ }
+ if (*ptr == delimiter && (++ptr >= ptrend || *ptr != delimiter))
+ break;
+ }
+
+ calloutlength = (PCRE2_SIZE)(ptr - startptr);
+ if (calloutlength > UINT32_MAX)
+ {
+ errorcode = ERR72;
+ goto FAILED;
+ }
+ *parsed_pattern++ = (uint32_t)calloutlength;
+ offset = (PCRE2_SIZE)(startptr - cb->start_pattern);
+ PUTOFFSET(offset, parsed_pattern);
+ }
+
+ /* Handle a callout with an optional numerical argument, which must be
+ less than or equal to 255. A missing argument gives 0. */
+
+ else
+ {
+ int n = 0;
+ *parsed_pattern = META_CALLOUT_NUMBER; /* Numerical callout */
+ parsed_pattern += 3; /* Skip pattern info */
+ while (ptr < ptrend && IS_DIGIT(*ptr))
+ {
+ n = n * 10 + *ptr++ - CHAR_0;
+ if (n > 255)
+ {
+ errorcode = ERR38;
+ goto FAILED;
+ }
+ }
+ *parsed_pattern++ = n;
+ }
+
+ /* Both formats must have a closing parenthesis */
+
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR39;
+ goto FAILED;
+ }
+ ptr++;
+
+ /* Remember the offset to the next item in the pattern, and set a default
+ length. This should get updated after the next item is read. */
+
+ previous_callout[1] = (uint32_t)(ptr - cb->start_pattern);
+ previous_callout[2] = 0;
+ break; /* End callout */
+
+
+ /* ---- Conditional group ---- */
+
+ /* A condition can be an assertion, a number (referring to a numbered
+ group's having been set), a name (referring to a named group), or 'R',
+ referring to overall recursion. R<digits> and R&name are also permitted
+ for recursion state tests. Numbers may be preceded by + or - to specify a
+ relative group number.
+
+ There are several syntaxes for testing a named group: (?(name)) is used
+ by Python; Perl 5.10 onwards uses (?(<name>) or (?('name')).
+
+ There are two unfortunate ambiguities. 'R' can be the recursive thing or
+ the name 'R' (and similarly for 'R' followed by digits). 'DEFINE' can be
+ the Perl DEFINE feature or the Python named test. We look for a name
+ first; if not found, we try the other case.
+
+ For compatibility with auto-callouts, we allow a callout to be specified
+ before a condition that is an assertion. */
+
+ case CHAR_LEFT_PARENTHESIS:
+ if (++ptr >= ptrend) goto UNCLOSED_PARENTHESIS;
+ nest_depth++;
+
+ /* If the next character is ? or * there must be an assertion next
+ (optionally preceded by a callout). We do not check this here, but
+ instead we set expect_cond_assert to 2. If this is still greater than
+ zero (callouts decrement it) when the next assertion is read, it will be
+ marked as a condition that must not be repeated. A value greater than
+ zero also causes checking that an assertion (possibly with callout)
+ follows. */
+
+ if (*ptr == CHAR_QUESTION_MARK || *ptr == CHAR_ASTERISK)
+ {
+ *parsed_pattern++ = META_COND_ASSERT;
+ ptr--; /* Pull pointer back to the opening parenthesis. */
+ expect_cond_assert = 2;
+ break; /* End of conditional */
+ }
+
+ /* Handle (?([+-]number)... */
+
+ if (read_number(&ptr, ptrend, cb->bracount, MAX_GROUP_NUMBER, ERR61, &i,
+ &errorcode))
+ {
+ if (i <= 0)
+ {
+ errorcode = ERR15;
+ goto FAILED;
+ }
+ *parsed_pattern++ = META_COND_NUMBER;
+ offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2);
+ PUTOFFSET(offset, parsed_pattern);
+ *parsed_pattern++ = i;
+ }
+ else if (errorcode != 0) goto FAILED; /* Number too big */
+
+ /* No number found. Handle the special case (?(VERSION[>]=n.m)... */
+
+ else if (ptrend - ptr >= 10 &&
+ PRIV(strncmp_c8)(ptr, STRING_VERSION, 7) == 0 &&
+ ptr[7] != CHAR_RIGHT_PARENTHESIS)
+ {
+ uint32_t ge = 0;
+ int major = 0;
+ int minor = 0;
+
+ ptr += 7;
+ if (*ptr == CHAR_GREATER_THAN_SIGN)
+ {
+ ge = 1;
+ ptr++;
+ }
+
+ /* NOTE: cannot write IS_DIGIT(*(++ptr)) here because IS_DIGIT
+ references its argument twice. */
+
+ if (*ptr != CHAR_EQUALS_SIGN || (ptr++, !IS_DIGIT(*ptr)))
+ goto BAD_VERSION_CONDITION;
+
+ if (!read_number(&ptr, ptrend, -1, 1000, ERR79, &major, &errorcode))
+ goto FAILED;
+
+ if (ptr >= ptrend) goto BAD_VERSION_CONDITION;
+ if (*ptr == CHAR_DOT)
+ {
+ if (++ptr >= ptrend || !IS_DIGIT(*ptr)) goto BAD_VERSION_CONDITION;
+ minor = (*ptr++ - CHAR_0) * 10;
+ if (ptr >= ptrend) goto BAD_VERSION_CONDITION;
+ if (IS_DIGIT(*ptr)) minor += *ptr++ - CHAR_0;
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ goto BAD_VERSION_CONDITION;
+ }
+
+ *parsed_pattern++ = META_COND_VERSION;
+ *parsed_pattern++ = ge;
+ *parsed_pattern++ = major;
+ *parsed_pattern++ = minor;
+ }
+
+ /* All the remaining cases now require us to read a name. We cannot at
+ this stage distinguish ambiguous cases such as (?(R12) which might be a
+ recursion test by number or a name, because the named groups have not yet
+ all been identified. Those cases are treated as names, but given a
+ different META code. */
+
+ else
+ {
+ BOOL was_r_ampersand = FALSE;
+
+ if (*ptr == CHAR_R && ptrend - ptr > 1 && ptr[1] == CHAR_AMPERSAND)
+ {
+ terminator = CHAR_RIGHT_PARENTHESIS;
+ was_r_ampersand = TRUE;
+ ptr++;
+ }
+ else if (*ptr == CHAR_LESS_THAN_SIGN)
+ terminator = CHAR_GREATER_THAN_SIGN;
+ else if (*ptr == CHAR_APOSTROPHE)
+ terminator = CHAR_APOSTROPHE;
+ else
+ {
+ terminator = CHAR_RIGHT_PARENTHESIS;
+ ptr--; /* Point to char before name */
+ }
+ if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,
+ &errorcode, cb)) goto FAILED;
+
+ /* Handle (?(R&name) */
+
+ if (was_r_ampersand)
+ {
+ *parsed_pattern = META_COND_RNAME;
+ ptr--; /* Back to closing parens */
+ }
+
+ /* Handle (?(name). If the name is "DEFINE" we identify it with a
+ special code. Likewise if the name consists of R followed only by
+ digits. Otherwise, handle it like a quoted name. */
+
+ else if (terminator == CHAR_RIGHT_PARENTHESIS)
+ {
+ if (namelen == 6 && PRIV(strncmp_c8)(name, STRING_DEFINE, 6) == 0)
+ *parsed_pattern = META_COND_DEFINE;
+ else
+ {
+ for (i = 1; i < (int)namelen; i++)
+ if (!IS_DIGIT(name[i])) break;
+ *parsed_pattern = (*name == CHAR_R && i >= (int)namelen)?
+ META_COND_RNUMBER : META_COND_NAME;
+ }
+ ptr--; /* Back to closing parens */
+ }
+
+ /* Handle (?('name') or (?(<name>) */
+
+ else *parsed_pattern = META_COND_NAME;
+
+ /* All these cases except DEFINE end with the name length and offset;
+ DEFINE just has an offset (for the "too many branches" error). */
+
+ if (*parsed_pattern++ != META_COND_DEFINE) *parsed_pattern++ = namelen;
+ PUTOFFSET(offset, parsed_pattern);
+ } /* End cases that read a name */
+
+ /* Check the closing parenthesis of the condition */
+
+ if (ptr >= ptrend || *ptr != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR24;
+ goto FAILED;
+ }
+ ptr++;
+ break; /* End of condition processing */
+
+
+ /* ---- Atomic group ---- */
+
+ case CHAR_GREATER_THAN_SIGN:
+ ATOMIC_GROUP: /* Come from (*atomic: */
+ *parsed_pattern++ = META_ATOMIC;
+ nest_depth++;
+ ptr++;
+ break;
+
+
+ /* ---- Lookahead assertions ---- */
+
+ case CHAR_EQUALS_SIGN:
+ POSITIVE_LOOK_AHEAD: /* Come from (*pla: */
+ *parsed_pattern++ = META_LOOKAHEAD;
+ ptr++;
+ goto POST_ASSERTION;
+
+ case CHAR_ASTERISK:
+ POSITIVE_NONATOMIC_LOOK_AHEAD: /* Come from (?* */
+ *parsed_pattern++ = META_LOOKAHEAD_NA;
+ ptr++;
+ goto POST_ASSERTION;
+
+ case CHAR_EXCLAMATION_MARK:
+ NEGATIVE_LOOK_AHEAD: /* Come from (*nla: */
+ *parsed_pattern++ = META_LOOKAHEADNOT;
+ ptr++;
+ goto POST_ASSERTION;
+
+
+ /* ---- Lookbehind assertions ---- */
+
+ /* (?< followed by = or ! or * is a lookbehind assertion. Otherwise (?<
+ is the start of the name of a capturing group. */
+
+ case CHAR_LESS_THAN_SIGN:
+ if (ptrend - ptr <= 1 ||
+ (ptr[1] != CHAR_EQUALS_SIGN &&
+ ptr[1] != CHAR_EXCLAMATION_MARK &&
+ ptr[1] != CHAR_ASTERISK))
+ {
+ terminator = CHAR_GREATER_THAN_SIGN;
+ goto DEFINE_NAME;
+ }
+ *parsed_pattern++ = (ptr[1] == CHAR_EQUALS_SIGN)?
+ META_LOOKBEHIND : (ptr[1] == CHAR_EXCLAMATION_MARK)?
+ META_LOOKBEHINDNOT : META_LOOKBEHIND_NA;
+
+ POST_LOOKBEHIND: /* Come from (*plb: (*naplb: and (*nlb: */
+ *has_lookbehind = TRUE;
+ offset = (PCRE2_SIZE)(ptr - cb->start_pattern - 2);
+ PUTOFFSET(offset, parsed_pattern);
+ ptr += 2;
+ /* Fall through */
+
+ /* If the previous item was a condition starting (?(? an assertion,
+ optionally preceded by a callout, is expected. This is checked later on,
+ during actual compilation. However we need to identify this kind of
+ assertion in this pass because it must not be qualified. The value of
+ expect_cond_assert is set to 2 after (?(? is processed. We decrement it
+ for a callout - still leaving a positive value that identifies the
+ assertion. Multiple callouts or any other items will make it zero or
+ less, which doesn't matter because they will cause an error later. */
+
+ POST_ASSERTION:
+ nest_depth++;
+ if (prev_expect_cond_assert > 0)
+ {
+ if (top_nest == NULL) top_nest = (nest_save *)(cb->start_workspace);
+ else if (++top_nest >= end_nests)
+ {
+ errorcode = ERR84;
+ goto FAILED;
+ }
+ top_nest->nest_depth = nest_depth;
+ top_nest->flags = NSF_CONDASSERT;
+ top_nest->options = options & PARSE_TRACKED_OPTIONS;
+ }
+ break;
+
+
+ /* ---- Define a named group ---- */
+
+ /* A named group may be defined as (?'name') or (?<name>). In the latter
+ case we jump to DEFINE_NAME from the disambiguation of (?< above with the
+ terminator set to '>'. */
+
+ case CHAR_APOSTROPHE:
+ terminator = CHAR_APOSTROPHE; /* Terminator */
+
+ DEFINE_NAME:
+ if (!read_name(&ptr, ptrend, utf, terminator, &offset, &name, &namelen,
+ &errorcode, cb)) goto FAILED;
+
+ /* We have a name for this capturing group. It is also assigned a number,
+ which is its primary means of identification. */
+
+ if (cb->bracount >= MAX_GROUP_NUMBER)
+ {
+ errorcode = ERR97;
+ goto FAILED;
+ }
+ cb->bracount++;
+ *parsed_pattern++ = META_CAPTURE | cb->bracount;
+ nest_depth++;
+
+ /* Check not too many names */
+
+ if (cb->names_found >= MAX_NAME_COUNT)
+ {
+ errorcode = ERR49;
+ goto FAILED;
+ }
+
+ /* Adjust the entry size to accommodate the longest name found. */
+
+ if (namelen + IMM2_SIZE + 1 > cb->name_entry_size)
+ cb->name_entry_size = (uint16_t)(namelen + IMM2_SIZE + 1);
+
+ /* Scan the list to check for duplicates. For duplicate names, if the
+ number is the same, break the loop, which causes the name to be
+ discarded; otherwise, if DUPNAMES is not set, give an error.
+ If it is set, allow the name with a different number, but continue
+ scanning in case this is a duplicate with the same number. For
+ non-duplicate names, give an error if the number is duplicated. */
+
+ isdupname = FALSE;
+ ng = cb->named_groups;
+ for (i = 0; i < cb->names_found; i++, ng++)
+ {
+ if (namelen == ng->length &&
+ PRIV(strncmp)(name, ng->name, (PCRE2_SIZE)namelen) == 0)
+ {
+ if (ng->number == cb->bracount) break;
+ if ((options & PCRE2_DUPNAMES) == 0)
+ {
+ errorcode = ERR43;
+ goto FAILED;
+ }
+ isdupname = ng->isdup = TRUE; /* Mark as a duplicate */
+ cb->dupnames = TRUE; /* Duplicate names exist */
+ }
+ else if (ng->number == cb->bracount)
+ {
+ errorcode = ERR65;
+ goto FAILED;
+ }
+ }
+
+ if (i < cb->names_found) break; /* Ignore duplicate with same number */
+
+ /* Increase the list size if necessary */
+
+ if (cb->names_found >= cb->named_group_list_size)
+ {
+ uint32_t newsize = cb->named_group_list_size * 2;
+ named_group *newspace =
+ cb->cx->memctl.malloc(newsize * sizeof(named_group),
+ cb->cx->memctl.memory_data);
+ if (newspace == NULL)
+ {
+ errorcode = ERR21;
+ goto FAILED;
+ }
+
+ memcpy(newspace, cb->named_groups,
+ cb->named_group_list_size * sizeof(named_group));
+ if (cb->named_group_list_size > NAMED_GROUP_LIST_SIZE)
+ cb->cx->memctl.free((void *)cb->named_groups,
+ cb->cx->memctl.memory_data);
+ cb->named_groups = newspace;
+ cb->named_group_list_size = newsize;
+ }
+
+ /* Add this name to the list */
+
+ cb->named_groups[cb->names_found].name = name;
+ cb->named_groups[cb->names_found].length = (uint16_t)namelen;
+ cb->named_groups[cb->names_found].number = cb->bracount;
+ cb->named_groups[cb->names_found].isdup = (uint16_t)isdupname;
+ cb->names_found++;
+ break;
+ } /* End of (? switch */
+ break; /* End of ( handling */
+
+
+ /* ---- Branch terminators ---- */
+
+ /* Alternation: reset the capture count if we are in a (?| group. */
+
+ case CHAR_VERTICAL_LINE:
+ if (top_nest != NULL && top_nest->nest_depth == nest_depth &&
+ (top_nest->flags & NSF_RESET) != 0)
+ {
+ if (cb->bracount > top_nest->max_group)
+ top_nest->max_group = (uint16_t)cb->bracount;
+ cb->bracount = top_nest->reset_group;
+ }
+ *parsed_pattern++ = META_ALT;
+ break;
+
+ /* End of group; reset the capture count to the maximum if we are in a (?|
+ group and/or reset the options that are tracked during parsing. Disallow
+ quantifier for a condition that is an assertion. */
+
+ case CHAR_RIGHT_PARENTHESIS:
+ okquantifier = TRUE;
+ if (top_nest != NULL && top_nest->nest_depth == nest_depth)
+ {
+ options = (options & ~PARSE_TRACKED_OPTIONS) | top_nest->options;
+ if ((top_nest->flags & NSF_RESET) != 0 &&
+ top_nest->max_group > cb->bracount)
+ cb->bracount = top_nest->max_group;
+ if ((top_nest->flags & NSF_CONDASSERT) != 0)
+ okquantifier = FALSE;
+
+ if ((top_nest->flags & NSF_ATOMICSR) != 0)
+ {
+ *parsed_pattern++ = META_KET;
+ }
+
+ if (top_nest == (nest_save *)(cb->start_workspace)) top_nest = NULL;
+ else top_nest--;
+ }
+ if (nest_depth == 0) /* Unmatched closing parenthesis */
+ {
+ errorcode = ERR22;
+ goto FAILED_BACK;
+ }
+ nest_depth--;
+ *parsed_pattern++ = META_KET;
+ break;
+ } /* End of switch on pattern character */
+ } /* End of main character scan loop */
+
+/* End of pattern reached. Check for missing ) at the end of a verb name. */
+
+if (inverbname && ptr >= ptrend)
+ {
+ errorcode = ERR60;
+ goto FAILED;
+ }
+
+/* Manage callout for the final item */
+
+PARSED_END:
+parsed_pattern = manage_callouts(ptr, &previous_callout, auto_callout,
+ parsed_pattern, cb);
+
+/* Insert trailing items for word and line matching (features provided for the
+benefit of pcre2grep). */
+
+if ((extra_options & PCRE2_EXTRA_MATCH_LINE) != 0)
+ {
+ *parsed_pattern++ = META_KET;
+ *parsed_pattern++ = META_DOLLAR;
+ }
+else if ((extra_options & PCRE2_EXTRA_MATCH_WORD) != 0)
+ {
+ *parsed_pattern++ = META_KET;
+ *parsed_pattern++ = META_ESCAPE + ESC_b;
+ }
+
+/* Terminate the parsed pattern, then return success if all groups are closed.
+Otherwise we have unclosed parentheses. */
+
+if (parsed_pattern >= parsed_pattern_end)
+ {
+ errorcode = ERR63; /* Internal error (parsed pattern overflow) */
+ goto FAILED;
+ }
+
+*parsed_pattern = META_END;
+if (nest_depth == 0) return 0;
+
+UNCLOSED_PARENTHESIS:
+errorcode = ERR14;
+
+/* Come here for all failures. */
+
+FAILED:
+cb->erroroffset = (PCRE2_SIZE)(ptr - cb->start_pattern);
+return errorcode;
+
+/* Some errors need to indicate the previous character. */
+
+FAILED_BACK:
+ptr--;
+goto FAILED;
+
+/* This failure happens several times. */
+
+BAD_VERSION_CONDITION:
+errorcode = ERR79;
+goto FAILED;
+}
+
+
+
+/*************************************************
+* Find first significant opcode *
+*************************************************/
+
+/* This is called by several functions that scan a compiled expression looking
+for a fixed first character, or an anchoring opcode etc. It skips over things
+that do not influence this. For some calls, it makes sense to skip negative
+forward and all backward assertions, and also the \b assertion; for others it
+does not.
+
+Arguments:
+ code pointer to the start of the group
+ skipassert TRUE if certain assertions are to be skipped
+
+Returns: pointer to the first significant opcode
+*/
+
+static const PCRE2_UCHAR*
+first_significant_code(PCRE2_SPTR code, BOOL skipassert)
+{
+for (;;)
+ {
+ switch ((int)*code)
+ {
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERTBACK_NA:
+ if (!skipassert) return code;
+ do code += GET(code, 1); while (*code == OP_ALT);
+ code += PRIV(OP_lengths)[*code];
+ break;
+
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_WORD_BOUNDARY:
+ if (!skipassert) return code;
+ /* Fall through */
+
+ case OP_CALLOUT:
+ case OP_CREF:
+ case OP_DNCREF:
+ case OP_RREF:
+ case OP_DNRREF:
+ case OP_FALSE:
+ case OP_TRUE:
+ code += PRIV(OP_lengths)[*code];
+ break;
+
+ case OP_CALLOUT_STR:
+ code += GET(code, 1 + 2*LINK_SIZE);
+ break;
+
+ case OP_SKIPZERO:
+ code += 2 + GET(code, 2) + LINK_SIZE;
+ break;
+
+ case OP_COND:
+ case OP_SCOND:
+ if (code[1+LINK_SIZE] != OP_FALSE || /* Not DEFINE */
+ code[GET(code, 1)] != OP_KET) /* More than one branch */
+ return code;
+ code += GET(code, 1) + 1 + LINK_SIZE;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ code += code[1] + PRIV(OP_lengths)[*code];
+ break;
+
+ default:
+ return code;
+ }
+ }
+/* Control never reaches here */
+}
+
+
+
+#ifdef SUPPORT_UNICODE
+/*************************************************
+* Get othercase range *
+*************************************************/
+
+/* This function is passed the start and end of a class range in UCP mode. It
+searches up the characters, looking for ranges of characters in the "other"
+case. Each call returns the next one, updating the start address. A character
+with multiple other cases is returned on its own with a special return value.
+
+Arguments:
+ cptr points to starting character value; updated
+ d end value
+ ocptr where to put start of othercase range
+ odptr where to put end of othercase range
+
+Yield: -1 when no more
+ 0 when a range is returned
+ >0 the CASESET offset for char with multiple other cases
+ in this case, ocptr contains the original
+*/
+
+static int
+get_othercase_range(uint32_t *cptr, uint32_t d, uint32_t *ocptr,
+ uint32_t *odptr)
+{
+uint32_t c, othercase, next;
+unsigned int co;
+
+/* Find the first character that has an other case. If it has multiple other
+cases, return its case offset value. */
+
+for (c = *cptr; c <= d; c++)
+ {
+ if ((co = UCD_CASESET(c)) != 0)
+ {
+ *ocptr = c++; /* Character that has the set */
+ *cptr = c; /* Rest of input range */
+ return (int)co;
+ }
+ if ((othercase = UCD_OTHERCASE(c)) != c) break;
+ }
+
+if (c > d) return -1; /* Reached end of range */
+
+/* Found a character that has a single other case. Search for the end of the
+range, which is either the end of the input range, or a character that has zero
+or more than one other cases. */
+
+*ocptr = othercase;
+next = othercase + 1;
+
+for (++c; c <= d; c++)
+ {
+ if ((co = UCD_CASESET(c)) != 0 || UCD_OTHERCASE(c) != next) break;
+ next++;
+ }
+
+*odptr = next - 1; /* End of othercase range */
+*cptr = c; /* Rest of input range */
+return 0;
+}
+#endif /* SUPPORT_UNICODE */
+
+
+
+/*************************************************
+* Add a character or range to a class (internal) *
+*************************************************/
+
+/* This function packages up the logic of adding a character or range of
+characters to a class. The character values in the arguments will be within the
+valid values for the current mode (8-bit, 16-bit, UTF, etc). This function is
+called only from within the "add to class" group of functions, some of which
+are recursive and mutually recursive. The external entry point is
+add_to_class().
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb compile data
+ start start of range character
+ end end of range character
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr,
+ uint32_t options, compile_block *cb, uint32_t start, uint32_t end)
+{
+uint32_t c;
+uint32_t classbits_end = (end <= 0xff ? end : 0xff);
+unsigned int n8 = 0;
+
+/* If caseless matching is required, scan the range and process alternate
+cases. In Unicode, there are 8-bit characters that have alternate cases that
+are greater than 255 and vice-versa. Sometimes we can just extend the original
+range. */
+
+if ((options & PCRE2_CASELESS) != 0)
+ {
+#ifdef SUPPORT_UNICODE
+ if ((options & (PCRE2_UTF|PCRE2_UCP)) != 0)
+ {
+ int rc;
+ uint32_t oc, od;
+
+ options &= ~PCRE2_CASELESS; /* Remove for recursive calls */
+ c = start;
+
+ while ((rc = get_othercase_range(&c, end, &oc, &od)) >= 0)
+ {
+ /* Handle a single character that has more than one other case. */
+
+ if (rc > 0) n8 += add_list_to_class_internal(classbits, uchardptr, options, cb,
+ PRIV(ucd_caseless_sets) + rc, oc);
+
+ /* Do nothing if the other case range is within the original range. */
+
+ else if (oc >= cb->class_range_start && od <= cb->class_range_end) continue;
+
+ /* Extend the original range if there is overlap, noting that if oc < c, we
+ can't have od > end because a subrange is always shorter than the basic
+ range. Otherwise, use a recursive call to add the additional range. */
+
+ else if (oc < start && od >= start - 1) start = oc; /* Extend downwards */
+ else if (od > end && oc <= end + 1)
+ {
+ end = od; /* Extend upwards */
+ if (end > classbits_end) classbits_end = (end <= 0xff ? end : 0xff);
+ }
+ else n8 += add_to_class_internal(classbits, uchardptr, options, cb, oc, od);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+
+ for (c = start; c <= classbits_end; c++)
+ {
+ SETBIT(classbits, cb->fcc[c]);
+ n8++;
+ }
+ }
+
+/* Now handle the originally supplied range. Adjust the final value according
+to the bit length - this means that the same lists of (e.g.) horizontal spaces
+can be used in all cases. */
+
+if ((options & PCRE2_UTF) == 0 && end > MAX_NON_UTF_CHAR)
+ end = MAX_NON_UTF_CHAR;
+
+if (start > cb->class_range_start && end < cb->class_range_end) return n8;
+
+/* Use the bitmap for characters < 256. Otherwise use extra data.*/
+
+for (c = start; c <= classbits_end; c++)
+ {
+ /* Regardless of start, c will always be <= 255. */
+ SETBIT(classbits, c);
+ n8++;
+ }
+
+#ifdef SUPPORT_WIDE_CHARS
+if (start <= 0xff) start = 0xff + 1;
+
+if (end >= start)
+ {
+ PCRE2_UCHAR *uchardata = *uchardptr;
+
+#ifdef SUPPORT_UNICODE
+ if ((options & PCRE2_UTF) != 0)
+ {
+ if (start < end)
+ {
+ *uchardata++ = XCL_RANGE;
+ uchardata += PRIV(ord2utf)(start, uchardata);
+ uchardata += PRIV(ord2utf)(end, uchardata);
+ }
+ else if (start == end)
+ {
+ *uchardata++ = XCL_SINGLE;
+ uchardata += PRIV(ord2utf)(start, uchardata);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Without UTF support, character values are constrained by the bit length,
+ and can only be > 256 for 16-bit and 32-bit libraries. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ {}
+#else
+ if (start < end)
+ {
+ *uchardata++ = XCL_RANGE;
+ *uchardata++ = start;
+ *uchardata++ = end;
+ }
+ else if (start == end)
+ {
+ *uchardata++ = XCL_SINGLE;
+ *uchardata++ = start;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ *uchardptr = uchardata; /* Updata extra data pointer */
+ }
+#else /* SUPPORT_WIDE_CHARS */
+ (void)uchardptr; /* Avoid compiler warning */
+#endif /* SUPPORT_WIDE_CHARS */
+
+return n8; /* Number of 8-bit characters */
+}
+
+
+
+#ifdef SUPPORT_UNICODE
+/*************************************************
+* Add a list of characters to a class (internal) *
+*************************************************/
+
+/* This function is used for adding a list of case-equivalent characters to a
+class when in UTF mode. This function is called only from within
+add_to_class_internal(), with which it is mutually recursive.
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb contains pointers to tables etc.
+ p points to row of 32-bit values, terminated by NOTACHAR
+ except character to omit; this is used when adding lists of
+ case-equivalent characters to avoid including the one we
+ already know about
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_list_to_class_internal(uint8_t *classbits, PCRE2_UCHAR **uchardptr,
+ uint32_t options, compile_block *cb, const uint32_t *p, unsigned int except)
+{
+unsigned int n8 = 0;
+while (p[0] < NOTACHAR)
+ {
+ unsigned int n = 0;
+ if (p[0] != except)
+ {
+ while(p[n+1] == p[0] + n + 1) n++;
+ n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]);
+ }
+ p += n + 1;
+ }
+return n8;
+}
+#endif
+
+
+
+/*************************************************
+* External entry point for add range to class *
+*************************************************/
+
+/* This function sets the overall range so that the internal functions can try
+to avoid duplication when handling case-independence.
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb compile data
+ start start of range character
+ end end of range character
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options,
+ compile_block *cb, uint32_t start, uint32_t end)
+{
+cb->class_range_start = start;
+cb->class_range_end = end;
+return add_to_class_internal(classbits, uchardptr, options, cb, start, end);
+}
+
+
+/*************************************************
+* External entry point for add list to class *
+*************************************************/
+
+/* This function is used for adding a list of horizontal or vertical whitespace
+characters to a class. The list must be in order so that ranges of characters
+can be detected and handled appropriately. This function sets the overall range
+so that the internal functions can try to avoid duplication when handling
+case-independence.
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb contains pointers to tables etc.
+ p points to row of 32-bit values, terminated by NOTACHAR
+ except character to omit; this is used when adding lists of
+ case-equivalent characters to avoid including the one we
+ already know about
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr, uint32_t options,
+ compile_block *cb, const uint32_t *p, unsigned int except)
+{
+unsigned int n8 = 0;
+while (p[0] < NOTACHAR)
+ {
+ unsigned int n = 0;
+ if (p[0] != except)
+ {
+ while(p[n+1] == p[0] + n + 1) n++;
+ cb->class_range_start = p[0];
+ cb->class_range_end = p[n];
+ n8 += add_to_class_internal(classbits, uchardptr, options, cb, p[0], p[n]);
+ }
+ p += n + 1;
+ }
+return n8;
+}
+
+
+
+/*************************************************
+* Add characters not in a list to a class *
+*************************************************/
+
+/* This function is used for adding the complement of a list of horizontal or
+vertical whitespace to a class. The list must be in order.
+
+Arguments:
+ classbits the bit map for characters < 256
+ uchardptr points to the pointer for extra data
+ options the options word
+ cb contains pointers to tables etc.
+ p points to row of 32-bit values, terminated by NOTACHAR
+
+Returns: the number of < 256 characters added
+ the pointer to extra data is updated
+*/
+
+static unsigned int
+add_not_list_to_class(uint8_t *classbits, PCRE2_UCHAR **uchardptr,
+ uint32_t options, compile_block *cb, const uint32_t *p)
+{
+BOOL utf = (options & PCRE2_UTF) != 0;
+unsigned int n8 = 0;
+if (p[0] > 0)
+ n8 += add_to_class(classbits, uchardptr, options, cb, 0, p[0] - 1);
+while (p[0] < NOTACHAR)
+ {
+ while (p[1] == p[0] + 1) p++;
+ n8 += add_to_class(classbits, uchardptr, options, cb, p[0] + 1,
+ (p[1] == NOTACHAR) ? (utf ? 0x10ffffu : 0xffffffffu) : p[1] - 1);
+ p++;
+ }
+return n8;
+}
+
+
+
+/*************************************************
+* Find details of duplicate group names *
+*************************************************/
+
+/* This is called from compile_branch() when it needs to know the index and
+count of duplicates in the names table when processing named backreferences,
+either directly, or as conditions.
+
+Arguments:
+ name points to the name
+ length the length of the name
+ indexptr where to put the index
+ countptr where to put the count of duplicates
+ errorcodeptr where to put an error code
+ cb the compile block
+
+Returns: TRUE if OK, FALSE if not, error code set
+*/
+
+static BOOL
+find_dupname_details(PCRE2_SPTR name, uint32_t length, int *indexptr,
+ int *countptr, int *errorcodeptr, compile_block *cb)
+{
+uint32_t i, groupnumber;
+int count;
+PCRE2_UCHAR *slot = cb->name_table;
+
+/* Find the first entry in the table */
+
+for (i = 0; i < cb->names_found; i++)
+ {
+ if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) == 0 &&
+ slot[IMM2_SIZE+length] == 0) break;
+ slot += cb->name_entry_size;
+ }
+
+/* This should not occur, because this function is called only when we know we
+have duplicate names. Give an internal error. */
+
+if (i >= cb->names_found)
+ {
+ *errorcodeptr = ERR53;
+ cb->erroroffset = name - cb->start_pattern;
+ return FALSE;
+ }
+
+/* Record the index and then see how many duplicates there are, updating the
+backref map and maximum back reference as we do. */
+
+*indexptr = i;
+count = 0;
+
+for (;;)
+ {
+ count++;
+ groupnumber = GET2(slot,0);
+ cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1;
+ if (groupnumber > cb->top_backref) cb->top_backref = groupnumber;
+ if (++i >= cb->names_found) break;
+ slot += cb->name_entry_size;
+ if (PRIV(strncmp)(name, slot+IMM2_SIZE, length) != 0 ||
+ (slot+IMM2_SIZE)[length] != 0) break;
+ }
+
+*countptr = count;
+return TRUE;
+}
+
+
+
+/*************************************************
+* Compile one branch *
+*************************************************/
+
+/* Scan the parsed pattern, compiling it into the a vector of PCRE2_UCHAR. If
+the options are changed during the branch, the pointer is used to change the
+external options bits. This function is used during the pre-compile phase when
+we are trying to find out the amount of memory needed, as well as during the
+real compile phase. The value of lengthptr distinguishes the two phases.
+
+Arguments:
+ optionsptr pointer to the option bits
+ codeptr points to the pointer to the current code point
+ pptrptr points to the current parsed pattern pointer
+ errorcodeptr points to error code variable
+ firstcuptr place to put the first required code unit
+ firstcuflagsptr place to put the first code unit flags
+ reqcuptr place to put the last required code unit
+ reqcuflagsptr place to put the last required code unit flags
+ bcptr points to current branch chain
+ cb contains pointers to tables etc.
+ lengthptr NULL during the real compile phase
+ points to length accumulator during pre-compile phase
+
+Returns: 0 There's been an error, *errorcodeptr is non-zero
+ +1 Success, this branch must match at least one character
+ -1 Success, this branch may match an empty string
+*/
+
+static int
+compile_branch(uint32_t *optionsptr, PCRE2_UCHAR **codeptr, uint32_t **pptrptr,
+ int *errorcodeptr, uint32_t *firstcuptr, uint32_t *firstcuflagsptr,
+ uint32_t *reqcuptr, uint32_t *reqcuflagsptr, branch_chain *bcptr,
+ compile_block *cb, PCRE2_SIZE *lengthptr)
+{
+int bravalue = 0;
+int okreturn = -1;
+int group_return = 0;
+uint32_t repeat_min = 0, repeat_max = 0; /* To please picky compilers */
+uint32_t greedy_default, greedy_non_default;
+uint32_t repeat_type, op_type;
+uint32_t options = *optionsptr; /* May change dynamically */
+uint32_t firstcu, reqcu;
+uint32_t zeroreqcu, zerofirstcu;
+uint32_t escape;
+uint32_t *pptr = *pptrptr;
+uint32_t meta, meta_arg;
+uint32_t firstcuflags, reqcuflags;
+uint32_t zeroreqcuflags, zerofirstcuflags;
+uint32_t req_caseopt, reqvary, tempreqvary;
+PCRE2_SIZE offset = 0;
+PCRE2_SIZE length_prevgroup = 0;
+PCRE2_UCHAR *code = *codeptr;
+PCRE2_UCHAR *last_code = code;
+PCRE2_UCHAR *orig_code = code;
+PCRE2_UCHAR *tempcode;
+PCRE2_UCHAR *previous = NULL;
+PCRE2_UCHAR op_previous;
+BOOL groupsetfirstcu = FALSE;
+BOOL had_accept = FALSE;
+BOOL matched_char = FALSE;
+BOOL previous_matched_char = FALSE;
+BOOL reset_caseful = FALSE;
+const uint8_t *cbits = cb->cbits;
+uint8_t classbits[32];
+
+/* We can fish out the UTF setting once and for all into a BOOL, but we must
+not do this for other options (e.g. PCRE2_EXTENDED) because they may change
+dynamically as we process the pattern. */
+
+#ifdef SUPPORT_UNICODE
+BOOL utf = (options & PCRE2_UTF) != 0;
+BOOL ucp = (options & PCRE2_UCP) != 0;
+#else /* No Unicode support */
+BOOL utf = FALSE;
+#endif
+
+/* Helper variables for OP_XCLASS opcode (for characters > 255). We define
+class_uchardata always so that it can be passed to add_to_class() always,
+though it will not be used in non-UTF 8-bit cases. This avoids having to supply
+alternative calls for the different cases. */
+
+PCRE2_UCHAR *class_uchardata;
+#ifdef SUPPORT_WIDE_CHARS
+BOOL xclass;
+PCRE2_UCHAR *class_uchardata_base;
+#endif
+
+/* Set up the default and non-default settings for greediness */
+
+greedy_default = ((options & PCRE2_UNGREEDY) != 0);
+greedy_non_default = greedy_default ^ 1;
+
+/* Initialize no first unit, no required unit. REQ_UNSET means "no char
+matching encountered yet". It gets changed to REQ_NONE if we hit something that
+matches a non-fixed first unit; reqcu just remains unset if we never find one.
+
+When we hit a repeat whose minimum is zero, we may have to adjust these values
+to take the zero repeat into account. This is implemented by setting them to
+zerofirstcu and zeroreqcu when such a repeat is encountered. The individual
+item types that can be repeated set these backoff variables appropriately. */
+
+firstcu = reqcu = zerofirstcu = zeroreqcu = 0;
+firstcuflags = reqcuflags = zerofirstcuflags = zeroreqcuflags = REQ_UNSET;
+
+/* The variable req_caseopt contains either the REQ_CASELESS bit or zero,
+according to the current setting of the caseless flag. The REQ_CASELESS value
+leaves the lower 28 bit empty. It is added into the firstcu or reqcu variables
+to record the case status of the value. This is used only for ASCII characters.
+*/
+
+req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0;
+
+/* Switch on next META item until the end of the branch */
+
+for (;; pptr++)
+ {
+#ifdef SUPPORT_WIDE_CHARS
+ BOOL xclass_has_prop;
+#endif
+ BOOL negate_class;
+ BOOL should_flip_negation;
+ BOOL match_all_or_no_wide_chars;
+ BOOL possessive_quantifier;
+ BOOL note_group_empty;
+ int class_has_8bitchar;
+ uint32_t mclength;
+ uint32_t skipunits;
+ uint32_t subreqcu, subfirstcu;
+ uint32_t groupnumber;
+ uint32_t verbarglen, verbculen;
+ uint32_t subreqcuflags, subfirstcuflags;
+ open_capitem *oc;
+ PCRE2_UCHAR mcbuffer[8];
+
+ /* Get next META item in the pattern and its potential argument. */
+
+ meta = META_CODE(*pptr);
+ meta_arg = META_DATA(*pptr);
+
+ /* If we are in the pre-compile phase, accumulate the length used for the
+ previous cycle of this loop, unless the next item is a quantifier. */
+
+ if (lengthptr != NULL)
+ {
+ if (code > cb->start_workspace + cb->workspace_size -
+ WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */
+ {
+ *errorcodeptr = (code >= cb->start_workspace + cb->workspace_size)?
+ ERR52 : ERR86;
+ return 0;
+ }
+
+ /* There is at least one situation where code goes backwards: this is the
+ case of a zero quantifier after a class (e.g. [ab]{0}). When the quantifier
+ is processed, the whole class is eliminated. However, it is created first,
+ so we have to allow memory for it. Therefore, don't ever reduce the length
+ at this point. */
+
+ if (code < last_code) code = last_code;
+
+ /* If the next thing is not a quantifier, we add the length of the previous
+ item into the total, and reset the code pointer to the start of the
+ workspace. Otherwise leave the previous item available to be quantified. */
+
+ if (meta < META_ASTERISK || meta > META_MINMAX_QUERY)
+ {
+ if (OFLOW_MAX - *lengthptr < (PCRE2_SIZE)(code - orig_code))
+ {
+ *errorcodeptr = ERR20; /* Integer overflow */
+ return 0;
+ }
+ *lengthptr += (PCRE2_SIZE)(code - orig_code);
+ if (*lengthptr > MAX_PATTERN_SIZE)
+ {
+ *errorcodeptr = ERR20; /* Pattern is too large */
+ return 0;
+ }
+ code = orig_code;
+ }
+
+ /* Remember where this code item starts so we can catch the "backwards"
+ case above next time round. */
+
+ last_code = code;
+ }
+
+ /* Process the next parsed pattern item. If it is not a quantifier, remember
+ where it starts so that it can be quantified when a quantifier follows.
+ Checking for the legality of quantifiers happens in parse_regex(), except for
+ a quantifier after an assertion that is a condition. */
+
+ if (meta < META_ASTERISK || meta > META_MINMAX_QUERY)
+ {
+ previous = code;
+ if (matched_char && !had_accept) okreturn = 1;
+ }
+
+ previous_matched_char = matched_char;
+ matched_char = FALSE;
+ note_group_empty = FALSE;
+ skipunits = 0; /* Default value for most subgroups */
+
+ switch(meta)
+ {
+ /* ===================================================================*/
+ /* The branch terminates at pattern end or | or ) */
+
+ case META_END:
+ case META_ALT:
+ case META_KET:
+ *firstcuptr = firstcu;
+ *firstcuflagsptr = firstcuflags;
+ *reqcuptr = reqcu;
+ *reqcuflagsptr = reqcuflags;
+ *codeptr = code;
+ *pptrptr = pptr;
+ return okreturn;
+
+
+ /* ===================================================================*/
+ /* Handle single-character metacharacters. In multiline mode, ^ disables
+ the setting of any following char as a first character. */
+
+ case META_CIRCUMFLEX:
+ if ((options & PCRE2_MULTILINE) != 0)
+ {
+ if (firstcuflags == REQ_UNSET)
+ zerofirstcuflags = firstcuflags = REQ_NONE;
+ *code++ = OP_CIRCM;
+ }
+ else *code++ = OP_CIRC;
+ break;
+
+ case META_DOLLAR:
+ *code++ = ((options & PCRE2_MULTILINE) != 0)? OP_DOLLM : OP_DOLL;
+ break;
+
+ /* There can never be a first char if '.' is first, whatever happens about
+ repeats. The value of reqcu doesn't change either. */
+
+ case META_DOT:
+ matched_char = TRUE;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+ *code++ = ((options & PCRE2_DOTALL) != 0)? OP_ALLANY: OP_ANY;
+ break;
+
+
+ /* ===================================================================*/
+ /* Empty character classes are allowed if PCRE2_ALLOW_EMPTY_CLASS is set.
+ Otherwise, an initial ']' is taken as a data character. When empty classes
+ are allowed, [] must always fail, so generate OP_FAIL, whereas [^] must
+ match any character, so generate OP_ALLANY. */
+
+ case META_CLASS_EMPTY:
+ case META_CLASS_EMPTY_NOT:
+ matched_char = TRUE;
+ *code++ = (meta == META_CLASS_EMPTY_NOT)? OP_ALLANY : OP_FAIL;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ break;
+
+
+ /* ===================================================================*/
+ /* Non-empty character class. If the included characters are all < 256, we
+ build a 32-byte bitmap of the permitted characters, except in the special
+ case where there is only one such character. For negated classes, we build
+ the map as usual, then invert it at the end. However, we use a different
+ opcode so that data characters > 255 can be handled correctly.
+
+ If the class contains characters outside the 0-255 range, a different
+ opcode is compiled. It may optionally have a bit map for characters < 256,
+ but those above are are explicitly listed afterwards. A flag code unit
+ tells whether the bitmap is present, and whether this is a negated class or
+ not. */
+
+ case META_CLASS_NOT:
+ case META_CLASS:
+ matched_char = TRUE;
+ negate_class = meta == META_CLASS_NOT;
+
+ /* We can optimize the case of a single character in a class by generating
+ OP_CHAR or OP_CHARI if it's positive, or OP_NOT or OP_NOTI if it's
+ negative. In the negative case there can be no first char if this item is
+ first, whatever repeat count may follow. In the case of reqcu, save the
+ previous value for reinstating. */
+
+ /* NOTE: at present this optimization is not effective if the only
+ character in a class in 32-bit, non-UCP mode has its top bit set. */
+
+ if (pptr[1] < META_END && pptr[2] == META_CLASS_END)
+ {
+#ifdef SUPPORT_UNICODE
+ uint32_t d;
+#endif
+ uint32_t c = pptr[1];
+
+ pptr += 2; /* Move on to class end */
+ if (meta == META_CLASS) /* A positive one-char class can be */
+ { /* handled as a normal literal character. */
+ meta = c; /* Set up the character */
+ goto NORMAL_CHAR_SET;
+ }
+
+ /* Handle a negative one-character class */
+
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+
+ /* For caseless UTF or UCP mode, check whether this character has more
+ than one other case. If so, generate a special OP_NOTPROP item instead of
+ OP_NOTI. */
+
+#ifdef SUPPORT_UNICODE
+ if ((utf||ucp) && (options & PCRE2_CASELESS) != 0 &&
+ (d = UCD_CASESET(c)) != 0)
+ {
+ *code++ = OP_NOTPROP;
+ *code++ = PT_CLIST;
+ *code++ = d;
+ break; /* We are finished with this class */
+ }
+#endif
+ /* Char has only one other case, or UCP not available */
+
+ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_NOTI: OP_NOT;
+ code += PUTCHAR(c, code);
+ break; /* We are finished with this class */
+ } /* End of 1-char optimization */
+
+ /* Handle character classes that contain more than just one literal
+ character. If there are exactly two characters in a positive class, see if
+ they are case partners. This can be optimized to generate a caseless single
+ character match (which also sets first/required code units if relevant). */
+
+ if (meta == META_CLASS && pptr[1] < META_END && pptr[2] < META_END &&
+ pptr[3] == META_CLASS_END)
+ {
+ uint32_t c = pptr[1];
+
+#ifdef SUPPORT_UNICODE
+ if (UCD_CASESET(c) == 0)
+#endif
+ {
+ uint32_t d;
+
+#ifdef SUPPORT_UNICODE
+ if ((utf || ucp) && c > 127) d = UCD_OTHERCASE(c); else
+#endif
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) d = c; else
+#endif
+ d = TABLE_GET(c, cb->fcc, c);
+ }
+
+ if (c != d && pptr[2] == d)
+ {
+ pptr += 3; /* Move on to class end */
+ meta = c;
+ if ((options & PCRE2_CASELESS) == 0)
+ {
+ reset_caseful = TRUE;
+ options |= PCRE2_CASELESS;
+ req_caseopt = REQ_CASELESS;
+ }
+ goto CLASS_CASELESS_CHAR;
+ }
+ }
+ }
+
+ /* If a non-extended class contains a negative special such as \S, we need
+ to flip the negation flag at the end, so that support for characters > 255
+ works correctly (they are all included in the class). An extended class may
+ need to insert specific matching or non-matching code for wide characters.
+ */
+
+ should_flip_negation = match_all_or_no_wide_chars = FALSE;
+
+ /* Extended class (xclass) will be used when characters > 255
+ might match. */
+
+#ifdef SUPPORT_WIDE_CHARS
+ xclass = FALSE;
+ class_uchardata = code + LINK_SIZE + 2; /* For XCLASS items */
+ class_uchardata_base = class_uchardata; /* Save the start */
+#endif
+
+ /* For optimization purposes, we track some properties of the class:
+ class_has_8bitchar will be non-zero if the class contains at least one
+ character with a code point less than 256; xclass_has_prop will be TRUE if
+ Unicode property checks are present in the class. */
+
+ class_has_8bitchar = 0;
+#ifdef SUPPORT_WIDE_CHARS
+ xclass_has_prop = FALSE;
+#endif
+
+ /* Initialize the 256-bit (32-byte) bit map to all zeros. We build the map
+ in a temporary bit of memory, in case the class contains fewer than two
+ 8-bit characters because in that case the compiled code doesn't use the bit
+ map. */
+
+ memset(classbits, 0, 32 * sizeof(uint8_t));
+
+ /* Process items until META_CLASS_END is reached. */
+
+ while ((meta = *(++pptr)) != META_CLASS_END)
+ {
+ /* Handle POSIX classes such as [:alpha:] etc. */
+
+ if (meta == META_POSIX || meta == META_POSIX_NEG)
+ {
+ BOOL local_negate = (meta == META_POSIX_NEG);
+ int posix_class = *(++pptr);
+ int taboffset, tabopt;
+ uint8_t pbits[32];
+
+ should_flip_negation = local_negate; /* Note negative special */
+
+ /* If matching is caseless, upper and lower are converted to alpha.
+ This relies on the fact that the class table starts with alpha,
+ lower, upper as the first 3 entries. */
+
+ if ((options & PCRE2_CASELESS) != 0 && posix_class <= 2)
+ posix_class = 0;
+
+ /* When PCRE2_UCP is set, some of the POSIX classes are converted to
+ different escape sequences that use Unicode properties \p or \P.
+ Others that are not available via \p or \P have to generate
+ XCL_PROP/XCL_NOTPROP directly, which is done here. */
+
+#ifdef SUPPORT_UNICODE
+ if ((options & PCRE2_UCP) != 0) switch(posix_class)
+ {
+ case PC_GRAPH:
+ case PC_PRINT:
+ case PC_PUNCT:
+ *class_uchardata++ = local_negate? XCL_NOTPROP : XCL_PROP;
+ *class_uchardata++ = (PCRE2_UCHAR)
+ ((posix_class == PC_GRAPH)? PT_PXGRAPH :
+ (posix_class == PC_PRINT)? PT_PXPRINT : PT_PXPUNCT);
+ *class_uchardata++ = 0;
+ xclass_has_prop = TRUE;
+ goto CONTINUE_CLASS;
+
+ /* For the other POSIX classes (ascii, xdigit) we are going to
+ fall through to the non-UCP case and build a bit map for
+ characters with code points less than 256. However, if we are in
+ a negated POSIX class, characters with code points greater than
+ 255 must either all match or all not match, depending on whether
+ the whole class is not or is negated. For example, for
+ [[:^ascii:]... they must all match, whereas for [^[:^xdigit:]...
+ they must not.
+
+ In the special case where there are no xclass items, this is
+ automatically handled by the use of OP_CLASS or OP_NCLASS, but an
+ explicit range is needed for OP_XCLASS. Setting a flag here
+ causes the range to be generated later when it is known that
+ OP_XCLASS is required. In the 8-bit library this is relevant only in
+ utf mode, since no wide characters can exist otherwise. */
+
+ default:
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+#endif
+ match_all_or_no_wide_chars |= local_negate;
+ break;
+ }
+#endif /* SUPPORT_UNICODE */
+
+ /* In the non-UCP case, or when UCP makes no difference, we build the
+ bit map for the POSIX class in a chunk of local store because we may
+ be adding and subtracting from it, and we don't want to subtract bits
+ that may be in the main map already. At the end we or the result into
+ the bit map that is being built. */
+
+ posix_class *= 3;
+
+ /* Copy in the first table (always present) */
+
+ memcpy(pbits, cbits + posix_class_maps[posix_class],
+ 32 * sizeof(uint8_t));
+
+ /* If there is a second table, add or remove it as required. */
+
+ taboffset = posix_class_maps[posix_class + 1];
+ tabopt = posix_class_maps[posix_class + 2];
+
+ if (taboffset >= 0)
+ {
+ if (tabopt >= 0)
+ for (int i = 0; i < 32; i++) pbits[i] |= cbits[(int)i + taboffset];
+ else
+ for (int i = 0; i < 32; i++) pbits[i] &= ~cbits[(int)i + taboffset];
+ }
+
+ /* Now see if we need to remove any special characters. An option
+ value of 1 removes vertical space and 2 removes underscore. */
+
+ if (tabopt < 0) tabopt = -tabopt;
+ if (tabopt == 1) pbits[1] &= ~0x3c;
+ else if (tabopt == 2) pbits[11] &= 0x7f;
+
+ /* Add the POSIX table or its complement into the main table that is
+ being built and we are done. */
+
+ if (local_negate)
+ for (int i = 0; i < 32; i++) classbits[i] |= (uint8_t)(~pbits[i]);
+ else
+ for (int i = 0; i < 32; i++) classbits[i] |= pbits[i];
+
+ /* Every class contains at least one < 256 character. */
+
+ class_has_8bitchar = 1;
+ goto CONTINUE_CLASS; /* End of POSIX handling */
+ }
+
+ /* Other than POSIX classes, the only items we should encounter are
+ \d-type escapes and literal characters (possibly as ranges). */
+
+ if (meta == META_BIGVALUE)
+ {
+ meta = *(++pptr);
+ goto CLASS_LITERAL;
+ }
+
+ /* Any other non-literal must be an escape */
+
+ if (meta >= META_END)
+ {
+ if (META_CODE(meta) != META_ESCAPE)
+ {
+#ifdef DEBUG_SHOW_PARSED
+ fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x "
+ "in character class\n", meta);
+#endif
+ *errorcodeptr = ERR89; /* Internal error - unrecognized. */
+ return 0;
+ }
+ escape = META_DATA(meta);
+
+ /* Every class contains at least one < 256 character. */
+
+ class_has_8bitchar++;
+
+ switch(escape)
+ {
+ case ESC_d:
+ for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_digit];
+ break;
+
+ case ESC_D:
+ should_flip_negation = TRUE;
+ for (int i = 0; i < 32; i++)
+ classbits[i] |= (uint8_t)(~cbits[i+cbit_digit]);
+ break;
+
+ case ESC_w:
+ for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_word];
+ break;
+
+ case ESC_W:
+ should_flip_negation = TRUE;
+ for (int i = 0; i < 32; i++)
+ classbits[i] |= (uint8_t)(~cbits[i+cbit_word]);
+ break;
+
+ /* Perl 5.004 onwards omitted VT from \s, but restored it at Perl
+ 5.18. Before PCRE 8.34, we had to preserve the VT bit if it was
+ previously set by something earlier in the character class.
+ Luckily, the value of CHAR_VT is 0x0b in both ASCII and EBCDIC, so
+ we could just adjust the appropriate bit. From PCRE 8.34 we no
+ longer treat \s and \S specially. */
+
+ case ESC_s:
+ for (int i = 0; i < 32; i++) classbits[i] |= cbits[i+cbit_space];
+ break;
+
+ case ESC_S:
+ should_flip_negation = TRUE;
+ for (int i = 0; i < 32; i++)
+ classbits[i] |= (uint8_t)(~cbits[i+cbit_space]);
+ break;
+
+ /* When adding the horizontal or vertical space lists to a class, or
+ their complements, disable PCRE2_CASELESS, because it justs wastes
+ time, and in the "not-x" UTF cases can create unwanted duplicates in
+ the XCLASS list (provoked by characters that have more than one other
+ case and by both cases being in the same "not-x" sublist). */
+
+ case ESC_h:
+ (void)add_list_to_class(classbits, &class_uchardata,
+ options & ~PCRE2_CASELESS, cb, PRIV(hspace_list), NOTACHAR);
+ break;
+
+ case ESC_H:
+ (void)add_not_list_to_class(classbits, &class_uchardata,
+ options & ~PCRE2_CASELESS, cb, PRIV(hspace_list));
+ break;
+
+ case ESC_v:
+ (void)add_list_to_class(classbits, &class_uchardata,
+ options & ~PCRE2_CASELESS, cb, PRIV(vspace_list), NOTACHAR);
+ break;
+
+ case ESC_V:
+ (void)add_not_list_to_class(classbits, &class_uchardata,
+ options & ~PCRE2_CASELESS, cb, PRIV(vspace_list));
+ break;
+
+ /* If Unicode is not supported, \P and \p are not allowed and are
+ faulted at parse time, so will never appear here. */
+
+#ifdef SUPPORT_UNICODE
+ case ESC_p:
+ case ESC_P:
+ {
+ uint32_t ptype = *(++pptr) >> 16;
+ uint32_t pdata = *pptr & 0xffff;
+ *class_uchardata++ = (escape == ESC_p)? XCL_PROP : XCL_NOTPROP;
+ *class_uchardata++ = ptype;
+ *class_uchardata++ = pdata;
+ xclass_has_prop = TRUE;
+ class_has_8bitchar--; /* Undo! */
+ }
+ break;
+#endif
+ }
+
+ goto CONTINUE_CLASS;
+ } /* End handling \d-type escapes */
+
+ /* A literal character may be followed by a range meta. At parse time
+ there are checks for out-of-order characters, for ranges where the two
+ characters are equal, and for hyphens that cannot indicate a range. At
+ this point, therefore, no checking is needed. */
+
+ else
+ {
+ uint32_t c, d;
+
+ CLASS_LITERAL:
+ c = d = meta;
+
+ /* Remember if \r or \n were explicitly used */
+
+ if (c == CHAR_CR || c == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF;
+
+ /* Process a character range */
+
+ if (pptr[1] == META_RANGE_LITERAL || pptr[1] == META_RANGE_ESCAPED)
+ {
+#ifdef EBCDIC
+ BOOL range_is_literal = (pptr[1] == META_RANGE_LITERAL);
+#endif
+ pptr += 2;
+ d = *pptr;
+ if (d == META_BIGVALUE) d = *(++pptr);
+
+ /* Remember an explicit \r or \n, and add the range to the class. */
+
+ if (d == CHAR_CR || d == CHAR_NL) cb->external_flags |= PCRE2_HASCRORLF;
+
+ /* In an EBCDIC environment, Perl treats alphabetic ranges specially
+ because there are holes in the encoding, and simply using the range
+ A-Z (for example) would include the characters in the holes. This
+ applies only to literal ranges; [\xC1-\xE9] is different to [A-Z]. */
+
+#ifdef EBCDIC
+ if (range_is_literal &&
+ (cb->ctypes[c] & ctype_letter) != 0 &&
+ (cb->ctypes[d] & ctype_letter) != 0 &&
+ (c <= CHAR_z) == (d <= CHAR_z))
+ {
+ uint32_t uc = (d <= CHAR_z)? 0 : 64;
+ uint32_t C = c - uc;
+ uint32_t D = d - uc;
+
+ if (C <= CHAR_i)
+ {
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, C + uc,
+ ((D < CHAR_i)? D : CHAR_i) + uc);
+ C = CHAR_j;
+ }
+
+ if (C <= D && C <= CHAR_r)
+ {
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, C + uc,
+ ((D < CHAR_r)? D : CHAR_r) + uc);
+ C = CHAR_s;
+ }
+
+ if (C <= D)
+ {
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, C + uc,
+ D + uc);
+ }
+ }
+ else
+#endif
+ /* Not an EBCDIC special range */
+
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, c, d);
+ goto CONTINUE_CLASS; /* Go get the next char in the class */
+ } /* End of range handling */
+
+
+ /* Handle a single character. */
+
+ class_has_8bitchar +=
+ add_to_class(classbits, &class_uchardata, options, cb, meta, meta);
+ }
+
+ /* Continue to the next item in the class. */
+
+ CONTINUE_CLASS:
+
+#ifdef SUPPORT_WIDE_CHARS
+ /* If any wide characters or Unicode properties have been encountered,
+ set xclass = TRUE. Then, in the pre-compile phase, accumulate the length
+ of the extra data and reset the pointer. This is so that very large
+ classes that contain a zillion wide characters or Unicode property tests
+ do not overwrite the workspace (which is on the stack). */
+
+ if (class_uchardata > class_uchardata_base)
+ {
+ xclass = TRUE;
+ if (lengthptr != NULL)
+ {
+ *lengthptr += class_uchardata - class_uchardata_base;
+ class_uchardata = class_uchardata_base;
+ }
+ }
+#endif
+
+ continue; /* Needed to avoid error when not supporting wide chars */
+ } /* End of main class-processing loop */
+
+ /* If this class is the first thing in the branch, there can be no first
+ char setting, whatever the repeat count. Any reqcu setting must remain
+ unchanged after any kind of repeat. */
+
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+
+ /* If there are characters with values > 255, or Unicode property settings
+ (\p or \P), we have to compile an extended class, with its own opcode,
+ unless there were no property settings and there was a negated special such
+ as \S in the class, and PCRE2_UCP is not set, because in that case all
+ characters > 255 are in or not in the class, so any that were explicitly
+ given as well can be ignored.
+
+ In the UCP case, if certain negated POSIX classes ([:^ascii:] or
+ [^:xdigit:]) were present in a class, we either have to match or not match
+ all wide characters (depending on whether the whole class is or is not
+ negated). This requirement is indicated by match_all_or_no_wide_chars being
+ true. We do this by including an explicit range, which works in both cases.
+ This applies only in UTF and 16-bit and 32-bit non-UTF modes, since there
+ cannot be any wide characters in 8-bit non-UTF mode.
+
+ When there *are* properties in a positive UTF-8 or any 16-bit or 32_bit
+ class where \S etc is present without PCRE2_UCP, causing an extended class
+ to be compiled, we make sure that all characters > 255 are included by
+ forcing match_all_or_no_wide_chars to be true.
+
+ If, when generating an xclass, there are no characters < 256, we can omit
+ the bitmap in the actual compiled code. */
+
+#ifdef SUPPORT_WIDE_CHARS /* Defined for 16/32 bits, or 8-bit with Unicode */
+ if (xclass && (
+#ifdef SUPPORT_UNICODE
+ (options & PCRE2_UCP) != 0 ||
+#endif
+ xclass_has_prop || !should_flip_negation))
+ {
+ if (match_all_or_no_wide_chars || (
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ utf &&
+#endif
+ should_flip_negation && !negate_class && (options & PCRE2_UCP) == 0))
+ {
+ *class_uchardata++ = XCL_RANGE;
+ if (utf) /* Will always be utf in the 8-bit library */
+ {
+ class_uchardata += PRIV(ord2utf)(0x100, class_uchardata);
+ class_uchardata += PRIV(ord2utf)(MAX_UTF_CODE_POINT, class_uchardata);
+ }
+ else /* Can only happen for the 16-bit & 32-bit libraries */
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 16
+ *class_uchardata++ = 0x100;
+ *class_uchardata++ = 0xffffu;
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ *class_uchardata++ = 0x100;
+ *class_uchardata++ = 0xffffffffu;
+#endif
+ }
+ }
+ *class_uchardata++ = XCL_END; /* Marks the end of extra data */
+ *code++ = OP_XCLASS;
+ code += LINK_SIZE;
+ *code = negate_class? XCL_NOT:0;
+ if (xclass_has_prop) *code |= XCL_HASPROP;
+
+ /* If the map is required, move up the extra data to make room for it;
+ otherwise just move the code pointer to the end of the extra data. */
+
+ if (class_has_8bitchar > 0)
+ {
+ *code++ |= XCL_MAP;
+ (void)memmove(code + (32 / sizeof(PCRE2_UCHAR)), code,
+ CU2BYTES(class_uchardata - code));
+ if (negate_class && !xclass_has_prop)
+ {
+ /* Using 255 ^ instead of ~ avoids clang sanitize warning. */
+ for (int i = 0; i < 32; i++) classbits[i] = 255 ^ classbits[i];
+ }
+ memcpy(code, classbits, 32);
+ code = class_uchardata + (32 / sizeof(PCRE2_UCHAR));
+ }
+ else code = class_uchardata;
+
+ /* Now fill in the complete length of the item */
+
+ PUT(previous, 1, (int)(code - previous));
+ break; /* End of class handling */
+ }
+#endif /* SUPPORT_WIDE_CHARS */
+
+ /* If there are no characters > 255, or they are all to be included or
+ excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the
+ whole class was negated and whether there were negative specials such as \S
+ (non-UCP) in the class. Then copy the 32-byte map into the code vector,
+ negating it if necessary. */
+
+ *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS;
+ if (lengthptr == NULL) /* Save time in the pre-compile phase */
+ {
+ if (negate_class)
+ {
+ /* Using 255 ^ instead of ~ avoids clang sanitize warning. */
+ for (int i = 0; i < 32; i++) classbits[i] = 255 ^ classbits[i];
+ }
+ memcpy(code, classbits, 32);
+ }
+ code += 32 / sizeof(PCRE2_UCHAR);
+ break; /* End of class processing */
+
+
+ /* ===================================================================*/
+ /* Deal with (*VERB)s. */
+
+ /* Check for open captures before ACCEPT and close those that are within
+ the same assertion level, also converting ACCEPT to ASSERT_ACCEPT in an
+ assertion. In the first pass, just accumulate the length required;
+ otherwise hitting (*ACCEPT) inside many nested parentheses can cause
+ workspace overflow. Do not set firstcu after *ACCEPT. */
+
+ case META_ACCEPT:
+ cb->had_accept = had_accept = TRUE;
+ for (oc = cb->open_caps;
+ oc != NULL && oc->assert_depth >= cb->assert_depth;
+ oc = oc->next)
+ {
+ if (lengthptr != NULL)
+ {
+ *lengthptr += CU2BYTES(1) + IMM2_SIZE;
+ }
+ else
+ {
+ *code++ = OP_CLOSE;
+ PUT2INC(code, 0, oc->number);
+ }
+ }
+ *code++ = (cb->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ break;
+
+ case META_PRUNE:
+ case META_SKIP:
+ cb->had_pruneorskip = TRUE;
+ /* Fall through */
+ case META_COMMIT:
+ case META_FAIL:
+ *code++ = verbops[(meta - META_MARK) >> 16];
+ break;
+
+ case META_THEN:
+ cb->external_flags |= PCRE2_HASTHEN;
+ *code++ = OP_THEN;
+ break;
+
+ /* Handle verbs with arguments. Arguments can be very long, especially in
+ 16- and 32-bit modes, and can overflow the workspace in the first pass.
+ However, the argument length is constrained to be small enough to fit in
+ one code unit. This check happens in parse_regex(). In the first pass,
+ instead of putting the argument into memory, we just update the length
+ counter and set up an empty argument. */
+
+ case META_THEN_ARG:
+ cb->external_flags |= PCRE2_HASTHEN;
+ goto VERB_ARG;
+
+ case META_PRUNE_ARG:
+ case META_SKIP_ARG:
+ cb->had_pruneorskip = TRUE;
+ /* Fall through */
+ case META_MARK:
+ case META_COMMIT_ARG:
+ VERB_ARG:
+ *code++ = verbops[(meta - META_MARK) >> 16];
+ /* The length is in characters. */
+ verbarglen = *(++pptr);
+ verbculen = 0;
+ tempcode = code++;
+ for (int i = 0; i < (int)verbarglen; i++)
+ {
+ meta = *(++pptr);
+#ifdef SUPPORT_UNICODE
+ if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else
+#endif
+ {
+ mclength = 1;
+ mcbuffer[0] = meta;
+ }
+ if (lengthptr != NULL) *lengthptr += mclength; else
+ {
+ memcpy(code, mcbuffer, CU2BYTES(mclength));
+ code += mclength;
+ verbculen += mclength;
+ }
+ }
+
+ *tempcode = verbculen; /* Fill in the code unit length */
+ *code++ = 0; /* Terminating zero */
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle options change. The new setting must be passed back for use in
+ subsequent branches. Reset the greedy defaults and the case value for
+ firstcu and reqcu. */
+
+ case META_OPTIONS:
+ *optionsptr = options = *(++pptr);
+ greedy_default = ((options & PCRE2_UNGREEDY) != 0);
+ greedy_non_default = greedy_default ^ 1;
+ req_caseopt = ((options & PCRE2_CASELESS) != 0)? REQ_CASELESS : 0;
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle conditional subpatterns. The case of (?(Rdigits) is ambiguous
+ because it could be a numerical check on recursion, or a name check on a
+ group's being set. The pre-pass sets up META_COND_RNUMBER as a name so that
+ we can handle it either way. We first try for a name; if not found, process
+ the number. */
+
+ case META_COND_RNUMBER: /* (?(Rdigits) */
+ case META_COND_NAME: /* (?(name) or (?'name') or ?(<name>) */
+ case META_COND_RNAME: /* (?(R&name) - test for recursion */
+ bravalue = OP_COND;
+ {
+ int count, index;
+ unsigned int i;
+ PCRE2_SPTR name;
+ named_group *ng = cb->named_groups;
+ uint32_t length = *(++pptr);
+
+ GETPLUSOFFSET(offset, pptr);
+ name = cb->start_pattern + offset;
+
+ /* In the first pass, the names generated in the pre-pass are available,
+ but the main name table has not yet been created. Scan the list of names
+ generated in the pre-pass in order to get a number and whether or not
+ this name is duplicated. If it is not duplicated, we can handle it as a
+ numerical group. */
+
+ for (i = 0; i < cb->names_found; i++, ng++)
+ {
+ if (length == ng->length &&
+ PRIV(strncmp)(name, ng->name, length) == 0)
+ {
+ if (!ng->isdup)
+ {
+ code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF;
+ PUT2(code, 2+LINK_SIZE, ng->number);
+ if (ng->number > cb->top_backref) cb->top_backref = ng->number;
+ skipunits = 1+IMM2_SIZE;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+ }
+ break; /* Found a duplicated name */
+ }
+ }
+
+ /* If the name was not found we have a bad reference, unless we are
+ dealing with R<digits>, which is treated as a recursion test by number.
+ */
+
+ if (i >= cb->names_found)
+ {
+ groupnumber = 0;
+ if (meta == META_COND_RNUMBER)
+ {
+ for (i = 1; i < length; i++)
+ {
+ groupnumber = groupnumber * 10 + name[i] - CHAR_0;
+ if (groupnumber > MAX_GROUP_NUMBER)
+ {
+ *errorcodeptr = ERR61;
+ cb->erroroffset = offset + i;
+ return 0;
+ }
+ }
+ }
+
+ if (meta != META_COND_RNUMBER || groupnumber > cb->bracount)
+ {
+ *errorcodeptr = ERR15;
+ cb->erroroffset = offset;
+ return 0;
+ }
+
+ /* (?Rdigits) treated as a recursion reference by number. A value of
+ zero (which is the result of both (?R) and (?R0)) means "any", and is
+ translated into RREF_ANY (which is 0xffff). */
+
+ if (groupnumber == 0) groupnumber = RREF_ANY;
+ code[1+LINK_SIZE] = OP_RREF;
+ PUT2(code, 2+LINK_SIZE, groupnumber);
+ skipunits = 1+IMM2_SIZE;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+ }
+
+ /* A duplicated name was found. Note that if an R<digits> name is found
+ (META_COND_RNUMBER), it is a reference test, not a recursion test. */
+
+ code[1+LINK_SIZE] = (meta == META_COND_RNAME)? OP_RREF : OP_CREF;
+
+ /* We have a duplicated name. In the compile pass we have to search the
+ main table in order to get the index and count values. */
+
+ count = 0; /* Values for first pass (avoids compiler warning) */
+ index = 0;
+ if (lengthptr == NULL && !find_dupname_details(name, length, &index,
+ &count, errorcodeptr, cb)) return 0;
+
+ /* Add one to the opcode to change CREF/RREF into DNCREF/DNRREF and
+ insert appropriate data values. */
+
+ code[1+LINK_SIZE]++;
+ skipunits = 1+2*IMM2_SIZE;
+ PUT2(code, 2+LINK_SIZE, index);
+ PUT2(code, 2+LINK_SIZE+IMM2_SIZE, count);
+ }
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ /* The DEFINE condition is always false. Its internal groups may never
+ be called, so matched_char must remain false, hence the jump to
+ GROUP_PROCESS rather than GROUP_PROCESS_NOTE_EMPTY. */
+
+ case META_COND_DEFINE:
+ bravalue = OP_COND;
+ GETPLUSOFFSET(offset, pptr);
+ code[1+LINK_SIZE] = OP_DEFINE;
+ skipunits = 1;
+ goto GROUP_PROCESS;
+
+ /* Conditional test of a group's being set. */
+
+ case META_COND_NUMBER:
+ bravalue = OP_COND;
+ GETPLUSOFFSET(offset, pptr);
+ groupnumber = *(++pptr);
+ if (groupnumber > cb->bracount)
+ {
+ *errorcodeptr = ERR15;
+ cb->erroroffset = offset;
+ return 0;
+ }
+ if (groupnumber > cb->top_backref) cb->top_backref = groupnumber;
+ offset -= 2; /* Point at initial ( for too many branches error */
+ code[1+LINK_SIZE] = OP_CREF;
+ skipunits = 1+IMM2_SIZE;
+ PUT2(code, 2+LINK_SIZE, groupnumber);
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ /* Test for the PCRE2 version. */
+
+ case META_COND_VERSION:
+ bravalue = OP_COND;
+ if (pptr[1] > 0)
+ code[1+LINK_SIZE] = ((PCRE2_MAJOR > pptr[2]) ||
+ (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR >= pptr[3]))?
+ OP_TRUE : OP_FALSE;
+ else
+ code[1+LINK_SIZE] = (PCRE2_MAJOR == pptr[2] && PCRE2_MINOR == pptr[3])?
+ OP_TRUE : OP_FALSE;
+ skipunits = 1;
+ pptr += 3;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ /* The condition is an assertion, possibly preceded by a callout. */
+
+ case META_COND_ASSERT:
+ bravalue = OP_COND;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+
+ /* ===================================================================*/
+ /* Handle all kinds of nested bracketed groups. The non-capturing,
+ non-conditional cases are here; others come to GROUP_PROCESS via goto. */
+
+ case META_LOOKAHEAD:
+ bravalue = OP_ASSERT;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ case META_LOOKAHEAD_NA:
+ bravalue = OP_ASSERT_NA;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ /* Optimize (?!) to (*FAIL) unless it is quantified - which is a weird
+ thing to do, but Perl allows all assertions to be quantified, and when
+ they contain capturing parentheses there may be a potential use for
+ this feature. Not that that applies to a quantified (?!) but we allow
+ it for uniformity. */
+
+ case META_LOOKAHEADNOT:
+ if (pptr[1] == META_KET &&
+ (pptr[2] < META_ASTERISK || pptr[2] > META_MINMAX_QUERY))
+ {
+ *code++ = OP_FAIL;
+ pptr++;
+ }
+ else
+ {
+ bravalue = OP_ASSERT_NOT;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+ }
+ break;
+
+ case META_LOOKBEHIND:
+ bravalue = OP_ASSERTBACK;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ case META_LOOKBEHINDNOT:
+ bravalue = OP_ASSERTBACK_NOT;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ case META_LOOKBEHIND_NA:
+ bravalue = OP_ASSERTBACK_NA;
+ cb->assert_depth += 1;
+ goto GROUP_PROCESS;
+
+ case META_ATOMIC:
+ bravalue = OP_ONCE;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ case META_SCRIPT_RUN:
+ bravalue = OP_SCRIPT_RUN;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+ case META_NOCAPTURE:
+ bravalue = OP_BRA;
+ /* Fall through */
+
+ /* Process nested bracketed regex. The nesting depth is maintained for the
+ benefit of the stackguard function. The test for too deep nesting is now
+ done in parse_regex(). Assertion and DEFINE groups come to GROUP_PROCESS;
+ others come to GROUP_PROCESS_NOTE_EMPTY, to indicate that we need to take
+ note of whether or not they may match an empty string. */
+
+ GROUP_PROCESS_NOTE_EMPTY:
+ note_group_empty = TRUE;
+
+ GROUP_PROCESS:
+ cb->parens_depth += 1;
+ *code = bravalue;
+ pptr++;
+ tempcode = code;
+ tempreqvary = cb->req_varyopt; /* Save value before group */
+ length_prevgroup = 0; /* Initialize for pre-compile phase */
+
+ if ((group_return =
+ compile_regex(
+ options, /* The option state */
+ &tempcode, /* Where to put code (updated) */
+ &pptr, /* Input pointer (updated) */
+ errorcodeptr, /* Where to put an error message */
+ skipunits, /* Skip over bracket number */
+ &subfirstcu, /* For possible first char */
+ &subfirstcuflags,
+ &subreqcu, /* For possible last char */
+ &subreqcuflags,
+ bcptr, /* Current branch chain */
+ cb, /* Compile data block */
+ (lengthptr == NULL)? NULL : /* Actual compile phase */
+ &length_prevgroup /* Pre-compile phase */
+ )) == 0)
+ return 0; /* Error */
+
+ cb->parens_depth -= 1;
+
+ /* If that was a non-conditional significant group (not an assertion, not a
+ DEFINE) that matches at least one character, then the current item matches
+ a character. Conditionals are handled below. */
+
+ if (note_group_empty && bravalue != OP_COND && group_return > 0)
+ matched_char = TRUE;
+
+ /* If we've just compiled an assertion, pop the assert depth. */
+
+ if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NA)
+ cb->assert_depth -= 1;
+
+ /* At the end of compiling, code is still pointing to the start of the
+ group, while tempcode has been updated to point past the end of the group.
+ The parsed pattern pointer (pptr) is on the closing META_KET.
+
+ If this is a conditional bracket, check that there are no more than
+ two branches in the group, or just one if it's a DEFINE group. We do this
+ in the real compile phase, not in the pre-pass, where the whole group may
+ not be available. */
+
+ if (bravalue == OP_COND && lengthptr == NULL)
+ {
+ PCRE2_UCHAR *tc = code;
+ int condcount = 0;
+
+ do {
+ condcount++;
+ tc += GET(tc,1);
+ }
+ while (*tc != OP_KET);
+
+ /* A DEFINE group is never obeyed inline (the "condition" is always
+ false). It must have only one branch. Having checked this, change the
+ opcode to OP_FALSE. */
+
+ if (code[LINK_SIZE+1] == OP_DEFINE)
+ {
+ if (condcount > 1)
+ {
+ cb->erroroffset = offset;
+ *errorcodeptr = ERR54;
+ return 0;
+ }
+ code[LINK_SIZE+1] = OP_FALSE;
+ bravalue = OP_DEFINE; /* A flag to suppress char handling below */
+ }
+
+ /* A "normal" conditional group. If there is just one branch, we must not
+ make use of its firstcu or reqcu, because this is equivalent to an
+ empty second branch. Also, it may match an empty string. If there are two
+ branches, this item must match a character if the group must. */
+
+ else
+ {
+ if (condcount > 2)
+ {
+ cb->erroroffset = offset;
+ *errorcodeptr = ERR27;
+ return 0;
+ }
+ if (condcount == 1) subfirstcuflags = subreqcuflags = REQ_NONE;
+ else if (group_return > 0) matched_char = TRUE;
+ }
+ }
+
+ /* In the pre-compile phase, update the length by the length of the group,
+ less the brackets at either end. Then reduce the compiled code to just a
+ set of non-capturing brackets so that it doesn't use much memory if it is
+ duplicated by a quantifier.*/
+
+ if (lengthptr != NULL)
+ {
+ if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE;
+ code++; /* This already contains bravalue */
+ PUTINC(code, 0, 1 + LINK_SIZE);
+ *code++ = OP_KET;
+ PUTINC(code, 0, 1 + LINK_SIZE);
+ break; /* No need to waste time with special character handling */
+ }
+
+ /* Otherwise update the main code pointer to the end of the group. */
+
+ code = tempcode;
+
+ /* For a DEFINE group, required and first character settings are not
+ relevant. */
+
+ if (bravalue == OP_DEFINE) break;
+
+ /* Handle updating of the required and first code units for other types of
+ group. Update for normal brackets of all kinds, and conditions with two
+ branches (see code above). If the bracket is followed by a quantifier with
+ zero repeat, we have to back off. Hence the definition of zeroreqcu and
+ zerofirstcu outside the main loop so that they can be accessed for the back
+ off. */
+
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ groupsetfirstcu = FALSE;
+
+ if (bravalue >= OP_ONCE) /* Not an assertion */
+ {
+ /* If we have not yet set a firstcu in this branch, take it from the
+ subpattern, remembering that it was set here so that a repeat of more
+ than one can replicate it as reqcu if necessary. If the subpattern has
+ no firstcu, set "none" for the whole branch. In both cases, a zero
+ repeat forces firstcu to "none". */
+
+ if (firstcuflags == REQ_UNSET && subfirstcuflags != REQ_UNSET)
+ {
+ if (subfirstcuflags < REQ_NONE)
+ {
+ firstcu = subfirstcu;
+ firstcuflags = subfirstcuflags;
+ groupsetfirstcu = TRUE;
+ }
+ else firstcuflags = REQ_NONE;
+ zerofirstcuflags = REQ_NONE;
+ }
+
+ /* If firstcu was previously set, convert the subpattern's firstcu
+ into reqcu if there wasn't one, using the vary flag that was in
+ existence beforehand. */
+
+ else if (subfirstcuflags < REQ_NONE && subreqcuflags >= REQ_NONE)
+ {
+ subreqcu = subfirstcu;
+ subreqcuflags = subfirstcuflags | tempreqvary;
+ }
+
+ /* If the subpattern set a required code unit (or set a first code unit
+ that isn't really the first code unit - see above), set it. */
+
+ if (subreqcuflags < REQ_NONE)
+ {
+ reqcu = subreqcu;
+ reqcuflags = subreqcuflags;
+ }
+ }
+
+ /* For a forward assertion, we take the reqcu, if set, provided that the
+ group has also set a firstcu. This can be helpful if the pattern that
+ follows the assertion doesn't set a different char. For example, it's
+ useful for /(?=abcde).+/. We can't set firstcu for an assertion, however
+ because it leads to incorrect effect for patterns such as /(?=a)a.+/ when
+ the "real" "a" would then become a reqcu instead of a firstcu. This is
+ overcome by a scan at the end if there's no firstcu, looking for an
+ asserted first char. A similar effect for patterns like /(?=.*X)X$/ means
+ we must only take the reqcu when the group also set a firstcu. Otherwise,
+ in that example, 'X' ends up set for both. */
+
+ else if ((bravalue == OP_ASSERT || bravalue == OP_ASSERT_NA) &&
+ subreqcuflags < REQ_NONE && subfirstcuflags < REQ_NONE)
+ {
+ reqcu = subreqcu;
+ reqcuflags = subreqcuflags;
+ }
+
+ break; /* End of nested group handling */
+
+
+ /* ===================================================================*/
+ /* Handle named backreferences and recursions. */
+
+ case META_BACKREF_BYNAME:
+ case META_RECURSE_BYNAME:
+ {
+ int count, index;
+ PCRE2_SPTR name;
+ BOOL is_dupname = FALSE;
+ named_group *ng = cb->named_groups;
+ uint32_t length = *(++pptr);
+
+ GETPLUSOFFSET(offset, pptr);
+ name = cb->start_pattern + offset;
+
+ /* In the first pass, the names generated in the pre-pass are available,
+ but the main name table has not yet been created. Scan the list of names
+ generated in the pre-pass in order to get a number and whether or not
+ this name is duplicated. */
+
+ groupnumber = 0;
+ for (unsigned int i = 0; i < cb->names_found; i++, ng++)
+ {
+ if (length == ng->length &&
+ PRIV(strncmp)(name, ng->name, length) == 0)
+ {
+ is_dupname = ng->isdup;
+ groupnumber = ng->number;
+
+ /* For a recursion, that's all that is needed. We can now go to
+ the code that handles numerical recursion, applying it to the first
+ group with the given name. */
+
+ if (meta == META_RECURSE_BYNAME)
+ {
+ meta_arg = groupnumber;
+ goto HANDLE_NUMERICAL_RECURSION;
+ }
+
+ /* For a back reference, update the back reference map and the
+ maximum back reference. */
+
+ cb->backref_map |= (groupnumber < 32)? (1u << groupnumber) : 1;
+ if (groupnumber > cb->top_backref)
+ cb->top_backref = groupnumber;
+ }
+ }
+
+ /* If the name was not found we have a bad reference. */
+
+ if (groupnumber == 0)
+ {
+ *errorcodeptr = ERR15;
+ cb->erroroffset = offset;
+ return 0;
+ }
+
+ /* If a back reference name is not duplicated, we can handle it as
+ a numerical reference. */
+
+ if (!is_dupname)
+ {
+ meta_arg = groupnumber;
+ goto HANDLE_SINGLE_REFERENCE;
+ }
+
+ /* If a back reference name is duplicated, we generate a different
+ opcode to a numerical back reference. In the second pass we must
+ search for the index and count in the final name table. */
+
+ count = 0; /* Values for first pass (avoids compiler warning) */
+ index = 0;
+ if (lengthptr == NULL && !find_dupname_details(name, length, &index,
+ &count, errorcodeptr, cb)) return 0;
+
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_DNREFI : OP_DNREF;
+ PUT2INC(code, 0, index);
+ PUT2INC(code, 0, count);
+ }
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle a numerical callout. */
+
+ case META_CALLOUT_NUMBER:
+ code[0] = OP_CALLOUT;
+ PUT(code, 1, pptr[1]); /* Offset to next pattern item */
+ PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */
+ code[1 + 2*LINK_SIZE] = pptr[3];
+ pptr += 3;
+ code += PRIV(OP_lengths)[OP_CALLOUT];
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle a callout with a string argument. In the pre-pass we just compute
+ the length without generating anything. The length in pptr[3] includes both
+ delimiters; in the actual compile only the first one is copied, but a
+ terminating zero is added. Any doubled delimiters within the string make
+ this an overestimate, but it is not worth bothering about. */
+
+ case META_CALLOUT_STRING:
+ if (lengthptr != NULL)
+ {
+ *lengthptr += pptr[3] + (1 + 4*LINK_SIZE);
+ pptr += 3;
+ SKIPOFFSET(pptr);
+ }
+
+ /* In the real compile we can copy the string. The starting delimiter is
+ included so that the client can discover it if they want. We also pass the
+ start offset to help a script language give better error messages. */
+
+ else
+ {
+ PCRE2_SPTR pp;
+ uint32_t delimiter;
+ uint32_t length = pptr[3];
+ PCRE2_UCHAR *callout_string = code + (1 + 4*LINK_SIZE);
+
+ code[0] = OP_CALLOUT_STR;
+ PUT(code, 1, pptr[1]); /* Offset to next pattern item */
+ PUT(code, 1 + LINK_SIZE, pptr[2]); /* Length of next pattern item */
+
+ pptr += 3;
+ GETPLUSOFFSET(offset, pptr); /* Offset to string in pattern */
+ pp = cb->start_pattern + offset;
+ delimiter = *callout_string++ = *pp++;
+ if (delimiter == CHAR_LEFT_CURLY_BRACKET)
+ delimiter = CHAR_RIGHT_CURLY_BRACKET;
+ PUT(code, 1 + 3*LINK_SIZE, (int)(offset + 1)); /* One after delimiter */
+
+ /* The syntax of the pattern was checked in the parsing scan. The length
+ includes both delimiters, but we have passed the opening one just above,
+ so we reduce length before testing it. The test is for > 1 because we do
+ not want to copy the final delimiter. This also ensures that pp[1] is
+ accessible. */
+
+ while (--length > 1)
+ {
+ if (*pp == delimiter && pp[1] == delimiter)
+ {
+ *callout_string++ = delimiter;
+ pp += 2;
+ length--;
+ }
+ else *callout_string++ = *pp++;
+ }
+ *callout_string++ = CHAR_NUL;
+
+ /* Set the length of the entire item, the advance to its end. */
+
+ PUT(code, 1 + 2*LINK_SIZE, (int)(callout_string - code));
+ code = callout_string;
+ }
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle repetition. The different types are all sorted out in the parsing
+ pass. */
+
+ case META_MINMAX_PLUS:
+ case META_MINMAX_QUERY:
+ case META_MINMAX:
+ repeat_min = *(++pptr);
+ repeat_max = *(++pptr);
+ goto REPEAT;
+
+ case META_ASTERISK:
+ case META_ASTERISK_PLUS:
+ case META_ASTERISK_QUERY:
+ repeat_min = 0;
+ repeat_max = REPEAT_UNLIMITED;
+ goto REPEAT;
+
+ case META_PLUS:
+ case META_PLUS_PLUS:
+ case META_PLUS_QUERY:
+ repeat_min = 1;
+ repeat_max = REPEAT_UNLIMITED;
+ goto REPEAT;
+
+ case META_QUERY:
+ case META_QUERY_PLUS:
+ case META_QUERY_QUERY:
+ repeat_min = 0;
+ repeat_max = 1;
+
+ REPEAT:
+ if (previous_matched_char && repeat_min > 0) matched_char = TRUE;
+
+ /* Remember whether this is a variable length repeat, and default to
+ single-char opcodes. */
+
+ reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY;
+ op_type = 0;
+
+ /* Adjust first and required code units for a zero repeat. */
+
+ if (repeat_min == 0)
+ {
+ firstcu = zerofirstcu;
+ firstcuflags = zerofirstcuflags;
+ reqcu = zeroreqcu;
+ reqcuflags = zeroreqcuflags;
+ }
+
+ /* Note the greediness and possessiveness. */
+
+ switch (meta)
+ {
+ case META_MINMAX_PLUS:
+ case META_ASTERISK_PLUS:
+ case META_PLUS_PLUS:
+ case META_QUERY_PLUS:
+ repeat_type = 0; /* Force greedy */
+ possessive_quantifier = TRUE;
+ break;
+
+ case META_MINMAX_QUERY:
+ case META_ASTERISK_QUERY:
+ case META_PLUS_QUERY:
+ case META_QUERY_QUERY:
+ repeat_type = greedy_non_default;
+ possessive_quantifier = FALSE;
+ break;
+
+ default:
+ repeat_type = greedy_default;
+ possessive_quantifier = FALSE;
+ break;
+ }
+
+ /* Save start of previous item, in case we have to move it up in order to
+ insert something before it, and remember what it was. */
+
+ tempcode = previous;
+ op_previous = *previous;
+
+ /* Now handle repetition for the different types of item. If the repeat
+ minimum and the repeat maximum are both 1, we can ignore the quantifier for
+ non-parenthesized items, as they have only one alternative. For anything in
+ parentheses, we must not ignore if {1} is possessive. */
+
+ switch (op_previous)
+ {
+ /* If previous was a character or negated character match, abolish the
+ item and generate a repeat item instead. If a char item has a minimum of
+ more than one, ensure that it is set in reqcu - it might not be if a
+ sequence such as x{3} is the first thing in a branch because the x will
+ have gone into firstcu instead. */
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;
+ op_type = chartypeoffset[op_previous - OP_CHAR];
+
+ /* Deal with UTF characters that take up more than one code unit. */
+
+#ifdef MAYBE_UTF_MULTI
+ if (utf && NOT_FIRSTCU(code[-1]))
+ {
+ PCRE2_UCHAR *lastchar = code - 1;
+ BACKCHAR(lastchar);
+ mclength = (uint32_t)(code - lastchar); /* Length of UTF character */
+ memcpy(mcbuffer, lastchar, CU2BYTES(mclength)); /* Save the char */
+ }
+ else
+#endif /* MAYBE_UTF_MULTI */
+
+ /* Handle the case of a single code unit - either with no UTF support, or
+ with UTF disabled, or for a single-code-unit UTF character. In the latter
+ case, for a repeated positive match, get the caseless flag for the
+ required code unit from the previous character, because a class like [Aa]
+ sets a caseless A but by now the req_caseopt flag has been reset. */
+
+ {
+ mcbuffer[0] = code[-1];
+ mclength = 1;
+ if (op_previous <= OP_CHARI && repeat_min > 1)
+ {
+ reqcu = mcbuffer[0];
+ reqcuflags = cb->req_varyopt;
+ if (op_previous == OP_CHARI) reqcuflags |= REQ_CASELESS;
+ }
+ }
+ goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */
+
+ /* If previous was a character class or a back reference, we put the
+ repeat stuff after it, but just skip the item if the repeat was {0,0}. */
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+#endif
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_REF:
+ case OP_REFI:
+ case OP_DNREF:
+ case OP_DNREFI:
+
+ if (repeat_max == 0)
+ {
+ code = previous;
+ goto END_REPEAT;
+ }
+ if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;
+
+ if (repeat_min == 0 && repeat_max == REPEAT_UNLIMITED)
+ *code++ = OP_CRSTAR + repeat_type;
+ else if (repeat_min == 1 && repeat_max == REPEAT_UNLIMITED)
+ *code++ = OP_CRPLUS + repeat_type;
+ else if (repeat_min == 0 && repeat_max == 1)
+ *code++ = OP_CRQUERY + repeat_type;
+ else
+ {
+ *code++ = OP_CRRANGE + repeat_type;
+ PUT2INC(code, 0, repeat_min);
+ if (repeat_max == REPEAT_UNLIMITED) repeat_max = 0; /* 2-byte encoding for max */
+ PUT2INC(code, 0, repeat_max);
+ }
+ break;
+
+ /* If previous is OP_FAIL, it was generated by an empty class []
+ (PCRE2_ALLOW_EMPTY_CLASS is set). The other ways in which OP_FAIL can be
+ generated, that is by (*FAIL) or (?!), disallow a quantifier at parse
+ time. We can just ignore this repeat. */
+
+ case OP_FAIL:
+ goto END_REPEAT;
+
+ /* Prior to 10.30, repeated recursions were wrapped in OP_ONCE brackets
+ because pcre2_match() could not handle backtracking into recursively
+ called groups. Now that this backtracking is available, we no longer need
+ to do this. However, we still need to replicate recursions as we do for
+ groups so as to have independent backtracking points. We can replicate
+ for the minimum number of repeats directly. For optional repeats we now
+ wrap the recursion in OP_BRA brackets and make use of the bracket
+ repetition. */
+
+ case OP_RECURSE:
+ if (repeat_max == 1 && repeat_min == 1 && !possessive_quantifier)
+ goto END_REPEAT;
+
+ /* Generate unwrapped repeats for a non-zero minimum, except when the
+ minimum is 1 and the maximum unlimited, because that can be handled with
+ OP_BRA terminated by OP_KETRMAX/MIN. When the maximum is equal to the
+ minimum, we just need to generate the appropriate additional copies.
+ Otherwise we need to generate one more, to simulate the situation when
+ the minimum is zero. */
+
+ if (repeat_min > 0 && (repeat_min != 1 || repeat_max != REPEAT_UNLIMITED))
+ {
+ int replicate = repeat_min;
+ if (repeat_min == repeat_max) replicate--;
+
+ /* In the pre-compile phase, we don't actually do the replication. We
+ just adjust the length as if we had. Do some paranoid checks for
+ potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit
+ integer type when available, otherwise double. */
+
+ if (lengthptr != NULL)
+ {
+ PCRE2_SIZE delta = replicate*(1 + LINK_SIZE);
+ if ((INT64_OR_DOUBLE)replicate*
+ (INT64_OR_DOUBLE)(1 + LINK_SIZE) >
+ (INT64_OR_DOUBLE)INT_MAX ||
+ OFLOW_MAX - *lengthptr < delta)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += delta;
+ }
+
+ else for (int i = 0; i < replicate; i++)
+ {
+ memcpy(code, previous, CU2BYTES(1 + LINK_SIZE));
+ previous = code;
+ code += 1 + LINK_SIZE;
+ }
+
+ /* If the number of repeats is fixed, we are done. Otherwise, adjust
+ the counts and fall through. */
+
+ if (repeat_min == repeat_max) break;
+ if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min;
+ repeat_min = 0;
+ }
+
+ /* Wrap the recursion call in OP_BRA brackets. */
+
+ (void)memmove(previous + 1 + LINK_SIZE, previous, CU2BYTES(1 + LINK_SIZE));
+ op_previous = *previous = OP_BRA;
+ PUT(previous, 1, 2 + 2*LINK_SIZE);
+ previous[2 + 2*LINK_SIZE] = OP_KET;
+ PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE);
+ code += 2 + 2 * LINK_SIZE;
+ length_prevgroup = 3 + 3*LINK_SIZE;
+ group_return = -1; /* Set "may match empty string" */
+
+ /* Now treat as a repeated OP_BRA. */
+ /* Fall through */
+
+ /* If previous was a bracket group, we may have to replicate it in
+ certain cases. Note that at this point we can encounter only the "basic"
+ bracket opcodes such as BRA and CBRA, as this is the place where they get
+ converted into the more special varieties such as BRAPOS and SBRA.
+ Originally, PCRE did not allow repetition of assertions, but now it does,
+ for Perl compatibility. */
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRA:
+ case OP_CBRA:
+ case OP_COND:
+ {
+ int len = (int)(code - previous);
+ PCRE2_UCHAR *bralink = NULL;
+ PCRE2_UCHAR *brazeroptr = NULL;
+
+ if (repeat_max == 1 && repeat_min == 1 && !possessive_quantifier)
+ goto END_REPEAT;
+
+ /* Repeating a DEFINE group (or any group where the condition is always
+ FALSE and there is only one branch) is pointless, but Perl allows the
+ syntax, so we just ignore the repeat. */
+
+ if (op_previous == OP_COND && previous[LINK_SIZE+1] == OP_FALSE &&
+ previous[GET(previous, 1)] != OP_ALT)
+ goto END_REPEAT;
+
+ /* Perl allows all assertions to be quantified, and when they contain
+ capturing parentheses and/or are optional there are potential uses for
+ this feature. PCRE2 used to force the maximum quantifier to 1 on the
+ invalid grounds that further repetition was never useful. This was
+ always a bit pointless, since an assertion could be wrapped with a
+ repeated group to achieve the effect. General repetition is now
+ permitted, but if the maximum is unlimited it is set to one more than
+ the minimum. */
+
+ if (op_previous < OP_ONCE) /* Assertion */
+ {
+ if (repeat_max == REPEAT_UNLIMITED) repeat_max = repeat_min + 1;
+ }
+
+ /* The case of a zero minimum is special because of the need to stick
+ OP_BRAZERO in front of it, and because the group appears once in the
+ data, whereas in other cases it appears the minimum number of times. For
+ this reason, it is simplest to treat this case separately, as otherwise
+ the code gets far too messy. There are several special subcases when the
+ minimum is zero. */
+
+ if (repeat_min == 0)
+ {
+ /* If the maximum is also zero, we used to just omit the group from
+ the output altogether, like this:
+
+ ** if (repeat_max == 0)
+ ** {
+ ** code = previous;
+ ** goto END_REPEAT;
+ ** }
+
+ However, that fails when a group or a subgroup within it is
+ referenced as a subroutine from elsewhere in the pattern, so now we
+ stick in OP_SKIPZERO in front of it so that it is skipped on
+ execution. As we don't have a list of which groups are referenced, we
+ cannot do this selectively.
+
+ If the maximum is 1 or unlimited, we just have to stick in the
+ BRAZERO and do no more at this point. */
+
+ if (repeat_max <= 1 || repeat_max == REPEAT_UNLIMITED)
+ {
+ (void)memmove(previous + 1, previous, CU2BYTES(len));
+ code++;
+ if (repeat_max == 0)
+ {
+ *previous++ = OP_SKIPZERO;
+ goto END_REPEAT;
+ }
+ brazeroptr = previous; /* Save for possessive optimizing */
+ *previous++ = OP_BRAZERO + repeat_type;
+ }
+
+ /* If the maximum is greater than 1 and limited, we have to replicate
+ in a nested fashion, sticking OP_BRAZERO before each set of brackets.
+ The first one has to be handled carefully because it's the original
+ copy, which has to be moved up. The remainder can be handled by code
+ that is common with the non-zero minimum case below. We have to
+ adjust the value or repeat_max, since one less copy is required. */
+
+ else
+ {
+ int linkoffset;
+ (void)memmove(previous + 2 + LINK_SIZE, previous, CU2BYTES(len));
+ code += 2 + LINK_SIZE;
+ *previous++ = OP_BRAZERO + repeat_type;
+ *previous++ = OP_BRA;
+
+ /* We chain together the bracket link offset fields that have to be
+ filled in later when the ends of the brackets are reached. */
+
+ linkoffset = (bralink == NULL)? 0 : (int)(previous - bralink);
+ bralink = previous;
+ PUTINC(previous, 0, linkoffset);
+ }
+
+ if (repeat_max != REPEAT_UNLIMITED) repeat_max--;
+ }
+
+ /* If the minimum is greater than zero, replicate the group as many
+ times as necessary, and adjust the maximum to the number of subsequent
+ copies that we need. */
+
+ else
+ {
+ if (repeat_min > 1)
+ {
+ /* In the pre-compile phase, we don't actually do the replication.
+ We just adjust the length as if we had. Do some paranoid checks for
+ potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit
+ integer type when available, otherwise double. */
+
+ if (lengthptr != NULL)
+ {
+ PCRE2_SIZE delta = (repeat_min - 1)*length_prevgroup;
+ if ((INT64_OR_DOUBLE)(repeat_min - 1)*
+ (INT64_OR_DOUBLE)length_prevgroup >
+ (INT64_OR_DOUBLE)INT_MAX ||
+ OFLOW_MAX - *lengthptr < delta)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += delta;
+ }
+
+ /* This is compiling for real. If there is a set first code unit
+ for the group, and we have not yet set a "required code unit", set
+ it. */
+
+ else
+ {
+ if (groupsetfirstcu && reqcuflags >= REQ_NONE)
+ {
+ reqcu = firstcu;
+ reqcuflags = firstcuflags;
+ }
+ for (uint32_t i = 1; i < repeat_min; i++)
+ {
+ memcpy(code, previous, CU2BYTES(len));
+ code += len;
+ }
+ }
+ }
+
+ if (repeat_max != REPEAT_UNLIMITED) repeat_max -= repeat_min;
+ }
+
+ /* This code is common to both the zero and non-zero minimum cases. If
+ the maximum is limited, it replicates the group in a nested fashion,
+ remembering the bracket starts on a stack. In the case of a zero
+ minimum, the first one was set up above. In all cases the repeat_max
+ now specifies the number of additional copies needed. Again, we must
+ remember to replicate entries on the forward reference list. */
+
+ if (repeat_max != REPEAT_UNLIMITED)
+ {
+ /* In the pre-compile phase, we don't actually do the replication. We
+ just adjust the length as if we had. For each repetition we must add
+ 1 to the length for BRAZERO and for all but the last repetition we
+ must add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some
+ paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type
+ is a 64-bit integer type when available, otherwise double. */
+
+ if (lengthptr != NULL && repeat_max > 0)
+ {
+ PCRE2_SIZE delta = repeat_max*(length_prevgroup + 1 + 2 + 2*LINK_SIZE) -
+ 2 - 2*LINK_SIZE; /* Last one doesn't nest */
+ if ((INT64_OR_DOUBLE)repeat_max *
+ (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE)
+ > (INT64_OR_DOUBLE)INT_MAX ||
+ OFLOW_MAX - *lengthptr < delta)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += delta;
+ }
+
+ /* This is compiling for real */
+
+ else for (uint32_t i = repeat_max; i >= 1; i--)
+ {
+ *code++ = OP_BRAZERO + repeat_type;
+
+ /* All but the final copy start a new nesting, maintaining the
+ chain of brackets outstanding. */
+
+ if (i != 1)
+ {
+ int linkoffset;
+ *code++ = OP_BRA;
+ linkoffset = (bralink == NULL)? 0 : (int)(code - bralink);
+ bralink = code;
+ PUTINC(code, 0, linkoffset);
+ }
+
+ memcpy(code, previous, CU2BYTES(len));
+ code += len;
+ }
+
+ /* Now chain through the pending brackets, and fill in their length
+ fields (which are holding the chain links pro tem). */
+
+ while (bralink != NULL)
+ {
+ int oldlinkoffset;
+ int linkoffset = (int)(code - bralink + 1);
+ PCRE2_UCHAR *bra = code - linkoffset;
+ oldlinkoffset = GET(bra, 1);
+ bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset;
+ *code++ = OP_KET;
+ PUTINC(code, 0, linkoffset);
+ PUT(bra, 1, linkoffset);
+ }
+ }
+
+ /* If the maximum is unlimited, set a repeater in the final copy. For
+ SCRIPT_RUN and ONCE brackets, that's all we need to do. However,
+ possessively repeated ONCE brackets can be converted into non-capturing
+ brackets, as the behaviour of (?:xx)++ is the same as (?>xx)++ and this
+ saves having to deal with possessive ONCEs specially.
+
+ Otherwise, when we are doing the actual compile phase, check to see
+ whether this group is one that could match an empty string. If so,
+ convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so
+ that runtime checking can be done. [This check is also applied to ONCE
+ and SCRIPT_RUN groups at runtime, but in a different way.]
+
+ Then, if the quantifier was possessive and the bracket is not a
+ conditional, we convert the BRA code to the POS form, and the KET code
+ to KETRPOS. (It turns out to be convenient at runtime to detect this
+ kind of subpattern at both the start and at the end.) The use of
+ special opcodes makes it possible to reduce greatly the stack usage in
+ pcre2_match(). If the group is preceded by OP_BRAZERO, convert this to
+ OP_BRAPOSZERO.
+
+ Then, if the minimum number of matches is 1 or 0, cancel the possessive
+ flag so that the default action below, of wrapping everything inside
+ atomic brackets, does not happen. When the minimum is greater than 1,
+ there will be earlier copies of the group, and so we still have to wrap
+ the whole thing. */
+
+ else
+ {
+ PCRE2_UCHAR *ketcode = code - 1 - LINK_SIZE;
+ PCRE2_UCHAR *bracode = ketcode - GET(ketcode, 1);
+
+ /* Convert possessive ONCE brackets to non-capturing */
+
+ if (*bracode == OP_ONCE && possessive_quantifier) *bracode = OP_BRA;
+
+ /* For non-possessive ONCE and for SCRIPT_RUN brackets, all we need
+ to do is to set the KET. */
+
+ if (*bracode == OP_ONCE || *bracode == OP_SCRIPT_RUN)
+ *ketcode = OP_KETRMAX + repeat_type;
+
+ /* Handle non-SCRIPT_RUN and non-ONCE brackets and possessive ONCEs
+ (which have been converted to non-capturing above). */
+
+ else
+ {
+ /* In the compile phase, adjust the opcode if the group can match
+ an empty string. For a conditional group with only one branch, the
+ value of group_return will not show "could be empty", so we must
+ check that separately. */
+
+ if (lengthptr == NULL)
+ {
+ if (group_return < 0) *bracode += OP_SBRA - OP_BRA;
+ if (*bracode == OP_COND && bracode[GET(bracode,1)] != OP_ALT)
+ *bracode = OP_SCOND;
+ }
+
+ /* Handle possessive quantifiers. */
+
+ if (possessive_quantifier)
+ {
+ /* For COND brackets, we wrap the whole thing in a possessively
+ repeated non-capturing bracket, because we have not invented POS
+ versions of the COND opcodes. */
+
+ if (*bracode == OP_COND || *bracode == OP_SCOND)
+ {
+ int nlen = (int)(code - bracode);
+ (void)memmove(bracode + 1 + LINK_SIZE, bracode, CU2BYTES(nlen));
+ code += 1 + LINK_SIZE;
+ nlen += 1 + LINK_SIZE;
+ *bracode = (*bracode == OP_COND)? OP_BRAPOS : OP_SBRAPOS;
+ *code++ = OP_KETRPOS;
+ PUTINC(code, 0, nlen);
+ PUT(bracode, 1, nlen);
+ }
+
+ /* For non-COND brackets, we modify the BRA code and use KETRPOS. */
+
+ else
+ {
+ *bracode += 1; /* Switch to xxxPOS opcodes */
+ *ketcode = OP_KETRPOS;
+ }
+
+ /* If the minimum is zero, mark it as possessive, then unset the
+ possessive flag when the minimum is 0 or 1. */
+
+ if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO;
+ if (repeat_min < 2) possessive_quantifier = FALSE;
+ }
+
+ /* Non-possessive quantifier */
+
+ else *ketcode = OP_KETRMAX + repeat_type;
+ }
+ }
+ }
+ break;
+
+ /* If previous was a character type match (\d or similar), abolish it and
+ create a suitable repeat item. The code is shared with single-character
+ repeats by setting op_type to add a suitable offset into repeat_type.
+ Note the the Unicode property types will be present only when
+ SUPPORT_UNICODE is defined, but we don't wrap the little bits of code
+ here because it just makes it horribly messy. */
+
+ default:
+ if (op_previous >= OP_EODN) /* Not a character type - internal error */
+ {
+ *errorcodeptr = ERR10;
+ return 0;
+ }
+ else
+ {
+ int prop_type, prop_value;
+ PCRE2_UCHAR *oldcode;
+
+ if (repeat_max == 1 && repeat_min == 1) goto END_REPEAT;
+
+ op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */
+ mclength = 0; /* Not a character */
+
+ if (op_previous == OP_PROP || op_previous == OP_NOTPROP)
+ {
+ prop_type = previous[1];
+ prop_value = previous[2];
+ }
+ else
+ {
+ /* Come here from just above with a character in mcbuffer/mclength. */
+ OUTPUT_SINGLE_REPEAT:
+ prop_type = prop_value = -1;
+ }
+
+ /* At this point, if prop_type == prop_value == -1 we either have a
+ character in mcbuffer when mclength is greater than zero, or we have
+ mclength zero, in which case there is a non-property character type in
+ op_previous. If prop_type/value are not negative, we have a property
+ character type in op_previous. */
+
+ oldcode = code; /* Save where we were */
+ code = previous; /* Usually overwrite previous item */
+
+ /* If the maximum is zero then the minimum must also be zero; Perl allows
+ this case, so we do too - by simply omitting the item altogether. */
+
+ if (repeat_max == 0) goto END_REPEAT;
+
+ /* Combine the op_type with the repeat_type */
+
+ repeat_type += op_type;
+
+ /* A minimum of zero is handled either as the special case * or ?, or as
+ an UPTO, with the maximum given. */
+
+ if (repeat_min == 0)
+ {
+ if (repeat_max == REPEAT_UNLIMITED) *code++ = OP_STAR + repeat_type;
+ else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type;
+ else
+ {
+ *code++ = OP_UPTO + repeat_type;
+ PUT2INC(code, 0, repeat_max);
+ }
+ }
+
+ /* A repeat minimum of 1 is optimized into some special cases. If the
+ maximum is unlimited, we use OP_PLUS. Otherwise, the original item is
+ left in place and, if the maximum is greater than 1, we use OP_UPTO with
+ one less than the maximum. */
+
+ else if (repeat_min == 1)
+ {
+ if (repeat_max == REPEAT_UNLIMITED)
+ *code++ = OP_PLUS + repeat_type;
+ else
+ {
+ code = oldcode; /* Leave previous item in place */
+ if (repeat_max == 1) goto END_REPEAT;
+ *code++ = OP_UPTO + repeat_type;
+ PUT2INC(code, 0, repeat_max - 1);
+ }
+ }
+
+ /* The case {n,n} is just an EXACT, while the general case {n,m} is
+ handled as an EXACT followed by an UPTO or STAR or QUERY. */
+
+ else
+ {
+ *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */
+ PUT2INC(code, 0, repeat_min);
+
+ /* Unless repeat_max equals repeat_min, fill in the data for EXACT,
+ and then generate the second opcode. For a repeated Unicode property
+ match, there are two extra values that define the required property,
+ and mclength is set zero to indicate this. */
+
+ if (repeat_max != repeat_min)
+ {
+ if (mclength > 0)
+ {
+ memcpy(code, mcbuffer, CU2BYTES(mclength));
+ code += mclength;
+ }
+ else
+ {
+ *code++ = op_previous;
+ if (prop_type >= 0)
+ {
+ *code++ = prop_type;
+ *code++ = prop_value;
+ }
+ }
+
+ /* Now set up the following opcode */
+
+ if (repeat_max == REPEAT_UNLIMITED)
+ *code++ = OP_STAR + repeat_type;
+ else
+ {
+ repeat_max -= repeat_min;
+ if (repeat_max == 1)
+ {
+ *code++ = OP_QUERY + repeat_type;
+ }
+ else
+ {
+ *code++ = OP_UPTO + repeat_type;
+ PUT2INC(code, 0, repeat_max);
+ }
+ }
+ }
+ }
+
+ /* Fill in the character or character type for the final opcode. */
+
+ if (mclength > 0)
+ {
+ memcpy(code, mcbuffer, CU2BYTES(mclength));
+ code += mclength;
+ }
+ else
+ {
+ *code++ = op_previous;
+ if (prop_type >= 0)
+ {
+ *code++ = prop_type;
+ *code++ = prop_value;
+ }
+ }
+ }
+ break;
+ } /* End of switch on different op_previous values */
+
+
+ /* If the character following a repeat is '+', possessive_quantifier is
+ TRUE. For some opcodes, there are special alternative opcodes for this
+ case. For anything else, we wrap the entire repeated item inside OP_ONCE
+ brackets. Logically, the '+' notation is just syntactic sugar, taken from
+ Sun's Java package, but the special opcodes can optimize it.
+
+ Some (but not all) possessively repeated subpatterns have already been
+ completely handled in the code just above. For them, possessive_quantifier
+ is always FALSE at this stage. Note that the repeated item starts at
+ tempcode, not at previous, which might be the first part of a string whose
+ (former) last char we repeated. */
+
+ if (possessive_quantifier)
+ {
+ int len;
+
+ /* Possessifying an EXACT quantifier has no effect, so we can ignore it.
+ However, QUERY, STAR, or UPTO may follow (for quantifiers such as {5,6},
+ {5,}, or {5,10}). We skip over an EXACT item; if the length of what
+ remains is greater than zero, there's a further opcode that can be
+ handled. If not, do nothing, leaving the EXACT alone. */
+
+ switch(*tempcode)
+ {
+ case OP_TYPEEXACT:
+ tempcode += PRIV(OP_lengths)[*tempcode] +
+ ((tempcode[1 + IMM2_SIZE] == OP_PROP
+ || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);
+ break;
+
+ /* CHAR opcodes are used for exacts whose count is 1. */
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_EXACT:
+ case OP_EXACTI:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ tempcode += PRIV(OP_lengths)[*tempcode];
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(tempcode[-1]))
+ tempcode += GET_EXTRALEN(tempcode[-1]);
+#endif
+ break;
+
+ /* For the class opcodes, the repeat operator appears at the end;
+ adjust tempcode to point to it. */
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ tempcode += 1 + 32/sizeof(PCRE2_UCHAR);
+ break;
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ tempcode += GET(tempcode, 1);
+ break;
+#endif
+ }
+
+ /* If tempcode is equal to code (which points to the end of the repeated
+ item), it means we have skipped an EXACT item but there is no following
+ QUERY, STAR, or UPTO; the value of len will be 0, and we do nothing. In
+ all other cases, tempcode will be pointing to the repeat opcode, and will
+ be less than code, so the value of len will be greater than 0. */
+
+ len = (int)(code - tempcode);
+ if (len > 0)
+ {
+ unsigned int repcode = *tempcode;
+
+ /* There is a table for possessifying opcodes, all of which are less
+ than OP_CALLOUT. A zero entry means there is no possessified version.
+ */
+
+ if (repcode < OP_CALLOUT && opcode_possessify[repcode] > 0)
+ *tempcode = opcode_possessify[repcode];
+
+ /* For opcode without a special possessified version, wrap the item in
+ ONCE brackets. */
+
+ else
+ {
+ (void)memmove(tempcode + 1 + LINK_SIZE, tempcode, CU2BYTES(len));
+ code += 1 + LINK_SIZE;
+ len += 1 + LINK_SIZE;
+ tempcode[0] = OP_ONCE;
+ *code++ = OP_KET;
+ PUTINC(code, 0, len);
+ PUT(tempcode, 1, len);
+ }
+ }
+ }
+
+ /* We set the "follows varying string" flag for subsequently encountered
+ reqcus if it isn't already set and we have just passed a varying length
+ item. */
+
+ END_REPEAT:
+ cb->req_varyopt |= reqvary;
+ break;
+
+
+ /* ===================================================================*/
+ /* Handle a 32-bit data character with a value greater than META_END. */
+
+ case META_BIGVALUE:
+ pptr++;
+ goto NORMAL_CHAR;
+
+
+ /* ===============================================================*/
+ /* Handle a back reference by number, which is the meta argument. The
+ pattern offsets for back references to group numbers less than 10 are held
+ in a special vector, to avoid using more than two parsed pattern elements
+ in 64-bit environments. We only need the offset to the first occurrence,
+ because if that doesn't fail, subsequent ones will also be OK. */
+
+ case META_BACKREF:
+ if (meta_arg < 10) offset = cb->small_ref_offset[meta_arg];
+ else GETPLUSOFFSET(offset, pptr);
+
+ if (meta_arg > cb->bracount)
+ {
+ cb->erroroffset = offset;
+ *errorcodeptr = ERR15; /* Non-existent subpattern */
+ return 0;
+ }
+
+ /* Come here from named backref handling when the reference is to a
+ single group (that is, not to a duplicated name). The back reference
+ data will have already been updated. We must disable firstcu if not
+ set, to cope with cases like (?=(\w+))\1: which would otherwise set ':'
+ later. */
+
+ HANDLE_SINGLE_REFERENCE:
+ if (firstcuflags == REQ_UNSET) zerofirstcuflags = firstcuflags = REQ_NONE;
+ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_REFI : OP_REF;
+ PUT2INC(code, 0, meta_arg);
+
+ /* Update the map of back references, and keep the highest one. We
+ could do this in parse_regex() for numerical back references, but not
+ for named back references, because we don't know the numbers to which
+ named back references refer. So we do it all in this function. */
+
+ cb->backref_map |= (meta_arg < 32)? (1u << meta_arg) : 1;
+ if (meta_arg > cb->top_backref) cb->top_backref = meta_arg;
+ break;
+
+
+ /* ===============================================================*/
+ /* Handle recursion by inserting the number of the called group (which is
+ the meta argument) after OP_RECURSE. At the end of compiling the pattern is
+ scanned and these numbers are replaced by offsets within the pattern. It is
+ done like this to avoid problems with forward references and adjusting
+ offsets when groups are duplicated and moved (as discovered in previous
+ implementations). Note that a recursion does not have a set first
+ character. */
+
+ case META_RECURSE:
+ GETPLUSOFFSET(offset, pptr);
+ if (meta_arg > cb->bracount)
+ {
+ cb->erroroffset = offset;
+ *errorcodeptr = ERR15; /* Non-existent subpattern */
+ return 0;
+ }
+ HANDLE_NUMERICAL_RECURSION:
+ *code = OP_RECURSE;
+ PUT(code, 1, meta_arg);
+ code += 1 + LINK_SIZE;
+ groupsetfirstcu = FALSE;
+ cb->had_recurse = TRUE;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ break;
+
+
+ /* ===============================================================*/
+ /* Handle capturing parentheses; the number is the meta argument. */
+
+ case META_CAPTURE:
+ bravalue = OP_CBRA;
+ skipunits = IMM2_SIZE;
+ PUT2(code, 1+LINK_SIZE, meta_arg);
+ cb->lastcapture = meta_arg;
+ goto GROUP_PROCESS_NOTE_EMPTY;
+
+
+ /* ===============================================================*/
+ /* Handle escape sequence items. For ones like \d, the ESC_values are
+ arranged to be the same as the corresponding OP_values in the default case
+ when PCRE2_UCP is not set (which is the only case in which they will appear
+ here).
+
+ Note: \Q and \E are never seen here, as they were dealt with in
+ parse_pattern(). Neither are numerical back references or recursions, which
+ were turned into META_BACKREF or META_RECURSE items, respectively. \k and
+ \g, when followed by names, are turned into META_BACKREF_BYNAME or
+ META_RECURSE_BYNAME. */
+
+ case META_ESCAPE:
+
+ /* We can test for escape sequences that consume a character because their
+ values lie between ESC_b and ESC_Z; this may have to change if any new ones
+ are ever created. For these sequences, we disable the setting of a first
+ character if it hasn't already been set. */
+
+ if (meta_arg > ESC_b && meta_arg < ESC_Z)
+ {
+ matched_char = TRUE;
+ if (firstcuflags == REQ_UNSET) firstcuflags = REQ_NONE;
+ }
+
+ /* Set values to reset to if this is followed by a zero repeat. */
+
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+
+ /* If Unicode is not supported, \P and \p are not allowed and are
+ faulted at parse time, so will never appear here. */
+
+#ifdef SUPPORT_UNICODE
+ if (meta_arg == ESC_P || meta_arg == ESC_p)
+ {
+ uint32_t ptype = *(++pptr) >> 16;
+ uint32_t pdata = *pptr & 0xffff;
+
+ /* The special case of \p{Any} is compiled to OP_ALLANY so as to benefit
+ from the auto-anchoring code. */
+
+ if (meta_arg == ESC_p && ptype == PT_ANY)
+ {
+ *code++ = OP_ALLANY;
+ }
+ else
+ {
+ *code++ = (meta_arg == ESC_p)? OP_PROP : OP_NOTPROP;
+ *code++ = ptype;
+ *code++ = pdata;
+ }
+ break; /* End META_ESCAPE */
+ }
+#endif
+
+ /* \K is forbidden in lookarounds since 10.38 because that's what Perl has
+ done. However, there's an option, in case anyone was relying on it. */
+
+ if (cb->assert_depth > 0 && meta_arg == ESC_K &&
+ (cb->cx->extra_options & PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK) == 0)
+ {
+ *errorcodeptr = ERR99;
+ return 0;
+ }
+
+ /* For the rest (including \X when Unicode is supported - if not it's
+ faulted at parse time), the OP value is the escape value when PCRE2_UCP is
+ not set; if it is set, these escapes do not show up here because they are
+ converted into Unicode property tests in parse_regex(). Note that \b and \B
+ do a one-character lookbehind, and \A also behaves as if it does. */
+
+ if (meta_arg == ESC_C) cb->external_flags |= PCRE2_HASBKC; /* Record */
+ if ((meta_arg == ESC_b || meta_arg == ESC_B || meta_arg == ESC_A) &&
+ cb->max_lookbehind == 0)
+ cb->max_lookbehind = 1;
+
+ /* In non-UTF mode, and for both 32-bit modes, we turn \C into OP_ALLANY
+ instead of OP_ANYBYTE so that it works in DFA mode and in lookbehinds. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ *code++ = (meta_arg == ESC_C)? OP_ALLANY : meta_arg;
+#else
+ *code++ = (!utf && meta_arg == ESC_C)? OP_ALLANY : meta_arg;
+#endif
+ break; /* End META_ESCAPE */
+
+
+ /* ===================================================================*/
+ /* Handle an unrecognized meta value. A parsed pattern value less than
+ META_END is a literal. Otherwise we have a problem. */
+
+ default:
+ if (meta >= META_END)
+ {
+#ifdef DEBUG_SHOW_PARSED
+ fprintf(stderr, "** Unrecognized parsed pattern item 0x%.8x\n", *pptr);
+#endif
+ *errorcodeptr = ERR89; /* Internal error - unrecognized. */
+ return 0;
+ }
+
+ /* Handle a literal character. We come here by goto in the case of a
+ 32-bit, non-UTF character whose value is greater than META_END. */
+
+ NORMAL_CHAR:
+ meta = *pptr; /* Get the full 32 bits */
+ NORMAL_CHAR_SET: /* Character is already in meta */
+ matched_char = TRUE;
+
+ /* For caseless UTF or UCP mode, check whether this character has more than
+ one other case. If so, generate a special OP_PROP item instead of OP_CHARI.
+ */
+
+#ifdef SUPPORT_UNICODE
+ if ((utf||ucp) && (options & PCRE2_CASELESS) != 0)
+ {
+ uint32_t caseset = UCD_CASESET(meta);
+ if (caseset != 0)
+ {
+ *code++ = OP_PROP;
+ *code++ = PT_CLIST;
+ *code++ = caseset;
+ if (firstcuflags == REQ_UNSET)
+ firstcuflags = zerofirstcuflags = REQ_NONE;
+ break; /* End handling this meta item */
+ }
+ }
+#endif
+
+ /* Caseful matches, or caseless and not one of the multicase characters. We
+ come here by goto in the case of a positive class that contains only
+ case-partners of a character with just two cases; matched_char has already
+ been set TRUE and options fudged if necessary. */
+
+ CLASS_CASELESS_CHAR:
+
+ /* Get the character's code units into mcbuffer, with the length in
+ mclength. When not in UTF mode, the length is always 1. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf) mclength = PRIV(ord2utf)(meta, mcbuffer); else
+#endif
+ {
+ mclength = 1;
+ mcbuffer[0] = meta;
+ }
+
+ /* Generate the appropriate code */
+
+ *code++ = ((options & PCRE2_CASELESS) != 0)? OP_CHARI : OP_CHAR;
+ memcpy(code, mcbuffer, CU2BYTES(mclength));
+ code += mclength;
+
+ /* Remember if \r or \n were seen */
+
+ if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL)
+ cb->external_flags |= PCRE2_HASCRORLF;
+
+ /* Set the first and required code units appropriately. If no previous
+ first code unit, set it from this character, but revert to none on a zero
+ repeat. Otherwise, leave the firstcu value alone, and don't change it on
+ a zero repeat. */
+
+ if (firstcuflags == REQ_UNSET)
+ {
+ zerofirstcuflags = REQ_NONE;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+
+ /* If the character is more than one code unit long, we can set a single
+ firstcu only if it is not to be matched caselessly. Multiple possible
+ starting code units may be picked up later in the studying code. */
+
+ if (mclength == 1 || req_caseopt == 0)
+ {
+ firstcu = mcbuffer[0];
+ firstcuflags = req_caseopt;
+ if (mclength != 1)
+ {
+ reqcu = code[-1];
+ reqcuflags = cb->req_varyopt;
+ }
+ }
+ else firstcuflags = reqcuflags = REQ_NONE;
+ }
+
+ /* firstcu was previously set; we can set reqcu only if the length is
+ 1 or the matching is caseful. */
+
+ else
+ {
+ zerofirstcu = firstcu;
+ zerofirstcuflags = firstcuflags;
+ zeroreqcu = reqcu;
+ zeroreqcuflags = reqcuflags;
+ if (mclength == 1 || req_caseopt == 0)
+ {
+ reqcu = code[-1];
+ reqcuflags = req_caseopt | cb->req_varyopt;
+ }
+ }
+
+ /* If caselessness was temporarily instated, reset it. */
+
+ if (reset_caseful)
+ {
+ options &= ~PCRE2_CASELESS;
+ req_caseopt = 0;
+ reset_caseful = FALSE;
+ }
+
+ break; /* End literal character handling */
+ } /* End of big switch */
+ } /* End of big loop */
+
+/* Control never reaches here. */
+}
+
+
+
+/*************************************************
+* Compile regex: a sequence of alternatives *
+*************************************************/
+
+/* On entry, pptr is pointing past the bracket meta, but on return it points to
+the closing bracket or META_END. The code variable is pointing at the code unit
+into which the BRA operator has been stored. This function is used during the
+pre-compile phase when we are trying to find out the amount of memory needed,
+as well as during the real compile phase. The value of lengthptr distinguishes
+the two phases.
+
+Arguments:
+ options option bits, including any changes for this subpattern
+ codeptr -> the address of the current code pointer
+ pptrptr -> the address of the current parsed pattern pointer
+ errorcodeptr -> pointer to error code variable
+ skipunits skip this many code units at start (for brackets and OP_COND)
+ firstcuptr place to put the first required code unit
+ firstcuflagsptr place to put the first code unit flags
+ reqcuptr place to put the last required code unit
+ reqcuflagsptr place to put the last required code unit flags
+ bcptr pointer to the chain of currently open branches
+ cb points to the data block with tables pointers etc.
+ lengthptr NULL during the real compile phase
+ points to length accumulator during pre-compile phase
+
+Returns: 0 There has been an error
+ +1 Success, this group must match at least one character
+ -1 Success, this group may match an empty string
+*/
+
+static int
+compile_regex(uint32_t options, PCRE2_UCHAR **codeptr, uint32_t **pptrptr,
+ int *errorcodeptr, uint32_t skipunits, uint32_t *firstcuptr,
+ uint32_t *firstcuflagsptr, uint32_t *reqcuptr, uint32_t *reqcuflagsptr,
+ branch_chain *bcptr, compile_block *cb, PCRE2_SIZE *lengthptr)
+{
+PCRE2_UCHAR *code = *codeptr;
+PCRE2_UCHAR *last_branch = code;
+PCRE2_UCHAR *start_bracket = code;
+BOOL lookbehind;
+open_capitem capitem;
+int capnumber = 0;
+int okreturn = 1;
+uint32_t *pptr = *pptrptr;
+uint32_t firstcu, reqcu;
+uint32_t lookbehindlength;
+uint32_t firstcuflags, reqcuflags;
+uint32_t branchfirstcu, branchreqcu;
+uint32_t branchfirstcuflags, branchreqcuflags;
+PCRE2_SIZE length;
+branch_chain bc;
+
+/* If set, call the external function that checks for stack availability. */
+
+if (cb->cx->stack_guard != NULL &&
+ cb->cx->stack_guard(cb->parens_depth, cb->cx->stack_guard_data))
+ {
+ *errorcodeptr= ERR33;
+ return 0;
+ }
+
+/* Miscellaneous initialization */
+
+bc.outer = bcptr;
+bc.current_branch = code;
+
+firstcu = reqcu = 0;
+firstcuflags = reqcuflags = REQ_UNSET;
+
+/* Accumulate the length for use in the pre-compile phase. Start with the
+length of the BRA and KET and any extra code units that are required at the
+beginning. We accumulate in a local variable to save frequent testing of
+lengthptr for NULL. We cannot do this by looking at the value of 'code' at the
+start and end of each alternative, because compiled items are discarded during
+the pre-compile phase so that the workspace is not exceeded. */
+
+length = 2 + 2*LINK_SIZE + skipunits;
+
+/* Remember if this is a lookbehind assertion, and if it is, save its length
+and skip over the pattern offset. */
+
+lookbehind = *code == OP_ASSERTBACK ||
+ *code == OP_ASSERTBACK_NOT ||
+ *code == OP_ASSERTBACK_NA;
+
+if (lookbehind)
+ {
+ lookbehindlength = META_DATA(pptr[-1]);
+ pptr += SIZEOFFSET;
+ }
+else lookbehindlength = 0;
+
+/* If this is a capturing subpattern, add to the chain of open capturing items
+so that we can detect them if (*ACCEPT) is encountered. Note that only OP_CBRA
+need be tested here; changing this opcode to one of its variants, e.g.
+OP_SCBRAPOS, happens later, after the group has been compiled. */
+
+if (*code == OP_CBRA)
+ {
+ capnumber = GET2(code, 1 + LINK_SIZE);
+ capitem.number = capnumber;
+ capitem.next = cb->open_caps;
+ capitem.assert_depth = cb->assert_depth;
+ cb->open_caps = &capitem;
+ }
+
+/* Offset is set zero to mark that this bracket is still open */
+
+PUT(code, 1, 0);
+code += 1 + LINK_SIZE + skipunits;
+
+/* Loop for each alternative branch */
+
+for (;;)
+ {
+ int branch_return;
+
+ /* Insert OP_REVERSE if this is as lookbehind assertion. */
+
+ if (lookbehind && lookbehindlength > 0)
+ {
+ *code++ = OP_REVERSE;
+ PUTINC(code, 0, lookbehindlength);
+ length += 1 + LINK_SIZE;
+ }
+
+ /* Now compile the branch; in the pre-compile phase its length gets added
+ into the length. */
+
+ if ((branch_return =
+ compile_branch(&options, &code, &pptr, errorcodeptr, &branchfirstcu,
+ &branchfirstcuflags, &branchreqcu, &branchreqcuflags, &bc,
+ cb, (lengthptr == NULL)? NULL : &length)) == 0)
+ return 0;
+
+ /* If a branch can match an empty string, so can the whole group. */
+
+ if (branch_return < 0) okreturn = -1;
+
+ /* In the real compile phase, there is some post-processing to be done. */
+
+ if (lengthptr == NULL)
+ {
+ /* If this is the first branch, the firstcu and reqcu values for the
+ branch become the values for the regex. */
+
+ if (*last_branch != OP_ALT)
+ {
+ firstcu = branchfirstcu;
+ firstcuflags = branchfirstcuflags;
+ reqcu = branchreqcu;
+ reqcuflags = branchreqcuflags;
+ }
+
+ /* If this is not the first branch, the first char and reqcu have to
+ match the values from all the previous branches, except that if the
+ previous value for reqcu didn't have REQ_VARY set, it can still match,
+ and we set REQ_VARY for the group from this branch's value. */
+
+ else
+ {
+ /* If we previously had a firstcu, but it doesn't match the new branch,
+ we have to abandon the firstcu for the regex, but if there was
+ previously no reqcu, it takes on the value of the old firstcu. */
+
+ if (firstcuflags != branchfirstcuflags || firstcu != branchfirstcu)
+ {
+ if (firstcuflags < REQ_NONE)
+ {
+ if (reqcuflags >= REQ_NONE)
+ {
+ reqcu = firstcu;
+ reqcuflags = firstcuflags;
+ }
+ }
+ firstcuflags = REQ_NONE;
+ }
+
+ /* If we (now or from before) have no firstcu, a firstcu from the
+ branch becomes a reqcu if there isn't a branch reqcu. */
+
+ if (firstcuflags >= REQ_NONE && branchfirstcuflags < REQ_NONE &&
+ branchreqcuflags >= REQ_NONE)
+ {
+ branchreqcu = branchfirstcu;
+ branchreqcuflags = branchfirstcuflags;
+ }
+
+ /* Now ensure that the reqcus match */
+
+ if (((reqcuflags & ~REQ_VARY) != (branchreqcuflags & ~REQ_VARY)) ||
+ reqcu != branchreqcu)
+ reqcuflags = REQ_NONE;
+ else
+ {
+ reqcu = branchreqcu;
+ reqcuflags |= branchreqcuflags; /* To "or" REQ_VARY if present */
+ }
+ }
+ }
+
+ /* Handle reaching the end of the expression, either ')' or end of pattern.
+ In the real compile phase, go back through the alternative branches and
+ reverse the chain of offsets, with the field in the BRA item now becoming an
+ offset to the first alternative. If there are no alternatives, it points to
+ the end of the group. The length in the terminating ket is always the length
+ of the whole bracketed item. Return leaving the pointer at the terminating
+ char. */
+
+ if (META_CODE(*pptr) != META_ALT)
+ {
+ if (lengthptr == NULL)
+ {
+ PCRE2_SIZE branch_length = code - last_branch;
+ do
+ {
+ PCRE2_SIZE prev_length = GET(last_branch, 1);
+ PUT(last_branch, 1, branch_length);
+ branch_length = prev_length;
+ last_branch -= branch_length;
+ }
+ while (branch_length > 0);
+ }
+
+ /* Fill in the ket */
+
+ *code = OP_KET;
+ PUT(code, 1, (int)(code - start_bracket));
+ code += 1 + LINK_SIZE;
+
+ /* If it was a capturing subpattern, remove the block from the chain. */
+
+ if (capnumber > 0) cb->open_caps = cb->open_caps->next;
+
+ /* Set values to pass back */
+
+ *codeptr = code;
+ *pptrptr = pptr;
+ *firstcuptr = firstcu;
+ *firstcuflagsptr = firstcuflags;
+ *reqcuptr = reqcu;
+ *reqcuflagsptr = reqcuflags;
+ if (lengthptr != NULL)
+ {
+ if (OFLOW_MAX - *lengthptr < length)
+ {
+ *errorcodeptr = ERR20;
+ return 0;
+ }
+ *lengthptr += length;
+ }
+ return okreturn;
+ }
+
+ /* Another branch follows. In the pre-compile phase, we can move the code
+ pointer back to where it was for the start of the first branch. (That is,
+ pretend that each branch is the only one.)
+
+ In the real compile phase, insert an ALT node. Its length field points back
+ to the previous branch while the bracket remains open. At the end the chain
+ is reversed. It's done like this so that the start of the bracket has a
+ zero offset until it is closed, making it possible to detect recursion. */
+
+ if (lengthptr != NULL)
+ {
+ code = *codeptr + 1 + LINK_SIZE + skipunits;
+ length += 1 + LINK_SIZE;
+ }
+ else
+ {
+ *code = OP_ALT;
+ PUT(code, 1, (int)(code - last_branch));
+ bc.current_branch = last_branch = code;
+ code += 1 + LINK_SIZE;
+ }
+
+ /* Set the lookbehind length (if not in a lookbehind the value will be zero)
+ and then advance past the vertical bar. */
+
+ lookbehindlength = META_DATA(*pptr);
+ pptr++;
+ }
+/* Control never reaches here */
+}
+
+
+
+/*************************************************
+* Check for anchored pattern *
+*************************************************/
+
+/* Try to find out if this is an anchored regular expression. Consider each
+alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket
+all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then
+it's anchored. However, if this is a multiline pattern, then only OP_SOD will
+be found, because ^ generates OP_CIRCM in that mode.
+
+We can also consider a regex to be anchored if OP_SOM starts all its branches.
+This is the code for \G, which means "match at start of match position, taking
+into account the match offset".
+
+A branch is also implicitly anchored if it starts with .* and DOTALL is set,
+because that will try the rest of the pattern at all possible matching points,
+so there is no point trying again.... er ....
+
+.... except when the .* appears inside capturing parentheses, and there is a
+subsequent back reference to those parentheses. We haven't enough information
+to catch that case precisely.
+
+At first, the best we could do was to detect when .* was in capturing brackets
+and the highest back reference was greater than or equal to that level.
+However, by keeping a bitmap of the first 31 back references, we can catch some
+of the more common cases more precisely.
+
+... A second exception is when the .* appears inside an atomic group, because
+this prevents the number of characters it matches from being adjusted.
+
+Arguments:
+ code points to start of the compiled pattern
+ bracket_map a bitmap of which brackets we are inside while testing; this
+ handles up to substring 31; after that we just have to take
+ the less precise approach
+ cb points to the compile data block
+ atomcount atomic group level
+ inassert TRUE if in an assertion
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+is_anchored(PCRE2_SPTR code, uint32_t bracket_map, compile_block *cb,
+ int atomcount, BOOL inassert)
+{
+do {
+ PCRE2_SPTR scode = first_significant_code(
+ code + PRIV(OP_lengths)[*code], FALSE);
+ int op = *scode;
+
+ /* Non-capturing brackets */
+
+ if (op == OP_BRA || op == OP_BRAPOS ||
+ op == OP_SBRA || op == OP_SBRAPOS)
+ {
+ if (!is_anchored(scode, bracket_map, cb, atomcount, inassert))
+ return FALSE;
+ }
+
+ /* Capturing brackets */
+
+ else if (op == OP_CBRA || op == OP_CBRAPOS ||
+ op == OP_SCBRA || op == OP_SCBRAPOS)
+ {
+ int n = GET2(scode, 1+LINK_SIZE);
+ uint32_t new_map = bracket_map | ((n < 32)? (1u << n) : 1);
+ if (!is_anchored(scode, new_map, cb, atomcount, inassert)) return FALSE;
+ }
+
+ /* Positive forward assertion */
+
+ else if (op == OP_ASSERT || op == OP_ASSERT_NA)
+ {
+ if (!is_anchored(scode, bracket_map, cb, atomcount, TRUE)) return FALSE;
+ }
+
+ /* Condition. If there is no second branch, it can't be anchored. */
+
+ else if (op == OP_COND || op == OP_SCOND)
+ {
+ if (scode[GET(scode,1)] != OP_ALT) return FALSE;
+ if (!is_anchored(scode, bracket_map, cb, atomcount, inassert))
+ return FALSE;
+ }
+
+ /* Atomic groups */
+
+ else if (op == OP_ONCE)
+ {
+ if (!is_anchored(scode, bracket_map, cb, atomcount + 1, inassert))
+ return FALSE;
+ }
+
+ /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and
+ it isn't in brackets that are or may be referenced or inside an atomic
+ group or an assertion. Also the pattern must not contain *PRUNE or *SKIP,
+ because these break the feature. Consider, for example, /(?s).*?(*PRUNE)b/
+ with the subject "aab", which matches "b", i.e. not at the start of a line.
+ There is also an option that disables auto-anchoring. */
+
+ else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR ||
+ op == OP_TYPEPOSSTAR))
+ {
+ if (scode[1] != OP_ALLANY || (bracket_map & cb->backref_map) != 0 ||
+ atomcount > 0 || cb->had_pruneorskip || inassert ||
+ (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)
+ return FALSE;
+ }
+
+ /* Check for explicit anchoring */
+
+ else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE;
+
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT); /* Loop for each alternative */
+return TRUE;
+}
+
+
+
+/*************************************************
+* Check for starting with ^ or .* *
+*************************************************/
+
+/* This is called to find out if every branch starts with ^ or .* so that
+"first char" processing can be done to speed things up in multiline
+matching and for non-DOTALL patterns that start with .* (which must start at
+the beginning or after \n). As in the case of is_anchored() (see above), we
+have to take account of back references to capturing brackets that contain .*
+because in that case we can't make the assumption. Also, the appearance of .*
+inside atomic brackets or in an assertion, or in a pattern that contains *PRUNE
+or *SKIP does not count, because once again the assumption no longer holds.
+
+Arguments:
+ code points to start of the compiled pattern or a group
+ bracket_map a bitmap of which brackets we are inside while testing; this
+ handles up to substring 31; after that we just have to take
+ the less precise approach
+ cb points to the compile data
+ atomcount atomic group level
+ inassert TRUE if in an assertion
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+is_startline(PCRE2_SPTR code, unsigned int bracket_map, compile_block *cb,
+ int atomcount, BOOL inassert)
+{
+do {
+ PCRE2_SPTR scode = first_significant_code(
+ code + PRIV(OP_lengths)[*code], FALSE);
+ int op = *scode;
+
+ /* If we are at the start of a conditional assertion group, *both* the
+ conditional assertion *and* what follows the condition must satisfy the test
+ for start of line. Other kinds of condition fail. Note that there may be an
+ auto-callout at the start of a condition. */
+
+ if (op == OP_COND)
+ {
+ scode += 1 + LINK_SIZE;
+
+ if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT];
+ else if (*scode == OP_CALLOUT_STR) scode += GET(scode, 1 + 2*LINK_SIZE);
+
+ switch (*scode)
+ {
+ case OP_CREF:
+ case OP_DNCREF:
+ case OP_RREF:
+ case OP_DNRREF:
+ case OP_FAIL:
+ case OP_FALSE:
+ case OP_TRUE:
+ return FALSE;
+
+ default: /* Assertion */
+ if (!is_startline(scode, bracket_map, cb, atomcount, TRUE)) return FALSE;
+ do scode += GET(scode, 1); while (*scode == OP_ALT);
+ scode += 1 + LINK_SIZE;
+ break;
+ }
+ scode = first_significant_code(scode, FALSE);
+ op = *scode;
+ }
+
+ /* Non-capturing brackets */
+
+ if (op == OP_BRA || op == OP_BRAPOS ||
+ op == OP_SBRA || op == OP_SBRAPOS)
+ {
+ if (!is_startline(scode, bracket_map, cb, atomcount, inassert))
+ return FALSE;
+ }
+
+ /* Capturing brackets */
+
+ else if (op == OP_CBRA || op == OP_CBRAPOS ||
+ op == OP_SCBRA || op == OP_SCBRAPOS)
+ {
+ int n = GET2(scode, 1+LINK_SIZE);
+ unsigned int new_map = bracket_map | ((n < 32)? (1u << n) : 1);
+ if (!is_startline(scode, new_map, cb, atomcount, inassert)) return FALSE;
+ }
+
+ /* Positive forward assertions */
+
+ else if (op == OP_ASSERT || op == OP_ASSERT_NA)
+ {
+ if (!is_startline(scode, bracket_map, cb, atomcount, TRUE))
+ return FALSE;
+ }
+
+ /* Atomic brackets */
+
+ else if (op == OP_ONCE)
+ {
+ if (!is_startline(scode, bracket_map, cb, atomcount + 1, inassert))
+ return FALSE;
+ }
+
+ /* .* means "start at start or after \n" if it isn't in atomic brackets or
+ brackets that may be referenced or an assertion, and as long as the pattern
+ does not contain *PRUNE or *SKIP, because these break the feature. Consider,
+ for example, /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab",
+ i.e. not at the start of a line. There is also an option that disables this
+ optimization. */
+
+ else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR)
+ {
+ if (scode[1] != OP_ANY || (bracket_map & cb->backref_map) != 0 ||
+ atomcount > 0 || cb->had_pruneorskip || inassert ||
+ (cb->external_options & PCRE2_NO_DOTSTAR_ANCHOR) != 0)
+ return FALSE;
+ }
+
+ /* Check for explicit circumflex; anything else gives a FALSE result. Note
+ in particular that this includes atomic brackets OP_ONCE because the number
+ of characters matched by .* cannot be adjusted inside them. */
+
+ else if (op != OP_CIRC && op != OP_CIRCM) return FALSE;
+
+ /* Move on to the next alternative */
+
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT); /* Loop for each alternative */
+return TRUE;
+}
+
+
+
+/*************************************************
+* Scan compiled regex for recursion reference *
+*************************************************/
+
+/* This function scans through a compiled pattern until it finds an instance of
+OP_RECURSE.
+
+Arguments:
+ code points to start of expression
+ utf TRUE in UTF mode
+
+Returns: pointer to the opcode for OP_RECURSE, or NULL if not found
+*/
+
+static PCRE2_SPTR
+find_recurse(PCRE2_SPTR code, BOOL utf)
+{
+for (;;)
+ {
+ PCRE2_UCHAR c = *code;
+ if (c == OP_END) return NULL;
+ if (c == OP_RECURSE) return code;
+
+ /* XCLASS is used for classes that cannot be represented just by a bit map.
+ This includes negated single high-valued characters. CALLOUT_STR is used for
+ callouts with string arguments. In both cases the length in the table is
+ zero; the actual length is stored in the compiled code. */
+
+ if (c == OP_XCLASS) code += GET(code, 1);
+ else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE);
+
+ /* Otherwise, we can get the item's length from the table, except that for
+ repeated character types, we have to test for \p and \P, which have an extra
+ two code units of parameters, and for MARK/PRUNE/SKIP/THEN with an argument,
+ we must add in its length. */
+
+ else
+ {
+ switch(c)
+ {
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
+ break;
+
+ case OP_TYPEPOSUPTO:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)
+ code += 2;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ code += code[1];
+ break;
+ }
+
+ /* Add in the fixed length from the table */
+
+ code += PRIV(OP_lengths)[c];
+
+ /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may
+ be followed by a multi-unit character. The length in the table is a
+ minimum, so we have to arrange to skip the extra units. */
+
+#ifdef MAYBE_UTF_MULTI
+ if (utf) switch(c)
+ {
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_EXACT:
+ case OP_EXACTI:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ case OP_UPTO:
+ case OP_UPTOI:
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ case OP_STAR:
+ case OP_STARI:
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);
+ break;
+ }
+#else
+ (void)(utf); /* Keep compiler happy by referencing function argument */
+#endif /* MAYBE_UTF_MULTI */
+ }
+ }
+}
+
+
+
+/*************************************************
+* Check for asserted fixed first code unit *
+*************************************************/
+
+/* During compilation, the "first code unit" settings from forward assertions
+are discarded, because they can cause conflicts with actual literals that
+follow. However, if we end up without a first code unit setting for an
+unanchored pattern, it is worth scanning the regex to see if there is an
+initial asserted first code unit. If all branches start with the same asserted
+code unit, or with a non-conditional bracket all of whose alternatives start
+with the same asserted code unit (recurse ad lib), then we return that code
+unit, with the flags set to zero or REQ_CASELESS; otherwise return zero with
+REQ_NONE in the flags.
+
+Arguments:
+ code points to start of compiled pattern
+ flags points to the first code unit flags
+ inassert non-zero if in an assertion
+
+Returns: the fixed first code unit, or 0 with REQ_NONE in flags
+*/
+
+static uint32_t
+find_firstassertedcu(PCRE2_SPTR code, uint32_t *flags, uint32_t inassert)
+{
+uint32_t c = 0;
+uint32_t cflags = REQ_NONE;
+
+*flags = REQ_NONE;
+do {
+ uint32_t d;
+ uint32_t dflags;
+ int xl = (*code == OP_CBRA || *code == OP_SCBRA ||
+ *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0;
+ PCRE2_SPTR scode = first_significant_code(code + 1+LINK_SIZE + xl, TRUE);
+ PCRE2_UCHAR op = *scode;
+
+ switch(op)
+ {
+ default:
+ return 0;
+
+ case OP_BRA:
+ case OP_BRAPOS:
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_ASSERT:
+ case OP_ASSERT_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ d = find_firstassertedcu(scode, &dflags, inassert +
+ ((op == OP_ASSERT || op == OP_ASSERT_NA)?1:0));
+ if (dflags >= REQ_NONE) return 0;
+ if (cflags >= REQ_NONE) { c = d; cflags = dflags; }
+ else if (c != d || cflags != dflags) return 0;
+ break;
+
+ case OP_EXACT:
+ scode += IMM2_SIZE;
+ /* Fall through */
+
+ case OP_CHAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSPLUS:
+ if (inassert == 0) return 0;
+ if (cflags >= REQ_NONE) { c = scode[1]; cflags = 0; }
+ else if (c != scode[1]) return 0;
+ break;
+
+ case OP_EXACTI:
+ scode += IMM2_SIZE;
+ /* Fall through */
+
+ case OP_CHARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSPLUSI:
+ if (inassert == 0) return 0;
+
+ /* If the character is more than one code unit long, we cannot set its
+ first code unit when matching caselessly. Later scanning may pick up
+ multiple code units. */
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (scode[1] >= 0x80) return 0;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ if (scode[1] >= 0xd800 && scode[1] <= 0xdfff) return 0;
+#endif
+#endif
+
+ if (cflags >= REQ_NONE) { c = scode[1]; cflags = REQ_CASELESS; }
+ else if (c != scode[1]) return 0;
+ break;
+ }
+
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT);
+
+*flags = cflags;
+return c;
+}
+
+
+
+/*************************************************
+* Add an entry to the name/number table *
+*************************************************/
+
+/* This function is called between compiling passes to add an entry to the
+name/number table, maintaining alphabetical order. Checking for permitted
+and forbidden duplicates has already been done.
+
+Arguments:
+ cb the compile data block
+ name the name to add
+ length the length of the name
+ groupno the group number
+ tablecount the count of names in the table so far
+
+Returns: nothing
+*/
+
+static void
+add_name_to_table(compile_block *cb, PCRE2_SPTR name, int length,
+ unsigned int groupno, uint32_t tablecount)
+{
+uint32_t i;
+PCRE2_UCHAR *slot = cb->name_table;
+
+for (i = 0; i < tablecount; i++)
+ {
+ int crc = memcmp(name, slot+IMM2_SIZE, CU2BYTES(length));
+ if (crc == 0 && slot[IMM2_SIZE+length] != 0)
+ crc = -1; /* Current name is a substring */
+
+ /* Make space in the table and break the loop for an earlier name. For a
+ duplicate or later name, carry on. We do this for duplicates so that in the
+ simple case (when ?(| is not used) they are in order of their numbers. In all
+ cases they are in the order in which they appear in the pattern. */
+
+ if (crc < 0)
+ {
+ (void)memmove(slot + cb->name_entry_size, slot,
+ CU2BYTES((tablecount - i) * cb->name_entry_size));
+ break;
+ }
+
+ /* Continue the loop for a later or duplicate name */
+
+ slot += cb->name_entry_size;
+ }
+
+PUT2(slot, 0, groupno);
+memcpy(slot + IMM2_SIZE, name, CU2BYTES(length));
+
+/* Add a terminating zero and fill the rest of the slot with zeroes so that
+the memory is all initialized. Otherwise valgrind moans about uninitialized
+memory when saving serialized compiled patterns. */
+
+memset(slot + IMM2_SIZE + length, 0,
+ CU2BYTES(cb->name_entry_size - length - IMM2_SIZE));
+}
+
+
+
+/*************************************************
+* Skip in parsed pattern *
+*************************************************/
+
+/* This function is called to skip parts of the parsed pattern when finding the
+length of a lookbehind branch. It is called after (*ACCEPT) and (*FAIL) to find
+the end of the branch, it is called to skip over an internal lookaround or
+(DEFINE) group, and it is also called to skip to the end of a class, during
+which it will never encounter nested groups (but there's no need to have
+special code for that).
+
+When called to find the end of a branch or group, pptr must point to the first
+meta code inside the branch, not the branch-starting code. In other cases it
+can point to the item that causes the function to be called.
+
+Arguments:
+ pptr current pointer to skip from
+ skiptype PSKIP_CLASS when skipping to end of class
+ PSKIP_ALT when META_ALT ends the skip
+ PSKIP_KET when only META_KET ends the skip
+
+Returns: new value of pptr
+ NULL if META_END is reached - should never occur
+ or for an unknown meta value - likewise
+*/
+
+static uint32_t *
+parsed_skip(uint32_t *pptr, uint32_t skiptype)
+{
+uint32_t nestlevel = 0;
+
+for (;; pptr++)
+ {
+ uint32_t meta = META_CODE(*pptr);
+
+ switch(meta)
+ {
+ default: /* Just skip over most items */
+ if (meta < META_END) continue; /* Literal */
+ break;
+
+ /* This should never occur. */
+
+ case META_END:
+ return NULL;
+
+ /* The data for these items is variable in length. */
+
+ case META_BACKREF: /* Offset is present only if group >= 10 */
+ if (META_DATA(*pptr) >= 10) pptr += SIZEOFFSET;
+ break;
+
+ case META_ESCAPE: /* A few escapes are followed by data items. */
+ switch (META_DATA(*pptr))
+ {
+ case ESC_P:
+ case ESC_p:
+ pptr += 1;
+ break;
+
+ case ESC_g:
+ case ESC_k:
+ pptr += 1 + SIZEOFFSET;
+ break;
+ }
+ break;
+
+ case META_MARK: /* Add the length of the name. */
+ case META_COMMIT_ARG:
+ case META_PRUNE_ARG:
+ case META_SKIP_ARG:
+ case META_THEN_ARG:
+ pptr += pptr[1];
+ break;
+
+ /* These are the "active" items in this loop. */
+
+ case META_CLASS_END:
+ if (skiptype == PSKIP_CLASS) return pptr;
+ break;
+
+ case META_ATOMIC:
+ case META_CAPTURE:
+ case META_COND_ASSERT:
+ case META_COND_DEFINE:
+ case META_COND_NAME:
+ case META_COND_NUMBER:
+ case META_COND_RNAME:
+ case META_COND_RNUMBER:
+ case META_COND_VERSION:
+ case META_LOOKAHEAD:
+ case META_LOOKAHEADNOT:
+ case META_LOOKAHEAD_NA:
+ case META_LOOKBEHIND:
+ case META_LOOKBEHINDNOT:
+ case META_LOOKBEHIND_NA:
+ case META_NOCAPTURE:
+ case META_SCRIPT_RUN:
+ nestlevel++;
+ break;
+
+ case META_ALT:
+ if (nestlevel == 0 && skiptype == PSKIP_ALT) return pptr;
+ break;
+
+ case META_KET:
+ if (nestlevel == 0) return pptr;
+ nestlevel--;
+ break;
+ }
+
+ /* The extra data item length for each meta is in a table. */
+
+ meta = (meta >> 16) & 0x7fff;
+ if (meta >= sizeof(meta_extra_lengths)) return NULL;
+ pptr += meta_extra_lengths[meta];
+ }
+/* Control never reaches here */
+return pptr;
+}
+
+
+
+/*************************************************
+* Find length of a parsed group *
+*************************************************/
+
+/* This is called for nested groups within a branch of a lookbehind whose
+length is being computed. If all the branches in the nested group have the same
+length, that is OK. On entry, the pointer must be at the first element after
+the group initializing code. On exit it points to OP_KET. Caching is used to
+improve processing speed when the same capturing group occurs many times.
+
+Arguments:
+ pptrptr pointer to pointer in the parsed pattern
+ isinline FALSE if a reference or recursion; TRUE for inline group
+ errcodeptr pointer to the errorcode
+ lcptr pointer to the loop counter
+ group number of captured group or -1 for a non-capturing group
+ recurses chain of recurse_check to catch mutual recursion
+ cb pointer to the compile data
+
+Returns: the group length or a negative number
+*/
+
+static int
+get_grouplength(uint32_t **pptrptr, BOOL isinline, int *errcodeptr, int *lcptr,
+ int group, parsed_recurse_check *recurses, compile_block *cb)
+{
+int branchlength;
+int grouplength = -1;
+
+/* The cache can be used only if there is no possibility of there being two
+groups with the same number. We do not need to set the end pointer for a group
+that is being processed as a back reference or recursion, but we must do so for
+an inline group. */
+
+if (group > 0 && (cb->external_flags & PCRE2_DUPCAPUSED) == 0)
+ {
+ uint32_t groupinfo = cb->groupinfo[group];
+ if ((groupinfo & GI_NOT_FIXED_LENGTH) != 0) return -1;
+ if ((groupinfo & GI_SET_FIXED_LENGTH) != 0)
+ {
+ if (isinline) *pptrptr = parsed_skip(*pptrptr, PSKIP_KET);
+ return groupinfo & GI_FIXED_LENGTH_MASK;
+ }
+ }
+
+/* Scan the group. In this case we find the end pointer of necessity. */
+
+for(;;)
+ {
+ branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb);
+ if (branchlength < 0) goto ISNOTFIXED;
+ if (grouplength == -1) grouplength = branchlength;
+ else if (grouplength != branchlength) goto ISNOTFIXED;
+ if (**pptrptr == META_KET) break;
+ *pptrptr += 1; /* Skip META_ALT */
+ }
+
+if (group > 0)
+ cb->groupinfo[group] |= (uint32_t)(GI_SET_FIXED_LENGTH | grouplength);
+return grouplength;
+
+ISNOTFIXED:
+if (group > 0) cb->groupinfo[group] |= GI_NOT_FIXED_LENGTH;
+return -1;
+}
+
+
+
+/*************************************************
+* Find length of a parsed branch *
+*************************************************/
+
+/* Return a fixed length for a branch in a lookbehind, giving an error if the
+length is not fixed. On entry, *pptrptr points to the first element inside the
+branch. On exit it is set to point to the ALT or KET.
+
+Arguments:
+ pptrptr pointer to pointer in the parsed pattern
+ errcodeptr pointer to error code
+ lcptr pointer to loop counter
+ recurses chain of recurse_check to catch mutual recursion
+ cb pointer to compile block
+
+Returns: the length, or a negative value on error
+*/
+
+static int
+get_branchlength(uint32_t **pptrptr, int *errcodeptr, int *lcptr,
+ parsed_recurse_check *recurses, compile_block *cb)
+{
+int branchlength = 0;
+int grouplength;
+uint32_t lastitemlength = 0;
+uint32_t *pptr = *pptrptr;
+PCRE2_SIZE offset;
+parsed_recurse_check this_recurse;
+
+/* A large and/or complex regex can take too long to process. This can happen
+more often when (?| groups are present in the pattern because their length
+cannot be cached. */
+
+if ((*lcptr)++ > 2000)
+ {
+ *errcodeptr = ERR35; /* Lookbehind is too complicated */
+ return -1;
+ }
+
+/* Scan the branch, accumulating the length. */
+
+for (;; pptr++)
+ {
+ parsed_recurse_check *r;
+ uint32_t *gptr, *gptrend;
+ uint32_t escape;
+ uint32_t group = 0;
+ uint32_t itemlength = 0;
+
+ if (*pptr < META_END)
+ {
+ itemlength = 1;
+ }
+
+ else switch (META_CODE(*pptr))
+ {
+ case META_KET:
+ case META_ALT:
+ goto EXIT;
+
+ /* (*ACCEPT) and (*FAIL) terminate the branch, but we must skip to the
+ actual termination. */
+
+ case META_ACCEPT:
+ case META_FAIL:
+ pptr = parsed_skip(pptr, PSKIP_ALT);
+ if (pptr == NULL) goto PARSED_SKIP_FAILED;
+ goto EXIT;
+
+ case META_MARK:
+ case META_COMMIT_ARG:
+ case META_PRUNE_ARG:
+ case META_SKIP_ARG:
+ case META_THEN_ARG:
+ pptr += pptr[1] + 1;
+ break;
+
+ case META_CIRCUMFLEX:
+ case META_COMMIT:
+ case META_DOLLAR:
+ case META_PRUNE:
+ case META_SKIP:
+ case META_THEN:
+ break;
+
+ case META_OPTIONS:
+ pptr += 1;
+ break;
+
+ case META_BIGVALUE:
+ itemlength = 1;
+ pptr += 1;
+ break;
+
+ case META_CLASS:
+ case META_CLASS_NOT:
+ itemlength = 1;
+ pptr = parsed_skip(pptr, PSKIP_CLASS);
+ if (pptr == NULL) goto PARSED_SKIP_FAILED;
+ break;
+
+ case META_CLASS_EMPTY_NOT:
+ case META_DOT:
+ itemlength = 1;
+ break;
+
+ case META_CALLOUT_NUMBER:
+ pptr += 3;
+ break;
+
+ case META_CALLOUT_STRING:
+ pptr += 3 + SIZEOFFSET;
+ break;
+
+ /* Only some escapes consume a character. Of those, \R and \X are never
+ allowed because they might match more than character. \C is allowed only in
+ 32-bit and non-UTF 8/16-bit modes. */
+
+ case META_ESCAPE:
+ escape = META_DATA(*pptr);
+ if (escape == ESC_R || escape == ESC_X) return -1;
+ if (escape > ESC_b && escape < ESC_Z)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if ((cb->external_options & PCRE2_UTF) != 0 && escape == ESC_C)
+ {
+ *errcodeptr = ERR36;
+ return -1;
+ }
+#endif
+ itemlength = 1;
+ if (escape == ESC_p || escape == ESC_P) pptr++; /* Skip prop data */
+ }
+ break;
+
+ /* Lookaheads do not contribute to the length of this branch, but they may
+ contain lookbehinds within them whose lengths need to be set. */
+
+ case META_LOOKAHEAD:
+ case META_LOOKAHEADNOT:
+ case META_LOOKAHEAD_NA:
+ *errcodeptr = check_lookbehinds(pptr + 1, &pptr, recurses, cb, lcptr);
+ if (*errcodeptr != 0) return -1;
+
+ /* Ignore any qualifiers that follow a lookahead assertion. */
+
+ switch (pptr[1])
+ {
+ case META_ASTERISK:
+ case META_ASTERISK_PLUS:
+ case META_ASTERISK_QUERY:
+ case META_PLUS:
+ case META_PLUS_PLUS:
+ case META_PLUS_QUERY:
+ case META_QUERY:
+ case META_QUERY_PLUS:
+ case META_QUERY_QUERY:
+ pptr++;
+ break;
+
+ case META_MINMAX:
+ case META_MINMAX_PLUS:
+ case META_MINMAX_QUERY:
+ pptr += 3;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ /* A nested lookbehind does not contribute any length to this lookbehind,
+ but must itself be checked and have its lengths set. */
+
+ case META_LOOKBEHIND:
+ case META_LOOKBEHINDNOT:
+ case META_LOOKBEHIND_NA:
+ if (!set_lookbehind_lengths(&pptr, errcodeptr, lcptr, recurses, cb))
+ return -1;
+ break;
+
+ /* Back references and recursions are handled by very similar code. At this
+ stage, the names generated in the parsing pass are available, but the main
+ name table has not yet been created. So for the named varieties, scan the
+ list of names in order to get the number of the first one in the pattern,
+ and whether or not this name is duplicated. */
+
+ case META_BACKREF_BYNAME:
+ if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0)
+ goto ISNOTFIXED;
+ /* Fall through */
+
+ case META_RECURSE_BYNAME:
+ {
+ int i;
+ PCRE2_SPTR name;
+ BOOL is_dupname = FALSE;
+ named_group *ng = cb->named_groups;
+ uint32_t meta_code = META_CODE(*pptr);
+ uint32_t length = *(++pptr);
+
+ GETPLUSOFFSET(offset, pptr);
+ name = cb->start_pattern + offset;
+ for (i = 0; i < cb->names_found; i++, ng++)
+ {
+ if (length == ng->length && PRIV(strncmp)(name, ng->name, length) == 0)
+ {
+ group = ng->number;
+ is_dupname = ng->isdup;
+ break;
+ }
+ }
+
+ if (group == 0)
+ {
+ *errcodeptr = ERR15; /* Non-existent subpattern */
+ cb->erroroffset = offset;
+ return -1;
+ }
+
+ /* A numerical back reference can be fixed length if duplicate capturing
+ groups are not being used. A non-duplicate named back reference can also
+ be handled. */
+
+ if (meta_code == META_RECURSE_BYNAME ||
+ (!is_dupname && (cb->external_flags & PCRE2_DUPCAPUSED) == 0))
+ goto RECURSE_OR_BACKREF_LENGTH; /* Handle as a numbered version. */
+ }
+ goto ISNOTFIXED; /* Duplicate name or number */
+
+ /* The offset values for back references < 10 are in a separate vector
+ because otherwise they would use more than two parsed pattern elements on
+ 64-bit systems. */
+
+ case META_BACKREF:
+ if ((cb->external_options & PCRE2_MATCH_UNSET_BACKREF) != 0 ||
+ (cb->external_flags & PCRE2_DUPCAPUSED) != 0)
+ goto ISNOTFIXED;
+ group = META_DATA(*pptr);
+ if (group < 10)
+ {
+ offset = cb->small_ref_offset[group];
+ goto RECURSE_OR_BACKREF_LENGTH;
+ }
+
+ /* Fall through */
+ /* For groups >= 10 - picking up group twice does no harm. */
+
+ /* A true recursion implies not fixed length, but a subroutine call may
+ be OK. Back reference "recursions" are also failed. */
+
+ case META_RECURSE:
+ group = META_DATA(*pptr);
+ GETPLUSOFFSET(offset, pptr);
+
+ RECURSE_OR_BACKREF_LENGTH:
+ if (group > cb->bracount)
+ {
+ cb->erroroffset = offset;
+ *errcodeptr = ERR15; /* Non-existent subpattern */
+ return -1;
+ }
+ if (group == 0) goto ISNOTFIXED; /* Local recursion */
+ for (gptr = cb->parsed_pattern; *gptr != META_END; gptr++)
+ {
+ if (META_CODE(*gptr) == META_BIGVALUE) gptr++;
+ else if (*gptr == (META_CAPTURE | group)) break;
+ }
+
+ /* We must start the search for the end of the group at the first meta code
+ inside the group. Otherwise it will be treated as an enclosed group. */
+
+ gptrend = parsed_skip(gptr + 1, PSKIP_KET);
+ if (gptrend == NULL) goto PARSED_SKIP_FAILED;
+ if (pptr > gptr && pptr < gptrend) goto ISNOTFIXED; /* Local recursion */
+ for (r = recurses; r != NULL; r = r->prev) if (r->groupptr == gptr) break;
+ if (r != NULL) goto ISNOTFIXED; /* Mutual recursion */
+ this_recurse.prev = recurses;
+ this_recurse.groupptr = gptr;
+
+ /* We do not need to know the position of the end of the group, that is,
+ gptr is not used after the call to get_grouplength(). Setting the second
+ argument FALSE stops it scanning for the end when the length can be found
+ in the cache. */
+
+ gptr++;
+ grouplength = get_grouplength(&gptr, FALSE, errcodeptr, lcptr, group,
+ &this_recurse, cb);
+ if (grouplength < 0)
+ {
+ if (*errcodeptr == 0) goto ISNOTFIXED;
+ return -1; /* Error already set */
+ }
+ itemlength = grouplength;
+ break;
+
+ /* A (DEFINE) group is never obeyed inline and so it does not contribute to
+ the length of this branch. Skip from the following item to the next
+ unpaired ket. */
+
+ case META_COND_DEFINE:
+ pptr = parsed_skip(pptr + 1, PSKIP_KET);
+ break;
+
+ /* Check other nested groups - advance past the initial data for each type
+ and then seek a fixed length with get_grouplength(). */
+
+ case META_COND_NAME:
+ case META_COND_NUMBER:
+ case META_COND_RNAME:
+ case META_COND_RNUMBER:
+ pptr += 2 + SIZEOFFSET;
+ goto CHECK_GROUP;
+
+ case META_COND_ASSERT:
+ pptr += 1;
+ goto CHECK_GROUP;
+
+ case META_COND_VERSION:
+ pptr += 4;
+ goto CHECK_GROUP;
+
+ case META_CAPTURE:
+ group = META_DATA(*pptr);
+ /* Fall through */
+
+ case META_ATOMIC:
+ case META_NOCAPTURE:
+ case META_SCRIPT_RUN:
+ pptr++;
+ CHECK_GROUP:
+ grouplength = get_grouplength(&pptr, TRUE, errcodeptr, lcptr, group,
+ recurses, cb);
+ if (grouplength < 0) return -1;
+ itemlength = grouplength;
+ break;
+
+ /* Exact repetition is OK; variable repetition is not. A repetition of zero
+ must subtract the length that has already been added. */
+
+ case META_MINMAX:
+ case META_MINMAX_PLUS:
+ case META_MINMAX_QUERY:
+ if (pptr[1] == pptr[2])
+ {
+ switch(pptr[1])
+ {
+ case 0:
+ branchlength -= lastitemlength;
+ break;
+
+ case 1:
+ itemlength = 0;
+ break;
+
+ default: /* Check for integer overflow */
+ if (lastitemlength != 0 && /* Should not occur, but just in case */
+ INT_MAX/lastitemlength < pptr[1] - 1)
+ {
+ *errcodeptr = ERR87; /* Integer overflow; lookbehind too big */
+ return -1;
+ }
+ itemlength = (pptr[1] - 1) * lastitemlength;
+ break;
+ }
+ pptr += 2;
+ break;
+ }
+ /* Fall through */
+
+ /* Any other item means this branch does not have a fixed length. */
+
+ default:
+ ISNOTFIXED:
+ *errcodeptr = ERR25; /* Not fixed length */
+ return -1;
+ }
+
+ /* Add the item length to the branchlength, checking for integer overflow and
+ for the branch length exceeding the limit. */
+
+ if (INT_MAX - branchlength < (int)itemlength ||
+ (branchlength += itemlength) > LOOKBEHIND_MAX)
+ {
+ *errcodeptr = ERR87;
+ return -1;
+ }
+
+ /* Save this item length for use if the next item is a quantifier. */
+
+ lastitemlength = itemlength;
+ }
+
+EXIT:
+*pptrptr = pptr;
+return branchlength;
+
+PARSED_SKIP_FAILED:
+*errcodeptr = ERR90;
+return -1;
+}
+
+
+
+/*************************************************
+* Set lengths in a lookbehind *
+*************************************************/
+
+/* This function is called for each lookbehind, to set the lengths in its
+branches. An error occurs if any branch does not have a fixed length that is
+less than the maximum (65535). On exit, the pointer must be left on the final
+ket.
+
+The function also maintains the max_lookbehind value. Any lookbehind branch
+that contains a nested lookbehind may actually look further back than the
+length of the branch. The additional amount is passed back from
+get_branchlength() as an "extra" value.
+
+Arguments:
+ pptrptr pointer to pointer in the parsed pattern
+ errcodeptr pointer to error code
+ lcptr pointer to loop counter
+ recurses chain of recurse_check to catch mutual recursion
+ cb pointer to compile block
+
+Returns: TRUE if all is well
+ FALSE otherwise, with error code and offset set
+*/
+
+static BOOL
+set_lookbehind_lengths(uint32_t **pptrptr, int *errcodeptr, int *lcptr,
+ parsed_recurse_check *recurses, compile_block *cb)
+{
+PCRE2_SIZE offset;
+int branchlength;
+uint32_t *bptr = *pptrptr;
+
+READPLUSOFFSET(offset, bptr); /* Offset for error messages */
+*pptrptr += SIZEOFFSET;
+
+do
+ {
+ *pptrptr += 1;
+ branchlength = get_branchlength(pptrptr, errcodeptr, lcptr, recurses, cb);
+ if (branchlength < 0)
+ {
+ /* The errorcode and offset may already be set from a nested lookbehind. */
+ if (*errcodeptr == 0) *errcodeptr = ERR25;
+ if (cb->erroroffset == PCRE2_UNSET) cb->erroroffset = offset;
+ return FALSE;
+ }
+ if (branchlength > cb->max_lookbehind) cb->max_lookbehind = branchlength;
+ *bptr |= branchlength; /* branchlength never more than 65535 */
+ bptr = *pptrptr;
+ }
+while (*bptr == META_ALT);
+
+return TRUE;
+}
+
+
+
+/*************************************************
+* Check parsed pattern lookbehinds *
+*************************************************/
+
+/* This function is called at the end of parsing a pattern if any lookbehinds
+were encountered. It scans the parsed pattern for them, calling
+set_lookbehind_lengths() for each one. At the start, the errorcode is zero and
+the error offset is marked unset. The enables the functions above not to
+override settings from deeper nestings.
+
+This function is called recursively from get_branchlength() for lookaheads in
+order to process any lookbehinds that they may contain. It stops when it hits a
+non-nested closing parenthesis in this case, returning a pointer to it.
+
+Arguments
+ pptr points to where to start (start of pattern or start of lookahead)
+ retptr if not NULL, return the ket pointer here
+ recurses chain of recurse_check to catch mutual recursion
+ cb points to the compile block
+ lcptr points to loop counter
+
+Returns: 0 on success, or an errorcode (cb->erroroffset will be set)
+*/
+
+static int
+check_lookbehinds(uint32_t *pptr, uint32_t **retptr,
+ parsed_recurse_check *recurses, compile_block *cb, int *lcptr)
+{
+int errorcode = 0;
+int nestlevel = 0;
+
+cb->erroroffset = PCRE2_UNSET;
+
+for (; *pptr != META_END; pptr++)
+ {
+ if (*pptr < META_END) continue; /* Literal */
+
+ switch (META_CODE(*pptr))
+ {
+ default:
+ return ERR70; /* Unrecognized meta code */
+
+ case META_ESCAPE:
+ if (*pptr - META_ESCAPE == ESC_P || *pptr - META_ESCAPE == ESC_p)
+ pptr += 1;
+ break;
+
+ case META_KET:
+ if (--nestlevel < 0)
+ {
+ if (retptr != NULL) *retptr = pptr;
+ return 0;
+ }
+ break;
+
+ case META_ATOMIC:
+ case META_CAPTURE:
+ case META_COND_ASSERT:
+ case META_LOOKAHEAD:
+ case META_LOOKAHEADNOT:
+ case META_LOOKAHEAD_NA:
+ case META_NOCAPTURE:
+ case META_SCRIPT_RUN:
+ nestlevel++;
+ break;
+
+ case META_ACCEPT:
+ case META_ALT:
+ case META_ASTERISK:
+ case META_ASTERISK_PLUS:
+ case META_ASTERISK_QUERY:
+ case META_BACKREF:
+ case META_CIRCUMFLEX:
+ case META_CLASS:
+ case META_CLASS_EMPTY:
+ case META_CLASS_EMPTY_NOT:
+ case META_CLASS_END:
+ case META_CLASS_NOT:
+ case META_COMMIT:
+ case META_DOLLAR:
+ case META_DOT:
+ case META_FAIL:
+ case META_PLUS:
+ case META_PLUS_PLUS:
+ case META_PLUS_QUERY:
+ case META_PRUNE:
+ case META_QUERY:
+ case META_QUERY_PLUS:
+ case META_QUERY_QUERY:
+ case META_RANGE_ESCAPED:
+ case META_RANGE_LITERAL:
+ case META_SKIP:
+ case META_THEN:
+ break;
+
+ case META_RECURSE:
+ pptr += SIZEOFFSET;
+ break;
+
+ case META_BACKREF_BYNAME:
+ case META_RECURSE_BYNAME:
+ pptr += 1 + SIZEOFFSET;
+ break;
+
+ case META_COND_DEFINE:
+ pptr += SIZEOFFSET;
+ nestlevel++;
+ break;
+
+ case META_COND_NAME:
+ case META_COND_NUMBER:
+ case META_COND_RNAME:
+ case META_COND_RNUMBER:
+ pptr += 1 + SIZEOFFSET;
+ nestlevel++;
+ break;
+
+ case META_COND_VERSION:
+ pptr += 3;
+ nestlevel++;
+ break;
+
+ case META_CALLOUT_STRING:
+ pptr += 3 + SIZEOFFSET;
+ break;
+
+ case META_BIGVALUE:
+ case META_OPTIONS:
+ case META_POSIX:
+ case META_POSIX_NEG:
+ pptr += 1;
+ break;
+
+ case META_MINMAX:
+ case META_MINMAX_QUERY:
+ case META_MINMAX_PLUS:
+ pptr += 2;
+ break;
+
+ case META_CALLOUT_NUMBER:
+ pptr += 3;
+ break;
+
+ case META_MARK:
+ case META_COMMIT_ARG:
+ case META_PRUNE_ARG:
+ case META_SKIP_ARG:
+ case META_THEN_ARG:
+ pptr += 1 + pptr[1];
+ break;
+
+ case META_LOOKBEHIND:
+ case META_LOOKBEHINDNOT:
+ case META_LOOKBEHIND_NA:
+ if (!set_lookbehind_lengths(&pptr, &errorcode, lcptr, recurses, cb))
+ return errorcode;
+ break;
+ }
+ }
+
+return 0;
+}
+
+
+
+/*************************************************
+* External function to compile a pattern *
+*************************************************/
+
+/* This function reads a regular expression in the form of a string and returns
+a pointer to a block of store holding a compiled version of the expression.
+
+Arguments:
+ pattern the regular expression
+ patlen the length of the pattern, or PCRE2_ZERO_TERMINATED
+ options option bits
+ errorptr pointer to errorcode
+ erroroffset pointer to error offset
+ ccontext points to a compile context or is NULL
+
+Returns: pointer to compiled data block, or NULL on error,
+ with errorcode and erroroffset set
+*/
+
+PCRE2_EXP_DEFN pcre2_code * PCRE2_CALL_CONVENTION
+pcre2_compile(PCRE2_SPTR pattern, PCRE2_SIZE patlen, uint32_t options,
+ int *errorptr, PCRE2_SIZE *erroroffset, pcre2_compile_context *ccontext)
+{
+BOOL utf; /* Set TRUE for UTF mode */
+BOOL ucp; /* Set TRUE for UCP mode */
+BOOL has_lookbehind = FALSE; /* Set TRUE if a lookbehind is found */
+BOOL zero_terminated; /* Set TRUE for zero-terminated pattern */
+pcre2_real_code *re = NULL; /* What we will return */
+compile_block cb; /* "Static" compile-time data */
+const uint8_t *tables; /* Char tables base pointer */
+
+PCRE2_UCHAR *code; /* Current pointer in compiled code */
+PCRE2_SPTR codestart; /* Start of compiled code */
+PCRE2_SPTR ptr; /* Current pointer in pattern */
+uint32_t *pptr; /* Current pointer in parsed pattern */
+
+PCRE2_SIZE length = 1; /* Allow for final END opcode */
+PCRE2_SIZE usedlength; /* Actual length used */
+PCRE2_SIZE re_blocksize; /* Size of memory block */
+PCRE2_SIZE big32count = 0; /* 32-bit literals >= 0x80000000 */
+PCRE2_SIZE parsed_size_needed; /* Needed for parsed pattern */
+
+uint32_t firstcuflags, reqcuflags; /* Type of first/req code unit */
+uint32_t firstcu, reqcu; /* Value of first/req code unit */
+uint32_t setflags = 0; /* NL and BSR set flags */
+
+uint32_t skipatstart; /* When checking (*UTF) etc */
+uint32_t limit_heap = UINT32_MAX;
+uint32_t limit_match = UINT32_MAX; /* Unset match limits */
+uint32_t limit_depth = UINT32_MAX;
+
+int newline = 0; /* Unset; can be set by the pattern */
+int bsr = 0; /* Unset; can be set by the pattern */
+int errorcode = 0; /* Initialize to avoid compiler warn */
+int regexrc; /* Return from compile */
+
+uint32_t i; /* Local loop counter */
+
+/* Comments at the head of this file explain about these variables. */
+
+uint32_t stack_groupinfo[GROUPINFO_DEFAULT_SIZE];
+uint32_t stack_parsed_pattern[PARSED_PATTERN_DEFAULT_SIZE];
+named_group named_groups[NAMED_GROUP_LIST_SIZE];
+
+/* The workspace is used in different ways in the different compiling phases.
+It needs to be 16-bit aligned for the preliminary parsing scan. */
+
+uint32_t c16workspace[C16_WORK_SIZE];
+PCRE2_UCHAR *cworkspace = (PCRE2_UCHAR *)c16workspace;
+
+
+/* -------------- Check arguments and set up the pattern ----------------- */
+
+/* There must be error code and offset pointers. */
+
+if (errorptr == NULL || erroroffset == NULL) return NULL;
+*errorptr = ERR0;
+*erroroffset = 0;
+
+/* There must be a pattern! */
+
+if (pattern == NULL)
+ {
+ *errorptr = ERR16;
+ return NULL;
+ }
+
+/* A NULL compile context means "use a default context" */
+
+if (ccontext == NULL)
+ ccontext = (pcre2_compile_context *)(&PRIV(default_compile_context));
+
+/* PCRE2_MATCH_INVALID_UTF implies UTF */
+
+if ((options & PCRE2_MATCH_INVALID_UTF) != 0) options |= PCRE2_UTF;
+
+/* Check that all undefined public option bits are zero. */
+
+if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0 ||
+ (ccontext->extra_options & ~PUBLIC_COMPILE_EXTRA_OPTIONS) != 0)
+ {
+ *errorptr = ERR17;
+ return NULL;
+ }
+
+if ((options & PCRE2_LITERAL) != 0 &&
+ ((options & ~PUBLIC_LITERAL_COMPILE_OPTIONS) != 0 ||
+ (ccontext->extra_options & ~PUBLIC_LITERAL_COMPILE_EXTRA_OPTIONS) != 0))
+ {
+ *errorptr = ERR92;
+ return NULL;
+ }
+
+/* A zero-terminated pattern is indicated by the special length value
+PCRE2_ZERO_TERMINATED. Check for an overlong pattern. */
+
+if ((zero_terminated = (patlen == PCRE2_ZERO_TERMINATED)))
+ patlen = PRIV(strlen)(pattern);
+
+if (patlen > ccontext->max_pattern_length)
+ {
+ *errorptr = ERR88;
+ return NULL;
+ }
+
+/* From here on, all returns from this function should end up going via the
+EXIT label. */
+
+
+/* ------------ Initialize the "static" compile data -------------- */
+
+tables = (ccontext->tables != NULL)? ccontext->tables : PRIV(default_tables);
+
+cb.lcc = tables + lcc_offset; /* Individual */
+cb.fcc = tables + fcc_offset; /* character */
+cb.cbits = tables + cbits_offset; /* tables */
+cb.ctypes = tables + ctypes_offset;
+
+cb.assert_depth = 0;
+cb.bracount = 0;
+cb.cx = ccontext;
+cb.dupnames = FALSE;
+cb.end_pattern = pattern + patlen;
+cb.erroroffset = 0;
+cb.external_flags = 0;
+cb.external_options = options;
+cb.groupinfo = stack_groupinfo;
+cb.had_recurse = FALSE;
+cb.lastcapture = 0;
+cb.max_lookbehind = 0;
+cb.name_entry_size = 0;
+cb.name_table = NULL;
+cb.named_groups = named_groups;
+cb.named_group_list_size = NAMED_GROUP_LIST_SIZE;
+cb.names_found = 0;
+cb.open_caps = NULL;
+cb.parens_depth = 0;
+cb.parsed_pattern = stack_parsed_pattern;
+cb.req_varyopt = 0;
+cb.start_code = cworkspace;
+cb.start_pattern = pattern;
+cb.start_workspace = cworkspace;
+cb.workspace_size = COMPILE_WORK_SIZE;
+
+/* Maximum back reference and backref bitmap. The bitmap records up to 31 back
+references to help in deciding whether (.*) can be treated as anchored or not.
+*/
+
+cb.top_backref = 0;
+cb.backref_map = 0;
+
+/* Escape sequences \1 to \9 are always back references, but as they are only
+two characters long, only two elements can be used in the parsed_pattern
+vector. The first contains the reference, and we'd like to use the second to
+record the offset in the pattern, so that forward references to non-existent
+groups can be diagnosed later with an offset. However, on 64-bit systems,
+PCRE2_SIZE won't fit. Instead, we have a vector of offsets for the first
+occurrence of \1 to \9, indexed by the second parsed_pattern value. All other
+references have enough space for the offset to be put into the parsed pattern.
+*/
+
+for (i = 0; i < 10; i++) cb.small_ref_offset[i] = PCRE2_UNSET;
+
+
+/* --------------- Start looking at the pattern --------------- */
+
+/* Unless PCRE2_LITERAL is set, check for global one-time option settings at
+the start of the pattern, and remember the offset to the actual regex. With
+valgrind support, make the terminator of a zero-terminated pattern
+inaccessible. This catches bugs that would otherwise only show up for
+non-zero-terminated patterns. */
+
+#ifdef SUPPORT_VALGRIND
+if (zero_terminated) VALGRIND_MAKE_MEM_NOACCESS(pattern + patlen, CU2BYTES(1));
+#endif
+
+ptr = pattern;
+skipatstart = 0;
+
+if ((options & PCRE2_LITERAL) == 0)
+ {
+ while (patlen - skipatstart >= 2 &&
+ ptr[skipatstart] == CHAR_LEFT_PARENTHESIS &&
+ ptr[skipatstart+1] == CHAR_ASTERISK)
+ {
+ for (i = 0; i < sizeof(pso_list)/sizeof(pso); i++)
+ {
+ uint32_t c, pp;
+ pso *p = pso_list + i;
+
+ if (patlen - skipatstart - 2 >= p->length &&
+ PRIV(strncmp_c8)(ptr + skipatstart + 2, (char *)(p->name),
+ p->length) == 0)
+ {
+ skipatstart += p->length + 2;
+ switch(p->type)
+ {
+ case PSO_OPT:
+ cb.external_options |= p->value;
+ break;
+
+ case PSO_FLG:
+ setflags |= p->value;
+ break;
+
+ case PSO_NL:
+ newline = p->value;
+ setflags |= PCRE2_NL_SET;
+ break;
+
+ case PSO_BSR:
+ bsr = p->value;
+ setflags |= PCRE2_BSR_SET;
+ break;
+
+ case PSO_LIMM:
+ case PSO_LIMD:
+ case PSO_LIMH:
+ c = 0;
+ pp = skipatstart;
+ if (!IS_DIGIT(ptr[pp]))
+ {
+ errorcode = ERR60;
+ ptr += pp;
+ goto HAD_EARLY_ERROR;
+ }
+ while (IS_DIGIT(ptr[pp]))
+ {
+ if (c > UINT32_MAX / 10 - 1) break; /* Integer overflow */
+ c = c*10 + (ptr[pp++] - CHAR_0);
+ }
+ if (ptr[pp++] != CHAR_RIGHT_PARENTHESIS)
+ {
+ errorcode = ERR60;
+ ptr += pp;
+ goto HAD_EARLY_ERROR;
+ }
+ if (p->type == PSO_LIMH) limit_heap = c;
+ else if (p->type == PSO_LIMM) limit_match = c;
+ else limit_depth = c;
+ skipatstart += pp - skipatstart;
+ break;
+ }
+ break; /* Out of the table scan loop */
+ }
+ }
+ if (i >= sizeof(pso_list)/sizeof(pso)) break; /* Out of pso loop */
+ }
+ }
+
+/* End of pattern-start options; advance to start of real regex. */
+
+ptr += skipatstart;
+
+/* Can't support UTF or UCP if PCRE2 was built without Unicode support. */
+
+#ifndef SUPPORT_UNICODE
+if ((cb.external_options & (PCRE2_UTF|PCRE2_UCP)) != 0)
+ {
+ errorcode = ERR32;
+ goto HAD_EARLY_ERROR;
+ }
+#endif
+
+/* Check UTF. We have the original options in 'options', with that value as
+modified by (*UTF) etc in cb->external_options. The extra option
+PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not permitted in UTF-16 mode because the
+surrogate code points cannot be represented in UTF-16. */
+
+utf = (cb.external_options & PCRE2_UTF) != 0;
+if (utf)
+ {
+ if ((options & PCRE2_NEVER_UTF) != 0)
+ {
+ errorcode = ERR74;
+ goto HAD_EARLY_ERROR;
+ }
+ if ((options & PCRE2_NO_UTF_CHECK) == 0 &&
+ (errorcode = PRIV(valid_utf)(pattern, patlen, erroroffset)) != 0)
+ goto HAD_ERROR; /* Offset was set by valid_utf() */
+
+#if PCRE2_CODE_UNIT_WIDTH == 16
+ if ((ccontext->extra_options & PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES) != 0)
+ {
+ errorcode = ERR91;
+ goto HAD_EARLY_ERROR;
+ }
+#endif
+ }
+
+/* Check UCP lockout. */
+
+ucp = (cb.external_options & PCRE2_UCP) != 0;
+if (ucp && (cb.external_options & PCRE2_NEVER_UCP) != 0)
+ {
+ errorcode = ERR75;
+ goto HAD_EARLY_ERROR;
+ }
+
+/* Process the BSR setting. */
+
+if (bsr == 0) bsr = ccontext->bsr_convention;
+
+/* Process the newline setting. */
+
+if (newline == 0) newline = ccontext->newline_convention;
+cb.nltype = NLTYPE_FIXED;
+switch(newline)
+ {
+ case PCRE2_NEWLINE_CR:
+ cb.nllen = 1;
+ cb.nl[0] = CHAR_CR;
+ break;
+
+ case PCRE2_NEWLINE_LF:
+ cb.nllen = 1;
+ cb.nl[0] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_NUL:
+ cb.nllen = 1;
+ cb.nl[0] = CHAR_NUL;
+ break;
+
+ case PCRE2_NEWLINE_CRLF:
+ cb.nllen = 2;
+ cb.nl[0] = CHAR_CR;
+ cb.nl[1] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_ANY:
+ cb.nltype = NLTYPE_ANY;
+ break;
+
+ case PCRE2_NEWLINE_ANYCRLF:
+ cb.nltype = NLTYPE_ANYCRLF;
+ break;
+
+ default:
+ errorcode = ERR56;
+ goto HAD_EARLY_ERROR;
+ }
+
+/* Pre-scan the pattern to do two things: (1) Discover the named groups and
+their numerical equivalents, so that this information is always available for
+the remaining processing. (2) At the same time, parse the pattern and put a
+processed version into the parsed_pattern vector. This has escapes interpreted
+and comments removed (amongst other things).
+
+In all but one case, when PCRE2_AUTO_CALLOUT is not set, the number of unsigned
+32-bit ints in the parsed pattern is bounded by the length of the pattern plus
+one (for the terminator) plus four if PCRE2_EXTRA_WORD or PCRE2_EXTRA_LINE is
+set. The exceptional case is when running in 32-bit, non-UTF mode, when literal
+characters greater than META_END (0x80000000) have to be coded as two units. In
+this case, therefore, we scan the pattern to check for such values. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (!utf)
+ {
+ PCRE2_SPTR p;
+ for (p = ptr; p < cb.end_pattern; p++) if (*p >= META_END) big32count++;
+ }
+#endif
+
+/* Ensure that the parsed pattern buffer is big enough. When PCRE2_AUTO_CALLOUT
+is set we have to assume a numerical callout (4 elements) for each character
+plus one at the end. This is overkill, but memory is plentiful these days. For
+many smaller patterns the vector on the stack (which was set up above) can be
+used. */
+
+parsed_size_needed = patlen - skipatstart + big32count;
+
+if ((ccontext->extra_options &
+ (PCRE2_EXTRA_MATCH_WORD|PCRE2_EXTRA_MATCH_LINE)) != 0)
+ parsed_size_needed += 4;
+
+if ((options & PCRE2_AUTO_CALLOUT) != 0)
+ parsed_size_needed = (parsed_size_needed + 1) * 5;
+
+if (parsed_size_needed >= PARSED_PATTERN_DEFAULT_SIZE)
+ {
+ uint32_t *heap_parsed_pattern = ccontext->memctl.malloc(
+ (parsed_size_needed + 1) * sizeof(uint32_t), ccontext->memctl.memory_data);
+ if (heap_parsed_pattern == NULL)
+ {
+ *errorptr = ERR21;
+ goto EXIT;
+ }
+ cb.parsed_pattern = heap_parsed_pattern;
+ }
+cb.parsed_pattern_end = cb.parsed_pattern + parsed_size_needed + 1;
+
+/* Do the parsing scan. */
+
+errorcode = parse_regex(ptr, cb.external_options, &has_lookbehind, &cb);
+if (errorcode != 0) goto HAD_CB_ERROR;
+
+/* Workspace is needed to remember information about numbered groups: whether a
+group can match an empty string and what its fixed length is. This is done to
+avoid the possibility of recursive references causing very long compile times
+when checking these features. Unnumbered groups do not have this exposure since
+they cannot be referenced. We use an indexed vector for this purpose. If there
+are sufficiently few groups, the default vector on the stack, as set up above,
+can be used. Otherwise we have to get/free a special vector. The vector must be
+initialized to zero. */
+
+if (cb.bracount >= GROUPINFO_DEFAULT_SIZE)
+ {
+ cb.groupinfo = ccontext->memctl.malloc(
+ (cb.bracount + 1)*sizeof(uint32_t), ccontext->memctl.memory_data);
+ if (cb.groupinfo == NULL)
+ {
+ errorcode = ERR21;
+ cb.erroroffset = 0;
+ goto HAD_CB_ERROR;
+ }
+ }
+memset(cb.groupinfo, 0, (cb.bracount + 1) * sizeof(uint32_t));
+
+/* If there were any lookbehinds, scan the parsed pattern to figure out their
+lengths. */
+
+if (has_lookbehind)
+ {
+ int loopcount = 0;
+ errorcode = check_lookbehinds(cb.parsed_pattern, NULL, NULL, &cb, &loopcount);
+ if (errorcode != 0) goto HAD_CB_ERROR;
+ }
+
+/* For debugging, there is a function that shows the parsed data vector. */
+
+#ifdef DEBUG_SHOW_PARSED
+fprintf(stderr, "+++ Pre-scan complete:\n");
+show_parsed(&cb);
+#endif
+
+/* For debugging capturing information this code can be enabled. */
+
+#ifdef DEBUG_SHOW_CAPTURES
+ {
+ named_group *ng = cb.named_groups;
+ fprintf(stderr, "+++Captures: %d\n", cb.bracount);
+ for (i = 0; i < cb.names_found; i++, ng++)
+ {
+ fprintf(stderr, "+++%3d %.*s\n", ng->number, ng->length, ng->name);
+ }
+ }
+#endif
+
+/* Pretend to compile the pattern while actually just accumulating the amount
+of memory required in the 'length' variable. This behaviour is triggered by
+passing a non-NULL final argument to compile_regex(). We pass a block of
+workspace (cworkspace) for it to compile parts of the pattern into; the
+compiled code is discarded when it is no longer needed, so hopefully this
+workspace will never overflow, though there is a test for its doing so.
+
+On error, errorcode will be set non-zero, so we don't need to look at the
+result of the function. The initial options have been put into the cb block,
+but we still have to pass a separate options variable (the first argument)
+because the options may change as the pattern is processed. */
+
+cb.erroroffset = patlen; /* For any subsequent errors that do not set it */
+pptr = cb.parsed_pattern;
+code = cworkspace;
+*code = OP_BRA;
+
+(void)compile_regex(cb.external_options, &code, &pptr, &errorcode, 0, &firstcu,
+ &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, &length);
+
+if (errorcode != 0) goto HAD_CB_ERROR; /* Offset is in cb.erroroffset */
+
+/* This should be caught in compile_regex(), but just in case... */
+
+if (length > MAX_PATTERN_SIZE)
+ {
+ errorcode = ERR20;
+ goto HAD_CB_ERROR;
+ }
+
+/* Compute the size of, and then get and initialize, the data block for storing
+the compiled pattern and names table. Integer overflow should no longer be
+possible because nowadays we limit the maximum value of cb.names_found and
+cb.name_entry_size. */
+
+re_blocksize = sizeof(pcre2_real_code) +
+ CU2BYTES(length +
+ (PCRE2_SIZE)cb.names_found * (PCRE2_SIZE)cb.name_entry_size);
+re = (pcre2_real_code *)
+ ccontext->memctl.malloc(re_blocksize, ccontext->memctl.memory_data);
+if (re == NULL)
+ {
+ errorcode = ERR21;
+ goto HAD_CB_ERROR;
+ }
+
+/* The compiler may put padding at the end of the pcre2_real_code structure in
+order to round it up to a multiple of 4 or 8 bytes. This means that when a
+compiled pattern is copied (for example, when serialized) undefined bytes are
+read, and this annoys debuggers such as valgrind. To avoid this, we explicitly
+write to the last 8 bytes of the structure before setting the fields. */
+
+memset((char *)re + sizeof(pcre2_real_code) - 8, 0, 8);
+re->memctl = ccontext->memctl;
+re->tables = tables;
+re->executable_jit = NULL;
+memset(re->start_bitmap, 0, 32 * sizeof(uint8_t));
+re->blocksize = re_blocksize;
+re->magic_number = MAGIC_NUMBER;
+re->compile_options = options;
+re->overall_options = cb.external_options;
+re->extra_options = ccontext->extra_options;
+re->flags = PCRE2_CODE_UNIT_WIDTH/8 | cb.external_flags | setflags;
+re->limit_heap = limit_heap;
+re->limit_match = limit_match;
+re->limit_depth = limit_depth;
+re->first_codeunit = 0;
+re->last_codeunit = 0;
+re->bsr_convention = bsr;
+re->newline_convention = newline;
+re->max_lookbehind = 0;
+re->minlength = 0;
+re->top_bracket = 0;
+re->top_backref = 0;
+re->name_entry_size = cb.name_entry_size;
+re->name_count = cb.names_found;
+
+/* The basic block is immediately followed by the name table, and the compiled
+code follows after that. */
+
+codestart = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code)) +
+ re->name_entry_size * re->name_count;
+
+/* Update the compile data block for the actual compile. The starting points of
+the name/number translation table and of the code are passed around in the
+compile data block. The start/end pattern and initial options are already set
+from the pre-compile phase, as is the name_entry_size field. */
+
+cb.parens_depth = 0;
+cb.assert_depth = 0;
+cb.lastcapture = 0;
+cb.name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code));
+cb.start_code = codestart;
+cb.req_varyopt = 0;
+cb.had_accept = FALSE;
+cb.had_pruneorskip = FALSE;
+cb.open_caps = NULL;
+
+/* If any named groups were found, create the name/number table from the list
+created in the pre-pass. */
+
+if (cb.names_found > 0)
+ {
+ named_group *ng = cb.named_groups;
+ for (i = 0; i < cb.names_found; i++, ng++)
+ add_name_to_table(&cb, ng->name, ng->length, ng->number, i);
+ }
+
+/* Set up a starting, non-extracting bracket, then compile the expression. On
+error, errorcode will be set non-zero, so we don't need to look at the result
+of the function here. */
+
+pptr = cb.parsed_pattern;
+code = (PCRE2_UCHAR *)codestart;
+*code = OP_BRA;
+regexrc = compile_regex(re->overall_options, &code, &pptr, &errorcode, 0,
+ &firstcu, &firstcuflags, &reqcu, &reqcuflags, NULL, &cb, NULL);
+if (regexrc < 0) re->flags |= PCRE2_MATCH_EMPTY;
+re->top_bracket = cb.bracount;
+re->top_backref = cb.top_backref;
+re->max_lookbehind = cb.max_lookbehind;
+
+if (cb.had_accept)
+ {
+ reqcu = 0; /* Must disable after (*ACCEPT) */
+ reqcuflags = REQ_NONE;
+ re->flags |= PCRE2_HASACCEPT; /* Disables minimum length */
+ }
+
+/* Fill in the final opcode and check for disastrous overflow. If no overflow,
+but the estimated length exceeds the really used length, adjust the value of
+re->blocksize, and if valgrind support is configured, mark the extra allocated
+memory as unaddressable, so that any out-of-bound reads can be detected. */
+
+*code++ = OP_END;
+usedlength = code - codestart;
+if (usedlength > length) errorcode = ERR23; else
+ {
+ re->blocksize -= CU2BYTES(length - usedlength);
+#ifdef SUPPORT_VALGRIND
+ VALGRIND_MAKE_MEM_NOACCESS(code, CU2BYTES(length - usedlength));
+#endif
+ }
+
+/* Scan the pattern for recursion/subroutine calls and convert the group
+numbers into offsets. Maintain a small cache so that repeated groups containing
+recursions are efficiently handled. */
+
+#define RSCAN_CACHE_SIZE 8
+
+if (errorcode == 0 && cb.had_recurse)
+ {
+ PCRE2_UCHAR *rcode;
+ PCRE2_SPTR rgroup;
+ unsigned int ccount = 0;
+ int start = RSCAN_CACHE_SIZE;
+ recurse_cache rc[RSCAN_CACHE_SIZE];
+
+ for (rcode = (PCRE2_UCHAR *)find_recurse(codestart, utf);
+ rcode != NULL;
+ rcode = (PCRE2_UCHAR *)find_recurse(rcode + 1 + LINK_SIZE, utf))
+ {
+ int p, groupnumber;
+
+ groupnumber = (int)GET(rcode, 1);
+ if (groupnumber == 0) rgroup = codestart; else
+ {
+ PCRE2_SPTR search_from = codestart;
+ rgroup = NULL;
+ for (i = 0, p = start; i < ccount; i++, p = (p + 1) & 7)
+ {
+ if (groupnumber == rc[p].groupnumber)
+ {
+ rgroup = rc[p].group;
+ break;
+ }
+
+ /* Group n+1 must always start to the right of group n, so we can save
+ search time below when the new group number is greater than any of the
+ previously found groups. */
+
+ if (groupnumber > rc[p].groupnumber) search_from = rc[p].group;
+ }
+
+ if (rgroup == NULL)
+ {
+ rgroup = PRIV(find_bracket)(search_from, utf, groupnumber);
+ if (rgroup == NULL)
+ {
+ errorcode = ERR53;
+ break;
+ }
+ if (--start < 0) start = RSCAN_CACHE_SIZE - 1;
+ rc[start].groupnumber = groupnumber;
+ rc[start].group = rgroup;
+ if (ccount < RSCAN_CACHE_SIZE) ccount++;
+ }
+ }
+
+ PUT(rcode, 1, rgroup - codestart);
+ }
+ }
+
+/* In rare debugging situations we sometimes need to look at the compiled code
+at this stage. */
+
+#ifdef DEBUG_CALL_PRINTINT
+pcre2_printint(re, stderr, TRUE);
+fprintf(stderr, "Length=%lu Used=%lu\n", length, usedlength);
+#endif
+
+/* Unless disabled, check whether any single character iterators can be
+auto-possessified. The function overwrites the appropriate opcode values, so
+the type of the pointer must be cast. NOTE: the intermediate variable "temp" is
+used in this code because at least one compiler gives a warning about loss of
+"const" attribute if the cast (PCRE2_UCHAR *)codestart is used directly in the
+function call. */
+
+if (errorcode == 0 && (re->overall_options & PCRE2_NO_AUTO_POSSESS) == 0)
+ {
+ PCRE2_UCHAR *temp = (PCRE2_UCHAR *)codestart;
+ if (PRIV(auto_possessify)(temp, &cb) != 0) errorcode = ERR80;
+ }
+
+/* Failed to compile, or error while post-processing. */
+
+if (errorcode != 0) goto HAD_CB_ERROR;
+
+/* Successful compile. If the anchored option was not passed, set it if
+we can determine that the pattern is anchored by virtue of ^ characters or \A
+or anything else, such as starting with non-atomic .* when DOTALL is set and
+there are no occurrences of *PRUNE or *SKIP (though there is an option to
+disable this case). */
+
+if ((re->overall_options & PCRE2_ANCHORED) == 0 &&
+ is_anchored(codestart, 0, &cb, 0, FALSE))
+ re->overall_options |= PCRE2_ANCHORED;
+
+/* Set up the first code unit or startline flag, the required code unit, and
+then study the pattern. This code need not be obeyed if PCRE2_NO_START_OPTIMIZE
+is set, as the data it would create will not be used. Note that a first code
+unit (but not the startline flag) is useful for anchored patterns because it
+can still give a quick "no match" and also avoid searching for a last code
+unit. */
+
+if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ int minminlength = 0; /* For minimal minlength from first/required CU */
+
+ /* If we do not have a first code unit, see if there is one that is asserted
+ (these are not saved during the compile because they can cause conflicts with
+ actual literals that follow). */
+
+ if (firstcuflags >= REQ_NONE)
+ firstcu = find_firstassertedcu(codestart, &firstcuflags, 0);
+
+ /* Save the data for a first code unit. The existence of one means the
+ minimum length must be at least 1. */
+
+ if (firstcuflags < REQ_NONE)
+ {
+ re->first_codeunit = firstcu;
+ re->flags |= PCRE2_FIRSTSET;
+ minminlength++;
+
+ /* Handle caseless first code units. */
+
+ if ((firstcuflags & REQ_CASELESS) != 0)
+ {
+ if (firstcu < 128 || (!utf && !ucp && firstcu < 255))
+ {
+ if (cb.fcc[firstcu] != firstcu) re->flags |= PCRE2_FIRSTCASELESS;
+ }
+
+ /* The first code unit is > 128 in UTF or UCP mode, or > 255 otherwise.
+ In 8-bit UTF mode, codepoints in the range 128-255 are introductory code
+ points and cannot have another case, but if UCP is set they may do. */
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ else if (ucp && !utf && UCD_OTHERCASE(firstcu) != firstcu)
+ re->flags |= PCRE2_FIRSTCASELESS;
+#else
+ else if ((utf || ucp) && firstcu <= MAX_UTF_CODE_POINT &&
+ UCD_OTHERCASE(firstcu) != firstcu)
+ re->flags |= PCRE2_FIRSTCASELESS;
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+
+ /* When there is no first code unit, for non-anchored patterns, see if we can
+ set the PCRE2_STARTLINE flag. This is helpful for multiline matches when all
+ branches start with ^ and also when all branches start with non-atomic .* for
+ non-DOTALL matches when *PRUNE and SKIP are not present. (There is an option
+ that disables this case.) */
+
+ else if ((re->overall_options & PCRE2_ANCHORED) == 0 &&
+ is_startline(codestart, 0, &cb, 0, FALSE))
+ re->flags |= PCRE2_STARTLINE;
+
+ /* Handle the "required code unit", if one is set. In the UTF case we can
+ increment the minimum minimum length only if we are sure this really is a
+ different character and not a non-starting code unit of the first character,
+ because the minimum length count is in characters, not code units. */
+
+ if (reqcuflags < REQ_NONE)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 16
+ if ((re->overall_options & PCRE2_UTF) == 0 || /* Not UTF */
+ firstcuflags >= REQ_NONE || /* First not set */
+ (firstcu & 0xf800) != 0xd800 || /* First not surrogate */
+ (reqcu & 0xfc00) != 0xdc00) /* Req not low surrogate */
+#elif PCRE2_CODE_UNIT_WIDTH == 8
+ if ((re->overall_options & PCRE2_UTF) == 0 || /* Not UTF */
+ firstcuflags >= REQ_NONE || /* First not set */
+ (firstcu & 0x80) == 0 || /* First is ASCII */
+ (reqcu & 0x80) == 0) /* Req is ASCII */
+#endif
+ {
+ minminlength++;
+ }
+
+ /* In the case of an anchored pattern, set up the value only if it follows
+ a variable length item in the pattern. */
+
+ if ((re->overall_options & PCRE2_ANCHORED) == 0 ||
+ (reqcuflags & REQ_VARY) != 0)
+ {
+ re->last_codeunit = reqcu;
+ re->flags |= PCRE2_LASTSET;
+
+ /* Handle caseless required code units as for first code units (above). */
+
+ if ((reqcuflags & REQ_CASELESS) != 0)
+ {
+ if (reqcu < 128 || (!utf && !ucp && reqcu < 255))
+ {
+ if (cb.fcc[reqcu] != reqcu) re->flags |= PCRE2_LASTCASELESS;
+ }
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ else if (ucp && !utf && UCD_OTHERCASE(reqcu) != reqcu)
+ re->flags |= PCRE2_LASTCASELESS;
+#else
+ else if ((utf || ucp) && reqcu <= MAX_UTF_CODE_POINT &&
+ UCD_OTHERCASE(reqcu) != reqcu)
+ re->flags |= PCRE2_LASTCASELESS;
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+ }
+
+ /* Study the compiled pattern to set up information such as a bitmap of
+ starting code units and a minimum matching length. */
+
+ if (PRIV(study)(re) != 0)
+ {
+ errorcode = ERR31;
+ goto HAD_CB_ERROR;
+ }
+
+ /* If study() set a bitmap of starting code units, it implies a minimum
+ length of at least one. */
+
+ if ((re->flags & PCRE2_FIRSTMAPSET) != 0 && minminlength == 0)
+ minminlength = 1;
+
+ /* If the minimum length set (or not set) by study() is less than the minimum
+ implied by required code units, override it. */
+
+ if (re->minlength < minminlength) re->minlength = minminlength;
+ } /* End of start-of-match optimizations. */
+
+/* Control ends up here in all cases. When running under valgrind, make a
+pattern's terminating zero defined again. If memory was obtained for the parsed
+version of the pattern, free it before returning. Also free the list of named
+groups if a larger one had to be obtained, and likewise the group information
+vector. */
+
+EXIT:
+#ifdef SUPPORT_VALGRIND
+if (zero_terminated) VALGRIND_MAKE_MEM_DEFINED(pattern + patlen, CU2BYTES(1));
+#endif
+if (cb.parsed_pattern != stack_parsed_pattern)
+ ccontext->memctl.free(cb.parsed_pattern, ccontext->memctl.memory_data);
+if (cb.named_group_list_size > NAMED_GROUP_LIST_SIZE)
+ ccontext->memctl.free((void *)cb.named_groups, ccontext->memctl.memory_data);
+if (cb.groupinfo != stack_groupinfo)
+ ccontext->memctl.free((void *)cb.groupinfo, ccontext->memctl.memory_data);
+return re; /* Will be NULL after an error */
+
+/* Errors discovered in parse_regex() set the offset value in the compile
+block. Errors discovered before it is called must compute it from the ptr
+value. After parse_regex() is called, the offset in the compile block is set to
+the end of the pattern, but certain errors in compile_regex() may reset it if
+an offset is available in the parsed pattern. */
+
+HAD_CB_ERROR:
+ptr = pattern + cb.erroroffset;
+
+HAD_EARLY_ERROR:
+*erroroffset = ptr - pattern;
+
+HAD_ERROR:
+*errorptr = errorcode;
+pcre2_code_free(re);
+re = NULL;
+goto EXIT;
+}
+
+/* These #undefs are here to enable unity builds with CMake. */
+
+#undef NLBLOCK /* Block containing newline information */
+#undef PSSTART /* Field containing processed string start */
+#undef PSEND /* Field containing processed string end */
+
+/* End of pcre2_compile.c */
diff --git a/contrib/libs/pcre2/src/pcre2_config.c b/contrib/libs/pcre2/src/pcre2_config.c
new file mode 100644
index 0000000000..04a011a29c
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_config.c
@@ -0,0 +1,252 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2020 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+/* Save the configured link size, which is in bytes. In 16-bit and 32-bit modes
+its value gets changed by pcre2_intmodedep.h (included by pcre2_internal.h) to
+be in code units. */
+
+static int configured_link_size = LINK_SIZE;
+
+#include "pcre2_internal.h"
+
+/* These macros are the standard way of turning unquoted text into C strings.
+They allow macros like PCRE2_MAJOR to be defined without quotes, which is
+convenient for user programs that want to test their values. */
+
+#define STRING(a) # a
+#define XSTRING(s) STRING(s)
+
+
+/*************************************************
+* Return info about what features are configured *
+*************************************************/
+
+/* If where is NULL, the length of memory required is returned.
+
+Arguments:
+ what what information is required
+ where where to put the information
+
+Returns: 0 if a numerical value is returned
+ >= 0 if a string value
+ PCRE2_ERROR_BADOPTION if "where" not recognized
+ or JIT target requested when JIT not enabled
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_config(uint32_t what, void *where)
+{
+if (where == NULL) /* Requests a length */
+ {
+ switch(what)
+ {
+ default:
+ return PCRE2_ERROR_BADOPTION;
+
+ case PCRE2_CONFIG_BSR:
+ case PCRE2_CONFIG_COMPILED_WIDTHS:
+ case PCRE2_CONFIG_DEPTHLIMIT:
+ case PCRE2_CONFIG_HEAPLIMIT:
+ case PCRE2_CONFIG_JIT:
+ case PCRE2_CONFIG_LINKSIZE:
+ case PCRE2_CONFIG_MATCHLIMIT:
+ case PCRE2_CONFIG_NEVER_BACKSLASH_C:
+ case PCRE2_CONFIG_NEWLINE:
+ case PCRE2_CONFIG_PARENSLIMIT:
+ case PCRE2_CONFIG_STACKRECURSE: /* Obsolete */
+ case PCRE2_CONFIG_TABLES_LENGTH:
+ case PCRE2_CONFIG_UNICODE:
+ return sizeof(uint32_t);
+
+ /* These are handled below */
+
+ case PCRE2_CONFIG_JITTARGET:
+ case PCRE2_CONFIG_UNICODE_VERSION:
+ case PCRE2_CONFIG_VERSION:
+ break;
+ }
+ }
+
+switch (what)
+ {
+ default:
+ return PCRE2_ERROR_BADOPTION;
+
+ case PCRE2_CONFIG_BSR:
+#ifdef BSR_ANYCRLF
+ *((uint32_t *)where) = PCRE2_BSR_ANYCRLF;
+#else
+ *((uint32_t *)where) = PCRE2_BSR_UNICODE;
+#endif
+ break;
+
+ case PCRE2_CONFIG_COMPILED_WIDTHS:
+ *((uint32_t *)where) = 0
+#ifdef SUPPORT_PCRE2_8
+ + 1
+#endif
+#ifdef SUPPORT_PCRE2_16
+ + 2
+#endif
+#ifdef SUPPORT_PCRE2_32
+ + 4
+#endif
+ ;
+ break;
+
+ case PCRE2_CONFIG_DEPTHLIMIT:
+ *((uint32_t *)where) = MATCH_LIMIT_DEPTH;
+ break;
+
+ case PCRE2_CONFIG_HEAPLIMIT:
+ *((uint32_t *)where) = HEAP_LIMIT;
+ break;
+
+ case PCRE2_CONFIG_JIT:
+#ifdef SUPPORT_JIT
+ *((uint32_t *)where) = 1;
+#else
+ *((uint32_t *)where) = 0;
+#endif
+ break;
+
+ case PCRE2_CONFIG_JITTARGET:
+#ifdef SUPPORT_JIT
+ {
+ const char *v = PRIV(jit_get_target)();
+ return (int)(1 + ((where == NULL)?
+ strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));
+ }
+#else
+ return PCRE2_ERROR_BADOPTION;
+#endif
+
+ case PCRE2_CONFIG_LINKSIZE:
+ *((uint32_t *)where) = (uint32_t)configured_link_size;
+ break;
+
+ case PCRE2_CONFIG_MATCHLIMIT:
+ *((uint32_t *)where) = MATCH_LIMIT;
+ break;
+
+ case PCRE2_CONFIG_NEWLINE:
+ *((uint32_t *)where) = NEWLINE_DEFAULT;
+ break;
+
+ case PCRE2_CONFIG_NEVER_BACKSLASH_C:
+#ifdef NEVER_BACKSLASH_C
+ *((uint32_t *)where) = 1;
+#else
+ *((uint32_t *)where) = 0;
+#endif
+ break;
+
+ case PCRE2_CONFIG_PARENSLIMIT:
+ *((uint32_t *)where) = PARENS_NEST_LIMIT;
+ break;
+
+ /* This is now obsolete. The stack is no longer used via recursion for
+ handling backtracking in pcre2_match(). */
+
+ case PCRE2_CONFIG_STACKRECURSE:
+ *((uint32_t *)where) = 0;
+ break;
+
+ case PCRE2_CONFIG_TABLES_LENGTH:
+ *((uint32_t *)where) = TABLES_LENGTH;
+ break;
+
+ case PCRE2_CONFIG_UNICODE_VERSION:
+ {
+#if defined SUPPORT_UNICODE
+ const char *v = PRIV(unicode_version);
+#else
+ const char *v = "Unicode not supported";
+#endif
+ return (int)(1 + ((where == NULL)?
+ strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));
+ }
+ break;
+
+ case PCRE2_CONFIG_UNICODE:
+#if defined SUPPORT_UNICODE
+ *((uint32_t *)where) = 1;
+#else
+ *((uint32_t *)where) = 0;
+#endif
+ break;
+
+ /* The hackery in setting "v" below is to cope with the case when
+ PCRE2_PRERELEASE is set to an empty string (which it is for real releases).
+ If the second alternative is used in this case, it does not leave a space
+ before the date. On the other hand, if all four macros are put into a single
+ XSTRING when PCRE2_PRERELEASE is not empty, an unwanted space is inserted.
+ There are problems using an "obvious" approach like this:
+
+ XSTRING(PCRE2_MAJOR) "." XSTRING(PCRE_MINOR)
+ XSTRING(PCRE2_PRERELEASE) " " XSTRING(PCRE_DATE)
+
+ because, when PCRE2_PRERELEASE is empty, this leads to an attempted expansion
+ of STRING(). The C standard states: "If (before argument substitution) any
+ argument consists of no preprocessing tokens, the behavior is undefined." It
+ turns out the gcc treats this case as a single empty string - which is what
+ we really want - but Visual C grumbles about the lack of an argument for the
+ macro. Unfortunately, both are within their rights. As there seems to be no
+ way to test for a macro's value being empty at compile time, we have to
+ resort to a runtime test. */
+
+ case PCRE2_CONFIG_VERSION:
+ {
+ const char *v = (XSTRING(Z PCRE2_PRERELEASE)[1] == 0)?
+ XSTRING(PCRE2_MAJOR.PCRE2_MINOR PCRE2_DATE) :
+ XSTRING(PCRE2_MAJOR.PCRE2_MINOR) XSTRING(PCRE2_PRERELEASE PCRE2_DATE);
+ return (int)(1 + ((where == NULL)?
+ strlen(v) : PRIV(strcpy_c8)((PCRE2_UCHAR *)where, v)));
+ }
+ }
+
+return 0;
+}
+
+/* End of pcre2_config.c */
diff --git a/contrib/libs/pcre2/src/pcre2_config.h b/contrib/libs/pcre2/src/pcre2_config.h
new file mode 100644
index 0000000000..14e49e08ce
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_config.h
@@ -0,0 +1,446 @@
+/* src/config.h. Generated from config.h.in by configure. */
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+
+/* PCRE2 is written in Standard C, but there are a few non-standard things it
+can cope with, allowing it to run on SunOS4 and other "close to standard"
+systems.
+
+In environments that support the GNU autotools, config.h.in is converted into
+config.h by the "configure" script. In environments that use CMake,
+config-cmake.in is converted into config.h. If you are going to build PCRE2 "by
+hand" without using "configure" or CMake, you should copy the distributed
+config.h.generic to config.h, and edit the macro definitions to be the way you
+need them. You must then add -DHAVE_CONFIG_H to all of your compile commands,
+so that config.h is included at the start of every source.
+
+Alternatively, you can avoid editing by using -D on the compiler command line
+to set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H,
+but if you do, default values will be taken from config.h for non-boolean
+macros that are not defined on the command line.
+
+Boolean macros such as HAVE_STDLIB_H and SUPPORT_PCRE2_8 should either be
+defined (conventionally to 1) for TRUE, and not defined at all for FALSE. All
+such macros are listed as a commented #undef in config.h.generic. Macros such
+as MATCH_LIMIT, whose actual value is relevant, have defaults defined, but are
+surrounded by #ifndef/#endif lines so that the value can be overridden by -D.
+
+PCRE2 uses memmove() if HAVE_MEMMOVE is defined; otherwise it uses bcopy() if
+HAVE_BCOPY is defined. If your system has neither bcopy() nor memmove(), make
+sure both macros are undefined; an emulation function will then be used. */
+
+/* By default, the \R escape sequence matches any Unicode line ending
+ character or sequence of characters. If BSR_ANYCRLF is defined (to any
+ value), this is changed so that backslash-R matches only CR, LF, or CRLF.
+ The build-time default can be overridden by the user of PCRE2 at runtime.
+ */
+/* #undef BSR_ANYCRLF */
+
+/* Define to any value to disable the use of the z and t modifiers in
+ formatting settings such as %zu or %td (this is rarely needed). */
+/* #undef DISABLE_PERCENT_ZT */
+
+/* If you are compiling for a system that uses EBCDIC instead of ASCII
+ character codes, define this macro to any value. When EBCDIC is set, PCRE2
+ assumes that all input strings are in EBCDIC. If you do not define this
+ macro, PCRE2 will assume input strings are ASCII or UTF-8/16/32 Unicode. It
+ is not possible to build a version of PCRE2 that supports both EBCDIC and
+ UTF-8/16/32. */
+/* #undef EBCDIC */
+
+/* In an EBCDIC environment, define this macro to any value to arrange for the
+ NL character to be 0x25 instead of the default 0x15. NL plays the role that
+ LF does in an ASCII/Unicode environment. */
+/* #undef EBCDIC_NL25 */
+
+/* Define this if your compiler supports __attribute__((uninitialized)) */
+/* #undef HAVE_ATTRIBUTE_UNINITIALIZED */
+
+/* Define to 1 if you have the `bcopy' function. */
+#define HAVE_BCOPY 1
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+/* #undef HAVE_BZLIB_H */
+
+/* Define to 1 if you have the <dirent.h> header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <editline/readline.h> header file. */
+/* #undef HAVE_EDITLINE_READLINE_H */
+
+/* Define to 1 if you have the <edit/readline/readline.h> header file. */
+/* #undef HAVE_EDIT_READLINE_READLINE_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the `memfd_create' function. */
+#define HAVE_MEMFD_CREATE 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <minix/config.h> header file. */
+/* #undef HAVE_MINIX_CONFIG_H */
+
+/* Define to 1 if you have the `mkostemp' function. */
+#define HAVE_MKOSTEMP 1
+
+/* Define if you have POSIX threads libraries and header files. */
+#define HAVE_PTHREAD 1
+
+/* Have PTHREAD_PRIO_INHERIT. */
+#define HAVE_PTHREAD_PRIO_INHERIT 1
+
+/* Define to 1 if you have the <readline.h> header file. */
+/* #undef HAVE_READLINE_H */
+
+/* Define to 1 if you have the <readline/history.h> header file. */
+/* #undef HAVE_READLINE_HISTORY_H */
+
+/* Define to 1 if you have the <readline/readline.h> header file. */
+/* #undef HAVE_READLINE_READLINE_H */
+
+/* Define to 1 if you have the `realpath' function. */
+#define HAVE_REALPATH 1
+
+/* Define to 1 if you have the `secure_getenv' function. */
+#define HAVE_SECURE_GETENV 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if the compiler supports simple visibility declarations. */
+#define HAVE_VISIBILITY 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <zlib.h> header file. */
+/* #undef HAVE_ZLIB_H */
+
+/* This limits the amount of memory that may be used while matching a pattern.
+ It applies to both pcre2_match() and pcre2_dfa_match(). It does not apply
+ to JIT matching. The value is in kibibytes (units of 1024 bytes). */
+#define HEAP_LIMIT 20000000
+
+/* The value of LINK_SIZE determines the number of bytes used to store links
+ as offsets within the compiled regex. The default is 2, which allows for
+ compiled patterns up to 65535 code units long. This covers the vast
+ majority of cases. However, PCRE2 can also be compiled to use 3 or 4 bytes
+ instead. This allows for longer patterns in extreme cases. */
+#define LINK_SIZE 2
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* The value of MATCH_LIMIT determines the default number of times the
+ pcre2_match() function can record a backtrack position during a single
+ matching attempt. The value is also used to limit a loop counter in
+ pcre2_dfa_match(). There is a runtime interface for setting a different
+ limit. The limit exists in order to catch runaway regular expressions that
+ take for ever to determine that they do not match. The default is set very
+ large so that it does not accidentally catch legitimate cases. */
+#define MATCH_LIMIT 10000000
+
+/* The above limit applies to all backtracks, whether or not they are nested.
+ In some environments it is desirable to limit the nesting of backtracking
+ (that is, the depth of tree that is searched) more strictly, in order to
+ restrict the maximum amount of heap memory that is used. The value of
+ MATCH_LIMIT_DEPTH provides this facility. To have any useful effect, it
+ must be less than the value of MATCH_LIMIT. The default is to use the same
+ value as MATCH_LIMIT. There is a runtime method for setting a different
+ limit. In the case of pcre2_dfa_match(), this limit controls the depth of
+ the internal nested function calls that are used for pattern recursions,
+ lookarounds, and atomic groups. */
+#define MATCH_LIMIT_DEPTH MATCH_LIMIT
+
+/* This limit is parameterized just in case anybody ever wants to change it.
+ Care must be taken if it is increased, because it guards against integer
+ overflow caused by enormously large patterns. */
+#define MAX_NAME_COUNT 10000
+
+/* This limit is parameterized just in case anybody ever wants to change it.
+ Care must be taken if it is increased, because it guards against integer
+ overflow caused by enormously large patterns. */
+#define MAX_NAME_SIZE 32
+
+/* Defining NEVER_BACKSLASH_C locks out the use of \C in all patterns. */
+/* #undef NEVER_BACKSLASH_C */
+
+/* The value of NEWLINE_DEFAULT determines the default newline character
+ sequence. PCRE2 client programs can override this by selecting other values
+ at run time. The valid values are 1 (CR), 2 (LF), 3 (CRLF), 4 (ANY), 5
+ (ANYCRLF), and 6 (NUL). */
+#define NEWLINE_DEFAULT 2
+
+/* Name of package */
+#define PACKAGE "pcre2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "PCRE2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "PCRE2 10.42"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "pcre2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "10.42"
+
+/* The value of PARENS_NEST_LIMIT specifies the maximum depth of nested
+ parentheses (of any kind) in a pattern. This limits the amount of system
+ stack that is used while compiling a pattern. */
+#define PARENS_NEST_LIMIT 250
+
+/* The value of PCRE2GREP_BUFSIZE is the starting size of the buffer used by
+ pcre2grep to hold parts of the file it is searching. The buffer will be
+ expanded up to PCRE2GREP_MAX_BUFSIZE if necessary, for files containing
+ very long lines. The actual amount of memory used by pcre2grep is three
+ times this number, because it allows for the buffering of "before" and
+ "after" lines. */
+#define PCRE2GREP_BUFSIZE 20480
+
+/* The value of PCRE2GREP_MAX_BUFSIZE specifies the maximum size of the buffer
+ used by pcre2grep to hold parts of the file it is searching. The actual
+ amount of memory used by pcre2grep is three times this number, because it
+ allows for the buffering of "before" and "after" lines. */
+#define PCRE2GREP_MAX_BUFSIZE 1048576
+
+/* to make a symbol visible */
+#define PCRE2POSIX_EXP_DECL extern __attribute__ ((visibility ("default")))
+
+/* to make a symbol visible */
+#define PCRE2POSIX_EXP_DEFN extern __attribute__ ((visibility ("default")))
+
+/* Define to any value to include debugging code. */
+/* #undef PCRE2_DEBUG */
+
+/* to make a symbol visible */
+#define PCRE2_EXP_DECL extern __attribute__ ((visibility ("default")))
+
+
+/* If you are compiling for a system other than a Unix-like system or
+ Win32, and it needs some magic to be inserted before the definition
+ of a function that is exported by the library, define this macro to
+ contain the relevant magic. If you do not define this macro, a suitable
+ __declspec value is used for Windows systems; in other environments
+ "extern" is used for a C compiler and "extern C" for a C++ compiler.
+ This macro apears at the start of every exported function that is part
+ of the external API. It does not appear on functions that are "external"
+ in the C sense, but which are internal to the library. */
+#define PCRE2_EXP_DEFN __attribute__ ((visibility ("default")))
+
+/* Define to any value if linking statically (TODO: make nice with Libtool) */
+/* #undef PCRE2_STATIC */
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+/* #undef PTHREAD_CREATE_JOINABLE */
+
+/* Define to any non-zero number to enable support for SELinux compatible
+ executable memory allocator in JIT. Note that this will have no effect
+ unless SUPPORT_JIT is also defined. */
+/* #undef SLJIT_PROT_EXECUTABLE_ALLOCATOR */
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Define to any value to enable support for Just-In-Time compiling. */
+#define SUPPORT_JIT /**/
+
+/* Define to any value to allow pcre2grep to be linked with libbz2, so that it
+ is able to handle .bz2 files. */
+/* #undef SUPPORT_LIBBZ2 */
+
+/* Define to any value to allow pcre2test to be linked with libedit. */
+/* #undef SUPPORT_LIBEDIT */
+
+/* Define to any value to allow pcre2test to be linked with libreadline. */
+/* #undef SUPPORT_LIBREADLINE */
+
+/* Define to any value to allow pcre2grep to be linked with libz, so that it
+ is able to handle .gz files. */
+/* #undef SUPPORT_LIBZ */
+
+/* Define to any value to enable callout script support in pcre2grep. */
+#define SUPPORT_PCRE2GREP_CALLOUT /**/
+
+/* Define to any value to enable fork support in pcre2grep callout scripts.
+ This will have no effect unless SUPPORT_PCRE2GREP_CALLOUT is also defined.
+ */
+#define SUPPORT_PCRE2GREP_CALLOUT_FORK /**/
+
+/* Define to any value to enable JIT support in pcre2grep. Note that this will
+ have no effect unless SUPPORT_JIT is also defined. */
+#define SUPPORT_PCRE2GREP_JIT /**/
+
+/* Define to any value to enable the 16 bit PCRE2 library. */
+#define SUPPORT_PCRE2_16 /**/
+
+/* Define to any value to enable the 32 bit PCRE2 library. */
+#define SUPPORT_PCRE2_32 /**/
+
+/* Define to any value to enable the 8 bit PCRE2 library. */
+#define SUPPORT_PCRE2_8 /**/
+
+/* Define to any value to enable support for Unicode and UTF encoding. This
+ will work even in an EBCDIC environment, but it is incompatible with the
+ EBCDIC macro. That is, PCRE2 can support *either* EBCDIC code *or*
+ ASCII/Unicode, but not both at once. */
+#define SUPPORT_UNICODE /**/
+
+/* Define to any value for valgrind support to find invalid memory reads. */
+/* #undef SUPPORT_VALGRIND */
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable general extensions on macOS. */
+#ifndef _DARWIN_C_SOURCE
+# define _DARWIN_C_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable X/Open compliant socket functions that do not require linking
+ with -lxnet on HP-UX 11.11. */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# define _HPUX_ALT_XOPEN_SOCKET_API 1
+#endif
+/* Identify the host operating system as Minix.
+ This macro does not affect the system headers' behavior.
+ A future release of Autoconf may stop defining this macro. */
+#ifndef _MINIX
+/* # undef _MINIX */
+#endif
+/* Enable general extensions on NetBSD.
+ Enable NetBSD compatibility extensions on Minix. */
+#ifndef _NETBSD_SOURCE
+# define _NETBSD_SOURCE 1
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+ Oddly enough, this does nothing on OpenBSD. */
+#ifndef _OPENBSD_SOURCE
+# define _OPENBSD_SOURCE 1
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_SOURCE
+/* # undef _POSIX_SOURCE */
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_1_SOURCE
+/* # undef _POSIX_1_SOURCE */
+#endif
+/* Enable POSIX-compatible threading on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# define __STDC_WANT_IEC_60559_BFP_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# define __STDC_WANT_IEC_60559_DFP_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# define __STDC_WANT_IEC_60559_TYPES_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
+#ifndef __STDC_WANT_LIB_EXT2__
+# define __STDC_WANT_LIB_EXT2__ 1
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009. */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# define __STDC_WANT_MATH_SPEC_FUNCS__ 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable X/Open extensions. Define to 500 only if necessary
+ to make mbstate_t available. */
+#ifndef _XOPEN_SOURCE
+/* # undef _XOPEN_SOURCE */
+#endif
+
+
+/* Version number of package */
+#define VERSION "10.42"
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+/* #undef int64_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
diff --git a/contrib/libs/pcre2/src/pcre2_context.c b/contrib/libs/pcre2/src/pcre2_context.c
new file mode 100644
index 0000000000..013cf6e9ed
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_context.c
@@ -0,0 +1,494 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+
+/*************************************************
+* Default malloc/free functions *
+*************************************************/
+
+/* Ignore the "user data" argument in each case. */
+
+static void *default_malloc(size_t size, void *data)
+{
+(void)data;
+return malloc(size);
+}
+
+
+static void default_free(void *block, void *data)
+{
+(void)data;
+free(block);
+}
+
+
+
+/*************************************************
+* Get a block and save memory control *
+*************************************************/
+
+/* This internal function is called to get a block of memory in which the
+memory control data is to be stored at the start for future use.
+
+Arguments:
+ size amount of memory required
+ memctl pointer to a memctl block or NULL
+
+Returns: pointer to memory or NULL on failure
+*/
+
+extern void *
+PRIV(memctl_malloc)(size_t size, pcre2_memctl *memctl)
+{
+pcre2_memctl *newmemctl;
+void *yield = (memctl == NULL)? malloc(size) :
+ memctl->malloc(size, memctl->memory_data);
+if (yield == NULL) return NULL;
+newmemctl = (pcre2_memctl *)yield;
+if (memctl == NULL)
+ {
+ newmemctl->malloc = default_malloc;
+ newmemctl->free = default_free;
+ newmemctl->memory_data = NULL;
+ }
+else *newmemctl = *memctl;
+return yield;
+}
+
+
+
+/*************************************************
+* Create and initialize contexts *
+*************************************************/
+
+/* Initializing for compile and match contexts is done in separate, private
+functions so that these can be called from functions such as pcre2_compile()
+when an external context is not supplied. The initializing functions have an
+option to set up default memory management. */
+
+PCRE2_EXP_DEFN pcre2_general_context * PCRE2_CALL_CONVENTION
+pcre2_general_context_create(void *(*private_malloc)(size_t, void *),
+ void (*private_free)(void *, void *), void *memory_data)
+{
+pcre2_general_context *gcontext;
+if (private_malloc == NULL) private_malloc = default_malloc;
+if (private_free == NULL) private_free = default_free;
+gcontext = private_malloc(sizeof(pcre2_real_general_context), memory_data);
+if (gcontext == NULL) return NULL;
+gcontext->memctl.malloc = private_malloc;
+gcontext->memctl.free = private_free;
+gcontext->memctl.memory_data = memory_data;
+return gcontext;
+}
+
+
+/* A default compile context is set up to save having to initialize at run time
+when no context is supplied to the compile function. */
+
+const pcre2_compile_context PRIV(default_compile_context) = {
+ { default_malloc, default_free, NULL }, /* Default memory handling */
+ NULL, /* Stack guard */
+ NULL, /* Stack guard data */
+ PRIV(default_tables), /* Character tables */
+ PCRE2_UNSET, /* Max pattern length */
+ BSR_DEFAULT, /* Backslash R default */
+ NEWLINE_DEFAULT, /* Newline convention */
+ PARENS_NEST_LIMIT, /* As it says */
+ 0 }; /* Extra options */
+
+/* The create function copies the default into the new memory, but must
+override the default memory handling functions if a gcontext was provided. */
+
+PCRE2_EXP_DEFN pcre2_compile_context * PCRE2_CALL_CONVENTION
+pcre2_compile_context_create(pcre2_general_context *gcontext)
+{
+pcre2_compile_context *ccontext = PRIV(memctl_malloc)(
+ sizeof(pcre2_real_compile_context), (pcre2_memctl *)gcontext);
+if (ccontext == NULL) return NULL;
+*ccontext = PRIV(default_compile_context);
+if (gcontext != NULL)
+ *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext);
+return ccontext;
+}
+
+
+/* A default match context is set up to save having to initialize at run time
+when no context is supplied to a match function. */
+
+const pcre2_match_context PRIV(default_match_context) = {
+ { default_malloc, default_free, NULL },
+#ifdef SUPPORT_JIT
+ NULL, /* JIT callback */
+ NULL, /* JIT callback data */
+#endif
+ NULL, /* Callout function */
+ NULL, /* Callout data */
+ NULL, /* Substitute callout function */
+ NULL, /* Substitute callout data */
+ PCRE2_UNSET, /* Offset limit */
+ HEAP_LIMIT,
+ MATCH_LIMIT,
+ MATCH_LIMIT_DEPTH };
+
+/* The create function copies the default into the new memory, but must
+override the default memory handling functions if a gcontext was provided. */
+
+PCRE2_EXP_DEFN pcre2_match_context * PCRE2_CALL_CONVENTION
+pcre2_match_context_create(pcre2_general_context *gcontext)
+{
+pcre2_match_context *mcontext = PRIV(memctl_malloc)(
+ sizeof(pcre2_real_match_context), (pcre2_memctl *)gcontext);
+if (mcontext == NULL) return NULL;
+*mcontext = PRIV(default_match_context);
+if (gcontext != NULL)
+ *((pcre2_memctl *)mcontext) = *((pcre2_memctl *)gcontext);
+return mcontext;
+}
+
+
+/* A default convert context is set up to save having to initialize at run time
+when no context is supplied to the convert function. */
+
+const pcre2_convert_context PRIV(default_convert_context) = {
+ { default_malloc, default_free, NULL }, /* Default memory handling */
+#ifdef _WIN32
+ CHAR_BACKSLASH, /* Default path separator */
+ CHAR_GRAVE_ACCENT /* Default escape character */
+#else /* Not Windows */
+ CHAR_SLASH, /* Default path separator */
+ CHAR_BACKSLASH /* Default escape character */
+#endif
+ };
+
+/* The create function copies the default into the new memory, but must
+override the default memory handling functions if a gcontext was provided. */
+
+PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION
+pcre2_convert_context_create(pcre2_general_context *gcontext)
+{
+pcre2_convert_context *ccontext = PRIV(memctl_malloc)(
+ sizeof(pcre2_real_convert_context), (pcre2_memctl *)gcontext);
+if (ccontext == NULL) return NULL;
+*ccontext = PRIV(default_convert_context);
+if (gcontext != NULL)
+ *((pcre2_memctl *)ccontext) = *((pcre2_memctl *)gcontext);
+return ccontext;
+}
+
+
+/*************************************************
+* Context copy functions *
+*************************************************/
+
+PCRE2_EXP_DEFN pcre2_general_context * PCRE2_CALL_CONVENTION
+pcre2_general_context_copy(pcre2_general_context *gcontext)
+{
+pcre2_general_context *new =
+ gcontext->memctl.malloc(sizeof(pcre2_real_general_context),
+ gcontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, gcontext, sizeof(pcre2_real_general_context));
+return new;
+}
+
+
+PCRE2_EXP_DEFN pcre2_compile_context * PCRE2_CALL_CONVENTION
+pcre2_compile_context_copy(pcre2_compile_context *ccontext)
+{
+pcre2_compile_context *new =
+ ccontext->memctl.malloc(sizeof(pcre2_real_compile_context),
+ ccontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, ccontext, sizeof(pcre2_real_compile_context));
+return new;
+}
+
+
+PCRE2_EXP_DEFN pcre2_match_context * PCRE2_CALL_CONVENTION
+pcre2_match_context_copy(pcre2_match_context *mcontext)
+{
+pcre2_match_context *new =
+ mcontext->memctl.malloc(sizeof(pcre2_real_match_context),
+ mcontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, mcontext, sizeof(pcre2_real_match_context));
+return new;
+}
+
+
+
+PCRE2_EXP_DEFN pcre2_convert_context * PCRE2_CALL_CONVENTION
+pcre2_convert_context_copy(pcre2_convert_context *ccontext)
+{
+pcre2_convert_context *new =
+ ccontext->memctl.malloc(sizeof(pcre2_real_convert_context),
+ ccontext->memctl.memory_data);
+if (new == NULL) return NULL;
+memcpy(new, ccontext, sizeof(pcre2_real_convert_context));
+return new;
+}
+
+
+/*************************************************
+* Context free functions *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_general_context_free(pcre2_general_context *gcontext)
+{
+if (gcontext != NULL)
+ gcontext->memctl.free(gcontext, gcontext->memctl.memory_data);
+}
+
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_compile_context_free(pcre2_compile_context *ccontext)
+{
+if (ccontext != NULL)
+ ccontext->memctl.free(ccontext, ccontext->memctl.memory_data);
+}
+
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_match_context_free(pcre2_match_context *mcontext)
+{
+if (mcontext != NULL)
+ mcontext->memctl.free(mcontext, mcontext->memctl.memory_data);
+}
+
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_convert_context_free(pcre2_convert_context *ccontext)
+{
+if (ccontext != NULL)
+ ccontext->memctl.free(ccontext, ccontext->memctl.memory_data);
+}
+
+
+/*************************************************
+* Set values in contexts *
+*************************************************/
+
+/* All these functions return 0 for success or PCRE2_ERROR_BADDATA if invalid
+data is given. Only some of the functions are able to test the validity of the
+data. */
+
+
+/* ------------ Compile context ------------ */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_character_tables(pcre2_compile_context *ccontext,
+ const uint8_t *tables)
+{
+ccontext->tables = tables;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_bsr(pcre2_compile_context *ccontext, uint32_t value)
+{
+switch(value)
+ {
+ case PCRE2_BSR_ANYCRLF:
+ case PCRE2_BSR_UNICODE:
+ ccontext->bsr_convention = value;
+ return 0;
+
+ default:
+ return PCRE2_ERROR_BADDATA;
+ }
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_max_pattern_length(pcre2_compile_context *ccontext, PCRE2_SIZE length)
+{
+ccontext->max_pattern_length = length;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_newline(pcre2_compile_context *ccontext, uint32_t newline)
+{
+switch(newline)
+ {
+ case PCRE2_NEWLINE_CR:
+ case PCRE2_NEWLINE_LF:
+ case PCRE2_NEWLINE_CRLF:
+ case PCRE2_NEWLINE_ANY:
+ case PCRE2_NEWLINE_ANYCRLF:
+ case PCRE2_NEWLINE_NUL:
+ ccontext->newline_convention = newline;
+ return 0;
+
+ default:
+ return PCRE2_ERROR_BADDATA;
+ }
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_parens_nest_limit(pcre2_compile_context *ccontext, uint32_t limit)
+{
+ccontext->parens_nest_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_compile_extra_options(pcre2_compile_context *ccontext, uint32_t options)
+{
+ccontext->extra_options = options;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_compile_recursion_guard(pcre2_compile_context *ccontext,
+ int (*guard)(uint32_t, void *), void *user_data)
+{
+ccontext->stack_guard = guard;
+ccontext->stack_guard_data = user_data;
+return 0;
+}
+
+
+/* ------------ Match context ------------ */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_callout(pcre2_match_context *mcontext,
+ int (*callout)(pcre2_callout_block *, void *), void *callout_data)
+{
+mcontext->callout = callout;
+mcontext->callout_data = callout_data;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_substitute_callout(pcre2_match_context *mcontext,
+ int (*substitute_callout)(pcre2_substitute_callout_block *, void *),
+ void *substitute_callout_data)
+{
+mcontext->substitute_callout = substitute_callout;
+mcontext->substitute_callout_data = substitute_callout_data;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_heap_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+mcontext->heap_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_match_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+mcontext->match_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_depth_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+mcontext->depth_limit = limit;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_offset_limit(pcre2_match_context *mcontext, PCRE2_SIZE limit)
+{
+mcontext->offset_limit = limit;
+return 0;
+}
+
+/* These functions became obsolete at release 10.30. The first is kept as a
+synonym for backwards compatibility. The second now does nothing. Exclude both
+from coverage reports. */
+
+/* LCOV_EXCL_START */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_recursion_limit(pcre2_match_context *mcontext, uint32_t limit)
+{
+return pcre2_set_depth_limit(mcontext, limit);
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_recursion_memory_management(pcre2_match_context *mcontext,
+ void *(*mymalloc)(size_t, void *), void (*myfree)(void *, void *),
+ void *mydata)
+{
+(void)mcontext;
+(void)mymalloc;
+(void)myfree;
+(void)mydata;
+return 0;
+}
+
+/* LCOV_EXCL_STOP */
+
+
+/* ------------ Convert context ------------ */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_glob_separator(pcre2_convert_context *ccontext, uint32_t separator)
+{
+if (separator != CHAR_SLASH && separator != CHAR_BACKSLASH &&
+ separator != CHAR_DOT) return PCRE2_ERROR_BADDATA;
+ccontext->glob_separator = separator;
+return 0;
+}
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_set_glob_escape(pcre2_convert_context *ccontext, uint32_t escape)
+{
+if (escape > 255 || (escape != 0 && !ispunct(escape)))
+ return PCRE2_ERROR_BADDATA;
+ccontext->glob_escape = escape;
+return 0;
+}
+
+/* End of pcre2_context.c */
+
diff --git a/contrib/libs/pcre2/src/pcre2_convert.c b/contrib/libs/pcre2/src/pcre2_convert.c
new file mode 100644
index 0000000000..240828bca5
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_convert.c
@@ -0,0 +1,1181 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+#define TYPE_OPTIONS (PCRE2_CONVERT_GLOB| \
+ PCRE2_CONVERT_POSIX_BASIC|PCRE2_CONVERT_POSIX_EXTENDED)
+
+#define ALL_OPTIONS (PCRE2_CONVERT_UTF|PCRE2_CONVERT_NO_UTF_CHECK| \
+ PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR| \
+ PCRE2_CONVERT_GLOB_NO_STARSTAR| \
+ TYPE_OPTIONS)
+
+#define DUMMY_BUFFER_SIZE 100
+
+/* Generated pattern fragments */
+
+#define STR_BACKSLASH_A STR_BACKSLASH STR_A
+#define STR_BACKSLASH_z STR_BACKSLASH STR_z
+#define STR_COLON_RIGHT_SQUARE_BRACKET STR_COLON STR_RIGHT_SQUARE_BRACKET
+#define STR_DOT_STAR_LOOKBEHIND STR_DOT STR_ASTERISK STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_LESS_THAN_SIGN STR_EQUALS_SIGN
+#define STR_LOOKAHEAD_NOT_DOT STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_EXCLAMATION_MARK STR_BACKSLASH STR_DOT STR_RIGHT_PARENTHESIS
+#define STR_QUERY_s STR_LEFT_PARENTHESIS STR_QUESTION_MARK STR_s STR_RIGHT_PARENTHESIS
+#define STR_STAR_NUL STR_LEFT_PARENTHESIS STR_ASTERISK STR_N STR_U STR_L STR_RIGHT_PARENTHESIS
+
+/* States for POSIX processing */
+
+enum { POSIX_START_REGEX, POSIX_ANCHORED, POSIX_NOT_BRACKET,
+ POSIX_CLASS_NOT_STARTED, POSIX_CLASS_STARTING, POSIX_CLASS_STARTED };
+
+/* Macro to add a character string to the output buffer, checking for overflow. */
+
+#define PUTCHARS(string) \
+ { \
+ for (s = (char *)(string); *s != 0; s++) \
+ { \
+ if (p >= endp) return PCRE2_ERROR_NOMEMORY; \
+ *p++ = *s; \
+ } \
+ }
+
+/* Literals that must be escaped: \ ? * + | . ^ $ { } [ ] ( ) */
+
+static const char *pcre2_escaped_literals =
+ STR_BACKSLASH STR_QUESTION_MARK STR_ASTERISK STR_PLUS
+ STR_VERTICAL_LINE STR_DOT STR_CIRCUMFLEX_ACCENT STR_DOLLAR_SIGN
+ STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET
+ STR_LEFT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET
+ STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS;
+
+/* Recognized escaped metacharacters in POSIX basic patterns. */
+
+static const char *posix_meta_escapes =
+ STR_LEFT_PARENTHESIS STR_RIGHT_PARENTHESIS
+ STR_LEFT_CURLY_BRACKET STR_RIGHT_CURLY_BRACKET
+ STR_1 STR_2 STR_3 STR_4 STR_5 STR_6 STR_7 STR_8 STR_9;
+
+
+
+/*************************************************
+* Convert a POSIX pattern *
+*************************************************/
+
+/* This function handles both basic and extended POSIX patterns.
+
+Arguments:
+ pattype the pattern type
+ pattern the pattern
+ plength length in code units
+ utf TRUE if UTF
+ use_buffer where to put the output
+ use_length length of use_buffer
+ bufflenptr where to put the used length
+ dummyrun TRUE if a dummy run
+ ccontext the convert context
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_posix(uint32_t pattype, PCRE2_SPTR pattern, PCRE2_SIZE plength,
+ BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length,
+ PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext)
+{
+char *s;
+PCRE2_SPTR posix = pattern;
+PCRE2_UCHAR *p = use_buffer;
+PCRE2_UCHAR *pp = p;
+PCRE2_UCHAR *endp = p + use_length - 1; /* Allow for trailing zero */
+PCRE2_SIZE convlength = 0;
+
+uint32_t bracount = 0;
+uint32_t posix_state = POSIX_START_REGEX;
+uint32_t lastspecial = 0;
+BOOL extended = (pattype & PCRE2_CONVERT_POSIX_EXTENDED) != 0;
+BOOL nextisliteral = FALSE;
+
+(void)utf; /* Not used when Unicode not supported */
+(void)ccontext; /* Not currently used */
+
+/* Initialize default for error offset as end of input. */
+
+*bufflenptr = plength;
+PUTCHARS(STR_STAR_NUL);
+
+/* Now scan the input. */
+
+while (plength > 0)
+ {
+ uint32_t c, sc;
+ int clength = 1;
+
+ /* Add in the length of the last item, then, if in the dummy run, pull the
+ pointer back to the start of the (temporary) buffer and then remember the
+ start of the next item. */
+
+ convlength += p - pp;
+ if (dummyrun) p = use_buffer;
+ pp = p;
+
+ /* Pick up the next character */
+
+#ifndef SUPPORT_UNICODE
+ c = *posix;
+#else
+ GETCHARLENTEST(c, posix, clength);
+#endif
+ posix += clength;
+ plength -= clength;
+
+ sc = nextisliteral? 0 : c;
+ nextisliteral = FALSE;
+
+ /* Handle a character within a class. */
+
+ if (posix_state >= POSIX_CLASS_NOT_STARTED)
+ {
+ if (c == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ PUTCHARS(STR_RIGHT_SQUARE_BRACKET);
+ posix_state = POSIX_NOT_BRACKET;
+ }
+
+ /* Not the end of the class */
+
+ else
+ {
+ switch (posix_state)
+ {
+ case POSIX_CLASS_STARTED:
+ if (c <= 127 && islower(c)) break; /* Remain in started state */
+ posix_state = POSIX_CLASS_NOT_STARTED;
+ if (c == CHAR_COLON && plength > 0 &&
+ *posix == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ PUTCHARS(STR_COLON_RIGHT_SQUARE_BRACKET);
+ plength--;
+ posix++;
+ continue; /* With next character after :] */
+ }
+ /* Fall through */
+
+ case POSIX_CLASS_NOT_STARTED:
+ if (c == CHAR_LEFT_SQUARE_BRACKET)
+ posix_state = POSIX_CLASS_STARTING;
+ break;
+
+ case POSIX_CLASS_STARTING:
+ if (c == CHAR_COLON) posix_state = POSIX_CLASS_STARTED;
+ break;
+ }
+
+ if (c == CHAR_BACKSLASH) PUTCHARS(STR_BACKSLASH);
+ if (p + clength > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix - clength, CU2BYTES(clength));
+ p += clength;
+ }
+ }
+
+ /* Handle a character not within a class. */
+
+ else switch(sc)
+ {
+ case CHAR_LEFT_SQUARE_BRACKET:
+ PUTCHARS(STR_LEFT_SQUARE_BRACKET);
+
+#ifdef NEVER
+ /* We could handle special cases [[:<:]] and [[:>:]] (which PCRE does
+ support) but they are not part of POSIX 1003.1. */
+
+ if (plength >= 6)
+ {
+ if (posix[0] == CHAR_LEFT_SQUARE_BRACKET &&
+ posix[1] == CHAR_COLON &&
+ (posix[2] == CHAR_LESS_THAN_SIGN ||
+ posix[2] == CHAR_GREATER_THAN_SIGN) &&
+ posix[3] == CHAR_COLON &&
+ posix[4] == CHAR_RIGHT_SQUARE_BRACKET &&
+ posix[5] == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ if (p + 6 > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix, CU2BYTES(6));
+ p += 6;
+ posix += 6;
+ plength -= 6;
+ continue; /* With next character */
+ }
+ }
+#endif
+
+ /* Handle start of "normal" character classes */
+
+ posix_state = POSIX_CLASS_NOT_STARTED;
+
+ /* Handle ^ and ] as first characters */
+
+ if (plength > 0)
+ {
+ if (*posix == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ posix++;
+ plength--;
+ PUTCHARS(STR_CIRCUMFLEX_ACCENT);
+ }
+ if (plength > 0 && *posix == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ posix++;
+ plength--;
+ PUTCHARS(STR_RIGHT_SQUARE_BRACKET);
+ }
+ }
+ break;
+
+ case CHAR_BACKSLASH:
+ if (plength == 0) return PCRE2_ERROR_END_BACKSLASH;
+ if (extended) nextisliteral = TRUE; else
+ {
+ if (*posix < 127 && strchr(posix_meta_escapes, *posix) != NULL)
+ {
+ if (isdigit(*posix)) PUTCHARS(STR_BACKSLASH);
+ if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY;
+ lastspecial = *p++ = *posix++;
+ plength--;
+ }
+ else nextisliteral = TRUE;
+ }
+ break;
+
+ case CHAR_RIGHT_PARENTHESIS:
+ if (!extended || bracount == 0) goto ESCAPE_LITERAL;
+ bracount--;
+ goto COPY_SPECIAL;
+
+ case CHAR_LEFT_PARENTHESIS:
+ bracount++;
+ /* Fall through */
+
+ case CHAR_QUESTION_MARK:
+ case CHAR_PLUS:
+ case CHAR_LEFT_CURLY_BRACKET:
+ case CHAR_RIGHT_CURLY_BRACKET:
+ case CHAR_VERTICAL_LINE:
+ if (!extended) goto ESCAPE_LITERAL;
+ /* Fall through */
+
+ case CHAR_DOT:
+ case CHAR_DOLLAR_SIGN:
+ posix_state = POSIX_NOT_BRACKET;
+ COPY_SPECIAL:
+ lastspecial = c;
+ if (p + 1 > endp) return PCRE2_ERROR_NOMEMORY;
+ *p++ = c;
+ break;
+
+ case CHAR_ASTERISK:
+ if (lastspecial != CHAR_ASTERISK)
+ {
+ if (!extended && (posix_state < POSIX_NOT_BRACKET ||
+ lastspecial == CHAR_LEFT_PARENTHESIS))
+ goto ESCAPE_LITERAL;
+ goto COPY_SPECIAL;
+ }
+ break; /* Ignore second and subsequent asterisks */
+
+ case CHAR_CIRCUMFLEX_ACCENT:
+ if (extended) goto COPY_SPECIAL;
+ if (posix_state == POSIX_START_REGEX ||
+ lastspecial == CHAR_LEFT_PARENTHESIS)
+ {
+ posix_state = POSIX_ANCHORED;
+ goto COPY_SPECIAL;
+ }
+ /* Fall through */
+
+ default:
+ if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL)
+ {
+ ESCAPE_LITERAL:
+ PUTCHARS(STR_BACKSLASH);
+ }
+ lastspecial = 0xff; /* Indicates nothing special */
+ if (p + clength > endp) return PCRE2_ERROR_NOMEMORY;
+ memcpy(p, posix - clength, CU2BYTES(clength));
+ p += clength;
+ posix_state = POSIX_NOT_BRACKET;
+ break;
+ }
+ }
+
+if (posix_state >= POSIX_CLASS_NOT_STARTED)
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+convlength += p - pp; /* Final segment */
+*bufflenptr = convlength;
+*p++ = 0;
+return 0;
+}
+
+
+/*************************************************
+* Convert a glob pattern *
+*************************************************/
+
+/* Context for writing the output into a buffer. */
+
+typedef struct pcre2_output_context {
+ PCRE2_UCHAR *output; /* current output position */
+ PCRE2_SPTR output_end; /* output end */
+ PCRE2_SIZE output_size; /* size of the output */
+ uint8_t out_str[8]; /* string copied to the output */
+} pcre2_output_context;
+
+
+/* Write a character into the output.
+
+Arguments:
+ out output context
+ chr the next character
+*/
+
+static void
+convert_glob_write(pcre2_output_context *out, PCRE2_UCHAR chr)
+{
+out->output_size++;
+
+if (out->output < out->output_end)
+ *out->output++ = chr;
+}
+
+
+/* Write a string into the output.
+
+Arguments:
+ out output context
+ length length of out->out_str
+*/
+
+static void
+convert_glob_write_str(pcre2_output_context *out, PCRE2_SIZE length)
+{
+uint8_t *out_str = out->out_str;
+PCRE2_UCHAR *output = out->output;
+PCRE2_SPTR output_end = out->output_end;
+PCRE2_SIZE output_size = out->output_size;
+
+do
+ {
+ output_size++;
+
+ if (output < output_end)
+ *output++ = *out_str++;
+ }
+while (--length != 0);
+
+out->output = output;
+out->output_size = output_size;
+}
+
+
+/* Prints the separator into the output.
+
+Arguments:
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+*/
+
+static void
+convert_glob_print_separator(pcre2_output_context *out,
+ PCRE2_UCHAR separator, BOOL with_escape)
+{
+if (with_escape)
+ convert_glob_write(out, CHAR_BACKSLASH);
+
+convert_glob_write(out, separator);
+}
+
+
+/* Prints a wildcard into the output.
+
+Arguments:
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+*/
+
+static void
+convert_glob_print_wildcard(pcre2_output_context *out,
+ PCRE2_UCHAR separator, BOOL with_escape)
+{
+out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET;
+out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT;
+convert_glob_write_str(out, 2);
+
+convert_glob_print_separator(out, separator, with_escape);
+
+convert_glob_write(out, CHAR_RIGHT_SQUARE_BRACKET);
+}
+
+
+/* Parse a posix class.
+
+Arguments:
+ from starting point of scanning the range
+ pattern_end end of pattern
+ out output context
+
+Returns: >0 => class index
+ 0 => malformed class
+*/
+
+static int
+convert_glob_parse_class(PCRE2_SPTR *from, PCRE2_SPTR pattern_end,
+ pcre2_output_context *out)
+{
+static const char *posix_classes = "alnum:alpha:ascii:blank:cntrl:digit:"
+ "graph:lower:print:punct:space:upper:word:xdigit:";
+PCRE2_SPTR start = *from + 1;
+PCRE2_SPTR pattern = start;
+const char *class_ptr;
+PCRE2_UCHAR c;
+int class_index;
+
+while (TRUE)
+ {
+ if (pattern >= pattern_end) return 0;
+
+ c = *pattern++;
+
+ if (c < CHAR_a || c > CHAR_z) break;
+ }
+
+if (c != CHAR_COLON || pattern >= pattern_end ||
+ *pattern != CHAR_RIGHT_SQUARE_BRACKET)
+ return 0;
+
+class_ptr = posix_classes;
+class_index = 1;
+
+while (TRUE)
+ {
+ if (*class_ptr == CHAR_NUL) return 0;
+
+ pattern = start;
+
+ while (*pattern == (PCRE2_UCHAR) *class_ptr)
+ {
+ if (*pattern == CHAR_COLON)
+ {
+ pattern += 2;
+ start -= 2;
+
+ do convert_glob_write(out, *start++); while (start < pattern);
+
+ *from = pattern;
+ return class_index;
+ }
+ pattern++;
+ class_ptr++;
+ }
+
+ while (*class_ptr != CHAR_COLON) class_ptr++;
+ class_ptr++;
+ class_index++;
+ }
+}
+
+/* Checks whether the character is in the class.
+
+Arguments:
+ class_index class index
+ c character
+
+Returns: !0 => character is found in the class
+ 0 => otherwise
+*/
+
+static BOOL
+convert_glob_char_in_class(int class_index, PCRE2_UCHAR c)
+{
+switch (class_index)
+ {
+ case 1: return isalnum(c);
+ case 2: return isalpha(c);
+ case 3: return 1;
+ case 4: return c == CHAR_HT || c == CHAR_SPACE;
+ case 5: return iscntrl(c);
+ case 6: return isdigit(c);
+ case 7: return isgraph(c);
+ case 8: return islower(c);
+ case 9: return isprint(c);
+ case 10: return ispunct(c);
+ case 11: return isspace(c);
+ case 12: return isupper(c);
+ case 13: return isalnum(c) || c == CHAR_UNDERSCORE;
+ default: return isxdigit(c);
+ }
+}
+
+/* Parse a range of characters.
+
+Arguments:
+ from starting point of scanning the range
+ pattern_end end of pattern
+ out output context
+ separator glob separator
+ with_escape backslash is needed before separator
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_glob_parse_range(PCRE2_SPTR *from, PCRE2_SPTR pattern_end,
+ pcre2_output_context *out, BOOL utf, PCRE2_UCHAR separator,
+ BOOL with_escape, PCRE2_UCHAR escape, BOOL no_wildsep)
+{
+BOOL is_negative = FALSE;
+BOOL separator_seen = FALSE;
+BOOL has_prev_c;
+PCRE2_SPTR pattern = *from;
+PCRE2_SPTR char_start = NULL;
+uint32_t c, prev_c;
+int len, class_index;
+
+(void)utf; /* Avoid compiler warning. */
+
+if (pattern >= pattern_end)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+ }
+
+if (*pattern == CHAR_EXCLAMATION_MARK
+ || *pattern == CHAR_CIRCUMFLEX_ACCENT)
+ {
+ pattern++;
+
+ if (pattern >= pattern_end)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+ }
+
+ is_negative = TRUE;
+
+ out->out_str[0] = CHAR_LEFT_SQUARE_BRACKET;
+ out->out_str[1] = CHAR_CIRCUMFLEX_ACCENT;
+ len = 2;
+
+ if (!no_wildsep)
+ {
+ if (with_escape)
+ {
+ out->out_str[len] = CHAR_BACKSLASH;
+ len++;
+ }
+ out->out_str[len] = (uint8_t) separator;
+ }
+
+ convert_glob_write_str(out, len + 1);
+ }
+else
+ convert_glob_write(out, CHAR_LEFT_SQUARE_BRACKET);
+
+has_prev_c = FALSE;
+prev_c = 0;
+
+if (*pattern == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ out->out_str[0] = CHAR_BACKSLASH;
+ out->out_str[1] = CHAR_RIGHT_SQUARE_BRACKET;
+ convert_glob_write_str(out, 2);
+ has_prev_c = TRUE;
+ prev_c = CHAR_RIGHT_SQUARE_BRACKET;
+ pattern++;
+ }
+
+while (pattern < pattern_end)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (c == CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ convert_glob_write(out, c);
+
+ if (!is_negative && !no_wildsep && separator_seen)
+ {
+ out->out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out->out_str[1] = CHAR_QUESTION_MARK;
+ out->out_str[2] = CHAR_LESS_THAN_SIGN;
+ out->out_str[3] = CHAR_EXCLAMATION_MARK;
+ convert_glob_write_str(out, 4);
+
+ convert_glob_print_separator(out, separator, with_escape);
+ convert_glob_write(out, CHAR_RIGHT_PARENTHESIS);
+ }
+
+ *from = pattern;
+ return 0;
+ }
+
+ if (pattern >= pattern_end) break;
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON)
+ {
+ *from = pattern;
+ class_index = convert_glob_parse_class(from, pattern_end, out);
+
+ if (class_index != 0)
+ {
+ pattern = *from;
+
+ has_prev_c = FALSE;
+ prev_c = 0;
+
+ if (!is_negative &&
+ convert_glob_char_in_class (class_index, separator))
+ separator_seen = TRUE;
+ continue;
+ }
+ }
+ else if (c == CHAR_MINUS && has_prev_c &&
+ *pattern != CHAR_RIGHT_SQUARE_BRACKET)
+ {
+ convert_glob_write(out, CHAR_MINUS);
+
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (pattern >= pattern_end) break;
+
+ if (escape != 0 && c == escape)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+ }
+ else if (c == CHAR_LEFT_SQUARE_BRACKET && *pattern == CHAR_COLON)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+
+ if (prev_c > c)
+ {
+ *from = pattern;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+
+ if (prev_c < separator && separator < c) separator_seen = TRUE;
+
+ has_prev_c = FALSE;
+ prev_c = 0;
+ }
+ else
+ {
+ if (escape != 0 && c == escape)
+ {
+ char_start = pattern;
+ GETCHARINCTEST(c, pattern);
+
+ if (pattern >= pattern_end) break;
+ }
+
+ has_prev_c = TRUE;
+ prev_c = c;
+ }
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET || c == CHAR_RIGHT_SQUARE_BRACKET ||
+ c == CHAR_BACKSLASH || c == CHAR_MINUS)
+ convert_glob_write(out, CHAR_BACKSLASH);
+
+ if (c == separator) separator_seen = TRUE;
+
+ do convert_glob_write(out, *char_start++); while (char_start < pattern);
+ }
+
+*from = pattern;
+return PCRE2_ERROR_MISSING_SQUARE_BRACKET;
+}
+
+
+/* Prints a (*COMMIT) into the output.
+
+Arguments:
+ out output context
+*/
+
+static void
+convert_glob_print_commit(pcre2_output_context *out)
+{
+out->out_str[0] = CHAR_LEFT_PARENTHESIS;
+out->out_str[1] = CHAR_ASTERISK;
+out->out_str[2] = CHAR_C;
+out->out_str[3] = CHAR_O;
+out->out_str[4] = CHAR_M;
+out->out_str[5] = CHAR_M;
+out->out_str[6] = CHAR_I;
+out->out_str[7] = CHAR_T;
+convert_glob_write_str(out, 8);
+convert_glob_write(out, CHAR_RIGHT_PARENTHESIS);
+}
+
+
+/* Bash glob converter.
+
+Arguments:
+ pattype the pattern type
+ pattern the pattern
+ plength length in code units
+ utf TRUE if UTF
+ use_buffer where to put the output
+ use_length length of use_buffer
+ bufflenptr where to put the used length
+ dummyrun TRUE if a dummy run
+ ccontext the convert context
+
+Returns: 0 => success
+ !0 => error code
+*/
+
+static int
+convert_glob(uint32_t options, PCRE2_SPTR pattern, PCRE2_SIZE plength,
+ BOOL utf, PCRE2_UCHAR *use_buffer, PCRE2_SIZE use_length,
+ PCRE2_SIZE *bufflenptr, BOOL dummyrun, pcre2_convert_context *ccontext)
+{
+pcre2_output_context out;
+PCRE2_SPTR pattern_start = pattern;
+PCRE2_SPTR pattern_end = pattern + plength;
+PCRE2_UCHAR separator = ccontext->glob_separator;
+PCRE2_UCHAR escape = ccontext->glob_escape;
+PCRE2_UCHAR c;
+BOOL no_wildsep = (options & PCRE2_CONVERT_GLOB_NO_WILD_SEPARATOR) != 0;
+BOOL no_starstar = (options & PCRE2_CONVERT_GLOB_NO_STARSTAR) != 0;
+BOOL in_atomic = FALSE;
+BOOL after_starstar = FALSE;
+BOOL no_slash_z = FALSE;
+BOOL with_escape, is_start, after_separator;
+int result = 0;
+
+(void)utf; /* Avoid compiler warning. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && (separator >= 128 || escape >= 128))
+ {
+ /* Currently only ASCII characters are supported. */
+ *bufflenptr = 0;
+ return PCRE2_ERROR_CONVERT_SYNTAX;
+ }
+#endif
+
+with_escape = strchr(pcre2_escaped_literals, separator) != NULL;
+
+/* Initialize default for error offset as end of input. */
+out.output = use_buffer;
+out.output_end = use_buffer + use_length;
+out.output_size = 0;
+
+out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+out.out_str[1] = CHAR_QUESTION_MARK;
+out.out_str[2] = CHAR_s;
+out.out_str[3] = CHAR_RIGHT_PARENTHESIS;
+convert_glob_write_str(&out, 4);
+
+is_start = TRUE;
+
+if (pattern < pattern_end && pattern[0] == CHAR_ASTERISK)
+ {
+ if (no_wildsep)
+ is_start = FALSE;
+ else if (!no_starstar && pattern + 1 < pattern_end &&
+ pattern[1] == CHAR_ASTERISK)
+ is_start = FALSE;
+ }
+
+if (is_start)
+ {
+ out.out_str[0] = CHAR_BACKSLASH;
+ out.out_str[1] = CHAR_A;
+ convert_glob_write_str(&out, 2);
+ }
+
+while (pattern < pattern_end)
+ {
+ c = *pattern++;
+
+ if (c == CHAR_ASTERISK)
+ {
+ is_start = pattern == pattern_start + 1;
+
+ if (in_atomic)
+ {
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+ in_atomic = FALSE;
+ }
+
+ if (!no_starstar && pattern < pattern_end && *pattern == CHAR_ASTERISK)
+ {
+ after_separator = is_start || (pattern[-2] == separator);
+
+ do pattern++; while (pattern < pattern_end &&
+ *pattern == CHAR_ASTERISK);
+
+ if (pattern >= pattern_end)
+ {
+ no_slash_z = TRUE;
+ break;
+ }
+
+ after_starstar = TRUE;
+
+ if (after_separator && escape != 0 && *pattern == escape &&
+ pattern + 1 < pattern_end && pattern[1] == separator)
+ pattern++;
+
+ if (is_start)
+ {
+ if (*pattern != separator) continue;
+
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_COLON;
+ out.out_str[3] = CHAR_BACKSLASH;
+ out.out_str[4] = CHAR_A;
+ out.out_str[5] = CHAR_VERTICAL_LINE;
+ convert_glob_write_str(&out, 6);
+
+ convert_glob_print_separator(&out, separator, with_escape);
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+
+ pattern++;
+ continue;
+ }
+
+ convert_glob_print_commit(&out);
+
+ if (!after_separator || *pattern != separator)
+ {
+ out.out_str[0] = CHAR_DOT;
+ out.out_str[1] = CHAR_ASTERISK;
+ out.out_str[2] = CHAR_QUESTION_MARK;
+ convert_glob_write_str(&out, 3);
+ continue;
+ }
+
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_COLON;
+ out.out_str[3] = CHAR_DOT;
+ out.out_str[4] = CHAR_ASTERISK;
+ out.out_str[5] = CHAR_QUESTION_MARK;
+
+ convert_glob_write_str(&out, 6);
+
+ convert_glob_print_separator(&out, separator, with_escape);
+
+ out.out_str[0] = CHAR_RIGHT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_QUESTION_MARK;
+ convert_glob_write_str(&out, 3);
+
+ pattern++;
+ continue;
+ }
+
+ if (pattern < pattern_end && *pattern == CHAR_ASTERISK)
+ {
+ do pattern++; while (pattern < pattern_end &&
+ *pattern == CHAR_ASTERISK);
+ }
+
+ if (no_wildsep)
+ {
+ if (pattern >= pattern_end)
+ {
+ no_slash_z = TRUE;
+ break;
+ }
+
+ /* Start check must be after the end check. */
+ if (is_start) continue;
+ }
+
+ if (!is_start)
+ {
+ if (after_starstar)
+ {
+ out.out_str[0] = CHAR_LEFT_PARENTHESIS;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ out.out_str[2] = CHAR_GREATER_THAN_SIGN;
+ convert_glob_write_str(&out, 3);
+ in_atomic = TRUE;
+ }
+ else
+ convert_glob_print_commit(&out);
+ }
+
+ if (no_wildsep)
+ convert_glob_write(&out, CHAR_DOT);
+ else
+ convert_glob_print_wildcard(&out, separator, with_escape);
+
+ out.out_str[0] = CHAR_ASTERISK;
+ out.out_str[1] = CHAR_QUESTION_MARK;
+ if (pattern >= pattern_end)
+ out.out_str[1] = CHAR_PLUS;
+ convert_glob_write_str(&out, 2);
+ continue;
+ }
+
+ if (c == CHAR_QUESTION_MARK)
+ {
+ if (no_wildsep)
+ convert_glob_write(&out, CHAR_DOT);
+ else
+ convert_glob_print_wildcard(&out, separator, with_escape);
+ continue;
+ }
+
+ if (c == CHAR_LEFT_SQUARE_BRACKET)
+ {
+ result = convert_glob_parse_range(&pattern, pattern_end,
+ &out, utf, separator, with_escape, escape, no_wildsep);
+ if (result != 0) break;
+ continue;
+ }
+
+ if (escape != 0 && c == escape)
+ {
+ if (pattern >= pattern_end)
+ {
+ result = PCRE2_ERROR_CONVERT_SYNTAX;
+ break;
+ }
+ c = *pattern++;
+ }
+
+ if (c < 128 && strchr(pcre2_escaped_literals, c) != NULL)
+ convert_glob_write(&out, CHAR_BACKSLASH);
+
+ convert_glob_write(&out, c);
+ }
+
+if (result == 0)
+ {
+ if (!no_slash_z)
+ {
+ out.out_str[0] = CHAR_BACKSLASH;
+ out.out_str[1] = CHAR_z;
+ convert_glob_write_str(&out, 2);
+ }
+
+ if (in_atomic)
+ convert_glob_write(&out, CHAR_RIGHT_PARENTHESIS);
+
+ convert_glob_write(&out, CHAR_NUL);
+
+ if (!dummyrun && out.output_size != (PCRE2_SIZE) (out.output - use_buffer))
+ result = PCRE2_ERROR_NOMEMORY;
+ }
+
+if (result != 0)
+ {
+ *bufflenptr = pattern - pattern_start;
+ return result;
+ }
+
+*bufflenptr = out.output_size - 1;
+return 0;
+}
+
+
+/*************************************************
+* Convert pattern *
+*************************************************/
+
+/* This is the external-facing function for converting other forms of pattern
+into PCRE2 regular expression patterns. On error, the bufflenptr argument is
+used to return an offset in the original pattern.
+
+Arguments:
+ pattern the input pattern
+ plength length of input, or PCRE2_ZERO_TERMINATED
+ options options bits
+ buffptr pointer to pointer to output buffer
+ bufflenptr pointer to length of output buffer
+ ccontext convert context or NULL
+
+Returns: 0 for success, else an error code (+ve or -ve)
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_pattern_convert(PCRE2_SPTR pattern, PCRE2_SIZE plength, uint32_t options,
+ PCRE2_UCHAR **buffptr, PCRE2_SIZE *bufflenptr,
+ pcre2_convert_context *ccontext)
+{
+int i, rc;
+PCRE2_UCHAR dummy_buffer[DUMMY_BUFFER_SIZE];
+PCRE2_UCHAR *use_buffer = dummy_buffer;
+PCRE2_SIZE use_length = DUMMY_BUFFER_SIZE;
+BOOL utf = (options & PCRE2_CONVERT_UTF) != 0;
+uint32_t pattype = options & TYPE_OPTIONS;
+
+if (pattern == NULL || bufflenptr == NULL) return PCRE2_ERROR_NULL;
+
+if ((options & ~ALL_OPTIONS) != 0 || /* Undefined bit set */
+ (pattype & (~pattype+1)) != pattype || /* More than one type set */
+ pattype == 0) /* No type set */
+ {
+ *bufflenptr = 0; /* Error offset */
+ return PCRE2_ERROR_BADOPTION;
+ }
+
+if (plength == PCRE2_ZERO_TERMINATED) plength = PRIV(strlen)(pattern);
+if (ccontext == NULL) ccontext =
+ (pcre2_convert_context *)(&PRIV(default_convert_context));
+
+/* Check UTF if required. */
+
+#ifndef SUPPORT_UNICODE
+if (utf)
+ {
+ *bufflenptr = 0; /* Error offset */
+ return PCRE2_ERROR_UNICODE_NOT_SUPPORTED;
+ }
+#else
+if (utf && (options & PCRE2_CONVERT_NO_UTF_CHECK) == 0)
+ {
+ PCRE2_SIZE erroroffset;
+ rc = PRIV(valid_utf)(pattern, plength, &erroroffset);
+ if (rc != 0)
+ {
+ *bufflenptr = erroroffset;
+ return rc;
+ }
+ }
+#endif
+
+/* If buffptr is not NULL, and what it points to is not NULL, we are being
+provided with a buffer and a length, so set them as the buffer to use. */
+
+if (buffptr != NULL && *buffptr != NULL)
+ {
+ use_buffer = *buffptr;
+ use_length = *bufflenptr;
+ }
+
+/* Call an individual converter, either just once (if a buffer was provided or
+just the length is needed), or twice (if a memory allocation is required). */
+
+for (i = 0; i < 2; i++)
+ {
+ PCRE2_UCHAR *allocated;
+ BOOL dummyrun = buffptr == NULL || *buffptr == NULL;
+
+ switch(pattype)
+ {
+ case PCRE2_CONVERT_GLOB:
+ rc = convert_glob(options & ~PCRE2_CONVERT_GLOB, pattern, plength, utf,
+ use_buffer, use_length, bufflenptr, dummyrun, ccontext);
+ break;
+
+ case PCRE2_CONVERT_POSIX_BASIC:
+ case PCRE2_CONVERT_POSIX_EXTENDED:
+ rc = convert_posix(pattype, pattern, plength, utf, use_buffer, use_length,
+ bufflenptr, dummyrun, ccontext);
+ break;
+
+ default:
+ *bufflenptr = 0; /* Error offset */
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ if (rc != 0 || /* Error */
+ buffptr == NULL || /* Just the length is required */
+ *buffptr != NULL) /* Buffer was provided or allocated */
+ return rc;
+
+ /* Allocate memory for the buffer, with hidden space for an allocator at
+ the start. The next time round the loop runs the conversion for real. */
+
+ allocated = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
+ (*bufflenptr + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)ccontext);
+ if (allocated == NULL) return PCRE2_ERROR_NOMEMORY;
+ *buffptr = (PCRE2_UCHAR *)(((char *)allocated) + sizeof(pcre2_memctl));
+
+ use_buffer = *buffptr;
+ use_length = *bufflenptr + 1;
+ }
+
+/* Control should never get here. */
+
+return PCRE2_ERROR_INTERNAL;
+}
+
+
+/*************************************************
+* Free converted pattern *
+*************************************************/
+
+/* This frees a converted pattern that was put in newly-allocated memory.
+
+Argument: the converted pattern
+Returns: nothing
+*/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_converted_pattern_free(PCRE2_UCHAR *converted)
+{
+if (converted != NULL)
+ {
+ pcre2_memctl *memctl =
+ (pcre2_memctl *)((char *)converted - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+/* End of pcre2_convert.c */
diff --git a/contrib/libs/pcre2/src/pcre2_dfa_match.c b/contrib/libs/pcre2/src/pcre2_dfa_match.c
new file mode 100644
index 0000000000..faa3db8b57
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_dfa_match.c
@@ -0,0 +1,4066 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains the external function pcre2_dfa_match(), which is an
+alternative matching function that uses a sort of DFA algorithm (not a true
+FSM). This is NOT Perl-compatible, but it has advantages in certain
+applications. */
+
+
+/* NOTE ABOUT PERFORMANCE: A user of this function sent some code that improved
+the performance of his patterns greatly. I could not use it as it stood, as it
+was not thread safe, and made assumptions about pattern sizes. Also, it caused
+test 7 to loop, and test 9 to crash with a segfault.
+
+The issue is the check for duplicate states, which is done by a simple linear
+search up the state list. (Grep for "duplicate" below to find the code.) For
+many patterns, there will never be many states active at one time, so a simple
+linear search is fine. In patterns that have many active states, it might be a
+bottleneck. The suggested code used an indexing scheme to remember which states
+had previously been used for each character, and avoided the linear search when
+it knew there was no chance of a duplicate. This was implemented when adding
+states to the state lists.
+
+I wrote some thread-safe, not-limited code to try something similar at the time
+of checking for duplicates (instead of when adding states), using index vectors
+on the stack. It did give a 13% improvement with one specially constructed
+pattern for certain subject strings, but on other strings and on many of the
+simpler patterns in the test suite it did worse. The major problem, I think,
+was the extra time to initialize the index. This had to be done for each call
+of internal_dfa_match(). (The supplied patch used a static vector, initialized
+only once - I suspect this was the cause of the problems with the tests.)
+
+Overall, I concluded that the gains in some cases did not outweigh the losses
+in others, so I abandoned this code. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#define NLBLOCK mb /* Block containing newline information */
+#define PSSTART start_subject /* Field containing processed string start */
+#define PSEND end_subject /* Field containing processed string end */
+
+#include "pcre2_internal.h"
+
+#define PUBLIC_DFA_MATCH_OPTIONS \
+ (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \
+ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \
+ PCRE2_PARTIAL_SOFT|PCRE2_DFA_SHORTEST|PCRE2_DFA_RESTART| \
+ PCRE2_COPY_MATCHED_SUBJECT)
+
+
+/*************************************************
+* Code parameters and static tables *
+*************************************************/
+
+/* These are offsets that are used to turn the OP_TYPESTAR and friends opcodes
+into others, under special conditions. A gap of 20 between the blocks should be
+enough. The resulting opcodes don't have to be less than 256 because they are
+never stored, so we push them well clear of the normal opcodes. */
+
+#define OP_PROP_EXTRA 300
+#define OP_EXTUNI_EXTRA 320
+#define OP_ANYNL_EXTRA 340
+#define OP_HSPACE_EXTRA 360
+#define OP_VSPACE_EXTRA 380
+
+
+/* This table identifies those opcodes that are followed immediately by a
+character that is to be tested in some way. This makes it possible to
+centralize the loading of these characters. In the case of Type * etc, the
+"character" is the opcode for \D, \d, \S, \s, \W, or \w, which will always be a
+small value. Non-zero values in the table are the offsets from the opcode where
+the character is to be found. ***NOTE*** If the start of this table is
+modified, the three tables that follow must also be modified. */
+
+static const uint8_t coptable[] = {
+ 0, /* End */
+ 0, 0, 0, 0, 0, /* \A, \G, \K, \B, \b */
+ 0, 0, 0, 0, 0, 0, /* \D, \d, \S, \s, \W, \w */
+ 0, 0, 0, /* Any, AllAny, Anybyte */
+ 0, 0, /* \P, \p */
+ 0, 0, 0, 0, 0, /* \R, \H, \h, \V, \v */
+ 0, /* \X */
+ 0, 0, 0, 0, 0, 0, /* \Z, \z, $, $M, ^, ^M */
+ 1, /* Char */
+ 1, /* Chari */
+ 1, /* not */
+ 1, /* noti */
+ /* Positive single-char repeats */
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto, minupto */
+ 1+IMM2_SIZE, /* exact */
+ 1, 1, 1, 1+IMM2_SIZE, /* *+, ++, ?+, upto+ */
+ 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto I, minupto I */
+ 1+IMM2_SIZE, /* exact I */
+ 1, 1, 1, 1+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */
+ /* Negative single-char repeats - only for chars < 256 */
+ 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto, minupto */
+ 1+IMM2_SIZE, /* NOT exact */
+ 1, 1, 1, 1+IMM2_SIZE, /* NOT *+, ++, ?+, upto+ */
+ 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto I, minupto I */
+ 1+IMM2_SIZE, /* NOT exact I */
+ 1, 1, 1, 1+IMM2_SIZE, /* NOT *+I, ++I, ?+I, upto+I */
+ /* Positive type repeats */
+ 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */
+ 1+IMM2_SIZE, 1+IMM2_SIZE, /* Type upto, minupto */
+ 1+IMM2_SIZE, /* Type exact */
+ 1, 1, 1, 1+IMM2_SIZE, /* Type *+, ++, ?+, upto+ */
+ /* Character class & ref repeats */
+ 0, 0, 0, 0, 0, 0, /* *, *?, +, +?, ?, ?? */
+ 0, 0, /* CRRANGE, CRMINRANGE */
+ 0, 0, 0, 0, /* Possessive *+, ++, ?+, CRPOSRANGE */
+ 0, /* CLASS */
+ 0, /* NCLASS */
+ 0, /* XCLASS - variable length */
+ 0, /* REF */
+ 0, /* REFI */
+ 0, /* DNREF */
+ 0, /* DNREFI */
+ 0, /* RECURSE */
+ 0, /* CALLOUT */
+ 0, /* CALLOUT_STR */
+ 0, /* Alt */
+ 0, /* Ket */
+ 0, /* KetRmax */
+ 0, /* KetRmin */
+ 0, /* KetRpos */
+ 0, /* Reverse */
+ 0, /* Assert */
+ 0, /* Assert not */
+ 0, /* Assert behind */
+ 0, /* Assert behind not */
+ 0, /* NA assert */
+ 0, /* NA assert behind */
+ 0, /* ONCE */
+ 0, /* SCRIPT_RUN */
+ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */
+ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */
+ 0, 0, /* CREF, DNCREF */
+ 0, 0, /* RREF, DNRREF */
+ 0, 0, /* FALSE, TRUE */
+ 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */
+ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */
+ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */
+ 0, 0, /* COMMIT, COMMIT_ARG */
+ 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */
+ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */
+};
+
+/* This table identifies those opcodes that inspect a character. It is used to
+remember the fact that a character could have been inspected when the end of
+the subject is reached. ***NOTE*** If the start of this table is modified, the
+two tables that follow must also be modified. */
+
+static const uint8_t poptable[] = {
+ 0, /* End */
+ 0, 0, 0, 1, 1, /* \A, \G, \K, \B, \b */
+ 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */
+ 1, 1, 1, /* Any, AllAny, Anybyte */
+ 1, 1, /* \P, \p */
+ 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */
+ 1, /* \X */
+ 0, 0, 0, 0, 0, 0, /* \Z, \z, $, $M, ^, ^M */
+ 1, /* Char */
+ 1, /* Chari */
+ 1, /* not */
+ 1, /* noti */
+ /* Positive single-char repeats */
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
+ 1, 1, 1, /* upto, minupto, exact */
+ 1, 1, 1, 1, /* *+, ++, ?+, upto+ */
+ 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */
+ 1, 1, 1, /* upto I, minupto I, exact I */
+ 1, 1, 1, 1, /* *+I, ++I, ?+I, upto+I */
+ /* Negative single-char repeats - only for chars < 256 */
+ 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */
+ 1, 1, 1, /* NOT upto, minupto, exact */
+ 1, 1, 1, 1, /* NOT *+, ++, ?+, upto+ */
+ 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */
+ 1, 1, 1, /* NOT upto I, minupto I, exact I */
+ 1, 1, 1, 1, /* NOT *+I, ++I, ?+I, upto+I */
+ /* Positive type repeats */
+ 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */
+ 1, 1, 1, /* Type upto, minupto, exact */
+ 1, 1, 1, 1, /* Type *+, ++, ?+, upto+ */
+ /* Character class & ref repeats */
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */
+ 1, 1, /* CRRANGE, CRMINRANGE */
+ 1, 1, 1, 1, /* Possessive *+, ++, ?+, CRPOSRANGE */
+ 1, /* CLASS */
+ 1, /* NCLASS */
+ 1, /* XCLASS - variable length */
+ 0, /* REF */
+ 0, /* REFI */
+ 0, /* DNREF */
+ 0, /* DNREFI */
+ 0, /* RECURSE */
+ 0, /* CALLOUT */
+ 0, /* CALLOUT_STR */
+ 0, /* Alt */
+ 0, /* Ket */
+ 0, /* KetRmax */
+ 0, /* KetRmin */
+ 0, /* KetRpos */
+ 0, /* Reverse */
+ 0, /* Assert */
+ 0, /* Assert not */
+ 0, /* Assert behind */
+ 0, /* Assert behind not */
+ 0, /* NA assert */
+ 0, /* NA assert behind */
+ 0, /* ONCE */
+ 0, /* SCRIPT_RUN */
+ 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */
+ 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */
+ 0, 0, /* CREF, DNCREF */
+ 0, 0, /* RREF, DNRREF */
+ 0, 0, /* FALSE, TRUE */
+ 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */
+ 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */
+ 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */
+ 0, 0, /* COMMIT, COMMIT_ARG */
+ 0, 0, 0, /* FAIL, ACCEPT, ASSERT_ACCEPT */
+ 0, 0, 0 /* CLOSE, SKIPZERO, DEFINE */
+};
+
+/* These 2 tables allow for compact code for testing for \D, \d, \S, \s, \W,
+and \w */
+
+static const uint8_t toptable1[] = {
+ 0, 0, 0, 0, 0, 0,
+ ctype_digit, ctype_digit,
+ ctype_space, ctype_space,
+ ctype_word, ctype_word,
+ 0, 0 /* OP_ANY, OP_ALLANY */
+};
+
+static const uint8_t toptable2[] = {
+ 0, 0, 0, 0, 0, 0,
+ ctype_digit, 0,
+ ctype_space, 0,
+ ctype_word, 0,
+ 1, 1 /* OP_ANY, OP_ALLANY */
+};
+
+
+/* Structure for holding data about a particular state, which is in effect the
+current data for an active path through the match tree. It must consist
+entirely of ints because the working vector we are passed, and which we put
+these structures in, is a vector of ints. */
+
+typedef struct stateblock {
+ int offset; /* Offset to opcode (-ve has meaning) */
+ int count; /* Count for repeats */
+ int data; /* Some use extra data */
+} stateblock;
+
+#define INTS_PER_STATEBLOCK (int)(sizeof(stateblock)/sizeof(int))
+
+
+/* Before version 10.32 the recursive calls of internal_dfa_match() were passed
+local working space and output vectors that were created on the stack. This has
+caused issues for some patterns, especially in small-stack environments such as
+Windows. A new scheme is now in use which sets up a vector on the stack, but if
+this is too small, heap memory is used, up to the heap_limit. The main
+parameters are all numbers of ints because the workspace is a vector of ints.
+
+The size of the starting stack vector, DFA_START_RWS_SIZE, is in bytes, and is
+defined in pcre2_internal.h so as to be available to pcre2test when it is
+finding the minimum heap requirement for a match. */
+
+#define OVEC_UNIT (sizeof(PCRE2_SIZE)/sizeof(int))
+
+#define RWS_BASE_SIZE (DFA_START_RWS_SIZE/sizeof(int)) /* Stack vector */
+#define RWS_RSIZE 1000 /* Work size for recursion */
+#define RWS_OVEC_RSIZE (1000*OVEC_UNIT) /* Ovector for recursion */
+#define RWS_OVEC_OSIZE (2*OVEC_UNIT) /* Ovector in other cases */
+
+/* This structure is at the start of each workspace block. */
+
+typedef struct RWS_anchor {
+ struct RWS_anchor *next;
+ uint32_t size; /* Number of ints */
+ uint32_t free; /* Number of ints */
+} RWS_anchor;
+
+#define RWS_ANCHOR_SIZE (sizeof(RWS_anchor)/sizeof(int))
+
+
+
+/*************************************************
+* Process a callout *
+*************************************************/
+
+/* This function is called to perform a callout.
+
+Arguments:
+ code current code pointer
+ offsets points to current capture offsets
+ current_subject start of current subject match
+ ptr current position in subject
+ mb the match block
+ extracode extra code offset when called from condition
+ lengthptr where to return the callout length
+
+Returns: the return from the callout
+*/
+
+static int
+do_callout_dfa(PCRE2_SPTR code, PCRE2_SIZE *offsets, PCRE2_SPTR current_subject,
+ PCRE2_SPTR ptr, dfa_match_block *mb, PCRE2_SIZE extracode,
+ PCRE2_SIZE *lengthptr)
+{
+pcre2_callout_block *cb = mb->cb;
+
+*lengthptr = (code[extracode] == OP_CALLOUT)?
+ (PCRE2_SIZE)PRIV(OP_lengths)[OP_CALLOUT] :
+ (PCRE2_SIZE)GET(code, 1 + 2*LINK_SIZE + extracode);
+
+if (mb->callout == NULL) return 0; /* No callout provided */
+
+/* Fixed fields in the callout block are set once and for all at the start of
+matching. */
+
+cb->offset_vector = offsets;
+cb->start_match = (PCRE2_SIZE)(current_subject - mb->start_subject);
+cb->current_position = (PCRE2_SIZE)(ptr - mb->start_subject);
+cb->pattern_position = GET(code, 1 + extracode);
+cb->next_item_length = GET(code, 1 + LINK_SIZE + extracode);
+
+if (code[extracode] == OP_CALLOUT)
+ {
+ cb->callout_number = code[1 + 2*LINK_SIZE + extracode];
+ cb->callout_string_offset = 0;
+ cb->callout_string = NULL;
+ cb->callout_string_length = 0;
+ }
+else
+ {
+ cb->callout_number = 0;
+ cb->callout_string_offset = GET(code, 1 + 3*LINK_SIZE + extracode);
+ cb->callout_string = code + (1 + 4*LINK_SIZE + extracode) + 1;
+ cb->callout_string_length = *lengthptr - (1 + 4*LINK_SIZE) - 2;
+ }
+
+return (mb->callout)(cb, mb->callout_data);
+}
+
+
+
+/*************************************************
+* Expand local workspace memory *
+*************************************************/
+
+/* This function is called when internal_dfa_match() is about to be called
+recursively and there is insufficient working space left in the current
+workspace block. If there's an existing next block, use it; otherwise get a new
+block unless the heap limit is reached.
+
+Arguments:
+ rwsptr pointer to block pointer (updated)
+ ovecsize space needed for an ovector
+ mb the match block
+
+Returns: 0 rwsptr has been updated
+ !0 an error code
+*/
+
+static int
+more_workspace(RWS_anchor **rwsptr, unsigned int ovecsize, dfa_match_block *mb)
+{
+RWS_anchor *rws = *rwsptr;
+RWS_anchor *new;
+
+if (rws->next != NULL)
+ {
+ new = rws->next;
+ }
+
+/* Sizes in the RWS_anchor blocks are in units of sizeof(int), but
+mb->heap_limit and mb->heap_used are in kibibytes. Play carefully, to avoid
+overflow. */
+
+else
+ {
+ uint32_t newsize = (rws->size >= UINT32_MAX/2)? UINT32_MAX/2 : rws->size * 2;
+ uint32_t newsizeK = newsize/(1024/sizeof(int));
+
+ if (newsizeK + mb->heap_used > mb->heap_limit)
+ newsizeK = (uint32_t)(mb->heap_limit - mb->heap_used);
+ newsize = newsizeK*(1024/sizeof(int));
+
+ if (newsize < RWS_RSIZE + ovecsize + RWS_ANCHOR_SIZE)
+ return PCRE2_ERROR_HEAPLIMIT;
+ new = mb->memctl.malloc(newsize*sizeof(int), mb->memctl.memory_data);
+ if (new == NULL) return PCRE2_ERROR_NOMEMORY;
+ mb->heap_used += newsizeK;
+ new->next = NULL;
+ new->size = newsize;
+ rws->next = new;
+ }
+
+new->free = new->size - RWS_ANCHOR_SIZE;
+*rwsptr = new;
+return 0;
+}
+
+
+
+/*************************************************
+* Match a Regular Expression - DFA engine *
+*************************************************/
+
+/* This internal function applies a compiled pattern to a subject string,
+starting at a given point, using a DFA engine. This function is called from the
+external one, possibly multiple times if the pattern is not anchored. The
+function calls itself recursively for some kinds of subpattern.
+
+Arguments:
+ mb the match_data block with fixed information
+ this_start_code the opening bracket of this subexpression's code
+ current_subject where we currently are in the subject string
+ start_offset start offset in the subject string
+ offsets vector to contain the matching string offsets
+ offsetcount size of same
+ workspace vector of workspace
+ wscount size of same
+ rlevel function call recursion level
+
+Returns: > 0 => number of match offset pairs placed in offsets
+ = 0 => offsets overflowed; longest matches are present
+ -1 => failed to match
+ < -1 => some kind of unexpected problem
+
+The following macros are used for adding states to the two state vectors (one
+for the current character, one for the following character). */
+
+#define ADD_ACTIVE(x,y) \
+ if (active_count++ < wscount) \
+ { \
+ next_active_state->offset = (x); \
+ next_active_state->count = (y); \
+ next_active_state++; \
+ } \
+ else return PCRE2_ERROR_DFA_WSSIZE
+
+#define ADD_ACTIVE_DATA(x,y,z) \
+ if (active_count++ < wscount) \
+ { \
+ next_active_state->offset = (x); \
+ next_active_state->count = (y); \
+ next_active_state->data = (z); \
+ next_active_state++; \
+ } \
+ else return PCRE2_ERROR_DFA_WSSIZE
+
+#define ADD_NEW(x,y) \
+ if (new_count++ < wscount) \
+ { \
+ next_new_state->offset = (x); \
+ next_new_state->count = (y); \
+ next_new_state++; \
+ } \
+ else return PCRE2_ERROR_DFA_WSSIZE
+
+#define ADD_NEW_DATA(x,y,z) \
+ if (new_count++ < wscount) \
+ { \
+ next_new_state->offset = (x); \
+ next_new_state->count = (y); \
+ next_new_state->data = (z); \
+ next_new_state++; \
+ } \
+ else return PCRE2_ERROR_DFA_WSSIZE
+
+/* And now, here is the code */
+
+static int
+internal_dfa_match(
+ dfa_match_block *mb,
+ PCRE2_SPTR this_start_code,
+ PCRE2_SPTR current_subject,
+ PCRE2_SIZE start_offset,
+ PCRE2_SIZE *offsets,
+ uint32_t offsetcount,
+ int *workspace,
+ int wscount,
+ uint32_t rlevel,
+ int *RWS)
+{
+stateblock *active_states, *new_states, *temp_states;
+stateblock *next_active_state, *next_new_state;
+const uint8_t *ctypes, *lcc, *fcc;
+PCRE2_SPTR ptr;
+PCRE2_SPTR end_code;
+dfa_recursion_info new_recursive;
+int active_count, new_count, match_count;
+
+/* Some fields in the mb block are frequently referenced, so we load them into
+independent variables in the hope that this will perform better. */
+
+PCRE2_SPTR start_subject = mb->start_subject;
+PCRE2_SPTR end_subject = mb->end_subject;
+PCRE2_SPTR start_code = mb->start_code;
+
+#ifdef SUPPORT_UNICODE
+BOOL utf = (mb->poptions & PCRE2_UTF) != 0;
+BOOL utf_or_ucp = utf || (mb->poptions & PCRE2_UCP) != 0;
+#else
+BOOL utf = FALSE;
+#endif
+
+BOOL reset_could_continue = FALSE;
+
+if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT;
+if (rlevel++ > mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT;
+offsetcount &= (uint32_t)(-2); /* Round down */
+
+wscount -= 2;
+wscount = (wscount - (wscount % (INTS_PER_STATEBLOCK * 2))) /
+ (2 * INTS_PER_STATEBLOCK);
+
+ctypes = mb->tables + ctypes_offset;
+lcc = mb->tables + lcc_offset;
+fcc = mb->tables + fcc_offset;
+
+match_count = PCRE2_ERROR_NOMATCH; /* A negative number */
+
+active_states = (stateblock *)(workspace + 2);
+next_new_state = new_states = active_states + wscount;
+new_count = 0;
+
+/* The first thing in any (sub) pattern is a bracket of some sort. Push all
+the alternative states onto the list, and find out where the end is. This
+makes is possible to use this function recursively, when we want to stop at a
+matching internal ket rather than at the end.
+
+If we are dealing with a backward assertion we have to find out the maximum
+amount to move back, and set up each alternative appropriately. */
+
+if (*this_start_code == OP_ASSERTBACK || *this_start_code == OP_ASSERTBACK_NOT)
+ {
+ size_t max_back = 0;
+ size_t gone_back;
+
+ end_code = this_start_code;
+ do
+ {
+ size_t back = (size_t)GET(end_code, 2+LINK_SIZE);
+ if (back > max_back) max_back = back;
+ end_code += GET(end_code, 1);
+ }
+ while (*end_code == OP_ALT);
+
+ /* If we can't go back the amount required for the longest lookbehind
+ pattern, go back as far as we can; some alternatives may still be viable. */
+
+#ifdef SUPPORT_UNICODE
+ /* In character mode we have to step back character by character */
+
+ if (utf)
+ {
+ for (gone_back = 0; gone_back < max_back; gone_back++)
+ {
+ if (current_subject <= start_subject) break;
+ current_subject--;
+ ACROSSCHAR(current_subject > start_subject, current_subject,
+ current_subject--);
+ }
+ }
+ else
+#endif
+
+ /* In byte-mode we can do this quickly. */
+
+ {
+ size_t current_offset = (size_t)(current_subject - start_subject);
+ gone_back = (current_offset < max_back)? current_offset : max_back;
+ current_subject -= gone_back;
+ }
+
+ /* Save the earliest consulted character */
+
+ if (current_subject < mb->start_used_ptr)
+ mb->start_used_ptr = current_subject;
+
+ /* Now we can process the individual branches. There will be an OP_REVERSE at
+ the start of each branch, except when the length of the branch is zero. */
+
+ end_code = this_start_code;
+ do
+ {
+ uint32_t revlen = (end_code[1+LINK_SIZE] == OP_REVERSE)? 1 + LINK_SIZE : 0;
+ size_t back = (revlen == 0)? 0 : (size_t)GET(end_code, 2+LINK_SIZE);
+ if (back <= gone_back)
+ {
+ int bstate = (int)(end_code - start_code + 1 + LINK_SIZE + revlen);
+ ADD_NEW_DATA(-bstate, 0, (int)(gone_back - back));
+ }
+ end_code += GET(end_code, 1);
+ }
+ while (*end_code == OP_ALT);
+ }
+
+/* This is the code for a "normal" subpattern (not a backward assertion). The
+start of a whole pattern is always one of these. If we are at the top level,
+we may be asked to restart matching from the same point that we reached for a
+previous partial match. We still have to scan through the top-level branches to
+find the end state. */
+
+else
+ {
+ end_code = this_start_code;
+
+ /* Restarting */
+
+ if (rlevel == 1 && (mb->moptions & PCRE2_DFA_RESTART) != 0)
+ {
+ do { end_code += GET(end_code, 1); } while (*end_code == OP_ALT);
+ new_count = workspace[1];
+ if (!workspace[0])
+ memcpy(new_states, active_states, (size_t)new_count * sizeof(stateblock));
+ }
+
+ /* Not restarting */
+
+ else
+ {
+ int length = 1 + LINK_SIZE +
+ ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA ||
+ *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS)
+ ? IMM2_SIZE:0);
+ do
+ {
+ ADD_NEW((int)(end_code - start_code + length), 0);
+ end_code += GET(end_code, 1);
+ length = 1 + LINK_SIZE;
+ }
+ while (*end_code == OP_ALT);
+ }
+ }
+
+workspace[0] = 0; /* Bit indicating which vector is current */
+
+/* Loop for scanning the subject */
+
+ptr = current_subject;
+for (;;)
+ {
+ int i, j;
+ int clen, dlen;
+ uint32_t c, d;
+ int forced_fail = 0;
+ BOOL partial_newline = FALSE;
+ BOOL could_continue = reset_could_continue;
+ reset_could_continue = FALSE;
+
+ if (ptr > mb->last_used_ptr) mb->last_used_ptr = ptr;
+
+ /* Make the new state list into the active state list and empty the
+ new state list. */
+
+ temp_states = active_states;
+ active_states = new_states;
+ new_states = temp_states;
+ active_count = new_count;
+ new_count = 0;
+
+ workspace[0] ^= 1; /* Remember for the restarting feature */
+ workspace[1] = active_count;
+
+ /* Set the pointers for adding new states */
+
+ next_active_state = active_states + active_count;
+ next_new_state = new_states;
+
+ /* Load the current character from the subject outside the loop, as many
+ different states may want to look at it, and we assume that at least one
+ will. */
+
+ if (ptr < end_subject)
+ {
+ clen = 1; /* Number of data items in the character */
+#ifdef SUPPORT_UNICODE
+ GETCHARLENTEST(c, ptr, clen);
+#else
+ c = *ptr;
+#endif /* SUPPORT_UNICODE */
+ }
+ else
+ {
+ clen = 0; /* This indicates the end of the subject */
+ c = NOTACHAR; /* This value should never actually be used */
+ }
+
+ /* Scan up the active states and act on each one. The result of an action
+ may be to add more states to the currently active list (e.g. on hitting a
+ parenthesis) or it may be to put states on the new list, for considering
+ when we move the character pointer on. */
+
+ for (i = 0; i < active_count; i++)
+ {
+ stateblock *current_state = active_states + i;
+ BOOL caseless = FALSE;
+ PCRE2_SPTR code;
+ uint32_t codevalue;
+ int state_offset = current_state->offset;
+ int rrc;
+ int count;
+
+ /* A negative offset is a special case meaning "hold off going to this
+ (negated) state until the number of characters in the data field have
+ been skipped". If the could_continue flag was passed over from a previous
+ state, arrange for it to passed on. */
+
+ if (state_offset < 0)
+ {
+ if (current_state->data > 0)
+ {
+ ADD_NEW_DATA(state_offset, current_state->count,
+ current_state->data - 1);
+ if (could_continue) reset_could_continue = TRUE;
+ continue;
+ }
+ else
+ {
+ current_state->offset = state_offset = -state_offset;
+ }
+ }
+
+ /* Check for a duplicate state with the same count, and skip if found.
+ See the note at the head of this module about the possibility of improving
+ performance here. */
+
+ for (j = 0; j < i; j++)
+ {
+ if (active_states[j].offset == state_offset &&
+ active_states[j].count == current_state->count)
+ goto NEXT_ACTIVE_STATE;
+ }
+
+ /* The state offset is the offset to the opcode */
+
+ code = start_code + state_offset;
+ codevalue = *code;
+
+ /* If this opcode inspects a character, but we are at the end of the
+ subject, remember the fact for use when testing for a partial match. */
+
+ if (clen == 0 && poptable[codevalue] != 0)
+ could_continue = TRUE;
+
+ /* If this opcode is followed by an inline character, load it. It is
+ tempting to test for the presence of a subject character here, but that
+ is wrong, because sometimes zero repetitions of the subject are
+ permitted.
+
+ We also use this mechanism for opcodes such as OP_TYPEPLUS that take an
+ argument that is not a data character - but is always one byte long because
+ the values are small. We have to take special action to deal with \P, \p,
+ \H, \h, \V, \v and \X in this case. To keep the other cases fast, convert
+ these ones to new opcodes. */
+
+ if (coptable[codevalue] > 0)
+ {
+ dlen = 1;
+#ifdef SUPPORT_UNICODE
+ if (utf) { GETCHARLEN(d, (code + coptable[codevalue]), dlen); } else
+#endif /* SUPPORT_UNICODE */
+ d = code[coptable[codevalue]];
+ if (codevalue >= OP_TYPESTAR)
+ {
+ switch(d)
+ {
+ case OP_ANYBYTE: return PCRE2_ERROR_DFA_UITEM;
+ case OP_NOTPROP:
+ case OP_PROP: codevalue += OP_PROP_EXTRA; break;
+ case OP_ANYNL: codevalue += OP_ANYNL_EXTRA; break;
+ case OP_EXTUNI: codevalue += OP_EXTUNI_EXTRA; break;
+ case OP_NOT_HSPACE:
+ case OP_HSPACE: codevalue += OP_HSPACE_EXTRA; break;
+ case OP_NOT_VSPACE:
+ case OP_VSPACE: codevalue += OP_VSPACE_EXTRA; break;
+ default: break;
+ }
+ }
+ }
+ else
+ {
+ dlen = 0; /* Not strictly necessary, but compilers moan */
+ d = NOTACHAR; /* if these variables are not set. */
+ }
+
+
+ /* Now process the individual opcodes */
+
+ switch (codevalue)
+ {
+/* ========================================================================== */
+ /* These cases are never obeyed. This is a fudge that causes a compile-
+ time error if the vectors coptable or poptable, which are indexed by
+ opcode, are not the correct length. It seems to be the only way to do
+ such a check at compile time, as the sizeof() operator does not work
+ in the C preprocessor. */
+
+ case OP_TABLE_LENGTH:
+ case OP_TABLE_LENGTH +
+ ((sizeof(coptable) == OP_TABLE_LENGTH) &&
+ (sizeof(poptable) == OP_TABLE_LENGTH)):
+ return 0;
+
+/* ========================================================================== */
+ /* Reached a closing bracket. If not at the end of the pattern, carry
+ on with the next opcode. For repeating opcodes, also add the repeat
+ state. Note that KETRPOS will always be encountered at the end of the
+ subpattern, because the possessive subpattern repeats are always handled
+ using recursive calls. Thus, it never adds any new states.
+
+ At the end of the (sub)pattern, unless we have an empty string and
+ PCRE2_NOTEMPTY is set, or PCRE2_NOTEMPTY_ATSTART is set and we are at the
+ start of the subject, save the match data, shifting up all previous
+ matches so we always have the longest first. */
+
+ case OP_KET:
+ case OP_KETRMIN:
+ case OP_KETRMAX:
+ case OP_KETRPOS:
+ if (code != end_code)
+ {
+ ADD_ACTIVE(state_offset + 1 + LINK_SIZE, 0);
+ if (codevalue != OP_KET)
+ {
+ ADD_ACTIVE(state_offset - (int)GET(code, 1), 0);
+ }
+ }
+ else
+ {
+ if (ptr > current_subject ||
+ ((mb->moptions & PCRE2_NOTEMPTY) == 0 &&
+ ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) == 0 ||
+ current_subject > start_subject + mb->start_offset)))
+ {
+ if (match_count < 0) match_count = (offsetcount >= 2)? 1 : 0;
+ else if (match_count > 0 && ++match_count * 2 > (int)offsetcount)
+ match_count = 0;
+ count = ((match_count == 0)? (int)offsetcount : match_count * 2) - 2;
+ if (count > 0) (void)memmove(offsets + 2, offsets,
+ (size_t)count * sizeof(PCRE2_SIZE));
+ if (offsetcount >= 2)
+ {
+ offsets[0] = (PCRE2_SIZE)(current_subject - start_subject);
+ offsets[1] = (PCRE2_SIZE)(ptr - start_subject);
+ }
+ if ((mb->moptions & PCRE2_DFA_SHORTEST) != 0) return match_count;
+ }
+ }
+ break;
+
+/* ========================================================================== */
+ /* These opcodes add to the current list of states without looking
+ at the current character. */
+
+ /*-----------------------------------------------------------------*/
+ case OP_ALT:
+ do { code += GET(code, 1); } while (*code == OP_ALT);
+ ADD_ACTIVE((int)(code - start_code), 0);
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_BRA:
+ case OP_SBRA:
+ do
+ {
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0);
+ code += GET(code, 1);
+ }
+ while (*code == OP_ALT);
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_CBRA:
+ case OP_SCBRA:
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE + IMM2_SIZE), 0);
+ code += GET(code, 1);
+ while (*code == OP_ALT)
+ {
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0);
+ code += GET(code, 1);
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ ADD_ACTIVE(state_offset + 1, 0);
+ code += 1 + GET(code, 2);
+ while (*code == OP_ALT) code += GET(code, 1);
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0);
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_SKIPZERO:
+ code += 1 + GET(code, 2);
+ while (*code == OP_ALT) code += GET(code, 1);
+ ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0);
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_CIRC:
+ if (ptr == start_subject && (mb->moptions & PCRE2_NOTBOL) == 0)
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_CIRCM:
+ if ((ptr == start_subject && (mb->moptions & PCRE2_NOTBOL) == 0) ||
+ ((ptr != end_subject || (mb->poptions & PCRE2_ALT_CIRCUMFLEX) != 0 )
+ && WAS_NEWLINE(ptr)))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EOD:
+ if (ptr >= end_subject)
+ {
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ return PCRE2_ERROR_PARTIAL;
+ else { ADD_ACTIVE(state_offset + 1, 0); }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_SOD:
+ if (ptr == start_subject) { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_SOM:
+ if (ptr == start_subject + start_offset) { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+
+/* ========================================================================== */
+ /* These opcodes inspect the next subject character, and sometimes
+ the previous one as well, but do not have an argument. The variable
+ clen contains the length of the current character and is zero if we are
+ at the end of the subject. */
+
+ /*-----------------------------------------------------------------*/
+ case OP_ANY:
+ if (clen > 0 && !IS_NEWLINE(ptr))
+ {
+ if (ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else
+ {
+ ADD_NEW(state_offset + 1, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_ALLANY:
+ if (clen > 0)
+ { ADD_NEW(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EODN:
+ if (clen == 0 || (IS_NEWLINE(ptr) && ptr == end_subject - mb->nllen))
+ {
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ return PCRE2_ERROR_PARTIAL;
+ ADD_ACTIVE(state_offset + 1, 0);
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_DOLL:
+ if ((mb->moptions & PCRE2_NOTEOL) == 0)
+ {
+ if (clen == 0 && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ could_continue = TRUE;
+ else if (clen == 0 ||
+ ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr) &&
+ (ptr == end_subject - mb->nllen)
+ ))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ else if (ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ {
+ reset_could_continue = TRUE;
+ ADD_NEW_DATA(-(state_offset + 1), 0, 1);
+ }
+ else could_continue = partial_newline = TRUE;
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_DOLLM:
+ if ((mb->moptions & PCRE2_NOTEOL) == 0)
+ {
+ if (clen == 0 && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ could_continue = TRUE;
+ else if (clen == 0 ||
+ ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr)))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ else if (ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ {
+ reset_could_continue = TRUE;
+ ADD_NEW_DATA(-(state_offset + 1), 0, 1);
+ }
+ else could_continue = partial_newline = TRUE;
+ }
+ }
+ else if (IS_NEWLINE(ptr))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+
+ case OP_DIGIT:
+ case OP_WHITESPACE:
+ case OP_WORDCHAR:
+ if (clen > 0 && c < 256 &&
+ ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0)
+ { ADD_NEW(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_NOT_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ if (clen > 0 && (c >= 256 ||
+ ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0))
+ { ADD_NEW(state_offset + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_WORD_BOUNDARY:
+ {
+ int left_word, right_word;
+
+ if (ptr > start_subject)
+ {
+ PCRE2_SPTR temp = ptr - 1;
+ if (temp < mb->start_used_ptr) mb->start_used_ptr = temp;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf) { BACKCHAR(temp); }
+#endif
+ GETCHARTEST(d, temp);
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (d == '_') left_word = TRUE; else
+ {
+ uint32_t cat = UCD_CATEGORY(d);
+ left_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif
+ left_word = d < 256 && (ctypes[d] & ctype_word) != 0;
+ }
+ else left_word = FALSE;
+
+ if (clen > 0)
+ {
+ if (ptr >= mb->last_used_ptr)
+ {
+ PCRE2_SPTR temp = ptr + 1;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf) { FORWARDCHARTEST(temp, mb->end_subject); }
+#endif
+ mb->last_used_ptr = temp;
+ }
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (c == '_') right_word = TRUE; else
+ {
+ uint32_t cat = UCD_CATEGORY(c);
+ right_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif
+ right_word = c < 256 && (ctypes[c] & ctype_word) != 0;
+ }
+ else right_word = FALSE;
+
+ if ((left_word == right_word) == (codevalue == OP_NOT_WORD_BOUNDARY))
+ { ADD_ACTIVE(state_offset + 1, 0); }
+ }
+ break;
+
+
+ /*-----------------------------------------------------------------*/
+ /* Check the next character by Unicode property. We will get here only
+ if the support is in the binary; otherwise a compile-time error occurs.
+ */
+
+#ifdef SUPPORT_UNICODE
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (clen > 0)
+ {
+ BOOL OK;
+ const uint32_t *cp;
+ const ucd_record * prop = GET_UCD(c);
+ switch(code[1])
+ {
+ case PT_ANY:
+ OK = TRUE;
+ break;
+
+ case PT_LAMP:
+ OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt;
+ break;
+
+ case PT_GC:
+ OK = PRIV(ucp_gentype)[prop->chartype] == code[2];
+ break;
+
+ case PT_PC:
+ OK = prop->chartype == code[2];
+ break;
+
+ case PT_SC:
+ OK = prop->script == code[2];
+ break;
+
+ case PT_SCX:
+ OK = (prop->script == code[2] ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), code[2]) != 0);
+ break;
+
+ /* These are specials for combination cases. */
+
+ case PT_ALNUM:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE;
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + code[2];
+ for (;;)
+ {
+ if (c < *cp) { OK = FALSE; break; }
+ if (c == *cp++) { OK = TRUE; break; }
+ }
+ break;
+
+ case PT_UCNC:
+ OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
+ c >= 0xe000;
+ break;
+
+ case PT_BIDICL:
+ OK = UCD_BIDICLASS(c) == code[2];
+ break;
+
+ case PT_BOOL:
+ OK = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), code[2]) != 0;
+ break;
+
+ /* Should never occur, but keep compilers from grumbling. */
+
+ default:
+ OK = codevalue != OP_PROP;
+ break;
+ }
+
+ if (OK == (codevalue == OP_PROP)) { ADD_NEW(state_offset + 3, 0); }
+ }
+ break;
+#endif
+
+
+
+/* ========================================================================== */
+ /* These opcodes likewise inspect the subject character, but have an
+ argument that is not a data character. It is one of these opcodes:
+ OP_ANY, OP_ALLANY, OP_DIGIT, OP_NOT_DIGIT, OP_WHITESPACE, OP_NOT_SPACE,
+ OP_WORDCHAR, OP_NOT_WORDCHAR. The value is loaded into d. */
+
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (count > 0 && codevalue == OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW(state_offset, count);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSQUERY:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (codevalue == OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset + 2, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPOSSTAR:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (codevalue == OP_TYPEPOSSTAR)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_TYPEEXACT:
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + 1 + IMM2_SIZE + 1, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEPOSUPTO:
+ ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0);
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ if (d == OP_ANY && ptr + 1 >= mb->end_subject &&
+ (mb->moptions & (PCRE2_PARTIAL_HARD)) != 0 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ c == NLBLOCK->nl[0])
+ {
+ could_continue = partial_newline = TRUE;
+ }
+ else if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) ||
+ (c < 256 &&
+ (d != OP_ANY || !IS_NEWLINE(ptr)) &&
+ ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0))
+ {
+ if (codevalue == OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + 2 + IMM2_SIZE, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+/* ========================================================================== */
+ /* These are virtual opcodes that are used when something like
+ OP_TYPEPLUS has OP_PROP, OP_NOTPROP, OP_ANYNL, or OP_EXTUNI as its
+ argument. It keeps the code above fast for the other cases. The argument
+ is in the d variable. */
+
+#ifdef SUPPORT_UNICODE
+ case OP_PROP_EXTRA + OP_TYPEPLUS:
+ case OP_PROP_EXTRA + OP_TYPEMINPLUS:
+ case OP_PROP_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 4, 0); }
+ if (clen > 0)
+ {
+ BOOL OK;
+ const uint32_t *cp;
+ const ucd_record * prop = GET_UCD(c);
+ switch(code[2])
+ {
+ case PT_ANY:
+ OK = TRUE;
+ break;
+
+ case PT_LAMP:
+ OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt;
+ break;
+
+ case PT_GC:
+ OK = PRIV(ucp_gentype)[prop->chartype] == code[3];
+ break;
+
+ case PT_PC:
+ OK = prop->chartype == code[3];
+ break;
+
+ case PT_SC:
+ OK = prop->script == code[3];
+ break;
+
+ case PT_SCX:
+ OK = (prop->script == code[3] ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), code[3]) != 0);
+ break;
+
+ /* These are specials for combination cases. */
+
+ case PT_ALNUM:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE;
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + code[3];
+ for (;;)
+ {
+ if (c < *cp) { OK = FALSE; break; }
+ if (c == *cp++) { OK = TRUE; break; }
+ }
+ break;
+
+ case PT_UCNC:
+ OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
+ c >= 0xe000;
+ break;
+
+ case PT_BIDICL:
+ OK = UCD_BIDICLASS(c) == code[3];
+ break;
+
+ case PT_BOOL:
+ OK = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), code[3]) != 0;
+ break;
+
+ /* Should never occur, but keep compilers from grumbling. */
+
+ default:
+ OK = codevalue != OP_PROP;
+ break;
+ }
+
+ if (OK == (d == OP_PROP))
+ {
+ if (count > 0 && codevalue == OP_PROP_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW(state_offset, count);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EXTUNI_EXTRA + OP_TYPEPLUS:
+ case OP_EXTUNI_EXTRA + OP_TYPEMINPLUS:
+ case OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ int ncount = 0;
+ if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
+ count++;
+ ADD_NEW_DATA(-state_offset, count, ncount);
+ }
+ break;
+#endif
+
+ /*-----------------------------------------------------------------*/
+ case OP_ANYNL_EXTRA + OP_TYPEPLUS:
+ case OP_ANYNL_EXTRA + OP_TYPEMINPLUS:
+ case OP_ANYNL_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ int ncount = 0;
+ switch (c)
+ {
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ goto ANYNL01;
+
+ case CHAR_CR:
+ if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1;
+ /* Fall through */
+
+ ANYNL01:
+ case CHAR_LF:
+ if (count > 0 && codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW_DATA(-state_offset, count, ncount);
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_VSPACE_EXTRA + OP_TYPEPLUS:
+ case OP_VSPACE_EXTRA + OP_TYPEMINPLUS:
+ case OP_VSPACE_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+
+ if (OK == (d == OP_VSPACE))
+ {
+ if (count > 0 && codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW_DATA(-state_offset, count, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_HSPACE_EXTRA + OP_TYPEPLUS:
+ case OP_HSPACE_EXTRA + OP_TYPEMINPLUS:
+ case OP_HSPACE_EXTRA + OP_TYPEPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); }
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ HSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+
+ if (OK == (d == OP_HSPACE))
+ {
+ if (count > 0 && codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW_DATA(-state_offset, count, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+#ifdef SUPPORT_UNICODE
+ case OP_PROP_EXTRA + OP_TYPEQUERY:
+ case OP_PROP_EXTRA + OP_TYPEMINQUERY:
+ case OP_PROP_EXTRA + OP_TYPEPOSQUERY:
+ count = 4;
+ goto QS1;
+
+ case OP_PROP_EXTRA + OP_TYPESTAR:
+ case OP_PROP_EXTRA + OP_TYPEMINSTAR:
+ case OP_PROP_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS1:
+
+ ADD_ACTIVE(state_offset + 4, 0);
+ if (clen > 0)
+ {
+ BOOL OK;
+ const uint32_t *cp;
+ const ucd_record * prop = GET_UCD(c);
+ switch(code[2])
+ {
+ case PT_ANY:
+ OK = TRUE;
+ break;
+
+ case PT_LAMP:
+ OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt;
+ break;
+
+ case PT_GC:
+ OK = PRIV(ucp_gentype)[prop->chartype] == code[3];
+ break;
+
+ case PT_PC:
+ OK = prop->chartype == code[3];
+ break;
+
+ case PT_SC:
+ OK = prop->script == code[3];
+ break;
+
+ case PT_SCX:
+ OK = (prop->script == code[3] ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), code[3]) != 0);
+ break;
+
+ /* These are specials for combination cases. */
+
+ case PT_ALNUM:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE;
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + code[3];
+ for (;;)
+ {
+ if (c < *cp) { OK = FALSE; break; }
+ if (c == *cp++) { OK = TRUE; break; }
+ }
+ break;
+
+ case PT_UCNC:
+ OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
+ c >= 0xe000;
+ break;
+
+ case PT_BIDICL:
+ OK = UCD_BIDICLASS(c) == code[3];
+ break;
+
+ case PT_BOOL:
+ OK = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), code[3]) != 0;
+ break;
+
+ /* Should never occur, but keep compilers from grumbling. */
+
+ default:
+ OK = codevalue != OP_PROP;
+ break;
+ }
+
+ if (OK == (d == OP_PROP))
+ {
+ if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_PROP_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset + count, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EXTUNI_EXTRA + OP_TYPEQUERY:
+ case OP_EXTUNI_EXTRA + OP_TYPEMINQUERY:
+ case OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY:
+ count = 2;
+ goto QS2;
+
+ case OP_EXTUNI_EXTRA + OP_TYPESTAR:
+ case OP_EXTUNI_EXTRA + OP_TYPEMINSTAR:
+ case OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS2:
+
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ int ncount = 0;
+ if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ (void)PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
+ ADD_NEW_DATA(-(state_offset + count), 0, ncount);
+ }
+ break;
+#endif
+
+ /*-----------------------------------------------------------------*/
+ case OP_ANYNL_EXTRA + OP_TYPEQUERY:
+ case OP_ANYNL_EXTRA + OP_TYPEMINQUERY:
+ case OP_ANYNL_EXTRA + OP_TYPEPOSQUERY:
+ count = 2;
+ goto QS3;
+
+ case OP_ANYNL_EXTRA + OP_TYPESTAR:
+ case OP_ANYNL_EXTRA + OP_TYPEMINSTAR:
+ case OP_ANYNL_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS3:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ int ncount = 0;
+ switch (c)
+ {
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ goto ANYNL02;
+
+ case CHAR_CR:
+ if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1;
+ /* Fall through */
+
+ ANYNL02:
+ case CHAR_LF:
+ if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW_DATA(-(state_offset + (int)count), 0, ncount);
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_VSPACE_EXTRA + OP_TYPEQUERY:
+ case OP_VSPACE_EXTRA + OP_TYPEMINQUERY:
+ case OP_VSPACE_EXTRA + OP_TYPEPOSQUERY:
+ count = 2;
+ goto QS4;
+
+ case OP_VSPACE_EXTRA + OP_TYPESTAR:
+ case OP_VSPACE_EXTRA + OP_TYPEMINSTAR:
+ case OP_VSPACE_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS4:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+ if (OK == (d == OP_VSPACE))
+ {
+ if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW_DATA(-(state_offset + (int)count), 0, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_HSPACE_EXTRA + OP_TYPEQUERY:
+ case OP_HSPACE_EXTRA + OP_TYPEMINQUERY:
+ case OP_HSPACE_EXTRA + OP_TYPEPOSQUERY:
+ count = 2;
+ goto QS5;
+
+ case OP_HSPACE_EXTRA + OP_TYPESTAR:
+ case OP_HSPACE_EXTRA + OP_TYPEMINSTAR:
+ case OP_HSPACE_EXTRA + OP_TYPEPOSSTAR:
+ count = 0;
+
+ QS5:
+ ADD_ACTIVE(state_offset + 2, 0);
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ HSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+
+ if (OK == (d == OP_HSPACE))
+ {
+ if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSSTAR ||
+ codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW_DATA(-(state_offset + (int)count), 0, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+#ifdef SUPPORT_UNICODE
+ case OP_PROP_EXTRA + OP_TYPEEXACT:
+ case OP_PROP_EXTRA + OP_TYPEUPTO:
+ case OP_PROP_EXTRA + OP_TYPEMINUPTO:
+ case OP_PROP_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_PROP_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 1 + IMM2_SIZE + 3, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ BOOL OK;
+ const uint32_t *cp;
+ const ucd_record * prop = GET_UCD(c);
+ switch(code[1 + IMM2_SIZE + 1])
+ {
+ case PT_ANY:
+ OK = TRUE;
+ break;
+
+ case PT_LAMP:
+ OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt;
+ break;
+
+ case PT_GC:
+ OK = PRIV(ucp_gentype)[prop->chartype] == code[1 + IMM2_SIZE + 2];
+ break;
+
+ case PT_PC:
+ OK = prop->chartype == code[1 + IMM2_SIZE + 2];
+ break;
+
+ case PT_SC:
+ OK = prop->script == code[1 + IMM2_SIZE + 2];
+ break;
+
+ case PT_SCX:
+ OK = (prop->script == code[1 + IMM2_SIZE + 2] ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop),
+ code[1 + IMM2_SIZE + 2]) != 0);
+ break;
+
+ /* These are specials for combination cases. */
+
+ case PT_ALNUM:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ c == CHAR_UNDERSCORE;
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + code[1 + IMM2_SIZE + 2];
+ for (;;)
+ {
+ if (c < *cp) { OK = FALSE; break; }
+ if (c == *cp++) { OK = TRUE; break; }
+ }
+ break;
+
+ case PT_UCNC:
+ OK = c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT || (c >= 0xa0 && c <= 0xd7ff) ||
+ c >= 0xe000;
+ break;
+
+ case PT_BIDICL:
+ OK = UCD_BIDICLASS(c) == code[1 + IMM2_SIZE + 2];
+ break;
+
+ case PT_BOOL:
+ OK = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), code[1 + IMM2_SIZE + 2]) != 0;
+ break;
+
+ /* Should never occur, but keep compilers from grumbling. */
+
+ default:
+ OK = codevalue != OP_PROP;
+ break;
+ }
+
+ if (OK == (d == OP_PROP))
+ {
+ if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + 1 + IMM2_SIZE + 3, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EXTUNI_EXTRA + OP_TYPEEXACT:
+ case OP_EXTUNI_EXTRA + OP_TYPEUPTO:
+ case OP_EXTUNI_EXTRA + OP_TYPEMINUPTO:
+ case OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_EXTUNI_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ PCRE2_SPTR nptr;
+ int ncount = 0;
+ if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject, end_subject, utf,
+ &ncount);
+ if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ reset_could_continue = TRUE;
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); }
+ else
+ { ADD_NEW_DATA(-state_offset, count, ncount); }
+ }
+ break;
+#endif
+
+ /*-----------------------------------------------------------------*/
+ case OP_ANYNL_EXTRA + OP_TYPEEXACT:
+ case OP_ANYNL_EXTRA + OP_TYPEUPTO:
+ case OP_ANYNL_EXTRA + OP_TYPEMINUPTO:
+ case OP_ANYNL_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_ANYNL_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ int ncount = 0;
+ switch (c)
+ {
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ goto ANYNL03;
+
+ case CHAR_CR:
+ if (ptr + 1 < end_subject && UCHAR21TEST(ptr + 1) == CHAR_LF) ncount = 1;
+ /* Fall through */
+
+ ANYNL03:
+ case CHAR_LF:
+ if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); }
+ else
+ { ADD_NEW_DATA(-state_offset, count, ncount); }
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_VSPACE_EXTRA + OP_TYPEEXACT:
+ case OP_VSPACE_EXTRA + OP_TYPEUPTO:
+ case OP_VSPACE_EXTRA + OP_TYPEMINUPTO:
+ case OP_VSPACE_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_VSPACE_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ VSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ }
+
+ if (OK == (d == OP_VSPACE))
+ {
+ if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); }
+ else
+ { ADD_NEW_DATA(-state_offset, count, 0); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_HSPACE_EXTRA + OP_TYPEEXACT:
+ case OP_HSPACE_EXTRA + OP_TYPEUPTO:
+ case OP_HSPACE_EXTRA + OP_TYPEMINUPTO:
+ case OP_HSPACE_EXTRA + OP_TYPEPOSUPTO:
+ if (codevalue != OP_HSPACE_EXTRA + OP_TYPEEXACT)
+ { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); }
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ BOOL OK;
+ switch (c)
+ {
+ HSPACE_CASES:
+ OK = TRUE;
+ break;
+
+ default:
+ OK = FALSE;
+ break;
+ }
+
+ if (OK == (d == OP_HSPACE))
+ {
+ if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); }
+ else
+ { ADD_NEW_DATA(-state_offset, count, 0); }
+ }
+ }
+ break;
+
+/* ========================================================================== */
+ /* These opcodes are followed by a character that is usually compared
+ to the current subject character; it is loaded into d. We still get
+ here even if there is no subject character, because in some cases zero
+ repetitions are permitted. */
+
+ /*-----------------------------------------------------------------*/
+ case OP_CHAR:
+ if (clen > 0 && c == d) { ADD_NEW(state_offset + dlen + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_CHARI:
+ if (clen == 0) break;
+
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp)
+ {
+ if (c == d) { ADD_NEW(state_offset + dlen + 1, 0); } else
+ {
+ unsigned int othercase;
+ if (c < 128)
+ othercase = fcc[c];
+ else
+ othercase = UCD_OTHERCASE(c);
+ if (d == othercase) { ADD_NEW(state_offset + dlen + 1, 0); }
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ /* Not UTF or UCP mode */
+ {
+ if (TABLE_GET(c, lcc, c) == TABLE_GET(d, lcc, d))
+ { ADD_NEW(state_offset + 2, 0); }
+ }
+ break;
+
+
+#ifdef SUPPORT_UNICODE
+ /*-----------------------------------------------------------------*/
+ /* This is a tricky one because it can match more than one character.
+ Find out how many characters to skip, and then set up a negative state
+ to wait for them to pass before continuing. */
+
+ case OP_EXTUNI:
+ if (clen > 0)
+ {
+ int ncount = 0;
+ PCRE2_SPTR nptr = PRIV(extuni)(c, ptr + clen, mb->start_subject,
+ end_subject, utf, &ncount);
+ if (nptr >= end_subject && (mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ reset_could_continue = TRUE;
+ ADD_NEW_DATA(-(state_offset + 1), 0, ncount);
+ }
+ break;
+#endif
+
+ /*-----------------------------------------------------------------*/
+ /* This is a tricky like EXTUNI because it too can match more than one
+ character (when CR is followed by LF). In this case, set up a negative
+ state to wait for one character to pass before continuing. */
+
+ case OP_ANYNL:
+ if (clen > 0) switch(c)
+ {
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) break;
+ /* Fall through */
+
+ case CHAR_LF:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+
+ case CHAR_CR:
+ if (ptr + 1 >= end_subject)
+ {
+ ADD_NEW(state_offset + 1, 0);
+ if ((mb->moptions & PCRE2_PARTIAL_HARD) != 0)
+ reset_could_continue = TRUE;
+ }
+ else if (UCHAR21TEST(ptr + 1) == CHAR_LF)
+ {
+ ADD_NEW_DATA(-(state_offset + 1), 0, 1);
+ }
+ else
+ {
+ ADD_NEW(state_offset + 1, 0);
+ }
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_NOT_VSPACE:
+ if (clen > 0) switch(c)
+ {
+ VSPACE_CASES:
+ break;
+
+ default:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_VSPACE:
+ if (clen > 0) switch(c)
+ {
+ VSPACE_CASES:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_NOT_HSPACE:
+ if (clen > 0) switch(c)
+ {
+ HSPACE_CASES:
+ break;
+
+ default:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_HSPACE:
+ if (clen > 0) switch(c)
+ {
+ HSPACE_CASES:
+ ADD_NEW(state_offset + 1, 0);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ /* Match a negated single character casefully. */
+
+ case OP_NOT:
+ if (clen > 0 && c != d) { ADD_NEW(state_offset + dlen + 1, 0); }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ /* Match a negated single character caselessly. */
+
+ case OP_NOTI:
+ if (clen > 0)
+ {
+ uint32_t otherd;
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ if (c != d && c != otherd)
+ { ADD_NEW(state_offset + dlen + 1, 0); }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSPLUSI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTPOSPLUSI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+
+ /* Fall through */
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSPLUS:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(state_offset + dlen + 1, 0); }
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (count > 0 &&
+ (codevalue == OP_POSPLUS || codevalue == OP_NOTPOSPLUS))
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW(state_offset, count);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_POSQUERYI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTPOSQUERYI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+ /* Fall through */
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_POSQUERY:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTPOSQUERY:
+ ADD_ACTIVE(state_offset + dlen + 1, 0);
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (codevalue == OP_POSQUERY || codevalue == OP_NOTPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset + dlen + 1, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_POSSTARI:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPOSSTARI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+ /* Fall through */
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_POSSTAR:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPOSSTAR:
+ ADD_ACTIVE(state_offset + dlen + 1, 0);
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (codevalue == OP_POSSTAR || codevalue == OP_NOTPOSSTAR)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset, 0);
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_EXACTI:
+ case OP_NOTEXACTI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+ /* Fall through */
+ case OP_EXACT:
+ case OP_NOTEXACT:
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_POSUPTOI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTPOSUPTOI:
+ caseless = TRUE;
+ codevalue -= OP_STARI - OP_STAR;
+ /* Fall through */
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_POSUPTO:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTPOSUPTO:
+ ADD_ACTIVE(state_offset + dlen + 1 + IMM2_SIZE, 0);
+ count = current_state->count; /* Number already matched */
+ if (clen > 0)
+ {
+ uint32_t otherd = NOTACHAR;
+ if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf_or_ucp && d >= 128)
+ otherd = UCD_OTHERCASE(d);
+ else
+#endif /* SUPPORT_UNICODE */
+ otherd = TABLE_GET(d, fcc, d);
+ }
+ if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR))
+ {
+ if (codevalue == OP_POSUPTO || codevalue == OP_NOTPOSUPTO)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ if (++count >= (int)GET2(code, 1))
+ { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ }
+ break;
+
+
+/* ========================================================================== */
+ /* These are the class-handling opcodes */
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_XCLASS:
+ {
+ BOOL isinclass = FALSE;
+ int next_state_offset;
+ PCRE2_SPTR ecode;
+
+ /* For a simple class, there is always just a 32-byte table, and we
+ can set isinclass from it. */
+
+ if (codevalue != OP_XCLASS)
+ {
+ ecode = code + 1 + (32 / sizeof(PCRE2_UCHAR));
+ if (clen > 0)
+ {
+ isinclass = (c > 255)? (codevalue == OP_NCLASS) :
+ ((((uint8_t *)(code + 1))[c/8] & (1u << (c&7))) != 0);
+ }
+ }
+
+ /* An extended class may have a table or a list of single characters,
+ ranges, or both, and it may be positive or negative. There's a
+ function that sorts all this out. */
+
+ else
+ {
+ ecode = code + GET(code, 1);
+ if (clen > 0) isinclass = PRIV(xclass)(c, code + 1 + LINK_SIZE, utf);
+ }
+
+ /* At this point, isinclass is set for all kinds of class, and ecode
+ points to the byte after the end of the class. If there is a
+ quantifier, this is where it will be. */
+
+ next_state_offset = (int)(ecode - start_code);
+
+ switch (*ecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPOSSTAR:
+ ADD_ACTIVE(next_state_offset + 1, 0);
+ if (isinclass)
+ {
+ if (*ecode == OP_CRPOSSTAR)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(state_offset, 0);
+ }
+ break;
+
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ count = current_state->count; /* Already matched */
+ if (count > 0) { ADD_ACTIVE(next_state_offset + 1, 0); }
+ if (isinclass)
+ {
+ if (count > 0 && *ecode == OP_CRPOSPLUS)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ count++;
+ ADD_NEW(state_offset, count);
+ }
+ break;
+
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ ADD_ACTIVE(next_state_offset + 1, 0);
+ if (isinclass)
+ {
+ if (*ecode == OP_CRPOSQUERY)
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+ ADD_NEW(next_state_offset + 1, 0);
+ }
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ count = current_state->count; /* Already matched */
+ if (count >= (int)GET2(ecode, 1))
+ { ADD_ACTIVE(next_state_offset + 1 + 2 * IMM2_SIZE, 0); }
+ if (isinclass)
+ {
+ int max = (int)GET2(ecode, 1 + IMM2_SIZE);
+
+ if (*ecode == OP_CRPOSRANGE && count >= (int)GET2(ecode, 1))
+ {
+ active_count--; /* Remove non-match possibility */
+ next_active_state--;
+ }
+
+ if (++count >= max && max != 0) /* Max 0 => no limit */
+ { ADD_NEW(next_state_offset + 1 + 2 * IMM2_SIZE, 0); }
+ else
+ { ADD_NEW(state_offset, count); }
+ }
+ break;
+
+ default:
+ if (isinclass) { ADD_NEW(next_state_offset, 0); }
+ break;
+ }
+ }
+ break;
+
+/* ========================================================================== */
+ /* These are the opcodes for fancy brackets of various kinds. We have
+ to use recursion in order to handle them. The "always failing" assertion
+ (?!) is optimised to OP_FAIL when compiling, so we have to support that,
+ though the other "backtracking verbs" are not supported. */
+
+ case OP_FAIL:
+ forced_fail++; /* Count FAILs for multiple states */
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ PCRE2_SPTR endasscode = code + GET(code, 1);
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1);
+
+ rc = internal_dfa_match(
+ mb, /* static match data */
+ code, /* this subexpression's code */
+ ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc;
+ if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK))
+ { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_COND:
+ case OP_SCOND:
+ {
+ int codelink = (int)GET(code, 1);
+ PCRE2_UCHAR condcode;
+
+ /* Because of the way auto-callout works during compile, a callout item
+ is inserted between OP_COND and an assertion condition. This does not
+ happen for the other conditions. */
+
+ if (code[LINK_SIZE + 1] == OP_CALLOUT
+ || code[LINK_SIZE + 1] == OP_CALLOUT_STR)
+ {
+ PCRE2_SIZE callout_length;
+ rrc = do_callout_dfa(code, offsets, current_subject, ptr, mb,
+ 1 + LINK_SIZE, &callout_length);
+ if (rrc < 0) return rrc; /* Abandon */
+ if (rrc > 0) break; /* Fail this thread */
+ code += callout_length; /* Skip callout data */
+ }
+
+ condcode = code[LINK_SIZE+1];
+
+ /* Back reference conditions and duplicate named recursion conditions
+ are not supported */
+
+ if (condcode == OP_CREF || condcode == OP_DNCREF ||
+ condcode == OP_DNRREF)
+ return PCRE2_ERROR_DFA_UCOND;
+
+ /* The DEFINE condition is always false, and the assertion (?!) is
+ converted to OP_FAIL. */
+
+ if (condcode == OP_FALSE || condcode == OP_FAIL)
+ { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
+
+ /* There is also an always-true condition */
+
+ else if (condcode == OP_TRUE)
+ { ADD_ACTIVE(state_offset + LINK_SIZE + 2, 0); }
+
+ /* The only supported version of OP_RREF is for the value RREF_ANY,
+ which means "test if in any recursion". We can't test for specifically
+ recursed groups. */
+
+ else if (condcode == OP_RREF)
+ {
+ unsigned int value = GET2(code, LINK_SIZE + 2);
+ if (value != RREF_ANY) return PCRE2_ERROR_DFA_UCOND;
+ if (mb->recursive != NULL)
+ { ADD_ACTIVE(state_offset + LINK_SIZE + 2 + IMM2_SIZE, 0); }
+ else { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
+ }
+
+ /* Otherwise, the condition is an assertion */
+
+ else
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ PCRE2_SPTR asscode = code + LINK_SIZE + 1;
+ PCRE2_SPTR endasscode = asscode + GET(asscode, 1);
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1);
+
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ asscode, /* this subexpression's code */
+ ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ if (rc < 0 && rc != PCRE2_ERROR_NOMATCH) return rc;
+ if ((rc >= 0) ==
+ (condcode == OP_ASSERT || condcode == OP_ASSERTBACK))
+ { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); }
+ else
+ { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_RECURSE:
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+ dfa_recursion_info *ri;
+ PCRE2_SPTR callpat = start_code + GET(code, 1);
+ uint32_t recno = (callpat == mb->start_code)? 0 :
+ GET2(callpat, 1 + LINK_SIZE);
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_RSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_RSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_RSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_RSIZE;
+
+ /* Check for repeating a recursion without advancing the subject
+ pointer. This should catch convoluted mutual recursions. (Some simple
+ cases are caught at compile time.) */
+
+ for (ri = mb->recursive; ri != NULL; ri = ri->prevrec)
+ if (recno == ri->group_num && ptr == ri->subject_position)
+ return PCRE2_ERROR_RECURSELOOP;
+
+ /* Remember this recursion and where we started it so as to
+ catch infinite loops. */
+
+ new_recursive.group_num = recno;
+ new_recursive.subject_position = ptr;
+ new_recursive.prevrec = mb->recursive;
+ mb->recursive = &new_recursive;
+
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ callpat, /* this subexpression's code */
+ ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_RSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ rws->free += RWS_RSIZE + RWS_OVEC_RSIZE;
+ mb->recursive = new_recursive.prevrec; /* Done this recursion */
+
+ /* Ran out of internal offsets */
+
+ if (rc == 0) return PCRE2_ERROR_DFA_RECURSE;
+
+ /* For each successful matched substring, set up the next state with a
+ count of characters to skip before trying it. Note that the count is in
+ characters, not bytes. */
+
+ if (rc > 0)
+ {
+ for (rc = rc*2 - 2; rc >= 0; rc -= 2)
+ {
+ PCRE2_SIZE charcount = local_offsets[rc+1] - local_offsets[rc];
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf)
+ {
+ PCRE2_SPTR p = start_subject + local_offsets[rc];
+ PCRE2_SPTR pp = start_subject + local_offsets[rc+1];
+ while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--;
+ }
+#endif
+ if (charcount > 0)
+ {
+ ADD_NEW_DATA(-(state_offset + LINK_SIZE + 1), 0,
+ (int)(charcount - 1));
+ }
+ else
+ {
+ ADD_ACTIVE(state_offset + LINK_SIZE + 1, 0);
+ }
+ }
+ }
+ else if (rc != PCRE2_ERROR_NOMATCH) return rc;
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_BRAPOSZERO:
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ PCRE2_SIZE charcount, matched_count;
+ PCRE2_SPTR local_ptr = ptr;
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+ BOOL allow_zero;
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ if (codevalue == OP_BRAPOSZERO)
+ {
+ allow_zero = TRUE;
+ codevalue = *(++code); /* Codevalue will be one of above BRAs */
+ }
+ else allow_zero = FALSE;
+
+ /* Loop to match the subpattern as many times as possible as if it were
+ a complete pattern. */
+
+ for (matched_count = 0;; matched_count++)
+ {
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ code, /* this subexpression's code */
+ local_ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ /* Failed to match */
+
+ if (rc < 0)
+ {
+ if (rc != PCRE2_ERROR_NOMATCH) return rc;
+ break;
+ }
+
+ /* Matched: break the loop if zero characters matched. */
+
+ charcount = local_offsets[1] - local_offsets[0];
+ if (charcount == 0) break;
+ local_ptr += charcount; /* Advance temporary position ptr */
+ }
+
+ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ /* At this point we have matched the subpattern matched_count
+ times, and local_ptr is pointing to the character after the end of the
+ last match. */
+
+ if (matched_count > 0 || allow_zero)
+ {
+ PCRE2_SPTR end_subpattern = code;
+ int next_state_offset;
+
+ do { end_subpattern += GET(end_subpattern, 1); }
+ while (*end_subpattern == OP_ALT);
+ next_state_offset =
+ (int)(end_subpattern - start_code + LINK_SIZE + 1);
+
+ /* Optimization: if there are no more active states, and there
+ are no new states yet set up, then skip over the subject string
+ right here, to save looping. Otherwise, set up the new state to swing
+ into action when the end of the matched substring is reached. */
+
+ if (i + 1 >= active_count && new_count == 0)
+ {
+ ptr = local_ptr;
+ clen = 0;
+ ADD_NEW(next_state_offset, 0);
+ }
+ else
+ {
+ PCRE2_SPTR p = ptr;
+ PCRE2_SPTR pp = local_ptr;
+ charcount = (PCRE2_SIZE)(pp - p);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf) while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--;
+#endif
+ ADD_NEW_DATA(-next_state_offset, 0, (int)(charcount - 1));
+ }
+ }
+ }
+ break;
+
+ /*-----------------------------------------------------------------*/
+ case OP_ONCE:
+ {
+ int rc;
+ int *local_workspace;
+ PCRE2_SIZE *local_offsets;
+ RWS_anchor *rws = (RWS_anchor *)RWS;
+
+ if (rws->free < RWS_RSIZE + RWS_OVEC_OSIZE)
+ {
+ rc = more_workspace(&rws, RWS_OVEC_OSIZE, mb);
+ if (rc != 0) return rc;
+ RWS = (int *)rws;
+ }
+
+ local_offsets = (PCRE2_SIZE *)(RWS + rws->size - rws->free);
+ local_workspace = ((int *)local_offsets) + RWS_OVEC_OSIZE;
+ rws->free -= RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ code, /* this subexpression's code */
+ ptr, /* where we currently are */
+ (PCRE2_SIZE)(ptr - start_subject), /* start offset */
+ local_offsets, /* offset vector */
+ RWS_OVEC_OSIZE/OVEC_UNIT, /* size of same */
+ local_workspace, /* workspace vector */
+ RWS_RSIZE, /* size of same */
+ rlevel, /* function recursion level */
+ RWS); /* recursion workspace */
+
+ rws->free += RWS_RSIZE + RWS_OVEC_OSIZE;
+
+ if (rc >= 0)
+ {
+ PCRE2_SPTR end_subpattern = code;
+ PCRE2_SIZE charcount = local_offsets[1] - local_offsets[0];
+ int next_state_offset, repeat_state_offset;
+
+ do { end_subpattern += GET(end_subpattern, 1); }
+ while (*end_subpattern == OP_ALT);
+ next_state_offset =
+ (int)(end_subpattern - start_code + LINK_SIZE + 1);
+
+ /* If the end of this subpattern is KETRMAX or KETRMIN, we must
+ arrange for the repeat state also to be added to the relevant list.
+ Calculate the offset, or set -1 for no repeat. */
+
+ repeat_state_offset = (*end_subpattern == OP_KETRMAX ||
+ *end_subpattern == OP_KETRMIN)?
+ (int)(end_subpattern - start_code - GET(end_subpattern, 1)) : -1;
+
+ /* If we have matched an empty string, add the next state at the
+ current character pointer. This is important so that the duplicate
+ checking kicks in, which is what breaks infinite loops that match an
+ empty string. */
+
+ if (charcount == 0)
+ {
+ ADD_ACTIVE(next_state_offset, 0);
+ }
+
+ /* Optimization: if there are no more active states, and there
+ are no new states yet set up, then skip over the subject string
+ right here, to save looping. Otherwise, set up the new state to swing
+ into action when the end of the matched substring is reached. */
+
+ else if (i + 1 >= active_count && new_count == 0)
+ {
+ ptr += charcount;
+ clen = 0;
+ ADD_NEW(next_state_offset, 0);
+
+ /* If we are adding a repeat state at the new character position,
+ we must fudge things so that it is the only current state.
+ Otherwise, it might be a duplicate of one we processed before, and
+ that would cause it to be skipped. */
+
+ if (repeat_state_offset >= 0)
+ {
+ next_active_state = active_states;
+ active_count = 0;
+ i = -1;
+ ADD_ACTIVE(repeat_state_offset, 0);
+ }
+ }
+ else
+ {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (utf)
+ {
+ PCRE2_SPTR p = start_subject + local_offsets[0];
+ PCRE2_SPTR pp = start_subject + local_offsets[1];
+ while (p < pp) if (NOT_FIRSTCU(*p++)) charcount--;
+ }
+#endif
+ ADD_NEW_DATA(-next_state_offset, 0, (int)(charcount - 1));
+ if (repeat_state_offset >= 0)
+ { ADD_NEW_DATA(-repeat_state_offset, 0, (int)(charcount - 1)); }
+ }
+ }
+ else if (rc != PCRE2_ERROR_NOMATCH) return rc;
+ }
+ break;
+
+
+/* ========================================================================== */
+ /* Handle callouts */
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ {
+ PCRE2_SIZE callout_length;
+ rrc = do_callout_dfa(code, offsets, current_subject, ptr, mb, 0,
+ &callout_length);
+ if (rrc < 0) return rrc; /* Abandon */
+ if (rrc == 0)
+ { ADD_ACTIVE(state_offset + (int)callout_length, 0); }
+ }
+ break;
+
+
+/* ========================================================================== */
+ default: /* Unsupported opcode */
+ return PCRE2_ERROR_DFA_UITEM;
+ }
+
+ NEXT_ACTIVE_STATE: continue;
+
+ } /* End of loop scanning active states */
+
+ /* We have finished the processing at the current subject character. If no
+ new states have been set for the next character, we have found all the
+ matches that we are going to find. If partial matching has been requested,
+ check for appropriate conditions.
+
+ The "forced_ fail" variable counts the number of (*F) encountered for the
+ character. If it is equal to the original active_count (saved in
+ workspace[1]) it means that (*F) was found on every active state. In this
+ case we don't want to give a partial match.
+
+ The "could_continue" variable is true if a state could have continued but
+ for the fact that the end of the subject was reached. */
+
+ if (new_count <= 0)
+ {
+ if (could_continue && /* Some could go on, and */
+ forced_fail != workspace[1] && /* Not all forced fail & */
+ ( /* either... */
+ (mb->moptions & PCRE2_PARTIAL_HARD) != 0 /* Hard partial */
+ || /* or... */
+ ((mb->moptions & PCRE2_PARTIAL_SOFT) != 0 && /* Soft partial and */
+ match_count < 0) /* no matches */
+ ) && /* And... */
+ (
+ partial_newline || /* Either partial NL */
+ ( /* or ... */
+ ptr >= end_subject && /* End of subject and */
+ ( /* either */
+ ptr > mb->start_used_ptr || /* Inspected non-empty string */
+ mb->allowemptypartial /* or pattern has lookbehind */
+ ) /* or could match empty */
+ )
+ ))
+ match_count = PCRE2_ERROR_PARTIAL;
+ break; /* Exit from loop along the subject string */
+ }
+
+ /* One or more states are active for the next character. */
+
+ ptr += clen; /* Advance to next subject character */
+ } /* Loop to move along the subject string */
+
+/* Control gets here from "break" a few lines above. If we have a match and
+PCRE2_ENDANCHORED is set, the match fails. */
+
+if (match_count >= 0 &&
+ ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0 &&
+ ptr < end_subject)
+ match_count = PCRE2_ERROR_NOMATCH;
+
+return match_count;
+}
+
+
+
+/*************************************************
+* Match a pattern using the DFA algorithm *
+*************************************************/
+
+/* This function matches a compiled pattern to a subject string, using the
+alternate matching algorithm that finds all matches at once.
+
+Arguments:
+ code points to the compiled pattern
+ subject subject string
+ length length of subject string
+ startoffset where to start matching in the subject
+ options option bits
+ match_data points to a match data structure
+ gcontext points to a match context
+ workspace pointer to workspace
+ wscount size of workspace
+
+Returns: > 0 => number of match offset pairs placed in offsets
+ = 0 => offsets overflowed; longest matches are present
+ -1 => failed to match
+ < -1 => some kind of unexpected problem
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_dfa_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
+ PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
+ pcre2_match_context *mcontext, int *workspace, PCRE2_SIZE wscount)
+{
+int rc;
+int was_zero_terminated = 0;
+
+const pcre2_real_code *re = (const pcre2_real_code *)code;
+
+PCRE2_SPTR start_match;
+PCRE2_SPTR end_subject;
+PCRE2_SPTR bumpalong_limit;
+PCRE2_SPTR req_cu_ptr;
+
+BOOL utf, anchored, startline, firstline;
+BOOL has_first_cu = FALSE;
+BOOL has_req_cu = FALSE;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+PCRE2_SPTR memchr_found_first_cu = NULL;
+PCRE2_SPTR memchr_found_first_cu2 = NULL;
+#endif
+
+PCRE2_UCHAR first_cu = 0;
+PCRE2_UCHAR first_cu2 = 0;
+PCRE2_UCHAR req_cu = 0;
+PCRE2_UCHAR req_cu2 = 0;
+
+const uint8_t *start_bits = NULL;
+
+/* We need to have mb pointing to a match block, because the IS_NEWLINE macro
+is used below, and it expects NLBLOCK to be defined as a pointer. */
+
+pcre2_callout_block cb;
+dfa_match_block actual_match_block;
+dfa_match_block *mb = &actual_match_block;
+
+/* Set up a starting block of memory for use during recursive calls to
+internal_dfa_match(). By putting this on the stack, it minimizes resource use
+in the case when it is not needed. If this is too small, more memory is
+obtained from the heap. At the start of each block is an anchor structure.*/
+
+int base_recursion_workspace[RWS_BASE_SIZE];
+RWS_anchor *rws = (RWS_anchor *)base_recursion_workspace;
+rws->next = NULL;
+rws->size = RWS_BASE_SIZE;
+rws->free = RWS_BASE_SIZE - RWS_ANCHOR_SIZE;
+
+/* Recognize NULL, length 0 as an empty string. */
+
+if (subject == NULL && length == 0) subject = (PCRE2_SPTR)"";
+
+/* Plausibility checks */
+
+if ((options & ~PUBLIC_DFA_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;
+if (re == NULL || subject == NULL || workspace == NULL || match_data == NULL)
+ return PCRE2_ERROR_NULL;
+
+if (length == PCRE2_ZERO_TERMINATED)
+ {
+ length = PRIV(strlen)(subject);
+ was_zero_terminated = 1;
+ }
+
+if (wscount < 20) return PCRE2_ERROR_DFA_WSSIZE;
+if (start_offset > length) return PCRE2_ERROR_BADOFFSET;
+
+/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same
+time. */
+
+if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0 &&
+ ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0)
+ return PCRE2_ERROR_BADOPTION;
+
+/* Invalid UTF support is not available for DFA matching. */
+
+if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0)
+ return PCRE2_ERROR_DFA_UINVALID_UTF;
+
+/* Check that the first field in the block is the magic number. If it is not,
+return with PCRE2_ERROR_BADMAGIC. */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+
+/* Check the code unit width. */
+
+if ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8)
+ return PCRE2_ERROR_BADMODE;
+
+/* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the
+options variable for this function. Users of PCRE2 who are not calling the
+function directly would like to have a way of setting these flags, in the same
+way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with
+constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and
+(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which can now be
+transferred to the options for this function. The bits are guaranteed to be
+adjacent, but do not have the same values. This bit of Boolean trickery assumes
+that the match-time bits are not more significant than the flag bits. If by
+accident this is not the case, a compile-time division by zero error will
+occur. */
+
+#define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET)
+#define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART)
+options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1)));
+#undef FF
+#undef OO
+
+/* If restarting after a partial match, do some sanity checks on the contents
+of the workspace. */
+
+if ((options & PCRE2_DFA_RESTART) != 0)
+ {
+ if ((workspace[0] & (-2)) != 0 || workspace[1] < 1 ||
+ workspace[1] > (int)((wscount - 2)/INTS_PER_STATEBLOCK))
+ return PCRE2_ERROR_DFA_BADRESTART;
+ }
+
+/* Set some local values */
+
+utf = (re->overall_options & PCRE2_UTF) != 0;
+start_match = subject + start_offset;
+end_subject = subject + length;
+req_cu_ptr = start_match - 1;
+anchored = (options & (PCRE2_ANCHORED|PCRE2_DFA_RESTART)) != 0 ||
+ (re->overall_options & PCRE2_ANCHORED) != 0;
+
+/* The "must be at the start of a line" flags are used in a loop when finding
+where to start. */
+
+startline = (re->flags & PCRE2_STARTLINE) != 0;
+firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0;
+bumpalong_limit = end_subject;
+
+/* Initialize and set up the fixed fields in the callout block, with a pointer
+in the match block. */
+
+mb->cb = &cb;
+cb.version = 2;
+cb.subject = subject;
+cb.subject_length = (PCRE2_SIZE)(end_subject - subject);
+cb.callout_flags = 0;
+cb.capture_top = 1; /* No capture support */
+cb.capture_last = 0;
+cb.mark = NULL; /* No (*MARK) support */
+
+/* Get data from the match context, if present, and fill in the remaining
+fields in the match block. It is an error to set an offset limit without
+setting the flag at compile time. */
+
+if (mcontext == NULL)
+ {
+ mb->callout = NULL;
+ mb->memctl = re->memctl;
+ mb->match_limit = PRIV(default_match_context).match_limit;
+ mb->match_limit_depth = PRIV(default_match_context).depth_limit;
+ mb->heap_limit = PRIV(default_match_context).heap_limit;
+ }
+else
+ {
+ if (mcontext->offset_limit != PCRE2_UNSET)
+ {
+ if ((re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0)
+ return PCRE2_ERROR_BADOFFSETLIMIT;
+ bumpalong_limit = subject + mcontext->offset_limit;
+ }
+ mb->callout = mcontext->callout;
+ mb->callout_data = mcontext->callout_data;
+ mb->memctl = mcontext->memctl;
+ mb->match_limit = mcontext->match_limit;
+ mb->match_limit_depth = mcontext->depth_limit;
+ mb->heap_limit = mcontext->heap_limit;
+ }
+
+if (mb->match_limit > re->limit_match)
+ mb->match_limit = re->limit_match;
+
+if (mb->match_limit_depth > re->limit_depth)
+ mb->match_limit_depth = re->limit_depth;
+
+if (mb->heap_limit > re->limit_heap)
+ mb->heap_limit = re->limit_heap;
+
+mb->start_code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) +
+ re->name_count * re->name_entry_size;
+mb->tables = re->tables;
+mb->start_subject = subject;
+mb->end_subject = end_subject;
+mb->start_offset = start_offset;
+mb->allowemptypartial = (re->max_lookbehind > 0) ||
+ (re->flags & PCRE2_MATCH_EMPTY) != 0;
+mb->moptions = options;
+mb->poptions = re->overall_options;
+mb->match_call_count = 0;
+mb->heap_used = 0;
+
+/* Process the \R and newline settings. */
+
+mb->bsr_convention = re->bsr_convention;
+mb->nltype = NLTYPE_FIXED;
+switch(re->newline_convention)
+ {
+ case PCRE2_NEWLINE_CR:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_CR;
+ break;
+
+ case PCRE2_NEWLINE_LF:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_NUL:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NUL;
+ break;
+
+ case PCRE2_NEWLINE_CRLF:
+ mb->nllen = 2;
+ mb->nl[0] = CHAR_CR;
+ mb->nl[1] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_ANY:
+ mb->nltype = NLTYPE_ANY;
+ break;
+
+ case PCRE2_NEWLINE_ANYCRLF:
+ mb->nltype = NLTYPE_ANYCRLF;
+ break;
+
+ default: return PCRE2_ERROR_INTERNAL;
+ }
+
+/* Check a UTF string for validity if required. For 8-bit and 16-bit strings,
+we must also check that a starting offset does not point into the middle of a
+multiunit character. We check only the portion of the subject that is going to
+be inspected during matching - from the offset minus the maximum back reference
+to the given length. This saves time when a small part of a large subject is
+being matched by the use of a starting offset. Note that the maximum lookbehind
+is a number of characters, not code units. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && (options & PCRE2_NO_UTF_CHECK) == 0)
+ {
+ PCRE2_SPTR check_subject = start_match; /* start_match includes offset */
+
+ if (start_offset > 0)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ unsigned int i;
+ if (start_match < end_subject && NOT_FIRSTCU(*start_match))
+ return PCRE2_ERROR_BADUTFOFFSET;
+ for (i = re->max_lookbehind; i > 0 && check_subject > subject; i--)
+ {
+ check_subject--;
+ while (check_subject > subject &&
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ (*check_subject & 0xc0) == 0x80)
+#else /* 16-bit */
+ (*check_subject & 0xfc00) == 0xdc00)
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ check_subject--;
+ }
+#else /* In the 32-bit library, one code unit equals one character. */
+ check_subject -= re->max_lookbehind;
+ if (check_subject < subject) check_subject = subject;
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+ }
+
+ /* Validate the relevant portion of the subject. After an error, adjust the
+ offset to be an absolute offset in the whole string. */
+
+ match_data->rc = PRIV(valid_utf)(check_subject,
+ length - (PCRE2_SIZE)(check_subject - subject), &(match_data->startchar));
+ if (match_data->rc != 0)
+ {
+ match_data->startchar += (PCRE2_SIZE)(check_subject - subject);
+ return match_data->rc;
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* Set up the first code unit to match, if available. If there's no first code
+unit there may be a bitmap of possible first characters. */
+
+if ((re->flags & PCRE2_FIRSTSET) != 0)
+ {
+ has_first_cu = TRUE;
+ first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);
+ if ((re->flags & PCRE2_FIRSTCASELESS) != 0)
+ {
+ first_cu2 = TABLE_GET(first_cu, mb->tables + fcc_offset, first_cu);
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (first_cu > 127 && !utf && (re->overall_options & PCRE2_UCP) != 0)
+ first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu);
+#else
+ if (first_cu > 127 && (utf || (re->overall_options & PCRE2_UCP) != 0))
+ first_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(first_cu);
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+else
+ if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)
+ start_bits = re->start_bitmap;
+
+/* There may be a "last known required code unit" set. */
+
+if ((re->flags & PCRE2_LASTSET) != 0)
+ {
+ has_req_cu = TRUE;
+ req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit);
+ if ((re->flags & PCRE2_LASTCASELESS) != 0)
+ {
+ req_cu2 = TABLE_GET(req_cu, mb->tables + fcc_offset, req_cu);
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (req_cu > 127 && !utf && (re->overall_options & PCRE2_UCP) != 0)
+ req_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(req_cu);
+#else
+ if (req_cu > 127 && (utf || (re->overall_options & PCRE2_UCP) != 0))
+ req_cu2 = (PCRE2_UCHAR)UCD_OTHERCASE(req_cu);
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+
+/* If the match data block was previously used with PCRE2_COPY_MATCHED_SUBJECT,
+free the memory that was obtained. */
+
+if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)
+ {
+ match_data->memctl.free((void *)match_data->subject,
+ match_data->memctl.memory_data);
+ match_data->flags &= ~PCRE2_MD_COPIED_SUBJECT;
+ }
+
+/* Fill in fields that are always returned in the match data. */
+
+match_data->code = re;
+match_data->subject = NULL; /* Default for no match */
+match_data->mark = NULL;
+match_data->matchedby = PCRE2_MATCHEDBY_DFA_INTERPRETER;
+
+/* Call the main matching function, looping for a non-anchored regex after a
+failed match. If not restarting, perform certain optimizations at the start of
+a match. */
+
+for (;;)
+ {
+ /* ----------------- Start of match optimizations ---------------- */
+
+ /* There are some optimizations that avoid running the match if a known
+ starting point is not found, or if a known later code unit is not present.
+ However, there is an option (settable at compile time) that disables
+ these, for testing and for ensuring that all callouts do actually occur.
+ The optimizations must also be avoided when restarting a DFA match. */
+
+ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 &&
+ (options & PCRE2_DFA_RESTART) == 0)
+ {
+ /* If firstline is TRUE, the start of the match is constrained to the first
+ line of a multiline string. That is, the match must be before or at the
+ first newline following the start of matching. Temporarily adjust
+ end_subject so that we stop the optimization scans for a first code unit
+ immediately after the first character of a newline (the first code unit can
+ legitimately be a newline). If the match fails at the newline, later code
+ breaks this loop. */
+
+ if (firstline)
+ {
+ PCRE2_SPTR t = start_match;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (t < end_subject && !IS_NEWLINE(t))
+ {
+ t++;
+ ACROSSCHAR(t < end_subject, t, t++);
+ }
+ }
+ else
+#endif
+ while (t < end_subject && !IS_NEWLINE(t)) t++;
+ end_subject = t;
+ }
+
+ /* Anchored: check the first code unit if one is recorded. This may seem
+ pointless but it can help in detecting a no match case without scanning for
+ the required code unit. */
+
+ if (anchored)
+ {
+ if (has_first_cu || start_bits != NULL)
+ {
+ BOOL ok = start_match < end_subject;
+ if (ok)
+ {
+ PCRE2_UCHAR c = UCHAR21TEST(start_match);
+ ok = has_first_cu && (c == first_cu || c == first_cu2);
+ if (!ok && start_bits != NULL)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ ok = (start_bits[c/8] & (1u << (c&7))) != 0;
+ }
+ }
+ if (!ok) break;
+ }
+ }
+
+ /* Not anchored. Advance to a unique first code unit if there is one. */
+
+ else
+ {
+ if (has_first_cu)
+ {
+ if (first_cu != first_cu2) /* Caseless */
+ {
+ /* In 16-bit and 32_bit modes we have to do our own search, so can
+ look for both cases at once. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ PCRE2_UCHAR smc;
+ while (start_match < end_subject &&
+ (smc = UCHAR21TEST(start_match)) != first_cu &&
+ smc != first_cu2)
+ start_match++;
+#else
+ /* In 8-bit mode, the use of memchr() gives a big speed up, even
+ though we have to call it twice in order to find the earliest
+ occurrence of the code unit in either of its cases. Caching is used
+ to remember the positions of previously found code units. This can
+ make a huge difference when the strings are very long and only one
+ case is actually present. */
+
+ PCRE2_SPTR pp1 = NULL;
+ PCRE2_SPTR pp2 = NULL;
+ PCRE2_SIZE searchlength = end_subject - start_match;
+
+ /* If we haven't got a previously found position for first_cu, or if
+ the current starting position is later, we need to do a search. If
+ the code unit is not found, set it to the end. */
+
+ if (memchr_found_first_cu == NULL ||
+ start_match > memchr_found_first_cu)
+ {
+ pp1 = memchr(start_match, first_cu, searchlength);
+ memchr_found_first_cu = (pp1 == NULL)? end_subject : pp1;
+ }
+
+ /* If the start is before a previously found position, use the
+ previous position, or NULL if a previous search failed. */
+
+ else pp1 = (memchr_found_first_cu == end_subject)? NULL :
+ memchr_found_first_cu;
+
+ /* Do the same thing for the other case. */
+
+ if (memchr_found_first_cu2 == NULL ||
+ start_match > memchr_found_first_cu2)
+ {
+ pp2 = memchr(start_match, first_cu2, searchlength);
+ memchr_found_first_cu2 = (pp2 == NULL)? end_subject : pp2;
+ }
+
+ else pp2 = (memchr_found_first_cu2 == end_subject)? NULL :
+ memchr_found_first_cu2;
+
+ /* Set the start to the end of the subject if neither case was found.
+ Otherwise, use the earlier found point. */
+
+ if (pp1 == NULL)
+ start_match = (pp2 == NULL)? end_subject : pp2;
+ else
+ start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2;
+
+#endif /* 8-bit handling */
+ }
+
+ /* The caseful case is much simpler. */
+
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (start_match < end_subject && UCHAR21TEST(start_match) !=
+ first_cu)
+ start_match++;
+#else /* 8-bit code units */
+ start_match = memchr(start_match, first_cu, end_subject - start_match);
+ if (start_match == NULL) start_match = end_subject;
+#endif
+ }
+
+ /* If we can't find the required code unit, having reached the true end
+ of the subject, break the bumpalong loop, to force a match failure,
+ except when doing partial matching, when we let the next cycle run at
+ the end of the subject. To see why, consider the pattern /(?<=abc)def/,
+ which partially matches "abc", even though the string does not contain
+ the starting character "d". If we have not reached the true end of the
+ subject (PCRE2_FIRSTLINE caused end_subject to be temporarily modified)
+ we also let the cycle run, because the matching string is legitimately
+ allowed to start with the first code unit of a newline. */
+
+ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 &&
+ start_match >= mb->end_subject)
+ break;
+ }
+
+ /* If there's no first code unit, advance to just after a linebreak for a
+ multiline match if required. */
+
+ else if (startline)
+ {
+ if (start_match > mb->start_subject + start_offset)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ {
+ start_match++;
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
+ }
+ }
+ else
+#endif
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ start_match++;
+
+ /* If we have just passed a CR and the newline option is ANY or
+ ANYCRLF, and we are now at a LF, advance the match position by one
+ more code unit. */
+
+ if (start_match[-1] == CHAR_CR &&
+ (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&
+ start_match < end_subject &&
+ UCHAR21TEST(start_match) == CHAR_NL)
+ start_match++;
+ }
+ }
+
+ /* If there's no first code unit or a requirement for a multiline line
+ start, advance to a non-unique first code unit if any have been
+ identified. The bitmap contains only 256 bits. When code units are 16 or
+ 32 bits wide, all code units greater than 254 set the 255 bit. */
+
+ else if (start_bits != NULL)
+ {
+ while (start_match < end_subject)
+ {
+ uint32_t c = UCHAR21TEST(start_match);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ if ((start_bits[c/8] & (1u << (c&7))) != 0) break;
+ start_match++;
+ }
+
+ /* See comment above in first_cu checking about the next line. */
+
+ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0 &&
+ start_match >= mb->end_subject)
+ break;
+ }
+ } /* End of first code unit handling */
+
+ /* Restore fudged end_subject */
+
+ end_subject = mb->end_subject;
+
+ /* The following two optimizations are disabled for partial matching. */
+
+ if ((mb->moptions & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) == 0)
+ {
+ PCRE2_SPTR p;
+
+ /* The minimum matching length is a lower bound; no actual string of that
+ length may actually match the pattern. Although the value is, strictly,
+ in characters, we treat it as code units to avoid spending too much time
+ in this optimization. */
+
+ if (end_subject - start_match < re->minlength) goto NOMATCH_EXIT;
+
+ /* If req_cu is set, we know that that code unit must appear in the
+ subject for the match to succeed. If the first code unit is set, req_cu
+ must be later in the subject; otherwise the test starts at the match
+ point. This optimization can save a huge amount of backtracking in
+ patterns with nested unlimited repeats that aren't going to match.
+ Writing separate code for cased/caseless versions makes it go faster, as
+ does using an autoincrement and backing off on a match. As in the case of
+ the first code unit, using memchr() in the 8-bit library gives a big
+ speed up. Unlike the first_cu check above, we do not need to call
+ memchr() twice in the caseless case because we only need to check for the
+ presence of the character in either case, not find the first occurrence.
+
+ The search can be skipped if the code unit was found later than the
+ current starting point in a previous iteration of the bumpalong loop.
+
+ HOWEVER: when the subject string is very, very long, searching to its end
+ can take a long time, and give bad performance on quite ordinary
+ patterns. This showed up when somebody was matching something like
+ /^\d+C/ on a 32-megabyte string... so we don't do this when the string is
+ sufficiently long, but it's worth searching a lot more for unanchored
+ patterns. */
+
+ p = start_match + (has_first_cu? 1:0);
+ if (has_req_cu && p > req_cu_ptr)
+ {
+ PCRE2_SIZE check_length = end_subject - start_match;
+
+ if (check_length < REQ_CU_MAX ||
+ (!anchored && check_length < REQ_CU_MAX * 1000))
+ {
+ if (req_cu != req_cu2) /* Caseless */
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (p < end_subject)
+ {
+ uint32_t pp = UCHAR21INCTEST(p);
+ if (pp == req_cu || pp == req_cu2) { p--; break; }
+ }
+#else /* 8-bit code units */
+ PCRE2_SPTR pp = p;
+ p = memchr(pp, req_cu, end_subject - pp);
+ if (p == NULL)
+ {
+ p = memchr(pp, req_cu2, end_subject - pp);
+ if (p == NULL) p = end_subject;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
+ }
+
+ /* The caseful case */
+
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (p < end_subject)
+ {
+ if (UCHAR21INCTEST(p) == req_cu) { p--; break; }
+ }
+
+#else /* 8-bit code units */
+ p = memchr(p, req_cu, end_subject - p);
+ if (p == NULL) p = end_subject;
+#endif
+ }
+
+ /* If we can't find the required code unit, break the matching loop,
+ forcing a match failure. */
+
+ if (p >= end_subject) break;
+
+ /* If we have found the required code unit, save the point where we
+ found it, so that we don't search again next time round the loop if
+ the start hasn't passed this code unit yet. */
+
+ req_cu_ptr = p;
+ }
+ }
+ }
+ }
+
+ /* ------------ End of start of match optimizations ------------ */
+
+ /* Give no match if we have passed the bumpalong limit. */
+
+ if (start_match > bumpalong_limit) break;
+
+ /* OK, now we can do the business */
+
+ mb->start_used_ptr = start_match;
+ mb->last_used_ptr = start_match;
+ mb->recursive = NULL;
+
+ rc = internal_dfa_match(
+ mb, /* fixed match data */
+ mb->start_code, /* this subexpression's code */
+ start_match, /* where we currently are */
+ start_offset, /* start offset in subject */
+ match_data->ovector, /* offset vector */
+ (uint32_t)match_data->oveccount * 2, /* actual size of same */
+ workspace, /* workspace vector */
+ (int)wscount, /* size of same */
+ 0, /* function recurse level */
+ base_recursion_workspace); /* initial workspace for recursion */
+
+ /* Anything other than "no match" means we are done, always; otherwise, carry
+ on only if not anchored. */
+
+ if (rc != PCRE2_ERROR_NOMATCH || anchored)
+ {
+ if (rc == PCRE2_ERROR_PARTIAL && match_data->oveccount > 0)
+ {
+ match_data->ovector[0] = (PCRE2_SIZE)(start_match - subject);
+ match_data->ovector[1] = (PCRE2_SIZE)(end_subject - subject);
+ }
+ match_data->leftchar = (PCRE2_SIZE)(mb->start_used_ptr - subject);
+ match_data->rightchar = (PCRE2_SIZE)( mb->last_used_ptr - subject);
+ match_data->startchar = (PCRE2_SIZE)(start_match - subject);
+ match_data->rc = rc;
+
+ if (rc >= 0 &&(options & PCRE2_COPY_MATCHED_SUBJECT) != 0)
+ {
+ length = CU2BYTES(length + was_zero_terminated);
+ match_data->subject = match_data->memctl.malloc(length,
+ match_data->memctl.memory_data);
+ if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy((void *)match_data->subject, subject, length);
+ match_data->flags |= PCRE2_MD_COPIED_SUBJECT;
+ }
+ else
+ {
+ if (rc >= 0 || rc == PCRE2_ERROR_PARTIAL) match_data->subject = subject;
+ }
+ goto EXIT;
+ }
+
+ /* Advance to the next subject character unless we are at the end of a line
+ and firstline is set. */
+
+ if (firstline && IS_NEWLINE(start_match)) break;
+ start_match++;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
+ }
+#endif
+ if (start_match > end_subject) break;
+
+ /* If we have just passed a CR and we are now at a LF, and the pattern does
+ not contain any explicit matches for \r or \n, and the newline option is CRLF
+ or ANY or ANYCRLF, advance the match position by one more character. */
+
+ if (UCHAR21TEST(start_match - 1) == CHAR_CR &&
+ start_match < end_subject &&
+ UCHAR21TEST(start_match) == CHAR_NL &&
+ (re->flags & PCRE2_HASCRORLF) == 0 &&
+ (mb->nltype == NLTYPE_ANY ||
+ mb->nltype == NLTYPE_ANYCRLF ||
+ mb->nllen == 2))
+ start_match++;
+
+ } /* "Bumpalong" loop */
+
+NOMATCH_EXIT:
+rc = PCRE2_ERROR_NOMATCH;
+
+EXIT:
+while (rws->next != NULL)
+ {
+ RWS_anchor *next = rws->next;
+ rws->next = next->next;
+ mb->memctl.free(next, mb->memctl.memory_data);
+ }
+
+return rc;
+}
+
+/* These #undefs are here to enable unity builds with CMake. */
+
+#undef NLBLOCK /* Block containing newline information */
+#undef PSSTART /* Field containing processed string start */
+#undef PSEND /* Field containing processed string end */
+
+/* End of pcre2_dfa_match.c */
diff --git a/contrib/libs/pcre2/src/pcre2_error.c b/contrib/libs/pcre2/src/pcre2_error.c
new file mode 100644
index 0000000000..cbb9629674
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_error.c
@@ -0,0 +1,341 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+#define STRING(a) # a
+#define XSTRING(s) STRING(s)
+
+/* The texts of compile-time error messages. Compile-time error numbers start
+at COMPILE_ERROR_BASE (100).
+
+This used to be a table of strings, but in order to reduce the number of
+relocations needed when a shared library is loaded dynamically, it is now one
+long string. We cannot use a table of offsets, because the lengths of inserts
+such as XSTRING(MAX_NAME_SIZE) are not known. Instead,
+pcre2_get_error_message() counts through to the one it wants - this isn't a
+performance issue because these strings are used only when there is an error.
+
+Each substring ends with \0 to insert a null character. This includes the final
+substring, so that the whole string ends with \0\0, which can be detected when
+counting through. */
+
+static const unsigned char compile_error_texts[] =
+ "no error\0"
+ "\\ at end of pattern\0"
+ "\\c at end of pattern\0"
+ "unrecognized character follows \\\0"
+ "numbers out of order in {} quantifier\0"
+ /* 5 */
+ "number too big in {} quantifier\0"
+ "missing terminating ] for character class\0"
+ "escape sequence is invalid in character class\0"
+ "range out of order in character class\0"
+ "quantifier does not follow a repeatable item\0"
+ /* 10 */
+ "internal error: unexpected repeat\0"
+ "unrecognized character after (? or (?-\0"
+ "POSIX named classes are supported only within a class\0"
+ "POSIX collating elements are not supported\0"
+ "missing closing parenthesis\0"
+ /* 15 */
+ "reference to non-existent subpattern\0"
+ "pattern passed as NULL\0"
+ "unrecognised compile-time option bit(s)\0"
+ "missing ) after (?# comment\0"
+ "parentheses are too deeply nested\0"
+ /* 20 */
+ "regular expression is too large\0"
+ "failed to allocate heap memory\0"
+ "unmatched closing parenthesis\0"
+ "internal error: code overflow\0"
+ "missing closing parenthesis for condition\0"
+ /* 25 */
+ "lookbehind assertion is not fixed length\0"
+ "a relative value of zero is not allowed\0"
+ "conditional subpattern contains more than two branches\0"
+ "assertion expected after (?( or (?(?C)\0"
+ "digit expected after (?+ or (?-\0"
+ /* 30 */
+ "unknown POSIX class name\0"
+ "internal error in pcre2_study(): should not occur\0"
+ "this version of PCRE2 does not have Unicode support\0"
+ "parentheses are too deeply nested (stack check)\0"
+ "character code point value in \\x{} or \\o{} is too large\0"
+ /* 35 */
+ "lookbehind is too complicated\0"
+ "\\C is not allowed in a lookbehind assertion in UTF-" XSTRING(PCRE2_CODE_UNIT_WIDTH) " mode\0"
+ "PCRE2 does not support \\F, \\L, \\l, \\N{name}, \\U, or \\u\0"
+ "number after (?C is greater than 255\0"
+ "closing parenthesis for (?C expected\0"
+ /* 40 */
+ "invalid escape sequence in (*VERB) name\0"
+ "unrecognized character after (?P\0"
+ "syntax error in subpattern name (missing terminator?)\0"
+ "two named subpatterns have the same name (PCRE2_DUPNAMES not set)\0"
+ "subpattern name must start with a non-digit\0"
+ /* 45 */
+ "this version of PCRE2 does not have support for \\P, \\p, or \\X\0"
+ "malformed \\P or \\p sequence\0"
+ "unknown property after \\P or \\p\0"
+ "subpattern name is too long (maximum " XSTRING(MAX_NAME_SIZE) " code units)\0"
+ "too many named subpatterns (maximum " XSTRING(MAX_NAME_COUNT) ")\0"
+ /* 50 */
+ "invalid range in character class\0"
+ "octal value is greater than \\377 in 8-bit non-UTF-8 mode\0"
+ "internal error: overran compiling workspace\0"
+ "internal error: previously-checked referenced subpattern not found\0"
+ "DEFINE subpattern contains more than one branch\0"
+ /* 55 */
+ "missing opening brace after \\o\0"
+ "internal error: unknown newline setting\0"
+ "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\0"
+ "(?R (recursive pattern call) must be followed by a closing parenthesis\0"
+ /* "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" */
+ "obsolete error (should not occur)\0" /* Was the above */
+ /* 60 */
+ "(*VERB) not recognized or malformed\0"
+ "subpattern number is too big\0"
+ "subpattern name expected\0"
+ "internal error: parsed pattern overflow\0"
+ "non-octal character in \\o{} (closing brace missing?)\0"
+ /* 65 */
+ "different names for subpatterns of the same number are not allowed\0"
+ "(*MARK) must have an argument\0"
+ "non-hex character in \\x{} (closing brace missing?)\0"
+#ifndef EBCDIC
+ "\\c must be followed by a printable ASCII character\0"
+#else
+ "\\c must be followed by a letter or one of [\\]^_?\0"
+#endif
+ "\\k is not followed by a braced, angle-bracketed, or quoted name\0"
+ /* 70 */
+ "internal error: unknown meta code in check_lookbehinds()\0"
+ "\\N is not supported in a class\0"
+ "callout string is too long\0"
+ "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\0"
+ "using UTF is disabled by the application\0"
+ /* 75 */
+ "using UCP is disabled by the application\0"
+ "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)\0"
+ "character code point value in \\u.... sequence is too large\0"
+ "digits missing in \\x{} or \\o{} or \\N{U+}\0"
+ "syntax error or number too big in (?(VERSION condition\0"
+ /* 80 */
+ "internal error: unknown opcode in auto_possessify()\0"
+ "missing terminating delimiter for callout with string argument\0"
+ "unrecognized string delimiter follows (?C\0"
+ "using \\C is disabled by the application\0"
+ "(?| and/or (?J: or (?x: parentheses are too deeply nested\0"
+ /* 85 */
+ "using \\C is disabled in this PCRE2 library\0"
+ "regular expression is too complicated\0"
+ "lookbehind assertion is too long\0"
+ "pattern string is longer than the limit set by the application\0"
+ "internal error: unknown code in parsed pattern\0"
+ /* 90 */
+ "internal error: bad code value in parsed_skip()\0"
+ "PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES is not allowed in UTF-16 mode\0"
+ "invalid option bits with PCRE2_LITERAL\0"
+ "\\N{U+dddd} is supported only in Unicode (UTF) mode\0"
+ "invalid hyphen in option setting\0"
+ /* 95 */
+ "(*alpha_assertion) not recognized\0"
+ "script runs require Unicode support, which this version of PCRE2 does not have\0"
+ "too many capturing groups (maximum 65535)\0"
+ "atomic assertion expected after (?( or (?(?C)\0"
+ "\\K is not allowed in lookarounds (but see PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK)\0"
+ ;
+
+/* Match-time and UTF error texts are in the same format. */
+
+static const unsigned char match_error_texts[] =
+ "no error\0"
+ "no match\0"
+ "partial match\0"
+ "UTF-8 error: 1 byte missing at end\0"
+ "UTF-8 error: 2 bytes missing at end\0"
+ /* 5 */
+ "UTF-8 error: 3 bytes missing at end\0"
+ "UTF-8 error: 4 bytes missing at end\0"
+ "UTF-8 error: 5 bytes missing at end\0"
+ "UTF-8 error: byte 2 top bits not 0x80\0"
+ "UTF-8 error: byte 3 top bits not 0x80\0"
+ /* 10 */
+ "UTF-8 error: byte 4 top bits not 0x80\0"
+ "UTF-8 error: byte 5 top bits not 0x80\0"
+ "UTF-8 error: byte 6 top bits not 0x80\0"
+ "UTF-8 error: 5-byte character is not allowed (RFC 3629)\0"
+ "UTF-8 error: 6-byte character is not allowed (RFC 3629)\0"
+ /* 15 */
+ "UTF-8 error: code points greater than 0x10ffff are not defined\0"
+ "UTF-8 error: code points 0xd800-0xdfff are not defined\0"
+ "UTF-8 error: overlong 2-byte sequence\0"
+ "UTF-8 error: overlong 3-byte sequence\0"
+ "UTF-8 error: overlong 4-byte sequence\0"
+ /* 20 */
+ "UTF-8 error: overlong 5-byte sequence\0"
+ "UTF-8 error: overlong 6-byte sequence\0"
+ "UTF-8 error: isolated byte with 0x80 bit set\0"
+ "UTF-8 error: illegal byte (0xfe or 0xff)\0"
+ "UTF-16 error: missing low surrogate at end\0"
+ /* 25 */
+ "UTF-16 error: invalid low surrogate\0"
+ "UTF-16 error: isolated low surrogate\0"
+ "UTF-32 error: code points 0xd800-0xdfff are not defined\0"
+ "UTF-32 error: code points greater than 0x10ffff are not defined\0"
+ "bad data value\0"
+ /* 30 */
+ "patterns do not all use the same character tables\0"
+ "magic number missing\0"
+ "pattern compiled in wrong mode: 8/16/32-bit error\0"
+ "bad offset value\0"
+ "bad option value\0"
+ /* 35 */
+ "invalid replacement string\0"
+ "bad offset into UTF string\0"
+ "callout error code\0" /* Never returned by PCRE2 itself */
+ "invalid data in workspace for DFA restart\0"
+ "too much recursion for DFA matching\0"
+ /* 40 */
+ "backreference condition or recursion test is not supported for DFA matching\0"
+ "function is not supported for DFA matching\0"
+ "pattern contains an item that is not supported for DFA matching\0"
+ "workspace size exceeded in DFA matching\0"
+ "internal error - pattern overwritten?\0"
+ /* 45 */
+ "bad JIT option\0"
+ "JIT stack limit reached\0"
+ "match limit exceeded\0"
+ "no more memory\0"
+ "unknown substring\0"
+ /* 50 */
+ "non-unique substring name\0"
+ "NULL argument passed with non-zero length\0"
+ "nested recursion at the same subject position\0"
+ "matching depth limit exceeded\0"
+ "requested value is not available\0"
+ /* 55 */
+ "requested value is not set\0"
+ "offset limit set without PCRE2_USE_OFFSET_LIMIT\0"
+ "bad escape sequence in replacement string\0"
+ "expected closing curly bracket in replacement string\0"
+ "bad substitution in replacement string\0"
+ /* 60 */
+ "match with end before start or start moved backwards is not supported\0"
+ "too many replacements (more than INT_MAX)\0"
+ "bad serialized data\0"
+ "heap limit exceeded\0"
+ "invalid syntax\0"
+ /* 65 */
+ "internal error - duplicate substitution match\0"
+ "PCRE2_MATCH_INVALID_UTF is not supported for DFA matching\0"
+ ;
+
+
+/*************************************************
+* Return error message *
+*************************************************/
+
+/* This function copies an error message into a buffer whose units are of an
+appropriate width. Error numbers are positive for compile-time errors, and
+negative for match-time errors (except for UTF errors), but the numbers are all
+distinct.
+
+Arguments:
+ enumber error number
+ buffer where to put the message (zero terminated)
+ size size of the buffer in code units
+
+Returns: length of message if all is well
+ negative on error
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_get_error_message(int enumber, PCRE2_UCHAR *buffer, PCRE2_SIZE size)
+{
+const unsigned char *message;
+PCRE2_SIZE i;
+int n;
+
+if (size == 0) return PCRE2_ERROR_NOMEMORY;
+
+if (enumber >= COMPILE_ERROR_BASE) /* Compile error */
+ {
+ message = compile_error_texts;
+ n = enumber - COMPILE_ERROR_BASE;
+ }
+else if (enumber < 0) /* Match or UTF error */
+ {
+ message = match_error_texts;
+ n = -enumber;
+ }
+else /* Invalid error number */
+ {
+ message = (unsigned char *)"\0"; /* Empty message list */
+ n = 1;
+ }
+
+for (; n > 0; n--)
+ {
+ while (*message++ != CHAR_NUL) {};
+ if (*message == CHAR_NUL) return PCRE2_ERROR_BADDATA;
+ }
+
+for (i = 0; *message != 0; i++)
+ {
+ if (i >= size - 1)
+ {
+ buffer[i] = 0; /* Terminate partial message */
+ return PCRE2_ERROR_NOMEMORY;
+ }
+ buffer[i] = *message++;
+ }
+
+buffer[i] = 0;
+return (int)i;
+}
+
+/* End of pcre2_error.c */
diff --git a/contrib/libs/pcre2/src/pcre2_extuni.c b/contrib/libs/pcre2/src/pcre2_extuni.c
new file mode 100644
index 0000000000..eb2e4706bb
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_extuni.c
@@ -0,0 +1,148 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains an internal function that is used to match a Unicode
+extended grapheme sequence. It is used by both pcre2_match() and
+pcre2_def_match(). However, it is called only when Unicode support is being
+compiled. Nevertheless, we provide a dummy function when there is no Unicode
+support, because some compilers do not like functionless source files. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+
+#include "pcre2_internal.h"
+
+
+/* Dummy function */
+
+#ifndef SUPPORT_UNICODE
+PCRE2_SPTR
+PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,
+ PCRE2_SPTR end_subject, BOOL utf, int *xcount)
+{
+(void)c;
+(void)eptr;
+(void)start_subject;
+(void)end_subject;
+(void)utf;
+(void)xcount;
+return NULL;
+}
+#else
+
+
+/*************************************************
+* Match an extended grapheme sequence *
+*************************************************/
+
+/*
+Arguments:
+ c the first character
+ eptr pointer to next character
+ start_subject pointer to start of subject
+ end_subject pointer to end of subject
+ utf TRUE if in UTF mode
+ xcount pointer to count of additional characters,
+ or NULL if count not needed
+
+Returns: pointer after the end of the sequence
+*/
+
+PCRE2_SPTR
+PRIV(extuni)(uint32_t c, PCRE2_SPTR eptr, PCRE2_SPTR start_subject,
+ PCRE2_SPTR end_subject, BOOL utf, int *xcount)
+{
+int lgb = UCD_GRAPHBREAK(c);
+
+while (eptr < end_subject)
+ {
+ int rgb;
+ int len = 1;
+ if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); }
+ rgb = UCD_GRAPHBREAK(c);
+ if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)
+ {
+ int ricount = 0;
+ PCRE2_SPTR bptr = eptr - 1;
+ if (utf) BACKCHAR(bptr);
+
+ /* bptr is pointing to the left-hand character */
+
+ while (bptr > start_subject)
+ {
+ bptr--;
+ if (utf)
+ {
+ BACKCHAR(bptr);
+ GETCHAR(c, bptr);
+ }
+ else
+ c = *bptr;
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator) break;
+ ricount++;
+ }
+ if ((ricount & 1) != 0) break; /* Grapheme break required */
+ }
+
+ /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this
+ allows any number of them before a following Extended_Pictographic. */
+
+ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) ||
+ lgb != ucp_gbExtended_Pictographic)
+ lgb = rgb;
+
+ eptr += len;
+ if (xcount != NULL) *xcount += 1;
+ }
+
+return eptr;
+}
+
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_extuni.c */
diff --git a/contrib/libs/pcre2/src/pcre2_find_bracket.c b/contrib/libs/pcre2/src/pcre2_find_bracket.c
new file mode 100644
index 0000000000..f80503a3f1
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_find_bracket.c
@@ -0,0 +1,219 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains a single function that scans through a compiled pattern
+until it finds a capturing bracket with the given number, or, if the number is
+negative, an instance of OP_REVERSE for a lookbehind. The function is called
+from pcre2_compile.c and also from pcre2_study.c when finding the minimum
+matching length. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Scan compiled regex for specific bracket *
+*************************************************/
+
+/*
+Arguments:
+ code points to start of expression
+ utf TRUE in UTF mode
+ number the required bracket number or negative to find a lookbehind
+
+Returns: pointer to the opcode for the bracket, or NULL if not found
+*/
+
+PCRE2_SPTR
+PRIV(find_bracket)(PCRE2_SPTR code, BOOL utf, int number)
+{
+for (;;)
+ {
+ PCRE2_UCHAR c = *code;
+
+ if (c == OP_END) return NULL;
+
+ /* XCLASS is used for classes that cannot be represented just by a bit map.
+ This includes negated single high-valued characters. CALLOUT_STR is used for
+ callouts with string arguments. In both cases the length in the table is
+ zero; the actual length is stored in the compiled code. */
+
+ if (c == OP_XCLASS) code += GET(code, 1);
+ else if (c == OP_CALLOUT_STR) code += GET(code, 1 + 2*LINK_SIZE);
+
+ /* Handle lookbehind */
+
+ else if (c == OP_REVERSE)
+ {
+ if (number < 0) return (PCRE2_UCHAR *)code;
+ code += PRIV(OP_lengths)[c];
+ }
+
+ /* Handle capturing bracket */
+
+ else if (c == OP_CBRA || c == OP_SCBRA ||
+ c == OP_CBRAPOS || c == OP_SCBRAPOS)
+ {
+ int n = (int)GET2(code, 1+LINK_SIZE);
+ if (n == number) return (PCRE2_UCHAR *)code;
+ code += PRIV(OP_lengths)[c];
+ }
+
+ /* Otherwise, we can get the item's length from the table, except that for
+ repeated character types, we have to test for \p and \P, which have an extra
+ two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we
+ must add in its length. */
+
+ else
+ {
+ switch(c)
+ {
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2;
+ break;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSUPTO:
+ if (code[1 + IMM2_SIZE] == OP_PROP || code[1 + IMM2_SIZE] == OP_NOTPROP)
+ code += 2;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ code += code[1];
+ break;
+ }
+
+ /* Add in the fixed length from the table */
+
+ code += PRIV(OP_lengths)[c];
+
+ /* In UTF-8 and UTF-16 modes, opcodes that are followed by a character may be
+ followed by a multi-byte character. The length in the table is a minimum, so
+ we have to arrange to skip the extra bytes. */
+
+#ifdef MAYBE_UTF_MULTI
+ if (utf) switch(c)
+ {
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_EXACT:
+ case OP_EXACTI:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ case OP_UPTO:
+ case OP_UPTOI:
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ case OP_STAR:
+ case OP_STARI:
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]);
+ break;
+ }
+#else
+ (void)(utf); /* Keep compiler happy by referencing function argument */
+#endif /* MAYBE_UTF_MULTI */
+ }
+ }
+}
+
+/* End of pcre2_find_bracket.c */
diff --git a/contrib/libs/pcre2/src/pcre2_internal.h b/contrib/libs/pcre2/src/pcre2_internal.h
new file mode 100644
index 0000000000..92dd3138d4
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_internal.h
@@ -0,0 +1,2047 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE2 is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef PCRE2_INTERNAL_H_IDEMPOTENT_GUARD
+#define PCRE2_INTERNAL_H_IDEMPOTENT_GUARD
+
+/* We do not support both EBCDIC and Unicode at the same time. The "configure"
+script prevents both being selected, but not everybody uses "configure". EBCDIC
+is only supported for the 8-bit library, but the check for this has to be later
+in this file, because the first part is not width-dependent, and is included by
+pcre2test.c with CODE_UNIT_WIDTH == 0. */
+
+#if defined EBCDIC && defined SUPPORT_UNICODE
+#error The use of both EBCDIC and SUPPORT_UNICODE is not supported.
+#endif
+
+/* Standard C headers */
+
+#include <ctype.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Macros to make boolean values more obvious. The #ifndef is to pacify
+compiler warnings in environments where these macros are defined elsewhere.
+Unfortunately, there is no way to do the same for the typedef. */
+
+typedef int BOOL;
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif
+
+/* Valgrind (memcheck) support */
+
+#ifdef SUPPORT_VALGRIND
+#include <valgrind/memcheck.h>
+#endif
+
+/* -ftrivial-auto-var-init support supports initializing all local variables
+to avoid some classes of bug, but this can cause an unacceptable slowdown
+for large on-stack arrays in hot functions. This macro lets us annotate
+such arrays. */
+
+#ifdef HAVE_ATTRIBUTE_UNINITIALIZED
+#define PCRE2_KEEP_UNINITIALIZED __attribute__((uninitialized))
+#else
+#define PCRE2_KEEP_UNINITIALIZED
+#endif
+
+/* Older versions of MSVC lack snprintf(). This define allows for
+warning/error-free compilation and testing with MSVC compilers back to at least
+MSVC 10/2010. Except for VC6 (which is missing some fundamentals and fails). */
+
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+#define snprintf _snprintf
+#endif
+
+/* When compiling a DLL for Windows, the exported symbols have to be declared
+using some MS magic. I found some useful information on this web page:
+http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the
+information there, using __declspec(dllexport) without "extern" we have a
+definition; with "extern" we have a declaration. The settings here override the
+setting in pcre2.h (which is included below); it defines only PCRE2_EXP_DECL,
+which is all that is needed for applications (they just import the symbols). We
+use:
+
+ PCRE2_EXP_DECL for declarations
+ PCRE2_EXP_DEFN for definitions
+
+The reason for wrapping this in #ifndef PCRE2_EXP_DECL is so that pcre2test,
+which is an application, but needs to import this file in order to "peek" at
+internals, can #include pcre2.h first to get an application's-eye view.
+
+In principle, people compiling for non-Windows, non-Unix-like (i.e. uncommon,
+special-purpose environments) might want to stick other stuff in front of
+exported symbols. That's why, in the non-Windows case, we set PCRE2_EXP_DEFN
+only if it is not already set. */
+
+#ifndef PCRE2_EXP_DECL
+# ifdef _WIN32
+# ifndef PCRE2_STATIC
+# define PCRE2_EXP_DECL extern __declspec(dllexport)
+# define PCRE2_EXP_DEFN __declspec(dllexport)
+# else
+# define PCRE2_EXP_DECL extern
+# define PCRE2_EXP_DEFN
+# endif
+# else
+# ifdef __cplusplus
+# define PCRE2_EXP_DECL extern "C"
+# else
+# define PCRE2_EXP_DECL extern
+# endif
+# ifndef PCRE2_EXP_DEFN
+# define PCRE2_EXP_DEFN PCRE2_EXP_DECL
+# endif
+# endif
+#endif
+
+/* Include the public PCRE2 header and the definitions of UCP character
+property values. This must follow the setting of PCRE2_EXP_DECL above. */
+
+#include "pcre2.h"
+#include "pcre2_ucp.h"
+
+/* When PCRE2 is compiled as a C++ library, the subject pointer can be replaced
+with a custom type. This makes it possible, for example, to allow pcre2_match()
+to process subject strings that are discontinuous by using a smart pointer
+class. It must always be possible to inspect all of the subject string in
+pcre2_match() because of the way it backtracks. */
+
+/* WARNING: This is as yet untested for PCRE2. */
+
+#ifdef CUSTOM_SUBJECT_PTR
+#undef PCRE2_SPTR
+#define PCRE2_SPTR CUSTOM_SUBJECT_PTR
+#endif
+
+/* When checking for integer overflow in pcre2_compile(), we need to handle
+large integers. If a 64-bit integer type is available, we can use that.
+Otherwise we have to cast to double, which of course requires floating point
+arithmetic. Handle this by defining a macro for the appropriate type. */
+
+#if defined INT64_MAX || defined int64_t
+#define INT64_OR_DOUBLE int64_t
+#else
+#define INT64_OR_DOUBLE double
+#endif
+
+/* External (in the C sense) functions and tables that are private to the
+libraries are always referenced using the PRIV macro. This makes it possible
+for pcre2test.c to include some of the source files from the libraries using a
+different PRIV definition to avoid name clashes. It also makes it clear in the
+code that a non-static object is being referenced. */
+
+#ifndef PRIV
+#define PRIV(name) _pcre2_##name
+#endif
+
+/* When compiling for use with the Virtual Pascal compiler, these functions
+need to have their names changed. PCRE2 must be compiled with the -DVPCOMPAT
+option on the command line. */
+
+#ifdef VPCOMPAT
+#define strlen(s) _strlen(s)
+#define strncmp(s1,s2,m) _strncmp(s1,s2,m)
+#define memcmp(s,c,n) _memcmp(s,c,n)
+#define memcpy(d,s,n) _memcpy(d,s,n)
+#define memmove(d,s,n) _memmove(d,s,n)
+#define memset(s,c,n) _memset(s,c,n)
+#else /* VPCOMPAT */
+
+/* Otherwise, to cope with SunOS4 and other systems that lack memmove(), define
+a macro that calls an emulating function. */
+
+#ifndef HAVE_MEMMOVE
+#undef memmove /* Some systems may have a macro */
+#define memmove(a, b, c) PRIV(memmove)(a, b, c)
+#endif /* not HAVE_MEMMOVE */
+#endif /* not VPCOMPAT */
+
+/* This is an unsigned int value that no UTF character can ever have, as
+Unicode doesn't go beyond 0x0010ffff. */
+
+#define NOTACHAR 0xffffffff
+
+/* This is the largest valid UTF/Unicode code point. */
+
+#define MAX_UTF_CODE_POINT 0x10ffff
+
+/* Compile-time positive error numbers (all except UTF errors, which are
+negative) start at this value. It should probably never be changed, in case
+some application is checking for specific numbers. There is a copy of this
+#define in pcre2posix.c (which now no longer includes this file). Ideally, a
+way of having a single definition should be found, but as the number is
+unlikely to change, this is not a pressing issue. The original reason for
+having a base other than 0 was to keep the absolute values of compile-time and
+run-time error numbers numerically different, but in the event the code does
+not rely on this. */
+
+#define COMPILE_ERROR_BASE 100
+
+/* The initial frames vector for remembering pcre2_match() backtracking points
+is allocated on the heap, of this size (bytes) or ten times the frame size if
+larger, unless the heap limit is smaller. Typical frame sizes are a few hundred
+bytes (it depends on the number of capturing parentheses) so 20KiB handles
+quite a few frames. A larger vector on the heap is obtained for matches that
+need more frames, subject to the heap limit. */
+
+#define START_FRAMES_SIZE 20480
+
+/* For DFA matching, an initial internal workspace vector is allocated on the
+stack. The heap is used only if this turns out to be too small. */
+
+#define DFA_START_RWS_SIZE 30720
+
+/* Define the default BSR convention. */
+
+#ifdef BSR_ANYCRLF
+#define BSR_DEFAULT PCRE2_BSR_ANYCRLF
+#else
+#define BSR_DEFAULT PCRE2_BSR_UNICODE
+#endif
+
+
+/* ---------------- Basic UTF-8 macros ---------------- */
+
+/* These UTF-8 macros are always defined because they are used in pcre2test for
+handling wide characters in 16-bit and 32-bit modes, even if an 8-bit library
+is not supported. */
+
+/* Tests whether a UTF-8 code point needs extra bytes to decode. */
+
+#define HASUTF8EXTRALEN(c) ((c) >= 0xc0)
+
+/* The following macros were originally written in the form of loops that used
+data from the tables whose names start with PRIV(utf8_table). They were
+rewritten by a user so as not to use loops, because in some environments this
+gives a significant performance advantage, and it seems never to do any harm.
+*/
+
+/* Base macro to pick up the remaining bytes of a UTF-8 character, not
+advancing the pointer. */
+
+#define GETUTF8(c, eptr) \
+ { \
+ if ((c & 0x20u) == 0) \
+ c = ((c & 0x1fu) << 6) | (eptr[1] & 0x3fu); \
+ else if ((c & 0x10u) == 0) \
+ c = ((c & 0x0fu) << 12) | ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \
+ else if ((c & 0x08u) == 0) \
+ c = ((c & 0x07u) << 18) | ((eptr[1] & 0x3fu) << 12) | \
+ ((eptr[2] & 0x3fu) << 6) | (eptr[3] & 0x3fu); \
+ else if ((c & 0x04u) == 0) \
+ c = ((c & 0x03u) << 24) | ((eptr[1] & 0x3fu) << 18) | \
+ ((eptr[2] & 0x3fu) << 12) | ((eptr[3] & 0x3fu) << 6) | \
+ (eptr[4] & 0x3fu); \
+ else \
+ c = ((c & 0x01u) << 30) | ((eptr[1] & 0x3fu) << 24) | \
+ ((eptr[2] & 0x3fu) << 18) | ((eptr[3] & 0x3fu) << 12) | \
+ ((eptr[4] & 0x3fu) << 6) | (eptr[5] & 0x3fu); \
+ }
+
+/* Base macro to pick up the remaining bytes of a UTF-8 character, advancing
+the pointer. */
+
+#define GETUTF8INC(c, eptr) \
+ { \
+ if ((c & 0x20u) == 0) \
+ c = ((c & 0x1fu) << 6) | (*eptr++ & 0x3fu); \
+ else if ((c & 0x10u) == 0) \
+ { \
+ c = ((c & 0x0fu) << 12) | ((*eptr & 0x3fu) << 6) | (eptr[1] & 0x3fu); \
+ eptr += 2; \
+ } \
+ else if ((c & 0x08u) == 0) \
+ { \
+ c = ((c & 0x07u) << 18) | ((*eptr & 0x3fu) << 12) | \
+ ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \
+ eptr += 3; \
+ } \
+ else if ((c & 0x04u) == 0) \
+ { \
+ c = ((c & 0x03u) << 24) | ((*eptr & 0x3fu) << 18) | \
+ ((eptr[1] & 0x3fu) << 12) | ((eptr[2] & 0x3fu) << 6) | \
+ (eptr[3] & 0x3fu); \
+ eptr += 4; \
+ } \
+ else \
+ { \
+ c = ((c & 0x01u) << 30) | ((*eptr & 0x3fu) << 24) | \
+ ((eptr[1] & 0x3fu) << 18) | ((eptr[2] & 0x3fu) << 12) | \
+ ((eptr[3] & 0x3fu) << 6) | (eptr[4] & 0x3fu); \
+ eptr += 5; \
+ } \
+ }
+
+/* Base macro to pick up the remaining bytes of a UTF-8 character, not
+advancing the pointer, incrementing the length. */
+
+#define GETUTF8LEN(c, eptr, len) \
+ { \
+ if ((c & 0x20u) == 0) \
+ { \
+ c = ((c & 0x1fu) << 6) | (eptr[1] & 0x3fu); \
+ len++; \
+ } \
+ else if ((c & 0x10u) == 0) \
+ { \
+ c = ((c & 0x0fu) << 12) | ((eptr[1] & 0x3fu) << 6) | (eptr[2] & 0x3fu); \
+ len += 2; \
+ } \
+ else if ((c & 0x08u) == 0) \
+ {\
+ c = ((c & 0x07u) << 18) | ((eptr[1] & 0x3fu) << 12) | \
+ ((eptr[2] & 0x3fu) << 6) | (eptr[3] & 0x3fu); \
+ len += 3; \
+ } \
+ else if ((c & 0x04u) == 0) \
+ { \
+ c = ((c & 0x03u) << 24) | ((eptr[1] & 0x3fu) << 18) | \
+ ((eptr[2] & 0x3fu) << 12) | ((eptr[3] & 0x3fu) << 6) | \
+ (eptr[4] & 0x3fu); \
+ len += 4; \
+ } \
+ else \
+ {\
+ c = ((c & 0x01u) << 30) | ((eptr[1] & 0x3fu) << 24) | \
+ ((eptr[2] & 0x3fu) << 18) | ((eptr[3] & 0x3fu) << 12) | \
+ ((eptr[4] & 0x3fu) << 6) | (eptr[5] & 0x3fu); \
+ len += 5; \
+ } \
+ }
+
+/* --------------- Whitespace macros ---------------- */
+
+/* Tests for Unicode horizontal and vertical whitespace characters must check a
+number of different values. Using a switch statement for this generates the
+fastest code (no loop, no memory access), and there are several places in the
+interpreter code where this happens. In order to ensure that all the case lists
+remain in step, we use macros so that there is only one place where the lists
+are defined.
+
+These values are also required as lists in pcre2_compile.c when processing \h,
+\H, \v and \V in a character class. The lists are defined in pcre2_tables.c,
+but macros that define the values are here so that all the definitions are
+together. The lists must be in ascending character order, terminated by
+NOTACHAR (which is 0xffffffff).
+
+Any changes should ensure that the various macros are kept in step with each
+other. NOTE: The values also appear in pcre2_jit_compile.c. */
+
+/* -------------- ASCII/Unicode environments -------------- */
+
+#ifndef EBCDIC
+
+/* Character U+180E (Mongolian Vowel Separator) is not included in the list of
+spaces in the Unicode file PropList.txt, and Perl does not recognize it as a
+space. However, in many other sources it is listed as a space and has been in
+PCRE (both APIs) for a long time. */
+
+#define HSPACE_LIST \
+ CHAR_HT, CHAR_SPACE, CHAR_NBSP, \
+ 0x1680, 0x180e, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, \
+ 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202f, 0x205f, 0x3000, \
+ NOTACHAR
+
+#define HSPACE_MULTIBYTE_CASES \
+ case 0x1680: /* OGHAM SPACE MARK */ \
+ case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ \
+ case 0x2000: /* EN QUAD */ \
+ case 0x2001: /* EM QUAD */ \
+ case 0x2002: /* EN SPACE */ \
+ case 0x2003: /* EM SPACE */ \
+ case 0x2004: /* THREE-PER-EM SPACE */ \
+ case 0x2005: /* FOUR-PER-EM SPACE */ \
+ case 0x2006: /* SIX-PER-EM SPACE */ \
+ case 0x2007: /* FIGURE SPACE */ \
+ case 0x2008: /* PUNCTUATION SPACE */ \
+ case 0x2009: /* THIN SPACE */ \
+ case 0x200A: /* HAIR SPACE */ \
+ case 0x202f: /* NARROW NO-BREAK SPACE */ \
+ case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ \
+ case 0x3000 /* IDEOGRAPHIC SPACE */
+
+#define HSPACE_BYTE_CASES \
+ case CHAR_HT: \
+ case CHAR_SPACE: \
+ case CHAR_NBSP
+
+#define HSPACE_CASES \
+ HSPACE_BYTE_CASES: \
+ HSPACE_MULTIBYTE_CASES
+
+#define VSPACE_LIST \
+ CHAR_LF, CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, 0x2028, 0x2029, NOTACHAR
+
+#define VSPACE_MULTIBYTE_CASES \
+ case 0x2028: /* LINE SEPARATOR */ \
+ case 0x2029 /* PARAGRAPH SEPARATOR */
+
+#define VSPACE_BYTE_CASES \
+ case CHAR_LF: \
+ case CHAR_VT: \
+ case CHAR_FF: \
+ case CHAR_CR: \
+ case CHAR_NEL
+
+#define VSPACE_CASES \
+ VSPACE_BYTE_CASES: \
+ VSPACE_MULTIBYTE_CASES
+
+/* -------------- EBCDIC environments -------------- */
+
+#else
+#define HSPACE_LIST CHAR_HT, CHAR_SPACE, CHAR_NBSP, NOTACHAR
+
+#define HSPACE_BYTE_CASES \
+ case CHAR_HT: \
+ case CHAR_SPACE: \
+ case CHAR_NBSP
+
+#define HSPACE_CASES HSPACE_BYTE_CASES
+
+#ifdef EBCDIC_NL25
+#define VSPACE_LIST \
+ CHAR_VT, CHAR_FF, CHAR_CR, CHAR_NEL, CHAR_LF, NOTACHAR
+#else
+#define VSPACE_LIST \
+ CHAR_VT, CHAR_FF, CHAR_CR, CHAR_LF, CHAR_NEL, NOTACHAR
+#endif
+
+#define VSPACE_BYTE_CASES \
+ case CHAR_LF: \
+ case CHAR_VT: \
+ case CHAR_FF: \
+ case CHAR_CR: \
+ case CHAR_NEL
+
+#define VSPACE_CASES VSPACE_BYTE_CASES
+#endif /* EBCDIC */
+
+/* -------------- End of whitespace macros -------------- */
+
+
+/* PCRE2 is able to support several different kinds of newline (CR, LF, CRLF,
+"any" and "anycrlf" at present). The following macros are used to package up
+testing for newlines. NLBLOCK, PSSTART, and PSEND are defined in the various
+modules to indicate in which datablock the parameters exist, and what the
+start/end of string field names are. */
+
+#define NLTYPE_FIXED 0 /* Newline is a fixed length string */
+#define NLTYPE_ANY 1 /* Newline is any Unicode line ending */
+#define NLTYPE_ANYCRLF 2 /* Newline is CR, LF, or CRLF */
+
+/* This macro checks for a newline at the given position */
+
+#define IS_NEWLINE(p) \
+ ((NLBLOCK->nltype != NLTYPE_FIXED)? \
+ ((p) < NLBLOCK->PSEND && \
+ PRIV(is_newline)((p), NLBLOCK->nltype, NLBLOCK->PSEND, \
+ &(NLBLOCK->nllen), utf)) \
+ : \
+ ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \
+ UCHAR21TEST(p) == NLBLOCK->nl[0] && \
+ (NLBLOCK->nllen == 1 || UCHAR21TEST(p+1) == NLBLOCK->nl[1]) \
+ ) \
+ )
+
+/* This macro checks for a newline immediately preceding the given position */
+
+#define WAS_NEWLINE(p) \
+ ((NLBLOCK->nltype != NLTYPE_FIXED)? \
+ ((p) > NLBLOCK->PSSTART && \
+ PRIV(was_newline)((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \
+ &(NLBLOCK->nllen), utf)) \
+ : \
+ ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \
+ UCHAR21TEST(p - NLBLOCK->nllen) == NLBLOCK->nl[0] && \
+ (NLBLOCK->nllen == 1 || UCHAR21TEST(p - NLBLOCK->nllen + 1) == NLBLOCK->nl[1]) \
+ ) \
+ )
+
+/* Private flags containing information about the compiled pattern. The first
+three must not be changed, because whichever is set is actually the number of
+bytes in a code unit in that mode. */
+
+#define PCRE2_MODE8 0x00000001 /* compiled in 8 bit mode */
+#define PCRE2_MODE16 0x00000002 /* compiled in 16 bit mode */
+#define PCRE2_MODE32 0x00000004 /* compiled in 32 bit mode */
+#define PCRE2_FIRSTSET 0x00000010 /* first_code unit is set */
+#define PCRE2_FIRSTCASELESS 0x00000020 /* caseless first code unit */
+#define PCRE2_FIRSTMAPSET 0x00000040 /* bitmap of first code units is set */
+#define PCRE2_LASTSET 0x00000080 /* last code unit is set */
+#define PCRE2_LASTCASELESS 0x00000100 /* caseless last code unit */
+#define PCRE2_STARTLINE 0x00000200 /* start after \n for multiline */
+#define PCRE2_JCHANGED 0x00000400 /* j option used in pattern */
+#define PCRE2_HASCRORLF 0x00000800 /* explicit \r or \n in pattern */
+#define PCRE2_HASTHEN 0x00001000 /* pattern contains (*THEN) */
+#define PCRE2_MATCH_EMPTY 0x00002000 /* pattern can match empty string */
+#define PCRE2_BSR_SET 0x00004000 /* BSR was set in the pattern */
+#define PCRE2_NL_SET 0x00008000 /* newline was set in the pattern */
+#define PCRE2_NOTEMPTY_SET 0x00010000 /* (*NOTEMPTY) used ) keep */
+#define PCRE2_NE_ATST_SET 0x00020000 /* (*NOTEMPTY_ATSTART) used) together */
+#define PCRE2_DEREF_TABLES 0x00040000 /* release character tables */
+#define PCRE2_NOJIT 0x00080000 /* (*NOJIT) used */
+#define PCRE2_HASBKPORX 0x00100000 /* contains \P, \p, or \X */
+#define PCRE2_DUPCAPUSED 0x00200000 /* contains (?| */
+#define PCRE2_HASBKC 0x00400000 /* contains \C */
+#define PCRE2_HASACCEPT 0x00800000 /* contains (*ACCEPT) */
+
+#define PCRE2_MODE_MASK (PCRE2_MODE8 | PCRE2_MODE16 | PCRE2_MODE32)
+
+/* Values for the matchedby field in a match data block. */
+
+enum { PCRE2_MATCHEDBY_INTERPRETER, /* pcre2_match() */
+ PCRE2_MATCHEDBY_DFA_INTERPRETER, /* pcre2_dfa_match() */
+ PCRE2_MATCHEDBY_JIT }; /* pcre2_jit_match() */
+
+/* Values for the flags field in a match data block. */
+
+#define PCRE2_MD_COPIED_SUBJECT 0x01u
+
+/* Magic number to provide a small check against being handed junk. */
+
+#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */
+
+/* The maximum remaining length of subject we are prepared to search for a
+req_unit match from an anchored pattern. In 8-bit mode, memchr() is used and is
+much faster than the search loop that has to be used in 16-bit and 32-bit
+modes. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define REQ_CU_MAX 5000
+#else
+#define REQ_CU_MAX 2000
+#endif
+
+/* Offsets for the bitmap tables in the cbits set of tables. Each table
+contains a set of bits for a class map. Some classes are built by combining
+these tables. */
+
+#define cbit_space 0 /* [:space:] or \s */
+#define cbit_xdigit 32 /* [:xdigit:] */
+#define cbit_digit 64 /* [:digit:] or \d */
+#define cbit_upper 96 /* [:upper:] */
+#define cbit_lower 128 /* [:lower:] */
+#define cbit_word 160 /* [:word:] or \w */
+#define cbit_graph 192 /* [:graph:] */
+#define cbit_print 224 /* [:print:] */
+#define cbit_punct 256 /* [:punct:] */
+#define cbit_cntrl 288 /* [:cntrl:] */
+#define cbit_length 320 /* Length of the cbits table */
+
+/* Bit definitions for entries in the ctypes table. Do not change these values
+without checking pcre2_jit_compile.c, which has an assertion to ensure that
+ctype_word has the value 16. */
+
+#define ctype_space 0x01
+#define ctype_letter 0x02
+#define ctype_lcletter 0x04
+#define ctype_digit 0x08
+#define ctype_word 0x10 /* alphanumeric or '_' */
+
+/* Offsets of the various tables from the base tables pointer, and
+total length of the tables. */
+
+#define lcc_offset 0 /* Lower case */
+#define fcc_offset 256 /* Flip case */
+#define cbits_offset 512 /* Character classes */
+#define ctypes_offset (cbits_offset + cbit_length) /* Character types */
+#define TABLES_LENGTH (ctypes_offset + 256)
+
+
+/* -------------------- Character and string names ------------------------ */
+
+/* If PCRE2 is to support UTF-8 on EBCDIC platforms, we cannot use normal
+character constants like '*' because the compiler would emit their EBCDIC code,
+which is different from their ASCII/UTF-8 code. Instead we define macros for
+the characters so that they always use the ASCII/UTF-8 code when UTF-8 support
+is enabled. When UTF-8 support is not enabled, the definitions use character
+literals. Both character and string versions of each character are needed, and
+there are some longer strings as well.
+
+This means that, on EBCDIC platforms, the PCRE2 library can handle either
+EBCDIC, or UTF-8, but not both. To support both in the same compiled library
+would need different lookups depending on whether PCRE2_UTF was set or not.
+This would make it impossible to use characters in switch/case statements,
+which would reduce performance. For a theoretical use (which nobody has asked
+for) in a minority area (EBCDIC platforms), this is not sensible. Any
+application that did need both could compile two versions of the library, using
+macros to give the functions distinct names. */
+
+#ifndef SUPPORT_UNICODE
+
+/* UTF-8 support is not enabled; use the platform-dependent character literals
+so that PCRE2 works in both ASCII and EBCDIC environments, but only in non-UTF
+mode. Newline characters are problematic in EBCDIC. Though it has CR and LF
+characters, a common practice has been to use its NL (0x15) character as the
+line terminator in C-like processing environments. However, sometimes the LF
+(0x25) character is used instead, according to this Unicode document:
+
+http://unicode.org/standard/reports/tr13/tr13-5.html
+
+PCRE2 defaults EBCDIC NL to 0x15, but has a build-time option to select 0x25
+instead. Whichever is *not* chosen is defined as NEL.
+
+In both ASCII and EBCDIC environments, CHAR_NL and CHAR_LF are synonyms for the
+same code point. */
+
+#ifdef EBCDIC
+
+#ifndef EBCDIC_NL25
+#define CHAR_NL '\x15'
+#define CHAR_NEL '\x25'
+#define STR_NL "\x15"
+#define STR_NEL "\x25"
+#else
+#define CHAR_NL '\x25'
+#define CHAR_NEL '\x15'
+#define STR_NL "\x25"
+#define STR_NEL "\x15"
+#endif
+
+#define CHAR_LF CHAR_NL
+#define STR_LF STR_NL
+
+#define CHAR_ESC '\047'
+#define CHAR_DEL '\007'
+#define CHAR_NBSP ((unsigned char)'\x41')
+#define STR_ESC "\047"
+#define STR_DEL "\007"
+
+#else /* Not EBCDIC */
+
+/* In ASCII/Unicode, linefeed is '\n' and we equate this to NL for
+compatibility. NEL is the Unicode newline character; make sure it is
+a positive value. */
+
+#define CHAR_LF '\n'
+#define CHAR_NL CHAR_LF
+#define CHAR_NEL ((unsigned char)'\x85')
+#define CHAR_ESC '\033'
+#define CHAR_DEL '\177'
+#define CHAR_NBSP ((unsigned char)'\xa0')
+
+#define STR_LF "\n"
+#define STR_NL STR_LF
+#define STR_NEL "\x85"
+#define STR_ESC "\033"
+#define STR_DEL "\177"
+
+#endif /* EBCDIC */
+
+/* The remaining definitions work in both environments. */
+
+#define CHAR_NUL '\0'
+#define CHAR_HT '\t'
+#define CHAR_VT '\v'
+#define CHAR_FF '\f'
+#define CHAR_CR '\r'
+#define CHAR_BS '\b'
+#define CHAR_BEL '\a'
+
+#define CHAR_SPACE ' '
+#define CHAR_EXCLAMATION_MARK '!'
+#define CHAR_QUOTATION_MARK '"'
+#define CHAR_NUMBER_SIGN '#'
+#define CHAR_DOLLAR_SIGN '$'
+#define CHAR_PERCENT_SIGN '%'
+#define CHAR_AMPERSAND '&'
+#define CHAR_APOSTROPHE '\''
+#define CHAR_LEFT_PARENTHESIS '('
+#define CHAR_RIGHT_PARENTHESIS ')'
+#define CHAR_ASTERISK '*'
+#define CHAR_PLUS '+'
+#define CHAR_COMMA ','
+#define CHAR_MINUS '-'
+#define CHAR_DOT '.'
+#define CHAR_SLASH '/'
+#define CHAR_0 '0'
+#define CHAR_1 '1'
+#define CHAR_2 '2'
+#define CHAR_3 '3'
+#define CHAR_4 '4'
+#define CHAR_5 '5'
+#define CHAR_6 '6'
+#define CHAR_7 '7'
+#define CHAR_8 '8'
+#define CHAR_9 '9'
+#define CHAR_COLON ':'
+#define CHAR_SEMICOLON ';'
+#define CHAR_LESS_THAN_SIGN '<'
+#define CHAR_EQUALS_SIGN '='
+#define CHAR_GREATER_THAN_SIGN '>'
+#define CHAR_QUESTION_MARK '?'
+#define CHAR_COMMERCIAL_AT '@'
+#define CHAR_A 'A'
+#define CHAR_B 'B'
+#define CHAR_C 'C'
+#define CHAR_D 'D'
+#define CHAR_E 'E'
+#define CHAR_F 'F'
+#define CHAR_G 'G'
+#define CHAR_H 'H'
+#define CHAR_I 'I'
+#define CHAR_J 'J'
+#define CHAR_K 'K'
+#define CHAR_L 'L'
+#define CHAR_M 'M'
+#define CHAR_N 'N'
+#define CHAR_O 'O'
+#define CHAR_P 'P'
+#define CHAR_Q 'Q'
+#define CHAR_R 'R'
+#define CHAR_S 'S'
+#define CHAR_T 'T'
+#define CHAR_U 'U'
+#define CHAR_V 'V'
+#define CHAR_W 'W'
+#define CHAR_X 'X'
+#define CHAR_Y 'Y'
+#define CHAR_Z 'Z'
+#define CHAR_LEFT_SQUARE_BRACKET '['
+#define CHAR_BACKSLASH '\\'
+#define CHAR_RIGHT_SQUARE_BRACKET ']'
+#define CHAR_CIRCUMFLEX_ACCENT '^'
+#define CHAR_UNDERSCORE '_'
+#define CHAR_GRAVE_ACCENT '`'
+#define CHAR_a 'a'
+#define CHAR_b 'b'
+#define CHAR_c 'c'
+#define CHAR_d 'd'
+#define CHAR_e 'e'
+#define CHAR_f 'f'
+#define CHAR_g 'g'
+#define CHAR_h 'h'
+#define CHAR_i 'i'
+#define CHAR_j 'j'
+#define CHAR_k 'k'
+#define CHAR_l 'l'
+#define CHAR_m 'm'
+#define CHAR_n 'n'
+#define CHAR_o 'o'
+#define CHAR_p 'p'
+#define CHAR_q 'q'
+#define CHAR_r 'r'
+#define CHAR_s 's'
+#define CHAR_t 't'
+#define CHAR_u 'u'
+#define CHAR_v 'v'
+#define CHAR_w 'w'
+#define CHAR_x 'x'
+#define CHAR_y 'y'
+#define CHAR_z 'z'
+#define CHAR_LEFT_CURLY_BRACKET '{'
+#define CHAR_VERTICAL_LINE '|'
+#define CHAR_RIGHT_CURLY_BRACKET '}'
+#define CHAR_TILDE '~'
+
+#define STR_HT "\t"
+#define STR_VT "\v"
+#define STR_FF "\f"
+#define STR_CR "\r"
+#define STR_BS "\b"
+#define STR_BEL "\a"
+
+#define STR_SPACE " "
+#define STR_EXCLAMATION_MARK "!"
+#define STR_QUOTATION_MARK "\""
+#define STR_NUMBER_SIGN "#"
+#define STR_DOLLAR_SIGN "$"
+#define STR_PERCENT_SIGN "%"
+#define STR_AMPERSAND "&"
+#define STR_APOSTROPHE "'"
+#define STR_LEFT_PARENTHESIS "("
+#define STR_RIGHT_PARENTHESIS ")"
+#define STR_ASTERISK "*"
+#define STR_PLUS "+"
+#define STR_COMMA ","
+#define STR_MINUS "-"
+#define STR_DOT "."
+#define STR_SLASH "/"
+#define STR_0 "0"
+#define STR_1 "1"
+#define STR_2 "2"
+#define STR_3 "3"
+#define STR_4 "4"
+#define STR_5 "5"
+#define STR_6 "6"
+#define STR_7 "7"
+#define STR_8 "8"
+#define STR_9 "9"
+#define STR_COLON ":"
+#define STR_SEMICOLON ";"
+#define STR_LESS_THAN_SIGN "<"
+#define STR_EQUALS_SIGN "="
+#define STR_GREATER_THAN_SIGN ">"
+#define STR_QUESTION_MARK "?"
+#define STR_COMMERCIAL_AT "@"
+#define STR_A "A"
+#define STR_B "B"
+#define STR_C "C"
+#define STR_D "D"
+#define STR_E "E"
+#define STR_F "F"
+#define STR_G "G"
+#define STR_H "H"
+#define STR_I "I"
+#define STR_J "J"
+#define STR_K "K"
+#define STR_L "L"
+#define STR_M "M"
+#define STR_N "N"
+#define STR_O "O"
+#define STR_P "P"
+#define STR_Q "Q"
+#define STR_R "R"
+#define STR_S "S"
+#define STR_T "T"
+#define STR_U "U"
+#define STR_V "V"
+#define STR_W "W"
+#define STR_X "X"
+#define STR_Y "Y"
+#define STR_Z "Z"
+#define STR_LEFT_SQUARE_BRACKET "["
+#define STR_BACKSLASH "\\"
+#define STR_RIGHT_SQUARE_BRACKET "]"
+#define STR_CIRCUMFLEX_ACCENT "^"
+#define STR_UNDERSCORE "_"
+#define STR_GRAVE_ACCENT "`"
+#define STR_a "a"
+#define STR_b "b"
+#define STR_c "c"
+#define STR_d "d"
+#define STR_e "e"
+#define STR_f "f"
+#define STR_g "g"
+#define STR_h "h"
+#define STR_i "i"
+#define STR_j "j"
+#define STR_k "k"
+#define STR_l "l"
+#define STR_m "m"
+#define STR_n "n"
+#define STR_o "o"
+#define STR_p "p"
+#define STR_q "q"
+#define STR_r "r"
+#define STR_s "s"
+#define STR_t "t"
+#define STR_u "u"
+#define STR_v "v"
+#define STR_w "w"
+#define STR_x "x"
+#define STR_y "y"
+#define STR_z "z"
+#define STR_LEFT_CURLY_BRACKET "{"
+#define STR_VERTICAL_LINE "|"
+#define STR_RIGHT_CURLY_BRACKET "}"
+#define STR_TILDE "~"
+
+#define STRING_ACCEPT0 "ACCEPT\0"
+#define STRING_COMMIT0 "COMMIT\0"
+#define STRING_F0 "F\0"
+#define STRING_FAIL0 "FAIL\0"
+#define STRING_MARK0 "MARK\0"
+#define STRING_PRUNE0 "PRUNE\0"
+#define STRING_SKIP0 "SKIP\0"
+#define STRING_THEN "THEN"
+
+#define STRING_atomic0 "atomic\0"
+#define STRING_pla0 "pla\0"
+#define STRING_plb0 "plb\0"
+#define STRING_napla0 "napla\0"
+#define STRING_naplb0 "naplb\0"
+#define STRING_nla0 "nla\0"
+#define STRING_nlb0 "nlb\0"
+#define STRING_sr0 "sr\0"
+#define STRING_asr0 "asr\0"
+#define STRING_positive_lookahead0 "positive_lookahead\0"
+#define STRING_positive_lookbehind0 "positive_lookbehind\0"
+#define STRING_non_atomic_positive_lookahead0 "non_atomic_positive_lookahead\0"
+#define STRING_non_atomic_positive_lookbehind0 "non_atomic_positive_lookbehind\0"
+#define STRING_negative_lookahead0 "negative_lookahead\0"
+#define STRING_negative_lookbehind0 "negative_lookbehind\0"
+#define STRING_script_run0 "script_run\0"
+#define STRING_atomic_script_run "atomic_script_run"
+
+#define STRING_alpha0 "alpha\0"
+#define STRING_lower0 "lower\0"
+#define STRING_upper0 "upper\0"
+#define STRING_alnum0 "alnum\0"
+#define STRING_ascii0 "ascii\0"
+#define STRING_blank0 "blank\0"
+#define STRING_cntrl0 "cntrl\0"
+#define STRING_digit0 "digit\0"
+#define STRING_graph0 "graph\0"
+#define STRING_print0 "print\0"
+#define STRING_punct0 "punct\0"
+#define STRING_space0 "space\0"
+#define STRING_word0 "word\0"
+#define STRING_xdigit "xdigit"
+
+#define STRING_DEFINE "DEFINE"
+#define STRING_VERSION "VERSION"
+#define STRING_WEIRD_STARTWORD "[:<:]]"
+#define STRING_WEIRD_ENDWORD "[:>:]]"
+
+#define STRING_CR_RIGHTPAR "CR)"
+#define STRING_LF_RIGHTPAR "LF)"
+#define STRING_CRLF_RIGHTPAR "CRLF)"
+#define STRING_ANY_RIGHTPAR "ANY)"
+#define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)"
+#define STRING_NUL_RIGHTPAR "NUL)"
+#define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)"
+#define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)"
+#define STRING_UTF8_RIGHTPAR "UTF8)"
+#define STRING_UTF16_RIGHTPAR "UTF16)"
+#define STRING_UTF32_RIGHTPAR "UTF32)"
+#define STRING_UTF_RIGHTPAR "UTF)"
+#define STRING_UCP_RIGHTPAR "UCP)"
+#define STRING_NO_AUTO_POSSESS_RIGHTPAR "NO_AUTO_POSSESS)"
+#define STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR "NO_DOTSTAR_ANCHOR)"
+#define STRING_NO_JIT_RIGHTPAR "NO_JIT)"
+#define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)"
+#define STRING_NOTEMPTY_RIGHTPAR "NOTEMPTY)"
+#define STRING_NOTEMPTY_ATSTART_RIGHTPAR "NOTEMPTY_ATSTART)"
+#define STRING_LIMIT_HEAP_EQ "LIMIT_HEAP="
+#define STRING_LIMIT_MATCH_EQ "LIMIT_MATCH="
+#define STRING_LIMIT_DEPTH_EQ "LIMIT_DEPTH="
+#define STRING_LIMIT_RECURSION_EQ "LIMIT_RECURSION="
+#define STRING_MARK "MARK"
+
+#define STRING_bc "bc"
+#define STRING_bidiclass "bidiclass"
+#define STRING_sc "sc"
+#define STRING_script "script"
+#define STRING_scriptextensions "scriptextensions"
+#define STRING_scx "scx"
+
+#else /* SUPPORT_UNICODE */
+
+/* UTF-8 support is enabled; always use UTF-8 (=ASCII) character codes. This
+works in both modes non-EBCDIC platforms, and on EBCDIC platforms in UTF-8 mode
+only. */
+
+#define CHAR_HT '\011'
+#define CHAR_VT '\013'
+#define CHAR_FF '\014'
+#define CHAR_CR '\015'
+#define CHAR_LF '\012'
+#define CHAR_NL CHAR_LF
+#define CHAR_NEL ((unsigned char)'\x85')
+#define CHAR_BS '\010'
+#define CHAR_BEL '\007'
+#define CHAR_ESC '\033'
+#define CHAR_DEL '\177'
+
+#define CHAR_NUL '\0'
+#define CHAR_SPACE '\040'
+#define CHAR_EXCLAMATION_MARK '\041'
+#define CHAR_QUOTATION_MARK '\042'
+#define CHAR_NUMBER_SIGN '\043'
+#define CHAR_DOLLAR_SIGN '\044'
+#define CHAR_PERCENT_SIGN '\045'
+#define CHAR_AMPERSAND '\046'
+#define CHAR_APOSTROPHE '\047'
+#define CHAR_LEFT_PARENTHESIS '\050'
+#define CHAR_RIGHT_PARENTHESIS '\051'
+#define CHAR_ASTERISK '\052'
+#define CHAR_PLUS '\053'
+#define CHAR_COMMA '\054'
+#define CHAR_MINUS '\055'
+#define CHAR_DOT '\056'
+#define CHAR_SLASH '\057'
+#define CHAR_0 '\060'
+#define CHAR_1 '\061'
+#define CHAR_2 '\062'
+#define CHAR_3 '\063'
+#define CHAR_4 '\064'
+#define CHAR_5 '\065'
+#define CHAR_6 '\066'
+#define CHAR_7 '\067'
+#define CHAR_8 '\070'
+#define CHAR_9 '\071'
+#define CHAR_COLON '\072'
+#define CHAR_SEMICOLON '\073'
+#define CHAR_LESS_THAN_SIGN '\074'
+#define CHAR_EQUALS_SIGN '\075'
+#define CHAR_GREATER_THAN_SIGN '\076'
+#define CHAR_QUESTION_MARK '\077'
+#define CHAR_COMMERCIAL_AT '\100'
+#define CHAR_A '\101'
+#define CHAR_B '\102'
+#define CHAR_C '\103'
+#define CHAR_D '\104'
+#define CHAR_E '\105'
+#define CHAR_F '\106'
+#define CHAR_G '\107'
+#define CHAR_H '\110'
+#define CHAR_I '\111'
+#define CHAR_J '\112'
+#define CHAR_K '\113'
+#define CHAR_L '\114'
+#define CHAR_M '\115'
+#define CHAR_N '\116'
+#define CHAR_O '\117'
+#define CHAR_P '\120'
+#define CHAR_Q '\121'
+#define CHAR_R '\122'
+#define CHAR_S '\123'
+#define CHAR_T '\124'
+#define CHAR_U '\125'
+#define CHAR_V '\126'
+#define CHAR_W '\127'
+#define CHAR_X '\130'
+#define CHAR_Y '\131'
+#define CHAR_Z '\132'
+#define CHAR_LEFT_SQUARE_BRACKET '\133'
+#define CHAR_BACKSLASH '\134'
+#define CHAR_RIGHT_SQUARE_BRACKET '\135'
+#define CHAR_CIRCUMFLEX_ACCENT '\136'
+#define CHAR_UNDERSCORE '\137'
+#define CHAR_GRAVE_ACCENT '\140'
+#define CHAR_a '\141'
+#define CHAR_b '\142'
+#define CHAR_c '\143'
+#define CHAR_d '\144'
+#define CHAR_e '\145'
+#define CHAR_f '\146'
+#define CHAR_g '\147'
+#define CHAR_h '\150'
+#define CHAR_i '\151'
+#define CHAR_j '\152'
+#define CHAR_k '\153'
+#define CHAR_l '\154'
+#define CHAR_m '\155'
+#define CHAR_n '\156'
+#define CHAR_o '\157'
+#define CHAR_p '\160'
+#define CHAR_q '\161'
+#define CHAR_r '\162'
+#define CHAR_s '\163'
+#define CHAR_t '\164'
+#define CHAR_u '\165'
+#define CHAR_v '\166'
+#define CHAR_w '\167'
+#define CHAR_x '\170'
+#define CHAR_y '\171'
+#define CHAR_z '\172'
+#define CHAR_LEFT_CURLY_BRACKET '\173'
+#define CHAR_VERTICAL_LINE '\174'
+#define CHAR_RIGHT_CURLY_BRACKET '\175'
+#define CHAR_TILDE '\176'
+#define CHAR_NBSP ((unsigned char)'\xa0')
+
+#define STR_HT "\011"
+#define STR_VT "\013"
+#define STR_FF "\014"
+#define STR_CR "\015"
+#define STR_NL "\012"
+#define STR_BS "\010"
+#define STR_BEL "\007"
+#define STR_ESC "\033"
+#define STR_DEL "\177"
+
+#define STR_SPACE "\040"
+#define STR_EXCLAMATION_MARK "\041"
+#define STR_QUOTATION_MARK "\042"
+#define STR_NUMBER_SIGN "\043"
+#define STR_DOLLAR_SIGN "\044"
+#define STR_PERCENT_SIGN "\045"
+#define STR_AMPERSAND "\046"
+#define STR_APOSTROPHE "\047"
+#define STR_LEFT_PARENTHESIS "\050"
+#define STR_RIGHT_PARENTHESIS "\051"
+#define STR_ASTERISK "\052"
+#define STR_PLUS "\053"
+#define STR_COMMA "\054"
+#define STR_MINUS "\055"
+#define STR_DOT "\056"
+#define STR_SLASH "\057"
+#define STR_0 "\060"
+#define STR_1 "\061"
+#define STR_2 "\062"
+#define STR_3 "\063"
+#define STR_4 "\064"
+#define STR_5 "\065"
+#define STR_6 "\066"
+#define STR_7 "\067"
+#define STR_8 "\070"
+#define STR_9 "\071"
+#define STR_COLON "\072"
+#define STR_SEMICOLON "\073"
+#define STR_LESS_THAN_SIGN "\074"
+#define STR_EQUALS_SIGN "\075"
+#define STR_GREATER_THAN_SIGN "\076"
+#define STR_QUESTION_MARK "\077"
+#define STR_COMMERCIAL_AT "\100"
+#define STR_A "\101"
+#define STR_B "\102"
+#define STR_C "\103"
+#define STR_D "\104"
+#define STR_E "\105"
+#define STR_F "\106"
+#define STR_G "\107"
+#define STR_H "\110"
+#define STR_I "\111"
+#define STR_J "\112"
+#define STR_K "\113"
+#define STR_L "\114"
+#define STR_M "\115"
+#define STR_N "\116"
+#define STR_O "\117"
+#define STR_P "\120"
+#define STR_Q "\121"
+#define STR_R "\122"
+#define STR_S "\123"
+#define STR_T "\124"
+#define STR_U "\125"
+#define STR_V "\126"
+#define STR_W "\127"
+#define STR_X "\130"
+#define STR_Y "\131"
+#define STR_Z "\132"
+#define STR_LEFT_SQUARE_BRACKET "\133"
+#define STR_BACKSLASH "\134"
+#define STR_RIGHT_SQUARE_BRACKET "\135"
+#define STR_CIRCUMFLEX_ACCENT "\136"
+#define STR_UNDERSCORE "\137"
+#define STR_GRAVE_ACCENT "\140"
+#define STR_a "\141"
+#define STR_b "\142"
+#define STR_c "\143"
+#define STR_d "\144"
+#define STR_e "\145"
+#define STR_f "\146"
+#define STR_g "\147"
+#define STR_h "\150"
+#define STR_i "\151"
+#define STR_j "\152"
+#define STR_k "\153"
+#define STR_l "\154"
+#define STR_m "\155"
+#define STR_n "\156"
+#define STR_o "\157"
+#define STR_p "\160"
+#define STR_q "\161"
+#define STR_r "\162"
+#define STR_s "\163"
+#define STR_t "\164"
+#define STR_u "\165"
+#define STR_v "\166"
+#define STR_w "\167"
+#define STR_x "\170"
+#define STR_y "\171"
+#define STR_z "\172"
+#define STR_LEFT_CURLY_BRACKET "\173"
+#define STR_VERTICAL_LINE "\174"
+#define STR_RIGHT_CURLY_BRACKET "\175"
+#define STR_TILDE "\176"
+
+#define STRING_ACCEPT0 STR_A STR_C STR_C STR_E STR_P STR_T "\0"
+#define STRING_COMMIT0 STR_C STR_O STR_M STR_M STR_I STR_T "\0"
+#define STRING_F0 STR_F "\0"
+#define STRING_FAIL0 STR_F STR_A STR_I STR_L "\0"
+#define STRING_MARK0 STR_M STR_A STR_R STR_K "\0"
+#define STRING_PRUNE0 STR_P STR_R STR_U STR_N STR_E "\0"
+#define STRING_SKIP0 STR_S STR_K STR_I STR_P "\0"
+#define STRING_THEN STR_T STR_H STR_E STR_N
+
+#define STRING_atomic0 STR_a STR_t STR_o STR_m STR_i STR_c "\0"
+#define STRING_pla0 STR_p STR_l STR_a "\0"
+#define STRING_plb0 STR_p STR_l STR_b "\0"
+#define STRING_napla0 STR_n STR_a STR_p STR_l STR_a "\0"
+#define STRING_naplb0 STR_n STR_a STR_p STR_l STR_b "\0"
+#define STRING_nla0 STR_n STR_l STR_a "\0"
+#define STRING_nlb0 STR_n STR_l STR_b "\0"
+#define STRING_sr0 STR_s STR_r "\0"
+#define STRING_asr0 STR_a STR_s STR_r "\0"
+#define STRING_positive_lookahead0 STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0"
+#define STRING_positive_lookbehind0 STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0"
+#define STRING_non_atomic_positive_lookahead0 STR_n STR_o STR_n STR_UNDERSCORE STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0"
+#define STRING_non_atomic_positive_lookbehind0 STR_n STR_o STR_n STR_UNDERSCORE STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_p STR_o STR_s STR_i STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0"
+#define STRING_negative_lookahead0 STR_n STR_e STR_g STR_a STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_a STR_h STR_e STR_a STR_d "\0"
+#define STRING_negative_lookbehind0 STR_n STR_e STR_g STR_a STR_t STR_i STR_v STR_e STR_UNDERSCORE STR_l STR_o STR_o STR_k STR_b STR_e STR_h STR_i STR_n STR_d "\0"
+#define STRING_script_run0 STR_s STR_c STR_r STR_i STR_p STR_t STR_UNDERSCORE STR_r STR_u STR_n "\0"
+#define STRING_atomic_script_run STR_a STR_t STR_o STR_m STR_i STR_c STR_UNDERSCORE STR_s STR_c STR_r STR_i STR_p STR_t STR_UNDERSCORE STR_r STR_u STR_n
+
+#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0"
+#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0"
+#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0"
+#define STRING_alnum0 STR_a STR_l STR_n STR_u STR_m "\0"
+#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0"
+#define STRING_blank0 STR_b STR_l STR_a STR_n STR_k "\0"
+#define STRING_cntrl0 STR_c STR_n STR_t STR_r STR_l "\0"
+#define STRING_digit0 STR_d STR_i STR_g STR_i STR_t "\0"
+#define STRING_graph0 STR_g STR_r STR_a STR_p STR_h "\0"
+#define STRING_print0 STR_p STR_r STR_i STR_n STR_t "\0"
+#define STRING_punct0 STR_p STR_u STR_n STR_c STR_t "\0"
+#define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_word0 STR_w STR_o STR_r STR_d "\0"
+#define STRING_xdigit STR_x STR_d STR_i STR_g STR_i STR_t
+
+#define STRING_DEFINE STR_D STR_E STR_F STR_I STR_N STR_E
+#define STRING_VERSION STR_V STR_E STR_R STR_S STR_I STR_O STR_N
+#define STRING_WEIRD_STARTWORD STR_LEFT_SQUARE_BRACKET STR_COLON STR_LESS_THAN_SIGN STR_COLON STR_RIGHT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET
+#define STRING_WEIRD_ENDWORD STR_LEFT_SQUARE_BRACKET STR_COLON STR_GREATER_THAN_SIGN STR_COLON STR_RIGHT_SQUARE_BRACKET STR_RIGHT_SQUARE_BRACKET
+
+#define STRING_CR_RIGHTPAR STR_C STR_R STR_RIGHT_PARENTHESIS
+#define STRING_LF_RIGHTPAR STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS
+#define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_NUL_RIGHTPAR STR_N STR_U STR_L STR_RIGHT_PARENTHESIS
+#define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS
+#define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS
+#define STRING_UTF8_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS
+#define STRING_UTF16_RIGHTPAR STR_U STR_T STR_F STR_1 STR_6 STR_RIGHT_PARENTHESIS
+#define STRING_UTF32_RIGHTPAR STR_U STR_T STR_F STR_3 STR_2 STR_RIGHT_PARENTHESIS
+#define STRING_UTF_RIGHTPAR STR_U STR_T STR_F STR_RIGHT_PARENTHESIS
+#define STRING_UCP_RIGHTPAR STR_U STR_C STR_P STR_RIGHT_PARENTHESIS
+#define STRING_NO_AUTO_POSSESS_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_A STR_U STR_T STR_O STR_UNDERSCORE STR_P STR_O STR_S STR_S STR_E STR_S STR_S STR_RIGHT_PARENTHESIS
+#define STRING_NO_DOTSTAR_ANCHOR_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_D STR_O STR_T STR_S STR_T STR_A STR_R STR_UNDERSCORE STR_A STR_N STR_C STR_H STR_O STR_R STR_RIGHT_PARENTHESIS
+#define STRING_NO_JIT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_J STR_I STR_T STR_RIGHT_PARENTHESIS
+#define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS
+#define STRING_NOTEMPTY_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_RIGHT_PARENTHESIS
+#define STRING_NOTEMPTY_ATSTART_RIGHTPAR STR_N STR_O STR_T STR_E STR_M STR_P STR_T STR_Y STR_UNDERSCORE STR_A STR_T STR_S STR_T STR_A STR_R STR_T STR_RIGHT_PARENTHESIS
+#define STRING_LIMIT_HEAP_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_H STR_E STR_A STR_P STR_EQUALS_SIGN
+#define STRING_LIMIT_MATCH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_M STR_A STR_T STR_C STR_H STR_EQUALS_SIGN
+#define STRING_LIMIT_DEPTH_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_D STR_E STR_P STR_T STR_H STR_EQUALS_SIGN
+#define STRING_LIMIT_RECURSION_EQ STR_L STR_I STR_M STR_I STR_T STR_UNDERSCORE STR_R STR_E STR_C STR_U STR_R STR_S STR_I STR_O STR_N STR_EQUALS_SIGN
+#define STRING_MARK STR_M STR_A STR_R STR_K
+
+#define STRING_bc STR_b STR_c
+#define STRING_bidiclass STR_b STR_i STR_d STR_i STR_c STR_l STR_a STR_s STR_s
+#define STRING_sc STR_s STR_c
+#define STRING_script STR_s STR_c STR_r STR_i STR_p STR_t
+#define STRING_scriptextensions STR_s STR_c STR_r STR_i STR_p STR_t STR_e STR_x STR_t STR_e STR_n STR_s STR_i STR_o STR_n STR_s
+#define STRING_scx STR_s STR_c STR_x
+
+
+#endif /* SUPPORT_UNICODE */
+
+/* -------------------- End of character and string names -------------------*/
+
+/* -------------------- Definitions for compiled patterns -------------------*/
+
+/* Codes for different types of Unicode property. If these definitions are
+changed, the autopossessifying table in pcre2_auto_possess.c must be updated to
+match. */
+
+#define PT_ANY 0 /* Any property - matches all chars */
+#define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */
+#define PT_GC 2 /* Specified general characteristic (e.g. L) */
+#define PT_PC 3 /* Specified particular characteristic (e.g. Lu) */
+#define PT_SC 4 /* Script only (e.g. Han) */
+#define PT_SCX 5 /* Script extensions (includes SC) */
+#define PT_ALNUM 6 /* Alphanumeric - the union of L and N */
+#define PT_SPACE 7 /* Perl space - general category Z plus 9,10,12,13 */
+#define PT_PXSPACE 8 /* POSIX space - Z plus 9,10,11,12,13 */
+#define PT_WORD 9 /* Word - L plus N plus underscore */
+#define PT_CLIST 10 /* Pseudo-property: match character list */
+#define PT_UCNC 11 /* Universal Character nameable character */
+#define PT_BIDICL 12 /* Specified bidi class */
+#define PT_BOOL 13 /* Boolean property */
+#define PT_TABSIZE 14 /* Size of square table for autopossessify tests */
+
+/* The following special properties are used only in XCLASS items, when POSIX
+classes are specified and PCRE2_UCP is set - in other words, for Unicode
+handling of these classes. They are not available via the \p or \P escapes like
+those in the above list, and so they do not take part in the autopossessifying
+table. */
+
+#define PT_PXGRAPH 14 /* [:graph:] - characters that mark the paper */
+#define PT_PXPRINT 15 /* [:print:] - [:graph:] plus non-control spaces */
+#define PT_PXPUNCT 16 /* [:punct:] - punctuation characters */
+
+/* This value is used when parsing \p and \P escapes to indicate that neither
+\p{script:...} nor \p{scx:...} has been encountered. */
+
+#define PT_NOTSCRIPT 255
+
+/* Flag bits and data types for the extended class (OP_XCLASS) for classes that
+contain characters with values greater than 255. */
+
+#define XCL_NOT 0x01 /* Flag: this is a negative class */
+#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */
+#define XCL_HASPROP 0x04 /* Flag: property checks are present. */
+
+#define XCL_END 0 /* Marks end of individual items */
+#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */
+#define XCL_RANGE 2 /* A range (two multibyte chars) follows */
+#define XCL_PROP 3 /* Unicode property (2-byte property code follows) */
+#define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */
+
+/* These are escaped items that aren't just an encoding of a particular data
+value such as \n. They must have non-zero values, as check_escape() returns 0
+for a data character. In the escapes[] table in pcre2_compile.c their values
+are negated in order to distinguish them from data values.
+
+They must appear here in the same order as in the opcode definitions below, up
+to ESC_z. There's a dummy for OP_ALLANY because it corresponds to "." in DOTALL
+mode rather than an escape sequence. It is also used for [^] in JavaScript
+compatibility mode, and for \C in non-utf mode. In non-DOTALL mode, "." behaves
+like \N.
+
+Negative numbers are used to encode a backreference (\1, \2, \3, etc.) in
+check_escape(). There are tests in the code for an escape greater than ESC_b
+and less than ESC_Z to detect the types that may be repeated. These are the
+types that consume characters. If any new escapes are put in between that don't
+consume a character, that code will have to change. */
+
+enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s,
+ ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H,
+ ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z,
+ ESC_E, ESC_Q, ESC_g, ESC_k };
+
+
+/********************** Opcode definitions ******************/
+
+/****** NOTE NOTE NOTE ******
+
+Starting from 1 (i.e. after OP_END), the values up to OP_EOD must correspond in
+order to the list of escapes immediately above. Furthermore, values up to
+OP_DOLLM must not be changed without adjusting the table called autoposstab in
+pcre2_auto_possess.c.
+
+Whenever this list is updated, the two macro definitions that follow must be
+updated to match. The possessification table called "opcode_possessify" in
+pcre2_compile.c must also be updated, and also the tables called "coptable"
+and "poptable" in pcre2_dfa_match.c.
+
+****** NOTE NOTE NOTE ******/
+
+
+/* The values between FIRST_AUTOTAB_OP and LAST_AUTOTAB_RIGHT_OP, inclusive,
+are used in a table for deciding whether a repeated character type can be
+auto-possessified. */
+
+#define FIRST_AUTOTAB_OP OP_NOT_DIGIT
+#define LAST_AUTOTAB_LEFT_OP OP_EXTUNI
+#define LAST_AUTOTAB_RIGHT_OP OP_DOLLM
+
+enum {
+ OP_END, /* 0 End of pattern */
+
+ /* Values corresponding to backslashed metacharacters */
+
+ OP_SOD, /* 1 Start of data: \A */
+ OP_SOM, /* 2 Start of match (subject + offset): \G */
+ OP_SET_SOM, /* 3 Set start of match (\K) */
+ OP_NOT_WORD_BOUNDARY, /* 4 \B */
+ OP_WORD_BOUNDARY, /* 5 \b */
+ OP_NOT_DIGIT, /* 6 \D */
+ OP_DIGIT, /* 7 \d */
+ OP_NOT_WHITESPACE, /* 8 \S */
+ OP_WHITESPACE, /* 9 \s */
+ OP_NOT_WORDCHAR, /* 10 \W */
+ OP_WORDCHAR, /* 11 \w */
+
+ OP_ANY, /* 12 Match any character except newline (\N) */
+ OP_ALLANY, /* 13 Match any character */
+ OP_ANYBYTE, /* 14 Match any byte (\C); different to OP_ANY for UTF-8 */
+ OP_NOTPROP, /* 15 \P (not Unicode property) */
+ OP_PROP, /* 16 \p (Unicode property) */
+ OP_ANYNL, /* 17 \R (any newline sequence) */
+ OP_NOT_HSPACE, /* 18 \H (not horizontal whitespace) */
+ OP_HSPACE, /* 19 \h (horizontal whitespace) */
+ OP_NOT_VSPACE, /* 20 \V (not vertical whitespace) */
+ OP_VSPACE, /* 21 \v (vertical whitespace) */
+ OP_EXTUNI, /* 22 \X (extended Unicode sequence */
+ OP_EODN, /* 23 End of data or \n at end of data (\Z) */
+ OP_EOD, /* 24 End of data (\z) */
+
+ /* Line end assertions */
+
+ OP_DOLL, /* 25 End of line - not multiline */
+ OP_DOLLM, /* 26 End of line - multiline */
+ OP_CIRC, /* 27 Start of line - not multiline */
+ OP_CIRCM, /* 28 Start of line - multiline */
+
+ /* Single characters; caseful must precede the caseless ones, and these
+ must remain in this order, and adjacent. */
+
+ OP_CHAR, /* 29 Match one character, casefully */
+ OP_CHARI, /* 30 Match one character, caselessly */
+ OP_NOT, /* 31 Match one character, not the given one, casefully */
+ OP_NOTI, /* 32 Match one character, not the given one, caselessly */
+
+ /* The following sets of 13 opcodes must always be kept in step because
+ the offset from the first one is used to generate the others. */
+
+ /* Repeated characters; caseful must precede the caseless ones */
+
+ OP_STAR, /* 33 The maximizing and minimizing versions of */
+ OP_MINSTAR, /* 34 these six opcodes must come in pairs, with */
+ OP_PLUS, /* 35 the minimizing one second. */
+ OP_MINPLUS, /* 36 */
+ OP_QUERY, /* 37 */
+ OP_MINQUERY, /* 38 */
+
+ OP_UPTO, /* 39 From 0 to n matches of one character, caseful*/
+ OP_MINUPTO, /* 40 */
+ OP_EXACT, /* 41 Exactly n matches */
+
+ OP_POSSTAR, /* 42 Possessified star, caseful */
+ OP_POSPLUS, /* 43 Possessified plus, caseful */
+ OP_POSQUERY, /* 44 Posesssified query, caseful */
+ OP_POSUPTO, /* 45 Possessified upto, caseful */
+
+ /* Repeated characters; caseless must follow the caseful ones */
+
+ OP_STARI, /* 46 */
+ OP_MINSTARI, /* 47 */
+ OP_PLUSI, /* 48 */
+ OP_MINPLUSI, /* 49 */
+ OP_QUERYI, /* 50 */
+ OP_MINQUERYI, /* 51 */
+
+ OP_UPTOI, /* 52 From 0 to n matches of one character, caseless */
+ OP_MINUPTOI, /* 53 */
+ OP_EXACTI, /* 54 */
+
+ OP_POSSTARI, /* 55 Possessified star, caseless */
+ OP_POSPLUSI, /* 56 Possessified plus, caseless */
+ OP_POSQUERYI, /* 57 Posesssified query, caseless */
+ OP_POSUPTOI, /* 58 Possessified upto, caseless */
+
+ /* The negated ones must follow the non-negated ones, and match them */
+ /* Negated repeated character, caseful; must precede the caseless ones */
+
+ OP_NOTSTAR, /* 59 The maximizing and minimizing versions of */
+ OP_NOTMINSTAR, /* 60 these six opcodes must come in pairs, with */
+ OP_NOTPLUS, /* 61 the minimizing one second. They must be in */
+ OP_NOTMINPLUS, /* 62 exactly the same order as those above. */
+ OP_NOTQUERY, /* 63 */
+ OP_NOTMINQUERY, /* 64 */
+
+ OP_NOTUPTO, /* 65 From 0 to n matches, caseful */
+ OP_NOTMINUPTO, /* 66 */
+ OP_NOTEXACT, /* 67 Exactly n matches */
+
+ OP_NOTPOSSTAR, /* 68 Possessified versions, caseful */
+ OP_NOTPOSPLUS, /* 69 */
+ OP_NOTPOSQUERY, /* 70 */
+ OP_NOTPOSUPTO, /* 71 */
+
+ /* Negated repeated character, caseless; must follow the caseful ones */
+
+ OP_NOTSTARI, /* 72 */
+ OP_NOTMINSTARI, /* 73 */
+ OP_NOTPLUSI, /* 74 */
+ OP_NOTMINPLUSI, /* 75 */
+ OP_NOTQUERYI, /* 76 */
+ OP_NOTMINQUERYI, /* 77 */
+
+ OP_NOTUPTOI, /* 78 From 0 to n matches, caseless */
+ OP_NOTMINUPTOI, /* 79 */
+ OP_NOTEXACTI, /* 80 Exactly n matches */
+
+ OP_NOTPOSSTARI, /* 81 Possessified versions, caseless */
+ OP_NOTPOSPLUSI, /* 82 */
+ OP_NOTPOSQUERYI, /* 83 */
+ OP_NOTPOSUPTOI, /* 84 */
+
+ /* Character types */
+
+ OP_TYPESTAR, /* 85 The maximizing and minimizing versions of */
+ OP_TYPEMINSTAR, /* 86 these six opcodes must come in pairs, with */
+ OP_TYPEPLUS, /* 87 the minimizing one second. These codes must */
+ OP_TYPEMINPLUS, /* 88 be in exactly the same order as those above. */
+ OP_TYPEQUERY, /* 89 */
+ OP_TYPEMINQUERY, /* 90 */
+
+ OP_TYPEUPTO, /* 91 From 0 to n matches */
+ OP_TYPEMINUPTO, /* 92 */
+ OP_TYPEEXACT, /* 93 Exactly n matches */
+
+ OP_TYPEPOSSTAR, /* 94 Possessified versions */
+ OP_TYPEPOSPLUS, /* 95 */
+ OP_TYPEPOSQUERY, /* 96 */
+ OP_TYPEPOSUPTO, /* 97 */
+
+ /* These are used for character classes and back references; only the
+ first six are the same as the sets above. */
+
+ OP_CRSTAR, /* 98 The maximizing and minimizing versions of */
+ OP_CRMINSTAR, /* 99 all these opcodes must come in pairs, with */
+ OP_CRPLUS, /* 100 the minimizing one second. These codes must */
+ OP_CRMINPLUS, /* 101 be in exactly the same order as those above. */
+ OP_CRQUERY, /* 102 */
+ OP_CRMINQUERY, /* 103 */
+
+ OP_CRRANGE, /* 104 These are different to the three sets above. */
+ OP_CRMINRANGE, /* 105 */
+
+ OP_CRPOSSTAR, /* 106 Possessified versions */
+ OP_CRPOSPLUS, /* 107 */
+ OP_CRPOSQUERY, /* 108 */
+ OP_CRPOSRANGE, /* 109 */
+
+ /* End of quantifier opcodes */
+
+ OP_CLASS, /* 110 Match a character class, chars < 256 only */
+ OP_NCLASS, /* 111 Same, but the bitmap was created from a negative
+ class - the difference is relevant only when a
+ character > 255 is encountered. */
+ OP_XCLASS, /* 112 Extended class for handling > 255 chars within the
+ class. This does both positive and negative. */
+ OP_REF, /* 113 Match a back reference, casefully */
+ OP_REFI, /* 114 Match a back reference, caselessly */
+ OP_DNREF, /* 115 Match a duplicate name backref, casefully */
+ OP_DNREFI, /* 116 Match a duplicate name backref, caselessly */
+ OP_RECURSE, /* 117 Match a numbered subpattern (possibly recursive) */
+ OP_CALLOUT, /* 118 Call out to external function if provided */
+ OP_CALLOUT_STR, /* 119 Call out with string argument */
+
+ OP_ALT, /* 120 Start of alternation */
+ OP_KET, /* 121 End of group that doesn't have an unbounded repeat */
+ OP_KETRMAX, /* 122 These two must remain together and in this */
+ OP_KETRMIN, /* 123 order. They are for groups the repeat for ever. */
+ OP_KETRPOS, /* 124 Possessive unlimited repeat. */
+
+ /* The assertions must come before BRA, CBRA, ONCE, and COND. */
+
+ OP_REVERSE, /* 125 Move pointer back - used in lookbehind assertions */
+ OP_ASSERT, /* 126 Positive lookahead */
+ OP_ASSERT_NOT, /* 127 Negative lookahead */
+ OP_ASSERTBACK, /* 128 Positive lookbehind */
+ OP_ASSERTBACK_NOT, /* 129 Negative lookbehind */
+ OP_ASSERT_NA, /* 130 Positive non-atomic lookahead */
+ OP_ASSERTBACK_NA, /* 131 Positive non-atomic lookbehind */
+
+ /* ONCE, SCRIPT_RUN, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come
+ immediately after the assertions, with ONCE first, as there's a test for >=
+ ONCE for a subpattern that isn't an assertion. The POS versions must
+ immediately follow the non-POS versions in each case. */
+
+ OP_ONCE, /* 132 Atomic group, contains captures */
+ OP_SCRIPT_RUN, /* 133 Non-capture, but check characters' scripts */
+ OP_BRA, /* 134 Start of non-capturing bracket */
+ OP_BRAPOS, /* 135 Ditto, with unlimited, possessive repeat */
+ OP_CBRA, /* 136 Start of capturing bracket */
+ OP_CBRAPOS, /* 137 Ditto, with unlimited, possessive repeat */
+ OP_COND, /* 138 Conditional group */
+
+ /* These five must follow the previous five, in the same order. There's a
+ check for >= SBRA to distinguish the two sets. */
+
+ OP_SBRA, /* 139 Start of non-capturing bracket, check empty */
+ OP_SBRAPOS, /* 149 Ditto, with unlimited, possessive repeat */
+ OP_SCBRA, /* 141 Start of capturing bracket, check empty */
+ OP_SCBRAPOS, /* 142 Ditto, with unlimited, possessive repeat */
+ OP_SCOND, /* 143 Conditional group, check empty */
+
+ /* The next two pairs must (respectively) be kept together. */
+
+ OP_CREF, /* 144 Used to hold a capture number as condition */
+ OP_DNCREF, /* 145 Used to point to duplicate names as a condition */
+ OP_RREF, /* 146 Used to hold a recursion number as condition */
+ OP_DNRREF, /* 147 Used to point to duplicate names as a condition */
+ OP_FALSE, /* 148 Always false (used by DEFINE and VERSION) */
+ OP_TRUE, /* 149 Always true (used by VERSION) */
+
+ OP_BRAZERO, /* 150 These two must remain together and in this */
+ OP_BRAMINZERO, /* 151 order. */
+ OP_BRAPOSZERO, /* 152 */
+
+ /* These are backtracking control verbs */
+
+ OP_MARK, /* 153 always has an argument */
+ OP_PRUNE, /* 154 */
+ OP_PRUNE_ARG, /* 155 same, but with argument */
+ OP_SKIP, /* 156 */
+ OP_SKIP_ARG, /* 157 same, but with argument */
+ OP_THEN, /* 158 */
+ OP_THEN_ARG, /* 159 same, but with argument */
+ OP_COMMIT, /* 160 */
+ OP_COMMIT_ARG, /* 161 same, but with argument */
+
+ /* These are forced failure and success verbs. FAIL and ACCEPT do accept an
+ argument, but these cases can be compiled as, for example, (*MARK:X)(*FAIL)
+ without the need for a special opcode. */
+
+ OP_FAIL, /* 162 */
+ OP_ACCEPT, /* 163 */
+ OP_ASSERT_ACCEPT, /* 164 Used inside assertions */
+ OP_CLOSE, /* 165 Used before OP_ACCEPT to close open captures */
+
+ /* This is used to skip a subpattern with a {0} quantifier */
+
+ OP_SKIPZERO, /* 166 */
+
+ /* This is used to identify a DEFINE group during compilation so that it can
+ be checked for having only one branch. It is changed to OP_FALSE before
+ compilation finishes. */
+
+ OP_DEFINE, /* 167 */
+
+ /* This is not an opcode, but is used to check that tables indexed by opcode
+ are the correct length, in order to catch updating errors - there have been
+ some in the past. */
+
+ OP_TABLE_LENGTH
+
+};
+
+/* *** NOTE NOTE NOTE *** Whenever the list above is updated, the two macro
+definitions that follow must also be updated to match. There are also tables
+called "opcode_possessify" in pcre2_compile.c and "coptable" and "poptable" in
+pcre2_dfa_match.c that must be updated. */
+
+
+/* This macro defines textual names for all the opcodes. These are used only
+for debugging, and some of them are only partial names. The macro is referenced
+only in pcre2_printint.c, which fills out the full names in many cases (and in
+some cases doesn't actually use these names at all). */
+
+#define OP_NAME_LIST \
+ "End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \
+ "\\S", "\\s", "\\W", "\\w", "Any", "AllAny", "Anybyte", \
+ "notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \
+ "extuni", "\\Z", "\\z", \
+ "$", "$", "^", "^", "char", "chari", "not", "noti", \
+ "*", "*?", "+", "+?", "?", "??", \
+ "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", \
+ "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", \
+ "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", \
+ "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \
+ "*+","++", "?+", "{", \
+ "*", "*?", "+", "+?", "?", "??", "{", "{", \
+ "*+","++", "?+", "{", \
+ "class", "nclass", "xclass", "Ref", "Refi", "DnRef", "DnRefi", \
+ "Recurse", "Callout", "CalloutStr", \
+ "Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \
+ "Reverse", "Assert", "Assert not", \
+ "Assert back", "Assert back not", \
+ "Non-atomic assert", "Non-atomic assert back", \
+ "Once", \
+ "Script run", \
+ "Bra", "BraPos", "CBra", "CBraPos", \
+ "Cond", \
+ "SBra", "SBraPos", "SCBra", "SCBraPos", \
+ "SCond", \
+ "Cond ref", "Cond dnref", "Cond rec", "Cond dnrec", \
+ "Cond false", "Cond true", \
+ "Brazero", "Braminzero", "Braposzero", \
+ "*MARK", "*PRUNE", "*PRUNE", "*SKIP", "*SKIP", \
+ "*THEN", "*THEN", "*COMMIT", "*COMMIT", "*FAIL", \
+ "*ACCEPT", "*ASSERT_ACCEPT", \
+ "Close", "Skip zero", "Define"
+
+
+/* This macro defines the length of fixed length operations in the compiled
+regex. The lengths are used when searching for specific things, and also in the
+debugging printing of a compiled regex. We use a macro so that it can be
+defined close to the definitions of the opcodes themselves.
+
+As things have been extended, some of these are no longer fixed lenths, but are
+minima instead. For example, the length of a single-character repeat may vary
+in UTF-8 mode. The code that uses this table must know about such things. */
+
+#define OP_LENGTHS \
+ 1, /* End */ \
+ 1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \
+ 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \
+ 1, 1, 1, /* Any, AllAny, Anybyte */ \
+ 3, 3, /* \P, \p */ \
+ 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \
+ 1, /* \X */ \
+ 1, 1, 1, 1, 1, 1, /* \Z, \z, $, $M ^, ^M */ \
+ 2, /* Char - the minimum length */ \
+ 2, /* Chari - the minimum length */ \
+ 2, /* not */ \
+ 2, /* noti */ \
+ /* Positive single-char repeats ** These are */ \
+ 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto, minupto ** mode */ \
+ 2+IMM2_SIZE, /* exact */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* *+, ++, ?+, upto+ */ \
+ 2, 2, 2, 2, 2, 2, /* *I, *?I, +I, +?I, ?I, ??I ** UTF-8 */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto I, minupto I */ \
+ 2+IMM2_SIZE, /* exact I */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */ \
+ /* Negative single-char repeats - only for chars < 256 */ \
+ 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto, minupto */ \
+ 2+IMM2_SIZE, /* NOT exact */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *, +, ?, upto */ \
+ 2, 2, 2, 2, 2, 2, /* NOT *I, *?I, +I, +?I, ?I, ??I */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto I, minupto I */ \
+ 2+IMM2_SIZE, /* NOT exact I */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *I, +I, ?I, upto I */ \
+ /* Positive type repeats */ \
+ 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \
+ 2+IMM2_SIZE, 2+IMM2_SIZE, /* Type upto, minupto */ \
+ 2+IMM2_SIZE, /* Type exact */ \
+ 2, 2, 2, 2+IMM2_SIZE, /* Possessive *+, ++, ?+, upto+ */ \
+ /* Character class & ref repeats */ \
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \
+ 1+2*IMM2_SIZE, 1+2*IMM2_SIZE, /* CRRANGE, CRMINRANGE */ \
+ 1, 1, 1, 1+2*IMM2_SIZE, /* Possessive *+, ++, ?+, CRPOSRANGE */ \
+ 1+(32/sizeof(PCRE2_UCHAR)), /* CLASS */ \
+ 1+(32/sizeof(PCRE2_UCHAR)), /* NCLASS */ \
+ 0, /* XCLASS - variable length */ \
+ 1+IMM2_SIZE, /* REF */ \
+ 1+IMM2_SIZE, /* REFI */ \
+ 1+2*IMM2_SIZE, /* DNREF */ \
+ 1+2*IMM2_SIZE, /* DNREFI */ \
+ 1+LINK_SIZE, /* RECURSE */ \
+ 1+2*LINK_SIZE+1, /* CALLOUT */ \
+ 0, /* CALLOUT_STR - variable length */ \
+ 1+LINK_SIZE, /* Alt */ \
+ 1+LINK_SIZE, /* Ket */ \
+ 1+LINK_SIZE, /* KetRmax */ \
+ 1+LINK_SIZE, /* KetRmin */ \
+ 1+LINK_SIZE, /* KetRpos */ \
+ 1+LINK_SIZE, /* Reverse */ \
+ 1+LINK_SIZE, /* Assert */ \
+ 1+LINK_SIZE, /* Assert not */ \
+ 1+LINK_SIZE, /* Assert behind */ \
+ 1+LINK_SIZE, /* Assert behind not */ \
+ 1+LINK_SIZE, /* NA Assert */ \
+ 1+LINK_SIZE, /* NA Assert behind */ \
+ 1+LINK_SIZE, /* ONCE */ \
+ 1+LINK_SIZE, /* SCRIPT_RUN */ \
+ 1+LINK_SIZE, /* BRA */ \
+ 1+LINK_SIZE, /* BRAPOS */ \
+ 1+LINK_SIZE+IMM2_SIZE, /* CBRA */ \
+ 1+LINK_SIZE+IMM2_SIZE, /* CBRAPOS */ \
+ 1+LINK_SIZE, /* COND */ \
+ 1+LINK_SIZE, /* SBRA */ \
+ 1+LINK_SIZE, /* SBRAPOS */ \
+ 1+LINK_SIZE+IMM2_SIZE, /* SCBRA */ \
+ 1+LINK_SIZE+IMM2_SIZE, /* SCBRAPOS */ \
+ 1+LINK_SIZE, /* SCOND */ \
+ 1+IMM2_SIZE, 1+2*IMM2_SIZE, /* CREF, DNCREF */ \
+ 1+IMM2_SIZE, 1+2*IMM2_SIZE, /* RREF, DNRREF */ \
+ 1, 1, /* FALSE, TRUE */ \
+ 1, 1, 1, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ \
+ 3, 1, 3, /* MARK, PRUNE, PRUNE_ARG */ \
+ 1, 3, /* SKIP, SKIP_ARG */ \
+ 1, 3, /* THEN, THEN_ARG */ \
+ 1, 3, /* COMMIT, COMMIT_ARG */ \
+ 1, 1, 1, /* FAIL, ACCEPT, ASSERT_ACCEPT */ \
+ 1+IMM2_SIZE, 1, /* CLOSE, SKIPZERO */ \
+ 1 /* DEFINE */
+
+/* A magic value for OP_RREF to indicate the "any recursion" condition. */
+
+#define RREF_ANY 0xffff
+
+
+/* ---------- Private structures that are mode-independent. ---------- */
+
+/* Structure to hold data for custom memory management. */
+
+typedef struct pcre2_memctl {
+ void * (*malloc)(size_t, void *);
+ void (*free)(void *, void *);
+ void *memory_data;
+} pcre2_memctl;
+
+/* Structure for building a chain of open capturing subpatterns during
+compiling, so that instructions to close them can be compiled when (*ACCEPT) is
+encountered. */
+
+typedef struct open_capitem {
+ struct open_capitem *next; /* Chain link */
+ uint16_t number; /* Capture number */
+ uint16_t assert_depth; /* Assertion depth when opened */
+} open_capitem;
+
+/* Layout of the UCP type table that translates property names into types and
+codes. Each entry used to point directly to a name, but to reduce the number of
+relocations in shared libraries, it now has an offset into a single string
+instead. */
+
+typedef struct {
+ uint16_t name_offset;
+ uint16_t type;
+ uint16_t value;
+} ucp_type_table;
+
+/* Unicode character database (UCD) record format */
+
+typedef struct {
+ uint8_t script; /* ucp_Arabic, etc. */
+ uint8_t chartype; /* ucp_Cc, etc. (general categories) */
+ uint8_t gbprop; /* ucp_gbControl, etc. (grapheme break property) */
+ uint8_t caseset; /* offset to multichar other cases or zero */
+ int32_t other_case; /* offset to other case, or zero if none */
+ uint16_t scriptx_bidiclass; /* script extension (11 bit) and bidi class (5 bit) values */
+ uint16_t bprops; /* binary properties offset */
+} ucd_record;
+
+/* UCD access macros */
+
+#define UCD_BLOCK_SIZE 128
+#define REAL_GET_UCD(ch) (PRIV(ucd_records) + \
+ PRIV(ucd_stage2)[PRIV(ucd_stage1)[(int)(ch) / UCD_BLOCK_SIZE] * \
+ UCD_BLOCK_SIZE + (int)(ch) % UCD_BLOCK_SIZE])
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+#define GET_UCD(ch) ((ch > MAX_UTF_CODE_POINT)? \
+ PRIV(dummy_ucd_record) : REAL_GET_UCD(ch))
+#else
+#define GET_UCD(ch) REAL_GET_UCD(ch)
+#endif
+
+#define UCD_SCRIPTX_MASK 0x3ff
+#define UCD_BIDICLASS_SHIFT 11
+#define UCD_BPROPS_MASK 0xfff
+
+#define UCD_SCRIPTX_PROP(prop) ((prop)->scriptx_bidiclass & UCD_SCRIPTX_MASK)
+#define UCD_BIDICLASS_PROP(prop) ((prop)->scriptx_bidiclass >> UCD_BIDICLASS_SHIFT)
+#define UCD_BPROPS_PROP(prop) ((prop)->bprops & UCD_BPROPS_MASK)
+
+#define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype
+#define UCD_SCRIPT(ch) GET_UCD(ch)->script
+#define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)]
+#define UCD_GRAPHBREAK(ch) GET_UCD(ch)->gbprop
+#define UCD_CASESET(ch) GET_UCD(ch)->caseset
+#define UCD_OTHERCASE(ch) ((uint32_t)((int)ch + (int)(GET_UCD(ch)->other_case)))
+#define UCD_SCRIPTX(ch) UCD_SCRIPTX_PROP(GET_UCD(ch))
+#define UCD_BPROPS(ch) UCD_BPROPS_PROP(GET_UCD(ch))
+#define UCD_BIDICLASS(ch) UCD_BIDICLASS_PROP(GET_UCD(ch))
+
+/* The "scriptx" and bprops fields contain offsets into vectors of 32-bit words
+that form a bitmap representing a list of scripts or boolean properties. These
+macros test or set a bit in the map by number. */
+
+#define MAPBIT(map,n) ((map)[(n)/32]&(1u<<((n)%32)))
+#define MAPSET(map,n) ((map)[(n)/32]|=(1u<<((n)%32)))
+
+/* Header for serialized pcre2 codes. */
+
+typedef struct pcre2_serialized_data {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t config;
+ int32_t number_of_codes;
+} pcre2_serialized_data;
+
+
+
+/* ----------------- Items that need PCRE2_CODE_UNIT_WIDTH ----------------- */
+
+/* When this file is included by pcre2test, PCRE2_CODE_UNIT_WIDTH is defined as
+0, so the following items are omitted. */
+
+#if defined PCRE2_CODE_UNIT_WIDTH && PCRE2_CODE_UNIT_WIDTH != 0
+
+/* EBCDIC is supported only for the 8-bit library. */
+
+#if defined EBCDIC && PCRE2_CODE_UNIT_WIDTH != 8
+#error EBCDIC is not supported for the 16-bit or 32-bit libraries
+#endif
+
+/* This is the largest non-UTF code point. */
+
+#define MAX_NON_UTF_CHAR (0xffffffffU >> (32 - PCRE2_CODE_UNIT_WIDTH))
+
+/* Internal shared data tables and variables. These are used by more than one
+of the exported public functions. They have to be "external" in the C sense,
+but are not part of the PCRE2 public API. Although the data for some of them is
+identical in all libraries, they must have different names so that multiple
+libraries can be simultaneously linked to a single application. However, UTF-8
+tables are needed only when compiling the 8-bit library. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+extern const int PRIV(utf8_table1)[];
+extern const int PRIV(utf8_table1_size);
+extern const int PRIV(utf8_table2)[];
+extern const int PRIV(utf8_table3)[];
+extern const uint8_t PRIV(utf8_table4)[];
+#endif
+
+#define _pcre2_OP_lengths PCRE2_SUFFIX(_pcre2_OP_lengths_)
+#define _pcre2_callout_end_delims PCRE2_SUFFIX(_pcre2_callout_end_delims_)
+#define _pcre2_callout_start_delims PCRE2_SUFFIX(_pcre2_callout_start_delims_)
+#define _pcre2_default_compile_context PCRE2_SUFFIX(_pcre2_default_compile_context_)
+#define _pcre2_default_convert_context PCRE2_SUFFIX(_pcre2_default_convert_context_)
+#define _pcre2_default_match_context PCRE2_SUFFIX(_pcre2_default_match_context_)
+#define _pcre2_default_tables PCRE2_SUFFIX(_pcre2_default_tables_)
+#if PCRE2_CODE_UNIT_WIDTH == 32
+#define _pcre2_dummy_ucd_record PCRE2_SUFFIX(_pcre2_dummy_ucd_record_)
+#endif
+#define _pcre2_hspace_list PCRE2_SUFFIX(_pcre2_hspace_list_)
+#define _pcre2_vspace_list PCRE2_SUFFIX(_pcre2_vspace_list_)
+#define _pcre2_ucd_boolprop_sets PCRE2_SUFFIX(_pcre2_ucd_boolprop_sets_)
+#define _pcre2_ucd_caseless_sets PCRE2_SUFFIX(_pcre2_ucd_caseless_sets_)
+#define _pcre2_ucd_digit_sets PCRE2_SUFFIX(_pcre2_ucd_digit_sets_)
+#define _pcre2_ucd_script_sets PCRE2_SUFFIX(_pcre2_ucd_script_sets_)
+#define _pcre2_ucd_records PCRE2_SUFFIX(_pcre2_ucd_records_)
+#define _pcre2_ucd_stage1 PCRE2_SUFFIX(_pcre2_ucd_stage1_)
+#define _pcre2_ucd_stage2 PCRE2_SUFFIX(_pcre2_ucd_stage2_)
+#define _pcre2_ucp_gbtable PCRE2_SUFFIX(_pcre2_ucp_gbtable_)
+#define _pcre2_ucp_gentype PCRE2_SUFFIX(_pcre2_ucp_gentype_)
+#define _pcre2_ucp_typerange PCRE2_SUFFIX(_pcre2_ucp_typerange_)
+#define _pcre2_unicode_version PCRE2_SUFFIX(_pcre2_unicode_version_)
+#define _pcre2_utt PCRE2_SUFFIX(_pcre2_utt_)
+#define _pcre2_utt_names PCRE2_SUFFIX(_pcre2_utt_names_)
+#define _pcre2_utt_size PCRE2_SUFFIX(_pcre2_utt_size_)
+
+extern const uint8_t PRIV(OP_lengths)[];
+extern const uint32_t PRIV(callout_end_delims)[];
+extern const uint32_t PRIV(callout_start_delims)[];
+extern const pcre2_compile_context PRIV(default_compile_context);
+extern const pcre2_convert_context PRIV(default_convert_context);
+extern const pcre2_match_context PRIV(default_match_context);
+extern const uint8_t PRIV(default_tables)[];
+extern const uint32_t PRIV(hspace_list)[];
+extern const uint32_t PRIV(vspace_list)[];
+extern const uint32_t PRIV(ucd_boolprop_sets)[];
+extern const uint32_t PRIV(ucd_caseless_sets)[];
+extern const uint32_t PRIV(ucd_digit_sets)[];
+extern const uint32_t PRIV(ucd_script_sets)[];
+extern const ucd_record PRIV(ucd_records)[];
+#if PCRE2_CODE_UNIT_WIDTH == 32
+extern const ucd_record PRIV(dummy_ucd_record)[];
+#endif
+extern const uint16_t PRIV(ucd_stage1)[];
+extern const uint16_t PRIV(ucd_stage2)[];
+extern const uint32_t PRIV(ucp_gbtable)[];
+extern const uint32_t PRIV(ucp_gentype)[];
+#ifdef SUPPORT_JIT
+extern const int PRIV(ucp_typerange)[];
+#endif
+extern const char *PRIV(unicode_version);
+extern const ucp_type_table PRIV(utt)[];
+extern const char PRIV(utt_names)[];
+extern const size_t PRIV(utt_size);
+
+/* Mode-dependent macros and hidden and private structures are defined in a
+separate file so that pcre2test can include them at all supported widths. When
+compiling the library, PCRE2_CODE_UNIT_WIDTH will be defined, and we can
+include them at the appropriate width, after setting up suffix macros for the
+private structures. */
+
+#define branch_chain PCRE2_SUFFIX(branch_chain_)
+#define compile_block PCRE2_SUFFIX(compile_block_)
+#define dfa_match_block PCRE2_SUFFIX(dfa_match_block_)
+#define match_block PCRE2_SUFFIX(match_block_)
+#define named_group PCRE2_SUFFIX(named_group_)
+
+#include "pcre2_intmodedep.h"
+
+/* Private "external" functions. These are internal functions that are called
+from modules other than the one in which they are defined. They have to be
+"external" in the C sense, but are not part of the PCRE2 public API. They are
+not referenced from pcre2test, and must not be defined when no code unit width
+is available. */
+
+#define _pcre2_auto_possessify PCRE2_SUFFIX(_pcre2_auto_possessify_)
+#define _pcre2_check_escape PCRE2_SUFFIX(_pcre2_check_escape_)
+#define _pcre2_extuni PCRE2_SUFFIX(_pcre2_extuni_)
+#define _pcre2_find_bracket PCRE2_SUFFIX(_pcre2_find_bracket_)
+#define _pcre2_is_newline PCRE2_SUFFIX(_pcre2_is_newline_)
+#define _pcre2_jit_free_rodata PCRE2_SUFFIX(_pcre2_jit_free_rodata_)
+#define _pcre2_jit_free PCRE2_SUFFIX(_pcre2_jit_free_)
+#define _pcre2_jit_get_size PCRE2_SUFFIX(_pcre2_jit_get_size_)
+#define _pcre2_jit_get_target PCRE2_SUFFIX(_pcre2_jit_get_target_)
+#define _pcre2_memctl_malloc PCRE2_SUFFIX(_pcre2_memctl_malloc_)
+#define _pcre2_ord2utf PCRE2_SUFFIX(_pcre2_ord2utf_)
+#define _pcre2_script_run PCRE2_SUFFIX(_pcre2_script_run_)
+#define _pcre2_strcmp PCRE2_SUFFIX(_pcre2_strcmp_)
+#define _pcre2_strcmp_c8 PCRE2_SUFFIX(_pcre2_strcmp_c8_)
+#define _pcre2_strcpy_c8 PCRE2_SUFFIX(_pcre2_strcpy_c8_)
+#define _pcre2_strlen PCRE2_SUFFIX(_pcre2_strlen_)
+#define _pcre2_strncmp PCRE2_SUFFIX(_pcre2_strncmp_)
+#define _pcre2_strncmp_c8 PCRE2_SUFFIX(_pcre2_strncmp_c8_)
+#define _pcre2_study PCRE2_SUFFIX(_pcre2_study_)
+#define _pcre2_valid_utf PCRE2_SUFFIX(_pcre2_valid_utf_)
+#define _pcre2_was_newline PCRE2_SUFFIX(_pcre2_was_newline_)
+#define _pcre2_xclass PCRE2_SUFFIX(_pcre2_xclass_)
+
+extern int _pcre2_auto_possessify(PCRE2_UCHAR *,
+ const compile_block *);
+extern int _pcre2_check_escape(PCRE2_SPTR *, PCRE2_SPTR, uint32_t *,
+ int *, uint32_t, uint32_t, BOOL, compile_block *);
+extern PCRE2_SPTR _pcre2_extuni(uint32_t, PCRE2_SPTR, PCRE2_SPTR, PCRE2_SPTR,
+ BOOL, int *);
+extern PCRE2_SPTR _pcre2_find_bracket(PCRE2_SPTR, BOOL, int);
+extern BOOL _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR,
+ uint32_t *, BOOL);
+extern void _pcre2_jit_free_rodata(void *, void *);
+extern void _pcre2_jit_free(void *, pcre2_memctl *);
+extern size_t _pcre2_jit_get_size(void *);
+const char * _pcre2_jit_get_target(void);
+extern void * _pcre2_memctl_malloc(size_t, pcre2_memctl *);
+extern unsigned int _pcre2_ord2utf(uint32_t, PCRE2_UCHAR *);
+extern BOOL _pcre2_script_run(PCRE2_SPTR, PCRE2_SPTR, BOOL);
+extern int _pcre2_strcmp(PCRE2_SPTR, PCRE2_SPTR);
+extern int _pcre2_strcmp_c8(PCRE2_SPTR, const char *);
+extern PCRE2_SIZE _pcre2_strcpy_c8(PCRE2_UCHAR *, const char *);
+extern PCRE2_SIZE _pcre2_strlen(PCRE2_SPTR);
+extern int _pcre2_strncmp(PCRE2_SPTR, PCRE2_SPTR, size_t);
+extern int _pcre2_strncmp_c8(PCRE2_SPTR, const char *, size_t);
+extern int _pcre2_study(pcre2_real_code *);
+extern int _pcre2_valid_utf(PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE *);
+extern BOOL _pcre2_was_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR,
+ uint32_t *, BOOL);
+extern BOOL _pcre2_xclass(uint32_t, PCRE2_SPTR, BOOL);
+
+/* This function is needed only when memmove() is not available. */
+
+#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE)
+#define _pcre2_memmove PCRE2_SUFFIX(_pcre2_memmove)
+extern void * _pcre2_memmove(void *, const void *, size_t);
+#endif
+
+#endif /* PCRE2_CODE_UNIT_WIDTH */
+#endif /* PCRE2_INTERNAL_H_IDEMPOTENT_GUARD */
+
+/* End of pcre2_internal.h */
diff --git a/contrib/libs/pcre2/src/pcre2_intmodedep.h b/contrib/libs/pcre2/src/pcre2_intmodedep.h
new file mode 100644
index 0000000000..390e737a6e
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_intmodedep.h
@@ -0,0 +1,934 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains mode-dependent macro and structure definitions. The
+file is #included by pcre2_internal.h if PCRE2_CODE_UNIT_WIDTH is defined.
+These mode-dependent items are kept in a separate file so that they can also be
+#included multiple times for different code unit widths by pcre2test in order
+to have access to the hidden structures at all supported widths.
+
+Some of the mode-dependent macros are required at different widths for
+different parts of the pcre2test code (in particular, the included
+pcre_printint.c file). We undefine them here so that they can be re-defined for
+multiple inclusions. Not all of these are used in pcre2test, but it's easier
+just to undefine them all. */
+
+#undef ACROSSCHAR
+#undef BACKCHAR
+#undef BYTES2CU
+#undef CHMAX_255
+#undef CU2BYTES
+#undef FORWARDCHAR
+#undef FORWARDCHARTEST
+#undef GET
+#undef GET2
+#undef GETCHAR
+#undef GETCHARINC
+#undef GETCHARINCTEST
+#undef GETCHARLEN
+#undef GETCHARLENTEST
+#undef GETCHARTEST
+#undef GET_EXTRALEN
+#undef HAS_EXTRALEN
+#undef IMM2_SIZE
+#undef MAX_255
+#undef MAX_MARK
+#undef MAX_PATTERN_SIZE
+#undef MAX_UTF_SINGLE_CU
+#undef NOT_FIRSTCU
+#undef PUT
+#undef PUT2
+#undef PUT2INC
+#undef PUTCHAR
+#undef PUTINC
+#undef TABLE_GET
+
+
+
+/* -------------------------- MACROS ----------------------------- */
+
+/* PCRE keeps offsets in its compiled code as at least 16-bit quantities
+(always stored in big-endian order in 8-bit mode) by default. These are used,
+for example, to link from the start of a subpattern to its alternatives and its
+end. The use of 16 bits per offset limits the size of an 8-bit compiled regex
+to around 64K, which is big enough for almost everybody. However, I received a
+request for an even bigger limit. For this reason, and also to make the code
+easier to maintain, the storing and loading of offsets from the compiled code
+unit string is now handled by the macros that are defined here.
+
+The macros are controlled by the value of LINK_SIZE. This defaults to 2, but
+values of 3 or 4 are also supported. */
+
+/* ------------------- 8-bit support ------------------ */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+
+#if LINK_SIZE == 2
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)((d) >> 8)), \
+ (a[(n)+1] = (PCRE2_UCHAR)((d) & 255))
+#define GET(a,n) \
+ (unsigned int)(((a)[n] << 8) | (a)[(n)+1])
+#define MAX_PATTERN_SIZE (1 << 16)
+
+#elif LINK_SIZE == 3
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)((d) >> 16)), \
+ (a[(n)+1] = (PCRE2_UCHAR)((d) >> 8)), \
+ (a[(n)+2] = (PCRE2_UCHAR)((d) & 255))
+#define GET(a,n) \
+ (unsigned int)(((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2])
+#define MAX_PATTERN_SIZE (1 << 24)
+
+#elif LINK_SIZE == 4
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)((d) >> 24)), \
+ (a[(n)+1] = (PCRE2_UCHAR)((d) >> 16)), \
+ (a[(n)+2] = (PCRE2_UCHAR)((d) >> 8)), \
+ (a[(n)+3] = (PCRE2_UCHAR)((d) & 255))
+#define GET(a,n) \
+ (unsigned int)(((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3])
+#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */
+
+#else
+#error LINK_SIZE must be 2, 3, or 4
+#endif
+
+
+/* ------------------- 16-bit support ------------------ */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+
+#if LINK_SIZE == 2
+#undef LINK_SIZE
+#define LINK_SIZE 1
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)(d))
+#define GET(a,n) \
+ (a[n])
+#define MAX_PATTERN_SIZE (1 << 16)
+
+#elif LINK_SIZE == 3 || LINK_SIZE == 4
+#undef LINK_SIZE
+#define LINK_SIZE 2
+#define PUT(a,n,d) \
+ (a[n] = (PCRE2_UCHAR)((d) >> 16)), \
+ (a[(n)+1] = (PCRE2_UCHAR)((d) & 65535))
+#define GET(a,n) \
+ (unsigned int)(((a)[n] << 16) | (a)[(n)+1])
+#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */
+
+#else
+#error LINK_SIZE must be 2, 3, or 4
+#endif
+
+
+/* ------------------- 32-bit support ------------------ */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+#undef LINK_SIZE
+#define LINK_SIZE 1
+#define PUT(a,n,d) \
+ (a[n] = (d))
+#define GET(a,n) \
+ (a[n])
+#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */
+
+#else
+#error Unsupported compiling mode
+#endif
+
+
+/* --------------- Other mode-specific macros ----------------- */
+
+/* PCRE uses some other (at least) 16-bit quantities that do not change when
+the size of offsets changes. There are used for repeat counts and for other
+things such as capturing parenthesis numbers in back references.
+
+Define the number of code units required to hold a 16-bit count/offset, and
+macros to load and store such a value. For reasons that I do not understand,
+the expression in the 8-bit GET2 macro is treated by gcc as a signed
+expression, even when a is declared as unsigned. It seems that any kind of
+arithmetic results in a signed value. Hence the cast. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define IMM2_SIZE 2
+#define GET2(a,n) (unsigned int)(((a)[n] << 8) | (a)[(n)+1])
+#define PUT2(a,n,d) a[n] = (d) >> 8, a[(n)+1] = (d) & 255
+
+#else /* Code units are 16 or 32 bits */
+#define IMM2_SIZE 1
+#define GET2(a,n) a[n]
+#define PUT2(a,n,d) a[n] = d
+#endif
+
+/* Other macros that are different for 8-bit mode. The MAX_255 macro checks
+whether its argument, which is assumed to be one code unit, is less than 256.
+The CHMAX_255 macro does not assume one code unit. The maximum length of a MARK
+name must fit in one code unit; currently it is set to 255 or 65535. The
+TABLE_GET macro is used to access elements of tables containing exactly 256
+items. Its argument is a code unit. When code points can be greater than 255, a
+check is needed before accessing these tables. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define MAX_255(c) TRUE
+#define MAX_MARK ((1u << 8) - 1)
+#define TABLE_GET(c, table, default) ((table)[c])
+#ifdef SUPPORT_UNICODE
+#define SUPPORT_WIDE_CHARS
+#define CHMAX_255(c) ((c) <= 255u)
+#else
+#define CHMAX_255(c) TRUE
+#endif /* SUPPORT_UNICODE */
+
+#else /* Code units are 16 or 32 bits */
+#define CHMAX_255(c) ((c) <= 255u)
+#define MAX_255(c) ((c) <= 255u)
+#define MAX_MARK ((1u << 16) - 1)
+#define SUPPORT_WIDE_CHARS
+#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default))
+#endif
+
+
+/* ----------------- Character-handling macros ----------------- */
+
+/* There is a proposed future special "UTF-21" mode, in which only the lowest
+21 bits of a 32-bit character are interpreted as UTF, with the remaining 11
+high-order bits available to the application for other uses. In preparation for
+the future implementation of this mode, there are macros that load a data item
+and, if in this special mode, mask it to 21 bits. These macros all have names
+starting with UCHAR21. In all other modes, including the normal 32-bit
+library, the macros all have the same simple definitions. When the new mode is
+implemented, it is expected that these definitions will be varied appropriately
+using #ifdef when compiling the library that supports the special mode. */
+
+#define UCHAR21(eptr) (*(eptr))
+#define UCHAR21TEST(eptr) (*(eptr))
+#define UCHAR21INC(eptr) (*(eptr)++)
+#define UCHAR21INCTEST(eptr) (*(eptr)++)
+
+/* When UTF encoding is being used, a character is no longer just a single
+byte in 8-bit mode or a single short in 16-bit mode. The macros for character
+handling generate simple sequences when used in the basic mode, and more
+complicated ones for UTF characters. GETCHARLENTEST and other macros are not
+used when UTF is not supported. To make sure they can never even appear when
+UTF support is omitted, we don't even define them. */
+
+#ifndef SUPPORT_UNICODE
+
+/* #define MAX_UTF_SINGLE_CU */
+/* #define HAS_EXTRALEN(c) */
+/* #define GET_EXTRALEN(c) */
+/* #define NOT_FIRSTCU(c) */
+#define GETCHAR(c, eptr) c = *eptr;
+#define GETCHARTEST(c, eptr) c = *eptr;
+#define GETCHARINC(c, eptr) c = *eptr++;
+#define GETCHARINCTEST(c, eptr) c = *eptr++;
+#define GETCHARLEN(c, eptr, len) c = *eptr;
+#define PUTCHAR(c, p) (*p = c, 1)
+/* #define GETCHARLENTEST(c, eptr, len) */
+/* #define BACKCHAR(eptr) */
+/* #define FORWARDCHAR(eptr) */
+/* #define FORWARCCHARTEST(eptr,end) */
+/* #define ACROSSCHAR(condition, eptr, action) */
+
+#else /* SUPPORT_UNICODE */
+
+/* ------------------- 8-bit support ------------------ */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define MAYBE_UTF_MULTI /* UTF chars may use multiple code units */
+
+/* The largest UTF code point that can be encoded as a single code unit. */
+
+#define MAX_UTF_SINGLE_CU 127
+
+/* Tests whether the code point needs extra characters to decode. */
+
+#define HAS_EXTRALEN(c) HASUTF8EXTRALEN(c)
+
+/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE.
+Otherwise it has an undefined behaviour. */
+
+#define GET_EXTRALEN(c) (PRIV(utf8_table4)[(c) & 0x3fu])
+
+/* Returns TRUE, if the given value is not the first code unit of a UTF
+sequence. */
+
+#define NOT_FIRSTCU(c) (((c) & 0xc0u) == 0x80u)
+
+/* Get the next UTF-8 character, not advancing the pointer. This is called when
+we know we are in UTF-8 mode. */
+
+#define GETCHAR(c, eptr) \
+ c = *eptr; \
+ if (c >= 0xc0u) GETUTF8(c, eptr);
+
+/* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the
+pointer. */
+
+#define GETCHARTEST(c, eptr) \
+ c = *eptr; \
+ if (utf && c >= 0xc0u) GETUTF8(c, eptr);
+
+/* Get the next UTF-8 character, advancing the pointer. This is called when we
+know we are in UTF-8 mode. */
+
+#define GETCHARINC(c, eptr) \
+ c = *eptr++; \
+ if (c >= 0xc0u) GETUTF8INC(c, eptr);
+
+/* Get the next character, testing for UTF-8 mode, and advancing the pointer.
+This is called when we don't know if we are in UTF-8 mode. */
+
+#define GETCHARINCTEST(c, eptr) \
+ c = *eptr++; \
+ if (utf && c >= 0xc0u) GETUTF8INC(c, eptr);
+
+/* Get the next UTF-8 character, not advancing the pointer, incrementing length
+if there are extra bytes. This is called when we know we are in UTF-8 mode. */
+
+#define GETCHARLEN(c, eptr, len) \
+ c = *eptr; \
+ if (c >= 0xc0u) GETUTF8LEN(c, eptr, len);
+
+/* Get the next UTF-8 character, testing for UTF-8 mode, not advancing the
+pointer, incrementing length if there are extra bytes. This is called when we
+do not know if we are in UTF-8 mode. */
+
+#define GETCHARLENTEST(c, eptr, len) \
+ c = *eptr; \
+ if (utf && c >= 0xc0u) GETUTF8LEN(c, eptr, len);
+
+/* If the pointer is not at the start of a character, move it back until
+it is. This is called only in UTF-8 mode - we don't put a test within the macro
+because almost all calls are already within a block of UTF-8 only code. */
+
+#define BACKCHAR(eptr) while((*eptr & 0xc0u) == 0x80u) eptr--
+
+/* Same as above, just in the other direction. */
+#define FORWARDCHAR(eptr) while((*eptr & 0xc0u) == 0x80u) eptr++
+#define FORWARDCHARTEST(eptr,end) while(eptr < end && (*eptr & 0xc0u) == 0x80u) eptr++
+
+/* Same as above, but it allows a fully customizable form. */
+#define ACROSSCHAR(condition, eptr, action) \
+ while((condition) && ((*eptr) & 0xc0u) == 0x80u) action
+
+/* Deposit a character into memory, returning the number of code units. */
+
+#define PUTCHAR(c, p) ((utf && c > MAX_UTF_SINGLE_CU)? \
+ PRIV(ord2utf)(c,p) : (*p = c, 1))
+
+
+/* ------------------- 16-bit support ------------------ */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#define MAYBE_UTF_MULTI /* UTF chars may use multiple code units */
+
+/* The largest UTF code point that can be encoded as a single code unit. */
+
+#define MAX_UTF_SINGLE_CU 65535
+
+/* Tests whether the code point needs extra characters to decode. */
+
+#define HAS_EXTRALEN(c) (((c) & 0xfc00u) == 0xd800u)
+
+/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE.
+Otherwise it has an undefined behaviour. */
+
+#define GET_EXTRALEN(c) 1
+
+/* Returns TRUE, if the given value is not the first code unit of a UTF
+sequence. */
+
+#define NOT_FIRSTCU(c) (((c) & 0xfc00u) == 0xdc00u)
+
+/* Base macro to pick up the low surrogate of a UTF-16 character, not
+advancing the pointer. */
+
+#define GETUTF16(c, eptr) \
+ { c = (((c & 0x3ffu) << 10) | (eptr[1] & 0x3ffu)) + 0x10000u; }
+
+/* Get the next UTF-16 character, not advancing the pointer. This is called when
+we know we are in UTF-16 mode. */
+
+#define GETCHAR(c, eptr) \
+ c = *eptr; \
+ if ((c & 0xfc00u) == 0xd800u) GETUTF16(c, eptr);
+
+/* Get the next UTF-16 character, testing for UTF-16 mode, and not advancing the
+pointer. */
+
+#define GETCHARTEST(c, eptr) \
+ c = *eptr; \
+ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16(c, eptr);
+
+/* Base macro to pick up the low surrogate of a UTF-16 character, advancing
+the pointer. */
+
+#define GETUTF16INC(c, eptr) \
+ { c = (((c & 0x3ffu) << 10) | (*eptr++ & 0x3ffu)) + 0x10000u; }
+
+/* Get the next UTF-16 character, advancing the pointer. This is called when we
+know we are in UTF-16 mode. */
+
+#define GETCHARINC(c, eptr) \
+ c = *eptr++; \
+ if ((c & 0xfc00u) == 0xd800u) GETUTF16INC(c, eptr);
+
+/* Get the next character, testing for UTF-16 mode, and advancing the pointer.
+This is called when we don't know if we are in UTF-16 mode. */
+
+#define GETCHARINCTEST(c, eptr) \
+ c = *eptr++; \
+ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16INC(c, eptr);
+
+/* Base macro to pick up the low surrogate of a UTF-16 character, not
+advancing the pointer, incrementing the length. */
+
+#define GETUTF16LEN(c, eptr, len) \
+ { c = (((c & 0x3ffu) << 10) | (eptr[1] & 0x3ffu)) + 0x10000u; len++; }
+
+/* Get the next UTF-16 character, not advancing the pointer, incrementing
+length if there is a low surrogate. This is called when we know we are in
+UTF-16 mode. */
+
+#define GETCHARLEN(c, eptr, len) \
+ c = *eptr; \
+ if ((c & 0xfc00u) == 0xd800u) GETUTF16LEN(c, eptr, len);
+
+/* Get the next UTF-816character, testing for UTF-16 mode, not advancing the
+pointer, incrementing length if there is a low surrogate. This is called when
+we do not know if we are in UTF-16 mode. */
+
+#define GETCHARLENTEST(c, eptr, len) \
+ c = *eptr; \
+ if (utf && (c & 0xfc00u) == 0xd800u) GETUTF16LEN(c, eptr, len);
+
+/* If the pointer is not at the start of a character, move it back until
+it is. This is called only in UTF-16 mode - we don't put a test within the
+macro because almost all calls are already within a block of UTF-16 only
+code. */
+
+#define BACKCHAR(eptr) if ((*eptr & 0xfc00u) == 0xdc00u) eptr--
+
+/* Same as above, just in the other direction. */
+#define FORWARDCHAR(eptr) if ((*eptr & 0xfc00u) == 0xdc00u) eptr++
+#define FORWARDCHARTEST(eptr,end) if (eptr < end && (*eptr & 0xfc00u) == 0xdc00u) eptr++
+
+/* Same as above, but it allows a fully customizable form. */
+#define ACROSSCHAR(condition, eptr, action) \
+ if ((condition) && ((*eptr) & 0xfc00u) == 0xdc00u) action
+
+/* Deposit a character into memory, returning the number of code units. */
+
+#define PUTCHAR(c, p) ((utf && c > MAX_UTF_SINGLE_CU)? \
+ PRIV(ord2utf)(c,p) : (*p = c, 1))
+
+
+/* ------------------- 32-bit support ------------------ */
+
+#else
+
+/* These are trivial for the 32-bit library, since all UTF-32 characters fit
+into one PCRE2_UCHAR unit. */
+
+#define MAX_UTF_SINGLE_CU (0x10ffffu)
+#define HAS_EXTRALEN(c) (0)
+#define GET_EXTRALEN(c) (0)
+#define NOT_FIRSTCU(c) (0)
+
+/* Get the next UTF-32 character, not advancing the pointer. This is called when
+we know we are in UTF-32 mode. */
+
+#define GETCHAR(c, eptr) \
+ c = *(eptr);
+
+/* Get the next UTF-32 character, testing for UTF-32 mode, and not advancing the
+pointer. */
+
+#define GETCHARTEST(c, eptr) \
+ c = *(eptr);
+
+/* Get the next UTF-32 character, advancing the pointer. This is called when we
+know we are in UTF-32 mode. */
+
+#define GETCHARINC(c, eptr) \
+ c = *((eptr)++);
+
+/* Get the next character, testing for UTF-32 mode, and advancing the pointer.
+This is called when we don't know if we are in UTF-32 mode. */
+
+#define GETCHARINCTEST(c, eptr) \
+ c = *((eptr)++);
+
+/* Get the next UTF-32 character, not advancing the pointer, not incrementing
+length (since all UTF-32 is of length 1). This is called when we know we are in
+UTF-32 mode. */
+
+#define GETCHARLEN(c, eptr, len) \
+ GETCHAR(c, eptr)
+
+/* Get the next UTF-32character, testing for UTF-32 mode, not advancing the
+pointer, not incrementing the length (since all UTF-32 is of length 1).
+This is called when we do not know if we are in UTF-32 mode. */
+
+#define GETCHARLENTEST(c, eptr, len) \
+ GETCHARTEST(c, eptr)
+
+/* If the pointer is not at the start of a character, move it back until
+it is. This is called only in UTF-32 mode - we don't put a test within the
+macro because almost all calls are already within a block of UTF-32 only
+code.
+
+These are all no-ops since all UTF-32 characters fit into one PCRE2_UCHAR. */
+
+#define BACKCHAR(eptr) do { } while (0)
+
+/* Same as above, just in the other direction. */
+
+#define FORWARDCHAR(eptr) do { } while (0)
+#define FORWARDCHARTEST(eptr,end) do { } while (0)
+
+/* Same as above, but it allows a fully customizable form. */
+
+#define ACROSSCHAR(condition, eptr, action) do { } while (0)
+
+/* Deposit a character into memory, returning the number of code units. */
+
+#define PUTCHAR(c, p) (*p = c, 1)
+
+#endif /* UTF-32 character handling */
+#endif /* SUPPORT_UNICODE */
+
+
+/* Mode-dependent macros that have the same definition in all modes. */
+
+#define CU2BYTES(x) ((x)*((PCRE2_CODE_UNIT_WIDTH/8)))
+#define BYTES2CU(x) ((x)/((PCRE2_CODE_UNIT_WIDTH/8)))
+#define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE
+#define PUT2INC(a,n,d) PUT2(a,n,d), a += IMM2_SIZE
+
+
+/* ----------------------- HIDDEN STRUCTURES ----------------------------- */
+
+/* NOTE: All these structures *must* start with a pcre2_memctl structure. The
+code that uses them is simpler because it assumes this. */
+
+/* The real general context structure. At present it holds only data for custom
+memory control. */
+
+typedef struct pcre2_real_general_context {
+ pcre2_memctl memctl;
+} pcre2_real_general_context;
+
+/* The real compile context structure */
+
+typedef struct pcre2_real_compile_context {
+ pcre2_memctl memctl;
+ int (*stack_guard)(uint32_t, void *);
+ void *stack_guard_data;
+ const uint8_t *tables;
+ PCRE2_SIZE max_pattern_length;
+ uint16_t bsr_convention;
+ uint16_t newline_convention;
+ uint32_t parens_nest_limit;
+ uint32_t extra_options;
+} pcre2_real_compile_context;
+
+/* The real match context structure. */
+
+typedef struct pcre2_real_match_context {
+ pcre2_memctl memctl;
+#ifdef SUPPORT_JIT
+ pcre2_jit_callback jit_callback;
+ void *jit_callback_data;
+#endif
+ int (*callout)(pcre2_callout_block *, void *);
+ void *callout_data;
+ int (*substitute_callout)(pcre2_substitute_callout_block *, void *);
+ void *substitute_callout_data;
+ PCRE2_SIZE offset_limit;
+ uint32_t heap_limit;
+ uint32_t match_limit;
+ uint32_t depth_limit;
+} pcre2_real_match_context;
+
+/* The real convert context structure. */
+
+typedef struct pcre2_real_convert_context {
+ pcre2_memctl memctl;
+ uint32_t glob_separator;
+ uint32_t glob_escape;
+} pcre2_real_convert_context;
+
+/* The real compiled code structure. The type for the blocksize field is
+defined specially because it is required in pcre2_serialize_decode() when
+copying the size from possibly unaligned memory into a variable of the same
+type. Use a macro rather than a typedef to avoid compiler warnings when this
+file is included multiple times by pcre2test. LOOKBEHIND_MAX specifies the
+largest lookbehind that is supported. (OP_REVERSE in a pattern has a 16-bit
+argument in 8-bit and 16-bit modes, so we need no more than a 16-bit field
+here.) */
+
+#undef CODE_BLOCKSIZE_TYPE
+#define CODE_BLOCKSIZE_TYPE size_t
+
+#undef LOOKBEHIND_MAX
+#define LOOKBEHIND_MAX UINT16_MAX
+
+typedef struct pcre2_real_code {
+ pcre2_memctl memctl; /* Memory control fields */
+ const uint8_t *tables; /* The character tables */
+ void *executable_jit; /* Pointer to JIT code */
+ uint8_t start_bitmap[32]; /* Bitmap for starting code unit < 256 */
+ CODE_BLOCKSIZE_TYPE blocksize; /* Total (bytes) that was malloc-ed */
+ uint32_t magic_number; /* Paranoid and endianness check */
+ uint32_t compile_options; /* Options passed to pcre2_compile() */
+ uint32_t overall_options; /* Options after processing the pattern */
+ uint32_t extra_options; /* Taken from compile_context */
+ uint32_t flags; /* Various state flags */
+ uint32_t limit_heap; /* Limit set in the pattern */
+ uint32_t limit_match; /* Limit set in the pattern */
+ uint32_t limit_depth; /* Limit set in the pattern */
+ uint32_t first_codeunit; /* Starting code unit */
+ uint32_t last_codeunit; /* This codeunit must be seen */
+ uint16_t bsr_convention; /* What \R matches */
+ uint16_t newline_convention; /* What is a newline? */
+ uint16_t max_lookbehind; /* Longest lookbehind (characters) */
+ uint16_t minlength; /* Minimum length of match */
+ uint16_t top_bracket; /* Highest numbered group */
+ uint16_t top_backref; /* Highest numbered back reference */
+ uint16_t name_entry_size; /* Size (code units) of table entries */
+ uint16_t name_count; /* Number of name entries in the table */
+} pcre2_real_code;
+
+/* The real match data structure. Define ovector as large as it can ever
+actually be so that array bound checkers don't grumble. Memory for this
+structure is obtained by calling pcre2_match_data_create(), which sets the size
+as the offset of ovector plus a pair of elements for each capturable string, so
+the size varies from call to call. As the maximum number of capturing
+subpatterns is 65535 we must allow for 65536 strings to include the overall
+match. (See also the heapframe structure below.) */
+
+struct heapframe; /* Forward reference */
+
+typedef struct pcre2_real_match_data {
+ pcre2_memctl memctl; /* Memory control fields */
+ const pcre2_real_code *code; /* The pattern used for the match */
+ PCRE2_SPTR subject; /* The subject that was matched */
+ PCRE2_SPTR mark; /* Pointer to last mark */
+ struct heapframe *heapframes; /* Backtracking frames heap memory */
+ PCRE2_SIZE heapframes_size; /* Malloc-ed size */
+ PCRE2_SIZE leftchar; /* Offset to leftmost code unit */
+ PCRE2_SIZE rightchar; /* Offset to rightmost code unit */
+ PCRE2_SIZE startchar; /* Offset to starting code unit */
+ uint8_t matchedby; /* Type of match (normal, JIT, DFA) */
+ uint8_t flags; /* Various flags */
+ uint16_t oveccount; /* Number of pairs */
+ int rc; /* The return code from the match */
+ PCRE2_SIZE ovector[131072]; /* Must be last in the structure */
+} pcre2_real_match_data;
+
+
+/* ----------------------- PRIVATE STRUCTURES ----------------------------- */
+
+/* These structures are not needed for pcre2test. */
+
+#ifndef PCRE2_PCRE2TEST
+
+/* Structures for checking for mutual recursion when scanning compiled or
+parsed code. */
+
+typedef struct recurse_check {
+ struct recurse_check *prev;
+ PCRE2_SPTR group;
+} recurse_check;
+
+typedef struct parsed_recurse_check {
+ struct parsed_recurse_check *prev;
+ uint32_t *groupptr;
+} parsed_recurse_check;
+
+/* Structure for building a cache when filling in recursion offsets. */
+
+typedef struct recurse_cache {
+ PCRE2_SPTR group;
+ int groupnumber;
+} recurse_cache;
+
+/* Structure for maintaining a chain of pointers to the currently incomplete
+branches, for testing for left recursion while compiling. */
+
+typedef struct branch_chain {
+ struct branch_chain *outer;
+ PCRE2_UCHAR *current_branch;
+} branch_chain;
+
+/* Structure for building a list of named groups during the first pass of
+compiling. */
+
+typedef struct named_group {
+ PCRE2_SPTR name; /* Points to the name in the pattern */
+ uint32_t number; /* Group number */
+ uint16_t length; /* Length of the name */
+ uint16_t isdup; /* TRUE if a duplicate */
+} named_group;
+
+/* Structure for passing "static" information around between the functions
+doing the compiling, so that they are thread-safe. */
+
+typedef struct compile_block {
+ pcre2_real_compile_context *cx; /* Points to the compile context */
+ const uint8_t *lcc; /* Points to lower casing table */
+ const uint8_t *fcc; /* Points to case-flipping table */
+ const uint8_t *cbits; /* Points to character type table */
+ const uint8_t *ctypes; /* Points to table of type maps */
+ PCRE2_SPTR start_workspace; /* The start of working space */
+ PCRE2_SPTR start_code; /* The start of the compiled code */
+ PCRE2_SPTR start_pattern; /* The start of the pattern */
+ PCRE2_SPTR end_pattern; /* The end of the pattern */
+ PCRE2_UCHAR *name_table; /* The name/number table */
+ PCRE2_SIZE workspace_size; /* Size of workspace */
+ PCRE2_SIZE small_ref_offset[10]; /* Offsets for \1 to \9 */
+ PCRE2_SIZE erroroffset; /* Offset of error in pattern */
+ uint16_t names_found; /* Number of entries so far */
+ uint16_t name_entry_size; /* Size of each entry */
+ uint16_t parens_depth; /* Depth of nested parentheses */
+ uint16_t assert_depth; /* Depth of nested assertions */
+ open_capitem *open_caps; /* Chain of open capture items */
+ named_group *named_groups; /* Points to vector in pre-compile */
+ uint32_t named_group_list_size; /* Number of entries in the list */
+ uint32_t external_options; /* External (initial) options */
+ uint32_t external_flags; /* External flag bits to be set */
+ uint32_t bracount; /* Count of capturing parentheses */
+ uint32_t lastcapture; /* Last capture encountered */
+ uint32_t *parsed_pattern; /* Parsed pattern buffer */
+ uint32_t *parsed_pattern_end; /* Parsed pattern should not get here */
+ uint32_t *groupinfo; /* Group info vector */
+ uint32_t top_backref; /* Maximum back reference */
+ uint32_t backref_map; /* Bitmap of low back refs */
+ uint32_t nltype; /* Newline type */
+ uint32_t nllen; /* Newline string length */
+ uint32_t class_range_start; /* Overall class range start */
+ uint32_t class_range_end; /* Overall class range end */
+ PCRE2_UCHAR nl[4]; /* Newline string when fixed length */
+ uint32_t req_varyopt; /* "After variable item" flag for reqbyte */
+ int max_lookbehind; /* Maximum lookbehind (characters) */
+ BOOL had_accept; /* (*ACCEPT) encountered */
+ BOOL had_pruneorskip; /* (*PRUNE) or (*SKIP) encountered */
+ BOOL had_recurse; /* Had a recursion or subroutine call */
+ BOOL dupnames; /* Duplicate names exist */
+} compile_block;
+
+/* Structure for keeping the properties of the in-memory stack used
+by the JIT matcher. */
+
+typedef struct pcre2_real_jit_stack {
+ pcre2_memctl memctl;
+ void* stack;
+} pcre2_real_jit_stack;
+
+/* Structure for items in a linked list that represents an explicit recursive
+call within the pattern when running pcre2_dfa_match(). */
+
+typedef struct dfa_recursion_info {
+ struct dfa_recursion_info *prevrec;
+ PCRE2_SPTR subject_position;
+ uint32_t group_num;
+} dfa_recursion_info;
+
+/* Structure for "stack" frames that are used for remembering backtracking
+positions during matching. As these are used in a vector, with the ovector item
+being extended, the size of the structure must be a multiple of PCRE2_SIZE. The
+only way to check this at compile time is to force an error by generating an
+array with a negative size. By putting this in a typedef (which is never used),
+we don't generate any code when all is well. */
+
+typedef struct heapframe {
+
+ /* The first set of fields are variables that have to be preserved over calls
+ to RRMATCH(), but which do not need to be copied to new frames. */
+
+ PCRE2_SPTR ecode; /* The current position in the pattern */
+ PCRE2_SPTR temp_sptr[2]; /* Used for short-term PCRE_SPTR values */
+ PCRE2_SIZE length; /* Used for character, string, or code lengths */
+ PCRE2_SIZE back_frame; /* Amount to subtract on RRETURN */
+ PCRE2_SIZE temp_size; /* Used for short-term PCRE2_SIZE values */
+ uint32_t rdepth; /* "Recursion" depth */
+ uint32_t group_frame_type; /* Type information for group frames */
+ uint32_t temp_32[4]; /* Used for short-term 32-bit or BOOL values */
+ uint8_t return_id; /* Where to go on in internal "return" */
+ uint8_t op; /* Processing opcode */
+
+ /* At this point, the structure is 16-bit aligned. On most architectures
+ the alignment requirement for a pointer will ensure that the eptr field below
+ is 32-bit or 64-bit aligned. However, on m68k it is fine to have a pointer
+ that is 16-bit aligned. We must therefore ensure that what comes between here
+ and eptr is an odd multiple of 16 bits so as to get back into 32-bit
+ alignment. This happens naturally when PCRE2_UCHAR is 8 bits wide, but needs
+ fudges in the other cases. In the 32-bit case the padding comes first so that
+ the occu field itself is 32-bit aligned. Without the padding, this structure
+ is no longer a multiple of PCRE2_SIZE on m68k, and the check below fails. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ PCRE2_UCHAR occu[6]; /* Used for other case code units */
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ PCRE2_UCHAR occu[2]; /* Used for other case code units */
+ uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */
+#else
+ uint8_t unused[2]; /* Ensure 32-bit alignment (see above) */
+ PCRE2_UCHAR occu[1]; /* Used for other case code units */
+#endif
+
+ /* The rest have to be copied from the previous frame whenever a new frame
+ becomes current. The final field is specified as a large vector so that
+ runtime array bound checks don't catch references to it. However, for any
+ specific call to pcre2_match() the memory allocated for each frame structure
+ allows for exactly the right size ovector for the number of capturing
+ parentheses. (See also the comment for pcre2_real_match_data above.) */
+
+ PCRE2_SPTR eptr; /* MUST BE FIRST */
+ PCRE2_SPTR start_match; /* Can be adjusted by \K */
+ PCRE2_SPTR mark; /* Most recent mark on the success path */
+ uint32_t current_recurse; /* Current (deepest) recursion number */
+ uint32_t capture_last; /* Most recent capture */
+ PCRE2_SIZE last_group_offset; /* Saved offset to most recent group frame */
+ PCRE2_SIZE offset_top; /* Offset after highest capture */
+ PCRE2_SIZE ovector[131072]; /* Must be last in the structure */
+} heapframe;
+
+/* This typedef is a check that the size of the heapframe structure is a
+multiple of PCRE2_SIZE. See various comments above. */
+
+typedef char check_heapframe_size[
+ ((sizeof(heapframe) % sizeof(PCRE2_SIZE)) == 0)? (+1):(-1)];
+
+/* Structure for computing the alignment of heapframe. */
+
+typedef struct heapframe_align {
+ char unalign; /* Completely unalign the current offset */
+ heapframe frame; /* Offset is its alignment */
+} heapframe_align;
+
+/* This define is the minimum alignment required for a heapframe, in bytes. */
+
+#define HEAPFRAME_ALIGNMENT offsetof(heapframe_align, frame)
+
+/* Structure for passing "static" information around between the functions
+doing traditional NFA matching (pcre2_match() and friends). */
+
+typedef struct match_block {
+ pcre2_memctl memctl; /* For general use */
+ PCRE2_SIZE heap_limit; /* As it says */
+ uint32_t match_limit; /* As it says */
+ uint32_t match_limit_depth; /* As it says */
+ uint32_t match_call_count; /* Number of times a new frame is created */
+ BOOL hitend; /* Hit the end of the subject at some point */
+ BOOL hasthen; /* Pattern contains (*THEN) */
+ BOOL allowemptypartial; /* Allow empty hard partial */
+ const uint8_t *lcc; /* Points to lower casing table */
+ const uint8_t *fcc; /* Points to case-flipping table */
+ const uint8_t *ctypes; /* Points to table of type maps */
+ PCRE2_SIZE start_offset; /* The start offset value */
+ PCRE2_SIZE end_offset_top; /* Highwater mark at end of match */
+ uint16_t partial; /* PARTIAL options */
+ uint16_t bsr_convention; /* \R interpretation */
+ uint16_t name_count; /* Number of names in name table */
+ uint16_t name_entry_size; /* Size of entry in names table */
+ PCRE2_SPTR name_table; /* Table of group names */
+ PCRE2_SPTR start_code; /* For use when recursing */
+ PCRE2_SPTR start_subject; /* Start of the subject string */
+ PCRE2_SPTR check_subject; /* Where UTF-checked from */
+ PCRE2_SPTR end_subject; /* End of the subject string */
+ PCRE2_SPTR end_match_ptr; /* Subject position at end match */
+ PCRE2_SPTR start_used_ptr; /* Earliest consulted character */
+ PCRE2_SPTR last_used_ptr; /* Latest consulted character */
+ PCRE2_SPTR mark; /* Mark pointer to pass back on success */
+ PCRE2_SPTR nomatch_mark; /* Mark pointer to pass back on failure */
+ PCRE2_SPTR verb_ecode_ptr; /* For passing back info */
+ PCRE2_SPTR verb_skip_ptr; /* For passing back a (*SKIP) name */
+ uint32_t verb_current_recurse; /* Current recurse when (*VERB) happens */
+ uint32_t moptions; /* Match options */
+ uint32_t poptions; /* Pattern options */
+ uint32_t skip_arg_count; /* For counting SKIP_ARGs */
+ uint32_t ignore_skip_arg; /* For re-run when SKIP arg name not found */
+ uint32_t nltype; /* Newline type */
+ uint32_t nllen; /* Newline string length */
+ PCRE2_UCHAR nl[4]; /* Newline string when fixed */
+ pcre2_callout_block *cb; /* Points to a callout block */
+ void *callout_data; /* To pass back to callouts */
+ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */
+} match_block;
+
+/* A similar structure is used for the same purpose by the DFA matching
+functions. */
+
+typedef struct dfa_match_block {
+ pcre2_memctl memctl; /* For general use */
+ PCRE2_SPTR start_code; /* Start of the compiled pattern */
+ PCRE2_SPTR start_subject ; /* Start of the subject string */
+ PCRE2_SPTR end_subject; /* End of subject string */
+ PCRE2_SPTR start_used_ptr; /* Earliest consulted character */
+ PCRE2_SPTR last_used_ptr; /* Latest consulted character */
+ const uint8_t *tables; /* Character tables */
+ PCRE2_SIZE start_offset; /* The start offset value */
+ PCRE2_SIZE heap_limit; /* As it says */
+ PCRE2_SIZE heap_used; /* As it says */
+ uint32_t match_limit; /* As it says */
+ uint32_t match_limit_depth; /* As it says */
+ uint32_t match_call_count; /* Number of calls of internal function */
+ uint32_t moptions; /* Match options */
+ uint32_t poptions; /* Pattern options */
+ uint32_t nltype; /* Newline type */
+ uint32_t nllen; /* Newline string length */
+ BOOL allowemptypartial; /* Allow empty hard partial */
+ PCRE2_UCHAR nl[4]; /* Newline string when fixed */
+ uint16_t bsr_convention; /* \R interpretation */
+ pcre2_callout_block *cb; /* Points to a callout block */
+ void *callout_data; /* To pass back to callouts */
+ int (*callout)(pcre2_callout_block *,void *); /* Callout function or NULL */
+ dfa_recursion_info *recursive; /* Linked list of recursion data */
+} dfa_match_block;
+
+#endif /* PCRE2_PCRE2TEST */
+
+/* End of pcre2_intmodedep.h */
diff --git a/contrib/libs/pcre2/src/pcre2_jit_compile.c b/contrib/libs/pcre2/src/pcre2_jit_compile.c
new file mode 100644
index 0000000000..3fb93ff659
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_jit_compile.c
@@ -0,0 +1,14507 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ This module by Zoltan Herczeg
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+#ifdef SUPPORT_JIT
+
+/* All-in-one: Since we use the JIT compiler only from here,
+we just include it. This way we don't need to touch the build
+system files. */
+
+#define SLJIT_CONFIG_AUTO 1
+#define SLJIT_CONFIG_STATIC 1
+#define SLJIT_VERBOSE 0
+
+#ifdef PCRE2_DEBUG
+#define SLJIT_DEBUG 1
+#else
+#define SLJIT_DEBUG 0
+#endif
+
+#define SLJIT_MALLOC(size, allocator_data) pcre2_jit_malloc(size, allocator_data)
+#define SLJIT_FREE(ptr, allocator_data) pcre2_jit_free(ptr, allocator_data)
+
+static void * pcre2_jit_malloc(size_t size, void *allocator_data)
+{
+pcre2_memctl *allocator = ((pcre2_memctl*)allocator_data);
+return allocator->malloc(size, allocator->memory_data);
+}
+
+static void pcre2_jit_free(void *ptr, void *allocator_data)
+{
+pcre2_memctl *allocator = ((pcre2_memctl*)allocator_data);
+allocator->free(ptr, allocator->memory_data);
+}
+
+#include "sljit/sljitLir.c"
+
+#if defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED
+#error Unsupported architecture
+#endif
+
+/* Defines for debugging purposes. */
+
+/* 1 - Use unoptimized capturing brackets.
+ 2 - Enable capture_last_ptr (includes option 1). */
+/* #define DEBUG_FORCE_UNOPTIMIZED_CBRAS 2 */
+
+/* 1 - Always have a control head. */
+/* #define DEBUG_FORCE_CONTROL_HEAD 1 */
+
+/* Allocate memory for the regex stack on the real machine stack.
+Fast, but limited size. */
+#define MACHINE_STACK_SIZE 32768
+
+/* Growth rate for stack allocated by the OS. Should be the multiply
+of page size. */
+#define STACK_GROWTH_RATE 8192
+
+/* Enable to check that the allocation could destroy temporaries. */
+#if defined SLJIT_DEBUG && SLJIT_DEBUG
+#define DESTROY_REGISTERS 1
+#endif
+
+/*
+Short summary about the backtracking mechanism empolyed by the jit code generator:
+
+The code generator follows the recursive nature of the PERL compatible regular
+expressions. The basic blocks of regular expressions are condition checkers
+whose execute different commands depending on the result of the condition check.
+The relationship between the operators can be horizontal (concatenation) and
+vertical (sub-expression) (See struct backtrack_common for more details).
+
+ 'ab' - 'a' and 'b' regexps are concatenated
+ 'a+' - 'a' is the sub-expression of the '+' operator
+
+The condition checkers are boolean (true/false) checkers. Machine code is generated
+for the checker itself and for the actions depending on the result of the checker.
+The 'true' case is called as the matching path (expected path), and the other is called as
+the 'backtrack' path. Branch instructions are expesive for all CPUs, so we avoid taken
+branches on the matching path.
+
+ Greedy star operator (*) :
+ Matching path: match happens.
+ Backtrack path: match failed.
+ Non-greedy star operator (*?) :
+ Matching path: no need to perform a match.
+ Backtrack path: match is required.
+
+The following example shows how the code generated for a capturing bracket
+with two alternatives. Let A, B, C, D are arbirary regular expressions, and
+we have the following regular expression:
+
+ A(B|C)D
+
+The generated code will be the following:
+
+ A matching path
+ '(' matching path (pushing arguments to the stack)
+ B matching path
+ ')' matching path (pushing arguments to the stack)
+ D matching path
+ return with successful match
+
+ D backtrack path
+ ')' backtrack path (If we arrived from "C" jump to the backtrack of "C")
+ B backtrack path
+ C expected path
+ jump to D matching path
+ C backtrack path
+ A backtrack path
+
+ Notice, that the order of backtrack code paths are the opposite of the fast
+ code paths. In this way the topmost value on the stack is always belong
+ to the current backtrack code path. The backtrack path must check
+ whether there is a next alternative. If so, it needs to jump back to
+ the matching path eventually. Otherwise it needs to clear out its own stack
+ frame and continue the execution on the backtrack code paths.
+*/
+
+/*
+Saved stack frames:
+
+Atomic blocks and asserts require reloading the values of private data
+when the backtrack mechanism performed. Because of OP_RECURSE, the data
+are not necessarly known in compile time, thus we need a dynamic restore
+mechanism.
+
+The stack frames are stored in a chain list, and have the following format:
+([ capturing bracket offset ][ start value ][ end value ])+ ... [ 0 ] [ previous head ]
+
+Thus we can restore the private data to a particular point in the stack.
+*/
+
+typedef struct jit_arguments {
+ /* Pointers first. */
+ struct sljit_stack *stack;
+ PCRE2_SPTR str;
+ PCRE2_SPTR begin;
+ PCRE2_SPTR end;
+ pcre2_match_data *match_data;
+ PCRE2_SPTR startchar_ptr;
+ PCRE2_UCHAR *mark_ptr;
+ int (*callout)(pcre2_callout_block *, void *);
+ void *callout_data;
+ /* Everything else after. */
+ sljit_uw offset_limit;
+ sljit_u32 limit_match;
+ sljit_u32 oveccount;
+ sljit_u32 options;
+} jit_arguments;
+
+#define JIT_NUMBER_OF_COMPILE_MODES 3
+
+typedef struct executable_functions {
+ void *executable_funcs[JIT_NUMBER_OF_COMPILE_MODES];
+ void *read_only_data_heads[JIT_NUMBER_OF_COMPILE_MODES];
+ sljit_uw executable_sizes[JIT_NUMBER_OF_COMPILE_MODES];
+ sljit_u32 top_bracket;
+ sljit_u32 limit_match;
+} executable_functions;
+
+typedef struct jump_list {
+ struct sljit_jump *jump;
+ struct jump_list *next;
+} jump_list;
+
+typedef struct stub_list {
+ struct sljit_jump *start;
+ struct sljit_label *quit;
+ struct stub_list *next;
+} stub_list;
+
+enum frame_types {
+ no_frame = -1,
+ no_stack = -2
+};
+
+enum control_types {
+ type_mark = 0,
+ type_then_trap = 1
+};
+
+enum early_fail_types {
+ type_skip = 0,
+ type_fail = 1,
+ type_fail_range = 2
+};
+
+typedef int (SLJIT_FUNC *jit_function)(jit_arguments *args);
+
+/* The following structure is the key data type for the recursive
+code generator. It is allocated by compile_matchingpath, and contains
+the arguments for compile_backtrackingpath. Must be the first member
+of its descendants. */
+typedef struct backtrack_common {
+ /* Concatenation stack. */
+ struct backtrack_common *prev;
+ jump_list *nextbacktracks;
+ /* Internal stack (for component operators). */
+ struct backtrack_common *top;
+ jump_list *topbacktracks;
+ /* Opcode pointer. */
+ PCRE2_SPTR cc;
+} backtrack_common;
+
+typedef struct assert_backtrack {
+ backtrack_common common;
+ jump_list *condfailed;
+ /* Less than 0 if a frame is not needed. */
+ int framesize;
+ /* Points to our private memory word on the stack. */
+ int private_data_ptr;
+ /* For iterators. */
+ struct sljit_label *matchingpath;
+} assert_backtrack;
+
+typedef struct bracket_backtrack {
+ backtrack_common common;
+ /* Where to coninue if an alternative is successfully matched. */
+ struct sljit_label *alternative_matchingpath;
+ /* For rmin and rmax iterators. */
+ struct sljit_label *recursive_matchingpath;
+ /* For greedy ? operator. */
+ struct sljit_label *zero_matchingpath;
+ /* Contains the branches of a failed condition. */
+ union {
+ /* Both for OP_COND, OP_SCOND. */
+ jump_list *condfailed;
+ assert_backtrack *assert;
+ /* For OP_ONCE. Less than 0 if not needed. */
+ int framesize;
+ /* For brackets with >3 alternatives. */
+ struct sljit_put_label *matching_put_label;
+ } u;
+ /* Points to our private memory word on the stack. */
+ int private_data_ptr;
+} bracket_backtrack;
+
+typedef struct bracketpos_backtrack {
+ backtrack_common common;
+ /* Points to our private memory word on the stack. */
+ int private_data_ptr;
+ /* Reverting stack is needed. */
+ int framesize;
+ /* Allocated stack size. */
+ int stacksize;
+} bracketpos_backtrack;
+
+typedef struct braminzero_backtrack {
+ backtrack_common common;
+ struct sljit_label *matchingpath;
+} braminzero_backtrack;
+
+typedef struct char_iterator_backtrack {
+ backtrack_common common;
+ /* Next iteration. */
+ struct sljit_label *matchingpath;
+ union {
+ jump_list *backtracks;
+ struct {
+ unsigned int othercasebit;
+ PCRE2_UCHAR chr;
+ BOOL enabled;
+ } charpos;
+ } u;
+} char_iterator_backtrack;
+
+typedef struct ref_iterator_backtrack {
+ backtrack_common common;
+ /* Next iteration. */
+ struct sljit_label *matchingpath;
+} ref_iterator_backtrack;
+
+typedef struct recurse_entry {
+ struct recurse_entry *next;
+ /* Contains the function entry label. */
+ struct sljit_label *entry_label;
+ /* Contains the function entry label. */
+ struct sljit_label *backtrack_label;
+ /* Collects the entry calls until the function is not created. */
+ jump_list *entry_calls;
+ /* Collects the backtrack calls until the function is not created. */
+ jump_list *backtrack_calls;
+ /* Points to the starting opcode. */
+ sljit_sw start;
+} recurse_entry;
+
+typedef struct recurse_backtrack {
+ backtrack_common common;
+ /* Return to the matching path. */
+ struct sljit_label *matchingpath;
+ /* Recursive pattern. */
+ recurse_entry *entry;
+ /* Pattern is inlined. */
+ BOOL inlined_pattern;
+} recurse_backtrack;
+
+#define OP_THEN_TRAP OP_TABLE_LENGTH
+
+typedef struct then_trap_backtrack {
+ backtrack_common common;
+ /* If then_trap is not NULL, this structure contains the real
+ then_trap for the backtracking path. */
+ struct then_trap_backtrack *then_trap;
+ /* Points to the starting opcode. */
+ sljit_sw start;
+ /* Exit point for the then opcodes of this alternative. */
+ jump_list *quit;
+ /* Frame size of the current alternative. */
+ int framesize;
+} then_trap_backtrack;
+
+#define MAX_N_CHARS 12
+#define MAX_DIFF_CHARS 5
+
+typedef struct fast_forward_char_data {
+ /* Number of characters in the chars array, 255 for any character. */
+ sljit_u8 count;
+ /* Number of last UTF-8 characters in the chars array. */
+ sljit_u8 last_count;
+ /* Available characters in the current position. */
+ PCRE2_UCHAR chars[MAX_DIFF_CHARS];
+} fast_forward_char_data;
+
+#define MAX_CLASS_RANGE_SIZE 4
+#define MAX_CLASS_CHARS_SIZE 3
+
+typedef struct compiler_common {
+ /* The sljit ceneric compiler. */
+ struct sljit_compiler *compiler;
+ /* Compiled regular expression. */
+ pcre2_real_code *re;
+ /* First byte code. */
+ PCRE2_SPTR start;
+ /* Maps private data offset to each opcode. */
+ sljit_s32 *private_data_ptrs;
+ /* Chain list of read-only data ptrs. */
+ void *read_only_data_head;
+ /* Tells whether the capturing bracket is optimized. */
+ sljit_u8 *optimized_cbracket;
+ /* Tells whether the starting offset is a target of then. */
+ sljit_u8 *then_offsets;
+ /* Current position where a THEN must jump. */
+ then_trap_backtrack *then_trap;
+ /* Starting offset of private data for capturing brackets. */
+ sljit_s32 cbra_ptr;
+ /* Output vector starting point. Must be divisible by 2. */
+ sljit_s32 ovector_start;
+ /* Points to the starting character of the current match. */
+ sljit_s32 start_ptr;
+ /* Last known position of the requested byte. */
+ sljit_s32 req_char_ptr;
+ /* Head of the last recursion. */
+ sljit_s32 recursive_head_ptr;
+ /* First inspected character for partial matching.
+ (Needed for avoiding zero length partial matches.) */
+ sljit_s32 start_used_ptr;
+ /* Starting pointer for partial soft matches. */
+ sljit_s32 hit_start;
+ /* Pointer of the match end position. */
+ sljit_s32 match_end_ptr;
+ /* Points to the marked string. */
+ sljit_s32 mark_ptr;
+ /* Recursive control verb management chain. */
+ sljit_s32 control_head_ptr;
+ /* Points to the last matched capture block index. */
+ sljit_s32 capture_last_ptr;
+ /* Fast forward skipping byte code pointer. */
+ PCRE2_SPTR fast_forward_bc_ptr;
+ /* Locals used by fast fail optimization. */
+ sljit_s32 early_fail_start_ptr;
+ sljit_s32 early_fail_end_ptr;
+ /* Variables used by recursive call generator. */
+ sljit_s32 recurse_bitset_size;
+ uint8_t *recurse_bitset;
+
+ /* Flipped and lower case tables. */
+ const sljit_u8 *fcc;
+ sljit_sw lcc;
+ /* Mode can be PCRE2_JIT_COMPLETE and others. */
+ int mode;
+ /* TRUE, when empty match is accepted for partial matching. */
+ BOOL allow_empty_partial;
+ /* TRUE, when minlength is greater than 0. */
+ BOOL might_be_empty;
+ /* \K is found in the pattern. */
+ BOOL has_set_som;
+ /* (*SKIP:arg) is found in the pattern. */
+ BOOL has_skip_arg;
+ /* (*THEN) is found in the pattern. */
+ BOOL has_then;
+ /* (*SKIP) or (*SKIP:arg) is found in lookbehind assertion. */
+ BOOL has_skip_in_assert_back;
+ /* Quit is redirected by recurse, negative assertion, or positive assertion in conditional block. */
+ BOOL local_quit_available;
+ /* Currently in a positive assertion. */
+ BOOL in_positive_assertion;
+ /* Newline control. */
+ int nltype;
+ sljit_u32 nlmax;
+ sljit_u32 nlmin;
+ int newline;
+ int bsr_nltype;
+ sljit_u32 bsr_nlmax;
+ sljit_u32 bsr_nlmin;
+ /* Dollar endonly. */
+ int endonly;
+ /* Tables. */
+ sljit_sw ctypes;
+ /* Named capturing brackets. */
+ PCRE2_SPTR name_table;
+ sljit_sw name_count;
+ sljit_sw name_entry_size;
+
+ /* Labels and jump lists. */
+ struct sljit_label *partialmatchlabel;
+ struct sljit_label *quit_label;
+ struct sljit_label *abort_label;
+ struct sljit_label *accept_label;
+ struct sljit_label *ff_newline_shortcut;
+ stub_list *stubs;
+ recurse_entry *entries;
+ recurse_entry *currententry;
+ jump_list *partialmatch;
+ jump_list *quit;
+ jump_list *positive_assertion_quit;
+ jump_list *abort;
+ jump_list *failed_match;
+ jump_list *accept;
+ jump_list *calllimit;
+ jump_list *stackalloc;
+ jump_list *revertframes;
+ jump_list *wordboundary;
+ jump_list *anynewline;
+ jump_list *hspace;
+ jump_list *vspace;
+ jump_list *casefulcmp;
+ jump_list *caselesscmp;
+ jump_list *reset_match;
+ BOOL unset_backref;
+ BOOL alt_circumflex;
+#ifdef SUPPORT_UNICODE
+ BOOL utf;
+ BOOL invalid_utf;
+ BOOL ucp;
+ /* Points to saving area for iref. */
+ sljit_s32 iref_ptr;
+ jump_list *getucd;
+ jump_list *getucdtype;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ jump_list *utfreadchar;
+ jump_list *utfreadtype8;
+ jump_list *utfpeakcharback;
+#endif
+#if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16
+ jump_list *utfreadchar_invalid;
+ jump_list *utfreadnewline_invalid;
+ jump_list *utfmoveback_invalid;
+ jump_list *utfpeakcharback_invalid;
+#endif
+#endif /* SUPPORT_UNICODE */
+} compiler_common;
+
+/* For byte_sequence_compare. */
+
+typedef struct compare_context {
+ int length;
+ int sourcereg;
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ int ucharptr;
+ union {
+ sljit_s32 asint;
+ sljit_u16 asushort;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ sljit_u8 asbyte;
+ sljit_u8 asuchars[4];
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ sljit_u16 asuchars[2];
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ sljit_u32 asuchars[1];
+#endif
+ } c;
+ union {
+ sljit_s32 asint;
+ sljit_u16 asushort;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ sljit_u8 asbyte;
+ sljit_u8 asuchars[4];
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ sljit_u16 asuchars[2];
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ sljit_u32 asuchars[1];
+#endif
+ } oc;
+#endif
+} compare_context;
+
+/* Undefine sljit macros. */
+#undef CMP
+
+/* Used for accessing the elements of the stack. */
+#define STACK(i) ((i) * SSIZE_OF(sw))
+
+#ifdef SLJIT_PREF_SHIFT_REG
+#if SLJIT_PREF_SHIFT_REG == SLJIT_R2
+/* Nothing. */
+#elif SLJIT_PREF_SHIFT_REG == SLJIT_R3
+#define SHIFT_REG_IS_R3
+#else
+#error "Unsupported shift register"
+#endif
+#endif
+
+#define TMP1 SLJIT_R0
+#ifdef SHIFT_REG_IS_R3
+#define TMP2 SLJIT_R3
+#define TMP3 SLJIT_R2
+#else
+#define TMP2 SLJIT_R2
+#define TMP3 SLJIT_R3
+#endif
+#define STR_PTR SLJIT_R1
+#define STR_END SLJIT_S0
+#define STACK_TOP SLJIT_S1
+#define STACK_LIMIT SLJIT_S2
+#define COUNT_MATCH SLJIT_S3
+#define ARGUMENTS SLJIT_S4
+#define RETURN_ADDR SLJIT_R4
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+#define HAS_VIRTUAL_REGISTERS 1
+#else
+#define HAS_VIRTUAL_REGISTERS 0
+#endif
+
+/* Local space layout. */
+/* These two locals can be used by the current opcode. */
+#define LOCALS0 (0 * sizeof(sljit_sw))
+#define LOCALS1 (1 * sizeof(sljit_sw))
+/* Two local variables for possessive quantifiers (char1 cannot use them). */
+#define POSSESSIVE0 (2 * sizeof(sljit_sw))
+#define POSSESSIVE1 (3 * sizeof(sljit_sw))
+/* Max limit of recursions. */
+#define LIMIT_MATCH (4 * sizeof(sljit_sw))
+/* The output vector is stored on the stack, and contains pointers
+to characters. The vector data is divided into two groups: the first
+group contains the start / end character pointers, and the second is
+the start pointers when the end of the capturing group has not yet reached. */
+#define OVECTOR_START (common->ovector_start)
+#define OVECTOR(i) (OVECTOR_START + (i) * SSIZE_OF(sw))
+#define OVECTOR_PRIV(i) (common->cbra_ptr + (i) * SSIZE_OF(sw))
+#define PRIVATE_DATA(cc) (common->private_data_ptrs[(cc) - common->start])
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define MOV_UCHAR SLJIT_MOV_U8
+#define IN_UCHARS(x) (x)
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#define MOV_UCHAR SLJIT_MOV_U16
+#define UCHAR_SHIFT (1)
+#define IN_UCHARS(x) ((x) * 2)
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+#define MOV_UCHAR SLJIT_MOV_U32
+#define UCHAR_SHIFT (2)
+#define IN_UCHARS(x) ((x) * 4)
+#else
+#error Unsupported compiling mode
+#endif
+
+/* Shortcuts. */
+#define DEFINE_COMPILER \
+ struct sljit_compiler *compiler = common->compiler
+#define OP1(op, dst, dstw, src, srcw) \
+ sljit_emit_op1(compiler, (op), (dst), (dstw), (src), (srcw))
+#define OP2(op, dst, dstw, src1, src1w, src2, src2w) \
+ sljit_emit_op2(compiler, (op), (dst), (dstw), (src1), (src1w), (src2), (src2w))
+#define OP2U(op, src1, src1w, src2, src2w) \
+ sljit_emit_op2u(compiler, (op), (src1), (src1w), (src2), (src2w))
+#define OP_SRC(op, src, srcw) \
+ sljit_emit_op_src(compiler, (op), (src), (srcw))
+#define LABEL() \
+ sljit_emit_label(compiler)
+#define JUMP(type) \
+ sljit_emit_jump(compiler, (type))
+#define JUMPTO(type, label) \
+ sljit_set_label(sljit_emit_jump(compiler, (type)), (label))
+#define JUMPHERE(jump) \
+ sljit_set_label((jump), sljit_emit_label(compiler))
+#define SET_LABEL(jump, label) \
+ sljit_set_label((jump), (label))
+#define CMP(type, src1, src1w, src2, src2w) \
+ sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w))
+#define CMPTO(type, src1, src1w, src2, src2w, label) \
+ sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label))
+#define OP_FLAGS(op, dst, dstw, type) \
+ sljit_emit_op_flags(compiler, (op), (dst), (dstw), (type))
+#define CMOV(type, dst_reg, src, srcw) \
+ sljit_emit_cmov(compiler, (type), (dst_reg), (src), (srcw))
+#define GET_LOCAL_BASE(dst, dstw, offset) \
+ sljit_get_local_base(compiler, (dst), (dstw), (offset))
+
+#define READ_CHAR_MAX 0x7fffffff
+
+#define INVALID_UTF_CHAR -1
+#define UNASSIGNED_UTF_CHAR 888
+
+#if defined SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+
+#define GETCHARINC_INVALID(c, ptr, end, invalid_action) \
+ { \
+ if (ptr[0] <= 0x7f) \
+ c = *ptr++; \
+ else if (ptr + 1 < end && ptr[1] >= 0x80 && ptr[1] < 0xc0) \
+ { \
+ c = ptr[1] - 0x80; \
+ \
+ if (ptr[0] >= 0xc2 && ptr[0] <= 0xdf) \
+ { \
+ c |= (ptr[0] - 0xc0) << 6; \
+ ptr += 2; \
+ } \
+ else if (ptr + 2 < end && ptr[2] >= 0x80 && ptr[2] < 0xc0) \
+ { \
+ c = c << 6 | (ptr[2] - 0x80); \
+ \
+ if (ptr[0] >= 0xe0 && ptr[0] <= 0xef) \
+ { \
+ c |= (ptr[0] - 0xe0) << 12; \
+ ptr += 3; \
+ \
+ if (c < 0x800 || (c >= 0xd800 && c < 0xe000)) \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else if (ptr + 3 < end && ptr[3] >= 0x80 && ptr[3] < 0xc0) \
+ { \
+ c = c << 6 | (ptr[3] - 0x80); \
+ \
+ if (ptr[0] >= 0xf0 && ptr[0] <= 0xf4) \
+ { \
+ c |= (ptr[0] - 0xf0) << 18; \
+ ptr += 4; \
+ \
+ if (c >= 0x110000 || c < 0x10000) \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \
+ { \
+ c = ptr[-1]; \
+ if (c <= 0x7f) \
+ ptr--; \
+ else if (ptr - 1 > start && ptr[-1] >= 0x80 && ptr[-1] < 0xc0) \
+ { \
+ c -= 0x80; \
+ \
+ if (ptr[-2] >= 0xc2 && ptr[-2] <= 0xdf) \
+ { \
+ c |= (ptr[-2] - 0xc0) << 6; \
+ ptr -= 2; \
+ } \
+ else if (ptr - 2 > start && ptr[-2] >= 0x80 && ptr[-2] < 0xc0) \
+ { \
+ c = c << 6 | (ptr[-2] - 0x80); \
+ \
+ if (ptr[-3] >= 0xe0 && ptr[-3] <= 0xef) \
+ { \
+ c |= (ptr[-3] - 0xe0) << 12; \
+ ptr -= 3; \
+ \
+ if (c < 0x800 || (c >= 0xd800 && c < 0xe000)) \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else if (ptr - 3 > start && ptr[-3] >= 0x80 && ptr[-3] < 0xc0) \
+ { \
+ c = c << 6 | (ptr[-3] - 0x80); \
+ \
+ if (ptr[-4] >= 0xf0 && ptr[-4] <= 0xf4) \
+ { \
+ c |= (ptr[-4] - 0xf0) << 18; \
+ ptr -= 4; \
+ \
+ if (c >= 0x110000 || c < 0x10000) \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+
+#define GETCHARINC_INVALID(c, ptr, end, invalid_action) \
+ { \
+ if (ptr[0] < 0xd800 || ptr[0] >= 0xe000) \
+ c = *ptr++; \
+ else if (ptr[0] < 0xdc00 && ptr + 1 < end && ptr[1] >= 0xdc00 && ptr[1] < 0xe000) \
+ { \
+ c = (((ptr[0] - 0xd800) << 10) | (ptr[1] - 0xdc00)) + 0x10000; \
+ ptr += 2; \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \
+ { \
+ c = ptr[-1]; \
+ if (c < 0xd800 || c >= 0xe000) \
+ ptr--; \
+ else if (c >= 0xdc00 && ptr - 1 > start && ptr[-2] >= 0xd800 && ptr[-2] < 0xdc00) \
+ { \
+ c = (((ptr[-2] - 0xd800) << 10) | (c - 0xdc00)) + 0x10000; \
+ ptr -= 2; \
+ } \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+
+#define GETCHARINC_INVALID(c, ptr, end, invalid_action) \
+ { \
+ if (ptr[0] < 0xd800 || (ptr[0] >= 0xe000 && ptr[0] < 0x110000)) \
+ c = *ptr++; \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#define GETCHARBACK_INVALID(c, ptr, start, invalid_action) \
+ { \
+ c = ptr[-1]; \
+ if (ptr[-1] < 0xd800 || (ptr[-1] >= 0xe000 && ptr[-1] < 0x110000)) \
+ ptr--; \
+ else \
+ { \
+ invalid_action; \
+ } \
+ }
+
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+
+static PCRE2_SPTR bracketend(PCRE2_SPTR cc)
+{
+SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND));
+do cc += GET(cc, 1); while (*cc == OP_ALT);
+SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS);
+cc += 1 + LINK_SIZE;
+return cc;
+}
+
+static int no_alternatives(PCRE2_SPTR cc)
+{
+int count = 0;
+SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND));
+do
+ {
+ cc += GET(cc, 1);
+ count++;
+ }
+while (*cc == OP_ALT);
+SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS);
+return count;
+}
+
+/* Functions whose might need modification for all new supported opcodes:
+ next_opcode
+ check_opcode_types
+ set_private_data_ptrs
+ get_framesize
+ init_frame
+ get_recurse_data_length
+ copy_recurse_data
+ compile_matchingpath
+ compile_backtrackingpath
+*/
+
+static PCRE2_SPTR next_opcode(compiler_common *common, PCRE2_SPTR cc)
+{
+SLJIT_UNUSED_ARG(common);
+switch(*cc)
+ {
+ case OP_SOD:
+ case OP_SOM:
+ case OP_SET_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_NOTPROP:
+ case OP_PROP:
+ case OP_ANYNL:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ case OP_EXTUNI:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ case OP_CRPOSQUERY:
+ case OP_CRPOSRANGE:
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_REF:
+ case OP_REFI:
+ case OP_DNREF:
+ case OP_DNREFI:
+ case OP_RECURSE:
+ case OP_CALLOUT:
+ case OP_ALT:
+ case OP_KET:
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_KETRPOS:
+ case OP_REVERSE:
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRA:
+ case OP_BRAPOS:
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_COND:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ case OP_SCOND:
+ case OP_CREF:
+ case OP_DNCREF:
+ case OP_RREF:
+ case OP_DNRREF:
+ case OP_FALSE:
+ case OP_TRUE:
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ case OP_BRAPOSZERO:
+ case OP_PRUNE:
+ case OP_SKIP:
+ case OP_THEN:
+ case OP_COMMIT:
+ case OP_FAIL:
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ case OP_CLOSE:
+ case OP_SKIPZERO:
+ return cc + PRIV(OP_lengths)[*cc];
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ cc += PRIV(OP_lengths)[*cc];
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ return cc;
+
+ /* Special cases. */
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+ return cc + PRIV(OP_lengths)[*cc] - 1;
+
+ case OP_ANYBYTE:
+#ifdef SUPPORT_UNICODE
+ if (common->utf) return NULL;
+#endif
+ return cc + 1;
+
+ case OP_CALLOUT_STR:
+ return cc + GET(cc, 1 + 2*LINK_SIZE);
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ return cc + GET(cc, 1);
+#endif
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ return cc + 1 + 2 + cc[1];
+
+ default:
+ SLJIT_UNREACHABLE();
+ return NULL;
+ }
+}
+
+static BOOL check_opcode_types(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend)
+{
+int count;
+PCRE2_SPTR slot;
+PCRE2_SPTR assert_back_end = cc - 1;
+PCRE2_SPTR assert_na_end = cc - 1;
+
+/* Calculate important variables (like stack size) and checks whether all opcodes are supported. */
+while (cc < ccend)
+ {
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ common->has_set_som = TRUE;
+ common->might_be_empty = TRUE;
+ cc += 1;
+ break;
+
+ case OP_REFI:
+#ifdef SUPPORT_UNICODE
+ if (common->iref_ptr == 0)
+ {
+ common->iref_ptr = common->ovector_start;
+ common->ovector_start += 3 * sizeof(sljit_sw);
+ }
+#endif /* SUPPORT_UNICODE */
+ /* Fall through. */
+ case OP_REF:
+ common->optimized_cbracket[GET2(cc, 1)] = 0;
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ slot = bracketend(cc);
+ if (slot > assert_na_end)
+ assert_na_end = slot;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] = 0;
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ case OP_SCOND:
+ /* Only AUTO_CALLOUT can insert this opcode. We do
+ not intend to support this case. */
+ if (cc[1 + LINK_SIZE] == OP_CALLOUT || cc[1 + LINK_SIZE] == OP_CALLOUT_STR)
+ return FALSE;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CREF:
+ common->optimized_cbracket[GET2(cc, 1)] = 0;
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ case OP_DNCREF:
+ count = GET2(cc, 1 + IMM2_SIZE);
+ slot = common->name_table + GET2(cc, 1) * common->name_entry_size;
+ while (count-- > 0)
+ {
+ common->optimized_cbracket[GET2(slot, 0)] = 0;
+ slot += common->name_entry_size;
+ }
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+
+ case OP_RECURSE:
+ /* Set its value only once. */
+ if (common->recursive_head_ptr == 0)
+ {
+ common->recursive_head_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ if (common->capture_last_ptr == 0)
+ {
+ common->capture_last_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+ cc += (*cc == OP_CALLOUT) ? PRIV(OP_lengths)[OP_CALLOUT] : GET(cc, 1 + 2*LINK_SIZE);
+ break;
+
+ case OP_ASSERTBACK:
+ slot = bracketend(cc);
+ if (slot > assert_back_end)
+ assert_back_end = slot;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_THEN_ARG:
+ common->has_then = TRUE;
+ common->control_head_ptr = 1;
+ /* Fall through. */
+
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ if (cc < assert_na_end)
+ return FALSE;
+ /* Fall through */
+ case OP_MARK:
+ if (common->mark_ptr == 0)
+ {
+ common->mark_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_THEN:
+ common->has_then = TRUE;
+ common->control_head_ptr = 1;
+ cc += 1;
+ break;
+
+ case OP_SKIP:
+ if (cc < assert_back_end)
+ common->has_skip_in_assert_back = TRUE;
+ if (cc < assert_na_end)
+ return FALSE;
+ cc += 1;
+ break;
+
+ case OP_SKIP_ARG:
+ common->control_head_ptr = 1;
+ common->has_skip_arg = TRUE;
+ if (cc < assert_back_end)
+ common->has_skip_in_assert_back = TRUE;
+ if (cc < assert_na_end)
+ return FALSE;
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_PRUNE:
+ case OP_COMMIT:
+ case OP_ASSERT_ACCEPT:
+ if (cc < assert_na_end)
+ return FALSE;
+ cc++;
+ break;
+
+ default:
+ cc = next_opcode(common, cc);
+ if (cc == NULL)
+ return FALSE;
+ break;
+ }
+ }
+return TRUE;
+}
+
+#define EARLY_FAIL_ENHANCE_MAX (1 + 3)
+
+/*
+start:
+ 0 - skip / early fail allowed
+ 1 - only early fail with range allowed
+ >1 - (start - 1) early fail is processed
+
+return: current number of iterators enhanced with fast fail
+*/
+static int detect_early_fail(compiler_common *common, PCRE2_SPTR cc, int *private_data_start,
+ sljit_s32 depth, int start, BOOL fast_forward_allowed)
+{
+PCRE2_SPTR begin = cc;
+PCRE2_SPTR next_alt;
+PCRE2_SPTR end;
+PCRE2_SPTR accelerated_start;
+BOOL prev_fast_forward_allowed;
+int result = 0;
+int count;
+
+SLJIT_ASSERT(*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA);
+SLJIT_ASSERT(*cc != OP_CBRA || common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] != 0);
+SLJIT_ASSERT(start < EARLY_FAIL_ENHANCE_MAX);
+
+next_alt = cc + GET(cc, 1);
+if (*next_alt == OP_ALT)
+ fast_forward_allowed = FALSE;
+
+do
+ {
+ count = start;
+ cc += 1 + LINK_SIZE + ((*cc == OP_CBRA) ? IMM2_SIZE : 0);
+
+ while (TRUE)
+ {
+ accelerated_start = NULL;
+
+ switch(*cc)
+ {
+ case OP_SOD:
+ case OP_SOM:
+ case OP_SET_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ /* Zero width assertions. */
+ cc++;
+ continue;
+
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ fast_forward_allowed = FALSE;
+ cc++;
+ continue;
+
+ case OP_ANYNL:
+ case OP_EXTUNI:
+ fast_forward_allowed = FALSE;
+ if (count == 0)
+ count = 1;
+ cc++;
+ continue;
+
+ case OP_NOTPROP:
+ case OP_PROP:
+ fast_forward_allowed = FALSE;
+ cc += 1 + 2;
+ continue;
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ fast_forward_allowed = FALSE;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ continue;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ /* The type or prop opcode is skipped in the next iteration. */
+ cc += 1;
+
+ if (cc[0] != OP_ANYNL && cc[0] != OP_EXTUNI)
+ {
+ accelerated_start = cc - 1;
+ break;
+ }
+
+ if (count == 0)
+ count = 1;
+ fast_forward_allowed = FALSE;
+ continue;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSUPTO:
+ cc += IMM2_SIZE;
+ /* Fall through */
+
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSQUERY:
+ /* The type or prop opcode is skipped in the next iteration. */
+ fast_forward_allowed = FALSE;
+ if (count == 0)
+ count = 1;
+ cc += 1;
+ continue;
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ accelerated_start = cc;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSUPTO:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSUPTOI:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSUPTO:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSUPTOI:
+ cc += IMM2_SIZE;
+ /* Fall through */
+
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_POSQUERY:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_POSQUERYI:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTPOSQUERY:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTPOSQUERYI:
+ fast_forward_allowed = FALSE;
+ if (count == 0)
+ count = 1;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ continue;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ accelerated_start = cc;
+ cc += ((*cc == OP_XCLASS) ? GET(cc, 1) : (unsigned int)(1 + (32 / sizeof(PCRE2_UCHAR))));
+#else
+ accelerated_start = cc;
+ cc += (1 + (32 / sizeof(PCRE2_UCHAR)));
+#endif
+
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ cc += 2 * IMM2_SIZE;
+ /* Fall through */
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ cc++;
+ if (count == 0)
+ count = 1;
+ /* Fall through */
+ default:
+ accelerated_start = NULL;
+ fast_forward_allowed = FALSE;
+ continue;
+ }
+ break;
+
+ case OP_ONCE:
+ case OP_BRA:
+ case OP_CBRA:
+ end = cc + GET(cc, 1);
+
+ prev_fast_forward_allowed = fast_forward_allowed;
+ fast_forward_allowed = FALSE;
+ if (depth >= 4)
+ break;
+
+ end = bracketend(cc) - (1 + LINK_SIZE);
+ if (*end != OP_KET || (*cc == OP_CBRA && common->optimized_cbracket[GET2(cc, 1 + LINK_SIZE)] == 0))
+ break;
+
+ count = detect_early_fail(common, cc, private_data_start, depth + 1, count, prev_fast_forward_allowed);
+
+ if (PRIVATE_DATA(cc) != 0)
+ common->private_data_ptrs[begin - common->start] = 1;
+
+ if (count < EARLY_FAIL_ENHANCE_MAX)
+ {
+ cc = end + (1 + LINK_SIZE);
+ continue;
+ }
+ break;
+
+ case OP_KET:
+ SLJIT_ASSERT(PRIVATE_DATA(cc) == 0);
+ if (cc >= next_alt)
+ break;
+ cc += 1 + LINK_SIZE;
+ continue;
+ }
+
+ if (accelerated_start != NULL)
+ {
+ if (count == 0)
+ {
+ count++;
+
+ if (fast_forward_allowed)
+ {
+ common->fast_forward_bc_ptr = accelerated_start;
+ common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_skip;
+ *private_data_start += sizeof(sljit_sw);
+ }
+ else
+ {
+ common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_fail;
+
+ if (common->early_fail_start_ptr == 0)
+ common->early_fail_start_ptr = *private_data_start;
+
+ *private_data_start += sizeof(sljit_sw);
+ common->early_fail_end_ptr = *private_data_start;
+
+ if (*private_data_start > SLJIT_MAX_LOCAL_SIZE)
+ return EARLY_FAIL_ENHANCE_MAX;
+ }
+ }
+ else
+ {
+ common->private_data_ptrs[(accelerated_start + 1) - common->start] = ((*private_data_start) << 3) | type_fail_range;
+
+ if (common->early_fail_start_ptr == 0)
+ common->early_fail_start_ptr = *private_data_start;
+
+ *private_data_start += 2 * sizeof(sljit_sw);
+ common->early_fail_end_ptr = *private_data_start;
+
+ if (*private_data_start > SLJIT_MAX_LOCAL_SIZE)
+ return EARLY_FAIL_ENHANCE_MAX;
+ }
+
+ /* Cannot be part of a repeat. */
+ common->private_data_ptrs[begin - common->start] = 1;
+ count++;
+
+ if (count < EARLY_FAIL_ENHANCE_MAX)
+ continue;
+ }
+
+ break;
+ }
+
+ if (*cc != OP_ALT && *cc != OP_KET)
+ result = EARLY_FAIL_ENHANCE_MAX;
+ else if (result < count)
+ result = count;
+
+ cc = next_alt;
+ next_alt = cc + GET(cc, 1);
+ }
+while (*cc == OP_ALT);
+
+return result;
+}
+
+static int get_class_iterator_size(PCRE2_SPTR cc)
+{
+sljit_u32 min;
+sljit_u32 max;
+switch(*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRPLUS:
+ return 2;
+
+ case OP_CRMINSTAR:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ return 1;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ min = GET2(cc, 1);
+ max = GET2(cc, 1 + IMM2_SIZE);
+ if (max == 0)
+ return (*cc == OP_CRRANGE) ? 2 : 1;
+ max -= min;
+ if (max > 2)
+ max = 2;
+ return max;
+
+ default:
+ return 0;
+ }
+}
+
+static BOOL detect_repeat(compiler_common *common, PCRE2_SPTR begin)
+{
+PCRE2_SPTR end = bracketend(begin);
+PCRE2_SPTR next;
+PCRE2_SPTR next_end;
+PCRE2_SPTR max_end;
+PCRE2_UCHAR type;
+sljit_sw length = end - begin;
+sljit_s32 min, max, i;
+
+/* Detect fixed iterations first. */
+if (end[-(1 + LINK_SIZE)] != OP_KET || PRIVATE_DATA(begin) != 0)
+ return FALSE;
+
+/* /(?:AB){4,6}/ is currently converted to /(?:AB){3}(?AB){1,3}/
+ * Skip the check of the second part. */
+if (PRIVATE_DATA(end - LINK_SIZE) != 0)
+ return TRUE;
+
+next = end;
+min = 1;
+while (1)
+ {
+ if (*next != *begin)
+ break;
+ next_end = bracketend(next);
+ if (next_end - next != length || memcmp(begin, next, IN_UCHARS(length)) != 0)
+ break;
+ next = next_end;
+ min++;
+ }
+
+if (min == 2)
+ return FALSE;
+
+max = 0;
+max_end = next;
+if (*next == OP_BRAZERO || *next == OP_BRAMINZERO)
+ {
+ type = *next;
+ while (1)
+ {
+ if (next[0] != type || next[1] != OP_BRA || next[2 + LINK_SIZE] != *begin)
+ break;
+ next_end = bracketend(next + 2 + LINK_SIZE);
+ if (next_end - next != (length + 2 + LINK_SIZE) || memcmp(begin, next + 2 + LINK_SIZE, IN_UCHARS(length)) != 0)
+ break;
+ next = next_end;
+ max++;
+ }
+
+ if (next[0] == type && next[1] == *begin && max >= 1)
+ {
+ next_end = bracketend(next + 1);
+ if (next_end - next == (length + 1) && memcmp(begin, next + 1, IN_UCHARS(length)) == 0)
+ {
+ for (i = 0; i < max; i++, next_end += 1 + LINK_SIZE)
+ if (*next_end != OP_KET)
+ break;
+
+ if (i == max)
+ {
+ common->private_data_ptrs[max_end - common->start - LINK_SIZE] = next_end - max_end;
+ common->private_data_ptrs[max_end - common->start - LINK_SIZE + 1] = (type == OP_BRAZERO) ? OP_UPTO : OP_MINUPTO;
+ /* +2 the original and the last. */
+ common->private_data_ptrs[max_end - common->start - LINK_SIZE + 2] = max + 2;
+ if (min == 1)
+ return TRUE;
+ min--;
+ max_end -= (1 + LINK_SIZE) + GET(max_end, -LINK_SIZE);
+ }
+ }
+ }
+ }
+
+if (min >= 3)
+ {
+ common->private_data_ptrs[end - common->start - LINK_SIZE] = max_end - end;
+ common->private_data_ptrs[end - common->start - LINK_SIZE + 1] = OP_EXACT;
+ common->private_data_ptrs[end - common->start - LINK_SIZE + 2] = min;
+ return TRUE;
+ }
+
+return FALSE;
+}
+
+#define CASE_ITERATOR_PRIVATE_DATA_1 \
+ case OP_MINSTAR: \
+ case OP_MINPLUS: \
+ case OP_QUERY: \
+ case OP_MINQUERY: \
+ case OP_MINSTARI: \
+ case OP_MINPLUSI: \
+ case OP_QUERYI: \
+ case OP_MINQUERYI: \
+ case OP_NOTMINSTAR: \
+ case OP_NOTMINPLUS: \
+ case OP_NOTQUERY: \
+ case OP_NOTMINQUERY: \
+ case OP_NOTMINSTARI: \
+ case OP_NOTMINPLUSI: \
+ case OP_NOTQUERYI: \
+ case OP_NOTMINQUERYI:
+
+#define CASE_ITERATOR_PRIVATE_DATA_2A \
+ case OP_STAR: \
+ case OP_PLUS: \
+ case OP_STARI: \
+ case OP_PLUSI: \
+ case OP_NOTSTAR: \
+ case OP_NOTPLUS: \
+ case OP_NOTSTARI: \
+ case OP_NOTPLUSI:
+
+#define CASE_ITERATOR_PRIVATE_DATA_2B \
+ case OP_UPTO: \
+ case OP_MINUPTO: \
+ case OP_UPTOI: \
+ case OP_MINUPTOI: \
+ case OP_NOTUPTO: \
+ case OP_NOTMINUPTO: \
+ case OP_NOTUPTOI: \
+ case OP_NOTMINUPTOI:
+
+#define CASE_ITERATOR_TYPE_PRIVATE_DATA_1 \
+ case OP_TYPEMINSTAR: \
+ case OP_TYPEMINPLUS: \
+ case OP_TYPEQUERY: \
+ case OP_TYPEMINQUERY:
+
+#define CASE_ITERATOR_TYPE_PRIVATE_DATA_2A \
+ case OP_TYPESTAR: \
+ case OP_TYPEPLUS:
+
+#define CASE_ITERATOR_TYPE_PRIVATE_DATA_2B \
+ case OP_TYPEUPTO: \
+ case OP_TYPEMINUPTO:
+
+static void set_private_data_ptrs(compiler_common *common, int *private_data_start, PCRE2_SPTR ccend)
+{
+PCRE2_SPTR cc = common->start;
+PCRE2_SPTR alternative;
+PCRE2_SPTR end = NULL;
+int private_data_ptr = *private_data_start;
+int space, size, bracketlen;
+BOOL repeat_check = TRUE;
+
+while (cc < ccend)
+ {
+ space = 0;
+ size = 0;
+ bracketlen = 0;
+ if (private_data_ptr > SLJIT_MAX_LOCAL_SIZE)
+ break;
+
+ /* When the bracket is prefixed by a zero iteration, skip the repeat check (at this point). */
+ if (repeat_check && (*cc == OP_ONCE || *cc == OP_BRA || *cc == OP_CBRA || *cc == OP_COND))
+ {
+ if (detect_repeat(common, cc))
+ {
+ /* These brackets are converted to repeats, so no global
+ based single character repeat is allowed. */
+ if (cc >= end)
+ end = bracketend(cc);
+ }
+ }
+ repeat_check = TRUE;
+
+ switch(*cc)
+ {
+ case OP_KET:
+ if (common->private_data_ptrs[cc + 1 - common->start] != 0)
+ {
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw);
+ cc += common->private_data_ptrs[cc + 1 - common->start];
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRAPOS:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCOND:
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw);
+ bracketlen = 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw);
+ bracketlen = 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ /* Might be a hidden SCOND. */
+ common->private_data_ptrs[cc - common->start] = 0;
+ alternative = cc + GET(cc, 1);
+ if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
+ {
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw);
+ }
+ bracketlen = 1 + LINK_SIZE;
+ break;
+
+ case OP_BRA:
+ bracketlen = 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ bracketlen = 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ case OP_BRAPOSZERO:
+ size = 1;
+ repeat_check = FALSE;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_1
+ size = -2;
+ space = 1;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2A
+ size = -2;
+ space = 2;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2B
+ size = -(2 + IMM2_SIZE);
+ space = 2;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+ size = 1;
+ space = 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+ size = 1;
+ if (cc[1] != OP_ANYNL && cc[1] != OP_EXTUNI)
+ space = 2;
+ break;
+
+ case OP_TYPEUPTO:
+ size = 1 + IMM2_SIZE;
+ if (cc[1 + IMM2_SIZE] != OP_ANYNL && cc[1 + IMM2_SIZE] != OP_EXTUNI)
+ space = 2;
+ break;
+
+ case OP_TYPEMINUPTO:
+ size = 1 + IMM2_SIZE;
+ space = 2;
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ size = 1 + 32 / sizeof(PCRE2_UCHAR);
+ space = get_class_iterator_size(cc + size);
+ break;
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ size = GET(cc, 1);
+ space = get_class_iterator_size(cc + size);
+ break;
+#endif
+
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ break;
+ }
+
+ /* Character iterators, which are not inside a repeated bracket,
+ gets a private slot instead of allocating it on the stack. */
+ if (space > 0 && cc >= end)
+ {
+ common->private_data_ptrs[cc - common->start] = private_data_ptr;
+ private_data_ptr += sizeof(sljit_sw) * space;
+ }
+
+ if (size != 0)
+ {
+ if (size < 0)
+ {
+ cc += -size;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ }
+ else
+ cc += size;
+ }
+
+ if (bracketlen > 0)
+ {
+ if (cc >= end)
+ {
+ end = bracketend(cc);
+ if (end[-1 - LINK_SIZE] == OP_KET)
+ end = NULL;
+ }
+ cc += bracketlen;
+ }
+ }
+*private_data_start = private_data_ptr;
+}
+
+/* Returns with a frame_types (always < 0) if no need for frame. */
+static int get_framesize(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, BOOL recursive, BOOL *needs_control_head)
+{
+int length = 0;
+int possessive = 0;
+BOOL stack_restore = FALSE;
+BOOL setsom_found = recursive;
+BOOL setmark_found = recursive;
+/* The last capture is a local variable even for recursions. */
+BOOL capture_last_found = FALSE;
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+SLJIT_ASSERT(common->control_head_ptr != 0);
+*needs_control_head = TRUE;
+#else
+*needs_control_head = FALSE;
+#endif
+
+if (ccend == NULL)
+ {
+ ccend = bracketend(cc) - (1 + LINK_SIZE);
+ if (!recursive && (*cc == OP_CBRAPOS || *cc == OP_SCBRAPOS))
+ {
+ possessive = length = (common->capture_last_ptr != 0) ? 5 : 3;
+ /* This is correct regardless of common->capture_last_ptr. */
+ capture_last_found = TRUE;
+ }
+ cc = next_opcode(common, cc);
+ }
+
+SLJIT_ASSERT(cc != NULL);
+while (cc < ccend)
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ stack_restore = TRUE;
+ if (!setsom_found)
+ {
+ length += 2;
+ setsom_found = TRUE;
+ }
+ cc += 1;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ stack_restore = TRUE;
+ if (!setmark_found)
+ {
+ length += 2;
+ setmark_found = TRUE;
+ }
+ if (common->control_head_ptr != 0)
+ *needs_control_head = TRUE;
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_RECURSE:
+ stack_restore = TRUE;
+ if (common->has_set_som && !setsom_found)
+ {
+ length += 2;
+ setsom_found = TRUE;
+ }
+ if (common->mark_ptr != 0 && !setmark_found)
+ {
+ length += 2;
+ setmark_found = TRUE;
+ }
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ length += 2;
+ capture_last_found = TRUE;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ stack_restore = TRUE;
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ length += 2;
+ capture_last_found = TRUE;
+ }
+ length += 3;
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_THEN:
+ stack_restore = TRUE;
+ if (common->control_head_ptr != 0)
+ *needs_control_head = TRUE;
+ cc ++;
+ break;
+
+ default:
+ stack_restore = TRUE;
+ /* Fall through. */
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ case OP_NOTPROP:
+ case OP_PROP:
+ case OP_ANYNL:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ case OP_EXTUNI:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_XCLASS:
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ break;
+ }
+
+/* Possessive quantifiers can use a special case. */
+if (SLJIT_UNLIKELY(possessive == length))
+ return stack_restore ? no_frame : no_stack;
+
+if (length > 0)
+ return length + 1;
+return stack_restore ? no_frame : no_stack;
+}
+
+static void init_frame(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, int stackpos, int stacktop)
+{
+DEFINE_COMPILER;
+BOOL setsom_found = FALSE;
+BOOL setmark_found = FALSE;
+/* The last capture is a local variable even for recursions. */
+BOOL capture_last_found = FALSE;
+int offset;
+
+/* >= 1 + shortest item size (2) */
+SLJIT_UNUSED_ARG(stacktop);
+SLJIT_ASSERT(stackpos >= stacktop + 2);
+
+stackpos = STACK(stackpos);
+if (ccend == NULL)
+ {
+ ccend = bracketend(cc) - (1 + LINK_SIZE);
+ if (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS)
+ cc = next_opcode(common, cc);
+ }
+
+SLJIT_ASSERT(cc != NULL);
+while (cc < ccend)
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ if (!setsom_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0));
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ setsom_found = TRUE;
+ }
+ cc += 1;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ if (!setmark_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ setmark_found = TRUE;
+ }
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_RECURSE:
+ if (common->has_set_som && !setsom_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -OVECTOR(0));
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ setsom_found = TRUE;
+ }
+ if (common->mark_ptr != 0 && !setmark_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->mark_ptr);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ setmark_found = TRUE;
+ }
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ capture_last_found = TRUE;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ if (common->capture_last_ptr != 0 && !capture_last_found)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, -common->capture_last_ptr);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ capture_last_found = TRUE;
+ }
+ offset = (GET2(cc, 1 + LINK_SIZE)) << 1;
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset));
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0);
+ stackpos -= SSIZE_OF(sw);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0);
+ stackpos -= SSIZE_OF(sw);
+
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ break;
+ }
+
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, 0);
+SLJIT_ASSERT(stackpos == STACK(stacktop));
+}
+
+#define RECURSE_TMP_REG_COUNT 3
+
+typedef struct delayed_mem_copy_status {
+ struct sljit_compiler *compiler;
+ int store_bases[RECURSE_TMP_REG_COUNT];
+ int store_offsets[RECURSE_TMP_REG_COUNT];
+ int tmp_regs[RECURSE_TMP_REG_COUNT];
+ int saved_tmp_regs[RECURSE_TMP_REG_COUNT];
+ int next_tmp_reg;
+} delayed_mem_copy_status;
+
+static void delayed_mem_copy_init(delayed_mem_copy_status *status, compiler_common *common)
+{
+int i;
+
+for (i = 0; i < RECURSE_TMP_REG_COUNT; i++)
+ {
+ SLJIT_ASSERT(status->tmp_regs[i] >= 0);
+ SLJIT_ASSERT(sljit_get_register_index(status->saved_tmp_regs[i]) < 0 || status->tmp_regs[i] == status->saved_tmp_regs[i]);
+
+ status->store_bases[i] = -1;
+ }
+status->next_tmp_reg = 0;
+status->compiler = common->compiler;
+}
+
+static void delayed_mem_copy_move(delayed_mem_copy_status *status, int load_base, sljit_sw load_offset,
+ int store_base, sljit_sw store_offset)
+{
+struct sljit_compiler *compiler = status->compiler;
+int next_tmp_reg = status->next_tmp_reg;
+int tmp_reg = status->tmp_regs[next_tmp_reg];
+
+SLJIT_ASSERT(load_base > 0 && store_base > 0);
+
+if (status->store_bases[next_tmp_reg] == -1)
+ {
+ /* Preserve virtual registers. */
+ if (sljit_get_register_index(status->saved_tmp_regs[next_tmp_reg]) < 0)
+ OP1(SLJIT_MOV, status->saved_tmp_regs[next_tmp_reg], 0, tmp_reg, 0);
+ }
+else
+ OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0);
+
+OP1(SLJIT_MOV, tmp_reg, 0, SLJIT_MEM1(load_base), load_offset);
+status->store_bases[next_tmp_reg] = store_base;
+status->store_offsets[next_tmp_reg] = store_offset;
+
+status->next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT;
+}
+
+static void delayed_mem_copy_finish(delayed_mem_copy_status *status)
+{
+struct sljit_compiler *compiler = status->compiler;
+int next_tmp_reg = status->next_tmp_reg;
+int tmp_reg, saved_tmp_reg, i;
+
+for (i = 0; i < RECURSE_TMP_REG_COUNT; i++)
+ {
+ if (status->store_bases[next_tmp_reg] != -1)
+ {
+ tmp_reg = status->tmp_regs[next_tmp_reg];
+ saved_tmp_reg = status->saved_tmp_regs[next_tmp_reg];
+
+ OP1(SLJIT_MOV, SLJIT_MEM1(status->store_bases[next_tmp_reg]), status->store_offsets[next_tmp_reg], tmp_reg, 0);
+
+ /* Restore virtual registers. */
+ if (sljit_get_register_index(saved_tmp_reg) < 0)
+ OP1(SLJIT_MOV, tmp_reg, 0, saved_tmp_reg, 0);
+ }
+
+ next_tmp_reg = (next_tmp_reg + 1) % RECURSE_TMP_REG_COUNT;
+ }
+}
+
+#undef RECURSE_TMP_REG_COUNT
+
+static BOOL recurse_check_bit(compiler_common *common, sljit_sw bit_index)
+{
+uint8_t *byte;
+uint8_t mask;
+
+SLJIT_ASSERT((bit_index & (sizeof(sljit_sw) - 1)) == 0);
+
+bit_index >>= SLJIT_WORD_SHIFT;
+
+SLJIT_ASSERT((bit_index >> 3) < common->recurse_bitset_size);
+
+mask = 1 << (bit_index & 0x7);
+byte = common->recurse_bitset + (bit_index >> 3);
+
+if (*byte & mask)
+ return FALSE;
+
+*byte |= mask;
+return TRUE;
+}
+
+enum get_recurse_flags {
+ recurse_flag_quit_found = (1 << 0),
+ recurse_flag_accept_found = (1 << 1),
+ recurse_flag_setsom_found = (1 << 2),
+ recurse_flag_setmark_found = (1 << 3),
+ recurse_flag_control_head_found = (1 << 4),
+};
+
+static int get_recurse_data_length(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, uint32_t *result_flags)
+{
+int length = 1;
+int size, offset;
+PCRE2_SPTR alternative;
+uint32_t recurse_flags = 0;
+
+memset(common->recurse_bitset, 0, common->recurse_bitset_size);
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+SLJIT_ASSERT(common->control_head_ptr != 0);
+recurse_flags |= recurse_flag_control_head_found;
+#endif
+
+/* Calculate the sum of the private machine words. */
+while (cc < ccend)
+ {
+ size = 0;
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ recurse_flags |= recurse_flag_setsom_found;
+ cc += 1;
+ break;
+
+ case OP_RECURSE:
+ if (common->has_set_som)
+ recurse_flags |= recurse_flag_setsom_found;
+ if (common->mark_ptr != 0)
+ recurse_flags |= recurse_flag_setmark_found;
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ length++;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_KET:
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0)
+ {
+ if (recurse_check_bit(common, offset))
+ length++;
+ SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
+ cc += PRIVATE_DATA(cc + 1);
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRAPOS:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCOND:
+ SLJIT_ASSERT(PRIVATE_DATA(cc) != 0);
+ if (recurse_check_bit(common, PRIVATE_DATA(cc)))
+ length++;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ if (recurse_check_bit(common, OVECTOR(offset << 1)))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, OVECTOR((offset << 1) + 1)));
+ length += 2;
+ }
+ if (common->optimized_cbracket[offset] == 0 && recurse_check_bit(common, OVECTOR_PRIV(offset)))
+ length++;
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ length++;
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ if (recurse_check_bit(common, OVECTOR(offset << 1)))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, OVECTOR((offset << 1) + 1)));
+ length += 2;
+ }
+ if (recurse_check_bit(common, OVECTOR_PRIV(offset)))
+ length++;
+ if (recurse_check_bit(common, PRIVATE_DATA(cc)))
+ length++;
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ length++;
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ /* Might be a hidden SCOND. */
+ alternative = cc + GET(cc, 1);
+ if ((*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) && recurse_check_bit(common, PRIVATE_DATA(cc)))
+ length++;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_1
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ length++;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2A
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
+ length += 2;
+ }
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2B
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
+ length += 2;
+ }
+ cc += 2 + IMM2_SIZE;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ length++;
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
+ length += 2;
+ }
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ {
+ SLJIT_ASSERT(recurse_check_bit(common, offset + sizeof(sljit_sw)));
+ length += 2;
+ }
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ size = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+#else
+ size = 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+#endif
+
+ offset = PRIVATE_DATA(cc);
+ if (offset != 0 && recurse_check_bit(common, offset))
+ length += get_class_iterator_size(cc + size);
+ cc += size;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ recurse_flags |= recurse_flag_setmark_found;
+ if (common->control_head_ptr != 0)
+ recurse_flags |= recurse_flag_control_head_found;
+ if (*cc != OP_MARK)
+ recurse_flags |= recurse_flag_quit_found;
+
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_PRUNE:
+ case OP_SKIP:
+ case OP_COMMIT:
+ recurse_flags |= recurse_flag_quit_found;
+ cc++;
+ break;
+
+ case OP_SKIP_ARG:
+ recurse_flags |= recurse_flag_quit_found;
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_THEN:
+ SLJIT_ASSERT(common->control_head_ptr != 0);
+ recurse_flags |= recurse_flag_quit_found | recurse_flag_control_head_found;
+ cc++;
+ break;
+
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ recurse_flags |= recurse_flag_accept_found;
+ cc++;
+ break;
+
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ break;
+ }
+ }
+SLJIT_ASSERT(cc == ccend);
+
+if (recurse_flags & recurse_flag_control_head_found)
+ length++;
+if (recurse_flags & recurse_flag_quit_found)
+ {
+ if (recurse_flags & recurse_flag_setsom_found)
+ length++;
+ if (recurse_flags & recurse_flag_setmark_found)
+ length++;
+ }
+
+*result_flags = recurse_flags;
+return length;
+}
+
+enum copy_recurse_data_types {
+ recurse_copy_from_global,
+ recurse_copy_private_to_global,
+ recurse_copy_shared_to_global,
+ recurse_copy_kept_shared_to_global,
+ recurse_swap_global
+};
+
+static void copy_recurse_data(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend,
+ int type, int stackptr, int stacktop, uint32_t recurse_flags)
+{
+delayed_mem_copy_status status;
+PCRE2_SPTR alternative;
+sljit_sw private_srcw[2];
+sljit_sw shared_srcw[3];
+sljit_sw kept_shared_srcw[2];
+int private_count, shared_count, kept_shared_count;
+int from_sp, base_reg, offset, i;
+
+memset(common->recurse_bitset, 0, common->recurse_bitset_size);
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+SLJIT_ASSERT(common->control_head_ptr != 0);
+recurse_check_bit(common, common->control_head_ptr);
+#endif
+
+switch (type)
+ {
+ case recurse_copy_from_global:
+ from_sp = TRUE;
+ base_reg = STACK_TOP;
+ break;
+
+ case recurse_copy_private_to_global:
+ case recurse_copy_shared_to_global:
+ case recurse_copy_kept_shared_to_global:
+ from_sp = FALSE;
+ base_reg = STACK_TOP;
+ break;
+
+ default:
+ SLJIT_ASSERT(type == recurse_swap_global);
+ from_sp = FALSE;
+ base_reg = TMP2;
+ break;
+ }
+
+stackptr = STACK(stackptr);
+stacktop = STACK(stacktop);
+
+status.tmp_regs[0] = TMP1;
+status.saved_tmp_regs[0] = TMP1;
+
+if (base_reg != TMP2)
+ {
+ status.tmp_regs[1] = TMP2;
+ status.saved_tmp_regs[1] = TMP2;
+ }
+else
+ {
+ status.saved_tmp_regs[1] = RETURN_ADDR;
+ if (HAS_VIRTUAL_REGISTERS)
+ status.tmp_regs[1] = STR_PTR;
+ else
+ status.tmp_regs[1] = RETURN_ADDR;
+ }
+
+status.saved_tmp_regs[2] = TMP3;
+if (HAS_VIRTUAL_REGISTERS)
+ status.tmp_regs[2] = STR_END;
+else
+ status.tmp_regs[2] = TMP3;
+
+delayed_mem_copy_init(&status, common);
+
+if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->recursive_head_ptr);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, common->recursive_head_ptr, base_reg, stackptr);
+ }
+
+stackptr += sizeof(sljit_sw);
+
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+if (type != recurse_copy_shared_to_global)
+ {
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, common->control_head_ptr);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, common->control_head_ptr, base_reg, stackptr);
+ }
+
+stackptr += sizeof(sljit_sw);
+#endif
+
+while (cc < ccend)
+ {
+ private_count = 0;
+ shared_count = 0;
+ kept_shared_count = 0;
+
+ switch(*cc)
+ {
+ case OP_SET_SOM:
+ SLJIT_ASSERT(common->has_set_som);
+ if ((recurse_flags & recurse_flag_quit_found) && recurse_check_bit(common, OVECTOR(0)))
+ {
+ kept_shared_srcw[0] = OVECTOR(0);
+ kept_shared_count = 1;
+ }
+ cc += 1;
+ break;
+
+ case OP_RECURSE:
+ if (recurse_flags & recurse_flag_quit_found)
+ {
+ if (common->has_set_som && recurse_check_bit(common, OVECTOR(0)))
+ {
+ kept_shared_srcw[0] = OVECTOR(0);
+ kept_shared_count = 1;
+ }
+ if (common->mark_ptr != 0 && recurse_check_bit(common, common->mark_ptr))
+ {
+ kept_shared_srcw[kept_shared_count] = common->mark_ptr;
+ kept_shared_count++;
+ }
+ }
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ {
+ shared_srcw[0] = common->capture_last_ptr;
+ shared_count = 1;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_KET:
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0)
+ {
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ SLJIT_ASSERT(PRIVATE_DATA(cc + 1) != 0);
+ cc += PRIVATE_DATA(cc + 1);
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRAPOS:
+ case OP_SBRA:
+ case OP_SBRAPOS:
+ case OP_SCOND:
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ cc += 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ shared_srcw[0] = OVECTOR(offset << 1);
+ if (recurse_check_bit(common, shared_srcw[0]))
+ {
+ shared_srcw[1] = shared_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, shared_srcw[1]));
+ shared_count = 2;
+ }
+
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ {
+ shared_srcw[shared_count] = common->capture_last_ptr;
+ shared_count++;
+ }
+
+ if (common->optimized_cbracket[offset] == 0)
+ {
+ private_srcw[0] = OVECTOR_PRIV(offset);
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ }
+
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ shared_srcw[0] = OVECTOR(offset << 1);
+ if (recurse_check_bit(common, shared_srcw[0]))
+ {
+ shared_srcw[1] = shared_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, shared_srcw[1]));
+ shared_count = 2;
+ }
+
+ if (common->capture_last_ptr != 0 && recurse_check_bit(common, common->capture_last_ptr))
+ {
+ shared_srcw[shared_count] = common->capture_last_ptr;
+ shared_count++;
+ }
+
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+
+ offset = OVECTOR_PRIV(offset);
+ if (recurse_check_bit(common, offset))
+ {
+ private_srcw[private_count] = offset;
+ private_count++;
+ }
+ cc += 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ case OP_COND:
+ /* Might be a hidden SCOND. */
+ alternative = cc + GET(cc, 1);
+ if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN)
+ {
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ }
+ cc += 1 + LINK_SIZE;
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_1
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2A
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_PRIVATE_DATA_2B
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ cc += 2 + IMM2_SIZE;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ private_count = 1;
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ cc += 1;
+ break;
+
+ CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
+ private_srcw[0] = PRIVATE_DATA(cc);
+ if (private_srcw[0] != 0 && recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ i = (*cc == OP_XCLASS) ? GET(cc, 1) : 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+#else
+ i = 1 + 32 / (int)sizeof(PCRE2_UCHAR);
+#endif
+ if (PRIVATE_DATA(cc) != 0)
+ {
+ private_count = 1;
+ private_srcw[0] = PRIVATE_DATA(cc);
+ switch(get_class_iterator_size(cc + i))
+ {
+ case 1:
+ break;
+
+ case 2:
+ if (recurse_check_bit(common, private_srcw[0]))
+ {
+ private_count = 2;
+ private_srcw[1] = private_srcw[0] + sizeof(sljit_sw);
+ SLJIT_ASSERT(recurse_check_bit(common, private_srcw[1]));
+ }
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ }
+ cc += i;
+ break;
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_THEN_ARG:
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ if ((recurse_flags & recurse_flag_quit_found) && recurse_check_bit(common, common->mark_ptr))
+ {
+ kept_shared_srcw[0] = common->mark_ptr;
+ kept_shared_count = 1;
+ }
+ if (common->control_head_ptr != 0 && recurse_check_bit(common, common->control_head_ptr))
+ {
+ private_srcw[0] = common->control_head_ptr;
+ private_count = 1;
+ }
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_THEN:
+ SLJIT_ASSERT(common->control_head_ptr != 0);
+ if (recurse_check_bit(common, common->control_head_ptr))
+ {
+ private_srcw[0] = common->control_head_ptr;
+ private_count = 1;
+ }
+ cc++;
+ break;
+
+ default:
+ cc = next_opcode(common, cc);
+ SLJIT_ASSERT(cc != NULL);
+ continue;
+ }
+
+ if (type != recurse_copy_shared_to_global && type != recurse_copy_kept_shared_to_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_private_to_global || type == recurse_swap_global);
+
+ for (i = 0; i < private_count; i++)
+ {
+ SLJIT_ASSERT(private_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, private_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, private_srcw[i], base_reg, stackptr);
+
+ stackptr += sizeof(sljit_sw);
+ }
+ }
+ else
+ stackptr += sizeof(sljit_sw) * private_count;
+
+ if (type != recurse_copy_private_to_global && type != recurse_copy_kept_shared_to_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_swap_global);
+
+ for (i = 0; i < shared_count; i++)
+ {
+ SLJIT_ASSERT(shared_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, shared_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, shared_srcw[i], base_reg, stackptr);
+
+ stackptr += sizeof(sljit_sw);
+ }
+ }
+ else
+ stackptr += sizeof(sljit_sw) * shared_count;
+
+ if (type != recurse_copy_private_to_global && type != recurse_swap_global)
+ {
+ SLJIT_ASSERT(type == recurse_copy_from_global || type == recurse_copy_shared_to_global || type == recurse_copy_kept_shared_to_global);
+
+ for (i = 0; i < kept_shared_count; i++)
+ {
+ SLJIT_ASSERT(kept_shared_srcw[i] != 0);
+
+ if (!from_sp)
+ delayed_mem_copy_move(&status, base_reg, stackptr, SLJIT_SP, kept_shared_srcw[i]);
+
+ if (from_sp || type == recurse_swap_global)
+ delayed_mem_copy_move(&status, SLJIT_SP, kept_shared_srcw[i], base_reg, stackptr);
+
+ stackptr += sizeof(sljit_sw);
+ }
+ }
+ else
+ stackptr += sizeof(sljit_sw) * kept_shared_count;
+ }
+
+SLJIT_ASSERT(cc == ccend && stackptr == stacktop);
+
+delayed_mem_copy_finish(&status);
+}
+
+static SLJIT_INLINE PCRE2_SPTR set_then_offsets(compiler_common *common, PCRE2_SPTR cc, sljit_u8 *current_offset)
+{
+PCRE2_SPTR end = bracketend(cc);
+BOOL has_alternatives = cc[GET(cc, 1)] == OP_ALT;
+
+/* Assert captures then. */
+if (*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA)
+ current_offset = NULL;
+/* Conditional block does not. */
+if (*cc == OP_COND || *cc == OP_SCOND)
+ has_alternatives = FALSE;
+
+cc = next_opcode(common, cc);
+if (has_alternatives)
+ current_offset = common->then_offsets + (cc - common->start);
+
+while (cc < end)
+ {
+ if ((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NA) || (*cc >= OP_ONCE && *cc <= OP_SCOND))
+ cc = set_then_offsets(common, cc, current_offset);
+ else
+ {
+ if (*cc == OP_ALT && has_alternatives)
+ current_offset = common->then_offsets + (cc + 1 + LINK_SIZE - common->start);
+ if (*cc >= OP_THEN && *cc <= OP_THEN_ARG && current_offset != NULL)
+ *current_offset = 1;
+ cc = next_opcode(common, cc);
+ }
+ }
+
+return end;
+}
+
+#undef CASE_ITERATOR_PRIVATE_DATA_1
+#undef CASE_ITERATOR_PRIVATE_DATA_2A
+#undef CASE_ITERATOR_PRIVATE_DATA_2B
+#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_1
+#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_2A
+#undef CASE_ITERATOR_TYPE_PRIVATE_DATA_2B
+
+static SLJIT_INLINE BOOL is_powerof2(unsigned int value)
+{
+return (value & (value - 1)) == 0;
+}
+
+static SLJIT_INLINE void set_jumps(jump_list *list, struct sljit_label *label)
+{
+while (list)
+ {
+ /* sljit_set_label is clever enough to do nothing
+ if either the jump or the label is NULL. */
+ SET_LABEL(list->jump, label);
+ list = list->next;
+ }
+}
+
+static SLJIT_INLINE void add_jump(struct sljit_compiler *compiler, jump_list **list, struct sljit_jump *jump)
+{
+jump_list *list_item = sljit_alloc_memory(compiler, sizeof(jump_list));
+if (list_item)
+ {
+ list_item->next = *list;
+ list_item->jump = jump;
+ *list = list_item;
+ }
+}
+
+static void add_stub(compiler_common *common, struct sljit_jump *start)
+{
+DEFINE_COMPILER;
+stub_list *list_item = sljit_alloc_memory(compiler, sizeof(stub_list));
+
+if (list_item)
+ {
+ list_item->start = start;
+ list_item->quit = LABEL();
+ list_item->next = common->stubs;
+ common->stubs = list_item;
+ }
+}
+
+static void flush_stubs(compiler_common *common)
+{
+DEFINE_COMPILER;
+stub_list *list_item = common->stubs;
+
+while (list_item)
+ {
+ JUMPHERE(list_item->start);
+ add_jump(compiler, &common->stackalloc, JUMP(SLJIT_FAST_CALL));
+ JUMPTO(SLJIT_JUMP, list_item->quit);
+ list_item = list_item->next;
+ }
+common->stubs = NULL;
+}
+
+static SLJIT_INLINE void count_match(compiler_common *common)
+{
+DEFINE_COMPILER;
+
+OP2(SLJIT_SUB | SLJIT_SET_Z, COUNT_MATCH, 0, COUNT_MATCH, 0, SLJIT_IMM, 1);
+add_jump(compiler, &common->calllimit, JUMP(SLJIT_ZERO));
+}
+
+static SLJIT_INLINE void allocate_stack(compiler_common *common, int size)
+{
+/* May destroy all locals and registers except TMP2. */
+DEFINE_COMPILER;
+
+SLJIT_ASSERT(size > 0);
+OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * SSIZE_OF(sw));
+#ifdef DESTROY_REGISTERS
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345);
+OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
+OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP1, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP1, 0);
+#endif
+add_stub(common, CMP(SLJIT_LESS, STACK_TOP, 0, STACK_LIMIT, 0));
+}
+
+static SLJIT_INLINE void free_stack(compiler_common *common, int size)
+{
+DEFINE_COMPILER;
+
+SLJIT_ASSERT(size > 0);
+OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * SSIZE_OF(sw));
+}
+
+static sljit_uw * allocate_read_only_data(compiler_common *common, sljit_uw size)
+{
+DEFINE_COMPILER;
+sljit_uw *result;
+
+if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+
+result = (sljit_uw *)SLJIT_MALLOC(size + sizeof(sljit_uw), compiler->allocator_data);
+if (SLJIT_UNLIKELY(result == NULL))
+ {
+ sljit_set_compiler_memory_error(compiler);
+ return NULL;
+ }
+
+*(void**)result = common->read_only_data_head;
+common->read_only_data_head = (void *)result;
+return result + 1;
+}
+
+static SLJIT_INLINE void reset_ovector(compiler_common *common, int length)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+sljit_s32 i;
+
+/* At this point we can freely use all temporary registers. */
+SLJIT_ASSERT(length > 1);
+/* TMP1 returns with begin - 1. */
+OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_S0), SLJIT_OFFSETOF(jit_arguments, begin), SLJIT_IMM, IN_UCHARS(1));
+if (length < 8)
+ {
+ for (i = 1; i < length; i++)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(i), SLJIT_R0, 0);
+ }
+else
+ {
+ if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw)) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START);
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1);
+ loop = LABEL();
+ sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, SLJIT_R0, SLJIT_MEM1(SLJIT_R1), sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ else
+ {
+ GET_LOCAL_BASE(SLJIT_R1, 0, OVECTOR_START + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, length - 1);
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R1), 0, SLJIT_R0, 0);
+ OP2(SLJIT_ADD, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ }
+}
+
+static SLJIT_INLINE void reset_early_fail(compiler_common *common)
+{
+DEFINE_COMPILER;
+sljit_u32 size = (sljit_u32)(common->early_fail_end_ptr - common->early_fail_start_ptr);
+sljit_u32 uncleared_size;
+sljit_s32 src = SLJIT_IMM;
+sljit_s32 i;
+struct sljit_label *loop;
+
+SLJIT_ASSERT(common->early_fail_start_ptr < common->early_fail_end_ptr);
+
+if (size == sizeof(sljit_sw))
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->early_fail_start_ptr, SLJIT_IMM, 0);
+ return;
+ }
+
+if (sljit_get_register_index(TMP3) >= 0 && !sljit_has_cpu_feature(SLJIT_HAS_ZERO_REGISTER))
+ {
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0);
+ src = TMP3;
+ }
+
+if (size <= 6 * sizeof(sljit_sw))
+ {
+ for (i = common->early_fail_start_ptr; i < common->early_fail_end_ptr; i += sizeof(sljit_sw))
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), i, src, 0);
+ return;
+ }
+
+GET_LOCAL_BASE(TMP1, 0, common->early_fail_start_ptr);
+
+uncleared_size = ((size / sizeof(sljit_sw)) % 3) * sizeof(sljit_sw);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, size - uncleared_size);
+
+loop = LABEL();
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), 0, src, 0);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_sw));
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -2 * SSIZE_OF(sw), src, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), -1 * SSIZE_OF(sw), src, 0);
+CMPTO(SLJIT_LESS, TMP1, 0, TMP2, 0, loop);
+
+if (uncleared_size >= sizeof(sljit_sw))
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), 0, src, 0);
+
+if (uncleared_size >= 2 * sizeof(sljit_sw))
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), sizeof(sljit_sw), src, 0);
+}
+
+static SLJIT_INLINE void do_reset_match(compiler_common *common, int length)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+int i;
+
+SLJIT_ASSERT(length > 1);
+/* OVECTOR(1) contains the "string begin - 1" constant. */
+if (length > 2)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
+if (length < 8)
+ {
+ for (i = 2; i < length; i++)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(i), TMP1, 0);
+ }
+else
+ {
+ if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw)) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2);
+ loop = LABEL();
+ sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_STORE | SLJIT_MEM_PRE, TMP1, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ else
+ {
+ GET_LOCAL_BASE(TMP2, 0, OVECTOR_START + 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_IMM, length - 2);
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, loop);
+ }
+ }
+
+if (!HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, stack));
+else
+ OP1(SLJIT_MOV, STACK_TOP, 0, ARGUMENTS, 0);
+
+if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, SLJIT_IMM, 0);
+if (common->control_head_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(jit_arguments, stack));
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), SLJIT_OFFSETOF(struct sljit_stack, end));
+}
+
+static sljit_sw SLJIT_FUNC do_search_mark(sljit_sw *current, PCRE2_SPTR skip_arg)
+{
+while (current != NULL)
+ {
+ switch (current[1])
+ {
+ case type_then_trap:
+ break;
+
+ case type_mark:
+ if (PRIV(strcmp)(skip_arg, (PCRE2_SPTR)current[2]) == 0)
+ return current[3];
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ SLJIT_ASSERT(current[0] == 0 || current < (sljit_sw*)current[0]);
+ current = (sljit_sw*)current[0];
+ }
+return 0;
+}
+
+static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+BOOL has_pre;
+
+/* At this point we can freely use all registers. */
+OP1(SLJIT_MOV, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(1), STR_PTR, 0);
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+ if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ OP1(SLJIT_MOV_U32, SLJIT_R1, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, oveccount));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_S0, 0);
+ if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, mark_ptr), SLJIT_R2, 0);
+ OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, match_data),
+ SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE));
+ }
+else
+ {
+ OP1(SLJIT_MOV, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, match_data));
+ if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ OP1(SLJIT_MOV_U32, SLJIT_R1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, oveccount));
+ OP1(SLJIT_MOV, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_S0, 0);
+ if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), SLJIT_R0, 0);
+ OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, SLJIT_OFFSETOF(pcre2_match_data, ovector) - sizeof(PCRE2_SIZE));
+ }
+
+has_pre = sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw)) == SLJIT_SUCCESS;
+
+GET_LOCAL_BASE(SLJIT_S0, 0, OVECTOR_START - (has_pre ? sizeof(sljit_sw) : 0));
+OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? SLJIT_R0 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+
+loop = LABEL();
+
+if (has_pre)
+ sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_S1, SLJIT_MEM1(SLJIT_S0), sizeof(sljit_sw));
+else
+ {
+ OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_S0), 0);
+ OP2(SLJIT_ADD, SLJIT_S0, 0, SLJIT_S0, 0, SLJIT_IMM, sizeof(sljit_sw));
+ }
+
+OP2(SLJIT_ADD, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, sizeof(PCRE2_SIZE));
+OP2(SLJIT_SUB, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_R0, 0);
+/* Copy the integer value to the output buffer */
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+OP2(SLJIT_ASHR, SLJIT_S1, 0, SLJIT_S1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+
+SLJIT_ASSERT(sizeof(PCRE2_SIZE) == 4 || sizeof(PCRE2_SIZE) == 8);
+OP1(((sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV), SLJIT_MEM1(SLJIT_R2), 0, SLJIT_S1, 0);
+
+OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+JUMPTO(SLJIT_NOT_ZERO, loop);
+
+/* Calculate the return value, which is the maximum ovector value. */
+if (topbracket > 1)
+ {
+ if (sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * SSIZE_OF(sw))) == SLJIT_SUCCESS)
+ {
+ GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + topbracket * 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1);
+
+ /* OVECTOR(0) is never equal to SLJIT_S2. */
+ loop = LABEL();
+ sljit_emit_mem_update(compiler, SLJIT_MOV | SLJIT_MEM_PRE, SLJIT_R2, SLJIT_MEM1(SLJIT_R0), -(2 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+ CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0);
+ }
+ else
+ {
+ GET_LOCAL_BASE(SLJIT_R0, 0, OVECTOR_START + (topbracket - 1) * 2 * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, topbracket + 1);
+
+ /* OVECTOR(0) is never equal to SLJIT_S2. */
+ loop = LABEL();
+ OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_R0), 0);
+ OP2(SLJIT_SUB, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, 2 * SSIZE_OF(sw));
+ OP2(SLJIT_SUB, SLJIT_R1, 0, SLJIT_R1, 0, SLJIT_IMM, 1);
+ CMPTO(SLJIT_EQUAL, SLJIT_R2, 0, SLJIT_S2, 0, loop);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_R1, 0);
+ }
+ }
+else
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1);
+}
+
+static SLJIT_INLINE void return_with_partial_match(compiler_common *common, struct sljit_label *quit)
+{
+DEFINE_COMPILER;
+sljit_s32 mov_opcode;
+sljit_s32 arguments_reg = !HAS_VIRTUAL_REGISTERS ? ARGUMENTS : SLJIT_R1;
+
+SLJIT_COMPILE_ASSERT(STR_END == SLJIT_S0, str_end_must_be_saved_reg0);
+SLJIT_ASSERT(common->start_used_ptr != 0 && common->start_ptr != 0
+ && (common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start != 0 : common->hit_start == 0));
+
+if (arguments_reg != ARGUMENTS)
+ OP1(SLJIT_MOV, arguments_reg, 0, ARGUMENTS, 0);
+OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP),
+ common->mode == PCRE2_JIT_PARTIAL_SOFT ? common->hit_start : common->start_ptr);
+OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_PARTIAL);
+
+/* Store match begin and end. */
+OP1(SLJIT_MOV, SLJIT_S1, 0, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, begin));
+OP1(SLJIT_MOV, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, startchar_ptr), SLJIT_R2, 0);
+OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_MEM1(arguments_reg), SLJIT_OFFSETOF(jit_arguments, match_data));
+
+mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV;
+
+OP2(SLJIT_SUB, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_S1, 0);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+OP2(SLJIT_ASHR, SLJIT_R2, 0, SLJIT_R2, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector), SLJIT_R2, 0);
+
+OP2(SLJIT_SUB, STR_END, 0, STR_END, 0, SLJIT_S1, 0);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+OP2(SLJIT_ASHR, STR_END, 0, STR_END, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+OP1(mov_opcode, SLJIT_MEM1(SLJIT_R1), SLJIT_OFFSETOF(pcre2_match_data, ovector) + sizeof(PCRE2_SIZE), STR_END, 0);
+
+JUMPTO(SLJIT_JUMP, quit);
+}
+
+static SLJIT_INLINE void check_start_used_ptr(compiler_common *common)
+{
+/* May destroy TMP1. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ /* The value of -1 must be kept for start_used_ptr! */
+ OP2(SLJIT_ADD, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, 1);
+ /* Jumps if start_used_ptr < STR_PTR, or start_used_ptr == -1. Although overwriting
+ is not necessary if start_used_ptr == STR_PTR, it does not hurt as well. */
+ jump = CMP(SLJIT_LESS_EQUAL, TMP1, 0, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+ }
+else if (common->mode == PCRE2_JIT_PARTIAL_HARD)
+ {
+ jump = CMP(SLJIT_LESS_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+ }
+}
+
+static SLJIT_INLINE BOOL char_has_othercase(compiler_common *common, PCRE2_SPTR cc)
+{
+/* Detects if the character has an othercase. */
+unsigned int c;
+
+#ifdef SUPPORT_UNICODE
+if (common->utf || common->ucp)
+ {
+ if (common->utf)
+ {
+ GETCHAR(c, cc);
+ }
+ else
+ c = *cc;
+
+ if (c > 127)
+ return c != UCD_OTHERCASE(c);
+
+ return common->fcc[c] != c;
+ }
+else
+#endif
+ c = *cc;
+return MAX_255(c) ? common->fcc[c] != c : FALSE;
+}
+
+static SLJIT_INLINE unsigned int char_othercase(compiler_common *common, unsigned int c)
+{
+/* Returns with the othercase. */
+#ifdef SUPPORT_UNICODE
+if ((common->utf || common->ucp) && c > 127)
+ return UCD_OTHERCASE(c);
+#endif
+return TABLE_GET(c, common->fcc, c);
+}
+
+static unsigned int char_get_othercase_bit(compiler_common *common, PCRE2_SPTR cc)
+{
+/* Detects if the character and its othercase has only 1 bit difference. */
+unsigned int c, oc, bit;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+int n;
+#endif
+
+#ifdef SUPPORT_UNICODE
+if (common->utf || common->ucp)
+ {
+ if (common->utf)
+ {
+ GETCHAR(c, cc);
+ }
+ else
+ c = *cc;
+
+ if (c <= 127)
+ oc = common->fcc[c];
+ else
+ oc = UCD_OTHERCASE(c);
+ }
+else
+ {
+ c = *cc;
+ oc = TABLE_GET(c, common->fcc, c);
+ }
+#else
+c = *cc;
+oc = TABLE_GET(c, common->fcc, c);
+#endif
+
+SLJIT_ASSERT(c != oc);
+
+bit = c ^ oc;
+/* Optimized for English alphabet. */
+if (c <= 127 && bit == 0x20)
+ return (0 << 8) | 0x20;
+
+/* Since c != oc, they must have at least 1 bit difference. */
+if (!is_powerof2(bit))
+ return 0;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+
+#ifdef SUPPORT_UNICODE
+if (common->utf && c > 127)
+ {
+ n = GET_EXTRALEN(*cc);
+ while ((bit & 0x3f) == 0)
+ {
+ n--;
+ bit >>= 6;
+ }
+ return (n << 8) | bit;
+ }
+#endif /* SUPPORT_UNICODE */
+return (0 << 8) | bit;
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+
+#ifdef SUPPORT_UNICODE
+if (common->utf && c > 65535)
+ {
+ if (bit >= (1u << 10))
+ bit >>= 10;
+ else
+ return (bit < 256) ? ((2 << 8) | bit) : ((3 << 8) | (bit >> 8));
+ }
+#endif /* SUPPORT_UNICODE */
+return (bit < 256) ? ((0u << 8) | bit) : ((1u << 8) | (bit >> 8));
+
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+}
+
+static void check_partial(compiler_common *common, BOOL force)
+{
+/* Checks whether a partial matching is occurred. Does not modify registers. */
+DEFINE_COMPILER;
+struct sljit_jump *jump = NULL;
+
+SLJIT_ASSERT(!force || common->mode != PCRE2_JIT_COMPLETE);
+
+if (common->mode == PCRE2_JIT_COMPLETE)
+ return;
+
+if (!force && !common->allow_empty_partial)
+ jump = CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+else if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ jump = CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1);
+
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+else
+ {
+ if (common->partialmatchlabel != NULL)
+ JUMPTO(SLJIT_JUMP, common->partialmatchlabel);
+ else
+ add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP));
+ }
+
+if (jump != NULL)
+ JUMPHERE(jump);
+}
+
+static void check_str_end(compiler_common *common, jump_list **end_reached)
+{
+/* Does not affect registers. Usually used in a tight spot. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+if (common->mode == PCRE2_JIT_COMPLETE)
+ {
+ add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ return;
+ }
+
+jump = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+ add_jump(compiler, end_reached, JUMP(SLJIT_JUMP));
+ }
+else
+ {
+ add_jump(compiler, end_reached, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0));
+ if (common->partialmatchlabel != NULL)
+ JUMPTO(SLJIT_JUMP, common->partialmatchlabel);
+ else
+ add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP));
+ }
+JUMPHERE(jump);
+}
+
+static void detect_partial_match(compiler_common *common, jump_list **backtracks)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+if (common->mode == PCRE2_JIT_COMPLETE)
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ return;
+ }
+
+/* Partial matching mode. */
+jump = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+if (!common->allow_empty_partial)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0));
+else if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1));
+
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ }
+else
+ {
+ if (common->partialmatchlabel != NULL)
+ JUMPTO(SLJIT_JUMP, common->partialmatchlabel);
+ else
+ add_jump(compiler, &common->partialmatch, JUMP(SLJIT_JUMP));
+ }
+JUMPHERE(jump);
+}
+
+static void process_partial_match(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+/* Partial matching mode. */
+if (common->mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+ JUMPHERE(jump);
+ }
+else if (common->mode == PCRE2_JIT_PARTIAL_HARD)
+ {
+ if (common->partialmatchlabel != NULL)
+ CMPTO(SLJIT_LESS, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0, common->partialmatchlabel);
+ else
+ add_jump(compiler, &common->partialmatch, CMP(SLJIT_LESS, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0));
+ }
+}
+
+static void detect_partial_match_to(compiler_common *common, struct sljit_label *label)
+{
+DEFINE_COMPILER;
+
+CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, label);
+process_partial_match(common);
+}
+
+static void peek_char(compiler_common *common, sljit_u32 max, sljit_s32 dst, sljit_sw dstw, jump_list **backtracks)
+{
+/* Reads the character into TMP1, keeps STR_PTR.
+Does not check STR_END. TMP2, dst, RETURN_ADDR Destroyed. */
+DEFINE_COMPILER;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_jump *jump;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+
+SLJIT_UNUSED_ARG(max);
+SLJIT_UNUSED_ARG(dst);
+SLJIT_UNUSED_ARG(dstw);
+SLJIT_UNUSED_ARG(backtracks);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+ if (max < 128) return;
+
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80);
+ OP1(SLJIT_MOV, dst, dstw, STR_PTR, 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, common->invalid_utf ? &common->utfreadchar_invalid : &common->utfreadchar, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, STR_PTR, 0, dst, dstw);
+ if (backtracks && common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf)
+ {
+ if (max < 0xd800) return;
+
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+
+ if (common->invalid_utf)
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ OP1(SLJIT_MOV, dst, dstw, STR_PTR, 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, STR_PTR, 0, dst, dstw);
+ if (backtracks && common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ }
+ else
+ {
+ /* TMP2 contains the high surrogate. */
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xdc00 - 0xd800);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ }
+
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf)
+ {
+ if (max < 0xd800) return;
+
+ if (backtracks != NULL)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800));
+ }
+ else
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ }
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+}
+
+static void peek_char_back(compiler_common *common, sljit_u32 max, jump_list **backtracks)
+{
+/* Reads one character back without moving STR_PTR. TMP2 must
+contain the start of the subject buffer. Affects TMP1, TMP2, and RETURN_ADDR. */
+DEFINE_COMPILER;
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_jump *jump;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+
+SLJIT_UNUSED_ARG(max);
+SLJIT_UNUSED_ARG(backtracks);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+ if (max < 128) return;
+
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80);
+ if (common->invalid_utf)
+ {
+ add_jump(compiler, &common->utfpeakcharback_invalid, JUMP(SLJIT_FAST_CALL));
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ }
+ else
+ add_jump(compiler, &common->utfpeakcharback, JUMP(SLJIT_FAST_CALL));
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf)
+ {
+ if (max < 0xd800) return;
+
+ if (common->invalid_utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
+ add_jump(compiler, &common->utfpeakcharback_invalid, JUMP(SLJIT_FAST_CALL));
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ }
+ else
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xdc00);
+ /* TMP2 contains the low surrogate. */
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x10000);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 10);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ }
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800));
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+}
+
+#define READ_CHAR_UPDATE_STR_PTR 0x1
+#define READ_CHAR_UTF8_NEWLINE 0x2
+#define READ_CHAR_NEWLINE (READ_CHAR_UPDATE_STR_PTR | READ_CHAR_UTF8_NEWLINE)
+#define READ_CHAR_VALID_UTF 0x4
+
+static void read_char(compiler_common *common, sljit_u32 min, sljit_u32 max,
+ jump_list **backtracks, sljit_u32 options)
+{
+/* Reads the precise value of a character into TMP1, if the character is
+between min and max (c >= min && c <= max). Otherwise it returns with a value
+outside the range. Does not check STR_END. */
+DEFINE_COMPILER;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_jump *jump;
+#endif
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+struct sljit_jump *jump2;
+#endif
+
+SLJIT_UNUSED_ARG(min);
+SLJIT_UNUSED_ARG(max);
+SLJIT_UNUSED_ARG(backtracks);
+SLJIT_UNUSED_ARG(options);
+SLJIT_ASSERT(min <= max);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+ if (max < 128 && !(options & READ_CHAR_UPDATE_STR_PTR)) return;
+
+ if (common->invalid_utf && !(options & READ_CHAR_VALID_UTF))
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80);
+
+ if (options & READ_CHAR_UTF8_NEWLINE)
+ add_jump(compiler, &common->utfreadnewline_invalid, JUMP(SLJIT_FAST_CALL));
+ else
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ JUMPHERE(jump);
+ return;
+ }
+
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
+ if (min >= 0x10000)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xf0);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2));
+ if (!(options & READ_CHAR_UPDATE_STR_PTR))
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ JUMPHERE(jump2);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0);
+ }
+ else if (min >= 0x800 && max <= 0xffff)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xe0);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0xf);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ if (!(options & READ_CHAR_UPDATE_STR_PTR))
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ JUMPHERE(jump2);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0);
+ }
+ else if (max >= 0x800)
+ {
+ add_jump(compiler, &common->utfreadchar, JUMP(SLJIT_FAST_CALL));
+ }
+ else if (max < 128)
+ {
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+ }
+ else
+ {
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (!(options & READ_CHAR_UPDATE_STR_PTR))
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ else
+ OP1(SLJIT_MOV_U8, RETURN_ADDR, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, RETURN_ADDR, 0);
+ }
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf)
+ {
+ if (max < 0xd800 && !(options & READ_CHAR_UPDATE_STR_PTR)) return;
+
+ if (common->invalid_utf && !(options & READ_CHAR_VALID_UTF))
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+
+ if (options & READ_CHAR_UTF8_NEWLINE)
+ add_jump(compiler, &common->utfreadnewline_invalid, JUMP(SLJIT_FAST_CALL));
+ else
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ JUMPHERE(jump);
+ return;
+ }
+
+ if (max >= 0x10000)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xdc00 - 0xd800);
+ /* TMP2 contains the high surrogate. */
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ JUMPHERE(jump);
+ return;
+ }
+
+ /* Skip low surrogate if necessary. */
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+
+ if (sljit_has_cpu_feature(SLJIT_HAS_CMOV) && !HAS_VIRTUAL_REGISTERS)
+ {
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, RETURN_ADDR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0x400);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ CMOV(SLJIT_LESS, STR_PTR, RETURN_ADDR, 0);
+ if (max >= 0xd800)
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, 0x10000);
+ }
+ else
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400);
+ if (options & READ_CHAR_UPDATE_STR_PTR)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ if (max >= 0xd800)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x10000);
+ JUMPHERE(jump);
+ }
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf)
+ {
+ if (backtracks != NULL)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800));
+ }
+ else
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ }
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+}
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+
+static BOOL is_char7_bitset(const sljit_u8 *bitset, BOOL nclass)
+{
+/* Tells whether the character codes below 128 are enough
+to determine a match. */
+const sljit_u8 value = nclass ? 0xff : 0;
+const sljit_u8 *end = bitset + 32;
+
+bitset += 16;
+do
+ {
+ if (*bitset++ != value)
+ return FALSE;
+ }
+while (bitset < end);
+return TRUE;
+}
+
+static void read_char7_type(compiler_common *common, jump_list **backtracks, BOOL negated)
+{
+/* Reads the precise character type of a character into TMP1, if the character
+is less than 128. Otherwise it returns with zero. Does not check STR_END. The
+full_read argument tells whether characters above max are accepted or not. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+SLJIT_ASSERT(common->utf);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+/* All values > 127 are zero in ctypes. */
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+
+if (negated)
+ {
+ jump = CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x80);
+
+ if (common->invalid_utf)
+ {
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+ }
+ JUMPHERE(jump);
+ }
+}
+
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */
+
+static void read_char8_type(compiler_common *common, jump_list **backtracks, BOOL negated)
+{
+/* Reads the character type into TMP1, updates STR_PTR. Does not check STR_END. */
+DEFINE_COMPILER;
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+struct sljit_jump *jump;
+#endif
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+struct sljit_jump *jump2;
+#endif
+
+SLJIT_UNUSED_ARG(backtracks);
+SLJIT_UNUSED_ARG(negated);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+ /* The result of this read may be unused, but saves an "else" part. */
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+ jump = CMP(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x80);
+
+ if (!negated)
+ {
+ if (common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2);
+ if (common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe0 - 0xc2));
+
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80);
+ if (common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40));
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+ JUMPHERE(jump2);
+ }
+ else if (common->invalid_utf)
+ {
+ add_jump(compiler, &common->utfreadchar_invalid, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP2, 0, TMP1, 0);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR));
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ jump2 = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+ JUMPHERE(jump2);
+ }
+ else
+ add_jump(compiler, &common->utfreadtype8, JUMP(SLJIT_FAST_CALL));
+
+ JUMPHERE(jump);
+ return;
+ }
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf && negated)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x110000));
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 32 */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+/* The ctypes array contains only 256 values. */
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+jump = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 255);
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+JUMPHERE(jump);
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf && negated)
+ {
+ /* Skip low surrogate if necessary. */
+ if (!common->invalid_utf)
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800);
+
+ if (sljit_has_cpu_feature(SLJIT_HAS_CMOV) && !HAS_VIRTUAL_REGISTERS)
+ {
+ OP2(SLJIT_ADD, RETURN_ADDR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0x400);
+ CMOV(SLJIT_LESS, STR_PTR, RETURN_ADDR, 0);
+ }
+ else
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPHERE(jump);
+ }
+ return;
+ }
+
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400));
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xdc00);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400));
+
+ JUMPHERE(jump);
+ return;
+ }
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16 */
+}
+
+static void move_back(compiler_common *common, jump_list **backtracks, BOOL must_be_valid)
+{
+/* Goes one character back. Affects STR_PTR and TMP1. If must_be_valid is TRUE,
+TMP2 is not used. Otherwise TMP2 must contain the start of the subject buffer,
+and it is destroyed. Does not modify STR_PTR for invalid character sequences. */
+DEFINE_COMPILER;
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_jump *jump;
+#endif
+
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+struct sljit_label *label;
+
+if (common->utf)
+ {
+ if (!must_be_valid && common->invalid_utf)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x80);
+ add_jump(compiler, &common->utfmoveback_invalid, JUMP(SLJIT_FAST_CALL));
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0));
+ JUMPHERE(jump);
+ return;
+ }
+
+ label = LABEL();
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, label);
+ return;
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utf)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ if (!must_be_valid && common->invalid_utf)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xe000 - 0xd800);
+ add_jump(compiler, &common->utfmoveback_invalid, JUMP(SLJIT_FAST_CALL));
+ if (backtracks != NULL)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0));
+ JUMPHERE(jump);
+ return;
+ }
+
+ /* Skip low surrogate if necessary. */
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0xdc00);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ return;
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+if (common->invalid_utf && !must_be_valid)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1));
+ if (backtracks != NULL)
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x110000));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ return;
+ }
+
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x110000);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_LESS);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ return;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+#endif /* SUPPORT_UNICODE */
+
+SLJIT_UNUSED_ARG(backtracks);
+SLJIT_UNUSED_ARG(must_be_valid);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+}
+
+static void check_newlinechar(compiler_common *common, int nltype, jump_list **backtracks, BOOL jumpifmatch)
+{
+/* Character comes in TMP1. Checks if it is a newline. TMP2 may be destroyed. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+if (nltype == NLTYPE_ANY)
+ {
+ add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(jumpifmatch ? SLJIT_NOT_ZERO : SLJIT_ZERO));
+ }
+else if (nltype == NLTYPE_ANYCRLF)
+ {
+ if (jumpifmatch)
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR));
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL));
+ }
+ else
+ {
+ jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL));
+ JUMPHERE(jump);
+ }
+ }
+else
+ {
+ SLJIT_ASSERT(nltype == NLTYPE_FIXED && common->newline < 256);
+ add_jump(compiler, backtracks, CMP(jumpifmatch ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline));
+ }
+}
+
+#ifdef SUPPORT_UNICODE
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+static void do_utfreadchar(compiler_common *common)
+{
+/* Fast decoding a UTF-8 character. TMP1 contains the first byte
+of the character (>= 0xc0). Return char value in TMP1. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* Searching for the first zero. */
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x800);
+jump = JUMP(SLJIT_NOT_ZERO);
+/* Two byte sequence. */
+OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3000);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x10000);
+jump = JUMP(SLJIT_NOT_ZERO);
+/* Three byte sequence. */
+OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0000);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Four byte sequence. */
+JUMPHERE(jump);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2));
+OP2(SLJIT_XOR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xf0000);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfreadtype8(compiler_common *common)
+{
+/* Fast decoding a UTF-8 character type. TMP2 contains the first byte
+of the character (>= 0xc0). Return value in TMP1. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_jump *compare;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, 0x20);
+jump = JUMP(SLJIT_NOT_ZERO);
+/* Two byte sequence. */
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x1f);
+/* The upper 5 bits are known at this point. */
+compare = CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, 0x3);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f);
+OP2(SLJIT_OR, TMP2, 0, TMP2, 0, TMP1, 0);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(compare);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* We only have types for characters less than 256. */
+JUMPHERE(jump);
+OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfreadchar_invalid(compiler_common *common)
+{
+/* Slow decoding a UTF-8 character. TMP1 contains the first byte
+of the character (>= 0xc0). Return char value in TMP1. STR_PTR is
+undefined for invalid characters. */
+DEFINE_COMPILER;
+sljit_s32 i;
+sljit_s32 has_cmov = sljit_has_cpu_feature(SLJIT_HAS_CMOV);
+struct sljit_jump *jump;
+struct sljit_jump *buffer_end_close;
+struct sljit_label *three_byte_entry;
+struct sljit_label *exit_invalid_label;
+struct sljit_jump *exit_invalid[11];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc2);
+
+/* Usually more than 3 characters remained in the subject buffer. */
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+
+/* Not a valid start of a multi-byte sequence, no more bytes read. */
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xf5 - 0xc2);
+
+buffer_end_close = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+/* If TMP2 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x800);
+jump = JUMP(SLJIT_NOT_ZERO);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump);
+
+/* Three-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, 0x20000);
+ exit_invalid[2] = NULL;
+ }
+else
+ exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x10000);
+jump = JUMP(SLJIT_NOT_ZERO);
+
+three_byte_entry = LABEL();
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2d800);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0xd800);
+ exit_invalid[3] = NULL;
+ }
+else
+ exit_invalid[3] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ exit_invalid[4] = NULL;
+ }
+else
+ exit_invalid[4] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump);
+
+/* Four-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, 0);
+ exit_invalid[5] = NULL;
+ }
+else
+ exit_invalid[5] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc10000);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0x10000);
+ exit_invalid[6] = NULL;
+ }
+else
+ exit_invalid[6] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(buffer_end_close);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+exit_invalid[7] = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0);
+
+/* Two-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+/* If TMP2 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+exit_invalid[8] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x800);
+jump = JUMP(SLJIT_NOT_ZERO);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Three-byte sequence. */
+JUMPHERE(jump);
+exit_invalid[9] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ exit_invalid[10] = NULL;
+ }
+else
+ exit_invalid[10] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+
+/* One will be substracted from STR_PTR later. */
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+
+/* Four byte sequences are not possible. */
+CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x30000, three_byte_entry);
+
+exit_invalid_label = LABEL();
+for (i = 0; i < 11; i++)
+ sljit_set_label(exit_invalid[i], exit_invalid_label);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfreadnewline_invalid(compiler_common *common)
+{
+/* Slow decoding a UTF-8 character, specialized for newlines.
+TMP1 contains the first byte of the character (>= 0xc0). Return
+char value in TMP1. */
+DEFINE_COMPILER;
+struct sljit_label *loop;
+struct sljit_label *skip_start;
+struct sljit_label *three_byte_exit;
+struct sljit_jump *jump[5];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+if (common->nltype != NLTYPE_ANY)
+ {
+ SLJIT_ASSERT(common->nltype != NLTYPE_FIXED || common->newline < 128);
+
+ /* All newlines are ascii, just skip intermediate octets. */
+ jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ loop = LABEL();
+ if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, TMP2, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ else
+ {
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0);
+ CMPTO(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, loop);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ JUMPHERE(jump[0]);
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+ OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+ return;
+ }
+
+jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+jump[1] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xc2);
+jump[2] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0xe2);
+
+skip_start = LABEL();
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0);
+jump[3] = CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80);
+
+/* Skip intermediate octets. */
+loop = LABEL();
+jump[4] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc0);
+CMPTO(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, loop);
+
+JUMPHERE(jump[3]);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+three_byte_exit = LABEL();
+JUMPHERE(jump[0]);
+JUMPHERE(jump[4]);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Two byte long newline: 0x85. */
+JUMPHERE(jump[1]);
+CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x85, skip_start);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x85);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Three byte long newlines: 0x2028 and 0x2029. */
+JUMPHERE(jump[2]);
+CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0x80, skip_start);
+CMPTO(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0, three_byte_exit);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP2, 0, SLJIT_IMM, 0x80);
+CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x40, skip_start);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0x2000);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfmoveback_invalid(compiler_common *common)
+{
+/* Goes one character back. */
+DEFINE_COMPILER;
+sljit_s32 i;
+struct sljit_jump *jump;
+struct sljit_jump *buffer_start_close;
+struct sljit_label *exit_ok_label;
+struct sljit_label *exit_invalid_label;
+struct sljit_jump *exit_invalid[7];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xc0);
+
+/* Two-byte sequence. */
+buffer_start_close = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x20);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Three-byte sequence. */
+JUMPHERE(jump);
+exit_invalid[1] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, -0x40);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0);
+jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x10);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Four-byte sequence. */
+JUMPHERE(jump);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0 - 0x80);
+exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x40);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xf0);
+exit_invalid[3] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x05);
+
+exit_ok_label = LABEL();
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+/* Two-byte sequence. */
+JUMPHERE(buffer_start_close);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+
+exit_invalid[4] = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x20, exit_ok_label);
+
+/* Three-byte sequence. */
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+exit_invalid[5] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, -0x40);
+exit_invalid[6] = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0);
+CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x10, exit_ok_label);
+
+/* Four-byte sequences are not possible. */
+
+exit_invalid_label = LABEL();
+sljit_set_label(exit_invalid[5], exit_invalid_label);
+sljit_set_label(exit_invalid[6], exit_invalid_label);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[4]);
+/* -2 + 4 = 2 */
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+
+exit_invalid_label = LABEL();
+for (i = 0; i < 4; i++)
+ sljit_set_label(exit_invalid[i], exit_invalid_label);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(4));
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfpeakcharback(compiler_common *common)
+{
+/* Peak a character back. Does not modify STR_PTR. */
+DEFINE_COMPILER;
+struct sljit_jump *jump[2];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0);
+jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x20);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0);
+jump[1] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x10);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-4));
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xe0 - 0x80);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf0);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+JUMPHERE(jump[1]);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+JUMPHERE(jump[0]);
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x80);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfpeakcharback_invalid(compiler_common *common)
+{
+/* Peak a character back. Does not modify STR_PTR. */
+DEFINE_COMPILER;
+sljit_s32 i;
+sljit_s32 has_cmov = sljit_has_cpu_feature(SLJIT_HAS_CMOV);
+struct sljit_jump *jump[2];
+struct sljit_label *two_byte_entry;
+struct sljit_label *three_byte_entry;
+struct sljit_label *exit_invalid_label;
+struct sljit_jump *exit_invalid[8];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(3));
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xc0);
+jump[0] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0);
+
+/* Two-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2);
+jump[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x1e);
+
+two_byte_entry = LABEL();
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+/* If TMP1 is in 0x80-0xbf range, TMP1 is also increased by (0x2 << 6). */
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump[1]);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2 - 0x80);
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* Three-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0);
+jump[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x10);
+
+three_byte_entry = LABEL();
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 12);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, -0xd800);
+ exit_invalid[2] = NULL;
+ }
+else
+ exit_invalid[2] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+ CMOV(SLJIT_LESS, TMP1, SLJIT_IMM, INVALID_UTF_CHAR);
+ exit_invalid[3] = NULL;
+ }
+else
+ exit_invalid[3] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x800);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump[1]);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0 - 0x80);
+exit_invalid[4] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 12);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* Four-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-4));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf0);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 18);
+/* ADD is used instead of OR because of the SUB 0x10000 above. */
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+
+if (has_cmov)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000);
+ CMOV(SLJIT_GREATER_EQUAL, TMP1, SLJIT_IMM, INVALID_UTF_CHAR - 0x10000);
+ exit_invalid[5] = NULL;
+ }
+else
+ exit_invalid[5] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x100000);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump[0]);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+jump[0] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0);
+
+/* Two-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2);
+CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x1e, two_byte_entry);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2 - 0x80);
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80);
+exit_invalid[6] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x40);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6);
+OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+/* Three-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-3));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xe0);
+CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x10, three_byte_entry);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump[0]);
+exit_invalid[7] = CMP(SLJIT_GREATER, TMP2, 0, STR_PTR, 0);
+
+/* Two-byte sequence. */
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xc2);
+CMPTO(SLJIT_LESS, TMP2, 0, SLJIT_IMM, 0x1e, two_byte_entry);
+
+exit_invalid_label = LABEL();
+for (i = 0; i < 8; i++)
+ sljit_set_label(exit_invalid[i], exit_invalid_label);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+
+#if PCRE2_CODE_UNIT_WIDTH == 16
+
+static void do_utfreadchar_invalid(compiler_common *common)
+{
+/* Slow decoding a UTF-16 character. TMP1 contains the first half
+of the character (>= 0xd800). Return char value in TMP1. STR_PTR is
+undefined for invalid characters. */
+DEFINE_COMPILER;
+struct sljit_jump *exit_invalid[3];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+/* TMP2 contains the high surrogate. */
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x10000);
+exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x400);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[0]);
+JUMPHERE(exit_invalid[1]);
+JUMPHERE(exit_invalid[2]);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfreadnewline_invalid(compiler_common *common)
+{
+/* Slow decoding a UTF-16 character, specialized for newlines.
+TMP1 contains the first half of the character (>= 0xd800). Return
+char value in TMP1. */
+
+DEFINE_COMPILER;
+struct sljit_jump *exit_invalid[2];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+/* TMP2 contains the high surrogate. */
+exit_invalid[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xdc00);
+
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xdc00);
+OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, SLJIT_IMM, 0x400);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0x10000);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[0]);
+JUMPHERE(exit_invalid[1]);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfmoveback_invalid(compiler_common *common)
+{
+/* Goes one character back. */
+DEFINE_COMPILER;
+struct sljit_jump *exit_invalid[3];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+exit_invalid[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x400);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0x400);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[0]);
+JUMPHERE(exit_invalid[1]);
+JUMPHERE(exit_invalid[2]);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_utfpeakcharback_invalid(compiler_common *common)
+{
+/* Peak a character back. Does not modify STR_PTR. */
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_jump *exit_invalid[3];
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xe000);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+exit_invalid[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xdc00);
+exit_invalid[1] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, STR_PTR, 0);
+
+OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000 - 0xdc00);
+OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xd800);
+exit_invalid[2] = CMP(SLJIT_GREATER_EQUAL, TMP2, 0, SLJIT_IMM, 0x400);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 10);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+
+JUMPHERE(jump);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(exit_invalid[0]);
+JUMPHERE(exit_invalid[1]);
+JUMPHERE(exit_invalid[2]);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+#endif /* PCRE2_CODE_UNIT_WIDTH == 16 */
+
+/* UCD_BLOCK_SIZE must be 128 (see the assert below). */
+#define UCD_BLOCK_MASK 127
+#define UCD_BLOCK_SHIFT 7
+
+static void do_getucd(compiler_common *common)
+{
+/* Search the UCD record for the character comes in TMP1.
+Returns chartype in TMP1 and UCD offset in TMP2. */
+DEFINE_COMPILER;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+struct sljit_jump *jump;
+#endif
+
+#if defined SLJIT_DEBUG && SLJIT_DEBUG
+/* dummy_ucd_record */
+const ucd_record *record = GET_UCD(UNASSIGNED_UTF_CHAR);
+SLJIT_ASSERT(record->script == ucp_Unknown && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther);
+SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0);
+#endif
+
+SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 12);
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif
+
+OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
+OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2));
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_getucdtype(compiler_common *common)
+{
+/* Search the UCD record for the character comes in TMP1.
+Returns chartype in TMP1 and UCD offset in TMP2. */
+DEFINE_COMPILER;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+struct sljit_jump *jump;
+#endif
+
+#if defined SLJIT_DEBUG && SLJIT_DEBUG
+/* dummy_ucd_record */
+const ucd_record *record = GET_UCD(UNASSIGNED_UTF_CHAR);
+SLJIT_ASSERT(record->script == ucp_Unknown && record->chartype == ucp_Cn && record->gbprop == ucp_gbOther);
+SLJIT_ASSERT(record->caseset == 0 && record->other_case == 0);
+#endif
+
+SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 12);
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif
+
+OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
+OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2));
+OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
+
+/* TMP2 is multiplied by 12. Same as (TMP2 << 2) + ((TMP2 << 2) << 1). */
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype));
+OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2);
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 1);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+#endif /* SUPPORT_UNICODE */
+
+static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_label *mainloop;
+struct sljit_label *newlinelabel = NULL;
+struct sljit_jump *start;
+struct sljit_jump *end = NULL;
+struct sljit_jump *end2 = NULL;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *loop;
+struct sljit_jump *jump;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+jump_list *newline = NULL;
+sljit_u32 overall_options = common->re->overall_options;
+BOOL hascrorlf = (common->re->flags & PCRE2_HASCRORLF) != 0;
+BOOL newlinecheck = FALSE;
+BOOL readuchar = FALSE;
+
+if (!(hascrorlf || (overall_options & PCRE2_FIRSTLINE) != 0)
+ && (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF || common->newline > 255))
+ newlinecheck = TRUE;
+
+SLJIT_ASSERT(common->abort_label == NULL);
+
+if ((overall_options & PCRE2_FIRSTLINE) != 0)
+ {
+ /* Search for the end of the first line. */
+ SLJIT_ASSERT(common->match_end_ptr != 0);
+ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ mainloop = LABEL();
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, mainloop);
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, mainloop);
+ JUMPHERE(end);
+ OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+ else
+ {
+ end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ mainloop = LABEL();
+ /* Continual stores does not cause data dependency. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0);
+ read_char(common, common->nlmin, common->nlmax, NULL, READ_CHAR_NEWLINE);
+ check_newlinechar(common, common->nltype, &newline, TRUE);
+ CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, mainloop);
+ JUMPHERE(end);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, STR_PTR, 0);
+ set_jumps(newline, LABEL());
+ }
+
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ }
+else if ((overall_options & PCRE2_USE_OFFSET_LIMIT) != 0)
+ {
+ /* Check whether offset limit is set and valid. */
+ SLJIT_ASSERT(common->match_end_ptr != 0);
+
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, offset_limit));
+ }
+ else
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, offset_limit));
+
+ OP1(SLJIT_MOV, TMP2, 0, STR_END, 0);
+ end = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw) PCRE2_UNSET);
+ if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif /* PCRE2_CODE_UNIT_WIDTH == [16|32] */
+ if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
+
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+ end2 = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0);
+ OP1(SLJIT_MOV, TMP2, 0, STR_END, 0);
+ JUMPHERE(end2);
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ add_jump(compiler, &common->abort, CMP(SLJIT_LESS, TMP2, 0, STR_PTR, 0));
+ JUMPHERE(end);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr, TMP2, 0);
+ }
+
+start = JUMP(SLJIT_JUMP);
+
+if (newlinecheck)
+ {
+ newlinelabel = LABEL();
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ end = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, common->newline & 0xff);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif /* PCRE2_CODE_UNIT_WIDTH == [16|32] */
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ end2 = JUMP(SLJIT_JUMP);
+ }
+
+mainloop = LABEL();
+
+/* Increasing the STR_PTR here requires one less jump in the most common case. */
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && !common->invalid_utf) readuchar = TRUE;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+if (newlinecheck) readuchar = TRUE;
+
+if (readuchar)
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+
+if (newlinecheck)
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, newlinelabel);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->invalid_utf)
+ {
+ /* Skip continuation code units. */
+ loop = LABEL();
+ jump = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x80);
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x40, loop);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPHERE(jump);
+ }
+else if (common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ JUMPHERE(jump);
+ }
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (common->invalid_utf)
+ {
+ /* Skip continuation code units. */
+ loop = LABEL();
+ jump = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xdc00);
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0x400, loop);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPHERE(jump);
+ }
+else if (common->utf)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xd800);
+
+ if (sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x400);
+ CMOV(SLJIT_LESS, STR_PTR, TMP2, 0);
+ }
+ else
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, SLJIT_IMM, 0x400);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_LESS);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ }
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+JUMPHERE(start);
+
+if (newlinecheck)
+ {
+ JUMPHERE(end);
+ JUMPHERE(end2);
+ }
+
+return mainloop;
+}
+
+
+static SLJIT_INLINE void add_prefix_char(PCRE2_UCHAR chr, fast_forward_char_data *chars, BOOL last)
+{
+sljit_u32 i, count = chars->count;
+
+if (count == 255)
+ return;
+
+if (count == 0)
+ {
+ chars->count = 1;
+ chars->chars[0] = chr;
+
+ if (last)
+ chars->last_count = 1;
+ return;
+ }
+
+for (i = 0; i < count; i++)
+ if (chars->chars[i] == chr)
+ return;
+
+if (count >= MAX_DIFF_CHARS)
+ {
+ chars->count = 255;
+ return;
+ }
+
+chars->chars[count] = chr;
+chars->count = count + 1;
+
+if (last)
+ chars->last_count++;
+}
+
+static int scan_prefix(compiler_common *common, PCRE2_SPTR cc, fast_forward_char_data *chars, int max_chars, sljit_u32 *rec_count)
+{
+/* Recursive function, which scans prefix literals. */
+BOOL last, any, class, caseless;
+int len, repeat, len_save, consumed = 0;
+sljit_u32 chr; /* Any unicode character. */
+sljit_u8 *bytes, *bytes_end, byte;
+PCRE2_SPTR alternative, cc_save, oc;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+PCRE2_UCHAR othercase[4];
+#elif defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 16
+PCRE2_UCHAR othercase[2];
+#else
+PCRE2_UCHAR othercase[1];
+#endif
+
+repeat = 1;
+while (TRUE)
+ {
+ if (*rec_count == 0)
+ return 0;
+ (*rec_count)--;
+
+ last = TRUE;
+ any = FALSE;
+ class = FALSE;
+ caseless = FALSE;
+
+ switch (*cc)
+ {
+ case OP_CHARI:
+ caseless = TRUE;
+ /* Fall through */
+ case OP_CHAR:
+ last = FALSE;
+ cc++;
+ break;
+
+ case OP_SOD:
+ case OP_SOM:
+ case OP_SET_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ /* Zero width assertions. */
+ cc++;
+ continue;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ cc = bracketend(cc);
+ continue;
+
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSPLUSI:
+ caseless = TRUE;
+ /* Fall through */
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSPLUS:
+ cc++;
+ break;
+
+ case OP_EXACTI:
+ caseless = TRUE;
+ /* Fall through */
+ case OP_EXACT:
+ repeat = GET2(cc, 1);
+ last = FALSE;
+ cc += 1 + IMM2_SIZE;
+ break;
+
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_POSQUERYI:
+ caseless = TRUE;
+ /* Fall through */
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_POSQUERY:
+ len = 1;
+ cc++;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc);
+#endif
+ max_chars = scan_prefix(common, cc + len, chars, max_chars, rec_count);
+ if (max_chars == 0)
+ return consumed;
+ last = FALSE;
+ break;
+
+ case OP_KET:
+ cc += 1 + LINK_SIZE;
+ continue;
+
+ case OP_ALT:
+ cc += GET(cc, 1);
+ continue;
+
+ case OP_ONCE:
+ case OP_BRA:
+ case OP_BRAPOS:
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ alternative = cc + GET(cc, 1);
+ while (*alternative == OP_ALT)
+ {
+ max_chars = scan_prefix(common, alternative + 1 + LINK_SIZE, chars, max_chars, rec_count);
+ if (max_chars == 0)
+ return consumed;
+ alternative += GET(alternative, 1);
+ }
+
+ if (*cc == OP_CBRA || *cc == OP_CBRAPOS)
+ cc += IMM2_SIZE;
+ cc += 1 + LINK_SIZE;
+ continue;
+
+ case OP_CLASS:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)(cc + 1), FALSE))
+ return consumed;
+#endif
+ class = TRUE;
+ break;
+
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ class = TRUE;
+ break;
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ any = TRUE;
+ cc += GET(cc, 1);
+ break;
+#endif
+
+ case OP_DIGIT:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_digit, FALSE))
+ return consumed;
+#endif
+ any = TRUE;
+ cc++;
+ break;
+
+ case OP_WHITESPACE:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_space, FALSE))
+ return consumed;
+#endif
+ any = TRUE;
+ cc++;
+ break;
+
+ case OP_WORDCHAR:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && !is_char7_bitset((const sljit_u8 *)common->ctypes - cbit_length + cbit_word, FALSE))
+ return consumed;
+#endif
+ any = TRUE;
+ cc++;
+ break;
+
+ case OP_NOT:
+ case OP_NOTI:
+ cc++;
+ /* Fall through. */
+ case OP_NOT_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ any = TRUE;
+ cc++;
+ break;
+
+#ifdef SUPPORT_UNICODE
+ case OP_NOTPROP:
+ case OP_PROP:
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ any = TRUE;
+ cc += 1 + 2;
+ break;
+#endif
+
+ case OP_TYPEEXACT:
+ repeat = GET2(cc, 1);
+ cc += 1 + IMM2_SIZE;
+ continue;
+
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf) return consumed;
+#endif
+ any = TRUE;
+ repeat = GET2(cc, 1);
+ cc += 1 + IMM2_SIZE + 1;
+ break;
+
+ default:
+ return consumed;
+ }
+
+ if (any)
+ {
+ do
+ {
+ chars->count = 255;
+
+ consumed++;
+ if (--max_chars == 0)
+ return consumed;
+ chars++;
+ }
+ while (--repeat > 0);
+
+ repeat = 1;
+ continue;
+ }
+
+ if (class)
+ {
+ bytes = (sljit_u8*) (cc + 1);
+ cc += 1 + 32 / sizeof(PCRE2_UCHAR);
+
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPOSSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ max_chars = scan_prefix(common, cc + 1, chars, max_chars, rec_count);
+ if (max_chars == 0)
+ return consumed;
+ break;
+
+ default:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ repeat = GET2(cc, 1);
+ if (repeat <= 0)
+ return consumed;
+ break;
+ }
+
+ do
+ {
+ if (bytes[31] & 0x80)
+ chars->count = 255;
+ else if (chars->count != 255)
+ {
+ bytes_end = bytes + 32;
+ chr = 0;
+ do
+ {
+ byte = *bytes++;
+ SLJIT_ASSERT((chr & 0x7) == 0);
+ if (byte == 0)
+ chr += 8;
+ else
+ {
+ do
+ {
+ if ((byte & 0x1) != 0)
+ add_prefix_char(chr, chars, TRUE);
+ byte >>= 1;
+ chr++;
+ }
+ while (byte != 0);
+ chr = (chr + 7) & ~7;
+ }
+ }
+ while (chars->count != 255 && bytes < bytes_end);
+ bytes = bytes_end - 32;
+ }
+
+ consumed++;
+ if (--max_chars == 0)
+ return consumed;
+ chars++;
+ }
+ while (--repeat > 0);
+
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPOSSTAR:
+ return consumed;
+
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSQUERY:
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ if (GET2(cc, 1) != GET2(cc, 1 + IMM2_SIZE))
+ return consumed;
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+ }
+
+ repeat = 1;
+ continue;
+ }
+
+ len = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(*cc)) len += GET_EXTRALEN(*cc);
+#endif
+
+ if (caseless && char_has_othercase(common, cc))
+ {
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ GETCHAR(chr, cc);
+ if ((int)PRIV(ord2utf)(char_othercase(common, chr), othercase) != len)
+ return consumed;
+ }
+ else
+#endif
+ {
+ chr = *cc;
+#ifdef SUPPORT_UNICODE
+ if (common->ucp && chr > 127)
+ othercase[0] = UCD_OTHERCASE(chr);
+ else
+#endif
+ othercase[0] = TABLE_GET(chr, common->fcc, chr);
+ }
+ }
+ else
+ {
+ caseless = FALSE;
+ othercase[0] = 0; /* Stops compiler warning - PH */
+ }
+
+ len_save = len;
+ cc_save = cc;
+ while (TRUE)
+ {
+ oc = othercase;
+ do
+ {
+ len--;
+ consumed++;
+
+ chr = *cc;
+ add_prefix_char(*cc, chars, len == 0);
+
+ if (caseless)
+ add_prefix_char(*oc, chars, len == 0);
+
+ if (--max_chars == 0)
+ return consumed;
+ chars++;
+ cc++;
+ oc++;
+ }
+ while (len > 0);
+
+ if (--repeat == 0)
+ break;
+
+ len = len_save;
+ cc = cc_save;
+ }
+
+ repeat = 1;
+ if (last)
+ return consumed;
+ }
+}
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+static void jumpto_if_not_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg, struct sljit_label *label)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0);
+CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0x80, label);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00);
+CMPTO(SLJIT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00, label);
+#else
+#error "Unknown code width"
+#endif
+}
+#endif
+
+#include "pcre2_jit_simd_inc.h"
+
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD
+
+static BOOL check_fast_forward_char_pair_simd(compiler_common *common, fast_forward_char_data *chars, int max)
+{
+ sljit_s32 i, j, max_i = 0, max_j = 0;
+ sljit_u32 max_pri = 0;
+ PCRE2_UCHAR a1, a2, a_pri, b1, b2, b_pri;
+
+ for (i = max - 1; i >= 1; i--)
+ {
+ if (chars[i].last_count > 2)
+ {
+ a1 = chars[i].chars[0];
+ a2 = chars[i].chars[1];
+ a_pri = chars[i].last_count;
+
+ j = i - max_fast_forward_char_pair_offset();
+ if (j < 0)
+ j = 0;
+
+ while (j < i)
+ {
+ b_pri = chars[j].last_count;
+ if (b_pri > 2 && (sljit_u32)a_pri + (sljit_u32)b_pri >= max_pri)
+ {
+ b1 = chars[j].chars[0];
+ b2 = chars[j].chars[1];
+
+ if (a1 != b1 && a1 != b2 && a2 != b1 && a2 != b2)
+ {
+ max_pri = a_pri + b_pri;
+ max_i = i;
+ max_j = j;
+ }
+ }
+ j++;
+ }
+ }
+ }
+
+if (max_pri == 0)
+ return FALSE;
+
+fast_forward_char_pair_simd(common, max_i, chars[max_i].chars[0], chars[max_i].chars[1], max_j, chars[max_j].chars[0], chars[max_j].chars[1]);
+return TRUE;
+}
+
+#endif /* JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD */
+
+static void fast_forward_first_char2(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
+{
+DEFINE_COMPILER;
+struct sljit_label *start;
+struct sljit_jump *match;
+struct sljit_jump *partial_quit;
+PCRE2_UCHAR mask;
+BOOL has_match_end = (common->match_end_ptr != 0);
+
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE || offset == 0);
+
+if (has_match_end)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+
+if (offset > 0)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
+
+if (has_match_end)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offset + 1));
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
+ }
+
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_SIMD
+
+if (JIT_HAS_FAST_FORWARD_CHAR_SIMD)
+ {
+ fast_forward_char_simd(common, char1, char2, offset);
+
+ if (offset > 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset));
+
+ if (has_match_end)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ return;
+ }
+
+#endif
+
+start = LABEL();
+
+partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (char1 == char2)
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1, start);
+else
+ {
+ mask = char1 ^ char2;
+ if (is_powerof2(mask))
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char1 | mask, start);
+ }
+ else
+ {
+ match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, char1);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, char2, start);
+ JUMPHERE(match);
+ }
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset > 0)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-(offset + 1)));
+ jumpto_if_not_utf_char_start(compiler, TMP1, start);
+ }
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offset + 1));
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial_quit);
+
+if (has_match_end)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+static SLJIT_INLINE BOOL fast_forward_first_n_chars(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_label *start;
+struct sljit_jump *match;
+fast_forward_char_data chars[MAX_N_CHARS];
+sljit_s32 offset;
+PCRE2_UCHAR mask;
+PCRE2_UCHAR *char_set, *char_set_end;
+int i, max, from;
+int range_right = -1, range_len;
+sljit_u8 *update_table = NULL;
+BOOL in_range;
+sljit_u32 rec_count;
+
+for (i = 0; i < MAX_N_CHARS; i++)
+ {
+ chars[i].count = 0;
+ chars[i].last_count = 0;
+ }
+
+rec_count = 10000;
+max = scan_prefix(common, common->start, chars, MAX_N_CHARS, &rec_count);
+
+if (max < 1)
+ return FALSE;
+
+/* Convert last_count to priority. */
+for (i = 0; i < max; i++)
+ {
+ SLJIT_ASSERT(chars[i].count > 0 && chars[i].last_count <= chars[i].count);
+
+ if (chars[i].count == 1)
+ {
+ chars[i].last_count = (chars[i].last_count == 1) ? 7 : 5;
+ /* Simplifies algorithms later. */
+ chars[i].chars[1] = chars[i].chars[0];
+ }
+ else if (chars[i].count == 2)
+ {
+ SLJIT_ASSERT(chars[i].chars[0] != chars[i].chars[1]);
+
+ if (is_powerof2(chars[i].chars[0] ^ chars[i].chars[1]))
+ chars[i].last_count = (chars[i].last_count == 2) ? 6 : 4;
+ else
+ chars[i].last_count = (chars[i].last_count == 2) ? 3 : 2;
+ }
+ else
+ chars[i].last_count = (chars[i].count == 255) ? 0 : 1;
+ }
+
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD
+if (JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD && check_fast_forward_char_pair_simd(common, chars, max))
+ return TRUE;
+#endif
+
+in_range = FALSE;
+/* Prevent compiler "uninitialized" warning */
+from = 0;
+range_len = 4 /* minimum length */ - 1;
+for (i = 0; i <= max; i++)
+ {
+ if (in_range && (i - from) > range_len && (chars[i - 1].count < 255))
+ {
+ range_len = i - from;
+ range_right = i - 1;
+ }
+
+ if (i < max && chars[i].count < 255)
+ {
+ SLJIT_ASSERT(chars[i].count > 0);
+ if (!in_range)
+ {
+ in_range = TRUE;
+ from = i;
+ }
+ }
+ else
+ in_range = FALSE;
+ }
+
+if (range_right >= 0)
+ {
+ update_table = (sljit_u8 *)allocate_read_only_data(common, 256);
+ if (update_table == NULL)
+ return TRUE;
+ memset(update_table, IN_UCHARS(range_len), 256);
+
+ for (i = 0; i < range_len; i++)
+ {
+ SLJIT_ASSERT(chars[range_right - i].count > 0 && chars[range_right - i].count < 255);
+
+ char_set = chars[range_right - i].chars;
+ char_set_end = char_set + chars[range_right - i].count;
+ do
+ {
+ if (update_table[(*char_set) & 0xff] > IN_UCHARS(i))
+ update_table[(*char_set) & 0xff] = IN_UCHARS(i);
+ char_set++;
+ }
+ while (char_set < char_set_end);
+ }
+ }
+
+offset = -1;
+/* Scan forward. */
+for (i = 0; i < max; i++)
+ {
+ if (range_right == i)
+ continue;
+
+ if (offset == -1)
+ {
+ if (chars[i].last_count >= 2)
+ offset = i;
+ }
+ else if (chars[offset].last_count < chars[i].last_count)
+ offset = i;
+ }
+
+SLJIT_ASSERT(offset == -1 || (chars[offset].count >= 1 && chars[offset].count <= 2));
+
+if (range_right < 0)
+ {
+ if (offset < 0)
+ return FALSE;
+ /* Works regardless the value is 1 or 2. */
+ fast_forward_first_char2(common, chars[offset].chars[0], chars[offset].chars[1], offset);
+ return TRUE;
+ }
+
+SLJIT_ASSERT(range_right != offset);
+
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_LESS, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
+ add_jump(compiler, &common->failed_match, JUMP(SLJIT_LESS));
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
+ }
+else
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_LESS, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
+ add_jump(compiler, &common->failed_match, JUMP(SLJIT_LESS));
+ }
+
+SLJIT_ASSERT(range_right >= 0);
+
+if (!HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, SLJIT_IMM, (sljit_sw)update_table);
+
+start = LABEL();
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0));
+
+#if PCRE2_CODE_UNIT_WIDTH == 8 || (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right));
+#else
+OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(range_right + 1) - 1);
+#endif
+
+if (!HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM2(RETURN_ADDR, TMP1), 0);
+else
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)update_table);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, start);
+
+if (offset >= 0)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(offset));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ if (chars[offset].count == 1)
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0], start);
+ else
+ {
+ mask = chars[offset].chars[0] ^ chars[offset].chars[1];
+ if (is_powerof2(mask))
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, mask);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0] | mask, start);
+ }
+ else
+ {
+ match = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[0]);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, chars[offset].chars[1], start);
+ JUMPHERE(match);
+ }
+ }
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset != 0)
+ {
+ if (offset < 0)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+ else
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+
+ jumpto_if_not_utf_char_start(compiler, TMP1, start);
+
+ if (offset < 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+#endif
+
+if (offset >= 0)
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+else
+ OP2(SLJIT_ADD, STR_END, 0, STR_END, 0, SLJIT_IMM, IN_UCHARS(max));
+return TRUE;
+}
+
+static SLJIT_INLINE void fast_forward_first_char(compiler_common *common)
+{
+PCRE2_UCHAR first_char = (PCRE2_UCHAR)(common->re->first_codeunit);
+PCRE2_UCHAR oc;
+
+oc = first_char;
+if ((common->re->flags & PCRE2_FIRSTCASELESS) != 0)
+ {
+ oc = TABLE_GET(first_char, common->fcc, first_char);
+#if defined SUPPORT_UNICODE
+ if (first_char > 127 && (common->utf || common->ucp))
+ oc = UCD_OTHERCASE(first_char);
+#endif
+ }
+
+fast_forward_first_char2(common, first_char, oc, 0);
+}
+
+static SLJIT_INLINE void fast_forward_newline(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+struct sljit_jump *lastchar = NULL;
+struct sljit_jump *firstchar;
+struct sljit_jump *quit = NULL;
+struct sljit_jump *foundcr = NULL;
+struct sljit_jump *notfoundnl;
+jump_list *newline = NULL;
+
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ }
+
+if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD
+ if (JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD && common->mode == PCRE2_JIT_COMPLETE)
+ {
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, STR_PTR, 0, TMP1, 0);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_NOT_EQUAL);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+ fast_forward_char_pair_simd(common, 1, common->newline & 0xff, common->newline & 0xff, 0, (common->newline >> 8) & 0xff, (common->newline >> 8) & 0xff);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ }
+ else
+#endif /* JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD */
+ {
+ lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, STR_PTR, 0, TMP1, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER_EQUAL);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+ loop = LABEL();
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, loop);
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, loop);
+
+ JUMPHERE(quit);
+ JUMPHERE(lastchar);
+ }
+
+ JUMPHERE(firstchar);
+
+ if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ return;
+ }
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ }
+else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+
+/* Example: match /^/ to \r\n from offset 1. */
+firstchar = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+
+if (common->nltype == NLTYPE_ANY)
+ move_back(common, NULL, FALSE);
+else
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+loop = LABEL();
+common->ff_newline_shortcut = loop;
+
+#ifdef JIT_HAS_FAST_FORWARD_CHAR_SIMD
+if (JIT_HAS_FAST_FORWARD_CHAR_SIMD && (common->nltype == NLTYPE_FIXED || common->nltype == NLTYPE_ANYCRLF))
+ {
+ if (common->nltype == NLTYPE_ANYCRLF)
+ {
+ fast_forward_char_simd(common, CHAR_CR, CHAR_LF, 0);
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ quit = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ }
+ else
+ {
+ fast_forward_char_simd(common, common->newline, common->newline, 0);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+ }
+ }
+else
+#endif /* JIT_HAS_FAST_FORWARD_CHAR_SIMD */
+ {
+ read_char(common, common->nlmin, common->nlmax, NULL, READ_CHAR_NEWLINE);
+ lastchar = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF)
+ foundcr = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ check_newlinechar(common, common->nltype, &newline, FALSE);
+ set_jumps(newline, loop);
+ }
+
+if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF)
+ {
+ if (quit == NULL)
+ {
+ quit = JUMP(SLJIT_JUMP);
+ JUMPHERE(foundcr);
+ }
+
+ notfoundnl = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
+#if PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, UCHAR_SHIFT);
+#endif
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ JUMPHERE(notfoundnl);
+ JUMPHERE(quit);
+ }
+
+if (lastchar)
+ JUMPHERE(lastchar);
+JUMPHERE(firstchar);
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks);
+
+static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common)
+{
+DEFINE_COMPILER;
+const sljit_u8 *start_bits = common->re->start_bitmap;
+struct sljit_label *start;
+struct sljit_jump *partial_quit;
+#if PCRE2_CODE_UNIT_WIDTH != 8
+struct sljit_jump *found = NULL;
+#endif
+jump_list *matches = NULL;
+
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, STR_END, 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_END, 0, TMP1, 0);
+ CMOV(SLJIT_GREATER, STR_END, TMP1, 0);
+ }
+
+start = LABEL();
+
+partial_quit = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit);
+
+OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (!optimize_class(common, start_bits, (start_bits[31] & 0x80) != 0, FALSE, &matches))
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if ((start_bits[31] & 0x80) != 0)
+ found = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255);
+ else
+ CMPTO(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 255, start);
+#elif defined SUPPORT_UNICODE
+ if (common->utf && is_char7_bitset(start_bits, FALSE))
+ CMPTO(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 127, start);
+#endif
+ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)start_bits);
+ if (!HAS_VIRTUAL_REGISTERS)
+ {
+ OP2(SLJIT_SHL, TMP3, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP3, 0);
+ }
+ else
+ {
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP2, 0);
+ }
+ JUMPTO(SLJIT_ZERO, start);
+ }
+else
+ set_jumps(matches, start);
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+if (found != NULL)
+ JUMPHERE(found);
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial_quit);
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, RETURN_ADDR, 0);
+}
+
+static SLJIT_INLINE jump_list *search_requested_char(compiler_common *common, PCRE2_UCHAR req_char, BOOL caseless, BOOL has_firstchar)
+{
+DEFINE_COMPILER;
+struct sljit_label *loop;
+struct sljit_jump *toolong;
+struct sljit_jump *already_found;
+struct sljit_jump *found;
+struct sljit_jump *found_oc = NULL;
+jump_list *not_found = NULL;
+sljit_u32 oc, bit;
+
+SLJIT_ASSERT(common->req_char_ptr != 0);
+OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(REQ_CU_MAX) * 100);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr);
+toolong = CMP(SLJIT_LESS, TMP2, 0, STR_END, 0);
+already_found = CMP(SLJIT_LESS, STR_PTR, 0, TMP1, 0);
+
+if (has_firstchar)
+ OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+else
+ OP1(SLJIT_MOV, TMP1, 0, STR_PTR, 0);
+
+oc = req_char;
+if (caseless)
+ {
+ oc = TABLE_GET(req_char, common->fcc, req_char);
+#if defined SUPPORT_UNICODE
+ if (req_char > 127 && (common->utf || common->ucp))
+ oc = UCD_OTHERCASE(req_char);
+#endif
+ }
+
+#ifdef JIT_HAS_FAST_REQUESTED_CHAR_SIMD
+if (JIT_HAS_FAST_REQUESTED_CHAR_SIMD)
+ {
+ not_found = fast_requested_char_simd(common, req_char, oc);
+ }
+else
+#endif
+ {
+ loop = LABEL();
+ add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(TMP1), 0);
+
+ if (req_char == oc)
+ found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char);
+ else
+ {
+ bit = req_char ^ oc;
+ if (is_powerof2(bit))
+ {
+ OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, bit);
+ found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char | bit);
+ }
+ else
+ {
+ found = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, req_char);
+ found_oc = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, oc);
+ }
+ }
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_JUMP, loop);
+
+ JUMPHERE(found);
+ if (found_oc)
+ JUMPHERE(found_oc);
+ }
+
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr, TMP1, 0);
+
+JUMPHERE(already_found);
+JUMPHERE(toolong);
+return not_found;
+}
+
+static void do_revertframes(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_label *mainloop;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+GET_LOCAL_BASE(TMP1, 0, 0);
+
+/* Drop frames until we reach STACK_TOP. */
+mainloop = LABEL();
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), -SSIZE_OF(sw));
+jump = CMP(SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw)));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), SLJIT_MEM1(STACK_TOP), -(3 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * SSIZE_OF(sw));
+ }
+else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw)));
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(3 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 3 * SSIZE_OF(sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP1, 0);
+ GET_LOCAL_BASE(TMP1, 0, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP3, 0);
+ }
+JUMPTO(SLJIT_JUMP, mainloop);
+
+JUMPHERE(jump);
+jump = CMP(SLJIT_NOT_ZERO /* SIG_LESS */, TMP2, 0, SLJIT_IMM, 0);
+/* End of reverting values. */
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+
+JUMPHERE(jump);
+OP2(SLJIT_SUB, TMP2, 0, SLJIT_IMM, 0, TMP2, 0);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * SSIZE_OF(sw));
+ }
+else
+ {
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(STACK_TOP), -(2 * SSIZE_OF(sw)));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, 2 * SSIZE_OF(sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, TMP3, 0);
+ }
+JUMPTO(SLJIT_JUMP, mainloop);
+}
+
+static void check_wordboundary(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *skipread;
+jump_list *skipread_list = NULL;
+#ifdef SUPPORT_UNICODE
+struct sljit_label *valid_utf;
+jump_list *invalid_utf1 = NULL;
+#endif /* SUPPORT_UNICODE */
+jump_list *invalid_utf2 = NULL;
+#if PCRE2_CODE_UNIT_WIDTH != 8 || defined SUPPORT_UNICODE
+struct sljit_jump *jump;
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 || SUPPORT_UNICODE */
+
+SLJIT_COMPILE_ASSERT(ctype_word == 0x10, ctype_word_must_be_16);
+
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+/* Get type of the previous char, and put it to TMP3. */
+OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0);
+skipread = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+
+#ifdef SUPPORT_UNICODE
+if (common->invalid_utf)
+ {
+ peek_char_back(common, READ_CHAR_MAX, &invalid_utf1);
+
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+ OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+ move_back(common, NULL, TRUE);
+ check_start_used_ptr(common);
+ OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP2, 0);
+ }
+ }
+else
+#endif /* SUPPORT_UNICODE */
+ {
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ peek_char_back(common, READ_CHAR_MAX, NULL);
+ else
+ {
+ move_back(common, NULL, TRUE);
+ check_start_used_ptr(common);
+ read_char(common, 0, READ_CHAR_MAX, NULL, READ_CHAR_UPDATE_STR_PTR);
+ }
+ }
+
+/* Testing char type. */
+#ifdef SUPPORT_UNICODE
+if (common->ucp)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1);
+ jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE);
+ add_jump(compiler, &common->getucdtype, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP3, 0, TMP2, 0);
+ }
+else
+#endif /* SUPPORT_UNICODE */
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+#elif defined SUPPORT_UNICODE
+ /* Here TMP3 has already been zeroed. */
+ jump = NULL;
+ if (common->utf)
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), common->ctypes);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 4 /* ctype_word */);
+ OP2(SLJIT_AND, TMP3, 0, TMP1, 0, SLJIT_IMM, 1);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ JUMPHERE(jump);
+#elif defined SUPPORT_UNICODE
+ if (jump != NULL)
+ JUMPHERE(jump);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ }
+JUMPHERE(skipread);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+check_str_end(common, &skipread_list);
+peek_char(common, READ_CHAR_MAX, SLJIT_MEM1(SLJIT_SP), LOCALS1, &invalid_utf2);
+
+/* Testing char type. This is a code duplication. */
+#ifdef SUPPORT_UNICODE
+
+valid_utf = LABEL();
+
+if (common->ucp)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1);
+ jump = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE);
+ add_jump(compiler, &common->getucdtype, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ JUMPHERE(jump);
+ }
+else
+#endif /* SUPPORT_UNICODE */
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ /* TMP2 may be destroyed by peek_char. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+#elif defined SUPPORT_UNICODE
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+ jump = NULL;
+ if (common->utf)
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+#endif
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP1), common->ctypes);
+ OP2(SLJIT_LSHR, TMP2, 0, TMP2, 0, SLJIT_IMM, 4 /* ctype_word */);
+ OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ JUMPHERE(jump);
+#elif defined SUPPORT_UNICODE
+ if (jump != NULL)
+ JUMPHERE(jump);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ }
+set_jumps(skipread_list, LABEL());
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP2(SLJIT_XOR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, TMP3, 0);
+OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+
+#ifdef SUPPORT_UNICODE
+if (common->invalid_utf)
+ {
+ set_jumps(invalid_utf1, LABEL());
+
+ peek_char(common, READ_CHAR_MAX, SLJIT_MEM1(SLJIT_SP), LOCALS1, NULL);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, INVALID_UTF_CHAR, valid_utf);
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, -1);
+ OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+
+ set_jumps(invalid_utf2, LABEL());
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP1(SLJIT_MOV, TMP2, 0, TMP3, 0);
+ OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+ }
+#endif /* SUPPORT_UNICODE */
+}
+
+static BOOL optimize_class_ranges(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+{
+/* May destroy TMP1. */
+DEFINE_COMPILER;
+int ranges[MAX_CLASS_RANGE_SIZE];
+sljit_u8 bit, cbit, all;
+int i, byte, length = 0;
+
+bit = bits[0] & 0x1;
+/* All bits will be zero or one (since bit is zero or one). */
+all = -bit;
+
+for (i = 0; i < 256; )
+ {
+ byte = i >> 3;
+ if ((i & 0x7) == 0 && bits[byte] == all)
+ i += 8;
+ else
+ {
+ cbit = (bits[byte] >> (i & 0x7)) & 0x1;
+ if (cbit != bit)
+ {
+ if (length >= MAX_CLASS_RANGE_SIZE)
+ return FALSE;
+ ranges[length] = i;
+ length++;
+ bit = cbit;
+ all = -cbit;
+ }
+ i++;
+ }
+ }
+
+if (((bit == 0) && nclass) || ((bit == 1) && !nclass))
+ {
+ if (length >= MAX_CLASS_RANGE_SIZE)
+ return FALSE;
+ ranges[length] = 256;
+ length++;
+ }
+
+if (length < 0 || length > 4)
+ return FALSE;
+
+bit = bits[0] & 0x1;
+if (invert) bit ^= 0x1;
+
+/* No character is accepted. */
+if (length == 0 && bit == 0)
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+
+switch(length)
+ {
+ case 0:
+ /* When bit != 0, all characters are accepted. */
+ return TRUE;
+
+ case 1:
+ add_jump(compiler, backtracks, CMP(bit == 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0]));
+ return TRUE;
+
+ case 2:
+ if (ranges[0] + 1 != ranges[1])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]);
+ add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0]));
+ return TRUE;
+
+ case 3:
+ if (bit != 0)
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2]));
+ if (ranges[0] + 1 != ranges[1])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0]));
+ return TRUE;
+ }
+
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[0]));
+ if (ranges[1] + 1 != ranges[2])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[1]);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[1]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1]));
+ return TRUE;
+
+ case 4:
+ if ((ranges[1] - ranges[0]) == (ranges[3] - ranges[2])
+ && (ranges[0] | (ranges[2] - ranges[0])) == ranges[2]
+ && (ranges[1] & (ranges[2] - ranges[0])) == 0
+ && is_powerof2(ranges[2] - ranges[0]))
+ {
+ SLJIT_ASSERT((ranges[0] & (ranges[2] - ranges[0])) == 0 && (ranges[2] & ranges[3] & (ranges[2] - ranges[0])) != 0);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[0]);
+ if (ranges[2] + 1 != ranges[3])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2]);
+ add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_LESS : SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[2]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(bit != 0 ? SLJIT_EQUAL : SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2]));
+ return TRUE;
+ }
+
+ if (bit != 0)
+ {
+ i = 0;
+ if (ranges[0] + 1 != ranges[1])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]));
+ i = ranges[0];
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[0]));
+
+ if (ranges[2] + 1 != ranges[3])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[2] - i);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[2]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[2] - i));
+ return TRUE;
+ }
+
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[0]);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, ranges[3] - ranges[0]));
+ if (ranges[1] + 1 != ranges[2])
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]);
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, ranges[2] - ranges[1]));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, ranges[1] - ranges[0]));
+ return TRUE;
+
+ default:
+ SLJIT_UNREACHABLE();
+ return FALSE;
+ }
+}
+
+static BOOL optimize_class_chars(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+{
+/* May destroy TMP1. */
+DEFINE_COMPILER;
+uint16_t char_list[MAX_CLASS_CHARS_SIZE];
+uint8_t byte;
+sljit_s32 type;
+int i, j, k, len, c;
+
+if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ return FALSE;
+
+len = 0;
+
+for (i = 0; i < 32; i++)
+ {
+ byte = bits[i];
+
+ if (nclass)
+ byte = ~byte;
+
+ j = 0;
+ while (byte != 0)
+ {
+ if (byte & 0x1)
+ {
+ c = i * 8 + j;
+
+ k = len;
+
+ if ((c & 0x20) != 0)
+ {
+ for (k = 0; k < len; k++)
+ if (char_list[k] == c - 0x20)
+ {
+ char_list[k] |= 0x120;
+ break;
+ }
+ }
+
+ if (k == len)
+ {
+ if (len >= MAX_CLASS_CHARS_SIZE)
+ return FALSE;
+
+ char_list[len++] = (uint16_t) c;
+ }
+ }
+
+ byte >>= 1;
+ j++;
+ }
+ }
+
+if (len == 0) return FALSE; /* Should never occur, but stops analyzers complaining. */
+
+i = 0;
+j = 0;
+
+if (char_list[0] == 0)
+ {
+ i++;
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_ZERO);
+ }
+else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0);
+
+while (i < len)
+ {
+ if ((char_list[i] & 0x100) != 0)
+ j++;
+ else
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, char_list[i]);
+ CMOV(SLJIT_ZERO, TMP2, TMP1, 0);
+ }
+ i++;
+ }
+
+if (j != 0)
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x20);
+
+ for (i = 0; i < len; i++)
+ if ((char_list[i] & 0x100) != 0)
+ {
+ j--;
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, char_list[i] & 0xff);
+ CMOV(SLJIT_ZERO, TMP2, TMP1, 0);
+ }
+ }
+
+if (invert)
+ nclass = !nclass;
+
+type = nclass ? SLJIT_NOT_EQUAL : SLJIT_EQUAL;
+add_jump(compiler, backtracks, CMP(type, TMP2, 0, SLJIT_IMM, 0));
+return TRUE;
+}
+
+static BOOL optimize_class(compiler_common *common, const sljit_u8 *bits, BOOL nclass, BOOL invert, jump_list **backtracks)
+{
+/* May destroy TMP1. */
+if (optimize_class_ranges(common, bits, nclass, invert, backtracks))
+ return TRUE;
+return optimize_class_chars(common, bits, nclass, invert, backtracks);
+}
+
+static void check_anynewline(compiler_common *common)
+{
+/* Check whether TMP1 contains a newline character. TMP2 destroyed. */
+DEFINE_COMPILER;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a);
+OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+#endif
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ }
+#endif
+#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void check_hspace(compiler_common *common)
+{
+/* Check whether TMP1 contains a newline character. TMP2 destroyed. */
+DEFINE_COMPILER;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x09);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x20);
+OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0xa0);
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+#endif
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x1680);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x180e);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ }
+#endif
+#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void check_vspace(compiler_common *common)
+{
+/* Check whether TMP1 contains a newline character. TMP2 destroyed. */
+DEFINE_COMPILER;
+
+sljit_emit_fast_enter(compiler, RETURN_ADDR, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a);
+OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a);
+OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a);
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utf)
+ {
+#endif
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ }
+#endif
+#endif /* SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == [16|32] */
+OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+
+OP_SRC(SLJIT_FAST_RETURN, RETURN_ADDR, 0);
+}
+
+static void do_casefulcmp(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_label *label;
+int char1_reg;
+int char2_reg;
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ char1_reg = STR_END;
+ char2_reg = STACK_TOP;
+ }
+else
+ {
+ char1_reg = TMP3;
+ char2_reg = RETURN_ADDR;
+ }
+
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+if (char1_reg == STR_END)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, char1_reg, 0);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, char2_reg, 0);
+ }
+
+if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ {
+ label = LABEL();
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ }
+else if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ label = LABEL();
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+else
+ {
+ label = LABEL();
+ OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0);
+ OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+ JUMPTO(SLJIT_NOT_ZERO, label);
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ }
+
+if (char1_reg == STR_END)
+ {
+ OP1(SLJIT_MOV, char1_reg, 0, TMP3, 0);
+ OP1(SLJIT_MOV, char2_reg, 0, RETURN_ADDR, 0);
+ }
+
+OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+}
+
+static void do_caselesscmp(compiler_common *common)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+struct sljit_label *label;
+int char1_reg = STR_END;
+int char2_reg;
+int lcc_table;
+int opt_type = 0;
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ char2_reg = STACK_TOP;
+ lcc_table = STACK_LIMIT;
+ }
+else
+ {
+ char2_reg = RETURN_ADDR;
+ lcc_table = TMP3;
+ }
+
+if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ opt_type = 1;
+else if (sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_SUPP | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1)) == SLJIT_SUCCESS)
+ opt_type = 2;
+
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, char1_reg, 0);
+
+if (char2_reg == STACK_TOP)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, char2_reg, 0);
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, lcc_table, 0);
+ }
+
+OP1(SLJIT_MOV, lcc_table, 0, SLJIT_IMM, common->lcc);
+
+if (opt_type == 1)
+ {
+ label = LABEL();
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_POST, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ }
+else if (opt_type == 2)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ label = LABEL();
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char1_reg, SLJIT_MEM1(TMP1), IN_UCHARS(1));
+ sljit_emit_mem_update(compiler, MOV_UCHAR | SLJIT_MEM_PRE, char2_reg, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ }
+else
+ {
+ label = LABEL();
+ OP1(MOV_UCHAR, char1_reg, 0, SLJIT_MEM1(TMP1), 0);
+ OP1(MOV_UCHAR, char2_reg, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1));
+ }
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+jump = CMP(SLJIT_GREATER, char1_reg, 0, SLJIT_IMM, 255);
+#endif
+OP1(SLJIT_MOV_U8, char1_reg, 0, SLJIT_MEM2(lcc_table, char1_reg), 0);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+JUMPHERE(jump);
+jump = CMP(SLJIT_GREATER, char2_reg, 0, SLJIT_IMM, 255);
+#endif
+OP1(SLJIT_MOV_U8, char2_reg, 0, SLJIT_MEM2(lcc_table, char2_reg), 0);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+JUMPHERE(jump);
+#endif
+
+if (opt_type == 0)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+jump = CMP(SLJIT_NOT_EQUAL, char1_reg, 0, char2_reg, 0);
+OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1));
+JUMPTO(SLJIT_NOT_ZERO, label);
+
+JUMPHERE(jump);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+
+if (opt_type == 2)
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+if (char2_reg == STACK_TOP)
+ {
+ OP1(SLJIT_MOV, char2_reg, 0, TMP3, 0);
+ OP1(SLJIT_MOV, lcc_table, 0, RETURN_ADDR, 0);
+ }
+
+OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+}
+
+static PCRE2_SPTR byte_sequence_compare(compiler_common *common, BOOL caseless, PCRE2_SPTR cc,
+ compare_context *context, jump_list **backtracks)
+{
+DEFINE_COMPILER;
+unsigned int othercasebit = 0;
+PCRE2_SPTR othercasechar = NULL;
+#ifdef SUPPORT_UNICODE
+int utflength;
+#endif
+
+if (caseless && char_has_othercase(common, cc))
+ {
+ othercasebit = char_get_othercase_bit(common, cc);
+ SLJIT_ASSERT(othercasebit);
+ /* Extracting bit difference info. */
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ othercasechar = cc + (othercasebit >> 8);
+ othercasebit &= 0xff;
+#elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ /* Note that this code only handles characters in the BMP. If there
+ ever are characters outside the BMP whose othercase differs in only one
+ bit from itself (there currently are none), this code will need to be
+ revised for PCRE2_CODE_UNIT_WIDTH == 32. */
+ othercasechar = cc + (othercasebit >> 9);
+ if ((othercasebit & 0x100) != 0)
+ othercasebit = (othercasebit & 0xff) << 8;
+ else
+ othercasebit &= 0xff;
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+ }
+
+if (context->sourcereg == -1)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ if (context->length >= 4)
+ OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ else if (context->length >= 2)
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ else
+#endif
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ if (context->length >= 4)
+ OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ else
+#endif
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16|32] */
+ context->sourcereg = TMP2;
+ }
+
+#ifdef SUPPORT_UNICODE
+utflength = 1;
+if (common->utf && HAS_EXTRALEN(*cc))
+ utflength += GET_EXTRALEN(*cc);
+
+do
+ {
+#endif
+
+ context->length -= IN_UCHARS(1);
+#if (defined SLJIT_UNALIGNED && SLJIT_UNALIGNED) && (PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16)
+
+ /* Unaligned read is supported. */
+ if (othercasebit != 0 && othercasechar == cc)
+ {
+ context->c.asuchars[context->ucharptr] = *cc | othercasebit;
+ context->oc.asuchars[context->ucharptr] = othercasebit;
+ }
+ else
+ {
+ context->c.asuchars[context->ucharptr] = *cc;
+ context->oc.asuchars[context->ucharptr] = 0;
+ }
+ context->ucharptr++;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (context->ucharptr >= 4 || context->length == 0 || (context->ucharptr == 2 && context->length == 1))
+#else
+ if (context->ucharptr >= 2 || context->length == 0)
+#endif
+ {
+ if (context->length >= 4)
+ OP1(SLJIT_MOV_S32, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+ else if (context->length >= 2)
+ OP1(SLJIT_MOV_U16, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ else if (context->length >= 1)
+ OP1(SLJIT_MOV_U8, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1;
+
+ switch(context->ucharptr)
+ {
+ case 4 / sizeof(PCRE2_UCHAR):
+ if (context->oc.asint != 0)
+ OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asint);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asint | context->oc.asint));
+ break;
+
+ case 2 / sizeof(PCRE2_UCHAR):
+ if (context->oc.asushort != 0)
+ OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asushort);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asushort | context->oc.asushort));
+ break;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ case 1:
+ if (context->oc.asbyte != 0)
+ OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asbyte);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asbyte | context->oc.asbyte));
+ break;
+#endif
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ context->ucharptr = 0;
+ }
+
+#else
+
+ /* Unaligned read is unsupported or in 32 bit mode. */
+ if (context->length >= 1)
+ OP1(MOV_UCHAR, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length);
+
+ context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1;
+
+ if (othercasebit != 0 && othercasechar == cc)
+ {
+ OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, othercasebit);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc | othercasebit));
+ }
+ else
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc));
+
+#endif
+
+ cc++;
+#ifdef SUPPORT_UNICODE
+ utflength--;
+ }
+while (utflength > 0);
+#endif
+
+return cc;
+}
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+
+#define SET_TYPE_OFFSET(value) \
+ if ((value) != typeoffset) \
+ { \
+ if ((value) < typeoffset) \
+ OP2(SLJIT_ADD, typereg, 0, typereg, 0, SLJIT_IMM, typeoffset - (value)); \
+ else \
+ OP2(SLJIT_SUB, typereg, 0, typereg, 0, SLJIT_IMM, (value) - typeoffset); \
+ } \
+ typeoffset = (value);
+
+#define SET_CHAR_OFFSET(value) \
+ if ((value) != charoffset) \
+ { \
+ if ((value) < charoffset) \
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)(charoffset - (value))); \
+ else \
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)((value) - charoffset)); \
+ } \
+ charoffset = (value);
+
+static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr);
+
+#ifdef SUPPORT_UNICODE
+#define XCLASS_SAVE_CHAR 0x001
+#define XCLASS_CHAR_SAVED 0x002
+#define XCLASS_HAS_TYPE 0x004
+#define XCLASS_HAS_SCRIPT 0x008
+#define XCLASS_HAS_SCRIPT_EXTENSION 0x010
+#define XCLASS_HAS_BOOL 0x020
+#define XCLASS_HAS_BIDICL 0x040
+#define XCLASS_NEEDS_UCD (XCLASS_HAS_TYPE | XCLASS_HAS_SCRIPT | XCLASS_HAS_SCRIPT_EXTENSION | XCLASS_HAS_BOOL | XCLASS_HAS_BIDICL)
+#define XCLASS_SCRIPT_EXTENSION_NOTPROP 0x080
+#define XCLASS_SCRIPT_EXTENSION_RESTORE_RETURN_ADDR 0x100
+#define XCLASS_SCRIPT_EXTENSION_RESTORE_LOCALS0 0x200
+
+#endif /* SUPPORT_UNICODE */
+
+static void compile_xclass_matchingpath(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks)
+{
+DEFINE_COMPILER;
+jump_list *found = NULL;
+jump_list **list = (cc[0] & XCL_NOT) == 0 ? &found : backtracks;
+sljit_uw c, charoffset, max = 256, min = READ_CHAR_MAX;
+struct sljit_jump *jump = NULL;
+PCRE2_SPTR ccbegin;
+int compares, invertcmp, numberofcmps;
+#if defined SUPPORT_UNICODE && (PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16)
+BOOL utf = common->utf;
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == [8|16] */
+
+#ifdef SUPPORT_UNICODE
+sljit_u32 unicode_status = 0;
+int typereg = TMP1;
+const sljit_u32 *other_cases;
+sljit_uw typeoffset;
+#endif /* SUPPORT_UNICODE */
+
+/* Scanning the necessary info. */
+cc++;
+ccbegin = cc;
+compares = 0;
+
+if (cc[-1] & XCL_MAP)
+ {
+ min = 0;
+ cc += 32 / sizeof(PCRE2_UCHAR);
+ }
+
+while (*cc != XCL_END)
+ {
+ compares++;
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ if (c > max) max = c;
+ if (c < min) min = c;
+#ifdef SUPPORT_UNICODE
+ unicode_status |= XCLASS_SAVE_CHAR;
+#endif /* SUPPORT_UNICODE */
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ if (c < min) min = c;
+ GETCHARINCTEST(c, cc);
+ if (c > max) max = c;
+#ifdef SUPPORT_UNICODE
+ unicode_status |= XCLASS_SAVE_CHAR;
+#endif /* SUPPORT_UNICODE */
+ }
+#ifdef SUPPORT_UNICODE
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_CLIST && cc[-1] == XCL_PROP)
+ {
+ other_cases = PRIV(ucd_caseless_sets) + cc[1];
+ while (*other_cases != NOTACHAR)
+ {
+ if (*other_cases > max) max = *other_cases;
+ if (*other_cases < min) min = *other_cases;
+ other_cases++;
+ }
+ }
+ else
+ {
+ max = READ_CHAR_MAX;
+ min = 0;
+ }
+
+ switch(*cc)
+ {
+ case PT_ANY:
+ /* Any either accepts everything or ignored. */
+ if (cc[-1] == XCL_PROP)
+ {
+ compile_char1_matchingpath(common, OP_ALLANY, cc, backtracks, FALSE);
+ if (list == backtracks)
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ return;
+ }
+ break;
+
+ case PT_LAMP:
+ case PT_GC:
+ case PT_PC:
+ case PT_ALNUM:
+ unicode_status |= XCLASS_HAS_TYPE;
+ break;
+
+ case PT_SCX:
+ unicode_status |= XCLASS_HAS_SCRIPT_EXTENSION;
+ if (cc[-1] == XCL_NOTPROP)
+ {
+ unicode_status |= XCLASS_SCRIPT_EXTENSION_NOTPROP;
+ break;
+ }
+ compares++;
+ /* Fall through */
+
+ case PT_SC:
+ unicode_status |= XCLASS_HAS_SCRIPT;
+ break;
+
+ case PT_SPACE:
+ case PT_PXSPACE:
+ case PT_WORD:
+ case PT_PXGRAPH:
+ case PT_PXPRINT:
+ case PT_PXPUNCT:
+ unicode_status |= XCLASS_SAVE_CHAR | XCLASS_HAS_TYPE;
+ break;
+
+ case PT_CLIST:
+ case PT_UCNC:
+ unicode_status |= XCLASS_SAVE_CHAR;
+ break;
+
+ case PT_BOOL:
+ unicode_status |= XCLASS_HAS_BOOL;
+ break;
+
+ case PT_BIDICL:
+ unicode_status |= XCLASS_HAS_BIDICL;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ cc += 2;
+ }
+#endif /* SUPPORT_UNICODE */
+ }
+SLJIT_ASSERT(compares > 0);
+
+/* We are not necessary in utf mode even in 8 bit mode. */
+cc = ccbegin;
+if ((cc[-1] & XCL_NOT) != 0)
+ read_char(common, min, max, backtracks, READ_CHAR_UPDATE_STR_PTR);
+else
+ {
+#ifdef SUPPORT_UNICODE
+ read_char(common, min, max, (unicode_status & XCLASS_NEEDS_UCD) ? backtracks : NULL, 0);
+#else /* !SUPPORT_UNICODE */
+ read_char(common, min, max, NULL, 0);
+#endif /* SUPPORT_UNICODE */
+ }
+
+if ((cc[-1] & XCL_HASPROP) == 0)
+ {
+ if ((cc[-1] & XCL_MAP) != 0)
+ {
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+ if (!optimize_class(common, (const sljit_u8 *)cc, (((const sljit_u8 *)cc)[31] & 0x80) != 0, TRUE, &found))
+ {
+ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP2, 0);
+ add_jump(compiler, &found, JUMP(SLJIT_NOT_ZERO));
+ }
+
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(jump);
+
+ cc += 32 / sizeof(PCRE2_UCHAR);
+ }
+ else
+ {
+ OP2(SLJIT_SUB, TMP2, 0, TMP1, 0, SLJIT_IMM, min);
+ add_jump(compiler, (cc[-1] & XCL_NOT) == 0 ? backtracks : &found, CMP(SLJIT_GREATER, TMP2, 0, SLJIT_IMM, max - min));
+ }
+ }
+else if ((cc[-1] & XCL_MAP) != 0)
+ {
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+#ifdef SUPPORT_UNICODE
+ unicode_status |= XCLASS_CHAR_SAVED;
+#endif /* SUPPORT_UNICODE */
+ if (!optimize_class(common, (const sljit_u8 *)cc, FALSE, TRUE, list))
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ jump = NULL;
+ if (common->utf)
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ jump = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+
+ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP2, 0);
+ add_jump(compiler, list, JUMP(SLJIT_NOT_ZERO));
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf)
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ JUMPHERE(jump);
+ }
+
+ OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0);
+ cc += 32 / sizeof(PCRE2_UCHAR);
+ }
+
+#ifdef SUPPORT_UNICODE
+if (unicode_status & XCLASS_NEEDS_UCD)
+ {
+ if ((unicode_status & (XCLASS_SAVE_CHAR | XCLASS_CHAR_SAVED)) == XCLASS_SAVE_CHAR)
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (!common->utf)
+ {
+ jump = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, MAX_UTF_CODE_POINT + 1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, UNASSIGNED_UTF_CHAR);
+ JUMPHERE(jump);
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+ OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1);
+ OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_stage1));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_stage2));
+ OP1(SLJIT_MOV_U16, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1);
+ OP2(SLJIT_SHL, TMP1, 0, TMP2, 0, SLJIT_IMM, 3);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+
+ ccbegin = cc;
+
+ if (unicode_status & XCLASS_HAS_BIDICL)
+ {
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, scriptx_bidiclass));
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BIDICLASS_SHIFT);
+
+ while (*cc != XCL_END)
+ {
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_BIDICL)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ if (cc[-1] == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+ jump = CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (int)cc[1]);
+ add_jump(compiler, compares > 0 ? list : backtracks, jump);
+ }
+ cc += 2;
+ }
+ }
+
+ cc = ccbegin;
+ }
+
+ if (unicode_status & XCLASS_HAS_BOOL)
+ {
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, bprops));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BPROPS_MASK);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 2);
+
+ while (*cc != XCL_END)
+ {
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_BOOL)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ if (cc[-1] == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP1), (sljit_sw)(PRIV(ucd_boolprop_sets) + (cc[1] >> 5)), SLJIT_IMM, (sljit_sw)1 << (cc[1] & 0x1f));
+ add_jump(compiler, compares > 0 ? list : backtracks, JUMP(SLJIT_NOT_ZERO ^ invertcmp));
+ }
+ cc += 2;
+ }
+ }
+
+ cc = ccbegin;
+ }
+
+ if (unicode_status & XCLASS_HAS_SCRIPT)
+ {
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script));
+
+ while (*cc != XCL_END)
+ {
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ switch (*cc)
+ {
+ case PT_SCX:
+ if (cc[-1] == XCL_NOTPROP)
+ break;
+ /* Fall through */
+
+ case PT_SC:
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ if (cc[-1] == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+
+ add_jump(compiler, compares > 0 ? list : backtracks, CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (int)cc[1]));
+ }
+ cc += 2;
+ }
+ }
+
+ cc = ccbegin;
+ }
+
+ if (unicode_status & XCLASS_HAS_SCRIPT_EXTENSION)
+ {
+ OP1(SLJIT_MOV_U16, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, scriptx_bidiclass));
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_SCRIPTX_MASK);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 2);
+
+ if (unicode_status & XCLASS_SCRIPT_EXTENSION_NOTPROP)
+ {
+ if (unicode_status & XCLASS_HAS_TYPE)
+ {
+ if (unicode_status & XCLASS_SAVE_CHAR)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, TMP2, 0);
+ unicode_status |= XCLASS_SCRIPT_EXTENSION_RESTORE_LOCALS0;
+ }
+ else
+ {
+ OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP2, 0);
+ unicode_status |= XCLASS_SCRIPT_EXTENSION_RESTORE_RETURN_ADDR;
+ }
+ }
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script));
+ }
+
+ while (*cc != XCL_END)
+ {
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ GETCHARINCTEST(c, cc);
+ }
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ cc++;
+ if (*cc == PT_SCX)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+
+ jump = NULL;
+ if (cc[-1] == XCL_NOTPROP)
+ {
+ jump = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, (int)cc[1]);
+ if (invertcmp)
+ {
+ add_jump(compiler, backtracks, jump);
+ jump = NULL;
+ }
+ invertcmp ^= 0x1;
+ }
+
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP1), (sljit_sw)(PRIV(ucd_script_sets) + (cc[1] >> 5)), SLJIT_IMM, (sljit_sw)1 << (cc[1] & 0x1f));
+ add_jump(compiler, compares > 0 ? list : backtracks, JUMP(SLJIT_NOT_ZERO ^ invertcmp));
+
+ if (jump != NULL)
+ JUMPHERE(jump);
+ }
+ cc += 2;
+ }
+ }
+
+ if (unicode_status & XCLASS_SCRIPT_EXTENSION_RESTORE_LOCALS0)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+ else if (unicode_status & XCLASS_SCRIPT_EXTENSION_RESTORE_RETURN_ADDR)
+ OP1(SLJIT_MOV, TMP2, 0, RETURN_ADDR, 0);
+ cc = ccbegin;
+ }
+
+ if (unicode_status & XCLASS_SAVE_CHAR)
+ OP1(SLJIT_MOV, TMP1, 0, RETURN_ADDR, 0);
+
+ if (unicode_status & XCLASS_HAS_TYPE)
+ {
+ if (unicode_status & XCLASS_SAVE_CHAR)
+ typereg = RETURN_ADDR;
+
+ OP1(SLJIT_MOV_U8, typereg, 0, SLJIT_MEM1(TMP2), (sljit_sw)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype));
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* Generating code. */
+charoffset = 0;
+numberofcmps = 0;
+#ifdef SUPPORT_UNICODE
+typeoffset = 0;
+#endif /* SUPPORT_UNICODE */
+
+while (*cc != XCL_END)
+ {
+ compares--;
+ invertcmp = (compares == 0 && list != backtracks);
+ jump = NULL;
+
+ if (*cc == XCL_SINGLE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+
+ if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE))
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ numberofcmps++;
+ }
+ else if (numberofcmps > 0)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ numberofcmps = 0;
+ }
+ else
+ {
+ jump = CMP(SLJIT_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ numberofcmps = 0;
+ }
+ }
+ else if (*cc == XCL_RANGE)
+ {
+ cc ++;
+ GETCHARINCTEST(c, cc);
+ SET_CHAR_OFFSET(c);
+ GETCHARINCTEST(c, cc);
+
+ if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE))
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ numberofcmps++;
+ }
+ else if (numberofcmps > 0)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ numberofcmps = 0;
+ }
+ else
+ {
+ jump = CMP(SLJIT_LESS_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, (sljit_sw)(c - charoffset));
+ numberofcmps = 0;
+ }
+ }
+#ifdef SUPPORT_UNICODE
+ else
+ {
+ SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP);
+ if (*cc == XCL_NOTPROP)
+ invertcmp ^= 0x1;
+ cc++;
+ switch(*cc)
+ {
+ case PT_ANY:
+ if (!invertcmp)
+ jump = JUMP(SLJIT_JUMP);
+ break;
+
+ case PT_LAMP:
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_GC:
+ c = PRIV(ucp_typerange)[(int)cc[1] * 2];
+ SET_TYPE_OFFSET(c);
+ jump = CMP(SLJIT_LESS_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, PRIV(ucp_typerange)[(int)cc[1] * 2 + 1] - c);
+ break;
+
+ case PT_PC:
+ jump = CMP(SLJIT_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, (int)cc[1] - typeoffset);
+ break;
+
+ case PT_SC:
+ case PT_SCX:
+ case PT_BOOL:
+ case PT_BIDICL:
+ compares++;
+ /* Do nothing. */
+ break;
+
+ case PT_SPACE:
+ case PT_PXSPACE:
+ SET_CHAR_OFFSET(9);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0xd - 0x9);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x85 - 0x9);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x180e - 0x9);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ SET_TYPE_OFFSET(ucp_Zl);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_WORD:
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_UNDERSCORE - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ /* Fall through. */
+
+ case PT_ALNUM:
+ SET_TYPE_OFFSET(ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll);
+ OP_FLAGS((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ SET_TYPE_OFFSET(ucp_Nd);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_CLIST:
+ other_cases = PRIV(ucd_caseless_sets) + cc[1];
+
+ /* At least three characters are required.
+ Otherwise this case would be handled by the normal code path. */
+ SLJIT_ASSERT(other_cases[0] != NOTACHAR && other_cases[1] != NOTACHAR && other_cases[2] != NOTACHAR);
+ SLJIT_ASSERT(other_cases[0] < other_cases[1] && other_cases[1] < other_cases[2]);
+
+ /* Optimizing character pairs, if their difference is power of 2. */
+ if (is_powerof2(other_cases[1] ^ other_cases[0]))
+ {
+ if (charoffset == 0)
+ OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
+ else
+ {
+ OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset);
+ OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
+ }
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, other_cases[1]);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ other_cases += 2;
+ }
+ else if (is_powerof2(other_cases[2] ^ other_cases[1]))
+ {
+ if (charoffset == 0)
+ OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, other_cases[2] ^ other_cases[1]);
+ else
+ {
+ OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, (sljit_sw)charoffset);
+ OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, other_cases[1] ^ other_cases[0]);
+ }
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, other_cases[2]);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(other_cases[0] - charoffset));
+ OP_FLAGS(SLJIT_OR | ((other_cases[3] == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL);
+
+ other_cases += 3;
+ }
+ else
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ }
+
+ while (*other_cases != NOTACHAR)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(*other_cases++ - charoffset));
+ OP_FLAGS(SLJIT_OR | ((*other_cases == NOTACHAR) ? SLJIT_SET_Z : 0), TMP2, 0, SLJIT_EQUAL);
+ }
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_UCNC:
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_DOLLAR_SIGN - charoffset));
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_COMMERCIAL_AT - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (sljit_sw)(CHAR_GRAVE_ACCENT - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ SET_CHAR_OFFSET(0xa0);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, (sljit_sw)(0xd7ff - charoffset));
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_LESS_EQUAL);
+ SET_CHAR_OFFSET(0);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, 0xe000 - 0);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_GREATER_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ case PT_PXGRAPH:
+ /* C and Z groups are the farthest two groups. */
+ SET_TYPE_OFFSET(ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER);
+
+ jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll);
+
+ /* In case of ucp_Cf, we overwrite the result. */
+ SET_CHAR_OFFSET(0x2066);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x180e - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ JUMPHERE(jump);
+ jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0);
+ break;
+
+ case PT_PXPRINT:
+ /* C and Z groups are the farthest two groups. */
+ SET_TYPE_OFFSET(ucp_Ll);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, typereg, 0, SLJIT_IMM, ucp_So - ucp_Ll);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_GREATER);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Ll);
+ OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_NOT_EQUAL);
+
+ jump = CMP(SLJIT_NOT_EQUAL, typereg, 0, SLJIT_IMM, ucp_Cf - ucp_Ll);
+
+ /* In case of ucp_Cf, we overwrite the result. */
+ SET_CHAR_OFFSET(0x2066);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x2069 - 0x2066);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0x061c - 0x2066);
+ OP_FLAGS(SLJIT_OR, TMP2, 0, SLJIT_EQUAL);
+
+ JUMPHERE(jump);
+ jump = CMP(SLJIT_ZERO ^ invertcmp, TMP2, 0, SLJIT_IMM, 0);
+ break;
+
+ case PT_PXPUNCT:
+ SET_TYPE_OFFSET(ucp_Sc);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_So - ucp_Sc);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ SET_CHAR_OFFSET(0);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, TMP1, 0, SLJIT_IMM, 0x7f);
+ OP_FLAGS(SLJIT_AND, TMP2, 0, SLJIT_LESS_EQUAL);
+
+ SET_TYPE_OFFSET(ucp_Pc);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS_EQUAL, typereg, 0, SLJIT_IMM, ucp_Ps - ucp_Pc);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_LESS_EQUAL);
+ jump = JUMP(SLJIT_NOT_ZERO ^ invertcmp);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ cc += 2;
+ }
+#endif /* SUPPORT_UNICODE */
+
+ if (jump != NULL)
+ add_jump(compiler, compares > 0 ? list : backtracks, jump);
+ }
+
+if (found != NULL)
+ set_jumps(found, LABEL());
+}
+
+#undef SET_TYPE_OFFSET
+#undef SET_CHAR_OFFSET
+
+#endif
+
+static PCRE2_SPTR compile_simple_assertion_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks)
+{
+DEFINE_COMPILER;
+int length;
+struct sljit_jump *jump[4];
+#ifdef SUPPORT_UNICODE
+struct sljit_label *label;
+#endif /* SUPPORT_UNICODE */
+
+switch(type)
+ {
+ case OP_SOD:
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ else
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, TMP1, 0));
+ return cc;
+
+ case OP_SOM:
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ }
+ else
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, TMP1, 0));
+ return cc;
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL));
+#ifdef SUPPORT_UNICODE
+ if (common->invalid_utf)
+ {
+ add_jump(compiler, backtracks, CMP((type == OP_NOT_WORD_BOUNDARY) ? SLJIT_NOT_EQUAL : SLJIT_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ return cc;
+ }
+#endif /* SUPPORT_UNICODE */
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_NOT_ZERO : SLJIT_ZERO));
+ return cc;
+
+ case OP_EODN:
+ /* Requires rather complex checks. */
+ jump[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0));
+ else
+ {
+ jump[1] = CMP(SLJIT_EQUAL, TMP2, 0, STR_END, 0);
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP2, 0, STR_END, 0);
+ OP_FLAGS(SLJIT_MOV, TMP2, 0, SLJIT_LESS);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
+ OP_FLAGS(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, SLJIT_NOT_EQUAL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL));
+ check_partial(common, TRUE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(jump[1]);
+ }
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else if (common->nltype == NLTYPE_FIXED)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_END, 0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline));
+ }
+ else
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP2U(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_GREATER, TMP2, 0, STR_END, 0);
+ jump[2] = JUMP(SLJIT_GREATER);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_EQUAL) /* LESS */);
+ /* Equal. */
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ jump[3] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+
+ JUMPHERE(jump[1]);
+ if (common->nltype == NLTYPE_ANYCRLF)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP2, 0, STR_END, 0));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+ read_char(common, common->nlmin, common->nlmax, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
+ add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ }
+ JUMPHERE(jump[2]);
+ JUMPHERE(jump[3]);
+ }
+ JUMPHERE(jump[0]);
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ check_partial(common, TRUE);
+ return cc;
+
+ case OP_EOD:
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0));
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ check_partial(common, TRUE);
+ return cc;
+
+ case OP_DOLL:
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ }
+ else
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+
+ if (!common->endonly)
+ compile_simple_assertion_matchingpath(common, OP_EODN, cc, backtracks);
+ else
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0));
+ check_partial(common, FALSE);
+ }
+ return cc;
+
+ case OP_DOLLM:
+ jump[1] = CMP(SLJIT_LESS, STR_PTR, 0, STR_END, 0);
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ }
+ else
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTEOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ check_partial(common, FALSE);
+ jump[0] = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump[1]);
+
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, TMP2, 0, STR_END, 0));
+ else
+ {
+ jump[1] = CMP(SLJIT_LESS_EQUAL, TMP2, 0, STR_END, 0);
+ /* STR_PTR = STR_END - IN_UCHARS(1) */
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ check_partial(common, TRUE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(jump[1]);
+ }
+
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else
+ {
+ peek_char(common, common->nlmax, TMP3, 0, NULL);
+ check_newlinechar(common, common->nltype, backtracks, FALSE);
+ }
+ JUMPHERE(jump[0]);
+ return cc;
+
+ case OP_CIRC:
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin));
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0));
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, TMP1, 0));
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ }
+ return cc;
+
+ case OP_CIRCM:
+ /* TMP2 might be used by peek_char_back. */
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP2, 0);
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+ jump[1] = CMP(SLJIT_GREATER, STR_PTR, 0, TMP2, 0);
+ OP2U(SLJIT_AND32 | SLJIT_SET_Z, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options), SLJIT_IMM, PCRE2_NOTBOL);
+ }
+ add_jump(compiler, backtracks, JUMP(SLJIT_NOT_ZERO));
+ jump[0] = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump[1]);
+
+ if (!common->alt_circumflex)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, TMP1, 0, TMP2, 0));
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2));
+ OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff));
+ }
+ else
+ {
+ peek_char_back(common, common->nlmax, backtracks);
+ check_newlinechar(common, common->nltype, backtracks, FALSE);
+ }
+ JUMPHERE(jump[0]);
+ return cc;
+
+ case OP_REVERSE:
+ length = GET(cc, 0);
+ if (length == 0)
+ return cc + LINK_SIZE;
+ if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin));
+ }
+ else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, begin));
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, length);
+ label = LABEL();
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0));
+ move_back(common, backtracks, FALSE);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP3, 0, TMP3, 0, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ else
+#endif
+ {
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length));
+ add_jump(compiler, backtracks, CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0));
+ }
+ check_start_used_ptr(common);
+ return cc + LINK_SIZE;
+ }
+SLJIT_UNREACHABLE();
+return cc;
+}
+
+#ifdef SUPPORT_UNICODE
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+static PCRE2_SPTR SLJIT_FUNC do_extuni_utf(jit_arguments *args, PCRE2_SPTR cc)
+{
+PCRE2_SPTR start_subject = args->begin;
+PCRE2_SPTR end_subject = args->end;
+int lgb, rgb, ricount;
+PCRE2_SPTR prevcc, endcc, bptr;
+BOOL first = TRUE;
+uint32_t c;
+
+prevcc = cc;
+endcc = NULL;
+do
+ {
+ GETCHARINC(c, cc);
+ rgb = UCD_GRAPHBREAK(c);
+
+ if (first)
+ {
+ lgb = rgb;
+ endcc = cc;
+ first = FALSE;
+ continue;
+ }
+
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0)
+ break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)
+ {
+ ricount = 0;
+ bptr = prevcc;
+
+ /* bptr is pointing to the left-hand character */
+ while (bptr > start_subject)
+ {
+ bptr--;
+ BACKCHAR(bptr);
+ GETCHAR(c, bptr);
+
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator)
+ break;
+
+ ricount++;
+ }
+
+ if ((ricount & 1) != 0) break; /* Grapheme break required */
+ }
+
+ /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this
+ allows any number of them before a following Extended_Pictographic. */
+
+ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) ||
+ lgb != ucp_gbExtended_Pictographic)
+ lgb = rgb;
+
+ prevcc = endcc;
+ endcc = cc;
+ }
+while (cc < end_subject);
+
+return endcc;
+}
+
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+static PCRE2_SPTR SLJIT_FUNC do_extuni_utf_invalid(jit_arguments *args, PCRE2_SPTR cc)
+{
+PCRE2_SPTR start_subject = args->begin;
+PCRE2_SPTR end_subject = args->end;
+int lgb, rgb, ricount;
+PCRE2_SPTR prevcc, endcc, bptr;
+BOOL first = TRUE;
+uint32_t c;
+
+prevcc = cc;
+endcc = NULL;
+do
+ {
+ GETCHARINC_INVALID(c, cc, end_subject, break);
+ rgb = UCD_GRAPHBREAK(c);
+
+ if (first)
+ {
+ lgb = rgb;
+ endcc = cc;
+ first = FALSE;
+ continue;
+ }
+
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0)
+ break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)
+ {
+ ricount = 0;
+ bptr = prevcc;
+
+ /* bptr is pointing to the left-hand character */
+ while (bptr > start_subject)
+ {
+ GETCHARBACK_INVALID(c, bptr, start_subject, break);
+
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator)
+ break;
+
+ ricount++;
+ }
+
+ if ((ricount & 1) != 0)
+ break; /* Grapheme break required */
+ }
+
+ /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this
+ allows any number of them before a following Extended_Pictographic. */
+
+ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) ||
+ lgb != ucp_gbExtended_Pictographic)
+ lgb = rgb;
+
+ prevcc = endcc;
+ endcc = cc;
+ }
+while (cc < end_subject);
+
+return endcc;
+}
+
+static PCRE2_SPTR SLJIT_FUNC do_extuni_no_utf(jit_arguments *args, PCRE2_SPTR cc)
+{
+PCRE2_SPTR start_subject = args->begin;
+PCRE2_SPTR end_subject = args->end;
+int lgb, rgb, ricount;
+PCRE2_SPTR bptr;
+uint32_t c;
+
+/* Patch by PH */
+/* GETCHARINC(c, cc); */
+c = *cc++;
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+if (c >= 0x110000)
+ return NULL;
+#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
+lgb = UCD_GRAPHBREAK(c);
+
+while (cc < end_subject)
+ {
+ c = *cc;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (c >= 0x110000)
+ break;
+#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
+ rgb = UCD_GRAPHBREAK(c);
+
+ if ((PRIV(ucp_gbtable)[lgb] & (1 << rgb)) == 0)
+ break;
+
+ /* Not breaking between Regional Indicators is allowed only if there
+ are an even number of preceding RIs. */
+
+ if (lgb == ucp_gbRegional_Indicator && rgb == ucp_gbRegional_Indicator)
+ {
+ ricount = 0;
+ bptr = cc - 1;
+
+ /* bptr is pointing to the left-hand character */
+ while (bptr > start_subject)
+ {
+ bptr--;
+ c = *bptr;
+#if PCRE2_CODE_UNIT_WIDTH == 32
+ if (c >= 0x110000)
+ break;
+#endif /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+ if (UCD_GRAPHBREAK(c) != ucp_gbRegional_Indicator) break;
+
+ ricount++;
+ }
+
+ if ((ricount & 1) != 0)
+ break; /* Grapheme break required */
+ }
+
+ /* If Extend or ZWJ follows Extended_Pictographic, do not update lgb; this
+ allows any number of them before a following Extended_Pictographic. */
+
+ if ((rgb != ucp_gbExtend && rgb != ucp_gbZWJ) ||
+ lgb != ucp_gbExtended_Pictographic)
+ lgb = rgb;
+
+ cc++;
+ }
+
+return cc;
+}
+
+#endif /* SUPPORT_UNICODE */
+
+static PCRE2_SPTR compile_char1_matchingpath(compiler_common *common, PCRE2_UCHAR type, PCRE2_SPTR cc, jump_list **backtracks, BOOL check_str_ptr)
+{
+DEFINE_COMPILER;
+int length;
+unsigned int c, oc, bit;
+compare_context context;
+struct sljit_jump *jump[3];
+jump_list *end_list;
+#ifdef SUPPORT_UNICODE
+PCRE2_UCHAR propdata[5];
+#endif /* SUPPORT_UNICODE */
+
+switch(type)
+ {
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ /* Digits are usually 0-9, so it is worth to optimize them. */
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_digit, FALSE))
+ read_char7_type(common, backtracks, type == OP_NOT_DIGIT);
+ else
+#endif
+ read_char8_type(common, backtracks, type == OP_NOT_DIGIT);
+ /* Flip the starting bit in the negative case. */
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, ctype_digit);
+ add_jump(compiler, backtracks, JUMP(type == OP_DIGIT ? SLJIT_ZERO : SLJIT_NOT_ZERO));
+ return cc;
+
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_space, FALSE))
+ read_char7_type(common, backtracks, type == OP_NOT_WHITESPACE);
+ else
+#endif
+ read_char8_type(common, backtracks, type == OP_NOT_WHITESPACE);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, ctype_space);
+ add_jump(compiler, backtracks, JUMP(type == OP_WHITESPACE ? SLJIT_ZERO : SLJIT_NOT_ZERO));
+ return cc;
+
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (common->utf && is_char7_bitset((const sljit_u8*)common->ctypes - cbit_length + cbit_word, FALSE))
+ read_char7_type(common, backtracks, type == OP_NOT_WORDCHAR);
+ else
+#endif
+ read_char8_type(common, backtracks, type == OP_NOT_WORDCHAR);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, ctype_word);
+ add_jump(compiler, backtracks, JUMP(type == OP_WORDCHAR ? SLJIT_ZERO : SLJIT_NOT_ZERO));
+ return cc;
+
+ case OP_ANY:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ read_char(common, common->nlmin, common->nlmax, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ if (common->nltype == NLTYPE_FIXED && common->newline > 255)
+ {
+ jump[0] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff);
+ end_list = NULL;
+ if (common->mode != PCRE2_JIT_PARTIAL_HARD)
+ add_jump(compiler, &end_list, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ else
+ check_str_end(common, &end_list);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline & 0xff));
+ set_jumps(end_list, LABEL());
+ JUMPHERE(jump[0]);
+ }
+ else
+ check_newlinechar(common, common->nltype, backtracks, TRUE);
+ return cc;
+
+ case OP_ALLANY:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ if (common->invalid_utf)
+ {
+ read_char(common, 0, READ_CHAR_MAX, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ return cc;
+ }
+
+#if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00);
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, 0xd800);
+ OP_FLAGS(SLJIT_MOV, TMP1, 0, SLJIT_EQUAL);
+ OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ JUMPHERE(jump[0]);
+ return cc;
+#endif /* PCRE2_CODE_UNIT_WIDTH == [8|16] */
+ }
+#endif /* SUPPORT_UNICODE */
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ return cc;
+
+ case OP_ANYBYTE:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ return cc;
+
+#ifdef SUPPORT_UNICODE
+ case OP_NOTPROP:
+ case OP_PROP:
+ propdata[0] = XCL_HASPROP;
+ propdata[1] = type == OP_NOTPROP ? XCL_NOTPROP : XCL_PROP;
+ propdata[2] = cc[0];
+ propdata[3] = cc[1];
+ propdata[4] = XCL_END;
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ compile_xclass_matchingpath(common, propdata, backtracks);
+ return cc + 2;
+#endif
+
+ case OP_ANYNL:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ read_char(common, common->bsr_nlmin, common->bsr_nlmax, NULL, 0);
+ jump[0] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR);
+ /* We don't need to handle soft partial matching case. */
+ end_list = NULL;
+ if (common->mode != PCRE2_JIT_PARTIAL_HARD)
+ add_jump(compiler, &end_list, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ else
+ check_str_end(common, &end_list);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ jump[1] = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump[2] = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump[0]);
+ check_newlinechar(common, common->bsr_nltype, backtracks, FALSE);
+ set_jumps(end_list, LABEL());
+ JUMPHERE(jump[1]);
+ JUMPHERE(jump[2]);
+ return cc;
+
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+ if (type == OP_NOT_HSPACE)
+ read_char(common, 0x9, 0x3000, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ else
+ read_char(common, 0x9, 0x3000, NULL, 0);
+
+ add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(type == OP_NOT_HSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
+ return cc;
+
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+ if (type == OP_NOT_VSPACE)
+ read_char(common, 0xa, 0x2029, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ else
+ read_char(common, 0xa, 0x2029, NULL, 0);
+
+ add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL));
+ sljit_set_current_flags(compiler, SLJIT_SET_Z);
+ add_jump(compiler, backtracks, JUMP(type == OP_NOT_VSPACE ? SLJIT_NOT_ZERO : SLJIT_ZERO));
+ return cc;
+
+#ifdef SUPPORT_UNICODE
+ case OP_EXTUNI:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+ SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+ OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM,
+ common->utf ? (common->invalid_utf ? SLJIT_FUNC_ADDR(do_extuni_utf_invalid) : SLJIT_FUNC_ADDR(do_extuni_utf)) : SLJIT_FUNC_ADDR(do_extuni_no_utf));
+ if (common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM,
+ common->invalid_utf ? SLJIT_FUNC_ADDR(do_extuni_utf_invalid) : SLJIT_FUNC_ADDR(do_extuni_no_utf));
+ if (!common->utf || common->invalid_utf)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0));
+#endif
+
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+
+ if (common->mode == PCRE2_JIT_PARTIAL_HARD)
+ {
+ jump[0] = CMP(SLJIT_LESS, SLJIT_RETURN_REG, 0, STR_END, 0);
+ /* Since we successfully read a char above, partial matching must occure. */
+ check_partial(common, TRUE);
+ JUMPHERE(jump[0]);
+ }
+ return cc;
+#endif
+
+ case OP_CHAR:
+ case OP_CHARI:
+ length = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(*cc)) length += GET_EXTRALEN(*cc);
+#endif
+
+ if (check_str_ptr && common->mode != PCRE2_JIT_COMPLETE)
+ detect_partial_match(common, backtracks);
+
+ if (type == OP_CHAR || !char_has_othercase(common, cc) || char_get_othercase_bit(common, cc) != 0)
+ {
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length));
+ if (length > 1 || (check_str_ptr && common->mode == PCRE2_JIT_COMPLETE))
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0));
+
+ context.length = IN_UCHARS(length);
+ context.sourcereg = -1;
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ context.ucharptr = 0;
+#endif
+ return byte_sequence_compare(common, type == OP_CHARI, cc, &context, backtracks);
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ GETCHAR(c, cc);
+ }
+ else
+#endif
+ c = *cc;
+
+ SLJIT_ASSERT(type == OP_CHARI && char_has_othercase(common, cc));
+
+ if (check_str_ptr && common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ oc = char_othercase(common, c);
+ read_char(common, c < oc ? c : oc, c > oc ? c : oc, NULL, 0);
+
+ SLJIT_ASSERT(!is_powerof2(c ^ oc));
+
+ if (sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, SLJIT_IMM, oc);
+ CMOV(SLJIT_EQUAL, TMP1, SLJIT_IMM, c);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, c));
+ }
+ else
+ {
+ jump[0] = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c);
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, oc));
+ JUMPHERE(jump[0]);
+ }
+ return cc + length;
+
+ case OP_NOT:
+ case OP_NOTI:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+ length = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ c = *cc;
+ if (c < 128 && !common->invalid_utf)
+ {
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(STR_PTR), 0);
+ if (type == OP_NOT || !char_has_othercase(common, cc))
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c));
+ else
+ {
+ /* Since UTF8 code page is fixed, we know that c is in [a-z] or [A-Z] range. */
+ OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x20);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, c | 0x20));
+ }
+ /* Skip the variable-length character. */
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ jump[0] = CMP(SLJIT_LESS, TMP1, 0, SLJIT_IMM, 0xc0);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)PRIV(utf8_table4) - 0xc0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+ JUMPHERE(jump[0]);
+ return cc + 1;
+ }
+ else
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+ {
+ GETCHARLEN(c, cc, length);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ c = *cc;
+
+ if (type == OP_NOT || !char_has_othercase(common, cc))
+ {
+ read_char(common, c, c, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c));
+ }
+ else
+ {
+ oc = char_othercase(common, c);
+ read_char(common, c < oc ? c : oc, c > oc ? c : oc, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ bit = c ^ oc;
+ if (is_powerof2(bit))
+ {
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, bit);
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c | bit));
+ }
+ else
+ {
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, c));
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, oc));
+ }
+ }
+ return cc + length;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ bit = (common->utf && is_char7_bitset((const sljit_u8 *)cc, type == OP_NCLASS)) ? 127 : 255;
+ if (type == OP_NCLASS)
+ read_char(common, 0, bit, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ else
+ read_char(common, 0, bit, NULL, 0);
+#else
+ if (type == OP_NCLASS)
+ read_char(common, 0, 255, backtracks, READ_CHAR_UPDATE_STR_PTR);
+ else
+ read_char(common, 0, 255, NULL, 0);
+#endif
+
+ if (optimize_class(common, (const sljit_u8 *)cc, type == OP_NCLASS, FALSE, backtracks))
+ return cc + 32 / sizeof(PCRE2_UCHAR);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ jump[0] = NULL;
+ if (common->utf)
+ {
+ jump[0] = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, bit);
+ if (type == OP_CLASS)
+ {
+ add_jump(compiler, backtracks, jump[0]);
+ jump[0] = NULL;
+ }
+ }
+#elif PCRE2_CODE_UNIT_WIDTH != 8
+ jump[0] = CMP(SLJIT_GREATER, TMP1, 0, SLJIT_IMM, 255);
+ if (type == OP_CLASS)
+ {
+ add_jump(compiler, backtracks, jump[0]);
+ jump[0] = NULL;
+ }
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */
+
+ OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7);
+ OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3);
+ OP1(SLJIT_MOV_U8, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_sw)cc);
+ OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP1, 0, TMP2, 0);
+ add_jump(compiler, backtracks, JUMP(SLJIT_ZERO));
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ if (jump[0] != NULL)
+ JUMPHERE(jump[0]);
+#endif
+ return cc + 32 / sizeof(PCRE2_UCHAR);
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ case OP_XCLASS:
+ if (check_str_ptr)
+ detect_partial_match(common, backtracks);
+ compile_xclass_matchingpath(common, cc + LINK_SIZE, backtracks);
+ return cc + GET(cc, 0) - 1;
+#endif
+ }
+SLJIT_UNREACHABLE();
+return cc;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_charn_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, jump_list **backtracks)
+{
+/* This function consumes at least one input character. */
+/* To decrease the number of length checks, we try to concatenate the fixed length character sequences. */
+DEFINE_COMPILER;
+PCRE2_SPTR ccbegin = cc;
+compare_context context;
+int size;
+
+context.length = 0;
+do
+ {
+ if (cc >= ccend)
+ break;
+
+ if (*cc == OP_CHAR)
+ {
+ size = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf && HAS_EXTRALEN(cc[1]))
+ size += GET_EXTRALEN(cc[1]);
+#endif
+ }
+ else if (*cc == OP_CHARI)
+ {
+ size = 1;
+#ifdef SUPPORT_UNICODE
+ if (common->utf)
+ {
+ if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0)
+ size = 0;
+ else if (HAS_EXTRALEN(cc[1]))
+ size += GET_EXTRALEN(cc[1]);
+ }
+ else
+#endif
+ if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0)
+ size = 0;
+ }
+ else
+ size = 0;
+
+ cc += 1 + size;
+ context.length += IN_UCHARS(size);
+ }
+while (size > 0 && context.length <= 128);
+
+cc = ccbegin;
+if (context.length > 0)
+ {
+ /* We have a fixed-length byte sequence. */
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, context.length);
+ add_jump(compiler, backtracks, CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0));
+
+ context.sourcereg = -1;
+#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED
+ context.ucharptr = 0;
+#endif
+ do cc = byte_sequence_compare(common, *cc == OP_CHARI, cc + 1, &context, backtracks); while (context.length > 0);
+ return cc;
+ }
+
+/* A non-fixed length character will be checked if length == 0. */
+return compile_char1_matchingpath(common, *cc, cc + 1, backtracks, TRUE);
+}
+
+/* Forward definitions. */
+static void compile_matchingpath(compiler_common *, PCRE2_SPTR, PCRE2_SPTR, backtrack_common *);
+static void compile_backtrackingpath(compiler_common *, struct backtrack_common *);
+
+#define PUSH_BACKTRACK(size, ccstart, error) \
+ do \
+ { \
+ backtrack = sljit_alloc_memory(compiler, (size)); \
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \
+ return error; \
+ memset(backtrack, 0, size); \
+ backtrack->prev = parent->top; \
+ backtrack->cc = (ccstart); \
+ parent->top = backtrack; \
+ } \
+ while (0)
+
+#define PUSH_BACKTRACK_NOVALUE(size, ccstart) \
+ do \
+ { \
+ backtrack = sljit_alloc_memory(compiler, (size)); \
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \
+ return; \
+ memset(backtrack, 0, size); \
+ backtrack->prev = parent->top; \
+ backtrack->cc = (ccstart); \
+ parent->top = backtrack; \
+ } \
+ while (0)
+
+#define BACKTRACK_AS(type) ((type *)backtrack)
+
+static void compile_dnref_search(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks)
+{
+/* The OVECTOR offset goes to TMP2. */
+DEFINE_COMPILER;
+int count = GET2(cc, 1 + IMM2_SIZE);
+PCRE2_SPTR slot = common->name_table + GET2(cc, 1) * common->name_entry_size;
+unsigned int offset;
+jump_list *found = NULL;
+
+SLJIT_ASSERT(*cc == OP_DNREF || *cc == OP_DNREFI);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
+
+count--;
+while (count-- > 0)
+ {
+ offset = GET2(slot, 0) << 1;
+ GET_LOCAL_BASE(TMP2, 0, OVECTOR(offset));
+ add_jump(compiler, &found, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0));
+ slot += common->name_entry_size;
+ }
+
+offset = GET2(slot, 0) << 1;
+GET_LOCAL_BASE(TMP2, 0, OVECTOR(offset));
+if (backtracks != NULL && !common->unset_backref)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0));
+
+set_jumps(found, LABEL());
+}
+
+static void compile_ref_matchingpath(compiler_common *common, PCRE2_SPTR cc, jump_list **backtracks, BOOL withchecks, BOOL emptyfail)
+{
+DEFINE_COMPILER;
+BOOL ref = (*cc == OP_REF || *cc == OP_REFI);
+int offset = 0;
+struct sljit_jump *jump = NULL;
+struct sljit_jump *partial;
+struct sljit_jump *nopartial;
+#if defined SUPPORT_UNICODE
+struct sljit_label *loop;
+struct sljit_label *caseless_loop;
+jump_list *no_match = NULL;
+int source_reg = COUNT_MATCH;
+int source_end_reg = ARGUMENTS;
+int char1_reg = STACK_LIMIT;
+#endif /* SUPPORT_UNICODE */
+
+if (ref)
+ {
+ offset = GET2(cc, 1) << 1;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ /* OVECTOR(1) contains the "string begin - 1" constant. */
+ if (withchecks && !common->unset_backref)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)));
+ }
+else
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+
+#if defined SUPPORT_UNICODE
+if (common->utf && *cc == OP_REFI)
+ {
+ SLJIT_ASSERT(common->iref_ptr != 0);
+
+ if (ref)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ else
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+
+ if (withchecks && emptyfail)
+ add_jump(compiler, backtracks, CMP(SLJIT_EQUAL, TMP1, 0, TMP2, 0));
+
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr, source_reg, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw), source_end_reg, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2, char1_reg, 0);
+
+ OP1(SLJIT_MOV, source_reg, 0, TMP1, 0);
+ OP1(SLJIT_MOV, source_end_reg, 0, TMP2, 0);
+
+ loop = LABEL();
+ jump = CMP(SLJIT_GREATER_EQUAL, source_reg, 0, source_end_reg, 0);
+ partial = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+
+ /* Read original character. It must be a valid UTF character. */
+ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, source_reg, 0);
+
+ read_char(common, 0, READ_CHAR_MAX, NULL, READ_CHAR_UPDATE_STR_PTR | READ_CHAR_VALID_UTF);
+
+ OP1(SLJIT_MOV, source_reg, 0, STR_PTR, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ OP1(SLJIT_MOV, char1_reg, 0, TMP1, 0);
+
+ /* Read second character. */
+ read_char(common, 0, READ_CHAR_MAX, &no_match, READ_CHAR_UPDATE_STR_PTR);
+
+ CMPTO(SLJIT_EQUAL, TMP1, 0, char1_reg, 0, loop);
+
+ OP1(SLJIT_MOV, TMP3, 0, TMP1, 0);
+
+ add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL));
+
+ OP2(SLJIT_SHL, TMP1, 0, TMP2, 0, SLJIT_IMM, 2);
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 3);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, TMP1, 0);
+
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_records));
+
+ OP1(SLJIT_MOV_S32, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(ucd_record, other_case));
+ OP1(SLJIT_MOV_U8, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(ucd_record, caseset));
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP3, 0);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, char1_reg, 0, loop);
+
+ add_jump(compiler, &no_match, CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 2);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_sw)PRIV(ucd_caseless_sets));
+
+ caseless_loop = LABEL();
+ OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, sizeof(uint32_t));
+ OP2U(SLJIT_SUB | SLJIT_SET_Z | SLJIT_SET_LESS, TMP1, 0, char1_reg, 0);
+ JUMPTO(SLJIT_EQUAL, loop);
+ JUMPTO(SLJIT_LESS, caseless_loop);
+
+ set_jumps(no_match, LABEL());
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial);
+
+ OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr);
+ OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ JUMPHERE(partial);
+ OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr);
+ OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2);
+
+ check_partial(common, FALSE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ }
+
+ JUMPHERE(jump);
+ OP1(SLJIT_MOV, source_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr);
+ OP1(SLJIT_MOV, source_end_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, char1_reg, 0, SLJIT_MEM1(SLJIT_SP), common->iref_ptr + sizeof(sljit_sw) * 2);
+ return;
+ }
+else
+#endif /* SUPPORT_UNICODE */
+ {
+ if (ref)
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0);
+ else
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw), TMP1, 0);
+
+ if (withchecks)
+ jump = JUMP(SLJIT_ZERO);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+ partial = CMP(SLJIT_GREATER, STR_PTR, 0, STR_END, 0);
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, backtracks, partial);
+
+ add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+
+ if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ nopartial = JUMP(SLJIT_JUMP);
+ JUMPHERE(partial);
+ /* TMP2 -= STR_END - STR_PTR */
+ OP2(SLJIT_SUB, TMP2, 0, TMP2, 0, STR_PTR, 0);
+ OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, STR_END, 0);
+ partial = CMP(SLJIT_EQUAL, TMP2, 0, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0);
+ add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL));
+ add_jump(compiler, backtracks, CMP(SLJIT_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0));
+ JUMPHERE(partial);
+ check_partial(common, FALSE);
+ add_jump(compiler, backtracks, JUMP(SLJIT_JUMP));
+ JUMPHERE(nopartial);
+ }
+ }
+
+if (jump != NULL)
+ {
+ if (emptyfail)
+ add_jump(compiler, backtracks, jump);
+ else
+ JUMPHERE(jump);
+ }
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_ref_iterator_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+BOOL ref = (*cc == OP_REF || *cc == OP_REFI);
+backtrack_common *backtrack;
+PCRE2_UCHAR type;
+int offset = 0;
+struct sljit_label *label;
+struct sljit_jump *zerolength;
+struct sljit_jump *jump = NULL;
+PCRE2_SPTR ccbegin = cc;
+int min = 0, max = 0;
+BOOL minimize;
+
+PUSH_BACKTRACK(sizeof(ref_iterator_backtrack), cc, NULL);
+
+if (ref)
+ offset = GET2(cc, 1) << 1;
+else
+ cc += IMM2_SIZE;
+type = cc[1 + IMM2_SIZE];
+
+SLJIT_COMPILE_ASSERT((OP_CRSTAR & 0x1) == 0, crstar_opcode_must_be_even);
+minimize = (type & 0x1) != 0;
+switch(type)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ min = 0;
+ max = 0;
+ cc += 1 + IMM2_SIZE + 1;
+ break;
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ min = 1;
+ max = 0;
+ cc += 1 + IMM2_SIZE + 1;
+ break;
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ min = 0;
+ max = 1;
+ cc += 1 + IMM2_SIZE + 1;
+ break;
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ min = GET2(cc, 1 + IMM2_SIZE + 1);
+ max = GET2(cc, 1 + IMM2_SIZE + 1 + IMM2_SIZE);
+ cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+if (!minimize)
+ {
+ if (min == 0)
+ {
+ allocate_stack(common, 2);
+ if (ref)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
+ /* Temporary release of STR_PTR. */
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ /* Handles both invalid and empty cases. Since the minimum repeat,
+ is zero the invalid case is basically the same as an empty case. */
+ if (ref)
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ else
+ {
+ compile_dnref_search(common, ccbegin, NULL);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, TMP2, 0);
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ }
+ /* Restore if not zero length. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ }
+ else
+ {
+ allocate_stack(common, 1);
+ if (ref)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ if (ref)
+ {
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)));
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ }
+ else
+ {
+ compile_dnref_search(common, ccbegin, &backtrack->topbacktracks);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, TMP2, 0);
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ }
+ }
+
+ if (min > 1 || max > 1)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, SLJIT_IMM, 0);
+
+ label = LABEL();
+ if (!ref)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1);
+ compile_ref_matchingpath(common, ccbegin, &backtrack->topbacktracks, FALSE, FALSE);
+
+ if (min > 1 || max > 1)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0);
+ if (min > 1)
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, label);
+ if (max > 1)
+ {
+ jump = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, max);
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ JUMPHERE(jump);
+ }
+ }
+
+ if (max == 0)
+ {
+ /* Includes min > 1 case as well. */
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ }
+
+ JUMPHERE(zerolength);
+ BACKTRACK_AS(ref_iterator_backtrack)->matchingpath = LABEL();
+
+ count_match(common);
+ return cc;
+ }
+
+allocate_stack(common, ref ? 2 : 3);
+if (ref)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+if (type != OP_CRMINSTAR)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
+
+if (min == 0)
+ {
+ /* Handles both invalid and empty cases. Since the minimum repeat,
+ is zero the invalid case is basically the same as an empty case. */
+ if (ref)
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ else
+ {
+ compile_dnref_search(common, ccbegin, NULL);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0);
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ }
+ /* Length is non-zero, we can match real repeats. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ jump = JUMP(SLJIT_JUMP);
+ }
+else
+ {
+ if (ref)
+ {
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1)));
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ }
+ else
+ {
+ compile_dnref_search(common, ccbegin, &backtrack->topbacktracks);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0);
+ zerolength = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_MEM1(TMP2), sizeof(sljit_sw));
+ }
+ }
+
+BACKTRACK_AS(ref_iterator_backtrack)->matchingpath = LABEL();
+if (max > 0)
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, max));
+
+if (!ref)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(2));
+compile_ref_matchingpath(common, ccbegin, &backtrack->topbacktracks, TRUE, TRUE);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+if (min > 1)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
+ CMPTO(SLJIT_LESS, TMP1, 0, SLJIT_IMM, min, BACKTRACK_AS(ref_iterator_backtrack)->matchingpath);
+ }
+else if (max > 0)
+ OP2(SLJIT_ADD, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1);
+
+if (jump != NULL)
+ JUMPHERE(jump);
+JUMPHERE(zerolength);
+
+count_match(common);
+return cc;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_recurse_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+recurse_entry *entry = common->entries;
+recurse_entry *prev = NULL;
+sljit_sw start = GET(cc, 1);
+PCRE2_SPTR start_cc;
+BOOL needs_control_head;
+
+PUSH_BACKTRACK(sizeof(recurse_backtrack), cc, NULL);
+
+/* Inlining simple patterns. */
+if (get_framesize(common, common->start + start, NULL, TRUE, &needs_control_head) == no_stack)
+ {
+ start_cc = common->start + start;
+ compile_matchingpath(common, next_opcode(common, start_cc), bracketend(start_cc) - (1 + LINK_SIZE), backtrack);
+ BACKTRACK_AS(recurse_backtrack)->inlined_pattern = TRUE;
+ return cc + 1 + LINK_SIZE;
+ }
+
+while (entry != NULL)
+ {
+ if (entry->start == start)
+ break;
+ prev = entry;
+ entry = entry->next;
+ }
+
+if (entry == NULL)
+ {
+ entry = sljit_alloc_memory(compiler, sizeof(recurse_entry));
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+ entry->next = NULL;
+ entry->entry_label = NULL;
+ entry->backtrack_label = NULL;
+ entry->entry_calls = NULL;
+ entry->backtrack_calls = NULL;
+ entry->start = start;
+
+ if (prev != NULL)
+ prev->next = entry;
+ else
+ common->entries = entry;
+ }
+
+BACKTRACK_AS(recurse_backtrack)->entry = entry;
+
+if (entry->entry_label == NULL)
+ add_jump(compiler, &entry->entry_calls, JUMP(SLJIT_FAST_CALL));
+else
+ JUMPTO(SLJIT_FAST_CALL, entry->entry_label);
+/* Leave if the match is failed. */
+add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0));
+BACKTRACK_AS(recurse_backtrack)->matchingpath = LABEL();
+return cc + 1 + LINK_SIZE;
+}
+
+static sljit_s32 SLJIT_FUNC do_callout_jit(struct jit_arguments *arguments, pcre2_callout_block *callout_block, PCRE2_SPTR *jit_ovector)
+{
+PCRE2_SPTR begin;
+PCRE2_SIZE *ovector;
+sljit_u32 oveccount, capture_top;
+
+if (arguments->callout == NULL)
+ return 0;
+
+SLJIT_COMPILE_ASSERT(sizeof (PCRE2_SIZE) <= sizeof (sljit_sw), pcre2_size_must_be_lower_than_sljit_sw_size);
+
+begin = arguments->begin;
+ovector = (PCRE2_SIZE*)(callout_block + 1);
+oveccount = callout_block->capture_top;
+
+SLJIT_ASSERT(oveccount >= 1);
+
+callout_block->version = 2;
+callout_block->callout_flags = 0;
+
+/* Offsets in subject. */
+callout_block->subject_length = arguments->end - arguments->begin;
+callout_block->start_match = jit_ovector[0] - begin;
+callout_block->current_position = (PCRE2_SPTR)callout_block->offset_vector - begin;
+callout_block->subject = begin;
+
+/* Convert and copy the JIT offset vector to the ovector array. */
+callout_block->capture_top = 1;
+callout_block->offset_vector = ovector;
+
+ovector[0] = PCRE2_UNSET;
+ovector[1] = PCRE2_UNSET;
+ovector += 2;
+jit_ovector += 2;
+capture_top = 1;
+
+/* Convert pointers to sizes. */
+while (--oveccount != 0)
+ {
+ capture_top++;
+
+ ovector[0] = (PCRE2_SIZE)(jit_ovector[0] - begin);
+ ovector[1] = (PCRE2_SIZE)(jit_ovector[1] - begin);
+
+ if (ovector[0] != PCRE2_UNSET)
+ callout_block->capture_top = capture_top;
+
+ ovector += 2;
+ jit_ovector += 2;
+ }
+
+return (arguments->callout)(callout_block, arguments->callout_data);
+}
+
+#define CALLOUT_ARG_OFFSET(arg) \
+ SLJIT_OFFSETOF(pcre2_callout_block, arg)
+
+static SLJIT_INLINE PCRE2_SPTR compile_callout_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+sljit_s32 mov_opcode;
+unsigned int callout_length = (*cc == OP_CALLOUT)
+ ? PRIV(OP_lengths)[OP_CALLOUT] : GET(cc, 1 + 2 * LINK_SIZE);
+sljit_sw value1;
+sljit_sw value2;
+sljit_sw value3;
+sljit_uw callout_arg_size = (common->re->top_bracket + 1) * 2 * SSIZE_OF(sw);
+
+PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL);
+
+callout_arg_size = (sizeof(pcre2_callout_block) + callout_arg_size + sizeof(sljit_sw) - 1) / sizeof(sljit_sw);
+
+allocate_stack(common, callout_arg_size);
+
+SLJIT_ASSERT(common->capture_last_ptr != 0);
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+value1 = (*cc == OP_CALLOUT) ? cc[1 + 2 * LINK_SIZE] : 0;
+OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_number), SLJIT_IMM, value1);
+OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_last), TMP2, 0);
+OP1(SLJIT_MOV_U32, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(capture_top), SLJIT_IMM, common->re->top_bracket + 1);
+
+/* These pointer sized fields temporarly stores internal variables. */
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(offset_vector), STR_PTR, 0);
+
+if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, mark_ptr));
+mov_opcode = (sizeof(PCRE2_SIZE) == 4) ? SLJIT_MOV_U32 : SLJIT_MOV;
+OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(pattern_position), SLJIT_IMM, GET(cc, 1));
+OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(next_item_length), SLJIT_IMM, GET(cc, 1 + LINK_SIZE));
+
+if (*cc == OP_CALLOUT)
+ {
+ value1 = 0;
+ value2 = 0;
+ value3 = 0;
+ }
+else
+ {
+ value1 = (sljit_sw) (cc + (1 + 4*LINK_SIZE) + 1);
+ value2 = (callout_length - (1 + 4*LINK_SIZE + 2));
+ value3 = (sljit_sw) (GET(cc, 1 + 3*LINK_SIZE));
+ }
+
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string), SLJIT_IMM, value1);
+OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_length), SLJIT_IMM, value2);
+OP1(mov_opcode, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(callout_string_offset), SLJIT_IMM, value3);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), CALLOUT_ARG_OFFSET(mark), (common->mark_ptr != 0) ? TMP2 : SLJIT_IMM, 0);
+
+SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+
+/* Needed to save important temporary registers. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0);
+/* SLJIT_R0 = arguments */
+OP1(SLJIT_MOV, SLJIT_R1, 0, STACK_TOP, 0);
+GET_LOCAL_BASE(SLJIT_R2, 0, OVECTOR_START);
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS3(32, W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(do_callout_jit));
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+free_stack(common, callout_arg_size);
+
+/* Check return value. */
+OP2U(SLJIT_SUB32 | SLJIT_SET_Z | SLJIT_SET_SIG_GREATER, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_SIG_GREATER));
+if (common->abort_label == NULL)
+ add_jump(compiler, &common->abort, JUMP(SLJIT_NOT_EQUAL) /* SIG_LESS */);
+else
+ JUMPTO(SLJIT_NOT_EQUAL /* SIG_LESS */, common->abort_label);
+return cc + callout_length;
+}
+
+#undef CALLOUT_ARG_SIZE
+#undef CALLOUT_ARG_OFFSET
+
+static SLJIT_INLINE BOOL assert_needs_str_ptr_saving(PCRE2_SPTR cc)
+{
+while (TRUE)
+ {
+ switch (*cc)
+ {
+ case OP_CALLOUT_STR:
+ cc += GET(cc, 1 + 2*LINK_SIZE);
+ break;
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CALLOUT:
+ case OP_ALT:
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+
+ case OP_KET:
+ return FALSE;
+
+ default:
+ return TRUE;
+ }
+ }
+}
+
+static PCRE2_SPTR compile_assert_matchingpath(compiler_common *common, PCRE2_SPTR cc, assert_backtrack *backtrack, BOOL conditional)
+{
+DEFINE_COMPILER;
+int framesize;
+int extrasize;
+BOOL local_quit_available = FALSE;
+BOOL needs_control_head;
+int private_data_ptr;
+backtrack_common altbacktrack;
+PCRE2_SPTR ccbegin;
+PCRE2_UCHAR opcode;
+PCRE2_UCHAR bra = OP_BRA;
+jump_list *tmp = NULL;
+jump_list **target = (conditional) ? &backtrack->condfailed : &backtrack->common.topbacktracks;
+jump_list **found;
+/* Saving previous accept variables. */
+BOOL save_local_quit_available = common->local_quit_available;
+BOOL save_in_positive_assertion = common->in_positive_assertion;
+then_trap_backtrack *save_then_trap = common->then_trap;
+struct sljit_label *save_quit_label = common->quit_label;
+struct sljit_label *save_accept_label = common->accept_label;
+jump_list *save_quit = common->quit;
+jump_list *save_positive_assertion_quit = common->positive_assertion_quit;
+jump_list *save_accept = common->accept;
+struct sljit_jump *jump;
+struct sljit_jump *brajump = NULL;
+
+/* Assert captures then. */
+common->then_trap = NULL;
+
+if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO)
+ {
+ SLJIT_ASSERT(!conditional);
+ bra = *cc;
+ cc++;
+ }
+private_data_ptr = PRIVATE_DATA(cc);
+SLJIT_ASSERT(private_data_ptr != 0);
+framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head);
+backtrack->framesize = framesize;
+backtrack->private_data_ptr = private_data_ptr;
+opcode = *cc;
+SLJIT_ASSERT(opcode >= OP_ASSERT && opcode <= OP_ASSERTBACK_NOT);
+found = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) ? &tmp : target;
+ccbegin = cc;
+cc += GET(cc, 1);
+
+if (bra == OP_BRAMINZERO)
+ {
+ /* This is a braminzero backtrack path. */
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ brajump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ }
+
+if (framesize < 0)
+ {
+ extrasize = 1;
+ if (bra == OP_BRA && !assert_needs_str_ptr_saving(ccbegin + 1 + LINK_SIZE))
+ extrasize = 0;
+
+ if (needs_control_head)
+ extrasize++;
+
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0);
+
+ if (extrasize > 0)
+ allocate_stack(common, extrasize);
+
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+ if (needs_control_head)
+ {
+ SLJIT_ASSERT(extrasize == 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
+ }
+ }
+else
+ {
+ extrasize = needs_control_head ? 3 : 2;
+ allocate_stack(common, framesize + extrasize);
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + extrasize) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+ if (needs_control_head)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+ }
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0);
+
+ init_frame(common, ccbegin, NULL, framesize + extrasize - 1, extrasize);
+ }
+
+memset(&altbacktrack, 0, sizeof(backtrack_common));
+if (conditional || (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT))
+ {
+ /* Control verbs cannot escape from these asserts. */
+ local_quit_available = TRUE;
+ common->local_quit_available = TRUE;
+ common->quit_label = NULL;
+ common->quit = NULL;
+ }
+
+common->in_positive_assertion = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK);
+common->positive_assertion_quit = NULL;
+
+while (1)
+ {
+ common->accept_label = NULL;
+ common->accept = NULL;
+ altbacktrack.top = NULL;
+ altbacktrack.topbacktracks = NULL;
+
+ if (*ccbegin == OP_ALT && extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ altbacktrack.cc = ccbegin;
+ compile_matchingpath(common, ccbegin + 1 + LINK_SIZE, cc, &altbacktrack);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ {
+ if (local_quit_available)
+ {
+ common->local_quit_available = save_local_quit_available;
+ common->quit_label = save_quit_label;
+ common->quit = save_quit;
+ }
+ common->in_positive_assertion = save_in_positive_assertion;
+ common->then_trap = save_then_trap;
+ common->accept_label = save_accept_label;
+ common->positive_assertion_quit = save_positive_assertion_quit;
+ common->accept = save_accept;
+ return NULL;
+ }
+ common->accept_label = LABEL();
+ if (common->accept != NULL)
+ set_jumps(common->accept, common->accept_label);
+
+ /* Reset stack. */
+ if (framesize < 0)
+ {
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ else if (extrasize > 0)
+ free_stack(common, extrasize);
+
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ }
+ else
+ {
+ if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional)
+ {
+ /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ }
+ else
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 2));
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw));
+ }
+ }
+
+ if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT)
+ {
+ /* We know that STR_PTR was stored on the top of the stack. */
+ if (conditional)
+ {
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), needs_control_head ? STACK(-2) : STACK(-1));
+ }
+ else if (bra == OP_BRAZERO)
+ {
+ if (framesize < 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize));
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-framesize - extrasize));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else if (framesize >= 0)
+ {
+ /* For OP_BRA and OP_BRAMINZERO. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-framesize - 1));
+ }
+ }
+ add_jump(compiler, found, JUMP(SLJIT_JUMP));
+
+ compile_backtrackingpath(common, altbacktrack.top);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ {
+ if (local_quit_available)
+ {
+ common->local_quit_available = save_local_quit_available;
+ common->quit_label = save_quit_label;
+ common->quit = save_quit;
+ }
+ common->in_positive_assertion = save_in_positive_assertion;
+ common->then_trap = save_then_trap;
+ common->accept_label = save_accept_label;
+ common->positive_assertion_quit = save_positive_assertion_quit;
+ common->accept = save_accept;
+ return NULL;
+ }
+ set_jumps(altbacktrack.topbacktracks, LABEL());
+
+ if (*cc != OP_ALT)
+ break;
+
+ ccbegin = cc;
+ cc += GET(cc, 1);
+ }
+
+if (local_quit_available)
+ {
+ SLJIT_ASSERT(common->positive_assertion_quit == NULL);
+ /* Makes the check less complicated below. */
+ common->positive_assertion_quit = common->quit;
+ }
+
+/* None of them matched. */
+if (common->positive_assertion_quit != NULL)
+ {
+ jump = JUMP(SLJIT_JUMP);
+ set_jumps(common->positive_assertion_quit, LABEL());
+ SLJIT_ASSERT(framesize != no_stack);
+ if (framesize < 0)
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, extrasize * sizeof(sljit_sw));
+ else
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (extrasize + 1) * sizeof(sljit_sw));
+ }
+ JUMPHERE(jump);
+ }
+
+if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(1));
+
+if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK)
+ {
+ /* Assert is failed. */
+ if ((conditional && extrasize > 0) || bra == OP_BRAZERO)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ if (framesize < 0)
+ {
+ /* The topmost item should be 0. */
+ if (bra == OP_BRAZERO)
+ {
+ if (extrasize == 2)
+ free_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else if (extrasize > 0)
+ free_stack(common, extrasize);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(extrasize - 1));
+ /* The topmost item should be 0. */
+ if (bra == OP_BRAZERO)
+ {
+ free_stack(common, framesize + extrasize - 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else
+ free_stack(common, framesize + extrasize);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ jump = JUMP(SLJIT_JUMP);
+ if (bra != OP_BRAZERO)
+ add_jump(compiler, target, jump);
+
+ /* Assert is successful. */
+ set_jumps(tmp, LABEL());
+ if (framesize < 0)
+ {
+ /* We know that STR_PTR was stored on the top of the stack. */
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize));
+
+ /* Keep the STR_PTR on the top of the stack. */
+ if (bra == OP_BRAZERO)
+ {
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ if (extrasize == 2)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ }
+ else if (bra == OP_BRAMINZERO)
+ {
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ }
+ else
+ {
+ if (bra == OP_BRA)
+ {
+ /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-extrasize + 1));
+ }
+ else
+ {
+ /* We don't need to keep the STR_PTR, only the previous private_data_ptr. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_sw));
+ if (extrasize == 2)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (bra == OP_BRAMINZERO)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else
+ {
+ SLJIT_ASSERT(extrasize == 3);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), bra == OP_BRAZERO ? STR_PTR : SLJIT_IMM, 0);
+ }
+ }
+ }
+
+ if (bra == OP_BRAZERO)
+ {
+ backtrack->matchingpath = LABEL();
+ SET_LABEL(jump, backtrack->matchingpath);
+ }
+ else if (bra == OP_BRAMINZERO)
+ {
+ JUMPTO(SLJIT_JUMP, backtrack->matchingpath);
+ JUMPHERE(brajump);
+ if (framesize >= 0)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ set_jumps(backtrack->common.topbacktracks, LABEL());
+ }
+ }
+else
+ {
+ /* AssertNot is successful. */
+ if (framesize < 0)
+ {
+ if (extrasize > 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ if (bra != OP_BRA)
+ {
+ if (extrasize == 2)
+ free_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else if (extrasize > 0)
+ free_stack(common, extrasize);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(extrasize - 1));
+ /* The topmost item should be 0. */
+ if (bra != OP_BRA)
+ {
+ free_stack(common, framesize + extrasize - 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ else
+ free_stack(common, framesize + extrasize);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+
+ if (bra == OP_BRAZERO)
+ backtrack->matchingpath = LABEL();
+ else if (bra == OP_BRAMINZERO)
+ {
+ JUMPTO(SLJIT_JUMP, backtrack->matchingpath);
+ JUMPHERE(brajump);
+ }
+
+ if (bra != OP_BRA)
+ {
+ SLJIT_ASSERT(found == &backtrack->common.topbacktracks);
+ set_jumps(backtrack->common.topbacktracks, LABEL());
+ backtrack->common.topbacktracks = NULL;
+ }
+ }
+
+if (local_quit_available)
+ {
+ common->local_quit_available = save_local_quit_available;
+ common->quit_label = save_quit_label;
+ common->quit = save_quit;
+ }
+common->in_positive_assertion = save_in_positive_assertion;
+common->then_trap = save_then_trap;
+common->accept_label = save_accept_label;
+common->positive_assertion_quit = save_positive_assertion_quit;
+common->accept = save_accept;
+return cc + 1 + LINK_SIZE;
+}
+
+static SLJIT_INLINE void match_once_common(compiler_common *common, PCRE2_UCHAR ket, int framesize, int private_data_ptr, BOOL has_alternatives, BOOL needs_control_head)
+{
+DEFINE_COMPILER;
+int stacksize;
+
+if (framesize < 0)
+ {
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ else
+ {
+ stacksize = needs_control_head ? 1 : 0;
+ if (ket != OP_KET || has_alternatives)
+ stacksize++;
+
+ if (stacksize > 0)
+ free_stack(common, stacksize);
+ }
+
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), (ket != OP_KET || has_alternatives) ? STACK(-2) : STACK(-1));
+
+ /* TMP2 which is set here used by OP_KETRMAX below. */
+ if (ket == OP_KETRMAX)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ else if (ket == OP_KETRMIN)
+ {
+ /* Move the STR_PTR to the private_data_ptr. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-1));
+ }
+ }
+else
+ {
+ stacksize = (ket != OP_KET || has_alternatives) ? 2 : 1;
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, (framesize + stacksize) * sizeof(sljit_sw));
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-1));
+
+ if (ket == OP_KETRMAX)
+ {
+ /* TMP2 which is set here used by OP_KETRMAX below. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ }
+ }
+if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP1, 0);
+}
+
+static SLJIT_INLINE int match_capture_common(compiler_common *common, int stacksize, int offset, int private_data_ptr)
+{
+DEFINE_COMPILER;
+
+if (common->capture_last_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
+ stacksize++;
+ }
+if (common->optimized_cbracket[offset >> 1] == 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ stacksize += 2;
+ }
+return stacksize;
+}
+
+static PCRE2_SPTR SLJIT_FUNC do_script_run(PCRE2_SPTR ptr, PCRE2_SPTR endptr)
+{
+ if (PRIV(script_run)(ptr, endptr, FALSE))
+ return endptr;
+ return NULL;
+}
+
+#ifdef SUPPORT_UNICODE
+
+static PCRE2_SPTR SLJIT_FUNC do_script_run_utf(PCRE2_SPTR ptr, PCRE2_SPTR endptr)
+{
+ if (PRIV(script_run)(ptr, endptr, TRUE))
+ return endptr;
+ return NULL;
+}
+
+#endif /* SUPPORT_UNICODE */
+
+static SLJIT_INLINE void match_script_run_common(compiler_common *common, int private_data_ptr, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+
+SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+#ifdef SUPPORT_UNICODE
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM,
+ common->utf ? SLJIT_FUNC_ADDR(do_script_run_utf) : SLJIT_FUNC_ADDR(do_script_run));
+#else
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(do_script_run));
+#endif
+
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+add_jump(compiler, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0));
+}
+
+/*
+ Handling bracketed expressions is probably the most complex part.
+
+ Stack layout naming characters:
+ S - Push the current STR_PTR
+ 0 - Push a 0 (NULL)
+ A - Push the current STR_PTR. Needed for restoring the STR_PTR
+ before the next alternative. Not pushed if there are no alternatives.
+ M - Any values pushed by the current alternative. Can be empty, or anything.
+ C - Push the previous OVECTOR(i), OVECTOR(i+1) and OVECTOR_PRIV(i) to the stack.
+ L - Push the previous local (pointed by localptr) to the stack
+ () - opional values stored on the stack
+ ()* - optonal, can be stored multiple times
+
+ The following list shows the regular expression templates, their PCRE byte codes
+ and stack layout supported by pcre-sljit.
+
+ (?:) OP_BRA | OP_KET A M
+ () OP_CBRA | OP_KET C M
+ (?:)+ OP_BRA | OP_KETRMAX 0 A M S ( A M S )*
+ OP_SBRA | OP_KETRMAX 0 L M S ( L M S )*
+ (?:)+? OP_BRA | OP_KETRMIN 0 A M S ( A M S )*
+ OP_SBRA | OP_KETRMIN 0 L M S ( L M S )*
+ ()+ OP_CBRA | OP_KETRMAX 0 C M S ( C M S )*
+ OP_SCBRA | OP_KETRMAX 0 C M S ( C M S )*
+ ()+? OP_CBRA | OP_KETRMIN 0 C M S ( C M S )*
+ OP_SCBRA | OP_KETRMIN 0 C M S ( C M S )*
+ (?:)? OP_BRAZERO | OP_BRA | OP_KET S ( A M 0 )
+ (?:)?? OP_BRAMINZERO | OP_BRA | OP_KET S ( A M 0 )
+ ()? OP_BRAZERO | OP_CBRA | OP_KET S ( C M 0 )
+ ()?? OP_BRAMINZERO | OP_CBRA | OP_KET S ( C M 0 )
+ (?:)* OP_BRAZERO | OP_BRA | OP_KETRMAX S 0 ( A M S )*
+ OP_BRAZERO | OP_SBRA | OP_KETRMAX S 0 ( L M S )*
+ (?:)*? OP_BRAMINZERO | OP_BRA | OP_KETRMIN S 0 ( A M S )*
+ OP_BRAMINZERO | OP_SBRA | OP_KETRMIN S 0 ( L M S )*
+ ()* OP_BRAZERO | OP_CBRA | OP_KETRMAX S 0 ( C M S )*
+ OP_BRAZERO | OP_SCBRA | OP_KETRMAX S 0 ( C M S )*
+ ()*? OP_BRAMINZERO | OP_CBRA | OP_KETRMIN S 0 ( C M S )*
+ OP_BRAMINZERO | OP_SCBRA | OP_KETRMIN S 0 ( C M S )*
+
+
+ Stack layout naming characters:
+ A - Push the alternative index (starting from 0) on the stack.
+ Not pushed if there is no alternatives.
+ M - Any values pushed by the current alternative. Can be empty, or anything.
+
+ The next list shows the possible content of a bracket:
+ (|) OP_*BRA | OP_ALT ... M A
+ (?()|) OP_*COND | OP_ALT M A
+ (?>|) OP_ONCE | OP_ALT ... [stack trace] M A
+ Or nothing, if trace is unnecessary
+*/
+
+static PCRE2_SPTR compile_bracket_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+PCRE2_UCHAR opcode;
+int private_data_ptr = 0;
+int offset = 0;
+int i, stacksize;
+int repeat_ptr = 0, repeat_length = 0;
+int repeat_type = 0, repeat_count = 0;
+PCRE2_SPTR ccbegin;
+PCRE2_SPTR matchingpath;
+PCRE2_SPTR slot;
+PCRE2_UCHAR bra = OP_BRA;
+PCRE2_UCHAR ket;
+assert_backtrack *assert;
+BOOL has_alternatives;
+BOOL needs_control_head = FALSE;
+struct sljit_jump *jump;
+struct sljit_jump *skip;
+struct sljit_label *rmax_label = NULL;
+struct sljit_jump *braminzero = NULL;
+
+PUSH_BACKTRACK(sizeof(bracket_backtrack), cc, NULL);
+
+if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO)
+ {
+ bra = *cc;
+ cc++;
+ opcode = *cc;
+ }
+
+opcode = *cc;
+ccbegin = cc;
+matchingpath = bracketend(cc) - 1 - LINK_SIZE;
+ket = *matchingpath;
+if (ket == OP_KET && PRIVATE_DATA(matchingpath) != 0)
+ {
+ repeat_ptr = PRIVATE_DATA(matchingpath);
+ repeat_length = PRIVATE_DATA(matchingpath + 1);
+ repeat_type = PRIVATE_DATA(matchingpath + 2);
+ repeat_count = PRIVATE_DATA(matchingpath + 3);
+ SLJIT_ASSERT(repeat_length != 0 && repeat_type != 0 && repeat_count != 0);
+ if (repeat_type == OP_UPTO)
+ ket = OP_KETRMAX;
+ if (repeat_type == OP_MINUPTO)
+ ket = OP_KETRMIN;
+ }
+
+matchingpath = ccbegin + 1 + LINK_SIZE;
+SLJIT_ASSERT(ket == OP_KET || ket == OP_KETRMAX || ket == OP_KETRMIN);
+SLJIT_ASSERT(!((bra == OP_BRAZERO && ket == OP_KETRMIN) || (bra == OP_BRAMINZERO && ket == OP_KETRMAX)));
+cc += GET(cc, 1);
+
+has_alternatives = *cc == OP_ALT;
+if (SLJIT_UNLIKELY(opcode == OP_COND || opcode == OP_SCOND))
+ {
+ SLJIT_COMPILE_ASSERT(OP_DNRREF == OP_RREF + 1 && OP_FALSE == OP_RREF + 2 && OP_TRUE == OP_RREF + 3,
+ compile_time_checks_must_be_grouped_together);
+ has_alternatives = ((*matchingpath >= OP_RREF && *matchingpath <= OP_TRUE) || *matchingpath == OP_FAIL) ? FALSE : TRUE;
+ }
+
+if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN))
+ opcode = OP_SCOND;
+
+if (opcode == OP_CBRA || opcode == OP_SCBRA)
+ {
+ /* Capturing brackets has a pre-allocated space. */
+ offset = GET2(ccbegin, 1 + LINK_SIZE);
+ if (common->optimized_cbracket[offset] == 0)
+ {
+ private_data_ptr = OVECTOR_PRIV(offset);
+ offset <<= 1;
+ }
+ else
+ {
+ offset <<= 1;
+ private_data_ptr = OVECTOR(offset);
+ }
+ BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr;
+ matchingpath += IMM2_SIZE;
+ }
+else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_ONCE || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND)
+ {
+ /* Other brackets simply allocate the next entry. */
+ private_data_ptr = PRIVATE_DATA(ccbegin);
+ SLJIT_ASSERT(private_data_ptr != 0);
+ BACKTRACK_AS(bracket_backtrack)->private_data_ptr = private_data_ptr;
+ if (opcode == OP_ONCE)
+ BACKTRACK_AS(bracket_backtrack)->u.framesize = get_framesize(common, ccbegin, NULL, FALSE, &needs_control_head);
+ }
+
+/* Instructions before the first alternative. */
+stacksize = 0;
+if (ket == OP_KETRMAX || (ket == OP_KETRMIN && bra != OP_BRAMINZERO))
+ stacksize++;
+if (bra == OP_BRAZERO)
+ stacksize++;
+
+if (stacksize > 0)
+ allocate_stack(common, stacksize);
+
+stacksize = 0;
+if (ket == OP_KETRMAX || (ket == OP_KETRMIN && bra != OP_BRAMINZERO))
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0);
+ stacksize++;
+ }
+
+if (bra == OP_BRAZERO)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+
+if (bra == OP_BRAMINZERO)
+ {
+ /* This is a backtrack path! (Since the try-path of OP_BRAMINZERO matches to the empty string) */
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (ket != OP_KETRMIN)
+ {
+ free_stack(common, 1);
+ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ }
+ else if (opcode == OP_ONCE || opcode >= OP_SBRA)
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ /* Nothing stored during the first run. */
+ skip = JUMP(SLJIT_JUMP);
+ JUMPHERE(jump);
+ /* Checking zero-length iteration. */
+ if (opcode != OP_ONCE || BACKTRACK_AS(bracket_backtrack)->u.framesize < 0)
+ {
+ /* When we come from outside, private_data_ptr contains the previous STR_PTR. */
+ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ }
+ else
+ {
+ /* Except when the whole stack frame must be saved. */
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ braminzero = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-BACKTRACK_AS(bracket_backtrack)->u.framesize - 2));
+ }
+ JUMPHERE(skip);
+ }
+ else
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ JUMPHERE(jump);
+ }
+ }
+
+if (repeat_type != 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, repeat_count);
+ if (repeat_type == OP_EXACT)
+ rmax_label = LABEL();
+ }
+
+if (ket == OP_KETRMIN)
+ BACKTRACK_AS(bracket_backtrack)->recursive_matchingpath = LABEL();
+
+if (ket == OP_KETRMAX)
+ {
+ rmax_label = LABEL();
+ if (has_alternatives && opcode >= OP_BRA && opcode < OP_SBRA && repeat_type == 0)
+ BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = rmax_label;
+ }
+
+/* Handling capturing brackets and alternatives. */
+if (opcode == OP_ONCE)
+ {
+ stacksize = 0;
+ if (needs_control_head)
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ stacksize++;
+ }
+
+ if (BACKTRACK_AS(bracket_backtrack)->u.framesize < 0)
+ {
+ /* Neither capturing brackets nor recursions are found in the block. */
+ if (ket == OP_KETRMIN)
+ {
+ stacksize += 2;
+ if (!needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ }
+ else
+ {
+ if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0);
+ if (ket == OP_KETRMAX || has_alternatives)
+ stacksize++;
+ }
+
+ if (stacksize > 0)
+ allocate_stack(common, stacksize);
+
+ stacksize = 0;
+ if (needs_control_head)
+ {
+ stacksize++;
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+
+ if (ket == OP_KETRMIN)
+ {
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ if (BACKTRACK_AS(bracket_backtrack)->u.framesize == no_frame)
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, needs_control_head ? (2 * sizeof(sljit_sw)) : sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize + 1), TMP2, 0);
+ }
+ else if (ket == OP_KETRMAX || has_alternatives)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ }
+ else
+ {
+ if (ket != OP_KET || has_alternatives)
+ stacksize++;
+
+ stacksize += BACKTRACK_AS(bracket_backtrack)->u.framesize + 1;
+ allocate_stack(common, stacksize);
+
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP2(SLJIT_ADD, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+
+ stacksize = needs_control_head ? 1 : 0;
+ if (ket != OP_KET || has_alternatives)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
+ stacksize++;
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP1, 0);
+ }
+ init_frame(common, ccbegin, NULL, BACKTRACK_AS(bracket_backtrack)->u.framesize + stacksize, stacksize + 1);
+ }
+ }
+else if (opcode == OP_CBRA || opcode == OP_SCBRA)
+ {
+ /* Saving the previous values. */
+ if (common->optimized_cbracket[offset >> 1] != 0)
+ {
+ SLJIT_ASSERT(private_data_ptr == OVECTOR(offset));
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr + sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+ }
+else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND)
+ {
+ /* Saving the previous value. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+else if (has_alternatives)
+ {
+ /* Pushing the starting string pointer. */
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ }
+
+/* Generating code for the first alternative. */
+if (opcode == OP_COND || opcode == OP_SCOND)
+ {
+ if (*matchingpath == OP_CREF)
+ {
+ SLJIT_ASSERT(has_alternatives);
+ add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed),
+ CMP(SLJIT_EQUAL, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(matchingpath, 1) << 1), SLJIT_MEM1(SLJIT_SP), OVECTOR(1)));
+ matchingpath += 1 + IMM2_SIZE;
+ }
+ else if (*matchingpath == OP_DNCREF)
+ {
+ SLJIT_ASSERT(has_alternatives);
+
+ i = GET2(matchingpath, 1 + IMM2_SIZE);
+ slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size;
+ OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(1));
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
+ slot += common->name_entry_size;
+ i--;
+ while (i-- > 0)
+ {
+ OP2(SLJIT_SUB, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(GET2(slot, 0) << 1), TMP1, 0);
+ OP2(SLJIT_OR | SLJIT_SET_Z, TMP2, 0, TMP2, 0, STR_PTR, 0);
+ slot += common->name_entry_size;
+ }
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ add_jump(compiler, &(BACKTRACK_AS(bracket_backtrack)->u.condfailed), JUMP(SLJIT_ZERO));
+ matchingpath += 1 + 2 * IMM2_SIZE;
+ }
+ else if ((*matchingpath >= OP_RREF && *matchingpath <= OP_TRUE) || *matchingpath == OP_FAIL)
+ {
+ /* Never has other case. */
+ BACKTRACK_AS(bracket_backtrack)->u.condfailed = NULL;
+ SLJIT_ASSERT(!has_alternatives);
+
+ if (*matchingpath == OP_TRUE)
+ {
+ stacksize = 1;
+ matchingpath++;
+ }
+ else if (*matchingpath == OP_FALSE || *matchingpath == OP_FAIL)
+ stacksize = 0;
+ else if (*matchingpath == OP_RREF)
+ {
+ stacksize = GET2(matchingpath, 1);
+ if (common->currententry == NULL)
+ stacksize = 0;
+ else if (stacksize == RREF_ANY)
+ stacksize = 1;
+ else if (common->currententry->start == 0)
+ stacksize = stacksize == 0;
+ else
+ stacksize = stacksize == (int)GET2(common->start, common->currententry->start + 1 + LINK_SIZE);
+
+ if (stacksize != 0)
+ matchingpath += 1 + IMM2_SIZE;
+ }
+ else
+ {
+ if (common->currententry == NULL || common->currententry->start == 0)
+ stacksize = 0;
+ else
+ {
+ stacksize = GET2(matchingpath, 1 + IMM2_SIZE);
+ slot = common->name_table + GET2(matchingpath, 1) * common->name_entry_size;
+ i = (int)GET2(common->start, common->currententry->start + 1 + LINK_SIZE);
+ while (stacksize > 0)
+ {
+ if ((int)GET2(slot, 0) == i)
+ break;
+ slot += common->name_entry_size;
+ stacksize--;
+ }
+ }
+
+ if (stacksize != 0)
+ matchingpath += 1 + 2 * IMM2_SIZE;
+ }
+
+ /* The stacksize == 0 is a common "else" case. */
+ if (stacksize == 0)
+ {
+ if (*cc == OP_ALT)
+ {
+ matchingpath = cc + 1 + LINK_SIZE;
+ cc += GET(cc, 1);
+ }
+ else
+ matchingpath = cc;
+ }
+ }
+ else
+ {
+ SLJIT_ASSERT(has_alternatives && *matchingpath >= OP_ASSERT && *matchingpath <= OP_ASSERTBACK_NOT);
+ /* Similar code as PUSH_BACKTRACK macro. */
+ assert = sljit_alloc_memory(compiler, sizeof(assert_backtrack));
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+ memset(assert, 0, sizeof(assert_backtrack));
+ assert->common.cc = matchingpath;
+ BACKTRACK_AS(bracket_backtrack)->u.assert = assert;
+ matchingpath = compile_assert_matchingpath(common, matchingpath, assert, TRUE);
+ }
+ }
+
+compile_matchingpath(common, matchingpath, cc, backtrack);
+if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+
+if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+
+if (opcode == OP_ONCE)
+ match_once_common(common, ket, BACKTRACK_AS(bracket_backtrack)->u.framesize, private_data_ptr, has_alternatives, needs_control_head);
+
+if (opcode == OP_SCRIPT_RUN)
+ match_script_run_common(common, private_data_ptr, backtrack);
+
+stacksize = 0;
+if (repeat_type == OP_MINUPTO)
+ {
+ /* We need to preserve the counter. TMP2 will be used below. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr);
+ stacksize++;
+ }
+if (ket != OP_KET || bra != OP_BRA)
+ stacksize++;
+if (offset != 0)
+ {
+ if (common->capture_last_ptr != 0)
+ stacksize++;
+ if (common->optimized_cbracket[offset >> 1] == 0)
+ stacksize += 2;
+ }
+if (has_alternatives && opcode != OP_ONCE)
+ stacksize++;
+
+if (stacksize > 0)
+ allocate_stack(common, stacksize);
+
+stacksize = 0;
+if (repeat_type == OP_MINUPTO)
+ {
+ /* TMP2 was set above. */
+ OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP2, 0, SLJIT_IMM, 1);
+ stacksize++;
+ }
+
+if (ket != OP_KET || bra != OP_BRA)
+ {
+ if (ket != OP_KET)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0);
+ stacksize++;
+ }
+
+if (offset != 0)
+ stacksize = match_capture_common(common, stacksize, offset, private_data_ptr);
+
+/* Skip and count the other alternatives. */
+i = 1;
+while (*cc == OP_ALT)
+ {
+ cc += GET(cc, 1);
+ i++;
+ }
+
+if (has_alternatives)
+ {
+ if (opcode != OP_ONCE)
+ {
+ if (i <= 3)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0);
+ else
+ BACKTRACK_AS(bracket_backtrack)->u.matching_put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(stacksize));
+ }
+ if (ket != OP_KETRMAX)
+ BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL();
+ }
+
+/* Must be after the matchingpath label. */
+if (offset != 0 && common->optimized_cbracket[offset >> 1] != 0)
+ {
+ SLJIT_ASSERT(private_data_ptr == OVECTOR(offset + 0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ }
+
+if (ket == OP_KETRMAX)
+ {
+ if (repeat_type != 0)
+ {
+ if (has_alternatives)
+ BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL();
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, rmax_label);
+ /* Drop STR_PTR for greedy plus quantifier. */
+ if (opcode != OP_ONCE)
+ free_stack(common, 1);
+ }
+ else if (opcode < OP_BRA || opcode >= OP_SBRA)
+ {
+ if (has_alternatives)
+ BACKTRACK_AS(bracket_backtrack)->alternative_matchingpath = LABEL();
+
+ /* Checking zero-length iteration. */
+ if (opcode != OP_ONCE)
+ {
+ /* This case includes opcodes such as OP_SCRIPT_RUN. */
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STR_PTR, 0, rmax_label);
+ /* Drop STR_PTR for greedy plus quantifier. */
+ if (bra != OP_BRAZERO)
+ free_stack(common, 1);
+ }
+ else
+ /* TMP2 must contain the starting STR_PTR. */
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, rmax_label);
+ }
+ else
+ JUMPTO(SLJIT_JUMP, rmax_label);
+ BACKTRACK_AS(bracket_backtrack)->recursive_matchingpath = LABEL();
+ }
+
+if (repeat_type == OP_EXACT)
+ {
+ count_match(common);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, rmax_label);
+ }
+else if (repeat_type == OP_UPTO)
+ {
+ /* We need to preserve the counter. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr);
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ }
+
+if (bra == OP_BRAZERO)
+ BACKTRACK_AS(bracket_backtrack)->zero_matchingpath = LABEL();
+
+if (bra == OP_BRAMINZERO)
+ {
+ /* This is a backtrack path! (From the viewpoint of OP_BRAMINZERO) */
+ JUMPTO(SLJIT_JUMP, ((braminzero_backtrack *)parent)->matchingpath);
+ if (braminzero != NULL)
+ {
+ JUMPHERE(braminzero);
+ /* We need to release the end pointer to perform the
+ backtrack for the zero-length iteration. When
+ framesize is < 0, OP_ONCE will do the release itself. */
+ if (opcode == OP_ONCE && BACKTRACK_AS(bracket_backtrack)->u.framesize >= 0)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (BACKTRACK_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw));
+ }
+ else if (ket == OP_KETRMIN && opcode != OP_ONCE)
+ free_stack(common, 1);
+ }
+ /* Continue to the normal backtrack. */
+ }
+
+if ((ket != OP_KET && bra != OP_BRAMINZERO) || bra == OP_BRAZERO)
+ count_match(common);
+
+cc += 1 + LINK_SIZE;
+
+if (opcode == OP_ONCE)
+ {
+ /* We temporarily encode the needs_control_head in the lowest bit.
+ Note: on the target architectures of SLJIT the ((x << 1) >> 1) returns
+ the same value for small signed numbers (including negative numbers). */
+ BACKTRACK_AS(bracket_backtrack)->u.framesize = (int)((unsigned)BACKTRACK_AS(bracket_backtrack)->u.framesize << 1) | (needs_control_head ? 1 : 0);
+ }
+return cc + repeat_length;
+}
+
+static PCRE2_SPTR compile_bracketpos_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+PCRE2_UCHAR opcode;
+int private_data_ptr;
+int cbraprivptr = 0;
+BOOL needs_control_head;
+int framesize;
+int stacksize;
+int offset = 0;
+BOOL zero = FALSE;
+PCRE2_SPTR ccbegin = NULL;
+int stack; /* Also contains the offset of control head. */
+struct sljit_label *loop = NULL;
+struct jump_list *emptymatch = NULL;
+
+PUSH_BACKTRACK(sizeof(bracketpos_backtrack), cc, NULL);
+if (*cc == OP_BRAPOSZERO)
+ {
+ zero = TRUE;
+ cc++;
+ }
+
+opcode = *cc;
+private_data_ptr = PRIVATE_DATA(cc);
+SLJIT_ASSERT(private_data_ptr != 0);
+BACKTRACK_AS(bracketpos_backtrack)->private_data_ptr = private_data_ptr;
+switch(opcode)
+ {
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ ccbegin = cc + 1 + LINK_SIZE;
+ break;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ offset = GET2(cc, 1 + LINK_SIZE);
+ /* This case cannot be optimized in the same was as
+ normal capturing brackets. */
+ SLJIT_ASSERT(common->optimized_cbracket[offset] == 0);
+ cbraprivptr = OVECTOR_PRIV(offset);
+ offset <<= 1;
+ ccbegin = cc + 1 + LINK_SIZE + IMM2_SIZE;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+framesize = get_framesize(common, cc, NULL, FALSE, &needs_control_head);
+BACKTRACK_AS(bracketpos_backtrack)->framesize = framesize;
+if (framesize < 0)
+ {
+ if (offset != 0)
+ {
+ stacksize = 2;
+ if (common->capture_last_ptr != 0)
+ stacksize++;
+ }
+ else
+ stacksize = 1;
+
+ if (needs_control_head)
+ stacksize++;
+ if (!zero)
+ stacksize++;
+
+ BACKTRACK_AS(bracketpos_backtrack)->stacksize = stacksize;
+ allocate_stack(common, stacksize);
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0);
+
+ stack = 0;
+ if (offset != 0)
+ {
+ stack = 2;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0);
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ if (common->capture_last_ptr != 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0);
+ stack = 3;
+ }
+ }
+ else
+ {
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ stack = 1;
+ }
+
+ if (needs_control_head)
+ stack++;
+ if (!zero)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), SLJIT_IMM, 1);
+ if (needs_control_head)
+ {
+ stack--;
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP2, 0);
+ }
+ }
+else
+ {
+ stacksize = framesize + 1;
+ if (!zero)
+ stacksize++;
+ if (needs_control_head)
+ stacksize++;
+ if (offset == 0)
+ stacksize++;
+ BACKTRACK_AS(bracketpos_backtrack)->stacksize = stacksize;
+
+ allocate_stack(common, stacksize);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ if (needs_control_head)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), private_data_ptr, STACK_TOP, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+
+ stack = 0;
+ if (!zero)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 1);
+ stack = 1;
+ }
+ if (needs_control_head)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP2, 0);
+ stack++;
+ }
+ if (offset == 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), STR_PTR, 0);
+ stack++;
+ }
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0);
+ init_frame(common, cc, NULL, stacksize - 1, stacksize - framesize);
+ stack -= 1 + (offset == 0);
+ }
+
+if (offset != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0);
+
+loop = LABEL();
+while (*cc != OP_KETRPOS)
+ {
+ backtrack->top = NULL;
+ backtrack->topbacktracks = NULL;
+ cc += GET(cc, 1);
+
+ compile_matchingpath(common, ccbegin, cc, backtrack);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+
+ if (framesize < 0)
+ {
+ if (framesize == no_frame)
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+
+ if (offset != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ }
+ else
+ {
+ if (opcode == OP_SBRAPOS)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ }
+
+ /* Even if the match is empty, we need to reset the control head. */
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack));
+
+ if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS)
+ add_jump(compiler, &emptymatch, CMP(SLJIT_EQUAL, TMP1, 0, STR_PTR, 0));
+
+ if (!zero)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0);
+ }
+ else
+ {
+ if (offset != 0)
+ {
+ OP2(SLJIT_SUB, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), cbraprivptr, STR_PTR, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, offset >> 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP2(SLJIT_SUB, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_sw));
+ if (opcode == OP_SBRAPOS)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(-framesize - 2), STR_PTR, 0);
+ }
+
+ /* Even if the match is empty, we need to reset the control head. */
+ if (needs_control_head)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_MEM1(STACK_TOP), STACK(stack));
+
+ if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS)
+ add_jump(compiler, &emptymatch, CMP(SLJIT_EQUAL, TMP1, 0, STR_PTR, 0));
+
+ if (!zero)
+ {
+ if (framesize < 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ }
+ }
+
+ JUMPTO(SLJIT_JUMP, loop);
+ flush_stubs(common);
+
+ compile_backtrackingpath(common, backtrack->top);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return NULL;
+ set_jumps(backtrack->topbacktracks, LABEL());
+
+ if (framesize < 0)
+ {
+ if (offset != 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
+ else
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ }
+ else
+ {
+ if (offset != 0)
+ {
+ /* Last alternative. */
+ if (*cc == OP_KETRPOS)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), cbraprivptr);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), STACK(-framesize - 2));
+ }
+ }
+
+ if (*cc == OP_KETRPOS)
+ break;
+ ccbegin = cc + 1 + LINK_SIZE;
+ }
+
+/* We don't have to restore the control head in case of a failed match. */
+
+backtrack->topbacktracks = NULL;
+if (!zero)
+ {
+ if (framesize < 0)
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0));
+ else /* TMP2 is set to [private_data_ptr] above. */
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(TMP2), STACK(-stacksize), SLJIT_IMM, 0));
+ }
+
+/* None of them matched. */
+set_jumps(emptymatch, LABEL());
+count_match(common);
+return cc + 1 + LINK_SIZE;
+}
+
+static SLJIT_INLINE PCRE2_SPTR get_iterator_parameters(compiler_common *common, PCRE2_SPTR cc, PCRE2_UCHAR *opcode, PCRE2_UCHAR *type, sljit_u32 *max, sljit_u32 *exact, PCRE2_SPTR *end)
+{
+int class_len;
+
+*opcode = *cc;
+*exact = 0;
+
+if (*opcode >= OP_STAR && *opcode <= OP_POSUPTO)
+ {
+ cc++;
+ *type = OP_CHAR;
+ }
+else if (*opcode >= OP_STARI && *opcode <= OP_POSUPTOI)
+ {
+ cc++;
+ *type = OP_CHARI;
+ *opcode -= OP_STARI - OP_STAR;
+ }
+else if (*opcode >= OP_NOTSTAR && *opcode <= OP_NOTPOSUPTO)
+ {
+ cc++;
+ *type = OP_NOT;
+ *opcode -= OP_NOTSTAR - OP_STAR;
+ }
+else if (*opcode >= OP_NOTSTARI && *opcode <= OP_NOTPOSUPTOI)
+ {
+ cc++;
+ *type = OP_NOTI;
+ *opcode -= OP_NOTSTARI - OP_STAR;
+ }
+else if (*opcode >= OP_TYPESTAR && *opcode <= OP_TYPEPOSUPTO)
+ {
+ cc++;
+ *opcode -= OP_TYPESTAR - OP_STAR;
+ *type = OP_END;
+ }
+else
+ {
+ SLJIT_ASSERT(*opcode == OP_CLASS || *opcode == OP_NCLASS || *opcode == OP_XCLASS);
+ *type = *opcode;
+ cc++;
+ class_len = (*type < OP_XCLASS) ? (int)(1 + (32 / sizeof(PCRE2_UCHAR))) : GET(cc, 0);
+ *opcode = cc[class_len - 1];
+
+ if (*opcode >= OP_CRSTAR && *opcode <= OP_CRMINQUERY)
+ {
+ *opcode -= OP_CRSTAR - OP_STAR;
+ *end = cc + class_len;
+
+ if (*opcode == OP_PLUS || *opcode == OP_MINPLUS)
+ {
+ *exact = 1;
+ *opcode -= OP_PLUS - OP_STAR;
+ }
+ }
+ else if (*opcode >= OP_CRPOSSTAR && *opcode <= OP_CRPOSQUERY)
+ {
+ *opcode -= OP_CRPOSSTAR - OP_POSSTAR;
+ *end = cc + class_len;
+
+ if (*opcode == OP_POSPLUS)
+ {
+ *exact = 1;
+ *opcode = OP_POSSTAR;
+ }
+ }
+ else
+ {
+ SLJIT_ASSERT(*opcode == OP_CRRANGE || *opcode == OP_CRMINRANGE || *opcode == OP_CRPOSRANGE);
+ *max = GET2(cc, (class_len + IMM2_SIZE));
+ *exact = GET2(cc, class_len);
+
+ if (*max == 0)
+ {
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSSTAR;
+ else
+ *opcode -= OP_CRRANGE - OP_STAR;
+ }
+ else
+ {
+ *max -= *exact;
+ if (*max == 0)
+ *opcode = OP_EXACT;
+ else if (*max == 1)
+ {
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSQUERY;
+ else
+ *opcode -= OP_CRRANGE - OP_QUERY;
+ }
+ else
+ {
+ if (*opcode == OP_CRPOSRANGE)
+ *opcode = OP_POSUPTO;
+ else
+ *opcode -= OP_CRRANGE - OP_UPTO;
+ }
+ }
+ *end = cc + class_len + 2 * IMM2_SIZE;
+ }
+ return cc;
+ }
+
+switch(*opcode)
+ {
+ case OP_EXACT:
+ *exact = GET2(cc, 0);
+ cc += IMM2_SIZE;
+ break;
+
+ case OP_PLUS:
+ case OP_MINPLUS:
+ *exact = 1;
+ *opcode -= OP_PLUS - OP_STAR;
+ break;
+
+ case OP_POSPLUS:
+ *exact = 1;
+ *opcode = OP_POSSTAR;
+ break;
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_POSUPTO:
+ *max = GET2(cc, 0);
+ cc += IMM2_SIZE;
+ break;
+ }
+
+if (*type == OP_END)
+ {
+ *type = *cc;
+ *end = next_opcode(common, cc);
+ cc++;
+ return cc;
+ }
+
+*end = cc + 1;
+#ifdef SUPPORT_UNICODE
+if (common->utf && HAS_EXTRALEN(*cc)) *end += GET_EXTRALEN(*cc);
+#endif
+return cc;
+}
+
+static PCRE2_SPTR compile_iterator_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+PCRE2_UCHAR opcode;
+PCRE2_UCHAR type;
+sljit_u32 max = 0, exact;
+sljit_s32 early_fail_ptr = PRIVATE_DATA(cc + 1);
+sljit_s32 early_fail_type;
+BOOL charpos_enabled;
+PCRE2_UCHAR charpos_char;
+unsigned int charpos_othercasebit;
+PCRE2_SPTR end;
+jump_list *no_match = NULL;
+jump_list *no_char1_match = NULL;
+struct sljit_jump *jump = NULL;
+struct sljit_label *label;
+int private_data_ptr = PRIVATE_DATA(cc);
+int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP);
+int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr;
+int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + SSIZE_OF(sw);
+int tmp_base, tmp_offset;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+BOOL use_tmp;
+#endif
+
+PUSH_BACKTRACK(sizeof(char_iterator_backtrack), cc, NULL);
+
+early_fail_type = (early_fail_ptr & 0x7);
+early_fail_ptr >>= 3;
+
+/* During recursion, these optimizations are disabled. */
+if (common->early_fail_start_ptr == 0 && common->fast_forward_bc_ptr == NULL)
+ {
+ early_fail_ptr = 0;
+ early_fail_type = type_skip;
+ }
+
+SLJIT_ASSERT(common->fast_forward_bc_ptr != NULL || early_fail_ptr == 0
+ || (early_fail_ptr >= common->early_fail_start_ptr && early_fail_ptr <= common->early_fail_end_ptr));
+
+if (early_fail_type == type_fail)
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr));
+
+cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end);
+
+if (type != OP_EXTUNI)
+ {
+ tmp_base = TMP3;
+ tmp_offset = 0;
+ }
+else
+ {
+ tmp_base = SLJIT_MEM1(SLJIT_SP);
+ tmp_offset = POSSESSIVE0;
+ }
+
+/* Handle fixed part first. */
+if (exact > 1)
+ {
+ SLJIT_ASSERT(early_fail_ptr == 0);
+
+ if (common->mode == PCRE2_JIT_COMPLETE
+#ifdef SUPPORT_UNICODE
+ && !common->utf
+#endif
+ && type != OP_ANYNL && type != OP_EXTUNI)
+ {
+ OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(exact));
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER, TMP1, 0, STR_END, 0));
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, exact);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+ }
+else if (exact == 1)
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, TRUE);
+
+if (early_fail_type == type_fail_range)
+ {
+ /* Range end first, followed by range start. */
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + SSIZE_OF(sw));
+ OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, TMP2, 0);
+ OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, TMP2, 0);
+ add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_LESS_EQUAL, TMP2, 0, TMP1, 0));
+
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr + SSIZE_OF(sw), STR_PTR, 0);
+ }
+
+switch(opcode)
+ {
+ case OP_STAR:
+ case OP_UPTO:
+ SLJIT_ASSERT(early_fail_ptr == 0 || opcode == OP_STAR);
+
+ if (type == OP_ANYNL || type == OP_EXTUNI)
+ {
+ SLJIT_ASSERT(private_data_ptr == 0);
+ SLJIT_ASSERT(early_fail_ptr == 0);
+
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0);
+
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, SLJIT_IMM, max);
+
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &BACKTRACK_AS(char_iterator_backtrack)->u.backtracks, TRUE);
+ if (opcode == OP_UPTO)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ jump = JUMP(SLJIT_ZERO);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE0, TMP1, 0);
+ }
+
+ /* We cannot use TMP3 because of allocate_stack. */
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ if (jump != NULL)
+ JUMPHERE(jump);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+ }
+#ifdef SUPPORT_UNICODE
+ else if (type == OP_ALLANY && !common->invalid_utf)
+#else
+ else if (type == OP_ALLANY)
+#endif
+ {
+ if (opcode == OP_STAR)
+ {
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+
+ OP1(SLJIT_MOV, base, offset0, STR_END, 0);
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+
+ OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0);
+ process_partial_match(common);
+
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_END, 0);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+ }
+#ifdef SUPPORT_UNICODE
+ else if (!common->utf)
+#else
+ else
+#endif
+ {
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(max));
+
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+ else
+ {
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, STR_END, 0);
+ process_partial_match(common);
+ JUMPHERE(jump);
+ }
+
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+ }
+ }
+
+ charpos_enabled = FALSE;
+ charpos_char = 0;
+ charpos_othercasebit = 0;
+
+ if ((type != OP_CHAR && type != OP_CHARI) && (*end == OP_CHAR || *end == OP_CHARI))
+ {
+#ifdef SUPPORT_UNICODE
+ charpos_enabled = !common->utf || !HAS_EXTRALEN(end[1]);
+#else
+ charpos_enabled = TRUE;
+#endif
+ if (charpos_enabled && *end == OP_CHARI && char_has_othercase(common, end + 1))
+ {
+ charpos_othercasebit = char_get_othercase_bit(common, end + 1);
+ if (charpos_othercasebit == 0)
+ charpos_enabled = FALSE;
+ }
+
+ if (charpos_enabled)
+ {
+ charpos_char = end[1];
+ /* Consume the OP_CHAR opcode. */
+ end += 2;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ SLJIT_ASSERT((charpos_othercasebit >> 8) == 0);
+#elif PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ SLJIT_ASSERT((charpos_othercasebit >> 9) == 0);
+ if ((charpos_othercasebit & 0x100) != 0)
+ charpos_othercasebit = (charpos_othercasebit & 0xff) << 8;
+#endif
+ if (charpos_othercasebit != 0)
+ charpos_char |= charpos_othercasebit;
+
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.enabled = TRUE;
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.chr = charpos_char;
+ BACKTRACK_AS(char_iterator_backtrack)->u.charpos.othercasebit = charpos_othercasebit;
+ }
+ }
+
+ if (charpos_enabled)
+ {
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max + 1);
+
+ /* Search the first instance of charpos_char. */
+ jump = JUMP(SLJIT_JUMP);
+ label = LABEL();
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_ZERO));
+ }
+ compile_char1_matchingpath(common, type, cc, &backtrack->topbacktracks, FALSE);
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+
+ detect_partial_match(common, &backtrack->topbacktracks);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (charpos_othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, charpos_othercasebit);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char, label);
+
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ }
+
+ /* Search the last instance of charpos_char. */
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, FALSE);
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ detect_partial_match(common, &no_match);
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0));
+ if (charpos_othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, charpos_othercasebit);
+
+ if (opcode == OP_STAR)
+ {
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char, label);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, label);
+ }
+ else
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, charpos_char);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPHERE(jump);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ JUMPTO(SLJIT_NOT_ZERO, label);
+ }
+
+ set_jumps(no_match, LABEL());
+ OP2(SLJIT_ADD, STR_PTR, 0, base, offset0, SLJIT_IMM, IN_UCHARS(1));
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ }
+ else
+ {
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+
+ OP1(SLJIT_MOV, base, offset1, STR_PTR, 0);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ use_tmp = (!HAS_VIRTUAL_REGISTERS && opcode == OP_STAR);
+ SLJIT_ASSERT(!use_tmp || tmp_base == TMP3);
+
+ if (common->utf)
+ OP1(SLJIT_MOV, use_tmp ? TMP3 : base, use_tmp ? 0 : offset0, STR_PTR, 0);
+#endif
+ if (opcode == OP_UPTO)
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ OP1(SLJIT_MOV, use_tmp ? TMP3 : base, use_tmp ? 0 : offset0, STR_PTR, 0);
+#endif
+
+ if (opcode == OP_UPTO)
+ {
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ }
+
+ detect_partial_match_to(common, label);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ set_jumps(no_char1_match, LABEL());
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ {
+ set_jumps(no_match, LABEL());
+ if (use_tmp)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+ OP1(SLJIT_MOV, base, offset0, TMP3, 0);
+ }
+ else
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ }
+ else
+#endif
+ {
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ }
+
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ }
+
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+
+ case OP_MINSTAR:
+ if (private_data_ptr == 0)
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ break;
+
+ case OP_MINUPTO:
+ SLJIT_ASSERT(early_fail_ptr == 0);
+ if (private_data_ptr == 0)
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ OP1(SLJIT_MOV, base, offset1, SLJIT_IMM, max + 1);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+
+ case OP_QUERY:
+ case OP_MINQUERY:
+ SLJIT_ASSERT(early_fail_ptr == 0);
+ if (private_data_ptr == 0)
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ if (opcode == OP_QUERY)
+ compile_char1_matchingpath(common, type, cc, &BACKTRACK_AS(char_iterator_backtrack)->u.backtracks, TRUE);
+ BACKTRACK_AS(char_iterator_backtrack)->matchingpath = LABEL();
+ break;
+
+ case OP_EXACT:
+ break;
+
+ case OP_POSSTAR:
+#if defined SUPPORT_UNICODE
+ if (type == OP_ALLANY && !common->invalid_utf)
+#else
+ if (type == OP_ALLANY)
+#endif
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, STR_END, 0);
+ process_partial_match(common);
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_END, 0);
+ break;
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ {
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, FALSE);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ detect_partial_match_to(common, label);
+
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset);
+ if (early_fail_ptr != 0)
+ {
+ if (!HAS_VIRTUAL_REGISTERS && tmp_base == TMP3)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, TMP3, 0);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ }
+ break;
+ }
+#endif
+
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
+ detect_partial_match_to(common, label);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ set_jumps(no_char1_match, LABEL());
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ if (early_fail_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), early_fail_ptr, STR_PTR, 0);
+ break;
+
+ case OP_POSUPTO:
+ SLJIT_ASSERT(early_fail_ptr == 0);
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_match, FALSE);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1, STR_PTR, 0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ detect_partial_match_to(common, label);
+
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), POSSESSIVE1);
+ break;
+ }
+#endif
+
+ if (type == OP_ALLANY)
+ {
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(max));
+
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ {
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+ else
+ {
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, STR_END, 0);
+ process_partial_match(common);
+ JUMPHERE(jump);
+ }
+ break;
+ }
+
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, SLJIT_IMM, max);
+
+ detect_partial_match(common, &no_match);
+ label = LABEL();
+ compile_char1_matchingpath(common, type, cc, &no_char1_match, FALSE);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, tmp_base, tmp_offset, tmp_base, tmp_offset, SLJIT_IMM, 1);
+ add_jump(compiler, &no_match, JUMP(SLJIT_ZERO));
+ detect_partial_match_to(common, label);
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ set_jumps(no_char1_match, LABEL());
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ set_jumps(no_match, LABEL());
+ break;
+
+ case OP_POSQUERY:
+ SLJIT_ASSERT(early_fail_ptr == 0);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ compile_char1_matchingpath(common, type, cc, &no_match, TRUE);
+ OP1(SLJIT_MOV, tmp_base, tmp_offset, STR_PTR, 0);
+ set_jumps(no_match, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, tmp_base, tmp_offset);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+count_match(common);
+return end;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_fail_accept_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+
+PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL);
+
+if (*cc == OP_FAIL)
+ {
+ add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP));
+ return cc + 1;
+ }
+
+if (*cc == OP_ACCEPT && common->currententry == NULL && (common->re->overall_options & PCRE2_ENDANCHORED) != 0)
+ add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0));
+
+if (*cc == OP_ASSERT_ACCEPT || common->currententry != NULL || !common->might_be_empty)
+ {
+ /* No need to check notempty conditions. */
+ if (common->accept_label == NULL)
+ add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP));
+ else
+ JUMPTO(SLJIT_JUMP, common->accept_label);
+ return cc + 1;
+ }
+
+if (common->accept_label == NULL)
+ add_jump(compiler, &common->accept, CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0)));
+else
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), common->accept_label);
+
+if (HAS_VIRTUAL_REGISTERS)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options));
+ }
+else
+ OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, options));
+
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY);
+add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_NOT_ZERO));
+OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART);
+if (common->accept_label == NULL)
+ add_jump(compiler, &common->accept, JUMP(SLJIT_ZERO));
+else
+ JUMPTO(SLJIT_ZERO, common->accept_label);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, str));
+if (common->accept_label == NULL)
+ add_jump(compiler, &common->accept, CMP(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0));
+else
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, common->accept_label);
+add_jump(compiler, &backtrack->topbacktracks, JUMP(SLJIT_JUMP));
+return cc + 1;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_close_matchingpath(compiler_common *common, PCRE2_SPTR cc)
+{
+DEFINE_COMPILER;
+int offset = GET2(cc, 1);
+BOOL optimized_cbracket = common->optimized_cbracket[offset] != 0;
+
+/* Data will be discarded anyway... */
+if (common->currententry != NULL)
+ return cc + 1 + IMM2_SIZE;
+
+if (!optimized_cbracket)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR_PRIV(offset));
+offset <<= 1;
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+if (!optimized_cbracket)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+return cc + 1 + IMM2_SIZE;
+}
+
+static SLJIT_INLINE PCRE2_SPTR compile_control_verb_matchingpath(compiler_common *common, PCRE2_SPTR cc, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+PCRE2_UCHAR opcode = *cc;
+PCRE2_SPTR ccend = cc + 1;
+
+if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG ||
+ opcode == OP_SKIP_ARG || opcode == OP_THEN_ARG)
+ ccend += 2 + cc[1];
+
+PUSH_BACKTRACK(sizeof(backtrack_common), cc, NULL);
+
+if (opcode == OP_SKIP)
+ {
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ return ccend;
+ }
+
+if (opcode == OP_COMMIT_ARG || opcode == OP_PRUNE_ARG || opcode == OP_THEN_ARG)
+ {
+ if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0);
+ }
+
+return ccend;
+}
+
+static PCRE2_UCHAR then_trap_opcode[1] = { OP_THEN_TRAP };
+
+static SLJIT_INLINE void compile_then_trap_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+BOOL needs_control_head;
+int size;
+
+PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc);
+common->then_trap = BACKTRACK_AS(then_trap_backtrack);
+BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode;
+BACKTRACK_AS(then_trap_backtrack)->start = (sljit_sw)(cc - common->start);
+BACKTRACK_AS(then_trap_backtrack)->framesize = get_framesize(common, cc, ccend, FALSE, &needs_control_head);
+
+size = BACKTRACK_AS(then_trap_backtrack)->framesize;
+size = 3 + (size < 0 ? 0 : size);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+allocate_stack(common, size);
+if (size > 3)
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0, SLJIT_IMM, (size - 3) * sizeof(sljit_sw));
+else
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 1), SLJIT_IMM, BACKTRACK_AS(then_trap_backtrack)->start);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 2), SLJIT_IMM, type_then_trap);
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(size - 3), TMP2, 0);
+
+size = BACKTRACK_AS(then_trap_backtrack)->framesize;
+if (size >= 0)
+ init_frame(common, cc, ccend, size - 1, 0);
+}
+
+static void compile_matchingpath(compiler_common *common, PCRE2_SPTR cc, PCRE2_SPTR ccend, backtrack_common *parent)
+{
+DEFINE_COMPILER;
+backtrack_common *backtrack;
+BOOL has_then_trap = FALSE;
+then_trap_backtrack *save_then_trap = NULL;
+
+SLJIT_ASSERT(*ccend == OP_END || (*ccend >= OP_ALT && *ccend <= OP_KETRPOS));
+
+if (common->has_then && common->then_offsets[cc - common->start] != 0)
+ {
+ SLJIT_ASSERT(*ccend != OP_END && common->control_head_ptr != 0);
+ has_then_trap = TRUE;
+ save_then_trap = common->then_trap;
+ /* Tail item on backtrack. */
+ compile_then_trap_matchingpath(common, cc, ccend, parent);
+ }
+
+while (cc < ccend)
+ {
+ switch(*cc)
+ {
+ case OP_SOD:
+ case OP_SOM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ case OP_EODN:
+ case OP_EOD:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_REVERSE:
+ cc = compile_simple_assertion_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ break;
+
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ case OP_NOTPROP:
+ case OP_PROP:
+ case OP_ANYNL:
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ case OP_EXTUNI:
+ case OP_NOT:
+ case OP_NOTI:
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
+ break;
+
+ case OP_SET_SOM:
+ PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+ cc++;
+ break;
+
+ case OP_CHAR:
+ case OP_CHARI:
+ if (common->mode == PCRE2_JIT_COMPLETE)
+ cc = compile_charn_matchingpath(common, cc, ccend, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ else
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
+ break;
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+ cc = compile_iterator_matchingpath(common, cc, parent);
+ break;
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ if (cc[1 + (32 / sizeof(PCRE2_UCHAR))] >= OP_CRSTAR && cc[1 + (32 / sizeof(PCRE2_UCHAR))] <= OP_CRPOSRANGE)
+ cc = compile_iterator_matchingpath(common, cc, parent);
+ else
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
+ break;
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH == 16 || PCRE2_CODE_UNIT_WIDTH == 32
+ case OP_XCLASS:
+ if (*(cc + GET(cc, 1)) >= OP_CRSTAR && *(cc + GET(cc, 1)) <= OP_CRPOSRANGE)
+ cc = compile_iterator_matchingpath(common, cc, parent);
+ else
+ cc = compile_char1_matchingpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE);
+ break;
+#endif
+
+ case OP_REF:
+ case OP_REFI:
+ if (cc[1 + IMM2_SIZE] >= OP_CRSTAR && cc[1 + IMM2_SIZE] <= OP_CRPOSRANGE)
+ cc = compile_ref_iterator_matchingpath(common, cc, parent);
+ else
+ {
+ compile_ref_matchingpath(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE, FALSE);
+ cc += 1 + IMM2_SIZE;
+ }
+ break;
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ if (cc[1 + 2 * IMM2_SIZE] >= OP_CRSTAR && cc[1 + 2 * IMM2_SIZE] <= OP_CRPOSRANGE)
+ cc = compile_ref_iterator_matchingpath(common, cc, parent);
+ else
+ {
+ compile_dnref_search(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks);
+ compile_ref_matchingpath(common, cc, parent->top != NULL ? &parent->top->nextbacktracks : &parent->topbacktracks, TRUE, FALSE);
+ cc += 1 + 2 * IMM2_SIZE;
+ }
+ break;
+
+ case OP_RECURSE:
+ cc = compile_recurse_matchingpath(common, cc, parent);
+ break;
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ cc = compile_callout_matchingpath(common, cc, parent);
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ PUSH_BACKTRACK_NOVALUE(sizeof(assert_backtrack), cc);
+ cc = compile_assert_matchingpath(common, cc, BACKTRACK_AS(assert_backtrack), FALSE);
+ break;
+
+ case OP_BRAMINZERO:
+ PUSH_BACKTRACK_NOVALUE(sizeof(braminzero_backtrack), cc);
+ cc = bracketend(cc + 1);
+ if (*(cc - 1 - LINK_SIZE) != OP_KETRMIN)
+ {
+ allocate_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+ }
+ else
+ {
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), STR_PTR, 0);
+ }
+ BACKTRACK_AS(braminzero_backtrack)->matchingpath = LABEL();
+ count_match(common);
+ break;
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRA:
+ case OP_CBRA:
+ case OP_COND:
+ case OP_SBRA:
+ case OP_SCBRA:
+ case OP_SCOND:
+ cc = compile_bracket_matchingpath(common, cc, parent);
+ break;
+
+ case OP_BRAZERO:
+ if (cc[1] > OP_ASSERTBACK_NOT)
+ cc = compile_bracket_matchingpath(common, cc, parent);
+ else
+ {
+ PUSH_BACKTRACK_NOVALUE(sizeof(assert_backtrack), cc);
+ cc = compile_assert_matchingpath(common, cc, BACKTRACK_AS(assert_backtrack), FALSE);
+ }
+ break;
+
+ case OP_BRAPOS:
+ case OP_CBRAPOS:
+ case OP_SBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_BRAPOSZERO:
+ cc = compile_bracketpos_matchingpath(common, cc, parent);
+ break;
+
+ case OP_MARK:
+ PUSH_BACKTRACK_NOVALUE(sizeof(backtrack_common), cc);
+ SLJIT_ASSERT(common->mark_ptr != 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->mark_ptr);
+ allocate_stack(common, common->has_skip_arg ? 5 : 1);
+ if (HAS_VIRTUAL_REGISTERS)
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(common->has_skip_arg ? 4 : 0), TMP2, 0);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_sw)(cc + 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(HAS_VIRTUAL_REGISTERS ? TMP1 : ARGUMENTS), SLJIT_OFFSETOF(jit_arguments, mark_ptr), TMP2, 0);
+ if (common->has_skip_arg)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, STACK_TOP, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, type_mark);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), SLJIT_IMM, (sljit_sw)(cc + 2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(3), STR_PTR, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0);
+ }
+ cc += 1 + 2 + cc[1];
+ break;
+
+ case OP_PRUNE:
+ case OP_PRUNE_ARG:
+ case OP_SKIP:
+ case OP_SKIP_ARG:
+ case OP_THEN:
+ case OP_THEN_ARG:
+ case OP_COMMIT:
+ case OP_COMMIT_ARG:
+ cc = compile_control_verb_matchingpath(common, cc, parent);
+ break;
+
+ case OP_FAIL:
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ cc = compile_fail_accept_matchingpath(common, cc, parent);
+ break;
+
+ case OP_CLOSE:
+ cc = compile_close_matchingpath(common, cc);
+ break;
+
+ case OP_SKIPZERO:
+ cc = bracketend(cc + 1);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ return;
+ }
+ if (cc == NULL)
+ return;
+ }
+
+if (has_then_trap)
+ {
+ /* Head item on backtrack. */
+ PUSH_BACKTRACK_NOVALUE(sizeof(then_trap_backtrack), cc);
+ BACKTRACK_AS(then_trap_backtrack)->common.cc = then_trap_opcode;
+ BACKTRACK_AS(then_trap_backtrack)->then_trap = common->then_trap;
+ common->then_trap = save_then_trap;
+ }
+SLJIT_ASSERT(cc == ccend);
+}
+
+#undef PUSH_BACKTRACK
+#undef PUSH_BACKTRACK_NOVALUE
+#undef BACKTRACK_AS
+
+#define COMPILE_BACKTRACKINGPATH(current) \
+ do \
+ { \
+ compile_backtrackingpath(common, (current)); \
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \
+ return; \
+ } \
+ while (0)
+
+#define CURRENT_AS(type) ((type *)current)
+
+static void compile_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+PCRE2_SPTR cc = current->cc;
+PCRE2_UCHAR opcode;
+PCRE2_UCHAR type;
+sljit_u32 max = 0, exact;
+struct sljit_label *label = NULL;
+struct sljit_jump *jump = NULL;
+jump_list *jumplist = NULL;
+PCRE2_SPTR end;
+int private_data_ptr = PRIVATE_DATA(cc);
+int base = (private_data_ptr == 0) ? SLJIT_MEM1(STACK_TOP) : SLJIT_MEM1(SLJIT_SP);
+int offset0 = (private_data_ptr == 0) ? STACK(0) : private_data_ptr;
+int offset1 = (private_data_ptr == 0) ? STACK(1) : private_data_ptr + SSIZE_OF(sw);
+
+cc = get_iterator_parameters(common, cc, &opcode, &type, &max, &exact, &end);
+
+switch(opcode)
+ {
+ case OP_STAR:
+ case OP_UPTO:
+ if (type == OP_ANYNL || type == OP_EXTUNI)
+ {
+ SLJIT_ASSERT(private_data_ptr == 0);
+ set_jumps(CURRENT_AS(char_iterator_backtrack)->u.backtracks, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ }
+ else
+ {
+ if (CURRENT_AS(char_iterator_backtrack)->u.charpos.enabled)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP1(SLJIT_MOV, TMP2, 0, base, offset1);
+ OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, TMP2, 0);
+ label = LABEL();
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1));
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ if (CURRENT_AS(char_iterator_backtrack)->u.charpos.othercasebit != 0)
+ OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, CURRENT_AS(char_iterator_backtrack)->u.charpos.othercasebit);
+ CMPTO(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, CURRENT_AS(char_iterator_backtrack)->u.charpos.chr, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ move_back(common, NULL, TRUE);
+ CMPTO(SLJIT_GREATER, STR_PTR, 0, TMP2, 0, label);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ jump = CMP(SLJIT_LESS_EQUAL, STR_PTR, 0, base, offset1);
+ move_back(common, NULL, TRUE);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ }
+ JUMPHERE(jump);
+ if (private_data_ptr == 0)
+ free_stack(common, 2);
+ }
+ break;
+
+ case OP_MINSTAR:
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ set_jumps(jumplist, LABEL());
+ if (private_data_ptr == 0)
+ free_stack(common, 1);
+ break;
+
+ case OP_MINUPTO:
+ OP1(SLJIT_MOV, TMP1, 0, base, offset1);
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP2(SLJIT_SUB | SLJIT_SET_Z, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+ add_jump(compiler, &jumplist, JUMP(SLJIT_ZERO));
+
+ OP1(SLJIT_MOV, base, offset1, TMP1, 0);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
+ OP1(SLJIT_MOV, base, offset0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+
+ set_jumps(jumplist, LABEL());
+ if (private_data_ptr == 0)
+ free_stack(common, 2);
+ break;
+
+ case OP_QUERY:
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ jump = JUMP(SLJIT_JUMP);
+ set_jumps(CURRENT_AS(char_iterator_backtrack)->u.backtracks, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ JUMPHERE(jump);
+ if (private_data_ptr == 0)
+ free_stack(common, 1);
+ break;
+
+ case OP_MINQUERY:
+ OP1(SLJIT_MOV, STR_PTR, 0, base, offset0);
+ OP1(SLJIT_MOV, base, offset0, SLJIT_IMM, 0);
+ jump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ compile_char1_matchingpath(common, type, cc, &jumplist, TRUE);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(char_iterator_backtrack)->matchingpath);
+ set_jumps(jumplist, LABEL());
+ JUMPHERE(jump);
+ if (private_data_ptr == 0)
+ free_stack(common, 1);
+ break;
+
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+set_jumps(current->topbacktracks, LABEL());
+}
+
+static SLJIT_INLINE void compile_ref_iterator_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+PCRE2_SPTR cc = current->cc;
+BOOL ref = (*cc == OP_REF || *cc == OP_REFI);
+PCRE2_UCHAR type;
+
+type = cc[ref ? 1 + IMM2_SIZE : 1 + 2 * IMM2_SIZE];
+
+if ((type & 0x1) == 0)
+ {
+ /* Maximize case. */
+ set_jumps(current->topbacktracks, LABEL());
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(ref_iterator_backtrack)->matchingpath);
+ return;
+ }
+
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(ref_iterator_backtrack)->matchingpath);
+set_jumps(current->topbacktracks, LABEL());
+free_stack(common, ref ? 2 : 3);
+}
+
+static SLJIT_INLINE void compile_recurse_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+recurse_entry *entry;
+
+if (!CURRENT_AS(recurse_backtrack)->inlined_pattern)
+ {
+ entry = CURRENT_AS(recurse_backtrack)->entry;
+ if (entry->backtrack_label == NULL)
+ add_jump(compiler, &entry->backtrack_calls, JUMP(SLJIT_FAST_CALL));
+ else
+ JUMPTO(SLJIT_FAST_CALL, entry->backtrack_label);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(recurse_backtrack)->matchingpath);
+ }
+else
+ compile_backtrackingpath(common, current->top);
+
+set_jumps(current->topbacktracks, LABEL());
+}
+
+static void compile_assert_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+PCRE2_SPTR cc = current->cc;
+PCRE2_UCHAR bra = OP_BRA;
+struct sljit_jump *brajump = NULL;
+
+SLJIT_ASSERT(*cc != OP_BRAMINZERO);
+if (*cc == OP_BRAZERO)
+ {
+ bra = *cc;
+ cc++;
+ }
+
+if (bra == OP_BRAZERO)
+ {
+ SLJIT_ASSERT(current->topbacktracks == NULL);
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ }
+
+if (CURRENT_AS(assert_backtrack)->framesize < 0)
+ {
+ set_jumps(current->topbacktracks, LABEL());
+
+ if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_backtrack)->matchingpath);
+ free_stack(common, 1);
+ }
+ return;
+ }
+
+if (bra == OP_BRAZERO)
+ {
+ if (*cc == OP_ASSERT_NOT || *cc == OP_ASSERTBACK_NOT)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_backtrack)->matchingpath);
+ free_stack(common, 1);
+ return;
+ }
+ free_stack(common, 1);
+ brajump = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0);
+ }
+
+if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(assert_backtrack)->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(assert_backtrack)->private_data_ptr, TMP1, 0);
+
+ set_jumps(current->topbacktracks, LABEL());
+ }
+else
+ set_jumps(current->topbacktracks, LABEL());
+
+if (bra == OP_BRAZERO)
+ {
+ /* We know there is enough place on the stack. */
+ OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0);
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_backtrack)->matchingpath);
+ JUMPHERE(brajump);
+ }
+}
+
+static void compile_bracket_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+int opcode, stacksize, alt_count, alt_max;
+int offset = 0;
+int private_data_ptr = CURRENT_AS(bracket_backtrack)->private_data_ptr;
+int repeat_ptr = 0, repeat_type = 0, repeat_count = 0;
+PCRE2_SPTR cc = current->cc;
+PCRE2_SPTR ccbegin;
+PCRE2_SPTR ccprev;
+PCRE2_UCHAR bra = OP_BRA;
+PCRE2_UCHAR ket;
+assert_backtrack *assert;
+BOOL has_alternatives;
+BOOL needs_control_head = FALSE;
+struct sljit_jump *brazero = NULL;
+struct sljit_jump *next_alt = NULL;
+struct sljit_jump *once = NULL;
+struct sljit_jump *cond = NULL;
+struct sljit_label *rmin_label = NULL;
+struct sljit_label *exact_label = NULL;
+struct sljit_put_label *put_label = NULL;
+
+if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO)
+ {
+ bra = *cc;
+ cc++;
+ }
+
+opcode = *cc;
+ccbegin = bracketend(cc) - 1 - LINK_SIZE;
+ket = *ccbegin;
+if (ket == OP_KET && PRIVATE_DATA(ccbegin) != 0)
+ {
+ repeat_ptr = PRIVATE_DATA(ccbegin);
+ repeat_type = PRIVATE_DATA(ccbegin + 2);
+ repeat_count = PRIVATE_DATA(ccbegin + 3);
+ SLJIT_ASSERT(repeat_type != 0 && repeat_count != 0);
+ if (repeat_type == OP_UPTO)
+ ket = OP_KETRMAX;
+ if (repeat_type == OP_MINUPTO)
+ ket = OP_KETRMIN;
+ }
+ccbegin = cc;
+cc += GET(cc, 1);
+has_alternatives = *cc == OP_ALT;
+if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND))
+ has_alternatives = (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT) || CURRENT_AS(bracket_backtrack)->u.condfailed != NULL;
+if (opcode == OP_CBRA || opcode == OP_SCBRA)
+ offset = (GET2(ccbegin, 1 + LINK_SIZE)) << 1;
+if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN))
+ opcode = OP_SCOND;
+
+alt_max = has_alternatives ? no_alternatives(ccbegin) : 0;
+
+/* Decoding the needs_control_head in framesize. */
+if (opcode == OP_ONCE)
+ {
+ needs_control_head = (CURRENT_AS(bracket_backtrack)->u.framesize & 0x1) != 0;
+ CURRENT_AS(bracket_backtrack)->u.framesize >>= 1;
+ }
+
+if (ket != OP_KET && repeat_type != 0)
+ {
+ /* TMP1 is used in OP_KETRMIN below. */
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ if (repeat_type == OP_UPTO)
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0, SLJIT_IMM, 1);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0);
+ }
+
+if (ket == OP_KETRMAX)
+ {
+ if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ brazero = CMP(SLJIT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+ }
+else if (ket == OP_KETRMIN)
+ {
+ if (bra != OP_BRAMINZERO)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (repeat_type != 0)
+ {
+ /* TMP1 was set a few lines above. */
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ /* Drop STR_PTR for non-greedy plus quantifier. */
+ if (opcode != OP_ONCE)
+ free_stack(common, 1);
+ }
+ else if (opcode >= OP_SBRA || opcode == OP_ONCE)
+ {
+ /* Checking zero-length iteration. */
+ if (opcode != OP_ONCE || CURRENT_AS(bracket_backtrack)->u.framesize < 0)
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr, CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 2), CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ }
+ /* Drop STR_PTR for non-greedy plus quantifier. */
+ if (opcode != OP_ONCE)
+ free_stack(common, 1);
+ }
+ else
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ }
+ rmin_label = LABEL();
+ if (repeat_type != 0)
+ OP2(SLJIT_ADD, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ }
+else if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ brazero = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+else if (repeat_type == OP_EXACT)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ exact_label = LABEL();
+ }
+
+if (offset != 0)
+ {
+ if (common->capture_last_ptr != 0)
+ {
+ SLJIT_ASSERT(common->optimized_cbracket[offset >> 1] == 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, TMP1, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(2));
+ free_stack(common, 3);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP2, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP1, 0);
+ }
+ else if (common->optimized_cbracket[offset >> 1] == 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ free_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0);
+ }
+ }
+
+if (SLJIT_UNLIKELY(opcode == OP_ONCE))
+ {
+ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracket_backtrack)->u.framesize - 1) * sizeof(sljit_sw));
+ }
+ once = JUMP(SLJIT_JUMP);
+ }
+else if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND))
+ {
+ if (has_alternatives)
+ {
+ /* Always exactly one alternative. */
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+
+ alt_max = 2;
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+ }
+else if (has_alternatives)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+
+ if (alt_max > 3)
+ {
+ sljit_emit_ijump(compiler, SLJIT_JUMP, TMP1, 0);
+
+ SLJIT_ASSERT(CURRENT_AS(bracket_backtrack)->u.matching_put_label);
+ sljit_set_put_label(CURRENT_AS(bracket_backtrack)->u.matching_put_label, LABEL());
+ sljit_emit_op0(compiler, SLJIT_ENDBR);
+ }
+ else
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+
+COMPILE_BACKTRACKINGPATH(current->top);
+if (current->topbacktracks)
+ set_jumps(current->topbacktracks, LABEL());
+
+if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND))
+ {
+ /* Conditional block always has at most one alternative. */
+ if (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT)
+ {
+ SLJIT_ASSERT(has_alternatives);
+ assert = CURRENT_AS(bracket_backtrack)->u.assert;
+ if (assert->framesize >= 0 && (ccbegin[1 + LINK_SIZE] == OP_ASSERT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK))
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0);
+ }
+ cond = JUMP(SLJIT_JUMP);
+ set_jumps(CURRENT_AS(bracket_backtrack)->u.assert->condfailed, LABEL());
+ }
+ else if (CURRENT_AS(bracket_backtrack)->u.condfailed != NULL)
+ {
+ SLJIT_ASSERT(has_alternatives);
+ cond = JUMP(SLJIT_JUMP);
+ set_jumps(CURRENT_AS(bracket_backtrack)->u.condfailed, LABEL());
+ }
+ else
+ SLJIT_ASSERT(!has_alternatives);
+ }
+
+if (has_alternatives)
+ {
+ alt_count = 1;
+ do
+ {
+ current->top = NULL;
+ current->topbacktracks = NULL;
+ current->nextbacktracks = NULL;
+ /* Conditional blocks always have an additional alternative, even if it is empty. */
+ if (*cc == OP_ALT)
+ {
+ ccprev = cc + 1 + LINK_SIZE;
+ cc += GET(cc, 1);
+ if (opcode != OP_COND && opcode != OP_SCOND)
+ {
+ if (opcode != OP_ONCE)
+ {
+ if (private_data_ptr != 0)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+ else
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ }
+ else
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(needs_control_head ? 1 : 0));
+ }
+ compile_matchingpath(common, ccprev, cc, current);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return;
+
+ if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), private_data_ptr);
+
+ if (opcode == OP_SCRIPT_RUN)
+ match_script_run_common(common, private_data_ptr, current);
+ }
+
+ /* Instructions after the current alternative is successfully matched. */
+ /* There is a similar code in compile_bracket_matchingpath. */
+ if (opcode == OP_ONCE)
+ match_once_common(common, ket, CURRENT_AS(bracket_backtrack)->u.framesize, private_data_ptr, has_alternatives, needs_control_head);
+
+ stacksize = 0;
+ if (repeat_type == OP_MINUPTO)
+ {
+ /* We need to preserve the counter. TMP2 will be used below. */
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr);
+ stacksize++;
+ }
+ if (ket != OP_KET || bra != OP_BRA)
+ stacksize++;
+ if (offset != 0)
+ {
+ if (common->capture_last_ptr != 0)
+ stacksize++;
+ if (common->optimized_cbracket[offset >> 1] == 0)
+ stacksize += 2;
+ }
+ if (opcode != OP_ONCE)
+ stacksize++;
+
+ if (stacksize > 0)
+ allocate_stack(common, stacksize);
+
+ stacksize = 0;
+ if (repeat_type == OP_MINUPTO)
+ {
+ /* TMP2 was set above. */
+ OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(stacksize), TMP2, 0, SLJIT_IMM, 1);
+ stacksize++;
+ }
+
+ if (ket != OP_KET || bra != OP_BRA)
+ {
+ if (ket != OP_KET)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0);
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0);
+ stacksize++;
+ }
+
+ if (offset != 0)
+ stacksize = match_capture_common(common, stacksize, offset, private_data_ptr);
+
+ if (opcode != OP_ONCE)
+ {
+ if (alt_max <= 3)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, alt_count);
+ else
+ put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(stacksize));
+ }
+
+ if (offset != 0 && ket == OP_KETRMAX && common->optimized_cbracket[offset >> 1] != 0)
+ {
+ /* If ket is not OP_KETRMAX, this code path is executed after the jump to alternative_matchingpath. */
+ SLJIT_ASSERT(private_data_ptr == OVECTOR(offset + 0));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), STR_PTR, 0);
+ }
+
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->alternative_matchingpath);
+
+ if (opcode != OP_ONCE)
+ {
+ if (alt_max <= 3)
+ {
+ JUMPHERE(next_alt);
+ alt_count++;
+ if (alt_count < alt_max)
+ {
+ SLJIT_ASSERT(alt_count == 2 && alt_max == 3);
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 1);
+ }
+ }
+ else
+ {
+ sljit_set_put_label(put_label, LABEL());
+ sljit_emit_op0(compiler, SLJIT_ENDBR);
+ }
+ }
+
+ COMPILE_BACKTRACKINGPATH(current->top);
+ if (current->topbacktracks)
+ set_jumps(current->topbacktracks, LABEL());
+ SLJIT_ASSERT(!current->nextbacktracks);
+ }
+ while (*cc == OP_ALT);
+
+ if (cond != NULL)
+ {
+ SLJIT_ASSERT(opcode == OP_COND || opcode == OP_SCOND);
+ assert = CURRENT_AS(bracket_backtrack)->u.assert;
+ if ((ccbegin[1 + LINK_SIZE] == OP_ASSERT_NOT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK_NOT) && assert->framesize >= 0)
+ {
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr);
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(-2));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (assert->framesize - 1) * sizeof(sljit_sw));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), assert->private_data_ptr, TMP1, 0);
+ }
+ JUMPHERE(cond);
+ }
+
+ /* Free the STR_PTR. */
+ if (private_data_ptr == 0)
+ free_stack(common, 1);
+ }
+
+if (offset != 0)
+ {
+ /* Using both tmp register is better for instruction scheduling. */
+ if (common->optimized_cbracket[offset >> 1] != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ free_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0);
+ }
+ else
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ }
+else if (opcode == OP_ASSERT_NA || opcode == OP_ASSERTBACK_NA || opcode == OP_SCRIPT_RUN || opcode == OP_SBRA || opcode == OP_SCOND)
+ {
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ }
+else if (opcode == OP_ONCE)
+ {
+ cc = ccbegin + GET(ccbegin, 1);
+ stacksize = needs_control_head ? 1 : 0;
+
+ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0)
+ {
+ /* Reset head and drop saved frame. */
+ stacksize += CURRENT_AS(bracket_backtrack)->u.framesize + ((ket != OP_KET || *cc == OP_ALT) ? 2 : 1);
+ }
+ else if (ket == OP_KETRMAX || (*cc == OP_ALT && ket != OP_KETRMIN))
+ {
+ /* The STR_PTR must be released. */
+ stacksize++;
+ }
+
+ if (stacksize > 0)
+ free_stack(common, stacksize);
+
+ JUMPHERE(once);
+ /* Restore previous private_data_ptr */
+ if (CURRENT_AS(bracket_backtrack)->u.framesize >= 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracket_backtrack)->u.framesize - 1));
+ else if (ket == OP_KETRMIN)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ /* See the comment below. */
+ free_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), private_data_ptr, TMP1, 0);
+ }
+ }
+
+if (repeat_type == OP_EXACT)
+ {
+ OP2(SLJIT_ADD, TMP1, 0, SLJIT_MEM1(SLJIT_SP), repeat_ptr, SLJIT_IMM, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), repeat_ptr, TMP1, 0);
+ CMPTO(SLJIT_LESS_EQUAL, TMP1, 0, SLJIT_IMM, repeat_count, exact_label);
+ }
+else if (ket == OP_KETRMAX)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ if (bra != OP_BRAZERO)
+ free_stack(common, 1);
+
+ CMPTO(SLJIT_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_backtrack)->recursive_matchingpath);
+ if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->zero_matchingpath);
+ JUMPHERE(brazero);
+ free_stack(common, 1);
+ }
+ }
+else if (ket == OP_KETRMIN)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ /* OP_ONCE removes everything in case of a backtrack, so we don't
+ need to explicitly release the STR_PTR. The extra release would
+ affect badly the free_stack(2) above. */
+ if (opcode != OP_ONCE)
+ free_stack(common, 1);
+ CMPTO(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, rmin_label);
+ if (opcode == OP_ONCE)
+ free_stack(common, bra == OP_BRAMINZERO ? 2 : 1);
+ else if (bra == OP_BRAMINZERO)
+ free_stack(common, 1);
+ }
+else if (bra == OP_BRAZERO)
+ {
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_backtrack)->zero_matchingpath);
+ JUMPHERE(brazero);
+ }
+}
+
+static SLJIT_INLINE void compile_bracketpos_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+int offset;
+struct sljit_jump *jump;
+
+if (CURRENT_AS(bracketpos_backtrack)->framesize < 0)
+ {
+ if (*current->cc == OP_CBRAPOS || *current->cc == OP_SCBRAPOS)
+ {
+ offset = (GET2(current->cc, 1 + LINK_SIZE)) << 1;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset), TMP1, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(2));
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(offset + 1), TMP2, 0);
+ if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, TMP1, 0);
+ }
+ set_jumps(current->topbacktracks, LABEL());
+ free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize);
+ return;
+ }
+
+OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr);
+add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(bracketpos_backtrack)->framesize - 1) * sizeof(sljit_sw));
+
+if (current->topbacktracks)
+ {
+ jump = JUMP(SLJIT_JUMP);
+ set_jumps(current->topbacktracks, LABEL());
+ /* Drop the stack frame. */
+ free_stack(common, CURRENT_AS(bracketpos_backtrack)->stacksize);
+ JUMPHERE(jump);
+ }
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), CURRENT_AS(bracketpos_backtrack)->private_data_ptr, SLJIT_MEM1(STACK_TOP), STACK(-CURRENT_AS(bracketpos_backtrack)->framesize - 1));
+}
+
+static SLJIT_INLINE void compile_braminzero_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+assert_backtrack backtrack;
+
+current->top = NULL;
+current->topbacktracks = NULL;
+current->nextbacktracks = NULL;
+if (current->cc[1] > OP_ASSERTBACK_NOT)
+ {
+ /* Manual call of compile_bracket_matchingpath and compile_bracket_backtrackingpath. */
+ compile_bracket_matchingpath(common, current->cc, current);
+ compile_bracket_backtrackingpath(common, current->top);
+ }
+else
+ {
+ memset(&backtrack, 0, sizeof(backtrack));
+ backtrack.common.cc = current->cc;
+ backtrack.matchingpath = CURRENT_AS(braminzero_backtrack)->matchingpath;
+ /* Manual call of compile_assert_matchingpath. */
+ compile_assert_matchingpath(common, current->cc, &backtrack, FALSE);
+ }
+SLJIT_ASSERT(!current->nextbacktracks && !current->topbacktracks);
+}
+
+static SLJIT_INLINE void compile_control_verb_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+PCRE2_UCHAR opcode = *current->cc;
+struct sljit_label *loop;
+struct sljit_jump *jump;
+
+if (opcode == OP_THEN || opcode == OP_THEN_ARG)
+ {
+ if (common->then_trap != NULL)
+ {
+ SLJIT_ASSERT(common->control_head_ptr != 0);
+
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, type_then_trap);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, common->then_trap->start);
+ jump = JUMP(SLJIT_JUMP);
+
+ loop = LABEL();
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ JUMPHERE(jump);
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0, loop);
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(2), TMP2, 0, loop);
+ add_jump(compiler, &common->then_trap->quit, JUMP(SLJIT_JUMP));
+ return;
+ }
+ else if (!common->local_quit_available && common->in_positive_assertion)
+ {
+ add_jump(compiler, &common->positive_assertion_quit, JUMP(SLJIT_JUMP));
+ return;
+ }
+ }
+
+if (common->local_quit_available)
+ {
+ /* Abort match with a fail. */
+ if (common->quit_label == NULL)
+ add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
+ else
+ JUMPTO(SLJIT_JUMP, common->quit_label);
+ return;
+ }
+
+if (opcode == OP_SKIP_ARG)
+ {
+ SLJIT_ASSERT(common->control_head_ptr != 0 && TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr);
+ OP1(SLJIT_MOV, SLJIT_R1, 0, SLJIT_IMM, (sljit_sw)(current->cc + 2));
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(do_search_mark));
+
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_R0, 0);
+ add_jump(compiler, &common->reset_match, CMP(SLJIT_NOT_EQUAL, SLJIT_R0, 0, SLJIT_IMM, 0));
+ return;
+ }
+
+if (opcode == OP_SKIP)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+else
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_IMM, 0);
+add_jump(compiler, &common->reset_match, JUMP(SLJIT_JUMP));
+}
+
+static SLJIT_INLINE void compile_then_trap_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+struct sljit_jump *jump;
+int size;
+
+if (CURRENT_AS(then_trap_backtrack)->then_trap)
+ {
+ common->then_trap = CURRENT_AS(then_trap_backtrack)->then_trap;
+ return;
+ }
+
+size = CURRENT_AS(then_trap_backtrack)->framesize;
+size = 3 + (size < 0 ? 0 : size);
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(size - 3));
+free_stack(common, size);
+jump = JUMP(SLJIT_JUMP);
+
+set_jumps(CURRENT_AS(then_trap_backtrack)->quit, LABEL());
+/* STACK_TOP is set by THEN. */
+if (CURRENT_AS(then_trap_backtrack)->framesize >= 0)
+ {
+ add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL));
+ OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (CURRENT_AS(then_trap_backtrack)->framesize - 1) * sizeof(sljit_sw));
+ }
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+free_stack(common, 3);
+
+JUMPHERE(jump);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP1, 0);
+}
+
+static void compile_backtrackingpath(compiler_common *common, struct backtrack_common *current)
+{
+DEFINE_COMPILER;
+then_trap_backtrack *save_then_trap = common->then_trap;
+
+while (current)
+ {
+ if (current->nextbacktracks != NULL)
+ set_jumps(current->nextbacktracks, LABEL());
+ switch(*current->cc)
+ {
+ case OP_SET_SOM:
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), TMP1, 0);
+ break;
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+ case OP_CLASS:
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+#endif
+ compile_iterator_backtrackingpath(common, current);
+ break;
+
+ case OP_REF:
+ case OP_REFI:
+ case OP_DNREF:
+ case OP_DNREFI:
+ compile_ref_iterator_backtrackingpath(common, current);
+ break;
+
+ case OP_RECURSE:
+ compile_recurse_backtrackingpath(common, current);
+ break;
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ compile_assert_backtrackingpath(common, current);
+ break;
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_BRA:
+ case OP_CBRA:
+ case OP_COND:
+ case OP_SBRA:
+ case OP_SCBRA:
+ case OP_SCOND:
+ compile_bracket_backtrackingpath(common, current);
+ break;
+
+ case OP_BRAZERO:
+ if (current->cc[1] > OP_ASSERTBACK_NOT)
+ compile_bracket_backtrackingpath(common, current);
+ else
+ compile_assert_backtrackingpath(common, current);
+ break;
+
+ case OP_BRAPOS:
+ case OP_CBRAPOS:
+ case OP_SBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_BRAPOSZERO:
+ compile_bracketpos_backtrackingpath(common, current);
+ break;
+
+ case OP_BRAMINZERO:
+ compile_braminzero_backtrackingpath(common, current);
+ break;
+
+ case OP_MARK:
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(common->has_skip_arg ? 4 : 0));
+ if (common->has_skip_arg)
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ free_stack(common, common->has_skip_arg ? 5 : 1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, TMP1, 0);
+ if (common->has_skip_arg)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, TMP2, 0);
+ break;
+
+ case OP_THEN:
+ case OP_THEN_ARG:
+ case OP_PRUNE:
+ case OP_PRUNE_ARG:
+ case OP_SKIP:
+ case OP_SKIP_ARG:
+ compile_control_verb_backtrackingpath(common, current);
+ break;
+
+ case OP_COMMIT:
+ case OP_COMMIT_ARG:
+ if (!common->local_quit_available)
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ if (common->quit_label == NULL)
+ add_jump(compiler, &common->quit, JUMP(SLJIT_JUMP));
+ else
+ JUMPTO(SLJIT_JUMP, common->quit_label);
+ break;
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ case OP_FAIL:
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ set_jumps(current->topbacktracks, LABEL());
+ break;
+
+ case OP_THEN_TRAP:
+ /* A virtual opcode for then traps. */
+ compile_then_trap_backtrackingpath(common, current);
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ current = current->prev;
+ }
+common->then_trap = save_then_trap;
+}
+
+static SLJIT_INLINE void compile_recurse(compiler_common *common)
+{
+DEFINE_COMPILER;
+PCRE2_SPTR cc = common->start + common->currententry->start;
+PCRE2_SPTR ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE);
+PCRE2_SPTR ccend = bracketend(cc) - (1 + LINK_SIZE);
+uint32_t recurse_flags = 0;
+int private_data_size = get_recurse_data_length(common, ccbegin, ccend, &recurse_flags);
+int alt_count, alt_max, local_size;
+backtrack_common altbacktrack;
+jump_list *match = NULL;
+struct sljit_jump *next_alt = NULL;
+struct sljit_jump *accept_exit = NULL;
+struct sljit_label *quit;
+struct sljit_put_label *put_label = NULL;
+
+/* Recurse captures then. */
+common->then_trap = NULL;
+
+SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS);
+
+alt_max = no_alternatives(cc);
+alt_count = 0;
+
+/* Matching path. */
+SLJIT_ASSERT(common->currententry->entry_label == NULL && common->recursive_head_ptr != 0);
+common->currententry->entry_label = LABEL();
+set_jumps(common->currententry->entry_calls, common->currententry->entry_label);
+
+sljit_emit_fast_enter(compiler, TMP2, 0);
+count_match(common);
+
+local_size = (alt_max > 1) ? 2 : 1;
+
+/* (Reversed) stack layout:
+ [private data][return address][optional: str ptr] ... [optional: alternative index][recursive_head_ptr] */
+
+allocate_stack(common, private_data_size + local_size);
+/* Save return address. */
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP2, 0);
+
+copy_recurse_data(common, ccbegin, ccend, recurse_copy_from_global, local_size, private_data_size + local_size, recurse_flags);
+
+/* This variable is saved and restored all time when we enter or exit from a recursive context. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr, STACK_TOP, 0);
+
+if (recurse_flags & recurse_flag_control_head_found)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+
+if (alt_max > 1)
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0);
+
+memset(&altbacktrack, 0, sizeof(backtrack_common));
+common->quit_label = NULL;
+common->accept_label = NULL;
+common->quit = NULL;
+common->accept = NULL;
+altbacktrack.cc = ccbegin;
+cc += GET(cc, 1);
+while (1)
+ {
+ altbacktrack.top = NULL;
+ altbacktrack.topbacktracks = NULL;
+
+ if (altbacktrack.cc != ccbegin)
+ OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+
+ compile_matchingpath(common, altbacktrack.cc, cc, &altbacktrack);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return;
+
+ allocate_stack(common, (alt_max > 1 || (recurse_flags & recurse_flag_accept_found)) ? 2 : 1);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
+
+ if (alt_max > 1 || (recurse_flags & recurse_flag_accept_found))
+ {
+ if (alt_max > 3)
+ put_label = sljit_emit_put_label(compiler, SLJIT_MEM1(STACK_TOP), STACK(1));
+ else
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, alt_count);
+ }
+
+ add_jump(compiler, &match, JUMP(SLJIT_JUMP));
+
+ if (alt_count == 0)
+ {
+ /* Backtracking path entry. */
+ SLJIT_ASSERT(common->currententry->backtrack_label == NULL);
+ common->currententry->backtrack_label = LABEL();
+ set_jumps(common->currententry->backtrack_calls, common->currententry->backtrack_label);
+
+ sljit_emit_fast_enter(compiler, TMP1, 0);
+
+ if (recurse_flags & recurse_flag_accept_found)
+ accept_exit = CMP(SLJIT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, -1);
+
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0));
+ /* Save return address. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), STACK(local_size - 1), TMP1, 0);
+
+ copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, recurse_flags);
+
+ if (alt_max > 1)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1));
+ free_stack(common, 2);
+
+ if (alt_max > 3)
+ {
+ sljit_emit_ijump(compiler, SLJIT_JUMP, TMP1, 0);
+ sljit_set_put_label(put_label, LABEL());
+ sljit_emit_op0(compiler, SLJIT_ENDBR);
+ }
+ else
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0);
+ }
+ else
+ free_stack(common, (recurse_flags & recurse_flag_accept_found) ? 2 : 1);
+ }
+ else if (alt_max > 3)
+ {
+ sljit_set_put_label(put_label, LABEL());
+ sljit_emit_op0(compiler, SLJIT_ENDBR);
+ }
+ else
+ {
+ JUMPHERE(next_alt);
+ if (alt_count + 1 < alt_max)
+ {
+ SLJIT_ASSERT(alt_count == 1 && alt_max == 3);
+ next_alt = CMP(SLJIT_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 1);
+ }
+ }
+
+ alt_count++;
+
+ compile_backtrackingpath(common, altbacktrack.top);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ return;
+ set_jumps(altbacktrack.topbacktracks, LABEL());
+
+ if (*cc != OP_ALT)
+ break;
+
+ altbacktrack.cc = cc + 1 + LINK_SIZE;
+ cc += GET(cc, 1);
+ }
+
+/* No alternative is matched. */
+
+quit = LABEL();
+
+copy_recurse_data(common, ccbegin, ccend, recurse_copy_private_to_global, local_size, private_data_size + local_size, recurse_flags);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1));
+free_stack(common, private_data_size + local_size);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+OP_SRC(SLJIT_FAST_RETURN, TMP2, 0);
+
+if (common->quit != NULL)
+ {
+ SLJIT_ASSERT(recurse_flags & recurse_flag_quit_found);
+
+ set_jumps(common->quit, LABEL());
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
+ copy_recurse_data(common, ccbegin, ccend, recurse_copy_shared_to_global, local_size, private_data_size + local_size, recurse_flags);
+ JUMPTO(SLJIT_JUMP, quit);
+ }
+
+if (recurse_flags & recurse_flag_accept_found)
+ {
+ JUMPHERE(accept_exit);
+ free_stack(common, 2);
+
+ /* Save return address. */
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1), TMP1, 0);
+
+ copy_recurse_data(common, ccbegin, ccend, recurse_copy_kept_shared_to_global, local_size, private_data_size + local_size, recurse_flags);
+
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(local_size - 1));
+ free_stack(common, private_data_size + local_size);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0);
+ OP_SRC(SLJIT_FAST_RETURN, TMP2, 0);
+ }
+
+if (common->accept != NULL)
+ {
+ SLJIT_ASSERT(recurse_flags & recurse_flag_accept_found);
+
+ set_jumps(common->accept, LABEL());
+
+ OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_SP), common->recursive_head_ptr);
+ OP1(SLJIT_MOV, TMP2, 0, STACK_TOP, 0);
+
+ allocate_stack(common, 2);
+ OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, -1);
+ }
+
+set_jumps(match, LABEL());
+
+OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0);
+
+copy_recurse_data(common, ccbegin, ccend, recurse_swap_global, local_size, private_data_size + local_size, recurse_flags);
+
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP2), STACK(local_size - 1));
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 1);
+OP_SRC(SLJIT_FAST_RETURN, TMP2, 0);
+}
+
+#undef COMPILE_BACKTRACKINGPATH
+#undef CURRENT_AS
+
+#define PUBLIC_JIT_COMPILE_CONFIGURATION_OPTIONS \
+ (PCRE2_JIT_INVALID_UTF)
+
+static int jit_compile(pcre2_code *code, sljit_u32 mode)
+{
+pcre2_real_code *re = (pcre2_real_code *)code;
+struct sljit_compiler *compiler;
+backtrack_common rootbacktrack;
+compiler_common common_data;
+compiler_common *common = &common_data;
+const sljit_u8 *tables = re->tables;
+void *allocator_data = &re->memctl;
+int private_data_size;
+PCRE2_SPTR ccend;
+executable_functions *functions;
+void *executable_func;
+sljit_uw executable_size;
+sljit_uw total_length;
+struct sljit_label *mainloop_label = NULL;
+struct sljit_label *continue_match_label;
+struct sljit_label *empty_match_found_label = NULL;
+struct sljit_label *empty_match_backtrack_label = NULL;
+struct sljit_label *reset_match_label;
+struct sljit_label *quit_label;
+struct sljit_jump *jump;
+struct sljit_jump *minlength_check_failed = NULL;
+struct sljit_jump *empty_match = NULL;
+struct sljit_jump *end_anchor_failed = NULL;
+jump_list *reqcu_not_found = NULL;
+
+SLJIT_ASSERT(tables);
+
+#if HAS_VIRTUAL_REGISTERS == 1
+SLJIT_ASSERT(sljit_get_register_index(TMP3) < 0 && sljit_get_register_index(ARGUMENTS) < 0 && sljit_get_register_index(RETURN_ADDR) < 0);
+#elif HAS_VIRTUAL_REGISTERS == 0
+SLJIT_ASSERT(sljit_get_register_index(TMP3) >= 0 && sljit_get_register_index(ARGUMENTS) >= 0 && sljit_get_register_index(RETURN_ADDR) >= 0);
+#else
+#error "Invalid value for HAS_VIRTUAL_REGISTERS"
+#endif
+
+memset(&rootbacktrack, 0, sizeof(backtrack_common));
+memset(common, 0, sizeof(compiler_common));
+common->re = re;
+common->name_table = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code));
+rootbacktrack.cc = common->name_table + re->name_count * re->name_entry_size;
+
+#ifdef SUPPORT_UNICODE
+common->invalid_utf = (mode & PCRE2_JIT_INVALID_UTF) != 0;
+#endif /* SUPPORT_UNICODE */
+mode &= ~PUBLIC_JIT_COMPILE_CONFIGURATION_OPTIONS;
+
+common->start = rootbacktrack.cc;
+common->read_only_data_head = NULL;
+common->fcc = tables + fcc_offset;
+common->lcc = (sljit_sw)(tables + lcc_offset);
+common->mode = mode;
+common->might_be_empty = (re->minlength == 0) || (re->flags & PCRE2_MATCH_EMPTY);
+common->allow_empty_partial = (re->max_lookbehind > 0) || (re->flags & PCRE2_MATCH_EMPTY);
+common->nltype = NLTYPE_FIXED;
+switch(re->newline_convention)
+ {
+ case PCRE2_NEWLINE_CR: common->newline = CHAR_CR; break;
+ case PCRE2_NEWLINE_LF: common->newline = CHAR_NL; break;
+ case PCRE2_NEWLINE_CRLF: common->newline = (CHAR_CR << 8) | CHAR_NL; break;
+ case PCRE2_NEWLINE_ANY: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANY; break;
+ case PCRE2_NEWLINE_ANYCRLF: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANYCRLF; break;
+ case PCRE2_NEWLINE_NUL: common->newline = CHAR_NUL; break;
+ default: return PCRE2_ERROR_INTERNAL;
+ }
+common->nlmax = READ_CHAR_MAX;
+common->nlmin = 0;
+if (re->bsr_convention == PCRE2_BSR_UNICODE)
+ common->bsr_nltype = NLTYPE_ANY;
+else if (re->bsr_convention == PCRE2_BSR_ANYCRLF)
+ common->bsr_nltype = NLTYPE_ANYCRLF;
+else
+ {
+#ifdef BSR_ANYCRLF
+ common->bsr_nltype = NLTYPE_ANYCRLF;
+#else
+ common->bsr_nltype = NLTYPE_ANY;
+#endif
+ }
+common->bsr_nlmax = READ_CHAR_MAX;
+common->bsr_nlmin = 0;
+common->endonly = (re->overall_options & PCRE2_DOLLAR_ENDONLY) != 0;
+common->ctypes = (sljit_sw)(tables + ctypes_offset);
+common->name_count = re->name_count;
+common->name_entry_size = re->name_entry_size;
+common->unset_backref = (re->overall_options & PCRE2_MATCH_UNSET_BACKREF) != 0;
+common->alt_circumflex = (re->overall_options & PCRE2_ALT_CIRCUMFLEX) != 0;
+#ifdef SUPPORT_UNICODE
+/* PCRE_UTF[16|32] have the same value as PCRE_UTF8. */
+common->utf = (re->overall_options & PCRE2_UTF) != 0;
+common->ucp = (re->overall_options & PCRE2_UCP) != 0;
+if (common->utf)
+ {
+ if (common->nltype == NLTYPE_ANY)
+ common->nlmax = 0x2029;
+ else if (common->nltype == NLTYPE_ANYCRLF)
+ common->nlmax = (CHAR_CR > CHAR_NL) ? CHAR_CR : CHAR_NL;
+ else
+ {
+ /* We only care about the first newline character. */
+ common->nlmax = common->newline & 0xff;
+ }
+
+ if (common->nltype == NLTYPE_FIXED)
+ common->nlmin = common->newline & 0xff;
+ else
+ common->nlmin = (CHAR_CR < CHAR_NL) ? CHAR_CR : CHAR_NL;
+
+ if (common->bsr_nltype == NLTYPE_ANY)
+ common->bsr_nlmax = 0x2029;
+ else
+ common->bsr_nlmax = (CHAR_CR > CHAR_NL) ? CHAR_CR : CHAR_NL;
+ common->bsr_nlmin = (CHAR_CR < CHAR_NL) ? CHAR_CR : CHAR_NL;
+ }
+else
+ common->invalid_utf = FALSE;
+#endif /* SUPPORT_UNICODE */
+ccend = bracketend(common->start);
+
+/* Calculate the local space size on the stack. */
+common->ovector_start = LIMIT_MATCH + sizeof(sljit_sw);
+common->optimized_cbracket = (sljit_u8 *)SLJIT_MALLOC(re->top_bracket + 1, allocator_data);
+if (!common->optimized_cbracket)
+ return PCRE2_ERROR_NOMEMORY;
+#if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 1
+memset(common->optimized_cbracket, 0, re->top_bracket + 1);
+#else
+memset(common->optimized_cbracket, 1, re->top_bracket + 1);
+#endif
+
+SLJIT_ASSERT(*common->start == OP_BRA && ccend[-(1 + LINK_SIZE)] == OP_KET);
+#if defined DEBUG_FORCE_UNOPTIMIZED_CBRAS && DEBUG_FORCE_UNOPTIMIZED_CBRAS == 2
+common->capture_last_ptr = common->ovector_start;
+common->ovector_start += sizeof(sljit_sw);
+#endif
+if (!check_opcode_types(common, common->start, ccend))
+ {
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+/* Checking flags and updating ovector_start. */
+if (mode == PCRE2_JIT_COMPLETE && (re->flags & PCRE2_LASTSET) != 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ common->req_char_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+if (mode != PCRE2_JIT_COMPLETE)
+ {
+ common->start_used_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ common->hit_start = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+ }
+if ((re->overall_options & (PCRE2_FIRSTLINE | PCRE2_USE_OFFSET_LIMIT)) != 0)
+ {
+ common->match_end_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+#if defined DEBUG_FORCE_CONTROL_HEAD && DEBUG_FORCE_CONTROL_HEAD
+common->control_head_ptr = 1;
+#endif
+if (common->control_head_ptr != 0)
+ {
+ common->control_head_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+if (common->has_set_som)
+ {
+ /* Saving the real start pointer is necessary. */
+ common->start_ptr = common->ovector_start;
+ common->ovector_start += sizeof(sljit_sw);
+ }
+
+/* Aligning ovector to even number of sljit words. */
+if ((common->ovector_start & sizeof(sljit_sw)) != 0)
+ common->ovector_start += sizeof(sljit_sw);
+
+if (common->start_ptr == 0)
+ common->start_ptr = OVECTOR(0);
+
+/* Capturing brackets cannot be optimized if callouts are allowed. */
+if (common->capture_last_ptr != 0)
+ memset(common->optimized_cbracket, 0, re->top_bracket + 1);
+
+SLJIT_ASSERT(!(common->req_char_ptr != 0 && common->start_used_ptr != 0));
+common->cbra_ptr = OVECTOR_START + (re->top_bracket + 1) * 2 * sizeof(sljit_sw);
+
+total_length = ccend - common->start;
+common->private_data_ptrs = (sljit_s32*)SLJIT_MALLOC(total_length * (sizeof(sljit_s32) + (common->has_then ? 1 : 0)), allocator_data);
+if (!common->private_data_ptrs)
+ {
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+memset(common->private_data_ptrs, 0, total_length * sizeof(sljit_s32));
+
+private_data_size = common->cbra_ptr + (re->top_bracket + 1) * sizeof(sljit_sw);
+
+if ((re->overall_options & PCRE2_ANCHORED) == 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0 && !common->has_skip_in_assert_back)
+ detect_early_fail(common, common->start, &private_data_size, 0, 0, TRUE);
+
+set_private_data_ptrs(common, &private_data_size, ccend);
+
+SLJIT_ASSERT(common->early_fail_start_ptr <= common->early_fail_end_ptr);
+
+if (private_data_size > SLJIT_MAX_LOCAL_SIZE)
+ {
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+if (common->has_then)
+ {
+ common->then_offsets = (sljit_u8 *)(common->private_data_ptrs + total_length);
+ memset(common->then_offsets, 0, total_length);
+ set_then_offsets(common, common->start, NULL);
+ }
+
+compiler = sljit_create_compiler(allocator_data, NULL);
+if (!compiler)
+ {
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+common->compiler = compiler;
+
+/* Main pcre2_jit_exec entry. */
+SLJIT_ASSERT((private_data_size & (sizeof(sljit_sw) - 1)) == 0);
+sljit_emit_enter(compiler, 0, SLJIT_ARGS1(W, W), 5, 5, 0, 0, private_data_size);
+
+/* Register init. */
+reset_ovector(common, (re->top_bracket + 1) * 2);
+if (common->req_char_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->req_char_ptr, SLJIT_R0, 0);
+
+OP1(SLJIT_MOV, ARGUMENTS, 0, SLJIT_S0, 0);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_S0, 0);
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end));
+OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack));
+OP1(SLJIT_MOV_U32, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, limit_match));
+OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, end));
+OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, start));
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH, TMP1, 0);
+
+if (common->early_fail_start_ptr < common->early_fail_end_ptr)
+ reset_early_fail(common);
+
+if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1);
+if (common->mark_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->mark_ptr, SLJIT_IMM, 0);
+if (common->control_head_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->control_head_ptr, SLJIT_IMM, 0);
+
+/* Main part of the matching */
+if ((re->overall_options & PCRE2_ANCHORED) == 0)
+ {
+ mainloop_label = mainloop_entry(common);
+ continue_match_label = LABEL();
+ /* Forward search if possible. */
+ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ if (mode == PCRE2_JIT_COMPLETE && fast_forward_first_n_chars(common))
+ ;
+ else if ((re->flags & PCRE2_FIRSTSET) != 0)
+ fast_forward_first_char(common);
+ else if ((re->flags & PCRE2_STARTLINE) != 0)
+ fast_forward_newline(common);
+ else if ((re->flags & PCRE2_FIRSTMAPSET) != 0)
+ fast_forward_start_bits(common);
+ }
+ }
+else
+ continue_match_label = LABEL();
+
+if (mode == PCRE2_JIT_COMPLETE && re->minlength > 0 && (re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(re->minlength));
+ minlength_check_failed = CMP(SLJIT_GREATER, TMP2, 0, STR_END, 0);
+ }
+if (common->req_char_ptr != 0)
+ reqcu_not_found = search_requested_char(common, (PCRE2_UCHAR)(re->last_codeunit), (re->flags & PCRE2_LASTCASELESS) != 0, (re->flags & PCRE2_FIRSTSET) != 0);
+
+/* Store the current STR_PTR in OVECTOR(0). */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), OVECTOR(0), STR_PTR, 0);
+/* Copy the limit of allowed recursions. */
+OP1(SLJIT_MOV, COUNT_MATCH, 0, SLJIT_MEM1(SLJIT_SP), LIMIT_MATCH);
+if (common->capture_last_ptr != 0)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->capture_last_ptr, SLJIT_IMM, 0);
+if (common->fast_forward_bc_ptr != NULL)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), PRIVATE_DATA(common->fast_forward_bc_ptr + 1) >> 3, STR_PTR, 0);
+
+if (common->start_ptr != OVECTOR(0))
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_ptr, STR_PTR, 0);
+
+/* Copy the beginning of the string. */
+if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ jump = CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+ JUMPHERE(jump);
+ }
+else if (mode == PCRE2_JIT_PARTIAL_HARD)
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, STR_PTR, 0);
+
+compile_matchingpath(common, common->start, ccend, &rootbacktrack);
+if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ {
+ sljit_free_compiler(compiler);
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+if ((re->overall_options & PCRE2_ENDANCHORED) != 0)
+ end_anchor_failed = CMP(SLJIT_NOT_EQUAL, STR_PTR, 0, STR_END, 0);
+
+if (common->might_be_empty)
+ {
+ empty_match = CMP(SLJIT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), OVECTOR(0));
+ empty_match_found_label = LABEL();
+ }
+
+common->accept_label = LABEL();
+if (common->accept != NULL)
+ set_jumps(common->accept, common->accept_label);
+
+/* This means we have a match. Update the ovector. */
+copy_ovector(common, re->top_bracket + 1);
+common->quit_label = common->abort_label = LABEL();
+if (common->quit != NULL)
+ set_jumps(common->quit, common->quit_label);
+if (common->abort != NULL)
+ set_jumps(common->abort, common->abort_label);
+if (minlength_check_failed != NULL)
+ SET_LABEL(minlength_check_failed, common->abort_label);
+
+sljit_emit_op0(compiler, SLJIT_SKIP_FRAMES_BEFORE_RETURN);
+sljit_emit_return(compiler, SLJIT_MOV, SLJIT_RETURN_REG, 0);
+
+if (common->failed_match != NULL)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+ set_jumps(common->failed_match, LABEL());
+ OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+ JUMPTO(SLJIT_JUMP, common->abort_label);
+ }
+
+if ((re->overall_options & PCRE2_ENDANCHORED) != 0)
+ JUMPHERE(end_anchor_failed);
+
+if (mode != PCRE2_JIT_COMPLETE)
+ {
+ common->partialmatchlabel = LABEL();
+ set_jumps(common->partialmatch, common->partialmatchlabel);
+ return_with_partial_match(common, common->quit_label);
+ }
+
+if (common->might_be_empty)
+ empty_match_backtrack_label = LABEL();
+compile_backtrackingpath(common, rootbacktrack.top);
+if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ {
+ sljit_free_compiler(compiler);
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+SLJIT_ASSERT(rootbacktrack.prev == NULL);
+reset_match_label = LABEL();
+
+if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ {
+ /* Update hit_start only in the first time. */
+ jump = CMP(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, 0);
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->start_ptr);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->start_used_ptr, SLJIT_IMM, -1);
+ OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), common->hit_start, TMP1, 0);
+ JUMPHERE(jump);
+ }
+
+/* Check we have remaining characters. */
+if ((re->overall_options & PCRE2_ANCHORED) == 0 && common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ }
+
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP),
+ (common->fast_forward_bc_ptr != NULL) ? (PRIVATE_DATA(common->fast_forward_bc_ptr + 1) >> 3) : common->start_ptr);
+
+if ((re->overall_options & PCRE2_ANCHORED) == 0)
+ {
+ if (common->ff_newline_shortcut != NULL)
+ {
+ /* There cannot be more newlines if PCRE2_FIRSTLINE is set. */
+ if ((re->overall_options & PCRE2_FIRSTLINE) == 0)
+ {
+ if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP1(SLJIT_MOV, STR_END, 0, TMP1, 0);
+ CMPTO(SLJIT_LESS, STR_PTR, 0, TMP1, 0, common->ff_newline_shortcut);
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+ }
+ else
+ CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, common->ff_newline_shortcut);
+ }
+ }
+ else
+ CMPTO(SLJIT_LESS, STR_PTR, 0, (common->match_end_ptr == 0) ? STR_END : TMP1, 0, mainloop_label);
+ }
+
+/* No more remaining characters. */
+if (reqcu_not_found != NULL)
+ set_jumps(reqcu_not_found, LABEL());
+
+if (mode == PCRE2_JIT_PARTIAL_SOFT)
+ CMPTO(SLJIT_NOT_EQUAL, SLJIT_MEM1(SLJIT_SP), common->hit_start, SLJIT_IMM, -1, common->partialmatchlabel);
+
+OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_NOMATCH);
+JUMPTO(SLJIT_JUMP, common->quit_label);
+
+flush_stubs(common);
+
+if (common->might_be_empty)
+ {
+ JUMPHERE(empty_match);
+ OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0);
+ OP1(SLJIT_MOV_U32, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, options));
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY);
+ JUMPTO(SLJIT_NOT_ZERO, empty_match_backtrack_label);
+ OP2U(SLJIT_AND | SLJIT_SET_Z, TMP2, 0, SLJIT_IMM, PCRE2_NOTEMPTY_ATSTART);
+ JUMPTO(SLJIT_ZERO, empty_match_found_label);
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str));
+ CMPTO(SLJIT_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found_label);
+ JUMPTO(SLJIT_JUMP, empty_match_backtrack_label);
+ }
+
+common->fast_forward_bc_ptr = NULL;
+common->early_fail_start_ptr = 0;
+common->early_fail_end_ptr = 0;
+common->currententry = common->entries;
+common->local_quit_available = TRUE;
+quit_label = common->quit_label;
+if (common->currententry != NULL)
+ {
+ /* A free bit for each private data. */
+ common->recurse_bitset_size = ((private_data_size / SSIZE_OF(sw)) + 7) >> 3;
+ SLJIT_ASSERT(common->recurse_bitset_size > 0);
+ common->recurse_bitset = (sljit_u8*)SLJIT_MALLOC(common->recurse_bitset_size, allocator_data);;
+
+ if (common->recurse_bitset != NULL)
+ {
+ do
+ {
+ /* Might add new entries. */
+ compile_recurse(common);
+ if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler)))
+ break;
+ flush_stubs(common);
+ common->currententry = common->currententry->next;
+ }
+ while (common->currententry != NULL);
+
+ SLJIT_FREE(common->recurse_bitset, allocator_data);
+ }
+
+ if (common->currententry != NULL)
+ {
+ /* The common->recurse_bitset has been freed. */
+ SLJIT_ASSERT(sljit_get_compiler_error(compiler) || common->recurse_bitset == NULL);
+
+ sljit_free_compiler(compiler);
+ SLJIT_FREE(common->optimized_cbracket, allocator_data);
+ SLJIT_FREE(common->private_data_ptrs, allocator_data);
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+ }
+common->local_quit_available = FALSE;
+common->quit_label = quit_label;
+
+/* Allocating stack, returns with PCRE_ERROR_JIT_STACKLIMIT if fails. */
+/* This is a (really) rare case. */
+set_jumps(common->stackalloc, LABEL());
+/* RETURN_ADDR is not a saved register. */
+sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+
+SLJIT_ASSERT(TMP1 == SLJIT_R0 && STR_PTR == SLJIT_R1);
+
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, STR_PTR, 0);
+OP1(SLJIT_MOV, SLJIT_R0, 0, ARGUMENTS, 0);
+OP2(SLJIT_SUB, SLJIT_R1, 0, STACK_LIMIT, 0, SLJIT_IMM, STACK_GROWTH_RATE);
+OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_R0), SLJIT_OFFSETOF(jit_arguments, stack));
+OP1(SLJIT_MOV, STACK_LIMIT, 0, TMP2, 0);
+
+sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS2(W, W, W), SLJIT_IMM, SLJIT_FUNC_ADDR(sljit_stack_resize));
+
+jump = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+OP1(SLJIT_MOV, TMP2, 0, STACK_LIMIT, 0);
+OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_RETURN_REG, 0);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+OP_SRC(SLJIT_FAST_RETURN, TMP1, 0);
+
+/* Allocation failed. */
+JUMPHERE(jump);
+/* We break the return address cache here, but this is a really rare case. */
+OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_JIT_STACKLIMIT);
+JUMPTO(SLJIT_JUMP, common->quit_label);
+
+/* Call limit reached. */
+set_jumps(common->calllimit, LABEL());
+OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE2_ERROR_MATCHLIMIT);
+JUMPTO(SLJIT_JUMP, common->quit_label);
+
+if (common->revertframes != NULL)
+ {
+ set_jumps(common->revertframes, LABEL());
+ do_revertframes(common);
+ }
+if (common->wordboundary != NULL)
+ {
+ set_jumps(common->wordboundary, LABEL());
+ check_wordboundary(common);
+ }
+if (common->anynewline != NULL)
+ {
+ set_jumps(common->anynewline, LABEL());
+ check_anynewline(common);
+ }
+if (common->hspace != NULL)
+ {
+ set_jumps(common->hspace, LABEL());
+ check_hspace(common);
+ }
+if (common->vspace != NULL)
+ {
+ set_jumps(common->vspace, LABEL());
+ check_vspace(common);
+ }
+if (common->casefulcmp != NULL)
+ {
+ set_jumps(common->casefulcmp, LABEL());
+ do_casefulcmp(common);
+ }
+if (common->caselesscmp != NULL)
+ {
+ set_jumps(common->caselesscmp, LABEL());
+ do_caselesscmp(common);
+ }
+if (common->reset_match != NULL)
+ {
+ set_jumps(common->reset_match, LABEL());
+ do_reset_match(common, (re->top_bracket + 1) * 2);
+ CMPTO(SLJIT_GREATER, STR_PTR, 0, TMP1, 0, continue_match_label);
+ OP1(SLJIT_MOV, STR_PTR, 0, TMP1, 0);
+ JUMPTO(SLJIT_JUMP, reset_match_label);
+ }
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+if (common->utfreadchar != NULL)
+ {
+ set_jumps(common->utfreadchar, LABEL());
+ do_utfreadchar(common);
+ }
+if (common->utfreadtype8 != NULL)
+ {
+ set_jumps(common->utfreadtype8, LABEL());
+ do_utfreadtype8(common);
+ }
+if (common->utfpeakcharback != NULL)
+ {
+ set_jumps(common->utfpeakcharback, LABEL());
+ do_utfpeakcharback(common);
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+#if PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16
+if (common->utfreadchar_invalid != NULL)
+ {
+ set_jumps(common->utfreadchar_invalid, LABEL());
+ do_utfreadchar_invalid(common);
+ }
+if (common->utfreadnewline_invalid != NULL)
+ {
+ set_jumps(common->utfreadnewline_invalid, LABEL());
+ do_utfreadnewline_invalid(common);
+ }
+if (common->utfmoveback_invalid)
+ {
+ set_jumps(common->utfmoveback_invalid, LABEL());
+ do_utfmoveback_invalid(common);
+ }
+if (common->utfpeakcharback_invalid)
+ {
+ set_jumps(common->utfpeakcharback_invalid, LABEL());
+ do_utfpeakcharback_invalid(common);
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 || PCRE2_CODE_UNIT_WIDTH == 16 */
+if (common->getucd != NULL)
+ {
+ set_jumps(common->getucd, LABEL());
+ do_getucd(common);
+ }
+if (common->getucdtype != NULL)
+ {
+ set_jumps(common->getucdtype, LABEL());
+ do_getucdtype(common);
+ }
+#endif /* SUPPORT_UNICODE */
+
+SLJIT_FREE(common->optimized_cbracket, allocator_data);
+SLJIT_FREE(common->private_data_ptrs, allocator_data);
+
+executable_func = sljit_generate_code(compiler);
+executable_size = sljit_get_generated_code_size(compiler);
+sljit_free_compiler(compiler);
+
+if (executable_func == NULL)
+ {
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+/* Reuse the function descriptor if possible. */
+if (re->executable_jit != NULL)
+ functions = (executable_functions *)re->executable_jit;
+else
+ {
+ functions = SLJIT_MALLOC(sizeof(executable_functions), allocator_data);
+ if (functions == NULL)
+ {
+ /* This case is highly unlikely since we just recently
+ freed a lot of memory. Not impossible though. */
+ sljit_free_code(executable_func, NULL);
+ PRIV(jit_free_rodata)(common->read_only_data_head, allocator_data);
+ return PCRE2_ERROR_NOMEMORY;
+ }
+ memset(functions, 0, sizeof(executable_functions));
+ functions->top_bracket = re->top_bracket + 1;
+ functions->limit_match = re->limit_match;
+ re->executable_jit = functions;
+ }
+
+/* Turn mode into an index. */
+if (mode == PCRE2_JIT_COMPLETE)
+ mode = 0;
+else
+ mode = (mode == PCRE2_JIT_PARTIAL_SOFT) ? 1 : 2;
+
+SLJIT_ASSERT(mode < JIT_NUMBER_OF_COMPILE_MODES);
+functions->executable_funcs[mode] = executable_func;
+functions->read_only_data_heads[mode] = common->read_only_data_head;
+functions->executable_sizes[mode] = executable_size;
+return 0;
+}
+
+#endif
+
+/*************************************************
+* JIT compile a Regular Expression *
+*************************************************/
+
+/* This function used JIT to convert a previously-compiled pattern into machine
+code.
+
+Arguments:
+ code a compiled pattern
+ options JIT option bits
+
+Returns: 0: success or (*NOJIT) was used
+ <0: an error code
+*/
+
+#define PUBLIC_JIT_COMPILE_OPTIONS \
+ (PCRE2_JIT_COMPLETE|PCRE2_JIT_PARTIAL_SOFT|PCRE2_JIT_PARTIAL_HARD|PCRE2_JIT_INVALID_UTF)
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_jit_compile(pcre2_code *code, uint32_t options)
+{
+pcre2_real_code *re = (pcre2_real_code *)code;
+#ifdef SUPPORT_JIT
+executable_functions *functions;
+static int executable_allocator_is_working = -1;
+#endif
+
+if (code == NULL)
+ return PCRE2_ERROR_NULL;
+
+if ((options & ~PUBLIC_JIT_COMPILE_OPTIONS) != 0)
+ return PCRE2_ERROR_JIT_BADOPTION;
+
+/* Support for invalid UTF was first introduced in JIT, with the option
+PCRE2_JIT_INVALID_UTF. Later, support was added to the interpreter, and the
+compile-time option PCRE2_MATCH_INVALID_UTF was created. This is now the
+preferred feature, with the earlier option deprecated. However, for backward
+compatibility, if the earlier option is set, it forces the new option so that
+if JIT matching falls back to the interpreter, there is still support for
+invalid UTF. However, if this function has already been successfully called
+without PCRE2_JIT_INVALID_UTF and without PCRE2_MATCH_INVALID_UTF (meaning that
+non-invalid-supporting JIT code was compiled), give an error.
+
+If in the future support for PCRE2_JIT_INVALID_UTF is withdrawn, the following
+actions are needed:
+
+ 1. Remove the definition from pcre2.h.in and from the list in
+ PUBLIC_JIT_COMPILE_OPTIONS above.
+
+ 2. Replace PCRE2_JIT_INVALID_UTF with a local flag in this module.
+
+ 3. Replace PCRE2_JIT_INVALID_UTF in pcre2_jit_test.c.
+
+ 4. Delete the following short block of code. The setting of "re" and
+ "functions" can be moved into the JIT-only block below, but if that is
+ done, (void)re and (void)functions will be needed in the non-JIT case, to
+ avoid compiler warnings.
+*/
+
+#ifdef SUPPORT_JIT
+functions = (executable_functions *)re->executable_jit;
+#endif
+
+if ((options & PCRE2_JIT_INVALID_UTF) != 0)
+ {
+ if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) == 0)
+ {
+#ifdef SUPPORT_JIT
+ if (functions != NULL) return PCRE2_ERROR_JIT_BADOPTION;
+#endif
+ re->overall_options |= PCRE2_MATCH_INVALID_UTF;
+ }
+ }
+
+/* The above tests are run with and without JIT support. This means that
+PCRE2_JIT_INVALID_UTF propagates back into the regex options (ensuring
+interpreter support) even in the absence of JIT. But now, if there is no JIT
+support, give an error return. */
+
+#ifndef SUPPORT_JIT
+return PCRE2_ERROR_JIT_BADOPTION;
+#else /* SUPPORT_JIT */
+
+/* There is JIT support. Do the necessary. */
+
+if ((re->flags & PCRE2_NOJIT) != 0) return 0;
+
+if (executable_allocator_is_working == -1)
+ {
+ /* Checks whether the executable allocator is working. This check
+ might run multiple times in multi-threaded environments, but the
+ result should not be affected by it. */
+ void *ptr = SLJIT_MALLOC_EXEC(32, NULL);
+ if (ptr != NULL)
+ {
+ SLJIT_FREE_EXEC(((sljit_u8*)(ptr)) + SLJIT_EXEC_OFFSET(ptr), NULL);
+ executable_allocator_is_working = 1;
+ }
+ else executable_allocator_is_working = 0;
+ }
+
+if (!executable_allocator_is_working)
+ return PCRE2_ERROR_NOMEMORY;
+
+if ((re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0)
+ options |= PCRE2_JIT_INVALID_UTF;
+
+if ((options & PCRE2_JIT_COMPLETE) != 0 && (functions == NULL
+ || functions->executable_funcs[0] == NULL)) {
+ uint32_t excluded_options = (PCRE2_JIT_PARTIAL_SOFT | PCRE2_JIT_PARTIAL_HARD);
+ int result = jit_compile(code, options & ~excluded_options);
+ if (result != 0)
+ return result;
+ }
+
+if ((options & PCRE2_JIT_PARTIAL_SOFT) != 0 && (functions == NULL
+ || functions->executable_funcs[1] == NULL)) {
+ uint32_t excluded_options = (PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_HARD);
+ int result = jit_compile(code, options & ~excluded_options);
+ if (result != 0)
+ return result;
+ }
+
+if ((options & PCRE2_JIT_PARTIAL_HARD) != 0 && (functions == NULL
+ || functions->executable_funcs[2] == NULL)) {
+ uint32_t excluded_options = (PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT);
+ int result = jit_compile(code, options & ~excluded_options);
+ if (result != 0)
+ return result;
+ }
+
+return 0;
+
+#endif /* SUPPORT_JIT */
+}
+
+/* JIT compiler uses an all-in-one approach. This improves security,
+ since the code generator functions are not exported. */
+
+#define INCLUDED_FROM_PCRE2_JIT_COMPILE
+
+#include "pcre2_jit_match.c"
+#include "pcre2_jit_misc.c"
+
+/* End of pcre2_jit_compile.c */
diff --git a/contrib/libs/pcre2/src/pcre2_jit_match.c b/contrib/libs/pcre2/src/pcre2_jit_match.c
new file mode 100644
index 0000000000..1ab3af073e
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_jit_match.c
@@ -0,0 +1,186 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef INCLUDED_FROM_PCRE2_JIT_COMPILE
+#error This file must be included from pcre2_jit_compile.c.
+#endif
+
+#ifdef SUPPORT_JIT
+
+static SLJIT_NOINLINE int jit_machine_stack_exec(jit_arguments *arguments, jit_function executable_func)
+{
+sljit_u8 local_space[MACHINE_STACK_SIZE];
+struct sljit_stack local_stack;
+
+local_stack.min_start = local_space;
+local_stack.start = local_space;
+local_stack.end = local_space + MACHINE_STACK_SIZE;
+local_stack.top = local_space + MACHINE_STACK_SIZE;
+arguments->stack = &local_stack;
+return executable_func(arguments);
+}
+
+#endif
+
+
+/*************************************************
+* Do a JIT pattern match *
+*************************************************/
+
+/* This function runs a JIT pattern match.
+
+Arguments:
+ code points to the compiled expression
+ subject points to the subject string
+ length length of subject string (may contain binary zeros)
+ start_offset where to start in the subject string
+ options option bits
+ match_data points to a match_data block
+ mcontext points to a match context
+
+Returns: > 0 => success; value is the number of ovector pairs filled
+ = 0 => success, but ovector is not big enough
+ -1 => failed to match (PCRE_ERROR_NOMATCH)
+ < -1 => some kind of unexpected problem
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_jit_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
+ PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
+ pcre2_match_context *mcontext)
+{
+#ifndef SUPPORT_JIT
+
+(void)code;
+(void)subject;
+(void)length;
+(void)start_offset;
+(void)options;
+(void)match_data;
+(void)mcontext;
+return PCRE2_ERROR_JIT_BADOPTION;
+
+#else /* SUPPORT_JIT */
+
+pcre2_real_code *re = (pcre2_real_code *)code;
+executable_functions *functions = (executable_functions *)re->executable_jit;
+pcre2_jit_stack *jit_stack;
+uint32_t oveccount = match_data->oveccount;
+uint32_t max_oveccount;
+union {
+ void *executable_func;
+ jit_function call_executable_func;
+} convert_executable_func;
+jit_arguments arguments;
+int rc;
+int index = 0;
+
+if ((options & PCRE2_PARTIAL_HARD) != 0)
+ index = 2;
+else if ((options & PCRE2_PARTIAL_SOFT) != 0)
+ index = 1;
+
+if (functions == NULL || functions->executable_funcs[index] == NULL)
+ return PCRE2_ERROR_JIT_BADOPTION;
+
+/* Sanity checks should be handled by pcre2_match. */
+arguments.str = subject + start_offset;
+arguments.begin = subject;
+arguments.end = subject + length;
+arguments.match_data = match_data;
+arguments.startchar_ptr = subject;
+arguments.mark_ptr = NULL;
+arguments.options = options;
+
+if (mcontext != NULL)
+ {
+ arguments.callout = mcontext->callout;
+ arguments.callout_data = mcontext->callout_data;
+ arguments.offset_limit = mcontext->offset_limit;
+ arguments.limit_match = (mcontext->match_limit < re->limit_match)?
+ mcontext->match_limit : re->limit_match;
+ if (mcontext->jit_callback != NULL)
+ jit_stack = mcontext->jit_callback(mcontext->jit_callback_data);
+ else
+ jit_stack = (pcre2_jit_stack *)mcontext->jit_callback_data;
+ }
+else
+ {
+ arguments.callout = NULL;
+ arguments.callout_data = NULL;
+ arguments.offset_limit = PCRE2_UNSET;
+ arguments.limit_match = (MATCH_LIMIT < re->limit_match)?
+ MATCH_LIMIT : re->limit_match;
+ jit_stack = NULL;
+ }
+
+
+max_oveccount = functions->top_bracket;
+if (oveccount > max_oveccount)
+ oveccount = max_oveccount;
+arguments.oveccount = oveccount << 1;
+
+
+convert_executable_func.executable_func = functions->executable_funcs[index];
+if (jit_stack != NULL)
+ {
+ arguments.stack = (struct sljit_stack *)(jit_stack->stack);
+ rc = convert_executable_func.call_executable_func(&arguments);
+ }
+else
+ rc = jit_machine_stack_exec(&arguments, convert_executable_func.call_executable_func);
+
+if (rc > (int)oveccount)
+ rc = 0;
+match_data->code = re;
+match_data->subject = (rc >= 0 || rc == PCRE2_ERROR_PARTIAL)? subject : NULL;
+match_data->rc = rc;
+match_data->startchar = arguments.startchar_ptr - subject;
+match_data->leftchar = 0;
+match_data->rightchar = 0;
+match_data->mark = arguments.mark_ptr;
+match_data->matchedby = PCRE2_MATCHEDBY_JIT;
+
+return match_data->rc;
+
+#endif /* SUPPORT_JIT */
+}
+
+/* End of pcre2_jit_match.c */
diff --git a/contrib/libs/pcre2/src/pcre2_jit_misc.c b/contrib/libs/pcre2/src/pcre2_jit_misc.c
new file mode 100644
index 0000000000..bb6a5589cb
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_jit_misc.c
@@ -0,0 +1,234 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifndef INCLUDED_FROM_PCRE2_JIT_COMPILE
+#error This file must be included from pcre2_jit_compile.c.
+#endif
+
+
+
+/*************************************************
+* Free JIT read-only data *
+*************************************************/
+
+void
+PRIV(jit_free_rodata)(void *current, void *allocator_data)
+{
+#ifndef SUPPORT_JIT
+(void)current;
+(void)allocator_data;
+#else /* SUPPORT_JIT */
+void *next;
+
+SLJIT_UNUSED_ARG(allocator_data);
+
+while (current != NULL)
+ {
+ next = *(void**)current;
+ SLJIT_FREE(current, allocator_data);
+ current = next;
+ }
+
+#endif /* SUPPORT_JIT */
+}
+
+/*************************************************
+* Free JIT compiled code *
+*************************************************/
+
+void
+PRIV(jit_free)(void *executable_jit, pcre2_memctl *memctl)
+{
+#ifndef SUPPORT_JIT
+(void)executable_jit;
+(void)memctl;
+#else /* SUPPORT_JIT */
+
+executable_functions *functions = (executable_functions *)executable_jit;
+void *allocator_data = memctl;
+int i;
+
+for (i = 0; i < JIT_NUMBER_OF_COMPILE_MODES; i++)
+ {
+ if (functions->executable_funcs[i] != NULL)
+ sljit_free_code(functions->executable_funcs[i], NULL);
+ PRIV(jit_free_rodata)(functions->read_only_data_heads[i], allocator_data);
+ }
+
+SLJIT_FREE(functions, allocator_data);
+
+#endif /* SUPPORT_JIT */
+}
+
+
+/*************************************************
+* Free unused JIT memory *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_jit_free_unused_memory(pcre2_general_context *gcontext)
+{
+#ifndef SUPPORT_JIT
+(void)gcontext; /* Suppress warning */
+#else /* SUPPORT_JIT */
+SLJIT_UNUSED_ARG(gcontext);
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+sljit_free_unused_memory_exec();
+#endif /* SLJIT_EXECUTABLE_ALLOCATOR */
+#endif /* SUPPORT_JIT */
+}
+
+
+
+/*************************************************
+* Allocate a JIT stack *
+*************************************************/
+
+PCRE2_EXP_DEFN pcre2_jit_stack * PCRE2_CALL_CONVENTION
+pcre2_jit_stack_create(size_t startsize, size_t maxsize,
+ pcre2_general_context *gcontext)
+{
+#ifndef SUPPORT_JIT
+
+(void)gcontext;
+(void)startsize;
+(void)maxsize;
+return NULL;
+
+#else /* SUPPORT_JIT */
+
+pcre2_jit_stack *jit_stack;
+
+if (startsize == 0 || maxsize == 0 || maxsize > SIZE_MAX - STACK_GROWTH_RATE)
+ return NULL;
+if (startsize > maxsize)
+ startsize = maxsize;
+startsize = (startsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1);
+maxsize = (maxsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1);
+
+jit_stack = PRIV(memctl_malloc)(sizeof(pcre2_real_jit_stack), (pcre2_memctl *)gcontext);
+if (jit_stack == NULL) return NULL;
+jit_stack->stack = sljit_allocate_stack(startsize, maxsize, &jit_stack->memctl);
+if (jit_stack->stack == NULL)
+ {
+ jit_stack->memctl.free(jit_stack, jit_stack->memctl.memory_data);
+ return NULL;
+ }
+return jit_stack;
+
+#endif
+}
+
+
+/*************************************************
+* Assign a JIT stack to a pattern *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_jit_stack_assign(pcre2_match_context *mcontext, pcre2_jit_callback callback,
+ void *callback_data)
+{
+#ifndef SUPPORT_JIT
+(void)mcontext;
+(void)callback;
+(void)callback_data;
+#else /* SUPPORT_JIT */
+
+if (mcontext == NULL) return;
+mcontext->jit_callback = callback;
+mcontext->jit_callback_data = callback_data;
+
+#endif /* SUPPORT_JIT */
+}
+
+
+/*************************************************
+* Free a JIT stack *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_jit_stack_free(pcre2_jit_stack *jit_stack)
+{
+#ifndef SUPPORT_JIT
+(void)jit_stack;
+#else /* SUPPORT_JIT */
+if (jit_stack != NULL)
+ {
+ sljit_free_stack((struct sljit_stack *)(jit_stack->stack), &jit_stack->memctl);
+ jit_stack->memctl.free(jit_stack, jit_stack->memctl.memory_data);
+ }
+#endif /* SUPPORT_JIT */
+}
+
+
+/*************************************************
+* Get target CPU type *
+*************************************************/
+
+const char*
+PRIV(jit_get_target)(void)
+{
+#ifndef SUPPORT_JIT
+return "JIT is not supported";
+#else /* SUPPORT_JIT */
+return sljit_get_platform_name();
+#endif /* SUPPORT_JIT */
+}
+
+
+/*************************************************
+* Get size of JIT code *
+*************************************************/
+
+size_t
+PRIV(jit_get_size)(void *executable_jit)
+{
+#ifndef SUPPORT_JIT
+(void)executable_jit;
+return 0;
+#else /* SUPPORT_JIT */
+sljit_uw *executable_sizes = ((executable_functions *)executable_jit)->executable_sizes;
+SLJIT_COMPILE_ASSERT(JIT_NUMBER_OF_COMPILE_MODES == 3, number_of_compile_modes_changed);
+return executable_sizes[0] + executable_sizes[1] + executable_sizes[2];
+#endif
+}
+
+/* End of pcre2_jit_misc.c */
diff --git a/contrib/libs/pcre2/src/pcre2_jit_simd_inc.h b/contrib/libs/pcre2/src/pcre2_jit_simd_inc.h
new file mode 100644
index 0000000000..6fcfac8f13
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_jit_simd_inc.h
@@ -0,0 +1,1858 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ This module by Zoltan Herczeg
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2019 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+#if !(defined SUPPORT_VALGRIND)
+
+#if ((defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X))
+
+typedef enum {
+ vector_compare_match1,
+ vector_compare_match1i,
+ vector_compare_match2,
+} vector_compare_type;
+
+static SLJIT_INLINE sljit_u32 max_fast_forward_char_pair_offset(void)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+return 15;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+return 7;
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+return 3;
+#else
+#error "Unsupported unit width"
+#endif
+}
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+static struct sljit_jump *jump_if_utf_char_start(struct sljit_compiler *compiler, sljit_s32 reg)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xc0);
+return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0x80);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+OP2(SLJIT_AND, reg, 0, reg, 0, SLJIT_IMM, 0xfc00);
+return CMP(SLJIT_NOT_EQUAL, reg, 0, SLJIT_IMM, 0xdc00);
+#else
+#error "Unknown code width"
+#endif
+}
+#endif
+
+#endif /* SLJIT_CONFIG_X86 || SLJIT_CONFIG_S390X */
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+
+static sljit_s32 character_to_int32(PCRE2_UCHAR chr)
+{
+sljit_u32 value = chr;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define SSE2_COMPARE_TYPE_INDEX 0
+return (sljit_s32)((value << 24) | (value << 16) | (value << 8) | value);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#define SSE2_COMPARE_TYPE_INDEX 1
+return (sljit_s32)((value << 16) | value);
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+#define SSE2_COMPARE_TYPE_INDEX 2
+return (sljit_s32)(value);
+#else
+#error "Unsupported unit width"
+#endif
+}
+
+static void load_from_mem_sse2(struct sljit_compiler *compiler, sljit_s32 dst_xmm_reg, sljit_s32 src_general_reg, sljit_s8 offset)
+{
+sljit_u8 instruction[5];
+
+SLJIT_ASSERT(dst_xmm_reg < 8);
+SLJIT_ASSERT(src_general_reg < 8);
+
+/* MOVDQA xmm1, xmm2/m128 */
+instruction[0] = ((sljit_u8)offset & 0xf) == 0 ? 0x66 : 0xf3;
+instruction[1] = 0x0f;
+instruction[2] = 0x6f;
+
+if (offset == 0)
+ {
+ instruction[3] = (dst_xmm_reg << 3) | src_general_reg;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+ }
+
+instruction[3] = 0x40 | (dst_xmm_reg << 3) | src_general_reg;
+instruction[4] = (sljit_u8)offset;
+sljit_emit_op_custom(compiler, instruction, 5);
+}
+
+static void fast_forward_char_pair_sse2_compare(struct sljit_compiler *compiler, vector_compare_type compare_type,
+ int step, sljit_s32 dst_ind, sljit_s32 cmp1_ind, sljit_s32 cmp2_ind, sljit_s32 tmp_ind)
+{
+sljit_u8 instruction[4];
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+
+SLJIT_ASSERT(step >= 0 && step <= 3);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (step == 0)
+ {
+ if (compare_type == vector_compare_match1i)
+ {
+ /* POR xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0xeb;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+ return;
+ }
+
+ if (step != 2)
+ return;
+
+ /* PCMPEQB/W/D xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+ }
+
+switch (step)
+ {
+ case 0:
+ /* MOVDQA xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x6f;
+ instruction[3] = 0xc0 | (tmp_ind << 3) | dst_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+
+ case 1:
+ /* PCMPEQB/W/D xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+ instruction[3] = 0xc0 | (dst_ind << 3) | cmp1_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+
+ case 2:
+ /* PCMPEQB/W/D xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0x74 + SSE2_COMPARE_TYPE_INDEX;
+ instruction[3] = 0xc0 | (tmp_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+
+ case 3:
+ /* POR xmm1, xmm2/m128 */
+ /* instruction[0] = 0x66; */
+ /* instruction[1] = 0x0f; */
+ instruction[2] = 0xeb;
+ instruction[3] = 0xc0 | (dst_ind << 3) | tmp_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ return;
+ }
+}
+
+#define JIT_HAS_FAST_FORWARD_CHAR_SIMD (sljit_has_cpu_feature(SLJIT_HAS_SSE2))
+
+static void fast_forward_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
+{
+DEFINE_COMPILER;
+sljit_u8 instruction[8];
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *quit;
+struct sljit_jump *partial_quit[2];
+vector_compare_type compare_type = vector_compare_match1;
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_u32 bit = 0;
+int i;
+
+SLJIT_UNUSED_ARG(offset);
+
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ compare_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit))
+ {
+ bit = 0;
+ compare_type = vector_compare_match2;
+ }
+ }
+
+partial_quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[0]);
+
+/* First part (unaligned start) */
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit));
+
+SLJIT_ASSERT(tmp1_reg_ind < 8);
+
+/* MOVD xmm, r/m32 */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6e;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char1 != char2)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2));
+
+ /* MOVD xmm, r/m32 */
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | tmp1_reg_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+
+/* PSHUFD xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x70;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | cmp1_ind;
+instruction[4] = 0;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char1 != char2)
+ {
+ /* PSHUFD xmm1, xmm2/m128, imm8 */
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf);
+
+load_from_mem_sse2(compiler, data_ind, str_ptr_reg_ind, 0);
+for (i = 0; i < 4; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | data_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+quit = CMP(SLJIT_NOT_ZERO, TMP1, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+/* Second part (aligned) */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+
+partial_quit[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[1]);
+
+load_from_mem_sse2(compiler, data_ind, str_ptr_reg_ind, 0);
+for (i = 0; i < 4; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | data_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+CMPTO(SLJIT_ZERO, TMP1, 0, SLJIT_IMM, 0, start);
+
+JUMPHERE(quit);
+
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_reg_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ JUMPHERE(partial_quit[0]);
+ JUMPHERE(partial_quit[1]);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+else
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset > 0)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
+
+ quit = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+ OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+ JUMPTO(SLJIT_JUMP, restart);
+
+ JUMPHERE(quit);
+ }
+#endif
+}
+
+#define JIT_HAS_FAST_REQUESTED_CHAR_SIMD (sljit_has_cpu_feature(SLJIT_HAS_SSE2))
+
+static jump_list *fast_requested_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2)
+{
+DEFINE_COMPILER;
+sljit_u8 instruction[8];
+struct sljit_label *start;
+struct sljit_jump *quit;
+jump_list *not_found = NULL;
+vector_compare_type compare_type = vector_compare_match1;
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_u32 bit = 0;
+int i;
+
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ compare_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit))
+ {
+ bit = 0;
+ compare_type = vector_compare_match2;
+ }
+ }
+
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+OP1(SLJIT_MOV, TMP2, 0, TMP1, 0);
+OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0);
+
+/* First part (unaligned start) */
+
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1 | bit));
+
+SLJIT_ASSERT(tmp1_reg_ind < 8);
+
+/* MOVD xmm, r/m32 */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6e;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char1 != char2)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(bit != 0 ? bit : char2));
+
+ /* MOVD xmm, r/m32 */
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | tmp1_reg_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+OP1(SLJIT_MOV, STR_PTR, 0, TMP2, 0);
+
+/* PSHUFD xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x70;
+instruction[3] = 0xc0 | (cmp1_ind << 3) | cmp1_ind;
+instruction[4] = 0;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char1 != char2)
+ {
+ /* PSHUFD xmm1, xmm2/m128, imm8 */
+ instruction[3] = 0xc0 | (cmp2_ind << 3) | cmp2_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf);
+
+load_from_mem_sse2(compiler, data_ind, str_ptr_reg_ind, 0);
+for (i = 0; i < 4; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | data_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+quit = CMP(SLJIT_NOT_ZERO, TMP1, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+/* Second part (aligned) */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+load_from_mem_sse2(compiler, data_ind, str_ptr_reg_ind, 0);
+for (i = 0; i < 4; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | data_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+CMPTO(SLJIT_ZERO, TMP1, 0, SLJIT_IMM, 0, start);
+
+JUMPHERE(quit);
+
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_reg_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, STR_PTR, 0);
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0);
+return not_found;
+}
+
+#ifndef _WIN64
+
+#define JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD (sljit_has_cpu_feature(SLJIT_HAS_SSE2))
+
+static void fast_forward_char_pair_simd(compiler_common *common, sljit_s32 offs1,
+ PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b)
+{
+DEFINE_COMPILER;
+sljit_u8 instruction[8];
+vector_compare_type compare1_type = vector_compare_match1;
+vector_compare_type compare2_type = vector_compare_match1;
+sljit_u32 bit1 = 0;
+sljit_u32 bit2 = 0;
+sljit_u32 diff = IN_UCHARS(offs1 - offs2);
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 tmp2_reg_ind = sljit_get_register_index(TMP2);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data1_ind = 0;
+sljit_s32 data2_ind = 1;
+sljit_s32 tmp1_ind = 2;
+sljit_s32 tmp2_ind = 3;
+sljit_s32 cmp1a_ind = 4;
+sljit_s32 cmp1b_ind = 5;
+sljit_s32 cmp2a_ind = 6;
+sljit_s32 cmp2b_ind = 7;
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *jump[2];
+int i;
+
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2);
+SLJIT_ASSERT(diff <= IN_UCHARS(max_fast_forward_char_pair_offset()));
+SLJIT_ASSERT(tmp1_reg_ind < 8 && tmp2_reg_ind == 1);
+
+/* Initialize. */
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1));
+
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, STR_END, 0);
+ CMOV(SLJIT_LESS, STR_END, TMP1, 0);
+ }
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+/* MOVD xmm, r/m32 */
+instruction[0] = 0x66;
+instruction[1] = 0x0f;
+instruction[2] = 0x6e;
+
+if (char1a == char1b)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a));
+else
+ {
+ bit1 = char1a ^ char1b;
+ if (is_powerof2(bit1))
+ {
+ compare1_type = vector_compare_match1i;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a | bit1));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit1));
+ }
+ else
+ {
+ compare1_type = vector_compare_match2;
+ bit1 = 0;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char1a));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char1b));
+ }
+ }
+
+instruction[3] = 0xc0 | (cmp1a_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char1a != char1b)
+ {
+ instruction[3] = 0xc0 | (cmp1b_ind << 3) | tmp2_reg_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+if (char2a == char2b)
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a));
+else
+ {
+ bit2 = char2a ^ char2b;
+ if (is_powerof2(bit2))
+ {
+ compare2_type = vector_compare_match1i;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a | bit2));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(bit2));
+ }
+ else
+ {
+ compare2_type = vector_compare_match2;
+ bit2 = 0;
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, character_to_int32(char2a));
+ OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, character_to_int32(char2b));
+ }
+ }
+
+instruction[3] = 0xc0 | (cmp2a_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+if (char2a != char2b)
+ {
+ instruction[3] = 0xc0 | (cmp2b_ind << 3) | tmp2_reg_ind;
+ sljit_emit_op_custom(compiler, instruction, 4);
+ }
+
+/* PSHUFD xmm1, xmm2/m128, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x70;
+instruction[4] = 0;
+
+instruction[3] = 0xc0 | (cmp1a_ind << 3) | cmp1a_ind;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char1a != char1b)
+ {
+ instruction[3] = 0xc0 | (cmp1b_ind << 3) | cmp1b_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+instruction[3] = 0xc0 | (cmp2a_ind << 3) | cmp2a_ind;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+if (char2a != char2b)
+ {
+ instruction[3] = 0xc0 | (cmp2b_ind << 3) | cmp2b_ind;
+ sljit_emit_op_custom(compiler, instruction, 5);
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+
+OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, diff);
+OP1(SLJIT_MOV, TMP2, 0, STR_PTR, 0);
+OP2(SLJIT_AND, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, ~0xf);
+
+load_from_mem_sse2(compiler, data1_ind, str_ptr_reg_ind, 0);
+
+jump[0] = CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_PTR, 0);
+
+load_from_mem_sse2(compiler, data2_ind, str_ptr_reg_ind, -(sljit_s8)diff);
+jump[1] = JUMP(SLJIT_JUMP);
+
+JUMPHERE(jump[0]);
+
+/* MOVDQA xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x6f;
+instruction[3] = 0xc0 | (data2_ind << 3) | data1_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PSLLDQ xmm1, imm8 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0x73;
+instruction[3] = 0xc0 | (7 << 3) | data2_ind;
+instruction[4] = diff;
+sljit_emit_op_custom(compiler, instruction, 5);
+
+JUMPHERE(jump[1]);
+
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xf);
+
+for (i = 0; i < 4; i++)
+ {
+ fast_forward_char_pair_sse2_compare(compiler, compare2_type, i, data2_ind, cmp2a_ind, cmp2b_ind, tmp2_ind);
+ fast_forward_char_pair_sse2_compare(compiler, compare1_type, i, data1_ind, cmp1a_ind, cmp1b_ind, tmp1_ind);
+ }
+
+/* PAND xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xdb;
+instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | 0;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* Ignore matches before the first STR_PTR. */
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, TMP2, 0);
+
+jump[0] = CMP(SLJIT_NOT_ZERO, TMP1, 0, SLJIT_IMM, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+/* Main loop. */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+load_from_mem_sse2(compiler, data1_ind, str_ptr_reg_ind, 0);
+load_from_mem_sse2(compiler, data2_ind, str_ptr_reg_ind, -(sljit_s8)diff);
+
+for (i = 0; i < 4; i++)
+ {
+ fast_forward_char_pair_sse2_compare(compiler, compare1_type, i, data1_ind, cmp1a_ind, cmp1b_ind, tmp2_ind);
+ fast_forward_char_pair_sse2_compare(compiler, compare2_type, i, data2_ind, cmp2a_ind, cmp2b_ind, tmp1_ind);
+ }
+
+/* PAND xmm1, xmm2/m128 */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xdb;
+instruction[3] = 0xc0 | (data1_ind << 3) | data2_ind;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+/* PMOVMSKB reg, xmm */
+/* instruction[0] = 0x66; */
+/* instruction[1] = 0x0f; */
+instruction[2] = 0xd7;
+instruction[3] = 0xc0 | (tmp1_reg_ind << 3) | 0;
+sljit_emit_op_custom(compiler, instruction, 4);
+
+CMPTO(SLJIT_ZERO, TMP1, 0, SLJIT_IMM, 0, start);
+
+JUMPHERE(jump[0]);
+
+/* BSF r32, r/m32 */
+instruction[0] = 0x0f;
+instruction[1] = 0xbc;
+instruction[2] = 0xc0 | (tmp1_reg_ind << 3) | tmp1_reg_ind;
+sljit_emit_op_custom(compiler, instruction, 3);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf)
+ {
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offs1));
+
+ jump[0] = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ CMPTO(SLJIT_LESS, STR_PTR, 0, STR_END, 0, restart);
+
+ add_jump(compiler, &common->failed_match, JUMP(SLJIT_JUMP));
+
+ JUMPHERE(jump[0]);
+ }
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+#endif /* !_WIN64 */
+
+#undef SSE2_COMPARE_TYPE_INDEX
+
+#endif /* SLJIT_CONFIG_X86 */
+
+#if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64 && (defined __ARM_NEON || defined __ARM_NEON__))
+
+#include <arm_neon.h>
+
+typedef union {
+ unsigned int x;
+ struct { unsigned char c1, c2, c3, c4; } c;
+} int_char;
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+static SLJIT_INLINE int utf_continue(PCRE2_SPTR s)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+return (*s & 0xc0) == 0x80;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+return (*s & 0xfc00) == 0xdc00;
+#else
+#error "Unknown code width"
+#endif
+}
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32 */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+# define VECTOR_FACTOR 16
+# define vect_t uint8x16_t
+# define VLD1Q(X) vld1q_u8((sljit_u8 *)(X))
+# define VCEQQ vceqq_u8
+# define VORRQ vorrq_u8
+# define VST1Q vst1q_u8
+# define VDUPQ vdupq_n_u8
+# define VEXTQ vextq_u8
+# define VANDQ vandq_u8
+typedef union {
+ uint8_t mem[16];
+ uint64_t dw[2];
+} quad_word;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+# define VECTOR_FACTOR 8
+# define vect_t uint16x8_t
+# define VLD1Q(X) vld1q_u16((sljit_u16 *)(X))
+# define VCEQQ vceqq_u16
+# define VORRQ vorrq_u16
+# define VST1Q vst1q_u16
+# define VDUPQ vdupq_n_u16
+# define VEXTQ vextq_u16
+# define VANDQ vandq_u16
+typedef union {
+ uint16_t mem[8];
+ uint64_t dw[2];
+} quad_word;
+#else
+# define VECTOR_FACTOR 4
+# define vect_t uint32x4_t
+# define VLD1Q(X) vld1q_u32((sljit_u32 *)(X))
+# define VCEQQ vceqq_u32
+# define VORRQ vorrq_u32
+# define VST1Q vst1q_u32
+# define VDUPQ vdupq_n_u32
+# define VEXTQ vextq_u32
+# define VANDQ vandq_u32
+typedef union {
+ uint32_t mem[4];
+ uint64_t dw[2];
+} quad_word;
+#endif
+
+#define FFCS
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCS
+
+#define FFCS_2
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCS_2
+
+#define FFCS_MASK
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCS_MASK
+
+#define JIT_HAS_FAST_FORWARD_CHAR_SIMD 1
+
+static void fast_forward_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
+{
+DEFINE_COMPILER;
+int_char ic;
+struct sljit_jump *partial_quit;
+/* Save temporary registers. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0);
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS1, TMP3, 0);
+
+/* Prepare function arguments */
+OP1(SLJIT_MOV, SLJIT_R0, 0, STR_END, 0);
+OP1(SLJIT_MOV, SLJIT_R1, 0, STR_PTR, 0);
+OP1(SLJIT_MOV, SLJIT_R2, 0, SLJIT_IMM, offset);
+
+if (char1 == char2)
+ {
+ ic.c.c1 = char1;
+ ic.c.c2 = char2;
+ OP1(SLJIT_MOV, SLJIT_R4, 0, SLJIT_IMM, ic.x);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf && offset > 0)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_utf));
+ else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs));
+#endif
+ }
+else
+ {
+ PCRE2_UCHAR mask = char1 ^ char2;
+ if (is_powerof2(mask))
+ {
+ ic.c.c1 = char1 | mask;
+ ic.c.c2 = mask;
+ OP1(SLJIT_MOV, SLJIT_R4, 0, SLJIT_IMM, ic.x);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf && offset > 0)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_mask_utf));
+ else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_mask));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_mask));
+#endif
+ }
+ else
+ {
+ ic.c.c1 = char1;
+ ic.c.c2 = char2;
+ OP1(SLJIT_MOV, SLJIT_R4, 0, SLJIT_IMM, ic.x);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf && offset > 0)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_2_utf));
+ else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_2));
+#else
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcs_2));
+#endif
+ }
+ }
+/* Restore registers. */
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(SLJIT_SP), LOCALS1);
+
+/* Check return value. */
+partial_quit = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit);
+
+/* Fast forward STR_PTR to the result of memchr. */
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ JUMPHERE(partial_quit);
+}
+
+typedef enum {
+ compare_match1,
+ compare_match1i,
+ compare_match2,
+} compare_type;
+
+static inline vect_t fast_forward_char_pair_compare(compare_type ctype, vect_t dst, vect_t cmp1, vect_t cmp2)
+{
+if (ctype == compare_match2)
+ {
+ vect_t tmp = dst;
+ dst = VCEQQ(dst, cmp1);
+ tmp = VCEQQ(tmp, cmp2);
+ dst = VORRQ(dst, tmp);
+ return dst;
+ }
+
+if (ctype == compare_match1i)
+ dst = VORRQ(dst, cmp2);
+dst = VCEQQ(dst, cmp1);
+return dst;
+}
+
+static SLJIT_INLINE sljit_u32 max_fast_forward_char_pair_offset(void)
+{
+#if PCRE2_CODE_UNIT_WIDTH == 8
+return 15;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+return 7;
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+return 3;
+#else
+#error "Unsupported unit width"
+#endif
+}
+
+/* ARM doesn't have a shift left across lanes. */
+static SLJIT_INLINE vect_t shift_left_n_lanes(vect_t a, sljit_u8 n)
+{
+vect_t zero = VDUPQ(0);
+SLJIT_ASSERT(0 < n && n < VECTOR_FACTOR);
+/* VEXTQ takes an immediate as last argument. */
+#define C(X) case X: return VEXTQ(zero, a, VECTOR_FACTOR - X);
+switch (n)
+ {
+ C(1); C(2); C(3);
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ C(4); C(5); C(6); C(7);
+# if PCRE2_CODE_UNIT_WIDTH != 16
+ C(8); C(9); C(10); C(11); C(12); C(13); C(14); C(15);
+# endif
+#endif
+ default:
+ /* Based on the ASSERT(0 < n && n < VECTOR_FACTOR) above, this won't
+ happen. The return is still here for compilers to not warn. */
+ return a;
+ }
+}
+
+#define FFCPS
+#define FFCPS_DIFF1
+#define FFCPS_CHAR1A2A
+
+#define FFCPS_0
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCPS_0
+
+#undef FFCPS_CHAR1A2A
+
+#define FFCPS_1
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCPS_1
+
+#undef FFCPS_DIFF1
+
+#define FFCPS_DEFAULT
+#error #include "pcre2_jit_neon_inc.h"
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+# define FF_UTF
+# error #include "pcre2_jit_neon_inc.h"
+# undef FF_UTF
+#endif
+#undef FFCPS
+
+#define JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD 1
+
+static void fast_forward_char_pair_simd(compiler_common *common, sljit_s32 offs1,
+ PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b)
+{
+DEFINE_COMPILER;
+sljit_u32 diff = IN_UCHARS(offs1 - offs2);
+struct sljit_jump *partial_quit;
+int_char ic;
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2);
+SLJIT_ASSERT(diff <= IN_UCHARS(max_fast_forward_char_pair_offset()));
+SLJIT_ASSERT(compiler->scratches == 5);
+
+/* Save temporary register STR_PTR. */
+OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_SP), LOCALS0, STR_PTR, 0);
+
+/* Prepare arguments for the function call. */
+if (common->match_end_ptr == 0)
+ OP1(SLJIT_MOV, SLJIT_R0, 0, STR_END, 0);
+else
+ {
+ OP1(SLJIT_MOV, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP2(SLJIT_ADD, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1));
+
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, STR_END, 0, SLJIT_R0, 0);
+ CMOV(SLJIT_LESS, SLJIT_R0, STR_END, 0);
+ }
+
+OP1(SLJIT_MOV, SLJIT_R1, 0, STR_PTR, 0);
+OP1(SLJIT_MOV_S32, SLJIT_R2, 0, SLJIT_IMM, offs1);
+OP1(SLJIT_MOV_S32, SLJIT_R3, 0, SLJIT_IMM, offs2);
+ic.c.c1 = char1a;
+ic.c.c2 = char1b;
+ic.c.c3 = char2a;
+ic.c.c4 = char2b;
+OP1(SLJIT_MOV_U32, SLJIT_R4, 0, SLJIT_IMM, ic.x);
+
+if (diff == 1) {
+ if (char1a == char1b && char2a == char2b) {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_0_utf));
+ else
+#endif
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_0));
+ } else {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_1_utf));
+ else
+#endif
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_1));
+ }
+} else {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+ if (common->utf)
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_default_utf));
+ else
+#endif
+ sljit_emit_icall(compiler, SLJIT_CALL, SLJIT_ARGS4(W, W, W, W, W),
+ SLJIT_IMM, SLJIT_FUNC_ADDR(ffcps_default));
+}
+
+/* Restore STR_PTR register. */
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_SP), LOCALS0);
+
+/* Check return value. */
+partial_quit = CMP(SLJIT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0);
+add_jump(compiler, &common->failed_match, partial_quit);
+
+/* Fast forward STR_PTR to the result of memchr. */
+OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0);
+
+JUMPHERE(partial_quit);
+}
+
+#endif /* SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64 */
+
+#if (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#define VECTOR_ELEMENT_SIZE 0
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+#define VECTOR_ELEMENT_SIZE 1
+#elif PCRE2_CODE_UNIT_WIDTH == 32
+#define VECTOR_ELEMENT_SIZE 2
+#else
+#error "Unsupported unit width"
+#endif
+
+static void load_from_mem_vector(struct sljit_compiler *compiler, BOOL vlbb, sljit_s32 dst_vreg,
+ sljit_s32 base_reg, sljit_s32 index_reg)
+{
+sljit_u16 instruction[3];
+
+instruction[0] = (sljit_u16)(0xe700 | (dst_vreg << 4) | index_reg);
+instruction[1] = (sljit_u16)(base_reg << 12);
+instruction[2] = (sljit_u16)((0x8 << 8) | (vlbb ? 0x07 : 0x06));
+
+sljit_emit_op_custom(compiler, instruction, 6);
+}
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+
+static void replicate_imm_vector(struct sljit_compiler *compiler, int step, sljit_s32 dst_vreg,
+ PCRE2_UCHAR chr, sljit_s32 tmp_general_reg)
+{
+sljit_u16 instruction[3];
+
+SLJIT_ASSERT(step >= 0 && step <= 1);
+
+if (chr < 0x7fff)
+ {
+ if (step == 1)
+ return;
+
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_vreg << 4));
+ instruction[1] = (sljit_u16)chr;
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+ }
+
+if (step == 0)
+ {
+ OP1(SLJIT_MOV, tmp_general_reg, 0, SLJIT_IMM, chr);
+
+ /* VLVG */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_vreg << 4) | sljit_get_register_index(tmp_general_reg));
+ instruction[1] = 0;
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x22);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+ }
+
+/* VREP */
+instruction[0] = (sljit_u16)(0xe700 | (dst_vreg << 4) | dst_vreg);
+instruction[1] = 0;
+instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xc << 8) | 0x4d);
+sljit_emit_op_custom(compiler, instruction, 6);
+}
+
+#endif
+
+static void fast_forward_char_pair_sse2_compare(struct sljit_compiler *compiler, vector_compare_type compare_type,
+ int step, sljit_s32 dst_ind, sljit_s32 cmp1_ind, sljit_s32 cmp2_ind, sljit_s32 tmp_ind)
+{
+sljit_u16 instruction[3];
+
+SLJIT_ASSERT(step >= 0 && step <= 2);
+
+if (step == 1)
+ {
+ /* VCEQ */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_ind << 4) | dst_ind);
+ instruction[1] = (sljit_u16)(cmp1_ind << 12);
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0xf8);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+ }
+
+if (compare_type != vector_compare_match2)
+ {
+ if (step == 0 && compare_type == vector_compare_match1i)
+ {
+ /* VO */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_ind << 4) | dst_ind);
+ instruction[1] = (sljit_u16)(cmp2_ind << 12);
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x6a);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+ return;
+ }
+
+switch (step)
+ {
+ case 0:
+ /* VCEQ */
+ instruction[0] = (sljit_u16)(0xe700 | (tmp_ind << 4) | dst_ind);
+ instruction[1] = (sljit_u16)(cmp2_ind << 12);
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0xf8);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+
+ case 2:
+ /* VO */
+ instruction[0] = (sljit_u16)(0xe700 | (dst_ind << 4) | dst_ind);
+ instruction[1] = (sljit_u16)(tmp_ind << 12);
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x6a);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ return;
+ }
+}
+
+#define JIT_HAS_FAST_FORWARD_CHAR_SIMD 1
+
+static void fast_forward_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2, sljit_s32 offset)
+{
+DEFINE_COMPILER;
+sljit_u16 instruction[3];
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *quit;
+struct sljit_jump *partial_quit[2];
+vector_compare_type compare_type = vector_compare_match1;
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_s32 zero_ind = 4;
+sljit_u32 bit = 0;
+int i;
+
+SLJIT_UNUSED_ARG(offset);
+
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ compare_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit))
+ {
+ bit = 0;
+ compare_type = vector_compare_match2;
+ }
+ }
+
+partial_quit[0] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[0]);
+
+/* First part (unaligned start) */
+
+OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 16);
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (cmp1_ind << 4));
+instruction[1] = (sljit_u16)(char1 | bit);
+instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+if (char1 != char2)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (cmp2_ind << 4));
+ instruction[1] = (sljit_u16)(bit != 0 ? bit : char2);
+ /* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+#else /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+for (int i = 0; i < 2; i++)
+ {
+ replicate_imm_vector(compiler, i, cmp1_ind, char1 | bit, TMP1);
+
+ if (char1 != char2)
+ replicate_imm_vector(compiler, i, cmp2_ind, bit != 0 ? bit : char2, TMP1);
+ }
+
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+if (compare_type == vector_compare_match2)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (zero_ind << 4));
+ instruction[1] = 0;
+ instruction[2] = (sljit_u16)((0x8 << 8) | 0x45);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+
+load_from_mem_vector(compiler, TRUE, data_ind, str_ptr_reg_ind, 0);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, ~15);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (compare_type == vector_compare_match1i)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, 0, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFEE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((cmp1_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0x80);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+else
+ {
+ for (i = 0; i < 3; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFENE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp1_reg_ind << 4) | data_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+quit = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, TMP2, 0, SLJIT_IMM, 16);
+
+/* Second part (aligned) */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+
+partial_quit[1] = CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0);
+if (common->mode == PCRE2_JIT_COMPLETE)
+ add_jump(compiler, &common->failed_match, partial_quit[1]);
+
+load_from_mem_vector(compiler, TRUE, data_ind, str_ptr_reg_ind, 0);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (compare_type == vector_compare_match1i)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, 0, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFEE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((cmp1_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0x80);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+else
+ {
+ for (i = 0; i < 3; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFENE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+sljit_set_current_flags(compiler, SLJIT_SET_OVERFLOW);
+JUMPTO(SLJIT_OVERFLOW, start);
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp1_reg_ind << 4) | data_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+
+JUMPHERE(quit);
+
+if (common->mode != PCRE2_JIT_COMPLETE)
+ {
+ JUMPHERE(partial_quit[0]);
+ JUMPHERE(partial_quit[1]);
+ OP2U(SLJIT_SUB | SLJIT_SET_GREATER, STR_PTR, 0, STR_END, 0);
+ CMOV(SLJIT_GREATER, STR_PTR, STR_END, 0);
+ }
+else
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf && offset > 0)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offset));
+
+ quit = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, 16);
+ JUMPTO(SLJIT_JUMP, restart);
+
+ JUMPHERE(quit);
+ }
+#endif
+}
+
+#define JIT_HAS_FAST_REQUESTED_CHAR_SIMD 1
+
+static jump_list *fast_requested_char_simd(compiler_common *common, PCRE2_UCHAR char1, PCRE2_UCHAR char2)
+{
+DEFINE_COMPILER;
+sljit_u16 instruction[3];
+struct sljit_label *start;
+struct sljit_jump *quit;
+jump_list *not_found = NULL;
+vector_compare_type compare_type = vector_compare_match1;
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 tmp3_reg_ind = sljit_get_register_index(TMP3);
+sljit_s32 data_ind = 0;
+sljit_s32 tmp_ind = 1;
+sljit_s32 cmp1_ind = 2;
+sljit_s32 cmp2_ind = 3;
+sljit_s32 zero_ind = 4;
+sljit_u32 bit = 0;
+int i;
+
+if (char1 != char2)
+ {
+ bit = char1 ^ char2;
+ compare_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit))
+ {
+ bit = 0;
+ compare_type = vector_compare_match2;
+ }
+ }
+
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+/* First part (unaligned start) */
+
+OP2(SLJIT_ADD, TMP2, 0, TMP1, 0, SLJIT_IMM, 16);
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (cmp1_ind << 4));
+instruction[1] = (sljit_u16)(char1 | bit);
+instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+if (char1 != char2)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (cmp2_ind << 4));
+ instruction[1] = (sljit_u16)(bit != 0 ? bit : char2);
+ /* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+#else /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+for (int i = 0; i < 2; i++)
+ {
+ replicate_imm_vector(compiler, i, cmp1_ind, char1 | bit, TMP3);
+
+ if (char1 != char2)
+ replicate_imm_vector(compiler, i, cmp2_ind, bit != 0 ? bit : char2, TMP3);
+ }
+
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+if (compare_type == vector_compare_match2)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (zero_ind << 4));
+ instruction[1] = 0;
+ instruction[2] = (sljit_u16)((0x8 << 8) | 0x45);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+load_from_mem_vector(compiler, TRUE, data_ind, tmp1_reg_ind, 0);
+OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, ~15);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (compare_type == vector_compare_match1i)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, 0, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFEE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((cmp1_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0x80);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+else
+ {
+ for (i = 0; i < 3; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFENE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp3_reg_ind << 4) | data_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP3, 0);
+quit = CMP(SLJIT_LESS, TMP1, 0, TMP2, 0);
+
+OP2(SLJIT_SUB, TMP1, 0, TMP2, 0, SLJIT_IMM, 16);
+
+/* Second part (aligned) */
+start = LABEL();
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 16);
+
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+load_from_mem_vector(compiler, TRUE, data_ind, tmp1_reg_ind, 0);
+
+if (compare_type != vector_compare_match2)
+ {
+ if (compare_type == vector_compare_match1i)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, 0, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFEE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((cmp1_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0xe << 8) | 0x80);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+else
+ {
+ for (i = 0; i < 3; i++)
+ fast_forward_char_pair_sse2_compare(compiler, compare_type, i, data_ind, cmp1_ind, cmp2_ind, tmp_ind);
+
+ /* VFENE */
+ instruction[0] = (sljit_u16)(0xe700 | (data_ind << 4) | data_ind);
+ instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+ instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+sljit_set_current_flags(compiler, SLJIT_SET_OVERFLOW);
+JUMPTO(SLJIT_OVERFLOW, start);
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp3_reg_ind << 4) | data_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP3, 0);
+
+JUMPHERE(quit);
+add_jump(compiler, &not_found, CMP(SLJIT_GREATER_EQUAL, TMP1, 0, STR_END, 0));
+
+return not_found;
+}
+
+#define JIT_HAS_FAST_FORWARD_CHAR_PAIR_SIMD 1
+
+static void fast_forward_char_pair_simd(compiler_common *common, sljit_s32 offs1,
+ PCRE2_UCHAR char1a, PCRE2_UCHAR char1b, sljit_s32 offs2, PCRE2_UCHAR char2a, PCRE2_UCHAR char2b)
+{
+DEFINE_COMPILER;
+sljit_u16 instruction[3];
+struct sljit_label *start;
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+struct sljit_label *restart;
+#endif
+struct sljit_jump *quit;
+struct sljit_jump *jump[2];
+vector_compare_type compare1_type = vector_compare_match1;
+vector_compare_type compare2_type = vector_compare_match1;
+sljit_u32 bit1 = 0;
+sljit_u32 bit2 = 0;
+sljit_s32 diff = IN_UCHARS(offs2 - offs1);
+sljit_s32 tmp1_reg_ind = sljit_get_register_index(TMP1);
+sljit_s32 tmp2_reg_ind = sljit_get_register_index(TMP2);
+sljit_s32 str_ptr_reg_ind = sljit_get_register_index(STR_PTR);
+sljit_s32 data1_ind = 0;
+sljit_s32 data2_ind = 1;
+sljit_s32 tmp1_ind = 2;
+sljit_s32 tmp2_ind = 3;
+sljit_s32 cmp1a_ind = 4;
+sljit_s32 cmp1b_ind = 5;
+sljit_s32 cmp2a_ind = 6;
+sljit_s32 cmp2b_ind = 7;
+sljit_s32 zero_ind = 8;
+int i;
+
+SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE && offs1 > offs2);
+SLJIT_ASSERT(-diff <= (sljit_s32)IN_UCHARS(max_fast_forward_char_pair_offset()));
+SLJIT_ASSERT(tmp1_reg_ind != 0 && tmp2_reg_ind != 0);
+
+if (char1a != char1b)
+ {
+ bit1 = char1a ^ char1b;
+ compare1_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit1))
+ {
+ bit1 = 0;
+ compare1_type = vector_compare_match2;
+ }
+ }
+
+if (char2a != char2b)
+ {
+ bit2 = char2a ^ char2b;
+ compare2_type = vector_compare_match1i;
+
+ if (!is_powerof2(bit2))
+ {
+ bit2 = 0;
+ compare2_type = vector_compare_match2;
+ }
+ }
+
+/* Initialize. */
+if (common->match_end_ptr != 0)
+ {
+ OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_SP), common->match_end_ptr);
+ OP1(SLJIT_MOV, TMP3, 0, STR_END, 0);
+ OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(offs1 + 1));
+
+ OP2U(SLJIT_SUB | SLJIT_SET_LESS, TMP1, 0, STR_END, 0);
+ CMOV(SLJIT_LESS, STR_END, TMP1, 0);
+ }
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, ~15);
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+
+OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, -diff);
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (cmp1a_ind << 4));
+instruction[1] = (sljit_u16)(char1a | bit1);
+instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+if (char1a != char1b)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (cmp1b_ind << 4));
+ instruction[1] = (sljit_u16)(bit1 != 0 ? bit1 : char1b);
+ /* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (cmp2a_ind << 4));
+instruction[1] = (sljit_u16)(char2a | bit2);
+/* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+sljit_emit_op_custom(compiler, instruction, 6);
+
+if (char2a != char2b)
+ {
+ /* VREPI */
+ instruction[0] = (sljit_u16)(0xe700 | (cmp2b_ind << 4));
+ instruction[1] = (sljit_u16)(bit2 != 0 ? bit2 : char2b);
+ /* instruction[2] = (sljit_u16)((VECTOR_ELEMENT_SIZE << 12) | (0x8 << 8) | 0x45); */
+ sljit_emit_op_custom(compiler, instruction, 6);
+ }
+
+#else /* PCRE2_CODE_UNIT_WIDTH == 32 */
+
+for (int i = 0; i < 2; i++)
+ {
+ replicate_imm_vector(compiler, i, cmp1a_ind, char1a | bit1, TMP1);
+
+ if (char1a != char1b)
+ replicate_imm_vector(compiler, i, cmp1b_ind, bit1 != 0 ? bit1 : char1b, TMP1);
+
+ replicate_imm_vector(compiler, i, cmp2a_ind, char2a | bit2, TMP1);
+
+ if (char2a != char2b)
+ replicate_imm_vector(compiler, i, cmp2b_ind, bit2 != 0 ? bit2 : char2b, TMP1);
+ }
+
+OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, -diff);
+
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+/* VREPI */
+instruction[0] = (sljit_u16)(0xe700 | (zero_ind << 4));
+instruction[1] = 0;
+instruction[2] = (sljit_u16)((0x8 << 8) | 0x45);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+restart = LABEL();
+#endif
+
+jump[0] = CMP(SLJIT_LESS, TMP1, 0, TMP2, 0);
+load_from_mem_vector(compiler, TRUE, data2_ind, tmp1_reg_ind, 0);
+jump[1] = JUMP(SLJIT_JUMP);
+JUMPHERE(jump[0]);
+load_from_mem_vector(compiler, FALSE, data2_ind, tmp1_reg_ind, 0);
+JUMPHERE(jump[1]);
+
+load_from_mem_vector(compiler, TRUE, data1_ind, str_ptr_reg_ind, 0);
+OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, 16);
+
+for (i = 0; i < 3; i++)
+ {
+ fast_forward_char_pair_sse2_compare(compiler, compare1_type, i, data1_ind, cmp1a_ind, cmp1b_ind, tmp1_ind);
+ fast_forward_char_pair_sse2_compare(compiler, compare2_type, i, data2_ind, cmp2a_ind, cmp2b_ind, tmp2_ind);
+ }
+
+/* VN */
+instruction[0] = (sljit_u16)(0xe700 | (data1_ind << 4) | data1_ind);
+instruction[1] = (sljit_u16)(data2_ind << 12);
+instruction[2] = (sljit_u16)((0xe << 8) | 0x68);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+/* VFENE */
+instruction[0] = (sljit_u16)(0xe700 | (data1_ind << 4) | data1_ind);
+instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp1_reg_ind << 4) | data1_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0);
+quit = CMP(SLJIT_LESS, STR_PTR, 0, TMP2, 0);
+
+OP2(SLJIT_SUB, STR_PTR, 0, TMP2, 0, SLJIT_IMM, 16);
+OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, diff);
+
+/* Main loop. */
+start = LABEL();
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, 16);
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+load_from_mem_vector(compiler, FALSE, data1_ind, str_ptr_reg_ind, 0);
+load_from_mem_vector(compiler, FALSE, data2_ind, str_ptr_reg_ind, tmp1_reg_ind);
+
+for (i = 0; i < 3; i++)
+ {
+ fast_forward_char_pair_sse2_compare(compiler, compare1_type, i, data1_ind, cmp1a_ind, cmp1b_ind, tmp1_ind);
+ fast_forward_char_pair_sse2_compare(compiler, compare2_type, i, data2_ind, cmp2a_ind, cmp2b_ind, tmp2_ind);
+ }
+
+/* VN */
+instruction[0] = (sljit_u16)(0xe700 | (data1_ind << 4) | data1_ind);
+instruction[1] = (sljit_u16)(data2_ind << 12);
+instruction[2] = (sljit_u16)((0xe << 8) | 0x68);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+/* VFENE */
+instruction[0] = (sljit_u16)(0xe700 | (data1_ind << 4) | data1_ind);
+instruction[1] = (sljit_u16)((zero_ind << 12) | (1 << 4));
+instruction[2] = (sljit_u16)((0xe << 8) | 0x81);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+sljit_set_current_flags(compiler, SLJIT_SET_OVERFLOW);
+JUMPTO(SLJIT_OVERFLOW, start);
+
+/* VLGVB */
+instruction[0] = (sljit_u16)(0xe700 | (tmp2_reg_ind << 4) | data1_ind);
+instruction[1] = 7;
+instruction[2] = (sljit_u16)((0x4 << 8) | 0x21);
+sljit_emit_op_custom(compiler, instruction, 6);
+
+OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0);
+
+JUMPHERE(quit);
+
+add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH != 32
+if (common->utf)
+ {
+ SLJIT_ASSERT(common->mode == PCRE2_JIT_COMPLETE);
+
+ OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-offs1));
+
+ quit = jump_if_utf_char_start(compiler, TMP1);
+
+ OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1));
+ add_jump(compiler, &common->failed_match, CMP(SLJIT_GREATER_EQUAL, STR_PTR, 0, STR_END, 0));
+
+ /* TMP1 contains diff. */
+ OP2(SLJIT_AND, TMP2, 0, STR_PTR, 0, SLJIT_IMM, ~15);
+ OP2(SLJIT_SUB, TMP1, 0, STR_PTR, 0, SLJIT_IMM, -diff);
+ JUMPTO(SLJIT_JUMP, restart);
+
+ JUMPHERE(quit);
+ }
+#endif
+
+OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(offs1));
+
+if (common->match_end_ptr != 0)
+ OP1(SLJIT_MOV, STR_END, 0, TMP3, 0);
+}
+
+#endif /* SLJIT_CONFIG_S390X */
+
+#endif /* !SUPPORT_VALGRIND */
diff --git a/contrib/libs/pcre2/src/pcre2_maketables.c b/contrib/libs/pcre2/src/pcre2_maketables.c
new file mode 100644
index 0000000000..3a4c47d00a
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_maketables.c
@@ -0,0 +1,163 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2020 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains the external function pcre2_maketables(), which builds
+character tables for PCRE2 in the current locale. The file is compiled on its
+own as part of the PCRE2 library. It is also included in the compilation of
+pcre2_dftables.c as a freestanding program, in which case the macro
+PCRE2_DFTABLES is defined. */
+
+#ifndef PCRE2_DFTABLES /* Compiling the library */
+# ifdef HAVE_CONFIG_H
+# include <pcre2_config.h>
+# endif
+# include "pcre2_internal.h"
+#endif
+
+
+
+/*************************************************
+* Create PCRE2 character tables *
+*************************************************/
+
+/* This function builds a set of character tables for use by PCRE2 and returns
+a pointer to them. They are build using the ctype functions, and consequently
+their contents will depend upon the current locale setting. When compiled as
+part of the library, the store is obtained via a general context malloc, if
+supplied, but when PCRE2_DFTABLES is defined (when compiling the pcre2_dftables
+freestanding auxiliary program) malloc() is used, and the function has a
+different name so as not to clash with the prototype in pcre2.h.
+
+Arguments: none when PCRE2_DFTABLES is defined
+ else a PCRE2 general context or NULL
+Returns: pointer to the contiguous block of data
+ else NULL if memory allocation failed
+*/
+
+#ifdef PCRE2_DFTABLES /* Included in freestanding pcre2_dftables program */
+static const uint8_t *maketables(void)
+{
+uint8_t *yield = (uint8_t *)malloc(TABLES_LENGTH);
+
+#else /* Not PCRE2_DFTABLES, that is, compiling the library */
+PCRE2_EXP_DEFN const uint8_t * PCRE2_CALL_CONVENTION
+pcre2_maketables(pcre2_general_context *gcontext)
+{
+uint8_t *yield = (uint8_t *)((gcontext != NULL)?
+ gcontext->memctl.malloc(TABLES_LENGTH, gcontext->memctl.memory_data) :
+ malloc(TABLES_LENGTH));
+#endif /* PCRE2_DFTABLES */
+
+int i;
+uint8_t *p;
+
+if (yield == NULL) return NULL;
+p = yield;
+
+/* First comes the lower casing table */
+
+for (i = 0; i < 256; i++) *p++ = tolower(i);
+
+/* Next the case-flipping table */
+
+for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i);
+
+/* Then the character class tables. Don't try to be clever and save effort on
+exclusive ones - in some locales things may be different.
+
+Note that the table for "space" includes everything "isspace" gives, including
+VT in the default locale. This makes it work for the POSIX class [:space:].
+From PCRE1 release 8.34 and for all PCRE2 releases it is also correct for Perl
+space, because Perl added VT at release 5.18.
+
+Note also that it is possible for a character to be alnum or alpha without
+being lower or upper, such as "male and female ordinals" (\xAA and \xBA) in the
+fr_FR locale (at least under Debian Linux's locales as of 12/2005). So we must
+test for alnum specially. */
+
+memset(p, 0, cbit_length);
+for (i = 0; i < 256; i++)
+ {
+ if (isdigit(i)) p[cbit_digit + i/8] |= 1u << (i&7);
+ if (isupper(i)) p[cbit_upper + i/8] |= 1u << (i&7);
+ if (islower(i)) p[cbit_lower + i/8] |= 1u << (i&7);
+ if (isalnum(i)) p[cbit_word + i/8] |= 1u << (i&7);
+ if (i == '_') p[cbit_word + i/8] |= 1u << (i&7);
+ if (isspace(i)) p[cbit_space + i/8] |= 1u << (i&7);
+ if (isxdigit(i)) p[cbit_xdigit + i/8] |= 1u << (i&7);
+ if (isgraph(i)) p[cbit_graph + i/8] |= 1u << (i&7);
+ if (isprint(i)) p[cbit_print + i/8] |= 1u << (i&7);
+ if (ispunct(i)) p[cbit_punct + i/8] |= 1u << (i&7);
+ if (iscntrl(i)) p[cbit_cntrl + i/8] |= 1u << (i&7);
+ }
+p += cbit_length;
+
+/* Finally, the character type table. In this, we used to exclude VT from the
+white space chars, because Perl didn't recognize it as such for \s and for
+comments within regexes. However, Perl changed at release 5.18, so PCRE1
+changed at release 8.34 and it's always been this way for PCRE2. */
+
+for (i = 0; i < 256; i++)
+ {
+ int x = 0;
+ if (isspace(i)) x += ctype_space;
+ if (isalpha(i)) x += ctype_letter;
+ if (islower(i)) x += ctype_lcletter;
+ if (isdigit(i)) x += ctype_digit;
+ if (isalnum(i) || i == '_') x += ctype_word;
+ *p++ = x;
+ }
+
+return yield;
+}
+
+#ifndef PCRE2_DFTABLES /* Compiling the library */
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_maketables_free(pcre2_general_context *gcontext, const uint8_t *tables)
+{
+ if (gcontext)
+ gcontext->memctl.free((void *)tables, gcontext->memctl.memory_data);
+ else
+ free((void *)tables);
+}
+#endif
+
+/* End of pcre2_maketables.c */
diff --git a/contrib/libs/pcre2/src/pcre2_match.c b/contrib/libs/pcre2/src/pcre2_match.c
new file mode 100644
index 0000000000..80255bbe17
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_match.c
@@ -0,0 +1,7544 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2015-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+/* These defines enable debugging code */
+
+/* #define DEBUG_FRAMES_DISPLAY */
+/* #define DEBUG_SHOW_OPS */
+/* #define DEBUG_SHOW_RMATCH */
+
+#ifdef DEBUG_FRAMES_DISPLAY
+#include <stdarg.h>
+#endif
+
+/* These defines identify the name of the block containing "static"
+information, and fields within it. */
+
+#define NLBLOCK mb /* Block containing newline information */
+#define PSSTART start_subject /* Field containing processed string start */
+#define PSEND end_subject /* Field containing processed string end */
+
+#include "pcre2_internal.h"
+
+#define RECURSE_UNSET 0xffffffffu /* Bigger than max group number */
+
+/* Masks for identifying the public options that are permitted at match time. */
+
+#define PUBLIC_MATCH_OPTIONS \
+ (PCRE2_ANCHORED|PCRE2_ENDANCHORED|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY| \
+ PCRE2_NOTEMPTY_ATSTART|PCRE2_NO_UTF_CHECK|PCRE2_PARTIAL_HARD| \
+ PCRE2_PARTIAL_SOFT|PCRE2_NO_JIT|PCRE2_COPY_MATCHED_SUBJECT)
+
+#define PUBLIC_JIT_MATCH_OPTIONS \
+ (PCRE2_NO_UTF_CHECK|PCRE2_NOTBOL|PCRE2_NOTEOL|PCRE2_NOTEMPTY|\
+ PCRE2_NOTEMPTY_ATSTART|PCRE2_PARTIAL_SOFT|PCRE2_PARTIAL_HARD|\
+ PCRE2_COPY_MATCHED_SUBJECT)
+
+/* Non-error returns from and within the match() function. Error returns are
+externally defined PCRE2_ERROR_xxx codes, which are all negative. */
+
+#define MATCH_MATCH 1
+#define MATCH_NOMATCH 0
+
+/* Special internal returns used in the match() function. Make them
+sufficiently negative to avoid the external error codes. */
+
+#define MATCH_ACCEPT (-999)
+#define MATCH_KETRPOS (-998)
+/* The next 5 must be kept together and in sequence so that a test that checks
+for any one of them can use a range. */
+#define MATCH_COMMIT (-997)
+#define MATCH_PRUNE (-996)
+#define MATCH_SKIP (-995)
+#define MATCH_SKIP_ARG (-994)
+#define MATCH_THEN (-993)
+#define MATCH_BACKTRACK_MAX MATCH_THEN
+#define MATCH_BACKTRACK_MIN MATCH_COMMIT
+
+/* Group frame type values. Zero means the frame is not a group frame. The
+lower 16 bits are used for data (e.g. the capture number). Group frames are
+used for most groups so that information about the start is easily available at
+the end without having to scan back through intermediate frames (backtrack
+points). */
+
+#define GF_CAPTURE 0x00010000u
+#define GF_NOCAPTURE 0x00020000u
+#define GF_CONDASSERT 0x00030000u
+#define GF_RECURSE 0x00040000u
+
+/* Masks for the identity and data parts of the group frame type. */
+
+#define GF_IDMASK(a) ((a) & 0xffff0000u)
+#define GF_DATAMASK(a) ((a) & 0x0000ffffu)
+
+/* Repetition types */
+
+enum { REPTYPE_MIN, REPTYPE_MAX, REPTYPE_POS };
+
+/* Min and max values for the common repeats; a maximum of UINT32_MAX =>
+infinity. */
+
+static const uint32_t rep_min[] = {
+ 0, 0, /* * and *? */
+ 1, 1, /* + and +? */
+ 0, 0, /* ? and ?? */
+ 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */
+ 0, 1, 0 }; /* OP_CRPOS{STAR, PLUS, QUERY} */
+
+static const uint32_t rep_max[] = {
+ UINT32_MAX, UINT32_MAX, /* * and *? */
+ UINT32_MAX, UINT32_MAX, /* + and +? */
+ 1, 1, /* ? and ?? */
+ 0, 0, /* dummy placefillers for OP_CR[MIN]RANGE */
+ UINT32_MAX, UINT32_MAX, 1 }; /* OP_CRPOS{STAR, PLUS, QUERY} */
+
+/* Repetition types - must include OP_CRPOSRANGE (not needed above) */
+
+static const uint32_t rep_typ[] = {
+ REPTYPE_MAX, REPTYPE_MIN, /* * and *? */
+ REPTYPE_MAX, REPTYPE_MIN, /* + and +? */
+ REPTYPE_MAX, REPTYPE_MIN, /* ? and ?? */
+ REPTYPE_MAX, REPTYPE_MIN, /* OP_CRRANGE and OP_CRMINRANGE */
+ REPTYPE_POS, REPTYPE_POS, /* OP_CRPOSSTAR, OP_CRPOSPLUS */
+ REPTYPE_POS, REPTYPE_POS }; /* OP_CRPOSQUERY, OP_CRPOSRANGE */
+
+/* Numbers for RMATCH calls at backtracking points. When these lists are
+changed, the code at RETURN_SWITCH below must be updated in sync. */
+
+enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10,
+ RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20,
+ RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30,
+ RM31, RM32, RM33, RM34, RM35, RM36 };
+
+#ifdef SUPPORT_WIDE_CHARS
+enum { RM100=100, RM101 };
+#endif
+
+#ifdef SUPPORT_UNICODE
+enum { RM200=200, RM201, RM202, RM203, RM204, RM205, RM206, RM207,
+ RM208, RM209, RM210, RM211, RM212, RM213, RM214, RM215,
+ RM216, RM217, RM218, RM219, RM220, RM221, RM222, RM223,
+ RM224, RM225 };
+#endif
+
+/* Define short names for general fields in the current backtrack frame, which
+is always pointed to by the F variable. Occasional references to fields in
+other frames are written out explicitly. There are also some fields in the
+current frame whose names start with "temp" that are used for short-term,
+localised backtracking memory. These are #defined with Lxxx names at the point
+of use and undefined afterwards. */
+
+#define Fback_frame F->back_frame
+#define Fcapture_last F->capture_last
+#define Fcurrent_recurse F->current_recurse
+#define Fecode F->ecode
+#define Feptr F->eptr
+#define Fgroup_frame_type F->group_frame_type
+#define Flast_group_offset F->last_group_offset
+#define Flength F->length
+#define Fmark F->mark
+#define Frdepth F->rdepth
+#define Fstart_match F->start_match
+#define Foffset_top F->offset_top
+#define Foccu F->occu
+#define Fop F->op
+#define Fovector F->ovector
+#define Freturn_id F->return_id
+
+
+#ifdef DEBUG_FRAMES_DISPLAY
+/*************************************************
+* Display current frames and contents *
+*************************************************/
+
+/* This debugging function displays the current set of frames and their
+contents. It is not called automatically from anywhere, the intention being
+that calls can be inserted where necessary when debugging frame-related
+problems.
+
+Arguments:
+ f the file to write to
+ F the current top frame
+ P a previous frame of interest
+ frame_size the frame size
+ mb points to the match block
+ match_data points to the match data block
+ s identification text
+
+Returns: nothing
+*/
+
+static void
+display_frames(FILE *f, heapframe *F, heapframe *P, PCRE2_SIZE frame_size,
+ match_block *mb, pcre2_match_data *match_data, const char *s, ...)
+{
+uint32_t i;
+heapframe *Q;
+va_list ap;
+va_start(ap, s);
+
+fprintf(f, "FRAMES ");
+vfprintf(f, s, ap);
+va_end(ap);
+
+if (P != NULL) fprintf(f, " P=%lu",
+ ((char *)P - (char *)(match_data->heapframes))/frame_size);
+fprintf(f, "\n");
+
+for (i = 0, Q = match_data->heapframes;
+ Q <= F;
+ i++, Q = (heapframe *)((char *)Q + frame_size))
+ {
+ fprintf(f, "Frame %d type=%x subj=%lu code=%d back=%lu id=%d",
+ i, Q->group_frame_type, Q->eptr - mb->start_subject, *(Q->ecode),
+ Q->back_frame, Q->return_id);
+
+ if (Q->last_group_offset == PCRE2_UNSET)
+ fprintf(f, " lgoffset=unset\n");
+ else
+ fprintf(f, " lgoffset=%lu\n", Q->last_group_offset/frame_size);
+ }
+}
+
+#endif
+
+
+
+/*************************************************
+* Process a callout *
+*************************************************/
+
+/* This function is called for all callouts, whether "standalone" or at the
+start of a conditional group. Feptr will be pointing to either OP_CALLOUT or
+OP_CALLOUT_STR. A callout block is allocated in pcre2_match() and initialized
+with fixed values.
+
+Arguments:
+ F points to the current backtracking frame
+ mb points to the match block
+ lengthptr where to return the length of the callout item
+
+Returns: the return from the callout
+ or 0 if no callout function exists
+*/
+
+static int
+do_callout(heapframe *F, match_block *mb, PCRE2_SIZE *lengthptr)
+{
+int rc;
+PCRE2_SIZE save0, save1;
+PCRE2_SIZE *callout_ovector;
+pcre2_callout_block *cb;
+
+*lengthptr = (*Fecode == OP_CALLOUT)?
+ PRIV(OP_lengths)[OP_CALLOUT] : GET(Fecode, 1 + 2*LINK_SIZE);
+
+if (mb->callout == NULL) return 0; /* No callout function provided */
+
+/* The original matching code (pre 10.30) worked directly with the ovector
+passed by the user, and this was passed to callouts. Now that the working
+ovector is in the backtracking frame, it no longer needs to reserve space for
+the overall match offsets (which would waste space in the frame). For backward
+compatibility, however, we pass capture_top and offset_vector to the callout as
+if for the extended ovector, and we ensure that the first two slots are unset
+by preserving and restoring their current contents. Picky compilers complain if
+references such as Fovector[-2] are use directly, so we set up a separate
+pointer. */
+
+callout_ovector = (PCRE2_SIZE *)(Fovector) - 2;
+
+/* The cb->version, cb->subject, cb->subject_length, and cb->start_match fields
+are set externally. The first 3 never change; the last is updated for each
+bumpalong. */
+
+cb = mb->cb;
+cb->capture_top = (uint32_t)Foffset_top/2 + 1;
+cb->capture_last = Fcapture_last;
+cb->offset_vector = callout_ovector;
+cb->mark = mb->nomatch_mark;
+cb->current_position = (PCRE2_SIZE)(Feptr - mb->start_subject);
+cb->pattern_position = GET(Fecode, 1);
+cb->next_item_length = GET(Fecode, 1 + LINK_SIZE);
+
+if (*Fecode == OP_CALLOUT) /* Numerical callout */
+ {
+ cb->callout_number = Fecode[1 + 2*LINK_SIZE];
+ cb->callout_string_offset = 0;
+ cb->callout_string = NULL;
+ cb->callout_string_length = 0;
+ }
+else /* String callout */
+ {
+ cb->callout_number = 0;
+ cb->callout_string_offset = GET(Fecode, 1 + 3*LINK_SIZE);
+ cb->callout_string = Fecode + (1 + 4*LINK_SIZE) + 1;
+ cb->callout_string_length =
+ *lengthptr - (1 + 4*LINK_SIZE) - 2;
+ }
+
+save0 = callout_ovector[0];
+save1 = callout_ovector[1];
+callout_ovector[0] = callout_ovector[1] = PCRE2_UNSET;
+rc = mb->callout(cb, mb->callout_data);
+callout_ovector[0] = save0;
+callout_ovector[1] = save1;
+cb->callout_flags = 0;
+return rc;
+}
+
+
+
+/*************************************************
+* Match a back-reference *
+*************************************************/
+
+/* This function is called only when it is known that the offset lies within
+the offsets that have so far been used in the match. Note that in caseless
+UTF-8 mode, the number of subject bytes matched may be different to the number
+of reference bytes. (In theory this could also happen in UTF-16 mode, but it
+seems unlikely.)
+
+Arguments:
+ offset index into the offset vector
+ caseless TRUE if caseless
+ F the current backtracking frame pointer
+ mb points to match block
+ lengthptr pointer for returning the length matched
+
+Returns: = 0 sucessful match; number of code units matched is set
+ < 0 no match
+ > 0 partial match
+*/
+
+static int
+match_ref(PCRE2_SIZE offset, BOOL caseless, heapframe *F, match_block *mb,
+ PCRE2_SIZE *lengthptr)
+{
+PCRE2_SPTR p;
+PCRE2_SIZE length;
+PCRE2_SPTR eptr;
+PCRE2_SPTR eptr_start;
+
+/* Deal with an unset group. The default is no match, but there is an option to
+match an empty string. */
+
+if (offset >= Foffset_top || Fovector[offset] == PCRE2_UNSET)
+ {
+ if ((mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)
+ {
+ *lengthptr = 0;
+ return 0; /* Match */
+ }
+ else return -1; /* No match */
+ }
+
+/* Separate the caseless and UTF cases for speed. */
+
+eptr = eptr_start = Feptr;
+p = mb->start_subject + Fovector[offset];
+length = Fovector[offset+1] - Fovector[offset];
+
+if (caseless)
+ {
+#if defined SUPPORT_UNICODE
+ BOOL utf = (mb->poptions & PCRE2_UTF) != 0;
+
+ if (utf || (mb->poptions & PCRE2_UCP) != 0)
+ {
+ PCRE2_SPTR endptr = p + length;
+
+ /* Match characters up to the end of the reference. NOTE: the number of
+ code units matched may differ, because in UTF-8 there are some characters
+ whose upper and lower case codes have different numbers of bytes. For
+ example, U+023A (2 bytes in UTF-8) is the upper case version of U+2C65 (3
+ bytes in UTF-8); a sequence of 3 of the former uses 6 bytes, as does a
+ sequence of two of the latter. It is important, therefore, to check the
+ length along the reference, not along the subject (earlier code did this
+ wrong). UCP without uses Unicode properties but without UTF encoding. */
+
+ while (p < endptr)
+ {
+ uint32_t c, d;
+ const ucd_record *ur;
+ if (eptr >= mb->end_subject) return 1; /* Partial match */
+
+ if (utf)
+ {
+ GETCHARINC(c, eptr);
+ GETCHARINC(d, p);
+ }
+ else
+ {
+ c = *eptr++;
+ d = *p++;
+ }
+
+ ur = GET_UCD(d);
+ if (c != d && c != (uint32_t)((int)d + ur->other_case))
+ {
+ const uint32_t *pp = PRIV(ucd_caseless_sets) + ur->caseset;
+ for (;;)
+ {
+ if (c < *pp) return -1; /* No match */
+ if (c == *pp++) break;
+ }
+ }
+ }
+ }
+ else
+#endif
+
+ /* Not in UTF or UCP mode */
+ {
+ for (; length > 0; length--)
+ {
+ uint32_t cc, cp;
+ if (eptr >= mb->end_subject) return 1; /* Partial match */
+ cc = UCHAR21TEST(eptr);
+ cp = UCHAR21TEST(p);
+ if (TABLE_GET(cp, mb->lcc, cp) != TABLE_GET(cc, mb->lcc, cc))
+ return -1; /* No match */
+ p++;
+ eptr++;
+ }
+ }
+ }
+
+/* In the caseful case, we can just compare the code units, whether or not we
+are in UTF and/or UCP mode. When partial matching, we have to do this unit by
+unit. */
+
+else
+ {
+ if (mb->partial != 0)
+ {
+ for (; length > 0; length--)
+ {
+ if (eptr >= mb->end_subject) return 1; /* Partial match */
+ if (UCHAR21INCTEST(p) != UCHAR21INCTEST(eptr)) return -1; /* No match */
+ }
+ }
+
+ /* Not partial matching */
+
+ else
+ {
+ if ((PCRE2_SIZE)(mb->end_subject - eptr) < length) return 1; /* Partial */
+ if (memcmp(p, eptr, CU2BYTES(length)) != 0) return -1; /* No match */
+ eptr += length;
+ }
+ }
+
+*lengthptr = eptr - eptr_start;
+return 0; /* Match */
+}
+
+
+
+/******************************************************************************
+*******************************************************************************
+ "Recursion" in the match() function
+
+The original match() function was highly recursive, but this proved to be the
+source of a number of problems over the years, mostly because of the relatively
+small system stacks that are commonly found. As new features were added to
+patterns, various kludges were invented to reduce the amount of stack used,
+making the code hard to understand in places.
+
+A version did exist that used individual frames on the heap instead of calling
+match() recursively, but this ran substantially slower. The current version is
+a refactoring that uses a vector of frames to remember backtracking points.
+This runs no slower, and possibly even a bit faster than the original recursive
+implementation.
+
+At first, an initial vector of size START_FRAMES_SIZE (enough for maybe 50
+frames) was allocated on the system stack. If this was not big enough, the heap
+was used for a larger vector. However, it turns out that there are environments
+where taking as little as 20KiB from the system stack is an embarrassment.
+After another refactoring, the heap is used exclusively, but a pointer the
+frames vector and its size are cached in the match_data block, so that there is
+no new memory allocation if the same match_data block is used for multiple
+matches (unless the frames vector has to be extended).
+*******************************************************************************
+******************************************************************************/
+
+
+
+
+/*************************************************
+* Macros for the match() function *
+*************************************************/
+
+/* These macros pack up tests that are used for partial matching several times
+in the code. The second one is used when we already know we are past the end of
+the subject. We set the "hit end" flag if the pointer is at the end of the
+subject and either (a) the pointer is past the earliest inspected character
+(i.e. something has been matched, even if not part of the actual matched
+string), or (b) the pattern contains a lookbehind. These are the conditions for
+which adding more characters may allow the current match to continue.
+
+For hard partial matching, we immediately return a partial match. Otherwise,
+carrying on means that a complete match on the current subject will be sought.
+A partial match is returned only if no complete match can be found. */
+
+#define CHECK_PARTIAL()\
+ if (Feptr >= mb->end_subject) \
+ { \
+ SCHECK_PARTIAL(); \
+ }
+
+#define SCHECK_PARTIAL()\
+ if (mb->partial != 0 && \
+ (Feptr > mb->start_used_ptr || mb->allowemptypartial)) \
+ { \
+ mb->hitend = TRUE; \
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL; \
+ }
+
+
+/* These macros are used to implement backtracking. They simulate a recursive
+call to the match() function by means of a local vector of frames which
+remember the backtracking points. */
+
+#define RMATCH(ra,rb)\
+ {\
+ start_ecode = ra;\
+ Freturn_id = rb;\
+ goto MATCH_RECURSE;\
+ L_##rb:;\
+ }
+
+#define RRETURN(ra)\
+ {\
+ rrc = ra;\
+ goto RETURN_SWITCH;\
+ }
+
+
+
+/*************************************************
+* Match from current position *
+*************************************************/
+
+/* This function is called to run one match attempt at a single starting point
+in the subject.
+
+Performance note: It might be tempting to extract commonly used fields from the
+mb structure (e.g. end_subject) into individual variables to improve
+performance. Tests using gcc on a SPARC disproved this; in the first case, it
+made performance worse.
+
+Arguments:
+ start_eptr starting character in subject
+ start_ecode starting position in compiled code
+ top_bracket number of capturing parentheses in the pattern
+ frame_size size of each backtracking frame
+ match_data pointer to the match_data block
+ mb pointer to "static" variables block
+
+Returns: MATCH_MATCH if matched ) these values are >= 0
+ MATCH_NOMATCH if failed to match )
+ negative MATCH_xxx value for PRUNE, SKIP, etc
+ negative PCRE2_ERROR_xxx value if aborted by an error condition
+ (e.g. stopped by repeated call or depth limit)
+*/
+
+static int
+match(PCRE2_SPTR start_eptr, PCRE2_SPTR start_ecode, uint16_t top_bracket,
+ PCRE2_SIZE frame_size, pcre2_match_data *match_data, match_block *mb)
+{
+/* Frame-handling variables */
+
+heapframe *F; /* Current frame pointer */
+heapframe *N = NULL; /* Temporary frame pointers */
+heapframe *P = NULL;
+
+heapframe *frames_top; /* End of frames vector */
+heapframe *assert_accept_frame = NULL; /* For passing back a frame with captures */
+PCRE2_SIZE heapframes_size; /* Usable size of frames vector */
+PCRE2_SIZE frame_copy_size; /* Amount to copy when creating a new frame */
+
+/* Local variables that do not need to be preserved over calls to RRMATCH(). */
+
+PCRE2_SPTR bracode; /* Temp pointer to start of group */
+PCRE2_SIZE offset; /* Used for group offsets */
+PCRE2_SIZE length; /* Used for various length calculations */
+
+int rrc; /* Return from functions & backtracking "recursions" */
+#ifdef SUPPORT_UNICODE
+int proptype; /* Type of character property */
+#endif
+
+uint32_t i; /* Used for local loops */
+uint32_t fc; /* Character values */
+uint32_t number; /* Used for group and other numbers */
+uint32_t reptype = 0; /* Type of repetition (0 to avoid compiler warning) */
+uint32_t group_frame_type; /* Specifies type for new group frames */
+
+BOOL condition; /* Used in conditional groups */
+BOOL cur_is_word; /* Used in "word" tests */
+BOOL prev_is_word; /* Used in "word" tests */
+
+/* UTF and UCP flags */
+
+#ifdef SUPPORT_UNICODE
+BOOL utf = (mb->poptions & PCRE2_UTF) != 0;
+BOOL ucp = (mb->poptions & PCRE2_UCP) != 0;
+#else
+BOOL utf = FALSE; /* Required for convenience even when no Unicode support */
+#endif
+
+/* This is the length of the last part of a backtracking frame that must be
+copied when a new frame is created. */
+
+frame_copy_size = frame_size - offsetof(heapframe, eptr);
+
+/* Set up the first frame and the end of the frames vector. We set the local
+heapframes_size to the usuable amount of the vector, that is, a whole number of
+frames. */
+
+F = match_data->heapframes;
+heapframes_size = (match_data->heapframes_size / frame_size) * frame_size;
+frames_top = (heapframe *)((char *)F + heapframes_size);
+
+Frdepth = 0; /* "Recursion" depth */
+Fcapture_last = 0; /* Number of most recent capture */
+Fcurrent_recurse = RECURSE_UNSET; /* Not pattern recursing. */
+Fstart_match = Feptr = start_eptr; /* Current data pointer and start match */
+Fmark = NULL; /* Most recent mark */
+Foffset_top = 0; /* End of captures within the frame */
+Flast_group_offset = PCRE2_UNSET; /* Saved frame of most recent group */
+group_frame_type = 0; /* Not a start of group frame */
+goto NEW_FRAME; /* Start processing with this frame */
+
+/* Come back here when we want to create a new frame for remembering a
+backtracking point. */
+
+MATCH_RECURSE:
+
+/* Set up a new backtracking frame. If the vector is full, get a new one,
+doubling the size, but constrained by the heap limit (which is in KiB). */
+
+N = (heapframe *)((char *)F + frame_size);
+if (N >= frames_top)
+ {
+ heapframe *new;
+ PCRE2_SIZE newsize = match_data->heapframes_size * 2;
+
+ if (newsize > mb->heap_limit)
+ {
+ PCRE2_SIZE maxsize = (mb->heap_limit/frame_size) * frame_size;
+ if (match_data->heapframes_size >= maxsize) return PCRE2_ERROR_HEAPLIMIT;
+ newsize = maxsize;
+ }
+
+ new = match_data->memctl.malloc(newsize, match_data->memctl.memory_data);
+ if (new == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy(new, match_data->heapframes, heapframes_size);
+
+ F = (heapframe *)((char *)new + ((char *)F - (char *)match_data->heapframes));
+ N = (heapframe *)((char *)F + frame_size);
+
+ match_data->memctl.free(match_data->heapframes, match_data->memctl.memory_data);
+ match_data->heapframes = new;
+ match_data->heapframes_size = newsize;
+
+ heapframes_size = (newsize / frame_size) * frame_size;
+ frames_top = (heapframe *)((char *)new + heapframes_size);
+ }
+
+#ifdef DEBUG_SHOW_RMATCH
+fprintf(stderr, "++ RMATCH %2d frame=%d", Freturn_id, Frdepth + 1);
+if (group_frame_type != 0)
+ {
+ fprintf(stderr, " type=%x ", group_frame_type);
+ switch (GF_IDMASK(group_frame_type))
+ {
+ case GF_CAPTURE:
+ fprintf(stderr, "capture=%d", GF_DATAMASK(group_frame_type));
+ break;
+
+ case GF_NOCAPTURE:
+ fprintf(stderr, "nocapture op=%d", GF_DATAMASK(group_frame_type));
+ break;
+
+ case GF_CONDASSERT:
+ fprintf(stderr, "condassert op=%d", GF_DATAMASK(group_frame_type));
+ break;
+
+ case GF_RECURSE:
+ fprintf(stderr, "recurse=%d", GF_DATAMASK(group_frame_type));
+ break;
+
+ default:
+ fprintf(stderr, "*** unknown ***");
+ break;
+ }
+ }
+fprintf(stderr, "\n");
+#endif
+
+/* Copy those fields that must be copied into the new frame, increase the
+"recursion" depth (i.e. the new frame's index) and then make the new frame
+current. */
+
+memcpy((char *)N + offsetof(heapframe, eptr),
+ (char *)F + offsetof(heapframe, eptr),
+ frame_copy_size);
+
+N->rdepth = Frdepth + 1;
+F = N;
+
+/* Carry on processing with a new frame. */
+
+NEW_FRAME:
+Fgroup_frame_type = group_frame_type;
+Fecode = start_ecode; /* Starting code pointer */
+Fback_frame = frame_size; /* Default is go back one frame */
+
+/* If this is a special type of group frame, remember its offset for quick
+access at the end of the group. If this is a recursion, set a new current
+recursion value. */
+
+if (group_frame_type != 0)
+ {
+ Flast_group_offset = (char *)F - (char *)match_data->heapframes;
+ if (GF_IDMASK(group_frame_type) == GF_RECURSE)
+ Fcurrent_recurse = GF_DATAMASK(group_frame_type);
+ group_frame_type = 0;
+ }
+
+
+/* ========================================================================= */
+/* This is the main processing loop. First check that we haven't recorded too
+many backtracks (search tree is too large), or that we haven't exceeded the
+recursive depth limit (used too many backtracking frames). If not, process the
+opcodes. */
+
+if (mb->match_call_count++ >= mb->match_limit) return PCRE2_ERROR_MATCHLIMIT;
+if (Frdepth >= mb->match_limit_depth) return PCRE2_ERROR_DEPTHLIMIT;
+
+for (;;)
+ {
+#ifdef DEBUG_SHOW_OPS
+fprintf(stderr, "++ op=%d\n", *Fecode);
+#endif
+
+ Fop = (uint8_t)(*Fecode); /* Cast needed for 16-bit and 32-bit modes */
+ switch(Fop)
+ {
+ /* ===================================================================== */
+ /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, to close
+ any currently open capturing brackets. Unlike reaching the end of a group,
+ where we know the starting frame is at the top of the chained frames, in
+ this case we have to search back for the relevant frame in case other types
+ of group that use chained frames have intervened. Multiple OP_CLOSEs always
+ come innermost first, which matches the chain order. We can ignore this in
+ a recursion, because captures are not passed out of recursions. */
+
+ case OP_CLOSE:
+ if (Fcurrent_recurse == RECURSE_UNSET)
+ {
+ number = GET2(Fecode, 1);
+ offset = Flast_group_offset;
+ for(;;)
+ {
+ if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;
+ N = (heapframe *)((char *)match_data->heapframes + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (N->group_frame_type == (GF_CAPTURE | number)) break;
+ offset = P->last_group_offset;
+ }
+ offset = (number << 1) - 2;
+ Fcapture_last = number;
+ Fovector[offset] = P->eptr - mb->start_subject;
+ Fovector[offset+1] = Feptr - mb->start_subject;
+ if (offset >= Foffset_top) Foffset_top = offset + 2;
+ }
+ Fecode += PRIV(OP_lengths)[*Fecode];
+ break;
+
+
+ /* ===================================================================== */
+ /* Real or forced end of the pattern, assertion, or recursion. In an
+ assertion ACCEPT, update the last used pointer and remember the current
+ frame so that the captures and mark can be fished out of it. */
+
+ case OP_ASSERT_ACCEPT:
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+ assert_accept_frame = F;
+ RRETURN(MATCH_ACCEPT);
+
+ /* If recursing, we have to find the most recent recursion. */
+
+ case OP_ACCEPT:
+ case OP_END:
+
+ /* Handle end of a recursion. */
+
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ offset = Flast_group_offset;
+ for(;;)
+ {
+ if (offset == PCRE2_UNSET) return PCRE2_ERROR_INTERNAL;
+ N = (heapframe *)((char *)match_data->heapframes + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (GF_IDMASK(N->group_frame_type) == GF_RECURSE) break;
+ offset = P->last_group_offset;
+ }
+
+ /* N is now the frame of the recursion; the previous frame is at the
+ OP_RECURSE position. Go back there, copying the current subject position
+ and mark, and the start_match position (\K might have changed it), and
+ then move on past the OP_RECURSE. */
+
+ P->eptr = Feptr;
+ P->mark = Fmark;
+ P->start_match = Fstart_match;
+ F = P;
+ Fecode += 1 + LINK_SIZE;
+ continue;
+ }
+
+ /* Not a recursion. Fail for an empty string match if either PCRE2_NOTEMPTY
+ is set, or if PCRE2_NOTEMPTY_ATSTART is set and we have matched at the
+ start of the subject. In both cases, backtracking will then try other
+ alternatives, if any. */
+
+ if (Feptr == Fstart_match &&
+ ((mb->moptions & PCRE2_NOTEMPTY) != 0 ||
+ ((mb->moptions & PCRE2_NOTEMPTY_ATSTART) != 0 &&
+ Fstart_match == mb->start_subject + mb->start_offset)))
+ RRETURN(MATCH_NOMATCH);
+
+ /* Also fail if PCRE2_ENDANCHORED is set and the end of the match is not
+ the end of the subject. After (*ACCEPT) we fail the entire match (at this
+ position) but backtrack on reaching the end of the pattern. */
+
+ if (Feptr < mb->end_subject &&
+ ((mb->moptions | mb->poptions) & PCRE2_ENDANCHORED) != 0)
+ {
+ if (Fop == OP_END) RRETURN(MATCH_NOMATCH);
+ return MATCH_NOMATCH;
+ }
+
+ /* We have a successful match of the whole pattern. Record the result and
+ then do a direct return from the function. If there is space in the offset
+ vector, set any pairs that follow the highest-numbered captured string but
+ are less than the number of capturing groups in the pattern to PCRE2_UNSET.
+ It is documented that this happens. "Gaps" are set to PCRE2_UNSET
+ dynamically. It is only those at the end that need setting here. */
+
+ mb->end_match_ptr = Feptr; /* Record where we ended */
+ mb->end_offset_top = Foffset_top; /* and how many extracts were taken */
+ mb->mark = Fmark; /* and the last success mark */
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+
+ match_data->ovector[0] = Fstart_match - mb->start_subject;
+ match_data->ovector[1] = Feptr - mb->start_subject;
+
+ /* Set i to the smaller of the sizes of the external and frame ovectors. */
+
+ i = 2 * ((top_bracket + 1 > match_data->oveccount)?
+ match_data->oveccount : top_bracket + 1);
+ memcpy(match_data->ovector + 2, Fovector, (i - 2) * sizeof(PCRE2_SIZE));
+ while (--i >= Foffset_top + 2) match_data->ovector[i] = PCRE2_UNSET;
+ return MATCH_MATCH; /* Note: NOT RRETURN */
+
+
+ /*===================================================================== */
+ /* Match any single character type except newline; have to take care with
+ CRLF newlines and partial matching. */
+
+ case OP_ANY:
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0 &&
+ Feptr == mb->end_subject - 1 &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ /* Fall through */
+
+ /* Match any single character whatsoever. */
+
+ case OP_ALLANY:
+ if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */
+ { /* not be updated before SCHECK_PARTIAL. */
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+#ifdef SUPPORT_UNICODE
+ if (utf) ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+#endif
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Match a single code unit, even in UTF mode. This opcode really does
+ match any code unit, even newline. (It really should be called ANYCODEUNIT,
+ of course - the byte name is from pre-16 bit days.) */
+
+ case OP_ANYBYTE:
+ if (Feptr >= mb->end_subject) /* DO NOT merge the Feptr++ here; it must */
+ { /* not be updated before SCHECK_PARTIAL. */
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Match a single character, casefully */
+
+ case OP_CHAR:
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ Flength = 1;
+ Fecode++;
+ GETCHARLEN(fc, Fecode, Flength);
+ if (Flength > (PCRE2_SIZE)(mb->end_subject - Feptr))
+ {
+ CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */
+ RRETURN(MATCH_NOMATCH);
+ }
+ for (; Flength > 0; Flength--)
+ {
+ if (*Fecode++ != UCHAR21INC(Feptr)) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+
+ /* Not UTF mode */
+ {
+ if (mb->end_subject - Feptr < 1)
+ {
+ SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Fecode[1] != *Feptr++) RRETURN(MATCH_NOMATCH);
+ Fecode += 2;
+ }
+ break;
+
+
+ /* ===================================================================== */
+ /* Match a single character, caselessly. If we are at the end of the
+ subject, give up immediately. We get here only when the pattern character
+ has at most one other case. Characters with more than two cases are coded
+ as OP_PROP with the pseudo-property PT_CLIST. */
+
+ case OP_CHARI:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ Flength = 1;
+ Fecode++;
+ GETCHARLEN(fc, Fecode, Flength);
+
+ /* If the pattern character's value is < 128, we know that its other case
+ (if any) is also < 128 (and therefore only one code unit long in all
+ code-unit widths), so we can use the fast lookup table. We checked above
+ that there is at least one character left in the subject. */
+
+ if (fc < 128)
+ {
+ uint32_t cc = UCHAR21(Feptr);
+ if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ Feptr++;
+ }
+
+ /* Otherwise we must pick up the subject character and use Unicode
+ property support to test its other case. Note that we cannot use the
+ value of "Flength" to check for sufficient bytes left, because the other
+ case of the character may have more or fewer code units. */
+
+ else
+ {
+ uint32_t dc;
+ GETCHARINC(dc, Feptr);
+ Fecode += Flength;
+ if (dc != fc && dc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ /* If UCP is set without UTF we must do the same as above, but with one
+ character per code unit. */
+
+ else if (ucp)
+ {
+ uint32_t cc = UCHAR21(Feptr);
+ fc = Fecode[1];
+ if (fc < 128)
+ {
+ if (mb->lcc[fc] != TABLE_GET(cc, mb->lcc, cc)) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ if (cc != fc && cc != UCD_OTHERCASE(fc)) RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+ Fecode += 2;
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF or UCP mode; use the table for characters < 256. */
+ {
+ if (TABLE_GET(Fecode[1], mb->lcc, Fecode[1])
+ != TABLE_GET(*Feptr, mb->lcc, *Feptr)) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ Fecode += 2;
+ }
+ break;
+
+
+ /* ===================================================================== */
+ /* Match not a single character. */
+
+ case OP_NOT:
+ case OP_NOTI:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t ch;
+ Fecode++;
+ GETCHARINC(ch, Fecode);
+ GETCHARINC(fc, Feptr);
+ if (ch == fc)
+ {
+ RRETURN(MATCH_NOMATCH); /* Caseful match */
+ }
+ else if (Fop == OP_NOTI) /* If caseless */
+ {
+ if (ch > 127)
+ ch = UCD_OTHERCASE(ch);
+ else
+ ch = (mb->fcc)[ch];
+ if (ch == fc) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ /* UCP without UTF is as above, but with one character per code unit. */
+
+ else if (ucp)
+ {
+ uint32_t ch;
+ fc = UCHAR21INC(Feptr);
+ ch = Fecode[1];
+ Fecode += 2;
+
+ if (ch == fc)
+ {
+ RRETURN(MATCH_NOMATCH); /* Caseful match */
+ }
+ else if (Fop == OP_NOTI) /* If caseless */
+ {
+ if (ch > 127)
+ ch = UCD_OTHERCASE(ch);
+ else
+ ch = (mb->fcc)[ch];
+ if (ch == fc) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Neither UTF nor UCP is set */
+
+ {
+ uint32_t ch = Fecode[1];
+ fc = UCHAR21INC(Feptr);
+ if (ch == fc || (Fop == OP_NOTI && TABLE_GET(ch, mb->fcc, ch) == fc))
+ RRETURN(MATCH_NOMATCH);
+ Fecode += 2;
+ }
+ break;
+
+
+ /* ===================================================================== */
+ /* Match a single character repeatedly. */
+
+#define Loclength F->temp_size
+#define Lstart_eptr F->temp_sptr[0]
+#define Lcharptr F->temp_sptr[1]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lc F->temp_32[2]
+#define Loc F->temp_32[3]
+
+ case OP_EXACT:
+ case OP_EXACTI:
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
+
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
+
+ case OP_UPTO:
+ case OP_UPTOI:
+ reptype = REPTYPE_MAX;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
+
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ reptype = REPTYPE_MIN;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATCHAR;
+
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATCHAR;
+
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATCHAR;
+
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
+ goto REPEATCHAR;
+
+ case OP_STAR:
+ case OP_STARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ fc = *Fecode++ - ((Fop < OP_STARI)? OP_STAR : OP_STARI);
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+
+ /* Common code for all repeated single-character matches. We first check
+ for the minimum number of characters. If the minimum equals the maximum, we
+ are done. Otherwise, if minimizing, check the rest of the pattern for a
+ match; if there isn't one, advance up to the maximum, one character at a
+ time.
+
+ If maximizing, advance up to the maximum number of matching characters,
+ until Feptr is past the end of the maximum run. If possessive, we are
+ then done (no backing up). Otherwise, match at this position; anything
+ other than no match is immediately returned. For nomatch, back up one
+ character, unless we are matching \R and the last thing matched was
+ \r\n, in which case, back up two code units until we reach the first
+ optional character position.
+
+ The various UTF/non-UTF and caseful/caseless cases are handled separately,
+ for speed. */
+
+ REPEATCHAR:
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ Flength = 1;
+ Lcharptr = Fecode;
+ GETCHARLEN(fc, Fecode, Flength);
+ Fecode += Flength;
+
+ /* Handle multi-code-unit character matching, caseful and caseless. */
+
+ if (Flength > 1)
+ {
+ uint32_t othercase;
+
+ if (Fop >= OP_STARI && /* Caseless */
+ (othercase = UCD_OTHERCASE(fc)) != fc)
+ Loclength = PRIV(ord2utf)(othercase, Foccu);
+ else Loclength = 0;
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ if (Lmin == Lmax) continue;
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM202);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0) Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ else /* Maximize */
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr <= mb->end_subject - Flength &&
+ memcmp(Feptr, Lcharptr, CU2BYTES(Flength)) == 0)
+ Feptr += Flength;
+ else if (Loclength > 0 &&
+ Feptr <= mb->end_subject - Loclength &&
+ memcmp(Feptr, Foccu, CU2BYTES(Loclength)) == 0)
+ Feptr += Loclength;
+ else
+ {
+ CHECK_PARTIAL();
+ break;
+ }
+ }
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM203);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ break; /* End of repeated wide character handling */
+ }
+
+ /* Length of UTF character is 1. Put it into the preserved variable and
+ fall through to the non-UTF code. */
+
+ Lc = fc;
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* When not in UTF mode, load a single-code-unit character. Then proceed as
+ above, using Unicode casing if either UTF or UCP is set. */
+
+ Lc = *Fecode++;
+
+ /* Caseless comparison */
+
+ if (Fop >= OP_STARI)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+#ifdef SUPPORT_UNICODE
+ if (ucp && !utf && Lc > 127) Loc = UCD_OTHERCASE(Lc);
+ else
+#endif /* SUPPORT_UNICODE */
+ /* Lc will be < 128 in UTF-8 mode. */
+ Loc = mb->fcc[Lc];
+#else /* 16-bit & 32-bit */
+#ifdef SUPPORT_UNICODE
+ if ((utf || ucp) && Lc > 127) Loc = UCD_OTHERCASE(Lc);
+ else
+#endif /* SUPPORT_UNICODE */
+ Loc = TABLE_GET(Lc, mb->fcc, Lc);
+#endif /* PCRE2_CODE_UNIT_WIDTH == 8 */
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ if (Lmin == Lmax) continue;
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ RMATCH(Fecode, RM25);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ /* Control never gets here */
+ }
+
+ else /* Maximize */
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ uint32_t cc; /* Faster than PCRE2_UCHAR */
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ cc = UCHAR21TEST(Feptr);
+ if (Lc != cc && Loc != cc) break;
+ Feptr++;
+ }
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM26);
+ Feptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ }
+ }
+
+ /* Caseful comparisons (includes all multi-byte characters) */
+
+ else
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH);
+ }
+
+ if (Lmin == Lmax) continue;
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM27);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc != UCHAR21INCTEST(Feptr)) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+ else /* Maximize */
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+
+ if (Lc != UCHAR21TEST(Feptr)) break;
+ Feptr++;
+ }
+
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM28);
+ Feptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ }
+ }
+ break;
+
+#undef Loclength
+#undef Lstart_eptr
+#undef Lcharptr
+#undef Lmin
+#undef Lmax
+#undef Lc
+#undef Loc
+
+
+ /* ===================================================================== */
+ /* Match a negated single one-byte character repeatedly. This is almost a
+ repeat of the code for a repeated single character, but I haven't found a
+ nice way of commoning these up that doesn't require a test of the
+ positive/negative option for each character match. Maybe that wouldn't add
+ very much to the time taken, but character matching *is* what this is all
+ about... */
+
+#define Lstart_eptr F->temp_sptr[0]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lc F->temp_32[2]
+#define Loc F->temp_32[3]
+
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = REPTYPE_MAX;
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = REPTYPE_MIN;
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ fc = *Fecode++ - ((Fop >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR);
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+
+ /* Common code for all repeated single-character non-matches. */
+
+ REPEATNOTCHAR:
+ GETCHARINCTEST(Lc, Fecode);
+
+ /* The code is duplicated for the caseless and caseful cases, for speed,
+ since matching characters is likely to be quite common. First, ensure the
+ minimum number of matches are present. If Lmin = Lmax, we are done.
+ Otherwise, if minimizing, keep trying the rest of the expression and
+ advancing one matching character if failing, up to the maximum.
+ Alternatively, if maximizing, find the maximum number of characters and
+ work backwards. */
+
+ if (Fop >= OP_NOTSTARI) /* Caseless */
+ {
+#ifdef SUPPORT_UNICODE
+ if ((utf || ucp) && Lc > 127)
+ Loc = UCD_OTHERCASE(Lc);
+ else
+#endif /* SUPPORT_UNICODE */
+
+ Loc = TABLE_GET(Lc, mb->fcc, Lc); /* Other case from table */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ }
+
+ if (Lmin == Lmax) continue; /* Finished for exact count */
+
+ if (reptype == REPTYPE_MIN)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (;;)
+ {
+ RMATCH(Fecode, RM204);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d || Loc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif /*SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM29);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr || Loc == *Feptr) RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* Maximize case */
+
+ else
+ {
+ Lstart_eptr = Feptr;
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(d, Feptr, len);
+ if (Lc == d || Loc == d) break;
+ Feptr += len;
+ }
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM205);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (Lc == *Feptr || Loc == *Feptr) break;
+ Feptr++;
+ }
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM30);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ }
+ }
+ }
+ }
+
+ /* Caseful comparisons */
+
+ else
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ if (Lmin == Lmax) continue;
+
+ if (reptype == REPTYPE_MIN)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (;;)
+ {
+ RMATCH(Fecode, RM206);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(d, Feptr);
+ if (Lc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM31);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lc == *Feptr++) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* Maximize case */
+
+ else
+ {
+ Lstart_eptr = Feptr;
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ uint32_t d;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(d, Feptr, len);
+ if (Lc == d) break;
+ Feptr += len;
+ }
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ if (reptype != REPTYPE_POS) for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM207);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (Lc == *Feptr) break;
+ Feptr++;
+ }
+ if (reptype != REPTYPE_POS) for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM32);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ }
+ }
+ }
+ }
+ break;
+
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+#undef Lc
+#undef Loc
+
+
+ /* ===================================================================== */
+ /* Match a bit-mapped character class, possibly repeatedly. These opcodes
+ are used when all the characters in the class have values in the range
+ 0-255, and either the matching is caseful, or the characters are in the
+ range 0-127 when UTF processing is enabled. The only difference between
+ OP_CLASS and OP_NCLASS occurs when a data character outside the range is
+ encountered. */
+
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lstart_eptr F->temp_sptr[0]
+#define Lbyte_map_address F->temp_sptr[1]
+#define Lbyte_map ((unsigned char *)Lbyte_map_address)
+
+ case OP_NCLASS:
+ case OP_CLASS:
+ {
+ Lbyte_map_address = Fecode + 1; /* Save for matching */
+ Fecode += 1 + (32 / sizeof(PCRE2_UCHAR)); /* Advance past the item */
+
+ /* Look past the end of the item to see if there is repeat information
+ following. Then obey similar code to character type repeats. */
+
+ switch (*Fecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ case OP_CRPOSQUERY:
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ Fecode += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default: /* No repeat follows */
+ Lmin = Lmax = 1;
+ break;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ fc = *Feptr++;
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+#endif
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ /* If Lmax == Lmin we are done. Continue with main loop. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, keep testing the rest of the expression and advancing
+ the pointer while it matches the class. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM200);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM23);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ fc = *Feptr++;
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+#endif
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest possible run, then work backwards. */
+
+ else
+ {
+ Lstart_eptr = Feptr;
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) break;
+ }
+ else
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) break;
+ Feptr += len;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ for (;;)
+ {
+ RMATCH(Fecode, RM201);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif
+ /* Not UTF mode */
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ fc = *Feptr;
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (fc > 255)
+ {
+ if (Fop == OP_CLASS) break;
+ }
+ else
+#endif
+ if ((Lbyte_map[fc/8] & (1u << (fc&7))) == 0) break;
+ Feptr++;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ while (Feptr >= Lstart_eptr)
+ {
+ RMATCH(Fecode, RM24);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+
+#undef Lbyte_map_address
+#undef Lbyte_map
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+
+
+ /* ===================================================================== */
+ /* Match an extended character class. In the 8-bit library, this opcode is
+ encountered only when UTF-8 mode mode is supported. In the 16-bit and
+ 32-bit libraries, codepoints greater than 255 may be encountered even when
+ UTF is not supported. */
+
+#define Lstart_eptr F->temp_sptr[0]
+#define Lxclass_data F->temp_sptr[1]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ {
+ Lxclass_data = Fecode + 1 + LINK_SIZE; /* Save for matching */
+ Fecode += GET(Fecode, 1); /* Advance past the item */
+
+ switch (*Fecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSPLUS:
+ case OP_CRPOSQUERY:
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ Fecode += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default: /* No repeat follows */
+ Lmin = Lmax = 1;
+ break;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH);
+ }
+
+ /* If Lmax == Lmin we can just continue with the main loop. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, keep testing the rest of the expression and advancing
+ the pointer while it matches the class. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM100);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest possible run, then work backwards. */
+
+ else
+ {
+ Lstart_eptr = Feptr;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+#ifdef SUPPORT_UNICODE
+ GETCHARLENTEST(fc, Feptr, len);
+#else
+ fc = *Feptr;
+#endif
+ if (!PRIV(xclass)(fc, Lxclass_data, utf)) break;
+ Feptr += len;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ for(;;)
+ {
+ RMATCH(Fecode, RM101);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Feptr-- <= Lstart_eptr) break; /* Tried at original position */
+#ifdef SUPPORT_UNICODE
+ if (utf) BACKCHAR(Feptr);
+#endif
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Control never gets here */
+ }
+#endif /* SUPPORT_WIDE_CHARS: end of XCLASS */
+
+#undef Lstart_eptr
+#undef Lxclass_data
+#undef Lmin
+#undef Lmax
+
+
+ /* ===================================================================== */
+ /* Match various character types when PCRE2_UCP is not set. These opcodes
+ are not generated when PCRE2_UCP is set - instead appropriate property
+ tests are compiled. */
+
+ case OP_NOT_DIGIT:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_DIGIT:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_WHITESPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_WORDCHAR:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (!CHMAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_ANYNL:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ }
+ else if (UCHAR21TEST(Feptr) == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ Fecode++;
+ break;
+
+ case OP_NOT_HSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: RRETURN(MATCH_NOMATCH); /* Byte and multibyte cases */
+ default: break;
+ }
+ Fecode++;
+ break;
+
+ case OP_HSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: break; /* Byte and multibyte cases */
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ Fecode++;
+ break;
+
+ case OP_NOT_VSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ Fecode++;
+ break;
+
+ case OP_VSPACE:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ Fecode++;
+ break;
+
+
+#ifdef SUPPORT_UNICODE
+
+ /* ===================================================================== */
+ /* Check the next character by Unicode property. We will get here only
+ if the support is in the binary; otherwise a compile-time error occurs. */
+
+ case OP_PROP:
+ case OP_NOTPROP:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ {
+ const uint32_t *cp;
+ const ucd_record *prop = GET_UCD(fc);
+ BOOL notmatch = Fop == OP_NOTPROP;
+
+ switch(Fecode[1])
+ {
+ case PT_ANY:
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_LAMP:
+ if ((prop->chartype == ucp_Lu ||
+ prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_GC:
+ if ((Fecode[2] == PRIV(ucp_gentype)[prop->chartype]) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_PC:
+ if ((Fecode[2] == prop->chartype) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_SC:
+ if ((Fecode[2] == prop->script) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_SCX:
+ {
+ BOOL ok = (Fecode[2] == prop->script ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Fecode[2]) != 0);
+ if (ok == notmatch) RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* These are specials */
+
+ case PT_ALNUM:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(fc)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N ||
+ fc == CHAR_UNDERSCORE) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_CLIST:
+ cp = PRIV(ucd_caseless_sets) + Fecode[2];
+ for (;;)
+ {
+ if (fc < *cp)
+ { if (notmatch) break; else { RRETURN(MATCH_NOMATCH); } }
+ if (fc == *cp++)
+ { if (notmatch) { RRETURN(MATCH_NOMATCH); } else break; }
+ }
+ break;
+
+ case PT_UCNC:
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_BIDICL:
+ if ((UCD_BIDICLASS_PROP(prop) == Fecode[2]) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case PT_BOOL:
+ {
+ BOOL ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), Fecode[2]) != 0;
+ if (ok == notmatch) RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* This should never occur */
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ Fecode += 3;
+ }
+ break;
+
+
+ /* ===================================================================== */
+ /* Match an extended Unicode sequence. We will get here only if the support
+ is in the binary; otherwise a compile-time error occurs. */
+
+ case OP_EXTUNI:
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject, utf,
+ NULL);
+ }
+ CHECK_PARTIAL();
+ Fecode++;
+ break;
+
+#endif /* SUPPORT_UNICODE */
+
+
+ /* ===================================================================== */
+ /* Match a single character type repeatedly. Note that the property type
+ does not need to be in a stack frame as it is not used within an RMATCH()
+ loop. */
+
+#define Lstart_eptr F->temp_sptr[0]
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lctype F->temp_32[2]
+#define Lpropvalue F->temp_32[3]
+
+ case OP_TYPEEXACT:
+ Lmin = Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATTYPE;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ reptype = (*Fecode == OP_TYPEMINUPTO)? REPTYPE_MIN : REPTYPE_MAX;
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATTYPE;
+
+ case OP_TYPEPOSSTAR:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATTYPE;
+
+ case OP_TYPEPOSPLUS:
+ reptype = REPTYPE_POS;
+ Lmin = 1;
+ Lmax = UINT32_MAX;
+ Fecode++;
+ goto REPEATTYPE;
+
+ case OP_TYPEPOSQUERY:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = 1;
+ Fecode++;
+ goto REPEATTYPE;
+
+ case OP_TYPEPOSUPTO:
+ reptype = REPTYPE_POS;
+ Lmin = 0;
+ Lmax = GET2(Fecode, 1);
+ Fecode += 1 + IMM2_SIZE;
+ goto REPEATTYPE;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ fc = *Fecode++ - OP_TYPESTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+
+ /* Common code for all repeated character type matches. */
+
+ REPEATTYPE:
+ Lctype = *Fecode++; /* Code for the character type */
+
+#ifdef SUPPORT_UNICODE
+ if (Lctype == OP_PROP || Lctype == OP_NOTPROP)
+ {
+ proptype = *Fecode++;
+ Lpropvalue = *Fecode++;
+ }
+ else proptype = -1;
+#endif
+
+ /* First, ensure the minimum number of matches are present. Use inline
+ code for maximizing the speed, and do the type test once at the start
+ (i.e. keep it out of the loops). As there are no calls to RMATCH in the
+ loops, we can use an ordinary variable for "notmatch". The code for UTF
+ mode is separated out for tidiness, except for Unicode property tests. */
+
+ if (Lmin > 0)
+ {
+#ifdef SUPPORT_UNICODE
+ if (proptype >= 0) /* Property tests in all modes */
+ {
+ BOOL notmatch = Lctype == OP_NOTPROP;
+ switch(proptype)
+ {
+ case PT_ANY:
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ }
+ break;
+
+ case PT_LAMP:
+ for (i = 1; i <= Lmin; i++)
+ {
+ int chartype;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ chartype = UCD_CHARTYPE(fc);
+ if ((chartype == ucp_Lu ||
+ chartype == ucp_Ll ||
+ chartype == ucp_Lt) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_GC:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_PC:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_SC:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_SCX:
+ for (i = 1; i <= Lmin; i++)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ prop = GET_UCD(fc);
+ ok = (prop->script == Lpropvalue ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);
+ if (ok == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_ALNUM:
+ for (i = 1; i <= Lmin; i++)
+ {
+ int category;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ if ((UCD_CATEGORY(fc) == ucp_Z) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ break;
+
+ case PT_WORD:
+ for (i = 1; i <= Lmin; i++)
+ {
+ int category;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N ||
+ fc == CHAR_UNDERSCORE) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_CLIST:
+ for (i = 1; i <= Lmin; i++)
+ {
+ const uint32_t *cp;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
+ for (;;)
+ {
+ if (fc < *cp)
+ {
+ if (notmatch) break;
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (fc == *cp++)
+ {
+ if (notmatch) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ }
+ break;
+
+ case PT_UCNC:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_BIDICL:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_BIDICLASS(fc) == Lpropvalue) == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case PT_BOOL:
+ for (i = 1; i <= Lmin; i++)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ prop = GET_UCD(fc);
+ ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), Lpropvalue) != 0;
+ if (ok == notmatch)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* This should not occur */
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+
+ /* Match extended Unicode sequences. We will get here only if the
+ support is in the binary; otherwise a compile-time error occurs. */
+
+ else if (Lctype == OP_EXTUNI)
+ {
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject,
+ mb->end_subject, utf, NULL);
+ }
+ CHECK_PARTIAL();
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+/* Handle all other cases in UTF mode */
+
+#ifdef SUPPORT_UNICODE
+ if (utf) switch(Lctype)
+ {
+ case OP_ANY:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_ALLANY:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_ANYBYTE:
+ if (Feptr > mb->end_subject - Lmin) RRETURN(MATCH_NOMATCH);
+ Feptr += Lmin;
+ break;
+
+ case OP_ANYNL:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ }
+ break;
+
+ case OP_HSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ }
+ break;
+
+ case OP_VSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ switch(fc)
+ {
+ VSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINC(fc, Feptr);
+ if (fc < 128 && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc >= 128 || (mb->ctypes[cc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc < 128 && (mb->ctypes[cc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc >= 128 || (mb->ctypes[cc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc < 128 && (mb->ctypes[cc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = 1; i <= Lmin; i++)
+ {
+ uint32_t cc;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ cc = UCHAR21(Feptr);
+ if (cc >= 128 || (mb->ctypes[cc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ /* No need to skip more code units - we know it has only one. */
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ } /* End switch(Lctype) */
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Code for the non-UTF case for minimum matching of operators other
+ than OP_PROP and OP_NOTPROP. */
+
+ switch(Lctype)
+ {
+ case OP_ANY:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ *Feptr == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Feptr++;
+ }
+ break;
+
+ case OP_ALLANY:
+ if (Feptr > mb->end_subject - Lmin)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr += Lmin;
+ break;
+
+ /* This OP_ANYBYTE case will never be reached because \C gets turned
+ into OP_ALLANY in non-UTF mode. Cut out the code so that coverage
+ reports don't complain about it's never being used. */
+
+/* case OP_ANYBYTE:
+* if (Feptr > mb->end_subject - Lmin)
+* {
+* SCHECK_PARTIAL();
+* RRETURN(MATCH_NOMATCH);
+* }
+* Feptr += Lmin;
+* break;
+*/
+ case OP_ANYNL:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ case 0x2028:
+ case 0x2029:
+#endif
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ default: break;
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ break;
+
+ case OP_HSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ break;
+ }
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ }
+ break;
+
+ case OP_VSPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ switch(*Feptr++)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ break;
+ }
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = 1; i <= Lmin; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ Feptr++;
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+
+ /* If Lmin = Lmax we are done. Continue with the main loop. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, we have to test the rest of the pattern before each
+ subsequent match. This means we cannot use a local "notmatch" variable as
+ in the other cases. As all 4 temporary 32-bit values in the frame are
+ already in use, just test the type each time. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+#ifdef SUPPORT_UNICODE
+ if (proptype >= 0)
+ {
+ switch(proptype)
+ {
+ case PT_ANY:
+ for (;;)
+ {
+ RMATCH(Fecode, RM208);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_LAMP:
+ for (;;)
+ {
+ int chartype;
+ RMATCH(Fecode, RM209);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ chartype = UCD_CHARTYPE(fc);
+ if ((chartype == ucp_Lu ||
+ chartype == ucp_Ll ||
+ chartype == ucp_Lt) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_GC:
+ for (;;)
+ {
+ RMATCH(Fecode, RM210);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_PC:
+ for (;;)
+ {
+ RMATCH(Fecode, RM211);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_SC:
+ for (;;)
+ {
+ RMATCH(Fecode, RM212);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_SCX:
+ for (;;)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ RMATCH(Fecode, RM225);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ prop = GET_UCD(fc);
+ ok = (prop->script == Lpropvalue
+ || MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);
+ if (ok == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_ALNUM:
+ for (;;)
+ {
+ int category;
+ RMATCH(Fecode, RM213);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ for (;;)
+ {
+ RMATCH(Fecode, RM214);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ switch(fc)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ if ((UCD_CATEGORY(fc) == ucp_Z) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ /* Control never gets here */
+
+ case PT_WORD:
+ for (;;)
+ {
+ int category;
+ RMATCH(Fecode, RM215);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L ||
+ category == ucp_N ||
+ fc == CHAR_UNDERSCORE) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_CLIST:
+ for (;;)
+ {
+ const uint32_t *cp;
+ RMATCH(Fecode, RM216);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
+ for (;;)
+ {
+ if (fc < *cp)
+ {
+ if (Lctype == OP_NOTPROP) break;
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (fc == *cp++)
+ {
+ if (Lctype == OP_NOTPROP) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ }
+ /* Control never gets here */
+
+ case PT_UCNC:
+ for (;;)
+ {
+ RMATCH(Fecode, RM217);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_BIDICL:
+ for (;;)
+ {
+ RMATCH(Fecode, RM224);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ if ((UCD_BIDICLASS(fc) == Lpropvalue) == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ case PT_BOOL:
+ for (;;)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ RMATCH(Fecode, RM223);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ GETCHARINCTEST(fc, Feptr);
+ prop = GET_UCD(fc);
+ ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), Lpropvalue) != 0;
+ if (ok == (Lctype == OP_NOTPROP))
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ /* This should never occur */
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+
+ /* Match extended Unicode sequences. We will get here only if the
+ support is in the binary; otherwise a compile-time error occurs. */
+
+ else if (Lctype == OP_EXTUNI)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM218);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,
+ utf, NULL);
+ }
+ CHECK_PARTIAL();
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* UTF mode for non-property testing character types. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM219);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lctype == OP_ANY && IS_NEWLINE(Feptr)) RRETURN(MATCH_NOMATCH);
+ GETCHARINC(fc, Feptr);
+ switch(Lctype)
+ {
+ case OP_ANY: /* This is the non-NL case */
+ if (mb->partial != 0 && /* Take care with CRLF partial */
+ Feptr >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ fc == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ break;
+
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ break;
+
+ case OP_ANYNL:
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr < mb->end_subject && UCHAR21(Feptr) == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#ifndef EBCDIC
+ case 0x2028:
+ case 0x2029:
+#endif /* Not EBCDIC */
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ switch(fc)
+ {
+ HSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ break;
+
+ case OP_HSPACE:
+ switch(fc)
+ {
+ HSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ switch(fc)
+ {
+ VSPACE_CASES: RRETURN(MATCH_NOMATCH);
+ default: break;
+ }
+ break;
+
+ case OP_VSPACE:
+ switch(fc)
+ {
+ VSPACE_CASES: break;
+ default: RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_DIGIT:
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WHITESPACE:
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WORDCHAR:
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ for (;;)
+ {
+ RMATCH(Fecode, RM33);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ if (Lctype == OP_ANY && IS_NEWLINE(Feptr))
+ RRETURN(MATCH_NOMATCH);
+ fc = *Feptr++;
+ switch(Lctype)
+ {
+ case OP_ANY: /* This is the non-NL case */
+ if (mb->partial != 0 && /* Take care with CRLF partial */
+ Feptr >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ fc == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ break;
+
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ break;
+
+ case OP_ANYNL:
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+
+ case CHAR_CR:
+ if (Feptr < mb->end_subject && *Feptr == CHAR_LF) Feptr++;
+ break;
+
+ case CHAR_LF:
+ break;
+
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_NEL:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ case 0x2028:
+ case 0x2029:
+#endif
+ if (mb->bsr_convention == PCRE2_BSR_ANYCRLF)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ switch(fc)
+ {
+ default: break;
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_HSPACE:
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ break;
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ switch(fc)
+ {
+ default: break;
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_VSPACE:
+ switch(fc)
+ {
+ default: RRETURN(MATCH_NOMATCH);
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ break;
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_DIGIT:
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WHITESPACE:
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (MAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WORDCHAR:
+ if (!MAX_255(fc) || (mb->ctypes[fc] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, it is worth using inline code for speed, doing the type
+ test once at the start (i.e. keep it out of the loops). Once again,
+ "notmatch" can be an ordinary local variable because the loops do not call
+ RMATCH. */
+
+ else
+ {
+ Lstart_eptr = Feptr; /* Remember where we started */
+
+#ifdef SUPPORT_UNICODE
+ if (proptype >= 0)
+ {
+ BOOL notmatch = Lctype == OP_NOTPROP;
+ switch(proptype)
+ {
+ case PT_ANY:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if (notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_LAMP:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int chartype;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ chartype = UCD_CHARTYPE(fc);
+ if ((chartype == ucp_Lu ||
+ chartype == ucp_Ll ||
+ chartype == ucp_Lt) == notmatch)
+ break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_GC:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_CATEGORY(fc) == Lpropvalue) == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_PC:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_CHARTYPE(fc) == Lpropvalue) == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_SC:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_SCRIPT(fc) == Lpropvalue) == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_SCX:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ prop = GET_UCD(fc);
+ ok = (prop->script == Lpropvalue ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), Lpropvalue) != 0);
+ if (ok == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_ALNUM:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int category;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N) == notmatch)
+ break;
+ Feptr+= len;
+ }
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ switch(fc)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (notmatch) goto ENDLOOP99; /* Break the loop */
+ break;
+
+ default:
+ if ((UCD_CATEGORY(fc) == ucp_Z) == notmatch)
+ goto ENDLOOP99; /* Break the loop */
+ break;
+ }
+ Feptr+= len;
+ }
+ ENDLOOP99:
+ break;
+
+ case PT_WORD:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int category;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ category = UCD_CATEGORY(fc);
+ if ((category == ucp_L || category == ucp_N ||
+ fc == CHAR_UNDERSCORE) == notmatch)
+ break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_CLIST:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ const uint32_t *cp;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ cp = PRIV(ucd_caseless_sets) + Lpropvalue;
+ for (;;)
+ {
+ if (fc < *cp)
+ { if (notmatch) break; else goto GOT_MAX; }
+ if (fc == *cp++)
+ { if (notmatch) goto GOT_MAX; else break; }
+ }
+ Feptr += len;
+ }
+ GOT_MAX:
+ break;
+
+ case PT_UCNC:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((fc == CHAR_DOLLAR_SIGN || fc == CHAR_COMMERCIAL_AT ||
+ fc == CHAR_GRAVE_ACCENT || (fc >= 0xa0 && fc <= 0xd7ff) ||
+ fc >= 0xe000) == notmatch)
+ break;
+ Feptr += len;
+ }
+ break;
+
+ case PT_BIDICL:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ if ((UCD_BIDICLASS(fc) == Lpropvalue) == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ case PT_BOOL:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ BOOL ok;
+ const ucd_record *prop;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLENTEST(fc, Feptr, len);
+ prop = GET_UCD(fc);
+ ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), Lpropvalue) != 0;
+ if (ok == notmatch) break;
+ Feptr+= len;
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ /* Feptr is now past the end of the maximum run */
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't
+ go too far. */
+
+ for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM222);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ if (utf) BACKCHAR(Feptr);
+ }
+ }
+
+ /* Match extended Unicode grapheme clusters. We will get here only if the
+ support is in the binary; otherwise a compile-time error occurs. */
+
+ else if (Lctype == OP_EXTUNI)
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ else
+ {
+ GETCHARINCTEST(fc, Feptr);
+ Feptr = PRIV(extuni)(fc, Feptr, mb->start_subject, mb->end_subject,
+ utf, NULL);
+ }
+ CHECK_PARTIAL();
+ }
+
+ /* Feptr is now past the end of the maximum run */
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* We use <= Lstart_eptr rather than == Lstart_eptr to detect the start
+ of the run while backtracking because the use of \C in UTF mode can
+ cause BACKCHAR to move back past Lstart_eptr. This is just palliative;
+ the use of \C in UTF mode is fraught with danger. */
+
+ for(;;)
+ {
+ int lgb, rgb;
+ PCRE2_SPTR fptr;
+
+ if (Feptr <= Lstart_eptr) break; /* At start of char run */
+ RMATCH(Fecode, RM220);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+
+ /* Backtracking over an extended grapheme cluster involves inspecting
+ the previous two characters (if present) to see if a break is
+ permitted between them. */
+
+ Feptr--;
+ if (!utf) fc = *Feptr; else
+ {
+ BACKCHAR(Feptr);
+ GETCHAR(fc, Feptr);
+ }
+ rgb = UCD_GRAPHBREAK(fc);
+
+ for (;;)
+ {
+ if (Feptr <= Lstart_eptr) break; /* At start of char run */
+ fptr = Feptr - 1;
+ if (!utf) fc = *fptr; else
+ {
+ BACKCHAR(fptr);
+ GETCHAR(fc, fptr);
+ }
+ lgb = UCD_GRAPHBREAK(fc);
+ if ((PRIV(ucp_gbtable)[lgb] & (1u << rgb)) == 0) break;
+ Feptr = fptr;
+ rgb = lgb;
+ }
+ }
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ switch(Lctype)
+ {
+ case OP_ANY:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (IS_NEWLINE(Feptr)) break;
+ if (mb->partial != 0 && /* Take care with CRLF partial */
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ break;
+
+ case OP_ALLANY:
+ if (Lmax < UINT32_MAX)
+ {
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ Feptr++;
+ ACROSSCHAR(Feptr < mb->end_subject, Feptr, Feptr++);
+ }
+ }
+ else
+ {
+ Feptr = mb->end_subject; /* Unlimited UTF-8 repeat */
+ SCHECK_PARTIAL();
+ }
+ break;
+
+ /* The "byte" (i.e. "code unit") case is the same as non-UTF */
+
+ case OP_ANYBYTE:
+ fc = Lmax - Lmin;
+ if (fc > (uint32_t)(mb->end_subject - Feptr))
+ {
+ Feptr = mb->end_subject;
+ SCHECK_PARTIAL();
+ }
+ else Feptr += fc;
+ break;
+
+ case OP_ANYNL:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc == CHAR_CR)
+ {
+ if (++Feptr >= mb->end_subject) break;
+ if (UCHAR21(Feptr) == CHAR_LF) Feptr++;
+ }
+ else
+ {
+ if (fc != CHAR_LF &&
+ (mb->bsr_convention == PCRE2_BSR_ANYCRLF ||
+ (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL
+#ifndef EBCDIC
+ && fc != 0x2028 && fc != 0x2029
+#endif /* Not EBCDIC */
+ )))
+ break;
+ Feptr += len;
+ }
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ case OP_HSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ BOOL gotspace;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ switch(fc)
+ {
+ HSPACE_CASES: gotspace = TRUE; break;
+ default: gotspace = FALSE; break;
+ }
+ if (gotspace == (Lctype == OP_NOT_HSPACE)) break;
+ Feptr += len;
+ }
+ break;
+
+ case OP_NOT_VSPACE:
+ case OP_VSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ BOOL gotspace;
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ switch(fc)
+ {
+ VSPACE_CASES: gotspace = TRUE; break;
+ default: gotspace = FALSE; break;
+ }
+ if (gotspace == (Lctype == OP_NOT_VSPACE)) break;
+ Feptr += len;
+ }
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_digit) != 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 ||(mb->ctypes[fc] & ctype_digit) == 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_space) != 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 ||(mb->ctypes[fc] & ctype_space) == 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc < 256 && (mb->ctypes[fc] & ctype_word) != 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ int len = 1;
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ GETCHARLEN(fc, Feptr, len);
+ if (fc >= 256 || (mb->ctypes[fc] & ctype_word) == 0) break;
+ Feptr+= len;
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ /* After \C in UTF mode, Lstart_eptr might be in the middle of a
+ Unicode character. Use <= Lstart_eptr to ensure backtracking doesn't go
+ too far. */
+
+ for(;;)
+ {
+ if (Feptr <= Lstart_eptr) break;
+ RMATCH(Fecode, RM221);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ BACKCHAR(Feptr);
+ if (Lctype == OP_ANYNL && Feptr > Lstart_eptr &&
+ UCHAR21(Feptr) == CHAR_NL && UCHAR21(Feptr - 1) == CHAR_CR)
+ Feptr--;
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF mode */
+ {
+ switch(Lctype)
+ {
+ case OP_ANY:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (IS_NEWLINE(Feptr)) break;
+ if (mb->partial != 0 && /* Take care with CRLF partial */
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ *Feptr == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Feptr++;
+ }
+ break;
+
+ case OP_ALLANY:
+ case OP_ANYBYTE:
+ fc = Lmax - Lmin;
+ if (fc > (uint32_t)(mb->end_subject - Feptr))
+ {
+ Feptr = mb->end_subject;
+ SCHECK_PARTIAL();
+ }
+ else Feptr += fc;
+ break;
+
+ case OP_ANYNL:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ fc = *Feptr;
+ if (fc == CHAR_CR)
+ {
+ if (++Feptr >= mb->end_subject) break;
+ if (*Feptr == CHAR_LF) Feptr++;
+ }
+ else
+ {
+ if (fc != CHAR_LF && (mb->bsr_convention == PCRE2_BSR_ANYCRLF ||
+ (fc != CHAR_VT && fc != CHAR_FF && fc != CHAR_NEL
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ && fc != 0x2028 && fc != 0x2029
+#endif
+ ))) break;
+ Feptr++;
+ }
+ }
+ break;
+
+ case OP_NOT_HSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ switch(*Feptr)
+ {
+ default: Feptr++; break;
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ goto ENDLOOP00;
+ }
+ }
+ ENDLOOP00:
+ break;
+
+ case OP_HSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ switch(*Feptr)
+ {
+ default: goto ENDLOOP01;
+ HSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ HSPACE_MULTIBYTE_CASES:
+#endif
+ Feptr++; break;
+ }
+ }
+ ENDLOOP01:
+ break;
+
+ case OP_NOT_VSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ switch(*Feptr)
+ {
+ default: Feptr++; break;
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ goto ENDLOOP02;
+ }
+ }
+ ENDLOOP02:
+ break;
+
+ case OP_VSPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ switch(*Feptr)
+ {
+ default: goto ENDLOOP03;
+ VSPACE_BYTE_CASES:
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ VSPACE_MULTIBYTE_CASES:
+#endif
+ Feptr++; break;
+ }
+ }
+ ENDLOOP03:
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_digit) != 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_digit) == 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_space) != 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_space) == 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (MAX_255(*Feptr) && (mb->ctypes[*Feptr] & ctype_word) != 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = Lmin; i < Lmax; i++)
+ {
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ break;
+ }
+ if (!MAX_255(*Feptr) || (mb->ctypes[*Feptr] & ctype_word) == 0)
+ break;
+ Feptr++;
+ }
+ break;
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ if (reptype == REPTYPE_POS) continue; /* No backtracking */
+
+ for (;;)
+ {
+ if (Feptr == Lstart_eptr) break;
+ RMATCH(Fecode, RM34);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr--;
+ if (Lctype == OP_ANYNL && Feptr > Lstart_eptr && *Feptr == CHAR_LF &&
+ Feptr[-1] == CHAR_CR) Feptr--;
+ }
+ }
+ }
+ break; /* End of repeat character type processing */
+
+#undef Lstart_eptr
+#undef Lmin
+#undef Lmax
+#undef Lctype
+#undef Lpropvalue
+
+
+ /* ===================================================================== */
+ /* Match a back reference, possibly repeatedly. Look past the end of the
+ item to see if there is repeat information following. The OP_REF and
+ OP_REFI opcodes are used for a reference to a numbered group or to a
+ non-duplicated named group. For a duplicated named group, OP_DNREF and
+ OP_DNREFI are used. In this case we must scan the list of groups to which
+ the name refers, and use the first one that is set. */
+
+#define Lmin F->temp_32[0]
+#define Lmax F->temp_32[1]
+#define Lcaseless F->temp_32[2]
+#define Lstart F->temp_sptr[0]
+#define Loffset F->temp_size
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ Lcaseless = (Fop == OP_DNREFI);
+ {
+ int count = GET2(Fecode, 1+IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ Fecode += 1 + 2*IMM2_SIZE;
+
+ while (count-- > 0)
+ {
+ Loffset = (GET2(slot, 0) << 1) - 2;
+ if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ goto REF_REPEAT;
+
+ case OP_REF:
+ case OP_REFI:
+ Lcaseless = (Fop == OP_REFI);
+ Loffset = (GET2(Fecode, 1) << 1) - 2;
+ Fecode += 1 + IMM2_SIZE;
+
+ /* Set up for repetition, or handle the non-repeated case. The maximum and
+ minimum must be in the heap frame, but as they are short-term values, we
+ use temporary fields. */
+
+ REF_REPEAT:
+ switch (*Fecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ fc = *Fecode++ - OP_CRSTAR;
+ Lmin = rep_min[fc];
+ Lmax = rep_max[fc];
+ reptype = rep_typ[fc];
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ Lmin = GET2(Fecode, 1);
+ Lmax = GET2(Fecode, 1 + IMM2_SIZE);
+ reptype = rep_typ[*Fecode - OP_CRSTAR];
+ if (Lmax == 0) Lmax = UINT32_MAX; /* Max 0 => infinity */
+ Fecode += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default: /* No repeat follows */
+ {
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &length);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ Feptr += length;
+ continue; /* With the main loop */
+ }
+
+ /* Handle repeated back references. If a set group has length zero, just
+ continue with the main loop, because it matches however many times. For an
+ unset reference, if the minimum is zero, we can also just continue. We can
+ also continue if PCRE2_MATCH_UNSET_BACKREF is set, because this makes unset
+ group behave as a zero-length group. For any other unset cases, carrying
+ on will result in NOMATCH. */
+
+ if (Loffset < Foffset_top && Fovector[Loffset] != PCRE2_UNSET)
+ {
+ if (Fovector[Loffset] == Fovector[Loffset + 1]) continue;
+ }
+ else /* Group is not set */
+ {
+ if (Lmin == 0 || (mb->poptions & PCRE2_MATCH_UNSET_BACKREF) != 0)
+ continue;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+ for (i = 1; i <= Lmin; i++)
+ {
+ PCRE2_SIZE slength;
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr += slength;
+ }
+
+ /* If min = max, we are done. They are not both allowed to be zero. */
+
+ if (Lmin == Lmax) continue;
+
+ /* If minimizing, keep trying and advancing the pointer. */
+
+ if (reptype == REPTYPE_MIN)
+ {
+ for (;;)
+ {
+ PCRE2_SIZE slength;
+ RMATCH(Fecode, RM20);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Lmin++ >= Lmax) RRETURN(MATCH_NOMATCH);
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ if (rrc > 0) Feptr = mb->end_subject; /* Partial match */
+ CHECK_PARTIAL();
+ RRETURN(MATCH_NOMATCH);
+ }
+ Feptr += slength;
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest string and work backwards, as long as
+ the matched lengths for each iteration are the same. */
+
+ else
+ {
+ BOOL samelengths = TRUE;
+ Lstart = Feptr; /* Starting position */
+ Flength = Fovector[Loffset+1] - Fovector[Loffset];
+
+ for (i = Lmin; i < Lmax; i++)
+ {
+ PCRE2_SIZE slength;
+ rrc = match_ref(Loffset, Lcaseless, F, mb, &slength);
+ if (rrc != 0)
+ {
+ /* Can't use CHECK_PARTIAL because we don't want to update Feptr in
+ the soft partial matching case. */
+
+ if (rrc > 0 && mb->partial != 0 &&
+ mb->end_subject > mb->start_used_ptr)
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ break;
+ }
+
+ if (slength != Flength) samelengths = FALSE;
+ Feptr += slength;
+ }
+
+ /* If the length matched for each repetition is the same as the length of
+ the captured group, we can easily work backwards. This is the normal
+ case. However, in caseless UTF-8 mode there are pairs of case-equivalent
+ characters whose lengths (in terms of code units) differ. However, this
+ is very rare, so we handle it by re-matching fewer and fewer times. */
+
+ if (samelengths)
+ {
+ while (Feptr >= Lstart)
+ {
+ RMATCH(Fecode, RM21);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Feptr -= Flength;
+ }
+ }
+
+ /* The rare case of non-matching lengths. Re-scan the repetition for each
+ iteration. We know that match_ref() will succeed every time. */
+
+ else
+ {
+ Lmax = i;
+ for (;;)
+ {
+ RMATCH(Fecode, RM22);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (Feptr == Lstart) break; /* Failed after minimal repetition */
+ Feptr = Lstart;
+ Lmax--;
+ for (i = Lmin; i < Lmax; i++)
+ {
+ PCRE2_SIZE slength;
+ (void)match_ref(Loffset, Lcaseless, F, mb, &slength);
+ Feptr += slength;
+ }
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+#undef Lcaseless
+#undef Lmin
+#undef Lmax
+#undef Lstart
+#undef Loffset
+
+
+
+/* ========================================================================= */
+/* Opcodes for the start of various parenthesized items */
+/* ========================================================================= */
+
+ /* In all cases, if the result of RMATCH() is MATCH_THEN, check whether the
+ (*THEN) is within the current branch by comparing the address of OP_THEN
+ that is passed back with the end of the branch. If (*THEN) is within the
+ current branch, and the branch is one of two or more alternatives (it
+ either starts or ends with OP_ALT), we have reached the limit of THEN's
+ action, so convert the return code to NOMATCH, which will cause normal
+ backtracking to happen from now on. Otherwise, THEN is passed back to an
+ outer alternative. This implements Perl's treatment of parenthesized
+ groups, where a group not containing | does not affect the current
+ alternative, that is, (X) is NOT the same as (X|(*F)). */
+
+
+ /* ===================================================================== */
+ /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a non-possessive
+ bracket group, indicating that it may occur zero times. It may repeat
+ infinitely, or not at all - i.e. it could be ()* or ()? or even (){0} in
+ the pattern. Brackets with fixed upper repeat limits are compiled as a
+ number of copies, with the optional ones preceded by BRAZERO or BRAMINZERO.
+ Possessive groups with possible zero repeats are preceded by BRAPOSZERO. */
+
+#define Lnext_ecode F->temp_sptr[0]
+
+ case OP_BRAZERO:
+ Lnext_ecode = Fecode + 1;
+ RMATCH(Lnext_ecode, RM9);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT);
+ Fecode = Lnext_ecode + 1 + LINK_SIZE;
+ break;
+
+ case OP_BRAMINZERO:
+ Lnext_ecode = Fecode + 1;
+ do Lnext_ecode += GET(Lnext_ecode, 1); while (*Lnext_ecode == OP_ALT);
+ RMATCH(Lnext_ecode + 1 + LINK_SIZE, RM10);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode++;
+ break;
+
+#undef Lnext_ecode
+
+ case OP_SKIPZERO:
+ Fecode++;
+ do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT);
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* Handle possessive brackets with an unlimited repeat. The end of these
+ brackets will always be OP_KETRPOS, which returns MATCH_KETRPOS without
+ going further in the pattern. */
+
+#define Lframe_type F->temp_32[0]
+#define Lmatched_once F->temp_32[1]
+#define Lzero_allowed F->temp_32[2]
+#define Lstart_eptr F->temp_sptr[0]
+#define Lstart_group F->temp_sptr[1]
+
+ case OP_BRAPOSZERO:
+ Lzero_allowed = TRUE; /* Zero repeat is allowed */
+ Fecode += 1;
+ if (*Fecode == OP_CBRAPOS || *Fecode == OP_SCBRAPOS)
+ goto POSSESSIVE_CAPTURE;
+ goto POSSESSIVE_NON_CAPTURE;
+
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ Lzero_allowed = FALSE; /* Zero repeat not allowed */
+
+ POSSESSIVE_NON_CAPTURE:
+ Lframe_type = GF_NOCAPTURE; /* Remembered frame type */
+ goto POSSESSIVE_GROUP;
+
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ Lzero_allowed = FALSE; /* Zero repeat not allowed */
+
+ POSSESSIVE_CAPTURE:
+ number = GET2(Fecode, 1+LINK_SIZE);
+ Lframe_type = GF_CAPTURE | number; /* Remembered frame type */
+
+ POSSESSIVE_GROUP:
+ Lmatched_once = FALSE; /* Never matched */
+ Lstart_group = Fecode; /* Start of this group */
+
+ for (;;)
+ {
+ Lstart_eptr = Feptr; /* Position at group start */
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM8);
+ if (rrc == MATCH_KETRPOS)
+ {
+ Lmatched_once = TRUE; /* Matched at least once */
+ if (Feptr == Lstart_eptr) /* Empty match; skip to end */
+ {
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ break;
+ }
+
+ Fecode = Lstart_group;
+ continue;
+ }
+
+ /* See comment above about handling THEN. */
+
+ if (rrc == MATCH_THEN)
+ {
+ PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1);
+ if (mb->verb_ecode_ptr < next_ecode &&
+ (*Fecode == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ }
+
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) break;
+ }
+
+ /* Success if matched something or zero repeat allowed */
+
+ if (Lmatched_once || Lzero_allowed)
+ {
+ Fecode += 1 + LINK_SIZE;
+ break;
+ }
+
+ RRETURN(MATCH_NOMATCH);
+
+#undef Lmatched_once
+#undef Lzero_allowed
+#undef Lframe_type
+#undef Lstart_eptr
+#undef Lstart_group
+
+
+ /* ===================================================================== */
+ /* Handle non-capturing brackets that cannot match an empty string. When we
+ get to the final alternative within the brackets, as long as there are no
+ THEN's in the pattern, we can optimize by not recording a new backtracking
+ point. (Ideally we should test for a THEN within this group, but we don't
+ have that information.) Don't do this if we are at the very top level,
+ however, because that would make handling assertions and once-only brackets
+ messier when there is nothing to go back to. */
+
+#define Lframe_type F->temp_32[0] /* Set for all that use GROUPLOOP */
+#define Lnext_branch F->temp_sptr[0] /* Used only in OP_BRA handling */
+
+ case OP_BRA:
+ if (mb->hasthen || Frdepth == 0)
+ {
+ Lframe_type = 0;
+ goto GROUPLOOP;
+ }
+
+ for (;;)
+ {
+ Lnext_branch = Fecode + GET(Fecode, 1);
+ if (*Lnext_branch != OP_ALT) break;
+
+ /* This is never the final branch. We do not need to test for MATCH_THEN
+ here because this code is not used when there is a THEN in the pattern. */
+
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM1);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode = Lnext_branch;
+ }
+
+ /* Hit the start of the final branch. Continue at this level. */
+
+ Fecode += PRIV(OP_lengths)[*Fecode];
+ break;
+
+#undef Lnext_branch
+
+
+ /* ===================================================================== */
+ /* Handle a capturing bracket, other than those that are possessive with an
+ unlimited repeat. */
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ Lframe_type = GF_CAPTURE | GET2(Fecode, 1+LINK_SIZE);
+ goto GROUPLOOP;
+
+
+ /* ===================================================================== */
+ /* Atomic groups and non-capturing brackets that can match an empty string
+ must record a backtracking point and also set up a chained frame. */
+
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_SBRA:
+ Lframe_type = GF_NOCAPTURE | Fop;
+
+ GROUPLOOP:
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM2);
+ if (rrc == MATCH_THEN)
+ {
+ PCRE2_SPTR next_ecode = Fecode + GET(Fecode,1);
+ if (mb->verb_ecode_ptr < next_ecode &&
+ (*Fecode == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ }
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never reaches here. */
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* Recursion either matches the current regex, or some subexpression. The
+ offset data is the offset to the starting bracket from the start of the
+ whole pattern. (This is so that it works from duplicated subpatterns.) */
+
+#define Lframe_type F->temp_32[0]
+#define Lstart_branch F->temp_sptr[0]
+
+ case OP_RECURSE:
+ bracode = mb->start_code + GET(Fecode, 1);
+ number = (bracode == mb->start_code)? 0 : GET2(bracode, 1 + LINK_SIZE);
+
+ /* If we are already in a recursion, check for repeating the same one
+ without advancing the subject pointer. This should catch convoluted mutual
+ recursions. (Some simple cases are caught at compile time.) */
+
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ offset = Flast_group_offset;
+ while (offset != PCRE2_UNSET)
+ {
+ N = (heapframe *)((char *)match_data->heapframes + offset);
+ P = (heapframe *)((char *)N - frame_size);
+ if (N->group_frame_type == (GF_RECURSE | number))
+ {
+ if (Feptr == P->eptr) return PCRE2_ERROR_RECURSELOOP;
+ break;
+ }
+ offset = P->last_group_offset;
+ }
+ }
+
+ /* Now run the recursion, branch by branch. */
+
+ Lstart_branch = bracode;
+ Lframe_type = GF_RECURSE | number;
+
+ for (;;)
+ {
+ PCRE2_SPTR next_ecode;
+
+ group_frame_type = Lframe_type;
+ RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM11);
+ next_ecode = Lstart_branch + GET(Lstart_branch,1);
+
+ /* Handle backtracking verbs, which are defined in a range that can
+ easily be tested for. PCRE does not allow THEN, SKIP, PRUNE or COMMIT to
+ escape beyond a recursion; they cause a NOMATCH for the entire recursion.
+
+ When one of these verbs triggers, the current recursion group number is
+ recorded. If it matches the recursion we are processing, the verb
+ happened within the recursion and we must deal with it. Otherwise it must
+ have happened after the recursion completed, and so has to be passed
+ back. See comment above about handling THEN. */
+
+ if (rrc >= MATCH_BACKTRACK_MIN && rrc <= MATCH_BACKTRACK_MAX &&
+ mb->verb_current_recurse == (Lframe_type ^ GF_RECURSE))
+ {
+ if (rrc == MATCH_THEN && mb->verb_ecode_ptr < next_ecode &&
+ (*Lstart_branch == OP_ALT || *next_ecode == OP_ALT))
+ rrc = MATCH_NOMATCH;
+ else RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Note that carrying on after (*ACCEPT) in a recursion is handled in the
+ OP_ACCEPT code. Nothing needs to be done here. */
+
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Lstart_branch = next_ecode;
+ if (*Lstart_branch != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never reaches here. */
+
+#undef Lframe_type
+#undef Lstart_branch
+
+
+ /* ===================================================================== */
+ /* Positive assertions are like other groups except that PCRE doesn't allow
+ the effect of (*THEN) to escape beyond an assertion; it is therefore
+ treated as NOMATCH. (*ACCEPT) is treated as successful assertion, with its
+ captures and mark retained. Any other return is an error. */
+
+#define Lframe_type F->temp_32[0]
+
+ case OP_ASSERT:
+ case OP_ASSERTBACK:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ Lframe_type = GF_NOCAPTURE | Fop;
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM3);
+ if (rrc == MATCH_ACCEPT)
+ {
+ memcpy(Fovector,
+ (char *)assert_accept_frame + offsetof(heapframe, ovector),
+ assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = assert_accept_frame->offset_top;
+ Fmark = assert_accept_frame->mark;
+ break;
+ }
+ if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc);
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+ }
+
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* Handle negative assertions. Loop for each non-matching branch as for
+ positive assertions. */
+
+#define Lframe_type F->temp_32[0]
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK_NOT:
+ Lframe_type = GF_NOCAPTURE | Fop;
+
+ for (;;)
+ {
+ group_frame_type = Lframe_type;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM4);
+ switch(rrc)
+ {
+ case MATCH_ACCEPT: /* Assertion matched, therefore it fails. */
+ case MATCH_MATCH:
+ RRETURN (MATCH_NOMATCH);
+
+ case MATCH_NOMATCH: /* Branch failed, try next if present. */
+ case MATCH_THEN:
+ Fecode += GET(Fecode, 1);
+ if (*Fecode != OP_ALT) goto ASSERT_NOT_FAILED;
+ break;
+
+ case MATCH_COMMIT: /* Assertion forced to fail, therefore continue. */
+ case MATCH_SKIP:
+ case MATCH_PRUNE:
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ goto ASSERT_NOT_FAILED;
+
+ default: /* Pass back any other return */
+ RRETURN(rrc);
+ }
+ }
+
+ /* None of the branches have matched or there was a backtrack to (*COMMIT),
+ (*SKIP), (*PRUNE), or (*THEN) in the last branch. This is success for a
+ negative assertion, so carry on. */
+
+ ASSERT_NOT_FAILED:
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+#undef Lframe_type
+
+
+ /* ===================================================================== */
+ /* The callout item calls an external function, if one is provided, passing
+ details of the match so far. This is mainly for debugging, though the
+ function is able to force a failure. */
+
+ case OP_CALLOUT:
+ case OP_CALLOUT_STR:
+ rrc = do_callout(F, mb, &length);
+ if (rrc > 0) RRETURN(MATCH_NOMATCH);
+ if (rrc < 0) RRETURN(rrc);
+ Fecode += length;
+ break;
+
+
+ /* ===================================================================== */
+ /* Conditional group: compilation checked that there are no more than two
+ branches. If the condition is false, skipping the first branch takes us
+ past the end of the item if there is only one branch, but that's exactly
+ what we want. */
+
+ case OP_COND:
+ case OP_SCOND:
+
+ /* The variable Flength will be added to Fecode when the condition is
+ false, to get to the second branch. Setting it to the offset to the ALT or
+ KET, then incrementing Fecode achieves this effect. However, if the second
+ branch is non-existent, we must point to the KET so that the end of the
+ group is correctly processed. We now have Fecode pointing to the condition
+ or callout. */
+
+ Flength = GET(Fecode, 1); /* Offset to the second branch */
+ if (Fecode[Flength] != OP_ALT) Flength -= 1 + LINK_SIZE;
+ Fecode += 1 + LINK_SIZE; /* From this opcode */
+
+ /* Because of the way auto-callout works during compile, a callout item is
+ inserted between OP_COND and an assertion condition. Such a callout can
+ also be inserted manually. */
+
+ if (*Fecode == OP_CALLOUT || *Fecode == OP_CALLOUT_STR)
+ {
+ rrc = do_callout(F, mb, &length);
+ if (rrc > 0) RRETURN(MATCH_NOMATCH);
+ if (rrc < 0) RRETURN(rrc);
+
+ /* Advance Fecode past the callout, so it now points to the condition. We
+ must adjust Flength so that the value of Fecode+Flength is unchanged. */
+
+ Fecode += length;
+ Flength -= length;
+ }
+
+ /* Test the various possible conditions */
+
+ condition = FALSE;
+ switch(*Fecode)
+ {
+ case OP_RREF: /* Group recursion test */
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ number = GET2(Fecode, 1);
+ condition = (number == RREF_ANY || number == Fcurrent_recurse);
+ }
+ break;
+
+ case OP_DNRREF: /* Duplicate named group recursion test */
+ if (Fcurrent_recurse != RECURSE_UNSET)
+ {
+ int count = GET2(Fecode, 1 + IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ while (count-- > 0)
+ {
+ number = GET2(slot, 0);
+ condition = number == Fcurrent_recurse;
+ if (condition) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ break;
+
+ case OP_CREF: /* Numbered group used test */
+ offset = (GET2(Fecode, 1) << 1) - 2; /* Doubled ref number */
+ condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET;
+ break;
+
+ case OP_DNCREF: /* Duplicate named group used test */
+ {
+ int count = GET2(Fecode, 1 + IMM2_SIZE);
+ PCRE2_SPTR slot = mb->name_table + GET2(Fecode, 1) * mb->name_entry_size;
+ while (count-- > 0)
+ {
+ offset = (GET2(slot, 0) << 1) - 2;
+ condition = offset < Foffset_top && Fovector[offset] != PCRE2_UNSET;
+ if (condition) break;
+ slot += mb->name_entry_size;
+ }
+ }
+ break;
+
+ case OP_FALSE:
+ case OP_FAIL: /* The assertion (?!) becomes OP_FAIL */
+ break;
+
+ case OP_TRUE:
+ condition = TRUE;
+ break;
+
+ /* The condition is an assertion. Run code similar to the assertion code
+ above. */
+
+#define Lpositive F->temp_32[0]
+#define Lstart_branch F->temp_sptr[0]
+
+ default:
+ Lpositive = (*Fecode == OP_ASSERT || *Fecode == OP_ASSERTBACK);
+ Lstart_branch = Fecode;
+
+ for (;;)
+ {
+ group_frame_type = GF_CONDASSERT | *Fecode;
+ RMATCH(Lstart_branch + PRIV(OP_lengths)[*Lstart_branch], RM5);
+
+ switch(rrc)
+ {
+ case MATCH_ACCEPT: /* Save captures */
+ memcpy(Fovector,
+ (char *)assert_accept_frame + offsetof(heapframe, ovector),
+ assert_accept_frame->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = assert_accept_frame->offset_top;
+
+ /* Fall through */
+ /* In the case of a match, the captures have already been put into
+ the current frame. */
+
+ case MATCH_MATCH:
+ condition = Lpositive; /* TRUE for positive assertion */
+ break;
+
+ /* PCRE doesn't allow the effect of (*THEN) to escape beyond an
+ assertion; it is therefore always treated as NOMATCH. */
+
+ case MATCH_NOMATCH:
+ case MATCH_THEN:
+ Lstart_branch += GET(Lstart_branch, 1);
+ if (*Lstart_branch == OP_ALT) continue; /* Try next branch */
+ condition = !Lpositive; /* TRUE for negative assertion */
+ break;
+
+ /* These force no match without checking other branches. */
+
+ case MATCH_COMMIT:
+ case MATCH_SKIP:
+ case MATCH_PRUNE:
+ condition = !Lpositive;
+ break;
+
+ default:
+ RRETURN(rrc);
+ }
+ break; /* Out of the branch loop */
+ }
+
+ /* If the condition is true, find the end of the assertion so that
+ advancing past it gets us to the start of the first branch. */
+
+ if (condition)
+ {
+ do Fecode += GET(Fecode, 1); while (*Fecode == OP_ALT);
+ }
+ break; /* End of assertion condition */
+ }
+
+#undef Lpositive
+#undef Lstart_branch
+
+ /* Choose branch according to the condition. */
+
+ Fecode += condition? PRIV(OP_lengths)[*Fecode] : Flength;
+
+ /* If the opcode is OP_SCOND it means we are at a repeated conditional
+ group that might match an empty string. We must therefore descend a level
+ so that the start is remembered for checking. For OP_COND we can just
+ continue at this level. */
+
+ if (Fop == OP_SCOND)
+ {
+ group_frame_type = GF_NOCAPTURE | Fop;
+ RMATCH(Fecode, RM35);
+ RRETURN(rrc);
+ }
+ break;
+
+
+
+/* ========================================================================= */
+/* End of start of parenthesis opcodes */
+/* ========================================================================= */
+
+
+ /* ===================================================================== */
+ /* Move the subject pointer back. This occurs only at the start of each
+ branch of a lookbehind assertion. If we are too close to the start to move
+ back, fail. When working with UTF-8 we move back a number of characters,
+ not bytes. */
+
+ case OP_REVERSE:
+ number = GET(Fecode, 1);
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (number-- > 0)
+ {
+ if (Feptr <= mb->check_subject) RRETURN(MATCH_NOMATCH);
+ Feptr--;
+ BACKCHAR(Feptr);
+ }
+ }
+ else
+#endif
+
+ /* No UTF-8 support, or not in UTF-8 mode: count is code unit count */
+
+ {
+ if ((ptrdiff_t)number > Feptr - mb->start_subject) RRETURN(MATCH_NOMATCH);
+ Feptr -= number;
+ }
+
+ /* Save the earliest consulted character, then skip to next opcode */
+
+ if (Feptr < mb->start_used_ptr) mb->start_used_ptr = Feptr;
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* An alternation is the end of a branch; scan along to find the end of the
+ bracketed group. */
+
+ case OP_ALT:
+ do Fecode += GET(Fecode,1); while (*Fecode == OP_ALT);
+ break;
+
+
+ /* ===================================================================== */
+ /* The end of a parenthesized group. For all but OP_BRA and OP_COND, the
+ starting frame was added to the chained frames in order to remember the
+ starting subject position for the group. */
+
+ case OP_KET:
+ case OP_KETRMIN:
+ case OP_KETRMAX:
+ case OP_KETRPOS:
+
+ bracode = Fecode - GET(Fecode, 1);
+
+ /* Point N to the frame at the start of the most recent group.
+ Remember the subject pointer at the start of the group. */
+
+ if (*bracode != OP_BRA && *bracode != OP_COND)
+ {
+ N = (heapframe *)((char *)match_data->heapframes + Flast_group_offset);
+ P = (heapframe *)((char *)N - frame_size);
+ Flast_group_offset = P->last_group_offset;
+
+#ifdef DEBUG_SHOW_RMATCH
+ fprintf(stderr, "++ KET for frame=%d type=%x prev char offset=%lu\n",
+ N->rdepth, N->group_frame_type,
+ (char *)P->eptr - (char *)mb->start_subject);
+#endif
+
+ /* If we are at the end of an assertion that is a condition, return a
+ match, discarding any intermediate backtracking points. Copy back the
+ mark setting and the captures into the frame before N so that they are
+ set on return. Doing this for all assertions, both positive and negative,
+ seems to match what Perl does. */
+
+ if (GF_IDMASK(N->group_frame_type) == GF_CONDASSERT)
+ {
+ memcpy((char *)P + offsetof(heapframe, ovector), Fovector,
+ Foffset_top * sizeof(PCRE2_SIZE));
+ P->offset_top = Foffset_top;
+ P->mark = Fmark;
+ Fback_frame = (char *)F - (char *)P;
+ RRETURN(MATCH_MATCH);
+ }
+ }
+ else P = NULL; /* Indicates starting frame not recorded */
+
+ /* The group was not a conditional assertion. */
+
+ switch (*bracode)
+ {
+ case OP_BRA: /* No need to do anything for these */
+ case OP_COND:
+ case OP_SCOND:
+ break;
+
+ /* Non-atomic positive assertions are like OP_BRA, except that the
+ subject pointer must be put back to where it was at the start of the
+ assertion. */
+
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+ Feptr = P->eptr;
+ break;
+
+ /* Atomic positive assertions are like OP_ONCE, except that in addition
+ the subject pointer must be put back to where it was at the start of the
+ assertion. */
+
+ case OP_ASSERT:
+ case OP_ASSERTBACK:
+ if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+ Feptr = P->eptr;
+ /* Fall through */
+
+ /* For an atomic group, discard internal backtracking points. We must
+ also ensure that any remaining branches within the top-level of the group
+ are not tried. Do this by adjusting the code pointer within the backtrack
+ frame so that it points to the final branch. */
+
+ case OP_ONCE:
+ Fback_frame = ((char *)F - (char *)P);
+ for (;;)
+ {
+ uint32_t y = GET(P->ecode,1);
+ if ((P->ecode)[y] != OP_ALT) break;
+ P->ecode += y;
+ }
+ break;
+
+ /* A matching negative assertion returns MATCH, which is turned into
+ NOMATCH at the assertion level. */
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK_NOT:
+ RRETURN(MATCH_MATCH);
+
+ /* At the end of a script run, apply the script-checking rules. This code
+ will never by exercised if Unicode support it not compiled, because in
+ that environment script runs cause an error at compile time. */
+
+ case OP_SCRIPT_RUN:
+ if (!PRIV(script_run)(P->eptr, Feptr, utf)) RRETURN(MATCH_NOMATCH);
+ break;
+
+ /* Whole-pattern recursion is coded as a recurse into group 0, so it
+ won't be picked up here. Instead, we catch it when the OP_END is reached.
+ Other recursion is handled here. */
+
+ case OP_CBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRA:
+ case OP_SCBRAPOS:
+ number = GET2(bracode, 1+LINK_SIZE);
+
+ /* Handle a recursively called group. We reinstate the previous set of
+ captures and then carry on after the recursion call. */
+
+ if (Fcurrent_recurse == number)
+ {
+ P = (heapframe *)((char *)N - frame_size);
+ memcpy((char *)F + offsetof(heapframe, ovector), P->ovector,
+ P->offset_top * sizeof(PCRE2_SIZE));
+ Foffset_top = P->offset_top;
+ Fcapture_last = P->capture_last;
+ Fcurrent_recurse = P->current_recurse;
+ Fecode = P->ecode + 1 + LINK_SIZE;
+ continue; /* With next opcode */
+ }
+
+ /* Deal with actual capturing. */
+
+ offset = (number << 1) - 2;
+ Fcapture_last = number;
+ Fovector[offset] = P->eptr - mb->start_subject;
+ Fovector[offset+1] = Feptr - mb->start_subject;
+ if (offset >= Foffset_top) Foffset_top = offset + 2;
+ break;
+ } /* End actions relating to the starting opcode */
+
+ /* OP_KETRPOS is a possessive repeating ket. Remember the current position,
+ and return the MATCH_KETRPOS. This makes it possible to do the repeats one
+ at a time from the outer level. This must precede the empty string test -
+ in this case that test is done at the outer level. */
+
+ if (*Fecode == OP_KETRPOS)
+ {
+ memcpy((char *)P + offsetof(heapframe, eptr),
+ (char *)F + offsetof(heapframe, eptr),
+ frame_copy_size);
+ RRETURN(MATCH_KETRPOS);
+ }
+
+ /* Handle the different kinds of closing brackets. A non-repeating ket
+ needs no special action, just continuing at this level. This also happens
+ for the repeating kets if the group matched no characters, in order to
+ forcibly break infinite loops. Otherwise, the repeating kets try the rest
+ of the pattern or restart from the preceding bracket, in the appropriate
+ order. */
+
+ if (Fop != OP_KET && (P == NULL || Feptr != P->eptr))
+ {
+ if (Fop == OP_KETRMIN)
+ {
+ RMATCH(Fecode + 1 + LINK_SIZE, RM6);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ Fecode -= GET(Fecode, 1);
+ break; /* End of ket processing */
+ }
+
+ /* Repeat the maximum number of times (KETRMAX) */
+
+ RMATCH(bracode, RM7);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+
+ /* Carry on at this level for a non-repeating ket, or after matching an
+ empty string, or after repeating for a maximum number of times. */
+
+ Fecode += 1 + LINK_SIZE;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start and end of line assertions, not multiline mode. */
+
+ case OP_CIRC: /* Start of line, unless PCRE2_NOTBOL is set. */
+ if (Feptr != mb->start_subject || (mb->moptions & PCRE2_NOTBOL) != 0)
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ case OP_SOD: /* Unconditional start of subject */
+ if (Feptr != mb->start_subject) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ /* When PCRE2_NOTEOL is unset, assert before the subject end, or a
+ terminating newline unless PCRE2_DOLLAR_ENDONLY is set. */
+
+ case OP_DOLL:
+ if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);
+ if ((mb->poptions & PCRE2_DOLLAR_ENDONLY) == 0) goto ASSERT_NL_OR_EOS;
+
+ /* Fall through */
+ /* Unconditional end of subject assertion (\z) */
+
+ case OP_EOD:
+ if (Feptr < mb->end_subject) RRETURN(MATCH_NOMATCH);
+ if (mb->partial != 0)
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Fecode++;
+ break;
+
+ /* End of subject or ending \n assertion (\Z) */
+
+ case OP_EODN:
+ ASSERT_NL_OR_EOS:
+ if (Feptr < mb->end_subject &&
+ (!IS_NEWLINE(Feptr) || Feptr != mb->end_subject - mb->nllen))
+ {
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Either at end of string or \n before end. */
+
+ if (mb->partial != 0)
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start and end of line assertions, multiline mode. */
+
+ /* Start of subject unless notbol, or after any newline except for one at
+ the very end, unless PCRE2_ALT_CIRCUMFLEX is set. */
+
+ case OP_CIRCM:
+ if ((mb->moptions & PCRE2_NOTBOL) != 0 && Feptr == mb->start_subject)
+ RRETURN(MATCH_NOMATCH);
+ if (Feptr != mb->start_subject &&
+ ((Feptr == mb->end_subject &&
+ (mb->poptions & PCRE2_ALT_CIRCUMFLEX) == 0) ||
+ !WAS_NEWLINE(Feptr)))
+ RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+ /* Assert before any newline, or before end of subject unless noteol is
+ set. */
+
+ case OP_DOLLM:
+ if (Feptr < mb->end_subject)
+ {
+ if (!IS_NEWLINE(Feptr))
+ {
+ if (mb->partial != 0 &&
+ Feptr + 1 >= mb->end_subject &&
+ NLBLOCK->nltype == NLTYPE_FIXED &&
+ NLBLOCK->nllen == 2 &&
+ UCHAR21TEST(Feptr) == NLBLOCK->nl[0])
+ {
+ mb->hitend = TRUE;
+ if (mb->partial > 1) return PCRE2_ERROR_PARTIAL;
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+ {
+ if ((mb->moptions & PCRE2_NOTEOL) != 0) RRETURN(MATCH_NOMATCH);
+ SCHECK_PARTIAL();
+ }
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Start of match assertion */
+
+ case OP_SOM:
+ if (Feptr != mb->start_subject + mb->start_offset) RRETURN(MATCH_NOMATCH);
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Reset the start of match point */
+
+ case OP_SET_SOM:
+ Fstart_match = Feptr;
+ Fecode++;
+ break;
+
+
+ /* ===================================================================== */
+ /* Word boundary assertions. Find out if the previous and current
+ characters are "word" characters. It takes a bit more work in UTF mode.
+ Characters > 255 are assumed to be "non-word" characters when PCRE2_UCP is
+ not set. When it is set, use Unicode properties if available, even when not
+ in UTF mode. Remember the earliest and latest consulted characters. */
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ if (Feptr == mb->check_subject) prev_is_word = FALSE; else
+ {
+ PCRE2_SPTR lastptr = Feptr - 1;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ BACKCHAR(lastptr);
+ GETCHAR(fc, lastptr);
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ fc = *lastptr;
+ if (lastptr < mb->start_used_ptr) mb->start_used_ptr = lastptr;
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (fc == '_') prev_is_word = TRUE; else
+ {
+ int cat = UCD_CATEGORY(fc);
+ prev_is_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ prev_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0;
+ }
+
+ /* Get status of next character */
+
+ if (Feptr >= mb->end_subject)
+ {
+ SCHECK_PARTIAL();
+ cur_is_word = FALSE;
+ }
+ else
+ {
+ PCRE2_SPTR nextptr = Feptr + 1;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ FORWARDCHARTEST(nextptr, mb->end_subject);
+ GETCHAR(fc, Feptr);
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ fc = *Feptr;
+ if (nextptr > mb->last_used_ptr) mb->last_used_ptr = nextptr;
+#ifdef SUPPORT_UNICODE
+ if ((mb->poptions & PCRE2_UCP) != 0)
+ {
+ if (fc == '_') cur_is_word = TRUE; else
+ {
+ int cat = UCD_CATEGORY(fc);
+ cur_is_word = (cat == ucp_L || cat == ucp_N);
+ }
+ }
+ else
+#endif /* SUPPORT_UNICODE */
+ cur_is_word = CHMAX_255(fc) && (mb->ctypes[fc] & ctype_word) != 0;
+ }
+
+ /* Now see if the situation is what we want */
+
+ if ((*Fecode++ == OP_WORD_BOUNDARY)?
+ cur_is_word == prev_is_word : cur_is_word != prev_is_word)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+
+ /* ===================================================================== */
+ /* Backtracking (*VERB)s, with and without arguments. Note that if the
+ pattern is successfully matched, we do not come back from RMATCH. */
+
+ case OP_MARK:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM12);
+
+ /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an
+ argument, and we must check whether that argument matches this MARK's
+ argument. It is passed back in mb->verb_skip_ptr. If it does match, we
+ return MATCH_SKIP with mb->verb_skip_ptr now pointing to the subject
+ position that corresponds to this mark. Otherwise, pass back the return
+ code unaltered. */
+
+ if (rrc == MATCH_SKIP_ARG &&
+ PRIV(strcmp)(Fecode + 2, mb->verb_skip_ptr) == 0)
+ {
+ mb->verb_skip_ptr = Feptr; /* Pass back current position */
+ RRETURN(MATCH_SKIP);
+ }
+ RRETURN(rrc);
+
+ case OP_FAIL:
+ RRETURN(MATCH_NOMATCH);
+
+ /* Record the current recursing group number in mb->verb_current_recurse
+ when a backtracking return such as MATCH_COMMIT is given. This enables the
+ recurse processing to catch verbs from within the recursion. */
+
+ case OP_COMMIT:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM13);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_COMMIT);
+
+ case OP_COMMIT_ARG:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM36);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_COMMIT);
+
+ case OP_PRUNE:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM14);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_PRUNE);
+
+ case OP_PRUNE_ARG:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM15);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_PRUNE);
+
+ case OP_SKIP:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM16);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_skip_ptr = Feptr; /* Pass back current position */
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_SKIP);
+
+ /* Note that, for Perl compatibility, SKIP with an argument does NOT set
+ nomatch_mark. When a pattern match ends with a SKIP_ARG for which there was
+ not a matching mark, we have to re-run the match, ignoring the SKIP_ARG
+ that failed and any that precede it (either they also failed, or were not
+ triggered). To do this, we maintain a count of executed SKIP_ARGs. If a
+ SKIP_ARG gets to top level, the match is re-run with mb->ignore_skip_arg
+ set to the count of the one that failed. */
+
+ case OP_SKIP_ARG:
+ mb->skip_arg_count++;
+ if (mb->skip_arg_count <= mb->ignore_skip_arg)
+ {
+ Fecode += PRIV(OP_lengths)[*Fecode] + Fecode[1];
+ break;
+ }
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM17);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+
+ /* Pass back the current skip name and return the special MATCH_SKIP_ARG
+ return code. This will either be caught by a matching MARK, or get to the
+ top, where it causes a rematch with mb->ignore_skip_arg set to the value of
+ mb->skip_arg_count. */
+
+ mb->verb_skip_ptr = Fecode + 2;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_SKIP_ARG);
+
+ /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that
+ the branch in which it occurs can be determined. */
+
+ case OP_THEN:
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode], RM18);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_ecode_ptr = Fecode;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_THEN);
+
+ case OP_THEN_ARG:
+ Fmark = mb->nomatch_mark = Fecode + 2;
+ RMATCH(Fecode + PRIV(OP_lengths)[*Fecode] + Fecode[1], RM19);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ mb->verb_ecode_ptr = Fecode;
+ mb->verb_current_recurse = Fcurrent_recurse;
+ RRETURN(MATCH_THEN);
+
+
+ /* ===================================================================== */
+ /* There's been some horrible disaster. Arrival here can only mean there is
+ something seriously wrong in the code above or the OP_xxx definitions. */
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+
+ /* Do not insert any code in here without much thought; it is assumed
+ that "continue" in the code above comes out to here to repeat the main
+ loop. */
+
+ } /* End of main loop */
+/* Control never reaches here */
+
+
+/* ========================================================================= */
+/* The RRETURN() macro jumps here. The number that is saved in Freturn_id
+indicates which label we actually want to return to. The value in Frdepth is
+the index number of the frame in the vector. The return value has been placed
+in rrc. */
+
+#define LBL(val) case val: goto L_RM##val;
+
+RETURN_SWITCH:
+if (Feptr > mb->last_used_ptr) mb->last_used_ptr = Feptr;
+if (Frdepth == 0) return rrc; /* Exit from the top level */
+F = (heapframe *)((char *)F - Fback_frame); /* Backtrack */
+mb->cb->callout_flags |= PCRE2_CALLOUT_BACKTRACK; /* Note for callouts */
+
+#ifdef DEBUG_SHOW_RMATCH
+fprintf(stderr, "++ RETURN %d to %d\n", rrc, Freturn_id);
+#endif
+
+switch (Freturn_id)
+ {
+ LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8)
+ LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(16)
+ LBL(17) LBL(18) LBL(19) LBL(20) LBL(21) LBL(22) LBL(23) LBL(24)
+ LBL(25) LBL(26) LBL(27) LBL(28) LBL(29) LBL(30) LBL(31) LBL(32)
+ LBL(33) LBL(34) LBL(35) LBL(36)
+
+#ifdef SUPPORT_WIDE_CHARS
+ LBL(100) LBL(101)
+#endif
+
+#ifdef SUPPORT_UNICODE
+ LBL(200) LBL(201) LBL(202) LBL(203) LBL(204) LBL(205) LBL(206)
+ LBL(207) LBL(208) LBL(209) LBL(210) LBL(211) LBL(212) LBL(213)
+ LBL(214) LBL(215) LBL(216) LBL(217) LBL(218) LBL(219) LBL(220)
+ LBL(221) LBL(222) LBL(223) LBL(224) LBL(225)
+#endif
+
+ default:
+ return PCRE2_ERROR_INTERNAL;
+ }
+#undef LBL
+}
+
+
+/*************************************************
+* Match a Regular Expression *
+*************************************************/
+
+/* This function applies a compiled pattern to a subject string and picks out
+portions of the string if it matches. Two elements in the vector are set for
+each substring: the offsets to the start and end of the substring.
+
+Arguments:
+ code points to the compiled expression
+ subject points to the subject string
+ length length of subject string (may contain binary zeros)
+ start_offset where to start in the subject string
+ options option bits
+ match_data points to a match_data block
+ mcontext points a PCRE2 context
+
+Returns: > 0 => success; value is the number of ovector pairs filled
+ = 0 => success, but ovector is not big enough
+ = -1 => failed to match (PCRE2_ERROR_NOMATCH)
+ = -2 => partial match (PCRE2_ERROR_PARTIAL)
+ < -2 => some kind of unexpected problem
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_match(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
+ PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
+ pcre2_match_context *mcontext)
+{
+int rc;
+int was_zero_terminated = 0;
+const uint8_t *start_bits = NULL;
+const pcre2_real_code *re = (const pcre2_real_code *)code;
+
+BOOL anchored;
+BOOL firstline;
+BOOL has_first_cu = FALSE;
+BOOL has_req_cu = FALSE;
+BOOL startline;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+PCRE2_SPTR memchr_found_first_cu;
+PCRE2_SPTR memchr_found_first_cu2;
+#endif
+
+PCRE2_UCHAR first_cu = 0;
+PCRE2_UCHAR first_cu2 = 0;
+PCRE2_UCHAR req_cu = 0;
+PCRE2_UCHAR req_cu2 = 0;
+
+PCRE2_SPTR bumpalong_limit;
+PCRE2_SPTR end_subject;
+PCRE2_SPTR true_end_subject;
+PCRE2_SPTR start_match;
+PCRE2_SPTR req_cu_ptr;
+PCRE2_SPTR start_partial;
+PCRE2_SPTR match_partial;
+
+#ifdef SUPPORT_JIT
+BOOL use_jit;
+#endif
+
+/* This flag is needed even when Unicode is not supported for convenience
+(it is used by the IS_NEWLINE macro). */
+
+BOOL utf = FALSE;
+
+#ifdef SUPPORT_UNICODE
+BOOL ucp = FALSE;
+BOOL allow_invalid;
+uint32_t fragment_options = 0;
+#ifdef SUPPORT_JIT
+BOOL jit_checked_utf = FALSE;
+#endif
+#endif /* SUPPORT_UNICODE */
+
+PCRE2_SIZE frame_size;
+PCRE2_SIZE heapframes_size;
+
+/* We need to have mb as a pointer to a match block, because the IS_NEWLINE
+macro is used below, and it expects NLBLOCK to be defined as a pointer. */
+
+pcre2_callout_block cb;
+match_block actual_match_block;
+match_block *mb = &actual_match_block;
+
+/* Recognize NULL, length 0 as an empty string. */
+
+if (subject == NULL && length == 0) subject = (PCRE2_SPTR)"";
+
+/* Plausibility checks */
+
+if ((options & ~PUBLIC_MATCH_OPTIONS) != 0) return PCRE2_ERROR_BADOPTION;
+if (code == NULL || subject == NULL || match_data == NULL)
+ return PCRE2_ERROR_NULL;
+
+start_match = subject + start_offset;
+req_cu_ptr = start_match - 1;
+if (length == PCRE2_ZERO_TERMINATED)
+ {
+ length = PRIV(strlen)(subject);
+ was_zero_terminated = 1;
+ }
+true_end_subject = end_subject = subject + length;
+
+if (start_offset > length) return PCRE2_ERROR_BADOFFSET;
+
+/* Check that the first field in the block is the magic number. */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+
+/* Check the code unit width. */
+
+if ((re->flags & PCRE2_MODE_MASK) != PCRE2_CODE_UNIT_WIDTH/8)
+ return PCRE2_ERROR_BADMODE;
+
+/* PCRE2_NOTEMPTY and PCRE2_NOTEMPTY_ATSTART are match-time flags in the
+options variable for this function. Users of PCRE2 who are not calling the
+function directly would like to have a way of setting these flags, in the same
+way that they can set pcre2_compile() flags like PCRE2_NO_AUTOPOSSESS with
+constructions like (*NO_AUTOPOSSESS). To enable this, (*NOTEMPTY) and
+(*NOTEMPTY_ATSTART) set bits in the pattern's "flag" function which we now
+transfer to the options for this function. The bits are guaranteed to be
+adjacent, but do not have the same values. This bit of Boolean trickery assumes
+that the match-time bits are not more significant than the flag bits. If by
+accident this is not the case, a compile-time division by zero error will
+occur. */
+
+#define FF (PCRE2_NOTEMPTY_SET|PCRE2_NE_ATST_SET)
+#define OO (PCRE2_NOTEMPTY|PCRE2_NOTEMPTY_ATSTART)
+options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1)));
+#undef FF
+#undef OO
+
+/* If the pattern was successfully studied with JIT support, we will run the
+JIT executable instead of the rest of this function. Most options must be set
+at compile time for the JIT code to be usable. */
+
+#ifdef SUPPORT_JIT
+use_jit = (re->executable_jit != NULL &&
+ (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0);
+#endif
+
+/* Initialize UTF/UCP parameters. */
+
+#ifdef SUPPORT_UNICODE
+utf = (re->overall_options & PCRE2_UTF) != 0;
+allow_invalid = (re->overall_options & PCRE2_MATCH_INVALID_UTF) != 0;
+ucp = (re->overall_options & PCRE2_UCP) != 0;
+#endif /* SUPPORT_UNICODE */
+
+/* Convert the partial matching flags into an integer. */
+
+mb->partial = ((options & PCRE2_PARTIAL_HARD) != 0)? 2 :
+ ((options & PCRE2_PARTIAL_SOFT) != 0)? 1 : 0;
+
+/* Partial matching and PCRE2_ENDANCHORED are currently not allowed at the same
+time. */
+
+if (mb->partial != 0 &&
+ ((re->overall_options | options) & PCRE2_ENDANCHORED) != 0)
+ return PCRE2_ERROR_BADOPTION;
+
+/* It is an error to set an offset limit without setting the flag at compile
+time. */
+
+if (mcontext != NULL && mcontext->offset_limit != PCRE2_UNSET &&
+ (re->overall_options & PCRE2_USE_OFFSET_LIMIT) == 0)
+ return PCRE2_ERROR_BADOFFSETLIMIT;
+
+/* If the match data block was previously used with PCRE2_COPY_MATCHED_SUBJECT,
+free the memory that was obtained. Set the field to NULL for no match cases. */
+
+if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)
+ {
+ match_data->memctl.free((void *)match_data->subject,
+ match_data->memctl.memory_data);
+ match_data->flags &= ~PCRE2_MD_COPIED_SUBJECT;
+ }
+match_data->subject = NULL;
+
+/* Zero the error offset in case the first code unit is invalid UTF. */
+
+match_data->startchar = 0;
+
+
+/* ============================= JIT matching ============================== */
+
+/* Prepare for JIT matching. Check a UTF string for validity unless no check is
+requested or invalid UTF can be handled. We check only the portion of the
+subject that might be be inspected during matching - from the offset minus the
+maximum lookbehind to the given length. This saves time when a small part of a
+large subject is being matched by the use of a starting offset. Note that the
+maximum lookbehind is a number of characters, not code units. */
+
+#ifdef SUPPORT_JIT
+if (use_jit)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf && (options & PCRE2_NO_UTF_CHECK) == 0 && !allow_invalid)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ unsigned int i;
+#endif
+
+ /* For 8-bit and 16-bit UTF, check that the first code unit is a valid
+ character start. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if (start_match < end_subject && NOT_FIRSTCU(*start_match))
+ {
+ if (start_offset > 0) return PCRE2_ERROR_BADUTFOFFSET;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ return PCRE2_ERROR_UTF8_ERR20; /* Isolated 0x80 byte */
+#else
+ return PCRE2_ERROR_UTF16_ERR3; /* Isolated low surrogate */
+#endif
+ }
+#endif /* WIDTH != 32 */
+
+ /* Move back by the maximum lookbehind, just in case it happens at the very
+ start of matching. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ for (i = re->max_lookbehind; i > 0 && start_match > subject; i--)
+ {
+ start_match--;
+ while (start_match > subject &&
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ (*start_match & 0xc0) == 0x80)
+#else /* 16-bit */
+ (*start_match & 0xfc00) == 0xdc00)
+#endif
+ start_match--;
+ }
+#else /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+ /* In the 32-bit library, one code unit equals one character. However,
+ we cannot just subtract the lookbehind and then compare pointers, because
+ a very large lookbehind could create an invalid pointer. */
+
+ if (start_offset >= re->max_lookbehind)
+ start_match -= re->max_lookbehind;
+ else
+ start_match = subject;
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+ /* Validate the relevant portion of the subject. Adjust the offset of an
+ invalid code point to be an absolute offset in the whole string. */
+
+ match_data->rc = PRIV(valid_utf)(start_match,
+ length - (start_match - subject), &(match_data->startchar));
+ if (match_data->rc != 0)
+ {
+ match_data->startchar += start_match - subject;
+ return match_data->rc;
+ }
+ jit_checked_utf = TRUE;
+ }
+#endif /* SUPPORT_UNICODE */
+
+ /* If JIT returns BADOPTION, which means that the selected complete or
+ partial matching mode was not compiled, fall through to the interpreter. */
+
+ rc = pcre2_jit_match(code, subject, length, start_offset, options,
+ match_data, mcontext);
+ if (rc != PCRE2_ERROR_JIT_BADOPTION)
+ {
+ if (rc >= 0 && (options & PCRE2_COPY_MATCHED_SUBJECT) != 0)
+ {
+ length = CU2BYTES(length + was_zero_terminated);
+ match_data->subject = match_data->memctl.malloc(length,
+ match_data->memctl.memory_data);
+ if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy((void *)match_data->subject, subject, length);
+ match_data->flags |= PCRE2_MD_COPIED_SUBJECT;
+ }
+ return rc;
+ }
+ }
+#endif /* SUPPORT_JIT */
+
+/* ========================= End of JIT matching ========================== */
+
+
+/* Proceed with non-JIT matching. The default is to allow lookbehinds to the
+start of the subject. A UTF check when there is a non-zero offset may change
+this. */
+
+mb->check_subject = subject;
+
+/* If a UTF subject string was not checked for validity in the JIT code above,
+check it here, and handle support for invalid UTF strings. The check above
+happens only when invalid UTF is not supported and PCRE2_NO_CHECK_UTF is unset.
+If we get here in those circumstances, it means the subject string is valid,
+but for some reason JIT matching was not successful. There is no need to check
+the subject again.
+
+We check only the portion of the subject that might be be inspected during
+matching - from the offset minus the maximum lookbehind to the given length.
+This saves time when a small part of a large subject is being matched by the
+use of a starting offset. Note that the maximum lookbehind is a number of
+characters, not code units.
+
+Note also that support for invalid UTF forces a check, overriding the setting
+of PCRE2_NO_CHECK_UTF. */
+
+#ifdef SUPPORT_UNICODE
+if (utf &&
+#ifdef SUPPORT_JIT
+ !jit_checked_utf &&
+#endif
+ ((options & PCRE2_NO_UTF_CHECK) == 0 || allow_invalid))
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ BOOL skipped_bad_start = FALSE;
+#endif
+
+ /* For 8-bit and 16-bit UTF, check that the first code unit is a valid
+ character start. If we are handling invalid UTF, just skip over such code
+ units. Otherwise, give an appropriate error. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if (allow_invalid)
+ {
+ while (start_match < end_subject && NOT_FIRSTCU(*start_match))
+ {
+ start_match++;
+ skipped_bad_start = TRUE;
+ }
+ }
+ else if (start_match < end_subject && NOT_FIRSTCU(*start_match))
+ {
+ if (start_offset > 0) return PCRE2_ERROR_BADUTFOFFSET;
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ return PCRE2_ERROR_UTF8_ERR20; /* Isolated 0x80 byte */
+#else
+ return PCRE2_ERROR_UTF16_ERR3; /* Isolated low surrogate */
+#endif
+ }
+#endif /* WIDTH != 32 */
+
+ /* The mb->check_subject field points to the start of UTF checking;
+ lookbehinds can go back no further than this. */
+
+ mb->check_subject = start_match;
+
+ /* Move back by the maximum lookbehind, just in case it happens at the very
+ start of matching, but don't do this if we skipped bad 8-bit or 16-bit code
+ units above. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ if (!skipped_bad_start)
+ {
+ unsigned int i;
+ for (i = re->max_lookbehind; i > 0 && mb->check_subject > subject; i--)
+ {
+ mb->check_subject--;
+ while (mb->check_subject > subject &&
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ (*mb->check_subject & 0xc0) == 0x80)
+#else /* 16-bit */
+ (*mb->check_subject & 0xfc00) == 0xdc00)
+#endif
+ mb->check_subject--;
+ }
+ }
+#else /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+ /* In the 32-bit library, one code unit equals one character. However,
+ we cannot just subtract the lookbehind and then compare pointers, because
+ a very large lookbehind could create an invalid pointer. */
+
+ if (start_offset >= re->max_lookbehind)
+ mb->check_subject -= re->max_lookbehind;
+ else
+ mb->check_subject = subject;
+#endif /* PCRE2_CODE_UNIT_WIDTH != 32 */
+
+ /* Validate the relevant portion of the subject. There's a loop in case we
+ encounter bad UTF in the characters preceding start_match which we are
+ scanning because of a lookbehind. */
+
+ for (;;)
+ {
+ match_data->rc = PRIV(valid_utf)(mb->check_subject,
+ length - (mb->check_subject - subject), &(match_data->startchar));
+
+ if (match_data->rc == 0) break; /* Valid UTF string */
+
+ /* Invalid UTF string. Adjust the offset to be an absolute offset in the
+ whole string. If we are handling invalid UTF strings, set end_subject to
+ stop before the bad code unit, and set the options to "not end of line".
+ Otherwise return the error. */
+
+ match_data->startchar += mb->check_subject - subject;
+ if (!allow_invalid || match_data->rc > 0) return match_data->rc;
+ end_subject = subject + match_data->startchar;
+
+ /* If the end precedes start_match, it means there is invalid UTF in the
+ extra code units we reversed over because of a lookbehind. Advance past the
+ first bad code unit, and then skip invalid character starting code units in
+ 8-bit and 16-bit modes, and try again with the original end point. */
+
+ if (end_subject < start_match)
+ {
+ mb->check_subject = end_subject + 1;
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ while (mb->check_subject < start_match && NOT_FIRSTCU(*mb->check_subject))
+ mb->check_subject++;
+#endif
+ end_subject = true_end_subject;
+ }
+
+ /* Otherwise, set the not end of line option, and do the match. */
+
+ else
+ {
+ fragment_options = PCRE2_NOTEOL;
+ break;
+ }
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* A NULL match context means "use a default context", but we take the memory
+control functions from the pattern. */
+
+if (mcontext == NULL)
+ {
+ mcontext = (pcre2_match_context *)(&PRIV(default_match_context));
+ mb->memctl = re->memctl;
+ }
+else mb->memctl = mcontext->memctl;
+
+anchored = ((re->overall_options | options) & PCRE2_ANCHORED) != 0;
+firstline = (re->overall_options & PCRE2_FIRSTLINE) != 0;
+startline = (re->flags & PCRE2_STARTLINE) != 0;
+bumpalong_limit = (mcontext->offset_limit == PCRE2_UNSET)?
+ true_end_subject : subject + mcontext->offset_limit;
+
+/* Initialize and set up the fixed fields in the callout block, with a pointer
+in the match block. */
+
+mb->cb = &cb;
+cb.version = 2;
+cb.subject = subject;
+cb.subject_length = (PCRE2_SIZE)(end_subject - subject);
+cb.callout_flags = 0;
+
+/* Fill in the remaining fields in the match block, except for moptions, which
+gets set later. */
+
+mb->callout = mcontext->callout;
+mb->callout_data = mcontext->callout_data;
+
+mb->start_subject = subject;
+mb->start_offset = start_offset;
+mb->end_subject = end_subject;
+mb->hasthen = (re->flags & PCRE2_HASTHEN) != 0;
+mb->allowemptypartial = (re->max_lookbehind > 0) ||
+ (re->flags & PCRE2_MATCH_EMPTY) != 0;
+mb->poptions = re->overall_options; /* Pattern options */
+mb->ignore_skip_arg = 0;
+mb->mark = mb->nomatch_mark = NULL; /* In case never set */
+
+/* The name table is needed for finding all the numbers associated with a
+given name, for condition testing. The code follows the name table. */
+
+mb->name_table = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code));
+mb->name_count = re->name_count;
+mb->name_entry_size = re->name_entry_size;
+mb->start_code = mb->name_table + re->name_count * re->name_entry_size;
+
+/* Process the \R and newline settings. */
+
+mb->bsr_convention = re->bsr_convention;
+mb->nltype = NLTYPE_FIXED;
+switch(re->newline_convention)
+ {
+ case PCRE2_NEWLINE_CR:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_CR;
+ break;
+
+ case PCRE2_NEWLINE_LF:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_NUL:
+ mb->nllen = 1;
+ mb->nl[0] = CHAR_NUL;
+ break;
+
+ case PCRE2_NEWLINE_CRLF:
+ mb->nllen = 2;
+ mb->nl[0] = CHAR_CR;
+ mb->nl[1] = CHAR_NL;
+ break;
+
+ case PCRE2_NEWLINE_ANY:
+ mb->nltype = NLTYPE_ANY;
+ break;
+
+ case PCRE2_NEWLINE_ANYCRLF:
+ mb->nltype = NLTYPE_ANYCRLF;
+ break;
+
+ default: return PCRE2_ERROR_INTERNAL;
+ }
+
+/* The backtracking frames have fixed data at the front, and a PCRE2_SIZE
+vector at the end, whose size depends on the number of capturing parentheses in
+the pattern. It is not used at all if there are no capturing parentheses.
+
+ frame_size is the total size of each frame
+ match_data->heapframes is the pointer to the frames vector
+ match_data->heapframes_size is the total size of the vector
+
+We must pad the frame_size for alignment to ensure subsequent frames are as
+aligned as heapframe. Whilst ovector is word-aligned due to being a PCRE2_SIZE
+array, that does not guarantee it is suitably aligned for pointers, as some
+architectures have pointers that are larger than a size_t. */
+
+frame_size = (offsetof(heapframe, ovector) +
+ re->top_bracket * 2 * sizeof(PCRE2_SIZE) + HEAPFRAME_ALIGNMENT - 1) &
+ ~(HEAPFRAME_ALIGNMENT - 1);
+
+/* Limits set in the pattern override the match context only if they are
+smaller. */
+
+mb->heap_limit = ((mcontext->heap_limit < re->limit_heap)?
+ mcontext->heap_limit : re->limit_heap) * 1024;
+
+mb->match_limit = (mcontext->match_limit < re->limit_match)?
+ mcontext->match_limit : re->limit_match;
+
+mb->match_limit_depth = (mcontext->depth_limit < re->limit_depth)?
+ mcontext->depth_limit : re->limit_depth;
+
+/* If a pattern has very many capturing parentheses, the frame size may be very
+large. Set the initial frame vector size to ensure that there are at least 10
+available frames, but enforce a minimum of START_FRAMES_SIZE. If this is
+greater than the heap limit, get as large a vector as possible. Always round
+the size to a multiple of the frame size. */
+
+heapframes_size = frame_size * 10;
+if (heapframes_size < START_FRAMES_SIZE) heapframes_size = START_FRAMES_SIZE;
+if (heapframes_size > mb->heap_limit)
+ {
+ if (frame_size > mb->heap_limit ) return PCRE2_ERROR_HEAPLIMIT;
+ heapframes_size = mb->heap_limit;
+ }
+
+/* If an existing frame vector in the match_data block is large enough, we can
+use it.Otherwise, free any pre-existing vector and get a new one. */
+
+if (match_data->heapframes_size < heapframes_size)
+ {
+ match_data->memctl.free(match_data->heapframes,
+ match_data->memctl.memory_data);
+ match_data->heapframes = match_data->memctl.malloc(heapframes_size,
+ match_data->memctl.memory_data);
+ if (match_data->heapframes == NULL)
+ {
+ match_data->heapframes_size = 0;
+ return PCRE2_ERROR_NOMEMORY;
+ }
+ match_data->heapframes_size = heapframes_size;
+ }
+
+/* Write to the ovector within the first frame to mark every capture unset and
+to avoid uninitialized memory read errors when it is copied to a new frame. */
+
+memset((char *)(match_data->heapframes) + offsetof(heapframe, ovector), 0xff,
+ frame_size - offsetof(heapframe, ovector));
+
+/* Pointers to the individual character tables */
+
+mb->lcc = re->tables + lcc_offset;
+mb->fcc = re->tables + fcc_offset;
+mb->ctypes = re->tables + ctypes_offset;
+
+/* Set up the first code unit to match, if available. If there's no first code
+unit there may be a bitmap of possible first characters. */
+
+if ((re->flags & PCRE2_FIRSTSET) != 0)
+ {
+ has_first_cu = TRUE;
+ first_cu = first_cu2 = (PCRE2_UCHAR)(re->first_codeunit);
+ if ((re->flags & PCRE2_FIRSTCASELESS) != 0)
+ {
+ first_cu2 = TABLE_GET(first_cu, mb->fcc, first_cu);
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (first_cu > 127 && ucp && !utf) first_cu2 = UCD_OTHERCASE(first_cu);
+#else
+ if (first_cu > 127 && (utf || ucp)) first_cu2 = UCD_OTHERCASE(first_cu);
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+else
+ if (!startline && (re->flags & PCRE2_FIRSTMAPSET) != 0)
+ start_bits = re->start_bitmap;
+
+/* There may also be a "last known required character" set. */
+
+if ((re->flags & PCRE2_LASTSET) != 0)
+ {
+ has_req_cu = TRUE;
+ req_cu = req_cu2 = (PCRE2_UCHAR)(re->last_codeunit);
+ if ((re->flags & PCRE2_LASTCASELESS) != 0)
+ {
+ req_cu2 = TABLE_GET(req_cu, mb->fcc, req_cu);
+#ifdef SUPPORT_UNICODE
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (req_cu > 127 && ucp && !utf) req_cu2 = UCD_OTHERCASE(req_cu);
+#else
+ if (req_cu > 127 && (utf || ucp)) req_cu2 = UCD_OTHERCASE(req_cu);
+#endif
+#endif /* SUPPORT_UNICODE */
+ }
+ }
+
+
+/* ==========================================================================*/
+
+/* Loop for handling unanchored repeated matching attempts; for anchored regexs
+the loop runs just once. */
+
+#ifdef SUPPORT_UNICODE
+FRAGMENT_RESTART:
+#endif
+
+start_partial = match_partial = NULL;
+mb->hitend = FALSE;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+memchr_found_first_cu = NULL;
+memchr_found_first_cu2 = NULL;
+#endif
+
+for(;;)
+ {
+ PCRE2_SPTR new_start_match;
+
+ /* ----------------- Start of match optimizations ---------------- */
+
+ /* There are some optimizations that avoid running the match if a known
+ starting point is not found, or if a known later code unit is not present.
+ However, there is an option (settable at compile time) that disables these,
+ for testing and for ensuring that all callouts do actually occur. */
+
+ if ((re->overall_options & PCRE2_NO_START_OPTIMIZE) == 0)
+ {
+ /* If firstline is TRUE, the start of the match is constrained to the first
+ line of a multiline string. That is, the match must be before or at the
+ first newline following the start of matching. Temporarily adjust
+ end_subject so that we stop the scans for a first code unit at a newline.
+ If the match fails at the newline, later code breaks the loop. */
+
+ if (firstline)
+ {
+ PCRE2_SPTR t = start_match;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (t < end_subject && !IS_NEWLINE(t))
+ {
+ t++;
+ ACROSSCHAR(t < end_subject, t, t++);
+ }
+ }
+ else
+#endif
+ while (t < end_subject && !IS_NEWLINE(t)) t++;
+ end_subject = t;
+ }
+
+ /* Anchored: check the first code unit if one is recorded. This may seem
+ pointless but it can help in detecting a no match case without scanning for
+ the required code unit. */
+
+ if (anchored)
+ {
+ if (has_first_cu || start_bits != NULL)
+ {
+ BOOL ok = start_match < end_subject;
+ if (ok)
+ {
+ PCRE2_UCHAR c = UCHAR21TEST(start_match);
+ ok = has_first_cu && (c == first_cu || c == first_cu2);
+ if (!ok && start_bits != NULL)
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ ok = (start_bits[c/8] & (1u << (c&7))) != 0;
+ }
+ }
+ if (!ok)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+ }
+ }
+
+ /* Not anchored. Advance to a unique first code unit if there is one. */
+
+ else
+ {
+ if (has_first_cu)
+ {
+ if (first_cu != first_cu2) /* Caseless */
+ {
+ /* In 16-bit and 32_bit modes we have to do our own search, so can
+ look for both cases at once. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ PCRE2_UCHAR smc;
+ while (start_match < end_subject &&
+ (smc = UCHAR21TEST(start_match)) != first_cu &&
+ smc != first_cu2)
+ start_match++;
+#else
+ /* In 8-bit mode, the use of memchr() gives a big speed up, even
+ though we have to call it twice in order to find the earliest
+ occurrence of the code unit in either of its cases. Caching is used
+ to remember the positions of previously found code units. This can
+ make a huge difference when the strings are very long and only one
+ case is actually present. */
+
+ PCRE2_SPTR pp1 = NULL;
+ PCRE2_SPTR pp2 = NULL;
+ PCRE2_SIZE searchlength = end_subject - start_match;
+
+ /* If we haven't got a previously found position for first_cu, or if
+ the current starting position is later, we need to do a search. If
+ the code unit is not found, set it to the end. */
+
+ if (memchr_found_first_cu == NULL ||
+ start_match > memchr_found_first_cu)
+ {
+ pp1 = memchr(start_match, first_cu, searchlength);
+ memchr_found_first_cu = (pp1 == NULL)? end_subject : pp1;
+ }
+
+ /* If the start is before a previously found position, use the
+ previous position, or NULL if a previous search failed. */
+
+ else pp1 = (memchr_found_first_cu == end_subject)? NULL :
+ memchr_found_first_cu;
+
+ /* Do the same thing for the other case. */
+
+ if (memchr_found_first_cu2 == NULL ||
+ start_match > memchr_found_first_cu2)
+ {
+ pp2 = memchr(start_match, first_cu2, searchlength);
+ memchr_found_first_cu2 = (pp2 == NULL)? end_subject : pp2;
+ }
+
+ else pp2 = (memchr_found_first_cu2 == end_subject)? NULL :
+ memchr_found_first_cu2;
+
+ /* Set the start to the end of the subject if neither case was found.
+ Otherwise, use the earlier found point. */
+
+ if (pp1 == NULL)
+ start_match = (pp2 == NULL)? end_subject : pp2;
+ else
+ start_match = (pp2 == NULL || pp1 < pp2)? pp1 : pp2;
+
+#endif /* 8-bit handling */
+ }
+
+ /* The caseful case is much simpler. */
+
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (start_match < end_subject && UCHAR21TEST(start_match) !=
+ first_cu)
+ start_match++;
+#else
+ start_match = memchr(start_match, first_cu, end_subject - start_match);
+ if (start_match == NULL) start_match = end_subject;
+#endif
+ }
+
+ /* If we can't find the required first code unit, having reached the
+ true end of the subject, break the bumpalong loop, to force a match
+ failure, except when doing partial matching, when we let the next cycle
+ run at the end of the subject. To see why, consider the pattern
+ /(?<=abc)def/, which partially matches "abc", even though the string
+ does not contain the starting character "d". If we have not reached the
+ true end of the subject (PCRE2_FIRSTLINE caused end_subject to be
+ temporarily modified) we also let the cycle run, because the matching
+ string is legitimately allowed to start with the first code unit of a
+ newline. */
+
+ if (mb->partial == 0 && start_match >= mb->end_subject)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+ }
+
+ /* If there's no first code unit, advance to just after a linebreak for a
+ multiline match if required. */
+
+ else if (startline)
+ {
+ if (start_match > mb->start_subject + start_offset)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ {
+ start_match++;
+ ACROSSCHAR(start_match < end_subject, start_match, start_match++);
+ }
+ }
+ else
+#endif
+ while (start_match < end_subject && !WAS_NEWLINE(start_match))
+ start_match++;
+
+ /* If we have just passed a CR and the newline option is ANY or
+ ANYCRLF, and we are now at a LF, advance the match position by one
+ more code unit. */
+
+ if (start_match[-1] == CHAR_CR &&
+ (mb->nltype == NLTYPE_ANY || mb->nltype == NLTYPE_ANYCRLF) &&
+ start_match < end_subject &&
+ UCHAR21TEST(start_match) == CHAR_NL)
+ start_match++;
+ }
+ }
+
+ /* If there's no first code unit or a requirement for a multiline line
+ start, advance to a non-unique first code unit if any have been
+ identified. The bitmap contains only 256 bits. When code units are 16 or
+ 32 bits wide, all code units greater than 254 set the 255 bit. */
+
+ else if (start_bits != NULL)
+ {
+ while (start_match < end_subject)
+ {
+ uint32_t c = UCHAR21TEST(start_match);
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (c > 255) c = 255;
+#endif
+ if ((start_bits[c/8] & (1u << (c&7))) != 0) break;
+ start_match++;
+ }
+
+ /* See comment above in first_cu checking about the next few lines. */
+
+ if (mb->partial == 0 && start_match >= mb->end_subject)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+ }
+ } /* End first code unit handling */
+
+ /* Restore fudged end_subject */
+
+ end_subject = mb->end_subject;
+
+ /* The following two optimizations must be disabled for partial matching. */
+
+ if (mb->partial == 0)
+ {
+ PCRE2_SPTR p;
+
+ /* The minimum matching length is a lower bound; no string of that length
+ may actually match the pattern. Although the value is, strictly, in
+ characters, we treat it as code units to avoid spending too much time in
+ this optimization. */
+
+ if (end_subject - start_match < re->minlength)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+
+ /* If req_cu is set, we know that that code unit must appear in the
+ subject for the (non-partial) match to succeed. If the first code unit is
+ set, req_cu must be later in the subject; otherwise the test starts at
+ the match point. This optimization can save a huge amount of backtracking
+ in patterns with nested unlimited repeats that aren't going to match.
+ Writing separate code for caseful/caseless versions makes it go faster,
+ as does using an autoincrement and backing off on a match. As in the case
+ of the first code unit, using memchr() in the 8-bit library gives a big
+ speed up. Unlike the first_cu check above, we do not need to call
+ memchr() twice in the caseless case because we only need to check for the
+ presence of the character in either case, not find the first occurrence.
+
+ The search can be skipped if the code unit was found later than the
+ current starting point in a previous iteration of the bumpalong loop.
+
+ HOWEVER: when the subject string is very, very long, searching to its end
+ can take a long time, and give bad performance on quite ordinary
+ anchored patterns. This showed up when somebody was matching something
+ like /^\d+C/ on a 32-megabyte string... so we don't do this when the
+ string is sufficiently long, but it's worth searching a lot more for
+ unanchored patterns. */
+
+ p = start_match + (has_first_cu? 1:0);
+ if (has_req_cu && p > req_cu_ptr)
+ {
+ PCRE2_SIZE check_length = end_subject - start_match;
+
+ if (check_length < REQ_CU_MAX ||
+ (!anchored && check_length < REQ_CU_MAX * 1000))
+ {
+ if (req_cu != req_cu2) /* Caseless */
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (p < end_subject)
+ {
+ uint32_t pp = UCHAR21INCTEST(p);
+ if (pp == req_cu || pp == req_cu2) { p--; break; }
+ }
+#else /* 8-bit code units */
+ PCRE2_SPTR pp = p;
+ p = memchr(pp, req_cu, end_subject - pp);
+ if (p == NULL)
+ {
+ p = memchr(pp, req_cu2, end_subject - pp);
+ if (p == NULL) p = end_subject;
+ }
+#endif /* PCRE2_CODE_UNIT_WIDTH != 8 */
+ }
+
+ /* The caseful case */
+
+ else
+ {
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ while (p < end_subject)
+ {
+ if (UCHAR21INCTEST(p) == req_cu) { p--; break; }
+ }
+
+#else /* 8-bit code units */
+ p = memchr(p, req_cu, end_subject - p);
+ if (p == NULL) p = end_subject;
+#endif
+ }
+
+ /* If we can't find the required code unit, break the bumpalong loop,
+ forcing a match failure. */
+
+ if (p >= end_subject)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+
+ /* If we have found the required code unit, save the point where we
+ found it, so that we don't search again next time round the bumpalong
+ loop if the start hasn't yet passed this code unit. */
+
+ req_cu_ptr = p;
+ }
+ }
+ }
+ }
+
+ /* ------------ End of start of match optimizations ------------ */
+
+ /* Give no match if we have passed the bumpalong limit. */
+
+ if (start_match > bumpalong_limit)
+ {
+ rc = MATCH_NOMATCH;
+ break;
+ }
+
+ /* OK, we can now run the match. If "hitend" is set afterwards, remember the
+ first starting point for which a partial match was found. */
+
+ cb.start_match = (PCRE2_SIZE)(start_match - subject);
+ cb.callout_flags |= PCRE2_CALLOUT_STARTMATCH;
+
+ mb->start_used_ptr = start_match;
+ mb->last_used_ptr = start_match;
+#ifdef SUPPORT_UNICODE
+ mb->moptions = options | fragment_options;
+#else
+ mb->moptions = options;
+#endif
+ mb->match_call_count = 0;
+ mb->end_offset_top = 0;
+ mb->skip_arg_count = 0;
+
+ rc = match(start_match, mb->start_code, re->top_bracket, frame_size,
+ match_data, mb);
+
+ if (mb->hitend && start_partial == NULL)
+ {
+ start_partial = mb->start_used_ptr;
+ match_partial = start_match;
+ }
+
+ switch(rc)
+ {
+ /* If MATCH_SKIP_ARG reaches this level it means that a MARK that matched
+ the SKIP's arg was not found. In this circumstance, Perl ignores the SKIP
+ entirely. The only way we can do that is to re-do the match at the same
+ point, with a flag to force SKIP with an argument to be ignored. Just
+ treating this case as NOMATCH does not work because it does not check other
+ alternatives in patterns such as A(*SKIP:A)B|AC when the subject is AC. */
+
+ case MATCH_SKIP_ARG:
+ new_start_match = start_match;
+ mb->ignore_skip_arg = mb->skip_arg_count;
+ break;
+
+ /* SKIP passes back the next starting point explicitly, but if it is no
+ greater than the match we have just done, treat it as NOMATCH. */
+
+ case MATCH_SKIP:
+ if (mb->verb_skip_ptr > start_match)
+ {
+ new_start_match = mb->verb_skip_ptr;
+ break;
+ }
+ /* Fall through */
+
+ /* NOMATCH and PRUNE advance by one character. THEN at this level acts
+ exactly like PRUNE. Unset ignore SKIP-with-argument. */
+
+ case MATCH_NOMATCH:
+ case MATCH_PRUNE:
+ case MATCH_THEN:
+ mb->ignore_skip_arg = 0;
+ new_start_match = start_match + 1;
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ ACROSSCHAR(new_start_match < end_subject, new_start_match,
+ new_start_match++);
+#endif
+ break;
+
+ /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */
+
+ case MATCH_COMMIT:
+ rc = MATCH_NOMATCH;
+ goto ENDLOOP;
+
+ /* Any other return is either a match, or some kind of error. */
+
+ default:
+ goto ENDLOOP;
+ }
+
+ /* Control reaches here for the various types of "no match at this point"
+ result. Reset the code to MATCH_NOMATCH for subsequent checking. */
+
+ rc = MATCH_NOMATCH;
+
+ /* If PCRE2_FIRSTLINE is set, the match must happen before or at the first
+ newline in the subject (though it may continue over the newline). Therefore,
+ if we have just failed to match, starting at a newline, do not continue. */
+
+ if (firstline && IS_NEWLINE(start_match)) break;
+
+ /* Advance to new matching position */
+
+ start_match = new_start_match;
+
+ /* Break the loop if the pattern is anchored or if we have passed the end of
+ the subject. */
+
+ if (anchored || start_match > end_subject) break;
+
+ /* If we have just passed a CR and we are now at a LF, and the pattern does
+ not contain any explicit matches for \r or \n, and the newline option is CRLF
+ or ANY or ANYCRLF, advance the match position by one more code unit. In
+ normal matching start_match will aways be greater than the first position at
+ this stage, but a failed *SKIP can cause a return at the same point, which is
+ why the first test exists. */
+
+ if (start_match > subject + start_offset &&
+ start_match[-1] == CHAR_CR &&
+ start_match < end_subject &&
+ *start_match == CHAR_NL &&
+ (re->flags & PCRE2_HASCRORLF) == 0 &&
+ (mb->nltype == NLTYPE_ANY ||
+ mb->nltype == NLTYPE_ANYCRLF ||
+ mb->nllen == 2))
+ start_match++;
+
+ mb->mark = NULL; /* Reset for start of next match attempt */
+ } /* End of for(;;) "bumpalong" loop */
+
+/* ==========================================================================*/
+
+/* When we reach here, one of the following stopping conditions is true:
+
+(1) The match succeeded, either completely, or partially;
+
+(2) The pattern is anchored or the match was failed after (*COMMIT);
+
+(3) We are past the end of the subject or the bumpalong limit;
+
+(4) PCRE2_FIRSTLINE is set and we have failed to match at a newline, because
+ this option requests that a match occur at or before the first newline in
+ the subject.
+
+(5) Some kind of error occurred.
+
+*/
+
+ENDLOOP:
+
+/* If end_subject != true_end_subject, it means we are handling invalid UTF,
+and have just processed a non-terminal fragment. If this resulted in no match
+or a partial match we must carry on to the next fragment (a partial match is
+returned to the caller only at the very end of the subject). A loop is used to
+avoid trying to match against empty fragments; if the pattern can match an
+empty string it would have done so already. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && end_subject != true_end_subject &&
+ (rc == MATCH_NOMATCH || rc == PCRE2_ERROR_PARTIAL))
+ {
+ for (;;)
+ {
+ /* Advance past the first bad code unit, and then skip invalid character
+ starting code units in 8-bit and 16-bit modes. */
+
+ start_match = end_subject + 1;
+
+#if PCRE2_CODE_UNIT_WIDTH != 32
+ while (start_match < true_end_subject && NOT_FIRSTCU(*start_match))
+ start_match++;
+#endif
+
+ /* If we have hit the end of the subject, there isn't another non-empty
+ fragment, so give up. */
+
+ if (start_match >= true_end_subject)
+ {
+ rc = MATCH_NOMATCH; /* In case it was partial */
+ break;
+ }
+
+ /* Check the rest of the subject */
+
+ mb->check_subject = start_match;
+ rc = PRIV(valid_utf)(start_match, length - (start_match - subject),
+ &(match_data->startchar));
+
+ /* The rest of the subject is valid UTF. */
+
+ if (rc == 0)
+ {
+ mb->end_subject = end_subject = true_end_subject;
+ fragment_options = PCRE2_NOTBOL;
+ goto FRAGMENT_RESTART;
+ }
+
+ /* A subsequent UTF error has been found; if the next fragment is
+ non-empty, set up to process it. Otherwise, let the loop advance. */
+
+ else if (rc < 0)
+ {
+ mb->end_subject = end_subject = start_match + match_data->startchar;
+ if (end_subject > start_match)
+ {
+ fragment_options = PCRE2_NOTBOL|PCRE2_NOTEOL;
+ goto FRAGMENT_RESTART;
+ }
+ }
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* Fill in fields that are always returned in the match data. */
+
+match_data->code = re;
+match_data->mark = mb->mark;
+match_data->matchedby = PCRE2_MATCHEDBY_INTERPRETER;
+
+/* Handle a fully successful match. Set the return code to the number of
+captured strings, or 0 if there were too many to fit into the ovector, and then
+set the remaining returned values before returning. Make a copy of the subject
+string if requested. */
+
+if (rc == MATCH_MATCH)
+ {
+ match_data->rc = ((int)mb->end_offset_top >= 2 * match_data->oveccount)?
+ 0 : (int)mb->end_offset_top/2 + 1;
+ match_data->startchar = start_match - subject;
+ match_data->leftchar = mb->start_used_ptr - subject;
+ match_data->rightchar = ((mb->last_used_ptr > mb->end_match_ptr)?
+ mb->last_used_ptr : mb->end_match_ptr) - subject;
+ if ((options & PCRE2_COPY_MATCHED_SUBJECT) != 0)
+ {
+ length = CU2BYTES(length + was_zero_terminated);
+ match_data->subject = match_data->memctl.malloc(length,
+ match_data->memctl.memory_data);
+ if (match_data->subject == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy((void *)match_data->subject, subject, length);
+ match_data->flags |= PCRE2_MD_COPIED_SUBJECT;
+ }
+ else match_data->subject = subject;
+ return match_data->rc;
+ }
+
+/* Control gets here if there has been a partial match, an error, or if the
+overall match attempt has failed at all permitted starting positions. Any mark
+data is in the nomatch_mark field. */
+
+match_data->mark = mb->nomatch_mark;
+
+/* For anything other than nomatch or partial match, just return the code. */
+
+if (rc != MATCH_NOMATCH && rc != PCRE2_ERROR_PARTIAL) match_data->rc = rc;
+
+/* Handle a partial match. If a "soft" partial match was requested, searching
+for a complete match will have continued, and the value of rc at this point
+will be MATCH_NOMATCH. For a "hard" partial match, it will already be
+PCRE2_ERROR_PARTIAL. */
+
+else if (match_partial != NULL)
+ {
+ match_data->subject = subject;
+ match_data->ovector[0] = match_partial - subject;
+ match_data->ovector[1] = end_subject - subject;
+ match_data->startchar = match_partial - subject;
+ match_data->leftchar = start_partial - subject;
+ match_data->rightchar = end_subject - subject;
+ match_data->rc = PCRE2_ERROR_PARTIAL;
+ }
+
+/* Else this is the classic nomatch case. */
+
+else match_data->rc = PCRE2_ERROR_NOMATCH;
+
+return match_data->rc;
+}
+
+/* These #undefs are here to enable unity builds with CMake. */
+
+#undef NLBLOCK /* Block containing newline information */
+#undef PSSTART /* Field containing processed string start */
+#undef PSEND /* Field containing processed string end */
+
+/* End of pcre2_match.c */
diff --git a/contrib/libs/pcre2/src/pcre2_match_data.c b/contrib/libs/pcre2/src/pcre2_match_data.c
new file mode 100644
index 0000000000..1b66359620
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_match_data.c
@@ -0,0 +1,173 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+
+/*************************************************
+* Create a match data block given ovector size *
+*************************************************/
+
+/* A minimum of 1 is imposed on the number of ovector pairs. A maximum is also
+imposed because the oveccount field in a match data block is uintt6_t. */
+
+PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION
+pcre2_match_data_create(uint32_t oveccount, pcre2_general_context *gcontext)
+{
+pcre2_match_data *yield;
+if (oveccount < 1) oveccount = 1;
+if (oveccount > UINT16_MAX) oveccount = UINT16_MAX;
+yield = PRIV(memctl_malloc)(
+ offsetof(pcre2_match_data, ovector) + 2*oveccount*sizeof(PCRE2_SIZE),
+ (pcre2_memctl *)gcontext);
+if (yield == NULL) return NULL;
+yield->oveccount = oveccount;
+yield->flags = 0;
+yield->heapframes = NULL;
+yield->heapframes_size = 0;
+return yield;
+}
+
+
+
+/*************************************************
+* Create a match data block using pattern data *
+*************************************************/
+
+/* If no context is supplied, use the memory allocator from the code. */
+
+PCRE2_EXP_DEFN pcre2_match_data * PCRE2_CALL_CONVENTION
+pcre2_match_data_create_from_pattern(const pcre2_code *code,
+ pcre2_general_context *gcontext)
+{
+if (gcontext == NULL) gcontext = (pcre2_general_context *)code;
+return pcre2_match_data_create(((pcre2_real_code *)code)->top_bracket + 1,
+ gcontext);
+}
+
+
+
+/*************************************************
+* Free a match data block *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_match_data_free(pcre2_match_data *match_data)
+{
+if (match_data != NULL)
+ {
+ if (match_data->heapframes != NULL)
+ match_data->memctl.free(match_data->heapframes,
+ match_data->memctl.memory_data);
+ if ((match_data->flags & PCRE2_MD_COPIED_SUBJECT) != 0)
+ match_data->memctl.free((void *)match_data->subject,
+ match_data->memctl.memory_data);
+ match_data->memctl.free(match_data, match_data->memctl.memory_data);
+ }
+}
+
+
+
+/*************************************************
+* Get last mark in match *
+*************************************************/
+
+PCRE2_EXP_DEFN PCRE2_SPTR PCRE2_CALL_CONVENTION
+pcre2_get_mark(pcre2_match_data *match_data)
+{
+return match_data->mark;
+}
+
+
+
+/*************************************************
+* Get pointer to ovector *
+*************************************************/
+
+PCRE2_EXP_DEFN PCRE2_SIZE * PCRE2_CALL_CONVENTION
+pcre2_get_ovector_pointer(pcre2_match_data *match_data)
+{
+return match_data->ovector;
+}
+
+
+
+/*************************************************
+* Get number of ovector slots *
+*************************************************/
+
+PCRE2_EXP_DEFN uint32_t PCRE2_CALL_CONVENTION
+pcre2_get_ovector_count(pcre2_match_data *match_data)
+{
+return match_data->oveccount;
+}
+
+
+
+/*************************************************
+* Get starting code unit in match *
+*************************************************/
+
+PCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION
+pcre2_get_startchar(pcre2_match_data *match_data)
+{
+return match_data->startchar;
+}
+
+
+
+/*************************************************
+* Get size of match data block *
+*************************************************/
+
+PCRE2_EXP_DEFN PCRE2_SIZE PCRE2_CALL_CONVENTION
+pcre2_get_match_data_size(pcre2_match_data *match_data)
+{
+return offsetof(pcre2_match_data, ovector) +
+ 2 * (match_data->oveccount) * sizeof(PCRE2_SIZE);
+}
+
+/* End of pcre2_match_data.c */
diff --git a/contrib/libs/pcre2/src/pcre2_newline.c b/contrib/libs/pcre2/src/pcre2_newline.c
new file mode 100644
index 0000000000..ae830b38df
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_newline.c
@@ -0,0 +1,243 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains internal functions for testing newlines when more than
+one kind of newline is to be recognized. When a newline is found, its length is
+returned. In principle, we could implement several newline "types", each
+referring to a different set of newline characters. At present, PCRE2 supports
+only NLTYPE_FIXED, which gets handled without these functions, NLTYPE_ANYCRLF,
+and NLTYPE_ANY. The full list of Unicode newline characters is taken from
+http://unicode.org/unicode/reports/tr18/. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+
+/*************************************************
+* Check for newline at given position *
+*************************************************/
+
+/* This function is called only via the IS_NEWLINE macro, which does so only
+when the newline type is NLTYPE_ANY or NLTYPE_ANYCRLF. The case of a fixed
+newline (NLTYPE_FIXED) is handled inline. It is guaranteed that the code unit
+pointed to by ptr is less than the end of the string.
+
+Arguments:
+ ptr pointer to possible newline
+ type the newline type
+ endptr pointer to the end of the string
+ lenptr where to return the length
+ utf TRUE if in utf mode
+
+Returns: TRUE or FALSE
+*/
+
+BOOL
+PRIV(is_newline)(PCRE2_SPTR ptr, uint32_t type, PCRE2_SPTR endptr,
+ uint32_t *lenptr, BOOL utf)
+{
+uint32_t c;
+
+#ifdef SUPPORT_UNICODE
+if (utf) { GETCHAR(c, ptr); } else c = *ptr;
+#else
+(void)utf;
+c = *ptr;
+#endif /* SUPPORT_UNICODE */
+
+if (type == NLTYPE_ANYCRLF) switch(c)
+ {
+ case CHAR_LF:
+ *lenptr = 1;
+ return TRUE;
+
+ case CHAR_CR:
+ *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+/* NLTYPE_ANY */
+
+else switch(c)
+ {
+#ifdef EBCDIC
+ case CHAR_NEL:
+#endif
+ case CHAR_LF:
+ case CHAR_VT:
+ case CHAR_FF:
+ *lenptr = 1;
+ return TRUE;
+
+ case CHAR_CR:
+ *lenptr = (ptr < endptr - 1 && ptr[1] == CHAR_LF)? 2 : 1;
+ return TRUE;
+
+#ifndef EBCDIC
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ case CHAR_NEL:
+ *lenptr = utf? 2 : 1;
+ return TRUE;
+
+ case 0x2028: /* LS */
+ case 0x2029: /* PS */
+ *lenptr = 3;
+ return TRUE;
+
+#else /* 16-bit or 32-bit code units */
+ case CHAR_NEL:
+ case 0x2028: /* LS */
+ case 0x2029: /* PS */
+ *lenptr = 1;
+ return TRUE;
+#endif
+#endif /* Not EBCDIC */
+
+ default:
+ return FALSE;
+ }
+}
+
+
+
+/*************************************************
+* Check for newline at previous position *
+*************************************************/
+
+/* This function is called only via the WAS_NEWLINE macro, which does so only
+when the newline type is NLTYPE_ANY or NLTYPE_ANYCRLF. The case of a fixed
+newline (NLTYPE_FIXED) is handled inline. It is guaranteed that the initial
+value of ptr is greater than the start of the string that is being processed.
+
+Arguments:
+ ptr pointer to possible newline
+ type the newline type
+ startptr pointer to the start of the string
+ lenptr where to return the length
+ utf TRUE if in utf mode
+
+Returns: TRUE or FALSE
+*/
+
+BOOL
+PRIV(was_newline)(PCRE2_SPTR ptr, uint32_t type, PCRE2_SPTR startptr,
+ uint32_t *lenptr, BOOL utf)
+{
+uint32_t c;
+ptr--;
+
+#ifdef SUPPORT_UNICODE
+if (utf)
+ {
+ BACKCHAR(ptr);
+ GETCHAR(c, ptr);
+ }
+else c = *ptr;
+#else
+(void)utf;
+c = *ptr;
+#endif /* SUPPORT_UNICODE */
+
+if (type == NLTYPE_ANYCRLF) switch(c)
+ {
+ case CHAR_LF:
+ *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1;
+ return TRUE;
+
+ case CHAR_CR:
+ *lenptr = 1;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+/* NLTYPE_ANY */
+
+else switch(c)
+ {
+ case CHAR_LF:
+ *lenptr = (ptr > startptr && ptr[-1] == CHAR_CR)? 2 : 1;
+ return TRUE;
+
+#ifdef EBCDIC
+ case CHAR_NEL:
+#endif
+ case CHAR_VT:
+ case CHAR_FF:
+ case CHAR_CR:
+ *lenptr = 1;
+ return TRUE;
+
+#ifndef EBCDIC
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ case CHAR_NEL:
+ *lenptr = utf? 2 : 1;
+ return TRUE;
+
+ case 0x2028: /* LS */
+ case 0x2029: /* PS */
+ *lenptr = 3;
+ return TRUE;
+
+#else /* 16-bit or 32-bit code units */
+ case CHAR_NEL:
+ case 0x2028: /* LS */
+ case 0x2029: /* PS */
+ *lenptr = 1;
+ return TRUE;
+#endif
+#endif /* Not EBCDIC */
+
+ default:
+ return FALSE;
+ }
+}
+
+/* End of pcre2_newline.c */
diff --git a/contrib/libs/pcre2/src/pcre2_ord2utf.c b/contrib/libs/pcre2/src/pcre2_ord2utf.c
new file mode 100644
index 0000000000..c2cf6e9856
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_ord2utf.c
@@ -0,0 +1,120 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This file contains a function that converts a Unicode character code point
+into a UTF string. The behaviour is different for each code unit width. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/* If SUPPORT_UNICODE is not defined, this function will never be called.
+Supply a dummy function because some compilers do not like empty source
+modules. */
+
+#ifndef SUPPORT_UNICODE
+unsigned int
+PRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer)
+{
+(void)(cvalue);
+(void)(buffer);
+return 0;
+}
+#else /* SUPPORT_UNICODE */
+
+
+/*************************************************
+* Convert code point to UTF *
+*************************************************/
+
+/*
+Arguments:
+ cvalue the character value
+ buffer pointer to buffer for result
+
+Returns: number of code units placed in the buffer
+*/
+
+unsigned int
+PRIV(ord2utf)(uint32_t cvalue, PCRE2_UCHAR *buffer)
+{
+/* Convert to UTF-8 */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+int i, j;
+for (i = 0; i < PRIV(utf8_table1_size); i++)
+ if ((int)cvalue <= PRIV(utf8_table1)[i]) break;
+buffer += i;
+for (j = i; j > 0; j--)
+ {
+ *buffer-- = 0x80 | (cvalue & 0x3f);
+ cvalue >>= 6;
+ }
+*buffer = PRIV(utf8_table2)[i] | cvalue;
+return i + 1;
+
+/* Convert to UTF-16 */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+if (cvalue <= 0xffff)
+ {
+ *buffer = (PCRE2_UCHAR)cvalue;
+ return 1;
+ }
+cvalue -= 0x10000;
+*buffer++ = 0xd800 | (cvalue >> 10);
+*buffer = 0xdc00 | (cvalue & 0x3ff);
+return 2;
+
+/* Convert to UTF-32 */
+
+#else
+*buffer = (PCRE2_UCHAR)cvalue;
+return 1;
+#endif
+}
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre_ord2utf.c */
diff --git a/contrib/libs/pcre2/src/pcre2_pattern_info.c b/contrib/libs/pcre2/src/pcre2_pattern_info.c
new file mode 100644
index 0000000000..6d3ad3c1ec
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_pattern_info.c
@@ -0,0 +1,432 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Return info about compiled pattern *
+*************************************************/
+
+/*
+Arguments:
+ code points to compiled code
+ what what information is required
+ where where to put the information; if NULL, return length
+
+Returns: 0 when data returned
+ > 0 when length requested
+ < 0 on error or unset value
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_pattern_info(const pcre2_code *code, uint32_t what, void *where)
+{
+const pcre2_real_code *re = (pcre2_real_code *)code;
+
+if (where == NULL) /* Requests field length */
+ {
+ switch(what)
+ {
+ case PCRE2_INFO_ALLOPTIONS:
+ case PCRE2_INFO_ARGOPTIONS:
+ case PCRE2_INFO_BACKREFMAX:
+ case PCRE2_INFO_BSR:
+ case PCRE2_INFO_CAPTURECOUNT:
+ case PCRE2_INFO_DEPTHLIMIT:
+ case PCRE2_INFO_EXTRAOPTIONS:
+ case PCRE2_INFO_FIRSTCODETYPE:
+ case PCRE2_INFO_FIRSTCODEUNIT:
+ case PCRE2_INFO_HASBACKSLASHC:
+ case PCRE2_INFO_HASCRORLF:
+ case PCRE2_INFO_HEAPLIMIT:
+ case PCRE2_INFO_JCHANGED:
+ case PCRE2_INFO_LASTCODETYPE:
+ case PCRE2_INFO_LASTCODEUNIT:
+ case PCRE2_INFO_MATCHEMPTY:
+ case PCRE2_INFO_MATCHLIMIT:
+ case PCRE2_INFO_MAXLOOKBEHIND:
+ case PCRE2_INFO_MINLENGTH:
+ case PCRE2_INFO_NAMEENTRYSIZE:
+ case PCRE2_INFO_NAMECOUNT:
+ case PCRE2_INFO_NEWLINE:
+ return sizeof(uint32_t);
+
+ case PCRE2_INFO_FIRSTBITMAP:
+ return sizeof(const uint8_t *);
+
+ case PCRE2_INFO_JITSIZE:
+ case PCRE2_INFO_SIZE:
+ case PCRE2_INFO_FRAMESIZE:
+ return sizeof(size_t);
+
+ case PCRE2_INFO_NAMETABLE:
+ return sizeof(PCRE2_SPTR);
+ }
+ }
+
+if (re == NULL) return PCRE2_ERROR_NULL;
+
+/* Check that the first field in the block is the magic number. If it is not,
+return with PCRE2_ERROR_BADMAGIC. */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+
+/* Check that this pattern was compiled in the correct bit mode */
+
+if ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE;
+
+switch(what)
+ {
+ case PCRE2_INFO_ALLOPTIONS:
+ *((uint32_t *)where) = re->overall_options;
+ break;
+
+ case PCRE2_INFO_ARGOPTIONS:
+ *((uint32_t *)where) = re->compile_options;
+ break;
+
+ case PCRE2_INFO_BACKREFMAX:
+ *((uint32_t *)where) = re->top_backref;
+ break;
+
+ case PCRE2_INFO_BSR:
+ *((uint32_t *)where) = re->bsr_convention;
+ break;
+
+ case PCRE2_INFO_CAPTURECOUNT:
+ *((uint32_t *)where) = re->top_bracket;
+ break;
+
+ case PCRE2_INFO_DEPTHLIMIT:
+ *((uint32_t *)where) = re->limit_depth;
+ if (re->limit_depth == UINT32_MAX) return PCRE2_ERROR_UNSET;
+ break;
+
+ case PCRE2_INFO_EXTRAOPTIONS:
+ *((uint32_t *)where) = re->extra_options;
+ break;
+
+ case PCRE2_INFO_FIRSTCODETYPE:
+ *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)? 1 :
+ ((re->flags & PCRE2_STARTLINE) != 0)? 2 : 0;
+ break;
+
+ case PCRE2_INFO_FIRSTCODEUNIT:
+ *((uint32_t *)where) = ((re->flags & PCRE2_FIRSTSET) != 0)?
+ re->first_codeunit : 0;
+ break;
+
+ case PCRE2_INFO_FIRSTBITMAP:
+ *((const uint8_t **)where) = ((re->flags & PCRE2_FIRSTMAPSET) != 0)?
+ &(re->start_bitmap[0]) : NULL;
+ break;
+
+ case PCRE2_INFO_FRAMESIZE:
+ *((size_t *)where) = offsetof(heapframe, ovector) +
+ re->top_bracket * 2 * sizeof(PCRE2_SIZE);
+ break;
+
+ case PCRE2_INFO_HASBACKSLASHC:
+ *((uint32_t *)where) = (re->flags & PCRE2_HASBKC) != 0;
+ break;
+
+ case PCRE2_INFO_HASCRORLF:
+ *((uint32_t *)where) = (re->flags & PCRE2_HASCRORLF) != 0;
+ break;
+
+ case PCRE2_INFO_HEAPLIMIT:
+ *((uint32_t *)where) = re->limit_heap;
+ if (re->limit_heap == UINT32_MAX) return PCRE2_ERROR_UNSET;
+ break;
+
+ case PCRE2_INFO_JCHANGED:
+ *((uint32_t *)where) = (re->flags & PCRE2_JCHANGED) != 0;
+ break;
+
+ case PCRE2_INFO_JITSIZE:
+#ifdef SUPPORT_JIT
+ *((size_t *)where) = (re->executable_jit != NULL)?
+ PRIV(jit_get_size)(re->executable_jit) : 0;
+#else
+ *((size_t *)where) = 0;
+#endif
+ break;
+
+ case PCRE2_INFO_LASTCODETYPE:
+ *((uint32_t *)where) = ((re->flags & PCRE2_LASTSET) != 0)? 1 : 0;
+ break;
+
+ case PCRE2_INFO_LASTCODEUNIT:
+ *((uint32_t *)where) = ((re->flags & PCRE2_LASTSET) != 0)?
+ re->last_codeunit : 0;
+ break;
+
+ case PCRE2_INFO_MATCHEMPTY:
+ *((uint32_t *)where) = (re->flags & PCRE2_MATCH_EMPTY) != 0;
+ break;
+
+ case PCRE2_INFO_MATCHLIMIT:
+ *((uint32_t *)where) = re->limit_match;
+ if (re->limit_match == UINT32_MAX) return PCRE2_ERROR_UNSET;
+ break;
+
+ case PCRE2_INFO_MAXLOOKBEHIND:
+ *((uint32_t *)where) = re->max_lookbehind;
+ break;
+
+ case PCRE2_INFO_MINLENGTH:
+ *((uint32_t *)where) = re->minlength;
+ break;
+
+ case PCRE2_INFO_NAMEENTRYSIZE:
+ *((uint32_t *)where) = re->name_entry_size;
+ break;
+
+ case PCRE2_INFO_NAMECOUNT:
+ *((uint32_t *)where) = re->name_count;
+ break;
+
+ case PCRE2_INFO_NAMETABLE:
+ *((PCRE2_SPTR *)where) = (PCRE2_SPTR)((char *)re + sizeof(pcre2_real_code));
+ break;
+
+ case PCRE2_INFO_NEWLINE:
+ *((uint32_t *)where) = re->newline_convention;
+ break;
+
+ case PCRE2_INFO_SIZE:
+ *((size_t *)where) = re->blocksize;
+ break;
+
+ default: return PCRE2_ERROR_BADOPTION;
+ }
+
+return 0;
+}
+
+
+
+/*************************************************
+* Callout enumerator *
+*************************************************/
+
+/*
+Arguments:
+ code points to compiled code
+ callback function called for each callout block
+ callout_data user data passed to the callback
+
+Returns: 0 when successfully completed
+ < 0 on local error
+ != 0 for callback error
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_callout_enumerate(const pcre2_code *code,
+ int (*callback)(pcre2_callout_enumerate_block *, void *), void *callout_data)
+{
+pcre2_real_code *re = (pcre2_real_code *)code;
+pcre2_callout_enumerate_block cb;
+PCRE2_SPTR cc;
+#ifdef SUPPORT_UNICODE
+BOOL utf;
+#endif
+
+if (re == NULL) return PCRE2_ERROR_NULL;
+
+#ifdef SUPPORT_UNICODE
+utf = (re->overall_options & PCRE2_UTF) != 0;
+#endif
+
+/* Check that the first field in the block is the magic number. If it is not,
+return with PCRE2_ERROR_BADMAGIC. */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+
+/* Check that this pattern was compiled in the correct bit mode */
+
+if ((re->flags & (PCRE2_CODE_UNIT_WIDTH/8)) == 0) return PCRE2_ERROR_BADMODE;
+
+cb.version = 0;
+cc = (PCRE2_SPTR)((uint8_t *)re + sizeof(pcre2_real_code))
+ + re->name_count * re->name_entry_size;
+
+while (TRUE)
+ {
+ int rc;
+ switch (*cc)
+ {
+ case OP_END:
+ return 0;
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_EXACT:
+ case OP_POSSTAR:
+ case OP_POSPLUS:
+ case OP_POSQUERY:
+ case OP_POSUPTO:
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_EXACTI:
+ case OP_POSSTARI:
+ case OP_POSPLUSI:
+ case OP_POSQUERYI:
+ case OP_POSUPTOI:
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ case OP_NOTEXACT:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSUPTO:
+ case OP_NOTSTARI:
+ case OP_NOTMINSTARI:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUSI:
+ case OP_NOTQUERYI:
+ case OP_NOTMINQUERYI:
+ case OP_NOTUPTOI:
+ case OP_NOTMINUPTOI:
+ case OP_NOTEXACTI:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSUPTOI:
+ cc += PRIV(OP_lengths)[*cc];
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEEXACT:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSPLUS:
+ case OP_TYPEPOSQUERY:
+ case OP_TYPEPOSUPTO:
+ cc += PRIV(OP_lengths)[*cc];
+#ifdef SUPPORT_UNICODE
+ if (cc[-1] == OP_PROP || cc[-1] == OP_NOTPROP) cc += 2;
+#endif
+ break;
+
+#if defined SUPPORT_UNICODE || PCRE2_CODE_UNIT_WIDTH != 8
+ case OP_XCLASS:
+ cc += GET(cc, 1);
+ break;
+#endif
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ cc += PRIV(OP_lengths)[*cc] + cc[1];
+ break;
+
+ case OP_CALLOUT:
+ cb.pattern_position = GET(cc, 1);
+ cb.next_item_length = GET(cc, 1 + LINK_SIZE);
+ cb.callout_number = cc[1 + 2*LINK_SIZE];
+ cb.callout_string_offset = 0;
+ cb.callout_string_length = 0;
+ cb.callout_string = NULL;
+ rc = callback(&cb, callout_data);
+ if (rc != 0) return rc;
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+
+ case OP_CALLOUT_STR:
+ cb.pattern_position = GET(cc, 1);
+ cb.next_item_length = GET(cc, 1 + LINK_SIZE);
+ cb.callout_number = 0;
+ cb.callout_string_offset = GET(cc, 1 + 3*LINK_SIZE);
+ cb.callout_string_length =
+ GET(cc, 1 + 2*LINK_SIZE) - (1 + 4*LINK_SIZE) - 2;
+ cb.callout_string = cc + (1 + 4*LINK_SIZE) + 1;
+ rc = callback(&cb, callout_data);
+ if (rc != 0) return rc;
+ cc += GET(cc, 1 + 2*LINK_SIZE);
+ break;
+
+ default:
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+ }
+ }
+}
+
+/* End of pcre2_pattern_info.c */
diff --git a/contrib/libs/pcre2/src/pcre2_script_run.c b/contrib/libs/pcre2/src/pcre2_script_run.c
new file mode 100644
index 0000000000..1ac2985b49
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_script_run.c
@@ -0,0 +1,344 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains the function for checking a script run. */
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Check script run *
+*************************************************/
+
+/* A script run is conceptually a sequence of characters all in the same
+Unicode script. However, it isn't quite that simple. There are special rules
+for scripts that are commonly used together, and also special rules for digits.
+This function implements the appropriate checks, which is possible only when
+PCRE2 is compiled with Unicode support. The function returns TRUE if there is
+no Unicode support; however, it should never be called in that circumstance
+because an error is given by pcre2_compile() if a script run is called for in a
+version of PCRE2 compiled without Unicode support.
+
+Arguments:
+ pgr point to the first character
+ endptr point after the last character
+ utf TRUE if in UTF mode
+
+Returns: TRUE if this is a valid script run
+*/
+
+/* These are states in the checking process. */
+
+enum { SCRIPT_UNSET, /* Requirement as yet unknown */
+ SCRIPT_MAP, /* Bitmap contains acceptable scripts */
+ SCRIPT_HANPENDING, /* Have had only Han characters */
+ SCRIPT_HANHIRAKATA, /* Expect Han or Hirikata */
+ SCRIPT_HANBOPOMOFO, /* Expect Han or Bopomofo */
+ SCRIPT_HANHANGUL /* Expect Han or Hangul */
+ };
+
+#define UCD_MAPSIZE (ucp_Unknown/32 + 1)
+#define FULL_MAPSIZE (ucp_Script_Count/32 + 1)
+
+BOOL
+PRIV(script_run)(PCRE2_SPTR ptr, PCRE2_SPTR endptr, BOOL utf)
+{
+#ifdef SUPPORT_UNICODE
+uint32_t require_state = SCRIPT_UNSET;
+uint32_t require_map[FULL_MAPSIZE];
+uint32_t map[FULL_MAPSIZE];
+uint32_t require_digitset = 0;
+uint32_t c;
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+(void)utf; /* Avoid compiler warning */
+#endif
+
+/* Any string containing fewer than 2 characters is a valid script run. */
+
+if (ptr >= endptr) return TRUE;
+GETCHARINCTEST(c, ptr);
+if (ptr >= endptr) return TRUE;
+
+/* Initialize the require map. This is a full-size bitmap that has a bit for
+every script, as opposed to the maps in ucd_script_sets, which only have bits
+for scripts less than ucp_Unknown - those that appear in script extension
+lists. */
+
+for (int i = 0; i < FULL_MAPSIZE; i++) require_map[i] = 0;
+
+/* Scan strings of two or more characters, checking the Unicode characteristics
+of each code point. There is special code for scripts that can be combined with
+characters from the Han Chinese script. This may be used in conjunction with
+four other scripts in these combinations:
+
+. Han with Hiragana and Katakana is allowed (for Japanese).
+. Han with Bopomofo is allowed (for Taiwanese Mandarin).
+. Han with Hangul is allowed (for Korean).
+
+If the first significant character's script is one of the four, the required
+script type is immediately known. However, if the first significant
+character's script is Han, we have to keep checking for a non-Han character.
+Hence the SCRIPT_HANPENDING state. */
+
+for (;;)
+ {
+ const ucd_record *ucd = GET_UCD(c);
+ uint32_t script = ucd->script;
+
+ /* If the script is Unknown, the string is not a valid script run. Such
+ characters can only form script runs of length one (see test above). */
+
+ if (script == ucp_Unknown) return FALSE;
+
+ /* A character without any script extensions whose script is Inherited or
+ Common is always accepted with any script. If there are extensions, the
+ following processing happens for all scripts. */
+
+ if (UCD_SCRIPTX_PROP(ucd) != 0 || (script != ucp_Inherited && script != ucp_Common))
+ {
+ BOOL OK;
+
+ /* Set up a full-sized map for this character that can include bits for all
+ scripts. Copy the scriptx map for this character (which covers those
+ scripts that appear in script extension lists), set the remaining values to
+ zero, and then, except for Common or Inherited, add this script's bit to
+ the map. */
+
+ memcpy(map, PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(ucd), UCD_MAPSIZE * sizeof(uint32_t));
+ memset(map + UCD_MAPSIZE, 0, (FULL_MAPSIZE - UCD_MAPSIZE) * sizeof(uint32_t));
+ if (script != ucp_Common && script != ucp_Inherited) MAPSET(map, script);
+
+ /* Handle the different checking states */
+
+ switch(require_state)
+ {
+ /* First significant character - it might follow Common or Inherited
+ characters that do not have any script extensions. */
+
+ case SCRIPT_UNSET:
+ switch(script)
+ {
+ case ucp_Han:
+ require_state = SCRIPT_HANPENDING;
+ break;
+
+ case ucp_Hiragana:
+ case ucp_Katakana:
+ require_state = SCRIPT_HANHIRAKATA;
+ break;
+
+ case ucp_Bopomofo:
+ require_state = SCRIPT_HANBOPOMOFO;
+ break;
+
+ case ucp_Hangul:
+ require_state = SCRIPT_HANHANGUL;
+ break;
+
+ default:
+ memcpy(require_map, map, FULL_MAPSIZE * sizeof(uint32_t));
+ require_state = SCRIPT_MAP;
+ break;
+ }
+ break;
+
+ /* The first significant character was Han. An inspection of the Unicode
+ 11.0.0 files shows that there are the following types of Script Extension
+ list that involve the Han, Bopomofo, Hiragana, Katakana, and Hangul
+ scripts:
+
+ . Bopomofo + Han
+ . Han + Hiragana + Katakana
+ . Hiragana + Katakana
+ . Bopopmofo + Hangul + Han + Hiragana + Katakana
+
+ The following code tries to make sense of this. */
+
+#define FOUND_BOPOMOFO 1
+#define FOUND_HIRAGANA 2
+#define FOUND_KATAKANA 4
+#define FOUND_HANGUL 8
+
+ case SCRIPT_HANPENDING:
+ if (script != ucp_Han) /* Another Han does nothing */
+ {
+ uint32_t chspecial = 0;
+
+ if (MAPBIT(map, ucp_Bopomofo) != 0) chspecial |= FOUND_BOPOMOFO;
+ if (MAPBIT(map, ucp_Hiragana) != 0) chspecial |= FOUND_HIRAGANA;
+ if (MAPBIT(map, ucp_Katakana) != 0) chspecial |= FOUND_KATAKANA;
+ if (MAPBIT(map, ucp_Hangul) != 0) chspecial |= FOUND_HANGUL;
+
+ if (chspecial == 0) return FALSE; /* Not allowed with Han */
+
+ if (chspecial == FOUND_BOPOMOFO)
+ require_state = SCRIPT_HANBOPOMOFO;
+ else if (chspecial == (FOUND_HIRAGANA|FOUND_KATAKANA))
+ require_state = SCRIPT_HANHIRAKATA;
+
+ /* Otherwise this character must be allowed with all of them, so remain
+ in the pending state. */
+ }
+ break;
+
+ /* Previously encountered one of the "with Han" scripts. Check that
+ this character is appropriate. */
+
+ case SCRIPT_HANHIRAKATA:
+ if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hiragana) +
+ MAPBIT(map, ucp_Katakana) == 0) return FALSE;
+ break;
+
+ case SCRIPT_HANBOPOMOFO:
+ if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Bopomofo) == 0) return FALSE;
+ break;
+
+ case SCRIPT_HANHANGUL:
+ if (MAPBIT(map, ucp_Han) + MAPBIT(map, ucp_Hangul) == 0) return FALSE;
+ break;
+
+ /* Previously encountered one or more characters that are allowed with a
+ list of scripts. */
+
+ case SCRIPT_MAP:
+ OK = FALSE;
+
+ for (int i = 0; i < FULL_MAPSIZE; i++)
+ {
+ if ((require_map[i] & map[i]) != 0)
+ {
+ OK = TRUE;
+ break;
+ }
+ }
+
+ if (!OK) return FALSE;
+
+ /* The rest of the string must be in this script, but we have to
+ allow for the Han complications. */
+
+ switch(script)
+ {
+ case ucp_Han:
+ require_state = SCRIPT_HANPENDING;
+ break;
+
+ case ucp_Hiragana:
+ case ucp_Katakana:
+ require_state = SCRIPT_HANHIRAKATA;
+ break;
+
+ case ucp_Bopomofo:
+ require_state = SCRIPT_HANBOPOMOFO;
+ break;
+
+ case ucp_Hangul:
+ require_state = SCRIPT_HANHANGUL;
+ break;
+
+ /* Compute the intersection of the required list of scripts and the
+ allowed scripts for this character. */
+
+ default:
+ for (int i = 0; i < FULL_MAPSIZE; i++) require_map[i] &= map[i];
+ break;
+ }
+
+ break;
+ }
+ } /* End checking character's script and extensions. */
+
+ /* The character is in an acceptable script. We must now ensure that all
+ decimal digits in the string come from the same set. Some scripts (e.g.
+ Common, Arabic) have more than one set of decimal digits. This code does
+ not allow mixing sets, even within the same script. The vector called
+ PRIV(ucd_digit_sets)[] contains, in its first element, the number of
+ following elements, and then, in ascending order, the code points of the
+ '9' characters in every set of 10 digits. Each set is identified by the
+ offset in the vector of its '9' character. An initial check of the first
+ value picks up ASCII digits quickly. Otherwise, a binary chop is used. */
+
+ if (ucd->chartype == ucp_Nd)
+ {
+ uint32_t digitset;
+
+ if (c <= PRIV(ucd_digit_sets)[1]) digitset = 1; else
+ {
+ int mid;
+ int bot = 1;
+ int top = PRIV(ucd_digit_sets)[0];
+ for (;;)
+ {
+ if (top <= bot + 1) /* <= rather than == is paranoia */
+ {
+ digitset = top;
+ break;
+ }
+ mid = (top + bot) / 2;
+ if (c <= PRIV(ucd_digit_sets)[mid]) top = mid; else bot = mid;
+ }
+ }
+
+ /* A required value of 0 means "unset". */
+
+ if (require_digitset == 0) require_digitset = digitset;
+ else if (digitset != require_digitset) return FALSE;
+ } /* End digit handling */
+
+ /* If we haven't yet got to the end, pick up the next character. */
+
+ if (ptr >= endptr) return TRUE;
+ GETCHARINCTEST(c, ptr);
+ } /* End checking loop */
+
+#else /* NOT SUPPORT_UNICODE */
+(void)ptr;
+(void)endptr;
+(void)utf;
+return TRUE;
+#endif /* SUPPORT_UNICODE */
+}
+
+/* End of pcre2_script_run.c */
diff --git a/contrib/libs/pcre2/src/pcre2_serialize.c b/contrib/libs/pcre2/src/pcre2_serialize.c
new file mode 100644
index 0000000000..6d5c44b038
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_serialize.c
@@ -0,0 +1,286 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2020 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains functions for serializing and deserializing
+a sequence of compiled codes. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+
+#include "pcre2_internal.h"
+
+/* Magic number to provide a small check against being handed junk. */
+
+#define SERIALIZED_DATA_MAGIC 0x50523253u
+
+/* Deserialization is limited to the current PCRE version and
+character width. */
+
+#define SERIALIZED_DATA_VERSION \
+ ((PCRE2_MAJOR) | ((PCRE2_MINOR) << 16))
+
+#define SERIALIZED_DATA_CONFIG \
+ (sizeof(PCRE2_UCHAR) | ((sizeof(void*)) << 8) | ((sizeof(PCRE2_SIZE)) << 16))
+
+
+
+/*************************************************
+* Serialize compiled patterns *
+*************************************************/
+
+PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION
+pcre2_serialize_encode(const pcre2_code **codes, int32_t number_of_codes,
+ uint8_t **serialized_bytes, PCRE2_SIZE *serialized_size,
+ pcre2_general_context *gcontext)
+{
+uint8_t *bytes;
+uint8_t *dst_bytes;
+int32_t i;
+PCRE2_SIZE total_size;
+const pcre2_real_code *re;
+const uint8_t *tables;
+pcre2_serialized_data *data;
+
+const pcre2_memctl *memctl = (gcontext != NULL) ?
+ &gcontext->memctl : &PRIV(default_compile_context).memctl;
+
+if (codes == NULL || serialized_bytes == NULL || serialized_size == NULL)
+ return PCRE2_ERROR_NULL;
+
+if (number_of_codes <= 0) return PCRE2_ERROR_BADDATA;
+
+/* Compute total size. */
+total_size = sizeof(pcre2_serialized_data) + TABLES_LENGTH;
+tables = NULL;
+
+for (i = 0; i < number_of_codes; i++)
+ {
+ if (codes[i] == NULL) return PCRE2_ERROR_NULL;
+ re = (const pcre2_real_code *)(codes[i]);
+ if (re->magic_number != MAGIC_NUMBER) return PCRE2_ERROR_BADMAGIC;
+ if (tables == NULL)
+ tables = re->tables;
+ else if (tables != re->tables)
+ return PCRE2_ERROR_MIXEDTABLES;
+ total_size += re->blocksize;
+ }
+
+/* Initialize the byte stream. */
+bytes = memctl->malloc(total_size + sizeof(pcre2_memctl), memctl->memory_data);
+if (bytes == NULL) return PCRE2_ERROR_NOMEMORY;
+
+/* The controller is stored as a hidden parameter. */
+memcpy(bytes, memctl, sizeof(pcre2_memctl));
+bytes += sizeof(pcre2_memctl);
+
+data = (pcre2_serialized_data *)bytes;
+data->magic = SERIALIZED_DATA_MAGIC;
+data->version = SERIALIZED_DATA_VERSION;
+data->config = SERIALIZED_DATA_CONFIG;
+data->number_of_codes = number_of_codes;
+
+/* Copy all compiled code data. */
+dst_bytes = bytes + sizeof(pcre2_serialized_data);
+memcpy(dst_bytes, tables, TABLES_LENGTH);
+dst_bytes += TABLES_LENGTH;
+
+for (i = 0; i < number_of_codes; i++)
+ {
+ re = (const pcre2_real_code *)(codes[i]);
+ (void)memcpy(dst_bytes, (char *)re, re->blocksize);
+
+ /* Certain fields in the compiled code block are re-set during
+ deserialization. In order to ensure that the serialized data stream is always
+ the same for the same pattern, set them to zero here. We can't assume the
+ copy of the pattern is correctly aligned for accessing the fields as part of
+ a structure. Note the use of sizeof(void *) in the second of these, to
+ specify the size of a pointer. If sizeof(uint8_t *) is used (tables is a
+ pointer to uint8_t), gcc gives a warning because the first argument is also a
+ pointer to uint8_t. Casting the first argument to (void *) can stop this, but
+ it didn't stop Coverity giving the same complaint. */
+
+ (void)memset(dst_bytes + offsetof(pcre2_real_code, memctl), 0,
+ sizeof(pcre2_memctl));
+ (void)memset(dst_bytes + offsetof(pcre2_real_code, tables), 0,
+ sizeof(void *));
+ (void)memset(dst_bytes + offsetof(pcre2_real_code, executable_jit), 0,
+ sizeof(void *));
+
+ dst_bytes += re->blocksize;
+ }
+
+*serialized_bytes = bytes;
+*serialized_size = total_size;
+return number_of_codes;
+}
+
+
+/*************************************************
+* Deserialize compiled patterns *
+*************************************************/
+
+PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION
+pcre2_serialize_decode(pcre2_code **codes, int32_t number_of_codes,
+ const uint8_t *bytes, pcre2_general_context *gcontext)
+{
+const pcre2_serialized_data *data = (const pcre2_serialized_data *)bytes;
+const pcre2_memctl *memctl = (gcontext != NULL) ?
+ &gcontext->memctl : &PRIV(default_compile_context).memctl;
+
+const uint8_t *src_bytes;
+pcre2_real_code *dst_re;
+uint8_t *tables;
+int32_t i, j;
+
+/* Sanity checks. */
+
+if (data == NULL || codes == NULL) return PCRE2_ERROR_NULL;
+if (number_of_codes <= 0) return PCRE2_ERROR_BADDATA;
+if (data->number_of_codes <= 0) return PCRE2_ERROR_BADSERIALIZEDDATA;
+if (data->magic != SERIALIZED_DATA_MAGIC) return PCRE2_ERROR_BADMAGIC;
+if (data->version != SERIALIZED_DATA_VERSION) return PCRE2_ERROR_BADMODE;
+if (data->config != SERIALIZED_DATA_CONFIG) return PCRE2_ERROR_BADMODE;
+
+if (number_of_codes > data->number_of_codes)
+ number_of_codes = data->number_of_codes;
+
+src_bytes = bytes + sizeof(pcre2_serialized_data);
+
+/* Decode tables. The reference count for the tables is stored immediately
+following them. */
+
+tables = memctl->malloc(TABLES_LENGTH + sizeof(PCRE2_SIZE), memctl->memory_data);
+if (tables == NULL) return PCRE2_ERROR_NOMEMORY;
+
+memcpy(tables, src_bytes, TABLES_LENGTH);
+*(PCRE2_SIZE *)(tables + TABLES_LENGTH) = number_of_codes;
+src_bytes += TABLES_LENGTH;
+
+/* Decode the byte stream. We must not try to read the size from the compiled
+code block in the stream, because it might be unaligned, which causes errors on
+hardware such as Sparc-64 that doesn't like unaligned memory accesses. The type
+of the blocksize field is given its own name to ensure that it is the same here
+as in the block. */
+
+for (i = 0; i < number_of_codes; i++)
+ {
+ CODE_BLOCKSIZE_TYPE blocksize;
+ memcpy(&blocksize, src_bytes + offsetof(pcre2_real_code, blocksize),
+ sizeof(CODE_BLOCKSIZE_TYPE));
+ if (blocksize <= sizeof(pcre2_real_code))
+ return PCRE2_ERROR_BADSERIALIZEDDATA;
+
+ /* The allocator provided by gcontext replaces the original one. */
+
+ dst_re = (pcre2_real_code *)PRIV(memctl_malloc)(blocksize,
+ (pcre2_memctl *)gcontext);
+ if (dst_re == NULL)
+ {
+ memctl->free(tables, memctl->memory_data);
+ for (j = 0; j < i; j++)
+ {
+ memctl->free(codes[j], memctl->memory_data);
+ codes[j] = NULL;
+ }
+ return PCRE2_ERROR_NOMEMORY;
+ }
+
+ /* The new allocator must be preserved. */
+
+ memcpy(((uint8_t *)dst_re) + sizeof(pcre2_memctl),
+ src_bytes + sizeof(pcre2_memctl), blocksize - sizeof(pcre2_memctl));
+ if (dst_re->magic_number != MAGIC_NUMBER ||
+ dst_re->name_entry_size > MAX_NAME_SIZE + IMM2_SIZE + 1 ||
+ dst_re->name_count > MAX_NAME_COUNT)
+ {
+ memctl->free(dst_re, memctl->memory_data);
+ return PCRE2_ERROR_BADSERIALIZEDDATA;
+ }
+
+ /* At the moment only one table is supported. */
+
+ dst_re->tables = tables;
+ dst_re->executable_jit = NULL;
+ dst_re->flags |= PCRE2_DEREF_TABLES;
+
+ codes[i] = dst_re;
+ src_bytes += blocksize;
+ }
+
+return number_of_codes;
+}
+
+
+/*************************************************
+* Get the number of serialized patterns *
+*************************************************/
+
+PCRE2_EXP_DEFN int32_t PCRE2_CALL_CONVENTION
+pcre2_serialize_get_number_of_codes(const uint8_t *bytes)
+{
+const pcre2_serialized_data *data = (const pcre2_serialized_data *)bytes;
+
+if (data == NULL) return PCRE2_ERROR_NULL;
+if (data->magic != SERIALIZED_DATA_MAGIC) return PCRE2_ERROR_BADMAGIC;
+if (data->version != SERIALIZED_DATA_VERSION) return PCRE2_ERROR_BADMODE;
+if (data->config != SERIALIZED_DATA_CONFIG) return PCRE2_ERROR_BADMODE;
+
+return data->number_of_codes;
+}
+
+
+/*************************************************
+* Free the allocated stream *
+*************************************************/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_serialize_free(uint8_t *bytes)
+{
+if (bytes != NULL)
+ {
+ pcre2_memctl *memctl = (pcre2_memctl *)(bytes - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+/* End of pcre2_serialize.c */
diff --git a/contrib/libs/pcre2/src/pcre2_string_utils.c b/contrib/libs/pcre2/src/pcre2_string_utils.c
new file mode 100644
index 0000000000..3230919d51
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_string_utils.c
@@ -0,0 +1,237 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2018-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains internal functions for comparing and finding the length
+of strings. These are used instead of strcmp() etc because the standard
+functions work only on 8-bit data. */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+/*************************************************
+* Emulated memmove() for systems without it *
+*************************************************/
+
+/* This function can make use of bcopy() if it is available. Otherwise do it by
+steam, as there some non-Unix environments that lack both memmove() and
+bcopy(). */
+
+#if !defined(VPCOMPAT) && !defined(HAVE_MEMMOVE)
+void *
+PRIV(memmove)(void *d, const void *s, size_t n)
+{
+#ifdef HAVE_BCOPY
+bcopy(s, d, n);
+return d;
+#else
+size_t i;
+unsigned char *dest = (unsigned char *)d;
+const unsigned char *src = (const unsigned char *)s;
+if (dest > src)
+ {
+ dest += n;
+ src += n;
+ for (i = 0; i < n; ++i) *(--dest) = *(--src);
+ return (void *)dest;
+ }
+else
+ {
+ for (i = 0; i < n; ++i) *dest++ = *src++;
+ return (void *)(dest - n);
+ }
+#endif /* not HAVE_BCOPY */
+}
+#endif /* not VPCOMPAT && not HAVE_MEMMOVE */
+
+
+/*************************************************
+* Compare two zero-terminated PCRE2 strings *
+*************************************************/
+
+/*
+Arguments:
+ str1 first string
+ str2 second string
+
+Returns: 0, 1, or -1
+*/
+
+int
+PRIV(strcmp)(PCRE2_SPTR str1, PCRE2_SPTR str2)
+{
+PCRE2_UCHAR c1, c2;
+while (*str1 != '\0' || *str2 != '\0')
+ {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) return ((c1 > c2) << 1) - 1;
+ }
+return 0;
+}
+
+
+/*************************************************
+* Compare zero-terminated PCRE2 & 8-bit strings *
+*************************************************/
+
+/* As the 8-bit string is almost always a literal, its type is specified as
+const char *.
+
+Arguments:
+ str1 first string
+ str2 second string
+
+Returns: 0, 1, or -1
+*/
+
+int
+PRIV(strcmp_c8)(PCRE2_SPTR str1, const char *str2)
+{
+PCRE2_UCHAR c1, c2;
+while (*str1 != '\0' || *str2 != '\0')
+ {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) return ((c1 > c2) << 1) - 1;
+ }
+return 0;
+}
+
+
+/*************************************************
+* Compare two PCRE2 strings, given a length *
+*************************************************/
+
+/*
+Arguments:
+ str1 first string
+ str2 second string
+ len the length
+
+Returns: 0, 1, or -1
+*/
+
+int
+PRIV(strncmp)(PCRE2_SPTR str1, PCRE2_SPTR str2, size_t len)
+{
+PCRE2_UCHAR c1, c2;
+for (; len > 0; len--)
+ {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) return ((c1 > c2) << 1) - 1;
+ }
+return 0;
+}
+
+
+/*************************************************
+* Compare PCRE2 string to 8-bit string by length *
+*************************************************/
+
+/* As the 8-bit string is almost always a literal, its type is specified as
+const char *.
+
+Arguments:
+ str1 first string
+ str2 second string
+ len the length
+
+Returns: 0, 1, or -1
+*/
+
+int
+PRIV(strncmp_c8)(PCRE2_SPTR str1, const char *str2, size_t len)
+{
+PCRE2_UCHAR c1, c2;
+for (; len > 0; len--)
+ {
+ c1 = *str1++;
+ c2 = *str2++;
+ if (c1 != c2) return ((c1 > c2) << 1) - 1;
+ }
+return 0;
+}
+
+
+/*************************************************
+* Find the length of a PCRE2 string *
+*************************************************/
+
+/*
+Argument: the string
+Returns: the length
+*/
+
+PCRE2_SIZE
+PRIV(strlen)(PCRE2_SPTR str)
+{
+PCRE2_SIZE c = 0;
+while (*str++ != 0) c++;
+return c;
+}
+
+
+/*************************************************
+* Copy 8-bit 0-terminated string to PCRE2 string *
+*************************************************/
+
+/* Arguments:
+ str1 buffer to receive the string
+ str2 8-bit string to be copied
+
+Returns: the number of code units used (excluding trailing zero)
+*/
+
+PCRE2_SIZE
+PRIV(strcpy_c8)(PCRE2_UCHAR *str1, const char *str2)
+{
+PCRE2_UCHAR *t = str1;
+while (*str2 != 0) *t++ = *str2++;
+*t = 0;
+return t - str1;
+}
+
+/* End of pcre2_string_utils.c */
diff --git a/contrib/libs/pcre2/src/pcre2_study.c b/contrib/libs/pcre2/src/pcre2_study.c
new file mode 100644
index 0000000000..b259d246eb
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_study.c
@@ -0,0 +1,1825 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains functions for scanning a compiled pattern and
+collecting data (e.g. minimum matching length). */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+/* The maximum remembered capturing brackets minimum. */
+
+#define MAX_CACHE_BACKREF 128
+
+/* Set a bit in the starting code unit bit map. */
+
+#define SET_BIT(c) re->start_bitmap[(c)/8] |= (1u << ((c)&7))
+
+/* Returns from set_start_bits() */
+
+enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN, SSB_TOODEEP };
+
+
+/*************************************************
+* Find the minimum subject length for a group *
+*************************************************/
+
+/* Scan a parenthesized group and compute the minimum length of subject that
+is needed to match it. This is a lower bound; it does not mean there is a
+string of that length that matches. In UTF mode, the result is in characters
+rather than code units. The field in a compiled pattern for storing the minimum
+length is 16-bits long (on the grounds that anything longer than that is
+pathological), so we give up when we reach that amount. This also means that
+integer overflow for really crazy patterns cannot happen.
+
+Backreference minimum lengths are cached to speed up multiple references. This
+function is called only when the highest back reference in the pattern is less
+than or equal to MAX_CACHE_BACKREF, which is one less than the size of the
+caching vector. The zeroth element contains the number of the highest set
+value.
+
+Arguments:
+ re compiled pattern block
+ code pointer to start of group (the bracket)
+ startcode pointer to start of the whole pattern's code
+ utf UTF flag
+ recurses chain of recurse_check to catch mutual recursion
+ countptr pointer to call count (to catch over complexity)
+ backref_cache vector for caching back references.
+
+This function is no longer called when the pattern contains (*ACCEPT); however,
+the old code for returning -1 is retained, just in case.
+
+Returns: the minimum length
+ -1 \C in UTF-8 mode
+ or (*ACCEPT)
+ or pattern too complicated
+ -2 internal error (missing capturing bracket)
+ -3 internal error (opcode not listed)
+*/
+
+static int
+find_minlength(const pcre2_real_code *re, PCRE2_SPTR code,
+ PCRE2_SPTR startcode, BOOL utf, recurse_check *recurses, int *countptr,
+ int *backref_cache)
+{
+int length = -1;
+int branchlength = 0;
+int prev_cap_recno = -1;
+int prev_cap_d = 0;
+int prev_recurse_recno = -1;
+int prev_recurse_d = 0;
+uint32_t once_fudge = 0;
+BOOL had_recurse = FALSE;
+BOOL dupcapused = (re->flags & PCRE2_DUPCAPUSED) != 0;
+PCRE2_SPTR nextbranch = code + GET(code, 1);
+PCRE2_UCHAR *cc = (PCRE2_UCHAR *)code + 1 + LINK_SIZE;
+recurse_check this_recurse;
+
+/* If this is a "could be empty" group, its minimum length is 0. */
+
+if (*code >= OP_SBRA && *code <= OP_SCOND) return 0;
+
+/* Skip over capturing bracket number */
+
+if (*code == OP_CBRA || *code == OP_CBRAPOS) cc += IMM2_SIZE;
+
+/* A large and/or complex regex can take too long to process. */
+
+if ((*countptr)++ > 1000) return -1;
+
+/* Scan along the opcodes for this branch. If we get to the end of the branch,
+check the length against that of the other branches. If the accumulated length
+passes 16-bits, reset to that value and skip the rest of the branch. */
+
+for (;;)
+ {
+ int d, min, recno;
+ PCRE2_UCHAR op, *cs, *ce;
+
+ if (branchlength >= UINT16_MAX)
+ {
+ branchlength = UINT16_MAX;
+ cc = (PCRE2_UCHAR *)nextbranch;
+ }
+
+ op = *cc;
+ switch (op)
+ {
+ case OP_COND:
+ case OP_SCOND:
+
+ /* If there is only one branch in a condition, the implied branch has zero
+ length, so we don't add anything. This covers the DEFINE "condition"
+ automatically. If there are two branches we can treat it the same as any
+ other non-capturing subpattern. */
+
+ cs = cc + GET(cc, 1);
+ if (*cs != OP_ALT)
+ {
+ cc = cs + 1 + LINK_SIZE;
+ break;
+ }
+ goto PROCESS_NON_CAPTURE;
+
+ case OP_BRA:
+ /* There's a special case of OP_BRA, when it is wrapped round a repeated
+ OP_RECURSE. We'd like to process the latter at this level so that
+ remembering the value works for repeated cases. So we do nothing, but
+ set a fudge value to skip over the OP_KET after the recurse. */
+
+ if (cc[1+LINK_SIZE] == OP_RECURSE && cc[2*(1+LINK_SIZE)] == OP_KET)
+ {
+ once_fudge = 1 + LINK_SIZE;
+ cc += 1 + LINK_SIZE;
+ break;
+ }
+ /* Fall through */
+
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_SBRA:
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ PROCESS_NON_CAPTURE:
+ d = find_minlength(re, cc, startcode, utf, recurses, countptr,
+ backref_cache);
+ if (d < 0) return d;
+ branchlength += d;
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ cc += 1 + LINK_SIZE;
+ break;
+
+ /* To save time for repeated capturing subpatterns, we remember the
+ length of the previous one. Unfortunately we can't do the same for
+ the unnumbered ones above. Nor can we do this if (?| is present in the
+ pattern because captures with the same number are not then identical. */
+
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ recno = (int)GET2(cc, 1+LINK_SIZE);
+ if (dupcapused || recno != prev_cap_recno)
+ {
+ prev_cap_recno = recno;
+ prev_cap_d = find_minlength(re, cc, startcode, utf, recurses, countptr,
+ backref_cache);
+ if (prev_cap_d < 0) return prev_cap_d;
+ }
+ branchlength += prev_cap_d;
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ cc += 1 + LINK_SIZE;
+ break;
+
+ /* ACCEPT makes things far too complicated; we have to give up. In fact,
+ from 10.34 onwards, if a pattern contains (*ACCEPT), this function is not
+ used. However, leave the code in place, just in case. */
+
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ return -1;
+
+ /* Reached end of a branch; if it's a ket it is the end of a nested
+ call. If it's ALT it is an alternation in a nested call. If it is END it's
+ the end of the outer call. All can be handled by the same code. If the
+ length of any branch is zero, there is no need to scan any subsequent
+ branches. */
+
+ case OP_ALT:
+ case OP_KET:
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_KETRPOS:
+ case OP_END:
+ if (length < 0 || (!had_recurse && branchlength < length))
+ length = branchlength;
+ if (op != OP_ALT || length == 0) return length;
+ nextbranch = cc + GET(cc, 1);
+ cc += 1 + LINK_SIZE;
+ branchlength = 0;
+ had_recurse = FALSE;
+ break;
+
+ /* Skip over assertive subpatterns */
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERT_NA:
+ case OP_ASSERTBACK_NA:
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ /* Fall through */
+
+ /* Skip over things that don't match chars */
+
+ case OP_REVERSE:
+ case OP_CREF:
+ case OP_DNCREF:
+ case OP_RREF:
+ case OP_DNRREF:
+ case OP_FALSE:
+ case OP_TRUE:
+ case OP_CALLOUT:
+ case OP_SOD:
+ case OP_SOM:
+ case OP_EOD:
+ case OP_EODN:
+ case OP_CIRC:
+ case OP_CIRCM:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ cc += PRIV(OP_lengths)[*cc];
+ break;
+
+ case OP_CALLOUT_STR:
+ cc += GET(cc, 1 + 2*LINK_SIZE);
+ break;
+
+ /* Skip over a subpattern that has a {0} or {0,x} quantifier */
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ case OP_BRAPOSZERO:
+ case OP_SKIPZERO:
+ cc += PRIV(OP_lengths)[*cc];
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ cc += 1 + LINK_SIZE;
+ break;
+
+ /* Handle literal characters and + repetitions */
+
+ case OP_CHAR:
+ case OP_CHARI:
+ case OP_NOT:
+ case OP_NOTI:
+ case OP_PLUS:
+ case OP_PLUSI:
+ case OP_MINPLUS:
+ case OP_MINPLUSI:
+ case OP_POSPLUS:
+ case OP_POSPLUSI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ branchlength++;
+ cc += 2;
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSPLUS:
+ branchlength++;
+ cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2;
+ break;
+
+ /* Handle exact repetitions. The count is already in characters, but we
+ may need to skip over a multibyte character in UTF mode. */
+
+ case OP_EXACT:
+ case OP_EXACTI:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ branchlength += GET2(cc,1);
+ cc += 2 + IMM2_SIZE;
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ case OP_TYPEEXACT:
+ branchlength += GET2(cc,1);
+ cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP
+ || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);
+ break;
+
+ /* Handle single-char non-literal matchers */
+
+ case OP_PROP:
+ case OP_NOTPROP:
+ cc += 2;
+ /* Fall through */
+
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ALLANY:
+ case OP_EXTUNI:
+ case OP_HSPACE:
+ case OP_NOT_HSPACE:
+ case OP_VSPACE:
+ case OP_NOT_VSPACE:
+ branchlength++;
+ cc++;
+ break;
+
+ /* "Any newline" might match two characters, but it also might match just
+ one. */
+
+ case OP_ANYNL:
+ branchlength += 1;
+ cc++;
+ break;
+
+ /* The single-byte matcher means we can't proceed in UTF mode. (In
+ non-UTF mode \C will actually be turned into OP_ALLANY, so won't ever
+ appear, but leave the code, just in case.) */
+
+ case OP_ANYBYTE:
+#ifdef SUPPORT_UNICODE
+ if (utf) return -1;
+#endif
+ branchlength++;
+ cc++;
+ break;
+
+ /* For repeated character types, we have to test for \p and \P, which have
+ an extra two bytes of parameters. */
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEPOSQUERY:
+ if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2;
+ cc += PRIV(OP_lengths)[op];
+ break;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEPOSUPTO:
+ if (cc[1 + IMM2_SIZE] == OP_PROP
+ || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2;
+ cc += PRIV(OP_lengths)[op];
+ break;
+
+ /* Check a class for variable quantification */
+
+ case OP_CLASS:
+ case OP_NCLASS:
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ /* The original code caused an unsigned overflow in 64 bit systems,
+ so now we use a conditional statement. */
+ if (op == OP_XCLASS)
+ cc += GET(cc, 1);
+ else
+ cc += PRIV(OP_lengths)[OP_CLASS];
+#else
+ cc += PRIV(OP_lengths)[OP_CLASS];
+#endif
+
+ switch (*cc)
+ {
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ branchlength++;
+ /* Fall through */
+
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSQUERY:
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ branchlength += GET2(cc,1);
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default:
+ branchlength++;
+ break;
+ }
+ break;
+
+ /* Backreferences and subroutine calls (OP_RECURSE) are treated in the same
+ way: we find the minimum length for the subpattern. A recursion
+ (backreference or subroutine) causes an a flag to be set that causes the
+ length of this branch to be ignored. The logic is that a recursion can only
+ make sense if there is another alternative that stops the recursing. That
+ will provide the minimum length (when no recursion happens).
+
+ If PCRE2_MATCH_UNSET_BACKREF is set, a backreference to an unset bracket
+ matches an empty string (by default it causes a matching failure), so in
+ that case we must set the minimum length to zero.
+
+ For backreferenes, if duplicate numbers are present in the pattern we check
+ for a reference to a duplicate. If it is, we don't know which version will
+ be referenced, so we have to set the minimum length to zero. */
+
+ /* Duplicate named pattern back reference. */
+
+ case OP_DNREF:
+ case OP_DNREFI:
+ if (!dupcapused && (re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0)
+ {
+ int count = GET2(cc, 1+IMM2_SIZE);
+ PCRE2_UCHAR *slot =
+ (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) +
+ GET2(cc, 1) * re->name_entry_size;
+
+ d = INT_MAX;
+
+ /* Scan all groups with the same name; find the shortest. */
+
+ while (count-- > 0)
+ {
+ int dd, i;
+ recno = GET2(slot, 0);
+
+ if (recno <= backref_cache[0] && backref_cache[recno] >= 0)
+ dd = backref_cache[recno];
+ else
+ {
+ ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno);
+ if (cs == NULL) return -2;
+ do ce += GET(ce, 1); while (*ce == OP_ALT);
+
+ dd = 0;
+ if (!dupcapused ||
+ (PCRE2_UCHAR *)PRIV(find_bracket)(ce, utf, recno) == NULL)
+ {
+ if (cc > cs && cc < ce) /* Simple recursion */
+ {
+ had_recurse = TRUE;
+ }
+ else
+ {
+ recurse_check *r = recurses;
+ for (r = recurses; r != NULL; r = r->prev)
+ if (r->group == cs) break;
+ if (r != NULL) /* Mutual recursion */
+ {
+ had_recurse = TRUE;
+ }
+ else
+ {
+ this_recurse.prev = recurses; /* No recursion */
+ this_recurse.group = cs;
+ dd = find_minlength(re, cs, startcode, utf, &this_recurse,
+ countptr, backref_cache);
+ if (dd < 0) return dd;
+ }
+ }
+ }
+
+ backref_cache[recno] = dd;
+ for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1;
+ backref_cache[0] = recno;
+ }
+
+ if (dd < d) d = dd;
+ if (d <= 0) break; /* No point looking at any more */
+ slot += re->name_entry_size;
+ }
+ }
+ else d = 0;
+ cc += 1 + 2*IMM2_SIZE;
+ goto REPEAT_BACK_REFERENCE;
+
+ /* Single back reference by number. References by name are converted to by
+ number when there is no duplication. */
+
+ case OP_REF:
+ case OP_REFI:
+ recno = GET2(cc, 1);
+ if (recno <= backref_cache[0] && backref_cache[recno] >= 0)
+ d = backref_cache[recno];
+ else
+ {
+ int i;
+ d = 0;
+
+ if ((re->overall_options & PCRE2_MATCH_UNSET_BACKREF) == 0)
+ {
+ ce = cs = (PCRE2_UCHAR *)PRIV(find_bracket)(startcode, utf, recno);
+ if (cs == NULL) return -2;
+ do ce += GET(ce, 1); while (*ce == OP_ALT);
+
+ if (!dupcapused ||
+ (PCRE2_UCHAR *)PRIV(find_bracket)(ce, utf, recno) == NULL)
+ {
+ if (cc > cs && cc < ce) /* Simple recursion */
+ {
+ had_recurse = TRUE;
+ }
+ else
+ {
+ recurse_check *r = recurses;
+ for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
+ if (r != NULL) /* Mutual recursion */
+ {
+ had_recurse = TRUE;
+ }
+ else /* No recursion */
+ {
+ this_recurse.prev = recurses;
+ this_recurse.group = cs;
+ d = find_minlength(re, cs, startcode, utf, &this_recurse, countptr,
+ backref_cache);
+ if (d < 0) return d;
+ }
+ }
+ }
+ }
+
+ backref_cache[recno] = d;
+ for (i = backref_cache[0] + 1; i < recno; i++) backref_cache[i] = -1;
+ backref_cache[0] = recno;
+ }
+
+ cc += 1 + IMM2_SIZE;
+
+ /* Handle repeated back references */
+
+ REPEAT_BACK_REFERENCE:
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSQUERY:
+ min = 0;
+ cc++;
+ break;
+
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRPOSPLUS:
+ min = 1;
+ cc++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ min = GET2(cc, 1);
+ cc += 1 + 2 * IMM2_SIZE;
+ break;
+
+ default:
+ min = 1;
+ break;
+ }
+
+ /* Take care not to overflow: (1) min and d are ints, so check that their
+ product is not greater than INT_MAX. (2) branchlength is limited to
+ UINT16_MAX (checked at the top of the loop). */
+
+ if ((d > 0 && (INT_MAX/d) < min) || UINT16_MAX - branchlength < min*d)
+ branchlength = UINT16_MAX;
+ else branchlength += min * d;
+ break;
+
+ /* Recursion always refers to the first occurrence of a subpattern with a
+ given number. Therefore, we can always make use of caching, even when the
+ pattern contains multiple subpatterns with the same number. */
+
+ case OP_RECURSE:
+ cs = ce = (PCRE2_UCHAR *)startcode + GET(cc, 1);
+ recno = GET2(cs, 1+LINK_SIZE);
+ if (recno == prev_recurse_recno)
+ {
+ branchlength += prev_recurse_d;
+ }
+ else
+ {
+ do ce += GET(ce, 1); while (*ce == OP_ALT);
+ if (cc > cs && cc < ce) /* Simple recursion */
+ had_recurse = TRUE;
+ else
+ {
+ recurse_check *r = recurses;
+ for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
+ if (r != NULL) /* Mutual recursion */
+ had_recurse = TRUE;
+ else
+ {
+ this_recurse.prev = recurses;
+ this_recurse.group = cs;
+ prev_recurse_d = find_minlength(re, cs, startcode, utf, &this_recurse,
+ countptr, backref_cache);
+ if (prev_recurse_d < 0) return prev_recurse_d;
+ prev_recurse_recno = recno;
+ branchlength += prev_recurse_d;
+ }
+ }
+ }
+ cc += 1 + LINK_SIZE + once_fudge;
+ once_fudge = 0;
+ break;
+
+ /* Anything else does not or need not match a character. We can get the
+ item's length from the table, but for those that can match zero occurrences
+ of a character, we must take special action for UTF-8 characters. As it
+ happens, the "NOT" versions of these opcodes are used at present only for
+ ASCII characters, so they could be omitted from this list. However, in
+ future that may change, so we include them here so as not to leave a
+ gotcha for a future maintainer. */
+
+ case OP_UPTO:
+ case OP_UPTOI:
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ case OP_MINUPTO:
+ case OP_MINUPTOI:
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ case OP_POSUPTO:
+ case OP_POSUPTOI:
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+
+ case OP_STAR:
+ case OP_STARI:
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_MINSTAR:
+ case OP_MINSTARI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_POSSTAR:
+ case OP_POSSTARI:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+
+ case OP_QUERY:
+ case OP_QUERYI:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_MINQUERY:
+ case OP_MINQUERYI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ case OP_POSQUERY:
+ case OP_POSQUERYI:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+
+ cc += PRIV(OP_lengths)[op];
+#ifdef SUPPORT_UNICODE
+ if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
+#endif
+ break;
+
+ /* Skip these, but we need to add in the name length. */
+
+ case OP_MARK:
+ case OP_COMMIT_ARG:
+ case OP_PRUNE_ARG:
+ case OP_SKIP_ARG:
+ case OP_THEN_ARG:
+ cc += PRIV(OP_lengths)[op] + cc[1];
+ break;
+
+ /* The remaining opcodes are just skipped over. */
+
+ case OP_CLOSE:
+ case OP_COMMIT:
+ case OP_FAIL:
+ case OP_PRUNE:
+ case OP_SET_SOM:
+ case OP_SKIP:
+ case OP_THEN:
+ cc += PRIV(OP_lengths)[op];
+ break;
+
+ /* This should not occur: we list all opcodes explicitly so that when
+ new ones get added they are properly considered. */
+
+ default:
+ return -3;
+ }
+ }
+/* Control never gets here */
+}
+
+
+
+/*************************************************
+* Set a bit and maybe its alternate case *
+*************************************************/
+
+/* Given a character, set its first code unit's bit in the table, and also the
+corresponding bit for the other version of a letter if we are caseless.
+
+Arguments:
+ re points to the regex block
+ p points to the first code unit of the character
+ caseless TRUE if caseless
+ utf TRUE for UTF mode
+ ucp TRUE for UCP mode
+
+Returns: pointer after the character
+*/
+
+static PCRE2_SPTR
+set_table_bit(pcre2_real_code *re, PCRE2_SPTR p, BOOL caseless, BOOL utf,
+ BOOL ucp)
+{
+uint32_t c = *p++; /* First code unit */
+
+(void)utf; /* Stop compiler warnings when UTF not supported */
+(void)ucp;
+
+/* In 16-bit and 32-bit modes, code units greater than 0xff set the bit for
+0xff. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+if (c > 0xff) SET_BIT(0xff); else
+#endif
+
+SET_BIT(c);
+
+/* In UTF-8 or UTF-16 mode, pick up the remaining code units in order to find
+the end of the character, even when caseless. */
+
+#ifdef SUPPORT_UNICODE
+if (utf)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (c >= 0xc0) GETUTF8INC(c, p);
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ if ((c & 0xfc00) == 0xd800) GETUTF16INC(c, p);
+#endif
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* If caseless, handle the other case of the character. */
+
+if (caseless)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf || ucp)
+ {
+ c = UCD_OTHERCASE(c);
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+ {
+ PCRE2_UCHAR buff[6];
+ (void)PRIV(ord2utf)(c, buff);
+ SET_BIT(buff[0]);
+ }
+ else if (c < 256) SET_BIT(c);
+#else /* 16-bit or 32-bit mode */
+ if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
+#endif
+ }
+
+ else
+#endif /* SUPPORT_UNICODE */
+
+ /* Not UTF or UCP */
+
+ if (MAX_255(c)) SET_BIT(re->tables[fcc_offset + c]);
+ }
+
+return p;
+}
+
+
+
+/*************************************************
+* Set bits for a positive character type *
+*************************************************/
+
+/* This function sets starting bits for a character type. In UTF-8 mode, we can
+only do a direct setting for bytes less than 128, as otherwise there can be
+confusion with bytes in the middle of UTF-8 characters. In a "traditional"
+environment, the tables will only recognize ASCII characters anyway, but in at
+least one Windows environment, some higher bytes bits were set in the tables.
+So we deal with that case by considering the UTF-8 encoding.
+
+Arguments:
+ re the regex block
+ cbit type the type of character wanted
+ table_limit 32 for non-UTF-8; 16 for UTF-8
+
+Returns: nothing
+*/
+
+static void
+set_type_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit)
+{
+uint32_t c;
+for (c = 0; c < table_limit; c++)
+ re->start_bitmap[c] |= re->tables[c+cbits_offset+cbit_type];
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+if (table_limit == 32) return;
+for (c = 128; c < 256; c++)
+ {
+ if ((re->tables[cbits_offset + c/8] & (1u << (c&7))) != 0)
+ {
+ PCRE2_UCHAR buff[6];
+ (void)PRIV(ord2utf)(c, buff);
+ SET_BIT(buff[0]);
+ }
+ }
+#endif /* UTF-8 */
+}
+
+
+/*************************************************
+* Set bits for a negative character type *
+*************************************************/
+
+/* This function sets starting bits for a negative character type such as \D.
+In UTF-8 mode, we can only do a direct setting for bytes less than 128, as
+otherwise there can be confusion with bytes in the middle of UTF-8 characters.
+Unlike in the positive case, where we can set appropriate starting bits for
+specific high-valued UTF-8 characters, in this case we have to set the bits for
+all high-valued characters. The lowest is 0xc2, but we overkill by starting at
+0xc0 (192) for simplicity.
+
+Arguments:
+ re the regex block
+ cbit type the type of character wanted
+ table_limit 32 for non-UTF-8; 16 for UTF-8
+
+Returns: nothing
+*/
+
+static void
+set_nottype_bits(pcre2_real_code *re, int cbit_type, unsigned int table_limit)
+{
+uint32_t c;
+for (c = 0; c < table_limit; c++)
+ re->start_bitmap[c] |= (uint8_t)(~(re->tables[c+cbits_offset+cbit_type]));
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+if (table_limit != 32) for (c = 24; c < 32; c++) re->start_bitmap[c] = 0xff;
+#endif
+}
+
+
+
+/*************************************************
+* Create bitmap of starting code units *
+*************************************************/
+
+/* This function scans a compiled unanchored expression recursively and
+attempts to build a bitmap of the set of possible starting code units whose
+values are less than 256. In 16-bit and 32-bit mode, values above 255 all cause
+the 255 bit to be set. When calling set[_not]_type_bits() in UTF-8 (sic) mode
+we pass a value of 16 rather than 32 as the final argument. (See comments in
+those functions for the reason.)
+
+The SSB_CONTINUE return is useful for parenthesized groups in patterns such as
+(a*)b where the group provides some optional starting code units but scanning
+must continue at the outer level to find at least one mandatory code unit. At
+the outermost level, this function fails unless the result is SSB_DONE.
+
+We restrict recursion (for nested groups) to 1000 to avoid stack overflow
+issues.
+
+Arguments:
+ re points to the compiled regex block
+ code points to an expression
+ utf TRUE if in UTF mode
+ ucp TRUE if in UCP mode
+ depthptr pointer to recurse depth
+
+Returns: SSB_FAIL => Failed to find any starting code units
+ SSB_DONE => Found mandatory starting code units
+ SSB_CONTINUE => Found optional starting code units
+ SSB_UNKNOWN => Hit an unrecognized opcode
+ SSB_TOODEEP => Recursion is too deep
+*/
+
+static int
+set_start_bits(pcre2_real_code *re, PCRE2_SPTR code, BOOL utf, BOOL ucp,
+ int *depthptr)
+{
+uint32_t c;
+int yield = SSB_DONE;
+
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+int table_limit = utf? 16:32;
+#else
+int table_limit = 32;
+#endif
+
+*depthptr += 1;
+if (*depthptr > 1000) return SSB_TOODEEP;
+
+do
+ {
+ BOOL try_next = TRUE;
+ PCRE2_SPTR tcode = code + 1 + LINK_SIZE;
+
+ if (*code == OP_CBRA || *code == OP_SCBRA ||
+ *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE;
+
+ while (try_next) /* Loop for items in this branch */
+ {
+ int rc;
+ uint8_t *classmap = NULL;
+#ifdef SUPPORT_WIDE_CHARS
+ PCRE2_UCHAR xclassflags;
+#endif
+
+ switch(*tcode)
+ {
+ /* If we reach something we don't understand, it means a new opcode has
+ been created that hasn't been added to this function. Hopefully this
+ problem will be discovered during testing. */
+
+ default:
+ return SSB_UNKNOWN;
+
+ /* Fail for a valid opcode that implies no starting bits. */
+
+ case OP_ACCEPT:
+ case OP_ASSERT_ACCEPT:
+ case OP_ALLANY:
+ case OP_ANY:
+ case OP_ANYBYTE:
+ case OP_CIRCM:
+ case OP_CLOSE:
+ case OP_COMMIT:
+ case OP_COMMIT_ARG:
+ case OP_COND:
+ case OP_CREF:
+ case OP_FALSE:
+ case OP_TRUE:
+ case OP_DNCREF:
+ case OP_DNREF:
+ case OP_DNREFI:
+ case OP_DNRREF:
+ case OP_DOLL:
+ case OP_DOLLM:
+ case OP_END:
+ case OP_EOD:
+ case OP_EODN:
+ case OP_EXTUNI:
+ case OP_FAIL:
+ case OP_MARK:
+ case OP_NOT:
+ case OP_NOTEXACT:
+ case OP_NOTEXACTI:
+ case OP_NOTI:
+ case OP_NOTMINPLUS:
+ case OP_NOTMINPLUSI:
+ case OP_NOTMINQUERY:
+ case OP_NOTMINQUERYI:
+ case OP_NOTMINSTAR:
+ case OP_NOTMINSTARI:
+ case OP_NOTMINUPTO:
+ case OP_NOTMINUPTOI:
+ case OP_NOTPLUS:
+ case OP_NOTPLUSI:
+ case OP_NOTPOSPLUS:
+ case OP_NOTPOSPLUSI:
+ case OP_NOTPOSQUERY:
+ case OP_NOTPOSQUERYI:
+ case OP_NOTPOSSTAR:
+ case OP_NOTPOSSTARI:
+ case OP_NOTPOSUPTO:
+ case OP_NOTPOSUPTOI:
+ case OP_NOTPROP:
+ case OP_NOTQUERY:
+ case OP_NOTQUERYI:
+ case OP_NOTSTAR:
+ case OP_NOTSTARI:
+ case OP_NOTUPTO:
+ case OP_NOTUPTOI:
+ case OP_NOT_HSPACE:
+ case OP_NOT_VSPACE:
+ case OP_PRUNE:
+ case OP_PRUNE_ARG:
+ case OP_RECURSE:
+ case OP_REF:
+ case OP_REFI:
+ case OP_REVERSE:
+ case OP_RREF:
+ case OP_SCOND:
+ case OP_SET_SOM:
+ case OP_SKIP:
+ case OP_SKIP_ARG:
+ case OP_SOD:
+ case OP_SOM:
+ case OP_THEN:
+ case OP_THEN_ARG:
+ return SSB_FAIL;
+
+ /* OP_CIRC happens only at the start of an anchored branch (multiline ^
+ uses OP_CIRCM). Skip over it. */
+
+ case OP_CIRC:
+ tcode += PRIV(OP_lengths)[OP_CIRC];
+ break;
+
+ /* A "real" property test implies no starting bits, but the fake property
+ PT_CLIST identifies a list of characters. These lists are short, as they
+ are used for characters with more than one "other case", so there is no
+ point in recognizing them for OP_NOTPROP. */
+
+ case OP_PROP:
+ if (tcode[1] != PT_CLIST) return SSB_FAIL;
+ {
+ const uint32_t *p = PRIV(ucd_caseless_sets) + tcode[2];
+ while ((c = *p++) < NOTACHAR)
+ {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+ {
+ PCRE2_UCHAR buff[6];
+ (void)PRIV(ord2utf)(c, buff);
+ c = buff[0];
+ }
+#endif
+ if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
+ }
+ }
+ try_next = FALSE;
+ break;
+
+ /* We can ignore word boundary tests. */
+
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_WORD_BOUNDARY:
+ tcode++;
+ break;
+
+ /* If we hit a bracket or a positive lookahead assertion, recurse to set
+ bits from within the subpattern. If it can't find anything, we have to
+ give up. If it finds some mandatory character(s), we are done for this
+ branch. Otherwise, carry on scanning after the subpattern. */
+
+ case OP_BRA:
+ case OP_SBRA:
+ case OP_CBRA:
+ case OP_SCBRA:
+ case OP_BRAPOS:
+ case OP_SBRAPOS:
+ case OP_CBRAPOS:
+ case OP_SCBRAPOS:
+ case OP_ONCE:
+ case OP_SCRIPT_RUN:
+ case OP_ASSERT:
+ case OP_ASSERT_NA:
+ rc = set_start_bits(re, tcode, utf, ucp, depthptr);
+ if (rc == SSB_DONE)
+ {
+ try_next = FALSE;
+ }
+ else if (rc == SSB_CONTINUE)
+ {
+ do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
+ tcode += 1 + LINK_SIZE;
+ }
+ else return rc; /* FAIL, UNKNOWN, or TOODEEP */
+ break;
+
+ /* If we hit ALT or KET, it means we haven't found anything mandatory in
+ this branch, though we might have found something optional. For ALT, we
+ continue with the next alternative, but we have to arrange that the final
+ result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET,
+ return SSB_CONTINUE: if this is the top level, that indicates failure,
+ but after a nested subpattern, it causes scanning to continue. */
+
+ case OP_ALT:
+ yield = SSB_CONTINUE;
+ try_next = FALSE;
+ break;
+
+ case OP_KET:
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_KETRPOS:
+ return SSB_CONTINUE;
+
+ /* Skip over callout */
+
+ case OP_CALLOUT:
+ tcode += PRIV(OP_lengths)[OP_CALLOUT];
+ break;
+
+ case OP_CALLOUT_STR:
+ tcode += GET(tcode, 1 + 2*LINK_SIZE);
+ break;
+
+ /* Skip over lookbehind and negative lookahead assertions */
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ASSERTBACK_NA:
+ do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
+ tcode += 1 + LINK_SIZE;
+ break;
+
+ /* BRAZERO does the bracket, but carries on. */
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ case OP_BRAPOSZERO:
+ rc = set_start_bits(re, ++tcode, utf, ucp, depthptr);
+ if (rc == SSB_FAIL || rc == SSB_UNKNOWN || rc == SSB_TOODEEP) return rc;
+ do tcode += GET(tcode,1); while (*tcode == OP_ALT);
+ tcode += 1 + LINK_SIZE;
+ break;
+
+ /* SKIPZERO skips the bracket. */
+
+ case OP_SKIPZERO:
+ tcode++;
+ do tcode += GET(tcode,1); while (*tcode == OP_ALT);
+ tcode += 1 + LINK_SIZE;
+ break;
+
+ /* Single-char * or ? sets the bit and tries the next item */
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_POSSTAR:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_POSQUERY:
+ tcode = set_table_bit(re, tcode + 1, FALSE, utf, ucp);
+ break;
+
+ case OP_STARI:
+ case OP_MINSTARI:
+ case OP_POSSTARI:
+ case OP_QUERYI:
+ case OP_MINQUERYI:
+ case OP_POSQUERYI:
+ tcode = set_table_bit(re, tcode + 1, TRUE, utf, ucp);
+ break;
+
+ /* Single-char upto sets the bit and tries the next */
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_POSUPTO:
+ tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, FALSE, utf, ucp);
+ break;
+
+ case OP_UPTOI:
+ case OP_MINUPTOI:
+ case OP_POSUPTOI:
+ tcode = set_table_bit(re, tcode + 1 + IMM2_SIZE, TRUE, utf, ucp);
+ break;
+
+ /* At least one single char sets the bit and stops */
+
+ case OP_EXACT:
+ tcode += IMM2_SIZE;
+ /* Fall through */
+ case OP_CHAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_POSPLUS:
+ (void)set_table_bit(re, tcode + 1, FALSE, utf, ucp);
+ try_next = FALSE;
+ break;
+
+ case OP_EXACTI:
+ tcode += IMM2_SIZE;
+ /* Fall through */
+ case OP_CHARI:
+ case OP_PLUSI:
+ case OP_MINPLUSI:
+ case OP_POSPLUSI:
+ (void)set_table_bit(re, tcode + 1, TRUE, utf, ucp);
+ try_next = FALSE;
+ break;
+
+ /* Special spacing and line-terminating items. These recognize specific
+ lists of characters. The difference between VSPACE and ANYNL is that the
+ latter can match the two-character CRLF sequence, but that is not
+ relevant for finding the first character, so their code here is
+ identical. */
+
+ case OP_HSPACE:
+ SET_BIT(CHAR_HT);
+ SET_BIT(CHAR_SPACE);
+
+ /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set
+ the bits for 0xA0 and for code units >= 255, independently of UTF. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(0xA0);
+ SET_BIT(0xFF);
+#else
+ /* For the 8-bit library in UTF-8 mode, set the bits for the first code
+ units of horizontal space characters. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ SET_BIT(0xC2); /* For U+00A0 */
+ SET_BIT(0xE1); /* For U+1680, U+180E */
+ SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */
+ SET_BIT(0xE3); /* For U+3000 */
+ }
+ else
+#endif
+ /* For the 8-bit library not in UTF-8 mode, set the bit for 0xA0, unless
+ the code is EBCDIC. */
+ {
+#ifndef EBCDIC
+ SET_BIT(0xA0);
+#endif /* Not EBCDIC */
+ }
+#endif /* 8-bit support */
+
+ try_next = FALSE;
+ break;
+
+ case OP_ANYNL:
+ case OP_VSPACE:
+ SET_BIT(CHAR_LF);
+ SET_BIT(CHAR_VT);
+ SET_BIT(CHAR_FF);
+ SET_BIT(CHAR_CR);
+
+ /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set
+ the bits for NEL and for code units >= 255, independently of UTF. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(CHAR_NEL);
+ SET_BIT(0xFF);
+#else
+ /* For the 8-bit library in UTF-8 mode, set the bits for the first code
+ units of vertical space characters. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ SET_BIT(0xC2); /* For U+0085 (NEL) */
+ SET_BIT(0xE2); /* For U+2028, U+2029 */
+ }
+ else
+#endif
+ /* For the 8-bit library not in UTF-8 mode, set the bit for NEL. */
+ {
+ SET_BIT(CHAR_NEL);
+ }
+#endif /* 8-bit support */
+
+ try_next = FALSE;
+ break;
+
+ /* Single character types set the bits and stop. Note that if PCRE2_UCP
+ is set, we do not see these opcodes because \d etc are converted to
+ properties. Therefore, these apply in the case when only characters less
+ than 256 are recognized to match the types. */
+
+ case OP_NOT_DIGIT:
+ set_nottype_bits(re, cbit_digit, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_DIGIT:
+ set_type_bits(re, cbit_digit, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_NOT_WHITESPACE:
+ set_nottype_bits(re, cbit_space, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_WHITESPACE:
+ set_type_bits(re, cbit_space, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_NOT_WORDCHAR:
+ set_nottype_bits(re, cbit_word, table_limit);
+ try_next = FALSE;
+ break;
+
+ case OP_WORDCHAR:
+ set_type_bits(re, cbit_word, table_limit);
+ try_next = FALSE;
+ break;
+
+ /* One or more character type fudges the pointer and restarts, knowing
+ it will hit a single character type and stop there. */
+
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEPOSPLUS:
+ tcode++;
+ break;
+
+ case OP_TYPEEXACT:
+ tcode += 1 + IMM2_SIZE;
+ break;
+
+ /* Zero or more repeats of character types set the bits and then
+ try again. */
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ case OP_TYPEPOSUPTO:
+ tcode += IMM2_SIZE; /* Fall through */
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPOSSTAR:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ case OP_TYPEPOSQUERY:
+ switch(tcode[1])
+ {
+ default:
+ case OP_ANY:
+ case OP_ALLANY:
+ return SSB_FAIL;
+
+ case OP_HSPACE:
+ SET_BIT(CHAR_HT);
+ SET_BIT(CHAR_SPACE);
+
+ /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set
+ the bits for 0xA0 and for code units >= 255, independently of UTF. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(0xA0);
+ SET_BIT(0xFF);
+#else
+ /* For the 8-bit library in UTF-8 mode, set the bits for the first code
+ units of horizontal space characters. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ SET_BIT(0xC2); /* For U+00A0 */
+ SET_BIT(0xE1); /* For U+1680, U+180E */
+ SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */
+ SET_BIT(0xE3); /* For U+3000 */
+ }
+ else
+#endif
+ /* For the 8-bit library not in UTF-8 mode, set the bit for 0xA0, unless
+ the code is EBCDIC. */
+ {
+#ifndef EBCDIC
+ SET_BIT(0xA0);
+#endif /* Not EBCDIC */
+ }
+#endif /* 8-bit support */
+ break;
+
+ case OP_ANYNL:
+ case OP_VSPACE:
+ SET_BIT(CHAR_LF);
+ SET_BIT(CHAR_VT);
+ SET_BIT(CHAR_FF);
+ SET_BIT(CHAR_CR);
+
+ /* For the 16-bit and 32-bit libraries (which can never be EBCDIC), set
+ the bits for NEL and for code units >= 255, independently of UTF. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(CHAR_NEL);
+ SET_BIT(0xFF);
+#else
+ /* For the 8-bit library in UTF-8 mode, set the bits for the first code
+ units of vertical space characters. */
+
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ SET_BIT(0xC2); /* For U+0085 (NEL) */
+ SET_BIT(0xE2); /* For U+2028, U+2029 */
+ }
+ else
+#endif
+ /* For the 8-bit library not in UTF-8 mode, set the bit for NEL. */
+ {
+ SET_BIT(CHAR_NEL);
+ }
+#endif /* 8-bit support */
+ break;
+
+ case OP_NOT_DIGIT:
+ set_nottype_bits(re, cbit_digit, table_limit);
+ break;
+
+ case OP_DIGIT:
+ set_type_bits(re, cbit_digit, table_limit);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ set_nottype_bits(re, cbit_space, table_limit);
+ break;
+
+ case OP_WHITESPACE:
+ set_type_bits(re, cbit_space, table_limit);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ set_nottype_bits(re, cbit_word, table_limit);
+ break;
+
+ case OP_WORDCHAR:
+ set_type_bits(re, cbit_word, table_limit);
+ break;
+ }
+
+ tcode += 2;
+ break;
+
+ /* Extended class: if there are any property checks, or if this is a
+ negative XCLASS without a map, give up. If there are no property checks,
+ there must be wide characters on the XCLASS list, because otherwise an
+ XCLASS would not have been created. This means that code points >= 255
+ are potential starters. In the UTF-8 case we can scan them and set bits
+ for the relevant leading bytes. */
+
+#ifdef SUPPORT_WIDE_CHARS
+ case OP_XCLASS:
+ xclassflags = tcode[1 + LINK_SIZE];
+ if ((xclassflags & XCL_HASPROP) != 0 ||
+ (xclassflags & (XCL_MAP|XCL_NOT)) == XCL_NOT)
+ return SSB_FAIL;
+
+ /* We have a positive XCLASS or a negative one without a map. Set up the
+ map pointer if there is one, and fall through. */
+
+ classmap = ((xclassflags & XCL_MAP) == 0)? NULL :
+ (uint8_t *)(tcode + 1 + LINK_SIZE + 1);
+
+ /* In UTF-8 mode, scan the character list and set bits for leading bytes,
+ then jump to handle the map. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf && (xclassflags & XCL_NOT) == 0)
+ {
+ PCRE2_UCHAR b, e;
+ PCRE2_SPTR p = tcode + 1 + LINK_SIZE + 1 + ((classmap == NULL)? 0:32);
+ tcode += GET(tcode, 1);
+
+ for (;;) switch (*p++)
+ {
+ case XCL_SINGLE:
+ b = *p++;
+ while ((*p & 0xc0) == 0x80) p++;
+ re->start_bitmap[b/8] |= (1u << (b&7));
+ break;
+
+ case XCL_RANGE:
+ b = *p++;
+ while ((*p & 0xc0) == 0x80) p++;
+ e = *p++;
+ while ((*p & 0xc0) == 0x80) p++;
+ for (; b <= e; b++)
+ re->start_bitmap[b/8] |= (1u << (b&7));
+ break;
+
+ case XCL_END:
+ goto HANDLE_CLASSMAP;
+
+ default:
+ return SSB_UNKNOWN; /* Internal error, should not occur */
+ }
+ }
+#endif /* SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8 */
+#endif /* SUPPORT_WIDE_CHARS */
+
+ /* It seems that the fall through comment must be outside the #ifdef if
+ it is to avoid the gcc compiler warning. */
+
+ /* Fall through */
+
+ /* Enter here for a negative non-XCLASS. In the 8-bit library, if we are
+ in UTF mode, any byte with a value >= 0xc4 is a potentially valid starter
+ because it starts a character with a value > 255. In 8-bit non-UTF mode,
+ there is no difference between CLASS and NCLASS. In all other wide
+ character modes, set the 0xFF bit to indicate code units >= 255. */
+
+ case OP_NCLASS:
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+ {
+ re->start_bitmap[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */
+ memset(re->start_bitmap+25, 0xff, 7); /* Bits for 0xc9 - 0xff */
+ }
+#elif PCRE2_CODE_UNIT_WIDTH != 8
+ SET_BIT(0xFF); /* For characters >= 255 */
+#endif
+ /* Fall through */
+
+ /* Enter here for a positive non-XCLASS. If we have fallen through from
+ an XCLASS, classmap will already be set; just advance the code pointer.
+ Otherwise, set up classmap for a a non-XCLASS and advance past it. */
+
+ case OP_CLASS:
+ if (*tcode == OP_XCLASS) tcode += GET(tcode, 1); else
+ {
+ classmap = (uint8_t *)(++tcode);
+ tcode += 32 / sizeof(PCRE2_UCHAR);
+ }
+
+ /* When wide characters are supported, classmap may be NULL. In UTF-8
+ (sic) mode, the bits in a class bit map correspond to character values,
+ not to byte values. However, the bit map we are constructing is for byte
+ values. So we have to do a conversion for characters whose code point is
+ greater than 127. In fact, there are only two possible starting bytes for
+ characters in the range 128 - 255. */
+
+#if defined SUPPORT_WIDE_CHARS && PCRE2_CODE_UNIT_WIDTH == 8
+ HANDLE_CLASSMAP:
+#endif
+ if (classmap != NULL)
+ {
+#if defined SUPPORT_UNICODE && PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf)
+ {
+ for (c = 0; c < 16; c++) re->start_bitmap[c] |= classmap[c];
+ for (c = 128; c < 256; c++)
+ {
+ if ((classmap[c/8] & (1u << (c&7))) != 0)
+ {
+ int d = (c >> 6) | 0xc0; /* Set bit for this starter */
+ re->start_bitmap[d/8] |= (1u << (d&7)); /* and then skip on to the */
+ c = (c & 0xc0) + 0x40 - 1; /* next relevant character. */
+ }
+ }
+ }
+ else
+#endif
+ /* In all modes except UTF-8, the two bit maps are compatible. */
+
+ {
+ for (c = 0; c < 32; c++) re->start_bitmap[c] |= classmap[c];
+ }
+ }
+
+ /* Act on what follows the class. For a zero minimum repeat, continue;
+ otherwise stop processing. */
+
+ switch (*tcode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ case OP_CRPOSSTAR:
+ case OP_CRPOSQUERY:
+ tcode++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ case OP_CRPOSRANGE:
+ if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE;
+ else try_next = FALSE;
+ break;
+
+ default:
+ try_next = FALSE;
+ break;
+ }
+ break; /* End of class handling case */
+ } /* End of switch for opcodes */
+ } /* End of try_next loop */
+
+ code += GET(code, 1); /* Advance to next branch */
+ }
+while (*code == OP_ALT);
+
+return yield;
+}
+
+
+
+/*************************************************
+* Study a compiled expression *
+*************************************************/
+
+/* This function is handed a compiled expression that it must study to produce
+information that will speed up the matching.
+
+Argument:
+ re points to the compiled expression
+
+Returns: 0 normally; non-zero should never normally occur
+ 1 unknown opcode in set_start_bits
+ 2 missing capturing bracket
+ 3 unknown opcode in find_minlength
+*/
+
+int
+PRIV(study)(pcre2_real_code *re)
+{
+int count = 0;
+PCRE2_UCHAR *code;
+BOOL utf = (re->overall_options & PCRE2_UTF) != 0;
+BOOL ucp = (re->overall_options & PCRE2_UCP) != 0;
+
+/* Find start of compiled code */
+
+code = (PCRE2_UCHAR *)((uint8_t *)re + sizeof(pcre2_real_code)) +
+ re->name_entry_size * re->name_count;
+
+/* For a pattern that has a first code unit, or a multiline pattern that
+matches only at "line start", there is no point in seeking a list of starting
+code units. */
+
+if ((re->flags & (PCRE2_FIRSTSET|PCRE2_STARTLINE)) == 0)
+ {
+ int depth = 0;
+ int rc = set_start_bits(re, code, utf, ucp, &depth);
+ if (rc == SSB_UNKNOWN) return 1;
+
+ /* If a list of starting code units was set up, scan the list to see if only
+ one or two were listed. Having only one listed is rare because usually a
+ single starting code unit will have been recognized and PCRE2_FIRSTSET set.
+ If two are listed, see if they are caseless versions of the same character;
+ if so we can replace the list with a caseless first code unit. This gives
+ better performance and is plausibly worth doing for patterns such as [Ww]ord
+ or (word|WORD). */
+
+ if (rc == SSB_DONE)
+ {
+ int i;
+ int a = -1;
+ int b = -1;
+ uint8_t *p = re->start_bitmap;
+ uint32_t flags = PCRE2_FIRSTMAPSET;
+
+ for (i = 0; i < 256; p++, i += 8)
+ {
+ uint8_t x = *p;
+ if (x != 0)
+ {
+ int c;
+ uint8_t y = x & (~x + 1); /* Least significant bit */
+ if (y != x) goto DONE; /* More than one bit set */
+
+ /* In the 16-bit and 32-bit libraries, the bit for 0xff means "0xff and
+ all wide characters", so we cannot use it here. */
+
+#if PCRE2_CODE_UNIT_WIDTH != 8
+ if (i == 248 && x == 0x80) goto DONE;
+#endif
+
+ /* Compute the character value */
+
+ c = i;
+ switch (x)
+ {
+ case 1: break;
+ case 2: c += 1; break; case 4: c += 2; break;
+ case 8: c += 3; break; case 16: c += 4; break;
+ case 32: c += 5; break; case 64: c += 6; break;
+ case 128: c += 7; break;
+ }
+
+ /* c contains the code unit value, in the range 0-255. In 8-bit UTF
+ mode, only values < 128 can be used. In all the other cases, c is a
+ character value. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ if (utf && c > 127) goto DONE;
+#endif
+ if (a < 0) a = c; /* First one found, save in a */
+ else if (b < 0) /* Second one found */
+ {
+ int d = TABLE_GET((unsigned int)c, re->tables + fcc_offset, c);
+
+#ifdef SUPPORT_UNICODE
+ if (utf || ucp)
+ {
+ if (UCD_CASESET(c) != 0) goto DONE; /* Multiple case set */
+ if (c > 127) d = UCD_OTHERCASE(c);
+ }
+#endif /* SUPPORT_UNICODE */
+
+ if (d != a) goto DONE; /* Not the other case of a */
+ b = c; /* Save second in b */
+ }
+ else goto DONE; /* More than two characters found */
+ }
+ }
+
+ /* Replace the start code unit bits with a first code unit, but only if it
+ is not the same as a required later code unit. This is because a search for
+ a required code unit starts after an explicit first code unit, but at a
+ code unit found from the bitmap. Patterns such as /a*a/ don't work
+ if both the start unit and required unit are the same. */
+
+ if (a >= 0 &&
+ (
+ (re->flags & PCRE2_LASTSET) == 0 ||
+ (
+ re->last_codeunit != (uint32_t)a &&
+ (b < 0 || re->last_codeunit != (uint32_t)b)
+ )
+ ))
+ {
+ re->first_codeunit = a;
+ flags = PCRE2_FIRSTSET;
+ if (b >= 0) flags |= PCRE2_FIRSTCASELESS;
+ }
+
+ DONE:
+ re->flags |= flags;
+ }
+ }
+
+/* Find the minimum length of subject string. If the pattern can match an empty
+string, the minimum length is already known. If the pattern contains (*ACCEPT)
+all bets are off, and we don't even try to find a minimum length. If there are
+more back references than the size of the vector we are going to cache them in,
+do nothing. A pattern that complicated will probably take a long time to
+analyze and may in any case turn out to be too complicated. Note that back
+reference minima are held as 16-bit numbers. */
+
+if ((re->flags & (PCRE2_MATCH_EMPTY|PCRE2_HASACCEPT)) == 0 &&
+ re->top_backref <= MAX_CACHE_BACKREF)
+ {
+ int min;
+ int backref_cache[MAX_CACHE_BACKREF+1];
+ backref_cache[0] = 0; /* Highest one that is set */
+ min = find_minlength(re, code, code, utf, NULL, &count, backref_cache);
+ switch(min)
+ {
+ case -1: /* \C in UTF mode or over-complex regex */
+ break; /* Leave minlength unchanged (will be zero) */
+
+ case -2:
+ return 2; /* missing capturing bracket */
+
+ case -3:
+ return 3; /* unrecognized opcode */
+
+ default:
+ re->minlength = (min > UINT16_MAX)? UINT16_MAX : min;
+ break;
+ }
+ }
+
+return 0;
+}
+
+/* End of pcre2_study.c */
diff --git a/contrib/libs/pcre2/src/pcre2_substitute.c b/contrib/libs/pcre2/src/pcre2_substitute.c
new file mode 100644
index 0000000000..d667d02f11
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_substitute.c
@@ -0,0 +1,1009 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+#define PTR_STACK_SIZE 20
+
+#define SUBSTITUTE_OPTIONS \
+ (PCRE2_SUBSTITUTE_EXTENDED|PCRE2_SUBSTITUTE_GLOBAL| \
+ PCRE2_SUBSTITUTE_LITERAL|PCRE2_SUBSTITUTE_MATCHED| \
+ PCRE2_SUBSTITUTE_OVERFLOW_LENGTH|PCRE2_SUBSTITUTE_REPLACEMENT_ONLY| \
+ PCRE2_SUBSTITUTE_UNKNOWN_UNSET|PCRE2_SUBSTITUTE_UNSET_EMPTY)
+
+
+
+/*************************************************
+* Find end of substitute text *
+*************************************************/
+
+/* In extended mode, we recognize ${name:+set text:unset text} and similar
+constructions. This requires the identification of unescaped : and }
+characters. This function scans for such. It must deal with nested ${
+constructions. The pointer to the text is updated, either to the required end
+character, or to where an error was detected.
+
+Arguments:
+ code points to the compiled expression (for options)
+ ptrptr points to the pointer to the start of the text (updated)
+ ptrend end of the whole string
+ last TRUE if the last expected string (only } recognized)
+
+Returns: 0 on success
+ negative error code on failure
+*/
+
+static int
+find_text_end(const pcre2_code *code, PCRE2_SPTR *ptrptr, PCRE2_SPTR ptrend,
+ BOOL last)
+{
+int rc = 0;
+uint32_t nestlevel = 0;
+BOOL literal = FALSE;
+PCRE2_SPTR ptr = *ptrptr;
+
+for (; ptr < ptrend; ptr++)
+ {
+ if (literal)
+ {
+ if (ptr[0] == CHAR_BACKSLASH && ptr < ptrend - 1 && ptr[1] == CHAR_E)
+ {
+ literal = FALSE;
+ ptr += 1;
+ }
+ }
+
+ else if (*ptr == CHAR_RIGHT_CURLY_BRACKET)
+ {
+ if (nestlevel == 0) goto EXIT;
+ nestlevel--;
+ }
+
+ else if (*ptr == CHAR_COLON && !last && nestlevel == 0) goto EXIT;
+
+ else if (*ptr == CHAR_DOLLAR_SIGN)
+ {
+ if (ptr < ptrend - 1 && ptr[1] == CHAR_LEFT_CURLY_BRACKET)
+ {
+ nestlevel++;
+ ptr += 1;
+ }
+ }
+
+ else if (*ptr == CHAR_BACKSLASH)
+ {
+ int erc;
+ int errorcode;
+ uint32_t ch;
+
+ if (ptr < ptrend - 1) switch (ptr[1])
+ {
+ case CHAR_L:
+ case CHAR_l:
+ case CHAR_U:
+ case CHAR_u:
+ ptr += 1;
+ continue;
+ }
+
+ ptr += 1; /* Must point after \ */
+ erc = PRIV(check_escape)(&ptr, ptrend, &ch, &errorcode,
+ code->overall_options, code->extra_options, FALSE, NULL);
+ ptr -= 1; /* Back to last code unit of escape */
+ if (errorcode != 0)
+ {
+ rc = errorcode;
+ goto EXIT;
+ }
+
+ switch(erc)
+ {
+ case 0: /* Data character */
+ case ESC_E: /* Isolated \E is ignored */
+ break;
+
+ case ESC_Q:
+ literal = TRUE;
+ break;
+
+ default:
+ rc = PCRE2_ERROR_BADREPESCAPE;
+ goto EXIT;
+ }
+ }
+ }
+
+rc = PCRE2_ERROR_REPMISSINGBRACE; /* Terminator not found */
+
+EXIT:
+*ptrptr = ptr;
+return rc;
+}
+
+
+
+/*************************************************
+* Match and substitute *
+*************************************************/
+
+/* This function applies a compiled re to a subject string and creates a new
+string with substitutions. The first 7 arguments are the same as for
+pcre2_match(). Either string length may be PCRE2_ZERO_TERMINATED.
+
+Arguments:
+ code points to the compiled expression
+ subject points to the subject string
+ length length of subject string (may contain binary zeros)
+ start_offset where to start in the subject string
+ options option bits
+ match_data points to a match_data block, or is NULL
+ context points a PCRE2 context
+ replacement points to the replacement string
+ rlength length of replacement string
+ buffer where to put the substituted string
+ blength points to length of buffer; updated to length of string
+
+Returns: >= 0 number of substitutions made
+ < 0 an error code
+ PCRE2_ERROR_BADREPLACEMENT means invalid use of $
+*/
+
+/* This macro checks for space in the buffer before copying into it. On
+overflow, either give an error immediately, or keep on, accumulating the
+length. */
+
+#define CHECKMEMCPY(from,length) \
+ { \
+ if (!overflowed && lengthleft < length) \
+ { \
+ if ((suboptions & PCRE2_SUBSTITUTE_OVERFLOW_LENGTH) == 0) goto NOROOM; \
+ overflowed = TRUE; \
+ extra_needed = length - lengthleft; \
+ } \
+ else if (overflowed) \
+ { \
+ extra_needed += length; \
+ } \
+ else \
+ { \
+ memcpy(buffer + buff_offset, from, CU2BYTES(length)); \
+ buff_offset += length; \
+ lengthleft -= length; \
+ } \
+ }
+
+/* Here's the function */
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substitute(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length,
+ PCRE2_SIZE start_offset, uint32_t options, pcre2_match_data *match_data,
+ pcre2_match_context *mcontext, PCRE2_SPTR replacement, PCRE2_SIZE rlength,
+ PCRE2_UCHAR *buffer, PCRE2_SIZE *blength)
+{
+int rc;
+int subs;
+int forcecase = 0;
+int forcecasereset = 0;
+uint32_t ovector_count;
+uint32_t goptions = 0;
+uint32_t suboptions;
+pcre2_match_data *internal_match_data = NULL;
+BOOL escaped_literal = FALSE;
+BOOL overflowed = FALSE;
+BOOL use_existing_match;
+BOOL replacement_only;
+#ifdef SUPPORT_UNICODE
+BOOL utf = (code->overall_options & PCRE2_UTF) != 0;
+BOOL ucp = (code->overall_options & PCRE2_UCP) != 0;
+#endif
+PCRE2_UCHAR temp[6];
+PCRE2_SPTR ptr;
+PCRE2_SPTR repend;
+PCRE2_SIZE extra_needed = 0;
+PCRE2_SIZE buff_offset, buff_length, lengthleft, fraglength;
+PCRE2_SIZE *ovector;
+PCRE2_SIZE ovecsave[3];
+pcre2_substitute_callout_block scb;
+
+/* General initialization */
+
+buff_offset = 0;
+lengthleft = buff_length = *blength;
+*blength = PCRE2_UNSET;
+ovecsave[0] = ovecsave[1] = ovecsave[2] = PCRE2_UNSET;
+
+/* Partial matching is not valid. This must come after setting *blength to
+PCRE2_UNSET, so as not to imply an offset in the replacement. */
+
+if ((options & (PCRE2_PARTIAL_HARD|PCRE2_PARTIAL_SOFT)) != 0)
+ return PCRE2_ERROR_BADOPTION;
+
+/* Validate length and find the end of the replacement. A NULL replacement of
+zero length is interpreted as an empty string. */
+
+if (replacement == NULL)
+ {
+ if (rlength != 0) return PCRE2_ERROR_NULL;
+ replacement = (PCRE2_SPTR)"";
+ }
+
+if (rlength == PCRE2_ZERO_TERMINATED) rlength = PRIV(strlen)(replacement);
+repend = replacement + rlength;
+
+/* Check for using a match that has already happened. Note that the subject
+pointer in the match data may be NULL after a no-match. */
+
+use_existing_match = ((options & PCRE2_SUBSTITUTE_MATCHED) != 0);
+replacement_only = ((options & PCRE2_SUBSTITUTE_REPLACEMENT_ONLY) != 0);
+
+/* If starting from an existing match, there must be an externally provided
+match data block. We create an internal match_data block in two cases: (a) an
+external one is not supplied (and we are not starting from an existing match);
+(b) an existing match is to be used for the first substitution. In the latter
+case, we copy the existing match into the internal block, except for any cached
+heap frame size and pointer. This ensures that no changes are made to the
+external match data block. */
+
+if (match_data == NULL)
+ {
+ pcre2_general_context *gcontext;
+ if (use_existing_match) return PCRE2_ERROR_NULL;
+ gcontext = (mcontext == NULL)?
+ (pcre2_general_context *)code :
+ (pcre2_general_context *)mcontext;
+ match_data = internal_match_data =
+ pcre2_match_data_create_from_pattern(code, gcontext);
+ if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY;
+ }
+
+else if (use_existing_match)
+ {
+ pcre2_general_context *gcontext = (mcontext == NULL)?
+ (pcre2_general_context *)code :
+ (pcre2_general_context *)mcontext;
+ int pairs = (code->top_bracket + 1 < match_data->oveccount)?
+ code->top_bracket + 1 : match_data->oveccount;
+ internal_match_data = pcre2_match_data_create(match_data->oveccount,
+ gcontext);
+ if (internal_match_data == NULL) return PCRE2_ERROR_NOMEMORY;
+ memcpy(internal_match_data, match_data, offsetof(pcre2_match_data, ovector)
+ + 2*pairs*sizeof(PCRE2_SIZE));
+ internal_match_data->heapframes = NULL;
+ internal_match_data->heapframes_size = 0;
+ match_data = internal_match_data;
+ }
+
+/* Remember ovector details */
+
+ovector = pcre2_get_ovector_pointer(match_data);
+ovector_count = pcre2_get_ovector_count(match_data);
+
+/* Fixed things in the callout block */
+
+scb.version = 0;
+scb.input = subject;
+scb.output = (PCRE2_SPTR)buffer;
+scb.ovector = ovector;
+
+/* A NULL subject of zero length is treated as an empty string. */
+
+if (subject == NULL)
+ {
+ if (length != 0) return PCRE2_ERROR_NULL;
+ subject = (PCRE2_SPTR)"";
+ }
+
+/* Find length of zero-terminated subject */
+
+if (length == PCRE2_ZERO_TERMINATED)
+ length = subject? PRIV(strlen)(subject) : 0;
+
+/* Check UTF replacement string if necessary. */
+
+#ifdef SUPPORT_UNICODE
+if (utf && (options & PCRE2_NO_UTF_CHECK) == 0)
+ {
+ rc = PRIV(valid_utf)(replacement, rlength, &(match_data->startchar));
+ if (rc != 0)
+ {
+ match_data->leftchar = 0;
+ goto EXIT;
+ }
+ }
+#endif /* SUPPORT_UNICODE */
+
+/* Save the substitute options and remove them from the match options. */
+
+suboptions = options & SUBSTITUTE_OPTIONS;
+options &= ~SUBSTITUTE_OPTIONS;
+
+/* Error if the start match offset is greater than the length of the subject. */
+
+if (start_offset > length)
+ {
+ match_data->leftchar = 0;
+ rc = PCRE2_ERROR_BADOFFSET;
+ goto EXIT;
+ }
+
+/* Copy up to the start offset, unless only the replacement is required. */
+
+if (!replacement_only) CHECKMEMCPY(subject, start_offset);
+
+/* Loop for global substituting. If PCRE2_SUBSTITUTE_MATCHED is set, the first
+match is taken from the match_data that was passed in. */
+
+subs = 0;
+do
+ {
+ PCRE2_SPTR ptrstack[PTR_STACK_SIZE];
+ uint32_t ptrstackptr = 0;
+
+ if (use_existing_match)
+ {
+ rc = match_data->rc;
+ use_existing_match = FALSE;
+ }
+ else rc = pcre2_match(code, subject, length, start_offset, options|goptions,
+ match_data, mcontext);
+
+#ifdef SUPPORT_UNICODE
+ if (utf) options |= PCRE2_NO_UTF_CHECK; /* Only need to check once */
+#endif
+
+ /* Any error other than no match returns the error code. No match when not
+ doing the special after-empty-match global rematch, or when at the end of the
+ subject, breaks the global loop. Otherwise, advance the starting point by one
+ character, copying it to the output, and try again. */
+
+ if (rc < 0)
+ {
+ PCRE2_SIZE save_start;
+
+ if (rc != PCRE2_ERROR_NOMATCH) goto EXIT;
+ if (goptions == 0 || start_offset >= length) break;
+
+ /* Advance by one code point. Then, if CRLF is a valid newline sequence and
+ we have advanced into the middle of it, advance one more code point. In
+ other words, do not start in the middle of CRLF, even if CR and LF on their
+ own are valid newlines. */
+
+ save_start = start_offset++;
+ if (subject[start_offset-1] == CHAR_CR &&
+ code->newline_convention != PCRE2_NEWLINE_CR &&
+ code->newline_convention != PCRE2_NEWLINE_LF &&
+ start_offset < length &&
+ subject[start_offset] == CHAR_LF)
+ start_offset++;
+
+ /* Otherwise, in UTF mode, advance past any secondary code points. */
+
+ else if ((code->overall_options & PCRE2_UTF) != 0)
+ {
+#if PCRE2_CODE_UNIT_WIDTH == 8
+ while (start_offset < length && (subject[start_offset] & 0xc0) == 0x80)
+ start_offset++;
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+ while (start_offset < length &&
+ (subject[start_offset] & 0xfc00) == 0xdc00)
+ start_offset++;
+#endif
+ }
+
+ /* Copy what we have advanced past (unless not required), reset the special
+ global options, and continue to the next match. */
+
+ fraglength = start_offset - save_start;
+ if (!replacement_only) CHECKMEMCPY(subject + save_start, fraglength);
+ goptions = 0;
+ continue;
+ }
+
+ /* Handle a successful match. Matches that use \K to end before they start
+ or start before the current point in the subject are not supported. */
+
+ if (ovector[1] < ovector[0] || ovector[0] < start_offset)
+ {
+ rc = PCRE2_ERROR_BADSUBSPATTERN;
+ goto EXIT;
+ }
+
+ /* Check for the same match as previous. This is legitimate after matching an
+ empty string that starts after the initial match offset. We have tried again
+ at the match point in case the pattern is one like /(?<=\G.)/ which can never
+ match at its starting point, so running the match achieves the bumpalong. If
+ we do get the same (null) match at the original match point, it isn't such a
+ pattern, so we now do the empty string magic. In all other cases, a repeat
+ match should never occur. */
+
+ if (ovecsave[0] == ovector[0] && ovecsave[1] == ovector[1])
+ {
+ if (ovector[0] == ovector[1] && ovecsave[2] != start_offset)
+ {
+ goptions = PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED;
+ ovecsave[2] = start_offset;
+ continue; /* Back to the top of the loop */
+ }
+ rc = PCRE2_ERROR_INTERNAL_DUPMATCH;
+ goto EXIT;
+ }
+
+ /* Count substitutions with a paranoid check for integer overflow; surely no
+ real call to this function would ever hit this! */
+
+ if (subs == INT_MAX)
+ {
+ rc = PCRE2_ERROR_TOOMANYREPLACE;
+ goto EXIT;
+ }
+ subs++;
+
+ /* Copy the text leading up to the match (unless not required), and remember
+ where the insert begins and how many ovector pairs are set. */
+
+ if (rc == 0) rc = ovector_count;
+ fraglength = ovector[0] - start_offset;
+ if (!replacement_only) CHECKMEMCPY(subject + start_offset, fraglength);
+ scb.output_offsets[0] = buff_offset;
+ scb.oveccount = rc;
+
+ /* Process the replacement string. If the entire replacement is literal, just
+ copy it with length check. */
+
+ ptr = replacement;
+ if ((suboptions & PCRE2_SUBSTITUTE_LITERAL) != 0)
+ {
+ CHECKMEMCPY(ptr, rlength);
+ }
+
+ /* Within a non-literal replacement, which must be scanned character by
+ character, local literal mode can be set by \Q, but only in extended mode
+ when backslashes are being interpreted. In extended mode we must handle
+ nested substrings that are to be reprocessed. */
+
+ else for (;;)
+ {
+ uint32_t ch;
+ unsigned int chlen;
+
+ /* If at the end of a nested substring, pop the stack. */
+
+ if (ptr >= repend)
+ {
+ if (ptrstackptr == 0) break; /* End of replacement string */
+ repend = ptrstack[--ptrstackptr];
+ ptr = ptrstack[--ptrstackptr];
+ continue;
+ }
+
+ /* Handle the next character */
+
+ if (escaped_literal)
+ {
+ if (ptr[0] == CHAR_BACKSLASH && ptr < repend - 1 && ptr[1] == CHAR_E)
+ {
+ escaped_literal = FALSE;
+ ptr += 2;
+ continue;
+ }
+ goto LOADLITERAL;
+ }
+
+ /* Not in literal mode. */
+
+ if (*ptr == CHAR_DOLLAR_SIGN)
+ {
+ int group, n;
+ uint32_t special = 0;
+ BOOL inparens;
+ BOOL star;
+ PCRE2_SIZE sublength;
+ PCRE2_SPTR text1_start = NULL;
+ PCRE2_SPTR text1_end = NULL;
+ PCRE2_SPTR text2_start = NULL;
+ PCRE2_SPTR text2_end = NULL;
+ PCRE2_UCHAR next;
+ PCRE2_UCHAR name[33];
+
+ if (++ptr >= repend) goto BAD;
+ if ((next = *ptr) == CHAR_DOLLAR_SIGN) goto LOADLITERAL;
+
+ group = -1;
+ n = 0;
+ inparens = FALSE;
+ star = FALSE;
+
+ if (next == CHAR_LEFT_CURLY_BRACKET)
+ {
+ if (++ptr >= repend) goto BAD;
+ next = *ptr;
+ inparens = TRUE;
+ }
+
+ if (next == CHAR_ASTERISK)
+ {
+ if (++ptr >= repend) goto BAD;
+ next = *ptr;
+ star = TRUE;
+ }
+
+ if (!star && next >= CHAR_0 && next <= CHAR_9)
+ {
+ group = next - CHAR_0;
+ while (++ptr < repend)
+ {
+ next = *ptr;
+ if (next < CHAR_0 || next > CHAR_9) break;
+ group = group * 10 + next - CHAR_0;
+
+ /* A check for a number greater than the hightest captured group
+ is sufficient here; no need for a separate overflow check. If unknown
+ groups are to be treated as unset, just skip over any remaining
+ digits and carry on. */
+
+ if (group > code->top_bracket)
+ {
+ if ((suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
+ {
+ while (++ptr < repend && *ptr >= CHAR_0 && *ptr <= CHAR_9);
+ break;
+ }
+ else
+ {
+ rc = PCRE2_ERROR_NOSUBSTRING;
+ goto PTREXIT;
+ }
+ }
+ }
+ }
+ else
+ {
+ const uint8_t *ctypes = code->tables + ctypes_offset;
+ while (MAX_255(next) && (ctypes[next] & ctype_word) != 0)
+ {
+ name[n++] = next;
+ if (n > 32) goto BAD;
+ if (++ptr >= repend) break;
+ next = *ptr;
+ }
+ if (n == 0) goto BAD;
+ name[n] = 0;
+ }
+
+ /* In extended mode we recognize ${name:+set text:unset text} and
+ ${name:-default text}. */
+
+ if (inparens)
+ {
+ if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 &&
+ !star && ptr < repend - 2 && next == CHAR_COLON)
+ {
+ special = *(++ptr);
+ if (special != CHAR_PLUS && special != CHAR_MINUS)
+ {
+ rc = PCRE2_ERROR_BADSUBSTITUTION;
+ goto PTREXIT;
+ }
+
+ text1_start = ++ptr;
+ rc = find_text_end(code, &ptr, repend, special == CHAR_MINUS);
+ if (rc != 0) goto PTREXIT;
+ text1_end = ptr;
+
+ if (special == CHAR_PLUS && *ptr == CHAR_COLON)
+ {
+ text2_start = ++ptr;
+ rc = find_text_end(code, &ptr, repend, TRUE);
+ if (rc != 0) goto PTREXIT;
+ text2_end = ptr;
+ }
+ }
+
+ else
+ {
+ if (ptr >= repend || *ptr != CHAR_RIGHT_CURLY_BRACKET)
+ {
+ rc = PCRE2_ERROR_REPMISSINGBRACE;
+ goto PTREXIT;
+ }
+ }
+
+ ptr++;
+ }
+
+ /* Have found a syntactically correct group number or name, or *name.
+ Only *MARK is currently recognized. */
+
+ if (star)
+ {
+ if (PRIV(strcmp_c8)(name, STRING_MARK) == 0)
+ {
+ PCRE2_SPTR mark = pcre2_get_mark(match_data);
+ if (mark != NULL)
+ {
+ PCRE2_SPTR mark_start = mark;
+ while (*mark != 0) mark++;
+ fraglength = mark - mark_start;
+ CHECKMEMCPY(mark_start, fraglength);
+ }
+ }
+ else goto BAD;
+ }
+
+ /* Substitute the contents of a group. We don't use substring_copy
+ functions any more, in order to support case forcing. */
+
+ else
+ {
+ PCRE2_SPTR subptr, subptrend;
+
+ /* Find a number for a named group. In case there are duplicate names,
+ search for the first one that is set. If the name is not found when
+ PCRE2_SUBSTITUTE_UNKNOWN_EMPTY is set, set the group number to a
+ non-existent group. */
+
+ if (group < 0)
+ {
+ PCRE2_SPTR first, last, entry;
+ rc = pcre2_substring_nametable_scan(code, name, &first, &last);
+ if (rc == PCRE2_ERROR_NOSUBSTRING &&
+ (suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
+ {
+ group = code->top_bracket + 1;
+ }
+ else
+ {
+ if (rc < 0) goto PTREXIT;
+ for (entry = first; entry <= last; entry += rc)
+ {
+ uint32_t ng = GET2(entry, 0);
+ if (ng < ovector_count)
+ {
+ if (group < 0) group = ng; /* First in ovector */
+ if (ovector[ng*2] != PCRE2_UNSET)
+ {
+ group = ng; /* First that is set */
+ break;
+ }
+ }
+ }
+
+ /* If group is still negative, it means we did not find a group
+ that is in the ovector. Just set the first group. */
+
+ if (group < 0) group = GET2(first, 0);
+ }
+ }
+
+ /* We now have a group that is identified by number. Find the length of
+ the captured string. If a group in a non-special substitution is unset
+ when PCRE2_SUBSTITUTE_UNSET_EMPTY is set, substitute nothing. */
+
+ rc = pcre2_substring_length_bynumber(match_data, group, &sublength);
+ if (rc < 0)
+ {
+ if (rc == PCRE2_ERROR_NOSUBSTRING &&
+ (suboptions & PCRE2_SUBSTITUTE_UNKNOWN_UNSET) != 0)
+ {
+ rc = PCRE2_ERROR_UNSET;
+ }
+ if (rc != PCRE2_ERROR_UNSET) goto PTREXIT; /* Non-unset errors */
+ if (special == 0) /* Plain substitution */
+ {
+ if ((suboptions & PCRE2_SUBSTITUTE_UNSET_EMPTY) != 0) continue;
+ goto PTREXIT; /* Else error */
+ }
+ }
+
+ /* If special is '+' we have a 'set' and possibly an 'unset' text,
+ both of which are reprocessed when used. If special is '-' we have a
+ default text for when the group is unset; it must be reprocessed. */
+
+ if (special != 0)
+ {
+ if (special == CHAR_MINUS)
+ {
+ if (rc == 0) goto LITERAL_SUBSTITUTE;
+ text2_start = text1_start;
+ text2_end = text1_end;
+ }
+
+ if (ptrstackptr >= PTR_STACK_SIZE) goto BAD;
+ ptrstack[ptrstackptr++] = ptr;
+ ptrstack[ptrstackptr++] = repend;
+
+ if (rc == 0)
+ {
+ ptr = text1_start;
+ repend = text1_end;
+ }
+ else
+ {
+ ptr = text2_start;
+ repend = text2_end;
+ }
+ continue;
+ }
+
+ /* Otherwise we have a literal substitution of a group's contents. */
+
+ LITERAL_SUBSTITUTE:
+ subptr = subject + ovector[group*2];
+ subptrend = subject + ovector[group*2 + 1];
+
+ /* Substitute a literal string, possibly forcing alphabetic case. */
+
+ while (subptr < subptrend)
+ {
+ GETCHARINCTEST(ch, subptr);
+ if (forcecase != 0)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf || ucp)
+ {
+ uint32_t type = UCD_CHARTYPE(ch);
+ if (PRIV(ucp_gentype)[type] == ucp_L &&
+ type != ((forcecase > 0)? ucp_Lu : ucp_Ll))
+ ch = UCD_OTHERCASE(ch);
+ }
+ else
+#endif
+ {
+ if (((code->tables + cbits_offset +
+ ((forcecase > 0)? cbit_upper:cbit_lower)
+ )[ch/8] & (1u << (ch%8))) == 0)
+ ch = (code->tables + fcc_offset)[ch];
+ }
+ forcecase = forcecasereset;
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (utf) chlen = PRIV(ord2utf)(ch, temp); else
+#endif
+ {
+ temp[0] = ch;
+ chlen = 1;
+ }
+ CHECKMEMCPY(temp, chlen);
+ }
+ }
+ }
+
+ /* Handle an escape sequence in extended mode. We can use check_escape()
+ to process \Q, \E, \c, \o, \x and \ followed by non-alphanumerics, but
+ the case-forcing escapes are not supported in pcre2_compile() so must be
+ recognized here. */
+
+ else if ((suboptions & PCRE2_SUBSTITUTE_EXTENDED) != 0 &&
+ *ptr == CHAR_BACKSLASH)
+ {
+ int errorcode;
+
+ if (ptr < repend - 1) switch (ptr[1])
+ {
+ case CHAR_L:
+ forcecase = forcecasereset = -1;
+ ptr += 2;
+ continue;
+
+ case CHAR_l:
+ forcecase = -1;
+ forcecasereset = 0;
+ ptr += 2;
+ continue;
+
+ case CHAR_U:
+ forcecase = forcecasereset = 1;
+ ptr += 2;
+ continue;
+
+ case CHAR_u:
+ forcecase = 1;
+ forcecasereset = 0;
+ ptr += 2;
+ continue;
+
+ default:
+ break;
+ }
+
+ ptr++; /* Point after \ */
+ rc = PRIV(check_escape)(&ptr, repend, &ch, &errorcode,
+ code->overall_options, code->extra_options, FALSE, NULL);
+ if (errorcode != 0) goto BADESCAPE;
+
+ switch(rc)
+ {
+ case ESC_E:
+ forcecase = forcecasereset = 0;
+ continue;
+
+ case ESC_Q:
+ escaped_literal = TRUE;
+ continue;
+
+ case 0: /* Data character */
+ goto LITERAL;
+
+ default:
+ goto BADESCAPE;
+ }
+ }
+
+ /* Handle a literal code unit */
+
+ else
+ {
+ LOADLITERAL:
+ GETCHARINCTEST(ch, ptr); /* Get character value, increment pointer */
+
+ LITERAL:
+ if (forcecase != 0)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf || ucp)
+ {
+ uint32_t type = UCD_CHARTYPE(ch);
+ if (PRIV(ucp_gentype)[type] == ucp_L &&
+ type != ((forcecase > 0)? ucp_Lu : ucp_Ll))
+ ch = UCD_OTHERCASE(ch);
+ }
+ else
+#endif
+ {
+ if (((code->tables + cbits_offset +
+ ((forcecase > 0)? cbit_upper:cbit_lower)
+ )[ch/8] & (1u << (ch%8))) == 0)
+ ch = (code->tables + fcc_offset)[ch];
+ }
+ forcecase = forcecasereset;
+ }
+
+#ifdef SUPPORT_UNICODE
+ if (utf) chlen = PRIV(ord2utf)(ch, temp); else
+#endif
+ {
+ temp[0] = ch;
+ chlen = 1;
+ }
+ CHECKMEMCPY(temp, chlen);
+ } /* End handling a literal code unit */
+ } /* End of loop for scanning the replacement. */
+
+ /* The replacement has been copied to the output, or its size has been
+ remembered. Do the callout if there is one and we have done an actual
+ replacement. */
+
+ if (!overflowed && mcontext != NULL && mcontext->substitute_callout != NULL)
+ {
+ scb.subscount = subs;
+ scb.output_offsets[1] = buff_offset;
+ rc = mcontext->substitute_callout(&scb, mcontext->substitute_callout_data);
+
+ /* A non-zero return means cancel this substitution. Instead, copy the
+ matched string fragment. */
+
+ if (rc != 0)
+ {
+ PCRE2_SIZE newlength = scb.output_offsets[1] - scb.output_offsets[0];
+ PCRE2_SIZE oldlength = ovector[1] - ovector[0];
+
+ buff_offset -= newlength;
+ lengthleft += newlength;
+ if (!replacement_only) CHECKMEMCPY(subject + ovector[0], oldlength);
+
+ /* A negative return means do not do any more. */
+
+ if (rc < 0) suboptions &= (~PCRE2_SUBSTITUTE_GLOBAL);
+ }
+ }
+
+ /* Save the details of this match. See above for how this data is used. If we
+ matched an empty string, do the magic for global matches. Update the start
+ offset to point to the rest of the subject string. If we re-used an existing
+ match for the first match, switch to the internal match data block. */
+
+ ovecsave[0] = ovector[0];
+ ovecsave[1] = ovector[1];
+ ovecsave[2] = start_offset;
+
+ goptions = (ovector[0] != ovector[1] || ovector[0] > start_offset)? 0 :
+ PCRE2_ANCHORED|PCRE2_NOTEMPTY_ATSTART;
+ start_offset = ovector[1];
+ } while ((suboptions & PCRE2_SUBSTITUTE_GLOBAL) != 0); /* Repeat "do" loop */
+
+/* Copy the rest of the subject unless not required, and terminate the output
+with a binary zero. */
+
+if (!replacement_only)
+ {
+ fraglength = length - start_offset;
+ CHECKMEMCPY(subject + start_offset, fraglength);
+ }
+
+temp[0] = 0;
+CHECKMEMCPY(temp, 1);
+
+/* If overflowed is set it means the PCRE2_SUBSTITUTE_OVERFLOW_LENGTH is set,
+and matching has carried on after a full buffer, in order to compute the length
+needed. Otherwise, an overflow generates an immediate error return. */
+
+if (overflowed)
+ {
+ rc = PCRE2_ERROR_NOMEMORY;
+ *blength = buff_length + extra_needed;
+ }
+
+/* After a successful execution, return the number of substitutions and set the
+length of buffer used, excluding the trailing zero. */
+
+else
+ {
+ rc = subs;
+ *blength = buff_offset - 1;
+ }
+
+EXIT:
+if (internal_match_data != NULL) pcre2_match_data_free(internal_match_data);
+ else match_data->rc = rc;
+return rc;
+
+NOROOM:
+rc = PCRE2_ERROR_NOMEMORY;
+goto EXIT;
+
+BAD:
+rc = PCRE2_ERROR_BADREPLACEMENT;
+goto PTREXIT;
+
+BADESCAPE:
+rc = PCRE2_ERROR_BADREPESCAPE;
+
+PTREXIT:
+*blength = (PCRE2_SIZE)(ptr - replacement);
+goto EXIT;
+}
+
+/* End of pcre2_substitute.c */
diff --git a/contrib/libs/pcre2/src/pcre2_substring.c b/contrib/libs/pcre2/src/pcre2_substring.c
new file mode 100644
index 0000000000..62b509da7b
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_substring.c
@@ -0,0 +1,547 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2018 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+#include "pcre2_internal.h"
+
+
+
+/*************************************************
+* Copy named captured string to given buffer *
+*************************************************/
+
+/* This function copies a single captured substring into a given buffer,
+identifying it by name. If the regex permits duplicate names, the first
+substring that is set is chosen.
+
+Arguments:
+ match_data points to the match data
+ stringname the name of the required substring
+ buffer where to put the substring
+ sizeptr the size of the buffer, updated to the size of the substring
+
+Returns: if successful: zero
+ if not successful, a negative error code:
+ (1) an error from nametable_scan()
+ (2) an error from copy_bynumber()
+ (3) PCRE2_ERROR_UNAVAILABLE: no group is in ovector
+ (4) PCRE2_ERROR_UNSET: all named groups in ovector are unset
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_copy_byname(pcre2_match_data *match_data, PCRE2_SPTR stringname,
+ PCRE2_UCHAR *buffer, PCRE2_SIZE *sizeptr)
+{
+PCRE2_SPTR first, last, entry;
+int failrc, entrysize;
+if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)
+ return PCRE2_ERROR_DFA_UFUNC;
+entrysize = pcre2_substring_nametable_scan(match_data->code, stringname,
+ &first, &last);
+if (entrysize < 0) return entrysize;
+failrc = PCRE2_ERROR_UNAVAILABLE;
+for (entry = first; entry <= last; entry += entrysize)
+ {
+ uint32_t n = GET2(entry, 0);
+ if (n < match_data->oveccount)
+ {
+ if (match_data->ovector[n*2] != PCRE2_UNSET)
+ return pcre2_substring_copy_bynumber(match_data, n, buffer, sizeptr);
+ failrc = PCRE2_ERROR_UNSET;
+ }
+ }
+return failrc;
+}
+
+
+
+/*************************************************
+* Copy numbered captured string to given buffer *
+*************************************************/
+
+/* This function copies a single captured substring into a given buffer,
+identifying it by number.
+
+Arguments:
+ match_data points to the match data
+ stringnumber the number of the required substring
+ buffer where to put the substring
+ sizeptr the size of the buffer, updated to the size of the substring
+
+Returns: if successful: 0
+ if not successful, a negative error code:
+ PCRE2_ERROR_NOMEMORY: buffer too small
+ PCRE2_ERROR_NOSUBSTRING: no such substring
+ PCRE2_ERROR_UNAVAILABLE: ovector too small
+ PCRE2_ERROR_UNSET: substring is not set
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_copy_bynumber(pcre2_match_data *match_data,
+ uint32_t stringnumber, PCRE2_UCHAR *buffer, PCRE2_SIZE *sizeptr)
+{
+int rc;
+PCRE2_SIZE size;
+rc = pcre2_substring_length_bynumber(match_data, stringnumber, &size);
+if (rc < 0) return rc;
+if (size + 1 > *sizeptr) return PCRE2_ERROR_NOMEMORY;
+memcpy(buffer, match_data->subject + match_data->ovector[stringnumber*2],
+ CU2BYTES(size));
+buffer[size] = 0;
+*sizeptr = size;
+return 0;
+}
+
+
+
+/*************************************************
+* Extract named captured string *
+*************************************************/
+
+/* This function copies a single captured substring, identified by name, into
+new memory. If the regex permits duplicate names, the first substring that is
+set is chosen.
+
+Arguments:
+ match_data pointer to match_data
+ stringname the name of the required substring
+ stringptr where to put the pointer to the new memory
+ sizeptr where to put the length of the substring
+
+Returns: if successful: zero
+ if not successful, a negative value:
+ (1) an error from nametable_scan()
+ (2) an error from get_bynumber()
+ (3) PCRE2_ERROR_UNAVAILABLE: no group is in ovector
+ (4) PCRE2_ERROR_UNSET: all named groups in ovector are unset
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_get_byname(pcre2_match_data *match_data,
+ PCRE2_SPTR stringname, PCRE2_UCHAR **stringptr, PCRE2_SIZE *sizeptr)
+{
+PCRE2_SPTR first, last, entry;
+int failrc, entrysize;
+if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)
+ return PCRE2_ERROR_DFA_UFUNC;
+entrysize = pcre2_substring_nametable_scan(match_data->code, stringname,
+ &first, &last);
+if (entrysize < 0) return entrysize;
+failrc = PCRE2_ERROR_UNAVAILABLE;
+for (entry = first; entry <= last; entry += entrysize)
+ {
+ uint32_t n = GET2(entry, 0);
+ if (n < match_data->oveccount)
+ {
+ if (match_data->ovector[n*2] != PCRE2_UNSET)
+ return pcre2_substring_get_bynumber(match_data, n, stringptr, sizeptr);
+ failrc = PCRE2_ERROR_UNSET;
+ }
+ }
+return failrc;
+}
+
+
+
+/*************************************************
+* Extract captured string to new memory *
+*************************************************/
+
+/* This function copies a single captured substring into a piece of new
+memory.
+
+Arguments:
+ match_data points to match data
+ stringnumber the number of the required substring
+ stringptr where to put a pointer to the new memory
+ sizeptr where to put the size of the substring
+
+Returns: if successful: 0
+ if not successful, a negative error code:
+ PCRE2_ERROR_NOMEMORY: failed to get memory
+ PCRE2_ERROR_NOSUBSTRING: no such substring
+ PCRE2_ERROR_UNAVAILABLE: ovector too small
+ PCRE2_ERROR_UNSET: substring is not set
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_get_bynumber(pcre2_match_data *match_data,
+ uint32_t stringnumber, PCRE2_UCHAR **stringptr, PCRE2_SIZE *sizeptr)
+{
+int rc;
+PCRE2_SIZE size;
+PCRE2_UCHAR *yield;
+rc = pcre2_substring_length_bynumber(match_data, stringnumber, &size);
+if (rc < 0) return rc;
+yield = PRIV(memctl_malloc)(sizeof(pcre2_memctl) +
+ (size + 1)*PCRE2_CODE_UNIT_WIDTH, (pcre2_memctl *)match_data);
+if (yield == NULL) return PCRE2_ERROR_NOMEMORY;
+yield = (PCRE2_UCHAR *)(((char *)yield) + sizeof(pcre2_memctl));
+memcpy(yield, match_data->subject + match_data->ovector[stringnumber*2],
+ CU2BYTES(size));
+yield[size] = 0;
+*stringptr = yield;
+*sizeptr = size;
+return 0;
+}
+
+
+
+/*************************************************
+* Free memory obtained by get_substring *
+*************************************************/
+
+/*
+Argument: the result of a previous pcre2_substring_get_byxxx()
+Returns: nothing
+*/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_substring_free(PCRE2_UCHAR *string)
+{
+if (string != NULL)
+ {
+ pcre2_memctl *memctl = (pcre2_memctl *)((char *)string - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+
+
+/*************************************************
+* Get length of a named substring *
+*************************************************/
+
+/* This function returns the length of a named captured substring. If the regex
+permits duplicate names, the first substring that is set is chosen.
+
+Arguments:
+ match_data pointer to match data
+ stringname the name of the required substring
+ sizeptr where to put the length
+
+Returns: 0 if successful, else a negative error number
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_length_byname(pcre2_match_data *match_data,
+ PCRE2_SPTR stringname, PCRE2_SIZE *sizeptr)
+{
+PCRE2_SPTR first, last, entry;
+int failrc, entrysize;
+if (match_data->matchedby == PCRE2_MATCHEDBY_DFA_INTERPRETER)
+ return PCRE2_ERROR_DFA_UFUNC;
+entrysize = pcre2_substring_nametable_scan(match_data->code, stringname,
+ &first, &last);
+if (entrysize < 0) return entrysize;
+failrc = PCRE2_ERROR_UNAVAILABLE;
+for (entry = first; entry <= last; entry += entrysize)
+ {
+ uint32_t n = GET2(entry, 0);
+ if (n < match_data->oveccount)
+ {
+ if (match_data->ovector[n*2] != PCRE2_UNSET)
+ return pcre2_substring_length_bynumber(match_data, n, sizeptr);
+ failrc = PCRE2_ERROR_UNSET;
+ }
+ }
+return failrc;
+}
+
+
+
+/*************************************************
+* Get length of a numbered substring *
+*************************************************/
+
+/* This function returns the length of a captured substring. If the start is
+beyond the end (which can happen when \K is used in an assertion), it sets the
+length to zero.
+
+Arguments:
+ match_data pointer to match data
+ stringnumber the number of the required substring
+ sizeptr where to put the length, if not NULL
+
+Returns: if successful: 0
+ if not successful, a negative error code:
+ PCRE2_ERROR_NOSUBSTRING: no such substring
+ PCRE2_ERROR_UNAVAILABLE: ovector is too small
+ PCRE2_ERROR_UNSET: substring is not set
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_length_bynumber(pcre2_match_data *match_data,
+ uint32_t stringnumber, PCRE2_SIZE *sizeptr)
+{
+PCRE2_SIZE left, right;
+int count = match_data->rc;
+if (count == PCRE2_ERROR_PARTIAL)
+ {
+ if (stringnumber > 0) return PCRE2_ERROR_PARTIAL;
+ count = 0;
+ }
+else if (count < 0) return count; /* Match failed */
+
+if (match_data->matchedby != PCRE2_MATCHEDBY_DFA_INTERPRETER)
+ {
+ if (stringnumber > match_data->code->top_bracket)
+ return PCRE2_ERROR_NOSUBSTRING;
+ if (stringnumber >= match_data->oveccount)
+ return PCRE2_ERROR_UNAVAILABLE;
+ if (match_data->ovector[stringnumber*2] == PCRE2_UNSET)
+ return PCRE2_ERROR_UNSET;
+ }
+else /* Matched using pcre2_dfa_match() */
+ {
+ if (stringnumber >= match_data->oveccount) return PCRE2_ERROR_UNAVAILABLE;
+ if (count != 0 && stringnumber >= (uint32_t)count) return PCRE2_ERROR_UNSET;
+ }
+
+left = match_data->ovector[stringnumber*2];
+right = match_data->ovector[stringnumber*2+1];
+if (sizeptr != NULL) *sizeptr = (left > right)? 0 : right - left;
+return 0;
+}
+
+
+
+/*************************************************
+* Extract all captured strings to new memory *
+*************************************************/
+
+/* This function gets one chunk of memory and builds a list of pointers and all
+the captured substrings in it. A NULL pointer is put on the end of the list.
+The substrings are zero-terminated, but also, if the final argument is
+non-NULL, a list of lengths is also returned. This allows binary data to be
+handled.
+
+Arguments:
+ match_data points to the match data
+ listptr set to point to the list of pointers
+ lengthsptr set to point to the list of lengths (may be NULL)
+
+Returns: if successful: 0
+ if not successful, a negative error code:
+ PCRE2_ERROR_NOMEMORY: failed to get memory,
+ or a match failure code
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_list_get(pcre2_match_data *match_data, PCRE2_UCHAR ***listptr,
+ PCRE2_SIZE **lengthsptr)
+{
+int i, count, count2;
+PCRE2_SIZE size;
+PCRE2_SIZE *lensp;
+pcre2_memctl *memp;
+PCRE2_UCHAR **listp;
+PCRE2_UCHAR *sp;
+PCRE2_SIZE *ovector;
+
+if ((count = match_data->rc) < 0) return count; /* Match failed */
+if (count == 0) count = match_data->oveccount; /* Ovector too small */
+
+count2 = 2*count;
+ovector = match_data->ovector;
+size = sizeof(pcre2_memctl) + sizeof(PCRE2_UCHAR *); /* For final NULL */
+if (lengthsptr != NULL) size += sizeof(PCRE2_SIZE) * count; /* For lengths */
+
+for (i = 0; i < count2; i += 2)
+ {
+ size += sizeof(PCRE2_UCHAR *) + CU2BYTES(1);
+ if (ovector[i+1] > ovector[i]) size += CU2BYTES(ovector[i+1] - ovector[i]);
+ }
+
+memp = PRIV(memctl_malloc)(size, (pcre2_memctl *)match_data);
+if (memp == NULL) return PCRE2_ERROR_NOMEMORY;
+
+*listptr = listp = (PCRE2_UCHAR **)((char *)memp + sizeof(pcre2_memctl));
+lensp = (PCRE2_SIZE *)((char *)listp + sizeof(PCRE2_UCHAR *) * (count + 1));
+
+if (lengthsptr == NULL)
+ {
+ sp = (PCRE2_UCHAR *)lensp;
+ lensp = NULL;
+ }
+else
+ {
+ *lengthsptr = lensp;
+ sp = (PCRE2_UCHAR *)((char *)lensp + sizeof(PCRE2_SIZE) * count);
+ }
+
+for (i = 0; i < count2; i += 2)
+ {
+ size = (ovector[i+1] > ovector[i])? (ovector[i+1] - ovector[i]) : 0;
+
+ /* Size == 0 includes the case when the capture is unset. Avoid adding
+ PCRE2_UNSET to match_data->subject because it overflows, even though with
+ zero size calling memcpy() is harmless. */
+
+ if (size != 0) memcpy(sp, match_data->subject + ovector[i], CU2BYTES(size));
+ *listp++ = sp;
+ if (lensp != NULL) *lensp++ = size;
+ sp += size;
+ *sp++ = 0;
+ }
+
+*listp = NULL;
+return 0;
+}
+
+
+
+/*************************************************
+* Free memory obtained by substring_list_get *
+*************************************************/
+
+/*
+Argument: the result of a previous pcre2_substring_list_get()
+Returns: nothing
+*/
+
+PCRE2_EXP_DEFN void PCRE2_CALL_CONVENTION
+pcre2_substring_list_free(PCRE2_SPTR *list)
+{
+if (list != NULL)
+ {
+ pcre2_memctl *memctl = (pcre2_memctl *)((char *)list - sizeof(pcre2_memctl));
+ memctl->free(memctl, memctl->memory_data);
+ }
+}
+
+
+
+/*************************************************
+* Find (multiple) entries for named string *
+*************************************************/
+
+/* This function scans the nametable for a given name, using binary chop. It
+returns either two pointers to the entries in the table, or, if no pointers are
+given, the number of a unique group with the given name. If duplicate names are
+permitted, and the name is not unique, an error is generated.
+
+Arguments:
+ code the compiled regex
+ stringname the name whose entries required
+ firstptr where to put the pointer to the first entry
+ lastptr where to put the pointer to the last entry
+
+Returns: PCRE2_ERROR_NOSUBSTRING if the name is not found
+ otherwise, if firstptr and lastptr are NULL:
+ a group number for a unique substring
+ else PCRE2_ERROR_NOUNIQUESUBSTRING
+ otherwise:
+ the length of each entry, having set firstptr and lastptr
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_nametable_scan(const pcre2_code *code, PCRE2_SPTR stringname,
+ PCRE2_SPTR *firstptr, PCRE2_SPTR *lastptr)
+{
+uint16_t bot = 0;
+uint16_t top = code->name_count;
+uint16_t entrysize = code->name_entry_size;
+PCRE2_SPTR nametable = (PCRE2_SPTR)((char *)code + sizeof(pcre2_real_code));
+
+while (top > bot)
+ {
+ uint16_t mid = (top + bot) / 2;
+ PCRE2_SPTR entry = nametable + entrysize*mid;
+ int c = PRIV(strcmp)(stringname, entry + IMM2_SIZE);
+ if (c == 0)
+ {
+ PCRE2_SPTR first;
+ PCRE2_SPTR last;
+ PCRE2_SPTR lastentry;
+ lastentry = nametable + entrysize * (code->name_count - 1);
+ first = last = entry;
+ while (first > nametable)
+ {
+ if (PRIV(strcmp)(stringname, (first - entrysize + IMM2_SIZE)) != 0) break;
+ first -= entrysize;
+ }
+ while (last < lastentry)
+ {
+ if (PRIV(strcmp)(stringname, (last + entrysize + IMM2_SIZE)) != 0) break;
+ last += entrysize;
+ }
+ if (firstptr == NULL) return (first == last)?
+ (int)GET2(entry, 0) : PCRE2_ERROR_NOUNIQUESUBSTRING;
+ *firstptr = first;
+ *lastptr = last;
+ return entrysize;
+ }
+ if (c > 0) bot = mid + 1; else top = mid;
+ }
+
+return PCRE2_ERROR_NOSUBSTRING;
+}
+
+
+/*************************************************
+* Find number for named string *
+*************************************************/
+
+/* This function is a convenience wrapper for pcre2_substring_nametable_scan()
+when it is known that names are unique. If there are duplicate names, it is not
+defined which number is returned.
+
+Arguments:
+ code the compiled regex
+ stringname the name whose number is required
+
+Returns: the number of the named parenthesis, or a negative number
+ PCRE2_ERROR_NOSUBSTRING if not found
+ PCRE2_ERROR_NOUNIQUESUBSTRING if not unique
+*/
+
+PCRE2_EXP_DEFN int PCRE2_CALL_CONVENTION
+pcre2_substring_number_from_name(const pcre2_code *code,
+ PCRE2_SPTR stringname)
+{
+return pcre2_substring_nametable_scan(code, stringname, NULL, NULL);
+}
+
+/* End of pcre2_substring.c */
diff --git a/contrib/libs/pcre2/src/pcre2_tables.c b/contrib/libs/pcre2/src/pcre2_tables.c
new file mode 100644
index 0000000000..701a4f7c01
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_tables.c
@@ -0,0 +1,234 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2021 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains some fixed tables that are used by more than one of the
+PCRE2 code modules. The tables are also #included by the pcre2test program,
+which uses macros to change their names from _pcre2_xxx to xxxx, thereby
+avoiding name clashes with the library. In this case, PCRE2_PCRE2TEST is
+defined. */
+
+#ifndef PCRE2_PCRE2TEST /* We're compiling the library */
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+#include "pcre2_internal.h"
+#endif /* PCRE2_PCRE2TEST */
+
+/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that
+the definition is next to the definition of the opcodes in pcre2_internal.h.
+This is mode-dependent, so it is skipped when this file is included by
+pcre2test. */
+
+#ifndef PCRE2_PCRE2TEST
+const uint8_t PRIV(OP_lengths)[] = { OP_LENGTHS };
+#endif
+
+/* Tables of horizontal and vertical whitespace characters, suitable for
+adding to classes. */
+
+const uint32_t PRIV(hspace_list)[] = { HSPACE_LIST };
+const uint32_t PRIV(vspace_list)[] = { VSPACE_LIST };
+
+/* These tables are the pairs of delimiters that are valid for callout string
+arguments. For each starting delimiter there must be a matching ending
+delimiter, which in fact is different only for bracket-like delimiters. */
+
+const uint32_t PRIV(callout_start_delims)[] = {
+ CHAR_GRAVE_ACCENT, CHAR_APOSTROPHE, CHAR_QUOTATION_MARK,
+ CHAR_CIRCUMFLEX_ACCENT, CHAR_PERCENT_SIGN, CHAR_NUMBER_SIGN,
+ CHAR_DOLLAR_SIGN, CHAR_LEFT_CURLY_BRACKET, 0 };
+
+const uint32_t PRIV(callout_end_delims[]) = {
+ CHAR_GRAVE_ACCENT, CHAR_APOSTROPHE, CHAR_QUOTATION_MARK,
+ CHAR_CIRCUMFLEX_ACCENT, CHAR_PERCENT_SIGN, CHAR_NUMBER_SIGN,
+ CHAR_DOLLAR_SIGN, CHAR_RIGHT_CURLY_BRACKET, 0 };
+
+
+/*************************************************
+* Tables for UTF-8 support *
+*************************************************/
+
+/* These tables are required by pcre2test in 16- or 32-bit mode, as well
+as for the library in 8-bit mode, because pcre2test uses UTF-8 internally for
+handling wide characters. */
+
+#if defined PCRE2_PCRE2TEST || \
+ (defined SUPPORT_UNICODE && \
+ defined PCRE2_CODE_UNIT_WIDTH && \
+ PCRE2_CODE_UNIT_WIDTH == 8)
+
+/* These are the breakpoints for different numbers of bytes in a UTF-8
+character. */
+
+const int PRIV(utf8_table1)[] =
+ { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff};
+
+const int PRIV(utf8_table1_size) = sizeof(PRIV(utf8_table1)) / sizeof(int);
+
+/* These are the indicator bits and the mask for the data bits to set in the
+first byte of a character, indexed by the number of additional bytes. */
+
+const int PRIV(utf8_table2)[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
+const int PRIV(utf8_table3)[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+/* Table of the number of extra bytes, indexed by the first byte masked with
+0x3f. The highest number for a valid UTF-8 first byte is in fact 0x3d. */
+
+const uint8_t PRIV(utf8_table4)[] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
+
+#endif /* UTF-8 support needed */
+
+/* Tables concerned with Unicode properties are relevant only when Unicode
+support is enabled. See also the pcre2_ucptables.c file, which is generated by
+a Python script from Unicode data files. */
+
+#ifdef SUPPORT_UNICODE
+
+/* Table to translate from particular type value to the general value. */
+
+const uint32_t PRIV(ucp_gentype)[] = {
+ ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */
+ ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */
+ ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */
+ ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */
+ ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */
+ ucp_P, ucp_P, /* Ps, Po */
+ ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */
+ ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */
+};
+
+/* This table encodes the rules for finding the end of an extended grapheme
+cluster. Every code point has a grapheme break property which is one of the
+ucp_gbXX values defined in pcre2_ucp.h. These changed between Unicode versions
+10 and 11. The 2-dimensional table is indexed by the properties of two adjacent
+code points. The left property selects a word from the table, and the right
+property selects a bit from that word like this:
+
+ PRIV(ucp_gbtable)[left-property] & (1u << right-property)
+
+The value is non-zero if a grapheme break is NOT permitted between the relevant
+two code points. The breaking rules are as follows:
+
+1. Break at the start and end of text (pretty obviously).
+
+2. Do not break between a CR and LF; otherwise, break before and after
+ controls.
+
+3. Do not break Hangul syllable sequences, the rules for which are:
+
+ L may be followed by L, V, LV or LVT
+ LV or V may be followed by V or T
+ LVT or T may be followed by T
+
+4. Do not break before extending characters or zero-width-joiner (ZWJ).
+
+The following rules are only for extended grapheme clusters (but that's what we
+are implementing).
+
+5. Do not break before SpacingMarks.
+
+6. Do not break after Prepend characters.
+
+7. Do not break within emoji modifier sequences or emoji zwj sequences. That
+ is, do not break between characters with the Extended_Pictographic property.
+ Extend and ZWJ characters are allowed between the characters; this cannot be
+ represented in this table, the code has to deal with it.
+
+8. Do not break within emoji flag sequences. That is, do not break between
+ regional indicator (RI) symbols if there are an odd number of RI characters
+ before the break point. This table encodes "join RI characters"; the code
+ has to deal with checking for previous adjoining RIs.
+
+9. Otherwise, break everywhere.
+*/
+
+#define ESZ (1<<ucp_gbExtend)|(1<<ucp_gbSpacingMark)|(1<<ucp_gbZWJ)
+
+const uint32_t PRIV(ucp_gbtable)[] = {
+ (1u<<ucp_gbLF), /* 0 CR */
+ 0, /* 1 LF */
+ 0, /* 2 Control */
+ ESZ, /* 3 Extend */
+ ESZ|(1u<<ucp_gbPrepend)| /* 4 Prepend */
+ (1u<<ucp_gbL)|(1u<<ucp_gbV)|(1u<<ucp_gbT)|
+ (1u<<ucp_gbLV)|(1u<<ucp_gbLVT)|(1u<<ucp_gbOther)|
+ (1u<<ucp_gbRegional_Indicator),
+ ESZ, /* 5 SpacingMark */
+ ESZ|(1u<<ucp_gbL)|(1u<<ucp_gbV)|(1u<<ucp_gbLV)| /* 6 L */
+ (1u<<ucp_gbLVT),
+ ESZ|(1u<<ucp_gbV)|(1u<<ucp_gbT), /* 7 V */
+ ESZ|(1u<<ucp_gbT), /* 8 T */
+ ESZ|(1u<<ucp_gbV)|(1u<<ucp_gbT), /* 9 LV */
+ ESZ|(1u<<ucp_gbT), /* 10 LVT */
+ (1u<<ucp_gbRegional_Indicator), /* 11 Regional Indicator */
+ ESZ, /* 12 Other */
+ ESZ, /* 13 ZWJ */
+ ESZ|(1u<<ucp_gbExtended_Pictographic) /* 14 Extended Pictographic */
+};
+
+#undef ESZ
+
+#ifdef SUPPORT_JIT
+/* This table reverses PRIV(ucp_gentype). We can save the cost
+of a memory load. */
+
+const int PRIV(ucp_typerange)[] = {
+ ucp_Cc, ucp_Cs,
+ ucp_Ll, ucp_Lu,
+ ucp_Mc, ucp_Mn,
+ ucp_Nd, ucp_No,
+ ucp_Pc, ucp_Ps,
+ ucp_Sc, ucp_So,
+ ucp_Zl, ucp_Zs,
+};
+#endif /* SUPPORT_JIT */
+
+/* Finally, include the tables that are auto-generated from the Unicode data
+files. */
+
+#include "pcre2_ucptables.c"
+
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_tables.c */
diff --git a/contrib/libs/pcre2/src/pcre2_ucd.c b/contrib/libs/pcre2/src/pcre2_ucd.c
new file mode 100644
index 0000000000..496c231915
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_ucd.c
@@ -0,0 +1,5396 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+This module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!
+Instead, modify the maint/GenerateUcd.py script and run it to generate
+a new version of this code.
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This file contains tables of Unicode properties that are extracted from
+Unicode data files. See the comments at the start of maint/GenerateUcd.py for
+details.
+
+As well as being part of the PCRE2 library, this file is #included by the
+pcre2test program, which redefines the PRIV macro to change table names from
+_pcre2_xxx to xxxx, thereby avoiding name clashes with the library. At present,
+just one of these tables is actually needed. When compiling the library, some
+headers are needed. */
+
+#ifndef PCRE2_PCRE2TEST
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+#include "pcre2_internal.h"
+#endif /* PCRE2_PCRE2TEST */
+
+/* The tables herein are needed only when UCP support is built, and in PCRE2
+that happens automatically with UTF support. This module should not be
+referenced otherwise, so it should not matter whether it is compiled or not.
+However a comment was received about space saving - maybe the guy linked all
+the modules rather than using a library - so we include a condition to cut out
+the tables when not needed. But don't leave a totally empty module because some
+compilers barf at that. Instead, just supply some small dummy tables. */
+
+#ifndef SUPPORT_UNICODE
+const ucd_record PRIV(ucd_records)[] = {{0,0,0,0,0,0,0 }};
+const uint16_t PRIV(ucd_stage1)[] = {0};
+const uint16_t PRIV(ucd_stage2)[] = {0};
+const uint32_t PRIV(ucd_caseless_sets)[] = {0};
+#else
+
+/* Total size: 111116 bytes, block size: 128. */
+
+const char *PRIV(unicode_version) = "14.0.0";
+
+/* When recompiling tables with a new Unicode version, please check the types
+in this structure definition with those in pcre2_internal.h (the actual field
+names will be different).
+
+typedef struct {
+uint8_t property_0;
+uint8_t property_1;
+uint8_t property_2;
+uint8_t property_3;
+int32_t property_4;
+uint16_t property_5;
+uint16_t property_6;
+} ucd_record;
+*/
+
+/* If the 32-bit library is run in non-32-bit mode, character values greater
+than 0x10ffff may be encountered. For these we set up a special record. */
+
+#if PCRE2_CODE_UNIT_WIDTH == 32
+const ucd_record PRIV(dummy_ucd_record)[] = {{
+ ucp_Unknown, /* script */
+ ucp_Cn, /* type unassigned */
+ ucp_gbOther, /* grapheme break property */
+ 0, /* case set */
+ 0, /* other case */
+ 0 | (ucp_bidiL << UCD_BIDICLASS_SHIFT), /* script extension and bidi class */
+ 0, /* bool properties offset */
+ }};
+#endif
+
+/* This table contains lists of characters that are caseless sets of
+more than one character. Each list is terminated by NOTACHAR. */
+
+const uint32_t PRIV(ucd_caseless_sets)[] = {
+ NOTACHAR,
+ 0x0053, 0x0073, 0x017f, NOTACHAR,
+ 0x01c4, 0x01c5, 0x01c6, NOTACHAR,
+ 0x01c7, 0x01c8, 0x01c9, NOTACHAR,
+ 0x01ca, 0x01cb, 0x01cc, NOTACHAR,
+ 0x01f1, 0x01f2, 0x01f3, NOTACHAR,
+ 0x0345, 0x0399, 0x03b9, 0x1fbe, NOTACHAR,
+ 0x00b5, 0x039c, 0x03bc, NOTACHAR,
+ 0x03a3, 0x03c2, 0x03c3, NOTACHAR,
+ 0x0392, 0x03b2, 0x03d0, NOTACHAR,
+ 0x0398, 0x03b8, 0x03d1, 0x03f4, NOTACHAR,
+ 0x03a6, 0x03c6, 0x03d5, NOTACHAR,
+ 0x03a0, 0x03c0, 0x03d6, NOTACHAR,
+ 0x039a, 0x03ba, 0x03f0, NOTACHAR,
+ 0x03a1, 0x03c1, 0x03f1, NOTACHAR,
+ 0x0395, 0x03b5, 0x03f5, NOTACHAR,
+ 0x0412, 0x0432, 0x1c80, NOTACHAR,
+ 0x0414, 0x0434, 0x1c81, NOTACHAR,
+ 0x041e, 0x043e, 0x1c82, NOTACHAR,
+ 0x0421, 0x0441, 0x1c83, NOTACHAR,
+ 0x0422, 0x0442, 0x1c84, 0x1c85, NOTACHAR,
+ 0x042a, 0x044a, 0x1c86, NOTACHAR,
+ 0x0462, 0x0463, 0x1c87, NOTACHAR,
+ 0x1e60, 0x1e61, 0x1e9b, NOTACHAR,
+ 0x03a9, 0x03c9, 0x2126, NOTACHAR,
+ 0x004b, 0x006b, 0x212a, NOTACHAR,
+ 0x00c5, 0x00e5, 0x212b, NOTACHAR,
+ 0x1c88, 0xa64a, 0xa64b, NOTACHAR,
+};
+
+/* When #included in pcre2test, we don't need the table of digit sets, nor the
+the large main UCD tables. */
+
+#ifndef PCRE2_PCRE2TEST
+
+/* This table lists the code points for the '9' characters in each set of
+decimal digits. It is used to ensure that all the digits in a script run come
+from the same set. */
+
+const uint32_t PRIV(ucd_digit_sets)[] = {
+ 66, /* Number of subsequent values */
+ 0x00039, 0x00669, 0x006f9, 0x007c9, 0x0096f, 0x009ef, 0x00a6f, 0x00aef,
+ 0x00b6f, 0x00bef, 0x00c6f, 0x00cef, 0x00d6f, 0x00def, 0x00e59, 0x00ed9,
+ 0x00f29, 0x01049, 0x01099, 0x017e9, 0x01819, 0x0194f, 0x019d9, 0x01a89,
+ 0x01a99, 0x01b59, 0x01bb9, 0x01c49, 0x01c59, 0x0a629, 0x0a8d9, 0x0a909,
+ 0x0a9d9, 0x0a9f9, 0x0aa59, 0x0abf9, 0x0ff19, 0x104a9, 0x10d39, 0x1106f,
+ 0x110f9, 0x1113f, 0x111d9, 0x112f9, 0x11459, 0x114d9, 0x11659, 0x116c9,
+ 0x11739, 0x118e9, 0x11959, 0x11c59, 0x11d59, 0x11da9, 0x16a69, 0x16ac9,
+ 0x16b59, 0x1d7d7, 0x1d7e1, 0x1d7eb, 0x1d7f5, 0x1d7ff, 0x1e149, 0x1e2f9,
+ 0x1e959, 0x1fbf9,
+};
+
+/* This vector is a list of script bitsets for the Script Extension property.
+The number of 32-bit words in each bitset is #defined in pcre2_ucp.h as
+ucd_script_sets_item_size. */
+
+const uint32_t PRIV(ucd_script_sets)[] = {
+ 0x00000000u, 0x00000000u, 0x00000000u,
+ 0x00000080u, 0x00000000u, 0x00000000u,
+ 0x00000040u, 0x00000000u, 0x00000000u,
+ 0x00000000u, 0x00004000u, 0x00000000u,
+ 0x00000002u, 0x00000000u, 0x00000000u,
+ 0x00800000u, 0x00000000u, 0x00000000u,
+ 0x00000001u, 0x00000000u, 0x00000000u,
+ 0x00000000u, 0x00000000u, 0x00000001u,
+ 0x00000010u, 0x00000000u, 0x00000000u,
+ 0x00000008u, 0x00000004u, 0x00000000u,
+ 0x00000008u, 0x40000000u, 0x00000000u,
+ 0x00000008u, 0x00000040u, 0x00000000u,
+ 0x00000018u, 0x00000000u, 0x00000000u,
+ 0x00000028u, 0x00000000u, 0x00000000u,
+ 0x000000c0u, 0x00000000u, 0x00000000u,
+ 0x00c00000u, 0x00000000u, 0x00000000u,
+ 0x00000000u, 0x00000102u, 0x00000000u,
+ 0x80000000u, 0x00000001u, 0x00000000u,
+ 0x00000004u, 0x00000008u, 0x00000000u,
+ 0x00000005u, 0x00000000u, 0x00000000u,
+ 0x00000004u, 0x00200000u, 0x00000000u,
+ 0x00000014u, 0x00000000u, 0x00000000u,
+ 0x00000040u, 0x00008000u, 0x00000000u,
+ 0x00000040u, 0x00000000u, 0x00000001u,
+ 0x00000040u, 0x00001000u, 0x00000000u,
+ 0x00000840u, 0x00000000u, 0x00000000u,
+ 0x00020001u, 0x00000000u, 0x00000000u,
+ 0x00000800u, 0x00008000u, 0x00000000u,
+ 0x00000200u, 0x00010000u, 0x00000000u,
+ 0x00000100u, 0x02000000u, 0x00000000u,
+ 0x00800001u, 0x00000000u, 0x00000000u,
+ 0x00300000u, 0x00000000u, 0x00000000u,
+ 0x00002000u, 0x00000000u, 0x00000001u,
+ 0x00080001u, 0x00000000u, 0x00000000u,
+ 0x00000000u, 0x00080000u, 0x00000008u,
+ 0x00080000u, 0x00000020u, 0x00000000u,
+ 0x00000038u, 0x00000000u, 0x00000000u,
+ 0x00000028u, 0x00000000u, 0x00000002u,
+ 0x00000080u, 0x00000810u, 0x00000000u,
+ 0x40010000u, 0x00000800u, 0x00000000u,
+ 0x80000000u, 0x00000001u, 0x00000004u,
+ 0x80000000u, 0x00020001u, 0x00000000u,
+ 0x00002040u, 0x00008000u, 0x00000000u,
+ 0x00000041u, 0x00008000u, 0x00000000u,
+ 0x00b00000u, 0x00000000u, 0x00000000u,
+ 0x00010001u, 0x00000080u, 0x00000000u,
+ 0x000020c0u, 0x00008000u, 0x00000000u,
+ 0x1e000000u, 0x00000000u, 0x00000000u,
+ 0x00000040u, 0x10040200u, 0x00000000u,
+ 0x00f40000u, 0x00000000u, 0x00000000u,
+ 0x00000038u, 0x40000040u, 0x00000002u,
+ 0x01f40000u, 0x00000000u, 0x00000000u,
+ 0x00007c40u, 0x00000000u, 0x00000000u,
+ 0x00000038u, 0x44000040u, 0x00000002u,
+ 0x000034c0u, 0x01008000u, 0x00000001u,
+ 0x00000018u, 0xc4480400u, 0x00000008u,
+ 0x00000340u, 0x11952200u, 0x00000000u,
+ 0x00007fc1u, 0x01008000u, 0x00000000u,
+ 0x00007fc1u, 0x01009000u, 0x00000000u,
+ 0x00002340u, 0x11952200u, 0x00000001u,
+ 0x00006340u, 0x11952200u, 0x00000001u,
+ 0x0000ffc0u, 0x3984a010u, 0x00000001u,
+ 0x2000ffc0u, 0x3984a010u, 0x00000001u,
+};
+
+/* This vector is a list of bitsets for Boolean properties. The number of
+32_bit words in each bitset is #defined as ucd_boolprop_sets_item_size in
+pcre2_ucp.h. */
+
+const uint32_t PRIV(ucd_boolprop_sets)[] = {
+ 0x00000000u, 0x00000000u,
+ 0x00000001u, 0x00000000u,
+ 0x00000001u, 0x00020040u,
+ 0x00800001u, 0x00020040u,
+ 0x00800001u, 0x00002820u,
+ 0x00800001u, 0x00000120u,
+ 0x00830001u, 0x00000020u,
+ 0x00800001u, 0x00000020u,
+ 0x00800021u, 0x00000120u,
+ 0x00800011u, 0x00000020u,
+ 0x00800001u, 0x00000028u,
+ 0x00800001u, 0x00002020u,
+ 0x00801001u, 0x00000020u,
+ 0x00800021u, 0x00002820u,
+ 0x24830003u, 0x00040000u,
+ 0x00800021u, 0x00002020u,
+ 0x00800011u, 0x00000028u,
+ 0x648003c7u, 0x000c8000u,
+ 0x608003c5u, 0x000c8000u,
+ 0x00808021u, 0x00000028u,
+ 0x20800001u, 0x00040000u,
+ 0x00808021u, 0x00000020u,
+ 0x64800d47u, 0x000c0004u,
+ 0x60800d45u, 0x000c0004u,
+ 0x60800d45u, 0x000c1004u,
+ 0x00000000u, 0x00020040u,
+ 0x00800000u, 0x00020000u,
+ 0x00800000u, 0x00000020u,
+ 0x00808020u, 0x00000000u,
+ 0x00a10000u, 0x00000020u,
+ 0x60800044u, 0x000c0004u,
+ 0x00800010u, 0x00000120u,
+ 0x00800000u, 0x00000028u,
+ 0x00002020u, 0x00000000u,
+ 0x00800000u, 0x00000000u,
+ 0x60800dc4u, 0x000c0004u,
+ 0x20c08020u, 0x00040000u,
+ 0x608003c4u, 0x000c8000u,
+ 0x60800d44u, 0x000c0004u,
+ 0x60800d44u, 0x000c1004u,
+ 0x60804dc4u, 0x000c0004u,
+ 0x60800004u, 0x000c0000u,
+ 0x608007c4u, 0x000c8000u,
+ 0x60800bc4u, 0x000c0000u,
+ 0x60808064u, 0x000c0004u,
+ 0x60808064u, 0x000c1004u,
+ 0x60808024u, 0x000c0000u,
+ 0x60c08024u, 0x000c0000u,
+ 0x21008020u, 0x00040000u,
+ 0x21008de4u, 0x00040004u,
+ 0x21002020u, 0x00040000u,
+ 0x21000020u, 0x00040000u,
+ 0x60808064u, 0x00000004u,
+ 0x00800000u, 0x00002000u,
+ 0x20800020u, 0x00042000u,
+ 0x60800dc4u, 0x000c000cu,
+ 0x60800044u, 0x000c8008u,
+ 0x60800044u, 0x000c8000u,
+ 0x608003c4u, 0x000c8008u,
+ 0x00800000u, 0x00000008u,
+ 0x01000020u, 0x00000000u,
+ 0x00800020u, 0x00000000u,
+ 0x00800000u, 0x00002800u,
+ 0x00801000u, 0x00000000u,
+ 0x21008024u, 0x00040000u,
+ 0x21000024u, 0x00040000u,
+ 0x00000020u, 0x00000080u,
+ 0x00002028u, 0x00000000u,
+ 0x60c00024u, 0x000c0000u,
+ 0x20800000u, 0x00040000u,
+ 0x60804004u, 0x000c0000u,
+ 0x60800024u, 0x000c0000u,
+ 0x20800004u, 0x00040000u,
+ 0x23008020u, 0x00040000u,
+ 0x21000004u, 0x00040000u,
+ 0x21408020u, 0x00040000u,
+ 0x60800004u, 0x00040000u,
+ 0x23000024u, 0x00040000u,
+ 0x60800004u, 0x000c0002u,
+ 0x00800010u, 0x00000000u,
+ 0x20808000u, 0x00040000u,
+ 0x21004024u, 0x00040000u,
+ 0x20808004u, 0x00040000u,
+ 0x60800944u, 0x000c0004u,
+ 0x60802004u, 0x000c0000u,
+ 0x60800344u, 0x000c8000u,
+ 0x22808000u, 0x00040000u,
+ 0x22800000u, 0x00040000u,
+ 0x00c00000u, 0x00000000u,
+ 0x21002020u, 0x00050000u,
+ 0x61000024u, 0x000c0000u,
+ 0x23000020u, 0x00040000u,
+ 0x01008020u, 0x00000000u,
+ 0x21408024u, 0x00040000u,
+ 0x00808000u, 0x00000000u,
+ 0x60800064u, 0x000c0004u,
+ 0x60800044u, 0x000c1004u,
+ 0x60800064u, 0x000c1004u,
+ 0x01002020u, 0x00000001u,
+ 0x00022020u, 0x00000001u,
+ 0x00002028u, 0x00000040u,
+ 0x00801000u, 0x00000020u,
+ 0x00800020u, 0x00000120u,
+ 0x00800000u, 0x00000120u,
+ 0x00800020u, 0x00000020u,
+ 0x00a10000u, 0x00002820u,
+ 0x00800000u, 0x00002820u,
+ 0x20800000u, 0x00040008u,
+ 0x00800010u, 0x00000020u,
+ 0x00002020u, 0x00000008u,
+ 0x00002000u, 0x00000000u,
+ 0x00006020u, 0x00000000u,
+ 0x00801000u, 0x00000008u,
+ 0x00800010u, 0x00000008u,
+ 0x21000020u, 0x00040008u,
+ 0x01020020u, 0x00000000u,
+ 0x60800044u, 0x000c000cu,
+ 0x60800000u, 0x000c0008u,
+ 0x00a10000u, 0x00000000u,
+ 0x60800000u, 0x000c0000u,
+ 0x60800004u, 0x000c0008u,
+ 0x60a10044u, 0x000c0004u,
+ 0x60800044u, 0x000c100cu,
+ 0x00a10000u, 0x00000028u,
+ 0x00800010u, 0x00000028u,
+ 0x00801000u, 0x00000028u,
+ 0x00b10000u, 0x00000020u,
+ 0x00804010u, 0x00000020u,
+ 0x00a00000u, 0x00000020u,
+ 0x00000000u, 0x00000020u,
+ 0x008003c4u, 0x00008000u,
+ 0x00a103c4u, 0x00008000u,
+ 0x00800d44u, 0x00000004u,
+ 0x00b10000u, 0x00000028u,
+ 0x00a00000u, 0x00000028u,
+ 0x00a90000u, 0x00000020u,
+ 0x00b90000u, 0x00000020u,
+ 0x00808024u, 0x00000020u,
+ 0x00800000u, 0x00002020u,
+ 0x00800000u, 0x00000200u,
+ 0x08800000u, 0x00000000u,
+ 0x10800000u, 0x00000000u,
+ 0xe0800004u, 0x000c0000u,
+ 0x21008000u, 0x00040000u,
+ 0x00a11000u, 0x00000020u,
+ 0x60808020u, 0x00000000u,
+ 0xe0800004u, 0x000c4000u,
+ 0x60808004u, 0x000c0000u,
+ 0x60800004u, 0x00000000u,
+ 0x00000000u, 0x00000010u,
+ 0x21022020u, 0x00050000u,
+ 0x00800000u, 0x00000100u,
+ 0x00800020u, 0x00002800u,
+ 0x00800020u, 0x00002000u,
+ 0x00800020u, 0x00000100u,
+ 0x24800000u, 0x00040000u,
+ 0x648003c4u, 0x000c8000u,
+ 0x00808020u, 0x00000008u,
+ 0x64800d44u, 0x000c0004u,
+ 0x00800010u, 0x00000100u,
+ 0x61008024u, 0x00040000u,
+ 0x00000020u, 0x00000000u,
+ 0x60c00004u, 0x000c0000u,
+ 0x21400020u, 0x00040000u,
+ 0xa1000020u, 0x00040000u,
+ 0x21000000u, 0x00040000u,
+ 0x00a00000u, 0x00000000u,
+ 0x00b10000u, 0x00000000u,
+ 0x00200000u, 0x00000000u,
+ 0x00800044u, 0x00008000u,
+ 0x00a10044u, 0x00008000u,
+ 0x00930000u, 0x00000400u,
+ 0x00b90000u, 0x00000000u,
+ 0x00a90000u, 0x00000000u,
+ 0x00970020u, 0x00000000u,
+ 0x00b30000u, 0x00000000u,
+ 0x01022020u, 0x00000000u,
+};
+
+/* These are the main two-stage UCD tables. The fields in each record are:
+script (8 bits), character type (8 bits), grapheme break property (8 bits),
+offset to multichar other cases or zero (8 bits), offset to other case or zero
+(32 bits, signed), bidi class (5 bits) and script extension (11 bits) packed
+into a 16-bit field, and offset in binary properties table (16 bits). */
+
+const ucd_record PRIV(ucd_records)[] = { /* 16908 bytes, record size 12 */
+ { 69, 0, 2, 0, 0, 6144, 2, }, /* 0 */
+ { 69, 0, 2, 0, 0, 43008, 4, }, /* 1 */
+ { 69, 0, 1, 0, 0, 4096, 4, }, /* 2 */
+ { 69, 0, 2, 0, 0, 45056, 4, }, /* 3 */
+ { 69, 0, 0, 0, 0, 4096, 4, }, /* 4 */
+ { 69, 0, 2, 0, 0, 4096, 2, }, /* 5 */
+ { 69, 0, 2, 0, 0, 43008, 2, }, /* 6 */
+ { 69, 29, 12, 0, 0, 45056, 6, }, /* 7 */
+ { 69, 21, 12, 0, 0, 28672, 8, }, /* 8 */
+ { 69, 21, 12, 0, 0, 28672, 10, }, /* 9 */
+ { 69, 21, 12, 0, 0, 14336, 12, }, /* 10 */
+ { 69, 23, 12, 0, 0, 14336, 14, }, /* 11 */
+ { 69, 21, 12, 0, 0, 14336, 14, }, /* 12 */
+ { 69, 21, 12, 0, 0, 28672, 14, }, /* 13 */
+ { 69, 21, 12, 0, 0, 28672, 16, }, /* 14 */
+ { 69, 22, 12, 0, 0, 28672, 18, }, /* 15 */
+ { 69, 18, 12, 0, 0, 28672, 18, }, /* 16 */
+ { 69, 21, 12, 0, 0, 28672, 12, }, /* 17 */
+ { 69, 25, 12, 0, 0, 12288, 20, }, /* 18 */
+ { 69, 21, 12, 0, 0, 8192, 22, }, /* 19 */
+ { 69, 17, 12, 0, 0, 12288, 24, }, /* 20 */
+ { 69, 21, 12, 0, 0, 8192, 26, }, /* 21 */
+ { 69, 21, 12, 0, 0, 8192, 14, }, /* 22 */
+ { 69, 13, 12, 0, 0, 10240, 28, }, /* 23 */
+ { 69, 21, 12, 0, 0, 8192, 30, }, /* 24 */
+ { 69, 21, 12, 0, 0, 28672, 22, }, /* 25 */
+ { 69, 25, 12, 0, 0, 28672, 32, }, /* 26 */
+ { 69, 25, 12, 0, 0, 28672, 20, }, /* 27 */
+ { 0, 9, 12, 0, 32, 18432, 34, }, /* 28 */
+ { 0, 9, 12, 0, 32, 18432, 36, }, /* 29 */
+ { 0, 9, 12, 100, 32, 18432, 36, }, /* 30 */
+ { 0, 9, 12, 1, 32, 18432, 36, }, /* 31 */
+ { 69, 24, 12, 0, 0, 28672, 38, }, /* 32 */
+ { 69, 16, 12, 0, 0, 28672, 40, }, /* 33 */
+ { 69, 24, 12, 0, 0, 28672, 42, }, /* 34 */
+ { 0, 5, 12, 0, -32, 18432, 44, }, /* 35 */
+ { 0, 5, 12, 0, -32, 18432, 46, }, /* 36 */
+ { 0, 5, 12, 0, -32, 18432, 48, }, /* 37 */
+ { 0, 5, 12, 100, -32, 18432, 46, }, /* 38 */
+ { 0, 5, 12, 1, -32, 18432, 46, }, /* 39 */
+ { 69, 0, 2, 0, 0, 6144, 0, }, /* 40 */
+ { 69, 0, 2, 0, 0, 4096, 50, }, /* 41 */
+ { 69, 29, 12, 0, 0, 8192, 52, }, /* 42 */
+ { 69, 21, 12, 0, 0, 28672, 54, }, /* 43 */
+ { 69, 23, 12, 0, 0, 14336, 54, }, /* 44 */
+ { 69, 26, 12, 0, 0, 28672, 54, }, /* 45 */
+ { 69, 24, 12, 0, 0, 28672, 56, }, /* 46 */
+ { 69, 26, 14, 0, 0, 28672, 58, }, /* 47 */
+ { 0, 7, 12, 0, 0, 18432, 60, }, /* 48 */
+ { 69, 20, 12, 0, 0, 28672, 62, }, /* 49 */
+ { 69, 25, 12, 0, 0, 28672, 64, }, /* 50 */
+ { 69, 1, 2, 0, 0, 6144, 66, }, /* 51 */
+ { 69, 26, 12, 0, 0, 14336, 54, }, /* 52 */
+ { 69, 25, 12, 0, 0, 14336, 64, }, /* 53 */
+ { 69, 15, 12, 0, 0, 10240, 68, }, /* 54 */
+ { 69, 5, 12, 26, 775, 18432, 70, }, /* 55 */
+ { 69, 21, 12, 0, 0, 28672, 72, }, /* 56 */
+ { 69, 19, 12, 0, 0, 28672, 62, }, /* 57 */
+ { 69, 15, 12, 0, 0, 28672, 68, }, /* 58 */
+ { 0, 9, 12, 0, 32, 18432, 74, }, /* 59 */
+ { 0, 9, 12, 104, 32, 18432, 74, }, /* 60 */
+ { 0, 5, 12, 0, 7615, 18432, 70, }, /* 61 */
+ { 0, 5, 12, 0, -32, 18432, 76, }, /* 62 */
+ { 0, 5, 12, 104, -32, 18432, 76, }, /* 63 */
+ { 0, 5, 12, 0, 121, 18432, 76, }, /* 64 */
+ { 0, 9, 12, 0, 1, 18432, 74, }, /* 65 */
+ { 0, 5, 12, 0, -1, 18432, 76, }, /* 66 */
+ { 0, 5, 12, 0, -1, 18432, 78, }, /* 67 */
+ { 0, 9, 12, 0, 0, 18432, 74, }, /* 68 */
+ { 0, 5, 12, 0, 0, 18432, 76, }, /* 69 */
+ { 0, 5, 12, 0, 0, 18432, 60, }, /* 70 */
+ { 0, 5, 12, 0, 0, 18432, 80, }, /* 71 */
+ { 0, 9, 12, 0, -121, 18432, 74, }, /* 72 */
+ { 0, 5, 12, 1, -268, 18432, 70, }, /* 73 */
+ { 0, 5, 12, 0, 195, 18432, 76, }, /* 74 */
+ { 0, 9, 12, 0, 210, 18432, 74, }, /* 75 */
+ { 0, 9, 12, 0, 206, 18432, 74, }, /* 76 */
+ { 0, 9, 12, 0, 205, 18432, 74, }, /* 77 */
+ { 0, 9, 12, 0, 79, 18432, 74, }, /* 78 */
+ { 0, 9, 12, 0, 202, 18432, 74, }, /* 79 */
+ { 0, 9, 12, 0, 203, 18432, 74, }, /* 80 */
+ { 0, 9, 12, 0, 207, 18432, 74, }, /* 81 */
+ { 0, 5, 12, 0, 97, 18432, 76, }, /* 82 */
+ { 0, 9, 12, 0, 211, 18432, 74, }, /* 83 */
+ { 0, 9, 12, 0, 209, 18432, 74, }, /* 84 */
+ { 0, 5, 12, 0, 163, 18432, 76, }, /* 85 */
+ { 0, 9, 12, 0, 213, 18432, 74, }, /* 86 */
+ { 0, 5, 12, 0, 130, 18432, 76, }, /* 87 */
+ { 0, 9, 12, 0, 214, 18432, 74, }, /* 88 */
+ { 0, 9, 12, 0, 218, 18432, 74, }, /* 89 */
+ { 0, 9, 12, 0, 217, 18432, 74, }, /* 90 */
+ { 0, 9, 12, 0, 219, 18432, 74, }, /* 91 */
+ { 0, 7, 12, 0, 0, 18432, 82, }, /* 92 */
+ { 0, 5, 12, 0, 56, 18432, 76, }, /* 93 */
+ { 0, 9, 12, 5, 2, 18432, 84, }, /* 94 */
+ { 0, 8, 12, 5, 1, 18432, 86, }, /* 95 */
+ { 0, 5, 12, 5, -2, 18432, 76, }, /* 96 */
+ { 0, 9, 12, 9, 2, 18432, 84, }, /* 97 */
+ { 0, 8, 12, 9, 1, 18432, 86, }, /* 98 */
+ { 0, 5, 12, 9, -2, 18432, 76, }, /* 99 */
+ { 0, 9, 12, 13, 2, 18432, 84, }, /* 100 */
+ { 0, 8, 12, 13, 1, 18432, 86, }, /* 101 */
+ { 0, 5, 12, 13, -2, 18432, 76, }, /* 102 */
+ { 0, 5, 12, 0, -79, 18432, 76, }, /* 103 */
+ { 0, 9, 12, 17, 2, 18432, 84, }, /* 104 */
+ { 0, 8, 12, 17, 1, 18432, 86, }, /* 105 */
+ { 0, 5, 12, 17, -2, 18432, 76, }, /* 106 */
+ { 0, 9, 12, 0, -97, 18432, 74, }, /* 107 */
+ { 0, 9, 12, 0, -56, 18432, 74, }, /* 108 */
+ { 0, 9, 12, 0, -130, 18432, 74, }, /* 109 */
+ { 0, 9, 12, 0, 10795, 18432, 74, }, /* 110 */
+ { 0, 9, 12, 0, -163, 18432, 74, }, /* 111 */
+ { 0, 9, 12, 0, 10792, 18432, 74, }, /* 112 */
+ { 0, 5, 12, 0, 10815, 18432, 76, }, /* 113 */
+ { 0, 9, 12, 0, -195, 18432, 74, }, /* 114 */
+ { 0, 9, 12, 0, 69, 18432, 74, }, /* 115 */
+ { 0, 9, 12, 0, 71, 18432, 74, }, /* 116 */
+ { 0, 5, 12, 0, 10783, 18432, 76, }, /* 117 */
+ { 0, 5, 12, 0, 10780, 18432, 76, }, /* 118 */
+ { 0, 5, 12, 0, 10782, 18432, 76, }, /* 119 */
+ { 0, 5, 12, 0, -210, 18432, 76, }, /* 120 */
+ { 0, 5, 12, 0, -206, 18432, 76, }, /* 121 */
+ { 0, 5, 12, 0, -205, 18432, 76, }, /* 122 */
+ { 0, 5, 12, 0, -202, 18432, 76, }, /* 123 */
+ { 0, 5, 12, 0, -203, 18432, 76, }, /* 124 */
+ { 0, 5, 12, 0, 42319, 18432, 76, }, /* 125 */
+ { 0, 5, 12, 0, 42315, 18432, 76, }, /* 126 */
+ { 0, 5, 12, 0, -207, 18432, 76, }, /* 127 */
+ { 0, 5, 12, 0, 42280, 18432, 76, }, /* 128 */
+ { 0, 5, 12, 0, 42308, 18432, 76, }, /* 129 */
+ { 0, 5, 12, 0, -209, 18432, 78, }, /* 130 */
+ { 0, 5, 12, 0, -211, 18432, 76, }, /* 131 */
+ { 0, 5, 12, 0, 10743, 18432, 76, }, /* 132 */
+ { 0, 5, 12, 0, 42305, 18432, 76, }, /* 133 */
+ { 0, 5, 12, 0, 10749, 18432, 76, }, /* 134 */
+ { 0, 5, 12, 0, -213, 18432, 76, }, /* 135 */
+ { 0, 5, 12, 0, -214, 18432, 76, }, /* 136 */
+ { 0, 5, 12, 0, 10727, 18432, 76, }, /* 137 */
+ { 0, 5, 12, 0, -218, 18432, 76, }, /* 138 */
+ { 0, 5, 12, 0, 42307, 18432, 76, }, /* 139 */
+ { 0, 5, 12, 0, 42282, 18432, 76, }, /* 140 */
+ { 0, 5, 12, 0, -69, 18432, 76, }, /* 141 */
+ { 0, 5, 12, 0, -217, 18432, 76, }, /* 142 */
+ { 0, 5, 12, 0, -71, 18432, 76, }, /* 143 */
+ { 0, 5, 12, 0, -219, 18432, 76, }, /* 144 */
+ { 0, 5, 12, 0, 42261, 18432, 78, }, /* 145 */
+ { 0, 5, 12, 0, 42258, 18432, 76, }, /* 146 */
+ { 0, 6, 12, 0, 0, 18432, 88, }, /* 147 */
+ { 0, 6, 12, 0, 0, 18432, 90, }, /* 148 */
+ { 69, 6, 12, 0, 0, 28672, 92, }, /* 149 */
+ { 69, 6, 12, 0, 0, 18432, 92, }, /* 150 */
+ { 69, 6, 12, 0, 0, 18432, 88, }, /* 151 */
+ { 69, 6, 12, 0, 0, 18432, 94, }, /* 152 */
+ { 22, 24, 12, 0, 0, 28672, 56, }, /* 153 */
+ { 84, 12, 3, 0, 0, 26624, 96, }, /* 154 */
+ { 84, 12, 3, 0, 0, 26636, 96, }, /* 155 */
+ { 84, 12, 3, 21, 116, 26636, 98, }, /* 156 */
+ { 84, 12, 3, 0, 0, 26624, 100, }, /* 157 */
+ { 84, 12, 3, 0, 0, 26624, 102, }, /* 158 */
+ { 84, 12, 3, 0, 0, 26642, 102, }, /* 159 */
+ { 1, 9, 12, 0, 1, 18432, 74, }, /* 160 */
+ { 1, 5, 12, 0, -1, 18432, 76, }, /* 161 */
+ { 1, 24, 12, 0, 0, 28672, 56, }, /* 162 */
+ { 68, 2, 12, 0, 0, 18432, 0, }, /* 163 */
+ { 1, 6, 12, 0, 0, 18432, 104, }, /* 164 */
+ { 1, 5, 12, 0, 130, 18432, 76, }, /* 165 */
+ { 69, 21, 12, 0, 0, 28672, 106, }, /* 166 */
+ { 1, 9, 12, 0, 116, 18432, 74, }, /* 167 */
+ { 1, 9, 12, 0, 38, 18432, 74, }, /* 168 */
+ { 69, 21, 12, 0, 0, 28672, 108, }, /* 169 */
+ { 1, 9, 12, 0, 37, 18432, 74, }, /* 170 */
+ { 1, 9, 12, 0, 64, 18432, 74, }, /* 171 */
+ { 1, 9, 12, 0, 63, 18432, 74, }, /* 172 */
+ { 1, 5, 12, 0, 0, 18432, 76, }, /* 173 */
+ { 1, 9, 12, 0, 32, 18432, 74, }, /* 174 */
+ { 1, 9, 12, 34, 32, 18432, 74, }, /* 175 */
+ { 1, 9, 12, 59, 32, 18432, 74, }, /* 176 */
+ { 1, 9, 12, 38, 32, 18432, 74, }, /* 177 */
+ { 1, 9, 12, 21, 32, 18432, 74, }, /* 178 */
+ { 1, 9, 12, 51, 32, 18432, 74, }, /* 179 */
+ { 1, 9, 12, 26, 32, 18432, 74, }, /* 180 */
+ { 1, 9, 12, 47, 32, 18432, 74, }, /* 181 */
+ { 1, 9, 12, 55, 32, 18432, 74, }, /* 182 */
+ { 1, 9, 12, 30, 32, 18432, 74, }, /* 183 */
+ { 1, 9, 12, 43, 32, 18432, 74, }, /* 184 */
+ { 1, 9, 12, 96, 32, 18432, 74, }, /* 185 */
+ { 1, 5, 12, 0, -38, 18432, 76, }, /* 186 */
+ { 1, 5, 12, 0, -37, 18432, 76, }, /* 187 */
+ { 1, 5, 12, 0, -32, 18432, 76, }, /* 188 */
+ { 1, 5, 12, 34, -32, 18432, 76, }, /* 189 */
+ { 1, 5, 12, 59, -32, 18432, 76, }, /* 190 */
+ { 1, 5, 12, 38, -32, 18432, 76, }, /* 191 */
+ { 1, 5, 12, 21, -116, 18432, 76, }, /* 192 */
+ { 1, 5, 12, 51, -32, 18432, 76, }, /* 193 */
+ { 1, 5, 12, 26, -775, 18432, 76, }, /* 194 */
+ { 1, 5, 12, 47, -32, 18432, 76, }, /* 195 */
+ { 1, 5, 12, 55, -32, 18432, 76, }, /* 196 */
+ { 1, 5, 12, 30, 1, 18432, 70, }, /* 197 */
+ { 1, 5, 12, 30, -32, 18432, 76, }, /* 198 */
+ { 1, 5, 12, 43, -32, 18432, 76, }, /* 199 */
+ { 1, 5, 12, 96, -32, 18432, 76, }, /* 200 */
+ { 1, 5, 12, 0, -64, 18432, 76, }, /* 201 */
+ { 1, 5, 12, 0, -63, 18432, 76, }, /* 202 */
+ { 1, 9, 12, 0, 8, 18432, 74, }, /* 203 */
+ { 1, 5, 12, 34, -30, 18432, 110, }, /* 204 */
+ { 1, 5, 12, 38, -25, 18432, 110, }, /* 205 */
+ { 1, 9, 12, 0, 0, 18432, 112, }, /* 206 */
+ { 1, 9, 12, 0, 0, 18432, 114, }, /* 207 */
+ { 1, 5, 12, 43, -15, 18432, 110, }, /* 208 */
+ { 1, 5, 12, 47, -22, 18432, 70, }, /* 209 */
+ { 1, 5, 12, 0, -8, 18432, 76, }, /* 210 */
+ { 34, 9, 12, 0, 1, 18432, 74, }, /* 211 */
+ { 34, 5, 12, 0, -1, 18432, 76, }, /* 212 */
+ { 1, 5, 12, 51, -54, 18432, 110, }, /* 213 */
+ { 1, 5, 12, 55, -48, 18432, 110, }, /* 214 */
+ { 1, 5, 12, 0, 7, 18432, 76, }, /* 215 */
+ { 1, 5, 12, 0, -116, 18432, 78, }, /* 216 */
+ { 1, 9, 12, 38, -60, 18432, 116, }, /* 217 */
+ { 1, 5, 12, 59, -64, 18432, 110, }, /* 218 */
+ { 1, 25, 12, 0, 0, 28672, 118, }, /* 219 */
+ { 1, 9, 12, 0, -7, 18432, 74, }, /* 220 */
+ { 1, 5, 12, 0, 0, 18432, 60, }, /* 221 */
+ { 1, 9, 12, 0, -130, 18432, 74, }, /* 222 */
+ { 2, 9, 12, 0, 80, 18432, 74, }, /* 223 */
+ { 2, 9, 12, 0, 32, 18432, 74, }, /* 224 */
+ { 2, 9, 12, 63, 32, 18432, 74, }, /* 225 */
+ { 2, 9, 12, 67, 32, 18432, 74, }, /* 226 */
+ { 2, 9, 12, 71, 32, 18432, 74, }, /* 227 */
+ { 2, 9, 12, 75, 32, 18432, 74, }, /* 228 */
+ { 2, 9, 12, 79, 32, 18432, 74, }, /* 229 */
+ { 2, 9, 12, 84, 32, 18432, 74, }, /* 230 */
+ { 2, 5, 12, 0, -32, 18432, 76, }, /* 231 */
+ { 2, 5, 12, 63, -32, 18432, 76, }, /* 232 */
+ { 2, 5, 12, 67, -32, 18432, 76, }, /* 233 */
+ { 2, 5, 12, 71, -32, 18432, 76, }, /* 234 */
+ { 2, 5, 12, 75, -32, 18432, 76, }, /* 235 */
+ { 2, 5, 12, 79, -32, 18432, 76, }, /* 236 */
+ { 2, 5, 12, 84, -32, 18432, 76, }, /* 237 */
+ { 2, 5, 12, 0, -80, 18432, 76, }, /* 238 */
+ { 2, 5, 12, 0, -80, 18432, 78, }, /* 239 */
+ { 2, 9, 12, 0, 1, 18432, 74, }, /* 240 */
+ { 2, 5, 12, 0, -1, 18432, 76, }, /* 241 */
+ { 2, 9, 12, 88, 1, 18432, 74, }, /* 242 */
+ { 2, 5, 12, 88, -1, 18432, 76, }, /* 243 */
+ { 2, 26, 12, 0, 0, 18432, 68, }, /* 244 */
+ { 2, 12, 3, 0, 0, 26684, 96, }, /* 245 */
+ { 2, 12, 3, 0, 0, 26678, 96, }, /* 246 */
+ { 84, 12, 3, 0, 0, 26681, 96, }, /* 247 */
+ { 2, 11, 3, 0, 0, 26624, 120, }, /* 248 */
+ { 2, 9, 12, 0, 15, 18432, 74, }, /* 249 */
+ { 2, 5, 12, 0, -15, 18432, 76, }, /* 250 */
+ { 70, 9, 12, 0, 48, 18432, 74, }, /* 251 */
+ { 70, 6, 12, 0, 0, 18432, 92, }, /* 252 */
+ { 70, 21, 12, 0, 0, 18432, 68, }, /* 253 */
+ { 70, 21, 12, 0, 0, 18432, 122, }, /* 254 */
+ { 70, 5, 12, 0, 0, 18432, 60, }, /* 255 */
+ { 70, 5, 12, 0, -48, 18432, 76, }, /* 256 */
+ { 70, 5, 12, 0, 0, 18432, 70, }, /* 257 */
+ { 70, 21, 12, 0, 0, 18432, 124, }, /* 258 */
+ { 70, 17, 12, 0, 0, 28672, 126, }, /* 259 */
+ { 70, 26, 12, 0, 0, 28672, 68, }, /* 260 */
+ { 70, 23, 12, 0, 0, 14336, 68, }, /* 261 */
+ { 68, 2, 12, 0, 0, 34816, 0, }, /* 262 */
+ { 71, 12, 3, 0, 0, 26624, 96, }, /* 263 */
+ { 71, 12, 3, 0, 0, 26624, 102, }, /* 264 */
+ { 71, 12, 3, 0, 0, 26624, 128, }, /* 265 */
+ { 71, 17, 12, 0, 0, 34816, 126, }, /* 266 */
+ { 71, 21, 12, 0, 0, 34816, 68, }, /* 267 */
+ { 71, 21, 12, 0, 0, 34816, 106, }, /* 268 */
+ { 71, 12, 3, 0, 0, 26624, 130, }, /* 269 */
+ { 71, 7, 12, 0, 0, 34816, 82, }, /* 270 */
+ { 71, 21, 12, 0, 0, 34816, 122, }, /* 271 */
+ { 3, 1, 4, 0, 0, 2048, 132, }, /* 272 */
+ { 69, 1, 4, 0, 0, 2048, 132, }, /* 273 */
+ { 3, 25, 12, 0, 0, 28672, 118, }, /* 274 */
+ { 3, 25, 12, 0, 0, 0, 118, }, /* 275 */
+ { 3, 21, 12, 0, 0, 14336, 68, }, /* 276 */
+ { 3, 23, 12, 0, 0, 0, 68, }, /* 277 */
+ { 69, 21, 12, 0, 0, 8342, 106, }, /* 278 */
+ { 3, 21, 12, 0, 0, 0, 68, }, /* 279 */
+ { 3, 26, 12, 0, 0, 28672, 68, }, /* 280 */
+ { 3, 12, 3, 0, 0, 26624, 130, }, /* 281 */
+ { 69, 21, 12, 0, 0, 150, 106, }, /* 282 */
+ { 3, 1, 2, 0, 0, 108, 134, }, /* 283 */
+ { 3, 21, 12, 0, 0, 0, 124, }, /* 284 */
+ { 69, 21, 12, 0, 0, 159, 124, }, /* 285 */
+ { 3, 7, 12, 0, 0, 0, 82, }, /* 286 */
+ { 69, 6, 12, 0, 0, 165, 136, }, /* 287 */
+ { 84, 12, 3, 0, 0, 26660, 128, }, /* 288 */
+ { 84, 12, 3, 0, 0, 26660, 130, }, /* 289 */
+ { 3, 12, 3, 0, 0, 26624, 128, }, /* 290 */
+ { 3, 12, 3, 0, 0, 26624, 96, }, /* 291 */
+ { 3, 13, 12, 0, 0, 2159, 138, }, /* 292 */
+ { 3, 21, 12, 0, 0, 2048, 68, }, /* 293 */
+ { 3, 7, 12, 0, 0, 0, 140, }, /* 294 */
+ { 3, 21, 12, 0, 0, 30, 124, }, /* 295 */
+ { 3, 6, 12, 0, 0, 0, 92, }, /* 296 */
+ { 3, 13, 12, 0, 0, 10240, 138, }, /* 297 */
+ { 3, 26, 12, 0, 0, 0, 68, }, /* 298 */
+ { 4, 21, 12, 0, 0, 0, 124, }, /* 299 */
+ { 4, 21, 12, 0, 0, 0, 106, }, /* 300 */
+ { 4, 21, 12, 0, 0, 0, 68, }, /* 301 */
+ { 68, 2, 12, 0, 0, 0, 0, }, /* 302 */
+ { 4, 1, 4, 0, 0, 0, 132, }, /* 303 */
+ { 4, 7, 12, 0, 0, 0, 82, }, /* 304 */
+ { 4, 12, 3, 0, 0, 26624, 130, }, /* 305 */
+ { 4, 12, 3, 0, 0, 26624, 128, }, /* 306 */
+ { 4, 12, 3, 0, 0, 26624, 96, }, /* 307 */
+ { 5, 7, 12, 0, 0, 0, 82, }, /* 308 */
+ { 5, 12, 3, 0, 0, 26624, 128, }, /* 309 */
+ { 38, 13, 12, 0, 0, 34816, 138, }, /* 310 */
+ { 38, 7, 12, 0, 0, 34816, 82, }, /* 311 */
+ { 38, 12, 3, 0, 0, 26624, 96, }, /* 312 */
+ { 38, 6, 12, 0, 0, 34816, 92, }, /* 313 */
+ { 38, 26, 12, 0, 0, 28672, 68, }, /* 314 */
+ { 38, 21, 12, 0, 0, 28672, 68, }, /* 315 */
+ { 38, 21, 12, 0, 0, 28672, 106, }, /* 316 */
+ { 38, 21, 12, 0, 0, 28672, 124, }, /* 317 */
+ { 38, 6, 12, 0, 0, 34816, 136, }, /* 318 */
+ { 38, 12, 3, 0, 0, 26624, 102, }, /* 319 */
+ { 38, 23, 12, 0, 0, 34816, 68, }, /* 320 */
+ { 110, 7, 12, 0, 0, 34816, 82, }, /* 321 */
+ { 110, 12, 3, 0, 0, 26624, 130, }, /* 322 */
+ { 110, 12, 3, 0, 0, 26624, 96, }, /* 323 */
+ { 110, 6, 12, 0, 0, 34816, 142, }, /* 324 */
+ { 110, 12, 3, 0, 0, 26624, 102, }, /* 325 */
+ { 110, 21, 12, 0, 0, 34816, 106, }, /* 326 */
+ { 110, 21, 12, 0, 0, 34816, 124, }, /* 327 */
+ { 42, 7, 12, 0, 0, 34816, 82, }, /* 328 */
+ { 42, 12, 3, 0, 0, 26624, 102, }, /* 329 */
+ { 42, 21, 12, 0, 0, 34816, 106, }, /* 330 */
+ { 3, 24, 12, 0, 0, 0, 122, }, /* 331 */
+ { 3, 12, 3, 0, 0, 26624, 102, }, /* 332 */
+ { 6, 12, 3, 0, 0, 26624, 130, }, /* 333 */
+ { 6, 10, 5, 0, 0, 18432, 144, }, /* 334 */
+ { 6, 7, 12, 0, 0, 18432, 82, }, /* 335 */
+ { 6, 12, 3, 0, 0, 26624, 96, }, /* 336 */
+ { 6, 12, 3, 0, 0, 26624, 146, }, /* 337 */
+ { 84, 12, 3, 0, 0, 26798, 96, }, /* 338 */
+ { 84, 12, 3, 0, 0, 26795, 96, }, /* 339 */
+ { 69, 21, 12, 0, 0, 18615, 124, }, /* 340 */
+ { 69, 21, 12, 0, 0, 18618, 124, }, /* 341 */
+ { 6, 13, 12, 0, 0, 18576, 138, }, /* 342 */
+ { 6, 21, 12, 0, 0, 18432, 68, }, /* 343 */
+ { 6, 6, 12, 0, 0, 18432, 92, }, /* 344 */
+ { 7, 7, 12, 0, 0, 18432, 82, }, /* 345 */
+ { 7, 12, 3, 0, 0, 26624, 130, }, /* 346 */
+ { 7, 10, 5, 0, 0, 18432, 144, }, /* 347 */
+ { 7, 12, 3, 0, 0, 26624, 96, }, /* 348 */
+ { 7, 10, 3, 0, 0, 18432, 148, }, /* 349 */
+ { 7, 12, 3, 0, 0, 26624, 146, }, /* 350 */
+ { 7, 13, 12, 0, 0, 18546, 138, }, /* 351 */
+ { 7, 23, 12, 0, 0, 14336, 68, }, /* 352 */
+ { 7, 15, 12, 0, 0, 18432, 68, }, /* 353 */
+ { 7, 26, 12, 0, 0, 18432, 68, }, /* 354 */
+ { 7, 21, 12, 0, 0, 18432, 68, }, /* 355 */
+ { 7, 12, 3, 0, 0, 26624, 102, }, /* 356 */
+ { 8, 12, 3, 0, 0, 26624, 130, }, /* 357 */
+ { 8, 10, 5, 0, 0, 18432, 144, }, /* 358 */
+ { 8, 7, 12, 0, 0, 18432, 82, }, /* 359 */
+ { 8, 12, 3, 0, 0, 26624, 96, }, /* 360 */
+ { 8, 12, 3, 0, 0, 26624, 146, }, /* 361 */
+ { 8, 13, 12, 0, 0, 18519, 138, }, /* 362 */
+ { 8, 21, 12, 0, 0, 18432, 68, }, /* 363 */
+ { 9, 12, 3, 0, 0, 26624, 130, }, /* 364 */
+ { 9, 10, 5, 0, 0, 18432, 144, }, /* 365 */
+ { 9, 7, 12, 0, 0, 18432, 82, }, /* 366 */
+ { 9, 12, 3, 0, 0, 26624, 96, }, /* 367 */
+ { 9, 12, 3, 0, 0, 26624, 146, }, /* 368 */
+ { 9, 13, 12, 0, 0, 18516, 138, }, /* 369 */
+ { 9, 21, 12, 0, 0, 18432, 68, }, /* 370 */
+ { 9, 23, 12, 0, 0, 14336, 68, }, /* 371 */
+ { 10, 12, 3, 0, 0, 26624, 130, }, /* 372 */
+ { 10, 10, 5, 0, 0, 18432, 144, }, /* 373 */
+ { 10, 7, 12, 0, 0, 18432, 82, }, /* 374 */
+ { 10, 12, 3, 0, 0, 26624, 96, }, /* 375 */
+ { 10, 10, 3, 0, 0, 18432, 148, }, /* 376 */
+ { 10, 12, 3, 0, 0, 26624, 146, }, /* 377 */
+ { 10, 12, 3, 0, 0, 26624, 150, }, /* 378 */
+ { 10, 13, 12, 0, 0, 18432, 138, }, /* 379 */
+ { 10, 26, 12, 0, 0, 18432, 68, }, /* 380 */
+ { 10, 15, 12, 0, 0, 18432, 68, }, /* 381 */
+ { 11, 12, 3, 0, 0, 26624, 130, }, /* 382 */
+ { 11, 7, 12, 0, 0, 18432, 82, }, /* 383 */
+ { 11, 10, 3, 0, 0, 18432, 148, }, /* 384 */
+ { 11, 10, 5, 0, 0, 18432, 144, }, /* 385 */
+ { 11, 12, 3, 0, 0, 26624, 146, }, /* 386 */
+ { 11, 13, 12, 0, 0, 18513, 138, }, /* 387 */
+ { 11, 15, 12, 0, 0, 18513, 68, }, /* 388 */
+ { 11, 26, 12, 0, 0, 28753, 68, }, /* 389 */
+ { 11, 26, 12, 0, 0, 28672, 68, }, /* 390 */
+ { 11, 23, 12, 0, 0, 14336, 68, }, /* 391 */
+ { 12, 12, 3, 0, 0, 26624, 130, }, /* 392 */
+ { 12, 10, 5, 0, 0, 18432, 144, }, /* 393 */
+ { 12, 12, 3, 0, 0, 26624, 102, }, /* 394 */
+ { 12, 7, 12, 0, 0, 18432, 82, }, /* 395 */
+ { 12, 12, 3, 0, 0, 26624, 96, }, /* 396 */
+ { 12, 12, 3, 0, 0, 26624, 146, }, /* 397 */
+ { 12, 13, 12, 0, 0, 18432, 138, }, /* 398 */
+ { 12, 21, 12, 0, 0, 18432, 68, }, /* 399 */
+ { 12, 15, 12, 0, 0, 28672, 68, }, /* 400 */
+ { 12, 26, 12, 0, 0, 18432, 68, }, /* 401 */
+ { 13, 7, 12, 0, 0, 18432, 82, }, /* 402 */
+ { 13, 12, 3, 0, 0, 26624, 130, }, /* 403 */
+ { 13, 10, 5, 0, 0, 18432, 144, }, /* 404 */
+ { 13, 21, 12, 0, 0, 18432, 68, }, /* 405 */
+ { 13, 12, 3, 0, 0, 26624, 96, }, /* 406 */
+ { 13, 12, 3, 0, 0, 18432, 130, }, /* 407 */
+ { 13, 10, 3, 0, 0, 18432, 148, }, /* 408 */
+ { 13, 12, 3, 0, 0, 26624, 146, }, /* 409 */
+ { 13, 13, 12, 0, 0, 18528, 138, }, /* 410 */
+ { 14, 12, 3, 0, 0, 26624, 130, }, /* 411 */
+ { 14, 10, 5, 0, 0, 18432, 144, }, /* 412 */
+ { 14, 7, 12, 0, 0, 18432, 82, }, /* 413 */
+ { 14, 12, 3, 0, 0, 26624, 146, }, /* 414 */
+ { 14, 10, 3, 0, 0, 18432, 148, }, /* 415 */
+ { 14, 7, 4, 0, 0, 18432, 82, }, /* 416 */
+ { 14, 26, 12, 0, 0, 18432, 68, }, /* 417 */
+ { 14, 15, 12, 0, 0, 18432, 68, }, /* 418 */
+ { 14, 13, 12, 0, 0, 18432, 138, }, /* 419 */
+ { 15, 12, 3, 0, 0, 26624, 130, }, /* 420 */
+ { 15, 10, 5, 0, 0, 18432, 144, }, /* 421 */
+ { 15, 7, 12, 0, 0, 18432, 82, }, /* 422 */
+ { 15, 12, 3, 0, 0, 26624, 146, }, /* 423 */
+ { 15, 10, 3, 0, 0, 18432, 148, }, /* 424 */
+ { 15, 13, 12, 0, 0, 18432, 138, }, /* 425 */
+ { 15, 21, 12, 0, 0, 18432, 68, }, /* 426 */
+ { 72, 7, 12, 0, 0, 18432, 82, }, /* 427 */
+ { 72, 12, 3, 0, 0, 26624, 130, }, /* 428 */
+ { 72, 7, 5, 0, 0, 18432, 152, }, /* 429 */
+ { 72, 12, 3, 0, 0, 26624, 154, }, /* 430 */
+ { 69, 23, 12, 0, 0, 14336, 68, }, /* 431 */
+ { 72, 7, 12, 0, 0, 18432, 156, }, /* 432 */
+ { 72, 6, 12, 0, 0, 18432, 136, }, /* 433 */
+ { 72, 12, 3, 0, 0, 26624, 96, }, /* 434 */
+ { 72, 21, 12, 0, 0, 18432, 68, }, /* 435 */
+ { 72, 13, 12, 0, 0, 18432, 138, }, /* 436 */
+ { 72, 21, 12, 0, 0, 18432, 106, }, /* 437 */
+ { 73, 7, 12, 0, 0, 18432, 82, }, /* 438 */
+ { 73, 12, 3, 0, 0, 26624, 130, }, /* 439 */
+ { 73, 7, 5, 0, 0, 18432, 152, }, /* 440 */
+ { 73, 12, 3, 0, 0, 26624, 146, }, /* 441 */
+ { 73, 7, 12, 0, 0, 18432, 156, }, /* 442 */
+ { 73, 6, 12, 0, 0, 18432, 136, }, /* 443 */
+ { 73, 12, 3, 0, 0, 26624, 96, }, /* 444 */
+ { 73, 13, 12, 0, 0, 18432, 138, }, /* 445 */
+ { 74, 7, 12, 0, 0, 18432, 82, }, /* 446 */
+ { 74, 26, 12, 0, 0, 18432, 68, }, /* 447 */
+ { 74, 21, 12, 0, 0, 18432, 68, }, /* 448 */
+ { 74, 21, 12, 0, 0, 18432, 106, }, /* 449 */
+ { 74, 12, 3, 0, 0, 26624, 96, }, /* 450 */
+ { 74, 13, 12, 0, 0, 18432, 138, }, /* 451 */
+ { 74, 15, 12, 0, 0, 18432, 68, }, /* 452 */
+ { 74, 22, 12, 0, 0, 28672, 158, }, /* 453 */
+ { 74, 18, 12, 0, 0, 28672, 158, }, /* 454 */
+ { 74, 10, 5, 0, 0, 18432, 160, }, /* 455 */
+ { 74, 12, 3, 0, 0, 26624, 130, }, /* 456 */
+ { 74, 12, 3, 0, 0, 26624, 162, }, /* 457 */
+ { 74, 10, 5, 0, 0, 18432, 144, }, /* 458 */
+ { 74, 12, 3, 0, 0, 26624, 146, }, /* 459 */
+ { 69, 26, 12, 0, 0, 18432, 68, }, /* 460 */
+ { 16, 7, 12, 0, 0, 18432, 82, }, /* 461 */
+ { 16, 10, 12, 0, 0, 18432, 144, }, /* 462 */
+ { 16, 12, 3, 0, 0, 26624, 130, }, /* 463 */
+ { 16, 10, 5, 0, 0, 18432, 144, }, /* 464 */
+ { 16, 12, 3, 0, 0, 26624, 96, }, /* 465 */
+ { 16, 12, 3, 0, 0, 26624, 146, }, /* 466 */
+ { 16, 13, 12, 0, 0, 18549, 138, }, /* 467 */
+ { 16, 21, 12, 0, 0, 18432, 124, }, /* 468 */
+ { 16, 21, 12, 0, 0, 18432, 68, }, /* 469 */
+ { 16, 10, 12, 0, 0, 18432, 164, }, /* 470 */
+ { 16, 12, 3, 0, 0, 26624, 128, }, /* 471 */
+ { 16, 13, 12, 0, 0, 18432, 138, }, /* 472 */
+ { 16, 26, 12, 0, 0, 18432, 68, }, /* 473 */
+ { 17, 9, 12, 0, 7264, 18432, 74, }, /* 474 */
+ { 17, 5, 12, 0, 3008, 18432, 166, }, /* 475 */
+ { 69, 21, 12, 0, 0, 18510, 68, }, /* 476 */
+ { 17, 6, 12, 0, 0, 18432, 142, }, /* 477 */
+ { 18, 7, 6, 0, 0, 18432, 82, }, /* 478 */
+ { 18, 7, 6, 0, 0, 18432, 168, }, /* 479 */
+ { 18, 7, 7, 0, 0, 18432, 168, }, /* 480 */
+ { 18, 7, 7, 0, 0, 18432, 82, }, /* 481 */
+ { 18, 7, 8, 0, 0, 18432, 82, }, /* 482 */
+ { 75, 7, 12, 0, 0, 18432, 82, }, /* 483 */
+ { 75, 12, 3, 0, 0, 26624, 96, }, /* 484 */
+ { 75, 21, 12, 0, 0, 18432, 68, }, /* 485 */
+ { 75, 21, 12, 0, 0, 18432, 106, }, /* 486 */
+ { 75, 21, 12, 0, 0, 18432, 124, }, /* 487 */
+ { 75, 15, 12, 0, 0, 18432, 138, }, /* 488 */
+ { 75, 15, 12, 0, 0, 18432, 68, }, /* 489 */
+ { 75, 26, 12, 0, 0, 28672, 68, }, /* 490 */
+ { 76, 9, 12, 0, 38864, 18432, 170, }, /* 491 */
+ { 76, 9, 12, 0, 8, 18432, 170, }, /* 492 */
+ { 76, 5, 12, 0, -8, 18432, 70, }, /* 493 */
+ { 77, 17, 12, 0, 0, 28672, 126, }, /* 494 */
+ { 77, 7, 12, 0, 0, 18432, 82, }, /* 495 */
+ { 77, 26, 12, 0, 0, 18432, 68, }, /* 496 */
+ { 77, 21, 12, 0, 0, 18432, 124, }, /* 497 */
+ { 78, 29, 12, 0, 0, 45056, 52, }, /* 498 */
+ { 78, 7, 12, 0, 0, 18432, 82, }, /* 499 */
+ { 78, 22, 12, 0, 0, 28672, 158, }, /* 500 */
+ { 78, 18, 12, 0, 0, 28672, 158, }, /* 501 */
+ { 79, 7, 12, 0, 0, 18432, 82, }, /* 502 */
+ { 69, 21, 12, 0, 0, 18432, 106, }, /* 503 */
+ { 79, 14, 12, 0, 0, 18432, 82, }, /* 504 */
+ { 25, 7, 12, 0, 0, 18432, 82, }, /* 505 */
+ { 25, 12, 3, 0, 0, 26624, 130, }, /* 506 */
+ { 25, 12, 3, 0, 0, 26624, 146, }, /* 507 */
+ { 25, 10, 5, 0, 0, 18432, 172, }, /* 508 */
+ { 26, 7, 12, 0, 0, 18432, 82, }, /* 509 */
+ { 26, 12, 3, 0, 0, 26624, 130, }, /* 510 */
+ { 26, 10, 5, 0, 0, 18432, 174, }, /* 511 */
+ { 69, 21, 12, 0, 0, 18573, 124, }, /* 512 */
+ { 27, 7, 12, 0, 0, 18432, 82, }, /* 513 */
+ { 27, 12, 3, 0, 0, 26624, 130, }, /* 514 */
+ { 28, 7, 12, 0, 0, 18432, 82, }, /* 515 */
+ { 28, 12, 3, 0, 0, 26624, 130, }, /* 516 */
+ { 80, 7, 12, 0, 0, 18432, 82, }, /* 517 */
+ { 80, 7, 12, 0, 0, 18432, 140, }, /* 518 */
+ { 80, 12, 3, 0, 0, 26624, 100, }, /* 519 */
+ { 80, 10, 5, 0, 0, 18432, 144, }, /* 520 */
+ { 80, 12, 3, 0, 0, 26624, 130, }, /* 521 */
+ { 80, 12, 3, 0, 0, 26624, 96, }, /* 522 */
+ { 80, 12, 3, 0, 0, 26624, 146, }, /* 523 */
+ { 80, 21, 12, 0, 0, 18432, 106, }, /* 524 */
+ { 80, 6, 12, 0, 0, 18432, 142, }, /* 525 */
+ { 80, 21, 12, 0, 0, 18432, 68, }, /* 526 */
+ { 80, 23, 12, 0, 0, 14336, 68, }, /* 527 */
+ { 80, 13, 12, 0, 0, 18432, 138, }, /* 528 */
+ { 80, 15, 12, 0, 0, 28672, 68, }, /* 529 */
+ { 19, 21, 12, 0, 0, 28672, 68, }, /* 530 */
+ { 69, 21, 12, 0, 0, 28777, 106, }, /* 531 */
+ { 69, 21, 12, 0, 0, 28777, 124, }, /* 532 */
+ { 19, 21, 12, 0, 0, 28672, 106, }, /* 533 */
+ { 19, 17, 12, 0, 0, 28672, 126, }, /* 534 */
+ { 19, 21, 12, 0, 0, 28672, 124, }, /* 535 */
+ { 19, 21, 12, 0, 0, 28672, 176, }, /* 536 */
+ { 19, 12, 3, 0, 0, 26624, 178, }, /* 537 */
+ { 19, 1, 2, 0, 0, 6144, 66, }, /* 538 */
+ { 19, 13, 12, 0, 0, 18432, 138, }, /* 539 */
+ { 19, 7, 12, 0, 0, 18432, 82, }, /* 540 */
+ { 19, 6, 12, 0, 0, 18432, 136, }, /* 541 */
+ { 19, 12, 3, 0, 0, 26624, 180, }, /* 542 */
+ { 19, 12, 3, 0, 0, 26624, 130, }, /* 543 */
+ { 29, 7, 12, 0, 0, 18432, 82, }, /* 544 */
+ { 29, 12, 3, 0, 0, 26624, 130, }, /* 545 */
+ { 29, 10, 5, 0, 0, 18432, 144, }, /* 546 */
+ { 29, 12, 3, 0, 0, 26624, 96, }, /* 547 */
+ { 29, 26, 12, 0, 0, 28672, 68, }, /* 548 */
+ { 29, 21, 12, 0, 0, 28672, 124, }, /* 549 */
+ { 29, 13, 12, 0, 0, 18432, 138, }, /* 550 */
+ { 30, 7, 12, 0, 0, 18432, 82, }, /* 551 */
+ { 89, 7, 12, 0, 0, 18432, 82, }, /* 552 */
+ { 89, 7, 12, 0, 0, 18432, 156, }, /* 553 */
+ { 89, 13, 12, 0, 0, 18432, 138, }, /* 554 */
+ { 89, 15, 12, 0, 0, 18432, 138, }, /* 555 */
+ { 89, 26, 12, 0, 0, 28672, 68, }, /* 556 */
+ { 80, 26, 12, 0, 0, 28672, 68, }, /* 557 */
+ { 33, 7, 12, 0, 0, 18432, 82, }, /* 558 */
+ { 33, 12, 3, 0, 0, 26624, 130, }, /* 559 */
+ { 33, 10, 5, 0, 0, 18432, 144, }, /* 560 */
+ { 33, 21, 12, 0, 0, 18432, 68, }, /* 561 */
+ { 106, 7, 12, 0, 0, 18432, 82, }, /* 562 */
+ { 106, 10, 5, 0, 0, 18432, 144, }, /* 563 */
+ { 106, 12, 3, 0, 0, 26624, 130, }, /* 564 */
+ { 106, 12, 3, 0, 0, 26624, 182, }, /* 565 */
+ { 106, 10, 12, 0, 0, 18432, 144, }, /* 566 */
+ { 106, 12, 3, 0, 0, 26624, 96, }, /* 567 */
+ { 106, 13, 12, 0, 0, 18432, 138, }, /* 568 */
+ { 106, 21, 12, 0, 0, 18432, 68, }, /* 569 */
+ { 106, 6, 12, 0, 0, 18432, 136, }, /* 570 */
+ { 106, 21, 12, 0, 0, 18432, 124, }, /* 571 */
+ { 84, 11, 3, 0, 0, 26624, 184, }, /* 572 */
+ { 84, 12, 3, 0, 0, 26624, 130, }, /* 573 */
+ { 93, 12, 3, 0, 0, 26624, 130, }, /* 574 */
+ { 93, 10, 5, 0, 0, 18432, 144, }, /* 575 */
+ { 93, 7, 12, 0, 0, 18432, 82, }, /* 576 */
+ { 93, 12, 3, 0, 0, 26624, 96, }, /* 577 */
+ { 93, 10, 3, 0, 0, 18432, 148, }, /* 578 */
+ { 93, 10, 5, 0, 0, 18432, 172, }, /* 579 */
+ { 93, 13, 12, 0, 0, 18432, 138, }, /* 580 */
+ { 93, 21, 12, 0, 0, 18432, 124, }, /* 581 */
+ { 93, 21, 12, 0, 0, 18432, 68, }, /* 582 */
+ { 93, 21, 12, 0, 0, 18432, 106, }, /* 583 */
+ { 93, 26, 12, 0, 0, 18432, 68, }, /* 584 */
+ { 96, 12, 3, 0, 0, 26624, 130, }, /* 585 */
+ { 96, 10, 5, 0, 0, 18432, 144, }, /* 586 */
+ { 96, 7, 12, 0, 0, 18432, 82, }, /* 587 */
+ { 96, 10, 5, 0, 0, 18432, 172, }, /* 588 */
+ { 96, 12, 3, 0, 0, 26624, 146, }, /* 589 */
+ { 96, 13, 12, 0, 0, 18432, 138, }, /* 590 */
+ { 119, 7, 12, 0, 0, 18432, 82, }, /* 591 */
+ { 119, 12, 3, 0, 0, 26624, 102, }, /* 592 */
+ { 119, 10, 5, 0, 0, 18432, 144, }, /* 593 */
+ { 119, 12, 3, 0, 0, 26624, 130, }, /* 594 */
+ { 119, 10, 5, 0, 0, 18432, 174, }, /* 595 */
+ { 119, 21, 12, 0, 0, 18432, 68, }, /* 596 */
+ { 97, 7, 12, 0, 0, 18432, 82, }, /* 597 */
+ { 97, 10, 5, 0, 0, 18432, 144, }, /* 598 */
+ { 97, 12, 3, 0, 0, 26624, 130, }, /* 599 */
+ { 97, 12, 3, 0, 0, 26624, 186, }, /* 600 */
+ { 97, 12, 3, 0, 0, 26624, 96, }, /* 601 */
+ { 97, 21, 12, 0, 0, 18432, 124, }, /* 602 */
+ { 97, 21, 12, 0, 0, 18432, 106, }, /* 603 */
+ { 97, 13, 12, 0, 0, 18432, 138, }, /* 604 */
+ { 98, 13, 12, 0, 0, 18432, 138, }, /* 605 */
+ { 98, 7, 12, 0, 0, 18432, 82, }, /* 606 */
+ { 98, 6, 12, 0, 0, 18432, 92, }, /* 607 */
+ { 98, 6, 12, 0, 0, 18432, 94, }, /* 608 */
+ { 98, 21, 12, 0, 0, 18432, 124, }, /* 609 */
+ { 2, 5, 12, 63, -6222, 18432, 70, }, /* 610 */
+ { 2, 5, 12, 67, -6221, 18432, 70, }, /* 611 */
+ { 2, 5, 12, 71, -6212, 18432, 70, }, /* 612 */
+ { 2, 5, 12, 75, -6210, 18432, 70, }, /* 613 */
+ { 2, 5, 12, 79, -6210, 18432, 70, }, /* 614 */
+ { 2, 5, 12, 79, -6211, 18432, 70, }, /* 615 */
+ { 2, 5, 12, 84, -6204, 18432, 70, }, /* 616 */
+ { 2, 5, 12, 88, -6180, 18432, 70, }, /* 617 */
+ { 2, 5, 12, 108, 35267, 18432, 70, }, /* 618 */
+ { 17, 9, 12, 0, -3008, 18432, 74, }, /* 619 */
+ { 96, 21, 12, 0, 0, 18432, 68, }, /* 620 */
+ { 84, 12, 3, 0, 0, 26762, 96, }, /* 621 */
+ { 84, 12, 3, 0, 0, 26630, 96, }, /* 622 */
+ { 69, 21, 12, 0, 0, 18498, 188, }, /* 623 */
+ { 84, 12, 3, 0, 0, 26666, 96, }, /* 624 */
+ { 84, 12, 3, 0, 0, 26696, 96, }, /* 625 */
+ { 84, 12, 3, 0, 0, 26780, 96, }, /* 626 */
+ { 69, 10, 5, 0, 0, 18474, 160, }, /* 627 */
+ { 69, 7, 12, 0, 0, 18501, 82, }, /* 628 */
+ { 69, 7, 12, 0, 0, 18474, 82, }, /* 629 */
+ { 69, 7, 12, 0, 0, 18438, 82, }, /* 630 */
+ { 69, 7, 12, 0, 0, 18594, 82, }, /* 631 */
+ { 69, 7, 12, 0, 0, 18498, 82, }, /* 632 */
+ { 84, 12, 3, 0, 0, 26750, 96, }, /* 633 */
+ { 69, 10, 5, 0, 0, 18435, 160, }, /* 634 */
+ { 84, 12, 3, 0, 0, 26690, 96, }, /* 635 */
+ { 69, 7, 12, 0, 0, 18453, 82, }, /* 636 */
+ { 2, 5, 12, 0, 0, 18432, 60, }, /* 637 */
+ { 1, 6, 12, 0, 0, 18432, 88, }, /* 638 */
+ { 2, 6, 12, 0, 0, 18432, 190, }, /* 639 */
+ { 0, 5, 12, 0, 35332, 18432, 76, }, /* 640 */
+ { 0, 5, 12, 0, 3814, 18432, 76, }, /* 641 */
+ { 0, 5, 12, 0, 35384, 18432, 76, }, /* 642 */
+ { 0, 5, 12, 0, 0, 18432, 192, }, /* 643 */
+ { 0, 6, 12, 0, 0, 18432, 190, }, /* 644 */
+ { 0, 6, 12, 0, 0, 18432, 194, }, /* 645 */
+ { 1, 6, 12, 0, 0, 18432, 190, }, /* 646 */
+ { 84, 12, 3, 0, 0, 26636, 102, }, /* 647 */
+ { 84, 12, 3, 0, 0, 26687, 96, }, /* 648 */
+ { 84, 12, 3, 0, 0, 26648, 96, }, /* 649 */
+ { 0, 9, 12, 92, 1, 18432, 74, }, /* 650 */
+ { 0, 5, 12, 92, -1, 18432, 76, }, /* 651 */
+ { 0, 5, 12, 0, 0, 18432, 70, }, /* 652 */
+ { 0, 5, 12, 92, -58, 18432, 70, }, /* 653 */
+ { 0, 9, 12, 0, -7615, 18432, 74, }, /* 654 */
+ { 1, 5, 12, 0, 8, 18432, 76, }, /* 655 */
+ { 1, 9, 12, 0, -8, 18432, 74, }, /* 656 */
+ { 1, 5, 12, 0, 74, 18432, 76, }, /* 657 */
+ { 1, 5, 12, 0, 86, 18432, 76, }, /* 658 */
+ { 1, 5, 12, 0, 100, 18432, 76, }, /* 659 */
+ { 1, 5, 12, 0, 128, 18432, 76, }, /* 660 */
+ { 1, 5, 12, 0, 112, 18432, 76, }, /* 661 */
+ { 1, 5, 12, 0, 126, 18432, 76, }, /* 662 */
+ { 1, 5, 12, 0, 8, 18432, 70, }, /* 663 */
+ { 1, 8, 12, 0, -8, 18432, 86, }, /* 664 */
+ { 1, 5, 12, 0, 0, 18432, 70, }, /* 665 */
+ { 1, 5, 12, 0, 9, 18432, 70, }, /* 666 */
+ { 1, 9, 12, 0, -74, 18432, 74, }, /* 667 */
+ { 1, 8, 12, 0, -9, 18432, 86, }, /* 668 */
+ { 1, 5, 12, 21, -7173, 18432, 76, }, /* 669 */
+ { 1, 9, 12, 0, -86, 18432, 74, }, /* 670 */
+ { 1, 9, 12, 0, -100, 18432, 74, }, /* 671 */
+ { 1, 9, 12, 0, -112, 18432, 74, }, /* 672 */
+ { 1, 9, 12, 0, -128, 18432, 74, }, /* 673 */
+ { 1, 9, 12, 0, -126, 18432, 74, }, /* 674 */
+ { 69, 29, 12, 0, 0, 45056, 52, }, /* 675 */
+ { 84, 1, 3, 0, 0, 6144, 196, }, /* 676 */
+ { 84, 1, 13, 0, 0, 6144, 198, }, /* 677 */
+ { 69, 1, 2, 0, 0, 18432, 200, }, /* 678 */
+ { 69, 1, 2, 0, 0, 34816, 200, }, /* 679 */
+ { 69, 17, 12, 0, 0, 28672, 202, }, /* 680 */
+ { 69, 21, 12, 0, 0, 28672, 64, }, /* 681 */
+ { 69, 20, 12, 0, 0, 28672, 204, }, /* 682 */
+ { 69, 19, 12, 0, 0, 28672, 204, }, /* 683 */
+ { 69, 22, 12, 0, 0, 28672, 206, }, /* 684 */
+ { 69, 20, 12, 0, 0, 28672, 206, }, /* 685 */
+ { 69, 19, 12, 0, 0, 28672, 206, }, /* 686 */
+ { 69, 21, 12, 0, 0, 28672, 208, }, /* 687 */
+ { 69, 27, 2, 0, 0, 45056, 50, }, /* 688 */
+ { 69, 28, 2, 0, 0, 4096, 50, }, /* 689 */
+ { 69, 1, 2, 0, 0, 20480, 134, }, /* 690 */
+ { 69, 1, 2, 0, 0, 36864, 134, }, /* 691 */
+ { 69, 1, 2, 0, 0, 30720, 134, }, /* 692 */
+ { 69, 1, 2, 0, 0, 24576, 134, }, /* 693 */
+ { 69, 1, 2, 0, 0, 40960, 134, }, /* 694 */
+ { 69, 29, 12, 0, 0, 8291, 52, }, /* 695 */
+ { 69, 21, 12, 0, 0, 14336, 54, }, /* 696 */
+ { 69, 21, 12, 0, 0, 14336, 64, }, /* 697 */
+ { 69, 21, 14, 0, 0, 28672, 210, }, /* 698 */
+ { 69, 21, 12, 0, 0, 28672, 212, }, /* 699 */
+ { 69, 16, 12, 0, 0, 28672, 138, }, /* 700 */
+ { 69, 16, 12, 0, 0, 28672, 214, }, /* 701 */
+ { 69, 25, 12, 0, 0, 8192, 64, }, /* 702 */
+ { 69, 22, 12, 0, 0, 28672, 216, }, /* 703 */
+ { 69, 18, 12, 0, 0, 28672, 216, }, /* 704 */
+ { 69, 21, 12, 0, 0, 28672, 202, }, /* 705 */
+ { 69, 1, 2, 0, 0, 6144, 218, }, /* 706 */
+ { 68, 2, 2, 0, 0, 6144, 220, }, /* 707 */
+ { 69, 1, 2, 0, 0, 22528, 134, }, /* 708 */
+ { 69, 1, 2, 0, 0, 38912, 134, }, /* 709 */
+ { 69, 1, 2, 0, 0, 16384, 134, }, /* 710 */
+ { 69, 1, 2, 0, 0, 32768, 134, }, /* 711 */
+ { 69, 1, 2, 0, 0, 6144, 222, }, /* 712 */
+ { 69, 25, 12, 0, 0, 12288, 118, }, /* 713 */
+ { 69, 25, 12, 0, 0, 12288, 224, }, /* 714 */
+ { 69, 25, 12, 0, 0, 28672, 118, }, /* 715 */
+ { 69, 22, 12, 0, 0, 28672, 226, }, /* 716 */
+ { 69, 18, 12, 0, 0, 28672, 226, }, /* 717 */
+ { 68, 2, 12, 0, 0, 14336, 0, }, /* 718 */
+ { 84, 12, 3, 0, 0, 26624, 228, }, /* 719 */
+ { 84, 11, 3, 0, 0, 26624, 120, }, /* 720 */
+ { 84, 11, 3, 0, 0, 26624, 230, }, /* 721 */
+ { 84, 12, 3, 0, 0, 26753, 102, }, /* 722 */
+ { 69, 26, 12, 0, 0, 28672, 68, }, /* 723 */
+ { 69, 9, 12, 0, 0, 18432, 112, }, /* 724 */
+ { 69, 5, 12, 0, 0, 18432, 232, }, /* 725 */
+ { 69, 25, 12, 0, 0, 28672, 234, }, /* 726 */
+ { 69, 26, 14, 0, 0, 28672, 236, }, /* 727 */
+ { 1, 9, 12, 96, -7517, 18432, 74, }, /* 728 */
+ { 69, 26, 12, 0, 0, 28672, 118, }, /* 729 */
+ { 0, 9, 12, 100, -8383, 18432, 74, }, /* 730 */
+ { 0, 9, 12, 104, -8262, 18432, 74, }, /* 731 */
+ { 69, 26, 12, 0, 0, 14336, 238, }, /* 732 */
+ { 0, 9, 12, 0, 28, 18432, 74, }, /* 733 */
+ { 69, 7, 12, 0, 0, 18432, 240, }, /* 734 */
+ { 69, 5, 14, 0, 0, 18432, 242, }, /* 735 */
+ { 69, 5, 12, 0, 0, 18432, 244, }, /* 736 */
+ { 0, 5, 12, 0, -28, 18432, 76, }, /* 737 */
+ { 0, 14, 12, 0, 16, 18432, 74, }, /* 738 */
+ { 0, 14, 12, 0, -16, 18432, 76, }, /* 739 */
+ { 0, 14, 12, 0, 0, 18432, 82, }, /* 740 */
+ { 69, 25, 14, 0, 0, 28672, 246, }, /* 741 */
+ { 69, 26, 14, 0, 0, 28672, 246, }, /* 742 */
+ { 69, 26, 12, 0, 0, 28672, 64, }, /* 743 */
+ { 69, 25, 12, 0, 0, 28672, 248, }, /* 744 */
+ { 69, 25, 12, 0, 0, 12288, 250, }, /* 745 */
+ { 69, 22, 12, 0, 0, 28672, 248, }, /* 746 */
+ { 69, 18, 12, 0, 0, 28672, 248, }, /* 747 */
+ { 69, 26, 14, 0, 0, 28672, 252, }, /* 748 */
+ { 69, 22, 12, 0, 0, 28672, 254, }, /* 749 */
+ { 69, 18, 12, 0, 0, 28672, 254, }, /* 750 */
+ { 69, 26, 12, 0, 0, 18432, 54, }, /* 751 */
+ { 69, 26, 14, 0, 0, 28672, 256, }, /* 752 */
+ { 68, 2, 12, 0, 0, 18432, 258, }, /* 753 */
+ { 69, 26, 12, 0, 26, 18432, 260, }, /* 754 */
+ { 69, 26, 14, 0, 26, 18432, 262, }, /* 755 */
+ { 69, 26, 12, 0, -26, 18432, 264, }, /* 756 */
+ { 69, 25, 14, 0, 0, 28672, 266, }, /* 757 */
+ { 69, 26, 14, 0, 0, 28672, 268, }, /* 758 */
+ { 69, 26, 14, 0, 0, 28672, 270, }, /* 759 */
+ { 69, 25, 14, 0, 0, 28672, 268, }, /* 760 */
+ { 69, 26, 14, 0, 0, 18432, 256, }, /* 761 */
+ { 69, 26, 14, 0, 0, 28672, 272, }, /* 762 */
+ { 88, 26, 12, 0, 0, 18432, 54, }, /* 763 */
+ { 69, 26, 12, 0, 0, 28672, 216, }, /* 764 */
+ { 35, 9, 12, 0, 48, 18432, 74, }, /* 765 */
+ { 35, 5, 12, 0, -48, 18432, 76, }, /* 766 */
+ { 0, 9, 12, 0, -10743, 18432, 74, }, /* 767 */
+ { 0, 9, 12, 0, -3814, 18432, 74, }, /* 768 */
+ { 0, 9, 12, 0, -10727, 18432, 74, }, /* 769 */
+ { 0, 5, 12, 0, -10795, 18432, 76, }, /* 770 */
+ { 0, 5, 12, 0, -10792, 18432, 76, }, /* 771 */
+ { 0, 9, 12, 0, -10780, 18432, 74, }, /* 772 */
+ { 0, 9, 12, 0, -10749, 18432, 74, }, /* 773 */
+ { 0, 9, 12, 0, -10783, 18432, 74, }, /* 774 */
+ { 0, 9, 12, 0, -10782, 18432, 74, }, /* 775 */
+ { 0, 9, 12, 0, -10815, 18432, 74, }, /* 776 */
+ { 34, 5, 12, 0, 0, 18432, 60, }, /* 777 */
+ { 34, 26, 12, 0, 0, 28672, 68, }, /* 778 */
+ { 34, 12, 3, 0, 0, 26624, 96, }, /* 779 */
+ { 34, 21, 12, 0, 0, 28672, 68, }, /* 780 */
+ { 34, 15, 12, 0, 0, 28672, 68, }, /* 781 */
+ { 17, 5, 12, 0, -7264, 18432, 76, }, /* 782 */
+ { 90, 7, 12, 0, 0, 18432, 82, }, /* 783 */
+ { 90, 6, 12, 0, 0, 18432, 142, }, /* 784 */
+ { 90, 21, 12, 0, 0, 18432, 68, }, /* 785 */
+ { 90, 12, 3, 0, 0, 26624, 182, }, /* 786 */
+ { 2, 12, 3, 0, 0, 26624, 130, }, /* 787 */
+ { 69, 20, 12, 0, 0, 28672, 216, }, /* 788 */
+ { 69, 19, 12, 0, 0, 28672, 216, }, /* 789 */
+ { 69, 6, 12, 0, 0, 28672, 274, }, /* 790 */
+ { 69, 21, 12, 0, 0, 28672, 276, }, /* 791 */
+ { 69, 21, 12, 0, 0, 28726, 54, }, /* 792 */
+ { 23, 26, 12, 0, 0, 28672, 278, }, /* 793 */
+ { 69, 26, 12, 0, 0, 28672, 280, }, /* 794 */
+ { 69, 26, 12, 0, 0, 28672, 282, }, /* 795 */
+ { 69, 21, 12, 0, 0, 28825, 276, }, /* 796 */
+ { 69, 21, 12, 0, 0, 28825, 212, }, /* 797 */
+ { 69, 21, 12, 0, 0, 28819, 54, }, /* 798 */
+ { 23, 6, 12, 0, 0, 18432, 136, }, /* 799 */
+ { 69, 7, 12, 0, 0, 18447, 284, }, /* 800 */
+ { 23, 14, 12, 0, 0, 18432, 284, }, /* 801 */
+ { 69, 22, 12, 0, 0, 28825, 216, }, /* 802 */
+ { 69, 18, 12, 0, 0, 28825, 216, }, /* 803 */
+ { 69, 22, 12, 0, 0, 28825, 62, }, /* 804 */
+ { 69, 18, 12, 0, 0, 28825, 62, }, /* 805 */
+ { 69, 26, 12, 0, 0, 28819, 54, }, /* 806 */
+ { 69, 17, 12, 0, 0, 28819, 202, }, /* 807 */
+ { 69, 22, 12, 0, 0, 28819, 206, }, /* 808 */
+ { 69, 18, 12, 0, 0, 28819, 206, }, /* 809 */
+ { 84, 12, 3, 0, 0, 26669, 96, }, /* 810 */
+ { 18, 10, 3, 0, 0, 18432, 286, }, /* 811 */
+ { 69, 17, 14, 0, 0, 28819, 288, }, /* 812 */
+ { 69, 6, 12, 0, 0, 18525, 136, }, /* 813 */
+ { 69, 26, 12, 0, 0, 28819, 68, }, /* 814 */
+ { 23, 6, 12, 0, 0, 18432, 142, }, /* 815 */
+ { 69, 7, 12, 0, 0, 18564, 82, }, /* 816 */
+ { 69, 21, 14, 0, 0, 28804, 236, }, /* 817 */
+ { 69, 26, 12, 0, 0, 28687, 68, }, /* 818 */
+ { 20, 7, 12, 0, 0, 18432, 82, }, /* 819 */
+ { 84, 12, 3, 0, 0, 26717, 96, }, /* 820 */
+ { 69, 24, 12, 0, 0, 28765, 290, }, /* 821 */
+ { 20, 6, 12, 0, 0, 18432, 136, }, /* 822 */
+ { 69, 17, 12, 0, 0, 28765, 126, }, /* 823 */
+ { 21, 7, 12, 0, 0, 18432, 82, }, /* 824 */
+ { 69, 21, 12, 0, 0, 28825, 68, }, /* 825 */
+ { 69, 6, 12, 0, 0, 18525, 94, }, /* 826 */
+ { 21, 6, 12, 0, 0, 18432, 136, }, /* 827 */
+ { 22, 7, 12, 0, 0, 18432, 82, }, /* 828 */
+ { 18, 7, 12, 0, 0, 18432, 82, }, /* 829 */
+ { 18, 7, 12, 0, 0, 18432, 168, }, /* 830 */
+ { 69, 26, 12, 0, 0, 18447, 68, }, /* 831 */
+ { 69, 15, 12, 0, 0, 18447, 68, }, /* 832 */
+ { 18, 26, 12, 0, 0, 18432, 68, }, /* 833 */
+ { 18, 26, 12, 0, 0, 28672, 68, }, /* 834 */
+ { 69, 15, 12, 0, 0, 18432, 68, }, /* 835 */
+ { 69, 26, 14, 0, 0, 18447, 236, }, /* 836 */
+ { 21, 26, 12, 0, 0, 18432, 68, }, /* 837 */
+ { 23, 7, 12, 0, 0, 18432, 292, }, /* 838 */
+ { 24, 7, 12, 0, 0, 18432, 82, }, /* 839 */
+ { 24, 6, 12, 0, 0, 18432, 136, }, /* 840 */
+ { 24, 26, 12, 0, 0, 28672, 68, }, /* 841 */
+ { 111, 7, 12, 0, 0, 18432, 82, }, /* 842 */
+ { 111, 6, 12, 0, 0, 18432, 142, }, /* 843 */
+ { 111, 21, 12, 0, 0, 18432, 106, }, /* 844 */
+ { 111, 21, 12, 0, 0, 18432, 124, }, /* 845 */
+ { 99, 7, 12, 0, 0, 18432, 82, }, /* 846 */
+ { 99, 6, 12, 0, 0, 18432, 136, }, /* 847 */
+ { 99, 21, 12, 0, 0, 28672, 106, }, /* 848 */
+ { 99, 21, 12, 0, 0, 28672, 124, }, /* 849 */
+ { 99, 13, 12, 0, 0, 18432, 138, }, /* 850 */
+ { 2, 9, 12, 108, 1, 18432, 74, }, /* 851 */
+ { 2, 5, 12, 108, -35267, 18432, 76, }, /* 852 */
+ { 2, 7, 12, 0, 0, 18432, 82, }, /* 853 */
+ { 2, 21, 12, 0, 0, 28672, 68, }, /* 854 */
+ { 2, 12, 3, 0, 0, 26624, 96, }, /* 855 */
+ { 2, 6, 12, 0, 0, 28672, 92, }, /* 856 */
+ { 2, 6, 12, 0, 0, 18432, 88, }, /* 857 */
+ { 112, 7, 12, 0, 0, 18432, 82, }, /* 858 */
+ { 112, 14, 12, 0, 0, 18432, 82, }, /* 859 */
+ { 112, 12, 3, 0, 0, 26624, 96, }, /* 860 */
+ { 112, 21, 12, 0, 0, 18432, 68, }, /* 861 */
+ { 112, 21, 12, 0, 0, 18432, 124, }, /* 862 */
+ { 112, 21, 12, 0, 0, 18432, 106, }, /* 863 */
+ { 69, 24, 12, 0, 0, 28762, 56, }, /* 864 */
+ { 0, 9, 12, 0, -35332, 18432, 74, }, /* 865 */
+ { 69, 24, 12, 0, 0, 18432, 56, }, /* 866 */
+ { 0, 9, 12, 0, -42280, 18432, 74, }, /* 867 */
+ { 0, 5, 12, 0, 48, 18432, 76, }, /* 868 */
+ { 0, 9, 12, 0, -42308, 18432, 74, }, /* 869 */
+ { 0, 9, 12, 0, -42319, 18432, 74, }, /* 870 */
+ { 0, 9, 12, 0, -42315, 18432, 74, }, /* 871 */
+ { 0, 9, 12, 0, -42305, 18432, 74, }, /* 872 */
+ { 0, 9, 12, 0, -42258, 18432, 74, }, /* 873 */
+ { 0, 9, 12, 0, -42282, 18432, 74, }, /* 874 */
+ { 0, 9, 12, 0, -42261, 18432, 74, }, /* 875 */
+ { 0, 9, 12, 0, 928, 18432, 74, }, /* 876 */
+ { 0, 9, 12, 0, -48, 18432, 74, }, /* 877 */
+ { 0, 9, 12, 0, -42307, 18432, 74, }, /* 878 */
+ { 0, 9, 12, 0, -35384, 18432, 74, }, /* 879 */
+ { 0, 6, 12, 0, 0, 18432, 142, }, /* 880 */
+ { 36, 7, 12, 0, 0, 18432, 82, }, /* 881 */
+ { 36, 12, 3, 0, 0, 26624, 130, }, /* 882 */
+ { 36, 12, 3, 0, 0, 26624, 182, }, /* 883 */
+ { 36, 10, 5, 0, 0, 18432, 144, }, /* 884 */
+ { 36, 26, 12, 0, 0, 28672, 68, }, /* 885 */
+ { 69, 15, 12, 0, 0, 18612, 68, }, /* 886 */
+ { 69, 15, 12, 0, 0, 18609, 68, }, /* 887 */
+ { 69, 26, 12, 0, 0, 18600, 68, }, /* 888 */
+ { 69, 23, 12, 0, 0, 14504, 68, }, /* 889 */
+ { 69, 26, 12, 0, 0, 14504, 68, }, /* 890 */
+ { 37, 7, 12, 0, 0, 18432, 82, }, /* 891 */
+ { 37, 21, 12, 0, 0, 28672, 68, }, /* 892 */
+ { 37, 21, 12, 0, 0, 28672, 124, }, /* 893 */
+ { 100, 10, 5, 0, 0, 18432, 144, }, /* 894 */
+ { 100, 7, 12, 0, 0, 18432, 82, }, /* 895 */
+ { 100, 12, 3, 0, 0, 26624, 146, }, /* 896 */
+ { 100, 12, 3, 0, 0, 26624, 130, }, /* 897 */
+ { 100, 21, 12, 0, 0, 18432, 124, }, /* 898 */
+ { 100, 13, 12, 0, 0, 18432, 138, }, /* 899 */
+ { 6, 12, 3, 0, 0, 26666, 96, }, /* 900 */
+ { 6, 7, 12, 0, 0, 18507, 82, }, /* 901 */
+ { 39, 13, 12, 0, 0, 18432, 138, }, /* 902 */
+ { 39, 7, 12, 0, 0, 18432, 82, }, /* 903 */
+ { 39, 12, 3, 0, 0, 26624, 130, }, /* 904 */
+ { 39, 12, 3, 0, 0, 26624, 96, }, /* 905 */
+ { 69, 21, 12, 0, 0, 18567, 188, }, /* 906 */
+ { 39, 21, 12, 0, 0, 18432, 124, }, /* 907 */
+ { 101, 7, 12, 0, 0, 18432, 82, }, /* 908 */
+ { 101, 12, 3, 0, 0, 26624, 130, }, /* 909 */
+ { 101, 10, 5, 0, 0, 18432, 144, }, /* 910 */
+ { 101, 10, 5, 0, 0, 18432, 172, }, /* 911 */
+ { 101, 21, 12, 0, 0, 18432, 68, }, /* 912 */
+ { 40, 12, 3, 0, 0, 26624, 130, }, /* 913 */
+ { 40, 10, 5, 0, 0, 18432, 144, }, /* 914 */
+ { 40, 7, 12, 0, 0, 18432, 82, }, /* 915 */
+ { 40, 12, 3, 0, 0, 26624, 96, }, /* 916 */
+ { 40, 10, 5, 0, 0, 18432, 172, }, /* 917 */
+ { 40, 21, 12, 0, 0, 18432, 68, }, /* 918 */
+ { 40, 21, 12, 0, 0, 18432, 106, }, /* 919 */
+ { 40, 21, 12, 0, 0, 18432, 124, }, /* 920 */
+ { 69, 6, 12, 0, 0, 18480, 136, }, /* 921 */
+ { 40, 13, 12, 0, 0, 18432, 138, }, /* 922 */
+ { 16, 6, 12, 0, 0, 18432, 136, }, /* 923 */
+ { 105, 7, 12, 0, 0, 18432, 82, }, /* 924 */
+ { 105, 12, 3, 0, 0, 26624, 130, }, /* 925 */
+ { 105, 10, 5, 0, 0, 18432, 144, }, /* 926 */
+ { 105, 13, 12, 0, 0, 18432, 138, }, /* 927 */
+ { 105, 21, 12, 0, 0, 18432, 68, }, /* 928 */
+ { 105, 21, 12, 0, 0, 18432, 124, }, /* 929 */
+ { 107, 7, 12, 0, 0, 18432, 82, }, /* 930 */
+ { 107, 12, 3, 0, 0, 26624, 130, }, /* 931 */
+ { 107, 7, 12, 0, 0, 18432, 156, }, /* 932 */
+ { 107, 12, 3, 0, 0, 26624, 96, }, /* 933 */
+ { 107, 7, 12, 0, 0, 18432, 294, }, /* 934 */
+ { 107, 6, 12, 0, 0, 18432, 136, }, /* 935 */
+ { 107, 21, 12, 0, 0, 18432, 68, }, /* 936 */
+ { 107, 21, 12, 0, 0, 18432, 106, }, /* 937 */
+ { 113, 7, 12, 0, 0, 18432, 82, }, /* 938 */
+ { 113, 10, 5, 0, 0, 18432, 144, }, /* 939 */
+ { 113, 12, 3, 0, 0, 26624, 130, }, /* 940 */
+ { 113, 21, 12, 0, 0, 18432, 124, }, /* 941 */
+ { 113, 6, 12, 0, 0, 18432, 136, }, /* 942 */
+ { 113, 12, 3, 0, 0, 26624, 146, }, /* 943 */
+ { 0, 5, 12, 0, -928, 18432, 76, }, /* 944 */
+ { 0, 6, 12, 0, 0, 18432, 92, }, /* 945 */
+ { 76, 5, 12, 0, -38864, 18432, 70, }, /* 946 */
+ { 113, 10, 5, 0, 0, 18432, 160, }, /* 947 */
+ { 113, 13, 12, 0, 0, 18432, 138, }, /* 948 */
+ { 18, 7, 9, 0, 0, 18432, 82, }, /* 949 */
+ { 18, 7, 10, 0, 0, 18432, 82, }, /* 950 */
+ { 68, 4, 12, 0, 0, 18432, 0, }, /* 951 */
+ { 68, 3, 12, 0, 0, 18432, 0, }, /* 952 */
+ { 23, 7, 12, 0, 0, 18432, 284, }, /* 953 */
+ { 71, 25, 12, 0, 0, 12288, 118, }, /* 954 */
+ { 3, 7, 12, 0, 0, 0, 296, }, /* 955 */
+ { 69, 18, 12, 0, 0, 28705, 54, }, /* 956 */
+ { 69, 22, 12, 0, 0, 28705, 54, }, /* 957 */
+ { 68, 2, 12, 0, 0, 6144, 298, }, /* 958 */
+ { 3, 7, 12, 0, 0, 39, 82, }, /* 959 */
+ { 3, 26, 12, 0, 0, 28711, 68, }, /* 960 */
+ { 84, 12, 3, 0, 0, 26624, 178, }, /* 961 */
+ { 84, 12, 3, 0, 0, 26624, 300, }, /* 962 */
+ { 69, 21, 12, 0, 0, 28672, 68, }, /* 963 */
+ { 69, 21, 12, 0, 0, 28672, 122, }, /* 964 */
+ { 69, 22, 12, 0, 0, 28672, 68, }, /* 965 */
+ { 69, 18, 12, 0, 0, 28672, 68, }, /* 966 */
+ { 69, 17, 12, 0, 0, 28672, 126, }, /* 967 */
+ { 69, 22, 12, 0, 0, 28672, 302, }, /* 968 */
+ { 69, 18, 12, 0, 0, 28672, 302, }, /* 969 */
+ { 69, 21, 12, 0, 0, 8192, 106, }, /* 970 */
+ { 69, 21, 12, 0, 0, 8192, 304, }, /* 971 */
+ { 69, 21, 12, 0, 0, 8192, 306, }, /* 972 */
+ { 69, 21, 12, 0, 0, 28672, 124, }, /* 973 */
+ { 69, 22, 12, 0, 0, 28672, 158, }, /* 974 */
+ { 69, 18, 12, 0, 0, 28672, 158, }, /* 975 */
+ { 69, 21, 12, 0, 0, 14336, 68, }, /* 976 */
+ { 69, 21, 12, 0, 0, 28672, 118, }, /* 977 */
+ { 69, 17, 12, 0, 0, 12288, 224, }, /* 978 */
+ { 69, 25, 12, 0, 0, 28672, 226, }, /* 979 */
+ { 69, 21, 12, 0, 0, 28672, 302, }, /* 980 */
+ { 69, 21, 12, 0, 0, 28672, 308, }, /* 981 */
+ { 69, 17, 12, 0, 0, 12288, 126, }, /* 982 */
+ { 69, 21, 12, 0, 0, 8192, 68, }, /* 983 */
+ { 69, 13, 12, 0, 0, 10240, 310, }, /* 984 */
+ { 0, 9, 12, 0, 32, 18432, 312, }, /* 985 */
+ { 69, 24, 12, 0, 0, 28672, 314, }, /* 986 */
+ { 0, 5, 12, 0, -32, 18432, 316, }, /* 987 */
+ { 69, 21, 12, 0, 0, 28825, 124, }, /* 988 */
+ { 69, 22, 12, 0, 0, 28825, 318, }, /* 989 */
+ { 69, 18, 12, 0, 0, 28825, 318, }, /* 990 */
+ { 69, 21, 12, 0, 0, 28825, 106, }, /* 991 */
+ { 69, 6, 3, 0, 0, 18525, 320, }, /* 992 */
+ { 69, 1, 2, 0, 0, 28672, 322, }, /* 993 */
+ { 31, 7, 12, 0, 0, 18432, 82, }, /* 994 */
+ { 69, 21, 12, 0, 0, 18552, 68, }, /* 995 */
+ { 69, 21, 12, 0, 0, 28792, 68, }, /* 996 */
+ { 69, 21, 12, 0, 0, 18483, 68, }, /* 997 */
+ { 69, 15, 12, 0, 0, 18555, 68, }, /* 998 */
+ { 69, 26, 12, 0, 0, 18483, 68, }, /* 999 */
+ { 1, 14, 12, 0, 0, 28672, 82, }, /* 1000 */
+ { 1, 15, 12, 0, 0, 28672, 68, }, /* 1001 */
+ { 1, 26, 12, 0, 0, 28672, 68, }, /* 1002 */
+ { 1, 26, 12, 0, 0, 18432, 68, }, /* 1003 */
+ { 102, 7, 12, 0, 0, 18432, 82, }, /* 1004 */
+ { 103, 7, 12, 0, 0, 18432, 82, }, /* 1005 */
+ { 84, 12, 3, 0, 0, 26651, 96, }, /* 1006 */
+ { 69, 15, 12, 0, 0, 10267, 68, }, /* 1007 */
+ { 81, 7, 12, 0, 0, 18432, 82, }, /* 1008 */
+ { 81, 15, 12, 0, 0, 18432, 68, }, /* 1009 */
+ { 82, 7, 12, 0, 0, 18432, 82, }, /* 1010 */
+ { 82, 14, 12, 0, 0, 18432, 82, }, /* 1011 */
+ { 53, 7, 12, 0, 0, 18432, 82, }, /* 1012 */
+ { 53, 12, 3, 0, 0, 26624, 130, }, /* 1013 */
+ { 85, 7, 12, 0, 0, 18432, 82, }, /* 1014 */
+ { 85, 21, 12, 0, 0, 18432, 106, }, /* 1015 */
+ { 91, 7, 12, 0, 0, 18432, 82, }, /* 1016 */
+ { 91, 21, 12, 0, 0, 18432, 106, }, /* 1017 */
+ { 91, 14, 12, 0, 0, 18432, 82, }, /* 1018 */
+ { 83, 9, 12, 0, 40, 18432, 74, }, /* 1019 */
+ { 83, 5, 12, 0, -40, 18432, 76, }, /* 1020 */
+ { 86, 7, 12, 0, 0, 18432, 82, }, /* 1021 */
+ { 87, 7, 12, 0, 0, 18432, 82, }, /* 1022 */
+ { 87, 13, 12, 0, 0, 18432, 138, }, /* 1023 */
+ { 145, 9, 12, 0, 40, 18432, 74, }, /* 1024 */
+ { 145, 5, 12, 0, -40, 18432, 76, }, /* 1025 */
+ { 127, 7, 12, 0, 0, 18432, 82, }, /* 1026 */
+ { 125, 7, 12, 0, 0, 18432, 82, }, /* 1027 */
+ { 125, 21, 12, 0, 0, 18432, 68, }, /* 1028 */
+ { 161, 9, 12, 0, 39, 18432, 74, }, /* 1029 */
+ { 161, 5, 12, 0, -39, 18432, 76, }, /* 1030 */
+ { 49, 7, 12, 0, 0, 18432, 82, }, /* 1031 */
+ { 0, 6, 12, 0, 0, 18432, 94, }, /* 1032 */
+ { 32, 7, 12, 0, 0, 34816, 82, }, /* 1033 */
+ { 114, 7, 12, 0, 0, 34816, 82, }, /* 1034 */
+ { 114, 21, 12, 0, 0, 34816, 106, }, /* 1035 */
+ { 114, 15, 12, 0, 0, 34816, 68, }, /* 1036 */
+ { 133, 7, 12, 0, 0, 34816, 82, }, /* 1037 */
+ { 133, 26, 12, 0, 0, 34816, 68, }, /* 1038 */
+ { 133, 15, 12, 0, 0, 34816, 68, }, /* 1039 */
+ { 132, 7, 12, 0, 0, 34816, 82, }, /* 1040 */
+ { 132, 15, 12, 0, 0, 34816, 68, }, /* 1041 */
+ { 139, 7, 12, 0, 0, 34816, 82, }, /* 1042 */
+ { 139, 15, 12, 0, 0, 34816, 68, }, /* 1043 */
+ { 95, 7, 12, 0, 0, 34816, 82, }, /* 1044 */
+ { 95, 15, 12, 0, 0, 34816, 68, }, /* 1045 */
+ { 95, 21, 12, 0, 0, 28672, 106, }, /* 1046 */
+ { 104, 7, 12, 0, 0, 34816, 82, }, /* 1047 */
+ { 104, 21, 12, 0, 0, 34816, 68, }, /* 1048 */
+ { 122, 7, 12, 0, 0, 34816, 82, }, /* 1049 */
+ { 121, 7, 12, 0, 0, 34816, 82, }, /* 1050 */
+ { 121, 15, 12, 0, 0, 34816, 68, }, /* 1051 */
+ { 92, 7, 12, 0, 0, 34816, 82, }, /* 1052 */
+ { 92, 12, 3, 0, 0, 26624, 130, }, /* 1053 */
+ { 92, 12, 3, 0, 0, 26624, 102, }, /* 1054 */
+ { 92, 12, 3, 0, 0, 26624, 182, }, /* 1055 */
+ { 92, 15, 12, 0, 0, 34816, 68, }, /* 1056 */
+ { 92, 21, 12, 0, 0, 34816, 68, }, /* 1057 */
+ { 92, 21, 12, 0, 0, 34816, 124, }, /* 1058 */
+ { 115, 7, 12, 0, 0, 34816, 82, }, /* 1059 */
+ { 115, 15, 12, 0, 0, 34816, 68, }, /* 1060 */
+ { 115, 21, 12, 0, 0, 34816, 68, }, /* 1061 */
+ { 131, 7, 12, 0, 0, 34816, 82, }, /* 1062 */
+ { 131, 15, 12, 0, 0, 34816, 68, }, /* 1063 */
+ { 51, 7, 12, 0, 0, 34816, 82, }, /* 1064 */
+ { 51, 26, 12, 0, 0, 34816, 68, }, /* 1065 */
+ { 51, 12, 3, 0, 0, 26624, 96, }, /* 1066 */
+ { 51, 15, 12, 0, 0, 34816, 68, }, /* 1067 */
+ { 51, 21, 12, 0, 0, 34816, 106, }, /* 1068 */
+ { 51, 21, 12, 0, 0, 34918, 106, }, /* 1069 */
+ { 51, 21, 12, 0, 0, 34816, 68, }, /* 1070 */
+ { 108, 7, 12, 0, 0, 34816, 82, }, /* 1071 */
+ { 108, 21, 12, 0, 0, 28672, 68, }, /* 1072 */
+ { 108, 21, 12, 0, 0, 28672, 106, }, /* 1073 */
+ { 116, 7, 12, 0, 0, 34816, 82, }, /* 1074 */
+ { 116, 15, 12, 0, 0, 34816, 68, }, /* 1075 */
+ { 117, 7, 12, 0, 0, 34816, 82, }, /* 1076 */
+ { 117, 15, 12, 0, 0, 34816, 68, }, /* 1077 */
+ { 54, 7, 12, 0, 0, 34816, 82, }, /* 1078 */
+ { 54, 21, 12, 0, 0, 34816, 106, }, /* 1079 */
+ { 54, 15, 12, 0, 0, 34816, 68, }, /* 1080 */
+ { 118, 7, 12, 0, 0, 34816, 82, }, /* 1081 */
+ { 140, 9, 12, 0, 64, 34816, 74, }, /* 1082 */
+ { 140, 5, 12, 0, -64, 34816, 76, }, /* 1083 */
+ { 140, 15, 12, 0, 0, 34816, 68, }, /* 1084 */
+ { 62, 7, 12, 0, 0, 0, 82, }, /* 1085 */
+ { 62, 7, 12, 0, 0, 0, 294, }, /* 1086 */
+ { 62, 12, 3, 0, 0, 26624, 128, }, /* 1087 */
+ { 62, 13, 12, 0, 0, 2048, 138, }, /* 1088 */
+ { 3, 15, 12, 0, 0, 2048, 68, }, /* 1089 */
+ { 65, 7, 12, 0, 0, 34816, 82, }, /* 1090 */
+ { 65, 12, 3, 0, 0, 26624, 130, }, /* 1091 */
+ { 65, 17, 12, 0, 0, 34816, 126, }, /* 1092 */
+ { 152, 7, 12, 0, 0, 34816, 82, }, /* 1093 */
+ { 152, 15, 12, 0, 0, 34816, 68, }, /* 1094 */
+ { 63, 7, 12, 0, 0, 0, 82, }, /* 1095 */
+ { 63, 12, 3, 0, 0, 26624, 96, }, /* 1096 */
+ { 63, 15, 12, 0, 0, 0, 68, }, /* 1097 */
+ { 63, 21, 12, 0, 0, 0, 124, }, /* 1098 */
+ { 67, 7, 12, 0, 0, 34816, 82, }, /* 1099 */
+ { 67, 12, 3, 0, 0, 26624, 96, }, /* 1100 */
+ { 67, 21, 12, 0, 0, 34816, 124, }, /* 1101 */
+ { 156, 7, 12, 0, 0, 34816, 82, }, /* 1102 */
+ { 156, 15, 12, 0, 0, 34816, 68, }, /* 1103 */
+ { 153, 7, 12, 0, 0, 34816, 82, }, /* 1104 */
+ { 120, 10, 5, 0, 0, 18432, 144, }, /* 1105 */
+ { 120, 12, 3, 0, 0, 26624, 130, }, /* 1106 */
+ { 120, 7, 12, 0, 0, 18432, 82, }, /* 1107 */
+ { 120, 12, 3, 0, 0, 26624, 146, }, /* 1108 */
+ { 120, 21, 12, 0, 0, 18432, 124, }, /* 1109 */
+ { 120, 21, 12, 0, 0, 18432, 106, }, /* 1110 */
+ { 120, 15, 12, 0, 0, 28672, 68, }, /* 1111 */
+ { 120, 13, 12, 0, 0, 18432, 138, }, /* 1112 */
+ { 120, 12, 3, 0, 0, 26624, 182, }, /* 1113 */
+ { 41, 12, 3, 0, 0, 26624, 102, }, /* 1114 */
+ { 41, 10, 5, 0, 0, 18432, 144, }, /* 1115 */
+ { 41, 7, 12, 0, 0, 18432, 82, }, /* 1116 */
+ { 41, 12, 3, 0, 0, 26624, 130, }, /* 1117 */
+ { 41, 12, 3, 0, 0, 26624, 146, }, /* 1118 */
+ { 41, 12, 3, 0, 0, 26624, 96, }, /* 1119 */
+ { 41, 21, 12, 0, 0, 18432, 68, }, /* 1120 */
+ { 41, 1, 4, 0, 0, 18432, 132, }, /* 1121 */
+ { 41, 21, 12, 0, 0, 18432, 124, }, /* 1122 */
+ { 124, 7, 12, 0, 0, 18432, 82, }, /* 1123 */
+ { 124, 13, 12, 0, 0, 18432, 138, }, /* 1124 */
+ { 43, 12, 3, 0, 0, 26624, 130, }, /* 1125 */
+ { 43, 7, 12, 0, 0, 18432, 82, }, /* 1126 */
+ { 43, 10, 5, 0, 0, 18432, 144, }, /* 1127 */
+ { 43, 12, 3, 0, 0, 26624, 146, }, /* 1128 */
+ { 43, 13, 12, 0, 0, 18432, 138, }, /* 1129 */
+ { 43, 21, 12, 0, 0, 18432, 68, }, /* 1130 */
+ { 43, 21, 12, 0, 0, 18432, 124, }, /* 1131 */
+ { 50, 7, 12, 0, 0, 18432, 82, }, /* 1132 */
+ { 50, 12, 3, 0, 0, 26624, 96, }, /* 1133 */
+ { 50, 21, 12, 0, 0, 18432, 68, }, /* 1134 */
+ { 44, 12, 3, 0, 0, 26624, 130, }, /* 1135 */
+ { 44, 10, 5, 0, 0, 18432, 144, }, /* 1136 */
+ { 44, 7, 12, 0, 0, 18432, 82, }, /* 1137 */
+ { 44, 10, 5, 0, 0, 18432, 172, }, /* 1138 */
+ { 44, 7, 4, 0, 0, 18432, 82, }, /* 1139 */
+ { 44, 21, 12, 0, 0, 18432, 124, }, /* 1140 */
+ { 44, 21, 12, 0, 0, 18432, 68, }, /* 1141 */
+ { 44, 12, 3, 0, 0, 26624, 102, }, /* 1142 */
+ { 44, 12, 3, 0, 0, 26624, 96, }, /* 1143 */
+ { 44, 13, 12, 0, 0, 18432, 138, }, /* 1144 */
+ { 15, 15, 12, 0, 0, 18432, 68, }, /* 1145 */
+ { 48, 7, 12, 0, 0, 18432, 82, }, /* 1146 */
+ { 48, 10, 5, 0, 0, 18432, 144, }, /* 1147 */
+ { 48, 12, 3, 0, 0, 26624, 130, }, /* 1148 */
+ { 48, 10, 5, 0, 0, 18432, 172, }, /* 1149 */
+ { 48, 12, 3, 0, 0, 26624, 96, }, /* 1150 */
+ { 48, 21, 12, 0, 0, 18432, 124, }, /* 1151 */
+ { 48, 21, 12, 0, 0, 18432, 106, }, /* 1152 */
+ { 48, 21, 12, 0, 0, 18432, 68, }, /* 1153 */
+ { 57, 7, 12, 0, 0, 18432, 82, }, /* 1154 */
+ { 57, 21, 12, 0, 0, 18432, 124, }, /* 1155 */
+ { 55, 7, 12, 0, 0, 18432, 82, }, /* 1156 */
+ { 55, 12, 3, 0, 0, 26624, 130, }, /* 1157 */
+ { 55, 10, 5, 0, 0, 18432, 144, }, /* 1158 */
+ { 55, 12, 3, 0, 0, 26624, 96, }, /* 1159 */
+ { 55, 12, 3, 0, 0, 26624, 146, }, /* 1160 */
+ { 55, 13, 12, 0, 0, 18432, 138, }, /* 1161 */
+ { 47, 12, 3, 0, 0, 26624, 130, }, /* 1162 */
+ { 47, 12, 3, 0, 0, 26705, 130, }, /* 1163 */
+ { 47, 10, 5, 0, 0, 18432, 144, }, /* 1164 */
+ { 47, 10, 5, 0, 0, 18513, 144, }, /* 1165 */
+ { 47, 7, 12, 0, 0, 18432, 82, }, /* 1166 */
+ { 84, 12, 3, 0, 0, 26705, 102, }, /* 1167 */
+ { 47, 12, 3, 0, 0, 26705, 96, }, /* 1168 */
+ { 47, 10, 3, 0, 0, 18432, 148, }, /* 1169 */
+ { 47, 10, 5, 0, 0, 18432, 172, }, /* 1170 */
+ { 47, 7, 12, 0, 0, 18432, 324, }, /* 1171 */
+ { 47, 12, 3, 0, 0, 26624, 96, }, /* 1172 */
+ { 144, 7, 12, 0, 0, 18432, 82, }, /* 1173 */
+ { 144, 10, 5, 0, 0, 18432, 144, }, /* 1174 */
+ { 144, 12, 3, 0, 0, 26624, 130, }, /* 1175 */
+ { 144, 12, 3, 0, 0, 26624, 146, }, /* 1176 */
+ { 144, 12, 3, 0, 0, 26624, 96, }, /* 1177 */
+ { 144, 21, 12, 0, 0, 18432, 124, }, /* 1178 */
+ { 144, 21, 12, 0, 0, 18432, 106, }, /* 1179 */
+ { 144, 21, 12, 0, 0, 18432, 68, }, /* 1180 */
+ { 144, 13, 12, 0, 0, 18432, 138, }, /* 1181 */
+ { 144, 12, 3, 0, 0, 26624, 102, }, /* 1182 */
+ { 56, 7, 12, 0, 0, 18432, 82, }, /* 1183 */
+ { 56, 10, 3, 0, 0, 18432, 148, }, /* 1184 */
+ { 56, 10, 5, 0, 0, 18432, 144, }, /* 1185 */
+ { 56, 12, 3, 0, 0, 26624, 130, }, /* 1186 */
+ { 56, 12, 3, 0, 0, 26624, 146, }, /* 1187 */
+ { 56, 12, 3, 0, 0, 26624, 96, }, /* 1188 */
+ { 56, 21, 12, 0, 0, 18432, 68, }, /* 1189 */
+ { 56, 13, 12, 0, 0, 18432, 138, }, /* 1190 */
+ { 135, 7, 12, 0, 0, 18432, 82, }, /* 1191 */
+ { 135, 10, 3, 0, 0, 18432, 148, }, /* 1192 */
+ { 135, 10, 5, 0, 0, 18432, 144, }, /* 1193 */
+ { 135, 12, 3, 0, 0, 26624, 130, }, /* 1194 */
+ { 135, 12, 3, 0, 0, 26624, 146, }, /* 1195 */
+ { 135, 12, 3, 0, 0, 26624, 96, }, /* 1196 */
+ { 135, 21, 12, 0, 0, 18432, 68, }, /* 1197 */
+ { 135, 21, 12, 0, 0, 18432, 124, }, /* 1198 */
+ { 135, 21, 12, 0, 0, 18432, 106, }, /* 1199 */
+ { 135, 21, 12, 0, 0, 18432, 176, }, /* 1200 */
+ { 52, 7, 12, 0, 0, 18432, 82, }, /* 1201 */
+ { 52, 10, 5, 0, 0, 18432, 144, }, /* 1202 */
+ { 52, 12, 3, 0, 0, 26624, 130, }, /* 1203 */
+ { 52, 12, 3, 0, 0, 26624, 146, }, /* 1204 */
+ { 52, 21, 12, 0, 0, 18432, 124, }, /* 1205 */
+ { 52, 21, 12, 0, 0, 18432, 68, }, /* 1206 */
+ { 52, 13, 12, 0, 0, 18432, 138, }, /* 1207 */
+ { 45, 7, 12, 0, 0, 18432, 82, }, /* 1208 */
+ { 45, 12, 3, 0, 0, 26624, 130, }, /* 1209 */
+ { 45, 10, 5, 0, 0, 18432, 144, }, /* 1210 */
+ { 45, 10, 5, 0, 0, 18432, 172, }, /* 1211 */
+ { 45, 12, 3, 0, 0, 26624, 96, }, /* 1212 */
+ { 45, 21, 12, 0, 0, 18432, 68, }, /* 1213 */
+ { 45, 13, 12, 0, 0, 18432, 138, }, /* 1214 */
+ { 137, 7, 12, 0, 0, 18432, 82, }, /* 1215 */
+ { 137, 12, 3, 0, 0, 26624, 130, }, /* 1216 */
+ { 137, 10, 12, 0, 0, 18432, 144, }, /* 1217 */
+ { 137, 10, 5, 0, 0, 18432, 144, }, /* 1218 */
+ { 137, 12, 3, 0, 0, 26624, 146, }, /* 1219 */
+ { 137, 13, 12, 0, 0, 18432, 138, }, /* 1220 */
+ { 137, 15, 12, 0, 0, 18432, 68, }, /* 1221 */
+ { 137, 21, 12, 0, 0, 18432, 124, }, /* 1222 */
+ { 137, 26, 12, 0, 0, 18432, 68, }, /* 1223 */
+ { 60, 7, 12, 0, 0, 18432, 82, }, /* 1224 */
+ { 60, 10, 5, 0, 0, 18432, 144, }, /* 1225 */
+ { 60, 12, 3, 0, 0, 26624, 130, }, /* 1226 */
+ { 60, 12, 3, 0, 0, 26624, 146, }, /* 1227 */
+ { 60, 12, 3, 0, 0, 26624, 96, }, /* 1228 */
+ { 60, 21, 12, 0, 0, 18432, 68, }, /* 1229 */
+ { 136, 9, 12, 0, 32, 18432, 74, }, /* 1230 */
+ { 136, 5, 12, 0, -32, 18432, 76, }, /* 1231 */
+ { 136, 13, 12, 0, 0, 18432, 138, }, /* 1232 */
+ { 136, 15, 12, 0, 0, 18432, 68, }, /* 1233 */
+ { 136, 7, 12, 0, 0, 18432, 82, }, /* 1234 */
+ { 157, 7, 12, 0, 0, 18432, 82, }, /* 1235 */
+ { 157, 10, 3, 0, 0, 18432, 148, }, /* 1236 */
+ { 157, 10, 5, 0, 0, 18432, 144, }, /* 1237 */
+ { 157, 12, 3, 0, 0, 26624, 130, }, /* 1238 */
+ { 157, 10, 5, 0, 0, 18432, 172, }, /* 1239 */
+ { 157, 12, 3, 0, 0, 26624, 146, }, /* 1240 */
+ { 157, 7, 4, 0, 0, 18432, 82, }, /* 1241 */
+ { 157, 12, 3, 0, 0, 26624, 96, }, /* 1242 */
+ { 157, 21, 12, 0, 0, 18432, 124, }, /* 1243 */
+ { 157, 21, 12, 0, 0, 18432, 68, }, /* 1244 */
+ { 157, 13, 12, 0, 0, 18432, 138, }, /* 1245 */
+ { 64, 7, 12, 0, 0, 18432, 82, }, /* 1246 */
+ { 64, 10, 5, 0, 0, 18432, 144, }, /* 1247 */
+ { 64, 12, 3, 0, 0, 26624, 130, }, /* 1248 */
+ { 64, 12, 3, 0, 0, 26624, 146, }, /* 1249 */
+ { 64, 21, 12, 0, 0, 18432, 68, }, /* 1250 */
+ { 149, 7, 12, 0, 0, 18432, 82, }, /* 1251 */
+ { 149, 12, 3, 0, 0, 26624, 130, }, /* 1252 */
+ { 149, 12, 3, 0, 0, 18432, 130, }, /* 1253 */
+ { 149, 12, 3, 0, 0, 26624, 102, }, /* 1254 */
+ { 149, 12, 3, 0, 0, 26624, 146, }, /* 1255 */
+ { 149, 10, 5, 0, 0, 18432, 144, }, /* 1256 */
+ { 149, 7, 4, 0, 0, 18432, 82, }, /* 1257 */
+ { 149, 21, 12, 0, 0, 18432, 68, }, /* 1258 */
+ { 149, 21, 12, 0, 0, 18432, 124, }, /* 1259 */
+ { 148, 7, 12, 0, 0, 18432, 82, }, /* 1260 */
+ { 148, 12, 3, 0, 0, 26624, 130, }, /* 1261 */
+ { 148, 10, 5, 0, 0, 18432, 144, }, /* 1262 */
+ { 148, 7, 4, 0, 0, 18432, 82, }, /* 1263 */
+ { 148, 12, 3, 0, 0, 26624, 326, }, /* 1264 */
+ { 148, 12, 3, 0, 0, 26624, 146, }, /* 1265 */
+ { 148, 21, 12, 0, 0, 18432, 68, }, /* 1266 */
+ { 148, 21, 12, 0, 0, 18432, 124, }, /* 1267 */
+ { 148, 21, 12, 0, 0, 18432, 106, }, /* 1268 */
+ { 134, 7, 12, 0, 0, 18432, 82, }, /* 1269 */
+ { 142, 7, 12, 0, 0, 18432, 82, }, /* 1270 */
+ { 142, 10, 5, 0, 0, 18432, 144, }, /* 1271 */
+ { 142, 12, 3, 0, 0, 26624, 130, }, /* 1272 */
+ { 142, 12, 3, 0, 0, 18432, 146, }, /* 1273 */
+ { 142, 21, 12, 0, 0, 18432, 124, }, /* 1274 */
+ { 142, 21, 12, 0, 0, 18432, 106, }, /* 1275 */
+ { 142, 21, 12, 0, 0, 18432, 68, }, /* 1276 */
+ { 142, 13, 12, 0, 0, 18432, 138, }, /* 1277 */
+ { 142, 15, 12, 0, 0, 18432, 68, }, /* 1278 */
+ { 143, 21, 12, 0, 0, 18432, 68, }, /* 1279 */
+ { 143, 21, 12, 0, 0, 18432, 106, }, /* 1280 */
+ { 143, 7, 12, 0, 0, 18432, 82, }, /* 1281 */
+ { 143, 12, 3, 0, 0, 26624, 130, }, /* 1282 */
+ { 143, 10, 5, 0, 0, 18432, 144, }, /* 1283 */
+ { 59, 7, 12, 0, 0, 18432, 82, }, /* 1284 */
+ { 59, 12, 3, 0, 0, 26624, 130, }, /* 1285 */
+ { 59, 12, 3, 0, 0, 26624, 96, }, /* 1286 */
+ { 59, 12, 3, 0, 0, 26624, 146, }, /* 1287 */
+ { 59, 7, 4, 0, 0, 18432, 82, }, /* 1288 */
+ { 59, 13, 12, 0, 0, 18432, 138, }, /* 1289 */
+ { 61, 7, 12, 0, 0, 18432, 82, }, /* 1290 */
+ { 61, 10, 5, 0, 0, 18432, 144, }, /* 1291 */
+ { 61, 12, 3, 0, 0, 26624, 130, }, /* 1292 */
+ { 61, 12, 3, 0, 0, 26624, 146, }, /* 1293 */
+ { 61, 13, 12, 0, 0, 18432, 138, }, /* 1294 */
+ { 150, 7, 12, 0, 0, 18432, 82, }, /* 1295 */
+ { 150, 12, 3, 0, 0, 26624, 130, }, /* 1296 */
+ { 150, 10, 5, 0, 0, 18432, 144, }, /* 1297 */
+ { 150, 21, 12, 0, 0, 18432, 124, }, /* 1298 */
+ { 11, 15, 12, 0, 0, 18432, 68, }, /* 1299 */
+ { 11, 21, 12, 0, 0, 18432, 68, }, /* 1300 */
+ { 94, 7, 12, 0, 0, 18432, 82, }, /* 1301 */
+ { 94, 14, 12, 0, 0, 18432, 82, }, /* 1302 */
+ { 94, 21, 12, 0, 0, 18432, 106, }, /* 1303 */
+ { 66, 7, 12, 0, 0, 18432, 82, }, /* 1304 */
+ { 66, 21, 12, 0, 0, 18432, 68, }, /* 1305 */
+ { 109, 7, 12, 0, 0, 18432, 82, }, /* 1306 */
+ { 109, 1, 2, 0, 0, 18432, 322, }, /* 1307 */
+ { 138, 7, 12, 0, 0, 18432, 82, }, /* 1308 */
+ { 130, 7, 12, 0, 0, 18432, 82, }, /* 1309 */
+ { 130, 13, 12, 0, 0, 18432, 138, }, /* 1310 */
+ { 130, 21, 12, 0, 0, 18432, 124, }, /* 1311 */
+ { 159, 7, 12, 0, 0, 18432, 82, }, /* 1312 */
+ { 159, 13, 12, 0, 0, 18432, 138, }, /* 1313 */
+ { 126, 7, 12, 0, 0, 18432, 82, }, /* 1314 */
+ { 126, 12, 3, 0, 0, 26624, 96, }, /* 1315 */
+ { 126, 21, 12, 0, 0, 18432, 124, }, /* 1316 */
+ { 128, 7, 12, 0, 0, 18432, 82, }, /* 1317 */
+ { 128, 12, 3, 0, 0, 26624, 96, }, /* 1318 */
+ { 128, 21, 12, 0, 0, 18432, 124, }, /* 1319 */
+ { 128, 21, 12, 0, 0, 18432, 106, }, /* 1320 */
+ { 128, 21, 12, 0, 0, 18432, 68, }, /* 1321 */
+ { 128, 26, 12, 0, 0, 18432, 68, }, /* 1322 */
+ { 128, 6, 12, 0, 0, 18432, 142, }, /* 1323 */
+ { 128, 6, 12, 0, 0, 18432, 136, }, /* 1324 */
+ { 128, 13, 12, 0, 0, 18432, 138, }, /* 1325 */
+ { 128, 15, 12, 0, 0, 18432, 68, }, /* 1326 */
+ { 151, 9, 12, 0, 32, 18432, 74, }, /* 1327 */
+ { 151, 5, 12, 0, -32, 18432, 76, }, /* 1328 */
+ { 151, 15, 12, 0, 0, 18432, 68, }, /* 1329 */
+ { 151, 21, 12, 0, 0, 18432, 106, }, /* 1330 */
+ { 151, 21, 12, 0, 0, 18432, 124, }, /* 1331 */
+ { 151, 21, 12, 0, 0, 18432, 68, }, /* 1332 */
+ { 123, 7, 12, 0, 0, 18432, 82, }, /* 1333 */
+ { 123, 12, 3, 0, 0, 26624, 130, }, /* 1334 */
+ { 123, 10, 5, 0, 0, 18432, 144, }, /* 1335 */
+ { 123, 12, 3, 0, 0, 26624, 128, }, /* 1336 */
+ { 123, 6, 12, 0, 0, 18432, 92, }, /* 1337 */
+ { 146, 6, 12, 0, 0, 18432, 136, }, /* 1338 */
+ { 147, 6, 12, 0, 0, 18432, 136, }, /* 1339 */
+ { 23, 21, 12, 0, 0, 28672, 68, }, /* 1340 */
+ { 158, 12, 3, 0, 0, 26624, 328, }, /* 1341 */
+ { 23, 10, 5, 0, 0, 18432, 164, }, /* 1342 */
+ { 146, 7, 12, 0, 0, 18432, 284, }, /* 1343 */
+ { 158, 7, 12, 0, 0, 18432, 284, }, /* 1344 */
+ { 21, 6, 12, 0, 0, 18432, 92, }, /* 1345 */
+ { 147, 7, 12, 0, 0, 18432, 284, }, /* 1346 */
+ { 46, 7, 12, 0, 0, 18432, 82, }, /* 1347 */
+ { 46, 26, 12, 0, 0, 18432, 68, }, /* 1348 */
+ { 46, 12, 3, 0, 0, 26624, 102, }, /* 1349 */
+ { 46, 12, 3, 0, 0, 26624, 130, }, /* 1350 */
+ { 46, 21, 12, 0, 0, 18432, 124, }, /* 1351 */
+ { 69, 1, 2, 0, 0, 6153, 66, }, /* 1352 */
+ { 69, 10, 3, 0, 0, 18432, 330, }, /* 1353 */
+ { 69, 10, 5, 0, 0, 18432, 138, }, /* 1354 */
+ { 69, 10, 5, 0, 0, 18432, 160, }, /* 1355 */
+ { 69, 10, 3, 0, 0, 18432, 286, }, /* 1356 */
+ { 1, 12, 3, 0, 0, 26624, 102, }, /* 1357 */
+ { 69, 25, 12, 0, 0, 18432, 118, }, /* 1358 */
+ { 69, 13, 12, 0, 0, 10240, 214, }, /* 1359 */
+ { 141, 26, 12, 0, 0, 18432, 68, }, /* 1360 */
+ { 141, 12, 3, 0, 0, 26624, 102, }, /* 1361 */
+ { 141, 21, 12, 0, 0, 18432, 106, }, /* 1362 */
+ { 141, 21, 12, 0, 0, 18432, 124, }, /* 1363 */
+ { 141, 21, 12, 0, 0, 18432, 68, }, /* 1364 */
+ { 35, 12, 3, 0, 0, 26624, 130, }, /* 1365 */
+ { 154, 7, 12, 0, 0, 18432, 82, }, /* 1366 */
+ { 154, 12, 3, 0, 0, 26624, 96, }, /* 1367 */
+ { 154, 6, 12, 0, 0, 18432, 142, }, /* 1368 */
+ { 154, 6, 12, 0, 0, 18432, 136, }, /* 1369 */
+ { 154, 13, 12, 0, 0, 18432, 138, }, /* 1370 */
+ { 154, 26, 12, 0, 0, 18432, 68, }, /* 1371 */
+ { 160, 7, 12, 0, 0, 18432, 82, }, /* 1372 */
+ { 160, 12, 3, 0, 0, 26624, 96, }, /* 1373 */
+ { 155, 7, 12, 0, 0, 18432, 82, }, /* 1374 */
+ { 155, 12, 3, 0, 0, 26624, 96, }, /* 1375 */
+ { 155, 13, 12, 0, 0, 18432, 138, }, /* 1376 */
+ { 155, 23, 12, 0, 0, 14336, 68, }, /* 1377 */
+ { 129, 7, 12, 0, 0, 34816, 82, }, /* 1378 */
+ { 129, 15, 12, 0, 0, 34816, 68, }, /* 1379 */
+ { 129, 12, 3, 0, 0, 26624, 96, }, /* 1380 */
+ { 58, 9, 12, 0, 34, 34816, 74, }, /* 1381 */
+ { 58, 5, 12, 0, -34, 34816, 76, }, /* 1382 */
+ { 58, 12, 3, 0, 0, 26624, 150, }, /* 1383 */
+ { 58, 12, 3, 0, 0, 26624, 130, }, /* 1384 */
+ { 58, 12, 3, 0, 0, 26624, 96, }, /* 1385 */
+ { 58, 6, 12, 0, 0, 34816, 142, }, /* 1386 */
+ { 58, 13, 12, 0, 0, 34816, 138, }, /* 1387 */
+ { 58, 21, 12, 0, 0, 34816, 68, }, /* 1388 */
+ { 69, 15, 12, 0, 0, 0, 68, }, /* 1389 */
+ { 69, 26, 12, 0, 0, 0, 68, }, /* 1390 */
+ { 69, 23, 12, 0, 0, 0, 68, }, /* 1391 */
+ { 3, 7, 12, 0, 0, 0, 240, }, /* 1392 */
+ { 69, 26, 14, 0, 0, 28672, 332, }, /* 1393 */
+ { 69, 26, 14, 0, 0, 28672, 334, }, /* 1394 */
+ { 68, 2, 14, 0, 0, 18432, 336, }, /* 1395 */
+ { 69, 26, 12, 0, 0, 18432, 338, }, /* 1396 */
+ { 69, 26, 14, 0, 0, 18432, 340, }, /* 1397 */
+ { 69, 26, 14, 0, 0, 18432, 334, }, /* 1398 */
+ { 69, 26, 11, 0, 0, 18432, 342, }, /* 1399 */
+ { 20, 26, 12, 0, 0, 18432, 68, }, /* 1400 */
+ { 69, 26, 14, 0, 0, 18432, 236, }, /* 1401 */
+ { 69, 26, 14, 0, 0, 18447, 334, }, /* 1402 */
+ { 69, 26, 14, 0, 0, 28672, 344, }, /* 1403 */
+ { 69, 26, 14, 0, 0, 28672, 346, }, /* 1404 */
+ { 69, 24, 3, 0, 0, 28672, 348, }, /* 1405 */
+ { 69, 26, 14, 0, 0, 28672, 350, }, /* 1406 */
+ { 69, 13, 12, 0, 0, 10240, 138, }, /* 1407 */
+ { 69, 1, 3, 0, 0, 6144, 352, }, /* 1408 */
+};
+
+const uint16_t PRIV(ucd_stage1)[] = { /* 17408 bytes */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* U+0000 */
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* U+0800 */
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 41, 41, 42, 43, 44, 45, /* U+1000 */
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* U+1800 */
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, /* U+2000 */
+ 78, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, /* U+2800 */
+ 93, 94, 95, 96, 97, 98, 99,100,101,101,101,101,101,101,101,101, /* U+3000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+3800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+4000 */
+101,101,101,101,101,101,101,101,101,101,101,102,101,101,101,101, /* U+4800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+5000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+5800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+6000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+6800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+7000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+7800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+8000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+8800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+9000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+9800 */
+103,104,104,104,104,104,104,104,104,105,106,106,107,108,109,110, /* U+A000 */
+111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,119, /* U+A800 */
+120,121,122,123,124,125,119,120,121,122,123,124,125,119,120,121, /* U+B000 */
+122,123,124,125,119,120,121,122,123,124,125,119,120,121,122,123, /* U+B800 */
+124,125,119,120,121,122,123,124,125,119,120,121,122,123,124,125, /* U+C000 */
+119,120,121,122,123,124,125,119,120,121,122,123,124,125,119,120, /* U+C800 */
+121,122,123,124,125,119,120,121,122,123,124,125,119,120,121,126, /* U+D000 */
+127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127, /* U+D800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+E000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+E800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F000 */
+128,128,129,129,130,131,132,133,134,135,136,137,138,139,140,141, /* U+F800 */
+142,143,144,145,146,147,148,149,150,151,152,153,154,154,155,156, /* U+10000 */
+157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172, /* U+10800 */
+173,174,175,176,177,178,179,146,180,181,146,182,183,184,185,146, /* U+11000 */
+186,187,188,189,190,191,146,146,192,193,194,195,146,196,146,197, /* U+11800 */
+198,198,198,198,198,198,198,199,200,198,201,146,146,146,146,146, /* U+12000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,202, /* U+12800 */
+203,203,203,203,203,203,203,203,204,146,146,146,146,146,146,146, /* U+13000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+13800 */
+146,146,146,146,146,146,146,146,205,205,205,205,206,146,146,146, /* U+14000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+14800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+15000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+15800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+16000 */
+207,207,207,207,208,209,210,211,146,146,146,146,212,213,214,215, /* U+16800 */
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, /* U+17000 */
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,216, /* U+17800 */
+216,216,216,216,216,216,216,216,216,216,216,216,216,216,216,217, /* U+18000 */
+216,216,216,216,216,216,218,218,218,219,220,146,146,146,146,146, /* U+18800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+19000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+19800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+1A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,221, /* U+1A800 */
+222,223,224,225,225,226,146,146,146,146,146,146,146,146,146,146, /* U+1B000 */
+146,146,146,146,146,146,146,146,227,228,146,146,146,146,146,146, /* U+1B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+1C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,229,230, /* U+1C800 */
+231,232,233,234,235,236,237,146,238,239,240,241,242,243,244,245, /* U+1D000 */
+246,246,246,246,247,248,146,146,146,146,146,146,146,146,249,146, /* U+1D800 */
+250,146,251,146,146,252,146,146,146,146,146,146,146,146,146,253, /* U+1E000 */
+254,255,256,168,168,168,168,168,257,258,259,168,260,261,168,168, /* U+1E800 */
+262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277, /* U+1F000 */
+278,279,280,281,282,283,284,285,267,267,267,267,267,267,267,286, /* U+1F800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+20000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+20800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+21000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+21800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+22000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+22800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+23000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+23800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+24000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+24800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+25000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+25800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+26000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+26800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+27000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+27800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+28000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+28800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+29000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+29800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,287,101,101, /* U+2A000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2A800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,288,101, /* U+2B000 */
+289,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2B800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2C000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,290,101,101, /* U+2C800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2D000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2D800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+2E000 */
+101,101,101,101,101,101,101,291,146,146,146,146,146,146,146,146, /* U+2E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+2F000 */
+129,129,129,129,292,146,146,146,146,146,146,146,146,146,146,293, /* U+2F800 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+30000 */
+101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, /* U+30800 */
+101,101,101,101,101,101,294,146,146,146,146,146,146,146,146,146, /* U+31000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+31800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+32000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+32800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+33000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+33800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+34000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+34800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+35000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+35800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+36000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+36800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+37000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+37800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+38000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+38800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+39000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+39800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+3F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+3F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+40000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+40800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+41000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+41800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+42000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+42800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+43000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+43800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+44000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+44800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+45000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+45800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+46000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+46800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+47000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+47800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+48000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+48800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+49000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+49800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+4F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+4F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+50000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+50800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+51000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+51800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+52000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+52800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+53000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+53800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+54000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+54800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+55000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+55800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+56000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+56800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+57000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+57800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+58000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+58800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+59000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+59800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+5F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+5F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+60000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+60800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+61000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+61800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+62000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+62800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+63000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+63800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+64000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+64800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+65000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+65800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+66000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+66800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+67000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+67800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+68000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+68800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+69000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+69800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+6F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+6F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+70000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+70800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+71000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+71800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+72000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+72800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+73000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+73800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+74000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+74800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+75000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+75800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+76000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+76800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+77000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+77800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+78000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+78800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+79000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+79800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+7F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+7F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+80000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+80800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+81000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+81800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+82000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+82800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+83000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+83800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+84000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+84800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+85000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+85800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+86000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+86800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+87000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+87800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+88000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+88800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+89000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+89800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+8F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+8F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+90000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+90800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+91000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+91800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+92000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+92800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+93000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+93800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+94000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+94800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+95000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+95800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+96000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+96800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+97000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+97800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+98000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+98800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+99000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+99800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9A000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9A800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9B000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9B800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9C000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9C800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9D000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9D800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9E000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9E800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+9F000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+9F800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A0000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+A9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AD000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AD800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+AF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+AF800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B0000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+B9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BD000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BD800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+BF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+BF800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C0000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+C9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CD000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CD800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+CF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+CF800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D0000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+D9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DD000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DD800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+DF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+DF800 */
+295,296,297,298,296,296,296,296,296,296,296,296,296,296,296,296, /* U+E0000 */
+296,296,296,296,296,296,296,296,296,296,296,296,296,296,296,296, /* U+E0800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E1000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E1800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E2000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E2800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E3000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E3800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E4000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E4800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E5000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E5800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E6000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E6800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E7000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E7800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E8000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E8800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E9000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+E9800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EA000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EA800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EB000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EB800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EC000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EC800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+ED000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+ED800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EE000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EE800 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, /* U+EF000 */
+146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,293, /* U+EF800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F0000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F0800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F1000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F1800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F2000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F2800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F3000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F3800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F4000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F4800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F5000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F5800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F6000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F6800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F7000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F7800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F8000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F8800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F9000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+F9800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FA000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FA800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FB000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FB800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FC000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FC800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FD000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FD800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FE000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FE800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+FF000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,299, /* U+FF800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+100000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+100800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+101000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+101800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+102000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+102800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+103000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+103800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+104000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+104800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+105000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+105800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+106000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+106800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+107000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+107800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+108000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+108800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+109000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+109800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10A000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10A800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10B000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10B800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10C000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10C800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10D000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10D800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10E000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10E800 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, /* U+10F000 */
+128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,299, /* U+10F800 */
+};
+
+const uint16_t PRIV(ucd_stage2)[] = { /* 76800 bytes, block = 128 */
+
+/* block 0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 3, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 25, 26, 27, 26, 8,
+ 13, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 30, 29, 29, 29, 29,
+ 29, 29, 29, 31, 29, 29, 29, 29, 29, 29, 29, 15, 13, 16, 32, 33,
+ 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 38, 36, 36, 36, 36,
+ 36, 36, 36, 39, 36, 36, 36, 36, 36, 36, 36, 15, 27, 16, 27, 0,
+
+/* block 1 */
+ 40, 40, 40, 40, 40, 41, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+ 42, 43, 44, 44, 44, 44, 45, 43, 46, 47, 48, 49, 50, 51, 47, 46,
+ 52, 53, 54, 54, 46, 55, 43, 56, 46, 54, 48, 57, 58, 58, 58, 43,
+ 59, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 50, 59, 59, 59, 59, 59, 59, 59, 61,
+ 62, 62, 62, 62, 62, 63, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 50, 62, 62, 62, 62, 62, 62, 62, 64,
+
+/* block 2 */
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67,
+ 68, 69, 65, 66, 65, 66, 65, 66, 70, 65, 66, 65, 66, 65, 66, 65,
+ 66, 65, 66, 65, 66, 65, 66, 65, 66, 71, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 72, 65, 66, 65, 66, 65, 66, 73,
+
+/* block 3 */
+ 74, 75, 65, 66, 65, 66, 76, 65, 66, 77, 77, 65, 66, 70, 78, 79,
+ 80, 65, 66, 77, 81, 82, 83, 84, 65, 66, 85, 70, 83, 86, 87, 88,
+ 65, 66, 65, 66, 65, 66, 89, 65, 66, 89, 70, 70, 65, 66, 89, 65,
+ 66, 90, 90, 65, 66, 65, 66, 91, 65, 66, 70, 92, 65, 66, 70, 93,
+ 92, 92, 92, 92, 94, 95, 96, 97, 98, 99,100,101,102, 65, 66, 65,
+ 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,103, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 69,104,105,106, 65, 66,107,108, 65, 66, 65, 66, 65, 66, 65, 66,
+
+/* block 4 */
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+109, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 70, 70, 70, 70, 70, 70,110, 65, 66,111,112,113,
+113, 65, 66,114,115,116, 65, 66, 65, 67, 65, 66, 65, 66, 65, 66,
+117,118,119,120,121, 70,122,122, 70,123, 70,124,125, 70, 70, 70,
+122,126, 70,127, 70,128,129, 70,130,131,129,132,133, 70, 70,131,
+ 70,134,135, 70, 70,136, 70, 70, 70, 70, 70, 70, 70,137, 70, 70,
+
+/* block 5 */
+138, 70,139,138, 70, 70, 70,140,138,141,142,142,143, 70, 70, 70,
+ 70, 70,144, 70, 92, 70, 70, 70, 70, 70, 70, 70, 70,145,146, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+147,147,148,147,147,147,147,147,147,149,149,150,150,150,150,150,
+151,151, 46, 46, 46, 46,149,149,149,149,149,149,149,149,149,149,
+152,152, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+147,147,147,147,147, 46, 46, 46, 46, 46,153,153,149, 46,150, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+
+/* block 6 */
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,155,154,154,156,154,154,154,154,154,154,154,154,154,157,
+154,154,154,154,154,154,154,154,158,158,158,158,158,154,154,154,
+154,154,154,159,159,159,159,159,159,159,159,159,159,159,159,159,
+160,161,160,161,149,162,160,161,163,163,164,165,165,165,166,167,
+
+/* block 7 */
+163,163,163,163,162, 46,168,169,170,170,170,163,171,163,172,172,
+173,174,175,174,174,176,174,174,177,178,179,174,180,174,174,174,
+181,182,163,183,174,174,184,174,174,185,174,174,186,187,187,187,
+173,188,189,188,188,190,188,188,191,192,193,188,194,188,188,188,
+195,196,197,198,188,188,199,188,188,200,188,188,201,202,202,203,
+204,205,206,207,207,208,209,210,160,161,160,161,160,161,160,161,
+160,161,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+213,214,215,216,217,218,219,160,161,220,160,161,221,222,222,222,
+
+/* block 8 */
+223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,
+224,224,225,224,226,224,224,224,224,224,224,224,224,224,227,224,
+224,228,229,224,224,224,224,224,224,224,230,224,224,224,224,224,
+231,231,232,231,233,231,231,231,231,231,231,231,231,231,234,231,
+231,235,236,231,231,231,231,231,231,231,237,231,231,231,231,231,
+238,238,238,238,238,238,239,238,239,238,238,238,238,238,238,238,
+240,241,242,243,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+
+/* block 9 */
+240,241,244,245,246,247,247,246,248,248,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+249,240,241,240,241,240,241,240,241,240,241,240,241,240,241,250,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+
+/* block 10 */
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+163,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,
+251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,
+251,251,251,251,251,251,251,163,163,252,253,253,253,253,253,254,
+255,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,
+256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,
+
+/* block 11 */
+256,256,256,256,256,256,256,257,255,258,259,163,163,260,260,261,
+262,263,263,263,263,263,263,263,263,263,263,263,263,263,263,263,
+263,263,264,263,263,263,263,263,263,263,263,263,263,263,263,263,
+265,265,265,265,265,265,265,265,265,265,265,265,265,265,266,265,
+267,265,265,268,265,269,267,269,262,262,262,262,262,262,262,262,
+270,270,270,270,270,270,270,270,270,270,270,270,270,270,270,270,
+270,270,270,270,270,270,270,270,270,270,270,262,262,262,262,270,
+270,270,270,267,271,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 12 */
+272,272,272,272,272,273,274,274,275,276,276,277,278,279,280,280,
+281,281,281,281,281,281,281,281,281,281,281,282,283,284,284,285,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+287,286,286,286,286,286,286,286,286,286,286,288,288,288,288,288,
+288,288,288,289,289,289,281,290,291,281,281,281,281,281,281,281,
+292,292,292,292,292,292,292,292,292,292,276,293,293,279,286,286,
+289,286,286,294,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 13 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,295,286,281,281,281,281,281,281,281,273,280,291,
+291,281,281,281,281,296,296,281,281,280,291,291,291,281,286,286,
+297,297,297,297,297,297,297,297,297,297,286,286,286,298,298,286,
+
+/* block 14 */
+299,299,299,300,300,300,300,300,300,300,300,301,300,301,302,303,
+304,305,304,304,304,304,304,304,304,304,304,304,304,304,304,304,
+304,304,304,304,304,304,304,304,304,304,304,304,304,304,304,304,
+306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,
+307,307,307,307,307,307,307,307,307,307,307,302,302,304,304,304,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 15 */
+308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,
+308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,308,
+308,308,308,308,308,308,309,309,309,309,309,309,309,309,309,309,
+309,308,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+310,310,310,310,310,310,310,310,310,310,311,311,311,311,311,311,
+311,311,311,311,311,311,311,311,311,311,311,311,311,311,311,311,
+311,311,311,311,311,311,311,311,311,311,311,312,312,312,312,312,
+312,312,312,312,313,313,314,315,316,317,318,262,262,319,320,320,
+
+/* block 16 */
+321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,321,
+321,321,321,321,321,321,322,322,323,323,324,322,322,322,322,322,
+322,322,322,322,324,322,322,322,324,322,322,322,322,325,262,262,
+326,326,326,326,326,326,326,327,326,327,326,326,326,327,327,262,
+328,328,328,328,328,328,328,328,328,328,328,328,328,328,328,328,
+328,328,328,328,328,328,328,328,328,329,329,329,262,262,330,262,
+304,304,304,304,304,304,304,304,304,304,304,302,302,302,302,302,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 17 */
+286,286,286,286,286,286,286,286,331,286,286,286,286,286,286,302,
+272,272,302,302,302,302,302,302,291,291,291,291,291,291,291,291,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,296,291,291,291,291,291,291,
+291,291,291,332,281,281,281,281,281,281,281,281,281,281,281,281,
+332,332,273,290,290,290,290,290,290,290,291,291,291,291,291,291,
+290,290,290,290,290,290,290,290,290,290,290,290,290,290,290,281,
+
+/* block 18 */
+333,333,333,334,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+335,335,335,335,335,335,335,335,335,335,333,334,336,335,334,334,
+334,333,333,333,333,333,333,333,333,334,334,334,334,337,334,334,
+335,338,339,154,154,333,333,333,335,335,335,335,335,335,335,335,
+335,335,333,333,340,341,342,342,342,342,342,342,342,342,342,342,
+343,344,335,335,335,335,335,335,335,335,335,335,335,335,335,335,
+
+/* block 19 */
+345,346,347,347,163,345,345,345,345,345,345,345,345,163,163,345,
+345,163,163,345,345,345,345,345,345,345,345,345,345,345,345,345,
+345,345,345,345,345,345,345,345,345,163,345,345,345,345,345,345,
+345,163,345,163,163,163,345,345,345,345,163,163,348,345,349,347,
+347,346,346,346,346,163,163,347,347,163,163,347,347,350,345,163,
+163,163,163,163,163,163,163,349,163,163,163,163,345,345,163,345,
+345,345,346,346,163,163,351,351,351,351,351,351,351,351,351,351,
+345,345,352,352,353,353,353,353,353,353,354,352,345,355,356,163,
+
+/* block 20 */
+163,357,357,358,163,359,359,359,359,359,359,163,163,163,163,359,
+359,163,163,359,359,359,359,359,359,359,359,359,359,359,359,359,
+359,359,359,359,359,359,359,359,359,163,359,359,359,359,359,359,
+359,163,359,359,163,359,359,163,359,359,163,163,360,163,358,358,
+358,357,357,163,163,163,163,357,357,163,163,357,357,361,163,163,
+163,357,163,163,163,163,163,163,163,359,359,359,359,163,359,163,
+163,163,163,163,163,163,362,362,362,362,362,362,362,362,362,362,
+357,357,359,359,359,357,363,163,163,163,163,163,163,163,163,163,
+
+/* block 21 */
+163,364,364,365,163,366,366,366,366,366,366,366,366,366,163,366,
+366,366,163,366,366,366,366,366,366,366,366,366,366,366,366,366,
+366,366,366,366,366,366,366,366,366,163,366,366,366,366,366,366,
+366,163,366,366,163,366,366,366,366,366,163,163,367,366,365,365,
+365,364,364,364,364,364,163,364,364,365,163,365,365,368,163,163,
+366,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+366,366,364,364,163,163,369,369,369,369,369,369,369,369,369,369,
+370,371,163,163,163,163,163,163,163,366,364,364,364,367,367,367,
+
+/* block 22 */
+163,372,373,373,163,374,374,374,374,374,374,374,374,163,163,374,
+374,163,163,374,374,374,374,374,374,374,374,374,374,374,374,374,
+374,374,374,374,374,374,374,374,374,163,374,374,374,374,374,374,
+374,163,374,374,163,374,374,374,374,374,163,163,375,374,376,372,
+373,372,372,372,372,163,163,373,373,163,163,373,373,377,163,163,
+163,163,163,163,163,378,372,376,163,163,163,163,374,374,163,374,
+374,374,372,372,163,163,379,379,379,379,379,379,379,379,379,379,
+380,374,381,381,381,381,381,381,163,163,163,163,163,163,163,163,
+
+/* block 23 */
+163,163,382,383,163,383,383,383,383,383,383,163,163,163,383,383,
+383,163,383,383,383,383,163,163,163,383,383,163,383,163,383,383,
+163,163,163,383,383,163,163,163,383,383,383,163,163,163,383,383,
+383,383,383,383,383,383,383,383,383,383,163,163,163,163,384,385,
+382,385,385,163,163,163,385,385,385,163,385,385,385,386,163,163,
+383,163,163,163,163,163,163,384,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,387,387,387,387,387,387,387,387,387,387,
+388,388,388,389,390,390,390,390,390,391,390,163,163,163,163,163,
+
+/* block 24 */
+392,393,393,393,394,395,395,395,395,395,395,395,395,163,395,395,
+395,163,395,395,395,395,395,395,395,395,395,395,395,395,395,395,
+395,395,395,395,395,395,395,395,395,163,395,395,395,395,395,395,
+395,395,395,395,395,395,395,395,395,395,163,163,396,395,392,392,
+392,393,393,393,393,163,392,392,392,163,392,392,392,397,163,163,
+163,163,163,163,163,392,392,163,395,395,395,163,163,395,163,163,
+395,395,392,392,163,163,398,398,398,398,398,398,398,398,398,398,
+163,163,163,163,163,163,163,399,400,400,400,400,400,400,400,401,
+
+/* block 25 */
+402,403,404,404,405,402,402,402,402,402,402,402,402,163,402,402,
+402,163,402,402,402,402,402,402,402,402,402,402,402,402,402,402,
+402,402,402,402,402,402,402,402,402,163,402,402,402,402,402,402,
+402,402,402,402,163,402,402,402,402,402,163,163,406,402,404,407,
+404,404,408,404,404,163,407,404,404,163,404,404,403,409,163,163,
+163,163,163,163,163,408,408,163,163,163,163,163,163,402,402,163,
+402,402,403,403,163,163,410,410,410,410,410,410,410,410,410,410,
+163,402,402,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 26 */
+411,411,412,412,413,413,413,413,413,413,413,413,413,163,413,413,
+413,163,413,413,413,413,413,413,413,413,413,413,413,413,413,413,
+413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,
+413,413,413,413,413,413,413,413,413,413,413,414,414,413,415,412,
+412,411,411,411,411,163,412,412,412,163,412,412,412,414,416,417,
+163,163,163,163,413,413,413,415,418,418,418,418,418,418,418,413,
+413,413,411,411,163,163,419,419,419,419,419,419,419,419,419,419,
+418,418,418,418,418,418,418,418,418,417,413,413,413,413,413,413,
+
+/* block 27 */
+163,420,421,421,163,422,422,422,422,422,422,422,422,422,422,422,
+422,422,422,422,422,422,422,163,163,163,422,422,422,422,422,422,
+422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,
+422,422,163,422,422,422,422,422,422,422,422,422,163,422,163,163,
+422,422,422,422,422,422,422,163,163,163,423,163,163,163,163,424,
+421,421,420,420,420,163,420,163,421,421,421,421,421,421,421,424,
+163,163,163,163,163,163,425,425,425,425,425,425,425,425,425,425,
+163,163,421,421,426,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 28 */
+163,427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,
+427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,
+427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,427,
+427,428,427,429,428,428,428,428,428,428,430,163,163,163,163,431,
+432,432,432,432,432,427,433,434,434,434,434,434,434,428,434,435,
+436,436,436,436,436,436,436,436,436,436,437,437,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 29 */
+163,438,438,163,438,163,438,438,438,438,438,163,438,438,438,438,
+438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,
+438,438,438,438,163,438,163,438,438,438,438,438,438,438,438,438,
+438,439,438,440,439,439,439,439,439,439,441,439,439,438,163,163,
+442,442,442,442,442,163,443,163,444,444,444,444,444,439,163,163,
+445,445,445,445,445,445,445,445,445,445,163,163,438,438,438,438,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 30 */
+446,447,447,447,448,448,448,448,449,448,448,448,448,449,449,449,
+449,449,449,447,448,447,447,447,450,450,447,447,447,447,447,447,
+451,451,451,451,451,451,451,451,451,451,452,452,452,452,452,452,
+452,452,452,452,447,450,447,450,447,450,453,454,453,454,455,455,
+446,446,446,446,446,446,446,446,163,446,446,446,446,446,446,446,
+446,446,446,446,446,446,446,446,446,446,446,446,446,446,446,446,
+446,446,446,446,446,446,446,446,446,446,446,446,446,163,163,163,
+163,456,456,456,456,456,456,457,456,457,456,456,456,456,456,458,
+
+/* block 31 */
+456,456,450,450,459,448,450,450,446,446,446,446,446,456,456,456,
+456,456,456,456,456,456,456,456,163,456,456,456,456,456,456,456,
+456,456,456,456,456,456,456,456,456,456,456,456,456,456,456,456,
+456,456,456,456,456,456,456,456,456,456,456,456,456,163,447,447,
+447,447,447,447,447,447,450,447,447,447,447,447,447,163,447,447,
+448,448,448,448,448,460,460,460,460,448,448,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 32 */
+461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,
+461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,
+461,461,461,461,461,461,461,461,461,461,461,462,462,463,463,463,
+463,464,463,463,463,463,463,465,462,466,466,464,464,463,463,461,
+467,467,467,467,467,467,467,467,467,467,468,468,469,469,469,469,
+461,461,461,461,461,461,464,464,463,463,461,461,461,461,463,463,
+463,461,462,470,470,461,461,462,462,470,470,470,470,470,461,461,
+461,463,463,463,463,461,461,461,461,461,461,461,461,461,461,461,
+
+/* block 33 */
+461,461,463,462,464,463,463,470,470,470,470,470,470,471,461,470,
+472,472,472,472,472,472,472,472,472,472,470,470,462,463,473,473,
+474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,
+474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,474,
+474,474,474,474,474,474,163,474,163,163,163,163,163,474,163,163,
+475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,
+475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,
+475,475,475,475,475,475,475,475,475,475,475,476,477,475,475,475,
+
+/* block 34 */
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,479,
+480,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+
+/* block 35 */
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,481,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+
+/* block 36 */
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,163,483,483,483,483,163,163,
+483,483,483,483,483,483,483,163,483,163,483,483,483,483,163,163,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+
+/* block 37 */
+483,483,483,483,483,483,483,483,483,163,483,483,483,483,163,163,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,163,483,483,483,483,163,163,483,483,483,483,483,483,483,163,
+483,163,483,483,483,483,163,163,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+
+/* block 38 */
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,163,483,483,483,483,163,163,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,483,483,483,483,163,163,484,484,484,
+485,486,487,486,486,486,486,487,487,488,488,488,488,488,488,488,
+488,488,489,489,489,489,489,489,489,489,489,489,489,163,163,163,
+
+/* block 39 */
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+490,490,490,490,490,490,490,490,490,490,163,163,163,163,163,163,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,
+492,492,492,492,492,492,163,163,493,493,493,493,493,493,163,163,
+
+/* block 40 */
+494,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+
+/* block 41 */
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+
+/* block 42 */
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,496,497,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+
+/* block 43 */
+498,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,
+499,499,499,499,499,499,499,499,499,499,499,500,501,163,163,163,
+502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,
+502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,
+502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,
+502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,502,
+502,502,502,502,502,502,502,502,502,502,502,503,503,503,504,504,
+504,502,502,502,502,502,502,502,502,163,163,163,163,163,163,163,
+
+/* block 44 */
+505,505,505,505,505,505,505,505,505,505,505,505,505,505,505,505,
+505,505,506,506,507,508,163,163,163,163,163,163,163,163,163,505,
+509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,509,
+509,509,510,510,511,512,512,163,163,163,163,163,163,163,163,163,
+513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,513,
+513,513,514,514,163,163,163,163,163,163,163,163,163,163,163,163,
+515,515,515,515,515,515,515,515,515,515,515,515,515,163,515,515,
+515,163,516,516,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 45 */
+517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,
+517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,
+517,517,517,518,518,517,517,517,517,517,517,517,517,517,517,517,
+517,517,517,517,519,519,520,521,521,521,521,521,521,521,520,520,
+520,520,520,520,520,520,521,520,520,522,522,522,522,522,522,522,
+522,522,523,522,524,524,524,525,526,526,524,527,517,522,163,163,
+528,528,528,528,528,528,528,528,528,528,163,163,163,163,163,163,
+529,529,529,529,529,529,529,529,529,529,163,163,163,163,163,163,
+
+/* block 46 */
+530,530,531,532,533,531,534,530,533,535,536,537,537,537,538,537,
+539,539,539,539,539,539,539,539,539,539,163,163,163,163,163,163,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,541,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,163,163,163,163,163,163,163,
+
+/* block 47 */
+540,540,540,540,540,542,542,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,540,
+540,540,540,540,540,540,540,540,540,543,540,163,163,163,163,163,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+495,495,495,495,495,495,163,163,163,163,163,163,163,163,163,163,
+
+/* block 48 */
+544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,
+544,544,544,544,544,544,544,544,544,544,544,544,544,544,544,163,
+545,545,545,546,546,546,546,545,545,546,546,546,163,163,163,163,
+546,546,545,546,546,546,546,546,546,547,547,547,163,163,163,163,
+548,163,163,163,549,549,550,550,550,550,550,550,550,550,550,550,
+551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,551,
+551,551,551,551,551,551,551,551,551,551,551,551,551,551,163,163,
+551,551,551,551,551,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 49 */
+552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
+552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,552,
+552,552,552,552,552,552,552,552,552,552,552,552,163,163,163,163,
+552,552,552,552,552,553,553,553,552,552,553,552,552,552,552,552,
+552,552,552,552,552,552,552,552,552,552,163,163,163,163,163,163,
+554,554,554,554,554,554,554,554,554,554,555,163,163,163,556,556,
+557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
+557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,557,
+
+/* block 50 */
+558,558,558,558,558,558,558,558,558,558,558,558,558,558,558,558,
+558,558,558,558,558,558,558,559,559,560,560,559,163,163,561,561,
+562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
+562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
+562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,562,
+562,562,562,562,562,563,564,563,564,564,564,564,564,564,564,163,
+565,566,564,566,566,564,564,564,564,564,564,564,564,563,563,563,
+563,563,563,564,564,567,567,567,567,567,567,567,567,163,163,567,
+
+/* block 51 */
+568,568,568,568,568,568,568,568,568,568,163,163,163,163,163,163,
+568,568,568,568,568,568,568,568,568,568,163,163,163,163,163,163,
+569,569,569,569,569,569,569,570,571,571,571,571,569,569,163,163,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,572,573,
+573,154,154,154,154,154,154,154,154,154,154,154,573,573,573,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 52 */
+574,574,574,574,575,576,576,576,576,576,576,576,576,576,576,576,
+576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,
+576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,576,
+576,576,576,576,577,578,574,574,574,574,574,575,574,575,575,575,
+575,575,574,575,579,576,576,576,576,576,576,576,576,163,163,163,
+580,580,580,580,580,580,580,580,580,580,581,581,582,583,581,581,
+582,584,584,584,584,584,584,584,584,584,584,577,577,577,577,577,
+577,577,577,577,584,584,584,584,584,584,584,584,584,581,581,163,
+
+/* block 53 */
+585,585,586,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,587,
+587,586,585,585,585,585,586,586,585,585,588,589,585,585,587,587,
+590,590,590,590,590,590,590,590,590,590,587,587,587,587,587,587,
+591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,
+591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,591,
+591,591,591,591,591,591,592,593,594,594,593,593,593,594,593,594,
+594,594,595,595,163,163,163,163,163,163,163,163,596,596,596,596,
+
+/* block 54 */
+597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,
+597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,597,
+597,597,597,597,598,598,598,598,598,598,598,598,599,599,599,599,
+599,599,599,599,598,598,600,601,163,163,163,602,602,603,603,603,
+604,604,604,604,604,604,604,604,604,604,163,163,163,597,597,597,
+605,605,605,605,605,605,605,605,605,605,606,606,606,606,606,606,
+606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,606,
+606,606,606,606,606,606,606,606,607,607,607,608,607,607,609,609,
+
+/* block 55 */
+610,611,612,613,614,615,616,617,618,163,163,163,163,163,163,163,
+619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,
+619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,619,
+619,619,619,619,619,619,619,619,619,619,619,163,163,619,619,619,
+620,620,620,620,620,620,620,620,163,163,163,163,163,163,163,163,
+621,622,621,623,622,624,624,625,624,625,626,622,625,625,622,622,
+625,627,622,622,622,622,622,622,622,628,629,630,630,624,630,630,
+630,630,631,632,633,629,629,634,635,635,636,163,163,163,163,163,
+
+/* block 56 */
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70,221,221,221,221,221,637,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,638,638,638,
+638,638,148,147,147,147,638,638,638,638,638, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70,639,640, 70, 70, 70,641, 70, 70,
+
+/* block 57 */
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,642, 70,
+ 70, 70, 70, 70, 70, 70,643, 70, 70, 70, 70,644,644,644,644,644,
+644,644,644,644,645,644,644,644,645,644,644,644,644,644,644,644,
+644,644,644,644,644,644,644,644,644,644,644,644,644,644,644,646,
+647,647,158,158,154,154,154,154,154,154,154,154,154,154,154,154,
+158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,
+158,158,158,158,158,158,158,573,573,573,573,573,573,573,573,573,
+573,573,573,573,573,154,154,154,648,154,649,154,154,154,154,154,
+
+/* block 58 */
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+650,651, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+
+/* block 59 */
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 69, 69, 69, 69,652,653, 70, 70,654, 70,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 67, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+
+/* block 60 */
+655,655,655,655,655,655,655,655,656,656,656,656,656,656,656,656,
+655,655,655,655,655,655,163,163,656,656,656,656,656,656,163,163,
+655,655,655,655,655,655,655,655,656,656,656,656,656,656,656,656,
+655,655,655,655,655,655,655,655,656,656,656,656,656,656,656,656,
+655,655,655,655,655,655,163,163,656,656,656,656,656,656,163,163,
+173,655,173,655,173,655,173,655,163,656,163,656,163,656,163,656,
+655,655,655,655,655,655,655,655,656,656,656,656,656,656,656,656,
+657,657,658,658,658,658,659,659,660,660,661,661,662,662,163,163,
+
+/* block 61 */
+663,663,663,663,663,663,663,663,664,664,664,664,664,664,664,664,
+663,663,663,663,663,663,663,663,664,664,664,664,664,664,664,664,
+663,663,663,663,663,663,663,663,664,664,664,664,664,664,664,664,
+655,655,665,666,665,163,173,665,656,656,667,667,668,162,669,162,
+162,162,665,666,665,163,173,665,670,670,670,670,668,162,162,162,
+655,655,173,173,163,163,173,173,656,656,671,671,163,162,162,162,
+655,655,173,173,173,215,173,173,656,656,672,672,220,162,162,162,
+163,163,665,666,665,163,173,665,673,673,674,674,668,162,162,163,
+
+/* block 62 */
+675,675,675,675,675,675,675,675,675,675,675, 51,676,677,678,679,
+680,680,680,680,680,680,681, 43,682,683,684,685,685,686,684,685,
+ 43, 43, 43, 43,687, 43, 43,687,688,689,690,691,692,693,694,695,
+696,696,697,697,697, 43, 43, 43, 43, 49, 57, 43,698,699, 43,700,
+701, 43, 43, 43,702,703,704,699,699,698, 43, 43, 43, 43, 43, 43,
+ 43, 43, 50,705,700, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,675,
+ 51,706,706,706,706,707,708,709,710,711,712,712,712,712,712,712,
+ 54,645,163,163, 54, 54, 54, 54, 54, 54,713,714,715,716,717,644,
+
+/* block 63 */
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,713,714,715,716,717,163,
+644,644,644,644,644,644,644,644,644,644,644,644,644,163,163,163,
+431,431,431,431,431,431,431,431,431,431,431,431,431,431,431,431,
+431,431,431,431,431,431,431,431,431,431,431,431,431,431,431,431,
+431,718,718,718,718,718,718,718,718,718,718,718,718,718,718,718,
+719,719,719,719,719,719,719,719,719,719,719,719,719,720,720,720,
+720,719,720,721,720,719,719,158,158,158,158,719,719,719,719,719,
+722,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 64 */
+723,723,724,723,723,723,723,724,723,723,725,724,724,724,725,725,
+724,724,724,725,723,724,723,723,726,724,724,724,724,724,723,723,
+723,723,727,723,724,723,728,723,724,729,730,731,724,724,732,725,
+724,724,733,724,725,734,734,734,734,735,723,723,725,725,724,724,
+715,715,715,715,715,724,725,725,736,736,723,715,723,723,737,460,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+738,738,738,738,738,738,738,738,738,738,738,738,738,738,738,738,
+739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,739,
+
+/* block 65 */
+740,740,740, 65, 66,740,740,740,740, 58,723,723,163,163,163,163,
+ 50, 50, 50, 50,741,742,742,742,742,742, 50, 50,743,743,743,743,
+ 50,743,743, 50,743,743, 50,743, 45,742,742,743,743,743, 50, 45,
+743,743, 45, 45, 45, 45,743,743, 45, 45, 45, 45,743,743,743,743,
+743,743,743,743,743,743,743,743,743,743,743,743,743,743, 50, 50,
+743,743, 50,743, 50,743,743,743,743,743,743,743, 45,743, 45, 45,
+ 45, 45, 45, 45,743,743, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+
+/* block 66 */
+ 50, 50, 50, 50, 50, 50, 50, 50,744,744,744,744,744,744, 50, 50,
+ 50, 50,745, 53, 50,744, 50, 50, 50, 50, 50, 50, 50, 50, 50,744,
+744,744,744, 50,744, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,744,744, 50, 50,
+ 50, 50, 50,744, 50,744, 50, 50, 50, 50, 50, 50,744, 50, 50, 50,
+ 50, 50,744,744,744,744, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50,744,744,744,744,744,744,744,744, 50, 50,744,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+
+/* block 67 */
+744,744,744,744,744,744,744,744,744,744,744,744, 50, 50, 50,744,
+744,744,744, 50, 50, 50, 50, 50,744, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50,744,744, 50, 50,744, 50,744,744, 50,744, 50, 50, 50, 50,
+744,744,744,744,744,744,744,744,744, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50,744,744,744,744,744, 50, 50,
+744,744, 50, 50, 50, 50,744,744,744,744,744,744,744,744,744,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744, 50, 50,
+744,744,744,744,744, 50,744,744, 50, 50,744,744,744,744,744, 50,
+
+/* block 68 */
+ 45, 45, 45, 45, 45, 45, 45, 45,746,747,746,747, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,748,748, 45, 45, 45, 45,
+ 50, 50, 45, 45, 45, 45, 45, 45, 47,749,750, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45,751,751,751,751,751,751,751,751,751,751,
+751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,
+751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,
+751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,751,
+751,751,751,751,751,751,751,751,751,751,751, 45, 50, 45, 45, 45,
+
+/* block 69 */
+ 45, 45, 45, 45, 45, 45, 45, 45,752, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45,751, 45, 45, 45, 45, 45, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50,743,743, 45,743, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47,
+743, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 50, 50, 50, 50,
+ 50, 50,743, 45, 45, 45, 45, 45, 45,748,748,748,748, 47, 47, 47,
+748, 47, 47,748, 45, 45, 45, 45, 47, 47, 47, 45, 45, 45, 45, 45,
+
+/* block 70 */
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45,753,753,753,753,753,753,753,753,753,
+753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,753,753,753,753,753,
+753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+
+/* block 71 */
+ 58, 58, 58, 58, 58, 58, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,754,754,754,754,754,754,754,754,754,754,
+754,754,755,754,754,754,754,754,754,754,754,754,754,754,754,754,
+756,756,756,756,756,756,756,756,756,756,756,756,756,756,756,756,
+756,756,756,756,756,756,756,756,756,756, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+
+/* block 72 */
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+
+/* block 73 */
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+743,743, 45, 45, 45, 45, 45, 45, 45, 45, 47, 47, 45, 45,743,743,
+743,743,743,743,743,743,742, 50, 45, 45, 45, 45,743,743,743,743,
+742, 50, 45, 45, 45, 45,743,743, 45, 45,743,743, 45, 45, 45,743,
+743,743,743,743, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45,743, 45,743, 45, 45,743,743,743,743,743,743, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 50, 50, 50,741,741,757,757, 50,
+
+/* block 74 */
+ 47, 47, 47, 47, 47,758,743,752,752,752,752,752,752,752, 47,752,
+752, 47,752, 45,748,748,752,752, 47,752,752,752,752,759,752,752,
+ 47,752, 47, 47,752,752, 47,752,752,752, 47,752,752,752, 47, 47,
+752,752,752,752,752,752,752,752, 47, 47, 47,752,752,752,752,752,
+742,752,742,752,752,752,752,752,748,748,748,748,748,748,748,748,
+748,748,748,748,752,752,752,752,752,752,752,752,752,752,752, 47,
+742,758,758,742,752, 47, 47,752, 47,752,752,752,752,758,758,760,
+752,752,752,752,752,752,752,752,752,752,752, 47,752,752, 47,748,
+
+/* block 75 */
+752,752,752,752,752,752, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+752,752, 47,748, 47, 47, 47, 47,752, 47,752, 47, 47,752,752,752,
+ 47,748,752,752,752,752,752, 47,752,752,748,748,761,752,752,752,
+ 47, 47,752,752,752,752,752,752,752,752,752,752,752,748,748,752,
+752,752,752,752,748,748,752,752, 47,752,752,752,752,752,748, 47,
+752, 47,752, 47,748,752,752,752,752,752,752,752,752,752,752,752,
+752,752,752,752,752,752,752,752,752, 47,748,752,752,752,752,752,
+ 47, 47,748,748, 47,748,752, 47, 47,759,748,752,752,748,752,752,
+
+/* block 76 */
+752,752, 47,752,752,748, 45, 45, 47, 47,762,762,759,759,752, 47,
+752,752, 47, 45, 47, 45, 47, 45, 45, 45, 45, 45, 45, 47, 45, 45,
+ 45, 47, 45, 45, 45, 45, 45, 45,748, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 47, 45, 45, 47, 45, 45, 45, 45,748, 45,748, 45,
+ 45, 45, 45,748,748,748, 45,748, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 47, 47,752,752,752,703,704,703,704,703,704,703,704,
+703,704,703,704,703,704, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+
+/* block 77 */
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 45,748,748,748, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 47, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+748, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,748,
+ 50, 50, 50,744,744,746,747, 50,744,744, 50,744, 50,744, 50, 50,
+ 50, 50, 50, 50, 50,744,744, 50, 50, 50, 50, 50,744,744,744, 50,
+ 50, 50,744,744,744,744,746,747,746,747,746,747,746,747,746,747,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+
+/* block 78 */
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,763,
+
+/* block 79 */
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50,741,741, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+
+/* block 80 */
+ 50, 50, 50,746,747,746,747,746,747,746,747,746,747,746,747,746,
+747,746,747,746,747,746,747,746,747, 50, 50,744, 50, 50, 50, 50,
+744, 50, 50,744,744,744, 50, 50,744,744,744,744,744,744,744,744,
+ 50, 50, 50, 50, 50, 50, 50, 50,744, 50, 50, 50, 50, 50, 50, 50,
+744,744, 50, 50,744,744, 50, 50, 50, 50, 50, 50, 50, 50, 50,744,
+744,744,744, 50,744,744, 50, 50,746,747,746,747, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50,744,744, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50,744, 50, 50,744,744, 50, 50,746,747, 50, 50,
+
+/* block 81 */
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,744,744,744,744, 50,
+ 50, 50, 50, 50,744,744, 50, 50, 50, 50, 50, 50,744,744, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50,744,744, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50,744,744,744,744,744,744,744,
+
+/* block 82 */
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+744,744,744, 50, 50, 50,744,744,744,744,744,744,744,744, 50,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,744,
+744,744,744,744,744,744,744, 50, 50, 50, 50, 50, 50, 50,744, 50,
+ 50, 50, 50,744,744,744, 50, 50, 50, 50, 50, 50,744,744,744, 50,
+ 50, 50, 50, 50, 50, 50, 50,744,744,744,744, 50, 50, 50, 50, 50,
+
+/* block 83 */
+ 45, 45, 45, 45, 45, 47, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,748,748, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 45, 45, 50, 50, 50, 50, 50, 50, 45, 45, 45,
+748, 45, 45, 45, 45,748, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45,753,753, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+
+/* block 84 */
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45,753, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,764, 45,
+
+/* block 85 */
+765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,
+765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,
+765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,765,
+766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,
+766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,
+766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,766,
+ 65, 66,767,768,769,770,771, 65, 66, 65, 66, 65, 66,772,773,774,
+775, 70, 65, 66, 70, 65, 66, 70, 70, 70, 70, 70,645,644,776,776,
+
+/* block 86 */
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,211,212,211,212,211,212,211,212,211,212,211,212,
+211,212,211,212,777,778,778,778,778,778,778,211,212,211,212,779,
+779,779,211,212,163,163,163,163,163,780,780,780,780,781,780,780,
+
+/* block 87 */
+782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,
+782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,782,
+782,782,782,782,782,782,163,782,163,163,163,163,163,782,163,163,
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,783,
+783,783,783,783,783,783,783,783,163,163,163,163,163,163,163,784,
+785,163,163,163,163,163,163,163,163,163,163,163,163,163,163,786,
+
+/* block 88 */
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,
+483,483,483,483,483,483,483,163,163,163,163,163,163,163,163,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+787,787,787,787,787,787,787,787,787,787,787,787,787,787,787,787,
+787,787,787,787,787,787,787,787,787,787,787,787,787,787,787,787,
+
+/* block 89 */
+ 43, 43,788,789,788,789, 43, 43, 43,788,789, 43,788,789, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43,680, 43, 43,680, 43,788,789, 43, 43,
+788,789,703,704,703,704,703,704,703,704, 43, 43, 43, 43,699,790,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,680,680,699, 43, 43, 43,
+680,791,684,792, 43, 43, 43, 43, 43, 43, 43, 43,791, 43,791,791,
+ 45, 45, 43,699,699,703,704,703,704,703,704,703,704,680,753,753,
+753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,
+753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,753,
+
+/* block 90 */
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,163,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 91 */
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+
+/* block 92 */
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,793,
+793,793,793,793,793,793,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+794,794,795,795,794,794,794,794,794,794,794,794,163,163,163,163,
+
+/* block 93 */
+675,796,797,798,723,799,800,801,802,803,802,803,804,805,804,805,
+802,803, 45,806,802,803,802,803,802,803,802,803,807,808,809,809,
+ 45,801,801,801,801,801,801,801,801,801,810,810,810,810,811,811,
+812,813,813,813,813,813,723,814,801,801,801,815,816,817,818,818,
+163,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+
+/* block 94 */
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,163,163,820,820,821,821,822,822,819,
+823,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,825,826,827,827,824,
+
+/* block 95 */
+163,163,163,163,163,828,828,828,828,828,828,828,828,828,828,828,
+828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,
+828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,
+163,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,830,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+
+/* block 96 */
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,163,
+831,831,832,832,832,832,831,831,831,831,831,831,831,831,831,831,
+828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,
+828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,828,
+818,818,818,818,818,818,818,818,818,818,818,818,818,818,818,818,
+818,818,818,818,818,818,818,818,818,818,818,818,818,818,818,818,
+818,818,818,818,163,163,163,163,163,163,163,163,163,163,163,163,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+
+/* block 97 */
+833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,
+833,833,833,833,833,833,833,833,833,833,833,833,833,834,834,163,
+832,832,832,832,832,832,832,832,832,832,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,835,835,835,835,835,835,835,835,
+723, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,833,
+833,833,833,833,833,833,833,833,833,833,833,833,834,834,834,460,
+
+/* block 98 */
+832,832,832,832,832,832,832,832,832,832,831,831,831,831,831,831,
+831,831,831,831,831,831,831,836,831,836,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,
+831, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+831,831,831,831,831,831,831,831,831,831,831,831,723,723,723,723,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,831,
+
+/* block 99 */
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,837,
+837,837,837,837,837,837,837,837,831,831,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,
+831,460,460,460,460,460,460,723,723,723,723,831,831,831,831,831,
+
+/* block 100 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,723,723,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,
+831,831,831,831,831,831,831,831,831,831,831,831,831,831,831,723,
+
+/* block 101 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+
+/* block 102 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+
+/* block 103 */
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,840,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+
+/* block 104 */
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,839,
+
+/* block 105 */
+839,839,839,839,839,839,839,839,839,839,839,839,839,163,163,163,
+841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,
+841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,
+841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,841,
+841,841,841,841,841,841,841,163,163,163,163,163,163,163,163,163,
+842,842,842,842,842,842,842,842,842,842,842,842,842,842,842,842,
+842,842,842,842,842,842,842,842,842,842,842,842,842,842,842,842,
+842,842,842,842,842,842,842,842,843,843,843,843,843,843,844,845,
+
+/* block 106 */
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+
+/* block 107 */
+846,846,846,846,846,846,846,846,846,846,846,846,847,848,849,849,
+846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,846,
+850,850,850,850,850,850,850,850,850,850,846,846,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+240,241,240,241,240,241,240,241,240,241,851,852,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,853,246,
+248,248,248,854,787,787,787,787,787,787,787,787,855,855,854,856,
+
+/* block 108 */
+240,241,240,241,240,241,240,241,240,241,240,241,240,241,240,241,
+240,241,240,241,240,241,240,241,240,241,240,241,857,857,787,787,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,859,859,859,859,859,859,859,859,859,859,
+860,860,861,862,863,863,863,862,163,163,163,163,163,163,163,163,
+
+/* block 109 */
+864,864,864,864,864,864,864,864, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46,149,149,149,149,149,149,149,149,149,
+ 46, 46, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 70, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+644, 70, 70, 70, 70, 70, 70, 70, 70, 65, 66, 65, 66,865, 65, 66,
+
+/* block 110 */
+ 65, 66, 65, 66, 65, 66, 65, 66,149,866,866, 65, 66,867, 70, 92,
+ 65, 66, 65, 66,868, 70, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,869,870,871,872,869, 70,
+873,874,875,876, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66, 65, 66,
+ 65, 66, 65, 66,877,878,879, 65, 66, 65, 66,163,163,163,163,163,
+ 65, 66,163, 70,163, 70, 65, 66, 65, 66,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,880,880,880, 65, 66, 92,147,147, 70, 92, 92, 92, 92, 92,
+
+/* block 111 */
+881,881,882,881,881,881,883,881,881,881,881,882,881,881,881,881,
+881,881,881,881,881,881,881,881,881,881,881,881,881,881,881,881,
+881,881,881,884,884,882,882,884,885,885,885,885,883,163,163,163,
+886,886,886,887,887,887,888,888,889,890,163,163,163,163,163,163,
+891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,
+891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,
+891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,891,
+891,891,891,891,892,892,893,893,163,163,163,163,163,163,163,163,
+
+/* block 112 */
+894,894,895,895,895,895,895,895,895,895,895,895,895,895,895,895,
+895,895,895,895,895,895,895,895,895,895,895,895,895,895,895,895,
+895,895,895,895,895,895,895,895,895,895,895,895,895,895,895,895,
+895,895,895,895,894,894,894,894,894,894,894,894,894,894,894,894,
+894,894,894,894,896,897,163,163,163,163,163,163,163,163,898,898,
+899,899,899,899,899,899,899,899,899,899,163,163,163,163,163,163,
+336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,
+336,900,335,901,335,335,335,335,343,343,343,335,343,335,335,333,
+
+/* block 113 */
+902,902,902,902,902,902,902,902,902,902,903,903,903,903,903,903,
+903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,903,
+903,903,903,903,903,903,904,904,904,904,904,905,905,905,906,907,
+908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,908,
+908,908,908,908,908,908,908,909,909,909,909,909,909,909,909,909,
+909,909,910,911,163,163,163,163,163,163,163,163,163,163,163,912,
+478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,478,
+478,478,478,478,478,478,478,478,478,478,478,478,478,163,163,163,
+
+/* block 114 */
+913,913,913,914,915,915,915,915,915,915,915,915,915,915,915,915,
+915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,
+915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,915,
+915,915,915,916,914,914,913,913,913,913,914,914,913,913,914,914,
+917,918,918,918,918,918,918,919,920,920,918,918,918,918,163,921,
+922,922,922,922,922,922,922,922,922,922,163,163,163,163,918,918,
+461,461,461,461,461,471,923,461,461,461,461,461,461,461,461,461,
+472,472,472,472,472,472,472,472,472,472,461,461,461,461,461,163,
+
+/* block 115 */
+924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,
+924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,924,
+924,924,924,924,924,924,924,924,924,925,925,925,925,925,925,926,
+926,925,925,926,926,925,925,163,163,163,163,163,163,163,163,163,
+924,924,924,925,924,924,924,924,924,924,924,924,925,926,163,163,
+927,927,927,927,927,927,927,927,927,927,163,163,928,929,929,929,
+461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,461,
+923,461,461,461,461,461,461,473,473,473,461,470,471,470,461,461,
+
+/* block 116 */
+930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,
+930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,
+930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,
+931,930,931,931,931,932,932,931,931,932,930,932,932,930,931,933,
+934,933,934,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,930,930,935,936,937,
+938,938,938,938,938,938,938,938,938,938,938,939,940,940,939,939,
+941,941,938,942,942,939,943,163,163,163,163,163,163,163,163,163,
+
+/* block 117 */
+163,483,483,483,483,483,483,163,163,483,483,483,483,483,483,163,
+163,483,483,483,483,483,483,163,163,163,163,163,163,163,163,163,
+483,483,483,483,483,483,483,163,483,483,483,483,483,483,483,163,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 70, 70, 70,944, 70, 70, 70, 70, 70, 70, 70,866,147,147,147,147,
+ 70, 70, 70, 70, 70,221, 70, 70, 70,945, 46, 46,163,163,163,163,
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+
+/* block 118 */
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,946,
+938,938,938,938,938,938,938,938,938,938,938,938,938,938,938,938,
+938,938,938,938,938,938,938,938,938,938,938,938,938,938,938,938,
+938,938,938,939,939,940,939,939,940,939,939,941,947,943,163,163,
+948,948,948,948,948,948,948,948,948,948,163,163,163,163,163,163,
+
+/* block 119 */
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 120 */
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+
+/* block 121 */
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 122 */
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+
+/* block 123 */
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 124 */
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 125 */
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+949,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,949,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,949,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+
+/* block 126 */
+950,950,950,950,950,950,950,950,949,950,950,950,950,950,950,950,
+950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,950,
+950,950,950,950,163,163,163,163,163,163,163,163,163,163,163,163,
+481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,481,
+481,481,481,481,481,481,481,163,163,163,163,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,
+482,482,482,482,482,482,482,482,482,482,482,482,163,163,163,163,
+
+/* block 127 */
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,951,
+
+/* block 128 */
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+
+/* block 129 */
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+
+/* block 130 */
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,838,838,
+953,838,953,838,838,953,953,953,953,953,953,953,953,953,953,838,
+953,838,953,838,838,953,953,838,838,838,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,163,163,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+
+/* block 131 */
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 132 */
+652,652,652,652,652,652,652,163,163,163,163,163,163,163,163,163,
+163,163,163,257,257,257,257,257,163,163,163,163,163,270,265,270,
+270,270,270,270,270,270,270,270,270,954,270,270,270,270,270,270,
+270,270,270,270,270,270,270,262,270,270,270,270,270,262,270,262,
+270,270,262,270,270,262,270,270,270,270,270,270,270,270,270,270,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 133 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,331,331,331,331,331,331,331,331,331,331,331,331,331,331,
+331,331,331,302,302,302,302,302,302,302,302,302,302,302,302,302,
+302,302,302,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 134 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,955,955,
+955,955,955,955,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 135 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 136 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,956,957,
+280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+
+/* block 137 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+302,302,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,302,302,302,302,302,302,302,280,
+958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,
+958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,958,
+286,286,959,286,286,286,286,286,286,286,955,955,277,960,280,280,
+
+/* block 138 */
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,962,
+963,963,963,964,963,963,963,965,966,963,163,163,163,163,163,163,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,855,855,
+963,967,967,700,700,965,966,965,966,965,966,965,966,965,966,965,
+966,968,969,968,969,798,798,965,966,963,963,963,963,700,700,700,
+970,166,971,163,166,972,973,973,967,974,975,974,975,974,975,976,
+963,977,713,978,979,979,715,163,977,431,976,963,163,163,163,163,
+955,286,955,286,955,302,955,286,955,286,955,286,955,286,955,286,
+
+/* block 139 */
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,286,
+286,286,286,286,286,286,286,286,286,286,286,286,286,302,302, 51,
+
+/* block 140 */
+163,973,980,976,431,976,963,981,974,975,963,713,970,982,971,983,
+984,984,984,984,984,984,984,984,984,984,972,166,979,715,979,973,
+963,985,985,985,985,985,985, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,974,977,975,986,700,
+ 46,987,987,987,987,987,987, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,974,715,975,715,974,
+975,988,989,990,991,825,824,824,824,824,824,824,824,824,824,824,
+826,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+
+/* block 141 */
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,824,
+824,824,824,824,824,824,824,824,824,824,824,824,824,824,992,992,
+830,829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,
+829,829,829,829,829,829,829,829,829,829,829,829,829,829,829,163,
+163,163,829,829,829,829,829,829,163,163,829,829,829,829,829,829,
+163,163,829,829,829,829,829,829,163,163,829,829,829,163,163,163,
+431,431,715, 46,723,431,431,163,723,715,715,715,715,723,723,163,
+707,707,707,707,707,707,707,707,707,993,993,993,723,723,958,958,
+
+/* block 142 */
+994,994,994,994,994,994,994,994,994,994,994,994,163,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,163,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,163,994,994,163,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,163,163,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 143 */
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,994,
+994,994,994,994,994,994,994,994,994,994,994,163,163,163,163,163,
+
+/* block 144 */
+995,996,997,163,163,163,163,998,998,998,998,998,998,998,998,998,
+998,998,998,998,998,998,998,998,998,998,998,998,998,998,998,998,
+998,998,998,998,998,998,998,998,998,998,998,998,998,998,998,998,
+998,998,998,998,163,163,163,999,999,999,999,999,999,999,999,999,
+1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,
+1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,
+1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000,
+1000,1000,1000,1000,1000,1001,1001,1001,1001,1002,1002,1002,1002,1002,1002,1002,
+
+/* block 145 */
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1001,1001,1002,1003,1003,163,
+723,723,723,723,723,723,723,723,723,723,723,723,723,163,163,163,
+1002,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,158,163,163,
+
+/* block 146 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 147 */
+1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,
+1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,1004,163,163,163,
+1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,
+1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,
+1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,1005,
+1005,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1006,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,
+1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,1007,163,163,163,163,
+
+/* block 148 */
+1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,
+1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,1008,
+1009,1009,1009,1009,163,163,163,163,163,163,163,163,163,1008,1008,1008,
+1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,1010,
+1010,1011,1010,1010,1010,1010,1010,1010,1010,1010,1011,163,163,163,163,163,
+1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,
+1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,1012,
+1012,1012,1012,1012,1012,1012,1013,1013,1013,1013,1013,163,163,163,163,163,
+
+/* block 149 */
+1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,
+1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,1014,163,1015,
+1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,
+1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,1016,
+1016,1016,1016,1016,163,163,163,163,1016,1016,1016,1016,1016,1016,1016,1016,
+1017,1018,1018,1018,1018,1018,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 150 */
+1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,
+1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,1019,
+1019,1019,1019,1019,1019,1019,1019,1019,1020,1020,1020,1020,1020,1020,1020,1020,
+1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,
+1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,1020,
+1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,
+1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,
+1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,1021,
+
+/* block 151 */
+1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,
+1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,1022,163,163,
+1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,163,163,163,163,163,163,
+1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,
+1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,1024,
+1024,1024,1024,1024,163,163,163,163,1025,1025,1025,1025,1025,1025,1025,1025,
+1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,
+1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,1025,163,163,163,163,
+
+/* block 152 */
+1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,
+1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,1026,
+1026,1026,1026,1026,1026,1026,1026,1026,163,163,163,163,163,163,163,163,
+1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,
+1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,
+1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,1027,
+1027,1027,1027,1027,163,163,163,163,163,163,163,163,163,163,163,1028,
+1029,1029,1029,1029,1029,1029,1029,1029,1029,1029,1029,163,1029,1029,1029,1029,
+
+/* block 153 */
+1029,1029,1029,1029,1029,1029,1029,1029,1029,1029,1029,163,1029,1029,1029,1029,
+1029,1029,1029,163,1029,1029,163,1030,1030,1030,1030,1030,1030,1030,1030,1030,
+1030,1030,163,1030,1030,1030,1030,1030,1030,1030,1030,1030,1030,1030,1030,1030,
+1030,1030,163,1030,1030,1030,1030,1030,1030,1030,163,1030,1030,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 154 */
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+
+/* block 155 */
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,1031,163,163,163,163,163,163,163,163,163,
+1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,1031,
+1031,1031,1031,1031,1031,1031,163,163,163,163,163,163,163,163,163,163,
+1031,1031,1031,1031,1031,1031,1031,1031,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 156 */
+147,1032,1032,147,147,147,163,147,147,147,147,147,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,
+147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,147,
+147,163,147,147,147,147,147,147,147,147,147,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 157 */
+1033,1033,1033,1033,1033,1033,262,262,1033,262,1033,1033,1033,1033,1033,1033,
+1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,
+1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,1033,
+1033,1033,1033,1033,1033,1033,262,1033,1033,262,262,262,1033,262,262,1033,
+1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,1034,
+1034,1034,1034,1034,1034,1034,262,1035,1036,1036,1036,1036,1036,1036,1036,1036,
+1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,1037,
+1037,1037,1037,1037,1037,1037,1037,1038,1038,1039,1039,1039,1039,1039,1039,1039,
+
+/* block 158 */
+1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,
+1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,1040,262,
+262,262,262,262,262,262,262,1041,1041,1041,1041,1041,1041,1041,1041,1041,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,1042,
+1042,1042,1042,262,1042,1042,262,262,262,262,262,1043,1043,1043,1043,1043,
+
+/* block 159 */
+1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,1044,
+1044,1044,1044,1044,1044,1044,1045,1045,1045,1045,1045,1045,262,262,262,1046,
+1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,
+1047,1047,1047,1047,1047,1047,1047,1047,1047,1047,262,262,262,262,262,1048,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 160 */
+1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,
+1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,1049,
+1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,1050,
+1050,1050,1050,1050,1050,1050,1050,1050,262,262,262,262,1051,1051,1050,1050,
+1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,
+262,262,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,
+1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,
+1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,1051,
+
+/* block 161 */
+1052,1053,1053,1053,262,1053,1053,262,262,262,262,262,1053,1053,1053,1053,
+1052,1052,1052,1052,262,1052,1052,1052,262,1052,1052,1052,1052,1052,1052,1052,
+1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,1052,
+1052,1052,1052,1052,1052,1052,262,262,1054,1054,1054,262,262,262,262,1055,
+1056,1056,1056,1056,1056,1056,1056,1056,1056,262,262,262,262,262,262,262,
+1057,1057,1057,1057,1057,1057,1058,1058,1057,262,262,262,262,262,262,262,
+1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,
+1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1059,1060,1060,1061,
+
+/* block 162 */
+1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,
+1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1062,1063,1063,1063,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1064,1064,1064,1064,1064,1064,1064,1064,1065,1064,1064,1064,1064,1064,1064,1064,
+1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,1064,
+1064,1064,1064,1064,1064,1066,1066,262,262,262,262,1067,1067,1067,1067,1067,
+1068,1068,1069,1068,1068,1068,1070,262,262,262,262,262,262,262,262,262,
+
+/* block 163 */
+1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,
+1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,
+1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,1071,
+1071,1071,1071,1071,1071,1071,262,262,262,1072,1073,1073,1073,1073,1073,1073,
+1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,1074,
+1074,1074,1074,1074,1074,1074,262,262,1075,1075,1075,1075,1075,1075,1075,1075,
+1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,1076,
+1076,1076,1076,262,262,262,262,262,1077,1077,1077,1077,1077,1077,1077,1077,
+
+/* block 164 */
+1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,1078,
+1078,1078,262,262,262,262,262,262,262,1079,1079,1079,1079,262,262,262,
+262,262,262,262,262,262,262,262,262,1080,1080,1080,1080,1080,1080,1080,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 165 */
+1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,
+1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,
+1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,
+1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,1081,
+1081,1081,1081,1081,1081,1081,1081,1081,1081,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 166 */
+1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,
+1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,
+1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,1082,
+1082,1082,1082,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,
+1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,
+1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,1083,
+1083,1083,1083,262,262,262,262,262,262,262,1084,1084,1084,1084,1084,1084,
+
+/* block 167 */
+1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,
+1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,1085,
+1085,1085,1086,1086,1087,1087,1087,1087,302,302,302,302,302,302,302,302,
+1088,1088,1088,1088,1088,1088,1088,1088,1088,1088,302,302,302,302,302,302,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 168 */
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 169 */
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,
+1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,1089,262,
+
+/* block 170 */
+1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,
+1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,
+1090,1090,1090,1090,1090,1090,1090,1090,1090,1090,262,1091,1091,1092,262,262,
+1090,1090,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 171 */
+1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,
+1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1093,1094,1094,1094,
+1094,1094,1094,1094,1094,1094,1094,1093,262,262,262,262,262,262,262,262,
+1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,1095,
+1095,1095,1095,1095,1095,1095,1096,1096,1096,1096,1096,1096,1096,1096,1096,1096,
+1096,1097,1097,1097,1097,1098,1098,1098,1098,1098,302,302,302,302,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,1099,
+
+/* block 172 */
+1099,1099,1100,1100,1100,1100,1101,1101,1101,1101,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,1102,
+1102,1102,1102,1102,1102,1103,1103,1103,1103,1103,1103,1103,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,1104,
+1104,1104,1104,1104,1104,1104,1104,262,262,262,262,262,262,262,262,262,
+
+/* block 173 */
+1105,1106,1105,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,
+1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,
+1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,1107,
+1107,1107,1107,1107,1107,1107,1107,1107,1106,1106,1106,1106,1106,1106,1106,1106,
+1106,1106,1106,1106,1106,1106,1108,1109,1109,1110,1110,1110,1110,1110,163,163,
+163,163,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,1111,
+1111,1111,1111,1111,1111,1111,1112,1112,1112,1112,1112,1112,1112,1112,1112,1112,
+1108,1107,1107,1106,1106,1107,163,163,163,163,163,163,163,163,163,1113,
+
+/* block 174 */
+1114,1114,1115,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,
+1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,
+1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,1116,
+1115,1115,1115,1117,1117,1117,1117,1115,1115,1118,1119,1120,1120,1121,1122,1122,
+1122,1122,1117,163,163,163,163,163,163,163,163,163,163,1121,163,163,
+1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,1123,
+1123,1123,1123,1123,1123,1123,1123,1123,1123,163,163,163,163,163,163,163,
+1124,1124,1124,1124,1124,1124,1124,1124,1124,1124,163,163,163,163,163,163,
+
+/* block 175 */
+1125,1125,1125,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,
+1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,1126,
+1126,1126,1126,1126,1126,1126,1126,1125,1125,1125,1125,1125,1127,1125,1125,1125,
+1125,1125,1125,1128,1128,163,1129,1129,1129,1129,1129,1129,1129,1129,1129,1129,
+1130,1131,1131,1131,1126,1127,1127,1126,163,163,163,163,163,163,163,163,
+1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,
+1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,1132,
+1132,1132,1132,1133,1134,1134,1132,163,163,163,163,163,163,163,163,163,
+
+/* block 176 */
+1135,1135,1136,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,
+1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,
+1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,1137,
+1137,1137,1137,1136,1136,1136,1135,1135,1135,1135,1135,1135,1135,1135,1135,1136,
+1138,1137,1139,1139,1137,1140,1140,1141,1141,1142,1143,1143,1143,1140,1136,1135,
+1144,1144,1144,1144,1144,1144,1144,1144,1144,1144,1137,1141,1137,1141,1140,1140,
+163,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,1145,
+1145,1145,1145,1145,1145,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 177 */
+1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,
+1146,1146,163,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,
+1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1146,1147,1147,1147,1148,
+1148,1148,1147,1147,1148,1149,1150,1148,1151,1151,1152,1151,1151,1153,1148,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 178 */
+1154,1154,1154,1154,1154,1154,1154,163,1154,163,1154,1154,1154,1154,163,1154,
+1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,1154,163,1154,
+1154,1154,1154,1154,1154,1154,1154,1154,1154,1155,163,163,163,163,163,163,
+1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,
+1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,
+1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1156,1157,
+1158,1158,1158,1157,1157,1157,1157,1157,1157,1159,1160,163,163,163,163,163,
+1161,1161,1161,1161,1161,1161,1161,1161,1161,1161,163,163,163,163,163,163,
+
+/* block 179 */
+1162,1163,1164,1165,163,1166,1166,1166,1166,1166,1166,1166,1166,163,163,1166,
+1166,163,163,1166,1166,1166,1166,1166,1166,1166,1166,1166,1166,1166,1166,1166,
+1166,1166,1166,1166,1166,1166,1166,1166,1166,163,1166,1166,1166,1166,1166,1166,
+1166,163,1166,1166,163,1166,1166,1166,1166,1166,163,1167,1168,1166,1169,1164,
+1162,1164,1164,1164,1164,163,163,1164,1164,163,163,1164,1164,1170,163,163,
+1166,163,163,163,163,163,163,1169,163,163,163,163,163,1171,1166,1166,
+1166,1166,1164,1164,163,163,1172,1172,1172,1172,1172,1172,1172,163,163,163,
+1172,1172,1172,1172,1172,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 180 */
+1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,
+1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,
+1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,1173,
+1173,1173,1173,1173,1173,1174,1174,1174,1175,1175,1175,1175,1175,1175,1175,1175,
+1174,1174,1176,1175,1175,1174,1177,1173,1173,1173,1173,1178,1178,1179,1180,1180,
+1181,1181,1181,1181,1181,1181,1181,1181,1181,1181,1179,1179,163,1180,1182,1173,
+1173,1173,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 181 */
+1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,
+1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,
+1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,1183,
+1184,1185,1185,1186,1186,1186,1186,1186,1186,1185,1186,1185,1185,1184,1185,1186,
+1186,1185,1187,1188,1183,1183,1189,1183,163,163,163,163,163,163,163,163,
+1190,1190,1190,1190,1190,1190,1190,1190,1190,1190,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 182 */
+1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,
+1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,
+1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1191,1192,
+1193,1193,1194,1194,1194,1194,163,163,1193,1193,1193,1193,1194,1194,1193,1195,
+1196,1197,1198,1198,1199,1199,1200,1200,1200,1198,1198,1198,1198,1198,1198,1198,
+1198,1198,1198,1198,1198,1198,1198,1198,1191,1191,1191,1191,1194,1194,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 183 */
+1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,
+1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,
+1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,1201,
+1202,1202,1202,1203,1203,1203,1203,1203,1203,1203,1203,1202,1202,1203,1202,1204,
+1203,1205,1205,1206,1201,163,163,163,163,163,163,163,163,163,163,163,
+1207,1207,1207,1207,1207,1207,1207,1207,1207,1207,163,163,163,163,163,163,
+530,530,530,530,530,530,530,530,530,530,530,530,530,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 184 */
+1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,
+1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,
+1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1208,1209,1210,1209,1210,1210,
+1209,1209,1209,1209,1209,1209,1211,1212,1208,1213,163,163,163,163,163,163,
+1214,1214,1214,1214,1214,1214,1214,1214,1214,1214,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 185 */
+1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,
+1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,1215,163,163,1216,1216,1216,
+1217,1217,1216,1216,1216,1216,1218,1216,1216,1216,1216,1219,163,163,163,163,
+1220,1220,1220,1220,1220,1220,1220,1220,1220,1220,1221,1221,1222,1222,1222,1223,
+1215,1215,1215,1215,1215,1215,1215,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 186 */
+1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,
+1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,
+1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1224,1225,1225,1225,1226,
+1226,1226,1226,1226,1226,1226,1226,1226,1225,1227,1228,1229,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 187 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,
+1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,1230,
+1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,
+1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,1231,
+1232,1232,1232,1232,1232,1232,1232,1232,1232,1232,1233,1233,1233,1233,1233,1233,
+1233,1233,1233,163,163,163,163,163,163,163,163,163,163,163,163,1234,
+
+/* block 188 */
+1235,1235,1235,1235,1235,1235,1235,163,163,1235,163,163,1235,1235,1235,1235,
+1235,1235,1235,1235,163,1235,1235,163,1235,1235,1235,1235,1235,1235,1235,1235,
+1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,1235,
+1236,1237,1237,1237,1237,1237,163,1237,1237,163,163,1238,1238,1239,1240,1241,
+1237,1241,1237,1242,1243,1244,1243,163,163,163,163,163,163,163,163,163,
+1245,1245,1245,1245,1245,1245,1245,1245,1245,1245,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 189 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1246,1246,1246,1246,1246,1246,1246,1246,163,163,1246,1246,1246,1246,1246,1246,
+1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,
+1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,1246,
+1246,1247,1247,1247,1248,1248,1248,1248,163,163,1248,1248,1247,1247,1247,1247,
+1249,1246,1250,1246,1247,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 190 */
+1251,1252,1252,1252,1252,1252,1252,1253,1253,1252,1252,1251,1251,1251,1251,1251,
+1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,
+1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,1251,
+1251,1251,1251,1254,1255,1252,1252,1252,1252,1256,1257,1252,1252,1252,1252,1258,
+1258,1258,1259,1259,1258,1258,1258,1255,163,163,163,163,163,163,163,163,
+1260,1261,1261,1261,1261,1261,1261,1262,1262,1261,1261,1261,1260,1260,1260,1260,
+1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,
+1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,1260,
+
+/* block 191 */
+1260,1260,1260,1260,1263,1263,1263,1263,1263,1263,1261,1261,1261,1261,1261,1261,
+1261,1261,1261,1261,1261,1261,1261,1262,1264,1265,1266,1267,1267,1260,1266,1266,
+1266,1268,1268,163,163,163,163,163,163,163,163,163,163,163,163,163,
+495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,
+1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,
+1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,
+1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,1269,
+1269,1269,1269,1269,1269,1269,1269,1269,1269,163,163,163,163,163,163,163,
+
+/* block 192 */
+1270,1270,1270,1270,1270,1270,1270,1270,1270,163,1270,1270,1270,1270,1270,1270,
+1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,
+1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1270,1271,
+1272,1272,1272,1272,1272,1272,1272,163,1272,1272,1272,1272,1272,1272,1271,1273,
+1270,1274,1274,1275,1276,1276,163,163,163,163,163,163,163,163,163,163,
+1277,1277,1277,1277,1277,1277,1277,1277,1277,1277,1278,1278,1278,1278,1278,1278,
+1278,1278,1278,1278,1278,1278,1278,1278,1278,1278,1278,1278,1278,163,163,163,
+1279,1280,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,
+
+/* block 193 */
+1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,1281,
+163,163,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,1282,
+1282,1282,1282,1282,1282,1282,1282,1282,163,1283,1282,1282,1282,1282,1282,1282,
+1282,1283,1282,1282,1283,1282,1282,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 194 */
+1284,1284,1284,1284,1284,1284,1284,163,1284,1284,163,1284,1284,1284,1284,1284,
+1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,
+1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,1284,
+1284,1285,1285,1285,1285,1285,1285,163,163,163,1285,163,1285,1285,163,1285,
+1285,1285,1286,1285,1287,1287,1288,1285,163,163,163,163,163,163,163,163,
+1289,1289,1289,1289,1289,1289,1289,1289,1289,1289,163,163,163,163,163,163,
+1290,1290,1290,1290,1290,1290,163,1290,1290,163,1290,1290,1290,1290,1290,1290,
+1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,
+
+/* block 195 */
+1290,1290,1290,1290,1290,1290,1290,1290,1290,1290,1291,1291,1291,1291,1291,163,
+1292,1292,163,1291,1291,1292,1291,1293,1290,163,163,163,163,163,163,163,
+1294,1294,1294,1294,1294,1294,1294,1294,1294,1294,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 196 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,1295,
+1295,1295,1295,1296,1296,1297,1297,1298,1298,163,163,163,163,163,163,163,
+
+/* block 197 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+842,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,1299,
+388,388,1299,388,1299,390,390,390,390,390,390,390,390,391,391,391,
+391,390,390,390,390,390,390,390,390,390,390,390,390,390,390,390,
+390,390,163,163,163,163,163,163,163,163,163,163,163,163,163,1300,
+
+/* block 198 */
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+
+/* block 199 */
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 200 */
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,
+1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,1302,163,
+1303,1303,1303,1303,1303,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 201 */
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,1301,
+1301,1301,1301,1301,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 202 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,1304,
+1304,1305,1305,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 203 */
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+
+/* block 204 */
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,
+1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,1306,163,
+1307,1307,1307,1307,1307,1307,1307,1307,1307,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 205 */
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+
+/* block 206 */
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,1308,
+1308,1308,1308,1308,1308,1308,1308,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 207 */
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+
+/* block 208 */
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,858,
+858,858,858,858,858,858,858,858,858,163,163,163,163,163,163,163,
+1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,
+1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,1309,163,
+1310,1310,1310,1310,1310,1310,1310,1310,1310,1310,163,163,163,163,1311,1311,
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,
+
+/* block 209 */
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,
+1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,1312,163,
+1313,1313,1313,1313,1313,1313,1313,1313,1313,1313,163,163,163,163,163,163,
+1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,
+1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,1314,163,163,
+1315,1315,1315,1315,1315,1316,163,163,163,163,163,163,163,163,163,163,
+
+/* block 210 */
+1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+1318,1318,1318,1318,1318,1318,1318,1319,1319,1320,1321,1321,1322,1322,1322,1322,
+1323,1323,1324,1324,1319,1322,163,163,163,163,163,163,163,163,163,163,
+1325,1325,1325,1325,1325,1325,1325,1325,1325,1325,163,1326,1326,1326,1326,1326,
+1326,1326,163,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+1317,1317,1317,1317,1317,1317,1317,1317,163,163,163,163,163,1317,1317,1317,
+
+/* block 211 */
+1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,1317,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 212 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,
+1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,1327,
+1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,
+1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,1328,
+
+/* block 213 */
+1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,1329,
+1329,1329,1329,1329,1329,1329,1329,1330,1331,1332,1332,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 214 */
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,
+1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,1333,163,163,163,163,1334,
+1333,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,
+1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,
+1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,1335,
+
+/* block 215 */
+1335,1335,1335,1335,1335,1335,1335,1335,163,163,163,163,163,163,163,1336,
+1336,1336,1336,1337,1337,1337,1337,1337,1337,1337,1337,1337,1337,1337,1337,1337,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1338,1339,1340,799,1341,163,163,163,163,163,163,163,163,163,163,163,
+1342,1342,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 216 */
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+
+/* block 217 */
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,1343,
+1343,1343,1343,1343,1343,1343,1343,1343,163,163,163,163,163,163,163,163,
+
+/* block 218 */
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+
+/* block 219 */
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,1344,
+1344,1344,1344,1344,1344,1344,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 220 */
+1343,1343,1343,1343,1343,1343,1343,1343,1343,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 221 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1345,1345,1345,1345,163,1345,1345,1345,1345,1345,1345,1345,163,1345,1345,163,
+
+/* block 222 */
+824,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+
+/* block 223 */
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+
+/* block 224 */
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,819,
+824,824,824,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+819,819,819,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,824,824,824,824,163,163,163,163,163,163,163,163,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+
+/* block 225 */
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+
+/* block 226 */
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,
+1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,1346,163,163,163,163,
+
+/* block 227 */
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,163,163,163,163,163,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,163,163,163,
+
+/* block 228 */
+1347,1347,1347,1347,1347,1347,1347,1347,1347,163,163,163,163,163,163,163,
+1347,1347,1347,1347,1347,1347,1347,1347,1347,1347,163,163,1348,1349,1350,1351,
+1352,1352,1352,1352,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 229 */
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,163,163,
+154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+154,154,154,154,154,154,154,163,163,163,163,163,163,163,163,163,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+
+/* block 230 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 231 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+
+/* block 232 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,163,163,163,163,163,163,163,163,163,163,
+
+/* block 233 */
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,163,163,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,1353,1354,154,154,154,460,460,460,1355,1356,1356,
+1356,1356,1356, 51, 51, 51, 51, 51, 51, 51, 51,154,154,154,154,154,
+
+/* block 234 */
+154,154,154,460,460,154,154,154,154,154,154,154,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,154,154,154,154,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,723,723,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 235 */
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,
+1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,1002,
+1002,1002,1357,1357,1357,1002,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 236 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+835,835,835,835,835,835,835,835,835,835,835,835,835,835,835,835,
+835,835,835,835,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 237 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,163,163,163,163,163,163,163,163,163,
+832,832,832,832,832,832,832,832,832,832,832,832,832,832,832,832,
+832,832,835,835,835,835,835,835,835,163,163,163,163,163,163,163,
+
+/* block 238 */
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,725,725,725,725,725,725,
+725,725,736,736,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,725,725,
+725,725,725,725,725,163,736,736,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+
+/* block 239 */
+724,724,725,725,725,725,725,725,725,725,736,736,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,724,163,724,724,
+163,163,724,163,163,724,724,163,163,724,724,724,724,163,724,724,
+724,724,724,724,724,724,725,725,725,725,163,725,163,725,736,736,
+725,725,725,725,163,725,725,725,725,725,725,725,725,725,725,725,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,725,725,725,725,725,725,
+725,725,736,736,725,725,725,725,725,725,725,725,725,725,725,725,
+
+/* block 240 */
+725,725,725,725,724,724,163,724,724,724,724,163,163,724,724,724,
+724,724,724,724,724,163,724,724,724,724,724,724,724,163,725,725,
+725,725,725,725,725,725,736,736,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,724,724,163,724,724,724,724,163,
+724,724,724,724,724,163,724,163,163,163,724,724,724,724,724,724,
+724,163,725,725,725,725,725,725,725,725,736,736,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+
+/* block 241 */
+724,724,724,724,724,724,725,725,725,725,725,725,725,725,736,736,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,725,725,725,725,725,725,
+725,725,736,736,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,725,725,
+725,725,725,725,725,725,736,736,725,725,725,725,725,725,725,725,
+
+/* block 242 */
+725,725,725,725,725,725,725,725,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,725,725,725,725,725,725,725,725,736,736,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,725,725,725,725,725,725,725,725,736,736,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+
+/* block 243 */
+724,724,724,724,724,724,724,724,724,724,725,725,725,725,725,725,
+725,725,736,736,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,163,163,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,1358,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,715,725,725,725,725,
+725,725,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,1358,725,725,725,725,
+
+/* block 244 */
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,715,725,725,725,725,725,725,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,1358,725,725,725,725,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,715,
+725,725,725,725,725,725,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,1358,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+
+/* block 245 */
+725,725,725,725,725,725,725,725,725,715,725,725,725,725,725,725,
+724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,724,
+724,724,724,724,724,724,724,724,724,1358,725,725,725,725,725,725,
+725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,725,
+725,725,725,715,725,725,725,725,725,725,724,725,163,163,1359,1359,
+1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,
+1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,
+1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,1359,
+
+/* block 246 */
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+
+/* block 247 */
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1360,1360,1360,1360,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1360,1360,1360,
+1360,1360,1360,1360,1360,1361,1360,1360,1360,1360,1360,1360,1360,1360,1360,1360,
+
+/* block 248 */
+1360,1360,1360,1360,1361,1360,1360,1362,1363,1362,1362,1364,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,1361,1361,1361,1361,1361,
+163,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,1361,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 249 */
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 92, 70, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,643, 70, 70, 70, 70,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 250 */
+1365,1365,1365,1365,1365,1365,1365,163,1365,1365,1365,1365,1365,1365,1365,1365,
+1365,1365,1365,1365,1365,1365,1365,1365,1365,163,163,1365,1365,1365,1365,1365,
+1365,1365,163,1365,1365,163,1365,1365,1365,1365,1365,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 251 */
+1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,
+1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,
+1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,1366,163,163,163,
+1367,1367,1367,1367,1367,1367,1367,1368,1368,1368,1368,1368,1369,1369,163,163,
+1370,1370,1370,1370,1370,1370,1370,1370,1370,1370,163,163,163,163,1366,1371,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 252 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,
+1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1372,1373,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,
+1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,
+1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1374,1375,1375,1375,1375,
+1376,1376,1376,1376,1376,1376,1376,1376,1376,1376,163,163,163,163,163,1377,
+
+/* block 253 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+483,483,483,483,483,483,483,163,483,483,483,483,163,483,483,163,
+483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,163,
+
+/* block 254 */
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+
+/* block 255 */
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,1378,
+1378,1378,1378,1378,1378,262,262,1379,1379,1379,1379,1379,1379,1379,1379,1379,
+1380,1380,1380,1380,1380,1380,1380,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 256 */
+1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,
+1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,1381,
+1381,1381,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,
+1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,1382,
+1382,1382,1382,1382,1383,1383,1383,1384,1385,1385,1385,1386,262,262,262,262,
+1387,1387,1387,1387,1387,1387,1387,1387,1387,1387,262,262,262,262,1388,1388,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 257 */
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+302,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+
+/* block 258 */
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1390,1389,1389,1389,
+1391,1389,1389,1389,1389,302,302,302,302,302,302,302,302,302,302,302,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 259 */
+302,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1390,1389,
+1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,1389,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,262,
+
+/* block 260 */
+1392,1392,1392,1392,302,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,
+1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,
+302,1392,1392,302,1392,302,302,1392,302,1392,1392,1392,1392,1392,1392,1392,
+1392,1392,1392,302,1392,1392,1392,1392,302,1392,302,1392,302,302,302,302,
+302,302,1392,302,302,302,302,1392,302,1392,302,1392,302,1392,1392,1392,
+302,1392,1392,302,1392,302,302,1392,302,1392,302,1392,302,1392,302,1392,
+302,1392,1392,302,1392,302,302,1392,1392,1392,1392,302,1392,1392,1392,1392,
+1392,1392,1392,302,1392,1392,1392,1392,302,1392,1392,1392,1392,302,1392,302,
+
+/* block 261 */
+1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,302,1392,1392,1392,1392,1392,
+1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,302,302,302,302,
+302,1392,1392,1392,302,1392,1392,1392,1392,1392,302,1392,1392,1392,1392,1392,
+1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,1392,302,302,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+274,274,302,302,302,302,302,302,302,302,302,302,302,302,302,302,
+
+/* block 262 */
+1393,1393,1393,1393,1394,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1395,1395,1395,1395,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+
+/* block 263 */
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1395,
+1395,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1395,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1394,
+1395,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 264 */
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 58, 58,1393,1393,1393,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,1393,
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,460,460,460,460,460,460,
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,723,723,1393,1393,1393,1393,
+1397,1397,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,1397,1397,
+
+/* block 265 */
+1396,1396,1396,1396,1396,1396,1396,1396,1396,1396,460,460,460,460,1398,460,
+460,1398,1398,1398,1398,1398,1398,1398,1398,1398,1398,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,1393,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,
+1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,1399,
+
+/* block 266 */
+1400,1398,1401,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+460,460,460,460,460,460,460,460,460,460,1398,460,460,460,460,460,
+460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,1398,
+460,460,1398,1398,1398,1398,1398,1401,1398,1398,1398,460,1395,1395,1395,1395,
+460,460,460,460,460,460,460,460,460,1395,1395,1395,1395,1395,1395,1395,
+1402,1402,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1393,1393,1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 267 */
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 268 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,727,1393,1393,727,727,727,727,727,727,727,727,727,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,727,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,727,1394,1394,
+
+/* block 269 */
+1394,1394,1394,1394,1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1393,1393,727,727,1393,727,727,727,1393,1393,727,727,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1403,1403,1403,1394,1394,1403,1394,1394,1403,1404,1404,727,727,1394,
+1394,1394,1394,1394,727,727,727,727,727,727,727,727,727,727,727,727,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1393,1393,727,1394,727,1393,727,1394,1394,1394,1405,1405,1405,1405,1405,
+
+/* block 270 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,727,
+1394,727,1403,1403,1394,1394,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,
+1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,
+1403,1403,1403,1403,1403,1403,1403,1403,1403,1394,1394,1394,1403,1394,1394,1394,
+
+/* block 271 */
+1394,1403,1403,1403,1394,1403,1403,1403,1394,1394,1394,1394,1394,1394,1394,1403,
+1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1403,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,727,1393,1394,
+
+/* block 272 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,723,723,
+723,723,723,723,723,723,1393,1393,1393,727,727,1394,1394,1394,1394,1393,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1393,1393,1393,1393,1393,1393,1393,727,
+727,1393,1393,727,1404,1404,727,727,727,727,1403,1393,1393,1393,1393,1393,
+
+/* block 273 */
+1393,1393,1393,1393,1393,1393,1393,727,1393,1393,727,727,727,727,1393,1393,
+1404,1393,1393,1393,1393,1403,1403,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1394,727,1393,1393,727,1393,1393,1393,1393,1393,1393,1393,
+1393,727,727,1393,1393,1393,1393,1393,1393,1393,1393,1393,727,1393,1393,1393,
+1393,1393,727,727,727,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,727,727,727,1393,1393,1393,1393,1393,1393,1393,1393,727,727,727,1393,
+1393,727,1393,727,1393,1393,1393,1393,727,1393,1393,1393,1393,1393,1393,727,
+1393,1393,1393,727,1393,1393,1393,1393,1393,1393,727,1394,1394,1394,1394,1394,
+
+/* block 274 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1403,1403,1403,1394,1394,1394,1403,1403,1403,1403,1403,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+
+/* block 275 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1403,1403,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1403,1394,1394,1394,1394,1394,1393,1393,1393,1393,1393,727,1403,727,727,727,
+1394,1394,1394,1393,1393,1394,1394,1394,1395,1395,1395,1395,1395,1394,1394,1394,
+727,727,727,727,727,727,1393,1393,1393,727,1393,1394,1394,1395,1395,1395,
+727,1393,1393,727,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,
+
+/* block 276 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 277 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,
+1394,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 278 */
+723,723,723,723,723,723,723,723,723,723,723,723,1395,1395,1395,1395,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,1395,1395,1395,1395,1395,1395,1395,1395,
+723,723,723,723,723,723,723,723,723,723,1395,1395,1395,1395,1395,1395,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+
+/* block 279 */
+723,723,723,723,723,723,723,723,1395,1395,1395,1395,1395,1395,1395,1395,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,1395,1395,
+1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 280 */
+723,723,723,723,723,723,723,723,723,723,723,723,1403,1394,1394,1403,
+1394,1394,1394,1394,1394,1394,1394,1394,1403,1403,1403,1403,1403,1403,1403,1403,
+1394,1394,1394,1394,1394,1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1394,723,1403,1403,1403,1394,
+1394,1394,1394,1394,1394,1394,723,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1403,1394,1394,1394,1394,1394,1394,1394,1394,
+
+/* block 281 */
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1406,1406,1406,1406,1394,1403,1403,1394,1403,1403,1394,1403,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1403,1403,1403,
+1394,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1403,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+
+/* block 282 */
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,
+1393,1393,1393,1393,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1393,1395,1395,
+1394,1394,1394,1394,1394,1395,1395,1395,1394,1394,1394,1394,1394,1395,1395,1395,
+
+/* block 283 */
+1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,1395,
+1394,1394,1394,1403,1403,1403,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,1395,1395,
+1394,1394,1394,1394,1394,1394,1394,1394,1395,1395,1395,1395,1395,1395,1395,1395,
+1403,1403,1403,1403,1403,1403,1403,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+
+/* block 284 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+
+/* block 285 */
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,163,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,723,
+723,723,723,723,723,723,723,723,723,723,723,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+1407,1407,1407,1407,1407,1407,1407,1407,1407,1407,163,163,163,163,163,163,
+
+/* block 286 */
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,
+1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,1395,958,958,
+
+/* block 287 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 288 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,163,163,163,163,163,163,163,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+
+/* block 289 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,163,163,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+
+/* block 290 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+
+/* block 291 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 292 */
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,953,
+953,953,953,953,953,953,953,953,953,953,953,953,953,953,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 293 */
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,958,958,
+
+/* block 294 */
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,838,
+838,838,838,838,838,838,838,838,838,838,838,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,
+
+/* block 295 */
+707,712,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,1408,
+
+/* block 296 */
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+
+/* block 297 */
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+
+/* block 298 */
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,961,
+707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,707,
+
+/* block 299 */
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,952,
+952,952,952,952,952,952,952,952,952,952,952,952,952,952,958,958,
+};
+
+#if UCD_BLOCK_SIZE != 128
+#error Please correct UCD_BLOCK_SIZE in pcre2_internal.h
+#endif
+#endif /* SUPPORT_UNICODE */
+
+#endif /* PCRE2_PCRE2TEST */
+
+/* End of pcre2_ucd.c */
diff --git a/contrib/libs/pcre2/src/pcre2_ucp.h b/contrib/libs/pcre2/src/pcre2_ucp.h
new file mode 100644
index 0000000000..282238982d
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_ucp.h
@@ -0,0 +1,394 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+This module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!
+Instead, modify the maint/GenerateUcpHeader.py script and run it to generate
+a new version of this code.
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef PCRE2_UCP_H_IDEMPOTENT_GUARD
+#define PCRE2_UCP_H_IDEMPOTENT_GUARD
+
+/* This file contains definitions of the Unicode property values that are
+returned by the UCD access macros and used throughout PCRE2.
+
+IMPORTANT: The specific values of the first two enums (general and particular
+character categories) are assumed by the table called catposstab in the file
+pcre2_auto_possess.c. They are unlikely to change, but should be checked after
+an update. */
+
+/* These are the general character categories. */
+
+enum {
+ ucp_C,
+ ucp_L,
+ ucp_M,
+ ucp_N,
+ ucp_P,
+ ucp_S,
+ ucp_Z,
+};
+
+/* These are the particular character categories. */
+
+enum {
+ ucp_Cc, /* Control */
+ ucp_Cf, /* Format */
+ ucp_Cn, /* Unassigned */
+ ucp_Co, /* Private use */
+ ucp_Cs, /* Surrogate */
+ ucp_Ll, /* Lower case letter */
+ ucp_Lm, /* Modifier letter */
+ ucp_Lo, /* Other letter */
+ ucp_Lt, /* Title case letter */
+ ucp_Lu, /* Upper case letter */
+ ucp_Mc, /* Spacing mark */
+ ucp_Me, /* Enclosing mark */
+ ucp_Mn, /* Non-spacing mark */
+ ucp_Nd, /* Decimal number */
+ ucp_Nl, /* Letter number */
+ ucp_No, /* Other number */
+ ucp_Pc, /* Connector punctuation */
+ ucp_Pd, /* Dash punctuation */
+ ucp_Pe, /* Close punctuation */
+ ucp_Pf, /* Final punctuation */
+ ucp_Pi, /* Initial punctuation */
+ ucp_Po, /* Other punctuation */
+ ucp_Ps, /* Open punctuation */
+ ucp_Sc, /* Currency symbol */
+ ucp_Sk, /* Modifier symbol */
+ ucp_Sm, /* Mathematical symbol */
+ ucp_So, /* Other symbol */
+ ucp_Zl, /* Line separator */
+ ucp_Zp, /* Paragraph separator */
+ ucp_Zs, /* Space separator */
+};
+
+/* These are Boolean properties. */
+
+enum {
+ ucp_ASCII,
+ ucp_ASCII_Hex_Digit,
+ ucp_Alphabetic,
+ ucp_Bidi_Control,
+ ucp_Bidi_Mirrored,
+ ucp_Case_Ignorable,
+ ucp_Cased,
+ ucp_Changes_When_Casefolded,
+ ucp_Changes_When_Casemapped,
+ ucp_Changes_When_Lowercased,
+ ucp_Changes_When_Titlecased,
+ ucp_Changes_When_Uppercased,
+ ucp_Dash,
+ ucp_Default_Ignorable_Code_Point,
+ ucp_Deprecated,
+ ucp_Diacritic,
+ ucp_Emoji,
+ ucp_Emoji_Component,
+ ucp_Emoji_Modifier,
+ ucp_Emoji_Modifier_Base,
+ ucp_Emoji_Presentation,
+ ucp_Extended_Pictographic,
+ ucp_Extender,
+ ucp_Grapheme_Base,
+ ucp_Grapheme_Extend,
+ ucp_Grapheme_Link,
+ ucp_Hex_Digit,
+ ucp_IDS_Binary_Operator,
+ ucp_IDS_Trinary_Operator,
+ ucp_ID_Continue,
+ ucp_ID_Start,
+ ucp_Ideographic,
+ ucp_Join_Control,
+ ucp_Logical_Order_Exception,
+ ucp_Lowercase,
+ ucp_Math,
+ ucp_Noncharacter_Code_Point,
+ ucp_Pattern_Syntax,
+ ucp_Pattern_White_Space,
+ ucp_Prepended_Concatenation_Mark,
+ ucp_Quotation_Mark,
+ ucp_Radical,
+ ucp_Regional_Indicator,
+ ucp_Sentence_Terminal,
+ ucp_Soft_Dotted,
+ ucp_Terminal_Punctuation,
+ ucp_Unified_Ideograph,
+ ucp_Uppercase,
+ ucp_Variation_Selector,
+ ucp_White_Space,
+ ucp_XID_Continue,
+ ucp_XID_Start,
+ /* This must be last */
+ ucp_Bprop_Count
+};
+
+/* Size of entries in ucd_boolprop_sets[] */
+
+#define ucd_boolprop_sets_item_size 2
+
+/* These are the bidi class values. */
+
+enum {
+ ucp_bidiAL, /* Arabic letter */
+ ucp_bidiAN, /* Arabic number */
+ ucp_bidiB, /* Paragraph separator */
+ ucp_bidiBN, /* Boundary neutral */
+ ucp_bidiCS, /* Common separator */
+ ucp_bidiEN, /* European number */
+ ucp_bidiES, /* European separator */
+ ucp_bidiET, /* European terminator */
+ ucp_bidiFSI, /* First strong isolate */
+ ucp_bidiL, /* Left to right */
+ ucp_bidiLRE, /* Left to right embedding */
+ ucp_bidiLRI, /* Left to right isolate */
+ ucp_bidiLRO, /* Left to right override */
+ ucp_bidiNSM, /* Non-spacing mark */
+ ucp_bidiON, /* Other neutral */
+ ucp_bidiPDF, /* Pop directional format */
+ ucp_bidiPDI, /* Pop directional isolate */
+ ucp_bidiR, /* Right to left */
+ ucp_bidiRLE, /* Right to left embedding */
+ ucp_bidiRLI, /* Right to left isolate */
+ ucp_bidiRLO, /* Right to left override */
+ ucp_bidiS, /* Segment separator */
+ ucp_bidiWS, /* White space */
+};
+
+/* These are grapheme break properties. The Extended Pictographic property
+comes from the emoji-data.txt file. */
+
+enum {
+ ucp_gbCR, /* 0 */
+ ucp_gbLF, /* 1 */
+ ucp_gbControl, /* 2 */
+ ucp_gbExtend, /* 3 */
+ ucp_gbPrepend, /* 4 */
+ ucp_gbSpacingMark, /* 5 */
+ ucp_gbL, /* 6 Hangul syllable type L */
+ ucp_gbV, /* 7 Hangul syllable type V */
+ ucp_gbT, /* 8 Hangul syllable type T */
+ ucp_gbLV, /* 9 Hangul syllable type LV */
+ ucp_gbLVT, /* 10 Hangul syllable type LVT */
+ ucp_gbRegional_Indicator, /* 11 */
+ ucp_gbOther, /* 12 */
+ ucp_gbZWJ, /* 13 */
+ ucp_gbExtended_Pictographic, /* 14 */
+};
+
+/* These are the script identifications. */
+
+enum {
+ /* Scripts which has characters in other scripts. */
+ ucp_Latin,
+ ucp_Greek,
+ ucp_Cyrillic,
+ ucp_Arabic,
+ ucp_Syriac,
+ ucp_Thaana,
+ ucp_Devanagari,
+ ucp_Bengali,
+ ucp_Gurmukhi,
+ ucp_Gujarati,
+ ucp_Oriya,
+ ucp_Tamil,
+ ucp_Telugu,
+ ucp_Kannada,
+ ucp_Malayalam,
+ ucp_Sinhala,
+ ucp_Myanmar,
+ ucp_Georgian,
+ ucp_Hangul,
+ ucp_Mongolian,
+ ucp_Hiragana,
+ ucp_Katakana,
+ ucp_Bopomofo,
+ ucp_Han,
+ ucp_Yi,
+ ucp_Tagalog,
+ ucp_Hanunoo,
+ ucp_Buhid,
+ ucp_Tagbanwa,
+ ucp_Limbu,
+ ucp_Tai_Le,
+ ucp_Linear_B,
+ ucp_Cypriot,
+ ucp_Buginese,
+ ucp_Coptic,
+ ucp_Glagolitic,
+ ucp_Syloti_Nagri,
+ ucp_Phags_Pa,
+ ucp_Nko,
+ ucp_Kayah_Li,
+ ucp_Javanese,
+ ucp_Kaithi,
+ ucp_Mandaic,
+ ucp_Chakma,
+ ucp_Sharada,
+ ucp_Takri,
+ ucp_Duployan,
+ ucp_Grantha,
+ ucp_Khojki,
+ ucp_Linear_A,
+ ucp_Mahajani,
+ ucp_Manichaean,
+ ucp_Modi,
+ ucp_Old_Permic,
+ ucp_Psalter_Pahlavi,
+ ucp_Khudawadi,
+ ucp_Tirhuta,
+ ucp_Multani,
+ ucp_Adlam,
+ ucp_Masaram_Gondi,
+ ucp_Dogra,
+ ucp_Gunjala_Gondi,
+ ucp_Hanifi_Rohingya,
+ ucp_Sogdian,
+ ucp_Nandinagari,
+ ucp_Yezidi,
+ ucp_Cypro_Minoan,
+ ucp_Old_Uyghur,
+
+ /* Scripts which has no characters in other scripts. */
+ ucp_Unknown,
+ ucp_Common,
+ ucp_Armenian,
+ ucp_Hebrew,
+ ucp_Thai,
+ ucp_Lao,
+ ucp_Tibetan,
+ ucp_Ethiopic,
+ ucp_Cherokee,
+ ucp_Canadian_Aboriginal,
+ ucp_Ogham,
+ ucp_Runic,
+ ucp_Khmer,
+ ucp_Old_Italic,
+ ucp_Gothic,
+ ucp_Deseret,
+ ucp_Inherited,
+ ucp_Ugaritic,
+ ucp_Shavian,
+ ucp_Osmanya,
+ ucp_Braille,
+ ucp_New_Tai_Lue,
+ ucp_Tifinagh,
+ ucp_Old_Persian,
+ ucp_Kharoshthi,
+ ucp_Balinese,
+ ucp_Cuneiform,
+ ucp_Phoenician,
+ ucp_Sundanese,
+ ucp_Lepcha,
+ ucp_Ol_Chiki,
+ ucp_Vai,
+ ucp_Saurashtra,
+ ucp_Rejang,
+ ucp_Lycian,
+ ucp_Carian,
+ ucp_Lydian,
+ ucp_Cham,
+ ucp_Tai_Tham,
+ ucp_Tai_Viet,
+ ucp_Avestan,
+ ucp_Egyptian_Hieroglyphs,
+ ucp_Samaritan,
+ ucp_Lisu,
+ ucp_Bamum,
+ ucp_Meetei_Mayek,
+ ucp_Imperial_Aramaic,
+ ucp_Old_South_Arabian,
+ ucp_Inscriptional_Parthian,
+ ucp_Inscriptional_Pahlavi,
+ ucp_Old_Turkic,
+ ucp_Batak,
+ ucp_Brahmi,
+ ucp_Meroitic_Cursive,
+ ucp_Meroitic_Hieroglyphs,
+ ucp_Miao,
+ ucp_Sora_Sompeng,
+ ucp_Caucasian_Albanian,
+ ucp_Bassa_Vah,
+ ucp_Elbasan,
+ ucp_Pahawh_Hmong,
+ ucp_Mende_Kikakui,
+ ucp_Mro,
+ ucp_Old_North_Arabian,
+ ucp_Nabataean,
+ ucp_Palmyrene,
+ ucp_Pau_Cin_Hau,
+ ucp_Siddham,
+ ucp_Warang_Citi,
+ ucp_Ahom,
+ ucp_Anatolian_Hieroglyphs,
+ ucp_Hatran,
+ ucp_Old_Hungarian,
+ ucp_SignWriting,
+ ucp_Bhaiksuki,
+ ucp_Marchen,
+ ucp_Newa,
+ ucp_Osage,
+ ucp_Tangut,
+ ucp_Nushu,
+ ucp_Soyombo,
+ ucp_Zanabazar_Square,
+ ucp_Makasar,
+ ucp_Medefaidrin,
+ ucp_Old_Sogdian,
+ ucp_Elymaic,
+ ucp_Nyiakeng_Puachue_Hmong,
+ ucp_Wancho,
+ ucp_Chorasmian,
+ ucp_Dives_Akuru,
+ ucp_Khitan_Small_Script,
+ ucp_Tangsa,
+ ucp_Toto,
+ ucp_Vithkuqi,
+
+ /* This must be last */
+ ucp_Script_Count
+};
+
+/* Size of entries in ucd_script_sets[] */
+
+#define ucd_script_sets_item_size 3
+
+#endif /* PCRE2_UCP_H_IDEMPOTENT_GUARD */
+
+/* End of pcre2_ucp.h */
diff --git a/contrib/libs/pcre2/src/pcre2_ucptables.c b/contrib/libs/pcre2/src/pcre2_ucptables.c
new file mode 100644
index 0000000000..bd1b67a9f1
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_ucptables.c
@@ -0,0 +1,1524 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+This module is auto-generated from Unicode data files. DO NOT EDIT MANUALLY!
+Instead, modify the maint/GenerateUcpTables.py script and run it to generate
+a new version of this code.
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+#ifdef SUPPORT_UNICODE
+
+/* The PRIV(utt)[] table below translates Unicode property names into type and
+code values. It is searched by binary chop, so must be in collating sequence of
+name. Originally, the table contained pointers to the name strings in the first
+field of each entry. However, that leads to a large number of relocations when
+a shared library is dynamically loaded. A significant reduction is made by
+putting all the names into a single, large string and using offsets instead.
+All letters are lower cased, and underscores are removed, in accordance with
+the "loose matching" rules that Unicode advises and Perl uses. */
+
+#define STRING_adlam0 STR_a STR_d STR_l STR_a STR_m "\0"
+#define STRING_adlm0 STR_a STR_d STR_l STR_m "\0"
+#define STRING_aghb0 STR_a STR_g STR_h STR_b "\0"
+#define STRING_ahex0 STR_a STR_h STR_e STR_x "\0"
+#define STRING_ahom0 STR_a STR_h STR_o STR_m "\0"
+#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0"
+#define STRING_alphabetic0 STR_a STR_l STR_p STR_h STR_a STR_b STR_e STR_t STR_i STR_c "\0"
+#define STRING_anatolianhieroglyphs0 STR_a STR_n STR_a STR_t STR_o STR_l STR_i STR_a STR_n STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0"
+#define STRING_any0 STR_a STR_n STR_y "\0"
+#define STRING_arab0 STR_a STR_r STR_a STR_b "\0"
+#define STRING_arabic0 STR_a STR_r STR_a STR_b STR_i STR_c "\0"
+#define STRING_armenian0 STR_a STR_r STR_m STR_e STR_n STR_i STR_a STR_n "\0"
+#define STRING_armi0 STR_a STR_r STR_m STR_i "\0"
+#define STRING_armn0 STR_a STR_r STR_m STR_n "\0"
+#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0"
+#define STRING_asciihexdigit0 STR_a STR_s STR_c STR_i STR_i STR_h STR_e STR_x STR_d STR_i STR_g STR_i STR_t "\0"
+#define STRING_avestan0 STR_a STR_v STR_e STR_s STR_t STR_a STR_n "\0"
+#define STRING_avst0 STR_a STR_v STR_s STR_t "\0"
+#define STRING_bali0 STR_b STR_a STR_l STR_i "\0"
+#define STRING_balinese0 STR_b STR_a STR_l STR_i STR_n STR_e STR_s STR_e "\0"
+#define STRING_bamu0 STR_b STR_a STR_m STR_u "\0"
+#define STRING_bamum0 STR_b STR_a STR_m STR_u STR_m "\0"
+#define STRING_bass0 STR_b STR_a STR_s STR_s "\0"
+#define STRING_bassavah0 STR_b STR_a STR_s STR_s STR_a STR_v STR_a STR_h "\0"
+#define STRING_batak0 STR_b STR_a STR_t STR_a STR_k "\0"
+#define STRING_batk0 STR_b STR_a STR_t STR_k "\0"
+#define STRING_beng0 STR_b STR_e STR_n STR_g "\0"
+#define STRING_bengali0 STR_b STR_e STR_n STR_g STR_a STR_l STR_i "\0"
+#define STRING_bhaiksuki0 STR_b STR_h STR_a STR_i STR_k STR_s STR_u STR_k STR_i "\0"
+#define STRING_bhks0 STR_b STR_h STR_k STR_s "\0"
+#define STRING_bidial0 STR_b STR_i STR_d STR_i STR_a STR_l "\0"
+#define STRING_bidian0 STR_b STR_i STR_d STR_i STR_a STR_n "\0"
+#define STRING_bidib0 STR_b STR_i STR_d STR_i STR_b "\0"
+#define STRING_bidibn0 STR_b STR_i STR_d STR_i STR_b STR_n "\0"
+#define STRING_bidic0 STR_b STR_i STR_d STR_i STR_c "\0"
+#define STRING_bidicontrol0 STR_b STR_i STR_d STR_i STR_c STR_o STR_n STR_t STR_r STR_o STR_l "\0"
+#define STRING_bidics0 STR_b STR_i STR_d STR_i STR_c STR_s "\0"
+#define STRING_bidien0 STR_b STR_i STR_d STR_i STR_e STR_n "\0"
+#define STRING_bidies0 STR_b STR_i STR_d STR_i STR_e STR_s "\0"
+#define STRING_bidiet0 STR_b STR_i STR_d STR_i STR_e STR_t "\0"
+#define STRING_bidifsi0 STR_b STR_i STR_d STR_i STR_f STR_s STR_i "\0"
+#define STRING_bidil0 STR_b STR_i STR_d STR_i STR_l "\0"
+#define STRING_bidilre0 STR_b STR_i STR_d STR_i STR_l STR_r STR_e "\0"
+#define STRING_bidilri0 STR_b STR_i STR_d STR_i STR_l STR_r STR_i "\0"
+#define STRING_bidilro0 STR_b STR_i STR_d STR_i STR_l STR_r STR_o "\0"
+#define STRING_bidim0 STR_b STR_i STR_d STR_i STR_m "\0"
+#define STRING_bidimirrored0 STR_b STR_i STR_d STR_i STR_m STR_i STR_r STR_r STR_o STR_r STR_e STR_d "\0"
+#define STRING_bidinsm0 STR_b STR_i STR_d STR_i STR_n STR_s STR_m "\0"
+#define STRING_bidion0 STR_b STR_i STR_d STR_i STR_o STR_n "\0"
+#define STRING_bidipdf0 STR_b STR_i STR_d STR_i STR_p STR_d STR_f "\0"
+#define STRING_bidipdi0 STR_b STR_i STR_d STR_i STR_p STR_d STR_i "\0"
+#define STRING_bidir0 STR_b STR_i STR_d STR_i STR_r "\0"
+#define STRING_bidirle0 STR_b STR_i STR_d STR_i STR_r STR_l STR_e "\0"
+#define STRING_bidirli0 STR_b STR_i STR_d STR_i STR_r STR_l STR_i "\0"
+#define STRING_bidirlo0 STR_b STR_i STR_d STR_i STR_r STR_l STR_o "\0"
+#define STRING_bidis0 STR_b STR_i STR_d STR_i STR_s "\0"
+#define STRING_bidiws0 STR_b STR_i STR_d STR_i STR_w STR_s "\0"
+#define STRING_bopo0 STR_b STR_o STR_p STR_o "\0"
+#define STRING_bopomofo0 STR_b STR_o STR_p STR_o STR_m STR_o STR_f STR_o "\0"
+#define STRING_brah0 STR_b STR_r STR_a STR_h "\0"
+#define STRING_brahmi0 STR_b STR_r STR_a STR_h STR_m STR_i "\0"
+#define STRING_brai0 STR_b STR_r STR_a STR_i "\0"
+#define STRING_braille0 STR_b STR_r STR_a STR_i STR_l STR_l STR_e "\0"
+#define STRING_bugi0 STR_b STR_u STR_g STR_i "\0"
+#define STRING_buginese0 STR_b STR_u STR_g STR_i STR_n STR_e STR_s STR_e "\0"
+#define STRING_buhd0 STR_b STR_u STR_h STR_d "\0"
+#define STRING_buhid0 STR_b STR_u STR_h STR_i STR_d "\0"
+#define STRING_c0 STR_c "\0"
+#define STRING_cakm0 STR_c STR_a STR_k STR_m "\0"
+#define STRING_canadianaboriginal0 STR_c STR_a STR_n STR_a STR_d STR_i STR_a STR_n STR_a STR_b STR_o STR_r STR_i STR_g STR_i STR_n STR_a STR_l "\0"
+#define STRING_cans0 STR_c STR_a STR_n STR_s "\0"
+#define STRING_cari0 STR_c STR_a STR_r STR_i "\0"
+#define STRING_carian0 STR_c STR_a STR_r STR_i STR_a STR_n "\0"
+#define STRING_cased0 STR_c STR_a STR_s STR_e STR_d "\0"
+#define STRING_caseignorable0 STR_c STR_a STR_s STR_e STR_i STR_g STR_n STR_o STR_r STR_a STR_b STR_l STR_e "\0"
+#define STRING_caucasianalbanian0 STR_c STR_a STR_u STR_c STR_a STR_s STR_i STR_a STR_n STR_a STR_l STR_b STR_a STR_n STR_i STR_a STR_n "\0"
+#define STRING_cc0 STR_c STR_c "\0"
+#define STRING_cf0 STR_c STR_f "\0"
+#define STRING_chakma0 STR_c STR_h STR_a STR_k STR_m STR_a "\0"
+#define STRING_cham0 STR_c STR_h STR_a STR_m "\0"
+#define STRING_changeswhencasefolded0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_c STR_a STR_s STR_e STR_f STR_o STR_l STR_d STR_e STR_d "\0"
+#define STRING_changeswhencasemapped0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_c STR_a STR_s STR_e STR_m STR_a STR_p STR_p STR_e STR_d "\0"
+#define STRING_changeswhenlowercased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_l STR_o STR_w STR_e STR_r STR_c STR_a STR_s STR_e STR_d "\0"
+#define STRING_changeswhentitlecased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_t STR_i STR_t STR_l STR_e STR_c STR_a STR_s STR_e STR_d "\0"
+#define STRING_changeswhenuppercased0 STR_c STR_h STR_a STR_n STR_g STR_e STR_s STR_w STR_h STR_e STR_n STR_u STR_p STR_p STR_e STR_r STR_c STR_a STR_s STR_e STR_d "\0"
+#define STRING_cher0 STR_c STR_h STR_e STR_r "\0"
+#define STRING_cherokee0 STR_c STR_h STR_e STR_r STR_o STR_k STR_e STR_e "\0"
+#define STRING_chorasmian0 STR_c STR_h STR_o STR_r STR_a STR_s STR_m STR_i STR_a STR_n "\0"
+#define STRING_chrs0 STR_c STR_h STR_r STR_s "\0"
+#define STRING_ci0 STR_c STR_i "\0"
+#define STRING_cn0 STR_c STR_n "\0"
+#define STRING_co0 STR_c STR_o "\0"
+#define STRING_common0 STR_c STR_o STR_m STR_m STR_o STR_n "\0"
+#define STRING_copt0 STR_c STR_o STR_p STR_t "\0"
+#define STRING_coptic0 STR_c STR_o STR_p STR_t STR_i STR_c "\0"
+#define STRING_cpmn0 STR_c STR_p STR_m STR_n "\0"
+#define STRING_cprt0 STR_c STR_p STR_r STR_t "\0"
+#define STRING_cs0 STR_c STR_s "\0"
+#define STRING_cuneiform0 STR_c STR_u STR_n STR_e STR_i STR_f STR_o STR_r STR_m "\0"
+#define STRING_cwcf0 STR_c STR_w STR_c STR_f "\0"
+#define STRING_cwcm0 STR_c STR_w STR_c STR_m "\0"
+#define STRING_cwl0 STR_c STR_w STR_l "\0"
+#define STRING_cwt0 STR_c STR_w STR_t "\0"
+#define STRING_cwu0 STR_c STR_w STR_u "\0"
+#define STRING_cypriot0 STR_c STR_y STR_p STR_r STR_i STR_o STR_t "\0"
+#define STRING_cyprominoan0 STR_c STR_y STR_p STR_r STR_o STR_m STR_i STR_n STR_o STR_a STR_n "\0"
+#define STRING_cyrillic0 STR_c STR_y STR_r STR_i STR_l STR_l STR_i STR_c "\0"
+#define STRING_cyrl0 STR_c STR_y STR_r STR_l "\0"
+#define STRING_dash0 STR_d STR_a STR_s STR_h "\0"
+#define STRING_defaultignorablecodepoint0 STR_d STR_e STR_f STR_a STR_u STR_l STR_t STR_i STR_g STR_n STR_o STR_r STR_a STR_b STR_l STR_e STR_c STR_o STR_d STR_e STR_p STR_o STR_i STR_n STR_t "\0"
+#define STRING_dep0 STR_d STR_e STR_p "\0"
+#define STRING_deprecated0 STR_d STR_e STR_p STR_r STR_e STR_c STR_a STR_t STR_e STR_d "\0"
+#define STRING_deseret0 STR_d STR_e STR_s STR_e STR_r STR_e STR_t "\0"
+#define STRING_deva0 STR_d STR_e STR_v STR_a "\0"
+#define STRING_devanagari0 STR_d STR_e STR_v STR_a STR_n STR_a STR_g STR_a STR_r STR_i "\0"
+#define STRING_di0 STR_d STR_i "\0"
+#define STRING_dia0 STR_d STR_i STR_a "\0"
+#define STRING_diacritic0 STR_d STR_i STR_a STR_c STR_r STR_i STR_t STR_i STR_c "\0"
+#define STRING_diak0 STR_d STR_i STR_a STR_k "\0"
+#define STRING_divesakuru0 STR_d STR_i STR_v STR_e STR_s STR_a STR_k STR_u STR_r STR_u "\0"
+#define STRING_dogr0 STR_d STR_o STR_g STR_r "\0"
+#define STRING_dogra0 STR_d STR_o STR_g STR_r STR_a "\0"
+#define STRING_dsrt0 STR_d STR_s STR_r STR_t "\0"
+#define STRING_dupl0 STR_d STR_u STR_p STR_l "\0"
+#define STRING_duployan0 STR_d STR_u STR_p STR_l STR_o STR_y STR_a STR_n "\0"
+#define STRING_ebase0 STR_e STR_b STR_a STR_s STR_e "\0"
+#define STRING_ecomp0 STR_e STR_c STR_o STR_m STR_p "\0"
+#define STRING_egyp0 STR_e STR_g STR_y STR_p "\0"
+#define STRING_egyptianhieroglyphs0 STR_e STR_g STR_y STR_p STR_t STR_i STR_a STR_n STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0"
+#define STRING_elba0 STR_e STR_l STR_b STR_a "\0"
+#define STRING_elbasan0 STR_e STR_l STR_b STR_a STR_s STR_a STR_n "\0"
+#define STRING_elym0 STR_e STR_l STR_y STR_m "\0"
+#define STRING_elymaic0 STR_e STR_l STR_y STR_m STR_a STR_i STR_c "\0"
+#define STRING_emod0 STR_e STR_m STR_o STR_d "\0"
+#define STRING_emoji0 STR_e STR_m STR_o STR_j STR_i "\0"
+#define STRING_emojicomponent0 STR_e STR_m STR_o STR_j STR_i STR_c STR_o STR_m STR_p STR_o STR_n STR_e STR_n STR_t "\0"
+#define STRING_emojimodifier0 STR_e STR_m STR_o STR_j STR_i STR_m STR_o STR_d STR_i STR_f STR_i STR_e STR_r "\0"
+#define STRING_emojimodifierbase0 STR_e STR_m STR_o STR_j STR_i STR_m STR_o STR_d STR_i STR_f STR_i STR_e STR_r STR_b STR_a STR_s STR_e "\0"
+#define STRING_emojipresentation0 STR_e STR_m STR_o STR_j STR_i STR_p STR_r STR_e STR_s STR_e STR_n STR_t STR_a STR_t STR_i STR_o STR_n "\0"
+#define STRING_epres0 STR_e STR_p STR_r STR_e STR_s "\0"
+#define STRING_ethi0 STR_e STR_t STR_h STR_i "\0"
+#define STRING_ethiopic0 STR_e STR_t STR_h STR_i STR_o STR_p STR_i STR_c "\0"
+#define STRING_ext0 STR_e STR_x STR_t "\0"
+#define STRING_extendedpictographic0 STR_e STR_x STR_t STR_e STR_n STR_d STR_e STR_d STR_p STR_i STR_c STR_t STR_o STR_g STR_r STR_a STR_p STR_h STR_i STR_c "\0"
+#define STRING_extender0 STR_e STR_x STR_t STR_e STR_n STR_d STR_e STR_r "\0"
+#define STRING_extpict0 STR_e STR_x STR_t STR_p STR_i STR_c STR_t "\0"
+#define STRING_geor0 STR_g STR_e STR_o STR_r "\0"
+#define STRING_georgian0 STR_g STR_e STR_o STR_r STR_g STR_i STR_a STR_n "\0"
+#define STRING_glag0 STR_g STR_l STR_a STR_g "\0"
+#define STRING_glagolitic0 STR_g STR_l STR_a STR_g STR_o STR_l STR_i STR_t STR_i STR_c "\0"
+#define STRING_gong0 STR_g STR_o STR_n STR_g "\0"
+#define STRING_gonm0 STR_g STR_o STR_n STR_m "\0"
+#define STRING_goth0 STR_g STR_o STR_t STR_h "\0"
+#define STRING_gothic0 STR_g STR_o STR_t STR_h STR_i STR_c "\0"
+#define STRING_gran0 STR_g STR_r STR_a STR_n "\0"
+#define STRING_grantha0 STR_g STR_r STR_a STR_n STR_t STR_h STR_a "\0"
+#define STRING_graphemebase0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_b STR_a STR_s STR_e "\0"
+#define STRING_graphemeextend0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_e STR_x STR_t STR_e STR_n STR_d "\0"
+#define STRING_graphemelink0 STR_g STR_r STR_a STR_p STR_h STR_e STR_m STR_e STR_l STR_i STR_n STR_k "\0"
+#define STRING_grbase0 STR_g STR_r STR_b STR_a STR_s STR_e "\0"
+#define STRING_greek0 STR_g STR_r STR_e STR_e STR_k "\0"
+#define STRING_grek0 STR_g STR_r STR_e STR_k "\0"
+#define STRING_grext0 STR_g STR_r STR_e STR_x STR_t "\0"
+#define STRING_grlink0 STR_g STR_r STR_l STR_i STR_n STR_k "\0"
+#define STRING_gujarati0 STR_g STR_u STR_j STR_a STR_r STR_a STR_t STR_i "\0"
+#define STRING_gujr0 STR_g STR_u STR_j STR_r "\0"
+#define STRING_gunjalagondi0 STR_g STR_u STR_n STR_j STR_a STR_l STR_a STR_g STR_o STR_n STR_d STR_i "\0"
+#define STRING_gurmukhi0 STR_g STR_u STR_r STR_m STR_u STR_k STR_h STR_i "\0"
+#define STRING_guru0 STR_g STR_u STR_r STR_u "\0"
+#define STRING_han0 STR_h STR_a STR_n "\0"
+#define STRING_hang0 STR_h STR_a STR_n STR_g "\0"
+#define STRING_hangul0 STR_h STR_a STR_n STR_g STR_u STR_l "\0"
+#define STRING_hani0 STR_h STR_a STR_n STR_i "\0"
+#define STRING_hanifirohingya0 STR_h STR_a STR_n STR_i STR_f STR_i STR_r STR_o STR_h STR_i STR_n STR_g STR_y STR_a "\0"
+#define STRING_hano0 STR_h STR_a STR_n STR_o "\0"
+#define STRING_hanunoo0 STR_h STR_a STR_n STR_u STR_n STR_o STR_o "\0"
+#define STRING_hatr0 STR_h STR_a STR_t STR_r "\0"
+#define STRING_hatran0 STR_h STR_a STR_t STR_r STR_a STR_n "\0"
+#define STRING_hebr0 STR_h STR_e STR_b STR_r "\0"
+#define STRING_hebrew0 STR_h STR_e STR_b STR_r STR_e STR_w "\0"
+#define STRING_hex0 STR_h STR_e STR_x "\0"
+#define STRING_hexdigit0 STR_h STR_e STR_x STR_d STR_i STR_g STR_i STR_t "\0"
+#define STRING_hira0 STR_h STR_i STR_r STR_a "\0"
+#define STRING_hiragana0 STR_h STR_i STR_r STR_a STR_g STR_a STR_n STR_a "\0"
+#define STRING_hluw0 STR_h STR_l STR_u STR_w "\0"
+#define STRING_hmng0 STR_h STR_m STR_n STR_g "\0"
+#define STRING_hmnp0 STR_h STR_m STR_n STR_p "\0"
+#define STRING_hung0 STR_h STR_u STR_n STR_g "\0"
+#define STRING_idc0 STR_i STR_d STR_c "\0"
+#define STRING_idcontinue0 STR_i STR_d STR_c STR_o STR_n STR_t STR_i STR_n STR_u STR_e "\0"
+#define STRING_ideo0 STR_i STR_d STR_e STR_o "\0"
+#define STRING_ideographic0 STR_i STR_d STR_e STR_o STR_g STR_r STR_a STR_p STR_h STR_i STR_c "\0"
+#define STRING_ids0 STR_i STR_d STR_s "\0"
+#define STRING_idsb0 STR_i STR_d STR_s STR_b "\0"
+#define STRING_idsbinaryoperator0 STR_i STR_d STR_s STR_b STR_i STR_n STR_a STR_r STR_y STR_o STR_p STR_e STR_r STR_a STR_t STR_o STR_r "\0"
+#define STRING_idst0 STR_i STR_d STR_s STR_t "\0"
+#define STRING_idstart0 STR_i STR_d STR_s STR_t STR_a STR_r STR_t "\0"
+#define STRING_idstrinaryoperator0 STR_i STR_d STR_s STR_t STR_r STR_i STR_n STR_a STR_r STR_y STR_o STR_p STR_e STR_r STR_a STR_t STR_o STR_r "\0"
+#define STRING_imperialaramaic0 STR_i STR_m STR_p STR_e STR_r STR_i STR_a STR_l STR_a STR_r STR_a STR_m STR_a STR_i STR_c "\0"
+#define STRING_inherited0 STR_i STR_n STR_h STR_e STR_r STR_i STR_t STR_e STR_d "\0"
+#define STRING_inscriptionalpahlavi0 STR_i STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_p STR_a STR_h STR_l STR_a STR_v STR_i "\0"
+#define STRING_inscriptionalparthian0 STR_i STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_p STR_a STR_r STR_t STR_h STR_i STR_a STR_n "\0"
+#define STRING_ital0 STR_i STR_t STR_a STR_l "\0"
+#define STRING_java0 STR_j STR_a STR_v STR_a "\0"
+#define STRING_javanese0 STR_j STR_a STR_v STR_a STR_n STR_e STR_s STR_e "\0"
+#define STRING_joinc0 STR_j STR_o STR_i STR_n STR_c "\0"
+#define STRING_joincontrol0 STR_j STR_o STR_i STR_n STR_c STR_o STR_n STR_t STR_r STR_o STR_l "\0"
+#define STRING_kaithi0 STR_k STR_a STR_i STR_t STR_h STR_i "\0"
+#define STRING_kali0 STR_k STR_a STR_l STR_i "\0"
+#define STRING_kana0 STR_k STR_a STR_n STR_a "\0"
+#define STRING_kannada0 STR_k STR_a STR_n STR_n STR_a STR_d STR_a "\0"
+#define STRING_katakana0 STR_k STR_a STR_t STR_a STR_k STR_a STR_n STR_a "\0"
+#define STRING_kayahli0 STR_k STR_a STR_y STR_a STR_h STR_l STR_i "\0"
+#define STRING_khar0 STR_k STR_h STR_a STR_r "\0"
+#define STRING_kharoshthi0 STR_k STR_h STR_a STR_r STR_o STR_s STR_h STR_t STR_h STR_i "\0"
+#define STRING_khitansmallscript0 STR_k STR_h STR_i STR_t STR_a STR_n STR_s STR_m STR_a STR_l STR_l STR_s STR_c STR_r STR_i STR_p STR_t "\0"
+#define STRING_khmer0 STR_k STR_h STR_m STR_e STR_r "\0"
+#define STRING_khmr0 STR_k STR_h STR_m STR_r "\0"
+#define STRING_khoj0 STR_k STR_h STR_o STR_j "\0"
+#define STRING_khojki0 STR_k STR_h STR_o STR_j STR_k STR_i "\0"
+#define STRING_khudawadi0 STR_k STR_h STR_u STR_d STR_a STR_w STR_a STR_d STR_i "\0"
+#define STRING_kits0 STR_k STR_i STR_t STR_s "\0"
+#define STRING_knda0 STR_k STR_n STR_d STR_a "\0"
+#define STRING_kthi0 STR_k STR_t STR_h STR_i "\0"
+#define STRING_l0 STR_l "\0"
+#define STRING_l_AMPERSAND0 STR_l STR_AMPERSAND "\0"
+#define STRING_lana0 STR_l STR_a STR_n STR_a "\0"
+#define STRING_lao0 STR_l STR_a STR_o "\0"
+#define STRING_laoo0 STR_l STR_a STR_o STR_o "\0"
+#define STRING_latin0 STR_l STR_a STR_t STR_i STR_n "\0"
+#define STRING_latn0 STR_l STR_a STR_t STR_n "\0"
+#define STRING_lc0 STR_l STR_c "\0"
+#define STRING_lepc0 STR_l STR_e STR_p STR_c "\0"
+#define STRING_lepcha0 STR_l STR_e STR_p STR_c STR_h STR_a "\0"
+#define STRING_limb0 STR_l STR_i STR_m STR_b "\0"
+#define STRING_limbu0 STR_l STR_i STR_m STR_b STR_u "\0"
+#define STRING_lina0 STR_l STR_i STR_n STR_a "\0"
+#define STRING_linb0 STR_l STR_i STR_n STR_b "\0"
+#define STRING_lineara0 STR_l STR_i STR_n STR_e STR_a STR_r STR_a "\0"
+#define STRING_linearb0 STR_l STR_i STR_n STR_e STR_a STR_r STR_b "\0"
+#define STRING_lisu0 STR_l STR_i STR_s STR_u "\0"
+#define STRING_ll0 STR_l STR_l "\0"
+#define STRING_lm0 STR_l STR_m "\0"
+#define STRING_lo0 STR_l STR_o "\0"
+#define STRING_loe0 STR_l STR_o STR_e "\0"
+#define STRING_logicalorderexception0 STR_l STR_o STR_g STR_i STR_c STR_a STR_l STR_o STR_r STR_d STR_e STR_r STR_e STR_x STR_c STR_e STR_p STR_t STR_i STR_o STR_n "\0"
+#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0"
+#define STRING_lowercase0 STR_l STR_o STR_w STR_e STR_r STR_c STR_a STR_s STR_e "\0"
+#define STRING_lt0 STR_l STR_t "\0"
+#define STRING_lu0 STR_l STR_u "\0"
+#define STRING_lyci0 STR_l STR_y STR_c STR_i "\0"
+#define STRING_lycian0 STR_l STR_y STR_c STR_i STR_a STR_n "\0"
+#define STRING_lydi0 STR_l STR_y STR_d STR_i "\0"
+#define STRING_lydian0 STR_l STR_y STR_d STR_i STR_a STR_n "\0"
+#define STRING_m0 STR_m "\0"
+#define STRING_mahajani0 STR_m STR_a STR_h STR_a STR_j STR_a STR_n STR_i "\0"
+#define STRING_mahj0 STR_m STR_a STR_h STR_j "\0"
+#define STRING_maka0 STR_m STR_a STR_k STR_a "\0"
+#define STRING_makasar0 STR_m STR_a STR_k STR_a STR_s STR_a STR_r "\0"
+#define STRING_malayalam0 STR_m STR_a STR_l STR_a STR_y STR_a STR_l STR_a STR_m "\0"
+#define STRING_mand0 STR_m STR_a STR_n STR_d "\0"
+#define STRING_mandaic0 STR_m STR_a STR_n STR_d STR_a STR_i STR_c "\0"
+#define STRING_mani0 STR_m STR_a STR_n STR_i "\0"
+#define STRING_manichaean0 STR_m STR_a STR_n STR_i STR_c STR_h STR_a STR_e STR_a STR_n "\0"
+#define STRING_marc0 STR_m STR_a STR_r STR_c "\0"
+#define STRING_marchen0 STR_m STR_a STR_r STR_c STR_h STR_e STR_n "\0"
+#define STRING_masaramgondi0 STR_m STR_a STR_s STR_a STR_r STR_a STR_m STR_g STR_o STR_n STR_d STR_i "\0"
+#define STRING_math0 STR_m STR_a STR_t STR_h "\0"
+#define STRING_mc0 STR_m STR_c "\0"
+#define STRING_me0 STR_m STR_e "\0"
+#define STRING_medefaidrin0 STR_m STR_e STR_d STR_e STR_f STR_a STR_i STR_d STR_r STR_i STR_n "\0"
+#define STRING_medf0 STR_m STR_e STR_d STR_f "\0"
+#define STRING_meeteimayek0 STR_m STR_e STR_e STR_t STR_e STR_i STR_m STR_a STR_y STR_e STR_k "\0"
+#define STRING_mend0 STR_m STR_e STR_n STR_d "\0"
+#define STRING_mendekikakui0 STR_m STR_e STR_n STR_d STR_e STR_k STR_i STR_k STR_a STR_k STR_u STR_i "\0"
+#define STRING_merc0 STR_m STR_e STR_r STR_c "\0"
+#define STRING_mero0 STR_m STR_e STR_r STR_o "\0"
+#define STRING_meroiticcursive0 STR_m STR_e STR_r STR_o STR_i STR_t STR_i STR_c STR_c STR_u STR_r STR_s STR_i STR_v STR_e "\0"
+#define STRING_meroitichieroglyphs0 STR_m STR_e STR_r STR_o STR_i STR_t STR_i STR_c STR_h STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0"
+#define STRING_miao0 STR_m STR_i STR_a STR_o "\0"
+#define STRING_mlym0 STR_m STR_l STR_y STR_m "\0"
+#define STRING_mn0 STR_m STR_n "\0"
+#define STRING_modi0 STR_m STR_o STR_d STR_i "\0"
+#define STRING_mong0 STR_m STR_o STR_n STR_g "\0"
+#define STRING_mongolian0 STR_m STR_o STR_n STR_g STR_o STR_l STR_i STR_a STR_n "\0"
+#define STRING_mro0 STR_m STR_r STR_o "\0"
+#define STRING_mroo0 STR_m STR_r STR_o STR_o "\0"
+#define STRING_mtei0 STR_m STR_t STR_e STR_i "\0"
+#define STRING_mult0 STR_m STR_u STR_l STR_t "\0"
+#define STRING_multani0 STR_m STR_u STR_l STR_t STR_a STR_n STR_i "\0"
+#define STRING_myanmar0 STR_m STR_y STR_a STR_n STR_m STR_a STR_r "\0"
+#define STRING_mymr0 STR_m STR_y STR_m STR_r "\0"
+#define STRING_n0 STR_n "\0"
+#define STRING_nabataean0 STR_n STR_a STR_b STR_a STR_t STR_a STR_e STR_a STR_n "\0"
+#define STRING_nand0 STR_n STR_a STR_n STR_d "\0"
+#define STRING_nandinagari0 STR_n STR_a STR_n STR_d STR_i STR_n STR_a STR_g STR_a STR_r STR_i "\0"
+#define STRING_narb0 STR_n STR_a STR_r STR_b "\0"
+#define STRING_nbat0 STR_n STR_b STR_a STR_t "\0"
+#define STRING_nchar0 STR_n STR_c STR_h STR_a STR_r "\0"
+#define STRING_nd0 STR_n STR_d "\0"
+#define STRING_newa0 STR_n STR_e STR_w STR_a "\0"
+#define STRING_newtailue0 STR_n STR_e STR_w STR_t STR_a STR_i STR_l STR_u STR_e "\0"
+#define STRING_nko0 STR_n STR_k STR_o "\0"
+#define STRING_nkoo0 STR_n STR_k STR_o STR_o "\0"
+#define STRING_nl0 STR_n STR_l "\0"
+#define STRING_no0 STR_n STR_o "\0"
+#define STRING_noncharactercodepoint0 STR_n STR_o STR_n STR_c STR_h STR_a STR_r STR_a STR_c STR_t STR_e STR_r STR_c STR_o STR_d STR_e STR_p STR_o STR_i STR_n STR_t "\0"
+#define STRING_nshu0 STR_n STR_s STR_h STR_u "\0"
+#define STRING_nushu0 STR_n STR_u STR_s STR_h STR_u "\0"
+#define STRING_nyiakengpuachuehmong0 STR_n STR_y STR_i STR_a STR_k STR_e STR_n STR_g STR_p STR_u STR_a STR_c STR_h STR_u STR_e STR_h STR_m STR_o STR_n STR_g "\0"
+#define STRING_ogam0 STR_o STR_g STR_a STR_m "\0"
+#define STRING_ogham0 STR_o STR_g STR_h STR_a STR_m "\0"
+#define STRING_olchiki0 STR_o STR_l STR_c STR_h STR_i STR_k STR_i "\0"
+#define STRING_olck0 STR_o STR_l STR_c STR_k "\0"
+#define STRING_oldhungarian0 STR_o STR_l STR_d STR_h STR_u STR_n STR_g STR_a STR_r STR_i STR_a STR_n "\0"
+#define STRING_olditalic0 STR_o STR_l STR_d STR_i STR_t STR_a STR_l STR_i STR_c "\0"
+#define STRING_oldnortharabian0 STR_o STR_l STR_d STR_n STR_o STR_r STR_t STR_h STR_a STR_r STR_a STR_b STR_i STR_a STR_n "\0"
+#define STRING_oldpermic0 STR_o STR_l STR_d STR_p STR_e STR_r STR_m STR_i STR_c "\0"
+#define STRING_oldpersian0 STR_o STR_l STR_d STR_p STR_e STR_r STR_s STR_i STR_a STR_n "\0"
+#define STRING_oldsogdian0 STR_o STR_l STR_d STR_s STR_o STR_g STR_d STR_i STR_a STR_n "\0"
+#define STRING_oldsoutharabian0 STR_o STR_l STR_d STR_s STR_o STR_u STR_t STR_h STR_a STR_r STR_a STR_b STR_i STR_a STR_n "\0"
+#define STRING_oldturkic0 STR_o STR_l STR_d STR_t STR_u STR_r STR_k STR_i STR_c "\0"
+#define STRING_olduyghur0 STR_o STR_l STR_d STR_u STR_y STR_g STR_h STR_u STR_r "\0"
+#define STRING_oriya0 STR_o STR_r STR_i STR_y STR_a "\0"
+#define STRING_orkh0 STR_o STR_r STR_k STR_h "\0"
+#define STRING_orya0 STR_o STR_r STR_y STR_a "\0"
+#define STRING_osage0 STR_o STR_s STR_a STR_g STR_e "\0"
+#define STRING_osge0 STR_o STR_s STR_g STR_e "\0"
+#define STRING_osma0 STR_o STR_s STR_m STR_a "\0"
+#define STRING_osmanya0 STR_o STR_s STR_m STR_a STR_n STR_y STR_a "\0"
+#define STRING_ougr0 STR_o STR_u STR_g STR_r "\0"
+#define STRING_p0 STR_p "\0"
+#define STRING_pahawhhmong0 STR_p STR_a STR_h STR_a STR_w STR_h STR_h STR_m STR_o STR_n STR_g "\0"
+#define STRING_palm0 STR_p STR_a STR_l STR_m "\0"
+#define STRING_palmyrene0 STR_p STR_a STR_l STR_m STR_y STR_r STR_e STR_n STR_e "\0"
+#define STRING_patsyn0 STR_p STR_a STR_t STR_s STR_y STR_n "\0"
+#define STRING_patternsyntax0 STR_p STR_a STR_t STR_t STR_e STR_r STR_n STR_s STR_y STR_n STR_t STR_a STR_x "\0"
+#define STRING_patternwhitespace0 STR_p STR_a STR_t STR_t STR_e STR_r STR_n STR_w STR_h STR_i STR_t STR_e STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_patws0 STR_p STR_a STR_t STR_w STR_s "\0"
+#define STRING_pauc0 STR_p STR_a STR_u STR_c "\0"
+#define STRING_paucinhau0 STR_p STR_a STR_u STR_c STR_i STR_n STR_h STR_a STR_u "\0"
+#define STRING_pc0 STR_p STR_c "\0"
+#define STRING_pcm0 STR_p STR_c STR_m "\0"
+#define STRING_pd0 STR_p STR_d "\0"
+#define STRING_pe0 STR_p STR_e "\0"
+#define STRING_perm0 STR_p STR_e STR_r STR_m "\0"
+#define STRING_pf0 STR_p STR_f "\0"
+#define STRING_phag0 STR_p STR_h STR_a STR_g "\0"
+#define STRING_phagspa0 STR_p STR_h STR_a STR_g STR_s STR_p STR_a "\0"
+#define STRING_phli0 STR_p STR_h STR_l STR_i "\0"
+#define STRING_phlp0 STR_p STR_h STR_l STR_p "\0"
+#define STRING_phnx0 STR_p STR_h STR_n STR_x "\0"
+#define STRING_phoenician0 STR_p STR_h STR_o STR_e STR_n STR_i STR_c STR_i STR_a STR_n "\0"
+#define STRING_pi0 STR_p STR_i "\0"
+#define STRING_plrd0 STR_p STR_l STR_r STR_d "\0"
+#define STRING_po0 STR_p STR_o "\0"
+#define STRING_prependedconcatenationmark0 STR_p STR_r STR_e STR_p STR_e STR_n STR_d STR_e STR_d STR_c STR_o STR_n STR_c STR_a STR_t STR_e STR_n STR_a STR_t STR_i STR_o STR_n STR_m STR_a STR_r STR_k "\0"
+#define STRING_prti0 STR_p STR_r STR_t STR_i "\0"
+#define STRING_ps0 STR_p STR_s "\0"
+#define STRING_psalterpahlavi0 STR_p STR_s STR_a STR_l STR_t STR_e STR_r STR_p STR_a STR_h STR_l STR_a STR_v STR_i "\0"
+#define STRING_qaac0 STR_q STR_a STR_a STR_c "\0"
+#define STRING_qaai0 STR_q STR_a STR_a STR_i "\0"
+#define STRING_qmark0 STR_q STR_m STR_a STR_r STR_k "\0"
+#define STRING_quotationmark0 STR_q STR_u STR_o STR_t STR_a STR_t STR_i STR_o STR_n STR_m STR_a STR_r STR_k "\0"
+#define STRING_radical0 STR_r STR_a STR_d STR_i STR_c STR_a STR_l "\0"
+#define STRING_regionalindicator0 STR_r STR_e STR_g STR_i STR_o STR_n STR_a STR_l STR_i STR_n STR_d STR_i STR_c STR_a STR_t STR_o STR_r "\0"
+#define STRING_rejang0 STR_r STR_e STR_j STR_a STR_n STR_g "\0"
+#define STRING_ri0 STR_r STR_i "\0"
+#define STRING_rjng0 STR_r STR_j STR_n STR_g "\0"
+#define STRING_rohg0 STR_r STR_o STR_h STR_g "\0"
+#define STRING_runic0 STR_r STR_u STR_n STR_i STR_c "\0"
+#define STRING_runr0 STR_r STR_u STR_n STR_r "\0"
+#define STRING_s0 STR_s "\0"
+#define STRING_samaritan0 STR_s STR_a STR_m STR_a STR_r STR_i STR_t STR_a STR_n "\0"
+#define STRING_samr0 STR_s STR_a STR_m STR_r "\0"
+#define STRING_sarb0 STR_s STR_a STR_r STR_b "\0"
+#define STRING_saur0 STR_s STR_a STR_u STR_r "\0"
+#define STRING_saurashtra0 STR_s STR_a STR_u STR_r STR_a STR_s STR_h STR_t STR_r STR_a "\0"
+#define STRING_sc0 STR_s STR_c "\0"
+#define STRING_sd0 STR_s STR_d "\0"
+#define STRING_sentenceterminal0 STR_s STR_e STR_n STR_t STR_e STR_n STR_c STR_e STR_t STR_e STR_r STR_m STR_i STR_n STR_a STR_l "\0"
+#define STRING_sgnw0 STR_s STR_g STR_n STR_w "\0"
+#define STRING_sharada0 STR_s STR_h STR_a STR_r STR_a STR_d STR_a "\0"
+#define STRING_shavian0 STR_s STR_h STR_a STR_v STR_i STR_a STR_n "\0"
+#define STRING_shaw0 STR_s STR_h STR_a STR_w "\0"
+#define STRING_shrd0 STR_s STR_h STR_r STR_d "\0"
+#define STRING_sidd0 STR_s STR_i STR_d STR_d "\0"
+#define STRING_siddham0 STR_s STR_i STR_d STR_d STR_h STR_a STR_m "\0"
+#define STRING_signwriting0 STR_s STR_i STR_g STR_n STR_w STR_r STR_i STR_t STR_i STR_n STR_g "\0"
+#define STRING_sind0 STR_s STR_i STR_n STR_d "\0"
+#define STRING_sinh0 STR_s STR_i STR_n STR_h "\0"
+#define STRING_sinhala0 STR_s STR_i STR_n STR_h STR_a STR_l STR_a "\0"
+#define STRING_sk0 STR_s STR_k "\0"
+#define STRING_sm0 STR_s STR_m "\0"
+#define STRING_so0 STR_s STR_o "\0"
+#define STRING_softdotted0 STR_s STR_o STR_f STR_t STR_d STR_o STR_t STR_t STR_e STR_d "\0"
+#define STRING_sogd0 STR_s STR_o STR_g STR_d "\0"
+#define STRING_sogdian0 STR_s STR_o STR_g STR_d STR_i STR_a STR_n "\0"
+#define STRING_sogo0 STR_s STR_o STR_g STR_o "\0"
+#define STRING_sora0 STR_s STR_o STR_r STR_a "\0"
+#define STRING_sorasompeng0 STR_s STR_o STR_r STR_a STR_s STR_o STR_m STR_p STR_e STR_n STR_g "\0"
+#define STRING_soyo0 STR_s STR_o STR_y STR_o "\0"
+#define STRING_soyombo0 STR_s STR_o STR_y STR_o STR_m STR_b STR_o "\0"
+#define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_sterm0 STR_s STR_t STR_e STR_r STR_m "\0"
+#define STRING_sund0 STR_s STR_u STR_n STR_d "\0"
+#define STRING_sundanese0 STR_s STR_u STR_n STR_d STR_a STR_n STR_e STR_s STR_e "\0"
+#define STRING_sylo0 STR_s STR_y STR_l STR_o "\0"
+#define STRING_sylotinagri0 STR_s STR_y STR_l STR_o STR_t STR_i STR_n STR_a STR_g STR_r STR_i "\0"
+#define STRING_syrc0 STR_s STR_y STR_r STR_c "\0"
+#define STRING_syriac0 STR_s STR_y STR_r STR_i STR_a STR_c "\0"
+#define STRING_tagalog0 STR_t STR_a STR_g STR_a STR_l STR_o STR_g "\0"
+#define STRING_tagb0 STR_t STR_a STR_g STR_b "\0"
+#define STRING_tagbanwa0 STR_t STR_a STR_g STR_b STR_a STR_n STR_w STR_a "\0"
+#define STRING_taile0 STR_t STR_a STR_i STR_l STR_e "\0"
+#define STRING_taitham0 STR_t STR_a STR_i STR_t STR_h STR_a STR_m "\0"
+#define STRING_taiviet0 STR_t STR_a STR_i STR_v STR_i STR_e STR_t "\0"
+#define STRING_takr0 STR_t STR_a STR_k STR_r "\0"
+#define STRING_takri0 STR_t STR_a STR_k STR_r STR_i "\0"
+#define STRING_tale0 STR_t STR_a STR_l STR_e "\0"
+#define STRING_talu0 STR_t STR_a STR_l STR_u "\0"
+#define STRING_tamil0 STR_t STR_a STR_m STR_i STR_l "\0"
+#define STRING_taml0 STR_t STR_a STR_m STR_l "\0"
+#define STRING_tang0 STR_t STR_a STR_n STR_g "\0"
+#define STRING_tangsa0 STR_t STR_a STR_n STR_g STR_s STR_a "\0"
+#define STRING_tangut0 STR_t STR_a STR_n STR_g STR_u STR_t "\0"
+#define STRING_tavt0 STR_t STR_a STR_v STR_t "\0"
+#define STRING_telu0 STR_t STR_e STR_l STR_u "\0"
+#define STRING_telugu0 STR_t STR_e STR_l STR_u STR_g STR_u "\0"
+#define STRING_term0 STR_t STR_e STR_r STR_m "\0"
+#define STRING_terminalpunctuation0 STR_t STR_e STR_r STR_m STR_i STR_n STR_a STR_l STR_p STR_u STR_n STR_c STR_t STR_u STR_a STR_t STR_i STR_o STR_n "\0"
+#define STRING_tfng0 STR_t STR_f STR_n STR_g "\0"
+#define STRING_tglg0 STR_t STR_g STR_l STR_g "\0"
+#define STRING_thaa0 STR_t STR_h STR_a STR_a "\0"
+#define STRING_thaana0 STR_t STR_h STR_a STR_a STR_n STR_a "\0"
+#define STRING_thai0 STR_t STR_h STR_a STR_i "\0"
+#define STRING_tibetan0 STR_t STR_i STR_b STR_e STR_t STR_a STR_n "\0"
+#define STRING_tibt0 STR_t STR_i STR_b STR_t "\0"
+#define STRING_tifinagh0 STR_t STR_i STR_f STR_i STR_n STR_a STR_g STR_h "\0"
+#define STRING_tirh0 STR_t STR_i STR_r STR_h "\0"
+#define STRING_tirhuta0 STR_t STR_i STR_r STR_h STR_u STR_t STR_a "\0"
+#define STRING_tnsa0 STR_t STR_n STR_s STR_a "\0"
+#define STRING_toto0 STR_t STR_o STR_t STR_o "\0"
+#define STRING_ugar0 STR_u STR_g STR_a STR_r "\0"
+#define STRING_ugaritic0 STR_u STR_g STR_a STR_r STR_i STR_t STR_i STR_c "\0"
+#define STRING_uideo0 STR_u STR_i STR_d STR_e STR_o "\0"
+#define STRING_unifiedideograph0 STR_u STR_n STR_i STR_f STR_i STR_e STR_d STR_i STR_d STR_e STR_o STR_g STR_r STR_a STR_p STR_h "\0"
+#define STRING_unknown0 STR_u STR_n STR_k STR_n STR_o STR_w STR_n "\0"
+#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0"
+#define STRING_uppercase0 STR_u STR_p STR_p STR_e STR_r STR_c STR_a STR_s STR_e "\0"
+#define STRING_vai0 STR_v STR_a STR_i "\0"
+#define STRING_vaii0 STR_v STR_a STR_i STR_i "\0"
+#define STRING_variationselector0 STR_v STR_a STR_r STR_i STR_a STR_t STR_i STR_o STR_n STR_s STR_e STR_l STR_e STR_c STR_t STR_o STR_r "\0"
+#define STRING_vith0 STR_v STR_i STR_t STR_h "\0"
+#define STRING_vithkuqi0 STR_v STR_i STR_t STR_h STR_k STR_u STR_q STR_i "\0"
+#define STRING_vs0 STR_v STR_s "\0"
+#define STRING_wancho0 STR_w STR_a STR_n STR_c STR_h STR_o "\0"
+#define STRING_wara0 STR_w STR_a STR_r STR_a "\0"
+#define STRING_warangciti0 STR_w STR_a STR_r STR_a STR_n STR_g STR_c STR_i STR_t STR_i "\0"
+#define STRING_wcho0 STR_w STR_c STR_h STR_o "\0"
+#define STRING_whitespace0 STR_w STR_h STR_i STR_t STR_e STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_wspace0 STR_w STR_s STR_p STR_a STR_c STR_e "\0"
+#define STRING_xan0 STR_x STR_a STR_n "\0"
+#define STRING_xidc0 STR_x STR_i STR_d STR_c "\0"
+#define STRING_xidcontinue0 STR_x STR_i STR_d STR_c STR_o STR_n STR_t STR_i STR_n STR_u STR_e "\0"
+#define STRING_xids0 STR_x STR_i STR_d STR_s "\0"
+#define STRING_xidstart0 STR_x STR_i STR_d STR_s STR_t STR_a STR_r STR_t "\0"
+#define STRING_xpeo0 STR_x STR_p STR_e STR_o "\0"
+#define STRING_xps0 STR_x STR_p STR_s "\0"
+#define STRING_xsp0 STR_x STR_s STR_p "\0"
+#define STRING_xsux0 STR_x STR_s STR_u STR_x "\0"
+#define STRING_xuc0 STR_x STR_u STR_c "\0"
+#define STRING_xwd0 STR_x STR_w STR_d "\0"
+#define STRING_yezi0 STR_y STR_e STR_z STR_i "\0"
+#define STRING_yezidi0 STR_y STR_e STR_z STR_i STR_d STR_i "\0"
+#define STRING_yi0 STR_y STR_i "\0"
+#define STRING_yiii0 STR_y STR_i STR_i STR_i "\0"
+#define STRING_z0 STR_z "\0"
+#define STRING_zanabazarsquare0 STR_z STR_a STR_n STR_a STR_b STR_a STR_z STR_a STR_r STR_s STR_q STR_u STR_a STR_r STR_e "\0"
+#define STRING_zanb0 STR_z STR_a STR_n STR_b "\0"
+#define STRING_zinh0 STR_z STR_i STR_n STR_h "\0"
+#define STRING_zl0 STR_z STR_l "\0"
+#define STRING_zp0 STR_z STR_p "\0"
+#define STRING_zs0 STR_z STR_s "\0"
+#define STRING_zyyy0 STR_z STR_y STR_y STR_y "\0"
+#define STRING_zzzz0 STR_z STR_z STR_z STR_z "\0"
+
+const char PRIV(utt_names)[] =
+ STRING_adlam0
+ STRING_adlm0
+ STRING_aghb0
+ STRING_ahex0
+ STRING_ahom0
+ STRING_alpha0
+ STRING_alphabetic0
+ STRING_anatolianhieroglyphs0
+ STRING_any0
+ STRING_arab0
+ STRING_arabic0
+ STRING_armenian0
+ STRING_armi0
+ STRING_armn0
+ STRING_ascii0
+ STRING_asciihexdigit0
+ STRING_avestan0
+ STRING_avst0
+ STRING_bali0
+ STRING_balinese0
+ STRING_bamu0
+ STRING_bamum0
+ STRING_bass0
+ STRING_bassavah0
+ STRING_batak0
+ STRING_batk0
+ STRING_beng0
+ STRING_bengali0
+ STRING_bhaiksuki0
+ STRING_bhks0
+ STRING_bidial0
+ STRING_bidian0
+ STRING_bidib0
+ STRING_bidibn0
+ STRING_bidic0
+ STRING_bidicontrol0
+ STRING_bidics0
+ STRING_bidien0
+ STRING_bidies0
+ STRING_bidiet0
+ STRING_bidifsi0
+ STRING_bidil0
+ STRING_bidilre0
+ STRING_bidilri0
+ STRING_bidilro0
+ STRING_bidim0
+ STRING_bidimirrored0
+ STRING_bidinsm0
+ STRING_bidion0
+ STRING_bidipdf0
+ STRING_bidipdi0
+ STRING_bidir0
+ STRING_bidirle0
+ STRING_bidirli0
+ STRING_bidirlo0
+ STRING_bidis0
+ STRING_bidiws0
+ STRING_bopo0
+ STRING_bopomofo0
+ STRING_brah0
+ STRING_brahmi0
+ STRING_brai0
+ STRING_braille0
+ STRING_bugi0
+ STRING_buginese0
+ STRING_buhd0
+ STRING_buhid0
+ STRING_c0
+ STRING_cakm0
+ STRING_canadianaboriginal0
+ STRING_cans0
+ STRING_cari0
+ STRING_carian0
+ STRING_cased0
+ STRING_caseignorable0
+ STRING_caucasianalbanian0
+ STRING_cc0
+ STRING_cf0
+ STRING_chakma0
+ STRING_cham0
+ STRING_changeswhencasefolded0
+ STRING_changeswhencasemapped0
+ STRING_changeswhenlowercased0
+ STRING_changeswhentitlecased0
+ STRING_changeswhenuppercased0
+ STRING_cher0
+ STRING_cherokee0
+ STRING_chorasmian0
+ STRING_chrs0
+ STRING_ci0
+ STRING_cn0
+ STRING_co0
+ STRING_common0
+ STRING_copt0
+ STRING_coptic0
+ STRING_cpmn0
+ STRING_cprt0
+ STRING_cs0
+ STRING_cuneiform0
+ STRING_cwcf0
+ STRING_cwcm0
+ STRING_cwl0
+ STRING_cwt0
+ STRING_cwu0
+ STRING_cypriot0
+ STRING_cyprominoan0
+ STRING_cyrillic0
+ STRING_cyrl0
+ STRING_dash0
+ STRING_defaultignorablecodepoint0
+ STRING_dep0
+ STRING_deprecated0
+ STRING_deseret0
+ STRING_deva0
+ STRING_devanagari0
+ STRING_di0
+ STRING_dia0
+ STRING_diacritic0
+ STRING_diak0
+ STRING_divesakuru0
+ STRING_dogr0
+ STRING_dogra0
+ STRING_dsrt0
+ STRING_dupl0
+ STRING_duployan0
+ STRING_ebase0
+ STRING_ecomp0
+ STRING_egyp0
+ STRING_egyptianhieroglyphs0
+ STRING_elba0
+ STRING_elbasan0
+ STRING_elym0
+ STRING_elymaic0
+ STRING_emod0
+ STRING_emoji0
+ STRING_emojicomponent0
+ STRING_emojimodifier0
+ STRING_emojimodifierbase0
+ STRING_emojipresentation0
+ STRING_epres0
+ STRING_ethi0
+ STRING_ethiopic0
+ STRING_ext0
+ STRING_extendedpictographic0
+ STRING_extender0
+ STRING_extpict0
+ STRING_geor0
+ STRING_georgian0
+ STRING_glag0
+ STRING_glagolitic0
+ STRING_gong0
+ STRING_gonm0
+ STRING_goth0
+ STRING_gothic0
+ STRING_gran0
+ STRING_grantha0
+ STRING_graphemebase0
+ STRING_graphemeextend0
+ STRING_graphemelink0
+ STRING_grbase0
+ STRING_greek0
+ STRING_grek0
+ STRING_grext0
+ STRING_grlink0
+ STRING_gujarati0
+ STRING_gujr0
+ STRING_gunjalagondi0
+ STRING_gurmukhi0
+ STRING_guru0
+ STRING_han0
+ STRING_hang0
+ STRING_hangul0
+ STRING_hani0
+ STRING_hanifirohingya0
+ STRING_hano0
+ STRING_hanunoo0
+ STRING_hatr0
+ STRING_hatran0
+ STRING_hebr0
+ STRING_hebrew0
+ STRING_hex0
+ STRING_hexdigit0
+ STRING_hira0
+ STRING_hiragana0
+ STRING_hluw0
+ STRING_hmng0
+ STRING_hmnp0
+ STRING_hung0
+ STRING_idc0
+ STRING_idcontinue0
+ STRING_ideo0
+ STRING_ideographic0
+ STRING_ids0
+ STRING_idsb0
+ STRING_idsbinaryoperator0
+ STRING_idst0
+ STRING_idstart0
+ STRING_idstrinaryoperator0
+ STRING_imperialaramaic0
+ STRING_inherited0
+ STRING_inscriptionalpahlavi0
+ STRING_inscriptionalparthian0
+ STRING_ital0
+ STRING_java0
+ STRING_javanese0
+ STRING_joinc0
+ STRING_joincontrol0
+ STRING_kaithi0
+ STRING_kali0
+ STRING_kana0
+ STRING_kannada0
+ STRING_katakana0
+ STRING_kayahli0
+ STRING_khar0
+ STRING_kharoshthi0
+ STRING_khitansmallscript0
+ STRING_khmer0
+ STRING_khmr0
+ STRING_khoj0
+ STRING_khojki0
+ STRING_khudawadi0
+ STRING_kits0
+ STRING_knda0
+ STRING_kthi0
+ STRING_l0
+ STRING_l_AMPERSAND0
+ STRING_lana0
+ STRING_lao0
+ STRING_laoo0
+ STRING_latin0
+ STRING_latn0
+ STRING_lc0
+ STRING_lepc0
+ STRING_lepcha0
+ STRING_limb0
+ STRING_limbu0
+ STRING_lina0
+ STRING_linb0
+ STRING_lineara0
+ STRING_linearb0
+ STRING_lisu0
+ STRING_ll0
+ STRING_lm0
+ STRING_lo0
+ STRING_loe0
+ STRING_logicalorderexception0
+ STRING_lower0
+ STRING_lowercase0
+ STRING_lt0
+ STRING_lu0
+ STRING_lyci0
+ STRING_lycian0
+ STRING_lydi0
+ STRING_lydian0
+ STRING_m0
+ STRING_mahajani0
+ STRING_mahj0
+ STRING_maka0
+ STRING_makasar0
+ STRING_malayalam0
+ STRING_mand0
+ STRING_mandaic0
+ STRING_mani0
+ STRING_manichaean0
+ STRING_marc0
+ STRING_marchen0
+ STRING_masaramgondi0
+ STRING_math0
+ STRING_mc0
+ STRING_me0
+ STRING_medefaidrin0
+ STRING_medf0
+ STRING_meeteimayek0
+ STRING_mend0
+ STRING_mendekikakui0
+ STRING_merc0
+ STRING_mero0
+ STRING_meroiticcursive0
+ STRING_meroitichieroglyphs0
+ STRING_miao0
+ STRING_mlym0
+ STRING_mn0
+ STRING_modi0
+ STRING_mong0
+ STRING_mongolian0
+ STRING_mro0
+ STRING_mroo0
+ STRING_mtei0
+ STRING_mult0
+ STRING_multani0
+ STRING_myanmar0
+ STRING_mymr0
+ STRING_n0
+ STRING_nabataean0
+ STRING_nand0
+ STRING_nandinagari0
+ STRING_narb0
+ STRING_nbat0
+ STRING_nchar0
+ STRING_nd0
+ STRING_newa0
+ STRING_newtailue0
+ STRING_nko0
+ STRING_nkoo0
+ STRING_nl0
+ STRING_no0
+ STRING_noncharactercodepoint0
+ STRING_nshu0
+ STRING_nushu0
+ STRING_nyiakengpuachuehmong0
+ STRING_ogam0
+ STRING_ogham0
+ STRING_olchiki0
+ STRING_olck0
+ STRING_oldhungarian0
+ STRING_olditalic0
+ STRING_oldnortharabian0
+ STRING_oldpermic0
+ STRING_oldpersian0
+ STRING_oldsogdian0
+ STRING_oldsoutharabian0
+ STRING_oldturkic0
+ STRING_olduyghur0
+ STRING_oriya0
+ STRING_orkh0
+ STRING_orya0
+ STRING_osage0
+ STRING_osge0
+ STRING_osma0
+ STRING_osmanya0
+ STRING_ougr0
+ STRING_p0
+ STRING_pahawhhmong0
+ STRING_palm0
+ STRING_palmyrene0
+ STRING_patsyn0
+ STRING_patternsyntax0
+ STRING_patternwhitespace0
+ STRING_patws0
+ STRING_pauc0
+ STRING_paucinhau0
+ STRING_pc0
+ STRING_pcm0
+ STRING_pd0
+ STRING_pe0
+ STRING_perm0
+ STRING_pf0
+ STRING_phag0
+ STRING_phagspa0
+ STRING_phli0
+ STRING_phlp0
+ STRING_phnx0
+ STRING_phoenician0
+ STRING_pi0
+ STRING_plrd0
+ STRING_po0
+ STRING_prependedconcatenationmark0
+ STRING_prti0
+ STRING_ps0
+ STRING_psalterpahlavi0
+ STRING_qaac0
+ STRING_qaai0
+ STRING_qmark0
+ STRING_quotationmark0
+ STRING_radical0
+ STRING_regionalindicator0
+ STRING_rejang0
+ STRING_ri0
+ STRING_rjng0
+ STRING_rohg0
+ STRING_runic0
+ STRING_runr0
+ STRING_s0
+ STRING_samaritan0
+ STRING_samr0
+ STRING_sarb0
+ STRING_saur0
+ STRING_saurashtra0
+ STRING_sc0
+ STRING_sd0
+ STRING_sentenceterminal0
+ STRING_sgnw0
+ STRING_sharada0
+ STRING_shavian0
+ STRING_shaw0
+ STRING_shrd0
+ STRING_sidd0
+ STRING_siddham0
+ STRING_signwriting0
+ STRING_sind0
+ STRING_sinh0
+ STRING_sinhala0
+ STRING_sk0
+ STRING_sm0
+ STRING_so0
+ STRING_softdotted0
+ STRING_sogd0
+ STRING_sogdian0
+ STRING_sogo0
+ STRING_sora0
+ STRING_sorasompeng0
+ STRING_soyo0
+ STRING_soyombo0
+ STRING_space0
+ STRING_sterm0
+ STRING_sund0
+ STRING_sundanese0
+ STRING_sylo0
+ STRING_sylotinagri0
+ STRING_syrc0
+ STRING_syriac0
+ STRING_tagalog0
+ STRING_tagb0
+ STRING_tagbanwa0
+ STRING_taile0
+ STRING_taitham0
+ STRING_taiviet0
+ STRING_takr0
+ STRING_takri0
+ STRING_tale0
+ STRING_talu0
+ STRING_tamil0
+ STRING_taml0
+ STRING_tang0
+ STRING_tangsa0
+ STRING_tangut0
+ STRING_tavt0
+ STRING_telu0
+ STRING_telugu0
+ STRING_term0
+ STRING_terminalpunctuation0
+ STRING_tfng0
+ STRING_tglg0
+ STRING_thaa0
+ STRING_thaana0
+ STRING_thai0
+ STRING_tibetan0
+ STRING_tibt0
+ STRING_tifinagh0
+ STRING_tirh0
+ STRING_tirhuta0
+ STRING_tnsa0
+ STRING_toto0
+ STRING_ugar0
+ STRING_ugaritic0
+ STRING_uideo0
+ STRING_unifiedideograph0
+ STRING_unknown0
+ STRING_upper0
+ STRING_uppercase0
+ STRING_vai0
+ STRING_vaii0
+ STRING_variationselector0
+ STRING_vith0
+ STRING_vithkuqi0
+ STRING_vs0
+ STRING_wancho0
+ STRING_wara0
+ STRING_warangciti0
+ STRING_wcho0
+ STRING_whitespace0
+ STRING_wspace0
+ STRING_xan0
+ STRING_xidc0
+ STRING_xidcontinue0
+ STRING_xids0
+ STRING_xidstart0
+ STRING_xpeo0
+ STRING_xps0
+ STRING_xsp0
+ STRING_xsux0
+ STRING_xuc0
+ STRING_xwd0
+ STRING_yezi0
+ STRING_yezidi0
+ STRING_yi0
+ STRING_yiii0
+ STRING_z0
+ STRING_zanabazarsquare0
+ STRING_zanb0
+ STRING_zinh0
+ STRING_zl0
+ STRING_zp0
+ STRING_zs0
+ STRING_zyyy0
+ STRING_zzzz0;
+
+const ucp_type_table PRIV(utt)[] = {
+ { 0, PT_SCX, ucp_Adlam },
+ { 6, PT_SCX, ucp_Adlam },
+ { 11, PT_SC, ucp_Caucasian_Albanian },
+ { 16, PT_BOOL, ucp_ASCII_Hex_Digit },
+ { 21, PT_SC, ucp_Ahom },
+ { 26, PT_BOOL, ucp_Alphabetic },
+ { 32, PT_BOOL, ucp_Alphabetic },
+ { 43, PT_SC, ucp_Anatolian_Hieroglyphs },
+ { 64, PT_ANY, 0 },
+ { 68, PT_SCX, ucp_Arabic },
+ { 73, PT_SCX, ucp_Arabic },
+ { 80, PT_SC, ucp_Armenian },
+ { 89, PT_SC, ucp_Imperial_Aramaic },
+ { 94, PT_SC, ucp_Armenian },
+ { 99, PT_BOOL, ucp_ASCII },
+ { 105, PT_BOOL, ucp_ASCII_Hex_Digit },
+ { 119, PT_SC, ucp_Avestan },
+ { 127, PT_SC, ucp_Avestan },
+ { 132, PT_SC, ucp_Balinese },
+ { 137, PT_SC, ucp_Balinese },
+ { 146, PT_SC, ucp_Bamum },
+ { 151, PT_SC, ucp_Bamum },
+ { 157, PT_SC, ucp_Bassa_Vah },
+ { 162, PT_SC, ucp_Bassa_Vah },
+ { 171, PT_SC, ucp_Batak },
+ { 177, PT_SC, ucp_Batak },
+ { 182, PT_SCX, ucp_Bengali },
+ { 187, PT_SCX, ucp_Bengali },
+ { 195, PT_SC, ucp_Bhaiksuki },
+ { 205, PT_SC, ucp_Bhaiksuki },
+ { 210, PT_BIDICL, ucp_bidiAL },
+ { 217, PT_BIDICL, ucp_bidiAN },
+ { 224, PT_BIDICL, ucp_bidiB },
+ { 230, PT_BIDICL, ucp_bidiBN },
+ { 237, PT_BOOL, ucp_Bidi_Control },
+ { 243, PT_BOOL, ucp_Bidi_Control },
+ { 255, PT_BIDICL, ucp_bidiCS },
+ { 262, PT_BIDICL, ucp_bidiEN },
+ { 269, PT_BIDICL, ucp_bidiES },
+ { 276, PT_BIDICL, ucp_bidiET },
+ { 283, PT_BIDICL, ucp_bidiFSI },
+ { 291, PT_BIDICL, ucp_bidiL },
+ { 297, PT_BIDICL, ucp_bidiLRE },
+ { 305, PT_BIDICL, ucp_bidiLRI },
+ { 313, PT_BIDICL, ucp_bidiLRO },
+ { 321, PT_BOOL, ucp_Bidi_Mirrored },
+ { 327, PT_BOOL, ucp_Bidi_Mirrored },
+ { 340, PT_BIDICL, ucp_bidiNSM },
+ { 348, PT_BIDICL, ucp_bidiON },
+ { 355, PT_BIDICL, ucp_bidiPDF },
+ { 363, PT_BIDICL, ucp_bidiPDI },
+ { 371, PT_BIDICL, ucp_bidiR },
+ { 377, PT_BIDICL, ucp_bidiRLE },
+ { 385, PT_BIDICL, ucp_bidiRLI },
+ { 393, PT_BIDICL, ucp_bidiRLO },
+ { 401, PT_BIDICL, ucp_bidiS },
+ { 407, PT_BIDICL, ucp_bidiWS },
+ { 414, PT_SCX, ucp_Bopomofo },
+ { 419, PT_SCX, ucp_Bopomofo },
+ { 428, PT_SC, ucp_Brahmi },
+ { 433, PT_SC, ucp_Brahmi },
+ { 440, PT_SC, ucp_Braille },
+ { 445, PT_SC, ucp_Braille },
+ { 453, PT_SCX, ucp_Buginese },
+ { 458, PT_SCX, ucp_Buginese },
+ { 467, PT_SCX, ucp_Buhid },
+ { 472, PT_SCX, ucp_Buhid },
+ { 478, PT_GC, ucp_C },
+ { 480, PT_SCX, ucp_Chakma },
+ { 485, PT_SC, ucp_Canadian_Aboriginal },
+ { 504, PT_SC, ucp_Canadian_Aboriginal },
+ { 509, PT_SC, ucp_Carian },
+ { 514, PT_SC, ucp_Carian },
+ { 521, PT_BOOL, ucp_Cased },
+ { 527, PT_BOOL, ucp_Case_Ignorable },
+ { 541, PT_SC, ucp_Caucasian_Albanian },
+ { 559, PT_PC, ucp_Cc },
+ { 562, PT_PC, ucp_Cf },
+ { 565, PT_SCX, ucp_Chakma },
+ { 572, PT_SC, ucp_Cham },
+ { 577, PT_BOOL, ucp_Changes_When_Casefolded },
+ { 599, PT_BOOL, ucp_Changes_When_Casemapped },
+ { 621, PT_BOOL, ucp_Changes_When_Lowercased },
+ { 643, PT_BOOL, ucp_Changes_When_Titlecased },
+ { 665, PT_BOOL, ucp_Changes_When_Uppercased },
+ { 687, PT_SC, ucp_Cherokee },
+ { 692, PT_SC, ucp_Cherokee },
+ { 701, PT_SC, ucp_Chorasmian },
+ { 712, PT_SC, ucp_Chorasmian },
+ { 717, PT_BOOL, ucp_Case_Ignorable },
+ { 720, PT_PC, ucp_Cn },
+ { 723, PT_PC, ucp_Co },
+ { 726, PT_SC, ucp_Common },
+ { 733, PT_SCX, ucp_Coptic },
+ { 738, PT_SCX, ucp_Coptic },
+ { 745, PT_SCX, ucp_Cypro_Minoan },
+ { 750, PT_SCX, ucp_Cypriot },
+ { 755, PT_PC, ucp_Cs },
+ { 758, PT_SC, ucp_Cuneiform },
+ { 768, PT_BOOL, ucp_Changes_When_Casefolded },
+ { 773, PT_BOOL, ucp_Changes_When_Casemapped },
+ { 778, PT_BOOL, ucp_Changes_When_Lowercased },
+ { 782, PT_BOOL, ucp_Changes_When_Titlecased },
+ { 786, PT_BOOL, ucp_Changes_When_Uppercased },
+ { 790, PT_SCX, ucp_Cypriot },
+ { 798, PT_SCX, ucp_Cypro_Minoan },
+ { 810, PT_SCX, ucp_Cyrillic },
+ { 819, PT_SCX, ucp_Cyrillic },
+ { 824, PT_BOOL, ucp_Dash },
+ { 829, PT_BOOL, ucp_Default_Ignorable_Code_Point },
+ { 855, PT_BOOL, ucp_Deprecated },
+ { 859, PT_BOOL, ucp_Deprecated },
+ { 870, PT_SC, ucp_Deseret },
+ { 878, PT_SCX, ucp_Devanagari },
+ { 883, PT_SCX, ucp_Devanagari },
+ { 894, PT_BOOL, ucp_Default_Ignorable_Code_Point },
+ { 897, PT_BOOL, ucp_Diacritic },
+ { 901, PT_BOOL, ucp_Diacritic },
+ { 911, PT_SC, ucp_Dives_Akuru },
+ { 916, PT_SC, ucp_Dives_Akuru },
+ { 927, PT_SCX, ucp_Dogra },
+ { 932, PT_SCX, ucp_Dogra },
+ { 938, PT_SC, ucp_Deseret },
+ { 943, PT_SCX, ucp_Duployan },
+ { 948, PT_SCX, ucp_Duployan },
+ { 957, PT_BOOL, ucp_Emoji_Modifier_Base },
+ { 963, PT_BOOL, ucp_Emoji_Component },
+ { 969, PT_SC, ucp_Egyptian_Hieroglyphs },
+ { 974, PT_SC, ucp_Egyptian_Hieroglyphs },
+ { 994, PT_SC, ucp_Elbasan },
+ { 999, PT_SC, ucp_Elbasan },
+ { 1007, PT_SC, ucp_Elymaic },
+ { 1012, PT_SC, ucp_Elymaic },
+ { 1020, PT_BOOL, ucp_Emoji_Modifier },
+ { 1025, PT_BOOL, ucp_Emoji },
+ { 1031, PT_BOOL, ucp_Emoji_Component },
+ { 1046, PT_BOOL, ucp_Emoji_Modifier },
+ { 1060, PT_BOOL, ucp_Emoji_Modifier_Base },
+ { 1078, PT_BOOL, ucp_Emoji_Presentation },
+ { 1096, PT_BOOL, ucp_Emoji_Presentation },
+ { 1102, PT_SC, ucp_Ethiopic },
+ { 1107, PT_SC, ucp_Ethiopic },
+ { 1116, PT_BOOL, ucp_Extender },
+ { 1120, PT_BOOL, ucp_Extended_Pictographic },
+ { 1141, PT_BOOL, ucp_Extender },
+ { 1150, PT_BOOL, ucp_Extended_Pictographic },
+ { 1158, PT_SCX, ucp_Georgian },
+ { 1163, PT_SCX, ucp_Georgian },
+ { 1172, PT_SCX, ucp_Glagolitic },
+ { 1177, PT_SCX, ucp_Glagolitic },
+ { 1188, PT_SCX, ucp_Gunjala_Gondi },
+ { 1193, PT_SCX, ucp_Masaram_Gondi },
+ { 1198, PT_SC, ucp_Gothic },
+ { 1203, PT_SC, ucp_Gothic },
+ { 1210, PT_SCX, ucp_Grantha },
+ { 1215, PT_SCX, ucp_Grantha },
+ { 1223, PT_BOOL, ucp_Grapheme_Base },
+ { 1236, PT_BOOL, ucp_Grapheme_Extend },
+ { 1251, PT_BOOL, ucp_Grapheme_Link },
+ { 1264, PT_BOOL, ucp_Grapheme_Base },
+ { 1271, PT_SCX, ucp_Greek },
+ { 1277, PT_SCX, ucp_Greek },
+ { 1282, PT_BOOL, ucp_Grapheme_Extend },
+ { 1288, PT_BOOL, ucp_Grapheme_Link },
+ { 1295, PT_SCX, ucp_Gujarati },
+ { 1304, PT_SCX, ucp_Gujarati },
+ { 1309, PT_SCX, ucp_Gunjala_Gondi },
+ { 1322, PT_SCX, ucp_Gurmukhi },
+ { 1331, PT_SCX, ucp_Gurmukhi },
+ { 1336, PT_SCX, ucp_Han },
+ { 1340, PT_SCX, ucp_Hangul },
+ { 1345, PT_SCX, ucp_Hangul },
+ { 1352, PT_SCX, ucp_Han },
+ { 1357, PT_SCX, ucp_Hanifi_Rohingya },
+ { 1372, PT_SCX, ucp_Hanunoo },
+ { 1377, PT_SCX, ucp_Hanunoo },
+ { 1385, PT_SC, ucp_Hatran },
+ { 1390, PT_SC, ucp_Hatran },
+ { 1397, PT_SC, ucp_Hebrew },
+ { 1402, PT_SC, ucp_Hebrew },
+ { 1409, PT_BOOL, ucp_Hex_Digit },
+ { 1413, PT_BOOL, ucp_Hex_Digit },
+ { 1422, PT_SCX, ucp_Hiragana },
+ { 1427, PT_SCX, ucp_Hiragana },
+ { 1436, PT_SC, ucp_Anatolian_Hieroglyphs },
+ { 1441, PT_SC, ucp_Pahawh_Hmong },
+ { 1446, PT_SC, ucp_Nyiakeng_Puachue_Hmong },
+ { 1451, PT_SC, ucp_Old_Hungarian },
+ { 1456, PT_BOOL, ucp_ID_Continue },
+ { 1460, PT_BOOL, ucp_ID_Continue },
+ { 1471, PT_BOOL, ucp_Ideographic },
+ { 1476, PT_BOOL, ucp_Ideographic },
+ { 1488, PT_BOOL, ucp_ID_Start },
+ { 1492, PT_BOOL, ucp_IDS_Binary_Operator },
+ { 1497, PT_BOOL, ucp_IDS_Binary_Operator },
+ { 1515, PT_BOOL, ucp_IDS_Trinary_Operator },
+ { 1520, PT_BOOL, ucp_ID_Start },
+ { 1528, PT_BOOL, ucp_IDS_Trinary_Operator },
+ { 1547, PT_SC, ucp_Imperial_Aramaic },
+ { 1563, PT_SC, ucp_Inherited },
+ { 1573, PT_SC, ucp_Inscriptional_Pahlavi },
+ { 1594, PT_SC, ucp_Inscriptional_Parthian },
+ { 1616, PT_SC, ucp_Old_Italic },
+ { 1621, PT_SCX, ucp_Javanese },
+ { 1626, PT_SCX, ucp_Javanese },
+ { 1635, PT_BOOL, ucp_Join_Control },
+ { 1641, PT_BOOL, ucp_Join_Control },
+ { 1653, PT_SCX, ucp_Kaithi },
+ { 1660, PT_SCX, ucp_Kayah_Li },
+ { 1665, PT_SCX, ucp_Katakana },
+ { 1670, PT_SCX, ucp_Kannada },
+ { 1678, PT_SCX, ucp_Katakana },
+ { 1687, PT_SCX, ucp_Kayah_Li },
+ { 1695, PT_SC, ucp_Kharoshthi },
+ { 1700, PT_SC, ucp_Kharoshthi },
+ { 1711, PT_SC, ucp_Khitan_Small_Script },
+ { 1729, PT_SC, ucp_Khmer },
+ { 1735, PT_SC, ucp_Khmer },
+ { 1740, PT_SCX, ucp_Khojki },
+ { 1745, PT_SCX, ucp_Khojki },
+ { 1752, PT_SCX, ucp_Khudawadi },
+ { 1762, PT_SC, ucp_Khitan_Small_Script },
+ { 1767, PT_SCX, ucp_Kannada },
+ { 1772, PT_SCX, ucp_Kaithi },
+ { 1777, PT_GC, ucp_L },
+ { 1779, PT_LAMP, 0 },
+ { 1782, PT_SC, ucp_Tai_Tham },
+ { 1787, PT_SC, ucp_Lao },
+ { 1791, PT_SC, ucp_Lao },
+ { 1796, PT_SCX, ucp_Latin },
+ { 1802, PT_SCX, ucp_Latin },
+ { 1807, PT_LAMP, 0 },
+ { 1810, PT_SC, ucp_Lepcha },
+ { 1815, PT_SC, ucp_Lepcha },
+ { 1822, PT_SCX, ucp_Limbu },
+ { 1827, PT_SCX, ucp_Limbu },
+ { 1833, PT_SCX, ucp_Linear_A },
+ { 1838, PT_SCX, ucp_Linear_B },
+ { 1843, PT_SCX, ucp_Linear_A },
+ { 1851, PT_SCX, ucp_Linear_B },
+ { 1859, PT_SC, ucp_Lisu },
+ { 1864, PT_PC, ucp_Ll },
+ { 1867, PT_PC, ucp_Lm },
+ { 1870, PT_PC, ucp_Lo },
+ { 1873, PT_BOOL, ucp_Logical_Order_Exception },
+ { 1877, PT_BOOL, ucp_Logical_Order_Exception },
+ { 1899, PT_BOOL, ucp_Lowercase },
+ { 1905, PT_BOOL, ucp_Lowercase },
+ { 1915, PT_PC, ucp_Lt },
+ { 1918, PT_PC, ucp_Lu },
+ { 1921, PT_SC, ucp_Lycian },
+ { 1926, PT_SC, ucp_Lycian },
+ { 1933, PT_SC, ucp_Lydian },
+ { 1938, PT_SC, ucp_Lydian },
+ { 1945, PT_GC, ucp_M },
+ { 1947, PT_SCX, ucp_Mahajani },
+ { 1956, PT_SCX, ucp_Mahajani },
+ { 1961, PT_SC, ucp_Makasar },
+ { 1966, PT_SC, ucp_Makasar },
+ { 1974, PT_SCX, ucp_Malayalam },
+ { 1984, PT_SCX, ucp_Mandaic },
+ { 1989, PT_SCX, ucp_Mandaic },
+ { 1997, PT_SCX, ucp_Manichaean },
+ { 2002, PT_SCX, ucp_Manichaean },
+ { 2013, PT_SC, ucp_Marchen },
+ { 2018, PT_SC, ucp_Marchen },
+ { 2026, PT_SCX, ucp_Masaram_Gondi },
+ { 2039, PT_BOOL, ucp_Math },
+ { 2044, PT_PC, ucp_Mc },
+ { 2047, PT_PC, ucp_Me },
+ { 2050, PT_SC, ucp_Medefaidrin },
+ { 2062, PT_SC, ucp_Medefaidrin },
+ { 2067, PT_SC, ucp_Meetei_Mayek },
+ { 2079, PT_SC, ucp_Mende_Kikakui },
+ { 2084, PT_SC, ucp_Mende_Kikakui },
+ { 2097, PT_SC, ucp_Meroitic_Cursive },
+ { 2102, PT_SC, ucp_Meroitic_Hieroglyphs },
+ { 2107, PT_SC, ucp_Meroitic_Cursive },
+ { 2123, PT_SC, ucp_Meroitic_Hieroglyphs },
+ { 2143, PT_SC, ucp_Miao },
+ { 2148, PT_SCX, ucp_Malayalam },
+ { 2153, PT_PC, ucp_Mn },
+ { 2156, PT_SCX, ucp_Modi },
+ { 2161, PT_SCX, ucp_Mongolian },
+ { 2166, PT_SCX, ucp_Mongolian },
+ { 2176, PT_SC, ucp_Mro },
+ { 2180, PT_SC, ucp_Mro },
+ { 2185, PT_SC, ucp_Meetei_Mayek },
+ { 2190, PT_SCX, ucp_Multani },
+ { 2195, PT_SCX, ucp_Multani },
+ { 2203, PT_SCX, ucp_Myanmar },
+ { 2211, PT_SCX, ucp_Myanmar },
+ { 2216, PT_GC, ucp_N },
+ { 2218, PT_SC, ucp_Nabataean },
+ { 2228, PT_SCX, ucp_Nandinagari },
+ { 2233, PT_SCX, ucp_Nandinagari },
+ { 2245, PT_SC, ucp_Old_North_Arabian },
+ { 2250, PT_SC, ucp_Nabataean },
+ { 2255, PT_BOOL, ucp_Noncharacter_Code_Point },
+ { 2261, PT_PC, ucp_Nd },
+ { 2264, PT_SC, ucp_Newa },
+ { 2269, PT_SC, ucp_New_Tai_Lue },
+ { 2279, PT_SCX, ucp_Nko },
+ { 2283, PT_SCX, ucp_Nko },
+ { 2288, PT_PC, ucp_Nl },
+ { 2291, PT_PC, ucp_No },
+ { 2294, PT_BOOL, ucp_Noncharacter_Code_Point },
+ { 2316, PT_SC, ucp_Nushu },
+ { 2321, PT_SC, ucp_Nushu },
+ { 2327, PT_SC, ucp_Nyiakeng_Puachue_Hmong },
+ { 2348, PT_SC, ucp_Ogham },
+ { 2353, PT_SC, ucp_Ogham },
+ { 2359, PT_SC, ucp_Ol_Chiki },
+ { 2367, PT_SC, ucp_Ol_Chiki },
+ { 2372, PT_SC, ucp_Old_Hungarian },
+ { 2385, PT_SC, ucp_Old_Italic },
+ { 2395, PT_SC, ucp_Old_North_Arabian },
+ { 2411, PT_SCX, ucp_Old_Permic },
+ { 2421, PT_SC, ucp_Old_Persian },
+ { 2432, PT_SC, ucp_Old_Sogdian },
+ { 2443, PT_SC, ucp_Old_South_Arabian },
+ { 2459, PT_SC, ucp_Old_Turkic },
+ { 2469, PT_SCX, ucp_Old_Uyghur },
+ { 2479, PT_SCX, ucp_Oriya },
+ { 2485, PT_SC, ucp_Old_Turkic },
+ { 2490, PT_SCX, ucp_Oriya },
+ { 2495, PT_SC, ucp_Osage },
+ { 2501, PT_SC, ucp_Osage },
+ { 2506, PT_SC, ucp_Osmanya },
+ { 2511, PT_SC, ucp_Osmanya },
+ { 2519, PT_SCX, ucp_Old_Uyghur },
+ { 2524, PT_GC, ucp_P },
+ { 2526, PT_SC, ucp_Pahawh_Hmong },
+ { 2538, PT_SC, ucp_Palmyrene },
+ { 2543, PT_SC, ucp_Palmyrene },
+ { 2553, PT_BOOL, ucp_Pattern_Syntax },
+ { 2560, PT_BOOL, ucp_Pattern_Syntax },
+ { 2574, PT_BOOL, ucp_Pattern_White_Space },
+ { 2592, PT_BOOL, ucp_Pattern_White_Space },
+ { 2598, PT_SC, ucp_Pau_Cin_Hau },
+ { 2603, PT_SC, ucp_Pau_Cin_Hau },
+ { 2613, PT_PC, ucp_Pc },
+ { 2616, PT_BOOL, ucp_Prepended_Concatenation_Mark },
+ { 2620, PT_PC, ucp_Pd },
+ { 2623, PT_PC, ucp_Pe },
+ { 2626, PT_SCX, ucp_Old_Permic },
+ { 2631, PT_PC, ucp_Pf },
+ { 2634, PT_SCX, ucp_Phags_Pa },
+ { 2639, PT_SCX, ucp_Phags_Pa },
+ { 2647, PT_SC, ucp_Inscriptional_Pahlavi },
+ { 2652, PT_SCX, ucp_Psalter_Pahlavi },
+ { 2657, PT_SC, ucp_Phoenician },
+ { 2662, PT_SC, ucp_Phoenician },
+ { 2673, PT_PC, ucp_Pi },
+ { 2676, PT_SC, ucp_Miao },
+ { 2681, PT_PC, ucp_Po },
+ { 2684, PT_BOOL, ucp_Prepended_Concatenation_Mark },
+ { 2711, PT_SC, ucp_Inscriptional_Parthian },
+ { 2716, PT_PC, ucp_Ps },
+ { 2719, PT_SCX, ucp_Psalter_Pahlavi },
+ { 2734, PT_SCX, ucp_Coptic },
+ { 2739, PT_SC, ucp_Inherited },
+ { 2744, PT_BOOL, ucp_Quotation_Mark },
+ { 2750, PT_BOOL, ucp_Quotation_Mark },
+ { 2764, PT_BOOL, ucp_Radical },
+ { 2772, PT_BOOL, ucp_Regional_Indicator },
+ { 2790, PT_SC, ucp_Rejang },
+ { 2797, PT_BOOL, ucp_Regional_Indicator },
+ { 2800, PT_SC, ucp_Rejang },
+ { 2805, PT_SCX, ucp_Hanifi_Rohingya },
+ { 2810, PT_SC, ucp_Runic },
+ { 2816, PT_SC, ucp_Runic },
+ { 2821, PT_GC, ucp_S },
+ { 2823, PT_SC, ucp_Samaritan },
+ { 2833, PT_SC, ucp_Samaritan },
+ { 2838, PT_SC, ucp_Old_South_Arabian },
+ { 2843, PT_SC, ucp_Saurashtra },
+ { 2848, PT_SC, ucp_Saurashtra },
+ { 2859, PT_PC, ucp_Sc },
+ { 2862, PT_BOOL, ucp_Soft_Dotted },
+ { 2865, PT_BOOL, ucp_Sentence_Terminal },
+ { 2882, PT_SC, ucp_SignWriting },
+ { 2887, PT_SCX, ucp_Sharada },
+ { 2895, PT_SC, ucp_Shavian },
+ { 2903, PT_SC, ucp_Shavian },
+ { 2908, PT_SCX, ucp_Sharada },
+ { 2913, PT_SC, ucp_Siddham },
+ { 2918, PT_SC, ucp_Siddham },
+ { 2926, PT_SC, ucp_SignWriting },
+ { 2938, PT_SCX, ucp_Khudawadi },
+ { 2943, PT_SCX, ucp_Sinhala },
+ { 2948, PT_SCX, ucp_Sinhala },
+ { 2956, PT_PC, ucp_Sk },
+ { 2959, PT_PC, ucp_Sm },
+ { 2962, PT_PC, ucp_So },
+ { 2965, PT_BOOL, ucp_Soft_Dotted },
+ { 2976, PT_SCX, ucp_Sogdian },
+ { 2981, PT_SCX, ucp_Sogdian },
+ { 2989, PT_SC, ucp_Old_Sogdian },
+ { 2994, PT_SC, ucp_Sora_Sompeng },
+ { 2999, PT_SC, ucp_Sora_Sompeng },
+ { 3011, PT_SC, ucp_Soyombo },
+ { 3016, PT_SC, ucp_Soyombo },
+ { 3024, PT_BOOL, ucp_White_Space },
+ { 3030, PT_BOOL, ucp_Sentence_Terminal },
+ { 3036, PT_SC, ucp_Sundanese },
+ { 3041, PT_SC, ucp_Sundanese },
+ { 3051, PT_SCX, ucp_Syloti_Nagri },
+ { 3056, PT_SCX, ucp_Syloti_Nagri },
+ { 3068, PT_SCX, ucp_Syriac },
+ { 3073, PT_SCX, ucp_Syriac },
+ { 3080, PT_SCX, ucp_Tagalog },
+ { 3088, PT_SCX, ucp_Tagbanwa },
+ { 3093, PT_SCX, ucp_Tagbanwa },
+ { 3102, PT_SCX, ucp_Tai_Le },
+ { 3108, PT_SC, ucp_Tai_Tham },
+ { 3116, PT_SC, ucp_Tai_Viet },
+ { 3124, PT_SCX, ucp_Takri },
+ { 3129, PT_SCX, ucp_Takri },
+ { 3135, PT_SCX, ucp_Tai_Le },
+ { 3140, PT_SC, ucp_New_Tai_Lue },
+ { 3145, PT_SCX, ucp_Tamil },
+ { 3151, PT_SCX, ucp_Tamil },
+ { 3156, PT_SC, ucp_Tangut },
+ { 3161, PT_SC, ucp_Tangsa },
+ { 3168, PT_SC, ucp_Tangut },
+ { 3175, PT_SC, ucp_Tai_Viet },
+ { 3180, PT_SCX, ucp_Telugu },
+ { 3185, PT_SCX, ucp_Telugu },
+ { 3192, PT_BOOL, ucp_Terminal_Punctuation },
+ { 3197, PT_BOOL, ucp_Terminal_Punctuation },
+ { 3217, PT_SC, ucp_Tifinagh },
+ { 3222, PT_SCX, ucp_Tagalog },
+ { 3227, PT_SCX, ucp_Thaana },
+ { 3232, PT_SCX, ucp_Thaana },
+ { 3239, PT_SC, ucp_Thai },
+ { 3244, PT_SC, ucp_Tibetan },
+ { 3252, PT_SC, ucp_Tibetan },
+ { 3257, PT_SC, ucp_Tifinagh },
+ { 3266, PT_SCX, ucp_Tirhuta },
+ { 3271, PT_SCX, ucp_Tirhuta },
+ { 3279, PT_SC, ucp_Tangsa },
+ { 3284, PT_SC, ucp_Toto },
+ { 3289, PT_SC, ucp_Ugaritic },
+ { 3294, PT_SC, ucp_Ugaritic },
+ { 3303, PT_BOOL, ucp_Unified_Ideograph },
+ { 3309, PT_BOOL, ucp_Unified_Ideograph },
+ { 3326, PT_SC, ucp_Unknown },
+ { 3334, PT_BOOL, ucp_Uppercase },
+ { 3340, PT_BOOL, ucp_Uppercase },
+ { 3350, PT_SC, ucp_Vai },
+ { 3354, PT_SC, ucp_Vai },
+ { 3359, PT_BOOL, ucp_Variation_Selector },
+ { 3377, PT_SC, ucp_Vithkuqi },
+ { 3382, PT_SC, ucp_Vithkuqi },
+ { 3391, PT_BOOL, ucp_Variation_Selector },
+ { 3394, PT_SC, ucp_Wancho },
+ { 3401, PT_SC, ucp_Warang_Citi },
+ { 3406, PT_SC, ucp_Warang_Citi },
+ { 3417, PT_SC, ucp_Wancho },
+ { 3422, PT_BOOL, ucp_White_Space },
+ { 3433, PT_BOOL, ucp_White_Space },
+ { 3440, PT_ALNUM, 0 },
+ { 3444, PT_BOOL, ucp_XID_Continue },
+ { 3449, PT_BOOL, ucp_XID_Continue },
+ { 3461, PT_BOOL, ucp_XID_Start },
+ { 3466, PT_BOOL, ucp_XID_Start },
+ { 3475, PT_SC, ucp_Old_Persian },
+ { 3480, PT_PXSPACE, 0 },
+ { 3484, PT_SPACE, 0 },
+ { 3488, PT_SC, ucp_Cuneiform },
+ { 3493, PT_UCNC, 0 },
+ { 3497, PT_WORD, 0 },
+ { 3501, PT_SCX, ucp_Yezidi },
+ { 3506, PT_SCX, ucp_Yezidi },
+ { 3513, PT_SCX, ucp_Yi },
+ { 3516, PT_SCX, ucp_Yi },
+ { 3521, PT_GC, ucp_Z },
+ { 3523, PT_SC, ucp_Zanabazar_Square },
+ { 3539, PT_SC, ucp_Zanabazar_Square },
+ { 3544, PT_SC, ucp_Inherited },
+ { 3549, PT_PC, ucp_Zl },
+ { 3552, PT_PC, ucp_Zp },
+ { 3555, PT_PC, ucp_Zs },
+ { 3558, PT_SC, ucp_Common },
+ { 3563, PT_SC, ucp_Unknown }
+};
+
+const size_t PRIV(utt_size) = sizeof(PRIV(utt)) / sizeof(ucp_type_table);
+
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_ucptables.c */
diff --git a/contrib/libs/pcre2/src/pcre2_valid_utf.c b/contrib/libs/pcre2/src/pcre2_valid_utf.c
new file mode 100644
index 0000000000..9373f71099
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_valid_utf.c
@@ -0,0 +1,398 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2020 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains an internal function for validating UTF character
+strings. This file is also #included by the pcre2test program, which uses
+macros to change names from _pcre2_xxx to xxxx, thereby avoiding name clashes
+with the library. In this case, PCRE2_PCRE2TEST is defined. */
+
+#ifndef PCRE2_PCRE2TEST /* We're compiling the library */
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+#include "pcre2_internal.h"
+#endif /* PCRE2_PCRE2TEST */
+
+
+#ifndef SUPPORT_UNICODE
+/*************************************************
+* Dummy function when Unicode is not supported *
+*************************************************/
+
+/* This function should never be called when Unicode is not supported. */
+
+int
+PRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset)
+{
+(void)string;
+(void)length;
+(void)erroroffset;
+return 0;
+}
+#else /* UTF is supported */
+
+
+
+/*************************************************
+* Validate a UTF string *
+*************************************************/
+
+/* This function is called (optionally) at the start of compile or match, to
+check that a supposed UTF string is actually valid. The early check means
+that subsequent code can assume it is dealing with a valid string. The check
+can be turned off for maximum performance, but the consequences of supplying an
+invalid string are then undefined.
+
+Arguments:
+ string points to the string
+ length length of string
+ errp pointer to an error position offset variable
+
+Returns: == 0 if the string is a valid UTF string
+ != 0 otherwise, setting the offset of the bad character
+*/
+
+int
+PRIV(valid_utf)(PCRE2_SPTR string, PCRE2_SIZE length, PCRE2_SIZE *erroroffset)
+{
+PCRE2_SPTR p;
+uint32_t c;
+
+/* ----------------- Check a UTF-8 string ----------------- */
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+
+/* Originally, this function checked according to RFC 2279, allowing for values
+in the range 0 to 0x7fffffff, up to 6 bytes long, but ensuring that they were
+in the canonical format. Once somebody had pointed out RFC 3629 to me (it
+obsoletes 2279), additional restrictions were applied. The values are now
+limited to be between 0 and 0x0010ffff, no more than 4 bytes long, and the
+subrange 0xd000 to 0xdfff is excluded. However, the format of 5-byte and 6-byte
+characters is still checked. Error returns are as follows:
+
+PCRE2_ERROR_UTF8_ERR1 Missing 1 byte at the end of the string
+PCRE2_ERROR_UTF8_ERR2 Missing 2 bytes at the end of the string
+PCRE2_ERROR_UTF8_ERR3 Missing 3 bytes at the end of the string
+PCRE2_ERROR_UTF8_ERR4 Missing 4 bytes at the end of the string
+PCRE2_ERROR_UTF8_ERR5 Missing 5 bytes at the end of the string
+PCRE2_ERROR_UTF8_ERR6 2nd-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR7 3rd-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR8 4th-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR9 5th-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR10 6th-byte's two top bits are not 0x80
+PCRE2_ERROR_UTF8_ERR11 5-byte character is not permitted by RFC 3629
+PCRE2_ERROR_UTF8_ERR12 6-byte character is not permitted by RFC 3629
+PCRE2_ERROR_UTF8_ERR13 4-byte character with value > 0x10ffff is not permitted
+PCRE2_ERROR_UTF8_ERR14 3-byte character with value 0xd800-0xdfff is not permitted
+PCRE2_ERROR_UTF8_ERR15 Overlong 2-byte sequence
+PCRE2_ERROR_UTF8_ERR16 Overlong 3-byte sequence
+PCRE2_ERROR_UTF8_ERR17 Overlong 4-byte sequence
+PCRE2_ERROR_UTF8_ERR18 Overlong 5-byte sequence (won't ever occur)
+PCRE2_ERROR_UTF8_ERR19 Overlong 6-byte sequence (won't ever occur)
+PCRE2_ERROR_UTF8_ERR20 Isolated 0x80 byte (not within UTF-8 character)
+PCRE2_ERROR_UTF8_ERR21 Byte with the illegal value 0xfe or 0xff
+*/
+
+for (p = string; length > 0; p++)
+ {
+ uint32_t ab, d;
+
+ c = *p;
+ length--;
+
+ if (c < 128) continue; /* ASCII character */
+
+ if (c < 0xc0) /* Isolated 10xx xxxx byte */
+ {
+ *erroroffset = (PCRE2_SIZE)(p - string);
+ return PCRE2_ERROR_UTF8_ERR20;
+ }
+
+ if (c >= 0xfe) /* Invalid 0xfe or 0xff bytes */
+ {
+ *erroroffset = (PCRE2_SIZE)(p - string);
+ return PCRE2_ERROR_UTF8_ERR21;
+ }
+
+ ab = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes (1-5) */
+ if (length < ab) /* Missing bytes */
+ {
+ *erroroffset = (PCRE2_SIZE)(p - string);
+ switch(ab - length)
+ {
+ case 1: return PCRE2_ERROR_UTF8_ERR1;
+ case 2: return PCRE2_ERROR_UTF8_ERR2;
+ case 3: return PCRE2_ERROR_UTF8_ERR3;
+ case 4: return PCRE2_ERROR_UTF8_ERR4;
+ case 5: return PCRE2_ERROR_UTF8_ERR5;
+ }
+ }
+ length -= ab; /* Length remaining */
+
+ /* Check top bits in the second byte */
+
+ if (((d = *(++p)) & 0xc0) != 0x80)
+ {
+ *erroroffset = (int)(p - string) - 1;
+ return PCRE2_ERROR_UTF8_ERR6;
+ }
+
+ /* For each length, check that the remaining bytes start with the 0x80 bit
+ set and not the 0x40 bit. Then check for an overlong sequence, and for the
+ excluded range 0xd800 to 0xdfff. */
+
+ switch (ab)
+ {
+ /* 2-byte character. No further bytes to check for 0x80. Check first byte
+ for for xx00 000x (overlong sequence). */
+
+ case 1: if ((c & 0x3e) == 0)
+ {
+ *erroroffset = (int)(p - string) - 1;
+ return PCRE2_ERROR_UTF8_ERR15;
+ }
+ break;
+
+ /* 3-byte character. Check third byte for 0x80. Then check first 2 bytes
+ for 1110 0000, xx0x xxxx (overlong sequence) or
+ 1110 1101, 1010 xxxx (0xd800 - 0xdfff) */
+
+ case 2:
+ if ((*(++p) & 0xc0) != 0x80) /* Third byte */
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR7;
+ }
+ if (c == 0xe0 && (d & 0x20) == 0)
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR16;
+ }
+ if (c == 0xed && d >= 0xa0)
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR14;
+ }
+ break;
+
+ /* 4-byte character. Check 3rd and 4th bytes for 0x80. Then check first 2
+ bytes for for 1111 0000, xx00 xxxx (overlong sequence), then check for a
+ character greater than 0x0010ffff (f4 8f bf bf) */
+
+ case 3:
+ if ((*(++p) & 0xc0) != 0x80) /* Third byte */
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR7;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR8;
+ }
+ if (c == 0xf0 && (d & 0x30) == 0)
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR17;
+ }
+ if (c > 0xf4 || (c == 0xf4 && d > 0x8f))
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR13;
+ }
+ break;
+
+ /* 5-byte and 6-byte characters are not allowed by RFC 3629, and will be
+ rejected by the length test below. However, we do the appropriate tests
+ here so that overlong sequences get diagnosed, and also in case there is
+ ever an option for handling these larger code points. */
+
+ /* 5-byte character. Check 3rd, 4th, and 5th bytes for 0x80. Then check for
+ 1111 1000, xx00 0xxx */
+
+ case 4:
+ if ((*(++p) & 0xc0) != 0x80) /* Third byte */
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR7;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR8;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */
+ {
+ *erroroffset = (int)(p - string) - 4;
+ return PCRE2_ERROR_UTF8_ERR9;
+ }
+ if (c == 0xf8 && (d & 0x38) == 0)
+ {
+ *erroroffset = (int)(p - string) - 4;
+ return PCRE2_ERROR_UTF8_ERR18;
+ }
+ break;
+
+ /* 6-byte character. Check 3rd-6th bytes for 0x80. Then check for
+ 1111 1100, xx00 00xx. */
+
+ case 5:
+ if ((*(++p) & 0xc0) != 0x80) /* Third byte */
+ {
+ *erroroffset = (int)(p - string) - 2;
+ return PCRE2_ERROR_UTF8_ERR7;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */
+ {
+ *erroroffset = (int)(p - string) - 3;
+ return PCRE2_ERROR_UTF8_ERR8;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */
+ {
+ *erroroffset = (int)(p - string) - 4;
+ return PCRE2_ERROR_UTF8_ERR9;
+ }
+ if ((*(++p) & 0xc0) != 0x80) /* Sixth byte */
+ {
+ *erroroffset = (int)(p - string) - 5;
+ return PCRE2_ERROR_UTF8_ERR10;
+ }
+ if (c == 0xfc && (d & 0x3c) == 0)
+ {
+ *erroroffset = (int)(p - string) - 5;
+ return PCRE2_ERROR_UTF8_ERR19;
+ }
+ break;
+ }
+
+ /* Character is valid under RFC 2279, but 4-byte and 5-byte characters are
+ excluded by RFC 3629. The pointer p is currently at the last byte of the
+ character. */
+
+ if (ab > 3)
+ {
+ *erroroffset = (int)(p - string) - ab;
+ return (ab == 4)? PCRE2_ERROR_UTF8_ERR11 : PCRE2_ERROR_UTF8_ERR12;
+ }
+ }
+return 0;
+
+
+/* ----------------- Check a UTF-16 string ----------------- */
+
+#elif PCRE2_CODE_UNIT_WIDTH == 16
+
+/* There's not so much work, nor so many errors, for UTF-16.
+PCRE2_ERROR_UTF16_ERR1 Missing low surrogate at the end of the string
+PCRE2_ERROR_UTF16_ERR2 Invalid low surrogate
+PCRE2_ERROR_UTF16_ERR3 Isolated low surrogate
+*/
+
+for (p = string; length > 0; p++)
+ {
+ c = *p;
+ length--;
+
+ if ((c & 0xf800) != 0xd800)
+ {
+ /* Normal UTF-16 code point. Neither high nor low surrogate. */
+ }
+ else if ((c & 0x0400) == 0)
+ {
+ /* High surrogate. Must be a followed by a low surrogate. */
+ if (length == 0)
+ {
+ *erroroffset = p - string;
+ return PCRE2_ERROR_UTF16_ERR1;
+ }
+ p++;
+ length--;
+ if ((*p & 0xfc00) != 0xdc00)
+ {
+ *erroroffset = p - string - 1;
+ return PCRE2_ERROR_UTF16_ERR2;
+ }
+ }
+ else
+ {
+ /* Isolated low surrogate. Always an error. */
+ *erroroffset = p - string;
+ return PCRE2_ERROR_UTF16_ERR3;
+ }
+ }
+return 0;
+
+
+
+/* ----------------- Check a UTF-32 string ----------------- */
+
+#else
+
+/* There is very little to do for a UTF-32 string.
+PCRE2_ERROR_UTF32_ERR1 Surrogate character
+PCRE2_ERROR_UTF32_ERR2 Character > 0x10ffff
+*/
+
+for (p = string; length > 0; length--, p++)
+ {
+ c = *p;
+ if ((c & 0xfffff800u) != 0xd800u)
+ {
+ /* Normal UTF-32 code point. Neither high nor low surrogate. */
+ if (c > 0x10ffffu)
+ {
+ *erroroffset = p - string;
+ return PCRE2_ERROR_UTF32_ERR2;
+ }
+ }
+ else
+ {
+ /* A surrogate */
+ *erroroffset = p - string;
+ return PCRE2_ERROR_UTF32_ERR1;
+ }
+ }
+return 0;
+#endif /* CODE_UNIT_WIDTH */
+}
+#endif /* SUPPORT_UNICODE */
+
+/* End of pcre2_valid_utf.c */
diff --git a/contrib/libs/pcre2/src/pcre2_xclass.c b/contrib/libs/pcre2/src/pcre2_xclass.c
new file mode 100644
index 0000000000..cd9331c33b
--- /dev/null
+++ b/contrib/libs/pcre2/src/pcre2_xclass.c
@@ -0,0 +1,289 @@
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+ Written by Philip Hazel
+ Original API code Copyright (c) 1997-2012 University of Cambridge
+ New API code Copyright (c) 2016-2022 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains an internal function that is used to match an extended
+class. It is used by pcre2_auto_possessify() and by both pcre2_match() and
+pcre2_def_match(). */
+
+
+#ifdef HAVE_CONFIG_H
+#include <pcre2_config.h>
+#endif
+
+
+#include "pcre2_internal.h"
+
+/*************************************************
+* Match character against an XCLASS *
+*************************************************/
+
+/* This function is called to match a character against an extended class that
+might contain codepoints above 255 and/or Unicode properties.
+
+Arguments:
+ c the character
+ data points to the flag code unit of the XCLASS data
+ utf TRUE if in UTF mode
+
+Returns: TRUE if character matches, else FALSE
+*/
+
+BOOL
+PRIV(xclass)(uint32_t c, PCRE2_SPTR data, BOOL utf)
+{
+PCRE2_UCHAR t;
+BOOL negated = (*data & XCL_NOT) != 0;
+
+#if PCRE2_CODE_UNIT_WIDTH == 8
+/* In 8 bit mode, this must always be TRUE. Help the compiler to know that. */
+utf = TRUE;
+#endif
+
+/* Code points < 256 are matched against a bitmap, if one is present. If not,
+we still carry on, because there may be ranges that start below 256 in the
+additional data. */
+
+if (c < 256)
+ {
+ if ((*data & XCL_HASPROP) == 0)
+ {
+ if ((*data & XCL_MAP) == 0) return negated;
+ return (((uint8_t *)(data + 1))[c/8] & (1u << (c&7))) != 0;
+ }
+ if ((*data & XCL_MAP) != 0 &&
+ (((uint8_t *)(data + 1))[c/8] & (1u << (c&7))) != 0)
+ return !negated; /* char found */
+ }
+
+/* First skip the bit map if present. Then match against the list of Unicode
+properties or large chars or ranges that end with a large char. We won't ever
+encounter XCL_PROP or XCL_NOTPROP when UTF support is not compiled. */
+
+if ((*data++ & XCL_MAP) != 0) data += 32 / sizeof(PCRE2_UCHAR);
+
+while ((t = *data++) != XCL_END)
+ {
+ uint32_t x, y;
+ if (t == XCL_SINGLE)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ GETCHARINC(x, data); /* macro generates multiple statements */
+ }
+ else
+#endif
+ x = *data++;
+ if (c == x) return !negated;
+ }
+ else if (t == XCL_RANGE)
+ {
+#ifdef SUPPORT_UNICODE
+ if (utf)
+ {
+ GETCHARINC(x, data); /* macro generates multiple statements */
+ GETCHARINC(y, data); /* macro generates multiple statements */
+ }
+ else
+#endif
+ {
+ x = *data++;
+ y = *data++;
+ }
+ if (c >= x && c <= y) return !negated;
+ }
+
+#ifdef SUPPORT_UNICODE
+ else /* XCL_PROP & XCL_NOTPROP */
+ {
+ const ucd_record *prop = GET_UCD(c);
+ BOOL isprop = t == XCL_PROP;
+ BOOL ok;
+
+ switch(*data)
+ {
+ case PT_ANY:
+ if (isprop) return !negated;
+ break;
+
+ case PT_LAMP:
+ if ((prop->chartype == ucp_Lu || prop->chartype == ucp_Ll ||
+ prop->chartype == ucp_Lt) == isprop) return !negated;
+ break;
+
+ case PT_GC:
+ if ((data[1] == PRIV(ucp_gentype)[prop->chartype]) == isprop)
+ return !negated;
+ break;
+
+ case PT_PC:
+ if ((data[1] == prop->chartype) == isprop) return !negated;
+ break;
+
+ case PT_SC:
+ if ((data[1] == prop->script) == isprop) return !negated;
+ break;
+
+ case PT_SCX:
+ ok = (data[1] == prop->script ||
+ MAPBIT(PRIV(ucd_script_sets) + UCD_SCRIPTX_PROP(prop), data[1]) != 0);
+ if (ok == isprop) return !negated;
+ break;
+
+ case PT_ALNUM:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N) == isprop)
+ return !negated;
+ break;
+
+ /* Perl space used to exclude VT, but from Perl 5.18 it is included,
+ which means that Perl space and POSIX space are now identical. PCRE
+ was changed at release 8.34. */
+
+ case PT_SPACE: /* Perl space */
+ case PT_PXSPACE: /* POSIX space */
+ switch(c)
+ {
+ HSPACE_CASES:
+ VSPACE_CASES:
+ if (isprop) return !negated;
+ break;
+
+ default:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z) == isprop)
+ return !negated;
+ break;
+ }
+ break;
+
+ case PT_WORD:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L ||
+ PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE)
+ == isprop)
+ return !negated;
+ break;
+
+ case PT_UCNC:
+ if (c < 0xa0)
+ {
+ if ((c == CHAR_DOLLAR_SIGN || c == CHAR_COMMERCIAL_AT ||
+ c == CHAR_GRAVE_ACCENT) == isprop)
+ return !negated;
+ }
+ else
+ {
+ if ((c < 0xd800 || c > 0xdfff) == isprop)
+ return !negated;
+ }
+ break;
+
+ case PT_BIDICL:
+ if ((UCD_BIDICLASS_PROP(prop) == data[1]) == isprop)
+ return !negated;
+ break;
+
+ case PT_BOOL:
+ ok = MAPBIT(PRIV(ucd_boolprop_sets) +
+ UCD_BPROPS_PROP(prop), data[1]) != 0;
+ if (ok == isprop) return !negated;
+ break;
+
+ /* The following three properties can occur only in an XCLASS, as there
+ is no \p or \P coding for them. */
+
+ /* Graphic character. Implement this as not Z (space or separator) and
+ not C (other), except for Cf (format) with a few exceptions. This seems
+ to be what Perl does. The exceptional characters are:
+
+ U+061C Arabic Letter Mark
+ U+180E Mongolian Vowel Separator
+ U+2066 - U+2069 Various "isolate"s
+ */
+
+ case PT_PXGRAPH:
+ if ((PRIV(ucp_gentype)[prop->chartype] != ucp_Z &&
+ (PRIV(ucp_gentype)[prop->chartype] != ucp_C ||
+ (prop->chartype == ucp_Cf &&
+ c != 0x061c && c != 0x180e && (c < 0x2066 || c > 0x2069))
+ )) == isprop)
+ return !negated;
+ break;
+
+ /* Printable character: same as graphic, with the addition of Zs, i.e.
+ not Zl and not Zp, and U+180E. */
+
+ case PT_PXPRINT:
+ if ((prop->chartype != ucp_Zl &&
+ prop->chartype != ucp_Zp &&
+ (PRIV(ucp_gentype)[prop->chartype] != ucp_C ||
+ (prop->chartype == ucp_Cf &&
+ c != 0x061c && (c < 0x2066 || c > 0x2069))
+ )) == isprop)
+ return !negated;
+ break;
+
+ /* Punctuation: all Unicode punctuation, plus ASCII characters that
+ Unicode treats as symbols rather than punctuation, for Perl
+ compatibility (these are $+<=>^`|~). */
+
+ case PT_PXPUNCT:
+ if ((PRIV(ucp_gentype)[prop->chartype] == ucp_P ||
+ (c < 128 && PRIV(ucp_gentype)[prop->chartype] == ucp_S)) == isprop)
+ return !negated;
+ break;
+
+ /* This should never occur, but compilers may mutter if there is no
+ default. */
+
+ default:
+ return FALSE;
+ }
+
+ data += 2;
+ }
+#else
+ (void)utf; /* Avoid compiler warning */
+#endif /* SUPPORT_UNICODE */
+ }
+
+return negated; /* char did not match */
+}
+
+/* End of pcre2_xclass.c */
diff --git a/contrib/libs/pcre2/src/sljit/sljitConfig.h b/contrib/libs/pcre2/src/sljit/sljitConfig.h
new file mode 100644
index 0000000000..5fba7aa638
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitConfig.h
@@ -0,0 +1,162 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+#ifndef SLJIT_CONFIG_H_
+#define SLJIT_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ This file contains the basic configuration options for the SLJIT compiler
+ and their default values. These options can be overridden in the
+ sljitConfigPre.h header file when SLJIT_HAVE_CONFIG_PRE is set to a
+ non-zero value.
+*/
+
+/* --------------------------------------------------------------------- */
+/* Architecture */
+/* --------------------------------------------------------------------- */
+
+/* Architecture selection. */
+/* #define SLJIT_CONFIG_X86_32 1 */
+/* #define SLJIT_CONFIG_X86_64 1 */
+/* #define SLJIT_CONFIG_ARM_V5 1 */
+/* #define SLJIT_CONFIG_ARM_V7 1 */
+/* #define SLJIT_CONFIG_ARM_THUMB2 1 */
+/* #define SLJIT_CONFIG_ARM_64 1 */
+/* #define SLJIT_CONFIG_PPC_32 1 */
+/* #define SLJIT_CONFIG_PPC_64 1 */
+/* #define SLJIT_CONFIG_MIPS_32 1 */
+/* #define SLJIT_CONFIG_MIPS_64 1 */
+/* #define SLJIT_CONFIG_RISCV_32 1 */
+/* #define SLJIT_CONFIG_RISCV_64 1 */
+/* #define SLJIT_CONFIG_S390X 1 */
+
+/* #define SLJIT_CONFIG_AUTO 1 */
+/* #define SLJIT_CONFIG_UNSUPPORTED 1 */
+
+/* --------------------------------------------------------------------- */
+/* Utilities */
+/* --------------------------------------------------------------------- */
+
+/* Implements a stack like data structure (by using mmap / VirtualAlloc */
+/* or a custom allocator). */
+#ifndef SLJIT_UTIL_STACK
+/* Enabled by default */
+#define SLJIT_UTIL_STACK 1
+#endif
+
+/* Uses user provided allocator to allocate the stack (see SLJIT_UTIL_STACK) */
+#ifndef SLJIT_UTIL_SIMPLE_STACK_ALLOCATION
+/* Disabled by default */
+#define SLJIT_UTIL_SIMPLE_STACK_ALLOCATION 0
+#endif
+
+/* Single threaded application. Does not require any locks. */
+#ifndef SLJIT_SINGLE_THREADED
+/* Disabled by default. */
+#define SLJIT_SINGLE_THREADED 0
+#endif
+
+/* --------------------------------------------------------------------- */
+/* Configuration */
+/* --------------------------------------------------------------------- */
+
+/* If SLJIT_STD_MACROS_DEFINED is not defined, the application should
+ define SLJIT_MALLOC, SLJIT_FREE, SLJIT_MEMCPY, and NULL. */
+#ifndef SLJIT_STD_MACROS_DEFINED
+/* Disabled by default. */
+#define SLJIT_STD_MACROS_DEFINED 0
+#endif
+
+/* Executable code allocation:
+ If SLJIT_EXECUTABLE_ALLOCATOR is not defined, the application should
+ define SLJIT_MALLOC_EXEC, SLJIT_FREE_EXEC, and SLJIT_EXEC_OFFSET. */
+#ifndef SLJIT_EXECUTABLE_ALLOCATOR
+/* Enabled by default. */
+#define SLJIT_EXECUTABLE_ALLOCATOR 1
+
+/* When SLJIT_PROT_EXECUTABLE_ALLOCATOR is enabled SLJIT uses
+ an allocator which does not set writable and executable
+ permission flags at the same time.
+ Instead, it creates a shared memory segment (usually backed by a file)
+ and maps it twice, with different permissions, depending on the use
+ case.
+ The trade-off is increased use of virtual memory, incompatibility with
+ fork(), and some possible additional security risks by the use of
+ publicly accessible files for the generated code. */
+#ifndef SLJIT_PROT_EXECUTABLE_ALLOCATOR
+/* Disabled by default. */
+#define SLJIT_PROT_EXECUTABLE_ALLOCATOR 0
+#endif
+
+/* When SLJIT_WX_EXECUTABLE_ALLOCATOR is enabled SLJIT uses an
+ allocator which does not set writable and executable permission
+ flags at the same time.
+ Instead, it creates a new independent map on each invocation and
+ switches permissions at the underlying pages as needed.
+ The trade-off is increased memory use and degraded performance. */
+#ifndef SLJIT_WX_EXECUTABLE_ALLOCATOR
+/* Disabled by default. */
+#define SLJIT_WX_EXECUTABLE_ALLOCATOR 0
+#endif
+
+#endif /* !SLJIT_EXECUTABLE_ALLOCATOR */
+
+/* Return with error when an invalid argument is passed. */
+#ifndef SLJIT_ARGUMENT_CHECKS
+/* Disabled by default */
+#define SLJIT_ARGUMENT_CHECKS 0
+#endif
+
+/* Debug checks (assertions, etc.). */
+#ifndef SLJIT_DEBUG
+/* Enabled by default */
+#define SLJIT_DEBUG 1
+#endif
+
+/* Verbose operations. */
+#ifndef SLJIT_VERBOSE
+/* Enabled by default */
+#define SLJIT_VERBOSE 1
+#endif
+
+/*
+ SLJIT_IS_FPU_AVAILABLE
+ The availability of the FPU can be controlled by SLJIT_IS_FPU_AVAILABLE.
+ zero value - FPU is NOT present.
+ nonzero value - FPU is present.
+*/
+
+/* For further configurations, see the beginning of sljitConfigInternal.h */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SLJIT_CONFIG_H_ */
diff --git a/contrib/libs/pcre2/src/sljit/sljitConfigInternal.h b/contrib/libs/pcre2/src/sljit/sljitConfigInternal.h
new file mode 100644
index 0000000000..cd3ce69734
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitConfigInternal.h
@@ -0,0 +1,851 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+#ifndef SLJIT_CONFIG_INTERNAL_H_
+#define SLJIT_CONFIG_INTERNAL_H_
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) \
+ || (defined SLJIT_DEBUG && SLJIT_DEBUG && (!defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE)))
+#include <stdio.h>
+#endif
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG \
+ && (!defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE) || !defined(SLJIT_HALT_PROCESS)))
+#include <stdlib.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ SLJIT defines the following architecture dependent types and macros:
+
+ Types:
+ sljit_s8, sljit_u8 : signed and unsigned 8 bit integer type
+ sljit_s16, sljit_u16 : signed and unsigned 16 bit integer type
+ sljit_s32, sljit_u32 : signed and unsigned 32 bit integer type
+ sljit_sw, sljit_uw : signed and unsigned machine word, enough to store a pointer
+ sljit_p : unsgined pointer value (usually the same as sljit_uw, but
+ some 64 bit ABIs may use 32 bit pointers)
+ sljit_f32 : 32 bit single precision floating point value
+ sljit_f64 : 64 bit double precision floating point value
+
+ Macros for feature detection (boolean):
+ SLJIT_32BIT_ARCHITECTURE : 32 bit architecture
+ SLJIT_64BIT_ARCHITECTURE : 64 bit architecture
+ SLJIT_LITTLE_ENDIAN : little endian architecture
+ SLJIT_BIG_ENDIAN : big endian architecture
+ SLJIT_UNALIGNED : unaligned memory accesses for non-fpu operations are supported
+ SLJIT_FPU_UNALIGNED : unaligned memory accesses for fpu operations are supported
+ SLJIT_INDIRECT_CALL : see SLJIT_FUNC_ADDR() for more information
+
+ Constants:
+ SLJIT_NUMBER_OF_REGISTERS : number of available registers
+ SLJIT_NUMBER_OF_SCRATCH_REGISTERS : number of available scratch registers
+ SLJIT_NUMBER_OF_SAVED_REGISTERS : number of available saved registers
+ SLJIT_NUMBER_OF_FLOAT_REGISTERS : number of available floating point registers
+ SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS : number of available floating point scratch registers
+ SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS : number of available floating point saved registers
+ SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_sw/sljit_uw array by index
+ SLJIT_F32_SHIFT : the shift required to apply when accessing
+ a single precision floating point array by index
+ SLJIT_F64_SHIFT : the shift required to apply when accessing
+ a double precision floating point array by index
+ SLJIT_PREF_SHIFT_REG : x86 systems prefers ecx for shifting by register
+ the scratch register index of ecx is stored in this variable
+ SLJIT_LOCALS_OFFSET : local space starting offset (SLJIT_SP + SLJIT_LOCALS_OFFSET)
+ SLJIT_RETURN_ADDRESS_OFFSET : a return instruction always adds this offset to the return address
+
+ Other macros:
+ SLJIT_FUNC : calling convention attribute for both calling JIT from C and C calling back from JIT
+ SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (platform independent helper)
+*/
+
+/*****************/
+/* Sanity check. */
+/*****************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ + (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ + (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ + (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ + (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ + (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ + (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ + (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ + (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \
+ + (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
+ + (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
+ + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \
+ + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2
+#error "Multiple architectures are selected"
+#endif
+
+#if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ && !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \
+ && !(defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ && !(defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) \
+ && !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
+ && !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X) \
+ && !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) \
+ && !(defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+#if defined SLJIT_CONFIG_AUTO && !SLJIT_CONFIG_AUTO
+#error "An architecture must be selected"
+#else /* SLJIT_CONFIG_AUTO */
+#define SLJIT_CONFIG_AUTO 1
+#endif /* !SLJIT_CONFIG_AUTO */
+#endif /* !SLJIT_CONFIG */
+
+/********************************************************/
+/* Automatic CPU detection (requires compiler support). */
+/********************************************************/
+
+#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO)
+
+#ifndef _WIN32
+
+#if defined(__i386__) || defined(__i386)
+#define SLJIT_CONFIG_X86_32 1
+#elif defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif defined(__arm__) || defined(__ARM__)
+#ifdef __thumb2__
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__)
+#define SLJIT_CONFIG_ARM_V7 1
+#else
+#define SLJIT_CONFIG_ARM_V5 1
+#endif
+#elif defined (__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#elif defined(__ppc64__) || defined(__powerpc64__) || (defined(_ARCH_PPC64) && defined(__64BIT__)) || (defined(_POWER) && defined(__64BIT__))
+#define SLJIT_CONFIG_PPC_64 1
+#elif defined(__ppc__) || defined(__powerpc__) || defined(_ARCH_PPC) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_POWER)
+#define SLJIT_CONFIG_PPC_32 1
+#elif defined(__mips__) && !defined(_LP64)
+#define SLJIT_CONFIG_MIPS_32 1
+#elif defined(__mips64)
+#define SLJIT_CONFIG_MIPS_64 1
+#elif defined (__riscv_xlen) && (__riscv_xlen == 32)
+#define SLJIT_CONFIG_RISCV_32 1
+#elif defined (__riscv_xlen) && (__riscv_xlen == 64)
+#define SLJIT_CONFIG_RISCV_64 1
+#elif defined(__s390x__)
+#define SLJIT_CONFIG_S390X 1
+#else
+/* Unsupported architecture */
+#define SLJIT_CONFIG_UNSUPPORTED 1
+#endif
+
+#else /* _WIN32 */
+
+#if defined(_M_X64) || defined(__x86_64__)
+#define SLJIT_CONFIG_X86_64 1
+#elif (defined(_M_ARM) && _M_ARM >= 7 && defined(_M_ARMT)) || defined(__thumb2__)
+#define SLJIT_CONFIG_ARM_THUMB2 1
+#elif (defined(_M_ARM) && _M_ARM >= 7)
+#define SLJIT_CONFIG_ARM_V7 1
+#elif defined(_ARM_)
+#define SLJIT_CONFIG_ARM_V5 1
+#elif defined(_M_ARM64) || defined(__aarch64__)
+#define SLJIT_CONFIG_ARM_64 1
+#else
+#define SLJIT_CONFIG_X86_32 1
+#endif
+
+#endif /* !_WIN32 */
+#endif /* SLJIT_CONFIG_AUTO */
+
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#undef SLJIT_EXECUTABLE_ALLOCATOR
+#endif
+
+/******************************/
+/* CPU family type detection. */
+/******************************/
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+#define SLJIT_CONFIG_ARM_32 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#define SLJIT_CONFIG_X86 1
+#elif (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+#define SLJIT_CONFIG_ARM 1
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_CONFIG_PPC 1
+#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) || (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SLJIT_CONFIG_MIPS 1
+#elif (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32) || (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+#define SLJIT_CONFIG_RISCV 1
+#endif
+
+/***********************************************************/
+/* Intel Control-flow Enforcement Technology (CET) spport. */
+/***********************************************************/
+
+#ifdef SLJIT_CONFIG_X86
+
+#if defined(__CET__) && !(defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET)
+#define SLJIT_CONFIG_X86_CET 1
+#endif
+
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined(__GNUC__)
+#include <x86intrin.h>
+#endif
+
+#endif /* SLJIT_CONFIG_X86 */
+
+/**********************************/
+/* External function definitions. */
+/**********************************/
+
+/* General macros:
+ Note: SLJIT is designed to be independent from them as possible.
+
+ In release mode (SLJIT_DEBUG is not defined) only the following
+ external functions are needed:
+*/
+
+#ifndef SLJIT_MALLOC
+#define SLJIT_MALLOC(size, allocator_data) malloc(size)
+#endif
+
+#ifndef SLJIT_FREE
+#define SLJIT_FREE(ptr, allocator_data) free(ptr)
+#endif
+
+#ifndef SLJIT_MEMCPY
+#define SLJIT_MEMCPY(dest, src, len) memcpy(dest, src, len)
+#endif
+
+#ifndef SLJIT_MEMMOVE
+#define SLJIT_MEMMOVE(dest, src, len) memmove(dest, src, len)
+#endif
+
+#ifndef SLJIT_ZEROMEM
+#define SLJIT_ZEROMEM(dest, len) memset(dest, 0, len)
+#endif
+
+/***************************/
+/* Compiler helper macros. */
+/***************************/
+
+#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY)
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define SLJIT_LIKELY(x) __builtin_expect((x), 1)
+#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0)
+#else
+#define SLJIT_LIKELY(x) (x)
+#define SLJIT_UNLIKELY(x) (x)
+#endif
+
+#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */
+
+#ifndef SLJIT_INLINE
+/* Inline functions. Some old compilers do not support them. */
+#ifdef __SUNPRO_C
+#if __SUNPRO_C < 0x560
+#define SLJIT_INLINE
+#else
+#define SLJIT_INLINE inline
+#endif /* __SUNPRO_C */
+#else
+#define SLJIT_INLINE __inline
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_NOINLINE
+/* Not inline functions. */
+#if defined(__GNUC__)
+#define SLJIT_NOINLINE __attribute__ ((noinline))
+#else
+#define SLJIT_NOINLINE
+#endif
+#endif /* !SLJIT_INLINE */
+
+#ifndef SLJIT_UNUSED_ARG
+/* Unused arguments. */
+#define SLJIT_UNUSED_ARG(arg) (void)arg
+#endif
+
+/*********************************/
+/* Type of public API functions. */
+/*********************************/
+
+#ifndef SLJIT_API_FUNC_ATTRIBUTE
+#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC)
+/* Static ABI functions. For all-in-one programs. */
+
+#if defined(__GNUC__)
+/* Disable unused warnings in gcc. */
+#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused))
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE static
+#endif
+
+#else
+#define SLJIT_API_FUNC_ATTRIBUTE
+#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */
+#endif /* defined SLJIT_API_FUNC_ATTRIBUTE */
+
+/****************************/
+/* Instruction cache flush. */
+/****************************/
+
+/*
+ * TODO:
+ *
+ * clang >= 15 could be safe to enable below
+ * older versions are known to abort in some targets
+ * https://github.com/PhilipHazel/pcre2/issues/92
+ *
+ * beware some vendors (ex: Microsoft, Apple) are known to have
+ * removed the code to support this builtin even if the call for
+ * __has_builtin reports it is available.
+ *
+ * make sure linking doesn't fail because __clear_cache() is
+ * missing before changing it or add an exception so that the
+ * system provided method that should be defined below is used
+ * instead.
+ */
+#if (!defined SLJIT_CACHE_FLUSH && defined __has_builtin)
+#if __has_builtin(__builtin___clear_cache) && !defined(__clang__)
+
+/*
+ * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=91248
+ * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=93811
+ * gcc's clear_cache builtin for power is broken
+ */
+#if !defined(SLJIT_CONFIG_PPC)
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __builtin___clear_cache((char*)(from), (char*)(to))
+#endif
+
+#endif /* gcc >= 10 */
+#endif /* (!defined SLJIT_CACHE_FLUSH && defined __has_builtin) */
+
+#ifndef SLJIT_CACHE_FLUSH
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+/* Not required to implement on archs with unified caches. */
+#define SLJIT_CACHE_FLUSH(from, to)
+
+#elif defined __APPLE__
+
+/* Supported by all macs since Mac OS 10.5.
+ However, it does not work on non-jailbroken iOS devices,
+ although the compilation is successful. */
+#include <libkern/OSCacheControl.h>
+#define SLJIT_CACHE_FLUSH(from, to) \
+ sys_icache_invalidate((void*)(from), (size_t)((char*)(to) - (char*)(from)))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ ppc_cache_flush((from), (to))
+#define SLJIT_CACHE_FLUSH_OWN_IMPL 1
+
+#elif defined(_WIN32)
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ FlushInstructionCache(GetCurrentProcess(), (void*)(from), (char*)(to) - (char*)(from))
+
+#elif (defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) || defined(__clang__)
+
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __builtin___clear_cache((char*)(from), (char*)(to))
+
+#elif defined __ANDROID__
+
+/* Android ARMv7 with gcc lacks __clear_cache; use cacheflush instead. */
+#include <sys/cachectl.h>
+#define SLJIT_CACHE_FLUSH(from, to) \
+ cacheflush((long)(from), (long)(to), 0)
+
+#else
+
+/* Call __ARM_NR_cacheflush on ARM-Linux or the corresponding MIPS syscall. */
+#define SLJIT_CACHE_FLUSH(from, to) \
+ __clear_cache((char*)(from), (char*)(to))
+
+#endif
+
+#endif /* !SLJIT_CACHE_FLUSH */
+
+/******************************************************/
+/* Integer and floating point type definitions. */
+/******************************************************/
+
+/* 8 bit byte type. */
+typedef unsigned char sljit_u8;
+typedef signed char sljit_s8;
+
+/* 16 bit half-word type. */
+typedef unsigned short int sljit_u16;
+typedef signed short int sljit_s16;
+
+/* 32 bit integer type. */
+typedef unsigned int sljit_u32;
+typedef signed int sljit_s32;
+
+/* Machine word type. Enough for storing a pointer.
+ 32 bit for 32 bit machines.
+ 64 bit for 64 bit machines. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+/* Just to have something. */
+#define SLJIT_WORD_SHIFT 0
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \
+ && !(defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64) \
+ && !(defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64) \
+ && !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+#define SLJIT_32BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 2
+typedef unsigned int sljit_uw;
+typedef int sljit_sw;
+#else
+#define SLJIT_64BIT_ARCHITECTURE 1
+#define SLJIT_WORD_SHIFT 3
+#ifdef _WIN32
+#ifdef __GNUC__
+/* These types do not require windows.h */
+typedef unsigned long long sljit_uw;
+typedef long long sljit_sw;
+#else
+typedef unsigned __int64 sljit_uw;
+typedef __int64 sljit_sw;
+#endif
+#else /* !_WIN32 */
+typedef unsigned long int sljit_uw;
+typedef long int sljit_sw;
+#endif /* _WIN32 */
+#endif
+
+typedef sljit_uw sljit_p;
+
+/* Floating point types. */
+typedef float sljit_f32;
+typedef double sljit_f64;
+
+/* Shift for pointer sized data. */
+#define SLJIT_POINTER_SHIFT SLJIT_WORD_SHIFT
+
+/* Shift for double precision sized data. */
+#define SLJIT_F32_SHIFT 2
+#define SLJIT_F64_SHIFT 3
+
+#ifndef SLJIT_W
+
+/* Defining long constants. */
+#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+#define SLJIT_W(w) (w##l)
+#elif (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#ifdef _WIN64
+#define SLJIT_W(w) (w##ll)
+#else /* !windows */
+#define SLJIT_W(w) (w##l)
+#endif /* windows */
+#else /* 32 bit */
+#define SLJIT_W(w) (w)
+#endif /* unknown */
+
+#endif /* !SLJIT_W */
+
+/*************************/
+/* Endianness detection. */
+/*************************/
+
+#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN)
+
+/* These macros are mostly useful for the applications. */
+#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#ifdef __LITTLE_ENDIAN__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#ifdef __MIPSEL__
+#define SLJIT_LITTLE_ENDIAN 1
+#else
+#define SLJIT_BIG_ENDIAN 1
+#endif
+
+#ifndef SLJIT_MIPS_REV
+
+/* Auto detecting mips revision. */
+#if (defined __mips_isa_rev) && (__mips_isa_rev >= 6)
+#define SLJIT_MIPS_REV 6
+#elif (defined __mips_isa_rev && __mips_isa_rev >= 1) \
+ || (defined __clang__ && defined _MIPS_ARCH_OCTEON) \
+ || (defined __clang__ && defined _MIPS_ARCH_P5600)
+/* clang either forgets to define (clang-7) __mips_isa_rev at all
+ * or sets it to zero (clang-8,-9) for -march=octeon (MIPS64 R2+)
+ * and -march=p5600 (MIPS32 R5).
+ * It also sets the __mips macro to 64 or 32 for -mipsN when N <= 5
+ * (should be set to N exactly) so we cannot rely on this too.
+ */
+#define SLJIT_MIPS_REV 1
+#endif
+
+#endif /* !SLJIT_MIPS_REV */
+
+#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+#define SLJIT_BIG_ENDIAN 1
+
+#else
+#define SLJIT_LITTLE_ENDIAN 1
+#endif
+
+#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */
+
+/* Sanity check. */
+#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#error "Exactly one endianness must be selected"
+#endif
+
+#ifndef SLJIT_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \
+ || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+#define SLJIT_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_UNALIGNED */
+
+#ifndef SLJIT_FPU_UNALIGNED
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+#define SLJIT_FPU_UNALIGNED 1
+#endif
+
+#endif /* !SLJIT_FPU_UNALIGNED */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+/* Auto detect SSE2 support using CPUID.
+ On 64 bit x86 cpus, sse2 must be present. */
+#define SLJIT_DETECT_SSE2 1
+#endif
+
+/*****************************************************************************************/
+/* Calling convention of functions generated by SLJIT or called from the generated code. */
+/*****************************************************************************************/
+
+#ifndef SLJIT_FUNC
+#define SLJIT_FUNC
+#endif /* !SLJIT_FUNC */
+
+#ifndef SLJIT_INDIRECT_CALL
+#if ((defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) && (!defined _CALL_ELF || _CALL_ELF == 1)) \
+ || ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && defined _AIX)
+/* It seems certain ppc compilers use an indirect addressing for functions
+ which makes things complicated. */
+#define SLJIT_INDIRECT_CALL 1
+#endif
+#endif /* SLJIT_INDIRECT_CALL */
+
+/* The offset which needs to be subtracted from the return address to
+determine the next executed instruction after return. */
+#ifndef SLJIT_RETURN_ADDRESS_OFFSET
+#define SLJIT_RETURN_ADDRESS_OFFSET 0
+#endif /* SLJIT_RETURN_ADDRESS_OFFSET */
+
+/***************************************************/
+/* Functions of the built-in executable allocator. */
+/***************************************************/
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#define SLJIT_BUILTIN_MALLOC_EXEC(size, exec_allocator_data) sljit_malloc_exec(size)
+#define SLJIT_BUILTIN_FREE_EXEC(ptr, exec_allocator_data) sljit_free_exec(ptr)
+
+#ifndef SLJIT_MALLOC_EXEC
+#define SLJIT_MALLOC_EXEC(size, exec_allocator_data) SLJIT_BUILTIN_MALLOC_EXEC((size), (exec_allocator_data))
+#endif /* SLJIT_MALLOC_EXEC */
+
+#ifndef SLJIT_FREE_EXEC
+#define SLJIT_FREE_EXEC(ptr, exec_allocator_data) SLJIT_BUILTIN_FREE_EXEC((ptr), (exec_allocator_data))
+#endif /* SLJIT_FREE_EXEC */
+
+#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR)
+SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr);
+#define SLJIT_EXEC_OFFSET(ptr) sljit_exec_offset(ptr)
+#else
+#define SLJIT_EXEC_OFFSET(ptr) 0
+#endif
+
+#endif /* SLJIT_EXECUTABLE_ALLOCATOR */
+
+/**********************************************/
+/* Registers and locals offset determination. */
+/**********************************************/
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 7
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 7
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE (8 * SSIZE_OF(sw))
+#define SLJIT_PREF_SHIFT_REG SLJIT_R2
+
+#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 13
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 15
+#ifndef _WIN64
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 6
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#else /* _WIN64 */
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 10
+#define SLJIT_LOCALS_OFFSET_BASE (4 * SSIZE_OF(sw))
+#endif /* !_WIN64 */
+#define SLJIT_PREF_SHIFT_REG SLJIT_R3
+
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 14
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 14
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+#define SLJIT_NUMBER_OF_REGISTERS 26
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 10
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE (2 * (sljit_s32)sizeof(sljit_sw))
+
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+#define SLJIT_NUMBER_OF_REGISTERS 23
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 17
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 18
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) || (defined _AIX)
+#define SLJIT_LOCALS_OFFSET_BASE ((6 + 8) * (sljit_s32)sizeof(sljit_sw))
+#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+/* Add +1 for double alignment. */
+#define SLJIT_LOCALS_OFFSET_BASE ((3 + 1) * (sljit_s32)sizeof(sljit_sw))
+#else
+#define SLJIT_LOCALS_OFFSET_BASE (3 * (sljit_s32)sizeof(sljit_sw))
+#endif /* SLJIT_CONFIG_PPC_64 || _AIX */
+
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+#define SLJIT_NUMBER_OF_REGISTERS 21
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define SLJIT_LOCALS_OFFSET_BASE (4 * (sljit_s32)sizeof(sljit_sw))
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 13
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 6
+#else
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 29
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#endif
+
+#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+
+#define SLJIT_NUMBER_OF_REGISTERS 23
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 12
+#define SLJIT_LOCALS_OFFSET_BASE 0
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 30
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 12
+
+#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+/*
+ * https://refspecs.linuxbase.org/ELF/zSeries/lzsabi0_zSeries.html#STACKFRAME
+ *
+ * 160
+ * .. FR6
+ * .. FR4
+ * .. FR2
+ * 128 FR0
+ * 120 R15 (used for SP)
+ * 112 R14
+ * 104 R13
+ * 96 R12
+ * ..
+ * 48 R6
+ * ..
+ * 16 R2
+ * 8 RESERVED
+ * 0 SP
+ */
+#define SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE 160
+
+#define SLJIT_NUMBER_OF_REGISTERS 12
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 8
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 15
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 8
+#define SLJIT_LOCALS_OFFSET_BASE SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE
+
+#elif (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SLJIT_NUMBER_OF_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_REGISTERS 0
+#define SLJIT_NUMBER_OF_FLOAT_REGISTERS 0
+#define SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS 0
+#define SLJIT_LOCALS_OFFSET_BASE 0
+
+#endif
+
+#define SLJIT_LOCALS_OFFSET (SLJIT_LOCALS_OFFSET_BASE)
+
+#define SLJIT_NUMBER_OF_SCRATCH_REGISTERS \
+ (SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SAVED_REGISTERS)
+
+#define SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS \
+ (SLJIT_NUMBER_OF_FLOAT_REGISTERS - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS)
+
+/********************************/
+/* CPU status flags management. */
+/********************************/
+
+#if (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) \
+ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+#define SLJIT_HAS_STATUS_FLAGS_STATE 1
+#endif
+
+/*************************************/
+/* Debug and verbose related macros. */
+/*************************************/
+
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+#if !defined(SLJIT_ASSERT) || !defined(SLJIT_UNREACHABLE)
+
+/* SLJIT_HALT_PROCESS must halt the process. */
+#ifndef SLJIT_HALT_PROCESS
+#define SLJIT_HALT_PROCESS() \
+ abort();
+#endif /* !SLJIT_HALT_PROCESS */
+
+#endif /* !SLJIT_ASSERT || !SLJIT_UNREACHABLE */
+
+/* Feel free to redefine these two macros. */
+#ifndef SLJIT_ASSERT
+
+#define SLJIT_ASSERT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) { \
+ printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } \
+ } while (0)
+
+#endif /* !SLJIT_ASSERT */
+
+#ifndef SLJIT_UNREACHABLE
+
+#define SLJIT_UNREACHABLE() \
+ do { \
+ printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \
+ SLJIT_HALT_PROCESS(); \
+ } while (0)
+
+#endif /* !SLJIT_UNREACHABLE */
+
+#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+/* Forcing empty, but valid statements. */
+#undef SLJIT_ASSERT
+#undef SLJIT_UNREACHABLE
+
+#define SLJIT_ASSERT(x) \
+ do { } while (0)
+#define SLJIT_UNREACHABLE() \
+ do { } while (0)
+
+#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */
+
+#ifndef SLJIT_COMPILE_ASSERT
+
+#define SLJIT_COMPILE_ASSERT(x, description) \
+ switch(0) { case 0: case ((x) ? 1 : 0): break; }
+
+#endif /* !SLJIT_COMPILE_ASSERT */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SLJIT_CONFIG_INTERNAL_H_ */
diff --git a/contrib/libs/pcre2/src/sljit/sljitExecAllocator.c b/contrib/libs/pcre2/src/sljit/sljitExecAllocator.c
new file mode 100644
index 0000000000..92d940ddc2
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitExecAllocator.c
@@ -0,0 +1,411 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/*
+ This file contains a simple executable memory allocator
+
+ It is assumed, that executable code blocks are usually medium (or sometimes
+ large) memory blocks, and the allocator is not too frequently called (less
+ optimized than other allocators). Thus, using it as a generic allocator is
+ not suggested.
+
+ How does it work:
+ Memory is allocated in continuous memory areas called chunks by alloc_chunk()
+ Chunk format:
+ [ block ][ block ] ... [ block ][ block terminator ]
+
+ All blocks and the block terminator is started with block_header. The block
+ header contains the size of the previous and the next block. These sizes
+ can also contain special values.
+ Block size:
+ 0 - The block is a free_block, with a different size member.
+ 1 - The block is a block terminator.
+ n - The block is used at the moment, and the value contains its size.
+ Previous block size:
+ 0 - This is the first block of the memory chunk.
+ n - The size of the previous block.
+
+ Using these size values we can go forward or backward on the block chain.
+ The unused blocks are stored in a chain list pointed by free_blocks. This
+ list is useful if we need to find a suitable memory area when the allocator
+ is called.
+
+ When a block is freed, the new free block is connected to its adjacent free
+ blocks if possible.
+
+ [ free block ][ used block ][ free block ]
+ and "used block" is freed, the three blocks are connected together:
+ [ one big free block ]
+*/
+
+/* --------------------------------------------------------------------- */
+/* System (OS) functions */
+/* --------------------------------------------------------------------- */
+
+/* 64 KByte. */
+#define CHUNK_SIZE (sljit_uw)0x10000u
+
+/*
+ alloc_chunk / free_chunk :
+ * allocate executable system memory chunks
+ * the size is always divisible by CHUNK_SIZE
+ SLJIT_ALLOCATOR_LOCK / SLJIT_ALLOCATOR_UNLOCK :
+ * provided as part of sljitUtils
+ * only the allocator requires this lock, sljit is fully thread safe
+ as it only uses local variables
+*/
+
+#ifdef _WIN32
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
+
+static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
+{
+ return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+}
+
+static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
+{
+ SLJIT_UNUSED_ARG(size);
+ VirtualFree(chunk, 0, MEM_RELEASE);
+}
+
+#else /* POSIX */
+
+#if defined(__APPLE__) && defined(MAP_JIT)
+/*
+ On macOS systems, returns MAP_JIT if it is defined _and_ we're running on a
+ version where it's OK to have more than one JIT block or where MAP_JIT is
+ required.
+ On non-macOS systems, returns MAP_JIT if it is defined.
+*/
+#include <TargetConditionals.h>
+#if TARGET_OS_OSX
+#if defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86
+#ifdef MAP_ANON
+#include <sys/utsname.h>
+#include <stdlib.h>
+
+#define SLJIT_MAP_JIT (get_map_jit_flag())
+
+static SLJIT_INLINE int get_map_jit_flag()
+{
+ size_t page_size;
+ void *ptr;
+ struct utsname name;
+ static int map_jit_flag = -1;
+
+ if (map_jit_flag < 0) {
+ map_jit_flag = 0;
+ uname(&name);
+
+ /* Kernel version for 10.14.0 (Mojave) or later */
+ if (atoi(name.release) >= 18) {
+ page_size = get_page_alignment() + 1;
+ /* Only use MAP_JIT if a hardened runtime is used */
+ ptr = mmap(NULL, page_size, PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ if (ptr != MAP_FAILED)
+ munmap(ptr, page_size);
+ else
+ map_jit_flag = MAP_JIT;
+ }
+ }
+ return map_jit_flag;
+}
+#endif /* MAP_ANON */
+#else /* !SLJIT_CONFIG_X86 */
+#if !(defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
+#error "Unsupported architecture"
+#endif /* SLJIT_CONFIG_ARM */
+#include <AvailabilityMacros.h>
+#include <pthread.h>
+
+#define SLJIT_MAP_JIT (MAP_JIT)
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
+ apple_update_wx_flags(enable_exec)
+
+static SLJIT_INLINE void apple_update_wx_flags(sljit_s32 enable_exec)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 110000
+ pthread_jit_write_protect_np(enable_exec);
+#else
+#error "Must target Big Sur or newer"
+#endif /* BigSur */
+}
+#endif /* SLJIT_CONFIG_X86 */
+#else /* !TARGET_OS_OSX */
+#define SLJIT_MAP_JIT (MAP_JIT)
+#endif /* TARGET_OS_OSX */
+#endif /* __APPLE__ && MAP_JIT */
+#ifndef SLJIT_UPDATE_WX_FLAGS
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
+#endif /* !SLJIT_UPDATE_WX_FLAGS */
+#ifndef SLJIT_MAP_JIT
+#define SLJIT_MAP_JIT (0)
+#endif /* !SLJIT_MAP_JIT */
+
+static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
+{
+ void *retval;
+ int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+ int flags = MAP_PRIVATE;
+ int fd = -1;
+
+#ifdef PROT_MAX
+ prot |= PROT_MAX(prot);
+#endif
+
+#ifdef MAP_ANON
+ flags |= MAP_ANON | SLJIT_MAP_JIT;
+#else /* !MAP_ANON */
+ if (SLJIT_UNLIKELY((dev_zero < 0) && open_dev_zero()))
+ return NULL;
+
+ fd = dev_zero;
+#endif /* MAP_ANON */
+
+ retval = mmap(NULL, size, prot, flags, fd, 0);
+ if (retval == MAP_FAILED)
+ return NULL;
+
+#ifdef __FreeBSD__
+ /* HardenedBSD's mmap lies, so check permissions again */
+ if (mprotect(retval, size, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
+ munmap(retval, size);
+ return NULL;
+ }
+#endif /* FreeBSD */
+
+ SLJIT_UPDATE_WX_FLAGS(retval, (uint8_t *)retval + size, 0);
+
+ return retval;
+}
+
+static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
+{
+ munmap(chunk, size);
+}
+
+#endif /* windows */
+
+/* --------------------------------------------------------------------- */
+/* Common functions */
+/* --------------------------------------------------------------------- */
+
+#define CHUNK_MASK (~(CHUNK_SIZE - 1))
+
+struct block_header {
+ sljit_uw size;
+ sljit_uw prev_size;
+};
+
+struct free_block {
+ struct block_header header;
+ struct free_block *next;
+ struct free_block *prev;
+ sljit_uw size;
+};
+
+#define AS_BLOCK_HEADER(base, offset) \
+ ((struct block_header*)(((sljit_u8*)base) + offset))
+#define AS_FREE_BLOCK(base, offset) \
+ ((struct free_block*)(((sljit_u8*)base) + offset))
+#define MEM_START(base) ((void*)(((sljit_u8*)base) + sizeof(struct block_header)))
+#define ALIGN_SIZE(size) (((size) + sizeof(struct block_header) + 7u) & ~(sljit_uw)7)
+
+static struct free_block* free_blocks;
+static sljit_uw allocated_size;
+static sljit_uw total_size;
+
+static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
+{
+ free_block->header.size = 0;
+ free_block->size = size;
+
+ free_block->next = free_blocks;
+ free_block->prev = NULL;
+ if (free_blocks)
+ free_blocks->prev = free_block;
+ free_blocks = free_block;
+}
+
+static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
+{
+ if (free_block->next)
+ free_block->next->prev = free_block->prev;
+
+ if (free_block->prev)
+ free_block->prev->next = free_block->next;
+ else {
+ SLJIT_ASSERT(free_blocks == free_block);
+ free_blocks = free_block->next;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+ struct block_header *header;
+ struct block_header *next_header;
+ struct free_block *free_block;
+ sljit_uw chunk_size;
+
+ SLJIT_ALLOCATOR_LOCK();
+ if (size < (64 - sizeof(struct block_header)))
+ size = (64 - sizeof(struct block_header));
+ size = ALIGN_SIZE(size);
+
+ free_block = free_blocks;
+ while (free_block) {
+ if (free_block->size >= size) {
+ chunk_size = free_block->size;
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
+ if (chunk_size > size + 64) {
+ /* We just cut a block from the end of the free block. */
+ chunk_size -= size;
+ free_block->size = chunk_size;
+ header = AS_BLOCK_HEADER(free_block, chunk_size);
+ header->prev_size = chunk_size;
+ AS_BLOCK_HEADER(header, size)->prev_size = size;
+ }
+ else {
+ sljit_remove_free_block(free_block);
+ header = (struct block_header*)free_block;
+ size = chunk_size;
+ }
+ allocated_size += size;
+ header->size = size;
+ SLJIT_ALLOCATOR_UNLOCK();
+ return MEM_START(header);
+ }
+ free_block = free_block->next;
+ }
+
+ chunk_size = (size + sizeof(struct block_header) + CHUNK_SIZE - 1) & CHUNK_MASK;
+ header = (struct block_header*)alloc_chunk(chunk_size);
+ if (!header) {
+ SLJIT_ALLOCATOR_UNLOCK();
+ return NULL;
+ }
+
+ chunk_size -= sizeof(struct block_header);
+ total_size += chunk_size;
+
+ header->prev_size = 0;
+ if (chunk_size > size + 64) {
+ /* Cut the allocated space into a free and a used block. */
+ allocated_size += size;
+ header->size = size;
+ chunk_size -= size;
+
+ free_block = AS_FREE_BLOCK(header, size);
+ free_block->header.prev_size = size;
+ sljit_insert_free_block(free_block, chunk_size);
+ next_header = AS_BLOCK_HEADER(free_block, chunk_size);
+ }
+ else {
+ /* All space belongs to this allocation. */
+ allocated_size += chunk_size;
+ header->size = chunk_size;
+ next_header = AS_BLOCK_HEADER(header, chunk_size);
+ }
+ next_header->size = 1;
+ next_header->prev_size = chunk_size;
+ SLJIT_ALLOCATOR_UNLOCK();
+ return MEM_START(header);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ struct block_header *header;
+ struct free_block* free_block;
+
+ SLJIT_ALLOCATOR_LOCK();
+ header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
+ allocated_size -= header->size;
+
+ /* Connecting free blocks together if possible. */
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
+
+ /* If header->prev_size == 0, free_block will equal to header.
+ In this case, free_block->header.size will be > 0. */
+ free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
+ if (SLJIT_UNLIKELY(!free_block->header.size)) {
+ free_block->size += header->size;
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+ else {
+ free_block = (struct free_block*)header;
+ sljit_insert_free_block(free_block, header->size);
+ }
+
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ if (SLJIT_UNLIKELY(!header->size)) {
+ free_block->size += ((struct free_block*)header)->size;
+ sljit_remove_free_block((struct free_block*)header);
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+
+ /* The whole chunk is free. */
+ if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
+ /* If this block is freed, we still have (allocated_size / 2) free space. */
+ if (total_size - free_block->size > (allocated_size * 3 / 2)) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size + sizeof(struct block_header));
+ }
+ }
+
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
+ SLJIT_ALLOCATOR_UNLOCK();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
+{
+ struct free_block* free_block;
+ struct free_block* next_free_block;
+
+ SLJIT_ALLOCATOR_LOCK();
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 0);
+
+ free_block = free_blocks;
+ while (free_block) {
+ next_free_block = free_block->next;
+ if (!free_block->header.prev_size &&
+ AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size + sizeof(struct block_header));
+ }
+ free_block = next_free_block;
+ }
+
+ SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
+ SLJIT_UPDATE_WX_FLAGS(NULL, NULL, 1);
+ SLJIT_ALLOCATOR_UNLOCK();
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitLir.c b/contrib/libs/pcre2/src/sljit/sljitLir.c
new file mode 100644
index 0000000000..abafe1add9
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitLir.c
@@ -0,0 +1,3136 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+#include "sljitLir.h"
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+#endif /* _WIN32 */
+
+#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED)
+
+/* These libraries are needed for the macros below. */
+#include <stdlib.h>
+#include <string.h>
+
+#endif /* SLJIT_STD_MACROS_DEFINED */
+
+#define CHECK_ERROR() \
+ do { \
+ if (SLJIT_UNLIKELY(compiler->error)) \
+ return compiler->error; \
+ } while (0)
+
+#define CHECK_ERROR_PTR() \
+ do { \
+ if (SLJIT_UNLIKELY(compiler->error)) \
+ return NULL; \
+ } while (0)
+
+#define FAIL_IF(expr) \
+ do { \
+ if (SLJIT_UNLIKELY(expr)) \
+ return compiler->error; \
+ } while (0)
+
+#define PTR_FAIL_IF(expr) \
+ do { \
+ if (SLJIT_UNLIKELY(expr)) \
+ return NULL; \
+ } while (0)
+
+#define FAIL_IF_NULL(ptr) \
+ do { \
+ if (SLJIT_UNLIKELY(!(ptr))) { \
+ compiler->error = SLJIT_ERR_ALLOC_FAILED; \
+ return SLJIT_ERR_ALLOC_FAILED; \
+ } \
+ } while (0)
+
+#define PTR_FAIL_IF_NULL(ptr) \
+ do { \
+ if (SLJIT_UNLIKELY(!(ptr))) { \
+ compiler->error = SLJIT_ERR_ALLOC_FAILED; \
+ return NULL; \
+ } \
+ } while (0)
+
+#define PTR_FAIL_WITH_EXEC_IF(ptr) \
+ do { \
+ if (SLJIT_UNLIKELY(!(ptr))) { \
+ compiler->error = SLJIT_ERR_EX_ALLOC_FAILED; \
+ return NULL; \
+ } \
+ } while (0)
+
+#if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#define SSIZE_OF(type) ((sljit_s32)sizeof(sljit_ ## type))
+
+#define VARIABLE_FLAG_SHIFT (10)
+#define VARIABLE_FLAG_MASK (0x3f << VARIABLE_FLAG_SHIFT)
+#define GET_FLAG_TYPE(op) ((op) >> VARIABLE_FLAG_SHIFT)
+
+#define GET_OPCODE(op) \
+ ((op) & ~(SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK))
+
+#define HAS_FLAGS(op) \
+ ((op) & (SLJIT_SET_Z | VARIABLE_FLAG_MASK))
+
+#define GET_ALL_FLAGS(op) \
+ ((op) & (SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK))
+
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define TYPE_CAST_NEEDED(op) \
+ ((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S32)
+#else /* !SLJIT_64BIT_ARCHITECTURE */
+#define TYPE_CAST_NEEDED(op) \
+ ((op) >= SLJIT_MOV_U8 && (op) <= SLJIT_MOV_S16)
+#endif /* SLJIT_64BIT_ARCHITECTURE */
+
+#define BUF_SIZE 4096
+
+#if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE)
+#define ABUF_SIZE 2048
+#else
+#define ABUF_SIZE 4096
+#endif
+
+/* Parameter parsing. */
+#define REG_MASK 0x3f
+#define OFFS_REG(reg) (((reg) >> 8) & REG_MASK)
+#define OFFS_REG_MASK (REG_MASK << 8)
+#define TO_OFFS_REG(reg) ((reg) << 8)
+/* When reg cannot be unused. */
+#define FAST_IS_REG(reg) ((reg) <= REG_MASK)
+
+/* Mask for argument types. */
+#define SLJIT_ARG_MASK 0x7
+#define SLJIT_ARG_FULL_MASK (SLJIT_ARG_MASK | SLJIT_ARG_TYPE_SCRATCH_REG)
+
+/* Mask for sljit_emit_mem. */
+#define REG_PAIR_MASK 0xff00
+#define REG_PAIR_FIRST(reg) ((reg) & 0xff)
+#define REG_PAIR_SECOND(reg) ((reg) >> 8)
+
+/* Mask for sljit_emit_enter. */
+#define SLJIT_KEPT_SAVEDS_COUNT(options) ((options) & 0x3)
+
+/* Jump flags. */
+#define JUMP_LABEL 0x1
+#define JUMP_ADDR 0x2
+/* SLJIT_REWRITABLE_JUMP is 0x1000. */
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+# define PATCH_MB 0x4
+# define PATCH_MW 0x8
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+# define PATCH_MD 0x10
+#endif
+# define TYPE_SHIFT 13
+#endif /* SLJIT_CONFIG_X86 */
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+# define IS_BL 0x4
+# define PATCH_B 0x8
+#endif /* SLJIT_CONFIG_ARM_V5 || SLJIT_CONFIG_ARM_V7 */
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+# define CPOOL_SIZE 512
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+# define IS_COND 0x04
+# define IS_BL 0x08
+ /* conditional + imm8 */
+# define PATCH_TYPE1 0x10
+ /* conditional + imm20 */
+# define PATCH_TYPE2 0x20
+ /* IT + imm24 */
+# define PATCH_TYPE3 0x30
+ /* imm11 */
+# define PATCH_TYPE4 0x40
+ /* imm24 */
+# define PATCH_TYPE5 0x50
+ /* BL + imm24 */
+# define PATCH_BL 0x60
+ /* 0xf00 cc code for branches */
+#endif /* SLJIT_CONFIG_ARM_THUMB2 */
+
+#if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+# define IS_COND 0x004
+# define IS_CBZ 0x008
+# define IS_BL 0x010
+# define PATCH_B 0x020
+# define PATCH_COND 0x040
+# define PATCH_ABS48 0x080
+# define PATCH_ABS64 0x100
+#endif /* SLJIT_CONFIG_ARM_64 */
+
+#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+# define IS_COND 0x004
+# define IS_CALL 0x008
+# define PATCH_B 0x010
+# define PATCH_ABS_B 0x020
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+# define PATCH_ABS32 0x040
+# define PATCH_ABS48 0x080
+#endif /* SLJIT_CONFIG_PPC_64 */
+# define REMOVE_COND 0x100
+#endif /* SLJIT_CONFIG_PPC */
+
+#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+# define IS_MOVABLE 0x004
+# define IS_JAL 0x008
+# define IS_CALL 0x010
+# define IS_BIT26_COND 0x020
+# define IS_BIT16_COND 0x040
+# define IS_BIT23_COND 0x080
+
+# define IS_COND (IS_BIT26_COND | IS_BIT16_COND | IS_BIT23_COND)
+
+# define PATCH_B 0x100
+# define PATCH_J 0x200
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+# define PATCH_ABS32 0x400
+# define PATCH_ABS48 0x800
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ /* instruction types */
+# define MOVABLE_INS 0
+ /* 1 - 31 last destination register */
+ /* no destination (i.e: store) */
+# define UNMOVABLE_INS 32
+ /* FPU status register */
+# define FCSR_FCC 33
+#endif /* SLJIT_CONFIG_MIPS */
+
+#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+# define IS_COND 0x004
+# define IS_CALL 0x008
+
+# define PATCH_B 0x010
+# define PATCH_J 0x020
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+# define PATCH_REL32 0x040
+# define PATCH_ABS32 0x080
+# define PATCH_ABS44 0x100
+# define PATCH_ABS52 0x200
+#else /* !SLJIT_CONFIG_RISCV_64 */
+# define PATCH_REL32 0x0
+#endif /* SLJIT_CONFIG_RISCV_64 */
+#endif /* SLJIT_CONFIG_RISCV */
+
+/* Stack management. */
+
+#define GET_SAVED_REGISTERS_SIZE(scratches, saveds, extra) \
+ (((scratches < SLJIT_NUMBER_OF_SCRATCH_REGISTERS ? 0 : (scratches - SLJIT_NUMBER_OF_SCRATCH_REGISTERS)) + \
+ (saveds) + (sljit_s32)(extra)) * (sljit_s32)sizeof(sljit_sw))
+
+#define GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, size) \
+ (((fscratches < SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS ? 0 : (fscratches - SLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS)) + \
+ (fsaveds)) * (sljit_s32)(size))
+
+#define ADJUST_LOCAL_OFFSET(p, i) \
+ if ((p) == (SLJIT_MEM1(SLJIT_SP))) \
+ (i) += SLJIT_LOCALS_OFFSET;
+
+#endif /* !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) */
+
+/* Utils can still be used even if SLJIT_CONFIG_UNSUPPORTED is set. */
+#include "sljitUtils.c"
+
+#if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+
+#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR)
+#include "sljitProtExecAllocator.c"
+#elif (defined SLJIT_WX_EXECUTABLE_ALLOCATOR && SLJIT_WX_EXECUTABLE_ALLOCATOR)
+#include "sljitWXExecAllocator.c"
+#else
+#include "sljitExecAllocator.c"
+#endif
+
+#endif
+
+#if (defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR)
+#define SLJIT_ADD_EXEC_OFFSET(ptr, exec_offset) ((sljit_u8 *)(ptr) + (exec_offset))
+#else
+#define SLJIT_ADD_EXEC_OFFSET(ptr, exec_offset) ((sljit_u8 *)(ptr))
+#endif
+
+#ifndef SLJIT_UPDATE_WX_FLAGS
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec)
+#endif
+
+/* Argument checking features. */
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+
+/* Returns with error when an invalid argument is passed. */
+
+#define CHECK_ARGUMENT(x) \
+ do { \
+ if (SLJIT_UNLIKELY(!(x))) \
+ return 1; \
+ } while (0)
+
+#define CHECK_RETURN_TYPE sljit_s32
+#define CHECK_RETURN_OK return 0
+
+#define CHECK(x) \
+ do { \
+ if (SLJIT_UNLIKELY(x)) { \
+ compiler->error = SLJIT_ERR_BAD_ARGUMENT; \
+ return SLJIT_ERR_BAD_ARGUMENT; \
+ } \
+ } while (0)
+
+#define CHECK_PTR(x) \
+ do { \
+ if (SLJIT_UNLIKELY(x)) { \
+ compiler->error = SLJIT_ERR_BAD_ARGUMENT; \
+ return NULL; \
+ } \
+ } while (0)
+
+#define CHECK_REG_INDEX(x) \
+ do { \
+ if (SLJIT_UNLIKELY(x)) { \
+ return -2; \
+ } \
+ } while (0)
+
+#elif (defined SLJIT_DEBUG && SLJIT_DEBUG)
+
+/* Assertion failure occures if an invalid argument is passed. */
+#undef SLJIT_ARGUMENT_CHECKS
+#define SLJIT_ARGUMENT_CHECKS 1
+
+#define CHECK_ARGUMENT(x) SLJIT_ASSERT(x)
+#define CHECK_RETURN_TYPE void
+#define CHECK_RETURN_OK return
+#define CHECK(x) x
+#define CHECK_PTR(x) x
+#define CHECK_REG_INDEX(x) x
+
+#elif (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+
+/* Arguments are not checked. */
+#define CHECK_RETURN_TYPE void
+#define CHECK_RETURN_OK return
+#define CHECK(x) x
+#define CHECK_PTR(x) x
+#define CHECK_REG_INDEX(x) x
+
+#else
+
+/* Arguments are not checked. */
+#define CHECK(x)
+#define CHECK_PTR(x)
+#define CHECK_REG_INDEX(x)
+
+#endif /* SLJIT_ARGUMENT_CHECKS */
+
+/* --------------------------------------------------------------------- */
+/* Public functions */
+/* --------------------------------------------------------------------- */
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+#define SLJIT_NEEDS_COMPILER_INIT 1
+static sljit_s32 compiler_initialized = 0;
+/* A thread safe initialization. */
+static void init_compiler(void);
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data, void *exec_allocator_data)
+{
+ struct sljit_compiler *compiler = (struct sljit_compiler*)SLJIT_MALLOC(sizeof(struct sljit_compiler), allocator_data);
+ if (!compiler)
+ return NULL;
+ SLJIT_ZEROMEM(compiler, sizeof(struct sljit_compiler));
+
+ SLJIT_COMPILE_ASSERT(
+ sizeof(sljit_s8) == 1 && sizeof(sljit_u8) == 1
+ && sizeof(sljit_s16) == 2 && sizeof(sljit_u16) == 2
+ && sizeof(sljit_s32) == 4 && sizeof(sljit_u32) == 4
+ && (sizeof(sljit_p) == 4 || sizeof(sljit_p) == 8)
+ && sizeof(sljit_p) <= sizeof(sljit_sw)
+ && (sizeof(sljit_sw) == 4 || sizeof(sljit_sw) == 8)
+ && (sizeof(sljit_uw) == 4 || sizeof(sljit_uw) == 8),
+ invalid_integer_types);
+ SLJIT_COMPILE_ASSERT(SLJIT_REWRITABLE_JUMP != SLJIT_32,
+ rewritable_jump_and_single_op_must_not_be_the_same);
+ SLJIT_COMPILE_ASSERT(!(SLJIT_EQUAL & 0x1) && !(SLJIT_LESS & 0x1) && !(SLJIT_F_EQUAL & 0x1) && !(SLJIT_JUMP & 0x1),
+ conditional_flags_must_be_even_numbers);
+
+ /* Only the non-zero members must be set. */
+ compiler->error = SLJIT_SUCCESS;
+
+ compiler->allocator_data = allocator_data;
+ compiler->exec_allocator_data = exec_allocator_data;
+ compiler->buf = (struct sljit_memory_fragment*)SLJIT_MALLOC(BUF_SIZE, allocator_data);
+ compiler->abuf = (struct sljit_memory_fragment*)SLJIT_MALLOC(ABUF_SIZE, allocator_data);
+
+ if (!compiler->buf || !compiler->abuf) {
+ if (compiler->buf)
+ SLJIT_FREE(compiler->buf, allocator_data);
+ if (compiler->abuf)
+ SLJIT_FREE(compiler->abuf, allocator_data);
+ SLJIT_FREE(compiler, allocator_data);
+ return NULL;
+ }
+
+ compiler->buf->next = NULL;
+ compiler->buf->used_size = 0;
+ compiler->abuf->next = NULL;
+ compiler->abuf->used_size = 0;
+
+ compiler->scratches = -1;
+ compiler->saveds = -1;
+ compiler->fscratches = -1;
+ compiler->fsaveds = -1;
+ compiler->local_size = -1;
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ compiler->args_size = -1;
+#endif
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ compiler->cpool = (sljit_uw*)SLJIT_MALLOC(CPOOL_SIZE * sizeof(sljit_uw)
+ + CPOOL_SIZE * sizeof(sljit_u8), allocator_data);
+ if (!compiler->cpool) {
+ SLJIT_FREE(compiler->buf, allocator_data);
+ SLJIT_FREE(compiler->abuf, allocator_data);
+ SLJIT_FREE(compiler, allocator_data);
+ return NULL;
+ }
+ compiler->cpool_unique = (sljit_u8*)(compiler->cpool + CPOOL_SIZE);
+ compiler->cpool_diff = 0xffffffff;
+#endif
+
+#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+ compiler->delay_slot = UNMOVABLE_INS;
+#endif
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
+ || (defined SLJIT_DEBUG && SLJIT_DEBUG)
+ compiler->last_flags = 0;
+ compiler->last_return = -1;
+ compiler->logical_local_size = 0;
+#endif
+
+#if (defined SLJIT_NEEDS_COMPILER_INIT && SLJIT_NEEDS_COMPILER_INIT)
+ if (!compiler_initialized) {
+ init_compiler();
+ compiler_initialized = 1;
+ }
+#endif
+
+ return compiler;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ struct sljit_memory_fragment *curr;
+ void *allocator_data = compiler->allocator_data;
+ SLJIT_UNUSED_ARG(allocator_data);
+
+ buf = compiler->buf;
+ while (buf) {
+ curr = buf;
+ buf = buf->next;
+ SLJIT_FREE(curr, allocator_data);
+ }
+
+ buf = compiler->abuf;
+ while (buf) {
+ curr = buf;
+ buf = buf->next;
+ SLJIT_FREE(curr, allocator_data);
+ }
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ SLJIT_FREE(compiler->cpool, allocator_data);
+#endif
+ SLJIT_FREE(compiler, allocator_data);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler)
+{
+ if (compiler->error == SLJIT_SUCCESS)
+ compiler->error = SLJIT_ERR_ALLOC_FAILED;
+}
+
+#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+
+ /* Remove thumb mode flag. */
+ SLJIT_FREE_EXEC((void*)((sljit_uw)code & ~(sljit_uw)0x1), exec_allocator_data);
+}
+#elif (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+
+ /* Resolve indirection. */
+ code = (void*)(*(sljit_uw*)code);
+ SLJIT_FREE_EXEC(code, exec_allocator_data);
+}
+#else
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+
+ SLJIT_FREE_EXEC(code, exec_allocator_data);
+}
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sljit_label* label)
+{
+ if (SLJIT_LIKELY(!!jump) && SLJIT_LIKELY(!!label)) {
+ jump->flags &= (sljit_uw)~JUMP_ADDR;
+ jump->flags |= JUMP_LABEL;
+ jump->u.label = label;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target)
+{
+ if (SLJIT_LIKELY(!!jump)) {
+ jump->flags &= (sljit_uw)~JUMP_LABEL;
+ jump->flags |= JUMP_ADDR;
+ jump->u.target = target;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_put_label(struct sljit_put_label *put_label, struct sljit_label *label)
+{
+ if (SLJIT_LIKELY(!!put_label))
+ put_label->label = label;
+}
+
+#define SLJIT_CURRENT_FLAGS_ALL \
+ (SLJIT_CURRENT_FLAGS_32 | SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB | SLJIT_CURRENT_FLAGS_COMPARE)
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, sljit_s32 current_flags)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(current_flags);
+
+#if (defined SLJIT_HAS_STATUS_FLAGS_STATE && SLJIT_HAS_STATUS_FLAGS_STATE)
+ compiler->status_flags_state = current_flags;
+#endif
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_flags = 0;
+ if ((current_flags & ~(VARIABLE_FLAG_MASK | SLJIT_SET_Z | SLJIT_CURRENT_FLAGS_ALL)) == 0) {
+ compiler->last_flags = GET_FLAG_TYPE(current_flags) | (current_flags & (SLJIT_32 | SLJIT_SET_Z));
+ }
+#endif
+}
+
+/* --------------------------------------------------------------------- */
+/* Private functions */
+/* --------------------------------------------------------------------- */
+
+static void* ensure_buf(struct sljit_compiler *compiler, sljit_uw size)
+{
+ sljit_u8 *ret;
+ struct sljit_memory_fragment *new_frag;
+
+ SLJIT_ASSERT(size <= 256);
+ if (compiler->buf->used_size + size <= (BUF_SIZE - (sljit_uw)SLJIT_OFFSETOF(struct sljit_memory_fragment, memory))) {
+ ret = compiler->buf->memory + compiler->buf->used_size;
+ compiler->buf->used_size += size;
+ return ret;
+ }
+ new_frag = (struct sljit_memory_fragment*)SLJIT_MALLOC(BUF_SIZE, compiler->allocator_data);
+ PTR_FAIL_IF_NULL(new_frag);
+ new_frag->next = compiler->buf;
+ compiler->buf = new_frag;
+ new_frag->used_size = size;
+ return new_frag->memory;
+}
+
+static void* ensure_abuf(struct sljit_compiler *compiler, sljit_uw size)
+{
+ sljit_u8 *ret;
+ struct sljit_memory_fragment *new_frag;
+
+ SLJIT_ASSERT(size <= 256);
+ if (compiler->abuf->used_size + size <= (ABUF_SIZE - (sljit_uw)SLJIT_OFFSETOF(struct sljit_memory_fragment, memory))) {
+ ret = compiler->abuf->memory + compiler->abuf->used_size;
+ compiler->abuf->used_size += size;
+ return ret;
+ }
+ new_frag = (struct sljit_memory_fragment*)SLJIT_MALLOC(ABUF_SIZE, compiler->allocator_data);
+ PTR_FAIL_IF_NULL(new_frag);
+ new_frag->next = compiler->abuf;
+ compiler->abuf = new_frag;
+ new_frag->used_size = size;
+ return new_frag->memory;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, sljit_s32 size)
+{
+ CHECK_ERROR_PTR();
+
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+ if (size <= 0 || size > 128)
+ return NULL;
+ size = (size + 7) & ~7;
+#else
+ if (size <= 0 || size > 64)
+ return NULL;
+ size = (size + 3) & ~3;
+#endif
+ return ensure_abuf(compiler, (sljit_uw)size);
+}
+
+static SLJIT_INLINE void reverse_buf(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf = compiler->buf;
+ struct sljit_memory_fragment *prev = NULL;
+ struct sljit_memory_fragment *tmp;
+
+ do {
+ tmp = buf->next;
+ buf->next = prev;
+ prev = buf;
+ buf = tmp;
+ } while (buf != NULL);
+
+ compiler->buf = prev;
+}
+
+/* Only used in RISC architectures where the instruction size is constant */
+#if !(defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ && !(defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+static SLJIT_INLINE sljit_uw compute_next_addr(struct sljit_label *label, struct sljit_jump *jump,
+ struct sljit_const *const_, struct sljit_put_label *put_label)
+{
+ sljit_uw result = ~(sljit_uw)0;
+
+ if (label)
+ result = label->size;
+
+ if (jump && jump->addr < result)
+ result = jump->addr;
+
+ if (const_ && const_->addr < result)
+ result = const_->addr;
+
+ if (put_label && put_label->addr < result)
+ result = put_label->addr;
+
+ return result;
+}
+
+#endif /* !SLJIT_CONFIG_X86 && !SLJIT_CONFIG_S390X */
+
+static SLJIT_INLINE void set_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(args);
+ SLJIT_UNUSED_ARG(local_size);
+
+ compiler->options = options;
+ compiler->scratches = scratches;
+ compiler->saveds = saveds;
+ compiler->fscratches = fscratches;
+ compiler->fsaveds = fsaveds;
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_return = args & SLJIT_ARG_MASK;
+ compiler->logical_local_size = local_size;
+#endif
+}
+
+static SLJIT_INLINE void set_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 args, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(args);
+ SLJIT_UNUSED_ARG(local_size);
+
+ compiler->options = options;
+ compiler->scratches = scratches;
+ compiler->saveds = saveds;
+ compiler->fscratches = fscratches;
+ compiler->fsaveds = fsaveds;
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_return = args & SLJIT_ARG_MASK;
+ compiler->logical_local_size = local_size;
+#endif
+}
+
+static SLJIT_INLINE void set_label(struct sljit_label *label, struct sljit_compiler *compiler)
+{
+ label->next = NULL;
+ label->size = compiler->size;
+ if (compiler->last_label)
+ compiler->last_label->next = label;
+ else
+ compiler->labels = label;
+ compiler->last_label = label;
+}
+
+static SLJIT_INLINE void set_jump(struct sljit_jump *jump, struct sljit_compiler *compiler, sljit_u32 flags)
+{
+ jump->next = NULL;
+ jump->flags = flags;
+ if (compiler->last_jump)
+ compiler->last_jump->next = jump;
+ else
+ compiler->jumps = jump;
+ compiler->last_jump = jump;
+}
+
+static SLJIT_INLINE void set_const(struct sljit_const *const_, struct sljit_compiler *compiler)
+{
+ const_->next = NULL;
+ const_->addr = compiler->size;
+ if (compiler->last_const)
+ compiler->last_const->next = const_;
+ else
+ compiler->consts = const_;
+ compiler->last_const = const_;
+}
+
+static SLJIT_INLINE void set_put_label(struct sljit_put_label *put_label, struct sljit_compiler *compiler, sljit_uw offset)
+{
+ put_label->next = NULL;
+ put_label->label = NULL;
+ put_label->addr = compiler->size - offset;
+ put_label->flags = 0;
+ if (compiler->last_put_label)
+ compiler->last_put_label->next = put_label;
+ else
+ compiler->put_labels = put_label;
+ compiler->last_put_label = put_label;
+}
+
+#define ADDRESSING_DEPENDS_ON(exp, reg) \
+ (((exp) & SLJIT_MEM) && (((exp) & REG_MASK) == reg || OFFS_REG(exp) == reg))
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+
+static sljit_s32 function_check_arguments(sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds, sljit_s32 fscratches)
+{
+ sljit_s32 word_arg_count, scratch_arg_end, saved_arg_count, float_arg_count, curr_type;
+
+ curr_type = (arg_types & SLJIT_ARG_FULL_MASK);
+
+ if (curr_type >= SLJIT_ARG_TYPE_F64) {
+ if (curr_type > SLJIT_ARG_TYPE_F32 || fscratches == 0)
+ return 0;
+ } else if (curr_type >= SLJIT_ARG_TYPE_W) {
+ if (scratches == 0)
+ return 0;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ word_arg_count = 0;
+ scratch_arg_end = 0;
+ saved_arg_count = 0;
+ float_arg_count = 0;
+ while (arg_types != 0) {
+ if (word_arg_count + float_arg_count >= 4)
+ return 0;
+
+ curr_type = (arg_types & SLJIT_ARG_MASK);
+
+ if (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) {
+ if (saveds == -1 || curr_type < SLJIT_ARG_TYPE_W || curr_type > SLJIT_ARG_TYPE_P)
+ return 0;
+
+ word_arg_count++;
+ scratch_arg_end = word_arg_count;
+ } else {
+ if (curr_type < SLJIT_ARG_TYPE_W || curr_type > SLJIT_ARG_TYPE_F32)
+ return 0;
+
+ if (curr_type < SLJIT_ARG_TYPE_F64) {
+ word_arg_count++;
+ saved_arg_count++;
+ } else
+ float_arg_count++;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (saveds == -1)
+ return (word_arg_count <= scratches && float_arg_count <= fscratches);
+
+ return (saved_arg_count <= saveds && scratch_arg_end <= scratches && float_arg_count <= fscratches);
+}
+
+#define FUNCTION_CHECK_IS_REG(r) \
+ (((r) >= SLJIT_R0 && (r) < (SLJIT_R0 + compiler->scratches)) \
+ || ((r) > (SLJIT_S0 - compiler->saveds) && (r) <= SLJIT_S0))
+
+#define FUNCTION_CHECK_IS_FREG(fr) \
+ (((fr) >= SLJIT_FR0 && (fr) < (SLJIT_FR0 + compiler->fscratches)) \
+ || ((fr) > (SLJIT_FS0 - compiler->fsaveds) && (fr) <= SLJIT_FS0))
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+#define CHECK_IF_VIRTUAL_REGISTER(p) ((p) <= SLJIT_S3 && (p) >= SLJIT_S8)
+#else
+#define CHECK_IF_VIRTUAL_REGISTER(p) 0
+#endif
+
+static sljit_s32 function_check_src_mem(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (!(p & SLJIT_MEM))
+ return 0;
+
+ if (p == SLJIT_MEM1(SLJIT_SP))
+ return (i >= 0 && i < compiler->logical_local_size);
+
+ if (!(!(p & REG_MASK) || FUNCTION_CHECK_IS_REG(p & REG_MASK)))
+ return 0;
+
+ if (CHECK_IF_VIRTUAL_REGISTER(p & REG_MASK))
+ return 0;
+
+ if (p & OFFS_REG_MASK) {
+ if (!(p & REG_MASK))
+ return 0;
+
+ if (!(FUNCTION_CHECK_IS_REG(OFFS_REG(p))))
+ return 0;
+
+ if (CHECK_IF_VIRTUAL_REGISTER(OFFS_REG(p)))
+ return 0;
+
+ if ((i & ~0x3) != 0)
+ return 0;
+ }
+
+ return (p & ~(SLJIT_MEM | REG_MASK | OFFS_REG_MASK)) == 0;
+}
+
+#define FUNCTION_CHECK_SRC_MEM(p, i) \
+ CHECK_ARGUMENT(function_check_src_mem(compiler, p, i));
+
+static sljit_s32 function_check_src(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_REG(p))
+ return (i == 0);
+
+ if (p == SLJIT_IMM)
+ return 1;
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_CHECK_SRC(p, i) \
+ CHECK_ARGUMENT(function_check_src(compiler, p, i));
+
+static sljit_s32 function_check_dst(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_REG(p))
+ return (i == 0);
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_CHECK_DST(p, i) \
+ CHECK_ARGUMENT(function_check_dst(compiler, p, i));
+
+static sljit_s32 function_fcheck(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if (compiler->scratches == -1 || compiler->saveds == -1)
+ return 0;
+
+ if (FUNCTION_CHECK_IS_FREG(p))
+ return (i == 0);
+
+ return function_check_src_mem(compiler, p, i);
+}
+
+#define FUNCTION_FCHECK(p, i) \
+ CHECK_ARGUMENT(function_fcheck(compiler, p, i));
+
+#endif /* SLJIT_ARGUMENT_CHECKS */
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose)
+{
+ compiler->verbose = verbose;
+}
+
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#ifdef _WIN64
+#ifdef __GNUC__
+# define SLJIT_PRINT_D "ll"
+#else
+# define SLJIT_PRINT_D "I64"
+#endif
+#else
+# define SLJIT_PRINT_D "l"
+#endif
+#else
+# define SLJIT_PRINT_D ""
+#endif
+
+static void sljit_verbose_reg(struct sljit_compiler *compiler, sljit_s32 r)
+{
+ if (r < (SLJIT_R0 + compiler->scratches))
+ fprintf(compiler->verbose, "r%d", r - SLJIT_R0);
+ else if (r != SLJIT_SP)
+ fprintf(compiler->verbose, "s%d", SLJIT_NUMBER_OF_REGISTERS - r);
+ else
+ fprintf(compiler->verbose, "sp");
+}
+
+static void sljit_verbose_freg(struct sljit_compiler *compiler, sljit_s32 r)
+{
+ if (r < (SLJIT_FR0 + compiler->fscratches))
+ fprintf(compiler->verbose, "fr%d", r - SLJIT_FR0);
+ else
+ fprintf(compiler->verbose, "fs%d", SLJIT_NUMBER_OF_FLOAT_REGISTERS - r);
+}
+
+static void sljit_verbose_param(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if ((p) & SLJIT_IMM)
+ fprintf(compiler->verbose, "#%" SLJIT_PRINT_D "d", (i));
+ else if ((p) & SLJIT_MEM) {
+ if ((p) & REG_MASK) {
+ fputc('[', compiler->verbose);
+ sljit_verbose_reg(compiler, (p) & REG_MASK);
+ if ((p) & OFFS_REG_MASK) {
+ fprintf(compiler->verbose, " + ");
+ sljit_verbose_reg(compiler, OFFS_REG(p));
+ if (i)
+ fprintf(compiler->verbose, " * %d", 1 << (i));
+ }
+ else if (i)
+ fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i));
+ fputc(']', compiler->verbose);
+ }
+ else
+ fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i));
+ } else
+ sljit_verbose_reg(compiler, p);
+}
+
+static void sljit_verbose_fparam(struct sljit_compiler *compiler, sljit_s32 p, sljit_sw i)
+{
+ if ((p) & SLJIT_MEM) {
+ if ((p) & REG_MASK) {
+ fputc('[', compiler->verbose);
+ sljit_verbose_reg(compiler, (p) & REG_MASK);
+ if ((p) & OFFS_REG_MASK) {
+ fprintf(compiler->verbose, " + ");
+ sljit_verbose_reg(compiler, OFFS_REG(p));
+ if (i)
+ fprintf(compiler->verbose, "%d", 1 << (i));
+ }
+ else if (i)
+ fprintf(compiler->verbose, " + %" SLJIT_PRINT_D "d", (i));
+ fputc(']', compiler->verbose);
+ }
+ else
+ fprintf(compiler->verbose, "[#%" SLJIT_PRINT_D "d]", (i));
+ }
+ else
+ sljit_verbose_freg(compiler, p);
+}
+
+static const char* op0_names[] = {
+ "breakpoint", "nop", "lmul.uw", "lmul.sw",
+ "divmod.u", "divmod.s", "div.u", "div.s",
+ "endbr", "skip_frames_before_return"
+};
+
+static const char* op1_names[] = {
+ "", ".u8", ".s8", ".u16",
+ ".s16", ".u32", ".s32", "32",
+ ".p", "not", "clz", "ctz"
+};
+
+static const char* op2_names[] = {
+ "add", "addc", "sub", "subc",
+ "mul", "and", "or", "xor",
+ "shl", "mshl", "lshr", "mlshr",
+ "ashr", "mashr", "rotl", "rotr"
+};
+
+static const char* op_src_names[] = {
+ "fast_return", "skip_frames_before_fast_return",
+ "prefetch_l1", "prefetch_l2",
+ "prefetch_l3", "prefetch_once",
+};
+
+static const char* fop1_names[] = {
+ "mov", "conv", "conv", "conv",
+ "conv", "conv", "cmp", "neg",
+ "abs",
+};
+
+static const char* fop2_names[] = {
+ "add", "sub", "mul", "div"
+};
+
+static const char* jump_names[] = {
+ "equal", "not_equal",
+ "less", "greater_equal",
+ "greater", "less_equal",
+ "sig_less", "sig_greater_equal",
+ "sig_greater", "sig_less_equal",
+ "overflow", "not_overflow",
+ "carry", "",
+ "f_equal", "f_not_equal",
+ "f_less", "f_greater_equal",
+ "f_greater", "f_less_equal",
+ "unordered", "ordered",
+ "ordered_equal", "unordered_or_not_equal",
+ "ordered_less", "unordered_or_greater_equal",
+ "ordered_greater", "unordered_or_less_equal",
+ "unordered_or_equal", "ordered_not_equal",
+ "unordered_or_less", "ordered_greater_equal",
+ "unordered_or_greater", "ordered_less_equal",
+ "jump", "fast_call",
+ "call", "call_reg_arg"
+};
+
+static const char* call_arg_names[] = {
+ "void", "w", "32", "p", "f64", "f32"
+};
+
+#endif /* SLJIT_VERBOSE */
+
+/* --------------------------------------------------------------------- */
+/* Arch dependent */
+/* --------------------------------------------------------------------- */
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
+ || (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+
+#define SLJIT_SKIP_CHECKS(compiler) (compiler)->skip_checks = 1
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_generate_code(struct sljit_compiler *compiler)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ struct sljit_jump *jump;
+#endif
+
+ SLJIT_UNUSED_ARG(compiler);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(compiler->size > 0);
+ jump = compiler->jumps;
+ while (jump) {
+ /* All jumps have target. */
+ CHECK_ARGUMENT(jump->flags & (JUMP_LABEL | JUMP_ADDR));
+ jump = jump->next;
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ if (options & SLJIT_ENTER_REG_ARG) {
+ CHECK_ARGUMENT(!(options & ~(0x3 | SLJIT_ENTER_REG_ARG)));
+ } else {
+ CHECK_ARGUMENT(options == 0);
+ }
+ CHECK_ARGUMENT(SLJIT_KEPT_SAVEDS_COUNT(options) <= 3 && SLJIT_KEPT_SAVEDS_COUNT(options) <= saveds);
+ CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS);
+ CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_SAVED_REGISTERS);
+ CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS);
+ CHECK_ARGUMENT(fscratches >= 0 && fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE);
+ CHECK_ARGUMENT((arg_types & SLJIT_ARG_FULL_MASK) <= SLJIT_ARG_TYPE_F32);
+ CHECK_ARGUMENT(function_check_arguments(arg_types, scratches, (options & SLJIT_ENTER_REG_ARG) ? 0 : saveds, fscratches));
+
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " enter ret[%s", call_arg_names[arg_types & SLJIT_ARG_MASK]);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s%s", call_arg_names[arg_types & SLJIT_ARG_MASK],
+ (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) ? "_r" : "");
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+
+ fprintf(compiler->verbose, "],");
+
+ if (options & SLJIT_ENTER_REG_ARG) {
+ fprintf(compiler->verbose, " enter:reg_arg,");
+
+ if (SLJIT_KEPT_SAVEDS_COUNT(options) > 0)
+ fprintf(compiler->verbose, " keep:%d,", SLJIT_KEPT_SAVEDS_COUNT(options));
+ }
+
+ fprintf(compiler->verbose, "scratches:%d, saveds:%d, fscratches:%d, fsaveds:%d, local_size:%d\n",
+ scratches, saveds, fscratches, fsaveds, local_size);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ if (options & SLJIT_ENTER_REG_ARG) {
+ CHECK_ARGUMENT(!(options & ~(0x3 | SLJIT_ENTER_REG_ARG)));
+ } else {
+ CHECK_ARGUMENT(options == 0);
+ }
+ CHECK_ARGUMENT(SLJIT_KEPT_SAVEDS_COUNT(options) <= 3 && SLJIT_KEPT_SAVEDS_COUNT(options) <= saveds);
+ CHECK_ARGUMENT(scratches >= 0 && scratches <= SLJIT_NUMBER_OF_REGISTERS);
+ CHECK_ARGUMENT(saveds >= 0 && saveds <= SLJIT_NUMBER_OF_SAVED_REGISTERS);
+ CHECK_ARGUMENT(scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS);
+ CHECK_ARGUMENT(fscratches >= 0 && fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(fsaveds >= 0 && fsaveds <= SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+ CHECK_ARGUMENT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE);
+ CHECK_ARGUMENT((arg_types & SLJIT_ARG_FULL_MASK) < SLJIT_ARG_TYPE_F64);
+ CHECK_ARGUMENT(function_check_arguments(arg_types, scratches, (options & SLJIT_ENTER_REG_ARG) ? 0 : saveds, fscratches));
+
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " set_context ret[%s", call_arg_names[arg_types & SLJIT_ARG_MASK]);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s%s", call_arg_names[arg_types & SLJIT_ARG_MASK],
+ (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) ? "_r" : "");
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+
+ fprintf(compiler->verbose, "],");
+
+ if (options & SLJIT_ENTER_REG_ARG) {
+ fprintf(compiler->verbose, " enter:reg_arg,");
+
+ if (SLJIT_KEPT_SAVEDS_COUNT(options) > 0)
+ fprintf(compiler->verbose, " keep:%d,", SLJIT_KEPT_SAVEDS_COUNT(options));
+ }
+
+ fprintf(compiler->verbose, " scratches:%d, saveds:%d, fscratches:%d, fsaveds:%d, local_size:%d\n",
+ scratches, saveds, fscratches, fsaveds, local_size);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(compiler->last_return == SLJIT_ARG_TYPE_VOID);
+#endif
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " return_void\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(compiler->scratches >= 0);
+
+ switch (compiler->last_return) {
+ case SLJIT_ARG_TYPE_W:
+ CHECK_ARGUMENT(op >= SLJIT_MOV && op <= SLJIT_MOV_S32);
+ break;
+ case SLJIT_ARG_TYPE_32:
+ CHECK_ARGUMENT(op == SLJIT_MOV32 || (op >= SLJIT_MOV32_U8 && op <= SLJIT_MOV32_S16));
+ break;
+ case SLJIT_ARG_TYPE_P:
+ CHECK_ARGUMENT(op == SLJIT_MOV_P);
+ break;
+ case SLJIT_ARG_TYPE_F64:
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(op == SLJIT_MOV_F64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(op == SLJIT_MOV_F32);
+ break;
+ default:
+ /* Context not initialized, void, etc. */
+ CHECK_ARGUMENT(0);
+ break;
+ }
+
+ if (GET_OPCODE(op) < SLJIT_MOV_F64) {
+ FUNCTION_CHECK_SRC(src, srcw);
+ } else {
+ FUNCTION_FCHECK(src, srcw);
+ }
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (GET_OPCODE(op) < SLJIT_MOV_F64) {
+ fprintf(compiler->verbose, " return%s%s ", !(op & SLJIT_32) ? "" : "32",
+ op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE]);
+ sljit_verbose_param(compiler, src, srcw);
+ } else {
+ fprintf(compiler->verbose, " return%s ", !(op & SLJIT_32) ? ".f64" : ".f32");
+ sljit_verbose_fparam(compiler, src, srcw);
+ }
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_SRC(src, srcw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " return_to ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_DST(dst, dstw);
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " fast_enter ");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((op >= SLJIT_BREAKPOINT && op <= SLJIT_LMUL_SW)
+ || ((op & ~SLJIT_32) >= SLJIT_DIVMOD_UW && (op & ~SLJIT_32) <= SLJIT_DIV_SW)
+ || (op >= SLJIT_ENDBR && op <= SLJIT_SKIP_FRAMES_BEFORE_RETURN));
+ CHECK_ARGUMENT(GET_OPCODE(op) < SLJIT_LMUL_UW || GET_OPCODE(op) >= SLJIT_ENDBR || compiler->scratches >= 2);
+ if ((GET_OPCODE(op) >= SLJIT_LMUL_UW && GET_OPCODE(op) <= SLJIT_DIV_SW) || op == SLJIT_SKIP_FRAMES_BEFORE_RETURN)
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose))
+ {
+ fprintf(compiler->verbose, " %s", op0_names[GET_OPCODE(op) - SLJIT_OP0_BASE]);
+ if (GET_OPCODE(op) >= SLJIT_DIVMOD_UW && GET_OPCODE(op) <= SLJIT_DIV_SW) {
+ fprintf(compiler->verbose, (op & SLJIT_32) ? "32" : "w");
+ }
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_CTZ);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_NOT:
+ /* Only SLJIT_32 and SLJIT_SET_Z are allowed. */
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+ break;
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_P:
+ /* Nothing allowed */
+ CHECK_ARGUMENT(!(op & (SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ break;
+ default:
+ /* Only SLJIT_32 is allowed. */
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ break;
+ }
+
+ FUNCTION_CHECK_DST(dst, dstw);
+ FUNCTION_CHECK_SRC(src, srcw);
+
+ if (GET_OPCODE(op) >= SLJIT_NOT) {
+ CHECK_ARGUMENT(src != SLJIT_IMM);
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_32 | SLJIT_SET_Z));
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (GET_OPCODE(op) <= SLJIT_MOV_P)
+ {
+ fprintf(compiler->verbose, " mov%s%s ", !(op & SLJIT_32) ? "" : "32",
+ op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE]);
+ }
+ else
+ {
+ fprintf(compiler->verbose, " %s%s%s%s%s ", op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE], !(op & SLJIT_32) ? "" : "32",
+ !(op & SLJIT_SET_Z) ? "" : ".z", !(op & VARIABLE_FLAG_MASK) ? "" : ".",
+ !(op & VARIABLE_FLAG_MASK) ? "" : jump_names[GET_FLAG_TYPE(op)]);
+ }
+
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 unset,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD && GET_OPCODE(op) <= SLJIT_ROTR);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+ break;
+ case SLJIT_MUL:
+ CHECK_ARGUMENT(!(op & SLJIT_SET_Z));
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == SLJIT_OVERFLOW);
+ break;
+ case SLJIT_ADD:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)
+ || GET_FLAG_TYPE(op) == SLJIT_OVERFLOW);
+ break;
+ case SLJIT_SUB:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || (GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_OVERFLOW)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ break;
+ case SLJIT_ADDC:
+ case SLJIT_SUBC:
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK)
+ || GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ CHECK_ARGUMENT((compiler->last_flags & 0xff) == GET_FLAG_TYPE(SLJIT_SET_CARRY));
+ CHECK_ARGUMENT((op & SLJIT_32) == (compiler->last_flags & SLJIT_32));
+ break;
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ if (unset) {
+ CHECK_ARGUMENT(HAS_FLAGS(op));
+ } else {
+ FUNCTION_CHECK_DST(dst, dstw);
+ }
+ FUNCTION_CHECK_SRC(src1, src1w);
+ FUNCTION_CHECK_SRC(src2, src2w);
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_32 | SLJIT_SET_Z));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s%s%s%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_32) ? "" : "32",
+ !(op & SLJIT_SET_Z) ? "" : ".z", !(op & VARIABLE_FLAG_MASK) ? "" : ".",
+ !(op & VARIABLE_FLAG_MASK) ? "" : jump_names[GET_FLAG_TYPE(op)]);
+ if (unset)
+ fprintf(compiler->verbose, "unset");
+ else
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_LSHR
+ || GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR);
+ CHECK_ARGUMENT((op & ~(0xff | SLJIT_32 | SLJIT_SHIFT_INTO_NON_ZERO)) == 0);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(src_dst));
+ FUNCTION_CHECK_SRC(src1, src1w);
+ FUNCTION_CHECK_SRC(src2, src2w);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s.into%s ", op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE], !(op & SLJIT_32) ? "" : "32",
+ (op & SLJIT_SHIFT_INTO_NON_ZERO) ? ".nz" : "");
+
+ sljit_verbose_reg(compiler, src_dst);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(op >= SLJIT_FAST_RETURN && op <= SLJIT_PREFETCH_ONCE);
+ FUNCTION_CHECK_SRC(src, srcw);
+
+ if (op == SLJIT_FAST_RETURN || op == SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN)
+ {
+ CHECK_ARGUMENT(src != SLJIT_IMM);
+ compiler->last_flags = 0;
+ }
+ else if (op >= SLJIT_PREFETCH_L1 && op <= SLJIT_PREFETCH_ONCE)
+ {
+ CHECK_ARGUMENT(src & SLJIT_MEM);
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s ", op_src_names[op - SLJIT_OP_SRC_BASE]);
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_register_index(sljit_s32 reg)
+{
+ SLJIT_UNUSED_ARG(reg);
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(reg > 0 && reg <= SLJIT_NUMBER_OF_REGISTERS);
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_float_register_index(sljit_s32 reg)
+{
+ SLJIT_UNUSED_ARG(reg);
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(reg > 0 && reg <= SLJIT_NUMBER_OF_FLOAT_REGISTERS);
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ sljit_u32 i;
+#endif
+
+ SLJIT_UNUSED_ARG(compiler);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(instruction);
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+ CHECK_ARGUMENT(size > 0 && size < 16);
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+ CHECK_ARGUMENT((size == 2 && (((sljit_sw)instruction) & 0x1) == 0)
+ || (size == 4 && (((sljit_sw)instruction) & 0x3) == 0));
+#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+ CHECK_ARGUMENT(size == 2 || size == 4 || size == 6);
+#else
+ CHECK_ARGUMENT(size == 4 && (((sljit_sw)instruction) & 0x3) == 0);
+#endif
+
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " op_custom");
+ for (i = 0; i < size; i++)
+ fprintf(compiler->verbose, " 0x%x", ((sljit_u8*)instruction)[i]);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_MOV_F64 && GET_OPCODE(op) <= SLJIT_ABS_F64);
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ FUNCTION_FCHECK(src, srcw);
+ FUNCTION_FCHECK(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
+ fprintf(compiler->verbose, " %s%s ", fop1_names[SLJIT_CONV_F64_FROM_F32 - SLJIT_FOP1_BASE],
+ (op & SLJIT_32) ? ".f32.from.f64" : ".f64.from.f32");
+ else
+ fprintf(compiler->verbose, " %s%s ", fop1_names[GET_OPCODE(op) - SLJIT_FOP1_BASE],
+ (op & SLJIT_32) ? ".f32" : ".f64");
+
+ sljit_verbose_fparam(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & SLJIT_32);
+#endif
+
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) == SLJIT_CMP_F64);
+ CHECK_ARGUMENT(!(op & SLJIT_SET_Z));
+ CHECK_ARGUMENT((op & VARIABLE_FLAG_MASK)
+ || (GET_FLAG_TYPE(op) >= SLJIT_F_EQUAL && GET_FLAG_TYPE(op) <= SLJIT_ORDERED_LESS_EQUAL));
+ FUNCTION_FCHECK(src1, src1w);
+ FUNCTION_FCHECK(src2, src2w);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s", fop1_names[SLJIT_CMP_F64 - SLJIT_FOP1_BASE], (op & SLJIT_32) ? ".f32" : ".f64");
+ if (op & VARIABLE_FLAG_MASK) {
+ fprintf(compiler->verbose, ".%s", jump_names[GET_FLAG_TYPE(op)]);
+ }
+ fprintf(compiler->verbose, " ");
+ sljit_verbose_fparam(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_CONV_SW_FROM_F64 && GET_OPCODE(op) <= SLJIT_CONV_S32_FROM_F64);
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ FUNCTION_FCHECK(src, srcw);
+ FUNCTION_CHECK_DST(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s.from%s ", fop1_names[GET_OPCODE(op) - SLJIT_FOP1_BASE],
+ (GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) ? ".s32" : ".sw",
+ (op & SLJIT_32) ? ".f32" : ".f64");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_CONV_F64_FROM_SW && GET_OPCODE(op) <= SLJIT_CONV_F64_FROM_S32);
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ FUNCTION_CHECK_SRC(src, srcw);
+ FUNCTION_FCHECK(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s.from%s ", fop1_names[GET_OPCODE(op) - SLJIT_FOP1_BASE],
+ (op & SLJIT_32) ? ".f32" : ".f64",
+ (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? ".s32" : ".sw");
+ sljit_verbose_fparam(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(GET_OPCODE(op) >= SLJIT_ADD_F64 && GET_OPCODE(op) <= SLJIT_DIV_F64);
+ CHECK_ARGUMENT(!(op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)));
+ FUNCTION_FCHECK(src1, src1w);
+ FUNCTION_FCHECK(src2, src2w);
+ FUNCTION_FCHECK(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s ", fop2_names[GET_OPCODE(op) - SLJIT_FOP2_BASE], (op & SLJIT_32) ? ".f32" : ".f64");
+ sljit_verbose_fparam(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_label(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ compiler->last_flags = 0;
+#endif
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose))
+ fprintf(compiler->verbose, "label:\n");
+#endif
+ CHECK_RETURN_OK;
+}
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
+#define CHECK_UNORDERED(type, last_flags) \
+ ((((type) & 0xff) == SLJIT_UNORDERED || ((type) & 0xff) == SLJIT_ORDERED) && \
+ ((last_flags) & 0xff) >= SLJIT_UNORDERED && ((last_flags) & 0xff) <= SLJIT_ORDERED_LESS_EQUAL)
+#else
+#define CHECK_UNORDERED(type, last_flags) 0
+#endif
+#endif /* SLJIT_ARGUMENT_CHECKS */
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_FAST_CALL);
+
+ if ((type & 0xff) < SLJIT_JUMP) {
+ if ((type & 0xff) <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else if ((compiler->last_flags & 0xff) == SLJIT_CARRY) {
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_CARRY || (type & 0xff) == SLJIT_NOT_CARRY);
+ compiler->last_flags = 0;
+ } else
+ CHECK_ARGUMENT((type & 0xff) == (compiler->last_flags & 0xff)
+ || ((type & 0xff) == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || CHECK_UNORDERED(type, compiler->last_flags));
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose))
+ fprintf(compiler->verbose, " jump%s %s\n", !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r",
+ jump_names[type & 0xff]);
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_CALL_RETURN)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_CALL && (type & 0xff) <= SLJIT_CALL_REG_ARG);
+ CHECK_ARGUMENT(function_check_arguments(arg_types, compiler->scratches, -1, compiler->fscratches));
+
+ if (type & SLJIT_CALL_RETURN) {
+ CHECK_ARGUMENT((arg_types & SLJIT_ARG_MASK) == compiler->last_return);
+
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL_REG_ARG);
+ } else {
+ CHECK_ARGUMENT((type & 0xff) != SLJIT_CALL_REG_ARG);
+ }
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s%s%s ret[%s", jump_names[type & 0xff],
+ !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r",
+ !(type & SLJIT_CALL_RETURN) ? "" : ".ret",
+ call_arg_names[arg_types & SLJIT_ARG_MASK]);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_ARG_MASK]);
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+ fprintf(compiler->verbose, "]\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_32)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_EQUAL && (type & 0xff) <= SLJIT_SIG_LESS_EQUAL);
+ FUNCTION_CHECK_SRC(src1, src1w);
+ FUNCTION_CHECK_SRC(src2, src2w);
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " cmp%s%s %s, ", (type & SLJIT_32) ? "32" : "",
+ !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", jump_names[type & 0xff]);
+ sljit_verbose_param(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(sljit_has_cpu_feature(SLJIT_HAS_FPU));
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP | SLJIT_32)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_F_EQUAL && (type & 0xff) <= SLJIT_ORDERED_LESS_EQUAL
+ && ((type & 0xff) <= SLJIT_ORDERED || sljit_cmp_info(type & 0xff)));
+ FUNCTION_FCHECK(src1, src1w);
+ FUNCTION_FCHECK(src2, src2w);
+ compiler->last_flags = 0;
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " fcmp%s%s %s, ", (type & SLJIT_32) ? ".f32" : ".f64",
+ !(type & SLJIT_REWRITABLE_JUMP) ? "" : ".r", jump_names[type & 0xff]);
+ sljit_verbose_fparam(compiler, src1, src1w);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_fparam(compiler, src2, src2w);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(type >= SLJIT_JUMP && type <= SLJIT_FAST_CALL);
+ FUNCTION_CHECK_SRC(src, srcw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " ijump.%s ", jump_names[type]);
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_CALL_RETURN)));
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_CALL && (type & 0xff) <= SLJIT_CALL_REG_ARG);
+ CHECK_ARGUMENT(function_check_arguments(arg_types, compiler->scratches, -1, compiler->fscratches));
+ FUNCTION_CHECK_SRC(src, srcw);
+
+ if (type & SLJIT_CALL_RETURN) {
+ CHECK_ARGUMENT((arg_types & SLJIT_ARG_MASK) == compiler->last_return);
+
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_CALL_REG_ARG);
+ } else {
+ CHECK_ARGUMENT((type & 0xff) != SLJIT_CALL_REG_ARG);
+ }
+ }
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " i%s%s ret[%s", jump_names[type & 0xff],
+ !(type & SLJIT_CALL_RETURN) ? "" : ".ret",
+ call_arg_names[arg_types & SLJIT_ARG_MASK]);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types) {
+ fprintf(compiler->verbose, "], args[");
+ do {
+ fprintf(compiler->verbose, "%s", call_arg_names[arg_types & SLJIT_ARG_MASK]);
+ arg_types >>= SLJIT_ARG_SHIFT;
+ if (arg_types)
+ fprintf(compiler->verbose, ",");
+ } while (arg_types);
+ }
+ fprintf(compiler->verbose, "], ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT(type >= SLJIT_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL);
+ CHECK_ARGUMENT(op == SLJIT_MOV || op == SLJIT_MOV32
+ || (GET_OPCODE(op) >= SLJIT_AND && GET_OPCODE(op) <= SLJIT_XOR));
+ CHECK_ARGUMENT(!(op & VARIABLE_FLAG_MASK));
+
+ if (type <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else
+ CHECK_ARGUMENT(type == (compiler->last_flags & 0xff)
+ || (type == SLJIT_NOT_CARRY && (compiler->last_flags & 0xff) == SLJIT_CARRY)
+ || (type == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || CHECK_UNORDERED(type, compiler->last_flags));
+
+ FUNCTION_CHECK_DST(dst, dstw);
+
+ if (GET_OPCODE(op) >= SLJIT_ADD)
+ compiler->last_flags = GET_FLAG_TYPE(op) | (op & (SLJIT_32 | SLJIT_SET_Z));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " flags.%s%s%s ",
+ GET_OPCODE(op) < SLJIT_OP2_BASE ? "mov" : op2_names[GET_OPCODE(op) - SLJIT_OP2_BASE],
+ GET_OPCODE(op) < SLJIT_OP2_BASE ? op1_names[GET_OPCODE(op) - SLJIT_OP1_BASE] : ((op & SLJIT_32) ? "32" : ""),
+ !(op & SLJIT_SET_Z) ? "" : ".z");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", %s\n", jump_names[type]);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ sljit_s32 cond = type & ~SLJIT_32;
+
+ CHECK_ARGUMENT(cond >= SLJIT_EQUAL && cond <= SLJIT_ORDERED_LESS_EQUAL);
+
+ CHECK_ARGUMENT(compiler->scratches != -1 && compiler->saveds != -1);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(dst_reg));
+ if (src != SLJIT_IMM) {
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(src));
+ CHECK_ARGUMENT(srcw == 0);
+ }
+
+ if (cond <= SLJIT_NOT_ZERO)
+ CHECK_ARGUMENT(compiler->last_flags & SLJIT_SET_Z);
+ else
+ CHECK_ARGUMENT(cond == (compiler->last_flags & 0xff)
+ || (cond == SLJIT_NOT_CARRY && (compiler->last_flags & 0xff) == SLJIT_CARRY)
+ || (cond == SLJIT_NOT_OVERFLOW && (compiler->last_flags & 0xff) == SLJIT_OVERFLOW)
+ || CHECK_UNORDERED(cond, compiler->last_flags));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " cmov%s %s, ",
+ !(type & SLJIT_32) ? "" : "32",
+ jump_names[type & ~SLJIT_32]);
+ sljit_verbose_reg(compiler, dst_reg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, src, srcw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ sljit_s32 allowed_flags;
+
+ if (type & SLJIT_MEM_UNALIGNED) {
+ CHECK_ARGUMENT(!(type & (SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)));
+ } else if (type & SLJIT_MEM_UNALIGNED_16) {
+ CHECK_ARGUMENT(!(type & SLJIT_MEM_UNALIGNED_32));
+ } else {
+ CHECK_ARGUMENT((reg & REG_PAIR_MASK) || (type & SLJIT_MEM_UNALIGNED_32));
+ }
+
+ allowed_flags = SLJIT_MEM_UNALIGNED;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ allowed_flags = SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16;
+ break;
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ allowed_flags = SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32;
+ break;
+ }
+
+ CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | allowed_flags)) == 0);
+
+ if (reg & REG_PAIR_MASK) {
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(REG_PAIR_FIRST(reg)));
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(REG_PAIR_SECOND(reg)));
+ CHECK_ARGUMENT(REG_PAIR_FIRST(reg) != REG_PAIR_SECOND(reg));
+ } else {
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P);
+ CHECK_ARGUMENT(!(type & SLJIT_32) || ((type & 0xff) >= SLJIT_MOV_U8 && (type & 0xff) <= SLJIT_MOV_S16));
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_REG(reg));
+ }
+
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if ((type & 0xff) == SLJIT_MOV32)
+ fprintf(compiler->verbose, " %s32",
+ (type & SLJIT_MEM_STORE) ? "store" : "load");
+ else
+ fprintf(compiler->verbose, " %s%s%s",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ !(type & SLJIT_32) ? "" : "32",
+ op1_names[(type & 0xff) - SLJIT_OP1_BASE]);
+
+ if (type & SLJIT_MEM_UNALIGNED)
+ printf(".un");
+ else if (type & SLJIT_MEM_UNALIGNED_16)
+ printf(".un16");
+ else if (type & SLJIT_MEM_UNALIGNED_32)
+ printf(".un32");
+
+ if (reg & REG_PAIR_MASK) {
+ fprintf(compiler->verbose, " {");
+ sljit_verbose_reg(compiler, REG_PAIR_FIRST(reg));
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_reg(compiler, REG_PAIR_SECOND(reg));
+ fprintf(compiler->verbose, "}, ");
+ } else {
+ fprintf(compiler->verbose, " ");
+ sljit_verbose_reg(compiler, reg);
+ fprintf(compiler->verbose, ", ");
+ }
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ if (SLJIT_UNLIKELY(compiler->skip_checks)) {
+ compiler->skip_checks = 0;
+ CHECK_RETURN_OK;
+ }
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((type & 0xff) >= SLJIT_MOV && (type & 0xff) <= SLJIT_MOV_P);
+ CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_POST)) == 0);
+ CHECK_ARGUMENT((mem & REG_MASK) != 0 && (mem & REG_MASK) != reg);
+
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (type & SLJIT_MEM_SUPP)
+ CHECK_RETURN_OK;
+ if (sljit_emit_mem_update(compiler, type | SLJIT_MEM_SUPP, reg, mem, memw) == SLJIT_ERR_UNSUPPORTED) {
+ fprintf(compiler->verbose, " # mem: unsupported form, no instructions are emitted\n");
+ CHECK_RETURN_OK;
+ }
+
+ if ((type & 0xff) == SLJIT_MOV32)
+ fprintf(compiler->verbose, " %s32.%s ",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ (type & SLJIT_MEM_POST) ? "post" : "pre");
+ else
+ fprintf(compiler->verbose, " %s%s%s.%s ",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ !(type & SLJIT_32) ? "" : "32",
+ op1_names[(type & 0xff) - SLJIT_OP1_BASE],
+ (type & SLJIT_MEM_POST) ? "post" : "pre");
+
+ sljit_verbose_reg(compiler, reg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV_F64);
+
+ if (type & SLJIT_MEM_UNALIGNED) {
+ CHECK_ARGUMENT(!(type & (SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)));
+ } else if (type & SLJIT_MEM_UNALIGNED_16) {
+ CHECK_ARGUMENT(!(type & SLJIT_MEM_UNALIGNED_32));
+ } else {
+ CHECK_ARGUMENT(type & SLJIT_MEM_UNALIGNED_32);
+ CHECK_ARGUMENT(!(type & SLJIT_32));
+ }
+
+ CHECK_ARGUMENT(!(type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)));
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_FREG(freg));
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " %s.%s",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ !(type & SLJIT_32) ? "f64" : "f32");
+
+ if (type & SLJIT_MEM_UNALIGNED)
+ printf(".un");
+ else if (type & SLJIT_MEM_UNALIGNED_16)
+ printf(".un16");
+ else if (type & SLJIT_MEM_UNALIGNED_32)
+ printf(".un32");
+
+ fprintf(compiler->verbose, " ");
+ sljit_verbose_freg(compiler, freg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ CHECK_ARGUMENT((type & 0xff) == SLJIT_MOV_F64);
+ CHECK_ARGUMENT((type & ~(0xff | SLJIT_32 | SLJIT_MEM_STORE | SLJIT_MEM_SUPP | SLJIT_MEM_POST)) == 0);
+ FUNCTION_CHECK_SRC_MEM(mem, memw);
+ CHECK_ARGUMENT(FUNCTION_CHECK_IS_FREG(freg));
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ if (type & SLJIT_MEM_SUPP)
+ CHECK_RETURN_OK;
+ if (sljit_emit_fmem_update(compiler, type | SLJIT_MEM_SUPP, freg, mem, memw) == SLJIT_ERR_UNSUPPORTED) {
+ fprintf(compiler->verbose, " # fmem: unsupported form, no instructions are emitted\n");
+ CHECK_RETURN_OK;
+ }
+
+ fprintf(compiler->verbose, " %s.%s.%s ",
+ (type & SLJIT_MEM_STORE) ? "store" : "load",
+ !(type & SLJIT_32) ? "f64" : "f32",
+ (type & SLJIT_MEM_POST) ? "post" : "pre");
+
+ sljit_verbose_freg(compiler, freg);
+ fprintf(compiler->verbose, ", ");
+ sljit_verbose_param(compiler, mem, memw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ /* Any offset is allowed. */
+ SLJIT_UNUSED_ARG(offset);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_DST(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " local_base ");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", #%" SLJIT_PRINT_D "d\n", offset);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ SLJIT_UNUSED_ARG(init_value);
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_DST(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " const ");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, ", #%" SLJIT_PRINT_D "d\n", init_value);
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+static SLJIT_INLINE CHECK_RETURN_TYPE check_sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS)
+ FUNCTION_CHECK_DST(dst, dstw);
+#endif
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ if (SLJIT_UNLIKELY(!!compiler->verbose)) {
+ fprintf(compiler->verbose, " put_label ");
+ sljit_verbose_param(compiler, dst, dstw);
+ fprintf(compiler->verbose, "\n");
+ }
+#endif
+ CHECK_RETURN_OK;
+}
+
+#else /* !SLJIT_ARGUMENT_CHECKS && !SLJIT_VERBOSE */
+
+#define SLJIT_SKIP_CHECKS(compiler)
+
+#endif /* SLJIT_ARGUMENT_CHECKS || SLJIT_VERBOSE */
+
+#define SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw) \
+ SLJIT_COMPILE_ASSERT(!(SLJIT_CONV_SW_FROM_F64 & 0x1) && !(SLJIT_CONV_F64_FROM_SW & 0x1), \
+ invalid_float_opcodes); \
+ if (GET_OPCODE(op) >= SLJIT_CONV_SW_FROM_F64 && GET_OPCODE(op) <= SLJIT_CMP_F64) { \
+ if (GET_OPCODE(op) == SLJIT_CMP_F64) { \
+ CHECK(check_sljit_emit_fop1_cmp(compiler, op, dst, dstw, src, srcw)); \
+ ADJUST_LOCAL_OFFSET(dst, dstw); \
+ ADJUST_LOCAL_OFFSET(src, srcw); \
+ return sljit_emit_fop1_cmp(compiler, op, dst, dstw, src, srcw); \
+ } \
+ if ((GET_OPCODE(op) | 0x1) == SLJIT_CONV_S32_FROM_F64) { \
+ CHECK(check_sljit_emit_fop1_conv_sw_from_f64(compiler, op, dst, dstw, src, srcw)); \
+ ADJUST_LOCAL_OFFSET(dst, dstw); \
+ ADJUST_LOCAL_OFFSET(src, srcw); \
+ return sljit_emit_fop1_conv_sw_from_f64(compiler, op, dst, dstw, src, srcw); \
+ } \
+ CHECK(check_sljit_emit_fop1_conv_f64_from_sw(compiler, op, dst, dstw, src, srcw)); \
+ ADJUST_LOCAL_OFFSET(dst, dstw); \
+ ADJUST_LOCAL_OFFSET(src, srcw); \
+ return sljit_emit_fop1_conv_f64_from_sw(compiler, op, dst, dstw, src, srcw); \
+ } \
+ CHECK(check_sljit_emit_fop1(compiler, op, dst, dstw, src, srcw)); \
+ ADJUST_LOCAL_OFFSET(dst, dstw); \
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ || (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC) \
+ || ((defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) && !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6)) \
+ || (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV) \
+ || (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+
+static SLJIT_INLINE sljit_s32 sljit_emit_cmov_generic(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ sljit_s32 op = (type & SLJIT_32) ? SLJIT_MOV32 : SLJIT_MOV;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ jump = sljit_emit_jump(compiler, (type & ~SLJIT_32) ^ 0x1);
+ FAIL_IF(!jump);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_op1(compiler, op, dst_reg, 0, src, srcw));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ label = sljit_emit_label(compiler);
+ FAIL_IF(!label);
+
+ sljit_set_label(jump, label);
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
+#if (!(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) || (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)) \
+ && !(defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+
+static sljit_s32 sljit_emit_mem_unaligned(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (type & SLJIT_MEM_STORE)
+ return sljit_emit_op1(compiler, type & (0xff | SLJIT_32), mem, memw, reg, 0);
+ return sljit_emit_op1(compiler, type & (0xff | SLJIT_32), reg, 0, mem, memw);
+}
+
+#endif /* (!SLJIT_CONFIG_MIPS || SLJIT_MIPS_REV >= 6) && !SLJIT_CONFIG_ARM_V5 */
+
+#if (!(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) || (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)) \
+ && !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32)
+
+static sljit_s32 sljit_emit_fmem_unaligned(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (type & SLJIT_MEM_STORE)
+ return sljit_emit_fop1(compiler, type & (0xff | SLJIT_32), mem, memw, freg, 0);
+ return sljit_emit_fop1(compiler, type & (0xff | SLJIT_32), freg, 0, mem, memw);
+}
+
+#endif /* (!SLJIT_CONFIG_MIPS || SLJIT_MIPS_REV >= 6) && !SLJIT_CONFIG_ARM */
+
+/* CPU description section */
+
+#if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE)
+#define SLJIT_CPUINFO_PART1 " 32bit ("
+#elif (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+#define SLJIT_CPUINFO_PART1 " 64bit ("
+#else
+#error "Internal error: CPU type info missing"
+#endif
+
+#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#define SLJIT_CPUINFO_PART2 "little endian + "
+#elif (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN)
+#define SLJIT_CPUINFO_PART2 "big endian + "
+#else
+#error "Internal error: CPU type info missing"
+#endif
+
+#if (defined SLJIT_UNALIGNED && SLJIT_UNALIGNED)
+#define SLJIT_CPUINFO_PART3 "unaligned)"
+#else
+#define SLJIT_CPUINFO_PART3 "aligned)"
+#endif
+
+#define SLJIT_CPUINFO SLJIT_CPUINFO_PART1 SLJIT_CPUINFO_PART2 SLJIT_CPUINFO_PART3
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+# include "sljitNativeX86_common.c"
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+# include "sljitNativeARM_32.c"
+#elif (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+# include "sljitNativeARM_32.c"
+#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2)
+# include "sljitNativeARM_T2_32.c"
+#elif (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+# include "sljitNativeARM_64.c"
+#elif (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+# include "sljitNativePPC_common.c"
+#elif (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+# include "sljitNativeMIPS_common.c"
+#elif (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+# include "sljitNativeRISCV_common.c"
+#elif (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+# include "sljitNativeS390X.c"
+#endif
+
+static SLJIT_INLINE sljit_s32 emit_mov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE)
+ /* At the moment the pointer size is always equal to sljit_sw. May be changed in the future. */
+ if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_P))
+ return SLJIT_SUCCESS;
+#else
+ if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P))
+ return SLJIT_SUCCESS;
+#endif
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw);
+}
+
+#if !(defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \
+ && !((defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) && defined __SOFTFP__)
+
+static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ if (src == SLJIT_FR0)
+ return SLJIT_SUCCESS;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw);
+}
+
+#endif /* !SLJIT_CONFIG_X86_32 && !(SLJIT_CONFIG_ARM_32 && __SOFTFP__) */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return(compiler, op, src, srcw));
+
+ if (GET_OPCODE(op) < SLJIT_MOV_F64) {
+ FAIL_IF(emit_mov_before_return(compiler, op, src, srcw));
+ } else {
+ FAIL_IF(emit_fmov_before_return(compiler, op, src, srcw));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_return_void(compiler);
+}
+
+#if !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS) \
+ && !(defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* Default compare for most architectures. */
+ sljit_s32 flags, tmp_src, condition;
+ sljit_sw tmp_srcw;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w));
+
+ condition = type & 0xff;
+#if (defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+ if ((condition == SLJIT_EQUAL || condition == SLJIT_NOT_EQUAL)) {
+ if ((src1 & SLJIT_IMM) && !src1w) {
+ src1 = src2;
+ src1w = src2w;
+ src2 = SLJIT_IMM;
+ src2w = 0;
+ }
+ if ((src2 & SLJIT_IMM) && !src2w)
+ return emit_cmp_to0(compiler, type, src1, src1w);
+ }
+#endif
+
+ if (SLJIT_UNLIKELY((src1 & SLJIT_IMM) && !(src2 & SLJIT_IMM))) {
+ /* Immediate is preferred as second argument by most architectures. */
+ switch (condition) {
+ case SLJIT_LESS:
+ condition = SLJIT_GREATER;
+ break;
+ case SLJIT_GREATER_EQUAL:
+ condition = SLJIT_LESS_EQUAL;
+ break;
+ case SLJIT_GREATER:
+ condition = SLJIT_LESS;
+ break;
+ case SLJIT_LESS_EQUAL:
+ condition = SLJIT_GREATER_EQUAL;
+ break;
+ case SLJIT_SIG_LESS:
+ condition = SLJIT_SIG_GREATER;
+ break;
+ case SLJIT_SIG_GREATER_EQUAL:
+ condition = SLJIT_SIG_LESS_EQUAL;
+ break;
+ case SLJIT_SIG_GREATER:
+ condition = SLJIT_SIG_LESS;
+ break;
+ case SLJIT_SIG_LESS_EQUAL:
+ condition = SLJIT_SIG_GREATER_EQUAL;
+ break;
+ }
+
+ type = condition | (type & (SLJIT_32 | SLJIT_REWRITABLE_JUMP));
+ tmp_src = src1;
+ src1 = src2;
+ src2 = tmp_src;
+ tmp_srcw = src1w;
+ src1w = src2w;
+ src2w = tmp_srcw;
+ }
+
+ if (condition <= SLJIT_NOT_ZERO)
+ flags = SLJIT_SET_Z;
+ else
+ flags = condition << VARIABLE_FLAG_SHIFT;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ PTR_FAIL_IF(sljit_emit_op2u(compiler,
+ SLJIT_SUB | flags | (type & SLJIT_32), src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, condition | (type & (SLJIT_REWRITABLE_JUMP | SLJIT_32)));
+}
+
+#endif /* !SLJIT_CONFIG_MIPS */
+
+#if (defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ if (type < SLJIT_UNORDERED || type > SLJIT_ORDERED_LESS_EQUAL)
+ return 0;
+
+ switch (type) {
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ return 0;
+ }
+
+ return 1;
+}
+
+#endif /* SLJIT_CONFIG_ARM */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ sljit_emit_fop1(compiler, SLJIT_CMP_F64 | ((type & 0xff) << VARIABLE_FLAG_SHIFT) | (type & SLJIT_32), src1, src1w, src2, src2w);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+#if !(defined SLJIT_CONFIG_ARM && SLJIT_CONFIG_ARM) \
+ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(reg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+#endif /* !SLJIT_CONFIG_ARM && !SLJIT_CONFIG_PPC */
+
+#if !(defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) \
+ && !(defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ return sljit_emit_fmem_unaligned(compiler, type, freg, mem, memw);
+}
+
+#endif /* !SLJIT_CONFIG_ARM_32 && !SLJIT_CONFIG_MIPS */
+
+#if !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64) \
+ && !(defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw));
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(freg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+#endif /* !SLJIT_CONFIG_ARM_64 && !SLJIT_CONFIG_PPC */
+
+#if !(defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86) \
+ && !(defined SLJIT_CONFIG_ARM_64 && SLJIT_CONFIG_ARM_64)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_get_local_base(compiler, dst, dstw, offset));
+
+ ADJUST_LOCAL_OFFSET(SLJIT_MEM1(SLJIT_SP), offset);
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (offset != 0)
+ return sljit_emit_op2(compiler, SLJIT_ADD, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset);
+ return sljit_emit_op1(compiler, SLJIT_MOV, dst, dstw, SLJIT_SP, 0);
+}
+
+#endif
+
+#else /* SLJIT_CONFIG_UNSUPPORTED */
+
+/* Empty function bodies for those machines, which are not (yet) supported. */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "unsupported";
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(allocator_data);
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, sljit_s32 size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(size);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(verbose);
+ SLJIT_UNREACHABLE();
+}
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ SLJIT_UNUSED_ARG(feature_type);
+ SLJIT_UNREACHABLE();
+ return 0;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNREACHABLE();
+ return 0;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data)
+{
+ SLJIT_UNUSED_ARG(code);
+ SLJIT_UNUSED_ARG(exec_allocator_data);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(options);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNUSED_ARG(scratches);
+ SLJIT_UNUSED_ARG(saveds);
+ SLJIT_UNUSED_ARG(fscratches);
+ SLJIT_UNUSED_ARG(fsaveds);
+ SLJIT_UNUSED_ARG(local_size);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(options);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNUSED_ARG(scratches);
+ SLJIT_UNUSED_ARG(saveds);
+ SLJIT_UNUSED_ARG(fscratches);
+ SLJIT_UNUSED_ARG(fsaveds);
+ SLJIT_UNUSED_ARG(local_size);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(src_dst);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ SLJIT_UNREACHABLE();
+ return reg;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(instruction);
+ SLJIT_UNUSED_ARG(size);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler, sljit_s32 current_flags)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(current_flags);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(src1);
+ SLJIT_UNUSED_ARG(src1w);
+ SLJIT_UNUSED_ARG(src2);
+ SLJIT_UNUSED_ARG(src2w);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sljit_label* label)
+{
+ SLJIT_UNUSED_ARG(jump);
+ SLJIT_UNUSED_ARG(label);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target)
+{
+ SLJIT_UNUSED_ARG(jump);
+ SLJIT_UNUSED_ARG(target);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_put_label(struct sljit_put_label *put_label, struct sljit_label *label)
+{
+ SLJIT_UNUSED_ARG(put_label);
+ SLJIT_UNUSED_ARG(label);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(arg_types);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(op);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(dst_reg);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(reg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 reg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(reg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(freg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 freg, sljit_s32 mem, sljit_sw memw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(type);
+ SLJIT_UNUSED_ARG(freg);
+ SLJIT_UNUSED_ARG(mem);
+ SLJIT_UNUSED_ARG(memw);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(offset);
+ SLJIT_UNREACHABLE();
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw initval)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ SLJIT_UNUSED_ARG(initval);
+ SLJIT_UNREACHABLE();
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(dst);
+ SLJIT_UNUSED_ARG(dstw);
+ return NULL;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ SLJIT_UNUSED_ARG(addr);
+ SLJIT_UNUSED_ARG(new_target);
+ SLJIT_UNUSED_ARG(executable_offset);
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ SLJIT_UNUSED_ARG(addr);
+ SLJIT_UNUSED_ARG(new_constant);
+ SLJIT_UNUSED_ARG(executable_offset);
+ SLJIT_UNREACHABLE();
+}
+
+#endif /* !SLJIT_CONFIG_UNSUPPORTED */
diff --git a/contrib/libs/pcre2/src/sljit/sljitLir.h b/contrib/libs/pcre2/src/sljit/sljitLir.h
new file mode 100644
index 0000000000..a666a2643f
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitLir.h
@@ -0,0 +1,1823 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+#ifndef SLJIT_LIR_H_
+#define SLJIT_LIR_H_
+
+/*
+ ------------------------------------------------------------------------
+ Stack-Less JIT compiler for multiple architectures (x86, ARM, PowerPC)
+ ------------------------------------------------------------------------
+
+ Short description
+ Advantages:
+ - The execution can be continued from any LIR instruction. In other
+ words, it is possible to jump to any label from anywhere, even from
+ a code fragment, which is compiled later, as long as the compiling
+ context is the same. See sljit_emit_enter for more details.
+ - Supports self modifying code: target of any jump and call
+ instructions and some constant values can be dynamically modified
+ during runtime. See SLJIT_REWRITABLE_JUMP.
+ - although it is not suggested to do it frequently
+ - can be used for inline caching: save an important value once
+ in the instruction stream
+ - A fixed stack space can be allocated for local variables
+ - The compiler is thread-safe
+ - The compiler is highly configurable through preprocessor macros.
+ You can disable unneeded features (multithreading in single
+ threaded applications), and you can use your own system functions
+ (including memory allocators). See sljitConfig.h.
+ Disadvantages:
+ - The compiler is more like a platform independent assembler, so
+ there is no built-in variable management. Registers and stack must
+ be managed manually (the name of the compiler refers to this).
+ In practice:
+ - This approach is very effective for interpreters
+ - One of the saved registers typically points to a stack interface
+ - It can jump to any exception handler anytime (even if it belongs
+ to another function)
+ - Hot paths can be modified during runtime reflecting the changes
+ of the fastest execution path of the dynamic language
+ - SLJIT supports complex memory addressing modes
+ - mainly position and context independent code (except some cases)
+
+ For valgrind users:
+ - pass --smc-check=all argument to valgrind, since JIT is a "self-modifying code"
+*/
+
+#if (defined SLJIT_HAVE_CONFIG_PRE && SLJIT_HAVE_CONFIG_PRE)
+#error #include "sljitConfigPre.h"
+#endif /* SLJIT_HAVE_CONFIG_PRE */
+
+#include "sljitConfig.h"
+
+/* The following header file defines useful macros for fine tuning
+SLJIT based code generators. They are listed in the beginning
+of sljitConfigInternal.h */
+
+#include "sljitConfigInternal.h"
+
+#if (defined SLJIT_HAVE_CONFIG_POST && SLJIT_HAVE_CONFIG_POST)
+#error #include "sljitConfigPost.h"
+#endif /* SLJIT_HAVE_CONFIG_POST */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Version numbers. */
+#define SLJIT_MAJOR_VERSION 0
+#define SLJIT_MINOR_VERSION 95
+
+/* --------------------------------------------------------------------- */
+/* Error codes */
+/* --------------------------------------------------------------------- */
+
+/* Indicates no error. */
+#define SLJIT_SUCCESS 0
+/* After the call of sljit_generate_code(), the error code of the compiler
+ is set to this value to avoid further code generation.
+ The complier should be freed after sljit_generate_code(). */
+#define SLJIT_ERR_COMPILED 1
+/* Cannot allocate non-executable memory. */
+#define SLJIT_ERR_ALLOC_FAILED 2
+/* Cannot allocate executable memory.
+ Only sljit_generate_code() returns with this error code. */
+#define SLJIT_ERR_EX_ALLOC_FAILED 3
+/* Return value for SLJIT_CONFIG_UNSUPPORTED placeholder architecture. */
+#define SLJIT_ERR_UNSUPPORTED 4
+/* An ivalid argument is passed to any SLJIT function. */
+#define SLJIT_ERR_BAD_ARGUMENT 5
+
+/* --------------------------------------------------------------------- */
+/* Registers */
+/* --------------------------------------------------------------------- */
+
+/*
+ Scratch (R) registers: registers which may not preserve their values
+ across function calls.
+
+ Saved (S) registers: registers which preserve their values across
+ function calls.
+
+ The scratch and saved register sets overlap. The last scratch register
+ is the first saved register, the one before the last is the second saved
+ register, and so on.
+
+ If an architecture provides two scratch and three saved registers,
+ its scratch and saved register sets are the following:
+
+ R0 | | R0 is always a scratch register
+ R1 | | R1 is always a scratch register
+ [R2] | S2 | R2 and S2 represent the same physical register
+ [R3] | S1 | R3 and S1 represent the same physical register
+ [R4] | S0 | R4 and S0 represent the same physical register
+
+ Note: SLJIT_NUMBER_OF_SCRATCH_REGISTERS would be 2 and
+ SLJIT_NUMBER_OF_SAVED_REGISTERS would be 3 for this architecture.
+
+ Note: On all supported architectures SLJIT_NUMBER_OF_REGISTERS >= 12
+ and SLJIT_NUMBER_OF_SAVED_REGISTERS >= 6. However, 6 registers
+ are virtual on x86-32. See below.
+
+ The purpose of this definition is convenience: saved registers can
+ be used as extra scratch registers. For example four registers can
+ be specified as scratch registers and the fifth one as saved register
+ on the CPU above and any user code which requires four scratch
+ registers can run unmodified. The SLJIT compiler automatically saves
+ the content of the two extra scratch register on the stack. Scratch
+ registers can also be preserved by saving their value on the stack
+ but this needs to be done manually.
+
+ Note: To emphasize that registers assigned to R2-R4 are saved
+ registers, they are enclosed by square brackets.
+
+ Note: sljit_emit_enter and sljit_set_context defines whether a register
+ is S or R register. E.g: when 3 scratches and 1 saved is mapped
+ by sljit_emit_enter, the allowed register set will be: R0-R2 and
+ S0. Although S2 is mapped to the same position as R2, it does not
+ available in the current configuration. Furthermore the S1 register
+ is not available at all.
+*/
+
+/* Scratch registers. */
+#define SLJIT_R0 1
+#define SLJIT_R1 2
+#define SLJIT_R2 3
+/* Note: on x86-32, R3 - R6 (same as S3 - S6) are emulated (they
+ are allocated on the stack). These registers are called virtual
+ and cannot be used for memory addressing (cannot be part of
+ any SLJIT_MEM1, SLJIT_MEM2 construct). There is no such
+ limitation on other CPUs. See sljit_get_register_index(). */
+#define SLJIT_R3 4
+#define SLJIT_R4 5
+#define SLJIT_R5 6
+#define SLJIT_R6 7
+#define SLJIT_R7 8
+#define SLJIT_R8 9
+#define SLJIT_R9 10
+/* All R registers provided by the architecture can be accessed by SLJIT_R(i)
+ The i parameter must be >= 0 and < SLJIT_NUMBER_OF_REGISTERS. */
+#define SLJIT_R(i) (1 + (i))
+
+/* Saved registers. */
+#define SLJIT_S0 (SLJIT_NUMBER_OF_REGISTERS)
+#define SLJIT_S1 (SLJIT_NUMBER_OF_REGISTERS - 1)
+#define SLJIT_S2 (SLJIT_NUMBER_OF_REGISTERS - 2)
+/* Note: on x86-32, S3 - S6 (same as R3 - R6) are emulated (they
+ are allocated on the stack). These registers are called virtual
+ and cannot be used for memory addressing (cannot be part of
+ any SLJIT_MEM1, SLJIT_MEM2 construct). There is no such
+ limitation on other CPUs. See sljit_get_register_index(). */
+#define SLJIT_S3 (SLJIT_NUMBER_OF_REGISTERS - 3)
+#define SLJIT_S4 (SLJIT_NUMBER_OF_REGISTERS - 4)
+#define SLJIT_S5 (SLJIT_NUMBER_OF_REGISTERS - 5)
+#define SLJIT_S6 (SLJIT_NUMBER_OF_REGISTERS - 6)
+#define SLJIT_S7 (SLJIT_NUMBER_OF_REGISTERS - 7)
+#define SLJIT_S8 (SLJIT_NUMBER_OF_REGISTERS - 8)
+#define SLJIT_S9 (SLJIT_NUMBER_OF_REGISTERS - 9)
+/* All S registers provided by the architecture can be accessed by SLJIT_S(i)
+ The i parameter must be >= 0 and < SLJIT_NUMBER_OF_SAVED_REGISTERS. */
+#define SLJIT_S(i) (SLJIT_NUMBER_OF_REGISTERS - (i))
+
+/* Registers >= SLJIT_FIRST_SAVED_REG are saved registers. */
+#define SLJIT_FIRST_SAVED_REG (SLJIT_S0 - SLJIT_NUMBER_OF_SAVED_REGISTERS + 1)
+
+/* The SLJIT_SP provides direct access to the linear stack space allocated by
+ sljit_emit_enter. It can only be used in the following form: SLJIT_MEM1(SLJIT_SP).
+ The immediate offset is extended by the relative stack offset automatically.
+ The sljit_get_local_base can be used to obtain the real address of a value. */
+#define SLJIT_SP (SLJIT_NUMBER_OF_REGISTERS + 1)
+
+/* Return with machine word. */
+
+#define SLJIT_RETURN_REG SLJIT_R0
+
+/* --------------------------------------------------------------------- */
+/* Floating point registers */
+/* --------------------------------------------------------------------- */
+
+/* Each floating point register can store a 32 or a 64 bit precision
+ value. The FR and FS register sets are overlap in the same way as R
+ and S register sets. See above. */
+
+/* Floating point scratch registers. */
+#define SLJIT_FR0 1
+#define SLJIT_FR1 2
+#define SLJIT_FR2 3
+#define SLJIT_FR3 4
+#define SLJIT_FR4 5
+#define SLJIT_FR5 6
+/* All FR registers provided by the architecture can be accessed by SLJIT_FR(i)
+ The i parameter must be >= 0 and < SLJIT_NUMBER_OF_FLOAT_REGISTERS. */
+#define SLJIT_FR(i) (1 + (i))
+
+/* Floating point saved registers. */
+#define SLJIT_FS0 (SLJIT_NUMBER_OF_FLOAT_REGISTERS)
+#define SLJIT_FS1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 1)
+#define SLJIT_FS2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 2)
+#define SLJIT_FS3 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 3)
+#define SLJIT_FS4 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 4)
+#define SLJIT_FS5 (SLJIT_NUMBER_OF_FLOAT_REGISTERS - 5)
+/* All S registers provided by the architecture can be accessed by SLJIT_FS(i)
+ The i parameter must be >= 0 and < SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS. */
+#define SLJIT_FS(i) (SLJIT_NUMBER_OF_FLOAT_REGISTERS - (i))
+
+/* Float registers >= SLJIT_FIRST_SAVED_FLOAT_REG are saved registers. */
+#define SLJIT_FIRST_SAVED_FLOAT_REG (SLJIT_FS0 - SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS + 1)
+
+/* Return with floating point arg. */
+
+#define SLJIT_RETURN_FREG SLJIT_FR0
+
+/* --------------------------------------------------------------------- */
+/* Argument type definitions */
+/* --------------------------------------------------------------------- */
+
+/* The following argument type definitions are used by sljit_emit_enter,
+ sljit_set_context, sljit_emit_call, and sljit_emit_icall functions.
+
+ As for sljit_emit_call and sljit_emit_icall, the first integer argument
+ must be placed into SLJIT_R0, the second one into SLJIT_R1, and so on.
+ Similarly the first floating point argument must be placed into SLJIT_FR0,
+ the second one into SLJIT_FR1, and so on.
+
+ As for sljit_emit_enter, the integer arguments can be stored in scratch
+ or saved registers. The first integer argument without _R postfix is
+ stored in SLJIT_S0, the next one in SLJIT_S1, and so on. The integer
+ arguments with _R postfix are placed into scratch registers. The index
+ of the scratch register is the count of the previous integer arguments
+ starting from SLJIT_R0. The floating point arguments are always placed
+ into SLJIT_FR0, SLJIT_FR1, and so on.
+
+ Note: if a function is called by sljit_emit_call/sljit_emit_icall and
+ an argument is stored in a scratch register by sljit_emit_enter,
+ that argument uses the same scratch register index for both
+ integer and floating point arguments.
+
+ Example function definition:
+ sljit_f32 SLJIT_FUNC example_c_callback(void *arg_a,
+ sljit_f64 arg_b, sljit_u32 arg_c, sljit_f32 arg_d);
+
+ Argument type definition:
+ SLJIT_ARG_RETURN(SLJIT_ARG_TYPE_F32)
+ | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_P, 1) | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_F64, 2)
+ | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_32, 3) | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_F32, 4)
+
+ Short form of argument type definition:
+ SLJIT_ARGS4(32, P, F64, 32, F32)
+
+ Argument passing:
+ arg_a must be placed in SLJIT_R0
+ arg_c must be placed in SLJIT_R1
+ arg_b must be placed in SLJIT_FR0
+ arg_d must be placed in SLJIT_FR1
+
+ Examples for argument processing by sljit_emit_enter:
+ SLJIT_ARGS4(VOID, P, 32_R, F32, W)
+ Arguments are placed into: SLJIT_S0, SLJIT_R1, SLJIT_FR0, SLJIT_S1
+
+ SLJIT_ARGS4(VOID, W, W_R, W, W_R)
+ Arguments are placed into: SLJIT_S0, SLJIT_R1, SLJIT_S1, SLJIT_R3
+
+ SLJIT_ARGS4(VOID, F64, W, F32, W_R)
+ Arguments are placed into: SLJIT_FR0, SLJIT_S0, SLJIT_FR1, SLJIT_R1
+
+ Note: it is recommended to pass the scratch arguments first
+ followed by the saved arguments:
+
+ SLJIT_ARGS4(VOID, W_R, W_R, W, W)
+ Arguments are placed into: SLJIT_R0, SLJIT_R1, SLJIT_S0, SLJIT_S1
+*/
+
+/* The following flag is only allowed for the integer arguments of
+ sljit_emit_enter. When the flag is set, the integer argument is
+ stored in a scratch register instead of a saved register. */
+#define SLJIT_ARG_TYPE_SCRATCH_REG 0x8
+
+/* Void result, can only be used by SLJIT_ARG_RETURN. */
+#define SLJIT_ARG_TYPE_VOID 0
+/* Machine word sized integer argument or result. */
+#define SLJIT_ARG_TYPE_W 1
+#define SLJIT_ARG_TYPE_W_R (SLJIT_ARG_TYPE_W | SLJIT_ARG_TYPE_SCRATCH_REG)
+/* 32 bit integer argument or result. */
+#define SLJIT_ARG_TYPE_32 2
+#define SLJIT_ARG_TYPE_32_R (SLJIT_ARG_TYPE_32 | SLJIT_ARG_TYPE_SCRATCH_REG)
+/* Pointer sized integer argument or result. */
+#define SLJIT_ARG_TYPE_P 3
+#define SLJIT_ARG_TYPE_P_R (SLJIT_ARG_TYPE_P | SLJIT_ARG_TYPE_SCRATCH_REG)
+/* 64 bit floating point argument or result. */
+#define SLJIT_ARG_TYPE_F64 4
+/* 32 bit floating point argument or result. */
+#define SLJIT_ARG_TYPE_F32 5
+
+#define SLJIT_ARG_SHIFT 4
+#define SLJIT_ARG_RETURN(type) (type)
+#define SLJIT_ARG_VALUE(type, idx) ((type) << ((idx) * SLJIT_ARG_SHIFT))
+
+/* Simplified argument list definitions.
+
+ The following definition:
+ SLJIT_ARG_RETURN(SLJIT_ARG_TYPE_W) | SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_F32, 1)
+
+ can be shortened to:
+ SLJIT_ARGS1(W, F32)
+*/
+
+#define SLJIT_ARG_TO_TYPE(type) SLJIT_ARG_TYPE_ ## type
+
+#define SLJIT_ARGS0(ret) \
+ SLJIT_ARG_RETURN(SLJIT_ARG_TO_TYPE(ret))
+
+#define SLJIT_ARGS1(ret, arg1) \
+ (SLJIT_ARGS0(ret) | SLJIT_ARG_VALUE(SLJIT_ARG_TO_TYPE(arg1), 1))
+
+#define SLJIT_ARGS2(ret, arg1, arg2) \
+ (SLJIT_ARGS1(ret, arg1) | SLJIT_ARG_VALUE(SLJIT_ARG_TO_TYPE(arg2), 2))
+
+#define SLJIT_ARGS3(ret, arg1, arg2, arg3) \
+ (SLJIT_ARGS2(ret, arg1, arg2) | SLJIT_ARG_VALUE(SLJIT_ARG_TO_TYPE(arg3), 3))
+
+#define SLJIT_ARGS4(ret, arg1, arg2, arg3, arg4) \
+ (SLJIT_ARGS3(ret, arg1, arg2, arg3) | SLJIT_ARG_VALUE(SLJIT_ARG_TO_TYPE(arg4), 4))
+
+/* --------------------------------------------------------------------- */
+/* Main structures and functions */
+/* --------------------------------------------------------------------- */
+
+/*
+ The following structures are private, and can be changed in the
+ future. Keeping them here allows code inlining.
+*/
+
+struct sljit_memory_fragment {
+ struct sljit_memory_fragment *next;
+ sljit_uw used_size;
+ /* Must be aligned to sljit_sw. */
+ sljit_u8 memory[1];
+};
+
+struct sljit_label {
+ struct sljit_label *next;
+ sljit_uw addr;
+ /* The maximum size difference. */
+ sljit_uw size;
+};
+
+struct sljit_jump {
+ struct sljit_jump *next;
+ sljit_uw addr;
+ /* Architecture dependent flags. */
+ sljit_uw flags;
+ union {
+ sljit_uw target;
+ struct sljit_label *label;
+ } u;
+};
+
+struct sljit_put_label {
+ struct sljit_put_label *next;
+ struct sljit_label *label;
+ sljit_uw addr;
+ sljit_uw flags;
+};
+
+struct sljit_const {
+ struct sljit_const *next;
+ sljit_uw addr;
+};
+
+struct sljit_compiler {
+ sljit_s32 error;
+ sljit_s32 options;
+
+ struct sljit_label *labels;
+ struct sljit_jump *jumps;
+ struct sljit_put_label *put_labels;
+ struct sljit_const *consts;
+ struct sljit_label *last_label;
+ struct sljit_jump *last_jump;
+ struct sljit_const *last_const;
+ struct sljit_put_label *last_put_label;
+
+ void *allocator_data;
+ void *exec_allocator_data;
+ struct sljit_memory_fragment *buf;
+ struct sljit_memory_fragment *abuf;
+
+ /* Available scratch registers. */
+ sljit_s32 scratches;
+ /* Available saved registers. */
+ sljit_s32 saveds;
+ /* Available float scratch registers. */
+ sljit_s32 fscratches;
+ /* Available float saved registers. */
+ sljit_s32 fsaveds;
+ /* Local stack size. */
+ sljit_s32 local_size;
+ /* Maximum code size. */
+ sljit_uw size;
+ /* Relative offset of the executable mapping from the writable mapping. */
+ sljit_sw executable_offset;
+ /* Executable size for statistical purposes. */
+ sljit_uw executable_size;
+
+#if (defined SLJIT_HAS_STATUS_FLAGS_STATE && SLJIT_HAS_STATUS_FLAGS_STATE)
+ sljit_s32 status_flags_state;
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_s32 args_size;
+#endif
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 mode32;
+#endif
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ /* Constant pool handling. */
+ sljit_uw *cpool;
+ sljit_u8 *cpool_unique;
+ sljit_uw cpool_diff;
+ sljit_uw cpool_fill;
+ /* Other members. */
+ /* Contains pointer, "ldr pc, [...]" pairs. */
+ sljit_uw patches;
+#endif
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ /* Temporary fields. */
+ sljit_uw shift_imm;
+#endif /* SLJIT_CONFIG_ARM_V5 || SLJIT_CONFIG_ARM_V7 */
+
+#if (defined SLJIT_CONFIG_ARM_32 && SLJIT_CONFIG_ARM_32) && (defined __SOFTFP__)
+ sljit_uw args_size;
+#endif
+
+#if (defined SLJIT_CONFIG_PPC && SLJIT_CONFIG_PPC)
+ sljit_u32 imm;
+#endif
+
+#if (defined SLJIT_CONFIG_MIPS && SLJIT_CONFIG_MIPS)
+ sljit_s32 delay_slot;
+ sljit_s32 cache_arg;
+ sljit_sw cache_argw;
+#endif
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ sljit_uw args_size;
+#endif
+
+#if (defined SLJIT_CONFIG_RISCV && SLJIT_CONFIG_RISCV)
+ sljit_s32 cache_arg;
+ sljit_sw cache_argw;
+#endif
+
+#if (defined SLJIT_CONFIG_S390X && SLJIT_CONFIG_S390X)
+ /* Need to allocate register save area to make calls. */
+ sljit_s32 mode;
+#endif
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ FILE* verbose;
+#endif
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
+ || (defined SLJIT_DEBUG && SLJIT_DEBUG)
+ /* Flags specified by the last arithmetic instruction.
+ It contains the type of the variable flag. */
+ sljit_s32 last_flags;
+ /* Return value type set by entry functions. */
+ sljit_s32 last_return;
+ /* Local size passed to entry functions. */
+ sljit_s32 logical_local_size;
+#endif
+
+#if (defined SLJIT_ARGUMENT_CHECKS && SLJIT_ARGUMENT_CHECKS) \
+ || (defined SLJIT_DEBUG && SLJIT_DEBUG) \
+ || (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+ /* Trust arguments when an API function is called.
+ Used internally for calling API functions. */
+ sljit_s32 skip_checks;
+#endif
+};
+
+/* --------------------------------------------------------------------- */
+/* Main functions */
+/* --------------------------------------------------------------------- */
+
+/* Creates an SLJIT compiler. The allocator_data is required by some
+ custom memory managers. This pointer is passed to SLJIT_MALLOC
+ and SLJIT_FREE macros. Most allocators (including the default
+ one) ignores this value, and it is recommended to pass NULL
+ as a dummy value for allocator_data. The exec_allocator_data
+ has the same purpose but this one is passed to SLJIT_MALLOC_EXEC /
+ SLJIT_MALLOC_FREE functions.
+
+ Returns NULL if failed. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data, void *exec_allocator_data);
+
+/* Frees everything except the compiled machine code. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler);
+
+/* Returns the current error code. If an error occurres, future calls
+ which uses the same compiler argument returns early with the same
+ error code. Thus there is no need for checking the error after every
+ call, it is enough to do it after the code is compiled. Removing
+ these checks increases the performance of the compiling process. */
+static SLJIT_INLINE sljit_s32 sljit_get_compiler_error(struct sljit_compiler *compiler) { return compiler->error; }
+
+/* Sets the compiler error code to SLJIT_ERR_ALLOC_FAILED except
+ if an error was detected before. After the error code is set
+ the compiler behaves as if the allocation failure happened
+ during an SLJIT function call. This can greatly simplify error
+ checking, since it is enough to check the compiler status
+ after the code is compiled. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_compiler_memory_error(struct sljit_compiler *compiler);
+
+/*
+ Allocate a small amount of memory. The size must be <= 64 bytes on 32 bit,
+ and <= 128 bytes on 64 bit architectures. The memory area is owned by the
+ compiler, and freed by sljit_free_compiler. The returned pointer is
+ sizeof(sljit_sw) aligned. Excellent for allocating small blocks during
+ compiling, and no need to worry about freeing them. The size is enough
+ to contain at most 16 pointers. If the size is outside of the range,
+ the function will return with NULL. However, this return value does not
+ indicate that there is no more memory (does not set the current error code
+ of the compiler to out-of-memory status).
+*/
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, sljit_s32 size);
+
+#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE)
+/* Passing NULL disables verbose. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose);
+#endif
+
+/*
+ Create executable code from the instruction stream. This is the final step
+ of the code generation so no more instructions can be emitted after this call.
+*/
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler);
+
+/* Free executable code. */
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data);
+
+/*
+ When the protected executable allocator is used the JIT code is mapped
+ twice. The first mapping has read/write and the second mapping has read/exec
+ permissions. This function returns with the relative offset of the executable
+ mapping using the writable mapping as the base after the machine code is
+ successfully generated. The returned value is always 0 for the normal executable
+ allocator, since it uses only one mapping with read/write/exec permissions.
+ Dynamic code modifications requires this value.
+
+ Before a successful code generation, this function returns with 0.
+*/
+static SLJIT_INLINE sljit_sw sljit_get_executable_offset(struct sljit_compiler *compiler) { return compiler->executable_offset; }
+
+/*
+ The executable memory consumption of the generated code can be retrieved by
+ this function. The returned value can be used for statistical purposes.
+
+ Before a successful code generation, this function returns with 0.
+*/
+static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler *compiler) { return compiler->executable_size; }
+
+/* Returns with non-zero if the feature or limitation type passed as its
+ argument is present on the current CPU. The return value is one, if a
+ feature is fully supported, and it is two, if partially supported.
+
+ Some features (e.g. floating point operations) require hardware (CPU)
+ support while others (e.g. move with update) are emulated if not available.
+ However, even when a feature is emulated, specialized code paths may be
+ faster than the emulation. Some limitations are emulated as well so their
+ general case is supported but it has extra performance costs. */
+
+/* [Not emulated] Floating-point support is available. */
+#define SLJIT_HAS_FPU 0
+/* [Limitation] Some registers are virtual registers. */
+#define SLJIT_HAS_VIRTUAL_REGISTERS 1
+/* [Emulated] Has zero register (setting a memory location to zero is efficient). */
+#define SLJIT_HAS_ZERO_REGISTER 2
+/* [Emulated] Count leading zero is supported. */
+#define SLJIT_HAS_CLZ 3
+/* [Emulated] Count trailing zero is supported. */
+#define SLJIT_HAS_CTZ 4
+/* [Emulated] Rotate left/right is supported. */
+#define SLJIT_HAS_ROT 5
+/* [Emulated] Conditional move is supported. */
+#define SLJIT_HAS_CMOV 6
+/* [Emulated] Prefetch instruction is available (emulated as a nop). */
+#define SLJIT_HAS_PREFETCH 7
+
+#if (defined SLJIT_CONFIG_X86 && SLJIT_CONFIG_X86)
+/* [Not emulated] SSE2 support is available on x86. */
+#define SLJIT_HAS_SSE2 100
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type);
+
+/* If type is between SLJIT_ORDERED_EQUAL and SLJIT_ORDERED_LESS_EQUAL,
+ sljit_cmp_info returns one, if the cpu supports the passed floating
+ point comparison type.
+
+ If type is SLJIT_UNORDERED or SLJIT_ORDERED, sljit_cmp_info returns
+ one, if the cpu supports checking the unordered comparison result
+ regardless of the comparison type passed to the comparison instruction.
+ The returned value is always one, if there is at least one type between
+ SLJIT_ORDERED_EQUAL and SLJIT_ORDERED_LESS_EQUAL where sljit_cmp_info
+ returns with a zero value.
+
+ Otherwise it returns zero. */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type);
+
+/* The following functions generate machine code. If there is no
+ error, they return with SLJIT_SUCCESS, otherwise they return
+ with an error code. */
+
+/*
+ The executable code is a function from the viewpoint of the C
+ language. The function calls must obey to the ABI (Application
+ Binary Interface) of the platform, which specify the purpose of
+ machine registers and stack handling among other things. The
+ sljit_emit_enter function emits the necessary instructions for
+ setting up a new context for the executable code. This is often
+ called as function prologue. Furthermore the options argument
+ can be used to pass configuration options to the compiler. The
+ available options are listed before sljit_emit_enter.
+
+ The function argument list is specified by the SLJIT_ARGSx
+ (SLJIT_ARGS0 .. SLJIT_ARGS4) macros. Currently maximum four
+ arguments are supported. See the description of SLJIT_ARGSx
+ macros about argument passing. Furthermore the register set
+ used by the function must be declared as well. The number of
+ scratch and saved registers available to the function must
+ be passed to sljit_emit_enter. Only R registers between R0
+ and "scratches" argument can be used later. E.g. if "scratches"
+ is set to two, the scratch register set will be limited to
+ SLJIT_R0 and SLJIT_R1. The S registers and the floating point
+ registers ("fscratches" and "fsaveds") are specified in a
+ similar manner. The sljit_emit_enter is also capable of
+ allocating a stack space for local data. The "local_size"
+ argument contains the size in bytes of this local area, and
+ it can be accessed using SLJIT_MEM1(SLJIT_SP). The memory
+ area between SLJIT_SP (inclusive) and SLJIT_SP + local_size
+ (exclusive) can be modified freely until the function returns.
+ The stack space is not initialized to zero.
+
+ Note: the following conditions must met:
+ 0 <= scratches <= SLJIT_NUMBER_OF_REGISTERS
+ 0 <= saveds <= SLJIT_NUMBER_OF_SAVED_REGISTERS
+ scratches + saveds <= SLJIT_NUMBER_OF_REGISTERS
+ 0 <= fscratches <= SLJIT_NUMBER_OF_FLOAT_REGISTERS
+ 0 <= fsaveds <= SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS
+ fscratches + fsaveds <= SLJIT_NUMBER_OF_FLOAT_REGISTERS
+
+ Note: the compiler can use saved registers as scratch registers,
+ but the opposite is not supported
+
+ Note: every call of sljit_emit_enter and sljit_set_context
+ overwrites the previous context.
+*/
+
+/* Saved registers between SLJIT_S0 and SLJIT_S(n - 1) (inclusive)
+ are not saved / restored on function enter / return. Instead,
+ these registers can be used to pass / return data (such as
+ global / local context pointers) across function calls. The
+ value of n must be between 1 and 3. This option is only
+ supported by SLJIT_ENTER_REG_ARG calling convention. */
+#define SLJIT_ENTER_KEEP(n) (n)
+
+/* The compiled function uses an SLJIT specific register argument
+ calling convention. This is a lightweight function call type where
+ both the caller and the called functions must be compiled by
+ SLJIT. The type argument of the call must be SLJIT_CALL_REG_ARG
+ and all arguments must be stored in scratch registers. */
+#define SLJIT_ENTER_REG_ARG 0x00000004
+
+/* The local_size must be >= 0 and <= SLJIT_MAX_LOCAL_SIZE. */
+#define SLJIT_MAX_LOCAL_SIZE 65536
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size);
+
+/* The SLJIT compiler has a current context (which contains the local
+ stack space size, number of used registers, etc.) which is initialized
+ by sljit_emit_enter. Several functions (such as sljit_emit_return)
+ requires this context to be able to generate the appropriate code.
+ However, some code fragments (compiled separately) may have no
+ normal entry point so their context is unknown for the compiler.
+
+ The sljit_set_context and sljit_emit_enter have the same arguments,
+ but sljit_set_context does not generate any machine code.
+
+ Note: every call of sljit_emit_enter and sljit_set_context overwrites
+ the previous context. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size);
+
+/* Return to the caller function. The sljit_emit_return_void function
+ does not return with any value. The sljit_emit_return function returns
+ with a single value loaded from its source operand. The load operation
+ can be between SLJIT_MOV and SLJIT_MOV_P (see sljit_emit_op1) and
+ SLJIT_MOV_F32/SLJIT_MOV_F64 (see sljit_emit_fop1) depending on the
+ return value specified by sljit_emit_enter/sljit_set_context. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler);
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Restores the saved registers and free the stack area, then the execution
+ continues from the address specified by the source operand. This
+ operation is similar to sljit_emit_return, but it ignores the return
+ address. The code where the exection continues should use the same context
+ as the caller function (see sljit_set_context). A word (pointer) value
+ can be passed in the SLJIT_RETURN_REG register. This function can be used
+ to jump to exception handlers. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Generating entry and exit points for fast call functions (see SLJIT_FAST_CALL).
+ Both sljit_emit_fast_enter and SLJIT_FAST_RETURN operations preserve the
+ values of all registers and stack frame. The return address is stored in the
+ dst argument of sljit_emit_fast_enter, and this return address can be passed
+ to SLJIT_FAST_RETURN to continue the execution after the fast call.
+
+ Fast calls are cheap operations (usually only a single call instruction is
+ emitted) but they do not preserve any registers. However the callee function
+ can freely use / update any registers and the local area which can be
+ efficiently exploited by various optimizations. Registers can be saved
+ and restored manually if needed.
+
+ Although returning to different address by SLJIT_FAST_RETURN is possible,
+ this address usually cannot be predicted by the return address predictor of
+ modern CPUs which may reduce performance. Furthermore certain security
+ enhancement technologies such as Intel Control-flow Enforcement Technology
+ (CET) may disallow returning to a different address.
+
+ Flags: - (does not modify flags). */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw);
+
+/*
+ Source and destination operands for arithmetical instructions
+ imm - a simple immediate value (cannot be used as a destination)
+ reg - any of the available registers (immediate argument must be 0)
+ [imm] - absolute memory address
+ [reg+imm] - indirect memory address
+ [reg+(reg<<imm)] - indirect indexed memory address (shift must be between 0 and 3)
+ useful for accessing arrays (fully supported by both x86 and
+ ARM architectures, and cheap operation on others)
+*/
+
+/*
+ IMPORTANT NOTE: memory accesses MUST be naturally aligned unless
+ SLJIT_UNALIGNED macro is defined and its value is 1.
+
+ length | alignment
+ ---------+-----------
+ byte | 1 byte (any physical_address is accepted)
+ half | 2 byte (physical_address & 0x1 == 0)
+ int | 4 byte (physical_address & 0x3 == 0)
+ word | 4 byte if SLJIT_32BIT_ARCHITECTURE is defined and its value is 1
+ | 8 byte if SLJIT_64BIT_ARCHITECTURE is defined and its value is 1
+ pointer | size of sljit_p type (4 byte on 32 bit machines, 4 or 8 byte
+ | on 64 bit machines)
+
+ Note: Different architectures have different addressing limitations.
+ A single instruction is enough for the following addressing
+ modes. Other adrressing modes are emulated by instruction
+ sequences. This information could help to improve those code
+ generators which focuses only a few architectures.
+
+ x86: [reg+imm], -2^32+1 <= imm <= 2^32-1 (full address space on x86-32)
+ [reg+(reg<<imm)] is supported
+ [imm], -2^32+1 <= imm <= 2^32-1 is supported
+ Write-back is not supported
+ arm: [reg+imm], -4095 <= imm <= 4095 or -255 <= imm <= 255 for signed
+ bytes, any halfs or floating point values)
+ [reg+(reg<<imm)] is supported
+ Write-back is supported
+ arm-t2: [reg+imm], -255 <= imm <= 4095
+ [reg+(reg<<imm)] is supported
+ Write back is supported only for [reg+imm], where -255 <= imm <= 255
+ arm64: [reg+imm], -256 <= imm <= 255, 0 <= aligned imm <= 4095 * alignment
+ [reg+(reg<<imm)] is supported
+ Write back is supported only for [reg+imm], where -256 <= imm <= 255
+ ppc: [reg+imm], -65536 <= imm <= 65535. 64 bit loads/stores and 32 bit
+ signed load on 64 bit requires immediates divisible by 4.
+ [reg+imm] is not supported for signed 8 bit values.
+ [reg+reg] is supported
+ Write-back is supported except for one instruction: 32 bit signed
+ load with [reg+imm] addressing mode on 64 bit.
+ mips: [reg+imm], -65536 <= imm <= 65535
+ Write-back is not supported
+ riscv: [reg+imm], -2048 <= imm <= 2047
+ Write-back is not supported
+ s390x: [reg+imm], -2^19 <= imm < 2^19
+ [reg+reg] is supported
+ Write-back is not supported
+*/
+
+/* Macros for specifying operand types. */
+#define SLJIT_MEM 0x80
+#define SLJIT_MEM0() (SLJIT_MEM)
+#define SLJIT_MEM1(r1) (SLJIT_MEM | (r1))
+#define SLJIT_MEM2(r1, r2) (SLJIT_MEM | (r1) | ((r2) << 8))
+#define SLJIT_IMM 0x40
+#define SLJIT_REG_PAIR(r1, r2) ((r1) | ((r2) << 8))
+
+/* Sets 32 bit operation mode on 64 bit CPUs. This option is ignored on
+ 32 bit CPUs. When this option is set for an arithmetic operation, only
+ the lower 32 bits of the input registers are used, and the CPU status
+ flags are set according to the 32 bit result. Although the higher 32 bit
+ of the input and the result registers are not defined by SLJIT, it might
+ be defined by the CPU architecture (e.g. MIPS). To satisfy these CPU
+ requirements all source registers must be the result of those operations
+ where this option was also set. Memory loads read 32 bit values rather
+ than 64 bit ones. In other words 32 bit and 64 bit operations cannot be
+ mixed. The only exception is SLJIT_MOV32 which source register can hold
+ any 32 or 64 bit value, and it is converted to a 32 bit compatible format
+ first. When the source and destination registers are the same, this
+ conversion is free (no instructions are emitted) on most CPUs. A 32 bit
+ value can also be converted to a 64 bit value by SLJIT_MOV_S32
+ (sign extension) or SLJIT_MOV_U32 (zero extension).
+
+ As for floating-point operations, this option sets 32 bit single
+ precision mode. Similar to the integer operations, all register arguments
+ must be the result of those operations where this option was also set.
+
+ Note: memory addressing always uses 64 bit values on 64 bit systems so
+ the result of a 32 bit operation must not be used with SLJIT_MEMx
+ macros.
+
+ This option is part of the instruction name, so there is no need to
+ manually set it. E.g:
+
+ SLJIT_ADD32 == (SLJIT_ADD | SLJIT_32) */
+#define SLJIT_32 0x100
+
+/* Many CPUs (x86, ARM, PPC) have status flag bits which can be set according
+ to the result of an operation. Other CPUs (MIPS) do not have status
+ flag bits, and results must be stored in registers. To cover both
+ architecture types efficiently only two flags are defined by SLJIT:
+
+ * Zero (equal) flag: it is set if the result is zero
+ * Variable flag: its value is defined by the arithmetic operation
+
+ SLJIT instructions can set any or both of these flags. The value of
+ these flags is undefined if the instruction does not specify their
+ value. The description of each instruction contains the list of
+ allowed flag types.
+
+ Note: the logical or operation can be used to set flags.
+
+ Example: SLJIT_ADD can set the Z, OVERFLOW, CARRY flags hence
+
+ sljit_op2(..., SLJIT_ADD, ...)
+ Both the zero and variable flags are undefined so they can
+ have any value after the operation is completed.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...)
+ Sets the zero flag if the result is zero, clears it otherwise.
+ The variable flag is undefined.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_OVERFLOW, ...)
+ Sets the variable flag if an integer overflow occurs, clears
+ it otherwise. The zero flag is undefined.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z | SLJIT_SET_CARRY, ...)
+ Sets the zero flag if the result is zero, clears it otherwise.
+ Sets the variable flag if unsigned overflow (carry) occurs,
+ clears it otherwise.
+
+ Certain instructions (e.g. SLJIT_MOV) does not modify flags, so
+ status flags are unchanged.
+
+ Example:
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...)
+ sljit_op1(..., SLJIT_MOV, ...)
+ Zero flag is set according to the result of SLJIT_ADD.
+
+ sljit_op2(..., SLJIT_ADD | SLJIT_SET_Z, ...)
+ sljit_op2(..., SLJIT_ADD, ...)
+ Zero flag has unknown value.
+
+ These flags can be used for code optimization. E.g. a fast loop can be
+ implemented by decreasing a counter register and set the zero flag
+ using a single instruction. The zero register can be used by a
+ conditional jump to restart the loop. A single comparison can set a
+ zero and less flags to check if a value is less, equal, or greater
+ than another value.
+
+ Motivation: although some CPUs can set a large number of flag bits,
+ usually their values are ignored or only a few of them are used. Emulating
+ a large number of flags on systems without a flag register is complicated
+ so SLJIT instructions must specify the flag they want to use and only
+ that flag is computed. The last arithmetic instruction can be repeated if
+ multiple flags need to be checked.
+*/
+
+/* Set Zero status flag. */
+#define SLJIT_SET_Z 0x0200
+/* Set the variable status flag if condition is true.
+ See comparison types (e.g. SLJIT_SET_LESS, SLJIT_SET_F_EQUAL). */
+#define SLJIT_SET(condition) ((condition) << 10)
+
+/* Starting index of opcodes for sljit_emit_op0. */
+#define SLJIT_OP0_BASE 0
+
+/* Flags: - (does not modify flags)
+ Note: breakpoint instruction is not supported by all architectures (e.g. ppc)
+ It falls back to SLJIT_NOP in those cases. */
+#define SLJIT_BREAKPOINT (SLJIT_OP0_BASE + 0)
+/* Flags: - (does not modify flags)
+ Note: may or may not cause an extra cycle wait
+ it can even decrease the runtime in a few cases. */
+#define SLJIT_NOP (SLJIT_OP0_BASE + 1)
+/* Flags: - (may destroy flags)
+ Unsigned multiplication of SLJIT_R0 and SLJIT_R1.
+ Result is placed into SLJIT_R1:SLJIT_R0 (high:low) word */
+#define SLJIT_LMUL_UW (SLJIT_OP0_BASE + 2)
+/* Flags: - (may destroy flags)
+ Signed multiplication of SLJIT_R0 and SLJIT_R1.
+ Result is placed into SLJIT_R1:SLJIT_R0 (high:low) word */
+#define SLJIT_LMUL_SW (SLJIT_OP0_BASE + 3)
+/* Flags: - (may destroy flags)
+ Unsigned divide of the value in SLJIT_R0 by the value in SLJIT_R1.
+ The result is placed into SLJIT_R0 and the remainder into SLJIT_R1.
+ Note: if SLJIT_R1 is 0, the behaviour is undefined. */
+#define SLJIT_DIVMOD_UW (SLJIT_OP0_BASE + 4)
+#define SLJIT_DIVMOD_U32 (SLJIT_DIVMOD_UW | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Signed divide of the value in SLJIT_R0 by the value in SLJIT_R1.
+ The result is placed into SLJIT_R0 and the remainder into SLJIT_R1.
+ Note: if SLJIT_R1 is 0, the behaviour is undefined.
+ Note: if SLJIT_R1 is -1 and SLJIT_R0 is integer min (0x800..00),
+ the behaviour is undefined. */
+#define SLJIT_DIVMOD_SW (SLJIT_OP0_BASE + 5)
+#define SLJIT_DIVMOD_S32 (SLJIT_DIVMOD_SW | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Unsigned divide of the value in SLJIT_R0 by the value in SLJIT_R1.
+ The result is placed into SLJIT_R0. SLJIT_R1 preserves its value.
+ Note: if SLJIT_R1 is 0, the behaviour is undefined. */
+#define SLJIT_DIV_UW (SLJIT_OP0_BASE + 6)
+#define SLJIT_DIV_U32 (SLJIT_DIV_UW | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Signed divide of the value in SLJIT_R0 by the value in SLJIT_R1.
+ The result is placed into SLJIT_R0. SLJIT_R1 preserves its value.
+ Note: if SLJIT_R1 is 0, the behaviour is undefined.
+ Note: if SLJIT_R1 is -1 and SLJIT_R0 is integer min (0x800..00),
+ the behaviour is undefined. */
+#define SLJIT_DIV_SW (SLJIT_OP0_BASE + 7)
+#define SLJIT_DIV_S32 (SLJIT_DIV_SW | SLJIT_32)
+/* Flags: - (does not modify flags)
+ ENDBR32 instruction for x86-32 and ENDBR64 instruction for x86-64
+ when Intel Control-flow Enforcement Technology (CET) is enabled.
+ No instructions are emitted for other architectures. */
+#define SLJIT_ENDBR (SLJIT_OP0_BASE + 8)
+/* Flags: - (may destroy flags)
+ Skip stack frames before return when Intel Control-flow
+ Enforcement Technology (CET) is enabled. No instructions
+ are emitted for other architectures. */
+#define SLJIT_SKIP_FRAMES_BEFORE_RETURN (SLJIT_OP0_BASE + 9)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op);
+
+/* Starting index of opcodes for sljit_emit_op1. */
+#define SLJIT_OP1_BASE 32
+
+/* The MOV instruction transfers data from source to destination.
+
+ MOV instruction suffixes:
+
+ U8 - unsigned 8 bit data transfer
+ S8 - signed 8 bit data transfer
+ U16 - unsigned 16 bit data transfer
+ S16 - signed 16 bit data transfer
+ U32 - unsigned int (32 bit) data transfer
+ S32 - signed int (32 bit) data transfer
+ P - pointer (sljit_p) data transfer
+*/
+
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV (SLJIT_OP1_BASE + 0)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_U8 (SLJIT_OP1_BASE + 1)
+#define SLJIT_MOV32_U8 (SLJIT_MOV_U8 | SLJIT_32)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_S8 (SLJIT_OP1_BASE + 2)
+#define SLJIT_MOV32_S8 (SLJIT_MOV_S8 | SLJIT_32)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_U16 (SLJIT_OP1_BASE + 3)
+#define SLJIT_MOV32_U16 (SLJIT_MOV_U16 | SLJIT_32)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_S16 (SLJIT_OP1_BASE + 4)
+#define SLJIT_MOV32_S16 (SLJIT_MOV_S16 | SLJIT_32)
+/* Flags: - (does not modify flags)
+ Note: no SLJIT_MOV32_U32 form, since it is the same as SLJIT_MOV32 */
+#define SLJIT_MOV_U32 (SLJIT_OP1_BASE + 5)
+/* Flags: - (does not modify flags)
+ Note: no SLJIT_MOV32_S32 form, since it is the same as SLJIT_MOV32 */
+#define SLJIT_MOV_S32 (SLJIT_OP1_BASE + 6)
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV32 (SLJIT_OP1_BASE + 7)
+/* Flags: - (does not modify flags)
+ Note: loads a pointer sized data, useful on x32 mode (a 64 bit mode
+ on x86-64 which uses 32 bit pointers) or similar compiling modes */
+#define SLJIT_MOV_P (SLJIT_OP1_BASE + 8)
+/* Flags: Z
+ Note: immediate source argument is not supported */
+#define SLJIT_NOT (SLJIT_OP1_BASE + 9)
+#define SLJIT_NOT32 (SLJIT_NOT | SLJIT_32)
+/* Count leading zeroes
+ Flags: - (may destroy flags)
+ Note: immediate source argument is not supported */
+#define SLJIT_CLZ (SLJIT_OP1_BASE + 10)
+#define SLJIT_CLZ32 (SLJIT_CLZ | SLJIT_32)
+/* Count trailing zeroes
+ Flags: - (may destroy flags)
+ Note: immediate source argument is not supported */
+#define SLJIT_CTZ (SLJIT_OP1_BASE + 11)
+#define SLJIT_CTZ32 (SLJIT_CTZ | SLJIT_32)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Starting index of opcodes for sljit_emit_op2. */
+#define SLJIT_OP2_BASE 96
+
+/* Flags: Z | OVERFLOW | CARRY */
+#define SLJIT_ADD (SLJIT_OP2_BASE + 0)
+#define SLJIT_ADD32 (SLJIT_ADD | SLJIT_32)
+/* Flags: CARRY */
+#define SLJIT_ADDC (SLJIT_OP2_BASE + 1)
+#define SLJIT_ADDC32 (SLJIT_ADDC | SLJIT_32)
+/* Flags: Z | LESS | GREATER_EQUAL | GREATER | LESS_EQUAL
+ SIG_LESS | SIG_GREATER_EQUAL | SIG_GREATER
+ SIG_LESS_EQUAL | OVERFLOW | CARRY */
+#define SLJIT_SUB (SLJIT_OP2_BASE + 2)
+#define SLJIT_SUB32 (SLJIT_SUB | SLJIT_32)
+/* Flags: CARRY */
+#define SLJIT_SUBC (SLJIT_OP2_BASE + 3)
+#define SLJIT_SUBC32 (SLJIT_SUBC | SLJIT_32)
+/* Note: integer mul
+ Flags: OVERFLOW */
+#define SLJIT_MUL (SLJIT_OP2_BASE + 4)
+#define SLJIT_MUL32 (SLJIT_MUL | SLJIT_32)
+/* Flags: Z */
+#define SLJIT_AND (SLJIT_OP2_BASE + 5)
+#define SLJIT_AND32 (SLJIT_AND | SLJIT_32)
+/* Flags: Z */
+#define SLJIT_OR (SLJIT_OP2_BASE + 6)
+#define SLJIT_OR32 (SLJIT_OR | SLJIT_32)
+/* Flags: Z */
+#define SLJIT_XOR (SLJIT_OP2_BASE + 7)
+#define SLJIT_XOR32 (SLJIT_XOR | SLJIT_32)
+/* Flags: Z
+ Let bit_length be the length of the shift operation: 32 or 64.
+ If src2 is immediate, src2w is masked by (bit_length - 1).
+ Otherwise, if the content of src2 is outside the range from 0
+ to bit_length - 1, the result is undefined. */
+#define SLJIT_SHL (SLJIT_OP2_BASE + 8)
+#define SLJIT_SHL32 (SLJIT_SHL | SLJIT_32)
+/* Flags: Z
+ Same as SLJIT_SHL, except the the second operand is
+ always masked by the length of the shift operation. */
+#define SLJIT_MSHL (SLJIT_OP2_BASE + 9)
+#define SLJIT_MSHL32 (SLJIT_MSHL | SLJIT_32)
+/* Flags: Z
+ Let bit_length be the length of the shift operation: 32 or 64.
+ If src2 is immediate, src2w is masked by (bit_length - 1).
+ Otherwise, if the content of src2 is outside the range from 0
+ to bit_length - 1, the result is undefined. */
+#define SLJIT_LSHR (SLJIT_OP2_BASE + 10)
+#define SLJIT_LSHR32 (SLJIT_LSHR | SLJIT_32)
+/* Flags: Z
+ Same as SLJIT_LSHR, except the the second operand is
+ always masked by the length of the shift operation. */
+#define SLJIT_MLSHR (SLJIT_OP2_BASE + 11)
+#define SLJIT_MLSHR32 (SLJIT_MLSHR | SLJIT_32)
+/* Flags: Z
+ Let bit_length be the length of the shift operation: 32 or 64.
+ If src2 is immediate, src2w is masked by (bit_length - 1).
+ Otherwise, if the content of src2 is outside the range from 0
+ to bit_length - 1, the result is undefined. */
+#define SLJIT_ASHR (SLJIT_OP2_BASE + 12)
+#define SLJIT_ASHR32 (SLJIT_ASHR | SLJIT_32)
+/* Flags: Z
+ Same as SLJIT_ASHR, except the the second operand is
+ always masked by the length of the shift operation. */
+#define SLJIT_MASHR (SLJIT_OP2_BASE + 13)
+#define SLJIT_MASHR32 (SLJIT_MASHR | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Let bit_length be the length of the rotate operation: 32 or 64.
+ The second operand is always masked by (bit_length - 1). */
+#define SLJIT_ROTL (SLJIT_OP2_BASE + 14)
+#define SLJIT_ROTL32 (SLJIT_ROTL | SLJIT_32)
+/* Flags: - (may destroy flags)
+ Let bit_length be the length of the rotate operation: 32 or 64.
+ The second operand is always masked by (bit_length - 1). */
+#define SLJIT_ROTR (SLJIT_OP2_BASE + 15)
+#define SLJIT_ROTR32 (SLJIT_ROTR | SLJIT_32)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* The sljit_emit_op2u function is the same as sljit_emit_op2
+ except the result is discarded. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Emit a left or right shift operation, where the bits shifted
+ in comes from a separate source operand. All operands are
+ interpreted as unsigned integers.
+
+ In the followings the value_mask variable is 31 for 32 bit
+ operations and word_size - 1 otherwise.
+
+ op must be one of the following operations:
+ SLJIT_SHL or SLJIT_SHL32:
+ src_dst <<= src2
+ src_dst |= ((src1 >> 1) >> (src2 ^ value_mask))
+ SLJIT_MSHL or SLJIT_MSHL32:
+ src2 &= value_mask
+ perform the SLJIT_SHL or SLJIT_SHL32 operation
+ SLJIT_LSHR or SLJIT_LSHR32:
+ src_dst >>= src2
+ src_dst |= ((src1 << 1) << (src2 ^ value_mask))
+ SLJIT_MLSHR or SLJIT_MLSHR32:
+ src2 &= value_mask
+ perform the SLJIT_LSHR or SLJIT_LSHR32 operation
+
+ op can be combined (or'ed) with SLJIT_SHIFT_INTO_NON_ZERO
+
+ src_dst must be a register which content is updated after
+ the operation is completed
+ src1 / src1w contains the bits which shifted into src_dst
+ src2 / src2w contains the shift amount
+
+ Note: a rotate operation can be performed if src_dst and
+ src1 are set to the same register
+
+ Flags: - (may destroy flags) */
+
+/* The src2 contains a non-zero value. Improves the generated
+ code on certain architectures, which provides a small
+ performance improvement. */
+#define SLJIT_SHIFT_INTO_NON_ZERO 0x200
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Starting index of opcodes for sljit_emit_op2. */
+#define SLJIT_OP_SRC_BASE 128
+
+/* Note: src cannot be an immedate value
+ Flags: - (does not modify flags) */
+#define SLJIT_FAST_RETURN (SLJIT_OP_SRC_BASE + 0)
+/* Skip stack frames before fast return.
+ Note: src cannot be an immedate value
+ Flags: may destroy flags. */
+#define SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN (SLJIT_OP_SRC_BASE + 1)
+/* Prefetch value into the level 1 data cache
+ Note: if the target CPU does not support data prefetch,
+ no instructions are emitted.
+ Note: this instruction never fails, even if the memory address is invalid.
+ Flags: - (does not modify flags) */
+#define SLJIT_PREFETCH_L1 (SLJIT_OP_SRC_BASE + 2)
+/* Prefetch value into the level 2 data cache
+ Note: same as SLJIT_PREFETCH_L1 if the target CPU
+ does not support this instruction form.
+ Note: this instruction never fails, even if the memory address is invalid.
+ Flags: - (does not modify flags) */
+#define SLJIT_PREFETCH_L2 (SLJIT_OP_SRC_BASE + 3)
+/* Prefetch value into the level 3 data cache
+ Note: same as SLJIT_PREFETCH_L2 if the target CPU
+ does not support this instruction form.
+ Note: this instruction never fails, even if the memory address is invalid.
+ Flags: - (does not modify flags) */
+#define SLJIT_PREFETCH_L3 (SLJIT_OP_SRC_BASE + 4)
+/* Prefetch a value which is only used once (and can be discarded afterwards)
+ Note: same as SLJIT_PREFETCH_L1 if the target CPU
+ does not support this instruction form.
+ Note: this instruction never fails, even if the memory address is invalid.
+ Flags: - (does not modify flags) */
+#define SLJIT_PREFETCH_ONCE (SLJIT_OP_SRC_BASE + 5)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Starting index of opcodes for sljit_emit_fop1. */
+#define SLJIT_FOP1_BASE 160
+
+/* Flags: - (does not modify flags) */
+#define SLJIT_MOV_F64 (SLJIT_FOP1_BASE + 0)
+#define SLJIT_MOV_F32 (SLJIT_MOV_F64 | SLJIT_32)
+/* Convert opcodes: CONV[DST_TYPE].FROM[SRC_TYPE]
+ SRC/DST TYPE can be: F64, F32, S32, SW
+ Rounding mode when the destination is SW or S32: round towards zero. */
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_F64_FROM_F32 (SLJIT_FOP1_BASE + 1)
+#define SLJIT_CONV_F32_FROM_F64 (SLJIT_CONV_F64_FROM_F32 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_SW_FROM_F64 (SLJIT_FOP1_BASE + 2)
+#define SLJIT_CONV_SW_FROM_F32 (SLJIT_CONV_SW_FROM_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_S32_FROM_F64 (SLJIT_FOP1_BASE + 3)
+#define SLJIT_CONV_S32_FROM_F32 (SLJIT_CONV_S32_FROM_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_F64_FROM_SW (SLJIT_FOP1_BASE + 4)
+#define SLJIT_CONV_F32_FROM_SW (SLJIT_CONV_F64_FROM_SW | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_CONV_F64_FROM_S32 (SLJIT_FOP1_BASE + 5)
+#define SLJIT_CONV_F32_FROM_S32 (SLJIT_CONV_F64_FROM_S32 | SLJIT_32)
+/* Note: dst is the left and src is the right operand for SLJIT_CMP_F32/64.
+ Flags: EQUAL_F | LESS_F | GREATER_EQUAL_F | GREATER_F | LESS_EQUAL_F */
+#define SLJIT_CMP_F64 (SLJIT_FOP1_BASE + 6)
+#define SLJIT_CMP_F32 (SLJIT_CMP_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_NEG_F64 (SLJIT_FOP1_BASE + 7)
+#define SLJIT_NEG_F32 (SLJIT_NEG_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_ABS_F64 (SLJIT_FOP1_BASE + 8)
+#define SLJIT_ABS_F32 (SLJIT_ABS_F64 | SLJIT_32)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw);
+
+/* Starting index of opcodes for sljit_emit_fop2. */
+#define SLJIT_FOP2_BASE 192
+
+/* Flags: - (may destroy flags) */
+#define SLJIT_ADD_F64 (SLJIT_FOP2_BASE + 0)
+#define SLJIT_ADD_F32 (SLJIT_ADD_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_SUB_F64 (SLJIT_FOP2_BASE + 1)
+#define SLJIT_SUB_F32 (SLJIT_SUB_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_MUL_F64 (SLJIT_FOP2_BASE + 2)
+#define SLJIT_MUL_F32 (SLJIT_MUL_F64 | SLJIT_32)
+/* Flags: - (may destroy flags) */
+#define SLJIT_DIV_F64 (SLJIT_FOP2_BASE + 3)
+#define SLJIT_DIV_F32 (SLJIT_DIV_F64 | SLJIT_32)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Label and jump instructions. */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler);
+
+/* Invert (negate) conditional type: xor (^) with 0x1 */
+
+/* Integer comparison types. */
+#define SLJIT_EQUAL 0
+#define SLJIT_ZERO SLJIT_EQUAL
+#define SLJIT_NOT_EQUAL 1
+#define SLJIT_NOT_ZERO SLJIT_NOT_EQUAL
+
+#define SLJIT_LESS 2
+#define SLJIT_SET_LESS SLJIT_SET(SLJIT_LESS)
+#define SLJIT_GREATER_EQUAL 3
+#define SLJIT_SET_GREATER_EQUAL SLJIT_SET(SLJIT_GREATER_EQUAL)
+#define SLJIT_GREATER 4
+#define SLJIT_SET_GREATER SLJIT_SET(SLJIT_GREATER)
+#define SLJIT_LESS_EQUAL 5
+#define SLJIT_SET_LESS_EQUAL SLJIT_SET(SLJIT_LESS_EQUAL)
+#define SLJIT_SIG_LESS 6
+#define SLJIT_SET_SIG_LESS SLJIT_SET(SLJIT_SIG_LESS)
+#define SLJIT_SIG_GREATER_EQUAL 7
+#define SLJIT_SET_SIG_GREATER_EQUAL SLJIT_SET(SLJIT_SIG_GREATER_EQUAL)
+#define SLJIT_SIG_GREATER 8
+#define SLJIT_SET_SIG_GREATER SLJIT_SET(SLJIT_SIG_GREATER)
+#define SLJIT_SIG_LESS_EQUAL 9
+#define SLJIT_SET_SIG_LESS_EQUAL SLJIT_SET(SLJIT_SIG_LESS_EQUAL)
+
+#define SLJIT_OVERFLOW 10
+#define SLJIT_SET_OVERFLOW SLJIT_SET(SLJIT_OVERFLOW)
+#define SLJIT_NOT_OVERFLOW 11
+
+/* Unlike other flags, sljit_emit_jump may destroy the carry flag. */
+#define SLJIT_CARRY 12
+#define SLJIT_SET_CARRY SLJIT_SET(SLJIT_CARRY)
+#define SLJIT_NOT_CARRY 13
+
+/* Basic floating point comparison types.
+
+ Note: when the comparison result is unordered, their behaviour is unspecified. */
+
+#define SLJIT_F_EQUAL 14
+#define SLJIT_SET_F_EQUAL SLJIT_SET(SLJIT_F_EQUAL)
+#define SLJIT_F_NOT_EQUAL 15
+#define SLJIT_SET_F_NOT_EQUAL SLJIT_SET(SLJIT_F_NOT_EQUAL)
+#define SLJIT_F_LESS 16
+#define SLJIT_SET_F_LESS SLJIT_SET(SLJIT_F_LESS)
+#define SLJIT_F_GREATER_EQUAL 17
+#define SLJIT_SET_F_GREATER_EQUAL SLJIT_SET(SLJIT_F_GREATER_EQUAL)
+#define SLJIT_F_GREATER 18
+#define SLJIT_SET_F_GREATER SLJIT_SET(SLJIT_F_GREATER)
+#define SLJIT_F_LESS_EQUAL 19
+#define SLJIT_SET_F_LESS_EQUAL SLJIT_SET(SLJIT_F_LESS_EQUAL)
+
+/* Jumps when either argument contains a NaN value. */
+#define SLJIT_UNORDERED 20
+#define SLJIT_SET_UNORDERED SLJIT_SET(SLJIT_UNORDERED)
+/* Jumps when neither argument contains a NaN value. */
+#define SLJIT_ORDERED 21
+#define SLJIT_SET_ORDERED SLJIT_SET(SLJIT_ORDERED)
+
+/* Ordered / unordered floating point comparison types.
+
+ Note: each comparison type has an ordered and unordered form. Some
+ architectures supports only either of them (see: sljit_cmp_info). */
+
+#define SLJIT_ORDERED_EQUAL 22
+#define SLJIT_SET_ORDERED_EQUAL SLJIT_SET(SLJIT_ORDERED_EQUAL)
+#define SLJIT_UNORDERED_OR_NOT_EQUAL 23
+#define SLJIT_SET_UNORDERED_OR_NOT_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_NOT_EQUAL)
+#define SLJIT_ORDERED_LESS 24
+#define SLJIT_SET_ORDERED_LESS SLJIT_SET(SLJIT_ORDERED_LESS)
+#define SLJIT_UNORDERED_OR_GREATER_EQUAL 25
+#define SLJIT_SET_UNORDERED_OR_GREATER_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_GREATER_EQUAL)
+#define SLJIT_ORDERED_GREATER 26
+#define SLJIT_SET_ORDERED_GREATER SLJIT_SET(SLJIT_ORDERED_GREATER)
+#define SLJIT_UNORDERED_OR_LESS_EQUAL 27
+#define SLJIT_SET_UNORDERED_OR_LESS_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_LESS_EQUAL)
+
+#define SLJIT_UNORDERED_OR_EQUAL 28
+#define SLJIT_SET_UNORDERED_OR_EQUAL SLJIT_SET(SLJIT_UNORDERED_OR_EQUAL)
+#define SLJIT_ORDERED_NOT_EQUAL 29
+#define SLJIT_SET_ORDERED_NOT_EQUAL SLJIT_SET(SLJIT_ORDERED_NOT_EQUAL)
+#define SLJIT_UNORDERED_OR_LESS 30
+#define SLJIT_SET_UNORDERED_OR_LESS SLJIT_SET(SLJIT_UNORDERED_OR_LESS)
+#define SLJIT_ORDERED_GREATER_EQUAL 31
+#define SLJIT_SET_ORDERED_GREATER_EQUAL SLJIT_SET(SLJIT_ORDERED_GREATER_EQUAL)
+#define SLJIT_UNORDERED_OR_GREATER 32
+#define SLJIT_SET_UNORDERED_OR_GREATER SLJIT_SET(SLJIT_UNORDERED_OR_GREATER)
+#define SLJIT_ORDERED_LESS_EQUAL 33
+#define SLJIT_SET_ORDERED_LESS_EQUAL SLJIT_SET(SLJIT_ORDERED_LESS_EQUAL)
+
+/* Unconditional jump types. */
+#define SLJIT_JUMP 34
+/* Fast calling method. See sljit_emit_fast_enter / SLJIT_FAST_RETURN. */
+#define SLJIT_FAST_CALL 35
+/* Default C calling convention. */
+#define SLJIT_CALL 36
+/* Called function must be compiled by SLJIT.
+ See SLJIT_ENTER_REG_ARG option. */
+#define SLJIT_CALL_REG_ARG 37
+
+/* The target can be changed during runtime (see: sljit_set_jump_addr). */
+#define SLJIT_REWRITABLE_JUMP 0x1000
+/* When this flag is passed, the execution of the current function ends and
+ the called function returns to the caller of the current function. The
+ stack usage is reduced before the call, but it is not necessarily reduced
+ to zero. In the latter case the compiler needs to allocate space for some
+ arguments and the return address must be stored on the stack as well. */
+#define SLJIT_CALL_RETURN 0x2000
+
+/* Emit a jump instruction. The destination is not set, only the type of the jump.
+ type must be between SLJIT_EQUAL and SLJIT_FAST_CALL
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
+
+ Flags: does not modify flags. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type);
+
+/* Emit a C compiler (ABI) compatible function call.
+ type must be SLJIT_CALL or SLJIT_CALL_REG_ARG
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP and/or SLJIT_CALL_RETURN
+ arg_types can be specified by SLJIT_ARGSx (SLJIT_ARG_RETURN / SLJIT_ARG_VALUE) macros
+
+ Flags: destroy all flags. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types);
+
+/* Basic arithmetic comparison. In most architectures it is implemented as
+ a compare operation followed by a sljit_emit_jump. However some
+ architectures (i.e: ARM64 or MIPS) may employ special optimizations
+ here. It is suggested to use this comparison form when appropriate.
+ type must be between SLJIT_EQUAL and SLJIT_SIG_LESS_EQUAL
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
+
+ Flags: may destroy flags. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Basic floating point comparison. In most architectures it is implemented as
+ a SLJIT_CMP_F32/64 operation (setting appropriate flags) followed by a
+ sljit_emit_jump. However some architectures (i.e: MIPS) may employ
+ special optimizations here. It is suggested to use this comparison form
+ when appropriate.
+ type must be between SLJIT_F_EQUAL and SLJIT_ORDERED_LESS_EQUAL
+ type can be combined (or'ed) with SLJIT_REWRITABLE_JUMP
+ Flags: destroy flags.
+ Note: when an operand is NaN the behaviour depends on the comparison type. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+/* Set the destination of the jump to this label. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sljit_label* label);
+/* Set the destination address of the jump to this label. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target);
+
+/* Emit an indirect jump or fast call.
+ Direct form: set src to SLJIT_IMM() and srcw to the address
+ Indirect form: any other valid addressing mode
+ type must be between SLJIT_JUMP and SLJIT_FAST_CALL
+
+ Flags: does not modify flags. */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw);
+
+/* Emit a C compiler (ABI) compatible function call.
+ Direct form: set src to SLJIT_IMM() and srcw to the address
+ Indirect form: any other valid addressing mode
+ type must be SLJIT_CALL or SLJIT_CALL_REG_ARG
+ type can be combined (or'ed) with SLJIT_CALL_RETURN
+ arg_types can be specified by SLJIT_ARGSx (SLJIT_ARG_RETURN / SLJIT_ARG_VALUE) macros
+
+ Flags: destroy all flags. */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 arg_types, sljit_s32 src, sljit_sw srcw);
+
+/* Perform an operation using the conditional flags as the second argument.
+ Type must always be between SLJIT_EQUAL and SLJIT_ORDERED_LESS_EQUAL.
+ The value represented by the type is 1, if the condition represented
+ by the type is fulfilled, and 0 otherwise.
+
+ When op is SLJIT_MOV or SLJIT_MOV32:
+ Set dst to the value represented by the type (0 or 1).
+ Flags: - (does not modify flags)
+ When op is SLJIT_AND, SLJIT_AND32, SLJIT_OR, SLJIT_OR32, SLJIT_XOR, or SLJIT_XOR32
+ Performs the binary operation using dst as the first, and the value
+ represented by type as the second argument. Result is written into dst.
+ Flags: Z (may destroy flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type);
+
+/* Emit a conditional mov instruction which moves source to destination,
+ if the condition is satisfied. Unlike other arithmetic operations this
+ instruction does not support memory access.
+
+ type must be between SLJIT_EQUAL and SLJIT_ORDERED_LESS_EQUAL
+ type can be combined (or'ed) with SLJIT_32
+ dst_reg must be a valid register
+ src must be a valid register or immediate (SLJIT_IMM)
+
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw);
+
+/* The following flags are used by sljit_emit_mem(), sljit_emit_mem_update(),
+ sljit_emit_fmem(), and sljit_emit_fmem_update(). */
+
+/* Memory load operation. This is the default. */
+#define SLJIT_MEM_LOAD 0x000000
+/* Memory store operation. */
+#define SLJIT_MEM_STORE 0x000200
+
+/* The following flags are used by sljit_emit_mem() and sljit_emit_fmem(). */
+
+/* Load or stora data from an unaligned (byte aligned) address. */
+#define SLJIT_MEM_UNALIGNED 0x000400
+/* Load or stora data from a 16 bit aligned address. */
+#define SLJIT_MEM_UNALIGNED_16 0x000800
+/* Load or stora data from a 32 bit aligned address. */
+#define SLJIT_MEM_UNALIGNED_32 0x001000
+
+/* The following flags are used by sljit_emit_mem_update(),
+ and sljit_emit_fmem_update(). */
+
+/* Base register is updated before the memory access (default). */
+#define SLJIT_MEM_PRE 0x000000
+/* Base register is updated after the memory access. */
+#define SLJIT_MEM_POST 0x000400
+
+/* When SLJIT_MEM_SUPP is passed, no instructions are emitted.
+ Instead the function returns with SLJIT_SUCCESS if the instruction
+ form is supported and SLJIT_ERR_UNSUPPORTED otherwise. This flag
+ allows runtime checking of available instruction forms. */
+#define SLJIT_MEM_SUPP 0x000800
+
+/* The sljit_emit_mem emits instructions for various memory operations:
+
+ When SLJIT_MEM_UNALIGNED / SLJIT_MEM_UNALIGNED_16 /
+ SLJIT_MEM_UNALIGNED_32 is set in type argument:
+ Emit instructions for unaligned memory loads or stores. When
+ SLJIT_UNALIGNED is not defined, the only way to access unaligned
+ memory data is using sljit_emit_mem. Otherwise all operations (e.g.
+ sljit_emit_op1/2, or sljit_emit_fop1/2) supports unaligned access.
+ In general, the performance of unaligned memory accesses are often
+ lower than aligned and should be avoided.
+
+ When a pair of registers is passed in reg argument:
+ Emit instructions for moving data between a register pair and
+ memory. The register pair can be specified by the SLJIT_REG_PAIR
+ macro. The first register is loaded from or stored into the
+ location specified by the mem/memw arguments, and the end address
+ of this operation is the starting address of the data transfer
+ between the second register and memory. The type argument must
+ be SLJIT_MOV. The SLJIT_MEM_UNALIGNED* options are allowed for
+ this operation.
+
+ type must be between SLJIT_MOV and SLJIT_MOV_P and can be
+ combined (or'ed) with SLJIT_MEM_* flags
+ reg is a register or register pair, which is the source or
+ destination of the operation
+ mem must be a memory operand
+
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Emit a single memory load or store with update instruction.
+ When the requested instruction form is not supported by the CPU,
+ it returns with SLJIT_ERR_UNSUPPORTED instead of emulating the
+ instruction. This allows specializing tight loops based on
+ the supported instruction forms (see SLJIT_MEM_SUPP flag).
+ Absolute address (SLJIT_MEM0) forms are never supported
+ and the base (first) register specified by the mem argument
+ must not be SLJIT_SP and must also be different from the
+ register specified by the reg argument.
+
+ type must be between SLJIT_MOV and SLJIT_MOV_P and can be
+ combined (or'ed) with SLJIT_MEM_* flags
+ reg is the source or destination register of the operation
+ mem must be a memory operand
+
+ Flags: - (does not modify flags) */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Same as sljit_emit_mem except the followings:
+
+ Loading or storing a pair of registers is not supported.
+
+ type must be SLJIT_MOV_F64 or SLJIT_MOV_F32 and can be
+ combined (or'ed) with SLJIT_MEM_* flags.
+ freg is the source or destination floating point register
+ of the operation
+ mem must be a memory operand
+
+ Flags: - (does not modify flags) */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Same as sljit_emit_mem_update except the followings:
+
+ type must be SLJIT_MOV_F64 or SLJIT_MOV_F32 and can be
+ combined (or'ed) with SLJIT_MEM_* flags
+ freg is the source or destination floating point register
+ of the operation
+ mem must be a memory operand
+
+ Flags: - (does not modify flags) */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw);
+
+/* Copies the base address of SLJIT_SP + offset to dst. The offset can
+ represent the starting address of a value in the local data (stack).
+ The offset is not limited by the local data limits, it can be any value.
+ For example if an array of bytes are stored on the stack from
+ offset 0x40, and R0 contains the offset of an array item plus 0x120,
+ this item can be changed by two SLJIT instructions:
+
+ sljit_get_local_base(compiler, SLJIT_R1, 0, 0x40 - 0x120);
+ sljit_emit_op1(compiler, SLJIT_MOV_U8, SLJIT_MEM2(SLJIT_R1, SLJIT_R0), 0, SLJIT_IMM, 0x5);
+
+ Flags: - (may destroy flags) */
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset);
+
+/* Store a value that can be changed runtime (see: sljit_get_const_addr / sljit_set_const)
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value);
+
+/* Store the value of a label (see: sljit_set_put_label)
+ Flags: - (does not modify flags) */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw);
+
+/* Set the value stored by put_label to this label. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_put_label(struct sljit_put_label *put_label, struct sljit_label *label);
+
+/* After the code generation the address for label, jump and const instructions
+ are computed. Since these structures are freed by sljit_free_compiler, the
+ addresses must be preserved by the user program elsewere. */
+static SLJIT_INLINE sljit_uw sljit_get_label_addr(struct sljit_label *label) { return label->addr; }
+static SLJIT_INLINE sljit_uw sljit_get_jump_addr(struct sljit_jump *jump) { return jump->addr; }
+static SLJIT_INLINE sljit_uw sljit_get_const_addr(struct sljit_const *const_) { return const_->addr; }
+
+/* Only the address and executable offset are required to perform dynamic
+ code modifications. See sljit_get_executable_offset function. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset);
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset);
+
+/* --------------------------------------------------------------------- */
+/* CPU specific functions */
+/* --------------------------------------------------------------------- */
+
+/* The following function is a helper function for sljit_emit_op_custom.
+ It returns with the real machine register index ( >=0 ) of any SLJIT_R,
+ SLJIT_S and SLJIT_SP registers.
+
+ Note: it returns with -1 for virtual registers (only on x86-32). */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg);
+
+/* The following function is a helper function for sljit_emit_op_custom.
+ It returns with the real machine register ( >= 0 ) index of any SLJIT_FR,
+ and SLJIT_FS register.
+
+ Note: the index is always an even number on ARM-32, MIPS. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg);
+
+/* Any instruction can be inserted into the instruction stream by
+ sljit_emit_op_custom. It has a similar purpose as inline assembly.
+ The size parameter must match to the instruction size of the target
+ architecture:
+
+ x86: 0 < size <= 15. The instruction argument can be byte aligned.
+ Thumb2: if size == 2, the instruction argument must be 2 byte aligned.
+ if size == 4, the instruction argument must be 4 byte aligned.
+ Otherwise: size must be 4 and instruction argument must be 4 byte aligned. */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size);
+
+/* Flags were set by a 32 bit operation. */
+#define SLJIT_CURRENT_FLAGS_32 SLJIT_32
+
+/* Flags were set by an ADD or ADDC operations. */
+#define SLJIT_CURRENT_FLAGS_ADD 0x01
+/* Flags were set by a SUB, SUBC, or NEG operation. */
+#define SLJIT_CURRENT_FLAGS_SUB 0x02
+
+/* Flags were set by sljit_emit_op2u with SLJIT_SUB opcode.
+ Must be combined with SLJIT_CURRENT_FLAGS_SUB. */
+#define SLJIT_CURRENT_FLAGS_COMPARE 0x04
+
+/* Define the currently available CPU status flags. It is usually used after
+ an sljit_emit_label or sljit_emit_op_custom operations to define which CPU
+ status flags are available.
+
+ The current_flags must be a valid combination of SLJIT_SET_* and
+ SLJIT_CURRENT_FLAGS_* constants. */
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_current_flags(struct sljit_compiler *compiler,
+ sljit_s32 current_flags);
+
+/* --------------------------------------------------------------------- */
+/* Miscellaneous utility functions */
+/* --------------------------------------------------------------------- */
+
+/* Get the human readable name of the platform. Can be useful on platforms
+ like ARM, where ARM and Thumb2 functions can be mixed, and it is useful
+ to know the type of the code generator. */
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void);
+
+/* Portable helper function to get an offset of a member. */
+#define SLJIT_OFFSETOF(base, member) ((sljit_sw)(&((base*)0x10)->member) - 0x10)
+
+#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK)
+
+/* The sljit_stack structure and its manipulation functions provides
+ an implementation for a top-down stack. The stack top is stored
+ in the end field of the sljit_stack structure and the stack goes
+ down to the min_start field, so the memory region reserved for
+ this stack is between min_start (inclusive) and end (exclusive)
+ fields. However the application can only use the region between
+ start (inclusive) and end (exclusive) fields. The sljit_stack_resize
+ function can be used to extend this region up to min_start.
+
+ This feature uses the "address space reserve" feature of modern
+ operating systems. Instead of allocating a large memory block
+ applications can allocate a small memory region and extend it
+ later without moving the content of the memory area. Therefore
+ after a successful resize by sljit_stack_resize all pointers into
+ this region are still valid.
+
+ Note:
+ this structure may not be supported by all operating systems.
+ end and max_limit fields are aligned to PAGE_SIZE bytes (usually
+ 4 Kbyte or more).
+ stack should grow in larger steps, e.g. 4Kbyte, 16Kbyte or more. */
+
+struct sljit_stack {
+ /* User data, anything can be stored here.
+ Initialized to the same value as the end field. */
+ sljit_u8 *top;
+/* These members are read only. */
+ /* End address of the stack */
+ sljit_u8 *end;
+ /* Current start address of the stack. */
+ sljit_u8 *start;
+ /* Lowest start address of the stack. */
+ sljit_u8 *min_start;
+};
+
+/* Allocates a new stack. Returns NULL if unsuccessful.
+ Note: see sljit_create_compiler for the explanation of allocator_data. */
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data);
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data);
+
+/* Can be used to increase (extend) or decrease (shrink) the stack
+ memory area. Returns with new_start if successful and NULL otherwise.
+ It always fails if new_start is less than min_start or greater or equal
+ than end fields. The fields of the stack are not changed if the returned
+ value is NULL (the current memory content is never lost). */
+SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start);
+
+#endif /* (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) */
+
+#if !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+
+/* Get the entry address of a given function (signed, unsigned result). */
+#define SLJIT_FUNC_ADDR(func_name) ((sljit_sw)func_name)
+#define SLJIT_FUNC_UADDR(func_name) ((sljit_uw)func_name)
+
+#else /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */
+
+/* All JIT related code should be placed in the same context (library, binary, etc.). */
+
+/* Get the entry address of a given function (signed, unsigned result). */
+#define SLJIT_FUNC_ADDR(func_name) (*(sljit_sw*)(void*)func_name)
+#define SLJIT_FUNC_UADDR(func_name) (*(sljit_uw*)(void*)func_name)
+
+/* For powerpc64, the function pointers point to a context descriptor. */
+struct sljit_function_context {
+ sljit_uw addr;
+ sljit_uw r2;
+ sljit_uw r11;
+};
+
+/* Fill the context arguments using the addr and the function.
+ If func_ptr is NULL, it will not be set to the address of context
+ If addr is NULL, the function address also comes from the func pointer. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct sljit_function_context* context, sljit_uw addr, void* func);
+
+#endif /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+/* Free unused executable memory. The allocator keeps some free memory
+ around to reduce the number of OS executable memory allocations.
+ This improves performance since these calls are costly. However
+ it is sometimes desired to free all unused memory regions, e.g.
+ before the application terminates. */
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void);
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SLJIT_LIR_H_ */
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeARM_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeARM_32.c
new file mode 100644
index 0000000000..54b8ade063
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeARM_32.c
@@ -0,0 +1,3700 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+#ifdef __SOFTFP__
+#define ARM_ABI_INFO " ABI:softfp"
+#else
+#define ARM_ABI_INFO " ABI:hardfp"
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ return "ARMv7" SLJIT_CPUINFO ARM_ABI_INFO;
+#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ return "ARMv5" SLJIT_CPUINFO ARM_ABI_INFO;
+#else
+#error "Internal error: Unknown ARM architecture"
+#endif
+}
+
+/* Last register + 1. */
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 4)
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+/* In ARM instruction words.
+ Cache lines are usually 32 byte aligned. */
+#define CONST_POOL_ALIGNMENT 8
+#define CONST_POOL_EMPTY 0xffffffff
+
+#define ALIGN_INSTRUCTION(ptr) \
+ (sljit_uw*)(((sljit_uw)(ptr) + (CONST_POOL_ALIGNMENT * sizeof(sljit_uw)) - 1) & ~((CONST_POOL_ALIGNMENT * sizeof(sljit_uw)) - 1))
+#define MAX_DIFFERENCE(max_diff) \
+ (((max_diff) / (sljit_s32)sizeof(sljit_uw)) - (CONST_POOL_ALIGNMENT - 1))
+
+/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
+ 0, 0, 1, 2, 3, 11, 10, 9, 8, 7, 6, 5, 4, 13, 12, 14, 15
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 15, 14, 13, 12, 11, 10, 9, 8, 6, 7
+};
+
+#define RM(rm) ((sljit_uw)reg_map[rm])
+#define RM8(rm) ((sljit_uw)reg_map[rm] << 8)
+#define RD(rd) ((sljit_uw)reg_map[rd] << 12)
+#define RN(rn) ((sljit_uw)reg_map[rn] << 16)
+
+#define VM(rm) ((sljit_uw)freg_map[rm])
+#define VD(rd) ((sljit_uw)freg_map[rd] << 12)
+#define VN(rn) ((sljit_uw)freg_map[rn] << 16)
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+/* The instruction includes the AL condition.
+ INST_NAME - CONDITIONAL remove this flag. */
+#define COND_MASK 0xf0000000
+#define CONDITIONAL 0xe0000000
+#define PUSH_POOL 0xff000000
+
+#define ADC 0xe0a00000
+#define ADD 0xe0800000
+#define AND 0xe0000000
+#define B 0xea000000
+#define BIC 0xe1c00000
+#define BL 0xeb000000
+#define BLX 0xe12fff30
+#define BX 0xe12fff10
+#define CLZ 0xe16f0f10
+#define CMN 0xe1600000
+#define CMP 0xe1400000
+#define BKPT 0xe1200070
+#define EOR 0xe0200000
+#define LDR 0xe5100000
+#define LDR_POST 0xe4100000
+#define MOV 0xe1a00000
+#define MUL 0xe0000090
+#define MVN 0xe1e00000
+#define NOP 0xe1a00000
+#define ORR 0xe1800000
+#define PUSH 0xe92d0000
+#define POP 0xe8bd0000
+#define RBIT 0xe6ff0f30
+#define RSB 0xe0600000
+#define RSC 0xe0e00000
+#define SBC 0xe0c00000
+#define SMULL 0xe0c00090
+#define STR 0xe5000000
+#define SUB 0xe0400000
+#define TST 0xe1000000
+#define UMULL 0xe0800090
+#define VABS_F32 0xeeb00ac0
+#define VADD_F32 0xee300a00
+#define VCMP_F32 0xeeb40a40
+#define VCVT_F32_S32 0xeeb80ac0
+#define VCVT_F64_F32 0xeeb70ac0
+#define VCVT_S32_F32 0xeebd0ac0
+#define VDIV_F32 0xee800a00
+#define VLDR_F32 0xed100a00
+#define VMOV_F32 0xeeb00a40
+#define VMOV 0xee000a10
+#define VMOV2 0xec400a10
+#define VMRS 0xeef1fa10
+#define VMUL_F32 0xee200a00
+#define VNEG_F32 0xeeb10a40
+#define VPOP 0xecbd0b00
+#define VPUSH 0xed2d0b00
+#define VSTR_F32 0xed000a00
+#define VSUB_F32 0xee300a40
+
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+/* Arm v7 specific instructions. */
+#define MOVW 0xe3000000
+#define MOVT 0xe3400000
+#define SXTB 0xe6af0070
+#define SXTH 0xe6bf0070
+#define UXTB 0xe6ef0070
+#define UXTH 0xe6ff0070
+#endif
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+
+static sljit_s32 push_cpool(struct sljit_compiler *compiler)
+{
+ /* Pushing the constant pool into the instruction stream. */
+ sljit_uw* inst;
+ sljit_uw* cpool_ptr;
+ sljit_uw* cpool_end;
+ sljit_s32 i;
+
+ /* The label could point the address after the constant pool. */
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ compiler->last_label->size += compiler->cpool_fill + (CONST_POOL_ALIGNMENT - 1) + 1;
+
+ SLJIT_ASSERT(compiler->cpool_fill > 0 && compiler->cpool_fill <= CPOOL_SIZE);
+ inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!inst);
+ compiler->size++;
+ *inst = 0xff000000 | compiler->cpool_fill;
+
+ for (i = 0; i < CONST_POOL_ALIGNMENT - 1; i++) {
+ inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!inst);
+ compiler->size++;
+ *inst = 0;
+ }
+
+ cpool_ptr = compiler->cpool;
+ cpool_end = cpool_ptr + compiler->cpool_fill;
+ while (cpool_ptr < cpool_end) {
+ inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!inst);
+ compiler->size++;
+ *inst = *cpool_ptr++;
+ }
+ compiler->cpool_diff = CONST_POOL_EMPTY;
+ compiler->cpool_fill = 0;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_uw inst)
+{
+ sljit_uw* ptr;
+
+ if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)))
+ FAIL_IF(push_cpool(compiler));
+
+ ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!ptr);
+ compiler->size++;
+ *ptr = inst;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_inst_with_literal(struct sljit_compiler *compiler, sljit_uw inst, sljit_uw literal)
+{
+ sljit_uw* ptr;
+ sljit_uw cpool_index = CPOOL_SIZE;
+ sljit_uw* cpool_ptr;
+ sljit_uw* cpool_end;
+ sljit_u8* cpool_unique_ptr;
+
+ if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)))
+ FAIL_IF(push_cpool(compiler));
+ else if (compiler->cpool_fill > 0) {
+ cpool_ptr = compiler->cpool;
+ cpool_end = cpool_ptr + compiler->cpool_fill;
+ cpool_unique_ptr = compiler->cpool_unique;
+ do {
+ if ((*cpool_ptr == literal) && !(*cpool_unique_ptr)) {
+ cpool_index = (sljit_uw)(cpool_ptr - compiler->cpool);
+ break;
+ }
+ cpool_ptr++;
+ cpool_unique_ptr++;
+ } while (cpool_ptr < cpool_end);
+ }
+
+ if (cpool_index == CPOOL_SIZE) {
+ /* Must allocate a new entry in the literal pool. */
+ if (compiler->cpool_fill < CPOOL_SIZE) {
+ cpool_index = compiler->cpool_fill;
+ compiler->cpool_fill++;
+ }
+ else {
+ FAIL_IF(push_cpool(compiler));
+ cpool_index = 0;
+ compiler->cpool_fill = 1;
+ }
+ }
+
+ SLJIT_ASSERT((inst & 0xfff) == 0);
+ ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!ptr);
+ compiler->size++;
+ *ptr = inst | cpool_index;
+
+ compiler->cpool[cpool_index] = literal;
+ compiler->cpool_unique[cpool_index] = 0;
+ if (compiler->cpool_diff == CONST_POOL_EMPTY)
+ compiler->cpool_diff = compiler->size;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_inst_with_unique_literal(struct sljit_compiler *compiler, sljit_uw inst, sljit_uw literal)
+{
+ sljit_uw* ptr;
+ if (SLJIT_UNLIKELY((compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)) || compiler->cpool_fill >= CPOOL_SIZE))
+ FAIL_IF(push_cpool(compiler));
+
+ SLJIT_ASSERT(compiler->cpool_fill < CPOOL_SIZE && (inst & 0xfff) == 0);
+ ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!ptr);
+ compiler->size++;
+ *ptr = inst | compiler->cpool_fill;
+
+ compiler->cpool[compiler->cpool_fill] = literal;
+ compiler->cpool_unique[compiler->cpool_fill] = 1;
+ compiler->cpool_fill++;
+ if (compiler->cpool_diff == CONST_POOL_EMPTY)
+ compiler->cpool_diff = compiler->size;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 prepare_blx(struct sljit_compiler *compiler)
+{
+ /* Place for at least two instruction (doesn't matter whether the first has a literal). */
+ if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4088)))
+ return push_cpool(compiler);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_blx(struct sljit_compiler *compiler)
+{
+ /* Must follow tightly the previous instruction (to be able to convert it to bl instruction). */
+ SLJIT_ASSERT(compiler->cpool_diff == CONST_POOL_EMPTY || compiler->size - compiler->cpool_diff < MAX_DIFFERENCE(4092));
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
+ return push_inst(compiler, BLX | RM(TMP_REG1));
+}
+
+static sljit_uw patch_pc_relative_loads(sljit_uw *last_pc_patch, sljit_uw *code_ptr, sljit_uw* const_pool, sljit_uw cpool_size)
+{
+ sljit_uw diff;
+ sljit_uw ind;
+ sljit_uw counter = 0;
+ sljit_uw* clear_const_pool = const_pool;
+ sljit_uw* clear_const_pool_end = const_pool + cpool_size;
+
+ SLJIT_ASSERT(const_pool - code_ptr <= CONST_POOL_ALIGNMENT);
+ /* Set unused flag for all literals in the constant pool.
+ I.e.: unused literals can belong to branches, which can be encoded as B or BL.
+ We can "compress" the constant pool by discarding these literals. */
+ while (clear_const_pool < clear_const_pool_end)
+ *clear_const_pool++ = (sljit_uw)(-1);
+
+ while (last_pc_patch < code_ptr) {
+ /* Data transfer instruction with Rn == r15. */
+ if ((*last_pc_patch & 0x0c0f0000) == 0x040f0000) {
+ diff = (sljit_uw)(const_pool - last_pc_patch);
+ ind = (*last_pc_patch) & 0xfff;
+
+ /* Must be a load instruction with immediate offset. */
+ SLJIT_ASSERT(ind < cpool_size && !(*last_pc_patch & (1 << 25)) && (*last_pc_patch & (1 << 20)));
+ if ((sljit_s32)const_pool[ind] < 0) {
+ const_pool[ind] = counter;
+ ind = counter;
+ counter++;
+ }
+ else
+ ind = const_pool[ind];
+
+ SLJIT_ASSERT(diff >= 1);
+ if (diff >= 2 || ind > 0) {
+ diff = (diff + (sljit_uw)ind - 2) << 2;
+ SLJIT_ASSERT(diff <= 0xfff);
+ *last_pc_patch = (*last_pc_patch & ~(sljit_uw)0xfff) | diff;
+ }
+ else
+ *last_pc_patch = (*last_pc_patch & ~(sljit_uw)(0xfff | (1 << 23))) | 0x004;
+ }
+ last_pc_patch++;
+ }
+ return counter;
+}
+
+/* In some rare ocasions we may need future patches. The probability is close to 0 in practice. */
+struct future_patch {
+ struct future_patch* next;
+ sljit_s32 index;
+ sljit_s32 value;
+};
+
+static sljit_s32 resolve_const_pool_index(struct sljit_compiler *compiler, struct future_patch **first_patch, sljit_uw cpool_current_index, sljit_uw *cpool_start_address, sljit_uw *buf_ptr)
+{
+ sljit_u32 value;
+ struct future_patch *curr_patch, *prev_patch;
+
+ SLJIT_UNUSED_ARG(compiler);
+
+ /* Using the values generated by patch_pc_relative_loads. */
+ if (!*first_patch)
+ value = cpool_start_address[cpool_current_index];
+ else {
+ curr_patch = *first_patch;
+ prev_patch = NULL;
+ while (1) {
+ if (!curr_patch) {
+ value = cpool_start_address[cpool_current_index];
+ break;
+ }
+ if ((sljit_uw)curr_patch->index == cpool_current_index) {
+ value = (sljit_uw)curr_patch->value;
+ if (prev_patch)
+ prev_patch->next = curr_patch->next;
+ else
+ *first_patch = curr_patch->next;
+ SLJIT_FREE(curr_patch, compiler->allocator_data);
+ break;
+ }
+ prev_patch = curr_patch;
+ curr_patch = curr_patch->next;
+ }
+ }
+
+ if ((sljit_sw)value >= 0) {
+ if (value > cpool_current_index) {
+ curr_patch = (struct future_patch*)SLJIT_MALLOC(sizeof(struct future_patch), compiler->allocator_data);
+ if (!curr_patch) {
+ while (*first_patch) {
+ curr_patch = *first_patch;
+ *first_patch = (*first_patch)->next;
+ SLJIT_FREE(curr_patch, compiler->allocator_data);
+ }
+ return SLJIT_ERR_ALLOC_FAILED;
+ }
+ curr_patch->next = *first_patch;
+ curr_patch->index = (sljit_sw)value;
+ curr_patch->value = (sljit_sw)cpool_start_address[value];
+ *first_patch = curr_patch;
+ }
+ cpool_start_address[value] = *buf_ptr;
+ }
+ return SLJIT_SUCCESS;
+}
+
+#else
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_uw inst)
+{
+ sljit_uw* ptr;
+
+ ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw));
+ FAIL_IF(!ptr);
+ compiler->size++;
+ *ptr = inst;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_imm(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
+{
+ FAIL_IF(push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | ((sljit_u32)imm & 0xfff)));
+ return push_inst(compiler, MOVT | RD(reg) | ((imm >> 12) & 0xf0000) | (((sljit_u32)imm >> 16) & 0xfff));
+}
+
+#endif
+
+static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_uw *code_ptr, sljit_uw *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ return 0;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (jump->flags & IS_BL)
+ code_ptr--;
+
+ if (jump->flags & JUMP_ADDR)
+ diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2) - executable_offset);
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)(code_ptr + 2));
+ }
+
+ /* Branch to Thumb code has not been optimized yet. */
+ if (diff & 0x3)
+ return 0;
+
+ if (jump->flags & IS_BL) {
+ if (diff <= 0x01ffffff && diff >= -0x02000000) {
+ *code_ptr = (BL - CONDITIONAL) | (*(code_ptr + 1) & COND_MASK);
+ jump->flags |= PATCH_B;
+ return 1;
+ }
+ }
+ else {
+ if (diff <= 0x01ffffff && diff >= -0x02000000) {
+ *code_ptr = (B - CONDITIONAL) | (*code_ptr & COND_MASK);
+ jump->flags |= PATCH_B;
+ }
+ }
+#else
+ if (jump->flags & JUMP_ADDR)
+ diff = ((sljit_sw)jump->u.target - (sljit_sw)code_ptr - executable_offset);
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)code_ptr);
+ }
+
+ /* Branch to Thumb code has not been optimized yet. */
+ if (diff & 0x3)
+ return 0;
+
+ if (diff <= 0x01ffffff && diff >= -0x02000000) {
+ code_ptr -= 2;
+ *code_ptr = ((jump->flags & IS_BL) ? (BL - CONDITIONAL) : (B - CONDITIONAL)) | (code_ptr[2] & COND_MASK);
+ jump->flags |= PATCH_B;
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+static SLJIT_INLINE void inline_set_jump_addr(sljit_uw jump_ptr, sljit_sw executable_offset, sljit_uw new_addr, sljit_s32 flush_cache)
+{
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_uw *ptr = (sljit_uw *)jump_ptr;
+ sljit_uw *inst = (sljit_uw *)ptr[0];
+ sljit_uw mov_pc = ptr[1];
+ sljit_s32 bl = (mov_pc & 0x0000f000) != RD(TMP_PC);
+ sljit_sw diff = (sljit_sw)(((sljit_sw)new_addr - (sljit_sw)(inst + 2) - executable_offset) >> 2);
+
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ if (diff <= 0x7fffff && diff >= -0x800000) {
+ /* Turn to branch. */
+ if (!bl) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 0);
+ }
+ inst[0] = (mov_pc & COND_MASK) | (B - CONDITIONAL) | (diff & 0xffffff);
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ } else {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ }
+ inst[0] = (mov_pc & COND_MASK) | (BL - CONDITIONAL) | (diff & 0xffffff);
+ inst[1] = NOP;
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+ }
+ }
+ } else {
+ /* Get the position of the constant. */
+ if (mov_pc & (1 << 23))
+ ptr = inst + ((mov_pc & 0xfff) >> 2) + 2;
+ else
+ ptr = inst + 1;
+
+ if (*inst != mov_pc) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + (!bl ? 1 : 2), 0);
+ }
+ inst[0] = mov_pc;
+ if (!bl) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ } else {
+ inst[1] = BLX | RM(TMP_REG1);
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+ }
+ }
+ }
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 0);
+ }
+
+ *ptr = new_addr;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 1);
+ }
+ }
+#else
+ sljit_uw *inst = (sljit_uw*)jump_ptr;
+
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT);
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ }
+
+ inst[0] = MOVW | (inst[0] & 0xf000) | ((new_addr << 4) & 0xf0000) | (new_addr & 0xfff);
+ inst[1] = MOVT | (inst[1] & 0xf000) | ((new_addr >> 12) & 0xf0000) | ((new_addr >> 16) & 0xfff);
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+ }
+#endif
+}
+
+static sljit_uw get_imm(sljit_uw imm);
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_uw imm);
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg);
+
+static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_sw executable_offset, sljit_uw new_constant, sljit_s32 flush_cache)
+{
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_uw *ptr = (sljit_uw*)addr;
+ sljit_uw *inst = (sljit_uw*)ptr[0];
+ sljit_uw ldr_literal = ptr[1];
+ sljit_uw src2;
+
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ src2 = get_imm(new_constant);
+ if (src2) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 0);
+ }
+
+ *inst = 0xe3a00000 | (ldr_literal & 0xf000) | src2;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ return;
+ }
+
+ src2 = get_imm(~new_constant);
+ if (src2) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 0);
+ }
+
+ *inst = 0xe3e00000 | (ldr_literal & 0xf000) | src2;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ return;
+ }
+
+ if (ldr_literal & (1 << 23))
+ ptr = inst + ((ldr_literal & 0xfff) >> 2) + 2;
+ else
+ ptr = inst + 1;
+
+ if (*inst != ldr_literal) {
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 0);
+ }
+
+ *inst = ldr_literal;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 1, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 1);
+ }
+ }
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 0);
+ }
+
+ *ptr = new_constant;
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 1);
+ }
+#else
+ sljit_uw *inst = (sljit_uw*)addr;
+
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT);
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ }
+
+ inst[0] = MOVW | (inst[0] & 0xf000) | ((new_constant << 4) & 0xf0000) | (new_constant & 0xfff);
+ inst[1] = MOVT | (inst[1] & 0xf000) | ((new_constant >> 12) & 0xf0000) | ((new_constant >> 16) & 0xfff);
+
+ if (flush_cache) {
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+ }
+#endif
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_uw *code;
+ sljit_uw *code_ptr;
+ sljit_uw *buf_ptr;
+ sljit_uw *buf_end;
+ sljit_uw size;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_uw addr;
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_uw cpool_size;
+ sljit_uw cpool_skip_alignment;
+ sljit_uw cpool_current_index;
+ sljit_uw *cpool_start_address;
+ sljit_uw *last_pc_patch;
+ struct future_patch *first_patch;
+#endif
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ /* Second code generation pass. */
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ size = compiler->size + (compiler->patches << 1);
+ if (compiler->cpool_fill > 0)
+ size += compiler->cpool_fill + CONST_POOL_ALIGNMENT - 1;
+#else
+ size = compiler->size;
+#endif
+ code = (sljit_uw*)SLJIT_MALLOC_EXEC(size * sizeof(sljit_uw), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ cpool_size = 0;
+ cpool_skip_alignment = 0;
+ cpool_current_index = 0;
+ cpool_start_address = NULL;
+ first_patch = NULL;
+ last_pc_patch = code;
+#endif
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 1;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ if (label && label->size == 0) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ label = label->next;
+ }
+
+ do {
+ buf_ptr = (sljit_uw*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ word_count++;
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (cpool_size > 0) {
+ if (cpool_skip_alignment > 0) {
+ buf_ptr++;
+ cpool_skip_alignment--;
+ }
+ else {
+ if (SLJIT_UNLIKELY(resolve_const_pool_index(compiler, &first_patch, cpool_current_index, cpool_start_address, buf_ptr))) {
+ SLJIT_FREE_EXEC(code, compiler->exec_allocator_data);
+ compiler->error = SLJIT_ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ buf_ptr++;
+ if (++cpool_current_index >= cpool_size) {
+ SLJIT_ASSERT(!first_patch);
+ cpool_size = 0;
+ if (label && label->size == word_count) {
+ /* Points after the current instruction. */
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ }
+ }
+ }
+ else if ((*buf_ptr & 0xff000000) != PUSH_POOL) {
+#endif
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (jump && jump->addr == word_count) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (detect_jump_type(jump, code_ptr, code, executable_offset))
+ code_ptr--;
+ jump->addr = (sljit_uw)code_ptr;
+#else
+ jump->addr = (sljit_uw)(code_ptr - 2);
+ if (detect_jump_type(jump, code_ptr, code, executable_offset))
+ code_ptr -= 2;
+#endif
+ jump = jump->next;
+ }
+ if (label && label->size == word_count) {
+ /* code_ptr can be affected above. */
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr + 1, executable_offset);
+ label->size = (sljit_uw)((code_ptr + 1) - code);
+ label = label->next;
+ }
+ if (const_ && const_->addr == word_count) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ const_->addr = (sljit_uw)code_ptr;
+#else
+ const_->addr = (sljit_uw)(code_ptr - 1);
+#endif
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ }
+ else {
+ /* Fortunately, no need to shift. */
+ cpool_size = *buf_ptr++ & ~PUSH_POOL;
+ SLJIT_ASSERT(cpool_size > 0);
+ cpool_start_address = ALIGN_INSTRUCTION(code_ptr + 1);
+ cpool_current_index = patch_pc_relative_loads(last_pc_patch, code_ptr, cpool_start_address, cpool_size);
+ if (cpool_current_index > 0) {
+ /* Unconditional branch. */
+ *code_ptr = B | (((sljit_uw)(cpool_start_address - code_ptr) + cpool_current_index - 2) & ~PUSH_POOL);
+ code_ptr = (sljit_uw*)(cpool_start_address + cpool_current_index);
+ }
+ cpool_skip_alignment = CONST_POOL_ALIGNMENT - 1;
+ cpool_current_index = 0;
+ last_pc_patch = code_ptr;
+ }
+#endif
+ } while (buf_ptr < buf_end);
+ buf = buf->next;
+ } while (buf);
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ SLJIT_ASSERT(cpool_size == 0);
+ if (compiler->cpool_fill > 0) {
+ cpool_start_address = ALIGN_INSTRUCTION(code_ptr);
+ cpool_current_index = patch_pc_relative_loads(last_pc_patch, code_ptr, cpool_start_address, compiler->cpool_fill);
+ if (cpool_current_index > 0)
+ code_ptr = (sljit_uw*)(cpool_start_address + cpool_current_index);
+
+ buf_ptr = compiler->cpool;
+ buf_end = buf_ptr + compiler->cpool_fill;
+ cpool_current_index = 0;
+ while (buf_ptr < buf_end) {
+ if (SLJIT_UNLIKELY(resolve_const_pool_index(compiler, &first_patch, cpool_current_index, cpool_start_address, buf_ptr))) {
+ SLJIT_FREE_EXEC(code, compiler->exec_allocator_data);
+ compiler->error = SLJIT_ERR_ALLOC_FAILED;
+ return NULL;
+ }
+ buf_ptr++;
+ cpool_current_index++;
+ }
+ SLJIT_ASSERT(!first_patch);
+ }
+#endif
+
+ jump = compiler->jumps;
+ while (jump) {
+ buf_ptr = (sljit_uw *)jump->addr;
+
+ if (jump->flags & PATCH_B) {
+ addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr + 2, executable_offset);
+ if (!(jump->flags & JUMP_ADDR)) {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ SLJIT_ASSERT((sljit_sw)(jump->u.label->addr - addr) <= 0x01ffffff && (sljit_sw)(jump->u.label->addr - addr) >= -0x02000000);
+ *buf_ptr |= ((jump->u.label->addr - addr) >> 2) & 0x00ffffff;
+ }
+ else {
+ SLJIT_ASSERT((sljit_sw)(jump->u.target - addr) <= 0x01ffffff && (sljit_sw)(jump->u.target - addr) >= -0x02000000);
+ *buf_ptr |= ((jump->u.target - addr) >> 2) & 0x00ffffff;
+ }
+ }
+ else if (jump->flags & SLJIT_REWRITABLE_JUMP) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ jump->addr = (sljit_uw)code_ptr;
+ code_ptr[0] = (sljit_uw)buf_ptr;
+ code_ptr[1] = *buf_ptr;
+ inline_set_jump_addr((sljit_uw)code_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
+ code_ptr += 2;
+#else
+ inline_set_jump_addr((sljit_uw)buf_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
+#endif
+ }
+ else {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (jump->flags & IS_BL)
+ buf_ptr--;
+ if (*buf_ptr & (1 << 23))
+ buf_ptr += ((*buf_ptr & 0xfff) >> 2) + 2;
+ else
+ buf_ptr += 1;
+ *buf_ptr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+#else
+ inline_set_jump_addr((sljit_uw)buf_ptr, executable_offset, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0);
+#endif
+ }
+ jump = jump->next;
+ }
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ const_ = compiler->consts;
+ while (const_) {
+ buf_ptr = (sljit_uw*)const_->addr;
+ const_->addr = (sljit_uw)code_ptr;
+
+ code_ptr[0] = (sljit_uw)buf_ptr;
+ code_ptr[1] = *buf_ptr;
+ if (*buf_ptr & (1 << 23))
+ buf_ptr += ((*buf_ptr & 0xfff) >> 2) + 2;
+ else
+ buf_ptr += 1;
+ /* Set the value again (can be a simple constant). */
+ inline_set_const((sljit_uw)code_ptr, executable_offset, *buf_ptr, 0);
+ code_ptr += 2;
+
+ const_ = const_->next;
+ }
+#endif
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ addr = put_label->label->addr;
+ buf_ptr = (sljit_uw*)put_label->addr;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ SLJIT_ASSERT((buf_ptr[0] & 0xffff0000) == 0xe59f0000);
+ buf_ptr[((buf_ptr[0] & 0xfff) >> 2) + 2] = addr;
+#else
+ SLJIT_ASSERT((buf_ptr[-1] & 0xfff00000) == MOVW && (buf_ptr[0] & 0xfff00000) == MOVT);
+ buf_ptr[-1] |= ((addr << 4) & 0xf0000) | (addr & 0xfff);
+ buf_ptr[0] |= ((addr >> 12) & 0xf0000) | ((addr >> 16) & 0xfff);
+#endif
+ put_label = put_label->next;
+ }
+
+ SLJIT_ASSERT(code_ptr - code <= (sljit_s32)size);
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_uw);
+
+ code = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_uw *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_CMOV:
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ case SLJIT_HAS_CTZ:
+ case SLJIT_HAS_PREFETCH:
+#endif
+ return 1;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ case SLJIT_HAS_CTZ:
+ return 2;
+#endif
+
+ default:
+ return 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+/* Creates an index in data_transfer_insts array. */
+#define WORD_SIZE 0x00
+#define BYTE_SIZE 0x01
+#define HALF_SIZE 0x02
+#define PRELOAD 0x03
+#define SIGNED 0x04
+#define LOAD_DATA 0x08
+
+/* Flag bits for emit_op. */
+#define ALLOW_IMM 0x10
+#define ALLOW_INV_IMM 0x20
+#define ALLOW_ANY_IMM (ALLOW_IMM | ALLOW_INV_IMM)
+#define ALLOW_NEG_IMM 0x40
+
+/* s/l - store/load (1 bit)
+ u/s - signed/unsigned (1 bit)
+ w/b/h/N - word/byte/half/NOT allowed (2 bit)
+ Storing signed and unsigned values are the same operations. */
+
+static const sljit_uw data_transfer_insts[16] = {
+/* s u w */ 0xe5000000 /* str */,
+/* s u b */ 0xe5400000 /* strb */,
+/* s u h */ 0xe10000b0 /* strh */,
+/* s u N */ 0x00000000 /* not allowed */,
+/* s s w */ 0xe5000000 /* str */,
+/* s s b */ 0xe5400000 /* strb */,
+/* s s h */ 0xe10000b0 /* strh */,
+/* s s N */ 0x00000000 /* not allowed */,
+
+/* l u w */ 0xe5100000 /* ldr */,
+/* l u b */ 0xe5500000 /* ldrb */,
+/* l u h */ 0xe11000b0 /* ldrh */,
+/* l u p */ 0xf5500000 /* preload */,
+/* l s w */ 0xe5100000 /* ldr */,
+/* l s b */ 0xe11000d0 /* ldrsb */,
+/* l s h */ 0xe11000f0 /* ldrsh */,
+/* l s N */ 0x00000000 /* not allowed */,
+};
+
+#define EMIT_DATA_TRANSFER(type, add, target_reg, base_reg, arg) \
+ (data_transfer_insts[(type) & 0xf] | ((add) << 23) | RD(target_reg) | RN(base_reg) | (sljit_uw)(arg))
+
+/* Normal ldr/str instruction.
+ Type2: ldrsb, ldrh, ldrsh */
+#define IS_TYPE1_TRANSFER(type) \
+ (data_transfer_insts[(type) & 0xf] & 0x04000000)
+#define TYPE2_TRANSFER_IMM(imm) \
+ (((imm) & 0xf) | (((imm) & 0xf0) << 4) | (1 << 22))
+
+#define EMIT_FPU_OPERATION(opcode, mode, dst, src1, src2) \
+ ((sljit_uw)(opcode) | (sljit_uw)(mode) | VD(dst) | VM(src1) | VN(src2))
+
+/* Flags for emit_op: */
+ /* Arguments are swapped. */
+#define ARGS_SWAPPED 0x01
+ /* Inverted immediate. */
+#define INV_IMM 0x02
+ /* Source and destination is register. */
+#define MOVE_REG_CONV 0x04
+ /* Unused return value. */
+#define UNUSED_RETURN 0x08
+/* SET_FLAGS must be (1 << 20) as it is also the value of S bit (can be used for optimization). */
+#define SET_FLAGS (1 << 20)
+/* dst: reg
+ src1: reg
+ src2: reg or imm (if allowed)
+ SRC2_IMM must be (1 << 25) as it is also the value of I bit (can be used for optimization). */
+#define SRC2_IMM (1 << 25)
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_uw imm, offset;
+ sljit_s32 i, tmp, size, word_arg_count;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+#ifdef __SOFTFP__
+ sljit_u32 float_arg_count;
+#else
+ sljit_u32 old_offset, f32_offset;
+ sljit_u32 remap[3];
+ sljit_u32 *remap_ptr = remap;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ imm = 0;
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--)
+ imm |= (sljit_uw)1 << reg_map[i];
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--)
+ imm |= (sljit_uw)1 << reg_map[i];
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ /* Push saved and temporary registers
+ multiple registers: stmdb sp!, {..., lr}
+ single register: str reg, [sp, #-4]! */
+ if (imm != 0)
+ FAIL_IF(push_inst(compiler, PUSH | (1 << 14) | imm));
+ else
+ FAIL_IF(push_inst(compiler, 0xe52d0004 | RD(TMP_REG2)));
+
+ /* Stack must be aligned to 8 bytes: */
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((size & SSIZE_OF(sw)) != 0) {
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | sizeof(sljit_sw)));
+ size += SSIZE_OF(sw);
+ }
+
+ if (fsaveds + fscratches >= SLJIT_NUMBER_OF_FLOAT_REGISTERS) {
+ FAIL_IF(push_inst(compiler, VPUSH | VD(SLJIT_FS0) | ((sljit_uw)SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS << 1)));
+ } else {
+ if (fsaveds > 0)
+ FAIL_IF(push_inst(compiler, VPUSH | VD(SLJIT_FS0) | ((sljit_uw)fsaveds << 1)));
+ if (fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)
+ FAIL_IF(push_inst(compiler, VPUSH | VD(fscratches) | ((sljit_uw)(fscratches - (SLJIT_FIRST_SAVED_FLOAT_REG - 1)) << 1)));
+ }
+ }
+
+ local_size = ((size + local_size + 0x7) & ~0x7) - size;
+ compiler->local_size = local_size;
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ arg_types = 0;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ word_arg_count = 0;
+ saved_arg_count = 0;
+#ifdef __SOFTFP__
+ SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start);
+
+ offset = 0;
+ float_arg_count = 0;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7)
+ offset += sizeof(sljit_sw);
+
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst(compiler, VMOV2 | (offset << 10) | ((offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ else
+ FAIL_IF(push_inst(compiler, VLDR_F32 | 0x800100 | RN(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ float_arg_count++;
+ offset += sizeof(sljit_f64) - sizeof(sljit_sw);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst(compiler, VMOV | (float_arg_count << 16) | (offset << 10)));
+ else
+ FAIL_IF(push_inst(compiler, VLDR_F32 | 0x800000 | RN(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ float_arg_count++;
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (word_arg_count - 1 != (sljit_s32)(offset >> 2))
+ tmp = word_arg_count;
+ else
+ break;
+
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst(compiler, MOV | RD(tmp) | (offset >> 2)));
+ else
+ FAIL_IF(push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(tmp) | (offset + (sljit_uw)size - 4 * sizeof(sljit_sw))));
+ break;
+ }
+
+ offset += sizeof(sljit_sw);
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ compiler->args_size = offset;
+#else
+ offset = SLJIT_FR0;
+ old_offset = SLJIT_FR0;
+ f32_offset = 0;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset != old_offset)
+ *remap_ptr++ = EMIT_FPU_OPERATION(VMOV_F32, SLJIT_32, offset, old_offset, 0);
+ old_offset++;
+ offset++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (f32_offset != 0) {
+ *remap_ptr++ = EMIT_FPU_OPERATION(VMOV_F32, 0x20, offset, f32_offset, 0);
+ f32_offset = 0;
+ } else {
+ if (offset != old_offset)
+ *remap_ptr++ = EMIT_FPU_OPERATION(VMOV_F32, 0, offset, old_offset, 0);
+ f32_offset = old_offset;
+ old_offset++;
+ }
+ offset++;
+ break;
+ default:
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, MOV | RD(SLJIT_S0 - saved_arg_count) | RM(SLJIT_R0 + word_arg_count)));
+ saved_arg_count++;
+ }
+
+ word_arg_count++;
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT((sljit_uw)(remap_ptr - remap) <= sizeof(remap));
+
+ while (remap_ptr > remap)
+ FAIL_IF(push_inst(compiler, *(--remap_ptr)));
+#endif
+
+ if (local_size > 0)
+ FAIL_IF(emit_op(compiler, SLJIT_SUB, ALLOW_IMM, SLJIT_SP, 0, SLJIT_SP, 0, SLJIT_IMM, local_size));
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 size;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+
+ if ((size & SSIZE_OF(sw)) != 0 && (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG))
+ size += SSIZE_OF(sw);
+
+ compiler->local_size = ((size + local_size + 0x7) & ~0x7) - size;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_add_sp(struct sljit_compiler *compiler, sljit_uw imm)
+{
+ sljit_uw imm2 = get_imm(imm);
+
+ if (imm2 == 0) {
+ imm2 = (imm & ~(sljit_uw)0x3ff) >> 10;
+ imm = (imm & 0x3ff) >> 2;
+
+ FAIL_IF(push_inst(compiler, ADD | SRC2_IMM | RD(SLJIT_SP) | RN(SLJIT_SP) | 0xb00 | imm2));
+ return push_inst(compiler, ADD | SRC2_IMM | RD(SLJIT_SP) | RN(SLJIT_SP) | 0xf00 | (imm & 0xff));
+ }
+
+ return push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | imm2);
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size)
+{
+ sljit_s32 local_size, fscratches, fsaveds, i, tmp;
+ sljit_s32 restored_reg = 0;
+ sljit_s32 lr_dst = TMP_PC;
+ sljit_uw reg_list = 0;
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14 && frame_size <= 128);
+
+ local_size = compiler->local_size;
+ fscratches = compiler->fscratches;
+ fsaveds = compiler->fsaveds;
+
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if (local_size > 0)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)local_size));
+
+ if (fsaveds + fscratches >= SLJIT_NUMBER_OF_FLOAT_REGISTERS) {
+ FAIL_IF(push_inst(compiler, VPOP | VD(SLJIT_FS0) | ((sljit_uw)SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS << 1)));
+ } else {
+ if (fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)
+ FAIL_IF(push_inst(compiler, VPOP | VD(fscratches) | ((sljit_uw)(fscratches - (SLJIT_FIRST_SAVED_FLOAT_REG - 1)) << 1)));
+ if (fsaveds > 0)
+ FAIL_IF(push_inst(compiler, VPOP | VD(SLJIT_FS0) | ((sljit_uw)fsaveds << 1)));
+ }
+
+ local_size = GET_SAVED_REGISTERS_SIZE(compiler->scratches, compiler->saveds, 1) & 0x7;
+ }
+
+ if (frame_size < 0) {
+ lr_dst = TMP_REG2;
+ frame_size = 0;
+ } else if (frame_size > 0) {
+ SLJIT_ASSERT(frame_size == 1 || (frame_size & 0x7) == 0);
+ lr_dst = 0;
+ frame_size &= ~0x7;
+ }
+
+ if (lr_dst != 0)
+ reg_list |= (sljit_uw)1 << reg_map[lr_dst];
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ if (tmp < i) {
+ restored_reg = i;
+ do {
+ reg_list |= (sljit_uw)1 << reg_map[i];
+ } while (--i > tmp);
+ }
+
+ i = compiler->scratches;
+ if (i >= SLJIT_FIRST_SAVED_REG) {
+ restored_reg = i;
+ do {
+ reg_list |= (sljit_uw)1 << reg_map[i];
+ } while (--i >= SLJIT_FIRST_SAVED_REG);
+ }
+
+ if (lr_dst == TMP_REG2 && reg_list == 0) {
+ restored_reg = TMP_REG2;
+ lr_dst = 0;
+ }
+
+ if (lr_dst == 0 && (reg_list & (reg_list - 1)) == 0) {
+ /* The local_size does not include the saved registers. */
+ tmp = 0;
+ if (reg_list != 0) {
+ tmp = 2;
+ if (local_size <= 0xfff) {
+ if (local_size == 0) {
+ SLJIT_ASSERT(restored_reg != TMP_REG2);
+ if (frame_size == 0)
+ return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | 0x800008);
+ if (frame_size > 2 * SSIZE_OF(sw))
+ return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)(frame_size - (2 * SSIZE_OF(sw))));
+ }
+
+ FAIL_IF(push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)local_size));
+ tmp = 1;
+ } else if (frame_size == 0) {
+ frame_size = (restored_reg == TMP_REG2) ? SSIZE_OF(sw) : 2 * SSIZE_OF(sw);
+ tmp = 3;
+ }
+
+ /* Place for the saved register. */
+ if (restored_reg != TMP_REG2)
+ local_size += SSIZE_OF(sw);
+ }
+
+ /* Place for the lr register. */
+ local_size += SSIZE_OF(sw);
+
+ if (frame_size > local_size)
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | (sljit_uw)(frame_size - local_size)));
+ else if (frame_size < local_size)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)(local_size - frame_size)));
+
+ if (tmp <= 1)
+ return SLJIT_SUCCESS;
+
+ if (tmp == 2) {
+ frame_size -= SSIZE_OF(sw);
+ if (restored_reg != TMP_REG2)
+ frame_size -= SSIZE_OF(sw);
+
+ return push_inst(compiler, LDR | 0x800000 | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)frame_size);
+ }
+
+ tmp = (restored_reg == TMP_REG2) ? 0x800004 : 0x800008;
+ return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(restored_reg) | (sljit_uw)tmp);
+ }
+
+ if (local_size > 0)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)local_size));
+
+ /* Pop saved and temporary registers
+ multiple registers: ldmia sp!, {...}
+ single register: ldr reg, [sp], #4 */
+ if ((reg_list & (reg_list - 1)) == 0) {
+ SLJIT_ASSERT(lr_dst != 0);
+ SLJIT_ASSERT(reg_list == (sljit_uw)1 << reg_map[lr_dst]);
+
+ return push_inst(compiler, LDR_POST | RN(SLJIT_SP) | RD(lr_dst) | 0x800004);
+ }
+
+ FAIL_IF(push_inst(compiler, POP | reg_list));
+
+ if (frame_size > 0)
+ return push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | ((sljit_uw)frame_size - sizeof(sljit_sw)));
+
+ if (lr_dst != 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 25) | sizeof(sljit_sw));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ return emit_stack_frame_release(compiler, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src)));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_uw dst, sljit_uw src1, sljit_uw src2)
+{
+ sljit_s32 is_masked;
+ sljit_uw shift_type;
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
+ if (dst != src2) {
+ if (src2 & SRC2_IMM) {
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
+ }
+ return push_inst(compiler, MOV | RD(dst) | RM(src2));
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
+ if (flags & MOVE_REG_CONV) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (op == SLJIT_MOV_U8)
+ return push_inst(compiler, AND | RD(dst) | RN(src2) | SRC2_IMM | 0xff);
+ FAIL_IF(push_inst(compiler, MOV | RD(dst) | (24 << 7) | RM(src2)));
+ return push_inst(compiler, MOV | RD(dst) | (24 << 7) | (op == SLJIT_MOV_U8 ? 0x20 : 0x40) | RM(dst));
+#else
+ return push_inst(compiler, (op == SLJIT_MOV_U8 ? UXTB : SXTB) | RD(dst) | RM(src2));
+#endif
+ }
+ else if (dst != src2) {
+ SLJIT_ASSERT(src2 & SRC2_IMM);
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
+ if (flags & MOVE_REG_CONV) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ FAIL_IF(push_inst(compiler, MOV | RD(dst) | (16 << 7) | RM(src2)));
+ return push_inst(compiler, MOV | RD(dst) | (16 << 7) | (op == SLJIT_MOV_U16 ? 0x20 : 0x40) | RM(dst));
+#else
+ return push_inst(compiler, (op == SLJIT_MOV_U16 ? UXTH : SXTH) | RD(dst) | RM(src2));
+#endif
+ }
+ else if (dst != src2) {
+ SLJIT_ASSERT(src2 & SRC2_IMM);
+ return push_inst(compiler, ((flags & INV_IMM) ? MVN : MOV) | RD(dst) | src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_NOT:
+ if (src2 & SRC2_IMM)
+ return push_inst(compiler, ((flags & INV_IMM) ? MOV : MVN) | (flags & SET_FLAGS) | RD(dst) | src2);
+
+ return push_inst(compiler, MVN | (flags & SET_FLAGS) | RD(dst) | RM(src2));
+
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM));
+ FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(src2)));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM));
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED));
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ FAIL_IF(push_inst(compiler, RSB | SRC2_IMM | RD(TMP_REG1) | RN(src2) | 0));
+ FAIL_IF(push_inst(compiler, AND | RD(TMP_REG2) | RN(src2) | RM(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, CMP | SET_FLAGS | SRC2_IMM | RN(dst) | 32));
+ return push_inst(compiler, (EOR ^ 0xf0000000) | SRC2_IMM | RD(dst) | RN(dst) | 0x1f);
+#else /* !SLJIT_CONFIG_ARM_V5 */
+ FAIL_IF(push_inst(compiler, RBIT | RD(dst) | RM(src2)));
+ return push_inst(compiler, CLZ | RD(dst) | RM(dst));
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ case SLJIT_ADD:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+
+ if ((flags & (UNUSED_RETURN | ARGS_SWAPPED)) == UNUSED_RETURN)
+ return push_inst(compiler, CMN | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+ return push_inst(compiler, ADD | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_ADDC:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ return push_inst(compiler, ADC | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_SUB:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+
+ if ((flags & (UNUSED_RETURN | ARGS_SWAPPED)) == UNUSED_RETURN)
+ return push_inst(compiler, CMP | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ return push_inst(compiler, (!(flags & ARGS_SWAPPED) ? SUB : RSB) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_SUBC:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ return push_inst(compiler, (!(flags & ARGS_SWAPPED) ? SBC : RSC) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_MUL:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ SLJIT_ASSERT(!(src2 & SRC2_IMM));
+ compiler->status_flags_state = 0;
+
+ if (!HAS_FLAGS(op))
+ return push_inst(compiler, MUL | RN(dst) | RM8(src2) | RM(src1));
+
+ FAIL_IF(push_inst(compiler, SMULL | RN(TMP_REG1) | RD(dst) | RM8(src2) | RM(src1)));
+
+ /* cmp TMP_REG1, dst asr #31. */
+ return push_inst(compiler, CMP | SET_FLAGS | RN(TMP_REG1) | RM(dst) | 0xfc0);
+
+ case SLJIT_AND:
+ if ((flags & (UNUSED_RETURN | INV_IMM)) == UNUSED_RETURN)
+ return push_inst(compiler, TST | SET_FLAGS | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+ return push_inst(compiler, (!(flags & INV_IMM) ? AND : BIC) | (flags & SET_FLAGS)
+ | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_OR:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ return push_inst(compiler, ORR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_XOR:
+ SLJIT_ASSERT(!(flags & INV_IMM));
+ return push_inst(compiler, EOR | (flags & SET_FLAGS) | RD(dst) | RN(src1) | ((src2 & SRC2_IMM) ? src2 : RM(src2)));
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ shift_type = 0;
+ is_masked = GET_OPCODE(op) == SLJIT_MSHL;
+ break;
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ shift_type = 1;
+ is_masked = GET_OPCODE(op) == SLJIT_MLSHR;
+ break;
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ shift_type = 2;
+ is_masked = GET_OPCODE(op) == SLJIT_MASHR;
+ break;
+
+ case SLJIT_ROTL:
+ if (compiler->shift_imm == 0x20) {
+ FAIL_IF(push_inst(compiler, RSB | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0));
+ src2 = TMP_REG2;
+ } else
+ compiler->shift_imm = (sljit_uw)(-(sljit_sw)compiler->shift_imm) & 0x1f;
+ /* fallthrough */
+
+ case SLJIT_ROTR:
+ shift_type = 3;
+ is_masked = 0;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+ }
+
+ SLJIT_ASSERT(!(flags & ARGS_SWAPPED) && !(flags & INV_IMM) && !(src2 & SRC2_IMM));
+
+ if (compiler->shift_imm != 0x20) {
+ SLJIT_ASSERT(src1 == TMP_REG1);
+
+ if (compiler->shift_imm != 0)
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) |
+ RD(dst) | (compiler->shift_imm << 7) | (shift_type << 5) | RM(src2));
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst) | RM(src2));
+ }
+
+ SLJIT_ASSERT(src1 != TMP_REG2);
+
+ if (is_masked) {
+ FAIL_IF(push_inst(compiler, AND | RD(TMP_REG2) | RN(src2) | SRC2_IMM | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, MOV | (flags & SET_FLAGS) | RD(dst)
+ | RM8(src2) | (sljit_uw)(shift_type << 5) | 0x10 | RM(src1));
+}
+
+#undef EMIT_SHIFT_INS_AND_RETURN
+
+/* Tests whether the immediate can be stored in the 12 bit imm field.
+ Returns with 0 if not possible. */
+static sljit_uw get_imm(sljit_uw imm)
+{
+ sljit_u32 rol;
+
+ if (imm <= 0xff)
+ return SRC2_IMM | imm;
+
+ if (!(imm & 0xff000000)) {
+ imm <<= 8;
+ rol = 8;
+ }
+ else {
+ imm = (imm << 24) | (imm >> 8);
+ rol = 0;
+ }
+
+ if (!(imm & 0xff000000)) {
+ imm <<= 8;
+ rol += 4;
+ }
+
+ if (!(imm & 0xf0000000)) {
+ imm <<= 4;
+ rol += 2;
+ }
+
+ if (!(imm & 0xc0000000)) {
+ imm <<= 2;
+ rol += 1;
+ }
+
+ if (!(imm & 0x00ffffff))
+ return SRC2_IMM | (imm >> 24) | (rol << 8);
+ else
+ return 0;
+}
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+static sljit_s32 generate_int(struct sljit_compiler *compiler, sljit_s32 reg, sljit_uw imm, sljit_s32 positive)
+{
+ sljit_uw mask;
+ sljit_uw imm1;
+ sljit_uw imm2;
+ sljit_uw rol;
+
+ /* Step1: Search a zero byte (8 continous zero bit). */
+ mask = 0xff000000;
+ rol = 8;
+ while(1) {
+ if (!(imm & mask)) {
+ /* Rol imm by rol. */
+ imm = (imm << rol) | (imm >> (32 - rol));
+ /* Calculate arm rol. */
+ rol = 4 + (rol >> 1);
+ break;
+ }
+ rol += 2;
+ mask >>= 2;
+ if (mask & 0x3) {
+ /* rol by 8. */
+ imm = (imm << 8) | (imm >> 24);
+ mask = 0xff00;
+ rol = 24;
+ while (1) {
+ if (!(imm & mask)) {
+ /* Rol imm by rol. */
+ imm = (imm << rol) | (imm >> (32 - rol));
+ /* Calculate arm rol. */
+ rol = (rol >> 1) - 8;
+ break;
+ }
+ rol += 2;
+ mask >>= 2;
+ if (mask & 0x3)
+ return 0;
+ }
+ break;
+ }
+ }
+
+ /* The low 8 bit must be zero. */
+ SLJIT_ASSERT(!(imm & 0xff));
+
+ if (!(imm & 0xff000000)) {
+ imm1 = SRC2_IMM | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8);
+ imm2 = SRC2_IMM | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8);
+ }
+ else if (imm & 0xc0000000) {
+ imm1 = SRC2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
+ imm <<= 8;
+ rol += 4;
+
+ if (!(imm & 0xff000000)) {
+ imm <<= 8;
+ rol += 4;
+ }
+
+ if (!(imm & 0xf0000000)) {
+ imm <<= 4;
+ rol += 2;
+ }
+
+ if (!(imm & 0xc0000000)) {
+ imm <<= 2;
+ rol += 1;
+ }
+
+ if (!(imm & 0x00ffffff))
+ imm2 = SRC2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
+ else
+ return 0;
+ }
+ else {
+ if (!(imm & 0xf0000000)) {
+ imm <<= 4;
+ rol += 2;
+ }
+
+ if (!(imm & 0xc0000000)) {
+ imm <<= 2;
+ rol += 1;
+ }
+
+ imm1 = SRC2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
+ imm <<= 8;
+ rol += 4;
+
+ if (!(imm & 0xf0000000)) {
+ imm <<= 4;
+ rol += 2;
+ }
+
+ if (!(imm & 0xc0000000)) {
+ imm <<= 2;
+ rol += 1;
+ }
+
+ if (!(imm & 0x00ffffff))
+ imm2 = SRC2_IMM | (imm >> 24) | ((rol & 0xf) << 8);
+ else
+ return 0;
+ }
+
+ FAIL_IF(push_inst(compiler, (positive ? MOV : MVN) | RD(reg) | imm1));
+ FAIL_IF(push_inst(compiler, (positive ? ORR : BIC) | RD(reg) | RN(reg) | imm2));
+ return 1;
+}
+#endif
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_uw imm)
+{
+ sljit_uw tmp;
+
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ if (!(imm & ~(sljit_uw)0xffff))
+ return push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff));
+#endif
+
+ /* Create imm by 1 inst. */
+ tmp = get_imm(imm);
+ if (tmp)
+ return push_inst(compiler, MOV | RD(reg) | tmp);
+
+ tmp = get_imm(~imm);
+ if (tmp)
+ return push_inst(compiler, MVN | RD(reg) | tmp);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ /* Create imm by 2 inst. */
+ FAIL_IF(generate_int(compiler, reg, imm, 1));
+ FAIL_IF(generate_int(compiler, reg, ~imm, 0));
+
+ /* Load integer. */
+ return push_inst_with_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, reg, TMP_PC, 0), imm);
+#else
+ FAIL_IF(push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff)));
+ if (imm <= 0xffff)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, MOVT | RD(reg) | ((imm >> 12) & 0xf0000) | ((imm >> 16) & 0xfff));
+#endif
+}
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
+{
+ sljit_uw imm, offset_reg, tmp;
+ sljit_sw mask = IS_TYPE1_TRANSFER(flags) ? 0xfff : 0xff;
+ sljit_sw sign = IS_TYPE1_TRANSFER(flags) ? 0x1000 : 0x100;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ SLJIT_ASSERT((arg & REG_MASK) != tmp_reg || (arg == SLJIT_MEM1(tmp_reg) && argw >= -mask && argw <= mask));
+
+ if (SLJIT_UNLIKELY(!(arg & REG_MASK))) {
+ tmp = (sljit_uw)(argw & (sign | mask));
+ tmp = (sljit_uw)((argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask);
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, tmp));
+
+ argw -= (sljit_sw)tmp;
+ tmp = 1;
+
+ if (argw < 0) {
+ argw = -argw;
+ tmp = 0;
+ }
+
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, tmp, reg, tmp_reg,
+ (mask == 0xff) ? TYPE2_TRANSFER_IMM(argw) : argw));
+ }
+
+ if (arg & OFFS_REG_MASK) {
+ offset_reg = OFFS_REG(arg);
+ arg &= REG_MASK;
+ argw &= 0x3;
+
+ if (argw != 0 && (mask == 0xff)) {
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | RM(offset_reg) | ((sljit_uw)argw << 7)));
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, tmp_reg, TYPE2_TRANSFER_IMM(0)));
+ }
+
+ /* Bit 25: RM is offset. */
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg,
+ RM(offset_reg) | (mask == 0xff ? 0 : (1 << 25)) | ((sljit_uw)argw << 7)));
+ }
+
+ arg &= REG_MASK;
+
+ if (argw > mask) {
+ tmp = (sljit_uw)(argw & (sign | mask));
+ tmp = (sljit_uw)((argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask);
+ imm = get_imm(tmp);
+
+ if (imm) {
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg) | imm));
+ argw -= (sljit_sw)tmp;
+ arg = tmp_reg;
+
+ SLJIT_ASSERT(argw >= -mask && argw <= mask);
+ }
+ } else if (argw < -mask) {
+ tmp = (sljit_uw)(-argw & (sign | mask));
+ tmp = (sljit_uw)((-argw + (tmp <= (sljit_uw)sign ? 0 : sign)) & ~mask);
+ imm = get_imm(tmp);
+
+ if (imm) {
+ FAIL_IF(push_inst(compiler, SUB | RD(tmp_reg) | RN(arg) | imm));
+ argw += (sljit_sw)tmp;
+ arg = tmp_reg;
+
+ SLJIT_ASSERT(argw >= -mask && argw <= mask);
+ }
+ }
+
+ if (argw <= mask && argw >= -mask) {
+ if (argw >= 0) {
+ if (mask == 0xff)
+ argw = TYPE2_TRANSFER_IMM(argw);
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg, argw));
+ }
+
+ argw = -argw;
+
+ if (mask == 0xff)
+ argw = TYPE2_TRANSFER_IMM(argw);
+
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 0, reg, arg, argw));
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw));
+ return push_inst(compiler, EMIT_DATA_TRANSFER(flags, 1, reg, arg,
+ RM(tmp_reg) | (mask == 0xff ? 0 : (1 << 25))));
+}
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 inp_flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* src1 is reg or TMP_REG1
+ src2 is reg, TMP_REG2, or imm
+ result goes to TMP_REG2, so put result can use TMP_REG1. */
+
+ /* We prefers register and simple consts. */
+ sljit_s32 dst_reg;
+ sljit_s32 src1_reg;
+ sljit_s32 src2_reg = 0;
+ sljit_s32 flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
+ sljit_s32 neg_op = 0;
+
+ if (dst == TMP_REG2)
+ flags |= UNUSED_RETURN;
+
+ SLJIT_ASSERT(!(inp_flags & ALLOW_INV_IMM) || (inp_flags & ALLOW_IMM));
+
+ if (inp_flags & ALLOW_NEG_IMM) {
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ neg_op = SLJIT_SUB;
+ break;
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ neg_op = SLJIT_SUBC;
+ break;
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ neg_op = SLJIT_ADD;
+ break;
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ neg_op = SLJIT_ADDC;
+ break;
+ }
+ }
+
+ do {
+ if (!(inp_flags & ALLOW_IMM))
+ break;
+
+ if (src2 & SLJIT_IMM) {
+ src2_reg = (sljit_s32)get_imm((sljit_uw)src2w);
+ if (src2_reg)
+ break;
+ if (inp_flags & ALLOW_INV_IMM) {
+ src2_reg = (sljit_s32)get_imm(~(sljit_uw)src2w);
+ if (src2_reg) {
+ flags |= INV_IMM;
+ break;
+ }
+ }
+ if (neg_op != 0) {
+ src2_reg = (sljit_s32)get_imm((sljit_uw)-src2w);
+ if (src2_reg) {
+ op = neg_op | GET_ALL_FLAGS(op);
+ break;
+ }
+ }
+ }
+
+ if (src1 & SLJIT_IMM) {
+ src2_reg = (sljit_s32)get_imm((sljit_uw)src1w);
+ if (src2_reg) {
+ flags |= ARGS_SWAPPED;
+ src1 = src2;
+ src1w = src2w;
+ break;
+ }
+ if (inp_flags & ALLOW_INV_IMM) {
+ src2_reg = (sljit_s32)get_imm(~(sljit_uw)src1w);
+ if (src2_reg) {
+ flags |= ARGS_SWAPPED | INV_IMM;
+ src1 = src2;
+ src1w = src2w;
+ break;
+ }
+ }
+ if (neg_op >= SLJIT_SUB) {
+ /* Note: additive operation (commutative). */
+ src2_reg = (sljit_s32)get_imm((sljit_uw)-src1w);
+ if (src2_reg) {
+ src1 = src2;
+ src1w = src2w;
+ op = neg_op | GET_ALL_FLAGS(op);
+ break;
+ }
+ }
+ }
+ } while(0);
+
+ /* Source 1. */
+ if (FAST_IS_REG(src1))
+ src1_reg = src1;
+ else if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1));
+ src1_reg = TMP_REG1;
+ }
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)src1w));
+ src1_reg = TMP_REG1;
+ }
+
+ /* Destination. */
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG2;
+
+ if (op <= SLJIT_MOV_P) {
+ if (dst & SLJIT_MEM) {
+ if (inp_flags & BYTE_SIZE)
+ inp_flags &= ~SIGNED;
+
+ if (FAST_IS_REG(src2))
+ return emit_op_mem(compiler, inp_flags, src2, dst, dstw, TMP_REG2);
+ }
+
+ if (FAST_IS_REG(src2) && dst_reg != TMP_REG2)
+ flags |= MOVE_REG_CONV;
+ }
+
+ /* Source 2. */
+ if (src2_reg == 0) {
+ src2_reg = (op <= SLJIT_MOV_P) ? dst_reg : TMP_REG2;
+
+ if (FAST_IS_REG(src2))
+ src2_reg = src2;
+ else if (src2 & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, inp_flags | LOAD_DATA, src2_reg, src2, src2w, TMP_REG2));
+ else
+ FAIL_IF(load_immediate(compiler, src2_reg, (sljit_uw)src2w));
+ }
+
+ FAIL_IF(emit_single_op(compiler, op, flags, (sljit_uw)dst_reg, (sljit_uw)src1_reg, (sljit_uw)src2_reg));
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, inp_flags, dst_reg, dst, dstw, TMP_REG1);
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__)
+extern unsigned int __aeabi_uidivmod(unsigned int numerator, unsigned int denominator);
+extern int __aeabi_idivmod(int numerator, int denominator);
+#else
+#error "Software divmod functions are needed"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ sljit_uw saved_reg_list[3];
+ sljit_sw saved_reg_count;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ FAIL_IF(push_inst(compiler, BKPT));
+ break;
+ case SLJIT_NOP:
+ FAIL_IF(push_inst(compiler, NOP));
+ break;
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ return push_inst(compiler, (op == SLJIT_LMUL_UW ? UMULL : SMULL)
+ | RN(SLJIT_R1) | RD(SLJIT_R0) | RM8(SLJIT_R0) | RM(SLJIT_R1));
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
+ SLJIT_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 3);
+
+ saved_reg_count = 0;
+ if (compiler->scratches >= 4)
+ saved_reg_list[saved_reg_count++] = 3;
+ if (compiler->scratches >= 3)
+ saved_reg_list[saved_reg_count++] = 2;
+ if (op >= SLJIT_DIV_UW)
+ saved_reg_list[saved_reg_count++] = 1;
+
+ if (saved_reg_count > 0) {
+ FAIL_IF(push_inst(compiler, STR | 0x2d0000 | (saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* str rX, [sp, #-8/-16]! */));
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst(compiler, STR | 0x8d0004 | (saved_reg_list[1] << 12) /* str rX, [sp, #4] */));
+ }
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst(compiler, STR | 0x8d0008 | (saved_reg_list[2] << 12) /* str rX, [sp, #8] */));
+ }
+ }
+
+#if defined(__GNUC__)
+ FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM,
+ ((op | 0x2) == SLJIT_DIV_UW ? SLJIT_FUNC_ADDR(__aeabi_uidivmod) : SLJIT_FUNC_ADDR(__aeabi_idivmod))));
+#else
+#error "Software divmod functions are needed"
+#endif
+
+ if (saved_reg_count > 0) {
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst(compiler, LDR | 0x8d0008 | (saved_reg_list[2] << 12) /* ldr rX, [sp, #8] */));
+ }
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst(compiler, LDR | 0x8d0004 | (saved_reg_list[1] << 12) /* ldr rX, [sp, #4] */));
+ }
+ return push_inst(compiler, (LDR ^ (1 << 24)) | 0x8d0000 | (sljit_uw)(saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* ldr rX, [sp], #8/16 */);
+ }
+ return SLJIT_SUCCESS;
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw);
+
+ case SLJIT_MOV_U8:
+ return emit_op(compiler, SLJIT_MOV_U8, ALLOW_ANY_IMM | BYTE_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
+
+ case SLJIT_MOV_S8:
+ return emit_op(compiler, SLJIT_MOV_S8, ALLOW_ANY_IMM | SIGNED | BYTE_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
+
+ case SLJIT_MOV_U16:
+ return emit_op(compiler, SLJIT_MOV_U16, ALLOW_ANY_IMM | HALF_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
+
+ case SLJIT_MOV_S16:
+ return emit_op(compiler, SLJIT_MOV_S16, ALLOW_ANY_IMM | SIGNED | HALF_SIZE, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
+
+ case SLJIT_NOT:
+ return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src, srcw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ case SLJIT_ADDC:
+ case SLJIT_SUB:
+ case SLJIT_SUBC:
+ return emit_op(compiler, op, ALLOW_IMM | ALLOW_NEG_IMM, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ return emit_op(compiler, op, ALLOW_IMM, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_MUL:
+ return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_AND:
+ return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (src2 & SLJIT_IMM) {
+ compiler->shift_imm = src2w & 0x1f;
+ return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src1, src1w);
+ } else {
+ compiler->shift_imm = 0x20;
+ return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w);
+ }
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_left;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ op = GET_OPCODE(op);
+ is_left = (op == SLJIT_SHL || op == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, is_left ? SLJIT_ROTL : SLJIT_ROTR, src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ /* Shift type of ROR is 3. */
+ if (src2 & SLJIT_IMM) {
+ src2w &= 0x1f;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ FAIL_IF(push_inst(compiler, MOV | RD(src_dst) | RM(src_dst) | ((sljit_uw)(is_left ? 0 : 1) << 5) | ((sljit_uw)src2w << 7)));
+ src2w = (src2w ^ 0x1f) + 1;
+ return push_inst(compiler, ORR | RD(src_dst) | RN(src_dst) | RM(src1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | ((sljit_uw)src2w << 7));
+ }
+
+ if (op == SLJIT_MSHL || op == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, AND | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ FAIL_IF(push_inst(compiler, MOV | RD(src_dst) | RM8(src2) | ((sljit_uw)(is_left ? 0 : 1) << 5) | 0x10 | RM(src_dst)));
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | (1 << 7)));
+ FAIL_IF(push_inst(compiler, EOR | SRC2_IMM | RD(TMP_REG2) | RN(src2) | 0x1f));
+ return push_inst(compiler, ORR | RD(src_dst) | RN(src_dst) | RM(TMP_REG1) | ((sljit_uw)(is_left ? 1 : 0) << 5) | 0x10 | RM8(TMP_REG2));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(src)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, src, srcw, TMP_REG1));
+
+ return push_inst(compiler, BX | RM(TMP_REG2));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ SLJIT_ASSERT(src & SLJIT_MEM);
+ return emit_op_mem(compiler, PRELOAD | LOAD_DATA, TMP_PC, src, srcw, TMP_REG1);
+#else /* !SLJIT_CONFIG_ARM_V7 */
+ return SLJIT_SUCCESS;
+#endif /* SLJIT_CONFIG_ARM_V7 */
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return (freg_map[reg] << 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ SLJIT_UNUSED_ARG(size);
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_uw*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FPU_LOAD (1 << 20)
+#define EMIT_FPU_DATA_TRANSFER(inst, add, base, freg, offs) \
+ ((inst) | (sljit_uw)((add) << 23) | RN(base) | VD(freg) | (sljit_uw)(offs))
+
+static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_uw imm;
+ sljit_uw inst = VSTR_F32 | (flags & (SLJIT_32 | FPU_LOAD));
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ arg &= ~SLJIT_MEM;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (((sljit_uw)argw & 0x3) << 7)));
+ arg = TMP_REG2;
+ argw = 0;
+ }
+
+ /* Fast loads and stores. */
+ if (arg) {
+ if (!(argw & ~0x3fc))
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, arg & REG_MASK, reg, argw >> 2));
+ if (!(-argw & ~0x3fc))
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, arg & REG_MASK, reg, (-argw) >> 2));
+
+ imm = get_imm((sljit_uw)argw & ~(sljit_uw)0x3fc);
+ if (imm) {
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | imm));
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG2, reg, (argw & 0x3fc) >> 2));
+ }
+ imm = get_imm((sljit_uw)-argw & ~(sljit_uw)0x3fc);
+ if (imm) {
+ argw = -argw;
+ FAIL_IF(push_inst(compiler, SUB | RD(TMP_REG2) | RN(arg & REG_MASK) | imm));
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 0, TMP_REG2, reg, (argw & 0x3fc) >> 2));
+ }
+ }
+
+ if (arg) {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, (sljit_uw)argw));
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG2) | RN(arg & REG_MASK) | RM(TMP_REG2)));
+ }
+ else
+ FAIL_IF(load_immediate(compiler, TMP_REG2, (sljit_uw)argw));
+
+ return push_inst(compiler, EMIT_FPU_DATA_TRANSFER(inst, 1, TMP_REG2, reg, 0));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ op ^= SLJIT_32;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src, srcw));
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_S32_F32, op & SLJIT_32, TMP_FREG1, src, 0)));
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, VMOV | (1 << 20) | RD(dst) | VN(TMP_FREG1));
+
+ /* Store the integer value from a VFP register. */
+ return emit_fop_mem(compiler, 0, TMP_FREG1, dst, dstw);
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ op ^= SLJIT_32;
+
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, VMOV | RD(src) | VN(TMP_FREG1)));
+ else if (src & SLJIT_MEM) {
+ /* Load the integer value into a VFP register. */
+ FAIL_IF(emit_fop_mem(compiler, FPU_LOAD, TMP_FREG1, src, srcw));
+ }
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)srcw));
+ FAIL_IF(push_inst(compiler, VMOV | RD(TMP_REG1) | VN(TMP_FREG1)));
+ }
+
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_F32_S32, op & SLJIT_32, dst_r, TMP_FREG1, 0)));
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ op ^= SLJIT_32;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src1, src1w));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG2, src2, src2w));
+ src2 = TMP_FREG2;
+ }
+
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCMP_F32, op & SLJIT_32, src1, src2, 0)));
+ return push_inst(compiler, VMRS);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, dst_r, src, srcw));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32, op & SLJIT_32, dst_r, src, 0)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VNEG_F32, op & SLJIT_32, dst_r, src, 0)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VABS_F32, op & SLJIT_32, dst_r, src, 0)));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VCVT_F64_F32, op & SLJIT_32, dst_r, src, 0)));
+ op ^= SLJIT_32;
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, (op & SLJIT_32), dst_r, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG2, src2, src2w));
+ src2 = TMP_FREG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src1, src1w));
+ src1 = TMP_FREG1;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VADD_F32, op & SLJIT_32, dst_r, src2, src1)));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VSUB_F32, op & SLJIT_32, dst_r, src2, src1)));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMUL_F32, op & SLJIT_32, dst_r, src2, src1)));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VDIV_F32, op & SLJIT_32, dst_r, src2, src1)));
+ break;
+ }
+
+ if (dst_r == TMP_FREG1)
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw));
+
+ return SLJIT_SUCCESS;
+}
+
+#undef EMIT_FPU_DATA_TRANSFER
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, MOV | RD(dst) | RM(TMP_REG2));
+
+ /* Memory. */
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ return 0x00000000;
+
+ case SLJIT_NOT_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ return 0x10000000;
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x20000000;
+ /* fallthrough */
+
+ case SLJIT_LESS:
+ return 0x30000000;
+
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x30000000;
+ /* fallthrough */
+
+ case SLJIT_GREATER_EQUAL:
+ return 0x20000000;
+
+ case SLJIT_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return 0x80000000;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return 0x90000000;
+
+ case SLJIT_SIG_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ return 0xb0000000;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return 0xa0000000;
+
+ case SLJIT_SIG_GREATER:
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ return 0xc0000000;
+
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return 0xd0000000;
+
+ case SLJIT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x10000000;
+ /* fallthrough */
+
+ case SLJIT_UNORDERED:
+ return 0x60000000;
+
+ case SLJIT_NOT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x00000000;
+ /* fallthrough */
+
+ case SLJIT_ORDERED:
+ return 0x70000000;
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ return 0x40000000;
+
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return 0x50000000;
+
+ default:
+ SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_REG_ARG);
+ return 0xe0000000;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (type >= SLJIT_FAST_CALL)
+ PTR_FAIL_IF(prepare_blx(compiler));
+ PTR_FAIL_IF(push_inst_with_unique_literal(compiler, ((EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1,
+ type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0)) & ~COND_MASK) | get_cc(compiler, type), 0));
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP) {
+ jump->addr = compiler->size;
+ compiler->patches++;
+ }
+
+ if (type >= SLJIT_FAST_CALL) {
+ jump->flags |= IS_BL;
+ PTR_FAIL_IF(emit_blx(compiler));
+ }
+
+ if (!(jump->flags & SLJIT_REWRITABLE_JUMP))
+ jump->addr = compiler->size;
+#else
+ if (type >= SLJIT_FAST_CALL)
+ jump->flags |= IS_BL;
+ PTR_FAIL_IF(emit_imm(compiler, TMP_REG1, 0));
+ PTR_FAIL_IF(push_inst(compiler, (((type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1)) & ~COND_MASK) | get_cc(compiler, type)));
+ jump->addr = compiler->size;
+#endif
+ return jump;
+}
+
+#ifdef __SOFTFP__
+
+static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src, sljit_u32 *extra_space)
+{
+ sljit_u32 is_tail_call = *extra_space & SLJIT_CALL_RETURN;
+ sljit_u32 offset = 0;
+ sljit_u32 word_arg_offset = 0;
+ sljit_u32 src_offset = 4 * sizeof(sljit_sw);
+ sljit_u32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_u8 offsets[4];
+ sljit_u8 *offset_ptr = offsets;
+
+ if (src && FAST_IS_REG(*src))
+ src_offset = (sljit_uw)reg_map[*src] * sizeof(sljit_sw);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7)
+ offset += sizeof(sljit_sw);
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_f64);
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_f32);
+ float_arg_count++;
+ break;
+ default:
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_sw);
+ word_arg_offset += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (offset > 4 * sizeof(sljit_sw) && (!is_tail_call || offset > compiler->args_size)) {
+ /* Keep lr register on the stack. */
+ if (is_tail_call)
+ offset += sizeof(sljit_sw);
+
+ offset = ((offset - 4 * sizeof(sljit_sw)) + 0x7) & ~(sljit_uw)0x7;
+
+ *extra_space = offset;
+
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, (sljit_s32)offset));
+ else
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | offset));
+ } else {
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, -1));
+ *extra_space = 0;
+ }
+
+ /* Process arguments in reversed direction. */
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count--;
+ offset = *(--offset_ptr);
+
+ SLJIT_ASSERT((offset & 0x7) == 0);
+
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset || src_offset == offset + sizeof(sljit_sw)) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst(compiler, VMOV2 | 0x100000 | (offset << 10) | ((offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ } else
+ FAIL_IF(push_inst(compiler, VSTR_F32 | 0x800100 | RN(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count--;
+ offset = *(--offset_ptr);
+
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst(compiler, VMOV | 0x100000 | (float_arg_count << 16) | (offset << 10)));
+ } else
+ FAIL_IF(push_inst(compiler, VSTR_F32 | 0x800000 | RN(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ break;
+ default:
+ word_arg_offset -= sizeof(sljit_sw);
+ offset = *(--offset_ptr);
+
+ SLJIT_ASSERT(offset >= word_arg_offset);
+
+ if (offset != word_arg_offset) {
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | (src_offset >> 2)));
+ *src = TMP_REG1;
+ }
+ else if (src_offset == word_arg_offset) {
+ *src = (sljit_s32)(SLJIT_R0 + (offset >> 2));
+ src_offset = offset;
+ }
+ FAIL_IF(push_inst(compiler, MOV | (offset << 10) | (word_arg_offset >> 2)));
+ } else
+ FAIL_IF(push_inst(compiler, STR | 0x800000 | RN(SLJIT_SP) | (word_arg_offset << 10) | (offset - 4 * sizeof(sljit_sw))));
+ }
+ break;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 softfloat_post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ if ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64)
+ FAIL_IF(push_inst(compiler, VMOV2 | (1 << 16) | (0 << 12) | 0));
+ if ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F32)
+ FAIL_IF(push_inst(compiler, VMOV | (0 << 16) | (0 << 12)));
+
+ return SLJIT_SUCCESS;
+}
+
+#else /* !__SOFTFP__ */
+
+static sljit_s32 hardfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_u32 offset = SLJIT_FR0;
+ sljit_u32 new_offset = SLJIT_FR0;
+ sljit_u32 f32_offset = 0;
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset != new_offset)
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32,
+ SLJIT_32, new_offset, offset, 0)));
+
+ new_offset++;
+ offset++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (f32_offset != 0) {
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32,
+ 0x400000, f32_offset, offset, 0)));
+ f32_offset = 0;
+ } else {
+ if (offset != new_offset)
+ FAIL_IF(push_inst(compiler, EMIT_FPU_OPERATION(VMOV_F32,
+ 0, new_offset, offset, 0)));
+ f32_offset = new_offset;
+ new_offset++;
+ }
+ offset++;
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif /* __SOFTFP__ */
+
+#undef EMIT_FPU_OPERATION
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#ifdef __SOFTFP__
+ struct sljit_jump *jump;
+ sljit_u32 extra_space = (sljit_u32)type;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#ifdef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL, &extra_space));
+ SLJIT_ASSERT((extra_space & 0x7) == 0);
+
+ if ((type & SLJIT_CALL_RETURN) && extra_space == 0)
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ if (extra_space > 0) {
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1,
+ TMP_REG2, SLJIT_SP, extra_space - sizeof(sljit_sw))));
+
+ PTR_FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | extra_space));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(push_inst(compiler, BX | RM(TMP_REG2)));
+ return jump;
+ }
+ }
+
+ SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN));
+ PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types));
+ return jump;
+ }
+#endif /* __SOFTFP__ */
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, -1));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+#ifndef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+#endif /* !__SOFTFP__ */
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
+ if (!(src & SLJIT_IMM)) {
+ if (FAST_IS_REG(src)) {
+ SLJIT_ASSERT(reg_map[src] != 14);
+ return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(src));
+ }
+
+ SLJIT_ASSERT(src & SLJIT_MEM);
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1));
+ }
+
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (type >= SLJIT_FAST_CALL)
+ FAIL_IF(prepare_blx(compiler));
+ FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0), 0));
+ if (type >= SLJIT_FAST_CALL)
+ FAIL_IF(emit_blx(compiler));
+#else
+ FAIL_IF(emit_imm(compiler, TMP_REG1, 0));
+ FAIL_IF(push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1)));
+#endif
+ jump->addr = compiler->size;
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+#ifdef __SOFTFP__
+ sljit_u32 extra_space = (sljit_u32)type;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ if ((type & SLJIT_CALL_RETURN) && (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG1) | RM(src)));
+ src = TMP_REG1;
+ }
+
+#ifdef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src, &extra_space));
+ SLJIT_ASSERT((extra_space & 0x7) == 0);
+
+ if ((type & SLJIT_CALL_RETURN) && extra_space == 0)
+ type = SLJIT_JUMP;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (extra_space > 0) {
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1,
+ TMP_REG2, SLJIT_SP, extra_space - sizeof(sljit_sw))));
+
+ FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RN(SLJIT_SP) | SRC2_IMM | extra_space));
+
+ if (type & SLJIT_CALL_RETURN)
+ return push_inst(compiler, BX | RM(TMP_REG2));
+ }
+
+ SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN));
+ return softfloat_post_call_with_args(compiler, arg_types);
+ }
+#endif /* __SOFTFP__ */
+
+ if (type & SLJIT_CALL_RETURN) {
+ FAIL_IF(emit_stack_frame_release(compiler, -1));
+ type = SLJIT_JUMP;
+ }
+
+#ifndef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+#endif /* !__SOFTFP__ */
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+#ifdef __SOFTFP__
+
+static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ if (src == SLJIT_FR0)
+ return SLJIT_SUCCESS;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw);
+ }
+
+ if (FAST_IS_REG(src)) {
+ if (op & SLJIT_32)
+ return push_inst(compiler, VMOV | (1 << 20) | RD(SLJIT_R0) | VN(src));
+ return push_inst(compiler, VMOV2 | (1 << 20) | RD(SLJIT_R0) | RN(SLJIT_R1) | VM(src));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (op & SLJIT_32)
+ return sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, src, srcw);
+ return sljit_emit_mem(compiler, SLJIT_MOV, SLJIT_REG_PAIR(SLJIT_R0, SLJIT_R1), src, srcw);
+}
+
+#endif /* __SOFTFP__ */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 dst_reg, flags = GET_ALL_FLAGS(op);
+ sljit_uw cc, ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ cc = get_cc(compiler, type);
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (op < SLJIT_ADD) {
+ FAIL_IF(push_inst(compiler, MOV | RD(dst_reg) | SRC2_IMM | 0));
+ FAIL_IF(push_inst(compiler, ((MOV | RD(dst_reg) | SRC2_IMM | 1) & ~COND_MASK) | cc));
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+ }
+
+ ins = (op == SLJIT_AND ? AND : (op == SLJIT_OR ? ORR : EOR));
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, dst, dstw, TMP_REG2));
+
+ FAIL_IF(push_inst(compiler, ((ins | RD(dst_reg) | RN(dst_reg) | SRC2_IMM | 1) & ~COND_MASK) | cc));
+
+ if (op == SLJIT_AND)
+ FAIL_IF(push_inst(compiler, ((ins | RD(dst_reg) | RN(dst_reg) | SRC2_IMM | 0) & ~COND_MASK) | (cc ^ 0x10000000)));
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (flags & SLJIT_SET_Z)
+ return push_inst(compiler, MOV | SET_FLAGS | RD(TMP_REG2) | RM(dst_reg));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_uw cc, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ cc = get_cc(compiler, type & ~SLJIT_32);
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ tmp = get_imm((sljit_uw)srcw);
+ if (tmp)
+ return push_inst(compiler, ((MOV | RD(dst_reg) | tmp) & ~COND_MASK) | cc);
+
+ tmp = get_imm(~(sljit_uw)srcw);
+ if (tmp)
+ return push_inst(compiler, ((MVN | RD(dst_reg) | tmp) & ~COND_MASK) | cc);
+
+#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7)
+ tmp = (sljit_uw)srcw;
+ FAIL_IF(push_inst(compiler, (MOVW & ~COND_MASK) | cc | RD(dst_reg) | ((tmp << 4) & 0xf0000) | (tmp & 0xfff)));
+ if (tmp <= 0xffff)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, (MOVT & ~COND_MASK) | cc | RD(dst_reg) | ((tmp >> 12) & 0xf0000) | ((tmp >> 16) & 0xfff));
+#else
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)srcw));
+ src = TMP_REG1;
+#endif
+ }
+
+ return push_inst(compiler, ((MOV | RD(dst_reg) | RM(src)) & ~COND_MASK) | cc);
+}
+
+static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s32 max_offset)
+{
+ sljit_s32 arg = *mem;
+ sljit_sw argw = *memw;
+ sljit_uw imm, tmp;
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_sw mask = max_offset >= 0xf00 ? 0xfff : 0xff;
+ sljit_sw sign = max_offset >= 0xf00 ? 0x1000 : 0x100;
+#else /* !SLJIT_CONFIG_ARM_V5 */
+ sljit_sw mask = 0xfff;
+ sljit_sw sign = 0x1000;
+
+ SLJIT_ASSERT(max_offset >= 0xf00);
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ *mem = TMP_REG1;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ *memw = 0;
+ return push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((sljit_uw)(argw & 0x3) << 7));
+ }
+
+ arg &= REG_MASK;
+
+ if (arg) {
+ if (argw <= max_offset && argw >= -mask) {
+ *mem = arg;
+ return SLJIT_SUCCESS;
+ }
+
+ if (argw >= 0) {
+ tmp = (sljit_uw)(argw & (sign | mask));
+ tmp = (sljit_uw)((argw + ((tmp <= (sljit_uw)max_offset || tmp == (sljit_uw)sign) ? 0 : sign)) & ~mask);
+ imm = get_imm(tmp);
+
+ if (imm) {
+ *memw = argw - (sljit_sw)tmp;
+ SLJIT_ASSERT(*memw >= -mask && *memw <= max_offset);
+
+ return push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg) | imm);
+ }
+ } else {
+ tmp = (sljit_uw)(-argw & (sign | mask));
+ tmp = (sljit_uw)((-argw + ((tmp <= (sljit_uw)((sign << 1) - max_offset - 1)) ? 0 : sign)) & ~mask);
+ imm = get_imm(tmp);
+
+ if (imm) {
+ *memw = argw + (sljit_sw)tmp;
+ SLJIT_ASSERT(*memw >= -mask && *memw <= max_offset);
+
+ return push_inst(compiler, SUB | RD(TMP_REG1) | RN(arg) | imm);
+ }
+ }
+ }
+
+ tmp = (sljit_uw)(argw & (sign | mask));
+ tmp = (sljit_uw)((argw + ((tmp <= (sljit_uw)max_offset || tmp == (sljit_uw)sign) ? 0 : sign)) & ~mask);
+ *memw = argw - (sljit_sw)tmp;
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, tmp));
+
+ if (arg == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADD | RD(TMP_REG1) | RN(TMP_REG1) | RM(arg));
+}
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+
+static sljit_s32 sljit_emit_mem_unaligned(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags, steps, tmp_reg;
+ sljit_uw add, shift;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ flags = BYTE_SIZE;
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+ if ((type & 0xff) == SLJIT_MOV_S8)
+ flags |= SIGNED;
+
+ return emit_op_mem(compiler, flags, reg, mem, memw, TMP_REG1);
+
+ case SLJIT_MOV_U16:
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 1));
+ flags = BYTE_SIZE;
+ steps = 1;
+ break;
+
+ case SLJIT_MOV_S16:
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xff - 1));
+ flags = BYTE_SIZE | SIGNED;
+ steps = 1;
+ break;
+
+ default:
+ if (type & SLJIT_MEM_UNALIGNED_32) {
+ flags = WORD_SIZE;
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ return emit_op_mem(compiler, flags, reg, mem, memw, TMP_REG1);
+ }
+
+ if (!(type & SLJIT_MEM_UNALIGNED_16)) {
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 3));
+ flags = BYTE_SIZE;
+ steps = 3;
+ break;
+ }
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xff - 2));
+
+ add = 1;
+ if (memw < 0) {
+ add = 0;
+ memw = -memw;
+ }
+
+ tmp_reg = reg;
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE, add, reg, mem, TYPE2_TRANSFER_IMM(memw))));
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(reg) | (16 << 7) | (2 << 4)));
+ } else {
+ if (reg == mem) {
+ SLJIT_ASSERT(reg != TMP_REG1);
+ tmp_reg = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE | LOAD_DATA, add, tmp_reg, mem, TYPE2_TRANSFER_IMM(memw))));
+ }
+
+ if (!add) {
+ memw -= 2;
+ if (memw <= 0) {
+ memw = -memw;
+ add = 1;
+ }
+ } else
+ memw += 2;
+
+ if (type & SLJIT_MEM_STORE)
+ return push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw)));
+
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(HALF_SIZE | LOAD_DATA, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw))));
+ return push_inst(compiler, ORR | RD(reg) | RN(tmp_reg) | RM(TMP_REG2) | (16 << 7));
+ }
+
+ SLJIT_ASSERT(steps > 0);
+
+ add = 1;
+ if (memw < 0) {
+ add = 0;
+ memw = -memw;
+ }
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE, add, reg, mem, memw)));
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(reg) | (8 << 7) | (2 << 4)));
+
+ while (1) {
+ if (!add) {
+ memw -= 1;
+ if (memw == 0)
+ add = 1;
+ } else
+ memw += 1;
+
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE, add, TMP_REG2, mem, memw)));
+
+ if (--steps == 0)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, MOV | RD(TMP_REG2) | RM(TMP_REG2) | (8 << 7) | (2 << 4)));
+ }
+ }
+
+ tmp_reg = reg;
+
+ if (reg == mem) {
+ SLJIT_ASSERT(reg != TMP_REG1);
+ tmp_reg = TMP_REG1;
+ }
+
+ shift = 8;
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE | LOAD_DATA, add, tmp_reg, mem, memw)));
+
+ do {
+ if (!add) {
+ memw -= 1;
+ if (memw == 0)
+ add = 1;
+ } else
+ memw += 1;
+
+ if (steps > 1) {
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(BYTE_SIZE | LOAD_DATA, add, TMP_REG2, mem, memw)));
+ FAIL_IF(push_inst(compiler, ORR | RD(tmp_reg) | RN(tmp_reg) | RM(TMP_REG2) | (shift << 7)));
+ shift += 8;
+ }
+ } while (--steps != 0);
+
+ flags |= LOAD_DATA;
+
+ if (flags & SIGNED)
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(flags, add, TMP_REG2, mem, TYPE2_TRANSFER_IMM(memw))));
+ else
+ FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(flags, add, TMP_REG2, mem, memw)));
+
+ return push_inst(compiler, ORR | RD(reg) | RN(tmp_reg) | RM(TMP_REG2) | (shift << 7));
+}
+
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK)) {
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ ADJUST_LOCAL_OFFSET(mem, memw);
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+ }
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (type & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16)) {
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, (type & SLJIT_MEM_UNALIGNED_16) ? 0xfff - 6 : 0xfff - 7));
+
+ if (!(type & SLJIT_MEM_STORE) && REG_PAIR_FIRST(reg) == (mem & REG_MASK)) {
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, type, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw)));
+ return sljit_emit_mem_unaligned(compiler, type, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw);
+ }
+
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, type, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw));
+ return sljit_emit_mem_unaligned(compiler, type, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw));
+ }
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+
+ flags = WORD_SIZE;
+
+ if (!(type & SLJIT_MEM_STORE)) {
+ if (REG_PAIR_FIRST(reg) == (mem & REG_MASK)) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw), TMP_REG1));
+ return emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw, TMP_REG1);
+ }
+
+ flags = WORD_SIZE | LOAD_DATA;
+ }
+
+ FAIL_IF(emit_op_mem(compiler, flags, REG_PAIR_FIRST(reg), SLJIT_MEM1(mem), memw, TMP_REG1));
+ return emit_op_mem(compiler, flags, REG_PAIR_SECOND(reg), SLJIT_MEM1(mem), memw + SSIZE_OF(sw), TMP_REG1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+ sljit_uw is_type1_transfer, inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+
+ is_type1_transfer = 1;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ flags = BYTE_SIZE;
+ break;
+ case SLJIT_MOV_S8:
+ if (!(type & SLJIT_MEM_STORE))
+ is_type1_transfer = 0;
+ flags = BYTE_SIZE | SIGNED;
+ break;
+ case SLJIT_MOV_U16:
+ is_type1_transfer = 0;
+ flags = HALF_SIZE;
+ break;
+ case SLJIT_MOV_S16:
+ is_type1_transfer = 0;
+ flags = HALF_SIZE | SIGNED;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ flags = WORD_SIZE;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ SLJIT_ASSERT(is_type1_transfer == !!IS_TYPE1_TRANSFER(flags));
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (!is_type1_transfer && memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+ } else {
+ if (is_type1_transfer) {
+ if (memw > 4095 || memw < -4095)
+ return SLJIT_ERR_UNSUPPORTED;
+ } else if (memw > 255 || memw < -255)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ memw &= 0x3;
+
+ inst = EMIT_DATA_TRANSFER(flags, 1, reg, mem & REG_MASK, RM(OFFS_REG(mem)) | ((sljit_uw)memw << 7));
+
+ if (is_type1_transfer)
+ inst |= (1 << 25);
+
+ if (type & SLJIT_MEM_POST)
+ inst ^= (1 << 24);
+ else
+ inst |= (1 << 21);
+
+ return push_inst(compiler, inst);
+ }
+
+ inst = EMIT_DATA_TRANSFER(flags, 0, reg, mem & REG_MASK, 0);
+
+ if (type & SLJIT_MEM_POST)
+ inst ^= (1 << 24);
+ else
+ inst |= (1 << 21);
+
+ if (is_type1_transfer) {
+ if (memw >= 0)
+ inst |= (1 << 23);
+ else
+ memw = -memw;
+
+ return push_inst(compiler, inst | (sljit_uw)memw);
+ }
+
+ if (memw >= 0)
+ inst |= (1 << 23);
+ else
+ memw = -memw;
+
+ return push_inst(compiler, inst | TYPE2_TRANSFER_IMM((sljit_uw)memw));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ sljit_s32 max_offset;
+ sljit_s32 dst;
+#endif /* SLJIT_CONFIG_ARM_V5 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ if (type & SLJIT_MEM_UNALIGNED_32)
+ return emit_fop_mem(compiler, ((type ^ SLJIT_32) & SLJIT_32) | ((type & SLJIT_MEM_STORE) ? 0 : FPU_LOAD), freg, mem, memw);
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | RD(TMP_REG2)));
+
+ if (type & SLJIT_32)
+ return sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw);
+
+ max_offset = 0xfff - 7;
+ if (type & SLJIT_MEM_UNALIGNED_16)
+ max_offset++;
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, max_offset));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw));
+
+ FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | 0x80 | RD(TMP_REG2)));
+ return sljit_emit_mem_unaligned(compiler, SLJIT_MOV | SLJIT_MEM_STORE | (type & SLJIT_MEM_UNALIGNED_16), TMP_REG2, mem, memw + 4);
+ }
+
+ max_offset = (type & SLJIT_32) ? 0xfff - 3 : 0xfff - 7;
+ if (type & SLJIT_MEM_UNALIGNED_16)
+ max_offset++;
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, max_offset));
+
+ dst = TMP_REG1;
+
+ /* Stack offset adjustment is not needed because dst
+ is not stored on the stack when mem is SLJIT_SP. */
+
+ if (mem == TMP_REG1) {
+ dst = SLJIT_R3;
+
+ if (compiler->scratches >= 4)
+ FAIL_IF(push_inst(compiler, STR | (1 << 21) | RN(SLJIT_SP) | RD(SLJIT_R3) | 8));
+ }
+
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | (type & SLJIT_MEM_UNALIGNED_16), dst, mem, memw));
+ FAIL_IF(push_inst(compiler, VMOV | VN(freg) | RD(dst)));
+
+ if (!(type & SLJIT_32)) {
+ FAIL_IF(sljit_emit_mem_unaligned(compiler, SLJIT_MOV | (type & SLJIT_MEM_UNALIGNED_16), dst, mem, memw + 4));
+ FAIL_IF(push_inst(compiler, VMOV | VN(freg) | 0x80 | RD(dst)));
+ }
+
+ if (dst == SLJIT_R3 && compiler->scratches >= 4)
+ FAIL_IF(push_inst(compiler, (LDR ^ (0x1 << 24)) | (0x1 << 23) | RN(SLJIT_SP) | RD(SLJIT_R3) | 8));
+ return SLJIT_SUCCESS;
+#else /* !SLJIT_CONFIG_ARM_V5 */
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | RD(TMP_REG2)));
+
+ if (type & SLJIT_32)
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1);
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1));
+ FAIL_IF(push_inst(compiler, VMOV | (1 << 20) | VN(freg) | 0x80 | RD(TMP_REG2)));
+ return emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw + 4, TMP_REG1);
+ }
+
+ if (type & SLJIT_32) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, mem, memw, TMP_REG1));
+ return push_inst(compiler, VMOV | VN(freg) | RD(TMP_REG2));
+ }
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG2, mem, memw, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | LOAD_DATA, TMP_REG1, mem, memw + 4, TMP_REG1));
+ return push_inst(compiler, VMOV2 | VM(freg) | RD(TMP_REG2) | RN(TMP_REG1));
+#endif /* SLJIT_CONFIG_ARM_V5 */
+}
+
+#undef FPU_LOAD
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ PTR_FAIL_IF(push_inst_with_unique_literal(compiler,
+ EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, dst_r, TMP_PC, 0), (sljit_uw)init_value));
+ compiler->patches++;
+#else
+ PTR_FAIL_IF(emit_imm(compiler, dst_r, init_value));
+#endif
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1));
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+
+#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5)
+ PTR_FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_SIZE | LOAD_DATA, 1, dst_r, TMP_PC, 0), 0));
+ compiler->patches++;
+#else
+ PTR_FAIL_IF(emit_imm(compiler, dst_r, 0));
+#endif
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, dst, dstw, TMP_REG1));
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ inline_set_jump_addr(addr, executable_offset, new_target, 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ inline_set_const(addr, executable_offset, (sljit_uw)new_constant, 1);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeARM_64.c b/contrib/libs/pcre2/src/sljit/sljitNativeARM_64.c
new file mode 100644
index 0000000000..89f747e7c8
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeARM_64.c
@@ -0,0 +1,2417 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "ARM-64" SLJIT_CPUINFO;
+}
+
+/* Length of an instruction word */
+typedef sljit_u32 sljit_ins;
+
+#define TMP_ZERO (0)
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_LR (SLJIT_NUMBER_OF_REGISTERS + 4)
+#define TMP_FP (SLJIT_NUMBER_OF_REGISTERS + 5)
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+/* r18 - platform register, currently not used */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 8] = {
+ 31, 0, 1, 2, 3, 4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 17, 8, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 31, 9, 10, 30, 29
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 15, 14, 13, 12, 11, 10, 9, 8, 30, 31
+};
+
+#define W_OP ((sljit_ins)1 << 31)
+#define RD(rd) ((sljit_ins)reg_map[rd])
+#define RT(rt) ((sljit_ins)reg_map[rt])
+#define RN(rn) ((sljit_ins)reg_map[rn] << 5)
+#define RT2(rt2) ((sljit_ins)reg_map[rt2] << 10)
+#define RM(rm) ((sljit_ins)reg_map[rm] << 16)
+#define VD(vd) ((sljit_ins)freg_map[vd])
+#define VT(vt) ((sljit_ins)freg_map[vt])
+#define VT2(vt) ((sljit_ins)freg_map[vt] << 10)
+#define VN(vn) ((sljit_ins)freg_map[vn] << 5)
+#define VM(vm) ((sljit_ins)freg_map[vm] << 16)
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+#define ADC 0x9a000000
+#define ADD 0x8b000000
+#define ADDE 0x8b200000
+#define ADDI 0x91000000
+#define AND 0x8a000000
+#define ANDI 0x92000000
+#define ASRV 0x9ac02800
+#define B 0x14000000
+#define B_CC 0x54000000
+#define BL 0x94000000
+#define BLR 0xd63f0000
+#define BR 0xd61f0000
+#define BRK 0xd4200000
+#define CBZ 0xb4000000
+#define CLZ 0xdac01000
+#define CSEL 0x9a800000
+#define CSINC 0x9a800400
+#define EOR 0xca000000
+#define EORI 0xd2000000
+#define EXTR 0x93c00000
+#define FABS 0x1e60c000
+#define FADD 0x1e602800
+#define FCMP 0x1e602000
+#define FCVT 0x1e224000
+#define FCVTZS 0x9e780000
+#define FDIV 0x1e601800
+#define FMOV 0x1e604000
+#define FMUL 0x1e600800
+#define FNEG 0x1e614000
+#define FSUB 0x1e603800
+#define LDRI 0xf9400000
+#define LDRI_F64 0xfd400000
+#define LDRI_POST 0xf8400400
+#define LDP 0xa9400000
+#define LDP_F64 0x6d400000
+#define LDP_POST 0xa8c00000
+#define LDR_PRE 0xf8400c00
+#define LSLV 0x9ac02000
+#define LSRV 0x9ac02400
+#define MADD 0x9b000000
+#define MOVK 0xf2800000
+#define MOVN 0x92800000
+#define MOVZ 0xd2800000
+#define NOP 0xd503201f
+#define ORN 0xaa200000
+#define ORR 0xaa000000
+#define ORRI 0xb2000000
+#define RBIT 0xdac00000
+#define RET 0xd65f0000
+#define RORV 0x9ac02c00
+#define SBC 0xda000000
+#define SBFM 0x93000000
+#define SCVTF 0x9e620000
+#define SDIV 0x9ac00c00
+#define SMADDL 0x9b200000
+#define SMULH 0x9b403c00
+#define STP 0xa9000000
+#define STP_F64 0x6d000000
+#define STP_PRE 0xa9800000
+#define STRB 0x38206800
+#define STRBI 0x39000000
+#define STRI 0xf9000000
+#define STRI_F64 0xfd000000
+#define STR_FI 0x3d000000
+#define STR_FR 0x3c206800
+#define STUR_FI 0x3c000000
+#define STURBI 0x38000000
+#define SUB 0xcb000000
+#define SUBI 0xd1000000
+#define SUBS 0xeb000000
+#define UBFM 0xd3000000
+#define UDIV 0x9ac00800
+#define UMULH 0x9bc03c00
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins)
+{
+ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ptr);
+ *ptr = ins;
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_imm64_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_uw imm)
+{
+ FAIL_IF(push_inst(compiler, MOVZ | RD(dst) | ((sljit_ins)(imm & 0xffff) << 5)));
+ FAIL_IF(push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)(imm >> 16) & 0xffff) << 5) | (1 << 21)));
+ FAIL_IF(push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)(imm >> 32) & 0xffff) << 5) | (2 << 21)));
+ return push_inst(compiler, MOVK | RD(dst) | ((sljit_ins)(imm >> 48) << 5) | (3 << 21));
+}
+
+static SLJIT_INLINE sljit_sw detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+ sljit_uw target_addr;
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP) {
+ jump->flags |= PATCH_ABS64;
+ return 0;
+ }
+
+ if (jump->flags & JUMP_ADDR)
+ target_addr = jump->u.target;
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset;
+ }
+
+ diff = (sljit_sw)target_addr - (sljit_sw)(code_ptr + 4) - executable_offset;
+
+ if (jump->flags & IS_COND) {
+ diff += SSIZE_OF(ins);
+ if (diff <= 0xfffff && diff >= -0x100000) {
+ code_ptr[-5] ^= (jump->flags & IS_CBZ) ? (0x1 << 24) : 0x1;
+ jump->addr -= sizeof(sljit_ins);
+ jump->flags |= PATCH_COND;
+ return 5;
+ }
+ diff -= SSIZE_OF(ins);
+ }
+
+ if (diff <= 0x7ffffff && diff >= -0x8000000) {
+ jump->flags |= PATCH_B;
+ return 4;
+ }
+
+ if (target_addr < 0x100000000l) {
+ if (jump->flags & IS_COND)
+ code_ptr[-5] -= (2 << 5);
+ code_ptr[-2] = code_ptr[0];
+ return 2;
+ }
+
+ if (target_addr < 0x1000000000000l) {
+ if (jump->flags & IS_COND)
+ code_ptr[-5] -= (1 << 5);
+ jump->flags |= PATCH_ABS48;
+ code_ptr[-1] = code_ptr[0];
+ return 1;
+ }
+
+ jump->flags |= PATCH_ABS64;
+ return 0;
+}
+
+static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label)
+{
+ if (max_label < 0x100000000l) {
+ put_label->flags = 0;
+ return 2;
+ }
+
+ if (max_label < 0x1000000000000l) {
+ put_label->flags = 1;
+ return 1;
+ }
+
+ put_label->flags = 2;
+ return 0;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_ins *code;
+ sljit_ins *code_ptr;
+ sljit_ins *buf_ptr;
+ sljit_ins *buf_end;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_sw addr;
+ sljit_u32 dst;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_ins*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == word_count) {
+ jump->addr = (sljit_uw)(code_ptr - 4);
+ code_ptr -= detect_jump_type(jump, code_ptr, code, executable_offset);
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == word_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)(code_ptr - 3);
+ code_ptr -= put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size));
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ word_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ do {
+ addr = (sljit_sw)((jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target);
+ buf_ptr = (sljit_ins *)jump->addr;
+
+ if (jump->flags & PATCH_B) {
+ addr = (addr - (sljit_sw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2;
+ SLJIT_ASSERT(addr <= 0x1ffffff && addr >= -0x2000000);
+ buf_ptr[0] = ((jump->flags & IS_BL) ? BL : B) | (sljit_ins)(addr & 0x3ffffff);
+ if (jump->flags & IS_COND)
+ buf_ptr[-1] -= (4 << 5);
+ break;
+ }
+ if (jump->flags & PATCH_COND) {
+ addr = (addr - (sljit_sw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset)) >> 2;
+ SLJIT_ASSERT(addr <= 0x3ffff && addr >= -0x40000);
+ buf_ptr[0] = (buf_ptr[0] & ~(sljit_ins)0xffffe0) | (sljit_ins)((addr & 0x7ffff) << 5);
+ break;
+ }
+
+ SLJIT_ASSERT((jump->flags & (PATCH_ABS48 | PATCH_ABS64)) || (sljit_uw)addr <= (sljit_uw)0xffffffff);
+ SLJIT_ASSERT((jump->flags & PATCH_ABS64) || (sljit_uw)addr <= (sljit_uw)0xffffffffffff);
+
+ dst = buf_ptr[0] & 0x1f;
+ buf_ptr[0] = MOVZ | dst | (((sljit_ins)addr & 0xffff) << 5);
+ buf_ptr[1] = MOVK | dst | (((sljit_ins)(addr >> 16) & 0xffff) << 5) | (1 << 21);
+ if (jump->flags & (PATCH_ABS48 | PATCH_ABS64))
+ buf_ptr[2] = MOVK | dst | (((sljit_ins)(addr >> 32) & 0xffff) << 5) | (2 << 21);
+ if (jump->flags & PATCH_ABS64)
+ buf_ptr[3] = MOVK | dst | ((sljit_ins)(addr >> 48) << 5) | (3 << 21);
+ } while (0);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ addr = (sljit_sw)put_label->label->addr;
+ buf_ptr = (sljit_ins*)put_label->addr;
+
+ buf_ptr[0] |= ((sljit_ins)addr & 0xffff) << 5;
+ buf_ptr[1] |= ((sljit_ins)(addr >> 16) & 0xffff) << 5;
+
+ if (put_label->flags >= 1)
+ buf_ptr[2] |= ((sljit_ins)(addr >> 32) & 0xffff) << 5;
+
+ if (put_label->flags >= 2)
+ buf_ptr[3] |= (sljit_ins)(addr >> 48) << 5;
+
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins);
+
+ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CTZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_CMOV:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Core code generator functions. */
+/* --------------------------------------------------------------------- */
+
+#define COUNT_TRAILING_ZERO(value, result) \
+ result = 0; \
+ if (!(value & 0xffffffff)) { \
+ result += 32; \
+ value >>= 32; \
+ } \
+ if (!(value & 0xffff)) { \
+ result += 16; \
+ value >>= 16; \
+ } \
+ if (!(value & 0xff)) { \
+ result += 8; \
+ value >>= 8; \
+ } \
+ if (!(value & 0xf)) { \
+ result += 4; \
+ value >>= 4; \
+ } \
+ if (!(value & 0x3)) { \
+ result += 2; \
+ value >>= 2; \
+ } \
+ if (!(value & 0x1)) { \
+ result += 1; \
+ value >>= 1; \
+ }
+
+#define LOGICAL_IMM_CHECK (sljit_ins)0x100
+
+static sljit_ins logical_imm(sljit_sw imm, sljit_u32 len)
+{
+ sljit_s32 negated;
+ sljit_u32 ones, right;
+ sljit_uw mask, uimm;
+ sljit_ins ins;
+
+ if (len & LOGICAL_IMM_CHECK) {
+ len &= ~LOGICAL_IMM_CHECK;
+ if (len == 32 && (imm == 0 || imm == -1))
+ return 0;
+ if (len == 16 && ((sljit_s32)imm == 0 || (sljit_s32)imm == -1))
+ return 0;
+ }
+
+ SLJIT_ASSERT((len == 32 && imm != 0 && imm != -1)
+ || (len == 16 && (sljit_s32)imm != 0 && (sljit_s32)imm != -1));
+
+ uimm = (sljit_uw)imm;
+ while (1) {
+ if (len <= 0) {
+ SLJIT_UNREACHABLE();
+ return 0;
+ }
+
+ mask = ((sljit_uw)1 << len) - 1;
+ if ((uimm & mask) != ((uimm >> len) & mask))
+ break;
+ len >>= 1;
+ }
+
+ len <<= 1;
+
+ negated = 0;
+ if (uimm & 0x1) {
+ negated = 1;
+ uimm = ~uimm;
+ }
+
+ if (len < 64)
+ uimm &= ((sljit_uw)1 << len) - 1;
+
+ /* Unsigned right shift. */
+ COUNT_TRAILING_ZERO(uimm, right);
+
+ /* Signed shift. We also know that the highest bit is set. */
+ imm = (sljit_sw)~uimm;
+ SLJIT_ASSERT(imm < 0);
+
+ COUNT_TRAILING_ZERO(imm, ones);
+
+ if (~imm)
+ return 0;
+
+ if (len == 64)
+ ins = 1 << 22;
+ else
+ ins = (0x3f - ((len << 1) - 1)) << 10;
+
+ if (negated)
+ return ins | ((len - ones - 1) << 10) | ((len - ones - right) << 16);
+
+ return ins | ((ones - 1) << 10) | ((len - right) << 16);
+}
+
+#undef COUNT_TRAILING_ZERO
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw simm)
+{
+ sljit_uw imm = (sljit_uw)simm;
+ sljit_u32 i, zeros, ones, first;
+ sljit_ins bitmask;
+
+ /* Handling simple immediates first. */
+ if (imm <= 0xffff)
+ return push_inst(compiler, MOVZ | RD(dst) | ((sljit_ins)imm << 5));
+
+ if (simm < 0 && simm >= -0x10000)
+ return push_inst(compiler, MOVN | RD(dst) | (((sljit_ins)~imm & 0xffff) << 5));
+
+ if (imm <= 0xffffffffl) {
+ if ((imm & 0xffff) == 0)
+ return push_inst(compiler, MOVZ | RD(dst) | ((sljit_ins)(imm >> 16) << 5) | (1 << 21));
+ if ((imm & 0xffff0000l) == 0xffff0000)
+ return push_inst(compiler, (MOVN ^ W_OP) | RD(dst) | (((sljit_ins)~imm & 0xffff) << 5));
+ if ((imm & 0xffff) == 0xffff)
+ return push_inst(compiler, (MOVN ^ W_OP) | RD(dst) | (((sljit_ins)~imm & 0xffff0000u) >> (16 - 5)) | (1 << 21));
+
+ bitmask = logical_imm(simm, 16);
+ if (bitmask != 0)
+ return push_inst(compiler, (ORRI ^ W_OP) | RD(dst) | RN(TMP_ZERO) | bitmask);
+
+ FAIL_IF(push_inst(compiler, MOVZ | RD(dst) | (((sljit_ins)imm & 0xffff) << 5)));
+ return push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)imm & 0xffff0000u) >> (16 - 5)) | (1 << 21));
+ }
+
+ bitmask = logical_imm(simm, 32);
+ if (bitmask != 0)
+ return push_inst(compiler, ORRI | RD(dst) | RN(TMP_ZERO) | bitmask);
+
+ if (simm < 0 && simm >= -0x100000000l) {
+ if ((imm & 0xffff) == 0xffff)
+ return push_inst(compiler, MOVN | RD(dst) | (((sljit_ins)~imm & 0xffff0000u) >> (16 - 5)) | (1 << 21));
+
+ FAIL_IF(push_inst(compiler, MOVN | RD(dst) | (((sljit_ins)~imm & 0xffff) << 5)));
+ return push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)imm & 0xffff0000u) >> (16 - 5)) | (1 << 21));
+ }
+
+ /* A large amount of number can be constructed from ORR and MOVx, but computing them is costly. */
+
+ zeros = 0;
+ ones = 0;
+ for (i = 4; i > 0; i--) {
+ if ((simm & 0xffff) == 0)
+ zeros++;
+ if ((simm & 0xffff) == 0xffff)
+ ones++;
+ simm >>= 16;
+ }
+
+ simm = (sljit_sw)imm;
+ first = 1;
+ if (ones > zeros) {
+ simm = ~simm;
+ for (i = 0; i < 4; i++) {
+ if (!(simm & 0xffff)) {
+ simm >>= 16;
+ continue;
+ }
+ if (first) {
+ first = 0;
+ FAIL_IF(push_inst(compiler, MOVN | RD(dst) | (((sljit_ins)simm & 0xffff) << 5) | (i << 21)));
+ }
+ else
+ FAIL_IF(push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)~simm & 0xffff) << 5) | (i << 21)));
+ simm >>= 16;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (!(simm & 0xffff)) {
+ simm >>= 16;
+ continue;
+ }
+ if (first) {
+ first = 0;
+ FAIL_IF(push_inst(compiler, MOVZ | RD(dst) | (((sljit_ins)simm & 0xffff) << 5) | (i << 21)));
+ }
+ else
+ FAIL_IF(push_inst(compiler, MOVK | RD(dst) | (((sljit_ins)simm & 0xffff) << 5) | (i << 21)));
+ simm >>= 16;
+ }
+ return SLJIT_SUCCESS;
+}
+
+#define ARG1_IMM 0x0010000
+#define ARG2_IMM 0x0020000
+#define INT_OP 0x0040000
+#define SET_FLAGS 0x0080000
+#define UNUSED_RETURN 0x0100000
+
+#define CHECK_FLAGS(flag_bits) \
+ if (flags & SET_FLAGS) { \
+ inv_bits |= flag_bits; \
+ if (flags & UNUSED_RETURN) \
+ dst = TMP_ZERO; \
+ }
+
+static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 dst, sljit_sw arg1, sljit_sw arg2)
+{
+ /* dst must be register, TMP_REG1
+ arg1 must be register, TMP_REG1, imm
+ arg2 must be register, TMP_REG2, imm */
+ sljit_ins inv_bits = (flags & INT_OP) ? W_OP : 0;
+ sljit_ins inst_bits;
+ sljit_s32 op = (flags & 0xffff);
+ sljit_s32 reg;
+ sljit_sw imm, nimm;
+
+ if (SLJIT_UNLIKELY((flags & (ARG1_IMM | ARG2_IMM)) == (ARG1_IMM | ARG2_IMM))) {
+ /* Both are immediates. */
+ flags &= ~ARG1_IMM;
+ if (arg1 == 0 && op != SLJIT_ADD && op != SLJIT_SUB)
+ arg1 = TMP_ZERO;
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, arg1));
+ arg1 = TMP_REG1;
+ }
+ }
+
+ if (flags & (ARG1_IMM | ARG2_IMM)) {
+ reg = (sljit_s32)((flags & ARG2_IMM) ? arg1 : arg2);
+ imm = (flags & ARG2_IMM) ? arg2 : arg1;
+
+ switch (op) {
+ case SLJIT_MUL:
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ case SLJIT_ADDC:
+ case SLJIT_SUBC:
+ /* No form with immediate operand (except imm 0, which
+ is represented by a ZERO register). */
+ break;
+ case SLJIT_MOV:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG1);
+ return load_immediate(compiler, dst, imm);
+ case SLJIT_NOT:
+ SLJIT_ASSERT(flags & ARG2_IMM);
+ FAIL_IF(load_immediate(compiler, dst, (flags & INT_OP) ? (~imm & 0xffffffff) : ~imm));
+ goto set_flags;
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (flags & ARG1_IMM)
+ break;
+ imm = -imm;
+ /* Fall through. */
+ case SLJIT_ADD:
+ if (op != SLJIT_SUB)
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+
+ if (imm == 0) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, ((op == SLJIT_ADD ? ADDI : SUBI) ^ inv_bits) | RD(dst) | RN(reg));
+ }
+ if (imm > 0 && imm <= 0xfff) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (ADDI ^ inv_bits) | RD(dst) | RN(reg) | ((sljit_ins)imm << 10));
+ }
+ nimm = -imm;
+ if (nimm > 0 && nimm <= 0xfff) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (SUBI ^ inv_bits) | RD(dst) | RN(reg) | ((sljit_ins)nimm << 10));
+ }
+ if (imm > 0 && imm <= 0xffffff && !(imm & 0xfff)) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (ADDI ^ inv_bits) | RD(dst) | RN(reg) | (((sljit_ins)imm >> 12) << 10) | (1 << 22));
+ }
+ if (nimm > 0 && nimm <= 0xffffff && !(nimm & 0xfff)) {
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (SUBI ^ inv_bits) | RD(dst) | RN(reg) | (((sljit_ins)nimm >> 12) << 10) | (1 << 22));
+ }
+ if (imm > 0 && imm <= 0xffffff && !(flags & SET_FLAGS)) {
+ FAIL_IF(push_inst(compiler, (ADDI ^ inv_bits) | RD(dst) | RN(reg) | (((sljit_ins)imm >> 12) << 10) | (1 << 22)));
+ return push_inst(compiler, (ADDI ^ inv_bits) | RD(dst) | RN(dst) | (((sljit_ins)imm & 0xfff) << 10));
+ }
+ if (nimm > 0 && nimm <= 0xffffff && !(flags & SET_FLAGS)) {
+ FAIL_IF(push_inst(compiler, (SUBI ^ inv_bits) | RD(dst) | RN(reg) | (((sljit_ins)nimm >> 12) << 10) | (1 << 22)));
+ return push_inst(compiler, (SUBI ^ inv_bits) | RD(dst) | RN(dst) | (((sljit_ins)nimm & 0xfff) << 10));
+ }
+ break;
+ case SLJIT_AND:
+ inst_bits = logical_imm(imm, LOGICAL_IMM_CHECK | ((flags & INT_OP) ? 16 : 32));
+ if (!inst_bits)
+ break;
+ CHECK_FLAGS(3 << 29);
+ return push_inst(compiler, (ANDI ^ inv_bits) | RD(dst) | RN(reg) | inst_bits);
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ inst_bits = logical_imm(imm, LOGICAL_IMM_CHECK | ((flags & INT_OP) ? 16 : 32));
+ if (!inst_bits)
+ break;
+ if (op == SLJIT_OR)
+ inst_bits |= ORRI;
+ else
+ inst_bits |= EORI;
+ FAIL_IF(push_inst(compiler, (inst_bits ^ inv_bits) | RD(dst) | RN(reg)));
+ goto set_flags;
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ if (flags & ARG1_IMM)
+ break;
+
+ if (flags & INT_OP) {
+ imm &= 0x1f;
+ inst_bits = (((sljit_ins)-imm & 0x1f) << 16) | ((31 - (sljit_ins)imm) << 10);
+ } else {
+ imm &= 0x3f;
+ inst_bits = ((sljit_ins)1 << 22) | (((sljit_ins)-imm & 0x3f) << 16) | ((63 - (sljit_ins)imm) << 10);
+ }
+
+ FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) | inst_bits));
+ goto set_flags;
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ if (flags & ARG1_IMM)
+ break;
+
+ if (op >= SLJIT_ASHR)
+ inv_bits |= 1 << 30;
+
+ if (flags & INT_OP) {
+ imm &= 0x1f;
+ inst_bits = ((sljit_ins)imm << 16) | (31 << 10);
+ } else {
+ imm &= 0x3f;
+ inst_bits = ((sljit_ins)1 << 22) | ((sljit_ins)imm << 16) | (63 << 10);
+ }
+
+ FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(dst) | RN(arg1) | inst_bits));
+ goto set_flags;
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & ARG1_IMM)
+ break;
+
+ if (op == SLJIT_ROTL)
+ imm = -imm;
+
+ imm &= (flags & INT_OP) ? 0x1f : 0x3f;
+ return push_inst(compiler, (EXTR ^ (inv_bits | (inv_bits >> 9))) | RD(dst) | RN(arg1) | RM(arg1) | ((sljit_ins)imm << 10));
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ if (flags & ARG2_IMM) {
+ if (arg2 == 0)
+ arg2 = TMP_ZERO;
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, arg2));
+ arg2 = TMP_REG2;
+ }
+ }
+ else {
+ if (arg1 == 0)
+ arg1 = TMP_ZERO;
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, arg1));
+ arg1 = TMP_REG1;
+ }
+ }
+ }
+
+ /* Both arguments are registers. */
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ if (dst == arg2)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ORR | RD(dst) | RN(TMP_ZERO) | RM(arg2));
+ case SLJIT_MOV_U8:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ return push_inst(compiler, (UBFM ^ W_OP) | RD(dst) | RN(arg2) | (7 << 10));
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ if (!(flags & INT_OP))
+ inv_bits |= 1 << 22;
+ return push_inst(compiler, (SBFM ^ inv_bits) | RD(dst) | RN(arg2) | (7 << 10));
+ case SLJIT_MOV_U16:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ return push_inst(compiler, (UBFM ^ W_OP) | RD(dst) | RN(arg2) | (15 << 10));
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ if (!(flags & INT_OP))
+ inv_bits |= 1 << 22;
+ return push_inst(compiler, (SBFM ^ inv_bits) | RD(dst) | RN(arg2) | (15 << 10));
+ case SLJIT_MOV32:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ if (dst == arg2)
+ return SLJIT_SUCCESS;
+ /* fallthrough */
+ case SLJIT_MOV_U32:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ return push_inst(compiler, (ORR ^ W_OP) | RD(dst) | RN(TMP_ZERO) | RM(arg2));
+ case SLJIT_MOV_S32:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1);
+ return push_inst(compiler, SBFM | (1 << 22) | RD(dst) | RN(arg2) | (31 << 10));
+ case SLJIT_NOT:
+ SLJIT_ASSERT(arg1 == TMP_REG1);
+ FAIL_IF(push_inst(compiler, (ORN ^ inv_bits) | RD(dst) | RN(TMP_ZERO) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(arg1 == TMP_REG1);
+ return push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(arg2));
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(arg1 == TMP_REG1);
+ FAIL_IF(push_inst(compiler, (RBIT ^ inv_bits) | RD(dst) | RN(arg2)));
+ return push_inst(compiler, (CLZ ^ inv_bits) | RD(dst) | RN(dst));
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (ADD ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (ADC ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (SUB ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ CHECK_FLAGS(1 << 29);
+ return push_inst(compiler, (SBC ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_MUL:
+ compiler->status_flags_state = 0;
+ if (!(flags & SET_FLAGS))
+ return push_inst(compiler, (MADD ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2) | RT2(TMP_ZERO));
+ if (flags & INT_OP) {
+ FAIL_IF(push_inst(compiler, SMADDL | RD(dst) | RN(arg1) | RM(arg2) | (31 << 10)));
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_LR) | RN(TMP_ZERO) | RM(dst) | (2 << 22) | (31 << 10)));
+ return push_inst(compiler, SUBS | RD(TMP_ZERO) | RN(TMP_LR) | RM(dst) | (2 << 22) | (63 << 10));
+ }
+ FAIL_IF(push_inst(compiler, SMULH | RD(TMP_LR) | RN(arg1) | RM(arg2)));
+ FAIL_IF(push_inst(compiler, MADD | RD(dst) | RN(arg1) | RM(arg2) | RT2(TMP_ZERO)));
+ return push_inst(compiler, SUBS | RD(TMP_ZERO) | RN(TMP_LR) | RM(dst) | (2 << 22) | (63 << 10));
+ case SLJIT_AND:
+ CHECK_FLAGS(3 << 29);
+ return push_inst(compiler, (AND ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ case SLJIT_OR:
+ FAIL_IF(push_inst(compiler, (ORR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_XOR:
+ FAIL_IF(push_inst(compiler, (EOR ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ FAIL_IF(push_inst(compiler, (LSLV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ FAIL_IF(push_inst(compiler, (LSRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ FAIL_IF(push_inst(compiler, (ASRV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2)));
+ break; /* Set flags. */
+ case SLJIT_ROTL:
+ FAIL_IF(push_inst(compiler, (SUB ^ inv_bits) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(arg2)));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_ROTR:
+ return push_inst(compiler, (RORV ^ inv_bits) | RD(dst) | RN(arg1) | RM(arg2));
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+ }
+
+set_flags:
+ if (flags & SET_FLAGS)
+ return push_inst(compiler, (SUBS ^ inv_bits) | RD(TMP_ZERO) | RN(dst) | RM(TMP_ZERO));
+ return SLJIT_SUCCESS;
+}
+
+#define STORE 0x10
+#define SIGNED 0x20
+
+#define BYTE_SIZE 0x0
+#define HALF_SIZE 0x1
+#define INT_SIZE 0x2
+#define WORD_SIZE 0x3
+
+#define MEM_SIZE_SHIFT(flags) ((sljit_ins)(flags) & 0x3)
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
+{
+ sljit_u32 shift = MEM_SIZE_SHIFT(flags);
+ sljit_u32 type = (shift << 30);
+
+ if (!(flags & STORE))
+ type |= (flags & SIGNED) ? 0x00800000 : 0x00400000;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ if (argw == 0 || argw == shift)
+ return push_inst(compiler, STRB | type | RT(reg)
+ | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0));
+
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_reg) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((sljit_ins)argw << 10)));
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg));
+ }
+
+ arg &= REG_MASK;
+
+ if (!arg) {
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw & ~(0xfff << shift)));
+
+ argw = (argw >> shift) & 0xfff;
+
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10));
+ }
+
+ if ((argw & ((1 << shift) - 1)) == 0) {
+ if (argw >= 0) {
+ if ((argw >> shift) <= 0xfff)
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(arg) | ((sljit_ins)argw << (10 - shift)));
+
+ if (argw <= 0xffffff) {
+ FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)argw >> 12) << 10)));
+
+ argw = ((argw & 0xfff) >> shift);
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10));
+ }
+ } else if (argw < -256 && argw >= -0xfff000) {
+ FAIL_IF(push_inst(compiler, SUBI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)(-argw + 0xfff) >> 12) << 10)));
+ argw = ((0x1000 + argw) & 0xfff) >> shift;
+ return push_inst(compiler, STRBI | type | RT(reg) | RN(tmp_reg) | ((sljit_ins)argw << 10));
+ }
+ }
+
+ if (argw <= 0xff && argw >= -0x100)
+ return push_inst(compiler, STURBI | type | RT(reg) | RN(arg) | (((sljit_ins)argw & 0x1ff) << 12));
+
+ if (argw >= 0) {
+ if (argw <= 0xfff0ff && ((argw + 0x100) & 0xfff) <= 0x1ff) {
+ FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)argw >> 12) << 10)));
+ return push_inst(compiler, STURBI | type | RT(reg) | RN(tmp_reg) | (((sljit_ins)argw & 0x1ff) << 12));
+ }
+ } else if (argw >= -0xfff100 && ((-argw + 0xff) & 0xfff) <= 0x1ff) {
+ FAIL_IF(push_inst(compiler, SUBI | (1 << 22) | RD(tmp_reg) | RN(arg) | (((sljit_ins)-argw >> 12) << 10)));
+ return push_inst(compiler, STURBI | type | RT(reg) | RN(tmp_reg) | (((sljit_ins)argw & 0x1ff) << 12));
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+
+ return push_inst(compiler, STRB | type | RT(reg) | RN(arg) | RM(tmp_reg));
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 prev, fprev, saved_regs_size, i, tmp;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_ins offs;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 2);
+ saved_regs_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, SSIZE_OF(f64));
+
+ local_size = (local_size + saved_regs_size + 0xf) & ~0xf;
+ compiler->local_size = local_size;
+
+ if (local_size <= 512) {
+ FAIL_IF(push_inst(compiler, STP_PRE | RT(TMP_FP) | RT2(TMP_LR)
+ | RN(SLJIT_SP) | (sljit_ins)((-(local_size >> 3) & 0x7f) << 15)));
+ offs = (sljit_ins)(local_size - 2 * SSIZE_OF(sw)) << (15 - 3);
+ local_size = 0;
+ } else {
+ saved_regs_size = ((saved_regs_size - 2 * SSIZE_OF(sw)) + 0xf) & ~0xf;
+
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((sljit_ins)saved_regs_size << 10)));
+ offs = (sljit_ins)(saved_regs_size - 2 * SSIZE_OF(sw)) << (15 - 3);
+ local_size -= saved_regs_size;
+ SLJIT_ASSERT(local_size > 0);
+ }
+
+ prev = -1;
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ if (prev == -1) {
+ prev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, STP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ prev = -1;
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ if (prev == -1) {
+ prev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, STP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ prev = -1;
+ }
+
+ fprev = -1;
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ if (fprev == -1) {
+ fprev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, STP_F64 | VT(fprev) | VT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ fprev = -1;
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ if (fprev == -1) {
+ fprev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, STP_F64 | VT(fprev) | VT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ fprev = -1;
+ }
+
+ if (fprev != -1)
+ FAIL_IF(push_inst(compiler, STRI_F64 | VT(fprev) | RN(SLJIT_SP) | (offs >> 5) | (1 << 10)));
+
+ if (prev != -1)
+ FAIL_IF(push_inst(compiler, STRI | RT(prev) | RN(SLJIT_SP) | (offs >> 5) | ((fprev == -1) ? (1 << 10) : 0)));
+
+
+#ifdef _WIN32
+ if (local_size > 4096)
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22)));
+#endif /* _WIN32 */
+
+ if (!(options & SLJIT_ENTER_REG_ARG)) {
+ arg_types >>= SLJIT_ARG_SHIFT;
+ saved_arg_count = 0;
+ tmp = SLJIT_R0;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, ORR | RD(SLJIT_S0 - saved_arg_count) | RN(TMP_ZERO) | RM(tmp)));
+ saved_arg_count++;
+ }
+ tmp++;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+ }
+
+#ifdef _WIN32
+ if (local_size > 4096) {
+ if (local_size < 4 * 4096) {
+ /* No need for a loop. */
+
+ if (local_size >= 2 * 4096) {
+ if (local_size >= 3 * 4096) {
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(SLJIT_SP)));
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22)));
+ }
+
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(SLJIT_SP)));
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22)));
+ }
+ }
+ else {
+ FAIL_IF(push_inst(compiler, MOVZ | RD(TMP_REG1) | ((((sljit_ins)local_size >> 12) - 1) << 5)));
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(SLJIT_SP)));
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (1 << 10) | (1 << 22)));
+ FAIL_IF(push_inst(compiler, SUBI | (1 << 29) | RD(TMP_REG1) | RN(TMP_REG1) | (1 << 10)));
+ FAIL_IF(push_inst(compiler, B_CC | ((((sljit_ins) -3) & 0x7ffff) << 5) | 0x1 /* not-equal */));
+ }
+
+ local_size &= 0xfff;
+
+ if (local_size > 0)
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_ZERO) | RN(SLJIT_SP)));
+ else
+ FAIL_IF(push_inst(compiler, STP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP)));
+ }
+
+ if (local_size > 0) {
+ if (local_size <= 512)
+ FAIL_IF(push_inst(compiler, STP_PRE | RT(TMP_FP) | RT2(TMP_LR)
+ | RN(SLJIT_SP) | (sljit_ins)((-(local_size >> 3) & 0x7f) << 15)));
+ else {
+ if (local_size >= 4096)
+ local_size = (1 << (22 - 10));
+
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((sljit_ins)local_size << 10)));
+ FAIL_IF(push_inst(compiler, STP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP)));
+ }
+ }
+
+#else /* !_WIN32 */
+
+ /* The local_size does not include saved registers size. */
+ if (local_size != 0) {
+ if (local_size > 0xfff) {
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | (((sljit_ins)local_size >> 12) << 10) | (1 << 22)));
+ local_size &= 0xfff;
+ }
+
+ if (local_size > 512 || local_size == 0) {
+ if (local_size != 0)
+ FAIL_IF(push_inst(compiler, SUBI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((sljit_ins)local_size << 10)));
+
+ FAIL_IF(push_inst(compiler, STP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP)));
+ } else
+ FAIL_IF(push_inst(compiler, STP_PRE | RT(TMP_FP) | RT2(TMP_LR)
+ | RN(SLJIT_SP) | (sljit_ins)((-(local_size >> 3) & 0x7f) << 15)));
+ }
+
+#endif /* _WIN32 */
+
+ return push_inst(compiler, ADDI | RD(TMP_FP) | RN(SLJIT_SP) | (0 << 10));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 saved_regs_size;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 2);
+ saved_regs_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, SSIZE_OF(f64));
+
+ compiler->local_size = (local_size + saved_regs_size + 0xf) & ~0xf;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_s32 local_size, prev, fprev, i, tmp;
+ sljit_ins offs;
+
+ local_size = compiler->local_size;
+
+ if (!is_return_to) {
+ if (local_size > 512 && local_size <= 512 + 496) {
+ FAIL_IF(push_inst(compiler, LDP_POST | RT(TMP_FP) | RT2(TMP_LR)
+ | RN(SLJIT_SP) | ((sljit_ins)(local_size - 512) << (15 - 3))));
+ local_size = 512;
+ } else
+ FAIL_IF(push_inst(compiler, LDP | RT(TMP_FP) | RT2(TMP_LR) | RN(SLJIT_SP)));
+ } else {
+ if (local_size > 512 && local_size <= 512 + 248) {
+ FAIL_IF(push_inst(compiler, LDRI_POST | RT(TMP_FP) | RN(SLJIT_SP) | ((sljit_ins)(local_size - 512) << 12)));
+ local_size = 512;
+ } else
+ FAIL_IF(push_inst(compiler, LDRI | RT(TMP_FP) | RN(SLJIT_SP) | 0));
+ }
+
+ if (local_size > 512) {
+ local_size -= 512;
+ if (local_size > 0xfff) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP)
+ | (((sljit_ins)local_size >> 12) << 10) | (1 << 22)));
+ local_size &= 0xfff;
+ }
+
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP) | ((sljit_ins)local_size << 10)));
+ local_size = 512;
+ }
+
+ offs = (sljit_ins)(local_size - 2 * SSIZE_OF(sw)) << (15 - 3);
+ prev = -1;
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) {
+ if (prev == -1) {
+ prev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, LDP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ prev = -1;
+ }
+
+ for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ if (prev == -1) {
+ prev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, LDP | RT(prev) | RT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ prev = -1;
+ }
+
+ fprev = -1;
+
+ tmp = SLJIT_FS0 - compiler->fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ if (fprev == -1) {
+ fprev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, LDP_F64 | VT(fprev) | VT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ fprev = -1;
+ }
+
+ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ if (fprev == -1) {
+ fprev = i;
+ continue;
+ }
+ FAIL_IF(push_inst(compiler, LDP_F64 | VT(fprev) | VT2(i) | RN(SLJIT_SP) | offs));
+ offs -= (sljit_ins)2 << 15;
+ fprev = -1;
+ }
+
+ if (fprev != -1)
+ FAIL_IF(push_inst(compiler, LDRI_F64 | VT(fprev) | RN(SLJIT_SP) | (offs >> 5) | (1 << 10)));
+
+ if (prev != -1)
+ FAIL_IF(push_inst(compiler, LDRI | RT(prev) | RN(SLJIT_SP) | (offs >> 5) | ((fprev == -1) ? (1 << 10) : 0)));
+
+ /* This and the next call/jump instruction can be executed parallelly. */
+ return push_inst(compiler, ADDI | RD(SLJIT_SP) | RN(SLJIT_SP) | (sljit_ins)(local_size << 10));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ return push_inst(compiler, RET | RN(TMP_LR));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ORR | RD(TMP_REG1) | RN(TMP_ZERO) | RM(src)));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ sljit_ins inv_bits = (op & SLJIT_32) ? W_OP : 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ return push_inst(compiler, BRK);
+ case SLJIT_NOP:
+ return push_inst(compiler, NOP);
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ FAIL_IF(push_inst(compiler, ORR | RD(TMP_REG1) | RN(TMP_ZERO) | RM(SLJIT_R0)));
+ FAIL_IF(push_inst(compiler, MADD | RD(SLJIT_R0) | RN(SLJIT_R0) | RM(SLJIT_R1) | RT2(TMP_ZERO)));
+ return push_inst(compiler, (op == SLJIT_LMUL_UW ? UMULH : SMULH) | RD(SLJIT_R1) | RN(TMP_REG1) | RM(SLJIT_R1));
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst(compiler, (ORR ^ inv_bits) | RD(TMP_REG1) | RN(TMP_ZERO) | RM(SLJIT_R0)));
+ FAIL_IF(push_inst(compiler, ((op == SLJIT_DIVMOD_UW ? UDIV : SDIV) ^ inv_bits) | RD(SLJIT_R0) | RN(SLJIT_R0) | RM(SLJIT_R1)));
+ FAIL_IF(push_inst(compiler, (MADD ^ inv_bits) | RD(SLJIT_R1) | RN(SLJIT_R0) | RM(SLJIT_R1) | RT2(TMP_ZERO)));
+ return push_inst(compiler, (SUB ^ inv_bits) | RD(SLJIT_R1) | RN(TMP_REG1) | RM(SLJIT_R1));
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ return push_inst(compiler, ((op == SLJIT_DIV_UW ? UDIV : SDIV) ^ inv_bits) | RD(SLJIT_R0) | RN(SLJIT_R0) | RM(SLJIT_R1));
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r, flags, mem_flags;
+ sljit_s32 op_flags = GET_ALL_FLAGS(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ op = GET_OPCODE(op);
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
+ /* Both operands are registers. */
+ if (dst_r != TMP_REG1 && FAST_IS_REG(src))
+ return emit_op_imm(compiler, op | ((op_flags & SLJIT_32) ? INT_OP : 0), dst_r, TMP_REG1, src);
+
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ mem_flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ mem_flags = BYTE_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u8)srcw;
+ break;
+ case SLJIT_MOV_S8:
+ mem_flags = BYTE_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s8)srcw;
+ break;
+ case SLJIT_MOV_U16:
+ mem_flags = HALF_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u16)srcw;
+ break;
+ case SLJIT_MOV_S16:
+ mem_flags = HALF_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s16)srcw;
+ break;
+ case SLJIT_MOV_U32:
+ mem_flags = INT_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u32)srcw;
+ break;
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ mem_flags = INT_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s32)srcw;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ mem_flags = 0;
+ break;
+ }
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG1, srcw));
+ else if (!(src & SLJIT_MEM))
+ dst_r = src;
+ else
+ FAIL_IF(emit_op_mem(compiler, mem_flags, dst_r, src, srcw, TMP_REG1));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+ }
+
+ flags = HAS_FLAGS(op_flags) ? SET_FLAGS : 0;
+ mem_flags = WORD_SIZE;
+
+ if (op_flags & SLJIT_32) {
+ flags |= INT_OP;
+ mem_flags = INT_SIZE;
+ }
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG2, src, srcw, TMP_REG2));
+ src = TMP_REG2;
+ }
+
+ emit_op_imm(compiler, flags | op, dst_r, TMP_REG1, src);
+
+ if (SLJIT_UNLIKELY(dst & SLJIT_MEM))
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r, flags, mem_flags;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
+ mem_flags = WORD_SIZE;
+
+ if (op & SLJIT_32) {
+ flags |= INT_OP;
+ mem_flags = INT_SIZE;
+ }
+
+ if (dst == TMP_REG1)
+ flags |= UNUSED_RETURN;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_IMM)
+ flags |= ARG1_IMM;
+ else
+ src1w = src1;
+
+ if (src2 & SLJIT_IMM)
+ flags |= ARG2_IMM;
+ else
+ src2w = src2;
+
+ emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src1w, src2w);
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins inv_bits, imm;
+ sljit_s32 is_left;
+ sljit_sw mask;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ inv_bits = (op & SLJIT_32) ? W_OP : 0;
+ mask = inv_bits ? 0x1f : 0x3f;
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= mask;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (is_left)
+ src2w = (src2w ^ mask) + 1;
+
+ return push_inst(compiler, (EXTR ^ (inv_bits | (inv_bits >> 9))) | RD(src_dst)
+ | RN(is_left ? src_dst : src1) | RM(is_left ? src1 : src_dst) | ((sljit_ins)src2w << 10));
+ }
+
+ FAIL_IF(push_inst(compiler, ((is_left ? LSLV : LSRV) ^ inv_bits) | RD(src_dst) | RN(src_dst) | RM(src2)));
+
+ if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) {
+ /* Shift left/right by 1. */
+ if (is_left)
+ imm = (sljit_ins)(inv_bits ? ((1 << 16) | (31 << 10)) : ((1 << 16) | (63 << 10) | (1 << 22)));
+ else
+ imm = (sljit_ins)(inv_bits ? ((31 << 16) | (30 << 10)) : ((63 << 16) | (62 << 10) | (1 << 22)));
+
+ FAIL_IF(push_inst(compiler, (UBFM ^ inv_bits) | RD(TMP_REG1) | RN(src1) | imm));
+
+ /* Set imm to mask. */
+ imm = (sljit_ins)(inv_bits ? (4 << 10) : ((5 << 10) | (1 << 22)));
+ FAIL_IF(push_inst(compiler, (EORI ^ inv_bits) | RD(TMP_REG2) | RN(src2) | imm));
+
+ src1 = TMP_REG1;
+ } else
+ FAIL_IF(push_inst(compiler, (SUB ^ inv_bits) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(src2)));
+
+ FAIL_IF(push_inst(compiler, ((is_left ? LSRV : LSLV) ^ inv_bits) | RD(TMP_REG1) | RN(src1) | RM(TMP_REG2)));
+ return push_inst(compiler, (ORR ^ inv_bits) | RD(src_dst) | RN(src_dst) | RM(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, ORR | RD(TMP_LR) | RN(TMP_ZERO) | RM(src)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_LR, src, srcw, TMP_REG1));
+
+ return push_inst(compiler, RET | RN(TMP_LR));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ SLJIT_ASSERT(reg_map[1] == 0 && reg_map[3] == 2 && reg_map[5] == 4);
+
+ /* The reg_map[op] should provide the appropriate constant. */
+ if (op == SLJIT_PREFETCH_L1)
+ op = 1;
+ else if (op == SLJIT_PREFETCH_L2)
+ op = 3;
+ else if (op == SLJIT_PREFETCH_L3)
+ op = 5;
+ else
+ op = 2;
+
+ /* Signed word sized load is the prefetch instruction. */
+ return emit_op_mem(compiler, WORD_SIZE | SIGNED, op, src, srcw, TMP_REG1);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return freg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ SLJIT_UNUSED_ARG(size);
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_ins*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_u32 shift = MEM_SIZE_SHIFT(flags);
+ sljit_ins type = (shift << 30);
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (!(flags & STORE))
+ type |= 0x00400000;
+
+ if (arg & OFFS_REG_MASK) {
+ argw &= 3;
+ if (argw == 0 || argw == shift)
+ return push_inst(compiler, STR_FR | type | VT(reg)
+ | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | (argw ? (1 << 12) : 0));
+
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(arg & REG_MASK) | RM(OFFS_REG(arg)) | ((sljit_ins)argw << 10)));
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1));
+ }
+
+ arg &= REG_MASK;
+
+ if (!arg) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, argw & ~(0xfff << shift)));
+
+ argw = (argw >> shift) & 0xfff;
+
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1) | ((sljit_ins)argw << 10));
+ }
+
+ if (argw >= 0 && (argw & ((1 << shift) - 1)) == 0) {
+ if ((argw >> shift) <= 0xfff)
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(arg) | ((sljit_ins)argw << (10 - shift)));
+
+ if (argw <= 0xffffff) {
+ FAIL_IF(push_inst(compiler, ADDI | (1 << 22) | RD(TMP_REG1) | RN(arg) | (((sljit_ins)argw >> 12) << 10)));
+
+ argw = ((argw & 0xfff) >> shift);
+ return push_inst(compiler, STR_FI | type | VT(reg) | RN(TMP_REG1) | ((sljit_ins)argw << 10));
+ }
+ }
+
+ if (argw <= 255 && argw >= -256)
+ return push_inst(compiler, STUR_FI | type | VT(reg) | RN(arg) | (((sljit_ins)argw & 0x1ff) << 12));
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, argw));
+ return push_inst(compiler, STR_FR | type | VT(reg) | RN(arg) | RM(TMP_REG1));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ sljit_ins inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+
+ if (GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64)
+ inv_bits |= W_OP;
+
+ if (src & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) ? INT_SIZE : WORD_SIZE, TMP_FREG1, src, srcw);
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst(compiler, (FCVTZS ^ inv_bits) | RD(dst_r) | VN(src)));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_S32_FROM_F64) ? INT_SIZE : WORD_SIZE) | STORE, TMP_REG1, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ sljit_ins inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ inv_bits |= W_OP;
+
+ if (src & SLJIT_MEM) {
+ emit_op_mem(compiler, ((GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) ? INT_SIZE : WORD_SIZE), TMP_REG1, src, srcw, TMP_REG1);
+ src = TMP_REG1;
+ } else if (src & SLJIT_IMM) {
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, (SCVTF ^ inv_bits) | VD(dst_r) | RN(src)));
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, ((op & SLJIT_32) ? INT_SIZE : WORD_SIZE) | STORE, TMP_FREG1, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 mem_flags = (op & SLJIT_32) ? INT_SIZE : WORD_SIZE;
+ sljit_ins inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+
+ if (src1 & SLJIT_MEM) {
+ emit_fop_mem(compiler, mem_flags, TMP_FREG1, src1, src1w);
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ emit_fop_mem(compiler, mem_flags, TMP_FREG2, src2, src2w);
+ src2 = TMP_FREG2;
+ }
+
+ return push_inst(compiler, (FCMP ^ inv_bits) | VN(src1) | VM(src2));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r, mem_flags = (op & SLJIT_32) ? INT_SIZE : WORD_SIZE;
+ sljit_ins inv_bits;
+
+ CHECK_ERROR();
+
+ SLJIT_COMPILE_ASSERT((INT_SIZE ^ 0x1) == WORD_SIZE, must_be_one_bit_difference);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+ emit_fop_mem(compiler, (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) ? (mem_flags ^ 0x1) : mem_flags, dst_r, src, srcw);
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, (FMOV ^ inv_bits) | VD(dst_r) | VN(src)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, (FNEG ^ inv_bits) | VD(dst_r) | VN(src)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, (FABS ^ inv_bits) | VD(dst_r) | VN(src)));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ FAIL_IF(push_inst(compiler, FCVT | (sljit_ins)((op & SLJIT_32) ? (1 << 22) : (1 << 15)) | VD(dst_r) | VN(src)));
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, mem_flags | STORE, dst_r, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r, mem_flags = (op & SLJIT_32) ? INT_SIZE : WORD_SIZE;
+ sljit_ins inv_bits = (op & SLJIT_32) ? (1 << 22) : 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ if (src1 & SLJIT_MEM) {
+ emit_fop_mem(compiler, mem_flags, TMP_FREG1, src1, src1w);
+ src1 = TMP_FREG1;
+ }
+ if (src2 & SLJIT_MEM) {
+ emit_fop_mem(compiler, mem_flags, TMP_FREG2, src2, src2w);
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, (FADD ^ inv_bits) | VD(dst_r) | VN(src1) | VM(src2)));
+ break;
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, (FSUB ^ inv_bits) | VD(dst_r) | VN(src1) | VM(src2)));
+ break;
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, (FMUL ^ inv_bits) | VD(dst_r) | VN(src1) | VM(src2)));
+ break;
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, (FDIV ^ inv_bits) | VD(dst_r) | VN(src1) | VM(src2)));
+ break;
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_fop_mem(compiler, mem_flags | STORE, TMP_FREG1, dst, dstw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, ORR | RD(dst) | RN(TMP_ZERO) | RM(TMP_LR));
+
+ /* Memory. */
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_LR, dst, dstw, TMP_REG1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+static sljit_ins get_cc(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ return 0x1;
+
+ case SLJIT_NOT_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ return 0x0;
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x3;
+ /* fallthrough */
+
+ case SLJIT_LESS:
+ return 0x2;
+
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x2;
+ /* fallthrough */
+
+ case SLJIT_GREATER_EQUAL:
+ return 0x3;
+
+ case SLJIT_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return 0x9;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return 0x8;
+
+ case SLJIT_SIG_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ return 0xa;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return 0xb;
+
+ case SLJIT_SIG_GREATER:
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ return 0xd;
+
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return 0xc;
+
+ case SLJIT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x0;
+ /* fallthrough */
+
+ case SLJIT_UNORDERED:
+ return 0x7;
+
+ case SLJIT_NOT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x1;
+ /* fallthrough */
+
+ case SLJIT_ORDERED:
+ return 0x6;
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ return 0x5;
+
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return 0x4;
+
+ default:
+ SLJIT_UNREACHABLE();
+ return 0xe;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ if (type < SLJIT_JUMP) {
+ jump->flags |= IS_COND;
+ PTR_FAIL_IF(push_inst(compiler, B_CC | (6 << 5) | get_cc(compiler, type)));
+ }
+ else if (type >= SLJIT_FAST_CALL)
+ jump->flags |= IS_BL;
+
+ PTR_FAIL_IF(emit_imm64_const(compiler, TMP_REG1, 0));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(TMP_REG1)));
+
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+static SLJIT_INLINE struct sljit_jump* emit_cmp_to0(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+ sljit_ins inv_bits = (type & SLJIT_32) ? W_OP : 0;
+
+ SLJIT_ASSERT((type & 0xff) == SLJIT_EQUAL || (type & 0xff) == SLJIT_NOT_EQUAL);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ jump->flags |= IS_CBZ | IS_COND;
+
+ if (src & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem(compiler, inv_bits ? INT_SIZE : WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+ else if (src & SLJIT_IMM) {
+ PTR_FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ src = TMP_REG1;
+ }
+
+ SLJIT_ASSERT(FAST_IS_REG(src));
+
+ if ((type & 0xff) == SLJIT_EQUAL)
+ inv_bits |= 1 << 24;
+
+ PTR_FAIL_IF(push_inst(compiler, (CBZ ^ inv_bits) | (6 << 5) | RT(src)));
+ PTR_FAIL_IF(emit_imm64_const(compiler, TMP_REG1, 0));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, BR | RN(TMP_REG1)));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (!(src & SLJIT_IMM)) {
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+ return push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(src));
+ }
+
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+ FAIL_IF(emit_imm64_const(compiler, TMP_REG1, 0));
+ jump->addr = compiler->size;
+ return push_inst(compiler, ((type >= SLJIT_FAST_CALL) ? BLR : BR) | RN(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ORR | RD(TMP_REG1) | RN(TMP_ZERO) | RM(src)));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP;
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 dst_r, src_r, flags, mem_flags;
+ sljit_ins cc;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ cc = get_cc(compiler, type);
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (GET_OPCODE(op) < SLJIT_ADD) {
+ FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(dst_r) | RN(TMP_ZERO) | RM(TMP_ZERO)));
+
+ if (dst_r == TMP_REG1) {
+ mem_flags = (GET_OPCODE(op) == SLJIT_MOV ? WORD_SIZE : INT_SIZE) | STORE;
+ return emit_op_mem(compiler, mem_flags, TMP_REG1, dst, dstw, TMP_REG2);
+ }
+
+ return SLJIT_SUCCESS;
+ }
+
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
+ mem_flags = WORD_SIZE;
+
+ if (op & SLJIT_32) {
+ flags |= INT_OP;
+ mem_flags = INT_SIZE;
+ }
+
+ src_r = dst;
+
+ if (dst & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, mem_flags, TMP_REG1, dst, dstw, TMP_REG1));
+ src_r = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, CSINC | (cc << 12) | RD(TMP_REG2) | RN(TMP_ZERO) | RM(TMP_ZERO)));
+ emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src_r, TMP_REG2);
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_flags | STORE, TMP_REG1, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins inv_bits = (type & SLJIT_32) ? W_OP : 0;
+ sljit_ins cc;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ if (type & SLJIT_32)
+ srcw = (sljit_s32)srcw;
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ cc = get_cc(compiler, type & ~SLJIT_32);
+
+ return push_inst(compiler, (CSEL ^ inv_bits) | (cc << 12) | RD(dst_reg) | RN(dst_reg) | RM(src));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u32 inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ if (!(mem & REG_MASK)) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, memw & ~0x1f8));
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ memw &= 0x1f8;
+ } else if (mem & OFFS_REG_MASK) {
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RN(mem & REG_MASK) | RM(OFFS_REG(mem)) | ((sljit_ins)(memw & 0x3) << 10)));
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ memw = 0;
+ } else if ((memw & 0x7) != 0 || memw > 0x1f8 || memw < -0x200) {
+ inst = ADDI;
+
+ if (memw < 0) {
+ /* Remains negative for integer min. */
+ memw = -memw;
+ inst = SUBI;
+ } else if ((memw & 0x7) == 0 && memw <= 0x7ff0) {
+ if (!(type & SLJIT_MEM_STORE) && (mem & REG_MASK) == REG_PAIR_FIRST(reg)) {
+ FAIL_IF(push_inst(compiler, LDRI | RD(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | ((sljit_ins)memw << 7)));
+ return push_inst(compiler, LDRI | RD(REG_PAIR_FIRST(reg)) | RN(mem & REG_MASK) | ((sljit_ins)(memw + 0x8) << 7));
+ }
+
+ inst = (type & SLJIT_MEM_STORE) ? STRI : LDRI;
+
+ FAIL_IF(push_inst(compiler, inst | RD(REG_PAIR_FIRST(reg)) | RN(mem & REG_MASK) | ((sljit_ins)memw << 7)));
+ return push_inst(compiler, inst | RD(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | ((sljit_ins)(memw + 0x8) << 7));
+ }
+
+ if ((sljit_uw)memw <= 0xfff) {
+ FAIL_IF(push_inst(compiler, inst | RD(TMP_REG1) | RN(mem & REG_MASK) | ((sljit_ins)memw << 10)));
+ memw = 0;
+ } else if ((sljit_uw)memw <= 0xffffff) {
+ FAIL_IF(push_inst(compiler, inst | (1 << 22) | RD(TMP_REG1) | RN(mem & REG_MASK) | (((sljit_ins)memw >> 12) << 10)));
+
+ if ((memw & 0xe07) != 0) {
+ FAIL_IF(push_inst(compiler, inst | RD(TMP_REG1) | RN(TMP_REG1) | (((sljit_ins)memw & 0xfff) << 10)));
+ memw = 0;
+ } else {
+ memw &= 0xfff;
+ }
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, memw));
+ FAIL_IF(push_inst(compiler, (inst == ADDI ? ADD : SUB) | RD(TMP_REG1) | RN(mem & REG_MASK) | RM(TMP_REG1)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+
+ if (inst == SUBI)
+ memw = -memw;
+ }
+
+ SLJIT_ASSERT((memw & 0x7) == 0 && memw <= 0x1f8 && memw >= -0x200);
+ return push_inst(compiler, ((type & SLJIT_MEM_STORE) ? STP : LDP) | RT(REG_PAIR_FIRST(reg)) | RT2(REG_PAIR_SECOND(reg)) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x3f8) << 12));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u32 sign = 0, inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+
+ if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -256))
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ inst = STURBI | (MEM_SIZE_SHIFT(WORD_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S8:
+ sign = 1;
+ /* fallthrough */
+ case SLJIT_MOV_U8:
+ inst = STURBI | (MEM_SIZE_SHIFT(BYTE_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S16:
+ sign = 1;
+ /* fallthrough */
+ case SLJIT_MOV_U16:
+ inst = STURBI | (MEM_SIZE_SHIFT(HALF_SIZE) << 30) | 0x400;
+ break;
+ case SLJIT_MOV_S32:
+ sign = 1;
+ /* fallthrough */
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV32:
+ inst = STURBI | (MEM_SIZE_SHIFT(INT_SIZE) << 30) | 0x400;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ inst = STURBI | (MEM_SIZE_SHIFT(WORD_SIZE) << 30) | 0x400;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ inst |= sign ? 0x00800000 : 0x00400000;
+
+ if (!(type & SLJIT_MEM_POST))
+ inst |= 0x800;
+
+ return push_inst(compiler, inst | RT(reg) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x1ff) << 12));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u32 inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw));
+
+ if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -256))
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ inst = STUR_FI | 0x80000400;
+
+ if (!(type & SLJIT_32))
+ inst |= 0x40000000;
+
+ if (!(type & SLJIT_MEM_STORE))
+ inst |= 0x00400000;
+
+ if (!(type & SLJIT_MEM_POST))
+ inst |= 0x800;
+
+ return push_inst(compiler, inst | VT(freg) | RN(mem & REG_MASK) | (sljit_ins)((memw & 0x1ff) << 12));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ sljit_s32 dst_reg;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_get_local_base(compiler, dst, dstw, offset));
+ ADJUST_LOCAL_OFFSET(SLJIT_MEM1(SLJIT_SP), offset);
+
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ /* Not all instruction forms support accessing SP register. */
+ if (offset <= 0xffffff && offset >= -0xffffff) {
+ ins = ADDI;
+ if (offset < 0) {
+ offset = -offset;
+ ins = SUBI;
+ }
+
+ if (offset <= 0xfff)
+ FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(SLJIT_SP) | (sljit_ins)(offset << 10)));
+ else {
+ FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(SLJIT_SP) | (sljit_ins)((offset & 0xfff000) >> (12 - 10)) | (1 << 22)));
+
+ offset &= 0xfff;
+ if (offset != 0)
+ FAIL_IF(push_inst(compiler, ins | RD(dst_reg) | RN(dst_reg) | (sljit_ins)(offset << 10)));
+ }
+ }
+ else {
+ FAIL_IF(load_immediate (compiler, dst_reg, offset));
+ /* Add extended register form. */
+ FAIL_IF(push_inst(compiler, ADDE | (0x3 << 13) | RD(dst_reg) | RN(SLJIT_SP) | RM(dst_reg)));
+ }
+
+ if (SLJIT_UNLIKELY(dst & SLJIT_MEM))
+ return emit_op_mem(compiler, WORD_SIZE | STORE, dst_reg, dst, dstw, TMP_REG1);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ PTR_FAIL_IF(emit_imm64_const(compiler, dst_r, (sljit_uw)init_value));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ PTR_FAIL_IF(emit_imm64_const(compiler, dst_r, 0));
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 1);
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
+
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins* inst = (sljit_ins*)addr;
+ sljit_u32 dst;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 4, 0);
+
+ dst = inst[0] & 0x1f;
+ SLJIT_ASSERT((inst[0] & 0xffe00000) == MOVZ && (inst[1] & 0xffe00000) == (MOVK | (1 << 21)));
+ inst[0] = MOVZ | dst | (((sljit_u32)new_target & 0xffff) << 5);
+ inst[1] = MOVK | dst | (((sljit_u32)(new_target >> 16) & 0xffff) << 5) | (1 << 21);
+ inst[2] = MOVK | dst | (((sljit_u32)(new_target >> 32) & 0xffff) << 5) | (2 << 21);
+ inst[3] = MOVK | dst | ((sljit_u32)(new_target >> 48) << 5) | (3 << 21);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 4, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 4);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeARM_T2_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeARM_T2_32.c
new file mode 100644
index 0000000000..7d6bac077e
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeARM_T2_32.c
@@ -0,0 +1,3150 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+#ifdef __SOFTFP__
+ return "ARM-Thumb2" SLJIT_CPUINFO " ABI:softfp";
+#else
+ return "ARM-Thumb2" SLJIT_CPUINFO " ABI:hardfp";
+#endif
+}
+
+/* Length of an instruction word. */
+typedef sljit_u32 sljit_ins;
+
+/* Last register + 1. */
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_PC (SLJIT_NUMBER_OF_REGISTERS + 4)
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
+ 0, 0, 1, 2, 3, 11, 10, 9, 8, 7, 6, 5, 4, 13, 12, 14, 15
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 0, 1, 2, 3, 4, 5, 15, 14, 13, 12, 11, 10, 9, 8, 6, 7
+};
+
+#define COPY_BITS(src, from, to, bits) \
+ ((from >= to ? ((sljit_ins)(src) >> (from - to)) : ((sljit_ins)(src) << (to - from))) & (((1 << bits) - 1) << to))
+
+#define NEGATE(uimm) ((sljit_uw)-(sljit_sw)(uimm))
+
+/* Thumb16 encodings. */
+#define RD3(rd) ((sljit_ins)reg_map[rd])
+#define RN3(rn) ((sljit_ins)reg_map[rn] << 3)
+#define RM3(rm) ((sljit_ins)reg_map[rm] << 6)
+#define RDN3(rdn) ((sljit_ins)reg_map[rdn] << 8)
+#define IMM3(imm) ((sljit_ins)imm << 6)
+#define IMM8(imm) ((sljit_ins)imm)
+
+/* Thumb16 helpers. */
+#define SET_REGS44(rd, rn) \
+ (((sljit_ins)reg_map[rn] << 3) | ((sljit_ins)reg_map[rd] & 0x7) | (((sljit_ins)reg_map[rd] & 0x8) << 4))
+#define IS_2_LO_REGS(reg1, reg2) \
+ (reg_map[reg1] <= 7 && reg_map[reg2] <= 7)
+#define IS_3_LO_REGS(reg1, reg2, reg3) \
+ (reg_map[reg1] <= 7 && reg_map[reg2] <= 7 && reg_map[reg3] <= 7)
+
+/* Thumb32 encodings. */
+#define RD4(rd) ((sljit_ins)reg_map[rd] << 8)
+#define RN4(rn) ((sljit_ins)reg_map[rn] << 16)
+#define RM4(rm) ((sljit_ins)reg_map[rm])
+#define RT4(rt) ((sljit_ins)reg_map[rt] << 12)
+#define DD4(dd) ((sljit_ins)freg_map[dd] << 12)
+#define DN4(dn) ((sljit_ins)freg_map[dn] << 16)
+#define DM4(dm) ((sljit_ins)freg_map[dm])
+#define IMM5(imm) \
+ (COPY_BITS(imm, 2, 12, 3) | (((sljit_ins)imm & 0x3) << 6))
+#define IMM12(imm) \
+ (COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | ((sljit_ins)imm & 0xff))
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+/* dot '.' changed to _
+ I immediate form (possibly followed by number of immediate bits). */
+#define ADCI 0xf1400000
+#define ADCS 0x4140
+#define ADC_W 0xeb400000
+#define ADD 0x4400
+#define ADDS 0x1800
+#define ADDSI3 0x1c00
+#define ADDSI8 0x3000
+#define ADDWI 0xf2000000
+#define ADD_SP 0x4485
+#define ADD_SP_I 0xb000
+#define ADD_W 0xeb000000
+#define ADD_WI 0xf1000000
+#define ANDI 0xf0000000
+#define ANDS 0x4000
+#define AND_W 0xea000000
+#define ASRS 0x4100
+#define ASRSI 0x1000
+#define ASR_W 0xfa40f000
+#define ASR_WI 0xea4f0020
+#define BCC 0xd000
+#define BICI 0xf0200000
+#define BKPT 0xbe00
+#define BLX 0x4780
+#define BX 0x4700
+#define CLZ 0xfab0f080
+#define CMNI_W 0xf1100f00
+#define CMP 0x4280
+#define CMPI 0x2800
+#define CMPI_W 0xf1b00f00
+#define CMP_X 0x4500
+#define CMP_W 0xebb00f00
+#define EORI 0xf0800000
+#define EORS 0x4040
+#define EOR_W 0xea800000
+#define IT 0xbf00
+#define LDR_SP 0x9800
+#define LDR 0xf8d00000
+#define LDRD 0xe9500000
+#define LDRI 0xf8500800
+#define LSLS 0x4080
+#define LSLSI 0x0000
+#define LSL_W 0xfa00f000
+#define LSL_WI 0xea4f0000
+#define LSRS 0x40c0
+#define LSRSI 0x0800
+#define LSR_W 0xfa20f000
+#define LSR_WI 0xea4f0010
+#define MOV 0x4600
+#define MOVS 0x0000
+#define MOVSI 0x2000
+#define MOVT 0xf2c00000
+#define MOVW 0xf2400000
+#define MOV_W 0xea4f0000
+#define MOV_WI 0xf04f0000
+#define MUL 0xfb00f000
+#define MVNS 0x43c0
+#define MVN_W 0xea6f0000
+#define MVN_WI 0xf06f0000
+#define NOP 0xbf00
+#define ORNI 0xf0600000
+#define ORRI 0xf0400000
+#define ORRS 0x4300
+#define ORR_W 0xea400000
+#define POP 0xbc00
+#define POP_W 0xe8bd0000
+#define PUSH 0xb400
+#define PUSH_W 0xe92d0000
+#define RBIT 0xfa90f0a0
+#define RORS 0x41c0
+#define ROR_W 0xfa60f000
+#define ROR_WI 0xea4f0030
+#define RSB_WI 0xf1c00000
+#define RSBSI 0x4240
+#define SBCI 0xf1600000
+#define SBCS 0x4180
+#define SBC_W 0xeb600000
+#define SDIV 0xfb90f0f0
+#define SMULL 0xfb800000
+#define STRD 0xe9400000
+#define STR_SP 0x9000
+#define SUBS 0x1a00
+#define SUBSI3 0x1e00
+#define SUBSI8 0x3800
+#define SUB_W 0xeba00000
+#define SUBWI 0xf2a00000
+#define SUB_SP_I 0xb080
+#define SUB_WI 0xf1a00000
+#define SXTB 0xb240
+#define SXTB_W 0xfa4ff080
+#define SXTH 0xb200
+#define SXTH_W 0xfa0ff080
+#define TST 0x4200
+#define TSTI 0xf0000f00
+#define TST_W 0xea000f00
+#define UDIV 0xfbb0f0f0
+#define UMULL 0xfba00000
+#define UXTB 0xb2c0
+#define UXTB_W 0xfa5ff080
+#define UXTH 0xb280
+#define UXTH_W 0xfa1ff080
+#define VABS_F32 0xeeb00ac0
+#define VADD_F32 0xee300a00
+#define VCMP_F32 0xeeb40a40
+#define VCVT_F32_S32 0xeeb80ac0
+#define VCVT_F64_F32 0xeeb70ac0
+#define VCVT_S32_F32 0xeebd0ac0
+#define VDIV_F32 0xee800a00
+#define VLDR_F32 0xed100a00
+#define VMOV_F32 0xeeb00a40
+#define VMOV 0xee000a10
+#define VMOV2 0xec400a10
+#define VMRS 0xeef1fa10
+#define VMUL_F32 0xee200a00
+#define VNEG_F32 0xeeb10a40
+#define VPOP 0xecbd0b00
+#define VPUSH 0xed2d0b00
+#define VSTR_F32 0xed000a00
+#define VSUB_F32 0xee300a40
+
+static sljit_s32 push_inst16(struct sljit_compiler *compiler, sljit_ins inst)
+{
+ sljit_u16 *ptr;
+ SLJIT_ASSERT(!(inst & 0xffff0000));
+
+ ptr = (sljit_u16*)ensure_buf(compiler, sizeof(sljit_u16));
+ FAIL_IF(!ptr);
+ *ptr = (sljit_u16)(inst);
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_inst32(struct sljit_compiler *compiler, sljit_ins inst)
+{
+ sljit_u16 *ptr = (sljit_u16*)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ptr);
+ *ptr++ = (sljit_u16)(inst >> 16);
+ *ptr = (sljit_u16)(inst);
+ compiler->size += 2;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_imm32_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_uw imm)
+{
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst)
+ | COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)));
+ return push_inst32(compiler, MOVT | RD4(dst)
+ | COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16));
+}
+
+static SLJIT_INLINE void modify_imm32_const(sljit_u16 *inst, sljit_uw new_imm)
+{
+ sljit_ins dst = inst[1] & 0x0f00;
+ SLJIT_ASSERT(((inst[0] & 0xfbf0) == (MOVW >> 16)) && ((inst[2] & 0xfbf0) == (MOVT >> 16)) && dst == (inst[3] & 0x0f00));
+ inst[0] = (sljit_u16)((MOVW >> 16) | COPY_BITS(new_imm, 12, 0, 4) | COPY_BITS(new_imm, 11, 10, 1));
+ inst[1] = (sljit_u16)(dst | COPY_BITS(new_imm, 8, 12, 3) | (new_imm & 0xff));
+ inst[2] = (sljit_u16)((MOVT >> 16) | COPY_BITS(new_imm, 12 + 16, 0, 4) | COPY_BITS(new_imm, 11 + 16, 10, 1));
+ inst[3] = (sljit_u16)(dst | COPY_BITS(new_imm, 8 + 16, 12, 3) | ((new_imm & 0xff0000) >> 16));
+}
+
+static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_u16 *code_ptr, sljit_u16 *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ return 0;
+
+ if (jump->flags & JUMP_ADDR) {
+ /* Branch to ARM code is not optimized yet. */
+ if (!(jump->u.target & 0x1))
+ return 0;
+ diff = ((sljit_sw)jump->u.target - (sljit_sw)(code_ptr + 2) - executable_offset) >> 1;
+ }
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ diff = ((sljit_sw)(code + jump->u.label->size) - (sljit_sw)(code_ptr + 2)) >> 1;
+ }
+
+ if (jump->flags & IS_COND) {
+ SLJIT_ASSERT(!(jump->flags & IS_BL));
+ if (diff <= 127 && diff >= -128) {
+ jump->flags |= PATCH_TYPE1;
+ return 5;
+ }
+ if (diff <= 524287 && diff >= -524288) {
+ jump->flags |= PATCH_TYPE2;
+ return 4;
+ }
+ /* +1 comes from the prefix IT instruction. */
+ diff--;
+ if (diff <= 8388607 && diff >= -8388608) {
+ jump->flags |= PATCH_TYPE3;
+ return 3;
+ }
+ }
+ else if (jump->flags & IS_BL) {
+ if (diff <= 8388607 && diff >= -8388608) {
+ jump->flags |= PATCH_BL;
+ return 3;
+ }
+ }
+ else {
+ if (diff <= 1023 && diff >= -1024) {
+ jump->flags |= PATCH_TYPE4;
+ return 4;
+ }
+ if (diff <= 8388607 && diff >= -8388608) {
+ jump->flags |= PATCH_TYPE5;
+ return 3;
+ }
+ }
+
+ return 0;
+}
+
+static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump, sljit_sw executable_offset)
+{
+ sljit_s32 type = (jump->flags >> 4) & 0xf;
+ sljit_sw diff;
+ sljit_u16 *jump_inst;
+ sljit_s32 s, j1, j2;
+
+ if (SLJIT_UNLIKELY(type == 0)) {
+ modify_imm32_const((sljit_u16*)jump->addr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target);
+ return;
+ }
+
+ if (jump->flags & JUMP_ADDR) {
+ SLJIT_ASSERT(jump->u.target & 0x1);
+ diff = ((sljit_sw)jump->u.target - (sljit_sw)(jump->addr + sizeof(sljit_u32)) - executable_offset) >> 1;
+ }
+ else {
+ SLJIT_ASSERT(jump->u.label->addr & 0x1);
+ diff = ((sljit_sw)(jump->u.label->addr) - (sljit_sw)(jump->addr + sizeof(sljit_u32)) - executable_offset) >> 1;
+ }
+ jump_inst = (sljit_u16*)jump->addr;
+
+ switch (type) {
+ case 1:
+ /* Encoding T1 of 'B' instruction */
+ SLJIT_ASSERT(diff <= 127 && diff >= -128 && (jump->flags & IS_COND));
+ jump_inst[0] = (sljit_u16)(0xd000 | (jump->flags & 0xf00) | ((sljit_ins)diff & 0xff));
+ return;
+ case 2:
+ /* Encoding T3 of 'B' instruction */
+ SLJIT_ASSERT(diff <= 524287 && diff >= -524288 && (jump->flags & IS_COND));
+ jump_inst[0] = (sljit_u16)(0xf000 | COPY_BITS(jump->flags, 8, 6, 4) | COPY_BITS(diff, 11, 0, 6) | COPY_BITS(diff, 19, 10, 1));
+ jump_inst[1] = (sljit_u16)(0x8000 | COPY_BITS(diff, 17, 13, 1) | COPY_BITS(diff, 18, 11, 1) | ((sljit_ins)diff & 0x7ff));
+ return;
+ case 3:
+ SLJIT_ASSERT(jump->flags & IS_COND);
+ *jump_inst++ = (sljit_u16)(IT | ((jump->flags >> 4) & 0xf0) | 0x8);
+ diff--;
+ type = 5;
+ break;
+ case 4:
+ /* Encoding T2 of 'B' instruction */
+ SLJIT_ASSERT(diff <= 1023 && diff >= -1024 && !(jump->flags & IS_COND));
+ jump_inst[0] = (sljit_u16)(0xe000 | (diff & 0x7ff));
+ return;
+ }
+
+ SLJIT_ASSERT(diff <= 8388607 && diff >= -8388608);
+
+ /* Really complex instruction form for branches. */
+ s = (diff >> 23) & 0x1;
+ j1 = (~(diff >> 22) ^ s) & 0x1;
+ j2 = (~(diff >> 21) ^ s) & 0x1;
+ jump_inst[0] = (sljit_u16)(0xf000 | ((sljit_ins)s << 10) | COPY_BITS(diff, 11, 0, 10));
+ jump_inst[1] = (sljit_u16)((j1 << 13) | (j2 << 11) | (diff & 0x7ff));
+
+ /* The others have a common form. */
+ if (type == 5) /* Encoding T4 of 'B' instruction */
+ jump_inst[1] |= 0x9000;
+ else if (type == 6) /* Encoding T1 of 'BL' instruction */
+ jump_inst[1] |= 0xd000;
+ else
+ SLJIT_UNREACHABLE();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_u16 *code;
+ sljit_u16 *code_ptr;
+ sljit_u16 *buf_ptr;
+ sljit_u16 *buf_end;
+ sljit_uw half_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ code = (sljit_u16*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_u16), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ half_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_u16*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 1);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == half_count) {
+ SLJIT_ASSERT(!label || label->size >= half_count);
+ SLJIT_ASSERT(!jump || jump->addr >= half_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= half_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= half_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == half_count) {
+ label->addr = ((sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset)) | 0x1;
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == half_count) {
+ jump->addr = (sljit_uw)code_ptr - ((jump->flags & IS_COND) ? 10 : 8);
+ code_ptr -= detect_jump_type(jump, code_ptr, code, executable_offset);
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == half_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == half_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ half_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == half_count) {
+ label->addr = ((sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset)) | 0x1;
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ set_jump_instruction(jump, executable_offset);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ modify_imm32_const((sljit_u16 *)put_label->addr, put_label->label->addr);
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_u16);
+
+ code = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+
+ /* Set thumb mode flag. */
+ return (void*)((sljit_uw)code | 0x1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CTZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_CMOV:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Core code generator functions. */
+/* --------------------------------------------------------------------- */
+
+#define INVALID_IMM 0x80000000
+static sljit_uw get_imm(sljit_uw imm)
+{
+ /* Thumb immediate form. */
+ sljit_s32 counter;
+
+ if (imm <= 0xff)
+ return imm;
+
+ if ((imm & 0xffff) == (imm >> 16)) {
+ /* Some special cases. */
+ if (!(imm & 0xff00))
+ return (1 << 12) | (imm & 0xff);
+ if (!(imm & 0xff))
+ return (2 << 12) | ((imm >> 8) & 0xff);
+ if ((imm & 0xff00) == ((imm & 0xff) << 8))
+ return (3 << 12) | (imm & 0xff);
+ }
+
+ /* Assembly optimization: count leading zeroes? */
+ counter = 8;
+ if (!(imm & 0xffff0000)) {
+ counter += 16;
+ imm <<= 16;
+ }
+ if (!(imm & 0xff000000)) {
+ counter += 8;
+ imm <<= 8;
+ }
+ if (!(imm & 0xf0000000)) {
+ counter += 4;
+ imm <<= 4;
+ }
+ if (!(imm & 0xc0000000)) {
+ counter += 2;
+ imm <<= 2;
+ }
+ if (!(imm & 0x80000000)) {
+ counter += 1;
+ imm <<= 1;
+ }
+ /* Since imm >= 128, this must be true. */
+ SLJIT_ASSERT(counter <= 31);
+
+ if (imm & 0x00ffffff)
+ return INVALID_IMM; /* Cannot be encoded. */
+
+ return ((imm >> 24) & 0x7f) | COPY_BITS(counter, 4, 26, 1) | COPY_BITS(counter, 1, 12, 3) | COPY_BITS(counter, 0, 7, 1);
+}
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst, sljit_uw imm)
+{
+ sljit_uw tmp;
+
+ /* MOVS cannot be used since it destroy flags. */
+
+ if (imm >= 0x10000) {
+ tmp = get_imm(imm);
+ if (tmp != INVALID_IMM)
+ return push_inst32(compiler, MOV_WI | RD4(dst) | tmp);
+ tmp = get_imm(~imm);
+ if (tmp != INVALID_IMM)
+ return push_inst32(compiler, MVN_WI | RD4(dst) | tmp);
+ }
+
+ /* set low 16 bits, set hi 16 bits to 0. */
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst)
+ | COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)));
+
+ /* set hi 16 bit if needed. */
+ if (imm >= 0x10000)
+ return push_inst32(compiler, MOVT | RD4(dst)
+ | COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16));
+ return SLJIT_SUCCESS;
+}
+
+#define ARG1_IMM 0x0010000
+#define ARG2_IMM 0x0020000
+/* SET_FLAGS must be 0x100000 as it is also the value of S bit (can be used for optimization). */
+#define SET_FLAGS 0x0100000
+#define UNUSED_RETURN 0x0200000
+
+static sljit_s32 emit_op_imm(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 dst, sljit_uw arg1, sljit_uw arg2)
+{
+ /* dst must be register, TMP_REG1
+ arg1 must be register, imm
+ arg2 must be register, imm */
+ sljit_s32 reg;
+ sljit_uw imm, imm2;
+
+ if (SLJIT_UNLIKELY((flags & (ARG1_IMM | ARG2_IMM)) == (ARG1_IMM | ARG2_IMM))) {
+ /* Both are immediates, no temporaries are used. */
+ flags &= ~ARG1_IMM;
+ FAIL_IF(load_immediate(compiler, TMP_REG1, arg1));
+ arg1 = TMP_REG1;
+ }
+
+ if (flags & (ARG1_IMM | ARG2_IMM)) {
+ reg = (sljit_s32)((flags & ARG2_IMM) ? arg1 : arg2);
+ imm = (flags & ARG2_IMM) ? arg2 : arg1;
+
+ switch (flags & 0xffff) {
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ case SLJIT_MUL:
+ /* No form with immediate operand. */
+ break;
+ case SLJIT_MOV:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG2);
+ return load_immediate(compiler, dst, imm);
+ case SLJIT_NOT:
+ if (!(flags & SET_FLAGS))
+ return load_immediate(compiler, dst, ~imm);
+ /* Since the flags should be set, we just fallback to the register mode.
+ Although some clever things could be done here, "NOT IMM" does not worth the efforts. */
+ break;
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ imm2 = NEGATE(imm);
+ if (IS_2_LO_REGS(reg, dst)) {
+ if (imm <= 0x7)
+ return push_inst16(compiler, ADDSI3 | IMM3(imm) | RD3(dst) | RN3(reg));
+ if (imm2 <= 0x7)
+ return push_inst16(compiler, SUBSI3 | IMM3(imm2) | RD3(dst) | RN3(reg));
+ if (reg == dst) {
+ if (imm <= 0xff)
+ return push_inst16(compiler, ADDSI8 | IMM8(imm) | RDN3(dst));
+ if (imm2 <= 0xff)
+ return push_inst16(compiler, SUBSI8 | IMM8(imm2) | RDN3(dst));
+ }
+ }
+ if (!(flags & SET_FLAGS)) {
+ if (imm <= 0xfff)
+ return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(imm));
+ if (imm2 <= 0xfff)
+ return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(imm2));
+ }
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2);
+ imm = get_imm(NEGATE(imm));
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ imm = get_imm(imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, ADCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (flags & ARG1_IMM) {
+ if (imm == 0 && IS_2_LO_REGS(reg, dst))
+ return push_inst16(compiler, RSBSI | RD3(dst) | RN3(reg));
+ imm = get_imm(imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, RSB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ }
+ if (flags & UNUSED_RETURN) {
+ if (imm <= 0xff && reg_map[reg] <= 7)
+ return push_inst16(compiler, CMPI | IMM8(imm) | RDN3(reg));
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, CMPI_W | RN4(reg) | imm2);
+ imm = get_imm(NEGATE(imm));
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, CMNI_W | RN4(reg) | imm);
+ break;
+ }
+ imm2 = NEGATE(imm);
+ if (IS_2_LO_REGS(reg, dst)) {
+ if (imm <= 0x7)
+ return push_inst16(compiler, SUBSI3 | IMM3(imm) | RD3(dst) | RN3(reg));
+ if (imm2 <= 0x7)
+ return push_inst16(compiler, ADDSI3 | IMM3(imm2) | RD3(dst) | RN3(reg));
+ if (reg == dst) {
+ if (imm <= 0xff)
+ return push_inst16(compiler, SUBSI8 | IMM8(imm) | RDN3(dst));
+ if (imm2 <= 0xff)
+ return push_inst16(compiler, ADDSI8 | IMM8(imm2) | RDN3(dst));
+ }
+ }
+ if (!(flags & SET_FLAGS)) {
+ if (imm <= 0xfff)
+ return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(imm));
+ if (imm2 <= 0xfff)
+ return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(imm2));
+ }
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2);
+ imm = get_imm(NEGATE(imm));
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (flags & ARG1_IMM)
+ break;
+ imm = get_imm(imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, SBCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_AND:
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, ((flags & UNUSED_RETURN) ? TSTI : ANDI) | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2);
+ imm = get_imm(~imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, BICI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_OR:
+ imm2 = get_imm(imm);
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, ORRI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm2);
+ imm = get_imm(~imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, ORNI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_XOR:
+ imm = get_imm(imm);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, EORI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm);
+ break;
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & ARG1_IMM)
+ break;
+ imm &= 0x1f;
+
+ if (imm == 0) {
+ if (!(flags & SET_FLAGS))
+ return push_inst16(compiler, MOV | SET_REGS44(dst, reg));
+ if (IS_2_LO_REGS(dst, reg))
+ return push_inst16(compiler, MOVS | RD3(dst) | RN3(reg));
+ return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(dst) | RM4(reg));
+ }
+
+ switch (flags & 0xffff) {
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ if (IS_2_LO_REGS(dst, reg))
+ return push_inst16(compiler, LSLSI | RD3(dst) | RN3(reg) | (imm << 6));
+ return push_inst32(compiler, LSL_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ if (IS_2_LO_REGS(dst, reg))
+ return push_inst16(compiler, LSRSI | RD3(dst) | RN3(reg) | (imm << 6));
+ return push_inst32(compiler, LSR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ if (IS_2_LO_REGS(dst, reg))
+ return push_inst16(compiler, ASRSI | RD3(dst) | RN3(reg) | (imm << 6));
+ return push_inst32(compiler, ASR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm));
+ case SLJIT_ROTL:
+ imm = (imm ^ 0x1f) + 1;
+ /* fallthrough */
+ default: /* SLJIT_ROTR */
+ return push_inst32(compiler, ROR_WI | RD4(dst) | RM4(reg) | IMM5(imm));
+ }
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ if (flags & ARG2_IMM) {
+ imm = arg2;
+ arg2 = (arg1 == TMP_REG1) ? TMP_REG2 : TMP_REG1;
+ FAIL_IF(load_immediate(compiler, (sljit_s32)arg2, imm));
+ }
+ else {
+ imm = arg1;
+ arg1 = (arg2 == TMP_REG1) ? TMP_REG2 : TMP_REG1;
+ FAIL_IF(load_immediate(compiler, (sljit_s32)arg1, imm));
+ }
+
+ SLJIT_ASSERT(arg1 != arg2);
+ }
+
+ /* Both arguments are registers. */
+ switch (flags & 0xffff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (dst == (sljit_s32)arg2)
+ return SLJIT_SUCCESS;
+ return push_inst16(compiler, MOV | SET_REGS44(dst, arg2));
+ case SLJIT_MOV_U8:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, UXTB | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, UXTB_W | RD4(dst) | RM4(arg2));
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, SXTB | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, SXTB_W | RD4(dst) | RM4(arg2));
+ case SLJIT_MOV_U16:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, UXTH | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, UXTH_W | RD4(dst) | RM4(arg2));
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, SXTH | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, SXTH_W | RD4(dst) | RM4(arg2));
+ case SLJIT_NOT:
+ SLJIT_ASSERT(arg1 == TMP_REG2);
+ if (IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, MVNS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, MVN_W | (flags & SET_FLAGS) | RD4(dst) | RM4(arg2));
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(arg1 == TMP_REG2);
+ return push_inst32(compiler, CLZ | RN4(arg2) | RD4(dst) | RM4(arg2));
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(arg1 == TMP_REG2);
+ FAIL_IF(push_inst32(compiler, RBIT | RN4(arg2) | RD4(dst) | RM4(arg2)));
+ return push_inst32(compiler, CLZ | RN4(dst) | RD4(dst) | RM4(dst));
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ if (IS_3_LO_REGS(dst, arg1, arg2))
+ return push_inst16(compiler, ADDS | RD3(dst) | RN3(arg1) | RM3(arg2));
+ if (dst == (sljit_s32)arg1 && !(flags & SET_FLAGS))
+ return push_inst16(compiler, ADD | SET_REGS44(dst, arg2));
+ return push_inst32(compiler, ADD_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ADCS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, ADC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (flags & UNUSED_RETURN) {
+ if (IS_2_LO_REGS(arg1, arg2))
+ return push_inst16(compiler, CMP | RD3(arg1) | RN3(arg2));
+ return push_inst16(compiler, CMP_X | SET_REGS44(arg1, arg2));
+ }
+ if (IS_3_LO_REGS(dst, arg1, arg2))
+ return push_inst16(compiler, SUBS | RD3(dst) | RN3(arg1) | RM3(arg2));
+ return push_inst32(compiler, SUB_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, SBCS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, SBC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_MUL:
+ compiler->status_flags_state = 0;
+ if (!(flags & SET_FLAGS))
+ return push_inst32(compiler, MUL | RD4(dst) | RN4(arg1) | RM4(arg2));
+ SLJIT_ASSERT(dst != TMP_REG2);
+ FAIL_IF(push_inst32(compiler, SMULL | RT4(dst) | RD4(TMP_REG2) | RN4(arg1) | RM4(arg2)));
+ /* cmp TMP_REG2, dst asr #31. */
+ return push_inst32(compiler, CMP_W | RN4(TMP_REG2) | 0x70e0 | RM4(dst));
+ case SLJIT_AND:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ANDS | RD3(dst) | RN3(arg2));
+ if ((flags & UNUSED_RETURN) && IS_2_LO_REGS(arg1, arg2))
+ return push_inst16(compiler, TST | RD3(arg1) | RN3(arg2));
+ return push_inst32(compiler, ((flags & UNUSED_RETURN) ? TST_W : AND_W) | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_OR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ORRS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, ORR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_XOR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, EORS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, EOR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_MSHL:
+ FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_SHL:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, LSLS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, LSL_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_MLSHR:
+ FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_LSHR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, LSRS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, LSR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_MASHR:
+ FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(arg2) | 0x1f));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_ASHR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, ASRS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, ASR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2));
+ case SLJIT_ROTL:
+ FAIL_IF(push_inst32(compiler, RSB_WI | RD4(TMP_REG2) | RN4(arg2) | 0));
+ arg2 = TMP_REG2;
+ /* fallthrough */
+ case SLJIT_ROTR:
+ if (dst == (sljit_s32)arg1 && IS_2_LO_REGS(dst, arg2))
+ return push_inst16(compiler, RORS | RD3(dst) | RN3(arg2));
+ return push_inst32(compiler, ROR_W | RD4(dst) | RN4(arg1) | RM4(arg2));
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+#define STORE 0x01
+#define SIGNED 0x02
+
+#define WORD_SIZE 0x00
+#define BYTE_SIZE 0x04
+#define HALF_SIZE 0x08
+#define PRELOAD 0x0c
+
+#define IS_WORD_SIZE(flags) (!((flags) & (BYTE_SIZE | HALF_SIZE)))
+#define ALIGN_CHECK(argw, imm, shift) (!((argw) & ~((imm) << (shift))))
+
+/*
+ 1st letter:
+ w = word
+ b = byte
+ h = half
+
+ 2nd letter:
+ s = signed
+ u = unsigned
+
+ 3rd letter:
+ l = load
+ s = store
+*/
+
+static const sljit_ins sljit_mem16[12] = {
+/* w u l */ 0x5800 /* ldr */,
+/* w u s */ 0x5000 /* str */,
+/* w s l */ 0x5800 /* ldr */,
+/* w s s */ 0x5000 /* str */,
+
+/* b u l */ 0x5c00 /* ldrb */,
+/* b u s */ 0x5400 /* strb */,
+/* b s l */ 0x5600 /* ldrsb */,
+/* b s s */ 0x5400 /* strb */,
+
+/* h u l */ 0x5a00 /* ldrh */,
+/* h u s */ 0x5200 /* strh */,
+/* h s l */ 0x5e00 /* ldrsh */,
+/* h s s */ 0x5200 /* strh */,
+};
+
+static const sljit_ins sljit_mem16_imm5[12] = {
+/* w u l */ 0x6800 /* ldr imm5 */,
+/* w u s */ 0x6000 /* str imm5 */,
+/* w s l */ 0x6800 /* ldr imm5 */,
+/* w s s */ 0x6000 /* str imm5 */,
+
+/* b u l */ 0x7800 /* ldrb imm5 */,
+/* b u s */ 0x7000 /* strb imm5 */,
+/* b s l */ 0x0000 /* not allowed */,
+/* b s s */ 0x7000 /* strb imm5 */,
+
+/* h u l */ 0x8800 /* ldrh imm5 */,
+/* h u s */ 0x8000 /* strh imm5 */,
+/* h s l */ 0x0000 /* not allowed */,
+/* h s s */ 0x8000 /* strh imm5 */,
+};
+
+#define MEM_IMM8 0xc00
+#define MEM_IMM12 0x800000
+static const sljit_ins sljit_mem32[13] = {
+/* w u l */ 0xf8500000 /* ldr.w */,
+/* w u s */ 0xf8400000 /* str.w */,
+/* w s l */ 0xf8500000 /* ldr.w */,
+/* w s s */ 0xf8400000 /* str.w */,
+
+/* b u l */ 0xf8100000 /* ldrb.w */,
+/* b u s */ 0xf8000000 /* strb.w */,
+/* b s l */ 0xf9100000 /* ldrsb.w */,
+/* b s s */ 0xf8000000 /* strb.w */,
+
+/* h u l */ 0xf8300000 /* ldrh.w */,
+/* h u s */ 0xf8200000 /* strsh.w */,
+/* h s l */ 0xf9300000 /* ldrsh.w */,
+/* h s s */ 0xf8200000 /* strsh.w */,
+
+/* p u l */ 0xf8100000 /* pld */,
+};
+
+/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */
+static sljit_s32 emit_set_delta(struct sljit_compiler *compiler, sljit_s32 dst, sljit_s32 reg, sljit_sw value)
+{
+ sljit_uw imm;
+
+ if (value >= 0) {
+ if (value <= 0xfff)
+ return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(value));
+ imm = get_imm((sljit_uw)value);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | RD4(dst) | RN4(reg) | imm);
+ }
+ else {
+ value = -value;
+ if (value <= 0xfff)
+ return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(value));
+ imm = get_imm((sljit_uw)value);
+ if (imm != INVALID_IMM)
+ return push_inst32(compiler, SUB_WI | RD4(dst) | RN4(reg) | imm);
+ }
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+static SLJIT_INLINE sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
+{
+ sljit_s32 other_r;
+ sljit_uw imm, tmp;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ SLJIT_ASSERT((arg & REG_MASK) != tmp_reg || (arg == SLJIT_MEM1(tmp_reg) && argw >= -0xff && argw <= 0xfff));
+
+ if (SLJIT_UNLIKELY(!(arg & REG_MASK))) {
+ imm = get_imm((sljit_uw)argw & ~(sljit_uw)0xfff);
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(tmp_reg) | imm));
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg) | (argw & 0xfff));
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw));
+ if (IS_2_LO_REGS(reg, tmp_reg) && sljit_mem16_imm5[flags])
+ return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(tmp_reg));
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(tmp_reg));
+ }
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+ other_r = OFFS_REG(arg);
+ arg &= REG_MASK;
+
+ if (!argw && IS_3_LO_REGS(reg, arg, other_r))
+ return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(other_r));
+ return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(other_r) | ((sljit_ins)argw << 4));
+ }
+
+ arg &= REG_MASK;
+
+ if (argw > 0xfff) {
+ imm = get_imm((sljit_uw)(argw & ~0xfff));
+ if (imm != INVALID_IMM) {
+ push_inst32(compiler, ADD_WI | RD4(tmp_reg) | RN4(arg) | imm);
+ arg = tmp_reg;
+ argw = argw & 0xfff;
+ }
+ }
+ else if (argw < -0xff) {
+ tmp = (sljit_uw)((-argw + 0xfff) & ~0xfff);
+ SLJIT_ASSERT(tmp >= (sljit_uw)-argw);
+ imm = get_imm(tmp);
+
+ if (imm != INVALID_IMM) {
+ push_inst32(compiler, SUB_WI | RD4(tmp_reg) | RN4(arg) | imm);
+ arg = tmp_reg;
+ argw += (sljit_sw)tmp;
+
+ SLJIT_ASSERT(argw >= 0 && argw <= 0xfff);
+ }
+ }
+
+ /* 16 bit instruction forms. */
+ if (IS_2_LO_REGS(reg, arg) && sljit_mem16_imm5[flags]) {
+ tmp = 3;
+ if (IS_WORD_SIZE(flags)) {
+ if (ALIGN_CHECK(argw, 0x1f, 2))
+ tmp = 2;
+ }
+ else if (flags & BYTE_SIZE)
+ {
+ if (ALIGN_CHECK(argw, 0x1f, 0))
+ tmp = 0;
+ }
+ else {
+ SLJIT_ASSERT(flags & HALF_SIZE);
+ if (ALIGN_CHECK(argw, 0x1f, 1))
+ tmp = 1;
+ }
+
+ if (tmp < 3)
+ return push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(arg) | ((sljit_ins)argw << (6 - tmp)));
+ }
+ else if (SLJIT_UNLIKELY(arg == SLJIT_SP) && IS_WORD_SIZE(flags) && ALIGN_CHECK(argw, 0xff, 2) && reg_map[reg] <= 7) {
+ /* SP based immediate. */
+ return push_inst16(compiler, STR_SP | (sljit_ins)((flags & STORE) ? 0 : 0x800) | RDN3(reg) | ((sljit_ins)argw >> 2));
+ }
+
+ if (argw >= 0 && argw <= 0xfff)
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | (sljit_ins)argw);
+ else if (argw < 0 && argw >= -0xff)
+ return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | (sljit_ins)-argw);
+
+ SLJIT_ASSERT(arg != tmp_reg);
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, (sljit_uw)argw));
+ if (IS_3_LO_REGS(reg, arg, tmp_reg))
+ return push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(tmp_reg));
+ return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(tmp_reg));
+}
+
+#undef ALIGN_CHECK
+#undef IS_WORD_SIZE
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 size, i, tmp, word_arg_count;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_uw offset;
+ sljit_uw imm = 0;
+#ifdef __SOFTFP__
+ sljit_u32 float_arg_count;
+#else
+ sljit_u32 old_offset, f32_offset;
+ sljit_u32 remap[3];
+ sljit_u32 *remap_ptr = remap;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--)
+ imm |= (sljit_uw)1 << reg_map[i];
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--)
+ imm |= (sljit_uw)1 << reg_map[i];
+
+ /* At least two registers must be set for PUSH_W and one for PUSH instruction. */
+ FAIL_IF((imm & 0xff00)
+ ? push_inst32(compiler, PUSH_W | (1 << 14) | imm)
+ : push_inst16(compiler, PUSH | (1 << 8) | imm));
+
+ /* Stack must be aligned to 8 bytes: (LR, R4) */
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((size & SSIZE_OF(sw)) != 0) {
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | (sizeof(sljit_sw) >> 2)));
+ size += SSIZE_OF(sw);
+ }
+
+ if (fsaveds + fscratches >= SLJIT_NUMBER_OF_FLOAT_REGISTERS) {
+ FAIL_IF(push_inst32(compiler, VPUSH | DD4(SLJIT_FS0) | ((sljit_uw)SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS << 1)));
+ } else {
+ if (fsaveds > 0)
+ FAIL_IF(push_inst32(compiler, VPUSH | DD4(SLJIT_FS0) | ((sljit_uw)fsaveds << 1)));
+ if (fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)
+ FAIL_IF(push_inst32(compiler, VPUSH | DD4(fscratches) | ((sljit_uw)(fscratches - (SLJIT_FIRST_SAVED_FLOAT_REG - 1)) << 1)));
+ }
+ }
+
+ local_size = ((size + local_size + 0x7) & ~0x7) - size;
+ compiler->local_size = local_size;
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ arg_types = 0;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ word_arg_count = 0;
+ saved_arg_count = 0;
+#ifdef __SOFTFP__
+ SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start);
+
+ offset = 0;
+ float_arg_count = 0;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7)
+ offset += sizeof(sljit_sw);
+
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst32(compiler, VMOV2 | (offset << 10) | ((offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ else
+ FAIL_IF(push_inst32(compiler, VLDR_F32 | 0x800100 | RN4(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ float_arg_count++;
+ offset += sizeof(sljit_f64) - sizeof(sljit_sw);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst32(compiler, VMOV | (float_arg_count << 16) | (offset << 10)));
+ else
+ FAIL_IF(push_inst32(compiler, VLDR_F32 | 0x800000 | RN4(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ float_arg_count++;
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (word_arg_count - 1 != (sljit_s32)(offset >> 2))
+ tmp = word_arg_count;
+ else
+ break;
+
+ if (offset < 4 * sizeof(sljit_sw))
+ FAIL_IF(push_inst16(compiler, MOV | ((sljit_ins)reg_map[tmp] & 0x7) | (((sljit_ins)reg_map[tmp] & 0x8) << 4) | (offset << 1)));
+ else if (reg_map[tmp] <= 7)
+ FAIL_IF(push_inst16(compiler, LDR_SP | RDN3(tmp)
+ | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)) >> 2)));
+ else
+ FAIL_IF(push_inst32(compiler, LDR | RT4(tmp) | RN4(SLJIT_SP)
+ | ((offset + (sljit_uw)size - 4 * sizeof(sljit_sw)))));
+ break;
+ }
+
+ offset += sizeof(sljit_sw);
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ compiler->args_size = offset;
+#else
+ offset = SLJIT_FR0;
+ old_offset = SLJIT_FR0;
+ f32_offset = 0;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset != old_offset)
+ *remap_ptr++ = VMOV_F32 | SLJIT_32 | DD4(offset) | DM4(old_offset);
+ old_offset++;
+ offset++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (f32_offset != 0) {
+ *remap_ptr++ = VMOV_F32 | 0x20 | DD4(offset) | DM4(f32_offset);
+ f32_offset = 0;
+ } else {
+ if (offset != old_offset)
+ *remap_ptr++ = VMOV_F32 | DD4(offset) | DM4(old_offset);
+ f32_offset = old_offset;
+ old_offset++;
+ }
+ offset++;
+ break;
+ default:
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_S0 - saved_arg_count, SLJIT_R0 + word_arg_count)));
+ saved_arg_count++;
+ }
+
+ word_arg_count++;
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT((sljit_uw)(remap_ptr - remap) <= sizeof(remap));
+
+ while (remap_ptr > remap)
+ FAIL_IF(push_inst32(compiler, *(--remap_ptr)));
+#endif
+
+#ifdef _WIN32
+ if (local_size >= 4096) {
+ imm = get_imm(4096);
+ SLJIT_ASSERT(imm != INVALID_IMM);
+
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm));
+
+ if (local_size < 4 * 4096) {
+ if (local_size > 2 * 4096) {
+ if (local_size > 3 * 4096) {
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm));
+ }
+
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm));
+ }
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, ((sljit_uw)local_size >> 12) - 1));
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm));
+ FAIL_IF(push_inst32(compiler, SUB_WI | SET_FLAGS | RD4(TMP_REG2) | RN4(TMP_REG2) | 1));
+ FAIL_IF(push_inst16(compiler, BCC | (0x1 << 8) /* not-equal */ | (-8 & 0xff)));
+ }
+
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ local_size &= 0xfff;
+ }
+
+ if (local_size >= 256) {
+ SLJIT_ASSERT(local_size < 4096);
+
+ if (local_size <= (127 << 2))
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | ((sljit_uw)local_size >> 2)));
+ else
+ FAIL_IF(emit_op_imm(compiler, SLJIT_SUB | ARG2_IMM, SLJIT_SP, SLJIT_SP, (sljit_uw)local_size));
+
+ FAIL_IF(push_inst32(compiler, LDRI | 0x400 | RT4(TMP_REG1) | RN4(SLJIT_SP)));
+ } else if (local_size > 0)
+ FAIL_IF(push_inst32(compiler, LDRI | 0x500 | RT4(TMP_REG1) | RN4(SLJIT_SP) | (sljit_uw)local_size));
+#else /* !_WIN32 */
+ if (local_size > 0) {
+ if (local_size <= (127 << 2))
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | ((sljit_uw)local_size >> 2)));
+ else
+ FAIL_IF(emit_op_imm(compiler, SLJIT_SUB | ARG2_IMM, SLJIT_SP, SLJIT_SP, (sljit_uw)local_size));
+ }
+#endif /* _WIN32 */
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 size;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+
+ if ((size & SSIZE_OF(sw)) != 0 && (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG))
+ size += SSIZE_OF(sw);
+
+ compiler->local_size = ((size + local_size + 0x7) & ~0x7) - size;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_add_sp(struct sljit_compiler *compiler, sljit_uw imm)
+{
+ sljit_uw imm2;
+
+ /* The TMP_REG1 register must keep its value. */
+ if (imm <= (127u << 2))
+ return push_inst16(compiler, ADD_SP_I | (imm >> 2));
+
+ if (imm <= 0xfff)
+ return push_inst32(compiler, ADDWI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | IMM12(imm));
+
+ imm2 = get_imm(imm);
+
+ if (imm2 != INVALID_IMM)
+ return push_inst32(compiler, ADD_WI | RD4(SLJIT_SP) | RN4(SLJIT_SP) | imm2);
+
+ FAIL_IF(load_immediate(compiler, TMP_REG2, imm));
+ return push_inst16(compiler, ADD_SP | RN3(TMP_REG2));
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size)
+{
+ sljit_s32 local_size, fscratches, fsaveds, i, tmp;
+ sljit_s32 restored_reg = 0;
+ sljit_s32 lr_dst = TMP_PC;
+ sljit_uw reg_list = 0;
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14 && frame_size <= 128);
+
+ local_size = compiler->local_size;
+ fscratches = compiler->fscratches;
+ fsaveds = compiler->fsaveds;
+
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if (local_size > 0)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)local_size));
+
+ if (fsaveds + fscratches >= SLJIT_NUMBER_OF_FLOAT_REGISTERS) {
+ FAIL_IF(push_inst32(compiler, VPOP | DD4(SLJIT_FS0) | ((sljit_uw)SLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS << 1)));
+ } else {
+ if (fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG)
+ FAIL_IF(push_inst32(compiler, VPOP | DD4(fscratches) | ((sljit_uw)(fscratches - (SLJIT_FIRST_SAVED_FLOAT_REG - 1)) << 1)));
+ if (fsaveds > 0)
+ FAIL_IF(push_inst32(compiler, VPOP | DD4(SLJIT_FS0) | ((sljit_uw)fsaveds << 1)));
+ }
+
+ local_size = GET_SAVED_REGISTERS_SIZE(compiler->scratches, compiler->saveds, 1) & 0x7;
+ }
+
+ if (frame_size < 0) {
+ lr_dst = TMP_REG2;
+ frame_size = 0;
+ } else if (frame_size > 0) {
+ SLJIT_ASSERT(frame_size == 1 || (frame_size & 0x7) == 0);
+ lr_dst = 0;
+ frame_size &= ~0x7;
+ }
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ if (tmp < i) {
+ restored_reg = i;
+ do {
+ reg_list |= (sljit_uw)1 << reg_map[i];
+ } while (--i > tmp);
+ }
+
+ i = compiler->scratches;
+ if (i >= SLJIT_FIRST_SAVED_REG) {
+ restored_reg = i;
+ do {
+ reg_list |= (sljit_uw)1 << reg_map[i];
+ } while (--i >= SLJIT_FIRST_SAVED_REG);
+ }
+
+ if (lr_dst == TMP_REG2 && reg_list == 0) {
+ reg_list |= (sljit_uw)1 << reg_map[TMP_REG2];
+ restored_reg = TMP_REG2;
+ lr_dst = 0;
+ }
+
+ if (lr_dst == 0 && (reg_list & (reg_list - 1)) == 0) {
+ /* The local_size does not include the saved registers. */
+ tmp = 0;
+ if (reg_list != 0) {
+ tmp = 2;
+ if (local_size <= 0xfff) {
+ if (local_size == 0) {
+ SLJIT_ASSERT(restored_reg != TMP_REG2);
+ if (frame_size == 0)
+ return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | 0x308);
+ if (frame_size > 2 * SSIZE_OF(sw))
+ return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | 0x100 | (sljit_ins)(frame_size - (2 * SSIZE_OF(sw))));
+ }
+
+ if (reg_map[restored_reg] <= 7 && local_size <= 0x3fc)
+ FAIL_IF(push_inst16(compiler, STR_SP | 0x800 | RDN3(restored_reg) | (sljit_ins)(local_size >> 2)));
+ else
+ FAIL_IF(push_inst32(compiler, LDR | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)local_size));
+ tmp = 1;
+ } else if (frame_size == 0) {
+ frame_size = (restored_reg == TMP_REG2) ? SSIZE_OF(sw) : 2 * SSIZE_OF(sw);
+ tmp = 3;
+ }
+
+ /* Place for the saved register. */
+ if (restored_reg != TMP_REG2)
+ local_size += SSIZE_OF(sw);
+ }
+
+ /* Place for the lr register. */
+ local_size += SSIZE_OF(sw);
+
+ if (frame_size > local_size)
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | ((sljit_ins)(frame_size - local_size) >> 2)));
+ else if (frame_size < local_size)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)(local_size - frame_size)));
+
+ if (tmp <= 1)
+ return SLJIT_SUCCESS;
+
+ if (tmp == 2) {
+ frame_size -= SSIZE_OF(sw);
+ if (restored_reg != TMP_REG2)
+ frame_size -= SSIZE_OF(sw);
+
+ if (reg_map[restored_reg] <= 7)
+ return push_inst16(compiler, STR_SP | 0x800 | RDN3(restored_reg) | (sljit_ins)(frame_size >> 2));
+
+ return push_inst32(compiler, LDR | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)frame_size);
+ }
+
+ tmp = (restored_reg == TMP_REG2) ? 0x304 : 0x308;
+ return push_inst32(compiler, LDRI | RT4(restored_reg) | RN4(SLJIT_SP) | (sljit_ins)tmp);
+ }
+
+ if (local_size > 0)
+ FAIL_IF(emit_add_sp(compiler, (sljit_uw)local_size));
+
+ if (!(reg_list & 0xff00) && lr_dst != TMP_REG2) {
+ if (lr_dst == TMP_PC)
+ reg_list |= 1u << 8;
+
+ /* At least one register must be set for POP instruction. */
+ SLJIT_ASSERT(reg_list != 0);
+
+ FAIL_IF(push_inst16(compiler, POP | reg_list));
+ } else {
+ if (lr_dst != 0)
+ reg_list |= (sljit_uw)1 << reg_map[lr_dst];
+
+ /* At least two registers must be set for POP_W instruction. */
+ SLJIT_ASSERT((reg_list & (reg_list - 1)) != 0);
+
+ FAIL_IF(push_inst32(compiler, POP_W | reg_list));
+ }
+
+ if (frame_size > 0)
+ return push_inst16(compiler, SUB_SP_I | (((sljit_ins)frame_size - sizeof(sljit_sw)) >> 2));
+
+ if (lr_dst != 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst16(compiler, ADD_SP_I | 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ return emit_stack_frame_release(compiler, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, src)));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+#if !(defined __ARM_FEATURE_IDIV) && !(defined __ARM_ARCH_EXT_IDIV__)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+extern unsigned long long __rt_udiv(unsigned int denominator, unsigned int numerator);
+extern long long __rt_sdiv(int denominator, int numerator);
+#elif defined(__GNUC__)
+extern unsigned int __aeabi_uidivmod(unsigned int numerator, int unsigned denominator);
+extern int __aeabi_idivmod(int numerator, int denominator);
+#else
+#error "Software divmod functions are needed"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__ARM_FEATURE_IDIV && !__ARM_ARCH_EXT_IDIV__ */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if !(defined __ARM_FEATURE_IDIV) && !(defined __ARM_ARCH_EXT_IDIV__)
+ sljit_uw saved_reg_list[3];
+ sljit_uw saved_reg_count;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ return push_inst16(compiler, BKPT);
+ case SLJIT_NOP:
+ return push_inst16(compiler, NOP);
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ return push_inst32(compiler, (op == SLJIT_LMUL_UW ? UMULL : SMULL)
+ | RD4(SLJIT_R1) | RT4(SLJIT_R0) | RN4(SLJIT_R0) | RM4(SLJIT_R1));
+#if (defined __ARM_FEATURE_IDIV) || (defined __ARM_ARCH_EXT_IDIV__)
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, SLJIT_R0)));
+ FAIL_IF(push_inst32(compiler, (op == SLJIT_DIVMOD_UW ? UDIV : SDIV) | RD4(SLJIT_R0) | RN4(SLJIT_R0) | RM4(SLJIT_R1)));
+ FAIL_IF(push_inst32(compiler, MUL | RD4(SLJIT_R1) | RN4(SLJIT_R0) | RM4(SLJIT_R1)));
+ return push_inst32(compiler, SUB_W | RD4(SLJIT_R1) | RN4(TMP_REG1) | RM4(SLJIT_R1));
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ return push_inst32(compiler, (op == SLJIT_DIV_UW ? UDIV : SDIV) | RD4(SLJIT_R0) | RN4(SLJIT_R0) | RM4(SLJIT_R1));
+#else /* !__ARM_FEATURE_IDIV && !__ARM_ARCH_EXT_IDIV__ */
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
+ SLJIT_ASSERT(reg_map[2] == 1 && reg_map[3] == 2 && reg_map[4] == 3);
+
+ saved_reg_count = 0;
+ if (compiler->scratches >= 4)
+ saved_reg_list[saved_reg_count++] = 3;
+ if (compiler->scratches >= 3)
+ saved_reg_list[saved_reg_count++] = 2;
+ if (op >= SLJIT_DIV_UW)
+ saved_reg_list[saved_reg_count++] = 1;
+
+ if (saved_reg_count > 0) {
+ FAIL_IF(push_inst32(compiler, 0xf84d0d00 | (saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* str rX, [sp, #-8/-16]! */));
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst16(compiler, 0x9001 | (saved_reg_list[1] << 8) /* str rX, [sp, #4] */));
+ }
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst16(compiler, 0x9002 | (saved_reg_list[2] << 8) /* str rX, [sp, #8] */));
+ }
+ }
+
+#ifdef _WIN32
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, SLJIT_R0)));
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_R0, SLJIT_R1)));
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_R1, TMP_REG1)));
+ FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM,
+ ((op | 0x2) == SLJIT_DIV_UW ? SLJIT_FUNC_ADDR(__rt_udiv) : SLJIT_FUNC_ADDR(__rt_sdiv))));
+#elif defined(__GNUC__)
+ FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM,
+ ((op | 0x2) == SLJIT_DIV_UW ? SLJIT_FUNC_ADDR(__aeabi_uidivmod) : SLJIT_FUNC_ADDR(__aeabi_idivmod))));
+#else
+#error "Software divmod functions are needed"
+#endif
+
+ if (saved_reg_count > 0) {
+ if (saved_reg_count >= 3) {
+ SLJIT_ASSERT(saved_reg_list[2] < 8);
+ FAIL_IF(push_inst16(compiler, 0x9802 | (saved_reg_list[2] << 8) /* ldr rX, [sp, #8] */));
+ }
+ if (saved_reg_count >= 2) {
+ SLJIT_ASSERT(saved_reg_list[1] < 8);
+ FAIL_IF(push_inst16(compiler, 0x9801 | (saved_reg_list[1] << 8) /* ldr rX, [sp, #4] */));
+ }
+ return push_inst32(compiler, 0xf85d0b00 | (saved_reg_count >= 3 ? 16 : 8)
+ | (saved_reg_list[0] << 12) /* ldr rX, [sp], #8/16 */);
+ }
+ return SLJIT_SUCCESS;
+#endif /* __ARM_FEATURE_IDIV || __ARM_ARCH_EXT_IDIV__ */
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r, flags;
+ sljit_s32 op_flags = GET_ALL_FLAGS(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ op = GET_OPCODE(op);
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ flags = BYTE_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u8)srcw;
+ break;
+ case SLJIT_MOV_S8:
+ flags = BYTE_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s8)srcw;
+ break;
+ case SLJIT_MOV_U16:
+ flags = HALF_SIZE;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_u16)srcw;
+ break;
+ case SLJIT_MOV_S16:
+ flags = HALF_SIZE | SIGNED;
+ if (src & SLJIT_IMM)
+ srcw = (sljit_s16)srcw;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ flags = 0;
+ break;
+ }
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG2, (sljit_uw)srcw));
+ else if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, flags, dst_r, src, srcw, TMP_REG1));
+ } else {
+ if (dst_r != TMP_REG1)
+ return emit_op_imm(compiler, op, dst_r, TMP_REG2, (sljit_uw)src);
+ dst_r = src;
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ }
+
+ flags = HAS_FLAGS(op_flags) ? SET_FLAGS : 0;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ emit_op_imm(compiler, flags | op, dst_r, TMP_REG2, (sljit_uw)src);
+
+ if (SLJIT_UNLIKELY(dst & SLJIT_MEM))
+ return emit_op_mem(compiler, flags | STORE, dst_r, dst, dstw, TMP_REG2);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_reg, flags, src2_reg;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ dst_reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ flags = HAS_FLAGS(op) ? SET_FLAGS : 0;
+
+ if (dst == TMP_REG1)
+ flags |= UNUSED_RETURN;
+
+ if (src1 & SLJIT_IMM)
+ flags |= ARG1_IMM;
+ else if (src1 & SLJIT_MEM) {
+ emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1);
+ src1w = TMP_REG1;
+ }
+ else
+ src1w = src1;
+
+ if (src2 & SLJIT_IMM)
+ flags |= ARG2_IMM;
+ else if (src2 & SLJIT_MEM) {
+ src2_reg = (!(flags & ARG1_IMM) && (src1w == TMP_REG1)) ? TMP_REG2 : TMP_REG1;
+ emit_op_mem(compiler, WORD_SIZE, src2_reg, src2, src2w, src2_reg);
+ src2w = src2_reg;
+ }
+ else
+ src2w = src2;
+
+ emit_op_imm(compiler, flags | GET_OPCODE(op), dst_reg, (sljit_uw)src1w, (sljit_uw)src2w);
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_op_mem(compiler, WORD_SIZE | STORE, dst_reg, dst, dstw, TMP_REG2);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_left;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ op = GET_OPCODE(op);
+ is_left = (op == SLJIT_SHL || op == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, is_left ? SLJIT_ROTL : SLJIT_ROTR, src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= 0x1f;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (reg_map[src_dst] <= 7)
+ FAIL_IF(push_inst16(compiler, (is_left ? LSLSI : LSRSI) | RD3(src_dst) | RN3(src_dst) | ((sljit_ins)src2w << 6)));
+ else
+ FAIL_IF(push_inst32(compiler, (is_left ? LSL_WI : LSR_WI) | RD4(src_dst) | RM4(src_dst) | IMM5(src2w)));
+
+ src2w = (src2w ^ 0x1f) + 1;
+ return push_inst32(compiler, ORR_W | RD4(src_dst) | RN4(src_dst) | RM4(src1) | (is_left ? 0x10 : 0x0) | IMM5(src2w));
+ }
+
+ if (op == SLJIT_MSHL || op == SLJIT_MLSHR) {
+ FAIL_IF(push_inst32(compiler, ANDI | RD4(TMP_REG2) | RN4(src2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ if (IS_2_LO_REGS(src_dst, src2))
+ FAIL_IF(push_inst16(compiler, (is_left ? LSLS : LSRS) | RD3(src_dst) | RN3(src2)));
+ else
+ FAIL_IF(push_inst32(compiler, (is_left ? LSL_W : LSR_W) | RD4(src_dst) | RN4(src_dst) | RM4(src2)));
+
+ FAIL_IF(push_inst32(compiler, (is_left ? LSR_WI : LSL_WI) | RD4(TMP_REG1) | RM4(src1) | (1 << 6)));
+ FAIL_IF(push_inst32(compiler, EORI | RD4(TMP_REG2) | RN4(src2) | 0x1f));
+ FAIL_IF(push_inst32(compiler, (is_left ? LSR_W : LSL_W) | RD4(TMP_REG1) | RN4(TMP_REG1) | RM4(TMP_REG2)));
+ return push_inst32(compiler, ORR_W | RD4(src_dst) | RN4(src_dst) | RM4(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG2, src)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, src, srcw, TMP_REG2));
+
+ return push_inst16(compiler, BX | RN3(TMP_REG2));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ return emit_op_mem(compiler, PRELOAD, TMP_PC, src, srcw, TMP_REG1);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return (freg_map[reg] << 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ if (size == 2)
+ return push_inst16(compiler, *(sljit_u16*)instruction);
+ return push_inst32(compiler, *(sljit_ins*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FPU_LOAD (1 << 20)
+
+static sljit_s32 emit_fop_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_uw imm;
+ sljit_ins inst = VSTR_F32 | (flags & (SLJIT_32 | FPU_LOAD));
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ /* Fast loads and stores. */
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | (((sljit_uw)argw & 0x3) << 6)));
+ arg = SLJIT_MEM | TMP_REG1;
+ argw = 0;
+ }
+
+ if ((arg & REG_MASK) && (argw & 0x3) == 0) {
+ if (!(argw & ~0x3fc))
+ return push_inst32(compiler, inst | 0x800000 | RN4(arg & REG_MASK) | DD4(reg) | ((sljit_uw)argw >> 2));
+ if (!(-argw & ~0x3fc))
+ return push_inst32(compiler, inst | RN4(arg & REG_MASK) | DD4(reg) | ((sljit_uw)-argw >> 2));
+ }
+
+ if (arg & REG_MASK) {
+ if (emit_set_delta(compiler, TMP_REG1, arg & REG_MASK, argw) != SLJIT_ERR_UNSUPPORTED) {
+ FAIL_IF(compiler->error);
+ return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG1) | DD4(reg));
+ }
+
+ imm = get_imm((sljit_uw)argw & ~(sljit_uw)0x3fc);
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(arg & REG_MASK) | imm));
+ return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG1) | DD4(reg) | (((sljit_uw)argw & 0x3fc) >> 2));
+ }
+
+ imm = get_imm((sljit_uw)-argw & ~(sljit_uw)0x3fc);
+ if (imm != INVALID_IMM) {
+ argw = -argw;
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(arg & REG_MASK) | imm));
+ return push_inst32(compiler, inst | RN4(TMP_REG1) | DD4(reg) | (((sljit_uw)argw & 0x3fc) >> 2));
+ }
+ }
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)argw));
+ if (arg & REG_MASK)
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, (arg & REG_MASK))));
+ return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG1) | DD4(reg));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ op ^= SLJIT_32;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src, srcw));
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst32(compiler, VCVT_S32_F32 | (op & SLJIT_32) | DD4(TMP_FREG1) | DM4(src)));
+
+ if (FAST_IS_REG(dst))
+ return push_inst32(compiler, VMOV | (1 << 20) | RT4(dst) | DN4(TMP_FREG1));
+
+ /* Store the integer value from a VFP register. */
+ return emit_fop_mem(compiler, 0, TMP_FREG1, dst, dstw);
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ op ^= SLJIT_32;
+
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst32(compiler, VMOV | RT4(src) | DN4(TMP_FREG1)));
+ else if (src & SLJIT_MEM) {
+ /* Load the integer value into a VFP register. */
+ FAIL_IF(emit_fop_mem(compiler, FPU_LOAD, TMP_FREG1, src, srcw));
+ }
+ else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)srcw));
+ FAIL_IF(push_inst32(compiler, VMOV | RT4(TMP_REG1) | DN4(TMP_FREG1)));
+ }
+
+ FAIL_IF(push_inst32(compiler, VCVT_F32_S32 | (op & SLJIT_32) | DD4(dst_r) | DM4(TMP_FREG1)));
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ op ^= SLJIT_32;
+
+ if (src1 & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src1, src1w);
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG2, src2, src2w);
+ src2 = TMP_FREG2;
+ }
+
+ FAIL_IF(push_inst32(compiler, VCMP_F32 | (op & SLJIT_32) | DD4(src1) | DM4(src2)));
+ return push_inst32(compiler, VMRS);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (GET_OPCODE(op) != SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ if (src & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, dst_r, src, srcw);
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | (op & SLJIT_32) | DD4(dst_r) | DM4(src)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst32(compiler, VNEG_F32 | (op & SLJIT_32) | DD4(dst_r) | DM4(src)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst32(compiler, VABS_F32 | (op & SLJIT_32) | DD4(dst_r) | DM4(src)));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ FAIL_IF(push_inst32(compiler, VCVT_F64_F32 | (op & SLJIT_32) | DD4(dst_r) | DM4(src)));
+ op ^= SLJIT_32;
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_fop_mem(compiler, (op & SLJIT_32), dst_r, dst, dstw);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ if (src1 & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG1, src1, src1w);
+ src1 = TMP_FREG1;
+ }
+ if (src2 & SLJIT_MEM) {
+ emit_fop_mem(compiler, (op & SLJIT_32) | FPU_LOAD, TMP_FREG2, src2, src2w);
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst32(compiler, VADD_F32 | (op & SLJIT_32) | DD4(dst_r) | DN4(src1) | DM4(src2)));
+ break;
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst32(compiler, VSUB_F32 | (op & SLJIT_32) | DD4(dst_r) | DN4(src1) | DM4(src2)));
+ break;
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst32(compiler, VMUL_F32 | (op & SLJIT_32) | DD4(dst_r) | DN4(src1) | DM4(src2)));
+ break;
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst32(compiler, VDIV_F32 | (op & SLJIT_32) | DD4(dst_r) | DN4(src1) | DM4(src2)));
+ break;
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_fop_mem(compiler, (op & SLJIT_32), TMP_FREG1, dst, dstw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ SLJIT_ASSERT(reg_map[TMP_REG2] == 14);
+
+ if (FAST_IS_REG(dst))
+ return push_inst16(compiler, MOV | SET_REGS44(dst, TMP_REG2));
+
+ /* Memory. */
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, TMP_REG1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+static sljit_uw get_cc(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ return 0x0;
+
+ case SLJIT_NOT_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ return 0x1;
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x2;
+ /* fallthrough */
+
+ case SLJIT_LESS:
+ return 0x3;
+
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD)
+ return 0x3;
+ /* fallthrough */
+
+ case SLJIT_GREATER_EQUAL:
+ return 0x2;
+
+ case SLJIT_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return 0x8;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return 0x9;
+
+ case SLJIT_SIG_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ return 0xb;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return 0xa;
+
+ case SLJIT_SIG_GREATER:
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ return 0xc;
+
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return 0xd;
+
+ case SLJIT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x1;
+ /* fallthrough */
+
+ case SLJIT_UNORDERED:
+ return 0x6;
+
+ case SLJIT_NOT_OVERFLOW:
+ if (!(compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)))
+ return 0x0;
+ /* fallthrough */
+
+ case SLJIT_ORDERED:
+ return 0x7;
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ return 0x4;
+
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return 0x5;
+
+ default: /* SLJIT_JUMP */
+ SLJIT_UNREACHABLE();
+ return 0xe;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+ sljit_ins cc;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ PTR_FAIL_IF(emit_imm32_const(compiler, TMP_REG1, 0));
+ if (type < SLJIT_JUMP) {
+ jump->flags |= IS_COND;
+ cc = get_cc(compiler, type);
+ jump->flags |= cc << 8;
+ PTR_FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ }
+
+ jump->addr = compiler->size;
+ if (type <= SLJIT_JUMP)
+ PTR_FAIL_IF(push_inst16(compiler, BX | RN3(TMP_REG1)));
+ else {
+ jump->flags |= IS_BL;
+ PTR_FAIL_IF(push_inst16(compiler, BLX | RN3(TMP_REG1)));
+ }
+
+ return jump;
+}
+
+#ifdef __SOFTFP__
+
+static sljit_s32 softfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src, sljit_u32 *extra_space)
+{
+ sljit_u32 is_tail_call = *extra_space & SLJIT_CALL_RETURN;
+ sljit_u32 offset = 0;
+ sljit_u32 word_arg_offset = 0;
+ sljit_u32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_u32 src_offset = 4 * sizeof(sljit_sw);
+ sljit_u8 offsets[4];
+ sljit_u8 *offset_ptr = offsets;
+
+ if (src && FAST_IS_REG(*src))
+ src_offset = (sljit_u32)reg_map[*src] * sizeof(sljit_sw);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7)
+ offset += sizeof(sljit_sw);
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_f64);
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_f32);
+ float_arg_count++;
+ break;
+ default:
+ *offset_ptr++ = (sljit_u8)offset;
+ offset += sizeof(sljit_sw);
+ word_arg_offset += sizeof(sljit_sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (offset > 4 * sizeof(sljit_sw) && (!is_tail_call || offset > compiler->args_size)) {
+ /* Keep lr register on the stack. */
+ if (is_tail_call)
+ offset += sizeof(sljit_sw);
+
+ offset = ((offset - 4 * sizeof(sljit_sw)) + 0x7) & ~(sljit_uw)0x7;
+
+ *extra_space = offset;
+
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, (sljit_s32)offset));
+ else
+ FAIL_IF(push_inst16(compiler, SUB_SP_I | (offset >> 2)));
+ } else {
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, -1));
+ *extra_space = 0;
+ }
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 12);
+
+ /* Process arguments in reversed direction. */
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count--;
+ offset = *(--offset_ptr);
+
+ SLJIT_ASSERT((offset & 0x7) == 0);
+
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset || src_offset == offset + sizeof(sljit_sw)) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst32(compiler, VMOV2 | 0x100000 | (offset << 10) | ((offset + sizeof(sljit_sw)) << 14) | float_arg_count));
+ } else
+ FAIL_IF(push_inst32(compiler, VSTR_F32 | 0x800100 | RN4(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count--;
+ offset = *(--offset_ptr);
+
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ FAIL_IF(push_inst32(compiler, VMOV | 0x100000 | (float_arg_count << 16) | (offset << 10)));
+ } else
+ FAIL_IF(push_inst32(compiler, VSTR_F32 | 0x800000 | RN4(SLJIT_SP)
+ | (float_arg_count << 12) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ break;
+ default:
+ word_arg_offset -= sizeof(sljit_sw);
+ offset = *(--offset_ptr);
+
+ SLJIT_ASSERT(offset >= word_arg_offset);
+
+ if (offset != word_arg_offset) {
+ if (offset < 4 * sizeof(sljit_sw)) {
+ if (src_offset == offset) {
+ FAIL_IF(push_inst16(compiler, MOV | (src_offset << 1) | 4 | (1 << 7)));
+ *src = TMP_REG1;
+ }
+ else if (src_offset == word_arg_offset) {
+ *src = (sljit_s32)(1 + (offset >> 2));
+ src_offset = offset;
+ }
+ FAIL_IF(push_inst16(compiler, MOV | (offset >> 2) | (word_arg_offset << 1)));
+ } else
+ FAIL_IF(push_inst16(compiler, STR_SP | (word_arg_offset << 6) | ((offset - 4 * sizeof(sljit_sw)) >> 2)));
+ }
+ break;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 softfloat_post_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ if ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64)
+ FAIL_IF(push_inst32(compiler, VMOV2 | (1 << 16) | (0 << 12) | 0));
+ if ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F32)
+ FAIL_IF(push_inst32(compiler, VMOV | (0 << 16) | (0 << 12)));
+
+ return SLJIT_SUCCESS;
+}
+
+#else
+
+static sljit_s32 hardfloat_call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_u32 offset = SLJIT_FR0;
+ sljit_u32 new_offset = SLJIT_FR0;
+ sljit_u32 f32_offset = 0;
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset != new_offset)
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | SLJIT_32 | DD4(new_offset) | DM4(offset)));
+
+ new_offset++;
+ offset++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (f32_offset != 0) {
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | 0x400000 | DD4(f32_offset) | DM4(offset)));
+ f32_offset = 0;
+ } else {
+ if (offset != new_offset)
+ FAIL_IF(push_inst32(compiler, VMOV_F32 | 0x400000 | DD4(new_offset) | DM4(offset)));
+ f32_offset = new_offset;
+ new_offset++;
+ }
+ offset++;
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+#ifdef __SOFTFP__
+ struct sljit_jump *jump;
+ sljit_u32 extra_space = (sljit_u32)type;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#ifdef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ PTR_FAIL_IF(softfloat_call_with_args(compiler, arg_types, NULL, &extra_space));
+ SLJIT_ASSERT((extra_space & 0x7) == 0);
+
+ if ((type & SLJIT_CALL_RETURN) && extra_space == 0)
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ if (extra_space > 0) {
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(push_inst32(compiler, LDR | RT4(TMP_REG2)
+ | RN4(SLJIT_SP) | (extra_space - sizeof(sljit_sw))));
+
+ PTR_FAIL_IF(push_inst16(compiler, ADD_SP_I | (extra_space >> 2)));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(push_inst16(compiler, BX | RN3(TMP_REG2)));
+ return jump;
+ }
+ }
+
+ SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN));
+ PTR_FAIL_IF(softfloat_post_call_with_args(compiler, arg_types));
+ return jump;
+ }
+#endif /* __SOFTFP__ */
+
+ if (type & SLJIT_CALL_RETURN) {
+ /* ldmia sp!, {..., lr} */
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, -1));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+#ifndef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+#endif /* !__SOFTFP__ */
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] != 14);
+
+ if (!(src & SLJIT_IMM)) {
+ if (FAST_IS_REG(src)) {
+ SLJIT_ASSERT(reg_map[src] != 14);
+ return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(src));
+ }
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, src, srcw, TMP_REG1));
+ if (type >= SLJIT_FAST_CALL)
+ return push_inst16(compiler, BLX | RN3(TMP_REG1));
+ }
+
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+ FAIL_IF(emit_imm32_const(compiler, TMP_REG1, 0));
+ jump->addr = compiler->size;
+ return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+#ifdef __SOFTFP__
+ sljit_u32 extra_space = (sljit_u32)type;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ if ((type & SLJIT_CALL_RETURN) && (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG1, src)));
+ src = TMP_REG1;
+ }
+
+#ifdef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ FAIL_IF(softfloat_call_with_args(compiler, arg_types, &src, &extra_space));
+ SLJIT_ASSERT((extra_space & 0x7) == 0);
+
+ if ((type & SLJIT_CALL_RETURN) && extra_space == 0)
+ type = SLJIT_JUMP;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (extra_space > 0) {
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(push_inst32(compiler, LDR | RT4(TMP_REG2)
+ | RN4(SLJIT_SP) | (extra_space - sizeof(sljit_sw))));
+
+ FAIL_IF(push_inst16(compiler, ADD_SP_I | (extra_space >> 2)));
+
+ if (type & SLJIT_CALL_RETURN)
+ return push_inst16(compiler, BX | RN3(TMP_REG2));
+ }
+
+ SLJIT_ASSERT(!(type & SLJIT_CALL_RETURN));
+ return softfloat_post_call_with_args(compiler, arg_types);
+ }
+#endif /* __SOFTFP__ */
+
+ if (type & SLJIT_CALL_RETURN) {
+ /* ldmia sp!, {..., lr} */
+ FAIL_IF(emit_stack_frame_release(compiler, -1));
+ type = SLJIT_JUMP;
+ }
+
+#ifndef __SOFTFP__
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ FAIL_IF(hardfloat_call_with_args(compiler, arg_types));
+#endif /* !__SOFTFP__ */
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+#ifdef __SOFTFP__
+
+static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ if (src == SLJIT_FR0)
+ return SLJIT_SUCCESS;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw);
+ }
+
+ if (FAST_IS_REG(src)) {
+ if (op & SLJIT_32)
+ return push_inst32(compiler, VMOV | (1 << 20) | DN4(src) | RT4(SLJIT_R0));
+ return push_inst32(compiler, VMOV2 | (1 << 20) | DM4(src) | RT4(SLJIT_R0) | RN4(SLJIT_R1));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (op & SLJIT_32)
+ return sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_R0, 0, src, srcw);
+ return sljit_emit_mem(compiler, SLJIT_MOV, SLJIT_REG_PAIR(SLJIT_R0, SLJIT_R1), src, srcw);
+}
+
+#endif /* __SOFTFP__ */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 dst_r, flags = GET_ALL_FLAGS(op);
+ sljit_ins cc;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ cc = get_cc(compiler, type);
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (op < SLJIT_ADD) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4));
+ if (reg_map[dst_r] > 7) {
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(dst_r) | 1));
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(dst_r) | 0));
+ } else {
+ /* The movsi (immediate) instruction does not set flags in IT block. */
+ FAIL_IF(push_inst16(compiler, MOVSI | RDN3(dst_r) | 1));
+ FAIL_IF(push_inst16(compiler, MOVSI | RDN3(dst_r) | 0));
+ }
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, TMP_REG2);
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (op == SLJIT_AND) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4));
+ FAIL_IF(push_inst32(compiler, ANDI | RN4(dst_r) | RD4(dst_r) | 1));
+ FAIL_IF(push_inst32(compiler, ANDI | RN4(dst_r) | RD4(dst_r) | 0));
+ }
+ else {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ FAIL_IF(push_inst32(compiler, ((op == SLJIT_OR) ? ORRI : EORI) | RN4(dst_r) | RD4(dst_r) | 1));
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, TMP_REG2));
+
+ if (!(flags & SLJIT_SET_Z))
+ return SLJIT_SUCCESS;
+
+ /* The condition must always be set, even if the ORR/EORI is not executed above. */
+ return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(TMP_REG1) | RM4(dst_r));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_uw cc, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ cc = get_cc(compiler, type & ~SLJIT_32);
+
+ if (!(src & SLJIT_IMM)) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst16(compiler, MOV | SET_REGS44(dst_reg, src));
+ }
+
+ tmp = (sljit_uw) srcw;
+
+ if (tmp < 0x10000) {
+ /* set low 16 bits, set hi 16 bits to 0. */
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst32(compiler, MOVW | RD4(dst_reg)
+ | COPY_BITS(tmp, 12, 16, 4) | COPY_BITS(tmp, 11, 26, 1) | COPY_BITS(tmp, 8, 12, 3) | (tmp & 0xff));
+ }
+
+ tmp = get_imm((sljit_uw)srcw);
+ if (tmp != INVALID_IMM) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst32(compiler, MOV_WI | RD4(dst_reg) | tmp);
+ }
+
+ tmp = get_imm(~(sljit_uw)srcw);
+ if (tmp != INVALID_IMM) {
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8));
+ return push_inst32(compiler, MVN_WI | RD4(dst_reg) | tmp);
+ }
+
+ FAIL_IF(push_inst16(compiler, IT | (cc << 4) | ((cc & 0x1) << 3) | 0x4));
+
+ tmp = (sljit_uw) srcw;
+ FAIL_IF(push_inst32(compiler, MOVW | RD4(dst_reg)
+ | COPY_BITS(tmp, 12, 16, 4) | COPY_BITS(tmp, 11, 26, 1) | COPY_BITS(tmp, 8, 12, 3) | (tmp & 0xff)));
+ return push_inst32(compiler, MOVT | RD4(dst_reg)
+ | COPY_BITS(tmp, 12 + 16, 16, 4) | COPY_BITS(tmp, 11 + 16, 26, 1) | COPY_BITS(tmp, 8 + 16, 12, 3) | ((tmp & 0xff0000) >> 16));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+ sljit_uw imm, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ if (type & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32)) {
+ if ((mem & REG_MASK) == 0) {
+ if ((memw & 0xfff) >= (0x1000 - SSIZE_OF(sw))) {
+ imm = get_imm((sljit_uw)((memw + 0x1000) & ~0xfff));
+
+ if (imm != INVALID_IMM)
+ memw = (memw & 0xfff) - 0x1000;
+ } else {
+ imm = get_imm((sljit_uw)(memw & ~0xfff));
+
+ if (imm != INVALID_IMM)
+ memw &= 0xff;
+ }
+
+ if (imm == INVALID_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ memw = 0;
+ } else
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(TMP_REG1) | imm));
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (mem & OFFS_REG_MASK) {
+ FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(mem & REG_MASK) | RM4(OFFS_REG(mem)) | ((sljit_uw)(memw & 0x3) << 6)));
+ memw = 0;
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (memw < -0xff) {
+ /* Zero value can be included in the first case. */
+ if ((-memw & 0xfff) <= SSIZE_OF(sw))
+ tmp = (sljit_uw)((-memw + 0x7ff) & ~0x7ff);
+ else
+ tmp = (sljit_uw)((-memw + 0xfff) & ~0xfff);
+
+ SLJIT_ASSERT(tmp >= (sljit_uw)-memw);
+ imm = get_imm(tmp);
+
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm));
+ memw += (sljit_sw)tmp;
+ SLJIT_ASSERT(memw >= 0 && memw <= 0xfff - SSIZE_OF(sw));
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (memw >= (0x1000 - SSIZE_OF(sw))) {
+ if ((memw & 0xfff) >= (0x1000 - SSIZE_OF(sw))) {
+ imm = get_imm((sljit_uw)((memw + 0x1000) & ~0xfff));
+
+ if (imm != INVALID_IMM)
+ memw = (memw & 0xfff) - 0x1000;
+ } else {
+ imm = get_imm((sljit_uw)(memw & ~0xfff));
+
+ if (imm != INVALID_IMM)
+ memw &= 0xfff;
+ }
+
+ if (imm != INVALID_IMM) {
+ SLJIT_ASSERT(memw >= -0xff && memw <= 0xfff);
+ FAIL_IF(push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm));
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ }
+
+ flags = WORD_SIZE;
+
+ SLJIT_ASSERT(memw <= 0xfff - SSIZE_OF(sw) && memw >= -0xff);
+
+ if (type & SLJIT_MEM_STORE) {
+ flags |= STORE;
+ } else if (REG_PAIR_FIRST(reg) == (mem & REG_MASK)) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, REG_PAIR_SECOND(reg), mem, memw + SSIZE_OF(sw), TMP_REG2));
+ return emit_op_mem(compiler, WORD_SIZE, REG_PAIR_FIRST(reg), mem, memw, TMP_REG2);
+ }
+
+ FAIL_IF(emit_op_mem(compiler, flags, REG_PAIR_FIRST(reg), mem, memw, TMP_REG2));
+ return emit_op_mem(compiler, flags, REG_PAIR_SECOND(reg), mem, memw + SSIZE_OF(sw), TMP_REG2);
+ }
+
+ flags = 1 << 23;
+
+ if ((mem & REG_MASK) == 0) {
+ tmp = (sljit_uw)(memw & 0x7fc);
+ imm = get_imm((sljit_uw)((memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc));
+
+ if (imm == INVALID_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ memw = 0;
+ } else {
+ FAIL_IF(push_inst32(compiler, MOV_WI | RD4(TMP_REG1) | imm));
+ memw = (memw & 0x3fc) >> 2;
+
+ if (tmp > 0x400) {
+ memw = 0x100 - memw;
+ flags = 0;
+ }
+
+ SLJIT_ASSERT(memw >= 0 && memw <= 0xff);
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (mem & OFFS_REG_MASK) {
+ FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(mem & REG_MASK) | RM4(OFFS_REG(mem)) | ((sljit_uw)(memw & 0x3) << 6)));
+ memw = 0;
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else if (memw < 0) {
+ if ((-memw & ~0x3fc) == 0) {
+ flags = 0;
+ memw = -memw >> 2;
+ } else {
+ tmp = (sljit_uw)(-memw & 0x7fc);
+ imm = get_imm((sljit_uw)((-memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc));
+
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm));
+ memw = (-memw & 0x3fc) >> 2;
+
+ if (tmp <= 0x400)
+ flags = 0;
+ else
+ memw = 0x100 - memw;
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ }
+ } else if ((memw & ~0x3fc) != 0) {
+ tmp = (sljit_uw)(memw & 0x7fc);
+ imm = get_imm((sljit_uw)((memw + (tmp <= 0x400 ? 0 : 0x400)) & ~0x3fc));
+
+ if (imm != INVALID_IMM) {
+ FAIL_IF(push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(mem & REG_MASK) | imm));
+ memw = (memw & 0x3fc) >> 2;
+
+ if (tmp > 0x400) {
+ memw = 0x100 - memw;
+ flags = 0;
+ }
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, (sljit_uw)memw));
+ FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, mem & REG_MASK)));
+ memw = 0;
+ }
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ } else
+ memw >>= 2;
+
+ SLJIT_ASSERT(memw >= 0 && memw <= 0xff);
+ return push_inst32(compiler, ((type & SLJIT_MEM_STORE) ? STRD : LDRD) | (sljit_ins)flags | RN4(mem & REG_MASK) | RT4(REG_PAIR_FIRST(reg)) | RD4(REG_PAIR_SECOND(reg)) | (sljit_ins)memw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+
+ if ((mem & OFFS_REG_MASK) || (memw > 255 || memw < -255))
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ case SLJIT_MOV_P:
+ flags = WORD_SIZE;
+ break;
+ case SLJIT_MOV_U8:
+ flags = BYTE_SIZE;
+ break;
+ case SLJIT_MOV_S8:
+ flags = BYTE_SIZE | SIGNED;
+ break;
+ case SLJIT_MOV_U16:
+ flags = HALF_SIZE;
+ break;
+ case SLJIT_MOV_S16:
+ flags = HALF_SIZE | SIGNED;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ flags = WORD_SIZE;
+ break;
+ }
+
+ if (type & SLJIT_MEM_STORE)
+ flags |= STORE;
+
+ inst = sljit_mem32[flags] | 0x900;
+
+ if (!(type & SLJIT_MEM_POST))
+ inst |= 0x400;
+
+ if (memw >= 0)
+ inst |= 0x200;
+ else
+ memw = -memw;
+
+ return push_inst32(compiler, inst | RT4(reg) | RN4(mem & REG_MASK) | (sljit_ins)memw);
+}
+
+static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s32 max_offset)
+{
+ sljit_s32 arg = *mem;
+ sljit_sw argw = *memw;
+ sljit_uw imm;
+
+ *mem = TMP_REG1;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ *memw = 0;
+ return push_inst32(compiler, ADD_W | RD4(TMP_REG1) | RN4(arg & REG_MASK) | RM4(OFFS_REG(arg)) | ((sljit_uw)(argw & 0x3) << 6));
+ }
+
+ arg &= REG_MASK;
+
+ if (arg) {
+ if (argw <= max_offset && argw >= -0xff) {
+ *mem = arg;
+ return SLJIT_SUCCESS;
+ }
+
+ if (argw < 0) {
+ imm = get_imm((sljit_uw)(-argw & ~0xff));
+
+ if (imm) {
+ *memw = -(-argw & 0xff);
+ return push_inst32(compiler, SUB_WI | RD4(TMP_REG1) | RN4(arg) | imm);
+ }
+ } else if ((argw & 0xfff) <= max_offset) {
+ imm = get_imm((sljit_uw)(argw & ~0xfff));
+
+ if (imm) {
+ *memw = argw & 0xfff;
+ return push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(arg) | imm);
+ }
+ } else {
+ imm = get_imm((sljit_uw)((argw | 0xfff) + 1));
+
+ if (imm) {
+ *memw = (argw & 0xfff) - 0x1000;
+ return push_inst32(compiler, ADD_WI | RD4(TMP_REG1) | RN4(arg) | imm);
+ }
+ }
+ }
+
+ imm = (sljit_uw)(argw & ~0xfff);
+
+ if ((argw & 0xfff) > max_offset) {
+ imm += 0x1000;
+ *memw = (argw & 0xfff) - 0x1000;
+ } else
+ *memw = argw & 0xfff;
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, imm));
+
+ if (arg == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst16(compiler, ADD | SET_REGS44(TMP_REG1, arg));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ if (type & SLJIT_MEM_UNALIGNED_32)
+ return emit_fop_mem(compiler, ((type ^ SLJIT_32) & SLJIT_32) | ((type & SLJIT_MEM_STORE) ? 0 : FPU_LOAD), freg, mem, memw);
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst32(compiler, VMOV | (1 << 20) | DN4(freg) | RT4(TMP_REG2)));
+
+ if (type & SLJIT_32)
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw, TMP_REG1);
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw, TMP_REG1));
+ FAIL_IF(push_inst32(compiler, VMOV | (1 << 20) | DN4(freg) | 0x80 | RT4(TMP_REG2)));
+ return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, mem, memw + 4, TMP_REG1);
+ }
+
+ if (type & SLJIT_32) {
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1));
+ return push_inst32(compiler, VMOV | DN4(freg) | RT4(TMP_REG2));
+ }
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, 0xfff - 4));
+ mem |= SLJIT_MEM;
+
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG2, mem, memw, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_SIZE, TMP_REG1, mem, memw + 4, TMP_REG1));
+ return push_inst32(compiler, VMOV2 | DM4(freg) | RT4(TMP_REG2) | RN4(TMP_REG1));
+}
+
+#undef FPU_LOAD
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ PTR_FAIL_IF(emit_imm32_const(compiler, dst_r, (sljit_uw)init_value));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ PTR_FAIL_IF(emit_imm32_const(compiler, dst_r, 0));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw, TMP_REG2));
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_u16 *inst = (sljit_u16*)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 4, 0);
+ modify_imm32_const(inst, new_target);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 4, 1);
+ inst = (sljit_u16 *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 4);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_32.c
new file mode 100644
index 0000000000..e6853c98f6
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_32.c
@@ -0,0 +1,314 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/* mips 32-bit arch dependent functions. */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_ar, sljit_sw imm)
+{
+ if (!(imm & ~0xffff))
+ return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
+
+ if (imm < 0 && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
+
+ FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value)
+{
+ FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 16), DR(dst)));
+ return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins *)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ SLJIT_ASSERT((inst[0] & 0xffe00000) == LUI && (inst[1] & 0xfc000000) == ORI);
+ inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 16) & 0xffff);
+ inst[1] = (inst[1] & 0xffff0000) | (new_target & 0xffff);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr, sljit_u32 *extra_space)
+{
+ sljit_u32 is_tail_call = *extra_space & SLJIT_CALL_RETURN;
+ sljit_u32 offset = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_ins prev_ins = NOP;
+ sljit_ins ins = NOP;
+ sljit_u8 offsets[4];
+ sljit_u8 *offsets_ptr = offsets;
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 4 && freg_map[TMP_FREG1] == 12);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ /* See ABI description in sljit_emit_enter. */
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+ *offsets_ptr = (sljit_u8)offset;
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (offset & 0x7) {
+ offset += sizeof(sljit_sw);
+ *offsets_ptr = (sljit_u8)offset;
+ }
+
+ if (word_arg_count == 0 && float_arg_count <= 1)
+ *offsets_ptr = (sljit_u8)(254 + float_arg_count);
+
+ offset += sizeof(sljit_f64);
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (word_arg_count == 0 && float_arg_count <= 1)
+ *offsets_ptr = (sljit_u8)(254 + float_arg_count);
+
+ offset += sizeof(sljit_f32);
+ float_arg_count++;
+ break;
+ default:
+ offset += sizeof(sljit_sw);
+ word_arg_count++;
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ offsets_ptr++;
+ }
+
+ /* Stack is aligned to 16 bytes. */
+ SLJIT_ASSERT(offset <= 8 * sizeof(sljit_sw));
+
+ if (offset > 4 * sizeof(sljit_sw) && (!is_tail_call || offset > compiler->args_size)) {
+ if (is_tail_call) {
+ offset = (offset + sizeof(sljit_sw) + 15) & ~(sljit_uw)0xf;
+ FAIL_IF(emit_stack_frame_release(compiler, (sljit_s32)offset, &prev_ins));
+ *extra_space = offset;
+ } else {
+ FAIL_IF(push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(-16), DR(SLJIT_SP)));
+ *extra_space = 16;
+ }
+ } else {
+ if (is_tail_call)
+ FAIL_IF(emit_stack_frame_release(compiler, 0, &prev_ins));
+ *extra_space = 0;
+ }
+
+ while (types) {
+ --offsets_ptr;
+
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (*offsets_ptr < 4 * sizeof (sljit_sw)) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+
+ /* Must be preceded by at least one other argument,
+ * and its starting offset must be 8 because of alignment. */
+ SLJIT_ASSERT((*offsets_ptr >> 2) == 2);
+
+ prev_ins = MFC1 | TA(6) | FS(float_arg_count) | (1 << 11);
+ ins = MFC1 | TA(7) | FS(float_arg_count);
+ } else if (*offsets_ptr < 254)
+ ins = SDC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(*offsets_ptr);
+ else if (*offsets_ptr == 254)
+ ins = MOV_S | FMT_D | FS(SLJIT_FR0) | FD(TMP_FREG1);
+
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (*offsets_ptr < 4 * sizeof (sljit_sw))
+ ins = MFC1 | TA(4 + (*offsets_ptr >> 2)) | FS(float_arg_count);
+ else if (*offsets_ptr < 254)
+ ins = SWC1 | S(SLJIT_SP) | FT(float_arg_count) | IMM(*offsets_ptr);
+ else if (*offsets_ptr == 254)
+ ins = MOV_S | FMT_S | FS(SLJIT_FR0) | FD(TMP_FREG1);
+
+ float_arg_count--;
+ break;
+ default:
+ if (*offsets_ptr >= 4 * sizeof (sljit_sw))
+ ins = SW | S(SLJIT_SP) | T(word_arg_count) | IMM(*offsets_ptr);
+ else if ((*offsets_ptr >> 2) != word_arg_count - 1)
+ ins = ADDU | S(word_arg_count) | TA(0) | DA(4 + (*offsets_ptr >> 2));
+ else if (*offsets_ptr == 0)
+ ins = ADDU | S(SLJIT_R0) | TA(0) | DA(4);
+
+ word_arg_count--;
+ break;
+ }
+
+ if (ins != NOP) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+ prev_ins = ins;
+ ins = NOP;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ *ins_ptr = prev_ins;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_u32 extra_space = 0;
+ sljit_ins ins = NOP;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG) {
+ extra_space = (sljit_u32)type;
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space));
+ } else if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (ins == NOP && compiler->delay_slot != UNMOVABLE_INS)
+ jump->flags |= IS_MOVABLE;
+
+ if (!(type & SLJIT_CALL_RETURN) || extra_space > 0) {
+ jump->flags |= IS_JAL;
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ jump->flags |= IS_CALL;
+
+ PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ } else
+ PTR_FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+
+ /* Maximum number of instructions required for generating a constant. */
+ compiler->size += 2;
+
+ if (extra_space == 0)
+ return jump;
+
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG,
+ SLJIT_MEM1(SLJIT_SP), (sljit_sw)(extra_space - sizeof(sljit_sw))));
+
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
+
+ PTR_FAIL_IF(push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(extra_space),
+ (type & SLJIT_CALL_RETURN) ? UNMOVABLE_INS : DR(SLJIT_SP)));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u32 extra_space = (sljit_u32)type;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ if (ins != NOP)
+ FAIL_IF(push_inst(compiler, ins, MOVABLE_INS));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+ }
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
+ else if (src != PIC_ADDR_REG)
+ FAIL_IF(push_inst(compiler, ADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+
+ FAIL_IF(call_with_args(compiler, arg_types, &ins, &extra_space));
+
+ /* Register input. */
+ if (!(type & SLJIT_CALL_RETURN) || extra_space > 0)
+ FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
+ FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+
+ if (extra_space == 0)
+ return SLJIT_SUCCESS;
+
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG,
+ SLJIT_MEM1(SLJIT_SP), (sljit_sw)(extra_space - sizeof(sljit_sw))));
+
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
+
+ return push_inst(compiler, ADDIU | S(SLJIT_SP) | T(SLJIT_SP) | IMM(extra_space),
+ (type & SLJIT_CALL_RETURN) ? UNMOVABLE_INS : DR(SLJIT_SP));
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_64.c b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_64.c
new file mode 100644
index 0000000000..d2a5924f8e
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_64.c
@@ -0,0 +1,319 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/* mips 64-bit arch dependent functions. */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_ar, sljit_sw imm)
+{
+ sljit_s32 shift = 32;
+ sljit_s32 shift2;
+ sljit_s32 inv = 0;
+ sljit_ins ins;
+ sljit_uw uimm;
+
+ if (!(imm & ~0xffff))
+ return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
+
+ if (imm < 0 && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar);
+
+ if (imm <= 0x7fffffffl && imm >= -0x80000000l) {
+ FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS;
+ }
+
+ /* Zero extended number. */
+ uimm = (sljit_uw)imm;
+ if (imm < 0) {
+ uimm = ~(sljit_uw)imm;
+ inv = 1;
+ }
+
+ while (!(uimm & 0xff00000000000000l)) {
+ shift -= 8;
+ uimm <<= 8;
+ }
+
+ if (!(uimm & 0xf000000000000000l)) {
+ shift -= 4;
+ uimm <<= 4;
+ }
+
+ if (!(uimm & 0xc000000000000000l)) {
+ shift -= 2;
+ uimm <<= 2;
+ }
+
+ if ((sljit_sw)uimm < 0) {
+ uimm >>= 1;
+ shift += 1;
+ }
+ SLJIT_ASSERT(((uimm & 0xc000000000000000l) == 0x4000000000000000l) && (shift > 0) && (shift <= 32));
+
+ if (inv)
+ uimm = ~uimm;
+
+ FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(uimm >> 48), dst_ar));
+ if (uimm & 0x0000ffff00000000l)
+ FAIL_IF(push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(uimm >> 32), dst_ar));
+
+ imm &= (1l << shift) - 1;
+ if (!(imm & ~0xffff)) {
+ ins = (shift == 32) ? DSLL32 : DSLL;
+ if (shift < 32)
+ ins |= SH_IMM(shift);
+ FAIL_IF(push_inst(compiler, ins | TA(dst_ar) | DA(dst_ar), dst_ar));
+ return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar);
+ }
+
+ /* Double shifts needs to be performed. */
+ uimm <<= 32;
+ shift2 = shift - 16;
+
+ while (!(uimm & 0xf000000000000000l)) {
+ shift2 -= 4;
+ uimm <<= 4;
+ }
+
+ if (!(uimm & 0xc000000000000000l)) {
+ shift2 -= 2;
+ uimm <<= 2;
+ }
+
+ if (!(uimm & 0x8000000000000000l)) {
+ shift2--;
+ uimm <<= 1;
+ }
+
+ SLJIT_ASSERT((uimm & 0x8000000000000000l) && (shift2 > 0) && (shift2 <= 16));
+
+ FAIL_IF(push_inst(compiler, DSLL | TA(dst_ar) | DA(dst_ar) | SH_IMM(shift - shift2), dst_ar));
+ FAIL_IF(push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(uimm >> 48), dst_ar));
+ FAIL_IF(push_inst(compiler, DSLL | TA(dst_ar) | DA(dst_ar) | SH_IMM(shift2), dst_ar));
+
+ imm &= (1l << shift2) - 1;
+ return !(imm & 0xffff) ? SLJIT_SUCCESS : push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar);
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value)
+{
+ FAIL_IF(push_inst(compiler, LUI | T(dst) | IMM(init_value >> 48), DR(dst)));
+ FAIL_IF(push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value >> 32), DR(dst)));
+ FAIL_IF(push_inst(compiler, DSLL | T(dst) | D(dst) | SH_IMM(16), DR(dst)));
+ FAIL_IF(push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value >> 16), DR(dst)));
+ FAIL_IF(push_inst(compiler, DSLL | T(dst) | D(dst) | SH_IMM(16), DR(dst)));
+ return push_inst(compiler, ORI | S(dst) | T(dst) | IMM(init_value), DR(dst));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins *)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 6, 0);
+ inst[0] = (inst[0] & 0xffff0000) | ((sljit_ins)(new_target >> 48) & 0xffff);
+ inst[1] = (inst[1] & 0xffff0000) | ((sljit_ins)(new_target >> 32) & 0xffff);
+ inst[3] = (inst[3] & 0xffff0000) | ((sljit_ins)(new_target >> 16) & 0xffff);
+ inst[5] = (inst[5] & 0xffff0000) | ((sljit_ins)new_target & 0xffff);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 6, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 6);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_ins *ins_ptr)
+{
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_ins prev_ins = *ins_ptr;
+ sljit_ins ins = NOP;
+
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 4 && freg_map[TMP_FREG1] == 12);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ case SLJIT_ARG_TYPE_F32:
+ arg_count++;
+ float_arg_count++;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (arg_count != float_arg_count)
+ ins = MOV_S | FMT_D | FS(float_arg_count) | FD(arg_count);
+ else if (arg_count == 1)
+ ins = MOV_S | FMT_D | FS(SLJIT_FR0) | FD(TMP_FREG1);
+ arg_count--;
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (arg_count != float_arg_count)
+ ins = MOV_S | FMT_S | FS(float_arg_count) | FD(arg_count);
+ else if (arg_count == 1)
+ ins = MOV_S | FMT_S | FS(SLJIT_FR0) | FD(TMP_FREG1);
+ arg_count--;
+ float_arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count)
+ ins = DADDU | S(word_arg_count) | TA(0) | D(arg_count);
+ else if (arg_count == 1)
+ ins = DADDU | S(SLJIT_R0) | TA(0) | DA(4);
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ if (ins != NOP) {
+ if (prev_ins != NOP)
+ FAIL_IF(push_inst(compiler, prev_ins, MOVABLE_INS));
+ prev_ins = ins;
+ ins = NOP;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ *ins_ptr = prev_ins;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_ins ins = NOP;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+
+ if (type & SLJIT_CALL_RETURN)
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, &ins));
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (ins == NOP && compiler->delay_slot != UNMOVABLE_INS)
+ jump->flags |= IS_MOVABLE;
+
+ if (!(type & SLJIT_CALL_RETURN)) {
+ jump->flags |= IS_JAL;
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ jump->flags |= IS_CALL;
+
+ PTR_FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ } else
+ PTR_FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, ins, UNMOVABLE_INS));
+
+ /* Maximum number of instructions required for generating a constant. */
+ compiler->size += 6;
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins ins = NOP;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ if (ins != NOP)
+ FAIL_IF(push_inst(compiler, ins, MOVABLE_INS));
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+ }
+
+ SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2);
+
+ if (src & SLJIT_IMM)
+ FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw));
+ else if (src != PIC_ADDR_REG)
+ FAIL_IF(push_inst(compiler, DADDU | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+
+ if (type & SLJIT_CALL_RETURN)
+ FAIL_IF(emit_stack_frame_release(compiler, 0, &ins));
+
+ FAIL_IF(call_with_args(compiler, arg_types, &ins));
+
+ /* Register input. */
+ if (!(type & SLJIT_CALL_RETURN))
+ FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, JR | S(PIC_ADDR_REG), UNMOVABLE_INS));
+ return push_inst(compiler, ins, UNMOVABLE_INS);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_common.c b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_common.c
new file mode 100644
index 0000000000..9afe901c38
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeMIPS_common.c
@@ -0,0 +1,3720 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/* Latest MIPS architecture. */
+
+#ifndef __mips_hard_float
+/* Disable automatic detection, covers both -msoft-float and -mno-float */
+#undef SLJIT_IS_FPU_AVAILABLE
+#define SLJIT_IS_FPU_AVAILABLE 0
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return "MIPS32-R6" SLJIT_CPUINFO;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ return "MIPS64-R6" SLJIT_CPUINFO;
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return "MIPS32-R2" SLJIT_CPUINFO;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ return "MIPS64-R2" SLJIT_CPUINFO;
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return "MIPS32-R1" SLJIT_CPUINFO;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ return "MIPS64-R1" SLJIT_CPUINFO;
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+#else /* SLJIT_MIPS_REV < 1 */
+ return "MIPS III" SLJIT_CPUINFO;
+#endif /* SLJIT_MIPS_REV >= 6 */
+}
+
+/* Length of an instruction word
+ Both for mips-32 and mips-64 */
+typedef sljit_u32 sljit_ins;
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
+
+/* For position independent code, t9 must contain the function address. */
+#define PIC_ADDR_REG TMP_REG2
+
+/* Floating point status register. */
+#define FCSR_REG 31
+/* Return address register. */
+#define RETURN_ADDR_REG 31
+
+/* Flags are kept in volatile registers. */
+#define EQUAL_FLAG 3
+#define OTHER_FLAG 1
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+#define TMP_FREG3 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 5] = {
+ 0, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 24, 23, 22, 21, 20, 19, 18, 17, 16, 29, 4, 25, 31
+};
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = {
+ 0, 0, 14, 2, 4, 6, 8, 18, 30, 28, 26, 24, 22, 20, 12, 10, 16
+};
+
+#else
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 4] = {
+ 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 1, 2, 3, 4, 5, 6, 7, 8, 9, 31, 30, 29, 28, 27, 26, 25, 24, 12, 11, 10
+};
+
+#endif
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+#define S(s) ((sljit_ins)reg_map[s] << 21)
+#define T(t) ((sljit_ins)reg_map[t] << 16)
+#define D(d) ((sljit_ins)reg_map[d] << 11)
+#define FT(t) ((sljit_ins)freg_map[t] << 16)
+#define FS(s) ((sljit_ins)freg_map[s] << 11)
+#define FD(d) ((sljit_ins)freg_map[d] << 6)
+/* Absolute registers. */
+#define SA(s) ((sljit_ins)(s) << 21)
+#define TA(t) ((sljit_ins)(t) << 16)
+#define DA(d) ((sljit_ins)(d) << 11)
+#define IMM(imm) ((sljit_ins)(imm) & 0xffff)
+#define SH_IMM(imm) ((sljit_ins)(imm) << 6)
+
+#define DR(dr) (reg_map[dr])
+#define FR(dr) (freg_map[dr])
+#define HI(opcode) ((sljit_ins)(opcode) << 26)
+#define LO(opcode) ((sljit_ins)(opcode))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+/* CMP.cond.fmt */
+/* S = (20 << 21) D = (21 << 21) */
+#define CMP_FMT_S (20 << 21)
+#endif /* SLJIT_MIPS_REV >= 6 */
+/* S = (16 << 21) D = (17 << 21) */
+#define FMT_S (16 << 21)
+#define FMT_D (17 << 21)
+
+#define ABS_S (HI(17) | FMT_S | LO(5))
+#define ADD_S (HI(17) | FMT_S | LO(0))
+#define ADDIU (HI(9))
+#define ADDU (HI(0) | LO(33))
+#define AND (HI(0) | LO(36))
+#define ANDI (HI(12))
+#define B (HI(4))
+#define BAL (HI(1) | (17 << 16))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define BC1EQZ (HI(17) | (9 << 21) | FT(TMP_FREG3))
+#define BC1NEZ (HI(17) | (13 << 21) | FT(TMP_FREG3))
+#else /* SLJIT_MIPS_REV < 6 */
+#define BC1F (HI(17) | (8 << 21))
+#define BC1T (HI(17) | (8 << 21) | (1 << 16))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define BEQ (HI(4))
+#define BGEZ (HI(1) | (1 << 16))
+#define BGTZ (HI(7))
+#define BLEZ (HI(6))
+#define BLTZ (HI(1) | (0 << 16))
+#define BNE (HI(5))
+#define BREAK (HI(0) | LO(13))
+#define CFC1 (HI(17) | (2 << 21))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define C_EQ_S (HI(17) | CMP_FMT_S | LO(2))
+#define C_OLE_S (HI(17) | CMP_FMT_S | LO(6))
+#define C_OLT_S (HI(17) | CMP_FMT_S | LO(4))
+#define C_UEQ_S (HI(17) | CMP_FMT_S | LO(3))
+#define C_ULE_S (HI(17) | CMP_FMT_S | LO(7))
+#define C_ULT_S (HI(17) | CMP_FMT_S | LO(5))
+#define C_UN_S (HI(17) | CMP_FMT_S | LO(1))
+#define C_FD (FD(TMP_FREG3))
+#else /* SLJIT_MIPS_REV < 6 */
+#define C_EQ_S (HI(17) | FMT_S | LO(50))
+#define C_OLE_S (HI(17) | FMT_S | LO(54))
+#define C_OLT_S (HI(17) | FMT_S | LO(52))
+#define C_UEQ_S (HI(17) | FMT_S | LO(51))
+#define C_ULE_S (HI(17) | FMT_S | LO(55))
+#define C_ULT_S (HI(17) | FMT_S | LO(53))
+#define C_UN_S (HI(17) | FMT_S | LO(49))
+#define C_FD (0)
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define CVT_S_S (HI(17) | FMT_S | LO(32))
+#define DADDIU (HI(25))
+#define DADDU (HI(0) | LO(45))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define DDIV (HI(0) | (2 << 6) | LO(30))
+#define DDIVU (HI(0) | (2 << 6) | LO(31))
+#define DMOD (HI(0) | (3 << 6) | LO(30))
+#define DMODU (HI(0) | (3 << 6) | LO(31))
+#define DIV (HI(0) | (2 << 6) | LO(26))
+#define DIVU (HI(0) | (2 << 6) | LO(27))
+#define DMUH (HI(0) | (3 << 6) | LO(28))
+#define DMUHU (HI(0) | (3 << 6) | LO(29))
+#define DMUL (HI(0) | (2 << 6) | LO(28))
+#define DMULU (HI(0) | (2 << 6) | LO(29))
+#else /* SLJIT_MIPS_REV < 6 */
+#define DDIV (HI(0) | LO(30))
+#define DDIVU (HI(0) | LO(31))
+#define DIV (HI(0) | LO(26))
+#define DIVU (HI(0) | LO(27))
+#define DMULT (HI(0) | LO(28))
+#define DMULTU (HI(0) | LO(29))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define DIV_S (HI(17) | FMT_S | LO(3))
+#define DINSU (HI(31) | LO(6))
+#define DROTR (HI(0) | (1 << 21) | LO(58))
+#define DROTR32 (HI(0) | (1 << 21) | LO(62))
+#define DROTRV (HI(0) | (1 << 6) | LO(22))
+#define DSLL (HI(0) | LO(56))
+#define DSLL32 (HI(0) | LO(60))
+#define DSLLV (HI(0) | LO(20))
+#define DSRA (HI(0) | LO(59))
+#define DSRA32 (HI(0) | LO(63))
+#define DSRAV (HI(0) | LO(23))
+#define DSRL (HI(0) | LO(58))
+#define DSRL32 (HI(0) | LO(62))
+#define DSRLV (HI(0) | LO(22))
+#define DSUBU (HI(0) | LO(47))
+#define J (HI(2))
+#define JAL (HI(3))
+#define JALR (HI(0) | LO(9))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define JR (HI(0) | LO(9))
+#else /* SLJIT_MIPS_REV < 6 */
+#define JR (HI(0) | LO(8))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define LD (HI(55))
+#define LDL (HI(26))
+#define LDR (HI(27))
+#define LDC1 (HI(53))
+#define LUI (HI(15))
+#define LW (HI(35))
+#define LWL (HI(34))
+#define LWR (HI(38))
+#define LWC1 (HI(49))
+#define MFC1 (HI(17))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define MOD (HI(0) | (3 << 6) | LO(26))
+#define MODU (HI(0) | (3 << 6) | LO(27))
+#else /* SLJIT_MIPS_REV < 6 */
+#define MFHI (HI(0) | LO(16))
+#define MFLO (HI(0) | LO(18))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define MOV_S (HI(17) | FMT_S | LO(6))
+#define MTC1 (HI(17) | (4 << 21))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define MUH (HI(0) | (3 << 6) | LO(24))
+#define MUHU (HI(0) | (3 << 6) | LO(25))
+#define MUL (HI(0) | (2 << 6) | LO(24))
+#define MULU (HI(0) | (2 << 6) | LO(25))
+#else /* SLJIT_MIPS_REV < 6 */
+#define MULT (HI(0) | LO(24))
+#define MULTU (HI(0) | LO(25))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define MUL_S (HI(17) | FMT_S | LO(2))
+#define NEG_S (HI(17) | FMT_S | LO(7))
+#define NOP (HI(0) | LO(0))
+#define NOR (HI(0) | LO(39))
+#define OR (HI(0) | LO(37))
+#define ORI (HI(13))
+#define ROTR (HI(0) | (1 << 21) | LO(2))
+#define ROTRV (HI(0) | (1 << 6) | LO(6))
+#define SD (HI(63))
+#define SDL (HI(44))
+#define SDR (HI(45))
+#define SDC1 (HI(61))
+#define SLT (HI(0) | LO(42))
+#define SLTI (HI(10))
+#define SLTIU (HI(11))
+#define SLTU (HI(0) | LO(43))
+#define SLL (HI(0) | LO(0))
+#define SLLV (HI(0) | LO(4))
+#define SRL (HI(0) | LO(2))
+#define SRLV (HI(0) | LO(6))
+#define SRA (HI(0) | LO(3))
+#define SRAV (HI(0) | LO(7))
+#define SUB_S (HI(17) | FMT_S | LO(1))
+#define SUBU (HI(0) | LO(35))
+#define SW (HI(43))
+#define SWL (HI(42))
+#define SWR (HI(46))
+#define SWC1 (HI(57))
+#define TRUNC_W_S (HI(17) | FMT_S | LO(13))
+#define XOR (HI(0) | LO(38))
+#define XORI (HI(14))
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+#define CLZ (HI(28) | LO(32))
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#define DCLZ (LO(18))
+#else /* SLJIT_MIPS_REV < 6 */
+#define DCLZ (HI(28) | LO(36))
+#define MOVF (HI(0) | (0 << 16) | LO(1))
+#define MOVN (HI(0) | LO(11))
+#define MOVT (HI(0) | (1 << 16) | LO(1))
+#define MOVZ (HI(0) | LO(10))
+#define MUL (HI(28) | LO(2))
+#endif /* SLJIT_MIPS_REV >= 6 */
+#define PREF (HI(51))
+#define PREFX (HI(19) | LO(15))
+#define SEB (HI(31) | (16 << 6) | LO(32))
+#define SEH (HI(31) | (24 << 6) | LO(32))
+#endif /* SLJIT_MIPS_REV >= 1 */
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define ADDU_W ADDU
+#define ADDIU_W ADDIU
+#define SLL_W SLL
+#define SRA_W SRA
+#define SUBU_W SUBU
+#define STORE_W SW
+#define LOAD_W LW
+#else
+#define ADDU_W DADDU
+#define ADDIU_W DADDIU
+#define SLL_W DSLL
+#define SRA_W DSRA
+#define SUBU_W DSUBU
+#define STORE_W SD
+#define LOAD_W LD
+#endif
+
+#define SIMM_MAX (0x7fff)
+#define SIMM_MIN (-0x8000)
+#define UIMM_MAX (0xffff)
+
+/* dest_reg is the absolute name of the register
+ Useful for reordering instructions in the delay slot. */
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit_s32 delay_slot)
+{
+ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
+ SLJIT_ASSERT(delay_slot == MOVABLE_INS || delay_slot >= UNMOVABLE_INS
+ || (sljit_ins)delay_slot == ((ins >> 11) & 0x1f)
+ || (sljit_ins)delay_slot == ((ins >> 16) & 0x1f));
+ FAIL_IF(!ptr);
+ *ptr = ins;
+ compiler->size++;
+ compiler->delay_slot = delay_slot;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_ins invert_branch(sljit_uw flags)
+{
+ if (flags & IS_BIT26_COND)
+ return (1 << 26);
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ if (flags & IS_BIT23_COND)
+ return (1 << 23);
+#endif /* SLJIT_MIPS_REV >= 6 */
+ return (1 << 16);
+}
+
+static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+ sljit_uw target_addr;
+ sljit_ins *inst;
+ sljit_ins saved_inst;
+
+ inst = (sljit_ins *)jump->addr;
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (jump->flags & (SLJIT_REWRITABLE_JUMP | IS_CALL))
+ goto exit;
+#else
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ goto exit;
+#endif
+
+ if (jump->flags & JUMP_ADDR)
+ target_addr = jump->u.target;
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset;
+ }
+
+ if (jump->flags & IS_COND)
+ inst--;
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (jump->flags & IS_CALL)
+ goto preserve_addr;
+#endif
+
+ /* B instructions. */
+ if (jump->flags & IS_MOVABLE) {
+ diff = ((sljit_sw)target_addr - (sljit_sw)inst - executable_offset) >> 2;
+ if (diff <= SIMM_MAX && diff >= SIMM_MIN) {
+ jump->flags |= PATCH_B;
+
+ if (!(jump->flags & IS_COND)) {
+ inst[0] = inst[-1];
+ inst[-1] = (jump->flags & IS_JAL) ? BAL : B;
+ jump->addr -= sizeof(sljit_ins);
+ return inst;
+ }
+ saved_inst = inst[0];
+ inst[0] = inst[-1];
+ inst[-1] = saved_inst ^ invert_branch(jump->flags);
+ jump->addr -= 2 * sizeof(sljit_ins);
+ return inst;
+ }
+ } else {
+ diff = ((sljit_sw)target_addr - (sljit_sw)(inst + 1) - executable_offset) >> 2;
+ if (diff <= SIMM_MAX && diff >= SIMM_MIN) {
+ jump->flags |= PATCH_B;
+
+ if (!(jump->flags & IS_COND)) {
+ inst[0] = (jump->flags & IS_JAL) ? BAL : B;
+ /* Keep inst[1] */
+ return inst + 1;
+ }
+ inst[0] ^= invert_branch(jump->flags);
+ inst[1] = NOP;
+ jump->addr -= sizeof(sljit_ins);
+ return inst + 1;
+ }
+ }
+
+ if (jump->flags & IS_COND) {
+ if ((jump->flags & IS_MOVABLE) && (target_addr & ~(sljit_uw)0xfffffff) == ((jump->addr + 2 * sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff)) {
+ jump->flags |= PATCH_J;
+ saved_inst = inst[0];
+ inst[0] = inst[-1];
+ inst[-1] = (saved_inst & 0xffff0000) | 3;
+ inst[1] = J;
+ inst[2] = NOP;
+ return inst + 2;
+ }
+ else if ((target_addr & ~(sljit_uw)0xfffffff) == ((jump->addr + 3 * sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff)) {
+ jump->flags |= PATCH_J;
+ inst[0] = (inst[0] & 0xffff0000) | 3;
+ inst[1] = NOP;
+ inst[2] = J;
+ inst[3] = NOP;
+ jump->addr += sizeof(sljit_ins);
+ return inst + 3;
+ }
+ }
+ else {
+ /* J instuctions. */
+ if ((jump->flags & IS_MOVABLE) && (target_addr & ~(sljit_uw)0xfffffff) == (jump->addr & ~(sljit_uw)0xfffffff)) {
+ jump->flags |= PATCH_J;
+ inst[0] = inst[-1];
+ inst[-1] = (jump->flags & IS_JAL) ? JAL : J;
+ jump->addr -= sizeof(sljit_ins);
+ return inst;
+ }
+
+ if ((target_addr & ~(sljit_uw)0xfffffff) == ((jump->addr + sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff)) {
+ jump->flags |= PATCH_J;
+ inst[0] = (jump->flags & IS_JAL) ? JAL : J;
+ /* Keep inst[1] */
+ return inst + 1;
+ }
+ }
+
+ if (jump->flags & IS_COND)
+ inst++;
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+preserve_addr:
+ if (target_addr <= 0x7fffffff) {
+ jump->flags |= PATCH_ABS32;
+ if (jump->flags & IS_COND)
+ inst[-1] -= 4;
+
+ inst[2] = inst[0];
+ inst[3] = inst[1];
+ return inst + 3;
+ }
+ if (target_addr <= 0x7fffffffffffl) {
+ jump->flags |= PATCH_ABS48;
+ if (jump->flags & IS_COND)
+ inst[-1] -= 2;
+
+ inst[4] = inst[0];
+ inst[5] = inst[1];
+ return inst + 5;
+ }
+#endif
+
+exit:
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ inst[2] = inst[0];
+ inst[3] = inst[1];
+ return inst + 3;
+#else
+ inst[6] = inst[0];
+ inst[7] = inst[1];
+ return inst + 7;
+#endif
+}
+
+#ifdef __GNUC__
+static __attribute__ ((noinline)) void sljit_cache_flush(void* code, void* code_ptr)
+{
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+}
+#endif
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+
+static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label)
+{
+ if (max_label < 0x80000000l) {
+ put_label->flags = PATCH_ABS32;
+ return 1;
+ }
+
+ if (max_label < 0x800000000000l) {
+ put_label->flags = PATCH_ABS48;
+ return 3;
+ }
+
+ put_label->flags = 0;
+ return 5;
+}
+
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+static SLJIT_INLINE void load_addr_to_reg(void *dst, sljit_u32 reg)
+{
+ struct sljit_jump *jump;
+ struct sljit_put_label *put_label;
+ sljit_uw flags;
+ sljit_ins *inst;
+ sljit_uw addr;
+
+ if (reg != 0) {
+ jump = (struct sljit_jump*)dst;
+ flags = jump->flags;
+ inst = (sljit_ins*)jump->addr;
+ addr = (flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ } else {
+ put_label = (struct sljit_put_label*)dst;
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ flags = put_label->flags;
+#endif
+ inst = (sljit_ins*)put_label->addr;
+ addr = put_label->label->addr;
+ reg = *inst;
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ inst[0] = LUI | T(reg) | IMM(addr >> 16);
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ if (flags & PATCH_ABS32) {
+ SLJIT_ASSERT(addr < 0x80000000l);
+ inst[0] = LUI | T(reg) | IMM(addr >> 16);
+ }
+ else if (flags & PATCH_ABS48) {
+ SLJIT_ASSERT(addr < 0x800000000000l);
+ inst[0] = LUI | T(reg) | IMM(addr >> 32);
+ inst[1] = ORI | S(reg) | T(reg) | IMM((addr >> 16) & 0xffff);
+ inst[2] = DSLL | T(reg) | D(reg) | SH_IMM(16);
+ inst += 2;
+ }
+ else {
+ inst[0] = LUI | T(reg) | IMM(addr >> 48);
+ inst[1] = ORI | S(reg) | T(reg) | IMM((addr >> 32) & 0xffff);
+ inst[2] = DSLL | T(reg) | D(reg) | SH_IMM(16);
+ inst[3] = ORI | S(reg) | T(reg) | IMM((addr >> 16) & 0xffff);
+ inst[4] = DSLL | T(reg) | D(reg) | SH_IMM(16);
+ inst += 4;
+ }
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ inst[1] = ORI | S(reg) | T(reg) | IMM(addr & 0xffff);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_ins *code;
+ sljit_ins *code_ptr;
+ sljit_ins *buf_ptr;
+ sljit_ins *buf_end;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_uw addr;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_ins*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == word_count) {
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ word_count += 2;
+#else
+ word_count += 6;
+#endif
+ jump->addr = (sljit_uw)(code_ptr - 1);
+ code_ptr = detect_jump_type(jump, code, executable_offset);
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == word_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ code_ptr += 1;
+ word_count += 1;
+#else
+ code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size));
+ word_count += 5;
+#endif
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ word_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)code_ptr;
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ do {
+ addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ buf_ptr = (sljit_ins *)jump->addr;
+
+ if (jump->flags & PATCH_B) {
+ addr = (sljit_uw)((sljit_sw)(addr - (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset) - sizeof(sljit_ins)) >> 2);
+ SLJIT_ASSERT((sljit_sw)addr <= SIMM_MAX && (sljit_sw)addr >= SIMM_MIN);
+ buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | ((sljit_ins)addr & 0xffff);
+ break;
+ }
+ if (jump->flags & PATCH_J) {
+ SLJIT_ASSERT((addr & ~(sljit_uw)0xfffffff)
+ == (((sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset) + sizeof(sljit_ins)) & ~(sljit_uw)0xfffffff));
+ buf_ptr[0] |= (sljit_ins)(addr >> 2) & 0x03ffffff;
+ break;
+ }
+
+ load_addr_to_reg(jump, PIC_ADDR_REG);
+ } while (0);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ load_addr_to_reg(put_label, 0);
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins);
+
+ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+#ifndef __GNUC__
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+#else
+ /* GCC workaround for invalid code generation with -O2. */
+ sljit_cache_flush(code, code_ptr);
+#endif
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+#if defined(__GNUC__) && !defined(SLJIT_IS_FPU_AVAILABLE)
+ sljit_sw fir = 0;
+#endif /* __GNUC__ && !SLJIT_IS_FPU_AVAILABLE */
+
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#elif defined(__GNUC__)
+ __asm__ ("cfc1 %0, $0" : "=r"(fir));
+ return (fir >> 22) & 0x1;
+#else
+#error "FIR check is not implemented for this architecture"
+#endif
+ case SLJIT_HAS_ZERO_REGISTER:
+ return 1;
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_CMOV:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ case SLJIT_HAS_CTZ:
+ return 2;
+#endif /* SLJIT_MIPS_REV >= 1 */
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+ case SLJIT_HAS_ROT:
+ return 1;
+#endif /* SLJIT_MIPS_REV >= 2 */
+ default:
+ return 0;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ return (type >= SLJIT_ORDERED_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL);
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+/* Creates an index in data_transfer_insts array. */
+#define LOAD_DATA 0x01
+#define WORD_DATA 0x00
+#define BYTE_DATA 0x02
+#define HALF_DATA 0x04
+#define INT_DATA 0x06
+#define SIGNED_DATA 0x08
+/* Separates integer and floating point registers */
+#define GPR_REG 0x0f
+#define DOUBLE_DATA 0x10
+#define SINGLE_DATA 0x12
+
+#define MEM_MASK 0x1f
+
+#define ARG_TEST 0x00020
+#define ALT_KEEP_CACHE 0x00040
+#define CUMULATIVE_OP 0x00080
+#define LOGICAL_OP 0x00100
+#define IMM_OP 0x00200
+#define MOVE_OP 0x00400
+#define SRC2_IMM 0x00800
+
+#define UNUSED_DEST 0x01000
+#define REG_DEST 0x02000
+#define REG1_SOURCE 0x04000
+#define REG2_SOURCE 0x08000
+#define SLOW_SRC1 0x10000
+#define SLOW_SRC2 0x20000
+#define SLOW_DEST 0x40000
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw);
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size, sljit_ins *ins_ptr);
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#include "sljitNativeMIPS_32.c"
+#else
+#include "sljitNativeMIPS_64.c"
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_ins base;
+ sljit_s32 i, tmp, offset;
+ sljit_s32 arg_count, word_arg_count, float_arg_count;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((local_size & SSIZE_OF(sw)) != 0)
+ local_size += SSIZE_OF(sw);
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+
+ local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+#else
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ local_size = (local_size + SLJIT_LOCALS_OFFSET + 31) & ~0x1f;
+#endif
+ compiler->local_size = local_size;
+
+ offset = 0;
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (!(options & SLJIT_ENTER_REG_ARG)) {
+ tmp = arg_types >> SLJIT_ARG_SHIFT;
+ arg_count = 0;
+
+ while (tmp) {
+ offset = arg_count;
+ if ((tmp & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F64) {
+ if ((arg_count & 0x1) != 0)
+ arg_count++;
+ arg_count++;
+ }
+
+ arg_count++;
+ tmp >>= SLJIT_ARG_SHIFT;
+ }
+
+ compiler->args_size = (sljit_uw)arg_count << 2;
+ offset = (offset >= 4) ? (offset << 2) : 0;
+ }
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ if (local_size + offset <= -SIMM_MIN) {
+ /* Frequent case. */
+ FAIL_IF(push_inst(compiler, ADDIU_W | S(SLJIT_SP) | T(SLJIT_SP) | IMM(-local_size), DR(SLJIT_SP)));
+ base = S(SLJIT_SP);
+ offset = local_size - SSIZE_OF(sw);
+ } else {
+ FAIL_IF(load_immediate(compiler, OTHER_FLAG, local_size));
+ FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_SP) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, SUBU_W | S(SLJIT_SP) | TA(OTHER_FLAG) | D(SLJIT_SP), DR(SLJIT_SP)));
+ base = S(TMP_REG2);
+ offset = -SSIZE_OF(sw);
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ local_size = 0;
+#endif
+ }
+
+ FAIL_IF(push_inst(compiler, STORE_W | base | TA(RETURN_ADDR_REG) | IMM(offset), UNMOVABLE_INS));
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STORE_W | base | T(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STORE_W | base | T(i) | IMM(offset), MOVABLE_INS));
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ /* This alignment is valid because offset is not used after storing FPU regs. */
+ if ((offset & SSIZE_OF(sw)) != 0)
+ offset -= SSIZE_OF(sw);
+#endif
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, SDC1 | base | FT(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, SDC1 | base | FT(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ return SLJIT_SUCCESS;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ arg_count = 0;
+ word_arg_count = 0;
+ float_arg_count = 0;
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ /* The first maximum two floating point arguments are passed in floating point
+ registers if no integer argument precedes them. The first 16 byte data is
+ passed in four integer registers, the rest is placed onto the stack.
+ The floating point registers are also part of the first 16 byte data, so
+ their corresponding integer registers are not used when they are present. */
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ if ((arg_count & 0x1) != 0)
+ arg_count++;
+
+ if (word_arg_count == 0 && float_arg_count <= 2) {
+ if (float_arg_count == 1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_D | FS(TMP_FREG1) | FD(SLJIT_FR0), MOVABLE_INS));
+ } else if (arg_count < 4) {
+ FAIL_IF(push_inst(compiler, MTC1 | TA(4 + arg_count) | FS(float_arg_count), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, MTC1 | TA(5 + arg_count) | FS(float_arg_count) | (1 << 11), MOVABLE_INS));
+ } else
+ FAIL_IF(push_inst(compiler, LDC1 | base | FT(float_arg_count) | IMM(local_size + (arg_count << 2)), MOVABLE_INS));
+ arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+
+ if (word_arg_count == 0 && float_arg_count <= 2) {
+ if (float_arg_count == 1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_S | FS(TMP_FREG1) | FD(SLJIT_FR0), MOVABLE_INS));
+ } else if (arg_count < 4)
+ FAIL_IF(push_inst(compiler, MTC1 | TA(4 + arg_count) | FS(float_arg_count), MOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, LWC1 | base | FT(float_arg_count) | IMM(local_size + (arg_count << 2)), MOVABLE_INS));
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (word_arg_count != arg_count + 1 || arg_count == 0)
+ tmp = word_arg_count;
+ else
+ break;
+
+ if (arg_count < 4)
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(4 + arg_count) | TA(0) | D(tmp), DR(tmp)));
+ else
+ FAIL_IF(push_inst(compiler, LW | base | T(tmp) | IMM(local_size + (arg_count << 2)), DR(tmp)));
+ break;
+ }
+ arg_count++;
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT(compiler->args_size == (sljit_uw)arg_count << 2);
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ while (arg_types) {
+ arg_count++;
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ if (arg_count != float_arg_count)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_D | FS(arg_count) | FD(float_arg_count), MOVABLE_INS));
+ else if (arg_count == 1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_D | FS(TMP_FREG1) | FD(SLJIT_FR0), MOVABLE_INS));
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+ if (arg_count != float_arg_count)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_S | FS(arg_count) | FD(float_arg_count), MOVABLE_INS));
+ else if (arg_count == 1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT_S | FS(TMP_FREG1) | FD(SLJIT_FR0), MOVABLE_INS));
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (word_arg_count != arg_count || word_arg_count <= 1)
+ tmp = word_arg_count;
+ else
+ break;
+
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(3 + arg_count) | TA(0) | D(tmp), DR(tmp)));
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((local_size & SSIZE_OF(sw)) != 0)
+ local_size += SSIZE_OF(sw);
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+
+ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+#else
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 31) & ~0x1f;
+#endif
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 frame_size, sljit_ins *ins_ptr)
+{
+ sljit_s32 local_size, i, tmp, offset;
+ sljit_s32 load_return_addr = (frame_size == 0);
+ sljit_s32 scratches = compiler->scratches;
+ sljit_s32 saveds = compiler->saveds;
+ sljit_s32 fsaveds = compiler->fsaveds;
+ sljit_s32 fscratches = compiler->fscratches;
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+
+ SLJIT_ASSERT(frame_size == 1 || (frame_size & 0xf) == 0);
+ frame_size &= ~0xf;
+
+ local_size = compiler->local_size;
+
+ tmp = GET_SAVED_REGISTERS_SIZE(scratches, saveds - kept_saveds_count, 1);
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((tmp & SSIZE_OF(sw)) != 0)
+ tmp += SSIZE_OF(sw);
+ tmp += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+#else
+ tmp += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+#endif
+
+ if (local_size <= SIMM_MAX) {
+ if (local_size < frame_size) {
+ FAIL_IF(push_inst(compiler, ADDIU_W | S(SLJIT_SP) | T(SLJIT_SP) | IMM(local_size - frame_size), DR(SLJIT_SP)));
+ local_size = frame_size;
+ }
+ } else {
+ if (tmp < frame_size)
+ tmp = frame_size;
+
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), local_size - tmp));
+ FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_SP) | T(TMP_REG1) | D(SLJIT_SP), DR(SLJIT_SP)));
+ local_size = tmp;
+ }
+
+ SLJIT_ASSERT(local_size >= frame_size);
+
+ offset = local_size - SSIZE_OF(sw);
+ if (load_return_addr)
+ FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | TA(RETURN_ADDR_REG) | IMM(offset), RETURN_ADDR_REG));
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - kept_saveds_count; i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | T(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, LOAD_W | S(SLJIT_SP) | T(i) | IMM(offset), MOVABLE_INS));
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ /* This alignment is valid because offset is not used after storing FPU regs. */
+ if ((offset & SSIZE_OF(sw)) != 0)
+ offset -= SSIZE_OF(sw);
+#endif
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, LDC1 | S(SLJIT_SP) | FT(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, LDC1 | S(SLJIT_SP) | FT(i) | IMM(offset), MOVABLE_INS));
+ }
+
+ if (local_size > frame_size)
+ *ins_ptr = ADDIU_W | S(SLJIT_SP) | T(SLJIT_SP) | IMM(local_size - frame_size);
+ else
+ *ins_ptr = NOP;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ emit_stack_frame_release(compiler, 0, &ins);
+
+ FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ return push_inst(compiler, ins, UNMOVABLE_INS);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(PIC_ADDR_REG), src, srcw));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | D(PIC_ADDR_REG), DR(PIC_ADDR_REG)));
+ src = PIC_ADDR_REG;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1, &ins));
+
+ if (!(src & SLJIT_IMM)) {
+ FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS));
+ return push_inst(compiler, ins, UNMOVABLE_INS);
+ }
+
+ if (ins != NOP)
+ FAIL_IF(push_inst(compiler, ins, MOVABLE_INS));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define ARCH_32_64(a, b) a
+#else
+#define ARCH_32_64(a, b) b
+#endif
+
+static const sljit_ins data_transfer_insts[16 + 4] = {
+/* u w s */ ARCH_32_64(HI(43) /* sw */, HI(63) /* sd */),
+/* u w l */ ARCH_32_64(HI(35) /* lw */, HI(55) /* ld */),
+/* u b s */ HI(40) /* sb */,
+/* u b l */ HI(36) /* lbu */,
+/* u h s */ HI(41) /* sh */,
+/* u h l */ HI(37) /* lhu */,
+/* u i s */ HI(43) /* sw */,
+/* u i l */ ARCH_32_64(HI(35) /* lw */, HI(39) /* lwu */),
+
+/* s w s */ ARCH_32_64(HI(43) /* sw */, HI(63) /* sd */),
+/* s w l */ ARCH_32_64(HI(35) /* lw */, HI(55) /* ld */),
+/* s b s */ HI(40) /* sb */,
+/* s b l */ HI(32) /* lb */,
+/* s h s */ HI(41) /* sh */,
+/* s h l */ HI(33) /* lh */,
+/* s i s */ HI(43) /* sw */,
+/* s i l */ HI(35) /* lw */,
+
+/* d s */ HI(61) /* sdc1 */,
+/* d l */ HI(53) /* ldc1 */,
+/* s s */ HI(57) /* swc1 */,
+/* s l */ HI(49) /* lwc1 */,
+};
+
+#undef ARCH_32_64
+
+/* reg_ar is an absoulute register! */
+
+/* Can perform an operation using at most 1 instruction. */
+static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw)
+{
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) {
+ /* Works for both absoulte and relative addresses. */
+ if (SLJIT_UNLIKELY(flags & ARG_TEST))
+ return 1;
+ FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(arg & REG_MASK)
+ | TA(reg_ar) | IMM(argw), ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) ? reg_ar : MOVABLE_INS));
+ return -1;
+ }
+ return 0;
+}
+
+#define TO_ARGW_HI(argw) (((argw) & ~0xffff) + (((argw) & 0x8000) ? 0x10000 : 0))
+
+/* See getput_arg below.
+ Note: can_cache is called only for binary operators. */
+static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
+{
+ SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM));
+
+ /* Simple operation except for updates. */
+ if (arg & OFFS_REG_MASK) {
+ argw &= 0x3;
+ next_argw &= 0x3;
+ if (argw && argw == next_argw && (arg == next_arg || (arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK)))
+ return 1;
+ return 0;
+ }
+
+ if (arg == next_arg) {
+ if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN)
+ || TO_ARGW_HI(argw) == TO_ARGW_HI(next_argw))
+ return 1;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Emit the necessary instructions. See can_cache above. */
+static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
+{
+ sljit_s32 tmp_ar, base, delay_slot;
+ sljit_sw offset, argw_hi;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ if (!(next_arg & SLJIT_MEM)) {
+ next_arg = 0;
+ next_argw = 0;
+ }
+
+ /* Since tmp can be the same as base or offset registers,
+ * these might be unavailable after modifying tmp. */
+ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) {
+ tmp_ar = reg_ar;
+ delay_slot = reg_ar;
+ }
+ else {
+ tmp_ar = DR(TMP_REG1);
+ delay_slot = MOVABLE_INS;
+ }
+ base = arg & REG_MASK;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ /* Using the cache. */
+ if (argw == compiler->cache_argw) {
+ if (arg == compiler->cache_arg)
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot);
+
+ if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) {
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(TMP_REG3), DR(TMP_REG3)));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), delay_slot);
+ }
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
+ }
+ }
+
+ if (SLJIT_UNLIKELY(argw)) {
+ compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK);
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | D(TMP_REG3) | SH_IMM(argw), DR(TMP_REG3)));
+ }
+
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | D(TMP_REG3), DR(TMP_REG3)));
+ tmp_ar = DR(TMP_REG3);
+ }
+ else
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? OFFS_REG(arg) : TMP_REG3) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
+ }
+
+ if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN)
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(argw - compiler->cache_argw), delay_slot);
+
+ if (compiler->cache_arg == SLJIT_MEM && (argw - compiler->cache_argw) <= SIMM_MAX && (argw - compiler->cache_argw) >= SIMM_MIN) {
+ offset = argw - compiler->cache_argw;
+ } else {
+ compiler->cache_arg = SLJIT_MEM;
+
+ argw_hi = TO_ARGW_HI(argw);
+
+ if (next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN && argw_hi != TO_ARGW_HI(next_argw)) {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw));
+ compiler->cache_argw = argw;
+ offset = 0;
+ } else {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw_hi));
+ compiler->cache_argw = argw_hi;
+ offset = argw & 0xffff;
+ argw = argw_hi;
+ }
+ }
+
+ if (!base)
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(offset), delay_slot);
+
+ if (arg == next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN) {
+ compiler->cache_arg = arg;
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(base) | D(TMP_REG3), DR(TMP_REG3)));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar) | IMM(offset), delay_slot);
+ }
+
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(base) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar) | IMM(offset), delay_slot);
+}
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg_ar, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_s32 tmp_ar, base, delay_slot;
+
+ if (getput_arg_fast(compiler, flags, reg_ar, arg, argw))
+ return compiler->error;
+
+ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA)) {
+ tmp_ar = reg_ar;
+ delay_slot = reg_ar;
+ }
+ else {
+ tmp_ar = DR(TMP_REG1);
+ delay_slot = MOVABLE_INS;
+ }
+ base = arg & REG_MASK;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ if (SLJIT_UNLIKELY(argw)) {
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | DA(tmp_ar) | SH_IMM(argw), tmp_ar));
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(tmp_ar) | T(base) | DA(tmp_ar), tmp_ar));
+ }
+ else
+ FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(OFFS_REG(arg)) | DA(tmp_ar), tmp_ar));
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), delay_slot);
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_ar, TO_ARGW_HI(argw)));
+
+ if (base != 0)
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(tmp_ar) | T(base) | DA(tmp_ar), tmp_ar));
+
+ return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar) | IMM(argw), delay_slot);
+}
+
+static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w)
+{
+ if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
+ return compiler->error;
+ return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
+}
+
+#define EMIT_LOGICAL(op_imm, op_reg) \
+ if (flags & SRC2_IMM) { \
+ if (op & SLJIT_SET_Z) \
+ FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \
+ if (!(flags & UNUSED_DEST)) \
+ FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \
+ } \
+ else { \
+ if (op & SLJIT_SET_Z) \
+ FAIL_IF(push_inst(compiler, op_reg | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \
+ if (!(flags & UNUSED_DEST)) \
+ FAIL_IF(push_inst(compiler, op_reg | S(src1) | T(src2) | D(dst), DR(dst))); \
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+
+#define SELECT_OP(a, b) (b)
+
+#define EMIT_SHIFT(dimm, dimm32, imm, dv, v) \
+ op_imm = (imm); \
+ op_v = (v);
+
+#else /* !SLJIT_CONFIG_MIPS_32 */
+
+#define SELECT_OP(a, b) \
+ (!(op & SLJIT_32) ? a : b)
+
+#define EMIT_SHIFT(dimm, dimm32, imm, dv, v) \
+ op_dimm = (dimm); \
+ op_dimm32 = (dimm32); \
+ op_imm = (imm); \
+ op_dv = (dv); \
+ op_v = (v);
+
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV < 1)
+
+static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw src)
+{
+ sljit_s32 is_clz = (GET_OPCODE(op) == SLJIT_CLZ);
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ sljit_ins max = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_RISCV_64 */
+ sljit_ins max = 32;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ /* The TMP_REG2 is the next value. */
+ if (src != TMP_REG2)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG2) | TA(0) | IMM(is_clz ? 13 : 14), UNMOVABLE_INS));
+ /* The OTHER_FLAG is the counter. Delay slot. */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(max), OTHER_FLAG));
+
+ if (!is_clz) {
+ FAIL_IF(push_inst(compiler, ANDI | S(TMP_REG2) | T(TMP_REG1) | IMM(1), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, BNE | S(TMP_REG1) | TA(0) | IMM(11), UNMOVABLE_INS));
+ } else
+ FAIL_IF(push_inst(compiler, BLTZ | S(TMP_REG2) | TA(0) | IMM(11), UNMOVABLE_INS));
+
+ /* Delay slot. */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | TA(OTHER_FLAG) | IMM(0), OTHER_FLAG));
+
+ /* The TMP_REG1 is the next shift. */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | SA(0) | T(TMP_REG1) | IMM(max), DR(TMP_REG1)));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(TMP_REG2) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRL, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), DR(TMP_REG1)));
+
+ FAIL_IF(push_inst(compiler, (is_clz ? SELECT_OP(DSRLV, SRLV) : SELECT_OP(DSLLV, SLLV)) | S(TMP_REG1) | TA(EQUAL_FLAG) | D(TMP_REG2), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, BNE | S(TMP_REG2) | TA(0) | IMM(-4), UNMOVABLE_INS));
+ /* Delay slot. */
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(TMP_REG1) | T(TMP_REG2) | IMM(-1), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, (is_clz ? SELECT_OP(DSRLV, SRLV) : SELECT_OP(DSLLV, SLLV)) | S(TMP_REG2) | TA(EQUAL_FLAG) | D(TMP_REG2), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG2) | TA(0) | IMM(-7), UNMOVABLE_INS));
+ /* Delay slot. */
+ FAIL_IF(push_inst(compiler, OR | SA(OTHER_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG));
+
+ return push_inst(compiler, SELECT_OP(DADDU, ADDU) | SA(OTHER_FLAG) | TA(0) | D(dst), DR(dst));
+}
+
+#endif /* SLJIT_MIPS_REV < 1 */
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_s32 src1, sljit_sw src2)
+{
+ sljit_s32 is_overflow, is_carry, carry_src_ar, is_handled;
+ sljit_ins op_imm, op_v;
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ sljit_ins ins, op_dimm, op_dimm32, op_dv;
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if (dst != src2)
+ return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src2) | TA(0) | D(dst), DR(dst));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE))
+ return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst));
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst));
+#else /* SLJIT_MIPS_REV < 1 */
+ FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(24), DR(dst)));
+ return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(24), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 1 */
+#else /* !SLJIT_CONFIG_MIPS_32 */
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ if (op & SLJIT_32)
+ return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 1 */
+ FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(24), DR(dst)));
+ return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(24), DR(dst));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE))
+ return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst));
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst));
+#else /* SLJIT_MIPS_REV < 1 */
+ FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(16), DR(dst)));
+ return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(16), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 1 */
+#else /* !SLJIT_CONFIG_MIPS_32 */
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ if (op & SLJIT_32)
+ return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 1 */
+ FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(16), DR(dst)));
+ return push_inst(compiler, DSRA32 | T(dst) | D(dst) | SH_IMM(16), DR(dst));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ case SLJIT_MOV_U32:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM) && !(op & SLJIT_32));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+ if (dst == src2)
+ return push_inst(compiler, DINSU | T(src2) | SA(0) | (31 << 11) | (0 << 11), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 2 */
+ FAIL_IF(push_inst(compiler, DSLL32 | T(src2) | D(dst) | SH_IMM(0), DR(dst)));
+ return push_inst(compiler, DSRL32 | T(dst) | D(dst) | SH_IMM(0), DR(dst));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S32:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM) && !(op & SLJIT_32));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ return push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(0), DR(dst));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ case SLJIT_NOT:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst)));
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ return push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | D(dst), DR(dst));
+#else /* SLJIT_MIPS_REV < 6 */
+ return push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(src2) | T(dst) | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG1), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, AND | S(src2) | T(TMP_REG1) | D(dst), DR(dst)));
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(dst) | D(dst), DR(dst)));
+#else /* SLJIT_MIPS_REV < 6 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DCLZ, CLZ) | S(dst) | T(dst) | D(dst), DR(dst)));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(dst) | T(TMP_REG1) | IMM(SELECT_OP(-64, -32)), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(SELECT_OP(26, 27)), DR(TMP_REG1)));
+ return push_inst(compiler, XOR | S(dst) | T(TMP_REG1) | D(dst), DR(dst));
+#else /* SLJIT_MIPS_REV < 1 */
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ return emit_clz_ctz(compiler, op, dst, src2);
+#endif /* SLJIT_MIPS_REV >= 1 */
+
+ case SLJIT_ADD:
+ /* Overflow computation (both add and sub): overflow = src1_sign ^ src2_sign ^ result_sign ^ carry_flag */
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ carry_src_ar = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_overflow) {
+ if (src2 >= 0)
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else
+ FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst)));
+ }
+ else {
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ if (is_overflow || carry_src_ar != 0) {
+ if (src1 != dst)
+ carry_src_ar = DR(src1);
+ else if (src2 != dst)
+ carry_src_ar = DR(src2);
+ else {
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | DA(OTHER_FLAG), OTHER_FLAG));
+ carry_src_ar = OTHER_FLAG;
+ }
+ }
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst)));
+ }
+
+ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */
+ if (is_overflow || carry_src_ar != 0) {
+ if (flags & SRC2_IMM)
+ FAIL_IF(push_inst(compiler, SLTIU | S(dst) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ else
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(carry_src_ar) | DA(OTHER_FLAG), OTHER_FLAG));
+ }
+
+ if (!is_overflow)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | D(TMP_REG1), DR(TMP_REG1)));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
+ return push_inst(compiler, XOR | S(TMP_REG1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_ADDC:
+ carry_src_ar = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(src2), DR(dst)));
+ } else {
+ if (carry_src_ar != 0) {
+ if (src1 != dst)
+ carry_src_ar = DR(src1);
+ else if (src2 != dst)
+ carry_src_ar = DR(src2);
+ else {
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ carry_src_ar = EQUAL_FLAG;
+ }
+ }
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | T(src2) | D(dst), DR(dst)));
+ }
+
+ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */
+ if (carry_src_ar != 0) {
+ if (flags & SRC2_IMM)
+ FAIL_IF(push_inst(compiler, SLTIU | S(dst) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
+ else
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(carry_src_ar) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ }
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)));
+
+ if (carry_src_ar == 0)
+ return SLJIT_SUCCESS;
+
+ /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG));
+ /* Set carry flag. */
+ return push_inst(compiler, OR | SA(OTHER_FLAG) | TA(EQUAL_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_SUB:
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ is_handled = 0;
+
+ if (flags & SRC2_IMM) {
+ if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ is_handled = 1;
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTI | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+ is_handled = 1;
+ }
+ }
+
+ if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) {
+ is_handled = 1;
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_LESS:
+ case SLJIT_GREATER_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+ break;
+ case SLJIT_GREATER:
+ case SLJIT_LESS_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ break;
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER_EQUAL:
+ FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+ break;
+ case SLJIT_SIG_GREATER:
+ case SLJIT_SIG_LESS_EQUAL:
+ FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ break;
+ }
+ }
+
+ if (is_handled) {
+ if (flags & SRC2_IMM) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst));
+ }
+ else {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst));
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_overflow) {
+ if (src2 >= 0)
+ FAIL_IF(push_inst(compiler, OR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else
+ FAIL_IF(push_inst(compiler, NOR | S(src1) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(OTHER_FLAG) | IMM(src2), OTHER_FLAG));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)));
+ }
+ else {
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(OTHER_FLAG), OTHER_FLAG));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)));
+ }
+
+ if (!is_overflow)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(EQUAL_FLAG) | D(TMP_REG1), DR(TMP_REG1)));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRL32, SRL) | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(31), DR(TMP_REG1)));
+ return push_inst(compiler, XOR | S(TMP_REG1) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_SUBC:
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DADDIU, ADDIU) | S(src1) | T(dst) | IMM(-src2), DR(dst)));
+ }
+ else {
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(src1) | T(src2) | D(dst), DR(dst)));
+ }
+
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(OTHER_FLAG) | D(TMP_REG1), DR(TMP_REG1)));
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst)));
+
+ if (!is_carry)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, OR | SA(EQUAL_FLAG) | T(TMP_REG1) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_MUL:
+ SLJIT_ASSERT(!(flags & SRC2_IMM));
+
+ if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW) {
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ return push_inst(compiler, SELECT_OP(DMUL, MUL) | S(src1) | T(src2) | D(dst), DR(dst));
+#elif (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst));
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ if (op & SLJIT_32)
+ return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst));
+ FAIL_IF(push_inst(compiler, DMULT | S(src1) | T(src2), MOVABLE_INS));
+ return push_inst(compiler, MFLO | D(dst), DR(dst));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+#else /* SLJIT_MIPS_REV < 1 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS));
+ return push_inst(compiler, MFLO | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ }
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ FAIL_IF(push_inst(compiler, SELECT_OP(DMUL, MUL) | S(src1) | T(src2) | D(dst), DR(dst)));
+ FAIL_IF(push_inst(compiler, SELECT_OP(DMUH, MUH) | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+#else /* SLJIT_MIPS_REV < 6 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DMULT, MULT) | S(src1) | T(src2), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, MFHI | DA(EQUAL_FLAG), EQUAL_FLAG));
+ FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst)));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSRA32, SRA) | T(dst) | DA(OTHER_FLAG) | SH_IMM(31), OTHER_FLAG));
+ return push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(EQUAL_FLAG) | TA(OTHER_FLAG) | DA(OTHER_FLAG), OTHER_FLAG);
+
+ case SLJIT_AND:
+ EMIT_LOGICAL(ANDI, AND);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_OR:
+ EMIT_LOGICAL(ORI, OR);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_XOR:
+ EMIT_LOGICAL(XORI, XOR);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ EMIT_SHIFT(DSLL, DSLL32, SLL, DSLLV, SLLV);
+ break;
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ EMIT_SHIFT(DSRL, DSRL32, SRL, DSRLV, SRLV);
+ break;
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ EMIT_SHIFT(DSRA, DSRA32, SRA, DSRAV, SRAV);
+ break;
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+ case SLJIT_ROTL:
+ if ((flags & SRC2_IMM) || src2 == 0) {
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ src2 = -src2 & 0x1f;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ src2 = -src2 & ((op & SLJIT_32) ? 0x1f : 0x3f);
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ } else {
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG2), DR(TMP_REG2)));
+ src2 = TMP_REG2;
+ }
+ /* fallthrough */
+
+ case SLJIT_ROTR:
+ EMIT_SHIFT(DROTR, DROTR32, ROTR, DROTRV, ROTRV);
+ break;
+#else /* SLJIT_MIPS_REV < 1 */
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & SRC2_IMM) {
+ SLJIT_ASSERT(src2 != 0);
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (!(op & SLJIT_32)) {
+ if (GET_OPCODE(op) == SLJIT_ROTL)
+ op_imm = ((src2 < 32) ? DSLL : DSLL32);
+ else
+ op_imm = ((src2 < 32) ? DSRL : DSRL32);
+
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(OTHER_FLAG) | (((sljit_ins)src2 & 0x1f) << 6), OTHER_FLAG));
+
+ src2 = 64 - src2;
+ if (GET_OPCODE(op) == SLJIT_ROTL)
+ op_imm = ((src2 < 32) ? DSRL : DSRL32);
+ else
+ op_imm = ((src2 < 32) ? DSLL : DSLL32);
+
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | (((sljit_ins)src2 & 0x1f) << 6), DR(dst)));
+ return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst));
+ }
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SLL : SRL;
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(OTHER_FLAG) | ((sljit_ins)src2 << 6), OTHER_FLAG));
+
+ src2 = 32 - src2;
+ op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SRL : SLL;
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | (((sljit_ins)src2 & 0x1f) << 6), DR(dst)));
+ return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst));
+ }
+
+ if (src2 == 0) {
+ if (dst != src1)
+ return push_inst(compiler, SELECT_OP(DADDU, ADDU) | S(src1) | TA(0) | D(dst), DR(dst));
+ return SLJIT_SUCCESS;
+ }
+
+ FAIL_IF(push_inst(compiler, SELECT_OP(DSUBU, SUBU) | SA(0) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (!(op & SLJIT_32)) {
+ op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? DSLLV : DSRLV;
+ FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? DSRLV : DSLLV;
+ FAIL_IF(push_inst(compiler, op_v | SA(EQUAL_FLAG) | T(src1) | D(dst), DR(dst)));
+ return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst));
+ }
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? SLLV : SRLV;
+ FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(OTHER_FLAG), OTHER_FLAG));
+ op_v = (GET_OPCODE(op) == SLJIT_ROTL) ? SRLV : SLLV;
+ FAIL_IF(push_inst(compiler, op_v | SA(EQUAL_FLAG) | T(src1) | D(dst), DR(dst)));
+ return push_inst(compiler, OR | S(dst) | TA(OTHER_FLAG) | D(dst), DR(dst));
+#endif /* SLJIT_MIPS_REV >= 2 */
+
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if ((flags & SRC2_IMM) || src2 == 0) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, op_imm | T(src1) | D(dst) | SH_IMM(src2), DR(dst));
+ }
+
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, op_v | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, op_v | S(src2) | T(src1) | D(dst), DR(dst));
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ if ((flags & SRC2_IMM) || src2 == 0) {
+ if (src2 >= 32) {
+ SLJIT_ASSERT(!(op & SLJIT_32));
+ ins = op_dimm32;
+ src2 -= 32;
+ }
+ else
+ ins = (op & SLJIT_32) ? op_imm : op_dimm;
+
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ins | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ins | T(src1) | D(dst) | SH_IMM(src2), DR(dst));
+ }
+
+ ins = (op & SLJIT_32) ? op_v : op_dv;
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ins | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ins | S(src2) | T(src1) | D(dst), DR(dst));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+}
+
+#define CHECK_IMM(flags, srcw) \
+ ((!((flags) & LOGICAL_OP) && ((srcw) <= SIMM_MAX && (srcw) >= SIMM_MIN)) \
+ || (((flags) & LOGICAL_OP) && !((srcw) & ~UIMM_MAX)))
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* arg1 goes to TMP_REG1 or src reg
+ arg2 goes to TMP_REG2, imm or src reg
+ TMP_REG3 can be used for caching
+ result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */
+ sljit_s32 dst_r = TMP_REG2;
+ sljit_s32 src1_r;
+ sljit_sw src2_r = 0;
+ sljit_s32 sugg_src2_r = TMP_REG2;
+
+ if (!(flags & ALT_KEEP_CACHE)) {
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+ }
+
+ if (dst == TMP_REG2) {
+ SLJIT_ASSERT(HAS_FLAGS(op));
+ flags |= UNUSED_DEST;
+ }
+ else if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ flags |= REG_DEST;
+ if (flags & MOVE_OP)
+ sugg_src2_r = dst_r;
+ }
+ else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, DR(TMP_REG1), dst, dstw))
+ flags |= SLOW_DEST;
+
+ if (flags & IMM_OP) {
+ if ((src2 & SLJIT_IMM) && src2w != 0 && CHECK_IMM(flags, src2w)) {
+ flags |= SRC2_IMM;
+ src2_r = src2w;
+ } else if ((flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w != 0 && CHECK_IMM(flags, src1w)) {
+ flags |= SRC2_IMM;
+ src2_r = src1w;
+
+ /* And swap arguments. */
+ src1 = src2;
+ src1w = src2w;
+ src2 = SLJIT_IMM;
+ /* src2w = src2_r unneeded. */
+ }
+ }
+
+ /* Source 1. */
+ if (FAST_IS_REG(src1)) {
+ src1_r = src1;
+ flags |= REG1_SOURCE;
+ }
+ else if (src1 & SLJIT_IMM) {
+ if (src1w) {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w));
+ src1_r = TMP_REG1;
+ }
+ else
+ src1_r = 0;
+ }
+ else {
+ if (getput_arg_fast(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w))
+ FAIL_IF(compiler->error);
+ else
+ flags |= SLOW_SRC1;
+ src1_r = TMP_REG1;
+ }
+
+ /* Source 2. */
+ if (FAST_IS_REG(src2)) {
+ src2_r = src2;
+ flags |= REG2_SOURCE;
+ if ((flags & (REG_DEST | MOVE_OP)) == MOVE_OP)
+ dst_r = (sljit_s32)src2_r;
+ }
+ else if (src2 & SLJIT_IMM) {
+ if (!(flags & SRC2_IMM)) {
+ if (src2w) {
+ FAIL_IF(load_immediate(compiler, DR(sugg_src2_r), src2w));
+ src2_r = sugg_src2_r;
+ }
+ else {
+ src2_r = 0;
+ if (flags & MOVE_OP) {
+ if (dst & SLJIT_MEM)
+ dst_r = 0;
+ else
+ op = SLJIT_MOV;
+ }
+ }
+ }
+ }
+ else {
+ if (getput_arg_fast(compiler, flags | LOAD_DATA, DR(sugg_src2_r), src2, src2w))
+ FAIL_IF(compiler->error);
+ else
+ flags |= SLOW_SRC2;
+ src2_r = sugg_src2_r;
+ }
+
+ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
+ SLJIT_ASSERT(src2_r == TMP_REG2);
+ if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG2), src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, dst, dstw));
+ }
+ else {
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG2), src2, src2w, dst, dstw));
+ }
+ }
+ else if (flags & SLOW_SRC1)
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, dst, dstw));
+ else if (flags & SLOW_SRC2)
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(sugg_src2_r), src2, src2w, dst, dstw));
+
+ FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));
+
+ if (dst & SLJIT_MEM) {
+ if (!(flags & SLOW_DEST)) {
+ getput_arg_fast(compiler, flags, DR(dst_r), dst, dstw);
+ return compiler->error;
+ }
+ return getput_arg(compiler, flags, DR(dst_r), dst, dstw, 0, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#undef CHECK_IMM
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ sljit_s32 int_op = op & SLJIT_32;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ return push_inst(compiler, BREAK, UNMOVABLE_INS);
+ case SLJIT_NOP:
+ return push_inst(compiler, NOP, UNMOVABLE_INS);
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? DMULU : DMUL) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? DMUHU : DMUH) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? MULU : MUL) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? MUHU : MUH) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+#endif /* SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | TA(0) | D(SLJIT_R0), DR(SLJIT_R0)));
+ return push_inst(compiler, ADDU_W | S(TMP_REG1) | TA(0) | D(SLJIT_R1), DR(SLJIT_R1));
+#else /* SLJIT_MIPS_REV < 6 */
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? DMULTU : DMULT) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, (op == SLJIT_LMUL_UW ? MULTU : MULT) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+#endif /* SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, MFLO | D(SLJIT_R0), DR(SLJIT_R0)));
+ return push_inst(compiler, MFHI | D(SLJIT_R1), DR(SLJIT_R1));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+ SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (int_op) {
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DIVU : DIV) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? MODU : MOD) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+ }
+ else {
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DDIVU : DDIV) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DMODU : DMOD) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+ }
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DIVU : DIV) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG3), DR(TMP_REG3)));
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? MODU : MOD) | S(SLJIT_R0) | T(SLJIT_R1) | D(TMP_REG1), DR(TMP_REG1)));
+#endif /* SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | TA(0) | D(SLJIT_R0), DR(SLJIT_R0)));
+ return (op >= SLJIT_DIV_UW) ? SLJIT_SUCCESS : push_inst(compiler, ADDU_W | S(TMP_REG1) | TA(0) | D(SLJIT_R1), DR(SLJIT_R1));
+#else /* SLJIT_MIPS_REV < 6 */
+#if !(defined SLJIT_MIPS_REV)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif /* !SLJIT_MIPS_REV */
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (int_op)
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DIVU : DIV) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DDIVU : DDIV) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, ((op | 0x2) == SLJIT_DIV_UW ? DIVU : DIV) | S(SLJIT_R0) | T(SLJIT_R1), MOVABLE_INS));
+#endif /* SLJIT_CONFIG_MIPS_64 */
+ FAIL_IF(push_inst(compiler, MFLO | D(SLJIT_R0), DR(SLJIT_R0)));
+ return (op >= SLJIT_DIV_UW) ? SLJIT_SUCCESS : push_inst(compiler, MFHI | D(SLJIT_R1), DR(SLJIT_R1));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (!(src & OFFS_REG_MASK)) {
+ if (srcw <= SIMM_MAX && srcw >= SIMM_MIN)
+ return push_inst(compiler, PREF | S(src & REG_MASK) | IMM(srcw), MOVABLE_INS);
+
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw));
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(TMP_REG1), MOVABLE_INS);
+ }
+
+ srcw &= 0x3;
+
+ if (SLJIT_UNLIKELY(srcw != 0)) {
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(src)) | D(TMP_REG1) | SH_IMM(srcw), DR(TMP_REG1)));
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(TMP_REG1), MOVABLE_INS);
+ }
+
+ return push_inst(compiler, PREFX | S(src & REG_MASK) | T(OFFS_REG(src)), MOVABLE_INS);
+}
+#endif /* SLJIT_MIPS_REV >= 1 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (op & SLJIT_32)
+ flags = INT_DATA | SIGNED_DATA;
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ case SLJIT_MOV_P:
+ return emit_op(compiler, SLJIT_MOV, WORD_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, srcw);
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ case SLJIT_MOV_U32:
+ return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u32)srcw : srcw);
+
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s32)srcw : srcw);
+#endif
+
+ case SLJIT_MOV_U8:
+ return emit_op(compiler, op, BYTE_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
+
+ case SLJIT_MOV_S8:
+ return emit_op(compiler, op, BYTE_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
+
+ case SLJIT_MOV_U16:
+ return emit_op(compiler, op, HALF_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
+
+ case SLJIT_MOV_S16:
+ return emit_op(compiler, op, HALF_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
+
+ case SLJIT_NOT:
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (op & SLJIT_32) {
+ flags |= INT_DATA | SIGNED_DATA;
+ if (src1 & SLJIT_IMM)
+ src1w = (sljit_s32)src1w;
+ if (src2 & SLJIT_IMM)
+ src2w = (sljit_s32)src2w;
+ }
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SUB:
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_MUL:
+ compiler->status_flags_state = 0;
+ return emit_op(compiler, op, flags | CUMULATIVE_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ return emit_op(compiler, op, flags | CUMULATIVE_OP | LOGICAL_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (src2 & SLJIT_IMM)
+ src2w &= 0x1f;
+#else
+ if (src2 & SLJIT_IMM) {
+ if (op & SLJIT_32)
+ src2w &= 0x1f;
+ else
+ src2w &= 0x3f;
+ }
+#endif
+ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w);
+}
+
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+#define SELECT_OP3(op, src2w, D, D32, W) (((op & SLJIT_32) ? (W) : ((src2w) < 32) ? (D) : (D32)) | (((sljit_ins)src2w & 0x1f) << 6))
+#define SELECT_OP2(op, D, W) ((op & SLJIT_32) ? (W) : (D))
+#else /* !SLJIT_CONFIG_MIPS_64 */
+#define SELECT_OP3(op, src2w, D, D32, W) ((W) | ((sljit_ins)(src2w) << 6))
+#define SELECT_OP2(op, D, W) (W)
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_left;
+ sljit_ins ins1, ins2, ins3;
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+ sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_MIPS_64 */
+ sljit_s32 inp_flags = WORD_DATA | LOAD_DATA;
+ sljit_sw bit_length = 32;
+#endif /* SLJIT_CONFIG_MIPS_64 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= bit_length - 1;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, DR(TMP_REG2), src2, src2w));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, DR(TMP_REG1), src1, src1w));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (is_left) {
+ ins1 = SELECT_OP3(op, src2w, DSLL, DSLL32, SLL);
+ src2w = bit_length - src2w;
+ ins2 = SELECT_OP3(op, src2w, DSRL, DSRL32, SRL);
+ } else {
+ ins1 = SELECT_OP3(op, src2w, DSRL, DSRL32, SRL);
+ src2w = bit_length - src2w;
+ ins2 = SELECT_OP3(op, src2w, DSLL, DSLL32, SLL);
+ }
+
+ FAIL_IF(push_inst(compiler, ins1 | T(src_dst) | D(src_dst), DR(src_dst)));
+ FAIL_IF(push_inst(compiler, ins2 | T(src1) | D(TMP_REG1), DR(TMP_REG1)));
+ return push_inst(compiler, OR | S(src_dst) | T(TMP_REG1) | D(src_dst), DR(src_dst));
+ }
+
+ if (is_left) {
+ ins1 = SELECT_OP2(op, DSRL, SRL);
+ ins2 = SELECT_OP2(op, DSLLV, SLLV);
+ ins3 = SELECT_OP2(op, DSRLV, SRLV);
+ } else {
+ ins1 = SELECT_OP2(op, DSLL, SLL);
+ ins2 = SELECT_OP2(op, DSRLV, SRLV);
+ ins3 = SELECT_OP2(op, DSLLV, SLLV);
+ }
+
+ FAIL_IF(push_inst(compiler, ins2 | S(src2) | T(src_dst) | D(src_dst), DR(src_dst)));
+
+ if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) {
+ FAIL_IF(push_inst(compiler, ins1 | T(src1) | D(TMP_REG1) | (1 << 6), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XORI | S(src2) | T(TMP_REG2) | ((sljit_ins)bit_length - 1), DR(TMP_REG2)));
+ src1 = TMP_REG1;
+ } else
+ FAIL_IF(push_inst(compiler, SELECT_OP2(op, DSUBU, SUBU) | SA(0) | T(src2) | D(TMP_REG2), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, ins3 | S(TMP_REG2) | T(src1) | D(TMP_REG1), DR(TMP_REG1)));
+ return push_inst(compiler, OR | S(src_dst) | T(TMP_REG1) | D(src_dst), DR(src_dst));
+}
+
+#undef SELECT_OP3
+#undef SELECT_OP2
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | DA(RETURN_ADDR_REG), RETURN_ADDR_REG));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw));
+
+ FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ return push_inst(compiler, NOP, UNMOVABLE_INS);
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1)
+ return emit_prefetch(compiler, src, srcw);
+#else /* SLJIT_MIPS_REV < 1 */
+ return SLJIT_SUCCESS;
+#endif /* SLJIT_MIPS_REV >= 1 */
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return FR(reg);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_ins*)instruction, UNMOVABLE_INS);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_32) >> 7))
+#define FMT(op) ((((sljit_ins)op & SLJIT_32) ^ SLJIT_32) << (21 - 8))
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+# define flags (sljit_u32)0
+#else
+ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)) << 21;
+#endif
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw));
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst(compiler, (TRUNC_W_S ^ (flags >> 19)) | FMT(op) | FS(src) | FD(TMP_FREG1), MOVABLE_INS));
+
+ if (FAST_IS_REG(dst)) {
+ FAIL_IF(push_inst(compiler, MFC1 | flags | T(dst) | FS(TMP_FREG1), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ return SLJIT_SUCCESS;
+ }
+
+ /* Store the integer value from a VFP register. */
+ return emit_op_mem2(compiler, flags ? DOUBLE_DATA : SINGLE_DATA, FR(TMP_FREG1), dst, dstw, 0, 0);
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+# undef flags
+#endif
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+# define flags (sljit_u32)0
+#else
+ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)) << 21;
+#endif
+
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (FAST_IS_REG(src)) {
+ FAIL_IF(push_inst(compiler, MTC1 | flags | T(src) | FS(TMP_FREG1), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ } else if (src & SLJIT_MEM) {
+ /* Load the integer value into a VFP register. */
+ FAIL_IF(emit_op_mem2(compiler, (flags ? DOUBLE_DATA : SINGLE_DATA) | LOAD_DATA, FR(TMP_FREG1), src, srcw, dst, dstw));
+ }
+ else {
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+#endif
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw));
+ FAIL_IF(push_inst(compiler, MTC1 | flags | T(TMP_REG1) | FS(TMP_FREG1), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ }
+
+ FAIL_IF(push_inst(compiler, CVT_S_S | flags | (4 << 21) | ((((sljit_ins)op & SLJIT_32) ^ SLJIT_32) >> 8) | FS(TMP_FREG1) | FD(dst_r), MOVABLE_INS));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem2(compiler, FLOAT_DATA(op), FR(TMP_FREG1), dst, dstw, 0, 0);
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+# undef flags
+#endif
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins inst;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, src2, src2w));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, 0, 0));
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ inst = C_EQ_S;
+ break;
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ inst = C_UEQ_S;
+ break;
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ inst = C_OLT_S;
+ break;
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ inst = C_ULT_S;
+ break;
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ inst = C_ULE_S;
+ break;
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ inst = C_OLE_S;
+ break;
+ default:
+ SLJIT_ASSERT(GET_FLAG_TYPE(op) == SLJIT_UNORDERED || GET_FLAG_TYPE(op) == SLJIT_ORDERED);
+ inst = C_UN_S;
+ break;
+ }
+ return push_inst(compiler, inst | FMT(op) | FT(src2) | FS(src1) | C_FD, UNMOVABLE_INS);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100) && !(DOUBLE_DATA & 0x2), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(dst_r), src, srcw, dst, dstw));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, MOV_S | FMT(op) | FS(src) | FD(dst_r), MOVABLE_INS));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, NEG_S | FMT(op) | FS(src) | FD(dst_r), MOVABLE_INS));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, ABS_S | FMT(op) | FS(src) | FD(dst_r), MOVABLE_INS));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ /* The SLJIT_32 bit is inverted because sljit_f32 needs to be loaded from the memory. */
+ FAIL_IF(push_inst(compiler, CVT_S_S | (sljit_ins)((op & SLJIT_32) ? 1 : (1 << 21)) | FS(src) | FD(dst_r), MOVABLE_INS));
+ op ^= SLJIT_32;
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem2(compiler, FLOAT_DATA(op), FR(dst_r), dst, dstw, 0, 0);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r, flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
+
+ if (src1 & SLJIT_MEM) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w)) {
+ FAIL_IF(compiler->error);
+ src1 = TMP_FREG1;
+ } else
+ flags |= SLOW_SRC1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w)) {
+ FAIL_IF(compiler->error);
+ src2 = TMP_FREG2;
+ } else
+ flags |= SLOW_SRC2;
+ }
+
+ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
+ if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, dst, dstw));
+ }
+ else {
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, dst, dstw));
+ }
+ }
+ else if (flags & SLOW_SRC1)
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG1), src1, src1w, dst, dstw));
+ else if (flags & SLOW_SRC2)
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, FR(TMP_FREG2), src2, src2w, dst, dstw));
+
+ if (flags & SLOW_SRC1)
+ src1 = TMP_FREG1;
+ if (flags & SLOW_SRC2)
+ src2 = TMP_FREG2;
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, ADD_S | FMT(op) | FT(src2) | FS(src1) | FD(dst_r), MOVABLE_INS));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, SUB_S | FMT(op) | FT(src2) | FS(src1) | FD(dst_r), MOVABLE_INS));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, MUL_S | FMT(op) | FT(src2) | FS(src1) | FD(dst_r), MOVABLE_INS));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, DIV_S | FMT(op) | FT(src2) | FS(src1) | FD(dst_r), MOVABLE_INS));
+ break;
+ }
+
+ if (dst_r == TMP_FREG2)
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), FR(TMP_FREG2), dst, dstw, 0, 0));
+
+ return SLJIT_SUCCESS;
+}
+
+#undef FLOAT_DATA
+#undef FMT
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, ADDU_W | SA(RETURN_ADDR_REG) | TA(0) | D(dst), UNMOVABLE_INS);
+
+ /* Memory. */
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, RETURN_ADDR_REG, dst, dstw));
+ compiler->delay_slot = UNMOVABLE_INS;
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ compiler->delay_slot = UNMOVABLE_INS;
+ return label;
+}
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define BRANCH_LENGTH 4
+#else
+#define BRANCH_LENGTH 8
+#endif
+
+#define BR_Z(src) \
+ inst = BEQ | SA(src) | TA(0) | BRANCH_LENGTH; \
+ flags = IS_BIT26_COND; \
+ delay_check = src;
+
+#define BR_NZ(src) \
+ inst = BNE | SA(src) | TA(0) | BRANCH_LENGTH; \
+ flags = IS_BIT26_COND; \
+ delay_check = src;
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+
+#define BR_T() \
+ inst = BC1NEZ; \
+ flags = IS_BIT23_COND; \
+ delay_check = FCSR_FCC;
+#define BR_F() \
+ inst = BC1EQZ; \
+ flags = IS_BIT23_COND; \
+ delay_check = FCSR_FCC;
+
+#else /* SLJIT_MIPS_REV < 6 */
+
+#define BR_T() \
+ inst = BC1T | BRANCH_LENGTH; \
+ flags = IS_BIT16_COND; \
+ delay_check = FCSR_FCC;
+#define BR_F() \
+ inst = BC1F | BRANCH_LENGTH; \
+ flags = IS_BIT16_COND; \
+ delay_check = FCSR_FCC;
+
+#endif /* SLJIT_MIPS_REV >= 6 */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+ sljit_ins inst;
+ sljit_u32 flags = 0;
+ sljit_s32 delay_check = UNMOVABLE_INS;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ BR_NZ(EQUAL_FLAG);
+ break;
+ case SLJIT_NOT_EQUAL:
+ BR_Z(EQUAL_FLAG);
+ break;
+ case SLJIT_LESS:
+ case SLJIT_GREATER:
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER:
+ case SLJIT_OVERFLOW:
+ case SLJIT_CARRY:
+ BR_Z(OTHER_FLAG);
+ break;
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_NOT_OVERFLOW:
+ case SLJIT_NOT_CARRY:
+ BR_NZ(OTHER_FLAG);
+ break;
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED:
+ BR_T();
+ break;
+ case SLJIT_F_EQUAL:
+ case SLJIT_F_LESS:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_UNORDERED:
+ BR_F();
+ break;
+ default:
+ /* Not conditional branch. */
+ inst = 0;
+ break;
+ }
+
+ jump->flags |= flags;
+ if (compiler->delay_slot == MOVABLE_INS || (compiler->delay_slot != UNMOVABLE_INS && compiler->delay_slot != delay_check))
+ jump->flags |= IS_MOVABLE;
+
+ if (inst)
+ PTR_FAIL_IF(push_inst(compiler, inst, UNMOVABLE_INS));
+
+ if (type <= SLJIT_JUMP)
+ PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS));
+ else {
+ jump->flags |= IS_JAL;
+ PTR_FAIL_IF(push_inst(compiler, JALR | S(TMP_REG2) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+ }
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ compiler->size += 2;
+#else
+ compiler->size += 6;
+#endif
+ return jump;
+}
+
+#define RESOLVE_IMM1() \
+ if (src1 & SLJIT_IMM) { \
+ if (src1w) { \
+ PTR_FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w)); \
+ src1 = TMP_REG1; \
+ } \
+ else \
+ src1 = 0; \
+ }
+
+#define RESOLVE_IMM2() \
+ if (src2 & SLJIT_IMM) { \
+ if (src2w) { \
+ PTR_FAIL_IF(load_immediate(compiler, DR(TMP_REG2), src2w)); \
+ src2 = TMP_REG2; \
+ } \
+ else \
+ src2 = 0; \
+ }
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ struct sljit_jump *jump;
+ sljit_s32 flags;
+ sljit_ins inst;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ flags = WORD_DATA | LOAD_DATA;
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ flags = ((type & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ if (src1 & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem2(compiler, flags, DR(TMP_REG1), src1, src1w, src2, src2w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem2(compiler, flags, DR(TMP_REG2), src2, src2w, 0, 0));
+ src2 = TMP_REG2;
+ }
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ if (type <= SLJIT_NOT_EQUAL) {
+ RESOLVE_IMM1();
+ RESOLVE_IMM2();
+ jump->flags |= IS_BIT26_COND;
+ if (compiler->delay_slot == MOVABLE_INS || (compiler->delay_slot != UNMOVABLE_INS && compiler->delay_slot != DR(src1) && compiler->delay_slot != DR(src2)))
+ jump->flags |= IS_MOVABLE;
+ PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_EQUAL ? BNE : BEQ) | S(src1) | T(src2) | BRANCH_LENGTH, UNMOVABLE_INS));
+ }
+ else if (type >= SLJIT_SIG_LESS && (((src1 & SLJIT_IMM) && (src1w == 0)) || ((src2 & SLJIT_IMM) && (src2w == 0)))) {
+ inst = NOP;
+ if ((src1 & SLJIT_IMM) && (src1w == 0)) {
+ RESOLVE_IMM2();
+ switch (type) {
+ case SLJIT_SIG_LESS:
+ inst = BLEZ;
+ jump->flags |= IS_BIT26_COND;
+ break;
+ case SLJIT_SIG_GREATER_EQUAL:
+ inst = BGTZ;
+ jump->flags |= IS_BIT26_COND;
+ break;
+ case SLJIT_SIG_GREATER:
+ inst = BGEZ;
+ jump->flags |= IS_BIT16_COND;
+ break;
+ case SLJIT_SIG_LESS_EQUAL:
+ inst = BLTZ;
+ jump->flags |= IS_BIT16_COND;
+ break;
+ }
+ src1 = src2;
+ }
+ else {
+ RESOLVE_IMM1();
+ switch (type) {
+ case SLJIT_SIG_LESS:
+ inst = BGEZ;
+ jump->flags |= IS_BIT16_COND;
+ break;
+ case SLJIT_SIG_GREATER_EQUAL:
+ inst = BLTZ;
+ jump->flags |= IS_BIT16_COND;
+ break;
+ case SLJIT_SIG_GREATER:
+ inst = BLEZ;
+ jump->flags |= IS_BIT26_COND;
+ break;
+ case SLJIT_SIG_LESS_EQUAL:
+ inst = BGTZ;
+ jump->flags |= IS_BIT26_COND;
+ break;
+ }
+ }
+ PTR_FAIL_IF(push_inst(compiler, inst | S(src1) | BRANCH_LENGTH, UNMOVABLE_INS));
+ }
+ else {
+ if (type == SLJIT_LESS || type == SLJIT_GREATER_EQUAL || type == SLJIT_SIG_LESS || type == SLJIT_SIG_GREATER_EQUAL) {
+ RESOLVE_IMM1();
+ if ((src2 & SLJIT_IMM) && src2w <= SIMM_MAX && src2w >= SIMM_MIN)
+ PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_LESS_EQUAL ? SLTIU : SLTI) | S(src1) | T(TMP_REG1) | IMM(src2w), DR(TMP_REG1)));
+ else {
+ RESOLVE_IMM2();
+ PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_LESS_EQUAL ? SLTU : SLT) | S(src1) | T(src2) | D(TMP_REG1), DR(TMP_REG1)));
+ }
+ type = (type == SLJIT_LESS || type == SLJIT_SIG_LESS) ? SLJIT_NOT_EQUAL : SLJIT_EQUAL;
+ }
+ else {
+ RESOLVE_IMM2();
+ if ((src1 & SLJIT_IMM) && src1w <= SIMM_MAX && src1w >= SIMM_MIN)
+ PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_LESS_EQUAL ? SLTIU : SLTI) | S(src2) | T(TMP_REG1) | IMM(src1w), DR(TMP_REG1)));
+ else {
+ RESOLVE_IMM1();
+ PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_LESS_EQUAL ? SLTU : SLT) | S(src2) | T(src1) | D(TMP_REG1), DR(TMP_REG1)));
+ }
+ type = (type == SLJIT_GREATER || type == SLJIT_SIG_GREATER) ? SLJIT_NOT_EQUAL : SLJIT_EQUAL;
+ }
+
+ jump->flags |= IS_BIT26_COND;
+ PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_EQUAL ? BNE : BEQ) | S(TMP_REG1) | TA(0) | BRANCH_LENGTH, UNMOVABLE_INS));
+ }
+
+ PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ compiler->size += 2;
+#else
+ compiler->size += 6;
+#endif
+ return jump;
+}
+
+#undef RESOLVE_IMM1
+#undef RESOLVE_IMM2
+
+#undef BRANCH_LENGTH
+#undef BR_Z
+#undef BR_NZ
+#undef BR_T
+#undef BR_F
+
+#undef FLOAT_DATA
+#undef FMT
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump = NULL;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (src & SLJIT_IMM) {
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_JAL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+ if (compiler->delay_slot != UNMOVABLE_INS)
+ jump->flags |= IS_MOVABLE;
+
+ src = TMP_REG2;
+ } else if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, DR(TMP_REG2), src, srcw));
+ src = TMP_REG2;
+ }
+
+ if (type <= SLJIT_JUMP)
+ FAIL_IF(push_inst(compiler, JR | S(src), UNMOVABLE_INS));
+ else
+ FAIL_IF(push_inst(compiler, JALR | S(src) | DA(RETURN_ADDR_REG), UNMOVABLE_INS));
+
+ if (jump != NULL) {
+ jump->addr = compiler->size;
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ compiler->size += 2;
+#else
+ compiler->size += 6;
+#endif
+ }
+
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 src_ar, dst_ar, invert;
+ sljit_s32 saved_op = op;
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ sljit_s32 mem_type = WORD_DATA;
+#else
+ sljit_s32 mem_type = ((op & SLJIT_32) || op == SLJIT_MOV32) ? (INT_DATA | SIGNED_DATA) : WORD_DATA;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ dst_ar = DR((op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, DR(TMP_REG1), dst, dstw, dst, dstw));
+
+ if (type < SLJIT_F_EQUAL) {
+ src_ar = OTHER_FLAG;
+ invert = type & 0x1;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_NOT_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
+ break;
+ case SLJIT_OVERFLOW:
+ case SLJIT_NOT_OVERFLOW:
+ if (compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) {
+ src_ar = OTHER_FLAG;
+ break;
+ }
+ FAIL_IF(push_inst(compiler, SLTIU | SA(OTHER_FLAG) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
+ invert ^= 0x1;
+ break;
+ }
+ } else {
+ invert = 0;
+
+ switch (type) {
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED:
+ invert = 1;
+ break;
+ }
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ FAIL_IF(push_inst(compiler, MFC1 | TA(dst_ar) | FS(TMP_FREG3), dst_ar));
+#else /* SLJIT_MIPS_REV < 6 */
+ FAIL_IF(push_inst(compiler, CFC1 | TA(dst_ar) | DA(FCSR_REG), dst_ar));
+#endif /* SLJIT_MIPS_REV >= 6 */
+ FAIL_IF(push_inst(compiler, SRL | TA(dst_ar) | DA(dst_ar) | SH_IMM(23), dst_ar));
+ FAIL_IF(push_inst(compiler, ANDI | SA(dst_ar) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
+ }
+
+ if (invert) {
+ FAIL_IF(push_inst(compiler, XORI | SA(src_ar) | TA(dst_ar) | IMM(1), dst_ar));
+ src_ar = dst_ar;
+ }
+
+ if (op < SLJIT_ADD) {
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_type, src_ar, dst, dstw);
+
+ if (src_ar != dst_ar)
+ return push_inst(compiler, ADDU_W | SA(src_ar) | TA(0) | DA(dst_ar), dst_ar);
+ return SLJIT_SUCCESS;
+ }
+
+ /* OTHER_FLAG cannot be specified as src2 argument at the moment. */
+ if (DR(TMP_REG2) != src_ar)
+ FAIL_IF(push_inst(compiler, ADDU_W | SA(src_ar) | TA(0) | D(TMP_REG2), DR(TMP_REG2)));
+
+ mem_type |= CUMULATIVE_OP | LOGICAL_OP | IMM_OP | ALT_KEEP_CACHE;
+
+ if (dst & SLJIT_MEM)
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, TMP_REG1, 0, TMP_REG2, 0);
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, dst, dstw, TMP_REG2, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6)
+ sljit_ins ins;
+#endif /* SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 1 && SLJIT_MIPS_REV < 6)
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+#if (defined SLJIT_CONFIG_MIPS_64 && SLJIT_CONFIG_MIPS_64)
+ if (type & SLJIT_32)
+ srcw = (sljit_s32)srcw;
+#endif
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), srcw));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ switch (type & ~SLJIT_32) {
+ case SLJIT_EQUAL:
+ ins = MOVZ | TA(EQUAL_FLAG);
+ break;
+ case SLJIT_NOT_EQUAL:
+ ins = MOVN | TA(EQUAL_FLAG);
+ break;
+ case SLJIT_LESS:
+ case SLJIT_GREATER:
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER:
+ case SLJIT_OVERFLOW:
+ ins = MOVN | TA(OTHER_FLAG);
+ break;
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_NOT_OVERFLOW:
+ ins = MOVZ | TA(OTHER_FLAG);
+ break;
+ case SLJIT_F_EQUAL:
+ case SLJIT_F_LESS:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_UNORDERED:
+ ins = MOVT;
+ break;
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED:
+ ins = MOVF;
+ break;
+ default:
+ ins = MOVZ | TA(OTHER_FLAG);
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ return push_inst(compiler, ins | S(src) | D(dst_reg), DR(dst_reg));
+
+#else /* SLJIT_MIPS_REV < 1 || SLJIT_MIPS_REV >= 6 */
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+#endif /* SLJIT_MIPS_REV >= 1 */
+}
+
+static sljit_s32 update_mem_addr(struct sljit_compiler *compiler, sljit_s32 *mem, sljit_sw *memw, sljit_s16 max_offset)
+{
+ sljit_s32 arg = *mem;
+ sljit_sw argw = *memw;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ if (SLJIT_UNLIKELY(argw)) {
+ FAIL_IF(push_inst(compiler, SLL_W | T(OFFS_REG(arg)) | D(TMP_REG1) | SH_IMM(argw), DR(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG1) | T(arg & REG_MASK) | D(TMP_REG1), DR(TMP_REG1)));
+ } else
+ FAIL_IF(push_inst(compiler, ADDU_W | S(arg & REG_MASK) | T(OFFS_REG(arg)) | D(TMP_REG1), DR(TMP_REG1)));
+
+ *mem = TMP_REG1;
+ *memw = 0;
+
+ return SLJIT_SUCCESS;
+ }
+
+ if (argw <= max_offset && argw >= SIMM_MIN) {
+ *mem = arg & REG_MASK;
+ return SLJIT_SUCCESS;
+ }
+
+ *mem = TMP_REG1;
+
+ if ((sljit_s16)argw > max_offset) {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), argw));
+ *memw = 0;
+ } else {
+ FAIL_IF(load_immediate(compiler, DR(TMP_REG1), TO_ARGW_HI(argw)));
+ *memw = (sljit_s16)argw;
+ }
+
+ if ((arg & REG_MASK) == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADDU_W | S(TMP_REG1) | T(arg & REG_MASK) | D(TMP_REG1), DR(TMP_REG1));
+}
+
+#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#define MEM16_IMM_FIRST(memw) IMM((memw) + 1)
+#define MEM16_IMM_SECOND(memw) IMM(memw)
+#define MEMF64_FS_FIRST(freg) FS(freg)
+#define MEMF64_FS_SECOND(freg) (FS(freg) | ((sljit_ins)1 << 11))
+#else /* !SLJIT_LITTLE_ENDIAN */
+#define MEM16_IMM_FIRST(memw) IMM(memw)
+#define MEM16_IMM_SECOND(memw) IMM((memw) + 1)
+#define MEMF64_FS_FIRST(freg) (FS(freg) | ((sljit_ins)1 << 11))
+#define MEMF64_FS_SECOND(freg) FS(freg)
+#endif /* SLJIT_LITTLE_ENDIAN */
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+#define MEM_CHECK_UNALIGNED(type) ((type) & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16))
+#else /* !SLJIT_CONFIG_MIPS_32 */
+#define MEM_CHECK_UNALIGNED(type) ((type) & (SLJIT_MEM_UNALIGNED | SLJIT_MEM_UNALIGNED_16 | SLJIT_MEM_UNALIGNED_32))
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 op = type & 0xff;
+ sljit_s32 flags = 0;
+ sljit_ins ins;
+#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ sljit_ins ins_right;
+#endif /* !(SLJIT_MIPS_REV >= 6) */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (reg & REG_PAIR_MASK) {
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ if (MEM_CHECK_UNALIGNED(type)) {
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - (2 * SSIZE_OF(sw) - 1)));
+
+ if (!(type & SLJIT_MEM_STORE) && (mem == REG_PAIR_FIRST(reg) || mem == REG_PAIR_SECOND(reg))) {
+ FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
+ mem = TMP_REG1;
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ ins = ((type & SLJIT_MEM_STORE) ? SWL : LWL) | S(mem);
+ ins_right = ((type & SLJIT_MEM_STORE) ? SWR : LWR) | S(mem);
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ ins = ((type & SLJIT_MEM_STORE) ? SDL : LDL) | S(mem);
+ ins_right = ((type & SLJIT_MEM_STORE) ? SDR : LDR) | S(mem);
+#endif /* SLJIT_CONFIG_MIPS_32 */
+
+ FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg))));
+ FAIL_IF(push_inst(compiler, ins_right | T(REG_PAIR_FIRST(reg)) | IMM(memw + (SSIZE_OF(sw) - 1)), DR(REG_PAIR_FIRST(reg))));
+ FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg))));
+ return push_inst(compiler, ins_right | T(REG_PAIR_SECOND(reg)) | IMM((memw + 2 * SSIZE_OF(sw) - 1)), DR(REG_PAIR_SECOND(reg)));
+ }
+#endif /* !(SLJIT_MIPS_REV >= 6) */
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - SSIZE_OF(sw)));
+
+ ins = ((type & SLJIT_MEM_STORE) ? STORE_W : LOAD_W) | S(mem);
+
+ if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) {
+ FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg))));
+ return push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg)));
+ }
+
+ FAIL_IF(push_inst(compiler, ins | T(REG_PAIR_FIRST(reg)) | IMM(memw), DR(REG_PAIR_FIRST(reg))));
+ return push_inst(compiler, ins | T(REG_PAIR_SECOND(reg)) | IMM(memw + SSIZE_OF(sw)), DR(REG_PAIR_SECOND(reg)));
+ }
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+#else /* !(SLJIT_MIPS_REV >= 6) */
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ switch (op) {
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ flags = BYTE_DATA;
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ if (op == SLJIT_MOV_S8)
+ flags |= SIGNED_DATA;
+
+ return emit_op_mem(compiler, flags, DR(reg), mem, memw);
+
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 1));
+ SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2);
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, SRA_W | T(reg) | D(TMP_REG2) | SH_IMM(8), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, data_transfer_insts[BYTE_DATA] | S(mem) | T(TMP_REG2) | MEM16_IMM_FIRST(memw), MOVABLE_INS));
+ return push_inst(compiler, data_transfer_insts[BYTE_DATA] | S(mem) | T(reg) | MEM16_IMM_SECOND(memw), MOVABLE_INS);
+ }
+
+ flags = BYTE_DATA | LOAD_DATA;
+
+ if (op == SLJIT_MOV_S16)
+ flags |= SIGNED_DATA;
+
+ FAIL_IF(push_inst(compiler, data_transfer_insts[flags] | S(mem) | T(TMP_REG2) | MEM16_IMM_FIRST(memw), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, data_transfer_insts[BYTE_DATA | LOAD_DATA] | S(mem) | T(reg) | MEM16_IMM_SECOND(memw), DR(reg)));
+ FAIL_IF(push_inst(compiler, SLL_W | T(TMP_REG2) | D(TMP_REG2) | SH_IMM(8), DR(TMP_REG2)));
+ return push_inst(compiler, OR | S(reg) | T(TMP_REG2) | D(reg), DR(reg));
+
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ if (type & SLJIT_MEM_UNALIGNED_32) {
+ flags = WORD_DATA;
+ if (!(type & SLJIT_MEM_STORE))
+ flags |= LOAD_DATA;
+
+ return emit_op_mem(compiler, flags, DR(reg), mem, memw);
+ }
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 7));
+ SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2);
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, SDL | S(mem) | T(reg) | IMM(memw), MOVABLE_INS));
+ return push_inst(compiler, SDR | S(mem) | T(reg) | IMM(memw + 7), MOVABLE_INS);
+ }
+
+ if (mem == reg) {
+ FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
+ mem = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, LDL | S(mem) | T(reg) | IMM(memw), DR(reg)));
+ return push_inst(compiler, LDR | S(mem) | T(reg) | IMM(memw + 7), DR(reg));
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ }
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - 3));
+ SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2);
+
+ if (type & SLJIT_MEM_STORE) {
+ FAIL_IF(push_inst(compiler, SWL | S(mem) | T(reg) | IMM(memw), MOVABLE_INS));
+ return push_inst(compiler, SWR | S(mem) | T(reg) | IMM(memw + 3), MOVABLE_INS);
+ }
+
+ if (mem == reg) {
+ FAIL_IF(push_inst(compiler, ADDU_W | S(mem) | TA(0) | D(TMP_REG1), DR(TMP_REG1)));
+ mem = TMP_REG1;
+ }
+
+ FAIL_IF(push_inst(compiler, LWL | S(mem) | T(reg) | IMM(memw), DR(reg)));
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ return push_inst(compiler, LWR | S(mem) | T(reg) | IMM(memw + 3), DR(reg));
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ FAIL_IF(push_inst(compiler, LWR | S(mem) | T(reg) | IMM(memw + 3), DR(reg)));
+
+ if (op != SLJIT_MOV_U32)
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 2)
+ return push_inst(compiler, DINSU | T(reg) | SA(0) | (31 << 11) | (0 << 11), DR(reg));
+#else /* SLJIT_MIPS_REV < 1 */
+ FAIL_IF(push_inst(compiler, DSLL32 | T(reg) | D(reg) | SH_IMM(0), DR(reg)));
+ return push_inst(compiler, DSRL32 | T(reg) | D(reg) | SH_IMM(0), DR(reg));
+#endif /* SLJIT_MIPS_REV >= 2 */
+#endif /* SLJIT_CONFIG_MIPS_32 */
+#endif /* SLJIT_MIPS_REV >= 6 */
+}
+
+#if !(defined SLJIT_MIPS_REV && SLJIT_MIPS_REV >= 6)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem(compiler, type, freg, mem, memw));
+
+ FAIL_IF(update_mem_addr(compiler, &mem, &memw, SIMM_MAX - (type & SLJIT_32) ? 3 : 7));
+ SLJIT_ASSERT(FAST_IS_REG(mem) && mem != TMP_REG2);
+
+ if (type & SLJIT_MEM_STORE) {
+ if (type & SLJIT_32) {
+ FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | FS(freg), DR(TMP_REG2)));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS));
+ return push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), MOVABLE_INS);
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | MEMF64_FS_FIRST(freg), DR(TMP_REG2)));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS));
+ FAIL_IF(push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), MOVABLE_INS));
+
+ FAIL_IF(push_inst(compiler, MFC1 | T(TMP_REG2) | MEMF64_FS_SECOND(freg), DR(TMP_REG2)));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ FAIL_IF(push_inst(compiler, SWL | S(mem) | T(TMP_REG2) | IMM(memw + 4), MOVABLE_INS));
+ return push_inst(compiler, SWR | S(mem) | T(TMP_REG2) | IMM(memw + 7), MOVABLE_INS);
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ FAIL_IF(push_inst(compiler, MFC1 | (1 << 21) | T(TMP_REG2) | FS(freg), DR(TMP_REG2)));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ FAIL_IF(push_inst(compiler, SDL | S(mem) | T(TMP_REG2) | IMM(memw), MOVABLE_INS));
+ return push_inst(compiler, SDR | S(mem) | T(TMP_REG2) | IMM(memw + 7), MOVABLE_INS);
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ }
+
+ if (type & SLJIT_32) {
+ FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | FS(freg), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 3), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | MEMF64_FS_FIRST(freg), MOVABLE_INS));
+
+ FAIL_IF(push_inst(compiler, LWL | S(mem) | T(TMP_REG2) | IMM(memw + 4), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, LWR | S(mem) | T(TMP_REG2) | IMM(memw + 7), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, MTC1 | T(TMP_REG2) | MEMF64_FS_SECOND(freg), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+#else /* !SLJIT_CONFIG_MIPS_32 */
+ FAIL_IF(push_inst(compiler, LDL | S(mem) | T(TMP_REG2) | IMM(memw), DR(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, LDR | S(mem) | T(TMP_REG2) | IMM(memw + 7), DR(TMP_REG2)));
+
+ FAIL_IF(push_inst(compiler, MTC1 | (1 << 21) | T(TMP_REG2) | FS(freg), MOVABLE_INS));
+#if (!defined SLJIT_MIPS_REV || SLJIT_MIPS_REV <= 3)
+ FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS));
+#endif
+#endif /* SLJIT_CONFIG_MIPS_32 */
+ return SLJIT_SUCCESS;
+}
+
+#endif /* !SLJIT_MIPS_REV || SLJIT_MIPS_REV < 6 */
+
+#undef MEM16_IMM_FIRST
+#undef MEM16_IMM_SECOND
+#undef MEMF64_FS_FIRST
+#undef MEMF64_FS_SECOND
+#undef MEM_CHECK_UNALIGNED
+
+#undef TO_ARGW_HI
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(emit_const(compiler, dst_r, init_value));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, DR(TMP_REG2), dst, dstw));
+
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r, UNMOVABLE_INS));
+#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, DR(TMP_REG2), dst, dstw));
+
+ return put_label;
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativePPC_32.c b/contrib/libs/pcre2/src/sljit/sljitNativePPC_32.c
new file mode 100644
index 0000000000..9449e4b9d7
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativePPC_32.c
@@ -0,0 +1,340 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/* ppc 32-bit arch dependent functions. */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
+{
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDI | D(reg) | A(0) | IMM(imm));
+
+ if (!(imm & ~0xffff))
+ return push_inst(compiler, ORI | S(TMP_ZERO) | A(reg) | IMM(imm));
+
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 16)));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS;
+}
+
+/* Simplified mnemonics: clrlwi. */
+#define INS_CLEAR_LEFT(dst, src, from) \
+ (RLWINM | S(src) | A(dst) | RLWI_MBE(from, 31))
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_s32 src1, sljit_s32 src2)
+{
+ sljit_u32 imm;
+
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV_P:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if (dst != src2)
+ return push_inst(compiler, OR | S(src2) | A(dst) | B(src2));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(src2) | A(dst));
+ return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 24));
+ }
+ else if ((flags & REG_DEST) && op == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(src2) | A(dst));
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S16)
+ return push_inst(compiler, EXTSH | S(src2) | A(dst));
+ return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 16));
+ }
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_NOT:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ return push_inst(compiler, NOR | RC(flags) | S(src2) | A(dst) | B(src2));
+
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ return push_inst(compiler, CNTLZW | S(src2) | A(dst));
+
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ FAIL_IF(push_inst(compiler, NEG | D(TMP_REG1) | A(src2)));
+ FAIL_IF(push_inst(compiler, AND | S(src2) | A(dst) | B(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, CNTLZW | S(dst) | A(dst)));
+ FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(dst) | IMM(-32)));
+ /* The highest bits are set, if dst < 32, zero otherwise. */
+ FAIL_IF(push_inst(compiler, SRWI(27) | S(TMP_REG1) | A(TMP_REG1)));
+ return push_inst(compiler, XOR | S(dst) | A(dst) | B(TMP_REG1));
+
+ case SLJIT_ADD:
+ if (flags & ALT_FORM1) {
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ return push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ }
+
+ if (flags & ALT_FORM2) {
+ /* Flags does not set: BIN_IMM_EXTS unnecessary. */
+ SLJIT_ASSERT(src2 == TMP_REG2);
+
+ if (flags & ALT_FORM3)
+ return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
+
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM4) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((imm >> 16) & 0xffff) + ((imm >> 15) & 0x1))));
+ src1 = dst;
+ }
+
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (imm & 0xffff));
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm);
+ }
+ SLJIT_ASSERT(!(flags & ALT_FORM4));
+ if (!(flags & ALT_SET_FLAGS))
+ return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2));
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2));
+
+ case SLJIT_ADDC:
+ return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2));
+
+ case SLJIT_SUB:
+ if (flags & ALT_FORM1) {
+ if (flags & ALT_FORM2) {
+ FAIL_IF(push_inst(compiler, CMPLI | CRD(0) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMPL | CRD(0) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM2) {
+ if (flags & ALT_FORM3) {
+ FAIL_IF(push_inst(compiler, CMPI | CRD(0) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM4))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMP | CRD(0) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM4))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM3) {
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ if (src1 != TMP_ZERO)
+ return push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
+ }
+
+ if (flags & ALT_FORM4) {
+ /* Flags does not set: BIN_IMM_EXTS unnecessary. */
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm);
+ }
+
+ if (!(flags & ALT_SET_FLAGS)) {
+ SLJIT_ASSERT(src1 != TMP_ZERO);
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+
+ if (src1 != TMP_ZERO)
+ return push_inst(compiler, SUBF | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, NEG | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
+
+ case SLJIT_SUBC:
+ return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1));
+
+ case SLJIT_MUL:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm);
+ }
+ return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
+
+ case SLJIT_AND:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ANDI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ANDIS | S(src1) | A(dst) | compiler->imm);
+ }
+ return push_inst(compiler, AND | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_OR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ORI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ORIS | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(imm)));
+ return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(imm >> 16));
+ }
+ return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_XOR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, XORI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, XORIS | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(imm)));
+ return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(imm >> 16));
+ }
+ return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm & 0x1f;
+ return push_inst(compiler, SLWI(imm) | RC(flags) | S(src1) | A(dst));
+ }
+
+ if (op == SLJIT_MSHL) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, SLW | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm & 0x1f;
+ /* Since imm can be 0, SRWI() cannot be used. */
+ return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | RLWI_SH((32 - imm) & 0x1f) | RLWI_MBE(imm, 31));
+ }
+
+ if (op == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, SRW | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm & 0x1f;
+ return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (imm << 11));
+ }
+
+ if (op == SLJIT_MASHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (op == SLJIT_ROTR)
+ imm = (sljit_u32)(-(sljit_s32)imm);
+
+ imm &= 0x1f;
+ return push_inst(compiler, RLWINM | S(src1) | A(dst) | RLWI_SH(imm) | RLWI_MBE(0, 31));
+ }
+
+ if (op == SLJIT_ROTR) {
+ FAIL_IF(push_inst(compiler, SUBFIC | D(TMP_REG2) | A(src2) | 0));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, RLWNM | S(src1) | A(dst) | B(src2) | RLWI_MBE(0, 31));
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw init_value)
+{
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 16)));
+ return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins *)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 0);
+ SLJIT_ASSERT((inst[0] & 0xfc1f0000) == ADDIS && (inst[1] & 0xfc000000) == ORI);
+ inst[0] = (inst[0] & 0xffff0000) | ((new_target >> 16) & 0xffff);
+ inst[1] = (inst[1] & 0xffff0000) | (new_target & 0xffff);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 2, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 2);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativePPC_64.c b/contrib/libs/pcre2/src/sljit/sljitNativePPC_64.c
new file mode 100644
index 0000000000..80549108bf
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativePPC_64.c
@@ -0,0 +1,579 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/* ppc 64-bit arch dependent functions. */
+
+#if defined(__GNUC__) || (defined(__IBM_GCC_ASM) && __IBM_GCC_ASM)
+#define ASM_SLJIT_CLZ(src, dst) \
+ __asm__ volatile ( "cntlzd %0, %1" : "=r"(dst) : "r"(src) )
+#elif defined(__xlc__)
+#error "Please enable GCC syntax for inline assembly statements"
+#else
+#error "Must implement count leading zeroes"
+#endif
+
+/* Computes SLDI(63 - shift). */
+#define PUSH_SLDI_NEG(reg, shift) \
+ push_inst(compiler, RLDICR | S(reg) | A(reg) | RLDI_SH(63 - shift) | RLDI_ME(shift))
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
+{
+ sljit_uw tmp;
+ sljit_uw shift;
+ sljit_uw tmp2;
+ sljit_uw shift2;
+
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDI | D(reg) | A(0) | IMM(imm));
+
+ if (!(imm & ~0xffff))
+ return push_inst(compiler, ORI | S(TMP_ZERO) | A(reg) | IMM(imm));
+
+ if (imm <= 0x7fffffffl && imm >= -0x80000000l) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 16)));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS;
+ }
+
+ /* Count leading zeroes. */
+ tmp = (sljit_uw)((imm >= 0) ? imm : ~imm);
+ ASM_SLJIT_CLZ(tmp, shift);
+ SLJIT_ASSERT(shift > 0);
+ shift--;
+ tmp = ((sljit_uw)imm << shift);
+
+ if ((tmp & ~0xffff000000000000ul) == 0) {
+ FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
+ shift += 15;
+ return PUSH_SLDI_NEG(reg, shift);
+ }
+
+ if ((tmp & ~0xffffffff00000000ul) == 0) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
+ FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp >> 32)));
+ shift += 31;
+ return PUSH_SLDI_NEG(reg, shift);
+ }
+
+ /* Cut out the 16 bit from immediate. */
+ shift += 15;
+ tmp2 = (sljit_uw)imm & (((sljit_uw)1 << (63 - shift)) - 1);
+
+ if (tmp2 <= 0xffff) {
+ FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
+ FAIL_IF(PUSH_SLDI_NEG(reg, shift));
+ return push_inst(compiler, ORI | S(reg) | A(reg) | (sljit_ins)tmp2);
+ }
+
+ if (tmp2 <= 0xffffffff) {
+ FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | IMM(tmp >> 48)));
+ FAIL_IF(PUSH_SLDI_NEG(reg, shift));
+ FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | (sljit_ins)(tmp2 >> 16)));
+ return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp2)) : SLJIT_SUCCESS;
+ }
+
+ ASM_SLJIT_CLZ(tmp2, shift2);
+ tmp2 <<= shift2;
+
+ if ((tmp2 & ~0xffff000000000000ul) == 0) {
+ FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | (sljit_ins)(tmp >> 48)));
+ shift2 += 15;
+ shift += (63 - shift2);
+ FAIL_IF(PUSH_SLDI_NEG(reg, shift));
+ FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | (sljit_ins)(tmp2 >> 48)));
+ return PUSH_SLDI_NEG(reg, shift2);
+ }
+
+ /* The general version. */
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | (sljit_ins)((sljit_uw)imm >> 48)));
+ FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm >> 32)));
+ FAIL_IF(PUSH_SLDI_NEG(reg, 31));
+ FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(imm >> 16)));
+ return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm));
+}
+
+#undef PUSH_SLDI_NEG
+
+#define CLRLDI(dst, src, n) \
+ (RLDICL | S(src) | A(dst) | RLDI_SH(0) | RLDI_MB(n))
+
+/* Sign extension for integer operations. */
+#define UN_EXTS() \
+ if ((flags & (ALT_SIGN_EXT | REG2_SOURCE)) == (ALT_SIGN_EXT | REG2_SOURCE)) { \
+ FAIL_IF(push_inst(compiler, EXTSW | S(src2) | A(TMP_REG2))); \
+ src2 = TMP_REG2; \
+ }
+
+#define BIN_EXTS() \
+ if (flags & ALT_SIGN_EXT) { \
+ if (flags & REG1_SOURCE) { \
+ FAIL_IF(push_inst(compiler, EXTSW | S(src1) | A(TMP_REG1))); \
+ src1 = TMP_REG1; \
+ } \
+ if (flags & REG2_SOURCE) { \
+ FAIL_IF(push_inst(compiler, EXTSW | S(src2) | A(TMP_REG2))); \
+ src2 = TMP_REG2; \
+ } \
+ }
+
+#define BIN_IMM_EXTS() \
+ if ((flags & (ALT_SIGN_EXT | REG1_SOURCE)) == (ALT_SIGN_EXT | REG1_SOURCE)) { \
+ FAIL_IF(push_inst(compiler, EXTSW | S(src1) | A(TMP_REG1))); \
+ src1 = TMP_REG1; \
+ }
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_s32 src1, sljit_s32 src2)
+{
+ sljit_u32 imm;
+
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if (dst != src2)
+ return push_inst(compiler, OR | S(src2) | A(dst) | B(src2));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S32)
+ return push_inst(compiler, EXTSW | S(src2) | A(dst));
+ return push_inst(compiler, CLRLDI(dst, src2, 32));
+ }
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(src2) | A(dst));
+ return push_inst(compiler, CLRLDI(dst, src2, 56));
+ }
+ else if ((flags & REG_DEST) && op == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(src2) | A(dst));
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ if (op == SLJIT_MOV_S16)
+ return push_inst(compiler, EXTSH | S(src2) | A(dst));
+ return push_inst(compiler, CLRLDI(dst, src2, 48));
+ }
+ else {
+ SLJIT_ASSERT(dst == src2);
+ }
+ return SLJIT_SUCCESS;
+
+ case SLJIT_NOT:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ UN_EXTS();
+ return push_inst(compiler, NOR | RC(flags) | S(src2) | A(dst) | B(src2));
+
+ case SLJIT_CLZ:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ return push_inst(compiler, ((flags & ALT_FORM1) ? CNTLZW : CNTLZD) | S(src2) | A(dst));
+
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1);
+ FAIL_IF(push_inst(compiler, NEG | D(TMP_REG1) | A(src2)));
+ FAIL_IF(push_inst(compiler, AND | S(src2) | A(dst) | B(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, ((flags & ALT_FORM1) ? CNTLZW : CNTLZD) | S(dst) | A(dst)));
+ FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(dst) | IMM((flags & ALT_FORM1) ? -32 : -64)));
+ /* The highest bits are set, if dst < bit width, zero otherwise. */
+ FAIL_IF(push_inst(compiler, ((flags & ALT_FORM1) ? SRWI(27) : SRDI(58)) | S(TMP_REG1) | A(TMP_REG1)));
+ return push_inst(compiler, XOR | S(dst) | A(dst) | B(TMP_REG1));
+
+ case SLJIT_ADD:
+ if (flags & ALT_FORM1) {
+ if (flags & ALT_SIGN_EXT) {
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(src1) | A(TMP_REG1)));
+ src1 = TMP_REG1;
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(src2) | A(TMP_REG2)));
+ src2 = TMP_REG2;
+ }
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ FAIL_IF(push_inst(compiler, ADD | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)));
+ if (flags & ALT_SIGN_EXT)
+ return push_inst(compiler, SRDI(32) | S(dst) | A(dst));
+ return SLJIT_SUCCESS;
+ }
+
+ if (flags & ALT_FORM2) {
+ /* Flags does not set: BIN_IMM_EXTS unnecessary. */
+ SLJIT_ASSERT(src2 == TMP_REG2);
+
+ if (flags & ALT_FORM3)
+ return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm);
+
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM4) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(dst) | A(src1) | (((imm >> 16) & 0xffff) + ((imm >> 15) & 0x1))));
+ src1 = dst;
+ }
+
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (imm & 0xffff));
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ BIN_IMM_EXTS();
+ return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm);
+ }
+ if (flags & ALT_FORM4) {
+ if (flags & ALT_FORM5)
+ FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm));
+ else
+ FAIL_IF(push_inst(compiler, ADD | D(dst) | A(src1) | B(src2)));
+ return push_inst(compiler, CMPI | A(dst) | 0);
+ }
+ if (!(flags & ALT_SET_FLAGS))
+ return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2));
+ BIN_EXTS();
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, ADDC | RC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2));
+ return push_inst(compiler, ADD | RC(flags) | D(dst) | A(src1) | B(src2));
+
+ case SLJIT_ADDC:
+ BIN_EXTS();
+ return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2));
+
+ case SLJIT_SUB:
+ if (flags & ALT_FORM1) {
+ if (flags & ALT_FORM2) {
+ FAIL_IF(push_inst(compiler, CMPLI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMPL | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM3))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM2) {
+ if (flags & ALT_FORM3) {
+ FAIL_IF(push_inst(compiler, CMPI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm));
+ if (!(flags & ALT_FORM4))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, ADDI | D(dst) | A(src1) | (-compiler->imm & 0xffff));
+ }
+ FAIL_IF(push_inst(compiler, CMP | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)));
+ if (!(flags & ALT_FORM4))
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ if (flags & ALT_FORM3) {
+ if (flags & ALT_SIGN_EXT) {
+ if (src1 != TMP_ZERO) {
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(src1) | A(TMP_REG1)));
+ src1 = TMP_REG1;
+ }
+ if (src2 != TMP_ZERO) {
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(src2) | A(TMP_REG2)));
+ src2 = TMP_REG2;
+ }
+ }
+
+ /* Setting XER SO is not enough, CR SO is also needed. */
+ if (src1 != TMP_ZERO)
+ FAIL_IF(push_inst(compiler, SUBF | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)));
+ else
+ FAIL_IF(push_inst(compiler, NEG | OE(ALT_SET_FLAGS) | RC(ALT_SET_FLAGS) | D(dst) | A(src2)));
+
+ if (flags & ALT_SIGN_EXT)
+ return push_inst(compiler, SRDI(32) | S(dst) | A(dst));
+ return SLJIT_SUCCESS;
+ }
+
+ if (flags & ALT_FORM4) {
+ /* Flags does not set: BIN_IMM_EXTS unnecessary. */
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm);
+ }
+
+ if (!(flags & ALT_SET_FLAGS)) {
+ SLJIT_ASSERT(src1 != TMP_ZERO);
+ return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1));
+ }
+
+ BIN_EXTS();
+ if (flags & ALT_FORM5)
+ return push_inst(compiler, SUBFC | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+
+ if (src1 != TMP_ZERO)
+ return push_inst(compiler, SUBF | RC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, NEG | RC(ALT_SET_FLAGS) | D(dst) | A(src2));
+
+ case SLJIT_SUBC:
+ BIN_EXTS();
+ return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1));
+
+ case SLJIT_MUL:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm);
+ }
+ BIN_EXTS();
+ if (flags & ALT_FORM2)
+ return push_inst(compiler, MULLW | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
+ return push_inst(compiler, MULLD | OE(flags) | RC(flags) | D(dst) | A(src2) | B(src1));
+
+ case SLJIT_AND:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ANDI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ANDIS | S(src1) | A(dst) | compiler->imm);
+ }
+ return push_inst(compiler, AND | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_OR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ORI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, ORIS | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(imm)));
+ return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(imm >> 16));
+ }
+ return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_XOR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, XORI | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM2) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ return push_inst(compiler, XORIS | S(src1) | A(dst) | compiler->imm);
+ }
+ if (flags & ALT_FORM3) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(imm)));
+ return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(imm >> 16));
+ }
+ return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM2) {
+ imm &= 0x1f;
+ return push_inst(compiler, SLWI(imm) | RC(flags) | S(src1) | A(dst));
+ }
+
+ imm &= 0x3f;
+ return push_inst(compiler, SLDI(imm) | RC(flags) | S(src1) | A(dst));
+ }
+
+ if (op == SLJIT_MSHL) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, ((flags & ALT_FORM2) ? SLW : SLD) | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM2) {
+ imm &= 0x1f;
+ /* Since imm can be 0, SRWI() cannot be used. */
+ return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | RLWI_SH((32 - imm) & 0x1f) | RLWI_MBE(imm, 31));
+ }
+
+ imm &= 0x3f;
+ /* Since imm can be 0, SRDI() cannot be used. */
+ return push_inst(compiler, RLDICL | RC(flags) | S(src1) | A(dst) | RLDI_SH((64 - imm) & 0x3f) | RLDI_MB(imm));
+ }
+
+ if (op == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, ((flags & ALT_FORM2) ? SRW : SRD) | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (flags & ALT_FORM2) {
+ imm &= 0x1f;
+ return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (imm << 11));
+ }
+
+ imm &= 0x3f;
+ return push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | RLDI_SH(imm));
+ }
+
+ if (op == SLJIT_MASHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | ((flags & ALT_FORM2) ? 0x1f : 0x3f)));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, ((flags & ALT_FORM2) ? SRAW : SRAD) | RC(flags) | S(src1) | A(dst) | B(src2));
+
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & ALT_FORM1) {
+ SLJIT_ASSERT(src2 == TMP_REG2);
+ imm = compiler->imm;
+
+ if (op == SLJIT_ROTR)
+ imm = (sljit_u32)(-(sljit_s32)imm);
+
+ if (flags & ALT_FORM2) {
+ imm &= 0x1f;
+ return push_inst(compiler, RLWINM | S(src1) | A(dst) | RLWI_SH(imm) | RLWI_MBE(0, 31));
+ }
+
+ imm &= 0x3f;
+ return push_inst(compiler, RLDICL | S(src1) | A(dst) | RLDI_SH(imm));
+ }
+
+ if (op == SLJIT_ROTR) {
+ FAIL_IF(push_inst(compiler, SUBFIC | D(TMP_REG2) | A(src2) | 0));
+ src2 = TMP_REG2;
+ }
+
+ return push_inst(compiler, ((flags & ALT_FORM2) ? (RLWNM | RLWI_MBE(0, 31)) : (RLDCL | RLDI_MB(0))) | S(src1) | A(dst) | B(src2));
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src)
+{
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 reg = 0;
+
+ if (src)
+ reg = *src & REG_MASK;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ case SLJIT_ARG_TYPE_F32:
+ arg_count++;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+
+ if (arg_count != word_arg_count && arg_count == reg) {
+ FAIL_IF(push_inst(compiler, OR | S(reg) | A(TMP_CALL_REG) | B(reg)));
+ *src = TMP_CALL_REG;
+ }
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ case SLJIT_ARG_TYPE_F32:
+ arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count)
+ FAIL_IF(push_inst(compiler, OR | S(word_arg_count) | A(arg_count) | B(word_arg_count)));
+
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw init_value)
+{
+ FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 48)));
+ FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value >> 32)));
+ FAIL_IF(push_inst(compiler, SLDI(32) | S(reg) | A(reg)));
+ FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(init_value >> 16)));
+ return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins*)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
+ inst[0] = (inst[0] & 0xffff0000u) | ((sljit_ins)(new_target >> 48) & 0xffff);
+ inst[1] = (inst[1] & 0xffff0000u) | ((sljit_ins)(new_target >> 32) & 0xffff);
+ inst[3] = (inst[3] & 0xffff0000u) | ((sljit_ins)(new_target >> 16) & 0xffff);
+ inst[4] = (inst[4] & 0xffff0000u) | ((sljit_ins)new_target & 0xffff);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 5);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativePPC_common.c b/contrib/libs/pcre2/src/sljit/sljitNativePPC_common.c
new file mode 100644
index 0000000000..790faffe46
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativePPC_common.c
@@ -0,0 +1,2851 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "PowerPC" SLJIT_CPUINFO;
+}
+
+/* Length of an instruction word.
+ Both for ppc-32 and ppc-64. */
+typedef sljit_u32 sljit_ins;
+
+#if ((defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) && (defined _AIX)) \
+ || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define SLJIT_PPC_STACK_FRAME_V2 1
+#endif
+
+#ifdef _AIX
+#error #include <sys/cache.h>
+#endif
+
+#if (defined _CALL_ELF && _CALL_ELF == 2)
+#define SLJIT_PASS_ENTRY_ADDR_TO_CALL 1
+#endif
+
+#if (defined SLJIT_CACHE_FLUSH_OWN_IMPL && SLJIT_CACHE_FLUSH_OWN_IMPL)
+
+static void ppc_cache_flush(sljit_ins *from, sljit_ins *to)
+{
+#ifdef _AIX
+ _sync_cache_range((caddr_t)from, (int)((size_t)to - (size_t)from));
+#elif defined(__GNUC__) || (defined(__IBM_GCC_ASM) && __IBM_GCC_ASM)
+# if defined(_ARCH_PWR) || defined(_ARCH_PWR2)
+ /* Cache flush for POWER architecture. */
+ while (from < to) {
+ __asm__ volatile (
+ "clf 0, %0\n"
+ "dcs\n"
+ : : "r"(from)
+ );
+ from++;
+ }
+ __asm__ volatile ( "ics" );
+# elif defined(_ARCH_COM) && !defined(_ARCH_PPC)
+# error "Cache flush is not implemented for PowerPC/POWER common mode."
+# else
+ /* Cache flush for PowerPC architecture. */
+ while (from < to) {
+ __asm__ volatile (
+ "dcbf 0, %0\n"
+ "sync\n"
+ "icbi 0, %0\n"
+ : : "r"(from)
+ );
+ from++;
+ }
+ __asm__ volatile ( "isync" );
+# endif
+# ifdef __xlc__
+# warning "This file may fail to compile if -qfuncsect is used"
+# endif
+#elif defined(__xlc__)
+#error "Please enable GCC syntax for inline assembly statements with -qasm=gcc"
+#else
+#error "This platform requires a cache flush implementation."
+#endif /* _AIX */
+}
+
+#endif /* (defined SLJIT_CACHE_FLUSH_OWN_IMPL && SLJIT_CACHE_FLUSH_OWN_IMPL) */
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_ZERO (SLJIT_NUMBER_OF_REGISTERS + 4)
+
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+#define TMP_CALL_REG (SLJIT_NUMBER_OF_REGISTERS + 5)
+#else
+#define TMP_CALL_REG TMP_REG2
+#endif
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = {
+ 0, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 1, 9, 10, 31, 12
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 0, 13
+};
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+#define D(d) ((sljit_ins)reg_map[d] << 21)
+#define S(s) ((sljit_ins)reg_map[s] << 21)
+#define A(a) ((sljit_ins)reg_map[a] << 16)
+#define B(b) ((sljit_ins)reg_map[b] << 11)
+#define C(c) ((sljit_ins)reg_map[c] << 6)
+#define FD(fd) ((sljit_ins)freg_map[fd] << 21)
+#define FS(fs) ((sljit_ins)freg_map[fs] << 21)
+#define FA(fa) ((sljit_ins)freg_map[fa] << 16)
+#define FB(fb) ((sljit_ins)freg_map[fb] << 11)
+#define FC(fc) ((sljit_ins)freg_map[fc] << 6)
+#define IMM(imm) ((sljit_ins)(imm) & 0xffff)
+#define CRD(d) ((sljit_ins)(d) << 21)
+
+/* Instruction bit sections.
+ OE and Rc flag (see ALT_SET_FLAGS). */
+#define OE(flags) ((flags) & ALT_SET_FLAGS)
+/* Rc flag (see ALT_SET_FLAGS). */
+#define RC(flags) (((flags) & ALT_SET_FLAGS) >> 10)
+#define HI(opcode) ((sljit_ins)(opcode) << 26)
+#define LO(opcode) ((sljit_ins)(opcode) << 1)
+
+#define ADD (HI(31) | LO(266))
+#define ADDC (HI(31) | LO(10))
+#define ADDE (HI(31) | LO(138))
+#define ADDI (HI(14))
+#define ADDIC (HI(13))
+#define ADDIS (HI(15))
+#define ADDME (HI(31) | LO(234))
+#define AND (HI(31) | LO(28))
+#define ANDI (HI(28))
+#define ANDIS (HI(29))
+#define Bx (HI(18))
+#define BCx (HI(16))
+#define BCCTR (HI(19) | LO(528) | (3 << 11))
+#define BLR (HI(19) | LO(16) | (0x14 << 21))
+#define CNTLZD (HI(31) | LO(58))
+#define CNTLZW (HI(31) | LO(26))
+#define CMP (HI(31) | LO(0))
+#define CMPI (HI(11))
+#define CMPL (HI(31) | LO(32))
+#define CMPLI (HI(10))
+#define CROR (HI(19) | LO(449))
+#define DCBT (HI(31) | LO(278))
+#define DIVD (HI(31) | LO(489))
+#define DIVDU (HI(31) | LO(457))
+#define DIVW (HI(31) | LO(491))
+#define DIVWU (HI(31) | LO(459))
+#define EXTSB (HI(31) | LO(954))
+#define EXTSH (HI(31) | LO(922))
+#define EXTSW (HI(31) | LO(986))
+#define FABS (HI(63) | LO(264))
+#define FADD (HI(63) | LO(21))
+#define FADDS (HI(59) | LO(21))
+#define FCFID (HI(63) | LO(846))
+#define FCMPU (HI(63) | LO(0))
+#define FCTIDZ (HI(63) | LO(815))
+#define FCTIWZ (HI(63) | LO(15))
+#define FDIV (HI(63) | LO(18))
+#define FDIVS (HI(59) | LO(18))
+#define FMR (HI(63) | LO(72))
+#define FMUL (HI(63) | LO(25))
+#define FMULS (HI(59) | LO(25))
+#define FNEG (HI(63) | LO(40))
+#define FRSP (HI(63) | LO(12))
+#define FSUB (HI(63) | LO(20))
+#define FSUBS (HI(59) | LO(20))
+#define LD (HI(58) | 0)
+#define LFD (HI(50))
+#define LWZ (HI(32))
+#define MFCR (HI(31) | LO(19))
+#define MFLR (HI(31) | LO(339) | 0x80000)
+#define MFXER (HI(31) | LO(339) | 0x10000)
+#define MTCTR (HI(31) | LO(467) | 0x90000)
+#define MTLR (HI(31) | LO(467) | 0x80000)
+#define MTXER (HI(31) | LO(467) | 0x10000)
+#define MULHD (HI(31) | LO(73))
+#define MULHDU (HI(31) | LO(9))
+#define MULHW (HI(31) | LO(75))
+#define MULHWU (HI(31) | LO(11))
+#define MULLD (HI(31) | LO(233))
+#define MULLI (HI(7))
+#define MULLW (HI(31) | LO(235))
+#define NEG (HI(31) | LO(104))
+#define NOP (HI(24))
+#define NOR (HI(31) | LO(124))
+#define OR (HI(31) | LO(444))
+#define ORI (HI(24))
+#define ORIS (HI(25))
+#define RLDCL (HI(30) | LO(8))
+#define RLDICL (HI(30) | LO(0 << 1))
+#define RLDICR (HI(30) | LO(1 << 1))
+#define RLDIMI (HI(30) | LO(3 << 1))
+#define RLWIMI (HI(20))
+#define RLWINM (HI(21))
+#define RLWNM (HI(23))
+#define SLD (HI(31) | LO(27))
+#define SLW (HI(31) | LO(24))
+#define SRAD (HI(31) | LO(794))
+#define SRADI (HI(31) | LO(413 << 1))
+#define SRAW (HI(31) | LO(792))
+#define SRAWI (HI(31) | LO(824))
+#define SRD (HI(31) | LO(539))
+#define SRW (HI(31) | LO(536))
+#define STD (HI(62) | 0)
+#define STDU (HI(62) | 1)
+#define STDUX (HI(31) | LO(181))
+#define STFD (HI(54))
+#define STFIWX (HI(31) | LO(983))
+#define STW (HI(36))
+#define STWU (HI(37))
+#define STWUX (HI(31) | LO(183))
+#define SUBF (HI(31) | LO(40))
+#define SUBFC (HI(31) | LO(8))
+#define SUBFE (HI(31) | LO(136))
+#define SUBFIC (HI(8))
+#define XOR (HI(31) | LO(316))
+#define XORI (HI(26))
+#define XORIS (HI(27))
+
+#define SIMM_MAX (0x7fff)
+#define SIMM_MIN (-0x8000)
+#define UIMM_MAX (0xffff)
+
+/* Shift helpers. */
+#define RLWI_SH(sh) ((sljit_ins)(sh) << 11)
+#define RLWI_MBE(mb, me) (((sljit_ins)(mb) << 6) | ((sljit_ins)(me) << 1))
+#define RLDI_SH(sh) ((((sljit_ins)(sh) & 0x1f) << 11) | (((sljit_ins)(sh) & 0x20) >> 4))
+#define RLDI_MB(mb) ((((sljit_ins)(mb) & 0x1f) << 6) | ((sljit_ins)(mb) & 0x20))
+#define RLDI_ME(me) RLDI_MB(me)
+
+#define SLWI(shift) (RLWINM | RLWI_SH(shift) | RLWI_MBE(0, 31 - (shift)))
+#define SLDI(shift) (RLDICR | RLDI_SH(shift) | RLDI_ME(63 - (shift)))
+/* shift > 0 */
+#define SRWI(shift) (RLWINM | RLWI_SH(32 - (shift)) | RLWI_MBE((shift), 31))
+#define SRDI(shift) (RLDICL | RLDI_SH(64 - (shift)) | RLDI_MB(shift))
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+#define SLWI_W(shift) SLWI(shift)
+#else /* !SLJIT_CONFIG_PPC_32 */
+#define SLWI_W(shift) SLDI(shift)
+#endif /* SLJIT_CONFIG_PPC_32 */
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct sljit_function_context* context, sljit_uw addr, void* func)
+{
+ sljit_uw* ptrs;
+
+ if (func_ptr)
+ *func_ptr = (void*)context;
+
+ ptrs = (sljit_uw*)func;
+ context->addr = addr ? addr : ptrs[0];
+ context->r2 = ptrs[1];
+ context->r11 = ptrs[2];
+}
+#endif
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins)
+{
+ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ptr);
+ *ptr = ins;
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 detect_jump_type(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+ sljit_uw target_addr;
+ sljit_uw extra_jump_flags;
+
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) && (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ if (jump->flags & (SLJIT_REWRITABLE_JUMP | IS_CALL))
+ return 0;
+#else
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ return 0;
+#endif
+
+ if (jump->flags & JUMP_ADDR)
+ target_addr = jump->u.target;
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset;
+ }
+
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL) && (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (jump->flags & IS_CALL)
+ goto keep_address;
+#endif
+
+ diff = ((sljit_sw)target_addr - (sljit_sw)(code_ptr) - executable_offset) & ~0x3l;
+
+ extra_jump_flags = 0;
+ if (jump->flags & IS_COND) {
+ if (diff <= 0x7fff && diff >= -0x8000) {
+ jump->flags |= PATCH_B;
+ return 1;
+ }
+ if (target_addr <= 0xffff) {
+ jump->flags |= PATCH_B | PATCH_ABS_B;
+ return 1;
+ }
+ extra_jump_flags = REMOVE_COND;
+
+ diff -= SSIZE_OF(ins);
+ }
+
+ if (diff <= 0x01ffffff && diff >= -0x02000000) {
+ jump->flags |= PATCH_B | extra_jump_flags;
+ return 1;
+ }
+
+ if (target_addr <= 0x03ffffff) {
+ jump->flags |= PATCH_B | PATCH_ABS_B | extra_jump_flags;
+ return 1;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+keep_address:
+#endif
+ if (target_addr <= 0x7fffffff) {
+ jump->flags |= PATCH_ABS32;
+ return 1;
+ }
+
+ if (target_addr <= 0x7fffffffffffl) {
+ jump->flags |= PATCH_ABS48;
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label)
+{
+ if (max_label < 0x100000000l) {
+ put_label->flags = 0;
+ return 1;
+ }
+
+ if (max_label < 0x1000000000000l) {
+ put_label->flags = 1;
+ return 3;
+ }
+
+ put_label->flags = 2;
+ return 4;
+}
+
+static SLJIT_INLINE void put_label_set(struct sljit_put_label *put_label)
+{
+ sljit_uw addr = put_label->label->addr;
+ sljit_ins *inst = (sljit_ins *)put_label->addr;
+ sljit_u32 reg = *inst;
+
+ if (put_label->flags == 0) {
+ SLJIT_ASSERT(addr < 0x100000000l);
+ inst[0] = ORIS | S(TMP_ZERO) | A(reg) | IMM(addr >> 16);
+ }
+ else {
+ if (put_label->flags == 1) {
+ SLJIT_ASSERT(addr < 0x1000000000000l);
+ inst[0] = ORI | S(TMP_ZERO) | A(reg) | IMM(addr >> 32);
+ }
+ else {
+ inst[0] = ORIS | S(TMP_ZERO) | A(reg) | IMM(addr >> 48);
+ inst[1] = ORI | S(reg) | A(reg) | IMM((addr >> 32) & 0xffff);
+ inst++;
+ }
+
+ inst[1] = SLDI(32) | S(reg) | A(reg);
+ inst[2] = ORIS | S(reg) | A(reg) | IMM((addr >> 16) & 0xffff);
+ inst += 2;
+ }
+
+ inst[1] = ORI | S(reg) | A(reg) | IMM(addr & 0xffff);
+}
+
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_ins *code;
+ sljit_ins *code_ptr;
+ sljit_ins *buf_ptr;
+ sljit_ins *buf_end;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_uw addr;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ compiler->size += (compiler->size & 0x1) + (sizeof(struct sljit_function_context) / sizeof(sljit_ins));
+#else
+ compiler->size += (sizeof(struct sljit_function_context) / sizeof(sljit_ins));
+#endif
+#endif
+ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_ins*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == word_count) {
+ /* Just recording the address. */
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == word_count) {
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ jump->addr = (sljit_uw)(code_ptr - 3);
+#else
+ jump->addr = (sljit_uw)(code_ptr - 6);
+#endif
+ if (detect_jump_type(jump, code_ptr, code, executable_offset)) {
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ code_ptr[-3] = code_ptr[0];
+ code_ptr -= 3;
+#else
+ if (jump->flags & PATCH_ABS32) {
+ code_ptr -= 3;
+ code_ptr[-1] = code_ptr[2];
+ code_ptr[0] = code_ptr[3];
+ }
+ else if (jump->flags & PATCH_ABS48) {
+ code_ptr--;
+ code_ptr[-1] = code_ptr[0];
+ code_ptr[0] = code_ptr[1];
+ /* rldicr rX,rX,32,31 -> rX,rX,16,47 */
+ SLJIT_ASSERT((code_ptr[-3] & 0xfc00ffff) == 0x780007c6);
+ code_ptr[-3] ^= 0x8422;
+ /* oris -> ori */
+ code_ptr[-2] ^= 0x4000000;
+ }
+ else {
+ code_ptr[-6] = code_ptr[0];
+ code_ptr -= 6;
+ }
+#endif
+ if (jump->flags & REMOVE_COND) {
+ code_ptr[0] = BCx | (2 << 2) | ((code_ptr[0] ^ (8 << 21)) & 0x03ff0001);
+ code_ptr++;
+ jump->addr += sizeof(sljit_ins);
+ code_ptr[0] = Bx;
+ jump->flags -= IS_COND;
+ }
+ }
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == word_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size));
+ word_count += 4;
+#endif
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ word_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)(compiler->size - (sizeof(struct sljit_function_context) / sizeof(sljit_ins))));
+#else
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+#endif
+
+ jump = compiler->jumps;
+ while (jump) {
+ do {
+ addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ buf_ptr = (sljit_ins *)jump->addr;
+
+ if (jump->flags & PATCH_B) {
+ if (jump->flags & IS_COND) {
+ if (!(jump->flags & PATCH_ABS_B)) {
+ addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset);
+ SLJIT_ASSERT((sljit_sw)addr <= 0x7fff && (sljit_sw)addr >= -0x8000);
+ *buf_ptr = BCx | ((sljit_ins)addr & 0xfffc) | ((*buf_ptr) & 0x03ff0001);
+ }
+ else {
+ SLJIT_ASSERT(addr <= 0xffff);
+ *buf_ptr = BCx | ((sljit_ins)addr & 0xfffc) | 0x2 | ((*buf_ptr) & 0x03ff0001);
+ }
+ }
+ else {
+ if (!(jump->flags & PATCH_ABS_B)) {
+ addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset);
+ SLJIT_ASSERT((sljit_sw)addr <= 0x01ffffff && (sljit_sw)addr >= -0x02000000);
+ *buf_ptr = Bx | ((sljit_ins)addr & 0x03fffffc) | ((*buf_ptr) & 0x1);
+ }
+ else {
+ SLJIT_ASSERT(addr <= 0x03ffffff);
+ *buf_ptr = Bx | ((sljit_ins)addr & 0x03fffffc) | 0x2 | ((*buf_ptr) & 0x1);
+ }
+ }
+ break;
+ }
+
+ /* Set the fields of immediate loads. */
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1]) & 0xffff) == 0);
+ buf_ptr[0] |= (sljit_ins)(addr >> 16) & 0xffff;
+ buf_ptr[1] |= (sljit_ins)addr & 0xffff;
+#else
+ if (jump->flags & PATCH_ABS32) {
+ SLJIT_ASSERT(addr <= 0x7fffffff);
+ SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1]) & 0xffff) == 0);
+ buf_ptr[0] |= (sljit_ins)(addr >> 16) & 0xffff;
+ buf_ptr[1] |= (sljit_ins)addr & 0xffff;
+ break;
+ }
+
+ if (jump->flags & PATCH_ABS48) {
+ SLJIT_ASSERT(addr <= 0x7fffffffffff);
+ SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1] | buf_ptr[3]) & 0xffff) == 0);
+ buf_ptr[0] |= (sljit_ins)(addr >> 32) & 0xffff;
+ buf_ptr[1] |= (sljit_ins)(addr >> 16) & 0xffff;
+ buf_ptr[3] |= (sljit_ins)addr & 0xffff;
+ break;
+ }
+
+ SLJIT_ASSERT(((buf_ptr[0] | buf_ptr[1] | buf_ptr[3] | buf_ptr[4]) & 0xffff) == 0);
+ buf_ptr[0] |= (sljit_ins)(addr >> 48) & 0xffff;
+ buf_ptr[1] |= (sljit_ins)(addr >> 32) & 0xffff;
+ buf_ptr[3] |= (sljit_ins)(addr >> 16) & 0xffff;
+ buf_ptr[4] |= (sljit_ins)addr & 0xffff;
+#endif
+ } while (0);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ addr = put_label->label->addr;
+ buf_ptr = (sljit_ins *)put_label->addr;
+
+ SLJIT_ASSERT((buf_ptr[0] & 0xfc1f0000) == ADDIS && (buf_ptr[1] & 0xfc000000) == ORI);
+ buf_ptr[0] |= (addr >> 16) & 0xffff;
+ buf_ptr[1] |= addr & 0xffff;
+#else
+ put_label_set(put_label);
+#endif
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins);
+
+ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (((sljit_sw)code_ptr) & 0x4)
+ code_ptr++;
+#endif
+ sljit_set_function_context(NULL, (struct sljit_function_context*)code_ptr, (sljit_uw)code, (void*)sljit_generate_code);
+#endif
+
+ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+
+#if (defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL)
+ return code_ptr;
+#else
+ return code;
+#endif
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#else
+ /* Available by default. */
+ return 1;
+#endif
+
+ /* A saved register is set to a zero value. */
+ case SLJIT_HAS_ZERO_REGISTER:
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ case SLJIT_HAS_CTZ:
+ return 2;
+
+ default:
+ return 0;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ return (type >= SLJIT_UNORDERED && type <= SLJIT_ORDERED_LESS_EQUAL);
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+/* inp_flags: */
+
+/* Creates an index in data_transfer_insts array. */
+#define LOAD_DATA 0x01
+#define INDEXED 0x02
+#define SIGNED_DATA 0x04
+
+#define WORD_DATA 0x00
+#define BYTE_DATA 0x08
+#define HALF_DATA 0x10
+#define INT_DATA 0x18
+/* Separates integer and floating point registers */
+#define GPR_REG 0x1f
+#define DOUBLE_DATA 0x20
+
+#define MEM_MASK 0x7f
+
+/* Other inp_flags. */
+
+/* Integer opertion and set flags -> requires exts on 64 bit systems. */
+#define ALT_SIGN_EXT 0x000100
+/* This flag affects the RC() and OERC() macros. */
+#define ALT_SET_FLAGS 0x000400
+#define ALT_FORM1 0x001000
+#define ALT_FORM2 0x002000
+#define ALT_FORM3 0x004000
+#define ALT_FORM4 0x008000
+#define ALT_FORM5 0x010000
+
+/* Source and destination is register. */
+#define REG_DEST 0x000001
+#define REG1_SOURCE 0x000002
+#define REG2_SOURCE 0x000004
+/*
+ALT_SIGN_EXT 0x000100
+ALT_SET_FLAGS 0x000200
+ALT_FORM1 0x001000
+...
+ALT_FORM5 0x010000 */
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+#include "sljitNativePPC_32.c"
+#else
+#include "sljitNativePPC_64.c"
+#endif
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+#define STACK_STORE STW
+#define STACK_LOAD LWZ
+#else
+#define STACK_STORE STD
+#define STACK_LOAD LD
+#endif
+
+#if (defined SLJIT_PPC_STACK_FRAME_V2 && SLJIT_PPC_STACK_FRAME_V2)
+#define LR_SAVE_OFFSET 2 * SSIZE_OF(sw)
+#else
+#define LR_SAVE_OFFSET SSIZE_OF(sw)
+#endif
+
+#define STACK_MAX_DISTANCE (0x8000 - SSIZE_OF(sw) - LR_SAVE_OFFSET)
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg);
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 i, tmp, base, offset;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ sljit_s32 arg_count = 0;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 0)
+ + GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ local_size += SSIZE_OF(sw);
+
+ local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+ compiler->local_size = local_size;
+
+ FAIL_IF(push_inst(compiler, MFLR | D(0)));
+
+ base = SLJIT_SP;
+ offset = local_size;
+
+ if (local_size <= STACK_MAX_DISTANCE) {
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ FAIL_IF(push_inst(compiler, STWU | S(SLJIT_SP) | A(SLJIT_SP) | IMM(-local_size)));
+#else
+ FAIL_IF(push_inst(compiler, STDU | S(SLJIT_SP) | A(SLJIT_SP) | IMM(-local_size)));
+#endif
+ } else {
+ base = TMP_REG1;
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_SP) | A(TMP_REG1) | B(SLJIT_SP)));
+ FAIL_IF(load_immediate(compiler, TMP_REG2, -local_size));
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ FAIL_IF(push_inst(compiler, STWUX | S(SLJIT_SP) | A(SLJIT_SP) | B(TMP_REG2)));
+#else
+ FAIL_IF(push_inst(compiler, STDUX | S(SLJIT_SP) | A(SLJIT_SP) | B(TMP_REG2)));
+#endif
+ local_size = 0;
+ offset = 0;
+ }
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, STFD | FS(i) | A(base) | IMM(offset)));
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, STFD | FS(i) | A(base) | IMM(offset)));
+ }
+
+ if (!(options & SLJIT_ENTER_REG_ARG)) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_STORE | S(TMP_ZERO) | A(base) | IMM(offset)));
+ }
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_STORE | S(i) | A(base) | IMM(offset)));
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_STORE | S(i) | A(base) | IMM(offset)));
+ }
+
+ FAIL_IF(push_inst(compiler, STACK_STORE | S(0) | A(base) | IMM(local_size + LR_SAVE_OFFSET)));
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, ADDI | D(TMP_ZERO) | A(0) | 0));
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ saved_arg_count = 0;
+
+ while (arg_types > 0) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ do {
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ tmp = SLJIT_S0 - saved_arg_count;
+ saved_arg_count++;
+ } else if (arg_count != word_arg_count)
+ tmp = SLJIT_R0 + word_arg_count;
+ else
+ break;
+
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0 + arg_count) | A(tmp) | B(SLJIT_R0 + arg_count)));
+ } while (0);
+#else
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0 + word_arg_count) | A(SLJIT_S0 - saved_arg_count) | B(SLJIT_R0 + word_arg_count)));
+ saved_arg_count++;
+ }
+#endif
+ word_arg_count++;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ arg_count++;
+#endif
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 0)
+ + GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ local_size += SSIZE_OF(sw);
+
+ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_s32 i, tmp, base, offset;
+ sljit_s32 local_size = compiler->local_size;
+
+ base = SLJIT_SP;
+ if (local_size > STACK_MAX_DISTANCE) {
+ base = TMP_REG1;
+ if (local_size > 2 * STACK_MAX_DISTANCE + LR_SAVE_OFFSET) {
+ FAIL_IF(push_inst(compiler, STACK_LOAD | D(base) | A(SLJIT_SP) | IMM(0)));
+ local_size = 0;
+ } else {
+ FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG1) | A(SLJIT_SP) | IMM(local_size - STACK_MAX_DISTANCE)));
+ local_size = STACK_MAX_DISTANCE;
+ }
+ }
+
+ offset = local_size;
+ if (!is_return_to)
+ FAIL_IF(push_inst(compiler, STACK_LOAD | S(0) | A(base) | IMM(offset + LR_SAVE_OFFSET)));
+
+ tmp = SLJIT_FS0 - compiler->fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, LFD | FS(i) | A(base) | IMM(offset)));
+ }
+
+ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, LFD | FS(i) | A(base) | IMM(offset)));
+ }
+
+ if (!(compiler->options & SLJIT_ENTER_REG_ARG)) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | S(TMP_ZERO) | A(base) | IMM(offset)));
+ }
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | S(i) | A(base) | IMM(offset)));
+ }
+
+ for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | S(i) | A(base) | IMM(offset)));
+ }
+
+ if (!is_return_to)
+ push_inst(compiler, MTLR | S(0));
+
+ if (local_size > 0)
+ return push_inst(compiler, ADDI | D(SLJIT_SP) | A(base) | IMM(local_size));
+
+ SLJIT_ASSERT(base == TMP_REG1);
+ return push_inst(compiler, OR | S(base) | A(SLJIT_SP) | B(base));
+}
+
+#undef STACK_STORE
+#undef STACK_LOAD
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ return push_inst(compiler, BLR);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG));
+ src = TMP_CALL_REG;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src)));
+ src = TMP_CALL_REG;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+/* s/l - store/load (1 bit)
+ i/x - immediate/indexed form
+ u/s - signed/unsigned (1 bit)
+ w/b/h/i - word/byte/half/int allowed (2 bit)
+
+ Some opcodes are repeated (e.g. store signed / unsigned byte is the same instruction). */
+
+/* 64 bit only: [reg+imm] must be aligned to 4 bytes. */
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define INT_ALIGNED 0x10000
+#endif
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+#define ARCH_32_64(a, b) a
+#define INST_CODE_AND_DST(inst, flags, reg) \
+ ((sljit_ins)(inst) | (sljit_ins)(((flags) & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)))
+#else
+#define ARCH_32_64(a, b) b
+#define INST_CODE_AND_DST(inst, flags, reg) \
+ (((sljit_ins)(inst) & ~(sljit_ins)INT_ALIGNED) | (sljit_ins)(((flags) & MEM_MASK) <= GPR_REG ? D(reg) : FD(reg)))
+#endif
+
+static const sljit_ins data_transfer_insts[64 + 16] = {
+
+/* -------- Integer -------- */
+
+/* Word. */
+
+/* w u i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */),
+/* w u i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */),
+/* w u x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */),
+/* w u x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */),
+
+/* w s i s */ ARCH_32_64(HI(36) /* stw */, HI(62) | INT_ALIGNED | 0x0 /* std */),
+/* w s i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x0 /* ld */),
+/* w s x s */ ARCH_32_64(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */),
+/* w s x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */),
+
+/* Byte. */
+
+/* b u i s */ HI(38) /* stb */,
+/* b u i l */ HI(34) /* lbz */,
+/* b u x s */ HI(31) | LO(215) /* stbx */,
+/* b u x l */ HI(31) | LO(87) /* lbzx */,
+
+/* b s i s */ HI(38) /* stb */,
+/* b s i l */ HI(34) /* lbz */ /* EXTS_REQ */,
+/* b s x s */ HI(31) | LO(215) /* stbx */,
+/* b s x l */ HI(31) | LO(87) /* lbzx */ /* EXTS_REQ */,
+
+/* Half. */
+
+/* h u i s */ HI(44) /* sth */,
+/* h u i l */ HI(40) /* lhz */,
+/* h u x s */ HI(31) | LO(407) /* sthx */,
+/* h u x l */ HI(31) | LO(279) /* lhzx */,
+
+/* h s i s */ HI(44) /* sth */,
+/* h s i l */ HI(42) /* lha */,
+/* h s x s */ HI(31) | LO(407) /* sthx */,
+/* h s x l */ HI(31) | LO(343) /* lhax */,
+
+/* Int. */
+
+/* i u i s */ HI(36) /* stw */,
+/* i u i l */ HI(32) /* lwz */,
+/* i u x s */ HI(31) | LO(151) /* stwx */,
+/* i u x l */ HI(31) | LO(23) /* lwzx */,
+
+/* i s i s */ HI(36) /* stw */,
+/* i s i l */ ARCH_32_64(HI(32) /* lwz */, HI(58) | INT_ALIGNED | 0x2 /* lwa */),
+/* i s x s */ HI(31) | LO(151) /* stwx */,
+/* i s x l */ ARCH_32_64(HI(31) | LO(23) /* lwzx */, HI(31) | LO(341) /* lwax */),
+
+/* -------- Floating point -------- */
+
+/* d i s */ HI(54) /* stfd */,
+/* d i l */ HI(50) /* lfd */,
+/* d x s */ HI(31) | LO(727) /* stfdx */,
+/* d x l */ HI(31) | LO(599) /* lfdx */,
+
+/* s i s */ HI(52) /* stfs */,
+/* s i l */ HI(48) /* lfs */,
+/* s x s */ HI(31) | LO(663) /* stfsx */,
+/* s x l */ HI(31) | LO(535) /* lfsx */,
+};
+
+static const sljit_ins updated_data_transfer_insts[64] = {
+
+/* -------- Integer -------- */
+
+/* Word. */
+
+/* w u i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */),
+/* w u i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */),
+/* w u x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */),
+/* w u x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */),
+
+/* w s i s */ ARCH_32_64(HI(37) /* stwu */, HI(62) | INT_ALIGNED | 0x1 /* stdu */),
+/* w s i l */ ARCH_32_64(HI(33) /* lwzu */, HI(58) | INT_ALIGNED | 0x1 /* ldu */),
+/* w s x s */ ARCH_32_64(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */),
+/* w s x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */),
+
+/* Byte. */
+
+/* b u i s */ HI(39) /* stbu */,
+/* b u i l */ HI(35) /* lbzu */,
+/* b u x s */ HI(31) | LO(247) /* stbux */,
+/* b u x l */ HI(31) | LO(119) /* lbzux */,
+
+/* b s i s */ HI(39) /* stbu */,
+/* b s i l */ 0 /* no such instruction */,
+/* b s x s */ HI(31) | LO(247) /* stbux */,
+/* b s x l */ 0 /* no such instruction */,
+
+/* Half. */
+
+/* h u i s */ HI(45) /* sthu */,
+/* h u i l */ HI(41) /* lhzu */,
+/* h u x s */ HI(31) | LO(439) /* sthux */,
+/* h u x l */ HI(31) | LO(311) /* lhzux */,
+
+/* h s i s */ HI(45) /* sthu */,
+/* h s i l */ HI(43) /* lhau */,
+/* h s x s */ HI(31) | LO(439) /* sthux */,
+/* h s x l */ HI(31) | LO(375) /* lhaux */,
+
+/* Int. */
+
+/* i u i s */ HI(37) /* stwu */,
+/* i u i l */ HI(33) /* lwzu */,
+/* i u x s */ HI(31) | LO(183) /* stwux */,
+/* i u x l */ HI(31) | LO(55) /* lwzux */,
+
+/* i s i s */ HI(37) /* stwu */,
+/* i s i l */ ARCH_32_64(HI(33) /* lwzu */, 0 /* no such instruction */),
+/* i s x s */ HI(31) | LO(183) /* stwux */,
+/* i s x l */ ARCH_32_64(HI(31) | LO(55) /* lwzux */, HI(31) | LO(373) /* lwaux */),
+
+/* -------- Floating point -------- */
+
+/* d i s */ HI(55) /* stfdu */,
+/* d i l */ HI(51) /* lfdu */,
+/* d x s */ HI(31) | LO(759) /* stfdux */,
+/* d x l */ HI(31) | LO(631) /* lfdux */,
+
+/* s i s */ HI(53) /* stfsu */,
+/* s i l */ HI(49) /* lfsu */,
+/* s x s */ HI(31) | LO(695) /* stfsux */,
+/* s x l */ HI(31) | LO(567) /* lfsux */,
+};
+
+#undef ARCH_32_64
+
+/* Simple cases, (no caching is required). */
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 inp_flags, sljit_s32 reg,
+ sljit_s32 arg, sljit_sw argw, sljit_s32 tmp_reg)
+{
+ sljit_ins inst;
+ sljit_s32 offs_reg;
+
+ /* Should work when (arg & REG_MASK) == 0. */
+ SLJIT_ASSERT(A(0) == 0);
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+ offs_reg = OFFS_REG(arg);
+
+ if (argw != 0) {
+ FAIL_IF(push_inst(compiler, SLWI_W(argw) | S(OFFS_REG(arg)) | A(tmp_reg)));
+ offs_reg = tmp_reg;
+ }
+
+ inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ SLJIT_ASSERT(!(inst & INT_ALIGNED));
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg & REG_MASK) | B(offs_reg));
+ }
+
+ inst = data_transfer_insts[inp_flags & MEM_MASK];
+ arg &= REG_MASK;
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((inst & INT_ALIGNED) && (argw & 0x3) != 0) {
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+
+ inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg));
+ }
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ if (argw <= SIMM_MAX && argw >= SIMM_MIN)
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | IMM(argw));
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (argw <= 0x7fff7fffl && argw >= -0x80000000l) {
+#endif /* SLJIT_CONFIG_PPC_64 */
+ FAIL_IF(push_inst(compiler, ADDIS | D(tmp_reg) | A(arg) | IMM((argw + 0x8000) >> 16)));
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(tmp_reg) | IMM(argw));
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ }
+
+ FAIL_IF(load_immediate(compiler, tmp_reg, argw));
+
+ inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, inp_flags, reg) | A(arg) | B(tmp_reg));
+#endif /* SLJIT_CONFIG_PPC_64 */
+}
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 input_flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* arg1 goes to TMP_REG1 or src reg
+ arg2 goes to TMP_REG2, imm or src reg
+ result goes to TMP_REG2, so put result can use TMP_REG1. */
+ sljit_s32 dst_r = TMP_REG2;
+ sljit_s32 src1_r;
+ sljit_s32 src2_r;
+ sljit_s32 sugg_src2_r = TMP_REG2;
+ sljit_s32 flags = input_flags & (ALT_FORM1 | ALT_FORM2 | ALT_FORM3 | ALT_FORM4 | ALT_FORM5 | ALT_SIGN_EXT | ALT_SET_FLAGS);
+
+ /* Destination check. */
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ /* The REG_DEST is only used by SLJIT_MOV operations, although
+ * it is set for op2 operations with unset destination. */
+ flags |= REG_DEST;
+
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P)
+ sugg_src2_r = dst_r;
+ }
+
+ /* Source 1. */
+ if (FAST_IS_REG(src1)) {
+ src1_r = src1;
+ flags |= REG1_SOURCE;
+ }
+ else if (src1 & SLJIT_IMM) {
+ src1_r = TMP_ZERO;
+ if (src1w != 0) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
+ src1_r = TMP_REG1;
+ }
+ }
+ else {
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, TMP_REG1, src1, src1w, TMP_REG1));
+ src1_r = TMP_REG1;
+ }
+
+ /* Source 2. */
+ if (FAST_IS_REG(src2)) {
+ src2_r = src2;
+ flags |= REG2_SOURCE;
+
+ if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOV_P)
+ dst_r = src2_r;
+ }
+ else if (src2 & SLJIT_IMM) {
+ src2_r = TMP_ZERO;
+ if (src2w != 0) {
+ FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w));
+ src2_r = sugg_src2_r;
+ }
+ }
+ else {
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, sugg_src2_r, src2, src2w, TMP_REG2));
+ src2_r = sugg_src2_r;
+ }
+
+ FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ return emit_op_mem(compiler, input_flags, dst_r, dst, dstw, TMP_REG1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ sljit_s32 int_op = op & SLJIT_32;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ case SLJIT_NOP:
+ return push_inst(compiler, NOP);
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0) | A(TMP_REG1) | B(SLJIT_R0)));
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ FAIL_IF(push_inst(compiler, MULLD | D(SLJIT_R0) | A(TMP_REG1) | B(SLJIT_R1)));
+ return push_inst(compiler, (op == SLJIT_LMUL_UW ? MULHDU : MULHD) | D(SLJIT_R1) | A(TMP_REG1) | B(SLJIT_R1));
+#else
+ FAIL_IF(push_inst(compiler, MULLW | D(SLJIT_R0) | A(TMP_REG1) | B(SLJIT_R1)));
+ return push_inst(compiler, (op == SLJIT_LMUL_UW ? MULHWU : MULHW) | D(SLJIT_R1) | A(TMP_REG1) | B(SLJIT_R1));
+#endif
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst(compiler, OR | S(SLJIT_R0) | A(TMP_REG1) | B(SLJIT_R0)));
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ FAIL_IF(push_inst(compiler, (int_op ? (op == SLJIT_DIVMOD_UW ? DIVWU : DIVW) : (op == SLJIT_DIVMOD_UW ? DIVDU : DIVD)) | D(SLJIT_R0) | A(SLJIT_R0) | B(SLJIT_R1)));
+ FAIL_IF(push_inst(compiler, (int_op ? MULLW : MULLD) | D(SLJIT_R1) | A(SLJIT_R0) | B(SLJIT_R1)));
+#else
+ FAIL_IF(push_inst(compiler, (op == SLJIT_DIVMOD_UW ? DIVWU : DIVW) | D(SLJIT_R0) | A(SLJIT_R0) | B(SLJIT_R1)));
+ FAIL_IF(push_inst(compiler, MULLW | D(SLJIT_R1) | A(SLJIT_R0) | B(SLJIT_R1)));
+#endif
+ return push_inst(compiler, SUBF | D(SLJIT_R1) | A(SLJIT_R1) | B(TMP_REG1));
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ return push_inst(compiler, (int_op ? (op == SLJIT_DIV_UW ? DIVWU : DIVW) : (op == SLJIT_DIV_UW ? DIVDU : DIVD)) | D(SLJIT_R0) | A(SLJIT_R0) | B(SLJIT_R1));
+#else
+ return push_inst(compiler, (op == SLJIT_DIV_UW ? DIVWU : DIVW) | D(SLJIT_R0) | A(SLJIT_R0) | B(SLJIT_R1));
+#endif
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (!(src & OFFS_REG_MASK)) {
+ if (srcw == 0 && (src & REG_MASK))
+ return push_inst(compiler, DCBT | A(0) | B(src & REG_MASK));
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ /* Works with SLJIT_MEM0() case as well. */
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1));
+ }
+
+ srcw &= 0x3;
+
+ if (srcw == 0)
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(OFFS_REG(src)));
+
+ FAIL_IF(push_inst(compiler, SLWI_W(srcw) | S(OFFS_REG(src)) | A(TMP_REG1)));
+ return push_inst(compiler, DCBT | A(src & REG_MASK) | B(TMP_REG1));
+}
+
+#define EMIT_MOV(type, type_flags, type_cast) \
+ emit_op(compiler, (src & SLJIT_IMM) ? SLJIT_MOV : type, flags | (type_flags), dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? type_cast srcw : srcw)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 flags = HAS_FLAGS(op) ? ALT_SET_FLAGS : 0;
+ sljit_s32 op_flags = GET_ALL_FLAGS(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ op = GET_OPCODE(op);
+
+ if (GET_FLAG_TYPE(op_flags) == SLJIT_OVERFLOW)
+ FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
+
+ if (op < SLJIT_NOT && FAST_IS_REG(src) && src == dst) {
+ if (!TYPE_CAST_NEEDED(op))
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (op_flags & SLJIT_32) {
+ if (op < SLJIT_NOT) {
+ if (src & SLJIT_MEM) {
+ if (op == SLJIT_MOV_S32)
+ op = SLJIT_MOV_U32;
+ }
+ else if (src & SLJIT_IMM) {
+ if (op == SLJIT_MOV_U32)
+ op = SLJIT_MOV_S32;
+ }
+ }
+ else {
+ /* Most operations expect sign extended arguments. */
+ flags |= INT_DATA | SIGNED_DATA;
+ if (HAS_FLAGS(op_flags))
+ flags |= ALT_SIGN_EXT;
+ }
+ }
+#endif
+
+ switch (op) {
+ case SLJIT_MOV:
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ case SLJIT_MOV_P:
+ return emit_op(compiler, SLJIT_MOV, flags | WORD_DATA, dst, dstw, TMP_REG1, 0, src, srcw);
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ case SLJIT_MOV_U32:
+ return EMIT_MOV(SLJIT_MOV_U32, INT_DATA, (sljit_u32));
+
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ return EMIT_MOV(SLJIT_MOV_S32, INT_DATA | SIGNED_DATA, (sljit_s32));
+#endif
+
+ case SLJIT_MOV_U8:
+ return EMIT_MOV(SLJIT_MOV_U8, BYTE_DATA, (sljit_u8));
+
+ case SLJIT_MOV_S8:
+ return EMIT_MOV(SLJIT_MOV_S8, BYTE_DATA | SIGNED_DATA, (sljit_s8));
+
+ case SLJIT_MOV_U16:
+ return EMIT_MOV(SLJIT_MOV_U16, HALF_DATA, (sljit_u16));
+
+ case SLJIT_MOV_S16:
+ return EMIT_MOV(SLJIT_MOV_S16, HALF_DATA | SIGNED_DATA, (sljit_s16));
+
+ case SLJIT_NOT:
+ return emit_op(compiler, SLJIT_NOT, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ return emit_op(compiler, op, flags | (!(op_flags & SLJIT_32) ? 0 : ALT_FORM1), dst, dstw, TMP_REG1, 0, src, srcw);
+#else
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+#endif
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#undef EMIT_MOV
+
+#define TEST_SL_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && (srcw) <= SIMM_MAX && (srcw) >= SIMM_MIN)
+
+#define TEST_UL_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & ~0xffff))
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define TEST_SH_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & 0xffff) && (srcw) <= 0x7fffffffl && (srcw) >= -0x80000000l)
+#else
+#define TEST_SH_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & 0xffff))
+#endif
+
+#define TEST_UH_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & ~(sljit_sw)0xffff0000))
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define TEST_ADD_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && (srcw) <= 0x7fff7fffl && (srcw) >= -0x80000000l)
+#else
+#define TEST_ADD_IMM(src, srcw) \
+ ((src) & SLJIT_IMM)
+#endif
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define TEST_UI_IMM(src, srcw) \
+ (((src) & SLJIT_IMM) && !((srcw) & ~0xffffffff))
+#else
+#define TEST_UI_IMM(src, srcw) \
+ ((src) & SLJIT_IMM)
+#endif
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define TEST_ADD_FORM1(op) \
+ (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW \
+ || (op & (SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_32 | SLJIT_SET_Z | SLJIT_SET_CARRY))
+#define TEST_SUB_FORM2(op) \
+ ((GET_FLAG_TYPE(op) >= SLJIT_SIG_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) \
+ || (op & (SLJIT_32 | SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_32 | SLJIT_SET_Z))
+#define TEST_SUB_FORM3(op) \
+ (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW \
+ || (op & (SLJIT_32 | SLJIT_SET_Z)) == (SLJIT_32 | SLJIT_SET_Z))
+#else
+#define TEST_ADD_FORM1(op) \
+ (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
+#define TEST_SUB_FORM2(op) \
+ (GET_FLAG_TYPE(op) >= SLJIT_SIG_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL)
+#define TEST_SUB_FORM3(op) \
+ (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 flags = HAS_FLAGS(op) ? ALT_SET_FLAGS : 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (op & SLJIT_32) {
+ /* Most operations expect sign extended arguments. */
+ flags |= INT_DATA | SIGNED_DATA;
+ if (src1 & SLJIT_IMM)
+ src1w = (sljit_s32)(src1w);
+ if (src2 & SLJIT_IMM)
+ src2w = (sljit_s32)(src2w);
+ if (HAS_FLAGS(op))
+ flags |= ALT_SIGN_EXT;
+ }
+#endif
+ if (GET_FLAG_TYPE(op) == SLJIT_OVERFLOW)
+ FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+
+ if (TEST_ADD_FORM1(op))
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM1, dst, dstw, src1, src1w, src2, src2w);
+
+ if (!HAS_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ if (TEST_SH_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)(src2w >> 16) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SH_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)(src1w >> 16) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ /* Range between -1 and -32768 is covered above. */
+ if (TEST_ADD_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffffffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_ADD_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffffffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((op & (SLJIT_32 | SLJIT_SET_Z)) == (SLJIT_32 | SLJIT_SET_Z)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4 | ALT_FORM5, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4 | ALT_FORM5, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w);
+ }
+#endif
+ if (HAS_FLAGS(op)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+ return emit_op(compiler, SLJIT_ADD, flags | ((GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ return emit_op(compiler, SLJIT_ADDC, flags, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SUB:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+
+ if (GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_LESS_EQUAL) {
+ if (dst == TMP_REG2) {
+ if (TEST_UL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if ((src2 & SLJIT_IMM) && src2w >= 0 && src2w <= (SIMM_MAX + 1)) {
+ compiler->imm = (sljit_ins)src2w;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM1 | ALT_FORM3, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if (dst == TMP_REG2 && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if (TEST_SUB_FORM2(op)) {
+ if ((src2 & SLJIT_IMM) && src2w >= -SIMM_MAX && src2w <= SIMM_MAX) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2 | ALT_FORM3 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ if (TEST_SUB_FORM3(op))
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM3, dst, dstw, src1, src1w, src2, src2w);
+
+ if (TEST_SL_IMM(src2, -src2w)) {
+ compiler->imm = (sljit_ins)(-src2w) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | (!HAS_FLAGS(op) ? ALT_FORM2 : ALT_FORM3), dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+
+ if (TEST_SL_IMM(src1, src1w) && !(op & SLJIT_SET_Z)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_SUB, flags | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+
+ if (!HAS_FLAGS(op)) {
+ if (TEST_SH_IMM(src2, -src2w)) {
+ compiler->imm = (sljit_ins)((-src2w) >> 16) & 0xffff;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ /* Range between -1 and -32768 is covered above. */
+ if (TEST_ADD_IMM(src2, -src2w)) {
+ compiler->imm = (sljit_ins)-src2w;
+ return emit_op(compiler, SLJIT_ADD, flags | ALT_FORM2 | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ }
+
+ /* We know ALT_SIGN_EXT is set if it is an SLJIT_32 on 64 bit systems. */
+ return emit_op(compiler, SLJIT_SUB, flags | ((GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ return emit_op(compiler, SLJIT_SUBC, flags, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_MUL:
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (op & SLJIT_32)
+ flags |= ALT_FORM2;
+#endif
+ if (!HAS_FLAGS(op)) {
+ if (TEST_SL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w & 0xffff;
+ return emit_op(compiler, SLJIT_MUL, flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_SL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w & 0xffff;
+ return emit_op(compiler, SLJIT_MUL, flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+ else
+ FAIL_IF(push_inst(compiler, MTXER | S(TMP_ZERO)));
+ return emit_op(compiler, SLJIT_MUL, flags, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ /* Commutative unsigned operations. */
+ if (!HAS_FLAGS(op) || GET_OPCODE(op) == SLJIT_AND) {
+ if (TEST_UL_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_UL_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ if (TEST_UH_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)(src2w >> 16) & 0xffff;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_UH_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)(src1w >> 16) & 0xffff;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+ if (!HAS_FLAGS(op) && GET_OPCODE(op) != SLJIT_AND) {
+ /* Unlike or and xor, the and resets unwanted bits as well. */
+ if (TEST_UI_IMM(src2, src2w)) {
+ compiler->imm = (sljit_ins)src2w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ if (TEST_UI_IMM(src1, src1w)) {
+ compiler->imm = (sljit_ins)src1w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0);
+ }
+ }
+ return emit_op(compiler, GET_OPCODE(op), flags, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (op & SLJIT_32)
+ flags |= ALT_FORM2;
+#endif
+ if (src2 & SLJIT_IMM) {
+ compiler->imm = (sljit_ins)src2w;
+ return emit_op(compiler, GET_OPCODE(op), flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0);
+ }
+ return emit_op(compiler, GET_OPCODE(op), flags, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w);
+}
+
+#undef TEST_ADD_FORM1
+#undef TEST_SUB_FORM2
+#undef TEST_SUB_FORM3
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_right;
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+ sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_PPC_64 */
+ sljit_s32 inp_flags = WORD_DATA | LOAD_DATA;
+ sljit_sw bit_length = 32;
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_right = (GET_OPCODE(op) == SLJIT_LSHR || GET_OPCODE(op) == SLJIT_MLSHR);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_right ? SLJIT_ROTR : SLJIT_ROTL) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= bit_length - 1;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG2, src2, src2w, TMP_REG2));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG1, src1, src1w, TMP_REG1));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (!(op & SLJIT_32)) {
+ if (is_right) {
+ FAIL_IF(push_inst(compiler, SRDI(src2w) | S(src_dst) | A(src_dst)));
+ return push_inst(compiler, RLDIMI | S(src1) | A(src_dst) | RLDI_SH(64 - src2w) | RLDI_MB(0));
+ }
+
+ FAIL_IF(push_inst(compiler, SLDI(src2w) | S(src_dst) | A(src_dst)));
+ /* Computes SRDI(64 - src2w). */
+ FAIL_IF(push_inst(compiler, RLDICL | S(src1) | A(TMP_REG1) | RLDI_SH(src2w) | RLDI_MB(64 - src2w)));
+ return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1));
+ }
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ if (is_right) {
+ FAIL_IF(push_inst(compiler, SRWI(src2w) | S(src_dst) | A(src_dst)));
+ return push_inst(compiler, RLWIMI | S(src1) | A(src_dst) | RLWI_SH(32 - src2w) | RLWI_MBE(0, src2w - 1));
+ }
+
+ FAIL_IF(push_inst(compiler, SLWI(src2w) | S(src_dst) | A(src_dst)));
+ return push_inst(compiler, RLWIMI | S(src1) | A(src_dst) | RLWI_SH(src2w) | RLWI_MBE(32 - src2w, 31));
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if (!(op & SLJIT_32)) {
+ if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x3f));
+ src2 = TMP_REG2;
+ }
+
+ FAIL_IF(push_inst(compiler, (is_right ? SRD : SLD) | S(src_dst) | A(src_dst) | B(src2)));
+ FAIL_IF(push_inst(compiler, (is_right ? SLDI(1) : SRDI(1)) | S(src1) | A(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XORI | S(src2) | A(TMP_REG2) | 0x3f));
+ FAIL_IF(push_inst(compiler, (is_right ? SLD : SRD) | S(TMP_REG1) | A(TMP_REG1) | B(TMP_REG2)));
+ return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1));
+ }
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) {
+ FAIL_IF(push_inst(compiler, ANDI | S(src2) | A(TMP_REG2) | 0x1f));
+ src2 = TMP_REG2;
+ }
+
+ FAIL_IF(push_inst(compiler, (is_right ? SRW : SLW) | S(src_dst) | A(src_dst) | B(src2)));
+ FAIL_IF(push_inst(compiler, (is_right ? SLWI(1) : SRWI(1)) | S(src1) | A(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, XORI | S(src2) | A(TMP_REG2) | 0x1f));
+ FAIL_IF(push_inst(compiler, (is_right ? SLW : SRW) | S(TMP_REG1) | A(TMP_REG1) | B(TMP_REG2)));
+ return push_inst(compiler, OR | S(src_dst) | A(src_dst) | B(TMP_REG1));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, MTLR | S(src)));
+ else {
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw, TMP_REG2));
+ FAIL_IF(push_inst(compiler, MTLR | S(TMP_REG2)));
+ }
+
+ return push_inst(compiler, BLR);
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ return emit_prefetch(compiler, src, srcw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return freg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_ins*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_32) >> 6))
+#define SELECT_FOP(op, single, double) ((sljit_ins)((op & SLJIT_32) ? single : double))
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+#define FLOAT_TMP_MEM_OFFSET (6 * sizeof(sljit_sw))
+#else
+#define FLOAT_TMP_MEM_OFFSET (2 * sizeof(sljit_sw))
+
+#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN)
+#define FLOAT_TMP_MEM_OFFSET_LOW (2 * sizeof(sljit_sw))
+#define FLOAT_TMP_MEM_OFFSET_HI (3 * sizeof(sljit_sw))
+#else
+#define FLOAT_TMP_MEM_OFFSET_LOW (3 * sizeof(sljit_sw))
+#define FLOAT_TMP_MEM_OFFSET_HI (2 * sizeof(sljit_sw))
+#endif
+
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ if (src & SLJIT_MEM) {
+ /* We can ignore the temporary data store on the stack from caching point of view. */
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1));
+ src = TMP_FREG1;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ op = GET_OPCODE(op);
+ FAIL_IF(push_inst(compiler, (op == SLJIT_CONV_S32_FROM_F64 ? FCTIWZ : FCTIDZ) | FD(TMP_FREG1) | FB(src)));
+
+ if (op == SLJIT_CONV_SW_FROM_F64) {
+ if (FAST_IS_REG(dst)) {
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ return emit_op_mem(compiler, WORD_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1);
+ }
+ return emit_op_mem(compiler, DOUBLE_DATA, TMP_FREG1, dst, dstw, TMP_REG1);
+ }
+#else
+ FAIL_IF(push_inst(compiler, FCTIWZ | FD(TMP_FREG1) | FB(src)));
+#endif
+
+ if (FAST_IS_REG(dst)) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, FLOAT_TMP_MEM_OFFSET));
+ FAIL_IF(push_inst(compiler, STFIWX | FS(TMP_FREG1) | A(SLJIT_SP) | B(TMP_REG1)));
+ return emit_op_mem(compiler, INT_DATA | LOAD_DATA, dst, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1);
+ }
+
+ SLJIT_ASSERT(dst & SLJIT_MEM);
+
+ if (dst & OFFS_REG_MASK) {
+ dstw &= 0x3;
+ if (dstw) {
+ FAIL_IF(push_inst(compiler, SLWI_W(dstw) | S(OFFS_REG(dst)) | A(TMP_REG1)));
+ dstw = TMP_REG1;
+ }
+ else
+ dstw = OFFS_REG(dst);
+ }
+ else {
+ if ((dst & REG_MASK) && !dstw) {
+ dstw = dst & REG_MASK;
+ dst = 0;
+ }
+ else {
+ /* This works regardless we have SLJIT_MEM1 or SLJIT_MEM0. */
+ FAIL_IF(load_immediate(compiler, TMP_REG1, dstw));
+ dstw = TMP_REG1;
+ }
+ }
+
+ return push_inst(compiler, STFIWX | FS(TMP_FREG1) | A(dst & REG_MASK) | B(dstw));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_IMM) {
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw));
+ src = TMP_REG1;
+ }
+ else if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32) {
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, EXTSW | S(src) | A(TMP_REG1)));
+ else
+ FAIL_IF(emit_op_mem(compiler, INT_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ if (FAST_IS_REG(src)) {
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, src, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ }
+ else
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, src, srcw, TMP_REG1));
+
+ FAIL_IF(push_inst(compiler, FCFID | FD(dst_r) | FB(TMP_FREG1)));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
+ if (op & SLJIT_32)
+ return push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r));
+ return SLJIT_SUCCESS;
+
+#else
+
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ sljit_s32 invert_sign = 1;
+
+ if (src & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw ^ (sljit_sw)0x80000000));
+ src = TMP_REG1;
+ invert_sign = 0;
+ }
+ else if (!FAST_IS_REG(src)) {
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | SIGNED_DATA | LOAD_DATA, TMP_REG1, src, srcw, TMP_REG1));
+ src = TMP_REG1;
+ }
+
+ /* First, a special double floating point value is constructed: (2^53 + (input xor (2^31)))
+ The double precision format has exactly 53 bit precision, so the lower 32 bit represents
+ the lower 32 bit of such value. The result of xor 2^31 is the same as adding 0x80000000
+ to the input, which shifts it into the 0 - 0xffffffff range. To get the converted floating
+ point value, we need to subtract 2^53 + 2^31 from the constructed value. */
+ FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG2) | A(0) | 0x4330));
+ if (invert_sign)
+ FAIL_IF(push_inst(compiler, XORIS | S(src) | A(TMP_REG1) | 0x8000));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_HI, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, TMP_REG2));
+ FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG1) | A(0) | 0x8000));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG1, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET_LOW, TMP_REG2));
+ FAIL_IF(emit_op_mem(compiler, DOUBLE_DATA | LOAD_DATA, TMP_FREG2, SLJIT_MEM1(SLJIT_SP), FLOAT_TMP_MEM_OFFSET, TMP_REG1));
+
+ FAIL_IF(push_inst(compiler, FSUB | FD(dst_r) | FA(TMP_FREG1) | FB(TMP_FREG2)));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, TMP_REG1);
+ if (op & SLJIT_32)
+ return push_inst(compiler, FRSP | FD(dst_r) | FB(dst_r));
+ return SLJIT_SUCCESS;
+
+#endif
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, TMP_REG1));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, TMP_REG2));
+ src2 = TMP_FREG2;
+ }
+
+ FAIL_IF(push_inst(compiler, FCMPU | CRD(4) | FA(src1) | FB(src2)));
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ return push_inst(compiler, CROR | ((4 + 2) << 21) | ((4 + 2) << 16) | ((4 + 3) << 11));
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return push_inst(compiler, CROR | ((4 + 0) << 21) | ((4 + 0) << 16) | ((4 + 3) << 11));
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return push_inst(compiler, CROR | ((4 + 1) << 21) | ((4 + 1) << 16) | ((4 + 3) << 11));
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100) && !(DOUBLE_DATA & 0x4), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, TMP_REG1));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_CONV_F64_FROM_F32:
+ op ^= SLJIT_32;
+ if (op & SLJIT_32) {
+ FAIL_IF(push_inst(compiler, FRSP | FD(dst_r) | FB(src)));
+ break;
+ }
+ /* Fall through. */
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, FMR | FD(dst_r) | FB(src)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, FNEG | FD(dst_r) | FB(src)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, FABS | FD(dst_r) | FB(src)));
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op), dst_r, dst, dstw, TMP_REG1));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, TMP_REG1));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, TMP_REG2));
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FADDS, FADD) | FD(dst_r) | FA(src1) | FB(src2)));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FSUBS, FSUB) | FD(dst_r) | FA(src1) | FB(src2)));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FMULS, FMUL) | FD(dst_r) | FA(src1) | FC(src2) /* FMUL use FC as src2 */));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, SELECT_FOP(op, FDIVS, FDIV) | FD(dst_r) | FA(src1) | FB(src2)));
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ FAIL_IF(emit_op_mem(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, TMP_REG1));
+
+ return SLJIT_SUCCESS;
+}
+
+#undef SELECT_FOP
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, MFLR | D(dst));
+
+ /* Memory. */
+ FAIL_IF(push_inst(compiler, MFLR | D(TMP_REG2)));
+ return emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+static sljit_ins get_bo_bi_flags(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ switch (type) {
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB)
+ return (4 << 21) | (2 << 16);
+ /* fallthrough */
+
+ case SLJIT_EQUAL:
+ return (12 << 21) | (2 << 16);
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB)
+ return (12 << 21) | (2 << 16);
+ /* fallthrough */
+
+ case SLJIT_NOT_EQUAL:
+ return (4 << 21) | (2 << 16);
+
+ case SLJIT_LESS:
+ case SLJIT_SIG_LESS:
+ return (12 << 21) | (0 << 16);
+
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ return (4 << 21) | (0 << 16);
+
+ case SLJIT_GREATER:
+ case SLJIT_SIG_GREATER:
+ return (12 << 21) | (1 << 16);
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ return (4 << 21) | (1 << 16);
+
+ case SLJIT_OVERFLOW:
+ return (12 << 21) | (3 << 16);
+
+ case SLJIT_NOT_OVERFLOW:
+ return (4 << 21) | (3 << 16);
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ return (12 << 21) | ((4 + 0) << 16);
+
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return (4 << 21) | ((4 + 0) << 16);
+
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return (12 << 21) | ((4 + 1) << 16);
+
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return (4 << 21) | ((4 + 1) << 16);
+
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ return (12 << 21) | ((4 + 2) << 16);
+
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ return (4 << 21) | ((4 + 2) << 16);
+
+ case SLJIT_UNORDERED:
+ return (12 << 21) | ((4 + 3) << 16);
+
+ case SLJIT_ORDERED:
+ return (4 << 21) | ((4 + 3) << 16);
+
+ default:
+ SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL_REG_ARG);
+ return (20 << 21);
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+ sljit_ins bo_bi_flags;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ bo_bi_flags = get_bo_bi_flags(compiler, type & 0xff);
+ if (!bo_bi_flags)
+ return NULL;
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, (sljit_u32)type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ if (type == SLJIT_CARRY || type == SLJIT_NOT_CARRY)
+ PTR_FAIL_IF(push_inst(compiler, ADDE | RC(ALT_SET_FLAGS) | D(TMP_REG1) | A(TMP_ZERO) | B(TMP_ZERO)));
+
+ /* In PPC, we don't need to touch the arguments. */
+ if (type < SLJIT_JUMP)
+ jump->flags |= IS_COND;
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+ if (type >= SLJIT_CALL)
+ jump->flags |= IS_CALL;
+#endif
+
+ PTR_FAIL_IF(emit_const(compiler, TMP_CALL_REG, 0));
+ PTR_FAIL_IF(push_inst(compiler, MTCTR | S(TMP_CALL_REG)));
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, BCCTR | bo_bi_flags | (type >= SLJIT_FAST_CALL ? 1 : 0)));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL));
+#endif
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump = NULL;
+ sljit_s32 src_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (FAST_IS_REG(src)) {
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+ if (type >= SLJIT_CALL && src != TMP_CALL_REG) {
+ FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src)));
+ src_r = TMP_CALL_REG;
+ }
+ else
+ src_r = src;
+#else /* SLJIT_PASS_ENTRY_ADDR_TO_CALL */
+ src_r = src;
+#endif /* SLJIT_PASS_ENTRY_ADDR_TO_CALL */
+ } else if (src & SLJIT_IMM) {
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR);
+ jump->u.target = (sljit_uw)srcw;
+
+#if (defined SLJIT_PASS_ENTRY_ADDR_TO_CALL && SLJIT_PASS_ENTRY_ADDR_TO_CALL)
+ if (type >= SLJIT_CALL)
+ jump->flags |= IS_CALL;
+#endif /* SLJIT_PASS_ENTRY_ADDR_TO_CALL */
+
+ FAIL_IF(emit_const(compiler, TMP_CALL_REG, 0));
+ src_r = TMP_CALL_REG;
+ } else {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG));
+ src_r = TMP_CALL_REG;
+ }
+
+ FAIL_IF(push_inst(compiler, MTCTR | S(src_r)));
+ if (jump)
+ jump->addr = compiler->size;
+ return push_inst(compiler, BCCTR | (20 << 21) | (type >= SLJIT_FAST_CALL ? 1 : 0));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_CALL_REG, src, srcw, TMP_CALL_REG));
+ src = TMP_CALL_REG;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, OR | S(src) | A(TMP_CALL_REG) | B(src)));
+ src = TMP_CALL_REG;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP;
+ }
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ FAIL_IF(call_with_args(compiler, arg_types, &src));
+#endif
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 reg, invert;
+ sljit_u32 bit, from_xer;
+ sljit_s32 saved_op = op;
+ sljit_sw saved_dstw = dstw;
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ sljit_s32 input_flags = ((op & SLJIT_32) || op == SLJIT_MOV32) ? INT_DATA : WORD_DATA;
+#else
+ sljit_s32 input_flags = WORD_DATA;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ reg = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2;
+
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem(compiler, input_flags | LOAD_DATA, TMP_REG1, dst, dstw, TMP_REG1));
+
+ invert = 0;
+ bit = 0;
+ from_xer = 0;
+
+ switch (type) {
+ case SLJIT_LESS:
+ case SLJIT_SIG_LESS:
+ break;
+
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ invert = 1;
+ break;
+
+ case SLJIT_GREATER:
+ case SLJIT_SIG_GREATER:
+ bit = 1;
+ break;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ bit = 1;
+ invert = 1;
+ break;
+
+ case SLJIT_EQUAL:
+ bit = 2;
+ break;
+
+ case SLJIT_NOT_EQUAL:
+ bit = 2;
+ invert = 1;
+ break;
+
+ case SLJIT_OVERFLOW:
+ from_xer = 1;
+ bit = 1;
+ break;
+
+ case SLJIT_NOT_OVERFLOW:
+ from_xer = 1;
+ bit = 1;
+ invert = 1;
+ break;
+
+ case SLJIT_CARRY:
+ from_xer = 1;
+ bit = 2;
+ invert = (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB) != 0;
+ break;
+
+ case SLJIT_NOT_CARRY:
+ from_xer = 1;
+ bit = 2;
+ invert = (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_ADD) != 0;
+ break;
+
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ bit = 4 + 0;
+ break;
+
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ bit = 4 + 0;
+ invert = 1;
+ break;
+
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ bit = 4 + 1;
+ break;
+
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ bit = 4 + 1;
+ invert = 1;
+ break;
+
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ bit = 4 + 2;
+ break;
+
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ bit = 4 + 2;
+ invert = 1;
+ break;
+
+ case SLJIT_UNORDERED:
+ bit = 4 + 3;
+ break;
+
+ case SLJIT_ORDERED:
+ bit = 4 + 3;
+ invert = 1;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ break;
+ }
+
+ FAIL_IF(push_inst(compiler, (from_xer ? MFXER : MFCR) | D(reg)));
+ /* Simplified mnemonics: extrwi. */
+ FAIL_IF(push_inst(compiler, RLWINM | S(reg) | A(reg) | RLWI_SH(1 + bit) | RLWI_MBE(31, 31)));
+
+ if (invert)
+ FAIL_IF(push_inst(compiler, XORI | S(reg) | A(reg) | 0x1));
+
+ if (op < SLJIT_ADD) {
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+ return emit_op_mem(compiler, input_flags, reg, dst, dstw, TMP_REG1);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (dst & SLJIT_MEM)
+ return sljit_emit_op2(compiler, saved_op, dst, saved_dstw, TMP_REG1, 0, TMP_REG2, 0);
+ return sljit_emit_op2(compiler, saved_op, dst, 0, dst, 0, TMP_REG2, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);;
+}
+
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+
+#define EMIT_MEM_LOAD_IMM(inst, mem, memw) \
+ ((sljit_s16)(memw) > SIMM_MAX - SSIZE_OF(sw))
+
+#else /* !SLJIT_CONFIG_PPC_32 */
+
+#define EMIT_MEM_LOAD_IMM(inst, mem, memw) \
+ ((((inst) & INT_ALIGNED) && ((memw) & 0x3) != 0) \
+ || ((sljit_s16)(memw) > SIMM_MAX - SSIZE_OF(sw)) \
+ || ((memw) > 0x7fff7fffl || (memw) < -0x80000000l)) \
+
+#endif /* SLJIT_CONFIG_PPC_32 */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ inst = data_transfer_insts[WORD_DATA | ((type & SLJIT_MEM_STORE) ? 0 : LOAD_DATA)];
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ memw &= 0x3;
+
+ if (memw != 0) {
+ FAIL_IF(push_inst(compiler, SLWI_W(memw) | S(OFFS_REG(mem)) | A(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, ADD | D(TMP_REG1) | A(TMP_REG1) | B(mem & REG_MASK)));
+ } else
+ FAIL_IF(push_inst(compiler, ADD | D(TMP_REG1) | A(mem & REG_MASK) | B(OFFS_REG(mem))));
+
+ mem = TMP_REG1;
+ memw = 0;
+ } else {
+ if (EMIT_MEM_LOAD_IMM(inst, mem, memw)) {
+ if ((mem & REG_MASK) != 0) {
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_op2(compiler, SLJIT_ADD, TMP_REG1, 0, mem & REG_MASK, 0, SLJIT_IMM, memw));
+ } else
+ FAIL_IF(load_immediate(compiler, TMP_REG1, memw));
+
+ memw = 0;
+ mem = TMP_REG1;
+ } else if (memw > SIMM_MAX || memw < SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDIS | D(TMP_REG1) | A(mem & REG_MASK) | IMM((memw + 0x8000) >> 16)));
+
+ memw &= 0xffff;
+ mem = TMP_REG1;
+ } else {
+ memw &= 0xffff;
+ mem &= REG_MASK;
+ }
+ }
+
+ SLJIT_ASSERT((memw >= 0 && memw <= SIMM_MAX - SSIZE_OF(sw)) || (memw >= 0x8000 && memw <= 0xffff));
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ inst &= (sljit_ins)~INT_ALIGNED;
+#endif /* SLJIT_CONFIG_PPC_64 */
+
+ if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) {
+ FAIL_IF(push_inst(compiler, inst | D(REG_PAIR_SECOND(reg)) | A(mem) | IMM(memw + SSIZE_OF(sw))));
+ return push_inst(compiler, inst | D(REG_PAIR_FIRST(reg)) | A(mem) | IMM(memw));
+ }
+
+ FAIL_IF(push_inst(compiler, inst | D(REG_PAIR_FIRST(reg)) | A(mem) | IMM(memw)));
+ return push_inst(compiler, inst | D(REG_PAIR_SECOND(reg)) | A(mem) | IMM(memw + SSIZE_OF(sw)));
+}
+
+#undef EMIT_MEM_LOAD_IMM
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 mem_flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem_update(compiler, type, reg, mem, memw));
+
+ if (type & SLJIT_MEM_POST)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ switch (type & 0xff) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ mem_flags = WORD_DATA;
+ break;
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV32:
+ mem_flags = INT_DATA;
+ break;
+
+ case SLJIT_MOV_S32:
+ mem_flags = INT_DATA;
+
+ if (!(type & SLJIT_MEM_STORE) && !(type & SLJIT_32)) {
+ if (mem & OFFS_REG_MASK)
+ mem_flags |= SIGNED_DATA;
+ else
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ break;
+#endif
+
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ mem_flags = BYTE_DATA;
+ break;
+
+ case SLJIT_MOV_U16:
+ mem_flags = HALF_DATA;
+ break;
+
+ case SLJIT_MOV_S16:
+ mem_flags = HALF_DATA | SIGNED_DATA;
+ break;
+
+ default:
+ SLJIT_UNREACHABLE();
+ mem_flags = WORD_DATA;
+ break;
+ }
+
+ if (!(type & SLJIT_MEM_STORE))
+ mem_flags |= LOAD_DATA;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ inst = updated_data_transfer_insts[mem_flags | INDEXED];
+ FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, 0, reg) | A(mem & REG_MASK) | B(OFFS_REG(mem))));
+ }
+ else {
+ if (memw > SIMM_MAX || memw < SIMM_MIN)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ inst = updated_data_transfer_insts[mem_flags];
+
+#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64)
+ if ((inst & INT_ALIGNED) && (memw & 0x3) != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+#endif
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, INST_CODE_AND_DST(inst, 0, reg) | A(mem & REG_MASK) | IMM(memw)));
+ }
+
+ if ((mem_flags & LOAD_DATA) && (type & 0xff) == SLJIT_MOV_S8)
+ return push_inst(compiler, EXTSB | S(reg) | A(reg));
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fmem_update(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 freg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 mem_flags;
+ sljit_ins inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fmem_update(compiler, type, freg, mem, memw));
+
+ if (type & SLJIT_MEM_POST)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ if (memw != 0)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+ else {
+ if (memw > SIMM_MAX || memw < SIMM_MIN)
+ return SLJIT_ERR_UNSUPPORTED;
+ }
+
+ if (type & SLJIT_MEM_SUPP)
+ return SLJIT_SUCCESS;
+
+ mem_flags = FLOAT_DATA(type);
+
+ if (!(type & SLJIT_MEM_STORE))
+ mem_flags |= LOAD_DATA;
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ inst = updated_data_transfer_insts[mem_flags | INDEXED];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, DOUBLE_DATA, freg) | A(mem & REG_MASK) | B(OFFS_REG(mem)));
+ }
+
+ inst = updated_data_transfer_insts[mem_flags];
+ return push_inst(compiler, INST_CODE_AND_DST(inst, DOUBLE_DATA, freg) | A(mem & REG_MASK) | IMM(memw));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(emit_const(compiler, dst_r, init_value));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0));
+
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32)
+ PTR_FAIL_IF(emit_const(compiler, dst_r, 0));
+#else
+ PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r));
+ compiler->size += 4;
+#endif
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0));
+
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_32.c
new file mode 100644
index 0000000000..b38e6924c8
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_32.c
@@ -0,0 +1,73 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r)
+{
+ SLJIT_UNUSED_ARG(tmp_r);
+ SLJIT_ASSERT(dst_r != tmp_r);
+
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm));
+
+ if (imm & 0x800)
+ imm += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
+
+ if ((imm & 0xfff) == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins)
+{
+ if ((init_value & 0x800) != 0)
+ init_value += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff)));
+ return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins*)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ if ((new_target & 0x800) != 0)
+ new_target += 0x1000;
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
+
+ SLJIT_ASSERT((inst[0] & 0x7f) == LUI);
+ inst[0] = (inst[0] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff);
+ SLJIT_ASSERT((inst[1] & 0x707f) == ADDI || (inst[1] & 0x707f) == JALR);
+ inst[1] = (inst[1] & 0xfffff) | IMM_I(new_target);
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 5);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_64.c b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_64.c
new file mode 100644
index 0000000000..32cec7848d
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_64.c
@@ -0,0 +1,183 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+static sljit_s32 load_immediate(struct sljit_compiler *compiler, sljit_s32 dst_r, sljit_sw imm, sljit_s32 tmp_r)
+{
+ sljit_sw high;
+
+ SLJIT_ASSERT(dst_r != tmp_r);
+
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN)
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm));
+
+ if (imm <= 0x7fffffffl && imm >= S32_MIN) {
+ if (imm > S32_MAX) {
+ SLJIT_ASSERT((imm & 0x800) != 0);
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
+ return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
+ }
+
+ if ((imm & 0x800) != 0)
+ imm += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
+
+ if ((imm & 0xfff) == 0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
+ }
+
+ /* Trailing zeroes could be used to produce shifted immediates. */
+
+ if (imm <= 0x7ffffffffffl && imm >= -0x80000000000l) {
+ high = imm >> 12;
+
+ if (imm & 0x800)
+ high = ~high;
+
+ if (high > S32_MAX) {
+ SLJIT_ASSERT((high & 0x800) != 0);
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
+ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(high)));
+ } else {
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(high & ~0xfff)));
+
+ if ((high & 0xfff) != 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(high)));
+ }
+
+ FAIL_IF(push_inst(compiler, SLLI | RD(dst_r) | RS1(dst_r) | IMM_I(12)));
+
+ if ((imm & 0xfff) != 0)
+ return push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm));
+
+ return SLJIT_SUCCESS;
+ }
+
+ high = imm >> 32;
+ imm = (sljit_s32)imm;
+
+ if ((imm & 0x80000000l) != 0)
+ high = ~high;
+
+ if (high <= 0x7ffff && high >= -0x80000) {
+ FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high << 12)));
+ high = 0x1000;
+ } else {
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(tmp_r) | (sljit_ins)(high & ~0xfff)));
+ high &= 0xfff;
+ }
+
+ if (imm <= SIMM_MAX && imm >= SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(TMP_ZERO) | IMM_I(imm)));
+ imm = 0;
+ } else if (imm > S32_MAX) {
+ SLJIT_ASSERT((imm & 0x800) != 0);
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)0x80000000u));
+ imm = 0x1000 | (imm & 0xfff);
+ } else {
+ if ((imm & 0x800) != 0)
+ imm += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(dst_r) | (sljit_ins)(imm & ~0xfff)));
+ imm &= 0xfff;
+ }
+
+ if ((high & 0xfff) != 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(tmp_r) | RS1(tmp_r) | IMM_I(high)));
+
+ if (imm & 0x1000)
+ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)));
+ else if (imm != 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(dst_r) | RS1(dst_r) | IMM_I(imm)));
+
+ FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(tmp_r) | IMM_I((high & 0x1000) ? 20 : 32)));
+ return push_inst(compiler, XOR | RD(dst_r) | RS1(dst_r) | RS2(tmp_r));
+}
+
+static SLJIT_INLINE sljit_s32 emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw init_value, sljit_ins last_ins)
+{
+ sljit_sw high;
+
+ if ((init_value & 0x800) != 0)
+ init_value += 0x1000;
+
+ high = init_value >> 32;
+
+ if ((init_value & 0x80000000l) != 0)
+ high = ~high;
+
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ FAIL_IF(push_inst(compiler, LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff)));
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high)));
+ FAIL_IF(push_inst(compiler, LUI | RD(dst) | (sljit_ins)(init_value & ~0xfff)));
+ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(32)));
+ FAIL_IF(push_inst(compiler, XOR | RD(dst) | RS1(dst) | RS2(TMP_REG3)));
+ return push_inst(compiler, last_ins | RS1(dst) | IMM_I(init_value));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ sljit_ins *inst = (sljit_ins*)addr;
+ sljit_sw high;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ if ((new_target & 0x800) != 0)
+ new_target += 0x1000;
+
+ high = (sljit_sw)new_target >> 32;
+
+ if ((new_target & 0x80000000l) != 0)
+ high = ~high;
+
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 0);
+
+ SLJIT_ASSERT((inst[0] & 0x7f) == LUI);
+ inst[0] = (inst[0] & 0xfff) | (sljit_ins)(high & ~0xfff);
+ SLJIT_ASSERT((inst[1] & 0x707f) == ADDI);
+ inst[1] = (inst[1] & 0xfffff) | IMM_I(high);
+ SLJIT_ASSERT((inst[2] & 0x7f) == LUI);
+ inst[2] = (inst[2] & 0xfff) | (sljit_ins)((sljit_sw)new_target & ~0xfff);
+ SLJIT_ASSERT((inst[5] & 0x707f) == ADDI || (inst[5] & 0x707f) == JALR);
+ inst[5] = (inst[5] & 0xfffff) | IMM_I(new_target);
+ SLJIT_UPDATE_WX_FLAGS(inst, inst + 5, 1);
+
+ inst = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(inst, executable_offset);
+ SLJIT_CACHE_FLUSH(inst, inst + 5);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_common.c b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_common.c
new file mode 100644
index 0000000000..58a48c649c
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeRISCV_common.c
@@ -0,0 +1,2762 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ return "RISC-V-32" SLJIT_CPUINFO;
+#else /* !SLJIT_CONFIG_RISCV_32 */
+ return "RISC-V-64" SLJIT_CPUINFO;
+#endif /* SLJIT_CONFIG_RISCV_32 */
+}
+
+/* Length of an instruction word
+ Both for riscv-32 and riscv-64 */
+typedef sljit_u32 sljit_ins;
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+#define TMP_REG3 (SLJIT_NUMBER_OF_REGISTERS + 4)
+#define TMP_ZERO 0
+
+/* Flags are kept in volatile registers. */
+#define EQUAL_FLAG (SLJIT_NUMBER_OF_REGISTERS + 5)
+#define RETURN_ADDR_REG TMP_REG2
+#define OTHER_FLAG (SLJIT_NUMBER_OF_REGISTERS + 6)
+
+#define TMP_FREG1 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1)
+#define TMP_FREG2 (SLJIT_NUMBER_OF_FLOAT_REGISTERS + 2)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 7] = {
+ 0, 10, 11, 12, 13, 14, 15, 16, 17, 29, 30, 31, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 2, 6, 1, 7, 5, 28
+};
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 3] = {
+ 0, 10, 11, 12, 13, 14, 15, 16, 17, 2, 3, 4, 5, 6, 7, 28, 29, 30, 31, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 9, 8, 0, 1,
+};
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+#define RD(rd) ((sljit_ins)reg_map[rd] << 7)
+#define RS1(rs1) ((sljit_ins)reg_map[rs1] << 15)
+#define RS2(rs2) ((sljit_ins)reg_map[rs2] << 20)
+#define FRD(rd) ((sljit_ins)freg_map[rd] << 7)
+#define FRS1(rs1) ((sljit_ins)freg_map[rs1] << 15)
+#define FRS2(rs2) ((sljit_ins)freg_map[rs2] << 20)
+#define IMM_I(imm) ((sljit_ins)(imm) << 20)
+#define IMM_S(imm) ((((sljit_ins)(imm) & 0xfe0) << 20) | (((sljit_ins)(imm) & 0x1f) << 7))
+
+/* Represents funct(i) parts of the instructions. */
+#define OPC(o) ((sljit_ins)(o))
+#define F3(f) ((sljit_ins)(f) << 12)
+#define F12(f) ((sljit_ins)(f) << 20)
+#define F7(f) ((sljit_ins)(f) << 25)
+
+#define ADD (F7(0x0) | F3(0x0) | OPC(0x33))
+#define ADDI (F3(0x0) | OPC(0x13))
+#define AND (F7(0x0) | F3(0x7) | OPC(0x33))
+#define ANDI (F3(0x7) | OPC(0x13))
+#define AUIPC (OPC(0x17))
+#define BEQ (F3(0x0) | OPC(0x63))
+#define BNE (F3(0x1) | OPC(0x63))
+#define BLT (F3(0x4) | OPC(0x63))
+#define BGE (F3(0x5) | OPC(0x63))
+#define BLTU (F3(0x6) | OPC(0x63))
+#define BGEU (F3(0x7) | OPC(0x63))
+#define DIV (F7(0x1) | F3(0x4) | OPC(0x33))
+#define DIVU (F7(0x1) | F3(0x5) | OPC(0x33))
+#define EBREAK (F12(0x1) | F3(0x0) | OPC(0x73))
+#define FADD_S (F7(0x0) | F3(0x7) | OPC(0x53))
+#define FDIV_S (F7(0xc) | F3(0x7) | OPC(0x53))
+#define FEQ_S (F7(0x50) | F3(0x2) | OPC(0x53))
+#define FLD (F3(0x3) | OPC(0x7))
+#define FLE_S (F7(0x50) | F3(0x0) | OPC(0x53))
+#define FLT_S (F7(0x50) | F3(0x1) | OPC(0x53))
+#define FSD (F3(0x3) | OPC(0x27))
+/* These conversion opcodes are partly defined. */
+#define FCVT_S_D (F7(0x20) | OPC(0x53))
+#define FCVT_S_W (F7(0x68) | OPC(0x53))
+#define FCVT_W_S (F7(0x60) | F3(0x1) | OPC(0x53))
+#define FMUL_S (F7(0x8) | F3(0x7) | OPC(0x53))
+#define FSGNJ_S (F7(0x10) | F3(0x0) | OPC(0x53))
+#define FSGNJN_S (F7(0x10) | F3(0x1) | OPC(0x53))
+#define FSGNJX_S (F7(0x10) | F3(0x2) | OPC(0x53))
+#define FSUB_S (F7(0x4) | F3(0x7) | OPC(0x53))
+#define JAL (OPC(0x6f))
+#define JALR (F3(0x0) | OPC(0x67))
+#define LD (F3(0x3) | OPC(0x3))
+#define LUI (OPC(0x37))
+#define LW (F3(0x2) | OPC(0x3))
+#define MUL (F7(0x1) | F3(0x0) | OPC(0x33))
+#define MULH (F7(0x1) | F3(0x1) | OPC(0x33))
+#define MULHU (F7(0x1) | F3(0x3) | OPC(0x33))
+#define OR (F7(0x0) | F3(0x6) | OPC(0x33))
+#define ORI (F3(0x6) | OPC(0x13))
+#define REM (F7(0x1) | F3(0x6) | OPC(0x33))
+#define REMU (F7(0x1) | F3(0x7) | OPC(0x33))
+#define SD (F3(0x3) | OPC(0x23))
+#define SLL (F7(0x0) | F3(0x1) | OPC(0x33))
+#define SLLI (IMM_I(0x0) | F3(0x1) | OPC(0x13))
+#define SLT (F7(0x0) | F3(0x2) | OPC(0x33))
+#define SLTI (F3(0x2) | OPC(0x13))
+#define SLTU (F7(0x0) | F3(0x3) | OPC(0x33))
+#define SLTUI (F3(0x3) | OPC(0x13))
+#define SRL (F7(0x0) | F3(0x5) | OPC(0x33))
+#define SRLI (IMM_I(0x0) | F3(0x5) | OPC(0x13))
+#define SRA (F7(0x20) | F3(0x5) | OPC(0x33))
+#define SRAI (IMM_I(0x400) | F3(0x5) | OPC(0x13))
+#define SUB (F7(0x20) | F3(0x0) | OPC(0x33))
+#define SW (F3(0x2) | OPC(0x23))
+#define XOR (F7(0x0) | F3(0x4) | OPC(0x33))
+#define XORI (F3(0x4) | OPC(0x13))
+
+#define SIMM_MAX (0x7ff)
+#define SIMM_MIN (-0x800)
+#define BRANCH_MAX (0xfff)
+#define BRANCH_MIN (-0x1000)
+#define JUMP_MAX (0xfffff)
+#define JUMP_MIN (-0x100000)
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+#define S32_MAX (0x7ffff7ffl)
+#define S32_MIN (-0x80000000l)
+#define S44_MAX (0x7fffffff7ffl)
+#define S52_MAX (0x7ffffffffffffl)
+#endif
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins)
+{
+ sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ptr);
+ *ptr = ins;
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 push_imm_s_inst(struct sljit_compiler *compiler, sljit_ins ins, sljit_sw imm)
+{
+ return push_inst(compiler, ins | IMM_S(imm));
+}
+
+static SLJIT_INLINE sljit_ins* detect_jump_type(struct sljit_jump *jump, sljit_ins *code, sljit_sw executable_offset)
+{
+ sljit_sw diff;
+ sljit_uw target_addr;
+ sljit_ins *inst;
+
+ inst = (sljit_ins *)jump->addr;
+
+ if (jump->flags & SLJIT_REWRITABLE_JUMP)
+ goto exit;
+
+ if (jump->flags & JUMP_ADDR)
+ target_addr = jump->u.target;
+ else {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ target_addr = (sljit_uw)(code + jump->u.label->size) + (sljit_uw)executable_offset;
+ }
+
+ diff = (sljit_sw)target_addr - (sljit_sw)inst - executable_offset;
+
+ if (jump->flags & IS_COND) {
+ inst--;
+ diff += SSIZE_OF(ins);
+
+ if (diff >= BRANCH_MIN && diff <= BRANCH_MAX) {
+ jump->flags |= PATCH_B;
+ inst[0] = (inst[0] & 0x1fff07f) ^ 0x1000;
+ jump->addr = (sljit_uw)inst;
+ return inst;
+ }
+
+ inst++;
+ diff -= SSIZE_OF(ins);
+ }
+
+ if (diff >= JUMP_MIN && diff <= JUMP_MAX) {
+ if (jump->flags & IS_COND) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ inst[-1] -= (sljit_ins)(1 * sizeof(sljit_ins)) << 7;
+#else
+ inst[-1] -= (sljit_ins)(5 * sizeof(sljit_ins)) << 7;
+#endif
+ }
+
+ jump->flags |= PATCH_J;
+ return inst;
+ }
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (diff >= S32_MIN && diff <= S32_MAX) {
+ if (jump->flags & IS_COND)
+ inst[-1] -= (sljit_ins)(4 * sizeof(sljit_ins)) << 7;
+
+ jump->flags |= PATCH_REL32;
+ inst[1] = inst[0];
+ return inst + 1;
+ }
+
+ if (target_addr <= (sljit_uw)S32_MAX) {
+ if (jump->flags & IS_COND)
+ inst[-1] -= (sljit_ins)(4 * sizeof(sljit_ins)) << 7;
+
+ jump->flags |= PATCH_ABS32;
+ inst[1] = inst[0];
+ return inst + 1;
+ }
+
+ if (target_addr <= S44_MAX) {
+ if (jump->flags & IS_COND)
+ inst[-1] -= (sljit_ins)(2 * sizeof(sljit_ins)) << 7;
+
+ jump->flags |= PATCH_ABS44;
+ inst[3] = inst[0];
+ return inst + 3;
+ }
+
+ if (target_addr <= S52_MAX) {
+ if (jump->flags & IS_COND)
+ inst[-1] -= (sljit_ins)(1 * sizeof(sljit_ins)) << 7;
+
+ jump->flags |= PATCH_ABS52;
+ inst[4] = inst[0];
+ return inst + 4;
+ }
+#endif
+
+exit:
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ inst[1] = inst[0];
+ return inst + 1;
+#else
+ inst[5] = inst[0];
+ return inst + 5;
+#endif
+}
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+
+static SLJIT_INLINE sljit_sw put_label_get_length(struct sljit_put_label *put_label, sljit_uw max_label)
+{
+ if (max_label <= (sljit_uw)S32_MAX) {
+ put_label->flags = PATCH_ABS32;
+ return 1;
+ }
+
+ if (max_label <= S44_MAX) {
+ put_label->flags = PATCH_ABS44;
+ return 3;
+ }
+
+ if (max_label <= S52_MAX) {
+ put_label->flags = PATCH_ABS52;
+ return 4;
+ }
+
+ put_label->flags = 0;
+ return 5;
+}
+
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+static SLJIT_INLINE void load_addr_to_reg(void *dst, sljit_u32 reg)
+{
+ struct sljit_jump *jump = NULL;
+ struct sljit_put_label *put_label;
+ sljit_uw flags;
+ sljit_ins *inst;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_sw high;
+#endif
+ sljit_uw addr;
+
+ if (reg != 0) {
+ jump = (struct sljit_jump*)dst;
+ flags = jump->flags;
+ inst = (sljit_ins*)jump->addr;
+ addr = (flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ } else {
+ put_label = (struct sljit_put_label*)dst;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ flags = put_label->flags;
+#endif
+ inst = (sljit_ins*)put_label->addr;
+ addr = put_label->label->addr;
+ reg = *inst;
+ }
+
+ if ((addr & 0x800) != 0)
+ addr += 0x1000;
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ inst[0] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff);
+#else /* !SLJIT_CONFIG_RISCV_32 */
+
+ if (flags & PATCH_ABS32) {
+ SLJIT_ASSERT(addr <= S32_MAX);
+ inst[0] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff);
+ } else if (flags & PATCH_ABS44) {
+ high = (sljit_sw)addr >> 12;
+ SLJIT_ASSERT((sljit_uw)high <= 0x7fffffff);
+
+ if (high > S32_MAX) {
+ SLJIT_ASSERT((high & 0x800) != 0);
+ inst[0] = LUI | RD(reg) | (sljit_ins)0x80000000u;
+ inst[1] = XORI | RD(reg) | RS1(reg) | IMM_I(high);
+ } else {
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ inst[0] = LUI | RD(reg) | (sljit_ins)(high & ~0xfff);
+ inst[1] = ADDI | RD(reg) | RS1(reg) | IMM_I(high);
+ }
+
+ inst[2] = SLLI | RD(reg) | RS1(reg) | IMM_I(12);
+ inst += 2;
+ } else {
+ high = (sljit_sw)addr >> 32;
+
+ if ((addr & 0x80000000l) != 0)
+ high = ~high;
+
+ if ((high & 0x800) != 0)
+ high += 0x1000;
+
+ if (flags & PATCH_ABS52) {
+ SLJIT_ASSERT(addr <= S52_MAX);
+ inst[0] = LUI | RD(TMP_REG3) | (sljit_ins)(high << 12);
+ } else {
+ inst[0] = LUI | RD(TMP_REG3) | (sljit_ins)(high & ~0xfff);
+ inst[1] = ADDI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I(high);
+ inst++;
+ }
+
+ inst[1] = LUI | RD(reg) | (sljit_ins)((sljit_sw)addr & ~0xfff);
+ inst[2] = SLLI | RD(TMP_REG3) | RS1(TMP_REG3) | IMM_I((flags & PATCH_ABS52) ? 20 : 32);
+ inst[3] = XOR | RD(reg) | RS1(reg) | RS2(TMP_REG3);
+ inst += 3;
+ }
+#endif /* !SLJIT_CONFIG_RISCV_32 */
+
+ if (jump != NULL) {
+ SLJIT_ASSERT((inst[1] & 0x707f) == JALR);
+ inst[1] = (inst[1] & 0xfffff) | IMM_I(addr);
+ } else
+ inst[1] = ADDI | RD(reg) | RS1(reg) | IMM_I(addr);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_ins *code;
+ sljit_ins *code_ptr;
+ sljit_ins *buf_ptr;
+ sljit_ins *buf_end;
+ sljit_uw word_count;
+ sljit_uw next_addr;
+ sljit_sw executable_offset;
+ sljit_uw addr;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins), compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ word_count = 0;
+ next_addr = 0;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+
+ do {
+ buf_ptr = (sljit_ins*)buf->memory;
+ buf_end = buf_ptr + (buf->used_size >> 2);
+ do {
+ *code_ptr = *buf_ptr++;
+ if (next_addr == word_count) {
+ SLJIT_ASSERT(!label || label->size >= word_count);
+ SLJIT_ASSERT(!jump || jump->addr >= word_count);
+ SLJIT_ASSERT(!const_ || const_->addr >= word_count);
+ SLJIT_ASSERT(!put_label || put_label->addr >= word_count);
+
+ /* These structures are ordered by their address. */
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+ if (jump && jump->addr == word_count) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ word_count += 1;
+#else
+ word_count += 5;
+#endif
+ jump->addr = (sljit_uw)code_ptr;
+ code_ptr = detect_jump_type(jump, code, executable_offset);
+ jump = jump->next;
+ }
+ if (const_ && const_->addr == word_count) {
+ const_->addr = (sljit_uw)code_ptr;
+ const_ = const_->next;
+ }
+ if (put_label && put_label->addr == word_count) {
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ code_ptr += 1;
+ word_count += 1;
+#else
+ code_ptr += put_label_get_length(put_label, (sljit_uw)(SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size));
+ word_count += 5;
+#endif
+ put_label = put_label->next;
+ }
+ next_addr = compute_next_addr(label, jump, const_, put_label);
+ }
+ code_ptr++;
+ word_count++;
+ } while (buf_ptr < buf_end);
+
+ buf = buf->next;
+ } while (buf);
+
+ if (label && label->size == word_count) {
+ label->addr = (sljit_uw)code_ptr;
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr - code <= (sljit_sw)compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ do {
+ if (!(jump->flags & (PATCH_B | PATCH_J | PATCH_REL32))) {
+ load_addr_to_reg(jump, TMP_REG1);
+ break;
+ }
+
+ addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target;
+ buf_ptr = (sljit_ins *)jump->addr;
+ addr -= (sljit_uw)SLJIT_ADD_EXEC_OFFSET(buf_ptr, executable_offset);
+
+ if (jump->flags & PATCH_B) {
+ SLJIT_ASSERT((sljit_sw)addr >= BRANCH_MIN && (sljit_sw)addr <= BRANCH_MAX);
+ addr = ((addr & 0x800) >> 4) | ((addr & 0x1e) << 7) | ((addr & 0x7e0) << 20) | ((addr & 0x1000) << 19);
+ buf_ptr[0] |= (sljit_ins)addr;
+ break;
+ }
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (jump->flags & PATCH_REL32) {
+ SLJIT_ASSERT((sljit_sw)addr >= S32_MIN && (sljit_sw)addr <= S32_MAX);
+
+ if ((addr & 0x800) != 0)
+ addr += 0x1000;
+
+ buf_ptr[0] = AUIPC | RD(TMP_REG1) | (sljit_ins)((sljit_sw)addr & ~0xfff);
+ SLJIT_ASSERT((buf_ptr[1] & 0x707f) == JALR);
+ buf_ptr[1] |= IMM_I(addr);
+ break;
+ }
+#endif
+
+ SLJIT_ASSERT((sljit_sw)addr >= JUMP_MIN && (sljit_sw)addr <= JUMP_MAX);
+ addr = (addr & 0xff000) | ((addr & 0x800) << 9) | ((addr & 0x7fe) << 20) | ((addr & 0x100000) << 11);
+ buf_ptr[0] = JAL | RD((jump->flags & IS_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | (sljit_ins)addr;
+ } while (0);
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+ load_addr_to_reg(put_label, 0);
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code) * sizeof(sljit_ins);
+
+ code = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = (sljit_ins *)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+ case SLJIT_HAS_ZERO_REGISTER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ return (type >= SLJIT_ORDERED_EQUAL && type <= SLJIT_ORDERED_LESS_EQUAL);
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+/* Creates an index in data_transfer_insts array. */
+#define LOAD_DATA 0x01
+#define WORD_DATA 0x00
+#define BYTE_DATA 0x02
+#define HALF_DATA 0x04
+#define INT_DATA 0x06
+#define SIGNED_DATA 0x08
+/* Separates integer and floating point registers */
+#define GPR_REG 0x0f
+#define DOUBLE_DATA 0x10
+#define SINGLE_DATA 0x12
+
+#define MEM_MASK 0x1f
+
+#define ARG_TEST 0x00020
+#define ALT_KEEP_CACHE 0x00040
+#define CUMULATIVE_OP 0x00080
+#define IMM_OP 0x00100
+#define MOVE_OP 0x00200
+#define SRC2_IMM 0x00400
+
+#define UNUSED_DEST 0x00800
+#define REG_DEST 0x01000
+#define REG1_SOURCE 0x02000
+#define REG2_SOURCE 0x04000
+#define SLOW_SRC1 0x08000
+#define SLOW_SRC2 0x10000
+#define SLOW_DEST 0x20000
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#define STACK_STORE SW
+#define STACK_LOAD LW
+#else
+#define STACK_STORE SD
+#define STACK_LOAD LD
+#endif
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#include "sljitNativeRISCV_32.c"
+#else
+#include "sljitNativeRISCV_64.c"
+#endif
+
+#define STACK_MAX_DISTANCE (-SIMM_MIN)
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw);
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 i, tmp, offset;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((local_size & SSIZE_OF(sw)) != 0)
+ local_size += SSIZE_OF(sw);
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+#else
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+#endif
+ local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+ compiler->local_size = local_size;
+
+ if (local_size <= STACK_MAX_DISTANCE) {
+ /* Frequent case. */
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-local_size)));
+ offset = local_size - SSIZE_OF(sw);
+ local_size = 0;
+ } else {
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(STACK_MAX_DISTANCE)));
+ local_size -= STACK_MAX_DISTANCE;
+
+ if (local_size > STACK_MAX_DISTANCE)
+ FAIL_IF(load_immediate(compiler, TMP_REG1, local_size, TMP_REG3));
+ offset = STACK_MAX_DISTANCE - SSIZE_OF(sw);
+ }
+
+ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(RETURN_ADDR_REG), offset));
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(i), offset));
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_imm_s_inst(compiler, STACK_STORE | RS1(SLJIT_SP) | RS2(i), offset));
+ }
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ /* This alignment is valid because offset is not used after storing FPU regs. */
+ if ((offset & SSIZE_OF(sw)) != 0)
+ offset -= SSIZE_OF(sw);
+#endif
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_imm_s_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(i), offset));
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_imm_s_inst(compiler, FSD | RS1(SLJIT_SP) | FRS2(i), offset));
+ }
+
+ if (local_size > STACK_MAX_DISTANCE)
+ FAIL_IF(push_inst(compiler, SUB | RD(SLJIT_SP) | RS1(SLJIT_SP) | RS2(TMP_REG1)));
+ else if (local_size > 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(-local_size)));
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ return SLJIT_SUCCESS;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ saved_arg_count = 0;
+ tmp = SLJIT_R0;
+
+ while (arg_types > 0) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_S0 - saved_arg_count) | RS1(tmp) | IMM_I(0)));
+ saved_arg_count++;
+ }
+ tmp++;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#undef STACK_MAX_DISTANCE
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ local_size += GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ if (fsaveds > 0 || fscratches >= SLJIT_FIRST_SAVED_FLOAT_REG) {
+ if ((local_size & SSIZE_OF(sw)) != 0)
+ local_size += SSIZE_OF(sw);
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+ }
+#else
+ local_size += GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, sizeof(sljit_f64));
+#endif
+ compiler->local_size = (local_size + SLJIT_LOCALS_OFFSET + 15) & ~0xf;
+
+ return SLJIT_SUCCESS;
+}
+
+#define STACK_MAX_DISTANCE (-SIMM_MIN - 16)
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_s32 i, tmp, offset;
+ sljit_s32 local_size = compiler->local_size;
+
+ if (local_size > STACK_MAX_DISTANCE) {
+ local_size -= STACK_MAX_DISTANCE;
+
+ if (local_size > STACK_MAX_DISTANCE) {
+ FAIL_IF(load_immediate(compiler, TMP_REG2, local_size, TMP_REG3));
+ FAIL_IF(push_inst(compiler, ADD | RD(SLJIT_SP) | RS1(SLJIT_SP) | RS2(TMP_REG2)));
+ } else
+ FAIL_IF(push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(local_size)));
+
+ local_size = STACK_MAX_DISTANCE;
+ }
+
+ SLJIT_ASSERT(local_size > 0);
+
+ offset = local_size - SSIZE_OF(sw);
+ if (!is_return_to)
+ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(RETURN_ADDR_REG) | RS1(SLJIT_SP) | IMM_I(offset)));
+
+ tmp = SLJIT_S0 - compiler->saveds;
+ for (i = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options); i > tmp; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(i) | RS1(SLJIT_SP) | IMM_I(offset)));
+ }
+
+ for (i = compiler->scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ offset -= SSIZE_OF(sw);
+ FAIL_IF(push_inst(compiler, STACK_LOAD | RD(i) | RS1(SLJIT_SP) | IMM_I(offset)));
+ }
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ /* This alignment is valid because offset is not used after storing FPU regs. */
+ if ((offset & SSIZE_OF(sw)) != 0)
+ offset -= SSIZE_OF(sw);
+#endif
+
+ tmp = SLJIT_FS0 - compiler->fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, FLD | FRD(i) | RS1(SLJIT_SP) | IMM_I(offset)));
+ }
+
+ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(push_inst(compiler, FLD | FRD(i) | RS1(SLJIT_SP) | IMM_I(offset)));
+ }
+
+ return push_inst(compiler, ADDI | RD(SLJIT_SP) | RS1(SLJIT_SP) | IMM_I(local_size));
+}
+
+#undef STACK_MAX_DISTANCE
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ return push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(RETURN_ADDR_REG) | IMM_I(0));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw));
+ src = TMP_REG1;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(src) | IMM_I(0)));
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#define ARCH_32_64(a, b) a
+#else
+#define ARCH_32_64(a, b) b
+#endif
+
+static const sljit_ins data_transfer_insts[16 + 4] = {
+/* u w s */ ARCH_32_64(F3(0x2) | OPC(0x23) /* sw */, F3(0x3) | OPC(0x23) /* sd */),
+/* u w l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x3) | OPC(0x3) /* ld */),
+/* u b s */ F3(0x0) | OPC(0x23) /* sb */,
+/* u b l */ F3(0x4) | OPC(0x3) /* lbu */,
+/* u h s */ F3(0x1) | OPC(0x23) /* sh */,
+/* u h l */ F3(0x5) | OPC(0x3) /* lhu */,
+/* u i s */ F3(0x2) | OPC(0x23) /* sw */,
+/* u i l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x6) | OPC(0x3) /* lwu */),
+
+/* s w s */ ARCH_32_64(F3(0x2) | OPC(0x23) /* sw */, F3(0x3) | OPC(0x23) /* sd */),
+/* s w l */ ARCH_32_64(F3(0x2) | OPC(0x3) /* lw */, F3(0x3) | OPC(0x3) /* ld */),
+/* s b s */ F3(0x0) | OPC(0x23) /* sb */,
+/* s b l */ F3(0x0) | OPC(0x3) /* lb */,
+/* s h s */ F3(0x1) | OPC(0x23) /* sh */,
+/* s h l */ F3(0x1) | OPC(0x3) /* lh */,
+/* s i s */ F3(0x2) | OPC(0x23) /* sw */,
+/* s i l */ F3(0x2) | OPC(0x3) /* lw */,
+
+/* d s */ F3(0x3) | OPC(0x27) /* fsd */,
+/* d l */ F3(0x3) | OPC(0x7) /* fld */,
+/* s s */ F3(0x2) | OPC(0x27) /* fsw */,
+/* s l */ F3(0x2) | OPC(0x7) /* flw */,
+};
+
+#undef ARCH_32_64
+
+static sljit_s32 push_mem_inst(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 base, sljit_sw offset)
+{
+ sljit_ins ins;
+
+ SLJIT_ASSERT(FAST_IS_REG(base) && offset <= 0xfff && offset >= SIMM_MIN);
+
+ ins = data_transfer_insts[flags & MEM_MASK] | RS1(base);
+ if (flags & LOAD_DATA)
+ ins |= ((flags & MEM_MASK) <= GPR_REG ? RD(reg) : FRD(reg)) | IMM_I(offset);
+ else
+ ins |= ((flags & MEM_MASK) <= GPR_REG ? RS2(reg) : FRS2(reg)) | IMM_S(offset);
+
+ return push_inst(compiler, ins);
+}
+
+/* Can perform an operation using at most 1 instruction. */
+static sljit_s32 getput_arg_fast(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+
+ if (!(arg & OFFS_REG_MASK) && argw <= SIMM_MAX && argw >= SIMM_MIN) {
+ /* Works for both absoulte and relative addresses. */
+ if (SLJIT_UNLIKELY(flags & ARG_TEST))
+ return 1;
+
+ FAIL_IF(push_mem_inst(compiler, flags, reg, arg & REG_MASK, argw));
+ return -1;
+ }
+ return 0;
+}
+
+#define TO_ARGW_HI(argw) (((argw) & ~0xfff) + (((argw) & 0x800) ? 0x1000 : 0))
+
+/* See getput_arg below.
+ Note: can_cache is called only for binary operators. */
+static sljit_s32 can_cache(sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
+{
+ SLJIT_ASSERT((arg & SLJIT_MEM) && (next_arg & SLJIT_MEM));
+
+ /* Simple operation except for updates. */
+ if (arg & OFFS_REG_MASK) {
+ argw &= 0x3;
+ next_argw &= 0x3;
+ if (argw && argw == next_argw && (arg == next_arg || (arg & OFFS_REG_MASK) == (next_arg & OFFS_REG_MASK)))
+ return 1;
+ return 0;
+ }
+
+ if (arg == next_arg) {
+ if (((next_argw - argw) <= SIMM_MAX && (next_argw - argw) >= SIMM_MIN)
+ || TO_ARGW_HI(argw) == TO_ARGW_HI(next_argw))
+ return 1;
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Emit the necessary instructions. See can_cache above. */
+static sljit_s32 getput_arg(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw, sljit_s32 next_arg, sljit_sw next_argw)
+{
+ sljit_s32 base = arg & REG_MASK;
+ sljit_s32 tmp_r = TMP_REG1;
+ sljit_sw offset, argw_hi;
+
+ SLJIT_ASSERT(arg & SLJIT_MEM);
+ if (!(next_arg & SLJIT_MEM)) {
+ next_arg = 0;
+ next_argw = 0;
+ }
+
+ /* Since tmp can be the same as base or offset registers,
+ * these might be unavailable after modifying tmp. */
+ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA))
+ tmp_r = reg;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ /* Using the cache. */
+ if (argw == compiler->cache_argw) {
+ if (arg == compiler->cache_arg)
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, 0);
+
+ if ((SLJIT_MEM | (arg & OFFS_REG_MASK)) == compiler->cache_arg) {
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(TMP_REG3) | RS2(base)));
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, 0);
+ }
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(TMP_REG3)));
+ return push_mem_inst(compiler, flags, reg, tmp_r, 0);
+ }
+ }
+
+ if (SLJIT_UNLIKELY(argw)) {
+ compiler->cache_arg = SLJIT_MEM | (arg & OFFS_REG_MASK);
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG3) | RS1(OFFS_REG(arg)) | IMM_I(argw)));
+ }
+
+ if (arg == next_arg && argw == (next_argw & 0x3)) {
+ compiler->cache_arg = arg;
+ compiler->cache_argw = argw;
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(base) | RS2(!argw ? OFFS_REG(arg) : TMP_REG3)));
+ tmp_r = TMP_REG3;
+ }
+ else
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(!argw ? OFFS_REG(arg) : TMP_REG3)));
+ return push_mem_inst(compiler, flags, reg, tmp_r, 0);
+ }
+
+ if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN)
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, argw - compiler->cache_argw);
+
+ if (compiler->cache_arg == SLJIT_MEM && (argw - compiler->cache_argw <= SIMM_MAX) && (argw - compiler->cache_argw >= SIMM_MIN)) {
+ offset = argw - compiler->cache_argw;
+ } else {
+ compiler->cache_arg = SLJIT_MEM;
+
+ argw_hi = TO_ARGW_HI(argw);
+
+ if (next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN && argw_hi != TO_ARGW_HI(next_argw)) {
+ FAIL_IF(load_immediate(compiler, TMP_REG3, argw, tmp_r));
+ compiler->cache_argw = argw;
+ offset = 0;
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG3, argw_hi, tmp_r));
+ compiler->cache_argw = argw_hi;
+ offset = argw & 0xfff;
+ argw = argw_hi;
+ }
+ }
+
+ if (!base)
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, offset);
+
+ if (arg == next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN) {
+ compiler->cache_arg = arg;
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG3) | RS1(TMP_REG3) | RS2(base)));
+ return push_mem_inst(compiler, flags, reg, TMP_REG3, offset);
+ }
+
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(TMP_REG3) | RS2(base)));
+ return push_mem_inst(compiler, flags, reg, tmp_r, offset);
+}
+
+static sljit_s32 emit_op_mem(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg, sljit_sw argw)
+{
+ sljit_s32 base = arg & REG_MASK;
+ sljit_s32 tmp_r = TMP_REG1;
+
+ if (getput_arg_fast(compiler, flags, reg, arg, argw))
+ return compiler->error;
+
+ if ((flags & MEM_MASK) <= GPR_REG && (flags & LOAD_DATA))
+ tmp_r = reg;
+
+ if (SLJIT_UNLIKELY(arg & OFFS_REG_MASK)) {
+ argw &= 0x3;
+
+ if (SLJIT_UNLIKELY(argw)) {
+ FAIL_IF(push_inst(compiler, SLLI | RD(tmp_r) | RS1(OFFS_REG(arg)) | IMM_I(argw)));
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(tmp_r) | RS2(base)));
+ }
+ else
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(base) | RS2(OFFS_REG(arg))));
+
+ argw = 0;
+ } else {
+ FAIL_IF(load_immediate(compiler, tmp_r, TO_ARGW_HI(argw), TMP_REG3));
+
+ if (base != 0)
+ FAIL_IF(push_inst(compiler, ADD | RD(tmp_r) | RS1(tmp_r) | RS2(base)));
+ }
+
+ return push_mem_inst(compiler, flags, reg, tmp_r, argw & 0xfff);
+}
+
+static SLJIT_INLINE sljit_s32 emit_op_mem2(struct sljit_compiler *compiler, sljit_s32 flags, sljit_s32 reg, sljit_s32 arg1, sljit_sw arg1w, sljit_s32 arg2, sljit_sw arg2w)
+{
+ if (getput_arg_fast(compiler, flags, reg, arg1, arg1w))
+ return compiler->error;
+ return getput_arg(compiler, flags, reg, arg1, arg1w, arg2, arg2w);
+}
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#define WORD 0
+#define IMM_EXTEND(v) (IMM_I(v))
+#else /* !SLJIT_CONFIG_RISCV_32 */
+#define WORD word
+#define IMM_EXTEND(v) (IMM_I((op & SLJIT_32) ? (v) : (32 + (v))))
+#endif /* SLJIT_CONFIG_RISCV_32 */
+
+static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 dst, sljit_sw src)
+{
+ sljit_s32 is_clz = (GET_OPCODE(op) == SLJIT_CLZ);
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_ins word = (op & SLJIT_32) >> 5;
+ sljit_ins max = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_RISCV_64 */
+ sljit_ins max = 32;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ SLJIT_ASSERT(WORD == 0 || WORD == 0x8);
+
+ /* The OTHER_FLAG is the counter. */
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(OTHER_FLAG) | RS1(TMP_ZERO) | IMM_I(max)));
+
+ /* The TMP_REG2 is the next value. */
+ if (src != TMP_REG2)
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG2) | RS1(src) | IMM_I(0)));
+
+ FAIL_IF(push_inst(compiler, BEQ | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)((is_clz ? 4 : 5) * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20)));
+
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(OTHER_FLAG) | RS1(TMP_ZERO) | IMM_I(0)));
+ if (!is_clz) {
+ FAIL_IF(push_inst(compiler, ANDI | RD(TMP_REG1) | RS1(TMP_REG2) | IMM_I(1)));
+ FAIL_IF(push_inst(compiler, BNE | RS1(TMP_REG1) | RS2(TMP_ZERO) | ((sljit_ins)(2 * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20)));
+ } else
+ FAIL_IF(push_inst(compiler, BLT | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)(2 * SSIZE_OF(ins)) << 7) | ((sljit_ins)(8 * SSIZE_OF(ins)) << 20)));
+
+ /* The TMP_REG1 is the next shift. */
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG1) | RS1(TMP_ZERO) | IMM_I(max)));
+
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(TMP_REG2) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_I(1)));
+
+ FAIL_IF(push_inst(compiler, (is_clz ? SRL : SLL) | WORD | RD(TMP_REG2) | RS1(EQUAL_FLAG) | RS2(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, BNE | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)0xfe000e80 - ((2 * SSIZE_OF(ins)) << 7))));
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(TMP_REG2) | RS1(TMP_REG1) | IMM_I(-1)));
+ FAIL_IF(push_inst(compiler, (is_clz ? SRL : SLL) | WORD | RD(TMP_REG2) | RS1(EQUAL_FLAG) | RS2(TMP_REG2)));
+ FAIL_IF(push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(TMP_REG1)));
+ FAIL_IF(push_inst(compiler, BEQ | RS1(TMP_REG2) | RS2(TMP_ZERO) | ((sljit_ins)0xfe000e80 - ((5 * SSIZE_OF(ins)) << 7))));
+
+ return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(OTHER_FLAG) | IMM_I(0));
+}
+
+#define EMIT_LOGICAL(op_imm, op_reg) \
+ if (flags & SRC2_IMM) { \
+ if (op & SLJIT_SET_Z) \
+ FAIL_IF(push_inst(compiler, op_imm | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2))); \
+ if (!(flags & UNUSED_DEST)) \
+ FAIL_IF(push_inst(compiler, op_imm | RD(dst) | RS1(src1) | IMM_I(src2))); \
+ } \
+ else { \
+ if (op & SLJIT_SET_Z) \
+ FAIL_IF(push_inst(compiler, op_reg | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2))); \
+ if (!(flags & UNUSED_DEST)) \
+ FAIL_IF(push_inst(compiler, op_reg | RD(dst) | RS1(src1) | RS2(src2))); \
+ }
+
+#define EMIT_SHIFT(imm, reg) \
+ op_imm = (imm); \
+ op_reg = (reg);
+
+static SLJIT_INLINE sljit_s32 emit_single_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_s32 src1, sljit_sw src2)
+{
+ sljit_s32 is_overflow, is_carry, carry_src_r, is_handled;
+ sljit_ins op_imm, op_reg;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_ins word = (op & SLJIT_32) >> 5;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ SLJIT_ASSERT(WORD == 0 || WORD == 0x8);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if (dst != src2)
+ return push_inst(compiler, ADDI | RD(dst) | RS1(src2) | IMM_I(0));
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE))
+ return push_inst(compiler, ANDI | RD(dst) | RS1(src2) | IMM_I(0xff));
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S8:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(24)));
+ return push_inst(compiler, SRAI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(24));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_U16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(16)));
+ return push_inst(compiler, SRLI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(16));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S16:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ FAIL_IF(push_inst(compiler, SLLI | WORD | RD(dst) | RS1(src2) | IMM_EXTEND(16)));
+ return push_inst(compiler, SRAI | WORD | RD(dst) | RS1(dst) | IMM_EXTEND(16));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ case SLJIT_MOV_U32:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) {
+ FAIL_IF(push_inst(compiler, SLLI | RD(dst) | RS1(src2) | IMM_I(32)));
+ return push_inst(compiler, SRLI | RD(dst) | RS1(dst) | IMM_I(32));
+ }
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_MOV_S32:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE))
+ return push_inst(compiler, ADDI | 0x8 | RD(dst) | RS1(src2) | IMM_I(0));
+ SLJIT_ASSERT(dst == src2);
+ return SLJIT_SUCCESS;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM));
+ return emit_clz_ctz(compiler, op, dst, src2);
+
+ case SLJIT_ADD:
+ /* Overflow computation (both add and sub): overflow = src1_sign ^ src2_sign ^ result_sign ^ carry_flag */
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ carry_src_r = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_overflow) {
+ if (src2 >= 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0)));
+ else
+ FAIL_IF(push_inst(compiler, XORI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-1)));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2)));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(src2)));
+ }
+ else {
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADD | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+
+ if (is_overflow || carry_src_r != 0) {
+ if (src1 != dst)
+ carry_src_r = (sljit_s32)src1;
+ else if (src2 != dst)
+ carry_src_r = (sljit_s32)src2;
+ else {
+ FAIL_IF(push_inst(compiler, ADDI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(0)));
+ carry_src_r = OTHER_FLAG;
+ }
+ }
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(src1) | RS2(src2)));
+ }
+
+ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */
+ if (is_overflow || carry_src_r != 0) {
+ if (flags & SRC2_IMM)
+ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(src2)));
+ else
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(dst) | RS2(carry_src_r)));
+ }
+
+ if (!is_overflow)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, XOR | RD(TMP_REG1) | RS1(dst) | RS2(EQUAL_FLAG)));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_EXTEND(31)));
+ return push_inst(compiler, XOR | RD(OTHER_FLAG) | RS1(TMP_REG1) | RS2(OTHER_FLAG));
+
+ case SLJIT_ADDC:
+ carry_src_r = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(src2)));
+ } else {
+ if (carry_src_r != 0) {
+ if (src1 != dst)
+ carry_src_r = (sljit_s32)src1;
+ else if (src2 != dst)
+ carry_src_r = (sljit_s32)src2;
+ else {
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0)));
+ carry_src_r = EQUAL_FLAG;
+ }
+ }
+
+ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(src1) | RS2(src2)));
+ }
+
+ /* Carry is zero if a + b >= a or a + b >= b, otherwise it is 1. */
+ if (carry_src_r != 0) {
+ if (flags & SRC2_IMM)
+ FAIL_IF(push_inst(compiler, SLTUI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(src2)));
+ else
+ FAIL_IF(push_inst(compiler, SLTU | RD(EQUAL_FLAG) | RS1(dst) | RS2(carry_src_r)));
+ }
+
+ FAIL_IF(push_inst(compiler, ADD | WORD | RD(dst) | RS1(dst) | RS2(OTHER_FLAG)));
+
+ if (carry_src_r == 0)
+ return SLJIT_SUCCESS;
+
+ /* Set ULESS_FLAG (dst == 0) && (OTHER_FLAG == 1). */
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(dst) | RS2(OTHER_FLAG)));
+ /* Set carry flag. */
+ return push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(EQUAL_FLAG));
+
+ case SLJIT_SUB:
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ is_handled = 0;
+
+ if (flags & SRC2_IMM) {
+ if (GET_FLAG_TYPE(op) == SLJIT_LESS || GET_FLAG_TYPE(op) == SLJIT_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2)));
+ is_handled = 1;
+ }
+ else if (GET_FLAG_TYPE(op) == SLJIT_SIG_LESS || GET_FLAG_TYPE(op) == SLJIT_SIG_GREATER_EQUAL) {
+ FAIL_IF(push_inst(compiler, SLTI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2)));
+ is_handled = 1;
+ }
+ }
+
+ if (!is_handled && GET_FLAG_TYPE(op) >= SLJIT_LESS && GET_FLAG_TYPE(op) <= SLJIT_SIG_LESS_EQUAL) {
+ is_handled = 1;
+
+ if (flags & SRC2_IMM) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_LESS:
+ case SLJIT_GREATER_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+ break;
+ case SLJIT_GREATER:
+ case SLJIT_LESS_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src2) | RS2(src1)));
+ break;
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER_EQUAL:
+ FAIL_IF(push_inst(compiler, SLT | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+ break;
+ case SLJIT_SIG_GREATER:
+ case SLJIT_SIG_LESS_EQUAL:
+ FAIL_IF(push_inst(compiler, SLT | RD(OTHER_FLAG) | RS1(src2) | RS2(src1)));
+ break;
+ }
+ }
+
+ if (is_handled) {
+ if (flags & SRC2_IMM) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-src2)));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2));
+ }
+ else {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+ if (!(flags & UNUSED_DEST))
+ return push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2));
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ is_overflow = GET_FLAG_TYPE(op) == SLJIT_OVERFLOW;
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_overflow) {
+ if (src2 >= 0)
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(0)));
+ else
+ FAIL_IF(push_inst(compiler, XORI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-1)));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(-src2)));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTUI | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2)));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2)));
+ }
+ else {
+ if (is_overflow)
+ FAIL_IF(push_inst(compiler, XOR | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+
+ if (is_overflow || is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+
+ /* Only the zero flag is needed. */
+ if (!(flags & UNUSED_DEST) || (op & VARIABLE_FLAG_MASK))
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2)));
+ }
+
+ if (!is_overflow)
+ return SLJIT_SUCCESS;
+
+ FAIL_IF(push_inst(compiler, XOR | RD(TMP_REG1) | RS1(dst) | RS2(EQUAL_FLAG)));
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, ADDI | RD(EQUAL_FLAG) | RS1(dst) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, SRLI | WORD | RD(TMP_REG1) | RS1(TMP_REG1) | IMM_EXTEND(31)));
+ return push_inst(compiler, XOR | RD(OTHER_FLAG) | RS1(TMP_REG1) | RS2(OTHER_FLAG));
+
+ case SLJIT_SUBC:
+ if ((flags & SRC2_IMM) && src2 == SIMM_MIN) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG2) | RS1(TMP_ZERO) | IMM_I(src2)));
+ src2 = TMP_REG2;
+ flags &= ~SRC2_IMM;
+ }
+
+ is_carry = GET_FLAG_TYPE(op) == GET_FLAG_TYPE(SLJIT_SET_CARRY);
+
+ if (flags & SRC2_IMM) {
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTUI | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2)));
+
+ FAIL_IF(push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(-src2)));
+ }
+ else {
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(src1) | RS2(src2)));
+ }
+
+ if (is_carry)
+ FAIL_IF(push_inst(compiler, SLTU | RD(TMP_REG1) | RS1(dst) | RS2(OTHER_FLAG)));
+
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(dst) | RS1(dst) | RS2(OTHER_FLAG)));
+
+ if (!is_carry)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, OR | RD(OTHER_FLAG) | RS1(EQUAL_FLAG) | RS2(TMP_REG1));
+
+ case SLJIT_MUL:
+ SLJIT_ASSERT(!(flags & SRC2_IMM));
+
+ if (GET_FLAG_TYPE(op) != SLJIT_OVERFLOW)
+ return push_inst(compiler, MUL | WORD | RD(dst) | RS1(src1) | RS2(src2));
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (word) {
+ FAIL_IF(push_inst(compiler, MUL | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+ FAIL_IF(push_inst(compiler, MUL | 0x8 | RD(dst) | RS1(src1) | RS2(src2)));
+ return push_inst(compiler, SUB | RD(OTHER_FLAG) | RS1(dst) | RS2(OTHER_FLAG));
+ }
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ FAIL_IF(push_inst(compiler, MULH | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+ FAIL_IF(push_inst(compiler, MUL | RD(dst) | RS1(src1) | RS2(src2)));
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ FAIL_IF(push_inst(compiler, SRAI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(31)));
+#else /* !SLJIT_CONFIG_RISCV_32 */
+ FAIL_IF(push_inst(compiler, SRAI | RD(OTHER_FLAG) | RS1(dst) | IMM_I(63)));
+#endif /* SLJIT_CONFIG_RISCV_32 */
+ return push_inst(compiler, SUB | RD(OTHER_FLAG) | RS1(EQUAL_FLAG) | RS2(OTHER_FLAG));
+
+ case SLJIT_AND:
+ EMIT_LOGICAL(ANDI, AND);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_OR:
+ EMIT_LOGICAL(ORI, OR);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_XOR:
+ EMIT_LOGICAL(XORI, XOR);
+ return SLJIT_SUCCESS;
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ EMIT_SHIFT(SLLI, SLL);
+ break;
+
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ EMIT_SHIFT(SRLI, SRL);
+ break;
+
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ EMIT_SHIFT(SRAI, SRA);
+ break;
+
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (flags & SRC2_IMM) {
+ SLJIT_ASSERT(src2 != 0);
+
+ op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SLLI : SRLI;
+ FAIL_IF(push_inst(compiler, op_imm | WORD | RD(OTHER_FLAG) | RS1(src1) | IMM_I(src2)));
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ src2 = ((op & SLJIT_32) ? 32 : 64) - src2;
+#else /* !SLJIT_CONFIG_RISCV_64 */
+ src2 = 32 - src2;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+ op_imm = (GET_OPCODE(op) == SLJIT_ROTL) ? SRLI : SLLI;
+ FAIL_IF(push_inst(compiler, op_imm | WORD | RD(dst) | RS1(src1) | IMM_I(src2)));
+ return push_inst(compiler, OR | RD(dst) | RS1(dst) | RS2(OTHER_FLAG));
+ }
+
+ if (src2 == TMP_ZERO) {
+ if (dst != src1)
+ return push_inst(compiler, ADDI | WORD | RD(dst) | RS1(src1) | IMM_I(0));
+ return SLJIT_SUCCESS;
+ }
+
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(EQUAL_FLAG) | RS1(TMP_ZERO) | RS2(src2)));
+ op_reg = (GET_OPCODE(op) == SLJIT_ROTL) ? SLL : SRL;
+ FAIL_IF(push_inst(compiler, op_reg | WORD | RD(OTHER_FLAG) | RS1(src1) | RS2(src2)));
+ op_reg = (GET_OPCODE(op) == SLJIT_ROTL) ? SRL : SLL;
+ FAIL_IF(push_inst(compiler, op_reg | WORD | RD(dst) | RS1(src1) | RS2(EQUAL_FLAG)));
+ return push_inst(compiler, OR | RD(dst) | RS1(dst) | RS2(OTHER_FLAG));
+
+ default:
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+ }
+
+ if (flags & SRC2_IMM) {
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, op_imm | WORD | RD(EQUAL_FLAG) | RS1(src1) | IMM_I(src2)));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, op_imm | WORD | RD(dst) | RS1(src1) | IMM_I(src2));
+ }
+
+ if (op & SLJIT_SET_Z)
+ FAIL_IF(push_inst(compiler, op_reg | WORD | RD(EQUAL_FLAG) | RS1(src1) | RS2(src2)));
+
+ if (flags & UNUSED_DEST)
+ return SLJIT_SUCCESS;
+ return push_inst(compiler, op_reg | WORD | RD(dst) | RS1(src1) | RS2(src2));
+}
+
+#undef IMM_EXTEND
+
+static sljit_s32 emit_op(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* arg1 goes to TMP_REG1 or src reg
+ arg2 goes to TMP_REG2, imm or src reg
+ TMP_REG3 can be used for caching
+ result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */
+ sljit_s32 dst_r = TMP_REG2;
+ sljit_s32 src1_r;
+ sljit_sw src2_r = 0;
+ sljit_s32 sugg_src2_r = TMP_REG2;
+
+ if (!(flags & ALT_KEEP_CACHE)) {
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+ }
+
+ if (dst == TMP_REG2) {
+ SLJIT_ASSERT(HAS_FLAGS(op));
+ flags |= UNUSED_DEST;
+ }
+ else if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ flags |= REG_DEST;
+ if (flags & MOVE_OP)
+ sugg_src2_r = dst_r;
+ }
+ else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, TMP_REG1, dst, dstw))
+ flags |= SLOW_DEST;
+
+ if (flags & IMM_OP) {
+ if ((src2 & SLJIT_IMM) && src2w != 0 && src2w <= SIMM_MAX && src2w >= SIMM_MIN) {
+ flags |= SRC2_IMM;
+ src2_r = src2w;
+ }
+ else if ((flags & CUMULATIVE_OP) && (src1 & SLJIT_IMM) && src1w != 0 && src1w <= SIMM_MAX && src1w >= SIMM_MIN) {
+ flags |= SRC2_IMM;
+ src2_r = src1w;
+
+ /* And swap arguments. */
+ src1 = src2;
+ src1w = src2w;
+ src2 = SLJIT_IMM;
+ /* src2w = src2_r unneeded. */
+ }
+ }
+
+ /* Source 1. */
+ if (FAST_IS_REG(src1)) {
+ src1_r = src1;
+ flags |= REG1_SOURCE;
+ }
+ else if (src1 & SLJIT_IMM) {
+ if (src1w) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3));
+ src1_r = TMP_REG1;
+ }
+ else
+ src1_r = TMP_ZERO;
+ }
+ else {
+ if (getput_arg_fast(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w))
+ FAIL_IF(compiler->error);
+ else
+ flags |= SLOW_SRC1;
+ src1_r = TMP_REG1;
+ }
+
+ /* Source 2. */
+ if (FAST_IS_REG(src2)) {
+ src2_r = src2;
+ flags |= REG2_SOURCE;
+ if ((flags & (REG_DEST | MOVE_OP)) == MOVE_OP)
+ dst_r = (sljit_s32)src2_r;
+ }
+ else if (src2 & SLJIT_IMM) {
+ if (!(flags & SRC2_IMM)) {
+ if (src2w) {
+ FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w, TMP_REG3));
+ src2_r = sugg_src2_r;
+ }
+ else {
+ src2_r = TMP_ZERO;
+ if (flags & MOVE_OP) {
+ if (dst & SLJIT_MEM)
+ dst_r = 0;
+ else
+ op = SLJIT_MOV;
+ }
+ }
+ }
+ }
+ else {
+ if (getput_arg_fast(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w))
+ FAIL_IF(compiler->error);
+ else
+ flags |= SLOW_SRC2;
+ src2_r = sugg_src2_r;
+ }
+
+ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
+ SLJIT_ASSERT(src2_r == TMP_REG2);
+ if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
+ }
+ else {
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw));
+ }
+ }
+ else if (flags & SLOW_SRC1)
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw));
+ else if (flags & SLOW_SRC2)
+ FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw));
+
+ FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r));
+
+ if (dst & SLJIT_MEM) {
+ if (!(flags & SLOW_DEST)) {
+ getput_arg_fast(compiler, flags, dst_r, dst, dstw);
+ return compiler->error;
+ }
+ return getput_arg(compiler, flags, dst_r, dst, dstw, 0, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_ins word = (op & SLJIT_32) >> 5;
+
+ SLJIT_ASSERT(word == 0 || word == 0x8);
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_BREAKPOINT:
+ return push_inst(compiler, EBREAK);
+ case SLJIT_NOP:
+ return push_inst(compiler, ADDI | RD(TMP_ZERO) | RS1(TMP_ZERO) | IMM_I(0));
+ case SLJIT_LMUL_UW:
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R1) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, MULHU | RD(SLJIT_R1) | RS1(SLJIT_R0) | RS2(SLJIT_R1)));
+ return push_inst(compiler, MUL | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(TMP_REG1));
+ case SLJIT_LMUL_SW:
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R1) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, MULH | RD(SLJIT_R1) | RS1(SLJIT_R0) | RS2(SLJIT_R1)));
+ return push_inst(compiler, MUL | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(TMP_REG1));
+ case SLJIT_DIVMOD_UW:
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R0) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, DIVU | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1)));
+ return push_inst(compiler, REMU | WORD | RD(SLJIT_R1) | RS1(TMP_REG1) | RS2(SLJIT_R1));
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(SLJIT_R0) | IMM_I(0)));
+ FAIL_IF(push_inst(compiler, DIV | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1)));
+ return push_inst(compiler, REM | WORD | RD(SLJIT_R1) | RS1(TMP_REG1) | RS2(SLJIT_R1));
+ case SLJIT_DIV_UW:
+ return push_inst(compiler, DIVU | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1));
+ case SLJIT_DIV_SW:
+ return push_inst(compiler, DIV | WORD | RD(SLJIT_R0) | RS1(SLJIT_R0) | RS2(SLJIT_R1));
+ case SLJIT_ENDBR:
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (op & SLJIT_32)
+ flags = INT_DATA | SIGNED_DATA;
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV:
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ case SLJIT_MOV_P:
+ return emit_op(compiler, SLJIT_MOV, WORD_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, srcw);
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ case SLJIT_MOV_U32:
+ return emit_op(compiler, SLJIT_MOV_U32, INT_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u32)srcw : srcw);
+
+ case SLJIT_MOV_S32:
+ /* Logical operators have no W variant, so sign extended input is necessary for them. */
+ case SLJIT_MOV32:
+ return emit_op(compiler, SLJIT_MOV_S32, INT_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s32)srcw : srcw);
+#endif
+
+ case SLJIT_MOV_U8:
+ return emit_op(compiler, op, BYTE_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u8)srcw : srcw);
+
+ case SLJIT_MOV_S8:
+ return emit_op(compiler, op, BYTE_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s8)srcw : srcw);
+
+ case SLJIT_MOV_U16:
+ return emit_op(compiler, op, HALF_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_u16)srcw : srcw);
+
+ case SLJIT_MOV_S16:
+ return emit_op(compiler, op, HALF_DATA | SIGNED_DATA | MOVE_OP, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (sljit_s16)srcw : srcw);
+
+ case SLJIT_NOT:
+ return emit_op(compiler, SLJIT_XOR | (op & (SLJIT_32 | SLJIT_SET_Z)), flags, dst, dstw, src, srcw, SLJIT_IMM, -1);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ return emit_op(compiler, op, flags, dst, dstw, TMP_REG1, 0, src, srcw);
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (op & SLJIT_32) {
+ flags |= INT_DATA | SIGNED_DATA;
+ if (src1 & SLJIT_IMM)
+ src1w = (sljit_s32)src1w;
+ if (src2 & SLJIT_IMM)
+ src2w = (sljit_s32)src2w;
+ }
+#endif
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ case SLJIT_ADDC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_ADD;
+ return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SUB:
+ case SLJIT_SUBC:
+ compiler->status_flags_state = SLJIT_CURRENT_FLAGS_SUB;
+ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_MUL:
+ compiler->status_flags_state = 0;
+ return emit_op(compiler, op, flags | CUMULATIVE_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ return emit_op(compiler, op, flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ src2w &= 0x1f;
+#else /* !SLJIT_CONFIG_RISCV_32 */
+ if (op & SLJIT_32)
+ src2w &= 0x1f;
+ else
+ src2w &= 0x3f;
+#endif /* SLJIT_CONFIG_RISCV_32 */
+ }
+
+ return emit_op(compiler, op, flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ SLJIT_UNREACHABLE();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG2, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_left;
+ sljit_ins ins1, ins2, ins3;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_ins word = (op & SLJIT_32) >> 5;
+ sljit_s32 inp_flags = ((op & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+ sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64;
+#else /* !SLJIT_CONFIG_RISCV_64 */
+ sljit_s32 inp_flags = WORD_DATA | LOAD_DATA;
+ sljit_sw bit_length = 32;
+#endif /* SLJIT_CONFIG_RISCV_64 */
+
+ SLJIT_ASSERT(WORD == 0 || WORD == 0x8);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_left ? SLJIT_ROTL : SLJIT_ROTR) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= bit_length - 1;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG2, src2, src2w));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem(compiler, inp_flags, TMP_REG1, src1, src1w));
+ src1 = TMP_REG1;
+ } else if (src1 & SLJIT_IMM) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (is_left) {
+ ins1 = SLLI | WORD | IMM_I(src2w);
+ src2w = bit_length - src2w;
+ ins2 = SRLI | WORD | IMM_I(src2w);
+ } else {
+ ins1 = SRLI | WORD | IMM_I(src2w);
+ src2w = bit_length - src2w;
+ ins2 = SLLI | WORD | IMM_I(src2w);
+ }
+
+ FAIL_IF(push_inst(compiler, ins1 | RD(src_dst) | RS1(src_dst)));
+ FAIL_IF(push_inst(compiler, ins2 | RD(TMP_REG1) | RS1(src1)));
+ return push_inst(compiler, OR | RD(src_dst) | RS1(src_dst) | RS2(TMP_REG1));
+ }
+
+ if (is_left) {
+ ins1 = SLL;
+ ins2 = SRLI;
+ ins3 = SRL;
+ } else {
+ ins1 = SRL;
+ ins2 = SLLI;
+ ins3 = SLL;
+ }
+
+ FAIL_IF(push_inst(compiler, ins1 | WORD | RD(src_dst) | RS1(src_dst) | RS2(src2)));
+
+ if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) {
+ FAIL_IF(push_inst(compiler, ins2 | WORD | RD(TMP_REG1) | RS1(src1) | IMM_I(1)));
+ FAIL_IF(push_inst(compiler, XORI | RD(TMP_REG2) | RS1(src2) | IMM_I((sljit_ins)bit_length - 1)));
+ src1 = TMP_REG1;
+ } else
+ FAIL_IF(push_inst(compiler, SUB | WORD | RD(TMP_REG2) | RS1(TMP_ZERO) | RS2(src2)));
+
+ FAIL_IF(push_inst(compiler, ins3 | WORD | RD(TMP_REG1) | RS1(src1) | RS2(TMP_REG2)));
+ return push_inst(compiler, OR | RD(src_dst) | RS1(src_dst) | RS2(TMP_REG1));
+}
+
+#undef WORD
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ if (FAST_IS_REG(src))
+ FAIL_IF(push_inst(compiler, ADDI | RD(RETURN_ADDR_REG) | RS1(src) | IMM_I(0)));
+ else
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw));
+
+ return push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(RETURN_ADDR_REG) | IMM_I(0));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return freg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ return push_inst(compiler, *(sljit_ins*)instruction);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FLOAT_DATA(op) (DOUBLE_DATA | ((op & SLJIT_32) >> 7))
+#define FMT(op) ((sljit_ins)((op & SLJIT_32) ^ SLJIT_32) << 17)
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+# define flags (sljit_u32)0
+#else
+ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)) << 21;
+#endif
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src, srcw, dst, dstw));
+ src = TMP_FREG1;
+ }
+
+ FAIL_IF(push_inst(compiler, FCVT_W_S | FMT(op) | flags | RD(dst_r) | FRS1(src)));
+
+ /* Store the integer value from a VFP register. */
+ if (dst & SLJIT_MEM) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ return emit_op_mem2(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0);
+#else
+ return emit_op_mem2(compiler, flags ? WORD_DATA : INT_DATA, TMP_REG2, dst, dstw, 0, 0);
+#endif
+ }
+ return SLJIT_SUCCESS;
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+# undef flags
+#endif
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins inst;
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ sljit_u32 flags = ((sljit_u32)(GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)) << 21;
+#endif
+
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ FAIL_IF(emit_op_mem2(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
+#else
+ FAIL_IF(emit_op_mem2(compiler, (flags ? WORD_DATA : INT_DATA) | LOAD_DATA, TMP_REG1, src, srcw, dst, dstw));
+#endif
+ src = TMP_REG1;
+ } else if (src & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_RISCV_64 && SLJIT_CONFIG_RISCV_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+#endif
+
+ FAIL_IF(load_immediate(compiler, TMP_REG1, srcw, TMP_REG3));
+ src = TMP_REG1;
+ }
+
+ inst = FCVT_S_W | FMT(op) | FRD(dst_r) | RS1(src);
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ if (op & SLJIT_32)
+ inst |= F3(0x7);
+#else
+ inst |= flags;
+
+ if (op != SLJIT_CONV_F64_FROM_S32)
+ inst |= F3(0x7);
+#endif
+
+ FAIL_IF(push_inst(compiler, inst));
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG1, dst, dstw, 0, 0);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins inst;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
+ src1 = TMP_FREG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, 0, 0));
+ src2 = TMP_FREG2;
+ }
+
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_F_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ inst = FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2);
+ break;
+ case SLJIT_F_LESS:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ inst = FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2);
+ break;
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ inst = FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src2) | FRS2(src1);
+ break;
+ case SLJIT_F_GREATER:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ inst = FLE_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2);
+ break;
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ inst = FLE_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src2) | FRS2(src1);
+ break;
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ FAIL_IF(push_inst(compiler, FLT_S | FMT(op) | RD(OTHER_FLAG) | FRS1(src1) | FRS2(src2)));
+ FAIL_IF(push_inst(compiler, FLT_S | FMT(op) | RD(TMP_REG1) | FRS1(src2) | FRS2(src1)));
+ inst = OR | RD(OTHER_FLAG) | RS1(OTHER_FLAG) | RS2(TMP_REG1);
+ break;
+ default: /* SLJIT_UNORDERED, SLJIT_ORDERED */
+ FAIL_IF(push_inst(compiler, FADD_S | FMT(op) | FRD(TMP_FREG1) | FRS1(src1) | FRS2(src2)));
+ inst = FEQ_S | FMT(op) | RD(OTHER_FLAG) | FRS1(TMP_FREG1) | FRS2(TMP_FREG1);
+ break;
+ }
+
+ return push_inst(compiler, inst);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ SLJIT_COMPILE_ASSERT((SLJIT_32 == 0x100) && !(DOUBLE_DATA & 0x2), float_transfer_bit_error);
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32)
+ op ^= SLJIT_32;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op) | LOAD_DATA, dst_r, src, srcw, dst, dstw));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (src != dst_r) {
+ if (dst_r != TMP_FREG1)
+ FAIL_IF(push_inst(compiler, FSGNJ_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src)));
+ else
+ dst_r = src;
+ }
+ break;
+ case SLJIT_NEG_F64:
+ FAIL_IF(push_inst(compiler, FSGNJN_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src)));
+ break;
+ case SLJIT_ABS_F64:
+ FAIL_IF(push_inst(compiler, FSGNJX_S | FMT(op) | FRD(dst_r) | FRS1(src) | FRS2(src)));
+ break;
+ case SLJIT_CONV_F64_FROM_F32:
+ /* The SLJIT_32 bit is inverted because sljit_f32 needs to be loaded from the memory. */
+ FAIL_IF(push_inst(compiler, FCVT_S_D | ((op & SLJIT_32) ? (1 << 25) : ((1 << 20) | F3(7))) | FRD(dst_r) | FRS1(src)));
+ op ^= SLJIT_32;
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return emit_op_mem2(compiler, FLOAT_DATA(op), dst_r, dst, dstw, 0, 0);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r, flags = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG2;
+
+ if (src1 & SLJIT_MEM) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w)) {
+ FAIL_IF(compiler->error);
+ src1 = TMP_FREG1;
+ } else
+ flags |= SLOW_SRC1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ if (getput_arg_fast(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w)) {
+ FAIL_IF(compiler->error);
+ src2 = TMP_FREG2;
+ } else
+ flags |= SLOW_SRC2;
+ }
+
+ if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) {
+ if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) {
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, src1, src1w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
+ }
+ else {
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, src2, src2w));
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
+ }
+ }
+ else if (flags & SLOW_SRC1)
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG1, src1, src1w, dst, dstw));
+ else if (flags & SLOW_SRC2)
+ FAIL_IF(getput_arg(compiler, FLOAT_DATA(op) | LOAD_DATA, TMP_FREG2, src2, src2w, dst, dstw));
+
+ if (flags & SLOW_SRC1)
+ src1 = TMP_FREG1;
+ if (flags & SLOW_SRC2)
+ src2 = TMP_FREG2;
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(push_inst(compiler, FADD_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2)));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(push_inst(compiler, FSUB_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2)));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(push_inst(compiler, FMUL_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2)));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(push_inst(compiler, FDIV_S | FMT(op) | FRD(dst_r) | FRS1(src1) | FRS2(src2)));
+ break;
+ }
+
+ if (dst_r == TMP_FREG2)
+ FAIL_IF(emit_op_mem2(compiler, FLOAT_DATA(op), TMP_FREG2, dst, dstw, 0, 0));
+
+ return SLJIT_SUCCESS;
+}
+
+#undef FLOAT_DATA
+#undef FMT
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, ADDI | RD(dst) | RS1(RETURN_ADDR_REG) | IMM_I(0));
+
+ /* Memory. */
+ return emit_op_mem(compiler, WORD_DATA, RETURN_ADDR_REG, dst, dstw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+#define BRANCH_LENGTH ((sljit_ins)(3 * sizeof(sljit_ins)) << 7)
+#else
+#define BRANCH_LENGTH ((sljit_ins)(7 * sizeof(sljit_ins)) << 7)
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ struct sljit_jump *jump;
+ sljit_ins inst;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ type &= 0xff;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ inst = BNE | RS1(EQUAL_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH;
+ break;
+ case SLJIT_NOT_EQUAL:
+ inst = BEQ | RS1(EQUAL_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH;
+ break;
+ case SLJIT_LESS:
+ case SLJIT_GREATER:
+ case SLJIT_SIG_LESS:
+ case SLJIT_SIG_GREATER:
+ case SLJIT_OVERFLOW:
+ case SLJIT_CARRY:
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL: /* Not supported. */
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_ORDERED_GREATER:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED:
+ inst = BEQ | RS1(OTHER_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH;
+ break;
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_SIG_GREATER_EQUAL:
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_NOT_OVERFLOW:
+ case SLJIT_NOT_CARRY:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED:
+ inst = BNE | RS1(OTHER_FLAG) | RS2(TMP_ZERO) | BRANCH_LENGTH;
+ break;
+ default:
+ /* Not conditional branch. */
+ inst = 0;
+ break;
+ }
+
+ if (inst != 0) {
+ PTR_FAIL_IF(push_inst(compiler, inst));
+ jump->flags |= IS_COND;
+ }
+
+ jump->addr = compiler->size;
+ inst = JALR | RS1(TMP_REG1) | IMM_I(0);
+
+ if (type >= SLJIT_FAST_CALL) {
+ jump->flags |= IS_CALL;
+ inst |= RD(RETURN_ADDR_REG);
+ }
+
+ PTR_FAIL_IF(push_inst(compiler, inst));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ struct sljit_jump *jump;
+ sljit_s32 flags;
+ sljit_ins inst;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ flags = WORD_DATA | LOAD_DATA;
+#else /* !SLJIT_CONFIG_RISCV_32 */
+ flags = ((type & SLJIT_32) ? INT_DATA : WORD_DATA) | LOAD_DATA;
+#endif /* SLJIT_CONFIG_RISCV_32 */
+
+ if (src1 & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem2(compiler, flags, TMP_REG1, src1, src1w, src2, src2w));
+ src1 = TMP_REG1;
+ }
+
+ if (src2 & SLJIT_MEM) {
+ PTR_FAIL_IF(emit_op_mem2(compiler, flags, TMP_REG2, src2, src2w, 0, 0));
+ src2 = TMP_REG2;
+ }
+
+ if (src1 & SLJIT_IMM) {
+ if (src1w != 0) {
+ PTR_FAIL_IF(load_immediate(compiler, TMP_REG1, src1w, TMP_REG3));
+ src1 = TMP_REG1;
+ }
+ else
+ src1 = TMP_ZERO;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (src2w != 0) {
+ PTR_FAIL_IF(load_immediate(compiler, TMP_REG2, src2w, TMP_REG3));
+ src2 = TMP_REG2;
+ }
+ else
+ src2 = TMP_ZERO;
+ }
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, (sljit_u32)((type & SLJIT_REWRITABLE_JUMP) | IS_COND));
+ type &= 0xff;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ inst = BNE | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_NOT_EQUAL:
+ inst = BEQ | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_LESS:
+ inst = BGEU | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_GREATER_EQUAL:
+ inst = BLTU | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_GREATER:
+ inst = BGEU | RS1(src2) | RS2(src1) | BRANCH_LENGTH;
+ break;
+ case SLJIT_LESS_EQUAL:
+ inst = BLTU | RS1(src2) | RS2(src1) | BRANCH_LENGTH;
+ break;
+ case SLJIT_SIG_LESS:
+ inst = BGE | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_SIG_GREATER_EQUAL:
+ inst = BLT | RS1(src1) | RS2(src2) | BRANCH_LENGTH;
+ break;
+ case SLJIT_SIG_GREATER:
+ inst = BGE | RS1(src2) | RS2(src1) | BRANCH_LENGTH;
+ break;
+ case SLJIT_SIG_LESS_EQUAL:
+ inst = BLT | RS1(src2) | RS2(src1) | BRANCH_LENGTH;
+ break;
+ }
+
+ PTR_FAIL_IF(push_inst(compiler, inst));
+
+ jump->addr = compiler->size;
+ PTR_FAIL_IF(push_inst(compiler, JALR | RD(TMP_ZERO) | RS1(TMP_REG1) | IMM_I(0)));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+ return jump;
+}
+
+#undef BRANCH_LENGTH
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (!(src & SLJIT_IMM)) {
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw));
+ src = TMP_REG1;
+ }
+ return push_inst(compiler, JALR | RD((type >= SLJIT_FAST_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | RS1(src) | IMM_I(0));
+ }
+
+ /* These jumps are converted to jump/call instructions when possible. */
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF(!jump);
+ set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_CALL : 0));
+ jump->u.target = (sljit_uw)srcw;
+
+ jump->addr = compiler->size;
+ FAIL_IF(push_inst(compiler, JALR | RD((type >= SLJIT_FAST_CALL) ? RETURN_ADDR_REG : TMP_ZERO) | RS1(TMP_REG1) | IMM_I(0)));
+
+ /* Maximum number of instructions required for generating a constant. */
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, TMP_REG1, src, srcw));
+ src = TMP_REG1;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, ADDI | RD(TMP_REG1) | RS1(src) | IMM_I(0)));
+ src = TMP_REG1;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP;
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_s32 src_r, dst_r, invert;
+ sljit_s32 saved_op = op;
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ sljit_s32 mem_type = WORD_DATA;
+#else
+ sljit_s32 mem_type = ((op & SLJIT_32) || op == SLJIT_MOV32) ? (INT_DATA | SIGNED_DATA) : WORD_DATA;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ op = GET_OPCODE(op);
+ dst_r = (op < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG2;
+
+ compiler->cache_arg = 0;
+ compiler->cache_argw = 0;
+
+ if (op >= SLJIT_ADD && (dst & SLJIT_MEM))
+ FAIL_IF(emit_op_mem2(compiler, mem_type | LOAD_DATA, TMP_REG1, dst, dstw, dst, dstw));
+
+ if (type < SLJIT_F_EQUAL) {
+ src_r = OTHER_FLAG;
+ invert = type & 0x1;
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_NOT_EQUAL:
+ FAIL_IF(push_inst(compiler, SLTUI | RD(dst_r) | RS1(EQUAL_FLAG) | IMM_I(1)));
+ src_r = dst_r;
+ break;
+ case SLJIT_OVERFLOW:
+ case SLJIT_NOT_OVERFLOW:
+ if (compiler->status_flags_state & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) {
+ src_r = OTHER_FLAG;
+ break;
+ }
+ FAIL_IF(push_inst(compiler, SLTUI | RD(dst_r) | RS1(OTHER_FLAG) | IMM_I(1)));
+ src_r = dst_r;
+ invert ^= 0x1;
+ break;
+ }
+ } else {
+ invert = 0;
+ src_r = OTHER_FLAG;
+
+ switch (type) {
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL: /* Not supported. */
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ case SLJIT_F_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED:
+ invert = 1;
+ break;
+ }
+ }
+
+ if (invert) {
+ FAIL_IF(push_inst(compiler, XORI | RD(dst_r) | RS1(src_r) | IMM_I(1)));
+ src_r = dst_r;
+ }
+
+ if (op < SLJIT_ADD) {
+ if (dst & SLJIT_MEM)
+ return emit_op_mem(compiler, mem_type, src_r, dst, dstw);
+
+ if (src_r != dst_r)
+ return push_inst(compiler, ADDI | RD(dst_r) | RS1(src_r) | IMM_I(0));
+ return SLJIT_SUCCESS;
+ }
+
+ mem_type |= CUMULATIVE_OP | IMM_OP | ALT_KEEP_CACHE;
+
+ if (dst & SLJIT_MEM)
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, TMP_REG1, 0, src_r, 0);
+ return emit_op(compiler, saved_op, mem_type, dst, dstw, dst, dstw, src_r, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_s32 flags;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ if (SLJIT_UNLIKELY(mem & OFFS_REG_MASK)) {
+ memw &= 0x3;
+
+ if (SLJIT_UNLIKELY(memw != 0)) {
+ FAIL_IF(push_inst(compiler, SLLI | RD(TMP_REG1) | RS1(OFFS_REG(mem)) | IMM_I(memw)));
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(TMP_REG1) | RS2(mem & REG_MASK)));
+ } else
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(mem & REG_MASK) | RS2(OFFS_REG(mem))));
+
+ mem = TMP_REG1;
+ memw = 0;
+ } else if (memw > SIMM_MAX - SSIZE_OF(sw) || memw < SIMM_MIN) {
+ if (((memw + 0x800) & 0xfff) <= 0xfff - SSIZE_OF(sw)) {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, TO_ARGW_HI(memw), TMP_REG3));
+ memw &= 0xfff;
+ } else {
+ FAIL_IF(load_immediate(compiler, TMP_REG1, memw, TMP_REG3));
+ memw = 0;
+ }
+
+ if (mem & REG_MASK)
+ FAIL_IF(push_inst(compiler, ADD | RD(TMP_REG1) | RS1(TMP_REG1) | RS2(mem & REG_MASK)));
+
+ mem = TMP_REG1;
+ } else {
+ mem &= REG_MASK;
+ memw &= 0xfff;
+ }
+
+ SLJIT_ASSERT((memw >= 0 && memw <= SIMM_MAX - SSIZE_OF(sw)) || (memw > SIMM_MAX && memw <= 0xfff));
+
+ if (!(type & SLJIT_MEM_STORE) && mem == REG_PAIR_FIRST(reg)) {
+ FAIL_IF(push_mem_inst(compiler, WORD_DATA | LOAD_DATA, REG_PAIR_SECOND(reg), mem, (memw + SSIZE_OF(sw)) & 0xfff));
+ return push_mem_inst(compiler, WORD_DATA | LOAD_DATA, REG_PAIR_FIRST(reg), mem, memw);
+ }
+
+ flags = WORD_DATA | (!(type & SLJIT_MEM_STORE) ? LOAD_DATA : 0);
+
+ FAIL_IF(push_mem_inst(compiler, flags, REG_PAIR_FIRST(reg), mem, memw));
+ return push_mem_inst(compiler, flags, REG_PAIR_SECOND(reg), mem, (memw + SSIZE_OF(sw)) & 0xfff);
+}
+
+#undef TO_ARGW_HI
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_const *const_;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(emit_const(compiler, dst_r, init_value, ADDI | RD(dst_r)));
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw));
+
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_s32 dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG2;
+ PTR_FAIL_IF(push_inst(compiler, (sljit_ins)dst_r));
+#if (defined SLJIT_CONFIG_RISCV_32 && SLJIT_CONFIG_RISCV_32)
+ compiler->size += 1;
+#else
+ compiler->size += 5;
+#endif
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(emit_op_mem(compiler, WORD_DATA, TMP_REG2, dst, dstw));
+
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeS390X.c b/contrib/libs/pcre2/src/sljit/sljitNativeS390X.c
new file mode 100644
index 0000000000..8b51bad9bc
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeS390X.c
@@ -0,0 +1,3747 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+#include <sys/auxv.h>
+
+#ifdef __ARCH__
+#define ENABLE_STATIC_FACILITY_DETECTION 1
+#else
+#define ENABLE_STATIC_FACILITY_DETECTION 0
+#endif
+#define ENABLE_DYNAMIC_FACILITY_DETECTION 1
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "s390x" SLJIT_CPUINFO;
+}
+
+/* Instructions. */
+typedef sljit_uw sljit_ins;
+
+/* Instruction tags (most significant halfword). */
+static const sljit_ins sljit_ins_const = (sljit_ins)1 << 48;
+
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 0, 1
+};
+
+/* there are also a[2-15] available, but they are slower to access and
+ * their use is limited as mundaym explained:
+ * https://github.com/zherczeg/sljit/pull/91#discussion_r486895689
+ */
+
+/* General Purpose Registers [0-15]. */
+typedef sljit_uw sljit_gpr;
+
+/*
+ * WARNING
+ * the following code is non standard and should be improved for
+ * consistency, but doesn't use SLJIT_NUMBER_OF_REGISTERS based
+ * registers because r0 and r1 are the ABI recommended volatiles.
+ * there is a gpr() function that maps sljit to physical register numbers
+ * that should be used instead of the usual index into reg_map[] and
+ * will be retired ASAP (TODO: carenas)
+ */
+
+static const sljit_gpr r0 = 0; /* reg_map[SLJIT_NUMBER_OF_REGISTERS + 2]: 0 in address calculations; reserved */
+static const sljit_gpr r1 = 1; /* reg_map[SLJIT_NUMBER_OF_REGISTERS + 3]: reserved */
+static const sljit_gpr r2 = 2; /* reg_map[1]: 1st argument */
+static const sljit_gpr r3 = 3; /* reg_map[2]: 2nd argument */
+static const sljit_gpr r4 = 4; /* reg_map[3]: 3rd argument */
+static const sljit_gpr r5 = 5; /* reg_map[4]: 4th argument */
+static const sljit_gpr r6 = 6; /* reg_map[5]: 5th argument; 1st saved register */
+static const sljit_gpr r7 = 7; /* reg_map[6] */
+static const sljit_gpr r8 = 8; /* reg_map[7] */
+static const sljit_gpr r9 = 9; /* reg_map[8] */
+static const sljit_gpr r10 = 10; /* reg_map[9] */
+static const sljit_gpr r11 = 11; /* reg_map[10] */
+static const sljit_gpr r12 = 12; /* reg_map[11]: GOT */
+static const sljit_gpr r13 = 13; /* reg_map[12]: Literal Pool pointer */
+static const sljit_gpr r14 = 14; /* reg_map[0]: return address and flag register */
+static const sljit_gpr r15 = 15; /* reg_map[SLJIT_NUMBER_OF_REGISTERS + 1]: stack pointer */
+
+/* WARNING: r12 and r13 shouldn't be used as per ABI recommendation */
+/* TODO(carenas): r12 might conflict in PIC code, reserve? */
+/* TODO(carenas): r13 is usually pointed to "pool" per ABI, using a tmp
+ * like we do know might be faster though, reserve?
+ */
+
+/* TODO(carenas): should be named TMP_REG[1-2] for consistency */
+#define tmp0 r0
+#define tmp1 r1
+
+/* TODO(carenas): flags should move to a different register so that
+ * link register doesn't need to change
+ */
+
+/* When reg cannot be unused. */
+#define IS_GPR_REG(reg) ((reg > 0) && (reg) <= SLJIT_SP)
+
+/* Link register. */
+static const sljit_gpr link_r = 14; /* r14 */
+
+#define TMP_FREG1 (0)
+
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = {
+ 1, 0, 2, 4, 6, 3, 5, 7, 15, 14, 13, 12, 11, 10, 9, 8,
+};
+
+#define R0A(r) (r)
+#define R4A(r) ((r) << 4)
+#define R8A(r) ((r) << 8)
+#define R12A(r) ((r) << 12)
+#define R16A(r) ((r) << 16)
+#define R20A(r) ((r) << 20)
+#define R28A(r) ((r) << 28)
+#define R32A(r) ((r) << 32)
+#define R36A(r) ((r) << 36)
+
+#define R0(r) ((sljit_ins)reg_map[r])
+
+#define F0(r) ((sljit_ins)freg_map[r])
+#define F4(r) (R4A((sljit_ins)freg_map[r]))
+#define F20(r) (R20A((sljit_ins)freg_map[r]))
+#define F36(r) (R36A((sljit_ins)freg_map[r]))
+
+struct sljit_s390x_const {
+ struct sljit_const const_; /* must be first */
+ sljit_sw init_value; /* required to build literal pool */
+};
+
+/* Convert SLJIT register to hardware register. */
+static SLJIT_INLINE sljit_gpr gpr(sljit_s32 r)
+{
+ SLJIT_ASSERT(r >= 0 && r < (sljit_s32)(sizeof(reg_map) / sizeof(reg_map[0])));
+ return reg_map[r];
+}
+
+static SLJIT_INLINE sljit_gpr fgpr(sljit_s32 r)
+{
+ SLJIT_ASSERT(r >= 0 && r < (sljit_s32)(sizeof(freg_map) / sizeof(freg_map[0])));
+ return freg_map[r];
+}
+
+/* Size of instruction in bytes. Tags must already be cleared. */
+static SLJIT_INLINE sljit_uw sizeof_ins(sljit_ins ins)
+{
+ /* keep faulting instructions */
+ if (ins == 0)
+ return 2;
+
+ if ((ins & 0x00000000ffffL) == ins)
+ return 2;
+ if ((ins & 0x0000ffffffffL) == ins)
+ return 4;
+ if ((ins & 0xffffffffffffL) == ins)
+ return 6;
+
+ SLJIT_UNREACHABLE();
+ return (sljit_uw)-1;
+}
+
+static sljit_s32 push_inst(struct sljit_compiler *compiler, sljit_ins ins)
+{
+ sljit_ins *ibuf = (sljit_ins *)ensure_buf(compiler, sizeof(sljit_ins));
+ FAIL_IF(!ibuf);
+ *ibuf = ins;
+ compiler->size++;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 encode_inst(void **ptr, sljit_ins ins)
+{
+ sljit_u16 *ibuf = (sljit_u16 *)*ptr;
+ sljit_uw size = sizeof_ins(ins);
+
+ SLJIT_ASSERT((size & 6) == size);
+ switch (size) {
+ case 6:
+ *ibuf++ = (sljit_u16)(ins >> 32);
+ /* fallthrough */
+ case 4:
+ *ibuf++ = (sljit_u16)(ins >> 16);
+ /* fallthrough */
+ case 2:
+ *ibuf++ = (sljit_u16)(ins);
+ }
+ *ptr = (void*)ibuf;
+ return SLJIT_SUCCESS;
+}
+
+#define SLJIT_ADD_SUB_NO_COMPARE(status_flags_state) \
+ (((status_flags_state) & (SLJIT_CURRENT_FLAGS_ADD | SLJIT_CURRENT_FLAGS_SUB)) \
+ && !((status_flags_state) & SLJIT_CURRENT_FLAGS_COMPARE))
+
+/* Map the given type to a 4-bit condition code mask. */
+static SLJIT_INLINE sljit_u8 get_cc(struct sljit_compiler *compiler, sljit_s32 type) {
+ const sljit_u8 cc0 = 1 << 3; /* equal {,to zero} */
+ const sljit_u8 cc1 = 1 << 2; /* less than {,zero} */
+ const sljit_u8 cc2 = 1 << 1; /* greater than {,zero} */
+ const sljit_u8 cc3 = 1 << 0; /* {overflow,NaN} */
+
+ switch (type) {
+ case SLJIT_EQUAL:
+ if (SLJIT_ADD_SUB_NO_COMPARE(compiler->status_flags_state)) {
+ sljit_s32 type = GET_FLAG_TYPE(compiler->status_flags_state);
+ if (type >= SLJIT_SIG_LESS && type <= SLJIT_SIG_LESS_EQUAL)
+ return cc0;
+ if (type == SLJIT_OVERFLOW)
+ return (cc0 | cc3);
+ return (cc0 | cc2);
+ }
+ /* fallthrough */
+
+ case SLJIT_F_EQUAL:
+ case SLJIT_ORDERED_EQUAL:
+ return cc0;
+
+ case SLJIT_NOT_EQUAL:
+ if (SLJIT_ADD_SUB_NO_COMPARE(compiler->status_flags_state)) {
+ sljit_s32 type = GET_FLAG_TYPE(compiler->status_flags_state);
+ if (type >= SLJIT_SIG_LESS && type <= SLJIT_SIG_LESS_EQUAL)
+ return (cc1 | cc2 | cc3);
+ if (type == SLJIT_OVERFLOW)
+ return (cc1 | cc2);
+ return (cc1 | cc3);
+ }
+ /* fallthrough */
+
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ return (cc1 | cc2 | cc3);
+
+ case SLJIT_LESS:
+ return cc1;
+
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ return (cc0 | cc2 | cc3);
+
+ case SLJIT_GREATER:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_COMPARE)
+ return cc2;
+ return cc3;
+
+ case SLJIT_LESS_EQUAL:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_COMPARE)
+ return (cc0 | cc1);
+ return (cc0 | cc1 | cc2);
+
+ case SLJIT_SIG_LESS:
+ case SLJIT_F_LESS:
+ case SLJIT_ORDERED_LESS:
+ return cc1;
+
+ case SLJIT_NOT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB)
+ return (cc2 | cc3);
+ /* fallthrough */
+
+ case SLJIT_SIG_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return (cc0 | cc1);
+
+ case SLJIT_CARRY:
+ if (compiler->status_flags_state & SLJIT_CURRENT_FLAGS_SUB)
+ return (cc0 | cc1);
+ /* fallthrough */
+
+ case SLJIT_SIG_GREATER:
+ case SLJIT_UNORDERED_OR_GREATER:
+ /* Overflow is considered greater, see SLJIT_SUB. */
+ return cc2 | cc3;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ return (cc0 | cc2 | cc3);
+
+ case SLJIT_OVERFLOW:
+ if (compiler->status_flags_state & SLJIT_SET_Z)
+ return (cc2 | cc3);
+ /* fallthrough */
+
+ case SLJIT_UNORDERED:
+ return cc3;
+
+ case SLJIT_NOT_OVERFLOW:
+ if (compiler->status_flags_state & SLJIT_SET_Z)
+ return (cc0 | cc1);
+ /* fallthrough */
+
+ case SLJIT_ORDERED:
+ return (cc0 | cc1 | cc2);
+
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ return (cc1 | cc2);
+
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_GREATER:
+ return cc2;
+
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ return (cc0 | cc2);
+
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return (cc0 | cc1 | cc3);
+
+ case SLJIT_UNORDERED_OR_EQUAL:
+ return (cc0 | cc3);
+
+ case SLJIT_UNORDERED_OR_LESS:
+ return (cc1 | cc3);
+ }
+
+ SLJIT_UNREACHABLE();
+ return (sljit_u8)-1;
+}
+
+/* Facility to bit index mappings.
+ Note: some facilities share the same bit index. */
+typedef sljit_uw facility_bit;
+#define STORE_FACILITY_LIST_EXTENDED_FACILITY 7
+#define FAST_LONG_DISPLACEMENT_FACILITY 19
+#define EXTENDED_IMMEDIATE_FACILITY 21
+#define GENERAL_INSTRUCTION_EXTENSION_FACILITY 34
+#define DISTINCT_OPERAND_FACILITY 45
+#define HIGH_WORD_FACILITY 45
+#define POPULATION_COUNT_FACILITY 45
+#define LOAD_STORE_ON_CONDITION_1_FACILITY 45
+#define MISCELLANEOUS_INSTRUCTION_EXTENSIONS_1_FACILITY 49
+#define LOAD_STORE_ON_CONDITION_2_FACILITY 53
+#define MISCELLANEOUS_INSTRUCTION_EXTENSIONS_2_FACILITY 58
+#define VECTOR_FACILITY 129
+#define VECTOR_ENHANCEMENTS_1_FACILITY 135
+
+/* Report whether a facility is known to be present due to the compiler
+ settings. This function should always be compiled to a constant
+ value given a constant argument. */
+static SLJIT_INLINE int have_facility_static(facility_bit x)
+{
+#if ENABLE_STATIC_FACILITY_DETECTION
+ switch (x) {
+ case FAST_LONG_DISPLACEMENT_FACILITY:
+ return (__ARCH__ >= 6 /* z990 */);
+ case EXTENDED_IMMEDIATE_FACILITY:
+ case STORE_FACILITY_LIST_EXTENDED_FACILITY:
+ return (__ARCH__ >= 7 /* z9-109 */);
+ case GENERAL_INSTRUCTION_EXTENSION_FACILITY:
+ return (__ARCH__ >= 8 /* z10 */);
+ case DISTINCT_OPERAND_FACILITY:
+ return (__ARCH__ >= 9 /* z196 */);
+ case MISCELLANEOUS_INSTRUCTION_EXTENSIONS_1_FACILITY:
+ return (__ARCH__ >= 10 /* zEC12 */);
+ case LOAD_STORE_ON_CONDITION_2_FACILITY:
+ case VECTOR_FACILITY:
+ return (__ARCH__ >= 11 /* z13 */);
+ case MISCELLANEOUS_INSTRUCTION_EXTENSIONS_2_FACILITY:
+ case VECTOR_ENHANCEMENTS_1_FACILITY:
+ return (__ARCH__ >= 12 /* z14 */);
+ default:
+ SLJIT_UNREACHABLE();
+ }
+#endif
+ return 0;
+}
+
+static SLJIT_INLINE unsigned long get_hwcap()
+{
+ static unsigned long hwcap = 0;
+ if (SLJIT_UNLIKELY(!hwcap)) {
+ hwcap = getauxval(AT_HWCAP);
+ SLJIT_ASSERT(hwcap != 0);
+ }
+ return hwcap;
+}
+
+static SLJIT_INLINE int have_stfle()
+{
+ if (have_facility_static(STORE_FACILITY_LIST_EXTENDED_FACILITY))
+ return 1;
+
+ return (get_hwcap() & HWCAP_S390_STFLE);
+}
+
+/* Report whether the given facility is available. This function always
+ performs a runtime check. */
+static int have_facility_dynamic(facility_bit x)
+{
+#if ENABLE_DYNAMIC_FACILITY_DETECTION
+ static struct {
+ sljit_uw bits[4];
+ } cpu_features;
+ size_t size = sizeof(cpu_features);
+ const sljit_uw word_index = x >> 6;
+ const sljit_uw bit_index = ((1UL << 63) >> (x & 63));
+
+ SLJIT_ASSERT(x < size * 8);
+ if (SLJIT_UNLIKELY(!have_stfle()))
+ return 0;
+
+ if (SLJIT_UNLIKELY(cpu_features.bits[0] == 0)) {
+ __asm__ __volatile__ (
+ "lgr %%r0, %0;"
+ "stfle 0(%1);"
+ /* outputs */:
+ /* inputs */: "d" ((size / 8) - 1), "a" (&cpu_features)
+ /* clobbers */: "r0", "cc", "memory"
+ );
+ SLJIT_ASSERT(cpu_features.bits[0] != 0);
+ }
+ return (cpu_features.bits[word_index] & bit_index) != 0;
+#else
+ return 0;
+#endif
+}
+
+#define HAVE_FACILITY(name, bit) \
+static SLJIT_INLINE int name() \
+{ \
+ static int have = -1; \
+ /* Static check first. May allow the function to be optimized away. */ \
+ if (have_facility_static(bit)) \
+ have = 1; \
+ else if (SLJIT_UNLIKELY(have < 0)) \
+ have = have_facility_dynamic(bit) ? 1 : 0; \
+\
+ return have; \
+}
+
+HAVE_FACILITY(have_eimm, EXTENDED_IMMEDIATE_FACILITY)
+HAVE_FACILITY(have_ldisp, FAST_LONG_DISPLACEMENT_FACILITY)
+HAVE_FACILITY(have_genext, GENERAL_INSTRUCTION_EXTENSION_FACILITY)
+HAVE_FACILITY(have_lscond1, LOAD_STORE_ON_CONDITION_1_FACILITY)
+HAVE_FACILITY(have_lscond2, LOAD_STORE_ON_CONDITION_2_FACILITY)
+HAVE_FACILITY(have_misc2, MISCELLANEOUS_INSTRUCTION_EXTENSIONS_2_FACILITY)
+#undef HAVE_FACILITY
+
+#define is_u12(d) (0 <= (d) && (d) <= 0x00000fffL)
+#define is_u32(d) (0 <= (d) && (d) <= 0xffffffffL)
+
+#define CHECK_SIGNED(v, bitlen) \
+ ((v) >= -(1 << ((bitlen) - 1)) && (v) < (1 << ((bitlen) - 1)))
+
+#define is_s8(d) CHECK_SIGNED((d), 8)
+#define is_s16(d) CHECK_SIGNED((d), 16)
+#define is_s20(d) CHECK_SIGNED((d), 20)
+#define is_s32(d) ((d) == (sljit_s32)(d))
+
+static SLJIT_INLINE sljit_ins disp_s20(sljit_s32 d)
+{
+ SLJIT_ASSERT(is_s20(d));
+
+ sljit_uw dh = (d >> 12) & 0xff;
+ sljit_uw dl = (d << 8) & 0xfff00;
+ return (dh | dl) << 8;
+}
+
+/* TODO(carenas): variadic macro is not strictly needed */
+#define SLJIT_S390X_INSTRUCTION(op, ...) \
+static SLJIT_INLINE sljit_ins op(__VA_ARGS__)
+
+/* RR form instructions. */
+#define SLJIT_S390X_RR(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src) \
+{ \
+ return (pattern) | ((dst & 0xf) << 4) | (src & 0xf); \
+}
+
+/* AND */
+SLJIT_S390X_RR(nr, 0x1400)
+
+/* BRANCH AND SAVE */
+SLJIT_S390X_RR(basr, 0x0d00)
+
+/* BRANCH ON CONDITION */
+SLJIT_S390X_RR(bcr, 0x0700) /* TODO(mundaym): type for mask? */
+
+/* DIVIDE */
+SLJIT_S390X_RR(dr, 0x1d00)
+
+/* EXCLUSIVE OR */
+SLJIT_S390X_RR(xr, 0x1700)
+
+/* LOAD */
+SLJIT_S390X_RR(lr, 0x1800)
+
+/* LOAD COMPLEMENT */
+SLJIT_S390X_RR(lcr, 0x1300)
+
+/* OR */
+SLJIT_S390X_RR(or, 0x1600)
+
+#undef SLJIT_S390X_RR
+
+/* RRE form instructions */
+#define SLJIT_S390X_RRE(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src) \
+{ \
+ return (pattern) | R4A(dst) | R0A(src); \
+}
+
+/* AND */
+SLJIT_S390X_RRE(ngr, 0xb9800000)
+
+/* DIVIDE LOGICAL */
+SLJIT_S390X_RRE(dlr, 0xb9970000)
+SLJIT_S390X_RRE(dlgr, 0xb9870000)
+
+/* DIVIDE SINGLE */
+SLJIT_S390X_RRE(dsgr, 0xb90d0000)
+
+/* EXCLUSIVE OR */
+SLJIT_S390X_RRE(xgr, 0xb9820000)
+
+/* LOAD */
+SLJIT_S390X_RRE(lgr, 0xb9040000)
+SLJIT_S390X_RRE(lgfr, 0xb9140000)
+
+/* LOAD BYTE */
+SLJIT_S390X_RRE(lbr, 0xb9260000)
+SLJIT_S390X_RRE(lgbr, 0xb9060000)
+
+/* LOAD COMPLEMENT */
+SLJIT_S390X_RRE(lcgr, 0xb9030000)
+
+/* LOAD HALFWORD */
+SLJIT_S390X_RRE(lhr, 0xb9270000)
+SLJIT_S390X_RRE(lghr, 0xb9070000)
+
+/* LOAD LOGICAL */
+SLJIT_S390X_RRE(llgfr, 0xb9160000)
+
+/* LOAD LOGICAL CHARACTER */
+SLJIT_S390X_RRE(llcr, 0xb9940000)
+SLJIT_S390X_RRE(llgcr, 0xb9840000)
+
+/* LOAD LOGICAL HALFWORD */
+SLJIT_S390X_RRE(llhr, 0xb9950000)
+SLJIT_S390X_RRE(llghr, 0xb9850000)
+
+/* MULTIPLY LOGICAL */
+SLJIT_S390X_RRE(mlgr, 0xb9860000)
+
+/* MULTIPLY SINGLE */
+SLJIT_S390X_RRE(msgfr, 0xb91c0000)
+
+/* OR */
+SLJIT_S390X_RRE(ogr, 0xb9810000)
+
+/* SUBTRACT */
+SLJIT_S390X_RRE(sgr, 0xb9090000)
+
+#undef SLJIT_S390X_RRE
+
+/* RI-a form instructions */
+#define SLJIT_S390X_RIA(name, pattern, imm_type) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr reg, imm_type imm) \
+{ \
+ return (pattern) | R20A(reg) | (imm & 0xffff); \
+}
+
+/* ADD HALFWORD IMMEDIATE */
+SLJIT_S390X_RIA(aghi, 0xa70b0000, sljit_s16)
+
+/* LOAD HALFWORD IMMEDIATE */
+SLJIT_S390X_RIA(lhi, 0xa7080000, sljit_s16)
+SLJIT_S390X_RIA(lghi, 0xa7090000, sljit_s16)
+
+/* LOAD LOGICAL IMMEDIATE */
+SLJIT_S390X_RIA(llihh, 0xa50c0000, sljit_u16)
+SLJIT_S390X_RIA(llihl, 0xa50d0000, sljit_u16)
+SLJIT_S390X_RIA(llilh, 0xa50e0000, sljit_u16)
+SLJIT_S390X_RIA(llill, 0xa50f0000, sljit_u16)
+
+/* MULTIPLY HALFWORD IMMEDIATE */
+SLJIT_S390X_RIA(mhi, 0xa70c0000, sljit_s16)
+SLJIT_S390X_RIA(mghi, 0xa70d0000, sljit_s16)
+
+/* OR IMMEDIATE */
+SLJIT_S390X_RIA(oilh, 0xa50a0000, sljit_u16)
+
+#undef SLJIT_S390X_RIA
+
+/* RIL-a form instructions (requires extended immediate facility) */
+#define SLJIT_S390X_RILA(name, pattern, imm_type) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr reg, imm_type imm) \
+{ \
+ SLJIT_ASSERT(have_eimm()); \
+ return (pattern) | R36A(reg) | ((sljit_ins)imm & 0xffffffffu); \
+}
+
+/* ADD IMMEDIATE */
+SLJIT_S390X_RILA(agfi, 0xc20800000000, sljit_s32)
+
+/* ADD IMMEDIATE HIGH */
+SLJIT_S390X_RILA(aih, 0xcc0800000000, sljit_s32) /* TODO(mundaym): high-word facility? */
+
+/* AND IMMEDIATE */
+SLJIT_S390X_RILA(nihf, 0xc00a00000000, sljit_u32)
+
+/* EXCLUSIVE OR IMMEDIATE */
+SLJIT_S390X_RILA(xilf, 0xc00700000000, sljit_u32)
+
+/* INSERT IMMEDIATE */
+SLJIT_S390X_RILA(iihf, 0xc00800000000, sljit_u32)
+SLJIT_S390X_RILA(iilf, 0xc00900000000, sljit_u32)
+
+/* LOAD IMMEDIATE */
+SLJIT_S390X_RILA(lgfi, 0xc00100000000, sljit_s32)
+
+/* LOAD LOGICAL IMMEDIATE */
+SLJIT_S390X_RILA(llihf, 0xc00e00000000, sljit_u32)
+SLJIT_S390X_RILA(llilf, 0xc00f00000000, sljit_u32)
+
+/* SUBTRACT LOGICAL IMMEDIATE */
+SLJIT_S390X_RILA(slfi, 0xc20500000000, sljit_u32)
+
+#undef SLJIT_S390X_RILA
+
+/* RX-a form instructions */
+#define SLJIT_S390X_RXA(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr r, sljit_s32 d, sljit_gpr x, sljit_gpr b) \
+{ \
+ SLJIT_ASSERT((d & 0xfff) == d); \
+\
+ return (pattern) | R20A(r) | R16A(x) | R12A(b) | (sljit_ins)(d & 0xfff); \
+}
+
+/* LOAD */
+SLJIT_S390X_RXA(l, 0x58000000)
+
+/* LOAD ADDRESS */
+SLJIT_S390X_RXA(la, 0x41000000)
+
+/* LOAD HALFWORD */
+SLJIT_S390X_RXA(lh, 0x48000000)
+
+/* MULTIPLY SINGLE */
+SLJIT_S390X_RXA(ms, 0x71000000)
+
+/* STORE */
+SLJIT_S390X_RXA(st, 0x50000000)
+
+/* STORE CHARACTER */
+SLJIT_S390X_RXA(stc, 0x42000000)
+
+/* STORE HALFWORD */
+SLJIT_S390X_RXA(sth, 0x40000000)
+
+#undef SLJIT_S390X_RXA
+
+/* RXY-a instructions */
+#define SLJIT_S390X_RXYA(name, pattern, cond) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr r, sljit_s32 d, sljit_gpr x, sljit_gpr b) \
+{ \
+ SLJIT_ASSERT(cond); \
+\
+ return (pattern) | R36A(r) | R32A(x) | R28A(b) | disp_s20(d); \
+}
+
+/* LOAD */
+SLJIT_S390X_RXYA(ly, 0xe30000000058, have_ldisp())
+SLJIT_S390X_RXYA(lg, 0xe30000000004, 1)
+SLJIT_S390X_RXYA(lgf, 0xe30000000014, 1)
+
+/* LOAD BYTE */
+SLJIT_S390X_RXYA(lb, 0xe30000000076, have_ldisp())
+SLJIT_S390X_RXYA(lgb, 0xe30000000077, have_ldisp())
+
+/* LOAD HALFWORD */
+SLJIT_S390X_RXYA(lhy, 0xe30000000078, have_ldisp())
+SLJIT_S390X_RXYA(lgh, 0xe30000000015, 1)
+
+/* LOAD LOGICAL */
+SLJIT_S390X_RXYA(llgf, 0xe30000000016, 1)
+
+/* LOAD LOGICAL CHARACTER */
+SLJIT_S390X_RXYA(llc, 0xe30000000094, have_eimm())
+SLJIT_S390X_RXYA(llgc, 0xe30000000090, 1)
+
+/* LOAD LOGICAL HALFWORD */
+SLJIT_S390X_RXYA(llh, 0xe30000000095, have_eimm())
+SLJIT_S390X_RXYA(llgh, 0xe30000000091, 1)
+
+/* MULTIPLY SINGLE */
+SLJIT_S390X_RXYA(msy, 0xe30000000051, have_ldisp())
+SLJIT_S390X_RXYA(msg, 0xe3000000000c, 1)
+
+/* STORE */
+SLJIT_S390X_RXYA(sty, 0xe30000000050, have_ldisp())
+SLJIT_S390X_RXYA(stg, 0xe30000000024, 1)
+
+/* STORE CHARACTER */
+SLJIT_S390X_RXYA(stcy, 0xe30000000072, have_ldisp())
+
+/* STORE HALFWORD */
+SLJIT_S390X_RXYA(sthy, 0xe30000000070, have_ldisp())
+
+#undef SLJIT_S390X_RXYA
+
+/* RSY-a instructions */
+#define SLJIT_S390X_RSYA(name, pattern, cond) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src, sljit_s32 d, sljit_gpr b) \
+{ \
+ SLJIT_ASSERT(cond); \
+\
+ return (pattern) | R36A(dst) | R32A(src) | R28A(b) | disp_s20(d); \
+}
+
+/* LOAD MULTIPLE */
+SLJIT_S390X_RSYA(lmg, 0xeb0000000004, 1)
+
+/* SHIFT LEFT LOGICAL */
+SLJIT_S390X_RSYA(sllg, 0xeb000000000d, 1)
+
+/* SHIFT RIGHT SINGLE */
+SLJIT_S390X_RSYA(srag, 0xeb000000000a, 1)
+
+/* STORE MULTIPLE */
+SLJIT_S390X_RSYA(stmg, 0xeb0000000024, 1)
+
+#undef SLJIT_S390X_RSYA
+
+/* RIE-f instructions (require general-instructions-extension facility) */
+#define SLJIT_S390X_RIEF(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src, sljit_u8 start, sljit_u8 end, sljit_u8 rot) \
+{ \
+ sljit_ins i3, i4, i5; \
+\
+ SLJIT_ASSERT(have_genext()); \
+ i3 = (sljit_ins)start << 24; \
+ i4 = (sljit_ins)end << 16; \
+ i5 = (sljit_ins)rot << 8; \
+\
+ return (pattern) | R36A(dst & 0xf) | R32A(src & 0xf) | i3 | i4 | i5; \
+}
+
+/* ROTATE THEN AND SELECTED BITS */
+/* SLJIT_S390X_RIEF(rnsbg, 0xec0000000054) */
+
+/* ROTATE THEN EXCLUSIVE OR SELECTED BITS */
+/* SLJIT_S390X_RIEF(rxsbg, 0xec0000000057) */
+
+/* ROTATE THEN OR SELECTED BITS */
+SLJIT_S390X_RIEF(rosbg, 0xec0000000056)
+
+/* ROTATE THEN INSERT SELECTED BITS */
+/* SLJIT_S390X_RIEF(risbg, 0xec0000000055) */
+/* SLJIT_S390X_RIEF(risbgn, 0xec0000000059) */
+
+/* ROTATE THEN INSERT SELECTED BITS HIGH */
+SLJIT_S390X_RIEF(risbhg, 0xec000000005d)
+
+/* ROTATE THEN INSERT SELECTED BITS LOW */
+/* SLJIT_S390X_RIEF(risblg, 0xec0000000051) */
+
+#undef SLJIT_S390X_RIEF
+
+/* RRF-c instructions (require load/store-on-condition 1 facility) */
+#define SLJIT_S390X_RRFC(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr dst, sljit_gpr src, sljit_uw mask) \
+{ \
+ sljit_ins m3; \
+\
+ SLJIT_ASSERT(have_lscond1()); \
+ m3 = (sljit_ins)(mask & 0xf) << 12; \
+\
+ return (pattern) | m3 | R4A(dst) | R0A(src); \
+}
+
+/* LOAD HALFWORD IMMEDIATE ON CONDITION */
+SLJIT_S390X_RRFC(locr, 0xb9f20000)
+SLJIT_S390X_RRFC(locgr, 0xb9e20000)
+
+#undef SLJIT_S390X_RRFC
+
+/* RIE-g instructions (require load/store-on-condition 2 facility) */
+#define SLJIT_S390X_RIEG(name, pattern) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr reg, sljit_sw imm, sljit_uw mask) \
+{ \
+ sljit_ins m3, i2; \
+\
+ SLJIT_ASSERT(have_lscond2()); \
+ m3 = (sljit_ins)(mask & 0xf) << 32; \
+ i2 = (sljit_ins)(imm & 0xffffL) << 16; \
+\
+ return (pattern) | R36A(reg) | m3 | i2; \
+}
+
+/* LOAD HALFWORD IMMEDIATE ON CONDITION */
+SLJIT_S390X_RIEG(lochi, 0xec0000000042)
+SLJIT_S390X_RIEG(locghi, 0xec0000000046)
+
+#undef SLJIT_S390X_RIEG
+
+#define SLJIT_S390X_RILB(name, pattern, cond) \
+SLJIT_S390X_INSTRUCTION(name, sljit_gpr reg, sljit_sw ri) \
+{ \
+ SLJIT_ASSERT(cond); \
+\
+ return (pattern) | R36A(reg) | (sljit_ins)(ri & 0xffffffff); \
+}
+
+/* BRANCH RELATIVE AND SAVE LONG */
+SLJIT_S390X_RILB(brasl, 0xc00500000000, 1)
+
+/* LOAD ADDRESS RELATIVE LONG */
+SLJIT_S390X_RILB(larl, 0xc00000000000, 1)
+
+/* LOAD RELATIVE LONG */
+SLJIT_S390X_RILB(lgrl, 0xc40800000000, have_genext())
+
+#undef SLJIT_S390X_RILB
+
+SLJIT_S390X_INSTRUCTION(br, sljit_gpr target)
+{
+ return 0x07f0 | target;
+}
+
+SLJIT_S390X_INSTRUCTION(brc, sljit_uw mask, sljit_sw target)
+{
+ sljit_ins m1 = (sljit_ins)(mask & 0xf) << 20;
+ sljit_ins ri2 = (sljit_ins)target & 0xffff;
+ return 0xa7040000L | m1 | ri2;
+}
+
+SLJIT_S390X_INSTRUCTION(brcl, sljit_uw mask, sljit_sw target)
+{
+ sljit_ins m1 = (sljit_ins)(mask & 0xf) << 36;
+ sljit_ins ri2 = (sljit_ins)target & 0xffffffff;
+ return 0xc00400000000L | m1 | ri2;
+}
+
+SLJIT_S390X_INSTRUCTION(flogr, sljit_gpr dst, sljit_gpr src)
+{
+ SLJIT_ASSERT(have_eimm());
+ return 0xb9830000 | R8A(dst) | R0A(src);
+}
+
+/* INSERT PROGRAM MASK */
+SLJIT_S390X_INSTRUCTION(ipm, sljit_gpr dst)
+{
+ return 0xb2220000 | R4A(dst);
+}
+
+/* SET PROGRAM MASK */
+SLJIT_S390X_INSTRUCTION(spm, sljit_gpr dst)
+{
+ return 0x0400 | R4A(dst);
+}
+
+/* ROTATE THEN INSERT SELECTED BITS HIGH (ZERO) */
+SLJIT_S390X_INSTRUCTION(risbhgz, sljit_gpr dst, sljit_gpr src, sljit_u8 start, sljit_u8 end, sljit_u8 rot)
+{
+ return risbhg(dst, src, start, 0x8 | end, rot);
+}
+
+#undef SLJIT_S390X_INSTRUCTION
+
+static sljit_s32 update_zero_overflow(struct sljit_compiler *compiler, sljit_s32 op, sljit_gpr dst_r)
+{
+ /* Condition codes: bits 18 and 19.
+ Transformation:
+ 0 (zero and no overflow) : unchanged
+ 1 (non-zero and no overflow) : unchanged
+ 2 (zero and overflow) : decreased by 1
+ 3 (non-zero and overflow) : decreased by 1 if non-zero */
+ FAIL_IF(push_inst(compiler, brc(0xc, 2 + 2 + ((op & SLJIT_32) ? 1 : 2) + 2 + 3 + 1)));
+ FAIL_IF(push_inst(compiler, ipm(tmp1)));
+ FAIL_IF(push_inst(compiler, (op & SLJIT_32) ? or(dst_r, dst_r) : ogr(dst_r, dst_r)));
+ FAIL_IF(push_inst(compiler, brc(0x8, 2 + 3)));
+ FAIL_IF(push_inst(compiler, slfi(tmp1, 0x10000000)));
+ FAIL_IF(push_inst(compiler, spm(tmp1)));
+ return SLJIT_SUCCESS;
+}
+
+/* load 64-bit immediate into register without clobbering flags */
+static sljit_s32 push_load_imm_inst(struct sljit_compiler *compiler, sljit_gpr target, sljit_sw v)
+{
+ /* 4 byte instructions */
+ if (is_s16(v))
+ return push_inst(compiler, lghi(target, (sljit_s16)v));
+
+ if (((sljit_uw)v & ~(sljit_uw)0x000000000000ffff) == 0)
+ return push_inst(compiler, llill(target, (sljit_u16)v));
+
+ if (((sljit_uw)v & ~(sljit_uw)0x00000000ffff0000) == 0)
+ return push_inst(compiler, llilh(target, (sljit_u16)(v >> 16)));
+
+ if (((sljit_uw)v & ~(sljit_uw)0x0000ffff00000000) == 0)
+ return push_inst(compiler, llihl(target, (sljit_u16)(v >> 32)));
+
+ if (((sljit_uw)v & ~(sljit_uw)0xffff000000000000) == 0)
+ return push_inst(compiler, llihh(target, (sljit_u16)(v >> 48)));
+
+ /* 6 byte instructions (requires extended immediate facility) */
+ if (have_eimm()) {
+ if (is_s32(v))
+ return push_inst(compiler, lgfi(target, (sljit_s32)v));
+
+ if (((sljit_uw)v >> 32) == 0)
+ return push_inst(compiler, llilf(target, (sljit_u32)v));
+
+ if (((sljit_uw)v << 32) == 0)
+ return push_inst(compiler, llihf(target, (sljit_u32)((sljit_uw)v >> 32)));
+
+ FAIL_IF(push_inst(compiler, llilf(target, (sljit_u32)v)));
+ return push_inst(compiler, iihf(target, (sljit_u32)(v >> 32)));
+ }
+
+ /* TODO(mundaym): instruction sequences that don't use extended immediates */
+ abort();
+}
+
+struct addr {
+ sljit_gpr base;
+ sljit_gpr index;
+ sljit_s32 offset;
+};
+
+/* transform memory operand into D(X,B) form with a signed 20-bit offset */
+static sljit_s32 make_addr_bxy(struct sljit_compiler *compiler,
+ struct addr *addr, sljit_s32 mem, sljit_sw off,
+ sljit_gpr tmp /* clobbered, must not be r0 */)
+{
+ sljit_gpr base = r0;
+ sljit_gpr index = r0;
+
+ SLJIT_ASSERT(tmp != r0);
+ if (mem & REG_MASK)
+ base = gpr(mem & REG_MASK);
+
+ if (mem & OFFS_REG_MASK) {
+ index = gpr(OFFS_REG(mem));
+ if (off != 0) {
+ /* shift and put the result into tmp */
+ SLJIT_ASSERT(0 <= off && off < 64);
+ FAIL_IF(push_inst(compiler, sllg(tmp, index, (sljit_s32)off, 0)));
+ index = tmp;
+ off = 0; /* clear offset */
+ }
+ }
+ else if (!is_s20(off)) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp, off));
+ index = tmp;
+ off = 0; /* clear offset */
+ }
+ addr->base = base;
+ addr->index = index;
+ addr->offset = (sljit_s32)off;
+ return SLJIT_SUCCESS;
+}
+
+/* transform memory operand into D(X,B) form with an unsigned 12-bit offset */
+static sljit_s32 make_addr_bx(struct sljit_compiler *compiler,
+ struct addr *addr, sljit_s32 mem, sljit_sw off,
+ sljit_gpr tmp /* clobbered, must not be r0 */)
+{
+ sljit_gpr base = r0;
+ sljit_gpr index = r0;
+
+ SLJIT_ASSERT(tmp != r0);
+ if (mem & REG_MASK)
+ base = gpr(mem & REG_MASK);
+
+ if (mem & OFFS_REG_MASK) {
+ index = gpr(OFFS_REG(mem));
+ if (off != 0) {
+ /* shift and put the result into tmp */
+ SLJIT_ASSERT(0 <= off && off < 64);
+ FAIL_IF(push_inst(compiler, sllg(tmp, index, (sljit_s32)off, 0)));
+ index = tmp;
+ off = 0; /* clear offset */
+ }
+ }
+ else if (!is_u12(off)) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp, off));
+ index = tmp;
+ off = 0; /* clear offset */
+ }
+ addr->base = base;
+ addr->index = index;
+ addr->offset = (sljit_s32)off;
+ return SLJIT_SUCCESS;
+}
+
+#define EVAL(op, r, addr) op(r, addr.offset, addr.index, addr.base)
+#define WHEN(cond, r, i1, i2, addr) \
+ (cond) ? EVAL(i1, r, addr) : EVAL(i2, r, addr)
+
+/* May clobber tmp1. */
+static sljit_s32 load_word(struct sljit_compiler *compiler, sljit_gpr dst_r,
+ sljit_s32 src, sljit_sw srcw,
+ sljit_s32 is_32bit)
+{
+ struct addr addr;
+ sljit_ins ins;
+
+ SLJIT_ASSERT(src & SLJIT_MEM);
+
+ if (is_32bit && ((src & OFFS_REG_MASK) || is_u12(srcw) || !is_s20(srcw))) {
+ FAIL_IF(make_addr_bx(compiler, &addr, src, srcw, tmp1));
+ return push_inst(compiler, 0x58000000 /* l */ | R20A(dst_r) | R16A(addr.index) | R12A(addr.base) | (sljit_ins)addr.offset);
+ }
+
+ FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1));
+
+ ins = is_32bit ? 0xe30000000058 /* ly */ : 0xe30000000004 /* lg */;
+ return push_inst(compiler, ins | R36A(dst_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+}
+
+/* May clobber tmp1. */
+static sljit_s32 load_unsigned_word(struct sljit_compiler *compiler, sljit_gpr dst_r,
+ sljit_s32 src, sljit_sw srcw,
+ sljit_s32 is_32bit)
+{
+ struct addr addr;
+ sljit_ins ins;
+
+ SLJIT_ASSERT(src & SLJIT_MEM);
+
+ FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1));
+
+ ins = is_32bit ? 0xe30000000016 /* llgf */ : 0xe30000000004 /* lg */;
+ return push_inst(compiler, ins | R36A(dst_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+}
+
+/* May clobber tmp1. */
+static sljit_s32 store_word(struct sljit_compiler *compiler, sljit_gpr src_r,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 is_32bit)
+{
+ struct addr addr;
+ sljit_ins ins;
+
+ SLJIT_ASSERT(dst & SLJIT_MEM);
+
+ if (is_32bit && ((dst & OFFS_REG_MASK) || is_u12(dstw) || !is_s20(dstw))) {
+ FAIL_IF(make_addr_bx(compiler, &addr, dst, dstw, tmp1));
+ return push_inst(compiler, 0x50000000 /* st */ | R20A(src_r) | R16A(addr.index) | R12A(addr.base) | (sljit_ins)addr.offset);
+ }
+
+ FAIL_IF(make_addr_bxy(compiler, &addr, dst, dstw, tmp1));
+
+ ins = is_32bit ? 0xe30000000050 /* sty */ : 0xe30000000024 /* stg */;
+ return push_inst(compiler, ins | R36A(src_r) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+}
+
+#undef WHEN
+
+static sljit_s32 emit_move(struct sljit_compiler *compiler,
+ sljit_gpr dst_r,
+ sljit_s32 src, sljit_sw srcw)
+{
+ SLJIT_ASSERT(!IS_GPR_REG(src) || dst_r != gpr(src & REG_MASK));
+
+ if (src & SLJIT_IMM)
+ return push_load_imm_inst(compiler, dst_r, srcw);
+
+ if (src & SLJIT_MEM)
+ return load_word(compiler, dst_r, src, srcw, (compiler->mode & SLJIT_32) != 0);
+
+ sljit_gpr src_r = gpr(src & REG_MASK);
+ return push_inst(compiler, (compiler->mode & SLJIT_32) ? lr(dst_r, src_r) : lgr(dst_r, src_r));
+}
+
+static sljit_s32 emit_rr(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_gpr dst_r = tmp0;
+ sljit_gpr src_r = tmp1;
+ sljit_s32 needs_move = 1;
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = gpr(dst);
+
+ if (dst == src1)
+ needs_move = 0;
+ else if (dst == src2) {
+ dst_r = tmp0;
+ needs_move = 2;
+ }
+ }
+
+ if (needs_move)
+ FAIL_IF(emit_move(compiler, dst_r, src1, src1w));
+
+ if (FAST_IS_REG(src2))
+ src_r = gpr(src2);
+ else
+ FAIL_IF(emit_move(compiler, tmp1, src2, src2w));
+
+ FAIL_IF(push_inst(compiler, ins | R4A(dst_r) | R0A(src_r)));
+
+ if (needs_move != 2)
+ return SLJIT_SUCCESS;
+
+ dst_r = gpr(dst & REG_MASK);
+ return push_inst(compiler, (compiler->mode & SLJIT_32) ? lr(dst_r, tmp0) : lgr(dst_r, tmp0));
+}
+
+static sljit_s32 emit_rr1(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w)
+{
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst) : tmp0;
+ sljit_gpr src_r = tmp1;
+
+ if (FAST_IS_REG(src1))
+ src_r = gpr(src1);
+ else
+ FAIL_IF(emit_move(compiler, tmp1, src1, src1w));
+
+ return push_inst(compiler, ins | R4A(dst_r) | R0A(src_r));
+}
+
+static sljit_s32 emit_rrf(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ sljit_gpr src1_r = tmp0;
+ sljit_gpr src2_r = tmp1;
+
+ if (FAST_IS_REG(src1))
+ src1_r = gpr(src1);
+ else
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+
+ if (FAST_IS_REG(src2))
+ src2_r = gpr(src2);
+ else
+ FAIL_IF(emit_move(compiler, tmp1, src2, src2w));
+
+ return push_inst(compiler, ins | R4A(dst_r) | R0A(src1_r) | R12A(src2_r));
+}
+
+typedef enum {
+ RI_A,
+ RIL_A,
+} emit_ril_type;
+
+static sljit_s32 emit_ri(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_sw src2w,
+ emit_ril_type type)
+{
+ sljit_gpr dst_r = tmp0;
+ sljit_s32 needs_move = 1;
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = gpr(dst);
+
+ if (dst == src1)
+ needs_move = 0;
+ }
+
+ if (needs_move)
+ FAIL_IF(emit_move(compiler, dst_r, src1, src1w));
+
+ if (type == RIL_A)
+ return push_inst(compiler, ins | R36A(dst_r) | (src2w & 0xffffffff));
+ return push_inst(compiler, ins | R20A(dst_r) | (src2w & 0xffff));
+}
+
+static sljit_s32 emit_rie_d(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_sw src2w)
+{
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst) : tmp0;
+ sljit_gpr src_r = tmp0;
+
+ if (!FAST_IS_REG(src1))
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+ else
+ src_r = gpr(src1 & REG_MASK);
+
+ return push_inst(compiler, ins | R36A(dst_r) | R32A(src_r) | (sljit_ins)(src2w & 0xffff) << 16);
+}
+
+typedef enum {
+ RX_A,
+ RXY_A,
+} emit_rx_type;
+
+static sljit_s32 emit_rx(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w,
+ emit_rx_type type)
+{
+ sljit_gpr dst_r = tmp0;
+ sljit_s32 needs_move = 1;
+ sljit_gpr base, index;
+
+ SLJIT_ASSERT(src2 & SLJIT_MEM);
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = gpr(dst);
+
+ if (dst == src1)
+ needs_move = 0;
+ else if (dst == (src2 & REG_MASK) || (dst == OFFS_REG(src2))) {
+ dst_r = tmp0;
+ needs_move = 2;
+ }
+ }
+
+ if (needs_move)
+ FAIL_IF(emit_move(compiler, dst_r, src1, src1w));
+
+ base = gpr(src2 & REG_MASK);
+ index = tmp0;
+
+ if (src2 & OFFS_REG_MASK) {
+ index = gpr(OFFS_REG(src2));
+
+ if (src2w != 0) {
+ FAIL_IF(push_inst(compiler, sllg(tmp1, index, src2w & 0x3, 0)));
+ src2w = 0;
+ index = tmp1;
+ }
+ } else if ((type == RX_A && !is_u12(src2w)) || (type == RXY_A && !is_s20(src2w))) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, src2w));
+
+ if (src2 & REG_MASK)
+ index = tmp1;
+ else
+ base = tmp1;
+ src2w = 0;
+ }
+
+ if (type == RX_A)
+ ins |= R20A(dst_r) | R16A(index) | R12A(base) | (sljit_ins)src2w;
+ else
+ ins |= R36A(dst_r) | R32A(index) | R28A(base) | disp_s20((sljit_s32)src2w);
+
+ FAIL_IF(push_inst(compiler, ins));
+
+ if (needs_move != 2)
+ return SLJIT_SUCCESS;
+
+ dst_r = gpr(dst);
+ return push_inst(compiler, (compiler->mode & SLJIT_32) ? lr(dst_r, tmp0) : lgr(dst_r, tmp0));
+}
+
+static sljit_s32 emit_siy(struct sljit_compiler *compiler, sljit_ins ins,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_sw srcw)
+{
+ SLJIT_ASSERT(dst & SLJIT_MEM);
+
+ sljit_gpr dst_r = tmp1;
+
+ if (dst & OFFS_REG_MASK) {
+ sljit_gpr index = tmp1;
+
+ if ((dstw & 0x3) == 0)
+ index = gpr(OFFS_REG(dst));
+ else
+ FAIL_IF(push_inst(compiler, sllg(tmp1, index, dstw & 0x3, 0)));
+
+ FAIL_IF(push_inst(compiler, la(tmp1, 0, dst_r, index)));
+ dstw = 0;
+ }
+ else if (!is_s20(dstw)) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, dstw));
+
+ if (dst & REG_MASK)
+ FAIL_IF(push_inst(compiler, la(tmp1, 0, dst_r, tmp1)));
+
+ dstw = 0;
+ }
+ else
+ dst_r = gpr(dst & REG_MASK);
+
+ return push_inst(compiler, ins | ((sljit_ins)(srcw & 0xff) << 32) | R28A(dst_r) | disp_s20((sljit_s32)dstw));
+}
+
+struct ins_forms {
+ sljit_ins op_r;
+ sljit_ins op_gr;
+ sljit_ins op_rk;
+ sljit_ins op_grk;
+ sljit_ins op;
+ sljit_ins op_y;
+ sljit_ins op_g;
+};
+
+static sljit_s32 emit_commutative(struct sljit_compiler *compiler, const struct ins_forms *forms,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 mode = compiler->mode;
+ sljit_ins ins, ins_k;
+
+ if ((src1 | src2) & SLJIT_MEM) {
+ sljit_ins ins12, ins20;
+
+ if (mode & SLJIT_32) {
+ ins12 = forms->op;
+ ins20 = forms->op_y;
+ }
+ else {
+ ins12 = 0;
+ ins20 = forms->op_g;
+ }
+
+ if (ins12 && ins20) {
+ /* Extra instructions needed for address computation can be executed independently. */
+ if ((src2 & SLJIT_MEM) && (!(src1 & SLJIT_MEM)
+ || ((src1 & OFFS_REG_MASK) ? (src1w & 0x3) == 0 : is_s20(src1w)))) {
+ if ((src2 & OFFS_REG_MASK) || is_u12(src2w) || !is_s20(src2w))
+ return emit_rx(compiler, ins12, dst, src1, src1w, src2, src2w, RX_A);
+
+ return emit_rx(compiler, ins20, dst, src1, src1w, src2, src2w, RXY_A);
+ }
+
+ if (src1 & SLJIT_MEM) {
+ if ((src1 & OFFS_REG_MASK) || is_u12(src1w) || !is_s20(src1w))
+ return emit_rx(compiler, ins12, dst, src2, src2w, src1, src1w, RX_A);
+
+ return emit_rx(compiler, ins20, dst, src2, src2w, src1, src1w, RXY_A);
+ }
+ }
+ else if (ins12 || ins20) {
+ emit_rx_type rx_type;
+
+ if (ins12) {
+ rx_type = RX_A;
+ ins = ins12;
+ }
+ else {
+ rx_type = RXY_A;
+ ins = ins20;
+ }
+
+ if ((src2 & SLJIT_MEM) && (!(src1 & SLJIT_MEM)
+ || ((src1 & OFFS_REG_MASK) ? (src1w & 0x3) == 0 : (rx_type == RX_A ? is_u12(src1w) : is_s20(src1w)))))
+ return emit_rx(compiler, ins, dst, src1, src1w, src2, src2w, rx_type);
+
+ if (src1 & SLJIT_MEM)
+ return emit_rx(compiler, ins, dst, src2, src2w, src1, src1w, rx_type);
+ }
+ }
+
+ if (mode & SLJIT_32) {
+ ins = forms->op_r;
+ ins_k = forms->op_rk;
+ }
+ else {
+ ins = forms->op_gr;
+ ins_k = forms->op_grk;
+ }
+
+ SLJIT_ASSERT(ins != 0 || ins_k != 0);
+
+ if (ins && FAST_IS_REG(dst)) {
+ if (dst == src1)
+ return emit_rr(compiler, ins, dst, src1, src1w, src2, src2w);
+
+ if (dst == src2)
+ return emit_rr(compiler, ins, dst, src2, src2w, src1, src1w);
+ }
+
+ if (ins_k == 0)
+ return emit_rr(compiler, ins, dst, src1, src1w, src2, src2w);
+
+ return emit_rrf(compiler, ins_k, dst, src1, src1w, src2, src2w);
+}
+
+static sljit_s32 emit_non_commutative(struct sljit_compiler *compiler, const struct ins_forms *forms,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 mode = compiler->mode;
+ sljit_ins ins;
+
+ if (src2 & SLJIT_MEM) {
+ sljit_ins ins12, ins20;
+
+ if (mode & SLJIT_32) {
+ ins12 = forms->op;
+ ins20 = forms->op_y;
+ }
+ else {
+ ins12 = 0;
+ ins20 = forms->op_g;
+ }
+
+ if (ins12 && ins20) {
+ if ((src2 & OFFS_REG_MASK) || is_u12(src2w) || !is_s20(src2w))
+ return emit_rx(compiler, ins12, dst, src1, src1w, src2, src2w, RX_A);
+
+ return emit_rx(compiler, ins20, dst, src1, src1w, src2, src2w, RXY_A);
+ }
+ else if (ins12)
+ return emit_rx(compiler, ins12, dst, src1, src1w, src2, src2w, RX_A);
+ else if (ins20)
+ return emit_rx(compiler, ins20, dst, src1, src1w, src2, src2w, RXY_A);
+ }
+
+ ins = (mode & SLJIT_32) ? forms->op_rk : forms->op_grk;
+
+ if (ins == 0 || (FAST_IS_REG(dst) && dst == src1))
+ return emit_rr(compiler, (mode & SLJIT_32) ? forms->op_r : forms->op_gr, dst, src1, src1w, src2, src2w);
+
+ return emit_rrf(compiler, ins, dst, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_s390x_const *const_;
+ struct sljit_put_label *put_label;
+ sljit_sw executable_offset;
+ sljit_uw ins_size = 0; /* instructions */
+ sljit_uw pool_size = 0; /* literal pool */
+ sljit_uw pad_size;
+ sljit_uw i, j = 0;
+ struct sljit_memory_fragment *buf;
+ void *code, *code_ptr;
+ sljit_uw *pool, *pool_ptr;
+ sljit_sw source, offset; /* TODO(carenas): only need 32 bit */
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ /* branch handling */
+ label = compiler->labels;
+ jump = compiler->jumps;
+ put_label = compiler->put_labels;
+
+ /* TODO(carenas): compiler->executable_size could be calculated
+ * before to avoid the following loop (except for
+ * pool_size)
+ */
+ /* calculate the size of the code */
+ for (buf = compiler->buf; buf != NULL; buf = buf->next) {
+ sljit_uw len = buf->used_size / sizeof(sljit_ins);
+ sljit_ins *ibuf = (sljit_ins *)buf->memory;
+ for (i = 0; i < len; ++i, ++j) {
+ sljit_ins ins = ibuf[i];
+
+ /* TODO(carenas): instruction tag vs size/addr == j
+ * using instruction tags for const is creative
+ * but unlike all other architectures, and is not
+ * done consistently for all other objects.
+ * This might need reviewing later.
+ */
+ if (ins & sljit_ins_const) {
+ pool_size += sizeof(*pool);
+ ins &= ~sljit_ins_const;
+ }
+ if (label && label->size == j) {
+ label->size = ins_size;
+ label = label->next;
+ }
+ if (jump && jump->addr == j) {
+ if ((jump->flags & SLJIT_REWRITABLE_JUMP) || (jump->flags & JUMP_ADDR)) {
+ /* encoded: */
+ /* brasl %r14, <rel_addr> (or brcl <mask>, <rel_addr>) */
+ /* replace with: */
+ /* lgrl %r1, <pool_addr> */
+ /* bras %r14, %r1 (or bcr <mask>, %r1) */
+ pool_size += sizeof(*pool);
+ ins_size += 2;
+ }
+ jump = jump->next;
+ }
+ if (put_label && put_label->addr == j) {
+ pool_size += sizeof(*pool);
+ put_label = put_label->next;
+ }
+ ins_size += sizeof_ins(ins);
+ }
+ }
+
+ /* emit trailing label */
+ if (label && label->size == j) {
+ label->size = ins_size;
+ label = label->next;
+ }
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!put_label);
+
+ /* pad code size to 8 bytes so is accessible with half word offsets */
+ /* the literal pool needs to be doubleword aligned */
+ pad_size = ((ins_size + 7UL) & ~7UL) - ins_size;
+ SLJIT_ASSERT(pad_size < 8UL);
+
+ /* allocate target buffer */
+ code = SLJIT_MALLOC_EXEC(ins_size + pad_size + pool_size,
+ compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ code_ptr = code;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ /* TODO(carenas): pool is optional, and the ABI recommends it to
+ * be created before the function code, instead of
+ * globally; if generated code is too big could
+ * need offsets bigger than 32bit words and asser()
+ */
+ pool = (sljit_uw *)((sljit_uw)code + ins_size + pad_size);
+ pool_ptr = pool;
+ const_ = (struct sljit_s390x_const *)compiler->consts;
+
+ /* update label addresses */
+ label = compiler->labels;
+ while (label) {
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(
+ (sljit_uw)code_ptr + label->size, executable_offset);
+ label = label->next;
+ }
+
+ /* reset jumps */
+ jump = compiler->jumps;
+ put_label = compiler->put_labels;
+
+ /* emit the code */
+ j = 0;
+ for (buf = compiler->buf; buf != NULL; buf = buf->next) {
+ sljit_uw len = buf->used_size / sizeof(sljit_ins);
+ sljit_ins *ibuf = (sljit_ins *)buf->memory;
+ for (i = 0; i < len; ++i, ++j) {
+ sljit_ins ins = ibuf[i];
+ if (ins & sljit_ins_const) {
+ /* clear the const tag */
+ ins &= ~sljit_ins_const;
+
+ /* update instruction with relative address of constant */
+ source = (sljit_sw)code_ptr;
+ offset = (sljit_sw)pool_ptr - source;
+
+ SLJIT_ASSERT(!(offset & 1));
+ offset >>= 1; /* halfword (not byte) offset */
+ SLJIT_ASSERT(is_s32(offset));
+
+ ins |= (sljit_ins)offset & 0xffffffff;
+
+ /* update address */
+ const_->const_.addr = (sljit_uw)pool_ptr;
+
+ /* store initial value into pool and update pool address */
+ *(pool_ptr++) = (sljit_uw)const_->init_value;
+
+ /* move to next constant */
+ const_ = (struct sljit_s390x_const *)const_->const_.next;
+ }
+ if (jump && jump->addr == j) {
+ sljit_sw target = (sljit_sw)((jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target);
+ if ((jump->flags & SLJIT_REWRITABLE_JUMP) || (jump->flags & JUMP_ADDR)) {
+ jump->addr = (sljit_uw)pool_ptr;
+
+ /* load address into tmp1 */
+ source = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ offset = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(pool_ptr, executable_offset) - source;
+
+ SLJIT_ASSERT(!(offset & 1));
+ offset >>= 1;
+ SLJIT_ASSERT(is_s32(offset));
+
+ encode_inst(&code_ptr, lgrl(tmp1, offset & 0xffffffff));
+
+ /* store jump target into pool and update pool address */
+ *(pool_ptr++) = (sljit_uw)target;
+
+ /* branch to tmp1 */
+ sljit_ins op = (ins >> 32) & 0xf;
+ sljit_ins arg = (ins >> 36) & 0xf;
+ switch (op) {
+ case 4: /* brcl -> bcr */
+ ins = bcr(arg, tmp1);
+ break;
+ case 5: /* brasl -> basr */
+ ins = basr(arg, tmp1);
+ break;
+ default:
+ abort();
+ }
+ }
+ else {
+ jump->addr = (sljit_uw)code_ptr + 2;
+ source = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ offset = target - source;
+
+ /* offset must be halfword aligned */
+ SLJIT_ASSERT(!(offset & 1));
+ offset >>= 1;
+ SLJIT_ASSERT(is_s32(offset)); /* TODO(mundaym): handle arbitrary offsets */
+
+ /* patch jump target */
+ ins |= (sljit_ins)offset & 0xffffffff;
+ }
+ jump = jump->next;
+ }
+ if (put_label && put_label->addr == j) {
+ source = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+
+ /* store target into pool */
+ *pool_ptr = put_label->label->addr;
+ offset = (sljit_sw)SLJIT_ADD_EXEC_OFFSET(pool_ptr, executable_offset) - source;
+ pool_ptr++;
+
+ SLJIT_ASSERT(!(offset & 1));
+ offset >>= 1;
+ SLJIT_ASSERT(is_s32(offset));
+ ins |= (sljit_ins)offset & 0xffffffff;
+
+ put_label = put_label->next;
+ }
+ encode_inst(&code_ptr, ins);
+ }
+ }
+ SLJIT_ASSERT((sljit_u8 *)code + ins_size == code_ptr);
+ SLJIT_ASSERT((sljit_u8 *)pool + pool_size == (sljit_u8 *)pool_ptr);
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = ins_size;
+ code = SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+ code_ptr = SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ SLJIT_CACHE_FLUSH(code, code_ptr);
+ SLJIT_UPDATE_WX_FLAGS(code, code_ptr, 1);
+ return code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ /* TODO(mundaym): implement all */
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+ case SLJIT_HAS_CLZ:
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+ case SLJIT_HAS_CTZ:
+ return 2;
+ case SLJIT_HAS_CMOV:
+ return have_lscond1() ? 1 : 0;
+ }
+ return 0;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ return (type >= SLJIT_UNORDERED && type <= SLJIT_ORDERED_LESS_EQUAL);
+}
+
+/* --------------------------------------------------------------------- */
+/* Entry, exit */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_s32 offset, i, tmp;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ /* Saved registers are stored in callee allocated save area. */
+ SLJIT_ASSERT(gpr(SLJIT_FIRST_SAVED_REG) == r6 && gpr(SLJIT_S0) == r13);
+
+ offset = 2 * SSIZE_OF(sw);
+ if (saveds + scratches >= SLJIT_NUMBER_OF_REGISTERS) {
+ if (saved_arg_count == 0) {
+ FAIL_IF(push_inst(compiler, stmg(r6, r14, offset, r15)));
+ offset += 9 * SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, stmg(r6, r13 - (sljit_gpr)saved_arg_count, offset, r15)));
+ offset += (8 - saved_arg_count) * SSIZE_OF(sw);
+ }
+ } else {
+ if (scratches == SLJIT_FIRST_SAVED_REG) {
+ FAIL_IF(push_inst(compiler, stg(r6, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else if (scratches > SLJIT_FIRST_SAVED_REG) {
+ FAIL_IF(push_inst(compiler, stmg(r6, r6 + (sljit_gpr)(scratches - SLJIT_FIRST_SAVED_REG), offset, r15)));
+ offset += (scratches - (SLJIT_FIRST_SAVED_REG - 1)) * SSIZE_OF(sw);
+ }
+
+ if (saved_arg_count == 0) {
+ if (saveds == 0) {
+ FAIL_IF(push_inst(compiler, stg(r14, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, stmg(r14 - (sljit_gpr)saveds, r14, offset, r15)));
+ offset += (saveds + 1) * SSIZE_OF(sw);
+ }
+ } else if (saveds > saved_arg_count) {
+ if (saveds == saved_arg_count + 1) {
+ FAIL_IF(push_inst(compiler, stg(r14 - (sljit_gpr)saveds, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, stmg(r14 - (sljit_gpr)saveds, r13 - (sljit_gpr)saved_arg_count, offset, r15)));
+ offset += (saveds - saved_arg_count) * SSIZE_OF(sw);
+ }
+ }
+ }
+
+ if (saved_arg_count > 0) {
+ FAIL_IF(push_inst(compiler, stg(r14, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ }
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ FAIL_IF(push_inst(compiler, 0x60000000 /* std */ | F20(i) | R12A(r15) | (sljit_ins)offset));
+ offset += SSIZE_OF(sw);
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ FAIL_IF(push_inst(compiler, 0x60000000 /* std */ | F20(i) | R12A(r15) | (sljit_ins)offset));
+ offset += SSIZE_OF(sw);
+ }
+
+ local_size = (local_size + SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE + 0xf) & ~0xf;
+ compiler->local_size = local_size;
+
+ FAIL_IF(push_inst(compiler, 0xe30000000071 /* lay */ | R36A(r15) | R28A(r15) | disp_s20(-local_size)));
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ return SLJIT_SUCCESS;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ saved_arg_count = 0;
+ tmp = 0;
+ while (arg_types > 0) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ if (!(arg_types & SLJIT_ARG_TYPE_SCRATCH_REG)) {
+ FAIL_IF(push_inst(compiler, lgr(gpr(SLJIT_S0 - saved_arg_count), gpr(SLJIT_R0 + tmp))));
+ saved_arg_count++;
+ }
+ tmp++;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ compiler->local_size = (local_size + SLJIT_S390X_DEFAULT_STACK_FRAME_SIZE + 0xf) & ~0xf;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_gpr last_reg)
+{
+ sljit_s32 offset, i, tmp;
+ sljit_s32 local_size = compiler->local_size;
+ sljit_s32 saveds = compiler->saveds;
+ sljit_s32 scratches = compiler->scratches;
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+
+ if (is_u12(local_size))
+ FAIL_IF(push_inst(compiler, 0x41000000 /* ly */ | R20A(r15) | R12A(r15) | (sljit_ins)local_size));
+ else
+ FAIL_IF(push_inst(compiler, 0xe30000000071 /* lay */ | R36A(r15) | R28A(r15) | disp_s20(local_size)));
+
+ offset = 2 * SSIZE_OF(sw);
+ if (saveds + scratches >= SLJIT_NUMBER_OF_REGISTERS) {
+ if (kept_saveds_count == 0) {
+ FAIL_IF(push_inst(compiler, lmg(r6, last_reg, offset, r15)));
+ offset += 9 * SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, lmg(r6, r13 - (sljit_gpr)kept_saveds_count, offset, r15)));
+ offset += (8 - kept_saveds_count) * SSIZE_OF(sw);
+ }
+ } else {
+ if (scratches == SLJIT_FIRST_SAVED_REG) {
+ FAIL_IF(push_inst(compiler, lg(r6, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else if (scratches > SLJIT_FIRST_SAVED_REG) {
+ FAIL_IF(push_inst(compiler, lmg(r6, r6 + (sljit_gpr)(scratches - SLJIT_FIRST_SAVED_REG), offset, r15)));
+ offset += (scratches - (SLJIT_FIRST_SAVED_REG - 1)) * SSIZE_OF(sw);
+ }
+
+ if (kept_saveds_count == 0) {
+ if (saveds == 0) {
+ if (last_reg == r14)
+ FAIL_IF(push_inst(compiler, lg(r14, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else if (saveds == 1 && last_reg == r13) {
+ FAIL_IF(push_inst(compiler, lg(r13, offset, 0, r15)));
+ offset += 2 * SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, lmg(r14 - (sljit_gpr)saveds, last_reg, offset, r15)));
+ offset += (saveds + 1) * SSIZE_OF(sw);
+ }
+ } else if (saveds > kept_saveds_count) {
+ if (saveds == kept_saveds_count + 1) {
+ FAIL_IF(push_inst(compiler, lg(r14 - (sljit_gpr)saveds, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ } else {
+ FAIL_IF(push_inst(compiler, lmg(r14 - (sljit_gpr)saveds, r13 - (sljit_gpr)kept_saveds_count, offset, r15)));
+ offset += (saveds - kept_saveds_count) * SSIZE_OF(sw);
+ }
+ }
+ }
+
+ if (kept_saveds_count > 0) {
+ if (last_reg == r14)
+ FAIL_IF(push_inst(compiler, lg(r14, offset, 0, r15)));
+ offset += SSIZE_OF(sw);
+ }
+
+ tmp = SLJIT_FS0 - compiler->fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ FAIL_IF(push_inst(compiler, 0x68000000 /* ld */ | F20(i) | R12A(r15) | (sljit_ins)offset));
+ offset += SSIZE_OF(sw);
+ }
+
+ for (i = compiler->fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ FAIL_IF(push_inst(compiler, 0x68000000 /* ld */ | F20(i) | R12A(r15) | (sljit_ins)offset));
+ offset += SSIZE_OF(sw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ FAIL_IF(emit_stack_frame_release(compiler, r14));
+ return push_inst(compiler, br(r14)); /* return */
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(load_word(compiler, tmp1, src, srcw, 0 /* 64-bit */));
+ src = TMP_REG2;
+ srcw = 0;
+ } else if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, lgr(tmp1, gpr(src))));
+ src = TMP_REG2;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, r13));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ sljit_gpr arg0 = gpr(SLJIT_R0);
+ sljit_gpr arg1 = gpr(SLJIT_R1);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ op = GET_OPCODE(op) | (op & SLJIT_32);
+ switch (op) {
+ case SLJIT_BREAKPOINT:
+ /* The following invalid instruction is emitted by gdb. */
+ return push_inst(compiler, 0x0001 /* 2-byte trap */);
+ case SLJIT_NOP:
+ return push_inst(compiler, 0x0700 /* 2-byte nop */);
+ case SLJIT_LMUL_UW:
+ FAIL_IF(push_inst(compiler, mlgr(arg0, arg0)));
+ break;
+ case SLJIT_LMUL_SW:
+ /* signed multiplication from: */
+ /* Hacker's Delight, Second Edition: Chapter 8-3. */
+ FAIL_IF(push_inst(compiler, srag(tmp0, arg0, 63, 0)));
+ FAIL_IF(push_inst(compiler, srag(tmp1, arg1, 63, 0)));
+ FAIL_IF(push_inst(compiler, ngr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, ngr(tmp1, arg0)));
+
+ /* unsigned multiplication */
+ FAIL_IF(push_inst(compiler, mlgr(arg0, arg0)));
+
+ FAIL_IF(push_inst(compiler, sgr(arg0, tmp0)));
+ FAIL_IF(push_inst(compiler, sgr(arg0, tmp1)));
+ break;
+ case SLJIT_DIV_U32:
+ case SLJIT_DIVMOD_U32:
+ FAIL_IF(push_inst(compiler, lhi(tmp0, 0)));
+ FAIL_IF(push_inst(compiler, lr(tmp1, arg0)));
+ FAIL_IF(push_inst(compiler, dlr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, lr(arg0, tmp1))); /* quotient */
+ if (op == SLJIT_DIVMOD_U32)
+ return push_inst(compiler, lr(arg1, tmp0)); /* remainder */
+
+ return SLJIT_SUCCESS;
+ case SLJIT_DIV_S32:
+ case SLJIT_DIVMOD_S32:
+ FAIL_IF(push_inst(compiler, lhi(tmp0, 0)));
+ FAIL_IF(push_inst(compiler, lr(tmp1, arg0)));
+ FAIL_IF(push_inst(compiler, dr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, lr(arg0, tmp1))); /* quotient */
+ if (op == SLJIT_DIVMOD_S32)
+ return push_inst(compiler, lr(arg1, tmp0)); /* remainder */
+
+ return SLJIT_SUCCESS;
+ case SLJIT_DIV_UW:
+ case SLJIT_DIVMOD_UW:
+ FAIL_IF(push_inst(compiler, lghi(tmp0, 0)));
+ FAIL_IF(push_inst(compiler, lgr(tmp1, arg0)));
+ FAIL_IF(push_inst(compiler, dlgr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, lgr(arg0, tmp1))); /* quotient */
+ if (op == SLJIT_DIVMOD_UW)
+ return push_inst(compiler, lgr(arg1, tmp0)); /* remainder */
+
+ return SLJIT_SUCCESS;
+ case SLJIT_DIV_SW:
+ case SLJIT_DIVMOD_SW:
+ FAIL_IF(push_inst(compiler, lgr(tmp1, arg0)));
+ FAIL_IF(push_inst(compiler, dsgr(tmp0, arg1)));
+ FAIL_IF(push_inst(compiler, lgr(arg0, tmp1))); /* quotient */
+ if (op == SLJIT_DIVMOD_SW)
+ return push_inst(compiler, lgr(arg1, tmp0)); /* remainder */
+
+ return SLJIT_SUCCESS;
+ case SLJIT_ENDBR:
+ return SLJIT_SUCCESS;
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return SLJIT_SUCCESS;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+ /* swap result registers */
+ FAIL_IF(push_inst(compiler, lgr(tmp0, arg0)));
+ FAIL_IF(push_inst(compiler, lgr(arg0, arg1)));
+ return push_inst(compiler, lgr(arg1, tmp0));
+}
+
+static sljit_s32 sljit_emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 op, sljit_gpr dst_r, sljit_gpr src_r)
+{
+ sljit_s32 is_ctz = (GET_OPCODE(op) == SLJIT_CTZ);
+
+ if ((op & SLJIT_32) && src_r != tmp0) {
+ FAIL_IF(push_inst(compiler, 0xb9160000 /* llgfr */ | R4A(tmp0) | R0A(src_r)));
+ src_r = tmp0;
+ }
+
+ if (is_ctz) {
+ FAIL_IF(push_inst(compiler, ((op & SLJIT_32) ? 0x1300 /* lcr */ : 0xb9030000 /* lcgr */) | R4A(tmp1) | R0A(src_r)));
+
+ if (src_r == tmp0)
+ FAIL_IF(push_inst(compiler, ((op & SLJIT_32) ? 0x1400 /* nr */ : 0xb9800000 /* ngr */) | R4A(tmp0) | R0A(tmp1)));
+ else
+ FAIL_IF(push_inst(compiler, 0xb9e40000 /* ngrk */ | R12A(tmp1) | R4A(tmp0) | R0A(src_r)));
+
+ src_r = tmp0;
+ }
+
+ FAIL_IF(push_inst(compiler, 0xb9830000 /* flogr */ | R4A(tmp0) | R0A(src_r)));
+
+ if (is_ctz)
+ FAIL_IF(push_inst(compiler, 0xec00000000d9 /* aghik */ | R36A(tmp1) | R32A(tmp0) | ((sljit_ins)(-64 & 0xffff) << 16)));
+
+ if (op & SLJIT_32) {
+ if (!is_ctz && dst_r != tmp0)
+ return push_inst(compiler, 0xec00000000d9 /* aghik */ | R36A(dst_r) | R32A(tmp0) | ((sljit_ins)(-32 & 0xffff) << 16));
+
+ FAIL_IF(push_inst(compiler, 0xc20800000000 /* agfi */ | R36A(tmp0) | (sljit_u32)-32));
+ }
+
+ if (is_ctz)
+ FAIL_IF(push_inst(compiler, 0xec0000000057 /* rxsbg */ | R36A(tmp0) | R32A(tmp1) | ((sljit_ins)((op & SLJIT_32) ? 59 : 58) << 24) | (63 << 16) | ((sljit_ins)((op & SLJIT_32) ? 5 : 6) << 8)));
+
+ if (dst_r == tmp0)
+ return SLJIT_SUCCESS;
+
+ return push_inst(compiler, ((op & SLJIT_32) ? 0x1800 /* lr */ : 0xb9040000 /* lgr */) | R4A(dst_r) | R0A(tmp0));
+}
+
+/* LEVAL will be defined later with different parameters as needed */
+#define WHEN2(cond, i1, i2) (cond) ? LEVAL(i1) : LEVAL(i2)
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins ins;
+ struct addr mem;
+ sljit_gpr dst_r;
+ sljit_gpr src_r;
+ sljit_s32 opcode = GET_OPCODE(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ if (opcode >= SLJIT_MOV && opcode <= SLJIT_MOV_P) {
+ /* LOAD REGISTER */
+ if (FAST_IS_REG(dst) && FAST_IS_REG(src)) {
+ dst_r = gpr(dst);
+ src_r = gpr(src);
+ switch (opcode | (op & SLJIT_32)) {
+ /* 32-bit */
+ case SLJIT_MOV32_U8:
+ ins = llcr(dst_r, src_r);
+ break;
+ case SLJIT_MOV32_S8:
+ ins = lbr(dst_r, src_r);
+ break;
+ case SLJIT_MOV32_U16:
+ ins = llhr(dst_r, src_r);
+ break;
+ case SLJIT_MOV32_S16:
+ ins = lhr(dst_r, src_r);
+ break;
+ case SLJIT_MOV32:
+ if (dst_r == src_r)
+ return SLJIT_SUCCESS;
+ ins = lr(dst_r, src_r);
+ break;
+ /* 64-bit */
+ case SLJIT_MOV_U8:
+ ins = llgcr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_S8:
+ ins = lgbr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_U16:
+ ins = llghr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_S16:
+ ins = lghr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_U32:
+ ins = llgfr(dst_r, src_r);
+ break;
+ case SLJIT_MOV_S32:
+ ins = lgfr(dst_r, src_r);
+ break;
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+ if (dst_r == src_r)
+ return SLJIT_SUCCESS;
+ ins = lgr(dst_r, src_r);
+ break;
+ default:
+ ins = 0;
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ FAIL_IF(push_inst(compiler, ins));
+ return SLJIT_SUCCESS;
+ }
+ /* LOAD IMMEDIATE */
+ if (FAST_IS_REG(dst) && (src & SLJIT_IMM)) {
+ switch (opcode) {
+ case SLJIT_MOV_U8:
+ srcw = (sljit_sw)((sljit_u8)(srcw));
+ break;
+ case SLJIT_MOV_S8:
+ srcw = (sljit_sw)((sljit_s8)(srcw));
+ break;
+ case SLJIT_MOV_U16:
+ srcw = (sljit_sw)((sljit_u16)(srcw));
+ break;
+ case SLJIT_MOV_S16:
+ srcw = (sljit_sw)((sljit_s16)(srcw));
+ break;
+ case SLJIT_MOV_U32:
+ srcw = (sljit_sw)((sljit_u32)(srcw));
+ break;
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ srcw = (sljit_sw)((sljit_s32)(srcw));
+ break;
+ }
+ return push_load_imm_inst(compiler, gpr(dst), srcw);
+ }
+ /* LOAD */
+ /* TODO(carenas): avoid reg being defined later */
+ #define LEVAL(i) EVAL(i, reg, mem)
+ if (FAST_IS_REG(dst) && (src & SLJIT_MEM)) {
+ sljit_gpr reg = gpr(dst);
+
+ FAIL_IF(make_addr_bxy(compiler, &mem, src, srcw, tmp1));
+ /* TODO(carenas): convert all calls below to LEVAL */
+ switch (opcode | (op & SLJIT_32)) {
+ case SLJIT_MOV32_U8:
+ ins = llc(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV32_S8:
+ ins = lb(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV32_U16:
+ ins = llh(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV32_S16:
+ ins = WHEN2(is_u12(mem.offset), lh, lhy);
+ break;
+ case SLJIT_MOV32:
+ ins = WHEN2(is_u12(mem.offset), l, ly);
+ break;
+ case SLJIT_MOV_U8:
+ ins = LEVAL(llgc);
+ break;
+ case SLJIT_MOV_S8:
+ ins = lgb(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV_U16:
+ ins = LEVAL(llgh);
+ break;
+ case SLJIT_MOV_S16:
+ ins = lgh(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV_U32:
+ ins = LEVAL(llgf);
+ break;
+ case SLJIT_MOV_S32:
+ ins = lgf(reg, mem.offset, mem.index, mem.base);
+ break;
+ case SLJIT_MOV_P:
+ case SLJIT_MOV:
+ ins = lg(reg, mem.offset, mem.index, mem.base);
+ break;
+ default:
+ ins = 0;
+ SLJIT_UNREACHABLE();
+ break;
+ }
+ FAIL_IF(push_inst(compiler, ins));
+ return SLJIT_SUCCESS;
+ }
+ /* STORE and STORE IMMEDIATE */
+ if ((dst & SLJIT_MEM)
+ && (FAST_IS_REG(src) || (src & SLJIT_IMM))) {
+ sljit_gpr reg = FAST_IS_REG(src) ? gpr(src) : tmp0;
+ if (src & SLJIT_IMM) {
+ /* TODO(mundaym): MOVE IMMEDIATE? */
+ FAIL_IF(push_load_imm_inst(compiler, reg, srcw));
+ }
+ struct addr mem;
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ switch (opcode) {
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ return push_inst(compiler,
+ WHEN2(is_u12(mem.offset), stc, stcy));
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ return push_inst(compiler,
+ WHEN2(is_u12(mem.offset), sth, sthy));
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ return push_inst(compiler,
+ WHEN2(is_u12(mem.offset), st, sty));
+ case SLJIT_MOV_P:
+ case SLJIT_MOV:
+ FAIL_IF(push_inst(compiler, LEVAL(stg)));
+ return SLJIT_SUCCESS;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+ }
+ #undef LEVAL
+ /* MOVE CHARACTERS */
+ if ((dst & SLJIT_MEM) && (src & SLJIT_MEM)) {
+ struct addr mem;
+ FAIL_IF(make_addr_bxy(compiler, &mem, src, srcw, tmp1));
+ switch (opcode) {
+ case SLJIT_MOV_U8:
+ case SLJIT_MOV_S8:
+ FAIL_IF(push_inst(compiler,
+ EVAL(llgc, tmp0, mem)));
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ return push_inst(compiler,
+ EVAL(stcy, tmp0, mem));
+ case SLJIT_MOV_U16:
+ case SLJIT_MOV_S16:
+ FAIL_IF(push_inst(compiler,
+ EVAL(llgh, tmp0, mem)));
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ return push_inst(compiler,
+ EVAL(sthy, tmp0, mem));
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+ FAIL_IF(push_inst(compiler,
+ EVAL(ly, tmp0, mem)));
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ return push_inst(compiler,
+ EVAL(sty, tmp0, mem));
+ case SLJIT_MOV_P:
+ case SLJIT_MOV:
+ FAIL_IF(push_inst(compiler,
+ EVAL(lg, tmp0, mem)));
+ FAIL_IF(make_addr_bxy(compiler, &mem, dst, dstw, tmp1));
+ FAIL_IF(push_inst(compiler,
+ EVAL(stg, tmp0, mem)));
+ return SLJIT_SUCCESS;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+ }
+ SLJIT_UNREACHABLE();
+ }
+
+ SLJIT_ASSERT((src & SLJIT_IMM) == 0); /* no immediates */
+
+ dst_r = FAST_IS_REG(dst) ? gpr(REG_MASK & dst) : tmp0;
+ src_r = FAST_IS_REG(src) ? gpr(REG_MASK & src) : tmp0;
+
+ compiler->status_flags_state = op & (VARIABLE_FLAG_MASK | SLJIT_SET_Z);
+
+ /* TODO(mundaym): optimize loads and stores */
+ switch (opcode) {
+ case SLJIT_NOT:
+ if (src & SLJIT_MEM)
+ FAIL_IF(load_word(compiler, src_r, src, srcw, op & SLJIT_32));
+
+ /* emulate ~x with x^-1 */
+ if (!(op & SLJIT_32)) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, -1));
+ if (src_r != dst_r)
+ FAIL_IF(push_inst(compiler, lgr(dst_r, src_r)));
+
+ FAIL_IF(push_inst(compiler, xgr(dst_r, tmp1)));
+ break;
+ }
+
+ if (have_eimm())
+ FAIL_IF(push_inst(compiler, xilf(dst_r, 0xffffffff)));
+ else {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, -1));
+ if (src_r != dst_r)
+ FAIL_IF(push_inst(compiler, lr(dst_r, src_r)));
+
+ FAIL_IF(push_inst(compiler, xr(dst_r, tmp1)));
+ }
+ break;
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ if (src & SLJIT_MEM)
+ FAIL_IF(load_unsigned_word(compiler, src_r, src, srcw, op & SLJIT_32));
+
+ FAIL_IF(sljit_emit_clz_ctz(compiler, op, dst_r, src_r));
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+
+ if ((op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_SET_Z | SLJIT_SET_OVERFLOW))
+ FAIL_IF(update_zero_overflow(compiler, op, dst_r));
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, dst_r, dst, dstw, op & SLJIT_32);
+
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE int is_commutative(sljit_s32 op)
+{
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ case SLJIT_ADDC:
+ case SLJIT_MUL:
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ return 1;
+ }
+ return 0;
+}
+
+static const struct ins_forms add_forms = {
+ 0x1a00, /* ar */
+ 0xb9080000, /* agr */
+ 0xb9f80000, /* ark */
+ 0xb9e80000, /* agrk */
+ 0x5a000000, /* a */
+ 0xe3000000005a, /* ay */
+ 0xe30000000008, /* ag */
+};
+
+static const struct ins_forms logical_add_forms = {
+ 0x1e00, /* alr */
+ 0xb90a0000, /* algr */
+ 0xb9fa0000, /* alrk */
+ 0xb9ea0000, /* algrk */
+ 0x5e000000, /* al */
+ 0xe3000000005e, /* aly */
+ 0xe3000000000a, /* alg */
+};
+
+static sljit_s32 sljit_emit_add(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ int sets_overflow = (op & VARIABLE_FLAG_MASK) == SLJIT_SET_OVERFLOW;
+ int sets_zero_overflow = (op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_SET_Z | SLJIT_SET_OVERFLOW);
+ const struct ins_forms *forms;
+ sljit_ins ins;
+
+ if (src2 & SLJIT_IMM) {
+ if (!sets_zero_overflow && is_s8(src2w) && (src1 & SLJIT_MEM) && (dst == src1 && dstw == src1w)) {
+ if (sets_overflow)
+ ins = (op & SLJIT_32) ? 0xeb000000006a /* asi */ : 0xeb000000007a /* agsi */;
+ else
+ ins = (op & SLJIT_32) ? 0xeb000000006e /* alsi */ : 0xeb000000007e /* algsi */;
+ return emit_siy(compiler, ins, dst, dstw, src2w);
+ }
+
+ if (is_s16(src2w)) {
+ if (sets_overflow)
+ ins = (op & SLJIT_32) ? 0xec00000000d8 /* ahik */ : 0xec00000000d9 /* aghik */;
+ else
+ ins = (op & SLJIT_32) ? 0xec00000000da /* alhsik */ : 0xec00000000db /* alghsik */;
+ FAIL_IF(emit_rie_d(compiler, ins, dst, src1, src1w, src2w));
+ goto done;
+ }
+
+ if (!sets_overflow) {
+ if ((op & SLJIT_32) || is_u32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20b00000000 /* alfi */ : 0xc20a00000000 /* algfi */;
+ FAIL_IF(emit_ri(compiler, ins, dst, src1, src1w, src2w, RIL_A));
+ goto done;
+ }
+ if (is_u32(-src2w)) {
+ FAIL_IF(emit_ri(compiler, 0xc20400000000 /* slgfi */, dst, src1, src1w, -src2w, RIL_A));
+ goto done;
+ }
+ }
+ else if ((op & SLJIT_32) || is_s32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20900000000 /* afi */ : 0xc20800000000 /* agfi */;
+ FAIL_IF(emit_ri(compiler, ins, dst, src1, src1w, src2w, RIL_A));
+ goto done;
+ }
+ }
+
+ forms = sets_overflow ? &add_forms : &logical_add_forms;
+ FAIL_IF(emit_commutative(compiler, forms, dst, src1, src1w, src2, src2w));
+
+done:
+ if (sets_zero_overflow)
+ FAIL_IF(update_zero_overflow(compiler, op, FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0));
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+
+ return SLJIT_SUCCESS;
+}
+
+static const struct ins_forms sub_forms = {
+ 0x1b00, /* sr */
+ 0xb9090000, /* sgr */
+ 0xb9f90000, /* srk */
+ 0xb9e90000, /* sgrk */
+ 0x5b000000, /* s */
+ 0xe3000000005b, /* sy */
+ 0xe30000000009, /* sg */
+};
+
+static const struct ins_forms logical_sub_forms = {
+ 0x1f00, /* slr */
+ 0xb90b0000, /* slgr */
+ 0xb9fb0000, /* slrk */
+ 0xb9eb0000, /* slgrk */
+ 0x5f000000, /* sl */
+ 0xe3000000005f, /* sly */
+ 0xe3000000000b, /* slg */
+};
+
+static sljit_s32 sljit_emit_sub(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 flag_type = GET_FLAG_TYPE(op);
+ int sets_signed = (flag_type >= SLJIT_SIG_LESS && flag_type <= SLJIT_NOT_OVERFLOW);
+ int sets_zero_overflow = (op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == (SLJIT_SET_Z | SLJIT_SET_OVERFLOW);
+ const struct ins_forms *forms;
+ sljit_ins ins;
+
+ if (dst == (sljit_s32)tmp0 && flag_type <= SLJIT_SIG_LESS_EQUAL) {
+ int compare_signed = flag_type >= SLJIT_SIG_LESS;
+
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_COMPARE;
+
+ if (src2 & SLJIT_IMM) {
+ if (compare_signed || ((op & VARIABLE_FLAG_MASK) == 0 && is_s32(src2w)))
+ {
+ if ((op & SLJIT_32) || is_s32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20d00000000 /* cfi */ : 0xc20c00000000 /* cgfi */;
+ return emit_ri(compiler, ins, src1, src1, src1w, src2w, RIL_A);
+ }
+ }
+ else {
+ if ((op & SLJIT_32) || is_u32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20f00000000 /* clfi */ : 0xc20e00000000 /* clgfi */;
+ return emit_ri(compiler, ins, src1, src1, src1w, src2w, RIL_A);
+ }
+ if (is_s16(src2w))
+ return emit_rie_d(compiler, 0xec00000000db /* alghsik */, (sljit_s32)tmp0, src1, src1w, src2w);
+ }
+ }
+ else if (src2 & SLJIT_MEM) {
+ if ((op & SLJIT_32) && ((src2 & OFFS_REG_MASK) || is_u12(src2w))) {
+ ins = compare_signed ? 0x59000000 /* c */ : 0x55000000 /* cl */;
+ return emit_rx(compiler, ins, src1, src1, src1w, src2, src2w, RX_A);
+ }
+
+ if (compare_signed)
+ ins = (op & SLJIT_32) ? 0xe30000000059 /* cy */ : 0xe30000000020 /* cg */;
+ else
+ ins = (op & SLJIT_32) ? 0xe30000000055 /* cly */ : 0xe30000000021 /* clg */;
+ return emit_rx(compiler, ins, src1, src1, src1w, src2, src2w, RXY_A);
+ }
+
+ if (compare_signed)
+ ins = (op & SLJIT_32) ? 0x1900 /* cr */ : 0xb9200000 /* cgr */;
+ else
+ ins = (op & SLJIT_32) ? 0x1500 /* clr */ : 0xb9210000 /* clgr */;
+ return emit_rr(compiler, ins, src1, src1, src1w, src2, src2w);
+ }
+
+ if (src1 == SLJIT_IMM && src1w == 0 && (flag_type == 0 || sets_signed)) {
+ ins = (op & SLJIT_32) ? 0x1300 /* lcr */ : 0xb9030000 /* lcgr */;
+ FAIL_IF(emit_rr1(compiler, ins, dst, src2, src2w));
+ goto done;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ sljit_sw neg_src2w = -src2w;
+
+ if (sets_signed || neg_src2w != 0 || (op & (SLJIT_SET_Z | VARIABLE_FLAG_MASK)) == 0) {
+ if (!sets_zero_overflow && is_s8(neg_src2w) && (src1 & SLJIT_MEM) && (dst == src1 && dstw == src1w)) {
+ if (sets_signed)
+ ins = (op & SLJIT_32) ? 0xeb000000006a /* asi */ : 0xeb000000007a /* agsi */;
+ else
+ ins = (op & SLJIT_32) ? 0xeb000000006e /* alsi */ : 0xeb000000007e /* algsi */;
+ return emit_siy(compiler, ins, dst, dstw, neg_src2w);
+ }
+
+ if (is_s16(neg_src2w)) {
+ if (sets_signed)
+ ins = (op & SLJIT_32) ? 0xec00000000d8 /* ahik */ : 0xec00000000d9 /* aghik */;
+ else
+ ins = (op & SLJIT_32) ? 0xec00000000da /* alhsik */ : 0xec00000000db /* alghsik */;
+ FAIL_IF(emit_rie_d(compiler, ins, dst, src1, src1w, neg_src2w));
+ goto done;
+ }
+ }
+
+ if (!sets_signed) {
+ if ((op & SLJIT_32) || is_u32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20500000000 /* slfi */ : 0xc20400000000 /* slgfi */;
+ FAIL_IF(emit_ri(compiler, ins, dst, src1, src1w, src2w, RIL_A));
+ goto done;
+ }
+ if (is_u32(neg_src2w)) {
+ FAIL_IF(emit_ri(compiler, 0xc20a00000000 /* algfi */, dst, src1, src1w, neg_src2w, RIL_A));
+ goto done;
+ }
+ }
+ else if ((op & SLJIT_32) || is_s32(neg_src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20900000000 /* afi */ : 0xc20800000000 /* agfi */;
+ FAIL_IF(emit_ri(compiler, ins, dst, src1, src1w, neg_src2w, RIL_A));
+ goto done;
+ }
+ }
+
+ forms = sets_signed ? &sub_forms : &logical_sub_forms;
+ FAIL_IF(emit_non_commutative(compiler, forms, dst, src1, src1w, src2, src2w));
+
+done:
+ if (sets_signed) {
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+
+ if ((op & VARIABLE_FLAG_MASK) != SLJIT_SET_OVERFLOW) {
+ /* In case of overflow, the sign bit of the two source operands must be different, and
+ - the first operand is greater if the sign bit of the result is set
+ - the first operand is less if the sign bit of the result is not set
+ The -result operation sets the corrent sign, because the result cannot be zero.
+ The overflow is considered greater, since the result must be equal to INT_MIN so its sign bit is set. */
+ FAIL_IF(push_inst(compiler, brc(0xe, 2 + 2)));
+ FAIL_IF(push_inst(compiler, (op & SLJIT_32) ? lcr(tmp1, dst_r) : lcgr(tmp1, dst_r)));
+ }
+ else if (op & SLJIT_SET_Z)
+ FAIL_IF(update_zero_overflow(compiler, op, dst_r));
+ }
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+
+ return SLJIT_SUCCESS;
+}
+
+static const struct ins_forms multiply_forms = {
+ 0xb2520000, /* msr */
+ 0xb90c0000, /* msgr */
+ 0xb9fd0000, /* msrkc */
+ 0xb9ed0000, /* msgrkc */
+ 0x71000000, /* ms */
+ 0xe30000000051, /* msy */
+ 0xe3000000000c, /* msg */
+};
+
+static const struct ins_forms multiply_overflow_forms = {
+ 0,
+ 0,
+ 0xb9fd0000, /* msrkc */
+ 0xb9ed0000, /* msgrkc */
+ 0,
+ 0xe30000000053, /* msc */
+ 0xe30000000083, /* msgc */
+};
+
+static sljit_s32 sljit_emit_multiply(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins ins;
+
+ if (HAS_FLAGS(op)) {
+ /* if have_misc2 fails, this operation should be emulated. 32 bit emulation:
+ FAIL_IF(push_inst(compiler, lgfr(tmp0, src1_r)));
+ FAIL_IF(push_inst(compiler, msgfr(tmp0, src2_r)));
+ if (dst_r != tmp0) {
+ FAIL_IF(push_inst(compiler, lr(dst_r, tmp0)));
+ }
+ FAIL_IF(push_inst(compiler, aih(tmp0, 1)));
+ FAIL_IF(push_inst(compiler, nihf(tmp0, ~1U)));
+ FAIL_IF(push_inst(compiler, ipm(tmp1)));
+ FAIL_IF(push_inst(compiler, oilh(tmp1, 0x2000))); */
+
+ return emit_commutative(compiler, &multiply_overflow_forms, dst, src1, src1w, src2, src2w);
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (is_s16(src2w)) {
+ ins = (op & SLJIT_32) ? 0xa70c0000 /* mhi */ : 0xa70d0000 /* mghi */;
+ return emit_ri(compiler, ins, dst, src1, src1w, src2w, RI_A);
+ }
+
+ if (is_s32(src2w)) {
+ ins = (op & SLJIT_32) ? 0xc20100000000 /* msfi */ : 0xc20000000000 /* msgfi */;
+ return emit_ri(compiler, ins, dst, src1, src1w, src2w, RIL_A);
+ }
+ }
+
+ return emit_commutative(compiler, &multiply_forms, dst, src1, src1w, src2, src2w);
+}
+
+static sljit_s32 sljit_emit_bitwise_imm(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_uw imm, sljit_s32 count16)
+{
+ sljit_s32 mode = compiler->mode;
+ sljit_gpr dst_r = tmp0;
+ sljit_s32 needs_move = 1;
+
+ if (IS_GPR_REG(dst)) {
+ dst_r = gpr(dst & REG_MASK);
+ if (dst == src1)
+ needs_move = 0;
+ }
+
+ if (needs_move)
+ FAIL_IF(emit_move(compiler, dst_r, src1, src1w));
+
+ if (type == SLJIT_AND) {
+ if (!(mode & SLJIT_32))
+ FAIL_IF(push_inst(compiler, 0xc00a00000000 /* nihf */ | R36A(dst_r) | (imm >> 32)));
+ return push_inst(compiler, 0xc00b00000000 /* nilf */ | R36A(dst_r) | (imm & 0xffffffff));
+ }
+ else if (type == SLJIT_OR) {
+ if (count16 >= 3) {
+ FAIL_IF(push_inst(compiler, 0xc00c00000000 /* oihf */ | R36A(dst_r) | (imm >> 32)));
+ return push_inst(compiler, 0xc00d00000000 /* oilf */ | R36A(dst_r) | (imm & 0xffffffff));
+ }
+
+ if (count16 >= 2) {
+ if ((imm & 0x00000000ffffffffull) == 0)
+ return push_inst(compiler, 0xc00c00000000 /* oihf */ | R36A(dst_r) | (imm >> 32));
+ if ((imm & 0xffffffff00000000ull) == 0)
+ return push_inst(compiler, 0xc00d00000000 /* oilf */ | R36A(dst_r) | (imm & 0xffffffff));
+ }
+
+ if ((imm & 0xffff000000000000ull) != 0)
+ FAIL_IF(push_inst(compiler, 0xa5080000 /* oihh */ | R20A(dst_r) | (imm >> 48)));
+ if ((imm & 0x0000ffff00000000ull) != 0)
+ FAIL_IF(push_inst(compiler, 0xa5090000 /* oihl */ | R20A(dst_r) | ((imm >> 32) & 0xffff)));
+ if ((imm & 0x00000000ffff0000ull) != 0)
+ FAIL_IF(push_inst(compiler, 0xa50a0000 /* oilh */ | R20A(dst_r) | ((imm >> 16) & 0xffff)));
+ if ((imm & 0x000000000000ffffull) != 0 || imm == 0)
+ return push_inst(compiler, 0xa50b0000 /* oill */ | R20A(dst_r) | (imm & 0xffff));
+ return SLJIT_SUCCESS;
+ }
+
+ if ((imm & 0xffffffff00000000ull) != 0)
+ FAIL_IF(push_inst(compiler, 0xc00600000000 /* xihf */ | R36A(dst_r) | (imm >> 32)));
+ if ((imm & 0x00000000ffffffffull) != 0 || imm == 0)
+ return push_inst(compiler, 0xc00700000000 /* xilf */ | R36A(dst_r) | (imm & 0xffffffff));
+ return SLJIT_SUCCESS;
+}
+
+static const struct ins_forms bitwise_and_forms = {
+ 0x1400, /* nr */
+ 0xb9800000, /* ngr */
+ 0xb9f40000, /* nrk */
+ 0xb9e40000, /* ngrk */
+ 0x54000000, /* n */
+ 0xe30000000054, /* ny */
+ 0xe30000000080, /* ng */
+};
+
+static const struct ins_forms bitwise_or_forms = {
+ 0x1600, /* or */
+ 0xb9810000, /* ogr */
+ 0xb9f60000, /* ork */
+ 0xb9e60000, /* ogrk */
+ 0x56000000, /* o */
+ 0xe30000000056, /* oy */
+ 0xe30000000081, /* og */
+};
+
+static const struct ins_forms bitwise_xor_forms = {
+ 0x1700, /* xr */
+ 0xb9820000, /* xgr */
+ 0xb9f70000, /* xrk */
+ 0xb9e70000, /* xgrk */
+ 0x57000000, /* x */
+ 0xe30000000057, /* xy */
+ 0xe30000000082, /* xg */
+};
+
+static sljit_s32 sljit_emit_bitwise(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 type = GET_OPCODE(op);
+ const struct ins_forms *forms;
+
+ if ((src2 & SLJIT_IMM) && (!(op & SLJIT_SET_Z) || (type == SLJIT_AND && dst == (sljit_s32)tmp0))) {
+ sljit_s32 count16 = 0;
+ sljit_uw imm = (sljit_uw)src2w;
+
+ if (op & SLJIT_32)
+ imm &= 0xffffffffull;
+
+ if ((imm & 0x000000000000ffffull) != 0 || imm == 0)
+ count16++;
+ if ((imm & 0x00000000ffff0000ull) != 0)
+ count16++;
+ if ((imm & 0x0000ffff00000000ull) != 0)
+ count16++;
+ if ((imm & 0xffff000000000000ull) != 0)
+ count16++;
+
+ if (type == SLJIT_AND && dst == (sljit_s32)tmp0 && count16 == 1) {
+ sljit_gpr src_r = tmp0;
+
+ if (FAST_IS_REG(src1))
+ src_r = gpr(src1 & REG_MASK);
+ else
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+
+ if ((imm & 0x000000000000ffffull) != 0 || imm == 0)
+ return push_inst(compiler, 0xa7010000 | R20A(src_r) | imm);
+ if ((imm & 0x00000000ffff0000ull) != 0)
+ return push_inst(compiler, 0xa7000000 | R20A(src_r) | (imm >> 16));
+ if ((imm & 0x0000ffff00000000ull) != 0)
+ return push_inst(compiler, 0xa7030000 | R20A(src_r) | (imm >> 32));
+ return push_inst(compiler, 0xa7020000 | R20A(src_r) | (imm >> 48));
+ }
+
+ if (!(op & SLJIT_SET_Z))
+ return sljit_emit_bitwise_imm(compiler, type, dst, src1, src1w, imm, count16);
+ }
+
+ if (type == SLJIT_AND)
+ forms = &bitwise_and_forms;
+ else if (type == SLJIT_OR)
+ forms = &bitwise_or_forms;
+ else
+ forms = &bitwise_xor_forms;
+
+ return emit_commutative(compiler, forms, dst, src1, src1w, src2, src2w);
+}
+
+static sljit_s32 sljit_emit_shift(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 type = GET_OPCODE(op);
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ sljit_gpr src_r = tmp0;
+ sljit_gpr base_r = tmp0;
+ sljit_ins imm = 0;
+ sljit_ins ins;
+
+ if (FAST_IS_REG(src1))
+ src_r = gpr(src1);
+ else
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+
+ if (!(src2 & SLJIT_IMM)) {
+ if (FAST_IS_REG(src2))
+ base_r = gpr(src2);
+ else {
+ FAIL_IF(emit_move(compiler, tmp1, src2, src2w));
+ base_r = tmp1;
+ }
+
+ if ((op & SLJIT_32) && (type == SLJIT_MSHL || type == SLJIT_MLSHR || type == SLJIT_MASHR)) {
+ if (base_r != tmp1) {
+ FAIL_IF(push_inst(compiler, 0xec0000000055 /* risbg */ | R36A(tmp1) | R32A(base_r) | (59 << 24) | (1 << 23) | (63 << 16)));
+ base_r = tmp1;
+ } else
+ FAIL_IF(push_inst(compiler, 0xa5070000 /* nill */ | R20A(tmp1) | 0x1f));
+ }
+ } else
+ imm = (sljit_ins)(src2w & ((op & SLJIT_32) ? 0x1f : 0x3f));
+
+ if ((op & SLJIT_32) && dst_r == src_r) {
+ if (type == SLJIT_SHL || type == SLJIT_MSHL)
+ ins = 0x89000000 /* sll */;
+ else if (type == SLJIT_LSHR || type == SLJIT_MLSHR)
+ ins = 0x88000000 /* srl */;
+ else
+ ins = 0x8a000000 /* sra */;
+
+ FAIL_IF(push_inst(compiler, ins | R20A(dst_r) | R12A(base_r) | imm));
+ } else {
+ if (type == SLJIT_SHL || type == SLJIT_MSHL)
+ ins = (op & SLJIT_32) ? 0xeb00000000df /* sllk */ : 0xeb000000000d /* sllg */;
+ else if (type == SLJIT_LSHR || type == SLJIT_MLSHR)
+ ins = (op & SLJIT_32) ? 0xeb00000000de /* srlk */ : 0xeb000000000c /* srlg */;
+ else
+ ins = (op & SLJIT_32) ? 0xeb00000000dc /* srak */ : 0xeb000000000a /* srag */;
+
+ FAIL_IF(push_inst(compiler, ins | R36A(dst_r) | R32A(src_r) | R28A(base_r) | (imm << 16)));
+ }
+
+ if ((op & SLJIT_SET_Z) && type != SLJIT_ASHR)
+ return push_inst(compiler, (op & SLJIT_32) ? or(dst_r, dst_r) : ogr(dst_r, dst_r));
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 sljit_emit_rotate(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ sljit_gpr src_r = tmp0;
+ sljit_gpr base_r = tmp0;
+ sljit_ins imm = 0;
+ sljit_ins ins;
+
+ if (FAST_IS_REG(src1))
+ src_r = gpr(src1);
+ else
+ FAIL_IF(emit_move(compiler, tmp0, src1, src1w));
+
+ if (!(src2 & SLJIT_IMM)) {
+ if (FAST_IS_REG(src2))
+ base_r = gpr(src2);
+ else {
+ FAIL_IF(emit_move(compiler, tmp1, src2, src2w));
+ base_r = tmp1;
+ }
+ }
+
+ if (GET_OPCODE(op) == SLJIT_ROTR) {
+ if (!(src2 & SLJIT_IMM)) {
+ ins = (op & SLJIT_32) ? 0x1300 /* lcr */ : 0xb9030000 /* lcgr */;
+ FAIL_IF(push_inst(compiler, ins | R4A(tmp1) | R0A(base_r)));
+ base_r = tmp1;
+ } else
+ src2w = -src2w;
+ }
+
+ if (src2 & SLJIT_IMM)
+ imm = (sljit_ins)(src2w & ((op & SLJIT_32) ? 0x1f : 0x3f));
+
+ ins = (op & SLJIT_32) ? 0xeb000000001d /* rll */ : 0xeb000000001c /* rllg */;
+ return push_inst(compiler, ins | R36A(dst_r) | R32A(src_r) | R28A(base_r) | (imm << 16));
+}
+
+static const struct ins_forms addc_forms = {
+ 0xb9980000, /* alcr */
+ 0xb9880000, /* alcgr */
+ 0,
+ 0,
+ 0,
+ 0xe30000000098, /* alc */
+ 0xe30000000088, /* alcg */
+};
+
+static const struct ins_forms subc_forms = {
+ 0xb9990000, /* slbr */
+ 0xb9890000, /* slbgr */
+ 0,
+ 0,
+ 0,
+ 0xe30000000099, /* slb */
+ 0xe30000000089, /* slbg */
+};
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ compiler->mode = op & SLJIT_32;
+ compiler->status_flags_state = op & (VARIABLE_FLAG_MASK | SLJIT_SET_Z);
+
+ if (is_commutative(op) && (src1 & SLJIT_IMM) && !(src2 & SLJIT_IMM)) {
+ src1 ^= src2;
+ src2 ^= src1;
+ src1 ^= src2;
+
+ src1w ^= src2w;
+ src2w ^= src1w;
+ src1w ^= src2w;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_ADD;
+ return sljit_emit_add(compiler, op, dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ADDC:
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_ADD;
+ FAIL_IF(emit_commutative(compiler, &addc_forms, dst, src1, src1w, src2, src2w));
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+ return SLJIT_SUCCESS;
+ case SLJIT_SUB:
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_SUB;
+ return sljit_emit_sub(compiler, op, dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_SUBC:
+ compiler->status_flags_state |= SLJIT_CURRENT_FLAGS_SUB;
+ FAIL_IF(emit_non_commutative(compiler, &subc_forms, dst, src1, src1w, src2, src2w));
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+ return SLJIT_SUCCESS;
+ case SLJIT_MUL:
+ FAIL_IF(sljit_emit_multiply(compiler, op, dst, src1, src1w, src2, src2w));
+ break;
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ FAIL_IF(sljit_emit_bitwise(compiler, op, dst, src1, src1w, src2, src2w));
+ break;
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ FAIL_IF(sljit_emit_shift(compiler, op, dst, src1, src1w, src2, src2w));
+ break;
+ case SLJIT_ROTL:
+ case SLJIT_ROTR:
+ FAIL_IF(sljit_emit_rotate(compiler, op, dst, src1, src1w, src2, src2w));
+ break;
+ }
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, tmp0, dst, dstw, op & SLJIT_32);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, (sljit_s32)tmp0, 0, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 is_right;
+ sljit_sw bit_length = (op & SLJIT_32) ? 32 : 64;
+ sljit_gpr src_dst_r = gpr(src_dst);
+ sljit_gpr src1_r = tmp0;
+ sljit_gpr src2_r = tmp1;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+
+ is_right = (GET_OPCODE(op) == SLJIT_LSHR || GET_OPCODE(op) == SLJIT_MLSHR);
+
+ if (src_dst == src1) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, (is_right ? SLJIT_ROTR : SLJIT_ROTL) | (op & SLJIT_32), src_dst, 0, src_dst, 0, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ if (src1 & SLJIT_MEM)
+ FAIL_IF(load_word(compiler, tmp0, src1, src1w, op & SLJIT_32));
+ else if (src1 & SLJIT_IMM)
+ FAIL_IF(push_load_imm_inst(compiler, tmp0, src1w));
+ else
+ src1_r = gpr(src1);
+
+ if (src2 & SLJIT_IMM) {
+ src2w &= bit_length - 1;
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ } else if (!(src2 & SLJIT_MEM))
+ src2_r = gpr(src2);
+ else
+ FAIL_IF(load_word(compiler, tmp1, src2, src2w, op & SLJIT_32));
+
+ if (src2 & SLJIT_IMM) {
+ if (op & SLJIT_32) {
+ ins = is_right ? 0x88000000 /* srl */ : 0x89000000 /* sll */;
+ FAIL_IF(push_inst(compiler, ins | R20A(src_dst_r) | (sljit_ins)src2w));
+ } else {
+ ins = is_right ? 0xeb000000000c /* srlg */ : 0xeb000000000d /* sllg */;
+ FAIL_IF(push_inst(compiler, ins | R36A(src_dst_r) | R32A(src_dst_r) | ((sljit_ins)src2w << 16)));
+ }
+
+ ins = 0xec0000000055 /* risbg */;
+
+ if (is_right) {
+ src2w = bit_length - src2w;
+ ins |= ((sljit_ins)(64 - bit_length) << 24) | ((sljit_ins)(63 - src2w) << 16) | ((sljit_ins)src2w << 8);
+ } else
+ ins |= ((sljit_ins)(64 - src2w) << 24) | ((sljit_ins)63 << 16) | ((sljit_ins)src2w << 8);
+
+ return push_inst(compiler, ins | R36A(src_dst_r) | R32A(src1_r));
+ }
+
+ if (op & SLJIT_32) {
+ if (GET_OPCODE(op) == SLJIT_MSHL || GET_OPCODE(op) == SLJIT_MLSHR) {
+ if (src2_r != tmp1) {
+ FAIL_IF(push_inst(compiler, 0xec0000000055 /* risbg */ | R36A(tmp1) | R32A(src2_r) | (59 << 24) | (1 << 23) | (63 << 16)));
+ src2_r = tmp1;
+ } else
+ FAIL_IF(push_inst(compiler, 0xa5070000 /* nill */ | R20A(tmp1) | 0x1f));
+ }
+
+ ins = is_right ? 0x88000000 /* srl */ : 0x89000000 /* sll */;
+ FAIL_IF(push_inst(compiler, ins | R20A(src_dst_r) | R12A(src2_r)));
+
+ if (src2_r != tmp1) {
+ FAIL_IF(push_inst(compiler, 0xa50f0000 /* llill */ | R20A(tmp1) | 0x1f));
+ FAIL_IF(push_inst(compiler, 0x1700 /* xr */ | R4A(tmp1) | R0A(src2_r)));
+ } else
+ FAIL_IF(push_inst(compiler, 0xc00700000000 /* xilf */ | R36A(tmp1) | 0x1f));
+
+ if (src1_r == tmp0) {
+ ins = is_right ? 0x89000000 /* sll */ : 0x88000000 /* srl */;
+ FAIL_IF(push_inst(compiler, ins | R20A(tmp0) | R12A(tmp1) | 0x1));
+ } else {
+ ins = is_right ? 0xeb00000000df /* sllk */ : 0xeb00000000de /* srlk */;
+ FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | R28A(tmp1) | (0x1 << 16)));
+ }
+
+ return push_inst(compiler, 0x1600 /* or */ | R4A(src_dst_r) | R0A(tmp0));
+ }
+
+ ins = is_right ? 0xeb000000000c /* srlg */ : 0xeb000000000d /* sllg */;
+ FAIL_IF(push_inst(compiler, ins | R36A(src_dst_r) | R32A(src_dst_r) | R28A(src2_r)));
+
+ ins = is_right ? 0xeb000000000d /* sllg */ : 0xeb000000000c /* srlg */;
+
+ if (!(op & SLJIT_SHIFT_INTO_NON_ZERO)) {
+ if (src2_r != tmp1)
+ FAIL_IF(push_inst(compiler, 0xa50f0000 /* llill */ | R20A(tmp1) | 0x3f));
+
+ FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | (0x1 << 16)));
+ src1_r = tmp0;
+
+ if (src2_r != tmp1)
+ FAIL_IF(push_inst(compiler, 0xb9820000 /* xgr */ | R4A(tmp1) | R0A(src2_r)));
+ else
+ FAIL_IF(push_inst(compiler, 0xc00700000000 /* xilf */ | R36A(tmp1) | 0x3f));
+ } else
+ FAIL_IF(push_inst(compiler, 0xb9030000 /* lcgr */ | R4A(tmp1) | R0A(src2_r)));
+
+ FAIL_IF(push_inst(compiler, ins | R36A(tmp0) | R32A(src1_r) | R28A(tmp1)));
+ return push_inst(compiler, 0xb9810000 /* ogr */ | R4A(src_dst_r) | R0A(tmp0));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(
+ struct sljit_compiler *compiler,
+ sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_gpr src_r;
+ struct addr addr;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ src_r = FAST_IS_REG(src) ? gpr(src) : tmp1;
+ if (src & SLJIT_MEM)
+ FAIL_IF(load_word(compiler, tmp1, src, srcw, 0));
+
+ return push_inst(compiler, br(src_r));
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ return SLJIT_SUCCESS;
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ FAIL_IF(make_addr_bxy(compiler, &addr, src, srcw, tmp1));
+ return push_inst(compiler, 0xe31000000036 /* pfd */ | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+ default:
+ return SLJIT_SUCCESS;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+ return (sljit_s32)gpr(reg);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+ return (sljit_s32)fgpr(reg);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ sljit_ins ins = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ memcpy((sljit_u8 *)&ins + sizeof(ins) - size, instruction, size);
+ return push_inst(compiler, ins);
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+#define FLOAT_LOAD 0
+#define FLOAT_STORE 1
+
+static sljit_s32 float_mem(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ struct addr addr;
+ sljit_ins ins;
+
+ SLJIT_ASSERT(mem & SLJIT_MEM);
+
+ if ((mem & OFFS_REG_MASK) || is_u12(memw) || !is_s20(memw)) {
+ FAIL_IF(make_addr_bx(compiler, &addr, mem, memw, tmp1));
+
+ if (op & FLOAT_STORE)
+ ins = (op & SLJIT_32) ? 0x70000000 /* ste */ : 0x60000000 /* std */;
+ else
+ ins = (op & SLJIT_32) ? 0x78000000 /* le */ : 0x68000000 /* ld */;
+
+ return push_inst(compiler, ins | F20(reg) | R16A(addr.index) | R12A(addr.base) | (sljit_ins)addr.offset);
+ }
+
+ FAIL_IF(make_addr_bxy(compiler, &addr, mem, memw, tmp1));
+
+ if (op & FLOAT_STORE)
+ ins = (op & SLJIT_32) ? 0xed0000000066 /* stey */ : 0xed0000000067 /* stdy */;
+ else
+ ins = (op & SLJIT_32) ? 0xed0000000064 /* ley */ : 0xed0000000065 /* ldy */;
+
+ return push_inst(compiler, ins | F36(reg) | R32A(addr.index) | R28A(addr.base) | disp_s20(addr.offset));
+}
+
+static sljit_s32 emit_float(struct sljit_compiler *compiler, sljit_ins ins_r, sljit_ins ins,
+ sljit_s32 reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ struct addr addr;
+
+ if (!(src & SLJIT_MEM))
+ return push_inst(compiler, ins_r | F4(reg) | F0(src));
+
+ FAIL_IF(make_addr_bx(compiler, &addr, src, srcw, tmp1));
+ return push_inst(compiler, ins | F36(reg) | R32A(addr.index) | R28A(addr.base) | ((sljit_ins)addr.offset << 16));
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins dst_r = FAST_IS_REG(dst) ? gpr(dst) : tmp0;
+ sljit_ins ins;
+
+ if (src & SLJIT_MEM) {
+ FAIL_IF(float_mem(compiler, FLOAT_LOAD | (op & SLJIT_32), TMP_FREG1, src, srcw));
+ src = TMP_FREG1;
+ }
+
+ /* M3 is set to 5 */
+ if (GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)
+ ins = (op & SLJIT_32) ? 0xb3a85000 /* cgebr */ : 0xb3a95000 /* cgdbr */;
+ else
+ ins = (op & SLJIT_32) ? 0xb3985000 /* cfebr */ : 0xb3995000 /* cfdbr */;
+
+ FAIL_IF(push_inst(compiler, ins | R4A(dst_r) | F0(src)));
+
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, dst_r, dst, dstw, GET_OPCODE(op) >= SLJIT_CONV_S32_FROM_F64);
+
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+ sljit_ins ins;
+
+ if (src & SLJIT_IMM) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp0, srcw));
+ src = (sljit_s32)tmp0;
+ }
+ else if (src & SLJIT_MEM) {
+ FAIL_IF(load_word(compiler, tmp0, src, srcw, GET_OPCODE(op) >= SLJIT_CONV_F64_FROM_S32));
+ src = (sljit_s32)tmp0;
+ }
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)
+ ins = (op & SLJIT_32) ? 0xb3a40000 /* cegbr */ : 0xb3a50000 /* cdgbr */;
+ else
+ ins = (op & SLJIT_32) ? 0xb3940000 /* cefbr */ : 0xb3950000 /* cdfbr */;
+
+ FAIL_IF(push_inst(compiler, ins | F4(dst_r) | R0(src)));
+
+ if (dst & SLJIT_MEM)
+ return float_mem(compiler, FLOAT_STORE | (op & SLJIT_32), TMP_FREG1, dst, dstw);
+
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_ins ins_r, ins;
+
+ if (src1 & SLJIT_MEM) {
+ FAIL_IF(float_mem(compiler, FLOAT_LOAD | (op & SLJIT_32), TMP_FREG1, src1, src1w));
+ src1 = TMP_FREG1;
+ }
+
+ if (op & SLJIT_32) {
+ ins_r = 0xb3090000 /* cebr */;
+ ins = 0xed0000000009 /* ceb */;
+ } else {
+ ins_r = 0xb3190000 /* cdbr */;
+ ins = 0xed0000000019 /* cdb */;
+ }
+
+ return emit_float(compiler, ins_r, ins, src1, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG1;
+
+ if (op == SLJIT_CONV_F64_FROM_F32)
+ FAIL_IF(emit_float(compiler, 0xb3040000 /* ldebr */, 0xed0000000004 /* ldeb */, dst_r, src, srcw));
+ else {
+ if (src & SLJIT_MEM) {
+ FAIL_IF(float_mem(compiler, FLOAT_LOAD | (op == SLJIT_CONV_F32_FROM_F64 ? 0 : (op & SLJIT_32)), dst_r, src, srcw));
+ src = dst_r;
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_MOV_F64:
+ if (FAST_IS_REG(dst)) {
+ if (dst == src)
+ return SLJIT_SUCCESS;
+
+ ins = (op & SLJIT_32) ? 0x3800 /* ler */ : 0x2800 /* ldr */;
+ break;
+ }
+ return float_mem(compiler, FLOAT_STORE | (op & SLJIT_32), src, dst, dstw);
+ case SLJIT_CONV_F64_FROM_F32:
+ /* Only SLJIT_CONV_F32_FROM_F64. */
+ ins = 0xb3440000 /* ledbr */;
+ break;
+ case SLJIT_NEG_F64:
+ ins = (op & SLJIT_32) ? 0xb3030000 /* lcebr */ : 0xb3130000 /* lcdbr */;
+ break;
+ default:
+ SLJIT_ASSERT(GET_OPCODE(op) == SLJIT_ABS_F64);
+ ins = (op & SLJIT_32) ? 0xb3000000 /* lpebr */ : 0xb3100000 /* lpdbr */;
+ break;
+ }
+
+ FAIL_IF(push_inst(compiler, ins | F4(dst_r) | F0(src)));
+ }
+
+ if (!(dst & SLJIT_MEM))
+ return SLJIT_SUCCESS;
+
+ SLJIT_ASSERT(dst_r == TMP_FREG1);
+
+ return float_mem(compiler, FLOAT_STORE | (op & SLJIT_32), TMP_FREG1, dst, dstw);
+}
+
+#define FLOAT_MOV(op, dst_r, src_r) \
+ (((op & SLJIT_32) ? 0x3800 /* ler */ : 0x2800 /* ldr */) | F4(dst_r) | F0(src_r))
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r = TMP_FREG1;
+ sljit_ins ins_r, ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ do {
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+
+ if (dst == src1)
+ break;
+
+ if (dst == src2) {
+ if (GET_OPCODE(op) == SLJIT_ADD_F64 || GET_OPCODE(op) == SLJIT_MUL_F64) {
+ src2 = src1;
+ src2w = src1w;
+ src1 = dst;
+ break;
+ }
+
+ FAIL_IF(push_inst(compiler, FLOAT_MOV(op, TMP_FREG1, src2)));
+ src2 = TMP_FREG1;
+ }
+ }
+
+ if (src1 & SLJIT_MEM)
+ FAIL_IF(float_mem(compiler, FLOAT_LOAD | (op & SLJIT_32), dst_r, src1, src1w));
+ else
+ FAIL_IF(push_inst(compiler, FLOAT_MOV(op, dst_r, src1)));
+ } while (0);
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ ins_r = (op & SLJIT_32) ? 0xb30a0000 /* aebr */ : 0xb31a0000 /* adbr */;
+ ins = (op & SLJIT_32) ? 0xed000000000a /* aeb */ : 0xed000000001a /* adb */;
+ break;
+ case SLJIT_SUB_F64:
+ ins_r = (op & SLJIT_32) ? 0xb30b0000 /* sebr */ : 0xb31b0000 /* sdbr */;
+ ins = (op & SLJIT_32) ? 0xed000000000b /* seb */ : 0xed000000001b /* sdb */;
+ break;
+ case SLJIT_MUL_F64:
+ ins_r = (op & SLJIT_32) ? 0xb3170000 /* meebr */ : 0xb31c0000 /* mdbr */;
+ ins = (op & SLJIT_32) ? 0xed0000000017 /* meeb */ : 0xed000000001c /* mdb */;
+ break;
+ default:
+ SLJIT_ASSERT(GET_OPCODE(op) == SLJIT_DIV_F64);
+ ins_r = (op & SLJIT_32) ? 0xb30d0000 /* debr */ : 0xb31d0000 /* ddbr */;
+ ins = (op & SLJIT_32) ? 0xed000000000d /* deb */ : 0xed000000001d /* ddb */;
+ break;
+ }
+
+ FAIL_IF(emit_float(compiler, ins_r, ins, dst_r, src2, src2w));
+
+ if (dst & SLJIT_MEM)
+ return float_mem(compiler, FLOAT_STORE | (op & SLJIT_32), TMP_FREG1, dst, dstw);
+
+ SLJIT_ASSERT(dst_r != TMP_FREG1);
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst))
+ return push_inst(compiler, lgr(gpr(dst), link_r));
+
+ /* memory */
+ return store_word(compiler, link_r, dst, dstw, 0);
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ sljit_u8 mask = ((type & 0xff) < SLJIT_JUMP) ? get_cc(compiler, type & 0xff) : 0xf;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ /* record jump */
+ struct sljit_jump *jump = (struct sljit_jump *)
+ ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF(!jump);
+ set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP);
+ jump->addr = compiler->size;
+
+ /* emit jump instruction */
+ type &= 0xff;
+ if (type >= SLJIT_FAST_CALL)
+ PTR_FAIL_IF(push_inst(compiler, brasl(link_r, 0)));
+ else
+ PTR_FAIL_IF(push_inst(compiler, brcl(mask, 0)));
+
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ SLJIT_UNUSED_ARG(arg_types);
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, r14));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_gpr src_r = FAST_IS_REG(src) ? gpr(src) : tmp1;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+
+ if (src & SLJIT_IMM) {
+ SLJIT_ASSERT(!(srcw & 1)); /* target address must be even */
+ FAIL_IF(push_load_imm_inst(compiler, src_r, srcw));
+ }
+ else if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(load_word(compiler, src_r, src, srcw, 0 /* 64-bit */));
+ }
+
+ /* emit jump instruction */
+ if (type >= SLJIT_FAST_CALL)
+ return push_inst(compiler, basr(link_r, src_r));
+
+ return push_inst(compiler, br(src_r));
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ SLJIT_ASSERT(gpr(TMP_REG2) == tmp1);
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ FAIL_IF(load_word(compiler, tmp1, src, srcw, 0 /* 64-bit */));
+ src = TMP_REG2;
+ srcw = 0;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ FAIL_IF(push_inst(compiler, lgr(tmp1, gpr(src))));
+ src = TMP_REG2;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, r14));
+ type = SLJIT_JUMP;
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_u8 mask = get_cc(compiler, type);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+
+ sljit_gpr dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ sljit_gpr loc_r = tmp1;
+ switch (GET_OPCODE(op)) {
+ case SLJIT_AND:
+ case SLJIT_OR:
+ case SLJIT_XOR:
+ compiler->status_flags_state = op & SLJIT_SET_Z;
+
+ /* dst is also source operand */
+ if (dst & SLJIT_MEM)
+ FAIL_IF(load_word(compiler, dst_r, dst, dstw, op & SLJIT_32));
+
+ break;
+ case SLJIT_MOV32:
+ op |= SLJIT_32;
+ /* fallthrough */
+ case SLJIT_MOV:
+ /* can write straight into destination */
+ loc_r = dst_r;
+ break;
+ default:
+ SLJIT_UNREACHABLE();
+ }
+
+ /* TODO(mundaym): fold into cmov helper function? */
+ #define LEVAL(i) i(loc_r, 1, mask)
+ if (have_lscond2()) {
+ FAIL_IF(push_load_imm_inst(compiler, loc_r, 0));
+ FAIL_IF(push_inst(compiler,
+ WHEN2(op & SLJIT_32, lochi, locghi)));
+ } else {
+ /* TODO(mundaym): no load/store-on-condition 2 facility (ipm? branch-and-set?) */
+ abort();
+ }
+ #undef LEVAL
+
+ /* apply bitwise op and set condition codes */
+ switch (GET_OPCODE(op)) {
+ #define LEVAL(i) i(dst_r, loc_r)
+ case SLJIT_AND:
+ FAIL_IF(push_inst(compiler,
+ WHEN2(op & SLJIT_32, nr, ngr)));
+ break;
+ case SLJIT_OR:
+ FAIL_IF(push_inst(compiler,
+ WHEN2(op & SLJIT_32, or, ogr)));
+ break;
+ case SLJIT_XOR:
+ FAIL_IF(push_inst(compiler,
+ WHEN2(op & SLJIT_32, xr, xgr)));
+ break;
+ #undef LEVAL
+ }
+
+ /* store result to memory if required */
+ if (dst & SLJIT_MEM)
+ return store_word(compiler, dst_r, dst, dstw, (op & SLJIT_32));
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_ins mask = get_cc(compiler, type & ~SLJIT_32);
+ sljit_gpr src_r;
+ sljit_ins ins;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+ if (type & SLJIT_32)
+ srcw = (sljit_s32)srcw;
+
+ if (have_lscond2() && (src & SLJIT_IMM) && is_s16(srcw)) {
+ ins = (type & SLJIT_32) ? 0xec0000000042 /* lochi */ : 0xec0000000046 /* locghi */;
+ return push_inst(compiler, ins | R36A(gpr(dst_reg)) | (mask << 32) | (sljit_ins)(srcw & 0xffff) << 16);
+ }
+
+ if (src & SLJIT_IMM) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp0, srcw));
+ src_r = tmp0;
+ } else
+ src_r = gpr(src);
+
+ if (have_lscond1()) {
+ ins = (type & SLJIT_32) ? 0xb9f20000 /* locr */ : 0xb9e20000 /* locgr */;
+ return push_inst(compiler, ins | (mask << 12) | R4A(gpr(dst_reg)) | R0A(src_r));
+ }
+
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_ins ins, reg1, reg2, base, offs = 0;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ base = gpr(mem & REG_MASK);
+ reg1 = gpr(REG_PAIR_FIRST(reg));
+ reg2 = gpr(REG_PAIR_SECOND(reg));
+
+ if (mem & OFFS_REG_MASK) {
+ memw &= 0x3;
+ offs = gpr(OFFS_REG(mem));
+
+ if (memw != 0) {
+ FAIL_IF(push_inst(compiler, 0xeb000000000d /* sllg */ | R36A(tmp1) | R32A(offs) | ((sljit_ins)memw << 16)));
+ offs = tmp1;
+ } else if (!(type & SLJIT_MEM_STORE) && (base == reg1 || base == reg2) && (offs == reg1 || offs == reg2)) {
+ FAIL_IF(push_inst(compiler, 0xb9f80000 | R12A(tmp1) | R4A(base) | R0A(offs)));
+ base = tmp1;
+ offs = 0;
+ }
+
+ memw = 0;
+ } else if (memw < -0x80000 || memw > 0x7ffff - ((reg2 == reg1 + 1) ? 0 : SSIZE_OF(sw))) {
+ FAIL_IF(push_load_imm_inst(compiler, tmp1, memw));
+
+ if (base == 0)
+ base = tmp1;
+ else
+ offs = tmp1;
+
+ memw = 0;
+ }
+
+ if (offs == 0 && reg2 == (reg1 + 1)) {
+ ins = (type & SLJIT_MEM_STORE) ? 0xeb0000000024 /* stmg */ : 0xeb0000000004 /* lmg */;
+ return push_inst(compiler, ins | R36A(reg1) | R32A(reg2) | R28A(base) | disp_s20((sljit_s32)memw));
+ }
+
+ ins = ((type & SLJIT_MEM_STORE) ? 0xe30000000024 /* stg */ : 0xe30000000004 /* lg */) | R32A(offs) | R28A(base);
+
+ if (!(type & SLJIT_MEM_STORE) && base == reg1) {
+ FAIL_IF(push_inst(compiler, ins | R36A(reg2) | disp_s20((sljit_s32)memw + SSIZE_OF(sw))));
+ return push_inst(compiler, ins | R36A(reg1) | disp_s20((sljit_s32)memw));
+ }
+
+ FAIL_IF(push_inst(compiler, ins | R36A(reg1) | disp_s20((sljit_s32)memw)));
+ return push_inst(compiler, ins | R36A(reg2) | disp_s20((sljit_s32)memw + SSIZE_OF(sw)));
+}
+
+/* --------------------------------------------------------------------- */
+/* Other instructions */
+/* --------------------------------------------------------------------- */
+
+/* On s390x we build a literal pool to hold constants. This has two main
+ advantages:
+
+ 1. we only need one instruction in the instruction stream (LGRL)
+ 2. we can store 64 bit addresses and use 32 bit offsets
+
+ To retrofit the extra information needed to build the literal pool we
+ add a new sljit_s390x_const struct that contains the initial value but
+ can still be cast to a sljit_const. */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ struct sljit_s390x_const *const_;
+ sljit_gpr dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+
+ const_ = (struct sljit_s390x_const*)ensure_abuf(compiler,
+ sizeof(struct sljit_s390x_const));
+ PTR_FAIL_IF(!const_);
+ set_const((struct sljit_const*)const_, compiler);
+ const_->init_value = init_value;
+
+ dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+ if (have_genext())
+ PTR_FAIL_IF(push_inst(compiler, sljit_ins_const | lgrl(dst_r, 0)));
+ else {
+ PTR_FAIL_IF(push_inst(compiler, sljit_ins_const | larl(tmp1, 0)));
+ PTR_FAIL_IF(push_inst(compiler, lg(dst_r, 0, r0, tmp1)));
+ }
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(store_word(compiler, dst_r, dst, dstw, 0 /* always 64-bit */));
+
+ return (struct sljit_const*)const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ /* Update the constant pool. */
+ sljit_uw *ptr = (sljit_uw *)addr;
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 0);
+ *ptr = new_target;
+ SLJIT_UPDATE_WX_FLAGS(ptr, ptr + 1, 1);
+ SLJIT_CACHE_FLUSH(ptr, ptr + 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ sljit_set_jump_addr(addr, (sljit_uw)new_constant, executable_offset);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label *sljit_emit_put_label(
+ struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_gpr dst_r;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+ dst_r = FAST_IS_REG(dst) ? gpr(dst & REG_MASK) : tmp0;
+
+ if (have_genext())
+ PTR_FAIL_IF(push_inst(compiler, lgrl(dst_r, 0)));
+ else {
+ PTR_FAIL_IF(push_inst(compiler, larl(tmp1, 0)));
+ PTR_FAIL_IF(push_inst(compiler, lg(dst_r, 0, r0, tmp1)));
+ }
+
+ if (dst & SLJIT_MEM)
+ PTR_FAIL_IF(store_word(compiler, dst_r, dst, dstw, 0));
+
+ return put_label;
+}
+
+/* TODO(carenas): EVAL probably should move up or be refactored */
+#undef WHEN2
+#undef EVAL
+
+#undef tmp1
+#undef tmp0
+
+/* TODO(carenas): undef other macros that spill like is_u12? */
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeX86_32.c b/contrib/libs/pcre2/src/sljit/sljitNativeX86_32.c
new file mode 100644
index 0000000000..08da03026d
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeX86_32.c
@@ -0,0 +1,1298 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/* x86 32-bit arch dependent functions. */
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+static sljit_s32 emit_do_imm(struct sljit_compiler *compiler, sljit_u8 opcode, sljit_sw imm)
+{
+ sljit_u8 *inst;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + sizeof(sljit_sw));
+ FAIL_IF(!inst);
+ INC_SIZE(1 + sizeof(sljit_sw));
+ *inst++ = opcode;
+ sljit_unaligned_store_sw(inst, imm);
+ return SLJIT_SUCCESS;
+}
+
+/* Size contains the flags as well. */
+static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw size,
+ /* The register or immediate operand. */
+ sljit_s32 a, sljit_sw imma,
+ /* The general operand (not immediate). */
+ sljit_s32 b, sljit_sw immb)
+{
+ sljit_u8 *inst;
+ sljit_u8 *buf_ptr;
+ sljit_u8 reg_map_b;
+ sljit_uw flags = size;
+ sljit_uw inst_size;
+
+ /* Both cannot be switched on. */
+ SLJIT_ASSERT((flags & (EX86_BIN_INS | EX86_SHIFT_INS)) != (EX86_BIN_INS | EX86_SHIFT_INS));
+ /* Size flags not allowed for typed instructions. */
+ SLJIT_ASSERT(!(flags & (EX86_BIN_INS | EX86_SHIFT_INS)) || (flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) == 0);
+ /* Both size flags cannot be switched on. */
+ SLJIT_ASSERT((flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) != (EX86_BYTE_ARG | EX86_HALF_ARG));
+ /* SSE2 and immediate is not possible. */
+ SLJIT_ASSERT(!(a & SLJIT_IMM) || !(flags & EX86_SSE2));
+ SLJIT_ASSERT((flags & (EX86_PREF_F2 | EX86_PREF_F3)) != (EX86_PREF_F2 | EX86_PREF_F3)
+ && (flags & (EX86_PREF_F2 | EX86_PREF_66)) != (EX86_PREF_F2 | EX86_PREF_66)
+ && (flags & (EX86_PREF_F3 | EX86_PREF_66)) != (EX86_PREF_F3 | EX86_PREF_66));
+
+ size &= 0xf;
+ inst_size = size;
+
+ if (flags & (EX86_PREF_F2 | EX86_PREF_F3))
+ inst_size++;
+ if (flags & EX86_PREF_66)
+ inst_size++;
+
+ /* Calculate size of b. */
+ inst_size += 1; /* mod r/m byte. */
+ if (b & SLJIT_MEM) {
+ if (!(b & REG_MASK))
+ inst_size += sizeof(sljit_sw);
+ else {
+ if (immb != 0 && !(b & OFFS_REG_MASK)) {
+ /* Immediate operand. */
+ if (immb <= 127 && immb >= -128)
+ inst_size += sizeof(sljit_s8);
+ else
+ inst_size += sizeof(sljit_sw);
+ }
+ else if (reg_map[b & REG_MASK] == 5) {
+ /* Swap registers if possible. */
+ if ((b & OFFS_REG_MASK) && (immb & 0x3) == 0 && reg_map[OFFS_REG(b)] != 5)
+ b = SLJIT_MEM | OFFS_REG(b) | TO_OFFS_REG(b & REG_MASK);
+ else
+ inst_size += sizeof(sljit_s8);
+ }
+
+ if (reg_map[b & REG_MASK] == 4 && !(b & OFFS_REG_MASK))
+ b |= TO_OFFS_REG(SLJIT_SP);
+
+ if (b & OFFS_REG_MASK)
+ inst_size += 1; /* SIB byte. */
+ }
+ }
+
+ /* Calculate size of a. */
+ if (a & SLJIT_IMM) {
+ if (flags & EX86_BIN_INS) {
+ if (imma <= 127 && imma >= -128) {
+ inst_size += 1;
+ flags |= EX86_BYTE_ARG;
+ } else
+ inst_size += 4;
+ }
+ else if (flags & EX86_SHIFT_INS) {
+ SLJIT_ASSERT(imma <= 0x1f);
+ if (imma != 1) {
+ inst_size++;
+ flags |= EX86_BYTE_ARG;
+ }
+ } else if (flags & EX86_BYTE_ARG)
+ inst_size++;
+ else if (flags & EX86_HALF_ARG)
+ inst_size += sizeof(short);
+ else
+ inst_size += sizeof(sljit_sw);
+ }
+ else
+ SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + inst_size);
+ PTR_FAIL_IF(!inst);
+
+ /* Encoding the byte. */
+ INC_SIZE(inst_size);
+ if (flags & EX86_PREF_F2)
+ *inst++ = 0xf2;
+ if (flags & EX86_PREF_F3)
+ *inst++ = 0xf3;
+ if (flags & EX86_PREF_66)
+ *inst++ = 0x66;
+
+ buf_ptr = inst + size;
+
+ /* Encode mod/rm byte. */
+ if (!(flags & EX86_SHIFT_INS)) {
+ if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM))
+ *inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81;
+
+ if (a & SLJIT_IMM)
+ *buf_ptr = 0;
+ else if (!(flags & EX86_SSE2_OP1))
+ *buf_ptr = U8(reg_map[a] << 3);
+ else
+ *buf_ptr = U8(a << 3);
+ }
+ else {
+ if (a & SLJIT_IMM) {
+ if (imma == 1)
+ *inst = GROUP_SHIFT_1;
+ else
+ *inst = GROUP_SHIFT_N;
+ } else
+ *inst = GROUP_SHIFT_CL;
+ *buf_ptr = 0;
+ }
+
+ if (!(b & SLJIT_MEM)) {
+ *buf_ptr = U8(*buf_ptr | MOD_REG | (!(flags & EX86_SSE2_OP2) ? reg_map[b] : b));
+ buf_ptr++;
+ } else if (b & REG_MASK) {
+ reg_map_b = reg_map[b & REG_MASK];
+
+ if (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) {
+ if (immb != 0 || reg_map_b == 5) {
+ if (immb <= 127 && immb >= -128)
+ *buf_ptr |= 0x40;
+ else
+ *buf_ptr |= 0x80;
+ }
+
+ if (!(b & OFFS_REG_MASK))
+ *buf_ptr++ |= reg_map_b;
+ else {
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = U8(reg_map_b | (reg_map[OFFS_REG(b)] << 3));
+ }
+
+ if (immb != 0 || reg_map_b == 5) {
+ if (immb <= 127 && immb >= -128)
+ *buf_ptr++ = U8(immb); /* 8 bit displacement. */
+ else {
+ sljit_unaligned_store_sw(buf_ptr, immb); /* 32 bit displacement. */
+ buf_ptr += sizeof(sljit_sw);
+ }
+ }
+ }
+ else {
+ if (reg_map_b == 5)
+ *buf_ptr |= 0x40;
+
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = U8(reg_map_b | (reg_map[OFFS_REG(b)] << 3) | (immb << 6));
+
+ if (reg_map_b == 5)
+ *buf_ptr++ = 0;
+ }
+ }
+ else {
+ *buf_ptr++ |= 0x05;
+ sljit_unaligned_store_sw(buf_ptr, immb); /* 32 bit displacement. */
+ buf_ptr += sizeof(sljit_sw);
+ }
+
+ if (a & SLJIT_IMM) {
+ if (flags & EX86_BYTE_ARG)
+ *buf_ptr = U8(imma);
+ else if (flags & EX86_HALF_ARG)
+ sljit_unaligned_store_s16(buf_ptr, (sljit_s16)imma);
+ else if (!(flags & EX86_SHIFT_INS))
+ sljit_unaligned_store_sw(buf_ptr, imma);
+ }
+
+ return !(flags & EX86_SHIFT_INS) ? inst : (inst + 1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Enter / return */
+/* --------------------------------------------------------------------- */
+
+static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_sw executable_offset)
+{
+ sljit_uw type = jump->flags >> TYPE_SHIFT;
+
+ if (type == SLJIT_JUMP) {
+ *code_ptr++ = JMP_i32;
+ jump->addr++;
+ }
+ else if (type >= SLJIT_FAST_CALL) {
+ *code_ptr++ = CALL_i32;
+ jump->addr++;
+ }
+ else {
+ *code_ptr++ = GROUP_0F;
+ *code_ptr++ = get_jump_code(type);
+ jump->addr += 2;
+ }
+
+ if (jump->flags & JUMP_LABEL)
+ jump->flags |= PATCH_MW;
+ else
+ sljit_unaligned_store_sw(code_ptr, (sljit_sw)(jump->u.target - (jump->addr + 4) - (sljit_uw)executable_offset));
+ code_ptr += 4;
+
+ return code_ptr;
+}
+
+#define ENTER_TMP_TO_R4 0x00001
+#define ENTER_TMP_TO_S 0x00002
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 word_arg_count, saved_arg_count, float_arg_count;
+ sljit_s32 size, args_size, types, status;
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_u8 *inst;
+#ifdef _WIN32
+ sljit_s32 r2_offset = -1;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ /* Emit ENDBR32 at function entry if needed. */
+ FAIL_IF(emit_endbranch(compiler));
+
+ SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ word_arg_count = 0;
+ status = 0;
+
+ if (options & SLJIT_ENTER_REG_ARG) {
+ args_size = 3 * SSIZE_OF(sw);
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ word_arg_count++;
+ if (word_arg_count >= 4)
+ status |= ENTER_TMP_TO_R4;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ compiler->args_size = 0;
+ } else {
+ types = arg_types;
+ saved_arg_count = 0;
+ float_arg_count = 0;
+ args_size = SSIZE_OF(sw);
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_load(compiler, 0, float_arg_count, SLJIT_MEM1(SLJIT_SP), args_size));
+ args_size += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_load(compiler, 1, float_arg_count, SLJIT_MEM1(SLJIT_SP), args_size));
+ args_size += SSIZE_OF(f32);
+ break;
+ default:
+ word_arg_count++;
+
+ if (!(types & SLJIT_ARG_TYPE_SCRATCH_REG))
+ saved_arg_count++;
+
+ if (word_arg_count == 4) {
+ if (types & SLJIT_ARG_TYPE_SCRATCH_REG) {
+ status |= ENTER_TMP_TO_R4;
+ arg_types &= ~(SLJIT_ARG_FULL_MASK << 3 * SLJIT_ARG_SHIFT);
+ } else if (saved_arg_count == 4) {
+ status |= ENTER_TMP_TO_S;
+ arg_types &= ~(SLJIT_ARG_FULL_MASK << 3 * SLJIT_ARG_SHIFT);
+ }
+ }
+
+ args_size += SSIZE_OF(sw);
+ break;
+ }
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ args_size -= SSIZE_OF(sw);
+ compiler->args_size = args_size;
+ }
+
+ size = (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3) - kept_saveds_count;
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ size++;
+
+ if (size != 0) {
+ inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(size + 1));
+ FAIL_IF(!inst);
+
+ INC_SIZE((sljit_uw)size);
+
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ PUSH_REG(reg_map[TMP_REG1]);
+
+ if ((saveds > 2 && kept_saveds_count <= 2) || scratches > 9)
+ PUSH_REG(reg_map[SLJIT_S2]);
+ if ((saveds > 1 && kept_saveds_count <= 1) || scratches > 10)
+ PUSH_REG(reg_map[SLJIT_S1]);
+ if ((saveds > 0 && kept_saveds_count == 0) || scratches > 11)
+ PUSH_REG(reg_map[SLJIT_S0]);
+
+ size *= SSIZE_OF(sw);
+ }
+
+ if (status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S))
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), args_size + size);
+
+ size += SSIZE_OF(sw);
+
+ local_size = ((SLJIT_LOCALS_OFFSET_BASE + local_size + size + 0xf) & ~0xf) - size;
+ compiler->local_size = local_size;
+
+ word_arg_count = 0;
+ saved_arg_count = 0;
+ args_size = size;
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ args_size += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ args_size += SSIZE_OF(f32);
+ break;
+ default:
+ word_arg_count++;
+ SLJIT_ASSERT(word_arg_count <= 3 || (word_arg_count == 4 && !(status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S))));
+
+ if (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) {
+#ifdef _WIN32
+ if (word_arg_count == 3 && local_size > 4 * 4096)
+ r2_offset = local_size + args_size;
+ else
+#endif
+ EMIT_MOV(compiler, word_arg_count, 0, SLJIT_MEM1(SLJIT_SP), args_size);
+
+ } else {
+ EMIT_MOV(compiler, SLJIT_S0 - saved_arg_count, 0, SLJIT_MEM1(SLJIT_SP), args_size);
+ saved_arg_count++;
+ }
+
+ args_size += SSIZE_OF(sw);
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT(SLJIT_LOCALS_OFFSET > 0);
+
+#ifdef _WIN32
+ SLJIT_ASSERT(r2_offset == -1 || local_size > 4 * 4096);
+
+ if (local_size > 4096) {
+ if (local_size <= 4 * 4096) {
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096);
+
+ if (local_size > 2 * 4096)
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 2);
+ if (local_size > 3 * 4096)
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 3);
+ }
+ else {
+ if (options & SLJIT_ENTER_REG_ARG) {
+ SLJIT_ASSERT(r2_offset == -1);
+
+ inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 1));
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ PUSH_REG(reg_map[SLJIT_R2]);
+
+ local_size -= SSIZE_OF(sw);
+ r2_offset = local_size;
+ }
+
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_IMM, local_size >> 12);
+
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -4096);
+ BINARY_IMM32(SUB, 4096, SLJIT_SP, 0);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+
+ INC_SIZE(2);
+ inst[0] = LOOP_i8;
+ inst[1] = (sljit_u8)-16;
+ local_size &= 0xfff;
+ }
+ }
+
+ if (local_size > 0) {
+ BINARY_IMM32(OR, 0, SLJIT_MEM1(SLJIT_SP), -local_size);
+ BINARY_IMM32(SUB, local_size, SLJIT_SP, 0);
+ }
+
+ if (r2_offset != -1)
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), r2_offset);
+
+#else /* !_WIN32 */
+
+ SLJIT_ASSERT(local_size > 0);
+
+ BINARY_IMM32(SUB, local_size, SLJIT_SP, 0);
+
+#endif /* _WIN32 */
+
+ size = SLJIT_LOCALS_OFFSET_BASE - SSIZE_OF(sw);
+ kept_saveds_count = SLJIT_R3 - kept_saveds_count;
+
+ while (saved_arg_count > 3) {
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), size, kept_saveds_count, 0);
+ kept_saveds_count++;
+ size -= SSIZE_OF(sw);
+ saved_arg_count--;
+ }
+
+ if (status & (ENTER_TMP_TO_R4 | ENTER_TMP_TO_S)) {
+ if (status & ENTER_TMP_TO_R4)
+ size = 2 * SSIZE_OF(sw);
+
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), size, TMP_REG1, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 args_size;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ args_size = 0;
+
+ if (!(options & SLJIT_ENTER_REG_ARG)) {
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ args_size += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ args_size += SSIZE_OF(f32);
+ break;
+ default:
+ args_size += SSIZE_OF(sw);
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+ }
+
+ compiler->args_size = args_size;
+
+ /* [esp+0] for saving temporaries and for function calls. */
+
+ saveds = (1 + (scratches > 9 ? (scratches - 9) : 0) + (saveds <= 3 ? saveds : 3) - SLJIT_KEPT_SAVEDS_COUNT(options)) * SSIZE_OF(sw);
+
+ /* Saving ebp. */
+ if (!(options & SLJIT_ENTER_REG_ARG))
+ saveds += SSIZE_OF(sw);
+
+ compiler->local_size = ((SLJIT_LOCALS_OFFSET_BASE + local_size + saveds + 0xf) & ~0xf) - saveds;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ sljit_s32 local_size, saveds;
+ sljit_uw size;
+ sljit_u8 *inst;
+
+ size = (sljit_uw)((compiler->scratches > 9 ? (compiler->scratches - 9) : 0) +
+ (compiler->saveds <= 3 ? compiler->saveds : 3) - kept_saveds_count);
+
+ local_size = compiler->local_size;
+
+ if (!(compiler->options & SLJIT_ENTER_REG_ARG))
+ size++;
+ else if (is_return_to && size == 0) {
+ local_size += SSIZE_OF(sw);
+ is_return_to = 0;
+ }
+
+ if (local_size > 0)
+ BINARY_IMM32(ADD, local_size, SLJIT_SP, 0);
+
+ if (size == 0)
+ return SLJIT_SUCCESS;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+
+ INC_SIZE(size);
+
+ saveds = compiler->saveds;
+
+ if ((saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11)
+ POP_REG(reg_map[SLJIT_S0]);
+ if ((saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10)
+ POP_REG(reg_map[SLJIT_S1]);
+ if ((saveds > 2 && kept_saveds_count <= 2) || compiler->scratches > 9)
+ POP_REG(reg_map[SLJIT_S2]);
+
+ if (!(compiler->options & SLJIT_ENTER_REG_ARG))
+ POP_REG(reg_map[TMP_REG1]);
+
+ if (is_return_to)
+ BINARY_IMM32(ADD, sizeof(sljit_sw), SLJIT_SP, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ SLJIT_ASSERT(compiler->args_size >= 0);
+ SLJIT_ASSERT(compiler->local_size > 0);
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ RET();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 src_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ if ((src & SLJIT_MEM) || (src > SLJIT_R2 && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ src_r = (compiler->options & SLJIT_ENTER_REG_ARG) ? TMP_REG1 : SLJIT_R1;
+
+ EMIT_MOV(compiler, src_r, 0, src, srcw);
+ src = src_r;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Call / return instructions */
+/* --------------------------------------------------------------------- */
+
+static sljit_s32 call_get_stack_size(sljit_s32 arg_types, sljit_s32 *word_arg_count_ptr)
+{
+ sljit_sw stack_size = 0;
+ sljit_s32 word_arg_count = 0;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ stack_size += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ stack_size += SSIZE_OF(f32);
+ break;
+ default:
+ word_arg_count++;
+ stack_size += SSIZE_OF(sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (word_arg_count_ptr)
+ *word_arg_count_ptr = word_arg_count;
+
+ if (stack_size <= 4 * SSIZE_OF(sw))
+ return 0;
+
+ return ((stack_size - (4 * SSIZE_OF(sw)) + 0xf) & ~0xf);
+}
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 arg_types, sljit_sw stack_size, sljit_s32 word_arg_count, sljit_s32 keep_tmp1)
+{
+ sljit_s32 float_arg_count = 0, arg4_reg = 0, arg_offset;
+ sljit_u8 *inst;
+
+ if (word_arg_count >= 4) {
+ arg4_reg = SLJIT_R0;
+
+ if (!keep_tmp1) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw));
+ arg4_reg = TMP_REG1;
+ }
+ }
+
+ if (stack_size > 0)
+ BINARY_IMM32(SUB, stack_size, SLJIT_SP, 0);
+
+ arg_offset = 0;
+ word_arg_count = 0;
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), arg_offset, float_arg_count));
+ arg_offset += SSIZE_OF(f64);
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ float_arg_count++;
+ FAIL_IF(emit_sse2_store(compiler, 1, SLJIT_MEM1(SLJIT_SP), arg_offset, float_arg_count));
+ arg_offset += SSIZE_OF(f32);
+ break;
+ default:
+ word_arg_count++;
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), arg_offset, (word_arg_count >= 4) ? arg4_reg : word_arg_count, 0);
+
+ if (word_arg_count == 1 && arg4_reg == SLJIT_R0)
+ EMIT_MOV(compiler, SLJIT_R0, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw) + stack_size);
+
+ arg_offset += SSIZE_OF(sw);
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 post_call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 arg_types, sljit_s32 stack_size)
+{
+ sljit_u8 *inst;
+ sljit_s32 single;
+
+ if (stack_size > 0)
+ BINARY_IMM32(ADD, stack_size, SLJIT_SP, 0);
+
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64)
+ return SLJIT_SUCCESS;
+
+ single = ((arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F32);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(3);
+ inst[0] = single ? FSTPS : FSTPD;
+ inst[1] = (0x03 << 3) | 0x04;
+ inst[2] = (0x04 << 3) | reg_map[SLJIT_SP];
+
+ return emit_sse2_load(compiler, single, SLJIT_FR0, SLJIT_MEM1(SLJIT_SP), 0);
+}
+
+static sljit_s32 tail_call_with_args(struct sljit_compiler *compiler,
+ sljit_s32 *extra_space, sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_sw args_size, saved_regs_size;
+ sljit_sw types, word_arg_count, float_arg_count;
+ sljit_sw stack_size, prev_stack_size, min_size, offset;
+ sljit_sw word_arg4_offset;
+ sljit_u8 r2_offset = 0;
+ sljit_s32 kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ sljit_u8* inst;
+
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ saved_regs_size = (1 + (compiler->scratches > 9 ? (compiler->scratches - 9) : 0)
+ + (compiler->saveds <= 3 ? compiler->saveds : 3) - kept_saveds_count) * SSIZE_OF(sw);
+
+ word_arg_count = 0;
+ float_arg_count = 0;
+ arg_types >>= SLJIT_ARG_SHIFT;
+ types = 0;
+ args_size = 0;
+
+ while (arg_types != 0) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ args_size += SSIZE_OF(f64);
+ float_arg_count++;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ args_size += SSIZE_OF(f32);
+ float_arg_count++;
+ break;
+ default:
+ word_arg_count++;
+ args_size += SSIZE_OF(sw);
+ break;
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (args_size <= compiler->args_size) {
+ *extra_space = 0;
+ stack_size = args_size + SSIZE_OF(sw) + saved_regs_size;
+
+ offset = stack_size + compiler->local_size;
+
+ if (!(src & SLJIT_IMM) && src != SLJIT_R0) {
+ if (word_arg_count >= 1) {
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_R0, 0);
+ r2_offset = sizeof(sljit_sw);
+ }
+ EMIT_MOV(compiler, SLJIT_R0, 0, src, srcw);
+ }
+
+ while (types != 0) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), offset, float_arg_count));
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ offset -= SSIZE_OF(f32);
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), offset, float_arg_count));
+ float_arg_count--;
+ break;
+ default:
+ switch (word_arg_count) {
+ case 1:
+ offset -= SSIZE_OF(sw);
+ if (r2_offset != 0) {
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 0);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ } else
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R0, 0);
+ break;
+ case 2:
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R1, 0);
+ break;
+ case 3:
+ offset -= SSIZE_OF(sw);
+ break;
+ case 4:
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw));
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ break;
+ }
+ word_arg_count--;
+ break;
+ }
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return emit_stack_frame_release(compiler, 0);
+ }
+
+ stack_size = args_size + SSIZE_OF(sw);
+
+ if (word_arg_count >= 1 && !(src & SLJIT_IMM) && src != SLJIT_R0) {
+ r2_offset = SSIZE_OF(sw);
+ stack_size += SSIZE_OF(sw);
+ }
+
+ if (word_arg_count >= 3)
+ stack_size += SSIZE_OF(sw);
+
+ prev_stack_size = SSIZE_OF(sw) + saved_regs_size;
+ min_size = prev_stack_size + compiler->local_size;
+
+ word_arg4_offset = 2 * SSIZE_OF(sw);
+
+ if (stack_size > min_size) {
+ BINARY_IMM32(SUB, stack_size - min_size, SLJIT_SP, 0);
+ if (src == SLJIT_MEM1(SLJIT_SP))
+ srcw += stack_size - min_size;
+ word_arg4_offset += stack_size - min_size;
+ }
+ else
+ stack_size = min_size;
+
+ if (word_arg_count >= 3) {
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), r2_offset, SLJIT_R2, 0);
+
+ if (word_arg_count >= 4)
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), word_arg4_offset);
+ }
+
+ if (!(src & SLJIT_IMM) && src != SLJIT_R0) {
+ if (word_arg_count >= 1) {
+ SLJIT_ASSERT(r2_offset == sizeof(sljit_sw));
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_R0, 0);
+ }
+ EMIT_MOV(compiler, SLJIT_R0, 0, src, srcw);
+ }
+
+ /* Restore saved registers. */
+ offset = stack_size - 2 * SSIZE_OF(sw);
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), offset);
+
+ if (compiler->saveds > 2 || compiler->scratches > 9) {
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_S2, 0, SLJIT_MEM1(SLJIT_SP), offset);
+ }
+ if ((compiler->saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10) {
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_S1, 0, SLJIT_MEM1(SLJIT_SP), offset);
+ }
+ if ((compiler->saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11) {
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_S0, 0, SLJIT_MEM1(SLJIT_SP), offset);
+ }
+
+ /* Copy fourth argument and return address. */
+ offset = stack_size - SSIZE_OF(sw);
+ *extra_space = args_size;
+
+ if (word_arg_count >= 4) {
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ }
+
+ while (types != 0) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ offset -= SSIZE_OF(f64);
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), offset, float_arg_count));
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ offset -= SSIZE_OF(f32);
+ FAIL_IF(emit_sse2_store(compiler, 0, SLJIT_MEM1(SLJIT_SP), offset, float_arg_count));
+ float_arg_count--;
+ break;
+ default:
+ switch (word_arg_count) {
+ case 1:
+ offset -= SSIZE_OF(sw);
+ if (r2_offset != 0) {
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), 0);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ } else
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R0, 0);
+ break;
+ case 2:
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R1, 0);
+ break;
+ case 3:
+ offset -= SSIZE_OF(sw);
+ EMIT_MOV(compiler, SLJIT_R2, 0, SLJIT_MEM1(SLJIT_SP), r2_offset);
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, SLJIT_R2, 0);
+ break;
+ }
+ word_arg_count--;
+ break;
+ }
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ SLJIT_ASSERT(offset >= 0);
+
+ if (offset == 0)
+ return SLJIT_SUCCESS;
+
+ BINARY_IMM32(ADD, offset, SLJIT_SP, 0);
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_tail_call_end(struct sljit_compiler *compiler, sljit_s32 extra_space)
+{
+ /* Called when stack consumption cannot be reduced to 0. */
+ sljit_u8 *inst;
+
+ BINARY_IMM32(ADD, extra_space, SLJIT_SP, 0);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ RET();
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 tail_call_reg_arg_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types)
+{
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 kept_saveds_count, offset;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64)
+ word_arg_count++;
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (word_arg_count < 4)
+ return SLJIT_SUCCESS;
+
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), 2 * SSIZE_OF(sw));
+
+ kept_saveds_count = SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ offset = compiler->local_size + 3 * SSIZE_OF(sw);
+
+ if ((compiler->saveds > 0 && kept_saveds_count == 0) || compiler->scratches > 11)
+ offset += SSIZE_OF(sw);
+ if ((compiler->saveds > 1 && kept_saveds_count <= 1) || compiler->scratches > 10)
+ offset += SSIZE_OF(sw);
+ if ((compiler->saveds > 2 && kept_saveds_count <= 2) || compiler->scratches > 9)
+ offset += SSIZE_OF(sw);
+
+ return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), offset, TMP_REG1, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ struct sljit_jump *jump;
+ sljit_sw stack_size = 0;
+ sljit_s32 word_arg_count;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ if (type & SLJIT_CALL_RETURN) {
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ PTR_FAIL_IF(tail_call_reg_arg_with_args(compiler, arg_types));
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP));
+ }
+
+ stack_size = type;
+ PTR_FAIL_IF(tail_call_with_args(compiler, &stack_size, arg_types, SLJIT_IMM, 0));
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (stack_size == 0)
+ return sljit_emit_jump(compiler, SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP));
+
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ PTR_FAIL_IF(emit_tail_call_end(compiler, stack_size));
+ return jump;
+ }
+
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+ }
+
+ stack_size = call_get_stack_size(arg_types, &word_arg_count);
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, stack_size, word_arg_count, 0));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ jump = sljit_emit_jump(compiler, type);
+ PTR_FAIL_IF(jump == NULL);
+
+ PTR_FAIL_IF(post_call_with_args(compiler, arg_types, stack_size));
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_sw stack_size = 0;
+ sljit_s32 word_arg_count;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ if (type & SLJIT_CALL_RETURN) {
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ FAIL_IF(tail_call_reg_arg_with_args(compiler, arg_types));
+
+ if ((src & SLJIT_MEM) || (src > SLJIT_R2 && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+ }
+
+ stack_size = type;
+ FAIL_IF(tail_call_with_args(compiler, &stack_size, arg_types, src, srcw));
+
+ if (!(src & SLJIT_IMM)) {
+ src = SLJIT_R0;
+ srcw = 0;
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+
+ if (stack_size == 0)
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+ return emit_tail_call_end(compiler, stack_size);
+ }
+
+ if ((type & 0xff) == SLJIT_CALL_REG_ARG) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+ }
+
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ if (src & SLJIT_MEM) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ stack_size = call_get_stack_size(arg_types, &word_arg_count);
+ FAIL_IF(call_with_args(compiler, arg_types, stack_size, word_arg_count, src == TMP_REG1));
+
+ if (stack_size > 0 && src == SLJIT_MEM1(SLJIT_SP))
+ srcw += stack_size;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ FAIL_IF(sljit_emit_ijump(compiler, type, src, srcw));
+
+ return post_call_with_args(compiler, arg_types, stack_size);
+}
+
+static SLJIT_INLINE sljit_s32 emit_fmov_before_return(struct sljit_compiler *compiler, sljit_s32 op, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ if (compiler->options & SLJIT_ENTER_REG_ARG) {
+ if (src == SLJIT_FR0)
+ return SLJIT_SUCCESS;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_fop1(compiler, op, SLJIT_RETURN_FREG, 0, src, srcw);
+ }
+
+ if (FAST_IS_REG(src)) {
+ FAIL_IF(emit_sse2_store(compiler, op & SLJIT_32, SLJIT_MEM1(SLJIT_SP), 0, src));
+
+ src = SLJIT_MEM1(SLJIT_SP);
+ srcw = 0;
+ } else {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ }
+
+ inst = emit_x86_instruction(compiler, 1 | EX86_SSE2_OP1, 0, 0, src, srcw);
+ *inst = (op & SLJIT_32) ? FLDS : FLDL;
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+ if (FAST_IS_REG(dst)) {
+ /* Unused dest is possible here. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+
+ INC_SIZE(1);
+ POP_REG(reg_map[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ /* Memory. */
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst++ = POP_rm;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8 *inst;
+
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ if (FAST_IS_REG(src)) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 1);
+ FAIL_IF(!inst);
+
+ INC_SIZE(1 + 1);
+ PUSH_REG(reg_map[src]);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_FF;
+ *inst |= PUSH_rm;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ }
+
+ RET();
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Other operations */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u8* inst;
+ sljit_s32 i, next, reg_idx, offset;
+ sljit_u8 regs[2];
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ regs[0] = U8(REG_PAIR_FIRST(reg));
+ regs[1] = U8(REG_PAIR_SECOND(reg));
+
+ next = SSIZE_OF(sw);
+
+ if (!(type & SLJIT_MEM_STORE) && (regs[0] == (mem & REG_MASK) || regs[0] == OFFS_REG(mem))) {
+ if (regs[1] == (mem & REG_MASK) || regs[1] == OFFS_REG(mem)) {
+ /* None of them are virtual register so TMP_REG1 will not be used. */
+ EMIT_MOV(compiler, TMP_REG1, 0, OFFS_REG(mem), 0);
+
+ if (regs[1] == OFFS_REG(mem))
+ next = -SSIZE_OF(sw);
+
+ mem = (mem & ~OFFS_REG_MASK) | TO_OFFS_REG(TMP_REG1);
+ } else {
+ next = -SSIZE_OF(sw);
+
+ if (!(mem & OFFS_REG_MASK))
+ memw += SSIZE_OF(sw);
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ reg_idx = next > 0 ? i : (i ^ 0x1);
+ reg = regs[reg_idx];
+
+ offset = -1;
+
+ if (reg >= SLJIT_R3 && reg <= SLJIT_S3) {
+ offset = (2 * SSIZE_OF(sw)) + ((reg) - SLJIT_R3) * SSIZE_OF(sw);
+ reg = TMP_REG1;
+
+ if (type & SLJIT_MEM_STORE)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), offset);
+ }
+
+ if ((mem & OFFS_REG_MASK) && (reg_idx == 1)) {
+ inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 4));
+ FAIL_IF(!inst);
+
+ INC_SIZE(4);
+
+ inst[0] = (type & SLJIT_MEM_STORE) ? MOV_rm_r : MOV_r_rm;
+ inst[1] = 0x44 | U8(reg_map[reg] << 3);
+ inst[2] = U8(memw << 6) | U8(reg_map[OFFS_REG(mem)] << 3) | reg_map[mem & REG_MASK];
+ inst[3] = sizeof(sljit_sw);
+ } else if (type & SLJIT_MEM_STORE) {
+ EMIT_MOV(compiler, mem, memw, reg, 0);
+ } else {
+ EMIT_MOV(compiler, reg, 0, mem, memw);
+ }
+
+ if (!(mem & OFFS_REG_MASK))
+ memw += next;
+
+ if (!(type & SLJIT_MEM_STORE) && offset != -1)
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), offset, TMP_REG1, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 skip_frames_before_return(struct sljit_compiler *compiler)
+{
+ sljit_sw size;
+
+ /* Don't adjust shadow stack if it isn't enabled. */
+ if (!cpu_has_shadow_stack())
+ return SLJIT_SUCCESS;
+
+ SLJIT_ASSERT(compiler->args_size >= 0);
+ SLJIT_ASSERT(compiler->local_size > 0);
+
+ size = compiler->local_size;
+ size += (1 + (compiler->scratches > 9 ? (compiler->scratches - 9) : 0)
+ + (compiler->saveds <= 3 ? compiler->saveds : 3)) * SSIZE_OF(sw);
+
+ return adjust_shadow_stack(compiler, SLJIT_MEM1(SLJIT_SP), size);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeX86_64.c b/contrib/libs/pcre2/src/sljit/sljitNativeX86_64.c
new file mode 100644
index 0000000000..4e938ffcf3
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeX86_64.c
@@ -0,0 +1,1092 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/* x86 64-bit arch dependent functions. */
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+static sljit_s32 emit_load_imm64(struct sljit_compiler *compiler, sljit_s32 reg, sljit_sw imm)
+{
+ sljit_u8 *inst;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2 + sizeof(sljit_sw));
+ FAIL_IF(!inst);
+ INC_SIZE(2 + sizeof(sljit_sw));
+ *inst++ = REX_W | ((reg_map[reg] <= 7) ? 0 : REX_B);
+ *inst++ = U8(MOV_r_i32 | (reg_map[reg] & 0x7));
+ sljit_unaligned_store_sw(inst, imm);
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_do_imm32(struct sljit_compiler *compiler, sljit_u8 rex, sljit_u8 opcode, sljit_sw imm)
+{
+ sljit_u8 *inst;
+ sljit_uw length = (rex ? 2 : 1) + sizeof(sljit_s32);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + length);
+ FAIL_IF(!inst);
+ INC_SIZE(length);
+ if (rex)
+ *inst++ = rex;
+ *inst++ = opcode;
+ sljit_unaligned_store_s32(inst, (sljit_s32)imm);
+ return SLJIT_SUCCESS;
+}
+
+static sljit_u8* emit_x86_instruction(struct sljit_compiler *compiler, sljit_uw size,
+ /* The register or immediate operand. */
+ sljit_s32 a, sljit_sw imma,
+ /* The general operand (not immediate). */
+ sljit_s32 b, sljit_sw immb)
+{
+ sljit_u8 *inst;
+ sljit_u8 *buf_ptr;
+ sljit_u8 rex = 0;
+ sljit_u8 reg_lmap_b;
+ sljit_uw flags = size;
+ sljit_uw inst_size;
+
+ /* The immediate operand must be 32 bit. */
+ SLJIT_ASSERT(!(a & SLJIT_IMM) || compiler->mode32 || IS_HALFWORD(imma));
+ /* Both cannot be switched on. */
+ SLJIT_ASSERT((flags & (EX86_BIN_INS | EX86_SHIFT_INS)) != (EX86_BIN_INS | EX86_SHIFT_INS));
+ /* Size flags not allowed for typed instructions. */
+ SLJIT_ASSERT(!(flags & (EX86_BIN_INS | EX86_SHIFT_INS)) || (flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) == 0);
+ /* Both size flags cannot be switched on. */
+ SLJIT_ASSERT((flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) != (EX86_BYTE_ARG | EX86_HALF_ARG));
+ /* SSE2 and immediate is not possible. */
+ SLJIT_ASSERT(!(a & SLJIT_IMM) || !(flags & EX86_SSE2));
+ SLJIT_ASSERT((flags & (EX86_PREF_F2 | EX86_PREF_F3)) != (EX86_PREF_F2 | EX86_PREF_F3)
+ && (flags & (EX86_PREF_F2 | EX86_PREF_66)) != (EX86_PREF_F2 | EX86_PREF_66)
+ && (flags & (EX86_PREF_F3 | EX86_PREF_66)) != (EX86_PREF_F3 | EX86_PREF_66));
+
+ size &= 0xf;
+ inst_size = size;
+
+ if (!compiler->mode32 && !(flags & EX86_NO_REXW))
+ rex |= REX_W;
+ else if (flags & EX86_REX)
+ rex |= REX;
+
+ if (flags & (EX86_PREF_F2 | EX86_PREF_F3))
+ inst_size++;
+ if (flags & EX86_PREF_66)
+ inst_size++;
+
+ /* Calculate size of b. */
+ inst_size += 1; /* mod r/m byte. */
+ if (b & SLJIT_MEM) {
+ if (!(b & OFFS_REG_MASK) && NOT_HALFWORD(immb)) {
+ PTR_FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immb));
+ immb = 0;
+ if (b & REG_MASK)
+ b |= TO_OFFS_REG(TMP_REG2);
+ else
+ b |= TMP_REG2;
+ }
+
+ if (!(b & REG_MASK))
+ inst_size += 1 + sizeof(sljit_s32); /* SIB byte required to avoid RIP based addressing. */
+ else {
+ if (immb != 0 && !(b & OFFS_REG_MASK)) {
+ /* Immediate operand. */
+ if (immb <= 127 && immb >= -128)
+ inst_size += sizeof(sljit_s8);
+ else
+ inst_size += sizeof(sljit_s32);
+ }
+ else if (reg_lmap[b & REG_MASK] == 5) {
+ /* Swap registers if possible. */
+ if ((b & OFFS_REG_MASK) && (immb & 0x3) == 0 && reg_lmap[OFFS_REG(b)] != 5)
+ b = SLJIT_MEM | OFFS_REG(b) | TO_OFFS_REG(b & REG_MASK);
+ else
+ inst_size += sizeof(sljit_s8);
+ }
+
+ if (reg_map[b & REG_MASK] >= 8)
+ rex |= REX_B;
+
+ if (reg_lmap[b & REG_MASK] == 4 && !(b & OFFS_REG_MASK))
+ b |= TO_OFFS_REG(SLJIT_SP);
+
+ if (b & OFFS_REG_MASK) {
+ inst_size += 1; /* SIB byte. */
+ if (reg_map[OFFS_REG(b)] >= 8)
+ rex |= REX_X;
+ }
+ }
+ }
+ else if (!(flags & EX86_SSE2_OP2)) {
+ if (reg_map[b] >= 8)
+ rex |= REX_B;
+ }
+ else if (freg_map[b] >= 8)
+ rex |= REX_B;
+
+ if (a & SLJIT_IMM) {
+ if (flags & EX86_BIN_INS) {
+ if (imma <= 127 && imma >= -128) {
+ inst_size += 1;
+ flags |= EX86_BYTE_ARG;
+ } else
+ inst_size += 4;
+ }
+ else if (flags & EX86_SHIFT_INS) {
+ SLJIT_ASSERT(imma <= (compiler->mode32 ? 0x1f : 0x3f));
+ if (imma != 1) {
+ inst_size++;
+ flags |= EX86_BYTE_ARG;
+ }
+ } else if (flags & EX86_BYTE_ARG)
+ inst_size++;
+ else if (flags & EX86_HALF_ARG)
+ inst_size += sizeof(short);
+ else
+ inst_size += sizeof(sljit_s32);
+ }
+ else {
+ SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG);
+ /* reg_map[SLJIT_PREF_SHIFT_REG] is less than 8. */
+ if (!(flags & EX86_SSE2_OP1)) {
+ if (reg_map[a] >= 8)
+ rex |= REX_R;
+ }
+ else if (freg_map[a] >= 8)
+ rex |= REX_R;
+ }
+
+ if (rex)
+ inst_size++;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + inst_size);
+ PTR_FAIL_IF(!inst);
+
+ /* Encoding the byte. */
+ INC_SIZE(inst_size);
+ if (flags & EX86_PREF_F2)
+ *inst++ = 0xf2;
+ if (flags & EX86_PREF_F3)
+ *inst++ = 0xf3;
+ if (flags & EX86_PREF_66)
+ *inst++ = 0x66;
+ if (rex)
+ *inst++ = rex;
+ buf_ptr = inst + size;
+
+ /* Encode mod/rm byte. */
+ if (!(flags & EX86_SHIFT_INS)) {
+ if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM))
+ *inst = (flags & EX86_BYTE_ARG) ? GROUP_BINARY_83 : GROUP_BINARY_81;
+
+ if (a & SLJIT_IMM)
+ *buf_ptr = 0;
+ else if (!(flags & EX86_SSE2_OP1))
+ *buf_ptr = U8(reg_lmap[a] << 3);
+ else
+ *buf_ptr = U8(freg_lmap[a] << 3);
+ }
+ else {
+ if (a & SLJIT_IMM) {
+ if (imma == 1)
+ *inst = GROUP_SHIFT_1;
+ else
+ *inst = GROUP_SHIFT_N;
+ } else
+ *inst = GROUP_SHIFT_CL;
+ *buf_ptr = 0;
+ }
+
+ if (!(b & SLJIT_MEM)) {
+ *buf_ptr = U8(*buf_ptr | MOD_REG | (!(flags & EX86_SSE2_OP2) ? reg_lmap[b] : freg_lmap[b]));
+ buf_ptr++;
+ } else if (b & REG_MASK) {
+ reg_lmap_b = reg_lmap[b & REG_MASK];
+
+ if (!(b & OFFS_REG_MASK) || (b & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_SP)) {
+ if (immb != 0 || reg_lmap_b == 5) {
+ if (immb <= 127 && immb >= -128)
+ *buf_ptr |= 0x40;
+ else
+ *buf_ptr |= 0x80;
+ }
+
+ if (!(b & OFFS_REG_MASK))
+ *buf_ptr++ |= reg_lmap_b;
+ else {
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = U8(reg_lmap_b | (reg_lmap[OFFS_REG(b)] << 3));
+ }
+
+ if (immb != 0 || reg_lmap_b == 5) {
+ if (immb <= 127 && immb >= -128)
+ *buf_ptr++ = U8(immb); /* 8 bit displacement. */
+ else {
+ sljit_unaligned_store_s32(buf_ptr, (sljit_s32)immb); /* 32 bit displacement. */
+ buf_ptr += sizeof(sljit_s32);
+ }
+ }
+ }
+ else {
+ if (reg_lmap_b == 5)
+ *buf_ptr |= 0x40;
+
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = U8(reg_lmap_b | (reg_lmap[OFFS_REG(b)] << 3) | (immb << 6));
+
+ if (reg_lmap_b == 5)
+ *buf_ptr++ = 0;
+ }
+ }
+ else {
+ *buf_ptr++ |= 0x04;
+ *buf_ptr++ = 0x25;
+ sljit_unaligned_store_s32(buf_ptr, (sljit_s32)immb); /* 32 bit displacement. */
+ buf_ptr += sizeof(sljit_s32);
+ }
+
+ if (a & SLJIT_IMM) {
+ if (flags & EX86_BYTE_ARG)
+ *buf_ptr = U8(imma);
+ else if (flags & EX86_HALF_ARG)
+ sljit_unaligned_store_s16(buf_ptr, (sljit_s16)imma);
+ else if (!(flags & EX86_SHIFT_INS))
+ sljit_unaligned_store_s32(buf_ptr, (sljit_s32)imma);
+ }
+
+ return !(flags & EX86_SHIFT_INS) ? inst : (inst + 1);
+}
+
+/* --------------------------------------------------------------------- */
+/* Enter / return */
+/* --------------------------------------------------------------------- */
+
+static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr)
+{
+ sljit_uw type = jump->flags >> TYPE_SHIFT;
+
+ int short_addr = !(jump->flags & SLJIT_REWRITABLE_JUMP) && !(jump->flags & JUMP_LABEL) && (jump->u.target <= 0xffffffff);
+
+ /* The relative jump below specialized for this case. */
+ SLJIT_ASSERT(reg_map[TMP_REG2] >= 8);
+
+ if (type < SLJIT_JUMP) {
+ /* Invert type. */
+ *code_ptr++ = U8(get_jump_code(type ^ 0x1) - 0x10);
+ *code_ptr++ = short_addr ? (6 + 3) : (10 + 3);
+ }
+
+ *code_ptr++ = short_addr ? REX_B : (REX_W | REX_B);
+ *code_ptr++ = MOV_r_i32 | reg_lmap[TMP_REG2];
+ jump->addr = (sljit_uw)code_ptr;
+
+ if (jump->flags & JUMP_LABEL)
+ jump->flags |= PATCH_MD;
+ else if (short_addr)
+ sljit_unaligned_store_s32(code_ptr, (sljit_s32)jump->u.target);
+ else
+ sljit_unaligned_store_sw(code_ptr, (sljit_sw)jump->u.target);
+
+ code_ptr += short_addr ? sizeof(sljit_s32) : sizeof(sljit_sw);
+
+ *code_ptr++ = REX_B;
+ *code_ptr++ = GROUP_FF;
+ *code_ptr++ = U8(MOD_REG | (type >= SLJIT_FAST_CALL ? CALL_rm : JMP_rm) | reg_lmap[TMP_REG2]);
+
+ return code_ptr;
+}
+
+static sljit_u8* generate_put_label_code(struct sljit_put_label *put_label, sljit_u8 *code_ptr, sljit_uw max_label)
+{
+ if (max_label > HALFWORD_MAX) {
+ put_label->addr -= put_label->flags;
+ put_label->flags = PATCH_MD;
+ return code_ptr;
+ }
+
+ if (put_label->flags == 0) {
+ /* Destination is register. */
+ code_ptr = (sljit_u8*)put_label->addr - 2 - sizeof(sljit_uw);
+
+ SLJIT_ASSERT((code_ptr[0] & 0xf8) == REX_W);
+ SLJIT_ASSERT((code_ptr[1] & 0xf8) == MOV_r_i32);
+
+ if ((code_ptr[0] & 0x07) != 0) {
+ code_ptr[0] = U8(code_ptr[0] & ~0x08);
+ code_ptr += 2 + sizeof(sljit_s32);
+ }
+ else {
+ code_ptr[0] = code_ptr[1];
+ code_ptr += 1 + sizeof(sljit_s32);
+ }
+
+ put_label->addr = (sljit_uw)code_ptr;
+ return code_ptr;
+ }
+
+ code_ptr -= put_label->flags + (2 + sizeof(sljit_uw));
+ SLJIT_MEMMOVE(code_ptr, code_ptr + (2 + sizeof(sljit_uw)), put_label->flags);
+
+ SLJIT_ASSERT((code_ptr[0] & 0xf8) == REX_W);
+
+ if ((code_ptr[1] & 0xf8) == MOV_r_i32) {
+ code_ptr += 2 + sizeof(sljit_uw);
+ SLJIT_ASSERT((code_ptr[0] & 0xf8) == REX_W);
+ }
+
+ SLJIT_ASSERT(code_ptr[1] == MOV_rm_r);
+
+ code_ptr[0] = U8(code_ptr[0] & ~0x4);
+ code_ptr[1] = MOV_rm_i32;
+ code_ptr[2] = U8(code_ptr[2] & ~(0x7 << 3));
+
+ code_ptr = (sljit_u8*)(put_label->addr - (2 + sizeof(sljit_uw)) + sizeof(sljit_s32));
+ put_label->addr = (sljit_uw)code_ptr;
+ put_label->flags = 0;
+ return code_ptr;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_uw size;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 saved_arg_count = SLJIT_KEPT_SAVEDS_COUNT(options);
+ sljit_s32 saved_regs_size, tmp, i;
+#ifdef _WIN64
+ sljit_s32 saved_float_regs_size;
+ sljit_s32 saved_float_regs_offset = 0;
+ sljit_s32 float_arg_count = 0;
+#endif /* _WIN64 */
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_emit_enter(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+ if (options & SLJIT_ENTER_REG_ARG)
+ arg_types = 0;
+
+ /* Emit ENDBR64 at function entry if needed. */
+ FAIL_IF(emit_endbranch(compiler));
+
+ compiler->mode32 = 0;
+
+ /* Including the return address saved by the call instruction. */
+ saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - saved_arg_count, 1);
+
+ tmp = SLJIT_S0 - saveds;
+ for (i = SLJIT_S0 - saved_arg_count; i > tmp; i--) {
+ size = reg_map[i] >= 8 ? 2 : 1;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ if (reg_map[i] >= 8)
+ *inst++ = REX_B;
+ PUSH_REG(reg_lmap[i]);
+ }
+
+ for (i = scratches; i >= SLJIT_FIRST_SAVED_REG; i--) {
+ size = reg_map[i] >= 8 ? 2 : 1;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ if (reg_map[i] >= 8)
+ *inst++ = REX_B;
+ PUSH_REG(reg_lmap[i]);
+ }
+
+#ifdef _WIN64
+ local_size += SLJIT_LOCALS_OFFSET;
+ saved_float_regs_size = GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, 16);
+
+ if (saved_float_regs_size > 0) {
+ saved_float_regs_offset = ((local_size + 0xf) & ~0xf);
+ local_size = saved_float_regs_offset + saved_float_regs_size;
+ }
+#else /* !_WIN64 */
+ SLJIT_ASSERT(SLJIT_LOCALS_OFFSET == 0);
+#endif /* _WIN64 */
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types > 0) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64) {
+ tmp = 0;
+#ifndef _WIN64
+ switch (word_arg_count) {
+ case 0:
+ tmp = SLJIT_R2;
+ break;
+ case 1:
+ tmp = SLJIT_R1;
+ break;
+ case 2:
+ tmp = TMP_REG1;
+ break;
+ default:
+ tmp = SLJIT_R3;
+ break;
+ }
+#else /* !_WIN64 */
+ switch (word_arg_count + float_arg_count) {
+ case 0:
+ tmp = SLJIT_R3;
+ break;
+ case 1:
+ tmp = SLJIT_R1;
+ break;
+ case 2:
+ tmp = SLJIT_R2;
+ break;
+ default:
+ tmp = TMP_REG1;
+ break;
+ }
+#endif /* _WIN64 */
+ if (arg_types & SLJIT_ARG_TYPE_SCRATCH_REG) {
+ if (tmp != SLJIT_R0 + word_arg_count)
+ EMIT_MOV(compiler, SLJIT_R0 + word_arg_count, 0, tmp, 0);
+ } else {
+ EMIT_MOV(compiler, SLJIT_S0 - saved_arg_count, 0, tmp, 0);
+ saved_arg_count++;
+ }
+ word_arg_count++;
+ } else {
+#ifdef _WIN64
+ SLJIT_COMPILE_ASSERT(SLJIT_FR0 == 1, float_register_index_start);
+ float_arg_count++;
+ if (float_arg_count != float_arg_count + word_arg_count)
+ FAIL_IF(emit_sse2_load(compiler, (arg_types & SLJIT_ARG_MASK) == SLJIT_ARG_TYPE_F32,
+ float_arg_count, float_arg_count + word_arg_count, 0));
+#endif /* _WIN64 */
+ }
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ local_size = ((local_size + saved_regs_size + 0xf) & ~0xf) - saved_regs_size;
+ compiler->local_size = local_size;
+
+#ifdef _WIN64
+ if (local_size > 0) {
+ if (local_size <= 4 * 4096) {
+ if (local_size > 4096)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096);
+ if (local_size > 2 * 4096)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 2);
+ if (local_size > 3 * 4096)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -4096 * 3);
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, local_size >> 12);
+
+ EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_MEM1(SLJIT_SP), -4096);
+ BINARY_IMM32(SUB, 4096, SLJIT_SP, 0);
+ BINARY_IMM32(SUB, 1, TMP_REG1, 0);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+
+ INC_SIZE(2);
+ inst[0] = JNE_i8;
+ inst[1] = (sljit_u8)-21;
+ local_size &= 0xfff;
+ }
+
+ if (local_size > 0)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(SLJIT_SP), -local_size);
+ }
+#endif /* _WIN64 */
+
+ if (local_size > 0)
+ BINARY_IMM32(SUB, local_size, SLJIT_SP, 0);
+
+#ifdef _WIN64
+ if (saved_float_regs_size > 0) {
+ compiler->mode32 = 1;
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ inst = emit_x86_instruction(compiler, 2 | EX86_SSE2, i, 0, SLJIT_MEM1(SLJIT_SP), saved_float_regs_offset);
+ *inst++ = GROUP_0F;
+ *inst = MOVAPS_xm_x;
+ saved_float_regs_offset += 16;
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ inst = emit_x86_instruction(compiler, 2 | EX86_SSE2, i, 0, SLJIT_MEM1(SLJIT_SP), saved_float_regs_offset);
+ *inst++ = GROUP_0F;
+ *inst = MOVAPS_xm_x;
+ saved_float_regs_offset += 16;
+ }
+ }
+#endif /* _WIN64 */
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_set_context(struct sljit_compiler *compiler,
+ sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
+ sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size)
+{
+ sljit_s32 saved_regs_size;
+#ifdef _WIN64
+ sljit_s32 saved_float_regs_size;
+#endif /* _WIN64 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size));
+ set_set_context(compiler, options, arg_types, scratches, saveds, fscratches, fsaveds, local_size);
+
+#ifdef _WIN64
+ local_size += SLJIT_LOCALS_OFFSET;
+ saved_float_regs_size = GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, 16);
+
+ if (saved_float_regs_size > 0)
+ local_size = ((local_size + 0xf) & ~0xf) + saved_float_regs_size;
+#else /* !_WIN64 */
+ SLJIT_ASSERT(SLJIT_LOCALS_OFFSET == 0);
+#endif /* _WIN64 */
+
+ /* Including the return address saved by the call instruction. */
+ saved_regs_size = GET_SAVED_REGISTERS_SIZE(scratches, saveds - SLJIT_KEPT_SAVEDS_COUNT(options), 1);
+ compiler->local_size = ((local_size + saved_regs_size + 0xf) & ~0xf) - saved_regs_size;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_stack_frame_release(struct sljit_compiler *compiler, sljit_s32 is_return_to)
+{
+ sljit_uw size;
+ sljit_s32 local_size, i, tmp;
+ sljit_u8 *inst;
+#ifdef _WIN64
+ sljit_s32 saved_float_regs_offset;
+ sljit_s32 fscratches = compiler->fscratches;
+ sljit_s32 fsaveds = compiler->fsaveds;
+#endif /* _WIN64 */
+
+#ifdef _WIN64
+ saved_float_regs_offset = GET_SAVED_FLOAT_REGISTERS_SIZE(fscratches, fsaveds, 16);
+
+ if (saved_float_regs_offset > 0) {
+ compiler->mode32 = 1;
+ saved_float_regs_offset = (compiler->local_size - saved_float_regs_offset) & ~0xf;
+
+ tmp = SLJIT_FS0 - fsaveds;
+ for (i = SLJIT_FS0; i > tmp; i--) {
+ inst = emit_x86_instruction(compiler, 2 | EX86_SSE2, i, 0, SLJIT_MEM1(SLJIT_SP), saved_float_regs_offset);
+ *inst++ = GROUP_0F;
+ *inst = MOVAPS_x_xm;
+ saved_float_regs_offset += 16;
+ }
+
+ for (i = fscratches; i >= SLJIT_FIRST_SAVED_FLOAT_REG; i--) {
+ inst = emit_x86_instruction(compiler, 2 | EX86_SSE2, i, 0, SLJIT_MEM1(SLJIT_SP), saved_float_regs_offset);
+ *inst++ = GROUP_0F;
+ *inst = MOVAPS_x_xm;
+ saved_float_regs_offset += 16;
+ }
+
+ compiler->mode32 = 0;
+ }
+#endif /* _WIN64 */
+
+ local_size = compiler->local_size;
+
+ if (is_return_to && compiler->scratches < SLJIT_FIRST_SAVED_REG && (compiler->saveds == SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ local_size += SSIZE_OF(sw);
+ is_return_to = 0;
+ }
+
+ if (local_size > 0)
+ BINARY_IMM32(ADD, local_size, SLJIT_SP, 0);
+
+ tmp = compiler->scratches;
+ for (i = SLJIT_FIRST_SAVED_REG; i <= tmp; i++) {
+ size = reg_map[i] >= 8 ? 2 : 1;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ if (reg_map[i] >= 8)
+ *inst++ = REX_B;
+ POP_REG(reg_lmap[i]);
+ }
+
+ tmp = SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options);
+ for (i = SLJIT_S0 + 1 - compiler->saveds; i <= tmp; i++) {
+ size = reg_map[i] >= 8 ? 2 : 1;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ if (reg_map[i] >= 8)
+ *inst++ = REX_B;
+ POP_REG(reg_lmap[i]);
+ }
+
+ if (is_return_to)
+ BINARY_IMM32(ADD, sizeof(sljit_sw), SLJIT_SP, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_void(struct sljit_compiler *compiler)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_void(compiler));
+
+ compiler->mode32 = 0;
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ RET();
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return_to(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_return_to(compiler, src, srcw));
+
+ compiler->mode32 = 0;
+
+ if ((src & SLJIT_MEM) || (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options)))) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ EMIT_MOV(compiler, TMP_REG2, 0, src, srcw);
+ src = TMP_REG2;
+ srcw = 0;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 1));
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, SLJIT_JUMP, src, srcw);
+}
+
+/* --------------------------------------------------------------------- */
+/* Call / return instructions */
+/* --------------------------------------------------------------------- */
+
+#ifndef _WIN64
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src_ptr)
+{
+ sljit_s32 src = src_ptr ? (*src_ptr) : 0;
+ sljit_s32 word_arg_count = 0;
+
+ SLJIT_ASSERT(reg_map[SLJIT_R1] == 6 && reg_map[SLJIT_R3] == 1 && reg_map[TMP_REG1] == 2);
+ SLJIT_ASSERT(!(src & SLJIT_MEM));
+
+ /* Remove return value. */
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ if ((arg_types & SLJIT_ARG_MASK) < SLJIT_ARG_TYPE_F64)
+ word_arg_count++;
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (word_arg_count == 0)
+ return SLJIT_SUCCESS;
+
+ if (word_arg_count >= 3) {
+ if (src == SLJIT_R2)
+ *src_ptr = TMP_REG1;
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_R2, 0);
+ }
+
+ return emit_mov(compiler, SLJIT_R2, 0, SLJIT_R0, 0);
+}
+
+#else
+
+static sljit_s32 call_with_args(struct sljit_compiler *compiler, sljit_s32 arg_types, sljit_s32 *src_ptr)
+{
+ sljit_s32 src = src_ptr ? (*src_ptr) : 0;
+ sljit_s32 arg_count = 0;
+ sljit_s32 word_arg_count = 0;
+ sljit_s32 float_arg_count = 0;
+ sljit_s32 types = 0;
+ sljit_s32 data_trandfer = 0;
+ static sljit_u8 word_arg_regs[5] = { 0, SLJIT_R3, SLJIT_R1, SLJIT_R2, TMP_REG1 };
+
+ SLJIT_ASSERT(reg_map[SLJIT_R3] == 1 && reg_map[SLJIT_R1] == 2 && reg_map[SLJIT_R2] == 8 && reg_map[TMP_REG1] == 9);
+ SLJIT_ASSERT(!(src & SLJIT_MEM));
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+
+ while (arg_types) {
+ types = (types << SLJIT_ARG_SHIFT) | (arg_types & SLJIT_ARG_MASK);
+
+ switch (arg_types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ case SLJIT_ARG_TYPE_F32:
+ arg_count++;
+ float_arg_count++;
+
+ if (arg_count != float_arg_count)
+ data_trandfer = 1;
+ break;
+ default:
+ arg_count++;
+ word_arg_count++;
+
+ if (arg_count != word_arg_count || arg_count != word_arg_regs[arg_count]) {
+ data_trandfer = 1;
+
+ if (src == word_arg_regs[arg_count]) {
+ EMIT_MOV(compiler, TMP_REG2, 0, src, 0);
+ *src_ptr = TMP_REG2;
+ }
+ }
+ break;
+ }
+
+ arg_types >>= SLJIT_ARG_SHIFT;
+ }
+
+ if (!data_trandfer)
+ return SLJIT_SUCCESS;
+
+ while (types) {
+ switch (types & SLJIT_ARG_MASK) {
+ case SLJIT_ARG_TYPE_F64:
+ if (arg_count != float_arg_count)
+ FAIL_IF(emit_sse2_load(compiler, 0, arg_count, float_arg_count, 0));
+ arg_count--;
+ float_arg_count--;
+ break;
+ case SLJIT_ARG_TYPE_F32:
+ if (arg_count != float_arg_count)
+ FAIL_IF(emit_sse2_load(compiler, 1, arg_count, float_arg_count, 0));
+ arg_count--;
+ float_arg_count--;
+ break;
+ default:
+ if (arg_count != word_arg_count || arg_count != word_arg_regs[arg_count])
+ EMIT_MOV(compiler, word_arg_regs[arg_count], 0, word_arg_count, 0);
+ arg_count--;
+ word_arg_count--;
+ break;
+ }
+
+ types >>= SLJIT_ARG_SHIFT;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_call(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types)
+{
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_call(compiler, type, arg_types));
+
+ compiler->mode32 = 0;
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ PTR_FAIL_IF(call_with_args(compiler, arg_types, NULL));
+
+ if (type & SLJIT_CALL_RETURN) {
+ PTR_FAIL_IF(emit_stack_frame_release(compiler, 0));
+ type = SLJIT_JUMP | (type & SLJIT_REWRITABLE_JUMP);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_jump(compiler, type);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_icall(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 arg_types,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_icall(compiler, type, arg_types, src, srcw));
+
+ compiler->mode32 = 0;
+
+ if (src & SLJIT_MEM) {
+ ADJUST_LOCAL_OFFSET(src, srcw);
+ EMIT_MOV(compiler, TMP_REG2, 0, src, srcw);
+ src = TMP_REG2;
+ }
+
+ if (type & SLJIT_CALL_RETURN) {
+ if (src >= SLJIT_FIRST_SAVED_REG && src <= (SLJIT_S0 - SLJIT_KEPT_SAVEDS_COUNT(compiler->options))) {
+ EMIT_MOV(compiler, TMP_REG2, 0, src, srcw);
+ src = TMP_REG2;
+ }
+
+ FAIL_IF(emit_stack_frame_release(compiler, 0));
+ }
+
+ if ((type & 0xff) != SLJIT_CALL_REG_ARG)
+ FAIL_IF(call_with_args(compiler, arg_types, &src));
+
+ if (type & SLJIT_CALL_RETURN)
+ type = SLJIT_JUMP;
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_ijump(compiler, type, src, srcw);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fast_enter(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fast_enter(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ if (FAST_IS_REG(dst)) {
+ if (reg_map[dst] < 8) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ POP_REG(reg_lmap[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = REX_B;
+ POP_REG(reg_lmap[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ /* REX_W is not necessary (src is not immediate). */
+ compiler->mode32 = 1;
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst++ = POP_rm;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_fast_return(struct sljit_compiler *compiler, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8 *inst;
+
+ if (FAST_IS_REG(src)) {
+ if (reg_map[src] < 8) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 1);
+ FAIL_IF(!inst);
+
+ INC_SIZE(1 + 1);
+ PUSH_REG(reg_lmap[src]);
+ }
+ else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2 + 1);
+ FAIL_IF(!inst);
+
+ INC_SIZE(2 + 1);
+ *inst++ = REX_B;
+ PUSH_REG(reg_lmap[src]);
+ }
+ }
+ else {
+ /* REX_W is not necessary (src is not immediate). */
+ compiler->mode32 = 1;
+ inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_FF;
+ *inst |= PUSH_rm;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ }
+
+ RET();
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Other operations */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_mem(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 reg,
+ sljit_s32 mem, sljit_sw memw)
+{
+ sljit_u8* inst;
+ sljit_s32 i, next, reg_idx;
+ sljit_u8 regs[2];
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_mem(compiler, type, reg, mem, memw));
+
+ if (!(reg & REG_PAIR_MASK))
+ return sljit_emit_mem_unaligned(compiler, type, reg, mem, memw);
+
+ ADJUST_LOCAL_OFFSET(mem, memw);
+
+ compiler->mode32 = 0;
+
+ if ((mem & REG_MASK) == 0) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, memw);
+
+ mem = SLJIT_MEM1(TMP_REG1);
+ memw = 0;
+ } else if (!(mem & OFFS_REG_MASK) && ((memw < HALFWORD_MIN) || (memw > HALFWORD_MAX - SSIZE_OF(sw)))) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, memw);
+
+ mem = SLJIT_MEM2(mem & REG_MASK, TMP_REG1);
+ memw = 0;
+ }
+
+ regs[0] = U8(REG_PAIR_FIRST(reg));
+ regs[1] = U8(REG_PAIR_SECOND(reg));
+
+ next = SSIZE_OF(sw);
+
+ if (!(type & SLJIT_MEM_STORE) && (regs[0] == (mem & REG_MASK) || regs[0] == OFFS_REG(mem))) {
+ if (regs[1] == (mem & REG_MASK) || regs[1] == OFFS_REG(mem)) {
+ /* Base and offset cannot be TMP_REG1. */
+ EMIT_MOV(compiler, TMP_REG1, 0, OFFS_REG(mem), 0);
+
+ if (regs[1] == OFFS_REG(mem))
+ next = -SSIZE_OF(sw);
+
+ mem = (mem & ~OFFS_REG_MASK) | TO_OFFS_REG(TMP_REG1);
+ } else {
+ next = -SSIZE_OF(sw);
+
+ if (!(mem & OFFS_REG_MASK))
+ memw += SSIZE_OF(sw);
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ reg_idx = next > 0 ? i : (i ^ 0x1);
+ reg = regs[reg_idx];
+
+ if ((mem & OFFS_REG_MASK) && (reg_idx == 1)) {
+ inst = (sljit_u8*)ensure_buf(compiler, (sljit_uw)(1 + 5));
+ FAIL_IF(!inst);
+
+ INC_SIZE(5);
+
+ inst[0] = U8(REX_W | ((reg_map[reg] >= 8) ? REX_R : 0) | ((reg_map[mem & REG_MASK] >= 8) ? REX_B : 0) | ((reg_map[OFFS_REG(mem)] >= 8) ? REX_X : 0));
+ inst[1] = (type & SLJIT_MEM_STORE) ? MOV_rm_r : MOV_r_rm;
+ inst[2] = 0x44 | U8(reg_lmap[reg] << 3);
+ inst[3] = U8(memw << 6) | U8(reg_lmap[OFFS_REG(mem)] << 3) | reg_lmap[mem & REG_MASK];
+ inst[4] = sizeof(sljit_sw);
+ } else if (type & SLJIT_MEM_STORE) {
+ EMIT_MOV(compiler, mem, memw, reg, 0);
+ } else {
+ EMIT_MOV(compiler, reg, 0, mem, memw);
+ }
+
+ if (!(mem & OFFS_REG_MASK))
+ memw += next;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_mov_int(struct sljit_compiler *compiler, sljit_s32 sign,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r;
+
+ compiler->mode32 = 0;
+
+ if (src & SLJIT_IMM) {
+ if (FAST_IS_REG(dst)) {
+ if (sign || ((sljit_uw)srcw <= 0x7fffffff)) {
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, (sljit_sw)(sljit_s32)srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+ }
+ return emit_load_imm64(compiler, dst, srcw);
+ }
+ compiler->mode32 = 1;
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, (sljit_sw)(sljit_s32)srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ compiler->mode32 = 0;
+ return SLJIT_SUCCESS;
+ }
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if ((dst & SLJIT_MEM) && FAST_IS_REG(src))
+ dst_r = src;
+ else {
+ if (sign) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = MOVSXD_r_rm;
+ } else {
+ compiler->mode32 = 1;
+ FAIL_IF(emit_mov(compiler, dst_r, 0, src, srcw));
+ compiler->mode32 = 0;
+ }
+ }
+
+ if (dst & SLJIT_MEM) {
+ compiler->mode32 = 1;
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ compiler->mode32 = 0;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 skip_frames_before_return(struct sljit_compiler *compiler)
+{
+ sljit_s32 tmp, size;
+
+ /* Don't adjust shadow stack if it isn't enabled. */
+ if (!cpu_has_shadow_stack())
+ return SLJIT_SUCCESS;
+
+ size = compiler->local_size;
+ tmp = compiler->scratches;
+ if (tmp >= SLJIT_FIRST_SAVED_REG)
+ size += (tmp - SLJIT_FIRST_SAVED_REG + 1) * SSIZE_OF(sw);
+ tmp = compiler->saveds < SLJIT_NUMBER_OF_SAVED_REGISTERS ? (SLJIT_S0 + 1 - compiler->saveds) : SLJIT_FIRST_SAVED_REG;
+ if (SLJIT_S0 >= tmp)
+ size += (SLJIT_S0 - tmp + 1) * SSIZE_OF(sw);
+
+ return adjust_shadow_stack(compiler, SLJIT_MEM1(SLJIT_SP), size);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitNativeX86_common.c b/contrib/libs/pcre2/src/sljit/sljitNativeX86_common.c
new file mode 100644
index 0000000000..651942be80
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitNativeX86_common.c
@@ -0,0 +1,3422 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+SLJIT_API_FUNC_ATTRIBUTE const char* sljit_get_platform_name(void)
+{
+ return "x86" SLJIT_CPUINFO;
+}
+
+/*
+ 32b register indexes:
+ 0 - EAX
+ 1 - ECX
+ 2 - EDX
+ 3 - EBX
+ 4 - ESP
+ 5 - EBP
+ 6 - ESI
+ 7 - EDI
+*/
+
+/*
+ 64b register indexes:
+ 0 - RAX
+ 1 - RCX
+ 2 - RDX
+ 3 - RBX
+ 4 - RSP
+ 5 - RBP
+ 6 - RSI
+ 7 - RDI
+ 8 - R8 - From now on REX prefix is required
+ 9 - R9
+ 10 - R10
+ 11 - R11
+ 12 - R12
+ 13 - R13
+ 14 - R14
+ 15 - R15
+*/
+
+#define TMP_FREG (0)
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+
+/* Last register + 1. */
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 3] = {
+ 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 7, 6, 3, 4, 5
+};
+
+#define CHECK_EXTRA_REGS(p, w, do) \
+ if (p >= SLJIT_R3 && p <= SLJIT_S3) { \
+ w = (2 * SSIZE_OF(sw)) + ((p) - SLJIT_R3) * SSIZE_OF(sw); \
+ p = SLJIT_MEM1(SLJIT_SP); \
+ do; \
+ }
+
+#else /* SLJIT_CONFIG_X86_32 */
+
+/* Last register + 1. */
+#define TMP_REG1 (SLJIT_NUMBER_OF_REGISTERS + 2)
+#define TMP_REG2 (SLJIT_NUMBER_OF_REGISTERS + 3)
+
+/* Note: r12 & 0x7 == 0b100, which decoded as SIB byte present
+ Note: avoid to use r12 and r13 for memory addessing
+ therefore r12 is better to be a higher saved register. */
+#ifndef _WIN64
+/* Args: rdi(=7), rsi(=6), rdx(=2), rcx(=1), r8, r9. Scratches: rax(=0), r10, r11 */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 6, 7, 1, 8, 11, 10, 12, 5, 13, 14, 15, 3, 4, 2, 9
+};
+/* low-map. reg_map & 0x7. */
+static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 6, 7, 1, 0, 3, 2, 4, 5, 5, 6, 7, 3, 4, 2, 1
+};
+#else
+/* Args: rcx(=1), rdx(=2), r8, r9. Scratches: rax(=0), r10, r11 */
+static const sljit_u8 reg_map[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 2, 8, 1, 11, 12, 5, 13, 14, 15, 7, 6, 3, 4, 9, 10
+};
+/* low-map. reg_map & 0x7. */
+static const sljit_u8 reg_lmap[SLJIT_NUMBER_OF_REGISTERS + 4] = {
+ 0, 0, 2, 0, 1, 3, 4, 5, 5, 6, 7, 7, 6, 3, 4, 1, 2
+};
+#endif
+
+/* Args: xmm0-xmm3 */
+static const sljit_u8 freg_map[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = {
+ 4, 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+/* low-map. freg_map & 0x7. */
+static const sljit_u8 freg_lmap[SLJIT_NUMBER_OF_FLOAT_REGISTERS + 1] = {
+ 4, 0, 1, 2, 3, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7
+};
+
+#define REX_W 0x48
+#define REX_R 0x44
+#define REX_X 0x42
+#define REX_B 0x41
+#define REX 0x40
+
+#ifndef _WIN64
+#define HALFWORD_MAX 0x7fffffffl
+#define HALFWORD_MIN -0x80000000l
+#else
+#define HALFWORD_MAX 0x7fffffffll
+#define HALFWORD_MIN -0x80000000ll
+#endif
+
+#define IS_HALFWORD(x) ((x) <= HALFWORD_MAX && (x) >= HALFWORD_MIN)
+#define NOT_HALFWORD(x) ((x) > HALFWORD_MAX || (x) < HALFWORD_MIN)
+
+#define CHECK_EXTRA_REGS(p, w, do)
+
+#endif /* SLJIT_CONFIG_X86_32 */
+
+#define U8(v) ((sljit_u8)(v))
+
+
+/* Size flags for emit_x86_instruction: */
+#define EX86_BIN_INS 0x0010
+#define EX86_SHIFT_INS 0x0020
+#define EX86_REX 0x0040
+#define EX86_NO_REXW 0x0080
+#define EX86_BYTE_ARG 0x0100
+#define EX86_HALF_ARG 0x0200
+#define EX86_PREF_66 0x0400
+#define EX86_PREF_F2 0x0800
+#define EX86_PREF_F3 0x1000
+#define EX86_SSE2_OP1 0x2000
+#define EX86_SSE2_OP2 0x4000
+#define EX86_SSE2 (EX86_SSE2_OP1 | EX86_SSE2_OP2)
+
+/* --------------------------------------------------------------------- */
+/* Instrucion forms */
+/* --------------------------------------------------------------------- */
+
+#define ADD (/* BINARY */ 0 << 3)
+#define ADD_EAX_i32 0x05
+#define ADD_r_rm 0x03
+#define ADD_rm_r 0x01
+#define ADDSD_x_xm 0x58
+#define ADC (/* BINARY */ 2 << 3)
+#define ADC_EAX_i32 0x15
+#define ADC_r_rm 0x13
+#define ADC_rm_r 0x11
+#define AND (/* BINARY */ 4 << 3)
+#define AND_EAX_i32 0x25
+#define AND_r_rm 0x23
+#define AND_rm_r 0x21
+#define ANDPD_x_xm 0x54
+#define BSR_r_rm (/* GROUP_0F */ 0xbd)
+#define BSF_r_rm (/* GROUP_0F */ 0xbc)
+#define CALL_i32 0xe8
+#define CALL_rm (/* GROUP_FF */ 2 << 3)
+#define CDQ 0x99
+#define CMOVE_r_rm (/* GROUP_0F */ 0x44)
+#define CMP (/* BINARY */ 7 << 3)
+#define CMP_EAX_i32 0x3d
+#define CMP_r_rm 0x3b
+#define CMP_rm_r 0x39
+#define CVTPD2PS_x_xm 0x5a
+#define CVTSI2SD_x_rm 0x2a
+#define CVTTSD2SI_r_xm 0x2c
+#define DIV (/* GROUP_F7 */ 6 << 3)
+#define DIVSD_x_xm 0x5e
+#define FLDS 0xd9
+#define FLDL 0xdd
+#define FSTPS 0xd9
+#define FSTPD 0xdd
+#define INT3 0xcc
+#define IDIV (/* GROUP_F7 */ 7 << 3)
+#define IMUL (/* GROUP_F7 */ 5 << 3)
+#define IMUL_r_rm (/* GROUP_0F */ 0xaf)
+#define IMUL_r_rm_i8 0x6b
+#define IMUL_r_rm_i32 0x69
+#define JE_i8 0x74
+#define JNE_i8 0x75
+#define JMP_i8 0xeb
+#define JMP_i32 0xe9
+#define JMP_rm (/* GROUP_FF */ 4 << 3)
+#define LEA_r_m 0x8d
+#define LOOP_i8 0xe2
+#define LZCNT_r_rm (/* GROUP_F3 */ /* GROUP_0F */ 0xbd)
+#define MOV_r_rm 0x8b
+#define MOV_r_i32 0xb8
+#define MOV_rm_r 0x89
+#define MOV_rm_i32 0xc7
+#define MOV_rm8_i8 0xc6
+#define MOV_rm8_r8 0x88
+#define MOVAPS_x_xm 0x28
+#define MOVAPS_xm_x 0x29
+#define MOVSD_x_xm 0x10
+#define MOVSD_xm_x 0x11
+#define MOVSXD_r_rm 0x63
+#define MOVSX_r_rm8 (/* GROUP_0F */ 0xbe)
+#define MOVSX_r_rm16 (/* GROUP_0F */ 0xbf)
+#define MOVZX_r_rm8 (/* GROUP_0F */ 0xb6)
+#define MOVZX_r_rm16 (/* GROUP_0F */ 0xb7)
+#define MUL (/* GROUP_F7 */ 4 << 3)
+#define MULSD_x_xm 0x59
+#define NEG_rm (/* GROUP_F7 */ 3 << 3)
+#define NOP 0x90
+#define NOT_rm (/* GROUP_F7 */ 2 << 3)
+#define OR (/* BINARY */ 1 << 3)
+#define OR_r_rm 0x0b
+#define OR_EAX_i32 0x0d
+#define OR_rm_r 0x09
+#define OR_rm8_r8 0x08
+#define POP_r 0x58
+#define POP_rm 0x8f
+#define POPF 0x9d
+#define PREFETCH 0x18
+#define PUSH_i32 0x68
+#define PUSH_r 0x50
+#define PUSH_rm (/* GROUP_FF */ 6 << 3)
+#define PUSHF 0x9c
+#define ROL (/* SHIFT */ 0 << 3)
+#define ROR (/* SHIFT */ 1 << 3)
+#define RET_near 0xc3
+#define RET_i16 0xc2
+#define SBB (/* BINARY */ 3 << 3)
+#define SBB_EAX_i32 0x1d
+#define SBB_r_rm 0x1b
+#define SBB_rm_r 0x19
+#define SAR (/* SHIFT */ 7 << 3)
+#define SHL (/* SHIFT */ 4 << 3)
+#define SHLD (/* GROUP_0F */ 0xa5)
+#define SHRD (/* GROUP_0F */ 0xad)
+#define SHR (/* SHIFT */ 5 << 3)
+#define SUB (/* BINARY */ 5 << 3)
+#define SUB_EAX_i32 0x2d
+#define SUB_r_rm 0x2b
+#define SUB_rm_r 0x29
+#define SUBSD_x_xm 0x5c
+#define TEST_EAX_i32 0xa9
+#define TEST_rm_r 0x85
+#define TZCNT_r_rm (/* GROUP_F3 */ /* GROUP_0F */ 0xbc)
+#define UCOMISD_x_xm 0x2e
+#define UNPCKLPD_x_xm 0x14
+#define XCHG_EAX_r 0x90
+#define XCHG_r_rm 0x87
+#define XOR (/* BINARY */ 6 << 3)
+#define XOR_EAX_i32 0x35
+#define XOR_r_rm 0x33
+#define XOR_rm_r 0x31
+#define XORPD_x_xm 0x57
+
+#define GROUP_0F 0x0f
+#define GROUP_F3 0xf3
+#define GROUP_F7 0xf7
+#define GROUP_FF 0xff
+#define GROUP_BINARY_81 0x81
+#define GROUP_BINARY_83 0x83
+#define GROUP_SHIFT_1 0xd1
+#define GROUP_SHIFT_N 0xc1
+#define GROUP_SHIFT_CL 0xd3
+
+#define MOD_REG 0xc0
+#define MOD_DISP8 0x40
+
+#define INC_SIZE(s) (*inst++ = U8(s), compiler->size += (s))
+
+#define PUSH_REG(r) (*inst++ = U8(PUSH_r + (r)))
+#define POP_REG(r) (*inst++ = U8(POP_r + (r)))
+#define RET() (*inst++ = RET_near)
+#define RET_I16(n) (*inst++ = RET_i16, *inst++ = U8(n), *inst++ = 0)
+
+/* Multithreading does not affect these static variables, since they store
+ built-in CPU features. Therefore they can be overwritten by different threads
+ if they detect the CPU features in the same time. */
+#define CPU_FEATURE_DETECTED 0x001
+#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+#define CPU_FEATURE_SSE2 0x002
+#endif
+#define CPU_FEATURE_LZCNT 0x004
+#define CPU_FEATURE_TZCNT 0x008
+#define CPU_FEATURE_CMOV 0x010
+
+static sljit_u32 cpu_feature_list = 0;
+
+#ifdef _WIN32_WCE
+#include <cmnintrin.h>
+#elif defined(_MSC_VER) && _MSC_VER >= 1400
+#include <intrin.h>
+#endif
+
+/******************************************************/
+/* Unaligned-store functions */
+/******************************************************/
+
+static SLJIT_INLINE void sljit_unaligned_store_s16(void *addr, sljit_s16 value)
+{
+ SLJIT_MEMCPY(addr, &value, sizeof(value));
+}
+
+static SLJIT_INLINE void sljit_unaligned_store_s32(void *addr, sljit_s32 value)
+{
+ SLJIT_MEMCPY(addr, &value, sizeof(value));
+}
+
+static SLJIT_INLINE void sljit_unaligned_store_sw(void *addr, sljit_sw value)
+{
+ SLJIT_MEMCPY(addr, &value, sizeof(value));
+}
+
+/******************************************************/
+/* Utility functions */
+/******************************************************/
+
+static void get_cpu_features(void)
+{
+ sljit_u32 feature_list = CPU_FEATURE_DETECTED;
+ sljit_u32 value;
+
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+
+ int CPUInfo[4];
+
+ __cpuid(CPUInfo, 0);
+ if (CPUInfo[0] >= 7) {
+ __cpuidex(CPUInfo, 7, 0);
+ if (CPUInfo[1] & 0x8)
+ feature_list |= CPU_FEATURE_TZCNT;
+ }
+
+ __cpuid(CPUInfo, (int)0x80000001);
+ if (CPUInfo[2] & 0x20)
+ feature_list |= CPU_FEATURE_LZCNT;
+
+ __cpuid(CPUInfo, 1);
+ value = (sljit_u32)CPUInfo[3];
+
+#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_C)
+
+ /* AT&T syntax. */
+ __asm__ (
+ "movl $0x0, %%eax\n"
+ "lzcnt %%eax, %%eax\n"
+ "setnz %%al\n"
+ "movl %%eax, %0\n"
+ : "=g" (value)
+ :
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ : "eax"
+#else
+ : "rax"
+#endif
+ );
+
+ if (value & 0x1)
+ feature_list |= CPU_FEATURE_LZCNT;
+
+ __asm__ (
+ "movl $0x0, %%eax\n"
+ "tzcnt %%eax, %%eax\n"
+ "setnz %%al\n"
+ "movl %%eax, %0\n"
+ : "=g" (value)
+ :
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ : "eax"
+#else
+ : "rax"
+#endif
+ );
+
+ if (value & 0x1)
+ feature_list |= CPU_FEATURE_TZCNT;
+
+ __asm__ (
+ "movl $0x1, %%eax\n"
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ /* On x86-32, there is no red zone, so this
+ should work (no need for a local variable). */
+ "push %%ebx\n"
+#endif
+ "cpuid\n"
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ "pop %%ebx\n"
+#endif
+ "movl %%edx, %0\n"
+ : "=g" (value)
+ :
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ : "%eax", "%ecx", "%edx"
+#else
+ : "%rax", "%rbx", "%rcx", "%rdx"
+#endif
+ );
+
+#else /* _MSC_VER && _MSC_VER >= 1400 */
+
+ /* Intel syntax. */
+ __asm {
+ mov eax, 0
+ lzcnt eax, eax
+ setnz al
+ mov value, eax
+ }
+
+ if (value & 0x1)
+ feature_list |= CPU_FEATURE_LZCNT;
+
+ __asm {
+ mov eax, 0
+ tzcnt eax, eax
+ setnz al
+ mov value, eax
+ }
+
+ if (value & 0x1)
+ feature_list |= CPU_FEATURE_TZCNT;
+
+ __asm {
+ mov eax, 1
+ cpuid
+ mov value, edx
+ }
+
+#endif /* _MSC_VER && _MSC_VER >= 1400 */
+
+#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+ if (value & 0x4000000)
+ feature_list |= CPU_FEATURE_SSE2;
+#endif
+ if (value & 0x8000)
+ feature_list |= CPU_FEATURE_CMOV;
+
+ cpu_feature_list = feature_list;
+}
+
+static sljit_u8 get_jump_code(sljit_uw type)
+{
+ switch (type) {
+ case SLJIT_EQUAL:
+ case SLJIT_F_EQUAL:
+ case SLJIT_UNORDERED_OR_EQUAL:
+ case SLJIT_ORDERED_EQUAL: /* Not supported. */
+ return 0x84 /* je */;
+
+ case SLJIT_NOT_EQUAL:
+ case SLJIT_F_NOT_EQUAL:
+ case SLJIT_ORDERED_NOT_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL: /* Not supported. */
+ return 0x85 /* jne */;
+
+ case SLJIT_LESS:
+ case SLJIT_CARRY:
+ case SLJIT_F_LESS:
+ case SLJIT_UNORDERED_OR_LESS:
+ case SLJIT_UNORDERED_OR_GREATER:
+ return 0x82 /* jc */;
+
+ case SLJIT_GREATER_EQUAL:
+ case SLJIT_NOT_CARRY:
+ case SLJIT_F_GREATER_EQUAL:
+ case SLJIT_ORDERED_GREATER_EQUAL:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ return 0x83 /* jae */;
+
+ case SLJIT_GREATER:
+ case SLJIT_F_GREATER:
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_ORDERED_GREATER:
+ return 0x87 /* jnbe */;
+
+ case SLJIT_LESS_EQUAL:
+ case SLJIT_F_LESS_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_LESS_EQUAL:
+ return 0x86 /* jbe */;
+
+ case SLJIT_SIG_LESS:
+ return 0x8c /* jl */;
+
+ case SLJIT_SIG_GREATER_EQUAL:
+ return 0x8d /* jnl */;
+
+ case SLJIT_SIG_GREATER:
+ return 0x8f /* jnle */;
+
+ case SLJIT_SIG_LESS_EQUAL:
+ return 0x8e /* jle */;
+
+ case SLJIT_OVERFLOW:
+ return 0x80 /* jo */;
+
+ case SLJIT_NOT_OVERFLOW:
+ return 0x81 /* jno */;
+
+ case SLJIT_UNORDERED:
+ return 0x8a /* jp */;
+
+ case SLJIT_ORDERED:
+ return 0x8b /* jpo */;
+ }
+ return 0;
+}
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_sw executable_offset);
+#else
+static sljit_u8* generate_far_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr);
+static sljit_u8* generate_put_label_code(struct sljit_put_label *put_label, sljit_u8 *code_ptr, sljit_uw max_label);
+#endif
+
+static sljit_u8* generate_near_jump_code(struct sljit_jump *jump, sljit_u8 *code_ptr, sljit_u8 *code, sljit_sw executable_offset)
+{
+ sljit_uw type = jump->flags >> TYPE_SHIFT;
+ sljit_s32 short_jump;
+ sljit_uw label_addr;
+
+ if (jump->flags & JUMP_LABEL)
+ label_addr = (sljit_uw)(code + jump->u.label->size);
+ else
+ label_addr = jump->u.target - (sljit_uw)executable_offset;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((sljit_sw)(label_addr - (jump->addr + 1)) > HALFWORD_MAX || (sljit_sw)(label_addr - (jump->addr + 1)) < HALFWORD_MIN)
+ return generate_far_jump_code(jump, code_ptr);
+#endif
+
+ short_jump = (sljit_sw)(label_addr - (jump->addr + 2)) >= -128 && (sljit_sw)(label_addr - (jump->addr + 2)) <= 127;
+
+ if (type == SLJIT_JUMP) {
+ if (short_jump)
+ *code_ptr++ = JMP_i8;
+ else
+ *code_ptr++ = JMP_i32;
+ jump->addr++;
+ }
+ else if (type >= SLJIT_FAST_CALL) {
+ short_jump = 0;
+ *code_ptr++ = CALL_i32;
+ jump->addr++;
+ }
+ else if (short_jump) {
+ *code_ptr++ = U8(get_jump_code(type) - 0x10);
+ jump->addr++;
+ }
+ else {
+ *code_ptr++ = GROUP_0F;
+ *code_ptr++ = get_jump_code(type);
+ jump->addr += 2;
+ }
+
+ if (short_jump) {
+ jump->flags |= PATCH_MB;
+ code_ptr += sizeof(sljit_s8);
+ } else {
+ jump->flags |= PATCH_MW;
+ code_ptr += sizeof(sljit_s32);
+ }
+
+ return code_ptr;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler)
+{
+ struct sljit_memory_fragment *buf;
+ sljit_u8 *code;
+ sljit_u8 *code_ptr;
+ sljit_u8 *buf_ptr;
+ sljit_u8 *buf_end;
+ sljit_u8 len;
+ sljit_sw executable_offset;
+ sljit_uw jump_addr;
+
+ struct sljit_label *label;
+ struct sljit_jump *jump;
+ struct sljit_const *const_;
+ struct sljit_put_label *put_label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_generate_code(compiler));
+ reverse_buf(compiler);
+
+ /* Second code generation pass. */
+ code = (sljit_u8*)SLJIT_MALLOC_EXEC(compiler->size, compiler->exec_allocator_data);
+ PTR_FAIL_WITH_EXEC_IF(code);
+ buf = compiler->buf;
+
+ code_ptr = code;
+ label = compiler->labels;
+ jump = compiler->jumps;
+ const_ = compiler->consts;
+ put_label = compiler->put_labels;
+ executable_offset = SLJIT_EXEC_OFFSET(code);
+
+ do {
+ buf_ptr = buf->memory;
+ buf_end = buf_ptr + buf->used_size;
+ do {
+ len = *buf_ptr++;
+ if (len > 0) {
+ /* The code is already generated. */
+ SLJIT_MEMCPY(code_ptr, buf_ptr, len);
+ code_ptr += len;
+ buf_ptr += len;
+ }
+ else {
+ switch (*buf_ptr) {
+ case 0:
+ label->addr = (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset);
+ label->size = (sljit_uw)(code_ptr - code);
+ label = label->next;
+ break;
+ case 1:
+ jump->addr = (sljit_uw)code_ptr;
+ if (!(jump->flags & SLJIT_REWRITABLE_JUMP))
+ code_ptr = generate_near_jump_code(jump, code_ptr, code, executable_offset);
+ else {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ code_ptr = generate_far_jump_code(jump, code_ptr, executable_offset);
+#else
+ code_ptr = generate_far_jump_code(jump, code_ptr);
+#endif
+ }
+ jump = jump->next;
+ break;
+ case 2:
+ const_->addr = ((sljit_uw)code_ptr) - sizeof(sljit_sw);
+ const_ = const_->next;
+ break;
+ default:
+ SLJIT_ASSERT(*buf_ptr == 3);
+ SLJIT_ASSERT(put_label->label);
+ put_label->addr = (sljit_uw)code_ptr;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ code_ptr = generate_put_label_code(put_label, code_ptr, (sljit_uw)SLJIT_ADD_EXEC_OFFSET(code, executable_offset) + put_label->label->size);
+#endif
+ put_label = put_label->next;
+ break;
+ }
+ buf_ptr++;
+ }
+ } while (buf_ptr < buf_end);
+ SLJIT_ASSERT(buf_ptr == buf_end);
+ buf = buf->next;
+ } while (buf);
+
+ SLJIT_ASSERT(!label);
+ SLJIT_ASSERT(!jump);
+ SLJIT_ASSERT(!const_);
+ SLJIT_ASSERT(!put_label);
+ SLJIT_ASSERT(code_ptr <= code + compiler->size);
+
+ jump = compiler->jumps;
+ while (jump) {
+ if (jump->flags & (PATCH_MB | PATCH_MW)) {
+ if (jump->flags & JUMP_LABEL)
+ jump_addr = jump->u.label->addr;
+ else
+ jump_addr = jump->u.target;
+
+ jump_addr -= jump->addr + (sljit_uw)executable_offset;
+
+ if (jump->flags & PATCH_MB) {
+ jump_addr -= sizeof(sljit_s8);
+ SLJIT_ASSERT((sljit_sw)jump_addr >= -128 && (sljit_sw)jump_addr <= 127);
+ *(sljit_u8*)jump->addr = U8(jump_addr);
+ } else {
+ jump_addr -= sizeof(sljit_s32);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)jump_addr);
+#else
+ SLJIT_ASSERT((sljit_sw)jump_addr >= HALFWORD_MIN && (sljit_sw)jump_addr <= HALFWORD_MAX);
+ sljit_unaligned_store_s32((void*)jump->addr, (sljit_s32)jump_addr);
+#endif
+ }
+ }
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ else if (jump->flags & PATCH_MD) {
+ SLJIT_ASSERT(jump->flags & JUMP_LABEL);
+ sljit_unaligned_store_sw((void*)jump->addr, (sljit_sw)jump->u.label->addr);
+ }
+#endif
+
+ jump = jump->next;
+ }
+
+ put_label = compiler->put_labels;
+ while (put_label) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_unaligned_store_sw((void*)(put_label->addr - sizeof(sljit_sw)), (sljit_sw)put_label->label->addr);
+#else
+ if (put_label->flags & PATCH_MD) {
+ SLJIT_ASSERT(put_label->label->addr > HALFWORD_MAX);
+ sljit_unaligned_store_sw((void*)(put_label->addr - sizeof(sljit_sw)), (sljit_sw)put_label->label->addr);
+ }
+ else {
+ SLJIT_ASSERT(put_label->label->addr <= HALFWORD_MAX);
+ sljit_unaligned_store_s32((void*)(put_label->addr - sizeof(sljit_s32)), (sljit_s32)put_label->label->addr);
+ }
+#endif
+
+ put_label = put_label->next;
+ }
+
+ compiler->error = SLJIT_ERR_COMPILED;
+ compiler->executable_offset = executable_offset;
+ compiler->executable_size = (sljit_uw)(code_ptr - code);
+
+ code = (sljit_u8*)SLJIT_ADD_EXEC_OFFSET(code, executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS(code, (sljit_u8*)SLJIT_ADD_EXEC_OFFSET(code_ptr, executable_offset), 1);
+ return (void*)code;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_has_cpu_feature(sljit_s32 feature_type)
+{
+ switch (feature_type) {
+ case SLJIT_HAS_FPU:
+#ifdef SLJIT_IS_FPU_AVAILABLE
+ return SLJIT_IS_FPU_AVAILABLE;
+#elif (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+ return (cpu_feature_list & CPU_FEATURE_SSE2) != 0;
+#else /* SLJIT_DETECT_SSE2 */
+ return 1;
+#endif /* SLJIT_DETECT_SSE2 */
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ case SLJIT_HAS_VIRTUAL_REGISTERS:
+ return 1;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ case SLJIT_HAS_CLZ:
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+
+ return (cpu_feature_list & CPU_FEATURE_LZCNT) ? 1 : 2;
+
+ case SLJIT_HAS_CTZ:
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+
+ return (cpu_feature_list & CPU_FEATURE_TZCNT) ? 1 : 2;
+
+ case SLJIT_HAS_CMOV:
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+ return (cpu_feature_list & CPU_FEATURE_CMOV) != 0;
+
+ case SLJIT_HAS_ROT:
+ case SLJIT_HAS_PREFETCH:
+ return 1;
+
+ case SLJIT_HAS_SSE2:
+#if (defined SLJIT_DETECT_SSE2 && SLJIT_DETECT_SSE2)
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+ return (cpu_feature_list & CPU_FEATURE_SSE2) != 0;
+#else /* !SLJIT_DETECT_SSE2 */
+ return 1;
+#endif /* SLJIT_DETECT_SSE2 */
+
+ default:
+ return 0;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_cmp_info(sljit_s32 type)
+{
+ if (type < SLJIT_UNORDERED || type > SLJIT_ORDERED_LESS_EQUAL)
+ return 0;
+
+ switch (type) {
+ case SLJIT_ORDERED_EQUAL:
+ case SLJIT_UNORDERED_OR_NOT_EQUAL:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* --------------------------------------------------------------------- */
+/* Operators */
+/* --------------------------------------------------------------------- */
+
+#define BINARY_OPCODE(opcode) (((opcode ## _EAX_i32) << 24) | ((opcode ## _r_rm) << 16) | ((opcode ## _rm_r) << 8) | (opcode))
+
+#define BINARY_IMM32(op_imm, immw, arg, argw) \
+ do { \
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, immw, arg, argw); \
+ FAIL_IF(!inst); \
+ *(inst + 1) |= (op_imm); \
+ } while (0)
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+
+#define BINARY_IMM(op_imm, op_mr, immw, arg, argw) \
+ do { \
+ if (IS_HALFWORD(immw) || compiler->mode32) { \
+ BINARY_IMM32(op_imm, immw, arg, argw); \
+ } \
+ else { \
+ FAIL_IF(emit_load_imm64(compiler, (arg == TMP_REG1) ? TMP_REG2 : TMP_REG1, immw)); \
+ inst = emit_x86_instruction(compiler, 1, (arg == TMP_REG1) ? TMP_REG2 : TMP_REG1, 0, arg, argw); \
+ FAIL_IF(!inst); \
+ *inst = (op_mr); \
+ } \
+ } while (0)
+
+#define BINARY_EAX_IMM(op_eax_imm, immw) \
+ FAIL_IF(emit_do_imm32(compiler, (!compiler->mode32) ? REX_W : 0, (op_eax_imm), immw))
+
+#else /* !SLJIT_CONFIG_X86_64 */
+
+#define BINARY_IMM(op_imm, op_mr, immw, arg, argw) \
+ BINARY_IMM32(op_imm, immw, arg, argw)
+
+#define BINARY_EAX_IMM(op_eax_imm, immw) \
+ FAIL_IF(emit_do_imm(compiler, (op_eax_imm), immw))
+
+#endif /* SLJIT_CONFIG_X86_64 */
+
+static sljit_s32 emit_mov(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw);
+
+#define EMIT_MOV(compiler, dst, dstw, src, srcw) \
+ FAIL_IF(emit_mov(compiler, dst, dstw, src, srcw));
+
+static SLJIT_INLINE sljit_s32 emit_sse2_store(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_sw dstw, sljit_s32 src);
+
+static SLJIT_INLINE sljit_s32 emit_sse2_load(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_s32 src, sljit_sw srcw);
+
+static sljit_s32 emit_cmp_binary(struct sljit_compiler *compiler,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w);
+
+static SLJIT_INLINE sljit_s32 emit_endbranch(struct sljit_compiler *compiler)
+{
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET)
+ /* Emit endbr32/endbr64 when CET is enabled. */
+ sljit_u8 *inst;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ *inst++ = 0xf3;
+ *inst++ = 0x0f;
+ *inst++ = 0x1e;
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ *inst = 0xfb;
+#else
+ *inst = 0xfa;
+#endif
+#else /* !SLJIT_CONFIG_X86_CET */
+ SLJIT_UNUSED_ARG(compiler);
+#endif /* SLJIT_CONFIG_X86_CET */
+ return SLJIT_SUCCESS;
+}
+
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined (__SHSTK__)
+
+static SLJIT_INLINE sljit_s32 emit_rdssp(struct sljit_compiler *compiler, sljit_s32 reg)
+{
+ sljit_u8 *inst;
+ sljit_s32 size;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ size = 5;
+#else
+ size = 4;
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ *inst++ = 0xf3;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ *inst++ = REX_W | (reg_map[reg] <= 7 ? 0 : REX_B);
+#endif
+ *inst++ = 0x0f;
+ *inst++ = 0x1e;
+ *inst = (0x3 << 6) | (0x1 << 3) | (reg_map[reg] & 0x7);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_incssp(struct sljit_compiler *compiler, sljit_s32 reg)
+{
+ sljit_u8 *inst;
+ sljit_s32 size;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ size = 5;
+#else
+ size = 4;
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ *inst++ = 0xf3;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ *inst++ = REX_W | (reg_map[reg] <= 7 ? 0 : REX_B);
+#endif
+ *inst++ = 0x0f;
+ *inst++ = 0xae;
+ *inst = (0x3 << 6) | (0x5 << 3) | (reg_map[reg] & 0x7);
+ return SLJIT_SUCCESS;
+}
+
+#endif /* SLJIT_CONFIG_X86_CET && __SHSTK__ */
+
+static SLJIT_INLINE sljit_s32 cpu_has_shadow_stack(void)
+{
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined (__SHSTK__)
+ return _get_ssp() != 0;
+#else /* !SLJIT_CONFIG_X86_CET || !__SHSTK__ */
+ return 0;
+#endif /* SLJIT_CONFIG_X86_CET && __SHSTK__ */
+}
+
+static SLJIT_INLINE sljit_s32 adjust_shadow_stack(struct sljit_compiler *compiler,
+ sljit_s32 src, sljit_sw srcw)
+{
+#if (defined SLJIT_CONFIG_X86_CET && SLJIT_CONFIG_X86_CET) && defined (__SHSTK__)
+ sljit_u8 *inst, *jz_after_cmp_inst;
+ sljit_uw size_jz_after_cmp_inst;
+
+ sljit_uw size_before_rdssp_inst = compiler->size;
+
+ /* Generate "RDSSP TMP_REG1". */
+ FAIL_IF(emit_rdssp(compiler, TMP_REG1));
+
+ /* Load return address on shadow stack into TMP_REG1. */
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ SLJIT_ASSERT(reg_map[TMP_REG1] == 5);
+
+ /* Hand code unsupported "mov 0x0(%ebp),%ebp". */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(3);
+ *inst++ = 0x8b;
+ *inst++ = 0x6d;
+ *inst = 0;
+#else /* !SLJIT_CONFIG_X86_32 */
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_MEM1(TMP_REG1), 0);
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ /* Compare return address against TMP_REG1. */
+ FAIL_IF(emit_cmp_binary (compiler, TMP_REG1, 0, src, srcw));
+
+ /* Generate JZ to skip shadow stack ajdustment when shadow
+ stack matches normal stack. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = get_jump_code(SLJIT_EQUAL) - 0x10;
+ size_jz_after_cmp_inst = compiler->size;
+ jz_after_cmp_inst = inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ /* REX_W is not necessary. */
+ compiler->mode32 = 1;
+#endif
+ /* Load 1 into TMP_REG1. */
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, 1);
+
+ /* Generate "INCSSP TMP_REG1". */
+ FAIL_IF(emit_incssp(compiler, TMP_REG1));
+
+ /* Jump back to "RDSSP TMP_REG1" to check shadow stack again. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = JMP_i8;
+ *inst = size_before_rdssp_inst - compiler->size;
+
+ *jz_after_cmp_inst = compiler->size - size_jz_after_cmp_inst;
+#else /* !SLJIT_CONFIG_X86_CET || !__SHSTK__ */
+ SLJIT_UNUSED_ARG(compiler);
+ SLJIT_UNUSED_ARG(src);
+ SLJIT_UNUSED_ARG(srcw);
+#endif /* SLJIT_CONFIG_X86_CET && __SHSTK__ */
+ return SLJIT_SUCCESS;
+}
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+#include "sljitNativeX86_32.c"
+#else
+#include "sljitNativeX86_64.c"
+#endif
+
+static sljit_s32 emit_mov(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ if (FAST_IS_REG(src)) {
+ inst = emit_x86_instruction(compiler, 1, src, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ return SLJIT_SUCCESS;
+ }
+ if (src & SLJIT_IMM) {
+ if (FAST_IS_REG(dst)) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ return emit_do_imm(compiler, MOV_r_i32 | reg_map[dst], srcw);
+#else
+ if (!compiler->mode32) {
+ if (NOT_HALFWORD(srcw))
+ return emit_load_imm64(compiler, dst, srcw);
+ }
+ else
+ return emit_do_imm32(compiler, (reg_map[dst] >= 8) ? REX_B : 0, U8(MOV_r_i32 | reg_lmap[dst]), srcw);
+#endif
+ }
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (!compiler->mode32 && NOT_HALFWORD(srcw)) {
+ /* Immediate to memory move. Only SLJIT_MOV operation copies
+ an immediate directly into memory so TMP_REG1 can be used. */
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, srcw));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ return SLJIT_SUCCESS;
+ }
+#endif
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+ }
+ if (FAST_IS_REG(dst)) {
+ inst = emit_x86_instruction(compiler, 1, dst, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst = MOV_r_rm;
+ return SLJIT_SUCCESS;
+ }
+
+ /* Memory to memory move. Only SLJIT_MOV operation copies
+ data from memory to memory so TMP_REG1 can be used. */
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst = MOV_r_rm;
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op0(struct sljit_compiler *compiler, sljit_s32 op)
+{
+ sljit_u8 *inst;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_uw size;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op0(compiler, op));
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_BREAKPOINT:
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = INT3;
+ break;
+ case SLJIT_NOP:
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = NOP;
+ break;
+ case SLJIT_LMUL_UW:
+ case SLJIT_LMUL_SW:
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_UW:
+ case SLJIT_DIV_SW:
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+#ifdef _WIN64
+ SLJIT_ASSERT(
+ reg_map[SLJIT_R0] == 0
+ && reg_map[SLJIT_R1] == 2
+ && reg_map[TMP_REG1] > 7);
+#else
+ SLJIT_ASSERT(
+ reg_map[SLJIT_R0] == 0
+ && reg_map[SLJIT_R1] < 7
+ && reg_map[TMP_REG1] == 2);
+#endif
+ compiler->mode32 = op & SLJIT_32;
+#endif
+ SLJIT_COMPILE_ASSERT((SLJIT_DIVMOD_UW & 0x2) == 0 && SLJIT_DIV_UW - 0x2 == SLJIT_DIVMOD_UW, bad_div_opcode_assignments);
+
+ op = GET_OPCODE(op);
+ if ((op | 0x2) == SLJIT_DIV_UW) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || defined(_WIN64)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_R1, 0);
+ inst = emit_x86_instruction(compiler, 1, SLJIT_R1, 0, SLJIT_R1, 0);
+#else
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, TMP_REG1, 0);
+#endif
+ FAIL_IF(!inst);
+ *inst = XOR_r_rm;
+ }
+
+ if ((op | 0x2) == SLJIT_DIV_SW) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || defined(_WIN64)
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_R1, 0);
+#endif
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = CDQ;
+#else
+ if (compiler->mode32) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = CDQ;
+ } else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = REX_W;
+ *inst = CDQ;
+ }
+#endif
+ }
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2);
+ *inst++ = GROUP_F7;
+ *inst = MOD_REG | ((op >= SLJIT_DIVMOD_UW) ? reg_map[TMP_REG1] : reg_map[SLJIT_R1]);
+#else
+#ifdef _WIN64
+ size = (!compiler->mode32 || op >= SLJIT_DIVMOD_UW) ? 3 : 2;
+#else
+ size = (!compiler->mode32) ? 3 : 2;
+#endif
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+#ifdef _WIN64
+ if (!compiler->mode32)
+ *inst++ = REX_W | ((op >= SLJIT_DIVMOD_UW) ? REX_B : 0);
+ else if (op >= SLJIT_DIVMOD_UW)
+ *inst++ = REX_B;
+ *inst++ = GROUP_F7;
+ *inst = MOD_REG | ((op >= SLJIT_DIVMOD_UW) ? reg_lmap[TMP_REG1] : reg_lmap[SLJIT_R1]);
+#else
+ if (!compiler->mode32)
+ *inst++ = REX_W;
+ *inst++ = GROUP_F7;
+ *inst = MOD_REG | reg_map[SLJIT_R1];
+#endif
+#endif
+ switch (op) {
+ case SLJIT_LMUL_UW:
+ *inst |= MUL;
+ break;
+ case SLJIT_LMUL_SW:
+ *inst |= IMUL;
+ break;
+ case SLJIT_DIVMOD_UW:
+ case SLJIT_DIV_UW:
+ *inst |= DIV;
+ break;
+ case SLJIT_DIVMOD_SW:
+ case SLJIT_DIV_SW:
+ *inst |= IDIV;
+ break;
+ }
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && !defined(_WIN64)
+ if (op <= SLJIT_DIVMOD_SW)
+ EMIT_MOV(compiler, SLJIT_R1, 0, TMP_REG1, 0);
+#else
+ if (op >= SLJIT_DIV_UW)
+ EMIT_MOV(compiler, SLJIT_R1, 0, TMP_REG1, 0);
+#endif
+ break;
+ case SLJIT_ENDBR:
+ return emit_endbranch(compiler);
+ case SLJIT_SKIP_FRAMES_BEFORE_RETURN:
+ return skip_frames_before_return(compiler);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+#define ENCODE_PREFIX(prefix) \
+ do { \
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1); \
+ FAIL_IF(!inst); \
+ INC_SIZE(1); \
+ *inst = U8(prefix); \
+ } while (0)
+
+static sljit_s32 emit_mov_byte(struct sljit_compiler *compiler, sljit_s32 sign,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r;
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_s32 work_r;
+#endif
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ if (src & SLJIT_IMM) {
+ if (FAST_IS_REG(dst)) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ return emit_do_imm(compiler, MOV_r_i32 | reg_map[dst], srcw);
+#else
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, srcw, dst, 0);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+#endif
+ }
+ inst = emit_x86_instruction(compiler, 1 | EX86_BYTE_ARG | EX86_NO_REXW, SLJIT_IMM, srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm8_i8;
+ return SLJIT_SUCCESS;
+ }
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if ((dst & SLJIT_MEM) && FAST_IS_REG(src)) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (reg_map[src] >= 4) {
+ SLJIT_ASSERT(dst_r == TMP_REG1);
+ EMIT_MOV(compiler, TMP_REG1, 0, src, 0);
+ } else
+ dst_r = src;
+#else
+ dst_r = src;
+#endif
+ }
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ else if (FAST_IS_REG(src) && reg_map[src] >= 4) {
+ /* src, dst are registers. */
+ SLJIT_ASSERT(FAST_IS_REG(dst));
+ if (reg_map[dst] < 4) {
+ if (dst != src)
+ EMIT_MOV(compiler, dst, 0, src, 0);
+ inst = emit_x86_instruction(compiler, 2, dst, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = sign ? MOVSX_r_rm8 : MOVZX_r_rm8;
+ }
+ else {
+ if (dst != src)
+ EMIT_MOV(compiler, dst, 0, src, 0);
+ if (sign) {
+ /* shl reg, 24 */
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 24, dst, 0);
+ FAIL_IF(!inst);
+ *inst |= SHL;
+ /* sar reg, 24 */
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 24, dst, 0);
+ FAIL_IF(!inst);
+ *inst |= SAR;
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 0xff, dst, 0);
+ FAIL_IF(!inst);
+ *(inst + 1) |= AND;
+ }
+ }
+ return SLJIT_SUCCESS;
+ }
+#endif
+ else {
+ /* src can be memory addr or reg_map[src] < 4 on x86_32 architectures. */
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = sign ? MOVSX_r_rm8 : MOVZX_r_rm8;
+ }
+
+ if (dst & SLJIT_MEM) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (dst_r == TMP_REG1) {
+ /* Find a non-used register, whose reg_map[src] < 4. */
+ if ((dst & REG_MASK) == SLJIT_R0) {
+ if ((dst & OFFS_REG_MASK) == TO_OFFS_REG(SLJIT_R1))
+ work_r = SLJIT_R2;
+ else
+ work_r = SLJIT_R1;
+ }
+ else {
+ if ((dst & OFFS_REG_MASK) != TO_OFFS_REG(SLJIT_R0))
+ work_r = SLJIT_R0;
+ else if ((dst & REG_MASK) == SLJIT_R1)
+ work_r = SLJIT_R2;
+ else
+ work_r = SLJIT_R1;
+ }
+
+ if (work_r == SLJIT_R0) {
+ ENCODE_PREFIX(XCHG_EAX_r | reg_map[TMP_REG1]);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, work_r, 0, dst_r, 0);
+ FAIL_IF(!inst);
+ *inst = XCHG_r_rm;
+ }
+
+ inst = emit_x86_instruction(compiler, 1, work_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm8_r8;
+
+ if (work_r == SLJIT_R0) {
+ ENCODE_PREFIX(XCHG_EAX_r | reg_map[TMP_REG1]);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, work_r, 0, dst_r, 0);
+ FAIL_IF(!inst);
+ *inst = XCHG_r_rm;
+ }
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm8_r8;
+ }
+#else
+ inst = emit_x86_instruction(compiler, 1 | EX86_REX | EX86_NO_REXW, dst_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm8_r8;
+#endif
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_prefetch(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+
+ inst = emit_x86_instruction(compiler, 2, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst++ = PREFETCH;
+
+ if (op == SLJIT_PREFETCH_L1)
+ *inst |= (1 << 3);
+ else if (op == SLJIT_PREFETCH_L2)
+ *inst |= (2 << 3);
+ else if (op == SLJIT_PREFETCH_L3)
+ *inst |= (3 << 3);
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_mov_half(struct sljit_compiler *compiler, sljit_s32 sign,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ if (src & SLJIT_IMM) {
+ if (FAST_IS_REG(dst)) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ return emit_do_imm(compiler, MOV_r_i32 | reg_map[dst], srcw);
+#else
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, srcw, dst, 0);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+#endif
+ }
+ inst = emit_x86_instruction(compiler, 1 | EX86_HALF_ARG | EX86_NO_REXW | EX86_PREF_66, SLJIT_IMM, srcw, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_i32;
+ return SLJIT_SUCCESS;
+ }
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if ((dst & SLJIT_MEM) && FAST_IS_REG(src))
+ dst_r = src;
+ else {
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = sign ? MOVSX_r_rm16 : MOVZX_r_rm16;
+ }
+
+ if (dst & SLJIT_MEM) {
+ inst = emit_x86_instruction(compiler, 1 | EX86_NO_REXW | EX86_PREF_66, dst_r, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = MOV_rm_r;
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_unary(struct sljit_compiler *compiler, sljit_u8 opcode,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ if (dst == src && dstw == srcw) {
+ /* Same input and output */
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= opcode;
+ return SLJIT_SUCCESS;
+ }
+
+ if (FAST_IS_REG(dst)) {
+ EMIT_MOV(compiler, dst, 0, src, srcw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= opcode;
+ return SLJIT_SUCCESS;
+ }
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= opcode;
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_not_with_flags(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ if (FAST_IS_REG(dst)) {
+ EMIT_MOV(compiler, dst, 0, src, srcw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= NOT_rm;
+ inst = emit_x86_instruction(compiler, 1, dst, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst = OR_r_rm;
+ return SLJIT_SUCCESS;
+ }
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ inst = emit_x86_instruction(compiler, 1, 0, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_F7;
+ *inst |= NOT_rm;
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst = OR_r_rm;
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+}
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+static const sljit_sw emit_clz_arg = 32 + 31;
+static const sljit_sw emit_ctz_arg = 32;
+#endif
+
+static sljit_s32 emit_clz_ctz(struct sljit_compiler *compiler, sljit_s32 is_clz,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r;
+ sljit_sw max;
+
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (is_clz ? (cpu_feature_list & CPU_FEATURE_LZCNT) : (cpu_feature_list & CPU_FEATURE_TZCNT)) {
+ /* Group prefix added separately. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst++ = GROUP_F3;
+
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = is_clz ? LZCNT_r_rm : TZCNT_r_rm;
+
+ if (dst & SLJIT_MEM)
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+ }
+
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = is_clz ? BSR_r_rm : BSF_r_rm;
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ max = is_clz ? (32 + 31) : 32;
+
+ if (cpu_feature_list & CPU_FEATURE_CMOV) {
+ if (dst_r != TMP_REG1) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, max);
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG1, 0);
+ }
+ else
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, SLJIT_MEM0(), is_clz ? (sljit_sw)&emit_clz_arg : (sljit_sw)&emit_ctz_arg);
+
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CMOVE_r_rm;
+ }
+ else
+ FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, max));
+
+ if (is_clz) {
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 31, dst_r, 0);
+ FAIL_IF(!inst);
+ *(inst + 1) |= XOR;
+ }
+#else
+ if (is_clz)
+ max = compiler->mode32 ? (32 + 31) : (64 + 63);
+ else
+ max = compiler->mode32 ? 32 : 64;
+
+ if (cpu_feature_list & CPU_FEATURE_CMOV) {
+ EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, max);
+
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CMOVE_r_rm;
+ }
+ else
+ FAIL_IF(sljit_emit_cmov_generic(compiler, SLJIT_EQUAL, dst_r, SLJIT_IMM, max));
+
+ if (is_clz) {
+ inst = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, max >> 1, dst_r, 0);
+ FAIL_IF(!inst);
+ *(inst + 1) |= XOR;
+ }
+#endif
+
+ if (dst & SLJIT_MEM)
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 op_flags = GET_ALL_FLAGS(op);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_s32 dst_is_ereg = 0;
+#endif
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ CHECK_EXTRA_REGS(dst, dstw, dst_is_ereg = 1);
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op_flags & SLJIT_32;
+#endif
+
+ op = GET_OPCODE(op);
+
+ if (op >= SLJIT_MOV && op <= SLJIT_MOV_P) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ if (FAST_IS_REG(src) && src == dst) {
+ if (!TYPE_CAST_NEEDED(op))
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (op_flags & SLJIT_32) {
+ if (src & SLJIT_MEM) {
+ if (op == SLJIT_MOV_S32)
+ op = SLJIT_MOV_U32;
+ }
+ else if (src & SLJIT_IMM) {
+ if (op == SLJIT_MOV_U32)
+ op = SLJIT_MOV_S32;
+ }
+ }
+#endif
+
+ if (src & SLJIT_IMM) {
+ switch (op) {
+ case SLJIT_MOV_U8:
+ srcw = (sljit_u8)srcw;
+ break;
+ case SLJIT_MOV_S8:
+ srcw = (sljit_s8)srcw;
+ break;
+ case SLJIT_MOV_U16:
+ srcw = (sljit_u16)srcw;
+ break;
+ case SLJIT_MOV_S16:
+ srcw = (sljit_s16)srcw;
+ break;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ case SLJIT_MOV_U32:
+ srcw = (sljit_u32)srcw;
+ break;
+ case SLJIT_MOV_S32:
+ srcw = (sljit_s32)srcw;
+ break;
+#endif
+ }
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (SLJIT_UNLIKELY(dst_is_ereg))
+ return emit_mov(compiler, dst, dstw, src, srcw);
+#endif
+ }
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (SLJIT_UNLIKELY(dst_is_ereg) && (!(op == SLJIT_MOV || op == SLJIT_MOV_U32 || op == SLJIT_MOV_S32 || op == SLJIT_MOV_P) || (src & SLJIT_MEM))) {
+ SLJIT_ASSERT(dst == SLJIT_MEM1(SLJIT_SP));
+ dst = TMP_REG1;
+ }
+#endif
+
+ switch (op) {
+ case SLJIT_MOV:
+ case SLJIT_MOV_P:
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ case SLJIT_MOV_U32:
+ case SLJIT_MOV_S32:
+ case SLJIT_MOV32:
+#endif
+ EMIT_MOV(compiler, dst, dstw, src, srcw);
+ break;
+ case SLJIT_MOV_U8:
+ FAIL_IF(emit_mov_byte(compiler, 0, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV_S8:
+ FAIL_IF(emit_mov_byte(compiler, 1, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV_U16:
+ FAIL_IF(emit_mov_half(compiler, 0, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV_S16:
+ FAIL_IF(emit_mov_half(compiler, 1, dst, dstw, src, srcw));
+ break;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ case SLJIT_MOV_U32:
+ FAIL_IF(emit_mov_int(compiler, 0, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV_S32:
+ FAIL_IF(emit_mov_int(compiler, 1, dst, dstw, src, srcw));
+ break;
+ case SLJIT_MOV32:
+ compiler->mode32 = 1;
+ EMIT_MOV(compiler, dst, dstw, src, srcw);
+ compiler->mode32 = 0;
+ break;
+#endif
+ }
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (SLJIT_UNLIKELY(dst_is_ereg) && dst == TMP_REG1)
+ return emit_mov(compiler, SLJIT_MEM1(SLJIT_SP), dstw, TMP_REG1, 0);
+#endif
+ return SLJIT_SUCCESS;
+ }
+
+ switch (op) {
+ case SLJIT_NOT:
+ if (SLJIT_UNLIKELY(op_flags & SLJIT_SET_Z))
+ return emit_not_with_flags(compiler, dst, dstw, src, srcw);
+ return emit_unary(compiler, NOT_rm, dst, dstw, src, srcw);
+
+ case SLJIT_CLZ:
+ case SLJIT_CTZ:
+ return emit_clz_ctz(compiler, (op == SLJIT_CLZ), dst, dstw, src, srcw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_cum_binary(struct sljit_compiler *compiler,
+ sljit_u32 op_types,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+ sljit_u8 op_eax_imm = U8(op_types >> 24);
+ sljit_u8 op_rm = U8((op_types >> 16) & 0xff);
+ sljit_u8 op_mr = U8((op_types >> 8) & 0xff);
+ sljit_u8 op_imm = U8(op_types & 0xff);
+
+ if (dst == src1 && dstw == src1w) {
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((dst == SLJIT_R0) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+#else
+ if ((dst == SLJIT_R0) && (src2w > 127 || src2w < -128)) {
+#endif
+ BINARY_EAX_IMM(op_eax_imm, src2w);
+ }
+ else {
+ BINARY_IMM(op_imm, op_mr, src2w, dst, dstw);
+ }
+ }
+ else if (FAST_IS_REG(dst)) {
+ inst = emit_x86_instruction(compiler, 1, dst, dstw, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ else if (FAST_IS_REG(src2)) {
+ /* Special exception for sljit_emit_op_flags. */
+ inst = emit_x86_instruction(compiler, 1, src2, src2w, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ /* Only for cumulative operations. */
+ if (dst == src2 && dstw == src2w) {
+ if (src1 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((dst == SLJIT_R0) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) {
+#else
+ if ((dst == SLJIT_R0) && (src1w > 127 || src1w < -128)) {
+#endif
+ BINARY_EAX_IMM(op_eax_imm, src1w);
+ }
+ else {
+ BINARY_IMM(op_imm, op_mr, src1w, dst, dstw);
+ }
+ }
+ else if (FAST_IS_REG(dst)) {
+ inst = emit_x86_instruction(compiler, 1, dst, dstw, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ else if (FAST_IS_REG(src1)) {
+ inst = emit_x86_instruction(compiler, 1, src1, src1w, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ /* General version. */
+ if (FAST_IS_REG(dst)) {
+ EMIT_MOV(compiler, dst, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(op_imm, op_mr, src2w, dst, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ }
+ else {
+ /* This version requires less memory writing. */
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(op_imm, op_mr, src2w, TMP_REG1, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_non_cum_binary(struct sljit_compiler *compiler,
+ sljit_u32 op_types,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+ sljit_u8 op_eax_imm = U8(op_types >> 24);
+ sljit_u8 op_rm = U8((op_types >> 16) & 0xff);
+ sljit_u8 op_mr = U8((op_types >> 8) & 0xff);
+ sljit_u8 op_imm = U8(op_types & 0xff);
+
+ if (dst == src1 && dstw == src1w) {
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((dst == SLJIT_R0) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+#else
+ if ((dst == SLJIT_R0) && (src2w > 127 || src2w < -128)) {
+#endif
+ BINARY_EAX_IMM(op_eax_imm, src2w);
+ }
+ else {
+ BINARY_IMM(op_imm, op_mr, src2w, dst, dstw);
+ }
+ }
+ else if (FAST_IS_REG(dst)) {
+ inst = emit_x86_instruction(compiler, 1, dst, dstw, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ else if (FAST_IS_REG(src2)) {
+ inst = emit_x86_instruction(compiler, 1, src2, src2w, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, dst, dstw);
+ FAIL_IF(!inst);
+ *inst = op_mr;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ /* General version. */
+ if (FAST_IS_REG(dst) && dst != src2) {
+ EMIT_MOV(compiler, dst, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(op_imm, op_mr, src2w, dst, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ }
+ else {
+ /* This version requires less memory writing. */
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(op_imm, op_mr, src2w, TMP_REG1, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = op_rm;
+ }
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_mul(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ /* Register destination. */
+ if (dst_r == src1 && !(src2 & SLJIT_IMM)) {
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+ else if (dst_r == src2 && !(src1 & SLJIT_IMM)) {
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+ else if (src1 & SLJIT_IMM) {
+ if (src2 & SLJIT_IMM) {
+ EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, src2w);
+ src2 = dst_r;
+ src2w = 0;
+ }
+
+ if (src1w <= 127 && src1w >= -128) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i8;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = U8(src1w);
+ }
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i32;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ sljit_unaligned_store_sw(inst, src1w);
+ }
+#else
+ else if (IS_HALFWORD(src1w)) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i32;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ sljit_unaligned_store_s32(inst, (sljit_s32)src1w);
+ }
+ else {
+ if (dst_r != src2)
+ EMIT_MOV(compiler, dst_r, 0, src2, src2w);
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src1w));
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+#endif
+ }
+ else if (src2 & SLJIT_IMM) {
+ /* Note: src1 is NOT immediate. */
+
+ if (src2w <= 127 && src2w >= -128) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i8;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = U8(src2w);
+ }
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ else {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i32;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ sljit_unaligned_store_sw(inst, src2w);
+ }
+#else
+ else if (IS_HALFWORD(src2w)) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = IMUL_r_rm_i32;
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4);
+ sljit_unaligned_store_s32(inst, (sljit_s32)src2w);
+ }
+ else {
+ if (dst_r != src1)
+ EMIT_MOV(compiler, dst_r, 0, src1, src1w);
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w));
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+#endif
+ }
+ else {
+ /* Neither argument is immediate. */
+ if (ADDRESSING_DEPENDS_ON(src2, dst_r))
+ dst_r = TMP_REG1;
+ EMIT_MOV(compiler, dst_r, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 2, dst_r, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = IMUL_r_rm;
+ }
+
+ if (dst & SLJIT_MEM)
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_lea_binary(struct sljit_compiler *compiler,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+ sljit_s32 dst_r, done = 0;
+
+ /* These cases better be left to handled by normal way. */
+ if (dst == src1 && dstw == src1w)
+ return SLJIT_ERR_UNSUPPORTED;
+ if (dst == src2 && dstw == src2w)
+ return SLJIT_ERR_UNSUPPORTED;
+
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (FAST_IS_REG(src1)) {
+ if (FAST_IS_REG(src2)) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM2(src1, src2), 0);
+ FAIL_IF(!inst);
+ *inst = LEA_r_m;
+ done = 1;
+ }
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((src2 & SLJIT_IMM) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src1), (sljit_s32)src2w);
+#else
+ if (src2 & SLJIT_IMM) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src1), src2w);
+#endif
+ FAIL_IF(!inst);
+ *inst = LEA_r_m;
+ done = 1;
+ }
+ }
+ else if (FAST_IS_REG(src2)) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if ((src1 & SLJIT_IMM) && (compiler->mode32 || IS_HALFWORD(src1w))) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src2), (sljit_s32)src1w);
+#else
+ if (src1 & SLJIT_IMM) {
+ inst = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src2), src1w);
+#endif
+ FAIL_IF(!inst);
+ *inst = LEA_r_m;
+ done = 1;
+ }
+ }
+
+ if (done) {
+ if (dst_r == TMP_REG1)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+ }
+ return SLJIT_ERR_UNSUPPORTED;
+}
+
+static sljit_s32 emit_cmp_binary(struct sljit_compiler *compiler,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (src1 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+#else
+ if (src1 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128)) {
+#endif
+ BINARY_EAX_IMM(CMP_EAX_i32, src2w);
+ return SLJIT_SUCCESS;
+ }
+
+ if (FAST_IS_REG(src1)) {
+ if (src2 & SLJIT_IMM) {
+ BINARY_IMM(CMP, CMP_rm_r, src2w, src1, 0);
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, src1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = CMP_r_rm;
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ if (FAST_IS_REG(src2) && !(src1 & SLJIT_IMM)) {
+ inst = emit_x86_instruction(compiler, 1, src2, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = CMP_rm_r;
+ return SLJIT_SUCCESS;
+ }
+
+ if (src2 & SLJIT_IMM) {
+ if (src1 & SLJIT_IMM) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ src1 = TMP_REG1;
+ src1w = 0;
+ }
+ BINARY_IMM(CMP, CMP_rm_r, src2w, src1, src1w);
+ }
+ else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = CMP_r_rm;
+ }
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_test_binary(struct sljit_compiler *compiler,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_u8* inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (src1 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) {
+#else
+ if (src1 == SLJIT_R0 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128)) {
+#endif
+ BINARY_EAX_IMM(TEST_EAX_i32, src2w);
+ return SLJIT_SUCCESS;
+ }
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (src2 == SLJIT_R0 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) {
+#else
+ if (src2 == SLJIT_R0 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128)) {
+#endif
+ BINARY_EAX_IMM(TEST_EAX_i32, src1w);
+ return SLJIT_SUCCESS;
+ }
+
+ if (!(src1 & SLJIT_IMM)) {
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (IS_HALFWORD(src2w) || compiler->mode32) {
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+ }
+ else {
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, src2w));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ }
+#else
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+#endif
+ return SLJIT_SUCCESS;
+ }
+ else if (FAST_IS_REG(src1)) {
+ inst = emit_x86_instruction(compiler, 1, src1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ return SLJIT_SUCCESS;
+ }
+ }
+
+ if (!(src2 & SLJIT_IMM)) {
+ if (src1 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (IS_HALFWORD(src1w) || compiler->mode32) {
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src1w, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+ }
+ else {
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, src1w));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ }
+#else
+ inst = emit_x86_instruction(compiler, 1, src1, src1w, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+#endif
+ return SLJIT_SUCCESS;
+ }
+ else if (FAST_IS_REG(src2)) {
+ inst = emit_x86_instruction(compiler, 1, src2, 0, src1, src1w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ return SLJIT_SUCCESS;
+ }
+ }
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (IS_HALFWORD(src2w) || compiler->mode32) {
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+ }
+ else {
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w));
+ inst = emit_x86_instruction(compiler, 1, TMP_REG2, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ }
+#else
+ inst = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst = GROUP_F7;
+#endif
+ }
+ else {
+ inst = emit_x86_instruction(compiler, 1, TMP_REG1, 0, src2, src2w);
+ FAIL_IF(!inst);
+ *inst = TEST_rm_r;
+ }
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_shift(struct sljit_compiler *compiler,
+ sljit_u8 mode,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 mode32;
+#endif
+ sljit_u8* inst;
+
+ if ((src2 & SLJIT_IMM) || (src2 == SLJIT_PREF_SHIFT_REG)) {
+ if (dst == src1 && dstw == src1w) {
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, dst, dstw);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ return SLJIT_SUCCESS;
+ }
+ if (dst == SLJIT_PREF_SHIFT_REG && src2 == SLJIT_PREF_SHIFT_REG) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+ }
+ if (FAST_IS_REG(dst)) {
+ EMIT_MOV(compiler, dst, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, dst, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ return SLJIT_SUCCESS;
+ }
+
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ EMIT_MOV(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+ }
+
+ if (dst == SLJIT_PREF_SHIFT_REG) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+ return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ }
+
+ if (FAST_IS_REG(dst) && dst != src2 && dst != TMP_REG1 && !ADDRESSING_DEPENDS_ON(src2, dst)) {
+ if (src1 != dst)
+ EMIT_MOV(compiler, dst, 0, src1, src1w);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ mode32 = compiler->mode32;
+ compiler->mode32 = 0;
+#endif
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_PREF_SHIFT_REG, 0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = mode32;
+#endif
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, dst, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = mode32;
+#endif
+ return SLJIT_SUCCESS;
+ }
+
+ /* This case is complex since ecx itself may be used for
+ addressing, and this case must be supported as well. */
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_SP), 0, SLJIT_PREF_SHIFT_REG, 0);
+#else /* !SLJIT_CONFIG_X86_32 */
+ mode32 = compiler->mode32;
+ compiler->mode32 = 0;
+ EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0);
+ compiler->mode32 = mode32;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+ inst = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ FAIL_IF(!inst);
+ *inst |= mode;
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_SP), 0);
+#else
+ compiler->mode32 = 0;
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG2, 0);
+ compiler->mode32 = mode32;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ if (dst != TMP_REG1)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_shift_with_flags(struct sljit_compiler *compiler,
+ sljit_u8 mode, sljit_s32 set_flags,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ /* The CPU does not set flags if the shift count is 0. */
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ src2w &= compiler->mode32 ? 0x1f : 0x3f;
+#else /* !SLJIT_CONFIG_X86_64 */
+ src2w &= 0x1f;
+#endif /* SLJIT_CONFIG_X86_64 */
+ if (src2w != 0)
+ return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w);
+
+ if (!set_flags)
+ return emit_mov(compiler, dst, dstw, src1, src1w);
+ /* OR dst, src, 0 */
+ return emit_cum_binary(compiler, BINARY_OPCODE(OR),
+ dst, dstw, src1, src1w, SLJIT_IMM, 0);
+ }
+
+ if (!set_flags)
+ return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w);
+
+ if (!FAST_IS_REG(dst))
+ FAIL_IF(emit_cmp_binary(compiler, src1, src1w, SLJIT_IMM, 0));
+
+ FAIL_IF(emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w));
+
+ if (FAST_IS_REG(dst))
+ return emit_cmp_binary(compiler, dst, dstw, SLJIT_IMM, 0);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+ CHECK_EXTRA_REGS(src1, src1w, (void)0);
+ CHECK_EXTRA_REGS(src2, src2w, (void)0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+
+ SLJIT_ASSERT(dst != TMP_REG1 || HAS_FLAGS(op));
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD:
+ if (!HAS_FLAGS(op)) {
+ if (emit_lea_binary(compiler, dst, dstw, src1, src1w, src2, src2w) != SLJIT_ERR_UNSUPPORTED)
+ return compiler->error;
+ }
+ return emit_cum_binary(compiler, BINARY_OPCODE(ADD),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ADDC:
+ return emit_cum_binary(compiler, BINARY_OPCODE(ADC),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_SUB:
+ if (src1 == SLJIT_IMM && src1w == 0)
+ return emit_unary(compiler, NEG_rm, dst, dstw, src2, src2w);
+
+ if (!HAS_FLAGS(op)) {
+ if ((src2 & SLJIT_IMM) && emit_lea_binary(compiler, dst, dstw, src1, src1w, SLJIT_IMM, -src2w) != SLJIT_ERR_UNSUPPORTED)
+ return compiler->error;
+ if (FAST_IS_REG(dst) && src2 == dst) {
+ FAIL_IF(emit_non_cum_binary(compiler, BINARY_OPCODE(SUB), dst, 0, dst, 0, src1, src1w));
+ return emit_unary(compiler, NEG_rm, dst, 0, dst, 0);
+ }
+ }
+
+ return emit_non_cum_binary(compiler, BINARY_OPCODE(SUB),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_SUBC:
+ return emit_non_cum_binary(compiler, BINARY_OPCODE(SBB),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_MUL:
+ return emit_mul(compiler, dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_AND:
+ return emit_cum_binary(compiler, BINARY_OPCODE(AND),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_OR:
+ return emit_cum_binary(compiler, BINARY_OPCODE(OR),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_XOR:
+ return emit_cum_binary(compiler, BINARY_OPCODE(XOR),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_SHL:
+ case SLJIT_MSHL:
+ return emit_shift_with_flags(compiler, SHL, HAS_FLAGS(op),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_LSHR:
+ case SLJIT_MLSHR:
+ return emit_shift_with_flags(compiler, SHR, HAS_FLAGS(op),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ASHR:
+ case SLJIT_MASHR:
+ return emit_shift_with_flags(compiler, SAR, HAS_FLAGS(op),
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ROTL:
+ return emit_shift_with_flags(compiler, ROL, 0,
+ dst, dstw, src1, src1w, src2, src2w);
+ case SLJIT_ROTR:
+ return emit_shift_with_flags(compiler, ROR, 0,
+ dst, dstw, src1, src1w, src2, src2w);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op2u(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 opcode = GET_OPCODE(op);
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op2(compiler, op, 1, 0, 0, src1, src1w, src2, src2w));
+
+ if (opcode != SLJIT_SUB && opcode != SLJIT_AND) {
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, TMP_REG1, 0, src1, src1w, src2, src2w);
+ }
+
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ CHECK_EXTRA_REGS(src1, src1w, (void)0);
+ CHECK_EXTRA_REGS(src2, src2w, (void)0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+
+ if (opcode == SLJIT_SUB) {
+ return emit_cmp_binary(compiler, src1, src1w, src2, src2w);
+ }
+ return emit_test_binary(compiler, src1, src1w, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_shift_into(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src_dst,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 restore_ecx = 0;
+ sljit_s32 is_rotate, is_left;
+ sljit_u8* inst;
+ sljit_sw dstw = 0;
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_s32 tmp2 = SLJIT_MEM1(SLJIT_SP);
+#else /* !SLJIT_CONFIG_X86_32 */
+ sljit_s32 tmp2 = TMP_REG2;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_shift_into(compiler, op, src_dst, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+ CHECK_EXTRA_REGS(src1, src1w, (void)0);
+ CHECK_EXTRA_REGS(src2, src2w, (void)0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+
+ if (src2 & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ src2w &= 0x1f;
+#else /* !SLJIT_CONFIG_X86_32 */
+ src2w &= (op & SLJIT_32) ? 0x1f : 0x3f;
+#endif /* SLJIT_CONFIG_X86_32 */
+
+ if (src2w == 0)
+ return SLJIT_SUCCESS;
+ }
+
+ is_left = (GET_OPCODE(op) == SLJIT_SHL || GET_OPCODE(op) == SLJIT_MSHL);
+
+ is_rotate = (src_dst == src1);
+ CHECK_EXTRA_REGS(src_dst, dstw, (void)0);
+
+ if (is_rotate)
+ return emit_shift(compiler, is_left ? ROL : ROR, src_dst, dstw, src1, src1w, src2, src2w);
+
+ if ((src2 & SLJIT_IMM) || src2 == SLJIT_PREF_SHIFT_REG) {
+ if (!FAST_IS_REG(src1)) {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+ src1 = TMP_REG1;
+ }
+ } else if (FAST_IS_REG(src1)) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_PREF_SHIFT_REG, 0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+
+ if (src1 == SLJIT_PREF_SHIFT_REG)
+ src1 = TMP_REG1;
+
+ if (src_dst == SLJIT_PREF_SHIFT_REG)
+ src_dst = TMP_REG1;
+
+ restore_ecx = 1;
+ } else {
+ EMIT_MOV(compiler, TMP_REG1, 0, src1, src1w);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+ EMIT_MOV(compiler, tmp2, 0, SLJIT_PREF_SHIFT_REG, 0);
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = op & SLJIT_32;
+#endif
+ EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w);
+
+ src1 = TMP_REG1;
+
+ if (src_dst == SLJIT_PREF_SHIFT_REG) {
+ src_dst = tmp2;
+ SLJIT_ASSERT(dstw == 0);
+ }
+
+ restore_ecx = 2;
+ }
+
+ inst = emit_x86_instruction(compiler, 2, src1, 0, src_dst, dstw);
+ FAIL_IF(!inst);
+ inst[0] = GROUP_0F;
+
+ if (src2 & SLJIT_IMM) {
+ inst[1] = U8((is_left ? SHLD : SHRD) - 1);
+
+ /* Immedate argument is added separately. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1);
+ *inst = U8(src2w);
+ } else
+ inst[1] = U8(is_left ? SHLD : SHRD);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ if (restore_ecx == 1)
+ return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG1, 0);
+ if (restore_ecx == 2)
+ return emit_mov(compiler, SLJIT_PREF_SHIFT_REG, 0, tmp2, 0);
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_src(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src, sljit_sw srcw)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_src(compiler, op, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ switch (op) {
+ case SLJIT_FAST_RETURN:
+ return emit_fast_return(compiler, src, srcw);
+ case SLJIT_SKIP_FRAMES_BEFORE_FAST_RETURN:
+ /* Don't adjust shadow stack if it isn't enabled. */
+ if (!cpu_has_shadow_stack ())
+ return SLJIT_SUCCESS;
+ return adjust_shadow_stack(compiler, src, srcw);
+ case SLJIT_PREFETCH_L1:
+ case SLJIT_PREFETCH_L2:
+ case SLJIT_PREFETCH_L3:
+ case SLJIT_PREFETCH_ONCE:
+ return emit_prefetch(compiler, op, src, srcw);
+ }
+
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_register_index(reg));
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ if (reg >= SLJIT_R3 && reg <= SLJIT_R8)
+ return -1;
+#endif
+ return reg_map[reg];
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_float_register_index(sljit_s32 reg)
+{
+ CHECK_REG_INDEX(check_sljit_get_float_register_index(reg));
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ return reg;
+#else
+ return freg_map[reg];
+#endif
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_custom(struct sljit_compiler *compiler,
+ void *instruction, sljit_u32 size)
+{
+ sljit_u8 *inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_custom(compiler, instruction, size));
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + size);
+ FAIL_IF(!inst);
+ INC_SIZE(size);
+ SLJIT_MEMCPY(inst, instruction, size);
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Floating point operators */
+/* --------------------------------------------------------------------- */
+
+/* Alignment(3) + 4 * 16 bytes. */
+static sljit_u32 sse2_data[3 + (4 * 4)];
+static sljit_u32 *sse2_buffer;
+
+static void init_compiler(void)
+{
+ /* Align to 16 bytes. */
+ sse2_buffer = (sljit_u32*)(((sljit_uw)sse2_data + 15) & ~(sljit_uw)0xf);
+
+ /* Single precision constants (each constant is 16 byte long). */
+ sse2_buffer[0] = 0x80000000;
+ sse2_buffer[4] = 0x7fffffff;
+ /* Double precision constants (each constant is 16 byte long). */
+ sse2_buffer[8] = 0;
+ sse2_buffer[9] = 0x80000000;
+ sse2_buffer[12] = 0xffffffff;
+ sse2_buffer[13] = 0x7fffffff;
+}
+
+static sljit_s32 emit_sse2(struct sljit_compiler *compiler, sljit_u8 opcode,
+ sljit_s32 single, sljit_s32 xmm1, sljit_s32 xmm2, sljit_sw xmm2w)
+{
+ sljit_u8 *inst;
+
+ inst = emit_x86_instruction(compiler, 2 | (single ? EX86_PREF_F3 : EX86_PREF_F2) | EX86_SSE2, xmm1, 0, xmm2, xmm2w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = opcode;
+ return SLJIT_SUCCESS;
+}
+
+static sljit_s32 emit_sse2_logic(struct sljit_compiler *compiler, sljit_u8 opcode,
+ sljit_s32 pref66, sljit_s32 xmm1, sljit_s32 xmm2, sljit_sw xmm2w)
+{
+ sljit_u8 *inst;
+
+ inst = emit_x86_instruction(compiler, 2 | (pref66 ? EX86_PREF_66 : 0) | EX86_SSE2, xmm1, 0, xmm2, xmm2w);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = opcode;
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 emit_sse2_load(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_s32 src, sljit_sw srcw)
+{
+ return emit_sse2(compiler, MOVSD_x_xm, single, dst, src, srcw);
+}
+
+static SLJIT_INLINE sljit_s32 emit_sse2_store(struct sljit_compiler *compiler,
+ sljit_s32 single, sljit_s32 dst, sljit_sw dstw, sljit_s32 src)
+{
+ return emit_sse2(compiler, MOVSD_xm_x, single, src, dst, dstw);
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_sw_from_f64(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_REG1;
+ sljit_u8 *inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_SW_FROM_F64)
+ compiler->mode32 = 0;
+#endif
+
+ inst = emit_x86_instruction(compiler, 2 | ((op & SLJIT_32) ? EX86_PREF_F3 : EX86_PREF_F2) | EX86_SSE2_OP2, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CVTTSD2SI_r_xm;
+
+ if (dst & SLJIT_MEM)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_conv_f64_from_sw(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG;
+ sljit_u8 *inst;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_SW)
+ compiler->mode32 = 0;
+#endif
+
+ if (src & SLJIT_IMM) {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_S32)
+ srcw = (sljit_s32)srcw;
+#endif
+ EMIT_MOV(compiler, TMP_REG1, 0, src, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ inst = emit_x86_instruction(compiler, 2 | ((op & SLJIT_32) ? EX86_PREF_F3 : EX86_PREF_F2) | EX86_SSE2_OP1, dst_r, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = CVTSI2SD_x_rm;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+ if (dst_r == TMP_FREG)
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ return SLJIT_SUCCESS;
+}
+
+static SLJIT_INLINE sljit_s32 sljit_emit_fop1_cmp(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ switch (GET_FLAG_TYPE(op)) {
+ case SLJIT_ORDERED_LESS:
+ case SLJIT_UNORDERED_OR_GREATER_EQUAL:
+ case SLJIT_UNORDERED_OR_GREATER:
+ case SLJIT_ORDERED_LESS_EQUAL:
+ if (!FAST_IS_REG(src2)) {
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src2, src2w));
+ src2 = TMP_FREG;
+ }
+
+ return emit_sse2_logic(compiler, UCOMISD_x_xm, !(op & SLJIT_32), src2, src1, src1w);
+ }
+
+ if (!FAST_IS_REG(src1)) {
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src1, src1w));
+ src1 = TMP_FREG;
+ }
+
+ return emit_sse2_logic(compiler, UCOMISD_x_xm, !(op & SLJIT_32), src1, src2, src2w);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop1(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_s32 dst_r;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+
+ CHECK_ERROR();
+ SELECT_FOP1_OPERATION_WITH_CHECKS(compiler, op, dst, dstw, src, srcw);
+
+ if (GET_OPCODE(op) == SLJIT_MOV_F64) {
+ if (FAST_IS_REG(dst))
+ return emit_sse2_load(compiler, op & SLJIT_32, dst, src, srcw);
+ if (FAST_IS_REG(src))
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, src);
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src, srcw));
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ }
+
+ if (GET_OPCODE(op) == SLJIT_CONV_F64_FROM_F32) {
+ dst_r = FAST_IS_REG(dst) ? dst : TMP_FREG;
+ if (FAST_IS_REG(src)) {
+ /* We overwrite the high bits of source. From SLJIT point of view,
+ this is not an issue.
+ Note: In SSE3, we could also use MOVDDUP and MOVSLDUP. */
+ FAIL_IF(emit_sse2_logic(compiler, UNPCKLPD_x_xm, op & SLJIT_32, src, src, 0));
+ }
+ else {
+ FAIL_IF(emit_sse2_load(compiler, !(op & SLJIT_32), TMP_FREG, src, srcw));
+ src = TMP_FREG;
+ }
+
+ FAIL_IF(emit_sse2_logic(compiler, CVTPD2PS_x_xm, op & SLJIT_32, dst_r, src, 0));
+ if (dst_r == TMP_FREG)
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ return SLJIT_SUCCESS;
+ }
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ if (dst != src)
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, dst_r, src, srcw));
+ }
+ else {
+ dst_r = TMP_FREG;
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, dst_r, src, srcw));
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_NEG_F64:
+ FAIL_IF(emit_sse2_logic(compiler, XORPD_x_xm, 1, dst_r, SLJIT_MEM0(), (sljit_sw)(op & SLJIT_32 ? sse2_buffer : sse2_buffer + 8)));
+ break;
+
+ case SLJIT_ABS_F64:
+ FAIL_IF(emit_sse2_logic(compiler, ANDPD_x_xm, 1, dst_r, SLJIT_MEM0(), (sljit_sw)(op & SLJIT_32 ? sse2_buffer + 4 : sse2_buffer + 12)));
+ break;
+ }
+
+ if (dst_r == TMP_FREG)
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_fop2(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 src1, sljit_sw src1w,
+ sljit_s32 src2, sljit_sw src2w)
+{
+ sljit_s32 dst_r;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ ADJUST_LOCAL_OFFSET(src1, src1w);
+ ADJUST_LOCAL_OFFSET(src2, src2w);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 1;
+#endif
+
+ if (FAST_IS_REG(dst)) {
+ dst_r = dst;
+ if (dst == src1)
+ ; /* Do nothing here. */
+ else if (dst == src2 && (op == SLJIT_ADD_F64 || op == SLJIT_MUL_F64)) {
+ /* Swap arguments. */
+ src2 = src1;
+ src2w = src1w;
+ }
+ else if (dst != src2)
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, dst_r, src1, src1w));
+ else {
+ dst_r = TMP_FREG;
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src1, src1w));
+ }
+ }
+ else {
+ dst_r = TMP_FREG;
+ FAIL_IF(emit_sse2_load(compiler, op & SLJIT_32, TMP_FREG, src1, src1w));
+ }
+
+ switch (GET_OPCODE(op)) {
+ case SLJIT_ADD_F64:
+ FAIL_IF(emit_sse2(compiler, ADDSD_x_xm, op & SLJIT_32, dst_r, src2, src2w));
+ break;
+
+ case SLJIT_SUB_F64:
+ FAIL_IF(emit_sse2(compiler, SUBSD_x_xm, op & SLJIT_32, dst_r, src2, src2w));
+ break;
+
+ case SLJIT_MUL_F64:
+ FAIL_IF(emit_sse2(compiler, MULSD_x_xm, op & SLJIT_32, dst_r, src2, src2w));
+ break;
+
+ case SLJIT_DIV_F64:
+ FAIL_IF(emit_sse2(compiler, DIVSD_x_xm, op & SLJIT_32, dst_r, src2, src2w));
+ break;
+ }
+
+ if (dst_r == TMP_FREG)
+ return emit_sse2_store(compiler, op & SLJIT_32, dst, dstw, TMP_FREG);
+ return SLJIT_SUCCESS;
+}
+
+/* --------------------------------------------------------------------- */
+/* Conditional instructions */
+/* --------------------------------------------------------------------- */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler)
+{
+ sljit_u8 *inst;
+ struct sljit_label *label;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_label(compiler));
+
+ if (compiler->last_label && compiler->last_label->size == compiler->size)
+ return compiler->last_label;
+
+ label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label));
+ PTR_FAIL_IF(!label);
+ set_label(label, compiler);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ PTR_FAIL_IF(!inst);
+
+ *inst++ = 0;
+ *inst++ = 0;
+
+ return label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, sljit_s32 type)
+{
+ sljit_u8 *inst;
+ struct sljit_jump *jump;
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_jump(compiler, type));
+
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ PTR_FAIL_IF_NULL(jump);
+ set_jump(jump, compiler, (sljit_u32)((type & SLJIT_REWRITABLE_JUMP) | ((type & 0xff) << TYPE_SHIFT)));
+ type &= 0xff;
+
+ /* Worst case size. */
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ compiler->size += (type >= SLJIT_JUMP) ? 5 : 6;
+#else
+ compiler->size += (type >= SLJIT_JUMP) ? (10 + 3) : (2 + 10 + 3);
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ PTR_FAIL_IF_NULL(inst);
+
+ *inst++ = 0;
+ *inst++ = 1;
+ return jump;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_ijump(struct sljit_compiler *compiler, sljit_s32 type, sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8 *inst;
+ struct sljit_jump *jump;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_ijump(compiler, type, src, srcw));
+ ADJUST_LOCAL_OFFSET(src, srcw);
+
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+ if (src == SLJIT_IMM) {
+ jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump));
+ FAIL_IF_NULL(jump);
+ set_jump(jump, compiler, (sljit_u32)(JUMP_ADDR | (type << TYPE_SHIFT)));
+ jump->u.target = (sljit_uw)srcw;
+
+ /* Worst case size. */
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ compiler->size += 5;
+#else
+ compiler->size += 10 + 3;
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ FAIL_IF_NULL(inst);
+
+ *inst++ = 0;
+ *inst++ = 1;
+ }
+ else {
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ /* REX_W is not necessary (src is not immediate). */
+ compiler->mode32 = 1;
+#endif
+ inst = emit_x86_instruction(compiler, 1, 0, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_FF;
+ *inst = U8(*inst | ((type >= SLJIT_FAST_CALL) ? CALL_rm : JMP_rm));
+ }
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_op_flags(struct sljit_compiler *compiler, sljit_s32 op,
+ sljit_s32 dst, sljit_sw dstw,
+ sljit_s32 type)
+{
+ sljit_u8 *inst;
+ sljit_u8 cond_set = 0;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 reg;
+#endif
+ /* ADJUST_LOCAL_OFFSET and CHECK_EXTRA_REGS might overwrite these values. */
+ sljit_s32 dst_save = dst;
+ sljit_sw dstw_save = dstw;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_op_flags(compiler, op, dst, dstw, type));
+
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+ /* setcc = jcc + 0x10. */
+ cond_set = U8(get_jump_code((sljit_uw)type) + 0x10);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst)) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(4 + 3);
+ /* Set low register to conditional flag. */
+ *inst++ = (reg_map[TMP_REG1] <= 7) ? REX : REX_B;
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | reg_lmap[TMP_REG1];
+ *inst++ = U8(REX | (reg_map[TMP_REG1] <= 7 ? 0 : REX_R) | (reg_map[dst] <= 7 ? 0 : REX_B));
+ *inst++ = OR_rm8_r8;
+ *inst++ = U8(MOD_REG | (reg_lmap[TMP_REG1] << 3) | reg_lmap[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ reg = (GET_OPCODE(op) < SLJIT_ADD && FAST_IS_REG(dst)) ? dst : TMP_REG1;
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 4 + 4);
+ FAIL_IF(!inst);
+ INC_SIZE(4 + 4);
+ /* Set low register to conditional flag. */
+ *inst++ = (reg_map[reg] <= 7) ? REX : REX_B;
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | reg_lmap[reg];
+ *inst++ = REX_W | (reg_map[reg] <= 7 ? 0 : (REX_B | REX_R));
+ /* The movzx instruction does not affect flags. */
+ *inst++ = GROUP_0F;
+ *inst++ = MOVZX_r_rm8;
+ *inst = U8(MOD_REG | (reg_lmap[reg] << 3) | reg_lmap[reg]);
+
+ if (reg != TMP_REG1)
+ return SLJIT_SUCCESS;
+
+ if (GET_OPCODE(op) < SLJIT_ADD) {
+ compiler->mode32 = GET_OPCODE(op) != SLJIT_MOV;
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+ }
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, dst_save, dstw_save, dst_save, dstw_save, TMP_REG1, 0);
+
+#else
+ /* The SLJIT_CONFIG_X86_32 code path starts here. */
+ if (GET_OPCODE(op) < SLJIT_ADD && FAST_IS_REG(dst)) {
+ if (reg_map[dst] <= 4) {
+ /* Low byte is accessible. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(3 + 3);
+ /* Set low byte to conditional flag. */
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = U8(MOD_REG | reg_map[dst]);
+
+ *inst++ = GROUP_0F;
+ *inst++ = MOVZX_r_rm8;
+ *inst = U8(MOD_REG | (reg_map[dst] << 3) | reg_map[dst]);
+ return SLJIT_SUCCESS;
+ }
+
+ /* Low byte is not accessible. */
+ if (cpu_feature_list == 0)
+ get_cpu_features();
+
+ if (cpu_feature_list & CPU_FEATURE_CMOV) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, 1);
+ /* a xor reg, reg operation would overwrite the flags. */
+ EMIT_MOV(compiler, dst, 0, SLJIT_IMM, 0);
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 3);
+ FAIL_IF(!inst);
+ INC_SIZE(3);
+
+ *inst++ = GROUP_0F;
+ /* cmovcc = setcc - 0x50. */
+ *inst++ = U8(cond_set - 0x50);
+ *inst++ = U8(MOD_REG | (reg_map[dst] << 3) | reg_map[TMP_REG1]);
+ return SLJIT_SUCCESS;
+ }
+
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 3 + 3 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1 + 3 + 3 + 1);
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ /* Set al to conditional flag. */
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | 0 /* eax */;
+
+ *inst++ = GROUP_0F;
+ *inst++ = MOVZX_r_rm8;
+ *inst++ = U8(MOD_REG | (reg_map[dst] << 3) | 0 /* eax */);
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ return SLJIT_SUCCESS;
+ }
+
+ if (GET_OPCODE(op) == SLJIT_OR && !GET_ALL_FLAGS(op) && FAST_IS_REG(dst) && reg_map[dst] <= 4) {
+ SLJIT_ASSERT(reg_map[SLJIT_R0] == 0);
+
+ if (dst != SLJIT_R0) {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 3 + 2 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1 + 3 + 2 + 1);
+ /* Set low register to conditional flag. */
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | 0 /* eax */;
+ *inst++ = OR_rm8_r8;
+ *inst++ = MOD_REG | (0 /* eax */ << 3) | reg_map[dst];
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ }
+ else {
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 2 + 3 + 2 + 2);
+ FAIL_IF(!inst);
+ INC_SIZE(2 + 3 + 2 + 2);
+ /* Set low register to conditional flag. */
+ *inst++ = XCHG_r_rm;
+ *inst++ = U8(MOD_REG | (1 /* ecx */ << 3) | reg_map[TMP_REG1]);
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | 1 /* ecx */;
+ *inst++ = OR_rm8_r8;
+ *inst++ = MOD_REG | (1 /* ecx */ << 3) | 0 /* eax */;
+ *inst++ = XCHG_r_rm;
+ *inst++ = U8(MOD_REG | (1 /* ecx */ << 3) | reg_map[TMP_REG1]);
+ }
+ return SLJIT_SUCCESS;
+ }
+
+ /* Set TMP_REG1 to the bit. */
+ inst = (sljit_u8*)ensure_buf(compiler, 1 + 1 + 3 + 3 + 1);
+ FAIL_IF(!inst);
+ INC_SIZE(1 + 3 + 3 + 1);
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+ /* Set al to conditional flag. */
+ *inst++ = GROUP_0F;
+ *inst++ = cond_set;
+ *inst++ = MOD_REG | 0 /* eax */;
+
+ *inst++ = GROUP_0F;
+ *inst++ = MOVZX_r_rm8;
+ *inst++ = MOD_REG | (0 << 3) /* eax */ | 0 /* eax */;
+
+ *inst++ = U8(XCHG_EAX_r | reg_map[TMP_REG1]);
+
+ if (GET_OPCODE(op) < SLJIT_ADD)
+ return emit_mov(compiler, dst, dstw, TMP_REG1, 0);
+
+ SLJIT_SKIP_CHECKS(compiler);
+ return sljit_emit_op2(compiler, op, dst_save, dstw_save, dst_save, dstw_save, TMP_REG1, 0);
+#endif /* SLJIT_CONFIG_X86_64 */
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_cmov(struct sljit_compiler *compiler, sljit_s32 type,
+ sljit_s32 dst_reg,
+ sljit_s32 src, sljit_sw srcw)
+{
+ sljit_u8* inst;
+
+ CHECK_ERROR();
+ CHECK(check_sljit_emit_cmov(compiler, type, dst_reg, src, srcw));
+
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ type &= ~SLJIT_32;
+
+ if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV) || (dst_reg >= SLJIT_R3 && dst_reg <= SLJIT_S3))
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+#else
+ if (!sljit_has_cpu_feature(SLJIT_HAS_CMOV))
+ return sljit_emit_cmov_generic(compiler, type, dst_reg, src, srcw);
+#endif
+
+ /* ADJUST_LOCAL_OFFSET is not needed. */
+ CHECK_EXTRA_REGS(src, srcw, (void)0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = type & SLJIT_32;
+ type &= ~SLJIT_32;
+#endif
+
+ if (SLJIT_UNLIKELY(src & SLJIT_IMM)) {
+ EMIT_MOV(compiler, TMP_REG1, 0, SLJIT_IMM, srcw);
+ src = TMP_REG1;
+ srcw = 0;
+ }
+
+ inst = emit_x86_instruction(compiler, 2, dst_reg, 0, src, srcw);
+ FAIL_IF(!inst);
+ *inst++ = GROUP_0F;
+ *inst = U8(get_jump_code((sljit_uw)type) - 0x40);
+ return SLJIT_SUCCESS;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_get_local_base(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw offset)
+{
+ CHECK_ERROR();
+ CHECK(check_sljit_get_local_base(compiler, dst, dstw, offset));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+#endif
+
+ ADJUST_LOCAL_OFFSET(SLJIT_MEM1(SLJIT_SP), offset);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (NOT_HALFWORD(offset)) {
+ FAIL_IF(emit_load_imm64(compiler, TMP_REG1, offset));
+#if (defined SLJIT_DEBUG && SLJIT_DEBUG)
+ SLJIT_ASSERT(emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0) != SLJIT_ERR_UNSUPPORTED);
+ return compiler->error;
+#else
+ return emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, TMP_REG1, 0);
+#endif
+ }
+#endif
+
+ if (offset != 0)
+ return emit_lea_binary(compiler, dst, dstw, SLJIT_SP, 0, SLJIT_IMM, offset);
+ return emit_mov(compiler, dst, dstw, SLJIT_SP, 0);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw, sljit_sw init_value)
+{
+ sljit_u8 *inst;
+ struct sljit_const *const_;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 reg;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_const(compiler, dst, dstw, init_value));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+ const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const));
+ PTR_FAIL_IF(!const_);
+ set_const(const_, compiler);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+ reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (emit_load_imm64(compiler, reg, init_value))
+ return NULL;
+#else
+ if (emit_mov(compiler, dst, dstw, SLJIT_IMM, init_value))
+ return NULL;
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ PTR_FAIL_IF(!inst);
+
+ *inst++ = 0;
+ *inst++ = 2;
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (dst & SLJIT_MEM)
+ if (emit_mov(compiler, dst, dstw, TMP_REG1, 0))
+ return NULL;
+#endif
+
+ return const_;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_put_label* sljit_emit_put_label(struct sljit_compiler *compiler, sljit_s32 dst, sljit_sw dstw)
+{
+ struct sljit_put_label *put_label;
+ sljit_u8 *inst;
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ sljit_s32 reg;
+ sljit_uw start_size;
+#endif
+
+ CHECK_ERROR_PTR();
+ CHECK_PTR(check_sljit_emit_put_label(compiler, dst, dstw));
+ ADJUST_LOCAL_OFFSET(dst, dstw);
+
+ CHECK_EXTRA_REGS(dst, dstw, (void)0);
+
+ put_label = (struct sljit_put_label*)ensure_abuf(compiler, sizeof(struct sljit_put_label));
+ PTR_FAIL_IF(!put_label);
+ set_put_label(put_label, compiler, 0);
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ compiler->mode32 = 0;
+ reg = FAST_IS_REG(dst) ? dst : TMP_REG1;
+
+ if (emit_load_imm64(compiler, reg, 0))
+ return NULL;
+#else
+ if (emit_mov(compiler, dst, dstw, SLJIT_IMM, 0))
+ return NULL;
+#endif
+
+#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64)
+ if (dst & SLJIT_MEM) {
+ start_size = compiler->size;
+ if (emit_mov(compiler, dst, dstw, TMP_REG1, 0))
+ return NULL;
+ put_label->flags = compiler->size - start_size;
+ }
+#endif
+
+ inst = (sljit_u8*)ensure_buf(compiler, 2);
+ PTR_FAIL_IF(!inst);
+
+ *inst++ = 0;
+ *inst++ = 3;
+
+ return put_label;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_target, sljit_sw executable_offset)
+{
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS((void*)addr, (void*)(addr + sizeof(sljit_uw)), 0);
+#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32)
+ sljit_unaligned_store_sw((void*)addr, (sljit_sw)(new_target - (addr + 4) - (sljit_uw)executable_offset));
+#else
+ sljit_unaligned_store_sw((void*)addr, (sljit_sw)new_target);
+#endif
+ SLJIT_UPDATE_WX_FLAGS((void*)addr, (void*)(addr + sizeof(sljit_uw)), 1);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_sw new_constant, sljit_sw executable_offset)
+{
+ SLJIT_UNUSED_ARG(executable_offset);
+
+ SLJIT_UPDATE_WX_FLAGS((void*)addr, (void*)(addr + sizeof(sljit_sw)), 0);
+ sljit_unaligned_store_sw((void*)addr, new_constant);
+ SLJIT_UPDATE_WX_FLAGS((void*)addr, (void*)(addr + sizeof(sljit_sw)), 1);
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitProtExecAllocator.c b/contrib/libs/pcre2/src/sljit/sljitProtExecAllocator.c
new file mode 100644
index 0000000000..915411fbed
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitProtExecAllocator.c
@@ -0,0 +1,474 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/*
+ This file contains a simple executable memory allocator
+
+ It is assumed, that executable code blocks are usually medium (or sometimes
+ large) memory blocks, and the allocator is not too frequently called (less
+ optimized than other allocators). Thus, using it as a generic allocator is
+ not suggested.
+
+ How does it work:
+ Memory is allocated in continuous memory areas called chunks by alloc_chunk()
+ Chunk format:
+ [ block ][ block ] ... [ block ][ block terminator ]
+
+ All blocks and the block terminator is started with block_header. The block
+ header contains the size of the previous and the next block. These sizes
+ can also contain special values.
+ Block size:
+ 0 - The block is a free_block, with a different size member.
+ 1 - The block is a block terminator.
+ n - The block is used at the moment, and the value contains its size.
+ Previous block size:
+ 0 - This is the first block of the memory chunk.
+ n - The size of the previous block.
+
+ Using these size values we can go forward or backward on the block chain.
+ The unused blocks are stored in a chain list pointed by free_blocks. This
+ list is useful if we need to find a suitable memory area when the allocator
+ is called.
+
+ When a block is freed, the new free block is connected to its adjacent free
+ blocks if possible.
+
+ [ free block ][ used block ][ free block ]
+ and "used block" is freed, the three blocks are connected together:
+ [ one big free block ]
+*/
+
+/* --------------------------------------------------------------------- */
+/* System (OS) functions */
+/* --------------------------------------------------------------------- */
+
+/* 64 KByte. */
+#define CHUNK_SIZE (sljit_uw)0x10000
+
+struct chunk_header {
+ void *executable;
+};
+
+/*
+ alloc_chunk / free_chunk :
+ * allocate executable system memory chunks
+ * the size is always divisible by CHUNK_SIZE
+ SLJIT_ALLOCATOR_LOCK / SLJIT_ALLOCATOR_UNLOCK :
+ * provided as part of sljitUtils
+ * only the allocator requires this lock, sljit is fully thread safe
+ as it only uses local variables
+*/
+
+#ifndef __NetBSD__
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef O_NOATIME
+#define O_NOATIME 0
+#endif
+
+/* this is a linux extension available since kernel 3.11 */
+#ifndef O_TMPFILE
+#define O_TMPFILE 020200000
+#endif
+
+#ifndef _GNU_SOURCE
+char *secure_getenv(const char *name);
+int mkostemp(char *template, int flags);
+#endif
+
+static SLJIT_INLINE int create_tempfile(void)
+{
+ int fd;
+ char tmp_name[256];
+ size_t tmp_name_len = 0;
+ char *dir;
+ struct stat st;
+#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
+ mode_t mode;
+#endif
+
+#ifdef HAVE_MEMFD_CREATE
+ /* this is a GNU extension, make sure to use -D_GNU_SOURCE */
+ fd = memfd_create("sljit", MFD_CLOEXEC);
+ if (fd != -1) {
+ fchmod(fd, 0);
+ return fd;
+ }
+#endif
+
+ dir = secure_getenv("TMPDIR");
+
+ if (dir) {
+ tmp_name_len = strlen(dir);
+ if (tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name)) {
+ if ((stat(dir, &st) == 0) && S_ISDIR(st.st_mode))
+ strcpy(tmp_name, dir);
+ }
+ }
+
+#ifdef P_tmpdir
+ if (!tmp_name_len) {
+ tmp_name_len = strlen(P_tmpdir);
+ if (tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name))
+ strcpy(tmp_name, P_tmpdir);
+ }
+#endif
+ if (!tmp_name_len) {
+ strcpy(tmp_name, "/tmp");
+ tmp_name_len = 4;
+ }
+
+ SLJIT_ASSERT(tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name));
+
+ if (tmp_name[tmp_name_len - 1] == '/')
+ tmp_name[--tmp_name_len] = '\0';
+
+#ifdef __linux__
+ /*
+ * the previous trimming might had left an empty string if TMPDIR="/"
+ * so work around the problem below
+ */
+ fd = open(tmp_name_len ? tmp_name : "/",
+ O_TMPFILE | O_EXCL | O_RDWR | O_NOATIME | O_CLOEXEC, 0);
+ if (fd != -1)
+ return fd;
+#endif
+
+ if (tmp_name_len + 7 >= sizeof(tmp_name))
+ return -1;
+
+ strcpy(tmp_name + tmp_name_len, "/XXXXXX");
+#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
+ mode = umask(0777);
+#endif
+ fd = mkostemp(tmp_name, O_CLOEXEC | O_NOATIME);
+#if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED
+ umask(mode);
+#else
+ fchmod(fd, 0);
+#endif
+
+ if (fd == -1)
+ return -1;
+
+ if (unlink(tmp_name)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static SLJIT_INLINE struct chunk_header* alloc_chunk(sljit_uw size)
+{
+ struct chunk_header *retval;
+ int fd;
+
+ fd = create_tempfile();
+ if (fd == -1)
+ return NULL;
+
+ if (ftruncate(fd, (off_t)size)) {
+ close(fd);
+ return NULL;
+ }
+
+ retval = (struct chunk_header *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if (retval == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+
+ retval->executable = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
+
+ if (retval->executable == MAP_FAILED) {
+ munmap((void *)retval, size);
+ close(fd);
+ return NULL;
+ }
+
+ close(fd);
+ return retval;
+}
+#else
+/*
+ * MAP_REMAPDUP is a NetBSD extension available sinde 8.0, make sure to
+ * adjust your feature macros (ex: -D_NETBSD_SOURCE) as needed
+ */
+static SLJIT_INLINE struct chunk_header* alloc_chunk(sljit_uw size)
+{
+ struct chunk_header *retval;
+
+ retval = (struct chunk_header *)mmap(NULL, size,
+ PROT_READ | PROT_WRITE | PROT_MPROTECT(PROT_EXEC),
+ MAP_ANON | MAP_SHARED, -1, 0);
+
+ if (retval == MAP_FAILED)
+ return NULL;
+
+ retval->executable = mremap(retval, size, NULL, size, MAP_REMAPDUP);
+ if (retval->executable == MAP_FAILED) {
+ munmap((void *)retval, size);
+ return NULL;
+ }
+
+ if (mprotect(retval->executable, size, PROT_READ | PROT_EXEC) == -1) {
+ munmap(retval->executable, size);
+ munmap((void *)retval, size);
+ return NULL;
+ }
+
+ return retval;
+}
+#endif /* NetBSD */
+
+static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
+{
+ struct chunk_header *header = ((struct chunk_header *)chunk) - 1;
+
+ munmap(header->executable, size);
+ munmap((void *)header, size);
+}
+
+/* --------------------------------------------------------------------- */
+/* Common functions */
+/* --------------------------------------------------------------------- */
+
+#define CHUNK_MASK (~(CHUNK_SIZE - 1))
+
+struct block_header {
+ sljit_uw size;
+ sljit_uw prev_size;
+ sljit_sw executable_offset;
+};
+
+struct free_block {
+ struct block_header header;
+ struct free_block *next;
+ struct free_block *prev;
+ sljit_uw size;
+};
+
+#define AS_BLOCK_HEADER(base, offset) \
+ ((struct block_header*)(((sljit_u8*)base) + offset))
+#define AS_FREE_BLOCK(base, offset) \
+ ((struct free_block*)(((sljit_u8*)base) + offset))
+#define MEM_START(base) ((void*)((base) + 1))
+#define ALIGN_SIZE(size) (((size) + sizeof(struct block_header) + 7u) & ~(sljit_uw)7)
+
+static struct free_block* free_blocks;
+static sljit_uw allocated_size;
+static sljit_uw total_size;
+
+static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
+{
+ free_block->header.size = 0;
+ free_block->size = size;
+
+ free_block->next = free_blocks;
+ free_block->prev = NULL;
+ if (free_blocks)
+ free_blocks->prev = free_block;
+ free_blocks = free_block;
+}
+
+static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
+{
+ if (free_block->next)
+ free_block->next->prev = free_block->prev;
+
+ if (free_block->prev)
+ free_block->prev->next = free_block->next;
+ else {
+ SLJIT_ASSERT(free_blocks == free_block);
+ free_blocks = free_block->next;
+ }
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+ struct chunk_header *chunk_header;
+ struct block_header *header;
+ struct block_header *next_header;
+ struct free_block *free_block;
+ sljit_uw chunk_size;
+ sljit_sw executable_offset;
+
+ SLJIT_ALLOCATOR_LOCK();
+ if (size < (64 - sizeof(struct block_header)))
+ size = (64 - sizeof(struct block_header));
+ size = ALIGN_SIZE(size);
+
+ free_block = free_blocks;
+ while (free_block) {
+ if (free_block->size >= size) {
+ chunk_size = free_block->size;
+ if (chunk_size > size + 64) {
+ /* We just cut a block from the end of the free block. */
+ chunk_size -= size;
+ free_block->size = chunk_size;
+ header = AS_BLOCK_HEADER(free_block, chunk_size);
+ header->prev_size = chunk_size;
+ header->executable_offset = free_block->header.executable_offset;
+ AS_BLOCK_HEADER(header, size)->prev_size = size;
+ }
+ else {
+ sljit_remove_free_block(free_block);
+ header = (struct block_header*)free_block;
+ size = chunk_size;
+ }
+ allocated_size += size;
+ header->size = size;
+ SLJIT_ALLOCATOR_UNLOCK();
+ return MEM_START(header);
+ }
+ free_block = free_block->next;
+ }
+
+ chunk_size = sizeof(struct chunk_header) + sizeof(struct block_header);
+ chunk_size = (chunk_size + size + CHUNK_SIZE - 1) & CHUNK_MASK;
+
+ chunk_header = alloc_chunk(chunk_size);
+ if (!chunk_header) {
+ SLJIT_ALLOCATOR_UNLOCK();
+ return NULL;
+ }
+
+ executable_offset = (sljit_sw)((sljit_u8*)chunk_header->executable - (sljit_u8*)chunk_header);
+
+ chunk_size -= sizeof(struct chunk_header) + sizeof(struct block_header);
+ total_size += chunk_size;
+
+ header = (struct block_header *)(chunk_header + 1);
+
+ header->prev_size = 0;
+ header->executable_offset = executable_offset;
+ if (chunk_size > size + 64) {
+ /* Cut the allocated space into a free and a used block. */
+ allocated_size += size;
+ header->size = size;
+ chunk_size -= size;
+
+ free_block = AS_FREE_BLOCK(header, size);
+ free_block->header.prev_size = size;
+ free_block->header.executable_offset = executable_offset;
+ sljit_insert_free_block(free_block, chunk_size);
+ next_header = AS_BLOCK_HEADER(free_block, chunk_size);
+ }
+ else {
+ /* All space belongs to this allocation. */
+ allocated_size += chunk_size;
+ header->size = chunk_size;
+ next_header = AS_BLOCK_HEADER(header, chunk_size);
+ }
+ next_header->size = 1;
+ next_header->prev_size = chunk_size;
+ next_header->executable_offset = executable_offset;
+ SLJIT_ALLOCATOR_UNLOCK();
+ return MEM_START(header);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ struct block_header *header;
+ struct free_block* free_block;
+
+ SLJIT_ALLOCATOR_LOCK();
+ header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
+ header = AS_BLOCK_HEADER(header, -header->executable_offset);
+ allocated_size -= header->size;
+
+ /* Connecting free blocks together if possible. */
+
+ /* If header->prev_size == 0, free_block will equal to header.
+ In this case, free_block->header.size will be > 0. */
+ free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
+ if (SLJIT_UNLIKELY(!free_block->header.size)) {
+ free_block->size += header->size;
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+ else {
+ free_block = (struct free_block*)header;
+ sljit_insert_free_block(free_block, header->size);
+ }
+
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ if (SLJIT_UNLIKELY(!header->size)) {
+ free_block->size += ((struct free_block*)header)->size;
+ sljit_remove_free_block((struct free_block*)header);
+ header = AS_BLOCK_HEADER(free_block, free_block->size);
+ header->prev_size = free_block->size;
+ }
+
+ /* The whole chunk is free. */
+ if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
+ /* If this block is freed, we still have (allocated_size / 2) free space. */
+ if (total_size - free_block->size > (allocated_size * 3 / 2)) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size +
+ sizeof(struct chunk_header) +
+ sizeof(struct block_header));
+ }
+ }
+
+ SLJIT_ALLOCATOR_UNLOCK();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
+{
+ struct free_block* free_block;
+ struct free_block* next_free_block;
+
+ SLJIT_ALLOCATOR_LOCK();
+
+ free_block = free_blocks;
+ while (free_block) {
+ next_free_block = free_block->next;
+ if (!free_block->header.prev_size &&
+ AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
+ total_size -= free_block->size;
+ sljit_remove_free_block(free_block);
+ free_chunk(free_block, free_block->size +
+ sizeof(struct chunk_header) +
+ sizeof(struct block_header));
+ }
+ free_block = next_free_block;
+ }
+
+ SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
+ SLJIT_ALLOCATOR_UNLOCK();
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr)
+{
+ return ((struct block_header *)(ptr))[-1].executable_offset;
+}
diff --git a/contrib/libs/pcre2/src/sljit/sljitUtils.c b/contrib/libs/pcre2/src/sljit/sljitUtils.c
new file mode 100644
index 0000000000..967593b157
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitUtils.c
@@ -0,0 +1,344 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/* ------------------------------------------------------------------------ */
+/* Locks */
+/* ------------------------------------------------------------------------ */
+
+/* Executable Allocator */
+
+#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) \
+ && !(defined SLJIT_WX_EXECUTABLE_ALLOCATOR && SLJIT_WX_EXECUTABLE_ALLOCATOR)
+#if (defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
+#define SLJIT_ALLOCATOR_LOCK()
+#define SLJIT_ALLOCATOR_UNLOCK()
+#elif !(defined _WIN32)
+#include <pthread.h>
+
+static pthread_mutex_t allocator_lock = PTHREAD_MUTEX_INITIALIZER;
+
+#define SLJIT_ALLOCATOR_LOCK() pthread_mutex_lock(&allocator_lock)
+#define SLJIT_ALLOCATOR_UNLOCK() pthread_mutex_unlock(&allocator_lock)
+#else /* windows */
+static HANDLE allocator_lock;
+
+static SLJIT_INLINE void allocator_grab_lock(void)
+{
+ HANDLE lock;
+ if (SLJIT_UNLIKELY(!InterlockedCompareExchangePointer(&allocator_lock, NULL, NULL))) {
+ lock = CreateMutex(NULL, FALSE, NULL);
+ if (InterlockedCompareExchangePointer(&allocator_lock, lock, NULL))
+ CloseHandle(lock);
+ }
+ WaitForSingleObject(allocator_lock, INFINITE);
+}
+
+#define SLJIT_ALLOCATOR_LOCK() allocator_grab_lock()
+#define SLJIT_ALLOCATOR_UNLOCK() ReleaseMutex(allocator_lock)
+#endif /* thread implementation */
+#endif /* SLJIT_EXECUTABLE_ALLOCATOR && !SLJIT_WX_EXECUTABLE_ALLOCATOR */
+
+/* ------------------------------------------------------------------------ */
+/* Stack */
+/* ------------------------------------------------------------------------ */
+
+#if ((defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) \
+ && !(defined SLJIT_UTIL_SIMPLE_STACK_ALLOCATION && SLJIT_UTIL_SIMPLE_STACK_ALLOCATION)) \
+ || ((defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) \
+ && !((defined SLJIT_PROT_EXECUTABLE_ALLOCATOR && SLJIT_PROT_EXECUTABLE_ALLOCATOR) \
+ || (defined SLJIT_WX_EXECUTABLE_ALLOCATOR && SLJIT_WX_EXECUTABLE_ALLOCATOR)))
+
+#ifndef _WIN32
+/* Provides mmap function. */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#ifndef MAP_ANON
+#ifdef MAP_ANONYMOUS
+#define MAP_ANON MAP_ANONYMOUS
+#endif /* MAP_ANONYMOUS */
+#endif /* !MAP_ANON */
+
+#ifndef MAP_ANON
+
+#include <fcntl.h>
+
+#ifdef O_CLOEXEC
+#define SLJIT_CLOEXEC O_CLOEXEC
+#else /* !O_CLOEXEC */
+#define SLJIT_CLOEXEC 0
+#endif /* O_CLOEXEC */
+
+/* Some old systems do not have MAP_ANON. */
+static int dev_zero = -1;
+
+#if (defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
+
+static SLJIT_INLINE int open_dev_zero(void)
+{
+ dev_zero = open("/dev/zero", O_RDWR | SLJIT_CLOEXEC);
+
+ return dev_zero < 0;
+}
+
+#else /* !SLJIT_SINGLE_THREADED */
+
+#include <pthread.h>
+
+static pthread_mutex_t dev_zero_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static SLJIT_INLINE int open_dev_zero(void)
+{
+ pthread_mutex_lock(&dev_zero_mutex);
+ if (SLJIT_UNLIKELY(dev_zero < 0))
+ dev_zero = open("/dev/zero", O_RDWR | SLJIT_CLOEXEC);
+
+ pthread_mutex_unlock(&dev_zero_mutex);
+ return dev_zero < 0;
+}
+
+#endif /* SLJIT_SINGLE_THREADED */
+#undef SLJIT_CLOEXEC
+#endif /* !MAP_ANON */
+#endif /* !_WIN32 */
+#endif /* open_dev_zero */
+
+#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) \
+ || (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR)
+
+#ifdef _WIN32
+
+static SLJIT_INLINE sljit_uw get_page_alignment(void) {
+ SYSTEM_INFO si;
+ static sljit_uw sljit_page_align = 0;
+ if (!sljit_page_align) {
+ GetSystemInfo(&si);
+ sljit_page_align = (sljit_uw)si.dwPageSize - 1;
+ }
+ return sljit_page_align;
+}
+
+#else
+
+#include <unistd.h>
+
+static SLJIT_INLINE sljit_uw get_page_alignment(void) {
+ static sljit_uw sljit_page_align = 0;
+
+ sljit_sw align;
+
+ if (!sljit_page_align) {
+#ifdef _SC_PAGESIZE
+ align = sysconf(_SC_PAGESIZE);
+#else
+ align = getpagesize();
+#endif
+ /* Should never happen. */
+ if (align < 0)
+ align = 4096;
+ sljit_page_align = (sljit_uw)align - 1;
+ }
+ return sljit_page_align;
+}
+
+#endif /* _WIN32 */
+
+#endif /* get_page_alignment() */
+
+#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK)
+
+#if (defined SLJIT_UTIL_SIMPLE_STACK_ALLOCATION && SLJIT_UTIL_SIMPLE_STACK_ALLOCATION)
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data)
+{
+ struct sljit_stack *stack;
+ void *ptr;
+
+ SLJIT_UNUSED_ARG(allocator_data);
+
+ if (start_size > max_size || start_size < 1)
+ return NULL;
+
+ stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack), allocator_data);
+ if (stack == NULL)
+ return NULL;
+
+ ptr = SLJIT_MALLOC(max_size, allocator_data);
+ if (ptr == NULL) {
+ SLJIT_FREE(stack, allocator_data);
+ return NULL;
+ }
+
+ stack->min_start = (sljit_u8 *)ptr;
+ stack->end = stack->min_start + max_size;
+ stack->start = stack->end - start_size;
+ stack->top = stack->end;
+ return stack;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
+{
+ SLJIT_UNUSED_ARG(allocator_data);
+ SLJIT_FREE((void*)stack->min_start, allocator_data);
+ SLJIT_FREE(stack, allocator_data);
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start)
+{
+ if ((new_start < stack->min_start) || (new_start >= stack->end))
+ return NULL;
+ stack->start = new_start;
+ return new_start;
+}
+
+#else /* !SLJIT_UTIL_SIMPLE_STACK_ALLOCATION */
+
+#ifdef _WIN32
+
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
+{
+ SLJIT_UNUSED_ARG(allocator_data);
+ VirtualFree((void*)stack->min_start, 0, MEM_RELEASE);
+ SLJIT_FREE(stack, allocator_data);
+}
+
+#else /* !_WIN32 */
+
+SLJIT_API_FUNC_ATTRIBUTE void SLJIT_FUNC sljit_free_stack(struct sljit_stack *stack, void *allocator_data)
+{
+ SLJIT_UNUSED_ARG(allocator_data);
+ munmap((void*)stack->min_start, (size_t)(stack->end - stack->min_start));
+ SLJIT_FREE(stack, allocator_data);
+}
+
+#endif /* _WIN32 */
+
+SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_FUNC sljit_allocate_stack(sljit_uw start_size, sljit_uw max_size, void *allocator_data)
+{
+ struct sljit_stack *stack;
+ void *ptr;
+ sljit_uw page_align;
+
+ SLJIT_UNUSED_ARG(allocator_data);
+
+ if (start_size > max_size || start_size < 1)
+ return NULL;
+
+ stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack), allocator_data);
+ if (stack == NULL)
+ return NULL;
+
+ /* Align max_size. */
+ page_align = get_page_alignment();
+ max_size = (max_size + page_align) & ~page_align;
+
+#ifdef _WIN32
+ ptr = VirtualAlloc(NULL, max_size, MEM_RESERVE, PAGE_READWRITE);
+ if (!ptr) {
+ SLJIT_FREE(stack, allocator_data);
+ return NULL;
+ }
+
+ stack->min_start = (sljit_u8 *)ptr;
+ stack->end = stack->min_start + max_size;
+ stack->start = stack->end;
+
+ if (sljit_stack_resize(stack, stack->end - start_size) == NULL) {
+ sljit_free_stack(stack, allocator_data);
+ return NULL;
+ }
+#else /* !_WIN32 */
+#ifdef MAP_ANON
+ ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+#else /* !MAP_ANON */
+ if (SLJIT_UNLIKELY((dev_zero < 0) && open_dev_zero())) {
+ SLJIT_FREE(stack, allocator_data);
+ return NULL;
+ }
+ ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero, 0);
+#endif /* MAP_ANON */
+ if (ptr == MAP_FAILED) {
+ SLJIT_FREE(stack, allocator_data);
+ return NULL;
+ }
+ stack->min_start = (sljit_u8 *)ptr;
+ stack->end = stack->min_start + max_size;
+ stack->start = stack->end - start_size;
+#endif /* _WIN32 */
+
+ stack->top = stack->end;
+ return stack;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_stack *stack, sljit_u8 *new_start)
+{
+#if defined _WIN32 || defined(POSIX_MADV_DONTNEED)
+ sljit_uw aligned_old_start;
+ sljit_uw aligned_new_start;
+ sljit_uw page_align;
+#endif
+
+ if ((new_start < stack->min_start) || (new_start >= stack->end))
+ return NULL;
+
+#ifdef _WIN32
+ page_align = get_page_alignment();
+
+ aligned_new_start = (sljit_uw)new_start & ~page_align;
+ aligned_old_start = ((sljit_uw)stack->start) & ~page_align;
+ if (aligned_new_start != aligned_old_start) {
+ if (aligned_new_start < aligned_old_start) {
+ if (!VirtualAlloc((void*)aligned_new_start, aligned_old_start - aligned_new_start, MEM_COMMIT, PAGE_READWRITE))
+ return NULL;
+ }
+ else {
+ if (!VirtualFree((void*)aligned_old_start, aligned_new_start - aligned_old_start, MEM_DECOMMIT))
+ return NULL;
+ }
+ }
+#elif defined(POSIX_MADV_DONTNEED)
+ if (stack->start < new_start) {
+ page_align = get_page_alignment();
+
+ aligned_new_start = (sljit_uw)new_start & ~page_align;
+ aligned_old_start = ((sljit_uw)stack->start) & ~page_align;
+
+ if (aligned_new_start > aligned_old_start) {
+ posix_madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, POSIX_MADV_DONTNEED);
+#ifdef MADV_FREE
+ madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, MADV_FREE);
+#endif /* MADV_FREE */
+ }
+ }
+#endif /* _WIN32 */
+
+ stack->start = new_start;
+ return new_start;
+}
+
+#endif /* SLJIT_UTIL_SIMPLE_STACK_ALLOCATION */
+
+#endif /* SLJIT_UTIL_STACK */
diff --git a/contrib/libs/pcre2/src/sljit/sljitWXExecAllocator.c b/contrib/libs/pcre2/src/sljit/sljitWXExecAllocator.c
new file mode 100644
index 0000000000..6893813155
--- /dev/null
+++ b/contrib/libs/pcre2/src/sljit/sljitWXExecAllocator.c
@@ -0,0 +1,204 @@
+/*
+ * Stack-less Just-In-Time compiler
+ *
+ * Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``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 COPYRIGHT HOLDER(S) OR CONTRIBUTORS 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.
+ */
+
+/*
+ This file contains a simple W^X executable memory allocator for POSIX
+ like systems and Windows
+
+ In *NIX, MAP_ANON is required (that is considered a feature) so make
+ sure to set the right availability macros for your system or the code
+ will fail to build.
+
+ If your system doesn't support mapping of anonymous pages (ex: IRIX) it
+ is also likely that it doesn't need this allocator and should be using
+ the standard one instead.
+
+ It allocates a separate map for each code block and may waste a lot of
+ memory, because whatever was requested, will be rounded up to the page
+ size (minimum 4KB, but could be even bigger).
+
+ It changes the page permissions (RW <-> RX) as needed and therefore, if you
+ will be updating the code after it has been generated, need to make sure to
+ block any concurrent execution, or could result in a SIGBUS, that could
+ even manifest itself at a different address than the one that was being
+ modified.
+
+ Only use if you are unable to use the regular allocator because of security
+ restrictions and adding exceptions to your application or the system are
+ not possible.
+*/
+
+#define SLJIT_UPDATE_WX_FLAGS(from, to, enable_exec) \
+ sljit_update_wx_flags((from), (to), (enable_exec))
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#ifdef __NetBSD__
+#define SLJIT_PROT_WX PROT_MPROTECT(PROT_EXEC)
+#define check_se_protected(ptr, size) (0)
+#else /* POSIX */
+#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED)
+#include <pthread.h>
+#define SLJIT_SE_LOCK() pthread_mutex_lock(&se_lock)
+#define SLJIT_SE_UNLOCK() pthread_mutex_unlock(&se_lock)
+#endif /* !SLJIT_SINGLE_THREADED */
+
+#define check_se_protected(ptr, size) generic_se_protected(ptr, size)
+
+static SLJIT_INLINE int generic_se_protected(void *ptr, sljit_uw size)
+{
+ if (SLJIT_LIKELY(!mprotect(ptr, size, PROT_EXEC)))
+ return mprotect(ptr, size, PROT_READ | PROT_WRITE);
+
+ return -1;
+}
+#endif /* NetBSD */
+
+#ifndef SLJIT_SE_LOCK
+#define SLJIT_SE_LOCK()
+#endif
+#ifndef SLJIT_SE_UNLOCK
+#define SLJIT_SE_UNLOCK()
+#endif
+#ifndef SLJIT_PROT_WX
+#define SLJIT_PROT_WX 0
+#endif
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+#if !(defined SLJIT_SINGLE_THREADED && SLJIT_SINGLE_THREADED) \
+ && !defined(__NetBSD__)
+ static pthread_mutex_t se_lock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+ static int se_protected = !SLJIT_PROT_WX;
+ int prot = PROT_READ | PROT_WRITE | SLJIT_PROT_WX;
+ sljit_uw* ptr;
+
+ if (SLJIT_UNLIKELY(se_protected < 0))
+ return NULL;
+
+#ifdef PROT_MAX
+ prot |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC);
+#endif
+
+ size += sizeof(sljit_uw);
+ ptr = (sljit_uw*)mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
+
+ if (ptr == MAP_FAILED)
+ return NULL;
+
+ if (SLJIT_UNLIKELY(se_protected > 0)) {
+ SLJIT_SE_LOCK();
+ se_protected = check_se_protected(ptr, size);
+ SLJIT_SE_UNLOCK();
+ if (SLJIT_UNLIKELY(se_protected < 0)) {
+ munmap((void *)ptr, size);
+ return NULL;
+ }
+ }
+
+ *ptr++ = size;
+ return ptr;
+}
+
+#undef SLJIT_PROT_WX
+#undef SLJIT_SE_UNLOCK
+#undef SLJIT_SE_LOCK
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ sljit_uw *start_ptr = ((sljit_uw*)ptr) - 1;
+ munmap((void*)start_ptr, *start_ptr);
+}
+
+static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
+{
+ sljit_uw page_mask = (sljit_uw)get_page_alignment();
+ sljit_uw start = (sljit_uw)from;
+ sljit_uw end = (sljit_uw)to;
+ int prot = PROT_READ | (enable_exec ? PROT_EXEC : PROT_WRITE);
+
+ SLJIT_ASSERT(start < end);
+
+ start &= ~page_mask;
+ end = (end + page_mask) & ~page_mask;
+
+ mprotect((void*)start, end - start, prot);
+}
+
+#else /* windows */
+
+SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
+{
+ sljit_uw *ptr;
+
+ size += sizeof(sljit_uw);
+ ptr = (sljit_uw*)VirtualAlloc(NULL, size,
+ MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+
+ if (!ptr)
+ return NULL;
+
+ *ptr++ = size;
+
+ return ptr;
+}
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
+{
+ sljit_uw start = (sljit_uw)ptr - sizeof(sljit_uw);
+#if defined(SLJIT_DEBUG) && SLJIT_DEBUG
+ sljit_uw page_mask = (sljit_uw)get_page_alignment();
+
+ SLJIT_ASSERT(!(start & page_mask));
+#endif
+ VirtualFree((void*)start, 0, MEM_RELEASE);
+}
+
+static void sljit_update_wx_flags(void *from, void *to, sljit_s32 enable_exec)
+{
+ DWORD oldprot;
+ sljit_uw page_mask = (sljit_uw)get_page_alignment();
+ sljit_uw start = (sljit_uw)from;
+ sljit_uw end = (sljit_uw)to;
+ DWORD prot = enable_exec ? PAGE_EXECUTE : PAGE_READWRITE;
+
+ SLJIT_ASSERT(start < end);
+
+ start &= ~page_mask;
+ end = (end + page_mask) & ~page_mask;
+
+ VirtualProtect((void*)start, end - start, prot, &oldprot);
+}
+
+#endif /* !windows */
+
+SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
+{
+ /* This allocator does not keep unused memory for future allocations. */
+}
diff --git a/contrib/libs/pcre2/ya.make b/contrib/libs/pcre2/ya.make
new file mode 100644
index 0000000000..27d3962e4b
--- /dev/null
+++ b/contrib/libs/pcre2/ya.make
@@ -0,0 +1,70 @@
+# Generated by devtools/yamaker from nixpkgs 22.05.
+
+LIBRARY()
+
+VERSION(10.42)
+
+ORIGINAL_SOURCE(https://github.com/PCRE2Project/pcre2/archive/pcre2-10.42.tar.gz)
+
+LICENSE(
+ BSD-2-Clause AND
+ BSD-3-Clause AND
+ FSFAP AND
+ GPL-1.0-or-later AND
+ LicenseRef-scancode-pcre AND
+ LicenseRef-scancode-unknown-license-reference AND
+ Public-Domain
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+ADDINCL(
+ contrib/libs/pcre2
+ contrib/libs/pcre2/src
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_RUNTIME()
+
+CFLAGS(
+ -DHAVE_CONFIG_H
+ -DPCRE2_CODE_UNIT_WIDTH=8
+)
+
+SRCS(
+ src/pcre2_auto_possess.c
+ src/pcre2_chartables.c
+ src/pcre2_compile.c
+ src/pcre2_config.c
+ src/pcre2_context.c
+ src/pcre2_convert.c
+ src/pcre2_dfa_match.c
+ src/pcre2_error.c
+ src/pcre2_extuni.c
+ src/pcre2_find_bracket.c
+ src/pcre2_jit_compile.c
+ src/pcre2_maketables.c
+ src/pcre2_match.c
+ src/pcre2_match_data.c
+ src/pcre2_newline.c
+ src/pcre2_ord2utf.c
+ src/pcre2_pattern_info.c
+ src/pcre2_script_run.c
+ src/pcre2_serialize.c
+ src/pcre2_string_utils.c
+ src/pcre2_study.c
+ src/pcre2_substitute.c
+ src/pcre2_substring.c
+ src/pcre2_tables.c
+ src/pcre2_ucd.c
+ src/pcre2_valid_utf.c
+ src/pcre2_xclass.c
+)
+
+END()
+
+RECURSE(
+ pcre2-16
+ pcre2-32
+)
diff --git a/contrib/libs/python/Include/opcode.h b/contrib/libs/python/Include/opcode.h
new file mode 100644
index 0000000000..add566d1ce
--- /dev/null
+++ b/contrib/libs/python/Include/opcode.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#ifdef USE_PYTHON3
+#include <contrib/tools/python3/src/Include/opcode.h>
+#else
+#include <contrib/tools/python/src/Include/opcode.h>
+#endif
diff --git a/contrib/python/coverage/plugins/coveragerc.txt b/contrib/python/coverage/plugins/coveragerc.txt
new file mode 100644
index 0000000000..83bfed8690
--- /dev/null
+++ b/contrib/python/coverage/plugins/coveragerc.txt
@@ -0,0 +1,29 @@
+[report]
+skip_empty = True
+
+exclude_lines =
+ pragma\s*:\s*no\s*cover
+ def __repr__
+ raise AssertionError
+ raise NotImplementedError
+ if 0:
+ if False:
+ if __name__ == .__main__.:
+ if self\.debug:
+ if settings\.DEBUG
+
+[run]
+suppress_plugin_errors = False
+plugins =
+ contrib.python.coverage.plugins.yarcadia.plugin
+ contrib.tools.cython.Cython.Coverage
+
+[contrib.python.coverage.plugins.yarcadia.plugin]
+pylib_paths =
+ # don't trace contrib
+ contrib/python
+ contrib/python3
+ # don't trace python sources
+ contrib/tools/python
+ contrib/tools/python3
+ contrib/libs/protobuf
diff --git a/contrib/python/coverage/plugins/ya.make b/contrib/python/coverage/plugins/ya.make
new file mode 100644
index 0000000000..30be33f72a
--- /dev/null
+++ b/contrib/python/coverage/plugins/ya.make
@@ -0,0 +1,19 @@
+PY23_LIBRARY()
+
+LICENSE(Apache-2.0)
+
+PEERDIR(
+ build/plugins/lib/test_const
+ contrib/tools/cython/Cython
+ library/python/testing/coverage_utils
+)
+
+PY_SRCS(
+ yarcadia/plugin.py
+)
+
+RESOURCE(
+ coveragerc.txt /coverage_plugins/coveragerc.txt
+)
+
+END()
diff --git a/contrib/python/coverage/plugins/yarcadia/plugin.py b/contrib/python/coverage/plugins/yarcadia/plugin.py
new file mode 100644
index 0000000000..44d9b003ca
--- /dev/null
+++ b/contrib/python/coverage/plugins/yarcadia/plugin.py
@@ -0,0 +1,114 @@
+# coding: utf-8
+
+import os
+
+import coverage.config
+import coverage.files
+import coverage.misc
+import coverage.parser
+import coverage.plugin
+import coverage.python
+
+from build.plugins.lib import test_const
+from library.python.testing import coverage_utils
+
+
+SKIP_FILENAME = '__SKIP_FILENAME__'
+
+
+class YarcadiaPlugin(
+ coverage.plugin.CoveragePlugin,
+ coverage.plugin.FileTracer
+):
+
+ def __init__(self, options):
+ self.config = coverage.config.CoverageConfig()
+ self.config.from_args(**options)
+
+ dirs = options.get("pylib_paths", "").split("\n")
+ dirs = [d for d in dirs if d and not d.startswith("#")]
+ self.pylib_paths = dirs
+
+ self._filename = None
+ self._exclude = None
+
+ self._setup_file_filter()
+
+ def _setup_file_filter(self):
+ prefix_filter = os.environ.get('PYTHON_COVERAGE_PREFIX_FILTER', '')
+ exclude_regexp = os.environ.get('PYTHON_COVERAGE_EXCLUDE_REGEXP', '')
+ self.file_filter = coverage_utils.make_filter(prefix_filter, exclude_regexp)
+
+ def configure(self, config):
+ self._exclude = coverage.misc.join_regex(config.get_option('report:exclude_lines'))
+
+ def get_pylib_paths(self):
+ return self.pylib_paths
+
+ def file_tracer(self, filename):
+ if not filename.endswith(test_const.COVERAGE_PYTHON_EXTS):
+ # Catch all generated modules (__file__ without proper extension)
+ self._filename = SKIP_FILENAME
+ return self
+
+ if not self.file_filter(filename):
+ # we need to catch all filtered out files (including cython) to pass them to get_source
+ self._filename = SKIP_FILENAME
+ return self
+
+ if filename.endswith(".py"):
+ self._filename = filename
+ return self
+
+ # Let cython plugin register it's own file tracer for pyx/pxi files
+ return None
+
+ def has_dynamic_source_filename(self):
+ return False
+
+ def source_filename(self):
+ return self._filename
+
+ def file_reporter(self, morf):
+ source_root = os.environ.get("PYTHON_COVERAGE_ARCADIA_SOURCE_ROOT")
+ if source_root:
+ return FileReporter(morf, source_root, self, self._exclude)
+ # use default file reporter
+ return "python"
+
+
+class FileReporter(coverage.python.PythonFileReporter):
+
+ def __init__(self, morf, source_root, coverage=None, exclude=None):
+ super(FileReporter, self).__init__(morf, coverage)
+ self._source = get_source(morf, source_root)
+ # use custom parser to provide proper way to get required source
+ self._parser = Parser(morf, self._source, exclude)
+ self._parser.parse_source()
+
+
+class Parser(coverage.parser.PythonParser):
+
+ def __init__(self, morf, source_code, exclude):
+ # provide source code to avoid default way to get it
+ super(Parser, self).__init__(text=source_code, filename=morf, exclude=exclude)
+
+
+def get_source(filename, source_root):
+ assert source_root
+
+ if filename == SKIP_FILENAME:
+ return ''
+
+ abs_filename = os.path.join(source_root, filename)
+ if not os.path.isfile(abs_filename):
+ # it's fake generated package
+ return u''
+
+ return coverage.python.get_python_source(abs_filename, force_fs=True)
+
+
+def coverage_init(reg, options):
+ plugin = YarcadiaPlugin(options)
+ reg.add_configurer(plugin)
+ reg.add_file_tracer(plugin)
diff --git a/contrib/python/coverage/py2/.dist-info/METADATA b/contrib/python/coverage/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..25a6049c45
--- /dev/null
+++ b/contrib/python/coverage/py2/.dist-info/METADATA
@@ -0,0 +1,190 @@
+Metadata-Version: 2.1
+Name: coverage
+Version: 5.5
+Summary: Code coverage measurement for Python
+Home-page: https://github.com/nedbat/coveragepy
+Author: Ned Batchelder and 142 others
+Author-email: ned@nedbatchelder.com
+License: Apache 2.0
+Project-URL: Documentation, https://coverage.readthedocs.io
+Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=pypi
+Project-URL: Issues, https://github.com/nedbat/coveragepy/issues
+Keywords: code coverage testing
+Platform: UNKNOWN
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Quality Assurance
+Classifier: Topic :: Software Development :: Testing
+Classifier: Development Status :: 5 - Production/Stable
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4
+Description-Content-Type: text/x-rst
+Provides-Extra: toml
+Requires-Dist: toml ; extra == 'toml'
+
+.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+===========
+Coverage.py
+===========
+
+Code coverage testing for Python.
+
+| |license| |versions| |status|
+| |test-status| |quality-status| |docs| |codecov|
+| |kit| |format| |repos| |downloads|
+| |stars| |forks| |contributors|
+| |tidelift| |twitter-coveragepy| |twitter-nedbat|
+
+Coverage.py measures code coverage, typically during test execution. It uses
+the code analysis tools and tracing hooks provided in the Python standard
+library to determine which lines are executable, and which have been executed.
+
+Coverage.py runs on many versions of Python:
+
+* CPython 2.7.
+* CPython 3.5 through 3.10 alpha.
+* PyPy2 7.3.3 and PyPy3 7.3.3.
+
+Documentation is on `Read the Docs`_. Code repository and issue tracker are on
+`GitHub`_.
+
+.. _Read the Docs: https://coverage.readthedocs.io/
+.. _GitHub: https://github.com/nedbat/coveragepy
+
+
+**New in 5.x:** SQLite data storage, JSON report, contexts, relative filenames,
+dropped support for Python 2.6, 3.3 and 3.4.
+
+
+For Enterprise
+--------------
+
+.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png
+ :alt: Tidelift
+ :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+
+.. list-table::
+ :widths: 10 100
+
+ * - |tideliftlogo|
+ - `Available as part of the Tidelift Subscription. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
+ Coverage and thousands of other packages are working with
+ Tidelift to deliver one enterprise subscription that covers all of the open
+ source you use. If you want the flexibility of open source and the confidence
+ of commercial-grade software, this is for you.
+ `Learn more. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
+
+
+Getting Started
+---------------
+
+See the `Quick Start section`_ of the docs.
+
+.. _Quick Start section: https://coverage.readthedocs.io/#quick-start
+
+
+Change history
+--------------
+
+The complete history of changes is on the `change history page`_.
+
+.. _change history page: https://coverage.readthedocs.io/en/latest/changes.html
+
+
+Contributing
+------------
+
+See the `Contributing section`_ of the docs.
+
+.. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html
+
+
+Security
+--------
+
+To report a security vulnerability, please use the `Tidelift security
+contact`_. Tidelift will coordinate the fix and disclosure.
+
+.. _Tidelift security contact: https://tidelift.com/security
+
+
+License
+-------
+
+Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
+
+.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
+.. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+
+.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
+ :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
+ :alt: Test suite status
+.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
+ :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
+ :alt: Quality check status
+.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
+ :target: https://coverage.readthedocs.io/
+ :alt: Documentation
+.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master
+ :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master
+ :alt: Requirements status
+.. |kit| image:: https://badge.fury.io/py/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: PyPI status
+.. |format| image:: https://img.shields.io/pypi/format/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Kit format
+.. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Weekly PyPI downloads
+.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072
+ :target: https://pypi.org/project/coverage/
+ :alt: Python versions supported
+.. |status| image:: https://img.shields.io/pypi/status/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Package stability
+.. |license| image:: https://img.shields.io/pypi/l/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: License
+.. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
+ :target: https://codecov.io/github/nedbat/coveragepy?branch=master
+ :alt: Coverage!
+.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
+ :target: https://repology.org/metapackage/python:coverage/versions
+ :alt: Packaging status
+.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage
+ :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+ :alt: Tidelift
+.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/stargazers
+ :alt: Github stars
+.. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/network/members
+ :alt: Github forks
+.. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/graphs/contributors
+ :alt: Contributors
+.. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/coveragepy
+ :alt: coverage.py on Twitter
+.. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/nedbat
+ :alt: nedbat on Twitter
+
+
diff --git a/contrib/python/coverage/py2/.dist-info/entry_points.txt b/contrib/python/coverage/py2/.dist-info/entry_points.txt
new file mode 100644
index 0000000000..cd083fc1ff
--- /dev/null
+++ b/contrib/python/coverage/py2/.dist-info/entry_points.txt
@@ -0,0 +1,5 @@
+[console_scripts]
+coverage = coverage.cmdline:main
+coverage-3.9 = coverage.cmdline:main
+coverage3 = coverage.cmdline:main
+
diff --git a/contrib/python/coverage/py2/.dist-info/top_level.txt b/contrib/python/coverage/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..4ebc8aea50
--- /dev/null
+++ b/contrib/python/coverage/py2/.dist-info/top_level.txt
@@ -0,0 +1 @@
+coverage
diff --git a/contrib/python/coverage/py2/LICENSE.txt b/contrib/python/coverage/py2/LICENSE.txt
new file mode 100644
index 0000000000..f433b1a53f
--- /dev/null
+++ b/contrib/python/coverage/py2/LICENSE.txt
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/contrib/python/coverage/py2/NOTICE.txt b/contrib/python/coverage/py2/NOTICE.txt
new file mode 100644
index 0000000000..37ded535bf
--- /dev/null
+++ b/contrib/python/coverage/py2/NOTICE.txt
@@ -0,0 +1,14 @@
+Copyright 2001 Gareth Rees. All rights reserved.
+Copyright 2004-2021 Ned Batchelder. All rights reserved.
+
+Except where noted otherwise, this software is licensed under the Apache
+License, Version 2.0 (the "License"); you may not use this work except in
+compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/contrib/python/coverage/py2/README.rst b/contrib/python/coverage/py2/README.rst
new file mode 100644
index 0000000000..072f30ffeb
--- /dev/null
+++ b/contrib/python/coverage/py2/README.rst
@@ -0,0 +1,151 @@
+.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+===========
+Coverage.py
+===========
+
+Code coverage testing for Python.
+
+| |license| |versions| |status|
+| |test-status| |quality-status| |docs| |codecov|
+| |kit| |format| |repos| |downloads|
+| |stars| |forks| |contributors|
+| |tidelift| |twitter-coveragepy| |twitter-nedbat|
+
+Coverage.py measures code coverage, typically during test execution. It uses
+the code analysis tools and tracing hooks provided in the Python standard
+library to determine which lines are executable, and which have been executed.
+
+Coverage.py runs on many versions of Python:
+
+* CPython 2.7.
+* CPython 3.5 through 3.10 alpha.
+* PyPy2 7.3.3 and PyPy3 7.3.3.
+
+Documentation is on `Read the Docs`_. Code repository and issue tracker are on
+`GitHub`_.
+
+.. _Read the Docs: https://coverage.readthedocs.io/
+.. _GitHub: https://github.com/nedbat/coveragepy
+
+
+**New in 5.x:** SQLite data storage, JSON report, contexts, relative filenames,
+dropped support for Python 2.6, 3.3 and 3.4.
+
+
+For Enterprise
+--------------
+
+.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png
+ :alt: Tidelift
+ :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+
+.. list-table::
+ :widths: 10 100
+
+ * - |tideliftlogo|
+ - `Available as part of the Tidelift Subscription. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
+ Coverage and thousands of other packages are working with
+ Tidelift to deliver one enterprise subscription that covers all of the open
+ source you use. If you want the flexibility of open source and the confidence
+ of commercial-grade software, this is for you.
+ `Learn more. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
+
+
+Getting Started
+---------------
+
+See the `Quick Start section`_ of the docs.
+
+.. _Quick Start section: https://coverage.readthedocs.io/#quick-start
+
+
+Change history
+--------------
+
+The complete history of changes is on the `change history page`_.
+
+.. _change history page: https://coverage.readthedocs.io/en/latest/changes.html
+
+
+Contributing
+------------
+
+See the `Contributing section`_ of the docs.
+
+.. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html
+
+
+Security
+--------
+
+To report a security vulnerability, please use the `Tidelift security
+contact`_. Tidelift will coordinate the fix and disclosure.
+
+.. _Tidelift security contact: https://tidelift.com/security
+
+
+License
+-------
+
+Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
+
+.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
+.. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+
+.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
+ :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
+ :alt: Test suite status
+.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
+ :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
+ :alt: Quality check status
+.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
+ :target: https://coverage.readthedocs.io/
+ :alt: Documentation
+.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master
+ :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master
+ :alt: Requirements status
+.. |kit| image:: https://badge.fury.io/py/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: PyPI status
+.. |format| image:: https://img.shields.io/pypi/format/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Kit format
+.. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Weekly PyPI downloads
+.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072
+ :target: https://pypi.org/project/coverage/
+ :alt: Python versions supported
+.. |status| image:: https://img.shields.io/pypi/status/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Package stability
+.. |license| image:: https://img.shields.io/pypi/l/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: License
+.. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
+ :target: https://codecov.io/github/nedbat/coveragepy?branch=master
+ :alt: Coverage!
+.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
+ :target: https://repology.org/metapackage/python:coverage/versions
+ :alt: Packaging status
+.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage
+ :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+ :alt: Tidelift
+.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/stargazers
+ :alt: Github stars
+.. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/network/members
+ :alt: Github forks
+.. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/graphs/contributors
+ :alt: Contributors
+.. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/coveragepy
+ :alt: coverage.py on Twitter
+.. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/nedbat
+ :alt: nedbat on Twitter
diff --git a/contrib/python/coverage/py2/coverage/__init__.py b/contrib/python/coverage/py2/coverage/__init__.py
new file mode 100644
index 0000000000..331b304b68
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/__init__.py
@@ -0,0 +1,36 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Code coverage measurement for Python.
+
+Ned Batchelder
+https://nedbatchelder.com/code/coverage
+
+"""
+
+import sys
+
+from coverage.version import __version__, __url__, version_info
+
+from coverage.control import Coverage, process_startup
+from coverage.data import CoverageData
+from coverage.misc import CoverageException
+from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
+from coverage.pytracer import PyTracer
+
+# Backward compatibility.
+coverage = Coverage
+
+# On Windows, we encode and decode deep enough that something goes wrong and
+# the encodings.utf_8 module is loaded and then unloaded, I don't know why.
+# Adding a reference here prevents it from being unloaded. Yuk.
+import encodings.utf_8 # pylint: disable=wrong-import-position, wrong-import-order
+
+# Because of the "from coverage.control import fooey" lines at the top of the
+# file, there's an entry for coverage.coverage in sys.modules, mapped to None.
+# This makes some inspection tools (like pydoc) unable to find the class
+# coverage.coverage. So remove that entry.
+try:
+ del sys.modules['coverage.coverage']
+except KeyError:
+ pass
diff --git a/contrib/python/coverage/py2/coverage/__main__.py b/contrib/python/coverage/py2/coverage/__main__.py
new file mode 100644
index 0000000000..79aa4e2b35
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/__main__.py
@@ -0,0 +1,8 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Coverage.py's main entry point."""
+
+import sys
+from coverage.cmdline import main
+sys.exit(main())
diff --git a/contrib/python/coverage/py2/coverage/annotate.py b/contrib/python/coverage/py2/coverage/annotate.py
new file mode 100644
index 0000000000..999ab6e557
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/annotate.py
@@ -0,0 +1,108 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Source file annotation for coverage.py."""
+
+import io
+import os
+import re
+
+from coverage.files import flat_rootname
+from coverage.misc import ensure_dir, isolate_module
+from coverage.report import get_analysis_to_report
+
+os = isolate_module(os)
+
+
+class AnnotateReporter(object):
+ """Generate annotated source files showing line coverage.
+
+ This reporter creates annotated copies of the measured source files. Each
+ .py file is copied as a .py,cover file, with a left-hand margin annotating
+ each line::
+
+ > def h(x):
+ - if 0: #pragma: no cover
+ - pass
+ > if x == 1:
+ ! a = 1
+ > else:
+ > a = 2
+
+ > h(2)
+
+ Executed lines use '>', lines not executed use '!', lines excluded from
+ consideration use '-'.
+
+ """
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+ self.directory = None
+
+ blank_re = re.compile(r"\s*(#|$)")
+ else_re = re.compile(r"\s*else\s*:\s*(#|$)")
+
+ def report(self, morfs, directory=None):
+ """Run the report.
+
+ See `coverage.report()` for arguments.
+
+ """
+ self.directory = directory
+ self.coverage.get_data()
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
+ self.annotate_file(fr, analysis)
+
+ def annotate_file(self, fr, analysis):
+ """Annotate a single file.
+
+ `fr` is the FileReporter for the file to annotate.
+
+ """
+ statements = sorted(analysis.statements)
+ missing = sorted(analysis.missing)
+ excluded = sorted(analysis.excluded)
+
+ if self.directory:
+ ensure_dir(self.directory)
+ dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename()))
+ if dest_file.endswith("_py"):
+ dest_file = dest_file[:-3] + ".py"
+ dest_file += ",cover"
+ else:
+ dest_file = fr.filename + ",cover"
+
+ with io.open(dest_file, 'w', encoding='utf8') as dest:
+ i = 0
+ j = 0
+ covered = True
+ source = fr.source()
+ for lineno, line in enumerate(source.splitlines(True), start=1):
+ while i < len(statements) and statements[i] < lineno:
+ i += 1
+ while j < len(missing) and missing[j] < lineno:
+ j += 1
+ if i < len(statements) and statements[i] == lineno:
+ covered = j >= len(missing) or missing[j] > lineno
+ if self.blank_re.match(line):
+ dest.write(u' ')
+ elif self.else_re.match(line):
+ # Special logic for lines containing only 'else:'.
+ if i >= len(statements) and j >= len(missing):
+ dest.write(u'! ')
+ elif i >= len(statements) or j >= len(missing):
+ dest.write(u'> ')
+ elif statements[i] == missing[j]:
+ dest.write(u'! ')
+ else:
+ dest.write(u'> ')
+ elif lineno in excluded:
+ dest.write(u'- ')
+ elif covered:
+ dest.write(u'> ')
+ else:
+ dest.write(u'! ')
+
+ dest.write(line)
diff --git a/contrib/python/coverage/py2/coverage/backward.py b/contrib/python/coverage/py2/coverage/backward.py
new file mode 100644
index 0000000000..ac781ab96a
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/backward.py
@@ -0,0 +1,267 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Add things to old Pythons so I can pretend they are newer."""
+
+# This file's purpose is to provide modules to be imported from here.
+# pylint: disable=unused-import
+
+import os
+import sys
+
+from datetime import datetime
+
+from coverage import env
+
+
+# Pythons 2 and 3 differ on where to get StringIO.
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+# In py3, ConfigParser was renamed to the more-standard configparser.
+# But there's a py3 backport that installs "configparser" in py2, and I don't
+# want it because it has annoying deprecation warnings. So try the real py2
+# import first.
+try:
+ import ConfigParser as configparser
+except ImportError:
+ import configparser
+
+# What's a string called?
+try:
+ string_class = basestring
+except NameError:
+ string_class = str
+
+# What's a Unicode string called?
+try:
+ unicode_class = unicode
+except NameError:
+ unicode_class = str
+
+# range or xrange?
+try:
+ range = xrange # pylint: disable=redefined-builtin
+except NameError:
+ range = range
+
+try:
+ from itertools import zip_longest
+except ImportError:
+ from itertools import izip_longest as zip_longest
+
+# Where do we get the thread id from?
+try:
+ from thread import get_ident as get_thread_id
+except ImportError:
+ from threading import get_ident as get_thread_id
+
+try:
+ os.PathLike
+except AttributeError:
+ # This is Python 2 and 3
+ path_types = (bytes, string_class, unicode_class)
+else:
+ # 3.6+
+ path_types = (bytes, str, os.PathLike)
+
+# shlex.quote is new, but there's an undocumented implementation in "pipes",
+# who knew!?
+try:
+ from shlex import quote as shlex_quote
+except ImportError:
+ # Useful function, available under a different (undocumented) name
+ # in Python versions earlier than 3.3.
+ from pipes import quote as shlex_quote
+
+try:
+ import reprlib
+except ImportError: # pragma: not covered
+ # We need this on Python 2, but in testing environments, a backport is
+ # installed, so this import isn't used.
+ import repr as reprlib
+
+# A function to iterate listlessly over a dict's items, and one to get the
+# items as a list.
+try:
+ {}.iteritems
+except AttributeError:
+ # Python 3
+ def iitems(d):
+ """Produce the items from dict `d`."""
+ return d.items()
+
+ def litems(d):
+ """Return a list of items from dict `d`."""
+ return list(d.items())
+else:
+ # Python 2
+ def iitems(d):
+ """Produce the items from dict `d`."""
+ return d.iteritems()
+
+ def litems(d):
+ """Return a list of items from dict `d`."""
+ return d.items()
+
+# Getting the `next` function from an iterator is different in 2 and 3.
+try:
+ iter([]).next
+except AttributeError:
+ def iternext(seq):
+ """Get the `next` function for iterating over `seq`."""
+ return iter(seq).__next__
+else:
+ def iternext(seq):
+ """Get the `next` function for iterating over `seq`."""
+ return iter(seq).next
+
+# Python 3.x is picky about bytes and strings, so provide methods to
+# get them right, and make them no-ops in 2.x
+if env.PY3:
+ def to_bytes(s):
+ """Convert string `s` to bytes."""
+ return s.encode('utf8')
+
+ def to_string(b):
+ """Convert bytes `b` to string."""
+ return b.decode('utf8')
+
+ def binary_bytes(byte_values):
+ """Produce a byte string with the ints from `byte_values`."""
+ return bytes(byte_values)
+
+ def byte_to_int(byte):
+ """Turn a byte indexed from a bytes object into an int."""
+ return byte
+
+ def bytes_to_ints(bytes_value):
+ """Turn a bytes object into a sequence of ints."""
+ # In Python 3, iterating bytes gives ints.
+ return bytes_value
+
+else:
+ def to_bytes(s):
+ """Convert string `s` to bytes (no-op in 2.x)."""
+ return s
+
+ def to_string(b):
+ """Convert bytes `b` to string."""
+ return b
+
+ def binary_bytes(byte_values):
+ """Produce a byte string with the ints from `byte_values`."""
+ return "".join(chr(b) for b in byte_values)
+
+ def byte_to_int(byte):
+ """Turn a byte indexed from a bytes object into an int."""
+ return ord(byte)
+
+ def bytes_to_ints(bytes_value):
+ """Turn a bytes object into a sequence of ints."""
+ for byte in bytes_value:
+ yield ord(byte)
+
+
+try:
+ # In Python 2.x, the builtins were in __builtin__
+ BUILTINS = sys.modules['__builtin__']
+except KeyError:
+ # In Python 3.x, they're in builtins
+ BUILTINS = sys.modules['builtins']
+
+
+# imp was deprecated in Python 3.3
+try:
+ import importlib
+ import importlib.util
+ imp = None
+except ImportError:
+ importlib = None
+
+# We only want to use importlib if it has everything we need.
+try:
+ importlib_util_find_spec = importlib.util.find_spec
+except Exception:
+ import imp
+ importlib_util_find_spec = None
+
+# What is the .pyc magic number for this version of Python?
+try:
+ PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER
+except AttributeError:
+ PYC_MAGIC_NUMBER = imp.get_magic()
+
+
+def code_object(fn):
+ """Get the code object from a function."""
+ try:
+ return fn.func_code
+ except AttributeError:
+ return fn.__code__
+
+
+try:
+ from types import SimpleNamespace
+except ImportError:
+ # The code from https://docs.python.org/3/library/types.html#types.SimpleNamespace
+ class SimpleNamespace:
+ """Python implementation of SimpleNamespace, for Python 2."""
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
+
+ def __repr__(self):
+ keys = sorted(self.__dict__)
+ items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
+ return "{}({})".format(type(self).__name__, ", ".join(items))
+
+
+def format_local_datetime(dt):
+ """Return a string with local timezone representing the date.
+ If python version is lower than 3.6, the time zone is not included.
+ """
+ try:
+ return dt.astimezone().strftime('%Y-%m-%d %H:%M %z')
+ except (TypeError, ValueError):
+ # Datetime.astimezone in Python 3.5 can not handle naive datetime
+ return dt.strftime('%Y-%m-%d %H:%M')
+
+
+def invalidate_import_caches():
+ """Invalidate any import caches that may or may not exist."""
+ if importlib and hasattr(importlib, "invalidate_caches"):
+ importlib.invalidate_caches()
+
+
+def import_local_file(modname, modfile=None):
+ """Import a local file as a module.
+
+ Opens a file in the current directory named `modname`.py, imports it
+ as `modname`, and returns the module object. `modfile` is the file to
+ import if it isn't in the current directory.
+
+ """
+ try:
+ import importlib.util as importlib_util
+ except ImportError:
+ importlib_util = None
+
+ if modfile is None:
+ modfile = modname + '.py'
+ if importlib_util:
+ spec = importlib_util.spec_from_file_location(modname, modfile)
+ mod = importlib_util.module_from_spec(spec)
+ sys.modules[modname] = mod
+ spec.loader.exec_module(mod)
+ else:
+ for suff in imp.get_suffixes(): # pragma: part covered
+ if suff[0] == '.py':
+ break
+
+ with open(modfile, 'r') as f:
+ # pylint: disable=undefined-loop-variable
+ mod = imp.load_module(modname, f, modfile, suff)
+
+ return mod
diff --git a/contrib/python/coverage/py2/coverage/bytecode.py b/contrib/python/coverage/py2/coverage/bytecode.py
new file mode 100644
index 0000000000..ceb18cf374
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/bytecode.py
@@ -0,0 +1,19 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Bytecode manipulation for coverage.py"""
+
+import types
+
+
+def code_objects(code):
+ """Iterate over all the code objects in `code`."""
+ stack = [code]
+ while stack:
+ # We're going to return the code object on the stack, but first
+ # push its children for later returning.
+ code = stack.pop()
+ for c in code.co_consts:
+ if isinstance(c, types.CodeType):
+ stack.append(c)
+ yield code
diff --git a/contrib/python/coverage/py2/coverage/cmdline.py b/contrib/python/coverage/py2/coverage/cmdline.py
new file mode 100644
index 0000000000..0be0cca19f
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/cmdline.py
@@ -0,0 +1,910 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Command-line support for coverage.py."""
+
+from __future__ import print_function
+
+import glob
+import optparse
+import os.path
+import shlex
+import sys
+import textwrap
+import traceback
+
+import coverage
+from coverage import Coverage
+from coverage import env
+from coverage.collector import CTracer
+from coverage.data import line_counts
+from coverage.debug import info_formatter, info_header, short_stack
+from coverage.execfile import PyRunner
+from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource, output_encoding
+from coverage.results import should_fail_under
+
+
+class Opts(object):
+ """A namespace class for individual options we'll build parsers from."""
+
+ append = optparse.make_option(
+ '-a', '--append', action='store_true',
+ help="Append coverage data to .coverage, otherwise it starts clean each time.",
+ )
+ keep = optparse.make_option(
+ '', '--keep', action='store_true',
+ help="Keep original coverage files, otherwise they are deleted.",
+ )
+ branch = optparse.make_option(
+ '', '--branch', action='store_true',
+ help="Measure branch coverage in addition to statement coverage.",
+ )
+ CONCURRENCY_CHOICES = [
+ "thread", "gevent", "greenlet", "eventlet", "multiprocessing",
+ ]
+ concurrency = optparse.make_option(
+ '', '--concurrency', action='store', metavar="LIB",
+ choices=CONCURRENCY_CHOICES,
+ help=(
+ "Properly measure code using a concurrency library. "
+ "Valid values are: %s."
+ ) % ", ".join(CONCURRENCY_CHOICES),
+ )
+ context = optparse.make_option(
+ '', '--context', action='store', metavar="LABEL",
+ help="The context label to record for this coverage run.",
+ )
+ debug = optparse.make_option(
+ '', '--debug', action='store', metavar="OPTS",
+ help="Debug options, separated by commas. [env: COVERAGE_DEBUG]",
+ )
+ directory = optparse.make_option(
+ '-d', '--directory', action='store', metavar="DIR",
+ help="Write the output files to DIR.",
+ )
+ fail_under = optparse.make_option(
+ '', '--fail-under', action='store', metavar="MIN", type="float",
+ help="Exit with a status of 2 if the total coverage is less than MIN.",
+ )
+ help = optparse.make_option(
+ '-h', '--help', action='store_true',
+ help="Get help on this command.",
+ )
+ ignore_errors = optparse.make_option(
+ '-i', '--ignore-errors', action='store_true',
+ help="Ignore errors while reading source files.",
+ )
+ include = optparse.make_option(
+ '', '--include', action='store',
+ metavar="PAT1,PAT2,...",
+ help=(
+ "Include only files whose paths match one of these patterns. "
+ "Accepts shell-style wildcards, which must be quoted."
+ ),
+ )
+ pylib = optparse.make_option(
+ '-L', '--pylib', action='store_true',
+ help=(
+ "Measure coverage even inside the Python installed library, "
+ "which isn't done by default."
+ ),
+ )
+ sort = optparse.make_option(
+ '--sort', action='store', metavar='COLUMN',
+ help="Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. "
+ "Default is name."
+ )
+ show_missing = optparse.make_option(
+ '-m', '--show-missing', action='store_true',
+ help="Show line numbers of statements in each module that weren't executed.",
+ )
+ skip_covered = optparse.make_option(
+ '--skip-covered', action='store_true',
+ help="Skip files with 100% coverage.",
+ )
+ no_skip_covered = optparse.make_option(
+ '--no-skip-covered', action='store_false', dest='skip_covered',
+ help="Disable --skip-covered.",
+ )
+ skip_empty = optparse.make_option(
+ '--skip-empty', action='store_true',
+ help="Skip files with no code.",
+ )
+ show_contexts = optparse.make_option(
+ '--show-contexts', action='store_true',
+ help="Show contexts for covered lines.",
+ )
+ omit = optparse.make_option(
+ '', '--omit', action='store',
+ metavar="PAT1,PAT2,...",
+ help=(
+ "Omit files whose paths match one of these patterns. "
+ "Accepts shell-style wildcards, which must be quoted."
+ ),
+ )
+ contexts = optparse.make_option(
+ '', '--contexts', action='store',
+ metavar="REGEX1,REGEX2,...",
+ help=(
+ "Only display data from lines covered in the given contexts. "
+ "Accepts Python regexes, which must be quoted."
+ ),
+ )
+ output_xml = optparse.make_option(
+ '-o', '', action='store', dest="outfile",
+ metavar="OUTFILE",
+ help="Write the XML report to this file. Defaults to 'coverage.xml'",
+ )
+ output_json = optparse.make_option(
+ '-o', '', action='store', dest="outfile",
+ metavar="OUTFILE",
+ help="Write the JSON report to this file. Defaults to 'coverage.json'",
+ )
+ json_pretty_print = optparse.make_option(
+ '', '--pretty-print', action='store_true',
+ help="Format the JSON for human readers.",
+ )
+ parallel_mode = optparse.make_option(
+ '-p', '--parallel-mode', action='store_true',
+ help=(
+ "Append the machine name, process id and random number to the "
+ ".coverage data file name to simplify collecting data from "
+ "many processes."
+ ),
+ )
+ module = optparse.make_option(
+ '-m', '--module', action='store_true',
+ help=(
+ "<pyfile> is an importable Python module, not a script path, "
+ "to be run as 'python -m' would run it."
+ ),
+ )
+ precision = optparse.make_option(
+ '', '--precision', action='store', metavar='N', type=int,
+ help=(
+ "Number of digits after the decimal point to display for "
+ "reported coverage percentages."
+ ),
+ )
+ rcfile = optparse.make_option(
+ '', '--rcfile', action='store',
+ help=(
+ "Specify configuration file. "
+ "By default '.coveragerc', 'setup.cfg', 'tox.ini', and "
+ "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]"
+ ),
+ )
+ source = optparse.make_option(
+ '', '--source', action='store', metavar="SRC1,SRC2,...",
+ help="A list of packages or directories of code to be measured.",
+ )
+ timid = optparse.make_option(
+ '', '--timid', action='store_true',
+ help=(
+ "Use a simpler but slower trace method. Try this if you get "
+ "seemingly impossible results!"
+ ),
+ )
+ title = optparse.make_option(
+ '', '--title', action='store', metavar="TITLE",
+ help="A text string to use as the title on the HTML.",
+ )
+ version = optparse.make_option(
+ '', '--version', action='store_true',
+ help="Display version information and exit.",
+ )
+
+
+class CoverageOptionParser(optparse.OptionParser, object):
+ """Base OptionParser for coverage.py.
+
+ Problems don't exit the program.
+ Defaults are initialized for all options.
+
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(CoverageOptionParser, self).__init__(
+ add_help_option=False, *args, **kwargs
+ )
+ self.set_defaults(
+ action=None,
+ append=None,
+ branch=None,
+ concurrency=None,
+ context=None,
+ debug=None,
+ directory=None,
+ fail_under=None,
+ help=None,
+ ignore_errors=None,
+ include=None,
+ keep=None,
+ module=None,
+ omit=None,
+ contexts=None,
+ parallel_mode=None,
+ precision=None,
+ pylib=None,
+ rcfile=True,
+ show_missing=None,
+ skip_covered=None,
+ skip_empty=None,
+ show_contexts=None,
+ sort=None,
+ source=None,
+ timid=None,
+ title=None,
+ version=None,
+ )
+
+ self.disable_interspersed_args()
+
+ class OptionParserError(Exception):
+ """Used to stop the optparse error handler ending the process."""
+ pass
+
+ def parse_args_ok(self, args=None, options=None):
+ """Call optparse.parse_args, but return a triple:
+
+ (ok, options, args)
+
+ """
+ try:
+ options, args = super(CoverageOptionParser, self).parse_args(args, options)
+ except self.OptionParserError:
+ return False, None, None
+ return True, options, args
+
+ def error(self, msg):
+ """Override optparse.error so sys.exit doesn't get called."""
+ show_help(msg)
+ raise self.OptionParserError
+
+
+class GlobalOptionParser(CoverageOptionParser):
+ """Command-line parser for coverage.py global option arguments."""
+
+ def __init__(self):
+ super(GlobalOptionParser, self).__init__()
+
+ self.add_options([
+ Opts.help,
+ Opts.version,
+ ])
+
+
+class CmdOptionParser(CoverageOptionParser):
+ """Parse one of the new-style commands for coverage.py."""
+
+ def __init__(self, action, options, defaults=None, usage=None, description=None):
+ """Create an OptionParser for a coverage.py command.
+
+ `action` is the slug to put into `options.action`.
+ `options` is a list of Option's for the command.
+ `defaults` is a dict of default value for options.
+ `usage` is the usage string to display in help.
+ `description` is the description of the command, for the help text.
+
+ """
+ if usage:
+ usage = "%prog " + usage
+ super(CmdOptionParser, self).__init__(
+ usage=usage,
+ description=description,
+ )
+ self.set_defaults(action=action, **(defaults or {}))
+ self.add_options(options)
+ self.cmd = action
+
+ def __eq__(self, other):
+ # A convenience equality, so that I can put strings in unit test
+ # results, and they will compare equal to objects.
+ return (other == "<CmdOptionParser:%s>" % self.cmd)
+
+ __hash__ = None # This object doesn't need to be hashed.
+
+ def get_prog_name(self):
+ """Override of an undocumented function in optparse.OptionParser."""
+ program_name = super(CmdOptionParser, self).get_prog_name()
+
+ # Include the sub-command for this parser as part of the command.
+ return "{command} {subcommand}".format(command=program_name, subcommand=self.cmd)
+
+
+GLOBAL_ARGS = [
+ Opts.debug,
+ Opts.help,
+ Opts.rcfile,
+ ]
+
+CMDS = {
+ 'annotate': CmdOptionParser(
+ "annotate",
+ [
+ Opts.directory,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description=(
+ "Make annotated copies of the given files, marking statements that are executed "
+ "with > and statements that are missed with !."
+ ),
+ ),
+
+ 'combine': CmdOptionParser(
+ "combine",
+ [
+ Opts.append,
+ Opts.keep,
+ ] + GLOBAL_ARGS,
+ usage="[options] <path1> <path2> ... <pathN>",
+ description=(
+ "Combine data from multiple coverage files collected "
+ "with 'run -p'. The combined results are written to a single "
+ "file representing the union of the data. The positional "
+ "arguments are data files or directories containing data files. "
+ "If no paths are provided, data files in the default data file's "
+ "directory are combined."
+ ),
+ ),
+
+ 'debug': CmdOptionParser(
+ "debug", GLOBAL_ARGS,
+ usage="<topic>",
+ description=(
+ "Display information about the internals of coverage.py, "
+ "for diagnosing problems. "
+ "Topics are: "
+ "'data' to show a summary of the collected data; "
+ "'sys' to show installation information; "
+ "'config' to show the configuration; "
+ "'premain' to show what is calling coverage."
+ ),
+ ),
+
+ 'erase': CmdOptionParser(
+ "erase", GLOBAL_ARGS,
+ description="Erase previously collected coverage data.",
+ ),
+
+ 'help': CmdOptionParser(
+ "help", GLOBAL_ARGS,
+ usage="[command]",
+ description="Describe how to use coverage.py",
+ ),
+
+ 'html': CmdOptionParser(
+ "html",
+ [
+ Opts.contexts,
+ Opts.directory,
+ Opts.fail_under,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ Opts.precision,
+ Opts.show_contexts,
+ Opts.skip_covered,
+ Opts.no_skip_covered,
+ Opts.skip_empty,
+ Opts.title,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description=(
+ "Create an HTML report of the coverage of the files. "
+ "Each file gets its own page, with the source decorated to show "
+ "executed, excluded, and missed lines."
+ ),
+ ),
+
+ 'json': CmdOptionParser(
+ "json",
+ [
+ Opts.contexts,
+ Opts.fail_under,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ Opts.output_json,
+ Opts.json_pretty_print,
+ Opts.show_contexts,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description="Generate a JSON report of coverage results."
+ ),
+
+ 'report': CmdOptionParser(
+ "report",
+ [
+ Opts.contexts,
+ Opts.fail_under,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ Opts.precision,
+ Opts.sort,
+ Opts.show_missing,
+ Opts.skip_covered,
+ Opts.no_skip_covered,
+ Opts.skip_empty,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description="Report coverage statistics on modules."
+ ),
+
+ 'run': CmdOptionParser(
+ "run",
+ [
+ Opts.append,
+ Opts.branch,
+ Opts.concurrency,
+ Opts.context,
+ Opts.include,
+ Opts.module,
+ Opts.omit,
+ Opts.pylib,
+ Opts.parallel_mode,
+ Opts.source,
+ Opts.timid,
+ ] + GLOBAL_ARGS,
+ usage="[options] <pyfile> [program options]",
+ description="Run a Python program, measuring code execution."
+ ),
+
+ 'xml': CmdOptionParser(
+ "xml",
+ [
+ Opts.fail_under,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ Opts.output_xml,
+ Opts.skip_empty,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description="Generate an XML report of coverage results."
+ ),
+}
+
+
+def show_help(error=None, topic=None, parser=None):
+ """Display an error message, or the named topic."""
+ assert error or topic or parser
+
+ program_path = sys.argv[0]
+ if program_path.endswith(os.path.sep + '__main__.py'):
+ # The path is the main module of a package; get that path instead.
+ program_path = os.path.dirname(program_path)
+ program_name = os.path.basename(program_path)
+ if env.WINDOWS:
+ # entry_points={'console_scripts':...} on Windows makes files
+ # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These
+ # invoke coverage-script.py, coverage3-script.py, and
+ # coverage-3.5-script.py. argv[0] is the .py file, but we want to
+ # get back to the original form.
+ auto_suffix = "-script.py"
+ if program_name.endswith(auto_suffix):
+ program_name = program_name[:-len(auto_suffix)]
+
+ help_params = dict(coverage.__dict__)
+ help_params['program_name'] = program_name
+ if CTracer is not None:
+ help_params['extension_modifier'] = 'with C extension'
+ else:
+ help_params['extension_modifier'] = 'without C extension'
+
+ if error:
+ print(error, file=sys.stderr)
+ print("Use '%s help' for help." % (program_name,), file=sys.stderr)
+ elif parser:
+ print(parser.format_help().strip())
+ print()
+ else:
+ help_msg = textwrap.dedent(HELP_TOPICS.get(topic, '')).strip()
+ if help_msg:
+ print(help_msg.format(**help_params))
+ else:
+ print("Don't know topic %r" % topic)
+ print("Full documentation is at {__url__}".format(**help_params))
+
+
+OK, ERR, FAIL_UNDER = 0, 1, 2
+
+
+class CoverageScript(object):
+ """The command-line interface to coverage.py."""
+
+ def __init__(self):
+ self.global_option = False
+ self.coverage = None
+
+ def command_line(self, argv):
+ """The bulk of the command line interface to coverage.py.
+
+ `argv` is the argument list to process.
+
+ Returns 0 if all is well, 1 if something went wrong.
+
+ """
+ # Collect the command-line options.
+ if not argv:
+ show_help(topic='minimum_help')
+ return OK
+
+ # The command syntax we parse depends on the first argument. Global
+ # switch syntax always starts with an option.
+ self.global_option = argv[0].startswith('-')
+ if self.global_option:
+ parser = GlobalOptionParser()
+ else:
+ parser = CMDS.get(argv[0])
+ if not parser:
+ show_help("Unknown command: '%s'" % argv[0])
+ return ERR
+ argv = argv[1:]
+
+ ok, options, args = parser.parse_args_ok(argv)
+ if not ok:
+ return ERR
+
+ # Handle help and version.
+ if self.do_help(options, args, parser):
+ return OK
+
+ # Listify the list options.
+ source = unshell_list(options.source)
+ omit = unshell_list(options.omit)
+ include = unshell_list(options.include)
+ debug = unshell_list(options.debug)
+ contexts = unshell_list(options.contexts)
+
+ # Do something.
+ self.coverage = Coverage(
+ data_suffix=options.parallel_mode,
+ cover_pylib=options.pylib,
+ timid=options.timid,
+ branch=options.branch,
+ config_file=options.rcfile,
+ source=source,
+ omit=omit,
+ include=include,
+ debug=debug,
+ concurrency=options.concurrency,
+ check_preimported=True,
+ context=options.context,
+ )
+
+ if options.action == "debug":
+ return self.do_debug(args)
+
+ elif options.action == "erase":
+ self.coverage.erase()
+ return OK
+
+ elif options.action == "run":
+ return self.do_run(options, args)
+
+ elif options.action == "combine":
+ if options.append:
+ self.coverage.load()
+ data_dirs = args or None
+ self.coverage.combine(data_dirs, strict=True, keep=bool(options.keep))
+ self.coverage.save()
+ return OK
+
+ # Remaining actions are reporting, with some common options.
+ report_args = dict(
+ morfs=unglob_args(args),
+ ignore_errors=options.ignore_errors,
+ omit=omit,
+ include=include,
+ contexts=contexts,
+ )
+
+ # We need to be able to import from the current directory, because
+ # plugins may try to, for example, to read Django settings.
+ sys.path.insert(0, '')
+
+ self.coverage.load()
+
+ total = None
+ if options.action == "report":
+ total = self.coverage.report(
+ show_missing=options.show_missing,
+ skip_covered=options.skip_covered,
+ skip_empty=options.skip_empty,
+ precision=options.precision,
+ sort=options.sort,
+ **report_args
+ )
+ elif options.action == "annotate":
+ self.coverage.annotate(directory=options.directory, **report_args)
+ elif options.action == "html":
+ total = self.coverage.html_report(
+ directory=options.directory,
+ title=options.title,
+ skip_covered=options.skip_covered,
+ skip_empty=options.skip_empty,
+ show_contexts=options.show_contexts,
+ precision=options.precision,
+ **report_args
+ )
+ elif options.action == "xml":
+ outfile = options.outfile
+ total = self.coverage.xml_report(
+ outfile=outfile, skip_empty=options.skip_empty,
+ **report_args
+ )
+ elif options.action == "json":
+ outfile = options.outfile
+ total = self.coverage.json_report(
+ outfile=outfile,
+ pretty_print=options.pretty_print,
+ show_contexts=options.show_contexts,
+ **report_args
+ )
+
+ if total is not None:
+ # Apply the command line fail-under options, and then use the config
+ # value, so we can get fail_under from the config file.
+ if options.fail_under is not None:
+ self.coverage.set_option("report:fail_under", options.fail_under)
+
+ fail_under = self.coverage.get_option("report:fail_under")
+ precision = self.coverage.get_option("report:precision")
+ if should_fail_under(total, fail_under, precision):
+ msg = "total of {total:.{p}f} is less than fail-under={fail_under:.{p}f}".format(
+ total=total, fail_under=fail_under, p=precision,
+ )
+ print("Coverage failure:", msg)
+ return FAIL_UNDER
+
+ return OK
+
+ def do_help(self, options, args, parser):
+ """Deal with help requests.
+
+ Return True if it handled the request, False if not.
+
+ """
+ # Handle help.
+ if options.help:
+ if self.global_option:
+ show_help(topic='help')
+ else:
+ show_help(parser=parser)
+ return True
+
+ if options.action == "help":
+ if args:
+ for a in args:
+ parser = CMDS.get(a)
+ if parser:
+ show_help(parser=parser)
+ else:
+ show_help(topic=a)
+ else:
+ show_help(topic='help')
+ return True
+
+ # Handle version.
+ if options.version:
+ show_help(topic='version')
+ return True
+
+ return False
+
+ def do_run(self, options, args):
+ """Implementation of 'coverage run'."""
+
+ if not args:
+ if options.module:
+ # Specified -m with nothing else.
+ show_help("No module specified for -m")
+ return ERR
+ command_line = self.coverage.get_option("run:command_line")
+ if command_line is not None:
+ args = shlex.split(command_line)
+ if args and args[0] == "-m":
+ options.module = True
+ args = args[1:]
+ if not args:
+ show_help("Nothing to do.")
+ return ERR
+
+ if options.append and self.coverage.get_option("run:parallel"):
+ show_help("Can't append to data files in parallel mode.")
+ return ERR
+
+ if options.concurrency == "multiprocessing":
+ # Can't set other run-affecting command line options with
+ # multiprocessing.
+ for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']:
+ # As it happens, all of these options have no default, meaning
+ # they will be None if they have not been specified.
+ if getattr(options, opt_name) is not None:
+ show_help(
+ "Options affecting multiprocessing must only be specified "
+ "in a configuration file.\n"
+ "Remove --{} from the command line.".format(opt_name)
+ )
+ return ERR
+
+ runner = PyRunner(args, as_module=bool(options.module))
+ runner.prepare()
+
+ if options.append:
+ self.coverage.load()
+
+ # Run the script.
+ self.coverage.start()
+ code_ran = True
+ try:
+ runner.run()
+ except NoSource:
+ code_ran = False
+ raise
+ finally:
+ self.coverage.stop()
+ if code_ran:
+ self.coverage.save()
+
+ return OK
+
+ def do_debug(self, args):
+ """Implementation of 'coverage debug'."""
+
+ if not args:
+ show_help("What information would you like: config, data, sys, premain?")
+ return ERR
+
+ for info in args:
+ if info == 'sys':
+ sys_info = self.coverage.sys_info()
+ print(info_header("sys"))
+ for line in info_formatter(sys_info):
+ print(" %s" % line)
+ elif info == 'data':
+ self.coverage.load()
+ data = self.coverage.get_data()
+ print(info_header("data"))
+ print("path: %s" % data.data_filename())
+ if data:
+ print("has_arcs: %r" % data.has_arcs())
+ summary = line_counts(data, fullpath=True)
+ filenames = sorted(summary.keys())
+ print("\n%d files:" % len(filenames))
+ for f in filenames:
+ line = "%s: %d lines" % (f, summary[f])
+ plugin = data.file_tracer(f)
+ if plugin:
+ line += " [%s]" % plugin
+ print(line)
+ else:
+ print("No data collected")
+ elif info == 'config':
+ print(info_header("config"))
+ config_info = self.coverage.config.__dict__.items()
+ for line in info_formatter(config_info):
+ print(" %s" % line)
+ elif info == "premain":
+ print(info_header("premain"))
+ print(short_stack())
+ else:
+ show_help("Don't know what you mean by %r" % info)
+ return ERR
+
+ return OK
+
+
+def unshell_list(s):
+ """Turn a command-line argument into a list."""
+ if not s:
+ return None
+ if env.WINDOWS:
+ # When running coverage.py as coverage.exe, some of the behavior
+ # of the shell is emulated: wildcards are expanded into a list of
+ # file names. So you have to single-quote patterns on the command
+ # line, but (not) helpfully, the single quotes are included in the
+ # argument, so we have to strip them off here.
+ s = s.strip("'")
+ return s.split(',')
+
+
+def unglob_args(args):
+ """Interpret shell wildcards for platforms that need it."""
+ if env.WINDOWS:
+ globbed = []
+ for arg in args:
+ if '?' in arg or '*' in arg:
+ globbed.extend(glob.glob(arg))
+ else:
+ globbed.append(arg)
+ args = globbed
+ return args
+
+
+HELP_TOPICS = {
+ 'help': """\
+ Coverage.py, version {__version__} {extension_modifier}
+ Measure, collect, and report on code coverage in Python programs.
+
+ usage: {program_name} <command> [options] [args]
+
+ Commands:
+ annotate Annotate source files with execution information.
+ combine Combine a number of data files.
+ debug Display information about the internals of coverage.py
+ erase Erase previously collected coverage data.
+ help Get help on using coverage.py.
+ html Create an HTML report.
+ json Create a JSON report of coverage results.
+ report Report coverage stats on modules.
+ run Run a Python program and measure code execution.
+ xml Create an XML report of coverage results.
+
+ Use "{program_name} help <command>" for detailed help on any command.
+ """,
+
+ 'minimum_help': """\
+ Code coverage for Python, version {__version__} {extension_modifier}. Use '{program_name} help' for help.
+ """,
+
+ 'version': """\
+ Coverage.py, version {__version__} {extension_modifier}
+ """,
+}
+
+
+def main(argv=None):
+ """The main entry point to coverage.py.
+
+ This is installed as the script entry point.
+
+ """
+ if argv is None:
+ argv = sys.argv[1:]
+ try:
+ status = CoverageScript().command_line(argv)
+ except ExceptionDuringRun as err:
+ # An exception was caught while running the product code. The
+ # sys.exc_info() return tuple is packed into an ExceptionDuringRun
+ # exception.
+ traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter
+ status = ERR
+ except BaseCoverageException as err:
+ # A controlled error inside coverage.py: print the message to the user.
+ msg = err.args[0]
+ if env.PY2:
+ msg = msg.encode(output_encoding())
+ print(msg)
+ status = ERR
+ except SystemExit as err:
+ # The user called `sys.exit()`. Exit with their argument, if any.
+ if err.args:
+ status = err.args[0]
+ else:
+ status = None
+ return status
+
+# Profiling using ox_profile. Install it from GitHub:
+# pip install git+https://github.com/emin63/ox_profile.git
+#
+# $set_env.py: COVERAGE_PROFILE - Set to use ox_profile.
+_profile = os.environ.get("COVERAGE_PROFILE", "")
+if _profile: # pragma: debugging
+ from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error
+ original_main = main
+
+ def main(argv=None): # pylint: disable=function-redefined
+ """A wrapper around main that profiles."""
+ profiler = SimpleLauncher.launch()
+ try:
+ return original_main(argv)
+ finally:
+ data, _ = profiler.query(re_filter='coverage', max_records=100)
+ print(profiler.show(query=data, limit=100, sep='', col=''))
+ profiler.cancel()
diff --git a/contrib/python/coverage/py2/coverage/collector.py b/contrib/python/coverage/py2/coverage/collector.py
new file mode 100644
index 0000000000..c42d29feec
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/collector.py
@@ -0,0 +1,455 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Raw data collector for coverage.py."""
+
+import os
+import sys
+
+from coverage import env
+from coverage.backward import litems, range # pylint: disable=redefined-builtin
+from coverage.debug import short_stack
+from coverage.disposition import FileDisposition
+from coverage.misc import CoverageException, isolate_module
+from coverage.pytracer import PyTracer
+
+os = isolate_module(os)
+
+
+try:
+ # Use the C extension code when we can, for speed.
+ from coverage.tracer import CTracer, CFileDisposition
+except ImportError:
+ # Couldn't import the C extension, maybe it isn't built.
+ if os.getenv('COVERAGE_TEST_TRACER') == 'c':
+ # During testing, we use the COVERAGE_TEST_TRACER environment variable
+ # to indicate that we've fiddled with the environment to test this
+ # fallback code. If we thought we had a C tracer, but couldn't import
+ # it, then exit quickly and clearly instead of dribbling confusing
+ # errors. I'm using sys.exit here instead of an exception because an
+ # exception here causes all sorts of other noise in unittest.
+ sys.stderr.write("*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n")
+ sys.exit(1)
+ CTracer = None
+
+
+class Collector(object):
+ """Collects trace data.
+
+ Creates a Tracer object for each thread, since they track stack
+ information. Each Tracer points to the same shared data, contributing
+ traced data points.
+
+ When the Collector is started, it creates a Tracer for the current thread,
+ and installs a function to create Tracers for each new thread started.
+ When the Collector is stopped, all active Tracers are stopped.
+
+ Threads started while the Collector is stopped will never have Tracers
+ associated with them.
+
+ """
+
+ # The stack of active Collectors. Collectors are added here when started,
+ # and popped when stopped. Collectors on the stack are paused when not
+ # the top, and resumed when they become the top again.
+ _collectors = []
+
+ # The concurrency settings we support here.
+ SUPPORTED_CONCURRENCIES = {"greenlet", "eventlet", "gevent", "thread"}
+
+ def __init__(
+ self, should_trace, check_include, should_start_context, file_mapper,
+ timid, branch, warn, concurrency,
+ ):
+ """Create a collector.
+
+ `should_trace` is a function, taking a file name and a frame, and
+ returning a `coverage.FileDisposition object`.
+
+ `check_include` is a function taking a file name and a frame. It returns
+ a boolean: True if the file should be traced, False if not.
+
+ `should_start_context` is a function taking a frame, and returning a
+ string. If the frame should be the start of a new context, the string
+ is the new context. If the frame should not be the start of a new
+ context, return None.
+
+ `file_mapper` is a function taking a filename, and returning a Unicode
+ filename. The result is the name that will be recorded in the data
+ file.
+
+ If `timid` is true, then a slower simpler trace function will be
+ used. This is important for some environments where manipulation of
+ tracing functions make the faster more sophisticated trace function not
+ operate properly.
+
+ If `branch` is true, then branches will be measured. This involves
+ collecting data on which statements followed each other (arcs). Use
+ `get_arc_data` to get the arc data.
+
+ `warn` is a warning function, taking a single string message argument
+ and an optional slug argument which will be a string or None, to be
+ used if a warning needs to be issued.
+
+ `concurrency` is a list of strings indicating the concurrency libraries
+ in use. Valid values are "greenlet", "eventlet", "gevent", or "thread"
+ (the default). Of these four values, only one can be supplied. Other
+ values are ignored.
+
+ """
+ self.should_trace = should_trace
+ self.check_include = check_include
+ self.should_start_context = should_start_context
+ self.file_mapper = file_mapper
+ self.warn = warn
+ self.branch = branch
+ self.threading = None
+ self.covdata = None
+
+ self.static_context = None
+
+ self.origin = short_stack()
+
+ self.concur_id_func = None
+ self.mapped_file_cache = {}
+
+ # We can handle a few concurrency options here, but only one at a time.
+ these_concurrencies = self.SUPPORTED_CONCURRENCIES.intersection(concurrency)
+ if len(these_concurrencies) > 1:
+ raise CoverageException("Conflicting concurrency settings: %s" % concurrency)
+ self.concurrency = these_concurrencies.pop() if these_concurrencies else ''
+
+ try:
+ if self.concurrency == "greenlet":
+ import greenlet
+ self.concur_id_func = greenlet.getcurrent
+ elif self.concurrency == "eventlet":
+ import eventlet.greenthread # pylint: disable=import-error,useless-suppression
+ self.concur_id_func = eventlet.greenthread.getcurrent
+ elif self.concurrency == "gevent":
+ import gevent # pylint: disable=import-error,useless-suppression
+ self.concur_id_func = gevent.getcurrent
+ elif self.concurrency == "thread" or not self.concurrency:
+ # It's important to import threading only if we need it. If
+ # it's imported early, and the program being measured uses
+ # gevent, then gevent's monkey-patching won't work properly.
+ import threading
+ self.threading = threading
+ else:
+ raise CoverageException("Don't understand concurrency=%s" % concurrency)
+ except ImportError:
+ raise CoverageException(
+ "Couldn't trace with concurrency=%s, the module isn't installed." % (
+ self.concurrency,
+ )
+ )
+
+ self.reset()
+
+ if timid:
+ # Being timid: use the simple Python trace function.
+ self._trace_class = PyTracer
+ else:
+ # Being fast: use the C Tracer if it is available, else the Python
+ # trace function.
+ self._trace_class = CTracer or PyTracer
+
+ if self._trace_class is CTracer:
+ self.file_disposition_class = CFileDisposition
+ self.supports_plugins = True
+ else:
+ self.file_disposition_class = FileDisposition
+ self.supports_plugins = False
+
+ def __repr__(self):
+ return "<Collector at 0x%x: %s>" % (id(self), self.tracer_name())
+
+ def use_data(self, covdata, context):
+ """Use `covdata` for recording data."""
+ self.covdata = covdata
+ self.static_context = context
+ self.covdata.set_context(self.static_context)
+
+ def tracer_name(self):
+ """Return the class name of the tracer we're using."""
+ return self._trace_class.__name__
+
+ def _clear_data(self):
+ """Clear out existing data, but stay ready for more collection."""
+ # We used to used self.data.clear(), but that would remove filename
+ # keys and data values that were still in use higher up the stack
+ # when we are called as part of switch_context.
+ for d in self.data.values():
+ d.clear()
+
+ for tracer in self.tracers:
+ tracer.reset_activity()
+
+ def reset(self):
+ """Clear collected data, and prepare to collect more."""
+ # A dictionary mapping file names to dicts with line number keys (if not
+ # branch coverage), or mapping file names to dicts with line number
+ # pairs as keys (if branch coverage).
+ self.data = {}
+
+ # A dictionary mapping file names to file tracer plugin names that will
+ # handle them.
+ self.file_tracers = {}
+
+ self.disabled_plugins = set()
+
+ # The .should_trace_cache attribute is a cache from file names to
+ # coverage.FileDisposition objects, or None. When a file is first
+ # considered for tracing, a FileDisposition is obtained from
+ # Coverage.should_trace. Its .trace attribute indicates whether the
+ # file should be traced or not. If it should be, a plugin with dynamic
+ # file names can decide not to trace it based on the dynamic file name
+ # being excluded by the inclusion rules, in which case the
+ # FileDisposition will be replaced by None in the cache.
+ if env.PYPY:
+ import __pypy__ # pylint: disable=import-error
+ # Alex Gaynor said:
+ # should_trace_cache is a strictly growing key: once a key is in
+ # it, it never changes. Further, the keys used to access it are
+ # generally constant, given sufficient context. That is to say, at
+ # any given point _trace() is called, pypy is able to know the key.
+ # This is because the key is determined by the physical source code
+ # line, and that's invariant with the call site.
+ #
+ # This property of a dict with immutable keys, combined with
+ # call-site-constant keys is a match for PyPy's module dict,
+ # which is optimized for such workloads.
+ #
+ # This gives a 20% benefit on the workload described at
+ # https://bitbucket.org/pypy/pypy/issue/1871/10x-slower-than-cpython-under-coverage
+ self.should_trace_cache = __pypy__.newdict("module")
+ else:
+ self.should_trace_cache = {}
+
+ # Our active Tracers.
+ self.tracers = []
+
+ self._clear_data()
+
+ def _start_tracer(self):
+ """Start a new Tracer object, and store it in self.tracers."""
+ tracer = self._trace_class()
+ tracer.data = self.data
+ tracer.trace_arcs = self.branch
+ tracer.should_trace = self.should_trace
+ tracer.should_trace_cache = self.should_trace_cache
+ tracer.warn = self.warn
+
+ if hasattr(tracer, 'concur_id_func'):
+ tracer.concur_id_func = self.concur_id_func
+ elif self.concur_id_func:
+ raise CoverageException(
+ "Can't support concurrency=%s with %s, only threads are supported" % (
+ self.concurrency, self.tracer_name(),
+ )
+ )
+
+ if hasattr(tracer, 'file_tracers'):
+ tracer.file_tracers = self.file_tracers
+ if hasattr(tracer, 'threading'):
+ tracer.threading = self.threading
+ if hasattr(tracer, 'check_include'):
+ tracer.check_include = self.check_include
+ if hasattr(tracer, 'should_start_context'):
+ tracer.should_start_context = self.should_start_context
+ tracer.switch_context = self.switch_context
+ if hasattr(tracer, 'disable_plugin'):
+ tracer.disable_plugin = self.disable_plugin
+
+ fn = tracer.start()
+ self.tracers.append(tracer)
+
+ return fn
+
+ # The trace function has to be set individually on each thread before
+ # execution begins. Ironically, the only support the threading module has
+ # for running code before the thread main is the tracing function. So we
+ # install this as a trace function, and the first time it's called, it does
+ # the real trace installation.
+
+ def _installation_trace(self, frame, event, arg):
+ """Called on new threads, installs the real tracer."""
+ # Remove ourselves as the trace function.
+ sys.settrace(None)
+ # Install the real tracer.
+ fn = self._start_tracer()
+ # Invoke the real trace function with the current event, to be sure
+ # not to lose an event.
+ if fn:
+ fn = fn(frame, event, arg)
+ # Return the new trace function to continue tracing in this scope.
+ return fn
+
+ def start(self):
+ """Start collecting trace information."""
+ if self._collectors:
+ self._collectors[-1].pause()
+
+ self.tracers = []
+
+ # Check to see whether we had a fullcoverage tracer installed. If so,
+ # get the stack frames it stashed away for us.
+ traces0 = []
+ fn0 = sys.gettrace()
+ if fn0:
+ tracer0 = getattr(fn0, '__self__', None)
+ if tracer0:
+ traces0 = getattr(tracer0, 'traces', [])
+
+ try:
+ # Install the tracer on this thread.
+ fn = self._start_tracer()
+ except:
+ if self._collectors:
+ self._collectors[-1].resume()
+ raise
+
+ # If _start_tracer succeeded, then we add ourselves to the global
+ # stack of collectors.
+ self._collectors.append(self)
+
+ # Replay all the events from fullcoverage into the new trace function.
+ for args in traces0:
+ (frame, event, arg), lineno = args
+ try:
+ fn(frame, event, arg, lineno=lineno)
+ except TypeError:
+ raise Exception("fullcoverage must be run with the C trace function.")
+
+ # Install our installation tracer in threading, to jump-start other
+ # threads.
+ if self.threading:
+ self.threading.settrace(self._installation_trace)
+
+ def stop(self):
+ """Stop collecting trace information."""
+ assert self._collectors
+ if self._collectors[-1] is not self:
+ print("self._collectors:")
+ for c in self._collectors:
+ print(" {!r}\n{}".format(c, c.origin))
+ assert self._collectors[-1] is self, (
+ "Expected current collector to be %r, but it's %r" % (self, self._collectors[-1])
+ )
+
+ self.pause()
+
+ # Remove this Collector from the stack, and resume the one underneath
+ # (if any).
+ self._collectors.pop()
+ if self._collectors:
+ self._collectors[-1].resume()
+
+ def pause(self):
+ """Pause tracing, but be prepared to `resume`."""
+ for tracer in self.tracers:
+ tracer.stop()
+ stats = tracer.get_stats()
+ if stats:
+ print("\nCoverage.py tracer stats:")
+ for k in sorted(stats.keys()):
+ print("%20s: %s" % (k, stats[k]))
+ if self.threading:
+ self.threading.settrace(None)
+
+ def resume(self):
+ """Resume tracing after a `pause`."""
+ for tracer in self.tracers:
+ tracer.start()
+ if self.threading:
+ self.threading.settrace(self._installation_trace)
+ else:
+ self._start_tracer()
+
+ def _activity(self):
+ """Has any activity been traced?
+
+ Returns a boolean, True if any trace function was invoked.
+
+ """
+ return any(tracer.activity() for tracer in self.tracers)
+
+ def switch_context(self, new_context):
+ """Switch to a new dynamic context."""
+ self.flush_data()
+ if self.static_context:
+ context = self.static_context
+ if new_context:
+ context += "|" + new_context
+ else:
+ context = new_context
+ self.covdata.set_context(context)
+
+ def disable_plugin(self, disposition):
+ """Disable the plugin mentioned in `disposition`."""
+ file_tracer = disposition.file_tracer
+ plugin = file_tracer._coverage_plugin
+ plugin_name = plugin._coverage_plugin_name
+ self.warn("Disabling plug-in {!r} due to previous exception".format(plugin_name))
+ plugin._coverage_enabled = False
+ disposition.trace = False
+
+ def cached_mapped_file(self, filename):
+ """A locally cached version of file names mapped through file_mapper."""
+ key = (type(filename), filename)
+ try:
+ return self.mapped_file_cache[key]
+ except KeyError:
+ return self.mapped_file_cache.setdefault(key, self.file_mapper(filename))
+
+ def mapped_file_dict(self, d):
+ """Return a dict like d, but with keys modified by file_mapper."""
+ # The call to litems() ensures that the GIL protects the dictionary
+ # iterator against concurrent modifications by tracers running
+ # in other threads. We try three times in case of concurrent
+ # access, hoping to get a clean copy.
+ runtime_err = None
+ for _ in range(3):
+ try:
+ items = litems(d)
+ except RuntimeError as ex:
+ runtime_err = ex
+ else:
+ break
+ else:
+ raise runtime_err
+
+ if getattr(sys, 'is_standalone_binary', False):
+ # filenames should stay relative to the arcadia root, because files may not exist
+ return dict((k, v) for k, v in items if v)
+
+ return dict((self.cached_mapped_file(k), v) for k, v in items if v)
+
+ def plugin_was_disabled(self, plugin):
+ """Record that `plugin` was disabled during the run."""
+ self.disabled_plugins.add(plugin._coverage_plugin_name)
+
+ def flush_data(self):
+ """Save the collected data to our associated `CoverageData`.
+
+ Data may have also been saved along the way. This forces the
+ last of the data to be saved.
+
+ Returns True if there was data to save, False if not.
+ """
+ if not self._activity():
+ return False
+
+ if self.branch:
+ self.covdata.add_arcs(self.mapped_file_dict(self.data))
+ else:
+ self.covdata.add_lines(self.mapped_file_dict(self.data))
+
+ file_tracers = {
+ k: v for k, v in self.file_tracers.items()
+ if v not in self.disabled_plugins
+ }
+ self.covdata.add_file_tracers(self.mapped_file_dict(file_tracers))
+
+ self._clear_data()
+ return True
diff --git a/contrib/python/coverage/py2/coverage/config.py b/contrib/python/coverage/py2/coverage/config.py
new file mode 100644
index 0000000000..ceb7201b65
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/config.py
@@ -0,0 +1,605 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Config file for coverage.py"""
+
+import collections
+import copy
+import os
+import os.path
+import re
+import sys
+
+from coverage import env
+from coverage.backward import configparser, iitems, string_class
+from coverage.misc import contract, CoverageException, isolate_module
+from coverage.misc import substitute_variables
+
+from coverage.tomlconfig import TomlConfigParser, TomlDecodeError
+
+os = isolate_module(os)
+
+
+class HandyConfigParser(configparser.RawConfigParser):
+ """Our specialization of ConfigParser."""
+
+ def __init__(self, our_file):
+ """Create the HandyConfigParser.
+
+ `our_file` is True if this config file is specifically for coverage,
+ False if we are examining another config file (tox.ini, setup.cfg)
+ for possible settings.
+ """
+
+ configparser.RawConfigParser.__init__(self)
+ self.section_prefixes = ["coverage:"]
+ if our_file:
+ self.section_prefixes.append("")
+
+ def read(self, filenames, encoding=None):
+ """Read a file name as UTF-8 configuration data."""
+ kwargs = {}
+ if env.PYVERSION >= (3, 2):
+ kwargs['encoding'] = encoding or "utf-8"
+ return configparser.RawConfigParser.read(self, filenames, **kwargs)
+
+ def has_option(self, section, option):
+ for section_prefix in self.section_prefixes:
+ real_section = section_prefix + section
+ has = configparser.RawConfigParser.has_option(self, real_section, option)
+ if has:
+ return has
+ return False
+
+ def has_section(self, section):
+ for section_prefix in self.section_prefixes:
+ real_section = section_prefix + section
+ has = configparser.RawConfigParser.has_section(self, real_section)
+ if has:
+ return real_section
+ return False
+
+ def options(self, section):
+ for section_prefix in self.section_prefixes:
+ real_section = section_prefix + section
+ if configparser.RawConfigParser.has_section(self, real_section):
+ return configparser.RawConfigParser.options(self, real_section)
+ raise configparser.NoSectionError(section)
+
+ def get_section(self, section):
+ """Get the contents of a section, as a dictionary."""
+ d = {}
+ for opt in self.options(section):
+ d[opt] = self.get(section, opt)
+ return d
+
+ def get(self, section, option, *args, **kwargs):
+ """Get a value, replacing environment variables also.
+
+ The arguments are the same as `RawConfigParser.get`, but in the found
+ value, ``$WORD`` or ``${WORD}`` are replaced by the value of the
+ environment variable ``WORD``.
+
+ Returns the finished value.
+
+ """
+ for section_prefix in self.section_prefixes:
+ real_section = section_prefix + section
+ if configparser.RawConfigParser.has_option(self, real_section, option):
+ break
+ else:
+ raise configparser.NoOptionError(option, section)
+
+ v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs)
+ v = substitute_variables(v, os.environ)
+ return v
+
+ def getlist(self, section, option):
+ """Read a list of strings.
+
+ The value of `section` and `option` is treated as a comma- and newline-
+ separated list of strings. Each value is stripped of whitespace.
+
+ Returns the list of strings.
+
+ """
+ value_list = self.get(section, option)
+ values = []
+ for value_line in value_list.split('\n'):
+ for value in value_line.split(','):
+ value = value.strip()
+ if value:
+ values.append(value)
+ return values
+
+ def getregexlist(self, section, option):
+ """Read a list of full-line regexes.
+
+ The value of `section` and `option` is treated as a newline-separated
+ list of regexes. Each value is stripped of whitespace.
+
+ Returns the list of strings.
+
+ """
+ line_list = self.get(section, option)
+ value_list = []
+ for value in line_list.splitlines():
+ value = value.strip()
+ try:
+ re.compile(value)
+ except re.error as e:
+ raise CoverageException(
+ "Invalid [%s].%s value %r: %s" % (section, option, value, e)
+ )
+ if value:
+ value_list.append(value)
+ return value_list
+
+
+# The default line exclusion regexes.
+DEFAULT_EXCLUDE = [
+ r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)',
+]
+
+# The default partial branch regexes, to be modified by the user.
+DEFAULT_PARTIAL = [
+ r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)',
+]
+
+# The default partial branch regexes, based on Python semantics.
+# These are any Python branching constructs that can't actually execute all
+# their branches.
+DEFAULT_PARTIAL_ALWAYS = [
+ 'while (True|1|False|0):',
+ 'if (True|1|False|0):',
+]
+
+
+class CoverageConfig(object):
+ """Coverage.py configuration.
+
+ The attributes of this class are the various settings that control the
+ operation of coverage.py.
+
+ """
+ # pylint: disable=too-many-instance-attributes
+
+ def __init__(self):
+ """Initialize the configuration attributes to their defaults."""
+ # Metadata about the config.
+ # We tried to read these config files.
+ self.attempted_config_files = []
+ # We did read these config files, but maybe didn't find any content for us.
+ self.config_files_read = []
+ # The file that gave us our configuration.
+ self.config_file = None
+ self._config_contents = None
+
+ # Defaults for [run] and [report]
+ self._include = None
+ self._omit = None
+
+ # Defaults for [run]
+ self.branch = False
+ self.command_line = None
+ self.concurrency = None
+ self.context = None
+ self.cover_pylib = False
+ self.data_file = ".coverage"
+ self.debug = []
+ self.disable_warnings = []
+ self.dynamic_context = None
+ self.note = None
+ self.parallel = False
+ self.plugins = []
+ self.relative_files = False
+ self.run_include = None
+ self.run_omit = None
+ self.source = None
+ self.source_pkgs = []
+ self.timid = False
+ self._crash = None
+
+ # Defaults for [report]
+ self.exclude_list = DEFAULT_EXCLUDE[:]
+ self.fail_under = 0.0
+ self.ignore_errors = False
+ self.report_include = None
+ self.report_omit = None
+ self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
+ self.partial_list = DEFAULT_PARTIAL[:]
+ self.precision = 0
+ self.report_contexts = None
+ self.show_missing = False
+ self.skip_covered = False
+ self.skip_empty = False
+ self.sort = None
+
+ # Defaults for [html]
+ self.extra_css = None
+ self.html_dir = "htmlcov"
+ self.html_skip_covered = None
+ self.html_skip_empty = None
+ self.html_title = "Coverage report"
+ self.show_contexts = False
+
+ # Defaults for [xml]
+ self.xml_output = "coverage.xml"
+ self.xml_package_depth = 99
+
+ # Defaults for [json]
+ self.json_output = "coverage.json"
+ self.json_pretty_print = False
+ self.json_show_contexts = False
+
+ # Defaults for [paths]
+ self.paths = collections.OrderedDict()
+
+ # Options for plugins
+ self.plugin_options = {}
+ self.suppress_plugin_errors = True
+
+ MUST_BE_LIST = [
+ "debug", "concurrency", "plugins",
+ "report_omit", "report_include",
+ "run_omit", "run_include",
+ ]
+
+ def from_args(self, **kwargs):
+ """Read config values from `kwargs`."""
+ for k, v in iitems(kwargs):
+ if v is not None:
+ if k in self.MUST_BE_LIST and isinstance(v, string_class):
+ v = [v]
+ setattr(self, k, v)
+
+ def from_resource(self, resource_name):
+ assert getattr(sys, 'is_standalone_binary', False), 'You have used method by mistake in script, not binary'
+ cp, self._config_contents = _load_config_from_resource(resource_name)
+ return self._parse_config(cp, resource_name, True)
+
+ @contract(filename=str)
+ def from_file(self, filename, our_file):
+ """Read configuration from a .rc file.
+
+ `filename` is a file name to read.
+
+ `our_file` is True if this config file is specifically for coverage,
+ False if we are examining another config file (tox.ini, setup.cfg)
+ for possible settings.
+
+ Returns True or False, whether the file could be read, and it had some
+ coverage.py settings in it.
+
+ """
+ _, ext = os.path.splitext(filename)
+ if ext == '.toml':
+ cp = TomlConfigParser(our_file)
+ else:
+ cp = HandyConfigParser(our_file)
+
+ self.attempted_config_files.append(filename)
+
+ try:
+ files_read = cp.read(filename)
+ except (configparser.Error, TomlDecodeError) as err:
+ raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
+ if not files_read:
+ return False
+
+ self.config_files_read.extend(map(os.path.abspath, files_read))
+
+ return self._parse_config(cp, filename, our_file)
+
+ def _parse_config(self, cp, filename, our_file):
+ any_set = False
+ try:
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ was_set = self._set_attr_from_config_option(cp, *option_spec)
+ if was_set:
+ any_set = True
+ except ValueError as err:
+ raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
+
+ # Check that there are no unrecognized options.
+ all_options = collections.defaultdict(set)
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ section, option = option_spec[1].split(":")
+ all_options[section].add(option)
+
+ for section, options in iitems(all_options):
+ real_section = cp.has_section(section)
+ if real_section:
+ for unknown in set(cp.options(section)) - options:
+ raise CoverageException(
+ "Unrecognized option '[%s] %s=' in config file %s" % (
+ real_section, unknown, filename
+ )
+ )
+
+ # [paths] is special
+ if cp.has_section('paths'):
+ for option in cp.options('paths'):
+ self.paths[option] = cp.getlist('paths', option)
+ any_set = True
+
+ # plugins can have options
+ for plugin in self.plugins:
+ if cp.has_section(plugin):
+ self.plugin_options[plugin] = cp.get_section(plugin)
+ any_set = True
+
+ # Was this file used as a config file? If it's specifically our file,
+ # then it was used. If we're piggybacking on someone else's file,
+ # then it was only used if we found some settings in it.
+ if our_file:
+ used = True
+ else:
+ used = any_set
+
+ if used:
+ self.config_file = os.path.abspath(filename)
+ if not getattr(sys, 'is_standalone_binary', False):
+ with open(filename, "rb") as f:
+ self._config_contents = f.read()
+
+ return used
+
+ def copy(self):
+ """Return a copy of the configuration."""
+ return copy.deepcopy(self)
+
+ CONFIG_FILE_OPTIONS = [
+ # These are *args for _set_attr_from_config_option:
+ # (attr, where, type_="")
+ #
+ # attr is the attribute to set on the CoverageConfig object.
+ # where is the section:name to read from the configuration file.
+ # type_ is the optional type to apply, by using .getTYPE to read the
+ # configuration value from the file.
+
+ # [run]
+ ('branch', 'run:branch', 'boolean'),
+ ('command_line', 'run:command_line'),
+ ('concurrency', 'run:concurrency', 'list'),
+ ('context', 'run:context'),
+ ('cover_pylib', 'run:cover_pylib', 'boolean'),
+ ('data_file', 'run:data_file'),
+ ('debug', 'run:debug', 'list'),
+ ('disable_warnings', 'run:disable_warnings', 'list'),
+ ('dynamic_context', 'run:dynamic_context'),
+ ('note', 'run:note'),
+ ('parallel', 'run:parallel', 'boolean'),
+ ('plugins', 'run:plugins', 'list'),
+ ('relative_files', 'run:relative_files', 'boolean'),
+ ('run_include', 'run:include', 'list'),
+ ('run_omit', 'run:omit', 'list'),
+ ('source', 'run:source', 'list'),
+ ('source_pkgs', 'run:source_pkgs', 'list'),
+ ('timid', 'run:timid', 'boolean'),
+ ('_crash', 'run:_crash'),
+ ('suppress_plugin_errors', 'run:suppress_plugin_errors', 'boolean'),
+
+ # [report]
+ ('exclude_list', 'report:exclude_lines', 'regexlist'),
+ ('fail_under', 'report:fail_under', 'float'),
+ ('ignore_errors', 'report:ignore_errors', 'boolean'),
+ ('partial_always_list', 'report:partial_branches_always', 'regexlist'),
+ ('partial_list', 'report:partial_branches', 'regexlist'),
+ ('precision', 'report:precision', 'int'),
+ ('report_contexts', 'report:contexts', 'list'),
+ ('report_include', 'report:include', 'list'),
+ ('report_omit', 'report:omit', 'list'),
+ ('show_missing', 'report:show_missing', 'boolean'),
+ ('skip_covered', 'report:skip_covered', 'boolean'),
+ ('skip_empty', 'report:skip_empty', 'boolean'),
+ ('sort', 'report:sort'),
+
+ # [html]
+ ('extra_css', 'html:extra_css'),
+ ('html_dir', 'html:directory'),
+ ('html_skip_covered', 'html:skip_covered', 'boolean'),
+ ('html_skip_empty', 'html:skip_empty', 'boolean'),
+ ('html_title', 'html:title'),
+ ('show_contexts', 'html:show_contexts', 'boolean'),
+
+ # [xml]
+ ('xml_output', 'xml:output'),
+ ('xml_package_depth', 'xml:package_depth', 'int'),
+
+ # [json]
+ ('json_output', 'json:output'),
+ ('json_pretty_print', 'json:pretty_print', 'boolean'),
+ ('json_show_contexts', 'json:show_contexts', 'boolean'),
+ ]
+
+ def _set_attr_from_config_option(self, cp, attr, where, type_=''):
+ """Set an attribute on self if it exists in the ConfigParser.
+
+ Returns True if the attribute was set.
+
+ """
+ section, option = where.split(":")
+ if cp.has_option(section, option):
+ method = getattr(cp, 'get' + type_)
+ setattr(self, attr, method(section, option))
+ return True
+ return False
+
+ def get_plugin_options(self, plugin):
+ """Get a dictionary of options for the plugin named `plugin`."""
+ return self.plugin_options.get(plugin, {})
+
+ def set_option(self, option_name, value):
+ """Set an option in the configuration.
+
+ `option_name` is a colon-separated string indicating the section and
+ option name. For example, the ``branch`` option in the ``[run]``
+ section of the config file would be indicated with `"run:branch"`.
+
+ `value` is the new value for the option.
+
+ """
+ # Special-cased options.
+ if option_name == "paths":
+ self.paths = value
+ return
+
+ # Check all the hard-coded options.
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ attr, where = option_spec[:2]
+ if where == option_name:
+ setattr(self, attr, value)
+ return
+
+ # See if it's a plugin option.
+ plugin_name, _, key = option_name.partition(":")
+ if key and plugin_name in self.plugins:
+ self.plugin_options.setdefault(plugin_name, {})[key] = value
+ return
+
+ # If we get here, we didn't find the option.
+ raise CoverageException("No such option: %r" % option_name)
+
+ def get_option(self, option_name):
+ """Get an option from the configuration.
+
+ `option_name` is a colon-separated string indicating the section and
+ option name. For example, the ``branch`` option in the ``[run]``
+ section of the config file would be indicated with `"run:branch"`.
+
+ Returns the value of the option.
+
+ """
+ # Special-cased options.
+ if option_name == "paths":
+ return self.paths
+
+ # Check all the hard-coded options.
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ attr, where = option_spec[:2]
+ if where == option_name:
+ return getattr(self, attr)
+
+ # See if it's a plugin option.
+ plugin_name, _, key = option_name.partition(":")
+ if key and plugin_name in self.plugins:
+ return self.plugin_options.get(plugin_name, {}).get(key)
+
+ # If we get here, we didn't find the option.
+ raise CoverageException("No such option: %r" % option_name)
+
+ def post_process_file(self, path):
+ """Make final adjustments to a file path to make it usable."""
+ return os.path.expanduser(path)
+
+ def post_process(self):
+ """Make final adjustments to settings to make them usable."""
+ self.data_file = self.post_process_file(self.data_file)
+ self.html_dir = self.post_process_file(self.html_dir)
+ self.xml_output = self.post_process_file(self.xml_output)
+ self.paths = collections.OrderedDict(
+ (k, [self.post_process_file(f) for f in v])
+ for k, v in self.paths.items()
+ )
+
+
+def config_files_to_try(config_file):
+ """What config files should we try to read?
+
+ Returns a list of tuples:
+ (filename, is_our_file, was_file_specified)
+ """
+
+ # Some API users were specifying ".coveragerc" to mean the same as
+ # True, so make it so.
+ if config_file == ".coveragerc":
+ config_file = True
+ specified_file = (config_file is not True)
+ if not specified_file:
+ # No file was specified. Check COVERAGE_RCFILE.
+ config_file = os.environ.get('COVERAGE_RCFILE')
+ if config_file:
+ specified_file = True
+ if not specified_file:
+ # Still no file specified. Default to .coveragerc
+ config_file = ".coveragerc"
+ files_to_try = [
+ (config_file, True, specified_file),
+ ("setup.cfg", False, False),
+ ("tox.ini", False, False),
+ ("pyproject.toml", False, False),
+ ]
+ return files_to_try
+
+
+def read_coverage_config(config_file, **kwargs):
+ """Read the coverage.py configuration.
+
+ Arguments:
+ config_file: a boolean or string, see the `Coverage` class for the
+ tricky details.
+ all others: keyword arguments from the `Coverage` class, used for
+ setting values in the configuration.
+
+ Returns:
+ config:
+ config is a CoverageConfig object read from the appropriate
+ configuration file.
+
+ """
+ # Build the configuration from a number of sources:
+ # 1) defaults:
+ config = CoverageConfig()
+
+ # 1.1 built-in config
+ if getattr(sys, 'is_standalone_binary', False):
+ config.from_resource("/coverage_plugins/coveragerc.txt")
+
+ # 2) from a file:
+ if config_file:
+ files_to_try = config_files_to_try(config_file)
+
+ for fname, our_file, specified_file in files_to_try:
+ if getattr(sys, 'is_standalone_binary', False) and fname == "/coverage_plugins/coveragerc.txt":
+ continue
+ config_read = config.from_file(fname, our_file=our_file)
+ if config_read:
+ break
+ if specified_file:
+ raise CoverageException("Couldn't read '%s' as a config file" % fname)
+
+ # $set_env.py: COVERAGE_DEBUG - Options for --debug.
+ # 3) from environment variables:
+ env_data_file = os.environ.get('COVERAGE_FILE')
+ if env_data_file:
+ config.data_file = env_data_file
+ debugs = os.environ.get('COVERAGE_DEBUG')
+ if debugs:
+ config.debug.extend(d.strip() for d in debugs.split(","))
+
+ # 4) from constructor arguments:
+ config.from_args(**kwargs)
+
+ # Once all the config has been collected, there's a little post-processing
+ # to do.
+ config.post_process()
+
+ return config
+
+
+def _load_config_from_resource(resource_name):
+ from io import StringIO
+ from library.python import resource
+
+ config_data = resource.find(resource_name)
+ if config_data is None:
+ raise IOError("No such resource: " + resource_name)
+
+ config_data = config_data.decode('utf-8')
+ cp = HandyConfigParser(True)
+ try:
+ cp.readfp(StringIO(config_data))
+ except configparser.Error as err:
+ raise CoverageException("Couldn't read config %s: %s" % (resource_name, err))
+ return cp, config_data
diff --git a/contrib/python/coverage/py2/coverage/context.py b/contrib/python/coverage/py2/coverage/context.py
new file mode 100644
index 0000000000..ea13da21ed
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/context.py
@@ -0,0 +1,91 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Determine contexts for coverage.py"""
+
+
+def combine_context_switchers(context_switchers):
+ """Create a single context switcher from multiple switchers.
+
+ `context_switchers` is a list of functions that take a frame as an
+ argument and return a string to use as the new context label.
+
+ Returns a function that composites `context_switchers` functions, or None
+ if `context_switchers` is an empty list.
+
+ When invoked, the combined switcher calls `context_switchers` one-by-one
+ until a string is returned. The combined switcher returns None if all
+ `context_switchers` return None.
+ """
+ if not context_switchers:
+ return None
+
+ if len(context_switchers) == 1:
+ return context_switchers[0]
+
+ def should_start_context(frame):
+ """The combiner for multiple context switchers."""
+ for switcher in context_switchers:
+ new_context = switcher(frame)
+ if new_context is not None:
+ return new_context
+ return None
+
+ return should_start_context
+
+
+def should_start_context_test_function(frame):
+ """Is this frame calling a test_* function?"""
+ co_name = frame.f_code.co_name
+ if co_name.startswith("test") or co_name == "runTest":
+ return qualname_from_frame(frame)
+ return None
+
+
+def qualname_from_frame(frame):
+ """Get a qualified name for the code running in `frame`."""
+ co = frame.f_code
+ fname = co.co_name
+ method = None
+ if co.co_argcount and co.co_varnames[0] == "self":
+ self = frame.f_locals["self"]
+ method = getattr(self, fname, None)
+
+ if method is None:
+ func = frame.f_globals.get(fname)
+ if func is None:
+ return None
+ return func.__module__ + '.' + fname
+
+ func = getattr(method, '__func__', None)
+ if func is None:
+ cls = self.__class__
+ return cls.__module__ + '.' + cls.__name__ + "." + fname
+
+ if hasattr(func, '__qualname__'):
+ qname = func.__module__ + '.' + func.__qualname__
+ else:
+ for cls in getattr(self.__class__, '__mro__', ()):
+ f = cls.__dict__.get(fname, None)
+ if f is None:
+ continue
+ if f is func:
+ qname = cls.__module__ + '.' + cls.__name__ + "." + fname
+ break
+ else:
+ # Support for old-style classes.
+ def mro(bases):
+ for base in bases:
+ f = base.__dict__.get(fname, None)
+ if f is func:
+ return base.__module__ + '.' + base.__name__ + "." + fname
+ for base in bases:
+ qname = mro(base.__bases__)
+ if qname is not None:
+ return qname
+ return None
+ qname = mro([self.__class__])
+ if qname is None:
+ qname = func.__module__ + '.' + fname
+
+ return qname
diff --git a/contrib/python/coverage/py2/coverage/control.py b/contrib/python/coverage/py2/coverage/control.py
new file mode 100644
index 0000000000..605b50c26b
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/control.py
@@ -0,0 +1,1162 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Core control stuff for coverage.py."""
+
+import atexit
+import collections
+import contextlib
+import os
+import os.path
+import platform
+import sys
+import time
+import json
+
+from coverage import env
+from coverage.annotate import AnnotateReporter
+from coverage.backward import string_class, iitems
+from coverage.collector import Collector, CTracer
+from coverage.config import read_coverage_config
+from coverage.context import should_start_context_test_function, combine_context_switchers
+from coverage.data import CoverageData, combine_parallel_data
+from coverage.debug import DebugControl, short_stack, write_formatted_info
+from coverage.disposition import disposition_debug_msg
+from coverage.files import PathAliases, abs_file, canonical_filename, relative_filename, set_relative_directory
+from coverage.html import HtmlReporter
+from coverage.inorout import InOrOut
+from coverage.jsonreport import JsonReporter
+from coverage.misc import CoverageException, bool_or_none, join_regex
+from coverage.misc import DefaultValue, ensure_dir_for_file, isolate_module
+from coverage.plugin import FileReporter
+from coverage.plugin_support import Plugins
+from coverage.python import PythonFileReporter
+from coverage.report import render_report
+from coverage.results import Analysis, Numbers
+from coverage.summary import SummaryReporter
+from coverage.xmlreport import XmlReporter
+
+try:
+ from coverage.multiproc import patch_multiprocessing
+except ImportError: # pragma: only jython
+ # Jython has no multiprocessing module.
+ patch_multiprocessing = None
+
+os = isolate_module(os)
+
+@contextlib.contextmanager
+def override_config(cov, **kwargs):
+ """Temporarily tweak the configuration of `cov`.
+
+ The arguments are applied to `cov.config` with the `from_args` method.
+ At the end of the with-statement, the old configuration is restored.
+ """
+ original_config = cov.config
+ cov.config = cov.config.copy()
+ try:
+ cov.config.from_args(**kwargs)
+ yield
+ finally:
+ cov.config = original_config
+
+
+_DEFAULT_DATAFILE = DefaultValue("MISSING")
+
+class Coverage(object):
+ """Programmatic access to coverage.py.
+
+ To use::
+
+ from coverage import Coverage
+
+ cov = Coverage()
+ cov.start()
+ #.. call your code ..
+ cov.stop()
+ cov.html_report(directory='covhtml')
+
+ Note: in keeping with Python custom, names starting with underscore are
+ not part of the public API. They might stop working at any point. Please
+ limit yourself to documented methods to avoid problems.
+
+ """
+
+ # The stack of started Coverage instances.
+ _instances = []
+
+ @classmethod
+ def current(cls):
+ """Get the latest started `Coverage` instance, if any.
+
+ Returns: a `Coverage` instance, or None.
+
+ .. versionadded:: 5.0
+
+ """
+ if cls._instances:
+ return cls._instances[-1]
+ else:
+ return None
+
+ def __init__(
+ self, data_file=_DEFAULT_DATAFILE, data_suffix=None, cover_pylib=None,
+ auto_data=False, timid=None, branch=None, config_file=True,
+ source=None, source_pkgs=None, omit=None, include=None, debug=None,
+ concurrency=None, check_preimported=False, context=None,
+ ): # pylint: disable=too-many-arguments
+ """
+ Many of these arguments duplicate and override values that can be
+ provided in a configuration file. Parameters that are missing here
+ will use values from the config file.
+
+ `data_file` is the base name of the data file to use. The config value
+ defaults to ".coverage". None can be provided to prevent writing a data
+ file. `data_suffix` is appended (with a dot) to `data_file` to create
+ the final file name. If `data_suffix` is simply True, then a suffix is
+ created with the machine and process identity included.
+
+ `cover_pylib` is a boolean determining whether Python code installed
+ with the Python interpreter is measured. This includes the Python
+ standard library and any packages installed with the interpreter.
+
+ If `auto_data` is true, then any existing data file will be read when
+ coverage measurement starts, and data will be saved automatically when
+ measurement stops.
+
+ If `timid` is true, then a slower and simpler trace function will be
+ used. This is important for some environments where manipulation of
+ tracing functions breaks the faster trace function.
+
+ If `branch` is true, then branch coverage will be measured in addition
+ to the usual statement coverage.
+
+ `config_file` determines what configuration file to read:
+
+ * If it is ".coveragerc", it is interpreted as if it were True,
+ for backward compatibility.
+
+ * If it is a string, it is the name of the file to read. If the
+ file can't be read, it is an error.
+
+ * If it is True, then a few standard files names are tried
+ (".coveragerc", "setup.cfg", "tox.ini"). It is not an error for
+ these files to not be found.
+
+ * If it is False, then no configuration file is read.
+
+ `source` is a list of file paths or package names. Only code located
+ in the trees indicated by the file paths or package names will be
+ measured.
+
+ `source_pkgs` is a list of package names. It works the same as
+ `source`, but can be used to name packages where the name can also be
+ interpreted as a file path.
+
+ `include` and `omit` are lists of file name patterns. Files that match
+ `include` will be measured, files that match `omit` will not. Each
+ will also accept a single string argument.
+
+ `debug` is a list of strings indicating what debugging information is
+ desired.
+
+ `concurrency` is a string indicating the concurrency library being used
+ in the measured code. Without this, coverage.py will get incorrect
+ results if these libraries are in use. Valid strings are "greenlet",
+ "eventlet", "gevent", "multiprocessing", or "thread" (the default).
+ This can also be a list of these strings.
+
+ If `check_preimported` is true, then when coverage is started, the
+ already-imported files will be checked to see if they should be
+ measured by coverage. Importing measured files before coverage is
+ started can mean that code is missed.
+
+ `context` is a string to use as the :ref:`static context
+ <static_contexts>` label for collected data.
+
+ .. versionadded:: 4.0
+ The `concurrency` parameter.
+
+ .. versionadded:: 4.2
+ The `concurrency` parameter can now be a list of strings.
+
+ .. versionadded:: 5.0
+ The `check_preimported` and `context` parameters.
+
+ .. versionadded:: 5.3
+ The `source_pkgs` parameter.
+
+ """
+ # data_file=None means no disk file at all. data_file missing means
+ # use the value from the config file.
+ self._no_disk = data_file is None
+ if data_file is _DEFAULT_DATAFILE:
+ data_file = None
+
+ # Build our configuration from a number of sources.
+ self.config = read_coverage_config(
+ config_file=config_file,
+ data_file=data_file, cover_pylib=cover_pylib, timid=timid,
+ branch=branch, parallel=bool_or_none(data_suffix),
+ source=source, source_pkgs=source_pkgs, run_omit=omit, run_include=include, debug=debug,
+ report_omit=omit, report_include=include,
+ concurrency=concurrency, context=context,
+ )
+
+ # This is injectable by tests.
+ self._debug_file = None
+
+ self._auto_load = self._auto_save = auto_data
+ self._data_suffix_specified = data_suffix
+
+ # Is it ok for no data to be collected?
+ self._warn_no_data = True
+ self._warn_unimported_source = True
+ self._warn_preimported_source = check_preimported
+ self._no_warn_slugs = None
+
+ # A record of all the warnings that have been issued.
+ self._warnings = []
+
+ # Other instance attributes, set later.
+ self._data = self._collector = None
+ self._plugins = None
+ self._inorout = None
+ self._data_suffix = self._run_suffix = None
+ self._exclude_re = None
+ self._debug = None
+ self._file_mapper = None
+
+ # State machine variables:
+ # Have we initialized everything?
+ self._inited = False
+ self._inited_for_start = False
+ # Have we started collecting and not stopped it?
+ self._started = False
+ # Should we write the debug output?
+ self._should_write_debug = True
+
+ # If we have sub-process measurement happening automatically, then we
+ # want any explicit creation of a Coverage object to mean, this process
+ # is already coverage-aware, so don't auto-measure it. By now, the
+ # auto-creation of a Coverage object has already happened. But we can
+ # find it and tell it not to save its data.
+ if not env.METACOV:
+ _prevent_sub_process_measurement()
+
+ # Store constructor args to reproduce Coverage object in a subprocess created via multiprocessing.Process
+ self._dumped_args = json.dumps(dict(
+ data_file=data_file, data_suffix=data_suffix, cover_pylib=cover_pylib,
+ auto_data=auto_data, timid=timid, branch=branch, config_file=config_file,
+ source=source, omit=omit, include=include, debug=debug,
+ concurrency=concurrency
+ ))
+
+ def _init(self):
+ """Set all the initial state.
+
+ This is called by the public methods to initialize state. This lets us
+ construct a :class:`Coverage` object, then tweak its state before this
+ function is called.
+
+ """
+ if self._inited:
+ return
+
+ self._inited = True
+
+ # Create and configure the debugging controller. COVERAGE_DEBUG_FILE
+ # is an environment variable, the name of a file to append debug logs
+ # to.
+ self._debug = DebugControl(self.config.debug, self._debug_file)
+
+ if "multiprocessing" in (self.config.concurrency or ()):
+ # Multi-processing uses parallel for the subprocesses, so also use
+ # it for the main process.
+ self.config.parallel = True
+
+ # _exclude_re is a dict that maps exclusion list names to compiled regexes.
+ self._exclude_re = {}
+
+ set_relative_directory()
+
+ if getattr(sys, 'is_standalone_binary', False):
+ self._file_mapper = canonical_filename
+ else:
+ self._file_mapper = relative_filename if self.config.relative_files else abs_file
+
+ # Load plugins
+ self._plugins = Plugins.load_plugins(self.config.plugins, self.config, self._debug)
+
+ # Run configuring plugins.
+ for plugin in self._plugins.configurers:
+ # We need an object with set_option and get_option. Either self or
+ # self.config will do. Choosing randomly stops people from doing
+ # other things with those objects, against the public API. Yes,
+ # this is a bit childish. :)
+ plugin.configure([self, self.config][int(time.time()) % 2])
+
+ def _post_init(self):
+ """Stuff to do after everything is initialized."""
+ if self._should_write_debug:
+ self._should_write_debug = False
+ self._write_startup_debug()
+
+ # '[run] _crash' will raise an exception if the value is close by in
+ # the call stack, for testing error handling.
+ if self.config._crash and self.config._crash in short_stack(limit=4):
+ raise Exception("Crashing because called by {}".format(self.config._crash))
+
+ def _write_startup_debug(self):
+ """Write out debug info at startup if needed."""
+ wrote_any = False
+ with self._debug.without_callers():
+ if self._debug.should('config'):
+ config_info = sorted(self.config.__dict__.items())
+ config_info = [(k, v) for k, v in config_info if not k.startswith('_')]
+ write_formatted_info(self._debug, "config", config_info)
+ wrote_any = True
+
+ if self._debug.should('sys'):
+ write_formatted_info(self._debug, "sys", self.sys_info())
+ for plugin in self._plugins:
+ header = "sys: " + plugin._coverage_plugin_name
+ info = plugin.sys_info()
+ write_formatted_info(self._debug, header, info)
+ wrote_any = True
+
+ if wrote_any:
+ write_formatted_info(self._debug, "end", ())
+
+ def _should_trace(self, filename, frame):
+ """Decide whether to trace execution in `filename`.
+
+ Calls `_should_trace_internal`, and returns the FileDisposition.
+
+ """
+ disp = self._inorout.should_trace(filename, frame)
+ if self._debug.should('trace'):
+ self._debug.write(disposition_debug_msg(disp))
+ return disp
+
+ def _check_include_omit_etc(self, filename, frame):
+ """Check a file name against the include/omit/etc, rules, verbosely.
+
+ Returns a boolean: True if the file should be traced, False if not.
+
+ """
+ reason = self._inorout.check_include_omit_etc(filename, frame)
+ if self._debug.should('trace'):
+ if not reason:
+ msg = "Including %r" % (filename,)
+ else:
+ msg = "Not including %r: %s" % (filename, reason)
+ self._debug.write(msg)
+
+ return not reason
+
+ def _warn(self, msg, slug=None, once=False):
+ """Use `msg` as a warning.
+
+ For warning suppression, use `slug` as the shorthand.
+
+ If `once` is true, only show this warning once (determined by the
+ slug.)
+
+ """
+ if self._no_warn_slugs is None:
+ self._no_warn_slugs = list(self.config.disable_warnings)
+
+ if slug in self._no_warn_slugs:
+ # Don't issue the warning
+ return
+
+ self._warnings.append(msg)
+ if slug:
+ msg = "%s (%s)" % (msg, slug)
+ if self._debug.should('pid'):
+ msg = "[%d] %s" % (os.getpid(), msg)
+ sys.stderr.write("Coverage.py warning: %s\n" % msg)
+
+ if once:
+ self._no_warn_slugs.append(slug)
+
+ def get_option(self, option_name):
+ """Get an option from the configuration.
+
+ `option_name` is a colon-separated string indicating the section and
+ option name. For example, the ``branch`` option in the ``[run]``
+ section of the config file would be indicated with `"run:branch"`.
+
+ Returns the value of the option. The type depends on the option
+ selected.
+
+ As a special case, an `option_name` of ``"paths"`` will return an
+ OrderedDict with the entire ``[paths]`` section value.
+
+ .. versionadded:: 4.0
+
+ """
+ return self.config.get_option(option_name)
+
+ def set_option(self, option_name, value):
+ """Set an option in the configuration.
+
+ `option_name` is a colon-separated string indicating the section and
+ option name. For example, the ``branch`` option in the ``[run]``
+ section of the config file would be indicated with ``"run:branch"``.
+
+ `value` is the new value for the option. This should be an
+ appropriate Python value. For example, use True for booleans, not the
+ string ``"True"``.
+
+ As an example, calling::
+
+ cov.set_option("run:branch", True)
+
+ has the same effect as this configuration file::
+
+ [run]
+ branch = True
+
+ As a special case, an `option_name` of ``"paths"`` will replace the
+ entire ``[paths]`` section. The value should be an OrderedDict.
+
+ .. versionadded:: 4.0
+
+ """
+ self.config.set_option(option_name, value)
+
+ def load(self):
+ """Load previously-collected coverage data from the data file."""
+ self._init()
+ if self._collector:
+ self._collector.reset()
+ should_skip = self.config.parallel and not os.path.exists(self.config.data_file)
+ if not should_skip:
+ self._init_data(suffix=None)
+ self._post_init()
+ if not should_skip:
+ self._data.read()
+
+ def _init_for_start(self):
+ """Initialization for start()"""
+ # Construct the collector.
+ concurrency = self.config.concurrency or ()
+ if "multiprocessing" in concurrency:
+ if not patch_multiprocessing:
+ raise CoverageException( # pragma: only jython
+ "multiprocessing is not supported on this Python"
+ )
+ patch_multiprocessing(rcfile=self.config.config_file, coverage_args=self._dumped_args)
+
+ dycon = self.config.dynamic_context
+ if not dycon or dycon == "none":
+ context_switchers = []
+ elif dycon == "test_function":
+ context_switchers = [should_start_context_test_function]
+ else:
+ raise CoverageException(
+ "Don't understand dynamic_context setting: {!r}".format(dycon)
+ )
+
+ context_switchers.extend(
+ plugin.dynamic_context for plugin in self._plugins.context_switchers
+ )
+
+ should_start_context = combine_context_switchers(context_switchers)
+
+ self._collector = Collector(
+ should_trace=self._should_trace,
+ check_include=self._check_include_omit_etc,
+ should_start_context=should_start_context,
+ file_mapper=self._file_mapper,
+ timid=self.config.timid,
+ branch=self.config.branch,
+ warn=self._warn,
+ concurrency=concurrency,
+ )
+
+ suffix = self._data_suffix_specified
+ if suffix or self.config.parallel:
+ if not isinstance(suffix, string_class):
+ # if data_suffix=True, use .machinename.pid.random
+ suffix = True
+ else:
+ suffix = None
+
+ self._init_data(suffix)
+
+ self._collector.use_data(self._data, self.config.context)
+
+ # Early warning if we aren't going to be able to support plugins.
+ if self._plugins.file_tracers and not self._collector.supports_plugins:
+ self._warn(
+ "Plugin file tracers (%s) aren't supported with %s" % (
+ ", ".join(
+ plugin._coverage_plugin_name
+ for plugin in self._plugins.file_tracers
+ ),
+ self._collector.tracer_name(),
+ )
+ )
+ for plugin in self._plugins.file_tracers:
+ plugin._coverage_enabled = False
+
+ # Create the file classifying substructure.
+ self._inorout = InOrOut(
+ warn=self._warn,
+ debug=(self._debug if self._debug.should('trace') else None),
+ )
+ self._inorout.configure(self.config)
+ self._inorout.plugins = self._plugins
+ self._inorout.disp_class = self._collector.file_disposition_class
+
+ # It's useful to write debug info after initing for start.
+ self._should_write_debug = True
+
+ atexit.register(self._atexit)
+
+ def _init_data(self, suffix):
+ """Create a data file if we don't have one yet."""
+ if self._data is None:
+ # Create the data file. We do this at construction time so that the
+ # data file will be written into the directory where the process
+ # started rather than wherever the process eventually chdir'd to.
+ ensure_dir_for_file(self.config.data_file)
+ self._data = CoverageData(
+ basename=self.config.data_file,
+ suffix=suffix,
+ warn=self._warn,
+ debug=self._debug,
+ no_disk=self._no_disk,
+ )
+
+ def start(self):
+ """Start measuring code coverage.
+
+ Coverage measurement only occurs in functions called after
+ :meth:`start` is invoked. Statements in the same scope as
+ :meth:`start` won't be measured.
+
+ Once you invoke :meth:`start`, you must also call :meth:`stop`
+ eventually, or your process might not shut down cleanly.
+
+ """
+ self._init()
+ if not self._inited_for_start:
+ self._inited_for_start = True
+ self._init_for_start()
+ self._post_init()
+
+ # Issue warnings for possible problems.
+ self._inorout.warn_conflicting_settings()
+
+ # See if we think some code that would eventually be measured has
+ # already been imported.
+ if self._warn_preimported_source:
+ self._inorout.warn_already_imported_files()
+
+ if self._auto_load:
+ self.load()
+
+ self._collector.start()
+ self._started = True
+ self._instances.append(self)
+
+ def stop(self):
+ """Stop measuring code coverage."""
+ if self._instances:
+ if self._instances[-1] is self:
+ self._instances.pop()
+ if self._started:
+ self._collector.stop()
+ self._started = False
+
+ def _atexit(self):
+ """Clean up on process shutdown."""
+ if self._debug.should("process"):
+ self._debug.write("atexit: pid: {}, instance: {!r}".format(os.getpid(), self))
+ if self._started:
+ self.stop()
+ if self._auto_save:
+ self.save()
+
+ def erase(self):
+ """Erase previously collected coverage data.
+
+ This removes the in-memory data collected in this session as well as
+ discarding the data file.
+
+ """
+ self._init()
+ self._post_init()
+ if self._collector:
+ self._collector.reset()
+ self._init_data(suffix=None)
+ self._data.erase(parallel=self.config.parallel)
+ self._data = None
+ self._inited_for_start = False
+
+ def switch_context(self, new_context):
+ """Switch to a new dynamic context.
+
+ `new_context` is a string to use as the :ref:`dynamic context
+ <dynamic_contexts>` label for collected data. If a :ref:`static
+ context <static_contexts>` is in use, the static and dynamic context
+ labels will be joined together with a pipe character.
+
+ Coverage collection must be started already.
+
+ .. versionadded:: 5.0
+
+ """
+ if not self._started: # pragma: part started
+ raise CoverageException(
+ "Cannot switch context, coverage is not started"
+ )
+
+ if self._collector.should_start_context:
+ self._warn("Conflicting dynamic contexts", slug="dynamic-conflict", once=True)
+
+ self._collector.switch_context(new_context)
+
+ def clear_exclude(self, which='exclude'):
+ """Clear the exclude list."""
+ self._init()
+ setattr(self.config, which + "_list", [])
+ self._exclude_regex_stale()
+
+ def exclude(self, regex, which='exclude'):
+ """Exclude source lines from execution consideration.
+
+ A number of lists of regular expressions are maintained. Each list
+ selects lines that are treated differently during reporting.
+
+ `which` determines which list is modified. The "exclude" list selects
+ lines that are not considered executable at all. The "partial" list
+ indicates lines with branches that are not taken.
+
+ `regex` is a regular expression. The regex is added to the specified
+ list. If any of the regexes in the list is found in a line, the line
+ is marked for special treatment during reporting.
+
+ """
+ self._init()
+ excl_list = getattr(self.config, which + "_list")
+ excl_list.append(regex)
+ self._exclude_regex_stale()
+
+ def _exclude_regex_stale(self):
+ """Drop all the compiled exclusion regexes, a list was modified."""
+ self._exclude_re.clear()
+
+ def _exclude_regex(self, which):
+ """Return a compiled regex for the given exclusion list."""
+ if which not in self._exclude_re:
+ excl_list = getattr(self.config, which + "_list")
+ self._exclude_re[which] = join_regex(excl_list)
+ return self._exclude_re[which]
+
+ def get_exclude_list(self, which='exclude'):
+ """Return a list of excluded regex patterns.
+
+ `which` indicates which list is desired. See :meth:`exclude` for the
+ lists that are available, and their meaning.
+
+ """
+ self._init()
+ return getattr(self.config, which + "_list")
+
+ def save(self):
+ """Save the collected coverage data to the data file."""
+ data = self.get_data()
+ data.write()
+
+ def combine(self, data_paths=None, strict=False, keep=False):
+ """Combine together a number of similarly-named coverage data files.
+
+ All coverage data files whose name starts with `data_file` (from the
+ coverage() constructor) will be read, and combined together into the
+ current measurements.
+
+ `data_paths` is a list of files or directories from which data should
+ be combined. If no list is passed, then the data files from the
+ directory indicated by the current data file (probably the current
+ directory) will be combined.
+
+ If `strict` is true, then it is an error to attempt to combine when
+ there are no data files to combine.
+
+ If `keep` is true, then original input data files won't be deleted.
+
+ .. versionadded:: 4.0
+ The `data_paths` parameter.
+
+ .. versionadded:: 4.3
+ The `strict` parameter.
+
+ .. versionadded: 5.5
+ The `keep` parameter.
+ """
+ self._init()
+ self._init_data(suffix=None)
+ self._post_init()
+ self.get_data()
+
+ aliases = None
+ if self.config.paths:
+ aliases = PathAliases()
+ for paths in self.config.paths.values():
+ result = paths[0]
+ for pattern in paths[1:]:
+ aliases.add(pattern, result)
+
+ combine_parallel_data(
+ self._data,
+ aliases=aliases,
+ data_paths=data_paths,
+ strict=strict,
+ keep=keep,
+ )
+
+ def get_data(self):
+ """Get the collected data.
+
+ Also warn about various problems collecting data.
+
+ Returns a :class:`coverage.CoverageData`, the collected coverage data.
+
+ .. versionadded:: 4.0
+
+ """
+ self._init()
+ self._init_data(suffix=None)
+ self._post_init()
+
+ for plugin in self._plugins:
+ if not plugin._coverage_enabled:
+ self._collector.plugin_was_disabled(plugin)
+
+ if self._collector and self._collector.flush_data():
+ self._post_save_work()
+
+ return self._data
+
+ def _post_save_work(self):
+ """After saving data, look for warnings, post-work, etc.
+
+ Warn about things that should have happened but didn't.
+ Look for unexecuted files.
+
+ """
+ # If there are still entries in the source_pkgs_unmatched list,
+ # then we never encountered those packages.
+ if self._warn_unimported_source:
+ self._inorout.warn_unimported_source()
+
+ # Find out if we got any data.
+ if not self._data and self._warn_no_data:
+ self._warn("No data was collected.", slug="no-data-collected")
+
+ # Touch all the files that could have executed, so that we can
+ # mark completely unexecuted files as 0% covered.
+ if self._data is not None:
+ file_paths = collections.defaultdict(list)
+ for file_path, plugin_name in self._inorout.find_possibly_unexecuted_files():
+ file_path = self._file_mapper(file_path)
+ file_paths[plugin_name].append(file_path)
+ for plugin_name, paths in file_paths.items():
+ self._data.touch_files(paths, plugin_name)
+
+ if self.config.note:
+ self._warn("The '[run] note' setting is no longer supported.")
+
+ # Backward compatibility with version 1.
+ def analysis(self, morf):
+ """Like `analysis2` but doesn't return excluded line numbers."""
+ f, s, _, m, mf = self.analysis2(morf)
+ return f, s, m, mf
+
+ def analysis2(self, morf):
+ """Analyze a module.
+
+ `morf` is a module or a file name. It will be analyzed to determine
+ its coverage statistics. The return value is a 5-tuple:
+
+ * The file name for the module.
+ * A list of line numbers of executable statements.
+ * A list of line numbers of excluded statements.
+ * A list of line numbers of statements not run (missing from
+ execution).
+ * A readable formatted string of the missing line numbers.
+
+ The analysis uses the source file itself and the current measured
+ coverage data.
+
+ """
+ analysis = self._analyze(morf)
+ return (
+ analysis.filename,
+ sorted(analysis.statements),
+ sorted(analysis.excluded),
+ sorted(analysis.missing),
+ analysis.missing_formatted(),
+ )
+
+ def _analyze(self, it):
+ """Analyze a single morf or code unit.
+
+ Returns an `Analysis` object.
+
+ """
+ # All reporting comes through here, so do reporting initialization.
+ self._init()
+ Numbers.set_precision(self.config.precision)
+ self._post_init()
+
+ data = self.get_data()
+ if not isinstance(it, FileReporter):
+ it = self._get_file_reporter(it)
+
+ return Analysis(data, it, self._file_mapper)
+
+ def _get_file_reporter(self, morf):
+ """Get a FileReporter for a module or file name."""
+ plugin = None
+ file_reporter = "python"
+
+ if isinstance(morf, string_class):
+ if getattr(sys, 'is_standalone_binary', False):
+ # Leave morf in canonical format - relative to the arcadia root
+ mapped_morf = morf
+ else:
+ mapped_morf = self._file_mapper(morf)
+ plugin_name = self._data.file_tracer(mapped_morf)
+ if plugin_name:
+ plugin = self._plugins.get(plugin_name)
+
+ if plugin:
+ file_reporter = plugin.file_reporter(mapped_morf)
+ if file_reporter is None:
+ raise CoverageException(
+ "Plugin %r did not provide a file reporter for %r." % (
+ plugin._coverage_plugin_name, morf
+ )
+ )
+
+ if file_reporter == "python":
+ file_reporter = PythonFileReporter(morf, self)
+
+ return file_reporter
+
+ def _get_file_reporters(self, morfs=None):
+ """Get a list of FileReporters for a list of modules or file names.
+
+ For each module or file name in `morfs`, find a FileReporter. Return
+ the list of FileReporters.
+
+ If `morfs` is a single module or file name, this returns a list of one
+ FileReporter. If `morfs` is empty or None, then the list of all files
+ measured is used to find the FileReporters.
+
+ """
+ if not morfs:
+ morfs = self._data.measured_files()
+
+ # Be sure we have a collection.
+ if not isinstance(morfs, (list, tuple, set)):
+ morfs = [morfs]
+
+ file_reporters = [self._get_file_reporter(morf) for morf in morfs]
+ return file_reporters
+
+ def report(
+ self, morfs=None, show_missing=None, ignore_errors=None,
+ file=None, omit=None, include=None, skip_covered=None,
+ contexts=None, skip_empty=None, precision=None, sort=None
+ ):
+ """Write a textual summary report to `file`.
+
+ Each module in `morfs` is listed, with counts of statements, executed
+ statements, missing statements, and a list of lines missed.
+
+ If `show_missing` is true, then details of which lines or branches are
+ missing will be included in the report. If `ignore_errors` is true,
+ then a failure while reporting a single file will not stop the entire
+ report.
+
+ `file` is a file-like object, suitable for writing.
+
+ `include` is a list of file name patterns. Files that match will be
+ included in the report. Files matching `omit` will not be included in
+ the report.
+
+ If `skip_covered` is true, don't report on files with 100% coverage.
+
+ If `skip_empty` is true, don't report on empty files (those that have
+ no statements).
+
+ `contexts` is a list of regular expressions. Only data from
+ :ref:`dynamic contexts <dynamic_contexts>` that match one of those
+ expressions (using :func:`re.search <python:re.search>`) will be
+ included in the report.
+
+ `precision` is the number of digits to display after the decimal
+ point for percentages.
+
+ All of the arguments default to the settings read from the
+ :ref:`configuration file <config>`.
+
+ Returns a float, the total percentage covered.
+
+ .. versionadded:: 4.0
+ The `skip_covered` parameter.
+
+ .. versionadded:: 5.0
+ The `contexts` and `skip_empty` parameters.
+
+ .. versionadded:: 5.2
+ The `precision` parameter.
+
+ """
+ with override_config(
+ self,
+ ignore_errors=ignore_errors, report_omit=omit, report_include=include,
+ show_missing=show_missing, skip_covered=skip_covered,
+ report_contexts=contexts, skip_empty=skip_empty, precision=precision,
+ sort=sort
+ ):
+ reporter = SummaryReporter(self)
+ return reporter.report(morfs, outfile=file)
+
+ def annotate(
+ self, morfs=None, directory=None, ignore_errors=None,
+ omit=None, include=None, contexts=None,
+ ):
+ """Annotate a list of modules.
+
+ Each module in `morfs` is annotated. The source is written to a new
+ file, named with a ",cover" suffix, with each line prefixed with a
+ marker to indicate the coverage of the line. Covered lines have ">",
+ excluded lines have "-", and missing lines have "!".
+
+ See :meth:`report` for other arguments.
+
+ """
+ with override_config(self,
+ ignore_errors=ignore_errors, report_omit=omit,
+ report_include=include, report_contexts=contexts,
+ ):
+ reporter = AnnotateReporter(self)
+ reporter.report(morfs, directory=directory)
+
+ def html_report(
+ self, morfs=None, directory=None, ignore_errors=None,
+ omit=None, include=None, extra_css=None, title=None,
+ skip_covered=None, show_contexts=None, contexts=None,
+ skip_empty=None, precision=None,
+ ):
+ """Generate an HTML report.
+
+ The HTML is written to `directory`. The file "index.html" is the
+ overview starting point, with links to more detailed pages for
+ individual modules.
+
+ `extra_css` is a path to a file of other CSS to apply on the page.
+ It will be copied into the HTML directory.
+
+ `title` is a text string (not HTML) to use as the title of the HTML
+ report.
+
+ See :meth:`report` for other arguments.
+
+ Returns a float, the total percentage covered.
+
+ .. note::
+ The HTML report files are generated incrementally based on the
+ source files and coverage results. If you modify the report files,
+ the changes will not be considered. You should be careful about
+ changing the files in the report folder.
+
+ """
+ with override_config(self,
+ ignore_errors=ignore_errors, report_omit=omit, report_include=include,
+ html_dir=directory, extra_css=extra_css, html_title=title,
+ html_skip_covered=skip_covered, show_contexts=show_contexts, report_contexts=contexts,
+ html_skip_empty=skip_empty, precision=precision,
+ ):
+ reporter = HtmlReporter(self)
+ return reporter.report(morfs)
+
+ def xml_report(
+ self, morfs=None, outfile=None, ignore_errors=None,
+ omit=None, include=None, contexts=None, skip_empty=None,
+ ):
+ """Generate an XML report of coverage results.
+
+ The report is compatible with Cobertura reports.
+
+ Each module in `morfs` is included in the report. `outfile` is the
+ path to write the file to, "-" will write to stdout.
+
+ See :meth:`report` for other arguments.
+
+ Returns a float, the total percentage covered.
+
+ """
+ with override_config(self,
+ ignore_errors=ignore_errors, report_omit=omit, report_include=include,
+ xml_output=outfile, report_contexts=contexts, skip_empty=skip_empty,
+ ):
+ return render_report(self.config.xml_output, XmlReporter(self), morfs)
+
+ def json_report(
+ self, morfs=None, outfile=None, ignore_errors=None,
+ omit=None, include=None, contexts=None, pretty_print=None,
+ show_contexts=None
+ ):
+ """Generate a JSON report of coverage results.
+
+ Each module in `morfs` is included in the report. `outfile` is the
+ path to write the file to, "-" will write to stdout.
+
+ See :meth:`report` for other arguments.
+
+ Returns a float, the total percentage covered.
+
+ .. versionadded:: 5.0
+
+ """
+ with override_config(self,
+ ignore_errors=ignore_errors, report_omit=omit, report_include=include,
+ json_output=outfile, report_contexts=contexts, json_pretty_print=pretty_print,
+ json_show_contexts=show_contexts
+ ):
+ return render_report(self.config.json_output, JsonReporter(self), morfs)
+
+ def sys_info(self):
+ """Return a list of (key, value) pairs showing internal information."""
+
+ import coverage as covmod
+
+ self._init()
+ self._post_init()
+
+ def plugin_info(plugins):
+ """Make an entry for the sys_info from a list of plug-ins."""
+ entries = []
+ for plugin in plugins:
+ entry = plugin._coverage_plugin_name
+ if not plugin._coverage_enabled:
+ entry += " (disabled)"
+ entries.append(entry)
+ return entries
+
+ info = [
+ ('version', covmod.__version__),
+ ('coverage', covmod.__file__),
+ ('tracer', self._collector.tracer_name() if self._collector else "-none-"),
+ ('CTracer', 'available' if CTracer else "unavailable"),
+ ('plugins.file_tracers', plugin_info(self._plugins.file_tracers)),
+ ('plugins.configurers', plugin_info(self._plugins.configurers)),
+ ('plugins.context_switchers', plugin_info(self._plugins.context_switchers)),
+ ('configs_attempted', self.config.attempted_config_files),
+ ('configs_read', self.config.config_files_read),
+ ('config_file', self.config.config_file),
+ ('config_contents',
+ repr(self.config._config_contents)
+ if self.config._config_contents
+ else '-none-'
+ ),
+ ('data_file', self._data.data_filename() if self._data is not None else "-none-"),
+ ('python', sys.version.replace('\n', '')),
+ ('platform', platform.platform()),
+ ('implementation', platform.python_implementation()),
+ ('executable', sys.executable),
+ ('def_encoding', sys.getdefaultencoding()),
+ ('fs_encoding', sys.getfilesystemencoding()),
+ ('pid', os.getpid()),
+ ('cwd', os.getcwd()),
+ ('path', sys.path),
+ ('environment', sorted(
+ ("%s = %s" % (k, v))
+ for k, v in iitems(os.environ)
+ if any(slug in k for slug in ("COV", "PY"))
+ )),
+ ('command_line', " ".join(getattr(sys, 'argv', ['-none-']))),
+ ]
+
+ if self._inorout:
+ info.extend(self._inorout.sys_info())
+
+ info.extend(CoverageData.sys_info())
+
+ return info
+
+
+# Mega debugging...
+# $set_env.py: COVERAGE_DEBUG_CALLS - Lots and lots of output about calls to Coverage.
+if int(os.environ.get("COVERAGE_DEBUG_CALLS", 0)): # pragma: debugging
+ from coverage.debug import decorate_methods, show_calls
+
+ Coverage = decorate_methods(show_calls(show_args=True), butnot=['get_data'])(Coverage)
+
+
+def process_startup():
+ """Call this at Python start-up to perhaps measure coverage.
+
+ If the environment variable COVERAGE_PROCESS_START is defined, coverage
+ measurement is started. The value of the variable is the config file
+ to use.
+
+ There are two ways to configure your Python installation to invoke this
+ function when Python starts:
+
+ #. Create or append to sitecustomize.py to add these lines::
+
+ import coverage
+ coverage.process_startup()
+
+ #. Create a .pth file in your Python installation containing::
+
+ import coverage; coverage.process_startup()
+
+ Returns the :class:`Coverage` instance that was started, or None if it was
+ not started by this call.
+
+ """
+ cps = os.environ.get("COVERAGE_PROCESS_START")
+ if not cps:
+ # No request for coverage, nothing to do.
+ return None
+
+ # This function can be called more than once in a process. This happens
+ # because some virtualenv configurations make the same directory visible
+ # twice in sys.path. This means that the .pth file will be found twice,
+ # and executed twice, executing this function twice. We set a global
+ # flag (an attribute on this function) to indicate that coverage.py has
+ # already been started, so we can avoid doing it twice.
+ #
+ # https://github.com/nedbat/coveragepy/issues/340 has more details.
+
+ if hasattr(process_startup, "coverage"):
+ # We've annotated this function before, so we must have already
+ # started coverage.py in this process. Nothing to do.
+ return None
+
+ cov = Coverage(config_file=cps)
+ process_startup.coverage = cov
+ cov._warn_no_data = False
+ cov._warn_unimported_source = False
+ cov._warn_preimported_source = False
+ cov._auto_save = True
+ cov.start()
+
+ return cov
+
+
+def _prevent_sub_process_measurement():
+ """Stop any subprocess auto-measurement from writing data."""
+ auto_created_coverage = getattr(process_startup, "coverage", None)
+ if auto_created_coverage is not None:
+ auto_created_coverage._auto_save = False
diff --git a/contrib/python/coverage/py2/coverage/ctracer/datastack.c b/contrib/python/coverage/py2/coverage/ctracer/datastack.c
new file mode 100644
index 0000000000..a9cfcc2cf2
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/datastack.c
@@ -0,0 +1,50 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#include "util.h"
+#include "datastack.h"
+
+#define STACK_DELTA 20
+
+int
+DataStack_init(Stats *pstats, DataStack *pdata_stack)
+{
+ pdata_stack->depth = -1;
+ pdata_stack->stack = NULL;
+ pdata_stack->alloc = 0;
+ return RET_OK;
+}
+
+void
+DataStack_dealloc(Stats *pstats, DataStack *pdata_stack)
+{
+ int i;
+
+ for (i = 0; i < pdata_stack->alloc; i++) {
+ Py_XDECREF(pdata_stack->stack[i].file_data);
+ }
+ PyMem_Free(pdata_stack->stack);
+}
+
+int
+DataStack_grow(Stats *pstats, DataStack *pdata_stack)
+{
+ pdata_stack->depth++;
+ if (pdata_stack->depth >= pdata_stack->alloc) {
+ /* We've outgrown our data_stack array: make it bigger. */
+ int bigger = pdata_stack->alloc + STACK_DELTA;
+ DataStackEntry * bigger_data_stack = PyMem_Realloc(pdata_stack->stack, bigger * sizeof(DataStackEntry));
+ STATS( pstats->stack_reallocs++; )
+ if (bigger_data_stack == NULL) {
+ PyErr_NoMemory();
+ pdata_stack->depth--;
+ return RET_ERROR;
+ }
+ /* Zero the new entries. */
+ memset(bigger_data_stack + pdata_stack->alloc, 0, STACK_DELTA * sizeof(DataStackEntry));
+
+ pdata_stack->stack = bigger_data_stack;
+ pdata_stack->alloc = bigger;
+ }
+ return RET_OK;
+}
diff --git a/contrib/python/coverage/py2/coverage/ctracer/datastack.h b/contrib/python/coverage/py2/coverage/ctracer/datastack.h
new file mode 100644
index 0000000000..3b3078ba27
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/datastack.h
@@ -0,0 +1,45 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_DATASTACK_H
+#define _COVERAGE_DATASTACK_H
+
+#include "util.h"
+#include "stats.h"
+
+/* An entry on the data stack. For each call frame, we need to record all
+ * the information needed for CTracer_handle_line to operate as quickly as
+ * possible.
+ */
+typedef struct DataStackEntry {
+ /* The current file_data dictionary. Owned. */
+ PyObject * file_data;
+
+ /* The disposition object for this frame. A borrowed instance of CFileDisposition. */
+ PyObject * disposition;
+
+ /* The FileTracer handling this frame, or None if it's Python. Borrowed. */
+ PyObject * file_tracer;
+
+ /* The line number of the last line recorded, for tracing arcs.
+ -1 means there was no previous line, as when entering a code object.
+ */
+ int last_line;
+
+ BOOL started_context;
+} DataStackEntry;
+
+/* A data stack is a dynamically allocated vector of DataStackEntry's. */
+typedef struct DataStack {
+ int depth; /* The index of the last-used entry in stack. */
+ int alloc; /* number of entries allocated at stack. */
+ /* The file data at each level, or NULL if not recording. */
+ DataStackEntry * stack;
+} DataStack;
+
+
+int DataStack_init(Stats * pstats, DataStack *pdata_stack);
+void DataStack_dealloc(Stats * pstats, DataStack *pdata_stack);
+int DataStack_grow(Stats * pstats, DataStack *pdata_stack);
+
+#endif /* _COVERAGE_DATASTACK_H */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/filedisp.c b/contrib/python/coverage/py2/coverage/ctracer/filedisp.c
new file mode 100644
index 0000000000..47782ae090
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/filedisp.c
@@ -0,0 +1,85 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#include "util.h"
+#include "filedisp.h"
+
+void
+CFileDisposition_dealloc(CFileDisposition *self)
+{
+ Py_XDECREF(self->original_filename);
+ Py_XDECREF(self->canonical_filename);
+ Py_XDECREF(self->source_filename);
+ Py_XDECREF(self->trace);
+ Py_XDECREF(self->reason);
+ Py_XDECREF(self->file_tracer);
+ Py_XDECREF(self->has_dynamic_filename);
+}
+
+static PyMemberDef
+CFileDisposition_members[] = {
+ { "original_filename", T_OBJECT, offsetof(CFileDisposition, original_filename), 0,
+ PyDoc_STR("") },
+
+ { "canonical_filename", T_OBJECT, offsetof(CFileDisposition, canonical_filename), 0,
+ PyDoc_STR("") },
+
+ { "source_filename", T_OBJECT, offsetof(CFileDisposition, source_filename), 0,
+ PyDoc_STR("") },
+
+ { "trace", T_OBJECT, offsetof(CFileDisposition, trace), 0,
+ PyDoc_STR("") },
+
+ { "reason", T_OBJECT, offsetof(CFileDisposition, reason), 0,
+ PyDoc_STR("") },
+
+ { "file_tracer", T_OBJECT, offsetof(CFileDisposition, file_tracer), 0,
+ PyDoc_STR("") },
+
+ { "has_dynamic_filename", T_OBJECT, offsetof(CFileDisposition, has_dynamic_filename), 0,
+ PyDoc_STR("") },
+
+ { NULL }
+};
+
+PyTypeObject
+CFileDispositionType = {
+ MyType_HEAD_INIT
+ "coverage.CFileDispositionType", /*tp_name*/
+ sizeof(CFileDisposition), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)CFileDisposition_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "CFileDisposition objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ CFileDisposition_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/contrib/python/coverage/py2/coverage/ctracer/filedisp.h b/contrib/python/coverage/py2/coverage/ctracer/filedisp.h
new file mode 100644
index 0000000000..860f9a50b1
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/filedisp.h
@@ -0,0 +1,26 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_FILEDISP_H
+#define _COVERAGE_FILEDISP_H
+
+#include "util.h"
+#include "structmember.h"
+
+typedef struct CFileDisposition {
+ PyObject_HEAD
+
+ PyObject * original_filename;
+ PyObject * canonical_filename;
+ PyObject * source_filename;
+ PyObject * trace;
+ PyObject * reason;
+ PyObject * file_tracer;
+ PyObject * has_dynamic_filename;
+} CFileDisposition;
+
+void CFileDisposition_dealloc(CFileDisposition *self);
+
+extern PyTypeObject CFileDispositionType;
+
+#endif /* _COVERAGE_FILEDISP_H */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/module.c b/contrib/python/coverage/py2/coverage/ctracer/module.c
new file mode 100644
index 0000000000..f308902b69
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/module.c
@@ -0,0 +1,108 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#include "util.h"
+#include "tracer.h"
+#include "filedisp.h"
+
+/* Module definition */
+
+#define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
+
+#if PY_MAJOR_VERSION >= 3
+
+static PyModuleDef
+moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "coverage.tracer",
+ MODULE_DOC,
+ -1,
+ NULL, /* methods */
+ NULL,
+ NULL, /* traverse */
+ NULL, /* clear */
+ NULL
+};
+
+
+PyObject *
+PyInit_tracer(void)
+{
+ PyObject * mod = PyModule_Create(&moduledef);
+ if (mod == NULL) {
+ return NULL;
+ }
+
+ if (CTracer_intern_strings() < 0) {
+ return NULL;
+ }
+
+ /* Initialize CTracer */
+ CTracerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CTracerType) < 0) {
+ Py_DECREF(mod);
+ return NULL;
+ }
+
+ Py_INCREF(&CTracerType);
+ if (PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ return NULL;
+ }
+
+ /* Initialize CFileDisposition */
+ CFileDispositionType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CFileDispositionType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ return NULL;
+ }
+
+ Py_INCREF(&CFileDispositionType);
+ if (PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ Py_DECREF(&CFileDispositionType);
+ return NULL;
+ }
+
+ return mod;
+}
+
+#else
+
+void
+inittracer(void)
+{
+ PyObject * mod;
+
+ mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
+ if (mod == NULL) {
+ return;
+ }
+
+ if (CTracer_intern_strings() < 0) {
+ return;
+ }
+
+ /* Initialize CTracer */
+ CTracerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CTracerType) < 0) {
+ return;
+ }
+
+ Py_INCREF(&CTracerType);
+ PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType);
+
+ /* Initialize CFileDisposition */
+ CFileDispositionType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CFileDispositionType) < 0) {
+ return;
+ }
+
+ Py_INCREF(&CFileDispositionType);
+ PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType);
+}
+
+#endif /* Py3k */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/stats.h b/contrib/python/coverage/py2/coverage/ctracer/stats.h
new file mode 100644
index 0000000000..05173369f7
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/stats.h
@@ -0,0 +1,31 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_STATS_H
+#define _COVERAGE_STATS_H
+
+#include "util.h"
+
+#if COLLECT_STATS
+#define STATS(x) x
+#else
+#define STATS(x)
+#endif
+
+typedef struct Stats {
+ unsigned int calls; /* Need at least one member, but the rest only if needed. */
+#if COLLECT_STATS
+ unsigned int lines;
+ unsigned int returns;
+ unsigned int exceptions;
+ unsigned int others;
+ unsigned int files;
+ unsigned int missed_returns;
+ unsigned int stack_reallocs;
+ unsigned int errors;
+ unsigned int pycalls;
+ unsigned int start_context_calls;
+#endif
+} Stats;
+
+#endif /* _COVERAGE_STATS_H */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/tracer.c b/contrib/python/coverage/py2/coverage/ctracer/tracer.c
new file mode 100644
index 0000000000..00e4218d8e
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/tracer.c
@@ -0,0 +1,1149 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+/* C-based Tracer for coverage.py. */
+
+#include "util.h"
+#include "datastack.h"
+#include "filedisp.h"
+#include "tracer.h"
+
+/* Python C API helpers. */
+
+static int
+pyint_as_int(PyObject * pyint, int *pint)
+{
+ int the_int = MyInt_AsInt(pyint);
+ if (the_int == -1 && PyErr_Occurred()) {
+ return RET_ERROR;
+ }
+
+ *pint = the_int;
+ return RET_OK;
+}
+
+
+/* Interned strings to speed GetAttr etc. */
+
+static PyObject *str_trace;
+static PyObject *str_file_tracer;
+static PyObject *str__coverage_enabled;
+static PyObject *str__coverage_plugin;
+static PyObject *str__coverage_plugin_name;
+static PyObject *str_dynamic_source_filename;
+static PyObject *str_line_number_range;
+
+int
+CTracer_intern_strings(void)
+{
+ int ret = RET_ERROR;
+
+#define INTERN_STRING(v, s) \
+ v = MyText_InternFromString(s); \
+ if (v == NULL) { \
+ goto error; \
+ }
+
+ INTERN_STRING(str_trace, "trace")
+ INTERN_STRING(str_file_tracer, "file_tracer")
+ INTERN_STRING(str__coverage_enabled, "_coverage_enabled")
+ INTERN_STRING(str__coverage_plugin, "_coverage_plugin")
+ INTERN_STRING(str__coverage_plugin_name, "_coverage_plugin_name")
+ INTERN_STRING(str_dynamic_source_filename, "dynamic_source_filename")
+ INTERN_STRING(str_line_number_range, "line_number_range")
+
+ ret = RET_OK;
+
+error:
+ return ret;
+}
+
+static void CTracer_disable_plugin(CTracer *self, PyObject * disposition);
+
+static int
+CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused)
+{
+ int ret = RET_ERROR;
+
+ if (DataStack_init(&self->stats, &self->data_stack) < 0) {
+ goto error;
+ }
+
+ self->pdata_stack = &self->data_stack;
+
+ self->context = Py_None;
+ Py_INCREF(self->context);
+
+ ret = RET_OK;
+ goto ok;
+
+error:
+ STATS( self->stats.errors++; )
+
+ok:
+ return ret;
+}
+
+static void
+CTracer_dealloc(CTracer *self)
+{
+ int i;
+
+ if (self->started) {
+ PyEval_SetTrace(NULL, NULL);
+ }
+
+ Py_XDECREF(self->should_trace);
+ Py_XDECREF(self->check_include);
+ Py_XDECREF(self->warn);
+ Py_XDECREF(self->concur_id_func);
+ Py_XDECREF(self->data);
+ Py_XDECREF(self->file_tracers);
+ Py_XDECREF(self->should_trace_cache);
+ Py_XDECREF(self->should_start_context);
+ Py_XDECREF(self->switch_context);
+ Py_XDECREF(self->context);
+ Py_XDECREF(self->disable_plugin);
+
+ DataStack_dealloc(&self->stats, &self->data_stack);
+ if (self->data_stacks) {
+ for (i = 0; i < self->data_stacks_used; i++) {
+ DataStack_dealloc(&self->stats, self->data_stacks + i);
+ }
+ PyMem_Free(self->data_stacks);
+ }
+
+ Py_XDECREF(self->data_stack_index);
+
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+#if TRACE_LOG
+static const char *
+indent(int n)
+{
+ static const char * spaces =
+ " "
+ " "
+ " "
+ " "
+ ;
+ return spaces + strlen(spaces) - n*2;
+}
+
+static BOOL logging = FALSE;
+/* Set these constants to be a file substring and line number to start logging. */
+static const char * start_file = "tests/views";
+static int start_line = 27;
+
+static void
+showlog(int depth, int lineno, PyObject * filename, const char * msg)
+{
+ if (logging) {
+ printf("%s%3d ", indent(depth), depth);
+ if (lineno) {
+ printf("%4d", lineno);
+ }
+ else {
+ printf(" ");
+ }
+ if (filename) {
+ PyObject *ascii = MyText_AS_BYTES(filename);
+ printf(" %s", MyBytes_AS_STRING(ascii));
+ Py_DECREF(ascii);
+ }
+ if (msg) {
+ printf(" %s", msg);
+ }
+ printf("\n");
+ }
+}
+
+#define SHOWLOG(a,b,c,d) showlog(a,b,c,d)
+#else
+#define SHOWLOG(a,b,c,d)
+#endif /* TRACE_LOG */
+
+#if WHAT_LOG
+static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "};
+#endif
+
+/* Record a pair of integers in self->pcur_entry->file_data. */
+static int
+CTracer_record_pair(CTracer *self, int l1, int l2)
+{
+ int ret = RET_ERROR;
+
+ PyObject * t = NULL;
+
+ t = Py_BuildValue("(ii)", l1, l2);
+ if (t == NULL) {
+ goto error;
+ }
+
+ if (PyDict_SetItem(self->pcur_entry->file_data, t, Py_None) < 0) {
+ goto error;
+ }
+
+ ret = RET_OK;
+
+error:
+ Py_XDECREF(t);
+
+ return ret;
+}
+
+/* Set self->pdata_stack to the proper data_stack to use. */
+static int
+CTracer_set_pdata_stack(CTracer *self)
+{
+ int ret = RET_ERROR;
+ PyObject * co_obj = NULL;
+ PyObject * stack_index = NULL;
+
+ if (self->concur_id_func != Py_None) {
+ int the_index = 0;
+
+ if (self->data_stack_index == NULL) {
+ PyObject * weakref = NULL;
+
+ weakref = PyImport_ImportModule("weakref");
+ if (weakref == NULL) {
+ goto error;
+ }
+ STATS( self->stats.pycalls++; )
+ self->data_stack_index = PyObject_CallMethod(weakref, "WeakKeyDictionary", NULL);
+ Py_XDECREF(weakref);
+
+ if (self->data_stack_index == NULL) {
+ goto error;
+ }
+ }
+
+ STATS( self->stats.pycalls++; )
+ co_obj = PyObject_CallObject(self->concur_id_func, NULL);
+ if (co_obj == NULL) {
+ goto error;
+ }
+ stack_index = PyObject_GetItem(self->data_stack_index, co_obj);
+ if (stack_index == NULL) {
+ /* PyObject_GetItem sets an exception if it didn't find the thing. */
+ PyErr_Clear();
+
+ /* A new concurrency object. Make a new data stack. */
+ the_index = self->data_stacks_used;
+ stack_index = MyInt_FromInt(the_index);
+ if (stack_index == NULL) {
+ goto error;
+ }
+ if (PyObject_SetItem(self->data_stack_index, co_obj, stack_index) < 0) {
+ goto error;
+ }
+ self->data_stacks_used++;
+ if (self->data_stacks_used >= self->data_stacks_alloc) {
+ int bigger = self->data_stacks_alloc + 10;
+ DataStack * bigger_stacks = PyMem_Realloc(self->data_stacks, bigger * sizeof(DataStack));
+ if (bigger_stacks == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ self->data_stacks = bigger_stacks;
+ self->data_stacks_alloc = bigger;
+ }
+ DataStack_init(&self->stats, &self->data_stacks[the_index]);
+ }
+ else {
+ if (pyint_as_int(stack_index, &the_index) < 0) {
+ goto error;
+ }
+ }
+
+ self->pdata_stack = &self->data_stacks[the_index];
+ }
+ else {
+ self->pdata_stack = &self->data_stack;
+ }
+
+ ret = RET_OK;
+
+error:
+
+ Py_XDECREF(co_obj);
+ Py_XDECREF(stack_index);
+
+ return ret;
+}
+
+/*
+ * Parts of the trace function.
+ */
+
+static int
+CTracer_check_missing_return(CTracer *self, PyFrameObject *frame)
+{
+ int ret = RET_ERROR;
+
+ if (self->last_exc_back) {
+ if (frame == self->last_exc_back) {
+ /* Looks like someone forgot to send a return event. We'll clear
+ the exception state and do the RETURN code here. Notice that the
+ frame we have in hand here is not the correct frame for the RETURN,
+ that frame is gone. Our handling for RETURN doesn't need the
+ actual frame, but we do log it, so that will look a little off if
+ you're looking at the detailed log.
+
+ If someday we need to examine the frame when doing RETURN, then
+ we'll need to keep more of the missed frame's state.
+ */
+ STATS( self->stats.missed_returns++; )
+ if (CTracer_set_pdata_stack(self) < 0) {
+ goto error;
+ }
+ if (self->pdata_stack->depth >= 0) {
+ if (self->tracing_arcs && self->pcur_entry->file_data) {
+ if (CTracer_record_pair(self, self->pcur_entry->last_line, -self->last_exc_firstlineno) < 0) {
+ goto error;
+ }
+ }
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "missedreturn");
+ self->pdata_stack->depth--;
+ self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
+ }
+ }
+ self->last_exc_back = NULL;
+ }
+
+ ret = RET_OK;
+
+error:
+
+ return ret;
+}
+
+static int
+CTracer_handle_call(CTracer *self, PyFrameObject *frame)
+{
+ int ret = RET_ERROR;
+ int ret2;
+
+ /* Owned references that we clean up at the very end of the function. */
+ PyObject * disposition = NULL;
+ PyObject * plugin = NULL;
+ PyObject * plugin_name = NULL;
+ PyObject * next_tracename = NULL;
+
+ /* Borrowed references. */
+ PyObject * filename = NULL;
+ PyObject * disp_trace = NULL;
+ PyObject * tracename = NULL;
+ PyObject * file_tracer = NULL;
+ PyObject * has_dynamic_filename = NULL;
+
+ CFileDisposition * pdisp = NULL;
+
+ STATS( self->stats.calls++; )
+
+ /* Grow the stack. */
+ if (CTracer_set_pdata_stack(self) < 0) {
+ goto error;
+ }
+ if (DataStack_grow(&self->stats, self->pdata_stack) < 0) {
+ goto error;
+ }
+ self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
+
+ /* See if this frame begins a new context. */
+ if (self->should_start_context != Py_None && self->context == Py_None) {
+ PyObject * context;
+ /* We're looking for our context, ask should_start_context if this is the start. */
+ STATS( self->stats.start_context_calls++; )
+ STATS( self->stats.pycalls++; )
+ context = PyObject_CallFunctionObjArgs(self->should_start_context, frame, NULL);
+ if (context == NULL) {
+ goto error;
+ }
+ if (context != Py_None) {
+ PyObject * val;
+ Py_DECREF(self->context);
+ self->context = context;
+ self->pcur_entry->started_context = TRUE;
+ STATS( self->stats.pycalls++; )
+ val = PyObject_CallFunctionObjArgs(self->switch_context, context, NULL);
+ if (val == NULL) {
+ goto error;
+ }
+ Py_DECREF(val);
+ }
+ else {
+ Py_DECREF(context);
+ self->pcur_entry->started_context = FALSE;
+ }
+ }
+ else {
+ self->pcur_entry->started_context = FALSE;
+ }
+
+ /* Check if we should trace this line. */
+ filename = frame->f_code->co_filename;
+ disposition = PyDict_GetItem(self->should_trace_cache, filename);
+ if (disposition == NULL) {
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ STATS( self->stats.files++; )
+
+ /* We've never considered this file before. */
+ /* Ask should_trace about it. */
+ STATS( self->stats.pycalls++; )
+ disposition = PyObject_CallFunctionObjArgs(self->should_trace, filename, frame, NULL);
+ if (disposition == NULL) {
+ /* An error occurred inside should_trace. */
+ goto error;
+ }
+ if (PyDict_SetItem(self->should_trace_cache, filename, disposition) < 0) {
+ goto error;
+ }
+ }
+ else {
+ Py_INCREF(disposition);
+ }
+
+ if (disposition == Py_None) {
+ /* A later check_include returned false, so don't trace it. */
+ disp_trace = Py_False;
+ }
+ else {
+ /* The object we got is a CFileDisposition, use it efficiently. */
+ pdisp = (CFileDisposition *) disposition;
+ disp_trace = pdisp->trace;
+ if (disp_trace == NULL) {
+ goto error;
+ }
+ }
+
+ if (disp_trace == Py_True) {
+ /* If tracename is a string, then we're supposed to trace. */
+ tracename = pdisp->source_filename;
+ if (tracename == NULL) {
+ goto error;
+ }
+ file_tracer = pdisp->file_tracer;
+ if (file_tracer == NULL) {
+ goto error;
+ }
+ if (file_tracer != Py_None) {
+ plugin = PyObject_GetAttr(file_tracer, str__coverage_plugin);
+ if (plugin == NULL) {
+ goto error;
+ }
+ plugin_name = PyObject_GetAttr(plugin, str__coverage_plugin_name);
+ if (plugin_name == NULL) {
+ goto error;
+ }
+ }
+ has_dynamic_filename = pdisp->has_dynamic_filename;
+ if (has_dynamic_filename == NULL) {
+ goto error;
+ }
+ if (has_dynamic_filename == Py_True) {
+ STATS( self->stats.pycalls++; )
+ next_tracename = PyObject_CallMethodObjArgs(
+ file_tracer, str_dynamic_source_filename,
+ tracename, frame, NULL
+ );
+ if (next_tracename == NULL) {
+ /* An exception from the function. Alert the user with a
+ * warning and a traceback.
+ */
+ CTracer_disable_plugin(self, disposition);
+ /* Because we handled the error, goto ok. */
+ goto ok;
+ }
+ tracename = next_tracename;
+
+ if (tracename != Py_None) {
+ /* Check the dynamic source filename against the include rules. */
+ PyObject * included = NULL;
+ int should_include;
+ included = PyDict_GetItem(self->should_trace_cache, tracename);
+ if (included == NULL) {
+ PyObject * should_include_bool;
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ STATS( self->stats.files++; )
+ STATS( self->stats.pycalls++; )
+ should_include_bool = PyObject_CallFunctionObjArgs(self->check_include, tracename, frame, NULL);
+ if (should_include_bool == NULL) {
+ goto error;
+ }
+ should_include = (should_include_bool == Py_True);
+ Py_DECREF(should_include_bool);
+ if (PyDict_SetItem(self->should_trace_cache, tracename, should_include ? disposition : Py_None) < 0) {
+ goto error;
+ }
+ }
+ else {
+ should_include = (included != Py_None);
+ }
+ if (!should_include) {
+ tracename = Py_None;
+ }
+ }
+ }
+ }
+ else {
+ tracename = Py_None;
+ }
+
+ if (tracename != Py_None) {
+ PyObject * file_data = PyDict_GetItem(self->data, tracename);
+
+ if (file_data == NULL) {
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ file_data = PyDict_New();
+ if (file_data == NULL) {
+ goto error;
+ }
+ ret2 = PyDict_SetItem(self->data, tracename, file_data);
+ if (ret2 < 0) {
+ goto error;
+ }
+
+ /* If the disposition mentions a plugin, record that. */
+ if (file_tracer != Py_None) {
+ ret2 = PyDict_SetItem(self->file_tracers, tracename, plugin_name);
+ if (ret2 < 0) {
+ goto error;
+ }
+ }
+ }
+ else {
+ /* PyDict_GetItem gives a borrowed reference. Own it. */
+ Py_INCREF(file_data);
+ }
+
+ Py_XDECREF(self->pcur_entry->file_data);
+ self->pcur_entry->file_data = file_data;
+ self->pcur_entry->file_tracer = file_tracer;
+
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), filename, "traced");
+ }
+ else {
+ Py_XDECREF(self->pcur_entry->file_data);
+ self->pcur_entry->file_data = NULL;
+ self->pcur_entry->file_tracer = Py_None;
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), filename, "skipped");
+ }
+
+ self->pcur_entry->disposition = disposition;
+
+ /* Make the frame right in case settrace(gettrace()) happens. */
+ Py_INCREF(self);
+ My_XSETREF(frame->f_trace, (PyObject*)self);
+
+ /* A call event is really a "start frame" event, and can happen for
+ * re-entering a generator also. f_lasti is -1 for a true call, and a
+ * real byte offset for a generator re-entry.
+ */
+ if (frame->f_lasti < 0) {
+ self->pcur_entry->last_line = -frame->f_code->co_firstlineno;
+ }
+ else {
+ self->pcur_entry->last_line = PyFrame_GetLineNumber(frame);
+ }
+
+ok:
+ ret = RET_OK;
+
+error:
+ Py_XDECREF(next_tracename);
+ Py_XDECREF(disposition);
+ Py_XDECREF(plugin);
+ Py_XDECREF(plugin_name);
+
+ return ret;
+}
+
+
+static void
+CTracer_disable_plugin(CTracer *self, PyObject * disposition)
+{
+ PyObject * ret;
+ PyErr_Print();
+
+ STATS( self->stats.pycalls++; )
+ ret = PyObject_CallFunctionObjArgs(self->disable_plugin, disposition, NULL);
+ if (ret == NULL) {
+ goto error;
+ }
+ Py_DECREF(ret);
+
+ return;
+
+error:
+ /* This function doesn't return a status, so if an error happens, print it,
+ * but don't interrupt the flow. */
+ /* PySys_WriteStderr is nicer, but is not in the public API. */
+ fprintf(stderr, "Error occurred while disabling plug-in:\n");
+ PyErr_Print();
+}
+
+
+static int
+CTracer_unpack_pair(CTracer *self, PyObject *pair, int *p_one, int *p_two)
+{
+ int ret = RET_ERROR;
+ int the_int;
+ PyObject * pyint = NULL;
+ int index;
+
+ if (!PyTuple_Check(pair) || PyTuple_Size(pair) != 2) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "line_number_range must return 2-tuple"
+ );
+ goto error;
+ }
+
+ for (index = 0; index < 2; index++) {
+ pyint = PyTuple_GetItem(pair, index);
+ if (pyint == NULL) {
+ goto error;
+ }
+ if (pyint_as_int(pyint, &the_int) < 0) {
+ goto error;
+ }
+ *(index == 0 ? p_one : p_two) = the_int;
+ }
+
+ ret = RET_OK;
+
+error:
+ return ret;
+}
+
+static int
+CTracer_handle_line(CTracer *self, PyFrameObject *frame)
+{
+ int ret = RET_ERROR;
+ int ret2;
+
+ STATS( self->stats.lines++; )
+ if (self->pdata_stack->depth >= 0) {
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "line");
+ if (self->pcur_entry->file_data) {
+ int lineno_from = -1;
+ int lineno_to = -1;
+
+ /* We're tracing in this frame: record something. */
+ if (self->pcur_entry->file_tracer != Py_None) {
+ PyObject * from_to = NULL;
+ STATS( self->stats.pycalls++; )
+ from_to = PyObject_CallMethodObjArgs(self->pcur_entry->file_tracer, str_line_number_range, frame, NULL);
+ if (from_to == NULL) {
+ CTracer_disable_plugin(self, self->pcur_entry->disposition);
+ goto ok;
+ }
+ ret2 = CTracer_unpack_pair(self, from_to, &lineno_from, &lineno_to);
+ Py_DECREF(from_to);
+ if (ret2 < 0) {
+ CTracer_disable_plugin(self, self->pcur_entry->disposition);
+ goto ok;
+ }
+ }
+ else {
+ lineno_from = lineno_to = PyFrame_GetLineNumber(frame);
+ }
+
+ if (lineno_from != -1) {
+ for (; lineno_from <= lineno_to; lineno_from++) {
+ if (self->tracing_arcs) {
+ /* Tracing arcs: key is (last_line,this_line). */
+ if (CTracer_record_pair(self, self->pcur_entry->last_line, lineno_from) < 0) {
+ goto error;
+ }
+ }
+ else {
+ /* Tracing lines: key is simply this_line. */
+ PyObject * this_line = MyInt_FromInt(lineno_from);
+ if (this_line == NULL) {
+ goto error;
+ }
+
+ ret2 = PyDict_SetItem(self->pcur_entry->file_data, this_line, Py_None);
+ Py_DECREF(this_line);
+ if (ret2 < 0) {
+ goto error;
+ }
+ }
+
+ self->pcur_entry->last_line = lineno_from;
+ }
+ }
+ }
+ }
+
+ok:
+ ret = RET_OK;
+
+error:
+
+ return ret;
+}
+
+static int
+CTracer_handle_return(CTracer *self, PyFrameObject *frame)
+{
+ int ret = RET_ERROR;
+
+ STATS( self->stats.returns++; )
+ /* A near-copy of this code is above in the missing-return handler. */
+ if (CTracer_set_pdata_stack(self) < 0) {
+ goto error;
+ }
+ self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
+
+ if (self->pdata_stack->depth >= 0) {
+ if (self->tracing_arcs && self->pcur_entry->file_data) {
+ /* Need to distinguish between RETURN_VALUE and YIELD_VALUE. Read
+ * the current bytecode to see what it is. In unusual circumstances
+ * (Cython code), co_code can be the empty string, so range-check
+ * f_lasti before reading the byte.
+ */
+ int bytecode = RETURN_VALUE;
+ PyObject * pCode = frame->f_code->co_code;
+ int lasti = frame->f_lasti;
+
+ if (lasti < MyBytes_GET_SIZE(pCode)) {
+ bytecode = MyBytes_AS_STRING(pCode)[lasti];
+ }
+ if (bytecode != YIELD_VALUE) {
+ int first = frame->f_code->co_firstlineno;
+ if (CTracer_record_pair(self, self->pcur_entry->last_line, -first) < 0) {
+ goto error;
+ }
+ }
+ }
+
+ /* If this frame started a context, then returning from it ends the context. */
+ if (self->pcur_entry->started_context) {
+ PyObject * val;
+ Py_DECREF(self->context);
+ self->context = Py_None;
+ Py_INCREF(self->context);
+ STATS( self->stats.pycalls++; )
+
+ val = PyObject_CallFunctionObjArgs(self->switch_context, self->context, NULL);
+ if (val == NULL) {
+ goto error;
+ }
+ Py_DECREF(val);
+ }
+
+ /* Pop the stack. */
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), frame->f_code->co_filename, "return");
+ self->pdata_stack->depth--;
+ self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
+ }
+
+ ret = RET_OK;
+
+error:
+
+ return ret;
+}
+
+static int
+CTracer_handle_exception(CTracer *self, PyFrameObject *frame)
+{
+ /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event
+ without a return event. To detect that, we'll keep a copy of the
+ parent frame for an exception event. If the next event is in that
+ frame, then we must have returned without a return event. We can
+ synthesize the missing event then.
+
+ Python itself fixed this problem in 2.4. Pyexpat still has the bug.
+ I've reported the problem with pyexpat as http://bugs.python.org/issue6359 .
+ If it gets fixed, this code should still work properly. Maybe some day
+ the bug will be fixed everywhere coverage.py is supported, and we can
+ remove this missing-return detection.
+
+ More about this fix: https://nedbatchelder.com/blog/200907/a_nasty_little_bug.html
+ */
+ STATS( self->stats.exceptions++; )
+ self->last_exc_back = frame->f_back;
+ self->last_exc_firstlineno = frame->f_code->co_firstlineno;
+
+ return RET_OK;
+}
+
+/*
+ * The Trace Function
+ */
+static int
+CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unused)
+{
+ int ret = RET_ERROR;
+
+ #if DO_NOTHING
+ return RET_OK;
+ #endif
+
+ if (!self->started) {
+ /* If CTracer.stop() has been called from another thread, the tracer
+ is still active in the current thread. Let's deactivate ourselves
+ now. */
+ PyEval_SetTrace(NULL, NULL);
+ return RET_OK;
+ }
+
+ #if WHAT_LOG || TRACE_LOG
+ PyObject * ascii = NULL;
+ #endif
+
+ #if WHAT_LOG
+ if (what <= (int)(sizeof(what_sym)/sizeof(const char *))) {
+ ascii = MyText_AS_BYTES(frame->f_code->co_filename);
+ printf("trace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame));
+ Py_DECREF(ascii);
+ }
+ #endif
+
+ #if TRACE_LOG
+ ascii = MyText_AS_BYTES(frame->f_code->co_filename);
+ if (strstr(MyBytes_AS_STRING(ascii), start_file) && PyFrame_GetLineNumber(frame) == start_line) {
+ logging = TRUE;
+ }
+ Py_DECREF(ascii);
+ #endif
+
+ /* See below for details on missing-return detection. */
+ if (CTracer_check_missing_return(self, frame) < 0) {
+ goto error;
+ }
+
+ self->activity = TRUE;
+
+ switch (what) {
+ case PyTrace_CALL:
+ if (CTracer_handle_call(self, frame) < 0) {
+ goto error;
+ }
+ break;
+
+ case PyTrace_RETURN:
+ if (CTracer_handle_return(self, frame) < 0) {
+ goto error;
+ }
+ break;
+
+ case PyTrace_LINE:
+ if (CTracer_handle_line(self, frame) < 0) {
+ goto error;
+ }
+ break;
+
+ case PyTrace_EXCEPTION:
+ if (CTracer_handle_exception(self, frame) < 0) {
+ goto error;
+ }
+ break;
+
+ default:
+ STATS( self->stats.others++; )
+ break;
+ }
+
+ ret = RET_OK;
+ goto cleanup;
+
+error:
+ STATS( self->stats.errors++; )
+
+cleanup:
+ return ret;
+}
+
+
+/*
+ * Python has two ways to set the trace function: sys.settrace(fn), which
+ * takes a Python callable, and PyEval_SetTrace(func, obj), which takes
+ * a C function and a Python object. The way these work together is that
+ * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the
+ * Python callable as the object in PyEval_SetTrace. So sys.gettrace()
+ * simply returns the Python object used as the second argument to
+ * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which
+ * means it must be callable to be used in sys.settrace().
+ *
+ * So we make ourself callable, equivalent to invoking our trace function.
+ *
+ * To help with the process of replaying stored frames, this function has an
+ * optional keyword argument:
+ *
+ * def CTracer_call(frame, event, arg, lineno=0)
+ *
+ * If provided, the lineno argument is used as the line number, and the
+ * frame's f_lineno member is ignored.
+ */
+static PyObject *
+CTracer_call(CTracer *self, PyObject *args, PyObject *kwds)
+{
+ PyFrameObject *frame;
+ PyObject *what_str;
+ PyObject *arg;
+ int lineno = 0;
+ int what;
+ int orig_lineno;
+ PyObject *ret = NULL;
+ PyObject * ascii = NULL;
+
+ #if DO_NOTHING
+ CRASH
+ #endif
+
+ static char *what_names[] = {
+ "call", "exception", "line", "return",
+ "c_call", "c_exception", "c_return",
+ NULL
+ };
+
+ static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist,
+ &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) {
+ goto done;
+ }
+
+ /* In Python, the what argument is a string, we need to find an int
+ for the C function. */
+ for (what = 0; what_names[what]; what++) {
+ int should_break;
+ ascii = MyText_AS_BYTES(what_str);
+ should_break = !strcmp(MyBytes_AS_STRING(ascii), what_names[what]);
+ Py_DECREF(ascii);
+ if (should_break) {
+ break;
+ }
+ }
+
+ #if WHAT_LOG
+ ascii = MyText_AS_BYTES(frame->f_code->co_filename);
+ printf("pytrace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame));
+ Py_DECREF(ascii);
+ #endif
+
+ /* Save off the frame's lineno, and use the forced one, if provided. */
+ orig_lineno = frame->f_lineno;
+ if (lineno > 0) {
+ frame->f_lineno = lineno;
+ }
+
+ /* Invoke the C function, and return ourselves. */
+ if (CTracer_trace(self, frame, what, arg) == RET_OK) {
+ Py_INCREF(self);
+ ret = (PyObject *)self;
+ }
+
+ /* Clean up. */
+ frame->f_lineno = orig_lineno;
+
+ /* For better speed, install ourselves the C way so that future calls go
+ directly to CTracer_trace, without this intermediate function.
+
+ Only do this if this is a CALL event, since new trace functions only
+ take effect then. If we don't condition it on CALL, then we'll clobber
+ the new trace function before it has a chance to get called. To
+ understand why, there are three internal values to track: frame.f_trace,
+ c_tracefunc, and c_traceobj. They are explained here:
+ https://nedbatchelder.com/text/trace-function.html
+
+ Without the conditional on PyTrace_CALL, this is what happens:
+
+ def func(): # f_trace c_tracefunc c_traceobj
+ # -------------- -------------- --------------
+ # CTracer CTracer.trace CTracer
+ sys.settrace(my_func)
+ # CTracer trampoline my_func
+ # Now Python calls trampoline(CTracer), which calls this function
+ # which calls PyEval_SetTrace below, setting us as the tracer again:
+ # CTracer CTracer.trace CTracer
+ # and it's as if the settrace never happened.
+ */
+ if (what == PyTrace_CALL) {
+ PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
+ }
+
+done:
+ return ret;
+}
+
+static PyObject *
+CTracer_start(CTracer *self, PyObject *args_unused)
+{
+ PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
+ self->started = TRUE;
+ self->tracing_arcs = self->trace_arcs && PyObject_IsTrue(self->trace_arcs);
+
+ /* start() returns a trace function usable with sys.settrace() */
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+static PyObject *
+CTracer_stop(CTracer *self, PyObject *args_unused)
+{
+ if (self->started) {
+ /* Set the started flag only. The actual call to
+ PyEval_SetTrace(NULL, NULL) is delegated to the callback
+ itself to ensure that it called from the right thread.
+ */
+ self->started = FALSE;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+CTracer_activity(CTracer *self, PyObject *args_unused)
+{
+ if (self->activity) {
+ Py_RETURN_TRUE;
+ }
+ else {
+ Py_RETURN_FALSE;
+ }
+}
+
+static PyObject *
+CTracer_reset_activity(CTracer *self, PyObject *args_unused)
+{
+ self->activity = FALSE;
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+CTracer_get_stats(CTracer *self, PyObject *args_unused)
+{
+#if COLLECT_STATS
+ return Py_BuildValue(
+ "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI,sI,sI}",
+ "calls", self->stats.calls,
+ "lines", self->stats.lines,
+ "returns", self->stats.returns,
+ "exceptions", self->stats.exceptions,
+ "others", self->stats.others,
+ "files", self->stats.files,
+ "missed_returns", self->stats.missed_returns,
+ "stack_reallocs", self->stats.stack_reallocs,
+ "stack_alloc", self->pdata_stack->alloc,
+ "errors", self->stats.errors,
+ "pycalls", self->stats.pycalls,
+ "start_context_calls", self->stats.start_context_calls
+ );
+#else
+ Py_RETURN_NONE;
+#endif /* COLLECT_STATS */
+}
+
+static PyMemberDef
+CTracer_members[] = {
+ { "should_trace", T_OBJECT, offsetof(CTracer, should_trace), 0,
+ PyDoc_STR("Function indicating whether to trace a file.") },
+
+ { "check_include", T_OBJECT, offsetof(CTracer, check_include), 0,
+ PyDoc_STR("Function indicating whether to include a file.") },
+
+ { "warn", T_OBJECT, offsetof(CTracer, warn), 0,
+ PyDoc_STR("Function for issuing warnings.") },
+
+ { "concur_id_func", T_OBJECT, offsetof(CTracer, concur_id_func), 0,
+ PyDoc_STR("Function for determining concurrency context") },
+
+ { "data", T_OBJECT, offsetof(CTracer, data), 0,
+ PyDoc_STR("The raw dictionary of trace data.") },
+
+ { "file_tracers", T_OBJECT, offsetof(CTracer, file_tracers), 0,
+ PyDoc_STR("Mapping from file name to plugin name.") },
+
+ { "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0,
+ PyDoc_STR("Dictionary caching should_trace results.") },
+
+ { "trace_arcs", T_OBJECT, offsetof(CTracer, trace_arcs), 0,
+ PyDoc_STR("Should we trace arcs, or just lines?") },
+
+ { "should_start_context", T_OBJECT, offsetof(CTracer, should_start_context), 0,
+ PyDoc_STR("Function for starting contexts.") },
+
+ { "switch_context", T_OBJECT, offsetof(CTracer, switch_context), 0,
+ PyDoc_STR("Function for switching to a new context.") },
+
+ { "disable_plugin", T_OBJECT, offsetof(CTracer, disable_plugin), 0,
+ PyDoc_STR("Function for disabling a plugin.") },
+
+ { NULL }
+};
+
+static PyMethodDef
+CTracer_methods[] = {
+ { "start", (PyCFunction) CTracer_start, METH_VARARGS,
+ PyDoc_STR("Start the tracer") },
+
+ { "stop", (PyCFunction) CTracer_stop, METH_VARARGS,
+ PyDoc_STR("Stop the tracer") },
+
+ { "get_stats", (PyCFunction) CTracer_get_stats, METH_VARARGS,
+ PyDoc_STR("Get statistics about the tracing") },
+
+ { "activity", (PyCFunction) CTracer_activity, METH_VARARGS,
+ PyDoc_STR("Has there been any activity?") },
+
+ { "reset_activity", (PyCFunction) CTracer_reset_activity, METH_VARARGS,
+ PyDoc_STR("Reset the activity flag") },
+
+ { NULL }
+};
+
+PyTypeObject
+CTracerType = {
+ MyType_HEAD_INIT
+ "coverage.CTracer", /*tp_name*/
+ sizeof(CTracer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)CTracer_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ (ternaryfunc)CTracer_call, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "CTracer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ CTracer_methods, /* tp_methods */
+ CTracer_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)CTracer_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/contrib/python/coverage/py2/coverage/ctracer/tracer.h b/contrib/python/coverage/py2/coverage/ctracer/tracer.h
new file mode 100644
index 0000000000..8994a9e3d6
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/tracer.h
@@ -0,0 +1,75 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_TRACER_H
+#define _COVERAGE_TRACER_H
+
+#include "util.h"
+#include "structmember.h"
+#include "frameobject.h"
+#include "opcode.h"
+
+#include "datastack.h"
+
+/* The CTracer type. */
+
+typedef struct CTracer {
+ PyObject_HEAD
+
+ /* Python objects manipulated directly by the Collector class. */
+ PyObject * should_trace;
+ PyObject * check_include;
+ PyObject * warn;
+ PyObject * concur_id_func;
+ PyObject * data;
+ PyObject * file_tracers;
+ PyObject * should_trace_cache;
+ PyObject * trace_arcs;
+ PyObject * should_start_context;
+ PyObject * switch_context;
+ PyObject * disable_plugin;
+
+ /* Has the tracer been started? */
+ BOOL started;
+ /* Are we tracing arcs, or just lines? */
+ BOOL tracing_arcs;
+ /* Have we had any activity? */
+ BOOL activity;
+ /* The current dynamic context. */
+ PyObject * context;
+
+ /*
+ The data stack is a stack of dictionaries. Each dictionary collects
+ data for a single source file. The data stack parallels the call stack:
+ each call pushes the new frame's file data onto the data stack, and each
+ return pops file data off.
+
+ The file data is a dictionary whose form depends on the tracing options.
+ If tracing arcs, the keys are line number pairs. If not tracing arcs,
+ the keys are line numbers. In both cases, the value is irrelevant
+ (None).
+ */
+
+ DataStack data_stack; /* Used if we aren't doing concurrency. */
+
+ PyObject * data_stack_index; /* Used if we are doing concurrency. */
+ DataStack * data_stacks;
+ int data_stacks_alloc;
+ int data_stacks_used;
+ DataStack * pdata_stack;
+
+ /* The current file's data stack entry. */
+ DataStackEntry * pcur_entry;
+
+ /* The parent frame for the last exception event, to fix missing returns. */
+ PyFrameObject * last_exc_back;
+ int last_exc_firstlineno;
+
+ Stats stats;
+} CTracer;
+
+int CTracer_intern_strings(void);
+
+extern PyTypeObject CTracerType;
+
+#endif /* _COVERAGE_TRACER_H */
diff --git a/contrib/python/coverage/py2/coverage/ctracer/util.h b/contrib/python/coverage/py2/coverage/ctracer/util.h
new file mode 100644
index 0000000000..5cba9b3096
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/ctracer/util.h
@@ -0,0 +1,67 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_UTIL_H
+#define _COVERAGE_UTIL_H
+
+#include <Python.h>
+
+/* Compile-time debugging helpers */
+#undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
+#undef TRACE_LOG /* Define to log our bookkeeping. */
+#undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
+#undef DO_NOTHING /* Define this to make the tracer do nothing. */
+
+/* Py 2.x and 3.x compatibility */
+
+#if PY_MAJOR_VERSION >= 3
+
+#define MyText_Type PyUnicode_Type
+#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o)
+#define MyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o)
+#define MyBytes_AS_STRING(o) PyBytes_AS_STRING(o)
+#define MyText_AsString(o) PyUnicode_AsUTF8(o)
+#define MyText_FromFormat PyUnicode_FromFormat
+#define MyInt_FromInt(i) PyLong_FromLong((long)i)
+#define MyInt_AsInt(o) (int)PyLong_AsLong(o)
+#define MyText_InternFromString(s) PyUnicode_InternFromString(s)
+
+#define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
+
+#else
+
+#define MyText_Type PyString_Type
+#define MyText_AS_BYTES(o) (Py_INCREF(o), o)
+#define MyBytes_GET_SIZE(o) PyString_GET_SIZE(o)
+#define MyBytes_AS_STRING(o) PyString_AS_STRING(o)
+#define MyText_AsString(o) PyString_AsString(o)
+#define MyText_FromFormat PyUnicode_FromFormat
+#define MyInt_FromInt(i) PyInt_FromLong((long)i)
+#define MyInt_AsInt(o) (int)PyInt_AsLong(o)
+#define MyText_InternFromString(s) PyString_InternFromString(s)
+
+#define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
+
+#endif /* Py3k */
+
+// Undocumented, and not in all 2.7.x, so our own copy of it.
+#define My_XSETREF(op, op2) \
+ do { \
+ PyObject *_py_tmp = (PyObject *)(op); \
+ (op) = (op2); \
+ Py_XDECREF(_py_tmp); \
+ } while (0)
+
+/* The values returned to indicate ok or error. */
+#define RET_OK 0
+#define RET_ERROR -1
+
+/* Nicer booleans */
+typedef int BOOL;
+#define FALSE 0
+#define TRUE 1
+
+/* Only for extreme machete-mode debugging! */
+#define CRASH { printf("*** CRASH! ***\n"); *((int*)1) = 1; }
+
+#endif /* _COVERAGE_UTIL_H */
diff --git a/contrib/python/coverage/py2/coverage/data.py b/contrib/python/coverage/py2/coverage/data.py
new file mode 100644
index 0000000000..5dd1dfe3f0
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/data.py
@@ -0,0 +1,125 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Coverage data for coverage.py.
+
+This file had the 4.x JSON data support, which is now gone. This file still
+has storage-agnostic helpers, and is kept to avoid changing too many imports.
+CoverageData is now defined in sqldata.py, and imported here to keep the
+imports working.
+
+"""
+
+import glob
+import os.path
+
+from coverage.misc import CoverageException, file_be_gone
+from coverage.sqldata import CoverageData
+
+
+def line_counts(data, fullpath=False):
+ """Return a dict summarizing the line coverage data.
+
+ Keys are based on the file names, and values are the number of executed
+ lines. If `fullpath` is true, then the keys are the full pathnames of
+ the files, otherwise they are the basenames of the files.
+
+ Returns a dict mapping file names to counts of lines.
+
+ """
+ summ = {}
+ if fullpath:
+ filename_fn = lambda f: f
+ else:
+ filename_fn = os.path.basename
+ for filename in data.measured_files():
+ summ[filename_fn(filename)] = len(data.lines(filename))
+ return summ
+
+
+def add_data_to_hash(data, filename, hasher):
+ """Contribute `filename`'s data to the `hasher`.
+
+ `hasher` is a `coverage.misc.Hasher` instance to be updated with
+ the file's data. It should only get the results data, not the run
+ data.
+
+ """
+ if data.has_arcs():
+ hasher.update(sorted(data.arcs(filename) or []))
+ else:
+ hasher.update(sorted(data.lines(filename) or []))
+ hasher.update(data.file_tracer(filename))
+
+
+def combine_parallel_data(data, aliases=None, data_paths=None, strict=False, keep=False):
+ """Combine a number of data files together.
+
+ Treat `data.filename` as a file prefix, and combine the data from all
+ of the data files starting with that prefix plus a dot.
+
+ If `aliases` is provided, it's a `PathAliases` object that is used to
+ re-map paths to match the local machine's.
+
+ If `data_paths` is provided, it is a list of directories or files to
+ combine. Directories are searched for files that start with
+ `data.filename` plus dot as a prefix, and those files are combined.
+
+ If `data_paths` is not provided, then the directory portion of
+ `data.filename` is used as the directory to search for data files.
+
+ Unless `keep` is True every data file found and combined is then deleted from disk. If a file
+ cannot be read, a warning will be issued, and the file will not be
+ deleted.
+
+ If `strict` is true, and no files are found to combine, an error is
+ raised.
+
+ """
+ # Because of the os.path.abspath in the constructor, data_dir will
+ # never be an empty string.
+ data_dir, local = os.path.split(data.base_filename())
+ localdot = local + '.*'
+
+ data_paths = data_paths or [data_dir]
+ files_to_combine = []
+ for p in data_paths:
+ if os.path.isfile(p):
+ files_to_combine.append(os.path.abspath(p))
+ elif os.path.isdir(p):
+ pattern = os.path.join(os.path.abspath(p), localdot)
+ files_to_combine.extend(glob.glob(pattern))
+ else:
+ raise CoverageException("Couldn't combine from non-existent path '%s'" % (p,))
+
+ if strict and not files_to_combine:
+ raise CoverageException("No data to combine")
+
+ files_combined = 0
+ for f in files_to_combine:
+ if f == data.data_filename():
+ # Sometimes we are combining into a file which is one of the
+ # parallel files. Skip that file.
+ if data._debug.should('dataio'):
+ data._debug.write("Skipping combining ourself: %r" % (f,))
+ continue
+ if data._debug.should('dataio'):
+ data._debug.write("Combining data file %r" % (f,))
+ try:
+ new_data = CoverageData(f, debug=data._debug)
+ new_data.read()
+ except CoverageException as exc:
+ if data._warn:
+ # The CoverageException has the file name in it, so just
+ # use the message as the warning.
+ data._warn(str(exc))
+ else:
+ data.update(new_data, aliases=aliases)
+ files_combined += 1
+ if not keep:
+ if data._debug.should('dataio'):
+ data._debug.write("Deleting combined data file %r" % (f,))
+ file_be_gone(f)
+
+ if strict and not files_combined:
+ raise CoverageException("No usable data files")
diff --git a/contrib/python/coverage/py2/coverage/debug.py b/contrib/python/coverage/py2/coverage/debug.py
new file mode 100644
index 0000000000..194f16f50d
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/debug.py
@@ -0,0 +1,406 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Control of and utilities for debugging."""
+
+import contextlib
+import functools
+import inspect
+import itertools
+import os
+import pprint
+import sys
+try:
+ import _thread
+except ImportError:
+ import thread as _thread
+
+from coverage.backward import reprlib, StringIO
+from coverage.misc import isolate_module
+
+os = isolate_module(os)
+
+
+# When debugging, it can be helpful to force some options, especially when
+# debugging the configuration mechanisms you usually use to control debugging!
+# This is a list of forced debugging options.
+FORCED_DEBUG = []
+FORCED_DEBUG_FILE = None
+
+
+class DebugControl(object):
+ """Control and output for debugging."""
+
+ show_repr_attr = False # For SimpleReprMixin
+
+ def __init__(self, options, output):
+ """Configure the options and output file for debugging."""
+ self.options = list(options) + FORCED_DEBUG
+ self.suppress_callers = False
+
+ filters = []
+ if self.should('pid'):
+ filters.append(add_pid_and_tid)
+ self.output = DebugOutputFile.get_one(
+ output,
+ show_process=self.should('process'),
+ filters=filters,
+ )
+ self.raw_output = self.output.outfile
+
+ def __repr__(self):
+ return "<DebugControl options=%r raw_output=%r>" % (self.options, self.raw_output)
+
+ def should(self, option):
+ """Decide whether to output debug information in category `option`."""
+ if option == "callers" and self.suppress_callers:
+ return False
+ return (option in self.options)
+
+ @contextlib.contextmanager
+ def without_callers(self):
+ """A context manager to prevent call stacks from being logged."""
+ old = self.suppress_callers
+ self.suppress_callers = True
+ try:
+ yield
+ finally:
+ self.suppress_callers = old
+
+ def write(self, msg):
+ """Write a line of debug output.
+
+ `msg` is the line to write. A newline will be appended.
+
+ """
+ self.output.write(msg+"\n")
+ if self.should('self'):
+ caller_self = inspect.stack()[1][0].f_locals.get('self')
+ if caller_self is not None:
+ self.output.write("self: {!r}\n".format(caller_self))
+ if self.should('callers'):
+ dump_stack_frames(out=self.output, skip=1)
+ self.output.flush()
+
+
+class DebugControlString(DebugControl):
+ """A `DebugControl` that writes to a StringIO, for testing."""
+ def __init__(self, options):
+ super(DebugControlString, self).__init__(options, StringIO())
+
+ def get_output(self):
+ """Get the output text from the `DebugControl`."""
+ return self.raw_output.getvalue()
+
+
+class NoDebugging(object):
+ """A replacement for DebugControl that will never try to do anything."""
+ def should(self, option): # pylint: disable=unused-argument
+ """Should we write debug messages? Never."""
+ return False
+
+
+def info_header(label):
+ """Make a nice header string."""
+ return "--{:-<60s}".format(" "+label+" ")
+
+
+def info_formatter(info):
+ """Produce a sequence of formatted lines from info.
+
+ `info` is a sequence of pairs (label, data). The produced lines are
+ nicely formatted, ready to print.
+
+ """
+ info = list(info)
+ if not info:
+ return
+ label_len = 30
+ assert all(len(l) < label_len for l, _ in info)
+ for label, data in info:
+ if data == []:
+ data = "-none-"
+ if isinstance(data, (list, set, tuple)):
+ prefix = "%*s:" % (label_len, label)
+ for e in data:
+ yield "%*s %s" % (label_len+1, prefix, e)
+ prefix = ""
+ else:
+ yield "%*s: %s" % (label_len, label, data)
+
+
+def write_formatted_info(writer, header, info):
+ """Write a sequence of (label,data) pairs nicely."""
+ writer.write(info_header(header))
+ for line in info_formatter(info):
+ writer.write(" %s" % line)
+
+
+def short_stack(limit=None, skip=0):
+ """Return a string summarizing the call stack.
+
+ The string is multi-line, with one line per stack frame. Each line shows
+ the function name, the file name, and the line number:
+
+ ...
+ start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95
+ import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81
+ import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159
+ ...
+
+ `limit` is the number of frames to include, defaulting to all of them.
+
+ `skip` is the number of frames to skip, so that debugging functions can
+ call this and not be included in the result.
+
+ """
+ stack = inspect.stack()[limit:skip:-1]
+ return "\n".join("%30s : %s:%d" % (t[3], t[1], t[2]) for t in stack)
+
+
+def dump_stack_frames(limit=None, out=None, skip=0):
+ """Print a summary of the stack to stdout, or someplace else."""
+ out = out or sys.stdout
+ out.write(short_stack(limit=limit, skip=skip+1))
+ out.write("\n")
+
+
+def clipped_repr(text, numchars=50):
+ """`repr(text)`, but limited to `numchars`."""
+ r = reprlib.Repr()
+ r.maxstring = numchars
+ return r.repr(text)
+
+
+def short_id(id64):
+ """Given a 64-bit id, make a shorter 16-bit one."""
+ id16 = 0
+ for offset in range(0, 64, 16):
+ id16 ^= id64 >> offset
+ return id16 & 0xFFFF
+
+
+def add_pid_and_tid(text):
+ """A filter to add pid and tid to debug messages."""
+ # Thread ids are useful, but too long. Make a shorter one.
+ tid = "{:04x}".format(short_id(_thread.get_ident()))
+ text = "{:5d}.{}: {}".format(os.getpid(), tid, text)
+ return text
+
+
+class SimpleReprMixin(object):
+ """A mixin implementing a simple __repr__."""
+ simple_repr_ignore = ['simple_repr_ignore', '$coverage.object_id']
+
+ def __repr__(self):
+ show_attrs = (
+ (k, v) for k, v in self.__dict__.items()
+ if getattr(v, "show_repr_attr", True)
+ and not callable(v)
+ and k not in self.simple_repr_ignore
+ )
+ return "<{klass} @0x{id:x} {attrs}>".format(
+ klass=self.__class__.__name__,
+ id=id(self),
+ attrs=" ".join("{}={!r}".format(k, v) for k, v in show_attrs),
+ )
+
+
+def simplify(v): # pragma: debugging
+ """Turn things which are nearly dict/list/etc into dict/list/etc."""
+ if isinstance(v, dict):
+ return {k:simplify(vv) for k, vv in v.items()}
+ elif isinstance(v, (list, tuple)):
+ return type(v)(simplify(vv) for vv in v)
+ elif hasattr(v, "__dict__"):
+ return simplify({'.'+k: v for k, v in v.__dict__.items()})
+ else:
+ return v
+
+
+def pp(v): # pragma: debugging
+ """Debug helper to pretty-print data, including SimpleNamespace objects."""
+ # Might not be needed in 3.9+
+ pprint.pprint(simplify(v))
+
+
+def filter_text(text, filters):
+ """Run `text` through a series of filters.
+
+ `filters` is a list of functions. Each takes a string and returns a
+ string. Each is run in turn.
+
+ Returns: the final string that results after all of the filters have
+ run.
+
+ """
+ clean_text = text.rstrip()
+ ending = text[len(clean_text):]
+ text = clean_text
+ for fn in filters:
+ lines = []
+ for line in text.splitlines():
+ lines.extend(fn(line).splitlines())
+ text = "\n".join(lines)
+ return text + ending
+
+
+class CwdTracker(object): # pragma: debugging
+ """A class to add cwd info to debug messages."""
+ def __init__(self):
+ self.cwd = None
+
+ def filter(self, text):
+ """Add a cwd message for each new cwd."""
+ cwd = os.getcwd()
+ if cwd != self.cwd:
+ text = "cwd is now {!r}\n".format(cwd) + text
+ self.cwd = cwd
+ return text
+
+
+class DebugOutputFile(object): # pragma: debugging
+ """A file-like object that includes pid and cwd information."""
+ def __init__(self, outfile, show_process, filters):
+ self.outfile = outfile
+ self.show_process = show_process
+ self.filters = list(filters)
+
+ if self.show_process:
+ self.filters.insert(0, CwdTracker().filter)
+ self.write("New process: executable: %r\n" % (sys.executable,))
+ self.write("New process: cmd: %r\n" % (getattr(sys, 'argv', None),))
+ if hasattr(os, 'getppid'):
+ self.write("New process: pid: %r, parent pid: %r\n" % (os.getpid(), os.getppid()))
+
+ SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one'
+
+ @classmethod
+ def get_one(cls, fileobj=None, show_process=True, filters=(), interim=False):
+ """Get a DebugOutputFile.
+
+ If `fileobj` is provided, then a new DebugOutputFile is made with it.
+
+ If `fileobj` isn't provided, then a file is chosen
+ (COVERAGE_DEBUG_FILE, or stderr), and a process-wide singleton
+ DebugOutputFile is made.
+
+ `show_process` controls whether the debug file adds process-level
+ information, and filters is a list of other message filters to apply.
+
+ `filters` are the text filters to apply to the stream to annotate with
+ pids, etc.
+
+ If `interim` is true, then a future `get_one` can replace this one.
+
+ """
+ if fileobj is not None:
+ # Make DebugOutputFile around the fileobj passed.
+ return cls(fileobj, show_process, filters)
+
+ # Because of the way igor.py deletes and re-imports modules,
+ # this class can be defined more than once. But we really want
+ # a process-wide singleton. So stash it in sys.modules instead of
+ # on a class attribute. Yes, this is aggressively gross.
+ the_one, is_interim = sys.modules.get(cls.SYS_MOD_NAME, (None, True))
+ if the_one is None or is_interim:
+ if fileobj is None:
+ debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE)
+ if debug_file_name:
+ fileobj = open(debug_file_name, "a")
+ else:
+ fileobj = sys.stderr
+ the_one = cls(fileobj, show_process, filters)
+ sys.modules[cls.SYS_MOD_NAME] = (the_one, interim)
+ return the_one
+
+ def write(self, text):
+ """Just like file.write, but filter through all our filters."""
+ self.outfile.write(filter_text(text, self.filters))
+ self.outfile.flush()
+
+ def flush(self):
+ """Flush our file."""
+ self.outfile.flush()
+
+
+def log(msg, stack=False): # pragma: debugging
+ """Write a log message as forcefully as possible."""
+ out = DebugOutputFile.get_one(interim=True)
+ out.write(msg+"\n")
+ if stack:
+ dump_stack_frames(out=out, skip=1)
+
+
+def decorate_methods(decorator, butnot=(), private=False): # pragma: debugging
+ """A class decorator to apply a decorator to methods."""
+ def _decorator(cls):
+ for name, meth in inspect.getmembers(cls, inspect.isroutine):
+ if name not in cls.__dict__:
+ continue
+ if name != "__init__":
+ if not private and name.startswith("_"):
+ continue
+ if name in butnot:
+ continue
+ setattr(cls, name, decorator(meth))
+ return cls
+ return _decorator
+
+
+def break_in_pudb(func): # pragma: debugging
+ """A function decorator to stop in the debugger for each call."""
+ @functools.wraps(func)
+ def _wrapper(*args, **kwargs):
+ import pudb
+ sys.stdout = sys.__stdout__
+ pudb.set_trace()
+ return func(*args, **kwargs)
+ return _wrapper
+
+
+OBJ_IDS = itertools.count()
+CALLS = itertools.count()
+OBJ_ID_ATTR = "$coverage.object_id"
+
+def show_calls(show_args=True, show_stack=False, show_return=False): # pragma: debugging
+ """A method decorator to debug-log each call to the function."""
+ def _decorator(func):
+ @functools.wraps(func)
+ def _wrapper(self, *args, **kwargs):
+ oid = getattr(self, OBJ_ID_ATTR, None)
+ if oid is None:
+ oid = "{:08d} {:04d}".format(os.getpid(), next(OBJ_IDS))
+ setattr(self, OBJ_ID_ATTR, oid)
+ extra = ""
+ if show_args:
+ eargs = ", ".join(map(repr, args))
+ ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items())
+ extra += "("
+ extra += eargs
+ if eargs and ekwargs:
+ extra += ", "
+ extra += ekwargs
+ extra += ")"
+ if show_stack:
+ extra += " @ "
+ extra += "; ".join(_clean_stack_line(l) for l in short_stack().splitlines())
+ callid = next(CALLS)
+ msg = "{} {:04d} {}{}\n".format(oid, callid, func.__name__, extra)
+ DebugOutputFile.get_one(interim=True).write(msg)
+ ret = func(self, *args, **kwargs)
+ if show_return:
+ msg = "{} {:04d} {} return {!r}\n".format(oid, callid, func.__name__, ret)
+ DebugOutputFile.get_one(interim=True).write(msg)
+ return ret
+ return _wrapper
+ return _decorator
+
+
+def _clean_stack_line(s): # pragma: debugging
+ """Simplify some paths in a stack trace, for compactness."""
+ s = s.strip()
+ s = s.replace(os.path.dirname(__file__) + '/', '')
+ s = s.replace(os.path.dirname(os.__file__) + '/', '')
+ s = s.replace(sys.prefix + '/', '')
+ return s
diff --git a/contrib/python/coverage/py2/coverage/disposition.py b/contrib/python/coverage/py2/coverage/disposition.py
new file mode 100644
index 0000000000..9b9a997d8a
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/disposition.py
@@ -0,0 +1,37 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Simple value objects for tracking what to do with files."""
+
+
+class FileDisposition(object):
+ """A simple value type for recording what to do with a file."""
+ pass
+
+
+# FileDisposition "methods": FileDisposition is a pure value object, so it can
+# be implemented in either C or Python. Acting on them is done with these
+# functions.
+
+def disposition_init(cls, original_filename):
+ """Construct and initialize a new FileDisposition object."""
+ disp = cls()
+ disp.original_filename = original_filename
+ disp.canonical_filename = original_filename
+ disp.source_filename = None
+ disp.trace = False
+ disp.reason = ""
+ disp.file_tracer = None
+ disp.has_dynamic_filename = False
+ return disp
+
+
+def disposition_debug_msg(disp):
+ """Make a nice debug message of what the FileDisposition is doing."""
+ if disp.trace:
+ msg = "Tracing %r" % (disp.original_filename,)
+ if disp.file_tracer:
+ msg += ": will be traced by %r" % disp.file_tracer
+ else:
+ msg = "Not tracing %r: %s" % (disp.original_filename, disp.reason)
+ return msg
diff --git a/contrib/python/coverage/py2/coverage/env.py b/contrib/python/coverage/py2/coverage/env.py
new file mode 100644
index 0000000000..ea78a5be89
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/env.py
@@ -0,0 +1,130 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Determine facts about the environment."""
+
+import os
+import platform
+import sys
+
+# Operating systems.
+WINDOWS = sys.platform == "win32"
+LINUX = sys.platform.startswith("linux")
+
+# Python implementations.
+CPYTHON = (platform.python_implementation() == "CPython")
+PYPY = (platform.python_implementation() == "PyPy")
+JYTHON = (platform.python_implementation() == "Jython")
+IRONPYTHON = (platform.python_implementation() == "IronPython")
+
+# Python versions. We amend version_info with one more value, a zero if an
+# official version, or 1 if built from source beyond an official version.
+PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),)
+PY2 = PYVERSION < (3, 0)
+PY3 = PYVERSION >= (3, 0)
+
+if PYPY:
+ PYPYVERSION = sys.pypy_version_info
+
+PYPY2 = PYPY and PY2
+PYPY3 = PYPY and PY3
+
+# Python behavior.
+class PYBEHAVIOR(object):
+ """Flags indicating this Python's behavior."""
+
+ pep626 = CPYTHON and (PYVERSION > (3, 10, 0, 'alpha', 4))
+
+ # Is "if __debug__" optimized away?
+ if PYPY3:
+ optimize_if_debug = True
+ elif PYPY2:
+ optimize_if_debug = False
+ else:
+ optimize_if_debug = not pep626
+
+ # Is "if not __debug__" optimized away?
+ optimize_if_not_debug = (not PYPY) and (PYVERSION >= (3, 7, 0, 'alpha', 4))
+ if pep626:
+ optimize_if_not_debug = False
+ if PYPY3:
+ optimize_if_not_debug = True
+
+ # Is "if not __debug__" optimized away even better?
+ optimize_if_not_debug2 = (not PYPY) and (PYVERSION >= (3, 8, 0, 'beta', 1))
+ if pep626:
+ optimize_if_not_debug2 = False
+
+ # Do we have yield-from?
+ yield_from = (PYVERSION >= (3, 3))
+
+ # Do we have PEP 420 namespace packages?
+ namespaces_pep420 = (PYVERSION >= (3, 3))
+
+ # Do .pyc files have the source file size recorded in them?
+ size_in_pyc = (PYVERSION >= (3, 3))
+
+ # Do we have async and await syntax?
+ async_syntax = (PYVERSION >= (3, 5))
+
+ # PEP 448 defined additional unpacking generalizations
+ unpackings_pep448 = (PYVERSION >= (3, 5))
+
+ # Can co_lnotab have negative deltas?
+ negative_lnotab = (PYVERSION >= (3, 6)) and not (PYPY and PYPYVERSION < (7, 2))
+
+ # Do .pyc files conform to PEP 552? Hash-based pyc's.
+ hashed_pyc_pep552 = (PYVERSION >= (3, 7, 0, 'alpha', 4))
+
+ # Python 3.7.0b3 changed the behavior of the sys.path[0] entry for -m. It
+ # used to be an empty string (meaning the current directory). It changed
+ # to be the actual path to the current directory, so that os.chdir wouldn't
+ # affect the outcome.
+ actual_syspath0_dash_m = CPYTHON and (PYVERSION >= (3, 7, 0, 'beta', 3))
+
+ # 3.7 changed how functions with only docstrings are numbered.
+ docstring_only_function = (not PYPY) and ((3, 7, 0, 'beta', 5) <= PYVERSION <= (3, 10))
+
+ # When a break/continue/return statement in a try block jumps to a finally
+ # block, does the finally block do the break/continue/return (pre-3.8), or
+ # does the finally jump back to the break/continue/return (3.8) to do the
+ # work?
+ finally_jumps_back = ((3, 8) <= PYVERSION < (3, 10))
+
+ # When a function is decorated, does the trace function get called for the
+ # @-line and also the def-line (new behavior in 3.8)? Or just the @-line
+ # (old behavior)?
+ trace_decorated_def = (PYVERSION >= (3, 8))
+
+ # Are while-true loops optimized into absolute jumps with no loop setup?
+ nix_while_true = (PYVERSION >= (3, 8))
+
+ # Python 3.9a1 made sys.argv[0] and other reported files absolute paths.
+ report_absolute_files = (PYVERSION >= (3, 9))
+
+ # Lines after break/continue/return/raise are no longer compiled into the
+ # bytecode. They used to be marked as missing, now they aren't executable.
+ omit_after_jump = pep626
+
+ # PyPy has always omitted statements after return.
+ omit_after_return = omit_after_jump or PYPY
+
+ # Modules used to have firstlineno equal to the line number of the first
+ # real line of code. Now they always start at 1.
+ module_firstline_1 = pep626
+
+ # Are "if 0:" lines (and similar) kept in the compiled code?
+ keep_constant_test = pep626
+
+# Coverage.py specifics.
+
+# Are we using the C-implemented trace function?
+C_TRACER = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c'
+
+# Are we coverage-measuring ourselves?
+METACOV = os.getenv('COVERAGE_COVERAGE', '') != ''
+
+# Are we running our test suite?
+# Even when running tests, you can use COVERAGE_TESTING=0 to disable the
+# test-specific behavior like contracts.
+TESTING = os.getenv('COVERAGE_TESTING', '') == 'True'
diff --git a/contrib/python/coverage/py2/coverage/execfile.py b/contrib/python/coverage/py2/coverage/execfile.py
new file mode 100644
index 0000000000..29409d517a
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/execfile.py
@@ -0,0 +1,362 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Execute files of Python code."""
+
+import inspect
+import marshal
+import os
+import struct
+import sys
+import types
+
+from coverage import env
+from coverage.backward import BUILTINS
+from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
+from coverage.files import canonical_filename, python_reported_file
+from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module
+from coverage.phystokens import compile_unicode
+from coverage.python import get_python_source
+
+os = isolate_module(os)
+
+
+class DummyLoader(object):
+ """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
+
+ Currently only implements the .fullname attribute
+ """
+ def __init__(self, fullname, *_args):
+ self.fullname = fullname
+
+
+if importlib_util_find_spec:
+ def find_module(modulename):
+ """Find the module named `modulename`.
+
+ Returns the file path of the module, the name of the enclosing
+ package, and the spec.
+ """
+ try:
+ spec = importlib_util_find_spec(modulename)
+ except ImportError as err:
+ raise NoSource(str(err))
+ if not spec:
+ raise NoSource("No module named %r" % (modulename,))
+ pathname = spec.origin
+ packagename = spec.name
+ if spec.submodule_search_locations:
+ mod_main = modulename + ".__main__"
+ spec = importlib_util_find_spec(mod_main)
+ if not spec:
+ raise NoSource(
+ "No module named %s; "
+ "%r is a package and cannot be directly executed"
+ % (mod_main, modulename)
+ )
+ pathname = spec.origin
+ packagename = spec.name
+ packagename = packagename.rpartition(".")[0]
+ return pathname, packagename, spec
+else:
+ def find_module(modulename):
+ """Find the module named `modulename`.
+
+ Returns the file path of the module, the name of the enclosing
+ package, and None (where a spec would have been).
+ """
+ openfile = None
+ glo, loc = globals(), locals()
+ try:
+ # Search for the module - inside its parent package, if any - using
+ # standard import mechanics.
+ if '.' in modulename:
+ packagename, name = modulename.rsplit('.', 1)
+ package = __import__(packagename, glo, loc, ['__path__'])
+ searchpath = package.__path__
+ else:
+ packagename, name = None, modulename
+ searchpath = None # "top-level search" in imp.find_module()
+ openfile, pathname, _ = imp.find_module(name, searchpath)
+
+ # Complain if this is a magic non-file module.
+ if openfile is None and pathname is None:
+ raise NoSource(
+ "module does not live in a file: %r" % modulename
+ )
+
+ # If `modulename` is actually a package, not a mere module, then we
+ # pretend to be Python 2.7 and try running its __main__.py script.
+ if openfile is None:
+ packagename = modulename
+ name = '__main__'
+ package = __import__(packagename, glo, loc, ['__path__'])
+ searchpath = package.__path__
+ openfile, pathname, _ = imp.find_module(name, searchpath)
+ except ImportError as err:
+ raise NoSource(str(err))
+ finally:
+ if openfile:
+ openfile.close()
+
+ return pathname, packagename, None
+
+
+class PyRunner(object):
+ """Multi-stage execution of Python code.
+
+ This is meant to emulate real Python execution as closely as possible.
+
+ """
+ def __init__(self, args, as_module=False):
+ self.args = args
+ self.as_module = as_module
+
+ self.arg0 = args[0]
+ self.package = self.modulename = self.pathname = self.loader = self.spec = None
+
+ def prepare(self):
+ """Set sys.path properly.
+
+ This needs to happen before any importing, and without importing anything.
+ """
+ if self.as_module:
+ if env.PYBEHAVIOR.actual_syspath0_dash_m:
+ path0 = os.getcwd()
+ else:
+ path0 = ""
+ elif os.path.isdir(self.arg0):
+ # Running a directory means running the __main__.py file in that
+ # directory.
+ path0 = self.arg0
+ else:
+ path0 = os.path.abspath(os.path.dirname(self.arg0))
+
+ if os.path.isdir(sys.path[0]):
+ # sys.path fakery. If we are being run as a command, then sys.path[0]
+ # is the directory of the "coverage" script. If this is so, replace
+ # sys.path[0] with the directory of the file we're running, or the
+ # current directory when running modules. If it isn't so, then we
+ # don't know what's going on, and just leave it alone.
+ top_file = inspect.stack()[-1][0].f_code.co_filename
+ sys_path_0_abs = os.path.abspath(sys.path[0])
+ top_file_dir_abs = os.path.abspath(os.path.dirname(top_file))
+ sys_path_0_abs = canonical_filename(sys_path_0_abs)
+ top_file_dir_abs = canonical_filename(top_file_dir_abs)
+ if sys_path_0_abs != top_file_dir_abs:
+ path0 = None
+
+ else:
+ # sys.path[0] is a file. Is the next entry the directory containing
+ # that file?
+ if sys.path[1] == os.path.dirname(sys.path[0]):
+ # Can it be right to always remove that?
+ del sys.path[1]
+
+ if path0 is not None:
+ sys.path[0] = python_reported_file(path0)
+
+ def _prepare2(self):
+ """Do more preparation to run Python code.
+
+ Includes finding the module to run and adjusting sys.argv[0].
+ This method is allowed to import code.
+
+ """
+ if self.as_module:
+ self.modulename = self.arg0
+ pathname, self.package, self.spec = find_module(self.modulename)
+ if self.spec is not None:
+ self.modulename = self.spec.name
+ self.loader = DummyLoader(self.modulename)
+ self.pathname = os.path.abspath(pathname)
+ self.args[0] = self.arg0 = self.pathname
+ elif os.path.isdir(self.arg0):
+ # Running a directory means running the __main__.py file in that
+ # directory.
+ for ext in [".py", ".pyc", ".pyo"]:
+ try_filename = os.path.join(self.arg0, "__main__" + ext)
+ if os.path.exists(try_filename):
+ self.arg0 = try_filename
+ break
+ else:
+ raise NoSource("Can't find '__main__' module in '%s'" % self.arg0)
+
+ if env.PY2:
+ self.arg0 = os.path.abspath(self.arg0)
+
+ # Make a spec. I don't know if this is the right way to do it.
+ try:
+ import importlib.machinery
+ except ImportError:
+ pass
+ else:
+ try_filename = python_reported_file(try_filename)
+ self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename)
+ self.spec.has_location = True
+ self.package = ""
+ self.loader = DummyLoader("__main__")
+ else:
+ if env.PY3:
+ self.loader = DummyLoader("__main__")
+
+ self.arg0 = python_reported_file(self.arg0)
+
+ def run(self):
+ """Run the Python code!"""
+
+ self._prepare2()
+
+ # Create a module to serve as __main__
+ main_mod = types.ModuleType('__main__')
+
+ from_pyc = self.arg0.endswith((".pyc", ".pyo"))
+ main_mod.__file__ = self.arg0
+ if from_pyc:
+ main_mod.__file__ = main_mod.__file__[:-1]
+ if self.package is not None:
+ main_mod.__package__ = self.package
+ main_mod.__loader__ = self.loader
+ if self.spec is not None:
+ main_mod.__spec__ = self.spec
+
+ main_mod.__builtins__ = BUILTINS
+
+ sys.modules['__main__'] = main_mod
+
+ # Set sys.argv properly.
+ sys.argv = self.args
+
+ try:
+ # Make a code object somehow.
+ if from_pyc:
+ code = make_code_from_pyc(self.arg0)
+ else:
+ code = make_code_from_py(self.arg0)
+ except CoverageException:
+ raise
+ except Exception as exc:
+ msg = "Couldn't run '{filename}' as Python code: {exc.__class__.__name__}: {exc}"
+ raise CoverageException(msg.format(filename=self.arg0, exc=exc))
+
+ # Execute the code object.
+ # Return to the original directory in case the test code exits in
+ # a non-existent directory.
+ cwd = os.getcwd()
+ try:
+ exec(code, main_mod.__dict__)
+ except SystemExit: # pylint: disable=try-except-raise
+ # The user called sys.exit(). Just pass it along to the upper
+ # layers, where it will be handled.
+ raise
+ except Exception:
+ # Something went wrong while executing the user code.
+ # Get the exc_info, and pack them into an exception that we can
+ # throw up to the outer loop. We peel one layer off the traceback
+ # so that the coverage.py code doesn't appear in the final printed
+ # traceback.
+ typ, err, tb = sys.exc_info()
+
+ # PyPy3 weirdness. If I don't access __context__, then somehow it
+ # is non-None when the exception is reported at the upper layer,
+ # and a nested exception is shown to the user. This getattr fixes
+ # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
+ getattr(err, '__context__', None)
+
+ # Call the excepthook.
+ try:
+ if hasattr(err, "__traceback__"):
+ err.__traceback__ = err.__traceback__.tb_next
+ sys.excepthook(typ, err, tb.tb_next)
+ except SystemExit: # pylint: disable=try-except-raise
+ raise
+ except Exception:
+ # Getting the output right in the case of excepthook
+ # shenanigans is kind of involved.
+ sys.stderr.write("Error in sys.excepthook:\n")
+ typ2, err2, tb2 = sys.exc_info()
+ err2.__suppress_context__ = True
+ if hasattr(err2, "__traceback__"):
+ err2.__traceback__ = err2.__traceback__.tb_next
+ sys.__excepthook__(typ2, err2, tb2.tb_next)
+ sys.stderr.write("\nOriginal exception was:\n")
+ raise ExceptionDuringRun(typ, err, tb.tb_next)
+ else:
+ sys.exit(1)
+ finally:
+ os.chdir(cwd)
+
+
+def run_python_module(args):
+ """Run a Python module, as though with ``python -m name args...``.
+
+ `args` is the argument array to present as sys.argv, including the first
+ element naming the module being executed.
+
+ This is a helper for tests, to encapsulate how to use PyRunner.
+
+ """
+ runner = PyRunner(args, as_module=True)
+ runner.prepare()
+ runner.run()
+
+
+def run_python_file(args):
+ """Run a Python file as if it were the main program on the command line.
+
+ `args` is the argument array to present as sys.argv, including the first
+ element naming the file being executed. `package` is the name of the
+ enclosing package, if any.
+
+ This is a helper for tests, to encapsulate how to use PyRunner.
+
+ """
+ runner = PyRunner(args, as_module=False)
+ runner.prepare()
+ runner.run()
+
+
+def make_code_from_py(filename):
+ """Get source from `filename` and make a code object of it."""
+ # Open the source file.
+ try:
+ source = get_python_source(filename)
+ except (IOError, NoSource):
+ raise NoSource("No file to run: '%s'" % filename)
+
+ code = compile_unicode(source, filename, "exec")
+ return code
+
+
+def make_code_from_pyc(filename):
+ """Get a code object from a .pyc file."""
+ try:
+ fpyc = open(filename, "rb")
+ except IOError:
+ raise NoCode("No file to run: '%s'" % filename)
+
+ with fpyc:
+ # First four bytes are a version-specific magic number. It has to
+ # match or we won't run the file.
+ magic = fpyc.read(4)
+ if magic != PYC_MAGIC_NUMBER:
+ raise NoCode("Bad magic number in .pyc file: {} != {}".format(magic, PYC_MAGIC_NUMBER))
+
+ date_based = True
+ if env.PYBEHAVIOR.hashed_pyc_pep552:
+ flags = struct.unpack('<L', fpyc.read(4))[0]
+ hash_based = flags & 0x01
+ if hash_based:
+ fpyc.read(8) # Skip the hash.
+ date_based = False
+ if date_based:
+ # Skip the junk in the header that we don't need.
+ fpyc.read(4) # Skip the moddate.
+ if env.PYBEHAVIOR.size_in_pyc:
+ # 3.3 added another long to the header (size), skip it.
+ fpyc.read(4)
+
+ # The rest of the file is the code object we want.
+ code = marshal.load(fpyc)
+
+ return code
diff --git a/contrib/python/coverage/py2/coverage/files.py b/contrib/python/coverage/py2/coverage/files.py
new file mode 100644
index 0000000000..5133ad07f3
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/files.py
@@ -0,0 +1,441 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""File wrangling."""
+
+import hashlib
+import fnmatch
+import ntpath
+import os
+import os.path
+import posixpath
+import re
+import sys
+
+from coverage import env
+from coverage.backward import unicode_class
+from coverage.misc import contract, CoverageException, join_regex, isolate_module
+
+
+os = isolate_module(os)
+
+
+def set_relative_directory():
+ """Set the directory that `relative_filename` will be relative to."""
+ global RELATIVE_DIR, CANONICAL_FILENAME_CACHE
+
+ # The absolute path to our current directory.
+ RELATIVE_DIR = os.path.normcase(abs_file(os.curdir) + os.sep)
+
+ # Cache of results of calling the canonical_filename() method, to
+ # avoid duplicating work.
+ CANONICAL_FILENAME_CACHE = {}
+
+
+def relative_directory():
+ """Return the directory that `relative_filename` is relative to."""
+ return RELATIVE_DIR
+
+
+@contract(returns='unicode')
+def relative_filename(filename):
+ """Return the relative form of `filename`.
+
+ The file name will be relative to the current directory when the
+ `set_relative_directory` was called.
+
+ """
+ fnorm = os.path.normcase(filename)
+ if fnorm.startswith(RELATIVE_DIR):
+ filename = filename[len(RELATIVE_DIR):]
+ return unicode_filename(filename)
+
+
+@contract(returns='unicode')
+def canonical_filename(filename):
+ """Return a canonical file name for `filename`.
+
+ An absolute path with no redundant components and normalized case.
+
+ """
+ if filename not in CANONICAL_FILENAME_CACHE:
+ cf = filename
+ if not os.path.isabs(filename):
+ for path in [os.curdir] + sys.path:
+ if path is None:
+ continue
+ f = os.path.join(path, filename)
+ try:
+ exists = os.path.exists(f)
+ except UnicodeError:
+ exists = False
+ if exists:
+ cf = f
+ break
+ cf = abs_file(cf)
+ CANONICAL_FILENAME_CACHE[filename] = cf
+ return CANONICAL_FILENAME_CACHE[filename]
+
+if getattr(sys, 'is_standalone_binary', False):
+ # filename for py files in binary is already canonical,
+ # it's relative to the arcadia root
+ def canonical_filename(filename):
+ # next assert does not needed in case when we load coverage from not arcadia source in arcadia binary
+ # assert not filename.startswith("/"), filename
+ return filename
+
+MAX_FLAT = 200
+
+@contract(filename='unicode', returns='unicode')
+def flat_rootname(filename):
+ """A base for a flat file name to correspond to this file.
+
+ Useful for writing files about the code where you want all the files in
+ the same directory, but need to differentiate same-named files from
+ different directories.
+
+ For example, the file a/b/c.py will return 'a_b_c_py'
+
+ """
+ name = ntpath.splitdrive(filename)[1]
+ name = re.sub(r"[\\/.:]", "_", name)
+ if len(name) > MAX_FLAT:
+ h = hashlib.sha1(name.encode('UTF-8')).hexdigest()
+ name = name[-(MAX_FLAT-len(h)-1):] + '_' + h
+ return name
+
+
+if env.WINDOWS:
+
+ _ACTUAL_PATH_CACHE = {}
+ _ACTUAL_PATH_LIST_CACHE = {}
+
+ def actual_path(path):
+ """Get the actual path of `path`, including the correct case."""
+ if env.PY2 and isinstance(path, unicode_class):
+ path = path.encode(sys.getfilesystemencoding())
+ if path in _ACTUAL_PATH_CACHE:
+ return _ACTUAL_PATH_CACHE[path]
+
+ head, tail = os.path.split(path)
+ if not tail:
+ # This means head is the drive spec: normalize it.
+ actpath = head.upper()
+ elif not head:
+ actpath = tail
+ else:
+ head = actual_path(head)
+ if head in _ACTUAL_PATH_LIST_CACHE:
+ files = _ACTUAL_PATH_LIST_CACHE[head]
+ else:
+ try:
+ files = os.listdir(head)
+ except Exception:
+ # This will raise OSError, or this bizarre TypeError:
+ # https://bugs.python.org/issue1776160
+ files = []
+ _ACTUAL_PATH_LIST_CACHE[head] = files
+ normtail = os.path.normcase(tail)
+ for f in files:
+ if os.path.normcase(f) == normtail:
+ tail = f
+ break
+ actpath = os.path.join(head, tail)
+ _ACTUAL_PATH_CACHE[path] = actpath
+ return actpath
+
+else:
+ def actual_path(filename):
+ """The actual path for non-Windows platforms."""
+ return filename
+
+
+if env.PY2:
+ @contract(returns='unicode')
+ def unicode_filename(filename):
+ """Return a Unicode version of `filename`."""
+ if isinstance(filename, str):
+ encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
+ filename = filename.decode(encoding, "replace")
+ return filename
+else:
+ @contract(filename='unicode', returns='unicode')
+ def unicode_filename(filename):
+ """Return a Unicode version of `filename`."""
+ return filename
+
+
+@contract(returns='unicode')
+def abs_file(path):
+ """Return the absolute normalized form of `path`."""
+ try:
+ path = os.path.realpath(path)
+ except UnicodeError:
+ pass
+ path = os.path.abspath(path)
+ path = actual_path(path)
+ path = unicode_filename(path)
+ return path
+
+
+def python_reported_file(filename):
+ """Return the string as Python would describe this file name."""
+ if env.PYBEHAVIOR.report_absolute_files:
+ filename = os.path.abspath(filename)
+ return filename
+
+
+RELATIVE_DIR = None
+CANONICAL_FILENAME_CACHE = None
+set_relative_directory()
+
+
+def isabs_anywhere(filename):
+ """Is `filename` an absolute path on any OS?"""
+ return ntpath.isabs(filename) or posixpath.isabs(filename)
+
+
+def prep_patterns(patterns):
+ """Prepare the file patterns for use in a `FnmatchMatcher`.
+
+ If a pattern starts with a wildcard, it is used as a pattern
+ as-is. If it does not start with a wildcard, then it is made
+ absolute with the current directory.
+
+ If `patterns` is None, an empty list is returned.
+
+ """
+ prepped = []
+ for p in patterns or []:
+ if p.startswith(("*", "?")):
+ prepped.append(p)
+ else:
+ prepped.append(abs_file(p))
+ return prepped
+
+
+class TreeMatcher(object):
+ """A matcher for files in a tree.
+
+ Construct with a list of paths, either files or directories. Paths match
+ with the `match` method if they are one of the files, or if they are
+ somewhere in a subtree rooted at one of the directories.
+
+ """
+ def __init__(self, paths):
+ self.paths = list(paths)
+
+ def __repr__(self):
+ return "<TreeMatcher %r>" % self.paths
+
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.paths
+
+ def match(self, fpath):
+ """Does `fpath` indicate a file in one of our trees?"""
+ for p in self.paths:
+ if fpath.startswith(p):
+ if fpath == p:
+ # This is the same file!
+ return True
+ if fpath[len(p)] == os.sep:
+ # This is a file in the directory
+ return True
+ return False
+
+
+class ModuleMatcher(object):
+ """A matcher for modules in a tree."""
+ def __init__(self, module_names):
+ self.modules = list(module_names)
+
+ def __repr__(self):
+ return "<ModuleMatcher %r>" % (self.modules)
+
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.modules
+
+ def match(self, module_name):
+ """Does `module_name` indicate a module in one of our packages?"""
+ if not module_name:
+ return False
+
+ for m in self.modules:
+ if module_name.startswith(m):
+ if module_name == m:
+ return True
+ if module_name[len(m)] == '.':
+ # This is a module in the package
+ return True
+
+ return False
+
+
+class FnmatchMatcher(object):
+ """A matcher for files by file name pattern."""
+ def __init__(self, pats):
+ self.pats = list(pats)
+ self.re = fnmatches_to_regex(self.pats, case_insensitive=env.WINDOWS)
+
+ def __repr__(self):
+ return "<FnmatchMatcher %r>" % self.pats
+
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.pats
+
+ def match(self, fpath):
+ """Does `fpath` match one of our file name patterns?"""
+ return self.re.match(fpath) is not None
+
+
+def sep(s):
+ """Find the path separator used in this string, or os.sep if none."""
+ sep_match = re.search(r"[\\/]", s)
+ if sep_match:
+ the_sep = sep_match.group(0)
+ else:
+ the_sep = os.sep
+ return the_sep
+
+
+def fnmatches_to_regex(patterns, case_insensitive=False, partial=False):
+ """Convert fnmatch patterns to a compiled regex that matches any of them.
+
+ Slashes are always converted to match either slash or backslash, for
+ Windows support, even when running elsewhere.
+
+ If `partial` is true, then the pattern will match if the target string
+ starts with the pattern. Otherwise, it must match the entire string.
+
+ Returns: a compiled regex object. Use the .match method to compare target
+ strings.
+
+ """
+ regexes = (fnmatch.translate(pattern) for pattern in patterns)
+ # Python3.7 fnmatch translates "/" as "/". Before that, it translates as "\/",
+ # so we have to deal with maybe a backslash.
+ regexes = (re.sub(r"\\?/", r"[\\\\/]", regex) for regex in regexes)
+
+ if partial:
+ # fnmatch always adds a \Z to match the whole string, which we don't
+ # want, so we remove the \Z. While removing it, we only replace \Z if
+ # followed by paren (introducing flags), or at end, to keep from
+ # destroying a literal \Z in the pattern.
+ regexes = (re.sub(r'\\Z(\(\?|$)', r'\1', regex) for regex in regexes)
+
+ flags = 0
+ if case_insensitive:
+ flags |= re.IGNORECASE
+ compiled = re.compile(join_regex(regexes), flags=flags)
+
+ return compiled
+
+
+class PathAliases(object):
+ """A collection of aliases for paths.
+
+ When combining data files from remote machines, often the paths to source
+ code are different, for example, due to OS differences, or because of
+ serialized checkouts on continuous integration machines.
+
+ A `PathAliases` object tracks a list of pattern/result pairs, and can
+ map a path through those aliases to produce a unified path.
+
+ """
+ def __init__(self):
+ self.aliases = []
+
+ def pprint(self): # pragma: debugging
+ """Dump the important parts of the PathAliases, for debugging."""
+ for regex, result in self.aliases:
+ print("{!r} --> {!r}".format(regex.pattern, result))
+
+ def add(self, pattern, result):
+ """Add the `pattern`/`result` pair to the list of aliases.
+
+ `pattern` is an `fnmatch`-style pattern. `result` is a simple
+ string. When mapping paths, if a path starts with a match against
+ `pattern`, then that match is replaced with `result`. This models
+ isomorphic source trees being rooted at different places on two
+ different machines.
+
+ `pattern` can't end with a wildcard component, since that would
+ match an entire tree, and not just its root.
+
+ """
+ pattern_sep = sep(pattern)
+
+ if len(pattern) > 1:
+ pattern = pattern.rstrip(r"\/")
+
+ # The pattern can't end with a wildcard component.
+ if pattern.endswith("*"):
+ raise CoverageException("Pattern must not end with wildcards.")
+
+ # The pattern is meant to match a filepath. Let's make it absolute
+ # unless it already is, or is meant to match any prefix.
+ if not pattern.startswith('*') and not isabs_anywhere(pattern +
+ pattern_sep):
+ pattern = abs_file(pattern)
+ if not pattern.endswith(pattern_sep):
+ pattern += pattern_sep
+
+ # Make a regex from the pattern.
+ regex = fnmatches_to_regex([pattern], case_insensitive=True, partial=True)
+
+ # Normalize the result: it must end with a path separator.
+ result_sep = sep(result)
+ result = result.rstrip(r"\/") + result_sep
+ self.aliases.append((regex, result))
+
+ def map(self, path):
+ """Map `path` through the aliases.
+
+ `path` is checked against all of the patterns. The first pattern to
+ match is used to replace the root of the path with the result root.
+ Only one pattern is ever used. If no patterns match, `path` is
+ returned unchanged.
+
+ The separator style in the result is made to match that of the result
+ in the alias.
+
+ Returns the mapped path. If a mapping has happened, this is a
+ canonical path. If no mapping has happened, it is the original value
+ of `path` unchanged.
+
+ """
+ for regex, result in self.aliases:
+ m = regex.match(path)
+ if m:
+ new = path.replace(m.group(0), result)
+ new = new.replace(sep(path), sep(result))
+ new = canonical_filename(new)
+ return new
+ return path
+
+
+def find_python_files(dirname):
+ """Yield all of the importable Python files in `dirname`, recursively.
+
+ To be importable, the files have to be in a directory with a __init__.py,
+ except for `dirname` itself, which isn't required to have one. The
+ assumption is that `dirname` was specified directly, so the user knows
+ best, but sub-directories are checked for a __init__.py to be sure we only
+ find the importable files.
+
+ """
+ for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)):
+ if i > 0 and '__init__.py' not in filenames:
+ # If a directory doesn't have __init__.py, then it isn't
+ # importable and neither are its files
+ del dirnames[:]
+ continue
+ for filename in filenames:
+ # We're only interested in files that look like reasonable Python
+ # files: Must end with .py or .pyw, and must not have certain funny
+ # characters that probably mean they are editor junk.
+ if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename):
+ yield os.path.join(dirpath, filename)
diff --git a/contrib/python/coverage/py2/coverage/fullcoverage/encodings.py b/contrib/python/coverage/py2/coverage/fullcoverage/encodings.py
new file mode 100644
index 0000000000..aeb416e406
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/fullcoverage/encodings.py
@@ -0,0 +1,60 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Imposter encodings module that installs a coverage-style tracer.
+
+This is NOT the encodings module; it is an imposter that sets up tracing
+instrumentation and then replaces itself with the real encodings module.
+
+If the directory that holds this file is placed first in the PYTHONPATH when
+using "coverage" to run Python's tests, then this file will become the very
+first module imported by the internals of Python 3. It installs a
+coverage.py-compatible trace function that can watch Standard Library modules
+execute from the very earliest stages of Python's own boot process. This fixes
+a problem with coverage.py - that it starts too late to trace the coverage of
+many of the most fundamental modules in the Standard Library.
+
+"""
+
+import sys
+
+class FullCoverageTracer(object):
+ def __init__(self):
+ # `traces` is a list of trace events. Frames are tricky: the same
+ # frame object is used for a whole scope, with new line numbers
+ # written into it. So in one scope, all the frame objects are the
+ # same object, and will eventually all will point to the last line
+ # executed. So we keep the line numbers alongside the frames.
+ # The list looks like:
+ #
+ # traces = [
+ # ((frame, event, arg), lineno), ...
+ # ]
+ #
+ self.traces = []
+
+ def fullcoverage_trace(self, *args):
+ frame, event, arg = args
+ self.traces.append((args, frame.f_lineno))
+ return self.fullcoverage_trace
+
+sys.settrace(FullCoverageTracer().fullcoverage_trace)
+
+# In coverage/files.py is actual_filename(), which uses glob.glob. I don't
+# understand why, but that use of glob borks everything if fullcoverage is in
+# effect. So here we make an ugly hail-mary pass to switch off glob.glob over
+# there. This means when using fullcoverage, Windows path names will not be
+# their actual case.
+
+#sys.fullcoverage = True
+
+# Finally, remove our own directory from sys.path; remove ourselves from
+# sys.modules; and re-import "encodings", which will be the real package
+# this time. Note that the delete from sys.modules dictionary has to
+# happen last, since all of the symbols in this module will become None
+# at that exact moment, including "sys".
+
+parentdir = max(filter(__file__.startswith, sys.path), key=len)
+sys.path.remove(parentdir)
+del sys.modules['encodings']
+import encodings
diff --git a/contrib/python/coverage/py2/coverage/html.py b/contrib/python/coverage/py2/coverage/html.py
new file mode 100644
index 0000000000..9d8e342716
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/html.py
@@ -0,0 +1,539 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""HTML reporting for coverage.py."""
+
+import datetime
+import json
+import os
+import re
+import shutil
+import sys
+
+import coverage
+from coverage import env
+from coverage.backward import iitems, SimpleNamespace, format_local_datetime
+from coverage.data import add_data_to_hash
+from coverage.files import flat_rootname
+from coverage.misc import CoverageException, ensure_dir, file_be_gone, Hasher, isolate_module
+from coverage.report import get_analysis_to_report
+from coverage.results import Numbers
+from coverage.templite import Templite
+
+os = isolate_module(os)
+
+
+# Static files are looked for in a list of places.
+STATIC_PATH = [
+ # The place Debian puts system Javascript libraries.
+ "/usr/share/javascript",
+
+ # Our htmlfiles directory.
+ os.path.join(os.path.dirname(__file__), "htmlfiles"),
+]
+
+
+def data_filename(fname, pkgdir=""):
+ """Return the path to a data file of ours.
+
+ The file is searched for on `STATIC_PATH`, and the first place it's found,
+ is returned.
+
+ Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir`
+ is provided, at that sub-directory.
+
+ """
+ tried = []
+ for static_dir in STATIC_PATH:
+ static_filename = os.path.join(static_dir, fname)
+ if os.path.exists(static_filename):
+ return static_filename
+ else:
+ tried.append(static_filename)
+ if pkgdir:
+ static_filename = os.path.join(static_dir, pkgdir, fname)
+ if os.path.exists(static_filename):
+ return static_filename
+ else:
+ tried.append(static_filename)
+ raise CoverageException(
+ "Couldn't find static file %r from %r, tried: %r" % (fname, os.getcwd(), tried)
+ )
+
+
+def get_htmlfiles_resource(name):
+ import pkgutil
+ return pkgutil.get_data(__package__, 'htmlfiles/' + name)
+
+
+def read_data(fname):
+ """Return the contents of a data file of ours."""
+ if getattr(sys, 'is_standalone_binary', False):
+ res_buf = get_htmlfiles_resource(fname).decode()
+ if res_buf is not None:
+ return res_buf
+
+ with open(data_filename(fname)) as data_file:
+ return data_file.read()
+
+
+def write_html(fname, html):
+ """Write `html` to `fname`, properly encoded."""
+ html = re.sub(r"(\A\s+)|(\s+$)", "", html, flags=re.MULTILINE) + "\n"
+ with open(fname, "wb") as fout:
+ fout.write(html.encode('ascii', 'xmlcharrefreplace'))
+
+
+class HtmlDataGeneration(object):
+ """Generate structured data to be turned into HTML reports."""
+
+ EMPTY = "(empty)"
+
+ def __init__(self, cov):
+ self.coverage = cov
+ self.config = self.coverage.config
+ data = self.coverage.get_data()
+ self.has_arcs = data.has_arcs()
+ if self.config.show_contexts:
+ if data.measured_contexts() == {""}:
+ self.coverage._warn("No contexts were measured")
+ data.set_query_contexts(self.config.report_contexts)
+
+ def data_for_file(self, fr, analysis):
+ """Produce the data needed for one file's report."""
+ if self.has_arcs:
+ missing_branch_arcs = analysis.missing_branch_arcs()
+ arcs_executed = analysis.arcs_executed()
+
+ if self.config.show_contexts:
+ contexts_by_lineno = analysis.data.contexts_by_lineno(analysis.filename)
+
+ lines = []
+
+ for lineno, tokens in enumerate(fr.source_token_lines(), start=1):
+ # Figure out how to mark this line.
+ category = None
+ short_annotations = []
+ long_annotations = []
+
+ if lineno in analysis.excluded:
+ category = 'exc'
+ elif lineno in analysis.missing:
+ category = 'mis'
+ elif self.has_arcs and lineno in missing_branch_arcs:
+ category = 'par'
+ for b in missing_branch_arcs[lineno]:
+ if b < 0:
+ short_annotations.append("exit")
+ else:
+ short_annotations.append(b)
+ long_annotations.append(fr.missing_arc_description(lineno, b, arcs_executed))
+ elif lineno in analysis.statements:
+ category = 'run'
+
+ contexts = contexts_label = None
+ context_list = None
+ if category and self.config.show_contexts:
+ contexts = sorted(c or self.EMPTY for c in contexts_by_lineno[lineno])
+ if contexts == [self.EMPTY]:
+ contexts_label = self.EMPTY
+ else:
+ contexts_label = "{} ctx".format(len(contexts))
+ context_list = contexts
+
+ lines.append(SimpleNamespace(
+ tokens=tokens,
+ number=lineno,
+ category=category,
+ statement=(lineno in analysis.statements),
+ contexts=contexts,
+ contexts_label=contexts_label,
+ context_list=context_list,
+ short_annotations=short_annotations,
+ long_annotations=long_annotations,
+ ))
+
+ file_data = SimpleNamespace(
+ relative_filename=fr.relative_filename(),
+ nums=analysis.numbers,
+ lines=lines,
+ )
+
+ return file_data
+
+
+class HtmlReporter(object):
+ """HTML reporting."""
+
+ # These files will be copied from the htmlfiles directory to the output
+ # directory.
+ STATIC_FILES = [
+ ("style.css", ""),
+ ("jquery.min.js", "jquery"),
+ ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"),
+ ("jquery.hotkeys.js", "jquery-hotkeys"),
+ ("jquery.isonscreen.js", "jquery-isonscreen"),
+ ("jquery.tablesorter.min.js", "jquery-tablesorter"),
+ ("coverage_html.js", ""),
+ ("keybd_closed.png", ""),
+ ("keybd_open.png", ""),
+ ("favicon_32.png", ""),
+ ]
+
+ def __init__(self, cov):
+ self.coverage = cov
+ self.config = self.coverage.config
+ self.directory = self.config.html_dir
+
+ self.skip_covered = self.config.html_skip_covered
+ if self.skip_covered is None:
+ self.skip_covered = self.config.skip_covered
+ self.skip_empty = self.config.html_skip_empty
+ if self.skip_empty is None:
+ self.skip_empty= self.config.skip_empty
+
+ title = self.config.html_title
+ if env.PY2:
+ title = title.decode("utf8")
+
+ if self.config.extra_css:
+ self.extra_css = os.path.basename(self.config.extra_css)
+ else:
+ self.extra_css = None
+
+ self.data = self.coverage.get_data()
+ self.has_arcs = self.data.has_arcs()
+
+ self.file_summaries = []
+ self.all_files_nums = []
+ self.incr = IncrementalChecker(self.directory)
+ self.datagen = HtmlDataGeneration(self.coverage)
+ self.totals = Numbers()
+
+ self.template_globals = {
+ # Functions available in the templates.
+ 'escape': escape,
+ 'pair': pair,
+ 'len': len,
+
+ # Constants for this report.
+ '__url__': coverage.__url__,
+ '__version__': coverage.__version__,
+ 'title': title,
+ 'time_stamp': format_local_datetime(datetime.datetime.now()),
+ 'extra_css': self.extra_css,
+ 'has_arcs': self.has_arcs,
+ 'show_contexts': self.config.show_contexts,
+
+ # Constants for all reports.
+ # These css classes determine which lines are highlighted by default.
+ 'category': {
+ 'exc': 'exc show_exc',
+ 'mis': 'mis show_mis',
+ 'par': 'par run show_par',
+ 'run': 'run',
+ }
+ }
+ self.pyfile_html_source = read_data("pyfile.html")
+ self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals)
+
+ def report(self, morfs):
+ """Generate an HTML report for `morfs`.
+
+ `morfs` is a list of modules or file names.
+
+ """
+ # Read the status data and check that this run used the same
+ # global data as the last run.
+ self.incr.read()
+ self.incr.check_global_data(self.config, self.pyfile_html_source)
+
+ # Process all the files.
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
+ self.html_file(fr, analysis)
+
+ if not self.all_files_nums:
+ raise CoverageException("No data to report.")
+
+ self.totals = sum(self.all_files_nums)
+
+ # Write the index file.
+ self.index_file()
+
+ self.make_local_static_report_files()
+ return self.totals.n_statements and self.totals.pc_covered
+
+ def make_local_static_report_files(self):
+ """Make local instances of static files for HTML report."""
+ # The files we provide must always be copied.
+ for static, pkgdir in self.STATIC_FILES:
+ if getattr(sys, 'is_standalone_binary', False):
+ data = get_htmlfiles_resource(static)
+ if data is None:
+ raise IOError("No such resource: " + static)
+
+ with open(os.path.join(self.directory, static), "wb") as afile:
+ afile.write(data)
+ else:
+ shutil.copyfile(
+ data_filename(static, pkgdir),
+ os.path.join(self.directory, static)
+ )
+
+ # The user may have extra CSS they want copied.
+ if self.extra_css:
+ shutil.copyfile(
+ self.config.extra_css,
+ os.path.join(self.directory, self.extra_css)
+ )
+
+ def html_file(self, fr, analysis):
+ """Generate an HTML file for one source file."""
+ rootname = flat_rootname(fr.relative_filename())
+ html_filename = rootname + ".html"
+ ensure_dir(self.directory)
+ html_path = os.path.join(self.directory, html_filename)
+
+ # Get the numbers for this file.
+ nums = analysis.numbers
+ self.all_files_nums.append(nums)
+
+ if self.skip_covered:
+ # Don't report on 100% files.
+ no_missing_lines = (nums.n_missing == 0)
+ no_missing_branches = (nums.n_partial_branches == 0)
+ if no_missing_lines and no_missing_branches:
+ # If there's an existing file, remove it.
+ file_be_gone(html_path)
+ return
+
+ if self.skip_empty:
+ # Don't report on empty files.
+ if nums.n_statements == 0:
+ file_be_gone(html_path)
+ return
+
+ # Find out if the file on disk is already correct.
+ if self.incr.can_skip_file(self.data, fr, rootname):
+ self.file_summaries.append(self.incr.index_info(rootname))
+ return
+
+ # Write the HTML page for this file.
+ file_data = self.datagen.data_for_file(fr, analysis)
+ for ldata in file_data.lines:
+ # Build the HTML for the line.
+ html = []
+ for tok_type, tok_text in ldata.tokens:
+ if tok_type == "ws":
+ html.append(escape(tok_text))
+ else:
+ tok_html = escape(tok_text) or '&nbsp;'
+ html.append(
+ u'<span class="{}">{}</span>'.format(tok_type, tok_html)
+ )
+ ldata.html = ''.join(html)
+
+ if ldata.short_annotations:
+ # 202F is NARROW NO-BREAK SPACE.
+ # 219B is RIGHTWARDS ARROW WITH STROKE.
+ ldata.annotate = u",&nbsp;&nbsp; ".join(
+ u"{}&#x202F;&#x219B;&#x202F;{}".format(ldata.number, d)
+ for d in ldata.short_annotations
+ )
+ else:
+ ldata.annotate = None
+
+ if ldata.long_annotations:
+ longs = ldata.long_annotations
+ if len(longs) == 1:
+ ldata.annotate_long = longs[0]
+ else:
+ ldata.annotate_long = u"{:d} missed branches: {}".format(
+ len(longs),
+ u", ".join(
+ u"{:d}) {}".format(num, ann_long)
+ for num, ann_long in enumerate(longs, start=1)
+ ),
+ )
+ else:
+ ldata.annotate_long = None
+
+ css_classes = []
+ if ldata.category:
+ css_classes.append(self.template_globals['category'][ldata.category])
+ ldata.css_class = ' '.join(css_classes) or "pln"
+
+ html = self.source_tmpl.render(file_data.__dict__)
+ write_html(html_path, html)
+
+ # Save this file's information for the index file.
+ index_info = {
+ 'nums': nums,
+ 'html_filename': html_filename,
+ 'relative_filename': fr.relative_filename(),
+ }
+ self.file_summaries.append(index_info)
+ self.incr.set_index_info(rootname, index_info)
+
+ def index_file(self):
+ """Write the index.html file for this report."""
+ index_tmpl = Templite(read_data("index.html"), self.template_globals)
+
+ html = index_tmpl.render({
+ 'files': self.file_summaries,
+ 'totals': self.totals,
+ })
+
+ write_html(os.path.join(self.directory, "index.html"), html)
+
+ # Write the latest hashes for next time.
+ self.incr.write()
+
+
+class IncrementalChecker(object):
+ """Logic and data to support incremental reporting."""
+
+ STATUS_FILE = "status.json"
+ STATUS_FORMAT = 2
+
+ # pylint: disable=wrong-spelling-in-comment,useless-suppression
+ # The data looks like:
+ #
+ # {
+ # "format": 2,
+ # "globals": "540ee119c15d52a68a53fe6f0897346d",
+ # "version": "4.0a1",
+ # "files": {
+ # "cogapp___init__": {
+ # "hash": "e45581a5b48f879f301c0f30bf77a50c",
+ # "index": {
+ # "html_filename": "cogapp___init__.html",
+ # "relative_filename": "cogapp/__init__",
+ # "nums": [ 1, 14, 0, 0, 0, 0, 0 ]
+ # }
+ # },
+ # ...
+ # "cogapp_whiteutils": {
+ # "hash": "8504bb427fc488c4176809ded0277d51",
+ # "index": {
+ # "html_filename": "cogapp_whiteutils.html",
+ # "relative_filename": "cogapp/whiteutils",
+ # "nums": [ 1, 59, 0, 1, 28, 2, 2 ]
+ # }
+ # }
+ # }
+ # }
+
+ def __init__(self, directory):
+ self.directory = directory
+ self.reset()
+
+ def reset(self):
+ """Initialize to empty. Causes all files to be reported."""
+ self.globals = ''
+ self.files = {}
+
+ def read(self):
+ """Read the information we stored last time."""
+ usable = False
+ try:
+ status_file = os.path.join(self.directory, self.STATUS_FILE)
+ with open(status_file) as fstatus:
+ status = json.load(fstatus)
+ except (IOError, ValueError):
+ usable = False
+ else:
+ usable = True
+ if status['format'] != self.STATUS_FORMAT:
+ usable = False
+ elif status['version'] != coverage.__version__:
+ usable = False
+
+ if usable:
+ self.files = {}
+ for filename, fileinfo in iitems(status['files']):
+ fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums'])
+ self.files[filename] = fileinfo
+ self.globals = status['globals']
+ else:
+ self.reset()
+
+ def write(self):
+ """Write the current status."""
+ status_file = os.path.join(self.directory, self.STATUS_FILE)
+ files = {}
+ for filename, fileinfo in iitems(self.files):
+ fileinfo['index']['nums'] = fileinfo['index']['nums'].init_args()
+ files[filename] = fileinfo
+
+ status = {
+ 'format': self.STATUS_FORMAT,
+ 'version': coverage.__version__,
+ 'globals': self.globals,
+ 'files': files,
+ }
+ with open(status_file, "w") as fout:
+ json.dump(status, fout, separators=(',', ':'))
+
+ def check_global_data(self, *data):
+ """Check the global data that can affect incremental reporting."""
+ m = Hasher()
+ for d in data:
+ m.update(d)
+ these_globals = m.hexdigest()
+ if self.globals != these_globals:
+ self.reset()
+ self.globals = these_globals
+
+ def can_skip_file(self, data, fr, rootname):
+ """Can we skip reporting this file?
+
+ `data` is a CoverageData object, `fr` is a `FileReporter`, and
+ `rootname` is the name being used for the file.
+ """
+ m = Hasher()
+ m.update(fr.source().encode('utf-8'))
+ add_data_to_hash(data, fr.filename, m)
+ this_hash = m.hexdigest()
+
+ that_hash = self.file_hash(rootname)
+
+ if this_hash == that_hash:
+ # Nothing has changed to require the file to be reported again.
+ return True
+ else:
+ self.set_file_hash(rootname, this_hash)
+ return False
+
+ def file_hash(self, fname):
+ """Get the hash of `fname`'s contents."""
+ return self.files.get(fname, {}).get('hash', '')
+
+ def set_file_hash(self, fname, val):
+ """Set the hash of `fname`'s contents."""
+ self.files.setdefault(fname, {})['hash'] = val
+
+ def index_info(self, fname):
+ """Get the information for index.html for `fname`."""
+ return self.files.get(fname, {}).get('index', {})
+
+ def set_index_info(self, fname, info):
+ """Set the information for index.html for `fname`."""
+ self.files.setdefault(fname, {})['index'] = info
+
+
+# Helpers for templates and generating HTML
+
+def escape(t):
+ """HTML-escape the text in `t`.
+
+ This is only suitable for HTML text, not attributes.
+
+ """
+ # Convert HTML special chars into HTML entities.
+ return t.replace("&", "&amp;").replace("<", "&lt;")
+
+
+def pair(ratio):
+ """Format a pair of numbers so JavaScript can read them in an attribute."""
+ return "%s %s" % ratio
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/coverage_html.js b/contrib/python/coverage/py2/coverage/htmlfiles/coverage_html.js
new file mode 100644
index 0000000000..27b49b36f9
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/coverage_html.js
@@ -0,0 +1,616 @@
+// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+// For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+// Coverage.py HTML report browser code.
+/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
+/*global coverage: true, document, window, $ */
+
+coverage = {};
+
+// Find all the elements with shortkey_* class, and use them to assign a shortcut key.
+coverage.assign_shortkeys = function () {
+ $("*[class*='shortkey_']").each(function (i, e) {
+ $.each($(e).attr("class").split(" "), function (i, c) {
+ if (/^shortkey_/.test(c)) {
+ $(document).bind('keydown', c.substr(9), function () {
+ $(e).click();
+ });
+ }
+ });
+ });
+};
+
+// Create the events for the help panel.
+coverage.wire_up_help_panel = function () {
+ $("#keyboard_icon").click(function () {
+ // Show the help panel, and position it so the keyboard icon in the
+ // panel is in the same place as the keyboard icon in the header.
+ $(".help_panel").show();
+ var koff = $("#keyboard_icon").offset();
+ var poff = $("#panel_icon").position();
+ $(".help_panel").offset({
+ top: koff.top-poff.top,
+ left: koff.left-poff.left
+ });
+ });
+ $("#panel_icon").click(function () {
+ $(".help_panel").hide();
+ });
+};
+
+// Create the events for the filter box.
+coverage.wire_up_filter = function () {
+ // Cache elements.
+ var table = $("table.index");
+ var table_rows = table.find("tbody tr");
+ var table_row_names = table_rows.find("td.name a");
+ var no_rows = $("#no_rows");
+
+ // Create a duplicate table footer that we can modify with dynamic summed values.
+ var table_footer = $("table.index tfoot tr");
+ var table_dynamic_footer = table_footer.clone();
+ table_dynamic_footer.attr('class', 'total_dynamic hidden');
+ table_footer.after(table_dynamic_footer);
+
+ // Observe filter keyevents.
+ $("#filter").on("keyup change", $.debounce(150, function (event) {
+ var filter_value = $(this).val();
+
+ if (filter_value === "") {
+ // Filter box is empty, remove all filtering.
+ table_rows.removeClass("hidden");
+
+ // Show standard footer, hide dynamic footer.
+ table_footer.removeClass("hidden");
+ table_dynamic_footer.addClass("hidden");
+
+ // Hide placeholder, show table.
+ if (no_rows.length > 0) {
+ no_rows.hide();
+ }
+ table.show();
+
+ }
+ else {
+ // Filter table items by value.
+ var hidden = 0;
+ var shown = 0;
+
+ // Hide / show elements.
+ $.each(table_row_names, function () {
+ var element = $(this).parents("tr");
+
+ if ($(this).text().indexOf(filter_value) === -1) {
+ // hide
+ element.addClass("hidden");
+ hidden++;
+ }
+ else {
+ // show
+ element.removeClass("hidden");
+ shown++;
+ }
+ });
+
+ // Show placeholder if no rows will be displayed.
+ if (no_rows.length > 0) {
+ if (shown === 0) {
+ // Show placeholder, hide table.
+ no_rows.show();
+ table.hide();
+ }
+ else {
+ // Hide placeholder, show table.
+ no_rows.hide();
+ table.show();
+ }
+ }
+
+ // Manage dynamic header:
+ if (hidden > 0) {
+ // Calculate new dynamic sum values based on visible rows.
+ for (var column = 2; column < 20; column++) {
+ // Calculate summed value.
+ var cells = table_rows.find('td:nth-child(' + column + ')');
+ if (!cells.length) {
+ // No more columns...!
+ break;
+ }
+
+ var sum = 0, numer = 0, denom = 0;
+ $.each(cells.filter(':visible'), function () {
+ var ratio = $(this).data("ratio");
+ if (ratio) {
+ var splitted = ratio.split(" ");
+ numer += parseInt(splitted[0], 10);
+ denom += parseInt(splitted[1], 10);
+ }
+ else {
+ sum += parseInt(this.innerHTML, 10);
+ }
+ });
+
+ // Get footer cell element.
+ var footer_cell = table_dynamic_footer.find('td:nth-child(' + column + ')');
+
+ // Set value into dynamic footer cell element.
+ if (cells[0].innerHTML.indexOf('%') > -1) {
+ // Percentage columns use the numerator and denominator,
+ // and adapt to the number of decimal places.
+ var match = /\.([0-9]+)/.exec(cells[0].innerHTML);
+ var places = 0;
+ if (match) {
+ places = match[1].length;
+ }
+ var pct = numer * 100 / denom;
+ footer_cell.text(pct.toFixed(places) + '%');
+ }
+ else {
+ footer_cell.text(sum);
+ }
+ }
+
+ // Hide standard footer, show dynamic footer.
+ table_footer.addClass("hidden");
+ table_dynamic_footer.removeClass("hidden");
+ }
+ else {
+ // Show standard footer, hide dynamic footer.
+ table_footer.removeClass("hidden");
+ table_dynamic_footer.addClass("hidden");
+ }
+ }
+ }));
+
+ // Trigger change event on setup, to force filter on page refresh
+ // (filter value may still be present).
+ $("#filter").trigger("change");
+};
+
+// Loaded on index.html
+coverage.index_ready = function ($) {
+ // Look for a localStorage item containing previous sort settings:
+ var sort_list = [];
+ var storage_name = "COVERAGE_INDEX_SORT";
+ var stored_list = undefined;
+ try {
+ stored_list = localStorage.getItem(storage_name);
+ } catch(err) {}
+
+ if (stored_list) {
+ sort_list = JSON.parse('[[' + stored_list + ']]');
+ }
+
+ // Create a new widget which exists only to save and restore
+ // the sort order:
+ $.tablesorter.addWidget({
+ id: "persistentSort",
+
+ // Format is called by the widget before displaying:
+ format: function (table) {
+ if (table.config.sortList.length === 0 && sort_list.length > 0) {
+ // This table hasn't been sorted before - we'll use
+ // our stored settings:
+ $(table).trigger('sorton', [sort_list]);
+ }
+ else {
+ // This is not the first load - something has
+ // already defined sorting so we'll just update
+ // our stored value to match:
+ sort_list = table.config.sortList;
+ }
+ }
+ });
+
+ // Configure our tablesorter to handle the variable number of
+ // columns produced depending on report options:
+ var headers = [];
+ var col_count = $("table.index > thead > tr > th").length;
+
+ headers[0] = { sorter: 'text' };
+ for (i = 1; i < col_count-1; i++) {
+ headers[i] = { sorter: 'digit' };
+ }
+ headers[col_count-1] = { sorter: 'percent' };
+
+ // Enable the table sorter:
+ $("table.index").tablesorter({
+ widgets: ['persistentSort'],
+ headers: headers
+ });
+
+ coverage.assign_shortkeys();
+ coverage.wire_up_help_panel();
+ coverage.wire_up_filter();
+
+ // Watch for page unload events so we can save the final sort settings:
+ $(window).on("unload", function () {
+ try {
+ localStorage.setItem(storage_name, sort_list.toString())
+ } catch(err) {}
+ });
+};
+
+// -- pyfile stuff --
+
+coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS";
+
+coverage.pyfile_ready = function ($) {
+ // If we're directed to a particular line number, highlight the line.
+ var frag = location.hash;
+ if (frag.length > 2 && frag[1] === 't') {
+ $(frag).addClass('highlight');
+ coverage.set_sel(parseInt(frag.substr(2), 10));
+ }
+ else {
+ coverage.set_sel(0);
+ }
+
+ $(document)
+ .bind('keydown', 'j', coverage.to_next_chunk_nicely)
+ .bind('keydown', 'k', coverage.to_prev_chunk_nicely)
+ .bind('keydown', '0', coverage.to_top)
+ .bind('keydown', '1', coverage.to_first_chunk)
+ ;
+
+ $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");});
+ $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");});
+ $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");});
+ $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");});
+
+ coverage.filters = undefined;
+ try {
+ coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE);
+ } catch(err) {}
+
+ if (coverage.filters) {
+ coverage.filters = JSON.parse(coverage.filters);
+ }
+ else {
+ coverage.filters = {run: false, exc: true, mis: true, par: true};
+ }
+
+ for (cls in coverage.filters) {
+ coverage.set_line_visibilty(cls, coverage.filters[cls]);
+ }
+
+ coverage.assign_shortkeys();
+ coverage.wire_up_help_panel();
+
+ coverage.init_scroll_markers();
+
+ // Rebuild scroll markers when the window height changes.
+ $(window).resize(coverage.build_scroll_markers);
+};
+
+coverage.toggle_lines = function (btn, cls) {
+ var onoff = !$(btn).hasClass("show_" + cls);
+ coverage.set_line_visibilty(cls, onoff);
+ coverage.build_scroll_markers();
+ coverage.filters[cls] = onoff;
+ try {
+ localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters));
+ } catch(err) {}
+};
+
+coverage.set_line_visibilty = function (cls, onoff) {
+ var show = "show_" + cls;
+ var btn = $(".button_toggle_" + cls);
+ if (onoff) {
+ $("#source ." + cls).addClass(show);
+ btn.addClass(show);
+ }
+ else {
+ $("#source ." + cls).removeClass(show);
+ btn.removeClass(show);
+ }
+};
+
+// Return the nth line div.
+coverage.line_elt = function (n) {
+ return $("#t" + n);
+};
+
+// Return the nth line number div.
+coverage.num_elt = function (n) {
+ return $("#n" + n);
+};
+
+// Set the selection. b and e are line numbers.
+coverage.set_sel = function (b, e) {
+ // The first line selected.
+ coverage.sel_begin = b;
+ // The next line not selected.
+ coverage.sel_end = (e === undefined) ? b+1 : e;
+};
+
+coverage.to_top = function () {
+ coverage.set_sel(0, 1);
+ coverage.scroll_window(0);
+};
+
+coverage.to_first_chunk = function () {
+ coverage.set_sel(0, 1);
+ coverage.to_next_chunk();
+};
+
+// Return a string indicating what kind of chunk this line belongs to,
+// or null if not a chunk.
+coverage.chunk_indicator = function (line_elt) {
+ var klass = line_elt.attr('class');
+ if (klass) {
+ var m = klass.match(/\bshow_\w+\b/);
+ if (m) {
+ return m[0];
+ }
+ }
+ return null;
+};
+
+coverage.to_next_chunk = function () {
+ var c = coverage;
+
+ // Find the start of the next colored chunk.
+ var probe = c.sel_end;
+ var chunk_indicator, probe_line;
+ while (true) {
+ probe_line = c.line_elt(probe);
+ if (probe_line.length === 0) {
+ return;
+ }
+ chunk_indicator = c.chunk_indicator(probe_line);
+ if (chunk_indicator) {
+ break;
+ }
+ probe++;
+ }
+
+ // There's a next chunk, `probe` points to it.
+ var begin = probe;
+
+ // Find the end of this chunk.
+ var next_indicator = chunk_indicator;
+ while (next_indicator === chunk_indicator) {
+ probe++;
+ probe_line = c.line_elt(probe);
+ next_indicator = c.chunk_indicator(probe_line);
+ }
+ c.set_sel(begin, probe);
+ c.show_selection();
+};
+
+coverage.to_prev_chunk = function () {
+ var c = coverage;
+
+ // Find the end of the prev colored chunk.
+ var probe = c.sel_begin-1;
+ var probe_line = c.line_elt(probe);
+ if (probe_line.length === 0) {
+ return;
+ }
+ var chunk_indicator = c.chunk_indicator(probe_line);
+ while (probe > 0 && !chunk_indicator) {
+ probe--;
+ probe_line = c.line_elt(probe);
+ if (probe_line.length === 0) {
+ return;
+ }
+ chunk_indicator = c.chunk_indicator(probe_line);
+ }
+
+ // There's a prev chunk, `probe` points to its last line.
+ var end = probe+1;
+
+ // Find the beginning of this chunk.
+ var prev_indicator = chunk_indicator;
+ while (prev_indicator === chunk_indicator) {
+ probe--;
+ probe_line = c.line_elt(probe);
+ prev_indicator = c.chunk_indicator(probe_line);
+ }
+ c.set_sel(probe+1, end);
+ c.show_selection();
+};
+
+// Return the line number of the line nearest pixel position pos
+coverage.line_at_pos = function (pos) {
+ var l1 = coverage.line_elt(1),
+ l2 = coverage.line_elt(2),
+ result;
+ if (l1.length && l2.length) {
+ var l1_top = l1.offset().top,
+ line_height = l2.offset().top - l1_top,
+ nlines = (pos - l1_top) / line_height;
+ if (nlines < 1) {
+ result = 1;
+ }
+ else {
+ result = Math.ceil(nlines);
+ }
+ }
+ else {
+ result = 1;
+ }
+ return result;
+};
+
+// Returns 0, 1, or 2: how many of the two ends of the selection are on
+// the screen right now?
+coverage.selection_ends_on_screen = function () {
+ if (coverage.sel_begin === 0) {
+ return 0;
+ }
+
+ var top = coverage.line_elt(coverage.sel_begin);
+ var next = coverage.line_elt(coverage.sel_end-1);
+
+ return (
+ (top.isOnScreen() ? 1 : 0) +
+ (next.isOnScreen() ? 1 : 0)
+ );
+};
+
+coverage.to_next_chunk_nicely = function () {
+ coverage.finish_scrolling();
+ if (coverage.selection_ends_on_screen() === 0) {
+ // The selection is entirely off the screen: select the top line on
+ // the screen.
+ var win = $(window);
+ coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop()));
+ }
+ coverage.to_next_chunk();
+};
+
+coverage.to_prev_chunk_nicely = function () {
+ coverage.finish_scrolling();
+ if (coverage.selection_ends_on_screen() === 0) {
+ var win = $(window);
+ coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height()));
+ }
+ coverage.to_prev_chunk();
+};
+
+// Select line number lineno, or if it is in a colored chunk, select the
+// entire chunk
+coverage.select_line_or_chunk = function (lineno) {
+ var c = coverage;
+ var probe_line = c.line_elt(lineno);
+ if (probe_line.length === 0) {
+ return;
+ }
+ var the_indicator = c.chunk_indicator(probe_line);
+ if (the_indicator) {
+ // The line is in a highlighted chunk.
+ // Search backward for the first line.
+ var probe = lineno;
+ var indicator = the_indicator;
+ while (probe > 0 && indicator === the_indicator) {
+ probe--;
+ probe_line = c.line_elt(probe);
+ if (probe_line.length === 0) {
+ break;
+ }
+ indicator = c.chunk_indicator(probe_line);
+ }
+ var begin = probe + 1;
+
+ // Search forward for the last line.
+ probe = lineno;
+ indicator = the_indicator;
+ while (indicator === the_indicator) {
+ probe++;
+ probe_line = c.line_elt(probe);
+ indicator = c.chunk_indicator(probe_line);
+ }
+
+ coverage.set_sel(begin, probe);
+ }
+ else {
+ coverage.set_sel(lineno);
+ }
+};
+
+coverage.show_selection = function () {
+ var c = coverage;
+
+ // Highlight the lines in the chunk
+ $(".linenos .highlight").removeClass("highlight");
+ for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) {
+ c.num_elt(probe).addClass("highlight");
+ }
+
+ c.scroll_to_selection();
+};
+
+coverage.scroll_to_selection = function () {
+ // Scroll the page if the chunk isn't fully visible.
+ if (coverage.selection_ends_on_screen() < 2) {
+ // Need to move the page. The html,body trick makes it scroll in all
+ // browsers, got it from http://stackoverflow.com/questions/3042651
+ var top = coverage.line_elt(coverage.sel_begin);
+ var top_pos = parseInt(top.offset().top, 10);
+ coverage.scroll_window(top_pos - 30);
+ }
+};
+
+coverage.scroll_window = function (to_pos) {
+ $("html,body").animate({scrollTop: to_pos}, 200);
+};
+
+coverage.finish_scrolling = function () {
+ $("html,body").stop(true, true);
+};
+
+coverage.init_scroll_markers = function () {
+ var c = coverage;
+ // Init some variables
+ c.lines_len = $('#source p').length;
+ c.body_h = $('body').height();
+ c.header_h = $('div#header').height();
+
+ // Build html
+ c.build_scroll_markers();
+};
+
+coverage.build_scroll_markers = function () {
+ var c = coverage,
+ min_line_height = 3,
+ max_line_height = 10,
+ visible_window_h = $(window).height();
+
+ c.lines_to_mark = $('#source').find('p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par');
+ $('#scroll_marker').remove();
+ // Don't build markers if the window has no scroll bar.
+ if (c.body_h <= visible_window_h) {
+ return;
+ }
+
+ $("body").append("<div id='scroll_marker'>&nbsp;</div>");
+ var scroll_marker = $('#scroll_marker'),
+ marker_scale = scroll_marker.height() / c.body_h,
+ line_height = scroll_marker.height() / c.lines_len;
+
+ // Line height must be between the extremes.
+ if (line_height > min_line_height) {
+ if (line_height > max_line_height) {
+ line_height = max_line_height;
+ }
+ }
+ else {
+ line_height = min_line_height;
+ }
+
+ var previous_line = -99,
+ last_mark,
+ last_top,
+ offsets = {};
+
+ // Calculate line offsets outside loop to prevent relayouts
+ c.lines_to_mark.each(function() {
+ offsets[this.id] = $(this).offset().top;
+ });
+ c.lines_to_mark.each(function () {
+ var id_name = $(this).attr('id'),
+ line_top = Math.round(offsets[id_name] * marker_scale),
+ line_number = parseInt(id_name.substring(1, id_name.length));
+
+ if (line_number === previous_line + 1) {
+ // If this solid missed block just make previous mark higher.
+ last_mark.css({
+ 'height': line_top + line_height - last_top
+ });
+ }
+ else {
+ // Add colored line in scroll_marker block.
+ scroll_marker.append('<div id="m' + line_number + '" class="marker"></div>');
+ last_mark = $('#m' + line_number);
+ last_mark.css({
+ 'height': line_height,
+ 'top': line_top
+ });
+ last_top = line_top;
+ }
+
+ previous_line = line_number;
+ });
+};
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/favicon_32.png b/contrib/python/coverage/py2/coverage/htmlfiles/favicon_32.png
new file mode 100644
index 0000000000..8649f0475d
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/favicon_32.png
Binary files differ
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/index.html b/contrib/python/coverage/py2/coverage/htmlfiles/index.html
new file mode 100644
index 0000000000..983db06125
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/index.html
@@ -0,0 +1,119 @@
+{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
+{# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt #}
+
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>{{ title|escape }}</title>
+ <link rel="icon" sizes="32x32" href="favicon_32.png">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ {% if extra_css %}
+ <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
+ {% endif %}
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.ba-throttle-debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
+ jQuery(document).ready(coverage.index_ready);
+ </script>
+</head>
+<body class="indexfile">
+
+<div id="header">
+ <div class="content">
+ <h1>{{ title|escape }}:
+ <span class="pc_cov">{{totals.pc_covered_str}}%</span>
+ </h1>
+
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
+
+ <form id="filter_container">
+ <input id="filter" type="text" value="" placeholder="filter..." />
+ </form>
+ </div>
+</div>
+
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
+ <div>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ {% if has_arcs %}
+ <span class="key">b</span>
+ <span class="key">p</span>
+ {% endif %}
+ <span class="key">c</span> &nbsp; change column sorting
+ </p>
+ </div>
+</div>
+
+<div id="index">
+ <table class="index">
+ <thead>
+ {# The title="" attr doesn"t work in Safari. #}
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
+ {% if has_arcs %}
+ <th class="shortkey_b">branches</th>
+ <th class="shortkey_p">partial</th>
+ {% endif %}
+ <th class="right shortkey_c">coverage</th>
+ </tr>
+ </thead>
+ {# HTML syntax requires thead, tfoot, tbody #}
+ <tfoot>
+ <tr class="total">
+ <td class="name left">Total</td>
+ <td>{{totals.n_statements}}</td>
+ <td>{{totals.n_missing}}</td>
+ <td>{{totals.n_excluded}}</td>
+ {% if has_arcs %}
+ <td>{{totals.n_branches}}</td>
+ <td>{{totals.n_partial_branches}}</td>
+ {% endif %}
+ <td class="right" data-ratio="{{totals.ratio_covered|pair}}">{{totals.pc_covered_str}}%</td>
+ </tr>
+ </tfoot>
+ <tbody>
+ {% for file in files %}
+ <tr class="file">
+ <td class="name left"><a href="{{file.html_filename}}">{{file.relative_filename}}</a></td>
+ <td>{{file.nums.n_statements}}</td>
+ <td>{{file.nums.n_missing}}</td>
+ <td>{{file.nums.n_excluded}}</td>
+ {% if has_arcs %}
+ <td>{{file.nums.n_branches}}</td>
+ <td>{{file.nums.n_partial_branches}}</td>
+ {% endif %}
+ <td class="right" data-ratio="{{file.nums.ratio_covered|pair}}">{{file.nums.pc_covered_str}}%</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+
+ <p id="no_rows">
+ No items found using the specified filter.
+ </p>
+</div>
+
+<div id="footer">
+ <div class="content">
+ <p>
+ <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
+ created at {{ time_stamp }}
+ </p>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
new file mode 100644
index 0000000000..648fe5d3c2
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
@@ -0,0 +1,9 @@
+/*
+ * jQuery throttle / debounce - v1.1 - 3/7/2010
+ * http://benalman.com/projects/jquery-throttle-debounce-plugin/
+ *
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.hotkeys.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.hotkeys.js
new file mode 100644
index 0000000000..09b21e03c7
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.hotkeys.js
@@ -0,0 +1,99 @@
+/*
+ * jQuery Hotkeys Plugin
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
+ * Based upon the plugin by Tzury Bar Yochay:
+ * http://github.com/tzuryby/hotkeys
+ *
+ * Original idea by:
+ * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
+*/
+
+(function(jQuery){
+
+ jQuery.hotkeys = {
+ version: "0.8",
+
+ specialKeys: {
+ 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
+ },
+
+ shiftNums: {
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
+ ".": ">", "/": "?", "\\": "|"
+ }
+ };
+
+ function keyHandler( handleObj ) {
+ // Only care when a possible input has been specified
+ if ( typeof handleObj.data !== "string" ) {
+ return;
+ }
+
+ var origHandler = handleObj.handler,
+ keys = handleObj.data.toLowerCase().split(" ");
+
+ handleObj.handler = function( event ) {
+ // Don't fire in text-accepting inputs that we didn't directly bind to
+ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
+ event.target.type === "text") ) {
+ return;
+ }
+
+ // Keypress represents characters, not special keys
+ var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
+ character = String.fromCharCode( event.which ).toLowerCase(),
+ key, modif = "", possible = {};
+
+ // check combinations (alt|ctrl|shift+anything)
+ if ( event.altKey && special !== "alt" ) {
+ modif += "alt+";
+ }
+
+ if ( event.ctrlKey && special !== "ctrl" ) {
+ modif += "ctrl+";
+ }
+
+ // TODO: Need to make sure this works consistently across platforms
+ if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
+ modif += "meta+";
+ }
+
+ if ( event.shiftKey && special !== "shift" ) {
+ modif += "shift+";
+ }
+
+ if ( special ) {
+ possible[ modif + special ] = true;
+
+ } else {
+ possible[ modif + character ] = true;
+ possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
+
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
+ if ( modif === "shift+" ) {
+ possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
+ }
+ }
+
+ for ( var i = 0, l = keys.length; i < l; i++ ) {
+ if ( possible[ keys[i] ] ) {
+ return origHandler.apply( this, arguments );
+ }
+ }
+ };
+ }
+
+ jQuery.each([ "keydown", "keyup", "keypress" ], function() {
+ jQuery.event.special[ this ] = { add: keyHandler };
+ });
+
+})( jQuery );
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.isonscreen.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.isonscreen.js
new file mode 100644
index 0000000000..0182ebd213
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.isonscreen.js
@@ -0,0 +1,53 @@
+/* Copyright (c) 2010
+ * @author Laurence Wheway
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * @version 1.2.0
+ */
+(function($) {
+ jQuery.extend({
+ isOnScreen: function(box, container) {
+ //ensure numbers come in as intgers (not strings) and remove 'px' is it's there
+ for(var i in box){box[i] = parseFloat(box[i])};
+ for(var i in container){container[i] = parseFloat(container[i])};
+
+ if(!container){
+ container = {
+ left: $(window).scrollLeft(),
+ top: $(window).scrollTop(),
+ width: $(window).width(),
+ height: $(window).height()
+ }
+ }
+
+ if( box.left+box.width-container.left > 0 &&
+ box.left < container.width+container.left &&
+ box.top+box.height-container.top > 0 &&
+ box.top < container.height+container.top
+ ) return true;
+ return false;
+ }
+ })
+
+
+ jQuery.fn.isOnScreen = function (container) {
+ for(var i in container){container[i] = parseFloat(container[i])};
+
+ if(!container){
+ container = {
+ left: $(window).scrollLeft(),
+ top: $(window).scrollTop(),
+ width: $(window).width(),
+ height: $(window).height()
+ }
+ }
+
+ if( $(this).offset().left+$(this).width()-container.left > 0 &&
+ $(this).offset().left < container.width+container.left &&
+ $(this).offset().top+$(this).height()-container.top > 0 &&
+ $(this).offset().top < container.height+container.top
+ ) return true;
+ return false;
+ }
+})(jQuery);
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.min.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.min.js
new file mode 100644
index 0000000000..d1608e37ff
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
+if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
+},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m}); \ No newline at end of file
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/jquery.tablesorter.min.js b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.tablesorter.min.js
new file mode 100644
index 0000000000..64c7007129
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/jquery.tablesorter.min.js
@@ -0,0 +1,2 @@
+
+(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,cells[i]);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,node){var l=parsers.length;for(var i=1;i<l;i++){if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)){return parsers[i];}}return parsers[0];}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=table.tBodies[0].rows[i],cols=[];cache.row.push($(c));for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));}cols.push(i);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){if(!node)return"";var t="";if(config.textExtraction=="simple"){if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){t=node.childNodes[0].innerHTML;}else{t=node.innerHTML;}}else{if(typeof(config.textExtraction)=="function"){t=config.textExtraction(node);}else{t=$(node).text();}}return t;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){rows.push(r[n[i][checkCell]]);if(!table.config.appender){var o=r[n[i][checkCell]];var l=o.length;for(var j=0;j<l;j++){tableBody[0].appendChild(o[j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false,tableHeadersRows=[];for(var i=0;i<table.tHead.rows.length;i++){tableHeadersRows[i]=0;};$tableHeaders=$("thead th",table);$tableHeaders.each(function(index){this.count=0;this.column=index;this.order=formatSortingOrder(table.config.sortInitialOrder);if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(!this.sortDisabled){$(this).addClass(table.config.cssHeader);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){i=(v.toLowerCase()=="desc")?1:0;}else{i=(v==(0||1))?v:0;}return i;}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(getCachedSortType(table.config.parsers,c)=="text")?((order==0)?"sortText":"sortTextDesc"):((order==0)?"sortNumeric":"sortNumericDesc");var e="e"+i;dynamicExp+="var "+e+" = "+s+"(a["+c+"],b["+c+"]); ";dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function sortText(a,b){return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){var DECIMAL='\\'+config.decimal;var exp='/(^[+]?0('+DECIMAL+'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)'+DECIMAL+'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*'+DECIMAL+'0+$)/';return RegExp(exp).test($.trim(s));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}$("tr:visible",table.tBodies[0]).filter(':even').removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0]).end().filter(':odd').removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery); \ No newline at end of file
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/keybd_closed.png b/contrib/python/coverage/py2/coverage/htmlfiles/keybd_closed.png
new file mode 100644
index 0000000000..db114023f0
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/keybd_closed.png
Binary files differ
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/keybd_open.png b/contrib/python/coverage/py2/coverage/htmlfiles/keybd_open.png
new file mode 100644
index 0000000000..db114023f0
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/keybd_open.png
Binary files differ
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/pyfile.html b/contrib/python/coverage/py2/coverage/htmlfiles/pyfile.html
new file mode 100644
index 0000000000..e15be066fb
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/pyfile.html
@@ -0,0 +1,113 @@
+{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
+{# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt #}
+
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ {# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #}
+ {# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #}
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for {{relative_filename|escape}}: {{nums.pc_covered_str}}%</title>
+ <link rel="icon" sizes="32x32" href="favicon_32.png">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ {% if extra_css %}
+ <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
+ {% endif %}
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
+ jQuery(document).ready(coverage.pyfile_ready);
+ </script>
+</head>
+<body class="pyfile">
+
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>{{relative_filename|escape}}</b> :
+ <span class="pc_cov">{{nums.pc_covered_str}}%</span>
+ </h1>
+
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
+
+ <h2 class="stats">
+ {{nums.n_statements}} statements &nbsp;
+ <button type="button" class="{{category.run}} shortkey_r button_toggle_run" title="Toggle lines run">{{nums.n_executed}} run</button>
+ <button type="button" class="{{category.mis}} shortkey_m button_toggle_mis" title="Toggle lines missing">{{nums.n_missing}} missing</button>
+ <button type="button" class="{{category.exc}} shortkey_x button_toggle_exc" title="Toggle lines excluded">{{nums.n_excluded}} excluded</button>
+
+ {% if has_arcs %}
+ <button type="button" class="{{category.par}} shortkey_p button_toggle_par" title="Toggle lines partially run">{{nums.n_partial_branches}} partial</button>
+ {% endif %}
+ </h2>
+ </div>
+</div>
+
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
+ <div>
+ <p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
+ </p>
+ <p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
+ </p>
+ <p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
+ </p>
+ <p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
+ </p>
+ </div>
+</div>
+
+<div id="source">
+ {% for line in lines -%}
+ {% joined %}
+ <p id="t{{line.number}}" class="{{line.css_class}}">
+ <span class="n"><a href="#t{{line.number}}">{{line.number}}</a></span>
+ <span class="t">{{line.html}}&nbsp;</span>
+ {% if line.context_list %}
+ <input type="checkbox" id="ctxs{{line.number}}" />
+ {% endif %}
+ {# Things that should float right in the line. #}
+ <span class="r">
+ {% if line.annotate %}
+ <span class="annotate short">{{line.annotate}}</span>
+ <span class="annotate long">{{line.annotate_long}}</span>
+ {% endif %}
+ {% if line.contexts %}
+ <label for="ctxs{{line.number}}" class="ctx">{{ line.contexts_label }}</label>
+ {% endif %}
+ </span>
+ {# Things that should appear below the line. #}
+ {% if line.context_list %}
+ <span class="ctxs">
+ {% for context in line.context_list %}
+ <span>{{context}}</span>
+ {% endfor %}
+ </span>
+ {% endif %}
+ </p>
+ {% endjoined %}
+ {% endfor %}
+</div>
+
+<div id="footer">
+ <div class="content">
+ <p>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
+ created at {{ time_stamp }}
+ </p>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/style.css b/contrib/python/coverage/py2/coverage/htmlfiles/style.css
new file mode 100644
index 0000000000..36ee2a6e65
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/style.css
@@ -0,0 +1,291 @@
+@charset "UTF-8";
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+/* Don't edit this .css file. Edit the .scss file instead! */
+html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
+
+body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 1em; background: #fff; color: #000; }
+
+@media (prefers-color-scheme: dark) { body { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { body { color: #eee; } }
+
+html > body { font-size: 16px; }
+
+a:active, a:focus { outline: 2px dashed #007acc; }
+
+p { font-size: .875em; line-height: 1.4em; }
+
+table { border-collapse: collapse; }
+
+td { vertical-align: top; }
+
+table tr.hidden { display: none !important; }
+
+p#no_rows { display: none; font-size: 1.2em; }
+
+a.nav { text-decoration: none; color: inherit; }
+
+a.nav:hover { text-decoration: underline; color: inherit; }
+
+#header { background: #f8f8f8; width: 100%; border-bottom: 1px solid #eee; }
+
+@media (prefers-color-scheme: dark) { #header { background: black; } }
+
+@media (prefers-color-scheme: dark) { #header { border-color: #333; } }
+
+.indexfile #footer { margin: 1rem 3.5rem; }
+
+.pyfile #footer { margin: 1rem 1rem; }
+
+#footer .content { padding: 0; color: #666; font-style: italic; }
+
+@media (prefers-color-scheme: dark) { #footer .content { color: #aaa; } }
+
+#index { margin: 1rem 0 0 3.5rem; }
+
+#header .content { padding: 1rem 3.5rem; }
+
+h1 { font-size: 1.25em; display: inline-block; }
+
+#filter_container { float: right; margin: 0 2em 0 0; }
+
+#filter_container input { width: 10em; padding: 0.2em 0.5em; border: 2px solid #ccc; background: #fff; color: #000; }
+
+@media (prefers-color-scheme: dark) { #filter_container input { border-color: #444; } }
+
+@media (prefers-color-scheme: dark) { #filter_container input { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { #filter_container input { color: #eee; } }
+
+#filter_container input:focus { border-color: #007acc; }
+
+h2.stats { margin-top: .5em; font-size: 1em; }
+
+.stats button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; color: inherit; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; }
+
+@media (prefers-color-scheme: dark) { .stats button { border-color: #444; } }
+
+.stats button:active, .stats button:focus { outline: 2px dashed #007acc; }
+
+.stats button:active, .stats button:focus { outline: 2px dashed #007acc; }
+
+.stats button.run { background: #eeffee; }
+
+@media (prefers-color-scheme: dark) { .stats button.run { background: #373d29; } }
+
+.stats button.run.show_run { background: #dfd; border: 2px solid #00dd00; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { .stats button.run.show_run { background: #373d29; } }
+
+.stats button.mis { background: #ffeeee; }
+
+@media (prefers-color-scheme: dark) { .stats button.mis { background: #4b1818; } }
+
+.stats button.mis.show_mis { background: #fdd; border: 2px solid #ff0000; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { .stats button.mis.show_mis { background: #4b1818; } }
+
+.stats button.exc { background: #f7f7f7; }
+
+@media (prefers-color-scheme: dark) { .stats button.exc { background: #333; } }
+
+.stats button.exc.show_exc { background: #eee; border: 2px solid #808080; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { .stats button.exc.show_exc { background: #333; } }
+
+.stats button.par { background: #ffffd5; }
+
+@media (prefers-color-scheme: dark) { .stats button.par { background: #650; } }
+
+.stats button.par.show_par { background: #ffa; border: 2px solid #dddd00; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { .stats button.par.show_par { background: #650; } }
+
+.help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; }
+
+#source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; }
+
+#keyboard_icon { float: right; margin: 5px; cursor: pointer; }
+
+.help_panel { padding: .5em; border: 1px solid #883; }
+
+.help_panel .legend { font-style: italic; margin-bottom: 1em; }
+
+.indexfile .help_panel { width: 20em; min-height: 4em; }
+
+.pyfile .help_panel { width: 16em; min-height: 8em; }
+
+#panel_icon { float: right; cursor: pointer; }
+
+.keyhelp { margin: .75em; }
+
+.keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; }
+
+#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
+
+#source p { position: relative; white-space: pre; }
+
+#source p * { box-sizing: border-box; }
+
+#source p .n { float: left; text-align: right; width: 3.5rem; box-sizing: border-box; margin-left: -3.5rem; padding-right: 1em; color: #999; }
+
+@media (prefers-color-scheme: dark) { #source p .n { color: #777; } }
+
+#source p .n a { text-decoration: none; color: #999; }
+
+@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } }
+
+#source p .n a:hover { text-decoration: underline; color: #999; }
+
+@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } }
+
+#source p.highlight .n { background: #ffdd00; }
+
+#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; }
+
+@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } }
+
+#source p .t:hover { background: #f2f2f2; }
+
+@media (prefers-color-scheme: dark) { #source p .t:hover { background: #282828; } }
+
+#source p .t:hover ~ .r .annotate.long { display: block; }
+
+#source p .t .com { color: #008000; font-style: italic; line-height: 1px; }
+
+@media (prefers-color-scheme: dark) { #source p .t .com { color: #6A9955; } }
+
+#source p .t .key { font-weight: bold; line-height: 1px; }
+
+#source p .t .str { color: #0451A5; }
+
+@media (prefers-color-scheme: dark) { #source p .t .str { color: #9CDCFE; } }
+
+#source p.mis .t { border-left: 0.2em solid #ff0000; }
+
+#source p.mis.show_mis .t { background: #fdd; }
+
+@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t { background: #4b1818; } }
+
+#source p.mis.show_mis .t:hover { background: #f2d2d2; }
+
+@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t:hover { background: #532323; } }
+
+#source p.run .t { border-left: 0.2em solid #00dd00; }
+
+#source p.run.show_run .t { background: #dfd; }
+
+@media (prefers-color-scheme: dark) { #source p.run.show_run .t { background: #373d29; } }
+
+#source p.run.show_run .t:hover { background: #d2f2d2; }
+
+@media (prefers-color-scheme: dark) { #source p.run.show_run .t:hover { background: #404633; } }
+
+#source p.exc .t { border-left: 0.2em solid #808080; }
+
+#source p.exc.show_exc .t { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t { background: #333; } }
+
+#source p.exc.show_exc .t:hover { background: #e2e2e2; }
+
+@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t:hover { background: #3c3c3c; } }
+
+#source p.par .t { border-left: 0.2em solid #dddd00; }
+
+#source p.par.show_par .t { background: #ffa; }
+
+@media (prefers-color-scheme: dark) { #source p.par.show_par .t { background: #650; } }
+
+#source p.par.show_par .t:hover { background: #f2f2a2; }
+
+@media (prefers-color-scheme: dark) { #source p.par.show_par .t:hover { background: #6d5d0c; } }
+
+#source p .r { position: absolute; top: 0; right: 2.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
+
+#source p .annotate { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; color: #666; padding-right: .5em; }
+
+@media (prefers-color-scheme: dark) { #source p .annotate { color: #ddd; } }
+
+#source p .annotate.short:hover ~ .long { display: block; }
+
+#source p .annotate.long { width: 30em; right: 2.5em; }
+
+#source p input { display: none; }
+
+#source p input ~ .r label.ctx { cursor: pointer; border-radius: .25em; }
+
+#source p input ~ .r label.ctx::before { content: "â–¶ "; }
+
+#source p input ~ .r label.ctx:hover { background: #d5f7ff; color: #666; }
+
+@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { background: #0f3a42; } }
+
+@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { color: #aaa; } }
+
+#source p input:checked ~ .r label.ctx { background: #aef; color: #666; border-radius: .75em .75em 0 0; padding: 0 .5em; margin: -.25em 0; }
+
+@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { background: #056; } }
+
+@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { color: #aaa; } }
+
+#source p input:checked ~ .r label.ctx::before { content: "â–¼ "; }
+
+#source p input:checked ~ .ctxs { padding: .25em .5em; overflow-y: scroll; max-height: 10.5em; }
+
+#source p label.ctx { color: #999; display: inline-block; padding: 0 .5em; font-size: .8333em; }
+
+@media (prefers-color-scheme: dark) { #source p label.ctx { color: #777; } }
+
+#source p .ctxs { display: block; max-height: 0; overflow-y: hidden; transition: all .2s; padding: 0 .5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; white-space: nowrap; background: #aef; border-radius: .25em; margin-right: 1.75em; }
+
+@media (prefers-color-scheme: dark) { #source p .ctxs { background: #056; } }
+
+#source p .ctxs span { display: block; text-align: right; }
+
+#index { font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.875em; }
+
+#index table.index { margin-left: -.5em; }
+
+#index td, #index th { text-align: right; width: 5em; padding: .25em .5em; border-bottom: 1px solid #eee; }
+
+@media (prefers-color-scheme: dark) { #index td, #index th { border-color: #333; } }
+
+#index td.name, #index th.name { text-align: left; width: auto; }
+
+#index th { font-style: italic; color: #333; cursor: pointer; }
+
+@media (prefers-color-scheme: dark) { #index th { color: #ddd; } }
+
+#index th:hover { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #index th:hover { background: #333; } }
+
+#index th.headerSortDown, #index th.headerSortUp { white-space: nowrap; background: #eee; }
+
+@media (prefers-color-scheme: dark) { #index th.headerSortDown, #index th.headerSortUp { background: #333; } }
+
+#index th.headerSortDown:after { content: " ↑"; }
+
+#index th.headerSortUp:after { content: " ↓"; }
+
+#index td.name a { text-decoration: none; color: inherit; }
+
+#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; }
+
+#index tr.file:hover { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #index tr.file:hover { background: #333; } }
+
+#index tr.file:hover td.name { text-decoration: underline; color: inherit; }
+
+#scroll_marker { position: fixed; right: 0; top: 0; width: 16px; height: 100%; background: #fff; border-left: 1px solid #eee; will-change: transform; }
+
+@media (prefers-color-scheme: dark) { #scroll_marker { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { #scroll_marker { border-color: #333; } }
+
+#scroll_marker .marker { background: #ccc; position: absolute; min-height: 3px; width: 100%; }
+
+@media (prefers-color-scheme: dark) { #scroll_marker .marker { background: #444; } }
diff --git a/contrib/python/coverage/py2/coverage/htmlfiles/style.scss b/contrib/python/coverage/py2/coverage/htmlfiles/style.scss
new file mode 100644
index 0000000000..158d1fb493
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/htmlfiles/style.scss
@@ -0,0 +1,660 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+// CSS styles for coverage.py HTML reports.
+
+// When you edit this file, you need to run "make css" to get the CSS file
+// generated, and then check in both the .scss and the .css files.
+
+// When working on the file, this command is useful:
+// sass --watch --style=compact --sourcemap=none --no-cache coverage/htmlfiles/style.scss:htmlcov/style.css
+//
+// OR you can process sass purely in python with `pip install pysass`, then:
+// pysassc --style=compact coverage/htmlfiles/style.scss coverage/htmlfiles/style.css
+
+// Ignore this comment, it's for the CSS output file:
+/* Don't edit this .css file. Edit the .scss file instead! */
+
+// Dimensions
+$left-gutter: 3.5rem;
+
+
+//
+// Declare colors and variables
+//
+
+$font-normal: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+$font-code: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+
+$off-button-lighten: 50%;
+$hover-dark-amt: 95%;
+
+$focus-color: #007acc;
+
+$mis-color: #ff0000;
+$run-color: #00dd00;
+$exc-color: #808080;
+$par-color: #dddd00;
+
+$light-bg: #fff;
+$light-fg: #000;
+$light-gray1: #f8f8f8;
+$light-gray2: #eee;
+$light-gray3: #ccc;
+$light-gray4: #999;
+$light-gray5: #666;
+$light-gray6: #333;
+$light-pln-bg: $light-bg;
+$light-mis-bg: #fdd;
+$light-run-bg: #dfd;
+$light-exc-bg: $light-gray2;
+$light-par-bg: #ffa;
+$light-token-com: #008000;
+$light-token-str: #0451A5;
+$light-context-bg-color: #aef;
+
+$dark-bg: #1e1e1e;
+$dark-fg: #eee;
+$dark-gray1: #222;
+$dark-gray2: #333;
+$dark-gray3: #444;
+$dark-gray4: #777;
+$dark-gray5: #aaa;
+$dark-gray6: #ddd;
+$dark-pln-bg: $dark-bg;
+$dark-mis-bg: #4b1818;
+$dark-run-bg: #373d29;
+$dark-exc-bg: $dark-gray2;
+$dark-par-bg: #650;
+$dark-token-com: #6A9955;
+$dark-token-str: #9CDCFE;
+$dark-context-bg-color: #056;
+
+//
+// Mixins and utilities
+//
+@mixin background-dark($color) {
+ @media (prefers-color-scheme: dark) {
+ background: $color;
+ }
+}
+@mixin color-dark($color) {
+ @media (prefers-color-scheme: dark) {
+ color: $color;
+ }
+}
+@mixin border-color-dark($color) {
+ @media (prefers-color-scheme: dark) {
+ border-color: $color;
+ }
+}
+
+// Add visual outline to navigable elements on focus improve accessibility.
+@mixin focus-border {
+ &:active, &:focus {
+ outline: 2px dashed $focus-color;
+ }
+}
+
+// Page-wide styles
+html, body, h1, h2, h3, p, table, td, th {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
+}
+
+// Set baseline grid to 16 pt.
+body {
+ font-family: $font-normal;
+ font-size: 1em;
+ background: $light-bg;
+ color: $light-fg;
+ @include background-dark($dark-bg);
+ @include color-dark($dark-fg);
+}
+
+html>body {
+ font-size: 16px;
+}
+
+a {
+ @include focus-border;
+}
+
+p {
+ font-size: .875em;
+ line-height: 1.4em;
+}
+
+table {
+ border-collapse: collapse;
+}
+td {
+ vertical-align: top;
+}
+table tr.hidden {
+ display: none !important;
+}
+
+p#no_rows {
+ display: none;
+ font-size: 1.2em;
+}
+
+a.nav {
+ text-decoration: none;
+ color: inherit;
+
+ &:hover {
+ text-decoration: underline;
+ color: inherit;
+ }
+}
+
+// Page structure
+#header {
+ background: $light-gray1;
+ @include background-dark(black);
+ width: 100%;
+ border-bottom: 1px solid $light-gray2;
+ @include border-color-dark($dark-gray2);
+}
+
+.indexfile #footer {
+ margin: 1rem $left-gutter;
+}
+
+.pyfile #footer {
+ margin: 1rem 1rem;
+}
+
+#footer .content {
+ padding: 0;
+ color: $light-gray5;
+ @include color-dark($dark-gray5);
+ font-style: italic;
+}
+
+#index {
+ margin: 1rem 0 0 $left-gutter;
+}
+
+// Header styles
+#header .content {
+ padding: 1rem $left-gutter;
+}
+
+h1 {
+ font-size: 1.25em;
+ display: inline-block;
+}
+
+#filter_container {
+ float: right;
+ margin: 0 2em 0 0;
+
+ input {
+ width: 10em;
+ padding: 0.2em 0.5em;
+ border: 2px solid $light-gray3;
+ background: $light-bg;
+ color: $light-fg;
+ @include border-color-dark($dark-gray3);
+ @include background-dark($dark-bg);
+ @include color-dark($dark-fg);
+ &:focus {
+ border-color: $focus-color;
+ }
+ }
+}
+
+h2.stats {
+ margin-top: .5em;
+ font-size: 1em;
+}
+.stats button {
+ font-family: inherit;
+ font-size: inherit;
+ border: 1px solid;
+ border-radius: .2em;
+ color: inherit;
+ padding: .1em .5em;
+ margin: 1px calc(.1em + 1px);
+ cursor: pointer;
+ border-color: $light-gray3;
+ @include border-color-dark($dark-gray3);
+ @include focus-border;
+
+ @include focus-border;
+
+ &.run {
+ background: mix($light-run-bg, $light-bg, $off-button-lighten);
+ @include background-dark($dark-run-bg);
+ &.show_run {
+ background: $light-run-bg;
+ @include background-dark($dark-run-bg);
+ border: 2px solid $run-color;
+ margin: 0 .1em;
+ }
+ }
+ &.mis {
+ background: mix($light-mis-bg, $light-bg, $off-button-lighten);
+ @include background-dark($dark-mis-bg);
+ &.show_mis {
+ background: $light-mis-bg;
+ @include background-dark($dark-mis-bg);
+ border: 2px solid $mis-color;
+ margin: 0 .1em;
+ }
+ }
+ &.exc {
+ background: mix($light-exc-bg, $light-bg, $off-button-lighten);
+ @include background-dark($dark-exc-bg);
+ &.show_exc {
+ background: $light-exc-bg;
+ @include background-dark($dark-exc-bg);
+ border: 2px solid $exc-color;
+ margin: 0 .1em;
+ }
+ }
+ &.par {
+ background: mix($light-par-bg, $light-bg, $off-button-lighten);
+ @include background-dark($dark-par-bg);
+ &.show_par {
+ background: $light-par-bg;
+ @include background-dark($dark-par-bg);
+ border: 2px solid $par-color;
+ margin: 0 .1em;
+ }
+ }
+}
+
+// Yellow post-it things.
+%popup {
+ display: none;
+ position: absolute;
+ z-index: 999;
+ background: #ffffcc;
+ border: 1px solid #888;
+ border-radius: .2em;
+ color: #333;
+ padding: .25em .5em;
+}
+
+// Yellow post-it's in the text listings.
+%in-text-popup {
+ @extend %popup;
+ white-space: normal;
+ float: right;
+ top: 1.75em;
+ right: 1em;
+ height: auto;
+}
+
+// Help panel
+#keyboard_icon {
+ float: right;
+ margin: 5px;
+ cursor: pointer;
+}
+
+.help_panel {
+ @extend %popup;
+ padding: .5em;
+ border: 1px solid #883;
+
+ .legend {
+ font-style: italic;
+ margin-bottom: 1em;
+ }
+
+ .indexfile & {
+ width: 20em;
+ min-height: 4em;
+ }
+
+ .pyfile & {
+ width: 16em;
+ min-height: 8em;
+ }
+}
+
+#panel_icon {
+ float: right;
+ cursor: pointer;
+}
+
+.keyhelp {
+ margin: .75em;
+
+ .key {
+ border: 1px solid black;
+ border-color: #888 #333 #333 #888;
+ padding: .1em .35em;
+ font-family: $font-code;
+ font-weight: bold;
+ background: #eee;
+ }
+}
+
+// Source file styles
+
+// The slim bar at the left edge of the source lines, colored by coverage.
+$border-indicator-width: .2em;
+
+#source {
+ padding: 1em 0 1em $left-gutter;
+ font-family: $font-code;
+
+ p {
+ // position relative makes position:absolute pop-ups appear in the right place.
+ position: relative;
+ white-space: pre;
+
+ * {
+ box-sizing: border-box;
+ }
+
+ .n {
+ float: left;
+ text-align: right;
+ width: $left-gutter;
+ box-sizing: border-box;
+ margin-left: -$left-gutter;
+ padding-right: 1em;
+ color: $light-gray4;
+ @include color-dark($dark-gray4);
+
+ a {
+ text-decoration: none;
+ color: $light-gray4;
+ @include color-dark($dark-gray4);
+ &:hover {
+ text-decoration: underline;
+ color: $light-gray4;
+ @include color-dark($dark-gray4);
+ }
+ }
+ }
+
+ &.highlight .n {
+ background: #ffdd00;
+ }
+
+ .t {
+ display: inline-block;
+ width: 100%;
+ box-sizing: border-box;
+ margin-left: -.5em;
+ padding-left: .5em - $border-indicator-width;
+ border-left: $border-indicator-width solid $light-bg;
+ @include border-color-dark($dark-bg);
+
+ &:hover {
+ background: mix($light-pln-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-pln-bg, $dark-fg, $hover-dark-amt));
+
+ & ~ .r .annotate.long {
+ display: block;
+ }
+ }
+
+ // Syntax coloring
+ .com {
+ color: $light-token-com;
+ @include color-dark($dark-token-com);
+ font-style: italic;
+ line-height: 1px;
+ }
+ .key {
+ font-weight: bold;
+ line-height: 1px;
+ }
+ .str {
+ color: $light-token-str;
+ @include color-dark($dark-token-str);
+ }
+ }
+
+ &.mis {
+ .t {
+ border-left: $border-indicator-width solid $mis-color;
+ }
+
+ &.show_mis .t {
+ background: $light-mis-bg;
+ @include background-dark($dark-mis-bg);
+
+ &:hover {
+ background: mix($light-mis-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-mis-bg, $dark-fg, $hover-dark-amt));
+ }
+ }
+ }
+
+ &.run {
+ .t {
+ border-left: $border-indicator-width solid $run-color;
+ }
+
+ &.show_run .t {
+ background: $light-run-bg;
+ @include background-dark($dark-run-bg);
+
+ &:hover {
+ background: mix($light-run-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-run-bg, $dark-fg, $hover-dark-amt));
+ }
+ }
+ }
+
+ &.exc {
+ .t {
+ border-left: $border-indicator-width solid $exc-color;
+ }
+
+ &.show_exc .t {
+ background: $light-exc-bg;
+ @include background-dark($dark-exc-bg);
+
+ &:hover {
+ background: mix($light-exc-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-exc-bg, $dark-fg, $hover-dark-amt));
+ }
+ }
+ }
+
+ &.par {
+ .t {
+ border-left: $border-indicator-width solid $par-color;
+ }
+
+ &.show_par .t {
+ background: $light-par-bg;
+ @include background-dark($dark-par-bg);
+
+ &:hover {
+ background: mix($light-par-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-par-bg, $dark-fg, $hover-dark-amt));
+ }
+ }
+
+ }
+
+ .r {
+ position: absolute;
+ top: 0;
+ right: 2.5em;
+ font-family: $font-normal;
+ }
+
+ .annotate {
+ font-family: $font-normal;
+ color: $light-gray5;
+ @include color-dark($dark-gray6);
+ padding-right: .5em;
+
+ &.short:hover ~ .long {
+ display: block;
+ }
+
+ &.long {
+ @extend %in-text-popup;
+ width: 30em;
+ right: 2.5em;
+ }
+ }
+
+ input {
+ display: none;
+
+ & ~ .r label.ctx {
+ cursor: pointer;
+ border-radius: .25em;
+ &::before {
+ content: "â–¶ ";
+ }
+ &:hover {
+ background: mix($light-context-bg-color, $light-bg, $off-button-lighten);
+ @include background-dark(mix($dark-context-bg-color, $dark-bg, $off-button-lighten));
+ color: $light-gray5;
+ @include color-dark($dark-gray5);
+ }
+ }
+
+ &:checked ~ .r label.ctx {
+ background: $light-context-bg-color;
+ @include background-dark($dark-context-bg-color);
+ color: $light-gray5;
+ @include color-dark($dark-gray5);
+ border-radius: .75em .75em 0 0;
+ padding: 0 .5em;
+ margin: -.25em 0;
+ &::before {
+ content: "â–¼ ";
+ }
+ }
+
+ &:checked ~ .ctxs {
+ padding: .25em .5em;
+ overflow-y: scroll;
+ max-height: 10.5em;
+ }
+ }
+
+ label.ctx {
+ color: $light-gray4;
+ @include color-dark($dark-gray4);
+ display: inline-block;
+ padding: 0 .5em;
+ font-size: .8333em; // 10/12
+ }
+
+ .ctxs {
+ display: block;
+ max-height: 0;
+ overflow-y: hidden;
+ transition: all .2s;
+ padding: 0 .5em;
+ font-family: $font-normal;
+ white-space: nowrap;
+ background: $light-context-bg-color;
+ @include background-dark($dark-context-bg-color);
+ border-radius: .25em;
+ margin-right: 1.75em;
+ span {
+ display: block;
+ text-align: right;
+ }
+ }
+ }
+}
+
+
+// index styles
+#index {
+ font-family: $font-code;
+ font-size: 0.875em;
+
+ table.index {
+ margin-left: -.5em;
+ }
+ td, th {
+ text-align: right;
+ width: 5em;
+ padding: .25em .5em;
+ border-bottom: 1px solid $light-gray2;
+ @include border-color-dark($dark-gray2);
+ &.name {
+ text-align: left;
+ width: auto;
+ }
+ }
+ th {
+ font-style: italic;
+ color: $light-gray6;
+ @include color-dark($dark-gray6);
+ cursor: pointer;
+ &:hover {
+ background: $light-gray2;
+ @include background-dark($dark-gray2);
+ }
+ &.headerSortDown, &.headerSortUp {
+ white-space: nowrap;
+ background: $light-gray2;
+ @include background-dark($dark-gray2);
+ }
+ &.headerSortDown:after {
+ content: " ↑";
+ }
+ &.headerSortUp:after {
+ content: " ↓";
+ }
+ }
+ td.name a {
+ text-decoration: none;
+ color: inherit;
+ }
+
+ tr.total td,
+ tr.total_dynamic td {
+ font-weight: bold;
+ border-top: 1px solid #ccc;
+ border-bottom: none;
+ }
+ tr.file:hover {
+ background: $light-gray2;
+ @include background-dark($dark-gray2);
+ td.name {
+ text-decoration: underline;
+ color: inherit;
+ }
+ }
+}
+
+// scroll marker styles
+#scroll_marker {
+ position: fixed;
+ right: 0;
+ top: 0;
+ width: 16px;
+ height: 100%;
+ background: $light-bg;
+ border-left: 1px solid $light-gray2;
+ @include background-dark($dark-bg);
+ @include border-color-dark($dark-gray2);
+ will-change: transform; // for faster scrolling of fixed element in Chrome
+
+ .marker {
+ background: $light-gray3;
+ @include background-dark($dark-gray3);
+ position: absolute;
+ min-height: 3px;
+ width: 100%;
+ }
+}
diff --git a/contrib/python/coverage/py2/coverage/inorout.py b/contrib/python/coverage/py2/coverage/inorout.py
new file mode 100644
index 0000000000..cbc80e8fb5
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/inorout.py
@@ -0,0 +1,513 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Determining whether files are being measured/reported or not."""
+
+# For finding the stdlib
+import atexit
+import inspect
+import itertools
+import os
+import platform
+import re
+import sys
+import traceback
+
+from coverage import env
+from coverage.backward import code_object
+from coverage.disposition import FileDisposition, disposition_init
+from coverage.files import TreeMatcher, FnmatchMatcher, ModuleMatcher
+from coverage.files import prep_patterns, find_python_files, canonical_filename
+from coverage.misc import CoverageException
+from coverage.python import source_for_file, source_for_morf
+
+
+# Pypy has some unusual stuff in the "stdlib". Consider those locations
+# when deciding where the stdlib is. These modules are not used for anything,
+# they are modules importable from the pypy lib directories, so that we can
+# find those directories.
+_structseq = _pypy_irc_topic = None
+if env.PYPY:
+ try:
+ import _structseq
+ except ImportError:
+ pass
+
+ try:
+ import _pypy_irc_topic
+ except ImportError:
+ pass
+
+
+def canonical_path(morf, directory=False):
+ """Return the canonical path of the module or file `morf`.
+
+ If the module is a package, then return its directory. If it is a
+ module, then return its file, unless `directory` is True, in which
+ case return its enclosing directory.
+
+ """
+ morf_path = canonical_filename(source_for_morf(morf))
+ if morf_path.endswith("__init__.py") or directory:
+ morf_path = os.path.split(morf_path)[0]
+ return morf_path
+
+
+def name_for_module(filename, frame):
+ """Get the name of the module for a filename and frame.
+
+ For configurability's sake, we allow __main__ modules to be matched by
+ their importable name.
+
+ If loaded via runpy (aka -m), we can usually recover the "original"
+ full dotted module name, otherwise, we resort to interpreting the
+ file name to get the module's name. In the case that the module name
+ can't be determined, None is returned.
+
+ """
+ module_globals = frame.f_globals if frame is not None else {}
+ if module_globals is None: # pragma: only ironpython
+ # IronPython doesn't provide globals: https://github.com/IronLanguages/main/issues/1296
+ module_globals = {}
+
+ dunder_name = module_globals.get('__name__', None)
+
+ if isinstance(dunder_name, str) and dunder_name != '__main__':
+ # This is the usual case: an imported module.
+ return dunder_name
+
+ loader = module_globals.get('__loader__', None)
+ for attrname in ('fullname', 'name'): # attribute renamed in py3.2
+ if hasattr(loader, attrname):
+ fullname = getattr(loader, attrname)
+ else:
+ continue
+
+ if isinstance(fullname, str) and fullname != '__main__':
+ # Module loaded via: runpy -m
+ return fullname
+
+ # Script as first argument to Python command line.
+ inspectedname = inspect.getmodulename(filename)
+ if inspectedname is not None:
+ return inspectedname
+ else:
+ return dunder_name
+
+
+def module_is_namespace(mod):
+ """Is the module object `mod` a PEP420 namespace module?"""
+ return hasattr(mod, '__path__') and getattr(mod, '__file__', None) is None
+
+
+def module_has_file(mod):
+ """Does the module object `mod` have an existing __file__ ?"""
+ mod__file__ = getattr(mod, '__file__', None)
+ if mod__file__ is None:
+ return False
+ return os.path.exists(mod__file__)
+
+
+class InOrOut(object):
+ """Machinery for determining what files to measure."""
+
+ def __init__(self, warn, debug):
+ self.warn = warn
+ self.debug = debug
+
+ # The matchers for should_trace.
+ self.source_match = None
+ self.source_pkgs_match = None
+ self.pylib_paths = self.cover_paths = None
+ self.pylib_match = self.cover_match = None
+ self.include_match = self.omit_match = None
+ self.plugins = []
+ self.disp_class = FileDisposition
+
+ # The source argument can be directories or package names.
+ self.source = []
+ self.source_pkgs = []
+ self.source_pkgs_unmatched = []
+ self.omit = self.include = None
+
+ def configure(self, config):
+ """Apply the configuration to get ready for decision-time."""
+ self.config = config
+ self.source_pkgs.extend(config.source_pkgs)
+ for src in config.source or []:
+ if os.path.isdir(src):
+ self.source.append(canonical_filename(src))
+ else:
+ self.source_pkgs.append(src)
+ self.source_pkgs_unmatched = self.source_pkgs[:]
+
+ self.omit = prep_patterns(config.run_omit)
+ if getattr(sys, 'is_standalone_binary', False):
+ # don't trace contrib
+ self.omit.append('contrib/python/*')
+ self.omit.append('contrib/libs/protobuf/*')
+ self.omit.append('library/python/pytest/*')
+ self.include = prep_patterns(config.run_include)
+
+ # The directories for files considered "installed with the interpreter".
+ self.pylib_paths = set()
+ if getattr(sys, 'is_standalone_binary', False):
+ self.pylib_paths.add('contrib/tools/python')
+ self.pylib_paths.add('contrib/tools/python3')
+ if not self.pylib_paths and not config.cover_pylib:
+ # Look at where some standard modules are located. That's the
+ # indication for "installed with the interpreter". In some
+ # environments (virtualenv, for example), these modules may be
+ # spread across a few locations. Look at all the candidate modules
+ # we've imported, and take all the different ones.
+ for m in (atexit, inspect, os, platform, _pypy_irc_topic, re, _structseq, traceback):
+ if m is not None and hasattr(m, "__file__"):
+ self.pylib_paths.add(canonical_path(m, directory=True))
+
+ if _structseq and not hasattr(_structseq, '__file__'):
+ # PyPy 2.4 has no __file__ in the builtin modules, but the code
+ # objects still have the file names. So dig into one to find
+ # the path to exclude. The "filename" might be synthetic,
+ # don't be fooled by those.
+ structseq_file = code_object(_structseq.structseq_new).co_filename
+ if not structseq_file.startswith("<"):
+ self.pylib_paths.add(canonical_path(structseq_file))
+
+ # To avoid tracing the coverage.py code itself, we skip anything
+ # located where we are.
+ if getattr(sys, 'is_standalone_binary', False):
+ self.cover_paths = ["contrib/python/coverage"]
+ else:
+ self.cover_paths = [canonical_path(__file__, directory=True)]
+ if env.TESTING:
+ # Don't include our own test code.
+ self.cover_paths.append(os.path.join(self.cover_paths[0], "tests"))
+
+ # When testing, we use PyContracts, which should be considered
+ # part of coverage.py, and it uses six. Exclude those directories
+ # just as we exclude ourselves.
+ import contracts
+ import six
+ for mod in [contracts, six]:
+ self.cover_paths.append(canonical_path(mod))
+
+ def debug(msg):
+ if self.debug:
+ self.debug.write(msg)
+
+ # Create the matchers we need for should_trace
+ if self.source or self.source_pkgs:
+ against = []
+ if self.source:
+ self.source_match = TreeMatcher(self.source)
+ against.append("trees {!r}".format(self.source_match))
+ if self.source_pkgs:
+ self.source_pkgs_match = ModuleMatcher(self.source_pkgs)
+ against.append("modules {!r}".format(self.source_pkgs_match))
+ debug("Source matching against " + " and ".join(against))
+ else:
+ if self.cover_paths:
+ self.cover_match = TreeMatcher(self.cover_paths)
+ debug("Coverage code matching: {!r}".format(self.cover_match))
+ if self.pylib_paths:
+ self.pylib_match = TreeMatcher(self.pylib_paths)
+ debug("Python stdlib matching: {!r}".format(self.pylib_match))
+ if self.include:
+ self.include_match = FnmatchMatcher(self.include)
+ debug("Include matching: {!r}".format(self.include_match))
+ if self.omit:
+ self.omit_match = FnmatchMatcher(self.omit)
+ debug("Omit matching: {!r}".format(self.omit_match))
+
+ def should_trace(self, filename, frame=None):
+ """Decide whether to trace execution in `filename`, with a reason.
+
+ This function is called from the trace function. As each new file name
+ is encountered, this function determines whether it is traced or not.
+
+ Returns a FileDisposition object.
+
+ """
+ original_filename = filename
+ disp = disposition_init(self.disp_class, filename)
+
+ def nope(disp, reason):
+ """Simple helper to make it easy to return NO."""
+ disp.trace = False
+ disp.reason = reason
+ return disp
+
+ if frame is not None:
+ # Compiled Python files have two file names: frame.f_code.co_filename is
+ # the file name at the time the .pyc was compiled. The second name is
+ # __file__, which is where the .pyc was actually loaded from. Since
+ # .pyc files can be moved after compilation (for example, by being
+ # installed), we look for __file__ in the frame and prefer it to the
+ # co_filename value.
+ dunder_file = frame.f_globals and frame.f_globals.get('__file__')
+ if dunder_file:
+ filename = source_for_file(dunder_file)
+ if original_filename and not original_filename.startswith('<'):
+ orig = os.path.basename(original_filename)
+ if orig != os.path.basename(filename):
+ # Files shouldn't be renamed when moved. This happens when
+ # exec'ing code. If it seems like something is wrong with
+ # the frame's file name, then just use the original.
+ filename = original_filename
+
+ if not filename:
+ # Empty string is pretty useless.
+ return nope(disp, "empty string isn't a file name")
+
+ if filename.startswith('memory:'):
+ return nope(disp, "memory isn't traceable")
+
+ if filename.startswith('<'):
+ # Lots of non-file execution is represented with artificial
+ # file names like "<string>", "<doctest readme.txt[0]>", or
+ # "<exec_function>". Don't ever trace these executions, since we
+ # can't do anything with the data later anyway.
+ return nope(disp, "not a real file name")
+
+ # pyexpat does a dumb thing, calling the trace function explicitly from
+ # C code with a C file name.
+ if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename):
+ return nope(disp, "pyexpat lies about itself")
+
+ # Jython reports the .class file to the tracer, use the source file.
+ if filename.endswith("$py.class"):
+ filename = filename[:-9] + ".py"
+
+ # XXX maybe we need to support both at the same time?
+ # Don't trace modules imported from environment in standalone mode
+ if getattr(sys, 'is_standalone_binary', False) and filename.startswith("/"):
+ return nope(disp, "skip modules from environment")
+
+ canonical = canonical_filename(filename)
+ disp.canonical_filename = canonical
+
+ # Try the plugins, see if they have an opinion about the file.
+ plugin = None
+ for plugin in self.plugins.file_tracers:
+ if not plugin._coverage_enabled:
+ continue
+
+ try:
+ file_tracer = plugin.file_tracer(canonical)
+ if file_tracer is not None:
+ file_tracer._coverage_plugin = plugin
+ disp.trace = True
+ disp.file_tracer = file_tracer
+ if file_tracer.has_dynamic_source_filename():
+ disp.has_dynamic_filename = True
+ else:
+ disp.source_filename = canonical_filename(
+ file_tracer.source_filename()
+ )
+ break
+ except Exception:
+ if not self.config.suppress_plugin_errors:
+ raise
+ self.warn(
+ "Disabling plug-in %r due to an exception:" % (plugin._coverage_plugin_name)
+ )
+ traceback.print_exc()
+ plugin._coverage_enabled = False
+ continue
+ else:
+ # No plugin wanted it: it's Python.
+ disp.trace = True
+ disp.source_filename = canonical
+
+ if not disp.has_dynamic_filename:
+ if not disp.source_filename:
+ raise CoverageException(
+ "Plugin %r didn't set source_filename for %r" %
+ (plugin, disp.original_filename)
+ )
+ reason = self.check_include_omit_etc(disp.source_filename, frame)
+ if reason:
+ nope(disp, reason)
+
+ return disp
+
+ def check_include_omit_etc(self, filename, frame):
+ """Check a file name against the include, omit, etc, rules.
+
+ Returns a string or None. String means, don't trace, and is the reason
+ why. None means no reason found to not trace.
+
+ """
+ modulename = name_for_module(filename, frame)
+
+ # If the user specified source or include, then that's authoritative
+ # about the outer bound of what to measure and we don't have to apply
+ # any canned exclusions. If they didn't, then we have to exclude the
+ # stdlib and coverage.py directories.
+ if self.source_match or self.source_pkgs_match:
+ extra = ""
+ ok = False
+ if self.source_pkgs_match:
+ if self.source_pkgs_match.match(modulename):
+ ok = True
+ if modulename in self.source_pkgs_unmatched:
+ self.source_pkgs_unmatched.remove(modulename)
+ else:
+ extra = "module {!r} ".format(modulename)
+ if not ok and self.source_match:
+ if self.source_match.match(filename):
+ ok = True
+ if not ok:
+ return extra + "falls outside the --source spec"
+ elif self.include_match:
+ if not self.include_match.match(filename):
+ return "falls outside the --include trees"
+ else:
+ # If we aren't supposed to trace installed code, then check if this
+ # is near the Python standard library and skip it if so.
+ if self.pylib_match and self.pylib_match.match(filename):
+ return "is in the stdlib"
+
+ # We exclude the coverage.py code itself, since a little of it
+ # will be measured otherwise.
+ if self.cover_match and self.cover_match.match(filename):
+ return "is part of coverage.py"
+
+ # Check the file against the omit pattern.
+ if self.omit_match and self.omit_match.match(filename):
+ return "is inside an --omit pattern"
+
+ # No point tracing a file we can't later write to SQLite.
+ try:
+ filename.encode("utf8")
+ except UnicodeEncodeError:
+ return "non-encodable filename"
+
+ # No reason found to skip this file.
+ return None
+
+ def warn_conflicting_settings(self):
+ """Warn if there are settings that conflict."""
+ if self.include:
+ if self.source or self.source_pkgs:
+ self.warn("--include is ignored because --source is set", slug="include-ignored")
+
+ def warn_already_imported_files(self):
+ """Warn if files have already been imported that we will be measuring."""
+ if self.include or self.source or self.source_pkgs:
+ warned = set()
+ for mod in list(sys.modules.values()):
+ filename = getattr(mod, "__file__", None)
+ if filename is None:
+ continue
+ if filename in warned:
+ continue
+
+ disp = self.should_trace(filename)
+ if disp.trace:
+ msg = "Already imported a file that will be measured: {}".format(filename)
+ self.warn(msg, slug="already-imported")
+ warned.add(filename)
+
+ def warn_unimported_source(self):
+ """Warn about source packages that were of interest, but never traced."""
+ for pkg in self.source_pkgs_unmatched:
+ self._warn_about_unmeasured_code(pkg)
+
+ def _warn_about_unmeasured_code(self, pkg):
+ """Warn about a package or module that we never traced.
+
+ `pkg` is a string, the name of the package or module.
+
+ """
+ mod = sys.modules.get(pkg)
+ if mod is None:
+ self.warn("Module %s was never imported." % pkg, slug="module-not-imported")
+ return
+
+ if module_is_namespace(mod):
+ # A namespace package. It's OK for this not to have been traced,
+ # since there is no code directly in it.
+ return
+
+ if not module_has_file(mod):
+ self.warn("Module %s has no Python source." % pkg, slug="module-not-python")
+ return
+
+ # The module was in sys.modules, and seems like a module with code, but
+ # we never measured it. I guess that means it was imported before
+ # coverage even started.
+ self.warn(
+ "Module %s was previously imported, but not measured" % pkg,
+ slug="module-not-measured",
+ )
+
+ def find_possibly_unexecuted_files(self):
+ """Find files in the areas of interest that might be untraced.
+
+ Yields pairs: file path, and responsible plug-in name.
+ """
+ for pkg in self.source_pkgs:
+ if (not pkg in sys.modules or
+ not module_has_file(sys.modules[pkg])):
+ continue
+ pkg_file = source_for_file(sys.modules[pkg].__file__)
+ for ret in self._find_executable_files(canonical_path(pkg_file)):
+ yield ret
+
+ for src in self.source:
+ for ret in self._find_executable_files(src):
+ yield ret
+
+ def _find_plugin_files(self, src_dir):
+ """Get executable files from the plugins."""
+ for plugin in self.plugins.file_tracers:
+ for x_file in plugin.find_executable_files(src_dir):
+ yield x_file, plugin._coverage_plugin_name
+
+ def _find_executable_files(self, src_dir):
+ """Find executable files in `src_dir`.
+
+ Search for files in `src_dir` that can be executed because they
+ are probably importable. Don't include ones that have been omitted
+ by the configuration.
+
+ Yield the file path, and the plugin name that handles the file.
+
+ """
+ py_files = ((py_file, None) for py_file in find_python_files(src_dir))
+ plugin_files = self._find_plugin_files(src_dir)
+
+ for file_path, plugin_name in itertools.chain(py_files, plugin_files):
+ file_path = canonical_filename(file_path)
+ if self.omit_match and self.omit_match.match(file_path):
+ # Turns out this file was omitted, so don't pull it back
+ # in as unexecuted.
+ continue
+ yield file_path, plugin_name
+
+ def sys_info(self):
+ """Our information for Coverage.sys_info.
+
+ Returns a list of (key, value) pairs.
+ """
+ info = [
+ ('cover_paths', self.cover_paths),
+ ('pylib_paths', self.pylib_paths),
+ ]
+
+ matcher_names = [
+ 'source_match', 'source_pkgs_match',
+ 'include_match', 'omit_match',
+ 'cover_match', 'pylib_match',
+ ]
+
+ for matcher_name in matcher_names:
+ matcher = getattr(self, matcher_name)
+ if matcher:
+ matcher_info = matcher.info()
+ else:
+ matcher_info = '-none-'
+ info.append((matcher_name, matcher_info))
+
+ return info
diff --git a/contrib/python/coverage/py2/coverage/jsonreport.py b/contrib/python/coverage/py2/coverage/jsonreport.py
new file mode 100644
index 0000000000..4287bc79a3
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/jsonreport.py
@@ -0,0 +1,103 @@
+# coding: utf-8
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Json reporting for coverage.py"""
+import datetime
+import json
+import sys
+
+from coverage import __version__
+from coverage.report import get_analysis_to_report
+from coverage.results import Numbers
+
+
+class JsonReporter(object):
+ """A reporter for writing JSON coverage results."""
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+ self.total = Numbers()
+ self.report_data = {}
+
+ def report(self, morfs, outfile=None):
+ """Generate a json report for `morfs`.
+
+ `morfs` is a list of modules or file names.
+
+ `outfile` is a file object to write the json to
+
+ """
+ outfile = outfile or sys.stdout
+ coverage_data = self.coverage.get_data()
+ coverage_data.set_query_contexts(self.config.report_contexts)
+ self.report_data["meta"] = {
+ "version": __version__,
+ "timestamp": datetime.datetime.now().isoformat(),
+ "branch_coverage": coverage_data.has_arcs(),
+ "show_contexts": self.config.json_show_contexts,
+ }
+
+ measured_files = {}
+ for file_reporter, analysis in get_analysis_to_report(self.coverage, morfs):
+ measured_files[file_reporter.relative_filename()] = self.report_one_file(
+ coverage_data,
+ analysis
+ )
+
+ self.report_data["files"] = measured_files
+
+ self.report_data["totals"] = {
+ 'covered_lines': self.total.n_executed,
+ 'num_statements': self.total.n_statements,
+ 'percent_covered': self.total.pc_covered,
+ 'missing_lines': self.total.n_missing,
+ 'excluded_lines': self.total.n_excluded,
+ }
+
+ if coverage_data.has_arcs():
+ self.report_data["totals"].update({
+ 'num_branches': self.total.n_branches,
+ 'num_partial_branches': self.total.n_partial_branches,
+ 'covered_branches': self.total.n_executed_branches,
+ 'missing_branches': self.total.n_missing_branches,
+ })
+
+ json.dump(
+ self.report_data,
+ outfile,
+ indent=4 if self.config.json_pretty_print else None
+ )
+
+ return self.total.n_statements and self.total.pc_covered
+
+ def report_one_file(self, coverage_data, analysis):
+ """Extract the relevant report data for a single file"""
+ nums = analysis.numbers
+ self.total += nums
+ summary = {
+ 'covered_lines': nums.n_executed,
+ 'num_statements': nums.n_statements,
+ 'percent_covered': nums.pc_covered,
+ 'missing_lines': nums.n_missing,
+ 'excluded_lines': nums.n_excluded,
+ }
+ reported_file = {
+ 'executed_lines': sorted(analysis.executed),
+ 'summary': summary,
+ 'missing_lines': sorted(analysis.missing),
+ 'excluded_lines': sorted(analysis.excluded)
+ }
+ if self.config.json_show_contexts:
+ reported_file['contexts'] = analysis.data.contexts_by_lineno(
+ analysis.filename,
+ )
+ if coverage_data.has_arcs():
+ reported_file['summary'].update({
+ 'num_branches': nums.n_branches,
+ 'num_partial_branches': nums.n_partial_branches,
+ 'covered_branches': nums.n_executed_branches,
+ 'missing_branches': nums.n_missing_branches,
+ })
+ return reported_file
diff --git a/contrib/python/coverage/py2/coverage/misc.py b/contrib/python/coverage/py2/coverage/misc.py
new file mode 100644
index 0000000000..034e288eb9
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/misc.py
@@ -0,0 +1,361 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Miscellaneous stuff for coverage.py."""
+
+import errno
+import hashlib
+import inspect
+import locale
+import os
+import os.path
+import random
+import re
+import socket
+import sys
+import types
+
+from coverage import env
+from coverage.backward import to_bytes, unicode_class
+
+ISOLATED_MODULES = {}
+
+
+def isolate_module(mod):
+ """Copy a module so that we are isolated from aggressive mocking.
+
+ If a test suite mocks os.path.exists (for example), and then we need to use
+ it during the test, everything will get tangled up if we use their mock.
+ Making a copy of the module when we import it will isolate coverage.py from
+ those complications.
+ """
+ if mod not in ISOLATED_MODULES:
+ new_mod = types.ModuleType(mod.__name__)
+ ISOLATED_MODULES[mod] = new_mod
+ for name in dir(mod):
+ value = getattr(mod, name)
+ if isinstance(value, types.ModuleType):
+ value = isolate_module(value)
+ setattr(new_mod, name, value)
+ return ISOLATED_MODULES[mod]
+
+os = isolate_module(os)
+
+
+def dummy_decorator_with_args(*args_unused, **kwargs_unused):
+ """Dummy no-op implementation of a decorator with arguments."""
+ def _decorator(func):
+ return func
+ return _decorator
+
+
+# Environment COVERAGE_NO_CONTRACTS=1 can turn off contracts while debugging
+# tests to remove noise from stack traces.
+# $set_env.py: COVERAGE_NO_CONTRACTS - Disable PyContracts to simplify stack traces.
+USE_CONTRACTS = env.TESTING and not bool(int(os.environ.get("COVERAGE_NO_CONTRACTS", 0)))
+
+# Use PyContracts for assertion testing on parameters and returns, but only if
+# we are running our own test suite.
+if USE_CONTRACTS:
+ from contracts import contract # pylint: disable=unused-import
+ from contracts import new_contract as raw_new_contract
+
+ def new_contract(*args, **kwargs):
+ """A proxy for contracts.new_contract that doesn't mind happening twice."""
+ try:
+ raw_new_contract(*args, **kwargs)
+ except ValueError:
+ # During meta-coverage, this module is imported twice, and
+ # PyContracts doesn't like redefining contracts. It's OK.
+ pass
+
+ # Define contract words that PyContract doesn't have.
+ new_contract('bytes', lambda v: isinstance(v, bytes))
+ if env.PY3:
+ new_contract('unicode', lambda v: isinstance(v, unicode_class))
+
+ def one_of(argnames):
+ """Ensure that only one of the argnames is non-None."""
+ def _decorator(func):
+ argnameset = {name.strip() for name in argnames.split(",")}
+ def _wrapper(*args, **kwargs):
+ vals = [kwargs.get(name) for name in argnameset]
+ assert sum(val is not None for val in vals) == 1
+ return func(*args, **kwargs)
+ return _wrapper
+ return _decorator
+else: # pragma: not testing
+ # We aren't using real PyContracts, so just define our decorators as
+ # stunt-double no-ops.
+ contract = dummy_decorator_with_args
+ one_of = dummy_decorator_with_args
+
+ def new_contract(*args_unused, **kwargs_unused):
+ """Dummy no-op implementation of `new_contract`."""
+ pass
+
+
+def nice_pair(pair):
+ """Make a nice string representation of a pair of numbers.
+
+ If the numbers are equal, just return the number, otherwise return the pair
+ with a dash between them, indicating the range.
+
+ """
+ start, end = pair
+ if start == end:
+ return "%d" % start
+ else:
+ return "%d-%d" % (start, end)
+
+
+def expensive(fn):
+ """A decorator to indicate that a method shouldn't be called more than once.
+
+ Normally, this does nothing. During testing, this raises an exception if
+ called more than once.
+
+ """
+ if env.TESTING:
+ attr = "_once_" + fn.__name__
+
+ def _wrapper(self):
+ if hasattr(self, attr):
+ raise AssertionError("Shouldn't have called %s more than once" % fn.__name__)
+ setattr(self, attr, True)
+ return fn(self)
+ return _wrapper
+ else:
+ return fn # pragma: not testing
+
+
+def bool_or_none(b):
+ """Return bool(b), but preserve None."""
+ if b is None:
+ return None
+ else:
+ return bool(b)
+
+
+def join_regex(regexes):
+ """Combine a list of regexes into one that matches any of them."""
+ return "|".join("(?:%s)" % r for r in regexes)
+
+
+def file_be_gone(path):
+ """Remove a file, and don't get annoyed if it doesn't exist."""
+ try:
+ os.remove(path)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+
+
+def ensure_dir(directory):
+ """Make sure the directory exists.
+
+ If `directory` is None or empty, do nothing.
+ """
+ if directory and not os.path.isdir(directory):
+ os.makedirs(directory)
+
+
+def ensure_dir_for_file(path):
+ """Make sure the directory for the path exists."""
+ ensure_dir(os.path.dirname(path))
+
+
+def output_encoding(outfile=None):
+ """Determine the encoding to use for output written to `outfile` or stdout."""
+ if outfile is None:
+ outfile = sys.stdout
+ encoding = (
+ getattr(outfile, "encoding", None) or
+ getattr(sys.__stdout__, "encoding", None) or
+ locale.getpreferredencoding()
+ )
+ return encoding
+
+
+def filename_suffix(suffix):
+ """Compute a filename suffix for a data file.
+
+ If `suffix` is a string or None, simply return it. If `suffix` is True,
+ then build a suffix incorporating the hostname, process id, and a random
+ number.
+
+ Returns a string or None.
+
+ """
+ if suffix is True:
+ # If data_suffix was a simple true value, then make a suffix with
+ # plenty of distinguishing information. We do this here in
+ # `save()` at the last minute so that the pid will be correct even
+ # if the process forks.
+ dice = random.Random(os.urandom(8)).randint(0, 999999)
+ suffix = "%s.%s.%06d" % (socket.gethostname(), os.getpid(), dice)
+ return suffix
+
+
+class Hasher(object):
+ """Hashes Python data into md5."""
+ def __init__(self):
+ self.md5 = hashlib.md5()
+
+ def update(self, v):
+ """Add `v` to the hash, recursively if needed."""
+ self.md5.update(to_bytes(str(type(v))))
+ if isinstance(v, unicode_class):
+ self.md5.update(v.encode('utf8'))
+ elif isinstance(v, bytes):
+ self.md5.update(v)
+ elif v is None:
+ pass
+ elif isinstance(v, (int, float)):
+ self.md5.update(to_bytes(str(v)))
+ elif isinstance(v, (tuple, list)):
+ for e in v:
+ self.update(e)
+ elif isinstance(v, dict):
+ keys = v.keys()
+ for k in sorted(keys):
+ self.update(k)
+ self.update(v[k])
+ else:
+ for k in dir(v):
+ if k.startswith('__'):
+ continue
+ a = getattr(v, k)
+ if inspect.isroutine(a):
+ continue
+ self.update(k)
+ self.update(a)
+ self.md5.update(b'.')
+
+ def hexdigest(self):
+ """Retrieve the hex digest of the hash."""
+ return self.md5.hexdigest()
+
+
+def _needs_to_implement(that, func_name):
+ """Helper to raise NotImplementedError in interface stubs."""
+ if hasattr(that, "_coverage_plugin_name"):
+ thing = "Plugin"
+ name = that._coverage_plugin_name
+ else:
+ thing = "Class"
+ klass = that.__class__
+ name = "{klass.__module__}.{klass.__name__}".format(klass=klass)
+
+ raise NotImplementedError(
+ "{thing} {name!r} needs to implement {func_name}()".format(
+ thing=thing, name=name, func_name=func_name
+ )
+ )
+
+
+class DefaultValue(object):
+ """A sentinel object to use for unusual default-value needs.
+
+ Construct with a string that will be used as the repr, for display in help
+ and Sphinx output.
+
+ """
+ def __init__(self, display_as):
+ self.display_as = display_as
+
+ def __repr__(self):
+ return self.display_as
+
+
+def substitute_variables(text, variables):
+ """Substitute ``${VAR}`` variables in `text` with their values.
+
+ Variables in the text can take a number of shell-inspired forms::
+
+ $VAR
+ ${VAR}
+ ${VAR?} strict: an error if VAR isn't defined.
+ ${VAR-missing} defaulted: "missing" if VAR isn't defined.
+ $$ just a dollar sign.
+
+ `variables` is a dictionary of variable values.
+
+ Returns the resulting text with values substituted.
+
+ """
+ dollar_pattern = r"""(?x) # Use extended regex syntax
+ \$ # A dollar sign,
+ (?: # then
+ (?P<dollar>\$) | # a dollar sign, or
+ (?P<word1>\w+) | # a plain word, or
+ { # a {-wrapped
+ (?P<word2>\w+) # word,
+ (?:
+ (?P<strict>\?) | # with a strict marker
+ -(?P<defval>[^}]*) # or a default value
+ )? # maybe.
+ }
+ )
+ """
+
+ def dollar_replace(match):
+ """Called for each $replacement."""
+ # Only one of the groups will have matched, just get its text.
+ word = next(g for g in match.group('dollar', 'word1', 'word2') if g)
+ if word == "$":
+ return "$"
+ elif word in variables:
+ return variables[word]
+ elif match.group('strict'):
+ msg = "Variable {} is undefined: {!r}".format(word, text)
+ raise CoverageException(msg)
+ else:
+ return match.group('defval')
+
+ text = re.sub(dollar_pattern, dollar_replace, text)
+ return text
+
+
+class BaseCoverageException(Exception):
+ """The base of all Coverage exceptions."""
+ pass
+
+
+class CoverageException(BaseCoverageException):
+ """An exception raised by a coverage.py function."""
+ pass
+
+
+class NoSource(CoverageException):
+ """We couldn't find the source for a module."""
+ pass
+
+
+class NoCode(NoSource):
+ """We couldn't find any code at all."""
+ pass
+
+
+class NotPython(CoverageException):
+ """A source file turned out not to be parsable Python."""
+ pass
+
+
+class ExceptionDuringRun(CoverageException):
+ """An exception happened while running customer code.
+
+ Construct it with three arguments, the values from `sys.exc_info`.
+
+ """
+ pass
+
+
+class StopEverything(BaseCoverageException):
+ """An exception that means everything should stop.
+
+ The CoverageTest class converts these to SkipTest, so that when running
+ tests, raising this exception will automatically skip the test.
+
+ """
+ pass
diff --git a/contrib/python/coverage/py2/coverage/multiproc.py b/contrib/python/coverage/py2/coverage/multiproc.py
new file mode 100644
index 0000000000..21ed2e2c95
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/multiproc.py
@@ -0,0 +1,117 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Monkey-patching to add multiprocessing support for coverage.py"""
+
+import multiprocessing
+import multiprocessing.process
+import os
+import os.path
+import sys
+import traceback
+
+from coverage import env
+from coverage.misc import contract
+
+# An attribute that will be set on the module to indicate that it has been
+# monkey-patched.
+PATCHED_MARKER = "_coverage$patched"
+
+COVERAGE_CONFIGURATION_ENV = "_COVERAGE_CONFIGURATION_ENV"
+
+
+if env.PYVERSION >= (3, 4):
+ OriginalProcess = multiprocessing.process.BaseProcess
+else:
+ OriginalProcess = multiprocessing.Process
+
+original_bootstrap = OriginalProcess._bootstrap
+
+class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-method
+ """A replacement for multiprocess.Process that starts coverage."""
+
+ def _bootstrap(self, *args, **kwargs):
+ """Wrapper around _bootstrap to start coverage."""
+ try:
+ from coverage import Coverage # avoid circular import
+ import json
+ kwconf = json.loads(os.environ[COVERAGE_CONFIGURATION_ENV])
+ cov = Coverage(**kwconf)
+ cov._warn_preimported_source = False
+ cov.start()
+ debug = cov._debug
+ if debug.should("multiproc"):
+ debug.write("Calling multiprocessing bootstrap")
+ except Exception:
+ print("Exception during multiprocessing bootstrap init:")
+ traceback.print_exc(file=sys.stdout)
+ sys.stdout.flush()
+ raise
+ try:
+ return original_bootstrap(self, *args, **kwargs)
+ finally:
+ if debug.should("multiproc"):
+ debug.write("Finished multiprocessing bootstrap")
+ cov.stop()
+ cov.save()
+ if debug.should("multiproc"):
+ debug.write("Saved multiprocessing data")
+
+class Stowaway(object):
+ """An object to pickle, so when it is unpickled, it can apply the monkey-patch."""
+ def __init__(self, rcfile):
+ self.rcfile = rcfile
+
+ def __getstate__(self):
+ return {'rcfile': self.rcfile}
+
+ def __setstate__(self, state):
+ patch_multiprocessing(state['rcfile'])
+
+
+@contract(rcfile=str)
+def patch_multiprocessing(rcfile, coverage_args):
+ """Monkey-patch the multiprocessing module.
+
+ This enables coverage measurement of processes started by multiprocessing.
+ This involves aggressive monkey-patching.
+
+ `rcfile` is the path to the rcfile being used.
+
+ """
+
+ if hasattr(multiprocessing, PATCHED_MARKER):
+ return
+
+ if env.PYVERSION >= (3, 4):
+ OriginalProcess._bootstrap = ProcessWithCoverage._bootstrap
+ else:
+ multiprocessing.Process = ProcessWithCoverage
+
+ # Set the value in ProcessWithCoverage that will be pickled into the child
+ # process.
+ os.environ["COVERAGE_RCFILE"] = os.path.abspath(rcfile)
+
+ os.environ[COVERAGE_CONFIGURATION_ENV] = coverage_args
+
+ # When spawning processes rather than forking them, we have no state in the
+ # new process. We sneak in there with a Stowaway: we stuff one of our own
+ # objects into the data that gets pickled and sent to the sub-process. When
+ # the Stowaway is unpickled, it's __setstate__ method is called, which
+ # re-applies the monkey-patch.
+ # Windows only spawns, so this is needed to keep Windows working.
+ try:
+ from multiprocessing import spawn
+ original_get_preparation_data = spawn.get_preparation_data
+ except (ImportError, AttributeError):
+ pass
+ else:
+ def get_preparation_data_with_stowaway(name):
+ """Get the original preparation data, and also insert our stowaway."""
+ d = original_get_preparation_data(name)
+ d['stowaway'] = Stowaway(rcfile)
+ return d
+
+ spawn.get_preparation_data = get_preparation_data_with_stowaway
+
+ setattr(multiprocessing, PATCHED_MARKER, True)
diff --git a/contrib/python/coverage/py2/coverage/numbits.py b/contrib/python/coverage/py2/coverage/numbits.py
new file mode 100644
index 0000000000..6ca96fbcf7
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/numbits.py
@@ -0,0 +1,163 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""
+Functions to manipulate packed binary representations of number sets.
+
+To save space, coverage stores sets of line numbers in SQLite using a packed
+binary representation called a numbits. A numbits is a set of positive
+integers.
+
+A numbits is stored as a blob in the database. The exact meaning of the bytes
+in the blobs should be considered an implementation detail that might change in
+the future. Use these functions to work with those binary blobs of data.
+
+"""
+import json
+
+from coverage import env
+from coverage.backward import byte_to_int, bytes_to_ints, binary_bytes, zip_longest
+from coverage.misc import contract, new_contract
+
+if env.PY3:
+ def _to_blob(b):
+ """Convert a bytestring into a type SQLite will accept for a blob."""
+ return b
+
+ new_contract('blob', lambda v: isinstance(v, bytes))
+else:
+ def _to_blob(b):
+ """Convert a bytestring into a type SQLite will accept for a blob."""
+ return buffer(b) # pylint: disable=undefined-variable
+
+ new_contract('blob', lambda v: isinstance(v, buffer)) # pylint: disable=undefined-variable
+
+
+@contract(nums='Iterable', returns='blob')
+def nums_to_numbits(nums):
+ """Convert `nums` into a numbits.
+
+ Arguments:
+ nums: a reusable iterable of integers, the line numbers to store.
+
+ Returns:
+ A binary blob.
+ """
+ try:
+ nbytes = max(nums) // 8 + 1
+ except ValueError:
+ # nums was empty.
+ return _to_blob(b'')
+ b = bytearray(nbytes)
+ for num in nums:
+ b[num//8] |= 1 << num % 8
+ return _to_blob(bytes(b))
+
+
+@contract(numbits='blob', returns='list[int]')
+def numbits_to_nums(numbits):
+ """Convert a numbits into a list of numbers.
+
+ Arguments:
+ numbits: a binary blob, the packed number set.
+
+ Returns:
+ A list of ints.
+
+ When registered as a SQLite function by :func:`register_sqlite_functions`,
+ this returns a string, a JSON-encoded list of ints.
+
+ """
+ nums = []
+ for byte_i, byte in enumerate(bytes_to_ints(numbits)):
+ for bit_i in range(8):
+ if (byte & (1 << bit_i)):
+ nums.append(byte_i * 8 + bit_i)
+ return nums
+
+
+@contract(numbits1='blob', numbits2='blob', returns='blob')
+def numbits_union(numbits1, numbits2):
+ """Compute the union of two numbits.
+
+ Returns:
+ A new numbits, the union of `numbits1` and `numbits2`.
+ """
+ byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
+ return _to_blob(binary_bytes(b1 | b2 for b1, b2 in byte_pairs))
+
+
+@contract(numbits1='blob', numbits2='blob', returns='blob')
+def numbits_intersection(numbits1, numbits2):
+ """Compute the intersection of two numbits.
+
+ Returns:
+ A new numbits, the intersection `numbits1` and `numbits2`.
+ """
+ byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
+ intersection_bytes = binary_bytes(b1 & b2 for b1, b2 in byte_pairs)
+ return _to_blob(intersection_bytes.rstrip(b'\0'))
+
+
+@contract(numbits1='blob', numbits2='blob', returns='bool')
+def numbits_any_intersection(numbits1, numbits2):
+ """Is there any number that appears in both numbits?
+
+ Determine whether two number sets have a non-empty intersection. This is
+ faster than computing the intersection.
+
+ Returns:
+ A bool, True if there is any number in both `numbits1` and `numbits2`.
+ """
+ byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
+ return any(b1 & b2 for b1, b2 in byte_pairs)
+
+
+@contract(num='int', numbits='blob', returns='bool')
+def num_in_numbits(num, numbits):
+ """Does the integer `num` appear in `numbits`?
+
+ Returns:
+ A bool, True if `num` is a member of `numbits`.
+ """
+ nbyte, nbit = divmod(num, 8)
+ if nbyte >= len(numbits):
+ return False
+ return bool(byte_to_int(numbits[nbyte]) & (1 << nbit))
+
+
+def register_sqlite_functions(connection):
+ """
+ Define numbits functions in a SQLite connection.
+
+ This defines these functions for use in SQLite statements:
+
+ * :func:`numbits_union`
+ * :func:`numbits_intersection`
+ * :func:`numbits_any_intersection`
+ * :func:`num_in_numbits`
+ * :func:`numbits_to_nums`
+
+ `connection` is a :class:`sqlite3.Connection <python:sqlite3.Connection>`
+ object. After creating the connection, pass it to this function to
+ register the numbits functions. Then you can use numbits functions in your
+ queries::
+
+ import sqlite3
+ from coverage.numbits import register_sqlite_functions
+
+ conn = sqlite3.connect('example.db')
+ register_sqlite_functions(conn)
+ c = conn.cursor()
+ # Kind of a nonsense query: find all the files and contexts that
+ # executed line 47 in any file:
+ c.execute(
+ "select file_id, context_id from line_bits where num_in_numbits(?, numbits)",
+ (47,)
+ )
+ """
+ connection.create_function("numbits_union", 2, numbits_union)
+ connection.create_function("numbits_intersection", 2, numbits_intersection)
+ connection.create_function("numbits_any_intersection", 2, numbits_any_intersection)
+ connection.create_function("num_in_numbits", 2, num_in_numbits)
+ connection.create_function("numbits_to_nums", 1, lambda b: json.dumps(numbits_to_nums(b)))
diff --git a/contrib/python/coverage/py2/coverage/parser.py b/contrib/python/coverage/py2/coverage/parser.py
new file mode 100644
index 0000000000..258f956039
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/parser.py
@@ -0,0 +1,1276 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Code parsing for coverage.py."""
+
+import ast
+import collections
+import os
+import re
+import token
+import tokenize
+
+from coverage import env
+from coverage.backward import range # pylint: disable=redefined-builtin
+from coverage.backward import bytes_to_ints, string_class
+from coverage.bytecode import code_objects
+from coverage.debug import short_stack
+from coverage.misc import contract, join_regex, new_contract, nice_pair, one_of
+from coverage.misc import NoSource, NotPython, StopEverything
+from coverage.phystokens import compile_unicode, generate_tokens, neuter_encoding_declaration
+
+
+class PythonParser(object):
+ """Parse code to find executable lines, excluded lines, etc.
+
+ This information is all based on static analysis: no code execution is
+ involved.
+
+ """
+ @contract(text='unicode|None')
+ def __init__(self, text=None, filename=None, exclude=None):
+ """
+ Source can be provided as `text`, the text itself, or `filename`, from
+ which the text will be read. Excluded lines are those that match
+ `exclude`, a regex.
+
+ """
+ assert text or filename, "PythonParser needs either text or filename"
+ self.filename = filename or "<code>"
+ self.text = text
+ if not self.text:
+ from coverage.python import get_python_source
+ try:
+ self.text = get_python_source(self.filename)
+ except IOError as err:
+ raise NoSource(
+ "No source for code: '%s': %s" % (self.filename, err)
+ )
+
+ self.exclude = exclude
+
+ # The text lines of the parsed code.
+ self.lines = self.text.split('\n')
+
+ # The normalized line numbers of the statements in the code. Exclusions
+ # are taken into account, and statements are adjusted to their first
+ # lines.
+ self.statements = set()
+
+ # The normalized line numbers of the excluded lines in the code,
+ # adjusted to their first lines.
+ self.excluded = set()
+
+ # The raw_* attributes are only used in this class, and in
+ # lab/parser.py to show how this class is working.
+
+ # The line numbers that start statements, as reported by the line
+ # number table in the bytecode.
+ self.raw_statements = set()
+
+ # The raw line numbers of excluded lines of code, as marked by pragmas.
+ self.raw_excluded = set()
+
+ # The line numbers of class definitions.
+ self.raw_classdefs = set()
+
+ # Function definitions (start, end, name)
+ self._raw_funcdefs = set()
+
+ # The line numbers of docstring lines.
+ self.raw_docstrings = set()
+
+ # Internal detail, used by lab/parser.py.
+ self.show_tokens = False
+
+ # A dict mapping line numbers to lexical statement starts for
+ # multi-line statements.
+ self._multiline = {}
+
+ # Lazily-created ByteParser, arc data, and missing arc descriptions.
+ self._byte_parser = None
+ self._all_arcs = None
+ self._missing_arc_fragments = None
+
+ @property
+ def byte_parser(self):
+ """Create a ByteParser on demand."""
+ if not self._byte_parser:
+ self._byte_parser = ByteParser(self.text, filename=self.filename)
+ return self._byte_parser
+
+ @property
+ def raw_funcdefs(self):
+ return self._raw_funcdefs
+
+ def lines_matching(self, *regexes):
+ """Find the lines matching one of a list of regexes.
+
+ Returns a set of line numbers, the lines that contain a match for one
+ of the regexes in `regexes`. The entire line needn't match, just a
+ part of it.
+
+ """
+ combined = join_regex(regexes)
+ if env.PY2:
+ combined = combined.decode("utf8")
+ regex_c = re.compile(combined)
+ matches = set()
+ for i, ltext in enumerate(self.lines, start=1):
+ if regex_c.search(ltext):
+ matches.add(i)
+ return matches
+
+ def _raw_parse(self):
+ """Parse the source to find the interesting facts about its lines.
+
+ A handful of attributes are updated.
+
+ """
+ # Find lines which match an exclusion pattern.
+ if self.exclude:
+ self.raw_excluded = self.lines_matching(self.exclude)
+
+ # Tokenize, to find excluded suites, to find docstrings, and to find
+ # multi-line statements.
+ indent = 0
+ exclude_indent = 0
+ excluding = False
+ excluding_decorators = False
+ prev_toktype = token.INDENT
+ first_line = None
+ empty = True
+ first_on_line = True
+
+ tokgen = generate_tokens(self.text)
+ for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen:
+ if self.show_tokens: # pragma: debugging
+ print("%10s %5s %-20r %r" % (
+ tokenize.tok_name.get(toktype, toktype),
+ nice_pair((slineno, elineno)), ttext, ltext
+ ))
+ if toktype == token.INDENT:
+ indent += 1
+ elif toktype == token.DEDENT:
+ indent -= 1
+ elif toktype == token.NAME:
+ if ttext == 'class':
+ # Class definitions look like branches in the bytecode, so
+ # we need to exclude them. The simplest way is to note the
+ # lines with the 'class' keyword.
+ self.raw_classdefs.add(slineno)
+ elif toktype == token.OP:
+ if ttext == ':':
+ should_exclude = (elineno in self.raw_excluded) or excluding_decorators
+ if not excluding and should_exclude:
+ # Start excluding a suite. We trigger off of the colon
+ # token so that the #pragma comment will be recognized on
+ # the same line as the colon.
+ self.raw_excluded.add(elineno)
+ exclude_indent = indent
+ excluding = True
+ excluding_decorators = False
+ elif ttext == '@' and first_on_line:
+ # A decorator.
+ if elineno in self.raw_excluded:
+ excluding_decorators = True
+ if excluding_decorators:
+ self.raw_excluded.add(elineno)
+ elif toktype == token.STRING and prev_toktype == token.INDENT:
+ # Strings that are first on an indented line are docstrings.
+ # (a trick from trace.py in the stdlib.) This works for
+ # 99.9999% of cases. For the rest (!) see:
+ # http://stackoverflow.com/questions/1769332/x/1769794#1769794
+ self.raw_docstrings.update(range(slineno, elineno+1))
+ elif toktype == token.NEWLINE:
+ if first_line is not None and elineno != first_line:
+ # We're at the end of a line, and we've ended on a
+ # different line than the first line of the statement,
+ # so record a multi-line range.
+ for l in range(first_line, elineno+1):
+ self._multiline[l] = first_line
+ first_line = None
+ first_on_line = True
+
+ if ttext.strip() and toktype != tokenize.COMMENT:
+ # A non-whitespace token.
+ empty = False
+ if first_line is None:
+ # The token is not whitespace, and is the first in a
+ # statement.
+ first_line = slineno
+ # Check whether to end an excluded suite.
+ if excluding and indent <= exclude_indent:
+ excluding = False
+ if excluding:
+ self.raw_excluded.add(elineno)
+ first_on_line = False
+
+ prev_toktype = toktype
+
+ # Find the starts of the executable statements.
+ if not empty:
+ self.raw_statements.update(self.byte_parser._find_statements())
+
+ # The first line of modules can lie and say 1 always, even if the first
+ # line of code is later. If so, map 1 to the actual first line of the
+ # module.
+ if env.PYBEHAVIOR.module_firstline_1 and self._multiline:
+ self._multiline[1] = min(self.raw_statements)
+
+ def first_line(self, line):
+ """Return the first line number of the statement including `line`."""
+ if line < 0:
+ line = -self._multiline.get(-line, -line)
+ else:
+ line = self._multiline.get(line, line)
+ return line
+
+ def first_lines(self, lines):
+ """Map the line numbers in `lines` to the correct first line of the
+ statement.
+
+ Returns a set of the first lines.
+
+ """
+ return {self.first_line(l) for l in lines}
+
+ def translate_lines(self, lines):
+ """Implement `FileReporter.translate_lines`."""
+ return self.first_lines(lines)
+
+ def translate_arcs(self, arcs):
+ """Implement `FileReporter.translate_arcs`."""
+ return [(self.first_line(a), self.first_line(b)) for (a, b) in arcs]
+
+ def parse_source(self):
+ """Parse source text to find executable lines, excluded lines, etc.
+
+ Sets the .excluded and .statements attributes, normalized to the first
+ line of multi-line statements.
+
+ """
+ try:
+ self._raw_parse()
+ except (tokenize.TokenError, IndentationError) as err:
+ if hasattr(err, "lineno"):
+ lineno = err.lineno # IndentationError
+ else:
+ lineno = err.args[1][0] # TokenError
+ raise NotPython(
+ u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
+ self.filename, err.args[0], lineno
+ )
+ )
+
+ self.excluded = self.first_lines(self.raw_excluded)
+
+ ignore = self.excluded | self.raw_docstrings
+ starts = self.raw_statements - ignore
+ self.statements = self.first_lines(starts) - ignore
+
+ def arcs(self):
+ """Get information about the arcs available in the code.
+
+ Returns a set of line number pairs. Line numbers have been normalized
+ to the first line of multi-line statements.
+
+ """
+ if self._all_arcs is None:
+ self._analyze_ast()
+ return self._all_arcs
+
+ def _analyze_ast(self):
+ """Run the AstArcAnalyzer and save its results.
+
+ `_all_arcs` is the set of arcs in the code.
+
+ """
+ aaa = AstArcAnalyzer(self.text, self.raw_statements, self._multiline)
+ aaa.analyze()
+
+ self._all_arcs = set()
+ for l1, l2 in aaa.arcs:
+ fl1 = self.first_line(l1)
+ fl2 = self.first_line(l2)
+ if fl1 != fl2:
+ self._all_arcs.add((fl1, fl2))
+
+ self._missing_arc_fragments = aaa.missing_arc_fragments
+ self._raw_funcdefs = aaa.funcdefs
+
+ def exit_counts(self):
+ """Get a count of exits from that each line.
+
+ Excluded lines are excluded.
+
+ """
+ exit_counts = collections.defaultdict(int)
+ for l1, l2 in self.arcs():
+ if l1 < 0:
+ # Don't ever report -1 as a line number
+ continue
+ if l1 in self.excluded:
+ # Don't report excluded lines as line numbers.
+ continue
+ if l2 in self.excluded:
+ # Arcs to excluded lines shouldn't count.
+ continue
+ exit_counts[l1] += 1
+
+ # Class definitions have one extra exit, so remove one for each:
+ for l in self.raw_classdefs:
+ # Ensure key is there: class definitions can include excluded lines.
+ if l in exit_counts:
+ exit_counts[l] -= 1
+
+ return exit_counts
+
+ def missing_arc_description(self, start, end, executed_arcs=None):
+ """Provide an English sentence describing a missing arc."""
+ if self._missing_arc_fragments is None:
+ self._analyze_ast()
+
+ actual_start = start
+
+ if (
+ executed_arcs and
+ end < 0 and end == -start and
+ (end, start) not in executed_arcs and
+ (end, start) in self._missing_arc_fragments
+ ):
+ # It's a one-line callable, and we never even started it,
+ # and we have a message about not starting it.
+ start, end = end, start
+
+ fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)])
+
+ msgs = []
+ for smsg, emsg in fragment_pairs:
+ if emsg is None:
+ if end < 0:
+ # Hmm, maybe we have a one-line callable, let's check.
+ if (-end, end) in self._missing_arc_fragments:
+ return self.missing_arc_description(-end, end)
+ emsg = "didn't jump to the function exit"
+ else:
+ emsg = "didn't jump to line {lineno}"
+ emsg = emsg.format(lineno=end)
+
+ msg = "line {start} {emsg}".format(start=actual_start, emsg=emsg)
+ if smsg is not None:
+ msg += ", because {smsg}".format(smsg=smsg.format(lineno=actual_start))
+
+ msgs.append(msg)
+
+ return " or ".join(msgs)
+
+
+class ByteParser(object):
+ """Parse bytecode to understand the structure of code."""
+
+ @contract(text='unicode')
+ def __init__(self, text, code=None, filename=None):
+ self.text = text
+ if code:
+ self.code = code
+ else:
+ try:
+ self.code = compile_unicode(text, filename, "exec")
+ except SyntaxError as synerr:
+ raise NotPython(
+ u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
+ filename, synerr.msg, synerr.lineno
+ )
+ )
+
+ # Alternative Python implementations don't always provide all the
+ # attributes on code objects that we need to do the analysis.
+ for attr in ['co_lnotab', 'co_firstlineno']:
+ if not hasattr(self.code, attr):
+ raise StopEverything( # pragma: only jython
+ "This implementation of Python doesn't support code analysis.\n"
+ "Run coverage.py under another Python for this command."
+ )
+
+ def child_parsers(self):
+ """Iterate over all the code objects nested within this one.
+
+ The iteration includes `self` as its first value.
+
+ """
+ return (ByteParser(self.text, code=c) for c in code_objects(self.code))
+
+ def _line_numbers(self):
+ """Yield the line numbers possible in this code object.
+
+ Uses co_lnotab described in Python/compile.c to find the
+ line numbers. Produces a sequence: l0, l1, ...
+ """
+ if hasattr(self.code, "co_lines"):
+ for _, _, line in self.code.co_lines():
+ if line is not None:
+ yield line
+ else:
+ # Adapted from dis.py in the standard library.
+ byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
+ line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
+
+ last_line_num = None
+ line_num = self.code.co_firstlineno
+ byte_num = 0
+ for byte_incr, line_incr in zip(byte_increments, line_increments):
+ if byte_incr:
+ if line_num != last_line_num:
+ yield line_num
+ last_line_num = line_num
+ byte_num += byte_incr
+ if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
+ line_incr -= 0x100
+ line_num += line_incr
+ if line_num != last_line_num:
+ yield line_num
+
+ def _find_statements(self):
+ """Find the statements in `self.code`.
+
+ Produce a sequence of line numbers that start statements. Recurses
+ into all code objects reachable from `self.code`.
+
+ """
+ for bp in self.child_parsers():
+ # Get all of the lineno information from this code.
+ for l in bp._line_numbers():
+ yield l
+
+
+#
+# AST analysis
+#
+
+class LoopBlock(object):
+ """A block on the block stack representing a `for` or `while` loop."""
+ @contract(start=int)
+ def __init__(self, start):
+ # The line number where the loop starts.
+ self.start = start
+ # A set of ArcStarts, the arcs from break statements exiting this loop.
+ self.break_exits = set()
+
+
+class FunctionBlock(object):
+ """A block on the block stack representing a function definition."""
+ @contract(start=int, name=str)
+ def __init__(self, start, name):
+ # The line number where the function starts.
+ self.start = start
+ # The name of the function.
+ self.name = name
+
+
+class TryBlock(object):
+ """A block on the block stack representing a `try` block."""
+ @contract(handler_start='int|None', final_start='int|None')
+ def __init__(self, handler_start, final_start):
+ # The line number of the first "except" handler, if any.
+ self.handler_start = handler_start
+ # The line number of the "finally:" clause, if any.
+ self.final_start = final_start
+
+ # The ArcStarts for breaks/continues/returns/raises inside the "try:"
+ # that need to route through the "finally:" clause.
+ self.break_from = set()
+ self.continue_from = set()
+ self.return_from = set()
+ self.raise_from = set()
+
+
+class ArcStart(collections.namedtuple("Arc", "lineno, cause")):
+ """The information needed to start an arc.
+
+ `lineno` is the line number the arc starts from.
+
+ `cause` is an English text fragment used as the `startmsg` for
+ AstArcAnalyzer.missing_arc_fragments. It will be used to describe why an
+ arc wasn't executed, so should fit well into a sentence of the form,
+ "Line 17 didn't run because {cause}." The fragment can include "{lineno}"
+ to have `lineno` interpolated into it.
+
+ """
+ def __new__(cls, lineno, cause=None):
+ return super(ArcStart, cls).__new__(cls, lineno, cause)
+
+
+# Define contract words that PyContract doesn't have.
+# ArcStarts is for a list or set of ArcStart's.
+new_contract('ArcStarts', lambda seq: all(isinstance(x, ArcStart) for x in seq))
+
+
+# Turn on AST dumps with an environment variable.
+# $set_env.py: COVERAGE_AST_DUMP - Dump the AST nodes when parsing code.
+AST_DUMP = bool(int(os.environ.get("COVERAGE_AST_DUMP", 0)))
+
+class NodeList(object):
+ """A synthetic fictitious node, containing a sequence of nodes.
+
+ This is used when collapsing optimized if-statements, to represent the
+ unconditional execution of one of the clauses.
+
+ """
+ def __init__(self, body):
+ self.body = body
+ self.lineno = body[0].lineno
+
+
+# TODO: some add_arcs methods here don't add arcs, they return them. Rename them.
+# TODO: the cause messages have too many commas.
+# TODO: Shouldn't the cause messages join with "and" instead of "or"?
+
+class AstArcAnalyzer(object):
+ """Analyze source text with an AST to find executable code paths."""
+
+ @contract(text='unicode', statements=set)
+ def __init__(self, text, statements, multiline):
+ self.root_node = ast.parse(neuter_encoding_declaration(text))
+ # TODO: I think this is happening in too many places.
+ self.statements = {multiline.get(l, l) for l in statements}
+ self.multiline = multiline
+
+ if AST_DUMP: # pragma: debugging
+ # Dump the AST so that failing tests have helpful output.
+ print("Statements: {}".format(self.statements))
+ print("Multiline map: {}".format(self.multiline))
+ ast_dump(self.root_node)
+
+ self.arcs = set()
+
+ # A map from arc pairs to a list of pairs of sentence fragments:
+ # { (start, end): [(startmsg, endmsg), ...], }
+ #
+ # For an arc from line 17, they should be usable like:
+ # "Line 17 {endmsg}, because {startmsg}"
+ self.missing_arc_fragments = collections.defaultdict(list)
+ self.block_stack = []
+ self.funcdefs = set()
+
+ # $set_env.py: COVERAGE_TRACK_ARCS - Trace every arc added while parsing code.
+ self.debug = bool(int(os.environ.get("COVERAGE_TRACK_ARCS", 0)))
+
+ def analyze(self):
+ """Examine the AST tree from `root_node` to determine possible arcs.
+
+ This sets the `arcs` attribute to be a set of (from, to) line number
+ pairs.
+
+ """
+ for node in ast.walk(self.root_node):
+ node_name = node.__class__.__name__
+ code_object_handler = getattr(self, "_code_object__" + node_name, None)
+ if code_object_handler is not None:
+ code_object_handler(node)
+
+ @contract(start=int, end=int)
+ def add_arc(self, start, end, smsg=None, emsg=None):
+ """Add an arc, including message fragments to use if it is missing."""
+ if self.debug: # pragma: debugging
+ print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg))
+ print(short_stack(limit=6))
+ self.arcs.add((start, end))
+
+ if smsg is not None or emsg is not None:
+ self.missing_arc_fragments[(start, end)].append((smsg, emsg))
+
+ def nearest_blocks(self):
+ """Yield the blocks in nearest-to-farthest order."""
+ return reversed(self.block_stack)
+
+ @contract(returns=int)
+ def line_for_node(self, node):
+ """What is the right line number to use for this node?
+
+ This dispatches to _line__Node functions where needed.
+
+ """
+ node_name = node.__class__.__name__
+ handler = getattr(self, "_line__" + node_name, None)
+ if handler is not None:
+ return handler(node)
+ else:
+ return node.lineno
+
+ def _line_decorated(self, node):
+ """Compute first line number for things that can be decorated (classes and functions)."""
+ lineno = node.lineno
+ if env.PYBEHAVIOR.trace_decorated_def:
+ if node.decorator_list:
+ lineno = node.decorator_list[0].lineno
+ return lineno
+
+ def _line__Assign(self, node):
+ return self.line_for_node(node.value)
+
+ _line__ClassDef = _line_decorated
+
+ def _line__Dict(self, node):
+ # Python 3.5 changed how dict literals are made.
+ if env.PYVERSION >= (3, 5) and node.keys:
+ if node.keys[0] is not None:
+ return node.keys[0].lineno
+ else:
+ # Unpacked dict literals `{**{'a':1}}` have None as the key,
+ # use the value in that case.
+ return node.values[0].lineno
+ else:
+ return node.lineno
+
+ _line__FunctionDef = _line_decorated
+ _line__AsyncFunctionDef = _line_decorated
+
+ def _line__List(self, node):
+ if node.elts:
+ return self.line_for_node(node.elts[0])
+ else:
+ return node.lineno
+
+ def _line__Module(self, node):
+ if env.PYBEHAVIOR.module_firstline_1:
+ return 1
+ elif node.body:
+ return self.line_for_node(node.body[0])
+ else:
+ # Empty modules have no line number, they always start at 1.
+ return 1
+
+ # The node types that just flow to the next node with no complications.
+ OK_TO_DEFAULT = {
+ "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global",
+ "Import", "ImportFrom", "Nonlocal", "Pass", "Print",
+ }
+
+ @contract(returns='ArcStarts')
+ def add_arcs(self, node):
+ """Add the arcs for `node`.
+
+ Return a set of ArcStarts, exits from this node to the next. Because a
+ node represents an entire sub-tree (including its children), the exits
+ from a node can be arbitrarily complex::
+
+ if something(1):
+ if other(2):
+ doit(3)
+ else:
+ doit(5)
+
+ There are two exits from line 1: they start at line 3 and line 5.
+
+ """
+ node_name = node.__class__.__name__
+ handler = getattr(self, "_handle__" + node_name, None)
+ if handler is not None:
+ return handler(node)
+ else:
+ # No handler: either it's something that's ok to default (a simple
+ # statement), or it's something we overlooked. Change this 0 to 1
+ # to see if it's overlooked.
+ if 0:
+ if node_name not in self.OK_TO_DEFAULT:
+ print("*** Unhandled: {}".format(node))
+
+ # Default for simple statements: one exit from this node.
+ return {ArcStart(self.line_for_node(node))}
+
+ @one_of("from_start, prev_starts")
+ @contract(returns='ArcStarts')
+ def add_body_arcs(self, body, from_start=None, prev_starts=None):
+ """Add arcs for the body of a compound statement.
+
+ `body` is the body node. `from_start` is a single `ArcStart` that can
+ be the previous line in flow before this body. `prev_starts` is a set
+ of ArcStarts that can be the previous line. Only one of them should be
+ given.
+
+ Returns a set of ArcStarts, the exits from this body.
+
+ """
+ if prev_starts is None:
+ prev_starts = {from_start}
+ for body_node in body:
+ lineno = self.line_for_node(body_node)
+ first_line = self.multiline.get(lineno, lineno)
+ if first_line not in self.statements:
+ body_node = self.find_non_missing_node(body_node)
+ if body_node is None:
+ continue
+ lineno = self.line_for_node(body_node)
+ for prev_start in prev_starts:
+ self.add_arc(prev_start.lineno, lineno, prev_start.cause)
+ prev_starts = self.add_arcs(body_node)
+ return prev_starts
+
+ def find_non_missing_node(self, node):
+ """Search `node` looking for a child that has not been optimized away.
+
+ This might return the node you started with, or it will work recursively
+ to find a child node in self.statements.
+
+ Returns a node, or None if none of the node remains.
+
+ """
+ # This repeats work just done in add_body_arcs, but this duplication
+ # means we can avoid a function call in the 99.9999% case of not
+ # optimizing away statements.
+ lineno = self.line_for_node(node)
+ first_line = self.multiline.get(lineno, lineno)
+ if first_line in self.statements:
+ return node
+
+ missing_fn = getattr(self, "_missing__" + node.__class__.__name__, None)
+ if missing_fn:
+ node = missing_fn(node)
+ else:
+ node = None
+ return node
+
+ # Missing nodes: _missing__*
+ #
+ # Entire statements can be optimized away by Python. They will appear in
+ # the AST, but not the bytecode. These functions are called (by
+ # find_non_missing_node) to find a node to use instead of the missing
+ # node. They can return None if the node should truly be gone.
+
+ def _missing__If(self, node):
+ # If the if-node is missing, then one of its children might still be
+ # here, but not both. So return the first of the two that isn't missing.
+ # Use a NodeList to hold the clauses as a single node.
+ non_missing = self.find_non_missing_node(NodeList(node.body))
+ if non_missing:
+ return non_missing
+ if node.orelse:
+ return self.find_non_missing_node(NodeList(node.orelse))
+ return None
+
+ def _missing__NodeList(self, node):
+ # A NodeList might be a mixture of missing and present nodes. Find the
+ # ones that are present.
+ non_missing_children = []
+ for child in node.body:
+ child = self.find_non_missing_node(child)
+ if child is not None:
+ non_missing_children.append(child)
+
+ # Return the simplest representation of the present children.
+ if not non_missing_children:
+ return None
+ if len(non_missing_children) == 1:
+ return non_missing_children[0]
+ return NodeList(non_missing_children)
+
+ def _missing__While(self, node):
+ body_nodes = self.find_non_missing_node(NodeList(node.body))
+ if not body_nodes:
+ return None
+ # Make a synthetic While-true node.
+ new_while = ast.While()
+ new_while.lineno = body_nodes.lineno
+ new_while.test = ast.Name()
+ new_while.test.lineno = body_nodes.lineno
+ new_while.test.id = "True"
+ new_while.body = body_nodes.body
+ new_while.orelse = None
+ return new_while
+
+ def is_constant_expr(self, node):
+ """Is this a compile-time constant?"""
+ node_name = node.__class__.__name__
+ if node_name in ["Constant", "NameConstant", "Num"]:
+ return "Num"
+ elif node_name == "Name":
+ if node.id in ["True", "False", "None", "__debug__"]:
+ return "Name"
+ return None
+
+ # In the fullness of time, these might be good tests to write:
+ # while EXPR:
+ # while False:
+ # listcomps hidden deep in other expressions
+ # listcomps hidden in lists: x = [[i for i in range(10)]]
+ # nested function definitions
+
+
+ # Exit processing: process_*_exits
+ #
+ # These functions process the four kinds of jump exits: break, continue,
+ # raise, and return. To figure out where an exit goes, we have to look at
+ # the block stack context. For example, a break will jump to the nearest
+ # enclosing loop block, or the nearest enclosing finally block, whichever
+ # is nearer.
+
+ @contract(exits='ArcStarts')
+ def process_break_exits(self, exits):
+ """Add arcs due to jumps from `exits` being breaks."""
+ for block in self.nearest_blocks():
+ if isinstance(block, LoopBlock):
+ block.break_exits.update(exits)
+ break
+ elif isinstance(block, TryBlock) and block.final_start is not None:
+ block.break_from.update(exits)
+ break
+
+ @contract(exits='ArcStarts')
+ def process_continue_exits(self, exits):
+ """Add arcs due to jumps from `exits` being continues."""
+ for block in self.nearest_blocks():
+ if isinstance(block, LoopBlock):
+ for xit in exits:
+ self.add_arc(xit.lineno, block.start, xit.cause)
+ break
+ elif isinstance(block, TryBlock) and block.final_start is not None:
+ block.continue_from.update(exits)
+ break
+
+ @contract(exits='ArcStarts')
+ def process_raise_exits(self, exits):
+ """Add arcs due to jumps from `exits` being raises."""
+ for block in self.nearest_blocks():
+ if isinstance(block, TryBlock):
+ if block.handler_start is not None:
+ for xit in exits:
+ self.add_arc(xit.lineno, block.handler_start, xit.cause)
+ break
+ elif block.final_start is not None:
+ block.raise_from.update(exits)
+ break
+ elif isinstance(block, FunctionBlock):
+ for xit in exits:
+ self.add_arc(
+ xit.lineno, -block.start, xit.cause,
+ "didn't except from function {!r}".format(block.name),
+ )
+ break
+
+ @contract(exits='ArcStarts')
+ def process_return_exits(self, exits):
+ """Add arcs due to jumps from `exits` being returns."""
+ for block in self.nearest_blocks():
+ if isinstance(block, TryBlock) and block.final_start is not None:
+ block.return_from.update(exits)
+ break
+ elif isinstance(block, FunctionBlock):
+ for xit in exits:
+ self.add_arc(
+ xit.lineno, -block.start, xit.cause,
+ "didn't return from function {!r}".format(block.name),
+ )
+ break
+
+
+ # Handlers: _handle__*
+ #
+ # Each handler deals with a specific AST node type, dispatched from
+ # add_arcs. Handlers return the set of exits from that node, and can
+ # also call self.add_arc to record arcs they find. These functions mirror
+ # the Python semantics of each syntactic construct. See the docstring
+ # for add_arcs to understand the concept of exits from a node.
+
+ @contract(returns='ArcStarts')
+ def _handle__Break(self, node):
+ here = self.line_for_node(node)
+ break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed")
+ self.process_break_exits([break_start])
+ return set()
+
+ @contract(returns='ArcStarts')
+ def _handle_decorated(self, node):
+ """Add arcs for things that can be decorated (classes and functions)."""
+ main_line = last = node.lineno
+ if node.decorator_list:
+ if env.PYBEHAVIOR.trace_decorated_def:
+ last = None
+ for dec_node in node.decorator_list:
+ dec_start = self.line_for_node(dec_node)
+ if last is not None and dec_start != last:
+ self.add_arc(last, dec_start)
+ last = dec_start
+ if env.PYBEHAVIOR.trace_decorated_def:
+ self.add_arc(last, main_line)
+ last = main_line
+ # The definition line may have been missed, but we should have it
+ # in `self.statements`. For some constructs, `line_for_node` is
+ # not what we'd think of as the first line in the statement, so map
+ # it to the first one.
+ if node.body:
+ body_start = self.line_for_node(node.body[0])
+ body_start = self.multiline.get(body_start, body_start)
+ for lineno in range(last+1, body_start):
+ if lineno in self.statements:
+ self.add_arc(last, lineno)
+ last = lineno
+ # The body is handled in collect_arcs.
+ return {ArcStart(last)}
+
+ _handle__ClassDef = _handle_decorated
+
+ @contract(returns='ArcStarts')
+ def _handle__Continue(self, node):
+ here = self.line_for_node(node)
+ continue_start = ArcStart(here, cause="the continue on line {lineno} wasn't executed")
+ self.process_continue_exits([continue_start])
+ return set()
+
+ @contract(returns='ArcStarts')
+ def _handle__For(self, node):
+ start = self.line_for_node(node.iter)
+ self.block_stack.append(LoopBlock(start=start))
+ from_start = ArcStart(start, cause="the loop on line {lineno} never started")
+ exits = self.add_body_arcs(node.body, from_start=from_start)
+ # Any exit from the body will go back to the top of the loop.
+ for xit in exits:
+ self.add_arc(xit.lineno, start, xit.cause)
+ my_block = self.block_stack.pop()
+ exits = my_block.break_exits
+ from_start = ArcStart(start, cause="the loop on line {lineno} didn't complete")
+ if node.orelse:
+ else_exits = self.add_body_arcs(node.orelse, from_start=from_start)
+ exits |= else_exits
+ else:
+ # No else clause: exit from the for line.
+ exits.add(from_start)
+ return exits
+
+ _handle__AsyncFor = _handle__For
+
+ _handle__FunctionDef = _handle_decorated
+ _handle__AsyncFunctionDef = _handle_decorated
+
+ @contract(returns='ArcStarts')
+ def _handle__If(self, node):
+ start = self.line_for_node(node.test)
+ from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
+ exits = self.add_body_arcs(node.body, from_start=from_start)
+ from_start = ArcStart(start, cause="the condition on line {lineno} was never false")
+ exits |= self.add_body_arcs(node.orelse, from_start=from_start)
+ return exits
+
+ @contract(returns='ArcStarts')
+ def _handle__NodeList(self, node):
+ start = self.line_for_node(node)
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
+ return exits
+
+ @contract(returns='ArcStarts')
+ def _handle__Raise(self, node):
+ here = self.line_for_node(node)
+ raise_start = ArcStart(here, cause="the raise on line {lineno} wasn't executed")
+ self.process_raise_exits([raise_start])
+ # `raise` statement jumps away, no exits from here.
+ return set()
+
+ @contract(returns='ArcStarts')
+ def _handle__Return(self, node):
+ here = self.line_for_node(node)
+ return_start = ArcStart(here, cause="the return on line {lineno} wasn't executed")
+ self.process_return_exits([return_start])
+ # `return` statement jumps away, no exits from here.
+ return set()
+
+ @contract(returns='ArcStarts')
+ def _handle__Try(self, node):
+ if node.handlers:
+ handler_start = self.line_for_node(node.handlers[0])
+ else:
+ handler_start = None
+
+ if node.finalbody:
+ final_start = self.line_for_node(node.finalbody[0])
+ else:
+ final_start = None
+
+ try_block = TryBlock(handler_start, final_start)
+ self.block_stack.append(try_block)
+
+ start = self.line_for_node(node)
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
+
+ # We're done with the `try` body, so this block no longer handles
+ # exceptions. We keep the block so the `finally` clause can pick up
+ # flows from the handlers and `else` clause.
+ if node.finalbody:
+ try_block.handler_start = None
+ if node.handlers:
+ # If there are `except` clauses, then raises in the try body
+ # will already jump to them. Start this set over for raises in
+ # `except` and `else`.
+ try_block.raise_from = set()
+ else:
+ self.block_stack.pop()
+
+ handler_exits = set()
+
+ if node.handlers:
+ last_handler_start = None
+ for handler_node in node.handlers:
+ handler_start = self.line_for_node(handler_node)
+ if last_handler_start is not None:
+ self.add_arc(last_handler_start, handler_start)
+ last_handler_start = handler_start
+ from_cause = "the exception caught by line {lineno} didn't happen"
+ from_start = ArcStart(handler_start, cause=from_cause)
+ handler_exits |= self.add_body_arcs(handler_node.body, from_start=from_start)
+
+ if node.orelse:
+ exits = self.add_body_arcs(node.orelse, prev_starts=exits)
+
+ exits |= handler_exits
+
+ if node.finalbody:
+ self.block_stack.pop()
+ final_from = ( # You can get to the `finally` clause from:
+ exits | # the exits of the body or `else` clause,
+ try_block.break_from | # or a `break`,
+ try_block.continue_from | # or a `continue`,
+ try_block.raise_from | # or a `raise`,
+ try_block.return_from # or a `return`.
+ )
+
+ final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from)
+
+ if try_block.break_from:
+ if env.PYBEHAVIOR.finally_jumps_back:
+ for break_line in try_block.break_from:
+ lineno = break_line.lineno
+ cause = break_line.cause.format(lineno=lineno)
+ for final_exit in final_exits:
+ self.add_arc(final_exit.lineno, lineno, cause)
+ breaks = try_block.break_from
+ else:
+ breaks = self._combine_finally_starts(try_block.break_from, final_exits)
+ self.process_break_exits(breaks)
+
+ if try_block.continue_from:
+ if env.PYBEHAVIOR.finally_jumps_back:
+ for continue_line in try_block.continue_from:
+ lineno = continue_line.lineno
+ cause = continue_line.cause.format(lineno=lineno)
+ for final_exit in final_exits:
+ self.add_arc(final_exit.lineno, lineno, cause)
+ continues = try_block.continue_from
+ else:
+ continues = self._combine_finally_starts(try_block.continue_from, final_exits)
+ self.process_continue_exits(continues)
+
+ if try_block.raise_from:
+ self.process_raise_exits(
+ self._combine_finally_starts(try_block.raise_from, final_exits)
+ )
+
+ if try_block.return_from:
+ if env.PYBEHAVIOR.finally_jumps_back:
+ for return_line in try_block.return_from:
+ lineno = return_line.lineno
+ cause = return_line.cause.format(lineno=lineno)
+ for final_exit in final_exits:
+ self.add_arc(final_exit.lineno, lineno, cause)
+ returns = try_block.return_from
+ else:
+ returns = self._combine_finally_starts(try_block.return_from, final_exits)
+ self.process_return_exits(returns)
+
+ if exits:
+ # The finally clause's exits are only exits for the try block
+ # as a whole if the try block had some exits to begin with.
+ exits = final_exits
+
+ return exits
+
+ @contract(starts='ArcStarts', exits='ArcStarts', returns='ArcStarts')
+ def _combine_finally_starts(self, starts, exits):
+ """Helper for building the cause of `finally` branches.
+
+ "finally" clauses might not execute their exits, and the causes could
+ be due to a failure to execute any of the exits in the try block. So
+ we use the causes from `starts` as the causes for `exits`.
+ """
+ causes = []
+ for start in sorted(starts):
+ if start.cause is not None:
+ causes.append(start.cause.format(lineno=start.lineno))
+ cause = " or ".join(causes)
+ exits = {ArcStart(xit.lineno, cause) for xit in exits}
+ return exits
+
+ @contract(returns='ArcStarts')
+ def _handle__TryExcept(self, node):
+ # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
+ # TryExcept, it means there was no finally, so fake it, and treat as
+ # a general Try node.
+ node.finalbody = []
+ return self._handle__Try(node)
+
+ @contract(returns='ArcStarts')
+ def _handle__TryFinally(self, node):
+ # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
+ # TryFinally, see if there's a TryExcept nested inside. If so, merge
+ # them. Otherwise, fake fields to complete a Try node.
+ node.handlers = []
+ node.orelse = []
+
+ first = node.body[0]
+ if first.__class__.__name__ == "TryExcept" and node.lineno == first.lineno:
+ assert len(node.body) == 1
+ node.body = first.body
+ node.handlers = first.handlers
+ node.orelse = first.orelse
+
+ return self._handle__Try(node)
+
+ @contract(returns='ArcStarts')
+ def _handle__While(self, node):
+ start = to_top = self.line_for_node(node.test)
+ constant_test = self.is_constant_expr(node.test)
+ top_is_body0 = False
+ if constant_test and (env.PY3 or constant_test == "Num"):
+ top_is_body0 = True
+ if env.PYBEHAVIOR.keep_constant_test:
+ top_is_body0 = False
+ if top_is_body0:
+ to_top = self.line_for_node(node.body[0])
+ self.block_stack.append(LoopBlock(start=to_top))
+ from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
+ exits = self.add_body_arcs(node.body, from_start=from_start)
+ for xit in exits:
+ self.add_arc(xit.lineno, to_top, xit.cause)
+ exits = set()
+ my_block = self.block_stack.pop()
+ exits.update(my_block.break_exits)
+ from_start = ArcStart(start, cause="the condition on line {lineno} was never false")
+ if node.orelse:
+ else_exits = self.add_body_arcs(node.orelse, from_start=from_start)
+ exits |= else_exits
+ else:
+ # No `else` clause: you can exit from the start.
+ if not constant_test:
+ exits.add(from_start)
+ return exits
+
+ @contract(returns='ArcStarts')
+ def _handle__With(self, node):
+ start = self.line_for_node(node)
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
+ return exits
+
+ _handle__AsyncWith = _handle__With
+
+ def _code_object__Module(self, node):
+ start = self.line_for_node(node)
+ if node.body:
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(-start))
+ for xit in exits:
+ self.add_arc(xit.lineno, -start, xit.cause, "didn't exit the module")
+ else:
+ # Empty module.
+ self.add_arc(-start, start)
+ self.add_arc(start, -start)
+
+ def _process_function_def(self, start, node):
+ self.funcdefs.add((start, node.body[-1].lineno, node.name))
+
+ def _code_object__FunctionDef(self, node):
+ start = self.line_for_node(node)
+ self.block_stack.append(FunctionBlock(start=start, name=node.name))
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(-start))
+ self.process_return_exits(exits)
+ self._process_function_def(start, node)
+ self.block_stack.pop()
+
+ _code_object__AsyncFunctionDef = _code_object__FunctionDef
+
+ def _code_object__ClassDef(self, node):
+ start = self.line_for_node(node)
+ self.add_arc(-start, start)
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
+ for xit in exits:
+ self.add_arc(
+ xit.lineno, -start, xit.cause,
+ "didn't exit the body of class {!r}".format(node.name),
+ )
+
+ def _make_oneline_code_method(noun): # pylint: disable=no-self-argument
+ """A function to make methods for online callable _code_object__ methods."""
+ def _code_object__oneline_callable(self, node):
+ start = self.line_for_node(node)
+ self.add_arc(-start, start, None, "didn't run the {} on line {}".format(noun, start))
+ self.add_arc(
+ start, -start, None,
+ "didn't finish the {} on line {}".format(noun, start),
+ )
+ return _code_object__oneline_callable
+
+ _code_object__Lambda = _make_oneline_code_method("lambda")
+ _code_object__GeneratorExp = _make_oneline_code_method("generator expression")
+ _code_object__DictComp = _make_oneline_code_method("dictionary comprehension")
+ _code_object__SetComp = _make_oneline_code_method("set comprehension")
+ if env.PY3:
+ _code_object__ListComp = _make_oneline_code_method("list comprehension")
+
+
+if AST_DUMP: # pragma: debugging
+ # Code only used when dumping the AST for debugging.
+
+ SKIP_DUMP_FIELDS = ["ctx"]
+
+ def _is_simple_value(value):
+ """Is `value` simple enough to be displayed on a single line?"""
+ return (
+ value in [None, [], (), {}, set()] or
+ isinstance(value, (string_class, int, float))
+ )
+
+ def ast_dump(node, depth=0):
+ """Dump the AST for `node`.
+
+ This recursively walks the AST, printing a readable version.
+
+ """
+ indent = " " * depth
+ if not isinstance(node, ast.AST):
+ print("{}<{} {!r}>".format(indent, node.__class__.__name__, node))
+ return
+
+ lineno = getattr(node, "lineno", None)
+ if lineno is not None:
+ linemark = " @ {}".format(node.lineno)
+ else:
+ linemark = ""
+ head = "{}<{}{}".format(indent, node.__class__.__name__, linemark)
+
+ named_fields = [
+ (name, value)
+ for name, value in ast.iter_fields(node)
+ if name not in SKIP_DUMP_FIELDS
+ ]
+ if not named_fields:
+ print("{}>".format(head))
+ elif len(named_fields) == 1 and _is_simple_value(named_fields[0][1]):
+ field_name, value = named_fields[0]
+ print("{} {}: {!r}>".format(head, field_name, value))
+ else:
+ print(head)
+ if 0:
+ print("{}# mro: {}".format(
+ indent, ", ".join(c.__name__ for c in node.__class__.__mro__[1:]),
+ ))
+ next_indent = indent + " "
+ for field_name, value in named_fields:
+ prefix = "{}{}:".format(next_indent, field_name)
+ if _is_simple_value(value):
+ print("{} {!r}".format(prefix, value))
+ elif isinstance(value, list):
+ print("{} [".format(prefix))
+ for n in value:
+ ast_dump(n, depth + 8)
+ print("{}]".format(next_indent))
+ else:
+ print(prefix)
+ ast_dump(value, depth + 8)
+
+ print("{}>".format(indent))
diff --git a/contrib/python/coverage/py2/coverage/phystokens.py b/contrib/python/coverage/py2/coverage/phystokens.py
new file mode 100644
index 0000000000..54378b3bc8
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/phystokens.py
@@ -0,0 +1,297 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Better tokenizing for coverage.py."""
+
+import codecs
+import keyword
+import re
+import sys
+import token
+import tokenize
+
+from coverage import env
+from coverage.backward import iternext, unicode_class
+from coverage.misc import contract
+
+
+def phys_tokens(toks):
+ """Return all physical tokens, even line continuations.
+
+ tokenize.generate_tokens() doesn't return a token for the backslash that
+ continues lines. This wrapper provides those tokens so that we can
+ re-create a faithful representation of the original source.
+
+ Returns the same values as generate_tokens()
+
+ """
+ last_line = None
+ last_lineno = -1
+ last_ttext = None
+ for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks:
+ if last_lineno != elineno:
+ if last_line and last_line.endswith("\\\n"):
+ # We are at the beginning of a new line, and the last line
+ # ended with a backslash. We probably have to inject a
+ # backslash token into the stream. Unfortunately, there's more
+ # to figure out. This code::
+ #
+ # usage = """\
+ # HEY THERE
+ # """
+ #
+ # triggers this condition, but the token text is::
+ #
+ # '"""\\\nHEY THERE\n"""'
+ #
+ # so we need to figure out if the backslash is already in the
+ # string token or not.
+ inject_backslash = True
+ if last_ttext.endswith("\\"):
+ inject_backslash = False
+ elif ttype == token.STRING:
+ if "\n" in ttext and ttext.split('\n', 1)[0][-1] == '\\':
+ # It's a multi-line string and the first line ends with
+ # a backslash, so we don't need to inject another.
+ inject_backslash = False
+ if inject_backslash:
+ # Figure out what column the backslash is in.
+ ccol = len(last_line.split("\n")[-2]) - 1
+ # Yield the token, with a fake token type.
+ yield (
+ 99999, "\\\n",
+ (slineno, ccol), (slineno, ccol+2),
+ last_line
+ )
+ last_line = ltext
+ if ttype not in (tokenize.NEWLINE, tokenize.NL):
+ last_ttext = ttext
+ yield ttype, ttext, (slineno, scol), (elineno, ecol), ltext
+ last_lineno = elineno
+
+
+@contract(source='unicode')
+def source_token_lines(source):
+ """Generate a series of lines, one for each line in `source`.
+
+ Each line is a list of pairs, each pair is a token::
+
+ [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
+
+ Each pair has a token class, and the token text.
+
+ If you concatenate all the token texts, and then join them with newlines,
+ you should have your original `source` back, with two differences:
+ trailing whitespace is not preserved, and a final line with no newline
+ is indistinguishable from a final line with a newline.
+
+ """
+
+ ws_tokens = {token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL}
+ line = []
+ col = 0
+
+ source = source.expandtabs(8).replace('\r\n', '\n')
+ tokgen = generate_tokens(source)
+
+ for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen):
+ mark_start = True
+ for part in re.split('(\n)', ttext):
+ if part == '\n':
+ yield line
+ line = []
+ col = 0
+ mark_end = False
+ elif part == '':
+ mark_end = False
+ elif ttype in ws_tokens:
+ mark_end = False
+ else:
+ if mark_start and scol > col:
+ line.append(("ws", u" " * (scol - col)))
+ mark_start = False
+ tok_class = tokenize.tok_name.get(ttype, 'xx').lower()[:3]
+ if ttype == token.NAME and keyword.iskeyword(ttext):
+ tok_class = "key"
+ line.append((tok_class, part))
+ mark_end = True
+ scol = 0
+ if mark_end:
+ col = ecol
+
+ if line:
+ yield line
+
+
+class CachedTokenizer(object):
+ """A one-element cache around tokenize.generate_tokens.
+
+ When reporting, coverage.py tokenizes files twice, once to find the
+ structure of the file, and once to syntax-color it. Tokenizing is
+ expensive, and easily cached.
+
+ This is a one-element cache so that our twice-in-a-row tokenizing doesn't
+ actually tokenize twice.
+
+ """
+ def __init__(self):
+ self.last_text = None
+ self.last_tokens = None
+
+ @contract(text='unicode')
+ def generate_tokens(self, text):
+ """A stand-in for `tokenize.generate_tokens`."""
+ if text != self.last_text:
+ self.last_text = text
+ readline = iternext(text.splitlines(True))
+ self.last_tokens = list(tokenize.generate_tokens(readline))
+ return self.last_tokens
+
+# Create our generate_tokens cache as a callable replacement function.
+generate_tokens = CachedTokenizer().generate_tokens
+
+
+COOKIE_RE = re.compile(r"^[ \t]*#.*coding[:=][ \t]*([-\w.]+)", flags=re.MULTILINE)
+
+@contract(source='bytes')
+def _source_encoding_py2(source):
+ """Determine the encoding for `source`, according to PEP 263.
+
+ `source` is a byte string, the text of the program.
+
+ Returns a string, the name of the encoding.
+
+ """
+ assert isinstance(source, bytes)
+
+ # Do this so the detect_encode code we copied will work.
+ readline = iternext(source.splitlines(True))
+
+ # This is mostly code adapted from Py3.2's tokenize module.
+
+ def _get_normal_name(orig_enc):
+ """Imitates get_normal_name in tokenizer.c."""
+ # Only care about the first 12 characters.
+ enc = orig_enc[:12].lower().replace("_", "-")
+ if re.match(r"^utf-8($|-)", enc):
+ return "utf-8"
+ if re.match(r"^(latin-1|iso-8859-1|iso-latin-1)($|-)", enc):
+ return "iso-8859-1"
+ return orig_enc
+
+ # From detect_encode():
+ # It detects the encoding from the presence of a UTF-8 BOM or an encoding
+ # cookie as specified in PEP-0263. If both a BOM and a cookie are present,
+ # but disagree, a SyntaxError will be raised. If the encoding cookie is an
+ # invalid charset, raise a SyntaxError. Note that if a UTF-8 BOM is found,
+ # 'utf-8-sig' is returned.
+
+ # If no encoding is specified, then the default will be returned.
+ default = 'ascii'
+
+ bom_found = False
+ encoding = None
+
+ def read_or_stop():
+ """Get the next source line, or ''."""
+ try:
+ return readline()
+ except StopIteration:
+ return ''
+
+ def find_cookie(line):
+ """Find an encoding cookie in `line`."""
+ try:
+ line_string = line.decode('ascii')
+ except UnicodeDecodeError:
+ return None
+
+ matches = COOKIE_RE.findall(line_string)
+ if not matches:
+ return None
+ encoding = _get_normal_name(matches[0])
+ try:
+ codec = codecs.lookup(encoding)
+ except LookupError:
+ # This behavior mimics the Python interpreter
+ raise SyntaxError("unknown encoding: " + encoding)
+
+ if bom_found:
+ # codecs in 2.3 were raw tuples of functions, assume the best.
+ codec_name = getattr(codec, 'name', encoding)
+ if codec_name != 'utf-8':
+ # This behavior mimics the Python interpreter
+ raise SyntaxError('encoding problem: utf-8')
+ encoding += '-sig'
+ return encoding
+
+ first = read_or_stop()
+ if first.startswith(codecs.BOM_UTF8):
+ bom_found = True
+ first = first[3:]
+ default = 'utf-8-sig'
+ if not first:
+ return default
+
+ encoding = find_cookie(first)
+ if encoding:
+ return encoding
+
+ second = read_or_stop()
+ if not second:
+ return default
+
+ encoding = find_cookie(second)
+ if encoding:
+ return encoding
+
+ return default
+
+
+@contract(source='bytes')
+def _source_encoding_py3(source):
+ """Determine the encoding for `source`, according to PEP 263.
+
+ `source` is a byte string: the text of the program.
+
+ Returns a string, the name of the encoding.
+
+ """
+ readline = iternext(source.splitlines(True))
+ return tokenize.detect_encoding(readline)[0]
+
+
+if env.PY3:
+ source_encoding = _source_encoding_py3
+else:
+ source_encoding = _source_encoding_py2
+
+
+@contract(source='unicode')
+def compile_unicode(source, filename, mode):
+ """Just like the `compile` builtin, but works on any Unicode string.
+
+ Python 2's compile() builtin has a stupid restriction: if the source string
+ is Unicode, then it may not have a encoding declaration in it. Why not?
+ Who knows! It also decodes to utf8, and then tries to interpret those utf8
+ bytes according to the encoding declaration. Why? Who knows!
+
+ This function neuters the coding declaration, and compiles it.
+
+ """
+ source = neuter_encoding_declaration(source)
+ if env.PY2 and isinstance(filename, unicode_class):
+ filename = filename.encode(sys.getfilesystemencoding(), "replace")
+ code = compile(source, filename, mode)
+ return code
+
+
+@contract(source='unicode', returns='unicode')
+def neuter_encoding_declaration(source):
+ """Return `source`, with any encoding declaration neutered."""
+ if COOKIE_RE.search(source):
+ source_lines = source.splitlines(True)
+ for lineno in range(min(2, len(source_lines))):
+ source_lines[lineno] = COOKIE_RE.sub("# (deleted declaration)", source_lines[lineno])
+ source = "".join(source_lines)
+ return source
diff --git a/contrib/python/coverage/py2/coverage/plugin.py b/contrib/python/coverage/py2/coverage/plugin.py
new file mode 100644
index 0000000000..6997b489bb
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/plugin.py
@@ -0,0 +1,533 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""
+.. versionadded:: 4.0
+
+Plug-in interfaces for coverage.py.
+
+Coverage.py supports a few different kinds of plug-ins that change its
+behavior:
+
+* File tracers implement tracing of non-Python file types.
+
+* Configurers add custom configuration, using Python code to change the
+ configuration.
+
+* Dynamic context switchers decide when the dynamic context has changed, for
+ example, to record what test function produced the coverage.
+
+To write a coverage.py plug-in, create a module with a subclass of
+:class:`~coverage.CoveragePlugin`. You will override methods in your class to
+participate in various aspects of coverage.py's processing.
+Different types of plug-ins have to override different methods.
+
+Any plug-in can optionally implement :meth:`~coverage.CoveragePlugin.sys_info`
+to provide debugging information about their operation.
+
+Your module must also contain a ``coverage_init`` function that registers an
+instance of your plug-in class::
+
+ import coverage
+
+ class MyPlugin(coverage.CoveragePlugin):
+ ...
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(MyPlugin())
+
+You use the `reg` parameter passed to your ``coverage_init`` function to
+register your plug-in object. The registration method you call depends on
+what kind of plug-in it is.
+
+If your plug-in takes options, the `options` parameter is a dictionary of your
+plug-in's options from the coverage.py configuration file. Use them however
+you want to configure your object before registering it.
+
+Coverage.py will store its own information on your plug-in object, using
+attributes whose names start with ``_coverage_``. Don't be startled.
+
+.. warning::
+ Plug-ins are imported by coverage.py before it begins measuring code.
+ If you write a plugin in your own project, it might import your product
+ code before coverage.py can start measuring. This can result in your
+ own code being reported as missing.
+
+ One solution is to put your plugins in your project tree, but not in
+ your importable Python package.
+
+
+.. _file_tracer_plugins:
+
+File Tracers
+============
+
+File tracers implement measurement support for non-Python files. File tracers
+implement the :meth:`~coverage.CoveragePlugin.file_tracer` method to claim
+files and the :meth:`~coverage.CoveragePlugin.file_reporter` method to report
+on those files.
+
+In your ``coverage_init`` function, use the ``add_file_tracer`` method to
+register your file tracer.
+
+
+.. _configurer_plugins:
+
+Configurers
+===========
+
+.. versionadded:: 4.5
+
+Configurers modify the configuration of coverage.py during start-up.
+Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to
+change the configuration.
+
+In your ``coverage_init`` function, use the ``add_configurer`` method to
+register your configurer.
+
+
+.. _dynamic_context_plugins:
+
+Dynamic Context Switchers
+=========================
+
+.. versionadded:: 5.0
+
+Dynamic context switcher plugins implement the
+:meth:`~coverage.CoveragePlugin.dynamic_context` method to dynamically compute
+the context label for each measured frame.
+
+Computed context labels are useful when you want to group measured data without
+modifying the source code.
+
+For example, you could write a plugin that checks `frame.f_code` to inspect
+the currently executed method, and set the context label to a fully qualified
+method name if it's an instance method of `unittest.TestCase` and the method
+name starts with 'test'. Such a plugin would provide basic coverage grouping
+by test and could be used with test runners that have no built-in coveragepy
+support.
+
+In your ``coverage_init`` function, use the ``add_dynamic_context`` method to
+register your dynamic context switcher.
+
+"""
+
+from coverage import files
+from coverage.misc import contract, _needs_to_implement
+
+
+class CoveragePlugin(object):
+ """Base class for coverage.py plug-ins."""
+
+ def file_tracer(self, filename): # pylint: disable=unused-argument
+ """Get a :class:`FileTracer` object for a file.
+
+ Plug-in type: file tracer.
+
+ Every Python source file is offered to your plug-in to give it a chance
+ to take responsibility for tracing the file. If your plug-in can
+ handle the file, it should return a :class:`FileTracer` object.
+ Otherwise return None.
+
+ There is no way to register your plug-in for particular files.
+ Instead, this method is invoked for all files as they are executed,
+ and the plug-in decides whether it can trace the file or not.
+ Be prepared for `filename` to refer to all kinds of files that have
+ nothing to do with your plug-in.
+
+ The file name will be a Python file being executed. There are two
+ broad categories of behavior for a plug-in, depending on the kind of
+ files your plug-in supports:
+
+ * Static file names: each of your original source files has been
+ converted into a distinct Python file. Your plug-in is invoked with
+ the Python file name, and it maps it back to its original source
+ file.
+
+ * Dynamic file names: all of your source files are executed by the same
+ Python file. In this case, your plug-in implements
+ :meth:`FileTracer.dynamic_source_filename` to provide the actual
+ source file for each execution frame.
+
+ `filename` is a string, the path to the file being considered. This is
+ the absolute real path to the file. If you are comparing to other
+ paths, be sure to take this into account.
+
+ Returns a :class:`FileTracer` object to use to trace `filename`, or
+ None if this plug-in cannot trace this file.
+
+ """
+ return None
+
+ def file_reporter(self, filename): # pylint: disable=unused-argument
+ """Get the :class:`FileReporter` class to use for a file.
+
+ Plug-in type: file tracer.
+
+ This will only be invoked if `filename` returns non-None from
+ :meth:`file_tracer`. It's an error to return None from this method.
+
+ Returns a :class:`FileReporter` object to use to report on `filename`,
+ or the string `"python"` to have coverage.py treat the file as Python.
+
+ """
+ _needs_to_implement(self, "file_reporter")
+
+ def dynamic_context(self, frame): # pylint: disable=unused-argument
+ """Get the dynamically computed context label for `frame`.
+
+ Plug-in type: dynamic context.
+
+ This method is invoked for each frame when outside of a dynamic
+ context, to see if a new dynamic context should be started. If it
+ returns a string, a new context label is set for this and deeper
+ frames. The dynamic context ends when this frame returns.
+
+ Returns a string to start a new dynamic context, or None if no new
+ context should be started.
+
+ """
+ return None
+
+ def find_executable_files(self, src_dir): # pylint: disable=unused-argument
+ """Yield all of the executable files in `src_dir`, recursively.
+
+ Plug-in type: file tracer.
+
+ Executability is a plug-in-specific property, but generally means files
+ which would have been considered for coverage analysis, had they been
+ included automatically.
+
+ Returns or yields a sequence of strings, the paths to files that could
+ have been executed, including files that had been executed.
+
+ """
+ return []
+
+ def configure(self, config):
+ """Modify the configuration of coverage.py.
+
+ Plug-in type: configurer.
+
+ This method is called during coverage.py start-up, to give your plug-in
+ a chance to change the configuration. The `config` parameter is an
+ object with :meth:`~coverage.Coverage.get_option` and
+ :meth:`~coverage.Coverage.set_option` methods. Do not call any other
+ methods on the `config` object.
+
+ """
+ pass
+
+ def sys_info(self):
+ """Get a list of information useful for debugging.
+
+ Plug-in type: any.
+
+ This method will be invoked for ``--debug=sys``. Your
+ plug-in can return any information it wants to be displayed.
+
+ Returns a list of pairs: `[(name, value), ...]`.
+
+ """
+ return []
+
+
+class FileTracer(object):
+ """Support needed for files during the execution phase.
+
+ File tracer plug-ins implement subclasses of FileTracer to return from
+ their :meth:`~CoveragePlugin.file_tracer` method.
+
+ You may construct this object from :meth:`CoveragePlugin.file_tracer` any
+ way you like. A natural choice would be to pass the file name given to
+ `file_tracer`.
+
+ `FileTracer` objects should only be created in the
+ :meth:`CoveragePlugin.file_tracer` method.
+
+ See :ref:`howitworks` for details of the different coverage.py phases.
+
+ """
+
+ def source_filename(self):
+ """The source file name for this file.
+
+ This may be any file name you like. A key responsibility of a plug-in
+ is to own the mapping from Python execution back to whatever source
+ file name was originally the source of the code.
+
+ See :meth:`CoveragePlugin.file_tracer` for details about static and
+ dynamic file names.
+
+ Returns the file name to credit with this execution.
+
+ """
+ _needs_to_implement(self, "source_filename")
+
+ def has_dynamic_source_filename(self):
+ """Does this FileTracer have dynamic source file names?
+
+ FileTracers can provide dynamically determined file names by
+ implementing :meth:`dynamic_source_filename`. Invoking that function
+ is expensive. To determine whether to invoke it, coverage.py uses the
+ result of this function to know if it needs to bother invoking
+ :meth:`dynamic_source_filename`.
+
+ See :meth:`CoveragePlugin.file_tracer` for details about static and
+ dynamic file names.
+
+ Returns True if :meth:`dynamic_source_filename` should be called to get
+ dynamic source file names.
+
+ """
+ return False
+
+ def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument
+ """Get a dynamically computed source file name.
+
+ Some plug-ins need to compute the source file name dynamically for each
+ frame.
+
+ This function will not be invoked if
+ :meth:`has_dynamic_source_filename` returns False.
+
+ Returns the source file name for this frame, or None if this frame
+ shouldn't be measured.
+
+ """
+ return None
+
+ def line_number_range(self, frame):
+ """Get the range of source line numbers for a given a call frame.
+
+ The call frame is examined, and the source line number in the original
+ file is returned. The return value is a pair of numbers, the starting
+ line number and the ending line number, both inclusive. For example,
+ returning (5, 7) means that lines 5, 6, and 7 should be considered
+ executed.
+
+ This function might decide that the frame doesn't indicate any lines
+ from the source file were executed. Return (-1, -1) in this case to
+ tell coverage.py that no lines should be recorded for this frame.
+
+ """
+ lineno = frame.f_lineno
+ return lineno, lineno
+
+
+class FileReporter(object):
+ """Support needed for files during the analysis and reporting phases.
+
+ File tracer plug-ins implement a subclass of `FileReporter`, and return
+ instances from their :meth:`CoveragePlugin.file_reporter` method.
+
+ There are many methods here, but only :meth:`lines` is required, to provide
+ the set of executable lines in the file.
+
+ See :ref:`howitworks` for details of the different coverage.py phases.
+
+ """
+
+ def __init__(self, filename):
+ """Simple initialization of a `FileReporter`.
+
+ The `filename` argument is the path to the file being reported. This
+ will be available as the `.filename` attribute on the object. Other
+ method implementations on this base class rely on this attribute.
+
+ """
+ self.filename = filename
+
+ def __repr__(self):
+ return "<{0.__class__.__name__} filename={0.filename!r}>".format(self)
+
+ def relative_filename(self):
+ """Get the relative file name for this file.
+
+ This file path will be displayed in reports. The default
+ implementation will supply the actual project-relative file path. You
+ only need to supply this method if you have an unusual syntax for file
+ paths.
+
+ """
+ return files.relative_filename(self.filename)
+
+ @contract(returns='unicode')
+ def source(self):
+ """Get the source for the file.
+
+ Returns a Unicode string.
+
+ The base implementation simply reads the `self.filename` file and
+ decodes it as UTF8. Override this method if your file isn't readable
+ as a text file, or if you need other encoding support.
+
+ """
+ with open(self.filename, "rb") as f:
+ return f.read().decode("utf8")
+
+ def lines(self):
+ """Get the executable lines in this file.
+
+ Your plug-in must determine which lines in the file were possibly
+ executable. This method returns a set of those line numbers.
+
+ Returns a set of line numbers.
+
+ """
+ _needs_to_implement(self, "lines")
+
+ def excluded_lines(self):
+ """Get the excluded executable lines in this file.
+
+ Your plug-in can use any method it likes to allow the user to exclude
+ executable lines from consideration.
+
+ Returns a set of line numbers.
+
+ The base implementation returns the empty set.
+
+ """
+ return set()
+
+ def translate_lines(self, lines):
+ """Translate recorded lines into reported lines.
+
+ Some file formats will want to report lines slightly differently than
+ they are recorded. For example, Python records the last line of a
+ multi-line statement, but reports are nicer if they mention the first
+ line.
+
+ Your plug-in can optionally define this method to perform these kinds
+ of adjustment.
+
+ `lines` is a sequence of integers, the recorded line numbers.
+
+ Returns a set of integers, the adjusted line numbers.
+
+ The base implementation returns the numbers unchanged.
+
+ """
+ return set(lines)
+
+ def arcs(self):
+ """Get the executable arcs in this file.
+
+ To support branch coverage, your plug-in needs to be able to indicate
+ possible execution paths, as a set of line number pairs. Each pair is
+ a `(prev, next)` pair indicating that execution can transition from the
+ `prev` line number to the `next` line number.
+
+ Returns a set of pairs of line numbers. The default implementation
+ returns an empty set.
+
+ """
+ return set()
+
+ def no_branch_lines(self):
+ """Get the lines excused from branch coverage in this file.
+
+ Your plug-in can use any method it likes to allow the user to exclude
+ lines from consideration of branch coverage.
+
+ Returns a set of line numbers.
+
+ The base implementation returns the empty set.
+
+ """
+ return set()
+
+ def translate_arcs(self, arcs):
+ """Translate recorded arcs into reported arcs.
+
+ Similar to :meth:`translate_lines`, but for arcs. `arcs` is a set of
+ line number pairs.
+
+ Returns a set of line number pairs.
+
+ The default implementation returns `arcs` unchanged.
+
+ """
+ return arcs
+
+ def exit_counts(self):
+ """Get a count of exits from that each line.
+
+ To determine which lines are branches, coverage.py looks for lines that
+ have more than one exit. This function creates a dict mapping each
+ executable line number to a count of how many exits it has.
+
+ To be honest, this feels wrong, and should be refactored. Let me know
+ if you attempt to implement this method in your plug-in...
+
+ """
+ return {}
+
+ def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument
+ """Provide an English sentence describing a missing arc.
+
+ The `start` and `end` arguments are the line numbers of the missing
+ arc. Negative numbers indicate entering or exiting code objects.
+
+ The `executed_arcs` argument is a set of line number pairs, the arcs
+ that were executed in this file.
+
+ By default, this simply returns the string "Line {start} didn't jump
+ to {end}".
+
+ """
+ return "Line {start} didn't jump to line {end}".format(start=start, end=end)
+
+ def source_token_lines(self):
+ """Generate a series of tokenized lines, one for each line in `source`.
+
+ These tokens are used for syntax-colored reports.
+
+ Each line is a list of pairs, each pair is a token::
+
+ [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
+
+ Each pair has a token class, and the token text. The token classes
+ are:
+
+ * ``'com'``: a comment
+ * ``'key'``: a keyword
+ * ``'nam'``: a name, or identifier
+ * ``'num'``: a number
+ * ``'op'``: an operator
+ * ``'str'``: a string literal
+ * ``'ws'``: some white space
+ * ``'txt'``: some other kind of text
+
+ If you concatenate all the token texts, and then join them with
+ newlines, you should have your original source back.
+
+ The default implementation simply returns each line tagged as
+ ``'txt'``.
+
+ """
+ for line in self.source().splitlines():
+ yield [('txt', line)]
+
+ # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
+ # of them defined.
+
+ def __eq__(self, other):
+ return isinstance(other, FileReporter) and self.filename == other.filename
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __lt__(self, other):
+ return self.filename < other.filename
+
+ def __le__(self, other):
+ return self.filename <= other.filename
+
+ def __gt__(self, other):
+ return self.filename > other.filename
+
+ def __ge__(self, other):
+ return self.filename >= other.filename
+
+ __hash__ = None # This object doesn't need to be hashed.
diff --git a/contrib/python/coverage/py2/coverage/plugin_support.py b/contrib/python/coverage/py2/coverage/plugin_support.py
new file mode 100644
index 0000000000..89c1c7658f
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/plugin_support.py
@@ -0,0 +1,281 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Support for plugins."""
+
+import os
+import os.path
+import sys
+
+from coverage.misc import CoverageException, isolate_module
+from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
+
+os = isolate_module(os)
+
+
+class Plugins(object):
+ """The currently loaded collection of coverage.py plugins."""
+
+ def __init__(self):
+ self.order = []
+ self.names = {}
+ self.file_tracers = []
+ self.configurers = []
+ self.context_switchers = []
+
+ self.current_module = None
+ self.debug = None
+
+ @classmethod
+ def load_plugins(cls, modules, config, debug=None):
+ """Load plugins from `modules`.
+
+ Returns a Plugins object with the loaded and configured plugins.
+
+ """
+ plugins = cls()
+ plugins.debug = debug
+
+ for module in modules:
+ plugins.current_module = module
+ __import__(module)
+ mod = sys.modules[module]
+
+ coverage_init = getattr(mod, "coverage_init", None)
+ if not coverage_init:
+ raise CoverageException(
+ "Plugin module %r didn't define a coverage_init function" % module
+ )
+
+ options = config.get_plugin_options(module)
+ coverage_init(plugins, options)
+
+ plugins.current_module = None
+ return plugins
+
+ def add_file_tracer(self, plugin):
+ """Add a file tracer plugin.
+
+ `plugin` is an instance of a third-party plugin class. It must
+ implement the :meth:`CoveragePlugin.file_tracer` method.
+
+ """
+ self._add_plugin(plugin, self.file_tracers)
+
+ def add_configurer(self, plugin):
+ """Add a configuring plugin.
+
+ `plugin` is an instance of a third-party plugin class. It must
+ implement the :meth:`CoveragePlugin.configure` method.
+
+ """
+ self._add_plugin(plugin, self.configurers)
+
+ def add_dynamic_context(self, plugin):
+ """Add a dynamic context plugin.
+
+ `plugin` is an instance of a third-party plugin class. It must
+ implement the :meth:`CoveragePlugin.dynamic_context` method.
+
+ """
+ self._add_plugin(plugin, self.context_switchers)
+
+ def add_noop(self, plugin):
+ """Add a plugin that does nothing.
+
+ This is only useful for testing the plugin support.
+
+ """
+ self._add_plugin(plugin, None)
+
+ def _add_plugin(self, plugin, specialized):
+ """Add a plugin object.
+
+ `plugin` is a :class:`CoveragePlugin` instance to add. `specialized`
+ is a list to append the plugin to.
+
+ """
+ plugin_name = "%s.%s" % (self.current_module, plugin.__class__.__name__)
+ if self.debug and self.debug.should('plugin'):
+ self.debug.write("Loaded plugin %r: %r" % (self.current_module, plugin))
+ labelled = LabelledDebug("plugin %r" % (self.current_module,), self.debug)
+ plugin = DebugPluginWrapper(plugin, labelled)
+
+ # pylint: disable=attribute-defined-outside-init
+ plugin._coverage_plugin_name = plugin_name
+ plugin._coverage_enabled = True
+ self.order.append(plugin)
+ self.names[plugin_name] = plugin
+ if specialized is not None:
+ specialized.append(plugin)
+
+ def __nonzero__(self):
+ return bool(self.order)
+
+ __bool__ = __nonzero__
+
+ def __iter__(self):
+ return iter(self.order)
+
+ def get(self, plugin_name):
+ """Return a plugin by name."""
+ return self.names[plugin_name]
+
+
+class LabelledDebug(object):
+ """A Debug writer, but with labels for prepending to the messages."""
+
+ def __init__(self, label, debug, prev_labels=()):
+ self.labels = list(prev_labels) + [label]
+ self.debug = debug
+
+ def add_label(self, label):
+ """Add a label to the writer, and return a new `LabelledDebug`."""
+ return LabelledDebug(label, self.debug, self.labels)
+
+ def message_prefix(self):
+ """The prefix to use on messages, combining the labels."""
+ prefixes = self.labels + ['']
+ return ":\n".join(" "*i+label for i, label in enumerate(prefixes))
+
+ def write(self, message):
+ """Write `message`, but with the labels prepended."""
+ self.debug.write("%s%s" % (self.message_prefix(), message))
+
+
+class DebugPluginWrapper(CoveragePlugin):
+ """Wrap a plugin, and use debug to report on what it's doing."""
+
+ def __init__(self, plugin, debug):
+ super(DebugPluginWrapper, self).__init__()
+ self.plugin = plugin
+ self.debug = debug
+
+ def file_tracer(self, filename):
+ tracer = self.plugin.file_tracer(filename)
+ self.debug.write("file_tracer(%r) --> %r" % (filename, tracer))
+ if tracer:
+ debug = self.debug.add_label("file %r" % (filename,))
+ tracer = DebugFileTracerWrapper(tracer, debug)
+ return tracer
+
+ def file_reporter(self, filename):
+ reporter = self.plugin.file_reporter(filename)
+ self.debug.write("file_reporter(%r) --> %r" % (filename, reporter))
+ if reporter:
+ debug = self.debug.add_label("file %r" % (filename,))
+ reporter = DebugFileReporterWrapper(filename, reporter, debug)
+ return reporter
+
+ def dynamic_context(self, frame):
+ context = self.plugin.dynamic_context(frame)
+ self.debug.write("dynamic_context(%r) --> %r" % (frame, context))
+ return context
+
+ def find_executable_files(self, src_dir):
+ executable_files = self.plugin.find_executable_files(src_dir)
+ self.debug.write("find_executable_files(%r) --> %r" % (src_dir, executable_files))
+ return executable_files
+
+ def configure(self, config):
+ self.debug.write("configure(%r)" % (config,))
+ self.plugin.configure(config)
+
+ def sys_info(self):
+ return self.plugin.sys_info()
+
+
+class DebugFileTracerWrapper(FileTracer):
+ """A debugging `FileTracer`."""
+
+ def __init__(self, tracer, debug):
+ self.tracer = tracer
+ self.debug = debug
+
+ def _show_frame(self, frame):
+ """A short string identifying a frame, for debug messages."""
+ return "%s@%d" % (
+ os.path.basename(frame.f_code.co_filename),
+ frame.f_lineno,
+ )
+
+ def source_filename(self):
+ sfilename = self.tracer.source_filename()
+ self.debug.write("source_filename() --> %r" % (sfilename,))
+ return sfilename
+
+ def has_dynamic_source_filename(self):
+ has = self.tracer.has_dynamic_source_filename()
+ self.debug.write("has_dynamic_source_filename() --> %r" % (has,))
+ return has
+
+ def dynamic_source_filename(self, filename, frame):
+ dyn = self.tracer.dynamic_source_filename(filename, frame)
+ self.debug.write("dynamic_source_filename(%r, %s) --> %r" % (
+ filename, self._show_frame(frame), dyn,
+ ))
+ return dyn
+
+ def line_number_range(self, frame):
+ pair = self.tracer.line_number_range(frame)
+ self.debug.write("line_number_range(%s) --> %r" % (self._show_frame(frame), pair))
+ return pair
+
+
+class DebugFileReporterWrapper(FileReporter):
+ """A debugging `FileReporter`."""
+
+ def __init__(self, filename, reporter, debug):
+ super(DebugFileReporterWrapper, self).__init__(filename)
+ self.reporter = reporter
+ self.debug = debug
+
+ def relative_filename(self):
+ ret = self.reporter.relative_filename()
+ self.debug.write("relative_filename() --> %r" % (ret,))
+ return ret
+
+ def lines(self):
+ ret = self.reporter.lines()
+ self.debug.write("lines() --> %r" % (ret,))
+ return ret
+
+ def excluded_lines(self):
+ ret = self.reporter.excluded_lines()
+ self.debug.write("excluded_lines() --> %r" % (ret,))
+ return ret
+
+ def translate_lines(self, lines):
+ ret = self.reporter.translate_lines(lines)
+ self.debug.write("translate_lines(%r) --> %r" % (lines, ret))
+ return ret
+
+ def translate_arcs(self, arcs):
+ ret = self.reporter.translate_arcs(arcs)
+ self.debug.write("translate_arcs(%r) --> %r" % (arcs, ret))
+ return ret
+
+ def no_branch_lines(self):
+ ret = self.reporter.no_branch_lines()
+ self.debug.write("no_branch_lines() --> %r" % (ret,))
+ return ret
+
+ def exit_counts(self):
+ ret = self.reporter.exit_counts()
+ self.debug.write("exit_counts() --> %r" % (ret,))
+ return ret
+
+ def arcs(self):
+ ret = self.reporter.arcs()
+ self.debug.write("arcs() --> %r" % (ret,))
+ return ret
+
+ def source(self):
+ ret = self.reporter.source()
+ self.debug.write("source() --> %d chars" % (len(ret),))
+ return ret
+
+ def source_token_lines(self):
+ ret = list(self.reporter.source_token_lines())
+ self.debug.write("source_token_lines() --> %d tokens" % (len(ret),))
+ return ret
diff --git a/contrib/python/coverage/py2/coverage/python.py b/contrib/python/coverage/py2/coverage/python.py
new file mode 100644
index 0000000000..6ff19c34fe
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/python.py
@@ -0,0 +1,261 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Python source expertise for coverage.py"""
+
+import sys
+import os.path
+import types
+import zipimport
+
+from coverage import env, files
+from coverage.misc import contract, expensive, isolate_module, join_regex
+from coverage.misc import CoverageException, NoSource
+from coverage.parser import PythonParser
+from coverage.phystokens import source_token_lines, source_encoding
+from coverage.plugin import FileReporter
+
+os = isolate_module(os)
+
+
+@contract(returns='bytes')
+def read_python_source(filename):
+ """Read the Python source text from `filename`.
+
+ Returns bytes.
+
+ """
+ with open(filename, "rb") as f:
+ source = f.read()
+
+ if env.IRONPYTHON:
+ # IronPython reads Unicode strings even for "rb" files.
+ source = bytes(source)
+
+ return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
+
+
+@contract(returns='unicode')
+def get_python_source(filename, force_fs=False):
+ """Return the source code, as unicode."""
+ if getattr(sys, 'is_standalone_binary', False) and not force_fs:
+ import __res
+
+ modname = __res.importer.file_source(filename)
+ if modname:
+ source = __res.find(modname)
+ source = source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
+ return source.decode('utf-8')
+ else:
+ # it's fake generated package
+ return u''
+ base, ext = os.path.splitext(filename)
+ if ext == ".py" and env.WINDOWS:
+ exts = [".py", ".pyw"]
+ else:
+ exts = [ext]
+
+ for ext in exts:
+ try_filename = base + ext
+ if os.path.exists(try_filename):
+ # A regular text file: open it.
+ source = read_python_source(try_filename)
+ break
+
+ # Maybe it's in a zip file?
+ source = get_zip_bytes(try_filename)
+ if source is not None:
+ break
+ else:
+ # Couldn't find source.
+ exc_msg = "No source for code: '%s'.\n" % (filename,)
+ exc_msg += "Aborting report output, consider using -i."
+ raise NoSource(exc_msg)
+
+ # Replace \f because of http://bugs.python.org/issue19035
+ source = source.replace(b'\f', b' ')
+ source = source.decode(source_encoding(source), "replace")
+
+ # Python code should always end with a line with a newline.
+ if source and source[-1] != '\n':
+ source += '\n'
+
+ return source
+
+
+@contract(returns='bytes|None')
+def get_zip_bytes(filename):
+ """Get data from `filename` if it is a zip file path.
+
+ Returns the bytestring data read from the zip file, or None if no zip file
+ could be found or `filename` isn't in it. The data returned will be
+ an empty string if the file is empty.
+
+ """
+ markers = ['.zip'+os.sep, '.egg'+os.sep, '.pex'+os.sep]
+ for marker in markers:
+ if marker in filename:
+ parts = filename.split(marker)
+ try:
+ zi = zipimport.zipimporter(parts[0]+marker[:-1])
+ except zipimport.ZipImportError:
+ continue
+ try:
+ data = zi.get_data(parts[1])
+ except IOError:
+ continue
+ return data
+ return None
+
+
+def source_for_file(filename):
+ """Return the source filename for `filename`.
+
+ Given a file name being traced, return the best guess as to the source
+ file to attribute it to.
+
+ """
+ if filename.endswith(".py"):
+ # .py files are themselves source files.
+ return filename
+
+ elif filename.endswith((".pyc", ".pyo")):
+ # Bytecode files probably have source files near them.
+ py_filename = filename[:-1]
+ if os.path.exists(py_filename):
+ # Found a .py file, use that.
+ return py_filename
+ if env.WINDOWS:
+ # On Windows, it could be a .pyw file.
+ pyw_filename = py_filename + "w"
+ if os.path.exists(pyw_filename):
+ return pyw_filename
+ # Didn't find source, but it's probably the .py file we want.
+ return py_filename
+
+ elif filename.endswith("$py.class"):
+ # Jython is easy to guess.
+ return filename[:-9] + ".py"
+
+ # No idea, just use the file name as-is.
+ return filename
+
+
+def source_for_morf(morf):
+ """Get the source filename for the module-or-file `morf`."""
+ if hasattr(morf, '__file__') and morf.__file__:
+ filename = morf.__file__
+ elif isinstance(morf, types.ModuleType):
+ # A module should have had .__file__, otherwise we can't use it.
+ # This could be a PEP-420 namespace package.
+ raise CoverageException("Module {} has no file".format(morf))
+ else:
+ filename = morf
+
+ filename = source_for_file(files.unicode_filename(filename))
+ return filename
+
+
+class PythonFileReporter(FileReporter):
+ """Report support for a Python file."""
+
+ def __init__(self, morf, coverage=None):
+ self.coverage = coverage
+
+ filename = source_for_morf(morf)
+
+ super(PythonFileReporter, self).__init__(files.canonical_filename(filename))
+
+ if hasattr(morf, '__name__'):
+ name = morf.__name__.replace(".", os.sep)
+ if os.path.basename(filename).startswith('__init__.'):
+ name += os.sep + "__init__"
+ name += ".py"
+ name = files.unicode_filename(name)
+ else:
+ name = files.relative_filename(filename)
+ self.relname = name
+
+ self._source = None
+ self._parser = None
+ self._excluded = None
+
+ def __repr__(self):
+ return "<PythonFileReporter {!r}>".format(self.filename)
+
+ @contract(returns='unicode')
+ def relative_filename(self):
+ return self.relname
+
+ @property
+ def parser(self):
+ """Lazily create a :class:`PythonParser`."""
+ if self._parser is None:
+ self._parser = PythonParser(
+ filename=self.filename,
+ exclude=self.coverage._exclude_regex('exclude'),
+ )
+ self._parser.parse_source()
+ return self._parser
+
+ def lines(self):
+ """Return the line numbers of statements in the file."""
+ return self.parser.statements
+
+ def excluded_lines(self):
+ """Return the line numbers of statements in the file."""
+ return self.parser.excluded
+
+ def translate_lines(self, lines):
+ return self.parser.translate_lines(lines)
+
+ def translate_arcs(self, arcs):
+ return self.parser.translate_arcs(arcs)
+
+ @expensive
+ def no_branch_lines(self):
+ no_branch = self.parser.lines_matching(
+ join_regex(self.coverage.config.partial_list),
+ join_regex(self.coverage.config.partial_always_list)
+ )
+ return no_branch
+
+ @expensive
+ def arcs(self):
+ return self.parser.arcs()
+
+ @expensive
+ def exit_counts(self):
+ return self.parser.exit_counts()
+
+ def missing_arc_description(self, start, end, executed_arcs=None):
+ return self.parser.missing_arc_description(start, end, executed_arcs)
+
+ @contract(returns='unicode')
+ def source(self):
+ if self._source is None:
+ self._source = get_python_source(self.filename)
+ return self._source
+
+ def should_be_python(self):
+ """Does it seem like this file should contain Python?
+
+ This is used to decide if a file reported as part of the execution of
+ a program was really likely to have contained Python in the first
+ place.
+
+ """
+ # Get the file extension.
+ _, ext = os.path.splitext(self.filename)
+
+ # Anything named *.py* should be Python.
+ if ext.startswith('.py'):
+ return True
+ # A file with no extension should be Python.
+ if not ext:
+ return True
+ # Everything else is probably not Python.
+ return False
+
+ def source_token_lines(self):
+ return source_token_lines(self.source())
diff --git a/contrib/python/coverage/py2/coverage/pytracer.py b/contrib/python/coverage/py2/coverage/pytracer.py
new file mode 100644
index 0000000000..7ab4d3ef92
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/pytracer.py
@@ -0,0 +1,274 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Raw data collector for coverage.py."""
+
+import atexit
+import dis
+import sys
+
+from coverage import env
+
+# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
+YIELD_VALUE = dis.opmap['YIELD_VALUE']
+if env.PY2:
+ YIELD_VALUE = chr(YIELD_VALUE)
+
+# When running meta-coverage, this file can try to trace itself, which confuses
+# everything. Don't trace ourselves.
+
+THIS_FILE = __file__.rstrip("co")
+
+
+class PyTracer(object):
+ """Python implementation of the raw data tracer."""
+
+ # Because of poor implementations of trace-function-manipulating tools,
+ # the Python trace function must be kept very simple. In particular, there
+ # must be only one function ever set as the trace function, both through
+ # sys.settrace, and as the return value from the trace function. Put
+ # another way, the trace function must always return itself. It cannot
+ # swap in other functions, or return None to avoid tracing a particular
+ # frame.
+ #
+ # The trace manipulator that introduced this restriction is DecoratorTools,
+ # which sets a trace function, and then later restores the pre-existing one
+ # by calling sys.settrace with a function it found in the current frame.
+ #
+ # Systems that use DecoratorTools (or similar trace manipulations) must use
+ # PyTracer to get accurate results. The command-line --timid argument is
+ # used to force the use of this tracer.
+
+ def __init__(self):
+ # Attributes set from the collector:
+ self.data = None
+ self.trace_arcs = False
+ self.should_trace = None
+ self.should_trace_cache = None
+ self.should_start_context = None
+ self.warn = None
+ # The threading module to use, if any.
+ self.threading = None
+
+ self.cur_file_dict = None
+ self.last_line = 0 # int, but uninitialized.
+ self.cur_file_name = None
+ self.context = None
+ self.started_context = False
+
+ self.data_stack = []
+ self.last_exc_back = None
+ self.last_exc_firstlineno = 0
+ self.thread = None
+ self.stopped = False
+ self._activity = False
+
+ self.in_atexit = False
+ # On exit, self.in_atexit = True
+ atexit.register(setattr, self, 'in_atexit', True)
+
+ def __repr__(self):
+ return "<PyTracer at {}: {} lines in {} files>".format(
+ id(self),
+ sum(len(v) for v in self.data.values()),
+ len(self.data),
+ )
+
+ def log(self, marker, *args):
+ """For hard-core logging of what this tracer is doing."""
+ with open("/tmp/debug_trace.txt", "a") as f:
+ f.write("{} {}[{}]".format(
+ marker,
+ id(self),
+ len(self.data_stack),
+ ))
+ if 0:
+ f.write(".{:x}.{:x}".format(
+ self.thread.ident,
+ self.threading.currentThread().ident,
+ ))
+ f.write(" {}".format(" ".join(map(str, args))))
+ if 0:
+ f.write(" | ")
+ stack = " / ".join(
+ (fname or "???").rpartition("/")[-1]
+ for _, fname, _, _ in self.data_stack
+ )
+ f.write(stack)
+ f.write("\n")
+
+ def _trace(self, frame, event, arg_unused):
+ """The trace function passed to sys.settrace."""
+
+ if THIS_FILE in frame.f_code.co_filename:
+ return None
+
+ #self.log(":", frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + "()", event)
+
+ if (self.stopped and sys.gettrace() == self._trace): # pylint: disable=comparison-with-callable
+ # The PyTrace.stop() method has been called, possibly by another
+ # thread, let's deactivate ourselves now.
+ if 0:
+ self.log("---\nX", frame.f_code.co_filename, frame.f_lineno)
+ f = frame
+ while f:
+ self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace)
+ f = f.f_back
+ sys.settrace(None)
+ self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
+ self.data_stack.pop()
+ )
+ return None
+
+ if self.last_exc_back:
+ if frame == self.last_exc_back:
+ # Someone forgot a return event.
+ if self.trace_arcs and self.cur_file_dict:
+ pair = (self.last_line, -self.last_exc_firstlineno)
+ self.cur_file_dict[pair] = None
+ self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
+ self.data_stack.pop()
+ )
+ self.last_exc_back = None
+
+ # if event != 'call' and frame.f_code.co_filename != self.cur_file_name:
+ # self.log("---\n*", frame.f_code.co_filename, self.cur_file_name, frame.f_lineno)
+
+ if event == 'call':
+ # Should we start a new context?
+ if self.should_start_context and self.context is None:
+ context_maybe = self.should_start_context(frame)
+ if context_maybe is not None:
+ self.context = context_maybe
+ self.started_context = True
+ self.switch_context(self.context)
+ else:
+ self.started_context = False
+ else:
+ self.started_context = False
+
+ # Entering a new frame. Decide if we should trace
+ # in this file.
+ self._activity = True
+ self.data_stack.append(
+ (
+ self.cur_file_dict,
+ self.cur_file_name,
+ self.last_line,
+ self.started_context,
+ )
+ )
+ filename = frame.f_code.co_filename
+ self.cur_file_name = filename
+ disp = self.should_trace_cache.get(filename)
+ if disp is None:
+ disp = self.should_trace(filename, frame)
+ self.should_trace_cache[filename] = disp
+
+ self.cur_file_dict = None
+ if disp.trace:
+ tracename = disp.source_filename
+ if tracename not in self.data:
+ self.data[tracename] = {}
+ self.cur_file_dict = self.data[tracename]
+ # The call event is really a "start frame" event, and happens for
+ # function calls and re-entering generators. The f_lasti field is
+ # -1 for calls, and a real offset for generators. Use <0 as the
+ # line number for calls, and the real line number for generators.
+ if getattr(frame, 'f_lasti', -1) < 0:
+ self.last_line = -frame.f_code.co_firstlineno
+ else:
+ self.last_line = frame.f_lineno
+ elif event == 'line':
+ # Record an executed line.
+ if self.cur_file_dict is not None:
+ lineno = frame.f_lineno
+
+ if self.trace_arcs:
+ self.cur_file_dict[(self.last_line, lineno)] = None
+ else:
+ self.cur_file_dict[lineno] = None
+ self.last_line = lineno
+ elif event == 'return':
+ if self.trace_arcs and self.cur_file_dict:
+ # Record an arc leaving the function, but beware that a
+ # "return" event might just mean yielding from a generator.
+ # Jython seems to have an empty co_code, so just assume return.
+ code = frame.f_code.co_code
+ if (not code) or code[frame.f_lasti] != YIELD_VALUE:
+ first = frame.f_code.co_firstlineno
+ self.cur_file_dict[(self.last_line, -first)] = None
+ # Leaving this function, pop the filename stack.
+ self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
+ self.data_stack.pop()
+ )
+ # Leaving a context?
+ if self.started_context:
+ self.context = None
+ self.switch_context(None)
+ elif event == 'exception':
+ self.last_exc_back = frame.f_back
+ self.last_exc_firstlineno = frame.f_code.co_firstlineno
+ return self._trace
+
+ def start(self):
+ """Start this Tracer.
+
+ Return a Python function suitable for use with sys.settrace().
+
+ """
+ self.stopped = False
+ if self.threading:
+ if self.thread is None:
+ self.thread = self.threading.currentThread()
+ else:
+ if self.thread.ident != self.threading.currentThread().ident:
+ # Re-starting from a different thread!? Don't set the trace
+ # function, but we are marked as running again, so maybe it
+ # will be ok?
+ #self.log("~", "starting on different threads")
+ return self._trace
+
+ sys.settrace(self._trace)
+ return self._trace
+
+ def stop(self):
+ """Stop this Tracer."""
+ # Get the active tracer callback before setting the stop flag to be
+ # able to detect if the tracer was changed prior to stopping it.
+ tf = sys.gettrace()
+
+ # Set the stop flag. The actual call to sys.settrace(None) will happen
+ # in the self._trace callback itself to make sure to call it from the
+ # right thread.
+ self.stopped = True
+
+ if self.threading and self.thread.ident != self.threading.currentThread().ident:
+ # Called on a different thread than started us: we can't unhook
+ # ourselves, but we've set the flag that we should stop, so we
+ # won't do any more tracing.
+ #self.log("~", "stopping on different threads")
+ return
+
+ if self.warn:
+ # PyPy clears the trace function before running atexit functions,
+ # so don't warn if we are in atexit on PyPy and the trace function
+ # has changed to None.
+ dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None)
+ if (not dont_warn) and tf != self._trace: # pylint: disable=comparison-with-callable
+ self.warn(
+ "Trace function changed, measurement is likely wrong: %r" % (tf,),
+ slug="trace-changed",
+ )
+
+ def activity(self):
+ """Has there been any activity?"""
+ return self._activity
+
+ def reset_activity(self):
+ """Reset the activity() flag."""
+ self._activity = False
+
+ def get_stats(self):
+ """Return a dictionary of statistics, or None."""
+ return None
diff --git a/contrib/python/coverage/py2/coverage/report.py b/contrib/python/coverage/py2/coverage/report.py
new file mode 100644
index 0000000000..64678ff95d
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/report.py
@@ -0,0 +1,86 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Reporter foundation for coverage.py."""
+import sys
+
+from coverage import env
+from coverage.files import prep_patterns, FnmatchMatcher
+from coverage.misc import CoverageException, NoSource, NotPython, ensure_dir_for_file, file_be_gone
+
+
+def render_report(output_path, reporter, morfs):
+ """Run the provided reporter ensuring any required setup and cleanup is done
+
+ At a high level this method ensures the output file is ready to be written to. Then writes the
+ report to it. Then closes the file and deletes any garbage created if necessary.
+ """
+ file_to_close = None
+ delete_file = False
+ if output_path:
+ if output_path == '-':
+ outfile = sys.stdout
+ else:
+ # Ensure that the output directory is created; done here
+ # because this report pre-opens the output file.
+ # HTMLReport does this using the Report plumbing because
+ # its task is more complex, being multiple files.
+ ensure_dir_for_file(output_path)
+ open_kwargs = {}
+ if env.PY3:
+ open_kwargs['encoding'] = 'utf8'
+ outfile = open(output_path, "w", **open_kwargs)
+ file_to_close = outfile
+ try:
+ return reporter.report(morfs, outfile=outfile)
+ except CoverageException:
+ delete_file = True
+ raise
+ finally:
+ if file_to_close:
+ file_to_close.close()
+ if delete_file:
+ file_be_gone(output_path)
+
+
+def get_analysis_to_report(coverage, morfs):
+ """Get the files to report on.
+
+ For each morf in `morfs`, if it should be reported on (based on the omit
+ and include configuration options), yield a pair, the `FileReporter` and
+ `Analysis` for the morf.
+
+ """
+ file_reporters = coverage._get_file_reporters(morfs)
+ config = coverage.config
+
+ if config.report_include:
+ matcher = FnmatchMatcher(prep_patterns(config.report_include))
+ file_reporters = [fr for fr in file_reporters if matcher.match(fr.filename)]
+
+ if config.report_omit:
+ matcher = FnmatchMatcher(prep_patterns(config.report_omit))
+ file_reporters = [fr for fr in file_reporters if not matcher.match(fr.filename)]
+
+ if not file_reporters:
+ raise CoverageException("No data to report.")
+
+ for fr in sorted(file_reporters):
+ try:
+ analysis = coverage._analyze(fr)
+ except NoSource:
+ if not config.ignore_errors:
+ raise
+ except NotPython:
+ # Only report errors for .py files, and only if we didn't
+ # explicitly suppress those errors.
+ # NotPython is only raised by PythonFileReporter, which has a
+ # should_be_python() method.
+ if fr.should_be_python():
+ if config.ignore_errors:
+ msg = "Couldn't parse Python file '{}'".format(fr.filename)
+ coverage._warn(msg, slug="couldnt-parse")
+ else:
+ raise
+ else:
+ yield (fr, analysis)
diff --git a/contrib/python/coverage/py2/coverage/results.py b/contrib/python/coverage/py2/coverage/results.py
new file mode 100644
index 0000000000..4916864df3
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/results.py
@@ -0,0 +1,343 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Results of coverage measurement."""
+
+import collections
+
+from coverage.backward import iitems
+from coverage.debug import SimpleReprMixin
+from coverage.misc import contract, CoverageException, nice_pair
+
+
+class Analysis(object):
+ """The results of analyzing a FileReporter."""
+
+ def __init__(self, data, file_reporter, file_mapper):
+ self.data = data
+ self.file_reporter = file_reporter
+ self.filename = file_mapper(self.file_reporter.filename)
+ self.statements = self.file_reporter.lines()
+ self.excluded = self.file_reporter.excluded_lines()
+
+ # Identify missing statements.
+ executed = self.data.lines(self.filename) or []
+ executed = self.file_reporter.translate_lines(executed)
+ self.executed = executed
+ self.missing = self.statements - self.executed
+
+ if self.data.has_arcs():
+ self._arc_possibilities = sorted(self.file_reporter.arcs())
+ self.exit_counts = self.file_reporter.exit_counts()
+ self.no_branch = self.file_reporter.no_branch_lines()
+ n_branches = self._total_branches()
+ mba = self.missing_branch_arcs()
+ n_partial_branches = sum(len(v) for k,v in iitems(mba) if k not in self.missing)
+ n_missing_branches = sum(len(v) for k,v in iitems(mba))
+ else:
+ self._arc_possibilities = []
+ self.exit_counts = {}
+ self.no_branch = set()
+ n_branches = n_partial_branches = n_missing_branches = 0
+
+ self.numbers = Numbers(
+ n_files=1,
+ n_statements=len(self.statements),
+ n_excluded=len(self.excluded),
+ n_missing=len(self.missing),
+ n_branches=n_branches,
+ n_partial_branches=n_partial_branches,
+ n_missing_branches=n_missing_branches,
+ )
+
+ def missing_formatted(self, branches=False):
+ """The missing line numbers, formatted nicely.
+
+ Returns a string like "1-2, 5-11, 13-14".
+
+ If `branches` is true, includes the missing branch arcs also.
+
+ """
+ if branches and self.has_arcs():
+ arcs = iitems(self.missing_branch_arcs())
+ else:
+ arcs = None
+
+ return format_lines(self.statements, self.missing, arcs=arcs)
+
+ def has_arcs(self):
+ """Were arcs measured in this result?"""
+ return self.data.has_arcs()
+
+ @contract(returns='list(tuple(int, int))')
+ def arc_possibilities(self):
+ """Returns a sorted list of the arcs in the code."""
+ return self._arc_possibilities
+
+ @contract(returns='list(tuple(int, int))')
+ def arcs_executed(self):
+ """Returns a sorted list of the arcs actually executed in the code."""
+ executed = self.data.arcs(self.filename) or []
+ executed = self.file_reporter.translate_arcs(executed)
+ return sorted(executed)
+
+ @contract(returns='list(tuple(int, int))')
+ def arcs_missing(self):
+ """Returns a sorted list of the arcs in the code not executed."""
+ possible = self.arc_possibilities()
+ executed = self.arcs_executed()
+ missing = (
+ p for p in possible
+ if p not in executed
+ and p[0] not in self.no_branch
+ )
+ return sorted(missing)
+
+ @contract(returns='list(tuple(int, int))')
+ def arcs_unpredicted(self):
+ """Returns a sorted list of the executed arcs missing from the code."""
+ possible = self.arc_possibilities()
+ executed = self.arcs_executed()
+ # Exclude arcs here which connect a line to itself. They can occur
+ # in executed data in some cases. This is where they can cause
+ # trouble, and here is where it's the least burden to remove them.
+ # Also, generators can somehow cause arcs from "enter" to "exit", so
+ # make sure we have at least one positive value.
+ unpredicted = (
+ e for e in executed
+ if e not in possible
+ and e[0] != e[1]
+ and (e[0] > 0 or e[1] > 0)
+ )
+ return sorted(unpredicted)
+
+ def _branch_lines(self):
+ """Returns a list of line numbers that have more than one exit."""
+ return [l1 for l1,count in iitems(self.exit_counts) if count > 1]
+
+ def _total_branches(self):
+ """How many total branches are there?"""
+ return sum(count for count in self.exit_counts.values() if count > 1)
+
+ @contract(returns='dict(int: list(int))')
+ def missing_branch_arcs(self):
+ """Return arcs that weren't executed from branch lines.
+
+ Returns {l1:[l2a,l2b,...], ...}
+
+ """
+ missing = self.arcs_missing()
+ branch_lines = set(self._branch_lines())
+ mba = collections.defaultdict(list)
+ for l1, l2 in missing:
+ if l1 in branch_lines:
+ mba[l1].append(l2)
+ return mba
+
+ @contract(returns='dict(int: tuple(int, int))')
+ def branch_stats(self):
+ """Get stats about branches.
+
+ Returns a dict mapping line numbers to a tuple:
+ (total_exits, taken_exits).
+ """
+
+ missing_arcs = self.missing_branch_arcs()
+ stats = {}
+ for lnum in self._branch_lines():
+ exits = self.exit_counts[lnum]
+ missing = len(missing_arcs[lnum])
+ stats[lnum] = (exits, exits - missing)
+ return stats
+
+
+class Numbers(SimpleReprMixin):
+ """The numerical results of measuring coverage.
+
+ This holds the basic statistics from `Analysis`, and is used to roll
+ up statistics across files.
+
+ """
+ # A global to determine the precision on coverage percentages, the number
+ # of decimal places.
+ _precision = 0
+ _near0 = 1.0 # These will change when _precision is changed.
+ _near100 = 99.0
+
+ def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
+ n_branches=0, n_partial_branches=0, n_missing_branches=0
+ ):
+ self.n_files = n_files
+ self.n_statements = n_statements
+ self.n_excluded = n_excluded
+ self.n_missing = n_missing
+ self.n_branches = n_branches
+ self.n_partial_branches = n_partial_branches
+ self.n_missing_branches = n_missing_branches
+
+ def init_args(self):
+ """Return a list for __init__(*args) to recreate this object."""
+ return [
+ self.n_files, self.n_statements, self.n_excluded, self.n_missing,
+ self.n_branches, self.n_partial_branches, self.n_missing_branches,
+ ]
+
+ @classmethod
+ def set_precision(cls, precision):
+ """Set the number of decimal places used to report percentages."""
+ assert 0 <= precision < 10
+ cls._precision = precision
+ cls._near0 = 1.0 / 10**precision
+ cls._near100 = 100.0 - cls._near0
+
+ @property
+ def n_executed(self):
+ """Returns the number of executed statements."""
+ return self.n_statements - self.n_missing
+
+ @property
+ def n_executed_branches(self):
+ """Returns the number of executed branches."""
+ return self.n_branches - self.n_missing_branches
+
+ @property
+ def pc_covered(self):
+ """Returns a single percentage value for coverage."""
+ if self.n_statements > 0:
+ numerator, denominator = self.ratio_covered
+ pc_cov = (100.0 * numerator) / denominator
+ else:
+ pc_cov = 100.0
+ return pc_cov
+
+ @property
+ def pc_covered_str(self):
+ """Returns the percent covered, as a string, without a percent sign.
+
+ Note that "0" is only returned when the value is truly zero, and "100"
+ is only returned when the value is truly 100. Rounding can never
+ result in either "0" or "100".
+
+ """
+ pc = self.pc_covered
+ if 0 < pc < self._near0:
+ pc = self._near0
+ elif self._near100 < pc < 100:
+ pc = self._near100
+ else:
+ pc = round(pc, self._precision)
+ return "%.*f" % (self._precision, pc)
+
+ @classmethod
+ def pc_str_width(cls):
+ """How many characters wide can pc_covered_str be?"""
+ width = 3 # "100"
+ if cls._precision > 0:
+ width += 1 + cls._precision
+ return width
+
+ @property
+ def ratio_covered(self):
+ """Return a numerator and denominator for the coverage ratio."""
+ numerator = self.n_executed + self.n_executed_branches
+ denominator = self.n_statements + self.n_branches
+ return numerator, denominator
+
+ def __add__(self, other):
+ nums = Numbers()
+ nums.n_files = self.n_files + other.n_files
+ nums.n_statements = self.n_statements + other.n_statements
+ nums.n_excluded = self.n_excluded + other.n_excluded
+ nums.n_missing = self.n_missing + other.n_missing
+ nums.n_branches = self.n_branches + other.n_branches
+ nums.n_partial_branches = (
+ self.n_partial_branches + other.n_partial_branches
+ )
+ nums.n_missing_branches = (
+ self.n_missing_branches + other.n_missing_branches
+ )
+ return nums
+
+ def __radd__(self, other):
+ # Implementing 0+Numbers allows us to sum() a list of Numbers.
+ if other == 0:
+ return self
+ return NotImplemented # pragma: not covered (we never call it this way)
+
+
+def _line_ranges(statements, lines):
+ """Produce a list of ranges for `format_lines`."""
+ statements = sorted(statements)
+ lines = sorted(lines)
+
+ pairs = []
+ start = None
+ lidx = 0
+ for stmt in statements:
+ if lidx >= len(lines):
+ break
+ if stmt == lines[lidx]:
+ lidx += 1
+ if not start:
+ start = stmt
+ end = stmt
+ elif start:
+ pairs.append((start, end))
+ start = None
+ if start:
+ pairs.append((start, end))
+ return pairs
+
+
+def format_lines(statements, lines, arcs=None):
+ """Nicely format a list of line numbers.
+
+ Format a list of line numbers for printing by coalescing groups of lines as
+ long as the lines represent consecutive statements. This will coalesce
+ even if there are gaps between statements.
+
+ For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and
+ `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14".
+
+ Both `lines` and `statements` can be any iterable. All of the elements of
+ `lines` must be in `statements`, and all of the values must be positive
+ integers.
+
+ If `arcs` is provided, they are (start,[end,end,end]) pairs that will be
+ included in the output as long as start isn't in `lines`.
+
+ """
+ line_items = [(pair[0], nice_pair(pair)) for pair in _line_ranges(statements, lines)]
+ if arcs:
+ line_exits = sorted(arcs)
+ for line, exits in line_exits:
+ for ex in sorted(exits):
+ if line not in lines and ex not in lines:
+ dest = (ex if ex > 0 else "exit")
+ line_items.append((line, "%d->%s" % (line, dest)))
+
+ ret = ', '.join(t[-1] for t in sorted(line_items))
+ return ret
+
+
+@contract(total='number', fail_under='number', precision=int, returns=bool)
+def should_fail_under(total, fail_under, precision):
+ """Determine if a total should fail due to fail-under.
+
+ `total` is a float, the coverage measurement total. `fail_under` is the
+ fail_under setting to compare with. `precision` is the number of digits
+ to consider after the decimal point.
+
+ Returns True if the total should fail.
+
+ """
+ # We can never achieve higher than 100% coverage, or less than zero.
+ if not (0 <= fail_under <= 100.0):
+ msg = "fail_under={} is invalid. Must be between 0 and 100.".format(fail_under)
+ raise CoverageException(msg)
+
+ # Special case for fail_under=100, it must really be 100.
+ if fail_under == 100.0 and total != 100.0:
+ return True
+
+ return round(total, precision) < fail_under
diff --git a/contrib/python/coverage/py2/coverage/sqldata.py b/contrib/python/coverage/py2/coverage/sqldata.py
new file mode 100644
index 0000000000..a150fdfd0f
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/sqldata.py
@@ -0,0 +1,1123 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Sqlite coverage data."""
+
+# TODO: factor out dataop debugging to a wrapper class?
+# TODO: make sure all dataop debugging is in place somehow
+
+import collections
+import datetime
+import glob
+import itertools
+import os
+import re
+import sqlite3
+import sys
+import zlib
+
+from coverage import env
+from coverage.backward import get_thread_id, iitems, to_bytes, to_string
+from coverage.debug import NoDebugging, SimpleReprMixin, clipped_repr
+from coverage.files import PathAliases
+from coverage.misc import CoverageException, contract, file_be_gone, filename_suffix, isolate_module
+from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits
+from coverage.version import __version__
+
+os = isolate_module(os)
+
+# If you change the schema, increment the SCHEMA_VERSION, and update the
+# docs in docs/dbschema.rst also.
+
+SCHEMA_VERSION = 7
+
+# Schema versions:
+# 1: Released in 5.0a2
+# 2: Added contexts in 5.0a3.
+# 3: Replaced line table with line_map table.
+# 4: Changed line_map.bitmap to line_map.numbits.
+# 5: Added foreign key declarations.
+# 6: Key-value in meta.
+# 7: line_map -> line_bits
+
+SCHEMA = """\
+CREATE TABLE coverage_schema (
+ -- One row, to record the version of the schema in this db.
+ version integer
+);
+
+CREATE TABLE meta (
+ -- Key-value pairs, to record metadata about the data
+ key text,
+ value text,
+ unique (key)
+ -- Keys:
+ -- 'has_arcs' boolean -- Is this data recording branches?
+ -- 'sys_argv' text -- The coverage command line that recorded the data.
+ -- 'version' text -- The version of coverage.py that made the file.
+ -- 'when' text -- Datetime when the file was created.
+);
+
+CREATE TABLE file (
+ -- A row per file measured.
+ id integer primary key,
+ path text,
+ unique (path)
+);
+
+CREATE TABLE context (
+ -- A row per context measured.
+ id integer primary key,
+ context text,
+ unique (context)
+);
+
+CREATE TABLE line_bits (
+ -- If recording lines, a row per context per file executed.
+ -- All of the line numbers for that file/context are in one numbits.
+ file_id integer, -- foreign key to `file`.
+ context_id integer, -- foreign key to `context`.
+ numbits blob, -- see the numbits functions in coverage.numbits
+ foreign key (file_id) references file (id),
+ foreign key (context_id) references context (id),
+ unique (file_id, context_id)
+);
+
+CREATE TABLE arc (
+ -- If recording branches, a row per context per from/to line transition executed.
+ file_id integer, -- foreign key to `file`.
+ context_id integer, -- foreign key to `context`.
+ fromno integer, -- line number jumped from.
+ tono integer, -- line number jumped to.
+ foreign key (file_id) references file (id),
+ foreign key (context_id) references context (id),
+ unique (file_id, context_id, fromno, tono)
+);
+
+CREATE TABLE tracer (
+ -- A row per file indicating the tracer used for that file.
+ file_id integer primary key,
+ tracer text,
+ foreign key (file_id) references file (id)
+);
+"""
+
+class CoverageData(SimpleReprMixin):
+ """Manages collected coverage data, including file storage.
+
+ This class is the public supported API to the data that coverage.py
+ collects during program execution. It includes information about what code
+ was executed. It does not include information from the analysis phase, to
+ determine what lines could have been executed, or what lines were not
+ executed.
+
+ .. note::
+
+ The data file is currently a SQLite database file, with a
+ :ref:`documented schema <dbschema>`. The schema is subject to change
+ though, so be careful about querying it directly. Use this API if you
+ can to isolate yourself from changes.
+
+ There are a number of kinds of data that can be collected:
+
+ * **lines**: the line numbers of source lines that were executed.
+ These are always available.
+
+ * **arcs**: pairs of source and destination line numbers for transitions
+ between source lines. These are only available if branch coverage was
+ used.
+
+ * **file tracer names**: the module names of the file tracer plugins that
+ handled each file in the data.
+
+ Lines, arcs, and file tracer names are stored for each source file. File
+ names in this API are case-sensitive, even on platforms with
+ case-insensitive file systems.
+
+ A data file either stores lines, or arcs, but not both.
+
+ A data file is associated with the data when the :class:`CoverageData`
+ is created, using the parameters `basename`, `suffix`, and `no_disk`. The
+ base name can be queried with :meth:`base_filename`, and the actual file
+ name being used is available from :meth:`data_filename`.
+
+ To read an existing coverage.py data file, use :meth:`read`. You can then
+ access the line, arc, or file tracer data with :meth:`lines`, :meth:`arcs`,
+ or :meth:`file_tracer`.
+
+ The :meth:`has_arcs` method indicates whether arc data is available. You
+ can get a set of the files in the data with :meth:`measured_files`. As
+ with most Python containers, you can determine if there is any data at all
+ by using this object as a boolean value.
+
+ The contexts for each line in a file can be read with
+ :meth:`contexts_by_lineno`.
+
+ To limit querying to certain contexts, use :meth:`set_query_context` or
+ :meth:`set_query_contexts`. These will narrow the focus of subsequent
+ :meth:`lines`, :meth:`arcs`, and :meth:`contexts_by_lineno` calls. The set
+ of all measured context names can be retrieved with
+ :meth:`measured_contexts`.
+
+ Most data files will be created by coverage.py itself, but you can use
+ methods here to create data files if you like. The :meth:`add_lines`,
+ :meth:`add_arcs`, and :meth:`add_file_tracers` methods add data, in ways
+ that are convenient for coverage.py.
+
+ To record data for contexts, use :meth:`set_context` to set a context to
+ be used for subsequent :meth:`add_lines` and :meth:`add_arcs` calls.
+
+ To add a source file without any measured data, use :meth:`touch_file`,
+ or :meth:`touch_files` for a list of such files.
+
+ Write the data to its file with :meth:`write`.
+
+ You can clear the data in memory with :meth:`erase`. Two data collections
+ can be combined by using :meth:`update` on one :class:`CoverageData`,
+ passing it the other.
+
+ Data in a :class:`CoverageData` can be serialized and deserialized with
+ :meth:`dumps` and :meth:`loads`.
+
+ """
+
+ def __init__(self, basename=None, suffix=None, no_disk=False, warn=None, debug=None):
+ """Create a :class:`CoverageData` object to hold coverage-measured data.
+
+ Arguments:
+ basename (str): the base name of the data file, defaulting to
+ ".coverage".
+ suffix (str or bool): has the same meaning as the `data_suffix`
+ argument to :class:`coverage.Coverage`.
+ no_disk (bool): if True, keep all data in memory, and don't
+ write any disk file.
+ warn: a warning callback function, accepting a warning message
+ argument.
+ debug: a `DebugControl` object (optional)
+
+ """
+ self._no_disk = no_disk
+ self._basename = os.path.abspath(basename or ".coverage")
+ self._suffix = suffix
+ self._warn = warn
+ self._debug = debug or NoDebugging()
+
+ self._choose_filename()
+ self._file_map = {}
+ # Maps thread ids to SqliteDb objects.
+ self._dbs = {}
+ self._pid = os.getpid()
+
+ # Are we in sync with the data file?
+ self._have_used = False
+
+ self._has_lines = False
+ self._has_arcs = False
+
+ self._current_context = None
+ self._current_context_id = None
+ self._query_context_ids = None
+
+ def _choose_filename(self):
+ """Set self._filename based on inited attributes."""
+ if self._no_disk:
+ self._filename = ":memory:"
+ else:
+ self._filename = self._basename
+ suffix = filename_suffix(self._suffix)
+ if suffix:
+ self._filename += "." + suffix
+
+ def _reset(self):
+ """Reset our attributes."""
+ if self._dbs:
+ for db in self._dbs.values():
+ db.close()
+ self._dbs = {}
+ self._file_map = {}
+ self._have_used = False
+ self._current_context_id = None
+
+ def _create_db(self):
+ """Create a db file that doesn't exist yet.
+
+ Initializes the schema and certain metadata.
+ """
+ if self._debug.should('dataio'):
+ self._debug.write("Creating data file {!r}".format(self._filename))
+ self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
+ with db:
+ db.executescript(SCHEMA)
+ db.execute("insert into coverage_schema (version) values (?)", (SCHEMA_VERSION,))
+ db.executemany(
+ "insert into meta (key, value) values (?, ?)",
+ [
+ ('sys_argv', str(getattr(sys, 'argv', None))),
+ ('version', __version__),
+ ('when', datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')),
+ ]
+ )
+
+ def _open_db(self):
+ """Open an existing db file, and read its metadata."""
+ if self._debug.should('dataio'):
+ self._debug.write("Opening data file {!r}".format(self._filename))
+ self._dbs[get_thread_id()] = SqliteDb(self._filename, self._debug)
+ self._read_db()
+
+ def _read_db(self):
+ """Read the metadata from a database so that we are ready to use it."""
+ with self._dbs[get_thread_id()] as db:
+ try:
+ schema_version, = db.execute_one("select version from coverage_schema")
+ except Exception as exc:
+ raise CoverageException(
+ "Data file {!r} doesn't seem to be a coverage data file: {}".format(
+ self._filename, exc
+ )
+ )
+ else:
+ if schema_version != SCHEMA_VERSION:
+ raise CoverageException(
+ "Couldn't use data file {!r}: wrong schema: {} instead of {}".format(
+ self._filename, schema_version, SCHEMA_VERSION
+ )
+ )
+
+ for row in db.execute("select value from meta where key = 'has_arcs'"):
+ self._has_arcs = bool(int(row[0]))
+ self._has_lines = not self._has_arcs
+
+ for path, file_id in db.execute("select path, id from file"):
+ self._file_map[path] = file_id
+
+ def _connect(self):
+ """Get the SqliteDb object to use."""
+ if get_thread_id() not in self._dbs:
+ if os.path.exists(self._filename):
+ self._open_db()
+ else:
+ self._create_db()
+ return self._dbs[get_thread_id()]
+
+ def __nonzero__(self):
+ if (get_thread_id() not in self._dbs and not os.path.exists(self._filename)):
+ return False
+ try:
+ with self._connect() as con:
+ rows = con.execute("select * from file limit 1")
+ return bool(list(rows))
+ except CoverageException:
+ return False
+
+ __bool__ = __nonzero__
+
+ @contract(returns='bytes')
+ def dumps(self):
+ """Serialize the current data to a byte string.
+
+ The format of the serialized data is not documented. It is only
+ suitable for use with :meth:`loads` in the same version of
+ coverage.py.
+
+ Returns:
+ A byte string of serialized data.
+
+ .. versionadded:: 5.0
+
+ """
+ if self._debug.should('dataio'):
+ self._debug.write("Dumping data from data file {!r}".format(self._filename))
+ with self._connect() as con:
+ return b'z' + zlib.compress(to_bytes(con.dump()))
+
+ @contract(data='bytes')
+ def loads(self, data):
+ """Deserialize data from :meth:`dumps`
+
+ Use with a newly-created empty :class:`CoverageData` object. It's
+ undefined what happens if the object already has data in it.
+
+ Arguments:
+ data: A byte string of serialized data produced by :meth:`dumps`.
+
+ .. versionadded:: 5.0
+
+ """
+ if self._debug.should('dataio'):
+ self._debug.write("Loading data into data file {!r}".format(self._filename))
+ if data[:1] != b'z':
+ raise CoverageException(
+ "Unrecognized serialization: {!r} (head of {} bytes)".format(data[:40], len(data))
+ )
+ script = to_string(zlib.decompress(data[1:]))
+ self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
+ with db:
+ db.executescript(script)
+ self._read_db()
+ self._have_used = True
+
+ def _file_id(self, filename, add=False):
+ """Get the file id for `filename`.
+
+ If filename is not in the database yet, add it if `add` is True.
+ If `add` is not True, return None.
+ """
+ if filename not in self._file_map:
+ if add:
+ with self._connect() as con:
+ cur = con.execute("insert or replace into file (path) values (?)", (filename,))
+ self._file_map[filename] = cur.lastrowid
+ return self._file_map.get(filename)
+
+ def _context_id(self, context):
+ """Get the id for a context."""
+ assert context is not None
+ self._start_using()
+ with self._connect() as con:
+ row = con.execute_one("select id from context where context = ?", (context,))
+ if row is not None:
+ return row[0]
+ else:
+ return None
+
+ def set_context(self, context):
+ """Set the current context for future :meth:`add_lines` etc.
+
+ `context` is a str, the name of the context to use for the next data
+ additions. The context persists until the next :meth:`set_context`.
+
+ .. versionadded:: 5.0
+
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Setting context: %r" % (context,))
+ self._current_context = context
+ self._current_context_id = None
+
+ def _set_context_id(self):
+ """Use the _current_context to set _current_context_id."""
+ context = self._current_context or ""
+ context_id = self._context_id(context)
+ if context_id is not None:
+ self._current_context_id = context_id
+ else:
+ with self._connect() as con:
+ cur = con.execute("insert into context (context) values (?)", (context,))
+ self._current_context_id = cur.lastrowid
+
+ def base_filename(self):
+ """The base filename for storing data.
+
+ .. versionadded:: 5.0
+
+ """
+ return self._basename
+
+ def data_filename(self):
+ """Where is the data stored?
+
+ .. versionadded:: 5.0
+
+ """
+ return self._filename
+
+ def add_lines(self, line_data):
+ """Add measured line data.
+
+ `line_data` is a dictionary mapping file names to dictionaries::
+
+ { filename: { lineno: None, ... }, ...}
+
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Adding lines: %d files, %d lines total" % (
+ len(line_data), sum(len(lines) for lines in line_data.values())
+ ))
+ self._start_using()
+ self._choose_lines_or_arcs(lines=True)
+ if not line_data:
+ return
+ with self._connect() as con:
+ self._set_context_id()
+ for filename, linenos in iitems(line_data):
+ linemap = nums_to_numbits(linenos)
+ file_id = self._file_id(filename, add=True)
+ query = "select numbits from line_bits where file_id = ? and context_id = ?"
+ existing = list(con.execute(query, (file_id, self._current_context_id)))
+ if existing:
+ linemap = numbits_union(linemap, existing[0][0])
+
+ con.execute(
+ "insert or replace into line_bits "
+ " (file_id, context_id, numbits) values (?, ?, ?)",
+ (file_id, self._current_context_id, linemap),
+ )
+
+ def add_arcs(self, arc_data):
+ """Add measured arc data.
+
+ `arc_data` is a dictionary mapping file names to dictionaries::
+
+ { filename: { (l1,l2): None, ... }, ...}
+
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Adding arcs: %d files, %d arcs total" % (
+ len(arc_data), sum(len(arcs) for arcs in arc_data.values())
+ ))
+ self._start_using()
+ self._choose_lines_or_arcs(arcs=True)
+ if not arc_data:
+ return
+ with self._connect() as con:
+ self._set_context_id()
+ for filename, arcs in iitems(arc_data):
+ file_id = self._file_id(filename, add=True)
+ data = [(file_id, self._current_context_id, fromno, tono) for fromno, tono in arcs]
+ con.executemany(
+ "insert or ignore into arc "
+ "(file_id, context_id, fromno, tono) values (?, ?, ?, ?)",
+ data,
+ )
+
+ def _choose_lines_or_arcs(self, lines=False, arcs=False):
+ """Force the data file to choose between lines and arcs."""
+ assert lines or arcs
+ assert not (lines and arcs)
+ if lines and self._has_arcs:
+ raise CoverageException("Can't add line measurements to existing branch data")
+ if arcs and self._has_lines:
+ raise CoverageException("Can't add branch measurements to existing line data")
+ if not self._has_arcs and not self._has_lines:
+ self._has_lines = lines
+ self._has_arcs = arcs
+ with self._connect() as con:
+ con.execute(
+ "insert into meta (key, value) values (?, ?)",
+ ('has_arcs', str(int(arcs)))
+ )
+
+ def add_file_tracers(self, file_tracers):
+ """Add per-file plugin information.
+
+ `file_tracers` is { filename: plugin_name, ... }
+
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Adding file tracers: %d files" % (len(file_tracers),))
+ if not file_tracers:
+ return
+ self._start_using()
+ with self._connect() as con:
+ for filename, plugin_name in iitems(file_tracers):
+ file_id = self._file_id(filename)
+ if file_id is None:
+ raise CoverageException(
+ "Can't add file tracer data for unmeasured file '%s'" % (filename,)
+ )
+
+ existing_plugin = self.file_tracer(filename)
+ if existing_plugin:
+ if existing_plugin != plugin_name:
+ raise CoverageException(
+ "Conflicting file tracer name for '%s': %r vs %r" % (
+ filename, existing_plugin, plugin_name,
+ )
+ )
+ elif plugin_name:
+ con.execute(
+ "insert into tracer (file_id, tracer) values (?, ?)",
+ (file_id, plugin_name)
+ )
+
+ def touch_file(self, filename, plugin_name=""):
+ """Ensure that `filename` appears in the data, empty if needed.
+
+ `plugin_name` is the name of the plugin responsible for this file. It is used
+ to associate the right filereporter, etc.
+ """
+ self.touch_files([filename], plugin_name)
+
+ def touch_files(self, filenames, plugin_name=""):
+ """Ensure that `filenames` appear in the data, empty if needed.
+
+ `plugin_name` is the name of the plugin responsible for these files. It is used
+ to associate the right filereporter, etc.
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Touching %r" % (filenames,))
+ self._start_using()
+ with self._connect(): # Use this to get one transaction.
+ if not self._has_arcs and not self._has_lines:
+ raise CoverageException("Can't touch files in an empty CoverageData")
+
+ for filename in filenames:
+ self._file_id(filename, add=True)
+ if plugin_name:
+ # Set the tracer for this file
+ self.add_file_tracers({filename: plugin_name})
+
+ def update(self, other_data, aliases=None):
+ """Update this data with data from several other :class:`CoverageData` instances.
+
+ If `aliases` is provided, it's a `PathAliases` object that is used to
+ re-map paths to match the local machine's.
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Updating with data from %r" % (
+ getattr(other_data, '_filename', '???'),
+ ))
+ if self._has_lines and other_data._has_arcs:
+ raise CoverageException("Can't combine arc data with line data")
+ if self._has_arcs and other_data._has_lines:
+ raise CoverageException("Can't combine line data with arc data")
+
+ aliases = aliases or PathAliases()
+
+ # Force the database we're writing to to exist before we start nesting
+ # contexts.
+ self._start_using()
+
+ # Collector for all arcs, lines and tracers
+ other_data.read()
+ with other_data._connect() as conn:
+ # Get files data.
+ cur = conn.execute('select path from file')
+ files = {path: aliases.map(path) for (path,) in cur}
+ cur.close()
+
+ # Get contexts data.
+ cur = conn.execute('select context from context')
+ contexts = [context for (context,) in cur]
+ cur.close()
+
+ # Get arc data.
+ cur = conn.execute(
+ 'select file.path, context.context, arc.fromno, arc.tono '
+ 'from arc '
+ 'inner join file on file.id = arc.file_id '
+ 'inner join context on context.id = arc.context_id'
+ )
+ arcs = [(files[path], context, fromno, tono) for (path, context, fromno, tono) in cur]
+ cur.close()
+
+ # Get line data.
+ cur = conn.execute(
+ 'select file.path, context.context, line_bits.numbits '
+ 'from line_bits '
+ 'inner join file on file.id = line_bits.file_id '
+ 'inner join context on context.id = line_bits.context_id'
+ )
+ lines = {
+ (files[path], context): numbits
+ for (path, context, numbits) in cur
+ }
+ cur.close()
+
+ # Get tracer data.
+ cur = conn.execute(
+ 'select file.path, tracer '
+ 'from tracer '
+ 'inner join file on file.id = tracer.file_id'
+ )
+ tracers = {files[path]: tracer for (path, tracer) in cur}
+ cur.close()
+
+ with self._connect() as conn:
+ conn.con.isolation_level = 'IMMEDIATE'
+
+ # Get all tracers in the DB. Files not in the tracers are assumed
+ # to have an empty string tracer. Since Sqlite does not support
+ # full outer joins, we have to make two queries to fill the
+ # dictionary.
+ this_tracers = {path: '' for path, in conn.execute('select path from file')}
+ this_tracers.update({
+ aliases.map(path): tracer
+ for path, tracer in conn.execute(
+ 'select file.path, tracer from tracer '
+ 'inner join file on file.id = tracer.file_id'
+ )
+ })
+
+ # Create all file and context rows in the DB.
+ conn.executemany(
+ 'insert or ignore into file (path) values (?)',
+ ((file,) for file in files.values())
+ )
+ file_ids = {
+ path: id
+ for id, path in conn.execute('select id, path from file')
+ }
+ conn.executemany(
+ 'insert or ignore into context (context) values (?)',
+ ((context,) for context in contexts)
+ )
+ context_ids = {
+ context: id
+ for id, context in conn.execute('select id, context from context')
+ }
+
+ # Prepare tracers and fail, if a conflict is found.
+ # tracer_paths is used to ensure consistency over the tracer data
+ # and tracer_map tracks the tracers to be inserted.
+ tracer_map = {}
+ for path in files.values():
+ this_tracer = this_tracers.get(path)
+ other_tracer = tracers.get(path, '')
+ # If there is no tracer, there is always the None tracer.
+ if this_tracer is not None and this_tracer != other_tracer:
+ raise CoverageException(
+ "Conflicting file tracer name for '%s': %r vs %r" % (
+ path, this_tracer, other_tracer
+ )
+ )
+ tracer_map[path] = other_tracer
+
+ # Prepare arc and line rows to be inserted by converting the file
+ # and context strings with integer ids. Then use the efficient
+ # `executemany()` to insert all rows at once.
+ arc_rows = (
+ (file_ids[file], context_ids[context], fromno, tono)
+ for file, context, fromno, tono in arcs
+ )
+
+ # Get line data.
+ cur = conn.execute(
+ 'select file.path, context.context, line_bits.numbits '
+ 'from line_bits '
+ 'inner join file on file.id = line_bits.file_id '
+ 'inner join context on context.id = line_bits.context_id'
+ )
+ for path, context, numbits in cur:
+ key = (aliases.map(path), context)
+ if key in lines:
+ numbits = numbits_union(lines[key], numbits)
+ lines[key] = numbits
+ cur.close()
+
+ if arcs:
+ self._choose_lines_or_arcs(arcs=True)
+
+ # Write the combined data.
+ conn.executemany(
+ 'insert or ignore into arc '
+ '(file_id, context_id, fromno, tono) values (?, ?, ?, ?)',
+ arc_rows
+ )
+
+ if lines:
+ self._choose_lines_or_arcs(lines=True)
+ conn.execute("delete from line_bits")
+ conn.executemany(
+ "insert into line_bits "
+ "(file_id, context_id, numbits) values (?, ?, ?)",
+ [
+ (file_ids[file], context_ids[context], numbits)
+ for (file, context), numbits in lines.items()
+ ]
+ )
+ conn.executemany(
+ 'insert or ignore into tracer (file_id, tracer) values (?, ?)',
+ ((file_ids[filename], tracer) for filename, tracer in tracer_map.items())
+ )
+
+ # Update all internal cache data.
+ self._reset()
+ self.read()
+
+ def erase(self, parallel=False):
+ """Erase the data in this object.
+
+ If `parallel` is true, then also deletes data files created from the
+ basename by parallel-mode.
+
+ """
+ self._reset()
+ if self._no_disk:
+ return
+ if self._debug.should('dataio'):
+ self._debug.write("Erasing data file {!r}".format(self._filename))
+ file_be_gone(self._filename)
+ if parallel:
+ data_dir, local = os.path.split(self._filename)
+ localdot = local + '.*'
+ pattern = os.path.join(os.path.abspath(data_dir), localdot)
+ for filename in glob.glob(pattern):
+ if self._debug.should('dataio'):
+ self._debug.write("Erasing parallel data file {!r}".format(filename))
+ file_be_gone(filename)
+
+ def read(self):
+ """Start using an existing data file."""
+ with self._connect(): # TODO: doesn't look right
+ self._have_used = True
+
+ def write(self):
+ """Ensure the data is written to the data file."""
+ pass
+
+ def _start_using(self):
+ """Call this before using the database at all."""
+ if self._pid != os.getpid():
+ # Looks like we forked! Have to start a new data file.
+ self._reset()
+ self._choose_filename()
+ self._pid = os.getpid()
+ if not self._have_used:
+ self.erase()
+ self._have_used = True
+
+ def has_arcs(self):
+ """Does the database have arcs (True) or lines (False)."""
+ return bool(self._has_arcs)
+
+ def measured_files(self):
+ """A set of all files that had been measured."""
+ return set(self._file_map)
+
+ def measured_contexts(self):
+ """A set of all contexts that have been measured.
+
+ .. versionadded:: 5.0
+
+ """
+ self._start_using()
+ with self._connect() as con:
+ contexts = {row[0] for row in con.execute("select distinct(context) from context")}
+ return contexts
+
+ def file_tracer(self, filename):
+ """Get the plugin name of the file tracer for a file.
+
+ Returns the name of the plugin that handles this file. If the file was
+ measured, but didn't use a plugin, then "" is returned. If the file
+ was not measured, then None is returned.
+
+ """
+ self._start_using()
+ with self._connect() as con:
+ file_id = self._file_id(filename)
+ if file_id is None:
+ return None
+ row = con.execute_one("select tracer from tracer where file_id = ?", (file_id,))
+ if row is not None:
+ return row[0] or ""
+ return "" # File was measured, but no tracer associated.
+
+ def set_query_context(self, context):
+ """Set a context for subsequent querying.
+
+ The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno`
+ calls will be limited to only one context. `context` is a string which
+ must match a context exactly. If it does not, no exception is raised,
+ but queries will return no data.
+
+ .. versionadded:: 5.0
+
+ """
+ self._start_using()
+ with self._connect() as con:
+ cur = con.execute("select id from context where context = ?", (context,))
+ self._query_context_ids = [row[0] for row in cur.fetchall()]
+
+ def set_query_contexts(self, contexts):
+ """Set a number of contexts for subsequent querying.
+
+ The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno`
+ calls will be limited to the specified contexts. `contexts` is a list
+ of Python regular expressions. Contexts will be matched using
+ :func:`re.search <python:re.search>`. Data will be included in query
+ results if they are part of any of the contexts matched.
+
+ .. versionadded:: 5.0
+
+ """
+ self._start_using()
+ if contexts:
+ with self._connect() as con:
+ context_clause = ' or '.join(['context regexp ?'] * len(contexts))
+ cur = con.execute("select id from context where " + context_clause, contexts)
+ self._query_context_ids = [row[0] for row in cur.fetchall()]
+ else:
+ self._query_context_ids = None
+
+ def lines(self, filename):
+ """Get the list of lines executed for a file.
+
+ If the file was not measured, returns None. A file might be measured,
+ and have no lines executed, in which case an empty list is returned.
+
+ If the file was executed, returns a list of integers, the line numbers
+ executed in the file. The list is in no particular order.
+
+ """
+ self._start_using()
+ if self.has_arcs():
+ arcs = self.arcs(filename)
+ if arcs is not None:
+ all_lines = itertools.chain.from_iterable(arcs)
+ return list({l for l in all_lines if l > 0})
+
+ with self._connect() as con:
+ file_id = self._file_id(filename)
+ if file_id is None:
+ return None
+ else:
+ query = "select numbits from line_bits where file_id = ?"
+ data = [file_id]
+ if self._query_context_ids is not None:
+ ids_array = ', '.join('?' * len(self._query_context_ids))
+ query += " and context_id in (" + ids_array + ")"
+ data += self._query_context_ids
+ bitmaps = list(con.execute(query, data))
+ nums = set()
+ for row in bitmaps:
+ nums.update(numbits_to_nums(row[0]))
+ return list(nums)
+
+ def arcs(self, filename):
+ """Get the list of arcs executed for a file.
+
+ If the file was not measured, returns None. A file might be measured,
+ and have no arcs executed, in which case an empty list is returned.
+
+ If the file was executed, returns a list of 2-tuples of integers. Each
+ pair is a starting line number and an ending line number for a
+ transition from one line to another. The list is in no particular
+ order.
+
+ Negative numbers have special meaning. If the starting line number is
+ -N, it represents an entry to the code object that starts at line N.
+ If the ending ling number is -N, it's an exit from the code object that
+ starts at line N.
+
+ """
+ self._start_using()
+ with self._connect() as con:
+ file_id = self._file_id(filename)
+ if file_id is None:
+ return None
+ else:
+ query = "select distinct fromno, tono from arc where file_id = ?"
+ data = [file_id]
+ if self._query_context_ids is not None:
+ ids_array = ', '.join('?' * len(self._query_context_ids))
+ query += " and context_id in (" + ids_array + ")"
+ data += self._query_context_ids
+ arcs = con.execute(query, data)
+ return list(arcs)
+
+ def contexts_by_lineno(self, filename):
+ """Get the contexts for each line in a file.
+
+ Returns:
+ A dict mapping line numbers to a list of context names.
+
+ .. versionadded:: 5.0
+
+ """
+ lineno_contexts_map = collections.defaultdict(list)
+ self._start_using()
+ with self._connect() as con:
+ file_id = self._file_id(filename)
+ if file_id is None:
+ return lineno_contexts_map
+ if self.has_arcs():
+ query = (
+ "select arc.fromno, arc.tono, context.context "
+ "from arc, context "
+ "where arc.file_id = ? and arc.context_id = context.id"
+ )
+ data = [file_id]
+ if self._query_context_ids is not None:
+ ids_array = ', '.join('?' * len(self._query_context_ids))
+ query += " and arc.context_id in (" + ids_array + ")"
+ data += self._query_context_ids
+ for fromno, tono, context in con.execute(query, data):
+ if context not in lineno_contexts_map[fromno]:
+ lineno_contexts_map[fromno].append(context)
+ if context not in lineno_contexts_map[tono]:
+ lineno_contexts_map[tono].append(context)
+ else:
+ query = (
+ "select l.numbits, c.context from line_bits l, context c "
+ "where l.context_id = c.id "
+ "and file_id = ?"
+ )
+ data = [file_id]
+ if self._query_context_ids is not None:
+ ids_array = ', '.join('?' * len(self._query_context_ids))
+ query += " and l.context_id in (" + ids_array + ")"
+ data += self._query_context_ids
+ for numbits, context in con.execute(query, data):
+ for lineno in numbits_to_nums(numbits):
+ lineno_contexts_map[lineno].append(context)
+ return lineno_contexts_map
+
+ @classmethod
+ def sys_info(cls):
+ """Our information for `Coverage.sys_info`.
+
+ Returns a list of (key, value) pairs.
+
+ """
+ with SqliteDb(":memory:", debug=NoDebugging()) as db:
+ temp_store = [row[0] for row in db.execute("pragma temp_store")]
+ compile_options = [row[0] for row in db.execute("pragma compile_options")]
+
+ return [
+ ('sqlite3_version', sqlite3.version),
+ ('sqlite3_sqlite_version', sqlite3.sqlite_version),
+ ('sqlite3_temp_store', temp_store),
+ ('sqlite3_compile_options', compile_options),
+ ]
+
+
+class SqliteDb(SimpleReprMixin):
+ """A simple abstraction over a SQLite database.
+
+ Use as a context manager, then you can use it like a
+ :class:`python:sqlite3.Connection` object::
+
+ with SqliteDb(filename, debug_control) as db:
+ db.execute("insert into schema (version) values (?)", (SCHEMA_VERSION,))
+
+ """
+ def __init__(self, filename, debug):
+ self.debug = debug if debug.should('sql') else None
+ self.filename = filename
+ self.nest = 0
+ self.con = None
+
+ def _connect(self):
+ """Connect to the db and do universal initialization."""
+ if self.con is not None:
+ return
+
+ # SQLite on Windows on py2 won't open a file if the filename argument
+ # has non-ascii characters in it. Opening a relative file name avoids
+ # a problem if the current directory has non-ascii.
+ filename = self.filename
+ if env.WINDOWS and env.PY2:
+ try:
+ filename = os.path.relpath(self.filename)
+ except ValueError:
+ # ValueError can be raised under Windows when os.getcwd() returns a
+ # folder from a different drive than the drive of self.filename in
+ # which case we keep the original value of self.filename unchanged,
+ # hoping that we won't face the non-ascii directory problem.
+ pass
+
+ # It can happen that Python switches threads while the tracer writes
+ # data. The second thread will also try to write to the data,
+ # effectively causing a nested context. However, given the idempotent
+ # nature of the tracer operations, sharing a connection among threads
+ # is not a problem.
+ if self.debug:
+ self.debug.write("Connecting to {!r}".format(self.filename))
+ self.con = sqlite3.connect(filename, check_same_thread=False)
+ self.con.create_function('REGEXP', 2, _regexp)
+
+ # This pragma makes writing faster. It disables rollbacks, but we never need them.
+ # PyPy needs the .close() calls here, or sqlite gets twisted up:
+ # https://bitbucket.org/pypy/pypy/issues/2872/default-isolation-mode-is-different-on
+ self.execute("pragma journal_mode=off").close()
+ # This pragma makes writing faster.
+ self.execute("pragma synchronous=off").close()
+
+ def close(self):
+ """If needed, close the connection."""
+ if self.con is not None and self.filename != ":memory:":
+ self.con.close()
+ self.con = None
+
+ def __enter__(self):
+ if self.nest == 0:
+ self._connect()
+ self.con.__enter__()
+ self.nest += 1
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.nest -= 1
+ if self.nest == 0:
+ try:
+ self.con.__exit__(exc_type, exc_value, traceback)
+ self.close()
+ except Exception as exc:
+ if self.debug:
+ self.debug.write("EXCEPTION from __exit__: {}".format(exc))
+ raise
+
+ def execute(self, sql, parameters=()):
+ """Same as :meth:`python:sqlite3.Connection.execute`."""
+ if self.debug:
+ tail = " with {!r}".format(parameters) if parameters else ""
+ self.debug.write("Executing {!r}{}".format(sql, tail))
+ try:
+ try:
+ return self.con.execute(sql, parameters)
+ except Exception:
+ # In some cases, an error might happen that isn't really an
+ # error. Try again immediately.
+ # https://github.com/nedbat/coveragepy/issues/1010
+ return self.con.execute(sql, parameters)
+ except sqlite3.Error as exc:
+ msg = str(exc)
+ try:
+ # `execute` is the first thing we do with the database, so try
+ # hard to provide useful hints if something goes wrong now.
+ with open(self.filename, "rb") as bad_file:
+ cov4_sig = b"!coverage.py: This is a private format"
+ if bad_file.read(len(cov4_sig)) == cov4_sig:
+ msg = (
+ "Looks like a coverage 4.x data file. "
+ "Are you mixing versions of coverage?"
+ )
+ except Exception:
+ pass
+ if self.debug:
+ self.debug.write("EXCEPTION from execute: {}".format(msg))
+ raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, msg))
+
+ def execute_one(self, sql, parameters=()):
+ """Execute a statement and return the one row that results.
+
+ This is like execute(sql, parameters).fetchone(), except it is
+ correct in reading the entire result set. This will raise an
+ exception if more than one row results.
+
+ Returns a row, or None if there were no rows.
+ """
+ rows = list(self.execute(sql, parameters))
+ if len(rows) == 0:
+ return None
+ elif len(rows) == 1:
+ return rows[0]
+ else:
+ raise CoverageException("Sql {!r} shouldn't return {} rows".format(sql, len(rows)))
+
+ def executemany(self, sql, data):
+ """Same as :meth:`python:sqlite3.Connection.executemany`."""
+ if self.debug:
+ data = list(data)
+ self.debug.write("Executing many {!r} with {} rows".format(sql, len(data)))
+ return self.con.executemany(sql, data)
+
+ def executescript(self, script):
+ """Same as :meth:`python:sqlite3.Connection.executescript`."""
+ if self.debug:
+ self.debug.write("Executing script with {} chars: {}".format(
+ len(script), clipped_repr(script, 100),
+ ))
+ self.con.executescript(script)
+
+ def dump(self):
+ """Return a multi-line string, the SQL dump of the database."""
+ return "\n".join(self.con.iterdump())
+
+
+def _regexp(text, pattern):
+ """A regexp function for SQLite."""
+ return re.search(text, pattern) is not None
diff --git a/contrib/python/coverage/py2/coverage/summary.py b/contrib/python/coverage/py2/coverage/summary.py
new file mode 100644
index 0000000000..65f8047006
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/summary.py
@@ -0,0 +1,152 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Summary reporting"""
+
+import sys
+
+from coverage import env
+from coverage.report import get_analysis_to_report
+from coverage.results import Numbers
+from coverage.misc import CoverageException, output_encoding
+
+
+class SummaryReporter(object):
+ """A reporter for writing the summary report."""
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+ self.branches = coverage.get_data().has_arcs()
+ self.outfile = None
+ self.fr_analysis = []
+ self.skipped_count = 0
+ self.empty_count = 0
+ self.total = Numbers()
+ self.fmt_err = u"%s %s: %s"
+
+ def writeout(self, line):
+ """Write a line to the output, adding a newline."""
+ if env.PY2:
+ line = line.encode(output_encoding())
+ self.outfile.write(line.rstrip())
+ self.outfile.write("\n")
+
+ def report(self, morfs, outfile=None):
+ """Writes a report summarizing coverage statistics per module.
+
+ `outfile` is a file object to write the summary to. It must be opened
+ for native strings (bytes on Python 2, Unicode on Python 3).
+
+ """
+ self.outfile = outfile or sys.stdout
+
+ self.coverage.get_data().set_query_contexts(self.config.report_contexts)
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
+ self.report_one_file(fr, analysis)
+
+ # Prepare the formatting strings, header, and column sorting.
+ max_name = max([len(fr.relative_filename()) for (fr, analysis) in self.fr_analysis] + [5])
+ fmt_name = u"%%- %ds " % max_name
+ fmt_skip_covered = u"\n%s file%s skipped due to complete coverage."
+ fmt_skip_empty = u"\n%s empty file%s skipped."
+
+ header = (fmt_name % "Name") + u" Stmts Miss"
+ fmt_coverage = fmt_name + u"%6d %6d"
+ if self.branches:
+ header += u" Branch BrPart"
+ fmt_coverage += u" %6d %6d"
+ width100 = Numbers.pc_str_width()
+ header += u"%*s" % (width100+4, "Cover")
+ fmt_coverage += u"%%%ds%%%%" % (width100+3,)
+ if self.config.show_missing:
+ header += u" Missing"
+ fmt_coverage += u" %s"
+ rule = u"-" * len(header)
+
+ column_order = dict(name=0, stmts=1, miss=2, cover=-1)
+ if self.branches:
+ column_order.update(dict(branch=3, brpart=4))
+
+ # Write the header
+ self.writeout(header)
+ self.writeout(rule)
+
+ # `lines` is a list of pairs, (line text, line values). The line text
+ # is a string that will be printed, and line values is a tuple of
+ # sortable values.
+ lines = []
+
+ for (fr, analysis) in self.fr_analysis:
+ nums = analysis.numbers
+
+ args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
+ if self.branches:
+ args += (nums.n_branches, nums.n_partial_branches)
+ args += (nums.pc_covered_str,)
+ if self.config.show_missing:
+ args += (analysis.missing_formatted(branches=True),)
+ text = fmt_coverage % args
+ # Add numeric percent coverage so that sorting makes sense.
+ args += (nums.pc_covered,)
+ lines.append((text, args))
+
+ # Sort the lines and write them out.
+ if getattr(self.config, 'sort', None):
+ sort_option = self.config.sort.lower()
+ reverse = False
+ if sort_option[0] == '-':
+ reverse = True
+ sort_option = sort_option[1:]
+ elif sort_option[0] == '+':
+ sort_option = sort_option[1:]
+
+ position = column_order.get(sort_option)
+ if position is None:
+ raise CoverageException("Invalid sorting option: {!r}".format(self.config.sort))
+ lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse)
+
+ for line in lines:
+ self.writeout(line[0])
+
+ # Write a TOTAL line if we had at least one file.
+ if self.total.n_files > 0:
+ self.writeout(rule)
+ args = ("TOTAL", self.total.n_statements, self.total.n_missing)
+ if self.branches:
+ args += (self.total.n_branches, self.total.n_partial_branches)
+ args += (self.total.pc_covered_str,)
+ if self.config.show_missing:
+ args += ("",)
+ self.writeout(fmt_coverage % args)
+
+ # Write other final lines.
+ if not self.total.n_files and not self.skipped_count:
+ raise CoverageException("No data to report.")
+
+ if self.config.skip_covered and self.skipped_count:
+ self.writeout(
+ fmt_skip_covered % (self.skipped_count, 's' if self.skipped_count > 1 else '')
+ )
+ if self.config.skip_empty and self.empty_count:
+ self.writeout(
+ fmt_skip_empty % (self.empty_count, 's' if self.empty_count > 1 else '')
+ )
+
+ return self.total.n_statements and self.total.pc_covered
+
+ def report_one_file(self, fr, analysis):
+ """Report on just one file, the callback from report()."""
+ nums = analysis.numbers
+ self.total += nums
+
+ no_missing_lines = (nums.n_missing == 0)
+ no_missing_branches = (nums.n_partial_branches == 0)
+ if self.config.skip_covered and no_missing_lines and no_missing_branches:
+ # Don't report on 100% files.
+ self.skipped_count += 1
+ elif self.config.skip_empty and nums.n_statements == 0:
+ # Don't report on empty files.
+ self.empty_count += 1
+ else:
+ self.fr_analysis.append((fr, analysis))
diff --git a/contrib/python/coverage/py2/coverage/templite.py b/contrib/python/coverage/py2/coverage/templite.py
new file mode 100644
index 0000000000..7d4024e0af
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/templite.py
@@ -0,0 +1,302 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""A simple Python template renderer, for a nano-subset of Django syntax.
+
+For a detailed discussion of this code, see this chapter from 500 Lines:
+http://aosabook.org/en/500L/a-template-engine.html
+
+"""
+
+# Coincidentally named the same as http://code.activestate.com/recipes/496702/
+
+import re
+
+from coverage import env
+
+
+class TempliteSyntaxError(ValueError):
+ """Raised when a template has a syntax error."""
+ pass
+
+
+class TempliteValueError(ValueError):
+ """Raised when an expression won't evaluate in a template."""
+ pass
+
+
+class CodeBuilder(object):
+ """Build source code conveniently."""
+
+ def __init__(self, indent=0):
+ self.code = []
+ self.indent_level = indent
+
+ def __str__(self):
+ return "".join(str(c) for c in self.code)
+
+ def add_line(self, line):
+ """Add a line of source to the code.
+
+ Indentation and newline will be added for you, don't provide them.
+
+ """
+ self.code.extend([" " * self.indent_level, line, "\n"])
+
+ def add_section(self):
+ """Add a section, a sub-CodeBuilder."""
+ section = CodeBuilder(self.indent_level)
+ self.code.append(section)
+ return section
+
+ INDENT_STEP = 4 # PEP8 says so!
+
+ def indent(self):
+ """Increase the current indent for following lines."""
+ self.indent_level += self.INDENT_STEP
+
+ def dedent(self):
+ """Decrease the current indent for following lines."""
+ self.indent_level -= self.INDENT_STEP
+
+ def get_globals(self):
+ """Execute the code, and return a dict of globals it defines."""
+ # A check that the caller really finished all the blocks they started.
+ assert self.indent_level == 0
+ # Get the Python source as a single string.
+ python_source = str(self)
+ # Execute the source, defining globals, and return them.
+ global_namespace = {}
+ exec(python_source, global_namespace)
+ return global_namespace
+
+
+class Templite(object):
+ """A simple template renderer, for a nano-subset of Django syntax.
+
+ Supported constructs are extended variable access::
+
+ {{var.modifier.modifier|filter|filter}}
+
+ loops::
+
+ {% for var in list %}...{% endfor %}
+
+ and ifs::
+
+ {% if var %}...{% endif %}
+
+ Comments are within curly-hash markers::
+
+ {# This will be ignored #}
+
+ Lines between `{% joined %}` and `{% endjoined %}` will have lines stripped
+ and joined. Be careful, this could join words together!
+
+ Any of these constructs can have a hyphen at the end (`-}}`, `-%}`, `-#}`),
+ which will collapse the whitespace following the tag.
+
+ Construct a Templite with the template text, then use `render` against a
+ dictionary context to create a finished string::
+
+ templite = Templite('''
+ <h1>Hello {{name|upper}}!</h1>
+ {% for topic in topics %}
+ <p>You are interested in {{topic}}.</p>
+ {% endif %}
+ ''',
+ {'upper': str.upper},
+ )
+ text = templite.render({
+ 'name': "Ned",
+ 'topics': ['Python', 'Geometry', 'Juggling'],
+ })
+
+ """
+ def __init__(self, text, *contexts):
+ """Construct a Templite with the given `text`.
+
+ `contexts` are dictionaries of values to use for future renderings.
+ These are good for filters and global values.
+
+ """
+ self.context = {}
+ for context in contexts:
+ self.context.update(context)
+
+ self.all_vars = set()
+ self.loop_vars = set()
+
+ # We construct a function in source form, then compile it and hold onto
+ # it, and execute it to render the template.
+ code = CodeBuilder()
+
+ code.add_line("def render_function(context, do_dots):")
+ code.indent()
+ vars_code = code.add_section()
+ code.add_line("result = []")
+ code.add_line("append_result = result.append")
+ code.add_line("extend_result = result.extend")
+ if env.PY2:
+ code.add_line("to_str = unicode")
+ else:
+ code.add_line("to_str = str")
+
+ buffered = []
+
+ def flush_output():
+ """Force `buffered` to the code builder."""
+ if len(buffered) == 1:
+ code.add_line("append_result(%s)" % buffered[0])
+ elif len(buffered) > 1:
+ code.add_line("extend_result([%s])" % ", ".join(buffered))
+ del buffered[:]
+
+ ops_stack = []
+
+ # Split the text to form a list of tokens.
+ tokens = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
+
+ squash = in_joined = False
+
+ for token in tokens:
+ if token.startswith('{'):
+ start, end = 2, -2
+ squash = (token[-3] == '-')
+ if squash:
+ end = -3
+
+ if token.startswith('{#'):
+ # Comment: ignore it and move on.
+ continue
+ elif token.startswith('{{'):
+ # An expression to evaluate.
+ expr = self._expr_code(token[start:end].strip())
+ buffered.append("to_str(%s)" % expr)
+ else:
+ # token.startswith('{%')
+ # Action tag: split into words and parse further.
+ flush_output()
+
+ words = token[start:end].strip().split()
+ if words[0] == 'if':
+ # An if statement: evaluate the expression to determine if.
+ if len(words) != 2:
+ self._syntax_error("Don't understand if", token)
+ ops_stack.append('if')
+ code.add_line("if %s:" % self._expr_code(words[1]))
+ code.indent()
+ elif words[0] == 'for':
+ # A loop: iterate over expression result.
+ if len(words) != 4 or words[2] != 'in':
+ self._syntax_error("Don't understand for", token)
+ ops_stack.append('for')
+ self._variable(words[1], self.loop_vars)
+ code.add_line(
+ "for c_%s in %s:" % (
+ words[1],
+ self._expr_code(words[3])
+ )
+ )
+ code.indent()
+ elif words[0] == 'joined':
+ ops_stack.append('joined')
+ in_joined = True
+ elif words[0].startswith('end'):
+ # Endsomething. Pop the ops stack.
+ if len(words) != 1:
+ self._syntax_error("Don't understand end", token)
+ end_what = words[0][3:]
+ if not ops_stack:
+ self._syntax_error("Too many ends", token)
+ start_what = ops_stack.pop()
+ if start_what != end_what:
+ self._syntax_error("Mismatched end tag", end_what)
+ if end_what == 'joined':
+ in_joined = False
+ else:
+ code.dedent()
+ else:
+ self._syntax_error("Don't understand tag", words[0])
+ else:
+ # Literal content. If it isn't empty, output it.
+ if in_joined:
+ token = re.sub(r"\s*\n\s*", "", token.strip())
+ elif squash:
+ token = token.lstrip()
+ if token:
+ buffered.append(repr(token))
+
+ if ops_stack:
+ self._syntax_error("Unmatched action tag", ops_stack[-1])
+
+ flush_output()
+
+ for var_name in self.all_vars - self.loop_vars:
+ vars_code.add_line("c_%s = context[%r]" % (var_name, var_name))
+
+ code.add_line('return "".join(result)')
+ code.dedent()
+ self._render_function = code.get_globals()['render_function']
+
+ def _expr_code(self, expr):
+ """Generate a Python expression for `expr`."""
+ if "|" in expr:
+ pipes = expr.split("|")
+ code = self._expr_code(pipes[0])
+ for func in pipes[1:]:
+ self._variable(func, self.all_vars)
+ code = "c_%s(%s)" % (func, code)
+ elif "." in expr:
+ dots = expr.split(".")
+ code = self._expr_code(dots[0])
+ args = ", ".join(repr(d) for d in dots[1:])
+ code = "do_dots(%s, %s)" % (code, args)
+ else:
+ self._variable(expr, self.all_vars)
+ code = "c_%s" % expr
+ return code
+
+ def _syntax_error(self, msg, thing):
+ """Raise a syntax error using `msg`, and showing `thing`."""
+ raise TempliteSyntaxError("%s: %r" % (msg, thing))
+
+ def _variable(self, name, vars_set):
+ """Track that `name` is used as a variable.
+
+ Adds the name to `vars_set`, a set of variable names.
+
+ Raises an syntax error if `name` is not a valid name.
+
+ """
+ if not re.match(r"[_a-zA-Z][_a-zA-Z0-9]*$", name):
+ self._syntax_error("Not a valid name", name)
+ vars_set.add(name)
+
+ def render(self, context=None):
+ """Render this template by applying it to `context`.
+
+ `context` is a dictionary of values to use in this rendering.
+
+ """
+ # Make the complete context we'll use.
+ render_context = dict(self.context)
+ if context:
+ render_context.update(context)
+ return self._render_function(render_context, self._do_dots)
+
+ def _do_dots(self, value, *dots):
+ """Evaluate dotted expressions at run-time."""
+ for dot in dots:
+ try:
+ value = getattr(value, dot)
+ except AttributeError:
+ try:
+ value = value[dot]
+ except (TypeError, KeyError):
+ raise TempliteValueError(
+ "Couldn't evaluate %r.%s" % (value, dot)
+ )
+ if callable(value):
+ value = value()
+ return value
diff --git a/contrib/python/coverage/py2/coverage/tomlconfig.py b/contrib/python/coverage/py2/coverage/tomlconfig.py
new file mode 100644
index 0000000000..3ad581571c
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/tomlconfig.py
@@ -0,0 +1,168 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""TOML configuration support for coverage.py"""
+
+import io
+import os
+import re
+
+from coverage import env
+from coverage.backward import configparser, path_types
+from coverage.misc import CoverageException, substitute_variables
+
+# TOML support is an install-time extra option.
+try:
+ import toml
+except ImportError: # pragma: not covered
+ toml = None
+
+
+class TomlDecodeError(Exception):
+ """An exception class that exists even when toml isn't installed."""
+ pass
+
+
+class TomlConfigParser:
+ """TOML file reading with the interface of HandyConfigParser."""
+
+ # This class has the same interface as config.HandyConfigParser, no
+ # need for docstrings.
+ # pylint: disable=missing-function-docstring
+
+ def __init__(self, our_file):
+ self.our_file = our_file
+ self.data = None
+
+ def read(self, filenames):
+ # RawConfigParser takes a filename or list of filenames, but we only
+ # ever call this with a single filename.
+ assert isinstance(filenames, path_types)
+ filename = filenames
+ if env.PYVERSION >= (3, 6):
+ filename = os.fspath(filename)
+
+ try:
+ with io.open(filename, encoding='utf-8') as fp:
+ toml_text = fp.read()
+ except IOError:
+ return []
+ if toml:
+ toml_text = substitute_variables(toml_text, os.environ)
+ try:
+ self.data = toml.loads(toml_text)
+ except toml.TomlDecodeError as err:
+ raise TomlDecodeError(*err.args)
+ return [filename]
+ else:
+ has_toml = re.search(r"^\[tool\.coverage\.", toml_text, flags=re.MULTILINE)
+ if self.our_file or has_toml:
+ # Looks like they meant to read TOML, but we can't read it.
+ msg = "Can't read {!r} without TOML support. Install with [toml] extra"
+ raise CoverageException(msg.format(filename))
+ return []
+
+ def _get_section(self, section):
+ """Get a section from the data.
+
+ Arguments:
+ section (str): A section name, which can be dotted.
+
+ Returns:
+ name (str): the actual name of the section that was found, if any,
+ or None.
+ data (str): the dict of data in the section, or None if not found.
+
+ """
+ prefixes = ["tool.coverage."]
+ if self.our_file:
+ prefixes.append("")
+ for prefix in prefixes:
+ real_section = prefix + section
+ parts = real_section.split(".")
+ try:
+ data = self.data[parts[0]]
+ for part in parts[1:]:
+ data = data[part]
+ except KeyError:
+ continue
+ break
+ else:
+ return None, None
+ return real_section, data
+
+ def _get(self, section, option):
+ """Like .get, but returns the real section name and the value."""
+ name, data = self._get_section(section)
+ if data is None:
+ raise configparser.NoSectionError(section)
+ try:
+ return name, data[option]
+ except KeyError:
+ raise configparser.NoOptionError(option, name)
+
+ def has_option(self, section, option):
+ _, data = self._get_section(section)
+ if data is None:
+ return False
+ return option in data
+
+ def has_section(self, section):
+ name, _ = self._get_section(section)
+ return name
+
+ def options(self, section):
+ _, data = self._get_section(section)
+ if data is None:
+ raise configparser.NoSectionError(section)
+ return list(data.keys())
+
+ def get_section(self, section):
+ _, data = self._get_section(section)
+ return data
+
+ def get(self, section, option):
+ _, value = self._get(section, option)
+ return value
+
+ def _check_type(self, section, option, value, type_, type_desc):
+ if not isinstance(value, type_):
+ raise ValueError(
+ 'Option {!r} in section {!r} is not {}: {!r}'
+ .format(option, section, type_desc, value)
+ )
+
+ def getboolean(self, section, option):
+ name, value = self._get(section, option)
+ self._check_type(name, option, value, bool, "a boolean")
+ return value
+
+ def getlist(self, section, option):
+ name, values = self._get(section, option)
+ self._check_type(name, option, values, list, "a list")
+ return values
+
+ def getregexlist(self, section, option):
+ name, values = self._get(section, option)
+ self._check_type(name, option, values, list, "a list")
+ for value in values:
+ value = value.strip()
+ try:
+ re.compile(value)
+ except re.error as e:
+ raise CoverageException(
+ "Invalid [%s].%s value %r: %s" % (name, option, value, e)
+ )
+ return values
+
+ def getint(self, section, option):
+ name, value = self._get(section, option)
+ self._check_type(name, option, value, int, "an integer")
+ return value
+
+ def getfloat(self, section, option):
+ name, value = self._get(section, option)
+ if isinstance(value, int):
+ value = float(value)
+ self._check_type(name, option, value, float, "a float")
+ return value
diff --git a/contrib/python/coverage/py2/coverage/version.py b/contrib/python/coverage/py2/coverage/version.py
new file mode 100644
index 0000000000..d141a11da3
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/version.py
@@ -0,0 +1,33 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""The version and URL for coverage.py"""
+# This file is exec'ed in setup.py, don't import anything!
+
+# Same semantics as sys.version_info.
+version_info = (5, 5, 0, "final", 0)
+
+
+def _make_version(major, minor, micro, releaselevel, serial):
+ """Create a readable version string from version_info tuple components."""
+ assert releaselevel in ['alpha', 'beta', 'candidate', 'final']
+ version = "%d.%d" % (major, minor)
+ if micro:
+ version += ".%d" % (micro,)
+ if releaselevel != 'final':
+ short = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc'}[releaselevel]
+ version += "%s%d" % (short, serial)
+ return version
+
+
+def _make_url(major, minor, micro, releaselevel, serial):
+ """Make the URL people should start at for this version of coverage.py."""
+ url = "https://coverage.readthedocs.io"
+ if releaselevel != 'final':
+ # For pre-releases, use a version-specific URL.
+ url += "/en/coverage-" + _make_version(major, minor, micro, releaselevel, serial)
+ return url
+
+
+__version__ = _make_version(*version_info)
+__url__ = _make_url(*version_info)
diff --git a/contrib/python/coverage/py2/coverage/xmlreport.py b/contrib/python/coverage/py2/coverage/xmlreport.py
new file mode 100644
index 0000000000..6d012ee692
--- /dev/null
+++ b/contrib/python/coverage/py2/coverage/xmlreport.py
@@ -0,0 +1,234 @@
+# coding: utf-8
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""XML reporting for coverage.py"""
+
+import os
+import os.path
+import sys
+import time
+import xml.dom.minidom
+
+from coverage import env
+from coverage import __url__, __version__, files
+from coverage.backward import iitems
+from coverage.misc import isolate_module
+from coverage.report import get_analysis_to_report
+
+os = isolate_module(os)
+
+
+DTD_URL = 'https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd'
+
+
+def rate(hit, num):
+ """Return the fraction of `hit`/`num`, as a string."""
+ if num == 0:
+ return "1"
+ else:
+ return "%.4g" % (float(hit) / num)
+
+
+class XmlReporter(object):
+ """A reporter for writing Cobertura-style XML coverage results."""
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+
+ self.source_paths = set()
+ if self.config.source:
+ for src in self.config.source:
+ if os.path.exists(src):
+ if not self.config.relative_files:
+ src = files.canonical_filename(src)
+ self.source_paths.add(src)
+ self.packages = {}
+ self.xml_out = None
+
+ def report(self, morfs, outfile=None):
+ """Generate a Cobertura-compatible XML report for `morfs`.
+
+ `morfs` is a list of modules or file names.
+
+ `outfile` is a file object to write the XML to.
+
+ """
+ # Initial setup.
+ outfile = outfile or sys.stdout
+ has_arcs = self.coverage.get_data().has_arcs()
+
+ # Create the DOM that will store the data.
+ impl = xml.dom.minidom.getDOMImplementation()
+ self.xml_out = impl.createDocument(None, "coverage", None)
+
+ # Write header stuff.
+ xcoverage = self.xml_out.documentElement
+ xcoverage.setAttribute("version", __version__)
+ xcoverage.setAttribute("timestamp", str(int(time.time()*1000)))
+ xcoverage.appendChild(self.xml_out.createComment(
+ " Generated by coverage.py: %s " % __url__
+ ))
+ xcoverage.appendChild(self.xml_out.createComment(" Based on %s " % DTD_URL))
+
+ # Call xml_file for each file in the data.
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
+ self.xml_file(fr, analysis, has_arcs)
+
+ xsources = self.xml_out.createElement("sources")
+ xcoverage.appendChild(xsources)
+
+ # Populate the XML DOM with the source info.
+ for path in sorted(self.source_paths):
+ xsource = self.xml_out.createElement("source")
+ xsources.appendChild(xsource)
+ txt = self.xml_out.createTextNode(path)
+ xsource.appendChild(txt)
+
+ lnum_tot, lhits_tot = 0, 0
+ bnum_tot, bhits_tot = 0, 0
+
+ xpackages = self.xml_out.createElement("packages")
+ xcoverage.appendChild(xpackages)
+
+ # Populate the XML DOM with the package info.
+ for pkg_name, pkg_data in sorted(iitems(self.packages)):
+ class_elts, lhits, lnum, bhits, bnum = pkg_data
+ xpackage = self.xml_out.createElement("package")
+ xpackages.appendChild(xpackage)
+ xclasses = self.xml_out.createElement("classes")
+ xpackage.appendChild(xclasses)
+ for _, class_elt in sorted(iitems(class_elts)):
+ xclasses.appendChild(class_elt)
+ xpackage.setAttribute("name", pkg_name.replace(os.sep, '.'))
+ xpackage.setAttribute("line-rate", rate(lhits, lnum))
+ if has_arcs:
+ branch_rate = rate(bhits, bnum)
+ else:
+ branch_rate = "0"
+ xpackage.setAttribute("branch-rate", branch_rate)
+ xpackage.setAttribute("complexity", "0")
+
+ lnum_tot += lnum
+ lhits_tot += lhits
+ bnum_tot += bnum
+ bhits_tot += bhits
+
+ xcoverage.setAttribute("lines-valid", str(lnum_tot))
+ xcoverage.setAttribute("lines-covered", str(lhits_tot))
+ xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot))
+ if has_arcs:
+ xcoverage.setAttribute("branches-valid", str(bnum_tot))
+ xcoverage.setAttribute("branches-covered", str(bhits_tot))
+ xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot))
+ else:
+ xcoverage.setAttribute("branches-covered", "0")
+ xcoverage.setAttribute("branches-valid", "0")
+ xcoverage.setAttribute("branch-rate", "0")
+ xcoverage.setAttribute("complexity", "0")
+
+ # Write the output file.
+ outfile.write(serialize_xml(self.xml_out))
+
+ # Return the total percentage.
+ denom = lnum_tot + bnum_tot
+ if denom == 0:
+ pct = 0.0
+ else:
+ pct = 100.0 * (lhits_tot + bhits_tot) / denom
+ return pct
+
+ def xml_file(self, fr, analysis, has_arcs):
+ """Add to the XML report for a single file."""
+
+ if self.config.skip_empty:
+ if analysis.numbers.n_statements == 0:
+ return
+
+ # Create the 'lines' and 'package' XML elements, which
+ # are populated later. Note that a package == a directory.
+ filename = fr.filename.replace("\\", "/")
+ for source_path in self.source_paths:
+ source_path = files.canonical_filename(source_path)
+ if filename.startswith(source_path.replace("\\", "/") + "/"):
+ rel_name = filename[len(source_path)+1:]
+ break
+ else:
+ rel_name = fr.relative_filename()
+ self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/"))
+
+ dirname = os.path.dirname(rel_name) or u"."
+ dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth])
+ package_name = dirname.replace("/", ".")
+
+ package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
+
+ xclass = self.xml_out.createElement("class")
+
+ xclass.appendChild(self.xml_out.createElement("methods"))
+
+ xlines = self.xml_out.createElement("lines")
+ xclass.appendChild(xlines)
+
+ xclass.setAttribute("name", os.path.relpath(rel_name, dirname))
+ xclass.setAttribute("filename", rel_name.replace("\\", "/"))
+ xclass.setAttribute("complexity", "0")
+
+ branch_stats = analysis.branch_stats()
+ missing_branch_arcs = analysis.missing_branch_arcs()
+
+ # For each statement, create an XML 'line' element.
+ for line in sorted(analysis.statements):
+ xline = self.xml_out.createElement("line")
+ xline.setAttribute("number", str(line))
+
+ # Q: can we get info about the number of times a statement is
+ # executed? If so, that should be recorded here.
+ xline.setAttribute("hits", str(int(line not in analysis.missing)))
+
+ if has_arcs:
+ if line in branch_stats:
+ total, taken = branch_stats[line]
+ xline.setAttribute("branch", "true")
+ xline.setAttribute(
+ "condition-coverage",
+ "%d%% (%d/%d)" % (100*taken//total, taken, total)
+ )
+ if line in missing_branch_arcs:
+ annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]]
+ xline.setAttribute("missing-branches", ",".join(annlines))
+ xlines.appendChild(xline)
+
+ class_lines = len(analysis.statements)
+ class_hits = class_lines - len(analysis.missing)
+
+ if has_arcs:
+ class_branches = sum(t for t, k in branch_stats.values())
+ missing_branches = sum(t - k for t, k in branch_stats.values())
+ class_br_hits = class_branches - missing_branches
+ else:
+ class_branches = 0.0
+ class_br_hits = 0.0
+
+ # Finalize the statistics that are collected in the XML DOM.
+ xclass.setAttribute("line-rate", rate(class_hits, class_lines))
+ if has_arcs:
+ branch_rate = rate(class_br_hits, class_branches)
+ else:
+ branch_rate = "0"
+ xclass.setAttribute("branch-rate", branch_rate)
+
+ package[0][rel_name] = xclass
+ package[1] += class_hits
+ package[2] += class_lines
+ package[3] += class_br_hits
+ package[4] += class_branches
+
+
+def serialize_xml(dom):
+ """Serialize a minidom node to XML."""
+ out = dom.toprettyxml()
+ if env.PY2:
+ out = out.encode("utf8")
+ return out
diff --git a/contrib/python/coverage/py2/ya.make b/contrib/python/coverage/py2/ya.make
new file mode 100644
index 0000000000..7bff35eea3
--- /dev/null
+++ b/contrib/python/coverage/py2/ya.make
@@ -0,0 +1,98 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(5.5)
+
+LICENSE(Apache-2.0)
+
+PEERDIR(
+ contrib/python/coverage/plugins
+ library/python/resource
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_LINT()
+
+NO_CHECK_IMPORTS(
+ coverage.fullcoverage.encodings
+)
+
+SRCS(
+ coverage/ctracer/datastack.c
+ coverage/ctracer/filedisp.c
+ coverage/ctracer/module.c
+ coverage/ctracer/tracer.c
+)
+
+PY_REGISTER(
+ coverage.tracer
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ coverage/__init__.py
+ coverage/__main__.py
+ coverage/annotate.py
+ coverage/backward.py
+ coverage/bytecode.py
+ coverage/cmdline.py
+ coverage/collector.py
+ coverage/config.py
+ coverage/context.py
+ coverage/control.py
+ coverage/data.py
+ coverage/debug.py
+ coverage/disposition.py
+ coverage/env.py
+ coverage/execfile.py
+ coverage/files.py
+ coverage/fullcoverage/encodings.py
+ coverage/html.py
+ coverage/inorout.py
+ coverage/jsonreport.py
+ coverage/misc.py
+ coverage/multiproc.py
+ coverage/numbits.py
+ coverage/parser.py
+ coverage/phystokens.py
+ coverage/plugin.py
+ coverage/plugin_support.py
+ coverage/python.py
+ coverage/pytracer.py
+ coverage/report.py
+ coverage/results.py
+ coverage/sqldata.py
+ coverage/summary.py
+ coverage/templite.py
+ coverage/tomlconfig.py
+ coverage/version.py
+ coverage/xmlreport.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/coverage/py2/
+ .dist-info/METADATA
+ .dist-info/entry_points.txt
+ .dist-info/top_level.txt
+ coverage/htmlfiles/coverage_html.js
+ coverage/htmlfiles/favicon_32.png
+ coverage/htmlfiles/index.html
+ coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
+ coverage/htmlfiles/jquery.hotkeys.js
+ coverage/htmlfiles/jquery.isonscreen.js
+ coverage/htmlfiles/jquery.min.js
+ coverage/htmlfiles/jquery.tablesorter.min.js
+ coverage/htmlfiles/keybd_closed.png
+ coverage/htmlfiles/keybd_open.png
+ coverage/htmlfiles/pyfile.html
+ coverage/htmlfiles/style.css
+ coverage/htmlfiles/style.scss
+)
+
+END()
+
+RECURSE(
+ bin
+)
diff --git a/contrib/python/coverage/py3/.dist-info/METADATA b/contrib/python/coverage/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..25a6049c45
--- /dev/null
+++ b/contrib/python/coverage/py3/.dist-info/METADATA
@@ -0,0 +1,190 @@
+Metadata-Version: 2.1
+Name: coverage
+Version: 5.5
+Summary: Code coverage measurement for Python
+Home-page: https://github.com/nedbat/coveragepy
+Author: Ned Batchelder and 142 others
+Author-email: ned@nedbatchelder.com
+License: Apache 2.0
+Project-URL: Documentation, https://coverage.readthedocs.io
+Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=pypi
+Project-URL: Issues, https://github.com/nedbat/coveragepy/issues
+Keywords: code coverage testing
+Platform: UNKNOWN
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Quality Assurance
+Classifier: Topic :: Software Development :: Testing
+Classifier: Development Status :: 5 - Production/Stable
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4
+Description-Content-Type: text/x-rst
+Provides-Extra: toml
+Requires-Dist: toml ; extra == 'toml'
+
+.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+===========
+Coverage.py
+===========
+
+Code coverage testing for Python.
+
+| |license| |versions| |status|
+| |test-status| |quality-status| |docs| |codecov|
+| |kit| |format| |repos| |downloads|
+| |stars| |forks| |contributors|
+| |tidelift| |twitter-coveragepy| |twitter-nedbat|
+
+Coverage.py measures code coverage, typically during test execution. It uses
+the code analysis tools and tracing hooks provided in the Python standard
+library to determine which lines are executable, and which have been executed.
+
+Coverage.py runs on many versions of Python:
+
+* CPython 2.7.
+* CPython 3.5 through 3.10 alpha.
+* PyPy2 7.3.3 and PyPy3 7.3.3.
+
+Documentation is on `Read the Docs`_. Code repository and issue tracker are on
+`GitHub`_.
+
+.. _Read the Docs: https://coverage.readthedocs.io/
+.. _GitHub: https://github.com/nedbat/coveragepy
+
+
+**New in 5.x:** SQLite data storage, JSON report, contexts, relative filenames,
+dropped support for Python 2.6, 3.3 and 3.4.
+
+
+For Enterprise
+--------------
+
+.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png
+ :alt: Tidelift
+ :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+
+.. list-table::
+ :widths: 10 100
+
+ * - |tideliftlogo|
+ - `Available as part of the Tidelift Subscription. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
+ Coverage and thousands of other packages are working with
+ Tidelift to deliver one enterprise subscription that covers all of the open
+ source you use. If you want the flexibility of open source and the confidence
+ of commercial-grade software, this is for you.
+ `Learn more. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
+
+
+Getting Started
+---------------
+
+See the `Quick Start section`_ of the docs.
+
+.. _Quick Start section: https://coverage.readthedocs.io/#quick-start
+
+
+Change history
+--------------
+
+The complete history of changes is on the `change history page`_.
+
+.. _change history page: https://coverage.readthedocs.io/en/latest/changes.html
+
+
+Contributing
+------------
+
+See the `Contributing section`_ of the docs.
+
+.. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html
+
+
+Security
+--------
+
+To report a security vulnerability, please use the `Tidelift security
+contact`_. Tidelift will coordinate the fix and disclosure.
+
+.. _Tidelift security contact: https://tidelift.com/security
+
+
+License
+-------
+
+Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
+
+.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
+.. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+
+.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
+ :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
+ :alt: Test suite status
+.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
+ :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
+ :alt: Quality check status
+.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
+ :target: https://coverage.readthedocs.io/
+ :alt: Documentation
+.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master
+ :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master
+ :alt: Requirements status
+.. |kit| image:: https://badge.fury.io/py/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: PyPI status
+.. |format| image:: https://img.shields.io/pypi/format/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Kit format
+.. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Weekly PyPI downloads
+.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072
+ :target: https://pypi.org/project/coverage/
+ :alt: Python versions supported
+.. |status| image:: https://img.shields.io/pypi/status/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Package stability
+.. |license| image:: https://img.shields.io/pypi/l/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: License
+.. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
+ :target: https://codecov.io/github/nedbat/coveragepy?branch=master
+ :alt: Coverage!
+.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
+ :target: https://repology.org/metapackage/python:coverage/versions
+ :alt: Packaging status
+.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage
+ :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+ :alt: Tidelift
+.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/stargazers
+ :alt: Github stars
+.. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/network/members
+ :alt: Github forks
+.. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/graphs/contributors
+ :alt: Contributors
+.. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/coveragepy
+ :alt: coverage.py on Twitter
+.. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/nedbat
+ :alt: nedbat on Twitter
+
+
diff --git a/contrib/python/coverage/py3/.dist-info/entry_points.txt b/contrib/python/coverage/py3/.dist-info/entry_points.txt
new file mode 100644
index 0000000000..cd083fc1ff
--- /dev/null
+++ b/contrib/python/coverage/py3/.dist-info/entry_points.txt
@@ -0,0 +1,5 @@
+[console_scripts]
+coverage = coverage.cmdline:main
+coverage-3.9 = coverage.cmdline:main
+coverage3 = coverage.cmdline:main
+
diff --git a/contrib/python/coverage/py3/.dist-info/top_level.txt b/contrib/python/coverage/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..4ebc8aea50
--- /dev/null
+++ b/contrib/python/coverage/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+coverage
diff --git a/contrib/python/coverage/py3/LICENSE.txt b/contrib/python/coverage/py3/LICENSE.txt
new file mode 100644
index 0000000000..f433b1a53f
--- /dev/null
+++ b/contrib/python/coverage/py3/LICENSE.txt
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/contrib/python/coverage/py3/NOTICE.txt b/contrib/python/coverage/py3/NOTICE.txt
new file mode 100644
index 0000000000..37ded535bf
--- /dev/null
+++ b/contrib/python/coverage/py3/NOTICE.txt
@@ -0,0 +1,14 @@
+Copyright 2001 Gareth Rees. All rights reserved.
+Copyright 2004-2021 Ned Batchelder. All rights reserved.
+
+Except where noted otherwise, this software is licensed under the Apache
+License, Version 2.0 (the "License"); you may not use this work except in
+compliance with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/contrib/python/coverage/py3/README.rst b/contrib/python/coverage/py3/README.rst
new file mode 100644
index 0000000000..072f30ffeb
--- /dev/null
+++ b/contrib/python/coverage/py3/README.rst
@@ -0,0 +1,151 @@
+.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+===========
+Coverage.py
+===========
+
+Code coverage testing for Python.
+
+| |license| |versions| |status|
+| |test-status| |quality-status| |docs| |codecov|
+| |kit| |format| |repos| |downloads|
+| |stars| |forks| |contributors|
+| |tidelift| |twitter-coveragepy| |twitter-nedbat|
+
+Coverage.py measures code coverage, typically during test execution. It uses
+the code analysis tools and tracing hooks provided in the Python standard
+library to determine which lines are executable, and which have been executed.
+
+Coverage.py runs on many versions of Python:
+
+* CPython 2.7.
+* CPython 3.5 through 3.10 alpha.
+* PyPy2 7.3.3 and PyPy3 7.3.3.
+
+Documentation is on `Read the Docs`_. Code repository and issue tracker are on
+`GitHub`_.
+
+.. _Read the Docs: https://coverage.readthedocs.io/
+.. _GitHub: https://github.com/nedbat/coveragepy
+
+
+**New in 5.x:** SQLite data storage, JSON report, contexts, relative filenames,
+dropped support for Python 2.6, 3.3 and 3.4.
+
+
+For Enterprise
+--------------
+
+.. |tideliftlogo| image:: https://nedbatchelder.com/pix/Tidelift_Logo_small.png
+ :alt: Tidelift
+ :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+
+.. list-table::
+ :widths: 10 100
+
+ * - |tideliftlogo|
+ - `Available as part of the Tidelift Subscription. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
+ Coverage and thousands of other packages are working with
+ Tidelift to deliver one enterprise subscription that covers all of the open
+ source you use. If you want the flexibility of open source and the confidence
+ of commercial-grade software, this is for you.
+ `Learn more. <https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme>`_
+
+
+Getting Started
+---------------
+
+See the `Quick Start section`_ of the docs.
+
+.. _Quick Start section: https://coverage.readthedocs.io/#quick-start
+
+
+Change history
+--------------
+
+The complete history of changes is on the `change history page`_.
+
+.. _change history page: https://coverage.readthedocs.io/en/latest/changes.html
+
+
+Contributing
+------------
+
+See the `Contributing section`_ of the docs.
+
+.. _Contributing section: https://coverage.readthedocs.io/en/latest/contributing.html
+
+
+Security
+--------
+
+To report a security vulnerability, please use the `Tidelift security
+contact`_. Tidelift will coordinate the fix and disclosure.
+
+.. _Tidelift security contact: https://tidelift.com/security
+
+
+License
+-------
+
+Licensed under the `Apache 2.0 License`_. For details, see `NOTICE.txt`_.
+
+.. _Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
+.. _NOTICE.txt: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+
+.. |test-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml/badge.svg?branch=master&event=push
+ :target: https://github.com/nedbat/coveragepy/actions/workflows/testsuite.yml
+ :alt: Test suite status
+.. |quality-status| image:: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml/badge.svg?branch=master&event=push
+ :target: https://github.com/nedbat/coveragepy/actions/workflows/quality.yml
+ :alt: Quality check status
+.. |docs| image:: https://readthedocs.org/projects/coverage/badge/?version=latest&style=flat
+ :target: https://coverage.readthedocs.io/
+ :alt: Documentation
+.. |reqs| image:: https://requires.io/github/nedbat/coveragepy/requirements.svg?branch=master
+ :target: https://requires.io/github/nedbat/coveragepy/requirements/?branch=master
+ :alt: Requirements status
+.. |kit| image:: https://badge.fury.io/py/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: PyPI status
+.. |format| image:: https://img.shields.io/pypi/format/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Kit format
+.. |downloads| image:: https://img.shields.io/pypi/dw/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Weekly PyPI downloads
+.. |versions| image:: https://img.shields.io/pypi/pyversions/coverage.svg?logo=python&logoColor=FBE072
+ :target: https://pypi.org/project/coverage/
+ :alt: Python versions supported
+.. |status| image:: https://img.shields.io/pypi/status/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: Package stability
+.. |license| image:: https://img.shields.io/pypi/l/coverage.svg
+ :target: https://pypi.org/project/coverage/
+ :alt: License
+.. |codecov| image:: https://codecov.io/github/nedbat/coveragepy/coverage.svg?branch=master&precision=2
+ :target: https://codecov.io/github/nedbat/coveragepy?branch=master
+ :alt: Coverage!
+.. |repos| image:: https://repology.org/badge/tiny-repos/python:coverage.svg
+ :target: https://repology.org/metapackage/python:coverage/versions
+ :alt: Packaging status
+.. |tidelift| image:: https://tidelift.com/badges/package/pypi/coverage
+ :target: https://tidelift.com/subscription/pkg/pypi-coverage?utm_source=pypi-coverage&utm_medium=referral&utm_campaign=readme
+ :alt: Tidelift
+.. |stars| image:: https://img.shields.io/github/stars/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/stargazers
+ :alt: Github stars
+.. |forks| image:: https://img.shields.io/github/forks/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/network/members
+ :alt: Github forks
+.. |contributors| image:: https://img.shields.io/github/contributors/nedbat/coveragepy.svg?logo=github
+ :target: https://github.com/nedbat/coveragepy/graphs/contributors
+ :alt: Contributors
+.. |twitter-coveragepy| image:: https://img.shields.io/twitter/follow/coveragepy.svg?label=coveragepy&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/coveragepy
+ :alt: coverage.py on Twitter
+.. |twitter-nedbat| image:: https://img.shields.io/twitter/follow/nedbat.svg?label=nedbat&style=flat&logo=twitter&logoColor=4FADFF
+ :target: https://twitter.com/nedbat
+ :alt: nedbat on Twitter
diff --git a/contrib/python/coverage/py3/coverage/__init__.py b/contrib/python/coverage/py3/coverage/__init__.py
new file mode 100644
index 0000000000..331b304b68
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/__init__.py
@@ -0,0 +1,36 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Code coverage measurement for Python.
+
+Ned Batchelder
+https://nedbatchelder.com/code/coverage
+
+"""
+
+import sys
+
+from coverage.version import __version__, __url__, version_info
+
+from coverage.control import Coverage, process_startup
+from coverage.data import CoverageData
+from coverage.misc import CoverageException
+from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
+from coverage.pytracer import PyTracer
+
+# Backward compatibility.
+coverage = Coverage
+
+# On Windows, we encode and decode deep enough that something goes wrong and
+# the encodings.utf_8 module is loaded and then unloaded, I don't know why.
+# Adding a reference here prevents it from being unloaded. Yuk.
+import encodings.utf_8 # pylint: disable=wrong-import-position, wrong-import-order
+
+# Because of the "from coverage.control import fooey" lines at the top of the
+# file, there's an entry for coverage.coverage in sys.modules, mapped to None.
+# This makes some inspection tools (like pydoc) unable to find the class
+# coverage.coverage. So remove that entry.
+try:
+ del sys.modules['coverage.coverage']
+except KeyError:
+ pass
diff --git a/contrib/python/coverage/py3/coverage/__main__.py b/contrib/python/coverage/py3/coverage/__main__.py
new file mode 100644
index 0000000000..79aa4e2b35
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/__main__.py
@@ -0,0 +1,8 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Coverage.py's main entry point."""
+
+import sys
+from coverage.cmdline import main
+sys.exit(main())
diff --git a/contrib/python/coverage/py3/coverage/annotate.py b/contrib/python/coverage/py3/coverage/annotate.py
new file mode 100644
index 0000000000..999ab6e557
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/annotate.py
@@ -0,0 +1,108 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Source file annotation for coverage.py."""
+
+import io
+import os
+import re
+
+from coverage.files import flat_rootname
+from coverage.misc import ensure_dir, isolate_module
+from coverage.report import get_analysis_to_report
+
+os = isolate_module(os)
+
+
+class AnnotateReporter(object):
+ """Generate annotated source files showing line coverage.
+
+ This reporter creates annotated copies of the measured source files. Each
+ .py file is copied as a .py,cover file, with a left-hand margin annotating
+ each line::
+
+ > def h(x):
+ - if 0: #pragma: no cover
+ - pass
+ > if x == 1:
+ ! a = 1
+ > else:
+ > a = 2
+
+ > h(2)
+
+ Executed lines use '>', lines not executed use '!', lines excluded from
+ consideration use '-'.
+
+ """
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+ self.directory = None
+
+ blank_re = re.compile(r"\s*(#|$)")
+ else_re = re.compile(r"\s*else\s*:\s*(#|$)")
+
+ def report(self, morfs, directory=None):
+ """Run the report.
+
+ See `coverage.report()` for arguments.
+
+ """
+ self.directory = directory
+ self.coverage.get_data()
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
+ self.annotate_file(fr, analysis)
+
+ def annotate_file(self, fr, analysis):
+ """Annotate a single file.
+
+ `fr` is the FileReporter for the file to annotate.
+
+ """
+ statements = sorted(analysis.statements)
+ missing = sorted(analysis.missing)
+ excluded = sorted(analysis.excluded)
+
+ if self.directory:
+ ensure_dir(self.directory)
+ dest_file = os.path.join(self.directory, flat_rootname(fr.relative_filename()))
+ if dest_file.endswith("_py"):
+ dest_file = dest_file[:-3] + ".py"
+ dest_file += ",cover"
+ else:
+ dest_file = fr.filename + ",cover"
+
+ with io.open(dest_file, 'w', encoding='utf8') as dest:
+ i = 0
+ j = 0
+ covered = True
+ source = fr.source()
+ for lineno, line in enumerate(source.splitlines(True), start=1):
+ while i < len(statements) and statements[i] < lineno:
+ i += 1
+ while j < len(missing) and missing[j] < lineno:
+ j += 1
+ if i < len(statements) and statements[i] == lineno:
+ covered = j >= len(missing) or missing[j] > lineno
+ if self.blank_re.match(line):
+ dest.write(u' ')
+ elif self.else_re.match(line):
+ # Special logic for lines containing only 'else:'.
+ if i >= len(statements) and j >= len(missing):
+ dest.write(u'! ')
+ elif i >= len(statements) or j >= len(missing):
+ dest.write(u'> ')
+ elif statements[i] == missing[j]:
+ dest.write(u'! ')
+ else:
+ dest.write(u'> ')
+ elif lineno in excluded:
+ dest.write(u'- ')
+ elif covered:
+ dest.write(u'> ')
+ else:
+ dest.write(u'! ')
+
+ dest.write(line)
diff --git a/contrib/python/coverage/py3/coverage/backward.py b/contrib/python/coverage/py3/coverage/backward.py
new file mode 100644
index 0000000000..ac781ab96a
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/backward.py
@@ -0,0 +1,267 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Add things to old Pythons so I can pretend they are newer."""
+
+# This file's purpose is to provide modules to be imported from here.
+# pylint: disable=unused-import
+
+import os
+import sys
+
+from datetime import datetime
+
+from coverage import env
+
+
+# Pythons 2 and 3 differ on where to get StringIO.
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+# In py3, ConfigParser was renamed to the more-standard configparser.
+# But there's a py3 backport that installs "configparser" in py2, and I don't
+# want it because it has annoying deprecation warnings. So try the real py2
+# import first.
+try:
+ import ConfigParser as configparser
+except ImportError:
+ import configparser
+
+# What's a string called?
+try:
+ string_class = basestring
+except NameError:
+ string_class = str
+
+# What's a Unicode string called?
+try:
+ unicode_class = unicode
+except NameError:
+ unicode_class = str
+
+# range or xrange?
+try:
+ range = xrange # pylint: disable=redefined-builtin
+except NameError:
+ range = range
+
+try:
+ from itertools import zip_longest
+except ImportError:
+ from itertools import izip_longest as zip_longest
+
+# Where do we get the thread id from?
+try:
+ from thread import get_ident as get_thread_id
+except ImportError:
+ from threading import get_ident as get_thread_id
+
+try:
+ os.PathLike
+except AttributeError:
+ # This is Python 2 and 3
+ path_types = (bytes, string_class, unicode_class)
+else:
+ # 3.6+
+ path_types = (bytes, str, os.PathLike)
+
+# shlex.quote is new, but there's an undocumented implementation in "pipes",
+# who knew!?
+try:
+ from shlex import quote as shlex_quote
+except ImportError:
+ # Useful function, available under a different (undocumented) name
+ # in Python versions earlier than 3.3.
+ from pipes import quote as shlex_quote
+
+try:
+ import reprlib
+except ImportError: # pragma: not covered
+ # We need this on Python 2, but in testing environments, a backport is
+ # installed, so this import isn't used.
+ import repr as reprlib
+
+# A function to iterate listlessly over a dict's items, and one to get the
+# items as a list.
+try:
+ {}.iteritems
+except AttributeError:
+ # Python 3
+ def iitems(d):
+ """Produce the items from dict `d`."""
+ return d.items()
+
+ def litems(d):
+ """Return a list of items from dict `d`."""
+ return list(d.items())
+else:
+ # Python 2
+ def iitems(d):
+ """Produce the items from dict `d`."""
+ return d.iteritems()
+
+ def litems(d):
+ """Return a list of items from dict `d`."""
+ return d.items()
+
+# Getting the `next` function from an iterator is different in 2 and 3.
+try:
+ iter([]).next
+except AttributeError:
+ def iternext(seq):
+ """Get the `next` function for iterating over `seq`."""
+ return iter(seq).__next__
+else:
+ def iternext(seq):
+ """Get the `next` function for iterating over `seq`."""
+ return iter(seq).next
+
+# Python 3.x is picky about bytes and strings, so provide methods to
+# get them right, and make them no-ops in 2.x
+if env.PY3:
+ def to_bytes(s):
+ """Convert string `s` to bytes."""
+ return s.encode('utf8')
+
+ def to_string(b):
+ """Convert bytes `b` to string."""
+ return b.decode('utf8')
+
+ def binary_bytes(byte_values):
+ """Produce a byte string with the ints from `byte_values`."""
+ return bytes(byte_values)
+
+ def byte_to_int(byte):
+ """Turn a byte indexed from a bytes object into an int."""
+ return byte
+
+ def bytes_to_ints(bytes_value):
+ """Turn a bytes object into a sequence of ints."""
+ # In Python 3, iterating bytes gives ints.
+ return bytes_value
+
+else:
+ def to_bytes(s):
+ """Convert string `s` to bytes (no-op in 2.x)."""
+ return s
+
+ def to_string(b):
+ """Convert bytes `b` to string."""
+ return b
+
+ def binary_bytes(byte_values):
+ """Produce a byte string with the ints from `byte_values`."""
+ return "".join(chr(b) for b in byte_values)
+
+ def byte_to_int(byte):
+ """Turn a byte indexed from a bytes object into an int."""
+ return ord(byte)
+
+ def bytes_to_ints(bytes_value):
+ """Turn a bytes object into a sequence of ints."""
+ for byte in bytes_value:
+ yield ord(byte)
+
+
+try:
+ # In Python 2.x, the builtins were in __builtin__
+ BUILTINS = sys.modules['__builtin__']
+except KeyError:
+ # In Python 3.x, they're in builtins
+ BUILTINS = sys.modules['builtins']
+
+
+# imp was deprecated in Python 3.3
+try:
+ import importlib
+ import importlib.util
+ imp = None
+except ImportError:
+ importlib = None
+
+# We only want to use importlib if it has everything we need.
+try:
+ importlib_util_find_spec = importlib.util.find_spec
+except Exception:
+ import imp
+ importlib_util_find_spec = None
+
+# What is the .pyc magic number for this version of Python?
+try:
+ PYC_MAGIC_NUMBER = importlib.util.MAGIC_NUMBER
+except AttributeError:
+ PYC_MAGIC_NUMBER = imp.get_magic()
+
+
+def code_object(fn):
+ """Get the code object from a function."""
+ try:
+ return fn.func_code
+ except AttributeError:
+ return fn.__code__
+
+
+try:
+ from types import SimpleNamespace
+except ImportError:
+ # The code from https://docs.python.org/3/library/types.html#types.SimpleNamespace
+ class SimpleNamespace:
+ """Python implementation of SimpleNamespace, for Python 2."""
+ def __init__(self, **kwargs):
+ self.__dict__.update(kwargs)
+
+ def __repr__(self):
+ keys = sorted(self.__dict__)
+ items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
+ return "{}({})".format(type(self).__name__, ", ".join(items))
+
+
+def format_local_datetime(dt):
+ """Return a string with local timezone representing the date.
+ If python version is lower than 3.6, the time zone is not included.
+ """
+ try:
+ return dt.astimezone().strftime('%Y-%m-%d %H:%M %z')
+ except (TypeError, ValueError):
+ # Datetime.astimezone in Python 3.5 can not handle naive datetime
+ return dt.strftime('%Y-%m-%d %H:%M')
+
+
+def invalidate_import_caches():
+ """Invalidate any import caches that may or may not exist."""
+ if importlib and hasattr(importlib, "invalidate_caches"):
+ importlib.invalidate_caches()
+
+
+def import_local_file(modname, modfile=None):
+ """Import a local file as a module.
+
+ Opens a file in the current directory named `modname`.py, imports it
+ as `modname`, and returns the module object. `modfile` is the file to
+ import if it isn't in the current directory.
+
+ """
+ try:
+ import importlib.util as importlib_util
+ except ImportError:
+ importlib_util = None
+
+ if modfile is None:
+ modfile = modname + '.py'
+ if importlib_util:
+ spec = importlib_util.spec_from_file_location(modname, modfile)
+ mod = importlib_util.module_from_spec(spec)
+ sys.modules[modname] = mod
+ spec.loader.exec_module(mod)
+ else:
+ for suff in imp.get_suffixes(): # pragma: part covered
+ if suff[0] == '.py':
+ break
+
+ with open(modfile, 'r') as f:
+ # pylint: disable=undefined-loop-variable
+ mod = imp.load_module(modname, f, modfile, suff)
+
+ return mod
diff --git a/contrib/python/coverage/py3/coverage/bytecode.py b/contrib/python/coverage/py3/coverage/bytecode.py
new file mode 100644
index 0000000000..ceb18cf374
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/bytecode.py
@@ -0,0 +1,19 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Bytecode manipulation for coverage.py"""
+
+import types
+
+
+def code_objects(code):
+ """Iterate over all the code objects in `code`."""
+ stack = [code]
+ while stack:
+ # We're going to return the code object on the stack, but first
+ # push its children for later returning.
+ code = stack.pop()
+ for c in code.co_consts:
+ if isinstance(c, types.CodeType):
+ stack.append(c)
+ yield code
diff --git a/contrib/python/coverage/py3/coverage/cmdline.py b/contrib/python/coverage/py3/coverage/cmdline.py
new file mode 100644
index 0000000000..0be0cca19f
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/cmdline.py
@@ -0,0 +1,910 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Command-line support for coverage.py."""
+
+from __future__ import print_function
+
+import glob
+import optparse
+import os.path
+import shlex
+import sys
+import textwrap
+import traceback
+
+import coverage
+from coverage import Coverage
+from coverage import env
+from coverage.collector import CTracer
+from coverage.data import line_counts
+from coverage.debug import info_formatter, info_header, short_stack
+from coverage.execfile import PyRunner
+from coverage.misc import BaseCoverageException, ExceptionDuringRun, NoSource, output_encoding
+from coverage.results import should_fail_under
+
+
+class Opts(object):
+ """A namespace class for individual options we'll build parsers from."""
+
+ append = optparse.make_option(
+ '-a', '--append', action='store_true',
+ help="Append coverage data to .coverage, otherwise it starts clean each time.",
+ )
+ keep = optparse.make_option(
+ '', '--keep', action='store_true',
+ help="Keep original coverage files, otherwise they are deleted.",
+ )
+ branch = optparse.make_option(
+ '', '--branch', action='store_true',
+ help="Measure branch coverage in addition to statement coverage.",
+ )
+ CONCURRENCY_CHOICES = [
+ "thread", "gevent", "greenlet", "eventlet", "multiprocessing",
+ ]
+ concurrency = optparse.make_option(
+ '', '--concurrency', action='store', metavar="LIB",
+ choices=CONCURRENCY_CHOICES,
+ help=(
+ "Properly measure code using a concurrency library. "
+ "Valid values are: %s."
+ ) % ", ".join(CONCURRENCY_CHOICES),
+ )
+ context = optparse.make_option(
+ '', '--context', action='store', metavar="LABEL",
+ help="The context label to record for this coverage run.",
+ )
+ debug = optparse.make_option(
+ '', '--debug', action='store', metavar="OPTS",
+ help="Debug options, separated by commas. [env: COVERAGE_DEBUG]",
+ )
+ directory = optparse.make_option(
+ '-d', '--directory', action='store', metavar="DIR",
+ help="Write the output files to DIR.",
+ )
+ fail_under = optparse.make_option(
+ '', '--fail-under', action='store', metavar="MIN", type="float",
+ help="Exit with a status of 2 if the total coverage is less than MIN.",
+ )
+ help = optparse.make_option(
+ '-h', '--help', action='store_true',
+ help="Get help on this command.",
+ )
+ ignore_errors = optparse.make_option(
+ '-i', '--ignore-errors', action='store_true',
+ help="Ignore errors while reading source files.",
+ )
+ include = optparse.make_option(
+ '', '--include', action='store',
+ metavar="PAT1,PAT2,...",
+ help=(
+ "Include only files whose paths match one of these patterns. "
+ "Accepts shell-style wildcards, which must be quoted."
+ ),
+ )
+ pylib = optparse.make_option(
+ '-L', '--pylib', action='store_true',
+ help=(
+ "Measure coverage even inside the Python installed library, "
+ "which isn't done by default."
+ ),
+ )
+ sort = optparse.make_option(
+ '--sort', action='store', metavar='COLUMN',
+ help="Sort the report by the named column: name, stmts, miss, branch, brpart, or cover. "
+ "Default is name."
+ )
+ show_missing = optparse.make_option(
+ '-m', '--show-missing', action='store_true',
+ help="Show line numbers of statements in each module that weren't executed.",
+ )
+ skip_covered = optparse.make_option(
+ '--skip-covered', action='store_true',
+ help="Skip files with 100% coverage.",
+ )
+ no_skip_covered = optparse.make_option(
+ '--no-skip-covered', action='store_false', dest='skip_covered',
+ help="Disable --skip-covered.",
+ )
+ skip_empty = optparse.make_option(
+ '--skip-empty', action='store_true',
+ help="Skip files with no code.",
+ )
+ show_contexts = optparse.make_option(
+ '--show-contexts', action='store_true',
+ help="Show contexts for covered lines.",
+ )
+ omit = optparse.make_option(
+ '', '--omit', action='store',
+ metavar="PAT1,PAT2,...",
+ help=(
+ "Omit files whose paths match one of these patterns. "
+ "Accepts shell-style wildcards, which must be quoted."
+ ),
+ )
+ contexts = optparse.make_option(
+ '', '--contexts', action='store',
+ metavar="REGEX1,REGEX2,...",
+ help=(
+ "Only display data from lines covered in the given contexts. "
+ "Accepts Python regexes, which must be quoted."
+ ),
+ )
+ output_xml = optparse.make_option(
+ '-o', '', action='store', dest="outfile",
+ metavar="OUTFILE",
+ help="Write the XML report to this file. Defaults to 'coverage.xml'",
+ )
+ output_json = optparse.make_option(
+ '-o', '', action='store', dest="outfile",
+ metavar="OUTFILE",
+ help="Write the JSON report to this file. Defaults to 'coverage.json'",
+ )
+ json_pretty_print = optparse.make_option(
+ '', '--pretty-print', action='store_true',
+ help="Format the JSON for human readers.",
+ )
+ parallel_mode = optparse.make_option(
+ '-p', '--parallel-mode', action='store_true',
+ help=(
+ "Append the machine name, process id and random number to the "
+ ".coverage data file name to simplify collecting data from "
+ "many processes."
+ ),
+ )
+ module = optparse.make_option(
+ '-m', '--module', action='store_true',
+ help=(
+ "<pyfile> is an importable Python module, not a script path, "
+ "to be run as 'python -m' would run it."
+ ),
+ )
+ precision = optparse.make_option(
+ '', '--precision', action='store', metavar='N', type=int,
+ help=(
+ "Number of digits after the decimal point to display for "
+ "reported coverage percentages."
+ ),
+ )
+ rcfile = optparse.make_option(
+ '', '--rcfile', action='store',
+ help=(
+ "Specify configuration file. "
+ "By default '.coveragerc', 'setup.cfg', 'tox.ini', and "
+ "'pyproject.toml' are tried. [env: COVERAGE_RCFILE]"
+ ),
+ )
+ source = optparse.make_option(
+ '', '--source', action='store', metavar="SRC1,SRC2,...",
+ help="A list of packages or directories of code to be measured.",
+ )
+ timid = optparse.make_option(
+ '', '--timid', action='store_true',
+ help=(
+ "Use a simpler but slower trace method. Try this if you get "
+ "seemingly impossible results!"
+ ),
+ )
+ title = optparse.make_option(
+ '', '--title', action='store', metavar="TITLE",
+ help="A text string to use as the title on the HTML.",
+ )
+ version = optparse.make_option(
+ '', '--version', action='store_true',
+ help="Display version information and exit.",
+ )
+
+
+class CoverageOptionParser(optparse.OptionParser, object):
+ """Base OptionParser for coverage.py.
+
+ Problems don't exit the program.
+ Defaults are initialized for all options.
+
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(CoverageOptionParser, self).__init__(
+ add_help_option=False, *args, **kwargs
+ )
+ self.set_defaults(
+ action=None,
+ append=None,
+ branch=None,
+ concurrency=None,
+ context=None,
+ debug=None,
+ directory=None,
+ fail_under=None,
+ help=None,
+ ignore_errors=None,
+ include=None,
+ keep=None,
+ module=None,
+ omit=None,
+ contexts=None,
+ parallel_mode=None,
+ precision=None,
+ pylib=None,
+ rcfile=True,
+ show_missing=None,
+ skip_covered=None,
+ skip_empty=None,
+ show_contexts=None,
+ sort=None,
+ source=None,
+ timid=None,
+ title=None,
+ version=None,
+ )
+
+ self.disable_interspersed_args()
+
+ class OptionParserError(Exception):
+ """Used to stop the optparse error handler ending the process."""
+ pass
+
+ def parse_args_ok(self, args=None, options=None):
+ """Call optparse.parse_args, but return a triple:
+
+ (ok, options, args)
+
+ """
+ try:
+ options, args = super(CoverageOptionParser, self).parse_args(args, options)
+ except self.OptionParserError:
+ return False, None, None
+ return True, options, args
+
+ def error(self, msg):
+ """Override optparse.error so sys.exit doesn't get called."""
+ show_help(msg)
+ raise self.OptionParserError
+
+
+class GlobalOptionParser(CoverageOptionParser):
+ """Command-line parser for coverage.py global option arguments."""
+
+ def __init__(self):
+ super(GlobalOptionParser, self).__init__()
+
+ self.add_options([
+ Opts.help,
+ Opts.version,
+ ])
+
+
+class CmdOptionParser(CoverageOptionParser):
+ """Parse one of the new-style commands for coverage.py."""
+
+ def __init__(self, action, options, defaults=None, usage=None, description=None):
+ """Create an OptionParser for a coverage.py command.
+
+ `action` is the slug to put into `options.action`.
+ `options` is a list of Option's for the command.
+ `defaults` is a dict of default value for options.
+ `usage` is the usage string to display in help.
+ `description` is the description of the command, for the help text.
+
+ """
+ if usage:
+ usage = "%prog " + usage
+ super(CmdOptionParser, self).__init__(
+ usage=usage,
+ description=description,
+ )
+ self.set_defaults(action=action, **(defaults or {}))
+ self.add_options(options)
+ self.cmd = action
+
+ def __eq__(self, other):
+ # A convenience equality, so that I can put strings in unit test
+ # results, and they will compare equal to objects.
+ return (other == "<CmdOptionParser:%s>" % self.cmd)
+
+ __hash__ = None # This object doesn't need to be hashed.
+
+ def get_prog_name(self):
+ """Override of an undocumented function in optparse.OptionParser."""
+ program_name = super(CmdOptionParser, self).get_prog_name()
+
+ # Include the sub-command for this parser as part of the command.
+ return "{command} {subcommand}".format(command=program_name, subcommand=self.cmd)
+
+
+GLOBAL_ARGS = [
+ Opts.debug,
+ Opts.help,
+ Opts.rcfile,
+ ]
+
+CMDS = {
+ 'annotate': CmdOptionParser(
+ "annotate",
+ [
+ Opts.directory,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description=(
+ "Make annotated copies of the given files, marking statements that are executed "
+ "with > and statements that are missed with !."
+ ),
+ ),
+
+ 'combine': CmdOptionParser(
+ "combine",
+ [
+ Opts.append,
+ Opts.keep,
+ ] + GLOBAL_ARGS,
+ usage="[options] <path1> <path2> ... <pathN>",
+ description=(
+ "Combine data from multiple coverage files collected "
+ "with 'run -p'. The combined results are written to a single "
+ "file representing the union of the data. The positional "
+ "arguments are data files or directories containing data files. "
+ "If no paths are provided, data files in the default data file's "
+ "directory are combined."
+ ),
+ ),
+
+ 'debug': CmdOptionParser(
+ "debug", GLOBAL_ARGS,
+ usage="<topic>",
+ description=(
+ "Display information about the internals of coverage.py, "
+ "for diagnosing problems. "
+ "Topics are: "
+ "'data' to show a summary of the collected data; "
+ "'sys' to show installation information; "
+ "'config' to show the configuration; "
+ "'premain' to show what is calling coverage."
+ ),
+ ),
+
+ 'erase': CmdOptionParser(
+ "erase", GLOBAL_ARGS,
+ description="Erase previously collected coverage data.",
+ ),
+
+ 'help': CmdOptionParser(
+ "help", GLOBAL_ARGS,
+ usage="[command]",
+ description="Describe how to use coverage.py",
+ ),
+
+ 'html': CmdOptionParser(
+ "html",
+ [
+ Opts.contexts,
+ Opts.directory,
+ Opts.fail_under,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ Opts.precision,
+ Opts.show_contexts,
+ Opts.skip_covered,
+ Opts.no_skip_covered,
+ Opts.skip_empty,
+ Opts.title,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description=(
+ "Create an HTML report of the coverage of the files. "
+ "Each file gets its own page, with the source decorated to show "
+ "executed, excluded, and missed lines."
+ ),
+ ),
+
+ 'json': CmdOptionParser(
+ "json",
+ [
+ Opts.contexts,
+ Opts.fail_under,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ Opts.output_json,
+ Opts.json_pretty_print,
+ Opts.show_contexts,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description="Generate a JSON report of coverage results."
+ ),
+
+ 'report': CmdOptionParser(
+ "report",
+ [
+ Opts.contexts,
+ Opts.fail_under,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ Opts.precision,
+ Opts.sort,
+ Opts.show_missing,
+ Opts.skip_covered,
+ Opts.no_skip_covered,
+ Opts.skip_empty,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description="Report coverage statistics on modules."
+ ),
+
+ 'run': CmdOptionParser(
+ "run",
+ [
+ Opts.append,
+ Opts.branch,
+ Opts.concurrency,
+ Opts.context,
+ Opts.include,
+ Opts.module,
+ Opts.omit,
+ Opts.pylib,
+ Opts.parallel_mode,
+ Opts.source,
+ Opts.timid,
+ ] + GLOBAL_ARGS,
+ usage="[options] <pyfile> [program options]",
+ description="Run a Python program, measuring code execution."
+ ),
+
+ 'xml': CmdOptionParser(
+ "xml",
+ [
+ Opts.fail_under,
+ Opts.ignore_errors,
+ Opts.include,
+ Opts.omit,
+ Opts.output_xml,
+ Opts.skip_empty,
+ ] + GLOBAL_ARGS,
+ usage="[options] [modules]",
+ description="Generate an XML report of coverage results."
+ ),
+}
+
+
+def show_help(error=None, topic=None, parser=None):
+ """Display an error message, or the named topic."""
+ assert error or topic or parser
+
+ program_path = sys.argv[0]
+ if program_path.endswith(os.path.sep + '__main__.py'):
+ # The path is the main module of a package; get that path instead.
+ program_path = os.path.dirname(program_path)
+ program_name = os.path.basename(program_path)
+ if env.WINDOWS:
+ # entry_points={'console_scripts':...} on Windows makes files
+ # called coverage.exe, coverage3.exe, and coverage-3.5.exe. These
+ # invoke coverage-script.py, coverage3-script.py, and
+ # coverage-3.5-script.py. argv[0] is the .py file, but we want to
+ # get back to the original form.
+ auto_suffix = "-script.py"
+ if program_name.endswith(auto_suffix):
+ program_name = program_name[:-len(auto_suffix)]
+
+ help_params = dict(coverage.__dict__)
+ help_params['program_name'] = program_name
+ if CTracer is not None:
+ help_params['extension_modifier'] = 'with C extension'
+ else:
+ help_params['extension_modifier'] = 'without C extension'
+
+ if error:
+ print(error, file=sys.stderr)
+ print("Use '%s help' for help." % (program_name,), file=sys.stderr)
+ elif parser:
+ print(parser.format_help().strip())
+ print()
+ else:
+ help_msg = textwrap.dedent(HELP_TOPICS.get(topic, '')).strip()
+ if help_msg:
+ print(help_msg.format(**help_params))
+ else:
+ print("Don't know topic %r" % topic)
+ print("Full documentation is at {__url__}".format(**help_params))
+
+
+OK, ERR, FAIL_UNDER = 0, 1, 2
+
+
+class CoverageScript(object):
+ """The command-line interface to coverage.py."""
+
+ def __init__(self):
+ self.global_option = False
+ self.coverage = None
+
+ def command_line(self, argv):
+ """The bulk of the command line interface to coverage.py.
+
+ `argv` is the argument list to process.
+
+ Returns 0 if all is well, 1 if something went wrong.
+
+ """
+ # Collect the command-line options.
+ if not argv:
+ show_help(topic='minimum_help')
+ return OK
+
+ # The command syntax we parse depends on the first argument. Global
+ # switch syntax always starts with an option.
+ self.global_option = argv[0].startswith('-')
+ if self.global_option:
+ parser = GlobalOptionParser()
+ else:
+ parser = CMDS.get(argv[0])
+ if not parser:
+ show_help("Unknown command: '%s'" % argv[0])
+ return ERR
+ argv = argv[1:]
+
+ ok, options, args = parser.parse_args_ok(argv)
+ if not ok:
+ return ERR
+
+ # Handle help and version.
+ if self.do_help(options, args, parser):
+ return OK
+
+ # Listify the list options.
+ source = unshell_list(options.source)
+ omit = unshell_list(options.omit)
+ include = unshell_list(options.include)
+ debug = unshell_list(options.debug)
+ contexts = unshell_list(options.contexts)
+
+ # Do something.
+ self.coverage = Coverage(
+ data_suffix=options.parallel_mode,
+ cover_pylib=options.pylib,
+ timid=options.timid,
+ branch=options.branch,
+ config_file=options.rcfile,
+ source=source,
+ omit=omit,
+ include=include,
+ debug=debug,
+ concurrency=options.concurrency,
+ check_preimported=True,
+ context=options.context,
+ )
+
+ if options.action == "debug":
+ return self.do_debug(args)
+
+ elif options.action == "erase":
+ self.coverage.erase()
+ return OK
+
+ elif options.action == "run":
+ return self.do_run(options, args)
+
+ elif options.action == "combine":
+ if options.append:
+ self.coverage.load()
+ data_dirs = args or None
+ self.coverage.combine(data_dirs, strict=True, keep=bool(options.keep))
+ self.coverage.save()
+ return OK
+
+ # Remaining actions are reporting, with some common options.
+ report_args = dict(
+ morfs=unglob_args(args),
+ ignore_errors=options.ignore_errors,
+ omit=omit,
+ include=include,
+ contexts=contexts,
+ )
+
+ # We need to be able to import from the current directory, because
+ # plugins may try to, for example, to read Django settings.
+ sys.path.insert(0, '')
+
+ self.coverage.load()
+
+ total = None
+ if options.action == "report":
+ total = self.coverage.report(
+ show_missing=options.show_missing,
+ skip_covered=options.skip_covered,
+ skip_empty=options.skip_empty,
+ precision=options.precision,
+ sort=options.sort,
+ **report_args
+ )
+ elif options.action == "annotate":
+ self.coverage.annotate(directory=options.directory, **report_args)
+ elif options.action == "html":
+ total = self.coverage.html_report(
+ directory=options.directory,
+ title=options.title,
+ skip_covered=options.skip_covered,
+ skip_empty=options.skip_empty,
+ show_contexts=options.show_contexts,
+ precision=options.precision,
+ **report_args
+ )
+ elif options.action == "xml":
+ outfile = options.outfile
+ total = self.coverage.xml_report(
+ outfile=outfile, skip_empty=options.skip_empty,
+ **report_args
+ )
+ elif options.action == "json":
+ outfile = options.outfile
+ total = self.coverage.json_report(
+ outfile=outfile,
+ pretty_print=options.pretty_print,
+ show_contexts=options.show_contexts,
+ **report_args
+ )
+
+ if total is not None:
+ # Apply the command line fail-under options, and then use the config
+ # value, so we can get fail_under from the config file.
+ if options.fail_under is not None:
+ self.coverage.set_option("report:fail_under", options.fail_under)
+
+ fail_under = self.coverage.get_option("report:fail_under")
+ precision = self.coverage.get_option("report:precision")
+ if should_fail_under(total, fail_under, precision):
+ msg = "total of {total:.{p}f} is less than fail-under={fail_under:.{p}f}".format(
+ total=total, fail_under=fail_under, p=precision,
+ )
+ print("Coverage failure:", msg)
+ return FAIL_UNDER
+
+ return OK
+
+ def do_help(self, options, args, parser):
+ """Deal with help requests.
+
+ Return True if it handled the request, False if not.
+
+ """
+ # Handle help.
+ if options.help:
+ if self.global_option:
+ show_help(topic='help')
+ else:
+ show_help(parser=parser)
+ return True
+
+ if options.action == "help":
+ if args:
+ for a in args:
+ parser = CMDS.get(a)
+ if parser:
+ show_help(parser=parser)
+ else:
+ show_help(topic=a)
+ else:
+ show_help(topic='help')
+ return True
+
+ # Handle version.
+ if options.version:
+ show_help(topic='version')
+ return True
+
+ return False
+
+ def do_run(self, options, args):
+ """Implementation of 'coverage run'."""
+
+ if not args:
+ if options.module:
+ # Specified -m with nothing else.
+ show_help("No module specified for -m")
+ return ERR
+ command_line = self.coverage.get_option("run:command_line")
+ if command_line is not None:
+ args = shlex.split(command_line)
+ if args and args[0] == "-m":
+ options.module = True
+ args = args[1:]
+ if not args:
+ show_help("Nothing to do.")
+ return ERR
+
+ if options.append and self.coverage.get_option("run:parallel"):
+ show_help("Can't append to data files in parallel mode.")
+ return ERR
+
+ if options.concurrency == "multiprocessing":
+ # Can't set other run-affecting command line options with
+ # multiprocessing.
+ for opt_name in ['branch', 'include', 'omit', 'pylib', 'source', 'timid']:
+ # As it happens, all of these options have no default, meaning
+ # they will be None if they have not been specified.
+ if getattr(options, opt_name) is not None:
+ show_help(
+ "Options affecting multiprocessing must only be specified "
+ "in a configuration file.\n"
+ "Remove --{} from the command line.".format(opt_name)
+ )
+ return ERR
+
+ runner = PyRunner(args, as_module=bool(options.module))
+ runner.prepare()
+
+ if options.append:
+ self.coverage.load()
+
+ # Run the script.
+ self.coverage.start()
+ code_ran = True
+ try:
+ runner.run()
+ except NoSource:
+ code_ran = False
+ raise
+ finally:
+ self.coverage.stop()
+ if code_ran:
+ self.coverage.save()
+
+ return OK
+
+ def do_debug(self, args):
+ """Implementation of 'coverage debug'."""
+
+ if not args:
+ show_help("What information would you like: config, data, sys, premain?")
+ return ERR
+
+ for info in args:
+ if info == 'sys':
+ sys_info = self.coverage.sys_info()
+ print(info_header("sys"))
+ for line in info_formatter(sys_info):
+ print(" %s" % line)
+ elif info == 'data':
+ self.coverage.load()
+ data = self.coverage.get_data()
+ print(info_header("data"))
+ print("path: %s" % data.data_filename())
+ if data:
+ print("has_arcs: %r" % data.has_arcs())
+ summary = line_counts(data, fullpath=True)
+ filenames = sorted(summary.keys())
+ print("\n%d files:" % len(filenames))
+ for f in filenames:
+ line = "%s: %d lines" % (f, summary[f])
+ plugin = data.file_tracer(f)
+ if plugin:
+ line += " [%s]" % plugin
+ print(line)
+ else:
+ print("No data collected")
+ elif info == 'config':
+ print(info_header("config"))
+ config_info = self.coverage.config.__dict__.items()
+ for line in info_formatter(config_info):
+ print(" %s" % line)
+ elif info == "premain":
+ print(info_header("premain"))
+ print(short_stack())
+ else:
+ show_help("Don't know what you mean by %r" % info)
+ return ERR
+
+ return OK
+
+
+def unshell_list(s):
+ """Turn a command-line argument into a list."""
+ if not s:
+ return None
+ if env.WINDOWS:
+ # When running coverage.py as coverage.exe, some of the behavior
+ # of the shell is emulated: wildcards are expanded into a list of
+ # file names. So you have to single-quote patterns on the command
+ # line, but (not) helpfully, the single quotes are included in the
+ # argument, so we have to strip them off here.
+ s = s.strip("'")
+ return s.split(',')
+
+
+def unglob_args(args):
+ """Interpret shell wildcards for platforms that need it."""
+ if env.WINDOWS:
+ globbed = []
+ for arg in args:
+ if '?' in arg or '*' in arg:
+ globbed.extend(glob.glob(arg))
+ else:
+ globbed.append(arg)
+ args = globbed
+ return args
+
+
+HELP_TOPICS = {
+ 'help': """\
+ Coverage.py, version {__version__} {extension_modifier}
+ Measure, collect, and report on code coverage in Python programs.
+
+ usage: {program_name} <command> [options] [args]
+
+ Commands:
+ annotate Annotate source files with execution information.
+ combine Combine a number of data files.
+ debug Display information about the internals of coverage.py
+ erase Erase previously collected coverage data.
+ help Get help on using coverage.py.
+ html Create an HTML report.
+ json Create a JSON report of coverage results.
+ report Report coverage stats on modules.
+ run Run a Python program and measure code execution.
+ xml Create an XML report of coverage results.
+
+ Use "{program_name} help <command>" for detailed help on any command.
+ """,
+
+ 'minimum_help': """\
+ Code coverage for Python, version {__version__} {extension_modifier}. Use '{program_name} help' for help.
+ """,
+
+ 'version': """\
+ Coverage.py, version {__version__} {extension_modifier}
+ """,
+}
+
+
+def main(argv=None):
+ """The main entry point to coverage.py.
+
+ This is installed as the script entry point.
+
+ """
+ if argv is None:
+ argv = sys.argv[1:]
+ try:
+ status = CoverageScript().command_line(argv)
+ except ExceptionDuringRun as err:
+ # An exception was caught while running the product code. The
+ # sys.exc_info() return tuple is packed into an ExceptionDuringRun
+ # exception.
+ traceback.print_exception(*err.args) # pylint: disable=no-value-for-parameter
+ status = ERR
+ except BaseCoverageException as err:
+ # A controlled error inside coverage.py: print the message to the user.
+ msg = err.args[0]
+ if env.PY2:
+ msg = msg.encode(output_encoding())
+ print(msg)
+ status = ERR
+ except SystemExit as err:
+ # The user called `sys.exit()`. Exit with their argument, if any.
+ if err.args:
+ status = err.args[0]
+ else:
+ status = None
+ return status
+
+# Profiling using ox_profile. Install it from GitHub:
+# pip install git+https://github.com/emin63/ox_profile.git
+#
+# $set_env.py: COVERAGE_PROFILE - Set to use ox_profile.
+_profile = os.environ.get("COVERAGE_PROFILE", "")
+if _profile: # pragma: debugging
+ from ox_profile.core.launchers import SimpleLauncher # pylint: disable=import-error
+ original_main = main
+
+ def main(argv=None): # pylint: disable=function-redefined
+ """A wrapper around main that profiles."""
+ profiler = SimpleLauncher.launch()
+ try:
+ return original_main(argv)
+ finally:
+ data, _ = profiler.query(re_filter='coverage', max_records=100)
+ print(profiler.show(query=data, limit=100, sep='', col=''))
+ profiler.cancel()
diff --git a/contrib/python/coverage/py3/coverage/collector.py b/contrib/python/coverage/py3/coverage/collector.py
new file mode 100644
index 0000000000..c42d29feec
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/collector.py
@@ -0,0 +1,455 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Raw data collector for coverage.py."""
+
+import os
+import sys
+
+from coverage import env
+from coverage.backward import litems, range # pylint: disable=redefined-builtin
+from coverage.debug import short_stack
+from coverage.disposition import FileDisposition
+from coverage.misc import CoverageException, isolate_module
+from coverage.pytracer import PyTracer
+
+os = isolate_module(os)
+
+
+try:
+ # Use the C extension code when we can, for speed.
+ from coverage.tracer import CTracer, CFileDisposition
+except ImportError:
+ # Couldn't import the C extension, maybe it isn't built.
+ if os.getenv('COVERAGE_TEST_TRACER') == 'c':
+ # During testing, we use the COVERAGE_TEST_TRACER environment variable
+ # to indicate that we've fiddled with the environment to test this
+ # fallback code. If we thought we had a C tracer, but couldn't import
+ # it, then exit quickly and clearly instead of dribbling confusing
+ # errors. I'm using sys.exit here instead of an exception because an
+ # exception here causes all sorts of other noise in unittest.
+ sys.stderr.write("*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n")
+ sys.exit(1)
+ CTracer = None
+
+
+class Collector(object):
+ """Collects trace data.
+
+ Creates a Tracer object for each thread, since they track stack
+ information. Each Tracer points to the same shared data, contributing
+ traced data points.
+
+ When the Collector is started, it creates a Tracer for the current thread,
+ and installs a function to create Tracers for each new thread started.
+ When the Collector is stopped, all active Tracers are stopped.
+
+ Threads started while the Collector is stopped will never have Tracers
+ associated with them.
+
+ """
+
+ # The stack of active Collectors. Collectors are added here when started,
+ # and popped when stopped. Collectors on the stack are paused when not
+ # the top, and resumed when they become the top again.
+ _collectors = []
+
+ # The concurrency settings we support here.
+ SUPPORTED_CONCURRENCIES = {"greenlet", "eventlet", "gevent", "thread"}
+
+ def __init__(
+ self, should_trace, check_include, should_start_context, file_mapper,
+ timid, branch, warn, concurrency,
+ ):
+ """Create a collector.
+
+ `should_trace` is a function, taking a file name and a frame, and
+ returning a `coverage.FileDisposition object`.
+
+ `check_include` is a function taking a file name and a frame. It returns
+ a boolean: True if the file should be traced, False if not.
+
+ `should_start_context` is a function taking a frame, and returning a
+ string. If the frame should be the start of a new context, the string
+ is the new context. If the frame should not be the start of a new
+ context, return None.
+
+ `file_mapper` is a function taking a filename, and returning a Unicode
+ filename. The result is the name that will be recorded in the data
+ file.
+
+ If `timid` is true, then a slower simpler trace function will be
+ used. This is important for some environments where manipulation of
+ tracing functions make the faster more sophisticated trace function not
+ operate properly.
+
+ If `branch` is true, then branches will be measured. This involves
+ collecting data on which statements followed each other (arcs). Use
+ `get_arc_data` to get the arc data.
+
+ `warn` is a warning function, taking a single string message argument
+ and an optional slug argument which will be a string or None, to be
+ used if a warning needs to be issued.
+
+ `concurrency` is a list of strings indicating the concurrency libraries
+ in use. Valid values are "greenlet", "eventlet", "gevent", or "thread"
+ (the default). Of these four values, only one can be supplied. Other
+ values are ignored.
+
+ """
+ self.should_trace = should_trace
+ self.check_include = check_include
+ self.should_start_context = should_start_context
+ self.file_mapper = file_mapper
+ self.warn = warn
+ self.branch = branch
+ self.threading = None
+ self.covdata = None
+
+ self.static_context = None
+
+ self.origin = short_stack()
+
+ self.concur_id_func = None
+ self.mapped_file_cache = {}
+
+ # We can handle a few concurrency options here, but only one at a time.
+ these_concurrencies = self.SUPPORTED_CONCURRENCIES.intersection(concurrency)
+ if len(these_concurrencies) > 1:
+ raise CoverageException("Conflicting concurrency settings: %s" % concurrency)
+ self.concurrency = these_concurrencies.pop() if these_concurrencies else ''
+
+ try:
+ if self.concurrency == "greenlet":
+ import greenlet
+ self.concur_id_func = greenlet.getcurrent
+ elif self.concurrency == "eventlet":
+ import eventlet.greenthread # pylint: disable=import-error,useless-suppression
+ self.concur_id_func = eventlet.greenthread.getcurrent
+ elif self.concurrency == "gevent":
+ import gevent # pylint: disable=import-error,useless-suppression
+ self.concur_id_func = gevent.getcurrent
+ elif self.concurrency == "thread" or not self.concurrency:
+ # It's important to import threading only if we need it. If
+ # it's imported early, and the program being measured uses
+ # gevent, then gevent's monkey-patching won't work properly.
+ import threading
+ self.threading = threading
+ else:
+ raise CoverageException("Don't understand concurrency=%s" % concurrency)
+ except ImportError:
+ raise CoverageException(
+ "Couldn't trace with concurrency=%s, the module isn't installed." % (
+ self.concurrency,
+ )
+ )
+
+ self.reset()
+
+ if timid:
+ # Being timid: use the simple Python trace function.
+ self._trace_class = PyTracer
+ else:
+ # Being fast: use the C Tracer if it is available, else the Python
+ # trace function.
+ self._trace_class = CTracer or PyTracer
+
+ if self._trace_class is CTracer:
+ self.file_disposition_class = CFileDisposition
+ self.supports_plugins = True
+ else:
+ self.file_disposition_class = FileDisposition
+ self.supports_plugins = False
+
+ def __repr__(self):
+ return "<Collector at 0x%x: %s>" % (id(self), self.tracer_name())
+
+ def use_data(self, covdata, context):
+ """Use `covdata` for recording data."""
+ self.covdata = covdata
+ self.static_context = context
+ self.covdata.set_context(self.static_context)
+
+ def tracer_name(self):
+ """Return the class name of the tracer we're using."""
+ return self._trace_class.__name__
+
+ def _clear_data(self):
+ """Clear out existing data, but stay ready for more collection."""
+ # We used to used self.data.clear(), but that would remove filename
+ # keys and data values that were still in use higher up the stack
+ # when we are called as part of switch_context.
+ for d in self.data.values():
+ d.clear()
+
+ for tracer in self.tracers:
+ tracer.reset_activity()
+
+ def reset(self):
+ """Clear collected data, and prepare to collect more."""
+ # A dictionary mapping file names to dicts with line number keys (if not
+ # branch coverage), or mapping file names to dicts with line number
+ # pairs as keys (if branch coverage).
+ self.data = {}
+
+ # A dictionary mapping file names to file tracer plugin names that will
+ # handle them.
+ self.file_tracers = {}
+
+ self.disabled_plugins = set()
+
+ # The .should_trace_cache attribute is a cache from file names to
+ # coverage.FileDisposition objects, or None. When a file is first
+ # considered for tracing, a FileDisposition is obtained from
+ # Coverage.should_trace. Its .trace attribute indicates whether the
+ # file should be traced or not. If it should be, a plugin with dynamic
+ # file names can decide not to trace it based on the dynamic file name
+ # being excluded by the inclusion rules, in which case the
+ # FileDisposition will be replaced by None in the cache.
+ if env.PYPY:
+ import __pypy__ # pylint: disable=import-error
+ # Alex Gaynor said:
+ # should_trace_cache is a strictly growing key: once a key is in
+ # it, it never changes. Further, the keys used to access it are
+ # generally constant, given sufficient context. That is to say, at
+ # any given point _trace() is called, pypy is able to know the key.
+ # This is because the key is determined by the physical source code
+ # line, and that's invariant with the call site.
+ #
+ # This property of a dict with immutable keys, combined with
+ # call-site-constant keys is a match for PyPy's module dict,
+ # which is optimized for such workloads.
+ #
+ # This gives a 20% benefit on the workload described at
+ # https://bitbucket.org/pypy/pypy/issue/1871/10x-slower-than-cpython-under-coverage
+ self.should_trace_cache = __pypy__.newdict("module")
+ else:
+ self.should_trace_cache = {}
+
+ # Our active Tracers.
+ self.tracers = []
+
+ self._clear_data()
+
+ def _start_tracer(self):
+ """Start a new Tracer object, and store it in self.tracers."""
+ tracer = self._trace_class()
+ tracer.data = self.data
+ tracer.trace_arcs = self.branch
+ tracer.should_trace = self.should_trace
+ tracer.should_trace_cache = self.should_trace_cache
+ tracer.warn = self.warn
+
+ if hasattr(tracer, 'concur_id_func'):
+ tracer.concur_id_func = self.concur_id_func
+ elif self.concur_id_func:
+ raise CoverageException(
+ "Can't support concurrency=%s with %s, only threads are supported" % (
+ self.concurrency, self.tracer_name(),
+ )
+ )
+
+ if hasattr(tracer, 'file_tracers'):
+ tracer.file_tracers = self.file_tracers
+ if hasattr(tracer, 'threading'):
+ tracer.threading = self.threading
+ if hasattr(tracer, 'check_include'):
+ tracer.check_include = self.check_include
+ if hasattr(tracer, 'should_start_context'):
+ tracer.should_start_context = self.should_start_context
+ tracer.switch_context = self.switch_context
+ if hasattr(tracer, 'disable_plugin'):
+ tracer.disable_plugin = self.disable_plugin
+
+ fn = tracer.start()
+ self.tracers.append(tracer)
+
+ return fn
+
+ # The trace function has to be set individually on each thread before
+ # execution begins. Ironically, the only support the threading module has
+ # for running code before the thread main is the tracing function. So we
+ # install this as a trace function, and the first time it's called, it does
+ # the real trace installation.
+
+ def _installation_trace(self, frame, event, arg):
+ """Called on new threads, installs the real tracer."""
+ # Remove ourselves as the trace function.
+ sys.settrace(None)
+ # Install the real tracer.
+ fn = self._start_tracer()
+ # Invoke the real trace function with the current event, to be sure
+ # not to lose an event.
+ if fn:
+ fn = fn(frame, event, arg)
+ # Return the new trace function to continue tracing in this scope.
+ return fn
+
+ def start(self):
+ """Start collecting trace information."""
+ if self._collectors:
+ self._collectors[-1].pause()
+
+ self.tracers = []
+
+ # Check to see whether we had a fullcoverage tracer installed. If so,
+ # get the stack frames it stashed away for us.
+ traces0 = []
+ fn0 = sys.gettrace()
+ if fn0:
+ tracer0 = getattr(fn0, '__self__', None)
+ if tracer0:
+ traces0 = getattr(tracer0, 'traces', [])
+
+ try:
+ # Install the tracer on this thread.
+ fn = self._start_tracer()
+ except:
+ if self._collectors:
+ self._collectors[-1].resume()
+ raise
+
+ # If _start_tracer succeeded, then we add ourselves to the global
+ # stack of collectors.
+ self._collectors.append(self)
+
+ # Replay all the events from fullcoverage into the new trace function.
+ for args in traces0:
+ (frame, event, arg), lineno = args
+ try:
+ fn(frame, event, arg, lineno=lineno)
+ except TypeError:
+ raise Exception("fullcoverage must be run with the C trace function.")
+
+ # Install our installation tracer in threading, to jump-start other
+ # threads.
+ if self.threading:
+ self.threading.settrace(self._installation_trace)
+
+ def stop(self):
+ """Stop collecting trace information."""
+ assert self._collectors
+ if self._collectors[-1] is not self:
+ print("self._collectors:")
+ for c in self._collectors:
+ print(" {!r}\n{}".format(c, c.origin))
+ assert self._collectors[-1] is self, (
+ "Expected current collector to be %r, but it's %r" % (self, self._collectors[-1])
+ )
+
+ self.pause()
+
+ # Remove this Collector from the stack, and resume the one underneath
+ # (if any).
+ self._collectors.pop()
+ if self._collectors:
+ self._collectors[-1].resume()
+
+ def pause(self):
+ """Pause tracing, but be prepared to `resume`."""
+ for tracer in self.tracers:
+ tracer.stop()
+ stats = tracer.get_stats()
+ if stats:
+ print("\nCoverage.py tracer stats:")
+ for k in sorted(stats.keys()):
+ print("%20s: %s" % (k, stats[k]))
+ if self.threading:
+ self.threading.settrace(None)
+
+ def resume(self):
+ """Resume tracing after a `pause`."""
+ for tracer in self.tracers:
+ tracer.start()
+ if self.threading:
+ self.threading.settrace(self._installation_trace)
+ else:
+ self._start_tracer()
+
+ def _activity(self):
+ """Has any activity been traced?
+
+ Returns a boolean, True if any trace function was invoked.
+
+ """
+ return any(tracer.activity() for tracer in self.tracers)
+
+ def switch_context(self, new_context):
+ """Switch to a new dynamic context."""
+ self.flush_data()
+ if self.static_context:
+ context = self.static_context
+ if new_context:
+ context += "|" + new_context
+ else:
+ context = new_context
+ self.covdata.set_context(context)
+
+ def disable_plugin(self, disposition):
+ """Disable the plugin mentioned in `disposition`."""
+ file_tracer = disposition.file_tracer
+ plugin = file_tracer._coverage_plugin
+ plugin_name = plugin._coverage_plugin_name
+ self.warn("Disabling plug-in {!r} due to previous exception".format(plugin_name))
+ plugin._coverage_enabled = False
+ disposition.trace = False
+
+ def cached_mapped_file(self, filename):
+ """A locally cached version of file names mapped through file_mapper."""
+ key = (type(filename), filename)
+ try:
+ return self.mapped_file_cache[key]
+ except KeyError:
+ return self.mapped_file_cache.setdefault(key, self.file_mapper(filename))
+
+ def mapped_file_dict(self, d):
+ """Return a dict like d, but with keys modified by file_mapper."""
+ # The call to litems() ensures that the GIL protects the dictionary
+ # iterator against concurrent modifications by tracers running
+ # in other threads. We try three times in case of concurrent
+ # access, hoping to get a clean copy.
+ runtime_err = None
+ for _ in range(3):
+ try:
+ items = litems(d)
+ except RuntimeError as ex:
+ runtime_err = ex
+ else:
+ break
+ else:
+ raise runtime_err
+
+ if getattr(sys, 'is_standalone_binary', False):
+ # filenames should stay relative to the arcadia root, because files may not exist
+ return dict((k, v) for k, v in items if v)
+
+ return dict((self.cached_mapped_file(k), v) for k, v in items if v)
+
+ def plugin_was_disabled(self, plugin):
+ """Record that `plugin` was disabled during the run."""
+ self.disabled_plugins.add(plugin._coverage_plugin_name)
+
+ def flush_data(self):
+ """Save the collected data to our associated `CoverageData`.
+
+ Data may have also been saved along the way. This forces the
+ last of the data to be saved.
+
+ Returns True if there was data to save, False if not.
+ """
+ if not self._activity():
+ return False
+
+ if self.branch:
+ self.covdata.add_arcs(self.mapped_file_dict(self.data))
+ else:
+ self.covdata.add_lines(self.mapped_file_dict(self.data))
+
+ file_tracers = {
+ k: v for k, v in self.file_tracers.items()
+ if v not in self.disabled_plugins
+ }
+ self.covdata.add_file_tracers(self.mapped_file_dict(file_tracers))
+
+ self._clear_data()
+ return True
diff --git a/contrib/python/coverage/py3/coverage/config.py b/contrib/python/coverage/py3/coverage/config.py
new file mode 100644
index 0000000000..ceb7201b65
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/config.py
@@ -0,0 +1,605 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Config file for coverage.py"""
+
+import collections
+import copy
+import os
+import os.path
+import re
+import sys
+
+from coverage import env
+from coverage.backward import configparser, iitems, string_class
+from coverage.misc import contract, CoverageException, isolate_module
+from coverage.misc import substitute_variables
+
+from coverage.tomlconfig import TomlConfigParser, TomlDecodeError
+
+os = isolate_module(os)
+
+
+class HandyConfigParser(configparser.RawConfigParser):
+ """Our specialization of ConfigParser."""
+
+ def __init__(self, our_file):
+ """Create the HandyConfigParser.
+
+ `our_file` is True if this config file is specifically for coverage,
+ False if we are examining another config file (tox.ini, setup.cfg)
+ for possible settings.
+ """
+
+ configparser.RawConfigParser.__init__(self)
+ self.section_prefixes = ["coverage:"]
+ if our_file:
+ self.section_prefixes.append("")
+
+ def read(self, filenames, encoding=None):
+ """Read a file name as UTF-8 configuration data."""
+ kwargs = {}
+ if env.PYVERSION >= (3, 2):
+ kwargs['encoding'] = encoding or "utf-8"
+ return configparser.RawConfigParser.read(self, filenames, **kwargs)
+
+ def has_option(self, section, option):
+ for section_prefix in self.section_prefixes:
+ real_section = section_prefix + section
+ has = configparser.RawConfigParser.has_option(self, real_section, option)
+ if has:
+ return has
+ return False
+
+ def has_section(self, section):
+ for section_prefix in self.section_prefixes:
+ real_section = section_prefix + section
+ has = configparser.RawConfigParser.has_section(self, real_section)
+ if has:
+ return real_section
+ return False
+
+ def options(self, section):
+ for section_prefix in self.section_prefixes:
+ real_section = section_prefix + section
+ if configparser.RawConfigParser.has_section(self, real_section):
+ return configparser.RawConfigParser.options(self, real_section)
+ raise configparser.NoSectionError(section)
+
+ def get_section(self, section):
+ """Get the contents of a section, as a dictionary."""
+ d = {}
+ for opt in self.options(section):
+ d[opt] = self.get(section, opt)
+ return d
+
+ def get(self, section, option, *args, **kwargs):
+ """Get a value, replacing environment variables also.
+
+ The arguments are the same as `RawConfigParser.get`, but in the found
+ value, ``$WORD`` or ``${WORD}`` are replaced by the value of the
+ environment variable ``WORD``.
+
+ Returns the finished value.
+
+ """
+ for section_prefix in self.section_prefixes:
+ real_section = section_prefix + section
+ if configparser.RawConfigParser.has_option(self, real_section, option):
+ break
+ else:
+ raise configparser.NoOptionError(option, section)
+
+ v = configparser.RawConfigParser.get(self, real_section, option, *args, **kwargs)
+ v = substitute_variables(v, os.environ)
+ return v
+
+ def getlist(self, section, option):
+ """Read a list of strings.
+
+ The value of `section` and `option` is treated as a comma- and newline-
+ separated list of strings. Each value is stripped of whitespace.
+
+ Returns the list of strings.
+
+ """
+ value_list = self.get(section, option)
+ values = []
+ for value_line in value_list.split('\n'):
+ for value in value_line.split(','):
+ value = value.strip()
+ if value:
+ values.append(value)
+ return values
+
+ def getregexlist(self, section, option):
+ """Read a list of full-line regexes.
+
+ The value of `section` and `option` is treated as a newline-separated
+ list of regexes. Each value is stripped of whitespace.
+
+ Returns the list of strings.
+
+ """
+ line_list = self.get(section, option)
+ value_list = []
+ for value in line_list.splitlines():
+ value = value.strip()
+ try:
+ re.compile(value)
+ except re.error as e:
+ raise CoverageException(
+ "Invalid [%s].%s value %r: %s" % (section, option, value, e)
+ )
+ if value:
+ value_list.append(value)
+ return value_list
+
+
+# The default line exclusion regexes.
+DEFAULT_EXCLUDE = [
+ r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)',
+]
+
+# The default partial branch regexes, to be modified by the user.
+DEFAULT_PARTIAL = [
+ r'#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(branch|BRANCH)',
+]
+
+# The default partial branch regexes, based on Python semantics.
+# These are any Python branching constructs that can't actually execute all
+# their branches.
+DEFAULT_PARTIAL_ALWAYS = [
+ 'while (True|1|False|0):',
+ 'if (True|1|False|0):',
+]
+
+
+class CoverageConfig(object):
+ """Coverage.py configuration.
+
+ The attributes of this class are the various settings that control the
+ operation of coverage.py.
+
+ """
+ # pylint: disable=too-many-instance-attributes
+
+ def __init__(self):
+ """Initialize the configuration attributes to their defaults."""
+ # Metadata about the config.
+ # We tried to read these config files.
+ self.attempted_config_files = []
+ # We did read these config files, but maybe didn't find any content for us.
+ self.config_files_read = []
+ # The file that gave us our configuration.
+ self.config_file = None
+ self._config_contents = None
+
+ # Defaults for [run] and [report]
+ self._include = None
+ self._omit = None
+
+ # Defaults for [run]
+ self.branch = False
+ self.command_line = None
+ self.concurrency = None
+ self.context = None
+ self.cover_pylib = False
+ self.data_file = ".coverage"
+ self.debug = []
+ self.disable_warnings = []
+ self.dynamic_context = None
+ self.note = None
+ self.parallel = False
+ self.plugins = []
+ self.relative_files = False
+ self.run_include = None
+ self.run_omit = None
+ self.source = None
+ self.source_pkgs = []
+ self.timid = False
+ self._crash = None
+
+ # Defaults for [report]
+ self.exclude_list = DEFAULT_EXCLUDE[:]
+ self.fail_under = 0.0
+ self.ignore_errors = False
+ self.report_include = None
+ self.report_omit = None
+ self.partial_always_list = DEFAULT_PARTIAL_ALWAYS[:]
+ self.partial_list = DEFAULT_PARTIAL[:]
+ self.precision = 0
+ self.report_contexts = None
+ self.show_missing = False
+ self.skip_covered = False
+ self.skip_empty = False
+ self.sort = None
+
+ # Defaults for [html]
+ self.extra_css = None
+ self.html_dir = "htmlcov"
+ self.html_skip_covered = None
+ self.html_skip_empty = None
+ self.html_title = "Coverage report"
+ self.show_contexts = False
+
+ # Defaults for [xml]
+ self.xml_output = "coverage.xml"
+ self.xml_package_depth = 99
+
+ # Defaults for [json]
+ self.json_output = "coverage.json"
+ self.json_pretty_print = False
+ self.json_show_contexts = False
+
+ # Defaults for [paths]
+ self.paths = collections.OrderedDict()
+
+ # Options for plugins
+ self.plugin_options = {}
+ self.suppress_plugin_errors = True
+
+ MUST_BE_LIST = [
+ "debug", "concurrency", "plugins",
+ "report_omit", "report_include",
+ "run_omit", "run_include",
+ ]
+
+ def from_args(self, **kwargs):
+ """Read config values from `kwargs`."""
+ for k, v in iitems(kwargs):
+ if v is not None:
+ if k in self.MUST_BE_LIST and isinstance(v, string_class):
+ v = [v]
+ setattr(self, k, v)
+
+ def from_resource(self, resource_name):
+ assert getattr(sys, 'is_standalone_binary', False), 'You have used method by mistake in script, not binary'
+ cp, self._config_contents = _load_config_from_resource(resource_name)
+ return self._parse_config(cp, resource_name, True)
+
+ @contract(filename=str)
+ def from_file(self, filename, our_file):
+ """Read configuration from a .rc file.
+
+ `filename` is a file name to read.
+
+ `our_file` is True if this config file is specifically for coverage,
+ False if we are examining another config file (tox.ini, setup.cfg)
+ for possible settings.
+
+ Returns True or False, whether the file could be read, and it had some
+ coverage.py settings in it.
+
+ """
+ _, ext = os.path.splitext(filename)
+ if ext == '.toml':
+ cp = TomlConfigParser(our_file)
+ else:
+ cp = HandyConfigParser(our_file)
+
+ self.attempted_config_files.append(filename)
+
+ try:
+ files_read = cp.read(filename)
+ except (configparser.Error, TomlDecodeError) as err:
+ raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
+ if not files_read:
+ return False
+
+ self.config_files_read.extend(map(os.path.abspath, files_read))
+
+ return self._parse_config(cp, filename, our_file)
+
+ def _parse_config(self, cp, filename, our_file):
+ any_set = False
+ try:
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ was_set = self._set_attr_from_config_option(cp, *option_spec)
+ if was_set:
+ any_set = True
+ except ValueError as err:
+ raise CoverageException("Couldn't read config file %s: %s" % (filename, err))
+
+ # Check that there are no unrecognized options.
+ all_options = collections.defaultdict(set)
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ section, option = option_spec[1].split(":")
+ all_options[section].add(option)
+
+ for section, options in iitems(all_options):
+ real_section = cp.has_section(section)
+ if real_section:
+ for unknown in set(cp.options(section)) - options:
+ raise CoverageException(
+ "Unrecognized option '[%s] %s=' in config file %s" % (
+ real_section, unknown, filename
+ )
+ )
+
+ # [paths] is special
+ if cp.has_section('paths'):
+ for option in cp.options('paths'):
+ self.paths[option] = cp.getlist('paths', option)
+ any_set = True
+
+ # plugins can have options
+ for plugin in self.plugins:
+ if cp.has_section(plugin):
+ self.plugin_options[plugin] = cp.get_section(plugin)
+ any_set = True
+
+ # Was this file used as a config file? If it's specifically our file,
+ # then it was used. If we're piggybacking on someone else's file,
+ # then it was only used if we found some settings in it.
+ if our_file:
+ used = True
+ else:
+ used = any_set
+
+ if used:
+ self.config_file = os.path.abspath(filename)
+ if not getattr(sys, 'is_standalone_binary', False):
+ with open(filename, "rb") as f:
+ self._config_contents = f.read()
+
+ return used
+
+ def copy(self):
+ """Return a copy of the configuration."""
+ return copy.deepcopy(self)
+
+ CONFIG_FILE_OPTIONS = [
+ # These are *args for _set_attr_from_config_option:
+ # (attr, where, type_="")
+ #
+ # attr is the attribute to set on the CoverageConfig object.
+ # where is the section:name to read from the configuration file.
+ # type_ is the optional type to apply, by using .getTYPE to read the
+ # configuration value from the file.
+
+ # [run]
+ ('branch', 'run:branch', 'boolean'),
+ ('command_line', 'run:command_line'),
+ ('concurrency', 'run:concurrency', 'list'),
+ ('context', 'run:context'),
+ ('cover_pylib', 'run:cover_pylib', 'boolean'),
+ ('data_file', 'run:data_file'),
+ ('debug', 'run:debug', 'list'),
+ ('disable_warnings', 'run:disable_warnings', 'list'),
+ ('dynamic_context', 'run:dynamic_context'),
+ ('note', 'run:note'),
+ ('parallel', 'run:parallel', 'boolean'),
+ ('plugins', 'run:plugins', 'list'),
+ ('relative_files', 'run:relative_files', 'boolean'),
+ ('run_include', 'run:include', 'list'),
+ ('run_omit', 'run:omit', 'list'),
+ ('source', 'run:source', 'list'),
+ ('source_pkgs', 'run:source_pkgs', 'list'),
+ ('timid', 'run:timid', 'boolean'),
+ ('_crash', 'run:_crash'),
+ ('suppress_plugin_errors', 'run:suppress_plugin_errors', 'boolean'),
+
+ # [report]
+ ('exclude_list', 'report:exclude_lines', 'regexlist'),
+ ('fail_under', 'report:fail_under', 'float'),
+ ('ignore_errors', 'report:ignore_errors', 'boolean'),
+ ('partial_always_list', 'report:partial_branches_always', 'regexlist'),
+ ('partial_list', 'report:partial_branches', 'regexlist'),
+ ('precision', 'report:precision', 'int'),
+ ('report_contexts', 'report:contexts', 'list'),
+ ('report_include', 'report:include', 'list'),
+ ('report_omit', 'report:omit', 'list'),
+ ('show_missing', 'report:show_missing', 'boolean'),
+ ('skip_covered', 'report:skip_covered', 'boolean'),
+ ('skip_empty', 'report:skip_empty', 'boolean'),
+ ('sort', 'report:sort'),
+
+ # [html]
+ ('extra_css', 'html:extra_css'),
+ ('html_dir', 'html:directory'),
+ ('html_skip_covered', 'html:skip_covered', 'boolean'),
+ ('html_skip_empty', 'html:skip_empty', 'boolean'),
+ ('html_title', 'html:title'),
+ ('show_contexts', 'html:show_contexts', 'boolean'),
+
+ # [xml]
+ ('xml_output', 'xml:output'),
+ ('xml_package_depth', 'xml:package_depth', 'int'),
+
+ # [json]
+ ('json_output', 'json:output'),
+ ('json_pretty_print', 'json:pretty_print', 'boolean'),
+ ('json_show_contexts', 'json:show_contexts', 'boolean'),
+ ]
+
+ def _set_attr_from_config_option(self, cp, attr, where, type_=''):
+ """Set an attribute on self if it exists in the ConfigParser.
+
+ Returns True if the attribute was set.
+
+ """
+ section, option = where.split(":")
+ if cp.has_option(section, option):
+ method = getattr(cp, 'get' + type_)
+ setattr(self, attr, method(section, option))
+ return True
+ return False
+
+ def get_plugin_options(self, plugin):
+ """Get a dictionary of options for the plugin named `plugin`."""
+ return self.plugin_options.get(plugin, {})
+
+ def set_option(self, option_name, value):
+ """Set an option in the configuration.
+
+ `option_name` is a colon-separated string indicating the section and
+ option name. For example, the ``branch`` option in the ``[run]``
+ section of the config file would be indicated with `"run:branch"`.
+
+ `value` is the new value for the option.
+
+ """
+ # Special-cased options.
+ if option_name == "paths":
+ self.paths = value
+ return
+
+ # Check all the hard-coded options.
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ attr, where = option_spec[:2]
+ if where == option_name:
+ setattr(self, attr, value)
+ return
+
+ # See if it's a plugin option.
+ plugin_name, _, key = option_name.partition(":")
+ if key and plugin_name in self.plugins:
+ self.plugin_options.setdefault(plugin_name, {})[key] = value
+ return
+
+ # If we get here, we didn't find the option.
+ raise CoverageException("No such option: %r" % option_name)
+
+ def get_option(self, option_name):
+ """Get an option from the configuration.
+
+ `option_name` is a colon-separated string indicating the section and
+ option name. For example, the ``branch`` option in the ``[run]``
+ section of the config file would be indicated with `"run:branch"`.
+
+ Returns the value of the option.
+
+ """
+ # Special-cased options.
+ if option_name == "paths":
+ return self.paths
+
+ # Check all the hard-coded options.
+ for option_spec in self.CONFIG_FILE_OPTIONS:
+ attr, where = option_spec[:2]
+ if where == option_name:
+ return getattr(self, attr)
+
+ # See if it's a plugin option.
+ plugin_name, _, key = option_name.partition(":")
+ if key and plugin_name in self.plugins:
+ return self.plugin_options.get(plugin_name, {}).get(key)
+
+ # If we get here, we didn't find the option.
+ raise CoverageException("No such option: %r" % option_name)
+
+ def post_process_file(self, path):
+ """Make final adjustments to a file path to make it usable."""
+ return os.path.expanduser(path)
+
+ def post_process(self):
+ """Make final adjustments to settings to make them usable."""
+ self.data_file = self.post_process_file(self.data_file)
+ self.html_dir = self.post_process_file(self.html_dir)
+ self.xml_output = self.post_process_file(self.xml_output)
+ self.paths = collections.OrderedDict(
+ (k, [self.post_process_file(f) for f in v])
+ for k, v in self.paths.items()
+ )
+
+
+def config_files_to_try(config_file):
+ """What config files should we try to read?
+
+ Returns a list of tuples:
+ (filename, is_our_file, was_file_specified)
+ """
+
+ # Some API users were specifying ".coveragerc" to mean the same as
+ # True, so make it so.
+ if config_file == ".coveragerc":
+ config_file = True
+ specified_file = (config_file is not True)
+ if not specified_file:
+ # No file was specified. Check COVERAGE_RCFILE.
+ config_file = os.environ.get('COVERAGE_RCFILE')
+ if config_file:
+ specified_file = True
+ if not specified_file:
+ # Still no file specified. Default to .coveragerc
+ config_file = ".coveragerc"
+ files_to_try = [
+ (config_file, True, specified_file),
+ ("setup.cfg", False, False),
+ ("tox.ini", False, False),
+ ("pyproject.toml", False, False),
+ ]
+ return files_to_try
+
+
+def read_coverage_config(config_file, **kwargs):
+ """Read the coverage.py configuration.
+
+ Arguments:
+ config_file: a boolean or string, see the `Coverage` class for the
+ tricky details.
+ all others: keyword arguments from the `Coverage` class, used for
+ setting values in the configuration.
+
+ Returns:
+ config:
+ config is a CoverageConfig object read from the appropriate
+ configuration file.
+
+ """
+ # Build the configuration from a number of sources:
+ # 1) defaults:
+ config = CoverageConfig()
+
+ # 1.1 built-in config
+ if getattr(sys, 'is_standalone_binary', False):
+ config.from_resource("/coverage_plugins/coveragerc.txt")
+
+ # 2) from a file:
+ if config_file:
+ files_to_try = config_files_to_try(config_file)
+
+ for fname, our_file, specified_file in files_to_try:
+ if getattr(sys, 'is_standalone_binary', False) and fname == "/coverage_plugins/coveragerc.txt":
+ continue
+ config_read = config.from_file(fname, our_file=our_file)
+ if config_read:
+ break
+ if specified_file:
+ raise CoverageException("Couldn't read '%s' as a config file" % fname)
+
+ # $set_env.py: COVERAGE_DEBUG - Options for --debug.
+ # 3) from environment variables:
+ env_data_file = os.environ.get('COVERAGE_FILE')
+ if env_data_file:
+ config.data_file = env_data_file
+ debugs = os.environ.get('COVERAGE_DEBUG')
+ if debugs:
+ config.debug.extend(d.strip() for d in debugs.split(","))
+
+ # 4) from constructor arguments:
+ config.from_args(**kwargs)
+
+ # Once all the config has been collected, there's a little post-processing
+ # to do.
+ config.post_process()
+
+ return config
+
+
+def _load_config_from_resource(resource_name):
+ from io import StringIO
+ from library.python import resource
+
+ config_data = resource.find(resource_name)
+ if config_data is None:
+ raise IOError("No such resource: " + resource_name)
+
+ config_data = config_data.decode('utf-8')
+ cp = HandyConfigParser(True)
+ try:
+ cp.readfp(StringIO(config_data))
+ except configparser.Error as err:
+ raise CoverageException("Couldn't read config %s: %s" % (resource_name, err))
+ return cp, config_data
diff --git a/contrib/python/coverage/py3/coverage/context.py b/contrib/python/coverage/py3/coverage/context.py
new file mode 100644
index 0000000000..ea13da21ed
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/context.py
@@ -0,0 +1,91 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Determine contexts for coverage.py"""
+
+
+def combine_context_switchers(context_switchers):
+ """Create a single context switcher from multiple switchers.
+
+ `context_switchers` is a list of functions that take a frame as an
+ argument and return a string to use as the new context label.
+
+ Returns a function that composites `context_switchers` functions, or None
+ if `context_switchers` is an empty list.
+
+ When invoked, the combined switcher calls `context_switchers` one-by-one
+ until a string is returned. The combined switcher returns None if all
+ `context_switchers` return None.
+ """
+ if not context_switchers:
+ return None
+
+ if len(context_switchers) == 1:
+ return context_switchers[0]
+
+ def should_start_context(frame):
+ """The combiner for multiple context switchers."""
+ for switcher in context_switchers:
+ new_context = switcher(frame)
+ if new_context is not None:
+ return new_context
+ return None
+
+ return should_start_context
+
+
+def should_start_context_test_function(frame):
+ """Is this frame calling a test_* function?"""
+ co_name = frame.f_code.co_name
+ if co_name.startswith("test") or co_name == "runTest":
+ return qualname_from_frame(frame)
+ return None
+
+
+def qualname_from_frame(frame):
+ """Get a qualified name for the code running in `frame`."""
+ co = frame.f_code
+ fname = co.co_name
+ method = None
+ if co.co_argcount and co.co_varnames[0] == "self":
+ self = frame.f_locals["self"]
+ method = getattr(self, fname, None)
+
+ if method is None:
+ func = frame.f_globals.get(fname)
+ if func is None:
+ return None
+ return func.__module__ + '.' + fname
+
+ func = getattr(method, '__func__', None)
+ if func is None:
+ cls = self.__class__
+ return cls.__module__ + '.' + cls.__name__ + "." + fname
+
+ if hasattr(func, '__qualname__'):
+ qname = func.__module__ + '.' + func.__qualname__
+ else:
+ for cls in getattr(self.__class__, '__mro__', ()):
+ f = cls.__dict__.get(fname, None)
+ if f is None:
+ continue
+ if f is func:
+ qname = cls.__module__ + '.' + cls.__name__ + "." + fname
+ break
+ else:
+ # Support for old-style classes.
+ def mro(bases):
+ for base in bases:
+ f = base.__dict__.get(fname, None)
+ if f is func:
+ return base.__module__ + '.' + base.__name__ + "." + fname
+ for base in bases:
+ qname = mro(base.__bases__)
+ if qname is not None:
+ return qname
+ return None
+ qname = mro([self.__class__])
+ if qname is None:
+ qname = func.__module__ + '.' + fname
+
+ return qname
diff --git a/contrib/python/coverage/py3/coverage/control.py b/contrib/python/coverage/py3/coverage/control.py
new file mode 100644
index 0000000000..605b50c26b
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/control.py
@@ -0,0 +1,1162 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Core control stuff for coverage.py."""
+
+import atexit
+import collections
+import contextlib
+import os
+import os.path
+import platform
+import sys
+import time
+import json
+
+from coverage import env
+from coverage.annotate import AnnotateReporter
+from coverage.backward import string_class, iitems
+from coverage.collector import Collector, CTracer
+from coverage.config import read_coverage_config
+from coverage.context import should_start_context_test_function, combine_context_switchers
+from coverage.data import CoverageData, combine_parallel_data
+from coverage.debug import DebugControl, short_stack, write_formatted_info
+from coverage.disposition import disposition_debug_msg
+from coverage.files import PathAliases, abs_file, canonical_filename, relative_filename, set_relative_directory
+from coverage.html import HtmlReporter
+from coverage.inorout import InOrOut
+from coverage.jsonreport import JsonReporter
+from coverage.misc import CoverageException, bool_or_none, join_regex
+from coverage.misc import DefaultValue, ensure_dir_for_file, isolate_module
+from coverage.plugin import FileReporter
+from coverage.plugin_support import Plugins
+from coverage.python import PythonFileReporter
+from coverage.report import render_report
+from coverage.results import Analysis, Numbers
+from coverage.summary import SummaryReporter
+from coverage.xmlreport import XmlReporter
+
+try:
+ from coverage.multiproc import patch_multiprocessing
+except ImportError: # pragma: only jython
+ # Jython has no multiprocessing module.
+ patch_multiprocessing = None
+
+os = isolate_module(os)
+
+@contextlib.contextmanager
+def override_config(cov, **kwargs):
+ """Temporarily tweak the configuration of `cov`.
+
+ The arguments are applied to `cov.config` with the `from_args` method.
+ At the end of the with-statement, the old configuration is restored.
+ """
+ original_config = cov.config
+ cov.config = cov.config.copy()
+ try:
+ cov.config.from_args(**kwargs)
+ yield
+ finally:
+ cov.config = original_config
+
+
+_DEFAULT_DATAFILE = DefaultValue("MISSING")
+
+class Coverage(object):
+ """Programmatic access to coverage.py.
+
+ To use::
+
+ from coverage import Coverage
+
+ cov = Coverage()
+ cov.start()
+ #.. call your code ..
+ cov.stop()
+ cov.html_report(directory='covhtml')
+
+ Note: in keeping with Python custom, names starting with underscore are
+ not part of the public API. They might stop working at any point. Please
+ limit yourself to documented methods to avoid problems.
+
+ """
+
+ # The stack of started Coverage instances.
+ _instances = []
+
+ @classmethod
+ def current(cls):
+ """Get the latest started `Coverage` instance, if any.
+
+ Returns: a `Coverage` instance, or None.
+
+ .. versionadded:: 5.0
+
+ """
+ if cls._instances:
+ return cls._instances[-1]
+ else:
+ return None
+
+ def __init__(
+ self, data_file=_DEFAULT_DATAFILE, data_suffix=None, cover_pylib=None,
+ auto_data=False, timid=None, branch=None, config_file=True,
+ source=None, source_pkgs=None, omit=None, include=None, debug=None,
+ concurrency=None, check_preimported=False, context=None,
+ ): # pylint: disable=too-many-arguments
+ """
+ Many of these arguments duplicate and override values that can be
+ provided in a configuration file. Parameters that are missing here
+ will use values from the config file.
+
+ `data_file` is the base name of the data file to use. The config value
+ defaults to ".coverage". None can be provided to prevent writing a data
+ file. `data_suffix` is appended (with a dot) to `data_file` to create
+ the final file name. If `data_suffix` is simply True, then a suffix is
+ created with the machine and process identity included.
+
+ `cover_pylib` is a boolean determining whether Python code installed
+ with the Python interpreter is measured. This includes the Python
+ standard library and any packages installed with the interpreter.
+
+ If `auto_data` is true, then any existing data file will be read when
+ coverage measurement starts, and data will be saved automatically when
+ measurement stops.
+
+ If `timid` is true, then a slower and simpler trace function will be
+ used. This is important for some environments where manipulation of
+ tracing functions breaks the faster trace function.
+
+ If `branch` is true, then branch coverage will be measured in addition
+ to the usual statement coverage.
+
+ `config_file` determines what configuration file to read:
+
+ * If it is ".coveragerc", it is interpreted as if it were True,
+ for backward compatibility.
+
+ * If it is a string, it is the name of the file to read. If the
+ file can't be read, it is an error.
+
+ * If it is True, then a few standard files names are tried
+ (".coveragerc", "setup.cfg", "tox.ini"). It is not an error for
+ these files to not be found.
+
+ * If it is False, then no configuration file is read.
+
+ `source` is a list of file paths or package names. Only code located
+ in the trees indicated by the file paths or package names will be
+ measured.
+
+ `source_pkgs` is a list of package names. It works the same as
+ `source`, but can be used to name packages where the name can also be
+ interpreted as a file path.
+
+ `include` and `omit` are lists of file name patterns. Files that match
+ `include` will be measured, files that match `omit` will not. Each
+ will also accept a single string argument.
+
+ `debug` is a list of strings indicating what debugging information is
+ desired.
+
+ `concurrency` is a string indicating the concurrency library being used
+ in the measured code. Without this, coverage.py will get incorrect
+ results if these libraries are in use. Valid strings are "greenlet",
+ "eventlet", "gevent", "multiprocessing", or "thread" (the default).
+ This can also be a list of these strings.
+
+ If `check_preimported` is true, then when coverage is started, the
+ already-imported files will be checked to see if they should be
+ measured by coverage. Importing measured files before coverage is
+ started can mean that code is missed.
+
+ `context` is a string to use as the :ref:`static context
+ <static_contexts>` label for collected data.
+
+ .. versionadded:: 4.0
+ The `concurrency` parameter.
+
+ .. versionadded:: 4.2
+ The `concurrency` parameter can now be a list of strings.
+
+ .. versionadded:: 5.0
+ The `check_preimported` and `context` parameters.
+
+ .. versionadded:: 5.3
+ The `source_pkgs` parameter.
+
+ """
+ # data_file=None means no disk file at all. data_file missing means
+ # use the value from the config file.
+ self._no_disk = data_file is None
+ if data_file is _DEFAULT_DATAFILE:
+ data_file = None
+
+ # Build our configuration from a number of sources.
+ self.config = read_coverage_config(
+ config_file=config_file,
+ data_file=data_file, cover_pylib=cover_pylib, timid=timid,
+ branch=branch, parallel=bool_or_none(data_suffix),
+ source=source, source_pkgs=source_pkgs, run_omit=omit, run_include=include, debug=debug,
+ report_omit=omit, report_include=include,
+ concurrency=concurrency, context=context,
+ )
+
+ # This is injectable by tests.
+ self._debug_file = None
+
+ self._auto_load = self._auto_save = auto_data
+ self._data_suffix_specified = data_suffix
+
+ # Is it ok for no data to be collected?
+ self._warn_no_data = True
+ self._warn_unimported_source = True
+ self._warn_preimported_source = check_preimported
+ self._no_warn_slugs = None
+
+ # A record of all the warnings that have been issued.
+ self._warnings = []
+
+ # Other instance attributes, set later.
+ self._data = self._collector = None
+ self._plugins = None
+ self._inorout = None
+ self._data_suffix = self._run_suffix = None
+ self._exclude_re = None
+ self._debug = None
+ self._file_mapper = None
+
+ # State machine variables:
+ # Have we initialized everything?
+ self._inited = False
+ self._inited_for_start = False
+ # Have we started collecting and not stopped it?
+ self._started = False
+ # Should we write the debug output?
+ self._should_write_debug = True
+
+ # If we have sub-process measurement happening automatically, then we
+ # want any explicit creation of a Coverage object to mean, this process
+ # is already coverage-aware, so don't auto-measure it. By now, the
+ # auto-creation of a Coverage object has already happened. But we can
+ # find it and tell it not to save its data.
+ if not env.METACOV:
+ _prevent_sub_process_measurement()
+
+ # Store constructor args to reproduce Coverage object in a subprocess created via multiprocessing.Process
+ self._dumped_args = json.dumps(dict(
+ data_file=data_file, data_suffix=data_suffix, cover_pylib=cover_pylib,
+ auto_data=auto_data, timid=timid, branch=branch, config_file=config_file,
+ source=source, omit=omit, include=include, debug=debug,
+ concurrency=concurrency
+ ))
+
+ def _init(self):
+ """Set all the initial state.
+
+ This is called by the public methods to initialize state. This lets us
+ construct a :class:`Coverage` object, then tweak its state before this
+ function is called.
+
+ """
+ if self._inited:
+ return
+
+ self._inited = True
+
+ # Create and configure the debugging controller. COVERAGE_DEBUG_FILE
+ # is an environment variable, the name of a file to append debug logs
+ # to.
+ self._debug = DebugControl(self.config.debug, self._debug_file)
+
+ if "multiprocessing" in (self.config.concurrency or ()):
+ # Multi-processing uses parallel for the subprocesses, so also use
+ # it for the main process.
+ self.config.parallel = True
+
+ # _exclude_re is a dict that maps exclusion list names to compiled regexes.
+ self._exclude_re = {}
+
+ set_relative_directory()
+
+ if getattr(sys, 'is_standalone_binary', False):
+ self._file_mapper = canonical_filename
+ else:
+ self._file_mapper = relative_filename if self.config.relative_files else abs_file
+
+ # Load plugins
+ self._plugins = Plugins.load_plugins(self.config.plugins, self.config, self._debug)
+
+ # Run configuring plugins.
+ for plugin in self._plugins.configurers:
+ # We need an object with set_option and get_option. Either self or
+ # self.config will do. Choosing randomly stops people from doing
+ # other things with those objects, against the public API. Yes,
+ # this is a bit childish. :)
+ plugin.configure([self, self.config][int(time.time()) % 2])
+
+ def _post_init(self):
+ """Stuff to do after everything is initialized."""
+ if self._should_write_debug:
+ self._should_write_debug = False
+ self._write_startup_debug()
+
+ # '[run] _crash' will raise an exception if the value is close by in
+ # the call stack, for testing error handling.
+ if self.config._crash and self.config._crash in short_stack(limit=4):
+ raise Exception("Crashing because called by {}".format(self.config._crash))
+
+ def _write_startup_debug(self):
+ """Write out debug info at startup if needed."""
+ wrote_any = False
+ with self._debug.without_callers():
+ if self._debug.should('config'):
+ config_info = sorted(self.config.__dict__.items())
+ config_info = [(k, v) for k, v in config_info if not k.startswith('_')]
+ write_formatted_info(self._debug, "config", config_info)
+ wrote_any = True
+
+ if self._debug.should('sys'):
+ write_formatted_info(self._debug, "sys", self.sys_info())
+ for plugin in self._plugins:
+ header = "sys: " + plugin._coverage_plugin_name
+ info = plugin.sys_info()
+ write_formatted_info(self._debug, header, info)
+ wrote_any = True
+
+ if wrote_any:
+ write_formatted_info(self._debug, "end", ())
+
+ def _should_trace(self, filename, frame):
+ """Decide whether to trace execution in `filename`.
+
+ Calls `_should_trace_internal`, and returns the FileDisposition.
+
+ """
+ disp = self._inorout.should_trace(filename, frame)
+ if self._debug.should('trace'):
+ self._debug.write(disposition_debug_msg(disp))
+ return disp
+
+ def _check_include_omit_etc(self, filename, frame):
+ """Check a file name against the include/omit/etc, rules, verbosely.
+
+ Returns a boolean: True if the file should be traced, False if not.
+
+ """
+ reason = self._inorout.check_include_omit_etc(filename, frame)
+ if self._debug.should('trace'):
+ if not reason:
+ msg = "Including %r" % (filename,)
+ else:
+ msg = "Not including %r: %s" % (filename, reason)
+ self._debug.write(msg)
+
+ return not reason
+
+ def _warn(self, msg, slug=None, once=False):
+ """Use `msg` as a warning.
+
+ For warning suppression, use `slug` as the shorthand.
+
+ If `once` is true, only show this warning once (determined by the
+ slug.)
+
+ """
+ if self._no_warn_slugs is None:
+ self._no_warn_slugs = list(self.config.disable_warnings)
+
+ if slug in self._no_warn_slugs:
+ # Don't issue the warning
+ return
+
+ self._warnings.append(msg)
+ if slug:
+ msg = "%s (%s)" % (msg, slug)
+ if self._debug.should('pid'):
+ msg = "[%d] %s" % (os.getpid(), msg)
+ sys.stderr.write("Coverage.py warning: %s\n" % msg)
+
+ if once:
+ self._no_warn_slugs.append(slug)
+
+ def get_option(self, option_name):
+ """Get an option from the configuration.
+
+ `option_name` is a colon-separated string indicating the section and
+ option name. For example, the ``branch`` option in the ``[run]``
+ section of the config file would be indicated with `"run:branch"`.
+
+ Returns the value of the option. The type depends on the option
+ selected.
+
+ As a special case, an `option_name` of ``"paths"`` will return an
+ OrderedDict with the entire ``[paths]`` section value.
+
+ .. versionadded:: 4.0
+
+ """
+ return self.config.get_option(option_name)
+
+ def set_option(self, option_name, value):
+ """Set an option in the configuration.
+
+ `option_name` is a colon-separated string indicating the section and
+ option name. For example, the ``branch`` option in the ``[run]``
+ section of the config file would be indicated with ``"run:branch"``.
+
+ `value` is the new value for the option. This should be an
+ appropriate Python value. For example, use True for booleans, not the
+ string ``"True"``.
+
+ As an example, calling::
+
+ cov.set_option("run:branch", True)
+
+ has the same effect as this configuration file::
+
+ [run]
+ branch = True
+
+ As a special case, an `option_name` of ``"paths"`` will replace the
+ entire ``[paths]`` section. The value should be an OrderedDict.
+
+ .. versionadded:: 4.0
+
+ """
+ self.config.set_option(option_name, value)
+
+ def load(self):
+ """Load previously-collected coverage data from the data file."""
+ self._init()
+ if self._collector:
+ self._collector.reset()
+ should_skip = self.config.parallel and not os.path.exists(self.config.data_file)
+ if not should_skip:
+ self._init_data(suffix=None)
+ self._post_init()
+ if not should_skip:
+ self._data.read()
+
+ def _init_for_start(self):
+ """Initialization for start()"""
+ # Construct the collector.
+ concurrency = self.config.concurrency or ()
+ if "multiprocessing" in concurrency:
+ if not patch_multiprocessing:
+ raise CoverageException( # pragma: only jython
+ "multiprocessing is not supported on this Python"
+ )
+ patch_multiprocessing(rcfile=self.config.config_file, coverage_args=self._dumped_args)
+
+ dycon = self.config.dynamic_context
+ if not dycon or dycon == "none":
+ context_switchers = []
+ elif dycon == "test_function":
+ context_switchers = [should_start_context_test_function]
+ else:
+ raise CoverageException(
+ "Don't understand dynamic_context setting: {!r}".format(dycon)
+ )
+
+ context_switchers.extend(
+ plugin.dynamic_context for plugin in self._plugins.context_switchers
+ )
+
+ should_start_context = combine_context_switchers(context_switchers)
+
+ self._collector = Collector(
+ should_trace=self._should_trace,
+ check_include=self._check_include_omit_etc,
+ should_start_context=should_start_context,
+ file_mapper=self._file_mapper,
+ timid=self.config.timid,
+ branch=self.config.branch,
+ warn=self._warn,
+ concurrency=concurrency,
+ )
+
+ suffix = self._data_suffix_specified
+ if suffix or self.config.parallel:
+ if not isinstance(suffix, string_class):
+ # if data_suffix=True, use .machinename.pid.random
+ suffix = True
+ else:
+ suffix = None
+
+ self._init_data(suffix)
+
+ self._collector.use_data(self._data, self.config.context)
+
+ # Early warning if we aren't going to be able to support plugins.
+ if self._plugins.file_tracers and not self._collector.supports_plugins:
+ self._warn(
+ "Plugin file tracers (%s) aren't supported with %s" % (
+ ", ".join(
+ plugin._coverage_plugin_name
+ for plugin in self._plugins.file_tracers
+ ),
+ self._collector.tracer_name(),
+ )
+ )
+ for plugin in self._plugins.file_tracers:
+ plugin._coverage_enabled = False
+
+ # Create the file classifying substructure.
+ self._inorout = InOrOut(
+ warn=self._warn,
+ debug=(self._debug if self._debug.should('trace') else None),
+ )
+ self._inorout.configure(self.config)
+ self._inorout.plugins = self._plugins
+ self._inorout.disp_class = self._collector.file_disposition_class
+
+ # It's useful to write debug info after initing for start.
+ self._should_write_debug = True
+
+ atexit.register(self._atexit)
+
+ def _init_data(self, suffix):
+ """Create a data file if we don't have one yet."""
+ if self._data is None:
+ # Create the data file. We do this at construction time so that the
+ # data file will be written into the directory where the process
+ # started rather than wherever the process eventually chdir'd to.
+ ensure_dir_for_file(self.config.data_file)
+ self._data = CoverageData(
+ basename=self.config.data_file,
+ suffix=suffix,
+ warn=self._warn,
+ debug=self._debug,
+ no_disk=self._no_disk,
+ )
+
+ def start(self):
+ """Start measuring code coverage.
+
+ Coverage measurement only occurs in functions called after
+ :meth:`start` is invoked. Statements in the same scope as
+ :meth:`start` won't be measured.
+
+ Once you invoke :meth:`start`, you must also call :meth:`stop`
+ eventually, or your process might not shut down cleanly.
+
+ """
+ self._init()
+ if not self._inited_for_start:
+ self._inited_for_start = True
+ self._init_for_start()
+ self._post_init()
+
+ # Issue warnings for possible problems.
+ self._inorout.warn_conflicting_settings()
+
+ # See if we think some code that would eventually be measured has
+ # already been imported.
+ if self._warn_preimported_source:
+ self._inorout.warn_already_imported_files()
+
+ if self._auto_load:
+ self.load()
+
+ self._collector.start()
+ self._started = True
+ self._instances.append(self)
+
+ def stop(self):
+ """Stop measuring code coverage."""
+ if self._instances:
+ if self._instances[-1] is self:
+ self._instances.pop()
+ if self._started:
+ self._collector.stop()
+ self._started = False
+
+ def _atexit(self):
+ """Clean up on process shutdown."""
+ if self._debug.should("process"):
+ self._debug.write("atexit: pid: {}, instance: {!r}".format(os.getpid(), self))
+ if self._started:
+ self.stop()
+ if self._auto_save:
+ self.save()
+
+ def erase(self):
+ """Erase previously collected coverage data.
+
+ This removes the in-memory data collected in this session as well as
+ discarding the data file.
+
+ """
+ self._init()
+ self._post_init()
+ if self._collector:
+ self._collector.reset()
+ self._init_data(suffix=None)
+ self._data.erase(parallel=self.config.parallel)
+ self._data = None
+ self._inited_for_start = False
+
+ def switch_context(self, new_context):
+ """Switch to a new dynamic context.
+
+ `new_context` is a string to use as the :ref:`dynamic context
+ <dynamic_contexts>` label for collected data. If a :ref:`static
+ context <static_contexts>` is in use, the static and dynamic context
+ labels will be joined together with a pipe character.
+
+ Coverage collection must be started already.
+
+ .. versionadded:: 5.0
+
+ """
+ if not self._started: # pragma: part started
+ raise CoverageException(
+ "Cannot switch context, coverage is not started"
+ )
+
+ if self._collector.should_start_context:
+ self._warn("Conflicting dynamic contexts", slug="dynamic-conflict", once=True)
+
+ self._collector.switch_context(new_context)
+
+ def clear_exclude(self, which='exclude'):
+ """Clear the exclude list."""
+ self._init()
+ setattr(self.config, which + "_list", [])
+ self._exclude_regex_stale()
+
+ def exclude(self, regex, which='exclude'):
+ """Exclude source lines from execution consideration.
+
+ A number of lists of regular expressions are maintained. Each list
+ selects lines that are treated differently during reporting.
+
+ `which` determines which list is modified. The "exclude" list selects
+ lines that are not considered executable at all. The "partial" list
+ indicates lines with branches that are not taken.
+
+ `regex` is a regular expression. The regex is added to the specified
+ list. If any of the regexes in the list is found in a line, the line
+ is marked for special treatment during reporting.
+
+ """
+ self._init()
+ excl_list = getattr(self.config, which + "_list")
+ excl_list.append(regex)
+ self._exclude_regex_stale()
+
+ def _exclude_regex_stale(self):
+ """Drop all the compiled exclusion regexes, a list was modified."""
+ self._exclude_re.clear()
+
+ def _exclude_regex(self, which):
+ """Return a compiled regex for the given exclusion list."""
+ if which not in self._exclude_re:
+ excl_list = getattr(self.config, which + "_list")
+ self._exclude_re[which] = join_regex(excl_list)
+ return self._exclude_re[which]
+
+ def get_exclude_list(self, which='exclude'):
+ """Return a list of excluded regex patterns.
+
+ `which` indicates which list is desired. See :meth:`exclude` for the
+ lists that are available, and their meaning.
+
+ """
+ self._init()
+ return getattr(self.config, which + "_list")
+
+ def save(self):
+ """Save the collected coverage data to the data file."""
+ data = self.get_data()
+ data.write()
+
+ def combine(self, data_paths=None, strict=False, keep=False):
+ """Combine together a number of similarly-named coverage data files.
+
+ All coverage data files whose name starts with `data_file` (from the
+ coverage() constructor) will be read, and combined together into the
+ current measurements.
+
+ `data_paths` is a list of files or directories from which data should
+ be combined. If no list is passed, then the data files from the
+ directory indicated by the current data file (probably the current
+ directory) will be combined.
+
+ If `strict` is true, then it is an error to attempt to combine when
+ there are no data files to combine.
+
+ If `keep` is true, then original input data files won't be deleted.
+
+ .. versionadded:: 4.0
+ The `data_paths` parameter.
+
+ .. versionadded:: 4.3
+ The `strict` parameter.
+
+ .. versionadded: 5.5
+ The `keep` parameter.
+ """
+ self._init()
+ self._init_data(suffix=None)
+ self._post_init()
+ self.get_data()
+
+ aliases = None
+ if self.config.paths:
+ aliases = PathAliases()
+ for paths in self.config.paths.values():
+ result = paths[0]
+ for pattern in paths[1:]:
+ aliases.add(pattern, result)
+
+ combine_parallel_data(
+ self._data,
+ aliases=aliases,
+ data_paths=data_paths,
+ strict=strict,
+ keep=keep,
+ )
+
+ def get_data(self):
+ """Get the collected data.
+
+ Also warn about various problems collecting data.
+
+ Returns a :class:`coverage.CoverageData`, the collected coverage data.
+
+ .. versionadded:: 4.0
+
+ """
+ self._init()
+ self._init_data(suffix=None)
+ self._post_init()
+
+ for plugin in self._plugins:
+ if not plugin._coverage_enabled:
+ self._collector.plugin_was_disabled(plugin)
+
+ if self._collector and self._collector.flush_data():
+ self._post_save_work()
+
+ return self._data
+
+ def _post_save_work(self):
+ """After saving data, look for warnings, post-work, etc.
+
+ Warn about things that should have happened but didn't.
+ Look for unexecuted files.
+
+ """
+ # If there are still entries in the source_pkgs_unmatched list,
+ # then we never encountered those packages.
+ if self._warn_unimported_source:
+ self._inorout.warn_unimported_source()
+
+ # Find out if we got any data.
+ if not self._data and self._warn_no_data:
+ self._warn("No data was collected.", slug="no-data-collected")
+
+ # Touch all the files that could have executed, so that we can
+ # mark completely unexecuted files as 0% covered.
+ if self._data is not None:
+ file_paths = collections.defaultdict(list)
+ for file_path, plugin_name in self._inorout.find_possibly_unexecuted_files():
+ file_path = self._file_mapper(file_path)
+ file_paths[plugin_name].append(file_path)
+ for plugin_name, paths in file_paths.items():
+ self._data.touch_files(paths, plugin_name)
+
+ if self.config.note:
+ self._warn("The '[run] note' setting is no longer supported.")
+
+ # Backward compatibility with version 1.
+ def analysis(self, morf):
+ """Like `analysis2` but doesn't return excluded line numbers."""
+ f, s, _, m, mf = self.analysis2(morf)
+ return f, s, m, mf
+
+ def analysis2(self, morf):
+ """Analyze a module.
+
+ `morf` is a module or a file name. It will be analyzed to determine
+ its coverage statistics. The return value is a 5-tuple:
+
+ * The file name for the module.
+ * A list of line numbers of executable statements.
+ * A list of line numbers of excluded statements.
+ * A list of line numbers of statements not run (missing from
+ execution).
+ * A readable formatted string of the missing line numbers.
+
+ The analysis uses the source file itself and the current measured
+ coverage data.
+
+ """
+ analysis = self._analyze(morf)
+ return (
+ analysis.filename,
+ sorted(analysis.statements),
+ sorted(analysis.excluded),
+ sorted(analysis.missing),
+ analysis.missing_formatted(),
+ )
+
+ def _analyze(self, it):
+ """Analyze a single morf or code unit.
+
+ Returns an `Analysis` object.
+
+ """
+ # All reporting comes through here, so do reporting initialization.
+ self._init()
+ Numbers.set_precision(self.config.precision)
+ self._post_init()
+
+ data = self.get_data()
+ if not isinstance(it, FileReporter):
+ it = self._get_file_reporter(it)
+
+ return Analysis(data, it, self._file_mapper)
+
+ def _get_file_reporter(self, morf):
+ """Get a FileReporter for a module or file name."""
+ plugin = None
+ file_reporter = "python"
+
+ if isinstance(morf, string_class):
+ if getattr(sys, 'is_standalone_binary', False):
+ # Leave morf in canonical format - relative to the arcadia root
+ mapped_morf = morf
+ else:
+ mapped_morf = self._file_mapper(morf)
+ plugin_name = self._data.file_tracer(mapped_morf)
+ if plugin_name:
+ plugin = self._plugins.get(plugin_name)
+
+ if plugin:
+ file_reporter = plugin.file_reporter(mapped_morf)
+ if file_reporter is None:
+ raise CoverageException(
+ "Plugin %r did not provide a file reporter for %r." % (
+ plugin._coverage_plugin_name, morf
+ )
+ )
+
+ if file_reporter == "python":
+ file_reporter = PythonFileReporter(morf, self)
+
+ return file_reporter
+
+ def _get_file_reporters(self, morfs=None):
+ """Get a list of FileReporters for a list of modules or file names.
+
+ For each module or file name in `morfs`, find a FileReporter. Return
+ the list of FileReporters.
+
+ If `morfs` is a single module or file name, this returns a list of one
+ FileReporter. If `morfs` is empty or None, then the list of all files
+ measured is used to find the FileReporters.
+
+ """
+ if not morfs:
+ morfs = self._data.measured_files()
+
+ # Be sure we have a collection.
+ if not isinstance(morfs, (list, tuple, set)):
+ morfs = [morfs]
+
+ file_reporters = [self._get_file_reporter(morf) for morf in morfs]
+ return file_reporters
+
+ def report(
+ self, morfs=None, show_missing=None, ignore_errors=None,
+ file=None, omit=None, include=None, skip_covered=None,
+ contexts=None, skip_empty=None, precision=None, sort=None
+ ):
+ """Write a textual summary report to `file`.
+
+ Each module in `morfs` is listed, with counts of statements, executed
+ statements, missing statements, and a list of lines missed.
+
+ If `show_missing` is true, then details of which lines or branches are
+ missing will be included in the report. If `ignore_errors` is true,
+ then a failure while reporting a single file will not stop the entire
+ report.
+
+ `file` is a file-like object, suitable for writing.
+
+ `include` is a list of file name patterns. Files that match will be
+ included in the report. Files matching `omit` will not be included in
+ the report.
+
+ If `skip_covered` is true, don't report on files with 100% coverage.
+
+ If `skip_empty` is true, don't report on empty files (those that have
+ no statements).
+
+ `contexts` is a list of regular expressions. Only data from
+ :ref:`dynamic contexts <dynamic_contexts>` that match one of those
+ expressions (using :func:`re.search <python:re.search>`) will be
+ included in the report.
+
+ `precision` is the number of digits to display after the decimal
+ point for percentages.
+
+ All of the arguments default to the settings read from the
+ :ref:`configuration file <config>`.
+
+ Returns a float, the total percentage covered.
+
+ .. versionadded:: 4.0
+ The `skip_covered` parameter.
+
+ .. versionadded:: 5.0
+ The `contexts` and `skip_empty` parameters.
+
+ .. versionadded:: 5.2
+ The `precision` parameter.
+
+ """
+ with override_config(
+ self,
+ ignore_errors=ignore_errors, report_omit=omit, report_include=include,
+ show_missing=show_missing, skip_covered=skip_covered,
+ report_contexts=contexts, skip_empty=skip_empty, precision=precision,
+ sort=sort
+ ):
+ reporter = SummaryReporter(self)
+ return reporter.report(morfs, outfile=file)
+
+ def annotate(
+ self, morfs=None, directory=None, ignore_errors=None,
+ omit=None, include=None, contexts=None,
+ ):
+ """Annotate a list of modules.
+
+ Each module in `morfs` is annotated. The source is written to a new
+ file, named with a ",cover" suffix, with each line prefixed with a
+ marker to indicate the coverage of the line. Covered lines have ">",
+ excluded lines have "-", and missing lines have "!".
+
+ See :meth:`report` for other arguments.
+
+ """
+ with override_config(self,
+ ignore_errors=ignore_errors, report_omit=omit,
+ report_include=include, report_contexts=contexts,
+ ):
+ reporter = AnnotateReporter(self)
+ reporter.report(morfs, directory=directory)
+
+ def html_report(
+ self, morfs=None, directory=None, ignore_errors=None,
+ omit=None, include=None, extra_css=None, title=None,
+ skip_covered=None, show_contexts=None, contexts=None,
+ skip_empty=None, precision=None,
+ ):
+ """Generate an HTML report.
+
+ The HTML is written to `directory`. The file "index.html" is the
+ overview starting point, with links to more detailed pages for
+ individual modules.
+
+ `extra_css` is a path to a file of other CSS to apply on the page.
+ It will be copied into the HTML directory.
+
+ `title` is a text string (not HTML) to use as the title of the HTML
+ report.
+
+ See :meth:`report` for other arguments.
+
+ Returns a float, the total percentage covered.
+
+ .. note::
+ The HTML report files are generated incrementally based on the
+ source files and coverage results. If you modify the report files,
+ the changes will not be considered. You should be careful about
+ changing the files in the report folder.
+
+ """
+ with override_config(self,
+ ignore_errors=ignore_errors, report_omit=omit, report_include=include,
+ html_dir=directory, extra_css=extra_css, html_title=title,
+ html_skip_covered=skip_covered, show_contexts=show_contexts, report_contexts=contexts,
+ html_skip_empty=skip_empty, precision=precision,
+ ):
+ reporter = HtmlReporter(self)
+ return reporter.report(morfs)
+
+ def xml_report(
+ self, morfs=None, outfile=None, ignore_errors=None,
+ omit=None, include=None, contexts=None, skip_empty=None,
+ ):
+ """Generate an XML report of coverage results.
+
+ The report is compatible with Cobertura reports.
+
+ Each module in `morfs` is included in the report. `outfile` is the
+ path to write the file to, "-" will write to stdout.
+
+ See :meth:`report` for other arguments.
+
+ Returns a float, the total percentage covered.
+
+ """
+ with override_config(self,
+ ignore_errors=ignore_errors, report_omit=omit, report_include=include,
+ xml_output=outfile, report_contexts=contexts, skip_empty=skip_empty,
+ ):
+ return render_report(self.config.xml_output, XmlReporter(self), morfs)
+
+ def json_report(
+ self, morfs=None, outfile=None, ignore_errors=None,
+ omit=None, include=None, contexts=None, pretty_print=None,
+ show_contexts=None
+ ):
+ """Generate a JSON report of coverage results.
+
+ Each module in `morfs` is included in the report. `outfile` is the
+ path to write the file to, "-" will write to stdout.
+
+ See :meth:`report` for other arguments.
+
+ Returns a float, the total percentage covered.
+
+ .. versionadded:: 5.0
+
+ """
+ with override_config(self,
+ ignore_errors=ignore_errors, report_omit=omit, report_include=include,
+ json_output=outfile, report_contexts=contexts, json_pretty_print=pretty_print,
+ json_show_contexts=show_contexts
+ ):
+ return render_report(self.config.json_output, JsonReporter(self), morfs)
+
+ def sys_info(self):
+ """Return a list of (key, value) pairs showing internal information."""
+
+ import coverage as covmod
+
+ self._init()
+ self._post_init()
+
+ def plugin_info(plugins):
+ """Make an entry for the sys_info from a list of plug-ins."""
+ entries = []
+ for plugin in plugins:
+ entry = plugin._coverage_plugin_name
+ if not plugin._coverage_enabled:
+ entry += " (disabled)"
+ entries.append(entry)
+ return entries
+
+ info = [
+ ('version', covmod.__version__),
+ ('coverage', covmod.__file__),
+ ('tracer', self._collector.tracer_name() if self._collector else "-none-"),
+ ('CTracer', 'available' if CTracer else "unavailable"),
+ ('plugins.file_tracers', plugin_info(self._plugins.file_tracers)),
+ ('plugins.configurers', plugin_info(self._plugins.configurers)),
+ ('plugins.context_switchers', plugin_info(self._plugins.context_switchers)),
+ ('configs_attempted', self.config.attempted_config_files),
+ ('configs_read', self.config.config_files_read),
+ ('config_file', self.config.config_file),
+ ('config_contents',
+ repr(self.config._config_contents)
+ if self.config._config_contents
+ else '-none-'
+ ),
+ ('data_file', self._data.data_filename() if self._data is not None else "-none-"),
+ ('python', sys.version.replace('\n', '')),
+ ('platform', platform.platform()),
+ ('implementation', platform.python_implementation()),
+ ('executable', sys.executable),
+ ('def_encoding', sys.getdefaultencoding()),
+ ('fs_encoding', sys.getfilesystemencoding()),
+ ('pid', os.getpid()),
+ ('cwd', os.getcwd()),
+ ('path', sys.path),
+ ('environment', sorted(
+ ("%s = %s" % (k, v))
+ for k, v in iitems(os.environ)
+ if any(slug in k for slug in ("COV", "PY"))
+ )),
+ ('command_line', " ".join(getattr(sys, 'argv', ['-none-']))),
+ ]
+
+ if self._inorout:
+ info.extend(self._inorout.sys_info())
+
+ info.extend(CoverageData.sys_info())
+
+ return info
+
+
+# Mega debugging...
+# $set_env.py: COVERAGE_DEBUG_CALLS - Lots and lots of output about calls to Coverage.
+if int(os.environ.get("COVERAGE_DEBUG_CALLS", 0)): # pragma: debugging
+ from coverage.debug import decorate_methods, show_calls
+
+ Coverage = decorate_methods(show_calls(show_args=True), butnot=['get_data'])(Coverage)
+
+
+def process_startup():
+ """Call this at Python start-up to perhaps measure coverage.
+
+ If the environment variable COVERAGE_PROCESS_START is defined, coverage
+ measurement is started. The value of the variable is the config file
+ to use.
+
+ There are two ways to configure your Python installation to invoke this
+ function when Python starts:
+
+ #. Create or append to sitecustomize.py to add these lines::
+
+ import coverage
+ coverage.process_startup()
+
+ #. Create a .pth file in your Python installation containing::
+
+ import coverage; coverage.process_startup()
+
+ Returns the :class:`Coverage` instance that was started, or None if it was
+ not started by this call.
+
+ """
+ cps = os.environ.get("COVERAGE_PROCESS_START")
+ if not cps:
+ # No request for coverage, nothing to do.
+ return None
+
+ # This function can be called more than once in a process. This happens
+ # because some virtualenv configurations make the same directory visible
+ # twice in sys.path. This means that the .pth file will be found twice,
+ # and executed twice, executing this function twice. We set a global
+ # flag (an attribute on this function) to indicate that coverage.py has
+ # already been started, so we can avoid doing it twice.
+ #
+ # https://github.com/nedbat/coveragepy/issues/340 has more details.
+
+ if hasattr(process_startup, "coverage"):
+ # We've annotated this function before, so we must have already
+ # started coverage.py in this process. Nothing to do.
+ return None
+
+ cov = Coverage(config_file=cps)
+ process_startup.coverage = cov
+ cov._warn_no_data = False
+ cov._warn_unimported_source = False
+ cov._warn_preimported_source = False
+ cov._auto_save = True
+ cov.start()
+
+ return cov
+
+
+def _prevent_sub_process_measurement():
+ """Stop any subprocess auto-measurement from writing data."""
+ auto_created_coverage = getattr(process_startup, "coverage", None)
+ if auto_created_coverage is not None:
+ auto_created_coverage._auto_save = False
diff --git a/contrib/python/coverage/py3/coverage/ctracer/datastack.c b/contrib/python/coverage/py3/coverage/ctracer/datastack.c
new file mode 100644
index 0000000000..a9cfcc2cf2
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/datastack.c
@@ -0,0 +1,50 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#include "util.h"
+#include "datastack.h"
+
+#define STACK_DELTA 20
+
+int
+DataStack_init(Stats *pstats, DataStack *pdata_stack)
+{
+ pdata_stack->depth = -1;
+ pdata_stack->stack = NULL;
+ pdata_stack->alloc = 0;
+ return RET_OK;
+}
+
+void
+DataStack_dealloc(Stats *pstats, DataStack *pdata_stack)
+{
+ int i;
+
+ for (i = 0; i < pdata_stack->alloc; i++) {
+ Py_XDECREF(pdata_stack->stack[i].file_data);
+ }
+ PyMem_Free(pdata_stack->stack);
+}
+
+int
+DataStack_grow(Stats *pstats, DataStack *pdata_stack)
+{
+ pdata_stack->depth++;
+ if (pdata_stack->depth >= pdata_stack->alloc) {
+ /* We've outgrown our data_stack array: make it bigger. */
+ int bigger = pdata_stack->alloc + STACK_DELTA;
+ DataStackEntry * bigger_data_stack = PyMem_Realloc(pdata_stack->stack, bigger * sizeof(DataStackEntry));
+ STATS( pstats->stack_reallocs++; )
+ if (bigger_data_stack == NULL) {
+ PyErr_NoMemory();
+ pdata_stack->depth--;
+ return RET_ERROR;
+ }
+ /* Zero the new entries. */
+ memset(bigger_data_stack + pdata_stack->alloc, 0, STACK_DELTA * sizeof(DataStackEntry));
+
+ pdata_stack->stack = bigger_data_stack;
+ pdata_stack->alloc = bigger;
+ }
+ return RET_OK;
+}
diff --git a/contrib/python/coverage/py3/coverage/ctracer/datastack.h b/contrib/python/coverage/py3/coverage/ctracer/datastack.h
new file mode 100644
index 0000000000..3b3078ba27
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/datastack.h
@@ -0,0 +1,45 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_DATASTACK_H
+#define _COVERAGE_DATASTACK_H
+
+#include "util.h"
+#include "stats.h"
+
+/* An entry on the data stack. For each call frame, we need to record all
+ * the information needed for CTracer_handle_line to operate as quickly as
+ * possible.
+ */
+typedef struct DataStackEntry {
+ /* The current file_data dictionary. Owned. */
+ PyObject * file_data;
+
+ /* The disposition object for this frame. A borrowed instance of CFileDisposition. */
+ PyObject * disposition;
+
+ /* The FileTracer handling this frame, or None if it's Python. Borrowed. */
+ PyObject * file_tracer;
+
+ /* The line number of the last line recorded, for tracing arcs.
+ -1 means there was no previous line, as when entering a code object.
+ */
+ int last_line;
+
+ BOOL started_context;
+} DataStackEntry;
+
+/* A data stack is a dynamically allocated vector of DataStackEntry's. */
+typedef struct DataStack {
+ int depth; /* The index of the last-used entry in stack. */
+ int alloc; /* number of entries allocated at stack. */
+ /* The file data at each level, or NULL if not recording. */
+ DataStackEntry * stack;
+} DataStack;
+
+
+int DataStack_init(Stats * pstats, DataStack *pdata_stack);
+void DataStack_dealloc(Stats * pstats, DataStack *pdata_stack);
+int DataStack_grow(Stats * pstats, DataStack *pdata_stack);
+
+#endif /* _COVERAGE_DATASTACK_H */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/filedisp.c b/contrib/python/coverage/py3/coverage/ctracer/filedisp.c
new file mode 100644
index 0000000000..47782ae090
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/filedisp.c
@@ -0,0 +1,85 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#include "util.h"
+#include "filedisp.h"
+
+void
+CFileDisposition_dealloc(CFileDisposition *self)
+{
+ Py_XDECREF(self->original_filename);
+ Py_XDECREF(self->canonical_filename);
+ Py_XDECREF(self->source_filename);
+ Py_XDECREF(self->trace);
+ Py_XDECREF(self->reason);
+ Py_XDECREF(self->file_tracer);
+ Py_XDECREF(self->has_dynamic_filename);
+}
+
+static PyMemberDef
+CFileDisposition_members[] = {
+ { "original_filename", T_OBJECT, offsetof(CFileDisposition, original_filename), 0,
+ PyDoc_STR("") },
+
+ { "canonical_filename", T_OBJECT, offsetof(CFileDisposition, canonical_filename), 0,
+ PyDoc_STR("") },
+
+ { "source_filename", T_OBJECT, offsetof(CFileDisposition, source_filename), 0,
+ PyDoc_STR("") },
+
+ { "trace", T_OBJECT, offsetof(CFileDisposition, trace), 0,
+ PyDoc_STR("") },
+
+ { "reason", T_OBJECT, offsetof(CFileDisposition, reason), 0,
+ PyDoc_STR("") },
+
+ { "file_tracer", T_OBJECT, offsetof(CFileDisposition, file_tracer), 0,
+ PyDoc_STR("") },
+
+ { "has_dynamic_filename", T_OBJECT, offsetof(CFileDisposition, has_dynamic_filename), 0,
+ PyDoc_STR("") },
+
+ { NULL }
+};
+
+PyTypeObject
+CFileDispositionType = {
+ MyType_HEAD_INIT
+ "coverage.CFileDispositionType", /*tp_name*/
+ sizeof(CFileDisposition), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)CFileDisposition_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "CFileDisposition objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ CFileDisposition_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/contrib/python/coverage/py3/coverage/ctracer/filedisp.h b/contrib/python/coverage/py3/coverage/ctracer/filedisp.h
new file mode 100644
index 0000000000..860f9a50b1
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/filedisp.h
@@ -0,0 +1,26 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_FILEDISP_H
+#define _COVERAGE_FILEDISP_H
+
+#include "util.h"
+#include "structmember.h"
+
+typedef struct CFileDisposition {
+ PyObject_HEAD
+
+ PyObject * original_filename;
+ PyObject * canonical_filename;
+ PyObject * source_filename;
+ PyObject * trace;
+ PyObject * reason;
+ PyObject * file_tracer;
+ PyObject * has_dynamic_filename;
+} CFileDisposition;
+
+void CFileDisposition_dealloc(CFileDisposition *self);
+
+extern PyTypeObject CFileDispositionType;
+
+#endif /* _COVERAGE_FILEDISP_H */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/module.c b/contrib/python/coverage/py3/coverage/ctracer/module.c
new file mode 100644
index 0000000000..f308902b69
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/module.c
@@ -0,0 +1,108 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#include "util.h"
+#include "tracer.h"
+#include "filedisp.h"
+
+/* Module definition */
+
+#define MODULE_DOC PyDoc_STR("Fast coverage tracer.")
+
+#if PY_MAJOR_VERSION >= 3
+
+static PyModuleDef
+moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "coverage.tracer",
+ MODULE_DOC,
+ -1,
+ NULL, /* methods */
+ NULL,
+ NULL, /* traverse */
+ NULL, /* clear */
+ NULL
+};
+
+
+PyObject *
+PyInit_tracer(void)
+{
+ PyObject * mod = PyModule_Create(&moduledef);
+ if (mod == NULL) {
+ return NULL;
+ }
+
+ if (CTracer_intern_strings() < 0) {
+ return NULL;
+ }
+
+ /* Initialize CTracer */
+ CTracerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CTracerType) < 0) {
+ Py_DECREF(mod);
+ return NULL;
+ }
+
+ Py_INCREF(&CTracerType);
+ if (PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ return NULL;
+ }
+
+ /* Initialize CFileDisposition */
+ CFileDispositionType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CFileDispositionType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ return NULL;
+ }
+
+ Py_INCREF(&CFileDispositionType);
+ if (PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType) < 0) {
+ Py_DECREF(mod);
+ Py_DECREF(&CTracerType);
+ Py_DECREF(&CFileDispositionType);
+ return NULL;
+ }
+
+ return mod;
+}
+
+#else
+
+void
+inittracer(void)
+{
+ PyObject * mod;
+
+ mod = Py_InitModule3("coverage.tracer", NULL, MODULE_DOC);
+ if (mod == NULL) {
+ return;
+ }
+
+ if (CTracer_intern_strings() < 0) {
+ return;
+ }
+
+ /* Initialize CTracer */
+ CTracerType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CTracerType) < 0) {
+ return;
+ }
+
+ Py_INCREF(&CTracerType);
+ PyModule_AddObject(mod, "CTracer", (PyObject *)&CTracerType);
+
+ /* Initialize CFileDisposition */
+ CFileDispositionType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&CFileDispositionType) < 0) {
+ return;
+ }
+
+ Py_INCREF(&CFileDispositionType);
+ PyModule_AddObject(mod, "CFileDisposition", (PyObject *)&CFileDispositionType);
+}
+
+#endif /* Py3k */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/stats.h b/contrib/python/coverage/py3/coverage/ctracer/stats.h
new file mode 100644
index 0000000000..05173369f7
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/stats.h
@@ -0,0 +1,31 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_STATS_H
+#define _COVERAGE_STATS_H
+
+#include "util.h"
+
+#if COLLECT_STATS
+#define STATS(x) x
+#else
+#define STATS(x)
+#endif
+
+typedef struct Stats {
+ unsigned int calls; /* Need at least one member, but the rest only if needed. */
+#if COLLECT_STATS
+ unsigned int lines;
+ unsigned int returns;
+ unsigned int exceptions;
+ unsigned int others;
+ unsigned int files;
+ unsigned int missed_returns;
+ unsigned int stack_reallocs;
+ unsigned int errors;
+ unsigned int pycalls;
+ unsigned int start_context_calls;
+#endif
+} Stats;
+
+#endif /* _COVERAGE_STATS_H */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/tracer.c b/contrib/python/coverage/py3/coverage/ctracer/tracer.c
new file mode 100644
index 0000000000..3e776958cd
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/tracer.c
@@ -0,0 +1,1149 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+/* C-based Tracer for coverage.py. */
+
+#include "util.h"
+#include "datastack.h"
+#include "filedisp.h"
+#include "tracer.h"
+
+/* Python C API helpers. */
+
+static int
+pyint_as_int(PyObject * pyint, int *pint)
+{
+ int the_int = MyInt_AsInt(pyint);
+ if (the_int == -1 && PyErr_Occurred()) {
+ return RET_ERROR;
+ }
+
+ *pint = the_int;
+ return RET_OK;
+}
+
+
+/* Interned strings to speed GetAttr etc. */
+
+static PyObject *str_trace;
+static PyObject *str_file_tracer;
+static PyObject *str__coverage_enabled;
+static PyObject *str__coverage_plugin;
+static PyObject *str__coverage_plugin_name;
+static PyObject *str_dynamic_source_filename;
+static PyObject *str_line_number_range;
+
+int
+CTracer_intern_strings(void)
+{
+ int ret = RET_ERROR;
+
+#define INTERN_STRING(v, s) \
+ v = MyText_InternFromString(s); \
+ if (v == NULL) { \
+ goto error; \
+ }
+
+ INTERN_STRING(str_trace, "trace")
+ INTERN_STRING(str_file_tracer, "file_tracer")
+ INTERN_STRING(str__coverage_enabled, "_coverage_enabled")
+ INTERN_STRING(str__coverage_plugin, "_coverage_plugin")
+ INTERN_STRING(str__coverage_plugin_name, "_coverage_plugin_name")
+ INTERN_STRING(str_dynamic_source_filename, "dynamic_source_filename")
+ INTERN_STRING(str_line_number_range, "line_number_range")
+
+ ret = RET_OK;
+
+error:
+ return ret;
+}
+
+static void CTracer_disable_plugin(CTracer *self, PyObject * disposition);
+
+static int
+CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused)
+{
+ int ret = RET_ERROR;
+
+ if (DataStack_init(&self->stats, &self->data_stack) < 0) {
+ goto error;
+ }
+
+ self->pdata_stack = &self->data_stack;
+
+ self->context = Py_None;
+ Py_INCREF(self->context);
+
+ ret = RET_OK;
+ goto ok;
+
+error:
+ STATS( self->stats.errors++; )
+
+ok:
+ return ret;
+}
+
+static void
+CTracer_dealloc(CTracer *self)
+{
+ int i;
+
+ if (self->started) {
+ PyEval_SetTrace(NULL, NULL);
+ }
+
+ Py_XDECREF(self->should_trace);
+ Py_XDECREF(self->check_include);
+ Py_XDECREF(self->warn);
+ Py_XDECREF(self->concur_id_func);
+ Py_XDECREF(self->data);
+ Py_XDECREF(self->file_tracers);
+ Py_XDECREF(self->should_trace_cache);
+ Py_XDECREF(self->should_start_context);
+ Py_XDECREF(self->switch_context);
+ Py_XDECREF(self->context);
+ Py_XDECREF(self->disable_plugin);
+
+ DataStack_dealloc(&self->stats, &self->data_stack);
+ if (self->data_stacks) {
+ for (i = 0; i < self->data_stacks_used; i++) {
+ DataStack_dealloc(&self->stats, self->data_stacks + i);
+ }
+ PyMem_Free(self->data_stacks);
+ }
+
+ Py_XDECREF(self->data_stack_index);
+
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+#if TRACE_LOG
+static const char *
+indent(int n)
+{
+ static const char * spaces =
+ " "
+ " "
+ " "
+ " "
+ ;
+ return spaces + strlen(spaces) - n*2;
+}
+
+static BOOL logging = FALSE;
+/* Set these constants to be a file substring and line number to start logging. */
+static const char * start_file = "tests/views";
+static int start_line = 27;
+
+static void
+showlog(int depth, int lineno, PyObject * filename, const char * msg)
+{
+ if (logging) {
+ printf("%s%3d ", indent(depth), depth);
+ if (lineno) {
+ printf("%4d", lineno);
+ }
+ else {
+ printf(" ");
+ }
+ if (filename) {
+ PyObject *ascii = MyText_AS_BYTES(filename);
+ printf(" %s", MyBytes_AS_STRING(ascii));
+ Py_DECREF(ascii);
+ }
+ if (msg) {
+ printf(" %s", msg);
+ }
+ printf("\n");
+ }
+}
+
+#define SHOWLOG(a,b,c,d) showlog(a,b,c,d)
+#else
+#define SHOWLOG(a,b,c,d)
+#endif /* TRACE_LOG */
+
+#if WHAT_LOG
+static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "};
+#endif
+
+/* Record a pair of integers in self->pcur_entry->file_data. */
+static int
+CTracer_record_pair(CTracer *self, int l1, int l2)
+{
+ int ret = RET_ERROR;
+
+ PyObject * t = NULL;
+
+ t = Py_BuildValue("(ii)", l1, l2);
+ if (t == NULL) {
+ goto error;
+ }
+
+ if (PyDict_SetItem(self->pcur_entry->file_data, t, Py_None) < 0) {
+ goto error;
+ }
+
+ ret = RET_OK;
+
+error:
+ Py_XDECREF(t);
+
+ return ret;
+}
+
+/* Set self->pdata_stack to the proper data_stack to use. */
+static int
+CTracer_set_pdata_stack(CTracer *self)
+{
+ int ret = RET_ERROR;
+ PyObject * co_obj = NULL;
+ PyObject * stack_index = NULL;
+
+ if (self->concur_id_func != Py_None) {
+ int the_index = 0;
+
+ if (self->data_stack_index == NULL) {
+ PyObject * weakref = NULL;
+
+ weakref = PyImport_ImportModule("weakref");
+ if (weakref == NULL) {
+ goto error;
+ }
+ STATS( self->stats.pycalls++; )
+ self->data_stack_index = PyObject_CallMethod(weakref, "WeakKeyDictionary", NULL);
+ Py_XDECREF(weakref);
+
+ if (self->data_stack_index == NULL) {
+ goto error;
+ }
+ }
+
+ STATS( self->stats.pycalls++; )
+ co_obj = PyObject_CallObject(self->concur_id_func, NULL);
+ if (co_obj == NULL) {
+ goto error;
+ }
+ stack_index = PyObject_GetItem(self->data_stack_index, co_obj);
+ if (stack_index == NULL) {
+ /* PyObject_GetItem sets an exception if it didn't find the thing. */
+ PyErr_Clear();
+
+ /* A new concurrency object. Make a new data stack. */
+ the_index = self->data_stacks_used;
+ stack_index = MyInt_FromInt(the_index);
+ if (stack_index == NULL) {
+ goto error;
+ }
+ if (PyObject_SetItem(self->data_stack_index, co_obj, stack_index) < 0) {
+ goto error;
+ }
+ self->data_stacks_used++;
+ if (self->data_stacks_used >= self->data_stacks_alloc) {
+ int bigger = self->data_stacks_alloc + 10;
+ DataStack * bigger_stacks = PyMem_Realloc(self->data_stacks, bigger * sizeof(DataStack));
+ if (bigger_stacks == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ self->data_stacks = bigger_stacks;
+ self->data_stacks_alloc = bigger;
+ }
+ DataStack_init(&self->stats, &self->data_stacks[the_index]);
+ }
+ else {
+ if (pyint_as_int(stack_index, &the_index) < 0) {
+ goto error;
+ }
+ }
+
+ self->pdata_stack = &self->data_stacks[the_index];
+ }
+ else {
+ self->pdata_stack = &self->data_stack;
+ }
+
+ ret = RET_OK;
+
+error:
+
+ Py_XDECREF(co_obj);
+ Py_XDECREF(stack_index);
+
+ return ret;
+}
+
+/*
+ * Parts of the trace function.
+ */
+
+static int
+CTracer_check_missing_return(CTracer *self, PyFrameObject *frame)
+{
+ int ret = RET_ERROR;
+
+ if (self->last_exc_back) {
+ if (frame == self->last_exc_back) {
+ /* Looks like someone forgot to send a return event. We'll clear
+ the exception state and do the RETURN code here. Notice that the
+ frame we have in hand here is not the correct frame for the RETURN,
+ that frame is gone. Our handling for RETURN doesn't need the
+ actual frame, but we do log it, so that will look a little off if
+ you're looking at the detailed log.
+
+ If someday we need to examine the frame when doing RETURN, then
+ we'll need to keep more of the missed frame's state.
+ */
+ STATS( self->stats.missed_returns++; )
+ if (CTracer_set_pdata_stack(self) < 0) {
+ goto error;
+ }
+ if (self->pdata_stack->depth >= 0) {
+ if (self->tracing_arcs && self->pcur_entry->file_data) {
+ if (CTracer_record_pair(self, self->pcur_entry->last_line, -self->last_exc_firstlineno) < 0) {
+ goto error;
+ }
+ }
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "missedreturn");
+ self->pdata_stack->depth--;
+ self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
+ }
+ }
+ self->last_exc_back = NULL;
+ }
+
+ ret = RET_OK;
+
+error:
+
+ return ret;
+}
+
+static int
+CTracer_handle_call(CTracer *self, PyFrameObject *frame)
+{
+ int ret = RET_ERROR;
+ int ret2;
+
+ /* Owned references that we clean up at the very end of the function. */
+ PyObject * disposition = NULL;
+ PyObject * plugin = NULL;
+ PyObject * plugin_name = NULL;
+ PyObject * next_tracename = NULL;
+
+ /* Borrowed references. */
+ PyObject * filename = NULL;
+ PyObject * disp_trace = NULL;
+ PyObject * tracename = NULL;
+ PyObject * file_tracer = NULL;
+ PyObject * has_dynamic_filename = NULL;
+
+ CFileDisposition * pdisp = NULL;
+
+ STATS( self->stats.calls++; )
+
+ /* Grow the stack. */
+ if (CTracer_set_pdata_stack(self) < 0) {
+ goto error;
+ }
+ if (DataStack_grow(&self->stats, self->pdata_stack) < 0) {
+ goto error;
+ }
+ self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
+
+ /* See if this frame begins a new context. */
+ if (self->should_start_context != Py_None && self->context == Py_None) {
+ PyObject * context;
+ /* We're looking for our context, ask should_start_context if this is the start. */
+ STATS( self->stats.start_context_calls++; )
+ STATS( self->stats.pycalls++; )
+ context = PyObject_CallFunctionObjArgs(self->should_start_context, frame, NULL);
+ if (context == NULL) {
+ goto error;
+ }
+ if (context != Py_None) {
+ PyObject * val;
+ Py_DECREF(self->context);
+ self->context = context;
+ self->pcur_entry->started_context = TRUE;
+ STATS( self->stats.pycalls++; )
+ val = PyObject_CallFunctionObjArgs(self->switch_context, context, NULL);
+ if (val == NULL) {
+ goto error;
+ }
+ Py_DECREF(val);
+ }
+ else {
+ Py_DECREF(context);
+ self->pcur_entry->started_context = FALSE;
+ }
+ }
+ else {
+ self->pcur_entry->started_context = FALSE;
+ }
+
+ /* Check if we should trace this line. */
+ filename = MyFrame_GetCode(frame)->co_filename;
+ disposition = PyDict_GetItem(self->should_trace_cache, filename);
+ if (disposition == NULL) {
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ STATS( self->stats.files++; )
+
+ /* We've never considered this file before. */
+ /* Ask should_trace about it. */
+ STATS( self->stats.pycalls++; )
+ disposition = PyObject_CallFunctionObjArgs(self->should_trace, filename, frame, NULL);
+ if (disposition == NULL) {
+ /* An error occurred inside should_trace. */
+ goto error;
+ }
+ if (PyDict_SetItem(self->should_trace_cache, filename, disposition) < 0) {
+ goto error;
+ }
+ }
+ else {
+ Py_INCREF(disposition);
+ }
+
+ if (disposition == Py_None) {
+ /* A later check_include returned false, so don't trace it. */
+ disp_trace = Py_False;
+ }
+ else {
+ /* The object we got is a CFileDisposition, use it efficiently. */
+ pdisp = (CFileDisposition *) disposition;
+ disp_trace = pdisp->trace;
+ if (disp_trace == NULL) {
+ goto error;
+ }
+ }
+
+ if (disp_trace == Py_True) {
+ /* If tracename is a string, then we're supposed to trace. */
+ tracename = pdisp->source_filename;
+ if (tracename == NULL) {
+ goto error;
+ }
+ file_tracer = pdisp->file_tracer;
+ if (file_tracer == NULL) {
+ goto error;
+ }
+ if (file_tracer != Py_None) {
+ plugin = PyObject_GetAttr(file_tracer, str__coverage_plugin);
+ if (plugin == NULL) {
+ goto error;
+ }
+ plugin_name = PyObject_GetAttr(plugin, str__coverage_plugin_name);
+ if (plugin_name == NULL) {
+ goto error;
+ }
+ }
+ has_dynamic_filename = pdisp->has_dynamic_filename;
+ if (has_dynamic_filename == NULL) {
+ goto error;
+ }
+ if (has_dynamic_filename == Py_True) {
+ STATS( self->stats.pycalls++; )
+ next_tracename = PyObject_CallMethodObjArgs(
+ file_tracer, str_dynamic_source_filename,
+ tracename, frame, NULL
+ );
+ if (next_tracename == NULL) {
+ /* An exception from the function. Alert the user with a
+ * warning and a traceback.
+ */
+ CTracer_disable_plugin(self, disposition);
+ /* Because we handled the error, goto ok. */
+ goto ok;
+ }
+ tracename = next_tracename;
+
+ if (tracename != Py_None) {
+ /* Check the dynamic source filename against the include rules. */
+ PyObject * included = NULL;
+ int should_include;
+ included = PyDict_GetItem(self->should_trace_cache, tracename);
+ if (included == NULL) {
+ PyObject * should_include_bool;
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ STATS( self->stats.files++; )
+ STATS( self->stats.pycalls++; )
+ should_include_bool = PyObject_CallFunctionObjArgs(self->check_include, tracename, frame, NULL);
+ if (should_include_bool == NULL) {
+ goto error;
+ }
+ should_include = (should_include_bool == Py_True);
+ Py_DECREF(should_include_bool);
+ if (PyDict_SetItem(self->should_trace_cache, tracename, should_include ? disposition : Py_None) < 0) {
+ goto error;
+ }
+ }
+ else {
+ should_include = (included != Py_None);
+ }
+ if (!should_include) {
+ tracename = Py_None;
+ }
+ }
+ }
+ }
+ else {
+ tracename = Py_None;
+ }
+
+ if (tracename != Py_None) {
+ PyObject * file_data = PyDict_GetItem(self->data, tracename);
+
+ if (file_data == NULL) {
+ if (PyErr_Occurred()) {
+ goto error;
+ }
+ file_data = PyDict_New();
+ if (file_data == NULL) {
+ goto error;
+ }
+ ret2 = PyDict_SetItem(self->data, tracename, file_data);
+ if (ret2 < 0) {
+ goto error;
+ }
+
+ /* If the disposition mentions a plugin, record that. */
+ if (file_tracer != Py_None) {
+ ret2 = PyDict_SetItem(self->file_tracers, tracename, plugin_name);
+ if (ret2 < 0) {
+ goto error;
+ }
+ }
+ }
+ else {
+ /* PyDict_GetItem gives a borrowed reference. Own it. */
+ Py_INCREF(file_data);
+ }
+
+ Py_XDECREF(self->pcur_entry->file_data);
+ self->pcur_entry->file_data = file_data;
+ self->pcur_entry->file_tracer = file_tracer;
+
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), filename, "traced");
+ }
+ else {
+ Py_XDECREF(self->pcur_entry->file_data);
+ self->pcur_entry->file_data = NULL;
+ self->pcur_entry->file_tracer = Py_None;
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), filename, "skipped");
+ }
+
+ self->pcur_entry->disposition = disposition;
+
+ /* Make the frame right in case settrace(gettrace()) happens. */
+ Py_INCREF(self);
+ My_XSETREF(frame->f_trace, (PyObject*)self);
+
+ /* A call event is really a "start frame" event, and can happen for
+ * re-entering a generator also. f_lasti is -1 for a true call, and a
+ * real byte offset for a generator re-entry.
+ */
+ if (MyFrame_GetLasti(frame) < 0) {
+ self->pcur_entry->last_line = -MyFrame_GetCode(frame)->co_firstlineno;
+ }
+ else {
+ self->pcur_entry->last_line = PyFrame_GetLineNumber(frame);
+ }
+
+ok:
+ ret = RET_OK;
+
+error:
+ Py_XDECREF(next_tracename);
+ Py_XDECREF(disposition);
+ Py_XDECREF(plugin);
+ Py_XDECREF(plugin_name);
+
+ return ret;
+}
+
+
+static void
+CTracer_disable_plugin(CTracer *self, PyObject * disposition)
+{
+ PyObject * ret;
+ PyErr_Print();
+
+ STATS( self->stats.pycalls++; )
+ ret = PyObject_CallFunctionObjArgs(self->disable_plugin, disposition, NULL);
+ if (ret == NULL) {
+ goto error;
+ }
+ Py_DECREF(ret);
+
+ return;
+
+error:
+ /* This function doesn't return a status, so if an error happens, print it,
+ * but don't interrupt the flow. */
+ /* PySys_WriteStderr is nicer, but is not in the public API. */
+ fprintf(stderr, "Error occurred while disabling plug-in:\n");
+ PyErr_Print();
+}
+
+
+static int
+CTracer_unpack_pair(CTracer *self, PyObject *pair, int *p_one, int *p_two)
+{
+ int ret = RET_ERROR;
+ int the_int;
+ PyObject * pyint = NULL;
+ int index;
+
+ if (!PyTuple_Check(pair) || PyTuple_Size(pair) != 2) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "line_number_range must return 2-tuple"
+ );
+ goto error;
+ }
+
+ for (index = 0; index < 2; index++) {
+ pyint = PyTuple_GetItem(pair, index);
+ if (pyint == NULL) {
+ goto error;
+ }
+ if (pyint_as_int(pyint, &the_int) < 0) {
+ goto error;
+ }
+ *(index == 0 ? p_one : p_two) = the_int;
+ }
+
+ ret = RET_OK;
+
+error:
+ return ret;
+}
+
+static int
+CTracer_handle_line(CTracer *self, PyFrameObject *frame)
+{
+ int ret = RET_ERROR;
+ int ret2;
+
+ STATS( self->stats.lines++; )
+ if (self->pdata_stack->depth >= 0) {
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "line");
+ if (self->pcur_entry->file_data) {
+ int lineno_from = -1;
+ int lineno_to = -1;
+
+ /* We're tracing in this frame: record something. */
+ if (self->pcur_entry->file_tracer != Py_None) {
+ PyObject * from_to = NULL;
+ STATS( self->stats.pycalls++; )
+ from_to = PyObject_CallMethodObjArgs(self->pcur_entry->file_tracer, str_line_number_range, frame, NULL);
+ if (from_to == NULL) {
+ CTracer_disable_plugin(self, self->pcur_entry->disposition);
+ goto ok;
+ }
+ ret2 = CTracer_unpack_pair(self, from_to, &lineno_from, &lineno_to);
+ Py_DECREF(from_to);
+ if (ret2 < 0) {
+ CTracer_disable_plugin(self, self->pcur_entry->disposition);
+ goto ok;
+ }
+ }
+ else {
+ lineno_from = lineno_to = PyFrame_GetLineNumber(frame);
+ }
+
+ if (lineno_from != -1) {
+ for (; lineno_from <= lineno_to; lineno_from++) {
+ if (self->tracing_arcs) {
+ /* Tracing arcs: key is (last_line,this_line). */
+ if (CTracer_record_pair(self, self->pcur_entry->last_line, lineno_from) < 0) {
+ goto error;
+ }
+ }
+ else {
+ /* Tracing lines: key is simply this_line. */
+ PyObject * this_line = MyInt_FromInt(lineno_from);
+ if (this_line == NULL) {
+ goto error;
+ }
+
+ ret2 = PyDict_SetItem(self->pcur_entry->file_data, this_line, Py_None);
+ Py_DECREF(this_line);
+ if (ret2 < 0) {
+ goto error;
+ }
+ }
+
+ self->pcur_entry->last_line = lineno_from;
+ }
+ }
+ }
+ }
+
+ok:
+ ret = RET_OK;
+
+error:
+
+ return ret;
+}
+
+static int
+CTracer_handle_return(CTracer *self, PyFrameObject *frame)
+{
+ int ret = RET_ERROR;
+
+ STATS( self->stats.returns++; )
+ /* A near-copy of this code is above in the missing-return handler. */
+ if (CTracer_set_pdata_stack(self) < 0) {
+ goto error;
+ }
+ self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
+
+ if (self->pdata_stack->depth >= 0) {
+ if (self->tracing_arcs && self->pcur_entry->file_data) {
+ /* Need to distinguish between RETURN_VALUE and YIELD_VALUE. Read
+ * the current bytecode to see what it is. In unusual circumstances
+ * (Cython code), co_code can be the empty string, so range-check
+ * f_lasti before reading the byte.
+ */
+ int bytecode = RETURN_VALUE;
+ PyObject * pCode = MyCode_GetCode(MyFrame_GetCode(frame));
+ int lasti = MyFrame_GetLasti(frame);
+
+ if (lasti < MyBytes_GET_SIZE(pCode)) {
+ bytecode = MyBytes_AS_STRING(pCode)[lasti];
+ }
+ if (bytecode != YIELD_VALUE) {
+ int first = MyFrame_GetCode(frame)->co_firstlineno;
+ if (CTracer_record_pair(self, self->pcur_entry->last_line, -first) < 0) {
+ goto error;
+ }
+ }
+ }
+
+ /* If this frame started a context, then returning from it ends the context. */
+ if (self->pcur_entry->started_context) {
+ PyObject * val;
+ Py_DECREF(self->context);
+ self->context = Py_None;
+ Py_INCREF(self->context);
+ STATS( self->stats.pycalls++; )
+
+ val = PyObject_CallFunctionObjArgs(self->switch_context, self->context, NULL);
+ if (val == NULL) {
+ goto error;
+ }
+ Py_DECREF(val);
+ }
+
+ /* Pop the stack. */
+ SHOWLOG(self->pdata_stack->depth, PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "return");
+ self->pdata_stack->depth--;
+ self->pcur_entry = &self->pdata_stack->stack[self->pdata_stack->depth];
+ }
+
+ ret = RET_OK;
+
+error:
+
+ return ret;
+}
+
+static int
+CTracer_handle_exception(CTracer *self, PyFrameObject *frame)
+{
+ /* Some code (Python 2.3, and pyexpat anywhere) fires an exception event
+ without a return event. To detect that, we'll keep a copy of the
+ parent frame for an exception event. If the next event is in that
+ frame, then we must have returned without a return event. We can
+ synthesize the missing event then.
+
+ Python itself fixed this problem in 2.4. Pyexpat still has the bug.
+ I've reported the problem with pyexpat as http://bugs.python.org/issue6359 .
+ If it gets fixed, this code should still work properly. Maybe some day
+ the bug will be fixed everywhere coverage.py is supported, and we can
+ remove this missing-return detection.
+
+ More about this fix: https://nedbatchelder.com/blog/200907/a_nasty_little_bug.html
+ */
+ STATS( self->stats.exceptions++; )
+ self->last_exc_back = frame->f_back;
+ self->last_exc_firstlineno = MyFrame_GetCode(frame)->co_firstlineno;
+
+ return RET_OK;
+}
+
+/*
+ * The Trace Function
+ */
+static int
+CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unused)
+{
+ int ret = RET_ERROR;
+
+ #if DO_NOTHING
+ return RET_OK;
+ #endif
+
+ if (!self->started) {
+ /* If CTracer.stop() has been called from another thread, the tracer
+ is still active in the current thread. Let's deactivate ourselves
+ now. */
+ PyEval_SetTrace(NULL, NULL);
+ return RET_OK;
+ }
+
+ #if WHAT_LOG || TRACE_LOG
+ PyObject * ascii = NULL;
+ #endif
+
+ #if WHAT_LOG
+ if (what <= (int)(sizeof(what_sym)/sizeof(const char *))) {
+ ascii = MyText_AS_BYTES(MyFrame_GetCode(frame)->co_filename);
+ printf("trace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame));
+ Py_DECREF(ascii);
+ }
+ #endif
+
+ #if TRACE_LOG
+ ascii = MyText_AS_BYTES(MyFrame_GetCode(frame)->co_filename);
+ if (strstr(MyBytes_AS_STRING(ascii), start_file) && PyFrame_GetLineNumber(frame) == start_line) {
+ logging = TRUE;
+ }
+ Py_DECREF(ascii);
+ #endif
+
+ /* See below for details on missing-return detection. */
+ if (CTracer_check_missing_return(self, frame) < 0) {
+ goto error;
+ }
+
+ self->activity = TRUE;
+
+ switch (what) {
+ case PyTrace_CALL:
+ if (CTracer_handle_call(self, frame) < 0) {
+ goto error;
+ }
+ break;
+
+ case PyTrace_RETURN:
+ if (CTracer_handle_return(self, frame) < 0) {
+ goto error;
+ }
+ break;
+
+ case PyTrace_LINE:
+ if (CTracer_handle_line(self, frame) < 0) {
+ goto error;
+ }
+ break;
+
+ case PyTrace_EXCEPTION:
+ if (CTracer_handle_exception(self, frame) < 0) {
+ goto error;
+ }
+ break;
+
+ default:
+ STATS( self->stats.others++; )
+ break;
+ }
+
+ ret = RET_OK;
+ goto cleanup;
+
+error:
+ STATS( self->stats.errors++; )
+
+cleanup:
+ return ret;
+}
+
+
+/*
+ * Python has two ways to set the trace function: sys.settrace(fn), which
+ * takes a Python callable, and PyEval_SetTrace(func, obj), which takes
+ * a C function and a Python object. The way these work together is that
+ * sys.settrace(pyfn) calls PyEval_SetTrace(builtin_func, pyfn), using the
+ * Python callable as the object in PyEval_SetTrace. So sys.gettrace()
+ * simply returns the Python object used as the second argument to
+ * PyEval_SetTrace. So sys.gettrace() will return our self parameter, which
+ * means it must be callable to be used in sys.settrace().
+ *
+ * So we make ourself callable, equivalent to invoking our trace function.
+ *
+ * To help with the process of replaying stored frames, this function has an
+ * optional keyword argument:
+ *
+ * def CTracer_call(frame, event, arg, lineno=0)
+ *
+ * If provided, the lineno argument is used as the line number, and the
+ * frame's f_lineno member is ignored.
+ */
+static PyObject *
+CTracer_call(CTracer *self, PyObject *args, PyObject *kwds)
+{
+ PyFrameObject *frame;
+ PyObject *what_str;
+ PyObject *arg;
+ int lineno = 0;
+ int what;
+ int orig_lineno;
+ PyObject *ret = NULL;
+ PyObject * ascii = NULL;
+
+ #if DO_NOTHING
+ CRASH
+ #endif
+
+ static char *what_names[] = {
+ "call", "exception", "line", "return",
+ "c_call", "c_exception", "c_return",
+ NULL
+ };
+
+ static char *kwlist[] = {"frame", "event", "arg", "lineno", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!O|i:Tracer_call", kwlist,
+ &PyFrame_Type, &frame, &MyText_Type, &what_str, &arg, &lineno)) {
+ goto done;
+ }
+
+ /* In Python, the what argument is a string, we need to find an int
+ for the C function. */
+ for (what = 0; what_names[what]; what++) {
+ int should_break;
+ ascii = MyText_AS_BYTES(what_str);
+ should_break = !strcmp(MyBytes_AS_STRING(ascii), what_names[what]);
+ Py_DECREF(ascii);
+ if (should_break) {
+ break;
+ }
+ }
+
+ #if WHAT_LOG
+ ascii = MyText_AS_BYTES(MyFrame_GetCode(frame)->co_filename);
+ printf("pytrace: %s @ %s %d\n", what_sym[what], MyBytes_AS_STRING(ascii), PyFrame_GetLineNumber(frame));
+ Py_DECREF(ascii);
+ #endif
+
+ /* Save off the frame's lineno, and use the forced one, if provided. */
+ orig_lineno = frame->f_lineno;
+ if (lineno > 0) {
+ frame->f_lineno = lineno;
+ }
+
+ /* Invoke the C function, and return ourselves. */
+ if (CTracer_trace(self, frame, what, arg) == RET_OK) {
+ Py_INCREF(self);
+ ret = (PyObject *)self;
+ }
+
+ /* Clean up. */
+ frame->f_lineno = orig_lineno;
+
+ /* For better speed, install ourselves the C way so that future calls go
+ directly to CTracer_trace, without this intermediate function.
+
+ Only do this if this is a CALL event, since new trace functions only
+ take effect then. If we don't condition it on CALL, then we'll clobber
+ the new trace function before it has a chance to get called. To
+ understand why, there are three internal values to track: frame.f_trace,
+ c_tracefunc, and c_traceobj. They are explained here:
+ https://nedbatchelder.com/text/trace-function.html
+
+ Without the conditional on PyTrace_CALL, this is what happens:
+
+ def func(): # f_trace c_tracefunc c_traceobj
+ # -------------- -------------- --------------
+ # CTracer CTracer.trace CTracer
+ sys.settrace(my_func)
+ # CTracer trampoline my_func
+ # Now Python calls trampoline(CTracer), which calls this function
+ # which calls PyEval_SetTrace below, setting us as the tracer again:
+ # CTracer CTracer.trace CTracer
+ # and it's as if the settrace never happened.
+ */
+ if (what == PyTrace_CALL) {
+ PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
+ }
+
+done:
+ return ret;
+}
+
+static PyObject *
+CTracer_start(CTracer *self, PyObject *args_unused)
+{
+ PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self);
+ self->started = TRUE;
+ self->tracing_arcs = self->trace_arcs && PyObject_IsTrue(self->trace_arcs);
+
+ /* start() returns a trace function usable with sys.settrace() */
+ Py_INCREF(self);
+ return (PyObject *)self;
+}
+
+static PyObject *
+CTracer_stop(CTracer *self, PyObject *args_unused)
+{
+ if (self->started) {
+ /* Set the started flag only. The actual call to
+ PyEval_SetTrace(NULL, NULL) is delegated to the callback
+ itself to ensure that it called from the right thread.
+ */
+ self->started = FALSE;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+CTracer_activity(CTracer *self, PyObject *args_unused)
+{
+ if (self->activity) {
+ Py_RETURN_TRUE;
+ }
+ else {
+ Py_RETURN_FALSE;
+ }
+}
+
+static PyObject *
+CTracer_reset_activity(CTracer *self, PyObject *args_unused)
+{
+ self->activity = FALSE;
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+CTracer_get_stats(CTracer *self, PyObject *args_unused)
+{
+#if COLLECT_STATS
+ return Py_BuildValue(
+ "{sI,sI,sI,sI,sI,sI,sI,sI,si,sI,sI,sI}",
+ "calls", self->stats.calls,
+ "lines", self->stats.lines,
+ "returns", self->stats.returns,
+ "exceptions", self->stats.exceptions,
+ "others", self->stats.others,
+ "files", self->stats.files,
+ "missed_returns", self->stats.missed_returns,
+ "stack_reallocs", self->stats.stack_reallocs,
+ "stack_alloc", self->pdata_stack->alloc,
+ "errors", self->stats.errors,
+ "pycalls", self->stats.pycalls,
+ "start_context_calls", self->stats.start_context_calls
+ );
+#else
+ Py_RETURN_NONE;
+#endif /* COLLECT_STATS */
+}
+
+static PyMemberDef
+CTracer_members[] = {
+ { "should_trace", T_OBJECT, offsetof(CTracer, should_trace), 0,
+ PyDoc_STR("Function indicating whether to trace a file.") },
+
+ { "check_include", T_OBJECT, offsetof(CTracer, check_include), 0,
+ PyDoc_STR("Function indicating whether to include a file.") },
+
+ { "warn", T_OBJECT, offsetof(CTracer, warn), 0,
+ PyDoc_STR("Function for issuing warnings.") },
+
+ { "concur_id_func", T_OBJECT, offsetof(CTracer, concur_id_func), 0,
+ PyDoc_STR("Function for determining concurrency context") },
+
+ { "data", T_OBJECT, offsetof(CTracer, data), 0,
+ PyDoc_STR("The raw dictionary of trace data.") },
+
+ { "file_tracers", T_OBJECT, offsetof(CTracer, file_tracers), 0,
+ PyDoc_STR("Mapping from file name to plugin name.") },
+
+ { "should_trace_cache", T_OBJECT, offsetof(CTracer, should_trace_cache), 0,
+ PyDoc_STR("Dictionary caching should_trace results.") },
+
+ { "trace_arcs", T_OBJECT, offsetof(CTracer, trace_arcs), 0,
+ PyDoc_STR("Should we trace arcs, or just lines?") },
+
+ { "should_start_context", T_OBJECT, offsetof(CTracer, should_start_context), 0,
+ PyDoc_STR("Function for starting contexts.") },
+
+ { "switch_context", T_OBJECT, offsetof(CTracer, switch_context), 0,
+ PyDoc_STR("Function for switching to a new context.") },
+
+ { "disable_plugin", T_OBJECT, offsetof(CTracer, disable_plugin), 0,
+ PyDoc_STR("Function for disabling a plugin.") },
+
+ { NULL }
+};
+
+static PyMethodDef
+CTracer_methods[] = {
+ { "start", (PyCFunction) CTracer_start, METH_VARARGS,
+ PyDoc_STR("Start the tracer") },
+
+ { "stop", (PyCFunction) CTracer_stop, METH_VARARGS,
+ PyDoc_STR("Stop the tracer") },
+
+ { "get_stats", (PyCFunction) CTracer_get_stats, METH_VARARGS,
+ PyDoc_STR("Get statistics about the tracing") },
+
+ { "activity", (PyCFunction) CTracer_activity, METH_VARARGS,
+ PyDoc_STR("Has there been any activity?") },
+
+ { "reset_activity", (PyCFunction) CTracer_reset_activity, METH_VARARGS,
+ PyDoc_STR("Reset the activity flag") },
+
+ { NULL }
+};
+
+PyTypeObject
+CTracerType = {
+ MyType_HEAD_INIT
+ "coverage.CTracer", /*tp_name*/
+ sizeof(CTracer), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)CTracer_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ (ternaryfunc)CTracer_call, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "CTracer objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ CTracer_methods, /* tp_methods */
+ CTracer_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)CTracer_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/contrib/python/coverage/py3/coverage/ctracer/tracer.h b/contrib/python/coverage/py3/coverage/ctracer/tracer.h
new file mode 100644
index 0000000000..8994a9e3d6
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/tracer.h
@@ -0,0 +1,75 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_TRACER_H
+#define _COVERAGE_TRACER_H
+
+#include "util.h"
+#include "structmember.h"
+#include "frameobject.h"
+#include "opcode.h"
+
+#include "datastack.h"
+
+/* The CTracer type. */
+
+typedef struct CTracer {
+ PyObject_HEAD
+
+ /* Python objects manipulated directly by the Collector class. */
+ PyObject * should_trace;
+ PyObject * check_include;
+ PyObject * warn;
+ PyObject * concur_id_func;
+ PyObject * data;
+ PyObject * file_tracers;
+ PyObject * should_trace_cache;
+ PyObject * trace_arcs;
+ PyObject * should_start_context;
+ PyObject * switch_context;
+ PyObject * disable_plugin;
+
+ /* Has the tracer been started? */
+ BOOL started;
+ /* Are we tracing arcs, or just lines? */
+ BOOL tracing_arcs;
+ /* Have we had any activity? */
+ BOOL activity;
+ /* The current dynamic context. */
+ PyObject * context;
+
+ /*
+ The data stack is a stack of dictionaries. Each dictionary collects
+ data for a single source file. The data stack parallels the call stack:
+ each call pushes the new frame's file data onto the data stack, and each
+ return pops file data off.
+
+ The file data is a dictionary whose form depends on the tracing options.
+ If tracing arcs, the keys are line number pairs. If not tracing arcs,
+ the keys are line numbers. In both cases, the value is irrelevant
+ (None).
+ */
+
+ DataStack data_stack; /* Used if we aren't doing concurrency. */
+
+ PyObject * data_stack_index; /* Used if we are doing concurrency. */
+ DataStack * data_stacks;
+ int data_stacks_alloc;
+ int data_stacks_used;
+ DataStack * pdata_stack;
+
+ /* The current file's data stack entry. */
+ DataStackEntry * pcur_entry;
+
+ /* The parent frame for the last exception event, to fix missing returns. */
+ PyFrameObject * last_exc_back;
+ int last_exc_firstlineno;
+
+ Stats stats;
+} CTracer;
+
+int CTracer_intern_strings(void);
+
+extern PyTypeObject CTracerType;
+
+#endif /* _COVERAGE_TRACER_H */
diff --git a/contrib/python/coverage/py3/coverage/ctracer/util.h b/contrib/python/coverage/py3/coverage/ctracer/util.h
new file mode 100644
index 0000000000..973672db02
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/ctracer/util.h
@@ -0,0 +1,103 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+#ifndef _COVERAGE_UTIL_H
+#define _COVERAGE_UTIL_H
+
+#include <Python.h>
+
+/* Compile-time debugging helpers */
+#undef WHAT_LOG /* Define to log the WHAT params in the trace function. */
+#undef TRACE_LOG /* Define to log our bookkeeping. */
+#undef COLLECT_STATS /* Collect counters: stats are printed when tracer is stopped. */
+#undef DO_NOTHING /* Define this to make the tracer do nothing. */
+
+#if PY_VERSION_HEX >= 0x030B00A0
+// 3.11 moved f_lasti into an internal structure. This is totally the wrong way
+// to make this work, but it's all I've got until https://bugs.python.org/issue40421
+// is resolved.
+#include <internal/pycore_frame.h>
+#if PY_VERSION_HEX >= 0x030B00A7
+#define MyFrame_GetLasti(f) (PyFrame_GetLasti(f))
+#else
+#define MyFrame_GetLasti(f) ((f)->f_frame->f_lasti * 2)
+#endif
+#elif PY_VERSION_HEX >= 0x030A00A7
+// The f_lasti field changed meaning in 3.10.0a7. It had been bytes, but
+// now is instructions, so we need to adjust it to use it as a byte index.
+#define MyFrame_GetLasti(f) ((f)->f_lasti * 2)
+#else
+#define MyFrame_GetLasti(f) ((f)->f_lasti)
+#endif
+
+// Access f_code should be done through a helper starting in 3.9.
+#if PY_VERSION_HEX >= 0x03090000
+#define MyFrame_GetCode(f) (PyFrame_GetCode(f))
+#else
+#define MyFrame_GetCode(f) ((f)->f_code)
+#endif
+
+#if PY_VERSION_HEX >= 0x030B00B1
+#define MyCode_GetCode(co) (PyCode_GetCode(co))
+#define MyCode_FreeCode(code) Py_XDECREF(code)
+#elif PY_VERSION_HEX >= 0x030B00A7
+#define MyCode_GetCode(co) (PyObject_GetAttrString((PyObject *)(co), "co_code"))
+#define MyCode_FreeCode(code) Py_XDECREF(code)
+#else
+#define MyCode_GetCode(co) ((co)->co_code)
+#define MyCode_FreeCode(code)
+#endif
+
+/* Py 2.x and 3.x compatibility */
+
+#if PY_MAJOR_VERSION >= 3
+
+#define MyText_Type PyUnicode_Type
+#define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o)
+#define MyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o)
+#define MyBytes_AS_STRING(o) PyBytes_AS_STRING(o)
+#define MyText_AsString(o) PyUnicode_AsUTF8(o)
+#define MyText_FromFormat PyUnicode_FromFormat
+#define MyInt_FromInt(i) PyLong_FromLong((long)i)
+#define MyInt_AsInt(o) (int)PyLong_AsLong(o)
+#define MyText_InternFromString(s) PyUnicode_InternFromString(s)
+
+#define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0)
+
+#else
+
+#define MyText_Type PyString_Type
+#define MyText_AS_BYTES(o) (Py_INCREF(o), o)
+#define MyBytes_GET_SIZE(o) PyString_GET_SIZE(o)
+#define MyBytes_AS_STRING(o) PyString_AS_STRING(o)
+#define MyText_AsString(o) PyString_AsString(o)
+#define MyText_FromFormat PyUnicode_FromFormat
+#define MyInt_FromInt(i) PyInt_FromLong((long)i)
+#define MyInt_AsInt(o) (int)PyInt_AsLong(o)
+#define MyText_InternFromString(s) PyString_InternFromString(s)
+
+#define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0,
+
+#endif /* Py3k */
+
+// Undocumented, and not in all 2.7.x, so our own copy of it.
+#define My_XSETREF(op, op2) \
+ do { \
+ PyObject *_py_tmp = (PyObject *)(op); \
+ (op) = (op2); \
+ Py_XDECREF(_py_tmp); \
+ } while (0)
+
+/* The values returned to indicate ok or error. */
+#define RET_OK 0
+#define RET_ERROR -1
+
+/* Nicer booleans */
+typedef int BOOL;
+#define FALSE 0
+#define TRUE 1
+
+/* Only for extreme machete-mode debugging! */
+#define CRASH { printf("*** CRASH! ***\n"); *((int*)1) = 1; }
+
+#endif /* _COVERAGE_UTIL_H */
diff --git a/contrib/python/coverage/py3/coverage/data.py b/contrib/python/coverage/py3/coverage/data.py
new file mode 100644
index 0000000000..5dd1dfe3f0
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/data.py
@@ -0,0 +1,125 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Coverage data for coverage.py.
+
+This file had the 4.x JSON data support, which is now gone. This file still
+has storage-agnostic helpers, and is kept to avoid changing too many imports.
+CoverageData is now defined in sqldata.py, and imported here to keep the
+imports working.
+
+"""
+
+import glob
+import os.path
+
+from coverage.misc import CoverageException, file_be_gone
+from coverage.sqldata import CoverageData
+
+
+def line_counts(data, fullpath=False):
+ """Return a dict summarizing the line coverage data.
+
+ Keys are based on the file names, and values are the number of executed
+ lines. If `fullpath` is true, then the keys are the full pathnames of
+ the files, otherwise they are the basenames of the files.
+
+ Returns a dict mapping file names to counts of lines.
+
+ """
+ summ = {}
+ if fullpath:
+ filename_fn = lambda f: f
+ else:
+ filename_fn = os.path.basename
+ for filename in data.measured_files():
+ summ[filename_fn(filename)] = len(data.lines(filename))
+ return summ
+
+
+def add_data_to_hash(data, filename, hasher):
+ """Contribute `filename`'s data to the `hasher`.
+
+ `hasher` is a `coverage.misc.Hasher` instance to be updated with
+ the file's data. It should only get the results data, not the run
+ data.
+
+ """
+ if data.has_arcs():
+ hasher.update(sorted(data.arcs(filename) or []))
+ else:
+ hasher.update(sorted(data.lines(filename) or []))
+ hasher.update(data.file_tracer(filename))
+
+
+def combine_parallel_data(data, aliases=None, data_paths=None, strict=False, keep=False):
+ """Combine a number of data files together.
+
+ Treat `data.filename` as a file prefix, and combine the data from all
+ of the data files starting with that prefix plus a dot.
+
+ If `aliases` is provided, it's a `PathAliases` object that is used to
+ re-map paths to match the local machine's.
+
+ If `data_paths` is provided, it is a list of directories or files to
+ combine. Directories are searched for files that start with
+ `data.filename` plus dot as a prefix, and those files are combined.
+
+ If `data_paths` is not provided, then the directory portion of
+ `data.filename` is used as the directory to search for data files.
+
+ Unless `keep` is True every data file found and combined is then deleted from disk. If a file
+ cannot be read, a warning will be issued, and the file will not be
+ deleted.
+
+ If `strict` is true, and no files are found to combine, an error is
+ raised.
+
+ """
+ # Because of the os.path.abspath in the constructor, data_dir will
+ # never be an empty string.
+ data_dir, local = os.path.split(data.base_filename())
+ localdot = local + '.*'
+
+ data_paths = data_paths or [data_dir]
+ files_to_combine = []
+ for p in data_paths:
+ if os.path.isfile(p):
+ files_to_combine.append(os.path.abspath(p))
+ elif os.path.isdir(p):
+ pattern = os.path.join(os.path.abspath(p), localdot)
+ files_to_combine.extend(glob.glob(pattern))
+ else:
+ raise CoverageException("Couldn't combine from non-existent path '%s'" % (p,))
+
+ if strict and not files_to_combine:
+ raise CoverageException("No data to combine")
+
+ files_combined = 0
+ for f in files_to_combine:
+ if f == data.data_filename():
+ # Sometimes we are combining into a file which is one of the
+ # parallel files. Skip that file.
+ if data._debug.should('dataio'):
+ data._debug.write("Skipping combining ourself: %r" % (f,))
+ continue
+ if data._debug.should('dataio'):
+ data._debug.write("Combining data file %r" % (f,))
+ try:
+ new_data = CoverageData(f, debug=data._debug)
+ new_data.read()
+ except CoverageException as exc:
+ if data._warn:
+ # The CoverageException has the file name in it, so just
+ # use the message as the warning.
+ data._warn(str(exc))
+ else:
+ data.update(new_data, aliases=aliases)
+ files_combined += 1
+ if not keep:
+ if data._debug.should('dataio'):
+ data._debug.write("Deleting combined data file %r" % (f,))
+ file_be_gone(f)
+
+ if strict and not files_combined:
+ raise CoverageException("No usable data files")
diff --git a/contrib/python/coverage/py3/coverage/debug.py b/contrib/python/coverage/py3/coverage/debug.py
new file mode 100644
index 0000000000..194f16f50d
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/debug.py
@@ -0,0 +1,406 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Control of and utilities for debugging."""
+
+import contextlib
+import functools
+import inspect
+import itertools
+import os
+import pprint
+import sys
+try:
+ import _thread
+except ImportError:
+ import thread as _thread
+
+from coverage.backward import reprlib, StringIO
+from coverage.misc import isolate_module
+
+os = isolate_module(os)
+
+
+# When debugging, it can be helpful to force some options, especially when
+# debugging the configuration mechanisms you usually use to control debugging!
+# This is a list of forced debugging options.
+FORCED_DEBUG = []
+FORCED_DEBUG_FILE = None
+
+
+class DebugControl(object):
+ """Control and output for debugging."""
+
+ show_repr_attr = False # For SimpleReprMixin
+
+ def __init__(self, options, output):
+ """Configure the options and output file for debugging."""
+ self.options = list(options) + FORCED_DEBUG
+ self.suppress_callers = False
+
+ filters = []
+ if self.should('pid'):
+ filters.append(add_pid_and_tid)
+ self.output = DebugOutputFile.get_one(
+ output,
+ show_process=self.should('process'),
+ filters=filters,
+ )
+ self.raw_output = self.output.outfile
+
+ def __repr__(self):
+ return "<DebugControl options=%r raw_output=%r>" % (self.options, self.raw_output)
+
+ def should(self, option):
+ """Decide whether to output debug information in category `option`."""
+ if option == "callers" and self.suppress_callers:
+ return False
+ return (option in self.options)
+
+ @contextlib.contextmanager
+ def without_callers(self):
+ """A context manager to prevent call stacks from being logged."""
+ old = self.suppress_callers
+ self.suppress_callers = True
+ try:
+ yield
+ finally:
+ self.suppress_callers = old
+
+ def write(self, msg):
+ """Write a line of debug output.
+
+ `msg` is the line to write. A newline will be appended.
+
+ """
+ self.output.write(msg+"\n")
+ if self.should('self'):
+ caller_self = inspect.stack()[1][0].f_locals.get('self')
+ if caller_self is not None:
+ self.output.write("self: {!r}\n".format(caller_self))
+ if self.should('callers'):
+ dump_stack_frames(out=self.output, skip=1)
+ self.output.flush()
+
+
+class DebugControlString(DebugControl):
+ """A `DebugControl` that writes to a StringIO, for testing."""
+ def __init__(self, options):
+ super(DebugControlString, self).__init__(options, StringIO())
+
+ def get_output(self):
+ """Get the output text from the `DebugControl`."""
+ return self.raw_output.getvalue()
+
+
+class NoDebugging(object):
+ """A replacement for DebugControl that will never try to do anything."""
+ def should(self, option): # pylint: disable=unused-argument
+ """Should we write debug messages? Never."""
+ return False
+
+
+def info_header(label):
+ """Make a nice header string."""
+ return "--{:-<60s}".format(" "+label+" ")
+
+
+def info_formatter(info):
+ """Produce a sequence of formatted lines from info.
+
+ `info` is a sequence of pairs (label, data). The produced lines are
+ nicely formatted, ready to print.
+
+ """
+ info = list(info)
+ if not info:
+ return
+ label_len = 30
+ assert all(len(l) < label_len for l, _ in info)
+ for label, data in info:
+ if data == []:
+ data = "-none-"
+ if isinstance(data, (list, set, tuple)):
+ prefix = "%*s:" % (label_len, label)
+ for e in data:
+ yield "%*s %s" % (label_len+1, prefix, e)
+ prefix = ""
+ else:
+ yield "%*s: %s" % (label_len, label, data)
+
+
+def write_formatted_info(writer, header, info):
+ """Write a sequence of (label,data) pairs nicely."""
+ writer.write(info_header(header))
+ for line in info_formatter(info):
+ writer.write(" %s" % line)
+
+
+def short_stack(limit=None, skip=0):
+ """Return a string summarizing the call stack.
+
+ The string is multi-line, with one line per stack frame. Each line shows
+ the function name, the file name, and the line number:
+
+ ...
+ start_import_stop : /Users/ned/coverage/trunk/tests/coveragetest.py @95
+ import_local_file : /Users/ned/coverage/trunk/tests/coveragetest.py @81
+ import_local_file : /Users/ned/coverage/trunk/coverage/backward.py @159
+ ...
+
+ `limit` is the number of frames to include, defaulting to all of them.
+
+ `skip` is the number of frames to skip, so that debugging functions can
+ call this and not be included in the result.
+
+ """
+ stack = inspect.stack()[limit:skip:-1]
+ return "\n".join("%30s : %s:%d" % (t[3], t[1], t[2]) for t in stack)
+
+
+def dump_stack_frames(limit=None, out=None, skip=0):
+ """Print a summary of the stack to stdout, or someplace else."""
+ out = out or sys.stdout
+ out.write(short_stack(limit=limit, skip=skip+1))
+ out.write("\n")
+
+
+def clipped_repr(text, numchars=50):
+ """`repr(text)`, but limited to `numchars`."""
+ r = reprlib.Repr()
+ r.maxstring = numchars
+ return r.repr(text)
+
+
+def short_id(id64):
+ """Given a 64-bit id, make a shorter 16-bit one."""
+ id16 = 0
+ for offset in range(0, 64, 16):
+ id16 ^= id64 >> offset
+ return id16 & 0xFFFF
+
+
+def add_pid_and_tid(text):
+ """A filter to add pid and tid to debug messages."""
+ # Thread ids are useful, but too long. Make a shorter one.
+ tid = "{:04x}".format(short_id(_thread.get_ident()))
+ text = "{:5d}.{}: {}".format(os.getpid(), tid, text)
+ return text
+
+
+class SimpleReprMixin(object):
+ """A mixin implementing a simple __repr__."""
+ simple_repr_ignore = ['simple_repr_ignore', '$coverage.object_id']
+
+ def __repr__(self):
+ show_attrs = (
+ (k, v) for k, v in self.__dict__.items()
+ if getattr(v, "show_repr_attr", True)
+ and not callable(v)
+ and k not in self.simple_repr_ignore
+ )
+ return "<{klass} @0x{id:x} {attrs}>".format(
+ klass=self.__class__.__name__,
+ id=id(self),
+ attrs=" ".join("{}={!r}".format(k, v) for k, v in show_attrs),
+ )
+
+
+def simplify(v): # pragma: debugging
+ """Turn things which are nearly dict/list/etc into dict/list/etc."""
+ if isinstance(v, dict):
+ return {k:simplify(vv) for k, vv in v.items()}
+ elif isinstance(v, (list, tuple)):
+ return type(v)(simplify(vv) for vv in v)
+ elif hasattr(v, "__dict__"):
+ return simplify({'.'+k: v for k, v in v.__dict__.items()})
+ else:
+ return v
+
+
+def pp(v): # pragma: debugging
+ """Debug helper to pretty-print data, including SimpleNamespace objects."""
+ # Might not be needed in 3.9+
+ pprint.pprint(simplify(v))
+
+
+def filter_text(text, filters):
+ """Run `text` through a series of filters.
+
+ `filters` is a list of functions. Each takes a string and returns a
+ string. Each is run in turn.
+
+ Returns: the final string that results after all of the filters have
+ run.
+
+ """
+ clean_text = text.rstrip()
+ ending = text[len(clean_text):]
+ text = clean_text
+ for fn in filters:
+ lines = []
+ for line in text.splitlines():
+ lines.extend(fn(line).splitlines())
+ text = "\n".join(lines)
+ return text + ending
+
+
+class CwdTracker(object): # pragma: debugging
+ """A class to add cwd info to debug messages."""
+ def __init__(self):
+ self.cwd = None
+
+ def filter(self, text):
+ """Add a cwd message for each new cwd."""
+ cwd = os.getcwd()
+ if cwd != self.cwd:
+ text = "cwd is now {!r}\n".format(cwd) + text
+ self.cwd = cwd
+ return text
+
+
+class DebugOutputFile(object): # pragma: debugging
+ """A file-like object that includes pid and cwd information."""
+ def __init__(self, outfile, show_process, filters):
+ self.outfile = outfile
+ self.show_process = show_process
+ self.filters = list(filters)
+
+ if self.show_process:
+ self.filters.insert(0, CwdTracker().filter)
+ self.write("New process: executable: %r\n" % (sys.executable,))
+ self.write("New process: cmd: %r\n" % (getattr(sys, 'argv', None),))
+ if hasattr(os, 'getppid'):
+ self.write("New process: pid: %r, parent pid: %r\n" % (os.getpid(), os.getppid()))
+
+ SYS_MOD_NAME = '$coverage.debug.DebugOutputFile.the_one'
+
+ @classmethod
+ def get_one(cls, fileobj=None, show_process=True, filters=(), interim=False):
+ """Get a DebugOutputFile.
+
+ If `fileobj` is provided, then a new DebugOutputFile is made with it.
+
+ If `fileobj` isn't provided, then a file is chosen
+ (COVERAGE_DEBUG_FILE, or stderr), and a process-wide singleton
+ DebugOutputFile is made.
+
+ `show_process` controls whether the debug file adds process-level
+ information, and filters is a list of other message filters to apply.
+
+ `filters` are the text filters to apply to the stream to annotate with
+ pids, etc.
+
+ If `interim` is true, then a future `get_one` can replace this one.
+
+ """
+ if fileobj is not None:
+ # Make DebugOutputFile around the fileobj passed.
+ return cls(fileobj, show_process, filters)
+
+ # Because of the way igor.py deletes and re-imports modules,
+ # this class can be defined more than once. But we really want
+ # a process-wide singleton. So stash it in sys.modules instead of
+ # on a class attribute. Yes, this is aggressively gross.
+ the_one, is_interim = sys.modules.get(cls.SYS_MOD_NAME, (None, True))
+ if the_one is None or is_interim:
+ if fileobj is None:
+ debug_file_name = os.environ.get("COVERAGE_DEBUG_FILE", FORCED_DEBUG_FILE)
+ if debug_file_name:
+ fileobj = open(debug_file_name, "a")
+ else:
+ fileobj = sys.stderr
+ the_one = cls(fileobj, show_process, filters)
+ sys.modules[cls.SYS_MOD_NAME] = (the_one, interim)
+ return the_one
+
+ def write(self, text):
+ """Just like file.write, but filter through all our filters."""
+ self.outfile.write(filter_text(text, self.filters))
+ self.outfile.flush()
+
+ def flush(self):
+ """Flush our file."""
+ self.outfile.flush()
+
+
+def log(msg, stack=False): # pragma: debugging
+ """Write a log message as forcefully as possible."""
+ out = DebugOutputFile.get_one(interim=True)
+ out.write(msg+"\n")
+ if stack:
+ dump_stack_frames(out=out, skip=1)
+
+
+def decorate_methods(decorator, butnot=(), private=False): # pragma: debugging
+ """A class decorator to apply a decorator to methods."""
+ def _decorator(cls):
+ for name, meth in inspect.getmembers(cls, inspect.isroutine):
+ if name not in cls.__dict__:
+ continue
+ if name != "__init__":
+ if not private and name.startswith("_"):
+ continue
+ if name in butnot:
+ continue
+ setattr(cls, name, decorator(meth))
+ return cls
+ return _decorator
+
+
+def break_in_pudb(func): # pragma: debugging
+ """A function decorator to stop in the debugger for each call."""
+ @functools.wraps(func)
+ def _wrapper(*args, **kwargs):
+ import pudb
+ sys.stdout = sys.__stdout__
+ pudb.set_trace()
+ return func(*args, **kwargs)
+ return _wrapper
+
+
+OBJ_IDS = itertools.count()
+CALLS = itertools.count()
+OBJ_ID_ATTR = "$coverage.object_id"
+
+def show_calls(show_args=True, show_stack=False, show_return=False): # pragma: debugging
+ """A method decorator to debug-log each call to the function."""
+ def _decorator(func):
+ @functools.wraps(func)
+ def _wrapper(self, *args, **kwargs):
+ oid = getattr(self, OBJ_ID_ATTR, None)
+ if oid is None:
+ oid = "{:08d} {:04d}".format(os.getpid(), next(OBJ_IDS))
+ setattr(self, OBJ_ID_ATTR, oid)
+ extra = ""
+ if show_args:
+ eargs = ", ".join(map(repr, args))
+ ekwargs = ", ".join("{}={!r}".format(*item) for item in kwargs.items())
+ extra += "("
+ extra += eargs
+ if eargs and ekwargs:
+ extra += ", "
+ extra += ekwargs
+ extra += ")"
+ if show_stack:
+ extra += " @ "
+ extra += "; ".join(_clean_stack_line(l) for l in short_stack().splitlines())
+ callid = next(CALLS)
+ msg = "{} {:04d} {}{}\n".format(oid, callid, func.__name__, extra)
+ DebugOutputFile.get_one(interim=True).write(msg)
+ ret = func(self, *args, **kwargs)
+ if show_return:
+ msg = "{} {:04d} {} return {!r}\n".format(oid, callid, func.__name__, ret)
+ DebugOutputFile.get_one(interim=True).write(msg)
+ return ret
+ return _wrapper
+ return _decorator
+
+
+def _clean_stack_line(s): # pragma: debugging
+ """Simplify some paths in a stack trace, for compactness."""
+ s = s.strip()
+ s = s.replace(os.path.dirname(__file__) + '/', '')
+ s = s.replace(os.path.dirname(os.__file__) + '/', '')
+ s = s.replace(sys.prefix + '/', '')
+ return s
diff --git a/contrib/python/coverage/py3/coverage/disposition.py b/contrib/python/coverage/py3/coverage/disposition.py
new file mode 100644
index 0000000000..9b9a997d8a
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/disposition.py
@@ -0,0 +1,37 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Simple value objects for tracking what to do with files."""
+
+
+class FileDisposition(object):
+ """A simple value type for recording what to do with a file."""
+ pass
+
+
+# FileDisposition "methods": FileDisposition is a pure value object, so it can
+# be implemented in either C or Python. Acting on them is done with these
+# functions.
+
+def disposition_init(cls, original_filename):
+ """Construct and initialize a new FileDisposition object."""
+ disp = cls()
+ disp.original_filename = original_filename
+ disp.canonical_filename = original_filename
+ disp.source_filename = None
+ disp.trace = False
+ disp.reason = ""
+ disp.file_tracer = None
+ disp.has_dynamic_filename = False
+ return disp
+
+
+def disposition_debug_msg(disp):
+ """Make a nice debug message of what the FileDisposition is doing."""
+ if disp.trace:
+ msg = "Tracing %r" % (disp.original_filename,)
+ if disp.file_tracer:
+ msg += ": will be traced by %r" % disp.file_tracer
+ else:
+ msg = "Not tracing %r: %s" % (disp.original_filename, disp.reason)
+ return msg
diff --git a/contrib/python/coverage/py3/coverage/env.py b/contrib/python/coverage/py3/coverage/env.py
new file mode 100644
index 0000000000..ea78a5be89
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/env.py
@@ -0,0 +1,130 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Determine facts about the environment."""
+
+import os
+import platform
+import sys
+
+# Operating systems.
+WINDOWS = sys.platform == "win32"
+LINUX = sys.platform.startswith("linux")
+
+# Python implementations.
+CPYTHON = (platform.python_implementation() == "CPython")
+PYPY = (platform.python_implementation() == "PyPy")
+JYTHON = (platform.python_implementation() == "Jython")
+IRONPYTHON = (platform.python_implementation() == "IronPython")
+
+# Python versions. We amend version_info with one more value, a zero if an
+# official version, or 1 if built from source beyond an official version.
+PYVERSION = sys.version_info + (int(platform.python_version()[-1] == "+"),)
+PY2 = PYVERSION < (3, 0)
+PY3 = PYVERSION >= (3, 0)
+
+if PYPY:
+ PYPYVERSION = sys.pypy_version_info
+
+PYPY2 = PYPY and PY2
+PYPY3 = PYPY and PY3
+
+# Python behavior.
+class PYBEHAVIOR(object):
+ """Flags indicating this Python's behavior."""
+
+ pep626 = CPYTHON and (PYVERSION > (3, 10, 0, 'alpha', 4))
+
+ # Is "if __debug__" optimized away?
+ if PYPY3:
+ optimize_if_debug = True
+ elif PYPY2:
+ optimize_if_debug = False
+ else:
+ optimize_if_debug = not pep626
+
+ # Is "if not __debug__" optimized away?
+ optimize_if_not_debug = (not PYPY) and (PYVERSION >= (3, 7, 0, 'alpha', 4))
+ if pep626:
+ optimize_if_not_debug = False
+ if PYPY3:
+ optimize_if_not_debug = True
+
+ # Is "if not __debug__" optimized away even better?
+ optimize_if_not_debug2 = (not PYPY) and (PYVERSION >= (3, 8, 0, 'beta', 1))
+ if pep626:
+ optimize_if_not_debug2 = False
+
+ # Do we have yield-from?
+ yield_from = (PYVERSION >= (3, 3))
+
+ # Do we have PEP 420 namespace packages?
+ namespaces_pep420 = (PYVERSION >= (3, 3))
+
+ # Do .pyc files have the source file size recorded in them?
+ size_in_pyc = (PYVERSION >= (3, 3))
+
+ # Do we have async and await syntax?
+ async_syntax = (PYVERSION >= (3, 5))
+
+ # PEP 448 defined additional unpacking generalizations
+ unpackings_pep448 = (PYVERSION >= (3, 5))
+
+ # Can co_lnotab have negative deltas?
+ negative_lnotab = (PYVERSION >= (3, 6)) and not (PYPY and PYPYVERSION < (7, 2))
+
+ # Do .pyc files conform to PEP 552? Hash-based pyc's.
+ hashed_pyc_pep552 = (PYVERSION >= (3, 7, 0, 'alpha', 4))
+
+ # Python 3.7.0b3 changed the behavior of the sys.path[0] entry for -m. It
+ # used to be an empty string (meaning the current directory). It changed
+ # to be the actual path to the current directory, so that os.chdir wouldn't
+ # affect the outcome.
+ actual_syspath0_dash_m = CPYTHON and (PYVERSION >= (3, 7, 0, 'beta', 3))
+
+ # 3.7 changed how functions with only docstrings are numbered.
+ docstring_only_function = (not PYPY) and ((3, 7, 0, 'beta', 5) <= PYVERSION <= (3, 10))
+
+ # When a break/continue/return statement in a try block jumps to a finally
+ # block, does the finally block do the break/continue/return (pre-3.8), or
+ # does the finally jump back to the break/continue/return (3.8) to do the
+ # work?
+ finally_jumps_back = ((3, 8) <= PYVERSION < (3, 10))
+
+ # When a function is decorated, does the trace function get called for the
+ # @-line and also the def-line (new behavior in 3.8)? Or just the @-line
+ # (old behavior)?
+ trace_decorated_def = (PYVERSION >= (3, 8))
+
+ # Are while-true loops optimized into absolute jumps with no loop setup?
+ nix_while_true = (PYVERSION >= (3, 8))
+
+ # Python 3.9a1 made sys.argv[0] and other reported files absolute paths.
+ report_absolute_files = (PYVERSION >= (3, 9))
+
+ # Lines after break/continue/return/raise are no longer compiled into the
+ # bytecode. They used to be marked as missing, now they aren't executable.
+ omit_after_jump = pep626
+
+ # PyPy has always omitted statements after return.
+ omit_after_return = omit_after_jump or PYPY
+
+ # Modules used to have firstlineno equal to the line number of the first
+ # real line of code. Now they always start at 1.
+ module_firstline_1 = pep626
+
+ # Are "if 0:" lines (and similar) kept in the compiled code?
+ keep_constant_test = pep626
+
+# Coverage.py specifics.
+
+# Are we using the C-implemented trace function?
+C_TRACER = os.getenv('COVERAGE_TEST_TRACER', 'c') == 'c'
+
+# Are we coverage-measuring ourselves?
+METACOV = os.getenv('COVERAGE_COVERAGE', '') != ''
+
+# Are we running our test suite?
+# Even when running tests, you can use COVERAGE_TESTING=0 to disable the
+# test-specific behavior like contracts.
+TESTING = os.getenv('COVERAGE_TESTING', '') == 'True'
diff --git a/contrib/python/coverage/py3/coverage/execfile.py b/contrib/python/coverage/py3/coverage/execfile.py
new file mode 100644
index 0000000000..29409d517a
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/execfile.py
@@ -0,0 +1,362 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Execute files of Python code."""
+
+import inspect
+import marshal
+import os
+import struct
+import sys
+import types
+
+from coverage import env
+from coverage.backward import BUILTINS
+from coverage.backward import PYC_MAGIC_NUMBER, imp, importlib_util_find_spec
+from coverage.files import canonical_filename, python_reported_file
+from coverage.misc import CoverageException, ExceptionDuringRun, NoCode, NoSource, isolate_module
+from coverage.phystokens import compile_unicode
+from coverage.python import get_python_source
+
+os = isolate_module(os)
+
+
+class DummyLoader(object):
+ """A shim for the pep302 __loader__, emulating pkgutil.ImpLoader.
+
+ Currently only implements the .fullname attribute
+ """
+ def __init__(self, fullname, *_args):
+ self.fullname = fullname
+
+
+if importlib_util_find_spec:
+ def find_module(modulename):
+ """Find the module named `modulename`.
+
+ Returns the file path of the module, the name of the enclosing
+ package, and the spec.
+ """
+ try:
+ spec = importlib_util_find_spec(modulename)
+ except ImportError as err:
+ raise NoSource(str(err))
+ if not spec:
+ raise NoSource("No module named %r" % (modulename,))
+ pathname = spec.origin
+ packagename = spec.name
+ if spec.submodule_search_locations:
+ mod_main = modulename + ".__main__"
+ spec = importlib_util_find_spec(mod_main)
+ if not spec:
+ raise NoSource(
+ "No module named %s; "
+ "%r is a package and cannot be directly executed"
+ % (mod_main, modulename)
+ )
+ pathname = spec.origin
+ packagename = spec.name
+ packagename = packagename.rpartition(".")[0]
+ return pathname, packagename, spec
+else:
+ def find_module(modulename):
+ """Find the module named `modulename`.
+
+ Returns the file path of the module, the name of the enclosing
+ package, and None (where a spec would have been).
+ """
+ openfile = None
+ glo, loc = globals(), locals()
+ try:
+ # Search for the module - inside its parent package, if any - using
+ # standard import mechanics.
+ if '.' in modulename:
+ packagename, name = modulename.rsplit('.', 1)
+ package = __import__(packagename, glo, loc, ['__path__'])
+ searchpath = package.__path__
+ else:
+ packagename, name = None, modulename
+ searchpath = None # "top-level search" in imp.find_module()
+ openfile, pathname, _ = imp.find_module(name, searchpath)
+
+ # Complain if this is a magic non-file module.
+ if openfile is None and pathname is None:
+ raise NoSource(
+ "module does not live in a file: %r" % modulename
+ )
+
+ # If `modulename` is actually a package, not a mere module, then we
+ # pretend to be Python 2.7 and try running its __main__.py script.
+ if openfile is None:
+ packagename = modulename
+ name = '__main__'
+ package = __import__(packagename, glo, loc, ['__path__'])
+ searchpath = package.__path__
+ openfile, pathname, _ = imp.find_module(name, searchpath)
+ except ImportError as err:
+ raise NoSource(str(err))
+ finally:
+ if openfile:
+ openfile.close()
+
+ return pathname, packagename, None
+
+
+class PyRunner(object):
+ """Multi-stage execution of Python code.
+
+ This is meant to emulate real Python execution as closely as possible.
+
+ """
+ def __init__(self, args, as_module=False):
+ self.args = args
+ self.as_module = as_module
+
+ self.arg0 = args[0]
+ self.package = self.modulename = self.pathname = self.loader = self.spec = None
+
+ def prepare(self):
+ """Set sys.path properly.
+
+ This needs to happen before any importing, and without importing anything.
+ """
+ if self.as_module:
+ if env.PYBEHAVIOR.actual_syspath0_dash_m:
+ path0 = os.getcwd()
+ else:
+ path0 = ""
+ elif os.path.isdir(self.arg0):
+ # Running a directory means running the __main__.py file in that
+ # directory.
+ path0 = self.arg0
+ else:
+ path0 = os.path.abspath(os.path.dirname(self.arg0))
+
+ if os.path.isdir(sys.path[0]):
+ # sys.path fakery. If we are being run as a command, then sys.path[0]
+ # is the directory of the "coverage" script. If this is so, replace
+ # sys.path[0] with the directory of the file we're running, or the
+ # current directory when running modules. If it isn't so, then we
+ # don't know what's going on, and just leave it alone.
+ top_file = inspect.stack()[-1][0].f_code.co_filename
+ sys_path_0_abs = os.path.abspath(sys.path[0])
+ top_file_dir_abs = os.path.abspath(os.path.dirname(top_file))
+ sys_path_0_abs = canonical_filename(sys_path_0_abs)
+ top_file_dir_abs = canonical_filename(top_file_dir_abs)
+ if sys_path_0_abs != top_file_dir_abs:
+ path0 = None
+
+ else:
+ # sys.path[0] is a file. Is the next entry the directory containing
+ # that file?
+ if sys.path[1] == os.path.dirname(sys.path[0]):
+ # Can it be right to always remove that?
+ del sys.path[1]
+
+ if path0 is not None:
+ sys.path[0] = python_reported_file(path0)
+
+ def _prepare2(self):
+ """Do more preparation to run Python code.
+
+ Includes finding the module to run and adjusting sys.argv[0].
+ This method is allowed to import code.
+
+ """
+ if self.as_module:
+ self.modulename = self.arg0
+ pathname, self.package, self.spec = find_module(self.modulename)
+ if self.spec is not None:
+ self.modulename = self.spec.name
+ self.loader = DummyLoader(self.modulename)
+ self.pathname = os.path.abspath(pathname)
+ self.args[0] = self.arg0 = self.pathname
+ elif os.path.isdir(self.arg0):
+ # Running a directory means running the __main__.py file in that
+ # directory.
+ for ext in [".py", ".pyc", ".pyo"]:
+ try_filename = os.path.join(self.arg0, "__main__" + ext)
+ if os.path.exists(try_filename):
+ self.arg0 = try_filename
+ break
+ else:
+ raise NoSource("Can't find '__main__' module in '%s'" % self.arg0)
+
+ if env.PY2:
+ self.arg0 = os.path.abspath(self.arg0)
+
+ # Make a spec. I don't know if this is the right way to do it.
+ try:
+ import importlib.machinery
+ except ImportError:
+ pass
+ else:
+ try_filename = python_reported_file(try_filename)
+ self.spec = importlib.machinery.ModuleSpec("__main__", None, origin=try_filename)
+ self.spec.has_location = True
+ self.package = ""
+ self.loader = DummyLoader("__main__")
+ else:
+ if env.PY3:
+ self.loader = DummyLoader("__main__")
+
+ self.arg0 = python_reported_file(self.arg0)
+
+ def run(self):
+ """Run the Python code!"""
+
+ self._prepare2()
+
+ # Create a module to serve as __main__
+ main_mod = types.ModuleType('__main__')
+
+ from_pyc = self.arg0.endswith((".pyc", ".pyo"))
+ main_mod.__file__ = self.arg0
+ if from_pyc:
+ main_mod.__file__ = main_mod.__file__[:-1]
+ if self.package is not None:
+ main_mod.__package__ = self.package
+ main_mod.__loader__ = self.loader
+ if self.spec is not None:
+ main_mod.__spec__ = self.spec
+
+ main_mod.__builtins__ = BUILTINS
+
+ sys.modules['__main__'] = main_mod
+
+ # Set sys.argv properly.
+ sys.argv = self.args
+
+ try:
+ # Make a code object somehow.
+ if from_pyc:
+ code = make_code_from_pyc(self.arg0)
+ else:
+ code = make_code_from_py(self.arg0)
+ except CoverageException:
+ raise
+ except Exception as exc:
+ msg = "Couldn't run '{filename}' as Python code: {exc.__class__.__name__}: {exc}"
+ raise CoverageException(msg.format(filename=self.arg0, exc=exc))
+
+ # Execute the code object.
+ # Return to the original directory in case the test code exits in
+ # a non-existent directory.
+ cwd = os.getcwd()
+ try:
+ exec(code, main_mod.__dict__)
+ except SystemExit: # pylint: disable=try-except-raise
+ # The user called sys.exit(). Just pass it along to the upper
+ # layers, where it will be handled.
+ raise
+ except Exception:
+ # Something went wrong while executing the user code.
+ # Get the exc_info, and pack them into an exception that we can
+ # throw up to the outer loop. We peel one layer off the traceback
+ # so that the coverage.py code doesn't appear in the final printed
+ # traceback.
+ typ, err, tb = sys.exc_info()
+
+ # PyPy3 weirdness. If I don't access __context__, then somehow it
+ # is non-None when the exception is reported at the upper layer,
+ # and a nested exception is shown to the user. This getattr fixes
+ # it somehow? https://bitbucket.org/pypy/pypy/issue/1903
+ getattr(err, '__context__', None)
+
+ # Call the excepthook.
+ try:
+ if hasattr(err, "__traceback__"):
+ err.__traceback__ = err.__traceback__.tb_next
+ sys.excepthook(typ, err, tb.tb_next)
+ except SystemExit: # pylint: disable=try-except-raise
+ raise
+ except Exception:
+ # Getting the output right in the case of excepthook
+ # shenanigans is kind of involved.
+ sys.stderr.write("Error in sys.excepthook:\n")
+ typ2, err2, tb2 = sys.exc_info()
+ err2.__suppress_context__ = True
+ if hasattr(err2, "__traceback__"):
+ err2.__traceback__ = err2.__traceback__.tb_next
+ sys.__excepthook__(typ2, err2, tb2.tb_next)
+ sys.stderr.write("\nOriginal exception was:\n")
+ raise ExceptionDuringRun(typ, err, tb.tb_next)
+ else:
+ sys.exit(1)
+ finally:
+ os.chdir(cwd)
+
+
+def run_python_module(args):
+ """Run a Python module, as though with ``python -m name args...``.
+
+ `args` is the argument array to present as sys.argv, including the first
+ element naming the module being executed.
+
+ This is a helper for tests, to encapsulate how to use PyRunner.
+
+ """
+ runner = PyRunner(args, as_module=True)
+ runner.prepare()
+ runner.run()
+
+
+def run_python_file(args):
+ """Run a Python file as if it were the main program on the command line.
+
+ `args` is the argument array to present as sys.argv, including the first
+ element naming the file being executed. `package` is the name of the
+ enclosing package, if any.
+
+ This is a helper for tests, to encapsulate how to use PyRunner.
+
+ """
+ runner = PyRunner(args, as_module=False)
+ runner.prepare()
+ runner.run()
+
+
+def make_code_from_py(filename):
+ """Get source from `filename` and make a code object of it."""
+ # Open the source file.
+ try:
+ source = get_python_source(filename)
+ except (IOError, NoSource):
+ raise NoSource("No file to run: '%s'" % filename)
+
+ code = compile_unicode(source, filename, "exec")
+ return code
+
+
+def make_code_from_pyc(filename):
+ """Get a code object from a .pyc file."""
+ try:
+ fpyc = open(filename, "rb")
+ except IOError:
+ raise NoCode("No file to run: '%s'" % filename)
+
+ with fpyc:
+ # First four bytes are a version-specific magic number. It has to
+ # match or we won't run the file.
+ magic = fpyc.read(4)
+ if magic != PYC_MAGIC_NUMBER:
+ raise NoCode("Bad magic number in .pyc file: {} != {}".format(magic, PYC_MAGIC_NUMBER))
+
+ date_based = True
+ if env.PYBEHAVIOR.hashed_pyc_pep552:
+ flags = struct.unpack('<L', fpyc.read(4))[0]
+ hash_based = flags & 0x01
+ if hash_based:
+ fpyc.read(8) # Skip the hash.
+ date_based = False
+ if date_based:
+ # Skip the junk in the header that we don't need.
+ fpyc.read(4) # Skip the moddate.
+ if env.PYBEHAVIOR.size_in_pyc:
+ # 3.3 added another long to the header (size), skip it.
+ fpyc.read(4)
+
+ # The rest of the file is the code object we want.
+ code = marshal.load(fpyc)
+
+ return code
diff --git a/contrib/python/coverage/py3/coverage/files.py b/contrib/python/coverage/py3/coverage/files.py
new file mode 100644
index 0000000000..5133ad07f3
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/files.py
@@ -0,0 +1,441 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""File wrangling."""
+
+import hashlib
+import fnmatch
+import ntpath
+import os
+import os.path
+import posixpath
+import re
+import sys
+
+from coverage import env
+from coverage.backward import unicode_class
+from coverage.misc import contract, CoverageException, join_regex, isolate_module
+
+
+os = isolate_module(os)
+
+
+def set_relative_directory():
+ """Set the directory that `relative_filename` will be relative to."""
+ global RELATIVE_DIR, CANONICAL_FILENAME_CACHE
+
+ # The absolute path to our current directory.
+ RELATIVE_DIR = os.path.normcase(abs_file(os.curdir) + os.sep)
+
+ # Cache of results of calling the canonical_filename() method, to
+ # avoid duplicating work.
+ CANONICAL_FILENAME_CACHE = {}
+
+
+def relative_directory():
+ """Return the directory that `relative_filename` is relative to."""
+ return RELATIVE_DIR
+
+
+@contract(returns='unicode')
+def relative_filename(filename):
+ """Return the relative form of `filename`.
+
+ The file name will be relative to the current directory when the
+ `set_relative_directory` was called.
+
+ """
+ fnorm = os.path.normcase(filename)
+ if fnorm.startswith(RELATIVE_DIR):
+ filename = filename[len(RELATIVE_DIR):]
+ return unicode_filename(filename)
+
+
+@contract(returns='unicode')
+def canonical_filename(filename):
+ """Return a canonical file name for `filename`.
+
+ An absolute path with no redundant components and normalized case.
+
+ """
+ if filename not in CANONICAL_FILENAME_CACHE:
+ cf = filename
+ if not os.path.isabs(filename):
+ for path in [os.curdir] + sys.path:
+ if path is None:
+ continue
+ f = os.path.join(path, filename)
+ try:
+ exists = os.path.exists(f)
+ except UnicodeError:
+ exists = False
+ if exists:
+ cf = f
+ break
+ cf = abs_file(cf)
+ CANONICAL_FILENAME_CACHE[filename] = cf
+ return CANONICAL_FILENAME_CACHE[filename]
+
+if getattr(sys, 'is_standalone_binary', False):
+ # filename for py files in binary is already canonical,
+ # it's relative to the arcadia root
+ def canonical_filename(filename):
+ # next assert does not needed in case when we load coverage from not arcadia source in arcadia binary
+ # assert not filename.startswith("/"), filename
+ return filename
+
+MAX_FLAT = 200
+
+@contract(filename='unicode', returns='unicode')
+def flat_rootname(filename):
+ """A base for a flat file name to correspond to this file.
+
+ Useful for writing files about the code where you want all the files in
+ the same directory, but need to differentiate same-named files from
+ different directories.
+
+ For example, the file a/b/c.py will return 'a_b_c_py'
+
+ """
+ name = ntpath.splitdrive(filename)[1]
+ name = re.sub(r"[\\/.:]", "_", name)
+ if len(name) > MAX_FLAT:
+ h = hashlib.sha1(name.encode('UTF-8')).hexdigest()
+ name = name[-(MAX_FLAT-len(h)-1):] + '_' + h
+ return name
+
+
+if env.WINDOWS:
+
+ _ACTUAL_PATH_CACHE = {}
+ _ACTUAL_PATH_LIST_CACHE = {}
+
+ def actual_path(path):
+ """Get the actual path of `path`, including the correct case."""
+ if env.PY2 and isinstance(path, unicode_class):
+ path = path.encode(sys.getfilesystemencoding())
+ if path in _ACTUAL_PATH_CACHE:
+ return _ACTUAL_PATH_CACHE[path]
+
+ head, tail = os.path.split(path)
+ if not tail:
+ # This means head is the drive spec: normalize it.
+ actpath = head.upper()
+ elif not head:
+ actpath = tail
+ else:
+ head = actual_path(head)
+ if head in _ACTUAL_PATH_LIST_CACHE:
+ files = _ACTUAL_PATH_LIST_CACHE[head]
+ else:
+ try:
+ files = os.listdir(head)
+ except Exception:
+ # This will raise OSError, or this bizarre TypeError:
+ # https://bugs.python.org/issue1776160
+ files = []
+ _ACTUAL_PATH_LIST_CACHE[head] = files
+ normtail = os.path.normcase(tail)
+ for f in files:
+ if os.path.normcase(f) == normtail:
+ tail = f
+ break
+ actpath = os.path.join(head, tail)
+ _ACTUAL_PATH_CACHE[path] = actpath
+ return actpath
+
+else:
+ def actual_path(filename):
+ """The actual path for non-Windows platforms."""
+ return filename
+
+
+if env.PY2:
+ @contract(returns='unicode')
+ def unicode_filename(filename):
+ """Return a Unicode version of `filename`."""
+ if isinstance(filename, str):
+ encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
+ filename = filename.decode(encoding, "replace")
+ return filename
+else:
+ @contract(filename='unicode', returns='unicode')
+ def unicode_filename(filename):
+ """Return a Unicode version of `filename`."""
+ return filename
+
+
+@contract(returns='unicode')
+def abs_file(path):
+ """Return the absolute normalized form of `path`."""
+ try:
+ path = os.path.realpath(path)
+ except UnicodeError:
+ pass
+ path = os.path.abspath(path)
+ path = actual_path(path)
+ path = unicode_filename(path)
+ return path
+
+
+def python_reported_file(filename):
+ """Return the string as Python would describe this file name."""
+ if env.PYBEHAVIOR.report_absolute_files:
+ filename = os.path.abspath(filename)
+ return filename
+
+
+RELATIVE_DIR = None
+CANONICAL_FILENAME_CACHE = None
+set_relative_directory()
+
+
+def isabs_anywhere(filename):
+ """Is `filename` an absolute path on any OS?"""
+ return ntpath.isabs(filename) or posixpath.isabs(filename)
+
+
+def prep_patterns(patterns):
+ """Prepare the file patterns for use in a `FnmatchMatcher`.
+
+ If a pattern starts with a wildcard, it is used as a pattern
+ as-is. If it does not start with a wildcard, then it is made
+ absolute with the current directory.
+
+ If `patterns` is None, an empty list is returned.
+
+ """
+ prepped = []
+ for p in patterns or []:
+ if p.startswith(("*", "?")):
+ prepped.append(p)
+ else:
+ prepped.append(abs_file(p))
+ return prepped
+
+
+class TreeMatcher(object):
+ """A matcher for files in a tree.
+
+ Construct with a list of paths, either files or directories. Paths match
+ with the `match` method if they are one of the files, or if they are
+ somewhere in a subtree rooted at one of the directories.
+
+ """
+ def __init__(self, paths):
+ self.paths = list(paths)
+
+ def __repr__(self):
+ return "<TreeMatcher %r>" % self.paths
+
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.paths
+
+ def match(self, fpath):
+ """Does `fpath` indicate a file in one of our trees?"""
+ for p in self.paths:
+ if fpath.startswith(p):
+ if fpath == p:
+ # This is the same file!
+ return True
+ if fpath[len(p)] == os.sep:
+ # This is a file in the directory
+ return True
+ return False
+
+
+class ModuleMatcher(object):
+ """A matcher for modules in a tree."""
+ def __init__(self, module_names):
+ self.modules = list(module_names)
+
+ def __repr__(self):
+ return "<ModuleMatcher %r>" % (self.modules)
+
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.modules
+
+ def match(self, module_name):
+ """Does `module_name` indicate a module in one of our packages?"""
+ if not module_name:
+ return False
+
+ for m in self.modules:
+ if module_name.startswith(m):
+ if module_name == m:
+ return True
+ if module_name[len(m)] == '.':
+ # This is a module in the package
+ return True
+
+ return False
+
+
+class FnmatchMatcher(object):
+ """A matcher for files by file name pattern."""
+ def __init__(self, pats):
+ self.pats = list(pats)
+ self.re = fnmatches_to_regex(self.pats, case_insensitive=env.WINDOWS)
+
+ def __repr__(self):
+ return "<FnmatchMatcher %r>" % self.pats
+
+ def info(self):
+ """A list of strings for displaying when dumping state."""
+ return self.pats
+
+ def match(self, fpath):
+ """Does `fpath` match one of our file name patterns?"""
+ return self.re.match(fpath) is not None
+
+
+def sep(s):
+ """Find the path separator used in this string, or os.sep if none."""
+ sep_match = re.search(r"[\\/]", s)
+ if sep_match:
+ the_sep = sep_match.group(0)
+ else:
+ the_sep = os.sep
+ return the_sep
+
+
+def fnmatches_to_regex(patterns, case_insensitive=False, partial=False):
+ """Convert fnmatch patterns to a compiled regex that matches any of them.
+
+ Slashes are always converted to match either slash or backslash, for
+ Windows support, even when running elsewhere.
+
+ If `partial` is true, then the pattern will match if the target string
+ starts with the pattern. Otherwise, it must match the entire string.
+
+ Returns: a compiled regex object. Use the .match method to compare target
+ strings.
+
+ """
+ regexes = (fnmatch.translate(pattern) for pattern in patterns)
+ # Python3.7 fnmatch translates "/" as "/". Before that, it translates as "\/",
+ # so we have to deal with maybe a backslash.
+ regexes = (re.sub(r"\\?/", r"[\\\\/]", regex) for regex in regexes)
+
+ if partial:
+ # fnmatch always adds a \Z to match the whole string, which we don't
+ # want, so we remove the \Z. While removing it, we only replace \Z if
+ # followed by paren (introducing flags), or at end, to keep from
+ # destroying a literal \Z in the pattern.
+ regexes = (re.sub(r'\\Z(\(\?|$)', r'\1', regex) for regex in regexes)
+
+ flags = 0
+ if case_insensitive:
+ flags |= re.IGNORECASE
+ compiled = re.compile(join_regex(regexes), flags=flags)
+
+ return compiled
+
+
+class PathAliases(object):
+ """A collection of aliases for paths.
+
+ When combining data files from remote machines, often the paths to source
+ code are different, for example, due to OS differences, or because of
+ serialized checkouts on continuous integration machines.
+
+ A `PathAliases` object tracks a list of pattern/result pairs, and can
+ map a path through those aliases to produce a unified path.
+
+ """
+ def __init__(self):
+ self.aliases = []
+
+ def pprint(self): # pragma: debugging
+ """Dump the important parts of the PathAliases, for debugging."""
+ for regex, result in self.aliases:
+ print("{!r} --> {!r}".format(regex.pattern, result))
+
+ def add(self, pattern, result):
+ """Add the `pattern`/`result` pair to the list of aliases.
+
+ `pattern` is an `fnmatch`-style pattern. `result` is a simple
+ string. When mapping paths, if a path starts with a match against
+ `pattern`, then that match is replaced with `result`. This models
+ isomorphic source trees being rooted at different places on two
+ different machines.
+
+ `pattern` can't end with a wildcard component, since that would
+ match an entire tree, and not just its root.
+
+ """
+ pattern_sep = sep(pattern)
+
+ if len(pattern) > 1:
+ pattern = pattern.rstrip(r"\/")
+
+ # The pattern can't end with a wildcard component.
+ if pattern.endswith("*"):
+ raise CoverageException("Pattern must not end with wildcards.")
+
+ # The pattern is meant to match a filepath. Let's make it absolute
+ # unless it already is, or is meant to match any prefix.
+ if not pattern.startswith('*') and not isabs_anywhere(pattern +
+ pattern_sep):
+ pattern = abs_file(pattern)
+ if not pattern.endswith(pattern_sep):
+ pattern += pattern_sep
+
+ # Make a regex from the pattern.
+ regex = fnmatches_to_regex([pattern], case_insensitive=True, partial=True)
+
+ # Normalize the result: it must end with a path separator.
+ result_sep = sep(result)
+ result = result.rstrip(r"\/") + result_sep
+ self.aliases.append((regex, result))
+
+ def map(self, path):
+ """Map `path` through the aliases.
+
+ `path` is checked against all of the patterns. The first pattern to
+ match is used to replace the root of the path with the result root.
+ Only one pattern is ever used. If no patterns match, `path` is
+ returned unchanged.
+
+ The separator style in the result is made to match that of the result
+ in the alias.
+
+ Returns the mapped path. If a mapping has happened, this is a
+ canonical path. If no mapping has happened, it is the original value
+ of `path` unchanged.
+
+ """
+ for regex, result in self.aliases:
+ m = regex.match(path)
+ if m:
+ new = path.replace(m.group(0), result)
+ new = new.replace(sep(path), sep(result))
+ new = canonical_filename(new)
+ return new
+ return path
+
+
+def find_python_files(dirname):
+ """Yield all of the importable Python files in `dirname`, recursively.
+
+ To be importable, the files have to be in a directory with a __init__.py,
+ except for `dirname` itself, which isn't required to have one. The
+ assumption is that `dirname` was specified directly, so the user knows
+ best, but sub-directories are checked for a __init__.py to be sure we only
+ find the importable files.
+
+ """
+ for i, (dirpath, dirnames, filenames) in enumerate(os.walk(dirname)):
+ if i > 0 and '__init__.py' not in filenames:
+ # If a directory doesn't have __init__.py, then it isn't
+ # importable and neither are its files
+ del dirnames[:]
+ continue
+ for filename in filenames:
+ # We're only interested in files that look like reasonable Python
+ # files: Must end with .py or .pyw, and must not have certain funny
+ # characters that probably mean they are editor junk.
+ if re.match(r"^[^.#~!$@%^&*()+=,]+\.pyw?$", filename):
+ yield os.path.join(dirpath, filename)
diff --git a/contrib/python/coverage/py3/coverage/fullcoverage/encodings.py b/contrib/python/coverage/py3/coverage/fullcoverage/encodings.py
new file mode 100644
index 0000000000..aeb416e406
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/fullcoverage/encodings.py
@@ -0,0 +1,60 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Imposter encodings module that installs a coverage-style tracer.
+
+This is NOT the encodings module; it is an imposter that sets up tracing
+instrumentation and then replaces itself with the real encodings module.
+
+If the directory that holds this file is placed first in the PYTHONPATH when
+using "coverage" to run Python's tests, then this file will become the very
+first module imported by the internals of Python 3. It installs a
+coverage.py-compatible trace function that can watch Standard Library modules
+execute from the very earliest stages of Python's own boot process. This fixes
+a problem with coverage.py - that it starts too late to trace the coverage of
+many of the most fundamental modules in the Standard Library.
+
+"""
+
+import sys
+
+class FullCoverageTracer(object):
+ def __init__(self):
+ # `traces` is a list of trace events. Frames are tricky: the same
+ # frame object is used for a whole scope, with new line numbers
+ # written into it. So in one scope, all the frame objects are the
+ # same object, and will eventually all will point to the last line
+ # executed. So we keep the line numbers alongside the frames.
+ # The list looks like:
+ #
+ # traces = [
+ # ((frame, event, arg), lineno), ...
+ # ]
+ #
+ self.traces = []
+
+ def fullcoverage_trace(self, *args):
+ frame, event, arg = args
+ self.traces.append((args, frame.f_lineno))
+ return self.fullcoverage_trace
+
+sys.settrace(FullCoverageTracer().fullcoverage_trace)
+
+# In coverage/files.py is actual_filename(), which uses glob.glob. I don't
+# understand why, but that use of glob borks everything if fullcoverage is in
+# effect. So here we make an ugly hail-mary pass to switch off glob.glob over
+# there. This means when using fullcoverage, Windows path names will not be
+# their actual case.
+
+#sys.fullcoverage = True
+
+# Finally, remove our own directory from sys.path; remove ourselves from
+# sys.modules; and re-import "encodings", which will be the real package
+# this time. Note that the delete from sys.modules dictionary has to
+# happen last, since all of the symbols in this module will become None
+# at that exact moment, including "sys".
+
+parentdir = max(filter(__file__.startswith, sys.path), key=len)
+sys.path.remove(parentdir)
+del sys.modules['encodings']
+import encodings
diff --git a/contrib/python/coverage/py3/coverage/html.py b/contrib/python/coverage/py3/coverage/html.py
new file mode 100644
index 0000000000..9d8e342716
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/html.py
@@ -0,0 +1,539 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""HTML reporting for coverage.py."""
+
+import datetime
+import json
+import os
+import re
+import shutil
+import sys
+
+import coverage
+from coverage import env
+from coverage.backward import iitems, SimpleNamespace, format_local_datetime
+from coverage.data import add_data_to_hash
+from coverage.files import flat_rootname
+from coverage.misc import CoverageException, ensure_dir, file_be_gone, Hasher, isolate_module
+from coverage.report import get_analysis_to_report
+from coverage.results import Numbers
+from coverage.templite import Templite
+
+os = isolate_module(os)
+
+
+# Static files are looked for in a list of places.
+STATIC_PATH = [
+ # The place Debian puts system Javascript libraries.
+ "/usr/share/javascript",
+
+ # Our htmlfiles directory.
+ os.path.join(os.path.dirname(__file__), "htmlfiles"),
+]
+
+
+def data_filename(fname, pkgdir=""):
+ """Return the path to a data file of ours.
+
+ The file is searched for on `STATIC_PATH`, and the first place it's found,
+ is returned.
+
+ Each directory in `STATIC_PATH` is searched as-is, and also, if `pkgdir`
+ is provided, at that sub-directory.
+
+ """
+ tried = []
+ for static_dir in STATIC_PATH:
+ static_filename = os.path.join(static_dir, fname)
+ if os.path.exists(static_filename):
+ return static_filename
+ else:
+ tried.append(static_filename)
+ if pkgdir:
+ static_filename = os.path.join(static_dir, pkgdir, fname)
+ if os.path.exists(static_filename):
+ return static_filename
+ else:
+ tried.append(static_filename)
+ raise CoverageException(
+ "Couldn't find static file %r from %r, tried: %r" % (fname, os.getcwd(), tried)
+ )
+
+
+def get_htmlfiles_resource(name):
+ import pkgutil
+ return pkgutil.get_data(__package__, 'htmlfiles/' + name)
+
+
+def read_data(fname):
+ """Return the contents of a data file of ours."""
+ if getattr(sys, 'is_standalone_binary', False):
+ res_buf = get_htmlfiles_resource(fname).decode()
+ if res_buf is not None:
+ return res_buf
+
+ with open(data_filename(fname)) as data_file:
+ return data_file.read()
+
+
+def write_html(fname, html):
+ """Write `html` to `fname`, properly encoded."""
+ html = re.sub(r"(\A\s+)|(\s+$)", "", html, flags=re.MULTILINE) + "\n"
+ with open(fname, "wb") as fout:
+ fout.write(html.encode('ascii', 'xmlcharrefreplace'))
+
+
+class HtmlDataGeneration(object):
+ """Generate structured data to be turned into HTML reports."""
+
+ EMPTY = "(empty)"
+
+ def __init__(self, cov):
+ self.coverage = cov
+ self.config = self.coverage.config
+ data = self.coverage.get_data()
+ self.has_arcs = data.has_arcs()
+ if self.config.show_contexts:
+ if data.measured_contexts() == {""}:
+ self.coverage._warn("No contexts were measured")
+ data.set_query_contexts(self.config.report_contexts)
+
+ def data_for_file(self, fr, analysis):
+ """Produce the data needed for one file's report."""
+ if self.has_arcs:
+ missing_branch_arcs = analysis.missing_branch_arcs()
+ arcs_executed = analysis.arcs_executed()
+
+ if self.config.show_contexts:
+ contexts_by_lineno = analysis.data.contexts_by_lineno(analysis.filename)
+
+ lines = []
+
+ for lineno, tokens in enumerate(fr.source_token_lines(), start=1):
+ # Figure out how to mark this line.
+ category = None
+ short_annotations = []
+ long_annotations = []
+
+ if lineno in analysis.excluded:
+ category = 'exc'
+ elif lineno in analysis.missing:
+ category = 'mis'
+ elif self.has_arcs and lineno in missing_branch_arcs:
+ category = 'par'
+ for b in missing_branch_arcs[lineno]:
+ if b < 0:
+ short_annotations.append("exit")
+ else:
+ short_annotations.append(b)
+ long_annotations.append(fr.missing_arc_description(lineno, b, arcs_executed))
+ elif lineno in analysis.statements:
+ category = 'run'
+
+ contexts = contexts_label = None
+ context_list = None
+ if category and self.config.show_contexts:
+ contexts = sorted(c or self.EMPTY for c in contexts_by_lineno[lineno])
+ if contexts == [self.EMPTY]:
+ contexts_label = self.EMPTY
+ else:
+ contexts_label = "{} ctx".format(len(contexts))
+ context_list = contexts
+
+ lines.append(SimpleNamespace(
+ tokens=tokens,
+ number=lineno,
+ category=category,
+ statement=(lineno in analysis.statements),
+ contexts=contexts,
+ contexts_label=contexts_label,
+ context_list=context_list,
+ short_annotations=short_annotations,
+ long_annotations=long_annotations,
+ ))
+
+ file_data = SimpleNamespace(
+ relative_filename=fr.relative_filename(),
+ nums=analysis.numbers,
+ lines=lines,
+ )
+
+ return file_data
+
+
+class HtmlReporter(object):
+ """HTML reporting."""
+
+ # These files will be copied from the htmlfiles directory to the output
+ # directory.
+ STATIC_FILES = [
+ ("style.css", ""),
+ ("jquery.min.js", "jquery"),
+ ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"),
+ ("jquery.hotkeys.js", "jquery-hotkeys"),
+ ("jquery.isonscreen.js", "jquery-isonscreen"),
+ ("jquery.tablesorter.min.js", "jquery-tablesorter"),
+ ("coverage_html.js", ""),
+ ("keybd_closed.png", ""),
+ ("keybd_open.png", ""),
+ ("favicon_32.png", ""),
+ ]
+
+ def __init__(self, cov):
+ self.coverage = cov
+ self.config = self.coverage.config
+ self.directory = self.config.html_dir
+
+ self.skip_covered = self.config.html_skip_covered
+ if self.skip_covered is None:
+ self.skip_covered = self.config.skip_covered
+ self.skip_empty = self.config.html_skip_empty
+ if self.skip_empty is None:
+ self.skip_empty= self.config.skip_empty
+
+ title = self.config.html_title
+ if env.PY2:
+ title = title.decode("utf8")
+
+ if self.config.extra_css:
+ self.extra_css = os.path.basename(self.config.extra_css)
+ else:
+ self.extra_css = None
+
+ self.data = self.coverage.get_data()
+ self.has_arcs = self.data.has_arcs()
+
+ self.file_summaries = []
+ self.all_files_nums = []
+ self.incr = IncrementalChecker(self.directory)
+ self.datagen = HtmlDataGeneration(self.coverage)
+ self.totals = Numbers()
+
+ self.template_globals = {
+ # Functions available in the templates.
+ 'escape': escape,
+ 'pair': pair,
+ 'len': len,
+
+ # Constants for this report.
+ '__url__': coverage.__url__,
+ '__version__': coverage.__version__,
+ 'title': title,
+ 'time_stamp': format_local_datetime(datetime.datetime.now()),
+ 'extra_css': self.extra_css,
+ 'has_arcs': self.has_arcs,
+ 'show_contexts': self.config.show_contexts,
+
+ # Constants for all reports.
+ # These css classes determine which lines are highlighted by default.
+ 'category': {
+ 'exc': 'exc show_exc',
+ 'mis': 'mis show_mis',
+ 'par': 'par run show_par',
+ 'run': 'run',
+ }
+ }
+ self.pyfile_html_source = read_data("pyfile.html")
+ self.source_tmpl = Templite(self.pyfile_html_source, self.template_globals)
+
+ def report(self, morfs):
+ """Generate an HTML report for `morfs`.
+
+ `morfs` is a list of modules or file names.
+
+ """
+ # Read the status data and check that this run used the same
+ # global data as the last run.
+ self.incr.read()
+ self.incr.check_global_data(self.config, self.pyfile_html_source)
+
+ # Process all the files.
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
+ self.html_file(fr, analysis)
+
+ if not self.all_files_nums:
+ raise CoverageException("No data to report.")
+
+ self.totals = sum(self.all_files_nums)
+
+ # Write the index file.
+ self.index_file()
+
+ self.make_local_static_report_files()
+ return self.totals.n_statements and self.totals.pc_covered
+
+ def make_local_static_report_files(self):
+ """Make local instances of static files for HTML report."""
+ # The files we provide must always be copied.
+ for static, pkgdir in self.STATIC_FILES:
+ if getattr(sys, 'is_standalone_binary', False):
+ data = get_htmlfiles_resource(static)
+ if data is None:
+ raise IOError("No such resource: " + static)
+
+ with open(os.path.join(self.directory, static), "wb") as afile:
+ afile.write(data)
+ else:
+ shutil.copyfile(
+ data_filename(static, pkgdir),
+ os.path.join(self.directory, static)
+ )
+
+ # The user may have extra CSS they want copied.
+ if self.extra_css:
+ shutil.copyfile(
+ self.config.extra_css,
+ os.path.join(self.directory, self.extra_css)
+ )
+
+ def html_file(self, fr, analysis):
+ """Generate an HTML file for one source file."""
+ rootname = flat_rootname(fr.relative_filename())
+ html_filename = rootname + ".html"
+ ensure_dir(self.directory)
+ html_path = os.path.join(self.directory, html_filename)
+
+ # Get the numbers for this file.
+ nums = analysis.numbers
+ self.all_files_nums.append(nums)
+
+ if self.skip_covered:
+ # Don't report on 100% files.
+ no_missing_lines = (nums.n_missing == 0)
+ no_missing_branches = (nums.n_partial_branches == 0)
+ if no_missing_lines and no_missing_branches:
+ # If there's an existing file, remove it.
+ file_be_gone(html_path)
+ return
+
+ if self.skip_empty:
+ # Don't report on empty files.
+ if nums.n_statements == 0:
+ file_be_gone(html_path)
+ return
+
+ # Find out if the file on disk is already correct.
+ if self.incr.can_skip_file(self.data, fr, rootname):
+ self.file_summaries.append(self.incr.index_info(rootname))
+ return
+
+ # Write the HTML page for this file.
+ file_data = self.datagen.data_for_file(fr, analysis)
+ for ldata in file_data.lines:
+ # Build the HTML for the line.
+ html = []
+ for tok_type, tok_text in ldata.tokens:
+ if tok_type == "ws":
+ html.append(escape(tok_text))
+ else:
+ tok_html = escape(tok_text) or '&nbsp;'
+ html.append(
+ u'<span class="{}">{}</span>'.format(tok_type, tok_html)
+ )
+ ldata.html = ''.join(html)
+
+ if ldata.short_annotations:
+ # 202F is NARROW NO-BREAK SPACE.
+ # 219B is RIGHTWARDS ARROW WITH STROKE.
+ ldata.annotate = u",&nbsp;&nbsp; ".join(
+ u"{}&#x202F;&#x219B;&#x202F;{}".format(ldata.number, d)
+ for d in ldata.short_annotations
+ )
+ else:
+ ldata.annotate = None
+
+ if ldata.long_annotations:
+ longs = ldata.long_annotations
+ if len(longs) == 1:
+ ldata.annotate_long = longs[0]
+ else:
+ ldata.annotate_long = u"{:d} missed branches: {}".format(
+ len(longs),
+ u", ".join(
+ u"{:d}) {}".format(num, ann_long)
+ for num, ann_long in enumerate(longs, start=1)
+ ),
+ )
+ else:
+ ldata.annotate_long = None
+
+ css_classes = []
+ if ldata.category:
+ css_classes.append(self.template_globals['category'][ldata.category])
+ ldata.css_class = ' '.join(css_classes) or "pln"
+
+ html = self.source_tmpl.render(file_data.__dict__)
+ write_html(html_path, html)
+
+ # Save this file's information for the index file.
+ index_info = {
+ 'nums': nums,
+ 'html_filename': html_filename,
+ 'relative_filename': fr.relative_filename(),
+ }
+ self.file_summaries.append(index_info)
+ self.incr.set_index_info(rootname, index_info)
+
+ def index_file(self):
+ """Write the index.html file for this report."""
+ index_tmpl = Templite(read_data("index.html"), self.template_globals)
+
+ html = index_tmpl.render({
+ 'files': self.file_summaries,
+ 'totals': self.totals,
+ })
+
+ write_html(os.path.join(self.directory, "index.html"), html)
+
+ # Write the latest hashes for next time.
+ self.incr.write()
+
+
+class IncrementalChecker(object):
+ """Logic and data to support incremental reporting."""
+
+ STATUS_FILE = "status.json"
+ STATUS_FORMAT = 2
+
+ # pylint: disable=wrong-spelling-in-comment,useless-suppression
+ # The data looks like:
+ #
+ # {
+ # "format": 2,
+ # "globals": "540ee119c15d52a68a53fe6f0897346d",
+ # "version": "4.0a1",
+ # "files": {
+ # "cogapp___init__": {
+ # "hash": "e45581a5b48f879f301c0f30bf77a50c",
+ # "index": {
+ # "html_filename": "cogapp___init__.html",
+ # "relative_filename": "cogapp/__init__",
+ # "nums": [ 1, 14, 0, 0, 0, 0, 0 ]
+ # }
+ # },
+ # ...
+ # "cogapp_whiteutils": {
+ # "hash": "8504bb427fc488c4176809ded0277d51",
+ # "index": {
+ # "html_filename": "cogapp_whiteutils.html",
+ # "relative_filename": "cogapp/whiteutils",
+ # "nums": [ 1, 59, 0, 1, 28, 2, 2 ]
+ # }
+ # }
+ # }
+ # }
+
+ def __init__(self, directory):
+ self.directory = directory
+ self.reset()
+
+ def reset(self):
+ """Initialize to empty. Causes all files to be reported."""
+ self.globals = ''
+ self.files = {}
+
+ def read(self):
+ """Read the information we stored last time."""
+ usable = False
+ try:
+ status_file = os.path.join(self.directory, self.STATUS_FILE)
+ with open(status_file) as fstatus:
+ status = json.load(fstatus)
+ except (IOError, ValueError):
+ usable = False
+ else:
+ usable = True
+ if status['format'] != self.STATUS_FORMAT:
+ usable = False
+ elif status['version'] != coverage.__version__:
+ usable = False
+
+ if usable:
+ self.files = {}
+ for filename, fileinfo in iitems(status['files']):
+ fileinfo['index']['nums'] = Numbers(*fileinfo['index']['nums'])
+ self.files[filename] = fileinfo
+ self.globals = status['globals']
+ else:
+ self.reset()
+
+ def write(self):
+ """Write the current status."""
+ status_file = os.path.join(self.directory, self.STATUS_FILE)
+ files = {}
+ for filename, fileinfo in iitems(self.files):
+ fileinfo['index']['nums'] = fileinfo['index']['nums'].init_args()
+ files[filename] = fileinfo
+
+ status = {
+ 'format': self.STATUS_FORMAT,
+ 'version': coverage.__version__,
+ 'globals': self.globals,
+ 'files': files,
+ }
+ with open(status_file, "w") as fout:
+ json.dump(status, fout, separators=(',', ':'))
+
+ def check_global_data(self, *data):
+ """Check the global data that can affect incremental reporting."""
+ m = Hasher()
+ for d in data:
+ m.update(d)
+ these_globals = m.hexdigest()
+ if self.globals != these_globals:
+ self.reset()
+ self.globals = these_globals
+
+ def can_skip_file(self, data, fr, rootname):
+ """Can we skip reporting this file?
+
+ `data` is a CoverageData object, `fr` is a `FileReporter`, and
+ `rootname` is the name being used for the file.
+ """
+ m = Hasher()
+ m.update(fr.source().encode('utf-8'))
+ add_data_to_hash(data, fr.filename, m)
+ this_hash = m.hexdigest()
+
+ that_hash = self.file_hash(rootname)
+
+ if this_hash == that_hash:
+ # Nothing has changed to require the file to be reported again.
+ return True
+ else:
+ self.set_file_hash(rootname, this_hash)
+ return False
+
+ def file_hash(self, fname):
+ """Get the hash of `fname`'s contents."""
+ return self.files.get(fname, {}).get('hash', '')
+
+ def set_file_hash(self, fname, val):
+ """Set the hash of `fname`'s contents."""
+ self.files.setdefault(fname, {})['hash'] = val
+
+ def index_info(self, fname):
+ """Get the information for index.html for `fname`."""
+ return self.files.get(fname, {}).get('index', {})
+
+ def set_index_info(self, fname, info):
+ """Set the information for index.html for `fname`."""
+ self.files.setdefault(fname, {})['index'] = info
+
+
+# Helpers for templates and generating HTML
+
+def escape(t):
+ """HTML-escape the text in `t`.
+
+ This is only suitable for HTML text, not attributes.
+
+ """
+ # Convert HTML special chars into HTML entities.
+ return t.replace("&", "&amp;").replace("<", "&lt;")
+
+
+def pair(ratio):
+ """Format a pair of numbers so JavaScript can read them in an attribute."""
+ return "%s %s" % ratio
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/coverage_html.js b/contrib/python/coverage/py3/coverage/htmlfiles/coverage_html.js
new file mode 100644
index 0000000000..27b49b36f9
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/coverage_html.js
@@ -0,0 +1,616 @@
+// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+// For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+// Coverage.py HTML report browser code.
+/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
+/*global coverage: true, document, window, $ */
+
+coverage = {};
+
+// Find all the elements with shortkey_* class, and use them to assign a shortcut key.
+coverage.assign_shortkeys = function () {
+ $("*[class*='shortkey_']").each(function (i, e) {
+ $.each($(e).attr("class").split(" "), function (i, c) {
+ if (/^shortkey_/.test(c)) {
+ $(document).bind('keydown', c.substr(9), function () {
+ $(e).click();
+ });
+ }
+ });
+ });
+};
+
+// Create the events for the help panel.
+coverage.wire_up_help_panel = function () {
+ $("#keyboard_icon").click(function () {
+ // Show the help panel, and position it so the keyboard icon in the
+ // panel is in the same place as the keyboard icon in the header.
+ $(".help_panel").show();
+ var koff = $("#keyboard_icon").offset();
+ var poff = $("#panel_icon").position();
+ $(".help_panel").offset({
+ top: koff.top-poff.top,
+ left: koff.left-poff.left
+ });
+ });
+ $("#panel_icon").click(function () {
+ $(".help_panel").hide();
+ });
+};
+
+// Create the events for the filter box.
+coverage.wire_up_filter = function () {
+ // Cache elements.
+ var table = $("table.index");
+ var table_rows = table.find("tbody tr");
+ var table_row_names = table_rows.find("td.name a");
+ var no_rows = $("#no_rows");
+
+ // Create a duplicate table footer that we can modify with dynamic summed values.
+ var table_footer = $("table.index tfoot tr");
+ var table_dynamic_footer = table_footer.clone();
+ table_dynamic_footer.attr('class', 'total_dynamic hidden');
+ table_footer.after(table_dynamic_footer);
+
+ // Observe filter keyevents.
+ $("#filter").on("keyup change", $.debounce(150, function (event) {
+ var filter_value = $(this).val();
+
+ if (filter_value === "") {
+ // Filter box is empty, remove all filtering.
+ table_rows.removeClass("hidden");
+
+ // Show standard footer, hide dynamic footer.
+ table_footer.removeClass("hidden");
+ table_dynamic_footer.addClass("hidden");
+
+ // Hide placeholder, show table.
+ if (no_rows.length > 0) {
+ no_rows.hide();
+ }
+ table.show();
+
+ }
+ else {
+ // Filter table items by value.
+ var hidden = 0;
+ var shown = 0;
+
+ // Hide / show elements.
+ $.each(table_row_names, function () {
+ var element = $(this).parents("tr");
+
+ if ($(this).text().indexOf(filter_value) === -1) {
+ // hide
+ element.addClass("hidden");
+ hidden++;
+ }
+ else {
+ // show
+ element.removeClass("hidden");
+ shown++;
+ }
+ });
+
+ // Show placeholder if no rows will be displayed.
+ if (no_rows.length > 0) {
+ if (shown === 0) {
+ // Show placeholder, hide table.
+ no_rows.show();
+ table.hide();
+ }
+ else {
+ // Hide placeholder, show table.
+ no_rows.hide();
+ table.show();
+ }
+ }
+
+ // Manage dynamic header:
+ if (hidden > 0) {
+ // Calculate new dynamic sum values based on visible rows.
+ for (var column = 2; column < 20; column++) {
+ // Calculate summed value.
+ var cells = table_rows.find('td:nth-child(' + column + ')');
+ if (!cells.length) {
+ // No more columns...!
+ break;
+ }
+
+ var sum = 0, numer = 0, denom = 0;
+ $.each(cells.filter(':visible'), function () {
+ var ratio = $(this).data("ratio");
+ if (ratio) {
+ var splitted = ratio.split(" ");
+ numer += parseInt(splitted[0], 10);
+ denom += parseInt(splitted[1], 10);
+ }
+ else {
+ sum += parseInt(this.innerHTML, 10);
+ }
+ });
+
+ // Get footer cell element.
+ var footer_cell = table_dynamic_footer.find('td:nth-child(' + column + ')');
+
+ // Set value into dynamic footer cell element.
+ if (cells[0].innerHTML.indexOf('%') > -1) {
+ // Percentage columns use the numerator and denominator,
+ // and adapt to the number of decimal places.
+ var match = /\.([0-9]+)/.exec(cells[0].innerHTML);
+ var places = 0;
+ if (match) {
+ places = match[1].length;
+ }
+ var pct = numer * 100 / denom;
+ footer_cell.text(pct.toFixed(places) + '%');
+ }
+ else {
+ footer_cell.text(sum);
+ }
+ }
+
+ // Hide standard footer, show dynamic footer.
+ table_footer.addClass("hidden");
+ table_dynamic_footer.removeClass("hidden");
+ }
+ else {
+ // Show standard footer, hide dynamic footer.
+ table_footer.removeClass("hidden");
+ table_dynamic_footer.addClass("hidden");
+ }
+ }
+ }));
+
+ // Trigger change event on setup, to force filter on page refresh
+ // (filter value may still be present).
+ $("#filter").trigger("change");
+};
+
+// Loaded on index.html
+coverage.index_ready = function ($) {
+ // Look for a localStorage item containing previous sort settings:
+ var sort_list = [];
+ var storage_name = "COVERAGE_INDEX_SORT";
+ var stored_list = undefined;
+ try {
+ stored_list = localStorage.getItem(storage_name);
+ } catch(err) {}
+
+ if (stored_list) {
+ sort_list = JSON.parse('[[' + stored_list + ']]');
+ }
+
+ // Create a new widget which exists only to save and restore
+ // the sort order:
+ $.tablesorter.addWidget({
+ id: "persistentSort",
+
+ // Format is called by the widget before displaying:
+ format: function (table) {
+ if (table.config.sortList.length === 0 && sort_list.length > 0) {
+ // This table hasn't been sorted before - we'll use
+ // our stored settings:
+ $(table).trigger('sorton', [sort_list]);
+ }
+ else {
+ // This is not the first load - something has
+ // already defined sorting so we'll just update
+ // our stored value to match:
+ sort_list = table.config.sortList;
+ }
+ }
+ });
+
+ // Configure our tablesorter to handle the variable number of
+ // columns produced depending on report options:
+ var headers = [];
+ var col_count = $("table.index > thead > tr > th").length;
+
+ headers[0] = { sorter: 'text' };
+ for (i = 1; i < col_count-1; i++) {
+ headers[i] = { sorter: 'digit' };
+ }
+ headers[col_count-1] = { sorter: 'percent' };
+
+ // Enable the table sorter:
+ $("table.index").tablesorter({
+ widgets: ['persistentSort'],
+ headers: headers
+ });
+
+ coverage.assign_shortkeys();
+ coverage.wire_up_help_panel();
+ coverage.wire_up_filter();
+
+ // Watch for page unload events so we can save the final sort settings:
+ $(window).on("unload", function () {
+ try {
+ localStorage.setItem(storage_name, sort_list.toString())
+ } catch(err) {}
+ });
+};
+
+// -- pyfile stuff --
+
+coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS";
+
+coverage.pyfile_ready = function ($) {
+ // If we're directed to a particular line number, highlight the line.
+ var frag = location.hash;
+ if (frag.length > 2 && frag[1] === 't') {
+ $(frag).addClass('highlight');
+ coverage.set_sel(parseInt(frag.substr(2), 10));
+ }
+ else {
+ coverage.set_sel(0);
+ }
+
+ $(document)
+ .bind('keydown', 'j', coverage.to_next_chunk_nicely)
+ .bind('keydown', 'k', coverage.to_prev_chunk_nicely)
+ .bind('keydown', '0', coverage.to_top)
+ .bind('keydown', '1', coverage.to_first_chunk)
+ ;
+
+ $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");});
+ $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");});
+ $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");});
+ $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");});
+
+ coverage.filters = undefined;
+ try {
+ coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE);
+ } catch(err) {}
+
+ if (coverage.filters) {
+ coverage.filters = JSON.parse(coverage.filters);
+ }
+ else {
+ coverage.filters = {run: false, exc: true, mis: true, par: true};
+ }
+
+ for (cls in coverage.filters) {
+ coverage.set_line_visibilty(cls, coverage.filters[cls]);
+ }
+
+ coverage.assign_shortkeys();
+ coverage.wire_up_help_panel();
+
+ coverage.init_scroll_markers();
+
+ // Rebuild scroll markers when the window height changes.
+ $(window).resize(coverage.build_scroll_markers);
+};
+
+coverage.toggle_lines = function (btn, cls) {
+ var onoff = !$(btn).hasClass("show_" + cls);
+ coverage.set_line_visibilty(cls, onoff);
+ coverage.build_scroll_markers();
+ coverage.filters[cls] = onoff;
+ try {
+ localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters));
+ } catch(err) {}
+};
+
+coverage.set_line_visibilty = function (cls, onoff) {
+ var show = "show_" + cls;
+ var btn = $(".button_toggle_" + cls);
+ if (onoff) {
+ $("#source ." + cls).addClass(show);
+ btn.addClass(show);
+ }
+ else {
+ $("#source ." + cls).removeClass(show);
+ btn.removeClass(show);
+ }
+};
+
+// Return the nth line div.
+coverage.line_elt = function (n) {
+ return $("#t" + n);
+};
+
+// Return the nth line number div.
+coverage.num_elt = function (n) {
+ return $("#n" + n);
+};
+
+// Set the selection. b and e are line numbers.
+coverage.set_sel = function (b, e) {
+ // The first line selected.
+ coverage.sel_begin = b;
+ // The next line not selected.
+ coverage.sel_end = (e === undefined) ? b+1 : e;
+};
+
+coverage.to_top = function () {
+ coverage.set_sel(0, 1);
+ coverage.scroll_window(0);
+};
+
+coverage.to_first_chunk = function () {
+ coverage.set_sel(0, 1);
+ coverage.to_next_chunk();
+};
+
+// Return a string indicating what kind of chunk this line belongs to,
+// or null if not a chunk.
+coverage.chunk_indicator = function (line_elt) {
+ var klass = line_elt.attr('class');
+ if (klass) {
+ var m = klass.match(/\bshow_\w+\b/);
+ if (m) {
+ return m[0];
+ }
+ }
+ return null;
+};
+
+coverage.to_next_chunk = function () {
+ var c = coverage;
+
+ // Find the start of the next colored chunk.
+ var probe = c.sel_end;
+ var chunk_indicator, probe_line;
+ while (true) {
+ probe_line = c.line_elt(probe);
+ if (probe_line.length === 0) {
+ return;
+ }
+ chunk_indicator = c.chunk_indicator(probe_line);
+ if (chunk_indicator) {
+ break;
+ }
+ probe++;
+ }
+
+ // There's a next chunk, `probe` points to it.
+ var begin = probe;
+
+ // Find the end of this chunk.
+ var next_indicator = chunk_indicator;
+ while (next_indicator === chunk_indicator) {
+ probe++;
+ probe_line = c.line_elt(probe);
+ next_indicator = c.chunk_indicator(probe_line);
+ }
+ c.set_sel(begin, probe);
+ c.show_selection();
+};
+
+coverage.to_prev_chunk = function () {
+ var c = coverage;
+
+ // Find the end of the prev colored chunk.
+ var probe = c.sel_begin-1;
+ var probe_line = c.line_elt(probe);
+ if (probe_line.length === 0) {
+ return;
+ }
+ var chunk_indicator = c.chunk_indicator(probe_line);
+ while (probe > 0 && !chunk_indicator) {
+ probe--;
+ probe_line = c.line_elt(probe);
+ if (probe_line.length === 0) {
+ return;
+ }
+ chunk_indicator = c.chunk_indicator(probe_line);
+ }
+
+ // There's a prev chunk, `probe` points to its last line.
+ var end = probe+1;
+
+ // Find the beginning of this chunk.
+ var prev_indicator = chunk_indicator;
+ while (prev_indicator === chunk_indicator) {
+ probe--;
+ probe_line = c.line_elt(probe);
+ prev_indicator = c.chunk_indicator(probe_line);
+ }
+ c.set_sel(probe+1, end);
+ c.show_selection();
+};
+
+// Return the line number of the line nearest pixel position pos
+coverage.line_at_pos = function (pos) {
+ var l1 = coverage.line_elt(1),
+ l2 = coverage.line_elt(2),
+ result;
+ if (l1.length && l2.length) {
+ var l1_top = l1.offset().top,
+ line_height = l2.offset().top - l1_top,
+ nlines = (pos - l1_top) / line_height;
+ if (nlines < 1) {
+ result = 1;
+ }
+ else {
+ result = Math.ceil(nlines);
+ }
+ }
+ else {
+ result = 1;
+ }
+ return result;
+};
+
+// Returns 0, 1, or 2: how many of the two ends of the selection are on
+// the screen right now?
+coverage.selection_ends_on_screen = function () {
+ if (coverage.sel_begin === 0) {
+ return 0;
+ }
+
+ var top = coverage.line_elt(coverage.sel_begin);
+ var next = coverage.line_elt(coverage.sel_end-1);
+
+ return (
+ (top.isOnScreen() ? 1 : 0) +
+ (next.isOnScreen() ? 1 : 0)
+ );
+};
+
+coverage.to_next_chunk_nicely = function () {
+ coverage.finish_scrolling();
+ if (coverage.selection_ends_on_screen() === 0) {
+ // The selection is entirely off the screen: select the top line on
+ // the screen.
+ var win = $(window);
+ coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop()));
+ }
+ coverage.to_next_chunk();
+};
+
+coverage.to_prev_chunk_nicely = function () {
+ coverage.finish_scrolling();
+ if (coverage.selection_ends_on_screen() === 0) {
+ var win = $(window);
+ coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height()));
+ }
+ coverage.to_prev_chunk();
+};
+
+// Select line number lineno, or if it is in a colored chunk, select the
+// entire chunk
+coverage.select_line_or_chunk = function (lineno) {
+ var c = coverage;
+ var probe_line = c.line_elt(lineno);
+ if (probe_line.length === 0) {
+ return;
+ }
+ var the_indicator = c.chunk_indicator(probe_line);
+ if (the_indicator) {
+ // The line is in a highlighted chunk.
+ // Search backward for the first line.
+ var probe = lineno;
+ var indicator = the_indicator;
+ while (probe > 0 && indicator === the_indicator) {
+ probe--;
+ probe_line = c.line_elt(probe);
+ if (probe_line.length === 0) {
+ break;
+ }
+ indicator = c.chunk_indicator(probe_line);
+ }
+ var begin = probe + 1;
+
+ // Search forward for the last line.
+ probe = lineno;
+ indicator = the_indicator;
+ while (indicator === the_indicator) {
+ probe++;
+ probe_line = c.line_elt(probe);
+ indicator = c.chunk_indicator(probe_line);
+ }
+
+ coverage.set_sel(begin, probe);
+ }
+ else {
+ coverage.set_sel(lineno);
+ }
+};
+
+coverage.show_selection = function () {
+ var c = coverage;
+
+ // Highlight the lines in the chunk
+ $(".linenos .highlight").removeClass("highlight");
+ for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) {
+ c.num_elt(probe).addClass("highlight");
+ }
+
+ c.scroll_to_selection();
+};
+
+coverage.scroll_to_selection = function () {
+ // Scroll the page if the chunk isn't fully visible.
+ if (coverage.selection_ends_on_screen() < 2) {
+ // Need to move the page. The html,body trick makes it scroll in all
+ // browsers, got it from http://stackoverflow.com/questions/3042651
+ var top = coverage.line_elt(coverage.sel_begin);
+ var top_pos = parseInt(top.offset().top, 10);
+ coverage.scroll_window(top_pos - 30);
+ }
+};
+
+coverage.scroll_window = function (to_pos) {
+ $("html,body").animate({scrollTop: to_pos}, 200);
+};
+
+coverage.finish_scrolling = function () {
+ $("html,body").stop(true, true);
+};
+
+coverage.init_scroll_markers = function () {
+ var c = coverage;
+ // Init some variables
+ c.lines_len = $('#source p').length;
+ c.body_h = $('body').height();
+ c.header_h = $('div#header').height();
+
+ // Build html
+ c.build_scroll_markers();
+};
+
+coverage.build_scroll_markers = function () {
+ var c = coverage,
+ min_line_height = 3,
+ max_line_height = 10,
+ visible_window_h = $(window).height();
+
+ c.lines_to_mark = $('#source').find('p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par');
+ $('#scroll_marker').remove();
+ // Don't build markers if the window has no scroll bar.
+ if (c.body_h <= visible_window_h) {
+ return;
+ }
+
+ $("body").append("<div id='scroll_marker'>&nbsp;</div>");
+ var scroll_marker = $('#scroll_marker'),
+ marker_scale = scroll_marker.height() / c.body_h,
+ line_height = scroll_marker.height() / c.lines_len;
+
+ // Line height must be between the extremes.
+ if (line_height > min_line_height) {
+ if (line_height > max_line_height) {
+ line_height = max_line_height;
+ }
+ }
+ else {
+ line_height = min_line_height;
+ }
+
+ var previous_line = -99,
+ last_mark,
+ last_top,
+ offsets = {};
+
+ // Calculate line offsets outside loop to prevent relayouts
+ c.lines_to_mark.each(function() {
+ offsets[this.id] = $(this).offset().top;
+ });
+ c.lines_to_mark.each(function () {
+ var id_name = $(this).attr('id'),
+ line_top = Math.round(offsets[id_name] * marker_scale),
+ line_number = parseInt(id_name.substring(1, id_name.length));
+
+ if (line_number === previous_line + 1) {
+ // If this solid missed block just make previous mark higher.
+ last_mark.css({
+ 'height': line_top + line_height - last_top
+ });
+ }
+ else {
+ // Add colored line in scroll_marker block.
+ scroll_marker.append('<div id="m' + line_number + '" class="marker"></div>');
+ last_mark = $('#m' + line_number);
+ last_mark.css({
+ 'height': line_height,
+ 'top': line_top
+ });
+ last_top = line_top;
+ }
+
+ previous_line = line_number;
+ });
+};
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/favicon_32.png b/contrib/python/coverage/py3/coverage/htmlfiles/favicon_32.png
new file mode 100644
index 0000000000..8649f0475d
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/favicon_32.png
Binary files differ
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/index.html b/contrib/python/coverage/py3/coverage/htmlfiles/index.html
new file mode 100644
index 0000000000..983db06125
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/index.html
@@ -0,0 +1,119 @@
+{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
+{# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt #}
+
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>{{ title|escape }}</title>
+ <link rel="icon" sizes="32x32" href="favicon_32.png">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ {% if extra_css %}
+ <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
+ {% endif %}
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.ba-throttle-debounce.min.js"></script>
+ <script type="text/javascript" src="jquery.tablesorter.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
+ jQuery(document).ready(coverage.index_ready);
+ </script>
+</head>
+<body class="indexfile">
+
+<div id="header">
+ <div class="content">
+ <h1>{{ title|escape }}:
+ <span class="pc_cov">{{totals.pc_covered_str}}%</span>
+ </h1>
+
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
+
+ <form id="filter_container">
+ <input id="filter" type="text" value="" placeholder="filter..." />
+ </form>
+ </div>
+</div>
+
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
+ <div>
+ <p class="keyhelp">
+ <span class="key">n</span>
+ <span class="key">s</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ {% if has_arcs %}
+ <span class="key">b</span>
+ <span class="key">p</span>
+ {% endif %}
+ <span class="key">c</span> &nbsp; change column sorting
+ </p>
+ </div>
+</div>
+
+<div id="index">
+ <table class="index">
+ <thead>
+ {# The title="" attr doesn"t work in Safari. #}
+ <tr class="tablehead" title="Click to sort">
+ <th class="name left headerSortDown shortkey_n">Module</th>
+ <th class="shortkey_s">statements</th>
+ <th class="shortkey_m">missing</th>
+ <th class="shortkey_x">excluded</th>
+ {% if has_arcs %}
+ <th class="shortkey_b">branches</th>
+ <th class="shortkey_p">partial</th>
+ {% endif %}
+ <th class="right shortkey_c">coverage</th>
+ </tr>
+ </thead>
+ {# HTML syntax requires thead, tfoot, tbody #}
+ <tfoot>
+ <tr class="total">
+ <td class="name left">Total</td>
+ <td>{{totals.n_statements}}</td>
+ <td>{{totals.n_missing}}</td>
+ <td>{{totals.n_excluded}}</td>
+ {% if has_arcs %}
+ <td>{{totals.n_branches}}</td>
+ <td>{{totals.n_partial_branches}}</td>
+ {% endif %}
+ <td class="right" data-ratio="{{totals.ratio_covered|pair}}">{{totals.pc_covered_str}}%</td>
+ </tr>
+ </tfoot>
+ <tbody>
+ {% for file in files %}
+ <tr class="file">
+ <td class="name left"><a href="{{file.html_filename}}">{{file.relative_filename}}</a></td>
+ <td>{{file.nums.n_statements}}</td>
+ <td>{{file.nums.n_missing}}</td>
+ <td>{{file.nums.n_excluded}}</td>
+ {% if has_arcs %}
+ <td>{{file.nums.n_branches}}</td>
+ <td>{{file.nums.n_partial_branches}}</td>
+ {% endif %}
+ <td class="right" data-ratio="{{file.nums.ratio_covered|pair}}">{{file.nums.pc_covered_str}}%</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+
+ <p id="no_rows">
+ No items found using the specified filter.
+ </p>
+</div>
+
+<div id="footer">
+ <div class="content">
+ <p>
+ <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
+ created at {{ time_stamp }}
+ </p>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
new file mode 100644
index 0000000000..648fe5d3c2
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
@@ -0,0 +1,9 @@
+/*
+ * jQuery throttle / debounce - v1.1 - 3/7/2010
+ * http://benalman.com/projects/jquery-throttle-debounce-plugin/
+ *
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.hotkeys.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.hotkeys.js
new file mode 100644
index 0000000000..09b21e03c7
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.hotkeys.js
@@ -0,0 +1,99 @@
+/*
+ * jQuery Hotkeys Plugin
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
+ * Based upon the plugin by Tzury Bar Yochay:
+ * http://github.com/tzuryby/hotkeys
+ *
+ * Original idea by:
+ * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
+*/
+
+(function(jQuery){
+
+ jQuery.hotkeys = {
+ version: "0.8",
+
+ specialKeys: {
+ 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
+ },
+
+ shiftNums: {
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
+ ".": ">", "/": "?", "\\": "|"
+ }
+ };
+
+ function keyHandler( handleObj ) {
+ // Only care when a possible input has been specified
+ if ( typeof handleObj.data !== "string" ) {
+ return;
+ }
+
+ var origHandler = handleObj.handler,
+ keys = handleObj.data.toLowerCase().split(" ");
+
+ handleObj.handler = function( event ) {
+ // Don't fire in text-accepting inputs that we didn't directly bind to
+ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
+ event.target.type === "text") ) {
+ return;
+ }
+
+ // Keypress represents characters, not special keys
+ var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
+ character = String.fromCharCode( event.which ).toLowerCase(),
+ key, modif = "", possible = {};
+
+ // check combinations (alt|ctrl|shift+anything)
+ if ( event.altKey && special !== "alt" ) {
+ modif += "alt+";
+ }
+
+ if ( event.ctrlKey && special !== "ctrl" ) {
+ modif += "ctrl+";
+ }
+
+ // TODO: Need to make sure this works consistently across platforms
+ if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
+ modif += "meta+";
+ }
+
+ if ( event.shiftKey && special !== "shift" ) {
+ modif += "shift+";
+ }
+
+ if ( special ) {
+ possible[ modif + special ] = true;
+
+ } else {
+ possible[ modif + character ] = true;
+ possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
+
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
+ if ( modif === "shift+" ) {
+ possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
+ }
+ }
+
+ for ( var i = 0, l = keys.length; i < l; i++ ) {
+ if ( possible[ keys[i] ] ) {
+ return origHandler.apply( this, arguments );
+ }
+ }
+ };
+ }
+
+ jQuery.each([ "keydown", "keyup", "keypress" ], function() {
+ jQuery.event.special[ this ] = { add: keyHandler };
+ });
+
+})( jQuery );
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.isonscreen.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.isonscreen.js
new file mode 100644
index 0000000000..0182ebd213
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.isonscreen.js
@@ -0,0 +1,53 @@
+/* Copyright (c) 2010
+ * @author Laurence Wheway
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
+ *
+ * @version 1.2.0
+ */
+(function($) {
+ jQuery.extend({
+ isOnScreen: function(box, container) {
+ //ensure numbers come in as intgers (not strings) and remove 'px' is it's there
+ for(var i in box){box[i] = parseFloat(box[i])};
+ for(var i in container){container[i] = parseFloat(container[i])};
+
+ if(!container){
+ container = {
+ left: $(window).scrollLeft(),
+ top: $(window).scrollTop(),
+ width: $(window).width(),
+ height: $(window).height()
+ }
+ }
+
+ if( box.left+box.width-container.left > 0 &&
+ box.left < container.width+container.left &&
+ box.top+box.height-container.top > 0 &&
+ box.top < container.height+container.top
+ ) return true;
+ return false;
+ }
+ })
+
+
+ jQuery.fn.isOnScreen = function (container) {
+ for(var i in container){container[i] = parseFloat(container[i])};
+
+ if(!container){
+ container = {
+ left: $(window).scrollLeft(),
+ top: $(window).scrollTop(),
+ width: $(window).width(),
+ height: $(window).height()
+ }
+ }
+
+ if( $(this).offset().left+$(this).width()-container.left > 0 &&
+ $(this).offset().left < container.width+container.left &&
+ $(this).offset().top+$(this).height()-container.top > 0 &&
+ $(this).offset().top < container.height+container.top
+ ) return true;
+ return false;
+ }
+})(jQuery);
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.min.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.min.js
new file mode 100644
index 0000000000..d1608e37ff
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
+if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
+},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m}); \ No newline at end of file
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/jquery.tablesorter.min.js b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.tablesorter.min.js
new file mode 100644
index 0000000000..64c7007129
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/jquery.tablesorter.min.js
@@ -0,0 +1,2 @@
+
+(function($){$.extend({tablesorter:new function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'.',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}var rows=table.tBodies[0].rows;if(table.tBodies[0].rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,cells[i]);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,node){var l=parsers.length;for(var i=1;i<l;i++){if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)){return parsers[i];}}return parsers[0];}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=table.tBodies[0].rows[i],cols=[];cache.row.push($(c));for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j]));}cols.push(i);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){if(!node)return"";var t="";if(config.textExtraction=="simple"){if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){t=node.childNodes[0].innerHTML;}else{t=node.innerHTML;}}else{if(typeof(config.textExtraction)=="function"){t=config.textExtraction(node);}else{t=$(node).text();}}return t;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){rows.push(r[n[i][checkCell]]);if(!table.config.appender){var o=r[n[i][checkCell]];var l=o.length;for(var j=0;j<l;j++){tableBody[0].appendChild(o[j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false,tableHeadersRows=[];for(var i=0;i<table.tHead.rows.length;i++){tableHeadersRows[i]=0;};$tableHeaders=$("thead th",table);$tableHeaders.each(function(index){this.count=0;this.column=index;this.order=formatSortingOrder(table.config.sortInitialOrder);if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(!this.sortDisabled){$(this).addClass(table.config.cssHeader);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){i=(v.toLowerCase()=="desc")?1:0;}else{i=(v==(0||1))?v:0;}return i;}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(getCachedSortType(table.config.parsers,c)=="text")?((order==0)?"sortText":"sortTextDesc"):((order==0)?"sortNumeric":"sortNumericDesc");var e="e"+i;dynamicExp+="var "+e+" = "+s+"(a["+c+"],b["+c+"]); ";dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function sortText(a,b){return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){var DECIMAL='\\'+config.decimal;var exp='/(^[+]?0('+DECIMAL+'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)'+DECIMAL+'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*'+DECIMAL+'0+$)/';return RegExp(exp).test($.trim(s));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}$("tr:visible",table.tBodies[0]).filter(':even').removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0]).end().filter(':odd').removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]);if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery); \ No newline at end of file
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/keybd_closed.png b/contrib/python/coverage/py3/coverage/htmlfiles/keybd_closed.png
new file mode 100644
index 0000000000..db114023f0
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/keybd_closed.png
Binary files differ
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/keybd_open.png b/contrib/python/coverage/py3/coverage/htmlfiles/keybd_open.png
new file mode 100644
index 0000000000..db114023f0
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/keybd_open.png
Binary files differ
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/pyfile.html b/contrib/python/coverage/py3/coverage/htmlfiles/pyfile.html
new file mode 100644
index 0000000000..e15be066fb
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/pyfile.html
@@ -0,0 +1,113 @@
+{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
+{# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt #}
+
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ {# IE8 rounds line-height incorrectly, and adding this emulateIE7 line makes it right! #}
+ {# http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/7684445e-f080-4d8f-8529-132763348e21 #}
+ <meta http-equiv="X-UA-Compatible" content="IE=emulateIE7" />
+ <title>Coverage for {{relative_filename|escape}}: {{nums.pc_covered_str}}%</title>
+ <link rel="icon" sizes="32x32" href="favicon_32.png">
+ <link rel="stylesheet" href="style.css" type="text/css">
+ {% if extra_css %}
+ <link rel="stylesheet" href="{{ extra_css }}" type="text/css">
+ {% endif %}
+ <script type="text/javascript" src="jquery.min.js"></script>
+ <script type="text/javascript" src="jquery.hotkeys.js"></script>
+ <script type="text/javascript" src="jquery.isonscreen.js"></script>
+ <script type="text/javascript" src="coverage_html.js"></script>
+ <script type="text/javascript">
+ jQuery(document).ready(coverage.pyfile_ready);
+ </script>
+</head>
+<body class="pyfile">
+
+<div id="header">
+ <div class="content">
+ <h1>Coverage for <b>{{relative_filename|escape}}</b> :
+ <span class="pc_cov">{{nums.pc_covered_str}}%</span>
+ </h1>
+
+ <img id="keyboard_icon" src="keybd_closed.png" alt="Show keyboard shortcuts" />
+
+ <h2 class="stats">
+ {{nums.n_statements}} statements &nbsp;
+ <button type="button" class="{{category.run}} shortkey_r button_toggle_run" title="Toggle lines run">{{nums.n_executed}} run</button>
+ <button type="button" class="{{category.mis}} shortkey_m button_toggle_mis" title="Toggle lines missing">{{nums.n_missing}} missing</button>
+ <button type="button" class="{{category.exc}} shortkey_x button_toggle_exc" title="Toggle lines excluded">{{nums.n_excluded}} excluded</button>
+
+ {% if has_arcs %}
+ <button type="button" class="{{category.par}} shortkey_p button_toggle_par" title="Toggle lines partially run">{{nums.n_partial_branches}} partial</button>
+ {% endif %}
+ </h2>
+ </div>
+</div>
+
+<div class="help_panel">
+ <img id="panel_icon" src="keybd_open.png" alt="Hide keyboard shortcuts" />
+ <p class="legend">Hot-keys on this page</p>
+ <div>
+ <p class="keyhelp">
+ <span class="key">r</span>
+ <span class="key">m</span>
+ <span class="key">x</span>
+ <span class="key">p</span> &nbsp; toggle line displays
+ </p>
+ <p class="keyhelp">
+ <span class="key">j</span>
+ <span class="key">k</span> &nbsp; next/prev highlighted chunk
+ </p>
+ <p class="keyhelp">
+ <span class="key">0</span> &nbsp; (zero) top of page
+ </p>
+ <p class="keyhelp">
+ <span class="key">1</span> &nbsp; (one) first highlighted chunk
+ </p>
+ </div>
+</div>
+
+<div id="source">
+ {% for line in lines -%}
+ {% joined %}
+ <p id="t{{line.number}}" class="{{line.css_class}}">
+ <span class="n"><a href="#t{{line.number}}">{{line.number}}</a></span>
+ <span class="t">{{line.html}}&nbsp;</span>
+ {% if line.context_list %}
+ <input type="checkbox" id="ctxs{{line.number}}" />
+ {% endif %}
+ {# Things that should float right in the line. #}
+ <span class="r">
+ {% if line.annotate %}
+ <span class="annotate short">{{line.annotate}}</span>
+ <span class="annotate long">{{line.annotate_long}}</span>
+ {% endif %}
+ {% if line.contexts %}
+ <label for="ctxs{{line.number}}" class="ctx">{{ line.contexts_label }}</label>
+ {% endif %}
+ </span>
+ {# Things that should appear below the line. #}
+ {% if line.context_list %}
+ <span class="ctxs">
+ {% for context in line.context_list %}
+ <span>{{context}}</span>
+ {% endfor %}
+ </span>
+ {% endif %}
+ </p>
+ {% endjoined %}
+ {% endfor %}
+</div>
+
+<div id="footer">
+ <div class="content">
+ <p>
+ <a class="nav" href="index.html">&#xab; index</a> &nbsp; &nbsp; <a class="nav" href="{{__url__}}">coverage.py v{{__version__}}</a>,
+ created at {{ time_stamp }}
+ </p>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/style.css b/contrib/python/coverage/py3/coverage/htmlfiles/style.css
new file mode 100644
index 0000000000..36ee2a6e65
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/style.css
@@ -0,0 +1,291 @@
+@charset "UTF-8";
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+/* Don't edit this .css file. Edit the .scss file instead! */
+html, body, h1, h2, h3, p, table, td, th { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; }
+
+body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; font-size: 1em; background: #fff; color: #000; }
+
+@media (prefers-color-scheme: dark) { body { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { body { color: #eee; } }
+
+html > body { font-size: 16px; }
+
+a:active, a:focus { outline: 2px dashed #007acc; }
+
+p { font-size: .875em; line-height: 1.4em; }
+
+table { border-collapse: collapse; }
+
+td { vertical-align: top; }
+
+table tr.hidden { display: none !important; }
+
+p#no_rows { display: none; font-size: 1.2em; }
+
+a.nav { text-decoration: none; color: inherit; }
+
+a.nav:hover { text-decoration: underline; color: inherit; }
+
+#header { background: #f8f8f8; width: 100%; border-bottom: 1px solid #eee; }
+
+@media (prefers-color-scheme: dark) { #header { background: black; } }
+
+@media (prefers-color-scheme: dark) { #header { border-color: #333; } }
+
+.indexfile #footer { margin: 1rem 3.5rem; }
+
+.pyfile #footer { margin: 1rem 1rem; }
+
+#footer .content { padding: 0; color: #666; font-style: italic; }
+
+@media (prefers-color-scheme: dark) { #footer .content { color: #aaa; } }
+
+#index { margin: 1rem 0 0 3.5rem; }
+
+#header .content { padding: 1rem 3.5rem; }
+
+h1 { font-size: 1.25em; display: inline-block; }
+
+#filter_container { float: right; margin: 0 2em 0 0; }
+
+#filter_container input { width: 10em; padding: 0.2em 0.5em; border: 2px solid #ccc; background: #fff; color: #000; }
+
+@media (prefers-color-scheme: dark) { #filter_container input { border-color: #444; } }
+
+@media (prefers-color-scheme: dark) { #filter_container input { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { #filter_container input { color: #eee; } }
+
+#filter_container input:focus { border-color: #007acc; }
+
+h2.stats { margin-top: .5em; font-size: 1em; }
+
+.stats button { font-family: inherit; font-size: inherit; border: 1px solid; border-radius: .2em; color: inherit; padding: .1em .5em; margin: 1px calc(.1em + 1px); cursor: pointer; border-color: #ccc; }
+
+@media (prefers-color-scheme: dark) { .stats button { border-color: #444; } }
+
+.stats button:active, .stats button:focus { outline: 2px dashed #007acc; }
+
+.stats button:active, .stats button:focus { outline: 2px dashed #007acc; }
+
+.stats button.run { background: #eeffee; }
+
+@media (prefers-color-scheme: dark) { .stats button.run { background: #373d29; } }
+
+.stats button.run.show_run { background: #dfd; border: 2px solid #00dd00; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { .stats button.run.show_run { background: #373d29; } }
+
+.stats button.mis { background: #ffeeee; }
+
+@media (prefers-color-scheme: dark) { .stats button.mis { background: #4b1818; } }
+
+.stats button.mis.show_mis { background: #fdd; border: 2px solid #ff0000; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { .stats button.mis.show_mis { background: #4b1818; } }
+
+.stats button.exc { background: #f7f7f7; }
+
+@media (prefers-color-scheme: dark) { .stats button.exc { background: #333; } }
+
+.stats button.exc.show_exc { background: #eee; border: 2px solid #808080; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { .stats button.exc.show_exc { background: #333; } }
+
+.stats button.par { background: #ffffd5; }
+
+@media (prefers-color-scheme: dark) { .stats button.par { background: #650; } }
+
+.stats button.par.show_par { background: #ffa; border: 2px solid #dddd00; margin: 0 .1em; }
+
+@media (prefers-color-scheme: dark) { .stats button.par.show_par { background: #650; } }
+
+.help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; }
+
+#source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; }
+
+#keyboard_icon { float: right; margin: 5px; cursor: pointer; }
+
+.help_panel { padding: .5em; border: 1px solid #883; }
+
+.help_panel .legend { font-style: italic; margin-bottom: 1em; }
+
+.indexfile .help_panel { width: 20em; min-height: 4em; }
+
+.pyfile .help_panel { width: 16em; min-height: 8em; }
+
+#panel_icon { float: right; cursor: pointer; }
+
+.keyhelp { margin: .75em; }
+
+.keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; }
+
+#source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
+
+#source p { position: relative; white-space: pre; }
+
+#source p * { box-sizing: border-box; }
+
+#source p .n { float: left; text-align: right; width: 3.5rem; box-sizing: border-box; margin-left: -3.5rem; padding-right: 1em; color: #999; }
+
+@media (prefers-color-scheme: dark) { #source p .n { color: #777; } }
+
+#source p .n a { text-decoration: none; color: #999; }
+
+@media (prefers-color-scheme: dark) { #source p .n a { color: #777; } }
+
+#source p .n a:hover { text-decoration: underline; color: #999; }
+
+@media (prefers-color-scheme: dark) { #source p .n a:hover { color: #777; } }
+
+#source p.highlight .n { background: #ffdd00; }
+
+#source p .t { display: inline-block; width: 100%; box-sizing: border-box; margin-left: -.5em; padding-left: 0.3em; border-left: 0.2em solid #fff; }
+
+@media (prefers-color-scheme: dark) { #source p .t { border-color: #1e1e1e; } }
+
+#source p .t:hover { background: #f2f2f2; }
+
+@media (prefers-color-scheme: dark) { #source p .t:hover { background: #282828; } }
+
+#source p .t:hover ~ .r .annotate.long { display: block; }
+
+#source p .t .com { color: #008000; font-style: italic; line-height: 1px; }
+
+@media (prefers-color-scheme: dark) { #source p .t .com { color: #6A9955; } }
+
+#source p .t .key { font-weight: bold; line-height: 1px; }
+
+#source p .t .str { color: #0451A5; }
+
+@media (prefers-color-scheme: dark) { #source p .t .str { color: #9CDCFE; } }
+
+#source p.mis .t { border-left: 0.2em solid #ff0000; }
+
+#source p.mis.show_mis .t { background: #fdd; }
+
+@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t { background: #4b1818; } }
+
+#source p.mis.show_mis .t:hover { background: #f2d2d2; }
+
+@media (prefers-color-scheme: dark) { #source p.mis.show_mis .t:hover { background: #532323; } }
+
+#source p.run .t { border-left: 0.2em solid #00dd00; }
+
+#source p.run.show_run .t { background: #dfd; }
+
+@media (prefers-color-scheme: dark) { #source p.run.show_run .t { background: #373d29; } }
+
+#source p.run.show_run .t:hover { background: #d2f2d2; }
+
+@media (prefers-color-scheme: dark) { #source p.run.show_run .t:hover { background: #404633; } }
+
+#source p.exc .t { border-left: 0.2em solid #808080; }
+
+#source p.exc.show_exc .t { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t { background: #333; } }
+
+#source p.exc.show_exc .t:hover { background: #e2e2e2; }
+
+@media (prefers-color-scheme: dark) { #source p.exc.show_exc .t:hover { background: #3c3c3c; } }
+
+#source p.par .t { border-left: 0.2em solid #dddd00; }
+
+#source p.par.show_par .t { background: #ffa; }
+
+@media (prefers-color-scheme: dark) { #source p.par.show_par .t { background: #650; } }
+
+#source p.par.show_par .t:hover { background: #f2f2a2; }
+
+@media (prefers-color-scheme: dark) { #source p.par.show_par .t:hover { background: #6d5d0c; } }
+
+#source p .r { position: absolute; top: 0; right: 2.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
+
+#source p .annotate { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; color: #666; padding-right: .5em; }
+
+@media (prefers-color-scheme: dark) { #source p .annotate { color: #ddd; } }
+
+#source p .annotate.short:hover ~ .long { display: block; }
+
+#source p .annotate.long { width: 30em; right: 2.5em; }
+
+#source p input { display: none; }
+
+#source p input ~ .r label.ctx { cursor: pointer; border-radius: .25em; }
+
+#source p input ~ .r label.ctx::before { content: "â–¶ "; }
+
+#source p input ~ .r label.ctx:hover { background: #d5f7ff; color: #666; }
+
+@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { background: #0f3a42; } }
+
+@media (prefers-color-scheme: dark) { #source p input ~ .r label.ctx:hover { color: #aaa; } }
+
+#source p input:checked ~ .r label.ctx { background: #aef; color: #666; border-radius: .75em .75em 0 0; padding: 0 .5em; margin: -.25em 0; }
+
+@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { background: #056; } }
+
+@media (prefers-color-scheme: dark) { #source p input:checked ~ .r label.ctx { color: #aaa; } }
+
+#source p input:checked ~ .r label.ctx::before { content: "â–¼ "; }
+
+#source p input:checked ~ .ctxs { padding: .25em .5em; overflow-y: scroll; max-height: 10.5em; }
+
+#source p label.ctx { color: #999; display: inline-block; padding: 0 .5em; font-size: .8333em; }
+
+@media (prefers-color-scheme: dark) { #source p label.ctx { color: #777; } }
+
+#source p .ctxs { display: block; max-height: 0; overflow-y: hidden; transition: all .2s; padding: 0 .5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; white-space: nowrap; background: #aef; border-radius: .25em; margin-right: 1.75em; }
+
+@media (prefers-color-scheme: dark) { #source p .ctxs { background: #056; } }
+
+#source p .ctxs span { display: block; text-align: right; }
+
+#index { font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.875em; }
+
+#index table.index { margin-left: -.5em; }
+
+#index td, #index th { text-align: right; width: 5em; padding: .25em .5em; border-bottom: 1px solid #eee; }
+
+@media (prefers-color-scheme: dark) { #index td, #index th { border-color: #333; } }
+
+#index td.name, #index th.name { text-align: left; width: auto; }
+
+#index th { font-style: italic; color: #333; cursor: pointer; }
+
+@media (prefers-color-scheme: dark) { #index th { color: #ddd; } }
+
+#index th:hover { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #index th:hover { background: #333; } }
+
+#index th.headerSortDown, #index th.headerSortUp { white-space: nowrap; background: #eee; }
+
+@media (prefers-color-scheme: dark) { #index th.headerSortDown, #index th.headerSortUp { background: #333; } }
+
+#index th.headerSortDown:after { content: " ↑"; }
+
+#index th.headerSortUp:after { content: " ↓"; }
+
+#index td.name a { text-decoration: none; color: inherit; }
+
+#index tr.total td, #index tr.total_dynamic td { font-weight: bold; border-top: 1px solid #ccc; border-bottom: none; }
+
+#index tr.file:hover { background: #eee; }
+
+@media (prefers-color-scheme: dark) { #index tr.file:hover { background: #333; } }
+
+#index tr.file:hover td.name { text-decoration: underline; color: inherit; }
+
+#scroll_marker { position: fixed; right: 0; top: 0; width: 16px; height: 100%; background: #fff; border-left: 1px solid #eee; will-change: transform; }
+
+@media (prefers-color-scheme: dark) { #scroll_marker { background: #1e1e1e; } }
+
+@media (prefers-color-scheme: dark) { #scroll_marker { border-color: #333; } }
+
+#scroll_marker .marker { background: #ccc; position: absolute; min-height: 3px; width: 100%; }
+
+@media (prefers-color-scheme: dark) { #scroll_marker .marker { background: #444; } }
diff --git a/contrib/python/coverage/py3/coverage/htmlfiles/style.scss b/contrib/python/coverage/py3/coverage/htmlfiles/style.scss
new file mode 100644
index 0000000000..158d1fb493
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/htmlfiles/style.scss
@@ -0,0 +1,660 @@
+/* Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 */
+/* For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt */
+
+// CSS styles for coverage.py HTML reports.
+
+// When you edit this file, you need to run "make css" to get the CSS file
+// generated, and then check in both the .scss and the .css files.
+
+// When working on the file, this command is useful:
+// sass --watch --style=compact --sourcemap=none --no-cache coverage/htmlfiles/style.scss:htmlcov/style.css
+//
+// OR you can process sass purely in python with `pip install pysass`, then:
+// pysassc --style=compact coverage/htmlfiles/style.scss coverage/htmlfiles/style.css
+
+// Ignore this comment, it's for the CSS output file:
+/* Don't edit this .css file. Edit the .scss file instead! */
+
+// Dimensions
+$left-gutter: 3.5rem;
+
+
+//
+// Declare colors and variables
+//
+
+$font-normal: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
+$font-code: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
+
+$off-button-lighten: 50%;
+$hover-dark-amt: 95%;
+
+$focus-color: #007acc;
+
+$mis-color: #ff0000;
+$run-color: #00dd00;
+$exc-color: #808080;
+$par-color: #dddd00;
+
+$light-bg: #fff;
+$light-fg: #000;
+$light-gray1: #f8f8f8;
+$light-gray2: #eee;
+$light-gray3: #ccc;
+$light-gray4: #999;
+$light-gray5: #666;
+$light-gray6: #333;
+$light-pln-bg: $light-bg;
+$light-mis-bg: #fdd;
+$light-run-bg: #dfd;
+$light-exc-bg: $light-gray2;
+$light-par-bg: #ffa;
+$light-token-com: #008000;
+$light-token-str: #0451A5;
+$light-context-bg-color: #aef;
+
+$dark-bg: #1e1e1e;
+$dark-fg: #eee;
+$dark-gray1: #222;
+$dark-gray2: #333;
+$dark-gray3: #444;
+$dark-gray4: #777;
+$dark-gray5: #aaa;
+$dark-gray6: #ddd;
+$dark-pln-bg: $dark-bg;
+$dark-mis-bg: #4b1818;
+$dark-run-bg: #373d29;
+$dark-exc-bg: $dark-gray2;
+$dark-par-bg: #650;
+$dark-token-com: #6A9955;
+$dark-token-str: #9CDCFE;
+$dark-context-bg-color: #056;
+
+//
+// Mixins and utilities
+//
+@mixin background-dark($color) {
+ @media (prefers-color-scheme: dark) {
+ background: $color;
+ }
+}
+@mixin color-dark($color) {
+ @media (prefers-color-scheme: dark) {
+ color: $color;
+ }
+}
+@mixin border-color-dark($color) {
+ @media (prefers-color-scheme: dark) {
+ border-color: $color;
+ }
+}
+
+// Add visual outline to navigable elements on focus improve accessibility.
+@mixin focus-border {
+ &:active, &:focus {
+ outline: 2px dashed $focus-color;
+ }
+}
+
+// Page-wide styles
+html, body, h1, h2, h3, p, table, td, th {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
+}
+
+// Set baseline grid to 16 pt.
+body {
+ font-family: $font-normal;
+ font-size: 1em;
+ background: $light-bg;
+ color: $light-fg;
+ @include background-dark($dark-bg);
+ @include color-dark($dark-fg);
+}
+
+html>body {
+ font-size: 16px;
+}
+
+a {
+ @include focus-border;
+}
+
+p {
+ font-size: .875em;
+ line-height: 1.4em;
+}
+
+table {
+ border-collapse: collapse;
+}
+td {
+ vertical-align: top;
+}
+table tr.hidden {
+ display: none !important;
+}
+
+p#no_rows {
+ display: none;
+ font-size: 1.2em;
+}
+
+a.nav {
+ text-decoration: none;
+ color: inherit;
+
+ &:hover {
+ text-decoration: underline;
+ color: inherit;
+ }
+}
+
+// Page structure
+#header {
+ background: $light-gray1;
+ @include background-dark(black);
+ width: 100%;
+ border-bottom: 1px solid $light-gray2;
+ @include border-color-dark($dark-gray2);
+}
+
+.indexfile #footer {
+ margin: 1rem $left-gutter;
+}
+
+.pyfile #footer {
+ margin: 1rem 1rem;
+}
+
+#footer .content {
+ padding: 0;
+ color: $light-gray5;
+ @include color-dark($dark-gray5);
+ font-style: italic;
+}
+
+#index {
+ margin: 1rem 0 0 $left-gutter;
+}
+
+// Header styles
+#header .content {
+ padding: 1rem $left-gutter;
+}
+
+h1 {
+ font-size: 1.25em;
+ display: inline-block;
+}
+
+#filter_container {
+ float: right;
+ margin: 0 2em 0 0;
+
+ input {
+ width: 10em;
+ padding: 0.2em 0.5em;
+ border: 2px solid $light-gray3;
+ background: $light-bg;
+ color: $light-fg;
+ @include border-color-dark($dark-gray3);
+ @include background-dark($dark-bg);
+ @include color-dark($dark-fg);
+ &:focus {
+ border-color: $focus-color;
+ }
+ }
+}
+
+h2.stats {
+ margin-top: .5em;
+ font-size: 1em;
+}
+.stats button {
+ font-family: inherit;
+ font-size: inherit;
+ border: 1px solid;
+ border-radius: .2em;
+ color: inherit;
+ padding: .1em .5em;
+ margin: 1px calc(.1em + 1px);
+ cursor: pointer;
+ border-color: $light-gray3;
+ @include border-color-dark($dark-gray3);
+ @include focus-border;
+
+ @include focus-border;
+
+ &.run {
+ background: mix($light-run-bg, $light-bg, $off-button-lighten);
+ @include background-dark($dark-run-bg);
+ &.show_run {
+ background: $light-run-bg;
+ @include background-dark($dark-run-bg);
+ border: 2px solid $run-color;
+ margin: 0 .1em;
+ }
+ }
+ &.mis {
+ background: mix($light-mis-bg, $light-bg, $off-button-lighten);
+ @include background-dark($dark-mis-bg);
+ &.show_mis {
+ background: $light-mis-bg;
+ @include background-dark($dark-mis-bg);
+ border: 2px solid $mis-color;
+ margin: 0 .1em;
+ }
+ }
+ &.exc {
+ background: mix($light-exc-bg, $light-bg, $off-button-lighten);
+ @include background-dark($dark-exc-bg);
+ &.show_exc {
+ background: $light-exc-bg;
+ @include background-dark($dark-exc-bg);
+ border: 2px solid $exc-color;
+ margin: 0 .1em;
+ }
+ }
+ &.par {
+ background: mix($light-par-bg, $light-bg, $off-button-lighten);
+ @include background-dark($dark-par-bg);
+ &.show_par {
+ background: $light-par-bg;
+ @include background-dark($dark-par-bg);
+ border: 2px solid $par-color;
+ margin: 0 .1em;
+ }
+ }
+}
+
+// Yellow post-it things.
+%popup {
+ display: none;
+ position: absolute;
+ z-index: 999;
+ background: #ffffcc;
+ border: 1px solid #888;
+ border-radius: .2em;
+ color: #333;
+ padding: .25em .5em;
+}
+
+// Yellow post-it's in the text listings.
+%in-text-popup {
+ @extend %popup;
+ white-space: normal;
+ float: right;
+ top: 1.75em;
+ right: 1em;
+ height: auto;
+}
+
+// Help panel
+#keyboard_icon {
+ float: right;
+ margin: 5px;
+ cursor: pointer;
+}
+
+.help_panel {
+ @extend %popup;
+ padding: .5em;
+ border: 1px solid #883;
+
+ .legend {
+ font-style: italic;
+ margin-bottom: 1em;
+ }
+
+ .indexfile & {
+ width: 20em;
+ min-height: 4em;
+ }
+
+ .pyfile & {
+ width: 16em;
+ min-height: 8em;
+ }
+}
+
+#panel_icon {
+ float: right;
+ cursor: pointer;
+}
+
+.keyhelp {
+ margin: .75em;
+
+ .key {
+ border: 1px solid black;
+ border-color: #888 #333 #333 #888;
+ padding: .1em .35em;
+ font-family: $font-code;
+ font-weight: bold;
+ background: #eee;
+ }
+}
+
+// Source file styles
+
+// The slim bar at the left edge of the source lines, colored by coverage.
+$border-indicator-width: .2em;
+
+#source {
+ padding: 1em 0 1em $left-gutter;
+ font-family: $font-code;
+
+ p {
+ // position relative makes position:absolute pop-ups appear in the right place.
+ position: relative;
+ white-space: pre;
+
+ * {
+ box-sizing: border-box;
+ }
+
+ .n {
+ float: left;
+ text-align: right;
+ width: $left-gutter;
+ box-sizing: border-box;
+ margin-left: -$left-gutter;
+ padding-right: 1em;
+ color: $light-gray4;
+ @include color-dark($dark-gray4);
+
+ a {
+ text-decoration: none;
+ color: $light-gray4;
+ @include color-dark($dark-gray4);
+ &:hover {
+ text-decoration: underline;
+ color: $light-gray4;
+ @include color-dark($dark-gray4);
+ }
+ }
+ }
+
+ &.highlight .n {
+ background: #ffdd00;
+ }
+
+ .t {
+ display: inline-block;
+ width: 100%;
+ box-sizing: border-box;
+ margin-left: -.5em;
+ padding-left: .5em - $border-indicator-width;
+ border-left: $border-indicator-width solid $light-bg;
+ @include border-color-dark($dark-bg);
+
+ &:hover {
+ background: mix($light-pln-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-pln-bg, $dark-fg, $hover-dark-amt));
+
+ & ~ .r .annotate.long {
+ display: block;
+ }
+ }
+
+ // Syntax coloring
+ .com {
+ color: $light-token-com;
+ @include color-dark($dark-token-com);
+ font-style: italic;
+ line-height: 1px;
+ }
+ .key {
+ font-weight: bold;
+ line-height: 1px;
+ }
+ .str {
+ color: $light-token-str;
+ @include color-dark($dark-token-str);
+ }
+ }
+
+ &.mis {
+ .t {
+ border-left: $border-indicator-width solid $mis-color;
+ }
+
+ &.show_mis .t {
+ background: $light-mis-bg;
+ @include background-dark($dark-mis-bg);
+
+ &:hover {
+ background: mix($light-mis-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-mis-bg, $dark-fg, $hover-dark-amt));
+ }
+ }
+ }
+
+ &.run {
+ .t {
+ border-left: $border-indicator-width solid $run-color;
+ }
+
+ &.show_run .t {
+ background: $light-run-bg;
+ @include background-dark($dark-run-bg);
+
+ &:hover {
+ background: mix($light-run-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-run-bg, $dark-fg, $hover-dark-amt));
+ }
+ }
+ }
+
+ &.exc {
+ .t {
+ border-left: $border-indicator-width solid $exc-color;
+ }
+
+ &.show_exc .t {
+ background: $light-exc-bg;
+ @include background-dark($dark-exc-bg);
+
+ &:hover {
+ background: mix($light-exc-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-exc-bg, $dark-fg, $hover-dark-amt));
+ }
+ }
+ }
+
+ &.par {
+ .t {
+ border-left: $border-indicator-width solid $par-color;
+ }
+
+ &.show_par .t {
+ background: $light-par-bg;
+ @include background-dark($dark-par-bg);
+
+ &:hover {
+ background: mix($light-par-bg, $light-fg, $hover-dark-amt);
+ @include background-dark(mix($dark-par-bg, $dark-fg, $hover-dark-amt));
+ }
+ }
+
+ }
+
+ .r {
+ position: absolute;
+ top: 0;
+ right: 2.5em;
+ font-family: $font-normal;
+ }
+
+ .annotate {
+ font-family: $font-normal;
+ color: $light-gray5;
+ @include color-dark($dark-gray6);
+ padding-right: .5em;
+
+ &.short:hover ~ .long {
+ display: block;
+ }
+
+ &.long {
+ @extend %in-text-popup;
+ width: 30em;
+ right: 2.5em;
+ }
+ }
+
+ input {
+ display: none;
+
+ & ~ .r label.ctx {
+ cursor: pointer;
+ border-radius: .25em;
+ &::before {
+ content: "â–¶ ";
+ }
+ &:hover {
+ background: mix($light-context-bg-color, $light-bg, $off-button-lighten);
+ @include background-dark(mix($dark-context-bg-color, $dark-bg, $off-button-lighten));
+ color: $light-gray5;
+ @include color-dark($dark-gray5);
+ }
+ }
+
+ &:checked ~ .r label.ctx {
+ background: $light-context-bg-color;
+ @include background-dark($dark-context-bg-color);
+ color: $light-gray5;
+ @include color-dark($dark-gray5);
+ border-radius: .75em .75em 0 0;
+ padding: 0 .5em;
+ margin: -.25em 0;
+ &::before {
+ content: "â–¼ ";
+ }
+ }
+
+ &:checked ~ .ctxs {
+ padding: .25em .5em;
+ overflow-y: scroll;
+ max-height: 10.5em;
+ }
+ }
+
+ label.ctx {
+ color: $light-gray4;
+ @include color-dark($dark-gray4);
+ display: inline-block;
+ padding: 0 .5em;
+ font-size: .8333em; // 10/12
+ }
+
+ .ctxs {
+ display: block;
+ max-height: 0;
+ overflow-y: hidden;
+ transition: all .2s;
+ padding: 0 .5em;
+ font-family: $font-normal;
+ white-space: nowrap;
+ background: $light-context-bg-color;
+ @include background-dark($dark-context-bg-color);
+ border-radius: .25em;
+ margin-right: 1.75em;
+ span {
+ display: block;
+ text-align: right;
+ }
+ }
+ }
+}
+
+
+// index styles
+#index {
+ font-family: $font-code;
+ font-size: 0.875em;
+
+ table.index {
+ margin-left: -.5em;
+ }
+ td, th {
+ text-align: right;
+ width: 5em;
+ padding: .25em .5em;
+ border-bottom: 1px solid $light-gray2;
+ @include border-color-dark($dark-gray2);
+ &.name {
+ text-align: left;
+ width: auto;
+ }
+ }
+ th {
+ font-style: italic;
+ color: $light-gray6;
+ @include color-dark($dark-gray6);
+ cursor: pointer;
+ &:hover {
+ background: $light-gray2;
+ @include background-dark($dark-gray2);
+ }
+ &.headerSortDown, &.headerSortUp {
+ white-space: nowrap;
+ background: $light-gray2;
+ @include background-dark($dark-gray2);
+ }
+ &.headerSortDown:after {
+ content: " ↑";
+ }
+ &.headerSortUp:after {
+ content: " ↓";
+ }
+ }
+ td.name a {
+ text-decoration: none;
+ color: inherit;
+ }
+
+ tr.total td,
+ tr.total_dynamic td {
+ font-weight: bold;
+ border-top: 1px solid #ccc;
+ border-bottom: none;
+ }
+ tr.file:hover {
+ background: $light-gray2;
+ @include background-dark($dark-gray2);
+ td.name {
+ text-decoration: underline;
+ color: inherit;
+ }
+ }
+}
+
+// scroll marker styles
+#scroll_marker {
+ position: fixed;
+ right: 0;
+ top: 0;
+ width: 16px;
+ height: 100%;
+ background: $light-bg;
+ border-left: 1px solid $light-gray2;
+ @include background-dark($dark-bg);
+ @include border-color-dark($dark-gray2);
+ will-change: transform; // for faster scrolling of fixed element in Chrome
+
+ .marker {
+ background: $light-gray3;
+ @include background-dark($dark-gray3);
+ position: absolute;
+ min-height: 3px;
+ width: 100%;
+ }
+}
diff --git a/contrib/python/coverage/py3/coverage/inorout.py b/contrib/python/coverage/py3/coverage/inorout.py
new file mode 100644
index 0000000000..cbc80e8fb5
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/inorout.py
@@ -0,0 +1,513 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Determining whether files are being measured/reported or not."""
+
+# For finding the stdlib
+import atexit
+import inspect
+import itertools
+import os
+import platform
+import re
+import sys
+import traceback
+
+from coverage import env
+from coverage.backward import code_object
+from coverage.disposition import FileDisposition, disposition_init
+from coverage.files import TreeMatcher, FnmatchMatcher, ModuleMatcher
+from coverage.files import prep_patterns, find_python_files, canonical_filename
+from coverage.misc import CoverageException
+from coverage.python import source_for_file, source_for_morf
+
+
+# Pypy has some unusual stuff in the "stdlib". Consider those locations
+# when deciding where the stdlib is. These modules are not used for anything,
+# they are modules importable from the pypy lib directories, so that we can
+# find those directories.
+_structseq = _pypy_irc_topic = None
+if env.PYPY:
+ try:
+ import _structseq
+ except ImportError:
+ pass
+
+ try:
+ import _pypy_irc_topic
+ except ImportError:
+ pass
+
+
+def canonical_path(morf, directory=False):
+ """Return the canonical path of the module or file `morf`.
+
+ If the module is a package, then return its directory. If it is a
+ module, then return its file, unless `directory` is True, in which
+ case return its enclosing directory.
+
+ """
+ morf_path = canonical_filename(source_for_morf(morf))
+ if morf_path.endswith("__init__.py") or directory:
+ morf_path = os.path.split(morf_path)[0]
+ return morf_path
+
+
+def name_for_module(filename, frame):
+ """Get the name of the module for a filename and frame.
+
+ For configurability's sake, we allow __main__ modules to be matched by
+ their importable name.
+
+ If loaded via runpy (aka -m), we can usually recover the "original"
+ full dotted module name, otherwise, we resort to interpreting the
+ file name to get the module's name. In the case that the module name
+ can't be determined, None is returned.
+
+ """
+ module_globals = frame.f_globals if frame is not None else {}
+ if module_globals is None: # pragma: only ironpython
+ # IronPython doesn't provide globals: https://github.com/IronLanguages/main/issues/1296
+ module_globals = {}
+
+ dunder_name = module_globals.get('__name__', None)
+
+ if isinstance(dunder_name, str) and dunder_name != '__main__':
+ # This is the usual case: an imported module.
+ return dunder_name
+
+ loader = module_globals.get('__loader__', None)
+ for attrname in ('fullname', 'name'): # attribute renamed in py3.2
+ if hasattr(loader, attrname):
+ fullname = getattr(loader, attrname)
+ else:
+ continue
+
+ if isinstance(fullname, str) and fullname != '__main__':
+ # Module loaded via: runpy -m
+ return fullname
+
+ # Script as first argument to Python command line.
+ inspectedname = inspect.getmodulename(filename)
+ if inspectedname is not None:
+ return inspectedname
+ else:
+ return dunder_name
+
+
+def module_is_namespace(mod):
+ """Is the module object `mod` a PEP420 namespace module?"""
+ return hasattr(mod, '__path__') and getattr(mod, '__file__', None) is None
+
+
+def module_has_file(mod):
+ """Does the module object `mod` have an existing __file__ ?"""
+ mod__file__ = getattr(mod, '__file__', None)
+ if mod__file__ is None:
+ return False
+ return os.path.exists(mod__file__)
+
+
+class InOrOut(object):
+ """Machinery for determining what files to measure."""
+
+ def __init__(self, warn, debug):
+ self.warn = warn
+ self.debug = debug
+
+ # The matchers for should_trace.
+ self.source_match = None
+ self.source_pkgs_match = None
+ self.pylib_paths = self.cover_paths = None
+ self.pylib_match = self.cover_match = None
+ self.include_match = self.omit_match = None
+ self.plugins = []
+ self.disp_class = FileDisposition
+
+ # The source argument can be directories or package names.
+ self.source = []
+ self.source_pkgs = []
+ self.source_pkgs_unmatched = []
+ self.omit = self.include = None
+
+ def configure(self, config):
+ """Apply the configuration to get ready for decision-time."""
+ self.config = config
+ self.source_pkgs.extend(config.source_pkgs)
+ for src in config.source or []:
+ if os.path.isdir(src):
+ self.source.append(canonical_filename(src))
+ else:
+ self.source_pkgs.append(src)
+ self.source_pkgs_unmatched = self.source_pkgs[:]
+
+ self.omit = prep_patterns(config.run_omit)
+ if getattr(sys, 'is_standalone_binary', False):
+ # don't trace contrib
+ self.omit.append('contrib/python/*')
+ self.omit.append('contrib/libs/protobuf/*')
+ self.omit.append('library/python/pytest/*')
+ self.include = prep_patterns(config.run_include)
+
+ # The directories for files considered "installed with the interpreter".
+ self.pylib_paths = set()
+ if getattr(sys, 'is_standalone_binary', False):
+ self.pylib_paths.add('contrib/tools/python')
+ self.pylib_paths.add('contrib/tools/python3')
+ if not self.pylib_paths and not config.cover_pylib:
+ # Look at where some standard modules are located. That's the
+ # indication for "installed with the interpreter". In some
+ # environments (virtualenv, for example), these modules may be
+ # spread across a few locations. Look at all the candidate modules
+ # we've imported, and take all the different ones.
+ for m in (atexit, inspect, os, platform, _pypy_irc_topic, re, _structseq, traceback):
+ if m is not None and hasattr(m, "__file__"):
+ self.pylib_paths.add(canonical_path(m, directory=True))
+
+ if _structseq and not hasattr(_structseq, '__file__'):
+ # PyPy 2.4 has no __file__ in the builtin modules, but the code
+ # objects still have the file names. So dig into one to find
+ # the path to exclude. The "filename" might be synthetic,
+ # don't be fooled by those.
+ structseq_file = code_object(_structseq.structseq_new).co_filename
+ if not structseq_file.startswith("<"):
+ self.pylib_paths.add(canonical_path(structseq_file))
+
+ # To avoid tracing the coverage.py code itself, we skip anything
+ # located where we are.
+ if getattr(sys, 'is_standalone_binary', False):
+ self.cover_paths = ["contrib/python/coverage"]
+ else:
+ self.cover_paths = [canonical_path(__file__, directory=True)]
+ if env.TESTING:
+ # Don't include our own test code.
+ self.cover_paths.append(os.path.join(self.cover_paths[0], "tests"))
+
+ # When testing, we use PyContracts, which should be considered
+ # part of coverage.py, and it uses six. Exclude those directories
+ # just as we exclude ourselves.
+ import contracts
+ import six
+ for mod in [contracts, six]:
+ self.cover_paths.append(canonical_path(mod))
+
+ def debug(msg):
+ if self.debug:
+ self.debug.write(msg)
+
+ # Create the matchers we need for should_trace
+ if self.source or self.source_pkgs:
+ against = []
+ if self.source:
+ self.source_match = TreeMatcher(self.source)
+ against.append("trees {!r}".format(self.source_match))
+ if self.source_pkgs:
+ self.source_pkgs_match = ModuleMatcher(self.source_pkgs)
+ against.append("modules {!r}".format(self.source_pkgs_match))
+ debug("Source matching against " + " and ".join(against))
+ else:
+ if self.cover_paths:
+ self.cover_match = TreeMatcher(self.cover_paths)
+ debug("Coverage code matching: {!r}".format(self.cover_match))
+ if self.pylib_paths:
+ self.pylib_match = TreeMatcher(self.pylib_paths)
+ debug("Python stdlib matching: {!r}".format(self.pylib_match))
+ if self.include:
+ self.include_match = FnmatchMatcher(self.include)
+ debug("Include matching: {!r}".format(self.include_match))
+ if self.omit:
+ self.omit_match = FnmatchMatcher(self.omit)
+ debug("Omit matching: {!r}".format(self.omit_match))
+
+ def should_trace(self, filename, frame=None):
+ """Decide whether to trace execution in `filename`, with a reason.
+
+ This function is called from the trace function. As each new file name
+ is encountered, this function determines whether it is traced or not.
+
+ Returns a FileDisposition object.
+
+ """
+ original_filename = filename
+ disp = disposition_init(self.disp_class, filename)
+
+ def nope(disp, reason):
+ """Simple helper to make it easy to return NO."""
+ disp.trace = False
+ disp.reason = reason
+ return disp
+
+ if frame is not None:
+ # Compiled Python files have two file names: frame.f_code.co_filename is
+ # the file name at the time the .pyc was compiled. The second name is
+ # __file__, which is where the .pyc was actually loaded from. Since
+ # .pyc files can be moved after compilation (for example, by being
+ # installed), we look for __file__ in the frame and prefer it to the
+ # co_filename value.
+ dunder_file = frame.f_globals and frame.f_globals.get('__file__')
+ if dunder_file:
+ filename = source_for_file(dunder_file)
+ if original_filename and not original_filename.startswith('<'):
+ orig = os.path.basename(original_filename)
+ if orig != os.path.basename(filename):
+ # Files shouldn't be renamed when moved. This happens when
+ # exec'ing code. If it seems like something is wrong with
+ # the frame's file name, then just use the original.
+ filename = original_filename
+
+ if not filename:
+ # Empty string is pretty useless.
+ return nope(disp, "empty string isn't a file name")
+
+ if filename.startswith('memory:'):
+ return nope(disp, "memory isn't traceable")
+
+ if filename.startswith('<'):
+ # Lots of non-file execution is represented with artificial
+ # file names like "<string>", "<doctest readme.txt[0]>", or
+ # "<exec_function>". Don't ever trace these executions, since we
+ # can't do anything with the data later anyway.
+ return nope(disp, "not a real file name")
+
+ # pyexpat does a dumb thing, calling the trace function explicitly from
+ # C code with a C file name.
+ if re.search(r"[/\\]Modules[/\\]pyexpat.c", filename):
+ return nope(disp, "pyexpat lies about itself")
+
+ # Jython reports the .class file to the tracer, use the source file.
+ if filename.endswith("$py.class"):
+ filename = filename[:-9] + ".py"
+
+ # XXX maybe we need to support both at the same time?
+ # Don't trace modules imported from environment in standalone mode
+ if getattr(sys, 'is_standalone_binary', False) and filename.startswith("/"):
+ return nope(disp, "skip modules from environment")
+
+ canonical = canonical_filename(filename)
+ disp.canonical_filename = canonical
+
+ # Try the plugins, see if they have an opinion about the file.
+ plugin = None
+ for plugin in self.plugins.file_tracers:
+ if not plugin._coverage_enabled:
+ continue
+
+ try:
+ file_tracer = plugin.file_tracer(canonical)
+ if file_tracer is not None:
+ file_tracer._coverage_plugin = plugin
+ disp.trace = True
+ disp.file_tracer = file_tracer
+ if file_tracer.has_dynamic_source_filename():
+ disp.has_dynamic_filename = True
+ else:
+ disp.source_filename = canonical_filename(
+ file_tracer.source_filename()
+ )
+ break
+ except Exception:
+ if not self.config.suppress_plugin_errors:
+ raise
+ self.warn(
+ "Disabling plug-in %r due to an exception:" % (plugin._coverage_plugin_name)
+ )
+ traceback.print_exc()
+ plugin._coverage_enabled = False
+ continue
+ else:
+ # No plugin wanted it: it's Python.
+ disp.trace = True
+ disp.source_filename = canonical
+
+ if not disp.has_dynamic_filename:
+ if not disp.source_filename:
+ raise CoverageException(
+ "Plugin %r didn't set source_filename for %r" %
+ (plugin, disp.original_filename)
+ )
+ reason = self.check_include_omit_etc(disp.source_filename, frame)
+ if reason:
+ nope(disp, reason)
+
+ return disp
+
+ def check_include_omit_etc(self, filename, frame):
+ """Check a file name against the include, omit, etc, rules.
+
+ Returns a string or None. String means, don't trace, and is the reason
+ why. None means no reason found to not trace.
+
+ """
+ modulename = name_for_module(filename, frame)
+
+ # If the user specified source or include, then that's authoritative
+ # about the outer bound of what to measure and we don't have to apply
+ # any canned exclusions. If they didn't, then we have to exclude the
+ # stdlib and coverage.py directories.
+ if self.source_match or self.source_pkgs_match:
+ extra = ""
+ ok = False
+ if self.source_pkgs_match:
+ if self.source_pkgs_match.match(modulename):
+ ok = True
+ if modulename in self.source_pkgs_unmatched:
+ self.source_pkgs_unmatched.remove(modulename)
+ else:
+ extra = "module {!r} ".format(modulename)
+ if not ok and self.source_match:
+ if self.source_match.match(filename):
+ ok = True
+ if not ok:
+ return extra + "falls outside the --source spec"
+ elif self.include_match:
+ if not self.include_match.match(filename):
+ return "falls outside the --include trees"
+ else:
+ # If we aren't supposed to trace installed code, then check if this
+ # is near the Python standard library and skip it if so.
+ if self.pylib_match and self.pylib_match.match(filename):
+ return "is in the stdlib"
+
+ # We exclude the coverage.py code itself, since a little of it
+ # will be measured otherwise.
+ if self.cover_match and self.cover_match.match(filename):
+ return "is part of coverage.py"
+
+ # Check the file against the omit pattern.
+ if self.omit_match and self.omit_match.match(filename):
+ return "is inside an --omit pattern"
+
+ # No point tracing a file we can't later write to SQLite.
+ try:
+ filename.encode("utf8")
+ except UnicodeEncodeError:
+ return "non-encodable filename"
+
+ # No reason found to skip this file.
+ return None
+
+ def warn_conflicting_settings(self):
+ """Warn if there are settings that conflict."""
+ if self.include:
+ if self.source or self.source_pkgs:
+ self.warn("--include is ignored because --source is set", slug="include-ignored")
+
+ def warn_already_imported_files(self):
+ """Warn if files have already been imported that we will be measuring."""
+ if self.include or self.source or self.source_pkgs:
+ warned = set()
+ for mod in list(sys.modules.values()):
+ filename = getattr(mod, "__file__", None)
+ if filename is None:
+ continue
+ if filename in warned:
+ continue
+
+ disp = self.should_trace(filename)
+ if disp.trace:
+ msg = "Already imported a file that will be measured: {}".format(filename)
+ self.warn(msg, slug="already-imported")
+ warned.add(filename)
+
+ def warn_unimported_source(self):
+ """Warn about source packages that were of interest, but never traced."""
+ for pkg in self.source_pkgs_unmatched:
+ self._warn_about_unmeasured_code(pkg)
+
+ def _warn_about_unmeasured_code(self, pkg):
+ """Warn about a package or module that we never traced.
+
+ `pkg` is a string, the name of the package or module.
+
+ """
+ mod = sys.modules.get(pkg)
+ if mod is None:
+ self.warn("Module %s was never imported." % pkg, slug="module-not-imported")
+ return
+
+ if module_is_namespace(mod):
+ # A namespace package. It's OK for this not to have been traced,
+ # since there is no code directly in it.
+ return
+
+ if not module_has_file(mod):
+ self.warn("Module %s has no Python source." % pkg, slug="module-not-python")
+ return
+
+ # The module was in sys.modules, and seems like a module with code, but
+ # we never measured it. I guess that means it was imported before
+ # coverage even started.
+ self.warn(
+ "Module %s was previously imported, but not measured" % pkg,
+ slug="module-not-measured",
+ )
+
+ def find_possibly_unexecuted_files(self):
+ """Find files in the areas of interest that might be untraced.
+
+ Yields pairs: file path, and responsible plug-in name.
+ """
+ for pkg in self.source_pkgs:
+ if (not pkg in sys.modules or
+ not module_has_file(sys.modules[pkg])):
+ continue
+ pkg_file = source_for_file(sys.modules[pkg].__file__)
+ for ret in self._find_executable_files(canonical_path(pkg_file)):
+ yield ret
+
+ for src in self.source:
+ for ret in self._find_executable_files(src):
+ yield ret
+
+ def _find_plugin_files(self, src_dir):
+ """Get executable files from the plugins."""
+ for plugin in self.plugins.file_tracers:
+ for x_file in plugin.find_executable_files(src_dir):
+ yield x_file, plugin._coverage_plugin_name
+
+ def _find_executable_files(self, src_dir):
+ """Find executable files in `src_dir`.
+
+ Search for files in `src_dir` that can be executed because they
+ are probably importable. Don't include ones that have been omitted
+ by the configuration.
+
+ Yield the file path, and the plugin name that handles the file.
+
+ """
+ py_files = ((py_file, None) for py_file in find_python_files(src_dir))
+ plugin_files = self._find_plugin_files(src_dir)
+
+ for file_path, plugin_name in itertools.chain(py_files, plugin_files):
+ file_path = canonical_filename(file_path)
+ if self.omit_match and self.omit_match.match(file_path):
+ # Turns out this file was omitted, so don't pull it back
+ # in as unexecuted.
+ continue
+ yield file_path, plugin_name
+
+ def sys_info(self):
+ """Our information for Coverage.sys_info.
+
+ Returns a list of (key, value) pairs.
+ """
+ info = [
+ ('cover_paths', self.cover_paths),
+ ('pylib_paths', self.pylib_paths),
+ ]
+
+ matcher_names = [
+ 'source_match', 'source_pkgs_match',
+ 'include_match', 'omit_match',
+ 'cover_match', 'pylib_match',
+ ]
+
+ for matcher_name in matcher_names:
+ matcher = getattr(self, matcher_name)
+ if matcher:
+ matcher_info = matcher.info()
+ else:
+ matcher_info = '-none-'
+ info.append((matcher_name, matcher_info))
+
+ return info
diff --git a/contrib/python/coverage/py3/coverage/jsonreport.py b/contrib/python/coverage/py3/coverage/jsonreport.py
new file mode 100644
index 0000000000..4287bc79a3
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/jsonreport.py
@@ -0,0 +1,103 @@
+# coding: utf-8
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Json reporting for coverage.py"""
+import datetime
+import json
+import sys
+
+from coverage import __version__
+from coverage.report import get_analysis_to_report
+from coverage.results import Numbers
+
+
+class JsonReporter(object):
+ """A reporter for writing JSON coverage results."""
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+ self.total = Numbers()
+ self.report_data = {}
+
+ def report(self, morfs, outfile=None):
+ """Generate a json report for `morfs`.
+
+ `morfs` is a list of modules or file names.
+
+ `outfile` is a file object to write the json to
+
+ """
+ outfile = outfile or sys.stdout
+ coverage_data = self.coverage.get_data()
+ coverage_data.set_query_contexts(self.config.report_contexts)
+ self.report_data["meta"] = {
+ "version": __version__,
+ "timestamp": datetime.datetime.now().isoformat(),
+ "branch_coverage": coverage_data.has_arcs(),
+ "show_contexts": self.config.json_show_contexts,
+ }
+
+ measured_files = {}
+ for file_reporter, analysis in get_analysis_to_report(self.coverage, morfs):
+ measured_files[file_reporter.relative_filename()] = self.report_one_file(
+ coverage_data,
+ analysis
+ )
+
+ self.report_data["files"] = measured_files
+
+ self.report_data["totals"] = {
+ 'covered_lines': self.total.n_executed,
+ 'num_statements': self.total.n_statements,
+ 'percent_covered': self.total.pc_covered,
+ 'missing_lines': self.total.n_missing,
+ 'excluded_lines': self.total.n_excluded,
+ }
+
+ if coverage_data.has_arcs():
+ self.report_data["totals"].update({
+ 'num_branches': self.total.n_branches,
+ 'num_partial_branches': self.total.n_partial_branches,
+ 'covered_branches': self.total.n_executed_branches,
+ 'missing_branches': self.total.n_missing_branches,
+ })
+
+ json.dump(
+ self.report_data,
+ outfile,
+ indent=4 if self.config.json_pretty_print else None
+ )
+
+ return self.total.n_statements and self.total.pc_covered
+
+ def report_one_file(self, coverage_data, analysis):
+ """Extract the relevant report data for a single file"""
+ nums = analysis.numbers
+ self.total += nums
+ summary = {
+ 'covered_lines': nums.n_executed,
+ 'num_statements': nums.n_statements,
+ 'percent_covered': nums.pc_covered,
+ 'missing_lines': nums.n_missing,
+ 'excluded_lines': nums.n_excluded,
+ }
+ reported_file = {
+ 'executed_lines': sorted(analysis.executed),
+ 'summary': summary,
+ 'missing_lines': sorted(analysis.missing),
+ 'excluded_lines': sorted(analysis.excluded)
+ }
+ if self.config.json_show_contexts:
+ reported_file['contexts'] = analysis.data.contexts_by_lineno(
+ analysis.filename,
+ )
+ if coverage_data.has_arcs():
+ reported_file['summary'].update({
+ 'num_branches': nums.n_branches,
+ 'num_partial_branches': nums.n_partial_branches,
+ 'covered_branches': nums.n_executed_branches,
+ 'missing_branches': nums.n_missing_branches,
+ })
+ return reported_file
diff --git a/contrib/python/coverage/py3/coverage/misc.py b/contrib/python/coverage/py3/coverage/misc.py
new file mode 100644
index 0000000000..034e288eb9
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/misc.py
@@ -0,0 +1,361 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Miscellaneous stuff for coverage.py."""
+
+import errno
+import hashlib
+import inspect
+import locale
+import os
+import os.path
+import random
+import re
+import socket
+import sys
+import types
+
+from coverage import env
+from coverage.backward import to_bytes, unicode_class
+
+ISOLATED_MODULES = {}
+
+
+def isolate_module(mod):
+ """Copy a module so that we are isolated from aggressive mocking.
+
+ If a test suite mocks os.path.exists (for example), and then we need to use
+ it during the test, everything will get tangled up if we use their mock.
+ Making a copy of the module when we import it will isolate coverage.py from
+ those complications.
+ """
+ if mod not in ISOLATED_MODULES:
+ new_mod = types.ModuleType(mod.__name__)
+ ISOLATED_MODULES[mod] = new_mod
+ for name in dir(mod):
+ value = getattr(mod, name)
+ if isinstance(value, types.ModuleType):
+ value = isolate_module(value)
+ setattr(new_mod, name, value)
+ return ISOLATED_MODULES[mod]
+
+os = isolate_module(os)
+
+
+def dummy_decorator_with_args(*args_unused, **kwargs_unused):
+ """Dummy no-op implementation of a decorator with arguments."""
+ def _decorator(func):
+ return func
+ return _decorator
+
+
+# Environment COVERAGE_NO_CONTRACTS=1 can turn off contracts while debugging
+# tests to remove noise from stack traces.
+# $set_env.py: COVERAGE_NO_CONTRACTS - Disable PyContracts to simplify stack traces.
+USE_CONTRACTS = env.TESTING and not bool(int(os.environ.get("COVERAGE_NO_CONTRACTS", 0)))
+
+# Use PyContracts for assertion testing on parameters and returns, but only if
+# we are running our own test suite.
+if USE_CONTRACTS:
+ from contracts import contract # pylint: disable=unused-import
+ from contracts import new_contract as raw_new_contract
+
+ def new_contract(*args, **kwargs):
+ """A proxy for contracts.new_contract that doesn't mind happening twice."""
+ try:
+ raw_new_contract(*args, **kwargs)
+ except ValueError:
+ # During meta-coverage, this module is imported twice, and
+ # PyContracts doesn't like redefining contracts. It's OK.
+ pass
+
+ # Define contract words that PyContract doesn't have.
+ new_contract('bytes', lambda v: isinstance(v, bytes))
+ if env.PY3:
+ new_contract('unicode', lambda v: isinstance(v, unicode_class))
+
+ def one_of(argnames):
+ """Ensure that only one of the argnames is non-None."""
+ def _decorator(func):
+ argnameset = {name.strip() for name in argnames.split(",")}
+ def _wrapper(*args, **kwargs):
+ vals = [kwargs.get(name) for name in argnameset]
+ assert sum(val is not None for val in vals) == 1
+ return func(*args, **kwargs)
+ return _wrapper
+ return _decorator
+else: # pragma: not testing
+ # We aren't using real PyContracts, so just define our decorators as
+ # stunt-double no-ops.
+ contract = dummy_decorator_with_args
+ one_of = dummy_decorator_with_args
+
+ def new_contract(*args_unused, **kwargs_unused):
+ """Dummy no-op implementation of `new_contract`."""
+ pass
+
+
+def nice_pair(pair):
+ """Make a nice string representation of a pair of numbers.
+
+ If the numbers are equal, just return the number, otherwise return the pair
+ with a dash between them, indicating the range.
+
+ """
+ start, end = pair
+ if start == end:
+ return "%d" % start
+ else:
+ return "%d-%d" % (start, end)
+
+
+def expensive(fn):
+ """A decorator to indicate that a method shouldn't be called more than once.
+
+ Normally, this does nothing. During testing, this raises an exception if
+ called more than once.
+
+ """
+ if env.TESTING:
+ attr = "_once_" + fn.__name__
+
+ def _wrapper(self):
+ if hasattr(self, attr):
+ raise AssertionError("Shouldn't have called %s more than once" % fn.__name__)
+ setattr(self, attr, True)
+ return fn(self)
+ return _wrapper
+ else:
+ return fn # pragma: not testing
+
+
+def bool_or_none(b):
+ """Return bool(b), but preserve None."""
+ if b is None:
+ return None
+ else:
+ return bool(b)
+
+
+def join_regex(regexes):
+ """Combine a list of regexes into one that matches any of them."""
+ return "|".join("(?:%s)" % r for r in regexes)
+
+
+def file_be_gone(path):
+ """Remove a file, and don't get annoyed if it doesn't exist."""
+ try:
+ os.remove(path)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+
+
+def ensure_dir(directory):
+ """Make sure the directory exists.
+
+ If `directory` is None or empty, do nothing.
+ """
+ if directory and not os.path.isdir(directory):
+ os.makedirs(directory)
+
+
+def ensure_dir_for_file(path):
+ """Make sure the directory for the path exists."""
+ ensure_dir(os.path.dirname(path))
+
+
+def output_encoding(outfile=None):
+ """Determine the encoding to use for output written to `outfile` or stdout."""
+ if outfile is None:
+ outfile = sys.stdout
+ encoding = (
+ getattr(outfile, "encoding", None) or
+ getattr(sys.__stdout__, "encoding", None) or
+ locale.getpreferredencoding()
+ )
+ return encoding
+
+
+def filename_suffix(suffix):
+ """Compute a filename suffix for a data file.
+
+ If `suffix` is a string or None, simply return it. If `suffix` is True,
+ then build a suffix incorporating the hostname, process id, and a random
+ number.
+
+ Returns a string or None.
+
+ """
+ if suffix is True:
+ # If data_suffix was a simple true value, then make a suffix with
+ # plenty of distinguishing information. We do this here in
+ # `save()` at the last minute so that the pid will be correct even
+ # if the process forks.
+ dice = random.Random(os.urandom(8)).randint(0, 999999)
+ suffix = "%s.%s.%06d" % (socket.gethostname(), os.getpid(), dice)
+ return suffix
+
+
+class Hasher(object):
+ """Hashes Python data into md5."""
+ def __init__(self):
+ self.md5 = hashlib.md5()
+
+ def update(self, v):
+ """Add `v` to the hash, recursively if needed."""
+ self.md5.update(to_bytes(str(type(v))))
+ if isinstance(v, unicode_class):
+ self.md5.update(v.encode('utf8'))
+ elif isinstance(v, bytes):
+ self.md5.update(v)
+ elif v is None:
+ pass
+ elif isinstance(v, (int, float)):
+ self.md5.update(to_bytes(str(v)))
+ elif isinstance(v, (tuple, list)):
+ for e in v:
+ self.update(e)
+ elif isinstance(v, dict):
+ keys = v.keys()
+ for k in sorted(keys):
+ self.update(k)
+ self.update(v[k])
+ else:
+ for k in dir(v):
+ if k.startswith('__'):
+ continue
+ a = getattr(v, k)
+ if inspect.isroutine(a):
+ continue
+ self.update(k)
+ self.update(a)
+ self.md5.update(b'.')
+
+ def hexdigest(self):
+ """Retrieve the hex digest of the hash."""
+ return self.md5.hexdigest()
+
+
+def _needs_to_implement(that, func_name):
+ """Helper to raise NotImplementedError in interface stubs."""
+ if hasattr(that, "_coverage_plugin_name"):
+ thing = "Plugin"
+ name = that._coverage_plugin_name
+ else:
+ thing = "Class"
+ klass = that.__class__
+ name = "{klass.__module__}.{klass.__name__}".format(klass=klass)
+
+ raise NotImplementedError(
+ "{thing} {name!r} needs to implement {func_name}()".format(
+ thing=thing, name=name, func_name=func_name
+ )
+ )
+
+
+class DefaultValue(object):
+ """A sentinel object to use for unusual default-value needs.
+
+ Construct with a string that will be used as the repr, for display in help
+ and Sphinx output.
+
+ """
+ def __init__(self, display_as):
+ self.display_as = display_as
+
+ def __repr__(self):
+ return self.display_as
+
+
+def substitute_variables(text, variables):
+ """Substitute ``${VAR}`` variables in `text` with their values.
+
+ Variables in the text can take a number of shell-inspired forms::
+
+ $VAR
+ ${VAR}
+ ${VAR?} strict: an error if VAR isn't defined.
+ ${VAR-missing} defaulted: "missing" if VAR isn't defined.
+ $$ just a dollar sign.
+
+ `variables` is a dictionary of variable values.
+
+ Returns the resulting text with values substituted.
+
+ """
+ dollar_pattern = r"""(?x) # Use extended regex syntax
+ \$ # A dollar sign,
+ (?: # then
+ (?P<dollar>\$) | # a dollar sign, or
+ (?P<word1>\w+) | # a plain word, or
+ { # a {-wrapped
+ (?P<word2>\w+) # word,
+ (?:
+ (?P<strict>\?) | # with a strict marker
+ -(?P<defval>[^}]*) # or a default value
+ )? # maybe.
+ }
+ )
+ """
+
+ def dollar_replace(match):
+ """Called for each $replacement."""
+ # Only one of the groups will have matched, just get its text.
+ word = next(g for g in match.group('dollar', 'word1', 'word2') if g)
+ if word == "$":
+ return "$"
+ elif word in variables:
+ return variables[word]
+ elif match.group('strict'):
+ msg = "Variable {} is undefined: {!r}".format(word, text)
+ raise CoverageException(msg)
+ else:
+ return match.group('defval')
+
+ text = re.sub(dollar_pattern, dollar_replace, text)
+ return text
+
+
+class BaseCoverageException(Exception):
+ """The base of all Coverage exceptions."""
+ pass
+
+
+class CoverageException(BaseCoverageException):
+ """An exception raised by a coverage.py function."""
+ pass
+
+
+class NoSource(CoverageException):
+ """We couldn't find the source for a module."""
+ pass
+
+
+class NoCode(NoSource):
+ """We couldn't find any code at all."""
+ pass
+
+
+class NotPython(CoverageException):
+ """A source file turned out not to be parsable Python."""
+ pass
+
+
+class ExceptionDuringRun(CoverageException):
+ """An exception happened while running customer code.
+
+ Construct it with three arguments, the values from `sys.exc_info`.
+
+ """
+ pass
+
+
+class StopEverything(BaseCoverageException):
+ """An exception that means everything should stop.
+
+ The CoverageTest class converts these to SkipTest, so that when running
+ tests, raising this exception will automatically skip the test.
+
+ """
+ pass
diff --git a/contrib/python/coverage/py3/coverage/multiproc.py b/contrib/python/coverage/py3/coverage/multiproc.py
new file mode 100644
index 0000000000..21ed2e2c95
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/multiproc.py
@@ -0,0 +1,117 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Monkey-patching to add multiprocessing support for coverage.py"""
+
+import multiprocessing
+import multiprocessing.process
+import os
+import os.path
+import sys
+import traceback
+
+from coverage import env
+from coverage.misc import contract
+
+# An attribute that will be set on the module to indicate that it has been
+# monkey-patched.
+PATCHED_MARKER = "_coverage$patched"
+
+COVERAGE_CONFIGURATION_ENV = "_COVERAGE_CONFIGURATION_ENV"
+
+
+if env.PYVERSION >= (3, 4):
+ OriginalProcess = multiprocessing.process.BaseProcess
+else:
+ OriginalProcess = multiprocessing.Process
+
+original_bootstrap = OriginalProcess._bootstrap
+
+class ProcessWithCoverage(OriginalProcess): # pylint: disable=abstract-method
+ """A replacement for multiprocess.Process that starts coverage."""
+
+ def _bootstrap(self, *args, **kwargs):
+ """Wrapper around _bootstrap to start coverage."""
+ try:
+ from coverage import Coverage # avoid circular import
+ import json
+ kwconf = json.loads(os.environ[COVERAGE_CONFIGURATION_ENV])
+ cov = Coverage(**kwconf)
+ cov._warn_preimported_source = False
+ cov.start()
+ debug = cov._debug
+ if debug.should("multiproc"):
+ debug.write("Calling multiprocessing bootstrap")
+ except Exception:
+ print("Exception during multiprocessing bootstrap init:")
+ traceback.print_exc(file=sys.stdout)
+ sys.stdout.flush()
+ raise
+ try:
+ return original_bootstrap(self, *args, **kwargs)
+ finally:
+ if debug.should("multiproc"):
+ debug.write("Finished multiprocessing bootstrap")
+ cov.stop()
+ cov.save()
+ if debug.should("multiproc"):
+ debug.write("Saved multiprocessing data")
+
+class Stowaway(object):
+ """An object to pickle, so when it is unpickled, it can apply the monkey-patch."""
+ def __init__(self, rcfile):
+ self.rcfile = rcfile
+
+ def __getstate__(self):
+ return {'rcfile': self.rcfile}
+
+ def __setstate__(self, state):
+ patch_multiprocessing(state['rcfile'])
+
+
+@contract(rcfile=str)
+def patch_multiprocessing(rcfile, coverage_args):
+ """Monkey-patch the multiprocessing module.
+
+ This enables coverage measurement of processes started by multiprocessing.
+ This involves aggressive monkey-patching.
+
+ `rcfile` is the path to the rcfile being used.
+
+ """
+
+ if hasattr(multiprocessing, PATCHED_MARKER):
+ return
+
+ if env.PYVERSION >= (3, 4):
+ OriginalProcess._bootstrap = ProcessWithCoverage._bootstrap
+ else:
+ multiprocessing.Process = ProcessWithCoverage
+
+ # Set the value in ProcessWithCoverage that will be pickled into the child
+ # process.
+ os.environ["COVERAGE_RCFILE"] = os.path.abspath(rcfile)
+
+ os.environ[COVERAGE_CONFIGURATION_ENV] = coverage_args
+
+ # When spawning processes rather than forking them, we have no state in the
+ # new process. We sneak in there with a Stowaway: we stuff one of our own
+ # objects into the data that gets pickled and sent to the sub-process. When
+ # the Stowaway is unpickled, it's __setstate__ method is called, which
+ # re-applies the monkey-patch.
+ # Windows only spawns, so this is needed to keep Windows working.
+ try:
+ from multiprocessing import spawn
+ original_get_preparation_data = spawn.get_preparation_data
+ except (ImportError, AttributeError):
+ pass
+ else:
+ def get_preparation_data_with_stowaway(name):
+ """Get the original preparation data, and also insert our stowaway."""
+ d = original_get_preparation_data(name)
+ d['stowaway'] = Stowaway(rcfile)
+ return d
+
+ spawn.get_preparation_data = get_preparation_data_with_stowaway
+
+ setattr(multiprocessing, PATCHED_MARKER, True)
diff --git a/contrib/python/coverage/py3/coverage/numbits.py b/contrib/python/coverage/py3/coverage/numbits.py
new file mode 100644
index 0000000000..6ca96fbcf7
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/numbits.py
@@ -0,0 +1,163 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""
+Functions to manipulate packed binary representations of number sets.
+
+To save space, coverage stores sets of line numbers in SQLite using a packed
+binary representation called a numbits. A numbits is a set of positive
+integers.
+
+A numbits is stored as a blob in the database. The exact meaning of the bytes
+in the blobs should be considered an implementation detail that might change in
+the future. Use these functions to work with those binary blobs of data.
+
+"""
+import json
+
+from coverage import env
+from coverage.backward import byte_to_int, bytes_to_ints, binary_bytes, zip_longest
+from coverage.misc import contract, new_contract
+
+if env.PY3:
+ def _to_blob(b):
+ """Convert a bytestring into a type SQLite will accept for a blob."""
+ return b
+
+ new_contract('blob', lambda v: isinstance(v, bytes))
+else:
+ def _to_blob(b):
+ """Convert a bytestring into a type SQLite will accept for a blob."""
+ return buffer(b) # pylint: disable=undefined-variable
+
+ new_contract('blob', lambda v: isinstance(v, buffer)) # pylint: disable=undefined-variable
+
+
+@contract(nums='Iterable', returns='blob')
+def nums_to_numbits(nums):
+ """Convert `nums` into a numbits.
+
+ Arguments:
+ nums: a reusable iterable of integers, the line numbers to store.
+
+ Returns:
+ A binary blob.
+ """
+ try:
+ nbytes = max(nums) // 8 + 1
+ except ValueError:
+ # nums was empty.
+ return _to_blob(b'')
+ b = bytearray(nbytes)
+ for num in nums:
+ b[num//8] |= 1 << num % 8
+ return _to_blob(bytes(b))
+
+
+@contract(numbits='blob', returns='list[int]')
+def numbits_to_nums(numbits):
+ """Convert a numbits into a list of numbers.
+
+ Arguments:
+ numbits: a binary blob, the packed number set.
+
+ Returns:
+ A list of ints.
+
+ When registered as a SQLite function by :func:`register_sqlite_functions`,
+ this returns a string, a JSON-encoded list of ints.
+
+ """
+ nums = []
+ for byte_i, byte in enumerate(bytes_to_ints(numbits)):
+ for bit_i in range(8):
+ if (byte & (1 << bit_i)):
+ nums.append(byte_i * 8 + bit_i)
+ return nums
+
+
+@contract(numbits1='blob', numbits2='blob', returns='blob')
+def numbits_union(numbits1, numbits2):
+ """Compute the union of two numbits.
+
+ Returns:
+ A new numbits, the union of `numbits1` and `numbits2`.
+ """
+ byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
+ return _to_blob(binary_bytes(b1 | b2 for b1, b2 in byte_pairs))
+
+
+@contract(numbits1='blob', numbits2='blob', returns='blob')
+def numbits_intersection(numbits1, numbits2):
+ """Compute the intersection of two numbits.
+
+ Returns:
+ A new numbits, the intersection `numbits1` and `numbits2`.
+ """
+ byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
+ intersection_bytes = binary_bytes(b1 & b2 for b1, b2 in byte_pairs)
+ return _to_blob(intersection_bytes.rstrip(b'\0'))
+
+
+@contract(numbits1='blob', numbits2='blob', returns='bool')
+def numbits_any_intersection(numbits1, numbits2):
+ """Is there any number that appears in both numbits?
+
+ Determine whether two number sets have a non-empty intersection. This is
+ faster than computing the intersection.
+
+ Returns:
+ A bool, True if there is any number in both `numbits1` and `numbits2`.
+ """
+ byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
+ return any(b1 & b2 for b1, b2 in byte_pairs)
+
+
+@contract(num='int', numbits='blob', returns='bool')
+def num_in_numbits(num, numbits):
+ """Does the integer `num` appear in `numbits`?
+
+ Returns:
+ A bool, True if `num` is a member of `numbits`.
+ """
+ nbyte, nbit = divmod(num, 8)
+ if nbyte >= len(numbits):
+ return False
+ return bool(byte_to_int(numbits[nbyte]) & (1 << nbit))
+
+
+def register_sqlite_functions(connection):
+ """
+ Define numbits functions in a SQLite connection.
+
+ This defines these functions for use in SQLite statements:
+
+ * :func:`numbits_union`
+ * :func:`numbits_intersection`
+ * :func:`numbits_any_intersection`
+ * :func:`num_in_numbits`
+ * :func:`numbits_to_nums`
+
+ `connection` is a :class:`sqlite3.Connection <python:sqlite3.Connection>`
+ object. After creating the connection, pass it to this function to
+ register the numbits functions. Then you can use numbits functions in your
+ queries::
+
+ import sqlite3
+ from coverage.numbits import register_sqlite_functions
+
+ conn = sqlite3.connect('example.db')
+ register_sqlite_functions(conn)
+ c = conn.cursor()
+ # Kind of a nonsense query: find all the files and contexts that
+ # executed line 47 in any file:
+ c.execute(
+ "select file_id, context_id from line_bits where num_in_numbits(?, numbits)",
+ (47,)
+ )
+ """
+ connection.create_function("numbits_union", 2, numbits_union)
+ connection.create_function("numbits_intersection", 2, numbits_intersection)
+ connection.create_function("numbits_any_intersection", 2, numbits_any_intersection)
+ connection.create_function("num_in_numbits", 2, num_in_numbits)
+ connection.create_function("numbits_to_nums", 1, lambda b: json.dumps(numbits_to_nums(b)))
diff --git a/contrib/python/coverage/py3/coverage/parser.py b/contrib/python/coverage/py3/coverage/parser.py
new file mode 100644
index 0000000000..258f956039
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/parser.py
@@ -0,0 +1,1276 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Code parsing for coverage.py."""
+
+import ast
+import collections
+import os
+import re
+import token
+import tokenize
+
+from coverage import env
+from coverage.backward import range # pylint: disable=redefined-builtin
+from coverage.backward import bytes_to_ints, string_class
+from coverage.bytecode import code_objects
+from coverage.debug import short_stack
+from coverage.misc import contract, join_regex, new_contract, nice_pair, one_of
+from coverage.misc import NoSource, NotPython, StopEverything
+from coverage.phystokens import compile_unicode, generate_tokens, neuter_encoding_declaration
+
+
+class PythonParser(object):
+ """Parse code to find executable lines, excluded lines, etc.
+
+ This information is all based on static analysis: no code execution is
+ involved.
+
+ """
+ @contract(text='unicode|None')
+ def __init__(self, text=None, filename=None, exclude=None):
+ """
+ Source can be provided as `text`, the text itself, or `filename`, from
+ which the text will be read. Excluded lines are those that match
+ `exclude`, a regex.
+
+ """
+ assert text or filename, "PythonParser needs either text or filename"
+ self.filename = filename or "<code>"
+ self.text = text
+ if not self.text:
+ from coverage.python import get_python_source
+ try:
+ self.text = get_python_source(self.filename)
+ except IOError as err:
+ raise NoSource(
+ "No source for code: '%s': %s" % (self.filename, err)
+ )
+
+ self.exclude = exclude
+
+ # The text lines of the parsed code.
+ self.lines = self.text.split('\n')
+
+ # The normalized line numbers of the statements in the code. Exclusions
+ # are taken into account, and statements are adjusted to their first
+ # lines.
+ self.statements = set()
+
+ # The normalized line numbers of the excluded lines in the code,
+ # adjusted to their first lines.
+ self.excluded = set()
+
+ # The raw_* attributes are only used in this class, and in
+ # lab/parser.py to show how this class is working.
+
+ # The line numbers that start statements, as reported by the line
+ # number table in the bytecode.
+ self.raw_statements = set()
+
+ # The raw line numbers of excluded lines of code, as marked by pragmas.
+ self.raw_excluded = set()
+
+ # The line numbers of class definitions.
+ self.raw_classdefs = set()
+
+ # Function definitions (start, end, name)
+ self._raw_funcdefs = set()
+
+ # The line numbers of docstring lines.
+ self.raw_docstrings = set()
+
+ # Internal detail, used by lab/parser.py.
+ self.show_tokens = False
+
+ # A dict mapping line numbers to lexical statement starts for
+ # multi-line statements.
+ self._multiline = {}
+
+ # Lazily-created ByteParser, arc data, and missing arc descriptions.
+ self._byte_parser = None
+ self._all_arcs = None
+ self._missing_arc_fragments = None
+
+ @property
+ def byte_parser(self):
+ """Create a ByteParser on demand."""
+ if not self._byte_parser:
+ self._byte_parser = ByteParser(self.text, filename=self.filename)
+ return self._byte_parser
+
+ @property
+ def raw_funcdefs(self):
+ return self._raw_funcdefs
+
+ def lines_matching(self, *regexes):
+ """Find the lines matching one of a list of regexes.
+
+ Returns a set of line numbers, the lines that contain a match for one
+ of the regexes in `regexes`. The entire line needn't match, just a
+ part of it.
+
+ """
+ combined = join_regex(regexes)
+ if env.PY2:
+ combined = combined.decode("utf8")
+ regex_c = re.compile(combined)
+ matches = set()
+ for i, ltext in enumerate(self.lines, start=1):
+ if regex_c.search(ltext):
+ matches.add(i)
+ return matches
+
+ def _raw_parse(self):
+ """Parse the source to find the interesting facts about its lines.
+
+ A handful of attributes are updated.
+
+ """
+ # Find lines which match an exclusion pattern.
+ if self.exclude:
+ self.raw_excluded = self.lines_matching(self.exclude)
+
+ # Tokenize, to find excluded suites, to find docstrings, and to find
+ # multi-line statements.
+ indent = 0
+ exclude_indent = 0
+ excluding = False
+ excluding_decorators = False
+ prev_toktype = token.INDENT
+ first_line = None
+ empty = True
+ first_on_line = True
+
+ tokgen = generate_tokens(self.text)
+ for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen:
+ if self.show_tokens: # pragma: debugging
+ print("%10s %5s %-20r %r" % (
+ tokenize.tok_name.get(toktype, toktype),
+ nice_pair((slineno, elineno)), ttext, ltext
+ ))
+ if toktype == token.INDENT:
+ indent += 1
+ elif toktype == token.DEDENT:
+ indent -= 1
+ elif toktype == token.NAME:
+ if ttext == 'class':
+ # Class definitions look like branches in the bytecode, so
+ # we need to exclude them. The simplest way is to note the
+ # lines with the 'class' keyword.
+ self.raw_classdefs.add(slineno)
+ elif toktype == token.OP:
+ if ttext == ':':
+ should_exclude = (elineno in self.raw_excluded) or excluding_decorators
+ if not excluding and should_exclude:
+ # Start excluding a suite. We trigger off of the colon
+ # token so that the #pragma comment will be recognized on
+ # the same line as the colon.
+ self.raw_excluded.add(elineno)
+ exclude_indent = indent
+ excluding = True
+ excluding_decorators = False
+ elif ttext == '@' and first_on_line:
+ # A decorator.
+ if elineno in self.raw_excluded:
+ excluding_decorators = True
+ if excluding_decorators:
+ self.raw_excluded.add(elineno)
+ elif toktype == token.STRING and prev_toktype == token.INDENT:
+ # Strings that are first on an indented line are docstrings.
+ # (a trick from trace.py in the stdlib.) This works for
+ # 99.9999% of cases. For the rest (!) see:
+ # http://stackoverflow.com/questions/1769332/x/1769794#1769794
+ self.raw_docstrings.update(range(slineno, elineno+1))
+ elif toktype == token.NEWLINE:
+ if first_line is not None and elineno != first_line:
+ # We're at the end of a line, and we've ended on a
+ # different line than the first line of the statement,
+ # so record a multi-line range.
+ for l in range(first_line, elineno+1):
+ self._multiline[l] = first_line
+ first_line = None
+ first_on_line = True
+
+ if ttext.strip() and toktype != tokenize.COMMENT:
+ # A non-whitespace token.
+ empty = False
+ if first_line is None:
+ # The token is not whitespace, and is the first in a
+ # statement.
+ first_line = slineno
+ # Check whether to end an excluded suite.
+ if excluding and indent <= exclude_indent:
+ excluding = False
+ if excluding:
+ self.raw_excluded.add(elineno)
+ first_on_line = False
+
+ prev_toktype = toktype
+
+ # Find the starts of the executable statements.
+ if not empty:
+ self.raw_statements.update(self.byte_parser._find_statements())
+
+ # The first line of modules can lie and say 1 always, even if the first
+ # line of code is later. If so, map 1 to the actual first line of the
+ # module.
+ if env.PYBEHAVIOR.module_firstline_1 and self._multiline:
+ self._multiline[1] = min(self.raw_statements)
+
+ def first_line(self, line):
+ """Return the first line number of the statement including `line`."""
+ if line < 0:
+ line = -self._multiline.get(-line, -line)
+ else:
+ line = self._multiline.get(line, line)
+ return line
+
+ def first_lines(self, lines):
+ """Map the line numbers in `lines` to the correct first line of the
+ statement.
+
+ Returns a set of the first lines.
+
+ """
+ return {self.first_line(l) for l in lines}
+
+ def translate_lines(self, lines):
+ """Implement `FileReporter.translate_lines`."""
+ return self.first_lines(lines)
+
+ def translate_arcs(self, arcs):
+ """Implement `FileReporter.translate_arcs`."""
+ return [(self.first_line(a), self.first_line(b)) for (a, b) in arcs]
+
+ def parse_source(self):
+ """Parse source text to find executable lines, excluded lines, etc.
+
+ Sets the .excluded and .statements attributes, normalized to the first
+ line of multi-line statements.
+
+ """
+ try:
+ self._raw_parse()
+ except (tokenize.TokenError, IndentationError) as err:
+ if hasattr(err, "lineno"):
+ lineno = err.lineno # IndentationError
+ else:
+ lineno = err.args[1][0] # TokenError
+ raise NotPython(
+ u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
+ self.filename, err.args[0], lineno
+ )
+ )
+
+ self.excluded = self.first_lines(self.raw_excluded)
+
+ ignore = self.excluded | self.raw_docstrings
+ starts = self.raw_statements - ignore
+ self.statements = self.first_lines(starts) - ignore
+
+ def arcs(self):
+ """Get information about the arcs available in the code.
+
+ Returns a set of line number pairs. Line numbers have been normalized
+ to the first line of multi-line statements.
+
+ """
+ if self._all_arcs is None:
+ self._analyze_ast()
+ return self._all_arcs
+
+ def _analyze_ast(self):
+ """Run the AstArcAnalyzer and save its results.
+
+ `_all_arcs` is the set of arcs in the code.
+
+ """
+ aaa = AstArcAnalyzer(self.text, self.raw_statements, self._multiline)
+ aaa.analyze()
+
+ self._all_arcs = set()
+ for l1, l2 in aaa.arcs:
+ fl1 = self.first_line(l1)
+ fl2 = self.first_line(l2)
+ if fl1 != fl2:
+ self._all_arcs.add((fl1, fl2))
+
+ self._missing_arc_fragments = aaa.missing_arc_fragments
+ self._raw_funcdefs = aaa.funcdefs
+
+ def exit_counts(self):
+ """Get a count of exits from that each line.
+
+ Excluded lines are excluded.
+
+ """
+ exit_counts = collections.defaultdict(int)
+ for l1, l2 in self.arcs():
+ if l1 < 0:
+ # Don't ever report -1 as a line number
+ continue
+ if l1 in self.excluded:
+ # Don't report excluded lines as line numbers.
+ continue
+ if l2 in self.excluded:
+ # Arcs to excluded lines shouldn't count.
+ continue
+ exit_counts[l1] += 1
+
+ # Class definitions have one extra exit, so remove one for each:
+ for l in self.raw_classdefs:
+ # Ensure key is there: class definitions can include excluded lines.
+ if l in exit_counts:
+ exit_counts[l] -= 1
+
+ return exit_counts
+
+ def missing_arc_description(self, start, end, executed_arcs=None):
+ """Provide an English sentence describing a missing arc."""
+ if self._missing_arc_fragments is None:
+ self._analyze_ast()
+
+ actual_start = start
+
+ if (
+ executed_arcs and
+ end < 0 and end == -start and
+ (end, start) not in executed_arcs and
+ (end, start) in self._missing_arc_fragments
+ ):
+ # It's a one-line callable, and we never even started it,
+ # and we have a message about not starting it.
+ start, end = end, start
+
+ fragment_pairs = self._missing_arc_fragments.get((start, end), [(None, None)])
+
+ msgs = []
+ for smsg, emsg in fragment_pairs:
+ if emsg is None:
+ if end < 0:
+ # Hmm, maybe we have a one-line callable, let's check.
+ if (-end, end) in self._missing_arc_fragments:
+ return self.missing_arc_description(-end, end)
+ emsg = "didn't jump to the function exit"
+ else:
+ emsg = "didn't jump to line {lineno}"
+ emsg = emsg.format(lineno=end)
+
+ msg = "line {start} {emsg}".format(start=actual_start, emsg=emsg)
+ if smsg is not None:
+ msg += ", because {smsg}".format(smsg=smsg.format(lineno=actual_start))
+
+ msgs.append(msg)
+
+ return " or ".join(msgs)
+
+
+class ByteParser(object):
+ """Parse bytecode to understand the structure of code."""
+
+ @contract(text='unicode')
+ def __init__(self, text, code=None, filename=None):
+ self.text = text
+ if code:
+ self.code = code
+ else:
+ try:
+ self.code = compile_unicode(text, filename, "exec")
+ except SyntaxError as synerr:
+ raise NotPython(
+ u"Couldn't parse '%s' as Python source: '%s' at line %d" % (
+ filename, synerr.msg, synerr.lineno
+ )
+ )
+
+ # Alternative Python implementations don't always provide all the
+ # attributes on code objects that we need to do the analysis.
+ for attr in ['co_lnotab', 'co_firstlineno']:
+ if not hasattr(self.code, attr):
+ raise StopEverything( # pragma: only jython
+ "This implementation of Python doesn't support code analysis.\n"
+ "Run coverage.py under another Python for this command."
+ )
+
+ def child_parsers(self):
+ """Iterate over all the code objects nested within this one.
+
+ The iteration includes `self` as its first value.
+
+ """
+ return (ByteParser(self.text, code=c) for c in code_objects(self.code))
+
+ def _line_numbers(self):
+ """Yield the line numbers possible in this code object.
+
+ Uses co_lnotab described in Python/compile.c to find the
+ line numbers. Produces a sequence: l0, l1, ...
+ """
+ if hasattr(self.code, "co_lines"):
+ for _, _, line in self.code.co_lines():
+ if line is not None:
+ yield line
+ else:
+ # Adapted from dis.py in the standard library.
+ byte_increments = bytes_to_ints(self.code.co_lnotab[0::2])
+ line_increments = bytes_to_ints(self.code.co_lnotab[1::2])
+
+ last_line_num = None
+ line_num = self.code.co_firstlineno
+ byte_num = 0
+ for byte_incr, line_incr in zip(byte_increments, line_increments):
+ if byte_incr:
+ if line_num != last_line_num:
+ yield line_num
+ last_line_num = line_num
+ byte_num += byte_incr
+ if env.PYBEHAVIOR.negative_lnotab and line_incr >= 0x80:
+ line_incr -= 0x100
+ line_num += line_incr
+ if line_num != last_line_num:
+ yield line_num
+
+ def _find_statements(self):
+ """Find the statements in `self.code`.
+
+ Produce a sequence of line numbers that start statements. Recurses
+ into all code objects reachable from `self.code`.
+
+ """
+ for bp in self.child_parsers():
+ # Get all of the lineno information from this code.
+ for l in bp._line_numbers():
+ yield l
+
+
+#
+# AST analysis
+#
+
+class LoopBlock(object):
+ """A block on the block stack representing a `for` or `while` loop."""
+ @contract(start=int)
+ def __init__(self, start):
+ # The line number where the loop starts.
+ self.start = start
+ # A set of ArcStarts, the arcs from break statements exiting this loop.
+ self.break_exits = set()
+
+
+class FunctionBlock(object):
+ """A block on the block stack representing a function definition."""
+ @contract(start=int, name=str)
+ def __init__(self, start, name):
+ # The line number where the function starts.
+ self.start = start
+ # The name of the function.
+ self.name = name
+
+
+class TryBlock(object):
+ """A block on the block stack representing a `try` block."""
+ @contract(handler_start='int|None', final_start='int|None')
+ def __init__(self, handler_start, final_start):
+ # The line number of the first "except" handler, if any.
+ self.handler_start = handler_start
+ # The line number of the "finally:" clause, if any.
+ self.final_start = final_start
+
+ # The ArcStarts for breaks/continues/returns/raises inside the "try:"
+ # that need to route through the "finally:" clause.
+ self.break_from = set()
+ self.continue_from = set()
+ self.return_from = set()
+ self.raise_from = set()
+
+
+class ArcStart(collections.namedtuple("Arc", "lineno, cause")):
+ """The information needed to start an arc.
+
+ `lineno` is the line number the arc starts from.
+
+ `cause` is an English text fragment used as the `startmsg` for
+ AstArcAnalyzer.missing_arc_fragments. It will be used to describe why an
+ arc wasn't executed, so should fit well into a sentence of the form,
+ "Line 17 didn't run because {cause}." The fragment can include "{lineno}"
+ to have `lineno` interpolated into it.
+
+ """
+ def __new__(cls, lineno, cause=None):
+ return super(ArcStart, cls).__new__(cls, lineno, cause)
+
+
+# Define contract words that PyContract doesn't have.
+# ArcStarts is for a list or set of ArcStart's.
+new_contract('ArcStarts', lambda seq: all(isinstance(x, ArcStart) for x in seq))
+
+
+# Turn on AST dumps with an environment variable.
+# $set_env.py: COVERAGE_AST_DUMP - Dump the AST nodes when parsing code.
+AST_DUMP = bool(int(os.environ.get("COVERAGE_AST_DUMP", 0)))
+
+class NodeList(object):
+ """A synthetic fictitious node, containing a sequence of nodes.
+
+ This is used when collapsing optimized if-statements, to represent the
+ unconditional execution of one of the clauses.
+
+ """
+ def __init__(self, body):
+ self.body = body
+ self.lineno = body[0].lineno
+
+
+# TODO: some add_arcs methods here don't add arcs, they return them. Rename them.
+# TODO: the cause messages have too many commas.
+# TODO: Shouldn't the cause messages join with "and" instead of "or"?
+
+class AstArcAnalyzer(object):
+ """Analyze source text with an AST to find executable code paths."""
+
+ @contract(text='unicode', statements=set)
+ def __init__(self, text, statements, multiline):
+ self.root_node = ast.parse(neuter_encoding_declaration(text))
+ # TODO: I think this is happening in too many places.
+ self.statements = {multiline.get(l, l) for l in statements}
+ self.multiline = multiline
+
+ if AST_DUMP: # pragma: debugging
+ # Dump the AST so that failing tests have helpful output.
+ print("Statements: {}".format(self.statements))
+ print("Multiline map: {}".format(self.multiline))
+ ast_dump(self.root_node)
+
+ self.arcs = set()
+
+ # A map from arc pairs to a list of pairs of sentence fragments:
+ # { (start, end): [(startmsg, endmsg), ...], }
+ #
+ # For an arc from line 17, they should be usable like:
+ # "Line 17 {endmsg}, because {startmsg}"
+ self.missing_arc_fragments = collections.defaultdict(list)
+ self.block_stack = []
+ self.funcdefs = set()
+
+ # $set_env.py: COVERAGE_TRACK_ARCS - Trace every arc added while parsing code.
+ self.debug = bool(int(os.environ.get("COVERAGE_TRACK_ARCS", 0)))
+
+ def analyze(self):
+ """Examine the AST tree from `root_node` to determine possible arcs.
+
+ This sets the `arcs` attribute to be a set of (from, to) line number
+ pairs.
+
+ """
+ for node in ast.walk(self.root_node):
+ node_name = node.__class__.__name__
+ code_object_handler = getattr(self, "_code_object__" + node_name, None)
+ if code_object_handler is not None:
+ code_object_handler(node)
+
+ @contract(start=int, end=int)
+ def add_arc(self, start, end, smsg=None, emsg=None):
+ """Add an arc, including message fragments to use if it is missing."""
+ if self.debug: # pragma: debugging
+ print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg))
+ print(short_stack(limit=6))
+ self.arcs.add((start, end))
+
+ if smsg is not None or emsg is not None:
+ self.missing_arc_fragments[(start, end)].append((smsg, emsg))
+
+ def nearest_blocks(self):
+ """Yield the blocks in nearest-to-farthest order."""
+ return reversed(self.block_stack)
+
+ @contract(returns=int)
+ def line_for_node(self, node):
+ """What is the right line number to use for this node?
+
+ This dispatches to _line__Node functions where needed.
+
+ """
+ node_name = node.__class__.__name__
+ handler = getattr(self, "_line__" + node_name, None)
+ if handler is not None:
+ return handler(node)
+ else:
+ return node.lineno
+
+ def _line_decorated(self, node):
+ """Compute first line number for things that can be decorated (classes and functions)."""
+ lineno = node.lineno
+ if env.PYBEHAVIOR.trace_decorated_def:
+ if node.decorator_list:
+ lineno = node.decorator_list[0].lineno
+ return lineno
+
+ def _line__Assign(self, node):
+ return self.line_for_node(node.value)
+
+ _line__ClassDef = _line_decorated
+
+ def _line__Dict(self, node):
+ # Python 3.5 changed how dict literals are made.
+ if env.PYVERSION >= (3, 5) and node.keys:
+ if node.keys[0] is not None:
+ return node.keys[0].lineno
+ else:
+ # Unpacked dict literals `{**{'a':1}}` have None as the key,
+ # use the value in that case.
+ return node.values[0].lineno
+ else:
+ return node.lineno
+
+ _line__FunctionDef = _line_decorated
+ _line__AsyncFunctionDef = _line_decorated
+
+ def _line__List(self, node):
+ if node.elts:
+ return self.line_for_node(node.elts[0])
+ else:
+ return node.lineno
+
+ def _line__Module(self, node):
+ if env.PYBEHAVIOR.module_firstline_1:
+ return 1
+ elif node.body:
+ return self.line_for_node(node.body[0])
+ else:
+ # Empty modules have no line number, they always start at 1.
+ return 1
+
+ # The node types that just flow to the next node with no complications.
+ OK_TO_DEFAULT = {
+ "Assign", "Assert", "AugAssign", "Delete", "Exec", "Expr", "Global",
+ "Import", "ImportFrom", "Nonlocal", "Pass", "Print",
+ }
+
+ @contract(returns='ArcStarts')
+ def add_arcs(self, node):
+ """Add the arcs for `node`.
+
+ Return a set of ArcStarts, exits from this node to the next. Because a
+ node represents an entire sub-tree (including its children), the exits
+ from a node can be arbitrarily complex::
+
+ if something(1):
+ if other(2):
+ doit(3)
+ else:
+ doit(5)
+
+ There are two exits from line 1: they start at line 3 and line 5.
+
+ """
+ node_name = node.__class__.__name__
+ handler = getattr(self, "_handle__" + node_name, None)
+ if handler is not None:
+ return handler(node)
+ else:
+ # No handler: either it's something that's ok to default (a simple
+ # statement), or it's something we overlooked. Change this 0 to 1
+ # to see if it's overlooked.
+ if 0:
+ if node_name not in self.OK_TO_DEFAULT:
+ print("*** Unhandled: {}".format(node))
+
+ # Default for simple statements: one exit from this node.
+ return {ArcStart(self.line_for_node(node))}
+
+ @one_of("from_start, prev_starts")
+ @contract(returns='ArcStarts')
+ def add_body_arcs(self, body, from_start=None, prev_starts=None):
+ """Add arcs for the body of a compound statement.
+
+ `body` is the body node. `from_start` is a single `ArcStart` that can
+ be the previous line in flow before this body. `prev_starts` is a set
+ of ArcStarts that can be the previous line. Only one of them should be
+ given.
+
+ Returns a set of ArcStarts, the exits from this body.
+
+ """
+ if prev_starts is None:
+ prev_starts = {from_start}
+ for body_node in body:
+ lineno = self.line_for_node(body_node)
+ first_line = self.multiline.get(lineno, lineno)
+ if first_line not in self.statements:
+ body_node = self.find_non_missing_node(body_node)
+ if body_node is None:
+ continue
+ lineno = self.line_for_node(body_node)
+ for prev_start in prev_starts:
+ self.add_arc(prev_start.lineno, lineno, prev_start.cause)
+ prev_starts = self.add_arcs(body_node)
+ return prev_starts
+
+ def find_non_missing_node(self, node):
+ """Search `node` looking for a child that has not been optimized away.
+
+ This might return the node you started with, or it will work recursively
+ to find a child node in self.statements.
+
+ Returns a node, or None if none of the node remains.
+
+ """
+ # This repeats work just done in add_body_arcs, but this duplication
+ # means we can avoid a function call in the 99.9999% case of not
+ # optimizing away statements.
+ lineno = self.line_for_node(node)
+ first_line = self.multiline.get(lineno, lineno)
+ if first_line in self.statements:
+ return node
+
+ missing_fn = getattr(self, "_missing__" + node.__class__.__name__, None)
+ if missing_fn:
+ node = missing_fn(node)
+ else:
+ node = None
+ return node
+
+ # Missing nodes: _missing__*
+ #
+ # Entire statements can be optimized away by Python. They will appear in
+ # the AST, but not the bytecode. These functions are called (by
+ # find_non_missing_node) to find a node to use instead of the missing
+ # node. They can return None if the node should truly be gone.
+
+ def _missing__If(self, node):
+ # If the if-node is missing, then one of its children might still be
+ # here, but not both. So return the first of the two that isn't missing.
+ # Use a NodeList to hold the clauses as a single node.
+ non_missing = self.find_non_missing_node(NodeList(node.body))
+ if non_missing:
+ return non_missing
+ if node.orelse:
+ return self.find_non_missing_node(NodeList(node.orelse))
+ return None
+
+ def _missing__NodeList(self, node):
+ # A NodeList might be a mixture of missing and present nodes. Find the
+ # ones that are present.
+ non_missing_children = []
+ for child in node.body:
+ child = self.find_non_missing_node(child)
+ if child is not None:
+ non_missing_children.append(child)
+
+ # Return the simplest representation of the present children.
+ if not non_missing_children:
+ return None
+ if len(non_missing_children) == 1:
+ return non_missing_children[0]
+ return NodeList(non_missing_children)
+
+ def _missing__While(self, node):
+ body_nodes = self.find_non_missing_node(NodeList(node.body))
+ if not body_nodes:
+ return None
+ # Make a synthetic While-true node.
+ new_while = ast.While()
+ new_while.lineno = body_nodes.lineno
+ new_while.test = ast.Name()
+ new_while.test.lineno = body_nodes.lineno
+ new_while.test.id = "True"
+ new_while.body = body_nodes.body
+ new_while.orelse = None
+ return new_while
+
+ def is_constant_expr(self, node):
+ """Is this a compile-time constant?"""
+ node_name = node.__class__.__name__
+ if node_name in ["Constant", "NameConstant", "Num"]:
+ return "Num"
+ elif node_name == "Name":
+ if node.id in ["True", "False", "None", "__debug__"]:
+ return "Name"
+ return None
+
+ # In the fullness of time, these might be good tests to write:
+ # while EXPR:
+ # while False:
+ # listcomps hidden deep in other expressions
+ # listcomps hidden in lists: x = [[i for i in range(10)]]
+ # nested function definitions
+
+
+ # Exit processing: process_*_exits
+ #
+ # These functions process the four kinds of jump exits: break, continue,
+ # raise, and return. To figure out where an exit goes, we have to look at
+ # the block stack context. For example, a break will jump to the nearest
+ # enclosing loop block, or the nearest enclosing finally block, whichever
+ # is nearer.
+
+ @contract(exits='ArcStarts')
+ def process_break_exits(self, exits):
+ """Add arcs due to jumps from `exits` being breaks."""
+ for block in self.nearest_blocks():
+ if isinstance(block, LoopBlock):
+ block.break_exits.update(exits)
+ break
+ elif isinstance(block, TryBlock) and block.final_start is not None:
+ block.break_from.update(exits)
+ break
+
+ @contract(exits='ArcStarts')
+ def process_continue_exits(self, exits):
+ """Add arcs due to jumps from `exits` being continues."""
+ for block in self.nearest_blocks():
+ if isinstance(block, LoopBlock):
+ for xit in exits:
+ self.add_arc(xit.lineno, block.start, xit.cause)
+ break
+ elif isinstance(block, TryBlock) and block.final_start is not None:
+ block.continue_from.update(exits)
+ break
+
+ @contract(exits='ArcStarts')
+ def process_raise_exits(self, exits):
+ """Add arcs due to jumps from `exits` being raises."""
+ for block in self.nearest_blocks():
+ if isinstance(block, TryBlock):
+ if block.handler_start is not None:
+ for xit in exits:
+ self.add_arc(xit.lineno, block.handler_start, xit.cause)
+ break
+ elif block.final_start is not None:
+ block.raise_from.update(exits)
+ break
+ elif isinstance(block, FunctionBlock):
+ for xit in exits:
+ self.add_arc(
+ xit.lineno, -block.start, xit.cause,
+ "didn't except from function {!r}".format(block.name),
+ )
+ break
+
+ @contract(exits='ArcStarts')
+ def process_return_exits(self, exits):
+ """Add arcs due to jumps from `exits` being returns."""
+ for block in self.nearest_blocks():
+ if isinstance(block, TryBlock) and block.final_start is not None:
+ block.return_from.update(exits)
+ break
+ elif isinstance(block, FunctionBlock):
+ for xit in exits:
+ self.add_arc(
+ xit.lineno, -block.start, xit.cause,
+ "didn't return from function {!r}".format(block.name),
+ )
+ break
+
+
+ # Handlers: _handle__*
+ #
+ # Each handler deals with a specific AST node type, dispatched from
+ # add_arcs. Handlers return the set of exits from that node, and can
+ # also call self.add_arc to record arcs they find. These functions mirror
+ # the Python semantics of each syntactic construct. See the docstring
+ # for add_arcs to understand the concept of exits from a node.
+
+ @contract(returns='ArcStarts')
+ def _handle__Break(self, node):
+ here = self.line_for_node(node)
+ break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed")
+ self.process_break_exits([break_start])
+ return set()
+
+ @contract(returns='ArcStarts')
+ def _handle_decorated(self, node):
+ """Add arcs for things that can be decorated (classes and functions)."""
+ main_line = last = node.lineno
+ if node.decorator_list:
+ if env.PYBEHAVIOR.trace_decorated_def:
+ last = None
+ for dec_node in node.decorator_list:
+ dec_start = self.line_for_node(dec_node)
+ if last is not None and dec_start != last:
+ self.add_arc(last, dec_start)
+ last = dec_start
+ if env.PYBEHAVIOR.trace_decorated_def:
+ self.add_arc(last, main_line)
+ last = main_line
+ # The definition line may have been missed, but we should have it
+ # in `self.statements`. For some constructs, `line_for_node` is
+ # not what we'd think of as the first line in the statement, so map
+ # it to the first one.
+ if node.body:
+ body_start = self.line_for_node(node.body[0])
+ body_start = self.multiline.get(body_start, body_start)
+ for lineno in range(last+1, body_start):
+ if lineno in self.statements:
+ self.add_arc(last, lineno)
+ last = lineno
+ # The body is handled in collect_arcs.
+ return {ArcStart(last)}
+
+ _handle__ClassDef = _handle_decorated
+
+ @contract(returns='ArcStarts')
+ def _handle__Continue(self, node):
+ here = self.line_for_node(node)
+ continue_start = ArcStart(here, cause="the continue on line {lineno} wasn't executed")
+ self.process_continue_exits([continue_start])
+ return set()
+
+ @contract(returns='ArcStarts')
+ def _handle__For(self, node):
+ start = self.line_for_node(node.iter)
+ self.block_stack.append(LoopBlock(start=start))
+ from_start = ArcStart(start, cause="the loop on line {lineno} never started")
+ exits = self.add_body_arcs(node.body, from_start=from_start)
+ # Any exit from the body will go back to the top of the loop.
+ for xit in exits:
+ self.add_arc(xit.lineno, start, xit.cause)
+ my_block = self.block_stack.pop()
+ exits = my_block.break_exits
+ from_start = ArcStart(start, cause="the loop on line {lineno} didn't complete")
+ if node.orelse:
+ else_exits = self.add_body_arcs(node.orelse, from_start=from_start)
+ exits |= else_exits
+ else:
+ # No else clause: exit from the for line.
+ exits.add(from_start)
+ return exits
+
+ _handle__AsyncFor = _handle__For
+
+ _handle__FunctionDef = _handle_decorated
+ _handle__AsyncFunctionDef = _handle_decorated
+
+ @contract(returns='ArcStarts')
+ def _handle__If(self, node):
+ start = self.line_for_node(node.test)
+ from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
+ exits = self.add_body_arcs(node.body, from_start=from_start)
+ from_start = ArcStart(start, cause="the condition on line {lineno} was never false")
+ exits |= self.add_body_arcs(node.orelse, from_start=from_start)
+ return exits
+
+ @contract(returns='ArcStarts')
+ def _handle__NodeList(self, node):
+ start = self.line_for_node(node)
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
+ return exits
+
+ @contract(returns='ArcStarts')
+ def _handle__Raise(self, node):
+ here = self.line_for_node(node)
+ raise_start = ArcStart(here, cause="the raise on line {lineno} wasn't executed")
+ self.process_raise_exits([raise_start])
+ # `raise` statement jumps away, no exits from here.
+ return set()
+
+ @contract(returns='ArcStarts')
+ def _handle__Return(self, node):
+ here = self.line_for_node(node)
+ return_start = ArcStart(here, cause="the return on line {lineno} wasn't executed")
+ self.process_return_exits([return_start])
+ # `return` statement jumps away, no exits from here.
+ return set()
+
+ @contract(returns='ArcStarts')
+ def _handle__Try(self, node):
+ if node.handlers:
+ handler_start = self.line_for_node(node.handlers[0])
+ else:
+ handler_start = None
+
+ if node.finalbody:
+ final_start = self.line_for_node(node.finalbody[0])
+ else:
+ final_start = None
+
+ try_block = TryBlock(handler_start, final_start)
+ self.block_stack.append(try_block)
+
+ start = self.line_for_node(node)
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
+
+ # We're done with the `try` body, so this block no longer handles
+ # exceptions. We keep the block so the `finally` clause can pick up
+ # flows from the handlers and `else` clause.
+ if node.finalbody:
+ try_block.handler_start = None
+ if node.handlers:
+ # If there are `except` clauses, then raises in the try body
+ # will already jump to them. Start this set over for raises in
+ # `except` and `else`.
+ try_block.raise_from = set()
+ else:
+ self.block_stack.pop()
+
+ handler_exits = set()
+
+ if node.handlers:
+ last_handler_start = None
+ for handler_node in node.handlers:
+ handler_start = self.line_for_node(handler_node)
+ if last_handler_start is not None:
+ self.add_arc(last_handler_start, handler_start)
+ last_handler_start = handler_start
+ from_cause = "the exception caught by line {lineno} didn't happen"
+ from_start = ArcStart(handler_start, cause=from_cause)
+ handler_exits |= self.add_body_arcs(handler_node.body, from_start=from_start)
+
+ if node.orelse:
+ exits = self.add_body_arcs(node.orelse, prev_starts=exits)
+
+ exits |= handler_exits
+
+ if node.finalbody:
+ self.block_stack.pop()
+ final_from = ( # You can get to the `finally` clause from:
+ exits | # the exits of the body or `else` clause,
+ try_block.break_from | # or a `break`,
+ try_block.continue_from | # or a `continue`,
+ try_block.raise_from | # or a `raise`,
+ try_block.return_from # or a `return`.
+ )
+
+ final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from)
+
+ if try_block.break_from:
+ if env.PYBEHAVIOR.finally_jumps_back:
+ for break_line in try_block.break_from:
+ lineno = break_line.lineno
+ cause = break_line.cause.format(lineno=lineno)
+ for final_exit in final_exits:
+ self.add_arc(final_exit.lineno, lineno, cause)
+ breaks = try_block.break_from
+ else:
+ breaks = self._combine_finally_starts(try_block.break_from, final_exits)
+ self.process_break_exits(breaks)
+
+ if try_block.continue_from:
+ if env.PYBEHAVIOR.finally_jumps_back:
+ for continue_line in try_block.continue_from:
+ lineno = continue_line.lineno
+ cause = continue_line.cause.format(lineno=lineno)
+ for final_exit in final_exits:
+ self.add_arc(final_exit.lineno, lineno, cause)
+ continues = try_block.continue_from
+ else:
+ continues = self._combine_finally_starts(try_block.continue_from, final_exits)
+ self.process_continue_exits(continues)
+
+ if try_block.raise_from:
+ self.process_raise_exits(
+ self._combine_finally_starts(try_block.raise_from, final_exits)
+ )
+
+ if try_block.return_from:
+ if env.PYBEHAVIOR.finally_jumps_back:
+ for return_line in try_block.return_from:
+ lineno = return_line.lineno
+ cause = return_line.cause.format(lineno=lineno)
+ for final_exit in final_exits:
+ self.add_arc(final_exit.lineno, lineno, cause)
+ returns = try_block.return_from
+ else:
+ returns = self._combine_finally_starts(try_block.return_from, final_exits)
+ self.process_return_exits(returns)
+
+ if exits:
+ # The finally clause's exits are only exits for the try block
+ # as a whole if the try block had some exits to begin with.
+ exits = final_exits
+
+ return exits
+
+ @contract(starts='ArcStarts', exits='ArcStarts', returns='ArcStarts')
+ def _combine_finally_starts(self, starts, exits):
+ """Helper for building the cause of `finally` branches.
+
+ "finally" clauses might not execute their exits, and the causes could
+ be due to a failure to execute any of the exits in the try block. So
+ we use the causes from `starts` as the causes for `exits`.
+ """
+ causes = []
+ for start in sorted(starts):
+ if start.cause is not None:
+ causes.append(start.cause.format(lineno=start.lineno))
+ cause = " or ".join(causes)
+ exits = {ArcStart(xit.lineno, cause) for xit in exits}
+ return exits
+
+ @contract(returns='ArcStarts')
+ def _handle__TryExcept(self, node):
+ # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
+ # TryExcept, it means there was no finally, so fake it, and treat as
+ # a general Try node.
+ node.finalbody = []
+ return self._handle__Try(node)
+
+ @contract(returns='ArcStarts')
+ def _handle__TryFinally(self, node):
+ # Python 2.7 uses separate TryExcept and TryFinally nodes. If we get
+ # TryFinally, see if there's a TryExcept nested inside. If so, merge
+ # them. Otherwise, fake fields to complete a Try node.
+ node.handlers = []
+ node.orelse = []
+
+ first = node.body[0]
+ if first.__class__.__name__ == "TryExcept" and node.lineno == first.lineno:
+ assert len(node.body) == 1
+ node.body = first.body
+ node.handlers = first.handlers
+ node.orelse = first.orelse
+
+ return self._handle__Try(node)
+
+ @contract(returns='ArcStarts')
+ def _handle__While(self, node):
+ start = to_top = self.line_for_node(node.test)
+ constant_test = self.is_constant_expr(node.test)
+ top_is_body0 = False
+ if constant_test and (env.PY3 or constant_test == "Num"):
+ top_is_body0 = True
+ if env.PYBEHAVIOR.keep_constant_test:
+ top_is_body0 = False
+ if top_is_body0:
+ to_top = self.line_for_node(node.body[0])
+ self.block_stack.append(LoopBlock(start=to_top))
+ from_start = ArcStart(start, cause="the condition on line {lineno} was never true")
+ exits = self.add_body_arcs(node.body, from_start=from_start)
+ for xit in exits:
+ self.add_arc(xit.lineno, to_top, xit.cause)
+ exits = set()
+ my_block = self.block_stack.pop()
+ exits.update(my_block.break_exits)
+ from_start = ArcStart(start, cause="the condition on line {lineno} was never false")
+ if node.orelse:
+ else_exits = self.add_body_arcs(node.orelse, from_start=from_start)
+ exits |= else_exits
+ else:
+ # No `else` clause: you can exit from the start.
+ if not constant_test:
+ exits.add(from_start)
+ return exits
+
+ @contract(returns='ArcStarts')
+ def _handle__With(self, node):
+ start = self.line_for_node(node)
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
+ return exits
+
+ _handle__AsyncWith = _handle__With
+
+ def _code_object__Module(self, node):
+ start = self.line_for_node(node)
+ if node.body:
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(-start))
+ for xit in exits:
+ self.add_arc(xit.lineno, -start, xit.cause, "didn't exit the module")
+ else:
+ # Empty module.
+ self.add_arc(-start, start)
+ self.add_arc(start, -start)
+
+ def _process_function_def(self, start, node):
+ self.funcdefs.add((start, node.body[-1].lineno, node.name))
+
+ def _code_object__FunctionDef(self, node):
+ start = self.line_for_node(node)
+ self.block_stack.append(FunctionBlock(start=start, name=node.name))
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(-start))
+ self.process_return_exits(exits)
+ self._process_function_def(start, node)
+ self.block_stack.pop()
+
+ _code_object__AsyncFunctionDef = _code_object__FunctionDef
+
+ def _code_object__ClassDef(self, node):
+ start = self.line_for_node(node)
+ self.add_arc(-start, start)
+ exits = self.add_body_arcs(node.body, from_start=ArcStart(start))
+ for xit in exits:
+ self.add_arc(
+ xit.lineno, -start, xit.cause,
+ "didn't exit the body of class {!r}".format(node.name),
+ )
+
+ def _make_oneline_code_method(noun): # pylint: disable=no-self-argument
+ """A function to make methods for online callable _code_object__ methods."""
+ def _code_object__oneline_callable(self, node):
+ start = self.line_for_node(node)
+ self.add_arc(-start, start, None, "didn't run the {} on line {}".format(noun, start))
+ self.add_arc(
+ start, -start, None,
+ "didn't finish the {} on line {}".format(noun, start),
+ )
+ return _code_object__oneline_callable
+
+ _code_object__Lambda = _make_oneline_code_method("lambda")
+ _code_object__GeneratorExp = _make_oneline_code_method("generator expression")
+ _code_object__DictComp = _make_oneline_code_method("dictionary comprehension")
+ _code_object__SetComp = _make_oneline_code_method("set comprehension")
+ if env.PY3:
+ _code_object__ListComp = _make_oneline_code_method("list comprehension")
+
+
+if AST_DUMP: # pragma: debugging
+ # Code only used when dumping the AST for debugging.
+
+ SKIP_DUMP_FIELDS = ["ctx"]
+
+ def _is_simple_value(value):
+ """Is `value` simple enough to be displayed on a single line?"""
+ return (
+ value in [None, [], (), {}, set()] or
+ isinstance(value, (string_class, int, float))
+ )
+
+ def ast_dump(node, depth=0):
+ """Dump the AST for `node`.
+
+ This recursively walks the AST, printing a readable version.
+
+ """
+ indent = " " * depth
+ if not isinstance(node, ast.AST):
+ print("{}<{} {!r}>".format(indent, node.__class__.__name__, node))
+ return
+
+ lineno = getattr(node, "lineno", None)
+ if lineno is not None:
+ linemark = " @ {}".format(node.lineno)
+ else:
+ linemark = ""
+ head = "{}<{}{}".format(indent, node.__class__.__name__, linemark)
+
+ named_fields = [
+ (name, value)
+ for name, value in ast.iter_fields(node)
+ if name not in SKIP_DUMP_FIELDS
+ ]
+ if not named_fields:
+ print("{}>".format(head))
+ elif len(named_fields) == 1 and _is_simple_value(named_fields[0][1]):
+ field_name, value = named_fields[0]
+ print("{} {}: {!r}>".format(head, field_name, value))
+ else:
+ print(head)
+ if 0:
+ print("{}# mro: {}".format(
+ indent, ", ".join(c.__name__ for c in node.__class__.__mro__[1:]),
+ ))
+ next_indent = indent + " "
+ for field_name, value in named_fields:
+ prefix = "{}{}:".format(next_indent, field_name)
+ if _is_simple_value(value):
+ print("{} {!r}".format(prefix, value))
+ elif isinstance(value, list):
+ print("{} [".format(prefix))
+ for n in value:
+ ast_dump(n, depth + 8)
+ print("{}]".format(next_indent))
+ else:
+ print(prefix)
+ ast_dump(value, depth + 8)
+
+ print("{}>".format(indent))
diff --git a/contrib/python/coverage/py3/coverage/phystokens.py b/contrib/python/coverage/py3/coverage/phystokens.py
new file mode 100644
index 0000000000..54378b3bc8
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/phystokens.py
@@ -0,0 +1,297 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Better tokenizing for coverage.py."""
+
+import codecs
+import keyword
+import re
+import sys
+import token
+import tokenize
+
+from coverage import env
+from coverage.backward import iternext, unicode_class
+from coverage.misc import contract
+
+
+def phys_tokens(toks):
+ """Return all physical tokens, even line continuations.
+
+ tokenize.generate_tokens() doesn't return a token for the backslash that
+ continues lines. This wrapper provides those tokens so that we can
+ re-create a faithful representation of the original source.
+
+ Returns the same values as generate_tokens()
+
+ """
+ last_line = None
+ last_lineno = -1
+ last_ttext = None
+ for ttype, ttext, (slineno, scol), (elineno, ecol), ltext in toks:
+ if last_lineno != elineno:
+ if last_line and last_line.endswith("\\\n"):
+ # We are at the beginning of a new line, and the last line
+ # ended with a backslash. We probably have to inject a
+ # backslash token into the stream. Unfortunately, there's more
+ # to figure out. This code::
+ #
+ # usage = """\
+ # HEY THERE
+ # """
+ #
+ # triggers this condition, but the token text is::
+ #
+ # '"""\\\nHEY THERE\n"""'
+ #
+ # so we need to figure out if the backslash is already in the
+ # string token or not.
+ inject_backslash = True
+ if last_ttext.endswith("\\"):
+ inject_backslash = False
+ elif ttype == token.STRING:
+ if "\n" in ttext and ttext.split('\n', 1)[0][-1] == '\\':
+ # It's a multi-line string and the first line ends with
+ # a backslash, so we don't need to inject another.
+ inject_backslash = False
+ if inject_backslash:
+ # Figure out what column the backslash is in.
+ ccol = len(last_line.split("\n")[-2]) - 1
+ # Yield the token, with a fake token type.
+ yield (
+ 99999, "\\\n",
+ (slineno, ccol), (slineno, ccol+2),
+ last_line
+ )
+ last_line = ltext
+ if ttype not in (tokenize.NEWLINE, tokenize.NL):
+ last_ttext = ttext
+ yield ttype, ttext, (slineno, scol), (elineno, ecol), ltext
+ last_lineno = elineno
+
+
+@contract(source='unicode')
+def source_token_lines(source):
+ """Generate a series of lines, one for each line in `source`.
+
+ Each line is a list of pairs, each pair is a token::
+
+ [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
+
+ Each pair has a token class, and the token text.
+
+ If you concatenate all the token texts, and then join them with newlines,
+ you should have your original `source` back, with two differences:
+ trailing whitespace is not preserved, and a final line with no newline
+ is indistinguishable from a final line with a newline.
+
+ """
+
+ ws_tokens = {token.INDENT, token.DEDENT, token.NEWLINE, tokenize.NL}
+ line = []
+ col = 0
+
+ source = source.expandtabs(8).replace('\r\n', '\n')
+ tokgen = generate_tokens(source)
+
+ for ttype, ttext, (_, scol), (_, ecol), _ in phys_tokens(tokgen):
+ mark_start = True
+ for part in re.split('(\n)', ttext):
+ if part == '\n':
+ yield line
+ line = []
+ col = 0
+ mark_end = False
+ elif part == '':
+ mark_end = False
+ elif ttype in ws_tokens:
+ mark_end = False
+ else:
+ if mark_start and scol > col:
+ line.append(("ws", u" " * (scol - col)))
+ mark_start = False
+ tok_class = tokenize.tok_name.get(ttype, 'xx').lower()[:3]
+ if ttype == token.NAME and keyword.iskeyword(ttext):
+ tok_class = "key"
+ line.append((tok_class, part))
+ mark_end = True
+ scol = 0
+ if mark_end:
+ col = ecol
+
+ if line:
+ yield line
+
+
+class CachedTokenizer(object):
+ """A one-element cache around tokenize.generate_tokens.
+
+ When reporting, coverage.py tokenizes files twice, once to find the
+ structure of the file, and once to syntax-color it. Tokenizing is
+ expensive, and easily cached.
+
+ This is a one-element cache so that our twice-in-a-row tokenizing doesn't
+ actually tokenize twice.
+
+ """
+ def __init__(self):
+ self.last_text = None
+ self.last_tokens = None
+
+ @contract(text='unicode')
+ def generate_tokens(self, text):
+ """A stand-in for `tokenize.generate_tokens`."""
+ if text != self.last_text:
+ self.last_text = text
+ readline = iternext(text.splitlines(True))
+ self.last_tokens = list(tokenize.generate_tokens(readline))
+ return self.last_tokens
+
+# Create our generate_tokens cache as a callable replacement function.
+generate_tokens = CachedTokenizer().generate_tokens
+
+
+COOKIE_RE = re.compile(r"^[ \t]*#.*coding[:=][ \t]*([-\w.]+)", flags=re.MULTILINE)
+
+@contract(source='bytes')
+def _source_encoding_py2(source):
+ """Determine the encoding for `source`, according to PEP 263.
+
+ `source` is a byte string, the text of the program.
+
+ Returns a string, the name of the encoding.
+
+ """
+ assert isinstance(source, bytes)
+
+ # Do this so the detect_encode code we copied will work.
+ readline = iternext(source.splitlines(True))
+
+ # This is mostly code adapted from Py3.2's tokenize module.
+
+ def _get_normal_name(orig_enc):
+ """Imitates get_normal_name in tokenizer.c."""
+ # Only care about the first 12 characters.
+ enc = orig_enc[:12].lower().replace("_", "-")
+ if re.match(r"^utf-8($|-)", enc):
+ return "utf-8"
+ if re.match(r"^(latin-1|iso-8859-1|iso-latin-1)($|-)", enc):
+ return "iso-8859-1"
+ return orig_enc
+
+ # From detect_encode():
+ # It detects the encoding from the presence of a UTF-8 BOM or an encoding
+ # cookie as specified in PEP-0263. If both a BOM and a cookie are present,
+ # but disagree, a SyntaxError will be raised. If the encoding cookie is an
+ # invalid charset, raise a SyntaxError. Note that if a UTF-8 BOM is found,
+ # 'utf-8-sig' is returned.
+
+ # If no encoding is specified, then the default will be returned.
+ default = 'ascii'
+
+ bom_found = False
+ encoding = None
+
+ def read_or_stop():
+ """Get the next source line, or ''."""
+ try:
+ return readline()
+ except StopIteration:
+ return ''
+
+ def find_cookie(line):
+ """Find an encoding cookie in `line`."""
+ try:
+ line_string = line.decode('ascii')
+ except UnicodeDecodeError:
+ return None
+
+ matches = COOKIE_RE.findall(line_string)
+ if not matches:
+ return None
+ encoding = _get_normal_name(matches[0])
+ try:
+ codec = codecs.lookup(encoding)
+ except LookupError:
+ # This behavior mimics the Python interpreter
+ raise SyntaxError("unknown encoding: " + encoding)
+
+ if bom_found:
+ # codecs in 2.3 were raw tuples of functions, assume the best.
+ codec_name = getattr(codec, 'name', encoding)
+ if codec_name != 'utf-8':
+ # This behavior mimics the Python interpreter
+ raise SyntaxError('encoding problem: utf-8')
+ encoding += '-sig'
+ return encoding
+
+ first = read_or_stop()
+ if first.startswith(codecs.BOM_UTF8):
+ bom_found = True
+ first = first[3:]
+ default = 'utf-8-sig'
+ if not first:
+ return default
+
+ encoding = find_cookie(first)
+ if encoding:
+ return encoding
+
+ second = read_or_stop()
+ if not second:
+ return default
+
+ encoding = find_cookie(second)
+ if encoding:
+ return encoding
+
+ return default
+
+
+@contract(source='bytes')
+def _source_encoding_py3(source):
+ """Determine the encoding for `source`, according to PEP 263.
+
+ `source` is a byte string: the text of the program.
+
+ Returns a string, the name of the encoding.
+
+ """
+ readline = iternext(source.splitlines(True))
+ return tokenize.detect_encoding(readline)[0]
+
+
+if env.PY3:
+ source_encoding = _source_encoding_py3
+else:
+ source_encoding = _source_encoding_py2
+
+
+@contract(source='unicode')
+def compile_unicode(source, filename, mode):
+ """Just like the `compile` builtin, but works on any Unicode string.
+
+ Python 2's compile() builtin has a stupid restriction: if the source string
+ is Unicode, then it may not have a encoding declaration in it. Why not?
+ Who knows! It also decodes to utf8, and then tries to interpret those utf8
+ bytes according to the encoding declaration. Why? Who knows!
+
+ This function neuters the coding declaration, and compiles it.
+
+ """
+ source = neuter_encoding_declaration(source)
+ if env.PY2 and isinstance(filename, unicode_class):
+ filename = filename.encode(sys.getfilesystemencoding(), "replace")
+ code = compile(source, filename, mode)
+ return code
+
+
+@contract(source='unicode', returns='unicode')
+def neuter_encoding_declaration(source):
+ """Return `source`, with any encoding declaration neutered."""
+ if COOKIE_RE.search(source):
+ source_lines = source.splitlines(True)
+ for lineno in range(min(2, len(source_lines))):
+ source_lines[lineno] = COOKIE_RE.sub("# (deleted declaration)", source_lines[lineno])
+ source = "".join(source_lines)
+ return source
diff --git a/contrib/python/coverage/py3/coverage/plugin.py b/contrib/python/coverage/py3/coverage/plugin.py
new file mode 100644
index 0000000000..6997b489bb
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/plugin.py
@@ -0,0 +1,533 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""
+.. versionadded:: 4.0
+
+Plug-in interfaces for coverage.py.
+
+Coverage.py supports a few different kinds of plug-ins that change its
+behavior:
+
+* File tracers implement tracing of non-Python file types.
+
+* Configurers add custom configuration, using Python code to change the
+ configuration.
+
+* Dynamic context switchers decide when the dynamic context has changed, for
+ example, to record what test function produced the coverage.
+
+To write a coverage.py plug-in, create a module with a subclass of
+:class:`~coverage.CoveragePlugin`. You will override methods in your class to
+participate in various aspects of coverage.py's processing.
+Different types of plug-ins have to override different methods.
+
+Any plug-in can optionally implement :meth:`~coverage.CoveragePlugin.sys_info`
+to provide debugging information about their operation.
+
+Your module must also contain a ``coverage_init`` function that registers an
+instance of your plug-in class::
+
+ import coverage
+
+ class MyPlugin(coverage.CoveragePlugin):
+ ...
+
+ def coverage_init(reg, options):
+ reg.add_file_tracer(MyPlugin())
+
+You use the `reg` parameter passed to your ``coverage_init`` function to
+register your plug-in object. The registration method you call depends on
+what kind of plug-in it is.
+
+If your plug-in takes options, the `options` parameter is a dictionary of your
+plug-in's options from the coverage.py configuration file. Use them however
+you want to configure your object before registering it.
+
+Coverage.py will store its own information on your plug-in object, using
+attributes whose names start with ``_coverage_``. Don't be startled.
+
+.. warning::
+ Plug-ins are imported by coverage.py before it begins measuring code.
+ If you write a plugin in your own project, it might import your product
+ code before coverage.py can start measuring. This can result in your
+ own code being reported as missing.
+
+ One solution is to put your plugins in your project tree, but not in
+ your importable Python package.
+
+
+.. _file_tracer_plugins:
+
+File Tracers
+============
+
+File tracers implement measurement support for non-Python files. File tracers
+implement the :meth:`~coverage.CoveragePlugin.file_tracer` method to claim
+files and the :meth:`~coverage.CoveragePlugin.file_reporter` method to report
+on those files.
+
+In your ``coverage_init`` function, use the ``add_file_tracer`` method to
+register your file tracer.
+
+
+.. _configurer_plugins:
+
+Configurers
+===========
+
+.. versionadded:: 4.5
+
+Configurers modify the configuration of coverage.py during start-up.
+Configurers implement the :meth:`~coverage.CoveragePlugin.configure` method to
+change the configuration.
+
+In your ``coverage_init`` function, use the ``add_configurer`` method to
+register your configurer.
+
+
+.. _dynamic_context_plugins:
+
+Dynamic Context Switchers
+=========================
+
+.. versionadded:: 5.0
+
+Dynamic context switcher plugins implement the
+:meth:`~coverage.CoveragePlugin.dynamic_context` method to dynamically compute
+the context label for each measured frame.
+
+Computed context labels are useful when you want to group measured data without
+modifying the source code.
+
+For example, you could write a plugin that checks `frame.f_code` to inspect
+the currently executed method, and set the context label to a fully qualified
+method name if it's an instance method of `unittest.TestCase` and the method
+name starts with 'test'. Such a plugin would provide basic coverage grouping
+by test and could be used with test runners that have no built-in coveragepy
+support.
+
+In your ``coverage_init`` function, use the ``add_dynamic_context`` method to
+register your dynamic context switcher.
+
+"""
+
+from coverage import files
+from coverage.misc import contract, _needs_to_implement
+
+
+class CoveragePlugin(object):
+ """Base class for coverage.py plug-ins."""
+
+ def file_tracer(self, filename): # pylint: disable=unused-argument
+ """Get a :class:`FileTracer` object for a file.
+
+ Plug-in type: file tracer.
+
+ Every Python source file is offered to your plug-in to give it a chance
+ to take responsibility for tracing the file. If your plug-in can
+ handle the file, it should return a :class:`FileTracer` object.
+ Otherwise return None.
+
+ There is no way to register your plug-in for particular files.
+ Instead, this method is invoked for all files as they are executed,
+ and the plug-in decides whether it can trace the file or not.
+ Be prepared for `filename` to refer to all kinds of files that have
+ nothing to do with your plug-in.
+
+ The file name will be a Python file being executed. There are two
+ broad categories of behavior for a plug-in, depending on the kind of
+ files your plug-in supports:
+
+ * Static file names: each of your original source files has been
+ converted into a distinct Python file. Your plug-in is invoked with
+ the Python file name, and it maps it back to its original source
+ file.
+
+ * Dynamic file names: all of your source files are executed by the same
+ Python file. In this case, your plug-in implements
+ :meth:`FileTracer.dynamic_source_filename` to provide the actual
+ source file for each execution frame.
+
+ `filename` is a string, the path to the file being considered. This is
+ the absolute real path to the file. If you are comparing to other
+ paths, be sure to take this into account.
+
+ Returns a :class:`FileTracer` object to use to trace `filename`, or
+ None if this plug-in cannot trace this file.
+
+ """
+ return None
+
+ def file_reporter(self, filename): # pylint: disable=unused-argument
+ """Get the :class:`FileReporter` class to use for a file.
+
+ Plug-in type: file tracer.
+
+ This will only be invoked if `filename` returns non-None from
+ :meth:`file_tracer`. It's an error to return None from this method.
+
+ Returns a :class:`FileReporter` object to use to report on `filename`,
+ or the string `"python"` to have coverage.py treat the file as Python.
+
+ """
+ _needs_to_implement(self, "file_reporter")
+
+ def dynamic_context(self, frame): # pylint: disable=unused-argument
+ """Get the dynamically computed context label for `frame`.
+
+ Plug-in type: dynamic context.
+
+ This method is invoked for each frame when outside of a dynamic
+ context, to see if a new dynamic context should be started. If it
+ returns a string, a new context label is set for this and deeper
+ frames. The dynamic context ends when this frame returns.
+
+ Returns a string to start a new dynamic context, or None if no new
+ context should be started.
+
+ """
+ return None
+
+ def find_executable_files(self, src_dir): # pylint: disable=unused-argument
+ """Yield all of the executable files in `src_dir`, recursively.
+
+ Plug-in type: file tracer.
+
+ Executability is a plug-in-specific property, but generally means files
+ which would have been considered for coverage analysis, had they been
+ included automatically.
+
+ Returns or yields a sequence of strings, the paths to files that could
+ have been executed, including files that had been executed.
+
+ """
+ return []
+
+ def configure(self, config):
+ """Modify the configuration of coverage.py.
+
+ Plug-in type: configurer.
+
+ This method is called during coverage.py start-up, to give your plug-in
+ a chance to change the configuration. The `config` parameter is an
+ object with :meth:`~coverage.Coverage.get_option` and
+ :meth:`~coverage.Coverage.set_option` methods. Do not call any other
+ methods on the `config` object.
+
+ """
+ pass
+
+ def sys_info(self):
+ """Get a list of information useful for debugging.
+
+ Plug-in type: any.
+
+ This method will be invoked for ``--debug=sys``. Your
+ plug-in can return any information it wants to be displayed.
+
+ Returns a list of pairs: `[(name, value), ...]`.
+
+ """
+ return []
+
+
+class FileTracer(object):
+ """Support needed for files during the execution phase.
+
+ File tracer plug-ins implement subclasses of FileTracer to return from
+ their :meth:`~CoveragePlugin.file_tracer` method.
+
+ You may construct this object from :meth:`CoveragePlugin.file_tracer` any
+ way you like. A natural choice would be to pass the file name given to
+ `file_tracer`.
+
+ `FileTracer` objects should only be created in the
+ :meth:`CoveragePlugin.file_tracer` method.
+
+ See :ref:`howitworks` for details of the different coverage.py phases.
+
+ """
+
+ def source_filename(self):
+ """The source file name for this file.
+
+ This may be any file name you like. A key responsibility of a plug-in
+ is to own the mapping from Python execution back to whatever source
+ file name was originally the source of the code.
+
+ See :meth:`CoveragePlugin.file_tracer` for details about static and
+ dynamic file names.
+
+ Returns the file name to credit with this execution.
+
+ """
+ _needs_to_implement(self, "source_filename")
+
+ def has_dynamic_source_filename(self):
+ """Does this FileTracer have dynamic source file names?
+
+ FileTracers can provide dynamically determined file names by
+ implementing :meth:`dynamic_source_filename`. Invoking that function
+ is expensive. To determine whether to invoke it, coverage.py uses the
+ result of this function to know if it needs to bother invoking
+ :meth:`dynamic_source_filename`.
+
+ See :meth:`CoveragePlugin.file_tracer` for details about static and
+ dynamic file names.
+
+ Returns True if :meth:`dynamic_source_filename` should be called to get
+ dynamic source file names.
+
+ """
+ return False
+
+ def dynamic_source_filename(self, filename, frame): # pylint: disable=unused-argument
+ """Get a dynamically computed source file name.
+
+ Some plug-ins need to compute the source file name dynamically for each
+ frame.
+
+ This function will not be invoked if
+ :meth:`has_dynamic_source_filename` returns False.
+
+ Returns the source file name for this frame, or None if this frame
+ shouldn't be measured.
+
+ """
+ return None
+
+ def line_number_range(self, frame):
+ """Get the range of source line numbers for a given a call frame.
+
+ The call frame is examined, and the source line number in the original
+ file is returned. The return value is a pair of numbers, the starting
+ line number and the ending line number, both inclusive. For example,
+ returning (5, 7) means that lines 5, 6, and 7 should be considered
+ executed.
+
+ This function might decide that the frame doesn't indicate any lines
+ from the source file were executed. Return (-1, -1) in this case to
+ tell coverage.py that no lines should be recorded for this frame.
+
+ """
+ lineno = frame.f_lineno
+ return lineno, lineno
+
+
+class FileReporter(object):
+ """Support needed for files during the analysis and reporting phases.
+
+ File tracer plug-ins implement a subclass of `FileReporter`, and return
+ instances from their :meth:`CoveragePlugin.file_reporter` method.
+
+ There are many methods here, but only :meth:`lines` is required, to provide
+ the set of executable lines in the file.
+
+ See :ref:`howitworks` for details of the different coverage.py phases.
+
+ """
+
+ def __init__(self, filename):
+ """Simple initialization of a `FileReporter`.
+
+ The `filename` argument is the path to the file being reported. This
+ will be available as the `.filename` attribute on the object. Other
+ method implementations on this base class rely on this attribute.
+
+ """
+ self.filename = filename
+
+ def __repr__(self):
+ return "<{0.__class__.__name__} filename={0.filename!r}>".format(self)
+
+ def relative_filename(self):
+ """Get the relative file name for this file.
+
+ This file path will be displayed in reports. The default
+ implementation will supply the actual project-relative file path. You
+ only need to supply this method if you have an unusual syntax for file
+ paths.
+
+ """
+ return files.relative_filename(self.filename)
+
+ @contract(returns='unicode')
+ def source(self):
+ """Get the source for the file.
+
+ Returns a Unicode string.
+
+ The base implementation simply reads the `self.filename` file and
+ decodes it as UTF8. Override this method if your file isn't readable
+ as a text file, or if you need other encoding support.
+
+ """
+ with open(self.filename, "rb") as f:
+ return f.read().decode("utf8")
+
+ def lines(self):
+ """Get the executable lines in this file.
+
+ Your plug-in must determine which lines in the file were possibly
+ executable. This method returns a set of those line numbers.
+
+ Returns a set of line numbers.
+
+ """
+ _needs_to_implement(self, "lines")
+
+ def excluded_lines(self):
+ """Get the excluded executable lines in this file.
+
+ Your plug-in can use any method it likes to allow the user to exclude
+ executable lines from consideration.
+
+ Returns a set of line numbers.
+
+ The base implementation returns the empty set.
+
+ """
+ return set()
+
+ def translate_lines(self, lines):
+ """Translate recorded lines into reported lines.
+
+ Some file formats will want to report lines slightly differently than
+ they are recorded. For example, Python records the last line of a
+ multi-line statement, but reports are nicer if they mention the first
+ line.
+
+ Your plug-in can optionally define this method to perform these kinds
+ of adjustment.
+
+ `lines` is a sequence of integers, the recorded line numbers.
+
+ Returns a set of integers, the adjusted line numbers.
+
+ The base implementation returns the numbers unchanged.
+
+ """
+ return set(lines)
+
+ def arcs(self):
+ """Get the executable arcs in this file.
+
+ To support branch coverage, your plug-in needs to be able to indicate
+ possible execution paths, as a set of line number pairs. Each pair is
+ a `(prev, next)` pair indicating that execution can transition from the
+ `prev` line number to the `next` line number.
+
+ Returns a set of pairs of line numbers. The default implementation
+ returns an empty set.
+
+ """
+ return set()
+
+ def no_branch_lines(self):
+ """Get the lines excused from branch coverage in this file.
+
+ Your plug-in can use any method it likes to allow the user to exclude
+ lines from consideration of branch coverage.
+
+ Returns a set of line numbers.
+
+ The base implementation returns the empty set.
+
+ """
+ return set()
+
+ def translate_arcs(self, arcs):
+ """Translate recorded arcs into reported arcs.
+
+ Similar to :meth:`translate_lines`, but for arcs. `arcs` is a set of
+ line number pairs.
+
+ Returns a set of line number pairs.
+
+ The default implementation returns `arcs` unchanged.
+
+ """
+ return arcs
+
+ def exit_counts(self):
+ """Get a count of exits from that each line.
+
+ To determine which lines are branches, coverage.py looks for lines that
+ have more than one exit. This function creates a dict mapping each
+ executable line number to a count of how many exits it has.
+
+ To be honest, this feels wrong, and should be refactored. Let me know
+ if you attempt to implement this method in your plug-in...
+
+ """
+ return {}
+
+ def missing_arc_description(self, start, end, executed_arcs=None): # pylint: disable=unused-argument
+ """Provide an English sentence describing a missing arc.
+
+ The `start` and `end` arguments are the line numbers of the missing
+ arc. Negative numbers indicate entering or exiting code objects.
+
+ The `executed_arcs` argument is a set of line number pairs, the arcs
+ that were executed in this file.
+
+ By default, this simply returns the string "Line {start} didn't jump
+ to {end}".
+
+ """
+ return "Line {start} didn't jump to line {end}".format(start=start, end=end)
+
+ def source_token_lines(self):
+ """Generate a series of tokenized lines, one for each line in `source`.
+
+ These tokens are used for syntax-colored reports.
+
+ Each line is a list of pairs, each pair is a token::
+
+ [('key', 'def'), ('ws', ' '), ('nam', 'hello'), ('op', '('), ... ]
+
+ Each pair has a token class, and the token text. The token classes
+ are:
+
+ * ``'com'``: a comment
+ * ``'key'``: a keyword
+ * ``'nam'``: a name, or identifier
+ * ``'num'``: a number
+ * ``'op'``: an operator
+ * ``'str'``: a string literal
+ * ``'ws'``: some white space
+ * ``'txt'``: some other kind of text
+
+ If you concatenate all the token texts, and then join them with
+ newlines, you should have your original source back.
+
+ The default implementation simply returns each line tagged as
+ ``'txt'``.
+
+ """
+ for line in self.source().splitlines():
+ yield [('txt', line)]
+
+ # Annoying comparison operators. Py3k wants __lt__ etc, and Py2k needs all
+ # of them defined.
+
+ def __eq__(self, other):
+ return isinstance(other, FileReporter) and self.filename == other.filename
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __lt__(self, other):
+ return self.filename < other.filename
+
+ def __le__(self, other):
+ return self.filename <= other.filename
+
+ def __gt__(self, other):
+ return self.filename > other.filename
+
+ def __ge__(self, other):
+ return self.filename >= other.filename
+
+ __hash__ = None # This object doesn't need to be hashed.
diff --git a/contrib/python/coverage/py3/coverage/plugin_support.py b/contrib/python/coverage/py3/coverage/plugin_support.py
new file mode 100644
index 0000000000..89c1c7658f
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/plugin_support.py
@@ -0,0 +1,281 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Support for plugins."""
+
+import os
+import os.path
+import sys
+
+from coverage.misc import CoverageException, isolate_module
+from coverage.plugin import CoveragePlugin, FileTracer, FileReporter
+
+os = isolate_module(os)
+
+
+class Plugins(object):
+ """The currently loaded collection of coverage.py plugins."""
+
+ def __init__(self):
+ self.order = []
+ self.names = {}
+ self.file_tracers = []
+ self.configurers = []
+ self.context_switchers = []
+
+ self.current_module = None
+ self.debug = None
+
+ @classmethod
+ def load_plugins(cls, modules, config, debug=None):
+ """Load plugins from `modules`.
+
+ Returns a Plugins object with the loaded and configured plugins.
+
+ """
+ plugins = cls()
+ plugins.debug = debug
+
+ for module in modules:
+ plugins.current_module = module
+ __import__(module)
+ mod = sys.modules[module]
+
+ coverage_init = getattr(mod, "coverage_init", None)
+ if not coverage_init:
+ raise CoverageException(
+ "Plugin module %r didn't define a coverage_init function" % module
+ )
+
+ options = config.get_plugin_options(module)
+ coverage_init(plugins, options)
+
+ plugins.current_module = None
+ return plugins
+
+ def add_file_tracer(self, plugin):
+ """Add a file tracer plugin.
+
+ `plugin` is an instance of a third-party plugin class. It must
+ implement the :meth:`CoveragePlugin.file_tracer` method.
+
+ """
+ self._add_plugin(plugin, self.file_tracers)
+
+ def add_configurer(self, plugin):
+ """Add a configuring plugin.
+
+ `plugin` is an instance of a third-party plugin class. It must
+ implement the :meth:`CoveragePlugin.configure` method.
+
+ """
+ self._add_plugin(plugin, self.configurers)
+
+ def add_dynamic_context(self, plugin):
+ """Add a dynamic context plugin.
+
+ `plugin` is an instance of a third-party plugin class. It must
+ implement the :meth:`CoveragePlugin.dynamic_context` method.
+
+ """
+ self._add_plugin(plugin, self.context_switchers)
+
+ def add_noop(self, plugin):
+ """Add a plugin that does nothing.
+
+ This is only useful for testing the plugin support.
+
+ """
+ self._add_plugin(plugin, None)
+
+ def _add_plugin(self, plugin, specialized):
+ """Add a plugin object.
+
+ `plugin` is a :class:`CoveragePlugin` instance to add. `specialized`
+ is a list to append the plugin to.
+
+ """
+ plugin_name = "%s.%s" % (self.current_module, plugin.__class__.__name__)
+ if self.debug and self.debug.should('plugin'):
+ self.debug.write("Loaded plugin %r: %r" % (self.current_module, plugin))
+ labelled = LabelledDebug("plugin %r" % (self.current_module,), self.debug)
+ plugin = DebugPluginWrapper(plugin, labelled)
+
+ # pylint: disable=attribute-defined-outside-init
+ plugin._coverage_plugin_name = plugin_name
+ plugin._coverage_enabled = True
+ self.order.append(plugin)
+ self.names[plugin_name] = plugin
+ if specialized is not None:
+ specialized.append(plugin)
+
+ def __nonzero__(self):
+ return bool(self.order)
+
+ __bool__ = __nonzero__
+
+ def __iter__(self):
+ return iter(self.order)
+
+ def get(self, plugin_name):
+ """Return a plugin by name."""
+ return self.names[plugin_name]
+
+
+class LabelledDebug(object):
+ """A Debug writer, but with labels for prepending to the messages."""
+
+ def __init__(self, label, debug, prev_labels=()):
+ self.labels = list(prev_labels) + [label]
+ self.debug = debug
+
+ def add_label(self, label):
+ """Add a label to the writer, and return a new `LabelledDebug`."""
+ return LabelledDebug(label, self.debug, self.labels)
+
+ def message_prefix(self):
+ """The prefix to use on messages, combining the labels."""
+ prefixes = self.labels + ['']
+ return ":\n".join(" "*i+label for i, label in enumerate(prefixes))
+
+ def write(self, message):
+ """Write `message`, but with the labels prepended."""
+ self.debug.write("%s%s" % (self.message_prefix(), message))
+
+
+class DebugPluginWrapper(CoveragePlugin):
+ """Wrap a plugin, and use debug to report on what it's doing."""
+
+ def __init__(self, plugin, debug):
+ super(DebugPluginWrapper, self).__init__()
+ self.plugin = plugin
+ self.debug = debug
+
+ def file_tracer(self, filename):
+ tracer = self.plugin.file_tracer(filename)
+ self.debug.write("file_tracer(%r) --> %r" % (filename, tracer))
+ if tracer:
+ debug = self.debug.add_label("file %r" % (filename,))
+ tracer = DebugFileTracerWrapper(tracer, debug)
+ return tracer
+
+ def file_reporter(self, filename):
+ reporter = self.plugin.file_reporter(filename)
+ self.debug.write("file_reporter(%r) --> %r" % (filename, reporter))
+ if reporter:
+ debug = self.debug.add_label("file %r" % (filename,))
+ reporter = DebugFileReporterWrapper(filename, reporter, debug)
+ return reporter
+
+ def dynamic_context(self, frame):
+ context = self.plugin.dynamic_context(frame)
+ self.debug.write("dynamic_context(%r) --> %r" % (frame, context))
+ return context
+
+ def find_executable_files(self, src_dir):
+ executable_files = self.plugin.find_executable_files(src_dir)
+ self.debug.write("find_executable_files(%r) --> %r" % (src_dir, executable_files))
+ return executable_files
+
+ def configure(self, config):
+ self.debug.write("configure(%r)" % (config,))
+ self.plugin.configure(config)
+
+ def sys_info(self):
+ return self.plugin.sys_info()
+
+
+class DebugFileTracerWrapper(FileTracer):
+ """A debugging `FileTracer`."""
+
+ def __init__(self, tracer, debug):
+ self.tracer = tracer
+ self.debug = debug
+
+ def _show_frame(self, frame):
+ """A short string identifying a frame, for debug messages."""
+ return "%s@%d" % (
+ os.path.basename(frame.f_code.co_filename),
+ frame.f_lineno,
+ )
+
+ def source_filename(self):
+ sfilename = self.tracer.source_filename()
+ self.debug.write("source_filename() --> %r" % (sfilename,))
+ return sfilename
+
+ def has_dynamic_source_filename(self):
+ has = self.tracer.has_dynamic_source_filename()
+ self.debug.write("has_dynamic_source_filename() --> %r" % (has,))
+ return has
+
+ def dynamic_source_filename(self, filename, frame):
+ dyn = self.tracer.dynamic_source_filename(filename, frame)
+ self.debug.write("dynamic_source_filename(%r, %s) --> %r" % (
+ filename, self._show_frame(frame), dyn,
+ ))
+ return dyn
+
+ def line_number_range(self, frame):
+ pair = self.tracer.line_number_range(frame)
+ self.debug.write("line_number_range(%s) --> %r" % (self._show_frame(frame), pair))
+ return pair
+
+
+class DebugFileReporterWrapper(FileReporter):
+ """A debugging `FileReporter`."""
+
+ def __init__(self, filename, reporter, debug):
+ super(DebugFileReporterWrapper, self).__init__(filename)
+ self.reporter = reporter
+ self.debug = debug
+
+ def relative_filename(self):
+ ret = self.reporter.relative_filename()
+ self.debug.write("relative_filename() --> %r" % (ret,))
+ return ret
+
+ def lines(self):
+ ret = self.reporter.lines()
+ self.debug.write("lines() --> %r" % (ret,))
+ return ret
+
+ def excluded_lines(self):
+ ret = self.reporter.excluded_lines()
+ self.debug.write("excluded_lines() --> %r" % (ret,))
+ return ret
+
+ def translate_lines(self, lines):
+ ret = self.reporter.translate_lines(lines)
+ self.debug.write("translate_lines(%r) --> %r" % (lines, ret))
+ return ret
+
+ def translate_arcs(self, arcs):
+ ret = self.reporter.translate_arcs(arcs)
+ self.debug.write("translate_arcs(%r) --> %r" % (arcs, ret))
+ return ret
+
+ def no_branch_lines(self):
+ ret = self.reporter.no_branch_lines()
+ self.debug.write("no_branch_lines() --> %r" % (ret,))
+ return ret
+
+ def exit_counts(self):
+ ret = self.reporter.exit_counts()
+ self.debug.write("exit_counts() --> %r" % (ret,))
+ return ret
+
+ def arcs(self):
+ ret = self.reporter.arcs()
+ self.debug.write("arcs() --> %r" % (ret,))
+ return ret
+
+ def source(self):
+ ret = self.reporter.source()
+ self.debug.write("source() --> %d chars" % (len(ret),))
+ return ret
+
+ def source_token_lines(self):
+ ret = list(self.reporter.source_token_lines())
+ self.debug.write("source_token_lines() --> %d tokens" % (len(ret),))
+ return ret
diff --git a/contrib/python/coverage/py3/coverage/python.py b/contrib/python/coverage/py3/coverage/python.py
new file mode 100644
index 0000000000..6ff19c34fe
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/python.py
@@ -0,0 +1,261 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Python source expertise for coverage.py"""
+
+import sys
+import os.path
+import types
+import zipimport
+
+from coverage import env, files
+from coverage.misc import contract, expensive, isolate_module, join_regex
+from coverage.misc import CoverageException, NoSource
+from coverage.parser import PythonParser
+from coverage.phystokens import source_token_lines, source_encoding
+from coverage.plugin import FileReporter
+
+os = isolate_module(os)
+
+
+@contract(returns='bytes')
+def read_python_source(filename):
+ """Read the Python source text from `filename`.
+
+ Returns bytes.
+
+ """
+ with open(filename, "rb") as f:
+ source = f.read()
+
+ if env.IRONPYTHON:
+ # IronPython reads Unicode strings even for "rb" files.
+ source = bytes(source)
+
+ return source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
+
+
+@contract(returns='unicode')
+def get_python_source(filename, force_fs=False):
+ """Return the source code, as unicode."""
+ if getattr(sys, 'is_standalone_binary', False) and not force_fs:
+ import __res
+
+ modname = __res.importer.file_source(filename)
+ if modname:
+ source = __res.find(modname)
+ source = source.replace(b"\r\n", b"\n").replace(b"\r", b"\n")
+ return source.decode('utf-8')
+ else:
+ # it's fake generated package
+ return u''
+ base, ext = os.path.splitext(filename)
+ if ext == ".py" and env.WINDOWS:
+ exts = [".py", ".pyw"]
+ else:
+ exts = [ext]
+
+ for ext in exts:
+ try_filename = base + ext
+ if os.path.exists(try_filename):
+ # A regular text file: open it.
+ source = read_python_source(try_filename)
+ break
+
+ # Maybe it's in a zip file?
+ source = get_zip_bytes(try_filename)
+ if source is not None:
+ break
+ else:
+ # Couldn't find source.
+ exc_msg = "No source for code: '%s'.\n" % (filename,)
+ exc_msg += "Aborting report output, consider using -i."
+ raise NoSource(exc_msg)
+
+ # Replace \f because of http://bugs.python.org/issue19035
+ source = source.replace(b'\f', b' ')
+ source = source.decode(source_encoding(source), "replace")
+
+ # Python code should always end with a line with a newline.
+ if source and source[-1] != '\n':
+ source += '\n'
+
+ return source
+
+
+@contract(returns='bytes|None')
+def get_zip_bytes(filename):
+ """Get data from `filename` if it is a zip file path.
+
+ Returns the bytestring data read from the zip file, or None if no zip file
+ could be found or `filename` isn't in it. The data returned will be
+ an empty string if the file is empty.
+
+ """
+ markers = ['.zip'+os.sep, '.egg'+os.sep, '.pex'+os.sep]
+ for marker in markers:
+ if marker in filename:
+ parts = filename.split(marker)
+ try:
+ zi = zipimport.zipimporter(parts[0]+marker[:-1])
+ except zipimport.ZipImportError:
+ continue
+ try:
+ data = zi.get_data(parts[1])
+ except IOError:
+ continue
+ return data
+ return None
+
+
+def source_for_file(filename):
+ """Return the source filename for `filename`.
+
+ Given a file name being traced, return the best guess as to the source
+ file to attribute it to.
+
+ """
+ if filename.endswith(".py"):
+ # .py files are themselves source files.
+ return filename
+
+ elif filename.endswith((".pyc", ".pyo")):
+ # Bytecode files probably have source files near them.
+ py_filename = filename[:-1]
+ if os.path.exists(py_filename):
+ # Found a .py file, use that.
+ return py_filename
+ if env.WINDOWS:
+ # On Windows, it could be a .pyw file.
+ pyw_filename = py_filename + "w"
+ if os.path.exists(pyw_filename):
+ return pyw_filename
+ # Didn't find source, but it's probably the .py file we want.
+ return py_filename
+
+ elif filename.endswith("$py.class"):
+ # Jython is easy to guess.
+ return filename[:-9] + ".py"
+
+ # No idea, just use the file name as-is.
+ return filename
+
+
+def source_for_morf(morf):
+ """Get the source filename for the module-or-file `morf`."""
+ if hasattr(morf, '__file__') and morf.__file__:
+ filename = morf.__file__
+ elif isinstance(morf, types.ModuleType):
+ # A module should have had .__file__, otherwise we can't use it.
+ # This could be a PEP-420 namespace package.
+ raise CoverageException("Module {} has no file".format(morf))
+ else:
+ filename = morf
+
+ filename = source_for_file(files.unicode_filename(filename))
+ return filename
+
+
+class PythonFileReporter(FileReporter):
+ """Report support for a Python file."""
+
+ def __init__(self, morf, coverage=None):
+ self.coverage = coverage
+
+ filename = source_for_morf(morf)
+
+ super(PythonFileReporter, self).__init__(files.canonical_filename(filename))
+
+ if hasattr(morf, '__name__'):
+ name = morf.__name__.replace(".", os.sep)
+ if os.path.basename(filename).startswith('__init__.'):
+ name += os.sep + "__init__"
+ name += ".py"
+ name = files.unicode_filename(name)
+ else:
+ name = files.relative_filename(filename)
+ self.relname = name
+
+ self._source = None
+ self._parser = None
+ self._excluded = None
+
+ def __repr__(self):
+ return "<PythonFileReporter {!r}>".format(self.filename)
+
+ @contract(returns='unicode')
+ def relative_filename(self):
+ return self.relname
+
+ @property
+ def parser(self):
+ """Lazily create a :class:`PythonParser`."""
+ if self._parser is None:
+ self._parser = PythonParser(
+ filename=self.filename,
+ exclude=self.coverage._exclude_regex('exclude'),
+ )
+ self._parser.parse_source()
+ return self._parser
+
+ def lines(self):
+ """Return the line numbers of statements in the file."""
+ return self.parser.statements
+
+ def excluded_lines(self):
+ """Return the line numbers of statements in the file."""
+ return self.parser.excluded
+
+ def translate_lines(self, lines):
+ return self.parser.translate_lines(lines)
+
+ def translate_arcs(self, arcs):
+ return self.parser.translate_arcs(arcs)
+
+ @expensive
+ def no_branch_lines(self):
+ no_branch = self.parser.lines_matching(
+ join_regex(self.coverage.config.partial_list),
+ join_regex(self.coverage.config.partial_always_list)
+ )
+ return no_branch
+
+ @expensive
+ def arcs(self):
+ return self.parser.arcs()
+
+ @expensive
+ def exit_counts(self):
+ return self.parser.exit_counts()
+
+ def missing_arc_description(self, start, end, executed_arcs=None):
+ return self.parser.missing_arc_description(start, end, executed_arcs)
+
+ @contract(returns='unicode')
+ def source(self):
+ if self._source is None:
+ self._source = get_python_source(self.filename)
+ return self._source
+
+ def should_be_python(self):
+ """Does it seem like this file should contain Python?
+
+ This is used to decide if a file reported as part of the execution of
+ a program was really likely to have contained Python in the first
+ place.
+
+ """
+ # Get the file extension.
+ _, ext = os.path.splitext(self.filename)
+
+ # Anything named *.py* should be Python.
+ if ext.startswith('.py'):
+ return True
+ # A file with no extension should be Python.
+ if not ext:
+ return True
+ # Everything else is probably not Python.
+ return False
+
+ def source_token_lines(self):
+ return source_token_lines(self.source())
diff --git a/contrib/python/coverage/py3/coverage/pytracer.py b/contrib/python/coverage/py3/coverage/pytracer.py
new file mode 100644
index 0000000000..7ab4d3ef92
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/pytracer.py
@@ -0,0 +1,274 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Raw data collector for coverage.py."""
+
+import atexit
+import dis
+import sys
+
+from coverage import env
+
+# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
+YIELD_VALUE = dis.opmap['YIELD_VALUE']
+if env.PY2:
+ YIELD_VALUE = chr(YIELD_VALUE)
+
+# When running meta-coverage, this file can try to trace itself, which confuses
+# everything. Don't trace ourselves.
+
+THIS_FILE = __file__.rstrip("co")
+
+
+class PyTracer(object):
+ """Python implementation of the raw data tracer."""
+
+ # Because of poor implementations of trace-function-manipulating tools,
+ # the Python trace function must be kept very simple. In particular, there
+ # must be only one function ever set as the trace function, both through
+ # sys.settrace, and as the return value from the trace function. Put
+ # another way, the trace function must always return itself. It cannot
+ # swap in other functions, or return None to avoid tracing a particular
+ # frame.
+ #
+ # The trace manipulator that introduced this restriction is DecoratorTools,
+ # which sets a trace function, and then later restores the pre-existing one
+ # by calling sys.settrace with a function it found in the current frame.
+ #
+ # Systems that use DecoratorTools (or similar trace manipulations) must use
+ # PyTracer to get accurate results. The command-line --timid argument is
+ # used to force the use of this tracer.
+
+ def __init__(self):
+ # Attributes set from the collector:
+ self.data = None
+ self.trace_arcs = False
+ self.should_trace = None
+ self.should_trace_cache = None
+ self.should_start_context = None
+ self.warn = None
+ # The threading module to use, if any.
+ self.threading = None
+
+ self.cur_file_dict = None
+ self.last_line = 0 # int, but uninitialized.
+ self.cur_file_name = None
+ self.context = None
+ self.started_context = False
+
+ self.data_stack = []
+ self.last_exc_back = None
+ self.last_exc_firstlineno = 0
+ self.thread = None
+ self.stopped = False
+ self._activity = False
+
+ self.in_atexit = False
+ # On exit, self.in_atexit = True
+ atexit.register(setattr, self, 'in_atexit', True)
+
+ def __repr__(self):
+ return "<PyTracer at {}: {} lines in {} files>".format(
+ id(self),
+ sum(len(v) for v in self.data.values()),
+ len(self.data),
+ )
+
+ def log(self, marker, *args):
+ """For hard-core logging of what this tracer is doing."""
+ with open("/tmp/debug_trace.txt", "a") as f:
+ f.write("{} {}[{}]".format(
+ marker,
+ id(self),
+ len(self.data_stack),
+ ))
+ if 0:
+ f.write(".{:x}.{:x}".format(
+ self.thread.ident,
+ self.threading.currentThread().ident,
+ ))
+ f.write(" {}".format(" ".join(map(str, args))))
+ if 0:
+ f.write(" | ")
+ stack = " / ".join(
+ (fname or "???").rpartition("/")[-1]
+ for _, fname, _, _ in self.data_stack
+ )
+ f.write(stack)
+ f.write("\n")
+
+ def _trace(self, frame, event, arg_unused):
+ """The trace function passed to sys.settrace."""
+
+ if THIS_FILE in frame.f_code.co_filename:
+ return None
+
+ #self.log(":", frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + "()", event)
+
+ if (self.stopped and sys.gettrace() == self._trace): # pylint: disable=comparison-with-callable
+ # The PyTrace.stop() method has been called, possibly by another
+ # thread, let's deactivate ourselves now.
+ if 0:
+ self.log("---\nX", frame.f_code.co_filename, frame.f_lineno)
+ f = frame
+ while f:
+ self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace)
+ f = f.f_back
+ sys.settrace(None)
+ self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
+ self.data_stack.pop()
+ )
+ return None
+
+ if self.last_exc_back:
+ if frame == self.last_exc_back:
+ # Someone forgot a return event.
+ if self.trace_arcs and self.cur_file_dict:
+ pair = (self.last_line, -self.last_exc_firstlineno)
+ self.cur_file_dict[pair] = None
+ self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
+ self.data_stack.pop()
+ )
+ self.last_exc_back = None
+
+ # if event != 'call' and frame.f_code.co_filename != self.cur_file_name:
+ # self.log("---\n*", frame.f_code.co_filename, self.cur_file_name, frame.f_lineno)
+
+ if event == 'call':
+ # Should we start a new context?
+ if self.should_start_context and self.context is None:
+ context_maybe = self.should_start_context(frame)
+ if context_maybe is not None:
+ self.context = context_maybe
+ self.started_context = True
+ self.switch_context(self.context)
+ else:
+ self.started_context = False
+ else:
+ self.started_context = False
+
+ # Entering a new frame. Decide if we should trace
+ # in this file.
+ self._activity = True
+ self.data_stack.append(
+ (
+ self.cur_file_dict,
+ self.cur_file_name,
+ self.last_line,
+ self.started_context,
+ )
+ )
+ filename = frame.f_code.co_filename
+ self.cur_file_name = filename
+ disp = self.should_trace_cache.get(filename)
+ if disp is None:
+ disp = self.should_trace(filename, frame)
+ self.should_trace_cache[filename] = disp
+
+ self.cur_file_dict = None
+ if disp.trace:
+ tracename = disp.source_filename
+ if tracename not in self.data:
+ self.data[tracename] = {}
+ self.cur_file_dict = self.data[tracename]
+ # The call event is really a "start frame" event, and happens for
+ # function calls and re-entering generators. The f_lasti field is
+ # -1 for calls, and a real offset for generators. Use <0 as the
+ # line number for calls, and the real line number for generators.
+ if getattr(frame, 'f_lasti', -1) < 0:
+ self.last_line = -frame.f_code.co_firstlineno
+ else:
+ self.last_line = frame.f_lineno
+ elif event == 'line':
+ # Record an executed line.
+ if self.cur_file_dict is not None:
+ lineno = frame.f_lineno
+
+ if self.trace_arcs:
+ self.cur_file_dict[(self.last_line, lineno)] = None
+ else:
+ self.cur_file_dict[lineno] = None
+ self.last_line = lineno
+ elif event == 'return':
+ if self.trace_arcs and self.cur_file_dict:
+ # Record an arc leaving the function, but beware that a
+ # "return" event might just mean yielding from a generator.
+ # Jython seems to have an empty co_code, so just assume return.
+ code = frame.f_code.co_code
+ if (not code) or code[frame.f_lasti] != YIELD_VALUE:
+ first = frame.f_code.co_firstlineno
+ self.cur_file_dict[(self.last_line, -first)] = None
+ # Leaving this function, pop the filename stack.
+ self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = (
+ self.data_stack.pop()
+ )
+ # Leaving a context?
+ if self.started_context:
+ self.context = None
+ self.switch_context(None)
+ elif event == 'exception':
+ self.last_exc_back = frame.f_back
+ self.last_exc_firstlineno = frame.f_code.co_firstlineno
+ return self._trace
+
+ def start(self):
+ """Start this Tracer.
+
+ Return a Python function suitable for use with sys.settrace().
+
+ """
+ self.stopped = False
+ if self.threading:
+ if self.thread is None:
+ self.thread = self.threading.currentThread()
+ else:
+ if self.thread.ident != self.threading.currentThread().ident:
+ # Re-starting from a different thread!? Don't set the trace
+ # function, but we are marked as running again, so maybe it
+ # will be ok?
+ #self.log("~", "starting on different threads")
+ return self._trace
+
+ sys.settrace(self._trace)
+ return self._trace
+
+ def stop(self):
+ """Stop this Tracer."""
+ # Get the active tracer callback before setting the stop flag to be
+ # able to detect if the tracer was changed prior to stopping it.
+ tf = sys.gettrace()
+
+ # Set the stop flag. The actual call to sys.settrace(None) will happen
+ # in the self._trace callback itself to make sure to call it from the
+ # right thread.
+ self.stopped = True
+
+ if self.threading and self.thread.ident != self.threading.currentThread().ident:
+ # Called on a different thread than started us: we can't unhook
+ # ourselves, but we've set the flag that we should stop, so we
+ # won't do any more tracing.
+ #self.log("~", "stopping on different threads")
+ return
+
+ if self.warn:
+ # PyPy clears the trace function before running atexit functions,
+ # so don't warn if we are in atexit on PyPy and the trace function
+ # has changed to None.
+ dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None)
+ if (not dont_warn) and tf != self._trace: # pylint: disable=comparison-with-callable
+ self.warn(
+ "Trace function changed, measurement is likely wrong: %r" % (tf,),
+ slug="trace-changed",
+ )
+
+ def activity(self):
+ """Has there been any activity?"""
+ return self._activity
+
+ def reset_activity(self):
+ """Reset the activity() flag."""
+ self._activity = False
+
+ def get_stats(self):
+ """Return a dictionary of statistics, or None."""
+ return None
diff --git a/contrib/python/coverage/py3/coverage/report.py b/contrib/python/coverage/py3/coverage/report.py
new file mode 100644
index 0000000000..64678ff95d
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/report.py
@@ -0,0 +1,86 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Reporter foundation for coverage.py."""
+import sys
+
+from coverage import env
+from coverage.files import prep_patterns, FnmatchMatcher
+from coverage.misc import CoverageException, NoSource, NotPython, ensure_dir_for_file, file_be_gone
+
+
+def render_report(output_path, reporter, morfs):
+ """Run the provided reporter ensuring any required setup and cleanup is done
+
+ At a high level this method ensures the output file is ready to be written to. Then writes the
+ report to it. Then closes the file and deletes any garbage created if necessary.
+ """
+ file_to_close = None
+ delete_file = False
+ if output_path:
+ if output_path == '-':
+ outfile = sys.stdout
+ else:
+ # Ensure that the output directory is created; done here
+ # because this report pre-opens the output file.
+ # HTMLReport does this using the Report plumbing because
+ # its task is more complex, being multiple files.
+ ensure_dir_for_file(output_path)
+ open_kwargs = {}
+ if env.PY3:
+ open_kwargs['encoding'] = 'utf8'
+ outfile = open(output_path, "w", **open_kwargs)
+ file_to_close = outfile
+ try:
+ return reporter.report(morfs, outfile=outfile)
+ except CoverageException:
+ delete_file = True
+ raise
+ finally:
+ if file_to_close:
+ file_to_close.close()
+ if delete_file:
+ file_be_gone(output_path)
+
+
+def get_analysis_to_report(coverage, morfs):
+ """Get the files to report on.
+
+ For each morf in `morfs`, if it should be reported on (based on the omit
+ and include configuration options), yield a pair, the `FileReporter` and
+ `Analysis` for the morf.
+
+ """
+ file_reporters = coverage._get_file_reporters(morfs)
+ config = coverage.config
+
+ if config.report_include:
+ matcher = FnmatchMatcher(prep_patterns(config.report_include))
+ file_reporters = [fr for fr in file_reporters if matcher.match(fr.filename)]
+
+ if config.report_omit:
+ matcher = FnmatchMatcher(prep_patterns(config.report_omit))
+ file_reporters = [fr for fr in file_reporters if not matcher.match(fr.filename)]
+
+ if not file_reporters:
+ raise CoverageException("No data to report.")
+
+ for fr in sorted(file_reporters):
+ try:
+ analysis = coverage._analyze(fr)
+ except NoSource:
+ if not config.ignore_errors:
+ raise
+ except NotPython:
+ # Only report errors for .py files, and only if we didn't
+ # explicitly suppress those errors.
+ # NotPython is only raised by PythonFileReporter, which has a
+ # should_be_python() method.
+ if fr.should_be_python():
+ if config.ignore_errors:
+ msg = "Couldn't parse Python file '{}'".format(fr.filename)
+ coverage._warn(msg, slug="couldnt-parse")
+ else:
+ raise
+ else:
+ yield (fr, analysis)
diff --git a/contrib/python/coverage/py3/coverage/results.py b/contrib/python/coverage/py3/coverage/results.py
new file mode 100644
index 0000000000..4916864df3
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/results.py
@@ -0,0 +1,343 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Results of coverage measurement."""
+
+import collections
+
+from coverage.backward import iitems
+from coverage.debug import SimpleReprMixin
+from coverage.misc import contract, CoverageException, nice_pair
+
+
+class Analysis(object):
+ """The results of analyzing a FileReporter."""
+
+ def __init__(self, data, file_reporter, file_mapper):
+ self.data = data
+ self.file_reporter = file_reporter
+ self.filename = file_mapper(self.file_reporter.filename)
+ self.statements = self.file_reporter.lines()
+ self.excluded = self.file_reporter.excluded_lines()
+
+ # Identify missing statements.
+ executed = self.data.lines(self.filename) or []
+ executed = self.file_reporter.translate_lines(executed)
+ self.executed = executed
+ self.missing = self.statements - self.executed
+
+ if self.data.has_arcs():
+ self._arc_possibilities = sorted(self.file_reporter.arcs())
+ self.exit_counts = self.file_reporter.exit_counts()
+ self.no_branch = self.file_reporter.no_branch_lines()
+ n_branches = self._total_branches()
+ mba = self.missing_branch_arcs()
+ n_partial_branches = sum(len(v) for k,v in iitems(mba) if k not in self.missing)
+ n_missing_branches = sum(len(v) for k,v in iitems(mba))
+ else:
+ self._arc_possibilities = []
+ self.exit_counts = {}
+ self.no_branch = set()
+ n_branches = n_partial_branches = n_missing_branches = 0
+
+ self.numbers = Numbers(
+ n_files=1,
+ n_statements=len(self.statements),
+ n_excluded=len(self.excluded),
+ n_missing=len(self.missing),
+ n_branches=n_branches,
+ n_partial_branches=n_partial_branches,
+ n_missing_branches=n_missing_branches,
+ )
+
+ def missing_formatted(self, branches=False):
+ """The missing line numbers, formatted nicely.
+
+ Returns a string like "1-2, 5-11, 13-14".
+
+ If `branches` is true, includes the missing branch arcs also.
+
+ """
+ if branches and self.has_arcs():
+ arcs = iitems(self.missing_branch_arcs())
+ else:
+ arcs = None
+
+ return format_lines(self.statements, self.missing, arcs=arcs)
+
+ def has_arcs(self):
+ """Were arcs measured in this result?"""
+ return self.data.has_arcs()
+
+ @contract(returns='list(tuple(int, int))')
+ def arc_possibilities(self):
+ """Returns a sorted list of the arcs in the code."""
+ return self._arc_possibilities
+
+ @contract(returns='list(tuple(int, int))')
+ def arcs_executed(self):
+ """Returns a sorted list of the arcs actually executed in the code."""
+ executed = self.data.arcs(self.filename) or []
+ executed = self.file_reporter.translate_arcs(executed)
+ return sorted(executed)
+
+ @contract(returns='list(tuple(int, int))')
+ def arcs_missing(self):
+ """Returns a sorted list of the arcs in the code not executed."""
+ possible = self.arc_possibilities()
+ executed = self.arcs_executed()
+ missing = (
+ p for p in possible
+ if p not in executed
+ and p[0] not in self.no_branch
+ )
+ return sorted(missing)
+
+ @contract(returns='list(tuple(int, int))')
+ def arcs_unpredicted(self):
+ """Returns a sorted list of the executed arcs missing from the code."""
+ possible = self.arc_possibilities()
+ executed = self.arcs_executed()
+ # Exclude arcs here which connect a line to itself. They can occur
+ # in executed data in some cases. This is where they can cause
+ # trouble, and here is where it's the least burden to remove them.
+ # Also, generators can somehow cause arcs from "enter" to "exit", so
+ # make sure we have at least one positive value.
+ unpredicted = (
+ e for e in executed
+ if e not in possible
+ and e[0] != e[1]
+ and (e[0] > 0 or e[1] > 0)
+ )
+ return sorted(unpredicted)
+
+ def _branch_lines(self):
+ """Returns a list of line numbers that have more than one exit."""
+ return [l1 for l1,count in iitems(self.exit_counts) if count > 1]
+
+ def _total_branches(self):
+ """How many total branches are there?"""
+ return sum(count for count in self.exit_counts.values() if count > 1)
+
+ @contract(returns='dict(int: list(int))')
+ def missing_branch_arcs(self):
+ """Return arcs that weren't executed from branch lines.
+
+ Returns {l1:[l2a,l2b,...], ...}
+
+ """
+ missing = self.arcs_missing()
+ branch_lines = set(self._branch_lines())
+ mba = collections.defaultdict(list)
+ for l1, l2 in missing:
+ if l1 in branch_lines:
+ mba[l1].append(l2)
+ return mba
+
+ @contract(returns='dict(int: tuple(int, int))')
+ def branch_stats(self):
+ """Get stats about branches.
+
+ Returns a dict mapping line numbers to a tuple:
+ (total_exits, taken_exits).
+ """
+
+ missing_arcs = self.missing_branch_arcs()
+ stats = {}
+ for lnum in self._branch_lines():
+ exits = self.exit_counts[lnum]
+ missing = len(missing_arcs[lnum])
+ stats[lnum] = (exits, exits - missing)
+ return stats
+
+
+class Numbers(SimpleReprMixin):
+ """The numerical results of measuring coverage.
+
+ This holds the basic statistics from `Analysis`, and is used to roll
+ up statistics across files.
+
+ """
+ # A global to determine the precision on coverage percentages, the number
+ # of decimal places.
+ _precision = 0
+ _near0 = 1.0 # These will change when _precision is changed.
+ _near100 = 99.0
+
+ def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
+ n_branches=0, n_partial_branches=0, n_missing_branches=0
+ ):
+ self.n_files = n_files
+ self.n_statements = n_statements
+ self.n_excluded = n_excluded
+ self.n_missing = n_missing
+ self.n_branches = n_branches
+ self.n_partial_branches = n_partial_branches
+ self.n_missing_branches = n_missing_branches
+
+ def init_args(self):
+ """Return a list for __init__(*args) to recreate this object."""
+ return [
+ self.n_files, self.n_statements, self.n_excluded, self.n_missing,
+ self.n_branches, self.n_partial_branches, self.n_missing_branches,
+ ]
+
+ @classmethod
+ def set_precision(cls, precision):
+ """Set the number of decimal places used to report percentages."""
+ assert 0 <= precision < 10
+ cls._precision = precision
+ cls._near0 = 1.0 / 10**precision
+ cls._near100 = 100.0 - cls._near0
+
+ @property
+ def n_executed(self):
+ """Returns the number of executed statements."""
+ return self.n_statements - self.n_missing
+
+ @property
+ def n_executed_branches(self):
+ """Returns the number of executed branches."""
+ return self.n_branches - self.n_missing_branches
+
+ @property
+ def pc_covered(self):
+ """Returns a single percentage value for coverage."""
+ if self.n_statements > 0:
+ numerator, denominator = self.ratio_covered
+ pc_cov = (100.0 * numerator) / denominator
+ else:
+ pc_cov = 100.0
+ return pc_cov
+
+ @property
+ def pc_covered_str(self):
+ """Returns the percent covered, as a string, without a percent sign.
+
+ Note that "0" is only returned when the value is truly zero, and "100"
+ is only returned when the value is truly 100. Rounding can never
+ result in either "0" or "100".
+
+ """
+ pc = self.pc_covered
+ if 0 < pc < self._near0:
+ pc = self._near0
+ elif self._near100 < pc < 100:
+ pc = self._near100
+ else:
+ pc = round(pc, self._precision)
+ return "%.*f" % (self._precision, pc)
+
+ @classmethod
+ def pc_str_width(cls):
+ """How many characters wide can pc_covered_str be?"""
+ width = 3 # "100"
+ if cls._precision > 0:
+ width += 1 + cls._precision
+ return width
+
+ @property
+ def ratio_covered(self):
+ """Return a numerator and denominator for the coverage ratio."""
+ numerator = self.n_executed + self.n_executed_branches
+ denominator = self.n_statements + self.n_branches
+ return numerator, denominator
+
+ def __add__(self, other):
+ nums = Numbers()
+ nums.n_files = self.n_files + other.n_files
+ nums.n_statements = self.n_statements + other.n_statements
+ nums.n_excluded = self.n_excluded + other.n_excluded
+ nums.n_missing = self.n_missing + other.n_missing
+ nums.n_branches = self.n_branches + other.n_branches
+ nums.n_partial_branches = (
+ self.n_partial_branches + other.n_partial_branches
+ )
+ nums.n_missing_branches = (
+ self.n_missing_branches + other.n_missing_branches
+ )
+ return nums
+
+ def __radd__(self, other):
+ # Implementing 0+Numbers allows us to sum() a list of Numbers.
+ if other == 0:
+ return self
+ return NotImplemented # pragma: not covered (we never call it this way)
+
+
+def _line_ranges(statements, lines):
+ """Produce a list of ranges for `format_lines`."""
+ statements = sorted(statements)
+ lines = sorted(lines)
+
+ pairs = []
+ start = None
+ lidx = 0
+ for stmt in statements:
+ if lidx >= len(lines):
+ break
+ if stmt == lines[lidx]:
+ lidx += 1
+ if not start:
+ start = stmt
+ end = stmt
+ elif start:
+ pairs.append((start, end))
+ start = None
+ if start:
+ pairs.append((start, end))
+ return pairs
+
+
+def format_lines(statements, lines, arcs=None):
+ """Nicely format a list of line numbers.
+
+ Format a list of line numbers for printing by coalescing groups of lines as
+ long as the lines represent consecutive statements. This will coalesce
+ even if there are gaps between statements.
+
+ For example, if `statements` is [1,2,3,4,5,10,11,12,13,14] and
+ `lines` is [1,2,5,10,11,13,14] then the result will be "1-2, 5-11, 13-14".
+
+ Both `lines` and `statements` can be any iterable. All of the elements of
+ `lines` must be in `statements`, and all of the values must be positive
+ integers.
+
+ If `arcs` is provided, they are (start,[end,end,end]) pairs that will be
+ included in the output as long as start isn't in `lines`.
+
+ """
+ line_items = [(pair[0], nice_pair(pair)) for pair in _line_ranges(statements, lines)]
+ if arcs:
+ line_exits = sorted(arcs)
+ for line, exits in line_exits:
+ for ex in sorted(exits):
+ if line not in lines and ex not in lines:
+ dest = (ex if ex > 0 else "exit")
+ line_items.append((line, "%d->%s" % (line, dest)))
+
+ ret = ', '.join(t[-1] for t in sorted(line_items))
+ return ret
+
+
+@contract(total='number', fail_under='number', precision=int, returns=bool)
+def should_fail_under(total, fail_under, precision):
+ """Determine if a total should fail due to fail-under.
+
+ `total` is a float, the coverage measurement total. `fail_under` is the
+ fail_under setting to compare with. `precision` is the number of digits
+ to consider after the decimal point.
+
+ Returns True if the total should fail.
+
+ """
+ # We can never achieve higher than 100% coverage, or less than zero.
+ if not (0 <= fail_under <= 100.0):
+ msg = "fail_under={} is invalid. Must be between 0 and 100.".format(fail_under)
+ raise CoverageException(msg)
+
+ # Special case for fail_under=100, it must really be 100.
+ if fail_under == 100.0 and total != 100.0:
+ return True
+
+ return round(total, precision) < fail_under
diff --git a/contrib/python/coverage/py3/coverage/sqldata.py b/contrib/python/coverage/py3/coverage/sqldata.py
new file mode 100644
index 0000000000..a150fdfd0f
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/sqldata.py
@@ -0,0 +1,1123 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Sqlite coverage data."""
+
+# TODO: factor out dataop debugging to a wrapper class?
+# TODO: make sure all dataop debugging is in place somehow
+
+import collections
+import datetime
+import glob
+import itertools
+import os
+import re
+import sqlite3
+import sys
+import zlib
+
+from coverage import env
+from coverage.backward import get_thread_id, iitems, to_bytes, to_string
+from coverage.debug import NoDebugging, SimpleReprMixin, clipped_repr
+from coverage.files import PathAliases
+from coverage.misc import CoverageException, contract, file_be_gone, filename_suffix, isolate_module
+from coverage.numbits import numbits_to_nums, numbits_union, nums_to_numbits
+from coverage.version import __version__
+
+os = isolate_module(os)
+
+# If you change the schema, increment the SCHEMA_VERSION, and update the
+# docs in docs/dbschema.rst also.
+
+SCHEMA_VERSION = 7
+
+# Schema versions:
+# 1: Released in 5.0a2
+# 2: Added contexts in 5.0a3.
+# 3: Replaced line table with line_map table.
+# 4: Changed line_map.bitmap to line_map.numbits.
+# 5: Added foreign key declarations.
+# 6: Key-value in meta.
+# 7: line_map -> line_bits
+
+SCHEMA = """\
+CREATE TABLE coverage_schema (
+ -- One row, to record the version of the schema in this db.
+ version integer
+);
+
+CREATE TABLE meta (
+ -- Key-value pairs, to record metadata about the data
+ key text,
+ value text,
+ unique (key)
+ -- Keys:
+ -- 'has_arcs' boolean -- Is this data recording branches?
+ -- 'sys_argv' text -- The coverage command line that recorded the data.
+ -- 'version' text -- The version of coverage.py that made the file.
+ -- 'when' text -- Datetime when the file was created.
+);
+
+CREATE TABLE file (
+ -- A row per file measured.
+ id integer primary key,
+ path text,
+ unique (path)
+);
+
+CREATE TABLE context (
+ -- A row per context measured.
+ id integer primary key,
+ context text,
+ unique (context)
+);
+
+CREATE TABLE line_bits (
+ -- If recording lines, a row per context per file executed.
+ -- All of the line numbers for that file/context are in one numbits.
+ file_id integer, -- foreign key to `file`.
+ context_id integer, -- foreign key to `context`.
+ numbits blob, -- see the numbits functions in coverage.numbits
+ foreign key (file_id) references file (id),
+ foreign key (context_id) references context (id),
+ unique (file_id, context_id)
+);
+
+CREATE TABLE arc (
+ -- If recording branches, a row per context per from/to line transition executed.
+ file_id integer, -- foreign key to `file`.
+ context_id integer, -- foreign key to `context`.
+ fromno integer, -- line number jumped from.
+ tono integer, -- line number jumped to.
+ foreign key (file_id) references file (id),
+ foreign key (context_id) references context (id),
+ unique (file_id, context_id, fromno, tono)
+);
+
+CREATE TABLE tracer (
+ -- A row per file indicating the tracer used for that file.
+ file_id integer primary key,
+ tracer text,
+ foreign key (file_id) references file (id)
+);
+"""
+
+class CoverageData(SimpleReprMixin):
+ """Manages collected coverage data, including file storage.
+
+ This class is the public supported API to the data that coverage.py
+ collects during program execution. It includes information about what code
+ was executed. It does not include information from the analysis phase, to
+ determine what lines could have been executed, or what lines were not
+ executed.
+
+ .. note::
+
+ The data file is currently a SQLite database file, with a
+ :ref:`documented schema <dbschema>`. The schema is subject to change
+ though, so be careful about querying it directly. Use this API if you
+ can to isolate yourself from changes.
+
+ There are a number of kinds of data that can be collected:
+
+ * **lines**: the line numbers of source lines that were executed.
+ These are always available.
+
+ * **arcs**: pairs of source and destination line numbers for transitions
+ between source lines. These are only available if branch coverage was
+ used.
+
+ * **file tracer names**: the module names of the file tracer plugins that
+ handled each file in the data.
+
+ Lines, arcs, and file tracer names are stored for each source file. File
+ names in this API are case-sensitive, even on platforms with
+ case-insensitive file systems.
+
+ A data file either stores lines, or arcs, but not both.
+
+ A data file is associated with the data when the :class:`CoverageData`
+ is created, using the parameters `basename`, `suffix`, and `no_disk`. The
+ base name can be queried with :meth:`base_filename`, and the actual file
+ name being used is available from :meth:`data_filename`.
+
+ To read an existing coverage.py data file, use :meth:`read`. You can then
+ access the line, arc, or file tracer data with :meth:`lines`, :meth:`arcs`,
+ or :meth:`file_tracer`.
+
+ The :meth:`has_arcs` method indicates whether arc data is available. You
+ can get a set of the files in the data with :meth:`measured_files`. As
+ with most Python containers, you can determine if there is any data at all
+ by using this object as a boolean value.
+
+ The contexts for each line in a file can be read with
+ :meth:`contexts_by_lineno`.
+
+ To limit querying to certain contexts, use :meth:`set_query_context` or
+ :meth:`set_query_contexts`. These will narrow the focus of subsequent
+ :meth:`lines`, :meth:`arcs`, and :meth:`contexts_by_lineno` calls. The set
+ of all measured context names can be retrieved with
+ :meth:`measured_contexts`.
+
+ Most data files will be created by coverage.py itself, but you can use
+ methods here to create data files if you like. The :meth:`add_lines`,
+ :meth:`add_arcs`, and :meth:`add_file_tracers` methods add data, in ways
+ that are convenient for coverage.py.
+
+ To record data for contexts, use :meth:`set_context` to set a context to
+ be used for subsequent :meth:`add_lines` and :meth:`add_arcs` calls.
+
+ To add a source file without any measured data, use :meth:`touch_file`,
+ or :meth:`touch_files` for a list of such files.
+
+ Write the data to its file with :meth:`write`.
+
+ You can clear the data in memory with :meth:`erase`. Two data collections
+ can be combined by using :meth:`update` on one :class:`CoverageData`,
+ passing it the other.
+
+ Data in a :class:`CoverageData` can be serialized and deserialized with
+ :meth:`dumps` and :meth:`loads`.
+
+ """
+
+ def __init__(self, basename=None, suffix=None, no_disk=False, warn=None, debug=None):
+ """Create a :class:`CoverageData` object to hold coverage-measured data.
+
+ Arguments:
+ basename (str): the base name of the data file, defaulting to
+ ".coverage".
+ suffix (str or bool): has the same meaning as the `data_suffix`
+ argument to :class:`coverage.Coverage`.
+ no_disk (bool): if True, keep all data in memory, and don't
+ write any disk file.
+ warn: a warning callback function, accepting a warning message
+ argument.
+ debug: a `DebugControl` object (optional)
+
+ """
+ self._no_disk = no_disk
+ self._basename = os.path.abspath(basename or ".coverage")
+ self._suffix = suffix
+ self._warn = warn
+ self._debug = debug or NoDebugging()
+
+ self._choose_filename()
+ self._file_map = {}
+ # Maps thread ids to SqliteDb objects.
+ self._dbs = {}
+ self._pid = os.getpid()
+
+ # Are we in sync with the data file?
+ self._have_used = False
+
+ self._has_lines = False
+ self._has_arcs = False
+
+ self._current_context = None
+ self._current_context_id = None
+ self._query_context_ids = None
+
+ def _choose_filename(self):
+ """Set self._filename based on inited attributes."""
+ if self._no_disk:
+ self._filename = ":memory:"
+ else:
+ self._filename = self._basename
+ suffix = filename_suffix(self._suffix)
+ if suffix:
+ self._filename += "." + suffix
+
+ def _reset(self):
+ """Reset our attributes."""
+ if self._dbs:
+ for db in self._dbs.values():
+ db.close()
+ self._dbs = {}
+ self._file_map = {}
+ self._have_used = False
+ self._current_context_id = None
+
+ def _create_db(self):
+ """Create a db file that doesn't exist yet.
+
+ Initializes the schema and certain metadata.
+ """
+ if self._debug.should('dataio'):
+ self._debug.write("Creating data file {!r}".format(self._filename))
+ self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
+ with db:
+ db.executescript(SCHEMA)
+ db.execute("insert into coverage_schema (version) values (?)", (SCHEMA_VERSION,))
+ db.executemany(
+ "insert into meta (key, value) values (?, ?)",
+ [
+ ('sys_argv', str(getattr(sys, 'argv', None))),
+ ('version', __version__),
+ ('when', datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')),
+ ]
+ )
+
+ def _open_db(self):
+ """Open an existing db file, and read its metadata."""
+ if self._debug.should('dataio'):
+ self._debug.write("Opening data file {!r}".format(self._filename))
+ self._dbs[get_thread_id()] = SqliteDb(self._filename, self._debug)
+ self._read_db()
+
+ def _read_db(self):
+ """Read the metadata from a database so that we are ready to use it."""
+ with self._dbs[get_thread_id()] as db:
+ try:
+ schema_version, = db.execute_one("select version from coverage_schema")
+ except Exception as exc:
+ raise CoverageException(
+ "Data file {!r} doesn't seem to be a coverage data file: {}".format(
+ self._filename, exc
+ )
+ )
+ else:
+ if schema_version != SCHEMA_VERSION:
+ raise CoverageException(
+ "Couldn't use data file {!r}: wrong schema: {} instead of {}".format(
+ self._filename, schema_version, SCHEMA_VERSION
+ )
+ )
+
+ for row in db.execute("select value from meta where key = 'has_arcs'"):
+ self._has_arcs = bool(int(row[0]))
+ self._has_lines = not self._has_arcs
+
+ for path, file_id in db.execute("select path, id from file"):
+ self._file_map[path] = file_id
+
+ def _connect(self):
+ """Get the SqliteDb object to use."""
+ if get_thread_id() not in self._dbs:
+ if os.path.exists(self._filename):
+ self._open_db()
+ else:
+ self._create_db()
+ return self._dbs[get_thread_id()]
+
+ def __nonzero__(self):
+ if (get_thread_id() not in self._dbs and not os.path.exists(self._filename)):
+ return False
+ try:
+ with self._connect() as con:
+ rows = con.execute("select * from file limit 1")
+ return bool(list(rows))
+ except CoverageException:
+ return False
+
+ __bool__ = __nonzero__
+
+ @contract(returns='bytes')
+ def dumps(self):
+ """Serialize the current data to a byte string.
+
+ The format of the serialized data is not documented. It is only
+ suitable for use with :meth:`loads` in the same version of
+ coverage.py.
+
+ Returns:
+ A byte string of serialized data.
+
+ .. versionadded:: 5.0
+
+ """
+ if self._debug.should('dataio'):
+ self._debug.write("Dumping data from data file {!r}".format(self._filename))
+ with self._connect() as con:
+ return b'z' + zlib.compress(to_bytes(con.dump()))
+
+ @contract(data='bytes')
+ def loads(self, data):
+ """Deserialize data from :meth:`dumps`
+
+ Use with a newly-created empty :class:`CoverageData` object. It's
+ undefined what happens if the object already has data in it.
+
+ Arguments:
+ data: A byte string of serialized data produced by :meth:`dumps`.
+
+ .. versionadded:: 5.0
+
+ """
+ if self._debug.should('dataio'):
+ self._debug.write("Loading data into data file {!r}".format(self._filename))
+ if data[:1] != b'z':
+ raise CoverageException(
+ "Unrecognized serialization: {!r} (head of {} bytes)".format(data[:40], len(data))
+ )
+ script = to_string(zlib.decompress(data[1:]))
+ self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
+ with db:
+ db.executescript(script)
+ self._read_db()
+ self._have_used = True
+
+ def _file_id(self, filename, add=False):
+ """Get the file id for `filename`.
+
+ If filename is not in the database yet, add it if `add` is True.
+ If `add` is not True, return None.
+ """
+ if filename not in self._file_map:
+ if add:
+ with self._connect() as con:
+ cur = con.execute("insert or replace into file (path) values (?)", (filename,))
+ self._file_map[filename] = cur.lastrowid
+ return self._file_map.get(filename)
+
+ def _context_id(self, context):
+ """Get the id for a context."""
+ assert context is not None
+ self._start_using()
+ with self._connect() as con:
+ row = con.execute_one("select id from context where context = ?", (context,))
+ if row is not None:
+ return row[0]
+ else:
+ return None
+
+ def set_context(self, context):
+ """Set the current context for future :meth:`add_lines` etc.
+
+ `context` is a str, the name of the context to use for the next data
+ additions. The context persists until the next :meth:`set_context`.
+
+ .. versionadded:: 5.0
+
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Setting context: %r" % (context,))
+ self._current_context = context
+ self._current_context_id = None
+
+ def _set_context_id(self):
+ """Use the _current_context to set _current_context_id."""
+ context = self._current_context or ""
+ context_id = self._context_id(context)
+ if context_id is not None:
+ self._current_context_id = context_id
+ else:
+ with self._connect() as con:
+ cur = con.execute("insert into context (context) values (?)", (context,))
+ self._current_context_id = cur.lastrowid
+
+ def base_filename(self):
+ """The base filename for storing data.
+
+ .. versionadded:: 5.0
+
+ """
+ return self._basename
+
+ def data_filename(self):
+ """Where is the data stored?
+
+ .. versionadded:: 5.0
+
+ """
+ return self._filename
+
+ def add_lines(self, line_data):
+ """Add measured line data.
+
+ `line_data` is a dictionary mapping file names to dictionaries::
+
+ { filename: { lineno: None, ... }, ...}
+
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Adding lines: %d files, %d lines total" % (
+ len(line_data), sum(len(lines) for lines in line_data.values())
+ ))
+ self._start_using()
+ self._choose_lines_or_arcs(lines=True)
+ if not line_data:
+ return
+ with self._connect() as con:
+ self._set_context_id()
+ for filename, linenos in iitems(line_data):
+ linemap = nums_to_numbits(linenos)
+ file_id = self._file_id(filename, add=True)
+ query = "select numbits from line_bits where file_id = ? and context_id = ?"
+ existing = list(con.execute(query, (file_id, self._current_context_id)))
+ if existing:
+ linemap = numbits_union(linemap, existing[0][0])
+
+ con.execute(
+ "insert or replace into line_bits "
+ " (file_id, context_id, numbits) values (?, ?, ?)",
+ (file_id, self._current_context_id, linemap),
+ )
+
+ def add_arcs(self, arc_data):
+ """Add measured arc data.
+
+ `arc_data` is a dictionary mapping file names to dictionaries::
+
+ { filename: { (l1,l2): None, ... }, ...}
+
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Adding arcs: %d files, %d arcs total" % (
+ len(arc_data), sum(len(arcs) for arcs in arc_data.values())
+ ))
+ self._start_using()
+ self._choose_lines_or_arcs(arcs=True)
+ if not arc_data:
+ return
+ with self._connect() as con:
+ self._set_context_id()
+ for filename, arcs in iitems(arc_data):
+ file_id = self._file_id(filename, add=True)
+ data = [(file_id, self._current_context_id, fromno, tono) for fromno, tono in arcs]
+ con.executemany(
+ "insert or ignore into arc "
+ "(file_id, context_id, fromno, tono) values (?, ?, ?, ?)",
+ data,
+ )
+
+ def _choose_lines_or_arcs(self, lines=False, arcs=False):
+ """Force the data file to choose between lines and arcs."""
+ assert lines or arcs
+ assert not (lines and arcs)
+ if lines and self._has_arcs:
+ raise CoverageException("Can't add line measurements to existing branch data")
+ if arcs and self._has_lines:
+ raise CoverageException("Can't add branch measurements to existing line data")
+ if not self._has_arcs and not self._has_lines:
+ self._has_lines = lines
+ self._has_arcs = arcs
+ with self._connect() as con:
+ con.execute(
+ "insert into meta (key, value) values (?, ?)",
+ ('has_arcs', str(int(arcs)))
+ )
+
+ def add_file_tracers(self, file_tracers):
+ """Add per-file plugin information.
+
+ `file_tracers` is { filename: plugin_name, ... }
+
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Adding file tracers: %d files" % (len(file_tracers),))
+ if not file_tracers:
+ return
+ self._start_using()
+ with self._connect() as con:
+ for filename, plugin_name in iitems(file_tracers):
+ file_id = self._file_id(filename)
+ if file_id is None:
+ raise CoverageException(
+ "Can't add file tracer data for unmeasured file '%s'" % (filename,)
+ )
+
+ existing_plugin = self.file_tracer(filename)
+ if existing_plugin:
+ if existing_plugin != plugin_name:
+ raise CoverageException(
+ "Conflicting file tracer name for '%s': %r vs %r" % (
+ filename, existing_plugin, plugin_name,
+ )
+ )
+ elif plugin_name:
+ con.execute(
+ "insert into tracer (file_id, tracer) values (?, ?)",
+ (file_id, plugin_name)
+ )
+
+ def touch_file(self, filename, plugin_name=""):
+ """Ensure that `filename` appears in the data, empty if needed.
+
+ `plugin_name` is the name of the plugin responsible for this file. It is used
+ to associate the right filereporter, etc.
+ """
+ self.touch_files([filename], plugin_name)
+
+ def touch_files(self, filenames, plugin_name=""):
+ """Ensure that `filenames` appear in the data, empty if needed.
+
+ `plugin_name` is the name of the plugin responsible for these files. It is used
+ to associate the right filereporter, etc.
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Touching %r" % (filenames,))
+ self._start_using()
+ with self._connect(): # Use this to get one transaction.
+ if not self._has_arcs and not self._has_lines:
+ raise CoverageException("Can't touch files in an empty CoverageData")
+
+ for filename in filenames:
+ self._file_id(filename, add=True)
+ if plugin_name:
+ # Set the tracer for this file
+ self.add_file_tracers({filename: plugin_name})
+
+ def update(self, other_data, aliases=None):
+ """Update this data with data from several other :class:`CoverageData` instances.
+
+ If `aliases` is provided, it's a `PathAliases` object that is used to
+ re-map paths to match the local machine's.
+ """
+ if self._debug.should('dataop'):
+ self._debug.write("Updating with data from %r" % (
+ getattr(other_data, '_filename', '???'),
+ ))
+ if self._has_lines and other_data._has_arcs:
+ raise CoverageException("Can't combine arc data with line data")
+ if self._has_arcs and other_data._has_lines:
+ raise CoverageException("Can't combine line data with arc data")
+
+ aliases = aliases or PathAliases()
+
+ # Force the database we're writing to to exist before we start nesting
+ # contexts.
+ self._start_using()
+
+ # Collector for all arcs, lines and tracers
+ other_data.read()
+ with other_data._connect() as conn:
+ # Get files data.
+ cur = conn.execute('select path from file')
+ files = {path: aliases.map(path) for (path,) in cur}
+ cur.close()
+
+ # Get contexts data.
+ cur = conn.execute('select context from context')
+ contexts = [context for (context,) in cur]
+ cur.close()
+
+ # Get arc data.
+ cur = conn.execute(
+ 'select file.path, context.context, arc.fromno, arc.tono '
+ 'from arc '
+ 'inner join file on file.id = arc.file_id '
+ 'inner join context on context.id = arc.context_id'
+ )
+ arcs = [(files[path], context, fromno, tono) for (path, context, fromno, tono) in cur]
+ cur.close()
+
+ # Get line data.
+ cur = conn.execute(
+ 'select file.path, context.context, line_bits.numbits '
+ 'from line_bits '
+ 'inner join file on file.id = line_bits.file_id '
+ 'inner join context on context.id = line_bits.context_id'
+ )
+ lines = {
+ (files[path], context): numbits
+ for (path, context, numbits) in cur
+ }
+ cur.close()
+
+ # Get tracer data.
+ cur = conn.execute(
+ 'select file.path, tracer '
+ 'from tracer '
+ 'inner join file on file.id = tracer.file_id'
+ )
+ tracers = {files[path]: tracer for (path, tracer) in cur}
+ cur.close()
+
+ with self._connect() as conn:
+ conn.con.isolation_level = 'IMMEDIATE'
+
+ # Get all tracers in the DB. Files not in the tracers are assumed
+ # to have an empty string tracer. Since Sqlite does not support
+ # full outer joins, we have to make two queries to fill the
+ # dictionary.
+ this_tracers = {path: '' for path, in conn.execute('select path from file')}
+ this_tracers.update({
+ aliases.map(path): tracer
+ for path, tracer in conn.execute(
+ 'select file.path, tracer from tracer '
+ 'inner join file on file.id = tracer.file_id'
+ )
+ })
+
+ # Create all file and context rows in the DB.
+ conn.executemany(
+ 'insert or ignore into file (path) values (?)',
+ ((file,) for file in files.values())
+ )
+ file_ids = {
+ path: id
+ for id, path in conn.execute('select id, path from file')
+ }
+ conn.executemany(
+ 'insert or ignore into context (context) values (?)',
+ ((context,) for context in contexts)
+ )
+ context_ids = {
+ context: id
+ for id, context in conn.execute('select id, context from context')
+ }
+
+ # Prepare tracers and fail, if a conflict is found.
+ # tracer_paths is used to ensure consistency over the tracer data
+ # and tracer_map tracks the tracers to be inserted.
+ tracer_map = {}
+ for path in files.values():
+ this_tracer = this_tracers.get(path)
+ other_tracer = tracers.get(path, '')
+ # If there is no tracer, there is always the None tracer.
+ if this_tracer is not None and this_tracer != other_tracer:
+ raise CoverageException(
+ "Conflicting file tracer name for '%s': %r vs %r" % (
+ path, this_tracer, other_tracer
+ )
+ )
+ tracer_map[path] = other_tracer
+
+ # Prepare arc and line rows to be inserted by converting the file
+ # and context strings with integer ids. Then use the efficient
+ # `executemany()` to insert all rows at once.
+ arc_rows = (
+ (file_ids[file], context_ids[context], fromno, tono)
+ for file, context, fromno, tono in arcs
+ )
+
+ # Get line data.
+ cur = conn.execute(
+ 'select file.path, context.context, line_bits.numbits '
+ 'from line_bits '
+ 'inner join file on file.id = line_bits.file_id '
+ 'inner join context on context.id = line_bits.context_id'
+ )
+ for path, context, numbits in cur:
+ key = (aliases.map(path), context)
+ if key in lines:
+ numbits = numbits_union(lines[key], numbits)
+ lines[key] = numbits
+ cur.close()
+
+ if arcs:
+ self._choose_lines_or_arcs(arcs=True)
+
+ # Write the combined data.
+ conn.executemany(
+ 'insert or ignore into arc '
+ '(file_id, context_id, fromno, tono) values (?, ?, ?, ?)',
+ arc_rows
+ )
+
+ if lines:
+ self._choose_lines_or_arcs(lines=True)
+ conn.execute("delete from line_bits")
+ conn.executemany(
+ "insert into line_bits "
+ "(file_id, context_id, numbits) values (?, ?, ?)",
+ [
+ (file_ids[file], context_ids[context], numbits)
+ for (file, context), numbits in lines.items()
+ ]
+ )
+ conn.executemany(
+ 'insert or ignore into tracer (file_id, tracer) values (?, ?)',
+ ((file_ids[filename], tracer) for filename, tracer in tracer_map.items())
+ )
+
+ # Update all internal cache data.
+ self._reset()
+ self.read()
+
+ def erase(self, parallel=False):
+ """Erase the data in this object.
+
+ If `parallel` is true, then also deletes data files created from the
+ basename by parallel-mode.
+
+ """
+ self._reset()
+ if self._no_disk:
+ return
+ if self._debug.should('dataio'):
+ self._debug.write("Erasing data file {!r}".format(self._filename))
+ file_be_gone(self._filename)
+ if parallel:
+ data_dir, local = os.path.split(self._filename)
+ localdot = local + '.*'
+ pattern = os.path.join(os.path.abspath(data_dir), localdot)
+ for filename in glob.glob(pattern):
+ if self._debug.should('dataio'):
+ self._debug.write("Erasing parallel data file {!r}".format(filename))
+ file_be_gone(filename)
+
+ def read(self):
+ """Start using an existing data file."""
+ with self._connect(): # TODO: doesn't look right
+ self._have_used = True
+
+ def write(self):
+ """Ensure the data is written to the data file."""
+ pass
+
+ def _start_using(self):
+ """Call this before using the database at all."""
+ if self._pid != os.getpid():
+ # Looks like we forked! Have to start a new data file.
+ self._reset()
+ self._choose_filename()
+ self._pid = os.getpid()
+ if not self._have_used:
+ self.erase()
+ self._have_used = True
+
+ def has_arcs(self):
+ """Does the database have arcs (True) or lines (False)."""
+ return bool(self._has_arcs)
+
+ def measured_files(self):
+ """A set of all files that had been measured."""
+ return set(self._file_map)
+
+ def measured_contexts(self):
+ """A set of all contexts that have been measured.
+
+ .. versionadded:: 5.0
+
+ """
+ self._start_using()
+ with self._connect() as con:
+ contexts = {row[0] for row in con.execute("select distinct(context) from context")}
+ return contexts
+
+ def file_tracer(self, filename):
+ """Get the plugin name of the file tracer for a file.
+
+ Returns the name of the plugin that handles this file. If the file was
+ measured, but didn't use a plugin, then "" is returned. If the file
+ was not measured, then None is returned.
+
+ """
+ self._start_using()
+ with self._connect() as con:
+ file_id = self._file_id(filename)
+ if file_id is None:
+ return None
+ row = con.execute_one("select tracer from tracer where file_id = ?", (file_id,))
+ if row is not None:
+ return row[0] or ""
+ return "" # File was measured, but no tracer associated.
+
+ def set_query_context(self, context):
+ """Set a context for subsequent querying.
+
+ The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno`
+ calls will be limited to only one context. `context` is a string which
+ must match a context exactly. If it does not, no exception is raised,
+ but queries will return no data.
+
+ .. versionadded:: 5.0
+
+ """
+ self._start_using()
+ with self._connect() as con:
+ cur = con.execute("select id from context where context = ?", (context,))
+ self._query_context_ids = [row[0] for row in cur.fetchall()]
+
+ def set_query_contexts(self, contexts):
+ """Set a number of contexts for subsequent querying.
+
+ The next :meth:`lines`, :meth:`arcs`, or :meth:`contexts_by_lineno`
+ calls will be limited to the specified contexts. `contexts` is a list
+ of Python regular expressions. Contexts will be matched using
+ :func:`re.search <python:re.search>`. Data will be included in query
+ results if they are part of any of the contexts matched.
+
+ .. versionadded:: 5.0
+
+ """
+ self._start_using()
+ if contexts:
+ with self._connect() as con:
+ context_clause = ' or '.join(['context regexp ?'] * len(contexts))
+ cur = con.execute("select id from context where " + context_clause, contexts)
+ self._query_context_ids = [row[0] for row in cur.fetchall()]
+ else:
+ self._query_context_ids = None
+
+ def lines(self, filename):
+ """Get the list of lines executed for a file.
+
+ If the file was not measured, returns None. A file might be measured,
+ and have no lines executed, in which case an empty list is returned.
+
+ If the file was executed, returns a list of integers, the line numbers
+ executed in the file. The list is in no particular order.
+
+ """
+ self._start_using()
+ if self.has_arcs():
+ arcs = self.arcs(filename)
+ if arcs is not None:
+ all_lines = itertools.chain.from_iterable(arcs)
+ return list({l for l in all_lines if l > 0})
+
+ with self._connect() as con:
+ file_id = self._file_id(filename)
+ if file_id is None:
+ return None
+ else:
+ query = "select numbits from line_bits where file_id = ?"
+ data = [file_id]
+ if self._query_context_ids is not None:
+ ids_array = ', '.join('?' * len(self._query_context_ids))
+ query += " and context_id in (" + ids_array + ")"
+ data += self._query_context_ids
+ bitmaps = list(con.execute(query, data))
+ nums = set()
+ for row in bitmaps:
+ nums.update(numbits_to_nums(row[0]))
+ return list(nums)
+
+ def arcs(self, filename):
+ """Get the list of arcs executed for a file.
+
+ If the file was not measured, returns None. A file might be measured,
+ and have no arcs executed, in which case an empty list is returned.
+
+ If the file was executed, returns a list of 2-tuples of integers. Each
+ pair is a starting line number and an ending line number for a
+ transition from one line to another. The list is in no particular
+ order.
+
+ Negative numbers have special meaning. If the starting line number is
+ -N, it represents an entry to the code object that starts at line N.
+ If the ending ling number is -N, it's an exit from the code object that
+ starts at line N.
+
+ """
+ self._start_using()
+ with self._connect() as con:
+ file_id = self._file_id(filename)
+ if file_id is None:
+ return None
+ else:
+ query = "select distinct fromno, tono from arc where file_id = ?"
+ data = [file_id]
+ if self._query_context_ids is not None:
+ ids_array = ', '.join('?' * len(self._query_context_ids))
+ query += " and context_id in (" + ids_array + ")"
+ data += self._query_context_ids
+ arcs = con.execute(query, data)
+ return list(arcs)
+
+ def contexts_by_lineno(self, filename):
+ """Get the contexts for each line in a file.
+
+ Returns:
+ A dict mapping line numbers to a list of context names.
+
+ .. versionadded:: 5.0
+
+ """
+ lineno_contexts_map = collections.defaultdict(list)
+ self._start_using()
+ with self._connect() as con:
+ file_id = self._file_id(filename)
+ if file_id is None:
+ return lineno_contexts_map
+ if self.has_arcs():
+ query = (
+ "select arc.fromno, arc.tono, context.context "
+ "from arc, context "
+ "where arc.file_id = ? and arc.context_id = context.id"
+ )
+ data = [file_id]
+ if self._query_context_ids is not None:
+ ids_array = ', '.join('?' * len(self._query_context_ids))
+ query += " and arc.context_id in (" + ids_array + ")"
+ data += self._query_context_ids
+ for fromno, tono, context in con.execute(query, data):
+ if context not in lineno_contexts_map[fromno]:
+ lineno_contexts_map[fromno].append(context)
+ if context not in lineno_contexts_map[tono]:
+ lineno_contexts_map[tono].append(context)
+ else:
+ query = (
+ "select l.numbits, c.context from line_bits l, context c "
+ "where l.context_id = c.id "
+ "and file_id = ?"
+ )
+ data = [file_id]
+ if self._query_context_ids is not None:
+ ids_array = ', '.join('?' * len(self._query_context_ids))
+ query += " and l.context_id in (" + ids_array + ")"
+ data += self._query_context_ids
+ for numbits, context in con.execute(query, data):
+ for lineno in numbits_to_nums(numbits):
+ lineno_contexts_map[lineno].append(context)
+ return lineno_contexts_map
+
+ @classmethod
+ def sys_info(cls):
+ """Our information for `Coverage.sys_info`.
+
+ Returns a list of (key, value) pairs.
+
+ """
+ with SqliteDb(":memory:", debug=NoDebugging()) as db:
+ temp_store = [row[0] for row in db.execute("pragma temp_store")]
+ compile_options = [row[0] for row in db.execute("pragma compile_options")]
+
+ return [
+ ('sqlite3_version', sqlite3.version),
+ ('sqlite3_sqlite_version', sqlite3.sqlite_version),
+ ('sqlite3_temp_store', temp_store),
+ ('sqlite3_compile_options', compile_options),
+ ]
+
+
+class SqliteDb(SimpleReprMixin):
+ """A simple abstraction over a SQLite database.
+
+ Use as a context manager, then you can use it like a
+ :class:`python:sqlite3.Connection` object::
+
+ with SqliteDb(filename, debug_control) as db:
+ db.execute("insert into schema (version) values (?)", (SCHEMA_VERSION,))
+
+ """
+ def __init__(self, filename, debug):
+ self.debug = debug if debug.should('sql') else None
+ self.filename = filename
+ self.nest = 0
+ self.con = None
+
+ def _connect(self):
+ """Connect to the db and do universal initialization."""
+ if self.con is not None:
+ return
+
+ # SQLite on Windows on py2 won't open a file if the filename argument
+ # has non-ascii characters in it. Opening a relative file name avoids
+ # a problem if the current directory has non-ascii.
+ filename = self.filename
+ if env.WINDOWS and env.PY2:
+ try:
+ filename = os.path.relpath(self.filename)
+ except ValueError:
+ # ValueError can be raised under Windows when os.getcwd() returns a
+ # folder from a different drive than the drive of self.filename in
+ # which case we keep the original value of self.filename unchanged,
+ # hoping that we won't face the non-ascii directory problem.
+ pass
+
+ # It can happen that Python switches threads while the tracer writes
+ # data. The second thread will also try to write to the data,
+ # effectively causing a nested context. However, given the idempotent
+ # nature of the tracer operations, sharing a connection among threads
+ # is not a problem.
+ if self.debug:
+ self.debug.write("Connecting to {!r}".format(self.filename))
+ self.con = sqlite3.connect(filename, check_same_thread=False)
+ self.con.create_function('REGEXP', 2, _regexp)
+
+ # This pragma makes writing faster. It disables rollbacks, but we never need them.
+ # PyPy needs the .close() calls here, or sqlite gets twisted up:
+ # https://bitbucket.org/pypy/pypy/issues/2872/default-isolation-mode-is-different-on
+ self.execute("pragma journal_mode=off").close()
+ # This pragma makes writing faster.
+ self.execute("pragma synchronous=off").close()
+
+ def close(self):
+ """If needed, close the connection."""
+ if self.con is not None and self.filename != ":memory:":
+ self.con.close()
+ self.con = None
+
+ def __enter__(self):
+ if self.nest == 0:
+ self._connect()
+ self.con.__enter__()
+ self.nest += 1
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.nest -= 1
+ if self.nest == 0:
+ try:
+ self.con.__exit__(exc_type, exc_value, traceback)
+ self.close()
+ except Exception as exc:
+ if self.debug:
+ self.debug.write("EXCEPTION from __exit__: {}".format(exc))
+ raise
+
+ def execute(self, sql, parameters=()):
+ """Same as :meth:`python:sqlite3.Connection.execute`."""
+ if self.debug:
+ tail = " with {!r}".format(parameters) if parameters else ""
+ self.debug.write("Executing {!r}{}".format(sql, tail))
+ try:
+ try:
+ return self.con.execute(sql, parameters)
+ except Exception:
+ # In some cases, an error might happen that isn't really an
+ # error. Try again immediately.
+ # https://github.com/nedbat/coveragepy/issues/1010
+ return self.con.execute(sql, parameters)
+ except sqlite3.Error as exc:
+ msg = str(exc)
+ try:
+ # `execute` is the first thing we do with the database, so try
+ # hard to provide useful hints if something goes wrong now.
+ with open(self.filename, "rb") as bad_file:
+ cov4_sig = b"!coverage.py: This is a private format"
+ if bad_file.read(len(cov4_sig)) == cov4_sig:
+ msg = (
+ "Looks like a coverage 4.x data file. "
+ "Are you mixing versions of coverage?"
+ )
+ except Exception:
+ pass
+ if self.debug:
+ self.debug.write("EXCEPTION from execute: {}".format(msg))
+ raise CoverageException("Couldn't use data file {!r}: {}".format(self.filename, msg))
+
+ def execute_one(self, sql, parameters=()):
+ """Execute a statement and return the one row that results.
+
+ This is like execute(sql, parameters).fetchone(), except it is
+ correct in reading the entire result set. This will raise an
+ exception if more than one row results.
+
+ Returns a row, or None if there were no rows.
+ """
+ rows = list(self.execute(sql, parameters))
+ if len(rows) == 0:
+ return None
+ elif len(rows) == 1:
+ return rows[0]
+ else:
+ raise CoverageException("Sql {!r} shouldn't return {} rows".format(sql, len(rows)))
+
+ def executemany(self, sql, data):
+ """Same as :meth:`python:sqlite3.Connection.executemany`."""
+ if self.debug:
+ data = list(data)
+ self.debug.write("Executing many {!r} with {} rows".format(sql, len(data)))
+ return self.con.executemany(sql, data)
+
+ def executescript(self, script):
+ """Same as :meth:`python:sqlite3.Connection.executescript`."""
+ if self.debug:
+ self.debug.write("Executing script with {} chars: {}".format(
+ len(script), clipped_repr(script, 100),
+ ))
+ self.con.executescript(script)
+
+ def dump(self):
+ """Return a multi-line string, the SQL dump of the database."""
+ return "\n".join(self.con.iterdump())
+
+
+def _regexp(text, pattern):
+ """A regexp function for SQLite."""
+ return re.search(text, pattern) is not None
diff --git a/contrib/python/coverage/py3/coverage/summary.py b/contrib/python/coverage/py3/coverage/summary.py
new file mode 100644
index 0000000000..65f8047006
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/summary.py
@@ -0,0 +1,152 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Summary reporting"""
+
+import sys
+
+from coverage import env
+from coverage.report import get_analysis_to_report
+from coverage.results import Numbers
+from coverage.misc import CoverageException, output_encoding
+
+
+class SummaryReporter(object):
+ """A reporter for writing the summary report."""
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+ self.branches = coverage.get_data().has_arcs()
+ self.outfile = None
+ self.fr_analysis = []
+ self.skipped_count = 0
+ self.empty_count = 0
+ self.total = Numbers()
+ self.fmt_err = u"%s %s: %s"
+
+ def writeout(self, line):
+ """Write a line to the output, adding a newline."""
+ if env.PY2:
+ line = line.encode(output_encoding())
+ self.outfile.write(line.rstrip())
+ self.outfile.write("\n")
+
+ def report(self, morfs, outfile=None):
+ """Writes a report summarizing coverage statistics per module.
+
+ `outfile` is a file object to write the summary to. It must be opened
+ for native strings (bytes on Python 2, Unicode on Python 3).
+
+ """
+ self.outfile = outfile or sys.stdout
+
+ self.coverage.get_data().set_query_contexts(self.config.report_contexts)
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
+ self.report_one_file(fr, analysis)
+
+ # Prepare the formatting strings, header, and column sorting.
+ max_name = max([len(fr.relative_filename()) for (fr, analysis) in self.fr_analysis] + [5])
+ fmt_name = u"%%- %ds " % max_name
+ fmt_skip_covered = u"\n%s file%s skipped due to complete coverage."
+ fmt_skip_empty = u"\n%s empty file%s skipped."
+
+ header = (fmt_name % "Name") + u" Stmts Miss"
+ fmt_coverage = fmt_name + u"%6d %6d"
+ if self.branches:
+ header += u" Branch BrPart"
+ fmt_coverage += u" %6d %6d"
+ width100 = Numbers.pc_str_width()
+ header += u"%*s" % (width100+4, "Cover")
+ fmt_coverage += u"%%%ds%%%%" % (width100+3,)
+ if self.config.show_missing:
+ header += u" Missing"
+ fmt_coverage += u" %s"
+ rule = u"-" * len(header)
+
+ column_order = dict(name=0, stmts=1, miss=2, cover=-1)
+ if self.branches:
+ column_order.update(dict(branch=3, brpart=4))
+
+ # Write the header
+ self.writeout(header)
+ self.writeout(rule)
+
+ # `lines` is a list of pairs, (line text, line values). The line text
+ # is a string that will be printed, and line values is a tuple of
+ # sortable values.
+ lines = []
+
+ for (fr, analysis) in self.fr_analysis:
+ nums = analysis.numbers
+
+ args = (fr.relative_filename(), nums.n_statements, nums.n_missing)
+ if self.branches:
+ args += (nums.n_branches, nums.n_partial_branches)
+ args += (nums.pc_covered_str,)
+ if self.config.show_missing:
+ args += (analysis.missing_formatted(branches=True),)
+ text = fmt_coverage % args
+ # Add numeric percent coverage so that sorting makes sense.
+ args += (nums.pc_covered,)
+ lines.append((text, args))
+
+ # Sort the lines and write them out.
+ if getattr(self.config, 'sort', None):
+ sort_option = self.config.sort.lower()
+ reverse = False
+ if sort_option[0] == '-':
+ reverse = True
+ sort_option = sort_option[1:]
+ elif sort_option[0] == '+':
+ sort_option = sort_option[1:]
+
+ position = column_order.get(sort_option)
+ if position is None:
+ raise CoverageException("Invalid sorting option: {!r}".format(self.config.sort))
+ lines.sort(key=lambda l: (l[1][position], l[0]), reverse=reverse)
+
+ for line in lines:
+ self.writeout(line[0])
+
+ # Write a TOTAL line if we had at least one file.
+ if self.total.n_files > 0:
+ self.writeout(rule)
+ args = ("TOTAL", self.total.n_statements, self.total.n_missing)
+ if self.branches:
+ args += (self.total.n_branches, self.total.n_partial_branches)
+ args += (self.total.pc_covered_str,)
+ if self.config.show_missing:
+ args += ("",)
+ self.writeout(fmt_coverage % args)
+
+ # Write other final lines.
+ if not self.total.n_files and not self.skipped_count:
+ raise CoverageException("No data to report.")
+
+ if self.config.skip_covered and self.skipped_count:
+ self.writeout(
+ fmt_skip_covered % (self.skipped_count, 's' if self.skipped_count > 1 else '')
+ )
+ if self.config.skip_empty and self.empty_count:
+ self.writeout(
+ fmt_skip_empty % (self.empty_count, 's' if self.empty_count > 1 else '')
+ )
+
+ return self.total.n_statements and self.total.pc_covered
+
+ def report_one_file(self, fr, analysis):
+ """Report on just one file, the callback from report()."""
+ nums = analysis.numbers
+ self.total += nums
+
+ no_missing_lines = (nums.n_missing == 0)
+ no_missing_branches = (nums.n_partial_branches == 0)
+ if self.config.skip_covered and no_missing_lines and no_missing_branches:
+ # Don't report on 100% files.
+ self.skipped_count += 1
+ elif self.config.skip_empty and nums.n_statements == 0:
+ # Don't report on empty files.
+ self.empty_count += 1
+ else:
+ self.fr_analysis.append((fr, analysis))
diff --git a/contrib/python/coverage/py3/coverage/templite.py b/contrib/python/coverage/py3/coverage/templite.py
new file mode 100644
index 0000000000..7d4024e0af
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/templite.py
@@ -0,0 +1,302 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""A simple Python template renderer, for a nano-subset of Django syntax.
+
+For a detailed discussion of this code, see this chapter from 500 Lines:
+http://aosabook.org/en/500L/a-template-engine.html
+
+"""
+
+# Coincidentally named the same as http://code.activestate.com/recipes/496702/
+
+import re
+
+from coverage import env
+
+
+class TempliteSyntaxError(ValueError):
+ """Raised when a template has a syntax error."""
+ pass
+
+
+class TempliteValueError(ValueError):
+ """Raised when an expression won't evaluate in a template."""
+ pass
+
+
+class CodeBuilder(object):
+ """Build source code conveniently."""
+
+ def __init__(self, indent=0):
+ self.code = []
+ self.indent_level = indent
+
+ def __str__(self):
+ return "".join(str(c) for c in self.code)
+
+ def add_line(self, line):
+ """Add a line of source to the code.
+
+ Indentation and newline will be added for you, don't provide them.
+
+ """
+ self.code.extend([" " * self.indent_level, line, "\n"])
+
+ def add_section(self):
+ """Add a section, a sub-CodeBuilder."""
+ section = CodeBuilder(self.indent_level)
+ self.code.append(section)
+ return section
+
+ INDENT_STEP = 4 # PEP8 says so!
+
+ def indent(self):
+ """Increase the current indent for following lines."""
+ self.indent_level += self.INDENT_STEP
+
+ def dedent(self):
+ """Decrease the current indent for following lines."""
+ self.indent_level -= self.INDENT_STEP
+
+ def get_globals(self):
+ """Execute the code, and return a dict of globals it defines."""
+ # A check that the caller really finished all the blocks they started.
+ assert self.indent_level == 0
+ # Get the Python source as a single string.
+ python_source = str(self)
+ # Execute the source, defining globals, and return them.
+ global_namespace = {}
+ exec(python_source, global_namespace)
+ return global_namespace
+
+
+class Templite(object):
+ """A simple template renderer, for a nano-subset of Django syntax.
+
+ Supported constructs are extended variable access::
+
+ {{var.modifier.modifier|filter|filter}}
+
+ loops::
+
+ {% for var in list %}...{% endfor %}
+
+ and ifs::
+
+ {% if var %}...{% endif %}
+
+ Comments are within curly-hash markers::
+
+ {# This will be ignored #}
+
+ Lines between `{% joined %}` and `{% endjoined %}` will have lines stripped
+ and joined. Be careful, this could join words together!
+
+ Any of these constructs can have a hyphen at the end (`-}}`, `-%}`, `-#}`),
+ which will collapse the whitespace following the tag.
+
+ Construct a Templite with the template text, then use `render` against a
+ dictionary context to create a finished string::
+
+ templite = Templite('''
+ <h1>Hello {{name|upper}}!</h1>
+ {% for topic in topics %}
+ <p>You are interested in {{topic}}.</p>
+ {% endif %}
+ ''',
+ {'upper': str.upper},
+ )
+ text = templite.render({
+ 'name': "Ned",
+ 'topics': ['Python', 'Geometry', 'Juggling'],
+ })
+
+ """
+ def __init__(self, text, *contexts):
+ """Construct a Templite with the given `text`.
+
+ `contexts` are dictionaries of values to use for future renderings.
+ These are good for filters and global values.
+
+ """
+ self.context = {}
+ for context in contexts:
+ self.context.update(context)
+
+ self.all_vars = set()
+ self.loop_vars = set()
+
+ # We construct a function in source form, then compile it and hold onto
+ # it, and execute it to render the template.
+ code = CodeBuilder()
+
+ code.add_line("def render_function(context, do_dots):")
+ code.indent()
+ vars_code = code.add_section()
+ code.add_line("result = []")
+ code.add_line("append_result = result.append")
+ code.add_line("extend_result = result.extend")
+ if env.PY2:
+ code.add_line("to_str = unicode")
+ else:
+ code.add_line("to_str = str")
+
+ buffered = []
+
+ def flush_output():
+ """Force `buffered` to the code builder."""
+ if len(buffered) == 1:
+ code.add_line("append_result(%s)" % buffered[0])
+ elif len(buffered) > 1:
+ code.add_line("extend_result([%s])" % ", ".join(buffered))
+ del buffered[:]
+
+ ops_stack = []
+
+ # Split the text to form a list of tokens.
+ tokens = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
+
+ squash = in_joined = False
+
+ for token in tokens:
+ if token.startswith('{'):
+ start, end = 2, -2
+ squash = (token[-3] == '-')
+ if squash:
+ end = -3
+
+ if token.startswith('{#'):
+ # Comment: ignore it and move on.
+ continue
+ elif token.startswith('{{'):
+ # An expression to evaluate.
+ expr = self._expr_code(token[start:end].strip())
+ buffered.append("to_str(%s)" % expr)
+ else:
+ # token.startswith('{%')
+ # Action tag: split into words and parse further.
+ flush_output()
+
+ words = token[start:end].strip().split()
+ if words[0] == 'if':
+ # An if statement: evaluate the expression to determine if.
+ if len(words) != 2:
+ self._syntax_error("Don't understand if", token)
+ ops_stack.append('if')
+ code.add_line("if %s:" % self._expr_code(words[1]))
+ code.indent()
+ elif words[0] == 'for':
+ # A loop: iterate over expression result.
+ if len(words) != 4 or words[2] != 'in':
+ self._syntax_error("Don't understand for", token)
+ ops_stack.append('for')
+ self._variable(words[1], self.loop_vars)
+ code.add_line(
+ "for c_%s in %s:" % (
+ words[1],
+ self._expr_code(words[3])
+ )
+ )
+ code.indent()
+ elif words[0] == 'joined':
+ ops_stack.append('joined')
+ in_joined = True
+ elif words[0].startswith('end'):
+ # Endsomething. Pop the ops stack.
+ if len(words) != 1:
+ self._syntax_error("Don't understand end", token)
+ end_what = words[0][3:]
+ if not ops_stack:
+ self._syntax_error("Too many ends", token)
+ start_what = ops_stack.pop()
+ if start_what != end_what:
+ self._syntax_error("Mismatched end tag", end_what)
+ if end_what == 'joined':
+ in_joined = False
+ else:
+ code.dedent()
+ else:
+ self._syntax_error("Don't understand tag", words[0])
+ else:
+ # Literal content. If it isn't empty, output it.
+ if in_joined:
+ token = re.sub(r"\s*\n\s*", "", token.strip())
+ elif squash:
+ token = token.lstrip()
+ if token:
+ buffered.append(repr(token))
+
+ if ops_stack:
+ self._syntax_error("Unmatched action tag", ops_stack[-1])
+
+ flush_output()
+
+ for var_name in self.all_vars - self.loop_vars:
+ vars_code.add_line("c_%s = context[%r]" % (var_name, var_name))
+
+ code.add_line('return "".join(result)')
+ code.dedent()
+ self._render_function = code.get_globals()['render_function']
+
+ def _expr_code(self, expr):
+ """Generate a Python expression for `expr`."""
+ if "|" in expr:
+ pipes = expr.split("|")
+ code = self._expr_code(pipes[0])
+ for func in pipes[1:]:
+ self._variable(func, self.all_vars)
+ code = "c_%s(%s)" % (func, code)
+ elif "." in expr:
+ dots = expr.split(".")
+ code = self._expr_code(dots[0])
+ args = ", ".join(repr(d) for d in dots[1:])
+ code = "do_dots(%s, %s)" % (code, args)
+ else:
+ self._variable(expr, self.all_vars)
+ code = "c_%s" % expr
+ return code
+
+ def _syntax_error(self, msg, thing):
+ """Raise a syntax error using `msg`, and showing `thing`."""
+ raise TempliteSyntaxError("%s: %r" % (msg, thing))
+
+ def _variable(self, name, vars_set):
+ """Track that `name` is used as a variable.
+
+ Adds the name to `vars_set`, a set of variable names.
+
+ Raises an syntax error if `name` is not a valid name.
+
+ """
+ if not re.match(r"[_a-zA-Z][_a-zA-Z0-9]*$", name):
+ self._syntax_error("Not a valid name", name)
+ vars_set.add(name)
+
+ def render(self, context=None):
+ """Render this template by applying it to `context`.
+
+ `context` is a dictionary of values to use in this rendering.
+
+ """
+ # Make the complete context we'll use.
+ render_context = dict(self.context)
+ if context:
+ render_context.update(context)
+ return self._render_function(render_context, self._do_dots)
+
+ def _do_dots(self, value, *dots):
+ """Evaluate dotted expressions at run-time."""
+ for dot in dots:
+ try:
+ value = getattr(value, dot)
+ except AttributeError:
+ try:
+ value = value[dot]
+ except (TypeError, KeyError):
+ raise TempliteValueError(
+ "Couldn't evaluate %r.%s" % (value, dot)
+ )
+ if callable(value):
+ value = value()
+ return value
diff --git a/contrib/python/coverage/py3/coverage/tomlconfig.py b/contrib/python/coverage/py3/coverage/tomlconfig.py
new file mode 100644
index 0000000000..3ad581571c
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/tomlconfig.py
@@ -0,0 +1,168 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""TOML configuration support for coverage.py"""
+
+import io
+import os
+import re
+
+from coverage import env
+from coverage.backward import configparser, path_types
+from coverage.misc import CoverageException, substitute_variables
+
+# TOML support is an install-time extra option.
+try:
+ import toml
+except ImportError: # pragma: not covered
+ toml = None
+
+
+class TomlDecodeError(Exception):
+ """An exception class that exists even when toml isn't installed."""
+ pass
+
+
+class TomlConfigParser:
+ """TOML file reading with the interface of HandyConfigParser."""
+
+ # This class has the same interface as config.HandyConfigParser, no
+ # need for docstrings.
+ # pylint: disable=missing-function-docstring
+
+ def __init__(self, our_file):
+ self.our_file = our_file
+ self.data = None
+
+ def read(self, filenames):
+ # RawConfigParser takes a filename or list of filenames, but we only
+ # ever call this with a single filename.
+ assert isinstance(filenames, path_types)
+ filename = filenames
+ if env.PYVERSION >= (3, 6):
+ filename = os.fspath(filename)
+
+ try:
+ with io.open(filename, encoding='utf-8') as fp:
+ toml_text = fp.read()
+ except IOError:
+ return []
+ if toml:
+ toml_text = substitute_variables(toml_text, os.environ)
+ try:
+ self.data = toml.loads(toml_text)
+ except toml.TomlDecodeError as err:
+ raise TomlDecodeError(*err.args)
+ return [filename]
+ else:
+ has_toml = re.search(r"^\[tool\.coverage\.", toml_text, flags=re.MULTILINE)
+ if self.our_file or has_toml:
+ # Looks like they meant to read TOML, but we can't read it.
+ msg = "Can't read {!r} without TOML support. Install with [toml] extra"
+ raise CoverageException(msg.format(filename))
+ return []
+
+ def _get_section(self, section):
+ """Get a section from the data.
+
+ Arguments:
+ section (str): A section name, which can be dotted.
+
+ Returns:
+ name (str): the actual name of the section that was found, if any,
+ or None.
+ data (str): the dict of data in the section, or None if not found.
+
+ """
+ prefixes = ["tool.coverage."]
+ if self.our_file:
+ prefixes.append("")
+ for prefix in prefixes:
+ real_section = prefix + section
+ parts = real_section.split(".")
+ try:
+ data = self.data[parts[0]]
+ for part in parts[1:]:
+ data = data[part]
+ except KeyError:
+ continue
+ break
+ else:
+ return None, None
+ return real_section, data
+
+ def _get(self, section, option):
+ """Like .get, but returns the real section name and the value."""
+ name, data = self._get_section(section)
+ if data is None:
+ raise configparser.NoSectionError(section)
+ try:
+ return name, data[option]
+ except KeyError:
+ raise configparser.NoOptionError(option, name)
+
+ def has_option(self, section, option):
+ _, data = self._get_section(section)
+ if data is None:
+ return False
+ return option in data
+
+ def has_section(self, section):
+ name, _ = self._get_section(section)
+ return name
+
+ def options(self, section):
+ _, data = self._get_section(section)
+ if data is None:
+ raise configparser.NoSectionError(section)
+ return list(data.keys())
+
+ def get_section(self, section):
+ _, data = self._get_section(section)
+ return data
+
+ def get(self, section, option):
+ _, value = self._get(section, option)
+ return value
+
+ def _check_type(self, section, option, value, type_, type_desc):
+ if not isinstance(value, type_):
+ raise ValueError(
+ 'Option {!r} in section {!r} is not {}: {!r}'
+ .format(option, section, type_desc, value)
+ )
+
+ def getboolean(self, section, option):
+ name, value = self._get(section, option)
+ self._check_type(name, option, value, bool, "a boolean")
+ return value
+
+ def getlist(self, section, option):
+ name, values = self._get(section, option)
+ self._check_type(name, option, values, list, "a list")
+ return values
+
+ def getregexlist(self, section, option):
+ name, values = self._get(section, option)
+ self._check_type(name, option, values, list, "a list")
+ for value in values:
+ value = value.strip()
+ try:
+ re.compile(value)
+ except re.error as e:
+ raise CoverageException(
+ "Invalid [%s].%s value %r: %s" % (name, option, value, e)
+ )
+ return values
+
+ def getint(self, section, option):
+ name, value = self._get(section, option)
+ self._check_type(name, option, value, int, "an integer")
+ return value
+
+ def getfloat(self, section, option):
+ name, value = self._get(section, option)
+ if isinstance(value, int):
+ value = float(value)
+ self._check_type(name, option, value, float, "a float")
+ return value
diff --git a/contrib/python/coverage/py3/coverage/version.py b/contrib/python/coverage/py3/coverage/version.py
new file mode 100644
index 0000000000..d141a11da3
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/version.py
@@ -0,0 +1,33 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""The version and URL for coverage.py"""
+# This file is exec'ed in setup.py, don't import anything!
+
+# Same semantics as sys.version_info.
+version_info = (5, 5, 0, "final", 0)
+
+
+def _make_version(major, minor, micro, releaselevel, serial):
+ """Create a readable version string from version_info tuple components."""
+ assert releaselevel in ['alpha', 'beta', 'candidate', 'final']
+ version = "%d.%d" % (major, minor)
+ if micro:
+ version += ".%d" % (micro,)
+ if releaselevel != 'final':
+ short = {'alpha': 'a', 'beta': 'b', 'candidate': 'rc'}[releaselevel]
+ version += "%s%d" % (short, serial)
+ return version
+
+
+def _make_url(major, minor, micro, releaselevel, serial):
+ """Make the URL people should start at for this version of coverage.py."""
+ url = "https://coverage.readthedocs.io"
+ if releaselevel != 'final':
+ # For pre-releases, use a version-specific URL.
+ url += "/en/coverage-" + _make_version(major, minor, micro, releaselevel, serial)
+ return url
+
+
+__version__ = _make_version(*version_info)
+__url__ = _make_url(*version_info)
diff --git a/contrib/python/coverage/py3/coverage/xmlreport.py b/contrib/python/coverage/py3/coverage/xmlreport.py
new file mode 100644
index 0000000000..6d012ee692
--- /dev/null
+++ b/contrib/python/coverage/py3/coverage/xmlreport.py
@@ -0,0 +1,234 @@
+# coding: utf-8
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""XML reporting for coverage.py"""
+
+import os
+import os.path
+import sys
+import time
+import xml.dom.minidom
+
+from coverage import env
+from coverage import __url__, __version__, files
+from coverage.backward import iitems
+from coverage.misc import isolate_module
+from coverage.report import get_analysis_to_report
+
+os = isolate_module(os)
+
+
+DTD_URL = 'https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd'
+
+
+def rate(hit, num):
+ """Return the fraction of `hit`/`num`, as a string."""
+ if num == 0:
+ return "1"
+ else:
+ return "%.4g" % (float(hit) / num)
+
+
+class XmlReporter(object):
+ """A reporter for writing Cobertura-style XML coverage results."""
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+
+ self.source_paths = set()
+ if self.config.source:
+ for src in self.config.source:
+ if os.path.exists(src):
+ if not self.config.relative_files:
+ src = files.canonical_filename(src)
+ self.source_paths.add(src)
+ self.packages = {}
+ self.xml_out = None
+
+ def report(self, morfs, outfile=None):
+ """Generate a Cobertura-compatible XML report for `morfs`.
+
+ `morfs` is a list of modules or file names.
+
+ `outfile` is a file object to write the XML to.
+
+ """
+ # Initial setup.
+ outfile = outfile or sys.stdout
+ has_arcs = self.coverage.get_data().has_arcs()
+
+ # Create the DOM that will store the data.
+ impl = xml.dom.minidom.getDOMImplementation()
+ self.xml_out = impl.createDocument(None, "coverage", None)
+
+ # Write header stuff.
+ xcoverage = self.xml_out.documentElement
+ xcoverage.setAttribute("version", __version__)
+ xcoverage.setAttribute("timestamp", str(int(time.time()*1000)))
+ xcoverage.appendChild(self.xml_out.createComment(
+ " Generated by coverage.py: %s " % __url__
+ ))
+ xcoverage.appendChild(self.xml_out.createComment(" Based on %s " % DTD_URL))
+
+ # Call xml_file for each file in the data.
+ for fr, analysis in get_analysis_to_report(self.coverage, morfs):
+ self.xml_file(fr, analysis, has_arcs)
+
+ xsources = self.xml_out.createElement("sources")
+ xcoverage.appendChild(xsources)
+
+ # Populate the XML DOM with the source info.
+ for path in sorted(self.source_paths):
+ xsource = self.xml_out.createElement("source")
+ xsources.appendChild(xsource)
+ txt = self.xml_out.createTextNode(path)
+ xsource.appendChild(txt)
+
+ lnum_tot, lhits_tot = 0, 0
+ bnum_tot, bhits_tot = 0, 0
+
+ xpackages = self.xml_out.createElement("packages")
+ xcoverage.appendChild(xpackages)
+
+ # Populate the XML DOM with the package info.
+ for pkg_name, pkg_data in sorted(iitems(self.packages)):
+ class_elts, lhits, lnum, bhits, bnum = pkg_data
+ xpackage = self.xml_out.createElement("package")
+ xpackages.appendChild(xpackage)
+ xclasses = self.xml_out.createElement("classes")
+ xpackage.appendChild(xclasses)
+ for _, class_elt in sorted(iitems(class_elts)):
+ xclasses.appendChild(class_elt)
+ xpackage.setAttribute("name", pkg_name.replace(os.sep, '.'))
+ xpackage.setAttribute("line-rate", rate(lhits, lnum))
+ if has_arcs:
+ branch_rate = rate(bhits, bnum)
+ else:
+ branch_rate = "0"
+ xpackage.setAttribute("branch-rate", branch_rate)
+ xpackage.setAttribute("complexity", "0")
+
+ lnum_tot += lnum
+ lhits_tot += lhits
+ bnum_tot += bnum
+ bhits_tot += bhits
+
+ xcoverage.setAttribute("lines-valid", str(lnum_tot))
+ xcoverage.setAttribute("lines-covered", str(lhits_tot))
+ xcoverage.setAttribute("line-rate", rate(lhits_tot, lnum_tot))
+ if has_arcs:
+ xcoverage.setAttribute("branches-valid", str(bnum_tot))
+ xcoverage.setAttribute("branches-covered", str(bhits_tot))
+ xcoverage.setAttribute("branch-rate", rate(bhits_tot, bnum_tot))
+ else:
+ xcoverage.setAttribute("branches-covered", "0")
+ xcoverage.setAttribute("branches-valid", "0")
+ xcoverage.setAttribute("branch-rate", "0")
+ xcoverage.setAttribute("complexity", "0")
+
+ # Write the output file.
+ outfile.write(serialize_xml(self.xml_out))
+
+ # Return the total percentage.
+ denom = lnum_tot + bnum_tot
+ if denom == 0:
+ pct = 0.0
+ else:
+ pct = 100.0 * (lhits_tot + bhits_tot) / denom
+ return pct
+
+ def xml_file(self, fr, analysis, has_arcs):
+ """Add to the XML report for a single file."""
+
+ if self.config.skip_empty:
+ if analysis.numbers.n_statements == 0:
+ return
+
+ # Create the 'lines' and 'package' XML elements, which
+ # are populated later. Note that a package == a directory.
+ filename = fr.filename.replace("\\", "/")
+ for source_path in self.source_paths:
+ source_path = files.canonical_filename(source_path)
+ if filename.startswith(source_path.replace("\\", "/") + "/"):
+ rel_name = filename[len(source_path)+1:]
+ break
+ else:
+ rel_name = fr.relative_filename()
+ self.source_paths.add(fr.filename[:-len(rel_name)].rstrip(r"\/"))
+
+ dirname = os.path.dirname(rel_name) or u"."
+ dirname = "/".join(dirname.split("/")[:self.config.xml_package_depth])
+ package_name = dirname.replace("/", ".")
+
+ package = self.packages.setdefault(package_name, [{}, 0, 0, 0, 0])
+
+ xclass = self.xml_out.createElement("class")
+
+ xclass.appendChild(self.xml_out.createElement("methods"))
+
+ xlines = self.xml_out.createElement("lines")
+ xclass.appendChild(xlines)
+
+ xclass.setAttribute("name", os.path.relpath(rel_name, dirname))
+ xclass.setAttribute("filename", rel_name.replace("\\", "/"))
+ xclass.setAttribute("complexity", "0")
+
+ branch_stats = analysis.branch_stats()
+ missing_branch_arcs = analysis.missing_branch_arcs()
+
+ # For each statement, create an XML 'line' element.
+ for line in sorted(analysis.statements):
+ xline = self.xml_out.createElement("line")
+ xline.setAttribute("number", str(line))
+
+ # Q: can we get info about the number of times a statement is
+ # executed? If so, that should be recorded here.
+ xline.setAttribute("hits", str(int(line not in analysis.missing)))
+
+ if has_arcs:
+ if line in branch_stats:
+ total, taken = branch_stats[line]
+ xline.setAttribute("branch", "true")
+ xline.setAttribute(
+ "condition-coverage",
+ "%d%% (%d/%d)" % (100*taken//total, taken, total)
+ )
+ if line in missing_branch_arcs:
+ annlines = ["exit" if b < 0 else str(b) for b in missing_branch_arcs[line]]
+ xline.setAttribute("missing-branches", ",".join(annlines))
+ xlines.appendChild(xline)
+
+ class_lines = len(analysis.statements)
+ class_hits = class_lines - len(analysis.missing)
+
+ if has_arcs:
+ class_branches = sum(t for t, k in branch_stats.values())
+ missing_branches = sum(t - k for t, k in branch_stats.values())
+ class_br_hits = class_branches - missing_branches
+ else:
+ class_branches = 0.0
+ class_br_hits = 0.0
+
+ # Finalize the statistics that are collected in the XML DOM.
+ xclass.setAttribute("line-rate", rate(class_hits, class_lines))
+ if has_arcs:
+ branch_rate = rate(class_br_hits, class_branches)
+ else:
+ branch_rate = "0"
+ xclass.setAttribute("branch-rate", branch_rate)
+
+ package[0][rel_name] = xclass
+ package[1] += class_hits
+ package[2] += class_lines
+ package[3] += class_br_hits
+ package[4] += class_branches
+
+
+def serialize_xml(dom):
+ """Serialize a minidom node to XML."""
+ out = dom.toprettyxml()
+ if env.PY2:
+ out = out.encode("utf8")
+ return out
diff --git a/contrib/python/coverage/py3/ya.make b/contrib/python/coverage/py3/ya.make
new file mode 100644
index 0000000000..76625b48eb
--- /dev/null
+++ b/contrib/python/coverage/py3/ya.make
@@ -0,0 +1,98 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(5.5)
+
+LICENSE(Apache-2.0)
+
+PEERDIR(
+ contrib/python/coverage/plugins
+ library/python/resource
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_LINT()
+
+NO_CHECK_IMPORTS(
+ coverage.fullcoverage.encodings
+)
+
+SRCS(
+ coverage/ctracer/datastack.c
+ coverage/ctracer/filedisp.c
+ coverage/ctracer/module.c
+ coverage/ctracer/tracer.c
+)
+
+PY_REGISTER(
+ coverage.tracer
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ coverage/__init__.py
+ coverage/__main__.py
+ coverage/annotate.py
+ coverage/backward.py
+ coverage/bytecode.py
+ coverage/cmdline.py
+ coverage/collector.py
+ coverage/config.py
+ coverage/context.py
+ coverage/control.py
+ coverage/data.py
+ coverage/debug.py
+ coverage/disposition.py
+ coverage/env.py
+ coverage/execfile.py
+ coverage/files.py
+ coverage/fullcoverage/encodings.py
+ coverage/html.py
+ coverage/inorout.py
+ coverage/jsonreport.py
+ coverage/misc.py
+ coverage/multiproc.py
+ coverage/numbits.py
+ coverage/parser.py
+ coverage/phystokens.py
+ coverage/plugin.py
+ coverage/plugin_support.py
+ coverage/python.py
+ coverage/pytracer.py
+ coverage/report.py
+ coverage/results.py
+ coverage/sqldata.py
+ coverage/summary.py
+ coverage/templite.py
+ coverage/tomlconfig.py
+ coverage/version.py
+ coverage/xmlreport.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/coverage/py3/
+ .dist-info/METADATA
+ .dist-info/entry_points.txt
+ .dist-info/top_level.txt
+ coverage/htmlfiles/coverage_html.js
+ coverage/htmlfiles/favicon_32.png
+ coverage/htmlfiles/index.html
+ coverage/htmlfiles/jquery.ba-throttle-debounce.min.js
+ coverage/htmlfiles/jquery.hotkeys.js
+ coverage/htmlfiles/jquery.isonscreen.js
+ coverage/htmlfiles/jquery.min.js
+ coverage/htmlfiles/jquery.tablesorter.min.js
+ coverage/htmlfiles/keybd_closed.png
+ coverage/htmlfiles/keybd_open.png
+ coverage/htmlfiles/pyfile.html
+ coverage/htmlfiles/style.css
+ coverage/htmlfiles/style.scss
+)
+
+END()
+
+RECURSE(
+ bin
+)
diff --git a/contrib/python/coverage/ya.make b/contrib/python/coverage/ya.make
new file mode 100644
index 0000000000..f7202723cd
--- /dev/null
+++ b/contrib/python/coverage/ya.make
@@ -0,0 +1,19 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/coverage/py2)
+ELSE()
+ PEERDIR(contrib/python/coverage/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ plugins
+ py2
+ py3
+)
diff --git a/contrib/python/diff-match-patch/py2/.dist-info/METADATA b/contrib/python/diff-match-patch/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..ea1c571881
--- /dev/null
+++ b/contrib/python/diff-match-patch/py2/.dist-info/METADATA
@@ -0,0 +1,112 @@
+Metadata-Version: 2.1
+Name: diff-match-patch
+Version: 20200713
+Summary: Repackaging of Google's Diff Match and Patch libraries. Offers robust algorithms to perform the operations required for synchronizing plain text.
+Home-page: https://github.com/diff-match-patch-python/diff-match-patch
+Author: Neil Fraser
+Author-email: fraser@google.com
+Maintainer: John Reese
+Maintainer-email: john@noswap.com
+License: Apache
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Text Processing
+Requires-Python: >=2.7
+Description-Content-Type: text/markdown
+
+# diff-match-patch
+
+Google's [Diff Match and Patch][DMP] library, packaged for modern Python.
+
+[![build status](https://travis-ci.org/diff-match-patch-python/diff-match-patch.svg?branch=master)](https://travis-ci.org/diff-match-patch-python/diff-match-patch)
+[![version](https://img.shields.io/pypi/v/diff-match-patch.svg)](https://pypi.org/project/diff-match-patch)
+[![license](https://img.shields.io/pypi/l/diff-match-patch.svg)](https://github.com/diff-match-patch-python/diff-match-patch/blob/master/LICENSE)
+
+## Install
+
+diff-match-patch is supported on Python 2.7 or Python 3.4 or newer.
+You can install it from PyPI:
+
+```shell
+python -m pip install diff-match-patch
+```
+
+## Usage
+
+Generating a patchset (analogous to unified diff) between two texts:
+
+```python
+from diff_match_patch import diff_match_patch
+
+dmp = diff_match_patch()
+patches = dmp.patch_make(text1, text2)
+diff = dmp.patch_toText(patches)
+```
+
+Applying a patchset to a text can then be done with:
+
+```python
+from diff_match_patch import diff_match_patch
+
+dmp = diff_match_patch()
+patches = dmp.patch_fromText(diff)
+new_text, _ = dmp.patch_apply(patches, text)
+```
+
+## Original README
+The Diff Match and Patch libraries offer robust algorithms to perform the
+operations required for synchronizing plain text.
+
+1. Diff:
+ * Compare two blocks of plain text and efficiently return a list of differences.
+ * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
+2. Match:
+ * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
+ * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
+3. Patch:
+ * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
+ * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
+
+Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
+
+### Reference
+
+* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
+* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
+* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
+* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
+* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
+
+### Languages
+Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
+
+* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
+* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
+* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
+* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
+* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
+* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
+* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
+* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
+
+A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
+
+### Algorithms
+This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
+
+This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
+
+[DMP]: https://github.com/google/diff-match-patch
+[API]: https://github.com/google/diff-match-patch/wiki/API
+
+
diff --git a/contrib/python/diff-match-patch/py2/.dist-info/top_level.txt b/contrib/python/diff-match-patch/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..63904d71d9
--- /dev/null
+++ b/contrib/python/diff-match-patch/py2/.dist-info/top_level.txt
@@ -0,0 +1 @@
+diff_match_patch
diff --git a/contrib/python/diff-match-patch/py2/AUTHORS b/contrib/python/diff-match-patch/py2/AUTHORS
new file mode 100644
index 0000000000..c82809e726
--- /dev/null
+++ b/contrib/python/diff-match-patch/py2/AUTHORS
@@ -0,0 +1,10 @@
+# Below is a list of people and organizations that have contributed
+# to the Diff Match Patch project.
+
+Google Inc.
+
+Duncan Cross <duncan.cross@gmail.com> (Lua port)
+Jan Weiß <jan@geheimwerk.de> (Objective C port)
+Matthaeus G. Chajdas <anteru@developer.shelter13.net> (C# port)
+Mike Slemmer <mikeslemmer@gmail.com> (C++ port)
+
diff --git a/contrib/python/diff-match-patch/py2/LICENSE b/contrib/python/diff-match-patch/py2/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/contrib/python/diff-match-patch/py2/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/contrib/python/diff-match-patch/py2/README.md b/contrib/python/diff-match-patch/py2/README.md
new file mode 100644
index 0000000000..54f17337a7
--- /dev/null
+++ b/contrib/python/diff-match-patch/py2/README.md
@@ -0,0 +1,84 @@
+# diff-match-patch
+
+Google's [Diff Match and Patch][DMP] library, packaged for modern Python.
+
+[![build status](https://travis-ci.org/diff-match-patch-python/diff-match-patch.svg?branch=master)](https://travis-ci.org/diff-match-patch-python/diff-match-patch)
+[![version](https://img.shields.io/pypi/v/diff-match-patch.svg)](https://pypi.org/project/diff-match-patch)
+[![license](https://img.shields.io/pypi/l/diff-match-patch.svg)](https://github.com/diff-match-patch-python/diff-match-patch/blob/master/LICENSE)
+
+## Install
+
+diff-match-patch is supported on Python 2.7 or Python 3.4 or newer.
+You can install it from PyPI:
+
+```shell
+python -m pip install diff-match-patch
+```
+
+## Usage
+
+Generating a patchset (analogous to unified diff) between two texts:
+
+```python
+from diff_match_patch import diff_match_patch
+
+dmp = diff_match_patch()
+patches = dmp.patch_make(text1, text2)
+diff = dmp.patch_toText(patches)
+```
+
+Applying a patchset to a text can then be done with:
+
+```python
+from diff_match_patch import diff_match_patch
+
+dmp = diff_match_patch()
+patches = dmp.patch_fromText(diff)
+new_text, _ = dmp.patch_apply(patches, text)
+```
+
+## Original README
+The Diff Match and Patch libraries offer robust algorithms to perform the
+operations required for synchronizing plain text.
+
+1. Diff:
+ * Compare two blocks of plain text and efficiently return a list of differences.
+ * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
+2. Match:
+ * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
+ * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
+3. Patch:
+ * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
+ * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
+
+Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
+
+### Reference
+
+* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
+* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
+* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
+* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
+* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
+
+### Languages
+Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
+
+* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
+* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
+* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
+* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
+* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
+* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
+* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
+* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
+
+A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
+
+### Algorithms
+This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
+
+This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
+
+[DMP]: https://github.com/google/diff-match-patch
+[API]: https://github.com/google/diff-match-patch/wiki/API
diff --git a/contrib/python/diff-match-patch/py2/diff_match_patch/__init__.py b/contrib/python/diff-match-patch/py2/diff_match_patch/__init__.py
new file mode 100644
index 0000000000..37e762a3b1
--- /dev/null
+++ b/contrib/python/diff-match-patch/py2/diff_match_patch/__init__.py
@@ -0,0 +1,9 @@
+import sys
+
+if sys.version_info >= (3, 0):
+ from .diff_match_patch import __author__, __doc__, diff_match_patch, patch_obj
+else:
+ from .diff_match_patch_py2 import __author__, __doc__, diff_match_patch, patch_obj
+
+__version__ = "20200713"
+__packager__ = "John Reese (john@noswap.com)"
diff --git a/contrib/python/diff-match-patch/py2/diff_match_patch/diff_match_patch_py2.py b/contrib/python/diff-match-patch/py2/diff_match_patch/diff_match_patch_py2.py
new file mode 100644
index 0000000000..5bfe9688a2
--- /dev/null
+++ b/contrib/python/diff-match-patch/py2/diff_match_patch/diff_match_patch_py2.py
@@ -0,0 +1,2037 @@
+#!/usr/bin/python2.4
+
+from __future__ import division
+
+"""Diff Match and Patch
+Copyright 2018 The diff-match-patch Authors.
+https://github.com/google/diff-match-patch
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+"""Functions for diff, match and patch.
+
+Computes the difference between two texts to create a patch.
+Applies the patch onto another text, allowing for errors.
+"""
+
+__author__ = "fraser@google.com (Neil Fraser)"
+
+import re
+import sys
+import time
+import urllib
+
+
+class diff_match_patch:
+ """Class containing the diff, match and patch methods.
+
+ Also contains the behaviour settings.
+ """
+
+ def __init__(self):
+ """Inits a diff_match_patch object with default settings.
+ Redefine these in your program to override the defaults.
+ """
+
+ # Number of seconds to map a diff before giving up (0 for infinity).
+ self.Diff_Timeout = 1.0
+ # Cost of an empty edit operation in terms of edit characters.
+ self.Diff_EditCost = 4
+ # At what point is no match declared (0.0 = perfection, 1.0 = very loose).
+ self.Match_Threshold = 0.5
+ # How far to search for a match (0 = exact location, 1000+ = broad match).
+ # A match this many characters away from the expected location will add
+ # 1.0 to the score (0.0 is a perfect match).
+ self.Match_Distance = 1000
+ # When deleting a large block of text (over ~64 characters), how close do
+ # the contents have to be to match the expected contents. (0.0 = perfection,
+ # 1.0 = very loose). Note that Match_Threshold controls how closely the
+ # end points of a delete need to match.
+ self.Patch_DeleteThreshold = 0.5
+ # Chunk size for context length.
+ self.Patch_Margin = 4
+
+ # The number of bits in an int.
+ # Python has no maximum, thus to disable patch splitting set to 0.
+ # However to avoid long patches in certain pathological cases, use 32.
+ # Multiple short patches (using native ints) are much faster than long ones.
+ self.Match_MaxBits = 32
+
+ # DIFF FUNCTIONS
+
+ # The data structure representing a diff is an array of tuples:
+ # [(DIFF_DELETE, "Hello"), (DIFF_INSERT, "Goodbye"), (DIFF_EQUAL, " world.")]
+ # which means: delete "Hello", add "Goodbye" and keep " world."
+ DIFF_DELETE = -1
+ DIFF_INSERT = 1
+ DIFF_EQUAL = 0
+
+ def diff_main(self, text1, text2, checklines=True, deadline=None):
+ """Find the differences between two texts. Simplifies the problem by
+ stripping any common prefix or suffix off the texts before diffing.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ checklines: Optional speedup flag. If present and false, then don't run
+ a line-level diff first to identify the changed areas.
+ Defaults to true, which does a faster, slightly less optimal diff.
+ deadline: Optional time when the diff should be complete by. Used
+ internally for recursive calls. Users should set DiffTimeout instead.
+
+ Returns:
+ Array of changes.
+ """
+ # Set a deadline by which time the diff must be complete.
+ if deadline == None:
+ # Unlike in most languages, Python counts time in seconds.
+ if self.Diff_Timeout <= 0:
+ deadline = sys.maxint
+ else:
+ deadline = time.time() + self.Diff_Timeout
+
+ # Check for null inputs.
+ if text1 == None or text2 == None:
+ raise ValueError("Null inputs. (diff_main)")
+
+ # Check for equality (speedup).
+ if text1 == text2:
+ if text1:
+ return [(self.DIFF_EQUAL, text1)]
+ return []
+
+ # Trim off common prefix (speedup).
+ commonlength = self.diff_commonPrefix(text1, text2)
+ commonprefix = text1[:commonlength]
+ text1 = text1[commonlength:]
+ text2 = text2[commonlength:]
+
+ # Trim off common suffix (speedup).
+ commonlength = self.diff_commonSuffix(text1, text2)
+ if commonlength == 0:
+ commonsuffix = ""
+ else:
+ commonsuffix = text1[-commonlength:]
+ text1 = text1[:-commonlength]
+ text2 = text2[:-commonlength]
+
+ # Compute the diff on the middle block.
+ diffs = self.diff_compute(text1, text2, checklines, deadline)
+
+ # Restore the prefix and suffix.
+ if commonprefix:
+ diffs[:0] = [(self.DIFF_EQUAL, commonprefix)]
+ if commonsuffix:
+ diffs.append((self.DIFF_EQUAL, commonsuffix))
+ self.diff_cleanupMerge(diffs)
+ return diffs
+
+ def diff_compute(self, text1, text2, checklines, deadline):
+ """Find the differences between two texts. Assumes that the texts do not
+ have any common prefix or suffix.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ checklines: Speedup flag. If false, then don't run a line-level diff
+ first to identify the changed areas.
+ If true, then run a faster, slightly less optimal diff.
+ deadline: Time when the diff should be complete by.
+
+ Returns:
+ Array of changes.
+ """
+ if not text1:
+ # Just add some text (speedup).
+ return [(self.DIFF_INSERT, text2)]
+
+ if not text2:
+ # Just delete some text (speedup).
+ return [(self.DIFF_DELETE, text1)]
+
+ if len(text1) > len(text2):
+ (longtext, shorttext) = (text1, text2)
+ else:
+ (shorttext, longtext) = (text1, text2)
+ i = longtext.find(shorttext)
+ if i != -1:
+ # Shorter text is inside the longer text (speedup).
+ diffs = [
+ (self.DIFF_INSERT, longtext[:i]),
+ (self.DIFF_EQUAL, shorttext),
+ (self.DIFF_INSERT, longtext[i + len(shorttext) :]),
+ ]
+ # Swap insertions for deletions if diff is reversed.
+ if len(text1) > len(text2):
+ diffs[0] = (self.DIFF_DELETE, diffs[0][1])
+ diffs[2] = (self.DIFF_DELETE, diffs[2][1])
+ return diffs
+
+ if len(shorttext) == 1:
+ # Single character string.
+ # After the previous speedup, the character can't be an equality.
+ return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]
+
+ # Check to see if the problem can be split in two.
+ hm = self.diff_halfMatch(text1, text2)
+ if hm:
+ # A half-match was found, sort out the return data.
+ (text1_a, text1_b, text2_a, text2_b, mid_common) = hm
+ # Send both pairs off for separate processing.
+ diffs_a = self.diff_main(text1_a, text2_a, checklines, deadline)
+ diffs_b = self.diff_main(text1_b, text2_b, checklines, deadline)
+ # Merge the results.
+ return diffs_a + [(self.DIFF_EQUAL, mid_common)] + diffs_b
+
+ if checklines and len(text1) > 100 and len(text2) > 100:
+ return self.diff_lineMode(text1, text2, deadline)
+
+ return self.diff_bisect(text1, text2, deadline)
+
+ def diff_lineMode(self, text1, text2, deadline):
+ """Do a quick line-level diff on both strings, then rediff the parts for
+ greater accuracy.
+ This speedup can produce non-minimal diffs.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ deadline: Time when the diff should be complete by.
+
+ Returns:
+ Array of changes.
+ """
+
+ # Scan the text on a line-by-line basis first.
+ (text1, text2, linearray) = self.diff_linesToChars(text1, text2)
+
+ diffs = self.diff_main(text1, text2, False, deadline)
+
+ # Convert the diff back to original text.
+ self.diff_charsToLines(diffs, linearray)
+ # Eliminate freak matches (e.g. blank lines)
+ self.diff_cleanupSemantic(diffs)
+
+ # Rediff any replacement blocks, this time character-by-character.
+ # Add a dummy entry at the end.
+ diffs.append((self.DIFF_EQUAL, ""))
+ pointer = 0
+ count_delete = 0
+ count_insert = 0
+ text_delete = ""
+ text_insert = ""
+ while pointer < len(diffs):
+ if diffs[pointer][0] == self.DIFF_INSERT:
+ count_insert += 1
+ text_insert += diffs[pointer][1]
+ elif diffs[pointer][0] == self.DIFF_DELETE:
+ count_delete += 1
+ text_delete += diffs[pointer][1]
+ elif diffs[pointer][0] == self.DIFF_EQUAL:
+ # Upon reaching an equality, check for prior redundancies.
+ if count_delete >= 1 and count_insert >= 1:
+ # Delete the offending records and add the merged ones.
+ subDiff = self.diff_main(text_delete, text_insert, False, deadline)
+ diffs[pointer - count_delete - count_insert : pointer] = subDiff
+ pointer = pointer - count_delete - count_insert + len(subDiff)
+ count_insert = 0
+ count_delete = 0
+ text_delete = ""
+ text_insert = ""
+
+ pointer += 1
+
+ diffs.pop() # Remove the dummy entry at the end.
+
+ return diffs
+
+ def diff_bisect(self, text1, text2, deadline):
+ """Find the 'middle snake' of a diff, split the problem in two
+ and return the recursively constructed diff.
+ See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ deadline: Time at which to bail if not yet complete.
+
+ Returns:
+ Array of diff tuples.
+ """
+
+ # Cache the text lengths to prevent multiple calls.
+ text1_length = len(text1)
+ text2_length = len(text2)
+ max_d = (text1_length + text2_length + 1) // 2
+ v_offset = max_d
+ v_length = 2 * max_d
+ v1 = [-1] * v_length
+ v1[v_offset + 1] = 0
+ v2 = v1[:]
+ delta = text1_length - text2_length
+ # If the total number of characters is odd, then the front path will
+ # collide with the reverse path.
+ front = delta % 2 != 0
+ # Offsets for start and end of k loop.
+ # Prevents mapping of space beyond the grid.
+ k1start = 0
+ k1end = 0
+ k2start = 0
+ k2end = 0
+ for d in xrange(max_d):
+ # Bail out if deadline is reached.
+ if time.time() > deadline:
+ break
+
+ # Walk the front path one step.
+ for k1 in xrange(-d + k1start, d + 1 - k1end, 2):
+ k1_offset = v_offset + k1
+ if k1 == -d or (k1 != d and v1[k1_offset - 1] < v1[k1_offset + 1]):
+ x1 = v1[k1_offset + 1]
+ else:
+ x1 = v1[k1_offset - 1] + 1
+ y1 = x1 - k1
+ while (
+ x1 < text1_length and y1 < text2_length and text1[x1] == text2[y1]
+ ):
+ x1 += 1
+ y1 += 1
+ v1[k1_offset] = x1
+ if x1 > text1_length:
+ # Ran off the right of the graph.
+ k1end += 2
+ elif y1 > text2_length:
+ # Ran off the bottom of the graph.
+ k1start += 2
+ elif front:
+ k2_offset = v_offset + delta - k1
+ if k2_offset >= 0 and k2_offset < v_length and v2[k2_offset] != -1:
+ # Mirror x2 onto top-left coordinate system.
+ x2 = text1_length - v2[k2_offset]
+ if x1 >= x2:
+ # Overlap detected.
+ return self.diff_bisectSplit(text1, text2, x1, y1, deadline)
+
+ # Walk the reverse path one step.
+ for k2 in xrange(-d + k2start, d + 1 - k2end, 2):
+ k2_offset = v_offset + k2
+ if k2 == -d or (k2 != d and v2[k2_offset - 1] < v2[k2_offset + 1]):
+ x2 = v2[k2_offset + 1]
+ else:
+ x2 = v2[k2_offset - 1] + 1
+ y2 = x2 - k2
+ while (
+ x2 < text1_length
+ and y2 < text2_length
+ and text1[-x2 - 1] == text2[-y2 - 1]
+ ):
+ x2 += 1
+ y2 += 1
+ v2[k2_offset] = x2
+ if x2 > text1_length:
+ # Ran off the left of the graph.
+ k2end += 2
+ elif y2 > text2_length:
+ # Ran off the top of the graph.
+ k2start += 2
+ elif not front:
+ k1_offset = v_offset + delta - k2
+ if k1_offset >= 0 and k1_offset < v_length and v1[k1_offset] != -1:
+ x1 = v1[k1_offset]
+ y1 = v_offset + x1 - k1_offset
+ # Mirror x2 onto top-left coordinate system.
+ x2 = text1_length - x2
+ if x1 >= x2:
+ # Overlap detected.
+ return self.diff_bisectSplit(text1, text2, x1, y1, deadline)
+
+ # Diff took too long and hit the deadline or
+ # number of diffs equals number of characters, no commonality at all.
+ return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]
+
+ def diff_bisectSplit(self, text1, text2, x, y, deadline):
+ """Given the location of the 'middle snake', split the diff in two parts
+ and recurse.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ x: Index of split point in text1.
+ y: Index of split point in text2.
+ deadline: Time at which to bail if not yet complete.
+
+ Returns:
+ Array of diff tuples.
+ """
+ text1a = text1[:x]
+ text2a = text2[:y]
+ text1b = text1[x:]
+ text2b = text2[y:]
+
+ # Compute both diffs serially.
+ diffs = self.diff_main(text1a, text2a, False, deadline)
+ diffsb = self.diff_main(text1b, text2b, False, deadline)
+
+ return diffs + diffsb
+
+ def diff_linesToChars(self, text1, text2):
+ """Split two texts into an array of strings. Reduce the texts to a string
+ of hashes where each Unicode character represents one line.
+
+ Args:
+ text1: First string.
+ text2: Second string.
+
+ Returns:
+ Three element tuple, containing the encoded text1, the encoded text2 and
+ the array of unique strings. The zeroth element of the array of unique
+ strings is intentionally blank.
+ """
+ lineArray = [] # e.g. lineArray[4] == "Hello\n"
+ lineHash = {} # e.g. lineHash["Hello\n"] == 4
+
+ # "\x00" is a valid character, but various debuggers don't like it.
+ # So we'll insert a junk entry to avoid generating a null character.
+ lineArray.append("")
+
+ def diff_linesToCharsMunge(text):
+ """Split a text into an array of strings. Reduce the texts to a string
+ of hashes where each Unicode character represents one line.
+ Modifies linearray and linehash through being a closure.
+
+ Args:
+ text: String to encode.
+
+ Returns:
+ Encoded string.
+ """
+ chars = []
+ # Walk the text, pulling out a substring for each line.
+ # text.split('\n') would would temporarily double our memory footprint.
+ # Modifying text would create many large strings to garbage collect.
+ lineStart = 0
+ lineEnd = -1
+ while lineEnd < len(text) - 1:
+ lineEnd = text.find("\n", lineStart)
+ if lineEnd == -1:
+ lineEnd = len(text) - 1
+ line = text[lineStart : lineEnd + 1]
+
+ if line in lineHash:
+ chars.append(unichr(lineHash[line]))
+ else:
+ if len(lineArray) == maxLines:
+ # Bail out at 65535 because unichr(65536) throws.
+ line = text[lineStart:]
+ lineEnd = len(text)
+ lineArray.append(line)
+ lineHash[line] = len(lineArray) - 1
+ chars.append(unichr(len(lineArray) - 1))
+ lineStart = lineEnd + 1
+ return "".join(chars)
+
+ # Allocate 2/3rds of the space for text1, the rest for text2.
+ maxLines = 40000
+ chars1 = diff_linesToCharsMunge(text1)
+ maxLines = 65535
+ chars2 = diff_linesToCharsMunge(text2)
+ return (chars1, chars2, lineArray)
+
+ def diff_charsToLines(self, diffs, lineArray):
+ """Rehydrate the text in a diff from a string of line hashes to real lines
+ of text.
+
+ Args:
+ diffs: Array of diff tuples.
+ lineArray: Array of unique strings.
+ """
+ for i in xrange(len(diffs)):
+ text = []
+ for char in diffs[i][1]:
+ text.append(lineArray[ord(char)])
+ diffs[i] = (diffs[i][0], "".join(text))
+
+ def diff_commonPrefix(self, text1, text2):
+ """Determine the common prefix of two strings.
+
+ Args:
+ text1: First string.
+ text2: Second string.
+
+ Returns:
+ The number of characters common to the start of each string.
+ """
+ # Quick check for common null cases.
+ if not text1 or not text2 or text1[0] != text2[0]:
+ return 0
+ # Binary search.
+ # Performance analysis: https://neil.fraser.name/news/2007/10/09/
+ pointermin = 0
+ pointermax = min(len(text1), len(text2))
+ pointermid = pointermax
+ pointerstart = 0
+ while pointermin < pointermid:
+ if text1[pointerstart:pointermid] == text2[pointerstart:pointermid]:
+ pointermin = pointermid
+ pointerstart = pointermin
+ else:
+ pointermax = pointermid
+ pointermid = (pointermax - pointermin) // 2 + pointermin
+ return pointermid
+
+ def diff_commonSuffix(self, text1, text2):
+ """Determine the common suffix of two strings.
+
+ Args:
+ text1: First string.
+ text2: Second string.
+
+ Returns:
+ The number of characters common to the end of each string.
+ """
+ # Quick check for common null cases.
+ if not text1 or not text2 or text1[-1] != text2[-1]:
+ return 0
+ # Binary search.
+ # Performance analysis: https://neil.fraser.name/news/2007/10/09/
+ pointermin = 0
+ pointermax = min(len(text1), len(text2))
+ pointermid = pointermax
+ pointerend = 0
+ while pointermin < pointermid:
+ if (
+ text1[-pointermid : len(text1) - pointerend]
+ == text2[-pointermid : len(text2) - pointerend]
+ ):
+ pointermin = pointermid
+ pointerend = pointermin
+ else:
+ pointermax = pointermid
+ pointermid = (pointermax - pointermin) // 2 + pointermin
+ return pointermid
+
+ def diff_commonOverlap(self, text1, text2):
+ """Determine if the suffix of one string is the prefix of another.
+
+ Args:
+ text1 First string.
+ text2 Second string.
+
+ Returns:
+ The number of characters common to the end of the first
+ string and the start of the second string.
+ """
+ # Cache the text lengths to prevent multiple calls.
+ text1_length = len(text1)
+ text2_length = len(text2)
+ # Eliminate the null case.
+ if text1_length == 0 or text2_length == 0:
+ return 0
+ # Truncate the longer string.
+ if text1_length > text2_length:
+ text1 = text1[-text2_length:]
+ elif text1_length < text2_length:
+ text2 = text2[:text1_length]
+ text_length = min(text1_length, text2_length)
+ # Quick check for the worst case.
+ if text1 == text2:
+ return text_length
+
+ # Start by looking for a single character match
+ # and increase length until no match is found.
+ # Performance analysis: https://neil.fraser.name/news/2010/11/04/
+ best = 0
+ length = 1
+ while True:
+ pattern = text1[-length:]
+ found = text2.find(pattern)
+ if found == -1:
+ return best
+ length += found
+ if found == 0 or text1[-length:] == text2[:length]:
+ best = length
+ length += 1
+
+ def diff_halfMatch(self, text1, text2):
+ """Do the two texts share a substring which is at least half the length of
+ the longer text?
+ This speedup can produce non-minimal diffs.
+
+ Args:
+ text1: First string.
+ text2: Second string.
+
+ Returns:
+ Five element Array, containing the prefix of text1, the suffix of text1,
+ the prefix of text2, the suffix of text2 and the common middle. Or None
+ if there was no match.
+ """
+ if self.Diff_Timeout <= 0:
+ # Don't risk returning a non-optimal diff if we have unlimited time.
+ return None
+ if len(text1) > len(text2):
+ (longtext, shorttext) = (text1, text2)
+ else:
+ (shorttext, longtext) = (text1, text2)
+ if len(longtext) < 4 or len(shorttext) * 2 < len(longtext):
+ return None # Pointless.
+
+ def diff_halfMatchI(longtext, shorttext, i):
+ """Does a substring of shorttext exist within longtext such that the
+ substring is at least half the length of longtext?
+ Closure, but does not reference any external variables.
+
+ Args:
+ longtext: Longer string.
+ shorttext: Shorter string.
+ i: Start index of quarter length substring within longtext.
+
+ Returns:
+ Five element Array, containing the prefix of longtext, the suffix of
+ longtext, the prefix of shorttext, the suffix of shorttext and the
+ common middle. Or None if there was no match.
+ """
+ seed = longtext[i : i + len(longtext) // 4]
+ best_common = ""
+ j = shorttext.find(seed)
+ while j != -1:
+ prefixLength = self.diff_commonPrefix(longtext[i:], shorttext[j:])
+ suffixLength = self.diff_commonSuffix(longtext[:i], shorttext[:j])
+ if len(best_common) < suffixLength + prefixLength:
+ best_common = (
+ shorttext[j - suffixLength : j]
+ + shorttext[j : j + prefixLength]
+ )
+ best_longtext_a = longtext[: i - suffixLength]
+ best_longtext_b = longtext[i + prefixLength :]
+ best_shorttext_a = shorttext[: j - suffixLength]
+ best_shorttext_b = shorttext[j + prefixLength :]
+ j = shorttext.find(seed, j + 1)
+
+ if len(best_common) * 2 >= len(longtext):
+ return (
+ best_longtext_a,
+ best_longtext_b,
+ best_shorttext_a,
+ best_shorttext_b,
+ best_common,
+ )
+ else:
+ return None
+
+ # First check if the second quarter is the seed for a half-match.
+ hm1 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 3) // 4)
+ # Check again based on the third quarter.
+ hm2 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 1) // 2)
+ if not hm1 and not hm2:
+ return None
+ elif not hm2:
+ hm = hm1
+ elif not hm1:
+ hm = hm2
+ else:
+ # Both matched. Select the longest.
+ if len(hm1[4]) > len(hm2[4]):
+ hm = hm1
+ else:
+ hm = hm2
+
+ # A half-match was found, sort out the return data.
+ if len(text1) > len(text2):
+ (text1_a, text1_b, text2_a, text2_b, mid_common) = hm
+ else:
+ (text2_a, text2_b, text1_a, text1_b, mid_common) = hm
+ return (text1_a, text1_b, text2_a, text2_b, mid_common)
+
+ def diff_cleanupSemantic(self, diffs):
+ """Reduce the number of edits by eliminating semantically trivial
+ equalities.
+
+ Args:
+ diffs: Array of diff tuples.
+ """
+ changes = False
+ equalities = [] # Stack of indices where equalities are found.
+ lastEquality = None # Always equal to diffs[equalities[-1]][1]
+ pointer = 0 # Index of current position.
+ # Number of chars that changed prior to the equality.
+ length_insertions1, length_deletions1 = 0, 0
+ # Number of chars that changed after the equality.
+ length_insertions2, length_deletions2 = 0, 0
+ while pointer < len(diffs):
+ if diffs[pointer][0] == self.DIFF_EQUAL: # Equality found.
+ equalities.append(pointer)
+ length_insertions1, length_insertions2 = length_insertions2, 0
+ length_deletions1, length_deletions2 = length_deletions2, 0
+ lastEquality = diffs[pointer][1]
+ else: # An insertion or deletion.
+ if diffs[pointer][0] == self.DIFF_INSERT:
+ length_insertions2 += len(diffs[pointer][1])
+ else:
+ length_deletions2 += len(diffs[pointer][1])
+ # Eliminate an equality that is smaller or equal to the edits on both
+ # sides of it.
+ if (
+ lastEquality
+ and (
+ len(lastEquality) <= max(length_insertions1, length_deletions1)
+ )
+ and (
+ len(lastEquality) <= max(length_insertions2, length_deletions2)
+ )
+ ):
+ # Duplicate record.
+ diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))
+ # Change second copy to insert.
+ diffs[equalities[-1] + 1] = (
+ self.DIFF_INSERT,
+ diffs[equalities[-1] + 1][1],
+ )
+ # Throw away the equality we just deleted.
+ equalities.pop()
+ # Throw away the previous equality (it needs to be reevaluated).
+ if len(equalities):
+ equalities.pop()
+ if len(equalities):
+ pointer = equalities[-1]
+ else:
+ pointer = -1
+ # Reset the counters.
+ length_insertions1, length_deletions1 = 0, 0
+ length_insertions2, length_deletions2 = 0, 0
+ lastEquality = None
+ changes = True
+ pointer += 1
+
+ # Normalize the diff.
+ if changes:
+ self.diff_cleanupMerge(diffs)
+ self.diff_cleanupSemanticLossless(diffs)
+
+ # Find any overlaps between deletions and insertions.
+ # e.g: <del>abcxxx</del><ins>xxxdef</ins>
+ # -> <del>abc</del>xxx<ins>def</ins>
+ # e.g: <del>xxxabc</del><ins>defxxx</ins>
+ # -> <ins>def</ins>xxx<del>abc</del>
+ # Only extract an overlap if it is as big as the edit ahead or behind it.
+ pointer = 1
+ while pointer < len(diffs):
+ if (
+ diffs[pointer - 1][0] == self.DIFF_DELETE
+ and diffs[pointer][0] == self.DIFF_INSERT
+ ):
+ deletion = diffs[pointer - 1][1]
+ insertion = diffs[pointer][1]
+ overlap_length1 = self.diff_commonOverlap(deletion, insertion)
+ overlap_length2 = self.diff_commonOverlap(insertion, deletion)
+ if overlap_length1 >= overlap_length2:
+ if (
+ overlap_length1 >= len(deletion) / 2.0
+ or overlap_length1 >= len(insertion) / 2.0
+ ):
+ # Overlap found. Insert an equality and trim the surrounding edits.
+ diffs.insert(
+ pointer, (self.DIFF_EQUAL, insertion[:overlap_length1])
+ )
+ diffs[pointer - 1] = (
+ self.DIFF_DELETE,
+ deletion[: len(deletion) - overlap_length1],
+ )
+ diffs[pointer + 1] = (
+ self.DIFF_INSERT,
+ insertion[overlap_length1:],
+ )
+ pointer += 1
+ else:
+ if (
+ overlap_length2 >= len(deletion) / 2.0
+ or overlap_length2 >= len(insertion) / 2.0
+ ):
+ # Reverse overlap found.
+ # Insert an equality and swap and trim the surrounding edits.
+ diffs.insert(
+ pointer, (self.DIFF_EQUAL, deletion[:overlap_length2])
+ )
+ diffs[pointer - 1] = (
+ self.DIFF_INSERT,
+ insertion[: len(insertion) - overlap_length2],
+ )
+ diffs[pointer + 1] = (
+ self.DIFF_DELETE,
+ deletion[overlap_length2:],
+ )
+ pointer += 1
+ pointer += 1
+ pointer += 1
+
+ def diff_cleanupSemanticLossless(self, diffs):
+ """Look for single edits surrounded on both sides by equalities
+ which can be shifted sideways to align the edit to a word boundary.
+ e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
+
+ Args:
+ diffs: Array of diff tuples.
+ """
+
+ def diff_cleanupSemanticScore(one, two):
+ """Given two strings, compute a score representing whether the
+ internal boundary falls on logical boundaries.
+ Scores range from 6 (best) to 0 (worst).
+ Closure, but does not reference any external variables.
+
+ Args:
+ one: First string.
+ two: Second string.
+
+ Returns:
+ The score.
+ """
+ if not one or not two:
+ # Edges are the best.
+ return 6
+
+ # Each port of this function behaves slightly differently due to
+ # subtle differences in each language's definition of things like
+ # 'whitespace'. Since this function's purpose is largely cosmetic,
+ # the choice has been made to use each language's native features
+ # rather than force total conformity.
+ char1 = one[-1]
+ char2 = two[0]
+ nonAlphaNumeric1 = not char1.isalnum()
+ nonAlphaNumeric2 = not char2.isalnum()
+ whitespace1 = nonAlphaNumeric1 and char1.isspace()
+ whitespace2 = nonAlphaNumeric2 and char2.isspace()
+ lineBreak1 = whitespace1 and (char1 == "\r" or char1 == "\n")
+ lineBreak2 = whitespace2 and (char2 == "\r" or char2 == "\n")
+ blankLine1 = lineBreak1 and self.BLANKLINEEND.search(one)
+ blankLine2 = lineBreak2 and self.BLANKLINESTART.match(two)
+
+ if blankLine1 or blankLine2:
+ # Five points for blank lines.
+ return 5
+ elif lineBreak1 or lineBreak2:
+ # Four points for line breaks.
+ return 4
+ elif nonAlphaNumeric1 and not whitespace1 and whitespace2:
+ # Three points for end of sentences.
+ return 3
+ elif whitespace1 or whitespace2:
+ # Two points for whitespace.
+ return 2
+ elif nonAlphaNumeric1 or nonAlphaNumeric2:
+ # One point for non-alphanumeric.
+ return 1
+ return 0
+
+ pointer = 1
+ # Intentionally ignore the first and last element (don't need checking).
+ while pointer < len(diffs) - 1:
+ if (
+ diffs[pointer - 1][0] == self.DIFF_EQUAL
+ and diffs[pointer + 1][0] == self.DIFF_EQUAL
+ ):
+ # This is a single edit surrounded by equalities.
+ equality1 = diffs[pointer - 1][1]
+ edit = diffs[pointer][1]
+ equality2 = diffs[pointer + 1][1]
+
+ # First, shift the edit as far left as possible.
+ commonOffset = self.diff_commonSuffix(equality1, edit)
+ if commonOffset:
+ commonString = edit[-commonOffset:]
+ equality1 = equality1[:-commonOffset]
+ edit = commonString + edit[:-commonOffset]
+ equality2 = commonString + equality2
+
+ # Second, step character by character right, looking for the best fit.
+ bestEquality1 = equality1
+ bestEdit = edit
+ bestEquality2 = equality2
+ bestScore = diff_cleanupSemanticScore(
+ equality1, edit
+ ) + diff_cleanupSemanticScore(edit, equality2)
+ while edit and equality2 and edit[0] == equality2[0]:
+ equality1 += edit[0]
+ edit = edit[1:] + equality2[0]
+ equality2 = equality2[1:]
+ score = diff_cleanupSemanticScore(
+ equality1, edit
+ ) + diff_cleanupSemanticScore(edit, equality2)
+ # The >= encourages trailing rather than leading whitespace on edits.
+ if score >= bestScore:
+ bestScore = score
+ bestEquality1 = equality1
+ bestEdit = edit
+ bestEquality2 = equality2
+
+ if diffs[pointer - 1][1] != bestEquality1:
+ # We have an improvement, save it back to the diff.
+ if bestEquality1:
+ diffs[pointer - 1] = (diffs[pointer - 1][0], bestEquality1)
+ else:
+ del diffs[pointer - 1]
+ pointer -= 1
+ diffs[pointer] = (diffs[pointer][0], bestEdit)
+ if bestEquality2:
+ diffs[pointer + 1] = (diffs[pointer + 1][0], bestEquality2)
+ else:
+ del diffs[pointer + 1]
+ pointer -= 1
+ pointer += 1
+
+ # Define some regex patterns for matching boundaries.
+ BLANKLINEEND = re.compile(r"\n\r?\n$")
+ BLANKLINESTART = re.compile(r"^\r?\n\r?\n")
+
+ def diff_cleanupEfficiency(self, diffs):
+ """Reduce the number of edits by eliminating operationally trivial
+ equalities.
+
+ Args:
+ diffs: Array of diff tuples.
+ """
+ changes = False
+ equalities = [] # Stack of indices where equalities are found.
+ lastEquality = None # Always equal to diffs[equalities[-1]][1]
+ pointer = 0 # Index of current position.
+ pre_ins = False # Is there an insertion operation before the last equality.
+ pre_del = False # Is there a deletion operation before the last equality.
+ post_ins = False # Is there an insertion operation after the last equality.
+ post_del = False # Is there a deletion operation after the last equality.
+ while pointer < len(diffs):
+ if diffs[pointer][0] == self.DIFF_EQUAL: # Equality found.
+ if len(diffs[pointer][1]) < self.Diff_EditCost and (
+ post_ins or post_del
+ ):
+ # Candidate found.
+ equalities.append(pointer)
+ pre_ins = post_ins
+ pre_del = post_del
+ lastEquality = diffs[pointer][1]
+ else:
+ # Not a candidate, and can never become one.
+ equalities = []
+ lastEquality = None
+
+ post_ins = post_del = False
+ else: # An insertion or deletion.
+ if diffs[pointer][0] == self.DIFF_DELETE:
+ post_del = True
+ else:
+ post_ins = True
+
+ # Five types to be split:
+ # <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+ # <ins>A</ins>X<ins>C</ins><del>D</del>
+ # <ins>A</ins><del>B</del>X<ins>C</ins>
+ # <ins>A</del>X<ins>C</ins><del>D</del>
+ # <ins>A</ins><del>B</del>X<del>C</del>
+
+ if lastEquality and (
+ (pre_ins and pre_del and post_ins and post_del)
+ or (
+ (len(lastEquality) < self.Diff_EditCost / 2)
+ and (pre_ins + pre_del + post_ins + post_del) == 3
+ )
+ ):
+ # Duplicate record.
+ diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))
+ # Change second copy to insert.
+ diffs[equalities[-1] + 1] = (
+ self.DIFF_INSERT,
+ diffs[equalities[-1] + 1][1],
+ )
+ equalities.pop() # Throw away the equality we just deleted.
+ lastEquality = None
+ if pre_ins and pre_del:
+ # No changes made which could affect previous entry, keep going.
+ post_ins = post_del = True
+ equalities = []
+ else:
+ if len(equalities):
+ equalities.pop() # Throw away the previous equality.
+ if len(equalities):
+ pointer = equalities[-1]
+ else:
+ pointer = -1
+ post_ins = post_del = False
+ changes = True
+ pointer += 1
+
+ if changes:
+ self.diff_cleanupMerge(diffs)
+
+ def diff_cleanupMerge(self, diffs):
+ """Reorder and merge like edit sections. Merge equalities.
+ Any edit section can move as long as it doesn't cross an equality.
+
+ Args:
+ diffs: Array of diff tuples.
+ """
+ diffs.append((self.DIFF_EQUAL, "")) # Add a dummy entry at the end.
+ pointer = 0
+ count_delete = 0
+ count_insert = 0
+ text_delete = ""
+ text_insert = ""
+ while pointer < len(diffs):
+ if diffs[pointer][0] == self.DIFF_INSERT:
+ count_insert += 1
+ text_insert += diffs[pointer][1]
+ pointer += 1
+ elif diffs[pointer][0] == self.DIFF_DELETE:
+ count_delete += 1
+ text_delete += diffs[pointer][1]
+ pointer += 1
+ elif diffs[pointer][0] == self.DIFF_EQUAL:
+ # Upon reaching an equality, check for prior redundancies.
+ if count_delete + count_insert > 1:
+ if count_delete != 0 and count_insert != 0:
+ # Factor out any common prefixies.
+ commonlength = self.diff_commonPrefix(text_insert, text_delete)
+ if commonlength != 0:
+ x = pointer - count_delete - count_insert - 1
+ if x >= 0 and diffs[x][0] == self.DIFF_EQUAL:
+ diffs[x] = (
+ diffs[x][0],
+ diffs[x][1] + text_insert[:commonlength],
+ )
+ else:
+ diffs.insert(
+ 0, (self.DIFF_EQUAL, text_insert[:commonlength])
+ )
+ pointer += 1
+ text_insert = text_insert[commonlength:]
+ text_delete = text_delete[commonlength:]
+ # Factor out any common suffixies.
+ commonlength = self.diff_commonSuffix(text_insert, text_delete)
+ if commonlength != 0:
+ diffs[pointer] = (
+ diffs[pointer][0],
+ text_insert[-commonlength:] + diffs[pointer][1],
+ )
+ text_insert = text_insert[:-commonlength]
+ text_delete = text_delete[:-commonlength]
+ # Delete the offending records and add the merged ones.
+ new_ops = []
+ if len(text_delete) != 0:
+ new_ops.append((self.DIFF_DELETE, text_delete))
+ if len(text_insert) != 0:
+ new_ops.append((self.DIFF_INSERT, text_insert))
+ pointer -= count_delete + count_insert
+ diffs[pointer : pointer + count_delete + count_insert] = new_ops
+ pointer += len(new_ops) + 1
+ elif pointer != 0 and diffs[pointer - 1][0] == self.DIFF_EQUAL:
+ # Merge this equality with the previous one.
+ diffs[pointer - 1] = (
+ diffs[pointer - 1][0],
+ diffs[pointer - 1][1] + diffs[pointer][1],
+ )
+ del diffs[pointer]
+ else:
+ pointer += 1
+
+ count_insert = 0
+ count_delete = 0
+ text_delete = ""
+ text_insert = ""
+
+ if diffs[-1][1] == "":
+ diffs.pop() # Remove the dummy entry at the end.
+
+ # Second pass: look for single edits surrounded on both sides by equalities
+ # which can be shifted sideways to eliminate an equality.
+ # e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+ changes = False
+ pointer = 1
+ # Intentionally ignore the first and last element (don't need checking).
+ while pointer < len(diffs) - 1:
+ if (
+ diffs[pointer - 1][0] == self.DIFF_EQUAL
+ and diffs[pointer + 1][0] == self.DIFF_EQUAL
+ ):
+ # This is a single edit surrounded by equalities.
+ if diffs[pointer][1].endswith(diffs[pointer - 1][1]):
+ # Shift the edit over the previous equality.
+ if diffs[pointer - 1][1] != "":
+ diffs[pointer] = (
+ diffs[pointer][0],
+ diffs[pointer - 1][1]
+ + diffs[pointer][1][: -len(diffs[pointer - 1][1])],
+ )
+ diffs[pointer + 1] = (
+ diffs[pointer + 1][0],
+ diffs[pointer - 1][1] + diffs[pointer + 1][1],
+ )
+ del diffs[pointer - 1]
+ changes = True
+ elif diffs[pointer][1].startswith(diffs[pointer + 1][1]):
+ # Shift the edit over the next equality.
+ diffs[pointer - 1] = (
+ diffs[pointer - 1][0],
+ diffs[pointer - 1][1] + diffs[pointer + 1][1],
+ )
+ diffs[pointer] = (
+ diffs[pointer][0],
+ diffs[pointer][1][len(diffs[pointer + 1][1]) :]
+ + diffs[pointer + 1][1],
+ )
+ del diffs[pointer + 1]
+ changes = True
+ pointer += 1
+
+ # If shifts were made, the diff needs reordering and another shift sweep.
+ if changes:
+ self.diff_cleanupMerge(diffs)
+
+ def diff_xIndex(self, diffs, loc):
+ """loc is a location in text1, compute and return the equivalent location
+ in text2. e.g. "The cat" vs "The big cat", 1->1, 5->8
+
+ Args:
+ diffs: Array of diff tuples.
+ loc: Location within text1.
+
+ Returns:
+ Location within text2.
+ """
+ chars1 = 0
+ chars2 = 0
+ last_chars1 = 0
+ last_chars2 = 0
+ for x in xrange(len(diffs)):
+ (op, text) = diffs[x]
+ if op != self.DIFF_INSERT: # Equality or deletion.
+ chars1 += len(text)
+ if op != self.DIFF_DELETE: # Equality or insertion.
+ chars2 += len(text)
+ if chars1 > loc: # Overshot the location.
+ break
+ last_chars1 = chars1
+ last_chars2 = chars2
+
+ if len(diffs) != x and diffs[x][0] == self.DIFF_DELETE:
+ # The location was deleted.
+ return last_chars2
+ # Add the remaining len(character).
+ return last_chars2 + (loc - last_chars1)
+
+ def diff_prettyHtml(self, diffs):
+ """Convert a diff array into a pretty HTML report.
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ HTML representation.
+ """
+ html = []
+ for (op, data) in diffs:
+ text = (
+ data.replace("&", "&amp;")
+ .replace("<", "&lt;")
+ .replace(">", "&gt;")
+ .replace("\n", "&para;<br>")
+ )
+ if op == self.DIFF_INSERT:
+ html.append('<ins style="background:#e6ffe6;">%s</ins>' % text)
+ elif op == self.DIFF_DELETE:
+ html.append('<del style="background:#ffe6e6;">%s</del>' % text)
+ elif op == self.DIFF_EQUAL:
+ html.append("<span>%s</span>" % text)
+ return "".join(html)
+
+ def diff_text1(self, diffs):
+ """Compute and return the source text (all equalities and deletions).
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ Source text.
+ """
+ text = []
+ for (op, data) in diffs:
+ if op != self.DIFF_INSERT:
+ text.append(data)
+ return "".join(text)
+
+ def diff_text2(self, diffs):
+ """Compute and return the destination text (all equalities and insertions).
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ Destination text.
+ """
+ text = []
+ for (op, data) in diffs:
+ if op != self.DIFF_DELETE:
+ text.append(data)
+ return "".join(text)
+
+ def diff_levenshtein(self, diffs):
+ """Compute the Levenshtein distance; the number of inserted, deleted or
+ substituted characters.
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ Number of changes.
+ """
+ levenshtein = 0
+ insertions = 0
+ deletions = 0
+ for (op, data) in diffs:
+ if op == self.DIFF_INSERT:
+ insertions += len(data)
+ elif op == self.DIFF_DELETE:
+ deletions += len(data)
+ elif op == self.DIFF_EQUAL:
+ # A deletion and an insertion is one substitution.
+ levenshtein += max(insertions, deletions)
+ insertions = 0
+ deletions = 0
+ levenshtein += max(insertions, deletions)
+ return levenshtein
+
+ def diff_toDelta(self, diffs):
+ """Crush the diff into an encoded string which describes the operations
+ required to transform text1 into text2.
+ E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'.
+ Operations are tab-separated. Inserted text is escaped using %xx notation.
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ Delta text.
+ """
+ text = []
+ for (op, data) in diffs:
+ if op == self.DIFF_INSERT:
+ # High ascii will raise UnicodeDecodeError. Use Unicode instead.
+ data = data.encode("utf-8")
+ text.append("+" + urllib.quote(data, "!~*'();/?:@&=+$,# "))
+ elif op == self.DIFF_DELETE:
+ text.append("-%d" % len(data))
+ elif op == self.DIFF_EQUAL:
+ text.append("=%d" % len(data))
+ return "\t".join(text)
+
+ def diff_fromDelta(self, text1, delta):
+ """Given the original text1, and an encoded string which describes the
+ operations required to transform text1 into text2, compute the full diff.
+
+ Args:
+ text1: Source string for the diff.
+ delta: Delta text.
+
+ Returns:
+ Array of diff tuples.
+
+ Raises:
+ ValueError: If invalid input.
+ """
+ if type(delta) == unicode:
+ # Deltas should be composed of a subset of ascii chars, Unicode not
+ # required. If this encode raises UnicodeEncodeError, delta is invalid.
+ delta = delta.encode("ascii")
+ diffs = []
+ pointer = 0 # Cursor in text1
+ tokens = delta.split("\t")
+ for token in tokens:
+ if token == "":
+ # Blank tokens are ok (from a trailing \t).
+ continue
+ # Each token begins with a one character parameter which specifies the
+ # operation of this token (delete, insert, equality).
+ param = token[1:]
+ if token[0] == "+":
+ param = urllib.unquote(param).decode("utf-8")
+ diffs.append((self.DIFF_INSERT, param))
+ elif token[0] == "-" or token[0] == "=":
+ try:
+ n = int(param)
+ except ValueError:
+ raise ValueError("Invalid number in diff_fromDelta: " + param)
+ if n < 0:
+ raise ValueError("Negative number in diff_fromDelta: " + param)
+ text = text1[pointer : pointer + n]
+ pointer += n
+ if token[0] == "=":
+ diffs.append((self.DIFF_EQUAL, text))
+ else:
+ diffs.append((self.DIFF_DELETE, text))
+ else:
+ # Anything else is an error.
+ raise ValueError(
+ "Invalid diff operation in diff_fromDelta: " + token[0]
+ )
+ if pointer != len(text1):
+ raise ValueError(
+ "Delta length (%d) does not equal source text length (%d)."
+ % (pointer, len(text1))
+ )
+ return diffs
+
+ # MATCH FUNCTIONS
+
+ def match_main(self, text, pattern, loc):
+ """Locate the best instance of 'pattern' in 'text' near 'loc'.
+
+ Args:
+ text: The text to search.
+ pattern: The pattern to search for.
+ loc: The location to search around.
+
+ Returns:
+ Best match index or -1.
+ """
+ # Check for null inputs.
+ if text == None or pattern == None:
+ raise ValueError("Null inputs. (match_main)")
+
+ loc = max(0, min(loc, len(text)))
+ if text == pattern:
+ # Shortcut (potentially not guaranteed by the algorithm)
+ return 0
+ elif not text:
+ # Nothing to match.
+ return -1
+ elif text[loc : loc + len(pattern)] == pattern:
+ # Perfect match at the perfect spot! (Includes case of null pattern)
+ return loc
+ else:
+ # Do a fuzzy compare.
+ match = self.match_bitap(text, pattern, loc)
+ return match
+
+ def match_bitap(self, text, pattern, loc):
+ """Locate the best instance of 'pattern' in 'text' near 'loc' using the
+ Bitap algorithm.
+
+ Args:
+ text: The text to search.
+ pattern: The pattern to search for.
+ loc: The location to search around.
+
+ Returns:
+ Best match index or -1.
+ """
+ # Python doesn't have a maxint limit, so ignore this check.
+ # if self.Match_MaxBits != 0 and len(pattern) > self.Match_MaxBits:
+ # raise ValueError("Pattern too long for this application.")
+
+ # Initialise the alphabet.
+ s = self.match_alphabet(pattern)
+
+ def match_bitapScore(e, x):
+ """Compute and return the score for a match with e errors and x location.
+ Accesses loc and pattern through being a closure.
+
+ Args:
+ e: Number of errors in match.
+ x: Location of match.
+
+ Returns:
+ Overall score for match (0.0 = good, 1.0 = bad).
+ """
+ accuracy = float(e) / len(pattern)
+ proximity = abs(loc - x)
+ if not self.Match_Distance:
+ # Dodge divide by zero error.
+ return proximity and 1.0 or accuracy
+ return accuracy + (proximity / float(self.Match_Distance))
+
+ # Highest score beyond which we give up.
+ score_threshold = self.Match_Threshold
+ # Is there a nearby exact match? (speedup)
+ best_loc = text.find(pattern, loc)
+ if best_loc != -1:
+ score_threshold = min(match_bitapScore(0, best_loc), score_threshold)
+ # What about in the other direction? (speedup)
+ best_loc = text.rfind(pattern, loc + len(pattern))
+ if best_loc != -1:
+ score_threshold = min(match_bitapScore(0, best_loc), score_threshold)
+
+ # Initialise the bit arrays.
+ matchmask = 1 << (len(pattern) - 1)
+ best_loc = -1
+
+ bin_max = len(pattern) + len(text)
+ # Empty initialization added to appease pychecker.
+ last_rd = None
+ for d in xrange(len(pattern)):
+ # Scan for the best match each iteration allows for one more error.
+ # Run a binary search to determine how far from 'loc' we can stray at
+ # this error level.
+ bin_min = 0
+ bin_mid = bin_max
+ while bin_min < bin_mid:
+ if match_bitapScore(d, loc + bin_mid) <= score_threshold:
+ bin_min = bin_mid
+ else:
+ bin_max = bin_mid
+ bin_mid = (bin_max - bin_min) // 2 + bin_min
+
+ # Use the result from this iteration as the maximum for the next.
+ bin_max = bin_mid
+ start = max(1, loc - bin_mid + 1)
+ finish = min(loc + bin_mid, len(text)) + len(pattern)
+
+ rd = [0] * (finish + 2)
+ rd[finish + 1] = (1 << d) - 1
+ for j in xrange(finish, start - 1, -1):
+ if len(text) <= j - 1:
+ # Out of range.
+ charMatch = 0
+ else:
+ charMatch = s.get(text[j - 1], 0)
+ if d == 0: # First pass: exact match.
+ rd[j] = ((rd[j + 1] << 1) | 1) & charMatch
+ else: # Subsequent passes: fuzzy match.
+ rd[j] = (
+ (((rd[j + 1] << 1) | 1) & charMatch)
+ | (((last_rd[j + 1] | last_rd[j]) << 1) | 1)
+ | last_rd[j + 1]
+ )
+ if rd[j] & matchmask:
+ score = match_bitapScore(d, j - 1)
+ # This match will almost certainly be better than any existing match.
+ # But check anyway.
+ if score <= score_threshold:
+ # Told you so.
+ score_threshold = score
+ best_loc = j - 1
+ if best_loc > loc:
+ # When passing loc, don't exceed our current distance from loc.
+ start = max(1, 2 * loc - best_loc)
+ else:
+ # Already passed loc, downhill from here on in.
+ break
+ # No hope for a (better) match at greater error levels.
+ if match_bitapScore(d + 1, loc) > score_threshold:
+ break
+ last_rd = rd
+ return best_loc
+
+ def match_alphabet(self, pattern):
+ """Initialise the alphabet for the Bitap algorithm.
+
+ Args:
+ pattern: The text to encode.
+
+ Returns:
+ Hash of character locations.
+ """
+ s = {}
+ for char in pattern:
+ s[char] = 0
+ for i in xrange(len(pattern)):
+ s[pattern[i]] |= 1 << (len(pattern) - i - 1)
+ return s
+
+ # PATCH FUNCTIONS
+
+ def patch_addContext(self, patch, text):
+ """Increase the context until it is unique,
+ but don't let the pattern expand beyond Match_MaxBits.
+
+ Args:
+ patch: The patch to grow.
+ text: Source text.
+ """
+ if len(text) == 0:
+ return
+ pattern = text[patch.start2 : patch.start2 + patch.length1]
+ padding = 0
+
+ # Look for the first and last matches of pattern in text. If two different
+ # matches are found, increase the pattern length.
+ while text.find(pattern) != text.rfind(pattern) and (
+ self.Match_MaxBits == 0
+ or len(pattern) < self.Match_MaxBits - self.Patch_Margin - self.Patch_Margin
+ ):
+ padding += self.Patch_Margin
+ pattern = text[
+ max(0, patch.start2 - padding) : patch.start2 + patch.length1 + padding
+ ]
+ # Add one chunk for good luck.
+ padding += self.Patch_Margin
+
+ # Add the prefix.
+ prefix = text[max(0, patch.start2 - padding) : patch.start2]
+ if prefix:
+ patch.diffs[:0] = [(self.DIFF_EQUAL, prefix)]
+ # Add the suffix.
+ suffix = text[
+ patch.start2 + patch.length1 : patch.start2 + patch.length1 + padding
+ ]
+ if suffix:
+ patch.diffs.append((self.DIFF_EQUAL, suffix))
+
+ # Roll back the start points.
+ patch.start1 -= len(prefix)
+ patch.start2 -= len(prefix)
+ # Extend lengths.
+ patch.length1 += len(prefix) + len(suffix)
+ patch.length2 += len(prefix) + len(suffix)
+
+ def patch_make(self, a, b=None, c=None):
+ """Compute a list of patches to turn text1 into text2.
+ Use diffs if provided, otherwise compute it ourselves.
+ There are four ways to call this function, depending on what data is
+ available to the caller:
+ Method 1:
+ a = text1, b = text2
+ Method 2:
+ a = diffs
+ Method 3 (optimal):
+ a = text1, b = diffs
+ Method 4 (deprecated, use method 3):
+ a = text1, b = text2, c = diffs
+
+ Args:
+ a: text1 (methods 1,3,4) or Array of diff tuples for text1 to
+ text2 (method 2).
+ b: text2 (methods 1,4) or Array of diff tuples for text1 to
+ text2 (method 3) or undefined (method 2).
+ c: Array of diff tuples for text1 to text2 (method 4) or
+ undefined (methods 1,2,3).
+
+ Returns:
+ Array of Patch objects.
+ """
+ text1 = None
+ diffs = None
+ # Note that texts may arrive as 'str' or 'unicode'.
+ if isinstance(a, basestring) and isinstance(b, basestring) and c is None:
+ # Method 1: text1, text2
+ # Compute diffs from text1 and text2.
+ text1 = a
+ diffs = self.diff_main(text1, b, True)
+ if len(diffs) > 2:
+ self.diff_cleanupSemantic(diffs)
+ self.diff_cleanupEfficiency(diffs)
+ elif isinstance(a, list) and b is None and c is None:
+ # Method 2: diffs
+ # Compute text1 from diffs.
+ diffs = a
+ text1 = self.diff_text1(diffs)
+ elif isinstance(a, basestring) and isinstance(b, list) and c is None:
+ # Method 3: text1, diffs
+ text1 = a
+ diffs = b
+ elif (
+ isinstance(a, basestring)
+ and isinstance(b, basestring)
+ and isinstance(c, list)
+ ):
+ # Method 4: text1, text2, diffs
+ # text2 is not used.
+ text1 = a
+ diffs = c
+ else:
+ raise ValueError("Unknown call format to patch_make.")
+
+ if not diffs:
+ return [] # Get rid of the None case.
+ patches = []
+ patch = patch_obj()
+ char_count1 = 0 # Number of characters into the text1 string.
+ char_count2 = 0 # Number of characters into the text2 string.
+ prepatch_text = text1 # Recreate the patches to determine context info.
+ postpatch_text = text1
+ for x in xrange(len(diffs)):
+ (diff_type, diff_text) = diffs[x]
+ if len(patch.diffs) == 0 and diff_type != self.DIFF_EQUAL:
+ # A new patch starts here.
+ patch.start1 = char_count1
+ patch.start2 = char_count2
+ if diff_type == self.DIFF_INSERT:
+ # Insertion
+ patch.diffs.append(diffs[x])
+ patch.length2 += len(diff_text)
+ postpatch_text = (
+ postpatch_text[:char_count2]
+ + diff_text
+ + postpatch_text[char_count2:]
+ )
+ elif diff_type == self.DIFF_DELETE:
+ # Deletion.
+ patch.length1 += len(diff_text)
+ patch.diffs.append(diffs[x])
+ postpatch_text = (
+ postpatch_text[:char_count2]
+ + postpatch_text[char_count2 + len(diff_text) :]
+ )
+ elif (
+ diff_type == self.DIFF_EQUAL
+ and len(diff_text) <= 2 * self.Patch_Margin
+ and len(patch.diffs) != 0
+ and len(diffs) != x + 1
+ ):
+ # Small equality inside a patch.
+ patch.diffs.append(diffs[x])
+ patch.length1 += len(diff_text)
+ patch.length2 += len(diff_text)
+
+ if diff_type == self.DIFF_EQUAL and len(diff_text) >= 2 * self.Patch_Margin:
+ # Time for a new patch.
+ if len(patch.diffs) != 0:
+ self.patch_addContext(patch, prepatch_text)
+ patches.append(patch)
+ patch = patch_obj()
+ # Unlike Unidiff, our patch lists have a rolling context.
+ # https://github.com/google/diff-match-patch/wiki/Unidiff
+ # Update prepatch text & pos to reflect the application of the
+ # just completed patch.
+ prepatch_text = postpatch_text
+ char_count1 = char_count2
+
+ # Update the current character count.
+ if diff_type != self.DIFF_INSERT:
+ char_count1 += len(diff_text)
+ if diff_type != self.DIFF_DELETE:
+ char_count2 += len(diff_text)
+
+ # Pick up the leftover patch if not empty.
+ if len(patch.diffs) != 0:
+ self.patch_addContext(patch, prepatch_text)
+ patches.append(patch)
+ return patches
+
+ def patch_deepCopy(self, patches):
+ """Given an array of patches, return another array that is identical.
+
+ Args:
+ patches: Array of Patch objects.
+
+ Returns:
+ Array of Patch objects.
+ """
+ patchesCopy = []
+ for patch in patches:
+ patchCopy = patch_obj()
+ # No need to deep copy the tuples since they are immutable.
+ patchCopy.diffs = patch.diffs[:]
+ patchCopy.start1 = patch.start1
+ patchCopy.start2 = patch.start2
+ patchCopy.length1 = patch.length1
+ patchCopy.length2 = patch.length2
+ patchesCopy.append(patchCopy)
+ return patchesCopy
+
+ def patch_apply(self, patches, text):
+ """Merge a set of patches onto the text. Return a patched text, as well
+ as a list of true/false values indicating which patches were applied.
+
+ Args:
+ patches: Array of Patch objects.
+ text: Old text.
+
+ Returns:
+ Two element Array, containing the new text and an array of boolean values.
+ """
+ if not patches:
+ return (text, [])
+
+ # Deep copy the patches so that no changes are made to originals.
+ patches = self.patch_deepCopy(patches)
+
+ nullPadding = self.patch_addPadding(patches)
+ text = nullPadding + text + nullPadding
+ self.patch_splitMax(patches)
+
+ # delta keeps track of the offset between the expected and actual location
+ # of the previous patch. If there are patches expected at positions 10 and
+ # 20, but the first patch was found at 12, delta is 2 and the second patch
+ # has an effective expected position of 22.
+ delta = 0
+ results = []
+ for patch in patches:
+ expected_loc = patch.start2 + delta
+ text1 = self.diff_text1(patch.diffs)
+ end_loc = -1
+ if len(text1) > self.Match_MaxBits:
+ # patch_splitMax will only provide an oversized pattern in the case of
+ # a monster delete.
+ start_loc = self.match_main(
+ text, text1[: self.Match_MaxBits], expected_loc
+ )
+ if start_loc != -1:
+ end_loc = self.match_main(
+ text,
+ text1[-self.Match_MaxBits :],
+ expected_loc + len(text1) - self.Match_MaxBits,
+ )
+ if end_loc == -1 or start_loc >= end_loc:
+ # Can't find valid trailing context. Drop this patch.
+ start_loc = -1
+ else:
+ start_loc = self.match_main(text, text1, expected_loc)
+ if start_loc == -1:
+ # No match found. :(
+ results.append(False)
+ # Subtract the delta for this failed patch from subsequent patches.
+ delta -= patch.length2 - patch.length1
+ else:
+ # Found a match. :)
+ results.append(True)
+ delta = start_loc - expected_loc
+ if end_loc == -1:
+ text2 = text[start_loc : start_loc + len(text1)]
+ else:
+ text2 = text[start_loc : end_loc + self.Match_MaxBits]
+ if text1 == text2:
+ # Perfect match, just shove the replacement text in.
+ text = (
+ text[:start_loc]
+ + self.diff_text2(patch.diffs)
+ + text[start_loc + len(text1) :]
+ )
+ else:
+ # Imperfect match.
+ # Run a diff to get a framework of equivalent indices.
+ diffs = self.diff_main(text1, text2, False)
+ if (
+ len(text1) > self.Match_MaxBits
+ and self.diff_levenshtein(diffs) / float(len(text1))
+ > self.Patch_DeleteThreshold
+ ):
+ # The end points match, but the content is unacceptably bad.
+ results[-1] = False
+ else:
+ self.diff_cleanupSemanticLossless(diffs)
+ index1 = 0
+ for (op, data) in patch.diffs:
+ if op != self.DIFF_EQUAL:
+ index2 = self.diff_xIndex(diffs, index1)
+ if op == self.DIFF_INSERT: # Insertion
+ text = (
+ text[: start_loc + index2]
+ + data
+ + text[start_loc + index2 :]
+ )
+ elif op == self.DIFF_DELETE: # Deletion
+ text = (
+ text[: start_loc + index2]
+ + text[
+ start_loc
+ + self.diff_xIndex(diffs, index1 + len(data)) :
+ ]
+ )
+ if op != self.DIFF_DELETE:
+ index1 += len(data)
+ # Strip the padding off.
+ text = text[len(nullPadding) : -len(nullPadding)]
+ return (text, results)
+
+ def patch_addPadding(self, patches):
+ """Add some padding on text start and end so that edges can match
+ something. Intended to be called only from within patch_apply.
+
+ Args:
+ patches: Array of Patch objects.
+
+ Returns:
+ The padding string added to each side.
+ """
+ paddingLength = self.Patch_Margin
+ nullPadding = ""
+ for x in xrange(1, paddingLength + 1):
+ nullPadding += chr(x)
+
+ # Bump all the patches forward.
+ for patch in patches:
+ patch.start1 += paddingLength
+ patch.start2 += paddingLength
+
+ # Add some padding on start of first diff.
+ patch = patches[0]
+ diffs = patch.diffs
+ if not diffs or diffs[0][0] != self.DIFF_EQUAL:
+ # Add nullPadding equality.
+ diffs.insert(0, (self.DIFF_EQUAL, nullPadding))
+ patch.start1 -= paddingLength # Should be 0.
+ patch.start2 -= paddingLength # Should be 0.
+ patch.length1 += paddingLength
+ patch.length2 += paddingLength
+ elif paddingLength > len(diffs[0][1]):
+ # Grow first equality.
+ extraLength = paddingLength - len(diffs[0][1])
+ newText = nullPadding[len(diffs[0][1]) :] + diffs[0][1]
+ diffs[0] = (diffs[0][0], newText)
+ patch.start1 -= extraLength
+ patch.start2 -= extraLength
+ patch.length1 += extraLength
+ patch.length2 += extraLength
+
+ # Add some padding on end of last diff.
+ patch = patches[-1]
+ diffs = patch.diffs
+ if not diffs or diffs[-1][0] != self.DIFF_EQUAL:
+ # Add nullPadding equality.
+ diffs.append((self.DIFF_EQUAL, nullPadding))
+ patch.length1 += paddingLength
+ patch.length2 += paddingLength
+ elif paddingLength > len(diffs[-1][1]):
+ # Grow last equality.
+ extraLength = paddingLength - len(diffs[-1][1])
+ newText = diffs[-1][1] + nullPadding[:extraLength]
+ diffs[-1] = (diffs[-1][0], newText)
+ patch.length1 += extraLength
+ patch.length2 += extraLength
+
+ return nullPadding
+
+ def patch_splitMax(self, patches):
+ """Look through the patches and break up any which are longer than the
+ maximum limit of the match algorithm.
+ Intended to be called only from within patch_apply.
+
+ Args:
+ patches: Array of Patch objects.
+ """
+ patch_size = self.Match_MaxBits
+ if patch_size == 0:
+ # Python has the option of not splitting strings due to its ability
+ # to handle integers of arbitrary precision.
+ return
+ for x in xrange(len(patches)):
+ if patches[x].length1 <= patch_size:
+ continue
+ bigpatch = patches[x]
+ # Remove the big old patch.
+ del patches[x]
+ x -= 1
+ start1 = bigpatch.start1
+ start2 = bigpatch.start2
+ precontext = ""
+ while len(bigpatch.diffs) != 0:
+ # Create one of several smaller patches.
+ patch = patch_obj()
+ empty = True
+ patch.start1 = start1 - len(precontext)
+ patch.start2 = start2 - len(precontext)
+ if precontext:
+ patch.length1 = patch.length2 = len(precontext)
+ patch.diffs.append((self.DIFF_EQUAL, precontext))
+
+ while (
+ len(bigpatch.diffs) != 0
+ and patch.length1 < patch_size - self.Patch_Margin
+ ):
+ (diff_type, diff_text) = bigpatch.diffs[0]
+ if diff_type == self.DIFF_INSERT:
+ # Insertions are harmless.
+ patch.length2 += len(diff_text)
+ start2 += len(diff_text)
+ patch.diffs.append(bigpatch.diffs.pop(0))
+ empty = False
+ elif (
+ diff_type == self.DIFF_DELETE
+ and len(patch.diffs) == 1
+ and patch.diffs[0][0] == self.DIFF_EQUAL
+ and len(diff_text) > 2 * patch_size
+ ):
+ # This is a large deletion. Let it pass in one chunk.
+ patch.length1 += len(diff_text)
+ start1 += len(diff_text)
+ empty = False
+ patch.diffs.append((diff_type, diff_text))
+ del bigpatch.diffs[0]
+ else:
+ # Deletion or equality. Only take as much as we can stomach.
+ diff_text = diff_text[
+ : patch_size - patch.length1 - self.Patch_Margin
+ ]
+ patch.length1 += len(diff_text)
+ start1 += len(diff_text)
+ if diff_type == self.DIFF_EQUAL:
+ patch.length2 += len(diff_text)
+ start2 += len(diff_text)
+ else:
+ empty = False
+
+ patch.diffs.append((diff_type, diff_text))
+ if diff_text == bigpatch.diffs[0][1]:
+ del bigpatch.diffs[0]
+ else:
+ bigpatch.diffs[0] = (
+ bigpatch.diffs[0][0],
+ bigpatch.diffs[0][1][len(diff_text) :],
+ )
+
+ # Compute the head context for the next patch.
+ precontext = self.diff_text2(patch.diffs)
+ precontext = precontext[-self.Patch_Margin :]
+ # Append the end context for this patch.
+ postcontext = self.diff_text1(bigpatch.diffs)[: self.Patch_Margin]
+ if postcontext:
+ patch.length1 += len(postcontext)
+ patch.length2 += len(postcontext)
+ if len(patch.diffs) != 0 and patch.diffs[-1][0] == self.DIFF_EQUAL:
+ patch.diffs[-1] = (
+ self.DIFF_EQUAL,
+ patch.diffs[-1][1] + postcontext,
+ )
+ else:
+ patch.diffs.append((self.DIFF_EQUAL, postcontext))
+
+ if not empty:
+ x += 1
+ patches.insert(x, patch)
+
+ def patch_toText(self, patches):
+ """Take a list of patches and return a textual representation.
+
+ Args:
+ patches: Array of Patch objects.
+
+ Returns:
+ Text representation of patches.
+ """
+ text = []
+ for patch in patches:
+ text.append(str(patch))
+ return "".join(text)
+
+ def patch_fromText(self, textline):
+ """Parse a textual representation of patches and return a list of patch
+ objects.
+
+ Args:
+ textline: Text representation of patches.
+
+ Returns:
+ Array of Patch objects.
+
+ Raises:
+ ValueError: If invalid input.
+ """
+ if type(textline) == unicode:
+ # Patches should be composed of a subset of ascii chars, Unicode not
+ # required. If this encode raises UnicodeEncodeError, patch is invalid.
+ textline = textline.encode("ascii")
+ patches = []
+ if not textline:
+ return patches
+ text = textline.split("\n")
+ while len(text) != 0:
+ m = re.match("^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$", text[0])
+ if not m:
+ raise ValueError("Invalid patch string: " + text[0])
+ patch = patch_obj()
+ patches.append(patch)
+ patch.start1 = int(m.group(1))
+ if m.group(2) == "":
+ patch.start1 -= 1
+ patch.length1 = 1
+ elif m.group(2) == "0":
+ patch.length1 = 0
+ else:
+ patch.start1 -= 1
+ patch.length1 = int(m.group(2))
+
+ patch.start2 = int(m.group(3))
+ if m.group(4) == "":
+ patch.start2 -= 1
+ patch.length2 = 1
+ elif m.group(4) == "0":
+ patch.length2 = 0
+ else:
+ patch.start2 -= 1
+ patch.length2 = int(m.group(4))
+
+ del text[0]
+
+ while len(text) != 0:
+ if text[0]:
+ sign = text[0][0]
+ else:
+ sign = ""
+ line = urllib.unquote(text[0][1:])
+ line = line.decode("utf-8")
+ if sign == "+":
+ # Insertion.
+ patch.diffs.append((self.DIFF_INSERT, line))
+ elif sign == "-":
+ # Deletion.
+ patch.diffs.append((self.DIFF_DELETE, line))
+ elif sign == " ":
+ # Minor equality.
+ patch.diffs.append((self.DIFF_EQUAL, line))
+ elif sign == "@":
+ # Start of next patch.
+ break
+ elif sign == "":
+ # Blank line? Whatever.
+ pass
+ else:
+ # WTF?
+ raise ValueError("Invalid patch mode: '%s'\n%s" % (sign, line))
+ del text[0]
+ return patches
+
+
+class patch_obj:
+ """Class representing one patch operation.
+ """
+
+ def __init__(self):
+ """Initializes with an empty list of diffs.
+ """
+ self.diffs = []
+ self.start1 = None
+ self.start2 = None
+ self.length1 = 0
+ self.length2 = 0
+
+ def __str__(self):
+ """Emulate GNU diff's format.
+ Header: @@ -382,8 +481,9 @@
+ Indices are printed as 1-based, not 0-based.
+
+ Returns:
+ The GNU diff string.
+ """
+ if self.length1 == 0:
+ coords1 = str(self.start1) + ",0"
+ elif self.length1 == 1:
+ coords1 = str(self.start1 + 1)
+ else:
+ coords1 = str(self.start1 + 1) + "," + str(self.length1)
+ if self.length2 == 0:
+ coords2 = str(self.start2) + ",0"
+ elif self.length2 == 1:
+ coords2 = str(self.start2 + 1)
+ else:
+ coords2 = str(self.start2 + 1) + "," + str(self.length2)
+ text = ["@@ -", coords1, " +", coords2, " @@\n"]
+ # Escape the body of the patch with %xx notation.
+ for (op, data) in self.diffs:
+ if op == diff_match_patch.DIFF_INSERT:
+ text.append("+")
+ elif op == diff_match_patch.DIFF_DELETE:
+ text.append("-")
+ elif op == diff_match_patch.DIFF_EQUAL:
+ text.append(" ")
+ # High ascii will raise UnicodeDecodeError. Use Unicode instead.
+ data = data.encode("utf-8")
+ text.append(urllib.quote(data, "!~*'();/?:@&=+$,# ") + "\n")
+ return "".join(text)
diff --git a/contrib/python/diff-match-patch/py2/ya.make b/contrib/python/diff-match-patch/py2/ya.make
new file mode 100644
index 0000000000..7dfc13ebe4
--- /dev/null
+++ b/contrib/python/diff-match-patch/py2/ya.make
@@ -0,0 +1,27 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(20200713)
+
+LICENSE(Apache-2.0)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ diff_match_patch/__init__.py
+ diff_match_patch/diff_match_patch_py2.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/diff-match-patch/py2/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/diff-match-patch/py3/.dist-info/METADATA b/contrib/python/diff-match-patch/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..eecf8db23f
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/.dist-info/METADATA
@@ -0,0 +1,108 @@
+Metadata-Version: 2.1
+Name: diff-match-patch
+Version: 20230430
+Summary: Diff Match and Patch
+Author-email: Neil Fraser <fraser@google.com>
+Maintainer-email: Amethyst Reese <amethyst@n7.gg>
+Requires-Python: >=3.7
+Description-Content-Type: text/markdown
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Text Processing
+Requires-Dist: attribution==1.6.2 ; extra == "dev"
+Requires-Dist: black==23.3.0 ; extra == "dev"
+Requires-Dist: flit==3.8.0 ; extra == "dev"
+Requires-Dist: mypy==1.2.0 ; extra == "dev"
+Requires-Dist: ufmt==2.1.0 ; extra == "dev"
+Requires-Dist: usort==1.0.6 ; extra == "dev"
+Project-URL: Changelog, https://github.com/diff-match-patch-python/diff-match-patch/blob/main/CHANGELOG.md
+Project-URL: Github, https://github.com/diff-match-patch-python/diff-match-patch
+Provides-Extra: dev
+
+# diff-match-patch
+
+Google's [Diff Match and Patch][DMP] library, packaged for modern Python.
+
+[![version](https://img.shields.io/pypi/v/diff-match-patch.svg)](https://pypi.org/project/diff-match-patch)
+[![changelog](https://img.shields.io/badge/change-log-blue)](https://github.com/diff-match-patch-python/diff-match-patch/blob/main/CHANGELOG.md)
+[![license](https://img.shields.io/pypi/l/diff-match-patch.svg)](https://github.com/diff-match-patch-python/diff-match-patch/blob/master/LICENSE)
+
+## Install
+
+diff-match-patch is supported on Python 3.7 or newer.
+You can install it from PyPI:
+
+```shell
+python -m pip install diff-match-patch
+```
+
+## Usage
+
+Generating a patchset (analogous to unified diff) between two texts:
+
+```python
+from diff_match_patch import diff_match_patch
+
+dmp = diff_match_patch()
+patches = dmp.patch_make(text1, text2)
+diff = dmp.patch_toText(patches)
+```
+
+Applying a patchset to a text can then be done with:
+
+```python
+from diff_match_patch import diff_match_patch
+
+dmp = diff_match_patch()
+patches = dmp.patch_fromText(diff)
+new_text, _ = dmp.patch_apply(patches, text)
+```
+
+## Original README
+The Diff Match and Patch libraries offer robust algorithms to perform the
+operations required for synchronizing plain text.
+
+1. Diff:
+ * Compare two blocks of plain text and efficiently return a list of differences.
+ * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
+2. Match:
+ * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
+ * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
+3. Patch:
+ * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
+ * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
+
+Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
+
+### Reference
+
+* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
+* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
+* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
+* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
+* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
+
+### Languages
+Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
+
+* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
+* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
+* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
+* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
+* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
+* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
+* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
+* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
+
+A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
+
+### Algorithms
+This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
+
+This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
+
+[DMP]: https://github.com/google/diff-match-patch
+[API]: https://github.com/google/diff-match-patch/wiki/API
+
diff --git a/contrib/python/diff-match-patch/py3/.dist-info/top_level.txt b/contrib/python/diff-match-patch/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..63904d71d9
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+diff_match_patch
diff --git a/contrib/python/diff-match-patch/py3/AUTHORS b/contrib/python/diff-match-patch/py3/AUTHORS
new file mode 100644
index 0000000000..c82809e726
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/AUTHORS
@@ -0,0 +1,10 @@
+# Below is a list of people and organizations that have contributed
+# to the Diff Match Patch project.
+
+Google Inc.
+
+Duncan Cross <duncan.cross@gmail.com> (Lua port)
+Jan Weiß <jan@geheimwerk.de> (Objective C port)
+Matthaeus G. Chajdas <anteru@developer.shelter13.net> (C# port)
+Mike Slemmer <mikeslemmer@gmail.com> (C++ port)
+
diff --git a/contrib/python/diff-match-patch/py3/LICENSE b/contrib/python/diff-match-patch/py3/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/contrib/python/diff-match-patch/py3/README.md b/contrib/python/diff-match-patch/py3/README.md
new file mode 100644
index 0000000000..bdcd2a46e7
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/README.md
@@ -0,0 +1,84 @@
+# diff-match-patch
+
+Google's [Diff Match and Patch][DMP] library, packaged for modern Python.
+
+[![version](https://img.shields.io/pypi/v/diff-match-patch.svg)](https://pypi.org/project/diff-match-patch)
+[![changelog](https://img.shields.io/badge/change-log-blue)](https://github.com/diff-match-patch-python/diff-match-patch/blob/main/CHANGELOG.md)
+[![license](https://img.shields.io/pypi/l/diff-match-patch.svg)](https://github.com/diff-match-patch-python/diff-match-patch/blob/master/LICENSE)
+
+## Install
+
+diff-match-patch is supported on Python 3.7 or newer.
+You can install it from PyPI:
+
+```shell
+python -m pip install diff-match-patch
+```
+
+## Usage
+
+Generating a patchset (analogous to unified diff) between two texts:
+
+```python
+from diff_match_patch import diff_match_patch
+
+dmp = diff_match_patch()
+patches = dmp.patch_make(text1, text2)
+diff = dmp.patch_toText(patches)
+```
+
+Applying a patchset to a text can then be done with:
+
+```python
+from diff_match_patch import diff_match_patch
+
+dmp = diff_match_patch()
+patches = dmp.patch_fromText(diff)
+new_text, _ = dmp.patch_apply(patches, text)
+```
+
+## Original README
+The Diff Match and Patch libraries offer robust algorithms to perform the
+operations required for synchronizing plain text.
+
+1. Diff:
+ * Compare two blocks of plain text and efficiently return a list of differences.
+ * [Diff Demo](https://neil.fraser.name/software/diff_match_patch/demos/diff.html)
+2. Match:
+ * Given a search string, find its best fuzzy match in a block of plain text. Weighted for both accuracy and location.
+ * [Match Demo](https://neil.fraser.name/software/diff_match_patch/demos/match.html)
+3. Patch:
+ * Apply a list of patches onto plain text. Use best-effort to apply patch even when the underlying text doesn't match.
+ * [Patch Demo](https://neil.fraser.name/software/diff_match_patch/demos/patch.html)
+
+Originally built in 2006 to power Google Docs, this library is now available in C++, C#, Dart, Java, JavaScript, Lua, Objective C, and Python.
+
+### Reference
+
+* [API](https://github.com/google/diff-match-patch/wiki/API) - Common API across all languages.
+* [Line or Word Diffs](https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs) - Less detailed diffs.
+* [Plain Text vs. Structured Content](https://github.com/google/diff-match-patch/wiki/Plain-Text-vs.-Structured-Content) - How to deal with data like XML.
+* [Unidiff](https://github.com/google/diff-match-patch/wiki/Unidiff) - The patch serialization format.
+* [Support](https://groups.google.com/forum/#!forum/diff-match-patch) - Newsgroup for developers.
+
+### Languages
+Although each language port of Diff Match Patch uses the same API, there are some language-specific notes.
+
+* [C++](https://github.com/google/diff-match-patch/wiki/Language:-Cpp)
+* [C#](https://github.com/google/diff-match-patch/wiki/Language:-C%23)
+* [Dart](https://github.com/google/diff-match-patch/wiki/Language:-Dart)
+* [Java](https://github.com/google/diff-match-patch/wiki/Language:-Java)
+* [JavaScript](https://github.com/google/diff-match-patch/wiki/Language:-JavaScript)
+* [Lua](https://github.com/google/diff-match-patch/wiki/Language:-Lua)
+* [Objective-C](https://github.com/google/diff-match-patch/wiki/Language:-Objective-C)
+* [Python](https://github.com/google/diff-match-patch/wiki/Language:-Python)
+
+A standardized speed test tracks the [relative performance of diffs](https://docs.google.com/spreadsheets/d/1zpZccuBpjMZTvL1nGDMKJc7rWL_m_drF4XKOJvB27Kc/edit#gid=0) in each language.
+
+### Algorithms
+This library implements [Myer's diff algorithm](https://neil.fraser.name/writing/diff/myers.pdf) which is generally considered to be the best general-purpose diff. A layer of [pre-diff speedups and post-diff cleanups](https://neil.fraser.name/writing/diff/) surround the diff algorithm, improving both performance and output quality.
+
+This library also implements a [Bitap matching algorithm](https://neil.fraser.name/writing/patch/bitap.ps) at the heart of a [flexible matching and patching strategy](https://neil.fraser.name/writing/patch/).
+
+[DMP]: https://github.com/google/diff-match-patch
+[API]: https://github.com/google/diff-match-patch/wiki/API
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/__init__.py b/contrib/python/diff-match-patch/py3/diff_match_patch/__init__.py
new file mode 100644
index 0000000000..18ac58aadb
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/diff_match_patch/__init__.py
@@ -0,0 +1,10 @@
+"""
+Repackaging of Google's Diff Match and Patch libraries.
+
+Offers robust algorithms to perform the operations required for synchronizing plain text.
+"""
+
+from .__version__ import __version__
+from .diff_match_patch import __author__, __doc__, diff_match_patch, patch_obj
+
+__packager__ = "Amethyst Reese (amy@noswap.com)"
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/__version__.py b/contrib/python/diff-match-patch/py3/diff_match_patch/__version__.py
new file mode 100644
index 0000000000..1e429654a2
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/diff_match_patch/__version__.py
@@ -0,0 +1,7 @@
+"""
+This file is automatically generated by attribution.
+
+Do not edit manually. Get more info at https://attribution.omnilib.dev
+"""
+
+__version__ = "20230430"
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/diff_match_patch.py b/contrib/python/diff-match-patch/py3/diff_match_patch/diff_match_patch.py
new file mode 100644
index 0000000000..683f9487f2
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/diff_match_patch/diff_match_patch.py
@@ -0,0 +1,2019 @@
+#!/usr/bin/python3
+
+"""Diff Match and Patch
+Copyright 2018 The diff-match-patch Authors.
+https://github.com/google/diff-match-patch
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+"""Functions for diff, match and patch.
+
+Computes the difference between two texts to create a patch.
+Applies the patch onto another text, allowing for errors.
+"""
+
+__author__ = "fraser@google.com (Neil Fraser)"
+
+import re
+import sys
+import time
+import urllib.parse
+
+
+class diff_match_patch:
+ """Class containing the diff, match and patch methods.
+
+ Also contains the behaviour settings.
+ """
+
+ def __init__(self):
+ """Inits a diff_match_patch object with default settings.
+ Redefine these in your program to override the defaults.
+ """
+
+ # Number of seconds to map a diff before giving up (0 for infinity).
+ self.Diff_Timeout = 1.0
+ # Cost of an empty edit operation in terms of edit characters.
+ self.Diff_EditCost = 4
+ # At what point is no match declared (0.0 = perfection, 1.0 = very loose).
+ self.Match_Threshold = 0.5
+ # How far to search for a match (0 = exact location, 1000+ = broad match).
+ # A match this many characters away from the expected location will add
+ # 1.0 to the score (0.0 is a perfect match).
+ self.Match_Distance = 1000
+ # When deleting a large block of text (over ~64 characters), how close do
+ # the contents have to be to match the expected contents. (0.0 = perfection,
+ # 1.0 = very loose). Note that Match_Threshold controls how closely the
+ # end points of a delete need to match.
+ self.Patch_DeleteThreshold = 0.5
+ # Chunk size for context length.
+ self.Patch_Margin = 4
+
+ # The number of bits in an int.
+ # Python has no maximum, thus to disable patch splitting set to 0.
+ # However to avoid long patches in certain pathological cases, use 32.
+ # Multiple short patches (using native ints) are much faster than long ones.
+ self.Match_MaxBits = 32
+
+ # DIFF FUNCTIONS
+
+ # The data structure representing a diff is an array of tuples:
+ # [(DIFF_DELETE, "Hello"), (DIFF_INSERT, "Goodbye"), (DIFF_EQUAL, " world.")]
+ # which means: delete "Hello", add "Goodbye" and keep " world."
+ DIFF_DELETE = -1
+ DIFF_INSERT = 1
+ DIFF_EQUAL = 0
+
+ def diff_main(self, text1, text2, checklines=True, deadline=None):
+ """Find the differences between two texts. Simplifies the problem by
+ stripping any common prefix or suffix off the texts before diffing.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ checklines: Optional speedup flag. If present and false, then don't run
+ a line-level diff first to identify the changed areas.
+ Defaults to true, which does a faster, slightly less optimal diff.
+ deadline: Optional time when the diff should be complete by. Used
+ internally for recursive calls. Users should set DiffTimeout instead.
+
+ Returns:
+ Array of changes.
+ """
+ # Set a deadline by which time the diff must be complete.
+ if deadline == None:
+ # Unlike in most languages, Python counts time in seconds.
+ if self.Diff_Timeout <= 0:
+ deadline = sys.maxsize
+ else:
+ deadline = time.time() + self.Diff_Timeout
+
+ # Check for null inputs.
+ if text1 == None or text2 == None:
+ raise ValueError("Null inputs. (diff_main)")
+
+ # Check for equality (speedup).
+ if text1 == text2:
+ if text1:
+ return [(self.DIFF_EQUAL, text1)]
+ return []
+
+ # Trim off common prefix (speedup).
+ commonlength = self.diff_commonPrefix(text1, text2)
+ commonprefix = text1[:commonlength]
+ text1 = text1[commonlength:]
+ text2 = text2[commonlength:]
+
+ # Trim off common suffix (speedup).
+ commonlength = self.diff_commonSuffix(text1, text2)
+ if commonlength == 0:
+ commonsuffix = ""
+ else:
+ commonsuffix = text1[-commonlength:]
+ text1 = text1[:-commonlength]
+ text2 = text2[:-commonlength]
+
+ # Compute the diff on the middle block.
+ diffs = self.diff_compute(text1, text2, checklines, deadline)
+
+ # Restore the prefix and suffix.
+ if commonprefix:
+ diffs[:0] = [(self.DIFF_EQUAL, commonprefix)]
+ if commonsuffix:
+ diffs.append((self.DIFF_EQUAL, commonsuffix))
+ self.diff_cleanupMerge(diffs)
+ return diffs
+
+ def diff_compute(self, text1, text2, checklines, deadline):
+ """Find the differences between two texts. Assumes that the texts do not
+ have any common prefix or suffix.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ checklines: Speedup flag. If false, then don't run a line-level diff
+ first to identify the changed areas.
+ If true, then run a faster, slightly less optimal diff.
+ deadline: Time when the diff should be complete by.
+
+ Returns:
+ Array of changes.
+ """
+ if not text1:
+ # Just add some text (speedup).
+ return [(self.DIFF_INSERT, text2)]
+
+ if not text2:
+ # Just delete some text (speedup).
+ return [(self.DIFF_DELETE, text1)]
+
+ if len(text1) > len(text2):
+ (longtext, shorttext) = (text1, text2)
+ else:
+ (shorttext, longtext) = (text1, text2)
+ i = longtext.find(shorttext)
+ if i != -1:
+ # Shorter text is inside the longer text (speedup).
+ diffs = [
+ (self.DIFF_INSERT, longtext[:i]),
+ (self.DIFF_EQUAL, shorttext),
+ (self.DIFF_INSERT, longtext[i + len(shorttext) :]),
+ ]
+ # Swap insertions for deletions if diff is reversed.
+ if len(text1) > len(text2):
+ diffs[0] = (self.DIFF_DELETE, diffs[0][1])
+ diffs[2] = (self.DIFF_DELETE, diffs[2][1])
+ return diffs
+
+ if len(shorttext) == 1:
+ # Single character string.
+ # After the previous speedup, the character can't be an equality.
+ return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]
+
+ # Check to see if the problem can be split in two.
+ hm = self.diff_halfMatch(text1, text2)
+ if hm:
+ # A half-match was found, sort out the return data.
+ (text1_a, text1_b, text2_a, text2_b, mid_common) = hm
+ # Send both pairs off for separate processing.
+ diffs_a = self.diff_main(text1_a, text2_a, checklines, deadline)
+ diffs_b = self.diff_main(text1_b, text2_b, checklines, deadline)
+ # Merge the results.
+ return diffs_a + [(self.DIFF_EQUAL, mid_common)] + diffs_b
+
+ if checklines and len(text1) > 100 and len(text2) > 100:
+ return self.diff_lineMode(text1, text2, deadline)
+
+ return self.diff_bisect(text1, text2, deadline)
+
+ def diff_lineMode(self, text1, text2, deadline):
+ """Do a quick line-level diff on both strings, then rediff the parts for
+ greater accuracy.
+ This speedup can produce non-minimal diffs.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ deadline: Time when the diff should be complete by.
+
+ Returns:
+ Array of changes.
+ """
+
+ # Scan the text on a line-by-line basis first.
+ (text1, text2, linearray) = self.diff_linesToChars(text1, text2)
+
+ diffs = self.diff_main(text1, text2, False, deadline)
+
+ # Convert the diff back to original text.
+ self.diff_charsToLines(diffs, linearray)
+ # Eliminate freak matches (e.g. blank lines)
+ self.diff_cleanupSemantic(diffs)
+
+ # Rediff any replacement blocks, this time character-by-character.
+ # Add a dummy entry at the end.
+ diffs.append((self.DIFF_EQUAL, ""))
+ pointer = 0
+ count_delete = 0
+ count_insert = 0
+ text_delete = ""
+ text_insert = ""
+ while pointer < len(diffs):
+ if diffs[pointer][0] == self.DIFF_INSERT:
+ count_insert += 1
+ text_insert += diffs[pointer][1]
+ elif diffs[pointer][0] == self.DIFF_DELETE:
+ count_delete += 1
+ text_delete += diffs[pointer][1]
+ elif diffs[pointer][0] == self.DIFF_EQUAL:
+ # Upon reaching an equality, check for prior redundancies.
+ if count_delete >= 1 and count_insert >= 1:
+ # Delete the offending records and add the merged ones.
+ subDiff = self.diff_main(text_delete, text_insert, False, deadline)
+ diffs[pointer - count_delete - count_insert : pointer] = subDiff
+ pointer = pointer - count_delete - count_insert + len(subDiff)
+ count_insert = 0
+ count_delete = 0
+ text_delete = ""
+ text_insert = ""
+
+ pointer += 1
+
+ diffs.pop() # Remove the dummy entry at the end.
+
+ return diffs
+
+ def diff_bisect(self, text1, text2, deadline):
+ """Find the 'middle snake' of a diff, split the problem in two
+ and return the recursively constructed diff.
+ See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ deadline: Time at which to bail if not yet complete.
+
+ Returns:
+ Array of diff tuples.
+ """
+
+ # Cache the text lengths to prevent multiple calls.
+ text1_length = len(text1)
+ text2_length = len(text2)
+ max_d = (text1_length + text2_length + 1) // 2
+ v_offset = max_d
+ v_length = 2 * max_d
+ v1 = [-1] * v_length
+ v1[v_offset + 1] = 0
+ v2 = v1[:]
+ delta = text1_length - text2_length
+ # If the total number of characters is odd, then the front path will
+ # collide with the reverse path.
+ front = delta % 2 != 0
+ # Offsets for start and end of k loop.
+ # Prevents mapping of space beyond the grid.
+ k1start = 0
+ k1end = 0
+ k2start = 0
+ k2end = 0
+ for d in range(max_d):
+ # Bail out if deadline is reached.
+ if time.time() > deadline:
+ break
+
+ # Walk the front path one step.
+ for k1 in range(-d + k1start, d + 1 - k1end, 2):
+ k1_offset = v_offset + k1
+ if k1 == -d or (k1 != d and v1[k1_offset - 1] < v1[k1_offset + 1]):
+ x1 = v1[k1_offset + 1]
+ else:
+ x1 = v1[k1_offset - 1] + 1
+ y1 = x1 - k1
+ while (
+ x1 < text1_length and y1 < text2_length and text1[x1] == text2[y1]
+ ):
+ x1 += 1
+ y1 += 1
+ v1[k1_offset] = x1
+ if x1 > text1_length:
+ # Ran off the right of the graph.
+ k1end += 2
+ elif y1 > text2_length:
+ # Ran off the bottom of the graph.
+ k1start += 2
+ elif front:
+ k2_offset = v_offset + delta - k1
+ if k2_offset >= 0 and k2_offset < v_length and v2[k2_offset] != -1:
+ # Mirror x2 onto top-left coordinate system.
+ x2 = text1_length - v2[k2_offset]
+ if x1 >= x2:
+ # Overlap detected.
+ return self.diff_bisectSplit(text1, text2, x1, y1, deadline)
+
+ # Walk the reverse path one step.
+ for k2 in range(-d + k2start, d + 1 - k2end, 2):
+ k2_offset = v_offset + k2
+ if k2 == -d or (k2 != d and v2[k2_offset - 1] < v2[k2_offset + 1]):
+ x2 = v2[k2_offset + 1]
+ else:
+ x2 = v2[k2_offset - 1] + 1
+ y2 = x2 - k2
+ while (
+ x2 < text1_length
+ and y2 < text2_length
+ and text1[-x2 - 1] == text2[-y2 - 1]
+ ):
+ x2 += 1
+ y2 += 1
+ v2[k2_offset] = x2
+ if x2 > text1_length:
+ # Ran off the left of the graph.
+ k2end += 2
+ elif y2 > text2_length:
+ # Ran off the top of the graph.
+ k2start += 2
+ elif not front:
+ k1_offset = v_offset + delta - k2
+ if k1_offset >= 0 and k1_offset < v_length and v1[k1_offset] != -1:
+ x1 = v1[k1_offset]
+ y1 = v_offset + x1 - k1_offset
+ # Mirror x2 onto top-left coordinate system.
+ x2 = text1_length - x2
+ if x1 >= x2:
+ # Overlap detected.
+ return self.diff_bisectSplit(text1, text2, x1, y1, deadline)
+
+ # Diff took too long and hit the deadline or
+ # number of diffs equals number of characters, no commonality at all.
+ return [(self.DIFF_DELETE, text1), (self.DIFF_INSERT, text2)]
+
+ def diff_bisectSplit(self, text1, text2, x, y, deadline):
+ """Given the location of the 'middle snake', split the diff in two parts
+ and recurse.
+
+ Args:
+ text1: Old string to be diffed.
+ text2: New string to be diffed.
+ x: Index of split point in text1.
+ y: Index of split point in text2.
+ deadline: Time at which to bail if not yet complete.
+
+ Returns:
+ Array of diff tuples.
+ """
+ text1a = text1[:x]
+ text2a = text2[:y]
+ text1b = text1[x:]
+ text2b = text2[y:]
+
+ # Compute both diffs serially.
+ diffs = self.diff_main(text1a, text2a, False, deadline)
+ diffsb = self.diff_main(text1b, text2b, False, deadline)
+
+ return diffs + diffsb
+
+ def diff_linesToChars(self, text1, text2):
+ """Split two texts into an array of strings. Reduce the texts to a string
+ of hashes where each Unicode character represents one line.
+
+ Args:
+ text1: First string.
+ text2: Second string.
+
+ Returns:
+ Three element tuple, containing the encoded text1, the encoded text2 and
+ the array of unique strings. The zeroth element of the array of unique
+ strings is intentionally blank.
+ """
+ lineArray = [] # e.g. lineArray[4] == "Hello\n"
+ lineHash = {} # e.g. lineHash["Hello\n"] == 4
+
+ # "\x00" is a valid character, but various debuggers don't like it.
+ # So we'll insert a junk entry to avoid generating a null character.
+ lineArray.append("")
+
+ def diff_linesToCharsMunge(text):
+ """Split a text into an array of strings. Reduce the texts to a string
+ of hashes where each Unicode character represents one line.
+ Modifies linearray and linehash through being a closure.
+
+ Args:
+ text: String to encode.
+
+ Returns:
+ Encoded string.
+ """
+ chars = []
+ # Walk the text, pulling out a substring for each line.
+ # text.split('\n') would would temporarily double our memory footprint.
+ # Modifying text would create many large strings to garbage collect.
+ lineStart = 0
+ lineEnd = -1
+ while lineEnd < len(text) - 1:
+ lineEnd = text.find("\n", lineStart)
+ if lineEnd == -1:
+ lineEnd = len(text) - 1
+ line = text[lineStart : lineEnd + 1]
+
+ if line in lineHash:
+ chars.append(chr(lineHash[line]))
+ else:
+ if len(lineArray) == maxLines:
+ # Bail out at 1114111 because chr(1114112) throws.
+ line = text[lineStart:]
+ lineEnd = len(text)
+ lineArray.append(line)
+ lineHash[line] = len(lineArray) - 1
+ chars.append(chr(len(lineArray) - 1))
+ lineStart = lineEnd + 1
+ return "".join(chars)
+
+ # Allocate 2/3rds of the space for text1, the rest for text2.
+ maxLines = 666666
+ chars1 = diff_linesToCharsMunge(text1)
+ maxLines = 1114111
+ chars2 = diff_linesToCharsMunge(text2)
+ return (chars1, chars2, lineArray)
+
+ def diff_charsToLines(self, diffs, lineArray):
+ """Rehydrate the text in a diff from a string of line hashes to real lines
+ of text.
+
+ Args:
+ diffs: Array of diff tuples.
+ lineArray: Array of unique strings.
+ """
+ for i in range(len(diffs)):
+ text = []
+ for char in diffs[i][1]:
+ text.append(lineArray[ord(char)])
+ diffs[i] = (diffs[i][0], "".join(text))
+
+ def diff_commonPrefix(self, text1, text2):
+ """Determine the common prefix of two strings.
+
+ Args:
+ text1: First string.
+ text2: Second string.
+
+ Returns:
+ The number of characters common to the start of each string.
+ """
+ # Quick check for common null cases.
+ if not text1 or not text2 or text1[0] != text2[0]:
+ return 0
+ # Binary search.
+ # Performance analysis: https://neil.fraser.name/news/2007/10/09/
+ pointermin = 0
+ pointermax = min(len(text1), len(text2))
+ pointermid = pointermax
+ pointerstart = 0
+ while pointermin < pointermid:
+ if text1[pointerstart:pointermid] == text2[pointerstart:pointermid]:
+ pointermin = pointermid
+ pointerstart = pointermin
+ else:
+ pointermax = pointermid
+ pointermid = (pointermax - pointermin) // 2 + pointermin
+ return pointermid
+
+ def diff_commonSuffix(self, text1, text2):
+ """Determine the common suffix of two strings.
+
+ Args:
+ text1: First string.
+ text2: Second string.
+
+ Returns:
+ The number of characters common to the end of each string.
+ """
+ # Quick check for common null cases.
+ if not text1 or not text2 or text1[-1] != text2[-1]:
+ return 0
+ # Binary search.
+ # Performance analysis: https://neil.fraser.name/news/2007/10/09/
+ pointermin = 0
+ pointermax = min(len(text1), len(text2))
+ pointermid = pointermax
+ pointerend = 0
+ while pointermin < pointermid:
+ if (
+ text1[-pointermid : len(text1) - pointerend]
+ == text2[-pointermid : len(text2) - pointerend]
+ ):
+ pointermin = pointermid
+ pointerend = pointermin
+ else:
+ pointermax = pointermid
+ pointermid = (pointermax - pointermin) // 2 + pointermin
+ return pointermid
+
+ def diff_commonOverlap(self, text1, text2):
+ """Determine if the suffix of one string is the prefix of another.
+
+ Args:
+ text1 First string.
+ text2 Second string.
+
+ Returns:
+ The number of characters common to the end of the first
+ string and the start of the second string.
+ """
+ # Cache the text lengths to prevent multiple calls.
+ text1_length = len(text1)
+ text2_length = len(text2)
+ # Eliminate the null case.
+ if text1_length == 0 or text2_length == 0:
+ return 0
+ # Truncate the longer string.
+ if text1_length > text2_length:
+ text1 = text1[-text2_length:]
+ elif text1_length < text2_length:
+ text2 = text2[:text1_length]
+ text_length = min(text1_length, text2_length)
+ # Quick check for the worst case.
+ if text1 == text2:
+ return text_length
+
+ # Start by looking for a single character match
+ # and increase length until no match is found.
+ # Performance analysis: https://neil.fraser.name/news/2010/11/04/
+ best = 0
+ length = 1
+ while True:
+ pattern = text1[-length:]
+ found = text2.find(pattern)
+ if found == -1:
+ return best
+ length += found
+ if found == 0 or text1[-length:] == text2[:length]:
+ best = length
+ length += 1
+
+ def diff_halfMatch(self, text1, text2):
+ """Do the two texts share a substring which is at least half the length of
+ the longer text?
+ This speedup can produce non-minimal diffs.
+
+ Args:
+ text1: First string.
+ text2: Second string.
+
+ Returns:
+ Five element Array, containing the prefix of text1, the suffix of text1,
+ the prefix of text2, the suffix of text2 and the common middle. Or None
+ if there was no match.
+ """
+ if self.Diff_Timeout <= 0:
+ # Don't risk returning a non-optimal diff if we have unlimited time.
+ return None
+ if len(text1) > len(text2):
+ (longtext, shorttext) = (text1, text2)
+ else:
+ (shorttext, longtext) = (text1, text2)
+ if len(longtext) < 4 or len(shorttext) * 2 < len(longtext):
+ return None # Pointless.
+
+ def diff_halfMatchI(longtext, shorttext, i):
+ """Does a substring of shorttext exist within longtext such that the
+ substring is at least half the length of longtext?
+ Closure, but does not reference any external variables.
+
+ Args:
+ longtext: Longer string.
+ shorttext: Shorter string.
+ i: Start index of quarter length substring within longtext.
+
+ Returns:
+ Five element Array, containing the prefix of longtext, the suffix of
+ longtext, the prefix of shorttext, the suffix of shorttext and the
+ common middle. Or None if there was no match.
+ """
+ seed = longtext[i : i + len(longtext) // 4]
+ best_common = ""
+ j = shorttext.find(seed)
+ while j != -1:
+ prefixLength = self.diff_commonPrefix(longtext[i:], shorttext[j:])
+ suffixLength = self.diff_commonSuffix(longtext[:i], shorttext[:j])
+ if len(best_common) < suffixLength + prefixLength:
+ best_common = (
+ shorttext[j - suffixLength : j]
+ + shorttext[j : j + prefixLength]
+ )
+ best_longtext_a = longtext[: i - suffixLength]
+ best_longtext_b = longtext[i + prefixLength :]
+ best_shorttext_a = shorttext[: j - suffixLength]
+ best_shorttext_b = shorttext[j + prefixLength :]
+ j = shorttext.find(seed, j + 1)
+
+ if len(best_common) * 2 >= len(longtext):
+ return (
+ best_longtext_a,
+ best_longtext_b,
+ best_shorttext_a,
+ best_shorttext_b,
+ best_common,
+ )
+ else:
+ return None
+
+ # First check if the second quarter is the seed for a half-match.
+ hm1 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 3) // 4)
+ # Check again based on the third quarter.
+ hm2 = diff_halfMatchI(longtext, shorttext, (len(longtext) + 1) // 2)
+ if not hm1 and not hm2:
+ return None
+ elif not hm2:
+ hm = hm1
+ elif not hm1:
+ hm = hm2
+ else:
+ # Both matched. Select the longest.
+ if len(hm1[4]) > len(hm2[4]):
+ hm = hm1
+ else:
+ hm = hm2
+
+ # A half-match was found, sort out the return data.
+ if len(text1) > len(text2):
+ (text1_a, text1_b, text2_a, text2_b, mid_common) = hm
+ else:
+ (text2_a, text2_b, text1_a, text1_b, mid_common) = hm
+ return (text1_a, text1_b, text2_a, text2_b, mid_common)
+
+ def diff_cleanupSemantic(self, diffs):
+ """Reduce the number of edits by eliminating semantically trivial
+ equalities.
+
+ Args:
+ diffs: Array of diff tuples.
+ """
+ changes = False
+ equalities = [] # Stack of indices where equalities are found.
+ lastEquality = None # Always equal to diffs[equalities[-1]][1]
+ pointer = 0 # Index of current position.
+ # Number of chars that changed prior to the equality.
+ length_insertions1, length_deletions1 = 0, 0
+ # Number of chars that changed after the equality.
+ length_insertions2, length_deletions2 = 0, 0
+ while pointer < len(diffs):
+ if diffs[pointer][0] == self.DIFF_EQUAL: # Equality found.
+ equalities.append(pointer)
+ length_insertions1, length_insertions2 = length_insertions2, 0
+ length_deletions1, length_deletions2 = length_deletions2, 0
+ lastEquality = diffs[pointer][1]
+ else: # An insertion or deletion.
+ if diffs[pointer][0] == self.DIFF_INSERT:
+ length_insertions2 += len(diffs[pointer][1])
+ else:
+ length_deletions2 += len(diffs[pointer][1])
+ # Eliminate an equality that is smaller or equal to the edits on both
+ # sides of it.
+ if (
+ lastEquality
+ and (
+ len(lastEquality) <= max(length_insertions1, length_deletions1)
+ )
+ and (
+ len(lastEquality) <= max(length_insertions2, length_deletions2)
+ )
+ ):
+ # Duplicate record.
+ diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))
+ # Change second copy to insert.
+ diffs[equalities[-1] + 1] = (
+ self.DIFF_INSERT,
+ diffs[equalities[-1] + 1][1],
+ )
+ # Throw away the equality we just deleted.
+ equalities.pop()
+ # Throw away the previous equality (it needs to be reevaluated).
+ if len(equalities):
+ equalities.pop()
+ if len(equalities):
+ pointer = equalities[-1]
+ else:
+ pointer = -1
+ # Reset the counters.
+ length_insertions1, length_deletions1 = 0, 0
+ length_insertions2, length_deletions2 = 0, 0
+ lastEquality = None
+ changes = True
+ pointer += 1
+
+ # Normalize the diff.
+ if changes:
+ self.diff_cleanupMerge(diffs)
+ self.diff_cleanupSemanticLossless(diffs)
+
+ # Find any overlaps between deletions and insertions.
+ # e.g: <del>abcxxx</del><ins>xxxdef</ins>
+ # -> <del>abc</del>xxx<ins>def</ins>
+ # e.g: <del>xxxabc</del><ins>defxxx</ins>
+ # -> <ins>def</ins>xxx<del>abc</del>
+ # Only extract an overlap if it is as big as the edit ahead or behind it.
+ pointer = 1
+ while pointer < len(diffs):
+ if (
+ diffs[pointer - 1][0] == self.DIFF_DELETE
+ and diffs[pointer][0] == self.DIFF_INSERT
+ ):
+ deletion = diffs[pointer - 1][1]
+ insertion = diffs[pointer][1]
+ overlap_length1 = self.diff_commonOverlap(deletion, insertion)
+ overlap_length2 = self.diff_commonOverlap(insertion, deletion)
+ if overlap_length1 >= overlap_length2:
+ if (
+ overlap_length1 >= len(deletion) / 2.0
+ or overlap_length1 >= len(insertion) / 2.0
+ ):
+ # Overlap found. Insert an equality and trim the surrounding edits.
+ diffs.insert(
+ pointer, (self.DIFF_EQUAL, insertion[:overlap_length1])
+ )
+ diffs[pointer - 1] = (
+ self.DIFF_DELETE,
+ deletion[: len(deletion) - overlap_length1],
+ )
+ diffs[pointer + 1] = (
+ self.DIFF_INSERT,
+ insertion[overlap_length1:],
+ )
+ pointer += 1
+ else:
+ if (
+ overlap_length2 >= len(deletion) / 2.0
+ or overlap_length2 >= len(insertion) / 2.0
+ ):
+ # Reverse overlap found.
+ # Insert an equality and swap and trim the surrounding edits.
+ diffs.insert(
+ pointer, (self.DIFF_EQUAL, deletion[:overlap_length2])
+ )
+ diffs[pointer - 1] = (
+ self.DIFF_INSERT,
+ insertion[: len(insertion) - overlap_length2],
+ )
+ diffs[pointer + 1] = (
+ self.DIFF_DELETE,
+ deletion[overlap_length2:],
+ )
+ pointer += 1
+ pointer += 1
+ pointer += 1
+
+ def diff_cleanupSemanticLossless(self, diffs):
+ """Look for single edits surrounded on both sides by equalities
+ which can be shifted sideways to align the edit to a word boundary.
+ e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
+
+ Args:
+ diffs: Array of diff tuples.
+ """
+
+ def diff_cleanupSemanticScore(one, two):
+ """Given two strings, compute a score representing whether the
+ internal boundary falls on logical boundaries.
+ Scores range from 6 (best) to 0 (worst).
+ Closure, but does not reference any external variables.
+
+ Args:
+ one: First string.
+ two: Second string.
+
+ Returns:
+ The score.
+ """
+ if not one or not two:
+ # Edges are the best.
+ return 6
+
+ # Each port of this function behaves slightly differently due to
+ # subtle differences in each language's definition of things like
+ # 'whitespace'. Since this function's purpose is largely cosmetic,
+ # the choice has been made to use each language's native features
+ # rather than force total conformity.
+ char1 = one[-1]
+ char2 = two[0]
+ nonAlphaNumeric1 = not char1.isalnum()
+ nonAlphaNumeric2 = not char2.isalnum()
+ whitespace1 = nonAlphaNumeric1 and char1.isspace()
+ whitespace2 = nonAlphaNumeric2 and char2.isspace()
+ lineBreak1 = whitespace1 and (char1 == "\r" or char1 == "\n")
+ lineBreak2 = whitespace2 and (char2 == "\r" or char2 == "\n")
+ blankLine1 = lineBreak1 and self.BLANKLINEEND.search(one)
+ blankLine2 = lineBreak2 and self.BLANKLINESTART.match(two)
+
+ if blankLine1 or blankLine2:
+ # Five points for blank lines.
+ return 5
+ elif lineBreak1 or lineBreak2:
+ # Four points for line breaks.
+ return 4
+ elif nonAlphaNumeric1 and not whitespace1 and whitespace2:
+ # Three points for end of sentences.
+ return 3
+ elif whitespace1 or whitespace2:
+ # Two points for whitespace.
+ return 2
+ elif nonAlphaNumeric1 or nonAlphaNumeric2:
+ # One point for non-alphanumeric.
+ return 1
+ return 0
+
+ pointer = 1
+ # Intentionally ignore the first and last element (don't need checking).
+ while pointer < len(diffs) - 1:
+ if (
+ diffs[pointer - 1][0] == self.DIFF_EQUAL
+ and diffs[pointer + 1][0] == self.DIFF_EQUAL
+ ):
+ # This is a single edit surrounded by equalities.
+ equality1 = diffs[pointer - 1][1]
+ edit = diffs[pointer][1]
+ equality2 = diffs[pointer + 1][1]
+
+ # First, shift the edit as far left as possible.
+ commonOffset = self.diff_commonSuffix(equality1, edit)
+ if commonOffset:
+ commonString = edit[-commonOffset:]
+ equality1 = equality1[:-commonOffset]
+ edit = commonString + edit[:-commonOffset]
+ equality2 = commonString + equality2
+
+ # Second, step character by character right, looking for the best fit.
+ bestEquality1 = equality1
+ bestEdit = edit
+ bestEquality2 = equality2
+ bestScore = diff_cleanupSemanticScore(
+ equality1, edit
+ ) + diff_cleanupSemanticScore(edit, equality2)
+ while edit and equality2 and edit[0] == equality2[0]:
+ equality1 += edit[0]
+ edit = edit[1:] + equality2[0]
+ equality2 = equality2[1:]
+ score = diff_cleanupSemanticScore(
+ equality1, edit
+ ) + diff_cleanupSemanticScore(edit, equality2)
+ # The >= encourages trailing rather than leading whitespace on edits.
+ if score >= bestScore:
+ bestScore = score
+ bestEquality1 = equality1
+ bestEdit = edit
+ bestEquality2 = equality2
+
+ if diffs[pointer - 1][1] != bestEquality1:
+ # We have an improvement, save it back to the diff.
+ if bestEquality1:
+ diffs[pointer - 1] = (diffs[pointer - 1][0], bestEquality1)
+ else:
+ del diffs[pointer - 1]
+ pointer -= 1
+ diffs[pointer] = (diffs[pointer][0], bestEdit)
+ if bestEquality2:
+ diffs[pointer + 1] = (diffs[pointer + 1][0], bestEquality2)
+ else:
+ del diffs[pointer + 1]
+ pointer -= 1
+ pointer += 1
+
+ # Define some regex patterns for matching boundaries.
+ BLANKLINEEND = re.compile(r"\n\r?\n$")
+ BLANKLINESTART = re.compile(r"^\r?\n\r?\n")
+
+ def diff_cleanupEfficiency(self, diffs):
+ """Reduce the number of edits by eliminating operationally trivial
+ equalities.
+
+ Args:
+ diffs: Array of diff tuples.
+ """
+ changes = False
+ equalities = [] # Stack of indices where equalities are found.
+ lastEquality = None # Always equal to diffs[equalities[-1]][1]
+ pointer = 0 # Index of current position.
+ pre_ins = False # Is there an insertion operation before the last equality.
+ pre_del = False # Is there a deletion operation before the last equality.
+ post_ins = False # Is there an insertion operation after the last equality.
+ post_del = False # Is there a deletion operation after the last equality.
+ while pointer < len(diffs):
+ if diffs[pointer][0] == self.DIFF_EQUAL: # Equality found.
+ if len(diffs[pointer][1]) < self.Diff_EditCost and (
+ post_ins or post_del
+ ):
+ # Candidate found.
+ equalities.append(pointer)
+ pre_ins = post_ins
+ pre_del = post_del
+ lastEquality = diffs[pointer][1]
+ else:
+ # Not a candidate, and can never become one.
+ equalities = []
+ lastEquality = None
+
+ post_ins = post_del = False
+ else: # An insertion or deletion.
+ if diffs[pointer][0] == self.DIFF_DELETE:
+ post_del = True
+ else:
+ post_ins = True
+
+ # Five types to be split:
+ # <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+ # <ins>A</ins>X<ins>C</ins><del>D</del>
+ # <ins>A</ins><del>B</del>X<ins>C</ins>
+ # <ins>A</del>X<ins>C</ins><del>D</del>
+ # <ins>A</ins><del>B</del>X<del>C</del>
+
+ if lastEquality and (
+ (pre_ins and pre_del and post_ins and post_del)
+ or (
+ (len(lastEquality) < self.Diff_EditCost / 2)
+ and (pre_ins + pre_del + post_ins + post_del) == 3
+ )
+ ):
+ # Duplicate record.
+ diffs.insert(equalities[-1], (self.DIFF_DELETE, lastEquality))
+ # Change second copy to insert.
+ diffs[equalities[-1] + 1] = (
+ self.DIFF_INSERT,
+ diffs[equalities[-1] + 1][1],
+ )
+ equalities.pop() # Throw away the equality we just deleted.
+ lastEquality = None
+ if pre_ins and pre_del:
+ # No changes made which could affect previous entry, keep going.
+ post_ins = post_del = True
+ equalities = []
+ else:
+ if len(equalities):
+ equalities.pop() # Throw away the previous equality.
+ if len(equalities):
+ pointer = equalities[-1]
+ else:
+ pointer = -1
+ post_ins = post_del = False
+ changes = True
+ pointer += 1
+
+ if changes:
+ self.diff_cleanupMerge(diffs)
+
+ def diff_cleanupMerge(self, diffs):
+ """Reorder and merge like edit sections. Merge equalities.
+ Any edit section can move as long as it doesn't cross an equality.
+
+ Args:
+ diffs: Array of diff tuples.
+ """
+ diffs.append((self.DIFF_EQUAL, "")) # Add a dummy entry at the end.
+ pointer = 0
+ count_delete = 0
+ count_insert = 0
+ text_delete = ""
+ text_insert = ""
+ while pointer < len(diffs):
+ if diffs[pointer][0] == self.DIFF_INSERT:
+ count_insert += 1
+ text_insert += diffs[pointer][1]
+ pointer += 1
+ elif diffs[pointer][0] == self.DIFF_DELETE:
+ count_delete += 1
+ text_delete += diffs[pointer][1]
+ pointer += 1
+ elif diffs[pointer][0] == self.DIFF_EQUAL:
+ # Upon reaching an equality, check for prior redundancies.
+ if count_delete + count_insert > 1:
+ if count_delete != 0 and count_insert != 0:
+ # Factor out any common prefixies.
+ commonlength = self.diff_commonPrefix(text_insert, text_delete)
+ if commonlength != 0:
+ x = pointer - count_delete - count_insert - 1
+ if x >= 0 and diffs[x][0] == self.DIFF_EQUAL:
+ diffs[x] = (
+ diffs[x][0],
+ diffs[x][1] + text_insert[:commonlength],
+ )
+ else:
+ diffs.insert(
+ 0, (self.DIFF_EQUAL, text_insert[:commonlength])
+ )
+ pointer += 1
+ text_insert = text_insert[commonlength:]
+ text_delete = text_delete[commonlength:]
+ # Factor out any common suffixies.
+ commonlength = self.diff_commonSuffix(text_insert, text_delete)
+ if commonlength != 0:
+ diffs[pointer] = (
+ diffs[pointer][0],
+ text_insert[-commonlength:] + diffs[pointer][1],
+ )
+ text_insert = text_insert[:-commonlength]
+ text_delete = text_delete[:-commonlength]
+ # Delete the offending records and add the merged ones.
+ new_ops = []
+ if len(text_delete) != 0:
+ new_ops.append((self.DIFF_DELETE, text_delete))
+ if len(text_insert) != 0:
+ new_ops.append((self.DIFF_INSERT, text_insert))
+ pointer -= count_delete + count_insert
+ diffs[pointer : pointer + count_delete + count_insert] = new_ops
+ pointer += len(new_ops) + 1
+ elif pointer != 0 and diffs[pointer - 1][0] == self.DIFF_EQUAL:
+ # Merge this equality with the previous one.
+ diffs[pointer - 1] = (
+ diffs[pointer - 1][0],
+ diffs[pointer - 1][1] + diffs[pointer][1],
+ )
+ del diffs[pointer]
+ else:
+ pointer += 1
+
+ count_insert = 0
+ count_delete = 0
+ text_delete = ""
+ text_insert = ""
+
+ if diffs[-1][1] == "":
+ diffs.pop() # Remove the dummy entry at the end.
+
+ # Second pass: look for single edits surrounded on both sides by equalities
+ # which can be shifted sideways to eliminate an equality.
+ # e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+ changes = False
+ pointer = 1
+ # Intentionally ignore the first and last element (don't need checking).
+ while pointer < len(diffs) - 1:
+ if (
+ diffs[pointer - 1][0] == self.DIFF_EQUAL
+ and diffs[pointer + 1][0] == self.DIFF_EQUAL
+ ):
+ # This is a single edit surrounded by equalities.
+ if diffs[pointer][1].endswith(diffs[pointer - 1][1]):
+ # Shift the edit over the previous equality.
+ if diffs[pointer - 1][1] != "":
+ diffs[pointer] = (
+ diffs[pointer][0],
+ diffs[pointer - 1][1]
+ + diffs[pointer][1][: -len(diffs[pointer - 1][1])],
+ )
+ diffs[pointer + 1] = (
+ diffs[pointer + 1][0],
+ diffs[pointer - 1][1] + diffs[pointer + 1][1],
+ )
+ del diffs[pointer - 1]
+ changes = True
+ elif diffs[pointer][1].startswith(diffs[pointer + 1][1]):
+ # Shift the edit over the next equality.
+ diffs[pointer - 1] = (
+ diffs[pointer - 1][0],
+ diffs[pointer - 1][1] + diffs[pointer + 1][1],
+ )
+ diffs[pointer] = (
+ diffs[pointer][0],
+ diffs[pointer][1][len(diffs[pointer + 1][1]) :]
+ + diffs[pointer + 1][1],
+ )
+ del diffs[pointer + 1]
+ changes = True
+ pointer += 1
+
+ # If shifts were made, the diff needs reordering and another shift sweep.
+ if changes:
+ self.diff_cleanupMerge(diffs)
+
+ def diff_xIndex(self, diffs, loc):
+ """loc is a location in text1, compute and return the equivalent location
+ in text2. e.g. "The cat" vs "The big cat", 1->1, 5->8
+
+ Args:
+ diffs: Array of diff tuples.
+ loc: Location within text1.
+
+ Returns:
+ Location within text2.
+ """
+ chars1 = 0
+ chars2 = 0
+ last_chars1 = 0
+ last_chars2 = 0
+ for x in range(len(diffs)):
+ (op, text) = diffs[x]
+ if op != self.DIFF_INSERT: # Equality or deletion.
+ chars1 += len(text)
+ if op != self.DIFF_DELETE: # Equality or insertion.
+ chars2 += len(text)
+ if chars1 > loc: # Overshot the location.
+ break
+ last_chars1 = chars1
+ last_chars2 = chars2
+
+ if len(diffs) != x and diffs[x][0] == self.DIFF_DELETE:
+ # The location was deleted.
+ return last_chars2
+ # Add the remaining len(character).
+ return last_chars2 + (loc - last_chars1)
+
+ def diff_prettyHtml(self, diffs):
+ """Convert a diff array into a pretty HTML report.
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ HTML representation.
+ """
+ html = []
+ for op, data in diffs:
+ text = (
+ data.replace("&", "&amp;")
+ .replace("<", "&lt;")
+ .replace(">", "&gt;")
+ .replace("\n", "&para;<br>")
+ )
+ if op == self.DIFF_INSERT:
+ html.append('<ins style="background:#e6ffe6;">%s</ins>' % text)
+ elif op == self.DIFF_DELETE:
+ html.append('<del style="background:#ffe6e6;">%s</del>' % text)
+ elif op == self.DIFF_EQUAL:
+ html.append("<span>%s</span>" % text)
+ return "".join(html)
+
+ def diff_text1(self, diffs):
+ """Compute and return the source text (all equalities and deletions).
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ Source text.
+ """
+ text = []
+ for op, data in diffs:
+ if op != self.DIFF_INSERT:
+ text.append(data)
+ return "".join(text)
+
+ def diff_text2(self, diffs):
+ """Compute and return the destination text (all equalities and insertions).
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ Destination text.
+ """
+ text = []
+ for op, data in diffs:
+ if op != self.DIFF_DELETE:
+ text.append(data)
+ return "".join(text)
+
+ def diff_levenshtein(self, diffs):
+ """Compute the Levenshtein distance; the number of inserted, deleted or
+ substituted characters.
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ Number of changes.
+ """
+ levenshtein = 0
+ insertions = 0
+ deletions = 0
+ for op, data in diffs:
+ if op == self.DIFF_INSERT:
+ insertions += len(data)
+ elif op == self.DIFF_DELETE:
+ deletions += len(data)
+ elif op == self.DIFF_EQUAL:
+ # A deletion and an insertion is one substitution.
+ levenshtein += max(insertions, deletions)
+ insertions = 0
+ deletions = 0
+ levenshtein += max(insertions, deletions)
+ return levenshtein
+
+ def diff_toDelta(self, diffs):
+ """Crush the diff into an encoded string which describes the operations
+ required to transform text1 into text2.
+ E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'.
+ Operations are tab-separated. Inserted text is escaped using %xx notation.
+
+ Args:
+ diffs: Array of diff tuples.
+
+ Returns:
+ Delta text.
+ """
+ text = []
+ for op, data in diffs:
+ if op == self.DIFF_INSERT:
+ # High ascii will raise UnicodeDecodeError. Use Unicode instead.
+ data = data.encode("utf-8")
+ text.append("+" + urllib.parse.quote(data, "!~*'();/?:@&=+$,# "))
+ elif op == self.DIFF_DELETE:
+ text.append("-%d" % len(data))
+ elif op == self.DIFF_EQUAL:
+ text.append("=%d" % len(data))
+ return "\t".join(text)
+
+ def diff_fromDelta(self, text1, delta):
+ """Given the original text1, and an encoded string which describes the
+ operations required to transform text1 into text2, compute the full diff.
+
+ Args:
+ text1: Source string for the diff.
+ delta: Delta text.
+
+ Returns:
+ Array of diff tuples.
+
+ Raises:
+ ValueError: If invalid input.
+ """
+ diffs = []
+ pointer = 0 # Cursor in text1
+ tokens = delta.split("\t")
+ for token in tokens:
+ if token == "":
+ # Blank tokens are ok (from a trailing \t).
+ continue
+ # Each token begins with a one character parameter which specifies the
+ # operation of this token (delete, insert, equality).
+ param = token[1:]
+ if token[0] == "+":
+ param = urllib.parse.unquote(param)
+ diffs.append((self.DIFF_INSERT, param))
+ elif token[0] == "-" or token[0] == "=":
+ try:
+ n = int(param)
+ except ValueError:
+ raise ValueError("Invalid number in diff_fromDelta: " + param)
+ if n < 0:
+ raise ValueError("Negative number in diff_fromDelta: " + param)
+ text = text1[pointer : pointer + n]
+ pointer += n
+ if token[0] == "=":
+ diffs.append((self.DIFF_EQUAL, text))
+ else:
+ diffs.append((self.DIFF_DELETE, text))
+ else:
+ # Anything else is an error.
+ raise ValueError(
+ "Invalid diff operation in diff_fromDelta: " + token[0]
+ )
+ if pointer != len(text1):
+ raise ValueError(
+ "Delta length (%d) does not equal source text length (%d)."
+ % (pointer, len(text1))
+ )
+ return diffs
+
+ # MATCH FUNCTIONS
+
+ def match_main(self, text, pattern, loc):
+ """Locate the best instance of 'pattern' in 'text' near 'loc'.
+
+ Args:
+ text: The text to search.
+ pattern: The pattern to search for.
+ loc: The location to search around.
+
+ Returns:
+ Best match index or -1.
+ """
+ # Check for null inputs.
+ if text == None or pattern == None:
+ raise ValueError("Null inputs. (match_main)")
+
+ loc = max(0, min(loc, len(text)))
+ if text == pattern:
+ # Shortcut (potentially not guaranteed by the algorithm)
+ return 0
+ elif not text:
+ # Nothing to match.
+ return -1
+ elif text[loc : loc + len(pattern)] == pattern:
+ # Perfect match at the perfect spot! (Includes case of null pattern)
+ return loc
+ else:
+ # Do a fuzzy compare.
+ match = self.match_bitap(text, pattern, loc)
+ return match
+
+ def match_bitap(self, text, pattern, loc):
+ """Locate the best instance of 'pattern' in 'text' near 'loc' using the
+ Bitap algorithm.
+
+ Args:
+ text: The text to search.
+ pattern: The pattern to search for.
+ loc: The location to search around.
+
+ Returns:
+ Best match index or -1.
+ """
+ # Python doesn't have a maxint limit, so ignore this check.
+ # if self.Match_MaxBits != 0 and len(pattern) > self.Match_MaxBits:
+ # raise ValueError("Pattern too long for this application.")
+
+ # Initialise the alphabet.
+ s = self.match_alphabet(pattern)
+
+ def match_bitapScore(e, x):
+ """Compute and return the score for a match with e errors and x location.
+ Accesses loc and pattern through being a closure.
+
+ Args:
+ e: Number of errors in match.
+ x: Location of match.
+
+ Returns:
+ Overall score for match (0.0 = good, 1.0 = bad).
+ """
+ accuracy = float(e) / len(pattern)
+ proximity = abs(loc - x)
+ if not self.Match_Distance:
+ # Dodge divide by zero error.
+ return proximity and 1.0 or accuracy
+ return accuracy + (proximity / float(self.Match_Distance))
+
+ # Highest score beyond which we give up.
+ score_threshold = self.Match_Threshold
+ # Is there a nearby exact match? (speedup)
+ best_loc = text.find(pattern, loc)
+ if best_loc != -1:
+ score_threshold = min(match_bitapScore(0, best_loc), score_threshold)
+ # What about in the other direction? (speedup)
+ best_loc = text.rfind(pattern, loc + len(pattern))
+ if best_loc != -1:
+ score_threshold = min(match_bitapScore(0, best_loc), score_threshold)
+
+ # Initialise the bit arrays.
+ matchmask = 1 << (len(pattern) - 1)
+ best_loc = -1
+
+ bin_max = len(pattern) + len(text)
+ # Empty initialization added to appease pychecker.
+ last_rd = None
+ for d in range(len(pattern)):
+ # Scan for the best match each iteration allows for one more error.
+ # Run a binary search to determine how far from 'loc' we can stray at
+ # this error level.
+ bin_min = 0
+ bin_mid = bin_max
+ while bin_min < bin_mid:
+ if match_bitapScore(d, loc + bin_mid) <= score_threshold:
+ bin_min = bin_mid
+ else:
+ bin_max = bin_mid
+ bin_mid = (bin_max - bin_min) // 2 + bin_min
+
+ # Use the result from this iteration as the maximum for the next.
+ bin_max = bin_mid
+ start = max(1, loc - bin_mid + 1)
+ finish = min(loc + bin_mid, len(text)) + len(pattern)
+
+ rd = [0] * (finish + 2)
+ rd[finish + 1] = (1 << d) - 1
+ for j in range(finish, start - 1, -1):
+ if len(text) <= j - 1:
+ # Out of range.
+ charMatch = 0
+ else:
+ charMatch = s.get(text[j - 1], 0)
+ if d == 0: # First pass: exact match.
+ rd[j] = ((rd[j + 1] << 1) | 1) & charMatch
+ else: # Subsequent passes: fuzzy match.
+ rd[j] = (
+ (((rd[j + 1] << 1) | 1) & charMatch)
+ | (((last_rd[j + 1] | last_rd[j]) << 1) | 1)
+ | last_rd[j + 1]
+ )
+ if rd[j] & matchmask:
+ score = match_bitapScore(d, j - 1)
+ # This match will almost certainly be better than any existing match.
+ # But check anyway.
+ if score <= score_threshold:
+ # Told you so.
+ score_threshold = score
+ best_loc = j - 1
+ if best_loc > loc:
+ # When passing loc, don't exceed our current distance from loc.
+ start = max(1, 2 * loc - best_loc)
+ else:
+ # Already passed loc, downhill from here on in.
+ break
+ # No hope for a (better) match at greater error levels.
+ if match_bitapScore(d + 1, loc) > score_threshold:
+ break
+ last_rd = rd
+ return best_loc
+
+ def match_alphabet(self, pattern):
+ """Initialise the alphabet for the Bitap algorithm.
+
+ Args:
+ pattern: The text to encode.
+
+ Returns:
+ Hash of character locations.
+ """
+ s = {}
+ for char in pattern:
+ s[char] = 0
+ for i in range(len(pattern)):
+ s[pattern[i]] |= 1 << (len(pattern) - i - 1)
+ return s
+
+ # PATCH FUNCTIONS
+
+ def patch_addContext(self, patch, text):
+ """Increase the context until it is unique,
+ but don't let the pattern expand beyond Match_MaxBits.
+
+ Args:
+ patch: The patch to grow.
+ text: Source text.
+ """
+ if len(text) == 0:
+ return
+ pattern = text[patch.start2 : patch.start2 + patch.length1]
+ padding = 0
+
+ # Look for the first and last matches of pattern in text. If two different
+ # matches are found, increase the pattern length.
+ while text.find(pattern) != text.rfind(pattern) and (
+ self.Match_MaxBits == 0
+ or len(pattern) < self.Match_MaxBits - self.Patch_Margin - self.Patch_Margin
+ ):
+ padding += self.Patch_Margin
+ pattern = text[
+ max(0, patch.start2 - padding) : patch.start2 + patch.length1 + padding
+ ]
+ # Add one chunk for good luck.
+ padding += self.Patch_Margin
+
+ # Add the prefix.
+ prefix = text[max(0, patch.start2 - padding) : patch.start2]
+ if prefix:
+ patch.diffs[:0] = [(self.DIFF_EQUAL, prefix)]
+ # Add the suffix.
+ suffix = text[
+ patch.start2 + patch.length1 : patch.start2 + patch.length1 + padding
+ ]
+ if suffix:
+ patch.diffs.append((self.DIFF_EQUAL, suffix))
+
+ # Roll back the start points.
+ patch.start1 -= len(prefix)
+ patch.start2 -= len(prefix)
+ # Extend lengths.
+ patch.length1 += len(prefix) + len(suffix)
+ patch.length2 += len(prefix) + len(suffix)
+
+ def patch_make(self, a, b=None, c=None):
+ """Compute a list of patches to turn text1 into text2.
+ Use diffs if provided, otherwise compute it ourselves.
+ There are four ways to call this function, depending on what data is
+ available to the caller:
+ Method 1:
+ a = text1, b = text2
+ Method 2:
+ a = diffs
+ Method 3 (optimal):
+ a = text1, b = diffs
+ Method 4 (deprecated, use method 3):
+ a = text1, b = text2, c = diffs
+
+ Args:
+ a: text1 (methods 1,3,4) or Array of diff tuples for text1 to
+ text2 (method 2).
+ b: text2 (methods 1,4) or Array of diff tuples for text1 to
+ text2 (method 3) or undefined (method 2).
+ c: Array of diff tuples for text1 to text2 (method 4) or
+ undefined (methods 1,2,3).
+
+ Returns:
+ Array of Patch objects.
+ """
+ text1 = None
+ diffs = None
+ if isinstance(a, str) and isinstance(b, str) and c is None:
+ # Method 1: text1, text2
+ # Compute diffs from text1 and text2.
+ text1 = a
+ diffs = self.diff_main(text1, b, True)
+ if len(diffs) > 2:
+ self.diff_cleanupSemantic(diffs)
+ self.diff_cleanupEfficiency(diffs)
+ elif isinstance(a, list) and b is None and c is None:
+ # Method 2: diffs
+ # Compute text1 from diffs.
+ diffs = a
+ text1 = self.diff_text1(diffs)
+ elif isinstance(a, str) and isinstance(b, list) and c is None:
+ # Method 3: text1, diffs
+ text1 = a
+ diffs = b
+ elif isinstance(a, str) and isinstance(b, str) and isinstance(c, list):
+ # Method 4: text1, text2, diffs
+ # text2 is not used.
+ text1 = a
+ diffs = c
+ else:
+ raise ValueError("Unknown call format to patch_make.")
+
+ if not diffs:
+ return [] # Get rid of the None case.
+ patches = []
+ patch = patch_obj()
+ char_count1 = 0 # Number of characters into the text1 string.
+ char_count2 = 0 # Number of characters into the text2 string.
+ prepatch_text = text1 # Recreate the patches to determine context info.
+ postpatch_text = text1
+ for x in range(len(diffs)):
+ (diff_type, diff_text) = diffs[x]
+ if len(patch.diffs) == 0 and diff_type != self.DIFF_EQUAL:
+ # A new patch starts here.
+ patch.start1 = char_count1
+ patch.start2 = char_count2
+ if diff_type == self.DIFF_INSERT:
+ # Insertion
+ patch.diffs.append(diffs[x])
+ patch.length2 += len(diff_text)
+ postpatch_text = (
+ postpatch_text[:char_count2]
+ + diff_text
+ + postpatch_text[char_count2:]
+ )
+ elif diff_type == self.DIFF_DELETE:
+ # Deletion.
+ patch.length1 += len(diff_text)
+ patch.diffs.append(diffs[x])
+ postpatch_text = (
+ postpatch_text[:char_count2]
+ + postpatch_text[char_count2 + len(diff_text) :]
+ )
+ elif (
+ diff_type == self.DIFF_EQUAL
+ and len(diff_text) <= 2 * self.Patch_Margin
+ and len(patch.diffs) != 0
+ and len(diffs) != x + 1
+ ):
+ # Small equality inside a patch.
+ patch.diffs.append(diffs[x])
+ patch.length1 += len(diff_text)
+ patch.length2 += len(diff_text)
+
+ if diff_type == self.DIFF_EQUAL and len(diff_text) >= 2 * self.Patch_Margin:
+ # Time for a new patch.
+ if len(patch.diffs) != 0:
+ self.patch_addContext(patch, prepatch_text)
+ patches.append(patch)
+ patch = patch_obj()
+ # Unlike Unidiff, our patch lists have a rolling context.
+ # https://github.com/google/diff-match-patch/wiki/Unidiff
+ # Update prepatch text & pos to reflect the application of the
+ # just completed patch.
+ prepatch_text = postpatch_text
+ char_count1 = char_count2
+
+ # Update the current character count.
+ if diff_type != self.DIFF_INSERT:
+ char_count1 += len(diff_text)
+ if diff_type != self.DIFF_DELETE:
+ char_count2 += len(diff_text)
+
+ # Pick up the leftover patch if not empty.
+ if len(patch.diffs) != 0:
+ self.patch_addContext(patch, prepatch_text)
+ patches.append(patch)
+ return patches
+
+ def patch_deepCopy(self, patches):
+ """Given an array of patches, return another array that is identical.
+
+ Args:
+ patches: Array of Patch objects.
+
+ Returns:
+ Array of Patch objects.
+ """
+ patchesCopy = []
+ for patch in patches:
+ patchCopy = patch_obj()
+ # No need to deep copy the tuples since they are immutable.
+ patchCopy.diffs = patch.diffs[:]
+ patchCopy.start1 = patch.start1
+ patchCopy.start2 = patch.start2
+ patchCopy.length1 = patch.length1
+ patchCopy.length2 = patch.length2
+ patchesCopy.append(patchCopy)
+ return patchesCopy
+
+ def patch_apply(self, patches, text):
+ """Merge a set of patches onto the text. Return a patched text, as well
+ as a list of true/false values indicating which patches were applied.
+
+ Args:
+ patches: Array of Patch objects.
+ text: Old text.
+
+ Returns:
+ Two element Array, containing the new text and an array of boolean values.
+ """
+ if not patches:
+ return (text, [])
+
+ # Deep copy the patches so that no changes are made to originals.
+ patches = self.patch_deepCopy(patches)
+
+ nullPadding = self.patch_addPadding(patches)
+ text = nullPadding + text + nullPadding
+ self.patch_splitMax(patches)
+
+ # delta keeps track of the offset between the expected and actual location
+ # of the previous patch. If there are patches expected at positions 10 and
+ # 20, but the first patch was found at 12, delta is 2 and the second patch
+ # has an effective expected position of 22.
+ delta = 0
+ results = []
+ for patch in patches:
+ expected_loc = patch.start2 + delta
+ text1 = self.diff_text1(patch.diffs)
+ end_loc = -1
+ if len(text1) > self.Match_MaxBits:
+ # patch_splitMax will only provide an oversized pattern in the case of
+ # a monster delete.
+ start_loc = self.match_main(
+ text, text1[: self.Match_MaxBits], expected_loc
+ )
+ if start_loc != -1:
+ end_loc = self.match_main(
+ text,
+ text1[-self.Match_MaxBits :],
+ expected_loc + len(text1) - self.Match_MaxBits,
+ )
+ if end_loc == -1 or start_loc >= end_loc:
+ # Can't find valid trailing context. Drop this patch.
+ start_loc = -1
+ else:
+ start_loc = self.match_main(text, text1, expected_loc)
+ if start_loc == -1:
+ # No match found. :(
+ results.append(False)
+ # Subtract the delta for this failed patch from subsequent patches.
+ delta -= patch.length2 - patch.length1
+ else:
+ # Found a match. :)
+ results.append(True)
+ delta = start_loc - expected_loc
+ if end_loc == -1:
+ text2 = text[start_loc : start_loc + len(text1)]
+ else:
+ text2 = text[start_loc : end_loc + self.Match_MaxBits]
+ if text1 == text2:
+ # Perfect match, just shove the replacement text in.
+ text = (
+ text[:start_loc]
+ + self.diff_text2(patch.diffs)
+ + text[start_loc + len(text1) :]
+ )
+ else:
+ # Imperfect match.
+ # Run a diff to get a framework of equivalent indices.
+ diffs = self.diff_main(text1, text2, False)
+ if (
+ len(text1) > self.Match_MaxBits
+ and self.diff_levenshtein(diffs) / float(len(text1))
+ > self.Patch_DeleteThreshold
+ ):
+ # The end points match, but the content is unacceptably bad.
+ results[-1] = False
+ else:
+ self.diff_cleanupSemanticLossless(diffs)
+ index1 = 0
+ for op, data in patch.diffs:
+ if op != self.DIFF_EQUAL:
+ index2 = self.diff_xIndex(diffs, index1)
+ if op == self.DIFF_INSERT: # Insertion
+ text = (
+ text[: start_loc + index2]
+ + data
+ + text[start_loc + index2 :]
+ )
+ elif op == self.DIFF_DELETE: # Deletion
+ text = (
+ text[: start_loc + index2]
+ + text[
+ start_loc
+ + self.diff_xIndex(diffs, index1 + len(data)) :
+ ]
+ )
+ if op != self.DIFF_DELETE:
+ index1 += len(data)
+ # Strip the padding off.
+ text = text[len(nullPadding) : -len(nullPadding)]
+ return (text, results)
+
+ def patch_addPadding(self, patches):
+ """Add some padding on text start and end so that edges can match
+ something. Intended to be called only from within patch_apply.
+
+ Args:
+ patches: Array of Patch objects.
+
+ Returns:
+ The padding string added to each side.
+ """
+ paddingLength = self.Patch_Margin
+ nullPadding = ""
+ for x in range(1, paddingLength + 1):
+ nullPadding += chr(x)
+
+ # Bump all the patches forward.
+ for patch in patches:
+ patch.start1 += paddingLength
+ patch.start2 += paddingLength
+
+ # Add some padding on start of first diff.
+ patch = patches[0]
+ diffs = patch.diffs
+ if not diffs or diffs[0][0] != self.DIFF_EQUAL:
+ # Add nullPadding equality.
+ diffs.insert(0, (self.DIFF_EQUAL, nullPadding))
+ patch.start1 -= paddingLength # Should be 0.
+ patch.start2 -= paddingLength # Should be 0.
+ patch.length1 += paddingLength
+ patch.length2 += paddingLength
+ elif paddingLength > len(diffs[0][1]):
+ # Grow first equality.
+ extraLength = paddingLength - len(diffs[0][1])
+ newText = nullPadding[len(diffs[0][1]) :] + diffs[0][1]
+ diffs[0] = (diffs[0][0], newText)
+ patch.start1 -= extraLength
+ patch.start2 -= extraLength
+ patch.length1 += extraLength
+ patch.length2 += extraLength
+
+ # Add some padding on end of last diff.
+ patch = patches[-1]
+ diffs = patch.diffs
+ if not diffs or diffs[-1][0] != self.DIFF_EQUAL:
+ # Add nullPadding equality.
+ diffs.append((self.DIFF_EQUAL, nullPadding))
+ patch.length1 += paddingLength
+ patch.length2 += paddingLength
+ elif paddingLength > len(diffs[-1][1]):
+ # Grow last equality.
+ extraLength = paddingLength - len(diffs[-1][1])
+ newText = diffs[-1][1] + nullPadding[:extraLength]
+ diffs[-1] = (diffs[-1][0], newText)
+ patch.length1 += extraLength
+ patch.length2 += extraLength
+
+ return nullPadding
+
+ def patch_splitMax(self, patches):
+ """Look through the patches and break up any which are longer than the
+ maximum limit of the match algorithm.
+ Intended to be called only from within patch_apply.
+
+ Args:
+ patches: Array of Patch objects.
+ """
+ patch_size = self.Match_MaxBits
+ if patch_size == 0:
+ # Python has the option of not splitting strings due to its ability
+ # to handle integers of arbitrary precision.
+ return
+ for x in range(len(patches)):
+ if patches[x].length1 <= patch_size:
+ continue
+ bigpatch = patches[x]
+ # Remove the big old patch.
+ del patches[x]
+ x -= 1
+ start1 = bigpatch.start1
+ start2 = bigpatch.start2
+ precontext = ""
+ while len(bigpatch.diffs) != 0:
+ # Create one of several smaller patches.
+ patch = patch_obj()
+ empty = True
+ patch.start1 = start1 - len(precontext)
+ patch.start2 = start2 - len(precontext)
+ if precontext:
+ patch.length1 = patch.length2 = len(precontext)
+ patch.diffs.append((self.DIFF_EQUAL, precontext))
+
+ while (
+ len(bigpatch.diffs) != 0
+ and patch.length1 < patch_size - self.Patch_Margin
+ ):
+ (diff_type, diff_text) = bigpatch.diffs[0]
+ if diff_type == self.DIFF_INSERT:
+ # Insertions are harmless.
+ patch.length2 += len(diff_text)
+ start2 += len(diff_text)
+ patch.diffs.append(bigpatch.diffs.pop(0))
+ empty = False
+ elif (
+ diff_type == self.DIFF_DELETE
+ and len(patch.diffs) == 1
+ and patch.diffs[0][0] == self.DIFF_EQUAL
+ and len(diff_text) > 2 * patch_size
+ ):
+ # This is a large deletion. Let it pass in one chunk.
+ patch.length1 += len(diff_text)
+ start1 += len(diff_text)
+ empty = False
+ patch.diffs.append((diff_type, diff_text))
+ del bigpatch.diffs[0]
+ else:
+ # Deletion or equality. Only take as much as we can stomach.
+ diff_text = diff_text[
+ : patch_size - patch.length1 - self.Patch_Margin
+ ]
+ patch.length1 += len(diff_text)
+ start1 += len(diff_text)
+ if diff_type == self.DIFF_EQUAL:
+ patch.length2 += len(diff_text)
+ start2 += len(diff_text)
+ else:
+ empty = False
+
+ patch.diffs.append((diff_type, diff_text))
+ if diff_text == bigpatch.diffs[0][1]:
+ del bigpatch.diffs[0]
+ else:
+ bigpatch.diffs[0] = (
+ bigpatch.diffs[0][0],
+ bigpatch.diffs[0][1][len(diff_text) :],
+ )
+
+ # Compute the head context for the next patch.
+ precontext = self.diff_text2(patch.diffs)
+ precontext = precontext[-self.Patch_Margin :]
+ # Append the end context for this patch.
+ postcontext = self.diff_text1(bigpatch.diffs)[: self.Patch_Margin]
+ if postcontext:
+ patch.length1 += len(postcontext)
+ patch.length2 += len(postcontext)
+ if len(patch.diffs) != 0 and patch.diffs[-1][0] == self.DIFF_EQUAL:
+ patch.diffs[-1] = (
+ self.DIFF_EQUAL,
+ patch.diffs[-1][1] + postcontext,
+ )
+ else:
+ patch.diffs.append((self.DIFF_EQUAL, postcontext))
+
+ if not empty:
+ x += 1
+ patches.insert(x, patch)
+
+ def patch_toText(self, patches):
+ """Take a list of patches and return a textual representation.
+
+ Args:
+ patches: Array of Patch objects.
+
+ Returns:
+ Text representation of patches.
+ """
+ text = []
+ for patch in patches:
+ text.append(str(patch))
+ return "".join(text)
+
+ def patch_fromText(self, textline):
+ """Parse a textual representation of patches and return a list of patch
+ objects.
+
+ Args:
+ textline: Text representation of patches.
+
+ Returns:
+ Array of Patch objects.
+
+ Raises:
+ ValueError: If invalid input.
+ """
+ patches = []
+ if not textline:
+ return patches
+ text = textline.split("\n")
+ while len(text) != 0:
+ m = re.match(r"^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$", text[0])
+ if not m:
+ raise ValueError("Invalid patch string: " + text[0])
+ patch = patch_obj()
+ patches.append(patch)
+ patch.start1 = int(m.group(1))
+ if m.group(2) == "":
+ patch.start1 -= 1
+ patch.length1 = 1
+ elif m.group(2) == "0":
+ patch.length1 = 0
+ else:
+ patch.start1 -= 1
+ patch.length1 = int(m.group(2))
+
+ patch.start2 = int(m.group(3))
+ if m.group(4) == "":
+ patch.start2 -= 1
+ patch.length2 = 1
+ elif m.group(4) == "0":
+ patch.length2 = 0
+ else:
+ patch.start2 -= 1
+ patch.length2 = int(m.group(4))
+
+ del text[0]
+
+ while len(text) != 0:
+ if text[0]:
+ sign = text[0][0]
+ else:
+ sign = ""
+ line = urllib.parse.unquote(text[0][1:])
+ if sign == "+":
+ # Insertion.
+ patch.diffs.append((self.DIFF_INSERT, line))
+ elif sign == "-":
+ # Deletion.
+ patch.diffs.append((self.DIFF_DELETE, line))
+ elif sign == " ":
+ # Minor equality.
+ patch.diffs.append((self.DIFF_EQUAL, line))
+ elif sign == "@":
+ # Start of next patch.
+ break
+ elif sign == "":
+ # Blank line? Whatever.
+ pass
+ else:
+ # WTF?
+ raise ValueError("Invalid patch mode: '%s'\n%s" % (sign, line))
+ del text[0]
+ return patches
+
+
+class patch_obj:
+ """Class representing one patch operation."""
+
+ def __init__(self):
+ """Initializes with an empty list of diffs."""
+ self.diffs = []
+ self.start1 = None
+ self.start2 = None
+ self.length1 = 0
+ self.length2 = 0
+
+ def __str__(self):
+ """Emulate GNU diff's format.
+ Header: @@ -382,8 +481,9 @@
+ Indices are printed as 1-based, not 0-based.
+
+ Returns:
+ The GNU diff string.
+ """
+ if self.length1 == 0:
+ coords1 = str(self.start1) + ",0"
+ elif self.length1 == 1:
+ coords1 = str(self.start1 + 1)
+ else:
+ coords1 = str(self.start1 + 1) + "," + str(self.length1)
+ if self.length2 == 0:
+ coords2 = str(self.start2) + ",0"
+ elif self.length2 == 1:
+ coords2 = str(self.start2 + 1)
+ else:
+ coords2 = str(self.start2 + 1) + "," + str(self.length2)
+ text = ["@@ -", coords1, " +", coords2, " @@\n"]
+ # Escape the body of the patch with %xx notation.
+ for op, data in self.diffs:
+ if op == diff_match_patch.DIFF_INSERT:
+ text.append("+")
+ elif op == diff_match_patch.DIFF_DELETE:
+ text.append("-")
+ elif op == diff_match_patch.DIFF_EQUAL:
+ text.append(" ")
+ # High ascii will raise UnicodeDecodeError. Use Unicode instead.
+ data = data.encode("utf-8")
+ text.append(urllib.parse.quote(data, "!~*'();/?:@&=+$,# ") + "\n")
+ return "".join(text)
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest1.txt b/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest1.txt
new file mode 100644
index 0000000000..54b438fd79
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest1.txt
@@ -0,0 +1,230 @@
+This is a '''list of newspapers published by [[Journal Register Company]]'''.
+
+The company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]] and [[Pennsylvania]], organized in six geographic "clusters":<ref>[http://www.journalregister.com/newspapers.html Journal Register Company: Our Newspapers], accessed February 10, 2008.</ref>
+
+== Capital-Saratoga ==
+Three dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].
+
+* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]
+* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]
+* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]
+* Weeklies:
+** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]
+** ''Rome Observer'' of [[Rome, New York]]
+** ''Life & Times of Utica'' of [[Utica, New York]]
+
+== Connecticut ==
+Five dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].
+
+* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]
+* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]
+* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]
+
+* [[New Haven Register#Competitors|Elm City Newspapers]] {{WS|ctcentral.com}}
+** ''The Advertiser'' of [[East Haven, Connecticut|East Haven]]
+** ''Hamden Chronicle'' of [[Hamden, Connecticut|Hamden]]
+** ''Milford Weekly'' of [[Milford, Connecticut|Milford]]
+** ''The Orange Bulletin'' of [[Orange, Connecticut|Orange]]
+** ''The Post'' of [[North Haven, Connecticut|North Haven]]
+** ''Shelton Weekly'' of [[Shelton, Connecticut|Shelton]]
+** ''The Stratford Bard'' of [[Stratford, Connecticut|Stratford]]
+** ''Wallingford Voice'' of [[Wallingford, Connecticut|Wallingford]]
+** ''West Haven News'' of [[West Haven, Connecticut|West Haven]]
+* Housatonic Publications
+** ''The New Milford Times'' {{WS|newmilfordtimes.com}} of [[New Milford, Connecticut|New Milford]]
+** ''The Brookfield Journal'' of [[Brookfield, Connecticut|Brookfield]]
+** ''The Kent Good Times Dispatch'' of [[Kent, Connecticut|Kent]]
+** ''The Bethel Beacon'' of [[Bethel, Connecticut|Bethel]]
+** ''The Litchfield Enquirer'' of [[Litchfield, Connecticut|Litchfield]]
+** ''Litchfield County Times'' of [[Litchfield, Connecticut|Litchfield]]
+* Imprint Newspapers {{WS|imprintnewspapers.com}}
+** ''West Hartford News'' of [[West Hartford, Connecticut|West Hartford]]
+** ''Windsor Journal'' of [[Windsor, Connecticut|Windsor]]
+** ''Windsor Locks Journal'' of [[Windsor Locks, Connecticut|Windsor Locks]]
+** ''Avon Post'' of [[Avon, Connecticut|Avon]]
+** ''Farmington Post'' of [[Farmington, Connecticut|Farmington]]
+** ''Simsbury Post'' of [[Simsbury, Connecticut|Simsbury]]
+** ''Tri-Town Post'' of [[Burlington, Connecticut|Burlington]], [[Canton, Connecticut|Canton]] and [[Harwinton, Connecticut|Harwinton]]
+* Minuteman Publications
+** ''[[Fairfield Minuteman]]'' of [[Fairfield, Connecticut|Fairfield]]
+** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]
+* Shoreline Newspapers weeklies:
+** ''Branford Review'' of [[Branford, Connecticut|Branford]]
+** ''Clinton Recorder'' of [[Clinton, Connecticut|Clinton]]
+** ''The Dolphin'' of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]
+** ''Main Street News'' {{WS|ctmainstreetnews.com}} of [[Essex, Connecticut|Essex]]
+** ''Pictorial Gazette'' of [[Old Saybrook, Connecticut|Old Saybrook]]
+** ''Regional Express'' of [[Colchester, Connecticut|Colchester]]
+** ''Regional Standard'' of [[Colchester, Connecticut|Colchester]]
+** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]
+** ''Shore View East'' of [[Madison, Connecticut|Madison]]
+** ''Shore View West'' of [[Guilford, Connecticut|Guilford]]
+* Other weeklies:
+** ''Registro'' {{WS|registroct.com}} of [[New Haven, Connecticut|New Haven]]
+** ''Thomaston Express'' {{WS|thomastownexpress.com}} of [[Thomaston, Connecticut|Thomaston]]
+** ''Foothills Traders'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton
+
+== Michigan ==
+Four dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]
+* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]
+* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]
+* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]
+* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of [[Mount Pleasant, Michigan|Mount Pleasant]]
+* Heritage Newspapers {{WS|heritage.com}}
+** ''Belleville View''
+** ''Ile Camera''
+** ''Monroe Guardian''
+** ''Ypsilanti Courier''
+** ''News-Herald''
+** ''Press & Guide''
+** ''Chelsea Standard & Dexter Leader''
+** ''Manchester Enterprise''
+** ''Milan News-Leader''
+** ''Saline Reporter''
+* Independent Newspapers {{WS|sourcenewspapers.com}}
+** ''Advisor''
+** ''Source''
+* Morning Star {{WS|morningstarpublishing.com}}
+** ''Alma Reminder''
+** ''Alpena Star''
+** ''Antrim County News''
+** ''Carson City Reminder''
+** ''The Leader & Kalkaskian''
+** ''Ogemaw/Oscoda County Star''
+** ''Petoskey/Charlevoix Star''
+** ''Presque Isle Star''
+** ''Preview Community Weekly''
+** ''Roscommon County Star''
+** ''St. Johns Reminder''
+** ''Straits Area Star''
+** ''The (Edmore) Advertiser''
+* Voice Newspapers {{WS|voicenews.com}}
+** ''Armada Times''
+** ''Bay Voice''
+** ''Blue Water Voice''
+** ''Downriver Voice''
+** ''Macomb Township Voice''
+** ''North Macomb Voice''
+** ''Weekend Voice''
+** ''Suburban Lifestyles'' {{WS|suburbanlifestyles.com}}
+
+== Mid-Hudson ==
+One daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].
+
+* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]
+
+== Ohio ==
+Two dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].
+
+* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]
+* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]
+
+== Philadelphia area ==
+Seven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].
+
+* ''The Daily Local'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]
+* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos
+* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]
+* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania|Phoenixville]]
+* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]
+* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]
+* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]
+
+* Weeklies
+** ''El Latino Expreso'' of [[Trenton, New Jersey]]
+** ''La Voz'' of [[Norristown, Pennsylvania]]
+** ''The Village News'' of [[Downingtown, Pennsylvania]]
+** ''The Times Record'' of [[Kennett Square, Pennsylvania]]
+** ''The Tri-County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]
+** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}}of [[Havertown, Pennsylvania]]
+** ''Main Line Times'' {{WS|mainlinetimes.com}}of [[Ardmore, Pennsylvania]]
+** ''Penny Pincher'' of [[Pottstown, Pennsylvania]]
+** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]
+* Chesapeake Publishing {{WS|pa8newsgroup.com}}
+** ''Solanco Sun Ledger'' of [[Quarryville, Pennsylvania]]
+** ''Columbia Ledger'' of [[Columbia, Pennsylvania]]
+** ''Coatesville Ledger'' of [[Downingtown, Pennsylvania]]
+** ''Parkesburg Post Ledger'' of [[Quarryville, Pennsylvania]]
+** ''Downingtown Ledger'' of [[Downingtown, Pennsylvania]]
+** ''The Kennett Paper'' of [[Kennett Square, Pennsylvania]]
+** ''Avon Grove Sun'' of [[West Grove, Pennsylvania]]
+** ''Oxford Tribune'' of [[Oxford, Pennsylvania]]
+** ''Elizabethtown Chronicle'' of [[Elizabethtown, Pennsylvania]]
+** ''Donegal Ledger'' of [[Donegal, Pennsylvania]]
+** ''Chadds Ford Post'' of [[Chadds Ford, Pennsylvania]]
+** ''The Central Record'' of [[Medford, New Jersey]]
+** ''Maple Shade Progress'' of [[Maple Shade, New Jersey]]
+* Intercounty Newspapers {{WS|buckslocalnews.com}}
+** ''The Review'' of Roxborough, Pennsylvania
+** ''The Recorder'' of [[Conshohocken, Pennsylvania]]
+** ''The Leader'' of [[Mount Airy, Pennsylvania|Mount Airy]] and West Oak Lake, Pennsylvania
+** ''The Pennington Post'' of [[Pennington, New Jersey]]
+** ''The Bristol Pilot'' of [[Bristol, Pennsylvania]]
+** ''Yardley News'' of [[Yardley, Pennsylvania]]
+** ''New Hope Gazette'' of [[New Hope, Pennsylvania]]
+** ''Doylestown Patriot'' of [[Doylestown, Pennsylvania]]
+** ''Newtown Advance'' of [[Newtown, Pennsylvania]]
+** ''The Plain Dealer'' of [[Williamstown, New Jersey]]
+** ''News Report'' of [[Sewell, New Jersey]]
+** ''Record Breeze'' of [[Berlin, New Jersey]]
+** ''Newsweekly'' of [[Moorestown, New Jersey]]
+** ''Haddon Herald'' of [[Haddonfield, New Jersey]]
+** ''New Egypt Press'' of [[New Egypt, New Jersey]]
+** ''Community News'' of [[Pemberton, New Jersey]]
+** ''Plymouth Meeting Journal'' of [[Plymouth Meeting, Pennsylvania]]
+** ''Lafayette Hill Journal'' of [[Lafayette Hill, Pennsylvania]]
+* Montgomery Newspapers {{WS|montgomerynews.com}}
+** ''Ambler Gazette'' of [[Ambler, Pennsylvania]]
+** ''Central Bucks Life'' of [[Bucks County, Pennsylvania]]
+** ''The Colonial'' of [[Plymouth Meeting, Pennsylvania]]
+** ''Glenside News'' of [[Glenside, Pennsylvania]]
+** ''The Globe'' of [[Lower Moreland Township, Pennsylvania]]
+** ''Main Line Life'' of [[Ardmore, Pennsylvania]]
+** ''Montgomery Life'' of [[Fort Washington, Pennsylvania]]
+** ''North Penn Life'' of [[Lansdale, Pennsylvania]]
+** ''Perkasie News Herald'' of [[Perkasie, Pennsylvania]]
+** ''Public Spirit'' of [[Hatboro, Pennsylvania]]
+** ''Souderton Independent'' of [[Souderton, Pennsylvania]]
+** ''Springfield Sun'' of [[Springfield, Pennsylvania]]
+** ''Spring-Ford Reporter'' of [[Royersford, Pennsylvania]]
+** ''Times Chronicle'' of [[Jenkintown, Pennsylvania]]
+** ''Valley Item'' of [[Perkiomenville, Pennsylvania]]
+** ''Willow Grove Guide'' of [[Willow Grove, Pennsylvania]]
+* News Gleaner Publications (closed December 2008) {{WS|newsgleaner.com}}
+** ''Life Newspapers'' of [[Philadelphia, Pennsylvania]]
+* Suburban Publications
+** ''The Suburban & Wayne Times'' {{WS|waynesuburban.com}} of [[Wayne, Pennsylvania]]
+** ''The Suburban Advertiser'' of [[Exton, Pennsylvania]]
+** ''The King of Prussia Courier'' of [[King of Prussia, Pennsylvania]]
+* Press Newspapers {{WS|countypressonline.com}}
+** ''County Press'' of [[Newtown Square, Pennsylvania]]
+** ''Garnet Valley Press'' of [[Glen Mills, Pennsylvania]]
+** ''Haverford Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)
+** ''Hometown Press'' of [[Glen Mills, Pennsylvania]] (closed January 2009)
+** ''Media Press'' of [[Newtown Square, Pennsylvania]] (closed January 2009)
+** ''Springfield Press'' of [[Springfield, Pennsylvania]]
+* Berks-Mont Newspapers {{WS|berksmontnews.com}}
+** ''The Boyertown Area Times'' of [[Boyertown, Pennsylvania]]
+** ''The Kutztown Area Patriot'' of [[Kutztown, Pennsylvania]]
+** ''The Hamburg Area Item'' of [[Hamburg, Pennsylvania]]
+** ''The Southern Berks News'' of [[Exeter Township, Berks County, Pennsylvania]]
+** ''The Free Press'' of [[Quakertown, Pennsylvania]]
+** ''The Saucon News'' of [[Quakertown, Pennsylvania]]
+** ''Westside Weekly'' of [[Reading, Pennsylvania]]
+
+* Magazines
+** ''Bucks Co. Town & Country Living''
+** ''Chester Co. Town & Country Living''
+** ''Montomgery Co. Town & Country Living''
+** ''Garden State Town & Country Living''
+** ''Montgomery Homes''
+** ''Philadelphia Golfer''
+** ''Parents Express''
+** ''Art Matters''
+
+{{JRC}}
+
+==References==
+<references />
+
+[[Category:Journal Register publications|*]]
diff --git a/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest2.txt b/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest2.txt
new file mode 100644
index 0000000000..8f25a80fff
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/diff_match_patch/tests/speedtest2.txt
@@ -0,0 +1,188 @@
+This is a '''list of newspapers published by [[Journal Register Company]]'''.
+
+The company owns daily and weekly newspapers, other print media properties and newspaper-affiliated local Websites in the [[U.S.]] states of [[Connecticut]], [[Michigan]], [[New York]], [[Ohio]], [[Pennsylvania]] and [[New Jersey]], organized in six geographic "clusters":<ref>[http://www.journalregister.com/publications.html Journal Register Company: Our Publications], accessed April 21, 2010.</ref>
+
+== Capital-Saratoga ==
+Three dailies, associated weeklies and [[pennysaver]]s in greater [[Albany, New York]]; also [http://www.capitalcentral.com capitalcentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].
+
+* ''The Oneida Daily Dispatch'' {{WS|oneidadispatch.com}} of [[Oneida, New York]]
+* ''[[The Record (Troy)|The Record]]'' {{WS|troyrecord.com}} of [[Troy, New York]]
+* ''[[The Saratogian]]'' {{WS|saratogian.com}} of [[Saratoga Springs, New York]]
+* Weeklies:
+** ''Community News'' {{WS|cnweekly.com}} weekly of [[Clifton Park, New York]]
+** ''Rome Observer'' {{WS|romeobserver.com}} of [[Rome, New York]]
+** ''WG Life '' {{WS|saratogian.com/wglife/}} of [[Wilton, New York]]
+** ''Ballston Spa Life '' {{WS|saratogian.com/bspalife}} of [[Ballston Spa, New York]]
+** ''Greenbush Life'' {{WS|troyrecord.com/greenbush}} of [[Troy, New York]]
+** ''Latham Life'' {{WS|troyrecord.com/latham}} of [[Latham, New York]]
+** ''River Life'' {{WS|troyrecord.com/river}} of [[Troy, New York]]
+
+== Connecticut ==
+Three dailies, associated weeklies and [[pennysaver]]s in the state of [[Connecticut]]; also [http://www.ctcentral.com CTcentral.com], [http://www.ctcarsandtrucks.com CTCarsAndTrucks.com] and [http://www.jobsinct.com JobsInCT.com].
+
+* ''The Middletown Press'' {{WS|middletownpress.com}} of [[Middletown, Connecticut|Middletown]]
+* ''[[New Haven Register]]'' {{WS|newhavenregister.com}} of [[New Haven, Connecticut|New Haven]]
+* ''The Register Citizen'' {{WS|registercitizen.com}} of [[Torrington, Connecticut|Torrington]]
+
+* Housatonic Publications
+** ''The Housatonic Times'' {{WS|housatonictimes.com}} of [[New Milford, Connecticut|New Milford]]
+** ''Litchfield County Times'' {{WS|countytimes.com}} of [[Litchfield, Connecticut|Litchfield]]
+
+* Minuteman Publications
+** ''[[Fairfield Minuteman]]'' {{WS|fairfieldminuteman.com}}of [[Fairfield, Connecticut|Fairfield]]
+** ''The Westport Minuteman'' {{WS|westportminuteman.com}} of [[Westport, Connecticut|Westport]]
+
+* Shoreline Newspapers
+** ''The Dolphin'' {{WS|dolphin-news.com}} of [[Naval Submarine Base New London]] in [[New London, Connecticut|New London]]
+** ''Shoreline Times'' {{WS|shorelinetimes.com}} of [[Guilford, Connecticut|Guilford]]
+
+* Foothills Media Group {{WS|foothillsmediagroup.com}}
+** ''Thomaston Express'' {{WS|thomastonexpress.com}} of [[Thomaston, Connecticut|Thomaston]]
+** ''Good News About Torrington'' {{WS|goodnewsabouttorrington.com}} of [[Torrington, Connecticut|Torrington]]
+** ''Granby News'' {{WS|foothillsmediagroup.com/granby}} of [[Granby, Connecticut|Granby]]
+** ''Canton News'' {{WS|foothillsmediagroup.com/canton}} of [[Canton, Connecticut|Canton]]
+** ''Avon News'' {{WS|foothillsmediagroup.com/avon}} of [[Avon, Connecticut|Avon]]
+** ''Simsbury News'' {{WS|foothillsmediagroup.com/simsbury}} of [[Simsbury, Connecticut|Simsbury]]
+** ''Litchfield News'' {{WS|foothillsmediagroup.com/litchfield}} of [[Litchfield, Connecticut|Litchfield]]
+** ''Foothills Trader'' {{WS|foothillstrader.com}} of Torrington, Bristol, Canton
+
+* Other weeklies
+** ''The Milford-Orange Bulletin'' {{WS|ctbulletin.com}} of [[Orange, Connecticut|Orange]]
+** ''The Post-Chronicle'' {{WS|ctpostchronicle.com}} of [[North Haven, Connecticut|North Haven]]
+** ''West Hartford News'' {{WS|westhartfordnews.com}} of [[West Hartford, Connecticut|West Hartford]]
+
+* Magazines
+** ''The Connecticut Bride'' {{WS|connecticutmag.com}}
+** ''Connecticut Magazine'' {{WS|theconnecticutbride.com}}
+** ''Passport Magazine'' {{WS|passport-mag.com}}
+
+== Michigan ==
+Four dailies, associated weeklies and [[pennysaver]]s in the state of [[Michigan]]; also [http://www.micentralhomes.com MIcentralhomes.com] and [http://www.micentralautos.com MIcentralautos.com]
+* ''[[Oakland Press]]'' {{WS|theoaklandpress.com}} of [[Oakland, Michigan|Oakland]]
+* ''Daily Tribune'' {{WS|dailytribune.com}} of [[Royal Oak, Michigan|Royal Oak]]
+* ''Macomb Daily'' {{WS|macombdaily.com}} of [[Mt. Clemens, Michigan|Mt. Clemens]]
+* ''[[Morning Sun]]'' {{WS|themorningsun.com}} of [[Mount Pleasant, Michigan|Mount Pleasant]]
+
+* Heritage Newspapers {{WS|heritage.com}}
+** ''Belleville View'' {{WS|bellevilleview.com}}
+** ''Ile Camera'' {{WS|thenewsherald.com/ile_camera}}
+** ''Monroe Guardian'' {{WS|monreguardian.com}}
+** ''Ypsilanti Courier'' {{WS|ypsilanticourier.com}}
+** ''News-Herald'' {{WS|thenewsherald.com}}
+** ''Press & Guide'' {{WS|pressandguide.com}}
+** ''Chelsea Standard & Dexter Leader'' {{WS|chelseastandard.com}}
+** ''Manchester Enterprise'' {{WS|manchesterguardian.com}}
+** ''Milan News-Leader'' {{WS|milannews.com}}
+** ''Saline Reporter'' {{WS|salinereporter.com}}
+* Independent Newspapers
+** ''Advisor'' {{WS|sourcenewspapers.com}}
+** ''Source'' {{WS|sourcenewspapers.com}}
+* Morning Star {{WS|morningstarpublishing.com}}
+** ''The Leader & Kalkaskian'' {{WS|leaderandkalkaskian.com}}
+** ''Grand Traverse Insider'' {{WS|grandtraverseinsider.com}}
+** ''Alma Reminder''
+** ''Alpena Star''
+** ''Ogemaw/Oscoda County Star''
+** ''Presque Isle Star''
+** ''St. Johns Reminder''
+
+* Voice Newspapers {{WS|voicenews.com}}
+** ''Armada Times''
+** ''Bay Voice''
+** ''Blue Water Voice''
+** ''Downriver Voice''
+** ''Macomb Township Voice''
+** ''North Macomb Voice''
+** ''Weekend Voice''
+
+== Mid-Hudson ==
+One daily, associated magazines in the [[Hudson River Valley]] of [[New York]]; also [http://www.midhudsoncentral.com MidHudsonCentral.com] and [http://www.jobsinnewyork.com JobsInNewYork.com].
+
+* ''[[Daily Freeman]]'' {{WS|dailyfreeman.com}} of [[Kingston, New York]]
+* ''Las Noticias'' {{WS|lasnoticiasny.com}} of [[Kingston, New York]]
+
+== Ohio ==
+Two dailies, associated magazines and three shared Websites, all in the state of [[Ohio]]: [http://www.allaroundcleveland.com AllAroundCleveland.com], [http://www.allaroundclevelandcars.com AllAroundClevelandCars.com] and [http://www.allaroundclevelandjobs.com AllAroundClevelandJobs.com].
+
+* ''[[The News-Herald (Ohio)|The News-Herald]]'' {{WS|news-herald.com}} of [[Willoughby, Ohio|Willoughby]]
+* ''[[The Morning Journal]]'' {{WS|morningjournal.com}} of [[Lorain, Ohio|Lorain]]
+* ''El Latino Expreso'' {{WS|lorainlatino.com}} of [[Lorain, Ohio|Lorain]]
+
+== Philadelphia area ==
+Seven dailies and associated weeklies and magazines in [[Pennsylvania]] and [[New Jersey]], and associated Websites: [http://www.allaroundphilly.com AllAroundPhilly.com], [http://www.jobsinnj.com JobsInNJ.com], [http://www.jobsinpa.com JobsInPA.com], and [http://www.phillycarsearch.com PhillyCarSearch.com].
+
+* ''[[The Daily Local News]]'' {{WS|dailylocal.com}} of [[West Chester, Pennsylvania|West Chester]]
+* ''[[Delaware County Daily and Sunday Times]] {{WS|delcotimes.com}} of Primos [[Upper Darby Township, Pennsylvania]]
+* ''[[The Mercury (Pennsylvania)|The Mercury]]'' {{WS|pottstownmercury.com}} of [[Pottstown, Pennsylvania|Pottstown]]
+* ''[[The Reporter (Lansdale)|The Reporter]]'' {{WS|thereporteronline.com}} of [[Lansdale, Pennsylvania|Lansdale]]
+* ''The Times Herald'' {{WS|timesherald.com}} of [[Norristown, Pennsylvania|Norristown]]
+* ''[[The Trentonian]]'' {{WS|trentonian.com}} of [[Trenton, New Jersey]]
+
+* Weeklies
+* ''The Phoenix'' {{WS|phoenixvillenews.com}} of [[Phoenixville, Pennsylvania]]
+** ''El Latino Expreso'' {{WS|njexpreso.com}} of [[Trenton, New Jersey]]
+** ''La Voz'' {{WS|lavozpa.com}} of [[Norristown, Pennsylvania]]
+** ''The Tri County Record'' {{WS|tricountyrecord.com}} of [[Morgantown, Pennsylvania]]
+** ''Penny Pincher'' {{WS|pennypincherpa.com}}of [[Pottstown, Pennsylvania]]
+
+* Chesapeake Publishing {{WS|southernchestercountyweeklies.com}}
+** ''The Kennett Paper'' {{WS|kennettpaper.com}} of [[Kennett Square, Pennsylvania]]
+** ''Avon Grove Sun'' {{WS|avongrovesun.com}} of [[West Grove, Pennsylvania]]
+** ''The Central Record'' {{WS|medfordcentralrecord.com}} of [[Medford, New Jersey]]
+** ''Maple Shade Progress'' {{WS|mapleshadeprogress.com}} of [[Maple Shade, New Jersey]]
+
+* Intercounty Newspapers {{WS|buckslocalnews.com}} {{WS|southjerseylocalnews.com}}
+** ''The Pennington Post'' {{WS|penningtonpost.com}} of [[Pennington, New Jersey]]
+** ''The Bristol Pilot'' {{WS|bristolpilot.com}} of [[Bristol, Pennsylvania]]
+** ''Yardley News'' {{WS|yardleynews.com}} of [[Yardley, Pennsylvania]]
+** ''Advance of Bucks County'' {{WS|advanceofbucks.com}} of [[Newtown, Pennsylvania]]
+** ''Record Breeze'' {{WS|recordbreeze.com}} of [[Berlin, New Jersey]]
+** ''Community News'' {{WS|sjcommunitynews.com}} of [[Pemberton, New Jersey]]
+
+* Montgomery Newspapers {{WS|montgomerynews.com}}
+** ''Ambler Gazette'' {{WS|amblergazette.com}} of [[Ambler, Pennsylvania]]
+** ''The Colonial'' {{WS|colonialnews.com}} of [[Plymouth Meeting, Pennsylvania]]
+** ''Glenside News'' {{WS|glensidenews.com}} of [[Glenside, Pennsylvania]]
+** ''The Globe'' {{WS|globenewspaper.com}} of [[Lower Moreland Township, Pennsylvania]]
+** ''Montgomery Life'' {{WS|montgomerylife.com}} of [[Fort Washington, Pennsylvania]]
+** ''North Penn Life'' {{WS|northpennlife.com}} of [[Lansdale, Pennsylvania]]
+** ''Perkasie News Herald'' {{WS|perkasienewsherald.com}} of [[Perkasie, Pennsylvania]]
+** ''Public Spirit'' {{WS|thepublicspirit.com}} of [[Hatboro, Pennsylvania]]
+** ''Souderton Independent'' {{WS|soudertonindependent.com}} of [[Souderton, Pennsylvania]]
+** ''Springfield Sun'' {{WS|springfieldsun.com}} of [[Springfield, Pennsylvania]]
+** ''Spring-Ford Reporter'' {{WS|springfordreporter.com}} of [[Royersford, Pennsylvania]]
+** ''Times Chronicle'' {{WS|thetimeschronicle.com}} of [[Jenkintown, Pennsylvania]]
+** ''Valley Item'' {{WS|valleyitem.com}} of [[Perkiomenville, Pennsylvania]]
+** ''Willow Grove Guide'' {{WS|willowgroveguide.com}} of [[Willow Grove, Pennsylvania]]
+** ''The Review'' {{WS|roxreview.com}} of [[Roxborough, Philadelphia, Pennsylvania]]
+
+* Main Line Media News {{WS|mainlinemedianews.com}}
+** ''Main Line Times'' {{WS|mainlinetimes.com}} of [[Ardmore, Pennsylvania]]
+** ''Main Line Life'' {{WS|mainlinelife.com}} of [[Ardmore, Pennsylvania]]
+** ''The King of Prussia Courier'' {{WS|kingofprussiacourier.com}} of [[King of Prussia, Pennsylvania]]
+
+* Delaware County News Network {{WS|delconewsnetwork.com}}
+** ''News of Delaware County'' {{WS|newsofdelawarecounty.com}} of [[Havertown, Pennsylvania]]
+** ''County Press'' {{WS|countypressonline.com}} of [[Newtown Square, Pennsylvania]]
+** ''Garnet Valley Press'' {{WS|countypressonline.com}} of [[Glen Mills, Pennsylvania]]
+** ''Springfield Press'' {{WS|countypressonline.com}} of [[Springfield, Pennsylvania]]
+** ''Town Talk'' {{WS|towntalknews.com}} of [[Ridley, Pennsylvania]]
+
+* Berks-Mont Newspapers {{WS|berksmontnews.com}}
+** ''The Boyertown Area Times'' {{WS|berksmontnews.com/boyertown_area_times}} of [[Boyertown, Pennsylvania]]
+** ''The Kutztown Area Patriot'' {{WS|berksmontnews.com/kutztown_area_patriot}} of [[Kutztown, Pennsylvania]]
+** ''The Hamburg Area Item'' {{WS|berksmontnews.com/hamburg_area_item}} of [[Hamburg, Pennsylvania]]
+** ''The Southern Berks News'' {{WS|berksmontnews.com/southern_berks_news}} of [[Exeter Township, Berks County, Pennsylvania]]
+** ''Community Connection'' {{WS|berksmontnews.com/community_connection}} of [[Boyertown, Pennsylvania]]
+
+* Magazines
+** ''Bucks Co. Town & Country Living'' {{WS|buckscountymagazine.com}}
+** ''Parents Express'' {{WS|parents-express.com}}
+** ''Real Men, Rednecks'' {{WS|realmenredneck.com}}
+
+{{JRC}}
+
+==References==
+<references />
+
+[[Category:Journal Register publications|*]]
diff --git a/contrib/python/diff-match-patch/py3/ya.make b/contrib/python/diff-match-patch/py3/ya.make
new file mode 100644
index 0000000000..9a510f1683
--- /dev/null
+++ b/contrib/python/diff-match-patch/py3/ya.make
@@ -0,0 +1,28 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(20230430)
+
+LICENSE(Apache-2.0)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ diff_match_patch/__init__.py
+ diff_match_patch/__version__.py
+ diff_match_patch/diff_match_patch.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/diff-match-patch/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/diff-match-patch/ya.make b/contrib/python/diff-match-patch/ya.make
new file mode 100644
index 0000000000..ca419d06b4
--- /dev/null
+++ b/contrib/python/diff-match-patch/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/diff-match-patch/py2)
+ELSE()
+ PEERDIR(contrib/python/diff-match-patch/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/distro/py2/.dist-info/METADATA b/contrib/python/distro/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..bc42dfcf16
--- /dev/null
+++ b/contrib/python/distro/py2/.dist-info/METADATA
@@ -0,0 +1,169 @@
+Metadata-Version: 2.1
+Name: distro
+Version: 1.6.0
+Summary: Distro - an OS platform information API
+Home-page: https://github.com/python-distro/distro
+Author: Nir Cohen
+Author-email: nir36g@gmail.com
+License: Apache License, Version 2.0
+Platform: All
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: POSIX :: BSD
+Classifier: Operating System :: POSIX :: BSD :: FreeBSD
+Classifier: Operating System :: POSIX :: BSD :: NetBSD
+Classifier: Operating System :: POSIX :: BSD :: OpenBSD
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Operating System
+Description-Content-Type: text/markdown
+License-File: LICENSE
+
+Distro - an OS platform information API
+=======================================
+
+[![CI Status](https://github.com/python-distro/distro/workflows/CI/badge.svg)](https://github.com/python-distro/distro/actions/workflows/ci.yaml)
+[![PyPI version](http://img.shields.io/pypi/v/distro.svg)](https://pypi.python.org/pypi/distro)
+[![Supported Python Versions](https://img.shields.io/pypi/pyversions/distro.svg)](https://img.shields.io/pypi/pyversions/distro.svg)
+[![Code Coverage](https://codecov.io/github/python-distro/distro/coverage.svg?branch=master)](https://codecov.io/github/python-distro/distro?branch=master)
+[![Is Wheel](https://img.shields.io/pypi/wheel/distro.svg?style=flat)](https://pypi.python.org/pypi/distro)
+[![Latest Github Release](https://readthedocs.org/projects/distro/badge/?version=stable)](http://distro.readthedocs.io/en/latest/)
+[![Join the chat at https://gitter.im/python-distro/distro](https://badges.gitter.im/python-distro/distro.svg)](https://gitter.im/python-distro/distro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+`distro` provides information about the
+OS distribution it runs on, such as a reliable machine-readable ID, or
+version information.
+
+It is the recommended replacement for Python's original
+[`platform.linux_distribution`](https://docs.python.org/3.7/library/platform.html#platform.linux_distribution)
+function (removed in Python 3.8). It also provides much more functionality
+which isn't necessarily Python bound, like a command-line interface.
+
+Distro currently supports Linux and BSD based systems but [Windows and OS X support](https://github.com/python-distro/distro/issues/177) is also planned.
+
+For Python 2.6 support, see https://github.com/python-distro/distro/tree/python2.6-support
+
+## Installation
+
+Installation of the latest released version from PyPI:
+
+```shell
+pip install distro
+```
+
+Installation of the latest development version:
+
+```shell
+pip install https://github.com/python-distro/distro/archive/master.tar.gz
+```
+
+
+## Usage
+
+```bash
+$ distro
+Name: Antergos Linux
+Version: 2015.10 (ISO-Rolling)
+Codename: ISO-Rolling
+
+$ distro -j
+{
+ "codename": "ISO-Rolling",
+ "id": "antergos",
+ "like": "arch",
+ "version": "16.9",
+ "version_parts": {
+ "build_number": "",
+ "major": "16",
+ "minor": "9"
+ }
+}
+
+
+$ python
+>>> import distro
+>>> distro.linux_distribution(full_distribution_name=False)
+('centos', '7.1.1503', 'Core')
+```
+
+
+## Documentation
+
+On top of the aforementioned API, several more functions are available. For a complete description of the
+API, see the [latest API documentation](http://distro.readthedocs.org/en/latest/).
+
+## Background
+
+An alternative implementation became necessary because Python 3.5 deprecated
+this function, and Python 3.8 removed it altogether. Its predecessor function
+[`platform.dist`](https://docs.python.org/3.7/library/platform.html#platform.dist)
+was already deprecated since Python 2.6 and removed in Python 3.8. Still, there
+are many cases in which access to that information is needed. See [Python issue
+1322](https://bugs.python.org/issue1322) for more information.
+
+The `distro` package implements a robust and inclusive way of retrieving the
+information about a distribution based on new standards and old methods,
+namely from these data sources (from high to low precedence):
+
+* The os-release file `/etc/os-release` if present, with a fall-back on `/usr/lib/os-release` if needed.
+* The output of the `lsb_release` command, if available.
+* The distro release file (`/etc/*(-|_)(release|version)`), if present.
+* The `uname` command for BSD based distrubtions.
+
+
+## Python and Distribution Support
+
+`distro` is supported and tested on Python 2.7, 3.4+ and PyPy and on
+any distribution that provides one or more of the data sources
+covered.
+
+This package is tested with test data that mimics the exact behavior of the data sources of [a number of Linux distributions](https://github.com/python-distro/distro/tree/master/tests/resources/distros).
+
+
+## Testing
+
+```shell
+git clone git@github.com:python-distro/distro.git
+cd distro
+pip install tox
+tox
+```
+
+
+## Contributions
+
+Pull requests are always welcome to deal with specific distributions or just
+for general merriment.
+
+See [CONTRIBUTIONS](https://github.com/python-distro/distro/blob/master/CONTRIBUTING.md) for contribution info.
+
+Reference implementations for supporting additional distributions and file
+formats can be found here:
+
+* https://github.com/saltstack/salt/blob/develop/salt/grains/core.py#L1172
+* https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb
+* https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/system/distribution.py
+* https://github.com/puppetlabs/facter/blob/master/lib/src/facts/linux/os_linux.cc
+
+## Package manager distributions
+
+* https://src.fedoraproject.org/rpms/python-distro
+* https://www.archlinux.org/packages/community/any/python-distro/
+* https://launchpad.net/ubuntu/+source/python-distro
+* https://packages.debian.org/sid/python-distro
+* https://packages.gentoo.org/packages/dev-python/distro
+* https://pkgs.org/download/python2-distro
+* https://slackbuilds.org/repository/14.2/python/python-distro/
+
+
diff --git a/contrib/python/distro/py2/.dist-info/entry_points.txt b/contrib/python/distro/py2/.dist-info/entry_points.txt
new file mode 100644
index 0000000000..dd4023997f
--- /dev/null
+++ b/contrib/python/distro/py2/.dist-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+distro = distro:main
+
diff --git a/contrib/python/distro/py2/.dist-info/top_level.txt b/contrib/python/distro/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..0e0933171d
--- /dev/null
+++ b/contrib/python/distro/py2/.dist-info/top_level.txt
@@ -0,0 +1 @@
+distro
diff --git a/contrib/python/distro/py2/LICENSE b/contrib/python/distro/py2/LICENSE
new file mode 100644
index 0000000000..e06d208186
--- /dev/null
+++ b/contrib/python/distro/py2/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/contrib/python/distro/py2/README.md b/contrib/python/distro/py2/README.md
new file mode 100644
index 0000000000..76eaef244d
--- /dev/null
+++ b/contrib/python/distro/py2/README.md
@@ -0,0 +1,135 @@
+Distro - an OS platform information API
+=======================================
+
+[![CI Status](https://github.com/python-distro/distro/workflows/CI/badge.svg)](https://github.com/python-distro/distro/actions/workflows/ci.yaml)
+[![PyPI version](http://img.shields.io/pypi/v/distro.svg)](https://pypi.python.org/pypi/distro)
+[![Supported Python Versions](https://img.shields.io/pypi/pyversions/distro.svg)](https://img.shields.io/pypi/pyversions/distro.svg)
+[![Code Coverage](https://codecov.io/github/python-distro/distro/coverage.svg?branch=master)](https://codecov.io/github/python-distro/distro?branch=master)
+[![Is Wheel](https://img.shields.io/pypi/wheel/distro.svg?style=flat)](https://pypi.python.org/pypi/distro)
+[![Latest Github Release](https://readthedocs.org/projects/distro/badge/?version=stable)](http://distro.readthedocs.io/en/latest/)
+[![Join the chat at https://gitter.im/python-distro/distro](https://badges.gitter.im/python-distro/distro.svg)](https://gitter.im/python-distro/distro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+`distro` provides information about the
+OS distribution it runs on, such as a reliable machine-readable ID, or
+version information.
+
+It is the recommended replacement for Python's original
+[`platform.linux_distribution`](https://docs.python.org/3.7/library/platform.html#platform.linux_distribution)
+function (removed in Python 3.8). It also provides much more functionality
+which isn't necessarily Python bound, like a command-line interface.
+
+Distro currently supports Linux and BSD based systems but [Windows and OS X support](https://github.com/python-distro/distro/issues/177) is also planned.
+
+For Python 2.6 support, see https://github.com/python-distro/distro/tree/python2.6-support
+
+## Installation
+
+Installation of the latest released version from PyPI:
+
+```shell
+pip install distro
+```
+
+Installation of the latest development version:
+
+```shell
+pip install https://github.com/python-distro/distro/archive/master.tar.gz
+```
+
+
+## Usage
+
+```bash
+$ distro
+Name: Antergos Linux
+Version: 2015.10 (ISO-Rolling)
+Codename: ISO-Rolling
+
+$ distro -j
+{
+ "codename": "ISO-Rolling",
+ "id": "antergos",
+ "like": "arch",
+ "version": "16.9",
+ "version_parts": {
+ "build_number": "",
+ "major": "16",
+ "minor": "9"
+ }
+}
+
+
+$ python
+>>> import distro
+>>> distro.linux_distribution(full_distribution_name=False)
+('centos', '7.1.1503', 'Core')
+```
+
+
+## Documentation
+
+On top of the aforementioned API, several more functions are available. For a complete description of the
+API, see the [latest API documentation](http://distro.readthedocs.org/en/latest/).
+
+## Background
+
+An alternative implementation became necessary because Python 3.5 deprecated
+this function, and Python 3.8 removed it altogether. Its predecessor function
+[`platform.dist`](https://docs.python.org/3.7/library/platform.html#platform.dist)
+was already deprecated since Python 2.6 and removed in Python 3.8. Still, there
+are many cases in which access to that information is needed. See [Python issue
+1322](https://bugs.python.org/issue1322) for more information.
+
+The `distro` package implements a robust and inclusive way of retrieving the
+information about a distribution based on new standards and old methods,
+namely from these data sources (from high to low precedence):
+
+* The os-release file `/etc/os-release` if present, with a fall-back on `/usr/lib/os-release` if needed.
+* The output of the `lsb_release` command, if available.
+* The distro release file (`/etc/*(-|_)(release|version)`), if present.
+* The `uname` command for BSD based distrubtions.
+
+
+## Python and Distribution Support
+
+`distro` is supported and tested on Python 2.7, 3.4+ and PyPy and on
+any distribution that provides one or more of the data sources
+covered.
+
+This package is tested with test data that mimics the exact behavior of the data sources of [a number of Linux distributions](https://github.com/python-distro/distro/tree/master/tests/resources/distros).
+
+
+## Testing
+
+```shell
+git clone git@github.com:python-distro/distro.git
+cd distro
+pip install tox
+tox
+```
+
+
+## Contributions
+
+Pull requests are always welcome to deal with specific distributions or just
+for general merriment.
+
+See [CONTRIBUTIONS](https://github.com/python-distro/distro/blob/master/CONTRIBUTING.md) for contribution info.
+
+Reference implementations for supporting additional distributions and file
+formats can be found here:
+
+* https://github.com/saltstack/salt/blob/develop/salt/grains/core.py#L1172
+* https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb
+* https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/system/distribution.py
+* https://github.com/puppetlabs/facter/blob/master/lib/src/facts/linux/os_linux.cc
+
+## Package manager distributions
+
+* https://src.fedoraproject.org/rpms/python-distro
+* https://www.archlinux.org/packages/community/any/python-distro/
+* https://launchpad.net/ubuntu/+source/python-distro
+* https://packages.debian.org/sid/python-distro
+* https://packages.gentoo.org/packages/dev-python/distro
+* https://pkgs.org/download/python2-distro
+* https://slackbuilds.org/repository/14.2/python/python-distro/
diff --git a/contrib/python/distro/py2/distro.py b/contrib/python/distro/py2/distro.py
new file mode 100644
index 0000000000..7892741347
--- /dev/null
+++ b/contrib/python/distro/py2/distro.py
@@ -0,0 +1,1386 @@
+# Copyright 2015,2016,2017 Nir Cohen
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+The ``distro`` package (``distro`` stands for Linux Distribution) provides
+information about the Linux distribution it runs on, such as a reliable
+machine-readable distro ID, or version information.
+
+It is the recommended replacement for Python's original
+:py:func:`platform.linux_distribution` function, but it provides much more
+functionality. An alternative implementation became necessary because Python
+3.5 deprecated this function, and Python 3.8 removed it altogether. Its
+predecessor function :py:func:`platform.dist` was already deprecated since
+Python 2.6 and removed in Python 3.8. Still, there are many cases in which
+access to OS distribution information is needed. See `Python issue 1322
+<https://bugs.python.org/issue1322>`_ for more information.
+"""
+
+import argparse
+import json
+import logging
+import os
+import re
+import shlex
+import subprocess
+import sys
+import warnings
+
+__version__ = "1.6.0"
+
+# Use `if False` to avoid an ImportError on Python 2. After dropping Python 2
+# support, can use typing.TYPE_CHECKING instead. See:
+# https://docs.python.org/3/library/typing.html#typing.TYPE_CHECKING
+if False: # pragma: nocover
+ from typing import (
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ Optional,
+ Sequence,
+ TextIO,
+ Tuple,
+ Type,
+ TypedDict,
+ Union,
+ )
+
+ VersionDict = TypedDict(
+ "VersionDict", {"major": str, "minor": str, "build_number": str}
+ )
+ InfoDict = TypedDict(
+ "InfoDict",
+ {
+ "id": str,
+ "version": str,
+ "version_parts": VersionDict,
+ "like": str,
+ "codename": str,
+ },
+ )
+
+
+_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc")
+_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib")
+_OS_RELEASE_BASENAME = "os-release"
+
+#: Translation table for normalizing the "ID" attribute defined in os-release
+#: files, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as defined in the os-release file, translated to lower case,
+#: with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_OS_ID = {
+ "ol": "oracle", # Oracle Linux
+}
+
+#: Translation table for normalizing the "Distributor ID" attribute returned by
+#: the lsb_release command, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as returned by the lsb_release command, translated to lower
+#: case, with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_LSB_ID = {
+ "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4
+ "enterpriseenterpriseserver": "oracle", # Oracle Linux 5
+ "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation
+ "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server
+ "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode
+}
+
+#: Translation table for normalizing the distro ID derived from the file name
+#: of distro release files, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as derived from the file name of a distro release file,
+#: translated to lower case, with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_DISTRO_ID = {
+ "redhat": "rhel", # RHEL 6.x, 7.x
+}
+
+# Pattern for content of distro release file (reversed)
+_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile(
+ r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)"
+)
+
+# Pattern for base file name of distro release file
+_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$")
+
+# Base file names to be ignored when searching for distro release file
+_DISTRO_RELEASE_IGNORE_BASENAMES = (
+ "debian_version",
+ "lsb-release",
+ "oem-release",
+ _OS_RELEASE_BASENAME,
+ "system-release",
+ "plesk-release",
+ "iredmail-release",
+)
+
+
+def linux_distribution(full_distribution_name=True):
+ # type: (bool) -> Tuple[str, str, str]
+ """
+ .. deprecated:: 1.6.0
+
+ :func:`distro.linux_distribution()` is deprecated. It should only be
+ used as a compatibility shim with Python's
+ :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`,
+ :func:`distro.version` and :func:`distro.name` instead.
+
+ Return information about the current OS distribution as a tuple
+ ``(id_name, version, codename)`` with items as follows:
+
+ * ``id_name``: If *full_distribution_name* is false, the result of
+ :func:`distro.id`. Otherwise, the result of :func:`distro.name`.
+
+ * ``version``: The result of :func:`distro.version`.
+
+ * ``codename``: The result of :func:`distro.codename`.
+
+ The interface of this function is compatible with the original
+ :py:func:`platform.linux_distribution` function, supporting a subset of
+ its parameters.
+
+ The data it returns may not exactly be the same, because it uses more data
+ sources than the original function, and that may lead to different data if
+ the OS distribution is not consistent across multiple data sources it
+ provides (there are indeed such distributions ...).
+
+ Another reason for differences is the fact that the :func:`distro.id`
+ method normalizes the distro ID string to a reliable machine-readable value
+ for a number of popular OS distributions.
+ """
+ warnings.warn(
+ "distro.linux_distribution() is deprecated. It should only be used as a "
+ "compatibility shim with Python's platform.linux_distribution(). Please use "
+ "distro.id(), distro.version() and distro.name() instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return _distro.linux_distribution(full_distribution_name)
+
+
+def id():
+ # type: () -> str
+ """
+ Return the distro ID of the current distribution, as a
+ machine-readable string.
+
+ For a number of OS distributions, the returned distro ID value is
+ *reliable*, in the sense that it is documented and that it does not change
+ across releases of the distribution.
+
+ This package maintains the following reliable distro ID values:
+
+ ============== =========================================
+ Distro ID Distribution
+ ============== =========================================
+ "ubuntu" Ubuntu
+ "debian" Debian
+ "rhel" RedHat Enterprise Linux
+ "centos" CentOS
+ "fedora" Fedora
+ "sles" SUSE Linux Enterprise Server
+ "opensuse" openSUSE
+ "amazon" Amazon Linux
+ "arch" Arch Linux
+ "cloudlinux" CloudLinux OS
+ "exherbo" Exherbo Linux
+ "gentoo" GenToo Linux
+ "ibm_powerkvm" IBM PowerKVM
+ "kvmibm" KVM for IBM z Systems
+ "linuxmint" Linux Mint
+ "mageia" Mageia
+ "mandriva" Mandriva Linux
+ "parallels" Parallels
+ "pidora" Pidora
+ "raspbian" Raspbian
+ "oracle" Oracle Linux (and Oracle Enterprise Linux)
+ "scientific" Scientific Linux
+ "slackware" Slackware
+ "xenserver" XenServer
+ "openbsd" OpenBSD
+ "netbsd" NetBSD
+ "freebsd" FreeBSD
+ "midnightbsd" MidnightBSD
+ ============== =========================================
+
+ If you have a need to get distros for reliable IDs added into this set,
+ or if you find that the :func:`distro.id` function returns a different
+ distro ID for one of the listed distros, please create an issue in the
+ `distro issue tracker`_.
+
+ **Lookup hierarchy and transformations:**
+
+ First, the ID is obtained from the following sources, in the specified
+ order. The first available and non-empty value is used:
+
+ * the value of the "ID" attribute of the os-release file,
+
+ * the value of the "Distributor ID" attribute returned by the lsb_release
+ command,
+
+ * the first part of the file name of the distro release file,
+
+ The so determined ID value then passes the following transformations,
+ before it is returned by this method:
+
+ * it is translated to lower case,
+
+ * blanks (which should not be there anyway) are translated to underscores,
+
+ * a normalization of the ID is performed, based upon
+ `normalization tables`_. The purpose of this normalization is to ensure
+ that the ID is as reliable as possible, even across incompatible changes
+ in the OS distributions. A common reason for an incompatible change is
+ the addition of an os-release file, or the addition of the lsb_release
+ command, with ID values that differ from what was previously determined
+ from the distro release file name.
+ """
+ return _distro.id()
+
+
+def name(pretty=False):
+ # type: (bool) -> str
+ """
+ Return the name of the current OS distribution, as a human-readable
+ string.
+
+ If *pretty* is false, the name is returned without version or codename.
+ (e.g. "CentOS Linux")
+
+ If *pretty* is true, the version and codename are appended.
+ (e.g. "CentOS Linux 7.1.1503 (Core)")
+
+ **Lookup hierarchy:**
+
+ The name is obtained from the following sources, in the specified order.
+ The first available and non-empty value is used:
+
+ * If *pretty* is false:
+
+ - the value of the "NAME" attribute of the os-release file,
+
+ - the value of the "Distributor ID" attribute returned by the lsb_release
+ command,
+
+ - the value of the "<name>" field of the distro release file.
+
+ * If *pretty* is true:
+
+ - the value of the "PRETTY_NAME" attribute of the os-release file,
+
+ - the value of the "Description" attribute returned by the lsb_release
+ command,
+
+ - the value of the "<name>" field of the distro release file, appended
+ with the value of the pretty version ("<version_id>" and "<codename>"
+ fields) of the distro release file, if available.
+ """
+ return _distro.name(pretty)
+
+
+def version(pretty=False, best=False):
+ # type: (bool, bool) -> str
+ """
+ Return the version of the current OS distribution, as a human-readable
+ string.
+
+ If *pretty* is false, the version is returned without codename (e.g.
+ "7.0").
+
+ If *pretty* is true, the codename in parenthesis is appended, if the
+ codename is non-empty (e.g. "7.0 (Maipo)").
+
+ Some distributions provide version numbers with different precisions in
+ the different sources of distribution information. Examining the different
+ sources in a fixed priority order does not always yield the most precise
+ version (e.g. for Debian 8.2, or CentOS 7.1).
+
+ The *best* parameter can be used to control the approach for the returned
+ version:
+
+ If *best* is false, the first non-empty version number in priority order of
+ the examined sources is returned.
+
+ If *best* is true, the most precise version number out of all examined
+ sources is returned.
+
+ **Lookup hierarchy:**
+
+ In all cases, the version number is obtained from the following sources.
+ If *best* is false, this order represents the priority order:
+
+ * the value of the "VERSION_ID" attribute of the os-release file,
+ * the value of the "Release" attribute returned by the lsb_release
+ command,
+ * the version number parsed from the "<version_id>" field of the first line
+ of the distro release file,
+ * the version number parsed from the "PRETTY_NAME" attribute of the
+ os-release file, if it follows the format of the distro release files.
+ * the version number parsed from the "Description" attribute returned by
+ the lsb_release command, if it follows the format of the distro release
+ files.
+ """
+ return _distro.version(pretty, best)
+
+
+def version_parts(best=False):
+ # type: (bool) -> Tuple[str, str, str]
+ """
+ Return the version of the current OS distribution as a tuple
+ ``(major, minor, build_number)`` with items as follows:
+
+ * ``major``: The result of :func:`distro.major_version`.
+
+ * ``minor``: The result of :func:`distro.minor_version`.
+
+ * ``build_number``: The result of :func:`distro.build_number`.
+
+ For a description of the *best* parameter, see the :func:`distro.version`
+ method.
+ """
+ return _distro.version_parts(best)
+
+
+def major_version(best=False):
+ # type: (bool) -> str
+ """
+ Return the major version of the current OS distribution, as a string,
+ if provided.
+ Otherwise, the empty string is returned. The major version is the first
+ part of the dot-separated version string.
+
+ For a description of the *best* parameter, see the :func:`distro.version`
+ method.
+ """
+ return _distro.major_version(best)
+
+
+def minor_version(best=False):
+ # type: (bool) -> str
+ """
+ Return the minor version of the current OS distribution, as a string,
+ if provided.
+ Otherwise, the empty string is returned. The minor version is the second
+ part of the dot-separated version string.
+
+ For a description of the *best* parameter, see the :func:`distro.version`
+ method.
+ """
+ return _distro.minor_version(best)
+
+
+def build_number(best=False):
+ # type: (bool) -> str
+ """
+ Return the build number of the current OS distribution, as a string,
+ if provided.
+ Otherwise, the empty string is returned. The build number is the third part
+ of the dot-separated version string.
+
+ For a description of the *best* parameter, see the :func:`distro.version`
+ method.
+ """
+ return _distro.build_number(best)
+
+
+def like():
+ # type: () -> str
+ """
+ Return a space-separated list of distro IDs of distributions that are
+ closely related to the current OS distribution in regards to packaging
+ and programming interfaces, for example distributions the current
+ distribution is a derivative from.
+
+ **Lookup hierarchy:**
+
+ This information item is only provided by the os-release file.
+ For details, see the description of the "ID_LIKE" attribute in the
+ `os-release man page
+ <http://www.freedesktop.org/software/systemd/man/os-release.html>`_.
+ """
+ return _distro.like()
+
+
+def codename():
+ # type: () -> str
+ """
+ Return the codename for the release of the current OS distribution,
+ as a string.
+
+ If the distribution does not have a codename, an empty string is returned.
+
+ Note that the returned codename is not always really a codename. For
+ example, openSUSE returns "x86_64". This function does not handle such
+ cases in any special way and just returns the string it finds, if any.
+
+ **Lookup hierarchy:**
+
+ * the codename within the "VERSION" attribute of the os-release file, if
+ provided,
+
+ * the value of the "Codename" attribute returned by the lsb_release
+ command,
+
+ * the value of the "<codename>" field of the distro release file.
+ """
+ return _distro.codename()
+
+
+def info(pretty=False, best=False):
+ # type: (bool, bool) -> InfoDict
+ """
+ Return certain machine-readable information items about the current OS
+ distribution in a dictionary, as shown in the following example:
+
+ .. sourcecode:: python
+
+ {
+ 'id': 'rhel',
+ 'version': '7.0',
+ 'version_parts': {
+ 'major': '7',
+ 'minor': '0',
+ 'build_number': ''
+ },
+ 'like': 'fedora',
+ 'codename': 'Maipo'
+ }
+
+ The dictionary structure and keys are always the same, regardless of which
+ information items are available in the underlying data sources. The values
+ for the various keys are as follows:
+
+ * ``id``: The result of :func:`distro.id`.
+
+ * ``version``: The result of :func:`distro.version`.
+
+ * ``version_parts -> major``: The result of :func:`distro.major_version`.
+
+ * ``version_parts -> minor``: The result of :func:`distro.minor_version`.
+
+ * ``version_parts -> build_number``: The result of
+ :func:`distro.build_number`.
+
+ * ``like``: The result of :func:`distro.like`.
+
+ * ``codename``: The result of :func:`distro.codename`.
+
+ For a description of the *pretty* and *best* parameters, see the
+ :func:`distro.version` method.
+ """
+ return _distro.info(pretty, best)
+
+
+def os_release_info():
+ # type: () -> Dict[str, str]
+ """
+ Return a dictionary containing key-value pairs for the information items
+ from the os-release file data source of the current OS distribution.
+
+ See `os-release file`_ for details about these information items.
+ """
+ return _distro.os_release_info()
+
+
+def lsb_release_info():
+ # type: () -> Dict[str, str]
+ """
+ Return a dictionary containing key-value pairs for the information items
+ from the lsb_release command data source of the current OS distribution.
+
+ See `lsb_release command output`_ for details about these information
+ items.
+ """
+ return _distro.lsb_release_info()
+
+
+def distro_release_info():
+ # type: () -> Dict[str, str]
+ """
+ Return a dictionary containing key-value pairs for the information items
+ from the distro release file data source of the current OS distribution.
+
+ See `distro release file`_ for details about these information items.
+ """
+ return _distro.distro_release_info()
+
+
+def uname_info():
+ # type: () -> Dict[str, str]
+ """
+ Return a dictionary containing key-value pairs for the information items
+ from the distro release file data source of the current OS distribution.
+ """
+ return _distro.uname_info()
+
+
+def os_release_attr(attribute):
+ # type: (str) -> str
+ """
+ Return a single named information item from the os-release file data source
+ of the current OS distribution.
+
+ Parameters:
+
+ * ``attribute`` (string): Key of the information item.
+
+ Returns:
+
+ * (string): Value of the information item, if the item exists.
+ The empty string, if the item does not exist.
+
+ See `os-release file`_ for details about these information items.
+ """
+ return _distro.os_release_attr(attribute)
+
+
+def lsb_release_attr(attribute):
+ # type: (str) -> str
+ """
+ Return a single named information item from the lsb_release command output
+ data source of the current OS distribution.
+
+ Parameters:
+
+ * ``attribute`` (string): Key of the information item.
+
+ Returns:
+
+ * (string): Value of the information item, if the item exists.
+ The empty string, if the item does not exist.
+
+ See `lsb_release command output`_ for details about these information
+ items.
+ """
+ return _distro.lsb_release_attr(attribute)
+
+
+def distro_release_attr(attribute):
+ # type: (str) -> str
+ """
+ Return a single named information item from the distro release file
+ data source of the current OS distribution.
+
+ Parameters:
+
+ * ``attribute`` (string): Key of the information item.
+
+ Returns:
+
+ * (string): Value of the information item, if the item exists.
+ The empty string, if the item does not exist.
+
+ See `distro release file`_ for details about these information items.
+ """
+ return _distro.distro_release_attr(attribute)
+
+
+def uname_attr(attribute):
+ # type: (str) -> str
+ """
+ Return a single named information item from the distro release file
+ data source of the current OS distribution.
+
+ Parameters:
+
+ * ``attribute`` (string): Key of the information item.
+
+ Returns:
+
+ * (string): Value of the information item, if the item exists.
+ The empty string, if the item does not exist.
+ """
+ return _distro.uname_attr(attribute)
+
+
+try:
+ from functools import cached_property
+except ImportError:
+ # Python < 3.8
+ class cached_property(object): # type: ignore
+ """A version of @property which caches the value. On access, it calls the
+ underlying function and sets the value in `__dict__` so future accesses
+ will not re-call the property.
+ """
+
+ def __init__(self, f):
+ # type: (Callable[[Any], Any]) -> None
+ self._fname = f.__name__
+ self._f = f
+
+ def __get__(self, obj, owner):
+ # type: (Any, Type[Any]) -> Any
+ assert obj is not None, "call {} on an instance".format(self._fname)
+ ret = obj.__dict__[self._fname] = self._f(obj)
+ return ret
+
+
+class LinuxDistribution(object):
+ """
+ Provides information about a OS distribution.
+
+ This package creates a private module-global instance of this class with
+ default initialization arguments, that is used by the
+ `consolidated accessor functions`_ and `single source accessor functions`_.
+ By using default initialization arguments, that module-global instance
+ returns data about the current OS distribution (i.e. the distro this
+ package runs on).
+
+ Normally, it is not necessary to create additional instances of this class.
+ However, in situations where control is needed over the exact data sources
+ that are used, instances of this class can be created with a specific
+ distro release file, or a specific os-release file, or without invoking the
+ lsb_release command.
+ """
+
+ def __init__(
+ self,
+ include_lsb=True,
+ os_release_file="",
+ distro_release_file="",
+ include_uname=True,
+ root_dir=None,
+ ):
+ # type: (bool, str, str, bool, Optional[str]) -> None
+ """
+ The initialization method of this class gathers information from the
+ available data sources, and stores that in private instance attributes.
+ Subsequent access to the information items uses these private instance
+ attributes, so that the data sources are read only once.
+
+ Parameters:
+
+ * ``include_lsb`` (bool): Controls whether the
+ `lsb_release command output`_ is included as a data source.
+
+ If the lsb_release command is not available in the program execution
+ path, the data source for the lsb_release command will be empty.
+
+ * ``os_release_file`` (string): The path name of the
+ `os-release file`_ that is to be used as a data source.
+
+ An empty string (the default) will cause the default path name to
+ be used (see `os-release file`_ for details).
+
+ If the specified or defaulted os-release file does not exist, the
+ data source for the os-release file will be empty.
+
+ * ``distro_release_file`` (string): The path name of the
+ `distro release file`_ that is to be used as a data source.
+
+ An empty string (the default) will cause a default search algorithm
+ to be used (see `distro release file`_ for details).
+
+ If the specified distro release file does not exist, or if no default
+ distro release file can be found, the data source for the distro
+ release file will be empty.
+
+ * ``include_uname`` (bool): Controls whether uname command output is
+ included as a data source. If the uname command is not available in
+ the program execution path the data source for the uname command will
+ be empty.
+
+ * ``root_dir`` (string): The absolute path to the root directory to use
+ to find distro-related information files.
+
+ Public instance attributes:
+
+ * ``os_release_file`` (string): The path name of the
+ `os-release file`_ that is actually used as a data source. The
+ empty string if no distro release file is used as a data source.
+
+ * ``distro_release_file`` (string): The path name of the
+ `distro release file`_ that is actually used as a data source. The
+ empty string if no distro release file is used as a data source.
+
+ * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter.
+ This controls whether the lsb information will be loaded.
+
+ * ``include_uname`` (bool): The result of the ``include_uname``
+ parameter. This controls whether the uname information will
+ be loaded.
+
+ Raises:
+
+ * :py:exc:`IOError`: Some I/O issue with an os-release file or distro
+ release file.
+
+ * :py:exc:`subprocess.CalledProcessError`: The lsb_release command had
+ some issue (other than not being available in the program execution
+ path).
+
+ * :py:exc:`UnicodeError`: A data source has unexpected characters or
+ uses an unexpected encoding.
+ """
+ self.root_dir = root_dir
+ self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR
+ self.usr_lib_dir = (
+ os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR
+ )
+
+ if os_release_file:
+ self.os_release_file = os_release_file
+ else:
+ etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME)
+ usr_lib_os_release_file = os.path.join(
+ self.usr_lib_dir, _OS_RELEASE_BASENAME
+ )
+
+ # NOTE: The idea is to respect order **and** have it set
+ # at all times for API backwards compatibility.
+ if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile(
+ usr_lib_os_release_file
+ ):
+ self.os_release_file = etc_dir_os_release_file
+ else:
+ self.os_release_file = usr_lib_os_release_file
+
+ self.distro_release_file = distro_release_file or "" # updated later
+ self.include_lsb = include_lsb
+ self.include_uname = include_uname
+
+ def __repr__(self):
+ # type: () -> str
+ """Return repr of all info"""
+ return (
+ "LinuxDistribution("
+ "os_release_file={self.os_release_file!r}, "
+ "distro_release_file={self.distro_release_file!r}, "
+ "include_lsb={self.include_lsb!r}, "
+ "include_uname={self.include_uname!r}, "
+ "_os_release_info={self._os_release_info!r}, "
+ "_lsb_release_info={self._lsb_release_info!r}, "
+ "_distro_release_info={self._distro_release_info!r}, "
+ "_uname_info={self._uname_info!r})".format(self=self)
+ )
+
+ def linux_distribution(self, full_distribution_name=True):
+ # type: (bool) -> Tuple[str, str, str]
+ """
+ Return information about the OS distribution that is compatible
+ with Python's :func:`platform.linux_distribution`, supporting a subset
+ of its parameters.
+
+ For details, see :func:`distro.linux_distribution`.
+ """
+ return (
+ self.name() if full_distribution_name else self.id(),
+ self.version(),
+ self.codename(),
+ )
+
+ def id(self):
+ # type: () -> str
+ """Return the distro ID of the OS distribution, as a string.
+
+ For details, see :func:`distro.id`.
+ """
+
+ def normalize(distro_id, table):
+ # type: (str, Dict[str, str]) -> str
+ distro_id = distro_id.lower().replace(" ", "_")
+ return table.get(distro_id, distro_id)
+
+ distro_id = self.os_release_attr("id")
+ if distro_id:
+ return normalize(distro_id, NORMALIZED_OS_ID)
+
+ distro_id = self.lsb_release_attr("distributor_id")
+ if distro_id:
+ return normalize(distro_id, NORMALIZED_LSB_ID)
+
+ distro_id = self.distro_release_attr("id")
+ if distro_id:
+ return normalize(distro_id, NORMALIZED_DISTRO_ID)
+
+ distro_id = self.uname_attr("id")
+ if distro_id:
+ return normalize(distro_id, NORMALIZED_DISTRO_ID)
+
+ return ""
+
+ def name(self, pretty=False):
+ # type: (bool) -> str
+ """
+ Return the name of the OS distribution, as a string.
+
+ For details, see :func:`distro.name`.
+ """
+ name = (
+ self.os_release_attr("name")
+ or self.lsb_release_attr("distributor_id")
+ or self.distro_release_attr("name")
+ or self.uname_attr("name")
+ )
+ if pretty:
+ name = self.os_release_attr("pretty_name") or self.lsb_release_attr(
+ "description"
+ )
+ if not name:
+ name = self.distro_release_attr("name") or self.uname_attr("name")
+ version = self.version(pretty=True)
+ if version:
+ name = name + " " + version
+ return name or ""
+
+ def version(self, pretty=False, best=False):
+ # type: (bool, bool) -> str
+ """
+ Return the version of the OS distribution, as a string.
+
+ For details, see :func:`distro.version`.
+ """
+ versions = [
+ self.os_release_attr("version_id"),
+ self.lsb_release_attr("release"),
+ self.distro_release_attr("version_id"),
+ self._parse_distro_release_content(self.os_release_attr("pretty_name")).get(
+ "version_id", ""
+ ),
+ self._parse_distro_release_content(
+ self.lsb_release_attr("description")
+ ).get("version_id", ""),
+ self.uname_attr("release"),
+ ]
+ version = ""
+ if best:
+ # This algorithm uses the last version in priority order that has
+ # the best precision. If the versions are not in conflict, that
+ # does not matter; otherwise, using the last one instead of the
+ # first one might be considered a surprise.
+ for v in versions:
+ if v.count(".") > version.count(".") or version == "":
+ version = v
+ else:
+ for v in versions:
+ if v != "":
+ version = v
+ break
+ if pretty and version and self.codename():
+ version = "{0} ({1})".format(version, self.codename())
+ return version
+
+ def version_parts(self, best=False):
+ # type: (bool) -> Tuple[str, str, str]
+ """
+ Return the version of the OS distribution, as a tuple of version
+ numbers.
+
+ For details, see :func:`distro.version_parts`.
+ """
+ version_str = self.version(best=best)
+ if version_str:
+ version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?")
+ matches = version_regex.match(version_str)
+ if matches:
+ major, minor, build_number = matches.groups()
+ return major, minor or "", build_number or ""
+ return "", "", ""
+
+ def major_version(self, best=False):
+ # type: (bool) -> str
+ """
+ Return the major version number of the current distribution.
+
+ For details, see :func:`distro.major_version`.
+ """
+ return self.version_parts(best)[0]
+
+ def minor_version(self, best=False):
+ # type: (bool) -> str
+ """
+ Return the minor version number of the current distribution.
+
+ For details, see :func:`distro.minor_version`.
+ """
+ return self.version_parts(best)[1]
+
+ def build_number(self, best=False):
+ # type: (bool) -> str
+ """
+ Return the build number of the current distribution.
+
+ For details, see :func:`distro.build_number`.
+ """
+ return self.version_parts(best)[2]
+
+ def like(self):
+ # type: () -> str
+ """
+ Return the IDs of distributions that are like the OS distribution.
+
+ For details, see :func:`distro.like`.
+ """
+ return self.os_release_attr("id_like") or ""
+
+ def codename(self):
+ # type: () -> str
+ """
+ Return the codename of the OS distribution.
+
+ For details, see :func:`distro.codename`.
+ """
+ try:
+ # Handle os_release specially since distros might purposefully set
+ # this to empty string to have no codename
+ return self._os_release_info["codename"]
+ except KeyError:
+ return (
+ self.lsb_release_attr("codename")
+ or self.distro_release_attr("codename")
+ or ""
+ )
+
+ def info(self, pretty=False, best=False):
+ # type: (bool, bool) -> InfoDict
+ """
+ Return certain machine-readable information about the OS
+ distribution.
+
+ For details, see :func:`distro.info`.
+ """
+ return dict(
+ id=self.id(),
+ version=self.version(pretty, best),
+ version_parts=dict(
+ major=self.major_version(best),
+ minor=self.minor_version(best),
+ build_number=self.build_number(best),
+ ),
+ like=self.like(),
+ codename=self.codename(),
+ )
+
+ def os_release_info(self):
+ # type: () -> Dict[str, str]
+ """
+ Return a dictionary containing key-value pairs for the information
+ items from the os-release file data source of the OS distribution.
+
+ For details, see :func:`distro.os_release_info`.
+ """
+ return self._os_release_info
+
+ def lsb_release_info(self):
+ # type: () -> Dict[str, str]
+ """
+ Return a dictionary containing key-value pairs for the information
+ items from the lsb_release command data source of the OS
+ distribution.
+
+ For details, see :func:`distro.lsb_release_info`.
+ """
+ return self._lsb_release_info
+
+ def distro_release_info(self):
+ # type: () -> Dict[str, str]
+ """
+ Return a dictionary containing key-value pairs for the information
+ items from the distro release file data source of the OS
+ distribution.
+
+ For details, see :func:`distro.distro_release_info`.
+ """
+ return self._distro_release_info
+
+ def uname_info(self):
+ # type: () -> Dict[str, str]
+ """
+ Return a dictionary containing key-value pairs for the information
+ items from the uname command data source of the OS distribution.
+
+ For details, see :func:`distro.uname_info`.
+ """
+ return self._uname_info
+
+ def os_release_attr(self, attribute):
+ # type: (str) -> str
+ """
+ Return a single named information item from the os-release file data
+ source of the OS distribution.
+
+ For details, see :func:`distro.os_release_attr`.
+ """
+ return self._os_release_info.get(attribute, "")
+
+ def lsb_release_attr(self, attribute):
+ # type: (str) -> str
+ """
+ Return a single named information item from the lsb_release command
+ output data source of the OS distribution.
+
+ For details, see :func:`distro.lsb_release_attr`.
+ """
+ return self._lsb_release_info.get(attribute, "")
+
+ def distro_release_attr(self, attribute):
+ # type: (str) -> str
+ """
+ Return a single named information item from the distro release file
+ data source of the OS distribution.
+
+ For details, see :func:`distro.distro_release_attr`.
+ """
+ return self._distro_release_info.get(attribute, "")
+
+ def uname_attr(self, attribute):
+ # type: (str) -> str
+ """
+ Return a single named information item from the uname command
+ output data source of the OS distribution.
+
+ For details, see :func:`distro.uname_attr`.
+ """
+ return self._uname_info.get(attribute, "")
+
+ @cached_property
+ def _os_release_info(self):
+ # type: () -> Dict[str, str]
+ """
+ Get the information items from the specified os-release file.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ if os.path.isfile(self.os_release_file):
+ with open(self.os_release_file) as release_file:
+ return self._parse_os_release_content(release_file)
+ return {}
+
+ @staticmethod
+ def _parse_os_release_content(lines):
+ # type: (TextIO) -> Dict[str, str]
+ """
+ Parse the lines of an os-release file.
+
+ Parameters:
+
+ * lines: Iterable through the lines in the os-release file.
+ Each line must be a unicode string or a UTF-8 encoded byte
+ string.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ props = {}
+ lexer = shlex.shlex(lines, posix=True)
+ lexer.whitespace_split = True
+
+ # The shlex module defines its `wordchars` variable using literals,
+ # making it dependent on the encoding of the Python source file.
+ # In Python 2.6 and 2.7, the shlex source file is encoded in
+ # 'iso-8859-1', and the `wordchars` variable is defined as a byte
+ # string. This causes a UnicodeDecodeError to be raised when the
+ # parsed content is a unicode object. The following fix resolves that
+ # (... but it should be fixed in shlex...):
+ if sys.version_info[0] == 2 and isinstance(lexer.wordchars, bytes):
+ lexer.wordchars = lexer.wordchars.decode("iso-8859-1")
+
+ tokens = list(lexer)
+ for token in tokens:
+ # At this point, all shell-like parsing has been done (i.e.
+ # comments processed, quotes and backslash escape sequences
+ # processed, multi-line values assembled, trailing newlines
+ # stripped, etc.), so the tokens are now either:
+ # * variable assignments: var=value
+ # * commands or their arguments (not allowed in os-release)
+ if "=" in token:
+ k, v = token.split("=", 1)
+ props[k.lower()] = v
+ else:
+ # Ignore any tokens that are not variable assignments
+ pass
+
+ if "version_codename" in props:
+ # os-release added a version_codename field. Use that in
+ # preference to anything else Note that some distros purposefully
+ # do not have code names. They should be setting
+ # version_codename=""
+ props["codename"] = props["version_codename"]
+ elif "ubuntu_codename" in props:
+ # Same as above but a non-standard field name used on older Ubuntus
+ props["codename"] = props["ubuntu_codename"]
+ elif "version" in props:
+ # If there is no version_codename, parse it from the version
+ match = re.search(r"(\(\D+\))|,(\s+)?\D+", props["version"])
+ if match:
+ codename = match.group()
+ codename = codename.strip("()")
+ codename = codename.strip(",")
+ codename = codename.strip()
+ # codename appears within paranthese.
+ props["codename"] = codename
+
+ return props
+
+ @cached_property
+ def _lsb_release_info(self):
+ # type: () -> Dict[str, str]
+ """
+ Get the information items from the lsb_release command output.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ if not self.include_lsb:
+ return {}
+ with open(os.devnull, "wb") as devnull:
+ try:
+ cmd = ("lsb_release", "-a")
+ stdout = subprocess.check_output(cmd, stderr=devnull)
+ # Command not found or lsb_release returned error
+ except (OSError, subprocess.CalledProcessError):
+ return {}
+ content = self._to_str(stdout).splitlines()
+ return self._parse_lsb_release_content(content)
+
+ @staticmethod
+ def _parse_lsb_release_content(lines):
+ # type: (Iterable[str]) -> Dict[str, str]
+ """
+ Parse the output of the lsb_release command.
+
+ Parameters:
+
+ * lines: Iterable through the lines of the lsb_release output.
+ Each line must be a unicode string or a UTF-8 encoded byte
+ string.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ props = {}
+ for line in lines:
+ kv = line.strip("\n").split(":", 1)
+ if len(kv) != 2:
+ # Ignore lines without colon.
+ continue
+ k, v = kv
+ props.update({k.replace(" ", "_").lower(): v.strip()})
+ return props
+
+ @cached_property
+ def _uname_info(self):
+ # type: () -> Dict[str, str]
+ with open(os.devnull, "wb") as devnull:
+ try:
+ cmd = ("uname", "-rs")
+ stdout = subprocess.check_output(cmd, stderr=devnull)
+ except OSError:
+ return {}
+ content = self._to_str(stdout).splitlines()
+ return self._parse_uname_content(content)
+
+ @staticmethod
+ def _parse_uname_content(lines):
+ # type: (Sequence[str]) -> Dict[str, str]
+ props = {}
+ match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
+ if match:
+ name, version = match.groups()
+
+ # This is to prevent the Linux kernel version from
+ # appearing as the 'best' version on otherwise
+ # identifiable distributions.
+ if name == "Linux":
+ return {}
+ props["id"] = name.lower()
+ props["name"] = name
+ props["release"] = version
+ return props
+
+ @staticmethod
+ def _to_str(text):
+ # type: (Union[bytes, str]) -> str
+ encoding = sys.getfilesystemencoding()
+ encoding = "utf-8" if encoding == "ascii" else encoding
+
+ if sys.version_info[0] >= 3:
+ if isinstance(text, bytes):
+ return text.decode(encoding)
+ else:
+ if isinstance(text, unicode): # noqa
+ return text.encode(encoding)
+
+ return text
+
+ @cached_property
+ def _distro_release_info(self):
+ # type: () -> Dict[str, str]
+ """
+ Get the information items from the specified distro release file.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ if self.distro_release_file:
+ # If it was specified, we use it and parse what we can, even if
+ # its file name or content does not match the expected pattern.
+ distro_info = self._parse_distro_release_file(self.distro_release_file)
+ basename = os.path.basename(self.distro_release_file)
+ # The file name pattern for user-specified distro release files
+ # is somewhat more tolerant (compared to when searching for the
+ # file), because we want to use what was specified as best as
+ # possible.
+ match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
+ if "name" in distro_info and "cloudlinux" in distro_info["name"].lower():
+ distro_info["id"] = "cloudlinux"
+ elif match:
+ distro_info["id"] = match.group(1)
+ return distro_info
+ else:
+ try:
+ basenames = os.listdir(self.etc_dir)
+ # We sort for repeatability in cases where there are multiple
+ # distro specific files; e.g. CentOS, Oracle, Enterprise all
+ # containing `redhat-release` on top of their own.
+ basenames.sort()
+ except OSError:
+ # This may occur when /etc is not readable but we can't be
+ # sure about the *-release files. Check common entries of
+ # /etc for information. If they turn out to not be there the
+ # error is handled in `_parse_distro_release_file()`.
+ basenames = [
+ "SuSE-release",
+ "arch-release",
+ "base-release",
+ "centos-release",
+ "fedora-release",
+ "gentoo-release",
+ "mageia-release",
+ "mandrake-release",
+ "mandriva-release",
+ "mandrivalinux-release",
+ "manjaro-release",
+ "oracle-release",
+ "redhat-release",
+ "sl-release",
+ "slackware-version",
+ ]
+ for basename in basenames:
+ if basename in _DISTRO_RELEASE_IGNORE_BASENAMES:
+ continue
+ match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
+ if match:
+ filepath = os.path.join(self.etc_dir, basename)
+ distro_info = self._parse_distro_release_file(filepath)
+ if "name" in distro_info:
+ # The name is always present if the pattern matches
+ self.distro_release_file = filepath
+ distro_info["id"] = match.group(1)
+ if "cloudlinux" in distro_info["name"].lower():
+ distro_info["id"] = "cloudlinux"
+ return distro_info
+ return {}
+
+ def _parse_distro_release_file(self, filepath):
+ # type: (str) -> Dict[str, str]
+ """
+ Parse a distro release file.
+
+ Parameters:
+
+ * filepath: Path name of the distro release file.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ try:
+ with open(filepath) as fp:
+ # Only parse the first line. For instance, on SLES there
+ # are multiple lines. We don't want them...
+ return self._parse_distro_release_content(fp.readline())
+ except (OSError, IOError):
+ # Ignore not being able to read a specific, seemingly version
+ # related file.
+ # See https://github.com/python-distro/distro/issues/162
+ return {}
+
+ @staticmethod
+ def _parse_distro_release_content(line):
+ # type: (str) -> Dict[str, str]
+ """
+ Parse a line from a distro release file.
+
+ Parameters:
+ * line: Line from the distro release file. Must be a unicode string
+ or a UTF-8 encoded byte string.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1])
+ distro_info = {}
+ if matches:
+ # regexp ensures non-None
+ distro_info["name"] = matches.group(3)[::-1]
+ if matches.group(2):
+ distro_info["version_id"] = matches.group(2)[::-1]
+ if matches.group(1):
+ distro_info["codename"] = matches.group(1)[::-1]
+ elif line:
+ distro_info["name"] = line.strip()
+ return distro_info
+
+
+_distro = LinuxDistribution()
+
+
+def main():
+ # type: () -> None
+ logger = logging.getLogger(__name__)
+ logger.setLevel(logging.DEBUG)
+ logger.addHandler(logging.StreamHandler(sys.stdout))
+
+ parser = argparse.ArgumentParser(description="OS distro info tool")
+ parser.add_argument(
+ "--json", "-j", help="Output in machine readable format", action="store_true"
+ )
+
+ parser.add_argument(
+ "--root-dir",
+ "-r",
+ type=str,
+ dest="root_dir",
+ help="Path to the root filesystem directory (defaults to /)",
+ )
+
+ args = parser.parse_args()
+
+ if args.root_dir:
+ dist = LinuxDistribution(
+ include_lsb=False, include_uname=False, root_dir=args.root_dir
+ )
+ else:
+ dist = _distro
+
+ if args.json:
+ logger.info(json.dumps(dist.info(), indent=4, sort_keys=True))
+ else:
+ logger.info("Name: %s", dist.name(pretty=True))
+ distribution_version = dist.version(pretty=True)
+ logger.info("Version: %s", distribution_version)
+ distribution_codename = dist.codename()
+ logger.info("Codename: %s", distribution_codename)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/python/distro/py2/ya.make b/contrib/python/distro/py2/ya.make
new file mode 100644
index 0000000000..e271753611
--- /dev/null
+++ b/contrib/python/distro/py2/ya.make
@@ -0,0 +1,31 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(1.6.0)
+
+LICENSE(Apache-2.0)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ distro.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/distro/py2/
+ .dist-info/METADATA
+ .dist-info/entry_points.txt
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE(
+ bin
+)
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/distro/py3/.dist-info/METADATA b/contrib/python/distro/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..4ea28c4980
--- /dev/null
+++ b/contrib/python/distro/py3/.dist-info/METADATA
@@ -0,0 +1,183 @@
+Metadata-Version: 2.1
+Name: distro
+Version: 1.8.0
+Summary: Distro - an OS platform information API
+Home-page: https://github.com/python-distro/distro
+Author: Nir Cohen
+Author-email: nir36g@gmail.com
+License: Apache License, Version 2.0
+Platform: All
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: POSIX :: BSD
+Classifier: Operating System :: POSIX :: BSD :: FreeBSD
+Classifier: Operating System :: POSIX :: BSD :: NetBSD
+Classifier: Operating System :: POSIX :: BSD :: OpenBSD
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Operating System
+Requires-Python: >=3.6
+Description-Content-Type: text/markdown
+License-File: LICENSE
+
+Distro - an OS platform information API
+=======================================
+
+[![CI Status](https://github.com/python-distro/distro/workflows/CI/badge.svg)](https://github.com/python-distro/distro/actions/workflows/ci.yaml)
+[![PyPI version](http://img.shields.io/pypi/v/distro.svg)](https://pypi.python.org/pypi/distro)
+[![Supported Python Versions](https://img.shields.io/pypi/pyversions/distro.svg)](https://img.shields.io/pypi/pyversions/distro.svg)
+[![Code Coverage](https://codecov.io/github/python-distro/distro/coverage.svg?branch=master)](https://codecov.io/github/python-distro/distro?branch=master)
+[![Is Wheel](https://img.shields.io/pypi/wheel/distro.svg?style=flat)](https://pypi.python.org/pypi/distro)
+[![Latest Github Release](https://readthedocs.org/projects/distro/badge/?version=stable)](http://distro.readthedocs.io/en/latest/)
+[![Join the chat at https://gitter.im/python-distro/distro](https://badges.gitter.im/python-distro/distro.svg)](https://gitter.im/python-distro/distro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+`distro` provides information about the
+OS distribution it runs on, such as a reliable machine-readable ID, or
+version information.
+
+It is the recommended replacement for Python's original
+[`platform.linux_distribution`](https://docs.python.org/3.7/library/platform.html#platform.linux_distribution)
+function (removed in Python 3.8). It also provides much more functionality
+which isn't necessarily Python bound, like a command-line interface.
+
+Distro currently supports Linux and BSD based systems but [Windows and OS X support](https://github.com/python-distro/distro/issues/177) is also planned.
+
+For Python 2.6 support, see https://github.com/python-distro/distro/tree/python2.6-support
+
+## Installation
+
+Installation of the latest released version from PyPI:
+
+```shell
+pip install distro
+```
+
+Installation of the latest development version:
+
+```shell
+pip install https://github.com/python-distro/distro/archive/master.tar.gz
+```
+
+To use as a standalone script, download `distro.py` directly:
+
+```shell
+curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py
+python distro.py
+```
+
+``distro`` is safe to vendor within projects that do not wish to add
+dependencies.
+
+```shell
+cd myproject
+curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py
+```
+
+## Usage
+
+```bash
+$ distro
+Name: Antergos Linux
+Version: 2015.10 (ISO-Rolling)
+Codename: ISO-Rolling
+
+$ distro -j
+{
+ "codename": "ISO-Rolling",
+ "id": "antergos",
+ "like": "arch",
+ "version": "16.9",
+ "version_parts": {
+ "build_number": "",
+ "major": "16",
+ "minor": "9"
+ }
+}
+
+
+$ python
+>>> import distro
+>>> distro.name(pretty=True)
+'CentOS Linux 8'
+>>> distro.id()
+'centos'
+>>> distro.version(best=True)
+'8.4.2105'
+```
+
+
+## Documentation
+
+On top of the aforementioned API, several more functions are available. For a complete description of the
+API, see the [latest API documentation](http://distro.readthedocs.org/en/latest/).
+
+## Background
+
+An alternative implementation became necessary because Python 3.5 deprecated
+this function, and Python 3.8 removed it altogether. Its predecessor function
+[`platform.dist`](https://docs.python.org/3.7/library/platform.html#platform.dist)
+was already deprecated since Python 2.6 and removed in Python 3.8. Still, there
+are many cases in which access to that information is needed. See [Python issue
+1322](https://bugs.python.org/issue1322) for more information.
+
+The `distro` package implements a robust and inclusive way of retrieving the
+information about a distribution based on new standards and old methods,
+namely from these data sources (from high to low precedence):
+
+* The os-release file `/etc/os-release` if present, with a fall-back on `/usr/lib/os-release` if needed.
+* The output of the `lsb_release` command, if available.
+* The distro release file (`/etc/*(-|_)(release|version)`), if present.
+* The `uname` command for BSD based distrubtions.
+
+
+## Python and Distribution Support
+
+`distro` is supported and tested on Python 3.6+ and PyPy and on any
+distribution that provides one or more of the data sources covered.
+
+This package is tested with test data that mimics the exact behavior of the data sources of [a number of Linux distributions](https://github.com/python-distro/distro/tree/master/tests/resources/distros).
+
+
+## Testing
+
+```shell
+git clone git@github.com:python-distro/distro.git
+cd distro
+pip install tox
+tox
+```
+
+
+## Contributions
+
+Pull requests are always welcome to deal with specific distributions or just
+for general merriment.
+
+See [CONTRIBUTIONS](https://github.com/python-distro/distro/blob/master/CONTRIBUTING.md) for contribution info.
+
+Reference implementations for supporting additional distributions and file
+formats can be found here:
+
+* https://github.com/saltstack/salt/blob/develop/salt/grains/core.py#L1172
+* https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb
+* https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/system/distribution.py
+* https://github.com/puppetlabs/facter/blob/master/lib/src/facts/linux/os_linux.cc
+
+## Package manager distributions
+
+* https://src.fedoraproject.org/rpms/python-distro
+* https://www.archlinux.org/packages/community/any/python-distro/
+* https://launchpad.net/ubuntu/+source/python-distro
+* https://packages.debian.org/stable/python3-distro
+* https://packages.gentoo.org/packages/dev-python/distro
+* https://pkgs.org/download/python3-distro
+* https://slackbuilds.org/repository/14.2/python/python-distro/
diff --git a/contrib/python/distro/py3/.dist-info/entry_points.txt b/contrib/python/distro/py3/.dist-info/entry_points.txt
new file mode 100644
index 0000000000..08d29c55c1
--- /dev/null
+++ b/contrib/python/distro/py3/.dist-info/entry_points.txt
@@ -0,0 +1,2 @@
+[console_scripts]
+distro = distro.distro:main
diff --git a/contrib/python/distro/py3/.dist-info/top_level.txt b/contrib/python/distro/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..0e0933171d
--- /dev/null
+++ b/contrib/python/distro/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+distro
diff --git a/contrib/python/distro/py3/LICENSE b/contrib/python/distro/py3/LICENSE
new file mode 100644
index 0000000000..e06d208186
--- /dev/null
+++ b/contrib/python/distro/py3/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/contrib/python/distro/py3/README.md b/contrib/python/distro/py3/README.md
new file mode 100644
index 0000000000..de6f3c79d3
--- /dev/null
+++ b/contrib/python/distro/py3/README.md
@@ -0,0 +1,152 @@
+Distro - an OS platform information API
+=======================================
+
+[![CI Status](https://github.com/python-distro/distro/workflows/CI/badge.svg)](https://github.com/python-distro/distro/actions/workflows/ci.yaml)
+[![PyPI version](http://img.shields.io/pypi/v/distro.svg)](https://pypi.python.org/pypi/distro)
+[![Supported Python Versions](https://img.shields.io/pypi/pyversions/distro.svg)](https://img.shields.io/pypi/pyversions/distro.svg)
+[![Code Coverage](https://codecov.io/github/python-distro/distro/coverage.svg?branch=master)](https://codecov.io/github/python-distro/distro?branch=master)
+[![Is Wheel](https://img.shields.io/pypi/wheel/distro.svg?style=flat)](https://pypi.python.org/pypi/distro)
+[![Latest Github Release](https://readthedocs.org/projects/distro/badge/?version=stable)](http://distro.readthedocs.io/en/latest/)
+[![Join the chat at https://gitter.im/python-distro/distro](https://badges.gitter.im/python-distro/distro.svg)](https://gitter.im/python-distro/distro?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+`distro` provides information about the
+OS distribution it runs on, such as a reliable machine-readable ID, or
+version information.
+
+It is the recommended replacement for Python's original
+[`platform.linux_distribution`](https://docs.python.org/3.7/library/platform.html#platform.linux_distribution)
+function (removed in Python 3.8). It also provides much more functionality
+which isn't necessarily Python bound, like a command-line interface.
+
+Distro currently supports Linux and BSD based systems but [Windows and OS X support](https://github.com/python-distro/distro/issues/177) is also planned.
+
+For Python 2.6 support, see https://github.com/python-distro/distro/tree/python2.6-support
+
+## Installation
+
+Installation of the latest released version from PyPI:
+
+```shell
+pip install distro
+```
+
+Installation of the latest development version:
+
+```shell
+pip install https://github.com/python-distro/distro/archive/master.tar.gz
+```
+
+To use as a standalone script, download `distro.py` directly:
+
+```shell
+curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py
+python distro.py
+```
+
+``distro`` is safe to vendor within projects that do not wish to add
+dependencies.
+
+```shell
+cd myproject
+curl -O https://raw.githubusercontent.com/python-distro/distro/master/src/distro/distro.py
+```
+
+## Usage
+
+```bash
+$ distro
+Name: Antergos Linux
+Version: 2015.10 (ISO-Rolling)
+Codename: ISO-Rolling
+
+$ distro -j
+{
+ "codename": "ISO-Rolling",
+ "id": "antergos",
+ "like": "arch",
+ "version": "16.9",
+ "version_parts": {
+ "build_number": "",
+ "major": "16",
+ "minor": "9"
+ }
+}
+
+
+$ python
+>>> import distro
+>>> distro.name(pretty=True)
+'CentOS Linux 8'
+>>> distro.id()
+'centos'
+>>> distro.version(best=True)
+'8.4.2105'
+```
+
+
+## Documentation
+
+On top of the aforementioned API, several more functions are available. For a complete description of the
+API, see the [latest API documentation](http://distro.readthedocs.org/en/latest/).
+
+## Background
+
+An alternative implementation became necessary because Python 3.5 deprecated
+this function, and Python 3.8 removed it altogether. Its predecessor function
+[`platform.dist`](https://docs.python.org/3.7/library/platform.html#platform.dist)
+was already deprecated since Python 2.6 and removed in Python 3.8. Still, there
+are many cases in which access to that information is needed. See [Python issue
+1322](https://bugs.python.org/issue1322) for more information.
+
+The `distro` package implements a robust and inclusive way of retrieving the
+information about a distribution based on new standards and old methods,
+namely from these data sources (from high to low precedence):
+
+* The os-release file `/etc/os-release` if present, with a fall-back on `/usr/lib/os-release` if needed.
+* The output of the `lsb_release` command, if available.
+* The distro release file (`/etc/*(-|_)(release|version)`), if present.
+* The `uname` command for BSD based distrubtions.
+
+
+## Python and Distribution Support
+
+`distro` is supported and tested on Python 3.6+ and PyPy and on any
+distribution that provides one or more of the data sources covered.
+
+This package is tested with test data that mimics the exact behavior of the data sources of [a number of Linux distributions](https://github.com/python-distro/distro/tree/master/tests/resources/distros).
+
+
+## Testing
+
+```shell
+git clone git@github.com:python-distro/distro.git
+cd distro
+pip install tox
+tox
+```
+
+
+## Contributions
+
+Pull requests are always welcome to deal with specific distributions or just
+for general merriment.
+
+See [CONTRIBUTIONS](https://github.com/python-distro/distro/blob/master/CONTRIBUTING.md) for contribution info.
+
+Reference implementations for supporting additional distributions and file
+formats can be found here:
+
+* https://github.com/saltstack/salt/blob/develop/salt/grains/core.py#L1172
+* https://github.com/chef/ohai/blob/master/lib/ohai/plugins/linux/platform.rb
+* https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/facts/system/distribution.py
+* https://github.com/puppetlabs/facter/blob/master/lib/src/facts/linux/os_linux.cc
+
+## Package manager distributions
+
+* https://src.fedoraproject.org/rpms/python-distro
+* https://www.archlinux.org/packages/community/any/python-distro/
+* https://launchpad.net/ubuntu/+source/python-distro
+* https://packages.debian.org/stable/python3-distro
+* https://packages.gentoo.org/packages/dev-python/distro
+* https://pkgs.org/download/python3-distro
+* https://slackbuilds.org/repository/14.2/python/python-distro/
diff --git a/contrib/python/distro/py3/distro/__init__.py b/contrib/python/distro/py3/distro/__init__.py
new file mode 100644
index 0000000000..7686fe85a7
--- /dev/null
+++ b/contrib/python/distro/py3/distro/__init__.py
@@ -0,0 +1,54 @@
+from .distro import (
+ NORMALIZED_DISTRO_ID,
+ NORMALIZED_LSB_ID,
+ NORMALIZED_OS_ID,
+ LinuxDistribution,
+ __version__,
+ build_number,
+ codename,
+ distro_release_attr,
+ distro_release_info,
+ id,
+ info,
+ like,
+ linux_distribution,
+ lsb_release_attr,
+ lsb_release_info,
+ major_version,
+ minor_version,
+ name,
+ os_release_attr,
+ os_release_info,
+ uname_attr,
+ uname_info,
+ version,
+ version_parts,
+)
+
+__all__ = [
+ "NORMALIZED_DISTRO_ID",
+ "NORMALIZED_LSB_ID",
+ "NORMALIZED_OS_ID",
+ "LinuxDistribution",
+ "build_number",
+ "codename",
+ "distro_release_attr",
+ "distro_release_info",
+ "id",
+ "info",
+ "like",
+ "linux_distribution",
+ "lsb_release_attr",
+ "lsb_release_info",
+ "major_version",
+ "minor_version",
+ "name",
+ "os_release_attr",
+ "os_release_info",
+ "uname_attr",
+ "uname_info",
+ "version",
+ "version_parts",
+]
+
+__version__ = __version__
diff --git a/contrib/python/distro/py3/distro/__main__.py b/contrib/python/distro/py3/distro/__main__.py
new file mode 100644
index 0000000000..0c01d5b08b
--- /dev/null
+++ b/contrib/python/distro/py3/distro/__main__.py
@@ -0,0 +1,4 @@
+from .distro import main
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/python/distro/py3/distro/distro.py b/contrib/python/distro/py3/distro/distro.py
new file mode 100644
index 0000000000..89e1868047
--- /dev/null
+++ b/contrib/python/distro/py3/distro/distro.py
@@ -0,0 +1,1399 @@
+#!/usr/bin/env python
+# Copyright 2015,2016,2017 Nir Cohen
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+The ``distro`` package (``distro`` stands for Linux Distribution) provides
+information about the Linux distribution it runs on, such as a reliable
+machine-readable distro ID, or version information.
+
+It is the recommended replacement for Python's original
+:py:func:`platform.linux_distribution` function, but it provides much more
+functionality. An alternative implementation became necessary because Python
+3.5 deprecated this function, and Python 3.8 removed it altogether. Its
+predecessor function :py:func:`platform.dist` was already deprecated since
+Python 2.6 and removed in Python 3.8. Still, there are many cases in which
+access to OS distribution information is needed. See `Python issue 1322
+<https://bugs.python.org/issue1322>`_ for more information.
+"""
+
+import argparse
+import json
+import logging
+import os
+import re
+import shlex
+import subprocess
+import sys
+import warnings
+from typing import (
+ Any,
+ Callable,
+ Dict,
+ Iterable,
+ Optional,
+ Sequence,
+ TextIO,
+ Tuple,
+ Type,
+)
+
+try:
+ from typing import TypedDict
+except ImportError:
+ # Python 3.7
+ TypedDict = dict
+
+__version__ = "1.8.0"
+
+
+class VersionDict(TypedDict):
+ major: str
+ minor: str
+ build_number: str
+
+
+class InfoDict(TypedDict):
+ id: str
+ version: str
+ version_parts: VersionDict
+ like: str
+ codename: str
+
+
+_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc")
+_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib")
+_OS_RELEASE_BASENAME = "os-release"
+
+#: Translation table for normalizing the "ID" attribute defined in os-release
+#: files, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as defined in the os-release file, translated to lower case,
+#: with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_OS_ID = {
+ "ol": "oracle", # Oracle Linux
+ "opensuse-leap": "opensuse", # Newer versions of OpenSuSE report as opensuse-leap
+}
+
+#: Translation table for normalizing the "Distributor ID" attribute returned by
+#: the lsb_release command, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as returned by the lsb_release command, translated to lower
+#: case, with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_LSB_ID = {
+ "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4
+ "enterpriseenterpriseserver": "oracle", # Oracle Linux 5
+ "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation
+ "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server
+ "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode
+}
+
+#: Translation table for normalizing the distro ID derived from the file name
+#: of distro release files, for use by the :func:`distro.id` method.
+#:
+#: * Key: Value as derived from the file name of a distro release file,
+#: translated to lower case, with blanks translated to underscores.
+#:
+#: * Value: Normalized value.
+NORMALIZED_DISTRO_ID = {
+ "redhat": "rhel", # RHEL 6.x, 7.x
+}
+
+# Pattern for content of distro release file (reversed)
+_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile(
+ r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)"
+)
+
+# Pattern for base file name of distro release file
+_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$")
+
+# Base file names to be looked up for if _UNIXCONFDIR is not readable.
+_DISTRO_RELEASE_BASENAMES = [
+ "SuSE-release",
+ "arch-release",
+ "base-release",
+ "centos-release",
+ "fedora-release",
+ "gentoo-release",
+ "mageia-release",
+ "mandrake-release",
+ "mandriva-release",
+ "mandrivalinux-release",
+ "manjaro-release",
+ "oracle-release",
+ "redhat-release",
+ "rocky-release",
+ "sl-release",
+ "slackware-version",
+]
+
+# Base file names to be ignored when searching for distro release file
+_DISTRO_RELEASE_IGNORE_BASENAMES = (
+ "debian_version",
+ "lsb-release",
+ "oem-release",
+ _OS_RELEASE_BASENAME,
+ "system-release",
+ "plesk-release",
+ "iredmail-release",
+)
+
+
+def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]:
+ """
+ .. deprecated:: 1.6.0
+
+ :func:`distro.linux_distribution()` is deprecated. It should only be
+ used as a compatibility shim with Python's
+ :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`,
+ :func:`distro.version` and :func:`distro.name` instead.
+
+ Return information about the current OS distribution as a tuple
+ ``(id_name, version, codename)`` with items as follows:
+
+ * ``id_name``: If *full_distribution_name* is false, the result of
+ :func:`distro.id`. Otherwise, the result of :func:`distro.name`.
+
+ * ``version``: The result of :func:`distro.version`.
+
+ * ``codename``: The extra item (usually in parentheses) after the
+ os-release version number, or the result of :func:`distro.codename`.
+
+ The interface of this function is compatible with the original
+ :py:func:`platform.linux_distribution` function, supporting a subset of
+ its parameters.
+
+ The data it returns may not exactly be the same, because it uses more data
+ sources than the original function, and that may lead to different data if
+ the OS distribution is not consistent across multiple data sources it
+ provides (there are indeed such distributions ...).
+
+ Another reason for differences is the fact that the :func:`distro.id`
+ method normalizes the distro ID string to a reliable machine-readable value
+ for a number of popular OS distributions.
+ """
+ warnings.warn(
+ "distro.linux_distribution() is deprecated. It should only be used as a "
+ "compatibility shim with Python's platform.linux_distribution(). Please use "
+ "distro.id(), distro.version() and distro.name() instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return _distro.linux_distribution(full_distribution_name)
+
+
+def id() -> str:
+ """
+ Return the distro ID of the current distribution, as a
+ machine-readable string.
+
+ For a number of OS distributions, the returned distro ID value is
+ *reliable*, in the sense that it is documented and that it does not change
+ across releases of the distribution.
+
+ This package maintains the following reliable distro ID values:
+
+ ============== =========================================
+ Distro ID Distribution
+ ============== =========================================
+ "ubuntu" Ubuntu
+ "debian" Debian
+ "rhel" RedHat Enterprise Linux
+ "centos" CentOS
+ "fedora" Fedora
+ "sles" SUSE Linux Enterprise Server
+ "opensuse" openSUSE
+ "amzn" Amazon Linux
+ "arch" Arch Linux
+ "buildroot" Buildroot
+ "cloudlinux" CloudLinux OS
+ "exherbo" Exherbo Linux
+ "gentoo" GenToo Linux
+ "ibm_powerkvm" IBM PowerKVM
+ "kvmibm" KVM for IBM z Systems
+ "linuxmint" Linux Mint
+ "mageia" Mageia
+ "mandriva" Mandriva Linux
+ "parallels" Parallels
+ "pidora" Pidora
+ "raspbian" Raspbian
+ "oracle" Oracle Linux (and Oracle Enterprise Linux)
+ "scientific" Scientific Linux
+ "slackware" Slackware
+ "xenserver" XenServer
+ "openbsd" OpenBSD
+ "netbsd" NetBSD
+ "freebsd" FreeBSD
+ "midnightbsd" MidnightBSD
+ "rocky" Rocky Linux
+ "aix" AIX
+ "guix" Guix System
+ ============== =========================================
+
+ If you have a need to get distros for reliable IDs added into this set,
+ or if you find that the :func:`distro.id` function returns a different
+ distro ID for one of the listed distros, please create an issue in the
+ `distro issue tracker`_.
+
+ **Lookup hierarchy and transformations:**
+
+ First, the ID is obtained from the following sources, in the specified
+ order. The first available and non-empty value is used:
+
+ * the value of the "ID" attribute of the os-release file,
+
+ * the value of the "Distributor ID" attribute returned by the lsb_release
+ command,
+
+ * the first part of the file name of the distro release file,
+
+ The so determined ID value then passes the following transformations,
+ before it is returned by this method:
+
+ * it is translated to lower case,
+
+ * blanks (which should not be there anyway) are translated to underscores,
+
+ * a normalization of the ID is performed, based upon
+ `normalization tables`_. The purpose of this normalization is to ensure
+ that the ID is as reliable as possible, even across incompatible changes
+ in the OS distributions. A common reason for an incompatible change is
+ the addition of an os-release file, or the addition of the lsb_release
+ command, with ID values that differ from what was previously determined
+ from the distro release file name.
+ """
+ return _distro.id()
+
+
+def name(pretty: bool = False) -> str:
+ """
+ Return the name of the current OS distribution, as a human-readable
+ string.
+
+ If *pretty* is false, the name is returned without version or codename.
+ (e.g. "CentOS Linux")
+
+ If *pretty* is true, the version and codename are appended.
+ (e.g. "CentOS Linux 7.1.1503 (Core)")
+
+ **Lookup hierarchy:**
+
+ The name is obtained from the following sources, in the specified order.
+ The first available and non-empty value is used:
+
+ * If *pretty* is false:
+
+ - the value of the "NAME" attribute of the os-release file,
+
+ - the value of the "Distributor ID" attribute returned by the lsb_release
+ command,
+
+ - the value of the "<name>" field of the distro release file.
+
+ * If *pretty* is true:
+
+ - the value of the "PRETTY_NAME" attribute of the os-release file,
+
+ - the value of the "Description" attribute returned by the lsb_release
+ command,
+
+ - the value of the "<name>" field of the distro release file, appended
+ with the value of the pretty version ("<version_id>" and "<codename>"
+ fields) of the distro release file, if available.
+ """
+ return _distro.name(pretty)
+
+
+def version(pretty: bool = False, best: bool = False) -> str:
+ """
+ Return the version of the current OS distribution, as a human-readable
+ string.
+
+ If *pretty* is false, the version is returned without codename (e.g.
+ "7.0").
+
+ If *pretty* is true, the codename in parenthesis is appended, if the
+ codename is non-empty (e.g. "7.0 (Maipo)").
+
+ Some distributions provide version numbers with different precisions in
+ the different sources of distribution information. Examining the different
+ sources in a fixed priority order does not always yield the most precise
+ version (e.g. for Debian 8.2, or CentOS 7.1).
+
+ Some other distributions may not provide this kind of information. In these
+ cases, an empty string would be returned. This behavior can be observed
+ with rolling releases distributions (e.g. Arch Linux).
+
+ The *best* parameter can be used to control the approach for the returned
+ version:
+
+ If *best* is false, the first non-empty version number in priority order of
+ the examined sources is returned.
+
+ If *best* is true, the most precise version number out of all examined
+ sources is returned.
+
+ **Lookup hierarchy:**
+
+ In all cases, the version number is obtained from the following sources.
+ If *best* is false, this order represents the priority order:
+
+ * the value of the "VERSION_ID" attribute of the os-release file,
+ * the value of the "Release" attribute returned by the lsb_release
+ command,
+ * the version number parsed from the "<version_id>" field of the first line
+ of the distro release file,
+ * the version number parsed from the "PRETTY_NAME" attribute of the
+ os-release file, if it follows the format of the distro release files.
+ * the version number parsed from the "Description" attribute returned by
+ the lsb_release command, if it follows the format of the distro release
+ files.
+ """
+ return _distro.version(pretty, best)
+
+
+def version_parts(best: bool = False) -> Tuple[str, str, str]:
+ """
+ Return the version of the current OS distribution as a tuple
+ ``(major, minor, build_number)`` with items as follows:
+
+ * ``major``: The result of :func:`distro.major_version`.
+
+ * ``minor``: The result of :func:`distro.minor_version`.
+
+ * ``build_number``: The result of :func:`distro.build_number`.
+
+ For a description of the *best* parameter, see the :func:`distro.version`
+ method.
+ """
+ return _distro.version_parts(best)
+
+
+def major_version(best: bool = False) -> str:
+ """
+ Return the major version of the current OS distribution, as a string,
+ if provided.
+ Otherwise, the empty string is returned. The major version is the first
+ part of the dot-separated version string.
+
+ For a description of the *best* parameter, see the :func:`distro.version`
+ method.
+ """
+ return _distro.major_version(best)
+
+
+def minor_version(best: bool = False) -> str:
+ """
+ Return the minor version of the current OS distribution, as a string,
+ if provided.
+ Otherwise, the empty string is returned. The minor version is the second
+ part of the dot-separated version string.
+
+ For a description of the *best* parameter, see the :func:`distro.version`
+ method.
+ """
+ return _distro.minor_version(best)
+
+
+def build_number(best: bool = False) -> str:
+ """
+ Return the build number of the current OS distribution, as a string,
+ if provided.
+ Otherwise, the empty string is returned. The build number is the third part
+ of the dot-separated version string.
+
+ For a description of the *best* parameter, see the :func:`distro.version`
+ method.
+ """
+ return _distro.build_number(best)
+
+
+def like() -> str:
+ """
+ Return a space-separated list of distro IDs of distributions that are
+ closely related to the current OS distribution in regards to packaging
+ and programming interfaces, for example distributions the current
+ distribution is a derivative from.
+
+ **Lookup hierarchy:**
+
+ This information item is only provided by the os-release file.
+ For details, see the description of the "ID_LIKE" attribute in the
+ `os-release man page
+ <http://www.freedesktop.org/software/systemd/man/os-release.html>`_.
+ """
+ return _distro.like()
+
+
+def codename() -> str:
+ """
+ Return the codename for the release of the current OS distribution,
+ as a string.
+
+ If the distribution does not have a codename, an empty string is returned.
+
+ Note that the returned codename is not always really a codename. For
+ example, openSUSE returns "x86_64". This function does not handle such
+ cases in any special way and just returns the string it finds, if any.
+
+ **Lookup hierarchy:**
+
+ * the codename within the "VERSION" attribute of the os-release file, if
+ provided,
+
+ * the value of the "Codename" attribute returned by the lsb_release
+ command,
+
+ * the value of the "<codename>" field of the distro release file.
+ """
+ return _distro.codename()
+
+
+def info(pretty: bool = False, best: bool = False) -> InfoDict:
+ """
+ Return certain machine-readable information items about the current OS
+ distribution in a dictionary, as shown in the following example:
+
+ .. sourcecode:: python
+
+ {
+ 'id': 'rhel',
+ 'version': '7.0',
+ 'version_parts': {
+ 'major': '7',
+ 'minor': '0',
+ 'build_number': ''
+ },
+ 'like': 'fedora',
+ 'codename': 'Maipo'
+ }
+
+ The dictionary structure and keys are always the same, regardless of which
+ information items are available in the underlying data sources. The values
+ for the various keys are as follows:
+
+ * ``id``: The result of :func:`distro.id`.
+
+ * ``version``: The result of :func:`distro.version`.
+
+ * ``version_parts -> major``: The result of :func:`distro.major_version`.
+
+ * ``version_parts -> minor``: The result of :func:`distro.minor_version`.
+
+ * ``version_parts -> build_number``: The result of
+ :func:`distro.build_number`.
+
+ * ``like``: The result of :func:`distro.like`.
+
+ * ``codename``: The result of :func:`distro.codename`.
+
+ For a description of the *pretty* and *best* parameters, see the
+ :func:`distro.version` method.
+ """
+ return _distro.info(pretty, best)
+
+
+def os_release_info() -> Dict[str, str]:
+ """
+ Return a dictionary containing key-value pairs for the information items
+ from the os-release file data source of the current OS distribution.
+
+ See `os-release file`_ for details about these information items.
+ """
+ return _distro.os_release_info()
+
+
+def lsb_release_info() -> Dict[str, str]:
+ """
+ Return a dictionary containing key-value pairs for the information items
+ from the lsb_release command data source of the current OS distribution.
+
+ See `lsb_release command output`_ for details about these information
+ items.
+ """
+ return _distro.lsb_release_info()
+
+
+def distro_release_info() -> Dict[str, str]:
+ """
+ Return a dictionary containing key-value pairs for the information items
+ from the distro release file data source of the current OS distribution.
+
+ See `distro release file`_ for details about these information items.
+ """
+ return _distro.distro_release_info()
+
+
+def uname_info() -> Dict[str, str]:
+ """
+ Return a dictionary containing key-value pairs for the information items
+ from the distro release file data source of the current OS distribution.
+ """
+ return _distro.uname_info()
+
+
+def os_release_attr(attribute: str) -> str:
+ """
+ Return a single named information item from the os-release file data source
+ of the current OS distribution.
+
+ Parameters:
+
+ * ``attribute`` (string): Key of the information item.
+
+ Returns:
+
+ * (string): Value of the information item, if the item exists.
+ The empty string, if the item does not exist.
+
+ See `os-release file`_ for details about these information items.
+ """
+ return _distro.os_release_attr(attribute)
+
+
+def lsb_release_attr(attribute: str) -> str:
+ """
+ Return a single named information item from the lsb_release command output
+ data source of the current OS distribution.
+
+ Parameters:
+
+ * ``attribute`` (string): Key of the information item.
+
+ Returns:
+
+ * (string): Value of the information item, if the item exists.
+ The empty string, if the item does not exist.
+
+ See `lsb_release command output`_ for details about these information
+ items.
+ """
+ return _distro.lsb_release_attr(attribute)
+
+
+def distro_release_attr(attribute: str) -> str:
+ """
+ Return a single named information item from the distro release file
+ data source of the current OS distribution.
+
+ Parameters:
+
+ * ``attribute`` (string): Key of the information item.
+
+ Returns:
+
+ * (string): Value of the information item, if the item exists.
+ The empty string, if the item does not exist.
+
+ See `distro release file`_ for details about these information items.
+ """
+ return _distro.distro_release_attr(attribute)
+
+
+def uname_attr(attribute: str) -> str:
+ """
+ Return a single named information item from the distro release file
+ data source of the current OS distribution.
+
+ Parameters:
+
+ * ``attribute`` (string): Key of the information item.
+
+ Returns:
+
+ * (string): Value of the information item, if the item exists.
+ The empty string, if the item does not exist.
+ """
+ return _distro.uname_attr(attribute)
+
+
+try:
+ from functools import cached_property
+except ImportError:
+ # Python < 3.8
+ class cached_property: # type: ignore
+ """A version of @property which caches the value. On access, it calls the
+ underlying function and sets the value in `__dict__` so future accesses
+ will not re-call the property.
+ """
+
+ def __init__(self, f: Callable[[Any], Any]) -> None:
+ self._fname = f.__name__
+ self._f = f
+
+ def __get__(self, obj: Any, owner: Type[Any]) -> Any:
+ assert obj is not None, f"call {self._fname} on an instance"
+ ret = obj.__dict__[self._fname] = self._f(obj)
+ return ret
+
+
+class LinuxDistribution:
+ """
+ Provides information about a OS distribution.
+
+ This package creates a private module-global instance of this class with
+ default initialization arguments, that is used by the
+ `consolidated accessor functions`_ and `single source accessor functions`_.
+ By using default initialization arguments, that module-global instance
+ returns data about the current OS distribution (i.e. the distro this
+ package runs on).
+
+ Normally, it is not necessary to create additional instances of this class.
+ However, in situations where control is needed over the exact data sources
+ that are used, instances of this class can be created with a specific
+ distro release file, or a specific os-release file, or without invoking the
+ lsb_release command.
+ """
+
+ def __init__(
+ self,
+ include_lsb: Optional[bool] = None,
+ os_release_file: str = "",
+ distro_release_file: str = "",
+ include_uname: Optional[bool] = None,
+ root_dir: Optional[str] = None,
+ include_oslevel: Optional[bool] = None,
+ ) -> None:
+ """
+ The initialization method of this class gathers information from the
+ available data sources, and stores that in private instance attributes.
+ Subsequent access to the information items uses these private instance
+ attributes, so that the data sources are read only once.
+
+ Parameters:
+
+ * ``include_lsb`` (bool): Controls whether the
+ `lsb_release command output`_ is included as a data source.
+
+ If the lsb_release command is not available in the program execution
+ path, the data source for the lsb_release command will be empty.
+
+ * ``os_release_file`` (string): The path name of the
+ `os-release file`_ that is to be used as a data source.
+
+ An empty string (the default) will cause the default path name to
+ be used (see `os-release file`_ for details).
+
+ If the specified or defaulted os-release file does not exist, the
+ data source for the os-release file will be empty.
+
+ * ``distro_release_file`` (string): The path name of the
+ `distro release file`_ that is to be used as a data source.
+
+ An empty string (the default) will cause a default search algorithm
+ to be used (see `distro release file`_ for details).
+
+ If the specified distro release file does not exist, or if no default
+ distro release file can be found, the data source for the distro
+ release file will be empty.
+
+ * ``include_uname`` (bool): Controls whether uname command output is
+ included as a data source. If the uname command is not available in
+ the program execution path the data source for the uname command will
+ be empty.
+
+ * ``root_dir`` (string): The absolute path to the root directory to use
+ to find distro-related information files. Note that ``include_*``
+ parameters must not be enabled in combination with ``root_dir``.
+
+ * ``include_oslevel`` (bool): Controls whether (AIX) oslevel command
+ output is included as a data source. If the oslevel command is not
+ available in the program execution path the data source will be
+ empty.
+
+ Public instance attributes:
+
+ * ``os_release_file`` (string): The path name of the
+ `os-release file`_ that is actually used as a data source. The
+ empty string if no distro release file is used as a data source.
+
+ * ``distro_release_file`` (string): The path name of the
+ `distro release file`_ that is actually used as a data source. The
+ empty string if no distro release file is used as a data source.
+
+ * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter.
+ This controls whether the lsb information will be loaded.
+
+ * ``include_uname`` (bool): The result of the ``include_uname``
+ parameter. This controls whether the uname information will
+ be loaded.
+
+ * ``include_oslevel`` (bool): The result of the ``include_oslevel``
+ parameter. This controls whether (AIX) oslevel information will be
+ loaded.
+
+ * ``root_dir`` (string): The result of the ``root_dir`` parameter.
+ The absolute path to the root directory to use to find distro-related
+ information files.
+
+ Raises:
+
+ * :py:exc:`ValueError`: Initialization parameters combination is not
+ supported.
+
+ * :py:exc:`OSError`: Some I/O issue with an os-release file or distro
+ release file.
+
+ * :py:exc:`UnicodeError`: A data source has unexpected characters or
+ uses an unexpected encoding.
+ """
+ self.root_dir = root_dir
+ self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR
+ self.usr_lib_dir = (
+ os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR
+ )
+
+ if os_release_file:
+ self.os_release_file = os_release_file
+ else:
+ etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME)
+ usr_lib_os_release_file = os.path.join(
+ self.usr_lib_dir, _OS_RELEASE_BASENAME
+ )
+
+ # NOTE: The idea is to respect order **and** have it set
+ # at all times for API backwards compatibility.
+ if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile(
+ usr_lib_os_release_file
+ ):
+ self.os_release_file = etc_dir_os_release_file
+ else:
+ self.os_release_file = usr_lib_os_release_file
+
+ self.distro_release_file = distro_release_file or "" # updated later
+
+ is_root_dir_defined = root_dir is not None
+ if is_root_dir_defined and (include_lsb or include_uname or include_oslevel):
+ raise ValueError(
+ "Including subprocess data sources from specific root_dir is disallowed"
+ " to prevent false information"
+ )
+ self.include_lsb = (
+ include_lsb if include_lsb is not None else not is_root_dir_defined
+ )
+ self.include_uname = (
+ include_uname if include_uname is not None else not is_root_dir_defined
+ )
+ self.include_oslevel = (
+ include_oslevel if include_oslevel is not None else not is_root_dir_defined
+ )
+
+ def __repr__(self) -> str:
+ """Return repr of all info"""
+ return (
+ "LinuxDistribution("
+ "os_release_file={self.os_release_file!r}, "
+ "distro_release_file={self.distro_release_file!r}, "
+ "include_lsb={self.include_lsb!r}, "
+ "include_uname={self.include_uname!r}, "
+ "include_oslevel={self.include_oslevel!r}, "
+ "root_dir={self.root_dir!r}, "
+ "_os_release_info={self._os_release_info!r}, "
+ "_lsb_release_info={self._lsb_release_info!r}, "
+ "_distro_release_info={self._distro_release_info!r}, "
+ "_uname_info={self._uname_info!r}, "
+ "_oslevel_info={self._oslevel_info!r})".format(self=self)
+ )
+
+ def linux_distribution(
+ self, full_distribution_name: bool = True
+ ) -> Tuple[str, str, str]:
+ """
+ Return information about the OS distribution that is compatible
+ with Python's :func:`platform.linux_distribution`, supporting a subset
+ of its parameters.
+
+ For details, see :func:`distro.linux_distribution`.
+ """
+ return (
+ self.name() if full_distribution_name else self.id(),
+ self.version(),
+ self._os_release_info.get("release_codename") or self.codename(),
+ )
+
+ def id(self) -> str:
+ """Return the distro ID of the OS distribution, as a string.
+
+ For details, see :func:`distro.id`.
+ """
+
+ def normalize(distro_id: str, table: Dict[str, str]) -> str:
+ distro_id = distro_id.lower().replace(" ", "_")
+ return table.get(distro_id, distro_id)
+
+ distro_id = self.os_release_attr("id")
+ if distro_id:
+ return normalize(distro_id, NORMALIZED_OS_ID)
+
+ distro_id = self.lsb_release_attr("distributor_id")
+ if distro_id:
+ return normalize(distro_id, NORMALIZED_LSB_ID)
+
+ distro_id = self.distro_release_attr("id")
+ if distro_id:
+ return normalize(distro_id, NORMALIZED_DISTRO_ID)
+
+ distro_id = self.uname_attr("id")
+ if distro_id:
+ return normalize(distro_id, NORMALIZED_DISTRO_ID)
+
+ return ""
+
+ def name(self, pretty: bool = False) -> str:
+ """
+ Return the name of the OS distribution, as a string.
+
+ For details, see :func:`distro.name`.
+ """
+ name = (
+ self.os_release_attr("name")
+ or self.lsb_release_attr("distributor_id")
+ or self.distro_release_attr("name")
+ or self.uname_attr("name")
+ )
+ if pretty:
+ name = self.os_release_attr("pretty_name") or self.lsb_release_attr(
+ "description"
+ )
+ if not name:
+ name = self.distro_release_attr("name") or self.uname_attr("name")
+ version = self.version(pretty=True)
+ if version:
+ name = f"{name} {version}"
+ return name or ""
+
+ def version(self, pretty: bool = False, best: bool = False) -> str:
+ """
+ Return the version of the OS distribution, as a string.
+
+ For details, see :func:`distro.version`.
+ """
+ versions = [
+ self.os_release_attr("version_id"),
+ self.lsb_release_attr("release"),
+ self.distro_release_attr("version_id"),
+ self._parse_distro_release_content(self.os_release_attr("pretty_name")).get(
+ "version_id", ""
+ ),
+ self._parse_distro_release_content(
+ self.lsb_release_attr("description")
+ ).get("version_id", ""),
+ self.uname_attr("release"),
+ ]
+ if self.uname_attr("id").startswith("aix"):
+ # On AIX platforms, prefer oslevel command output.
+ versions.insert(0, self.oslevel_info())
+ elif self.id() == "debian" or "debian" in self.like().split():
+ # On Debian-like, add debian_version file content to candidates list.
+ versions.append(self._debian_version)
+ version = ""
+ if best:
+ # This algorithm uses the last version in priority order that has
+ # the best precision. If the versions are not in conflict, that
+ # does not matter; otherwise, using the last one instead of the
+ # first one might be considered a surprise.
+ for v in versions:
+ if v.count(".") > version.count(".") or version == "":
+ version = v
+ else:
+ for v in versions:
+ if v != "":
+ version = v
+ break
+ if pretty and version and self.codename():
+ version = f"{version} ({self.codename()})"
+ return version
+
+ def version_parts(self, best: bool = False) -> Tuple[str, str, str]:
+ """
+ Return the version of the OS distribution, as a tuple of version
+ numbers.
+
+ For details, see :func:`distro.version_parts`.
+ """
+ version_str = self.version(best=best)
+ if version_str:
+ version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?")
+ matches = version_regex.match(version_str)
+ if matches:
+ major, minor, build_number = matches.groups()
+ return major, minor or "", build_number or ""
+ return "", "", ""
+
+ def major_version(self, best: bool = False) -> str:
+ """
+ Return the major version number of the current distribution.
+
+ For details, see :func:`distro.major_version`.
+ """
+ return self.version_parts(best)[0]
+
+ def minor_version(self, best: bool = False) -> str:
+ """
+ Return the minor version number of the current distribution.
+
+ For details, see :func:`distro.minor_version`.
+ """
+ return self.version_parts(best)[1]
+
+ def build_number(self, best: bool = False) -> str:
+ """
+ Return the build number of the current distribution.
+
+ For details, see :func:`distro.build_number`.
+ """
+ return self.version_parts(best)[2]
+
+ def like(self) -> str:
+ """
+ Return the IDs of distributions that are like the OS distribution.
+
+ For details, see :func:`distro.like`.
+ """
+ return self.os_release_attr("id_like") or ""
+
+ def codename(self) -> str:
+ """
+ Return the codename of the OS distribution.
+
+ For details, see :func:`distro.codename`.
+ """
+ try:
+ # Handle os_release specially since distros might purposefully set
+ # this to empty string to have no codename
+ return self._os_release_info["codename"]
+ except KeyError:
+ return (
+ self.lsb_release_attr("codename")
+ or self.distro_release_attr("codename")
+ or ""
+ )
+
+ def info(self, pretty: bool = False, best: bool = False) -> InfoDict:
+ """
+ Return certain machine-readable information about the OS
+ distribution.
+
+ For details, see :func:`distro.info`.
+ """
+ return dict(
+ id=self.id(),
+ version=self.version(pretty, best),
+ version_parts=dict(
+ major=self.major_version(best),
+ minor=self.minor_version(best),
+ build_number=self.build_number(best),
+ ),
+ like=self.like(),
+ codename=self.codename(),
+ )
+
+ def os_release_info(self) -> Dict[str, str]:
+ """
+ Return a dictionary containing key-value pairs for the information
+ items from the os-release file data source of the OS distribution.
+
+ For details, see :func:`distro.os_release_info`.
+ """
+ return self._os_release_info
+
+ def lsb_release_info(self) -> Dict[str, str]:
+ """
+ Return a dictionary containing key-value pairs for the information
+ items from the lsb_release command data source of the OS
+ distribution.
+
+ For details, see :func:`distro.lsb_release_info`.
+ """
+ return self._lsb_release_info
+
+ def distro_release_info(self) -> Dict[str, str]:
+ """
+ Return a dictionary containing key-value pairs for the information
+ items from the distro release file data source of the OS
+ distribution.
+
+ For details, see :func:`distro.distro_release_info`.
+ """
+ return self._distro_release_info
+
+ def uname_info(self) -> Dict[str, str]:
+ """
+ Return a dictionary containing key-value pairs for the information
+ items from the uname command data source of the OS distribution.
+
+ For details, see :func:`distro.uname_info`.
+ """
+ return self._uname_info
+
+ def oslevel_info(self) -> str:
+ """
+ Return AIX' oslevel command output.
+ """
+ return self._oslevel_info
+
+ def os_release_attr(self, attribute: str) -> str:
+ """
+ Return a single named information item from the os-release file data
+ source of the OS distribution.
+
+ For details, see :func:`distro.os_release_attr`.
+ """
+ return self._os_release_info.get(attribute, "")
+
+ def lsb_release_attr(self, attribute: str) -> str:
+ """
+ Return a single named information item from the lsb_release command
+ output data source of the OS distribution.
+
+ For details, see :func:`distro.lsb_release_attr`.
+ """
+ return self._lsb_release_info.get(attribute, "")
+
+ def distro_release_attr(self, attribute: str) -> str:
+ """
+ Return a single named information item from the distro release file
+ data source of the OS distribution.
+
+ For details, see :func:`distro.distro_release_attr`.
+ """
+ return self._distro_release_info.get(attribute, "")
+
+ def uname_attr(self, attribute: str) -> str:
+ """
+ Return a single named information item from the uname command
+ output data source of the OS distribution.
+
+ For details, see :func:`distro.uname_attr`.
+ """
+ return self._uname_info.get(attribute, "")
+
+ @cached_property
+ def _os_release_info(self) -> Dict[str, str]:
+ """
+ Get the information items from the specified os-release file.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ if os.path.isfile(self.os_release_file):
+ with open(self.os_release_file, encoding="utf-8") as release_file:
+ return self._parse_os_release_content(release_file)
+ return {}
+
+ @staticmethod
+ def _parse_os_release_content(lines: TextIO) -> Dict[str, str]:
+ """
+ Parse the lines of an os-release file.
+
+ Parameters:
+
+ * lines: Iterable through the lines in the os-release file.
+ Each line must be a unicode string or a UTF-8 encoded byte
+ string.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ props = {}
+ lexer = shlex.shlex(lines, posix=True)
+ lexer.whitespace_split = True
+
+ tokens = list(lexer)
+ for token in tokens:
+ # At this point, all shell-like parsing has been done (i.e.
+ # comments processed, quotes and backslash escape sequences
+ # processed, multi-line values assembled, trailing newlines
+ # stripped, etc.), so the tokens are now either:
+ # * variable assignments: var=value
+ # * commands or their arguments (not allowed in os-release)
+ # Ignore any tokens that are not variable assignments
+ if "=" in token:
+ k, v = token.split("=", 1)
+ props[k.lower()] = v
+
+ if "version" in props:
+ # extract release codename (if any) from version attribute
+ match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"])
+ if match:
+ release_codename = match.group(1) or match.group(2)
+ props["codename"] = props["release_codename"] = release_codename
+
+ if "version_codename" in props:
+ # os-release added a version_codename field. Use that in
+ # preference to anything else Note that some distros purposefully
+ # do not have code names. They should be setting
+ # version_codename=""
+ props["codename"] = props["version_codename"]
+ elif "ubuntu_codename" in props:
+ # Same as above but a non-standard field name used on older Ubuntus
+ props["codename"] = props["ubuntu_codename"]
+
+ return props
+
+ @cached_property
+ def _lsb_release_info(self) -> Dict[str, str]:
+ """
+ Get the information items from the lsb_release command output.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ if not self.include_lsb:
+ return {}
+ try:
+ cmd = ("lsb_release", "-a")
+ stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
+ # Command not found or lsb_release returned error
+ except (OSError, subprocess.CalledProcessError):
+ return {}
+ content = self._to_str(stdout).splitlines()
+ return self._parse_lsb_release_content(content)
+
+ @staticmethod
+ def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]:
+ """
+ Parse the output of the lsb_release command.
+
+ Parameters:
+
+ * lines: Iterable through the lines of the lsb_release output.
+ Each line must be a unicode string or a UTF-8 encoded byte
+ string.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ props = {}
+ for line in lines:
+ kv = line.strip("\n").split(":", 1)
+ if len(kv) != 2:
+ # Ignore lines without colon.
+ continue
+ k, v = kv
+ props.update({k.replace(" ", "_").lower(): v.strip()})
+ return props
+
+ @cached_property
+ def _uname_info(self) -> Dict[str, str]:
+ if not self.include_uname:
+ return {}
+ try:
+ cmd = ("uname", "-rs")
+ stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
+ except OSError:
+ return {}
+ content = self._to_str(stdout).splitlines()
+ return self._parse_uname_content(content)
+
+ @cached_property
+ def _oslevel_info(self) -> str:
+ if not self.include_oslevel:
+ return ""
+ try:
+ stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL)
+ except (OSError, subprocess.CalledProcessError):
+ return ""
+ return self._to_str(stdout).strip()
+
+ @cached_property
+ def _debian_version(self) -> str:
+ try:
+ with open(
+ os.path.join(self.etc_dir, "debian_version"), encoding="ascii"
+ ) as fp:
+ return fp.readline().rstrip()
+ except FileNotFoundError:
+ return ""
+
+ @staticmethod
+ def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
+ if not lines:
+ return {}
+ props = {}
+ match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
+ if match:
+ name, version = match.groups()
+
+ # This is to prevent the Linux kernel version from
+ # appearing as the 'best' version on otherwise
+ # identifiable distributions.
+ if name == "Linux":
+ return {}
+ props["id"] = name.lower()
+ props["name"] = name
+ props["release"] = version
+ return props
+
+ @staticmethod
+ def _to_str(bytestring: bytes) -> str:
+ encoding = sys.getfilesystemencoding()
+ return bytestring.decode(encoding)
+
+ @cached_property
+ def _distro_release_info(self) -> Dict[str, str]:
+ """
+ Get the information items from the specified distro release file.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ if self.distro_release_file:
+ # If it was specified, we use it and parse what we can, even if
+ # its file name or content does not match the expected pattern.
+ distro_info = self._parse_distro_release_file(self.distro_release_file)
+ basename = os.path.basename(self.distro_release_file)
+ # The file name pattern for user-specified distro release files
+ # is somewhat more tolerant (compared to when searching for the
+ # file), because we want to use what was specified as best as
+ # possible.
+ match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
+ else:
+ try:
+ basenames = [
+ basename
+ for basename in os.listdir(self.etc_dir)
+ if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES
+ and os.path.isfile(os.path.join(self.etc_dir, basename))
+ ]
+ # We sort for repeatability in cases where there are multiple
+ # distro specific files; e.g. CentOS, Oracle, Enterprise all
+ # containing `redhat-release` on top of their own.
+ basenames.sort()
+ except OSError:
+ # This may occur when /etc is not readable but we can't be
+ # sure about the *-release files. Check common entries of
+ # /etc for information. If they turn out to not be there the
+ # error is handled in `_parse_distro_release_file()`.
+ basenames = _DISTRO_RELEASE_BASENAMES
+ for basename in basenames:
+ match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
+ if match is None:
+ continue
+ filepath = os.path.join(self.etc_dir, basename)
+ distro_info = self._parse_distro_release_file(filepath)
+ # The name is always present if the pattern matches.
+ if "name" not in distro_info:
+ continue
+ self.distro_release_file = filepath
+ break
+ else: # the loop didn't "break": no candidate.
+ return {}
+
+ if match is not None:
+ distro_info["id"] = match.group(1)
+
+ # CloudLinux < 7: manually enrich info with proper id.
+ if "cloudlinux" in distro_info.get("name", "").lower():
+ distro_info["id"] = "cloudlinux"
+
+ return distro_info
+
+ def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]:
+ """
+ Parse a distro release file.
+
+ Parameters:
+
+ * filepath: Path name of the distro release file.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ try:
+ with open(filepath, encoding="utf-8") as fp:
+ # Only parse the first line. For instance, on SLES there
+ # are multiple lines. We don't want them...
+ return self._parse_distro_release_content(fp.readline())
+ except OSError:
+ # Ignore not being able to read a specific, seemingly version
+ # related file.
+ # See https://github.com/python-distro/distro/issues/162
+ return {}
+
+ @staticmethod
+ def _parse_distro_release_content(line: str) -> Dict[str, str]:
+ """
+ Parse a line from a distro release file.
+
+ Parameters:
+ * line: Line from the distro release file. Must be a unicode string
+ or a UTF-8 encoded byte string.
+
+ Returns:
+ A dictionary containing all information items.
+ """
+ matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1])
+ distro_info = {}
+ if matches:
+ # regexp ensures non-None
+ distro_info["name"] = matches.group(3)[::-1]
+ if matches.group(2):
+ distro_info["version_id"] = matches.group(2)[::-1]
+ if matches.group(1):
+ distro_info["codename"] = matches.group(1)[::-1]
+ elif line:
+ distro_info["name"] = line.strip()
+ return distro_info
+
+
+_distro = LinuxDistribution()
+
+
+def main() -> None:
+ logger = logging.getLogger(__name__)
+ logger.setLevel(logging.DEBUG)
+ logger.addHandler(logging.StreamHandler(sys.stdout))
+
+ parser = argparse.ArgumentParser(description="OS distro info tool")
+ parser.add_argument(
+ "--json", "-j", help="Output in machine readable format", action="store_true"
+ )
+
+ parser.add_argument(
+ "--root-dir",
+ "-r",
+ type=str,
+ dest="root_dir",
+ help="Path to the root filesystem directory (defaults to /)",
+ )
+
+ args = parser.parse_args()
+
+ if args.root_dir:
+ dist = LinuxDistribution(
+ include_lsb=False,
+ include_uname=False,
+ include_oslevel=False,
+ root_dir=args.root_dir,
+ )
+ else:
+ dist = _distro
+
+ if args.json:
+ logger.info(json.dumps(dist.info(), indent=4, sort_keys=True))
+ else:
+ logger.info("Name: %s", dist.name(pretty=True))
+ distribution_version = dist.version(pretty=True)
+ logger.info("Version: %s", distribution_version)
+ distribution_codename = dist.codename()
+ logger.info("Codename: %s", distribution_codename)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/python/distro/py3/distro/py.typed b/contrib/python/distro/py3/distro/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/distro/py3/distro/py.typed
diff --git a/contrib/python/distro/py3/ya.make b/contrib/python/distro/py3/ya.make
new file mode 100644
index 0000000000..465f72ce57
--- /dev/null
+++ b/contrib/python/distro/py3/ya.make
@@ -0,0 +1,34 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(1.8.0)
+
+LICENSE(Apache-2.0)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ distro/__init__.py
+ distro/__main__.py
+ distro/distro.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/distro/py3/
+ .dist-info/METADATA
+ .dist-info/entry_points.txt
+ .dist-info/top_level.txt
+ distro/py.typed
+)
+
+END()
+
+RECURSE(
+ bin
+)
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/distro/ya.make b/contrib/python/distro/ya.make
new file mode 100644
index 0000000000..82a91a17a2
--- /dev/null
+++ b/contrib/python/distro/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/distro/py2)
+ELSE()
+ PEERDIR(contrib/python/distro/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/humanfriendly/py2/LICENSE.txt b/contrib/python/humanfriendly/py2/LICENSE.txt
new file mode 100644
index 0000000000..96ece318ed
--- /dev/null
+++ b/contrib/python/humanfriendly/py2/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2021 Peter Odding
+
+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/contrib/python/humanfriendly/py2/README.rst b/contrib/python/humanfriendly/py2/README.rst
new file mode 100644
index 0000000000..80145d564c
--- /dev/null
+++ b/contrib/python/humanfriendly/py2/README.rst
@@ -0,0 +1,170 @@
+humanfriendly: Human friendly input/output in Python
+====================================================
+
+.. image:: https://github.com/xolox/python-humanfriendly/actions/workflows/test.yml/badge.svg?branch=master
+ :target: https://github.com/xolox/python-humanfriendly/actions
+
+.. image:: https://codecov.io/gh/xolox/python-humanfriendly/branch/master/graph/badge.svg?token=jYaj4T74TU
+ :target: https://codecov.io/gh/xolox/python-humanfriendly
+
+The functions and classes in the `humanfriendly` package can be used to make
+text interfaces more user friendly. Some example features:
+
+- Parsing and formatting numbers, file sizes, pathnames and timespans in
+ simple, human friendly formats.
+
+- Easy to use timers for long running operations, with human friendly
+ formatting of the resulting timespans.
+
+- Prompting the user to select a choice from a list of options by typing the
+ option's number or a unique substring of the option.
+
+- Terminal interaction including text styling (`ANSI escape sequences`_), user
+ friendly rendering of usage messages and querying the terminal for its
+ size.
+
+The `humanfriendly` package is currently tested on Python 2.7, 3.5+ and PyPy
+(2.7) on Linux and macOS. While the intention is to support Windows as well,
+you may encounter some rough edges.
+
+.. contents::
+ :local:
+
+Getting started
+---------------
+
+It's very simple to start using the `humanfriendly` package::
+
+ >>> from humanfriendly import format_size, parse_size
+ >>> from humanfriendly.prompts import prompt_for_input
+ >>> user_input = prompt_for_input("Enter a readable file size: ")
+
+ Enter a readable file size: 16G
+
+ >>> num_bytes = parse_size(user_input)
+ >>> print(num_bytes)
+ 16000000000
+ >>> print("You entered:", format_size(num_bytes))
+ You entered: 16 GB
+ >>> print("You entered:", format_size(num_bytes, binary=True))
+ You entered: 14.9 GiB
+
+To get a demonstration of supported terminal text styles (based on
+`ANSI escape sequences`_) you can run the following command::
+
+ $ humanfriendly --demo
+
+Command line
+------------
+
+.. A DRY solution to avoid duplication of the `humanfriendly --help' text:
+..
+.. [[[cog
+.. from humanfriendly.usage import inject_usage
+.. inject_usage('humanfriendly.cli')
+.. ]]]
+
+**Usage:** `humanfriendly [OPTIONS]`
+
+Human friendly input/output (text formatting) on the command
+line based on the Python package with the same name.
+
+**Supported options:**
+
+.. csv-table::
+ :header: Option, Description
+ :widths: 30, 70
+
+
+ "``-c``, ``--run-command``","Execute an external command (given as the positional arguments) and render
+ a spinner and timer while the command is running. The exit status of the
+ command is propagated."
+ ``--format-table``,"Read tabular data from standard input (each line is a row and each
+ whitespace separated field is a column), format the data as a table and
+ print the resulting table to standard output. See also the ``--delimiter``
+ option."
+ "``-d``, ``--delimiter=VALUE``","Change the delimiter used by ``--format-table`` to ``VALUE`` (a string). By default
+ all whitespace is treated as a delimiter."
+ "``-l``, ``--format-length=LENGTH``","Convert a length count (given as the integer or float ``LENGTH``) into a human
+ readable string and print that string to standard output."
+ "``-n``, ``--format-number=VALUE``","Format a number (given as the integer or floating point number ``VALUE``) with
+ thousands separators and two decimal places (if needed) and print the
+ formatted number to standard output."
+ "``-s``, ``--format-size=BYTES``","Convert a byte count (given as the integer ``BYTES``) into a human readable
+ string and print that string to standard output."
+ "``-b``, ``--binary``","Change the output of ``-s``, ``--format-size`` to use binary multiples of bytes
+ (base-2) instead of the default decimal multiples of bytes (base-10)."
+ "``-t``, ``--format-timespan=SECONDS``","Convert a number of seconds (given as the floating point number ``SECONDS``)
+ into a human readable timespan and print that string to standard output."
+ ``--parse-length=VALUE``,"Parse a human readable length (given as the string ``VALUE``) and print the
+ number of metres to standard output."
+ ``--parse-size=VALUE``,"Parse a human readable data size (given as the string ``VALUE``) and print the
+ number of bytes to standard output."
+ ``--demo``,"Demonstrate changing the style and color of the terminal font using ANSI
+ escape sequences."
+ "``-h``, ``--help``",Show this message and exit.
+
+.. [[[end]]]
+
+A note about size units
+-----------------------
+
+When I originally published the `humanfriendly` package I went with binary
+multiples of bytes (powers of two). It was pointed out several times that this
+was a poor choice (see issue `#4`_ and pull requests `#8`_ and `#9`_) and thus
+the new default became decimal multiples of bytes (powers of ten):
+
++------+---------------+---------------+
+| Unit | Binary value | Decimal value |
++------+---------------+---------------+
+| KB | 1024 | 1000 +
++------+---------------+---------------+
+| MB | 1048576 | 1000000 |
++------+---------------+---------------+
+| GB | 1073741824 | 1000000000 |
++------+---------------+---------------+
+| TB | 1099511627776 | 1000000000000 |
++------+---------------+---------------+
+| etc | | |
++------+---------------+---------------+
+
+The option to use binary multiples of bytes remains by passing the keyword
+argument `binary=True` to the `format_size()`_ and `parse_size()`_ functions.
+
+Windows support
+---------------
+
+Windows 10 gained native support for ANSI escape sequences which means commands
+like ``humanfriendly --demo`` should work out of the box (if your system is
+up-to-date enough). If this doesn't work then you can install the colorama_
+package, it will be used automatically once installed.
+
+Contact
+-------
+
+The latest version of `humanfriendly` is available on PyPI_ and GitHub_. The
+documentation is hosted on `Read the Docs`_ and includes a changelog_. For bug
+reports please create an issue on GitHub_. If you have questions, suggestions,
+etc. feel free to send me an e-mail at `peter@peterodding.com`_.
+
+License
+-------
+
+This software is licensed under the `MIT license`_.
+
+© 2021 Peter Odding.
+
+.. External references:
+.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
+.. _#8: https://github.com/xolox/python-humanfriendly/pull/8
+.. _#9: https://github.com/xolox/python-humanfriendly/pull/9
+.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code
+.. _changelog: https://humanfriendly.readthedocs.io/en/latest/changelog.html
+.. _colorama: https://pypi.org/project/colorama
+.. _format_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.format_size
+.. _GitHub: https://github.com/xolox/python-humanfriendly
+.. _MIT license: https://en.wikipedia.org/wiki/MIT_License
+.. _parse_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.parse_size
+.. _peter@peterodding.com: peter@peterodding.com
+.. _PyPI: https://pypi.org/project/humanfriendly
+.. _Read the Docs: https://humanfriendly.readthedocs.io
diff --git a/contrib/python/humanfriendly/py3/.dist-info/METADATA b/contrib/python/humanfriendly/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..c36fa4cafe
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/.dist-info/METADATA
@@ -0,0 +1,216 @@
+Metadata-Version: 2.1
+Name: humanfriendly
+Version: 10.0
+Summary: Human friendly output for text interfaces using Python
+Home-page: https://humanfriendly.readthedocs.io
+Author: Peter Odding
+Author-email: peter@peterodding.com
+License: MIT
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Environment :: Console
+Classifier: Framework :: Sphinx :: Extension
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Natural Language :: English
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Communications
+Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces
+Classifier: Topic :: Software Development
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: User Interfaces
+Classifier: Topic :: System :: Shells
+Classifier: Topic :: System :: System Shells
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Terminals
+Classifier: Topic :: Text Processing :: General
+Classifier: Topic :: Text Processing :: Linguistic
+Classifier: Topic :: Utilities
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
+Requires-Dist: monotonic ; python_version == "2.7"
+Requires-Dist: pyreadline ; sys_platform == "win32" and python_version<"3.8"
+Requires-Dist: pyreadline3 ; sys_platform == "win32" and python_version>="3.8"
+
+humanfriendly: Human friendly input/output in Python
+====================================================
+
+.. image:: https://github.com/xolox/python-humanfriendly/actions/workflows/test.yml/badge.svg?branch=master
+ :target: https://github.com/xolox/python-humanfriendly/actions
+
+.. image:: https://codecov.io/gh/xolox/python-humanfriendly/branch/master/graph/badge.svg?token=jYaj4T74TU
+ :target: https://codecov.io/gh/xolox/python-humanfriendly
+
+The functions and classes in the `humanfriendly` package can be used to make
+text interfaces more user friendly. Some example features:
+
+- Parsing and formatting numbers, file sizes, pathnames and timespans in
+ simple, human friendly formats.
+
+- Easy to use timers for long running operations, with human friendly
+ formatting of the resulting timespans.
+
+- Prompting the user to select a choice from a list of options by typing the
+ option's number or a unique substring of the option.
+
+- Terminal interaction including text styling (`ANSI escape sequences`_), user
+ friendly rendering of usage messages and querying the terminal for its
+ size.
+
+The `humanfriendly` package is currently tested on Python 2.7, 3.5+ and PyPy
+(2.7) on Linux and macOS. While the intention is to support Windows as well,
+you may encounter some rough edges.
+
+.. contents::
+ :local:
+
+Getting started
+---------------
+
+It's very simple to start using the `humanfriendly` package::
+
+ >>> from humanfriendly import format_size, parse_size
+ >>> from humanfriendly.prompts import prompt_for_input
+ >>> user_input = prompt_for_input("Enter a readable file size: ")
+
+ Enter a readable file size: 16G
+
+ >>> num_bytes = parse_size(user_input)
+ >>> print(num_bytes)
+ 16000000000
+ >>> print("You entered:", format_size(num_bytes))
+ You entered: 16 GB
+ >>> print("You entered:", format_size(num_bytes, binary=True))
+ You entered: 14.9 GiB
+
+To get a demonstration of supported terminal text styles (based on
+`ANSI escape sequences`_) you can run the following command::
+
+ $ humanfriendly --demo
+
+Command line
+------------
+
+.. A DRY solution to avoid duplication of the `humanfriendly --help' text:
+..
+.. [[[cog
+.. from humanfriendly.usage import inject_usage
+.. inject_usage('humanfriendly.cli')
+.. ]]]
+
+**Usage:** `humanfriendly [OPTIONS]`
+
+Human friendly input/output (text formatting) on the command
+line based on the Python package with the same name.
+
+**Supported options:**
+
+.. csv-table::
+ :header: Option, Description
+ :widths: 30, 70
+
+
+ "``-c``, ``--run-command``","Execute an external command (given as the positional arguments) and render
+ a spinner and timer while the command is running. The exit status of the
+ command is propagated."
+ ``--format-table``,"Read tabular data from standard input (each line is a row and each
+ whitespace separated field is a column), format the data as a table and
+ print the resulting table to standard output. See also the ``--delimiter``
+ option."
+ "``-d``, ``--delimiter=VALUE``","Change the delimiter used by ``--format-table`` to ``VALUE`` (a string). By default
+ all whitespace is treated as a delimiter."
+ "``-l``, ``--format-length=LENGTH``","Convert a length count (given as the integer or float ``LENGTH``) into a human
+ readable string and print that string to standard output."
+ "``-n``, ``--format-number=VALUE``","Format a number (given as the integer or floating point number ``VALUE``) with
+ thousands separators and two decimal places (if needed) and print the
+ formatted number to standard output."
+ "``-s``, ``--format-size=BYTES``","Convert a byte count (given as the integer ``BYTES``) into a human readable
+ string and print that string to standard output."
+ "``-b``, ``--binary``","Change the output of ``-s``, ``--format-size`` to use binary multiples of bytes
+ (base-2) instead of the default decimal multiples of bytes (base-10)."
+ "``-t``, ``--format-timespan=SECONDS``","Convert a number of seconds (given as the floating point number ``SECONDS``)
+ into a human readable timespan and print that string to standard output."
+ ``--parse-length=VALUE``,"Parse a human readable length (given as the string ``VALUE``) and print the
+ number of metres to standard output."
+ ``--parse-size=VALUE``,"Parse a human readable data size (given as the string ``VALUE``) and print the
+ number of bytes to standard output."
+ ``--demo``,"Demonstrate changing the style and color of the terminal font using ANSI
+ escape sequences."
+ "``-h``, ``--help``",Show this message and exit.
+
+.. [[[end]]]
+
+A note about size units
+-----------------------
+
+When I originally published the `humanfriendly` package I went with binary
+multiples of bytes (powers of two). It was pointed out several times that this
+was a poor choice (see issue `#4`_ and pull requests `#8`_ and `#9`_) and thus
+the new default became decimal multiples of bytes (powers of ten):
+
++------+---------------+---------------+
+| Unit | Binary value | Decimal value |
++------+---------------+---------------+
+| KB | 1024 | 1000 +
++------+---------------+---------------+
+| MB | 1048576 | 1000000 |
++------+---------------+---------------+
+| GB | 1073741824 | 1000000000 |
++------+---------------+---------------+
+| TB | 1099511627776 | 1000000000000 |
++------+---------------+---------------+
+| etc | | |
++------+---------------+---------------+
+
+The option to use binary multiples of bytes remains by passing the keyword
+argument `binary=True` to the `format_size()`_ and `parse_size()`_ functions.
+
+Windows support
+---------------
+
+Windows 10 gained native support for ANSI escape sequences which means commands
+like ``humanfriendly --demo`` should work out of the box (if your system is
+up-to-date enough). If this doesn't work then you can install the colorama_
+package, it will be used automatically once installed.
+
+Contact
+-------
+
+The latest version of `humanfriendly` is available on PyPI_ and GitHub_. The
+documentation is hosted on `Read the Docs`_ and includes a changelog_. For bug
+reports please create an issue on GitHub_. If you have questions, suggestions,
+etc. feel free to send me an e-mail at `peter@peterodding.com`_.
+
+License
+-------
+
+This software is licensed under the `MIT license`_.
+
+© 2021 Peter Odding.
+
+.. External references:
+.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
+.. _#8: https://github.com/xolox/python-humanfriendly/pull/8
+.. _#9: https://github.com/xolox/python-humanfriendly/pull/9
+.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code
+.. _changelog: https://humanfriendly.readthedocs.io/en/latest/changelog.html
+.. _colorama: https://pypi.org/project/colorama
+.. _format_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.format_size
+.. _GitHub: https://github.com/xolox/python-humanfriendly
+.. _MIT license: https://en.wikipedia.org/wiki/MIT_License
+.. _parse_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.parse_size
+.. _peter@peterodding.com: peter@peterodding.com
+.. _PyPI: https://pypi.org/project/humanfriendly
+.. _Read the Docs: https://humanfriendly.readthedocs.io
+
+
diff --git a/contrib/python/humanfriendly/py3/.dist-info/entry_points.txt b/contrib/python/humanfriendly/py3/.dist-info/entry_points.txt
new file mode 100644
index 0000000000..2ce8fb8353
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/.dist-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+humanfriendly = humanfriendly.cli:main
+
diff --git a/contrib/python/humanfriendly/py3/.dist-info/top_level.txt b/contrib/python/humanfriendly/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..f5368c4974
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+humanfriendly
diff --git a/contrib/python/humanfriendly/py3/LICENSE.txt b/contrib/python/humanfriendly/py3/LICENSE.txt
new file mode 100644
index 0000000000..96ece318ed
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2021 Peter Odding
+
+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/contrib/python/humanfriendly/py3/README.rst b/contrib/python/humanfriendly/py3/README.rst
new file mode 100644
index 0000000000..80145d564c
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/README.rst
@@ -0,0 +1,170 @@
+humanfriendly: Human friendly input/output in Python
+====================================================
+
+.. image:: https://github.com/xolox/python-humanfriendly/actions/workflows/test.yml/badge.svg?branch=master
+ :target: https://github.com/xolox/python-humanfriendly/actions
+
+.. image:: https://codecov.io/gh/xolox/python-humanfriendly/branch/master/graph/badge.svg?token=jYaj4T74TU
+ :target: https://codecov.io/gh/xolox/python-humanfriendly
+
+The functions and classes in the `humanfriendly` package can be used to make
+text interfaces more user friendly. Some example features:
+
+- Parsing and formatting numbers, file sizes, pathnames and timespans in
+ simple, human friendly formats.
+
+- Easy to use timers for long running operations, with human friendly
+ formatting of the resulting timespans.
+
+- Prompting the user to select a choice from a list of options by typing the
+ option's number or a unique substring of the option.
+
+- Terminal interaction including text styling (`ANSI escape sequences`_), user
+ friendly rendering of usage messages and querying the terminal for its
+ size.
+
+The `humanfriendly` package is currently tested on Python 2.7, 3.5+ and PyPy
+(2.7) on Linux and macOS. While the intention is to support Windows as well,
+you may encounter some rough edges.
+
+.. contents::
+ :local:
+
+Getting started
+---------------
+
+It's very simple to start using the `humanfriendly` package::
+
+ >>> from humanfriendly import format_size, parse_size
+ >>> from humanfriendly.prompts import prompt_for_input
+ >>> user_input = prompt_for_input("Enter a readable file size: ")
+
+ Enter a readable file size: 16G
+
+ >>> num_bytes = parse_size(user_input)
+ >>> print(num_bytes)
+ 16000000000
+ >>> print("You entered:", format_size(num_bytes))
+ You entered: 16 GB
+ >>> print("You entered:", format_size(num_bytes, binary=True))
+ You entered: 14.9 GiB
+
+To get a demonstration of supported terminal text styles (based on
+`ANSI escape sequences`_) you can run the following command::
+
+ $ humanfriendly --demo
+
+Command line
+------------
+
+.. A DRY solution to avoid duplication of the `humanfriendly --help' text:
+..
+.. [[[cog
+.. from humanfriendly.usage import inject_usage
+.. inject_usage('humanfriendly.cli')
+.. ]]]
+
+**Usage:** `humanfriendly [OPTIONS]`
+
+Human friendly input/output (text formatting) on the command
+line based on the Python package with the same name.
+
+**Supported options:**
+
+.. csv-table::
+ :header: Option, Description
+ :widths: 30, 70
+
+
+ "``-c``, ``--run-command``","Execute an external command (given as the positional arguments) and render
+ a spinner and timer while the command is running. The exit status of the
+ command is propagated."
+ ``--format-table``,"Read tabular data from standard input (each line is a row and each
+ whitespace separated field is a column), format the data as a table and
+ print the resulting table to standard output. See also the ``--delimiter``
+ option."
+ "``-d``, ``--delimiter=VALUE``","Change the delimiter used by ``--format-table`` to ``VALUE`` (a string). By default
+ all whitespace is treated as a delimiter."
+ "``-l``, ``--format-length=LENGTH``","Convert a length count (given as the integer or float ``LENGTH``) into a human
+ readable string and print that string to standard output."
+ "``-n``, ``--format-number=VALUE``","Format a number (given as the integer or floating point number ``VALUE``) with
+ thousands separators and two decimal places (if needed) and print the
+ formatted number to standard output."
+ "``-s``, ``--format-size=BYTES``","Convert a byte count (given as the integer ``BYTES``) into a human readable
+ string and print that string to standard output."
+ "``-b``, ``--binary``","Change the output of ``-s``, ``--format-size`` to use binary multiples of bytes
+ (base-2) instead of the default decimal multiples of bytes (base-10)."
+ "``-t``, ``--format-timespan=SECONDS``","Convert a number of seconds (given as the floating point number ``SECONDS``)
+ into a human readable timespan and print that string to standard output."
+ ``--parse-length=VALUE``,"Parse a human readable length (given as the string ``VALUE``) and print the
+ number of metres to standard output."
+ ``--parse-size=VALUE``,"Parse a human readable data size (given as the string ``VALUE``) and print the
+ number of bytes to standard output."
+ ``--demo``,"Demonstrate changing the style and color of the terminal font using ANSI
+ escape sequences."
+ "``-h``, ``--help``",Show this message and exit.
+
+.. [[[end]]]
+
+A note about size units
+-----------------------
+
+When I originally published the `humanfriendly` package I went with binary
+multiples of bytes (powers of two). It was pointed out several times that this
+was a poor choice (see issue `#4`_ and pull requests `#8`_ and `#9`_) and thus
+the new default became decimal multiples of bytes (powers of ten):
+
++------+---------------+---------------+
+| Unit | Binary value | Decimal value |
++------+---------------+---------------+
+| KB | 1024 | 1000 +
++------+---------------+---------------+
+| MB | 1048576 | 1000000 |
++------+---------------+---------------+
+| GB | 1073741824 | 1000000000 |
++------+---------------+---------------+
+| TB | 1099511627776 | 1000000000000 |
++------+---------------+---------------+
+| etc | | |
++------+---------------+---------------+
+
+The option to use binary multiples of bytes remains by passing the keyword
+argument `binary=True` to the `format_size()`_ and `parse_size()`_ functions.
+
+Windows support
+---------------
+
+Windows 10 gained native support for ANSI escape sequences which means commands
+like ``humanfriendly --demo`` should work out of the box (if your system is
+up-to-date enough). If this doesn't work then you can install the colorama_
+package, it will be used automatically once installed.
+
+Contact
+-------
+
+The latest version of `humanfriendly` is available on PyPI_ and GitHub_. The
+documentation is hosted on `Read the Docs`_ and includes a changelog_. For bug
+reports please create an issue on GitHub_. If you have questions, suggestions,
+etc. feel free to send me an e-mail at `peter@peterodding.com`_.
+
+License
+-------
+
+This software is licensed under the `MIT license`_.
+
+© 2021 Peter Odding.
+
+.. External references:
+.. _#4: https://github.com/xolox/python-humanfriendly/issues/4
+.. _#8: https://github.com/xolox/python-humanfriendly/pull/8
+.. _#9: https://github.com/xolox/python-humanfriendly/pull/9
+.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code
+.. _changelog: https://humanfriendly.readthedocs.io/en/latest/changelog.html
+.. _colorama: https://pypi.org/project/colorama
+.. _format_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.format_size
+.. _GitHub: https://github.com/xolox/python-humanfriendly
+.. _MIT license: https://en.wikipedia.org/wiki/MIT_License
+.. _parse_size(): https://humanfriendly.readthedocs.io/en/latest/#humanfriendly.parse_size
+.. _peter@peterodding.com: peter@peterodding.com
+.. _PyPI: https://pypi.org/project/humanfriendly
+.. _Read the Docs: https://humanfriendly.readthedocs.io
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/__init__.py b/contrib/python/humanfriendly/py3/humanfriendly/__init__.py
new file mode 100644
index 0000000000..4c0a333861
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/__init__.py
@@ -0,0 +1,838 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: September 17, 2021
+# URL: https://humanfriendly.readthedocs.io
+
+"""The main module of the `humanfriendly` package."""
+
+# Standard library modules.
+import collections
+import datetime
+import decimal
+import numbers
+import os
+import os.path
+import re
+import time
+
+# Modules included in our package.
+from humanfriendly.compat import is_string, monotonic
+from humanfriendly.deprecation import define_aliases
+from humanfriendly.text import concatenate, format, pluralize, tokenize
+
+# Public identifiers that require documentation.
+__all__ = (
+ 'CombinedUnit',
+ 'InvalidDate',
+ 'InvalidLength',
+ 'InvalidSize',
+ 'InvalidTimespan',
+ 'SizeUnit',
+ 'Timer',
+ '__version__',
+ 'coerce_boolean',
+ 'coerce_pattern',
+ 'coerce_seconds',
+ 'disk_size_units',
+ 'format_length',
+ 'format_number',
+ 'format_path',
+ 'format_size',
+ 'format_timespan',
+ 'length_size_units',
+ 'parse_date',
+ 'parse_length',
+ 'parse_path',
+ 'parse_size',
+ 'parse_timespan',
+ 'round_number',
+ 'time_units',
+)
+
+# Semi-standard module versioning.
+__version__ = '10.0'
+
+# Named tuples to define units of size.
+SizeUnit = collections.namedtuple('SizeUnit', 'divider, symbol, name')
+CombinedUnit = collections.namedtuple('CombinedUnit', 'decimal, binary')
+
+# Common disk size units in binary (base-2) and decimal (base-10) multiples.
+disk_size_units = (
+ CombinedUnit(SizeUnit(1000**1, 'KB', 'kilobyte'), SizeUnit(1024**1, 'KiB', 'kibibyte')),
+ CombinedUnit(SizeUnit(1000**2, 'MB', 'megabyte'), SizeUnit(1024**2, 'MiB', 'mebibyte')),
+ CombinedUnit(SizeUnit(1000**3, 'GB', 'gigabyte'), SizeUnit(1024**3, 'GiB', 'gibibyte')),
+ CombinedUnit(SizeUnit(1000**4, 'TB', 'terabyte'), SizeUnit(1024**4, 'TiB', 'tebibyte')),
+ CombinedUnit(SizeUnit(1000**5, 'PB', 'petabyte'), SizeUnit(1024**5, 'PiB', 'pebibyte')),
+ CombinedUnit(SizeUnit(1000**6, 'EB', 'exabyte'), SizeUnit(1024**6, 'EiB', 'exbibyte')),
+ CombinedUnit(SizeUnit(1000**7, 'ZB', 'zettabyte'), SizeUnit(1024**7, 'ZiB', 'zebibyte')),
+ CombinedUnit(SizeUnit(1000**8, 'YB', 'yottabyte'), SizeUnit(1024**8, 'YiB', 'yobibyte')),
+)
+
+# Common length size units, used for formatting and parsing.
+length_size_units = (dict(prefix='nm', divider=1e-09, singular='nm', plural='nm'),
+ dict(prefix='mm', divider=1e-03, singular='mm', plural='mm'),
+ dict(prefix='cm', divider=1e-02, singular='cm', plural='cm'),
+ dict(prefix='m', divider=1, singular='metre', plural='metres'),
+ dict(prefix='km', divider=1000, singular='km', plural='km'))
+
+# Common time units, used for formatting of time spans.
+time_units = (dict(divider=1e-9, singular='nanosecond', plural='nanoseconds', abbreviations=['ns']),
+ dict(divider=1e-6, singular='microsecond', plural='microseconds', abbreviations=['us']),
+ dict(divider=1e-3, singular='millisecond', plural='milliseconds', abbreviations=['ms']),
+ dict(divider=1, singular='second', plural='seconds', abbreviations=['s', 'sec', 'secs']),
+ dict(divider=60, singular='minute', plural='minutes', abbreviations=['m', 'min', 'mins']),
+ dict(divider=60 * 60, singular='hour', plural='hours', abbreviations=['h']),
+ dict(divider=60 * 60 * 24, singular='day', plural='days', abbreviations=['d']),
+ dict(divider=60 * 60 * 24 * 7, singular='week', plural='weeks', abbreviations=['w']),
+ dict(divider=60 * 60 * 24 * 7 * 52, singular='year', plural='years', abbreviations=['y']))
+
+
+def coerce_boolean(value):
+ """
+ Coerce any value to a boolean.
+
+ :param value: Any Python value. If the value is a string:
+
+ - The strings '1', 'yes', 'true' and 'on' are coerced to :data:`True`.
+ - The strings '0', 'no', 'false' and 'off' are coerced to :data:`False`.
+ - Other strings raise an exception.
+
+ Other Python values are coerced using :class:`bool`.
+ :returns: A proper boolean value.
+ :raises: :exc:`exceptions.ValueError` when the value is a string but
+ cannot be coerced with certainty.
+ """
+ if is_string(value):
+ normalized = value.strip().lower()
+ if normalized in ('1', 'yes', 'true', 'on'):
+ return True
+ elif normalized in ('0', 'no', 'false', 'off', ''):
+ return False
+ else:
+ msg = "Failed to coerce string to boolean! (%r)"
+ raise ValueError(format(msg, value))
+ else:
+ return bool(value)
+
+
+def coerce_pattern(value, flags=0):
+ """
+ Coerce strings to compiled regular expressions.
+
+ :param value: A string containing a regular expression pattern
+ or a compiled regular expression.
+ :param flags: The flags used to compile the pattern (an integer).
+ :returns: A compiled regular expression.
+ :raises: :exc:`~exceptions.ValueError` when `value` isn't a string
+ and also isn't a compiled regular expression.
+ """
+ if is_string(value):
+ value = re.compile(value, flags)
+ else:
+ empty_pattern = re.compile('')
+ pattern_type = type(empty_pattern)
+ if not isinstance(value, pattern_type):
+ msg = "Failed to coerce value to compiled regular expression! (%r)"
+ raise ValueError(format(msg, value))
+ return value
+
+
+def coerce_seconds(value):
+ """
+ Coerce a value to the number of seconds.
+
+ :param value: An :class:`int`, :class:`float` or
+ :class:`datetime.timedelta` object.
+ :returns: An :class:`int` or :class:`float` value.
+
+ When `value` is a :class:`datetime.timedelta` object the
+ :meth:`~datetime.timedelta.total_seconds()` method is called.
+ """
+ if isinstance(value, datetime.timedelta):
+ return value.total_seconds()
+ if not isinstance(value, numbers.Number):
+ msg = "Failed to coerce value to number of seconds! (%r)"
+ raise ValueError(format(msg, value))
+ return value
+
+
+def format_size(num_bytes, keep_width=False, binary=False):
+ """
+ Format a byte count as a human readable file size.
+
+ :param num_bytes: The size to format in bytes (an integer).
+ :param keep_width: :data:`True` if trailing zeros should not be stripped,
+ :data:`False` if they can be stripped.
+ :param binary: :data:`True` to use binary multiples of bytes (base-2),
+ :data:`False` to use decimal multiples of bytes (base-10).
+ :returns: The corresponding human readable file size (a string).
+
+ This function knows how to format sizes in bytes, kilobytes, megabytes,
+ gigabytes, terabytes and petabytes. Some examples:
+
+ >>> from humanfriendly import format_size
+ >>> format_size(0)
+ '0 bytes'
+ >>> format_size(1)
+ '1 byte'
+ >>> format_size(5)
+ '5 bytes'
+ > format_size(1000)
+ '1 KB'
+ > format_size(1024, binary=True)
+ '1 KiB'
+ >>> format_size(1000 ** 3 * 4)
+ '4 GB'
+ """
+ for unit in reversed(disk_size_units):
+ if num_bytes >= unit.binary.divider and binary:
+ number = round_number(float(num_bytes) / unit.binary.divider, keep_width=keep_width)
+ return pluralize(number, unit.binary.symbol, unit.binary.symbol)
+ elif num_bytes >= unit.decimal.divider and not binary:
+ number = round_number(float(num_bytes) / unit.decimal.divider, keep_width=keep_width)
+ return pluralize(number, unit.decimal.symbol, unit.decimal.symbol)
+ return pluralize(num_bytes, 'byte')
+
+
+def parse_size(size, binary=False):
+ """
+ Parse a human readable data size and return the number of bytes.
+
+ :param size: The human readable file size to parse (a string).
+ :param binary: :data:`True` to use binary multiples of bytes (base-2) for
+ ambiguous unit symbols and names, :data:`False` to use
+ decimal multiples of bytes (base-10).
+ :returns: The corresponding size in bytes (an integer).
+ :raises: :exc:`InvalidSize` when the input can't be parsed.
+
+ This function knows how to parse sizes in bytes, kilobytes, megabytes,
+ gigabytes, terabytes and petabytes. Some examples:
+
+ >>> from humanfriendly import parse_size
+ >>> parse_size('42')
+ 42
+ >>> parse_size('13b')
+ 13
+ >>> parse_size('5 bytes')
+ 5
+ >>> parse_size('1 KB')
+ 1000
+ >>> parse_size('1 kilobyte')
+ 1000
+ >>> parse_size('1 KiB')
+ 1024
+ >>> parse_size('1 KB', binary=True)
+ 1024
+ >>> parse_size('1.5 GB')
+ 1500000000
+ >>> parse_size('1.5 GB', binary=True)
+ 1610612736
+ """
+ tokens = tokenize(size)
+ if tokens and isinstance(tokens[0], numbers.Number):
+ # Get the normalized unit (if any) from the tokenized input.
+ normalized_unit = tokens[1].lower() if len(tokens) == 2 and is_string(tokens[1]) else ''
+ # If the input contains only a number, it's assumed to be the number of
+ # bytes. The second token can also explicitly reference the unit bytes.
+ if len(tokens) == 1 or normalized_unit.startswith('b'):
+ return int(tokens[0])
+ # Otherwise we expect two tokens: A number and a unit.
+ if normalized_unit:
+ # Convert plural units to singular units, for details:
+ # https://github.com/xolox/python-humanfriendly/issues/26
+ normalized_unit = normalized_unit.rstrip('s')
+ for unit in disk_size_units:
+ # First we check for unambiguous symbols (KiB, MiB, GiB, etc)
+ # and names (kibibyte, mebibyte, gibibyte, etc) because their
+ # handling is always the same.
+ if normalized_unit in (unit.binary.symbol.lower(), unit.binary.name.lower()):
+ return int(tokens[0] * unit.binary.divider)
+ # Now we will deal with ambiguous prefixes (K, M, G, etc),
+ # symbols (KB, MB, GB, etc) and names (kilobyte, megabyte,
+ # gigabyte, etc) according to the caller's preference.
+ if (normalized_unit in (unit.decimal.symbol.lower(), unit.decimal.name.lower()) or
+ normalized_unit.startswith(unit.decimal.symbol[0].lower())):
+ return int(tokens[0] * (unit.binary.divider if binary else unit.decimal.divider))
+ # We failed to parse the size specification.
+ msg = "Failed to parse size! (input %r was tokenized as %r)"
+ raise InvalidSize(format(msg, size, tokens))
+
+
+def format_length(num_metres, keep_width=False):
+ """
+ Format a metre count as a human readable length.
+
+ :param num_metres: The length to format in metres (float / integer).
+ :param keep_width: :data:`True` if trailing zeros should not be stripped,
+ :data:`False` if they can be stripped.
+ :returns: The corresponding human readable length (a string).
+
+ This function supports ranges from nanometres to kilometres.
+
+ Some examples:
+
+ >>> from humanfriendly import format_length
+ >>> format_length(0)
+ '0 metres'
+ >>> format_length(1)
+ '1 metre'
+ >>> format_length(5)
+ '5 metres'
+ >>> format_length(1000)
+ '1 km'
+ >>> format_length(0.004)
+ '4 mm'
+ """
+ for unit in reversed(length_size_units):
+ if num_metres >= unit['divider']:
+ number = round_number(float(num_metres) / unit['divider'], keep_width=keep_width)
+ return pluralize(number, unit['singular'], unit['plural'])
+ return pluralize(num_metres, 'metre')
+
+
+def parse_length(length):
+ """
+ Parse a human readable length and return the number of metres.
+
+ :param length: The human readable length to parse (a string).
+ :returns: The corresponding length in metres (a float).
+ :raises: :exc:`InvalidLength` when the input can't be parsed.
+
+ Some examples:
+
+ >>> from humanfriendly import parse_length
+ >>> parse_length('42')
+ 42
+ >>> parse_length('1 km')
+ 1000
+ >>> parse_length('5mm')
+ 0.005
+ >>> parse_length('15.3cm')
+ 0.153
+ """
+ tokens = tokenize(length)
+ if tokens and isinstance(tokens[0], numbers.Number):
+ # If the input contains only a number, it's assumed to be the number of metres.
+ if len(tokens) == 1:
+ return tokens[0]
+ # Otherwise we expect to find two tokens: A number and a unit.
+ if len(tokens) == 2 and is_string(tokens[1]):
+ normalized_unit = tokens[1].lower()
+ # Try to match the first letter of the unit.
+ for unit in length_size_units:
+ if normalized_unit.startswith(unit['prefix']):
+ return tokens[0] * unit['divider']
+ # We failed to parse the length specification.
+ msg = "Failed to parse length! (input %r was tokenized as %r)"
+ raise InvalidLength(format(msg, length, tokens))
+
+
+def format_number(number, num_decimals=2):
+ """
+ Format a number as a string including thousands separators.
+
+ :param number: The number to format (a number like an :class:`int`,
+ :class:`long` or :class:`float`).
+ :param num_decimals: The number of decimals to render (2 by default). If no
+ decimal places are required to represent the number
+ they will be omitted regardless of this argument.
+ :returns: The formatted number (a string).
+
+ This function is intended to make it easier to recognize the order of size
+ of the number being formatted.
+
+ Here's an example:
+
+ >>> from humanfriendly import format_number
+ >>> print(format_number(6000000))
+ 6,000,000
+ > print(format_number(6000000000.42))
+ 6,000,000,000.42
+ > print(format_number(6000000000.42, num_decimals=0))
+ 6,000,000,000
+ """
+ integer_part, _, decimal_part = str(float(number)).partition('.')
+ negative_sign = integer_part.startswith('-')
+ reversed_digits = ''.join(reversed(integer_part.lstrip('-')))
+ parts = []
+ while reversed_digits:
+ parts.append(reversed_digits[:3])
+ reversed_digits = reversed_digits[3:]
+ formatted_number = ''.join(reversed(','.join(parts)))
+ decimals_to_add = decimal_part[:num_decimals].rstrip('0')
+ if decimals_to_add:
+ formatted_number += '.' + decimals_to_add
+ if negative_sign:
+ formatted_number = '-' + formatted_number
+ return formatted_number
+
+
+def round_number(count, keep_width=False):
+ """
+ Round a floating point number to two decimal places in a human friendly format.
+
+ :param count: The number to format.
+ :param keep_width: :data:`True` if trailing zeros should not be stripped,
+ :data:`False` if they can be stripped.
+ :returns: The formatted number as a string. If no decimal places are
+ required to represent the number, they will be omitted.
+
+ The main purpose of this function is to be used by functions like
+ :func:`format_length()`, :func:`format_size()` and
+ :func:`format_timespan()`.
+
+ Here are some examples:
+
+ >>> from humanfriendly import round_number
+ >>> round_number(1)
+ '1'
+ >>> round_number(math.pi)
+ '3.14'
+ >>> round_number(5.001)
+ '5'
+ """
+ text = '%.2f' % float(count)
+ if not keep_width:
+ text = re.sub('0+$', '', text)
+ text = re.sub(r'\.$', '', text)
+ return text
+
+
+def format_timespan(num_seconds, detailed=False, max_units=3):
+ """
+ Format a timespan in seconds as a human readable string.
+
+ :param num_seconds: Any value accepted by :func:`coerce_seconds()`.
+ :param detailed: If :data:`True` milliseconds are represented separately
+ instead of being represented as fractional seconds
+ (defaults to :data:`False`).
+ :param max_units: The maximum number of units to show in the formatted time
+ span (an integer, defaults to three).
+ :returns: The formatted timespan as a string.
+ :raise: See :func:`coerce_seconds()`.
+
+ Some examples:
+
+ >>> from humanfriendly import format_timespan
+ >>> format_timespan(0)
+ '0 seconds'
+ >>> format_timespan(1)
+ '1 second'
+ >>> import math
+ >>> format_timespan(math.pi)
+ '3.14 seconds'
+ >>> hour = 60 * 60
+ >>> day = hour * 24
+ >>> week = day * 7
+ >>> format_timespan(week * 52 + day * 2 + hour * 3)
+ '1 year, 2 days and 3 hours'
+ """
+ num_seconds = coerce_seconds(num_seconds)
+ if num_seconds < 60 and not detailed:
+ # Fast path.
+ return pluralize(round_number(num_seconds), 'second')
+ else:
+ # Slow path.
+ result = []
+ num_seconds = decimal.Decimal(str(num_seconds))
+ relevant_units = list(reversed(time_units[0 if detailed else 3:]))
+ for unit in relevant_units:
+ # Extract the unit count from the remaining time.
+ divider = decimal.Decimal(str(unit['divider']))
+ count = num_seconds / divider
+ num_seconds %= divider
+ # Round the unit count appropriately.
+ if unit != relevant_units[-1]:
+ # Integer rounding for all but the smallest unit.
+ count = int(count)
+ else:
+ # Floating point rounding for the smallest unit.
+ count = round_number(count)
+ # Only include relevant units in the result.
+ if count not in (0, '0'):
+ result.append(pluralize(count, unit['singular'], unit['plural']))
+ if len(result) == 1:
+ # A single count/unit combination.
+ return result[0]
+ else:
+ if not detailed:
+ # Remove `insignificant' data from the formatted timespan.
+ result = result[:max_units]
+ # Format the timespan in a readable way.
+ return concatenate(result)
+
+
+def parse_timespan(timespan):
+ """
+ Parse a "human friendly" timespan into the number of seconds.
+
+ :param value: A string like ``5h`` (5 hours), ``10m`` (10 minutes) or
+ ``42s`` (42 seconds).
+ :returns: The number of seconds as a floating point number.
+ :raises: :exc:`InvalidTimespan` when the input can't be parsed.
+
+ Note that the :func:`parse_timespan()` function is not meant to be the
+ "mirror image" of the :func:`format_timespan()` function. Instead it's
+ meant to allow humans to easily and succinctly specify a timespan with a
+ minimal amount of typing. It's very useful to accept easy to write time
+ spans as e.g. command line arguments to programs.
+
+ The time units (and abbreviations) supported by this function are:
+
+ - ms, millisecond, milliseconds
+ - s, sec, secs, second, seconds
+ - m, min, mins, minute, minutes
+ - h, hour, hours
+ - d, day, days
+ - w, week, weeks
+ - y, year, years
+
+ Some examples:
+
+ >>> from humanfriendly import parse_timespan
+ >>> parse_timespan('42')
+ 42.0
+ >>> parse_timespan('42s')
+ 42.0
+ >>> parse_timespan('1m')
+ 60.0
+ >>> parse_timespan('1h')
+ 3600.0
+ >>> parse_timespan('1d')
+ 86400.0
+ """
+ tokens = tokenize(timespan)
+ if tokens and isinstance(tokens[0], numbers.Number):
+ # If the input contains only a number, it's assumed to be the number of seconds.
+ if len(tokens) == 1:
+ return float(tokens[0])
+ # Otherwise we expect to find two tokens: A number and a unit.
+ if len(tokens) == 2 and is_string(tokens[1]):
+ normalized_unit = tokens[1].lower()
+ for unit in time_units:
+ if (normalized_unit == unit['singular'] or
+ normalized_unit == unit['plural'] or
+ normalized_unit in unit['abbreviations']):
+ return float(tokens[0]) * unit['divider']
+ # We failed to parse the timespan specification.
+ msg = "Failed to parse timespan! (input %r was tokenized as %r)"
+ raise InvalidTimespan(format(msg, timespan, tokens))
+
+
+def parse_date(datestring):
+ """
+ Parse a date/time string into a tuple of integers.
+
+ :param datestring: The date/time string to parse.
+ :returns: A tuple with the numbers ``(year, month, day, hour, minute,
+ second)`` (all numbers are integers).
+ :raises: :exc:`InvalidDate` when the date cannot be parsed.
+
+ Supported date/time formats:
+
+ - ``YYYY-MM-DD``
+ - ``YYYY-MM-DD HH:MM:SS``
+
+ .. note:: If you want to parse date/time strings with a fixed, known
+ format and :func:`parse_date()` isn't useful to you, consider
+ :func:`time.strptime()` or :meth:`datetime.datetime.strptime()`,
+ both of which are included in the Python standard library.
+ Alternatively for more complex tasks consider using the date/time
+ parsing module in the dateutil_ package.
+
+ Examples:
+
+ >>> from humanfriendly import parse_date
+ >>> parse_date('2013-06-17')
+ (2013, 6, 17, 0, 0, 0)
+ >>> parse_date('2013-06-17 02:47:42')
+ (2013, 6, 17, 2, 47, 42)
+
+ Here's how you convert the result to a number (`Unix time`_):
+
+ >>> from humanfriendly import parse_date
+ >>> from time import mktime
+ >>> mktime(parse_date('2013-06-17 02:47:42') + (-1, -1, -1))
+ 1371430062.0
+
+ And here's how you convert it to a :class:`datetime.datetime` object:
+
+ >>> from humanfriendly import parse_date
+ >>> from datetime import datetime
+ >>> datetime(*parse_date('2013-06-17 02:47:42'))
+ datetime.datetime(2013, 6, 17, 2, 47, 42)
+
+ Here's an example that combines :func:`format_timespan()` and
+ :func:`parse_date()` to calculate a human friendly timespan since a
+ given date:
+
+ >>> from humanfriendly import format_timespan, parse_date
+ >>> from time import mktime, time
+ >>> unix_time = mktime(parse_date('2013-06-17 02:47:42') + (-1, -1, -1))
+ >>> seconds_since_then = time() - unix_time
+ >>> print(format_timespan(seconds_since_then))
+ 1 year, 43 weeks and 1 day
+
+ .. _dateutil: https://dateutil.readthedocs.io/en/latest/parser.html
+ .. _Unix time: http://en.wikipedia.org/wiki/Unix_time
+ """
+ try:
+ tokens = [t.strip() for t in datestring.split()]
+ if len(tokens) >= 2:
+ date_parts = list(map(int, tokens[0].split('-'))) + [1, 1]
+ time_parts = list(map(int, tokens[1].split(':'))) + [0, 0, 0]
+ return tuple(date_parts[0:3] + time_parts[0:3])
+ else:
+ year, month, day = (list(map(int, datestring.split('-'))) + [1, 1])[0:3]
+ return (year, month, day, 0, 0, 0)
+ except Exception:
+ msg = "Invalid date! (expected 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' but got: %r)"
+ raise InvalidDate(format(msg, datestring))
+
+
+def format_path(pathname):
+ """
+ Shorten a pathname to make it more human friendly.
+
+ :param pathname: An absolute pathname (a string).
+ :returns: The pathname with the user's home directory abbreviated.
+
+ Given an absolute pathname, this function abbreviates the user's home
+ directory to ``~/`` in order to shorten the pathname without losing
+ information. It is not an error if the pathname is not relative to the
+ current user's home directory.
+
+ Here's an example of its usage:
+
+ >>> from os import environ
+ >>> from os.path import join
+ >>> vimrc = join(environ['HOME'], '.vimrc')
+ >>> vimrc
+ '/home/peter/.vimrc'
+ >>> from humanfriendly import format_path
+ >>> format_path(vimrc)
+ '~/.vimrc'
+ """
+ pathname = os.path.abspath(pathname)
+ home = os.environ.get('HOME')
+ if home:
+ home = os.path.abspath(home)
+ if pathname.startswith(home):
+ pathname = os.path.join('~', os.path.relpath(pathname, home))
+ return pathname
+
+
+def parse_path(pathname):
+ """
+ Convert a human friendly pathname to an absolute pathname.
+
+ Expands leading tildes using :func:`os.path.expanduser()` and
+ environment variables using :func:`os.path.expandvars()` and makes the
+ resulting pathname absolute using :func:`os.path.abspath()`.
+
+ :param pathname: A human friendly pathname (a string).
+ :returns: An absolute pathname (a string).
+ """
+ return os.path.abspath(os.path.expanduser(os.path.expandvars(pathname)))
+
+
+class Timer(object):
+
+ """
+ Easy to use timer to keep track of long during operations.
+ """
+
+ def __init__(self, start_time=None, resumable=False):
+ """
+ Remember the time when the :class:`Timer` was created.
+
+ :param start_time: The start time (a float, defaults to the current time).
+ :param resumable: Create a resumable timer (defaults to :data:`False`).
+
+ When `start_time` is given :class:`Timer` uses :func:`time.time()` as a
+ clock source, otherwise it uses :func:`humanfriendly.compat.monotonic()`.
+ """
+ if resumable:
+ self.monotonic = True
+ self.resumable = True
+ self.start_time = 0.0
+ self.total_time = 0.0
+ elif start_time:
+ self.monotonic = False
+ self.resumable = False
+ self.start_time = start_time
+ else:
+ self.monotonic = True
+ self.resumable = False
+ self.start_time = monotonic()
+
+ def __enter__(self):
+ """
+ Start or resume counting elapsed time.
+
+ :returns: The :class:`Timer` object.
+ :raises: :exc:`~exceptions.ValueError` when the timer isn't resumable.
+ """
+ if not self.resumable:
+ raise ValueError("Timer is not resumable!")
+ self.start_time = monotonic()
+ return self
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """
+ Stop counting elapsed time.
+
+ :raises: :exc:`~exceptions.ValueError` when the timer isn't resumable.
+ """
+ if not self.resumable:
+ raise ValueError("Timer is not resumable!")
+ if self.start_time:
+ self.total_time += monotonic() - self.start_time
+ self.start_time = 0.0
+
+ def sleep(self, seconds):
+ """
+ Easy to use rate limiting of repeating actions.
+
+ :param seconds: The number of seconds to sleep (an
+ integer or floating point number).
+
+ This method sleeps for the given number of seconds minus the
+ :attr:`elapsed_time`. If the resulting duration is negative
+ :func:`time.sleep()` will still be called, but the argument
+ given to it will be the number 0 (negative numbers cause
+ :func:`time.sleep()` to raise an exception).
+
+ The use case for this is to initialize a :class:`Timer` inside
+ the body of a :keyword:`for` or :keyword:`while` loop and call
+ :func:`Timer.sleep()` at the end of the loop body to rate limit
+ whatever it is that is being done inside the loop body.
+
+ For posterity: Although the implementation of :func:`sleep()` only
+ requires a single line of code I've added it to :mod:`humanfriendly`
+ anyway because now that I've thought about how to tackle this once I
+ never want to have to think about it again :-P (unless I find ways to
+ improve this).
+ """
+ time.sleep(max(0, seconds - self.elapsed_time))
+
+ @property
+ def elapsed_time(self):
+ """
+ Get the number of seconds counted so far.
+ """
+ elapsed_time = 0
+ if self.resumable:
+ elapsed_time += self.total_time
+ if self.start_time:
+ current_time = monotonic() if self.monotonic else time.time()
+ elapsed_time += current_time - self.start_time
+ return elapsed_time
+
+ @property
+ def rounded(self):
+ """Human readable timespan rounded to seconds (a string)."""
+ return format_timespan(round(self.elapsed_time))
+
+ def __str__(self):
+ """Show the elapsed time since the :class:`Timer` was created."""
+ return format_timespan(self.elapsed_time)
+
+
+class InvalidDate(Exception):
+
+ """
+ Raised when a string cannot be parsed into a date.
+
+ For example:
+
+ >>> from humanfriendly import parse_date
+ >>> parse_date('2013-06-XY')
+ Traceback (most recent call last):
+ File "humanfriendly.py", line 206, in parse_date
+ raise InvalidDate(format(msg, datestring))
+ humanfriendly.InvalidDate: Invalid date! (expected 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' but got: '2013-06-XY')
+ """
+
+
+class InvalidSize(Exception):
+
+ """
+ Raised when a string cannot be parsed into a file size.
+
+ For example:
+
+ >>> from humanfriendly import parse_size
+ >>> parse_size('5 Z')
+ Traceback (most recent call last):
+ File "humanfriendly/__init__.py", line 267, in parse_size
+ raise InvalidSize(format(msg, size, tokens))
+ humanfriendly.InvalidSize: Failed to parse size! (input '5 Z' was tokenized as [5, 'Z'])
+ """
+
+
+class InvalidLength(Exception):
+
+ """
+ Raised when a string cannot be parsed into a length.
+
+ For example:
+
+ >>> from humanfriendly import parse_length
+ >>> parse_length('5 Z')
+ Traceback (most recent call last):
+ File "humanfriendly/__init__.py", line 267, in parse_length
+ raise InvalidLength(format(msg, length, tokens))
+ humanfriendly.InvalidLength: Failed to parse length! (input '5 Z' was tokenized as [5, 'Z'])
+ """
+
+
+class InvalidTimespan(Exception):
+
+ """
+ Raised when a string cannot be parsed into a timespan.
+
+ For example:
+
+ >>> from humanfriendly import parse_timespan
+ >>> parse_timespan('1 age')
+ Traceback (most recent call last):
+ File "humanfriendly/__init__.py", line 419, in parse_timespan
+ raise InvalidTimespan(format(msg, timespan, tokens))
+ humanfriendly.InvalidTimespan: Failed to parse timespan! (input '1 age' was tokenized as [1, 'age'])
+ """
+
+
+# Define aliases for backwards compatibility.
+define_aliases(
+ module_name=__name__,
+ # In humanfriendly 1.23 the format_table() function was added to render a
+ # table using characters like dashes and vertical bars to emulate borders.
+ # Since then support for other tables has been added and the name of
+ # format_table() has changed.
+ format_table='humanfriendly.tables.format_pretty_table',
+ # In humanfriendly 1.30 the following text manipulation functions were
+ # moved out into a separate module to enable their usage in other modules
+ # of the humanfriendly package (without causing circular imports).
+ compact='humanfriendly.text.compact',
+ concatenate='humanfriendly.text.concatenate',
+ dedent='humanfriendly.text.dedent',
+ format='humanfriendly.text.format',
+ is_empty_line='humanfriendly.text.is_empty_line',
+ pluralize='humanfriendly.text.pluralize',
+ tokenize='humanfriendly.text.tokenize',
+ trim_empty_lines='humanfriendly.text.trim_empty_lines',
+ # In humanfriendly 1.38 the prompt_for_choice() function was moved out into a
+ # separate module because several variants of interactive prompts were added.
+ prompt_for_choice='humanfriendly.prompts.prompt_for_choice',
+ # In humanfriendly 8.0 the Spinner class and minimum_spinner_interval
+ # variable were extracted to a new module and the erase_line_code,
+ # hide_cursor_code and show_cursor_code variables were moved.
+ AutomaticSpinner='humanfriendly.terminal.spinners.AutomaticSpinner',
+ Spinner='humanfriendly.terminal.spinners.Spinner',
+ erase_line_code='humanfriendly.terminal.ANSI_ERASE_LINE',
+ hide_cursor_code='humanfriendly.terminal.ANSI_SHOW_CURSOR',
+ minimum_spinner_interval='humanfriendly.terminal.spinners.MINIMUM_INTERVAL',
+ show_cursor_code='humanfriendly.terminal.ANSI_HIDE_CURSOR',
+)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/case.py b/contrib/python/humanfriendly/py3/humanfriendly/case.py
new file mode 100644
index 0000000000..4c71857e40
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/case.py
@@ -0,0 +1,157 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: April 19, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Simple case insensitive dictionaries.
+
+The :class:`CaseInsensitiveDict` class is a dictionary whose string keys
+are case insensitive. It works by automatically coercing string keys to
+:class:`CaseInsensitiveKey` objects. Keys that are not strings are
+supported as well, just without case insensitivity.
+
+At its core this module works by normalizing strings to lowercase before
+comparing or hashing them. It doesn't support proper case folding nor
+does it support Unicode normalization, hence the word "simple".
+"""
+
+# Standard library modules.
+import collections
+
+try:
+ # Python >= 3.3.
+ from collections.abc import Iterable, Mapping
+except ImportError:
+ # Python 2.7.
+ from collections import Iterable, Mapping
+
+# Modules included in our package.
+from humanfriendly.compat import basestring, unicode
+
+# Public identifiers that require documentation.
+__all__ = ("CaseInsensitiveDict", "CaseInsensitiveKey")
+
+
+class CaseInsensitiveDict(collections.OrderedDict):
+
+ """
+ Simple case insensitive dictionary implementation (that remembers insertion order).
+
+ This class works by overriding methods that deal with dictionary keys to
+ coerce string keys to :class:`CaseInsensitiveKey` objects before calling
+ down to the regular dictionary handling methods. While intended to be
+ complete this class has not been extensively tested yet.
+ """
+
+ def __init__(self, other=None, **kw):
+ """Initialize a :class:`CaseInsensitiveDict` object."""
+ # Initialize our superclass.
+ super(CaseInsensitiveDict, self).__init__()
+ # Handle the initializer arguments.
+ self.update(other, **kw)
+
+ def coerce_key(self, key):
+ """
+ Coerce string keys to :class:`CaseInsensitiveKey` objects.
+
+ :param key: The value to coerce (any type).
+ :returns: If `key` is a string then a :class:`CaseInsensitiveKey`
+ object is returned, otherwise the value of `key` is
+ returned unmodified.
+ """
+ if isinstance(key, basestring):
+ key = CaseInsensitiveKey(key)
+ return key
+
+ @classmethod
+ def fromkeys(cls, iterable, value=None):
+ """Create a case insensitive dictionary with keys from `iterable` and values set to `value`."""
+ return cls((k, value) for k in iterable)
+
+ def get(self, key, default=None):
+ """Get the value of an existing item."""
+ return super(CaseInsensitiveDict, self).get(self.coerce_key(key), default)
+
+ def pop(self, key, default=None):
+ """Remove an item from a case insensitive dictionary."""
+ return super(CaseInsensitiveDict, self).pop(self.coerce_key(key), default)
+
+ def setdefault(self, key, default=None):
+ """Get the value of an existing item or add a new item."""
+ return super(CaseInsensitiveDict, self).setdefault(self.coerce_key(key), default)
+
+ def update(self, other=None, **kw):
+ """Update a case insensitive dictionary with new items."""
+ if isinstance(other, Mapping):
+ # Copy the items from the given mapping.
+ for key, value in other.items():
+ self[key] = value
+ elif isinstance(other, Iterable):
+ # Copy the items from the given iterable.
+ for key, value in other:
+ self[key] = value
+ elif other is not None:
+ # Complain about unsupported values.
+ msg = "'%s' object is not iterable"
+ type_name = type(value).__name__
+ raise TypeError(msg % type_name)
+ # Copy the keyword arguments (if any).
+ for key, value in kw.items():
+ self[key] = value
+
+ def __contains__(self, key):
+ """Check if a case insensitive dictionary contains the given key."""
+ return super(CaseInsensitiveDict, self).__contains__(self.coerce_key(key))
+
+ def __delitem__(self, key):
+ """Delete an item in a case insensitive dictionary."""
+ return super(CaseInsensitiveDict, self).__delitem__(self.coerce_key(key))
+
+ def __getitem__(self, key):
+ """Get the value of an item in a case insensitive dictionary."""
+ return super(CaseInsensitiveDict, self).__getitem__(self.coerce_key(key))
+
+ def __setitem__(self, key, value):
+ """Set the value of an item in a case insensitive dictionary."""
+ return super(CaseInsensitiveDict, self).__setitem__(self.coerce_key(key), value)
+
+
+class CaseInsensitiveKey(unicode):
+
+ """
+ Simple case insensitive dictionary key implementation.
+
+ The :class:`CaseInsensitiveKey` class provides an intentionally simple
+ implementation of case insensitive strings to be used as dictionary keys.
+
+ If you need features like Unicode normalization or proper case folding
+ please consider using a more advanced implementation like the :pypi:`istr`
+ package instead.
+ """
+
+ def __new__(cls, value):
+ """Create a :class:`CaseInsensitiveKey` object."""
+ # Delegate string object creation to our superclass.
+ obj = unicode.__new__(cls, value)
+ # Store the lowercased string and its hash value.
+ normalized = obj.lower()
+ obj._normalized = normalized
+ obj._hash_value = hash(normalized)
+ return obj
+
+ def __hash__(self):
+ """Get the hash value of the lowercased string."""
+ return self._hash_value
+
+ def __eq__(self, other):
+ """Compare two strings as lowercase."""
+ if isinstance(other, CaseInsensitiveKey):
+ # Fast path (and the most common case): Comparison with same type.
+ return self._normalized == other._normalized
+ elif isinstance(other, unicode):
+ # Slow path: Comparison with strings that need lowercasing.
+ return self._normalized == other.lower()
+ else:
+ return NotImplemented
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/cli.py b/contrib/python/humanfriendly/py3/humanfriendly/cli.py
new file mode 100644
index 0000000000..eb81db172b
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/cli.py
@@ -0,0 +1,291 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: March 1, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Usage: humanfriendly [OPTIONS]
+
+Human friendly input/output (text formatting) on the command
+line based on the Python package with the same name.
+
+Supported options:
+
+ -c, --run-command
+
+ Execute an external command (given as the positional arguments) and render
+ a spinner and timer while the command is running. The exit status of the
+ command is propagated.
+
+ --format-table
+
+ Read tabular data from standard input (each line is a row and each
+ whitespace separated field is a column), format the data as a table and
+ print the resulting table to standard output. See also the --delimiter
+ option.
+
+ -d, --delimiter=VALUE
+
+ Change the delimiter used by --format-table to VALUE (a string). By default
+ all whitespace is treated as a delimiter.
+
+ -l, --format-length=LENGTH
+
+ Convert a length count (given as the integer or float LENGTH) into a human
+ readable string and print that string to standard output.
+
+ -n, --format-number=VALUE
+
+ Format a number (given as the integer or floating point number VALUE) with
+ thousands separators and two decimal places (if needed) and print the
+ formatted number to standard output.
+
+ -s, --format-size=BYTES
+
+ Convert a byte count (given as the integer BYTES) into a human readable
+ string and print that string to standard output.
+
+ -b, --binary
+
+ Change the output of -s, --format-size to use binary multiples of bytes
+ (base-2) instead of the default decimal multiples of bytes (base-10).
+
+ -t, --format-timespan=SECONDS
+
+ Convert a number of seconds (given as the floating point number SECONDS)
+ into a human readable timespan and print that string to standard output.
+
+ --parse-length=VALUE
+
+ Parse a human readable length (given as the string VALUE) and print the
+ number of metres to standard output.
+
+ --parse-size=VALUE
+
+ Parse a human readable data size (given as the string VALUE) and print the
+ number of bytes to standard output.
+
+ --demo
+
+ Demonstrate changing the style and color of the terminal font using ANSI
+ escape sequences.
+
+ -h, --help
+
+ Show this message and exit.
+"""
+
+# Standard library modules.
+import functools
+import getopt
+import pipes
+import subprocess
+import sys
+
+# Modules included in our package.
+from humanfriendly import (
+ Timer,
+ format_length,
+ format_number,
+ format_size,
+ format_timespan,
+ parse_length,
+ parse_size,
+)
+from humanfriendly.tables import format_pretty_table, format_smart_table
+from humanfriendly.terminal import (
+ ANSI_COLOR_CODES,
+ ANSI_TEXT_STYLES,
+ HIGHLIGHT_COLOR,
+ ansi_strip,
+ ansi_wrap,
+ enable_ansi_support,
+ find_terminal_size,
+ output,
+ usage,
+ warning,
+)
+from humanfriendly.terminal.spinners import Spinner
+
+# Public identifiers that require documentation.
+__all__ = (
+ 'demonstrate_256_colors',
+ 'demonstrate_ansi_formatting',
+ 'main',
+ 'print_formatted_length',
+ 'print_formatted_number',
+ 'print_formatted_size',
+ 'print_formatted_table',
+ 'print_formatted_timespan',
+ 'print_parsed_length',
+ 'print_parsed_size',
+ 'run_command',
+)
+
+
+def main():
+ """Command line interface for the ``humanfriendly`` program."""
+ enable_ansi_support()
+ try:
+ options, arguments = getopt.getopt(sys.argv[1:], 'cd:l:n:s:bt:h', [
+ 'run-command', 'format-table', 'delimiter=', 'format-length=',
+ 'format-number=', 'format-size=', 'binary', 'format-timespan=',
+ 'parse-length=', 'parse-size=', 'demo', 'help',
+ ])
+ except Exception as e:
+ warning("Error: %s", e)
+ sys.exit(1)
+ actions = []
+ delimiter = None
+ should_format_table = False
+ binary = any(o in ('-b', '--binary') for o, v in options)
+ for option, value in options:
+ if option in ('-d', '--delimiter'):
+ delimiter = value
+ elif option == '--parse-size':
+ actions.append(functools.partial(print_parsed_size, value))
+ elif option == '--parse-length':
+ actions.append(functools.partial(print_parsed_length, value))
+ elif option in ('-c', '--run-command'):
+ actions.append(functools.partial(run_command, arguments))
+ elif option in ('-l', '--format-length'):
+ actions.append(functools.partial(print_formatted_length, value))
+ elif option in ('-n', '--format-number'):
+ actions.append(functools.partial(print_formatted_number, value))
+ elif option in ('-s', '--format-size'):
+ actions.append(functools.partial(print_formatted_size, value, binary))
+ elif option == '--format-table':
+ should_format_table = True
+ elif option in ('-t', '--format-timespan'):
+ actions.append(functools.partial(print_formatted_timespan, value))
+ elif option == '--demo':
+ actions.append(demonstrate_ansi_formatting)
+ elif option in ('-h', '--help'):
+ usage(__doc__)
+ return
+ if should_format_table:
+ actions.append(functools.partial(print_formatted_table, delimiter))
+ if not actions:
+ usage(__doc__)
+ return
+ for partial in actions:
+ partial()
+
+
+def run_command(command_line):
+ """Run an external command and show a spinner while the command is running."""
+ timer = Timer()
+ spinner_label = "Waiting for command: %s" % " ".join(map(pipes.quote, command_line))
+ with Spinner(label=spinner_label, timer=timer) as spinner:
+ process = subprocess.Popen(command_line)
+ while True:
+ spinner.step()
+ spinner.sleep()
+ if process.poll() is not None:
+ break
+ sys.exit(process.returncode)
+
+
+def print_formatted_length(value):
+ """Print a human readable length."""
+ if '.' in value:
+ output(format_length(float(value)))
+ else:
+ output(format_length(int(value)))
+
+
+def print_formatted_number(value):
+ """Print large numbers in a human readable format."""
+ output(format_number(float(value)))
+
+
+def print_formatted_size(value, binary):
+ """Print a human readable size."""
+ output(format_size(int(value), binary=binary))
+
+
+def print_formatted_table(delimiter):
+ """Read tabular data from standard input and print a table."""
+ data = []
+ for line in sys.stdin:
+ line = line.rstrip()
+ data.append(line.split(delimiter))
+ output(format_pretty_table(data))
+
+
+def print_formatted_timespan(value):
+ """Print a human readable timespan."""
+ output(format_timespan(float(value)))
+
+
+def print_parsed_length(value):
+ """Parse a human readable length and print the number of metres."""
+ output(parse_length(value))
+
+
+def print_parsed_size(value):
+ """Parse a human readable data size and print the number of bytes."""
+ output(parse_size(value))
+
+
+def demonstrate_ansi_formatting():
+ """Demonstrate the use of ANSI escape sequences."""
+ # First we demonstrate the supported text styles.
+ output('%s', ansi_wrap('Text styles:', bold=True))
+ styles = ['normal', 'bright']
+ styles.extend(ANSI_TEXT_STYLES.keys())
+ for style_name in sorted(styles):
+ options = dict(color=HIGHLIGHT_COLOR)
+ if style_name != 'normal':
+ options[style_name] = True
+ style_label = style_name.replace('_', ' ').capitalize()
+ output(' - %s', ansi_wrap(style_label, **options))
+ # Now we demonstrate named foreground and background colors.
+ for color_type, color_label in (('color', 'Foreground colors'),
+ ('background', 'Background colors')):
+ intensities = [
+ ('normal', dict()),
+ ('bright', dict(bright=True)),
+ ]
+ if color_type != 'background':
+ intensities.insert(0, ('faint', dict(faint=True)))
+ output('\n%s' % ansi_wrap('%s:' % color_label, bold=True))
+ output(format_smart_table([
+ [color_name] + [
+ ansi_wrap(
+ 'XXXXXX' if color_type != 'background' else (' ' * 6),
+ **dict(list(kw.items()) + [(color_type, color_name)])
+ ) for label, kw in intensities
+ ] for color_name in sorted(ANSI_COLOR_CODES.keys())
+ ], column_names=['Color'] + [
+ label.capitalize() for label, kw in intensities
+ ]))
+ # Demonstrate support for 256 colors as well.
+ demonstrate_256_colors(0, 7, 'standard colors')
+ demonstrate_256_colors(8, 15, 'high-intensity colors')
+ demonstrate_256_colors(16, 231, '216 colors')
+ demonstrate_256_colors(232, 255, 'gray scale colors')
+
+
+def demonstrate_256_colors(i, j, group=None):
+ """Demonstrate 256 color mode support."""
+ # Generate the label.
+ label = '256 color mode'
+ if group:
+ label += ' (%s)' % group
+ output('\n' + ansi_wrap('%s:' % label, bold=True))
+ # Generate a simple rendering of the colors in the requested range and
+ # check if it will fit on a single line (given the terminal's width).
+ single_line = ''.join(' ' + ansi_wrap(str(n), color=n) for n in range(i, j + 1))
+ lines, columns = find_terminal_size()
+ if columns >= len(ansi_strip(single_line)):
+ output(single_line)
+ else:
+ # Generate a more complex rendering of the colors that will nicely wrap
+ # over multiple lines without using too many lines.
+ width = len(str(j)) + 1
+ colors_per_line = int(columns / width)
+ colors = [ansi_wrap(str(n).rjust(width), color=n) for n in range(i, j + 1)]
+ blocks = [colors[n:n + colors_per_line] for n in range(0, len(colors), colors_per_line)]
+ output('\n'.join(''.join(b) for b in blocks))
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/compat.py b/contrib/python/humanfriendly/py3/humanfriendly/compat.py
new file mode 100644
index 0000000000..24c9d1833a
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/compat.py
@@ -0,0 +1,146 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: September 17, 2021
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Compatibility with Python 2 and 3.
+
+This module exposes aliases and functions that make it easier to write Python
+code that is compatible with Python 2 and Python 3.
+
+.. data:: basestring
+
+ Alias for :func:`python2:basestring` (in Python 2) or :class:`python3:str`
+ (in Python 3). See also :func:`is_string()`.
+
+.. data:: HTMLParser
+
+ Alias for :class:`python2:HTMLParser.HTMLParser` (in Python 2) or
+ :class:`python3:html.parser.HTMLParser` (in Python 3).
+
+.. data:: interactive_prompt
+
+ Alias for :func:`python2:raw_input()` (in Python 2) or
+ :func:`python3:input()` (in Python 3).
+
+.. data:: StringIO
+
+ Alias for :class:`python2:StringIO.StringIO` (in Python 2) or
+ :class:`python3:io.StringIO` (in Python 3).
+
+.. data:: unicode
+
+ Alias for :func:`python2:unicode` (in Python 2) or :class:`python3:str` (in
+ Python 3). See also :func:`coerce_string()`.
+
+.. data:: monotonic
+
+ Alias for :func:`python3:time.monotonic()` (in Python 3.3 and higher) or
+ `monotonic.monotonic()` (a `conditional dependency
+ <https://pypi.org/project/monotonic/>`_ on older Python versions).
+"""
+
+__all__ = (
+ 'HTMLParser',
+ 'StringIO',
+ 'basestring',
+ 'coerce_string',
+ 'interactive_prompt',
+ 'is_string',
+ 'is_unicode',
+ 'monotonic',
+ 'name2codepoint',
+ 'on_macos',
+ 'on_windows',
+ 'unichr',
+ 'unicode',
+ 'which',
+)
+
+# Standard library modules.
+import sys
+
+# Differences between Python 2 and 3.
+try:
+ # Python 2.
+ unicode = unicode
+ unichr = unichr
+ basestring = basestring
+ interactive_prompt = raw_input
+ from distutils.spawn import find_executable as which
+ from HTMLParser import HTMLParser
+ from StringIO import StringIO
+ from htmlentitydefs import name2codepoint
+except (ImportError, NameError):
+ # Python 3.
+ unicode = str
+ unichr = chr
+ basestring = str
+ interactive_prompt = input
+ from shutil import which
+ from html.parser import HTMLParser
+ from io import StringIO
+ from html.entities import name2codepoint
+
+try:
+ # Python 3.3 and higher.
+ from time import monotonic
+except ImportError:
+ # A replacement for older Python versions:
+ # https://pypi.org/project/monotonic/
+ try:
+ from monotonic import monotonic
+ except (ImportError, RuntimeError):
+ # We fall back to the old behavior of using time.time() instead of
+ # failing when {time,monotonic}.monotonic() are both missing.
+ from time import time as monotonic
+
+
+def coerce_string(value):
+ """
+ Coerce any value to a Unicode string (:func:`python2:unicode` in Python 2 and :class:`python3:str` in Python 3).
+
+ :param value: The value to coerce.
+ :returns: The value coerced to a Unicode string.
+ """
+ return value if is_string(value) else unicode(value)
+
+
+def is_string(value):
+ """
+ Check if a value is a :func:`python2:basestring` (in Python 2) or :class:`python3:str` (in Python 3) object.
+
+ :param value: The value to check.
+ :returns: :data:`True` if the value is a string, :data:`False` otherwise.
+ """
+ return isinstance(value, basestring)
+
+
+def is_unicode(value):
+ """
+ Check if a value is a :func:`python2:unicode` (in Python 2) or :class:`python2:str` (in Python 3) object.
+
+ :param value: The value to check.
+ :returns: :data:`True` if the value is a Unicode string, :data:`False` otherwise.
+ """
+ return isinstance(value, unicode)
+
+
+def on_macos():
+ """
+ Check if we're running on Apple MacOS.
+
+ :returns: :data:`True` if running MacOS, :data:`False` otherwise.
+ """
+ return sys.platform.startswith('darwin')
+
+
+def on_windows():
+ """
+ Check if we're running on the Microsoft Windows OS.
+
+ :returns: :data:`True` if running Windows, :data:`False` otherwise.
+ """
+ return sys.platform.startswith('win')
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/decorators.py b/contrib/python/humanfriendly/py3/humanfriendly/decorators.py
new file mode 100644
index 0000000000..c90a59ea28
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/decorators.py
@@ -0,0 +1,43 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: March 2, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""Simple function decorators to make Python programming easier."""
+
+# Standard library modules.
+import functools
+
+# Public identifiers that require documentation.
+__all__ = ('RESULTS_ATTRIBUTE', 'cached')
+
+RESULTS_ATTRIBUTE = 'cached_results'
+"""The name of the property used to cache the return values of functions (a string)."""
+
+
+def cached(function):
+ """
+ Rudimentary caching decorator for functions.
+
+ :param function: The function whose return value should be cached.
+ :returns: The decorated function.
+
+ The given function will only be called once, the first time the wrapper
+ function is called. The return value is cached by the wrapper function as
+ an attribute of the given function and returned on each subsequent call.
+
+ .. note:: Currently no function arguments are supported because only a
+ single return value can be cached. Accepting any function
+ arguments at all would imply that the cache is parametrized on
+ function arguments, which is not currently the case.
+ """
+ @functools.wraps(function)
+ def wrapper():
+ try:
+ return getattr(wrapper, RESULTS_ATTRIBUTE)
+ except AttributeError:
+ result = function()
+ setattr(wrapper, RESULTS_ATTRIBUTE, result)
+ return result
+ return wrapper
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/deprecation.py b/contrib/python/humanfriendly/py3/humanfriendly/deprecation.py
new file mode 100644
index 0000000000..f2012bbd6f
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/deprecation.py
@@ -0,0 +1,251 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: March 2, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Support for deprecation warnings when importing names from old locations.
+
+When software evolves, things tend to move around. This is usually detrimental
+to backwards compatibility (in Python this primarily manifests itself as
+:exc:`~exceptions.ImportError` exceptions).
+
+While backwards compatibility is very important, it should not get in the way
+of progress. It would be great to have the agility to move things around
+without breaking backwards compatibility.
+
+This is where the :mod:`humanfriendly.deprecation` module comes in: It enables
+the definition of backwards compatible aliases that emit a deprecation warning
+when they are accessed.
+
+The way it works is that it wraps the original module in an :class:`DeprecationProxy`
+object that defines a :func:`~DeprecationProxy.__getattr__()` special method to
+override attribute access of the module.
+"""
+
+# Standard library modules.
+import collections
+import functools
+import importlib
+import inspect
+import sys
+import types
+import warnings
+
+# Modules included in our package.
+from humanfriendly.text import format
+
+# Registry of known aliases (used by humanfriendly.sphinx).
+REGISTRY = collections.defaultdict(dict)
+
+# Public identifiers that require documentation.
+__all__ = ("DeprecationProxy", "define_aliases", "deprecated_args", "get_aliases", "is_method")
+
+
+def define_aliases(module_name, **aliases):
+ """
+ Update a module with backwards compatible aliases.
+
+ :param module_name: The ``__name__`` of the module (a string).
+ :param aliases: Each keyword argument defines an alias. The values
+ are expected to be "dotted paths" (strings).
+
+ The behavior of this function depends on whether the Sphinx documentation
+ generator is active, because the use of :class:`DeprecationProxy` to shadow the
+ real module in :data:`sys.modules` has the unintended side effect of
+ breaking autodoc support for ``:data:`` members (module variables).
+
+ To avoid breaking Sphinx the proxy object is omitted and instead the
+ aliased names are injected into the original module namespace, to make sure
+ that imports can be satisfied when the documentation is being rendered.
+
+ If you run into cyclic dependencies caused by :func:`define_aliases()` when
+ running Sphinx, you can try moving the call to :func:`define_aliases()` to
+ the bottom of the Python module you're working on.
+ """
+ module = sys.modules[module_name]
+ proxy = DeprecationProxy(module, aliases)
+ # Populate the registry of aliases.
+ for name, target in aliases.items():
+ REGISTRY[module.__name__][name] = target
+ # Avoid confusing Sphinx.
+ if "sphinx" in sys.modules:
+ for name, target in aliases.items():
+ setattr(module, name, proxy.resolve(target))
+ else:
+ # Install a proxy object to raise DeprecationWarning.
+ sys.modules[module_name] = proxy
+
+
+def get_aliases(module_name):
+ """
+ Get the aliases defined by a module.
+
+ :param module_name: The ``__name__`` of the module (a string).
+ :returns: A dictionary with string keys and values:
+
+ 1. Each key gives the name of an alias
+ created for backwards compatibility.
+
+ 2. Each value gives the dotted path of
+ the proper location of the identifier.
+
+ An empty dictionary is returned for modules that
+ don't define any backwards compatible aliases.
+ """
+ return REGISTRY.get(module_name, {})
+
+
+def deprecated_args(*names):
+ """
+ Deprecate positional arguments without dropping backwards compatibility.
+
+ :param names:
+
+ The positional arguments to :func:`deprecated_args()` give the names of
+ the positional arguments that the to-be-decorated function should warn
+ about being deprecated and translate to keyword arguments.
+
+ :returns: A decorator function specialized to `names`.
+
+ The :func:`deprecated_args()` decorator function was created to make it
+ easy to switch from positional arguments to keyword arguments [#]_ while
+ preserving backwards compatibility [#]_ and informing call sites
+ about the change.
+
+ .. [#] Increased flexibility is the main reason why I find myself switching
+ from positional arguments to (optional) keyword arguments as my code
+ evolves to support more use cases.
+
+ .. [#] In my experience positional argument order implicitly becomes part
+ of API compatibility whether intended or not. While this makes sense
+ for functions that over time adopt more and more optional arguments,
+ at a certain point it becomes an inconvenience to code maintenance.
+
+ Here's an example of how to use the decorator::
+
+ @deprecated_args('text')
+ def report_choice(**options):
+ print(options['text'])
+
+ When the decorated function is called with positional arguments
+ a deprecation warning is given::
+
+ >>> report_choice('this will give a deprecation warning')
+ DeprecationWarning: report_choice has deprecated positional arguments, please switch to keyword arguments
+ this will give a deprecation warning
+
+ But when the function is called with keyword arguments no deprecation
+ warning is emitted::
+
+ >>> report_choice(text='this will not give a deprecation warning')
+ this will not give a deprecation warning
+ """
+ def decorator(function):
+ def translate(args, kw):
+ # Raise TypeError when too many positional arguments are passed to the decorated function.
+ if len(args) > len(names):
+ raise TypeError(
+ format(
+ "{name} expected at most {limit} arguments, got {count}",
+ name=function.__name__,
+ limit=len(names),
+ count=len(args),
+ )
+ )
+ # Emit a deprecation warning when positional arguments are used.
+ if args:
+ warnings.warn(
+ format(
+ "{name} has deprecated positional arguments, please switch to keyword arguments",
+ name=function.__name__,
+ ),
+ category=DeprecationWarning,
+ stacklevel=3,
+ )
+ # Translate positional arguments to keyword arguments.
+ for name, value in zip(names, args):
+ kw[name] = value
+ if is_method(function):
+ @functools.wraps(function)
+ def wrapper(*args, **kw):
+ """Wrapper for instance methods."""
+ args = list(args)
+ self = args.pop(0)
+ translate(args, kw)
+ return function(self, **kw)
+ else:
+ @functools.wraps(function)
+ def wrapper(*args, **kw):
+ """Wrapper for module level functions."""
+ translate(args, kw)
+ return function(**kw)
+ return wrapper
+ return decorator
+
+
+def is_method(function):
+ """Check if the expected usage of the given function is as an instance method."""
+ try:
+ # Python 3.3 and newer.
+ signature = inspect.signature(function)
+ return "self" in signature.parameters
+ except AttributeError:
+ # Python 3.2 and older.
+ metadata = inspect.getargspec(function)
+ return "self" in metadata.args
+
+
+class DeprecationProxy(types.ModuleType):
+
+ """Emit deprecation warnings for imports that should be updated."""
+
+ def __init__(self, module, aliases):
+ """
+ Initialize an :class:`DeprecationProxy` object.
+
+ :param module: The original module object.
+ :param aliases: A dictionary of aliases.
+ """
+ # Initialize our superclass.
+ super(DeprecationProxy, self).__init__(name=module.__name__)
+ # Store initializer arguments.
+ self.module = module
+ self.aliases = aliases
+
+ def __getattr__(self, name):
+ """
+ Override module attribute lookup.
+
+ :param name: The name to look up (a string).
+ :returns: The attribute value.
+ """
+ # Check if the given name is an alias.
+ target = self.aliases.get(name)
+ if target is not None:
+ # Emit the deprecation warning.
+ warnings.warn(
+ format("%s.%s was moved to %s, please update your imports", self.module.__name__, name, target),
+ category=DeprecationWarning,
+ stacklevel=2,
+ )
+ # Resolve the dotted path.
+ return self.resolve(target)
+ # Look up the name in the original module namespace.
+ value = getattr(self.module, name, None)
+ if value is not None:
+ return value
+ # Fall back to the default behavior.
+ raise AttributeError(format("module '%s' has no attribute '%s'", self.module.__name__, name))
+
+ def resolve(self, target):
+ """
+ Look up the target of an alias.
+
+ :param target: The fully qualified dotted path (a string).
+ :returns: The value of the given target.
+ """
+ module_name, _, member = target.rpartition(".")
+ module = importlib.import_module(module_name)
+ return getattr(module, member)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/prompts.py b/contrib/python/humanfriendly/py3/humanfriendly/prompts.py
new file mode 100644
index 0000000000..07166b6709
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/prompts.py
@@ -0,0 +1,376 @@
+# vim: fileencoding=utf-8
+
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: February 9, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Interactive terminal prompts.
+
+The :mod:`~humanfriendly.prompts` module enables interaction with the user
+(operator) by asking for confirmation (:func:`prompt_for_confirmation()`) and
+asking to choose from a list of options (:func:`prompt_for_choice()`). It works
+by rendering interactive prompts on the terminal.
+"""
+
+# Standard library modules.
+import logging
+import sys
+
+# Modules included in our package.
+from humanfriendly.compat import interactive_prompt
+from humanfriendly.terminal import (
+ HIGHLIGHT_COLOR,
+ ansi_strip,
+ ansi_wrap,
+ connected_to_terminal,
+ terminal_supports_colors,
+ warning,
+)
+from humanfriendly.text import format, concatenate
+
+# Public identifiers that require documentation.
+__all__ = (
+ 'MAX_ATTEMPTS',
+ 'TooManyInvalidReplies',
+ 'logger',
+ 'prepare_friendly_prompts',
+ 'prepare_prompt_text',
+ 'prompt_for_choice',
+ 'prompt_for_confirmation',
+ 'prompt_for_input',
+ 'retry_limit',
+)
+
+MAX_ATTEMPTS = 10
+"""The number of times an interactive prompt is shown on invalid input (an integer)."""
+
+# Initialize a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+def prompt_for_confirmation(question, default=None, padding=True):
+ """
+ Prompt the user for confirmation.
+
+ :param question: The text that explains what the user is confirming (a string).
+ :param default: The default value (a boolean) or :data:`None`.
+ :param padding: Refer to the documentation of :func:`prompt_for_input()`.
+ :returns: - If the user enters 'yes' or 'y' then :data:`True` is returned.
+ - If the user enters 'no' or 'n' then :data:`False` is returned.
+ - If the user doesn't enter any text or standard input is not
+ connected to a terminal (which makes it impossible to prompt
+ the user) the value of the keyword argument ``default`` is
+ returned (if that value is not :data:`None`).
+ :raises: - Any exceptions raised by :func:`retry_limit()`.
+ - Any exceptions raised by :func:`prompt_for_input()`.
+
+ When `default` is :data:`False` and the user doesn't enter any text an
+ error message is printed and the prompt is repeated:
+
+ >>> prompt_for_confirmation("Are you sure?")
+ <BLANKLINE>
+ Are you sure? [y/n]
+ <BLANKLINE>
+ Error: Please enter 'yes' or 'no' (there's no default choice).
+ <BLANKLINE>
+ Are you sure? [y/n]
+
+ The same thing happens when the user enters text that isn't recognized:
+
+ >>> prompt_for_confirmation("Are you sure?")
+ <BLANKLINE>
+ Are you sure? [y/n] about what?
+ <BLANKLINE>
+ Error: Please enter 'yes' or 'no' (the text 'about what?' is not recognized).
+ <BLANKLINE>
+ Are you sure? [y/n]
+ """
+ # Generate the text for the prompt.
+ prompt_text = prepare_prompt_text(question, bold=True)
+ # Append the valid replies (and default reply) to the prompt text.
+ hint = "[Y/n]" if default else "[y/N]" if default is not None else "[y/n]"
+ prompt_text += " %s " % prepare_prompt_text(hint, color=HIGHLIGHT_COLOR)
+ # Loop until a valid response is given.
+ logger.debug("Requesting interactive confirmation from terminal: %r", ansi_strip(prompt_text).rstrip())
+ for attempt in retry_limit():
+ reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
+ if reply.lower() in ('y', 'yes'):
+ logger.debug("Confirmation granted by reply (%r).", reply)
+ return True
+ elif reply.lower() in ('n', 'no'):
+ logger.debug("Confirmation denied by reply (%r).", reply)
+ return False
+ elif (not reply) and default is not None:
+ logger.debug("Default choice selected by empty reply (%r).",
+ "granted" if default else "denied")
+ return default
+ else:
+ details = ("the text '%s' is not recognized" % reply
+ if reply else "there's no default choice")
+ logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
+ "invalid" if reply else "empty", details,
+ attempt, MAX_ATTEMPTS)
+ warning("{indent}Error: Please enter 'yes' or 'no' ({details}).",
+ indent=' ' if padding else '', details=details)
+
+
+def prompt_for_choice(choices, default=None, padding=True):
+ """
+ Prompt the user to select a choice from a group of options.
+
+ :param choices: A sequence of strings with available options.
+ :param default: The default choice if the user simply presses Enter
+ (expected to be a string, defaults to :data:`None`).
+ :param padding: Refer to the documentation of
+ :func:`~humanfriendly.prompts.prompt_for_input()`.
+ :returns: The string corresponding to the user's choice.
+ :raises: - :exc:`~exceptions.ValueError` if `choices` is an empty sequence.
+ - Any exceptions raised by
+ :func:`~humanfriendly.prompts.retry_limit()`.
+ - Any exceptions raised by
+ :func:`~humanfriendly.prompts.prompt_for_input()`.
+
+ When no options are given an exception is raised:
+
+ >>> prompt_for_choice([])
+ Traceback (most recent call last):
+ File "humanfriendly/prompts.py", line 148, in prompt_for_choice
+ raise ValueError("Can't prompt for choice without any options!")
+ ValueError: Can't prompt for choice without any options!
+
+ If a single option is given the user isn't prompted:
+
+ >>> prompt_for_choice(['only one choice'])
+ 'only one choice'
+
+ Here's what the actual prompt looks like by default:
+
+ >>> prompt_for_choice(['first option', 'second option'])
+ <BLANKLINE>
+ 1. first option
+ 2. second option
+ <BLANKLINE>
+ Enter your choice as a number or unique substring (Control-C aborts): second
+ <BLANKLINE>
+ 'second option'
+
+ If you don't like the whitespace (empty lines and indentation):
+
+ >>> prompt_for_choice(['first option', 'second option'], padding=False)
+ 1. first option
+ 2. second option
+ Enter your choice as a number or unique substring (Control-C aborts): first
+ 'first option'
+ """
+ indent = ' ' if padding else ''
+ # Make sure we can use 'choices' more than once (i.e. not a generator).
+ choices = list(choices)
+ if len(choices) == 1:
+ # If there's only one option there's no point in prompting the user.
+ logger.debug("Skipping interactive prompt because there's only option (%r).", choices[0])
+ return choices[0]
+ elif not choices:
+ # We can't render a choice prompt without any options.
+ raise ValueError("Can't prompt for choice without any options!")
+ # Generate the prompt text.
+ prompt_text = ('\n\n' if padding else '\n').join([
+ # Present the available choices in a user friendly way.
+ "\n".join([
+ (u" %i. %s" % (i, choice)) + (" (default choice)" if choice == default else "")
+ for i, choice in enumerate(choices, start=1)
+ ]),
+ # Instructions for the user.
+ "Enter your choice as a number or unique substring (Control-C aborts): ",
+ ])
+ prompt_text = prepare_prompt_text(prompt_text, bold=True)
+ # Loop until a valid choice is made.
+ logger.debug("Requesting interactive choice on terminal (options are %s) ..",
+ concatenate(map(repr, choices)))
+ for attempt in retry_limit():
+ reply = prompt_for_input(prompt_text, '', padding=padding, strip=True)
+ if not reply and default is not None:
+ logger.debug("Default choice selected by empty reply (%r).", default)
+ return default
+ elif reply.isdigit():
+ index = int(reply) - 1
+ if 0 <= index < len(choices):
+ logger.debug("Option (%r) selected by numeric reply (%s).", choices[index], reply)
+ return choices[index]
+ # Check for substring matches.
+ matches = []
+ for choice in choices:
+ lower_reply = reply.lower()
+ lower_choice = choice.lower()
+ if lower_reply == lower_choice:
+ # If we have an 'exact' match we return it immediately.
+ logger.debug("Option (%r) selected by reply (exact match).", choice)
+ return choice
+ elif lower_reply in lower_choice and len(lower_reply) > 0:
+ # Otherwise we gather substring matches.
+ matches.append(choice)
+ if len(matches) == 1:
+ # If a single choice was matched we return it.
+ logger.debug("Option (%r) selected by reply (substring match on %r).", matches[0], reply)
+ return matches[0]
+ else:
+ # Give the user a hint about what went wrong.
+ if matches:
+ details = format("text '%s' matches more than one choice: %s", reply, concatenate(matches))
+ elif reply.isdigit():
+ details = format("number %i is not a valid choice", int(reply))
+ elif reply and not reply.isspace():
+ details = format("text '%s' doesn't match any choices", reply)
+ else:
+ details = "there's no default choice"
+ logger.debug("Got %s reply (%s), retrying (%i/%i) ..",
+ "invalid" if reply else "empty", details,
+ attempt, MAX_ATTEMPTS)
+ warning("%sError: Invalid input (%s).", indent, details)
+
+
+def prompt_for_input(question, default=None, padding=True, strip=True):
+ """
+ Prompt the user for input (free form text).
+
+ :param question: An explanation of what is expected from the user (a string).
+ :param default: The return value if the user doesn't enter any text or
+ standard input is not connected to a terminal (which
+ makes it impossible to prompt the user).
+ :param padding: Render empty lines before and after the prompt to make it
+ stand out from the surrounding text? (a boolean, defaults
+ to :data:`True`)
+ :param strip: Strip leading/trailing whitespace from the user's reply?
+ :returns: The text entered by the user (a string) or the value of the
+ `default` argument.
+ :raises: - :exc:`~exceptions.KeyboardInterrupt` when the program is
+ interrupted_ while the prompt is active, for example
+ because the user presses Control-C_.
+ - :exc:`~exceptions.EOFError` when reading from `standard input`_
+ fails, for example because the user presses Control-D_ or
+ because the standard input stream is redirected (only if
+ `default` is :data:`None`).
+
+ .. _Control-C: https://en.wikipedia.org/wiki/Control-C#In_command-line_environments
+ .. _Control-D: https://en.wikipedia.org/wiki/End-of-transmission_character#Meaning_in_Unix
+ .. _interrupted: https://en.wikipedia.org/wiki/Unix_signal#SIGINT
+ .. _standard input: https://en.wikipedia.org/wiki/Standard_streams#Standard_input_.28stdin.29
+ """
+ prepare_friendly_prompts()
+ reply = None
+ try:
+ # Prefix an empty line to the text and indent by one space?
+ if padding:
+ question = '\n' + question
+ question = question.replace('\n', '\n ')
+ # Render the prompt and wait for the user's reply.
+ try:
+ reply = interactive_prompt(question)
+ finally:
+ if reply is None:
+ # If the user terminated the prompt using Control-C or
+ # Control-D instead of pressing Enter no newline will be
+ # rendered after the prompt's text. The result looks kind of
+ # weird:
+ #
+ # $ python -c 'print(raw_input("Are you sure? "))'
+ # Are you sure? ^CTraceback (most recent call last):
+ # File "<string>", line 1, in <module>
+ # KeyboardInterrupt
+ #
+ # We can avoid this by emitting a newline ourselves if an
+ # exception was raised (signaled by `reply' being None).
+ sys.stderr.write('\n')
+ if padding:
+ # If the caller requested (didn't opt out of) `padding' then we'll
+ # emit a newline regardless of whether an exception is being
+ # handled. This helps to make interactive prompts `stand out' from
+ # a surrounding `wall of text' on the terminal.
+ sys.stderr.write('\n')
+ except BaseException as e:
+ if isinstance(e, EOFError) and default is not None:
+ # If standard input isn't connected to an interactive terminal
+ # but the caller provided a default we'll return that.
+ logger.debug("Got EOF from terminal, returning default value (%r) ..", default)
+ return default
+ else:
+ # Otherwise we log that the prompt was interrupted but propagate
+ # the exception to the caller.
+ logger.warning("Interactive prompt was interrupted by exception!", exc_info=True)
+ raise
+ if default is not None and not reply:
+ # If the reply is empty and `default' is None we don't want to return
+ # None because it's nicer for callers to be able to assume that the
+ # return value is always a string.
+ return default
+ else:
+ return reply.strip()
+
+
+def prepare_prompt_text(prompt_text, **options):
+ """
+ Wrap a text to be rendered as an interactive prompt in ANSI escape sequences.
+
+ :param prompt_text: The text to render on the prompt (a string).
+ :param options: Any keyword arguments are passed on to :func:`.ansi_wrap()`.
+ :returns: The resulting prompt text (a string).
+
+ ANSI escape sequences are only used when the standard output stream is
+ connected to a terminal. When the standard input stream is connected to a
+ terminal any escape sequences are wrapped in "readline hints".
+ """
+ return (ansi_wrap(prompt_text, readline_hints=connected_to_terminal(sys.stdin), **options)
+ if terminal_supports_colors(sys.stdout)
+ else prompt_text)
+
+
+def prepare_friendly_prompts():
+ u"""
+ Make interactive prompts more user friendly.
+
+ The prompts presented by :func:`python2:raw_input()` (in Python 2) and
+ :func:`python3:input()` (in Python 3) are not very user friendly by
+ default, for example the cursor keys (:kbd:`â†`, :kbd:`↑`, :kbd:`→` and
+ :kbd:`↓`) and the :kbd:`Home` and :kbd:`End` keys enter characters instead
+ of performing the action you would expect them to. By simply importing the
+ :mod:`readline` module these prompts become much friendlier (as mentioned
+ in the Python standard library documentation).
+
+ This function is called by the other functions in this module to enable
+ user friendly prompts.
+ """
+ try:
+ import readline # NOQA
+ except ImportError:
+ # might not be available on Windows if pyreadline isn't installed
+ pass
+
+
+def retry_limit(limit=MAX_ATTEMPTS):
+ """
+ Allow the user to provide valid input up to `limit` times.
+
+ :param limit: The maximum number of attempts (a number,
+ defaults to :data:`MAX_ATTEMPTS`).
+ :returns: A generator of numbers starting from one.
+ :raises: :exc:`TooManyInvalidReplies` when an interactive prompt
+ receives repeated invalid input (:data:`MAX_ATTEMPTS`).
+
+ This function returns a generator for interactive prompts that want to
+ repeat on invalid input without getting stuck in infinite loops.
+ """
+ for i in range(limit):
+ yield i + 1
+ msg = "Received too many invalid replies on interactive prompt, giving up! (tried %i times)"
+ formatted_msg = msg % limit
+ # Make sure the event is logged.
+ logger.warning(formatted_msg)
+ # Force the caller to decide what to do now.
+ raise TooManyInvalidReplies(formatted_msg)
+
+
+class TooManyInvalidReplies(Exception):
+
+ """Raised by interactive prompts when they've received too many invalid inputs."""
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/sphinx.py b/contrib/python/humanfriendly/py3/humanfriendly/sphinx.py
new file mode 100644
index 0000000000..cf5d1b3935
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/sphinx.py
@@ -0,0 +1,315 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: June 11, 2021
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Customizations for and integration with the Sphinx_ documentation generator.
+
+The :mod:`humanfriendly.sphinx` module uses the `Sphinx extension API`_ to
+customize the process of generating Sphinx based Python documentation. To
+explore the functionality this module offers its best to start reading
+from the :func:`setup()` function.
+
+.. _Sphinx: http://www.sphinx-doc.org/
+.. _Sphinx extension API: http://sphinx-doc.org/extdev/appapi.html
+"""
+
+# Standard library modules.
+import logging
+import types
+
+# External dependencies (if Sphinx is installed docutils will be installed).
+import docutils.nodes
+import docutils.utils
+
+# Modules included in our package.
+from humanfriendly.deprecation import get_aliases
+from humanfriendly.text import compact, dedent, format
+from humanfriendly.usage import USAGE_MARKER, render_usage
+
+# Public identifiers that require documentation.
+__all__ = (
+ "deprecation_note_callback",
+ "enable_deprecation_notes",
+ "enable_man_role",
+ "enable_pypi_role",
+ "enable_special_methods",
+ "enable_usage_formatting",
+ "logger",
+ "man_role",
+ "pypi_role",
+ "setup",
+ "special_methods_callback",
+ "usage_message_callback",
+)
+
+# Initialize a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+def deprecation_note_callback(app, what, name, obj, options, lines):
+ """
+ Automatically document aliases defined using :func:`~humanfriendly.deprecation.define_aliases()`.
+
+ Refer to :func:`enable_deprecation_notes()` to enable the use of this
+ function (you probably don't want to call :func:`deprecation_note_callback()`
+ directly).
+
+ This function implements a callback for ``autodoc-process-docstring`` that
+ reformats module docstrings to append an overview of aliases defined by the
+ module.
+
+ The parameters expected by this function are those defined for Sphinx event
+ callback functions (i.e. I'm not going to document them here :-).
+ """
+ if isinstance(obj, types.ModuleType) and lines:
+ aliases = get_aliases(obj.__name__)
+ if aliases:
+ # Convert the existing docstring to a string and remove leading
+ # indentation from that string, otherwise our generated content
+ # would have to match the existing indentation in order not to
+ # break docstring parsing (because indentation is significant
+ # in the reStructuredText format).
+ blocks = [dedent("\n".join(lines))]
+ # Use an admonition to group the deprecated aliases together and
+ # to distinguish them from the autodoc entries that follow.
+ blocks.append(".. note:: Deprecated names")
+ indent = " " * 3
+ if len(aliases) == 1:
+ explanation = """
+ The following alias exists to preserve backwards compatibility,
+ however a :exc:`~exceptions.DeprecationWarning` is triggered
+ when it is accessed, because this alias will be removed
+ in a future release.
+ """
+ else:
+ explanation = """
+ The following aliases exist to preserve backwards compatibility,
+ however a :exc:`~exceptions.DeprecationWarning` is triggered
+ when they are accessed, because these aliases will be
+ removed in a future release.
+ """
+ blocks.append(indent + compact(explanation))
+ for name, target in aliases.items():
+ blocks.append(format("%s.. data:: %s", indent, name))
+ blocks.append(format("%sAlias for :obj:`%s`.", indent * 2, target))
+ update_lines(lines, "\n\n".join(blocks))
+
+
+def enable_deprecation_notes(app):
+ """
+ Enable documenting backwards compatibility aliases using the autodoc_ extension.
+
+ :param app: The Sphinx application object.
+
+ This function connects the :func:`deprecation_note_callback()` function to
+ ``autodoc-process-docstring`` events.
+
+ .. _autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html
+ """
+ app.connect("autodoc-process-docstring", deprecation_note_callback)
+
+
+def enable_man_role(app):
+ """
+ Enable the ``:man:`` role for linking to Debian Linux manual pages.
+
+ :param app: The Sphinx application object.
+
+ This function registers the :func:`man_role()` function to handle the
+ ``:man:`` role.
+ """
+ app.add_role("man", man_role)
+
+
+def enable_pypi_role(app):
+ """
+ Enable the ``:pypi:`` role for linking to the Python Package Index.
+
+ :param app: The Sphinx application object.
+
+ This function registers the :func:`pypi_role()` function to handle the
+ ``:pypi:`` role.
+ """
+ app.add_role("pypi", pypi_role)
+
+
+def enable_special_methods(app):
+ """
+ Enable documenting "special methods" using the autodoc_ extension.
+
+ :param app: The Sphinx application object.
+
+ This function connects the :func:`special_methods_callback()` function to
+ ``autodoc-skip-member`` events.
+
+ .. _autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html
+ """
+ app.connect("autodoc-skip-member", special_methods_callback)
+
+
+def enable_usage_formatting(app):
+ """
+ Reformat human friendly usage messages to reStructuredText_.
+
+ :param app: The Sphinx application object (as given to ``setup()``).
+
+ This function connects the :func:`usage_message_callback()` function to
+ ``autodoc-process-docstring`` events.
+
+ .. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText
+ """
+ app.connect("autodoc-process-docstring", usage_message_callback)
+
+
+def man_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
+ """
+ Convert a Linux manual topic to a hyperlink.
+
+ Using the ``:man:`` role is very simple, here's an example:
+
+ .. code-block:: rst
+
+ See the :man:`python` documentation.
+
+ This results in the following:
+
+ See the :man:`python` documentation.
+
+ As the example shows you can use the role inline, embedded in sentences of
+ text. In the generated documentation the ``:man:`` text is omitted and a
+ hyperlink pointing to the Debian Linux manual pages is emitted.
+ """
+ man_url = "https://manpages.debian.org/%s" % text
+ reference = docutils.nodes.reference(rawtext, docutils.utils.unescape(text), refuri=man_url, **options)
+ return [reference], []
+
+
+def pypi_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
+ """
+ Generate hyperlinks to the Python Package Index.
+
+ Using the ``:pypi:`` role is very simple, here's an example:
+
+ .. code-block:: rst
+
+ See the :pypi:`humanfriendly` package.
+
+ This results in the following:
+
+ See the :pypi:`humanfriendly` package.
+
+ As the example shows you can use the role inline, embedded in sentences of
+ text. In the generated documentation the ``:pypi:`` text is omitted and a
+ hyperlink pointing to the Python Package Index is emitted.
+ """
+ pypi_url = "https://pypi.org/project/%s/" % text
+ reference = docutils.nodes.reference(rawtext, docutils.utils.unescape(text), refuri=pypi_url, **options)
+ return [reference], []
+
+
+def setup(app):
+ """
+ Enable all of the provided Sphinx_ customizations.
+
+ :param app: The Sphinx application object.
+
+ The :func:`setup()` function makes it easy to enable all of the Sphinx
+ customizations provided by the :mod:`humanfriendly.sphinx` module with the
+ least amount of code. All you need to do is to add the module name to the
+ ``extensions`` variable in your ``conf.py`` file:
+
+ .. code-block:: python
+
+ # Sphinx extension module names.
+ extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.intersphinx',
+ 'humanfriendly.sphinx',
+ ]
+
+ When Sphinx sees the :mod:`humanfriendly.sphinx` name it will import the
+ module and call its :func:`setup()` function. This function will then call
+ the following:
+
+ - :func:`enable_deprecation_notes()`
+ - :func:`enable_man_role()`
+ - :func:`enable_pypi_role()`
+ - :func:`enable_special_methods()`
+ - :func:`enable_usage_formatting()`
+
+ Of course more functionality may be added at a later stage. If you don't
+ like that idea you may be better of calling the individual functions from
+ your own ``setup()`` function.
+ """
+ from humanfriendly import __version__
+
+ enable_deprecation_notes(app)
+ enable_man_role(app)
+ enable_pypi_role(app)
+ enable_special_methods(app)
+ enable_usage_formatting(app)
+
+ return dict(parallel_read_safe=True, parallel_write_safe=True, version=__version__)
+
+
+def special_methods_callback(app, what, name, obj, skip, options):
+ """
+ Enable documenting "special methods" using the autodoc_ extension.
+
+ Refer to :func:`enable_special_methods()` to enable the use of this
+ function (you probably don't want to call
+ :func:`special_methods_callback()` directly).
+
+ This function implements a callback for ``autodoc-skip-member`` events to
+ include documented "special methods" (method names with two leading and two
+ trailing underscores) in your documentation. The result is similar to the
+ use of the ``special-members`` flag with one big difference: Special
+ methods are included but other types of members are ignored. This means
+ that attributes like ``__weakref__`` will always be ignored (this was my
+ main annoyance with the ``special-members`` flag).
+
+ The parameters expected by this function are those defined for Sphinx event
+ callback functions (i.e. I'm not going to document them here :-).
+ """
+ if getattr(obj, "__doc__", None) and isinstance(obj, (types.FunctionType, types.MethodType)):
+ return False
+ else:
+ return skip
+
+
+def update_lines(lines, text):
+ """Private helper for ``autodoc-process-docstring`` callbacks."""
+ while lines:
+ lines.pop()
+ lines.extend(text.splitlines())
+
+
+def usage_message_callback(app, what, name, obj, options, lines):
+ """
+ Reformat human friendly usage messages to reStructuredText_.
+
+ Refer to :func:`enable_usage_formatting()` to enable the use of this
+ function (you probably don't want to call :func:`usage_message_callback()`
+ directly).
+
+ This function implements a callback for ``autodoc-process-docstring`` that
+ reformats module docstrings using :func:`.render_usage()` so that Sphinx
+ doesn't mangle usage messages that were written to be human readable
+ instead of machine readable. Only module docstrings whose first line starts
+ with :data:`.USAGE_MARKER` are reformatted.
+
+ The parameters expected by this function are those defined for Sphinx event
+ callback functions (i.e. I'm not going to document them here :-).
+ """
+ # Make sure we only modify the docstrings of modules.
+ if isinstance(obj, types.ModuleType) and lines:
+ # Make sure we only modify docstrings containing a usage message.
+ if lines[0].startswith(USAGE_MARKER):
+ # Convert the usage message to reStructuredText.
+ text = render_usage("\n".join(lines))
+ # Fill up the buffer with our modified docstring.
+ update_lines(lines, text)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/tables.py b/contrib/python/humanfriendly/py3/humanfriendly/tables.py
new file mode 100644
index 0000000000..3a32ba3b3f
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/tables.py
@@ -0,0 +1,341 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: February 16, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Functions that render ASCII tables.
+
+Some generic notes about the table formatting functions in this module:
+
+- These functions were not written with performance in mind (*at all*) because
+ they're intended to format tabular data to be presented on a terminal. If
+ someone were to run into a performance problem using these functions, they'd
+ be printing so much tabular data to the terminal that a human wouldn't be
+ able to digest the tabular data anyway, so the point is moot :-).
+
+- These functions ignore ANSI escape sequences (at least the ones generated by
+ the :mod:`~humanfriendly.terminal` module) in the calculation of columns
+ widths. On reason for this is that column names are highlighted in color when
+ connected to a terminal. It also means that you can use ANSI escape sequences
+ to highlight certain column's values if you feel like it (for example to
+ highlight deviations from the norm in an overview of calculated values).
+"""
+
+# Standard library modules.
+import collections
+import re
+
+# Modules included in our package.
+from humanfriendly.compat import coerce_string
+from humanfriendly.terminal import (
+ ansi_strip,
+ ansi_width,
+ ansi_wrap,
+ terminal_supports_colors,
+ find_terminal_size,
+ HIGHLIGHT_COLOR,
+)
+
+# Public identifiers that require documentation.
+__all__ = (
+ 'format_pretty_table',
+ 'format_robust_table',
+ 'format_rst_table',
+ 'format_smart_table',
+)
+
+# Compiled regular expression pattern to recognize table columns containing
+# numeric data (integer and/or floating point numbers). Used to right-align the
+# contents of such columns.
+#
+# Pre-emptive snarky comment: This pattern doesn't match every possible
+# floating point number notation!?!1!1
+#
+# Response: I know, that's intentional. The use of this regular expression
+# pattern has a very high DWIM level and weird floating point notations do not
+# fall under the DWIM umbrella :-).
+NUMERIC_DATA_PATTERN = re.compile(r'^\d+(\.\d+)?$')
+
+
+def format_smart_table(data, column_names):
+ """
+ Render tabular data using the most appropriate representation.
+
+ :param data: An iterable (e.g. a :func:`tuple` or :class:`list`)
+ containing the rows of the table, where each row is an
+ iterable containing the columns of the table (strings).
+ :param column_names: An iterable of column names (strings).
+ :returns: The rendered table (a string).
+
+ If you want an easy way to render tabular data on a terminal in a human
+ friendly format then this function is for you! It works as follows:
+
+ - If the input data doesn't contain any line breaks the function
+ :func:`format_pretty_table()` is used to render a pretty table. If the
+ resulting table fits in the terminal without wrapping the rendered pretty
+ table is returned.
+
+ - If the input data does contain line breaks or if a pretty table would
+ wrap (given the width of the terminal) then the function
+ :func:`format_robust_table()` is used to render a more robust table that
+ can deal with data containing line breaks and long text.
+ """
+ # Normalize the input in case we fall back from a pretty table to a robust
+ # table (in which case we'll definitely iterate the input more than once).
+ data = [normalize_columns(r) for r in data]
+ column_names = normalize_columns(column_names)
+ # Make sure the input data doesn't contain any line breaks (because pretty
+ # tables break horribly when a column's text contains a line break :-).
+ if not any(any('\n' in c for c in r) for r in data):
+ # Render a pretty table.
+ pretty_table = format_pretty_table(data, column_names)
+ # Check if the pretty table fits in the terminal.
+ table_width = max(map(ansi_width, pretty_table.splitlines()))
+ num_rows, num_columns = find_terminal_size()
+ if table_width <= num_columns:
+ # The pretty table fits in the terminal without wrapping!
+ return pretty_table
+ # Fall back to a robust table when a pretty table won't work.
+ return format_robust_table(data, column_names)
+
+
+def format_pretty_table(data, column_names=None, horizontal_bar='-', vertical_bar='|'):
+ """
+ Render a table using characters like dashes and vertical bars to emulate borders.
+
+ :param data: An iterable (e.g. a :func:`tuple` or :class:`list`)
+ containing the rows of the table, where each row is an
+ iterable containing the columns of the table (strings).
+ :param column_names: An iterable of column names (strings).
+ :param horizontal_bar: The character used to represent a horizontal bar (a
+ string).
+ :param vertical_bar: The character used to represent a vertical bar (a
+ string).
+ :returns: The rendered table (a string).
+
+ Here's an example:
+
+ >>> from humanfriendly.tables import format_pretty_table
+ >>> column_names = ['Version', 'Uploaded on', 'Downloads']
+ >>> humanfriendly_releases = [
+ ... ['1.23', '2015-05-25', '218'],
+ ... ['1.23.1', '2015-05-26', '1354'],
+ ... ['1.24', '2015-05-26', '223'],
+ ... ['1.25', '2015-05-26', '4319'],
+ ... ['1.25.1', '2015-06-02', '197'],
+ ... ]
+ >>> print(format_pretty_table(humanfriendly_releases, column_names))
+ -------------------------------------
+ | Version | Uploaded on | Downloads |
+ -------------------------------------
+ | 1.23 | 2015-05-25 | 218 |
+ | 1.23.1 | 2015-05-26 | 1354 |
+ | 1.24 | 2015-05-26 | 223 |
+ | 1.25 | 2015-05-26 | 4319 |
+ | 1.25.1 | 2015-06-02 | 197 |
+ -------------------------------------
+
+ Notes about the resulting table:
+
+ - If a column contains numeric data (integer and/or floating point
+ numbers) in all rows (ignoring column names of course) then the content
+ of that column is right-aligned, as can be seen in the example above. The
+ idea here is to make it easier to compare the numbers in different
+ columns to each other.
+
+ - The column names are highlighted in color so they stand out a bit more
+ (see also :data:`.HIGHLIGHT_COLOR`). The following screen shot shows what
+ that looks like (my terminals are always set to white text on a black
+ background):
+
+ .. image:: images/pretty-table.png
+ """
+ # Normalize the input because we'll have to iterate it more than once.
+ data = [normalize_columns(r, expandtabs=True) for r in data]
+ if column_names is not None:
+ column_names = normalize_columns(column_names)
+ if column_names:
+ if terminal_supports_colors():
+ column_names = [highlight_column_name(n) for n in column_names]
+ data.insert(0, column_names)
+ # Calculate the maximum width of each column.
+ widths = collections.defaultdict(int)
+ numeric_data = collections.defaultdict(list)
+ for row_index, row in enumerate(data):
+ for column_index, column in enumerate(row):
+ widths[column_index] = max(widths[column_index], ansi_width(column))
+ if not (column_names and row_index == 0):
+ numeric_data[column_index].append(bool(NUMERIC_DATA_PATTERN.match(ansi_strip(column))))
+ # Create a horizontal bar of dashes as a delimiter.
+ line_delimiter = horizontal_bar * (sum(widths.values()) + len(widths) * 3 + 1)
+ # Start the table with a vertical bar.
+ lines = [line_delimiter]
+ # Format the rows and columns.
+ for row_index, row in enumerate(data):
+ line = [vertical_bar]
+ for column_index, column in enumerate(row):
+ padding = ' ' * (widths[column_index] - ansi_width(column))
+ if all(numeric_data[column_index]):
+ line.append(' ' + padding + column + ' ')
+ else:
+ line.append(' ' + column + padding + ' ')
+ line.append(vertical_bar)
+ lines.append(u''.join(line))
+ if column_names and row_index == 0:
+ lines.append(line_delimiter)
+ # End the table with a vertical bar.
+ lines.append(line_delimiter)
+ # Join the lines, returning a single string.
+ return u'\n'.join(lines)
+
+
+def format_robust_table(data, column_names):
+ """
+ Render tabular data with one column per line (allowing columns with line breaks).
+
+ :param data: An iterable (e.g. a :func:`tuple` or :class:`list`)
+ containing the rows of the table, where each row is an
+ iterable containing the columns of the table (strings).
+ :param column_names: An iterable of column names (strings).
+ :returns: The rendered table (a string).
+
+ Here's an example:
+
+ >>> from humanfriendly.tables import format_robust_table
+ >>> column_names = ['Version', 'Uploaded on', 'Downloads']
+ >>> humanfriendly_releases = [
+ ... ['1.23', '2015-05-25', '218'],
+ ... ['1.23.1', '2015-05-26', '1354'],
+ ... ['1.24', '2015-05-26', '223'],
+ ... ['1.25', '2015-05-26', '4319'],
+ ... ['1.25.1', '2015-06-02', '197'],
+ ... ]
+ >>> print(format_robust_table(humanfriendly_releases, column_names))
+ -----------------------
+ Version: 1.23
+ Uploaded on: 2015-05-25
+ Downloads: 218
+ -----------------------
+ Version: 1.23.1
+ Uploaded on: 2015-05-26
+ Downloads: 1354
+ -----------------------
+ Version: 1.24
+ Uploaded on: 2015-05-26
+ Downloads: 223
+ -----------------------
+ Version: 1.25
+ Uploaded on: 2015-05-26
+ Downloads: 4319
+ -----------------------
+ Version: 1.25.1
+ Uploaded on: 2015-06-02
+ Downloads: 197
+ -----------------------
+
+ The column names are highlighted in bold font and color so they stand out a
+ bit more (see :data:`.HIGHLIGHT_COLOR`).
+ """
+ blocks = []
+ column_names = ["%s:" % n for n in normalize_columns(column_names)]
+ if terminal_supports_colors():
+ column_names = [highlight_column_name(n) for n in column_names]
+ # Convert each row into one or more `name: value' lines (one per column)
+ # and group each `row of lines' into a block (i.e. rows become blocks).
+ for row in data:
+ lines = []
+ for column_index, column_text in enumerate(normalize_columns(row)):
+ stripped_column = column_text.strip()
+ if '\n' not in stripped_column:
+ # Columns without line breaks are formatted inline.
+ lines.append("%s %s" % (column_names[column_index], stripped_column))
+ else:
+ # Columns with line breaks could very well contain indented
+ # lines, so we'll put the column name on a separate line. This
+ # way any indentation remains intact, and it's easier to
+ # copy/paste the text.
+ lines.append(column_names[column_index])
+ lines.extend(column_text.rstrip().splitlines())
+ blocks.append(lines)
+ # Calculate the width of the row delimiter.
+ num_rows, num_columns = find_terminal_size()
+ longest_line = max(max(map(ansi_width, lines)) for lines in blocks)
+ delimiter = u"\n%s\n" % ('-' * min(longest_line, num_columns))
+ # Force a delimiter at the start and end of the table.
+ blocks.insert(0, "")
+ blocks.append("")
+ # Embed the row delimiter between every two blocks.
+ return delimiter.join(u"\n".join(b) for b in blocks).strip()
+
+
+def format_rst_table(data, column_names=None):
+ """
+ Render a table in reStructuredText_ format.
+
+ :param data: An iterable (e.g. a :func:`tuple` or :class:`list`)
+ containing the rows of the table, where each row is an
+ iterable containing the columns of the table (strings).
+ :param column_names: An iterable of column names (strings).
+ :returns: The rendered table (a string).
+
+ Here's an example:
+
+ >>> from humanfriendly.tables import format_rst_table
+ >>> column_names = ['Version', 'Uploaded on', 'Downloads']
+ >>> humanfriendly_releases = [
+ ... ['1.23', '2015-05-25', '218'],
+ ... ['1.23.1', '2015-05-26', '1354'],
+ ... ['1.24', '2015-05-26', '223'],
+ ... ['1.25', '2015-05-26', '4319'],
+ ... ['1.25.1', '2015-06-02', '197'],
+ ... ]
+ >>> print(format_rst_table(humanfriendly_releases, column_names))
+ ======= =========== =========
+ Version Uploaded on Downloads
+ ======= =========== =========
+ 1.23 2015-05-25 218
+ 1.23.1 2015-05-26 1354
+ 1.24 2015-05-26 223
+ 1.25 2015-05-26 4319
+ 1.25.1 2015-06-02 197
+ ======= =========== =========
+
+ .. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText
+ """
+ data = [normalize_columns(r) for r in data]
+ if column_names:
+ data.insert(0, normalize_columns(column_names))
+ # Calculate the maximum width of each column.
+ widths = collections.defaultdict(int)
+ for row in data:
+ for index, column in enumerate(row):
+ widths[index] = max(widths[index], len(column))
+ # Pad the columns using whitespace.
+ for row in data:
+ for index, column in enumerate(row):
+ if index < (len(row) - 1):
+ row[index] = column.ljust(widths[index])
+ # Add table markers.
+ delimiter = ['=' * w for i, w in sorted(widths.items())]
+ if column_names:
+ data.insert(1, delimiter)
+ data.insert(0, delimiter)
+ data.append(delimiter)
+ # Join the lines and columns together.
+ return '\n'.join(' '.join(r) for r in data)
+
+
+def normalize_columns(row, expandtabs=False):
+ results = []
+ for value in row:
+ text = coerce_string(value)
+ if expandtabs:
+ text = text.expandtabs()
+ results.append(text)
+ return results
+
+
+def highlight_column_name(name):
+ return ansi_wrap(name, bold=True, color=HIGHLIGHT_COLOR)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/terminal/__init__.py b/contrib/python/humanfriendly/py3/humanfriendly/terminal/__init__.py
new file mode 100644
index 0000000000..ba9739ccb2
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/terminal/__init__.py
@@ -0,0 +1,776 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: March 1, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Interaction with interactive text terminals.
+
+The :mod:`~humanfriendly.terminal` module makes it easy to interact with
+interactive text terminals and format text for rendering on such terminals. If
+the terms used in the documentation of this module don't make sense to you then
+please refer to the `Wikipedia article on ANSI escape sequences`_ for details
+about how ANSI escape sequences work.
+
+This module was originally developed for use on UNIX systems, but since then
+Windows 10 gained native support for ANSI escape sequences and this module was
+enhanced to recognize and support this. For details please refer to the
+:func:`enable_ansi_support()` function.
+
+.. _Wikipedia article on ANSI escape sequences: http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
+"""
+
+# Standard library modules.
+import codecs
+import numbers
+import os
+import platform
+import re
+import subprocess
+import sys
+
+# The `fcntl' module is platform specific so importing it may give an error. We
+# hide this implementation detail from callers by handling the import error and
+# setting a flag instead.
+try:
+ import fcntl
+ import termios
+ import struct
+ HAVE_IOCTL = True
+except ImportError:
+ HAVE_IOCTL = False
+
+# Modules included in our package.
+from humanfriendly.compat import coerce_string, is_unicode, on_windows, which
+from humanfriendly.decorators import cached
+from humanfriendly.deprecation import define_aliases
+from humanfriendly.text import concatenate, format
+from humanfriendly.usage import format_usage
+
+# Public identifiers that require documentation.
+__all__ = (
+ 'ANSI_COLOR_CODES',
+ 'ANSI_CSI',
+ 'ANSI_ERASE_LINE',
+ 'ANSI_HIDE_CURSOR',
+ 'ANSI_RESET',
+ 'ANSI_SGR',
+ 'ANSI_SHOW_CURSOR',
+ 'ANSI_TEXT_STYLES',
+ 'CLEAN_OUTPUT_PATTERN',
+ 'DEFAULT_COLUMNS',
+ 'DEFAULT_ENCODING',
+ 'DEFAULT_LINES',
+ 'HIGHLIGHT_COLOR',
+ 'ansi_strip',
+ 'ansi_style',
+ 'ansi_width',
+ 'ansi_wrap',
+ 'auto_encode',
+ 'clean_terminal_output',
+ 'connected_to_terminal',
+ 'enable_ansi_support',
+ 'find_terminal_size',
+ 'find_terminal_size_using_ioctl',
+ 'find_terminal_size_using_stty',
+ 'get_pager_command',
+ 'have_windows_native_ansi_support',
+ 'message',
+ 'output',
+ 'readline_strip',
+ 'readline_wrap',
+ 'show_pager',
+ 'terminal_supports_colors',
+ 'usage',
+ 'warning',
+)
+
+ANSI_CSI = '\x1b['
+"""The ANSI "Control Sequence Introducer" (a string)."""
+
+ANSI_SGR = 'm'
+"""The ANSI "Select Graphic Rendition" sequence (a string)."""
+
+ANSI_ERASE_LINE = '%sK' % ANSI_CSI
+"""The ANSI escape sequence to erase the current line (a string)."""
+
+ANSI_RESET = '%s0%s' % (ANSI_CSI, ANSI_SGR)
+"""The ANSI escape sequence to reset styling (a string)."""
+
+ANSI_HIDE_CURSOR = '%s?25l' % ANSI_CSI
+"""The ANSI escape sequence to hide the text cursor (a string)."""
+
+ANSI_SHOW_CURSOR = '%s?25h' % ANSI_CSI
+"""The ANSI escape sequence to show the text cursor (a string)."""
+
+ANSI_COLOR_CODES = dict(black=0, red=1, green=2, yellow=3, blue=4, magenta=5, cyan=6, white=7)
+"""
+A dictionary with (name, number) pairs of `portable color codes`_. Used by
+:func:`ansi_style()` to generate ANSI escape sequences that change font color.
+
+.. _portable color codes: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
+"""
+
+ANSI_TEXT_STYLES = dict(bold=1, faint=2, italic=3, underline=4, inverse=7, strike_through=9)
+"""
+A dictionary with (name, number) pairs of text styles (effects). Used by
+:func:`ansi_style()` to generate ANSI escape sequences that change text
+styles. Only widely supported text styles are included here.
+"""
+
+CLEAN_OUTPUT_PATTERN = re.compile(u'(\r|\n|\b|%s)' % re.escape(ANSI_ERASE_LINE))
+"""
+A compiled regular expression used to separate significant characters from other text.
+
+This pattern is used by :func:`clean_terminal_output()` to split terminal
+output into regular text versus backspace, carriage return and line feed
+characters and ANSI 'erase line' escape sequences.
+"""
+
+DEFAULT_LINES = 25
+"""The default number of lines in a terminal (an integer)."""
+
+DEFAULT_COLUMNS = 80
+"""The default number of columns in a terminal (an integer)."""
+
+DEFAULT_ENCODING = 'UTF-8'
+"""The output encoding for Unicode strings."""
+
+HIGHLIGHT_COLOR = os.environ.get('HUMANFRIENDLY_HIGHLIGHT_COLOR', 'green')
+"""
+The color used to highlight important tokens in formatted text (e.g. the usage
+message of the ``humanfriendly`` program). If the environment variable
+``$HUMANFRIENDLY_HIGHLIGHT_COLOR`` is set it determines the value of
+:data:`HIGHLIGHT_COLOR`.
+"""
+
+
+def ansi_strip(text, readline_hints=True):
+ """
+ Strip ANSI escape sequences from the given string.
+
+ :param text: The text from which ANSI escape sequences should be removed (a
+ string).
+ :param readline_hints: If :data:`True` then :func:`readline_strip()` is
+ used to remove `readline hints`_ from the string.
+ :returns: The text without ANSI escape sequences (a string).
+ """
+ pattern = '%s.*?%s' % (re.escape(ANSI_CSI), re.escape(ANSI_SGR))
+ text = re.sub(pattern, '', text)
+ if readline_hints:
+ text = readline_strip(text)
+ return text
+
+
+def ansi_style(**kw):
+ """
+ Generate ANSI escape sequences for the given color and/or style(s).
+
+ :param color: The foreground color. Three types of values are supported:
+
+ - The name of a color (one of the strings 'black', 'red',
+ 'green', 'yellow', 'blue', 'magenta', 'cyan' or 'white').
+ - An integer that refers to the 256 color mode palette.
+ - A tuple or list with three integers representing an RGB
+ (red, green, blue) value.
+
+ The value :data:`None` (the default) means no escape
+ sequence to switch color will be emitted.
+ :param background: The background color (see the description
+ of the `color` argument).
+ :param bright: Use high intensity colors instead of default colors
+ (a boolean, defaults to :data:`False`).
+ :param readline_hints: If :data:`True` then :func:`readline_wrap()` is
+ applied to the generated ANSI escape sequences (the
+ default is :data:`False`).
+ :param kw: Any additional keyword arguments are expected to match a key
+ in the :data:`ANSI_TEXT_STYLES` dictionary. If the argument's
+ value evaluates to :data:`True` the respective style will be
+ enabled.
+ :returns: The ANSI escape sequences to enable the requested text styles or
+ an empty string if no styles were requested.
+ :raises: :exc:`~exceptions.ValueError` when an invalid color name is given.
+
+ Even though only eight named colors are supported, the use of `bright=True`
+ and `faint=True` increases the number of available colors to around 24 (it
+ may be slightly lower, for example because faint black is just black).
+
+ **Support for 8-bit colors**
+
+ In `release 4.7`_ support for 256 color mode was added. While this
+ significantly increases the available colors it's not very human friendly
+ in usage because you need to look up color codes in the `256 color mode
+ palette <https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit>`_.
+
+ You can use the ``humanfriendly --demo`` command to get a demonstration of
+ the available colors, see also the screen shot below. Note that the small
+ font size in the screen shot was so that the demonstration of 256 color
+ mode support would fit into a single screen shot without scrolling :-)
+ (I wasn't feeling very creative).
+
+ .. image:: images/ansi-demo.png
+
+ **Support for 24-bit colors**
+
+ In `release 4.14`_ support for 24-bit colors was added by accepting a tuple
+ or list with three integers representing the RGB (red, green, blue) value
+ of a color. This is not included in the demo because rendering millions of
+ colors was deemed unpractical ;-).
+
+ .. _release 4.7: http://humanfriendly.readthedocs.io/en/latest/changelog.html#release-4-7-2018-01-14
+ .. _release 4.14: http://humanfriendly.readthedocs.io/en/latest/changelog.html#release-4-14-2018-07-13
+ """
+ # Start with sequences that change text styles.
+ sequences = [ANSI_TEXT_STYLES[k] for k, v in kw.items() if k in ANSI_TEXT_STYLES and v]
+ # Append the color code (if any).
+ for color_type in 'color', 'background':
+ color_value = kw.get(color_type)
+ if isinstance(color_value, (tuple, list)):
+ if len(color_value) != 3:
+ msg = "Invalid color value %r! (expected tuple or list with three numbers)"
+ raise ValueError(msg % color_value)
+ sequences.append(48 if color_type == 'background' else 38)
+ sequences.append(2)
+ sequences.extend(map(int, color_value))
+ elif isinstance(color_value, numbers.Number):
+ # Numeric values are assumed to be 256 color codes.
+ sequences.extend((
+ 39 if color_type == 'background' else 38,
+ 5, int(color_value)
+ ))
+ elif color_value:
+ # Other values are assumed to be strings containing one of the known color names.
+ if color_value not in ANSI_COLOR_CODES:
+ msg = "Invalid color value %r! (expected an integer or one of the strings %s)"
+ raise ValueError(msg % (color_value, concatenate(map(repr, sorted(ANSI_COLOR_CODES)))))
+ # Pick the right offset for foreground versus background
+ # colors and regular intensity versus bright colors.
+ offset = (
+ (100 if kw.get('bright') else 40)
+ if color_type == 'background'
+ else (90 if kw.get('bright') else 30)
+ )
+ # Combine the offset and color code into a single integer.
+ sequences.append(offset + ANSI_COLOR_CODES[color_value])
+ if sequences:
+ encoded = ANSI_CSI + ';'.join(map(str, sequences)) + ANSI_SGR
+ return readline_wrap(encoded) if kw.get('readline_hints') else encoded
+ else:
+ return ''
+
+
+def ansi_width(text):
+ """
+ Calculate the effective width of the given text (ignoring ANSI escape sequences).
+
+ :param text: The text whose width should be calculated (a string).
+ :returns: The width of the text without ANSI escape sequences (an
+ integer).
+
+ This function uses :func:`ansi_strip()` to strip ANSI escape sequences from
+ the given string and returns the length of the resulting string.
+ """
+ return len(ansi_strip(text))
+
+
+def ansi_wrap(text, **kw):
+ """
+ Wrap text in ANSI escape sequences for the given color and/or style(s).
+
+ :param text: The text to wrap (a string).
+ :param kw: Any keyword arguments are passed to :func:`ansi_style()`.
+ :returns: The result of this function depends on the keyword arguments:
+
+ - If :func:`ansi_style()` generates an ANSI escape sequence based
+ on the keyword arguments, the given text is prefixed with the
+ generated ANSI escape sequence and suffixed with
+ :data:`ANSI_RESET`.
+
+ - If :func:`ansi_style()` returns an empty string then the text
+ given by the caller is returned unchanged.
+ """
+ start_sequence = ansi_style(**kw)
+ if start_sequence:
+ end_sequence = ANSI_RESET
+ if kw.get('readline_hints'):
+ end_sequence = readline_wrap(end_sequence)
+ return start_sequence + text + end_sequence
+ else:
+ return text
+
+
+def auto_encode(stream, text, *args, **kw):
+ """
+ Reliably write Unicode strings to the terminal.
+
+ :param stream: The file-like object to write to (a value like
+ :data:`sys.stdout` or :data:`sys.stderr`).
+ :param text: The text to write to the stream (a string).
+ :param args: Refer to :func:`~humanfriendly.text.format()`.
+ :param kw: Refer to :func:`~humanfriendly.text.format()`.
+
+ Renders the text using :func:`~humanfriendly.text.format()` and writes it
+ to the given stream. If an :exc:`~exceptions.UnicodeEncodeError` is
+ encountered in doing so, the text is encoded using :data:`DEFAULT_ENCODING`
+ and the write is retried. The reasoning behind this rather blunt approach
+ is that it's preferable to get output on the command line in the wrong
+ encoding then to have the Python program blow up with a
+ :exc:`~exceptions.UnicodeEncodeError` exception.
+ """
+ text = format(text, *args, **kw)
+ try:
+ stream.write(text)
+ except UnicodeEncodeError:
+ stream.write(codecs.encode(text, DEFAULT_ENCODING))
+
+
+def clean_terminal_output(text):
+ """
+ Clean up the terminal output of a command.
+
+ :param text: The raw text with special characters (a Unicode string).
+ :returns: A list of Unicode strings (one for each line).
+
+ This function emulates the effect of backspace (0x08), carriage return
+ (0x0D) and line feed (0x0A) characters and the ANSI 'erase line' escape
+ sequence on interactive terminals. It's intended to clean up command output
+ that was originally meant to be rendered on an interactive terminal and
+ that has been captured using e.g. the :man:`script` program [#]_ or the
+ :mod:`pty` module [#]_.
+
+ .. [#] My coloredlogs_ package supports the ``coloredlogs --to-html``
+ command which uses :man:`script` to fool a subprocess into thinking
+ that it's connected to an interactive terminal (in order to get it
+ to emit ANSI escape sequences).
+
+ .. [#] My capturer_ package uses the :mod:`pty` module to fool the current
+ process and subprocesses into thinking they are connected to an
+ interactive terminal (in order to get them to emit ANSI escape
+ sequences).
+
+ **Some caveats about the use of this function:**
+
+ - Strictly speaking the effect of carriage returns cannot be emulated
+ outside of an actual terminal due to the interaction between overlapping
+ output, terminal widths and line wrapping. The goal of this function is
+ to sanitize noise in terminal output while preserving useful output.
+ Think of it as a useful and pragmatic but possibly lossy conversion.
+
+ - The algorithm isn't smart enough to properly handle a pair of ANSI escape
+ sequences that open before a carriage return and close after the last
+ carriage return in a linefeed delimited string; the resulting string will
+ contain only the closing end of the ANSI escape sequence pair. Tracking
+ this kind of complexity requires a state machine and proper parsing.
+
+ .. _capturer: https://pypi.org/project/capturer
+ .. _coloredlogs: https://pypi.org/project/coloredlogs
+ """
+ cleaned_lines = []
+ current_line = ''
+ current_position = 0
+ for token in CLEAN_OUTPUT_PATTERN.split(text):
+ if token == '\r':
+ # Seek back to the start of the current line.
+ current_position = 0
+ elif token == '\b':
+ # Seek back one character in the current line.
+ current_position = max(0, current_position - 1)
+ else:
+ if token == '\n':
+ # Capture the current line.
+ cleaned_lines.append(current_line)
+ if token in ('\n', ANSI_ERASE_LINE):
+ # Clear the current line.
+ current_line = ''
+ current_position = 0
+ elif token:
+ # Merge regular output into the current line.
+ new_position = current_position + len(token)
+ prefix = current_line[:current_position]
+ suffix = current_line[new_position:]
+ current_line = prefix + token + suffix
+ current_position = new_position
+ # Capture the last line (if any).
+ cleaned_lines.append(current_line)
+ # Remove any empty trailing lines.
+ while cleaned_lines and not cleaned_lines[-1]:
+ cleaned_lines.pop(-1)
+ return cleaned_lines
+
+
+def connected_to_terminal(stream=None):
+ """
+ Check if a stream is connected to a terminal.
+
+ :param stream: The stream to check (a file-like object,
+ defaults to :data:`sys.stdout`).
+ :returns: :data:`True` if the stream is connected to a terminal,
+ :data:`False` otherwise.
+
+ See also :func:`terminal_supports_colors()`.
+ """
+ stream = sys.stdout if stream is None else stream
+ try:
+ return stream.isatty()
+ except Exception:
+ return False
+
+
+@cached
+def enable_ansi_support():
+ """
+ Try to enable support for ANSI escape sequences (required on Windows).
+
+ :returns: :data:`True` if ANSI is supported, :data:`False` otherwise.
+
+ This functions checks for the following supported configurations, in the
+ given order:
+
+ 1. On Windows, if :func:`have_windows_native_ansi_support()` confirms
+ native support for ANSI escape sequences :mod:`ctypes` will be used to
+ enable this support.
+
+ 2. On Windows, if the environment variable ``$ANSICON`` is set nothing is
+ done because it is assumed that support for ANSI escape sequences has
+ already been enabled via `ansicon <https://github.com/adoxa/ansicon>`_.
+
+ 3. On Windows, an attempt is made to import and initialize the Python
+ package :pypi:`colorama` instead (of course for this to work
+ :pypi:`colorama` has to be installed).
+
+ 4. On other platforms this function calls :func:`connected_to_terminal()`
+ to determine whether ANSI escape sequences are supported (that is to
+ say all platforms that are not Windows are assumed to support ANSI
+ escape sequences natively, without weird contortions like above).
+
+ This makes it possible to call :func:`enable_ansi_support()`
+ unconditionally without checking the current platform.
+
+ The :func:`~humanfriendly.decorators.cached` decorator is used to ensure
+ that this function is only executed once, but its return value remains
+ available on later calls.
+ """
+ if have_windows_native_ansi_support():
+ import ctypes
+ ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-11), 7)
+ ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-12), 7)
+ return True
+ elif on_windows():
+ if 'ANSICON' in os.environ:
+ return True
+ try:
+ import colorama
+ colorama.init()
+ return True
+ except ImportError:
+ return False
+ else:
+ return connected_to_terminal()
+
+
+def find_terminal_size():
+ """
+ Determine the number of lines and columns visible in the terminal.
+
+ :returns: A tuple of two integers with the line and column count.
+
+ The result of this function is based on the first of the following three
+ methods that works:
+
+ 1. First :func:`find_terminal_size_using_ioctl()` is tried,
+ 2. then :func:`find_terminal_size_using_stty()` is tried,
+ 3. finally :data:`DEFAULT_LINES` and :data:`DEFAULT_COLUMNS` are returned.
+
+ .. note:: The :func:`find_terminal_size()` function performs the steps
+ above every time it is called, the result is not cached. This is
+ because the size of a virtual terminal can change at any time and
+ the result of :func:`find_terminal_size()` should be correct.
+
+ `Pre-emptive snarky comment`_: It's possible to cache the result
+ of this function and use :mod:`signal.SIGWINCH <signal>` to
+ refresh the cached values!
+
+ Response: As a library I don't consider it the role of the
+ :mod:`humanfriendly.terminal` module to install a process wide
+ signal handler ...
+
+ .. _Pre-emptive snarky comment: http://blogs.msdn.com/b/oldnewthing/archive/2008/01/30/7315957.aspx
+ """
+ # The first method. Any of the standard streams may have been redirected
+ # somewhere and there's no telling which, so we'll just try them all.
+ for stream in sys.stdin, sys.stdout, sys.stderr:
+ try:
+ result = find_terminal_size_using_ioctl(stream)
+ if min(result) >= 1:
+ return result
+ except Exception:
+ pass
+ # The second method.
+ try:
+ result = find_terminal_size_using_stty()
+ if min(result) >= 1:
+ return result
+ except Exception:
+ pass
+ # Fall back to conservative defaults.
+ return DEFAULT_LINES, DEFAULT_COLUMNS
+
+
+def find_terminal_size_using_ioctl(stream):
+ """
+ Find the terminal size using :func:`fcntl.ioctl()`.
+
+ :param stream: A stream connected to the terminal (a file object with a
+ ``fileno`` attribute).
+ :returns: A tuple of two integers with the line and column count.
+ :raises: This function can raise exceptions but I'm not going to document
+ them here, you should be using :func:`find_terminal_size()`.
+
+ Based on an `implementation found on StackOverflow <http://stackoverflow.com/a/3010495/788200>`_.
+ """
+ if not HAVE_IOCTL:
+ raise NotImplementedError("It looks like the `fcntl' module is not available!")
+ h, w, hp, wp = struct.unpack('HHHH', fcntl.ioctl(stream, termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0)))
+ return h, w
+
+
+def find_terminal_size_using_stty():
+ """
+ Find the terminal size using the external command ``stty size``.
+
+ :param stream: A stream connected to the terminal (a file object).
+ :returns: A tuple of two integers with the line and column count.
+ :raises: This function can raise exceptions but I'm not going to document
+ them here, you should be using :func:`find_terminal_size()`.
+ """
+ stty = subprocess.Popen(['stty', 'size'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = stty.communicate()
+ tokens = stdout.split()
+ if len(tokens) != 2:
+ raise Exception("Invalid output from `stty size'!")
+ return tuple(map(int, tokens))
+
+
+def get_pager_command(text=None):
+ """
+ Get the command to show a text on the terminal using a pager.
+
+ :param text: The text to print to the terminal (a string).
+ :returns: A list of strings with the pager command and arguments.
+
+ The use of a pager helps to avoid the wall of text effect where the user
+ has to scroll up to see where the output began (not very user friendly).
+
+ If the given text contains ANSI escape sequences the command ``less
+ --RAW-CONTROL-CHARS`` is used, otherwise the environment variable
+ ``$PAGER`` is used (if ``$PAGER`` isn't set :man:`less` is used).
+
+ When the selected pager is :man:`less`, the following options are used to
+ make the experience more user friendly:
+
+ - ``--quit-if-one-screen`` causes :man:`less` to automatically exit if the
+ entire text can be displayed on the first screen. This makes the use of a
+ pager transparent for smaller texts (because the operator doesn't have to
+ quit the pager).
+
+ - ``--no-init`` prevents :man:`less` from clearing the screen when it
+ exits. This ensures that the operator gets a chance to review the text
+ (for example a usage message) after quitting the pager, while composing
+ the next command.
+ """
+ # Compose the pager command.
+ if text and ANSI_CSI in text:
+ command_line = ['less', '--RAW-CONTROL-CHARS']
+ else:
+ command_line = [os.environ.get('PAGER', 'less')]
+ # Pass some additional options to `less' (to make it more
+ # user friendly) without breaking support for other pagers.
+ if os.path.basename(command_line[0]) == 'less':
+ command_line.append('--no-init')
+ command_line.append('--quit-if-one-screen')
+ return command_line
+
+
+@cached
+def have_windows_native_ansi_support():
+ """
+ Check if we're running on a Windows 10 release with native support for ANSI escape sequences.
+
+ :returns: :data:`True` if so, :data:`False` otherwise.
+
+ The :func:`~humanfriendly.decorators.cached` decorator is used as a minor
+ performance optimization. Semantically this should have zero impact because
+ the answer doesn't change in the lifetime of a computer process.
+ """
+ if on_windows():
+ try:
+ # I can't be 100% sure this will never break and I'm not in a
+ # position to test it thoroughly either, so I decided that paying
+ # the price of one additional try / except statement is worth the
+ # additional peace of mind :-).
+ components = tuple(int(c) for c in platform.version().split('.'))
+ return components >= (10, 0, 14393)
+ except Exception:
+ pass
+ return False
+
+
+def message(text, *args, **kw):
+ """
+ Print a formatted message to the standard error stream.
+
+ For details about argument handling please refer to
+ :func:`~humanfriendly.text.format()`.
+
+ Renders the message using :func:`~humanfriendly.text.format()` and writes
+ the resulting string (followed by a newline) to :data:`sys.stderr` using
+ :func:`auto_encode()`.
+ """
+ auto_encode(sys.stderr, coerce_string(text) + '\n', *args, **kw)
+
+
+def output(text, *args, **kw):
+ """
+ Print a formatted message to the standard output stream.
+
+ For details about argument handling please refer to
+ :func:`~humanfriendly.text.format()`.
+
+ Renders the message using :func:`~humanfriendly.text.format()` and writes
+ the resulting string (followed by a newline) to :data:`sys.stdout` using
+ :func:`auto_encode()`.
+ """
+ auto_encode(sys.stdout, coerce_string(text) + '\n', *args, **kw)
+
+
+def readline_strip(expr):
+ """
+ Remove `readline hints`_ from a string.
+
+ :param text: The text to strip (a string).
+ :returns: The stripped text.
+ """
+ return expr.replace('\001', '').replace('\002', '')
+
+
+def readline_wrap(expr):
+ """
+ Wrap an ANSI escape sequence in `readline hints`_.
+
+ :param text: The text with the escape sequence to wrap (a string).
+ :returns: The wrapped text.
+
+ .. _readline hints: http://superuser.com/a/301355
+ """
+ return '\001' + expr + '\002'
+
+
+def show_pager(formatted_text, encoding=DEFAULT_ENCODING):
+ """
+ Print a large text to the terminal using a pager.
+
+ :param formatted_text: The text to print to the terminal (a string).
+ :param encoding: The name of the text encoding used to encode the formatted
+ text if the formatted text is a Unicode string (a string,
+ defaults to :data:`DEFAULT_ENCODING`).
+
+ When :func:`connected_to_terminal()` returns :data:`True` a pager is used
+ to show the text on the terminal, otherwise the text is printed directly
+ without invoking a pager.
+
+ The use of a pager helps to avoid the wall of text effect where the user
+ has to scroll up to see where the output began (not very user friendly).
+
+ Refer to :func:`get_pager_command()` for details about the command line
+ that's used to invoke the pager.
+ """
+ if connected_to_terminal():
+ # Make sure the selected pager command is available.
+ command_line = get_pager_command(formatted_text)
+ if which(command_line[0]):
+ pager = subprocess.Popen(command_line, stdin=subprocess.PIPE)
+ if is_unicode(formatted_text):
+ formatted_text = formatted_text.encode(encoding)
+ pager.communicate(input=formatted_text)
+ return
+ output(formatted_text)
+
+
+def terminal_supports_colors(stream=None):
+ """
+ Check if a stream is connected to a terminal that supports ANSI escape sequences.
+
+ :param stream: The stream to check (a file-like object,
+ defaults to :data:`sys.stdout`).
+ :returns: :data:`True` if the terminal supports ANSI escape sequences,
+ :data:`False` otherwise.
+
+ This function was originally inspired by the implementation of
+ `django.core.management.color.supports_color()
+ <https://github.com/django/django/blob/master/django/core/management/color.py>`_
+ but has since evolved significantly.
+ """
+ if on_windows():
+ # On Windows support for ANSI escape sequences is not a given.
+ have_ansicon = 'ANSICON' in os.environ
+ have_colorama = 'colorama' in sys.modules
+ have_native_support = have_windows_native_ansi_support()
+ if not (have_ansicon or have_colorama or have_native_support):
+ return False
+ return connected_to_terminal(stream)
+
+
+def usage(usage_text):
+ """
+ Print a human friendly usage message to the terminal.
+
+ :param text: The usage message to print (a string).
+
+ This function does two things:
+
+ 1. If :data:`sys.stdout` is connected to a terminal (see
+ :func:`connected_to_terminal()`) then the usage message is formatted
+ using :func:`.format_usage()`.
+ 2. The usage message is shown using a pager (see :func:`show_pager()`).
+ """
+ if terminal_supports_colors(sys.stdout):
+ usage_text = format_usage(usage_text)
+ show_pager(usage_text)
+
+
+def warning(text, *args, **kw):
+ """
+ Show a warning message on the terminal.
+
+ For details about argument handling please refer to
+ :func:`~humanfriendly.text.format()`.
+
+ Renders the message using :func:`~humanfriendly.text.format()` and writes
+ the resulting string (followed by a newline) to :data:`sys.stderr` using
+ :func:`auto_encode()`.
+
+ If :data:`sys.stderr` is connected to a terminal that supports colors,
+ :func:`ansi_wrap()` is used to color the message in a red font (to make
+ the warning stand out from surrounding text).
+ """
+ text = coerce_string(text)
+ if terminal_supports_colors(sys.stderr):
+ text = ansi_wrap(text, color='red')
+ auto_encode(sys.stderr, text + '\n', *args, **kw)
+
+
+# Define aliases for backwards compatibility.
+define_aliases(
+ module_name=__name__,
+ # In humanfriendly 1.31 the find_meta_variables() and format_usage()
+ # functions were extracted to the new module humanfriendly.usage.
+ find_meta_variables='humanfriendly.usage.find_meta_variables',
+ format_usage='humanfriendly.usage.format_usage',
+ # In humanfriendly 8.0 the html_to_ansi() function and HTMLConverter
+ # class were extracted to the new module humanfriendly.terminal.html.
+ html_to_ansi='humanfriendly.terminal.html.html_to_ansi',
+ HTMLConverter='humanfriendly.terminal.html.HTMLConverter',
+)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/terminal/html.py b/contrib/python/humanfriendly/py3/humanfriendly/terminal/html.py
new file mode 100644
index 0000000000..4214e09e70
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/terminal/html.py
@@ -0,0 +1,423 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: February 29, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""Convert HTML with simple text formatting to text with ANSI escape sequences."""
+
+# Standard library modules.
+import re
+
+# Modules included in our package.
+from humanfriendly.compat import HTMLParser, StringIO, name2codepoint, unichr
+from humanfriendly.text import compact_empty_lines
+from humanfriendly.terminal import ANSI_COLOR_CODES, ANSI_RESET, ansi_style
+
+# Public identifiers that require documentation.
+__all__ = ('HTMLConverter', 'html_to_ansi')
+
+
+def html_to_ansi(data, callback=None):
+ """
+ Convert HTML with simple text formatting to text with ANSI escape sequences.
+
+ :param data: The HTML to convert (a string).
+ :param callback: Optional callback to pass to :class:`HTMLConverter`.
+ :returns: Text with ANSI escape sequences (a string).
+
+ Please refer to the documentation of the :class:`HTMLConverter` class for
+ details about the conversion process (like which tags are supported) and an
+ example with a screenshot.
+ """
+ converter = HTMLConverter(callback=callback)
+ return converter(data)
+
+
+class HTMLConverter(HTMLParser):
+
+ """
+ Convert HTML with simple text formatting to text with ANSI escape sequences.
+
+ The following text styles are supported:
+
+ - Bold: ``<b>``, ``<strong>`` and ``<span style="font-weight: bold;">``
+ - Italic: ``<i>``, ``<em>`` and ``<span style="font-style: italic;">``
+ - Strike-through: ``<del>``, ``<s>`` and ``<span style="text-decoration: line-through;">``
+ - Underline: ``<ins>``, ``<u>`` and ``<span style="text-decoration: underline">``
+
+ Colors can be specified as follows:
+
+ - Foreground color: ``<span style="color: #RRGGBB;">``
+ - Background color: ``<span style="background-color: #RRGGBB;">``
+
+ Here's a small demonstration:
+
+ .. code-block:: python
+
+ from humanfriendly.text import dedent
+ from humanfriendly.terminal import html_to_ansi
+
+ print(html_to_ansi(dedent('''
+ <b>Hello world!</b>
+ <i>Is this thing on?</i>
+ I guess I can <u>underline</u> or <s>strike-through</s> text?
+ And what about <span style="color: red">color</span>?
+ ''')))
+
+ rainbow_colors = [
+ '#FF0000', '#E2571E', '#FF7F00', '#FFFF00', '#00FF00',
+ '#96BF33', '#0000FF', '#4B0082', '#8B00FF', '#FFFFFF',
+ ]
+ html_rainbow = "".join('<span style="color: %s">o</span>' % c for c in rainbow_colors)
+ print(html_to_ansi("Let's try a rainbow: %s" % html_rainbow))
+
+ Here's what the results look like:
+
+ .. image:: images/html-to-ansi.png
+
+ Some more details:
+
+ - Nested tags are supported, within reasonable limits.
+
+ - Text in ``<code>`` and ``<pre>`` tags will be highlighted in a
+ different color from the main text (currently this is yellow).
+
+ - ``<a href="URL">TEXT</a>`` is converted to the format "TEXT (URL)" where
+ the uppercase symbols are highlighted in light blue with an underline.
+
+ - ``<div>``, ``<p>`` and ``<pre>`` tags are considered block level tags
+ and are wrapped in vertical whitespace to prevent their content from
+ "running into" surrounding text. This may cause runs of multiple empty
+ lines to be emitted. As a *workaround* the :func:`__call__()` method
+ will automatically call :func:`.compact_empty_lines()` on the generated
+ output before returning it to the caller. Of course this won't work
+ when `output` is set to something like :data:`sys.stdout`.
+
+ - ``<br>`` is converted to a single plain text line break.
+
+ Implementation notes:
+
+ - A list of dictionaries with style information is used as a stack where
+ new styling can be pushed and a pop will restore the previous styling.
+ When new styling is pushed, it is merged with (but overrides) the current
+ styling.
+
+ - If you're going to be converting a lot of HTML it might be useful from
+ a performance standpoint to re-use an existing :class:`HTMLConverter`
+ object for unrelated HTML fragments, in this case take a look at the
+ :func:`__call__()` method (it makes this use case very easy).
+
+ .. versionadded:: 4.15
+ :class:`humanfriendly.terminal.HTMLConverter` was added to the
+ `humanfriendly` package during the initial development of my new
+ `chat-archive <https://chat-archive.readthedocs.io/>`_ project, whose
+ command line interface makes for a great demonstration of the
+ flexibility that this feature provides (hint: check out how the search
+ keyword highlighting combines with the regular highlighting).
+ """
+
+ BLOCK_TAGS = ('div', 'p', 'pre')
+ """The names of tags that are padded with vertical whitespace."""
+
+ def __init__(self, *args, **kw):
+ """
+ Initialize an :class:`HTMLConverter` object.
+
+ :param callback: Optional keyword argument to specify a function that
+ will be called to process text fragments before they
+ are emitted on the output stream. Note that link text
+ and preformatted text fragments are not processed by
+ this callback.
+ :param output: Optional keyword argument to redirect the output to the
+ given file-like object. If this is not given a new
+ :class:`~python3:io.StringIO` object is created.
+ """
+ # Hide our optional keyword arguments from the superclass.
+ self.callback = kw.pop("callback", None)
+ self.output = kw.pop("output", None)
+ # Initialize the superclass.
+ HTMLParser.__init__(self, *args, **kw)
+
+ def __call__(self, data):
+ """
+ Reset the parser, convert some HTML and get the text with ANSI escape sequences.
+
+ :param data: The HTML to convert to text (a string).
+ :returns: The converted text (only in case `output` is
+ a :class:`~python3:io.StringIO` object).
+ """
+ self.reset()
+ self.feed(data)
+ self.close()
+ if isinstance(self.output, StringIO):
+ return compact_empty_lines(self.output.getvalue())
+
+ @property
+ def current_style(self):
+ """Get the current style from the top of the stack (a dictionary)."""
+ return self.stack[-1] if self.stack else {}
+
+ def close(self):
+ """
+ Close previously opened ANSI escape sequences.
+
+ This method overrides the same method in the superclass to ensure that
+ an :data:`.ANSI_RESET` code is emitted when parsing reaches the end of
+ the input but a style is still active. This is intended to prevent
+ malformed HTML from messing up terminal output.
+ """
+ if any(self.stack):
+ self.output.write(ANSI_RESET)
+ self.stack = []
+ HTMLParser.close(self)
+
+ def emit_style(self, style=None):
+ """
+ Emit an ANSI escape sequence for the given or current style to the output stream.
+
+ :param style: A dictionary with arguments for :func:`.ansi_style()` or
+ :data:`None`, in which case the style at the top of the
+ stack is emitted.
+ """
+ # Clear the current text styles.
+ self.output.write(ANSI_RESET)
+ # Apply a new text style?
+ style = self.current_style if style is None else style
+ if style:
+ self.output.write(ansi_style(**style))
+
+ def handle_charref(self, value):
+ """
+ Process a decimal or hexadecimal numeric character reference.
+
+ :param value: The decimal or hexadecimal value (a string).
+ """
+ self.output.write(unichr(int(value[1:], 16) if value.startswith('x') else int(value)))
+
+ def handle_data(self, data):
+ """
+ Process textual data.
+
+ :param data: The decoded text (a string).
+ """
+ if self.link_url:
+ # Link text is captured literally so that we can reliably check
+ # whether the text and the URL of the link are the same string.
+ self.link_text = data
+ elif self.callback and self.preformatted_text_level == 0:
+ # Text that is not part of a link and not preformatted text is
+ # passed to the user defined callback to allow for arbitrary
+ # pre-processing.
+ data = self.callback(data)
+ # All text is emitted unmodified on the output stream.
+ self.output.write(data)
+
+ def handle_endtag(self, tag):
+ """
+ Process the end of an HTML tag.
+
+ :param tag: The name of the tag (a string).
+ """
+ if tag in ('a', 'b', 'code', 'del', 'em', 'i', 'ins', 'pre', 's', 'strong', 'span', 'u'):
+ old_style = self.current_style
+ # The following conditional isn't necessary for well formed
+ # HTML but prevents raising exceptions on malformed HTML.
+ if self.stack:
+ self.stack.pop(-1)
+ new_style = self.current_style
+ if tag == 'a':
+ if self.urls_match(self.link_text, self.link_url):
+ # Don't render the URL when it's part of the link text.
+ self.emit_style(new_style)
+ else:
+ self.emit_style(new_style)
+ self.output.write(' (')
+ self.emit_style(old_style)
+ self.output.write(self.render_url(self.link_url))
+ self.emit_style(new_style)
+ self.output.write(')')
+ else:
+ self.emit_style(new_style)
+ if tag in ('code', 'pre'):
+ self.preformatted_text_level -= 1
+ if tag in self.BLOCK_TAGS:
+ # Emit an empty line after block level tags.
+ self.output.write('\n\n')
+
+ def handle_entityref(self, name):
+ """
+ Process a named character reference.
+
+ :param name: The name of the character reference (a string).
+ """
+ self.output.write(unichr(name2codepoint[name]))
+
+ def handle_starttag(self, tag, attrs):
+ """
+ Process the start of an HTML tag.
+
+ :param tag: The name of the tag (a string).
+ :param attrs: A list of tuples with two strings each.
+ """
+ if tag in self.BLOCK_TAGS:
+ # Emit an empty line before block level tags.
+ self.output.write('\n\n')
+ if tag == 'a':
+ self.push_styles(color='blue', bright=True, underline=True)
+ # Store the URL that the link points to for later use, so that we
+ # can render the link text before the URL (with the reasoning that
+ # this is the most intuitive way to present a link in a plain text
+ # interface).
+ self.link_url = next((v for n, v in attrs if n == 'href'), '')
+ elif tag == 'b' or tag == 'strong':
+ self.push_styles(bold=True)
+ elif tag == 'br':
+ self.output.write('\n')
+ elif tag == 'code' or tag == 'pre':
+ self.push_styles(color='yellow')
+ self.preformatted_text_level += 1
+ elif tag == 'del' or tag == 's':
+ self.push_styles(strike_through=True)
+ elif tag == 'em' or tag == 'i':
+ self.push_styles(italic=True)
+ elif tag == 'ins' or tag == 'u':
+ self.push_styles(underline=True)
+ elif tag == 'span':
+ styles = {}
+ css = next((v for n, v in attrs if n == 'style'), "")
+ for rule in css.split(';'):
+ name, _, value = rule.partition(':')
+ name = name.strip()
+ value = value.strip()
+ if name == 'background-color':
+ styles['background'] = self.parse_color(value)
+ elif name == 'color':
+ styles['color'] = self.parse_color(value)
+ elif name == 'font-style' and value == 'italic':
+ styles['italic'] = True
+ elif name == 'font-weight' and value == 'bold':
+ styles['bold'] = True
+ elif name == 'text-decoration' and value == 'line-through':
+ styles['strike_through'] = True
+ elif name == 'text-decoration' and value == 'underline':
+ styles['underline'] = True
+ self.push_styles(**styles)
+
+ def normalize_url(self, url):
+ """
+ Normalize a URL to enable string equality comparison.
+
+ :param url: The URL to normalize (a string).
+ :returns: The normalized URL (a string).
+ """
+ return re.sub('^mailto:', '', url)
+
+ def parse_color(self, value):
+ """
+ Convert a CSS color to something that :func:`.ansi_style()` understands.
+
+ :param value: A string like ``rgb(1,2,3)``, ``#AABBCC`` or ``yellow``.
+ :returns: A color value supported by :func:`.ansi_style()` or :data:`None`.
+ """
+ # Parse an 'rgb(N,N,N)' expression.
+ if value.startswith('rgb'):
+ tokens = re.findall(r'\d+', value)
+ if len(tokens) == 3:
+ return tuple(map(int, tokens))
+ # Parse an '#XXXXXX' expression.
+ elif value.startswith('#'):
+ value = value[1:]
+ length = len(value)
+ if length == 6:
+ # Six hex digits (proper notation).
+ return (
+ int(value[:2], 16),
+ int(value[2:4], 16),
+ int(value[4:6], 16),
+ )
+ elif length == 3:
+ # Three hex digits (shorthand).
+ return (
+ int(value[0], 16),
+ int(value[1], 16),
+ int(value[2], 16),
+ )
+ # Try to recognize a named color.
+ value = value.lower()
+ if value in ANSI_COLOR_CODES:
+ return value
+
+ def push_styles(self, **changes):
+ """
+ Push new style information onto the stack.
+
+ :param changes: Any keyword arguments are passed on to :func:`.ansi_style()`.
+
+ This method is a helper for :func:`handle_starttag()`
+ that does the following:
+
+ 1. Make a copy of the current styles (from the top of the stack),
+ 2. Apply the given `changes` to the copy of the current styles,
+ 3. Add the new styles to the stack,
+ 4. Emit the appropriate ANSI escape sequence to the output stream.
+ """
+ prototype = self.current_style
+ if prototype:
+ new_style = dict(prototype)
+ new_style.update(changes)
+ else:
+ new_style = changes
+ self.stack.append(new_style)
+ self.emit_style(new_style)
+
+ def render_url(self, url):
+ """
+ Prepare a URL for rendering on the terminal.
+
+ :param url: The URL to simplify (a string).
+ :returns: The simplified URL (a string).
+
+ This method pre-processes a URL before rendering on the terminal. The
+ following modifications are made:
+
+ - The ``mailto:`` prefix is stripped.
+ - Spaces are converted to ``%20``.
+ - A trailing parenthesis is converted to ``%29``.
+ """
+ url = re.sub('^mailto:', '', url)
+ url = re.sub(' ', '%20', url)
+ url = re.sub(r'\)$', '%29', url)
+ return url
+
+ def reset(self):
+ """
+ Reset the state of the HTML parser and ANSI converter.
+
+ When `output` is a :class:`~python3:io.StringIO` object a new
+ instance will be created (and the old one garbage collected).
+ """
+ # Reset the state of the superclass.
+ HTMLParser.reset(self)
+ # Reset our instance variables.
+ self.link_text = None
+ self.link_url = None
+ self.preformatted_text_level = 0
+ if self.output is None or isinstance(self.output, StringIO):
+ # If the caller specified something like output=sys.stdout then it
+ # doesn't make much sense to negate that choice here in reset().
+ self.output = StringIO()
+ self.stack = []
+
+ def urls_match(self, a, b):
+ """
+ Compare two URLs for equality using :func:`normalize_url()`.
+
+ :param a: A string containing a URL.
+ :param b: A string containing a URL.
+ :returns: :data:`True` if the URLs are the same, :data:`False` otherwise.
+
+ This method is used by :func:`handle_endtag()` to omit the URL of a
+ hyperlink (``<a href="...">``) when the link text is that same URL.
+ """
+ return self.normalize_url(a) == self.normalize_url(b)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/terminal/spinners.py b/contrib/python/humanfriendly/py3/humanfriendly/terminal/spinners.py
new file mode 100644
index 0000000000..e4dc55d302
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/terminal/spinners.py
@@ -0,0 +1,310 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: March 1, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Support for spinners that represent progress on interactive terminals.
+
+The :class:`Spinner` class shows a "spinner" on the terminal to let the user
+know that something is happening during long running operations that would
+otherwise be silent (leaving the user to wonder what they're waiting for).
+Below are some visual examples that should illustrate the point.
+
+**Simple spinners:**
+
+ Here's a screen capture that shows the simplest form of spinner:
+
+ .. image:: images/spinner-basic.gif
+ :alt: Animated screen capture of a simple spinner.
+
+ The following code was used to create the spinner above:
+
+ .. code-block:: python
+
+ import itertools
+ import time
+ from humanfriendly import Spinner
+
+ with Spinner(label="Downloading") as spinner:
+ for i in itertools.count():
+ # Do something useful here.
+ time.sleep(0.1)
+ # Advance the spinner.
+ spinner.step()
+
+**Spinners that show elapsed time:**
+
+ Here's a spinner that shows the elapsed time since it started:
+
+ .. image:: images/spinner-with-timer.gif
+ :alt: Animated screen capture of a spinner showing elapsed time.
+
+ The following code was used to create the spinner above:
+
+ .. code-block:: python
+
+ import itertools
+ import time
+ from humanfriendly import Spinner, Timer
+
+ with Spinner(label="Downloading", timer=Timer()) as spinner:
+ for i in itertools.count():
+ # Do something useful here.
+ time.sleep(0.1)
+ # Advance the spinner.
+ spinner.step()
+
+**Spinners that show progress:**
+
+ Here's a spinner that shows a progress percentage:
+
+ .. image:: images/spinner-with-progress.gif
+ :alt: Animated screen capture of spinner showing progress.
+
+ The following code was used to create the spinner above:
+
+ .. code-block:: python
+
+ import itertools
+ import random
+ import time
+ from humanfriendly import Spinner, Timer
+
+ with Spinner(label="Downloading", total=100) as spinner:
+ progress = 0
+ while progress < 100:
+ # Do something useful here.
+ time.sleep(0.1)
+ # Advance the spinner.
+ spinner.step(progress)
+ # Determine the new progress value.
+ progress += random.random() * 5
+
+If you want to provide user feedback during a long running operation but it's
+not practical to periodically call the :func:`~Spinner.step()` method consider
+using :class:`AutomaticSpinner` instead.
+
+As you may already have noticed in the examples above, :class:`Spinner` objects
+can be used as context managers to automatically call :func:`Spinner.clear()`
+when the spinner ends.
+"""
+
+# Standard library modules.
+import multiprocessing
+import sys
+import time
+
+# Modules included in our package.
+from humanfriendly import Timer
+from humanfriendly.deprecation import deprecated_args
+from humanfriendly.terminal import ANSI_ERASE_LINE
+
+# Public identifiers that require documentation.
+__all__ = ("AutomaticSpinner", "GLYPHS", "MINIMUM_INTERVAL", "Spinner")
+
+GLYPHS = ["-", "\\", "|", "/"]
+"""A list of strings with characters that together form a crude animation :-)."""
+
+MINIMUM_INTERVAL = 0.2
+"""Spinners are redrawn with a frequency no higher than this number (a floating point number of seconds)."""
+
+
+class Spinner(object):
+
+ """Show a spinner on the terminal as a simple means of feedback to the user."""
+
+ @deprecated_args('label', 'total', 'stream', 'interactive', 'timer')
+ def __init__(self, **options):
+ """
+ Initialize a :class:`Spinner` object.
+
+ :param label:
+
+ The label for the spinner (a string or :data:`None`, defaults to
+ :data:`None`).
+
+ :param total:
+
+ The expected number of steps (an integer or :data:`None`). If this is
+ provided the spinner will show a progress percentage.
+
+ :param stream:
+
+ The output stream to show the spinner on (a file-like object,
+ defaults to :data:`sys.stderr`).
+
+ :param interactive:
+
+ :data:`True` to enable rendering of the spinner, :data:`False` to
+ disable (defaults to the result of ``stream.isatty()``).
+
+ :param timer:
+
+ A :class:`.Timer` object (optional). If this is given the spinner
+ will show the elapsed time according to the timer.
+
+ :param interval:
+
+ The spinner will be updated at most once every this many seconds
+ (a floating point number, defaults to :data:`MINIMUM_INTERVAL`).
+
+ :param glyphs:
+
+ A list of strings with single characters that are drawn in the same
+ place in succession to implement a simple animated effect (defaults
+ to :data:`GLYPHS`).
+ """
+ # Store initializer arguments.
+ self.interactive = options.get('interactive')
+ self.interval = options.get('interval', MINIMUM_INTERVAL)
+ self.label = options.get('label')
+ self.states = options.get('glyphs', GLYPHS)
+ self.stream = options.get('stream', sys.stderr)
+ self.timer = options.get('timer')
+ self.total = options.get('total')
+ # Define instance variables.
+ self.counter = 0
+ self.last_update = 0
+ # Try to automatically discover whether the stream is connected to
+ # a terminal, but don't fail if no isatty() method is available.
+ if self.interactive is None:
+ try:
+ self.interactive = self.stream.isatty()
+ except Exception:
+ self.interactive = False
+
+ def step(self, progress=0, label=None):
+ """
+ Advance the spinner by one step and redraw it.
+
+ :param progress: The number of the current step, relative to the total
+ given to the :class:`Spinner` constructor (an integer,
+ optional). If not provided the spinner will not show
+ progress.
+ :param label: The label to use while redrawing (a string, optional). If
+ not provided the label given to the :class:`Spinner`
+ constructor is used instead.
+
+ This method advances the spinner by one step without starting a new
+ line, causing an animated effect which is very simple but much nicer
+ than waiting for a prompt which is completely silent for a long time.
+
+ .. note:: This method uses time based rate limiting to avoid redrawing
+ the spinner too frequently. If you know you're dealing with
+ code that will call :func:`step()` at a high frequency,
+ consider using :func:`sleep()` to avoid creating the
+ equivalent of a busy loop that's rate limiting the spinner
+ 99% of the time.
+ """
+ if self.interactive:
+ time_now = time.time()
+ if time_now - self.last_update >= self.interval:
+ self.last_update = time_now
+ state = self.states[self.counter % len(self.states)]
+ label = label or self.label
+ if not label:
+ raise Exception("No label set for spinner!")
+ elif self.total and progress:
+ label = "%s: %.2f%%" % (label, progress / (self.total / 100.0))
+ elif self.timer and self.timer.elapsed_time > 2:
+ label = "%s (%s)" % (label, self.timer.rounded)
+ self.stream.write("%s %s %s ..\r" % (ANSI_ERASE_LINE, state, label))
+ self.counter += 1
+
+ def sleep(self):
+ """
+ Sleep for a short period before redrawing the spinner.
+
+ This method is useful when you know you're dealing with code that will
+ call :func:`step()` at a high frequency. It will sleep for the interval
+ with which the spinner is redrawn (less than a second). This avoids
+ creating the equivalent of a busy loop that's rate limiting the
+ spinner 99% of the time.
+
+ This method doesn't redraw the spinner, you still have to call
+ :func:`step()` in order to do that.
+ """
+ time.sleep(MINIMUM_INTERVAL)
+
+ def clear(self):
+ """
+ Clear the spinner.
+
+ The next line which is shown on the standard output or error stream
+ after calling this method will overwrite the line that used to show the
+ spinner.
+ """
+ if self.interactive:
+ self.stream.write(ANSI_ERASE_LINE)
+
+ def __enter__(self):
+ """
+ Enable the use of spinners as context managers.
+
+ :returns: The :class:`Spinner` object.
+ """
+ return self
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Clear the spinner when leaving the context."""
+ self.clear()
+
+
+class AutomaticSpinner(object):
+
+ """
+ Show a spinner on the terminal that automatically starts animating.
+
+ This class shows a spinner on the terminal (just like :class:`Spinner`
+ does) that automatically starts animating. This class should be used as a
+ context manager using the :keyword:`with` statement. The animation
+ continues for as long as the context is active.
+
+ :class:`AutomaticSpinner` provides an alternative to :class:`Spinner`
+ for situations where it is not practical for the caller to periodically
+ call :func:`~Spinner.step()` to advance the animation, e.g. because
+ you're performing a blocking call and don't fancy implementing threading or
+ subprocess handling just to provide some user feedback.
+
+ This works using the :mod:`multiprocessing` module by spawning a
+ subprocess to render the spinner while the main process is busy doing
+ something more useful. By using the :keyword:`with` statement you're
+ guaranteed that the subprocess is properly terminated at the appropriate
+ time.
+ """
+
+ def __init__(self, label, show_time=True):
+ """
+ Initialize an automatic spinner.
+
+ :param label: The label for the spinner (a string).
+ :param show_time: If this is :data:`True` (the default) then the spinner
+ shows elapsed time.
+ """
+ self.label = label
+ self.show_time = show_time
+ self.shutdown_event = multiprocessing.Event()
+ self.subprocess = multiprocessing.Process(target=self._target)
+
+ def __enter__(self):
+ """Enable the use of automatic spinners as context managers."""
+ self.subprocess.start()
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Enable the use of automatic spinners as context managers."""
+ self.shutdown_event.set()
+ self.subprocess.join()
+
+ def _target(self):
+ try:
+ timer = Timer() if self.show_time else None
+ with Spinner(label=self.label, timer=timer) as spinner:
+ while not self.shutdown_event.is_set():
+ spinner.step()
+ spinner.sleep()
+ except KeyboardInterrupt:
+ # Swallow Control-C signals without producing a nasty traceback that
+ # won't make any sense to the average user.
+ pass
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/testing.py b/contrib/python/humanfriendly/py3/humanfriendly/testing.py
new file mode 100644
index 0000000000..f6abddf074
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/testing.py
@@ -0,0 +1,669 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: March 6, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Utility classes and functions that make it easy to write :mod:`unittest` compatible test suites.
+
+Over the years I've developed the habit of writing test suites for Python
+projects using the :mod:`unittest` module. During those years I've come to know
+:pypi:`pytest` and in fact I use :pypi:`pytest` to run my test suites (due to
+its much better error reporting) but I've yet to publish a test suite that
+*requires* :pypi:`pytest`. I have several reasons for doing so:
+
+- It's nice to keep my test suites as simple and accessible as possible and
+ not requiring a specific test runner is part of that attitude.
+
+- Whereas :mod:`unittest` is quite explicit, :pypi:`pytest` contains a lot of
+ magic, which kind of contradicts the Python mantra "explicit is better than
+ implicit" (IMHO).
+"""
+
+# Standard library module
+import functools
+import logging
+import os
+import pipes
+import shutil
+import sys
+import tempfile
+import time
+import unittest
+
+# Modules included in our package.
+from humanfriendly.compat import StringIO
+from humanfriendly.text import random_string
+
+# Initialize a logger for this module.
+logger = logging.getLogger(__name__)
+
+# A unique object reference used to detect missing attributes.
+NOTHING = object()
+
+# Public identifiers that require documentation.
+__all__ = (
+ 'CallableTimedOut',
+ 'CaptureBuffer',
+ 'CaptureOutput',
+ 'ContextManager',
+ 'CustomSearchPath',
+ 'MockedProgram',
+ 'PatchedAttribute',
+ 'PatchedItem',
+ 'TemporaryDirectory',
+ 'TestCase',
+ 'configure_logging',
+ 'make_dirs',
+ 'retry',
+ 'run_cli',
+ 'skip_on_raise',
+ 'touch',
+)
+
+
+def configure_logging(log_level=logging.DEBUG):
+ """configure_logging(log_level=logging.DEBUG)
+ Automatically configure logging to the terminal.
+
+ :param log_level: The log verbosity (a number, defaults
+ to :mod:`logging.DEBUG <logging>`).
+
+ When :mod:`coloredlogs` is installed :func:`coloredlogs.install()` will be
+ used to configure logging to the terminal. When this fails with an
+ :exc:`~exceptions.ImportError` then :func:`logging.basicConfig()` is used
+ as a fall back.
+ """
+ try:
+ import coloredlogs
+ coloredlogs.install(level=log_level)
+ except ImportError:
+ logging.basicConfig(
+ level=log_level,
+ format='%(asctime)s %(name)s[%(process)d] %(levelname)s %(message)s',
+ datefmt='%Y-%m-%d %H:%M:%S')
+
+
+def make_dirs(pathname):
+ """
+ Create missing directories.
+
+ :param pathname: The pathname of a directory (a string).
+ """
+ if not os.path.isdir(pathname):
+ os.makedirs(pathname)
+
+
+def retry(func, timeout=60, exc_type=AssertionError):
+ """retry(func, timeout=60, exc_type=AssertionError)
+ Retry a function until assertions no longer fail.
+
+ :param func: A callable. When the callable returns
+ :data:`False` it will also be retried.
+ :param timeout: The number of seconds after which to abort (a number,
+ defaults to 60).
+ :param exc_type: The type of exceptions to retry (defaults
+ to :exc:`~exceptions.AssertionError`).
+ :returns: The value returned by `func`.
+ :raises: Once the timeout has expired :func:`retry()` will raise the
+ previously retried assertion error. When `func` keeps returning
+ :data:`False` until `timeout` expires :exc:`CallableTimedOut`
+ will be raised.
+
+ This function sleeps between retries to avoid claiming CPU cycles we don't
+ need. It starts by sleeping for 0.1 second but adjusts this to one second
+ as the number of retries grows.
+ """
+ pause = 0.1
+ timeout += time.time()
+ while True:
+ try:
+ result = func()
+ if result is not False:
+ return result
+ except exc_type:
+ if time.time() > timeout:
+ raise
+ else:
+ if time.time() > timeout:
+ raise CallableTimedOut()
+ time.sleep(pause)
+ if pause < 1:
+ pause *= 2
+
+
+def run_cli(entry_point, *arguments, **options):
+ """
+ Test a command line entry point.
+
+ :param entry_point: The function that implements the command line interface
+ (a callable).
+ :param arguments: Any positional arguments (strings) become the command
+ line arguments (:data:`sys.argv` items 1-N).
+ :param options: The following keyword arguments are supported:
+
+ **capture**
+ Whether to use :class:`CaptureOutput`. Defaults
+ to :data:`True` but can be disabled by passing
+ :data:`False` instead.
+ **input**
+ Refer to :class:`CaptureOutput`.
+ **merged**
+ Refer to :class:`CaptureOutput`.
+ **program_name**
+ Used to set :data:`sys.argv` item 0.
+ :returns: A tuple with two values:
+
+ 1. The return code (an integer).
+ 2. The captured output (a string).
+ """
+ # Add the `program_name' option to the arguments.
+ arguments = list(arguments)
+ arguments.insert(0, options.pop('program_name', sys.executable))
+ # Log the command line arguments (and the fact that we're about to call the
+ # command line entry point function).
+ logger.debug("Calling command line entry point with arguments: %s", arguments)
+ # Prepare to capture the return code and output even if the command line
+ # interface raises an exception (whether the exception type is SystemExit
+ # or something else).
+ returncode = 0
+ stdout = None
+ stderr = None
+ try:
+ # Temporarily override sys.argv.
+ with PatchedAttribute(sys, 'argv', arguments):
+ # Manipulate the standard input/output/error streams?
+ options['enabled'] = options.pop('capture', True)
+ with CaptureOutput(**options) as capturer:
+ try:
+ # Call the command line interface.
+ entry_point()
+ finally:
+ # Get the output even if an exception is raised.
+ stdout = capturer.stdout.getvalue()
+ stderr = capturer.stderr.getvalue()
+ # Reconfigure logging to the terminal because it is very
+ # likely that the entry point function has changed the
+ # configured log level.
+ configure_logging()
+ except BaseException as e:
+ if isinstance(e, SystemExit):
+ logger.debug("Intercepting return code %s from SystemExit exception.", e.code)
+ returncode = e.code
+ else:
+ logger.warning("Defaulting return code to 1 due to raised exception.", exc_info=True)
+ returncode = 1
+ else:
+ logger.debug("Command line entry point returned successfully!")
+ # Always log the output captured on stdout/stderr, to make it easier to
+ # diagnose test failures (but avoid duplicate logging when merged=True).
+ is_merged = options.get('merged', False)
+ merged_streams = [('merged streams', stdout)]
+ separate_streams = [('stdout', stdout), ('stderr', stderr)]
+ streams = merged_streams if is_merged else separate_streams
+ for name, value in streams:
+ if value:
+ logger.debug("Output on %s:\n%s", name, value)
+ else:
+ logger.debug("No output on %s.", name)
+ return returncode, stdout
+
+
+def skip_on_raise(*exc_types):
+ """
+ Decorate a test function to translation specific exception types to :exc:`unittest.SkipTest`.
+
+ :param exc_types: One or more positional arguments give the exception
+ types to be translated to :exc:`unittest.SkipTest`.
+ :returns: A decorator function specialized to `exc_types`.
+ """
+ def decorator(function):
+ @functools.wraps(function)
+ def wrapper(*args, **kw):
+ try:
+ return function(*args, **kw)
+ except exc_types as e:
+ logger.debug("Translating exception to unittest.SkipTest ..", exc_info=True)
+ raise unittest.SkipTest("skipping test because %s was raised" % type(e))
+ return wrapper
+ return decorator
+
+
+def touch(filename):
+ """
+ The equivalent of the UNIX :man:`touch` program in Python.
+
+ :param filename: The pathname of the file to touch (a string).
+
+ Note that missing directories are automatically created using
+ :func:`make_dirs()`.
+ """
+ make_dirs(os.path.dirname(filename))
+ with open(filename, 'a'):
+ os.utime(filename, None)
+
+
+class CallableTimedOut(Exception):
+
+ """Raised by :func:`retry()` when the timeout expires."""
+
+
+class ContextManager(object):
+
+ """Base class to enable composition of context managers."""
+
+ def __enter__(self):
+ """Enable use as context managers."""
+ return self
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Enable use as context managers."""
+
+
+class PatchedAttribute(ContextManager):
+
+ """Context manager that temporary replaces an object attribute using :func:`setattr()`."""
+
+ def __init__(self, obj, name, value):
+ """
+ Initialize a :class:`PatchedAttribute` object.
+
+ :param obj: The object to patch.
+ :param name: An attribute name.
+ :param value: The value to set.
+ """
+ self.object_to_patch = obj
+ self.attribute_to_patch = name
+ self.patched_value = value
+ self.original_value = NOTHING
+
+ def __enter__(self):
+ """
+ Replace (patch) the attribute.
+
+ :returns: The object whose attribute was patched.
+ """
+ # Enable composition of context managers.
+ super(PatchedAttribute, self).__enter__()
+ # Patch the object's attribute.
+ self.original_value = getattr(self.object_to_patch, self.attribute_to_patch, NOTHING)
+ setattr(self.object_to_patch, self.attribute_to_patch, self.patched_value)
+ return self.object_to_patch
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Restore the attribute to its original value."""
+ # Enable composition of context managers.
+ super(PatchedAttribute, self).__exit__(exc_type, exc_value, traceback)
+ # Restore the object's attribute.
+ if self.original_value is NOTHING:
+ delattr(self.object_to_patch, self.attribute_to_patch)
+ else:
+ setattr(self.object_to_patch, self.attribute_to_patch, self.original_value)
+
+
+class PatchedItem(ContextManager):
+
+ """Context manager that temporary replaces an object item using :meth:`~object.__setitem__()`."""
+
+ def __init__(self, obj, item, value):
+ """
+ Initialize a :class:`PatchedItem` object.
+
+ :param obj: The object to patch.
+ :param item: The item to patch.
+ :param value: The value to set.
+ """
+ self.object_to_patch = obj
+ self.item_to_patch = item
+ self.patched_value = value
+ self.original_value = NOTHING
+
+ def __enter__(self):
+ """
+ Replace (patch) the item.
+
+ :returns: The object whose item was patched.
+ """
+ # Enable composition of context managers.
+ super(PatchedItem, self).__enter__()
+ # Patch the object's item.
+ try:
+ self.original_value = self.object_to_patch[self.item_to_patch]
+ except KeyError:
+ self.original_value = NOTHING
+ self.object_to_patch[self.item_to_patch] = self.patched_value
+ return self.object_to_patch
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Restore the item to its original value."""
+ # Enable composition of context managers.
+ super(PatchedItem, self).__exit__(exc_type, exc_value, traceback)
+ # Restore the object's item.
+ if self.original_value is NOTHING:
+ del self.object_to_patch[self.item_to_patch]
+ else:
+ self.object_to_patch[self.item_to_patch] = self.original_value
+
+
+class TemporaryDirectory(ContextManager):
+
+ """
+ Easy temporary directory creation & cleanup using the :keyword:`with` statement.
+
+ Here's an example of how to use this:
+
+ .. code-block:: python
+
+ with TemporaryDirectory() as directory:
+ # Do something useful here.
+ assert os.path.isdir(directory)
+ """
+
+ def __init__(self, **options):
+ """
+ Initialize a :class:`TemporaryDirectory` object.
+
+ :param options: Any keyword arguments are passed on to
+ :func:`tempfile.mkdtemp()`.
+ """
+ self.mkdtemp_options = options
+ self.temporary_directory = None
+
+ def __enter__(self):
+ """
+ Create the temporary directory using :func:`tempfile.mkdtemp()`.
+
+ :returns: The pathname of the directory (a string).
+ """
+ # Enable composition of context managers.
+ super(TemporaryDirectory, self).__enter__()
+ # Create the temporary directory.
+ self.temporary_directory = tempfile.mkdtemp(**self.mkdtemp_options)
+ return self.temporary_directory
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Cleanup the temporary directory using :func:`shutil.rmtree()`."""
+ # Enable composition of context managers.
+ super(TemporaryDirectory, self).__exit__(exc_type, exc_value, traceback)
+ # Cleanup the temporary directory.
+ if self.temporary_directory is not None:
+ shutil.rmtree(self.temporary_directory)
+ self.temporary_directory = None
+
+
+class MockedHomeDirectory(PatchedItem, TemporaryDirectory):
+
+ """
+ Context manager to temporarily change ``$HOME`` (the current user's profile directory).
+
+ This class is a composition of the :class:`PatchedItem` and
+ :class:`TemporaryDirectory` context managers.
+ """
+
+ def __init__(self):
+ """Initialize a :class:`MockedHomeDirectory` object."""
+ PatchedItem.__init__(self, os.environ, 'HOME', os.environ.get('HOME'))
+ TemporaryDirectory.__init__(self)
+
+ def __enter__(self):
+ """
+ Activate the custom ``$PATH``.
+
+ :returns: The pathname of the directory that has
+ been added to ``$PATH`` (a string).
+ """
+ # Get the temporary directory.
+ directory = TemporaryDirectory.__enter__(self)
+ # Override the value to patch now that we have
+ # the pathname of the temporary directory.
+ self.patched_value = directory
+ # Temporary patch $HOME.
+ PatchedItem.__enter__(self)
+ # Pass the pathname of the temporary directory to the caller.
+ return directory
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Deactivate the custom ``$HOME``."""
+ super(MockedHomeDirectory, self).__exit__(exc_type, exc_value, traceback)
+
+
+class CustomSearchPath(PatchedItem, TemporaryDirectory):
+
+ """
+ Context manager to temporarily customize ``$PATH`` (the executable search path).
+
+ This class is a composition of the :class:`PatchedItem` and
+ :class:`TemporaryDirectory` context managers.
+ """
+
+ def __init__(self, isolated=False):
+ """
+ Initialize a :class:`CustomSearchPath` object.
+
+ :param isolated: :data:`True` to clear the original search path,
+ :data:`False` to add the temporary directory to the
+ start of the search path.
+ """
+ # Initialize our own instance variables.
+ self.isolated_search_path = isolated
+ # Selectively initialize our superclasses.
+ PatchedItem.__init__(self, os.environ, 'PATH', self.current_search_path)
+ TemporaryDirectory.__init__(self)
+
+ def __enter__(self):
+ """
+ Activate the custom ``$PATH``.
+
+ :returns: The pathname of the directory that has
+ been added to ``$PATH`` (a string).
+ """
+ # Get the temporary directory.
+ directory = TemporaryDirectory.__enter__(self)
+ # Override the value to patch now that we have
+ # the pathname of the temporary directory.
+ self.patched_value = (
+ directory if self.isolated_search_path
+ else os.pathsep.join([directory] + self.current_search_path.split(os.pathsep))
+ )
+ # Temporary patch the $PATH.
+ PatchedItem.__enter__(self)
+ # Pass the pathname of the temporary directory to the caller
+ # because they may want to `install' custom executables.
+ return directory
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Deactivate the custom ``$PATH``."""
+ super(CustomSearchPath, self).__exit__(exc_type, exc_value, traceback)
+
+ @property
+ def current_search_path(self):
+ """The value of ``$PATH`` or :data:`os.defpath` (a string)."""
+ return os.environ.get('PATH', os.defpath)
+
+
+class MockedProgram(CustomSearchPath):
+
+ """
+ Context manager to mock the existence of a program (executable).
+
+ This class extends the functionality of :class:`CustomSearchPath`.
+ """
+
+ def __init__(self, name, returncode=0, script=None):
+ """
+ Initialize a :class:`MockedProgram` object.
+
+ :param name: The name of the program (a string).
+ :param returncode: The return code that the program should emit (a
+ number, defaults to zero).
+ :param script: Shell script code to include in the mocked program (a
+ string or :data:`None`). This can be used to mock a
+ program that is expected to generate specific output.
+ """
+ # Initialize our own instance variables.
+ self.program_name = name
+ self.program_returncode = returncode
+ self.program_script = script
+ self.program_signal_file = None
+ # Initialize our superclasses.
+ super(MockedProgram, self).__init__()
+
+ def __enter__(self):
+ """
+ Create the mock program.
+
+ :returns: The pathname of the directory that has
+ been added to ``$PATH`` (a string).
+ """
+ directory = super(MockedProgram, self).__enter__()
+ self.program_signal_file = os.path.join(directory, 'program-was-run-%s' % random_string(10))
+ pathname = os.path.join(directory, self.program_name)
+ with open(pathname, 'w') as handle:
+ handle.write('#!/bin/sh\n')
+ handle.write('echo > %s\n' % pipes.quote(self.program_signal_file))
+ if self.program_script:
+ handle.write('%s\n' % self.program_script.strip())
+ handle.write('exit %i\n' % self.program_returncode)
+ os.chmod(pathname, 0o755)
+ return directory
+
+ def __exit__(self, *args, **kw):
+ """
+ Ensure that the mock program was run.
+
+ :raises: :exc:`~exceptions.AssertionError` when
+ the mock program hasn't been run.
+ """
+ try:
+ assert self.program_signal_file and os.path.isfile(self.program_signal_file), \
+ ("It looks like %r was never run!" % self.program_name)
+ finally:
+ return super(MockedProgram, self).__exit__(*args, **kw)
+
+
+class CaptureOutput(ContextManager):
+
+ """
+ Context manager that captures what's written to :data:`sys.stdout` and :data:`sys.stderr`.
+
+ .. attribute:: stdin
+
+ The :class:`~humanfriendly.compat.StringIO` object used to feed the standard input stream.
+
+ .. attribute:: stdout
+
+ The :class:`CaptureBuffer` object used to capture the standard output stream.
+
+ .. attribute:: stderr
+
+ The :class:`CaptureBuffer` object used to capture the standard error stream.
+ """
+
+ def __init__(self, merged=False, input='', enabled=True):
+ """
+ Initialize a :class:`CaptureOutput` object.
+
+ :param merged: :data:`True` to merge the streams,
+ :data:`False` to capture them separately.
+ :param input: The data that reads from :data:`sys.stdin`
+ should return (a string).
+ :param enabled: :data:`True` to enable capturing (the default),
+ :data:`False` otherwise. This makes it easy to
+ unconditionally use :class:`CaptureOutput` in
+ a :keyword:`with` block while preserving the
+ choice to opt out of capturing output.
+ """
+ self.stdin = StringIO(input)
+ self.stdout = CaptureBuffer()
+ self.stderr = self.stdout if merged else CaptureBuffer()
+ self.patched_attributes = []
+ if enabled:
+ self.patched_attributes.extend(
+ PatchedAttribute(sys, name, getattr(self, name))
+ for name in ('stdin', 'stdout', 'stderr')
+ )
+
+ def __enter__(self):
+ """Start capturing what's written to :data:`sys.stdout` and :data:`sys.stderr`."""
+ super(CaptureOutput, self).__enter__()
+ for context in self.patched_attributes:
+ context.__enter__()
+ return self
+
+ def __exit__(self, exc_type=None, exc_value=None, traceback=None):
+ """Stop capturing what's written to :data:`sys.stdout` and :data:`sys.stderr`."""
+ super(CaptureOutput, self).__exit__(exc_type, exc_value, traceback)
+ for context in self.patched_attributes:
+ context.__exit__(exc_type, exc_value, traceback)
+
+ def get_lines(self):
+ """Get the contents of :attr:`stdout` split into separate lines."""
+ return self.get_text().splitlines()
+
+ def get_text(self):
+ """Get the contents of :attr:`stdout` as a Unicode string."""
+ return self.stdout.get_text()
+
+ def getvalue(self):
+ """Get the text written to :data:`sys.stdout`."""
+ return self.stdout.getvalue()
+
+
+class CaptureBuffer(StringIO):
+
+ """
+ Helper for :class:`CaptureOutput` to provide an easy to use API.
+
+ The two methods defined by this subclass were specifically chosen to match
+ the names of the methods provided by my :pypi:`capturer` package which
+ serves a similar role as :class:`CaptureOutput` but knows how to simulate
+ an interactive terminal (tty).
+ """
+
+ def get_lines(self):
+ """Get the contents of the buffer split into separate lines."""
+ return self.get_text().splitlines()
+
+ def get_text(self):
+ """Get the contents of the buffer as a Unicode string."""
+ return self.getvalue()
+
+
+class TestCase(unittest.TestCase):
+
+ """Subclass of :class:`unittest.TestCase` with automatic logging and other miscellaneous features."""
+
+ def __init__(self, *args, **kw):
+ """
+ Initialize a :class:`TestCase` object.
+
+ Any positional and/or keyword arguments are passed on to the
+ initializer of the superclass.
+ """
+ super(TestCase, self).__init__(*args, **kw)
+
+ def setUp(self, log_level=logging.DEBUG):
+ """setUp(log_level=logging.DEBUG)
+ Automatically configure logging to the terminal.
+
+ :param log_level: Refer to :func:`configure_logging()`.
+
+ The :func:`setUp()` method is automatically called by
+ :class:`unittest.TestCase` before each test method starts.
+ It does two things:
+
+ - Logging to the terminal is configured using
+ :func:`configure_logging()`.
+
+ - Before the test method starts a newline is emitted, to separate the
+ name of the test method (which will be printed to the terminal by
+ :mod:`unittest` or :pypi:`pytest`) from the first line of logging
+ output that the test method is likely going to generate.
+ """
+ # Configure logging to the terminal.
+ configure_logging(log_level)
+ # Separate the name of the test method (printed by the superclass
+ # and/or py.test without a newline at the end) from the first line of
+ # logging output that the test method is likely going to generate.
+ sys.stderr.write("\n")
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/text.py b/contrib/python/humanfriendly/py3/humanfriendly/text.py
new file mode 100644
index 0000000000..a257a6a189
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/text.py
@@ -0,0 +1,449 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: December 1, 2020
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Simple text manipulation functions.
+
+The :mod:`~humanfriendly.text` module contains simple functions to manipulate text:
+
+- The :func:`concatenate()` and :func:`pluralize()` functions make it easy to
+ generate human friendly output.
+
+- The :func:`format()`, :func:`compact()` and :func:`dedent()` functions
+ provide a clean and simple to use syntax for composing large text fragments
+ with interpolated variables.
+
+- The :func:`tokenize()` function parses simple user input.
+"""
+
+# Standard library modules.
+import numbers
+import random
+import re
+import string
+import textwrap
+
+# Public identifiers that require documentation.
+__all__ = (
+ 'compact',
+ 'compact_empty_lines',
+ 'concatenate',
+ 'dedent',
+ 'format',
+ 'generate_slug',
+ 'is_empty_line',
+ 'join_lines',
+ 'pluralize',
+ 'pluralize_raw',
+ 'random_string',
+ 'split',
+ 'split_paragraphs',
+ 'tokenize',
+ 'trim_empty_lines',
+)
+
+
+def compact(text, *args, **kw):
+ '''
+ Compact whitespace in a string.
+
+ Trims leading and trailing whitespace, replaces runs of whitespace
+ characters with a single space and interpolates any arguments using
+ :func:`format()`.
+
+ :param text: The text to compact (a string).
+ :param args: Any positional arguments are interpolated using :func:`format()`.
+ :param kw: Any keyword arguments are interpolated using :func:`format()`.
+ :returns: The compacted text (a string).
+
+ Here's an example of how I like to use the :func:`compact()` function, this
+ is an example from a random unrelated project I'm working on at the moment::
+
+ raise PortDiscoveryError(compact("""
+ Failed to discover port(s) that Apache is listening on!
+ Maybe I'm parsing the wrong configuration file? ({filename})
+ """, filename=self.ports_config))
+
+ The combination of :func:`compact()` and Python's multi line strings allows
+ me to write long text fragments with interpolated variables that are easy
+ to write, easy to read and work well with Python's whitespace
+ sensitivity.
+ '''
+ non_whitespace_tokens = text.split()
+ compacted_text = ' '.join(non_whitespace_tokens)
+ return format(compacted_text, *args, **kw)
+
+
+def compact_empty_lines(text):
+ """
+ Replace repeating empty lines with a single empty line (similar to ``cat -s``).
+
+ :param text: The text in which to compact empty lines (a string).
+ :returns: The text with empty lines compacted (a string).
+ """
+ i = 0
+ lines = text.splitlines(True)
+ while i < len(lines):
+ if i > 0 and is_empty_line(lines[i - 1]) and is_empty_line(lines[i]):
+ lines.pop(i)
+ else:
+ i += 1
+ return ''.join(lines)
+
+
+def concatenate(items, conjunction='and', serial_comma=False):
+ """
+ Concatenate a list of items in a human friendly way.
+
+ :param items:
+
+ A sequence of strings.
+
+ :param conjunction:
+
+ The word to use before the last item (a string, defaults to "and").
+
+ :param serial_comma:
+
+ :data:`True` to use a `serial comma`_, :data:`False` otherwise
+ (defaults to :data:`False`).
+
+ :returns:
+
+ A single string.
+
+ >>> from humanfriendly.text import concatenate
+ >>> concatenate(["eggs", "milk", "bread"])
+ 'eggs, milk and bread'
+
+ .. _serial comma: https://en.wikipedia.org/wiki/Serial_comma
+ """
+ items = list(items)
+ if len(items) > 1:
+ final_item = items.pop()
+ formatted = ', '.join(items)
+ if serial_comma:
+ formatted += ','
+ return ' '.join([formatted, conjunction, final_item])
+ elif items:
+ return items[0]
+ else:
+ return ''
+
+
+def dedent(text, *args, **kw):
+ """
+ Dedent a string (remove common leading whitespace from all lines).
+
+ Removes common leading whitespace from all lines in the string using
+ :func:`textwrap.dedent()`, removes leading and trailing empty lines using
+ :func:`trim_empty_lines()` and interpolates any arguments using
+ :func:`format()`.
+
+ :param text: The text to dedent (a string).
+ :param args: Any positional arguments are interpolated using :func:`format()`.
+ :param kw: Any keyword arguments are interpolated using :func:`format()`.
+ :returns: The dedented text (a string).
+
+ The :func:`compact()` function's documentation contains an example of how I
+ like to use the :func:`compact()` and :func:`dedent()` functions. The main
+ difference is that I use :func:`compact()` for text that will be presented
+ to the user (where whitespace is not so significant) and :func:`dedent()`
+ for data file and code generation tasks (where newlines and indentation are
+ very significant).
+ """
+ dedented_text = textwrap.dedent(text)
+ trimmed_text = trim_empty_lines(dedented_text)
+ return format(trimmed_text, *args, **kw)
+
+
+def format(text, *args, **kw):
+ """
+ Format a string using the string formatting operator and/or :meth:`str.format()`.
+
+ :param text: The text to format (a string).
+ :param args: Any positional arguments are interpolated into the text using
+ the string formatting operator (``%``). If no positional
+ arguments are given no interpolation is done.
+ :param kw: Any keyword arguments are interpolated into the text using the
+ :meth:`str.format()` function. If no keyword arguments are given
+ no interpolation is done.
+ :returns: The text with any positional and/or keyword arguments
+ interpolated (a string).
+
+ The implementation of this function is so trivial that it seems silly to
+ even bother writing and documenting it. Justifying this requires some
+ context :-).
+
+ **Why format() instead of the string formatting operator?**
+
+ For really simple string interpolation Python's string formatting operator
+ is ideal, but it does have some strange quirks:
+
+ - When you switch from interpolating a single value to interpolating
+ multiple values you have to wrap them in tuple syntax. Because
+ :func:`format()` takes a `variable number of arguments`_ it always
+ receives a tuple (which saves me a context switch :-). Here's an
+ example:
+
+ >>> from humanfriendly.text import format
+ >>> # The string formatting operator.
+ >>> print('the magic number is %s' % 42)
+ the magic number is 42
+ >>> print('the magic numbers are %s and %s' % (12, 42))
+ the magic numbers are 12 and 42
+ >>> # The format() function.
+ >>> print(format('the magic number is %s', 42))
+ the magic number is 42
+ >>> print(format('the magic numbers are %s and %s', 12, 42))
+ the magic numbers are 12 and 42
+
+ - When you interpolate a single value and someone accidentally passes in a
+ tuple your code raises a :exc:`~exceptions.TypeError`. Because
+ :func:`format()` takes a `variable number of arguments`_ it always
+ receives a tuple so this can never happen. Here's an example:
+
+ >>> # How expecting to interpolate a single value can fail.
+ >>> value = (12, 42)
+ >>> print('the magic value is %s' % value)
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ TypeError: not all arguments converted during string formatting
+ >>> # The following line works as intended, no surprises here!
+ >>> print(format('the magic value is %s', value))
+ the magic value is (12, 42)
+
+ **Why format() instead of the str.format() method?**
+
+ When you're doing complex string interpolation the :meth:`str.format()`
+ function results in more readable code, however I frequently find myself
+ adding parentheses to force evaluation order. The :func:`format()` function
+ avoids this because of the relative priority between the comma and dot
+ operators. Here's an example:
+
+ >>> "{adjective} example" + " " + "(can't think of anything less {adjective})".format(adjective='silly')
+ "{adjective} example (can't think of anything less silly)"
+ >>> ("{adjective} example" + " " + "(can't think of anything less {adjective})").format(adjective='silly')
+ "silly example (can't think of anything less silly)"
+ >>> format("{adjective} example" + " " + "(can't think of anything less {adjective})", adjective='silly')
+ "silly example (can't think of anything less silly)"
+
+ The :func:`compact()` and :func:`dedent()` functions are wrappers that
+ combine :func:`format()` with whitespace manipulation to make it easy to
+ write nice to read Python code.
+
+ .. _variable number of arguments: https://docs.python.org/2/tutorial/controlflow.html#arbitrary-argument-lists
+ """
+ if args:
+ text %= args
+ if kw:
+ text = text.format(**kw)
+ return text
+
+
+def generate_slug(text, delimiter="-"):
+ """
+ Convert text to a normalized "slug" without whitespace.
+
+ :param text: The original text, for example ``Some Random Text!``.
+ :param delimiter: The delimiter used to separate words
+ (defaults to the ``-`` character).
+ :returns: The slug text, for example ``some-random-text``.
+ :raises: :exc:`~exceptions.ValueError` when the provided
+ text is nonempty but results in an empty slug.
+ """
+ slug = text.lower()
+ escaped = delimiter.replace("\\", "\\\\")
+ slug = re.sub("[^a-z0-9]+", escaped, slug)
+ slug = slug.strip(delimiter)
+ if text and not slug:
+ msg = "The provided text %r results in an empty slug!"
+ raise ValueError(format(msg, text))
+ return slug
+
+
+def is_empty_line(text):
+ """
+ Check if a text is empty or contains only whitespace.
+
+ :param text: The text to check for "emptiness" (a string).
+ :returns: :data:`True` if the text is empty or contains only whitespace,
+ :data:`False` otherwise.
+ """
+ return len(text) == 0 or text.isspace()
+
+
+def join_lines(text):
+ """
+ Remove "hard wrapping" from the paragraphs in a string.
+
+ :param text: The text to reformat (a string).
+ :returns: The text without hard wrapping (a string).
+
+ This function works by removing line breaks when the last character before
+ a line break and the first character after the line break are both
+ non-whitespace characters. This means that common leading indentation will
+ break :func:`join_lines()` (in that case you can use :func:`dedent()`
+ before calling :func:`join_lines()`).
+ """
+ return re.sub(r'(\S)\n(\S)', r'\1 \2', text)
+
+
+def pluralize(count, singular, plural=None):
+ """
+ Combine a count with the singular or plural form of a word.
+
+ :param count: The count (a number).
+ :param singular: The singular form of the word (a string).
+ :param plural: The plural form of the word (a string or :data:`None`).
+ :returns: The count and singular or plural word concatenated (a string).
+
+ See :func:`pluralize_raw()` for the logic underneath :func:`pluralize()`.
+ """
+ return '%s %s' % (count, pluralize_raw(count, singular, plural))
+
+
+def pluralize_raw(count, singular, plural=None):
+ """
+ Select the singular or plural form of a word based on a count.
+
+ :param count: The count (a number).
+ :param singular: The singular form of the word (a string).
+ :param plural: The plural form of the word (a string or :data:`None`).
+ :returns: The singular or plural form of the word (a string).
+
+ When the given count is exactly 1.0 the singular form of the word is
+ selected, in all other cases the plural form of the word is selected.
+
+ If the plural form of the word is not provided it is obtained by
+ concatenating the singular form of the word with the letter "s". Of course
+ this will not always be correct, which is why you have the option to
+ specify both forms.
+ """
+ if not plural:
+ plural = singular + 's'
+ return singular if float(count) == 1.0 else plural
+
+
+def random_string(length=(25, 100), characters=string.ascii_letters):
+ """random_string(length=(25, 100), characters=string.ascii_letters)
+ Generate a random string.
+
+ :param length: The length of the string to be generated (a number or a
+ tuple with two numbers). If this is a tuple then a random
+ number between the two numbers given in the tuple is used.
+ :param characters: The characters to be used (a string, defaults
+ to :data:`string.ascii_letters`).
+ :returns: A random string.
+
+ The :func:`random_string()` function is very useful in test suites; by the
+ time I included it in :mod:`humanfriendly.text` I had already included
+ variants of this function in seven different test suites :-).
+ """
+ if not isinstance(length, numbers.Number):
+ length = random.randint(length[0], length[1])
+ return ''.join(random.choice(characters) for _ in range(length))
+
+
+def split(text, delimiter=','):
+ """
+ Split a comma-separated list of strings.
+
+ :param text: The text to split (a string).
+ :param delimiter: The delimiter to split on (a string).
+ :returns: A list of zero or more nonempty strings.
+
+ Here's the default behavior of Python's built in :meth:`str.split()`
+ function:
+
+ >>> 'foo,bar, baz,'.split(',')
+ ['foo', 'bar', ' baz', '']
+
+ In contrast here's the default behavior of the :func:`split()` function:
+
+ >>> from humanfriendly.text import split
+ >>> split('foo,bar, baz,')
+ ['foo', 'bar', 'baz']
+
+ Here is an example that parses a nested data structure (a mapping of
+ logging level names to one or more styles per level) that's encoded in a
+ string so it can be set as an environment variable:
+
+ >>> from pprint import pprint
+ >>> encoded_data = 'debug=green;warning=yellow;error=red;critical=red,bold'
+ >>> parsed_data = dict((k, split(v, ',')) for k, v in (split(kv, '=') for kv in split(encoded_data, ';')))
+ >>> pprint(parsed_data)
+ {'debug': ['green'],
+ 'warning': ['yellow'],
+ 'error': ['red'],
+ 'critical': ['red', 'bold']}
+ """
+ return [token.strip() for token in text.split(delimiter) if token and not token.isspace()]
+
+
+def split_paragraphs(text):
+ """
+ Split a string into paragraphs (one or more lines delimited by an empty line).
+
+ :param text: The text to split into paragraphs (a string).
+ :returns: A list of strings.
+ """
+ paragraphs = []
+ for chunk in text.split('\n\n'):
+ chunk = trim_empty_lines(chunk)
+ if chunk and not chunk.isspace():
+ paragraphs.append(chunk)
+ return paragraphs
+
+
+def tokenize(text):
+ """
+ Tokenize a text into numbers and strings.
+
+ :param text: The text to tokenize (a string).
+ :returns: A list of strings and/or numbers.
+
+ This function is used to implement robust tokenization of user input in
+ functions like :func:`.parse_size()` and :func:`.parse_timespan()`. It
+ automatically coerces integer and floating point numbers, ignores
+ whitespace and knows how to separate numbers from strings even without
+ whitespace. Some examples to make this more concrete:
+
+ >>> from humanfriendly.text import tokenize
+ >>> tokenize('42')
+ [42]
+ >>> tokenize('42MB')
+ [42, 'MB']
+ >>> tokenize('42.5MB')
+ [42.5, 'MB']
+ >>> tokenize('42.5 MB')
+ [42.5, 'MB']
+ """
+ tokenized_input = []
+ for token in re.split(r'(\d+(?:\.\d+)?)', text):
+ token = token.strip()
+ if re.match(r'\d+\.\d+', token):
+ tokenized_input.append(float(token))
+ elif token.isdigit():
+ tokenized_input.append(int(token))
+ elif token:
+ tokenized_input.append(token)
+ return tokenized_input
+
+
+def trim_empty_lines(text):
+ """
+ Trim leading and trailing empty lines from the given text.
+
+ :param text: The text to trim (a string).
+ :returns: The trimmed text (a string).
+ """
+ lines = text.splitlines(True)
+ while lines and is_empty_line(lines[0]):
+ lines.pop(0)
+ while lines and is_empty_line(lines[-1]):
+ lines.pop(-1)
+ return ''.join(lines)
diff --git a/contrib/python/humanfriendly/py3/humanfriendly/usage.py b/contrib/python/humanfriendly/py3/humanfriendly/usage.py
new file mode 100644
index 0000000000..81ba943ae0
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/humanfriendly/usage.py
@@ -0,0 +1,351 @@
+# Human friendly input/output in Python.
+#
+# Author: Peter Odding <peter@peterodding.com>
+# Last Change: June 11, 2021
+# URL: https://humanfriendly.readthedocs.io
+
+"""
+Parsing and reformatting of usage messages.
+
+The :mod:`~humanfriendly.usage` module parses and reformats usage messages:
+
+- The :func:`format_usage()` function takes a usage message and inserts ANSI
+ escape sequences that highlight items of special significance like command
+ line options, meta variables, etc. The resulting usage message is (intended
+ to be) easier to read on a terminal.
+
+- The :func:`render_usage()` function takes a usage message and rewrites it to
+ reStructuredText_ suitable for inclusion in the documentation of a Python
+ package. This provides a DRY solution to keeping a single authoritative
+ definition of the usage message while making it easily available in
+ documentation. As a cherry on the cake it's not just a pre-formatted dump of
+ the usage message but a nicely formatted reStructuredText_ fragment.
+
+- The remaining functions in this module support the two functions above.
+
+Usage messages in general are free format of course, however the functions in
+this module assume a certain structure from usage messages in order to
+successfully parse and reformat them, refer to :func:`parse_usage()` for
+details.
+
+.. _DRY: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
+.. _reStructuredText: https://en.wikipedia.org/wiki/ReStructuredText
+"""
+
+# Standard library modules.
+import csv
+import functools
+import logging
+import re
+
+# Standard library module or external dependency (see setup.py).
+from importlib import import_module
+
+# Modules included in our package.
+from humanfriendly.compat import StringIO
+from humanfriendly.text import dedent, split_paragraphs, trim_empty_lines
+
+# Public identifiers that require documentation.
+__all__ = (
+ 'find_meta_variables',
+ 'format_usage',
+ 'import_module', # previously exported (backwards compatibility)
+ 'inject_usage',
+ 'parse_usage',
+ 'render_usage',
+ 'USAGE_MARKER',
+)
+
+USAGE_MARKER = "Usage:"
+"""The string that starts the first line of a usage message."""
+
+START_OF_OPTIONS_MARKER = "Supported options:"
+"""The string that marks the start of the documented command line options."""
+
+# Compiled regular expression used to tokenize usage messages.
+USAGE_PATTERN = re.compile(r'''
+ # Make sure whatever we're matching isn't preceded by a non-whitespace
+ # character.
+ (?<!\S)
+ (
+ # A short command line option or a long command line option
+ # (possibly including a meta variable for a value).
+ (-\w|--\w+(-\w+)*(=\S+)?)
+ # Or ...
+ |
+ # An environment variable.
+ \$[A-Za-z_][A-Za-z0-9_]*
+ # Or ...
+ |
+ # Might be a meta variable (usage() will figure it out).
+ [A-Z][A-Z0-9_]+
+ )
+''', re.VERBOSE)
+
+# Compiled regular expression used to recognize options.
+OPTION_PATTERN = re.compile(r'^(-\w|--\w+(-\w+)*(=\S+)?)$')
+
+# Initialize a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+def format_usage(usage_text):
+ """
+ Highlight special items in a usage message.
+
+ :param usage_text: The usage message to process (a string).
+ :returns: The usage message with special items highlighted.
+
+ This function highlights the following special items:
+
+ - The initial line of the form "Usage: ..."
+ - Short and long command line options
+ - Environment variables
+ - Meta variables (see :func:`find_meta_variables()`)
+
+ All items are highlighted in the color defined by
+ :data:`.HIGHLIGHT_COLOR`.
+ """
+ # Ugly workaround to avoid circular import errors due to interdependencies
+ # between the humanfriendly.terminal and humanfriendly.usage modules.
+ from humanfriendly.terminal import ansi_wrap, HIGHLIGHT_COLOR
+ formatted_lines = []
+ meta_variables = find_meta_variables(usage_text)
+ for line in usage_text.strip().splitlines(True):
+ if line.startswith(USAGE_MARKER):
+ # Highlight the "Usage: ..." line in bold font and color.
+ formatted_lines.append(ansi_wrap(line, color=HIGHLIGHT_COLOR))
+ else:
+ # Highlight options, meta variables and environment variables.
+ formatted_lines.append(replace_special_tokens(
+ line, meta_variables,
+ lambda token: ansi_wrap(token, color=HIGHLIGHT_COLOR),
+ ))
+ return ''.join(formatted_lines)
+
+
+def find_meta_variables(usage_text):
+ """
+ Find the meta variables in the given usage message.
+
+ :param usage_text: The usage message to parse (a string).
+ :returns: A list of strings with any meta variables found in the usage
+ message.
+
+ When a command line option requires an argument, the convention is to
+ format such options as ``--option=ARG``. The text ``ARG`` in this example
+ is the meta variable.
+ """
+ meta_variables = set()
+ for match in USAGE_PATTERN.finditer(usage_text):
+ token = match.group(0)
+ if token.startswith('-'):
+ option, _, value = token.partition('=')
+ if value:
+ meta_variables.add(value)
+ return list(meta_variables)
+
+
+def parse_usage(text):
+ """
+ Parse a usage message by inferring its structure (and making some assumptions :-).
+
+ :param text: The usage message to parse (a string).
+ :returns: A tuple of two lists:
+
+ 1. A list of strings with the paragraphs of the usage message's
+ "introduction" (the paragraphs before the documentation of the
+ supported command line options).
+
+ 2. A list of strings with pairs of command line options and their
+ descriptions: Item zero is a line listing a supported command
+ line option, item one is the description of that command line
+ option, item two is a line listing another supported command
+ line option, etc.
+
+ Usage messages in general are free format of course, however
+ :func:`parse_usage()` assume a certain structure from usage messages in
+ order to successfully parse them:
+
+ - The usage message starts with a line ``Usage: ...`` that shows a symbolic
+ representation of the way the program is to be invoked.
+
+ - After some free form text a line ``Supported options:`` (surrounded by
+ empty lines) precedes the documentation of the supported command line
+ options.
+
+ - The command line options are documented as follows::
+
+ -v, --verbose
+
+ Make more noise.
+
+ So all of the variants of the command line option are shown together on a
+ separate line, followed by one or more paragraphs describing the option.
+
+ - There are several other minor assumptions, but to be honest I'm not sure if
+ anyone other than me is ever going to use this functionality, so for now I
+ won't list every intricate detail :-).
+
+ If you're curious anyway, refer to the usage message of the `humanfriendly`
+ package (defined in the :mod:`humanfriendly.cli` module) and compare it with
+ the usage message you see when you run ``humanfriendly --help`` and the
+ generated usage message embedded in the readme.
+
+ Feel free to request more detailed documentation if you're interested in
+ using the :mod:`humanfriendly.usage` module outside of the little ecosystem
+ of Python packages that I have been building over the past years.
+ """
+ introduction = []
+ documented_options = []
+ # Split the raw usage message into paragraphs.
+ paragraphs = split_paragraphs(text)
+ # Get the paragraphs that are part of the introduction.
+ while paragraphs:
+ # Check whether we've found the end of the introduction.
+ end_of_intro = (paragraphs[0] == START_OF_OPTIONS_MARKER)
+ # Append the current paragraph to the introduction.
+ introduction.append(paragraphs.pop(0))
+ # Stop after we've processed the complete introduction.
+ if end_of_intro:
+ break
+ logger.debug("Parsed introduction: %s", introduction)
+ # Parse the paragraphs that document command line options.
+ while paragraphs:
+ documented_options.append(dedent(paragraphs.pop(0)))
+ description = []
+ while paragraphs:
+ # Check if the next paragraph starts the documentation of another
+ # command line option. We split on a comma followed by a space so
+ # that our parsing doesn't trip up when the label used for an
+ # option's value contains commas.
+ tokens = [t.strip() for t in re.split(r',\s', paragraphs[0]) if t and not t.isspace()]
+ if all(OPTION_PATTERN.match(t) for t in tokens):
+ break
+ else:
+ description.append(paragraphs.pop(0))
+ # Join the description's paragraphs back together so we can remove
+ # common leading indentation.
+ documented_options.append(dedent('\n\n'.join(description)))
+ logger.debug("Parsed options: %s", documented_options)
+ return introduction, documented_options
+
+
+def render_usage(text):
+ """
+ Reformat a command line program's usage message to reStructuredText_.
+
+ :param text: The plain text usage message (a string).
+ :returns: The usage message rendered to reStructuredText_ (a string).
+ """
+ meta_variables = find_meta_variables(text)
+ introduction, options = parse_usage(text)
+ output = [render_paragraph(p, meta_variables) for p in introduction]
+ if options:
+ output.append('\n'.join([
+ '.. csv-table::',
+ ' :header: Option, Description',
+ ' :widths: 30, 70',
+ '',
+ ]))
+ csv_buffer = StringIO()
+ csv_writer = csv.writer(csv_buffer)
+ while options:
+ variants = options.pop(0)
+ description = options.pop(0)
+ csv_writer.writerow([
+ render_paragraph(variants, meta_variables),
+ ('\n\n'.join(render_paragraph(p, meta_variables) for p in split_paragraphs(description))).rstrip(),
+ ])
+ csv_lines = csv_buffer.getvalue().splitlines()
+ output.append('\n'.join(' %s' % line for line in csv_lines))
+ logger.debug("Rendered output: %s", output)
+ return '\n\n'.join(trim_empty_lines(o) for o in output)
+
+
+def inject_usage(module_name):
+ """
+ Use cog_ to inject a usage message into a reStructuredText_ file.
+
+ :param module_name: The name of the module whose ``__doc__`` attribute is
+ the source of the usage message (a string).
+
+ This simple wrapper around :func:`render_usage()` makes it very easy to
+ inject a reformatted usage message into your documentation using cog_. To
+ use it you add a fragment like the following to your ``*.rst`` file::
+
+ .. [[[cog
+ .. from humanfriendly.usage import inject_usage
+ .. inject_usage('humanfriendly.cli')
+ .. ]]]
+ .. [[[end]]]
+
+ The lines in the fragment above are single line reStructuredText_ comments
+ that are not copied to the output. Their purpose is to instruct cog_ where
+ to inject the reformatted usage message. Once you've added these lines to
+ your ``*.rst`` file, updating the rendered usage message becomes really
+ simple thanks to cog_:
+
+ .. code-block:: sh
+
+ $ cog.py -r README.rst
+
+ This will inject or replace the rendered usage message in your
+ ``README.rst`` file with an up to date copy.
+
+ .. _cog: http://nedbatchelder.com/code/cog/
+ """
+ import cog
+ usage_text = import_module(module_name).__doc__
+ cog.out("\n" + render_usage(usage_text) + "\n\n")
+
+
+def render_paragraph(paragraph, meta_variables):
+ # Reformat the "Usage:" line to highlight "Usage:" in bold and show the
+ # remainder of the line as pre-formatted text.
+ if paragraph.startswith(USAGE_MARKER):
+ tokens = paragraph.split()
+ return "**%s** `%s`" % (tokens[0], ' '.join(tokens[1:]))
+ # Reformat the "Supported options:" line to highlight it in bold.
+ if paragraph == 'Supported options:':
+ return "**%s**" % paragraph
+ # Reformat shell transcripts into code blocks.
+ if re.match(r'^\s*\$\s+\S', paragraph):
+ # Split the paragraph into lines.
+ lines = paragraph.splitlines()
+ # Check if the paragraph is already indented.
+ if not paragraph[0].isspace():
+ # If the paragraph isn't already indented we'll indent it now.
+ lines = [' %s' % line for line in lines]
+ lines.insert(0, '.. code-block:: sh')
+ lines.insert(1, '')
+ return "\n".join(lines)
+ # The following reformatting applies only to paragraphs which are not
+ # indented. Yes this is a hack - for now we assume that indented paragraphs
+ # are code blocks, even though this assumption can be wrong.
+ if not paragraph[0].isspace():
+ # Change UNIX style `quoting' so it doesn't trip up DocUtils.
+ paragraph = re.sub("`(.+?)'", r'"\1"', paragraph)
+ # Escape asterisks.
+ paragraph = paragraph.replace('*', r'\*')
+ # Reformat inline tokens.
+ paragraph = replace_special_tokens(
+ paragraph, meta_variables,
+ lambda token: '``%s``' % token,
+ )
+ return paragraph
+
+
+def replace_special_tokens(text, meta_variables, replace_fn):
+ return USAGE_PATTERN.sub(functools.partial(
+ replace_tokens_callback,
+ meta_variables=meta_variables,
+ replace_fn=replace_fn
+ ), text)
+
+
+def replace_tokens_callback(match, meta_variables, replace_fn):
+ token = match.group(0)
+ if not (re.match('^[A-Z][A-Z0-9_]+$', token) and token not in meta_variables):
+ token = replace_fn(token)
+ return token
diff --git a/contrib/python/humanfriendly/py3/ya.make b/contrib/python/humanfriendly/py3/ya.make
new file mode 100644
index 0000000000..e6814f8a46
--- /dev/null
+++ b/contrib/python/humanfriendly/py3/ya.make
@@ -0,0 +1,41 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(10.0)
+
+LICENSE(MIT)
+
+NO_LINT()
+
+NO_CHECK_IMPORTS(
+ humanfriendly.sphinx
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ humanfriendly/__init__.py
+ humanfriendly/case.py
+ humanfriendly/cli.py
+ humanfriendly/compat.py
+ humanfriendly/decorators.py
+ humanfriendly/deprecation.py
+ humanfriendly/prompts.py
+ humanfriendly/sphinx.py
+ humanfriendly/tables.py
+ humanfriendly/terminal/__init__.py
+ humanfriendly/terminal/html.py
+ humanfriendly/terminal/spinners.py
+ humanfriendly/testing.py
+ humanfriendly/text.py
+ humanfriendly/usage.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/humanfriendly/py3/
+ .dist-info/METADATA
+ .dist-info/entry_points.txt
+ .dist-info/top_level.txt
+)
+
+END()
diff --git a/contrib/python/humanfriendly/ya.make b/contrib/python/humanfriendly/ya.make
new file mode 100644
index 0000000000..24b7388aa5
--- /dev/null
+++ b/contrib/python/humanfriendly/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/humanfriendly/py2)
+ELSE()
+ PEERDIR(contrib/python/humanfriendly/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/marisa-trie/agent.pxd b/contrib/python/marisa-trie/agent.pxd
new file mode 100644
index 0000000000..bf019673c2
--- /dev/null
+++ b/contrib/python/marisa-trie/agent.pxd
@@ -0,0 +1,22 @@
+cimport query, key
+
+cdef extern from "<marisa/agent.h>" namespace "marisa" nogil:
+ cdef cppclass Agent:
+ Agent() except +
+
+ query.Query &query()
+ key.Key &key()
+
+ void set_query(char *str)
+ void set_query(char *ptr, int length)
+ void set_query(int key_id)
+
+ void set_key(char *str)
+ void set_key(char *ptr, int length)
+ void set_key(int id)
+
+ void clear()
+
+ void init_state()
+
+ void swap(Agent &rhs)
diff --git a/contrib/python/marisa-trie/base.pxd b/contrib/python/marisa-trie/base.pxd
new file mode 100644
index 0000000000..c434e82122
--- /dev/null
+++ b/contrib/python/marisa-trie/base.pxd
@@ -0,0 +1,63 @@
+cdef extern from "<marisa/base.h>":
+
+ # A dictionary consists of 3 tries in default. Usually more tries make a
+ # dictionary space-efficient but time-inefficient.
+ ctypedef enum marisa_num_tries:
+ MARISA_MIN_NUM_TRIES
+ MARISA_MAX_NUM_TRIES
+ MARISA_DEFAULT_NUM_TRIES
+
+
+ # This library uses a cache technique to accelerate search functions. The
+ # following enumerated type marisa_cache_level gives a list of available cache
+ # size options. A larger cache enables faster search but takes a more space.
+ ctypedef enum marisa_cache_level:
+ MARISA_HUGE_CACHE
+ MARISA_LARGE_CACHE
+ MARISA_NORMAL_CACHE
+ MARISA_SMALL_CACHE
+ MARISA_TINY_CACHE
+ MARISA_DEFAULT_CACHE
+
+ # This library provides 2 kinds of TAIL implementations.
+ ctypedef enum marisa_tail_mode:
+ # MARISA_TEXT_TAIL merges last labels as zero-terminated strings. So, it is
+ # available if and only if the last labels do not contain a NULL character.
+ # If MARISA_TEXT_TAIL is specified and a NULL character exists in the last
+ # labels, the setting is automatically switched to MARISA_BINARY_TAIL.
+ MARISA_TEXT_TAIL
+
+ # MARISA_BINARY_TAIL also merges last labels but as byte sequences. It uses
+ # a bit vector to detect the end of a sequence, instead of NULL characters.
+ # So, MARISA_BINARY_TAIL requires a larger space if the average length of
+ # labels is greater than 8.
+ MARISA_BINARY_TAIL
+
+ MARISA_DEFAULT_TAIL
+
+ # The arrangement of nodes affects the time cost of matching and the order of
+ # predictive search.
+ ctypedef enum marisa_node_order:
+ # MARISA_LABEL_ORDER arranges nodes in ascending label order.
+ # MARISA_LABEL_ORDER is useful if an application needs to predict keys in
+ # label order.
+ MARISA_LABEL_ORDER
+
+ # MARISA_WEIGHT_ORDER arranges nodes in descending weight order.
+ # MARISA_WEIGHT_ORDER is generally a better choice because it enables faster
+ # matching.
+ MARISA_WEIGHT_ORDER
+ MARISA_DEFAULT_ORDER
+
+ ctypedef enum marisa_config_mask:
+ MARISA_NUM_TRIES_MASK
+ MARISA_CACHE_LEVEL_MASK
+ MARISA_TAIL_MODE_MASK
+ MARISA_NODE_ORDER_MASK
+ MARISA_CONFIG_MASK
+
+
+cdef extern from "<marisa/base.h>" namespace "marisa":
+ ctypedef marisa_cache_level CacheLevel
+ ctypedef marisa_tail_mode TailMode
+ ctypedef marisa_node_order NodeOrder
diff --git a/contrib/python/marisa-trie/iostream.pxd b/contrib/python/marisa-trie/iostream.pxd
new file mode 100644
index 0000000000..435ee85bb0
--- /dev/null
+++ b/contrib/python/marisa-trie/iostream.pxd
@@ -0,0 +1,7 @@
+from std_iostream cimport istream, ostream
+from trie cimport Trie
+
+cdef extern from "<marisa/iostream.h>" namespace "marisa" nogil:
+
+ istream &read(istream &stream, Trie *trie)
+ ostream &write(ostream &stream, Trie &trie)
diff --git a/contrib/python/marisa-trie/key.pxd b/contrib/python/marisa-trie/key.pxd
new file mode 100644
index 0000000000..d99dee5e04
--- /dev/null
+++ b/contrib/python/marisa-trie/key.pxd
@@ -0,0 +1,22 @@
+cdef extern from "<marisa/key.h>" namespace "marisa" nogil:
+
+ cdef cppclass Key:
+ Key()
+ Key(Key &query)
+
+ #Key &operator=(Key &query)
+
+ char operator[](int i)
+
+ void set_str(char *str)
+ void set_str(char *ptr, int length)
+ void set_id(int id)
+ void set_weight(float weight)
+
+ char *ptr()
+ int length()
+ int id()
+ float weight()
+
+ void clear()
+ void swap(Key &rhs)
diff --git a/contrib/python/marisa-trie/keyset.pxd b/contrib/python/marisa-trie/keyset.pxd
new file mode 100644
index 0000000000..1fb99a40c5
--- /dev/null
+++ b/contrib/python/marisa-trie/keyset.pxd
@@ -0,0 +1,30 @@
+cimport key
+
+cdef extern from "<marisa/keyset.h>" namespace "marisa" nogil:
+ cdef cppclass Keyset:
+
+# cdef enum constants:
+# BASE_BLOCK_SIZE = 4096
+# EXTRA_BLOCK_SIZE = 1024
+# KEY_BLOCK_SIZE = 256
+
+ Keyset()
+
+ void push_back(key.Key &key)
+ void push_back(key.Key &key, char end_marker)
+
+ void push_back(char *str)
+ void push_back(char *ptr, int length)
+ void push_back(char *ptr, int length, float weight)
+
+ key.Key &operator[](int i)
+
+ int num_keys()
+ bint empty()
+
+ int size()
+ int total_length()
+
+ void reset()
+ void clear()
+ void swap(Keyset &rhs)
diff --git a/contrib/python/marisa-trie/marisa/agent.cc b/contrib/python/marisa-trie/marisa/agent.cc
new file mode 100644
index 0000000000..7f7f49f1bc
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/agent.cc
@@ -0,0 +1,51 @@
+#include <new>
+
+#include "agent.h"
+#include "grimoire/trie.h"
+
+namespace marisa {
+
+Agent::Agent() : query_(), key_(), state_() {}
+
+Agent::~Agent() {}
+
+void Agent::set_query(const char *str) {
+ MARISA_THROW_IF(str == NULL, MARISA_NULL_ERROR);
+ if (state_.get() != NULL) {
+ state_->reset();
+ }
+ query_.set_str(str);
+}
+
+void Agent::set_query(const char *ptr, std::size_t length) {
+ MARISA_THROW_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
+ if (state_.get() != NULL) {
+ state_->reset();
+ }
+ query_.set_str(ptr, length);
+}
+
+void Agent::set_query(std::size_t key_id) {
+ if (state_.get() != NULL) {
+ state_->reset();
+ }
+ query_.set_id(key_id);
+}
+
+void Agent::init_state() {
+ MARISA_THROW_IF(state_.get() != NULL, MARISA_STATE_ERROR);
+ state_.reset(new (std::nothrow) grimoire::State);
+ MARISA_THROW_IF(state_.get() == NULL, MARISA_MEMORY_ERROR);
+}
+
+void Agent::clear() {
+ Agent().swap(*this);
+}
+
+void Agent::swap(Agent &rhs) {
+ query_.swap(rhs.query_);
+ key_.swap(rhs.key_);
+ state_.swap(rhs.state_);
+}
+
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/agent.h b/contrib/python/marisa-trie/marisa/agent.h
new file mode 100644
index 0000000000..0f89f7df0f
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/agent.h
@@ -0,0 +1,75 @@
+#pragma once
+
+#ifndef MARISA_AGENT_H_
+#define MARISA_AGENT_H_
+
+#include "key.h"
+#include "query.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class State;
+
+} // namespace trie
+} // namespace grimoire
+
+class Agent {
+ public:
+ Agent();
+ ~Agent();
+
+ const Query &query() const {
+ return query_;
+ }
+ const Key &key() const {
+ return key_;
+ }
+
+ void set_query(const char *str);
+ void set_query(const char *ptr, std::size_t length);
+ void set_query(std::size_t key_id);
+
+ const grimoire::trie::State &state() const {
+ return *state_;
+ }
+ grimoire::trie::State &state() {
+ return *state_;
+ }
+
+ void set_key(const char *str) {
+ MARISA_DEBUG_IF(str == NULL, MARISA_NULL_ERROR);
+ key_.set_str(str);
+ }
+ void set_key(const char *ptr, std::size_t length) {
+ MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
+ MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ key_.set_str(ptr, length);
+ }
+ void set_key(std::size_t id) {
+ MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ key_.set_id(id);
+ }
+
+ bool has_state() const {
+ return state_.get() != NULL;
+ }
+ void init_state();
+
+ void clear();
+ void swap(Agent &rhs);
+
+ private:
+ Query query_;
+ Key key_;
+ scoped_ptr<grimoire::trie::State> state_;
+
+ // Disallows copy and assignment.
+ Agent(const Agent &);
+ Agent &operator=(const Agent &);
+};
+
+} // namespace marisa
+
+#endif // MARISA_AGENT_H_
diff --git a/contrib/python/marisa-trie/marisa/base.h b/contrib/python/marisa-trie/marisa/base.h
new file mode 100644
index 0000000000..5c595dcd2b
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/base.h
@@ -0,0 +1,196 @@
+#pragma once
+
+#ifndef MARISA_BASE_H_
+#define MARISA_BASE_H_
+
+// Old Visual C++ does not provide stdint.h.
+#ifndef _MSC_VER
+ #include <stdint.h>
+#endif // _MSC_VER
+
+#ifdef __cplusplus
+ #include <cstddef>
+#else // __cplusplus
+ #include <stddef.h>
+#endif // __cplusplus
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#ifdef _MSC_VER
+typedef unsigned __int8 marisa_uint8;
+typedef unsigned __int16 marisa_uint16;
+typedef unsigned __int32 marisa_uint32;
+typedef unsigned __int64 marisa_uint64;
+#else // _MSC_VER
+typedef uint8_t marisa_uint8;
+typedef uint16_t marisa_uint16;
+typedef uint32_t marisa_uint32;
+typedef uint64_t marisa_uint64;
+#endif // _MSC_VER
+
+#if defined(_WIN64) || defined(__amd64__) || defined(__x86_64__) || \
+ defined(__ia64__) || defined(__ppc64__) || defined(__powerpc64__) || \
+ defined(__sparc64__) || defined(__mips64__) || defined(__aarch64__) || \
+ defined(__s390x__)
+ #define MARISA_WORD_SIZE 64
+#else // defined(_WIN64), etc.
+ #define MARISA_WORD_SIZE 32
+#endif // defined(_WIN64), etc.
+
+//#define MARISA_WORD_SIZE (sizeof(void *) * 8)
+
+#define MARISA_UINT8_MAX ((marisa_uint8)~(marisa_uint8)0)
+#define MARISA_UINT16_MAX ((marisa_uint16)~(marisa_uint16)0)
+#define MARISA_UINT32_MAX ((marisa_uint32)~(marisa_uint32)0)
+#define MARISA_UINT64_MAX ((marisa_uint64)~(marisa_uint64)0)
+#define MARISA_SIZE_MAX ((size_t)~(size_t)0)
+
+#define MARISA_INVALID_LINK_ID MARISA_UINT32_MAX
+#define MARISA_INVALID_KEY_ID MARISA_UINT32_MAX
+#define MARISA_INVALID_EXTRA (MARISA_UINT32_MAX >> 8)
+
+// Error codes are defined as members of marisa_error_code. This library throws
+// an exception with one of the error codes when an error occurs.
+typedef enum marisa_error_code_ {
+ // MARISA_OK means that a requested operation has succeeded. In practice, an
+ // exception never has MARISA_OK because it is not an error.
+ MARISA_OK = 0,
+
+ // MARISA_STATE_ERROR means that an object was not ready for a requested
+ // operation. For example, an operation to modify a fixed vector throws an
+ // exception with MARISA_STATE_ERROR.
+ MARISA_STATE_ERROR = 1,
+
+ // MARISA_NULL_ERROR means that an invalid NULL pointer has been given.
+ MARISA_NULL_ERROR = 2,
+
+ // MARISA_BOUND_ERROR means that an operation has tried to access an out of
+ // range address.
+ MARISA_BOUND_ERROR = 3,
+
+ // MARISA_RANGE_ERROR means that an out of range value has appeared in
+ // operation.
+ MARISA_RANGE_ERROR = 4,
+
+ // MARISA_CODE_ERROR means that an undefined code has appeared in operation.
+ MARISA_CODE_ERROR = 5,
+
+ // MARISA_RESET_ERROR means that a smart pointer has tried to reset itself.
+ MARISA_RESET_ERROR = 6,
+
+ // MARISA_SIZE_ERROR means that a size has exceeded a library limitation.
+ MARISA_SIZE_ERROR = 7,
+
+ // MARISA_MEMORY_ERROR means that a memory allocation has failed.
+ MARISA_MEMORY_ERROR = 8,
+
+ // MARISA_IO_ERROR means that an I/O operation has failed.
+ MARISA_IO_ERROR = 9,
+
+ // MARISA_FORMAT_ERROR means that input was in invalid format.
+ MARISA_FORMAT_ERROR = 10,
+} marisa_error_code;
+
+// Min/max values, flags and masks for dictionary settings are defined below.
+// Please note that unspecified settings will be replaced with the default
+// settings. For example, 0 is equivalent to (MARISA_DEFAULT_NUM_TRIES |
+// MARISA_DEFAULT_TRIE | MARISA_DEFAULT_TAIL | MARISA_DEFAULT_ORDER).
+
+// A dictionary consists of 3 tries in default. Usually more tries make a
+// dictionary space-efficient but time-inefficient.
+typedef enum marisa_num_tries_ {
+ MARISA_MIN_NUM_TRIES = 0x00001,
+ MARISA_MAX_NUM_TRIES = 0x0007F,
+ MARISA_DEFAULT_NUM_TRIES = 0x00003,
+} marisa_num_tries;
+
+// This library uses a cache technique to accelerate search functions. The
+// following enumerated type marisa_cache_level gives a list of available cache
+// size options. A larger cache enables faster search but takes a more space.
+typedef enum marisa_cache_level_ {
+ MARISA_HUGE_CACHE = 0x00080,
+ MARISA_LARGE_CACHE = 0x00100,
+ MARISA_NORMAL_CACHE = 0x00200,
+ MARISA_SMALL_CACHE = 0x00400,
+ MARISA_TINY_CACHE = 0x00800,
+ MARISA_DEFAULT_CACHE = MARISA_NORMAL_CACHE
+} marisa_cache_level;
+
+// This library provides 2 kinds of TAIL implementations.
+typedef enum marisa_tail_mode_ {
+ // MARISA_TEXT_TAIL merges last labels as zero-terminated strings. So, it is
+ // available if and only if the last labels do not contain a NULL character.
+ // If MARISA_TEXT_TAIL is specified and a NULL character exists in the last
+ // labels, the setting is automatically switched to MARISA_BINARY_TAIL.
+ MARISA_TEXT_TAIL = 0x01000,
+
+ // MARISA_BINARY_TAIL also merges last labels but as byte sequences. It uses
+ // a bit vector to detect the end of a sequence, instead of NULL characters.
+ // So, MARISA_BINARY_TAIL requires a larger space if the average length of
+ // labels is greater than 8.
+ MARISA_BINARY_TAIL = 0x02000,
+
+ MARISA_DEFAULT_TAIL = MARISA_TEXT_TAIL,
+} marisa_tail_mode;
+
+// The arrangement of nodes affects the time cost of matching and the order of
+// predictive search.
+typedef enum marisa_node_order_ {
+ // MARISA_LABEL_ORDER arranges nodes in ascending label order.
+ // MARISA_LABEL_ORDER is useful if an application needs to predict keys in
+ // label order.
+ MARISA_LABEL_ORDER = 0x10000,
+
+ // MARISA_WEIGHT_ORDER arranges nodes in descending weight order.
+ // MARISA_WEIGHT_ORDER is generally a better choice because it enables faster
+ // matching.
+ MARISA_WEIGHT_ORDER = 0x20000,
+
+ MARISA_DEFAULT_ORDER = MARISA_WEIGHT_ORDER,
+} marisa_node_order;
+
+typedef enum marisa_config_mask_ {
+ MARISA_NUM_TRIES_MASK = 0x0007F,
+ MARISA_CACHE_LEVEL_MASK = 0x00F80,
+ MARISA_TAIL_MODE_MASK = 0x0F000,
+ MARISA_NODE_ORDER_MASK = 0xF0000,
+ MARISA_CONFIG_MASK = 0xFFFFF
+} marisa_config_mask;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#ifdef __cplusplus
+namespace marisa {
+
+typedef ::marisa_uint8 UInt8;
+typedef ::marisa_uint16 UInt16;
+typedef ::marisa_uint32 UInt32;
+typedef ::marisa_uint64 UInt64;
+
+typedef ::marisa_error_code ErrorCode;
+
+typedef ::marisa_cache_level CacheLevel;
+typedef ::marisa_tail_mode TailMode;
+typedef ::marisa_node_order NodeOrder;
+
+template <typename T>
+inline void swap(T &lhs, T &rhs) {
+ T temp = lhs;
+ lhs = rhs;
+ rhs = temp;
+}
+
+} // namespace marisa
+#endif // __cplusplus
+
+#ifdef __cplusplus
+ #include "exception.h"
+ #include "scoped-ptr.h"
+ #include "scoped-array.h"
+#endif // __cplusplus
+
+#endif // MARISA_BASE_H_
diff --git a/contrib/python/marisa-trie/marisa/exception.h b/contrib/python/marisa-trie/marisa/exception.h
new file mode 100644
index 0000000000..630936b23b
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/exception.h
@@ -0,0 +1,84 @@
+#pragma once
+
+#ifndef MARISA_EXCEPTION_H_
+#define MARISA_EXCEPTION_H_
+
+#include <exception>
+
+#include "base.h"
+
+namespace marisa {
+
+// An exception object keeps a filename, a line number, an error code and an
+// error message. The message format is as follows:
+// "__FILE__:__LINE__: error_code: error_message"
+class Exception : public std::exception {
+ public:
+ Exception(const char *filename, int line,
+ ErrorCode error_code, const char *error_message)
+ : std::exception(), filename_(filename), line_(line),
+ error_code_(error_code), error_message_(error_message) {}
+ Exception(const Exception &ex)
+ : std::exception(), filename_(ex.filename_), line_(ex.line_),
+ error_code_(ex.error_code_), error_message_(ex.error_message_) {}
+ virtual ~Exception() {}
+
+ Exception &operator=(const Exception &rhs) {
+ filename_ = rhs.filename_;
+ line_ = rhs.line_;
+ error_code_ = rhs.error_code_;
+ error_message_ = rhs.error_message_;
+ return *this;
+ }
+
+ const char *filename() const {
+ return filename_;
+ }
+ int line() const {
+ return line_;
+ }
+ ErrorCode error_code() const {
+ return error_code_;
+ }
+ const char *error_message() const {
+ return error_message_;
+ }
+
+ virtual const char *what() const noexcept {
+ return error_message_;
+ }
+
+ private:
+ const char *filename_;
+ int line_;
+ ErrorCode error_code_;
+ const char *error_message_;
+};
+
+// These macros are used to convert a line number to a string constant.
+#define MARISA_INT_TO_STR(value) #value
+#define MARISA_LINE_TO_STR(line) MARISA_INT_TO_STR(line)
+#define MARISA_LINE_STR MARISA_LINE_TO_STR(__LINE__)
+
+// MARISA_THROW throws an exception with a filename, a line number, an error
+// code and an error message. The message format is as follows:
+// "__FILE__:__LINE__: error_code: error_message"
+#define MARISA_THROW(error_code, error_message) \
+ (throw marisa::Exception(__FILE__, __LINE__, error_code, \
+ __FILE__ ":" MARISA_LINE_STR ": " #error_code ": " error_message))
+
+// MARISA_THROW_IF throws an exception if `condition' is true.
+#define MARISA_THROW_IF(condition, error_code) \
+ (void)((!(condition)) || (MARISA_THROW(error_code, #condition), 0))
+
+// MARISA_DEBUG_IF is ignored if _DEBUG is undefined. So, it is useful for
+// debugging time-critical codes.
+#ifdef _DEBUG
+ #define MARISA_DEBUG_IF(cond, error_code) MARISA_THROW_IF(cond, error_code)
+#else
+ #define MARISA_DEBUG_IF(cond, error_code)
+#endif
+
+} // namespace marisa
+
+#endif // MARISA_EXCEPTION_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/algorithm.h b/contrib/python/marisa-trie/marisa/grimoire/algorithm.h
new file mode 100644
index 0000000000..71baec34ac
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/algorithm.h
@@ -0,0 +1,27 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_ALGORITHM_H_
+#define MARISA_GRIMOIRE_ALGORITHM_H_
+
+#include "algorithm/sort.h"
+
+namespace marisa {
+namespace grimoire {
+
+class Algorithm {
+ public:
+ Algorithm() {}
+
+ template <typename Iterator>
+ std::size_t sort(Iterator begin, Iterator end) const {
+ return algorithm::sort(begin, end);
+ }
+
+ private:
+ Algorithm(const Algorithm &);
+ Algorithm &operator=(const Algorithm &);
+};
+
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_ALGORITHM_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/algorithm/sort.h b/contrib/python/marisa-trie/marisa/grimoire/algorithm/sort.h
new file mode 100644
index 0000000000..9090336ce6
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/algorithm/sort.h
@@ -0,0 +1,197 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_ALGORITHM_SORT_H_
+#define MARISA_GRIMOIRE_ALGORITHM_SORT_H_
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace algorithm {
+namespace details {
+
+enum {
+ MARISA_INSERTION_SORT_THRESHOLD = 10
+};
+
+template <typename T>
+int get_label(const T &unit, std::size_t depth) {
+ MARISA_DEBUG_IF(depth > unit.length(), MARISA_BOUND_ERROR);
+
+ return (depth < unit.length()) ? (int)(UInt8)unit[depth] : -1;
+}
+
+template <typename T>
+int median(const T &a, const T &b, const T &c, std::size_t depth) {
+ const int x = get_label(a, depth);
+ const int y = get_label(b, depth);
+ const int z = get_label(c, depth);
+ if (x < y) {
+ if (y < z) {
+ return y;
+ } else if (x < z) {
+ return z;
+ }
+ return x;
+ } else if (x < z) {
+ return x;
+ } else if (y < z) {
+ return z;
+ }
+ return y;
+}
+
+template <typename T>
+int compare(const T &lhs, const T &rhs, std::size_t depth) {
+ for (std::size_t i = depth; i < lhs.length(); ++i) {
+ if (i == rhs.length()) {
+ return 1;
+ }
+ if (lhs[i] != rhs[i]) {
+ return (UInt8)lhs[i] - (UInt8)rhs[i];
+ }
+ }
+ if (lhs.length() == rhs.length()) {
+ return 0;
+ }
+ return (lhs.length() < rhs.length()) ? -1 : 1;
+}
+
+template <typename Iterator>
+std::size_t insertion_sort(Iterator l, Iterator r, std::size_t depth) {
+ MARISA_DEBUG_IF(l > r, MARISA_BOUND_ERROR);
+
+ std::size_t count = 1;
+ for (Iterator i = l + 1; i < r; ++i) {
+ int result = 0;
+ for (Iterator j = i; j > l; --j) {
+ result = compare(*(j - 1), *j, depth);
+ if (result <= 0) {
+ break;
+ }
+ marisa::swap(*(j - 1), *j);
+ }
+ if (result != 0) {
+ ++count;
+ }
+ }
+ return count;
+}
+
+template <typename Iterator>
+std::size_t sort(Iterator l, Iterator r, std::size_t depth) {
+ MARISA_DEBUG_IF(l > r, MARISA_BOUND_ERROR);
+
+ std::size_t count = 0;
+ while ((r - l) > MARISA_INSERTION_SORT_THRESHOLD) {
+ Iterator pl = l;
+ Iterator pr = r;
+ Iterator pivot_l = l;
+ Iterator pivot_r = r;
+
+ const int pivot = median(*l, *(l + (r - l) / 2), *(r - 1), depth);
+ for ( ; ; ) {
+ while (pl < pr) {
+ const int label = get_label(*pl, depth);
+ if (label > pivot) {
+ break;
+ } else if (label == pivot) {
+ marisa::swap(*pl, *pivot_l);
+ ++pivot_l;
+ }
+ ++pl;
+ }
+ while (pl < pr) {
+ const int label = get_label(*--pr, depth);
+ if (label < pivot) {
+ break;
+ } else if (label == pivot) {
+ marisa::swap(*pr, *--pivot_r);
+ }
+ }
+ if (pl >= pr) {
+ break;
+ }
+ marisa::swap(*pl, *pr);
+ ++pl;
+ }
+ while (pivot_l > l) {
+ marisa::swap(*--pivot_l, *--pl);
+ }
+ while (pivot_r < r) {
+ marisa::swap(*pivot_r, *pr);
+ ++pivot_r;
+ ++pr;
+ }
+
+ if (((pl - l) > (pr - pl)) || ((r - pr) > (pr - pl))) {
+ if ((pr - pl) == 1) {
+ ++count;
+ } else if ((pr - pl) > 1) {
+ if (pivot == -1) {
+ ++count;
+ } else {
+ count += sort(pl, pr, depth + 1);
+ }
+ }
+
+ if ((pl - l) < (r - pr)) {
+ if ((pl - l) == 1) {
+ ++count;
+ } else if ((pl - l) > 1) {
+ count += sort(l, pl, depth);
+ }
+ l = pr;
+ } else {
+ if ((r - pr) == 1) {
+ ++count;
+ } else if ((r - pr) > 1) {
+ count += sort(pr, r, depth);
+ }
+ r = pl;
+ }
+ } else {
+ if ((pl - l) == 1) {
+ ++count;
+ } else if ((pl - l) > 1) {
+ count += sort(l, pl, depth);
+ }
+
+ if ((r - pr) == 1) {
+ ++count;
+ } else if ((r - pr) > 1) {
+ count += sort(pr, r, depth);
+ }
+
+ l = pl, r = pr;
+ if ((pr - pl) == 1) {
+ ++count;
+ } else if ((pr - pl) > 1) {
+ if (pivot == -1) {
+ l = r;
+ ++count;
+ } else {
+ ++depth;
+ }
+ }
+ }
+ }
+
+ if ((r - l) > 1) {
+ count += insertion_sort(l, r, depth);
+ }
+ return count;
+}
+
+} // namespace details
+
+template <typename Iterator>
+std::size_t sort(Iterator begin, Iterator end) {
+ MARISA_DEBUG_IF(begin > end, MARISA_BOUND_ERROR);
+ return details::sort(begin, end, 0);
+};
+
+} // namespace algorithm
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_ALGORITHM_SORT_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/intrin.h b/contrib/python/marisa-trie/marisa/grimoire/intrin.h
new file mode 100644
index 0000000000..16843b353c
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/intrin.h
@@ -0,0 +1,116 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_INTRIN_H_
+#define MARISA_GRIMOIRE_INTRIN_H_
+
+#include "../base.h"
+
+#if defined(__x86_64__) || defined(_M_X64)
+ #define MARISA_X64
+#elif defined(__i386__) || defined(_M_IX86)
+ #define MARISA_X86
+#else // defined(__i386__) || defined(_M_IX86)
+ #ifdef MARISA_USE_POPCNT
+ #undef MARISA_USE_POPCNT
+ #endif // MARISA_USE_POPCNT
+ #ifdef MARISA_USE_SSE4A
+ #undef MARISA_USE_SSE4A
+ #endif // MARISA_USE_SSE4A
+ #ifdef MARISA_USE_SSE4
+ #undef MARISA_USE_SSE4
+ #endif // MARISA_USE_SSE4
+ #ifdef MARISA_USE_SSE4_2
+ #undef MARISA_USE_SSE4_2
+ #endif // MARISA_USE_SSE4_2
+ #ifdef MARISA_USE_SSE4_1
+ #undef MARISA_USE_SSE4_1
+ #endif // MARISA_USE_SSE4_1
+ #ifdef MARISA_USE_SSSE3
+ #undef MARISA_USE_SSSE3
+ #endif // MARISA_USE_SSSE3
+ #ifdef MARISA_USE_SSE3
+ #undef MARISA_USE_SSE3
+ #endif // MARISA_USE_SSE3
+ #ifdef MARISA_USE_SSE2
+ #undef MARISA_USE_SSE2
+ #endif // MARISA_USE_SSE2
+#endif // defined(__i386__) || defined(_M_IX86)
+
+#ifdef MARISA_USE_POPCNT
+ #ifndef MARISA_USE_SSE3
+ #define MARISA_USE_SSE3
+ #endif // MARISA_USE_SSE3
+ #ifdef _MSC_VER
+ #include <intrin.h>
+ #else // _MSC_VER
+ #include <popcntintrin.h>
+ #endif // _MSC_VER
+#endif // MARISA_USE_POPCNT
+
+#ifdef MARISA_USE_SSE4A
+ #ifndef MARISA_USE_SSE3
+ #define MARISA_USE_SSE3
+ #endif // MARISA_USE_SSE3
+ #ifndef MARISA_USE_POPCNT
+ #define MARISA_USE_POPCNT
+ #endif // MARISA_USE_POPCNT
+#endif // MARISA_USE_SSE4A
+
+#ifdef MARISA_USE_SSE4
+ #ifndef MARISA_USE_SSE4_2
+ #define MARISA_USE_SSE4_2
+ #endif // MARISA_USE_SSE4_2
+#endif // MARISA_USE_SSE4
+
+#ifdef MARISA_USE_SSE4_2
+ #ifndef MARISA_USE_SSE4_1
+ #define MARISA_USE_SSE4_1
+ #endif // MARISA_USE_SSE4_1
+ #ifndef MARISA_USE_POPCNT
+ #define MARISA_USE_POPCNT
+ #endif // MARISA_USE_POPCNT
+#endif // MARISA_USE_SSE4_2
+
+#ifdef MARISA_USE_SSE4_1
+ #ifndef MARISA_USE_SSSE3
+ #define MARISA_USE_SSSE3
+ #endif // MARISA_USE_SSSE3
+#endif // MARISA_USE_SSE4_1
+
+#ifdef MARISA_USE_SSSE3
+ #ifndef MARISA_USE_SSE3
+ #define MARISA_USE_SSE3
+ #endif // MARISA_USE_SSE3
+ #ifdef MARISA_X64
+ #define MARISA_X64_SSSE3
+ #else // MARISA_X64
+ #define MARISA_X86_SSSE3
+ #endif // MAIRSA_X64
+ #include <tmmintrin.h>
+#endif // MARISA_USE_SSSE3
+
+#ifdef MARISA_USE_SSE3
+ #ifndef MARISA_USE_SSE2
+ #define MARISA_USE_SSE2
+ #endif // MARISA_USE_SSE2
+#endif // MARISA_USE_SSE3
+
+#ifdef MARISA_USE_SSE2
+ #ifdef MARISA_X64
+ #define MARISA_X64_SSE2
+ #else // MARISA_X64
+ #define MARISA_X86_SSE2
+ #endif // MAIRSA_X64
+ #include <emmintrin.h>
+#endif // MARISA_USE_SSE2
+
+#ifdef _MSC_VER
+ #if MARISA_WORD_SIZE == 64
+ #include <intrin.h>
+ #pragma intrinsic(_BitScanForward64)
+ #else // MARISA_WORD_SIZE == 64
+ #include <intrin.h>
+ #pragma intrinsic(_BitScanForward)
+ #endif // MARISA_WORD_SIZE == 64
+#endif // _MSC_VER
+
+#endif // MARISA_GRIMOIRE_INTRIN_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io.h b/contrib/python/marisa-trie/marisa/grimoire/io.h
new file mode 100644
index 0000000000..4de0110dbb
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/io.h
@@ -0,0 +1,19 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_IO_H_
+#define MARISA_GRIMOIRE_IO_H_
+
+#include "io/mapper.h"
+#include "io/reader.h"
+#include "io/writer.h"
+
+namespace marisa {
+namespace grimoire {
+
+using io::Mapper;
+using io::Reader;
+using io::Writer;
+
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_IO_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/mapper.cc b/contrib/python/marisa-trie/marisa/grimoire/io/mapper.cc
new file mode 100644
index 0000000000..9ed6ffc755
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/io/mapper.cc
@@ -0,0 +1,163 @@
+#if (defined _WIN32) || (defined _WIN64)
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <windows.h>
+#else // (defined _WIN32) || (defined _WIN64)
+ #include <sys/mman.h>
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+#endif // (defined _WIN32) || (defined _WIN64)
+
+#include "mapper.h"
+
+namespace marisa {
+namespace grimoire {
+namespace io {
+
+#if (defined _WIN32) || (defined _WIN64)
+Mapper::Mapper()
+ : ptr_(NULL), origin_(NULL), avail_(0), size_(0),
+ file_(NULL), map_(NULL) {}
+#else // (defined _WIN32) || (defined _WIN64)
+Mapper::Mapper()
+ : ptr_(NULL), origin_(MAP_FAILED), avail_(0), size_(0), fd_(-1) {}
+#endif // (defined _WIN32) || (defined _WIN64)
+
+#if (defined _WIN32) || (defined _WIN64)
+Mapper::~Mapper() {
+ if (origin_ != NULL) {
+ ::UnmapViewOfFile(origin_);
+ }
+
+ if (map_ != NULL) {
+ ::CloseHandle(map_);
+ }
+
+ if (file_ != NULL) {
+ ::CloseHandle(file_);
+ }
+}
+#else // (defined _WIN32) || (defined _WIN64)
+Mapper::~Mapper() {
+ if (origin_ != MAP_FAILED) {
+ ::munmap(origin_, size_);
+ }
+
+ if (fd_ != -1) {
+ ::close(fd_);
+ }
+}
+#endif // (defined _WIN32) || (defined _WIN64)
+
+void Mapper::open(const char *filename) {
+ MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
+
+ Mapper temp;
+ temp.open_(filename);
+ swap(temp);
+}
+
+void Mapper::open(const void *ptr, std::size_t size) {
+ MARISA_THROW_IF((ptr == NULL) && (size != 0), MARISA_NULL_ERROR);
+
+ Mapper temp;
+ temp.open_(ptr, size);
+ swap(temp);
+}
+
+void Mapper::seek(std::size_t size) {
+ MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
+ MARISA_THROW_IF(size > avail_, MARISA_IO_ERROR);
+
+ map_data(size);
+}
+
+bool Mapper::is_open() const {
+ return ptr_ != NULL;
+}
+
+void Mapper::clear() {
+ Mapper().swap(*this);
+}
+
+void Mapper::swap(Mapper &rhs) {
+ marisa::swap(ptr_, rhs.ptr_);
+ marisa::swap(avail_, rhs.avail_);
+ marisa::swap(origin_, rhs.origin_);
+ marisa::swap(size_, rhs.size_);
+#if (defined _WIN32) || (defined _WIN64)
+ marisa::swap(file_, rhs.file_);
+ marisa::swap(map_, rhs.map_);
+#else // (defined _WIN32) || (defined _WIN64)
+ marisa::swap(fd_, rhs.fd_);
+#endif // (defined _WIN32) || (defined _WIN64)
+}
+
+const void *Mapper::map_data(std::size_t size) {
+ MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
+ MARISA_THROW_IF(size > avail_, MARISA_IO_ERROR);
+
+ const char * const data = static_cast<const char *>(ptr_);
+ ptr_ = data + size;
+ avail_ -= size;
+ return data;
+}
+
+#if (defined _WIN32) || (defined _WIN64)
+ #ifdef __MSVCRT_VERSION__
+ #if __MSVCRT_VERSION__ >= 0x0601
+ #define MARISA_HAS_STAT64
+ #endif // __MSVCRT_VERSION__ >= 0x0601
+ #endif // __MSVCRT_VERSION__
+void Mapper::open_(const char *filename) {
+ #ifdef MARISA_HAS_STAT64
+ struct __stat64 st;
+ MARISA_THROW_IF(::_stat64(filename, &st) != 0, MARISA_IO_ERROR);
+ #else // MARISA_HAS_STAT64
+ struct _stat st;
+ MARISA_THROW_IF(::_stat(filename, &st) != 0, MARISA_IO_ERROR);
+ #endif // MARISA_HAS_STAT64
+ MARISA_THROW_IF((UInt64)st.st_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+ size_ = (std::size_t)st.st_size;
+
+ file_ = ::CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ MARISA_THROW_IF(file_ == INVALID_HANDLE_VALUE, MARISA_IO_ERROR);
+
+ map_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY, 0, 0, NULL);
+ MARISA_THROW_IF(map_ == NULL, MARISA_IO_ERROR);
+
+ origin_ = ::MapViewOfFile(map_, FILE_MAP_READ, 0, 0, 0);
+ MARISA_THROW_IF(origin_ == NULL, MARISA_IO_ERROR);
+
+ ptr_ = static_cast<const char *>(origin_);
+ avail_ = size_;
+}
+#else // (defined _WIN32) || (defined _WIN64)
+void Mapper::open_(const char *filename) {
+ struct stat st;
+ MARISA_THROW_IF(::stat(filename, &st) != 0, MARISA_IO_ERROR);
+ MARISA_THROW_IF((UInt64)st.st_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+ size_ = (std::size_t)st.st_size;
+
+ fd_ = ::open(filename, O_RDONLY);
+ MARISA_THROW_IF(fd_ == -1, MARISA_IO_ERROR);
+
+ origin_ = ::mmap(NULL, size_, PROT_READ, MAP_SHARED, fd_, 0);
+ MARISA_THROW_IF(origin_ == MAP_FAILED, MARISA_IO_ERROR);
+
+ ptr_ = static_cast<const char *>(origin_);
+ avail_ = size_;
+}
+#endif // (defined _WIN32) || (defined _WIN64)
+
+void Mapper::open_(const void *ptr, std::size_t size) {
+ ptr_ = ptr;
+ avail_ = size;
+}
+
+} // namespace io
+} // namespace grimoire
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/mapper.h b/contrib/python/marisa-trie/marisa/grimoire/io/mapper.h
new file mode 100644
index 0000000000..e06072501d
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/io/mapper.h
@@ -0,0 +1,68 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_IO_MAPPER_H_
+#define MARISA_GRIMOIRE_IO_MAPPER_H_
+
+#include <cstdio>
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace io {
+
+class Mapper {
+ public:
+ Mapper();
+ ~Mapper();
+
+ void open(const char *filename);
+ void open(const void *ptr, std::size_t size);
+
+ template <typename T>
+ void map(T *obj) {
+ MARISA_THROW_IF(obj == NULL, MARISA_NULL_ERROR);
+ *obj = *static_cast<const T *>(map_data(sizeof(T)));
+ }
+
+ template <typename T>
+ void map(const T **objs, std::size_t num_objs) {
+ MARISA_THROW_IF((objs == NULL) && (num_objs != 0), MARISA_NULL_ERROR);
+ MARISA_THROW_IF(num_objs > (MARISA_SIZE_MAX / sizeof(T)),
+ MARISA_SIZE_ERROR);
+ *objs = static_cast<const T *>(map_data(sizeof(T) * num_objs));
+ }
+
+ void seek(std::size_t size);
+
+ bool is_open() const;
+
+ void clear();
+ void swap(Mapper &rhs);
+
+ private:
+ const void *ptr_;
+ void *origin_;
+ std::size_t avail_;
+ std::size_t size_;
+#if (defined _WIN32) || (defined _WIN64)
+ void *file_;
+ void *map_;
+#else // (defined _WIN32) || (defined _WIN64)
+ int fd_;
+#endif // (defined _WIN32) || (defined _WIN64)
+
+ void open_(const char *filename);
+ void open_(const void *ptr, std::size_t size);
+
+ const void *map_data(std::size_t size);
+
+ // Disallows copy and assignment.
+ Mapper(const Mapper &);
+ Mapper &operator=(const Mapper &);
+};
+
+} // namespace io
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_IO_MAPPER_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/reader.cc b/contrib/python/marisa-trie/marisa/grimoire/io/reader.cc
new file mode 100644
index 0000000000..cb22fcbd4a
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/io/reader.cc
@@ -0,0 +1,147 @@
+#include <stdio.h>
+
+#ifdef _WIN32
+ #include <io.h>
+#else // _WIN32
+ #include <unistd.h>
+#endif // _WIN32
+
+#include <limits>
+
+#include "reader.h"
+
+namespace marisa {
+namespace grimoire {
+namespace io {
+
+Reader::Reader()
+ : file_(NULL), fd_(-1), stream_(NULL), needs_fclose_(false) {}
+
+Reader::~Reader() {
+ if (needs_fclose_) {
+ ::fclose(file_);
+ }
+}
+
+void Reader::open(const char *filename) {
+ MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
+
+ Reader temp;
+ temp.open_(filename);
+ swap(temp);
+}
+
+void Reader::open(std::FILE *file) {
+ MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
+
+ Reader temp;
+ temp.open_(file);
+ swap(temp);
+}
+
+void Reader::open(int fd) {
+ MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
+
+ Reader temp;
+ temp.open_(fd);
+ swap(temp);
+}
+
+void Reader::open(std::istream &stream) {
+ Reader temp;
+ temp.open_(stream);
+ swap(temp);
+}
+
+void Reader::clear() {
+ Reader().swap(*this);
+}
+
+void Reader::swap(Reader &rhs) {
+ marisa::swap(file_, rhs.file_);
+ marisa::swap(fd_, rhs.fd_);
+ marisa::swap(stream_, rhs.stream_);
+ marisa::swap(needs_fclose_, rhs.needs_fclose_);
+}
+
+void Reader::seek(std::size_t size) {
+ MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
+ if (size == 0) {
+ return;
+ } else if (size <= 16) {
+ char buf[16];
+ read_data(buf, size);
+ } else {
+ char buf[1024];
+ while (size != 0) {
+ const std::size_t count = (size < sizeof(buf)) ? size : sizeof(buf);
+ read_data(buf, count);
+ size -= count;
+ }
+ }
+}
+
+bool Reader::is_open() const {
+ return (file_ != NULL) || (fd_ != -1) || (stream_ != NULL);
+}
+
+void Reader::open_(const char *filename) {
+ std::FILE *file = NULL;
+#ifdef _MSC_VER
+ MARISA_THROW_IF(::fopen_s(&file, filename, "rb") != 0, MARISA_IO_ERROR);
+#else // _MSC_VER
+ file = ::fopen(filename, "rb");
+ MARISA_THROW_IF(file == NULL, MARISA_IO_ERROR);
+#endif // _MSC_VER
+ file_ = file;
+ needs_fclose_ = true;
+}
+
+void Reader::open_(std::FILE *file) {
+ file_ = file;
+}
+
+void Reader::open_(int fd) {
+ fd_ = fd;
+}
+
+void Reader::open_(std::istream &stream) {
+ stream_ = &stream;
+}
+
+void Reader::read_data(void *buf, std::size_t size) {
+ MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
+ if (size == 0) {
+ return;
+ } else if (fd_ != -1) {
+ while (size != 0) {
+#ifdef _WIN32
+ static const std::size_t CHUNK_SIZE =
+ std::numeric_limits<int>::max();
+ const unsigned int count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
+ const int size_read = ::_read(fd_, buf, count);
+#else // _WIN32
+ static const std::size_t CHUNK_SIZE =
+ std::numeric_limits< ::ssize_t>::max();
+ const ::size_t count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
+ const ::ssize_t size_read = ::read(fd_, buf, count);
+#endif // _WIN32
+ MARISA_THROW_IF(size_read <= 0, MARISA_IO_ERROR);
+ buf = static_cast<char *>(buf) + size_read;
+ size -= size_read;
+ }
+ } else if (file_ != NULL) {
+ MARISA_THROW_IF(::fread(buf, 1, size, file_) != size, MARISA_IO_ERROR);
+ } else if (stream_ != NULL) {
+ try {
+ MARISA_THROW_IF(!stream_->read(static_cast<char *>(buf), size),
+ MARISA_IO_ERROR);
+ } catch (const std::ios_base::failure &) {
+ MARISA_THROW(MARISA_IO_ERROR, "std::ios_base::failure");
+ }
+ }
+}
+
+} // namespace io
+} // namespace grimoire
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/reader.h b/contrib/python/marisa-trie/marisa/grimoire/io/reader.h
new file mode 100644
index 0000000000..fc1ba5eea7
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/io/reader.h
@@ -0,0 +1,67 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_IO_READER_H_
+#define MARISA_GRIMOIRE_IO_READER_H_
+
+#include <cstdio>
+#include <iostream>
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace io {
+
+class Reader {
+ public:
+ Reader();
+ ~Reader();
+
+ void open(const char *filename);
+ void open(std::FILE *file);
+ void open(int fd);
+ void open(std::istream &stream);
+
+ template <typename T>
+ void read(T *obj) {
+ MARISA_THROW_IF(obj == NULL, MARISA_NULL_ERROR);
+ read_data(obj, sizeof(T));
+ }
+
+ template <typename T>
+ void read(T *objs, std::size_t num_objs) {
+ MARISA_THROW_IF((objs == NULL) && (num_objs != 0), MARISA_NULL_ERROR);
+ MARISA_THROW_IF(num_objs > (MARISA_SIZE_MAX / sizeof(T)),
+ MARISA_SIZE_ERROR);
+ read_data(objs, sizeof(T) * num_objs);
+ }
+
+ void seek(std::size_t size);
+
+ bool is_open() const;
+
+ void clear();
+ void swap(Reader &rhs);
+
+ private:
+ std::FILE *file_;
+ int fd_;
+ std::istream *stream_;
+ bool needs_fclose_;
+
+ void open_(const char *filename);
+ void open_(std::FILE *file);
+ void open_(int fd);
+ void open_(std::istream &stream);
+
+ void read_data(void *buf, std::size_t size);
+
+ // Disallows copy and assignment.
+ Reader(const Reader &);
+ Reader &operator=(const Reader &);
+};
+
+} // namespace io
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_IO_READER_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/writer.cc b/contrib/python/marisa-trie/marisa/grimoire/io/writer.cc
new file mode 100644
index 0000000000..1f079d8ce6
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/io/writer.cc
@@ -0,0 +1,148 @@
+#include <stdio.h>
+
+#ifdef _WIN32
+ #include <io.h>
+#else // _WIN32
+ #include <unistd.h>
+#endif // _WIN32
+
+#include <limits>
+
+#include "writer.h"
+
+namespace marisa {
+namespace grimoire {
+namespace io {
+
+Writer::Writer()
+ : file_(NULL), fd_(-1), stream_(NULL), needs_fclose_(false) {}
+
+Writer::~Writer() {
+ if (needs_fclose_) {
+ ::fclose(file_);
+ }
+}
+
+void Writer::open(const char *filename) {
+ MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
+
+ Writer temp;
+ temp.open_(filename);
+ swap(temp);
+}
+
+void Writer::open(std::FILE *file) {
+ MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
+
+ Writer temp;
+ temp.open_(file);
+ swap(temp);
+}
+
+void Writer::open(int fd) {
+ MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
+
+ Writer temp;
+ temp.open_(fd);
+ swap(temp);
+}
+
+void Writer::open(std::ostream &stream) {
+ Writer temp;
+ temp.open_(stream);
+ swap(temp);
+}
+
+void Writer::clear() {
+ Writer().swap(*this);
+}
+
+void Writer::swap(Writer &rhs) {
+ marisa::swap(file_, rhs.file_);
+ marisa::swap(fd_, rhs.fd_);
+ marisa::swap(stream_, rhs.stream_);
+ marisa::swap(needs_fclose_, rhs.needs_fclose_);
+}
+
+void Writer::seek(std::size_t size) {
+ MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
+ if (size == 0) {
+ return;
+ } else if (size <= 16) {
+ const char buf[16] = {};
+ write_data(buf, size);
+ } else {
+ const char buf[1024] = {};
+ do {
+ const std::size_t count = (size < sizeof(buf)) ? size : sizeof(buf);
+ write_data(buf, count);
+ size -= count;
+ } while (size != 0);
+ }
+}
+
+bool Writer::is_open() const {
+ return (file_ != NULL) || (fd_ != -1) || (stream_ != NULL);
+}
+
+void Writer::open_(const char *filename) {
+ std::FILE *file = NULL;
+#ifdef _MSC_VER
+ MARISA_THROW_IF(::fopen_s(&file, filename, "wb") != 0, MARISA_IO_ERROR);
+#else // _MSC_VER
+ file = ::fopen(filename, "wb");
+ MARISA_THROW_IF(file == NULL, MARISA_IO_ERROR);
+#endif // _MSC_VER
+ file_ = file;
+ needs_fclose_ = true;
+}
+
+void Writer::open_(std::FILE *file) {
+ file_ = file;
+}
+
+void Writer::open_(int fd) {
+ fd_ = fd;
+}
+
+void Writer::open_(std::ostream &stream) {
+ stream_ = &stream;
+}
+
+void Writer::write_data(const void *data, std::size_t size) {
+ MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
+ if (size == 0) {
+ return;
+ } else if (fd_ != -1) {
+ while (size != 0) {
+#ifdef _WIN32
+ static const std::size_t CHUNK_SIZE =
+ std::numeric_limits<int>::max();
+ const unsigned int count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
+ const int size_written = ::_write(fd_, data, count);
+#else // _WIN32
+ static const std::size_t CHUNK_SIZE =
+ std::numeric_limits< ::ssize_t>::max();
+ const ::size_t count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
+ const ::ssize_t size_written = ::write(fd_, data, count);
+#endif // _WIN32
+ MARISA_THROW_IF(size_written <= 0, MARISA_IO_ERROR);
+ data = static_cast<const char *>(data) + size_written;
+ size -= size_written;
+ }
+ } else if (file_ != NULL) {
+ MARISA_THROW_IF(::fwrite(data, 1, size, file_) != size, MARISA_IO_ERROR);
+ MARISA_THROW_IF(::fflush(file_) != 0, MARISA_IO_ERROR);
+ } else if (stream_ != NULL) {
+ try {
+ MARISA_THROW_IF(!stream_->write(static_cast<const char *>(data), size),
+ MARISA_IO_ERROR);
+ } catch (const std::ios_base::failure &) {
+ MARISA_THROW(MARISA_IO_ERROR, "std::ios_base::failure");
+ }
+ }
+}
+
+} // namespace io
+} // namespace grimoire
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/io/writer.h b/contrib/python/marisa-trie/marisa/grimoire/io/writer.h
new file mode 100644
index 0000000000..1707b23de2
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/io/writer.h
@@ -0,0 +1,66 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_IO_WRITER_H_
+#define MARISA_GRIMOIRE_IO_WRITER_H_
+
+#include <cstdio>
+#include <iostream>
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace io {
+
+class Writer {
+ public:
+ Writer();
+ ~Writer();
+
+ void open(const char *filename);
+ void open(std::FILE *file);
+ void open(int fd);
+ void open(std::ostream &stream);
+
+ template <typename T>
+ void write(const T &obj) {
+ write_data(&obj, sizeof(T));
+ }
+
+ template <typename T>
+ void write(const T *objs, std::size_t num_objs) {
+ MARISA_THROW_IF((objs == NULL) && (num_objs != 0), MARISA_NULL_ERROR);
+ MARISA_THROW_IF(num_objs > (MARISA_SIZE_MAX / sizeof(T)),
+ MARISA_SIZE_ERROR);
+ write_data(objs, sizeof(T) * num_objs);
+ }
+
+ void seek(std::size_t size);
+
+ bool is_open() const;
+
+ void clear();
+ void swap(Writer &rhs);
+
+ private:
+ std::FILE *file_;
+ int fd_;
+ std::ostream *stream_;
+ bool needs_fclose_;
+
+ void open_(const char *filename);
+ void open_(std::FILE *file);
+ void open_(int fd);
+ void open_(std::ostream &stream);
+
+ void write_data(const void *data, std::size_t size);
+
+ // Disallows copy and assignment.
+ Writer(const Writer &);
+ Writer &operator=(const Writer &);
+};
+
+} // namespace io
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_IO_WRITER_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie.h b/contrib/python/marisa-trie/marisa/grimoire/trie.h
new file mode 100644
index 0000000000..d23852a4fd
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie.h
@@ -0,0 +1,17 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_H_
+#define MARISA_GRIMOIRE_TRIE_H_
+
+#include "trie/state.h"
+#include "trie/louds-trie.h"
+
+namespace marisa {
+namespace grimoire {
+
+using trie::State;
+using trie::LoudsTrie;
+
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/cache.h b/contrib/python/marisa-trie/marisa/grimoire/trie/cache.h
new file mode 100644
index 0000000000..f9da360869
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/cache.h
@@ -0,0 +1,82 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_CACHE_H_
+#define MARISA_GRIMOIRE_TRIE_CACHE_H_
+
+#include <cfloat>
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class Cache {
+ public:
+ Cache() : parent_(0), child_(0), union_() {
+ union_.weight = FLT_MIN;
+ }
+ Cache(const Cache &cache)
+ : parent_(cache.parent_), child_(cache.child_), union_(cache.union_) {}
+
+ Cache &operator=(const Cache &cache) {
+ parent_ = cache.parent_;
+ child_ = cache.child_;
+ union_ = cache.union_;
+ return *this;
+ }
+
+ void set_parent(std::size_t parent) {
+ MARISA_DEBUG_IF(parent > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ parent_ = (UInt32)parent;
+ }
+ void set_child(std::size_t child) {
+ MARISA_DEBUG_IF(child > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ child_ = (UInt32)child;
+ }
+ void set_base(UInt8 base) {
+ union_.link = (union_.link & ~0xFFU) | base;
+ }
+ void set_extra(std::size_t extra) {
+ MARISA_DEBUG_IF(extra > (MARISA_UINT32_MAX >> 8), MARISA_SIZE_ERROR);
+ union_.link = (UInt32)((union_.link & 0xFFU) | (extra << 8));
+ }
+ void set_weight(float weight) {
+ union_.weight = weight;
+ }
+
+ std::size_t parent() const {
+ return parent_;
+ }
+ std::size_t child() const {
+ return child_;
+ }
+ UInt8 base() const {
+ return (UInt8)(union_.link & 0xFFU);
+ }
+ std::size_t extra() const {
+ return union_.link >> 8;
+ }
+ char label() const {
+ return (char)base();
+ }
+ std::size_t link() const {
+ return union_.link;
+ }
+ float weight() const {
+ return union_.weight;
+ }
+
+ private:
+ UInt32 parent_;
+ UInt32 child_;
+ union Union {
+ UInt32 link;
+ float weight;
+ } union_;
+};
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_CACHE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/config.h b/contrib/python/marisa-trie/marisa/grimoire/trie/config.h
new file mode 100644
index 0000000000..9b307de3e1
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/config.h
@@ -0,0 +1,156 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_CONFIG_H_
+#define MARISA_GRIMOIRE_TRIE_CONFIG_H_
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class Config {
+ public:
+ Config()
+ : num_tries_(MARISA_DEFAULT_NUM_TRIES),
+ cache_level_(MARISA_DEFAULT_CACHE),
+ tail_mode_(MARISA_DEFAULT_TAIL),
+ node_order_(MARISA_DEFAULT_ORDER) {}
+
+ void parse(int config_flags) {
+ Config temp;
+ temp.parse_(config_flags);
+ swap(temp);
+ }
+
+ int flags() const {
+ return (int)num_tries_ | tail_mode_ | node_order_;
+ }
+
+ std::size_t num_tries() const {
+ return num_tries_;
+ }
+ CacheLevel cache_level() const {
+ return cache_level_;
+ }
+ TailMode tail_mode() const {
+ return tail_mode_;
+ }
+ NodeOrder node_order() const {
+ return node_order_;
+ }
+
+ void clear() {
+ Config().swap(*this);
+ }
+ void swap(Config &rhs) {
+ marisa::swap(num_tries_, rhs.num_tries_);
+ marisa::swap(cache_level_, rhs.cache_level_);
+ marisa::swap(tail_mode_, rhs.tail_mode_);
+ marisa::swap(node_order_, rhs.node_order_);
+ }
+
+ private:
+ std::size_t num_tries_;
+ CacheLevel cache_level_;
+ TailMode tail_mode_;
+ NodeOrder node_order_;
+
+ void parse_(int config_flags) {
+ MARISA_THROW_IF((config_flags & ~MARISA_CONFIG_MASK) != 0,
+ MARISA_CODE_ERROR);
+
+ parse_num_tries(config_flags);
+ parse_cache_level(config_flags);
+ parse_tail_mode(config_flags);
+ parse_node_order(config_flags);
+ }
+
+ void parse_num_tries(int config_flags) {
+ const int num_tries = config_flags & MARISA_NUM_TRIES_MASK;
+ if (num_tries != 0) {
+ num_tries_ = num_tries;
+ }
+ }
+
+ void parse_cache_level(int config_flags) {
+ switch (config_flags & MARISA_CACHE_LEVEL_MASK) {
+ case 0: {
+ cache_level_ = MARISA_DEFAULT_CACHE;
+ break;
+ }
+ case MARISA_HUGE_CACHE: {
+ cache_level_ = MARISA_HUGE_CACHE;
+ break;
+ }
+ case MARISA_LARGE_CACHE: {
+ cache_level_ = MARISA_LARGE_CACHE;
+ break;
+ }
+ case MARISA_NORMAL_CACHE: {
+ cache_level_ = MARISA_NORMAL_CACHE;
+ break;
+ }
+ case MARISA_SMALL_CACHE: {
+ cache_level_ = MARISA_SMALL_CACHE;
+ break;
+ }
+ case MARISA_TINY_CACHE: {
+ cache_level_ = MARISA_TINY_CACHE;
+ break;
+ }
+ default: {
+ MARISA_THROW(MARISA_CODE_ERROR, "undefined cache level");
+ }
+ }
+ }
+
+ void parse_tail_mode(int config_flags) {
+ switch (config_flags & MARISA_TAIL_MODE_MASK) {
+ case 0: {
+ tail_mode_ = MARISA_DEFAULT_TAIL;
+ break;
+ }
+ case MARISA_TEXT_TAIL: {
+ tail_mode_ = MARISA_TEXT_TAIL;
+ break;
+ }
+ case MARISA_BINARY_TAIL: {
+ tail_mode_ = MARISA_BINARY_TAIL;
+ break;
+ }
+ default: {
+ MARISA_THROW(MARISA_CODE_ERROR, "undefined tail mode");
+ }
+ }
+ }
+
+ void parse_node_order(int config_flags) {
+ switch (config_flags & MARISA_NODE_ORDER_MASK) {
+ case 0: {
+ node_order_ = MARISA_DEFAULT_ORDER;
+ break;
+ }
+ case MARISA_LABEL_ORDER: {
+ node_order_ = MARISA_LABEL_ORDER;
+ break;
+ }
+ case MARISA_WEIGHT_ORDER: {
+ node_order_ = MARISA_WEIGHT_ORDER;
+ break;
+ }
+ default: {
+ MARISA_THROW(MARISA_CODE_ERROR, "undefined node order");
+ }
+ }
+ }
+
+ // Disallows copy and assignment.
+ Config(const Config &);
+ Config &operator=(const Config &);
+};
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_CONFIG_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/entry.h b/contrib/python/marisa-trie/marisa/grimoire/trie/entry.h
new file mode 100644
index 0000000000..834ab95e1e
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/entry.h
@@ -0,0 +1,83 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_ENTRY_H_
+#define MARISA_GRIMOIRE_TRIE_ENTRY_H_
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class Entry {
+ public:
+ Entry()
+ : ptr_(reinterpret_cast<const char *>(-1)), length_(0), id_(0) {}
+ Entry(const Entry &entry)
+ : ptr_(entry.ptr_), length_(entry.length_), id_(entry.id_) {}
+
+ Entry &operator=(const Entry &entry) {
+ ptr_ = entry.ptr_;
+ length_ = entry.length_;
+ id_ = entry.id_;
+ return *this;
+ }
+
+ char operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
+ return *(ptr_ - i);
+ }
+
+ void set_str(const char *ptr, std::size_t length) {
+ MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
+ MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ ptr_ = ptr + length - 1;
+ length_ = (UInt32)length;
+ }
+ void set_id(std::size_t id) {
+ MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ id_ = (UInt32)id;
+ }
+
+ const char *ptr() const {
+ return ptr_ - length_ + 1;
+ }
+ std::size_t length() const {
+ return length_;
+ }
+ std::size_t id() const {
+ return id_;
+ }
+
+ class StringComparer {
+ public:
+ bool operator()(const Entry &lhs, const Entry &rhs) const {
+ for (std::size_t i = 0; i < lhs.length(); ++i) {
+ if (i == rhs.length()) {
+ return true;
+ }
+ if (lhs[i] != rhs[i]) {
+ return (UInt8)lhs[i] > (UInt8)rhs[i];
+ }
+ }
+ return lhs.length() > rhs.length();
+ }
+ };
+
+ class IDComparer {
+ public:
+ bool operator()(const Entry &lhs, const Entry &rhs) const {
+ return lhs.id_ < rhs.id_;
+ }
+ };
+
+ private:
+ const char *ptr_;
+ UInt32 length_;
+ UInt32 id_;
+};
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_ENTRY_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/header.h b/contrib/python/marisa-trie/marisa/grimoire/trie/header.h
new file mode 100644
index 0000000000..04839f67e1
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/header.h
@@ -0,0 +1,62 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_HEADER_H_
+#define MARISA_GRIMOIRE_TRIE_HEADER_H_
+
+#include "../io.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class Header {
+ public:
+ enum {
+ HEADER_SIZE = 16
+ };
+
+ Header() {}
+
+ void map(Mapper &mapper) {
+ const char *ptr;
+ mapper.map(&ptr, HEADER_SIZE);
+ MARISA_THROW_IF(!test_header(ptr), MARISA_FORMAT_ERROR);
+ }
+ void read(Reader &reader) {
+ char buf[HEADER_SIZE];
+ reader.read(buf, HEADER_SIZE);
+ MARISA_THROW_IF(!test_header(buf), MARISA_FORMAT_ERROR);
+ }
+ void write(Writer &writer) const {
+ writer.write(get_header(), HEADER_SIZE);
+ }
+
+ std::size_t io_size() const {
+ return HEADER_SIZE;
+ }
+
+ private:
+
+ static const char *get_header() {
+ static const char buf[HEADER_SIZE] = "We love Marisa.";
+ return buf;
+ }
+
+ static bool test_header(const char *ptr) {
+ for (std::size_t i = 0; i < HEADER_SIZE; ++i) {
+ if (ptr[i] != get_header()[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Disallows copy and assignment.
+ Header(const Header &);
+ Header &operator=(const Header &);
+};
+
+} // namespace trie
+} // namespace marisa
+} // namespace grimoire
+
+#endif // MARISA_GRIMOIRE_TRIE_HEADER_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/history.h b/contrib/python/marisa-trie/marisa/grimoire/trie/history.h
new file mode 100644
index 0000000000..9a3d272260
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/history.h
@@ -0,0 +1,66 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_STATE_HISTORY_H_
+#define MARISA_GRIMOIRE_TRIE_STATE_HISTORY_H_
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class History {
+ public:
+ History()
+ : node_id_(0), louds_pos_(0), key_pos_(0),
+ link_id_(MARISA_INVALID_LINK_ID), key_id_(MARISA_INVALID_KEY_ID) {}
+
+ void set_node_id(std::size_t node_id) {
+ MARISA_DEBUG_IF(node_id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ node_id_ = (UInt32)node_id;
+ }
+ void set_louds_pos(std::size_t louds_pos) {
+ MARISA_DEBUG_IF(louds_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ louds_pos_ = (UInt32)louds_pos;
+ }
+ void set_key_pos(std::size_t key_pos) {
+ MARISA_DEBUG_IF(key_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ key_pos_ = (UInt32)key_pos;
+ }
+ void set_link_id(std::size_t link_id) {
+ MARISA_DEBUG_IF(link_id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ link_id_ = (UInt32)link_id;
+ }
+ void set_key_id(std::size_t key_id) {
+ MARISA_DEBUG_IF(key_id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ key_id_ = (UInt32)key_id;
+ }
+
+ std::size_t node_id() const {
+ return node_id_;
+ }
+ std::size_t louds_pos() const {
+ return louds_pos_;
+ }
+ std::size_t key_pos() const {
+ return key_pos_;
+ }
+ std::size_t link_id() const {
+ return link_id_;
+ }
+ std::size_t key_id() const {
+ return key_id_;
+ }
+
+ private:
+ UInt32 node_id_;
+ UInt32 louds_pos_;
+ UInt32 key_pos_;
+ UInt32 link_id_;
+ UInt32 key_id_;
+};
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_STATE_HISTORY_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/key.h b/contrib/python/marisa-trie/marisa/grimoire/trie/key.h
new file mode 100644
index 0000000000..c09ea86cf8
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/key.h
@@ -0,0 +1,227 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_KEY_H_
+#define MARISA_GRIMOIRE_TRIE_KEY_H_
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class Key {
+ public:
+ Key() : ptr_(NULL), length_(0), union_(), id_(0) {
+ union_.terminal = 0;
+ }
+ Key(const Key &entry)
+ : ptr_(entry.ptr_), length_(entry.length_),
+ union_(entry.union_), id_(entry.id_) {}
+
+ Key &operator=(const Key &entry) {
+ ptr_ = entry.ptr_;
+ length_ = entry.length_;
+ union_ = entry.union_;
+ id_ = entry.id_;
+ return *this;
+ }
+
+ char operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
+ return ptr_[i];
+ }
+
+ void substr(std::size_t pos, std::size_t length) {
+ MARISA_DEBUG_IF(pos > length_, MARISA_BOUND_ERROR);
+ MARISA_DEBUG_IF(length > length_, MARISA_BOUND_ERROR);
+ MARISA_DEBUG_IF(pos > (length_ - length), MARISA_BOUND_ERROR);
+ ptr_ += pos;
+ length_ = (UInt32)length;
+ }
+
+ void set_str(const char *ptr, std::size_t length) {
+ MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
+ MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ ptr_ = ptr;
+ length_ = (UInt32)length;
+ }
+ void set_weight(float weight) {
+ union_.weight = weight;
+ }
+ void set_terminal(std::size_t terminal) {
+ MARISA_DEBUG_IF(terminal > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ union_.terminal = (UInt32)terminal;
+ }
+ void set_id(std::size_t id) {
+ MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ id_ = (UInt32)id;
+ }
+
+ const char *ptr() const {
+ return ptr_;
+ }
+ std::size_t length() const {
+ return length_;
+ }
+ float weight() const {
+ return union_.weight;
+ }
+ std::size_t terminal() const {
+ return union_.terminal;
+ }
+ std::size_t id() const {
+ return id_;
+ }
+
+ private:
+ const char *ptr_;
+ UInt32 length_;
+ union Union {
+ float weight;
+ UInt32 terminal;
+ } union_;
+ UInt32 id_;
+};
+
+inline bool operator==(const Key &lhs, const Key &rhs) {
+ if (lhs.length() != rhs.length()) {
+ return false;
+ }
+ for (std::size_t i = 0; i < lhs.length(); ++i) {
+ if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const Key &lhs, const Key &rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator<(const Key &lhs, const Key &rhs) {
+ for (std::size_t i = 0; i < lhs.length(); ++i) {
+ if (i == rhs.length()) {
+ return false;
+ }
+ if (lhs[i] != rhs[i]) {
+ return (UInt8)lhs[i] < (UInt8)rhs[i];
+ }
+ }
+ return lhs.length() < rhs.length();
+}
+
+inline bool operator>(const Key &lhs, const Key &rhs) {
+ return rhs < lhs;
+}
+
+class ReverseKey {
+ public:
+ ReverseKey() : ptr_(NULL), length_(0), union_(), id_(0) {
+ union_.terminal = 0;
+ }
+ ReverseKey(const ReverseKey &entry)
+ : ptr_(entry.ptr_), length_(entry.length_),
+ union_(entry.union_), id_(entry.id_) {}
+
+ ReverseKey &operator=(const ReverseKey &entry) {
+ ptr_ = entry.ptr_;
+ length_ = entry.length_;
+ union_ = entry.union_;
+ id_ = entry.id_;
+ return *this;
+ }
+
+ char operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
+ return *(ptr_ - i - 1);
+ }
+
+ void substr(std::size_t pos, std::size_t length) {
+ MARISA_DEBUG_IF(pos > length_, MARISA_BOUND_ERROR);
+ MARISA_DEBUG_IF(length > length_, MARISA_BOUND_ERROR);
+ MARISA_DEBUG_IF(pos > (length_ - length), MARISA_BOUND_ERROR);
+ ptr_ -= pos;
+ length_ = (UInt32)length;
+ }
+
+ void set_str(const char *ptr, std::size_t length) {
+ MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
+ MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ ptr_ = ptr + length;
+ length_ = (UInt32)length;
+ }
+ void set_weight(float weight) {
+ union_.weight = weight;
+ }
+ void set_terminal(std::size_t terminal) {
+ MARISA_DEBUG_IF(terminal > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ union_.terminal = (UInt32)terminal;
+ }
+ void set_id(std::size_t id) {
+ MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ id_ = (UInt32)id;
+ }
+
+ const char *ptr() const {
+ return ptr_ - length_;
+ }
+ std::size_t length() const {
+ return length_;
+ }
+ float weight() const {
+ return union_.weight;
+ }
+ std::size_t terminal() const {
+ return union_.terminal;
+ }
+ std::size_t id() const {
+ return id_;
+ }
+
+ private:
+ const char *ptr_;
+ UInt32 length_;
+ union Union {
+ float weight;
+ UInt32 terminal;
+ } union_;
+ UInt32 id_;
+};
+
+inline bool operator==(const ReverseKey &lhs, const ReverseKey &rhs) {
+ if (lhs.length() != rhs.length()) {
+ return false;
+ }
+ for (std::size_t i = 0; i < lhs.length(); ++i) {
+ if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const ReverseKey &lhs, const ReverseKey &rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator<(const ReverseKey &lhs, const ReverseKey &rhs) {
+ for (std::size_t i = 0; i < lhs.length(); ++i) {
+ if (i == rhs.length()) {
+ return false;
+ }
+ if (lhs[i] != rhs[i]) {
+ return (UInt8)lhs[i] < (UInt8)rhs[i];
+ }
+ }
+ return lhs.length() < rhs.length();
+}
+
+inline bool operator>(const ReverseKey &lhs, const ReverseKey &rhs) {
+ return rhs < lhs;
+}
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_KEY_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.cc b/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.cc
new file mode 100644
index 0000000000..ed168539bc
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.cc
@@ -0,0 +1,877 @@
+#include <algorithm>
+#include <functional>
+#include <queue>
+
+#include "../algorithm.h"
+#include "header.h"
+#include "range.h"
+#include "state.h"
+#include "louds-trie.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+LoudsTrie::LoudsTrie()
+ : louds_(), terminal_flags_(), link_flags_(), bases_(), extras_(),
+ tail_(), next_trie_(), cache_(), cache_mask_(0), num_l1_nodes_(0),
+ config_(), mapper_() {}
+
+LoudsTrie::~LoudsTrie() {}
+
+void LoudsTrie::build(Keyset &keyset, int flags) {
+ Config config;
+ config.parse(flags);
+
+ LoudsTrie temp;
+ temp.build_(keyset, config);
+ swap(temp);
+}
+
+void LoudsTrie::map(Mapper &mapper) {
+ Header().map(mapper);
+
+ LoudsTrie temp;
+ temp.map_(mapper);
+ temp.mapper_.swap(mapper);
+ swap(temp);
+}
+
+void LoudsTrie::read(Reader &reader) {
+ Header().read(reader);
+
+ LoudsTrie temp;
+ temp.read_(reader);
+ swap(temp);
+}
+
+void LoudsTrie::write(Writer &writer) const {
+ Header().write(writer);
+
+ write_(writer);
+}
+
+bool LoudsTrie::lookup(Agent &agent) const {
+ MARISA_DEBUG_IF(!agent.has_state(), MARISA_STATE_ERROR);
+
+ State &state = agent.state();
+ state.lookup_init();
+ while (state.query_pos() < agent.query().length()) {
+ if (!find_child(agent)) {
+ return false;
+ }
+ }
+ if (!terminal_flags_[state.node_id()]) {
+ return false;
+ }
+ agent.set_key(agent.query().ptr(), agent.query().length());
+ agent.set_key(terminal_flags_.rank1(state.node_id()));
+ return true;
+}
+
+void LoudsTrie::reverse_lookup(Agent &agent) const {
+ MARISA_DEBUG_IF(!agent.has_state(), MARISA_STATE_ERROR);
+ MARISA_THROW_IF(agent.query().id() >= size(), MARISA_BOUND_ERROR);
+
+ State &state = agent.state();
+ state.reverse_lookup_init();
+
+ state.set_node_id(terminal_flags_.select1(agent.query().id()));
+ if (state.node_id() == 0) {
+ agent.set_key(state.key_buf().begin(), state.key_buf().size());
+ agent.set_key(agent.query().id());
+ return;
+ }
+ for ( ; ; ) {
+ if (link_flags_[state.node_id()]) {
+ const std::size_t prev_key_pos = state.key_buf().size();
+ restore(agent, get_link(state.node_id()));
+ std::reverse(state.key_buf().begin() + prev_key_pos,
+ state.key_buf().end());
+ } else {
+ state.key_buf().push_back((char)bases_[state.node_id()]);
+ }
+
+ if (state.node_id() <= num_l1_nodes_) {
+ std::reverse(state.key_buf().begin(), state.key_buf().end());
+ agent.set_key(state.key_buf().begin(), state.key_buf().size());
+ agent.set_key(agent.query().id());
+ return;
+ }
+ state.set_node_id(louds_.select1(state.node_id()) - state.node_id() - 1);
+ }
+}
+
+bool LoudsTrie::common_prefix_search(Agent &agent) const {
+ MARISA_DEBUG_IF(!agent.has_state(), MARISA_STATE_ERROR);
+
+ State &state = agent.state();
+ if (state.status_code() == MARISA_END_OF_COMMON_PREFIX_SEARCH) {
+ return false;
+ }
+
+ if (state.status_code() != MARISA_READY_TO_COMMON_PREFIX_SEARCH) {
+ state.common_prefix_search_init();
+ if (terminal_flags_[state.node_id()]) {
+ agent.set_key(agent.query().ptr(), state.query_pos());
+ agent.set_key(terminal_flags_.rank1(state.node_id()));
+ return true;
+ }
+ }
+
+ while (state.query_pos() < agent.query().length()) {
+ if (!find_child(agent)) {
+ state.set_status_code(MARISA_END_OF_COMMON_PREFIX_SEARCH);
+ return false;
+ } else if (terminal_flags_[state.node_id()]) {
+ agent.set_key(agent.query().ptr(), state.query_pos());
+ agent.set_key(terminal_flags_.rank1(state.node_id()));
+ return true;
+ }
+ }
+ state.set_status_code(MARISA_END_OF_COMMON_PREFIX_SEARCH);
+ return false;
+}
+
+bool LoudsTrie::predictive_search(Agent &agent) const {
+ MARISA_DEBUG_IF(!agent.has_state(), MARISA_STATE_ERROR);
+
+ State &state = agent.state();
+ if (state.status_code() == MARISA_END_OF_PREDICTIVE_SEARCH) {
+ return false;
+ }
+
+ if (state.status_code() != MARISA_READY_TO_PREDICTIVE_SEARCH) {
+ state.predictive_search_init();
+ while (state.query_pos() < agent.query().length()) {
+ if (!predictive_find_child(agent)) {
+ state.set_status_code(MARISA_END_OF_PREDICTIVE_SEARCH);
+ return false;
+ }
+ }
+
+ History history;
+ history.set_node_id(state.node_id());
+ history.set_key_pos(state.key_buf().size());
+ state.history().push_back(history);
+ state.set_history_pos(1);
+
+ if (terminal_flags_[state.node_id()]) {
+ agent.set_key(state.key_buf().begin(), state.key_buf().size());
+ agent.set_key(terminal_flags_.rank1(state.node_id()));
+ return true;
+ }
+ }
+
+ for ( ; ; ) {
+ if (state.history_pos() == state.history().size()) {
+ const History &current = state.history().back();
+ History next;
+ next.set_louds_pos(louds_.select0(current.node_id()) + 1);
+ next.set_node_id(next.louds_pos() - current.node_id() - 1);
+ state.history().push_back(next);
+ }
+
+ History &next = state.history()[state.history_pos()];
+ const bool link_flag = louds_[next.louds_pos()];
+ next.set_louds_pos(next.louds_pos() + 1);
+ if (link_flag) {
+ state.set_history_pos(state.history_pos() + 1);
+ if (link_flags_[next.node_id()]) {
+ next.set_link_id(update_link_id(next.link_id(), next.node_id()));
+ restore(agent, get_link(next.node_id(), next.link_id()));
+ } else {
+ state.key_buf().push_back((char)bases_[next.node_id()]);
+ }
+ next.set_key_pos(state.key_buf().size());
+
+ if (terminal_flags_[next.node_id()]) {
+ if (next.key_id() == MARISA_INVALID_KEY_ID) {
+ next.set_key_id(terminal_flags_.rank1(next.node_id()));
+ } else {
+ next.set_key_id(next.key_id() + 1);
+ }
+ agent.set_key(state.key_buf().begin(), state.key_buf().size());
+ agent.set_key(next.key_id());
+ return true;
+ }
+ } else if (state.history_pos() != 1) {
+ History &current = state.history()[state.history_pos() - 1];
+ current.set_node_id(current.node_id() + 1);
+ const History &prev =
+ state.history()[state.history_pos() - 2];
+ state.key_buf().resize(prev.key_pos());
+ state.set_history_pos(state.history_pos() - 1);
+ } else {
+ state.set_status_code(MARISA_END_OF_PREDICTIVE_SEARCH);
+ return false;
+ }
+ }
+}
+
+std::size_t LoudsTrie::total_size() const {
+ return louds_.total_size() + terminal_flags_.total_size()
+ + link_flags_.total_size() + bases_.total_size()
+ + extras_.total_size() + tail_.total_size()
+ + ((next_trie_.get() != NULL) ? next_trie_->total_size() : 0)
+ + cache_.total_size();
+}
+
+std::size_t LoudsTrie::io_size() const {
+ return Header().io_size() + louds_.io_size()
+ + terminal_flags_.io_size() + link_flags_.io_size()
+ + bases_.io_size() + extras_.io_size() + tail_.io_size()
+ + ((next_trie_.get() != NULL) ?
+ (next_trie_->io_size() - Header().io_size()) : 0)
+ + cache_.io_size() + (sizeof(UInt32) * 2);
+}
+
+void LoudsTrie::clear() {
+ LoudsTrie().swap(*this);
+}
+
+void LoudsTrie::swap(LoudsTrie &rhs) {
+ louds_.swap(rhs.louds_);
+ terminal_flags_.swap(rhs.terminal_flags_);
+ link_flags_.swap(rhs.link_flags_);
+ bases_.swap(rhs.bases_);
+ extras_.swap(rhs.extras_);
+ tail_.swap(rhs.tail_);
+ next_trie_.swap(rhs.next_trie_);
+ cache_.swap(rhs.cache_);
+ marisa::swap(cache_mask_, rhs.cache_mask_);
+ marisa::swap(num_l1_nodes_, rhs.num_l1_nodes_);
+ config_.swap(rhs.config_);
+ mapper_.swap(rhs.mapper_);
+}
+
+void LoudsTrie::build_(Keyset &keyset, const Config &config) {
+ Vector<Key> keys;
+ keys.resize(keyset.size());
+ for (std::size_t i = 0; i < keyset.size(); ++i) {
+ keys[i].set_str(keyset[i].ptr(), keyset[i].length());
+ keys[i].set_weight(keyset[i].weight());
+ }
+
+ Vector<UInt32> terminals;
+ build_trie(keys, &terminals, config, 1);
+
+ typedef std::pair<UInt32, UInt32> TerminalIdPair;
+
+ Vector<TerminalIdPair> pairs;
+ pairs.resize(terminals.size());
+ for (std::size_t i = 0; i < pairs.size(); ++i) {
+ pairs[i].first = terminals[i];
+ pairs[i].second = (UInt32)i;
+ }
+ terminals.clear();
+ std::sort(pairs.begin(), pairs.end());
+
+ std::size_t node_id = 0;
+ for (std::size_t i = 0; i < pairs.size(); ++i) {
+ while (node_id < pairs[i].first) {
+ terminal_flags_.push_back(false);
+ ++node_id;
+ }
+ if (node_id == pairs[i].first) {
+ terminal_flags_.push_back(true);
+ ++node_id;
+ }
+ }
+ while (node_id < bases_.size()) {
+ terminal_flags_.push_back(false);
+ ++node_id;
+ }
+ terminal_flags_.push_back(false);
+ terminal_flags_.build(false, true);
+
+ for (std::size_t i = 0; i < keyset.size(); ++i) {
+ keyset[pairs[i].second].set_id(terminal_flags_.rank1(pairs[i].first));
+ }
+}
+
+template <typename T>
+void LoudsTrie::build_trie(Vector<T> &keys,
+ Vector<UInt32> *terminals, const Config &config, std::size_t trie_id) {
+ build_current_trie(keys, terminals, config, trie_id);
+
+ Vector<UInt32> next_terminals;
+ if (!keys.empty()) {
+ build_next_trie(keys, &next_terminals, config, trie_id);
+ }
+
+ if (next_trie_.get() != NULL) {
+ config_.parse(static_cast<int>((next_trie_->num_tries() + 1)) |
+ next_trie_->tail_mode() | next_trie_->node_order());
+ } else {
+ config_.parse(1 | tail_.mode() | config.node_order() |
+ config.cache_level());
+ }
+
+ link_flags_.build(false, false);
+ std::size_t node_id = 0;
+ for (std::size_t i = 0; i < next_terminals.size(); ++i) {
+ while (!link_flags_[node_id]) {
+ ++node_id;
+ }
+ bases_[node_id] = (UInt8)(next_terminals[i] % 256);
+ next_terminals[i] /= 256;
+ ++node_id;
+ }
+ extras_.build(next_terminals);
+ fill_cache();
+}
+
+template <typename T>
+void LoudsTrie::build_current_trie(Vector<T> &keys,
+ Vector<UInt32> *terminals, const Config &config,
+ std::size_t trie_id) try {
+ for (std::size_t i = 0; i < keys.size(); ++i) {
+ keys[i].set_id(i);
+ }
+ const std::size_t num_keys = Algorithm().sort(keys.begin(), keys.end());
+ reserve_cache(config, trie_id, num_keys);
+
+ louds_.push_back(true);
+ louds_.push_back(false);
+ bases_.push_back('\0');
+ link_flags_.push_back(false);
+
+ Vector<T> next_keys;
+ std::queue<Range> queue;
+ Vector<WeightedRange> w_ranges;
+
+ queue.push(make_range(0, keys.size(), 0));
+ while (!queue.empty()) {
+ const std::size_t node_id = link_flags_.size() - queue.size();
+
+ Range range = queue.front();
+ queue.pop();
+
+ while ((range.begin() < range.end()) &&
+ (keys[range.begin()].length() == range.key_pos())) {
+ keys[range.begin()].set_terminal(node_id);
+ range.set_begin(range.begin() + 1);
+ }
+
+ if (range.begin() == range.end()) {
+ louds_.push_back(false);
+ continue;
+ }
+
+ w_ranges.clear();
+ double weight = keys[range.begin()].weight();
+ for (std::size_t i = range.begin() + 1; i < range.end(); ++i) {
+ if (keys[i - 1][range.key_pos()] != keys[i][range.key_pos()]) {
+ w_ranges.push_back(make_weighted_range(
+ range.begin(), i, range.key_pos(), (float)weight));
+ range.set_begin(i);
+ weight = 0.0;
+ }
+ weight += keys[i].weight();
+ }
+ w_ranges.push_back(make_weighted_range(
+ range.begin(), range.end(), range.key_pos(), (float)weight));
+ if (config.node_order() == MARISA_WEIGHT_ORDER) {
+ std::stable_sort(w_ranges.begin(), w_ranges.end(),
+ std::greater<WeightedRange>());
+ }
+
+ if (node_id == 0) {
+ num_l1_nodes_ = w_ranges.size();
+ }
+
+ for (std::size_t i = 0; i < w_ranges.size(); ++i) {
+ WeightedRange &w_range = w_ranges[i];
+ std::size_t key_pos = w_range.key_pos() + 1;
+ while (key_pos < keys[w_range.begin()].length()) {
+ std::size_t j;
+ for (j = w_range.begin() + 1; j < w_range.end(); ++j) {
+ if (keys[j - 1][key_pos] != keys[j][key_pos]) {
+ break;
+ }
+ }
+ if (j < w_range.end()) {
+ break;
+ }
+ ++key_pos;
+ }
+ cache<T>(node_id, bases_.size(), w_range.weight(),
+ keys[w_range.begin()][w_range.key_pos()]);
+
+ if (key_pos == w_range.key_pos() + 1) {
+ bases_.push_back(keys[w_range.begin()][w_range.key_pos()]);
+ link_flags_.push_back(false);
+ } else {
+ bases_.push_back('\0');
+ link_flags_.push_back(true);
+ T next_key;
+ next_key.set_str(keys[w_range.begin()].ptr(),
+ keys[w_range.begin()].length());
+ next_key.substr(w_range.key_pos(), key_pos - w_range.key_pos());
+ next_key.set_weight(w_range.weight());
+ next_keys.push_back(next_key);
+ }
+ w_range.set_key_pos(key_pos);
+ queue.push(w_range.range());
+ louds_.push_back(true);
+ }
+ louds_.push_back(false);
+ }
+
+ louds_.push_back(false);
+ louds_.build(trie_id == 1, true);
+ bases_.shrink();
+
+ build_terminals(keys, terminals);
+ keys.swap(next_keys);
+} catch (const std::bad_alloc &) {
+ MARISA_THROW(MARISA_MEMORY_ERROR, "std::bad_alloc");
+}
+
+template <>
+void LoudsTrie::build_next_trie(Vector<Key> &keys,
+ Vector<UInt32> *terminals, const Config &config, std::size_t trie_id) {
+ if (trie_id == config.num_tries()) {
+ Vector<Entry> entries;
+ entries.resize(keys.size());
+ for (std::size_t i = 0; i < keys.size(); ++i) {
+ entries[i].set_str(keys[i].ptr(), keys[i].length());
+ }
+ tail_.build(entries, terminals, config.tail_mode());
+ return;
+ }
+ Vector<ReverseKey> reverse_keys;
+ reverse_keys.resize(keys.size());
+ for (std::size_t i = 0; i < keys.size(); ++i) {
+ reverse_keys[i].set_str(keys[i].ptr(), keys[i].length());
+ reverse_keys[i].set_weight(keys[i].weight());
+ }
+ keys.clear();
+ next_trie_.reset(new (std::nothrow) LoudsTrie);
+ MARISA_THROW_IF(next_trie_.get() == NULL, MARISA_MEMORY_ERROR);
+ next_trie_->build_trie(reverse_keys, terminals, config, trie_id + 1);
+}
+
+template <>
+void LoudsTrie::build_next_trie(Vector<ReverseKey> &keys,
+ Vector<UInt32> *terminals, const Config &config, std::size_t trie_id) {
+ if (trie_id == config.num_tries()) {
+ Vector<Entry> entries;
+ entries.resize(keys.size());
+ for (std::size_t i = 0; i < keys.size(); ++i) {
+ entries[i].set_str(keys[i].ptr(), keys[i].length());
+ }
+ tail_.build(entries, terminals, config.tail_mode());
+ return;
+ }
+ next_trie_.reset(new (std::nothrow) LoudsTrie);
+ MARISA_THROW_IF(next_trie_.get() == NULL, MARISA_MEMORY_ERROR);
+ next_trie_->build_trie(keys, terminals, config, trie_id + 1);
+}
+
+template <typename T>
+void LoudsTrie::build_terminals(const Vector<T> &keys,
+ Vector<UInt32> *terminals) const {
+ Vector<UInt32> temp;
+ temp.resize(keys.size());
+ for (std::size_t i = 0; i < keys.size(); ++i) {
+ temp[keys[i].id()] = (UInt32)keys[i].terminal();
+ }
+ terminals->swap(temp);
+}
+
+template <>
+void LoudsTrie::cache<Key>(std::size_t parent, std::size_t child,
+ float weight, char label) {
+ MARISA_DEBUG_IF(parent >= child, MARISA_RANGE_ERROR);
+
+ const std::size_t cache_id = get_cache_id(parent, label);
+ if (weight > cache_[cache_id].weight()) {
+ cache_[cache_id].set_parent(parent);
+ cache_[cache_id].set_child(child);
+ cache_[cache_id].set_weight(weight);
+ }
+}
+
+void LoudsTrie::reserve_cache(const Config &config, std::size_t trie_id,
+ std::size_t num_keys) {
+ std::size_t cache_size = (trie_id == 1) ? 256 : 1;
+ while (cache_size < (num_keys / config.cache_level())) {
+ cache_size *= 2;
+ }
+ cache_.resize(cache_size);
+ cache_mask_ = cache_size - 1;
+}
+
+template <>
+void LoudsTrie::cache<ReverseKey>(std::size_t parent, std::size_t child,
+ float weight, char) {
+ MARISA_DEBUG_IF(parent >= child, MARISA_RANGE_ERROR);
+
+ const std::size_t cache_id = get_cache_id(child);
+ if (weight > cache_[cache_id].weight()) {
+ cache_[cache_id].set_parent(parent);
+ cache_[cache_id].set_child(child);
+ cache_[cache_id].set_weight(weight);
+ }
+}
+
+void LoudsTrie::fill_cache() {
+ for (std::size_t i = 0; i < cache_.size(); ++i) {
+ const std::size_t node_id = cache_[i].child();
+ if (node_id != 0) {
+ cache_[i].set_base(bases_[node_id]);
+ cache_[i].set_extra(!link_flags_[node_id] ?
+ MARISA_INVALID_EXTRA : extras_[link_flags_.rank1(node_id)]);
+ } else {
+ cache_[i].set_parent(MARISA_UINT32_MAX);
+ cache_[i].set_child(MARISA_UINT32_MAX);
+ }
+ }
+}
+
+void LoudsTrie::map_(Mapper &mapper) {
+ louds_.map(mapper);
+ terminal_flags_.map(mapper);
+ link_flags_.map(mapper);
+ bases_.map(mapper);
+ extras_.map(mapper);
+ tail_.map(mapper);
+ if ((link_flags_.num_1s() != 0) && tail_.empty()) {
+ next_trie_.reset(new (std::nothrow) LoudsTrie);
+ MARISA_THROW_IF(next_trie_.get() == NULL, MARISA_MEMORY_ERROR);
+ next_trie_->map_(mapper);
+ }
+ cache_.map(mapper);
+ cache_mask_ = cache_.size() - 1;
+ {
+ UInt32 temp_num_l1_nodes;
+ mapper.map(&temp_num_l1_nodes);
+ num_l1_nodes_ = temp_num_l1_nodes;
+ }
+ {
+ UInt32 temp_config_flags;
+ mapper.map(&temp_config_flags);
+ config_.parse((int)temp_config_flags);
+ }
+}
+
+void LoudsTrie::read_(Reader &reader) {
+ louds_.read(reader);
+ terminal_flags_.read(reader);
+ link_flags_.read(reader);
+ bases_.read(reader);
+ extras_.read(reader);
+ tail_.read(reader);
+ if ((link_flags_.num_1s() != 0) && tail_.empty()) {
+ next_trie_.reset(new (std::nothrow) LoudsTrie);
+ MARISA_THROW_IF(next_trie_.get() == NULL, MARISA_MEMORY_ERROR);
+ next_trie_->read_(reader);
+ }
+ cache_.read(reader);
+ cache_mask_ = cache_.size() - 1;
+ {
+ UInt32 temp_num_l1_nodes;
+ reader.read(&temp_num_l1_nodes);
+ num_l1_nodes_ = temp_num_l1_nodes;
+ }
+ {
+ UInt32 temp_config_flags;
+ reader.read(&temp_config_flags);
+ config_.parse((int)temp_config_flags);
+ }
+}
+
+void LoudsTrie::write_(Writer &writer) const {
+ louds_.write(writer);
+ terminal_flags_.write(writer);
+ link_flags_.write(writer);
+ bases_.write(writer);
+ extras_.write(writer);
+ tail_.write(writer);
+ if (next_trie_.get() != NULL) {
+ next_trie_->write_(writer);
+ }
+ cache_.write(writer);
+ writer.write((UInt32)num_l1_nodes_);
+ writer.write((UInt32)config_.flags());
+}
+
+bool LoudsTrie::find_child(Agent &agent) const {
+ MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
+ MARISA_BOUND_ERROR);
+
+ State &state = agent.state();
+ const std::size_t cache_id = get_cache_id(state.node_id(),
+ agent.query()[state.query_pos()]);
+ if (state.node_id() == cache_[cache_id].parent()) {
+ if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
+ if (!match(agent, cache_[cache_id].link())) {
+ return false;
+ }
+ } else {
+ state.set_query_pos(state.query_pos() + 1);
+ }
+ state.set_node_id(cache_[cache_id].child());
+ return true;
+ }
+
+ std::size_t louds_pos = louds_.select0(state.node_id()) + 1;
+ if (!louds_[louds_pos]) {
+ return false;
+ }
+ state.set_node_id(louds_pos - state.node_id() - 1);
+ std::size_t link_id = MARISA_INVALID_LINK_ID;
+ do {
+ if (link_flags_[state.node_id()]) {
+ link_id = update_link_id(link_id, state.node_id());
+ const std::size_t prev_query_pos = state.query_pos();
+ if (match(agent, get_link(state.node_id(), link_id))) {
+ return true;
+ } else if (state.query_pos() != prev_query_pos) {
+ return false;
+ }
+ } else if (bases_[state.node_id()] ==
+ (UInt8)agent.query()[state.query_pos()]) {
+ state.set_query_pos(state.query_pos() + 1);
+ return true;
+ }
+ state.set_node_id(state.node_id() + 1);
+ ++louds_pos;
+ } while (louds_[louds_pos]);
+ return false;
+}
+
+bool LoudsTrie::predictive_find_child(Agent &agent) const {
+ MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
+ MARISA_BOUND_ERROR);
+
+ State &state = agent.state();
+ const std::size_t cache_id = get_cache_id(state.node_id(),
+ agent.query()[state.query_pos()]);
+ if (state.node_id() == cache_[cache_id].parent()) {
+ if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
+ if (!prefix_match(agent, cache_[cache_id].link())) {
+ return false;
+ }
+ } else {
+ state.key_buf().push_back(cache_[cache_id].label());
+ state.set_query_pos(state.query_pos() + 1);
+ }
+ state.set_node_id(cache_[cache_id].child());
+ return true;
+ }
+
+ std::size_t louds_pos = louds_.select0(state.node_id()) + 1;
+ if (!louds_[louds_pos]) {
+ return false;
+ }
+ state.set_node_id(louds_pos - state.node_id() - 1);
+ std::size_t link_id = MARISA_INVALID_LINK_ID;
+ do {
+ if (link_flags_[state.node_id()]) {
+ link_id = update_link_id(link_id, state.node_id());
+ const std::size_t prev_query_pos = state.query_pos();
+ if (prefix_match(agent, get_link(state.node_id(), link_id))) {
+ return true;
+ } else if (state.query_pos() != prev_query_pos) {
+ return false;
+ }
+ } else if (bases_[state.node_id()] ==
+ (UInt8)agent.query()[state.query_pos()]) {
+ state.key_buf().push_back((char)bases_[state.node_id()]);
+ state.set_query_pos(state.query_pos() + 1);
+ return true;
+ }
+ state.set_node_id(state.node_id() + 1);
+ ++louds_pos;
+ } while (louds_[louds_pos]);
+ return false;
+}
+
+void LoudsTrie::restore(Agent &agent, std::size_t link) const {
+ if (next_trie_.get() != NULL) {
+ next_trie_->restore_(agent, link);
+ } else {
+ tail_.restore(agent, link);
+ }
+}
+
+bool LoudsTrie::match(Agent &agent, std::size_t link) const {
+ if (next_trie_.get() != NULL) {
+ return next_trie_->match_(agent, link);
+ } else {
+ return tail_.match(agent, link);
+ }
+}
+
+bool LoudsTrie::prefix_match(Agent &agent, std::size_t link) const {
+ if (next_trie_.get() != NULL) {
+ return next_trie_->prefix_match_(agent, link);
+ } else {
+ return tail_.prefix_match(agent, link);
+ }
+}
+
+void LoudsTrie::restore_(Agent &agent, std::size_t node_id) const {
+ MARISA_DEBUG_IF(node_id == 0, MARISA_RANGE_ERROR);
+
+ State &state = agent.state();
+ for ( ; ; ) {
+ const std::size_t cache_id = get_cache_id(node_id);
+ if (node_id == cache_[cache_id].child()) {
+ if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
+ restore(agent, cache_[cache_id].link());
+ } else {
+ state.key_buf().push_back(cache_[cache_id].label());
+ }
+
+ node_id = cache_[cache_id].parent();
+ if (node_id == 0) {
+ return;
+ }
+ continue;
+ }
+
+ if (link_flags_[node_id]) {
+ restore(agent, get_link(node_id));
+ } else {
+ state.key_buf().push_back((char)bases_[node_id]);
+ }
+
+ if (node_id <= num_l1_nodes_) {
+ return;
+ }
+ node_id = louds_.select1(node_id) - node_id - 1;
+ }
+}
+
+bool LoudsTrie::match_(Agent &agent, std::size_t node_id) const {
+ MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
+ MARISA_BOUND_ERROR);
+ MARISA_DEBUG_IF(node_id == 0, MARISA_RANGE_ERROR);
+
+ State &state = agent.state();
+ for ( ; ; ) {
+ const std::size_t cache_id = get_cache_id(node_id);
+ if (node_id == cache_[cache_id].child()) {
+ if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
+ if (!match(agent, cache_[cache_id].link())) {
+ return false;
+ }
+ } else if (cache_[cache_id].label() ==
+ agent.query()[state.query_pos()]) {
+ state.set_query_pos(state.query_pos() + 1);
+ } else {
+ return false;
+ }
+
+ node_id = cache_[cache_id].parent();
+ if (node_id == 0) {
+ return true;
+ } else if (state.query_pos() >= agent.query().length()) {
+ return false;
+ }
+ continue;
+ }
+
+ if (link_flags_[node_id]) {
+ if (next_trie_.get() != NULL) {
+ if (!match(agent, get_link(node_id))) {
+ return false;
+ }
+ } else if (!tail_.match(agent, get_link(node_id))) {
+ return false;
+ }
+ } else if (bases_[node_id] == (UInt8)agent.query()[state.query_pos()]) {
+ state.set_query_pos(state.query_pos() + 1);
+ } else {
+ return false;
+ }
+
+ if (node_id <= num_l1_nodes_) {
+ return true;
+ } else if (state.query_pos() >= agent.query().length()) {
+ return false;
+ }
+ node_id = louds_.select1(node_id) - node_id - 1;
+ }
+}
+
+bool LoudsTrie::prefix_match_(Agent &agent, std::size_t node_id) const {
+ MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
+ MARISA_BOUND_ERROR);
+ MARISA_DEBUG_IF(node_id == 0, MARISA_RANGE_ERROR);
+
+ State &state = agent.state();
+ for ( ; ; ) {
+ const std::size_t cache_id = get_cache_id(node_id);
+ if (node_id == cache_[cache_id].child()) {
+ if (cache_[cache_id].extra() != MARISA_INVALID_EXTRA) {
+ if (!prefix_match(agent, cache_[cache_id].link())) {
+ return false;
+ }
+ } else if (cache_[cache_id].label() ==
+ agent.query()[state.query_pos()]) {
+ state.key_buf().push_back(cache_[cache_id].label());
+ state.set_query_pos(state.query_pos() + 1);
+ } else {
+ return false;
+ }
+
+ node_id = cache_[cache_id].parent();
+ if (node_id == 0) {
+ return true;
+ }
+ } else {
+ if (link_flags_[node_id]) {
+ if (!prefix_match(agent, get_link(node_id))) {
+ return false;
+ }
+ } else if (bases_[node_id] == (UInt8)agent.query()[state.query_pos()]) {
+ state.key_buf().push_back((char)bases_[node_id]);
+ state.set_query_pos(state.query_pos() + 1);
+ } else {
+ return false;
+ }
+
+ if (node_id <= num_l1_nodes_) {
+ return true;
+ }
+ node_id = louds_.select1(node_id) - node_id - 1;
+ }
+
+ if (state.query_pos() >= agent.query().length()) {
+ restore_(agent, node_id);
+ return true;
+ }
+ }
+}
+
+std::size_t LoudsTrie::get_cache_id(std::size_t node_id, char label) const {
+ return (node_id ^ (node_id << 5) ^ (UInt8)label) & cache_mask_;
+}
+
+std::size_t LoudsTrie::get_cache_id(std::size_t node_id) const {
+ return node_id & cache_mask_;
+}
+
+std::size_t LoudsTrie::get_link(std::size_t node_id) const {
+ return bases_[node_id] | (extras_[link_flags_.rank1(node_id)] * 256);
+}
+
+std::size_t LoudsTrie::get_link(std::size_t node_id,
+ std::size_t link_id) const {
+ return bases_[node_id] | (extras_[link_id] * 256);
+}
+
+std::size_t LoudsTrie::update_link_id(std::size_t link_id,
+ std::size_t node_id) const {
+ return (link_id == MARISA_INVALID_LINK_ID) ?
+ link_flags_.rank1(node_id) : (link_id + 1);
+}
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.h b/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.h
new file mode 100644
index 0000000000..5a757ac8fc
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/louds-trie.h
@@ -0,0 +1,135 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_LOUDS_TRIE_H_
+#define MARISA_GRIMOIRE_TRIE_LOUDS_TRIE_H_
+
+#include "../../keyset.h"
+#include "../../agent.h"
+#include "../vector.h"
+#include "config.h"
+#include "key.h"
+#include "tail.h"
+#include "cache.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class LoudsTrie {
+ public:
+ LoudsTrie();
+ ~LoudsTrie();
+
+ void build(Keyset &keyset, int flags);
+
+ void map(Mapper &mapper);
+ void read(Reader &reader);
+ void write(Writer &writer) const;
+
+ bool lookup(Agent &agent) const;
+ void reverse_lookup(Agent &agent) const;
+ bool common_prefix_search(Agent &agent) const;
+ bool predictive_search(Agent &agent) const;
+
+ std::size_t num_tries() const {
+ return config_.num_tries();
+ }
+ std::size_t num_keys() const {
+ return size();
+ }
+ std::size_t num_nodes() const {
+ return (louds_.size() / 2) - 1;
+ }
+
+ CacheLevel cache_level() const {
+ return config_.cache_level();
+ }
+ TailMode tail_mode() const {
+ return config_.tail_mode();
+ }
+ NodeOrder node_order() const {
+ return config_.node_order();
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+ std::size_t size() const {
+ return terminal_flags_.num_1s();
+ }
+ std::size_t total_size() const;
+ std::size_t io_size() const;
+
+ void clear();
+ void swap(LoudsTrie &rhs);
+
+ private:
+ BitVector louds_;
+ BitVector terminal_flags_;
+ BitVector link_flags_;
+ Vector<UInt8> bases_;
+ FlatVector extras_;
+ Tail tail_;
+ scoped_ptr<LoudsTrie> next_trie_;
+ Vector<Cache> cache_;
+ std::size_t cache_mask_;
+ std::size_t num_l1_nodes_;
+ Config config_;
+ Mapper mapper_;
+
+ void build_(Keyset &keyset, const Config &config);
+
+ template <typename T>
+ void build_trie(Vector<T> &keys,
+ Vector<UInt32> *terminals, const Config &config, std::size_t trie_id);
+ template <typename T>
+ void build_current_trie(Vector<T> &keys,
+ Vector<UInt32> *terminals, const Config &config, std::size_t trie_id);
+ template <typename T>
+ void build_next_trie(Vector<T> &keys,
+ Vector<UInt32> *terminals, const Config &config, std::size_t trie_id);
+ template <typename T>
+ void build_terminals(const Vector<T> &keys,
+ Vector<UInt32> *terminals) const;
+
+ void reserve_cache(const Config &config, std::size_t trie_id,
+ std::size_t num_keys);
+ template <typename T>
+ void cache(std::size_t parent, std::size_t child,
+ float weight, char label);
+ void fill_cache();
+
+ void map_(Mapper &mapper);
+ void read_(Reader &reader);
+ void write_(Writer &writer) const;
+
+ inline bool find_child(Agent &agent) const;
+ inline bool predictive_find_child(Agent &agent) const;
+
+ inline void restore(Agent &agent, std::size_t node_id) const;
+ inline bool match(Agent &agent, std::size_t node_id) const;
+ inline bool prefix_match(Agent &agent, std::size_t node_id) const;
+
+ void restore_(Agent &agent, std::size_t node_id) const;
+ bool match_(Agent &agent, std::size_t node_id) const;
+ bool prefix_match_(Agent &agent, std::size_t node_id) const;
+
+ inline std::size_t get_cache_id(std::size_t node_id, char label) const;
+ inline std::size_t get_cache_id(std::size_t node_id) const;
+
+ inline std::size_t get_link(std::size_t node_id) const;
+ inline std::size_t get_link(std::size_t node_id,
+ std::size_t link_id) const;
+
+ inline std::size_t update_link_id(std::size_t link_id,
+ std::size_t node_id) const;
+
+ // Disallows copy and assignment.
+ LoudsTrie(const LoudsTrie &);
+ LoudsTrie &operator=(const LoudsTrie &);
+};
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_LOUDS_TRIE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/range.h b/contrib/python/marisa-trie/marisa/grimoire/trie/range.h
new file mode 100644
index 0000000000..4ca39a9c37
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/range.h
@@ -0,0 +1,116 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_RANGE_H_
+#define MARISA_GRIMOIRE_TRIE_RANGE_H_
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class Range {
+ public:
+ Range() : begin_(0), end_(0), key_pos_(0) {}
+
+ void set_begin(std::size_t begin) {
+ MARISA_DEBUG_IF(begin > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ begin_ = static_cast<UInt32>(begin);
+ }
+ void set_end(std::size_t end) {
+ MARISA_DEBUG_IF(end > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ end_ = static_cast<UInt32>(end);
+ }
+ void set_key_pos(std::size_t key_pos) {
+ MARISA_DEBUG_IF(key_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ key_pos_ = static_cast<UInt32>(key_pos);
+ }
+
+ std::size_t begin() const {
+ return begin_;
+ }
+ std::size_t end() const {
+ return end_;
+ }
+ std::size_t key_pos() const {
+ return key_pos_;
+ }
+
+ private:
+ UInt32 begin_;
+ UInt32 end_;
+ UInt32 key_pos_;
+};
+
+inline Range make_range(std::size_t begin, std::size_t end,
+ std::size_t key_pos) {
+ Range range;
+ range.set_begin(begin);
+ range.set_end(end);
+ range.set_key_pos(key_pos);
+ return range;
+}
+
+class WeightedRange {
+ public:
+ WeightedRange() : range_(), weight_(0.0F) {}
+
+ void set_range(const Range &range) {
+ range_ = range;
+ }
+ void set_begin(std::size_t begin) {
+ range_.set_begin(begin);
+ }
+ void set_end(std::size_t end) {
+ range_.set_end(end);
+ }
+ void set_key_pos(std::size_t key_pos) {
+ range_.set_key_pos(key_pos);
+ }
+ void set_weight(float weight) {
+ weight_ = weight;
+ }
+
+ const Range &range() const {
+ return range_;
+ }
+ std::size_t begin() const {
+ return range_.begin();
+ }
+ std::size_t end() const {
+ return range_.end();
+ }
+ std::size_t key_pos() const {
+ return range_.key_pos();
+ }
+ float weight() const {
+ return weight_;
+ }
+
+ private:
+ Range range_;
+ float weight_;
+};
+
+inline bool operator<(const WeightedRange &lhs, const WeightedRange &rhs) {
+ return lhs.weight() < rhs.weight();
+}
+
+inline bool operator>(const WeightedRange &lhs, const WeightedRange &rhs) {
+ return lhs.weight() > rhs.weight();
+}
+
+inline WeightedRange make_weighted_range(std::size_t begin, std::size_t end,
+ std::size_t key_pos, float weight) {
+ WeightedRange range;
+ range.set_begin(begin);
+ range.set_end(end);
+ range.set_key_pos(key_pos);
+ range.set_weight(weight);
+ return range;
+}
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_RANGE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/state.h b/contrib/python/marisa-trie/marisa/grimoire/trie/state.h
new file mode 100644
index 0000000000..219bf9e03a
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/state.h
@@ -0,0 +1,118 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_STATE_H_
+#define MARISA_GRIMOIRE_TRIE_STATE_H_
+
+#include "../vector.h"
+#include "history.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+// A search agent has its internal state and the status codes are defined
+// below.
+typedef enum StatusCode {
+ MARISA_READY_TO_ALL,
+ MARISA_READY_TO_COMMON_PREFIX_SEARCH,
+ MARISA_READY_TO_PREDICTIVE_SEARCH,
+ MARISA_END_OF_COMMON_PREFIX_SEARCH,
+ MARISA_END_OF_PREDICTIVE_SEARCH,
+} StatusCode;
+
+class State {
+ public:
+ State()
+ : key_buf_(), history_(), node_id_(0), query_pos_(0),
+ history_pos_(0), status_code_(MARISA_READY_TO_ALL) {}
+
+ void set_node_id(std::size_t node_id) {
+ MARISA_DEBUG_IF(node_id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ node_id_ = (UInt32)node_id;
+ }
+ void set_query_pos(std::size_t query_pos) {
+ MARISA_DEBUG_IF(query_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ query_pos_ = (UInt32)query_pos;
+ }
+ void set_history_pos(std::size_t history_pos) {
+ MARISA_DEBUG_IF(history_pos > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ history_pos_ = (UInt32)history_pos;
+ }
+ void set_status_code(StatusCode status_code) {
+ status_code_ = status_code;
+ }
+
+ std::size_t node_id() const {
+ return node_id_;
+ }
+ std::size_t query_pos() const {
+ return query_pos_;
+ }
+ std::size_t history_pos() const {
+ return history_pos_;
+ }
+ StatusCode status_code() const {
+ return status_code_;
+ }
+
+ const Vector<char> &key_buf() const {
+ return key_buf_;
+ }
+ const Vector<History> &history() const {
+ return history_;
+ }
+
+ Vector<char> &key_buf() {
+ return key_buf_;
+ }
+ Vector<History> &history() {
+ return history_;
+ }
+
+ void reset() {
+ status_code_ = MARISA_READY_TO_ALL;
+ }
+
+ void lookup_init() {
+ node_id_ = 0;
+ query_pos_ = 0;
+ status_code_ = MARISA_READY_TO_ALL;
+ }
+ void reverse_lookup_init() {
+ key_buf_.resize(0);
+ key_buf_.reserve(32);
+ status_code_ = MARISA_READY_TO_ALL;
+ }
+ void common_prefix_search_init() {
+ node_id_ = 0;
+ query_pos_ = 0;
+ status_code_ = MARISA_READY_TO_COMMON_PREFIX_SEARCH;
+ }
+ void predictive_search_init() {
+ key_buf_.resize(0);
+ key_buf_.reserve(64);
+ history_.resize(0);
+ history_.reserve(4);
+ node_id_ = 0;
+ query_pos_ = 0;
+ history_pos_ = 0;
+ status_code_ = MARISA_READY_TO_PREDICTIVE_SEARCH;
+ }
+
+ private:
+ Vector<char> key_buf_;
+ Vector<History> history_;
+ UInt32 node_id_;
+ UInt32 query_pos_;
+ UInt32 history_pos_;
+ StatusCode status_code_;
+
+ // Disallows copy and assignment.
+ State(const State &);
+ State &operator=(const State &);
+};
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_STATE_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/tail.cc b/contrib/python/marisa-trie/marisa/grimoire/trie/tail.cc
new file mode 100644
index 0000000000..6ec3652e1c
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/tail.cc
@@ -0,0 +1,218 @@
+#include "../algorithm.h"
+#include "state.h"
+#include "tail.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+Tail::Tail() : buf_(), end_flags_() {}
+
+void Tail::build(Vector<Entry> &entries, Vector<UInt32> *offsets,
+ TailMode mode) {
+ MARISA_THROW_IF(offsets == NULL, MARISA_NULL_ERROR);
+
+ switch (mode) {
+ case MARISA_TEXT_TAIL: {
+ for (std::size_t i = 0; i < entries.size(); ++i) {
+ const char * const ptr = entries[i].ptr();
+ const std::size_t length = entries[i].length();
+ for (std::size_t j = 0; j < length; ++j) {
+ if (ptr[j] == '\0') {
+ mode = MARISA_BINARY_TAIL;
+ break;
+ }
+ }
+ if (mode == MARISA_BINARY_TAIL) {
+ break;
+ }
+ }
+ break;
+ }
+ case MARISA_BINARY_TAIL: {
+ break;
+ }
+ default: {
+ MARISA_THROW(MARISA_CODE_ERROR, "undefined tail mode");
+ }
+ }
+
+ Tail temp;
+ temp.build_(entries, offsets, mode);
+ swap(temp);
+}
+
+void Tail::map(Mapper &mapper) {
+ Tail temp;
+ temp.map_(mapper);
+ swap(temp);
+}
+
+void Tail::read(Reader &reader) {
+ Tail temp;
+ temp.read_(reader);
+ swap(temp);
+}
+
+void Tail::write(Writer &writer) const {
+ write_(writer);
+}
+
+void Tail::restore(Agent &agent, std::size_t offset) const {
+ MARISA_DEBUG_IF(buf_.empty(), MARISA_STATE_ERROR);
+
+ State &state = agent.state();
+ if (end_flags_.empty()) {
+ for (const char *ptr = &buf_[offset]; *ptr != '\0'; ++ptr) {
+ state.key_buf().push_back(*ptr);
+ }
+ } else {
+ do {
+ state.key_buf().push_back(buf_[offset]);
+ } while (!end_flags_[offset++]);
+ }
+}
+
+bool Tail::match(Agent &agent, std::size_t offset) const {
+ MARISA_DEBUG_IF(buf_.empty(), MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(agent.state().query_pos() >= agent.query().length(),
+ MARISA_BOUND_ERROR);
+
+ State &state = agent.state();
+ if (end_flags_.empty()) {
+ const char * const ptr = &buf_[offset] - state.query_pos();
+ do {
+ if (ptr[state.query_pos()] != agent.query()[state.query_pos()]) {
+ return false;
+ }
+ state.set_query_pos(state.query_pos() + 1);
+ if (ptr[state.query_pos()] == '\0') {
+ return true;
+ }
+ } while (state.query_pos() < agent.query().length());
+ return false;
+ } else {
+ do {
+ if (buf_[offset] != agent.query()[state.query_pos()]) {
+ return false;
+ }
+ state.set_query_pos(state.query_pos() + 1);
+ if (end_flags_[offset++]) {
+ return true;
+ }
+ } while (state.query_pos() < agent.query().length());
+ return false;
+ }
+}
+
+bool Tail::prefix_match(Agent &agent, std::size_t offset) const {
+ MARISA_DEBUG_IF(buf_.empty(), MARISA_STATE_ERROR);
+
+ State &state = agent.state();
+ if (end_flags_.empty()) {
+ const char *ptr = &buf_[offset] - state.query_pos();
+ do {
+ if (ptr[state.query_pos()] != agent.query()[state.query_pos()]) {
+ return false;
+ }
+ state.key_buf().push_back(ptr[state.query_pos()]);
+ state.set_query_pos(state.query_pos() + 1);
+ if (ptr[state.query_pos()] == '\0') {
+ return true;
+ }
+ } while (state.query_pos() < agent.query().length());
+ ptr += state.query_pos();
+ do {
+ state.key_buf().push_back(*ptr);
+ } while (*++ptr != '\0');
+ return true;
+ } else {
+ do {
+ if (buf_[offset] != agent.query()[state.query_pos()]) {
+ return false;
+ }
+ state.key_buf().push_back(buf_[offset]);
+ state.set_query_pos(state.query_pos() + 1);
+ if (end_flags_[offset++]) {
+ return true;
+ }
+ } while (state.query_pos() < agent.query().length());
+ do {
+ state.key_buf().push_back(buf_[offset]);
+ } while (!end_flags_[offset++]);
+ return true;
+ }
+}
+
+void Tail::clear() {
+ Tail().swap(*this);
+}
+
+void Tail::swap(Tail &rhs) {
+ buf_.swap(rhs.buf_);
+ end_flags_.swap(rhs.end_flags_);
+}
+
+void Tail::build_(Vector<Entry> &entries, Vector<UInt32> *offsets,
+ TailMode mode) {
+ for (std::size_t i = 0; i < entries.size(); ++i) {
+ entries[i].set_id(i);
+ }
+ Algorithm().sort(entries.begin(), entries.end());
+
+ Vector<UInt32> temp_offsets;
+ temp_offsets.resize(entries.size(), 0);
+
+ const Entry dummy;
+ const Entry *last = &dummy;
+ for (std::size_t i = entries.size(); i > 0; --i) {
+ const Entry &current = entries[i - 1];
+ MARISA_THROW_IF(current.length() == 0, MARISA_RANGE_ERROR);
+ std::size_t match = 0;
+ while ((match < current.length()) && (match < last->length()) &&
+ ((*last)[match] == current[match])) {
+ ++match;
+ }
+ if ((match == current.length()) && (last->length() != 0)) {
+ temp_offsets[current.id()] = (UInt32)(
+ temp_offsets[last->id()] + (last->length() - match));
+ } else {
+ temp_offsets[current.id()] = (UInt32)buf_.size();
+ for (std::size_t j = 1; j <= current.length(); ++j) {
+ buf_.push_back(current[current.length() - j]);
+ }
+ if (mode == MARISA_TEXT_TAIL) {
+ buf_.push_back('\0');
+ } else {
+ for (std::size_t j = 1; j < current.length(); ++j) {
+ end_flags_.push_back(false);
+ }
+ end_flags_.push_back(true);
+ }
+ MARISA_THROW_IF(buf_.size() > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ }
+ last = &current;
+ }
+ buf_.shrink();
+
+ offsets->swap(temp_offsets);
+}
+
+void Tail::map_(Mapper &mapper) {
+ buf_.map(mapper);
+ end_flags_.map(mapper);
+}
+
+void Tail::read_(Reader &reader) {
+ buf_.read(reader);
+ end_flags_.read(reader);
+}
+
+void Tail::write_(Writer &writer) const {
+ buf_.write(writer);
+ end_flags_.write(writer);
+}
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/trie/tail.h b/contrib/python/marisa-trie/marisa/grimoire/trie/tail.h
new file mode 100644
index 0000000000..7e5ca1d3e7
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/trie/tail.h
@@ -0,0 +1,73 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_TRIE_TAIL_H_
+#define MARISA_GRIMOIRE_TRIE_TAIL_H_
+
+#include "../../agent.h"
+#include "../vector.h"
+#include "entry.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class Tail {
+ public:
+ Tail();
+
+ void build(Vector<Entry> &entries, Vector<UInt32> *offsets,
+ TailMode mode);
+
+ void map(Mapper &mapper);
+ void read(Reader &reader);
+ void write(Writer &writer) const;
+
+ void restore(Agent &agent, std::size_t offset) const;
+ bool match(Agent &agent, std::size_t offset) const;
+ bool prefix_match(Agent &agent, std::size_t offset) const;
+
+ const char &operator[](std::size_t offset) const {
+ MARISA_DEBUG_IF(offset >= buf_.size(), MARISA_BOUND_ERROR);
+ return buf_[offset];
+ }
+
+ TailMode mode() const {
+ return end_flags_.empty() ? MARISA_TEXT_TAIL : MARISA_BINARY_TAIL;
+ }
+
+ bool empty() const {
+ return buf_.empty();
+ }
+ std::size_t size() const {
+ return buf_.size();
+ }
+ std::size_t total_size() const {
+ return buf_.total_size() + end_flags_.total_size();
+ }
+ std::size_t io_size() const {
+ return buf_.io_size() + end_flags_.io_size();
+ }
+
+ void clear();
+ void swap(Tail &rhs);
+
+ private:
+ Vector<char> buf_;
+ BitVector end_flags_;
+
+ void build_(Vector<Entry> &entries, Vector<UInt32> *offsets,
+ TailMode mode);
+
+ void map_(Mapper &mapper);
+ void read_(Reader &reader);
+ void write_(Writer &writer) const;
+
+ // Disallows copy and assignment.
+ Tail(const Tail &);
+ Tail &operator=(const Tail &);
+};
+
+} // namespace trie
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_TRIE_TAIL_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector.h b/contrib/python/marisa-trie/marisa/grimoire/vector.h
new file mode 100644
index 0000000000..d942a7f279
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/vector.h
@@ -0,0 +1,19 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_VECTOR_H_
+#define MARISA_GRIMOIRE_VECTOR_H_
+
+#include "vector/vector.h"
+#include "vector/flat-vector.h"
+#include "vector/bit-vector.h"
+
+namespace marisa {
+namespace grimoire {
+
+using vector::Vector;
+typedef vector::FlatVector FlatVector;
+typedef vector::BitVector BitVector;
+
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_VECTOR_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.cc b/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.cc
new file mode 100644
index 0000000000..a5abc69319
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.cc
@@ -0,0 +1,825 @@
+#include "pop-count.h"
+#include "bit-vector.h"
+
+namespace marisa {
+namespace grimoire {
+namespace vector {
+namespace {
+
+const UInt8 SELECT_TABLE[8][256] = {
+ {
+ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
+ },
+ {
+ 7, 7, 7, 1, 7, 2, 2, 1, 7, 3, 3, 1, 3, 2, 2, 1,
+ 7, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
+ 7, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1,
+ 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
+ 7, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1,
+ 6, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
+ 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1,
+ 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
+ 7, 7, 7, 1, 7, 2, 2, 1, 7, 3, 3, 1, 3, 2, 2, 1,
+ 7, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
+ 7, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1,
+ 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
+ 7, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1,
+ 6, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1,
+ 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1,
+ 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1
+ },
+ {
+ 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 3, 7, 3, 3, 2,
+ 7, 7, 7, 4, 7, 4, 4, 2, 7, 4, 4, 3, 4, 3, 3, 2,
+ 7, 7, 7, 5, 7, 5, 5, 2, 7, 5, 5, 3, 5, 3, 3, 2,
+ 7, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2,
+ 7, 7, 7, 6, 7, 6, 6, 2, 7, 6, 6, 3, 6, 3, 3, 2,
+ 7, 6, 6, 4, 6, 4, 4, 2, 6, 4, 4, 3, 4, 3, 3, 2,
+ 7, 6, 6, 5, 6, 5, 5, 2, 6, 5, 5, 3, 5, 3, 3, 2,
+ 6, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2,
+ 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 3, 7, 3, 3, 2,
+ 7, 7, 7, 4, 7, 4, 4, 2, 7, 4, 4, 3, 4, 3, 3, 2,
+ 7, 7, 7, 5, 7, 5, 5, 2, 7, 5, 5, 3, 5, 3, 3, 2,
+ 7, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2,
+ 7, 7, 7, 6, 7, 6, 6, 2, 7, 6, 6, 3, 6, 3, 3, 2,
+ 7, 6, 6, 4, 6, 4, 4, 2, 6, 4, 4, 3, 4, 3, 3, 2,
+ 7, 6, 6, 5, 6, 5, 5, 2, 6, 5, 5, 3, 5, 3, 3, 2,
+ 6, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2
+ },
+ {
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3,
+ 7, 7, 7, 7, 7, 7, 7, 4, 7, 7, 7, 4, 7, 4, 4, 3,
+ 7, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 5, 5, 3,
+ 7, 7, 7, 5, 7, 5, 5, 4, 7, 5, 5, 4, 5, 4, 4, 3,
+ 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 3,
+ 7, 7, 7, 6, 7, 6, 6, 4, 7, 6, 6, 4, 6, 4, 4, 3,
+ 7, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 3,
+ 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3,
+ 7, 7, 7, 7, 7, 7, 7, 4, 7, 7, 7, 4, 7, 4, 4, 3,
+ 7, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 5, 5, 3,
+ 7, 7, 7, 5, 7, 5, 5, 4, 7, 5, 5, 4, 5, 4, 4, 3,
+ 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 3,
+ 7, 7, 7, 6, 7, 6, 6, 4, 7, 6, 6, 4, 6, 4, 4, 3,
+ 7, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 3,
+ 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3
+ },
+ {
+ 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, 4,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5,
+ 7, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 5, 5, 4,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
+ 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 4,
+ 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 5,
+ 7, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4,
+ 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, 4,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5,
+ 7, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 5, 7, 5, 5, 4,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
+ 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 4,
+ 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 5,
+ 7, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4
+ },
+ {
+ 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, 5,
+ 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, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
+ 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 5,
+ 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, 5,
+ 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, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
+ 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 6, 7, 6, 6, 5
+ },
+ {
+ 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,
+ 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, 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,
+ 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, 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,
+ 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,
+ 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,
+ 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
+ }
+};
+
+#if MARISA_WORD_SIZE == 64
+const UInt64 MASK_55 = 0x5555555555555555ULL;
+const UInt64 MASK_33 = 0x3333333333333333ULL;
+const UInt64 MASK_0F = 0x0F0F0F0F0F0F0F0FULL;
+const UInt64 MASK_01 = 0x0101010101010101ULL;
+const UInt64 MASK_80 = 0x8080808080808080ULL;
+
+std::size_t select_bit(std::size_t i, std::size_t bit_id, UInt64 unit) {
+ UInt64 counts;
+ {
+ #if defined(MARISA_X64) && defined(MARISA_USE_SSSE3)
+ __m128i lower_nibbles = _mm_cvtsi64_si128(unit & 0x0F0F0F0F0F0F0F0FULL);
+ __m128i upper_nibbles = _mm_cvtsi64_si128(unit & 0xF0F0F0F0F0F0F0F0ULL);
+ upper_nibbles = _mm_srli_epi32(upper_nibbles, 4);
+
+ __m128i lower_counts =
+ _mm_set_epi8(4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0);
+ lower_counts = _mm_shuffle_epi8(lower_counts, lower_nibbles);
+ __m128i upper_counts =
+ _mm_set_epi8(4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0);
+ upper_counts = _mm_shuffle_epi8(upper_counts, upper_nibbles);
+
+ counts = _mm_cvtsi128_si64(_mm_add_epi8(lower_counts, upper_counts));
+ #else // defined(MARISA_X64) && defined(MARISA_USE_SSSE3)
+ counts = unit - ((unit >> 1) & MASK_55);
+ counts = (counts & MASK_33) + ((counts >> 2) & MASK_33);
+ counts = (counts + (counts >> 4)) & MASK_0F;
+ #endif // defined(MARISA_X64) && defined(MARISA_USE_SSSE3)
+ counts *= MASK_01;
+ }
+
+ #if defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
+ UInt8 skip;
+ {
+ __m128i x = _mm_cvtsi64_si128((i + 1) * MASK_01);
+ __m128i y = _mm_cvtsi64_si128(counts);
+ x = _mm_cmpgt_epi8(x, y);
+ skip = (UInt8)PopCount::count(_mm_cvtsi128_si64(x));
+ }
+ #else // defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
+ const UInt64 x = (counts | MASK_80) - ((i + 1) * MASK_01);
+ #ifdef _MSC_VER
+ unsigned long skip;
+ ::_BitScanForward64(&skip, (x & MASK_80) >> 7);
+ #else // _MSC_VER
+ const int skip = ::__builtin_ctzll((x & MASK_80) >> 7);
+ #endif // _MSC_VER
+ #endif // defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
+
+ bit_id += skip;
+ unit >>= skip;
+ i -= ((counts << 8) >> skip) & 0xFF;
+
+ return bit_id + SELECT_TABLE[i][unit & 0xFF];
+}
+#else // MARISA_WORD_SIZE == 64
+ #ifdef MARISA_USE_SSE2
+const UInt8 POPCNT_TABLE[256] = {
+ 0, 8, 8, 16, 8, 16, 16, 24, 8, 16, 16, 24, 16, 24, 24, 32,
+ 8, 16, 16, 24, 16, 24, 24, 32, 16, 24, 24, 32, 24, 32, 32, 40,
+ 8, 16, 16, 24, 16, 24, 24, 32, 16, 24, 24, 32, 24, 32, 32, 40,
+ 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
+ 8, 16, 16, 24, 16, 24, 24, 32, 16, 24, 24, 32, 24, 32, 32, 40,
+ 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
+ 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
+ 24, 32, 32, 40, 32, 40, 40, 48, 32, 40, 40, 48, 40, 48, 48, 56,
+ 8, 16, 16, 24, 16, 24, 24, 32, 16, 24, 24, 32, 24, 32, 32, 40,
+ 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
+ 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
+ 24, 32, 32, 40, 32, 40, 40, 48, 32, 40, 40, 48, 40, 48, 48, 56,
+ 16, 24, 24, 32, 24, 32, 32, 40, 24, 32, 32, 40, 32, 40, 40, 48,
+ 24, 32, 32, 40, 32, 40, 40, 48, 32, 40, 40, 48, 40, 48, 48, 56,
+ 24, 32, 32, 40, 32, 40, 40, 48, 32, 40, 40, 48, 40, 48, 48, 56,
+ 32, 40, 40, 48, 40, 48, 48, 56, 40, 48, 48, 56, 48, 56, 56, 64
+};
+
+std::size_t select_bit(std::size_t i, std::size_t bit_id,
+ UInt32 unit_lo, UInt32 unit_hi) {
+ __m128i unit;
+ {
+ __m128i lower_dword = _mm_cvtsi32_si128(unit_lo);
+ __m128i upper_dword = _mm_cvtsi32_si128(unit_hi);
+ upper_dword = _mm_slli_si128(upper_dword, 4);
+ unit = _mm_or_si128(lower_dword, upper_dword);
+ }
+
+ __m128i counts;
+ {
+ #ifdef MARISA_USE_SSSE3
+ __m128i lower_nibbles = _mm_set1_epi8(0x0F);
+ lower_nibbles = _mm_and_si128(lower_nibbles, unit);
+ __m128i upper_nibbles = _mm_set1_epi8((UInt8)0xF0);
+ upper_nibbles = _mm_and_si128(upper_nibbles, unit);
+ upper_nibbles = _mm_srli_epi32(upper_nibbles, 4);
+
+ __m128i lower_counts =
+ _mm_set_epi8(4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0);
+ lower_counts = _mm_shuffle_epi8(lower_counts, lower_nibbles);
+ __m128i upper_counts =
+ _mm_set_epi8(4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0);
+ upper_counts = _mm_shuffle_epi8(upper_counts, upper_nibbles);
+
+ counts = _mm_add_epi8(lower_counts, upper_counts);
+ #else // MARISA_USE_SSSE3
+ __m128i x = _mm_srli_epi32(unit, 1);
+ x = _mm_and_si128(x, _mm_set1_epi8(0x55));
+ x = _mm_sub_epi8(unit, x);
+
+ __m128i y = _mm_srli_epi32(x, 2);
+ y = _mm_and_si128(y, _mm_set1_epi8(0x33));
+ x = _mm_and_si128(x, _mm_set1_epi8(0x33));
+ x = _mm_add_epi8(x, y);
+
+ y = _mm_srli_epi32(x, 4);
+ x = _mm_add_epi8(x, y);
+ counts = _mm_and_si128(x, _mm_set1_epi8(0x0F));
+ #endif // MARISA_USE_SSSE3
+ }
+
+ __m128i accumulated_counts;
+ {
+ __m128i x = counts;
+ x = _mm_slli_si128(x, 1);
+ __m128i y = counts;
+ y = _mm_add_epi32(y, x);
+
+ x = y;
+ y = _mm_slli_si128(y, 2);
+ x = _mm_add_epi32(x, y);
+
+ y = x;
+ x = _mm_slli_si128(x, 4);
+ y = _mm_add_epi32(y, x);
+
+ accumulated_counts = _mm_set_epi32(0x7F7F7F7FU, 0x7F7F7F7FU, 0, 0);
+ accumulated_counts = _mm_or_si128(accumulated_counts, y);
+ }
+
+ UInt8 skip;
+ {
+ __m128i x = _mm_set1_epi8((UInt8)(i + 1));
+ x = _mm_cmpgt_epi8(x, accumulated_counts);
+ skip = POPCNT_TABLE[_mm_movemask_epi8(x)];
+ }
+
+ UInt8 byte;
+ {
+ #ifdef _MSC_VER
+ __declspec(align(16)) UInt8 unit_bytes[16];
+ __declspec(align(16)) UInt8 accumulated_counts_bytes[16];
+ #else // _MSC_VER
+ UInt8 unit_bytes[16] __attribute__ ((aligned (16)));
+ UInt8 accumulated_counts_bytes[16] __attribute__ ((aligned (16)));
+ #endif // _MSC_VER
+ accumulated_counts = _mm_slli_si128(accumulated_counts, 1);
+ _mm_store_si128(reinterpret_cast<__m128i *>(unit_bytes), unit);
+ _mm_store_si128(reinterpret_cast<__m128i *>(accumulated_counts_bytes),
+ accumulated_counts);
+
+ bit_id += skip;
+ byte = unit_bytes[skip / 8];
+ i -= accumulated_counts_bytes[skip / 8];
+ }
+
+ return bit_id + SELECT_TABLE[i][byte];
+}
+ #endif // MARISA_USE_SSE2
+#endif // MARISA_WORD_SIZE == 64
+
+} // namespace
+
+#if MARISA_WORD_SIZE == 64
+
+std::size_t BitVector::rank1(std::size_t i) const {
+ MARISA_DEBUG_IF(ranks_.empty(), MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(i > size_, MARISA_BOUND_ERROR);
+
+ const RankIndex &rank = ranks_[i / 512];
+ std::size_t offset = rank.abs();
+ switch ((i / 64) % 8) {
+ case 1: {
+ offset += rank.rel1();
+ break;
+ }
+ case 2: {
+ offset += rank.rel2();
+ break;
+ }
+ case 3: {
+ offset += rank.rel3();
+ break;
+ }
+ case 4: {
+ offset += rank.rel4();
+ break;
+ }
+ case 5: {
+ offset += rank.rel5();
+ break;
+ }
+ case 6: {
+ offset += rank.rel6();
+ break;
+ }
+ case 7: {
+ offset += rank.rel7();
+ break;
+ }
+ }
+ offset += PopCount::count(units_[i / 64] & ((1ULL << (i % 64)) - 1));
+ return offset;
+}
+
+std::size_t BitVector::select0(std::size_t i) const {
+ MARISA_DEBUG_IF(select0s_.empty(), MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(i >= num_0s(), MARISA_BOUND_ERROR);
+
+ const std::size_t select_id = i / 512;
+ MARISA_DEBUG_IF((select_id + 1) >= select0s_.size(), MARISA_BOUND_ERROR);
+ if ((i % 512) == 0) {
+ return select0s_[select_id];
+ }
+ std::size_t begin = select0s_[select_id] / 512;
+ std::size_t end = (select0s_[select_id + 1] + 511) / 512;
+ if (begin + 10 >= end) {
+ while (i >= ((begin + 1) * 512) - ranks_[begin + 1].abs()) {
+ ++begin;
+ }
+ } else {
+ while (begin + 1 < end) {
+ const std::size_t middle = (begin + end) / 2;
+ if (i < (middle * 512) - ranks_[middle].abs()) {
+ end = middle;
+ } else {
+ begin = middle;
+ }
+ }
+ }
+ const std::size_t rank_id = begin;
+ i -= (rank_id * 512) - ranks_[rank_id].abs();
+
+ const RankIndex &rank = ranks_[rank_id];
+ std::size_t unit_id = rank_id * 8;
+ if (i < (256U - rank.rel4())) {
+ if (i < (128U - rank.rel2())) {
+ if (i >= (64U - rank.rel1())) {
+ unit_id += 1;
+ i -= 64 - rank.rel1();
+ }
+ } else if (i < (192U - rank.rel3())) {
+ unit_id += 2;
+ i -= 128 - rank.rel2();
+ } else {
+ unit_id += 3;
+ i -= 192 - rank.rel3();
+ }
+ } else if (i < (384U - rank.rel6())) {
+ if (i < (320U - rank.rel5())) {
+ unit_id += 4;
+ i -= 256 - rank.rel4();
+ } else {
+ unit_id += 5;
+ i -= 320 - rank.rel5();
+ }
+ } else if (i < (448U - rank.rel7())) {
+ unit_id += 6;
+ i -= 384 - rank.rel6();
+ } else {
+ unit_id += 7;
+ i -= 448 - rank.rel7();
+ }
+
+ return select_bit(i, unit_id * 64, ~units_[unit_id]);
+}
+
+std::size_t BitVector::select1(std::size_t i) const {
+ MARISA_DEBUG_IF(select1s_.empty(), MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(i >= num_1s(), MARISA_BOUND_ERROR);
+
+ const std::size_t select_id = i / 512;
+ MARISA_DEBUG_IF((select_id + 1) >= select1s_.size(), MARISA_BOUND_ERROR);
+ if ((i % 512) == 0) {
+ return select1s_[select_id];
+ }
+ std::size_t begin = select1s_[select_id] / 512;
+ std::size_t end = (select1s_[select_id + 1] + 511) / 512;
+ if (begin + 10 >= end) {
+ while (i >= ranks_[begin + 1].abs()) {
+ ++begin;
+ }
+ } else {
+ while (begin + 1 < end) {
+ const std::size_t middle = (begin + end) / 2;
+ if (i < ranks_[middle].abs()) {
+ end = middle;
+ } else {
+ begin = middle;
+ }
+ }
+ }
+ const std::size_t rank_id = begin;
+ i -= ranks_[rank_id].abs();
+
+ const RankIndex &rank = ranks_[rank_id];
+ std::size_t unit_id = rank_id * 8;
+ if (i < rank.rel4()) {
+ if (i < rank.rel2()) {
+ if (i >= rank.rel1()) {
+ unit_id += 1;
+ i -= rank.rel1();
+ }
+ } else if (i < rank.rel3()) {
+ unit_id += 2;
+ i -= rank.rel2();
+ } else {
+ unit_id += 3;
+ i -= rank.rel3();
+ }
+ } else if (i < rank.rel6()) {
+ if (i < rank.rel5()) {
+ unit_id += 4;
+ i -= rank.rel4();
+ } else {
+ unit_id += 5;
+ i -= rank.rel5();
+ }
+ } else if (i < rank.rel7()) {
+ unit_id += 6;
+ i -= rank.rel6();
+ } else {
+ unit_id += 7;
+ i -= rank.rel7();
+ }
+
+ return select_bit(i, unit_id * 64, units_[unit_id]);
+}
+
+#else // MARISA_WORD_SIZE == 64
+
+std::size_t BitVector::rank1(std::size_t i) const {
+ MARISA_DEBUG_IF(ranks_.empty(), MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(i > size_, MARISA_BOUND_ERROR);
+
+ const RankIndex &rank = ranks_[i / 512];
+ std::size_t offset = rank.abs();
+ switch ((i / 64) % 8) {
+ case 1: {
+ offset += rank.rel1();
+ break;
+ }
+ case 2: {
+ offset += rank.rel2();
+ break;
+ }
+ case 3: {
+ offset += rank.rel3();
+ break;
+ }
+ case 4: {
+ offset += rank.rel4();
+ break;
+ }
+ case 5: {
+ offset += rank.rel5();
+ break;
+ }
+ case 6: {
+ offset += rank.rel6();
+ break;
+ }
+ case 7: {
+ offset += rank.rel7();
+ break;
+ }
+ }
+ if (((i / 32) & 1) == 1) {
+ offset += PopCount::count(units_[(i / 32) - 1]);
+ }
+ offset += PopCount::count(units_[i / 32] & ((1U << (i % 32)) - 1));
+ return offset;
+}
+
+std::size_t BitVector::select0(std::size_t i) const {
+ MARISA_DEBUG_IF(select0s_.empty(), MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(i >= num_0s(), MARISA_BOUND_ERROR);
+
+ const std::size_t select_id = i / 512;
+ MARISA_DEBUG_IF((select_id + 1) >= select0s_.size(), MARISA_BOUND_ERROR);
+ if ((i % 512) == 0) {
+ return select0s_[select_id];
+ }
+ std::size_t begin = select0s_[select_id] / 512;
+ std::size_t end = (select0s_[select_id + 1] + 511) / 512;
+ if (begin + 10 >= end) {
+ while (i >= ((begin + 1) * 512) - ranks_[begin + 1].abs()) {
+ ++begin;
+ }
+ } else {
+ while (begin + 1 < end) {
+ const std::size_t middle = (begin + end) / 2;
+ if (i < (middle * 512) - ranks_[middle].abs()) {
+ end = middle;
+ } else {
+ begin = middle;
+ }
+ }
+ }
+ const std::size_t rank_id = begin;
+ i -= (rank_id * 512) - ranks_[rank_id].abs();
+
+ const RankIndex &rank = ranks_[rank_id];
+ std::size_t unit_id = rank_id * 16;
+ if (i < (256U - rank.rel4())) {
+ if (i < (128U - rank.rel2())) {
+ if (i >= (64U - rank.rel1())) {
+ unit_id += 2;
+ i -= 64 - rank.rel1();
+ }
+ } else if (i < (192U - rank.rel3())) {
+ unit_id += 4;
+ i -= 128 - rank.rel2();
+ } else {
+ unit_id += 6;
+ i -= 192 - rank.rel3();
+ }
+ } else if (i < (384U - rank.rel6())) {
+ if (i < (320U - rank.rel5())) {
+ unit_id += 8;
+ i -= 256 - rank.rel4();
+ } else {
+ unit_id += 10;
+ i -= 320 - rank.rel5();
+ }
+ } else if (i < (448U - rank.rel7())) {
+ unit_id += 12;
+ i -= 384 - rank.rel6();
+ } else {
+ unit_id += 14;
+ i -= 448 - rank.rel7();
+ }
+
+#ifdef MARISA_USE_SSE2
+ return select_bit(i, unit_id * 32, ~units_[unit_id], ~units_[unit_id + 1]);
+#else // MARISA_USE_SSE2
+ UInt32 unit = ~units_[unit_id];
+ PopCount count(unit);
+ if (i >= count.lo32()) {
+ ++unit_id;
+ i -= count.lo32();
+ unit = ~units_[unit_id];
+ count = PopCount(unit);
+ }
+
+ std::size_t bit_id = unit_id * 32;
+ if (i < count.lo16()) {
+ if (i >= count.lo8()) {
+ bit_id += 8;
+ unit >>= 8;
+ i -= count.lo8();
+ }
+ } else if (i < count.lo24()) {
+ bit_id += 16;
+ unit >>= 16;
+ i -= count.lo16();
+ } else {
+ bit_id += 24;
+ unit >>= 24;
+ i -= count.lo24();
+ }
+ return bit_id + SELECT_TABLE[i][unit & 0xFF];
+#endif // MARISA_USE_SSE2
+}
+
+std::size_t BitVector::select1(std::size_t i) const {
+ MARISA_DEBUG_IF(select1s_.empty(), MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(i >= num_1s(), MARISA_BOUND_ERROR);
+
+ const std::size_t select_id = i / 512;
+ MARISA_DEBUG_IF((select_id + 1) >= select1s_.size(), MARISA_BOUND_ERROR);
+ if ((i % 512) == 0) {
+ return select1s_[select_id];
+ }
+ std::size_t begin = select1s_[select_id] / 512;
+ std::size_t end = (select1s_[select_id + 1] + 511) / 512;
+ if (begin + 10 >= end) {
+ while (i >= ranks_[begin + 1].abs()) {
+ ++begin;
+ }
+ } else {
+ while (begin + 1 < end) {
+ const std::size_t middle = (begin + end) / 2;
+ if (i < ranks_[middle].abs()) {
+ end = middle;
+ } else {
+ begin = middle;
+ }
+ }
+ }
+ const std::size_t rank_id = begin;
+ i -= ranks_[rank_id].abs();
+
+ const RankIndex &rank = ranks_[rank_id];
+ std::size_t unit_id = rank_id * 16;
+ if (i < rank.rel4()) {
+ if (i < rank.rel2()) {
+ if (i >= rank.rel1()) {
+ unit_id += 2;
+ i -= rank.rel1();
+ }
+ } else if (i < rank.rel3()) {
+ unit_id += 4;
+ i -= rank.rel2();
+ } else {
+ unit_id += 6;
+ i -= rank.rel3();
+ }
+ } else if (i < rank.rel6()) {
+ if (i < rank.rel5()) {
+ unit_id += 8;
+ i -= rank.rel4();
+ } else {
+ unit_id += 10;
+ i -= rank.rel5();
+ }
+ } else if (i < rank.rel7()) {
+ unit_id += 12;
+ i -= rank.rel6();
+ } else {
+ unit_id += 14;
+ i -= rank.rel7();
+ }
+
+#ifdef MARISA_USE_SSE2
+ return select_bit(i, unit_id * 32, units_[unit_id], units_[unit_id + 1]);
+#else // MARISA_USE_SSE2
+ UInt32 unit = units_[unit_id];
+ PopCount count(unit);
+ if (i >= count.lo32()) {
+ ++unit_id;
+ i -= count.lo32();
+ unit = units_[unit_id];
+ count = PopCount(unit);
+ }
+
+ std::size_t bit_id = unit_id * 32;
+ if (i < count.lo16()) {
+ if (i >= count.lo8()) {
+ bit_id += 8;
+ unit >>= 8;
+ i -= count.lo8();
+ }
+ } else if (i < count.lo24()) {
+ bit_id += 16;
+ unit >>= 16;
+ i -= count.lo16();
+ } else {
+ bit_id += 24;
+ unit >>= 24;
+ i -= count.lo24();
+ }
+ return bit_id + SELECT_TABLE[i][unit & 0xFF];
+#endif // MARISA_USE_SSE2
+}
+
+#endif // MARISA_WORD_SIZE == 64
+
+void BitVector::build_index(const BitVector &bv,
+ bool enables_select0, bool enables_select1) {
+ ranks_.resize((bv.size() / 512) + (((bv.size() % 512) != 0) ? 1 : 0) + 1);
+
+ std::size_t num_0s = 0;
+ std::size_t num_1s = 0;
+
+ for (std::size_t i = 0; i < bv.size(); ++i) {
+ if ((i % 64) == 0) {
+ const std::size_t rank_id = i / 512;
+ switch ((i / 64) % 8) {
+ case 0: {
+ ranks_[rank_id].set_abs(num_1s);
+ break;
+ }
+ case 1: {
+ ranks_[rank_id].set_rel1(num_1s - ranks_[rank_id].abs());
+ break;
+ }
+ case 2: {
+ ranks_[rank_id].set_rel2(num_1s - ranks_[rank_id].abs());
+ break;
+ }
+ case 3: {
+ ranks_[rank_id].set_rel3(num_1s - ranks_[rank_id].abs());
+ break;
+ }
+ case 4: {
+ ranks_[rank_id].set_rel4(num_1s - ranks_[rank_id].abs());
+ break;
+ }
+ case 5: {
+ ranks_[rank_id].set_rel5(num_1s - ranks_[rank_id].abs());
+ break;
+ }
+ case 6: {
+ ranks_[rank_id].set_rel6(num_1s - ranks_[rank_id].abs());
+ break;
+ }
+ case 7: {
+ ranks_[rank_id].set_rel7(num_1s - ranks_[rank_id].abs());
+ break;
+ }
+ }
+ }
+
+ if (bv[i]) {
+ if (enables_select1 && ((num_1s % 512) == 0)) {
+ select1s_.push_back(static_cast<UInt32>(i));
+ }
+ ++num_1s;
+ } else {
+ if (enables_select0 && ((num_0s % 512) == 0)) {
+ select0s_.push_back(static_cast<UInt32>(i));
+ }
+ ++num_0s;
+ }
+ }
+
+ if ((bv.size() % 512) != 0) {
+ const std::size_t rank_id = (bv.size() - 1) / 512;
+ switch (((bv.size() - 1) / 64) % 8) {
+ case 0: {
+ ranks_[rank_id].set_rel1(num_1s - ranks_[rank_id].abs());
+ }
+ case 1: {
+ ranks_[rank_id].set_rel2(num_1s - ranks_[rank_id].abs());
+ }
+ case 2: {
+ ranks_[rank_id].set_rel3(num_1s - ranks_[rank_id].abs());
+ }
+ case 3: {
+ ranks_[rank_id].set_rel4(num_1s - ranks_[rank_id].abs());
+ }
+ case 4: {
+ ranks_[rank_id].set_rel5(num_1s - ranks_[rank_id].abs());
+ }
+ case 5: {
+ ranks_[rank_id].set_rel6(num_1s - ranks_[rank_id].abs());
+ }
+ case 6: {
+ ranks_[rank_id].set_rel7(num_1s - ranks_[rank_id].abs());
+ break;
+ }
+ }
+ }
+
+ size_ = bv.size();
+ num_1s_ = bv.num_1s();
+
+ ranks_.back().set_abs(num_1s);
+ if (enables_select0) {
+ select0s_.push_back(static_cast<UInt32>(bv.size()));
+ select0s_.shrink();
+ }
+ if (enables_select1) {
+ select1s_.push_back(static_cast<UInt32>(bv.size()));
+ select1s_.shrink();
+ }
+}
+
+} // namespace vector
+} // namespace grimoire
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.h b/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.h
new file mode 100644
index 0000000000..56f99ed699
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/vector/bit-vector.h
@@ -0,0 +1,180 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_VECTOR_BIT_VECTOR_H_
+#define MARISA_GRIMOIRE_VECTOR_BIT_VECTOR_H_
+
+#include "rank-index.h"
+#include "vector.h"
+
+namespace marisa {
+namespace grimoire {
+namespace vector {
+
+class BitVector {
+ public:
+#if MARISA_WORD_SIZE == 64
+ typedef UInt64 Unit;
+#else // MARISA_WORD_SIZE == 64
+ typedef UInt32 Unit;
+#endif // MARISA_WORD_SIZE == 64
+
+ BitVector()
+ : units_(), size_(0), num_1s_(0), ranks_(), select0s_(), select1s_() {}
+
+ void build(bool enables_select0, bool enables_select1) {
+ BitVector temp;
+ temp.build_index(*this, enables_select0, enables_select1);
+ units_.shrink();
+ temp.units_.swap(units_);
+ swap(temp);
+ }
+
+ void map(Mapper &mapper) {
+ BitVector temp;
+ temp.map_(mapper);
+ swap(temp);
+ }
+ void read(Reader &reader) {
+ BitVector temp;
+ temp.read_(reader);
+ swap(temp);
+ }
+ void write(Writer &writer) const {
+ write_(writer);
+ }
+
+ void disable_select0() {
+ select0s_.clear();
+ }
+ void disable_select1() {
+ select1s_.clear();
+ }
+
+ void push_back(bool bit) {
+ MARISA_THROW_IF(size_ == MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ if (size_ == (MARISA_WORD_SIZE * units_.size())) {
+ units_.resize(units_.size() + (64 / MARISA_WORD_SIZE), 0);
+ }
+ if (bit) {
+ units_[size_ / MARISA_WORD_SIZE] |=
+ (Unit)1 << (size_ % MARISA_WORD_SIZE);
+ ++num_1s_;
+ }
+ ++size_;
+ }
+
+ bool operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
+ return (units_[i / MARISA_WORD_SIZE]
+ & ((Unit)1 << (i % MARISA_WORD_SIZE))) != 0;
+ }
+
+ std::size_t rank0(std::size_t i) const {
+ MARISA_DEBUG_IF(ranks_.empty(), MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(i > size_, MARISA_BOUND_ERROR);
+ return i - rank1(i);
+ }
+ std::size_t rank1(std::size_t i) const;
+
+ std::size_t select0(std::size_t i) const;
+ std::size_t select1(std::size_t i) const;
+
+ std::size_t num_0s() const {
+ return size_ - num_1s_;
+ }
+ std::size_t num_1s() const {
+ return num_1s_;
+ }
+
+ bool empty() const {
+ return size_ == 0;
+ }
+ std::size_t size() const {
+ return size_;
+ }
+ std::size_t total_size() const {
+ return units_.total_size() + ranks_.total_size()
+ + select0s_.total_size() + select1s_.total_size();
+ }
+ std::size_t io_size() const {
+ return units_.io_size() + (sizeof(UInt32) * 2) + ranks_.io_size()
+ + select0s_.io_size() + select1s_.io_size();
+ }
+
+ void clear() {
+ BitVector().swap(*this);
+ }
+ void swap(BitVector &rhs) {
+ units_.swap(rhs.units_);
+ marisa::swap(size_, rhs.size_);
+ marisa::swap(num_1s_, rhs.num_1s_);
+ ranks_.swap(rhs.ranks_);
+ select0s_.swap(rhs.select0s_);
+ select1s_.swap(rhs.select1s_);
+ }
+
+ private:
+ Vector<Unit> units_;
+ std::size_t size_;
+ std::size_t num_1s_;
+ Vector<RankIndex> ranks_;
+ Vector<UInt32> select0s_;
+ Vector<UInt32> select1s_;
+
+ void build_index(const BitVector &bv,
+ bool enables_select0, bool enables_select1);
+
+ void map_(Mapper &mapper) {
+ units_.map(mapper);
+ {
+ UInt32 temp_size;
+ mapper.map(&temp_size);
+ size_ = temp_size;
+ }
+ {
+ UInt32 temp_num_1s;
+ mapper.map(&temp_num_1s);
+ MARISA_THROW_IF(temp_num_1s > size_, MARISA_FORMAT_ERROR);
+ num_1s_ = temp_num_1s;
+ }
+ ranks_.map(mapper);
+ select0s_.map(mapper);
+ select1s_.map(mapper);
+ }
+
+ void read_(Reader &reader) {
+ units_.read(reader);
+ {
+ UInt32 temp_size;
+ reader.read(&temp_size);
+ size_ = temp_size;
+ }
+ {
+ UInt32 temp_num_1s;
+ reader.read(&temp_num_1s);
+ MARISA_THROW_IF(temp_num_1s > size_, MARISA_FORMAT_ERROR);
+ num_1s_ = temp_num_1s;
+ }
+ ranks_.read(reader);
+ select0s_.read(reader);
+ select1s_.read(reader);
+ }
+
+ void write_(Writer &writer) const {
+ units_.write(writer);
+ writer.write((UInt32)size_);
+ writer.write((UInt32)num_1s_);
+ ranks_.write(writer);
+ select0s_.write(writer);
+ select1s_.write(writer);
+ }
+
+ // Disallows copy and assignment.
+ BitVector(const BitVector &);
+ BitVector &operator=(const BitVector &);
+};
+
+} // namespace vector
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_VECTOR_BIT_VECTOR_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/flat-vector.h b/contrib/python/marisa-trie/marisa/grimoire/vector/flat-vector.h
new file mode 100644
index 0000000000..14b25d7d82
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/vector/flat-vector.h
@@ -0,0 +1,206 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_VECTOR_FLAT_VECTOR_H_
+#define MARISA_GRIMOIRE_VECTOR_FLAT_VECTOR_H_
+
+#include "vector.h"
+
+namespace marisa {
+namespace grimoire {
+namespace vector {
+
+class FlatVector {
+ public:
+#if MARISA_WORD_SIZE == 64
+ typedef UInt64 Unit;
+#else // MARISA_WORD_SIZE == 64
+ typedef UInt32 Unit;
+#endif // MARISA_WORD_SIZE == 64
+
+ FlatVector() : units_(), value_size_(0), mask_(0), size_(0) {}
+
+ void build(const Vector<UInt32> &values) {
+ FlatVector temp;
+ temp.build_(values);
+ swap(temp);
+ }
+
+ void map(Mapper &mapper) {
+ FlatVector temp;
+ temp.map_(mapper);
+ swap(temp);
+ }
+ void read(Reader &reader) {
+ FlatVector temp;
+ temp.read_(reader);
+ swap(temp);
+ }
+ void write(Writer &writer) const {
+ write_(writer);
+ }
+
+ UInt32 operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
+
+ const std::size_t pos = i * value_size_;
+ const std::size_t unit_id = pos / MARISA_WORD_SIZE;
+ const std::size_t unit_offset = pos % MARISA_WORD_SIZE;
+
+ if ((unit_offset + value_size_) <= MARISA_WORD_SIZE) {
+ return (UInt32)(units_[unit_id] >> unit_offset) & mask_;
+ } else {
+ return (UInt32)((units_[unit_id] >> unit_offset)
+ | (units_[unit_id + 1] << (MARISA_WORD_SIZE - unit_offset))) & mask_;
+ }
+ }
+
+ std::size_t value_size() const {
+ return value_size_;
+ }
+ UInt32 mask() const {
+ return mask_;
+ }
+
+ bool empty() const {
+ return size_ == 0;
+ }
+ std::size_t size() const {
+ return size_;
+ }
+ std::size_t total_size() const {
+ return units_.total_size();
+ }
+ std::size_t io_size() const {
+ return units_.io_size() + (sizeof(UInt32) * 2) + sizeof(UInt64);
+ }
+
+ void clear() {
+ FlatVector().swap(*this);
+ }
+ void swap(FlatVector &rhs) {
+ units_.swap(rhs.units_);
+ marisa::swap(value_size_, rhs.value_size_);
+ marisa::swap(mask_, rhs.mask_);
+ marisa::swap(size_, rhs.size_);
+ }
+
+ private:
+ Vector<Unit> units_;
+ std::size_t value_size_;
+ UInt32 mask_;
+ std::size_t size_;
+
+ void build_(const Vector<UInt32> &values) {
+ UInt32 max_value = 0;
+ for (std::size_t i = 0; i < values.size(); ++i) {
+ if (values[i] > max_value) {
+ max_value = values[i];
+ }
+ }
+
+ std::size_t value_size = 0;
+ while (max_value != 0) {
+ ++value_size;
+ max_value >>= 1;
+ }
+
+ std::size_t num_units = values.empty() ? 0 : (64 / MARISA_WORD_SIZE);
+ if (value_size != 0) {
+ num_units = (std::size_t)(
+ (((UInt64)value_size * values.size()) + (MARISA_WORD_SIZE - 1))
+ / MARISA_WORD_SIZE);
+ num_units += num_units % (64 / MARISA_WORD_SIZE);
+ }
+
+ units_.resize(num_units);
+ if (num_units > 0) {
+ units_.back() = 0;
+ }
+
+ value_size_ = value_size;
+ if (value_size != 0) {
+ mask_ = MARISA_UINT32_MAX >> (32 - value_size);
+ }
+ size_ = values.size();
+
+ for (std::size_t i = 0; i < values.size(); ++i) {
+ set(i, values[i]);
+ }
+ }
+
+ void map_(Mapper &mapper) {
+ units_.map(mapper);
+ {
+ UInt32 temp_value_size;
+ mapper.map(&temp_value_size);
+ MARISA_THROW_IF(temp_value_size > 32, MARISA_FORMAT_ERROR);
+ value_size_ = temp_value_size;
+ }
+ {
+ UInt32 temp_mask;
+ mapper.map(&temp_mask);
+ mask_ = temp_mask;
+ }
+ {
+ UInt64 temp_size;
+ mapper.map(&temp_size);
+ MARISA_THROW_IF(temp_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+ size_ = (std::size_t)temp_size;
+ }
+ }
+
+ void read_(Reader &reader) {
+ units_.read(reader);
+ {
+ UInt32 temp_value_size;
+ reader.read(&temp_value_size);
+ MARISA_THROW_IF(temp_value_size > 32, MARISA_FORMAT_ERROR);
+ value_size_ = temp_value_size;
+ }
+ {
+ UInt32 temp_mask;
+ reader.read(&temp_mask);
+ mask_ = temp_mask;
+ }
+ {
+ UInt64 temp_size;
+ reader.read(&temp_size);
+ MARISA_THROW_IF(temp_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+ size_ = (std::size_t)temp_size;
+ }
+ }
+
+ void write_(Writer &writer) const {
+ units_.write(writer);
+ writer.write((UInt32)value_size_);
+ writer.write((UInt32)mask_);
+ writer.write((UInt64)size_);
+ }
+
+ void set(std::size_t i, UInt32 value) {
+ MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
+ MARISA_DEBUG_IF(value > mask_, MARISA_RANGE_ERROR);
+
+ const std::size_t pos = i * value_size_;
+ const std::size_t unit_id = pos / MARISA_WORD_SIZE;
+ const std::size_t unit_offset = pos % MARISA_WORD_SIZE;
+
+ units_[unit_id] &= ~((Unit)mask_ << unit_offset);
+ units_[unit_id] |= (Unit)(value & mask_) << unit_offset;
+ if ((unit_offset + value_size_) > MARISA_WORD_SIZE) {
+ units_[unit_id + 1] &=
+ ~((Unit)mask_ >> (MARISA_WORD_SIZE - unit_offset));
+ units_[unit_id + 1] |=
+ (Unit)(value & mask_) >> (MARISA_WORD_SIZE - unit_offset);
+ }
+ }
+
+ // Disallows copy and assignment.
+ FlatVector(const FlatVector &);
+ FlatVector &operator=(const FlatVector &);
+};
+
+} // namespace vector
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_VECTOR_FLAT_VECTOR_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/pop-count.h b/contrib/python/marisa-trie/marisa/grimoire/vector/pop-count.h
new file mode 100644
index 0000000000..6d04cf831d
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/vector/pop-count.h
@@ -0,0 +1,111 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_VECTOR_POP_COUNT_H_
+#define MARISA_GRIMOIRE_VECTOR_POP_COUNT_H_
+
+#include "../intrin.h"
+
+namespace marisa {
+namespace grimoire {
+namespace vector {
+
+#if MARISA_WORD_SIZE == 64
+
+class PopCount {
+ public:
+ explicit PopCount(UInt64 x) : value_() {
+ x = (x & 0x5555555555555555ULL) + ((x & 0xAAAAAAAAAAAAAAAAULL) >> 1);
+ x = (x & 0x3333333333333333ULL) + ((x & 0xCCCCCCCCCCCCCCCCULL) >> 2);
+ x = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x & 0xF0F0F0F0F0F0F0F0ULL) >> 4);
+ x *= 0x0101010101010101ULL;
+ value_ = x;
+ }
+
+ std::size_t lo8() const {
+ return (std::size_t)(value_ & 0xFFU);
+ }
+ std::size_t lo16() const {
+ return (std::size_t)((value_ >> 8) & 0xFFU);
+ }
+ std::size_t lo24() const {
+ return (std::size_t)((value_ >> 16) & 0xFFU);
+ }
+ std::size_t lo32() const {
+ return (std::size_t)((value_ >> 24) & 0xFFU);
+ }
+ std::size_t lo40() const {
+ return (std::size_t)((value_ >> 32) & 0xFFU);
+ }
+ std::size_t lo48() const {
+ return (std::size_t)((value_ >> 40) & 0xFFU);
+ }
+ std::size_t lo56() const {
+ return (std::size_t)((value_ >> 48) & 0xFFU);
+ }
+ std::size_t lo64() const {
+ return (std::size_t)((value_ >> 56) & 0xFFU);
+ }
+
+ static std::size_t count(UInt64 x) {
+#if defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
+ #ifdef _MSC_VER
+ return __popcnt64(x);
+ #else // _MSC_VER
+ return _mm_popcnt_u64(x);
+ #endif // _MSC_VER
+#else // defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
+ return PopCount(x).lo64();
+#endif // defined(MARISA_X64) && defined(MARISA_USE_POPCNT)
+ }
+
+ private:
+ UInt64 value_;
+};
+
+#else // MARISA_WORD_SIZE == 64
+
+class PopCount {
+ public:
+ explicit PopCount(UInt32 x) : value_() {
+ x = (x & 0x55555555U) + ((x & 0xAAAAAAAAU) >> 1);
+ x = (x & 0x33333333U) + ((x & 0xCCCCCCCCU) >> 2);
+ x = (x & 0x0F0F0F0FU) + ((x & 0xF0F0F0F0U) >> 4);
+ x *= 0x01010101U;
+ value_ = x;
+ }
+
+ std::size_t lo8() const {
+ return value_ & 0xFFU;
+ }
+ std::size_t lo16() const {
+ return (value_ >> 8) & 0xFFU;
+ }
+ std::size_t lo24() const {
+ return (value_ >> 16) & 0xFFU;
+ }
+ std::size_t lo32() const {
+ return (value_ >> 24) & 0xFFU;
+ }
+
+ static std::size_t count(UInt32 x) {
+#ifdef MARISA_USE_POPCNT
+ #ifdef _MSC_VER
+ return __popcnt(x);
+ #else // _MSC_VER
+ return _mm_popcnt_u32(x);
+ #endif // _MSC_VER
+#else // MARISA_USE_POPCNT
+ return PopCount(x).lo32();
+#endif // MARISA_USE_POPCNT
+ }
+
+ private:
+ UInt32 value_;
+};
+
+#endif // MARISA_WORD_SIZE == 64
+
+} // namespace vector
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_VECTOR_POP_COUNT_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/rank-index.h b/contrib/python/marisa-trie/marisa/grimoire/vector/rank-index.h
new file mode 100644
index 0000000000..2403709954
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/vector/rank-index.h
@@ -0,0 +1,83 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_VECTOR_RANK_INDEX_H_
+#define MARISA_GRIMOIRE_VECTOR_RANK_INDEX_H_
+
+#include "../../base.h"
+
+namespace marisa {
+namespace grimoire {
+namespace vector {
+
+class RankIndex {
+ public:
+ RankIndex() : abs_(0), rel_lo_(0), rel_hi_(0) {}
+
+ void set_abs(std::size_t value) {
+ MARISA_DEBUG_IF(value > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ abs_ = (UInt32)value;
+ }
+ void set_rel1(std::size_t value) {
+ MARISA_DEBUG_IF(value > 64, MARISA_RANGE_ERROR);
+ rel_lo_ = (UInt32)((rel_lo_ & ~0x7FU) | (value & 0x7FU));
+ }
+ void set_rel2(std::size_t value) {
+ MARISA_DEBUG_IF(value > 128, MARISA_RANGE_ERROR);
+ rel_lo_ = (UInt32)((rel_lo_ & ~(0xFFU << 7)) | ((value & 0xFFU) << 7));
+ }
+ void set_rel3(std::size_t value) {
+ MARISA_DEBUG_IF(value > 192, MARISA_RANGE_ERROR);
+ rel_lo_ = (UInt32)((rel_lo_ & ~(0xFFU << 15)) | ((value & 0xFFU) << 15));
+ }
+ void set_rel4(std::size_t value) {
+ MARISA_DEBUG_IF(value > 256, MARISA_RANGE_ERROR);
+ rel_lo_ = (UInt32)((rel_lo_ & ~(0x1FFU << 23)) | ((value & 0x1FFU) << 23));
+ }
+ void set_rel5(std::size_t value) {
+ MARISA_DEBUG_IF(value > 320, MARISA_RANGE_ERROR);
+ rel_hi_ = (UInt32)((rel_hi_ & ~0x1FFU) | (value & 0x1FFU));
+ }
+ void set_rel6(std::size_t value) {
+ MARISA_DEBUG_IF(value > 384, MARISA_RANGE_ERROR);
+ rel_hi_ = (UInt32)((rel_hi_ & ~(0x1FFU << 9)) | ((value & 0x1FFU) << 9));
+ }
+ void set_rel7(std::size_t value) {
+ MARISA_DEBUG_IF(value > 448, MARISA_RANGE_ERROR);
+ rel_hi_ = (UInt32)((rel_hi_ & ~(0x1FFU << 18)) | ((value & 0x1FFU) << 18));
+ }
+
+ std::size_t abs() const {
+ return abs_;
+ }
+ std::size_t rel1() const {
+ return rel_lo_ & 0x7FU;
+ }
+ std::size_t rel2() const {
+ return (rel_lo_ >> 7) & 0xFFU;
+ }
+ std::size_t rel3() const {
+ return (rel_lo_ >> 15) & 0xFFU;
+ }
+ std::size_t rel4() const {
+ return (rel_lo_ >> 23) & 0x1FFU;
+ }
+ std::size_t rel5() const {
+ return rel_hi_ & 0x1FFU;
+ }
+ std::size_t rel6() const {
+ return (rel_hi_ >> 9) & 0x1FFU;
+ }
+ std::size_t rel7() const {
+ return (rel_hi_ >> 18) & 0x1FFU;
+ }
+
+ private:
+ UInt32 abs_;
+ UInt32 rel_lo_;
+ UInt32 rel_hi_;
+};
+
+} // namespace vector
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_VECTOR_RANK_INDEX_H_
diff --git a/contrib/python/marisa-trie/marisa/grimoire/vector/vector.h b/contrib/python/marisa-trie/marisa/grimoire/vector/vector.h
new file mode 100644
index 0000000000..148cc8b491
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/grimoire/vector/vector.h
@@ -0,0 +1,257 @@
+#pragma once
+#ifndef MARISA_GRIMOIRE_VECTOR_VECTOR_H_
+#define MARISA_GRIMOIRE_VECTOR_VECTOR_H_
+
+#include <new>
+
+#include "../io.h"
+
+namespace marisa {
+namespace grimoire {
+namespace vector {
+
+template <typename T>
+class Vector {
+ public:
+ Vector()
+ : buf_(), objs_(NULL), const_objs_(NULL),
+ size_(0), capacity_(0), fixed_(false) {}
+ ~Vector() {
+ if (objs_ != NULL) {
+ for (std::size_t i = 0; i < size_; ++i) {
+ objs_[i].~T();
+ }
+ }
+ }
+
+ void map(Mapper &mapper) {
+ Vector temp;
+ temp.map_(mapper);
+ swap(temp);
+ }
+
+ void read(Reader &reader) {
+ Vector temp;
+ temp.read_(reader);
+ swap(temp);
+ }
+
+ void write(Writer &writer) const {
+ write_(writer);
+ }
+
+ void push_back(const T &x) {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(size_ == max_size(), MARISA_SIZE_ERROR);
+ reserve(size_ + 1);
+ new (&objs_[size_]) T(x);
+ ++size_;
+ }
+
+ void pop_back() {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
+ objs_[--size_].~T();
+ }
+
+ // resize() assumes that T's placement new does not throw an exception.
+ void resize(std::size_t size) {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ reserve(size);
+ for (std::size_t i = size_; i < size; ++i) {
+ new (&objs_[i]) T;
+ }
+ for (std::size_t i = size; i < size_; ++i) {
+ objs_[i].~T();
+ }
+ size_ = size;
+ }
+
+ // resize() assumes that T's placement new does not throw an exception.
+ void resize(std::size_t size, const T &x) {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ reserve(size);
+ for (std::size_t i = size_; i < size; ++i) {
+ new (&objs_[i]) T(x);
+ }
+ for (std::size_t i = size; i < size_; ++i) {
+ objs_[i].~T();
+ }
+ size_ = size;
+ }
+
+ void reserve(std::size_t capacity) {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ if (capacity <= capacity_) {
+ return;
+ }
+ MARISA_DEBUG_IF(capacity > max_size(), MARISA_SIZE_ERROR);
+ std::size_t new_capacity = capacity;
+ if (capacity_ > (capacity / 2)) {
+ if (capacity_ > (max_size() / 2)) {
+ new_capacity = max_size();
+ } else {
+ new_capacity = capacity_ * 2;
+ }
+ }
+ realloc(new_capacity);
+ }
+
+ void shrink() {
+ MARISA_THROW_IF(fixed_, MARISA_STATE_ERROR);
+ if (size_ != capacity_) {
+ realloc(size_);
+ }
+ }
+
+ void fix() {
+ MARISA_THROW_IF(fixed_, MARISA_STATE_ERROR);
+ fixed_ = true;
+ }
+
+ const T *begin() const {
+ return const_objs_;
+ }
+ const T *end() const {
+ return const_objs_ + size_;
+ }
+ const T &operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
+ return const_objs_[i];
+ }
+ const T &front() const {
+ MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
+ return const_objs_[0];
+ }
+ const T &back() const {
+ MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
+ return const_objs_[size_ - 1];
+ }
+
+ T *begin() {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ return objs_;
+ }
+ T *end() {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ return objs_ + size_;
+ }
+ T &operator[](std::size_t i) {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
+ return objs_[i];
+ }
+ T &front() {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
+ return objs_[0];
+ }
+ T &back() {
+ MARISA_DEBUG_IF(fixed_, MARISA_STATE_ERROR);
+ MARISA_DEBUG_IF(size_ == 0, MARISA_STATE_ERROR);
+ return objs_[size_ - 1];
+ }
+
+ std::size_t size() const {
+ return size_;
+ }
+ std::size_t capacity() const {
+ return capacity_;
+ }
+ bool fixed() const {
+ return fixed_;
+ }
+
+ bool empty() const {
+ return size_ == 0;
+ }
+ std::size_t total_size() const {
+ return sizeof(T) * size_;
+ }
+ std::size_t io_size() const {
+ return sizeof(UInt64) + ((total_size() + 7) & ~(std::size_t)0x07);
+ }
+
+ void clear() {
+ Vector().swap(*this);
+ }
+ void swap(Vector &rhs) {
+ buf_.swap(rhs.buf_);
+ marisa::swap(objs_, rhs.objs_);
+ marisa::swap(const_objs_, rhs.const_objs_);
+ marisa::swap(size_, rhs.size_);
+ marisa::swap(capacity_, rhs.capacity_);
+ marisa::swap(fixed_, rhs.fixed_);
+ }
+
+ static std::size_t max_size() {
+ return MARISA_SIZE_MAX / sizeof(T);
+ }
+
+ private:
+ scoped_array<char> buf_;
+ T *objs_;
+ const T *const_objs_;
+ std::size_t size_;
+ std::size_t capacity_;
+ bool fixed_;
+
+ void map_(Mapper &mapper) {
+ UInt64 total_size;
+ mapper.map(&total_size);
+ MARISA_THROW_IF(total_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+ MARISA_THROW_IF((total_size % sizeof(T)) != 0, MARISA_FORMAT_ERROR);
+ const std::size_t size = (std::size_t)(total_size / sizeof(T));
+ mapper.map(&const_objs_, size);
+ mapper.seek((std::size_t)((8 - (total_size % 8)) % 8));
+ size_ = size;
+ fix();
+ }
+ void read_(Reader &reader) {
+ UInt64 total_size;
+ reader.read(&total_size);
+ MARISA_THROW_IF(total_size > MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+ MARISA_THROW_IF((total_size % sizeof(T)) != 0, MARISA_FORMAT_ERROR);
+ const std::size_t size = (std::size_t)(total_size / sizeof(T));
+ resize(size);
+ reader.read(objs_, size);
+ reader.seek((std::size_t)((8 - (total_size % 8)) % 8));
+ }
+ void write_(Writer &writer) const {
+ writer.write((UInt64)total_size());
+ writer.write(const_objs_, size_);
+ writer.seek((8 - (total_size() % 8)) % 8);
+ }
+
+ // realloc() assumes that T's placement new does not throw an exception.
+ void realloc(std::size_t new_capacity) {
+ MARISA_DEBUG_IF(new_capacity > max_size(), MARISA_SIZE_ERROR);
+
+ scoped_array<char> new_buf(
+ new (std::nothrow) char[sizeof(T) * new_capacity]);
+ MARISA_DEBUG_IF(new_buf.get() == NULL, MARISA_MEMORY_ERROR);
+ T *new_objs = reinterpret_cast<T *>(new_buf.get());
+
+ for (std::size_t i = 0; i < size_; ++i) {
+ new (&new_objs[i]) T(objs_[i]);
+ }
+ for (std::size_t i = 0; i < size_; ++i) {
+ objs_[i].~T();
+ }
+
+ buf_.swap(new_buf);
+ objs_ = new_objs;
+ const_objs_ = new_objs;
+ capacity_ = new_capacity;
+ }
+
+ // Disallows copy and assignment.
+ Vector(const Vector &);
+ Vector &operator=(const Vector &);
+};
+
+} // namespace vector
+} // namespace grimoire
+} // namespace marisa
+
+#endif // MARISA_GRIMOIRE_VECTOR_VECTOR_H_
diff --git a/contrib/python/marisa-trie/marisa/iostream.h b/contrib/python/marisa-trie/marisa/iostream.h
new file mode 100644
index 0000000000..da5ec77a6c
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/iostream.h
@@ -0,0 +1,19 @@
+#pragma once
+#ifndef MARISA_IOSTREAM_H_
+#define MARISA_IOSTREAM_H_
+
+#include <iosfwd>
+
+namespace marisa {
+
+class Trie;
+
+std::istream &read(std::istream &stream, Trie *trie);
+std::ostream &write(std::ostream &stream, const Trie &trie);
+
+std::istream &operator>>(std::istream &stream, Trie &trie);
+std::ostream &operator<<(std::ostream &stream, const Trie &trie);
+
+} // namespace marisa
+
+#endif // MARISA_IOSTREAM_H_
diff --git a/contrib/python/marisa-trie/marisa/key.h b/contrib/python/marisa-trie/marisa/key.h
new file mode 100644
index 0000000000..48e03226c4
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/key.h
@@ -0,0 +1,86 @@
+#pragma once
+#ifndef MARISA_KEY_H_
+#define MARISA_KEY_H_
+
+#include "base.h"
+
+namespace marisa {
+
+class Key {
+ public:
+ Key() : ptr_(NULL), length_(0), union_() {
+ union_.id = 0;
+ }
+ Key(const Key &key)
+ : ptr_(key.ptr_), length_(key.length_), union_(key.union_) {}
+
+ Key &operator=(const Key &key) {
+ ptr_ = key.ptr_;
+ length_ = key.length_;
+ union_ = key.union_;
+ return *this;
+ }
+
+ char operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
+ return ptr_[i];
+ }
+
+ void set_str(const char *str) {
+ MARISA_DEBUG_IF(str == NULL, MARISA_NULL_ERROR);
+ std::size_t length = 0;
+ while (str[length] != '\0') {
+ ++length;
+ }
+ MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ ptr_ = str;
+ length_ = (UInt32)length;
+ }
+ void set_str(const char *ptr, std::size_t length) {
+ MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
+ MARISA_DEBUG_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ ptr_ = ptr;
+ length_ = (UInt32)length;
+ }
+ void set_id(std::size_t id) {
+ MARISA_DEBUG_IF(id > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+ union_.id = (UInt32)id;
+ }
+ void set_weight(float weight) {
+ union_.weight = weight;
+ }
+
+ const char *ptr() const {
+ return ptr_;
+ }
+ std::size_t length() const {
+ return length_;
+ }
+ std::size_t id() const {
+ return union_.id;
+ }
+ float weight() const {
+ return union_.weight;
+ }
+
+ void clear() {
+ Key().swap(*this);
+ }
+ void swap(Key &rhs) {
+ marisa::swap(ptr_, rhs.ptr_);
+ marisa::swap(length_, rhs.length_);
+ marisa::swap(union_.id, rhs.union_.id);
+ }
+
+ private:
+ const char *ptr_;
+ UInt32 length_;
+ union Union {
+ UInt32 id;
+ float weight;
+ } union_;
+};
+
+} // namespace marisa
+
+#endif // MARISA_KEY_H_
diff --git a/contrib/python/marisa-trie/marisa/keyset.cc b/contrib/python/marisa-trie/marisa/keyset.cc
new file mode 100644
index 0000000000..adb82b31fe
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/keyset.cc
@@ -0,0 +1,181 @@
+#include <new>
+
+#include "keyset.h"
+
+namespace marisa {
+
+Keyset::Keyset()
+ : base_blocks_(), base_blocks_size_(0), base_blocks_capacity_(0),
+ extra_blocks_(), extra_blocks_size_(0), extra_blocks_capacity_(0),
+ key_blocks_(), key_blocks_size_(0), key_blocks_capacity_(0),
+ ptr_(NULL), avail_(0), size_(0), total_length_(0) {}
+
+void Keyset::push_back(const Key &key) {
+ MARISA_DEBUG_IF(size_ == MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+
+ char * const key_ptr = reserve(key.length());
+ for (std::size_t i = 0; i < key.length(); ++i) {
+ key_ptr[i] = key[i];
+ }
+
+ Key &new_key = key_blocks_[size_ / KEY_BLOCK_SIZE][size_ % KEY_BLOCK_SIZE];
+ new_key.set_str(key_ptr, key.length());
+ new_key.set_id(key.id());
+ ++size_;
+ total_length_ += new_key.length();
+}
+
+void Keyset::push_back(const Key &key, char end_marker) {
+ MARISA_DEBUG_IF(size_ == MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+
+ if ((size_ / KEY_BLOCK_SIZE) == key_blocks_size_) {
+ append_key_block();
+ }
+
+ char * const key_ptr = reserve(key.length() + 1);
+ for (std::size_t i = 0; i < key.length(); ++i) {
+ key_ptr[i] = key[i];
+ }
+ key_ptr[key.length()] = end_marker;
+
+ Key &new_key = key_blocks_[size_ / KEY_BLOCK_SIZE][size_ % KEY_BLOCK_SIZE];
+ new_key.set_str(key_ptr, key.length());
+ new_key.set_id(key.id());
+ ++size_;
+ total_length_ += new_key.length();
+}
+
+void Keyset::push_back(const char *str) {
+ MARISA_DEBUG_IF(size_ == MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+ MARISA_THROW_IF(str == NULL, MARISA_NULL_ERROR);
+
+ std::size_t length = 0;
+ while (str[length] != '\0') {
+ ++length;
+ }
+ push_back(str, length);
+}
+
+void Keyset::push_back(const char *ptr, std::size_t length, float weight) {
+ MARISA_DEBUG_IF(size_ == MARISA_SIZE_MAX, MARISA_SIZE_ERROR);
+ MARISA_THROW_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
+ MARISA_THROW_IF(length > MARISA_UINT32_MAX, MARISA_SIZE_ERROR);
+
+ char * const key_ptr = reserve(length);
+ for (std::size_t i = 0; i < length; ++i) {
+ key_ptr[i] = ptr[i];
+ }
+
+ Key &key = key_blocks_[size_ / KEY_BLOCK_SIZE][size_ % KEY_BLOCK_SIZE];
+ key.set_str(key_ptr, length);
+ key.set_weight(weight);
+ ++size_;
+ total_length_ += length;
+}
+
+void Keyset::reset() {
+ base_blocks_size_ = 0;
+ extra_blocks_size_ = 0;
+ ptr_ = NULL;
+ avail_ = 0;
+ size_ = 0;
+ total_length_ = 0;
+}
+
+void Keyset::clear() {
+ Keyset().swap(*this);
+}
+
+void Keyset::swap(Keyset &rhs) {
+ base_blocks_.swap(rhs.base_blocks_);
+ marisa::swap(base_blocks_size_, rhs.base_blocks_size_);
+ marisa::swap(base_blocks_capacity_, rhs.base_blocks_capacity_);
+ extra_blocks_.swap(rhs.extra_blocks_);
+ marisa::swap(extra_blocks_size_, rhs.extra_blocks_size_);
+ marisa::swap(extra_blocks_capacity_, rhs.extra_blocks_capacity_);
+ key_blocks_.swap(rhs.key_blocks_);
+ marisa::swap(key_blocks_size_, rhs.key_blocks_size_);
+ marisa::swap(key_blocks_capacity_, rhs.key_blocks_capacity_);
+ marisa::swap(ptr_, rhs.ptr_);
+ marisa::swap(avail_, rhs.avail_);
+ marisa::swap(size_, rhs.size_);
+ marisa::swap(total_length_, rhs.total_length_);
+}
+
+char *Keyset::reserve(std::size_t size) {
+ if ((size_ / KEY_BLOCK_SIZE) == key_blocks_size_) {
+ append_key_block();
+ }
+
+ if (size > EXTRA_BLOCK_SIZE) {
+ append_extra_block(size);
+ return extra_blocks_[extra_blocks_size_ - 1].get();
+ } else {
+ if (size > avail_) {
+ append_base_block();
+ }
+ ptr_ += size;
+ avail_ -= size;
+ return ptr_ - size;
+ }
+}
+
+void Keyset::append_base_block() {
+ if (base_blocks_size_ == base_blocks_capacity_) {
+ const std::size_t new_capacity =
+ (base_blocks_size_ != 0) ? (base_blocks_size_ * 2) : 1;
+ scoped_array<scoped_array<char> > new_blocks(
+ new (std::nothrow) scoped_array<char>[new_capacity]);
+ MARISA_THROW_IF(new_blocks.get() == NULL, MARISA_MEMORY_ERROR);
+ for (std::size_t i = 0; i < base_blocks_size_; ++i) {
+ base_blocks_[i].swap(new_blocks[i]);
+ }
+ base_blocks_.swap(new_blocks);
+ base_blocks_capacity_ = new_capacity;
+ }
+ if (base_blocks_[base_blocks_size_].get() == NULL) {
+ scoped_array<char> new_block(new (std::nothrow) char[BASE_BLOCK_SIZE]);
+ MARISA_THROW_IF(new_block.get() == NULL, MARISA_MEMORY_ERROR);
+ base_blocks_[base_blocks_size_].swap(new_block);
+ }
+ ptr_ = base_blocks_[base_blocks_size_++].get();
+ avail_ = BASE_BLOCK_SIZE;
+}
+
+void Keyset::append_extra_block(std::size_t size) {
+ if (extra_blocks_size_ == extra_blocks_capacity_) {
+ const std::size_t new_capacity =
+ (extra_blocks_size_ != 0) ? (extra_blocks_size_ * 2) : 1;
+ scoped_array<scoped_array<char> > new_blocks(
+ new (std::nothrow) scoped_array<char>[new_capacity]);
+ MARISA_THROW_IF(new_blocks.get() == NULL, MARISA_MEMORY_ERROR);
+ for (std::size_t i = 0; i < extra_blocks_size_; ++i) {
+ extra_blocks_[i].swap(new_blocks[i]);
+ }
+ extra_blocks_.swap(new_blocks);
+ extra_blocks_capacity_ = new_capacity;
+ }
+ scoped_array<char> new_block(new (std::nothrow) char[size]);
+ MARISA_THROW_IF(new_block.get() == NULL, MARISA_MEMORY_ERROR);
+ extra_blocks_[extra_blocks_size_++].swap(new_block);
+}
+
+void Keyset::append_key_block() {
+ if (key_blocks_size_ == key_blocks_capacity_) {
+ const std::size_t new_capacity =
+ (key_blocks_size_ != 0) ? (key_blocks_size_ * 2) : 1;
+ scoped_array<scoped_array<Key> > new_blocks(
+ new (std::nothrow) scoped_array<Key>[new_capacity]);
+ MARISA_THROW_IF(new_blocks.get() == NULL, MARISA_MEMORY_ERROR);
+ for (std::size_t i = 0; i < key_blocks_size_; ++i) {
+ key_blocks_[i].swap(new_blocks[i]);
+ }
+ key_blocks_.swap(new_blocks);
+ key_blocks_capacity_ = new_capacity;
+ }
+ scoped_array<Key> new_block(new (std::nothrow) Key[KEY_BLOCK_SIZE]);
+ MARISA_THROW_IF(new_block.get() == NULL, MARISA_MEMORY_ERROR);
+ key_blocks_[key_blocks_size_++].swap(new_block);
+}
+
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/keyset.h b/contrib/python/marisa-trie/marisa/keyset.h
new file mode 100644
index 0000000000..86762dba47
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/keyset.h
@@ -0,0 +1,81 @@
+#pragma once
+#ifndef MARISA_KEYSET_H_
+#define MARISA_KEYSET_H_
+
+#include "key.h"
+
+namespace marisa {
+
+class Keyset {
+ public:
+ enum {
+ BASE_BLOCK_SIZE = 4096,
+ EXTRA_BLOCK_SIZE = 1024,
+ KEY_BLOCK_SIZE = 256
+ };
+
+ Keyset();
+
+ void push_back(const Key &key);
+ void push_back(const Key &key, char end_marker);
+
+ void push_back(const char *str);
+ void push_back(const char *ptr, std::size_t length, float weight = 1.0);
+
+ const Key &operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
+ return key_blocks_[i / KEY_BLOCK_SIZE][i % KEY_BLOCK_SIZE];
+ }
+ Key &operator[](std::size_t i) {
+ MARISA_DEBUG_IF(i >= size_, MARISA_BOUND_ERROR);
+ return key_blocks_[i / KEY_BLOCK_SIZE][i % KEY_BLOCK_SIZE];
+ }
+
+ std::size_t num_keys() const {
+ return size_;
+ }
+
+ bool empty() const {
+ return size_ == 0;
+ }
+ std::size_t size() const {
+ return size_;
+ }
+ std::size_t total_length() const {
+ return total_length_;
+ }
+
+ void reset();
+
+ void clear();
+ void swap(Keyset &rhs);
+
+ private:
+ scoped_array<scoped_array<char> > base_blocks_;
+ std::size_t base_blocks_size_;
+ std::size_t base_blocks_capacity_;
+ scoped_array<scoped_array<char> > extra_blocks_;
+ std::size_t extra_blocks_size_;
+ std::size_t extra_blocks_capacity_;
+ scoped_array<scoped_array<Key> > key_blocks_;
+ std::size_t key_blocks_size_;
+ std::size_t key_blocks_capacity_;
+ char *ptr_;
+ std::size_t avail_;
+ std::size_t size_;
+ std::size_t total_length_;
+
+ char *reserve(std::size_t size);
+
+ void append_base_block();
+ void append_extra_block(std::size_t size);
+ void append_key_block();
+
+ // Disallows copy and assignment.
+ Keyset(const Keyset &);
+ Keyset &operator=(const Keyset &);
+};
+
+} // namespace marisa
+
+#endif // MARISA_KEYSET_H_
diff --git a/contrib/python/marisa-trie/marisa/query.h b/contrib/python/marisa-trie/marisa/query.h
new file mode 100644
index 0000000000..e08f8f72dc
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/query.h
@@ -0,0 +1,72 @@
+#pragma once
+#ifndef MARISA_QUERY_H_
+#define MARISA_QUERY_H_
+
+#include "base.h"
+
+namespace marisa {
+
+class Query {
+ public:
+ Query() : ptr_(NULL), length_(0), id_(0) {}
+ Query(const Query &query)
+ : ptr_(query.ptr_), length_(query.length_), id_(query.id_) {}
+
+ Query &operator=(const Query &query) {
+ ptr_ = query.ptr_;
+ length_ = query.length_;
+ id_ = query.id_;
+ return *this;
+ }
+
+ char operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(i >= length_, MARISA_BOUND_ERROR);
+ return ptr_[i];
+ }
+
+ void set_str(const char *str) {
+ MARISA_DEBUG_IF(str == NULL, MARISA_NULL_ERROR);
+ std::size_t length = 0;
+ while (str[length] != '\0') {
+ ++length;
+ }
+ ptr_ = str;
+ length_ = length;
+ }
+ void set_str(const char *ptr, std::size_t length) {
+ MARISA_DEBUG_IF((ptr == NULL) && (length != 0), MARISA_NULL_ERROR);
+ ptr_ = ptr;
+ length_ = length;
+ }
+ void set_id(std::size_t id) {
+ id_ = id;
+ }
+
+ const char *ptr() const {
+ return ptr_;
+ }
+ std::size_t length() const {
+ return length_;
+ }
+ std::size_t id() const {
+ return id_;
+ }
+
+ void clear() {
+ Query().swap(*this);
+ }
+ void swap(Query &rhs) {
+ marisa::swap(ptr_, rhs.ptr_);
+ marisa::swap(length_, rhs.length_);
+ marisa::swap(id_, rhs.id_);
+ }
+
+ private:
+ const char *ptr_;
+ std::size_t length_;
+ std::size_t id_;
+};
+
+} // namespace marisa
+
+#endif // MARISA_QUERY_H_
diff --git a/contrib/python/marisa-trie/marisa/scoped-array.h b/contrib/python/marisa-trie/marisa/scoped-array.h
new file mode 100644
index 0000000000..210cb908a7
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/scoped-array.h
@@ -0,0 +1,49 @@
+#pragma once
+#ifndef MARISA_SCOPED_ARRAY_H_
+#define MARISA_SCOPED_ARRAY_H_
+
+#include "base.h"
+
+namespace marisa {
+
+template <typename T>
+class scoped_array {
+ public:
+ scoped_array() : array_(NULL) {}
+ explicit scoped_array(T *array) : array_(array) {}
+
+ ~scoped_array() {
+ delete [] array_;
+ }
+
+ void reset(T *array = NULL) {
+ MARISA_THROW_IF((array != NULL) && (array == array_), MARISA_RESET_ERROR);
+ scoped_array(array).swap(*this);
+ }
+
+ T &operator[](std::size_t i) const {
+ MARISA_DEBUG_IF(array_ == NULL, MARISA_STATE_ERROR);
+ return array_[i];
+ }
+ T *get() const {
+ return array_;
+ }
+
+ void clear() {
+ scoped_array().swap(*this);
+ }
+ void swap(scoped_array &rhs) {
+ marisa::swap(array_, rhs.array_);
+ }
+
+ private:
+ T *array_;
+
+ // Disallows copy and assignment.
+ scoped_array(const scoped_array &);
+ scoped_array &operator=(const scoped_array &);
+};
+
+} // namespace marisa
+
+#endif // MARISA_SCOPED_ARRAY_H_
diff --git a/contrib/python/marisa-trie/marisa/scoped-ptr.h b/contrib/python/marisa-trie/marisa/scoped-ptr.h
new file mode 100644
index 0000000000..9a9c447353
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/scoped-ptr.h
@@ -0,0 +1,53 @@
+#pragma once
+#ifndef MARISA_SCOPED_PTR_H_
+#define MARISA_SCOPED_PTR_H_
+
+#include "base.h"
+
+namespace marisa {
+
+template <typename T>
+class scoped_ptr {
+ public:
+ scoped_ptr() : ptr_(NULL) {}
+ explicit scoped_ptr(T *ptr) : ptr_(ptr) {}
+
+ ~scoped_ptr() {
+ delete ptr_;
+ }
+
+ void reset(T *ptr = NULL) {
+ MARISA_THROW_IF((ptr != NULL) && (ptr == ptr_), MARISA_RESET_ERROR);
+ scoped_ptr(ptr).swap(*this);
+ }
+
+ T &operator*() const {
+ MARISA_DEBUG_IF(ptr_ == NULL, MARISA_STATE_ERROR);
+ return *ptr_;
+ }
+ T *operator->() const {
+ MARISA_DEBUG_IF(ptr_ == NULL, MARISA_STATE_ERROR);
+ return ptr_;
+ }
+ T *get() const {
+ return ptr_;
+ }
+
+ void clear() {
+ scoped_ptr().swap(*this);
+ }
+ void swap(scoped_ptr &rhs) {
+ marisa::swap(ptr_, rhs.ptr_);
+ }
+
+ private:
+ T *ptr_;
+
+ // Disallows copy and assignment.
+ scoped_ptr(const scoped_ptr &);
+ scoped_ptr &operator=(const scoped_ptr &);
+};
+
+} // namespace marisa
+
+#endif // MARISA_SCOPED_PTR_H_
diff --git a/contrib/python/marisa-trie/marisa/stdio.h b/contrib/python/marisa-trie/marisa/stdio.h
new file mode 100644
index 0000000000..334ce56816
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/stdio.h
@@ -0,0 +1,16 @@
+#pragma once
+#ifndef MARISA_MYSTDIO_H_
+#define MARISA_MYSTDIO_H_
+
+#include <cstdio>
+
+namespace marisa {
+
+class Trie;
+
+void fread(std::FILE *file, Trie *trie);
+void fwrite(std::FILE *file, const Trie &trie);
+
+} // namespace marisa
+
+#endif // MARISA_MYSTDIO_H_
diff --git a/contrib/python/marisa-trie/marisa/trie.cc b/contrib/python/marisa-trie/marisa/trie.cc
new file mode 100644
index 0000000000..5baaf9b288
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/trie.cc
@@ -0,0 +1,249 @@
+#include "stdio.h"
+#include "iostream.h"
+#include "trie.h"
+#include "grimoire/trie.h"
+
+namespace marisa {
+
+Trie::Trie() : trie_() {}
+
+Trie::~Trie() {}
+
+void Trie::build(Keyset &keyset, int config_flags) {
+ scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
+ MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
+
+ temp->build(keyset, config_flags);
+ trie_.swap(temp);
+}
+
+void Trie::mmap(const char *filename) {
+ MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
+
+ scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
+ MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
+
+ grimoire::Mapper mapper;
+ mapper.open(filename);
+ temp->map(mapper);
+ trie_.swap(temp);
+}
+
+void Trie::map(const void *ptr, std::size_t size) {
+ MARISA_THROW_IF((ptr == NULL) && (size != 0), MARISA_NULL_ERROR);
+
+ scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
+ MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
+
+ grimoire::Mapper mapper;
+ mapper.open(ptr, size);
+ temp->map(mapper);
+ trie_.swap(temp);
+}
+
+void Trie::load(const char *filename) {
+ MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
+
+ scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
+ MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
+
+ grimoire::Reader reader;
+ reader.open(filename);
+ temp->read(reader);
+ trie_.swap(temp);
+}
+
+void Trie::read(int fd) {
+ MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
+
+ scoped_ptr<grimoire::LoudsTrie> temp(new (std::nothrow) grimoire::LoudsTrie);
+ MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
+
+ grimoire::Reader reader;
+ reader.open(fd);
+ temp->read(reader);
+ trie_.swap(temp);
+}
+
+void Trie::save(const char *filename) const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
+
+ grimoire::Writer writer;
+ writer.open(filename);
+ trie_->write(writer);
+}
+
+void Trie::write(int fd) const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
+
+ grimoire::Writer writer;
+ writer.open(fd);
+ trie_->write(writer);
+}
+
+bool Trie::lookup(Agent &agent) const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ if (!agent.has_state()) {
+ agent.init_state();
+ }
+ return trie_->lookup(agent);
+}
+
+void Trie::reverse_lookup(Agent &agent) const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ if (!agent.has_state()) {
+ agent.init_state();
+ }
+ trie_->reverse_lookup(agent);
+}
+
+bool Trie::common_prefix_search(Agent &agent) const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ if (!agent.has_state()) {
+ agent.init_state();
+ }
+ return trie_->common_prefix_search(agent);
+}
+
+bool Trie::predictive_search(Agent &agent) const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ if (!agent.has_state()) {
+ agent.init_state();
+ }
+ return trie_->predictive_search(agent);
+}
+
+std::size_t Trie::num_tries() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->num_tries();
+}
+
+std::size_t Trie::num_keys() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->num_keys();
+}
+
+std::size_t Trie::num_nodes() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->num_nodes();
+}
+
+TailMode Trie::tail_mode() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->tail_mode();
+}
+
+NodeOrder Trie::node_order() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->node_order();
+}
+
+bool Trie::empty() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->empty();
+}
+
+std::size_t Trie::size() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->size();
+}
+
+std::size_t Trie::total_size() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->total_size();
+}
+
+std::size_t Trie::io_size() const {
+ MARISA_THROW_IF(trie_.get() == NULL, MARISA_STATE_ERROR);
+ return trie_->io_size();
+}
+
+void Trie::clear() {
+ Trie().swap(*this);
+}
+
+void Trie::swap(Trie &rhs) {
+ trie_.swap(rhs.trie_);
+}
+
+} // namespace marisa
+
+#include <iostream>
+
+namespace marisa {
+
+class TrieIO {
+ public:
+ static void fread(std::FILE *file, Trie *trie) {
+ MARISA_THROW_IF(trie == NULL, MARISA_NULL_ERROR);
+
+ scoped_ptr<grimoire::LoudsTrie> temp(
+ new (std::nothrow) grimoire::LoudsTrie);
+ MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
+
+ grimoire::Reader reader;
+ reader.open(file);
+ temp->read(reader);
+ trie->trie_.swap(temp);
+ }
+ static void fwrite(std::FILE *file, const Trie &trie) {
+ MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
+ MARISA_THROW_IF(trie.trie_.get() == NULL, MARISA_STATE_ERROR);
+ grimoire::Writer writer;
+ writer.open(file);
+ trie.trie_->write(writer);
+ }
+
+ static std::istream &read(std::istream &stream, Trie *trie) {
+ MARISA_THROW_IF(trie == NULL, MARISA_NULL_ERROR);
+
+ scoped_ptr<grimoire::LoudsTrie> temp(
+ new (std::nothrow) grimoire::LoudsTrie);
+ MARISA_THROW_IF(temp.get() == NULL, MARISA_MEMORY_ERROR);
+
+ grimoire::Reader reader;
+ reader.open(stream);
+ temp->read(reader);
+ trie->trie_.swap(temp);
+ return stream;
+ }
+ static std::ostream &write(std::ostream &stream, const Trie &trie) {
+ MARISA_THROW_IF(trie.trie_.get() == NULL, MARISA_STATE_ERROR);
+ grimoire::Writer writer;
+ writer.open(stream);
+ trie.trie_->write(writer);
+ return stream;
+ }
+};
+
+void fread(std::FILE *file, Trie *trie) {
+ MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
+ MARISA_THROW_IF(trie == NULL, MARISA_NULL_ERROR);
+ TrieIO::fread(file, trie);
+}
+
+void fwrite(std::FILE *file, const Trie &trie) {
+ MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
+ TrieIO::fwrite(file, trie);
+}
+
+std::istream &read(std::istream &stream, Trie *trie) {
+ MARISA_THROW_IF(trie == NULL, MARISA_NULL_ERROR);
+ return TrieIO::read(stream, trie);
+}
+
+std::ostream &write(std::ostream &stream, const Trie &trie) {
+ return TrieIO::write(stream, trie);
+}
+
+std::istream &operator>>(std::istream &stream, Trie &trie) {
+ return read(stream, &trie);
+}
+
+std::ostream &operator<<(std::ostream &stream, const Trie &trie) {
+ return write(stream, trie);
+}
+
+} // namespace marisa
diff --git a/contrib/python/marisa-trie/marisa/trie.h b/contrib/python/marisa-trie/marisa/trie.h
new file mode 100644
index 0000000000..df85bd86ba
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa/trie.h
@@ -0,0 +1,65 @@
+#pragma once
+#ifndef MARISA_TRIE_H_
+#define MARISA_TRIE_H_
+
+#include "keyset.h"
+#include "agent.h"
+
+namespace marisa {
+namespace grimoire {
+namespace trie {
+
+class LoudsTrie;
+
+} // namespace trie
+} // namespace grimoire
+
+class Trie {
+ friend class TrieIO;
+
+ public:
+ Trie();
+ ~Trie();
+
+ void build(Keyset &keyset, int config_flags = 0);
+
+ void mmap(const char *filename);
+ void map(const void *ptr, std::size_t size);
+
+ void load(const char *filename);
+ void read(int fd);
+
+ void save(const char *filename) const;
+ void write(int fd) const;
+
+ bool lookup(Agent &agent) const;
+ void reverse_lookup(Agent &agent) const;
+ bool common_prefix_search(Agent &agent) const;
+ bool predictive_search(Agent &agent) const;
+
+ std::size_t num_tries() const;
+ std::size_t num_keys() const;
+ std::size_t num_nodes() const;
+
+ TailMode tail_mode() const;
+ NodeOrder node_order() const;
+
+ bool empty() const;
+ std::size_t size() const;
+ std::size_t total_size() const;
+ std::size_t io_size() const;
+
+ void clear();
+ void swap(Trie &rhs);
+
+ private:
+ scoped_ptr<grimoire::trie::LoudsTrie> trie_;
+
+ // Disallows copy and assignment.
+ Trie(const Trie &);
+ Trie &operator=(const Trie &);
+};
+
+} // namespace marisa
+
+#endif // MARISA_TRIE_H_
diff --git a/contrib/python/marisa-trie/marisa_trie.pyx b/contrib/python/marisa-trie/marisa_trie.pyx
new file mode 100644
index 0000000000..f9fe6f331b
--- /dev/null
+++ b/contrib/python/marisa-trie/marisa_trie.pyx
@@ -0,0 +1,763 @@
+# cython: profile=False, embedsignature=True
+
+from __future__ import unicode_literals
+
+from std_iostream cimport stringstream, istream, ostream
+from libc.string cimport strncmp
+cimport keyset
+cimport key
+cimport agent
+cimport trie
+cimport iostream
+cimport base
+
+import itertools
+import struct
+import warnings
+
+try:
+ from itertools import izip
+except ImportError:
+ izip = zip
+
+
+DEFAULT_CACHE = base.MARISA_DEFAULT_CACHE
+HUGE_CACHE = base.MARISA_HUGE_CACHE
+LARGE_CACHE = base.MARISA_LARGE_CACHE
+NORMAL_CACHE = base.MARISA_NORMAL_CACHE
+SMALL_CACHE = base.MARISA_SMALL_CACHE
+TINY_CACHE = base.MARISA_TINY_CACHE
+
+MIN_NUM_TRIES = base.MARISA_MIN_NUM_TRIES
+MAX_NUM_TRIES = base.MARISA_MAX_NUM_TRIES
+DEFAULT_NUM_TRIES = base.MARISA_DEFAULT_NUM_TRIES
+
+# MARISA_TEXT_TAIL merges last labels as zero-terminated strings. So, it is
+# available if and only if the last labels do not contain a NULL character.
+# If MARISA_TEXT_TAIL is specified and a NULL character exists in the last
+# labels, the setting is automatically switched to MARISA_BINARY_TAIL.
+TEXT_TAIL = base.MARISA_TEXT_TAIL
+
+# MARISA_BINARY_TAIL also merges last labels but as byte sequences. It uses
+# a bit vector to detect the end of a sequence, instead of NULL characters.
+# So, MARISA_BINARY_TAIL requires a larger space if the average length of
+# labels is greater than 8.
+BINARY_TAIL = base.MARISA_BINARY_TAIL
+DEFAULT_TAIL = base.MARISA_DEFAULT_TAIL
+
+
+# MARISA_LABEL_ORDER arranges nodes in ascending label order.
+# MARISA_LABEL_ORDER is useful if an application needs to predict keys in
+# label order.
+LABEL_ORDER = base.MARISA_LABEL_ORDER
+
+# MARISA_WEIGHT_ORDER arranges nodes in descending weight order.
+# MARISA_WEIGHT_ORDER is generally a better choice because it enables faster
+# matching.
+WEIGHT_ORDER = base.MARISA_WEIGHT_ORDER
+DEFAULT_ORDER = base.MARISA_DEFAULT_ORDER
+
+
+cdef class _Trie:
+ cdef trie.Trie* _trie
+
+ cdef bytes _encode_key(self, key):
+ return key
+
+ cdef _get_key(self, agent.Agent& ag):
+ return ag.key().ptr()[:ag.key().length()]
+
+ def __init__(self, arg=None, num_tries=DEFAULT_NUM_TRIES, binary=False,
+ cache_size=DEFAULT_CACHE, order=DEFAULT_ORDER, weights=None):
+ """
+ ``arg`` can be one of the following:
+
+ * an iterable with bytes keys;
+ * None (if you're going to load a trie later).
+
+ Pass a ``weights`` iterable with expected lookup frequencies
+ to optimize lookup and prefix search speed.
+ """
+
+ if self._trie:
+ return
+ self._trie = new trie.Trie()
+
+ byte_keys = (self._encode_key(key) for key in (arg or []))
+
+ self._build(
+ byte_keys,
+ weights,
+ num_tries=num_tries,
+ binary=binary,
+ cache_size=cache_size,
+ order=order
+ )
+
+ def __dealloc__(self):
+ if self._trie:
+ del self._trie
+
+ def _config_flags(self, num_tries=DEFAULT_NUM_TRIES, binary=False,
+ cache_size=DEFAULT_CACHE, order=DEFAULT_ORDER):
+ if not MIN_NUM_TRIES <= num_tries <= MAX_NUM_TRIES:
+ raise ValueError(
+ "num_tries (which is %d) must be between between %d and %d" %
+ (num_tries, MIN_NUM_TRIES, MAX_NUM_TRIES))
+
+ binary_flag = BINARY_TAIL if binary else TEXT_TAIL
+ return num_tries | binary_flag | cache_size | order
+
+ def _build(self, byte_keys, weights=None, **options):
+ if weights is None:
+ weights = itertools.repeat(1.0)
+
+ cdef char* data
+ cdef float weight
+ cdef keyset.Keyset *ks = new keyset.Keyset()
+
+ try:
+ for key, weight in izip(byte_keys, weights):
+ ks.push_back(<char *>key, len(key), weight)
+ self._trie.build(ks[0], self._config_flags(**options))
+ finally:
+ del ks
+
+ def __richcmp__(self, other, int op):
+ if op == 2: # ==
+ if other is self:
+ return True
+ elif not isinstance(other, _Trie):
+ return False
+
+ return (<_Trie>self)._equals(other)
+ elif op == 3: # !=
+ return not (self == other)
+
+ raise TypeError("unorderable types: {0} and {1}".format(
+ self.__class__, other.__class__))
+
+ cdef bint _equals(self, _Trie other) nogil:
+ cdef int num_keys = self._trie.num_keys()
+ cdef base.NodeOrder node_order = self._trie.node_order()
+ if (other._trie.num_keys() != num_keys or
+ other._trie.node_order() != node_order):
+ return False
+
+ cdef agent.Agent ag1, ag2
+ ag1.set_query(b"")
+ ag2.set_query(b"")
+ cdef int i
+ cdef key.Key key1, key2
+ for i in range(num_keys):
+ self._trie.predictive_search(ag1)
+ other._trie.predictive_search(ag2)
+ key1 = ag1.key()
+ key2 = ag2.key()
+ if (key1.length() != key2.length() or
+ strncmp(key1.ptr(), key2.ptr(), key1.length()) != 0):
+ return False
+ return True
+
+ def __iter__(self):
+ return self.iterkeys()
+
+ def __len__(self):
+ return self._trie.num_keys()
+
+ def __contains__(self, key):
+ cdef bytes _key = self._encode_key(key)
+ return self._contains(_key)
+
+ cdef bint _contains(self, bytes key):
+ cdef agent.Agent ag
+ ag.set_query(key, len(key))
+ return self._trie.lookup(ag)
+
+ def read(self, f):
+ """Read a trie from an open file.
+
+ :param file f: a "real" on-disk file object. Passing a *file-like*
+ object would result in an error.
+
+ .. deprecated:: 0.7.3
+
+ The method will be removed in version 0.8.0. Please use
+ :meth:`load` instead.
+ """
+ warnings.warn("Trie.save is deprecated and will "
+ "be removed in marisa_trie 0.8.0. Please use "
+ "Trie.load instead.", DeprecationWarning)
+ self._trie.read(f.fileno())
+ return self
+
+ def write(self, f):
+ """Write a trie to an open file.
+
+ :param file f: a "real" on-disk file object. Passing a *file-like*
+ object would result in an error.
+
+ .. deprecated:: 0.7.3
+
+ The method will be removed in version 0.8.0. Please use
+ :meth:`save` instead.
+ """
+ warnings.warn("Trie.write is deprecated and will "
+ "be removed in marisa_trie 0.8.0. Please use "
+ "Trie.save instead.", DeprecationWarning)
+ self._trie.write(f.fileno())
+
+ def save(self, path):
+ """Save a trie to a specified path."""
+ with open(path, 'w') as f:
+ self._trie.write(f.fileno())
+
+ def load(self, path):
+ """Load a trie from a specified path."""
+ with open(path, 'r') as f:
+ self._trie.read(f.fileno())
+ return self
+
+ cpdef bytes tobytes(self) except +:
+ """Return raw trie content as bytes."""
+ cdef stringstream stream
+ iostream.write((<ostream *> &stream)[0], self._trie[0])
+ cdef bytes res = stream.str()
+ return res
+
+ cpdef frombytes(self, bytes data) except +:
+ """Load a trie from raw bytes generated by :meth:`tobytes`."""
+ cdef stringstream* stream = new stringstream(data)
+ try:
+ iostream.read((<istream *> stream)[0], self._trie)
+ finally:
+ del stream
+ return self
+
+ def __reduce__(self):
+ return self.__class__, (), self.tobytes()
+
+ __setstate__ = frombytes
+
+ def mmap(self, path):
+ """Memory map the content of a trie stored in a file.
+
+ This allows to query trie without loading it fully in memory.
+ """
+ import sys
+ str_path = path.encode(sys.getfilesystemencoding())
+ cdef char* c_path = str_path
+ self._trie.mmap(c_path)
+ return self
+
+ def iterkeys(self, prefix=None):
+ """
+ Return an iterator over trie keys starting with a given ``prefix``.
+ """
+ cdef agent.Agent ag
+ cdef bytes b_prefix = b''
+ if prefix is not None:
+ b_prefix = self._encode_key(prefix)
+ ag.set_query(b_prefix, len(b_prefix))
+
+ while self._trie.predictive_search(ag):
+ yield self._get_key(ag)
+
+ cpdef list keys(self, prefix=None):
+ """Return a list of trie keys starting with a given ``prefix``."""
+ # non-generator inlined version of iterkeys()
+ cdef list res = []
+ cdef bytes b_prefix = b''
+ if prefix is not None:
+ b_prefix = self._encode_key(prefix)
+ cdef agent.Agent ag
+ ag.set_query(b_prefix, len(b_prefix))
+
+ while self._trie.predictive_search(ag):
+ res.append(self._get_key(ag))
+
+ return res
+
+ def has_keys_with_prefix(self, prefix=""):
+ """
+ Return ``True`` if any key in the trie begins with ``prefix``.
+
+ .. deprecated:: 0.7.3
+
+ The method will be removed in version 0.8.0. Please use
+ :meth:`iterkeys` instead.
+ """
+ warnings.warn("Trie.has_keys_with_prefix is deprecated and will "
+ "be removed in marisa_trie 0.8.0. Please use "
+ "Trie.iterkeys instead.", DeprecationWarning)
+
+ cdef agent.Agent ag
+ cdef bytes b_prefix = self._encode_key(prefix)
+ ag.set_query(b_prefix, len(b_prefix))
+ return self._trie.predictive_search(ag)
+
+
+cdef class BinaryTrie(_Trie):
+ """A trie mapping bytes keys to auto-generated unique IDs."""
+
+ # key_id method is not in _Trie because it won't work for BytesTrie
+ cpdef int key_id(self, bytes key) except -1:
+ """Return an ID generated for a given ``key``.
+
+ :raises KeyError: if key is not present in this trie.
+ """
+ cdef int res = self._key_id(key, len(key))
+ if res == -1:
+ raise KeyError(key)
+ return res
+
+ cdef int _key_id(self, char* key, int len):
+ cdef bint res
+ cdef agent.Agent ag
+ ag.set_query(key, len)
+ res = self._trie.lookup(ag)
+ if not res:
+ return -1
+ return ag.key().id()
+
+ cpdef restore_key(self, int index):
+ """Return a key corresponding to a given ID."""
+ cdef agent.Agent ag
+ ag.set_query(index)
+ try:
+ self._trie.reverse_lookup(ag)
+ except KeyError:
+ raise KeyError(index)
+ return self._get_key(ag)
+
+ def __getitem__(self, bytes key):
+ return self.key_id(key)
+
+ def get(self, bytes key, default=None):
+ """
+ Return an ID for a given ``key`` or ``default`` if ``key`` is
+ not present in this trie.
+ """
+ cdef int res
+
+ res = self._key_id(key, len(key))
+ if res == -1:
+ return default
+ return res
+
+ def iter_prefixes(self, bytes key):
+ """
+ Return an iterator of all prefixes of a given key.
+ """
+ cdef agent.Agent ag
+ ag.set_query(key, len(key))
+
+ while self._trie.common_prefix_search(ag):
+ yield self._get_key(ag)
+
+ def prefixes(self, bytes key):
+ """
+ Return a list with all prefixes of a given key.
+ """
+ # this an inlined version of ``list(self.iter_prefixes(key))``
+
+ cdef list res = []
+ cdef agent.Agent ag
+ ag.set_query(key, len(key))
+
+ while self._trie.common_prefix_search(ag):
+ res.append(self._get_key(ag))
+ return res
+
+ def items(self, bytes prefix=b""):
+ # inlined for speed
+ cdef list res = []
+ cdef agent.Agent ag
+ ag.set_query(prefix, len(prefix))
+
+ while self._trie.predictive_search(ag):
+ res.append((self._get_key(ag), ag.key().id()))
+
+ return res
+
+ def iteritems(self, bytes prefix=b""):
+ """
+ Return an iterator over items that have a prefix ``prefix``.
+ """
+ cdef agent.Agent ag
+ ag.set_query(prefix, len(prefix))
+
+ while self._trie.predictive_search(ag):
+ yield self._get_key(ag), ag.key().id()
+
+
+cdef class _UnicodeKeyedTrie(_Trie):
+ """
+ MARISA-trie wrapper for unicode keys.
+ """
+ cdef bytes _encode_key(self, key):
+ return key.encode('utf8')
+
+ cdef _get_key(self, agent.Agent& ag):
+ return <unicode>_Trie._get_key(self, ag).decode('utf8')
+
+
+cdef class Trie(_UnicodeKeyedTrie):
+ """A trie mapping unicode keys to auto-generated unique IDs."""
+
+ # key_id method is not in _Trie because it won't work for BytesTrie
+ cpdef int key_id(self, unicode key) except -1:
+ """Return an ID generated for a given ``key``.
+
+ :raises KeyError: if key is not present in this trie.
+ """
+ cdef bytes _key = <bytes>key.encode('utf8')
+ cdef int res = self._key_id(_key)
+ if res == -1:
+ raise KeyError(key)
+ return res
+
+ def __getitem__(self, unicode key):
+ return self.key_id(key)
+
+ def get(self, key, default=None):
+ """
+ Return an ID for a given ``key`` or ``default`` if ``key`` is
+ not present in this trie.
+ """
+ cdef bytes b_key
+ cdef int res
+
+ if isinstance(key, unicode):
+ b_key = <bytes>(<unicode>key).encode('utf8')
+ else:
+ b_key = key
+
+ res = self._key_id(b_key)
+ if res == -1:
+ return default
+ return res
+
+ cpdef restore_key(self, int index):
+ """Return a key corresponding to a given ID."""
+ cdef agent.Agent ag
+ ag.set_query(index)
+ try:
+ self._trie.reverse_lookup(ag)
+ except KeyError:
+ raise KeyError(index)
+ return self._get_key(ag)
+
+ cdef int _key_id(self, char* key):
+ cdef bint res
+ cdef agent.Agent ag
+ ag.set_query(key)
+ res = self._trie.lookup(ag)
+ if not res:
+ return -1
+ return ag.key().id()
+
+ def iter_prefixes(self, unicode key):
+ """
+ Return an iterator of all prefixes of a given key.
+ """
+ cdef bytes b_key = <bytes>key.encode('utf8')
+ cdef agent.Agent ag
+ ag.set_query(b_key)
+
+ while self._trie.common_prefix_search(ag):
+ yield self._get_key(ag)
+
+ def prefixes(self, unicode key):
+ """
+ Return a list with all prefixes of a given key.
+ """
+ # this an inlined version of ``list(self.iter_prefixes(key))``
+
+ cdef list res = []
+ cdef bytes b_key = <bytes>key.encode('utf8')
+ cdef agent.Agent ag
+ ag.set_query(b_key)
+
+ while self._trie.common_prefix_search(ag):
+ res.append(self._get_key(ag))
+ return res
+
+ def iteritems(self, unicode prefix=""):
+ """
+ Return an iterator over items that have a prefix ``prefix``.
+ """
+ cdef bytes b_prefix = <bytes>prefix.encode('utf8')
+ cdef agent.Agent ag
+ ag.set_query(b_prefix)
+
+ while self._trie.predictive_search(ag):
+ yield self._get_key(ag), ag.key().id()
+
+ def items(self, unicode prefix=""):
+ # inlined for speed
+ cdef list res = []
+ cdef bytes b_prefix = <bytes>prefix.encode('utf8')
+ cdef agent.Agent ag
+ ag.set_query(b_prefix)
+
+ while self._trie.predictive_search(ag):
+ res.append((self._get_key(ag), ag.key().id()))
+
+ return res
+
+
+# This symbol is not allowed in utf8 so it is safe to use
+# as a separator between utf8-encoded string and binary payload.
+# XXX: b'\xff' value changes sort order for BytesTrie and RecordTrie.
+# See https://github.com/kmike/DAWG docs for a description of a similar issue.
+cdef bytes _VALUE_SEPARATOR = b'\xff'
+
+
+cdef class BytesTrie(_UnicodeKeyedTrie):
+ """A trie mapping unicode keys to lists of bytes objects.
+
+ The mapping is implemented by appending binary values to UTF8-encoded
+ and storing the result in MARISA-trie.
+ """
+ cdef bytes _b_value_separator
+ cdef unsigned char _c_value_separator
+
+ def __init__(self, arg=None, bytes value_separator=_VALUE_SEPARATOR,
+ **options):
+ """
+ ``arg`` must be an iterable of tuples (unicode_key, bytes_payload).
+ """
+ super(BytesTrie, self).__init__()
+
+ self._b_value_separator = value_separator
+ self._c_value_separator = <unsigned char>ord(value_separator)
+
+ byte_keys = (self._raw_key(d[0], d[1]) for d in (arg or []))
+ self._build(byte_keys, **options)
+
+ cpdef bytes _raw_key(self, unicode key, bytes payload):
+ return key.encode('utf8') + self._b_value_separator + payload
+
+ cdef bint _contains(self, bytes key):
+ cdef agent.Agent ag
+ cdef bytes _key = key + self._b_value_separator
+ ag.set_query(_key)
+ return self._trie.predictive_search(ag)
+
+ cpdef list prefixes(self, unicode key):
+ """
+ Return a list with all prefixes of a given key.
+ """
+
+ # XXX: is there a char-walking API in libmarisa?
+ # This implementation is suboptimal.
+
+ cdef agent.Agent ag
+ cdef list res = []
+ cdef int key_len = len(key)
+ cdef unicode prefix
+ cdef bytes b_prefix
+ cdef int ind = 1
+
+ while ind <= key_len:
+ prefix = key[:ind]
+ b_prefix = <bytes>(prefix.encode('utf8') + self._b_value_separator)
+ ag.set_query(b_prefix)
+ if self._trie.predictive_search(ag):
+ res.append(prefix)
+
+ ind += 1
+
+ return res
+
+ def __getitem__(self, key):
+ cdef list res = self.get(key)
+ if res is None:
+ raise KeyError(key)
+ return res
+
+ cpdef get(self, key, default=None):
+ """
+ Return a list of payloads (as byte objects) for a given key
+ or ``default`` if the key is not found.
+ """
+ cdef list res
+
+ if isinstance(key, unicode):
+ res = self.get_value(<unicode>key)
+ else:
+ res = self.b_get_value(key)
+
+ if not res:
+ return default
+ return res
+
+ cpdef list get_value(self, unicode key):
+ """
+ Return a list of payloads (as byte objects) for a given unicode key.
+ """
+ cdef bytes b_key = <bytes>key.encode('utf8')
+ return self.b_get_value(b_key)
+
+ cpdef list b_get_value(self, bytes key):
+ """
+ Return a list of payloads (as byte objects) for a given utf8-encoded key.
+ """
+ cdef list res = []
+ cdef bytes value
+ cdef bytes b_prefix = key + self._b_value_separator
+ cdef int prefix_len = len(b_prefix)
+
+ cdef agent.Agent ag
+ ag.set_query(b_prefix)
+
+ while self._trie.predictive_search(ag):
+ value = ag.key().ptr()[prefix_len:ag.key().length()]
+ res.append(value)
+
+ return res
+
+ cpdef list items(self, unicode prefix=""):
+ # copied from iteritems for speed
+ cdef bytes b_prefix = <bytes>prefix.encode('utf8')
+ cdef bytes value
+ cdef unicode key
+ cdef unsigned char* raw_key
+ cdef list res = []
+ cdef int i, value_len
+
+ cdef agent.Agent ag
+ ag.set_query(b_prefix)
+
+ while self._trie.predictive_search(ag):
+ raw_key = <unsigned char*>ag.key().ptr()
+
+ for i in range(0, ag.key().length()):
+ if raw_key[i] == self._c_value_separator:
+ break
+
+ key = raw_key[:i].decode('utf8')
+ value = raw_key[i+1:ag.key().length()]
+
+ res.append(
+ (key, value)
+ )
+ return res
+
+ def iteritems(self, unicode prefix=""):
+ cdef bytes b_prefix = <bytes>prefix.encode('utf8')
+ cdef bytes value
+ cdef unicode key
+ cdef unsigned char* raw_key
+ cdef int i, value_len
+
+ cdef agent.Agent ag
+ ag.set_query(b_prefix)
+
+ while self._trie.predictive_search(ag):
+ raw_key = <unsigned char*>ag.key().ptr()
+
+ for i in range(0, ag.key().length()):
+ if raw_key[i] == self._c_value_separator:
+ break
+
+ key = raw_key[:i].decode('utf8')
+ value = raw_key[i+1:ag.key().length()]
+
+ yield key, value
+
+ cpdef list keys(self, prefix=""):
+ # copied from iterkeys for speed
+ cdef bytes b_prefix = <bytes>prefix.encode('utf8')
+ cdef unicode key
+ cdef unsigned char* raw_key
+ cdef list res = []
+ cdef int i
+
+ cdef agent.Agent ag
+ ag.set_query(b_prefix)
+
+ while self._trie.predictive_search(ag):
+ raw_key = <unsigned char*>ag.key().ptr()
+
+ for i in range(0, ag.key().length()):
+ if raw_key[i] == self._c_value_separator:
+ key = raw_key[:i].decode('utf8')
+ res.append(key)
+ break
+ return res
+
+ def iterkeys(self, unicode prefix=""):
+ cdef bytes b_prefix = <bytes>prefix.encode('utf8')
+ cdef unicode key
+ cdef unsigned char* raw_key
+ cdef int i
+
+ cdef agent.Agent ag
+ ag.set_query(b_prefix)
+
+ while self._trie.predictive_search(ag):
+ raw_key = <unsigned char*>ag.key().ptr()
+
+ for i in range(0, ag.key().length()):
+ if raw_key[i] == self._c_value_separator:
+ yield raw_key[:i].decode('utf8')
+ break
+
+
+cdef class _UnpackTrie(BytesTrie):
+
+ def __init__(self, arg=None, **options):
+ keys = ((d[0], self._pack(d[1])) for d in (arg or []))
+ super(_UnpackTrie, self).__init__(keys, **options)
+
+ cdef _unpack(self, bytes value):
+ return value
+
+ cdef bytes _pack(self, value):
+ return value
+
+ cpdef list b_get_value(self, bytes key):
+ cdef list values = BytesTrie.b_get_value(self, key)
+ return [self._unpack(val) for val in values]
+
+ cpdef list items(self, unicode prefix=""):
+ cdef list items = BytesTrie.items(self, prefix)
+ return [(key, self._unpack(val)) for (key, val) in items]
+
+ def iteritems(self, unicode prefix=""):
+ return ((key, self._unpack(val)) for key, val in BytesTrie.iteritems(self, prefix))
+
+
+cdef class RecordTrie(_UnpackTrie):
+ """A trie mapping unicode keys to lists of data tuples.
+
+ The data is packed using :mod:`struct` module, therefore all
+ tuples must be of the same format. See :mod:`struct` documentation
+ for available format strings.
+
+ The mapping is implemented by appending binary values to UTF8-encoded
+ and storing the result in MARISA-trie.
+ """
+ cdef _struct
+ cdef _fmt
+
+ def __init__(self, fmt, arg=None, **options):
+ """
+ ``arg`` must be an iterable of tuples (unicode_key, data_tuple).
+ Data tuples will be converted to bytes with
+ ``struct.pack(fmt, *data_tuple)``.
+ """
+ self._fmt = fmt
+ self._struct = struct.Struct(str(fmt))
+ super(RecordTrie, self).__init__(arg, **options)
+
+ cdef _unpack(self, bytes value):
+ return self._struct.unpack(value)
+
+ cdef bytes _pack(self, value):
+ return self._struct.pack(*value)
+
+ def __reduce__(self):
+ return self.__class__, (self._fmt, ), self.tobytes()
diff --git a/contrib/python/marisa-trie/query.pxd b/contrib/python/marisa-trie/query.pxd
new file mode 100644
index 0000000000..a650bb8965
--- /dev/null
+++ b/contrib/python/marisa-trie/query.pxd
@@ -0,0 +1,20 @@
+cdef extern from "<marisa/query.h>" namespace "marisa" nogil:
+
+ cdef cppclass Query:
+ Query()
+ Query(Query &query)
+
+ #Query &operator=(Query &query)
+
+ char operator[](int i)
+
+ void set_str(char *str)
+ void set_str(char *ptr, int length)
+ void set_id(int id)
+
+ char *ptr()
+ int length()
+ int id()
+
+ void clear()
+ void swap(Query &rhs)
diff --git a/contrib/python/marisa-trie/std_iostream.pxd b/contrib/python/marisa-trie/std_iostream.pxd
new file mode 100644
index 0000000000..bf7d0e89aa
--- /dev/null
+++ b/contrib/python/marisa-trie/std_iostream.pxd
@@ -0,0 +1,18 @@
+from libcpp.string cimport string
+
+cdef extern from "<istream>" namespace "std" nogil:
+ cdef cppclass istream:
+ istream() except +
+ istream& read (char* s, int n) except +
+
+ cdef cppclass ostream:
+ ostream() except +
+ ostream& write (char* s, int n) except +
+
+cdef extern from "<sstream>" namespace "std" nogil:
+
+ cdef cppclass stringstream:
+ stringstream()
+ stringstream(string s)
+ string str ()
+
diff --git a/contrib/python/marisa-trie/trie.pxd b/contrib/python/marisa-trie/trie.pxd
new file mode 100644
index 0000000000..f525caf8ad
--- /dev/null
+++ b/contrib/python/marisa-trie/trie.pxd
@@ -0,0 +1,41 @@
+cimport agent
+cimport base
+cimport keyset
+
+
+cdef extern from "<marisa/trie.h>" namespace "marisa" nogil:
+
+ cdef cppclass Trie:
+ Trie()
+
+ void build(keyset.Keyset &keyset, int config_flags) except +
+ void build(keyset.Keyset &keyset) except +
+
+ void mmap(char *filename) except +
+ void map(void *ptr, int size) except +
+
+ void load(char *filename) except +
+ void read(int fd) except +
+
+ void save(char *filename) except +
+ void write(int fd) except +
+
+ bint lookup(agent.Agent &agent) except +
+ void reverse_lookup(agent.Agent &agent) except +KeyError
+ bint common_prefix_search(agent.Agent &agent) except +
+ bint predictive_search(agent.Agent &agent) except +
+
+ int num_tries() except +
+ int num_keys() except +
+ int num_nodes() except +
+
+ base.TailMode tail_mode()
+ base.NodeOrder node_order()
+
+ bint empty() except +
+ int size() except +
+ int total_size() except +
+ int io_size() except +
+
+ void clear() except +
+ void swap(Trie &rhs) except +
diff --git a/contrib/python/marisa-trie/ya.make b/contrib/python/marisa-trie/ya.make
new file mode 100644
index 0000000000..490eef9afa
--- /dev/null
+++ b/contrib/python/marisa-trie/ya.make
@@ -0,0 +1,33 @@
+PY23_LIBRARY()
+
+LICENSE(MIT)
+
+VERSION(0.7.5)
+
+NO_COMPILER_WARNINGS()
+
+ADDINCL(
+ contrib/python/marisa-trie
+)
+
+SRCS(
+ marisa/agent.cc
+ marisa/keyset.cc
+ marisa/trie.cc
+
+ marisa/grimoire/io/mapper.cc
+ marisa/grimoire/io/reader.cc
+ marisa/grimoire/io/writer.cc
+ marisa/grimoire/trie/louds-trie.cc
+ marisa/grimoire/trie/tail.cc
+ marisa/grimoire/vector/bit-vector.cc
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ marisa_trie.pyx
+)
+
+NO_LINT()
+
+END()
diff --git a/contrib/python/path.py/py2/LICENSE b/contrib/python/path.py/py2/LICENSE
new file mode 100644
index 0000000000..5e795a61f3
--- /dev/null
+++ b/contrib/python/path.py/py2/LICENSE
@@ -0,0 +1,7 @@
+Copyright Jason R. Coombs
+
+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/contrib/python/path.py/py2/README.rst b/contrib/python/path.py/py2/README.rst
new file mode 100644
index 0000000000..424d3e77c8
--- /dev/null
+++ b/contrib/python/path.py/py2/README.rst
@@ -0,0 +1,134 @@
+.. image:: https://img.shields.io/pypi/v/path.py.svg
+ :target: https://pypi.org/project/path.py
+
+.. image:: https://img.shields.io/pypi/pyversions/path.py.svg
+
+.. image:: https://img.shields.io/travis/jaraco/path.py/master.svg
+ :target: https://travis-ci.org/jaraco/path.py
+
+.. image:: https://img.shields.io/appveyor/ci/jaraco/path-py/master.svg
+ :target: https://ci.appveyor.com/project/jaraco/path-py/branch/master
+
+.. image:: https://readthedocs.org/projects/pathpy/badge/?version=latest
+ :target: https://pathpy.readthedocs.io/en/latest/?badge=latest
+
+``path.py`` implements path objects as first-class entities, allowing
+common operations on files to be invoked on those path objects directly. For
+example:
+
+.. code-block:: python
+
+ from path import Path
+ d = Path('/home/guido/bin')
+ for f in d.files('*.py'):
+ f.chmod(0o755)
+
+ # Globbing
+ for f in d.files('*.py'):
+ f.chmod(0o755)
+
+ # Changing the working directory:
+ with Path("somewhere"):
+ # cwd in now `somewhere`
+ ...
+
+ # Concatenate paths with /
+ foo_txt = Path("bar") / "foo.txt"
+
+``path.py`` is `hosted at Github <https://github.com/jaraco/path.py>`_.
+
+Find `the documentation here <https://pathpy.readthedocs.io>`_.
+
+Guides and Testimonials
+=======================
+
+Yasoob wrote the Python 101 `Writing a Cleanup Script
+<http://freepythontips.wordpress.com/2014/01/23/python-101-writing-a-cleanup-script/>`_
+based on ``path.py``.
+
+Installing
+==========
+
+Path.py may be installed using ``setuptools``, ``distribute``, or ``pip``::
+
+ pip install path.py
+
+The latest release is always updated to the `Python Package Index
+<http://pypi.python.org/pypi/path.py>`_.
+
+You may also always download the source distribution (zip/tarball), extract
+it, and run ``python setup.py`` to install it.
+
+Advantages
+==========
+
+Python 3.4 introduced
+`pathlib <https://docs.python.org/3/library/pathlib.html>`_,
+which shares many characteristics with ``path.py``. In particular,
+it provides an object encapsulation for representing filesystem paths.
+One may have imagined ``pathlib`` would supersede ``path.py``.
+
+But the implementation and the usage quickly diverge, and ``path.py``
+has several advantages over ``pathlib``:
+
+- ``path.py`` implements ``Path`` objects as a subclass of
+ ``str`` (unicode on Python 2), and as a result these ``Path``
+ objects may be passed directly to other APIs that expect simple
+ text representations of paths, whereas with ``pathlib``, one
+ must first cast values to strings before passing them to
+ APIs unaware of ``pathlib``. This shortcoming was `addressed
+ by PEP 519 <https://www.python.org/dev/peps/pep-0519/>`_,
+ in Python 3.6.
+- ``path.py`` goes beyond exposing basic functionality of a path
+ and exposes commonly-used behaviors on a path, providing
+ methods like ``rmtree`` (from shlib) and ``remove_p`` (remove
+ a file if it exists).
+- As a PyPI-hosted package, ``path.py`` is free to iterate
+ faster than a stdlib package. Contributions are welcome
+ and encouraged.
+- ``path.py`` provides a uniform abstraction over its Path object,
+ freeing the implementer to subclass it readily. One cannot
+ subclass a ``pathlib.Path`` to add functionality, but must
+ subclass ``Path``, ``PosixPath``, and ``WindowsPath``, even
+ if one only wishes to add a ``__dict__`` to the subclass
+ instances. ``path.py`` instead allows the ``Path.module``
+ object to be overridden by subclasses, defaulting to the
+ ``os.path``. Even advanced uses of ``path.Path`` that
+ subclass the model do not need to be concerned with
+ OS-specific nuances.
+
+Alternatives
+============
+
+In addition to
+`pathlib <https://docs.python.org/3/library/pathlib.html>`_, the
+`pylib project <https://pypi.org/project/py/>`_ implements a
+`LocalPath <https://github.com/pytest-dev/py/blob/72601dc8bbb5e11298bf9775bb23b0a395deb09b/py/_path/local.py#L106>`_
+class, which shares some behaviors and interfaces with ``path.py``.
+
+Development
+===========
+
+To install a development version, use the Github links to clone or
+download a snapshot of the latest code. Alternatively, if you have git
+installed, you may be able to use ``pip`` to install directly from
+the repository::
+
+ pip install git+https://github.com/jaraco/path.py.git
+
+Testing
+=======
+
+Tests are continuously run by Travis-CI: |BuildStatus|_
+
+.. |BuildStatus| image:: https://secure.travis-ci.org/jaraco/path.py.png
+.. _BuildStatus: http://travis-ci.org/jaraco/path.py
+
+To run the tests, refer to the ``.travis.yml`` file for the steps run on the
+Travis-CI hosts.
+
+Releasing
+=========
+
+Tagged releases are automatically published to PyPI by Travis-CI, assuming
+the tests pass.
diff --git a/contrib/python/path.py/py3/.dist-info/METADATA b/contrib/python/path.py/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..68873c1391
--- /dev/null
+++ b/contrib/python/path.py/py3/.dist-info/METADATA
@@ -0,0 +1,36 @@
+Metadata-Version: 2.1
+Name: path.py
+Version: 12.5.0
+Summary: A module wrapper for os.path
+Home-page: https://github.com/jaraco/path
+Author: Jason Orendorff
+Author-email: jason.orendorff@gmail.com
+Maintainer: Jason R. Coombs
+Maintainer-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.5
+Requires-Dist: path
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=3.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (!=3.7.3,>=3.5) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=1.2.3) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-black-multipy ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: appdirs ; extra == 'testing'
+Requires-Dist: packaging ; extra == 'testing'
+Requires-Dist: pygments ; extra == 'testing'
+
+``path.py`` has been renamed to `path <https://pypi.org/project/path>`_.
+
+
diff --git a/contrib/python/path.py/py3/.dist-info/top_level.txt b/contrib/python/path.py/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..e7a8fd4d0a
--- /dev/null
+++ b/contrib/python/path.py/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+path
diff --git a/contrib/python/path.py/py3/LICENSE b/contrib/python/path.py/py3/LICENSE
new file mode 100644
index 0000000000..5e795a61f3
--- /dev/null
+++ b/contrib/python/path.py/py3/LICENSE
@@ -0,0 +1,7 @@
+Copyright Jason R. Coombs
+
+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/contrib/python/path.py/py3/README.rst b/contrib/python/path.py/py3/README.rst
new file mode 100644
index 0000000000..4bc121481c
--- /dev/null
+++ b/contrib/python/path.py/py3/README.rst
@@ -0,0 +1 @@
+``path.py`` has been renamed to `path <https://pypi.org/project/path>`_.
diff --git a/contrib/python/path.py/py3/ya.make b/contrib/python/path.py/py3/ya.make
new file mode 100644
index 0000000000..241d32971e
--- /dev/null
+++ b/contrib/python/path.py/py3/ya.make
@@ -0,0 +1,21 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(12.5.0)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/python/path
+)
+
+NO_LINT()
+
+RESOURCE_FILES(
+ PREFIX contrib/python/path.py/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
diff --git a/contrib/python/path.py/ya.make b/contrib/python/path.py/ya.make
new file mode 100644
index 0000000000..d0c1aca518
--- /dev/null
+++ b/contrib/python/path.py/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/path.py/py2)
+ELSE()
+ PEERDIR(contrib/python/path.py/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/path/.dist-info/METADATA b/contrib/python/path/.dist-info/METADATA
new file mode 100644
index 0000000000..7cddb38723
--- /dev/null
+++ b/contrib/python/path/.dist-info/METADATA
@@ -0,0 +1,201 @@
+Metadata-Version: 2.1
+Name: path
+Version: 16.7.1
+Summary: A module wrapper for os.path
+Home-page: https://github.com/jaraco/path
+Author: Jason Orendorff
+Author-email: jason.orendorff@gmail.com
+Maintainer: Jason R. Coombs
+Maintainer-email: jaraco@jaraco.com
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.8
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: sphinx (>=3.5) ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=9.3) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Requires-Dist: furo ; extra == 'docs'
+Requires-Dist: sphinx-lint ; extra == 'docs'
+Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=2.2) ; extra == 'testing'
+Requires-Dist: pytest-ruff ; extra == 'testing'
+Requires-Dist: appdirs ; extra == 'testing'
+Requires-Dist: packaging ; extra == 'testing'
+Requires-Dist: pygments ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pywin32 ; (platform_system == "Windows" and python_version < "3.12") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/path.svg
+ :target: https://pypi.org/project/path
+
+.. image:: https://img.shields.io/pypi/pyversions/path.svg
+
+.. image:: https://github.com/jaraco/path/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/path/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+ :target: https://github.com/astral-sh/ruff
+ :alt: Ruff
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/path/badge/?version=latest
+ :target: https://path.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2023-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/path
+ :target: https://tidelift.com/subscription/pkg/pypi-path?utm_source=pypi-path&utm_medium=readme
+
+
+``path`` (aka path pie, formerly ``path.py``) implements path
+objects as first-class entities, allowing common operations on
+files to be invoked on those path objects directly. For example:
+
+.. code-block:: python
+
+ from path import Path
+
+ d = Path("/home/guido/bin")
+ for f in d.files("*.py"):
+ f.chmod(0o755)
+
+ # Globbing
+ for f in d.files("*.py"):
+ f.chmod("u+rwx")
+
+ # Changing the working directory:
+ with Path("somewhere"):
+ # cwd in now `somewhere`
+ ...
+
+ # Concatenate paths with /
+ foo_txt = Path("bar") / "foo.txt"
+
+Path pie is `hosted at Github <https://github.com/jaraco/path>`_.
+
+Find `the documentation here <https://path.readthedocs.io>`_.
+
+Guides and Testimonials
+=======================
+
+Yasoob wrote the Python 101 `Writing a Cleanup Script
+<http://freepythontips.wordpress.com/2014/01/23/python-101-writing-a-cleanup-script/>`_
+based on ``path``.
+
+Advantages
+==========
+
+Python 3.4 introduced
+`pathlib <https://docs.python.org/3/library/pathlib.html>`_,
+which shares many characteristics with ``path``. In particular,
+it provides an object encapsulation for representing filesystem paths.
+One may have imagined ``pathlib`` would supersede ``path``.
+
+But the implementation and the usage quickly diverge, and ``path``
+has several advantages over ``pathlib``:
+
+- ``path`` implements ``Path`` objects as a subclass of
+ ``str``, and as a result these ``Path``
+ objects may be passed directly to other APIs that expect simple
+ text representations of paths, whereas with ``pathlib``, one
+ must first cast values to strings before passing them to
+ APIs unaware of ``pathlib``. This shortcoming was `addressed
+ by PEP 519 <https://www.python.org/dev/peps/pep-0519/>`_,
+ in Python 3.6.
+- ``path`` goes beyond exposing basic functionality of a path
+ and exposes commonly-used behaviors on a path, providing
+ methods like ``rmtree`` (from shlib) and ``remove_p`` (remove
+ a file if it exists).
+- As a PyPI-hosted package, ``path`` is free to iterate
+ faster than a stdlib package. Contributions are welcome
+ and encouraged.
+- ``path`` provides a uniform abstraction over its Path object,
+ freeing the implementer to subclass it readily. One cannot
+ subclass a ``pathlib.Path`` to add functionality, but must
+ subclass ``Path``, ``PosixPath``, and ``WindowsPath``, even
+ if one only wishes to add a ``__dict__`` to the subclass
+ instances. ``path`` instead allows the ``Path.module``
+ object to be overridden by subclasses, defaulting to the
+ ``os.path``. Even advanced uses of ``path.Path`` that
+ subclass the model do not need to be concerned with
+ OS-specific nuances.
+
+This path project has the explicit aim to provide compatibility
+with ``pathlib`` objects where possible, such that a ``path.Path``
+object is a drop-in replacement for ``pathlib.Path*`` objects.
+This project welcomes contributions to improve that compatibility
+where it's lacking.
+
+Alternatives
+============
+
+In addition to
+`pathlib <https://docs.python.org/3/library/pathlib.html>`_, the
+`pylib project <https://pypi.org/project/py/>`_ implements a
+`LocalPath <https://github.com/pytest-dev/py/blob/72601dc8bbb5e11298bf9775bb23b0a395deb09b/py/_path/local.py#L106>`_
+class, which shares some behaviors and interfaces with ``path``.
+
+Development
+===========
+
+To install a development version, use the Github links to clone or
+download a snapshot of the latest code. Alternatively, if you have git
+installed, you may be able to use ``pip`` to install directly from
+the repository::
+
+ pip install git+https://github.com/jaraco/path.git
+
+Testing
+=======
+
+Tests are invoked with `tox <https://pypi.org/project/tox>`_. After
+having installed tox, simply invoke ``tox`` in a checkout of the repo
+to invoke the tests.
+
+Tests are also run in continuous integration. See the badges above
+for links to the CI runs.
+
+Releasing
+=========
+
+Tagged releases are automatically published to PyPI by Azure
+Pipelines, assuming the tests pass.
+
+Origins
+=======
+
+The ``path.py`` project was initially released in 2003 by Jason Orendorff
+and has been continuously developed and supported by several maintainers
+over the years.
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-path?utm_source=pypi-path&utm_medium=referral&utm_campaign=github>`_.
+
+Security Contact
+================
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
diff --git a/contrib/python/path/.dist-info/top_level.txt b/contrib/python/path/.dist-info/top_level.txt
new file mode 100644
index 0000000000..e7a8fd4d0a
--- /dev/null
+++ b/contrib/python/path/.dist-info/top_level.txt
@@ -0,0 +1 @@
+path
diff --git a/contrib/python/path/LICENSE b/contrib/python/path/LICENSE
new file mode 100644
index 0000000000..1bb5a44356
--- /dev/null
+++ b/contrib/python/path/LICENSE
@@ -0,0 +1,17 @@
+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/contrib/python/path/README.rst b/contrib/python/path/README.rst
new file mode 100644
index 0000000000..69aa8737d6
--- /dev/null
+++ b/contrib/python/path/README.rst
@@ -0,0 +1,163 @@
+.. image:: https://img.shields.io/pypi/v/path.svg
+ :target: https://pypi.org/project/path
+
+.. image:: https://img.shields.io/pypi/pyversions/path.svg
+
+.. image:: https://github.com/jaraco/path/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/path/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+ :target: https://github.com/astral-sh/ruff
+ :alt: Ruff
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/path/badge/?version=latest
+ :target: https://path.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2023-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/path
+ :target: https://tidelift.com/subscription/pkg/pypi-path?utm_source=pypi-path&utm_medium=readme
+
+
+``path`` (aka path pie, formerly ``path.py``) implements path
+objects as first-class entities, allowing common operations on
+files to be invoked on those path objects directly. For example:
+
+.. code-block:: python
+
+ from path import Path
+
+ d = Path("/home/guido/bin")
+ for f in d.files("*.py"):
+ f.chmod(0o755)
+
+ # Globbing
+ for f in d.files("*.py"):
+ f.chmod("u+rwx")
+
+ # Changing the working directory:
+ with Path("somewhere"):
+ # cwd in now `somewhere`
+ ...
+
+ # Concatenate paths with /
+ foo_txt = Path("bar") / "foo.txt"
+
+Path pie is `hosted at Github <https://github.com/jaraco/path>`_.
+
+Find `the documentation here <https://path.readthedocs.io>`_.
+
+Guides and Testimonials
+=======================
+
+Yasoob wrote the Python 101 `Writing a Cleanup Script
+<http://freepythontips.wordpress.com/2014/01/23/python-101-writing-a-cleanup-script/>`_
+based on ``path``.
+
+Advantages
+==========
+
+Python 3.4 introduced
+`pathlib <https://docs.python.org/3/library/pathlib.html>`_,
+which shares many characteristics with ``path``. In particular,
+it provides an object encapsulation for representing filesystem paths.
+One may have imagined ``pathlib`` would supersede ``path``.
+
+But the implementation and the usage quickly diverge, and ``path``
+has several advantages over ``pathlib``:
+
+- ``path`` implements ``Path`` objects as a subclass of
+ ``str``, and as a result these ``Path``
+ objects may be passed directly to other APIs that expect simple
+ text representations of paths, whereas with ``pathlib``, one
+ must first cast values to strings before passing them to
+ APIs unaware of ``pathlib``. This shortcoming was `addressed
+ by PEP 519 <https://www.python.org/dev/peps/pep-0519/>`_,
+ in Python 3.6.
+- ``path`` goes beyond exposing basic functionality of a path
+ and exposes commonly-used behaviors on a path, providing
+ methods like ``rmtree`` (from shlib) and ``remove_p`` (remove
+ a file if it exists).
+- As a PyPI-hosted package, ``path`` is free to iterate
+ faster than a stdlib package. Contributions are welcome
+ and encouraged.
+- ``path`` provides a uniform abstraction over its Path object,
+ freeing the implementer to subclass it readily. One cannot
+ subclass a ``pathlib.Path`` to add functionality, but must
+ subclass ``Path``, ``PosixPath``, and ``WindowsPath``, even
+ if one only wishes to add a ``__dict__`` to the subclass
+ instances. ``path`` instead allows the ``Path.module``
+ object to be overridden by subclasses, defaulting to the
+ ``os.path``. Even advanced uses of ``path.Path`` that
+ subclass the model do not need to be concerned with
+ OS-specific nuances.
+
+This path project has the explicit aim to provide compatibility
+with ``pathlib`` objects where possible, such that a ``path.Path``
+object is a drop-in replacement for ``pathlib.Path*`` objects.
+This project welcomes contributions to improve that compatibility
+where it's lacking.
+
+Alternatives
+============
+
+In addition to
+`pathlib <https://docs.python.org/3/library/pathlib.html>`_, the
+`pylib project <https://pypi.org/project/py/>`_ implements a
+`LocalPath <https://github.com/pytest-dev/py/blob/72601dc8bbb5e11298bf9775bb23b0a395deb09b/py/_path/local.py#L106>`_
+class, which shares some behaviors and interfaces with ``path``.
+
+Development
+===========
+
+To install a development version, use the Github links to clone or
+download a snapshot of the latest code. Alternatively, if you have git
+installed, you may be able to use ``pip`` to install directly from
+the repository::
+
+ pip install git+https://github.com/jaraco/path.git
+
+Testing
+=======
+
+Tests are invoked with `tox <https://pypi.org/project/tox>`_. After
+having installed tox, simply invoke ``tox`` in a checkout of the repo
+to invoke the tests.
+
+Tests are also run in continuous integration. See the badges above
+for links to the CI runs.
+
+Releasing
+=========
+
+Tagged releases are automatically published to PyPI by Azure
+Pipelines, assuming the tests pass.
+
+Origins
+=======
+
+The ``path.py`` project was initially released in 2003 by Jason Orendorff
+and has been continuously developed and supported by several maintainers
+over the years.
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-path?utm_source=pypi-path&utm_medium=referral&utm_campaign=github>`_.
+
+Security Contact
+================
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
diff --git a/contrib/python/path/path/__init__.py b/contrib/python/path/path/__init__.py
new file mode 100644
index 0000000000..eebdc3a0b8
--- /dev/null
+++ b/contrib/python/path/path/__init__.py
@@ -0,0 +1,1665 @@
+"""
+Path Pie
+
+Implements ``path.Path`` - An object representing a
+path to a file or directory.
+
+Example::
+
+ from path import Path
+ d = Path('/home/guido/bin')
+
+ # Globbing
+ for f in d.files('*.py'):
+ f.chmod(0o755)
+
+ # Changing the working directory:
+ with Path("somewhere"):
+ # cwd in now `somewhere`
+ ...
+
+ # Concatenate paths with /
+ foo_txt = Path("bar") / "foo.txt"
+"""
+
+import sys
+import warnings
+import os
+import fnmatch
+import glob
+import shutil
+import hashlib
+import errno
+import tempfile
+import functools
+import re
+import contextlib
+import importlib
+import itertools
+import datetime
+from numbers import Number
+from typing import Union
+
+with contextlib.suppress(ImportError):
+ import win32security
+
+with contextlib.suppress(ImportError):
+ import pwd
+
+with contextlib.suppress(ImportError):
+ import grp
+
+from . import matchers
+from . import masks
+from . import classes
+
+
+__all__ = ['Path', 'TempDir']
+
+
+LINESEPS = ['\r\n', '\r', '\n']
+U_LINESEPS = LINESEPS + ['\u0085', '\u2028', '\u2029']
+B_NEWLINE = re.compile('|'.join(LINESEPS).encode())
+U_NEWLINE = re.compile('|'.join(U_LINESEPS))
+B_NL_END = re.compile(B_NEWLINE.pattern + b'$')
+U_NL_END = re.compile(U_NEWLINE.pattern + '$')
+
+_default_linesep = object()
+
+
+def _make_timestamp_ns(value: Union[Number, datetime.datetime]) -> Number:
+ timestamp_s = value if isinstance(value, Number) else value.timestamp()
+ return int(timestamp_s * 10**9)
+
+
+class TreeWalkWarning(Warning):
+ pass
+
+
+class Traversal:
+ """
+ Wrap a walk result to customize the traversal.
+
+ `follow` is a function that takes an item and returns
+ True if that item should be followed and False otherwise.
+
+ For example, to avoid traversing into directories that
+ begin with `.`:
+
+ >>> traverse = Traversal(lambda dir: not dir.startswith('.'))
+ >>> items = list(traverse(Path('.').walk()))
+
+ Directories beginning with `.` will appear in the results, but
+ their children will not.
+
+ >>> dot_dir = next(item for item in items if item.isdir() and item.startswith('.'))
+ >>> any(item.parent == dot_dir for item in items)
+ False
+ """
+
+ def __init__(self, follow):
+ self.follow = follow
+
+ def __call__(self, walker):
+ traverse = None
+ while True:
+ try:
+ item = walker.send(traverse)
+ except StopIteration:
+ return
+ yield item
+
+ traverse = functools.partial(self.follow, item)
+
+
+def _strip_newlines(lines):
+ r"""
+ >>> list(_strip_newlines(['Hello World\r\n', 'foo']))
+ ['Hello World', 'foo']
+ """
+ return (U_NL_END.sub('', line) for line in lines)
+
+
+class Path(str):
+ """
+ Represents a filesystem path.
+
+ For documentation on individual methods, consult their
+ counterparts in :mod:`os.path`.
+
+ Some methods are additionally included from :mod:`shutil`.
+ The functions are linked directly into the class namespace
+ such that they will be bound to the Path instance. For example,
+ ``Path(src).copy(target)`` is equivalent to
+ ``shutil.copy(src, target)``. Therefore, when referencing
+ the docs for these methods, assume `src` references `self`,
+ the Path instance.
+ """
+
+ module = os.path
+ """ The path module to use for path operations.
+
+ .. seealso:: :mod:`os.path`
+ """
+
+ def __init__(self, other=''):
+ if other is None:
+ raise TypeError("Invalid initial value for path: None")
+ with contextlib.suppress(AttributeError):
+ self._validate()
+
+ @classmethod
+ @functools.lru_cache
+ def using_module(cls, module):
+ subclass_name = cls.__name__ + '_' + module.__name__
+ bases = (cls,)
+ ns = {'module': module}
+ return type(subclass_name, bases, ns)
+
+ @classes.ClassProperty
+ @classmethod
+ def _next_class(cls):
+ """
+ What class should be used to construct new instances from this class
+ """
+ return cls
+
+ # --- Special Python methods.
+
+ def __repr__(self):
+ return '{}({})'.format(type(self).__name__, super().__repr__())
+
+ # Adding a Path and a string yields a Path.
+ def __add__(self, more):
+ return self._next_class(super().__add__(more))
+
+ def __radd__(self, other):
+ return self._next_class(other.__add__(self))
+
+ # The / operator joins Paths.
+ def __div__(self, rel):
+ """fp.__div__(rel) == fp / rel == fp.joinpath(rel)
+
+ Join two path components, adding a separator character if
+ needed.
+
+ .. seealso:: :func:`os.path.join`
+ """
+ return self._next_class(self.module.join(self, rel))
+
+ # Make the / operator work even when true division is enabled.
+ __truediv__ = __div__
+
+ # The / operator joins Paths the other way around
+ def __rdiv__(self, rel):
+ """fp.__rdiv__(rel) == rel / fp
+
+ Join two path components, adding a separator character if
+ needed.
+
+ .. seealso:: :func:`os.path.join`
+ """
+ return self._next_class(self.module.join(rel, self))
+
+ # Make the / operator work even when true division is enabled.
+ __rtruediv__ = __rdiv__
+
+ def __enter__(self):
+ self._old_dir = self.getcwd()
+ os.chdir(self)
+ return self
+
+ def __exit__(self, *_):
+ os.chdir(self._old_dir)
+
+ @classmethod
+ def getcwd(cls):
+ """Return the current working directory as a path object.
+
+ .. seealso:: :func:`os.getcwd`
+ """
+ return cls(os.getcwd())
+
+ #
+ # --- Operations on Path strings.
+
+ def abspath(self):
+ """.. seealso:: :func:`os.path.abspath`"""
+ return self._next_class(self.module.abspath(self))
+
+ def normcase(self):
+ """.. seealso:: :func:`os.path.normcase`"""
+ return self._next_class(self.module.normcase(self))
+
+ def normpath(self):
+ """.. seealso:: :func:`os.path.normpath`"""
+ return self._next_class(self.module.normpath(self))
+
+ def realpath(self):
+ """.. seealso:: :func:`os.path.realpath`"""
+ return self._next_class(self.module.realpath(self))
+
+ def expanduser(self):
+ """.. seealso:: :func:`os.path.expanduser`"""
+ return self._next_class(self.module.expanduser(self))
+
+ def expandvars(self):
+ """.. seealso:: :func:`os.path.expandvars`"""
+ return self._next_class(self.module.expandvars(self))
+
+ def dirname(self):
+ """.. seealso:: :attr:`parent`, :func:`os.path.dirname`"""
+ return self._next_class(self.module.dirname(self))
+
+ def basename(self):
+ """.. seealso:: :attr:`name`, :func:`os.path.basename`"""
+ return self._next_class(self.module.basename(self))
+
+ def expand(self):
+ """Clean up a filename by calling :meth:`expandvars()`,
+ :meth:`expanduser()`, and :meth:`normpath()` on it.
+
+ This is commonly everything needed to clean up a filename
+ read from a configuration file, for example.
+ """
+ return self.expandvars().expanduser().normpath()
+
+ @property
+ def stem(self):
+ """The same as :meth:`name`, but with one file extension stripped off.
+
+ >>> Path('/home/guido/python.tar.gz').stem
+ 'python.tar'
+ """
+ base, ext = self.module.splitext(self.name)
+ return base
+
+ @property
+ def ext(self):
+ """The file extension, for example ``'.py'``."""
+ f, ext = self.module.splitext(self)
+ return ext
+
+ def with_suffix(self, suffix):
+ """Return a new path with the file suffix changed (or added, if none)
+
+ >>> Path('/home/guido/python.tar.gz').with_suffix(".foo")
+ Path('/home/guido/python.tar.foo')
+
+ >>> Path('python').with_suffix('.zip')
+ Path('python.zip')
+
+ >>> Path('filename.ext').with_suffix('zip')
+ Traceback (most recent call last):
+ ...
+ ValueError: Invalid suffix 'zip'
+ """
+ if not suffix.startswith('.'):
+ raise ValueError(f"Invalid suffix {suffix!r}")
+
+ return self.stripext() + suffix
+
+ @property
+ def drive(self):
+ """The drive specifier, for example ``'C:'``.
+
+ This is always empty on systems that don't use drive specifiers.
+ """
+ drive, r = self.module.splitdrive(self)
+ return self._next_class(drive)
+
+ parent = property(
+ dirname,
+ None,
+ None,
+ """ This path's parent directory, as a new Path object.
+
+ For example,
+ ``Path('/usr/local/lib/libpython.so').parent ==
+ Path('/usr/local/lib')``
+
+ .. seealso:: :meth:`dirname`, :func:`os.path.dirname`
+ """,
+ )
+
+ name = property(
+ basename,
+ None,
+ None,
+ """ The name of this file or directory without the full path.
+
+ For example,
+ ``Path('/usr/local/lib/libpython.so').name == 'libpython.so'``
+
+ .. seealso:: :meth:`basename`, :func:`os.path.basename`
+ """,
+ )
+
+ def splitpath(self):
+ """Return two-tuple of ``.parent``, ``.name``.
+
+ .. seealso:: :attr:`parent`, :attr:`name`, :func:`os.path.split`
+ """
+ parent, child = self.module.split(self)
+ return self._next_class(parent), child
+
+ def splitdrive(self):
+ """Return two-tuple of ``.drive`` and rest without drive.
+
+ Split the drive specifier from this path. If there is
+ no drive specifier, :samp:`{p.drive}` is empty, so the return value
+ is simply ``(Path(''), p)``. This is always the case on Unix.
+
+ .. seealso:: :func:`os.path.splitdrive`
+ """
+ drive, rel = self.module.splitdrive(self)
+ return self._next_class(drive), self._next_class(rel)
+
+ def splitext(self):
+ """Return two-tuple of ``.stripext()`` and ``.ext``.
+
+ Split the filename extension from this path and return
+ the two parts. Either part may be empty.
+
+ The extension is everything from ``'.'`` to the end of the
+ last path segment. This has the property that if
+ ``(a, b) == p.splitext()``, then ``a + b == p``.
+
+ .. seealso:: :func:`os.path.splitext`
+ """
+ filename, ext = self.module.splitext(self)
+ return self._next_class(filename), ext
+
+ def stripext(self):
+ """Remove one file extension from the path.
+
+ For example, ``Path('/home/guido/python.tar.gz').stripext()``
+ returns ``Path('/home/guido/python.tar')``.
+ """
+ return self.splitext()[0]
+
+ @classes.multimethod
+ def joinpath(cls, first, *others):
+ """
+ Join first to zero or more :class:`Path` components,
+ adding a separator character (:samp:`{first}.module.sep`)
+ if needed. Returns a new instance of
+ :samp:`{first}._next_class`.
+
+ .. seealso:: :func:`os.path.join`
+ """
+ return cls._next_class(cls.module.join(first, *others))
+
+ def splitall(self):
+ r"""Return a list of the path components in this path.
+
+ The first item in the list will be a Path. Its value will be
+ either :data:`os.curdir`, :data:`os.pardir`, empty, or the root
+ directory of this path (for example, ``'/'`` or ``'C:\\'``). The
+ other items in the list will be strings.
+
+ ``Path.joinpath(*result)`` will yield the original path.
+
+ >>> Path('/foo/bar/baz').splitall()
+ [Path('/'), 'foo', 'bar', 'baz']
+ """
+ return list(self._parts())
+
+ def parts(self):
+ """
+ >>> Path('/foo/bar/baz').parts()
+ (Path('/'), 'foo', 'bar', 'baz')
+ """
+ return tuple(self._parts())
+
+ def _parts(self):
+ return reversed(tuple(self._parts_iter()))
+
+ def _parts_iter(self):
+ loc = self
+ while loc != os.curdir and loc != os.pardir:
+ prev = loc
+ loc, child = prev.splitpath()
+ if loc == prev:
+ break
+ yield child
+ yield loc
+
+ def relpath(self, start='.'):
+ """Return this path as a relative path,
+ based from `start`, which defaults to the current working directory.
+ """
+ cwd = self._next_class(start)
+ return cwd.relpathto(self)
+
+ def relpathto(self, dest):
+ """Return a relative path from `self` to `dest`.
+
+ If there is no relative path from `self` to `dest`, for example if
+ they reside on different drives in Windows, then this returns
+ ``dest.abspath()``.
+ """
+ origin = self.abspath()
+ dest = self._next_class(dest).abspath()
+
+ orig_list = origin.normcase().splitall()
+ # Don't normcase dest! We want to preserve the case.
+ dest_list = dest.splitall()
+
+ if orig_list[0] != self.module.normcase(dest_list[0]):
+ # Can't get here from there.
+ return dest
+
+ # Find the location where the two paths start to differ.
+ i = 0
+ for start_seg, dest_seg in zip(orig_list, dest_list):
+ if start_seg != self.module.normcase(dest_seg):
+ break
+ i += 1
+
+ # Now i is the point where the two paths diverge.
+ # Need a certain number of "os.pardir"s to work up
+ # from the origin to the point of divergence.
+ segments = [os.pardir] * (len(orig_list) - i)
+ # Need to add the diverging part of dest_list.
+ segments += dest_list[i:]
+ if len(segments) == 0:
+ # If they happen to be identical, use os.curdir.
+ relpath = os.curdir
+ else:
+ relpath = self.module.join(*segments)
+ return self._next_class(relpath)
+
+ # --- Listing, searching, walking, and matching
+
+ def listdir(self, match=None):
+ """List of items in this directory.
+
+ Use :meth:`files` or :meth:`dirs` instead if you want a listing
+ of just files or just subdirectories.
+
+ The elements of the list are Path objects.
+
+ With the optional `match` argument, a callable,
+ only return items whose names match the given pattern.
+
+ .. seealso:: :meth:`files`, :meth:`dirs`
+ """
+ match = matchers.load(match)
+ return list(filter(match, (self / child for child in os.listdir(self))))
+
+ def dirs(self, *args, **kwargs):
+ """List of this directory's subdirectories.
+
+ The elements of the list are Path objects.
+ This does not walk recursively into subdirectories
+ (but see :meth:`walkdirs`).
+
+ Accepts parameters to :meth:`listdir`.
+ """
+ return [p for p in self.listdir(*args, **kwargs) if p.isdir()]
+
+ def files(self, *args, **kwargs):
+ """List of the files in self.
+
+ The elements of the list are Path objects.
+ This does not walk into subdirectories (see :meth:`walkfiles`).
+
+ Accepts parameters to :meth:`listdir`.
+ """
+
+ return [p for p in self.listdir(*args, **kwargs) if p.isfile()]
+
+ def walk(self, match=None, errors='strict'):
+ """Iterator over files and subdirs, recursively.
+
+ The iterator yields Path objects naming each child item of
+ this directory and its descendants. This requires that
+ ``D.isdir()``.
+
+ This performs a depth-first traversal of the directory tree.
+ Each directory is returned just before all its children.
+
+ The `errors=` keyword argument controls behavior when an
+ error occurs. The default is ``'strict'``, which causes an
+ exception. Other allowed values are ``'warn'`` (which
+ reports the error via :func:`warnings.warn()`), and ``'ignore'``.
+ `errors` may also be an arbitrary callable taking a msg parameter.
+ """
+
+ errors = Handlers._resolve(errors)
+ match = matchers.load(match)
+
+ try:
+ childList = self.listdir()
+ except Exception as exc:
+ errors(f"Unable to list directory '{self}': {exc}")
+ return
+
+ for child in childList:
+ traverse = None
+ if match(child):
+ traverse = yield child
+ traverse = traverse or child.isdir
+ try:
+ do_traverse = traverse()
+ except Exception as exc:
+ errors(f"Unable to access '{child}': {exc}")
+ continue
+
+ if do_traverse:
+ yield from child.walk(errors=errors, match=match)
+
+ def walkdirs(self, *args, **kwargs):
+ """Iterator over subdirs, recursively."""
+ return (item for item in self.walk(*args, **kwargs) if item.isdir())
+
+ def walkfiles(self, *args, **kwargs):
+ """Iterator over files, recursively."""
+ return (item for item in self.walk(*args, **kwargs) if item.isfile())
+
+ def fnmatch(self, pattern, normcase=None):
+ """Return ``True`` if `self.name` matches the given `pattern`.
+
+ `pattern` - A filename pattern with wildcards,
+ for example ``'*.py'``. If the pattern contains a `normcase`
+ attribute, it is applied to the name and path prior to comparison.
+
+ `normcase` - (optional) A function used to normalize the pattern and
+ filename before matching. Defaults to normcase from
+ ``self.module``, :func:`os.path.normcase`.
+
+ .. seealso:: :func:`fnmatch.fnmatch`
+ """
+ default_normcase = getattr(pattern, 'normcase', self.module.normcase)
+ normcase = normcase or default_normcase
+ name = normcase(self.name)
+ pattern = normcase(pattern)
+ return fnmatch.fnmatchcase(name, pattern)
+
+ def glob(self, pattern):
+ """Return a list of Path objects that match the pattern.
+
+ `pattern` - a path relative to this directory, with wildcards.
+
+ For example, ``Path('/users').glob('*/bin/*')`` returns a list
+ of all the files users have in their :file:`bin` directories.
+
+ .. seealso:: :func:`glob.glob`
+
+ .. note:: Glob is **not** recursive, even when using ``**``.
+ To do recursive globbing see :func:`walk`,
+ :func:`walkdirs` or :func:`walkfiles`.
+ """
+ cls = self._next_class
+ return [cls(s) for s in glob.glob(self / pattern)]
+
+ def iglob(self, pattern):
+ """Return an iterator of Path objects that match the pattern.
+
+ `pattern` - a path relative to this directory, with wildcards.
+
+ For example, ``Path('/users').iglob('*/bin/*')`` returns an
+ iterator of all the files users have in their :file:`bin`
+ directories.
+
+ .. seealso:: :func:`glob.iglob`
+
+ .. note:: Glob is **not** recursive, even when using ``**``.
+ To do recursive globbing see :func:`walk`,
+ :func:`walkdirs` or :func:`walkfiles`.
+ """
+ cls = self._next_class
+ return (cls(s) for s in glob.iglob(self / pattern))
+
+ #
+ # --- Reading or writing an entire file at once.
+
+ def open(self, *args, **kwargs):
+ """Open this file and return a corresponding file object.
+
+ Keyword arguments work as in :func:`io.open`. If the file cannot be
+ opened, an :class:`OSError` is raised.
+ """
+ return open(self, *args, **kwargs)
+
+ def bytes(self):
+ """Open this file, read all bytes, return them as a string."""
+ with self.open('rb') as f:
+ return f.read()
+
+ def chunks(self, size, *args, **kwargs):
+ """Returns a generator yielding chunks of the file, so it can
+ be read piece by piece with a simple for loop.
+
+ Any argument you pass after `size` will be passed to :meth:`open`.
+
+ :example:
+
+ >>> hash = hashlib.md5()
+ >>> for chunk in Path("NEWS.rst").chunks(8192, mode='rb'):
+ ... hash.update(chunk)
+
+ This will read the file by chunks of 8192 bytes.
+ """
+ with self.open(*args, **kwargs) as f:
+ yield from iter(lambda: f.read(size) or None, None)
+
+ def write_bytes(self, bytes, append=False):
+ """Open this file and write the given bytes to it.
+
+ Default behavior is to overwrite any existing file.
+ Call ``p.write_bytes(bytes, append=True)`` to append instead.
+ """
+ with self.open('ab' if append else 'wb') as f:
+ f.write(bytes)
+
+ def read_text(self, encoding=None, errors=None):
+ r"""Open this file, read it in, return the content as a string.
+
+ Optional parameters are passed to :meth:`open`.
+
+ .. seealso:: :meth:`lines`
+ """
+ with self.open(encoding=encoding, errors=errors) as f:
+ return f.read()
+
+ def read_bytes(self):
+ r"""Return the contents of this file as bytes."""
+ with self.open(mode='rb') as f:
+ return f.read()
+
+ def text(self, encoding=None, errors='strict'):
+ r"""Legacy function to read text.
+
+ Converts all newline sequences to ``\n``.
+ """
+ warnings.warn(
+ ".text is deprecated; use read_text",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return U_NEWLINE.sub('\n', self.read_text(encoding, errors))
+
+ def write_text(
+ self, text, encoding=None, errors='strict', linesep=os.linesep, append=False
+ ):
+ r"""Write the given text to this file.
+
+ The default behavior is to overwrite any existing file;
+ to append instead, use the `append=True` keyword argument.
+
+ There are two differences between :meth:`write_text` and
+ :meth:`write_bytes`: newline handling and Unicode handling.
+ See below.
+
+ Parameters:
+
+ `text` - str/bytes - The text to be written.
+
+ `encoding` - str - The text encoding used.
+
+ `errors` - str - How to handle Unicode encoding errors.
+ Default is ``'strict'``. See ``help(unicode.encode)`` for the
+ options. Ignored if `text` isn't a Unicode string.
+
+ `linesep` - keyword argument - str/unicode - The sequence of
+ characters to be used to mark end-of-line. The default is
+ :data:`os.linesep`. Specify ``None`` to
+ use newlines unmodified.
+
+ `append` - keyword argument - bool - Specifies what to do if
+ the file already exists (``True``: append to the end of it;
+ ``False``: overwrite it). The default is ``False``.
+
+
+ --- Newline handling.
+
+ ``write_text()`` converts all standard end-of-line sequences
+ (``'\n'``, ``'\r'``, and ``'\r\n'``) to your platform's default
+ end-of-line sequence (see :data:`os.linesep`; on Windows, for example,
+ the end-of-line marker is ``'\r\n'``).
+
+ To override the platform's default, pass the `linesep=`
+ keyword argument. To preserve the newlines as-is, pass
+ ``linesep=None``.
+
+ This handling applies to Unicode text and bytes, except
+ with Unicode, additional non-ASCII newlines are recognized:
+ ``\x85``, ``\r\x85``, and ``\u2028``.
+
+ --- Unicode
+
+ If `text` isn't Unicode, then apart from newline handling, the
+ bytes are written verbatim to the file. The `encoding` and
+ `errors` arguments are not used and must be omitted.
+
+ If `text` is Unicode, it is first converted to :func:`bytes` using the
+ specified `encoding` (or the default encoding if `encoding`
+ isn't specified). The `errors` argument applies only to this
+ conversion.
+ """
+ if isinstance(text, str):
+ if linesep is not None:
+ text = U_NEWLINE.sub(linesep, text)
+ bytes = text.encode(encoding or sys.getdefaultencoding(), errors)
+ else:
+ warnings.warn(
+ "Writing bytes in write_text is deprecated",
+ DeprecationWarning,
+ stacklevel=1,
+ )
+ assert encoding is None
+ if linesep is not None:
+ text = B_NEWLINE.sub(linesep.encode(), text)
+ bytes = text
+ self.write_bytes(bytes, append=append)
+
+ def lines(self, encoding=None, errors=None, retain=True):
+ r"""Open this file, read all lines, return them in a list.
+
+ Optional arguments:
+ `encoding` - The Unicode encoding (or character set) of
+ the file. The default is ``None``, meaning use
+ ``locale.getpreferredencoding()``.
+ `errors` - How to handle Unicode errors; see
+ `open <https://docs.python.org/3/library/functions.html#open>`_
+ for the options. Default is ``None`` meaning "strict".
+ `retain` - If ``True`` (default), retain newline characters,
+ but translate all newline
+ characters to ``\n``. If ``False``, newline characters are
+ omitted.
+
+ .. seealso:: :meth:`text`
+ """
+ text = U_NEWLINE.sub('\n', self.read_text(encoding, errors))
+ return text.splitlines(retain)
+
+ def write_lines(
+ self,
+ lines,
+ encoding=None,
+ errors='strict',
+ linesep=_default_linesep,
+ append=False,
+ ):
+ r"""Write the given lines of text to this file.
+
+ By default this overwrites any existing file at this path.
+
+ This puts a platform-specific newline sequence on every line.
+ See `linesep` below.
+
+ `lines` - A list of strings.
+
+ `encoding` - A Unicode encoding to use. This applies only if
+ `lines` contains any Unicode strings.
+
+ `errors` - How to handle errors in Unicode encoding. This
+ also applies only to Unicode strings.
+
+ linesep - (deprecated) The desired line-ending. This line-ending is
+ applied to every line. If a line already has any
+ standard line ending (``'\r'``, ``'\n'``, ``'\r\n'``,
+ ``u'\x85'``, ``u'\r\x85'``, ``u'\u2028'``), that will
+ be stripped off and this will be used instead. The
+ default is os.linesep, which is platform-dependent
+ (``'\r\n'`` on Windows, ``'\n'`` on Unix, etc.).
+ Specify ``None`` to write the lines as-is, like
+ ``.writelines`` on a file object.
+
+ Use the keyword argument ``append=True`` to append lines to the
+ file. The default is to overwrite the file.
+ """
+ mode = 'a' if append else 'w'
+ with self.open(mode, encoding=encoding, errors=errors, newline='') as f:
+ f.writelines(self._replace_linesep(lines, linesep))
+
+ @staticmethod
+ def _replace_linesep(lines, linesep):
+ if linesep != _default_linesep:
+ warnings.warn("linesep is deprecated", DeprecationWarning, stacklevel=3)
+ else:
+ linesep = os.linesep
+ if linesep is None:
+ return lines
+
+ return (line + linesep for line in _strip_newlines(lines))
+
+ def read_md5(self):
+ """Calculate the md5 hash for this file.
+
+ This reads through the entire file.
+
+ .. seealso:: :meth:`read_hash`
+ """
+ return self.read_hash('md5')
+
+ def _hash(self, hash_name):
+ """Returns a hash object for the file at the current path.
+
+ `hash_name` should be a hash algo name (such as ``'md5'``
+ or ``'sha1'``) that's available in the :mod:`hashlib` module.
+ """
+ m = hashlib.new(hash_name)
+ for chunk in self.chunks(8192, mode="rb"):
+ m.update(chunk)
+ return m
+
+ def read_hash(self, hash_name):
+ """Calculate given hash for this file.
+
+ List of supported hashes can be obtained from :mod:`hashlib` package.
+ This reads the entire file.
+
+ .. seealso:: :meth:`hashlib.hash.digest`
+ """
+ return self._hash(hash_name).digest()
+
+ def read_hexhash(self, hash_name):
+ """Calculate given hash for this file, returning hexdigest.
+
+ List of supported hashes can be obtained from :mod:`hashlib` package.
+ This reads the entire file.
+
+ .. seealso:: :meth:`hashlib.hash.hexdigest`
+ """
+ return self._hash(hash_name).hexdigest()
+
+ # --- Methods for querying the filesystem.
+ # N.B. On some platforms, the os.path functions may be implemented in C
+ # (e.g. isdir on Windows, Python 3.2.2), and compiled functions don't get
+ # bound. Playing it safe and wrapping them all in method calls.
+
+ def isabs(self):
+ """
+ >>> Path('.').isabs()
+ False
+
+ .. seealso:: :func:`os.path.isabs`
+ """
+ return self.module.isabs(self)
+
+ def exists(self):
+ """.. seealso:: :func:`os.path.exists`"""
+ return self.module.exists(self)
+
+ def isdir(self):
+ """.. seealso:: :func:`os.path.isdir`"""
+ return self.module.isdir(self)
+
+ def isfile(self):
+ """.. seealso:: :func:`os.path.isfile`"""
+ return self.module.isfile(self)
+
+ def islink(self):
+ """.. seealso:: :func:`os.path.islink`"""
+ return self.module.islink(self)
+
+ def ismount(self):
+ """
+ >>> Path('.').ismount()
+ False
+
+ .. seealso:: :func:`os.path.ismount`
+ """
+ return self.module.ismount(self)
+
+ def samefile(self, other):
+ """.. seealso:: :func:`os.path.samefile`"""
+ return self.module.samefile(self, other)
+
+ def getatime(self):
+ """.. seealso:: :attr:`atime`, :func:`os.path.getatime`"""
+ return self.module.getatime(self)
+
+ def set_atime(self, value):
+ mtime_ns = self.stat().st_atime_ns
+ self.utime(ns=(_make_timestamp_ns(value), mtime_ns))
+
+ atime = property(
+ getatime,
+ set_atime,
+ None,
+ """
+ Last access time of the file.
+
+ >>> Path('.').atime > 0
+ True
+
+ Allows setting:
+
+ >>> some_file = Path(getfixture('tmp_path')).joinpath('file.txt').touch()
+ >>> MST = datetime.timezone(datetime.timedelta(hours=-7))
+ >>> some_file.atime = datetime.datetime(1976, 5, 7, 10, tzinfo=MST)
+ >>> some_file.atime
+ 200336400.0
+
+ .. seealso:: :meth:`getatime`, :func:`os.path.getatime`
+ """,
+ )
+
+ def getmtime(self):
+ """.. seealso:: :attr:`mtime`, :func:`os.path.getmtime`"""
+ return self.module.getmtime(self)
+
+ def set_mtime(self, value):
+ atime_ns = self.stat().st_atime_ns
+ self.utime(ns=(atime_ns, _make_timestamp_ns(value)))
+
+ mtime = property(
+ getmtime,
+ set_mtime,
+ None,
+ """
+ Last modified time of the file.
+
+ Allows setting:
+
+ >>> some_file = Path(getfixture('tmp_path')).joinpath('file.txt').touch()
+ >>> MST = datetime.timezone(datetime.timedelta(hours=-7))
+ >>> some_file.mtime = datetime.datetime(1976, 5, 7, 10, tzinfo=MST)
+ >>> some_file.mtime
+ 200336400.0
+
+ .. seealso:: :meth:`getmtime`, :func:`os.path.getmtime`
+ """,
+ )
+
+ def getctime(self):
+ """.. seealso:: :attr:`ctime`, :func:`os.path.getctime`"""
+ return self.module.getctime(self)
+
+ ctime = property(
+ getctime,
+ None,
+ None,
+ """ Creation time of the file.
+
+ .. seealso:: :meth:`getctime`, :func:`os.path.getctime`
+ """,
+ )
+
+ def getsize(self):
+ """.. seealso:: :attr:`size`, :func:`os.path.getsize`"""
+ return self.module.getsize(self)
+
+ size = property(
+ getsize,
+ None,
+ None,
+ """ Size of the file, in bytes.
+
+ .. seealso:: :meth:`getsize`, :func:`os.path.getsize`
+ """,
+ )
+
+ @property
+ def permissions(self) -> masks.Permissions:
+ """
+ Permissions.
+
+ >>> perms = Path('.').permissions
+ >>> isinstance(perms, int)
+ True
+ >>> set(perms.symbolic) <= set('rwx-')
+ True
+ >>> perms.symbolic
+ 'r...'
+ """
+ return masks.Permissions(self.stat().st_mode)
+
+ def access(self, *args, **kwargs):
+ """
+ Return does the real user have access to this path.
+
+ >>> Path('.').access(os.F_OK)
+ True
+
+ .. seealso:: :func:`os.access`
+ """
+ return os.access(self, *args, **kwargs)
+
+ def stat(self):
+ """
+ Perform a ``stat()`` system call on this path.
+
+ >>> Path('.').stat()
+ os.stat_result(...)
+
+ .. seealso:: :meth:`lstat`, :func:`os.stat`
+ """
+ return os.stat(self)
+
+ def lstat(self):
+ """
+ Like :meth:`stat`, but do not follow symbolic links.
+
+ >>> Path('.').lstat() == Path('.').stat()
+ True
+
+ .. seealso:: :meth:`stat`, :func:`os.lstat`
+ """
+ return os.lstat(self)
+
+ def __get_owner_windows(self): # pragma: nocover
+ r"""
+ Return the name of the owner of this file or directory. Follow
+ symbolic links.
+
+ Return a name of the form ``DOMAIN\User Name``; may be a group.
+
+ .. seealso:: :attr:`owner`
+ """
+ desc = win32security.GetFileSecurity(
+ self, win32security.OWNER_SECURITY_INFORMATION
+ )
+ sid = desc.GetSecurityDescriptorOwner()
+ account, domain, typecode = win32security.LookupAccountSid(None, sid)
+ return domain + '\\' + account
+
+ def __get_owner_unix(self): # pragma: nocover
+ """
+ Return the name of the owner of this file or directory. Follow
+ symbolic links.
+
+ .. seealso:: :attr:`owner`
+ """
+ st = self.stat()
+ return pwd.getpwuid(st.st_uid).pw_name
+
+ def __get_owner_not_implemented(self): # pragma: nocover
+ raise NotImplementedError("Ownership not available on this platform.")
+
+ get_owner = (
+ __get_owner_windows
+ if 'win32security' in globals()
+ else __get_owner_unix
+ if 'pwd' in globals()
+ else __get_owner_not_implemented
+ )
+
+ owner = property(
+ get_owner,
+ None,
+ None,
+ """ Name of the owner of this file or directory.
+
+ .. seealso:: :meth:`get_owner`""",
+ )
+
+ if hasattr(os, 'statvfs'):
+
+ def statvfs(self):
+ """Perform a ``statvfs()`` system call on this path.
+
+ .. seealso:: :func:`os.statvfs`
+ """
+ return os.statvfs(self)
+
+ if hasattr(os, 'pathconf'):
+
+ def pathconf(self, name):
+ """.. seealso:: :func:`os.pathconf`"""
+ return os.pathconf(self, name)
+
+ #
+ # --- Modifying operations on files and directories
+
+ def utime(self, *args, **kwargs):
+ """Set the access and modified times of this file.
+
+ .. seealso:: :func:`os.utime`
+ """
+ os.utime(self, *args, **kwargs)
+ return self
+
+ def chmod(self, mode):
+ """
+ Set the mode. May be the new mode (os.chmod behavior) or a `symbolic
+ mode <http://en.wikipedia.org/wiki/Chmod#Symbolic_modes>`_.
+
+ >>> a_file = Path(getfixture('tmp_path')).joinpath('afile.txt').touch()
+ >>> a_file.chmod(0o700)
+ Path(...
+ >>> a_file.chmod('u+x')
+ Path(...
+
+ .. seealso:: :func:`os.chmod`
+ """
+ if isinstance(mode, str):
+ mask = masks.compound(mode)
+ mode = mask(self.stat().st_mode)
+ os.chmod(self, mode)
+ return self
+
+ if hasattr(os, 'chown'):
+
+ def chown(self, uid=-1, gid=-1):
+ """
+ Change the owner and group by names or numbers.
+
+ .. seealso:: :func:`os.chown`
+ """
+
+ def resolve_uid(uid):
+ return uid if isinstance(uid, int) else pwd.getpwnam(uid).pw_uid
+
+ def resolve_gid(gid):
+ return gid if isinstance(gid, int) else grp.getgrnam(gid).gr_gid
+
+ os.chown(self, resolve_uid(uid), resolve_gid(gid))
+ return self
+
+ def rename(self, new):
+ """.. seealso:: :func:`os.rename`"""
+ os.rename(self, new)
+ return self._next_class(new)
+
+ def renames(self, new):
+ """.. seealso:: :func:`os.renames`"""
+ os.renames(self, new)
+ return self._next_class(new)
+
+ #
+ # --- Create/delete operations on directories
+
+ def mkdir(self, mode=0o777):
+ """.. seealso:: :func:`os.mkdir`"""
+ os.mkdir(self, mode)
+ return self
+
+ def mkdir_p(self, mode=0o777):
+ """Like :meth:`mkdir`, but does not raise an exception if the
+ directory already exists."""
+ with contextlib.suppress(FileExistsError):
+ self.mkdir(mode)
+ return self
+
+ def makedirs(self, mode=0o777):
+ """.. seealso:: :func:`os.makedirs`"""
+ os.makedirs(self, mode)
+ return self
+
+ def makedirs_p(self, mode=0o777):
+ """Like :meth:`makedirs`, but does not raise an exception if the
+ directory already exists."""
+ with contextlib.suppress(FileExistsError):
+ self.makedirs(mode)
+ return self
+
+ def rmdir(self):
+ """.. seealso:: :func:`os.rmdir`"""
+ os.rmdir(self)
+ return self
+
+ def rmdir_p(self):
+ """Like :meth:`rmdir`, but does not raise an exception if the
+ directory is not empty or does not exist."""
+ suppressed = FileNotFoundError, FileExistsError, DirectoryNotEmpty
+ with contextlib.suppress(suppressed):
+ with DirectoryNotEmpty.translate():
+ self.rmdir()
+ return self
+
+ def removedirs(self):
+ """.. seealso:: :func:`os.removedirs`"""
+ os.removedirs(self)
+ return self
+
+ def removedirs_p(self):
+ """Like :meth:`removedirs`, but does not raise an exception if the
+ directory is not empty or does not exist."""
+ with contextlib.suppress(FileExistsError, DirectoryNotEmpty):
+ with DirectoryNotEmpty.translate():
+ self.removedirs()
+ return self
+
+ # --- Modifying operations on files
+
+ def touch(self):
+ """Set the access/modified times of this file to the current time.
+ Create the file if it does not exist.
+ """
+ os.close(os.open(self, os.O_WRONLY | os.O_CREAT, 0o666))
+ os.utime(self, None)
+ return self
+
+ def remove(self):
+ """.. seealso:: :func:`os.remove`"""
+ os.remove(self)
+ return self
+
+ def remove_p(self):
+ """Like :meth:`remove`, but does not raise an exception if the
+ file does not exist."""
+ with contextlib.suppress(FileNotFoundError):
+ self.unlink()
+ return self
+
+ unlink = remove
+ unlink_p = remove_p
+
+ # --- Links
+
+ def link(self, newpath):
+ """Create a hard link at `newpath`, pointing to this file.
+
+ .. seealso:: :func:`os.link`
+ """
+ os.link(self, newpath)
+ return self._next_class(newpath)
+
+ def symlink(self, newlink=None):
+ """Create a symbolic link at `newlink`, pointing here.
+
+ If newlink is not supplied, the symbolic link will assume
+ the name self.basename(), creating the link in the cwd.
+
+ .. seealso:: :func:`os.symlink`
+ """
+ if newlink is None:
+ newlink = self.basename()
+ os.symlink(self, newlink)
+ return self._next_class(newlink)
+
+ def readlink(self):
+ """Return the path to which this symbolic link points.
+
+ The result may be an absolute or a relative path.
+
+ .. seealso:: :meth:`readlinkabs`, :func:`os.readlink`
+ """
+ return self._next_class(os.readlink(self))
+
+ def readlinkabs(self):
+ """Return the path to which this symbolic link points.
+
+ The result is always an absolute path.
+
+ .. seealso:: :meth:`readlink`, :func:`os.readlink`
+ """
+ p = self.readlink()
+ return p if p.isabs() else (self.parent / p).abspath()
+
+ # High-level functions from shutil
+ # These functions will be bound to the instance such that
+ # Path(name).copy(target) will invoke shutil.copy(name, target)
+
+ copyfile = shutil.copyfile
+ copymode = shutil.copymode
+ copystat = shutil.copystat
+ copy = shutil.copy
+ copy2 = shutil.copy2
+ copytree = shutil.copytree
+ if hasattr(shutil, 'move'):
+ move = shutil.move
+ rmtree = shutil.rmtree
+
+ def rmtree_p(self):
+ """Like :meth:`rmtree`, but does not raise an exception if the
+ directory does not exist."""
+ with contextlib.suppress(FileNotFoundError):
+ self.rmtree()
+ return self
+
+ def chdir(self):
+ """.. seealso:: :func:`os.chdir`"""
+ os.chdir(self)
+
+ cd = chdir
+
+ def merge_tree(
+ self,
+ dst,
+ symlinks=False,
+ *,
+ copy_function=shutil.copy2,
+ ignore=lambda dir, contents: [],
+ ):
+ """
+ Copy entire contents of self to dst, overwriting existing
+ contents in dst with those in self.
+
+ Pass ``symlinks=True`` to copy symbolic links as links.
+
+ Accepts a ``copy_function``, similar to copytree.
+
+ To avoid overwriting newer files, supply a copy function
+ wrapped in ``only_newer``. For example::
+
+ src.merge_tree(dst, copy_function=only_newer(shutil.copy2))
+ """
+ dst = self._next_class(dst)
+ dst.makedirs_p()
+
+ sources = self.listdir()
+ _ignored = ignore(self, [item.name for item in sources])
+
+ def ignored(item):
+ return item.name in _ignored
+
+ for source in itertools.filterfalse(ignored, sources):
+ dest = dst / source.name
+ if symlinks and source.islink():
+ target = source.readlink()
+ target.symlink(dest)
+ elif source.isdir():
+ source.merge_tree(
+ dest,
+ symlinks=symlinks,
+ copy_function=copy_function,
+ ignore=ignore,
+ )
+ else:
+ copy_function(source, dest)
+
+ self.copystat(dst)
+
+ #
+ # --- Special stuff from os
+
+ if hasattr(os, 'chroot'):
+
+ def chroot(self): # pragma: nocover
+ """.. seealso:: :func:`os.chroot`"""
+ os.chroot(self)
+
+ if hasattr(os, 'startfile'):
+
+ def startfile(self, *args, **kwargs): # pragma: nocover
+ """.. seealso:: :func:`os.startfile`"""
+ os.startfile(self, *args, **kwargs)
+ return self
+
+ # in-place re-writing, courtesy of Martijn Pieters
+ # http://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/
+ @contextlib.contextmanager
+ def in_place(
+ self,
+ mode='r',
+ buffering=-1,
+ encoding=None,
+ errors=None,
+ newline=None,
+ backup_extension=None,
+ ):
+ """
+ A context in which a file may be re-written in-place with
+ new content.
+
+ Yields a tuple of :samp:`({readable}, {writable})` file
+ objects, where `writable` replaces `readable`.
+
+ If an exception occurs, the old file is restored, removing the
+ written data.
+
+ Mode *must not* use ``'w'``, ``'a'``, or ``'+'``; only
+ read-only-modes are allowed. A :exc:`ValueError` is raised
+ on invalid modes.
+
+ For example, to add line numbers to a file::
+
+ p = Path(filename)
+ assert p.isfile()
+ with p.in_place() as (reader, writer):
+ for number, line in enumerate(reader, 1):
+ writer.write('{0:3}: '.format(number)))
+ writer.write(line)
+
+ Thereafter, the file at `filename` will have line numbers in it.
+ """
+ if set(mode).intersection('wa+'):
+ raise ValueError('Only read-only file modes can be used')
+
+ # move existing file to backup, create new file with same permissions
+ # borrowed extensively from the fileinput module
+ backup_fn = self + (backup_extension or os.extsep + 'bak')
+ backup_fn.remove_p()
+ self.rename(backup_fn)
+ readable = open(
+ backup_fn,
+ mode,
+ buffering=buffering,
+ encoding=encoding,
+ errors=errors,
+ newline=newline,
+ )
+ try:
+ perm = os.fstat(readable.fileno()).st_mode
+ except OSError:
+ writable = self.open(
+ 'w' + mode.replace('r', ''),
+ buffering=buffering,
+ encoding=encoding,
+ errors=errors,
+ newline=newline,
+ )
+ else:
+ os_mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
+ os_mode |= getattr(os, 'O_BINARY', 0)
+ fd = os.open(self, os_mode, perm)
+ writable = open(
+ fd,
+ "w" + mode.replace('r', ''),
+ buffering=buffering,
+ encoding=encoding,
+ errors=errors,
+ newline=newline,
+ )
+ with contextlib.suppress(OSError, AttributeError):
+ self.chmod(perm)
+ try:
+ yield readable, writable
+ except Exception:
+ # move backup back
+ readable.close()
+ writable.close()
+ self.remove_p()
+ backup_fn.rename(self)
+ raise
+ else:
+ readable.close()
+ writable.close()
+ finally:
+ backup_fn.remove_p()
+
+ @classes.ClassProperty
+ @classmethod
+ def special(cls):
+ """
+ Return a SpecialResolver object suitable referencing a suitable
+ directory for the relevant platform for the given
+ type of content.
+
+ For example, to get a user config directory, invoke:
+
+ dir = Path.special().user.config
+
+ Uses the `appdirs
+ <https://pypi.python.org/pypi/appdirs/1.4.0>`_ to resolve
+ the paths in a platform-friendly way.
+
+ To create a config directory for 'My App', consider:
+
+ dir = Path.special("My App").user.config.makedirs_p()
+
+ If the ``appdirs`` module is not installed, invocation
+ of special will raise an ImportError.
+ """
+ return functools.partial(SpecialResolver, cls)
+
+
+class DirectoryNotEmpty(OSError):
+ @staticmethod
+ @contextlib.contextmanager
+ def translate():
+ try:
+ yield
+ except OSError as exc:
+ if exc.errno == errno.ENOTEMPTY:
+ raise DirectoryNotEmpty(*exc.args) from exc
+ raise
+
+
+def only_newer(copy_func):
+ """
+ Wrap a copy function (like shutil.copy2) to return
+ the dst if it's newer than the source.
+ """
+
+ @functools.wraps(copy_func)
+ def wrapper(src, dst, *args, **kwargs):
+ is_newer_dst = dst.exists() and dst.getmtime() >= src.getmtime()
+ if is_newer_dst:
+ return dst
+ return copy_func(src, dst, *args, **kwargs)
+
+ return wrapper
+
+
+class ExtantPath(Path):
+ """
+ >>> ExtantPath('.')
+ ExtantPath('.')
+ >>> ExtantPath('does-not-exist')
+ Traceback (most recent call last):
+ OSError: does-not-exist does not exist.
+ """
+
+ def _validate(self):
+ if not self.exists():
+ raise OSError(f"{self} does not exist.")
+
+
+class ExtantFile(Path):
+ """
+ >>> ExtantFile('.')
+ Traceback (most recent call last):
+ FileNotFoundError: . does not exist as a file.
+ >>> ExtantFile('does-not-exist')
+ Traceback (most recent call last):
+ FileNotFoundError: does-not-exist does not exist as a file.
+ """
+
+ def _validate(self):
+ if not self.isfile():
+ raise FileNotFoundError(f"{self} does not exist as a file.")
+
+
+class SpecialResolver:
+ class ResolverScope:
+ def __init__(self, paths, scope):
+ self.paths = paths
+ self.scope = scope
+
+ def __getattr__(self, class_):
+ return self.paths.get_dir(self.scope, class_)
+
+ def __init__(self, path_class, *args, **kwargs):
+ appdirs = importlib.import_module('appdirs')
+
+ vars(self).update(
+ path_class=path_class, wrapper=appdirs.AppDirs(*args, **kwargs)
+ )
+
+ def __getattr__(self, scope):
+ return self.ResolverScope(self, scope)
+
+ def get_dir(self, scope, class_):
+ """
+ Return the callable function from appdirs, but with the
+ result wrapped in self.path_class
+ """
+ prop_name = f'{scope}_{class_}_dir'
+ value = getattr(self.wrapper, prop_name)
+ MultiPath = Multi.for_class(self.path_class)
+ return MultiPath.detect(value)
+
+
+class Multi:
+ """
+ A mix-in for a Path which may contain multiple Path separated by pathsep.
+ """
+
+ @classmethod
+ def for_class(cls, path_cls):
+ name = 'Multi' + path_cls.__name__
+ return type(name, (cls, path_cls), {})
+
+ @classmethod
+ def detect(cls, input):
+ if os.pathsep not in input:
+ cls = cls._next_class
+ return cls(input)
+
+ def __iter__(self):
+ return iter(map(self._next_class, self.split(os.pathsep)))
+
+ @classes.ClassProperty
+ @classmethod
+ def _next_class(cls):
+ """
+ Multi-subclasses should use the parent class
+ """
+ return next(class_ for class_ in cls.__mro__ if not issubclass(class_, Multi))
+
+
+class TempDir(Path):
+ """
+ A temporary directory via :func:`tempfile.mkdtemp`, and
+ constructed with the same parameters that you can use
+ as a context manager.
+
+ For example:
+
+ >>> with TempDir() as d:
+ ... d.isdir() and isinstance(d, Path)
+ True
+
+ The directory is deleted automatically.
+
+ >>> d.isdir()
+ False
+
+ .. seealso:: :func:`tempfile.mkdtemp`
+ """
+
+ @classes.ClassProperty
+ @classmethod
+ def _next_class(cls):
+ return Path
+
+ def __new__(cls, *args, **kwargs):
+ dirname = tempfile.mkdtemp(*args, **kwargs)
+ return super().__new__(cls, dirname)
+
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def __enter__(self):
+ # TempDir should return a Path version of itself and not itself
+ # so that a second context manager does not create a second
+ # temporary directory, but rather changes CWD to the location
+ # of the temporary directory.
+ return self._next_class(self)
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.rmtree()
+
+
+class Handlers:
+ def strict(msg):
+ raise
+
+ def warn(msg):
+ warnings.warn(msg, TreeWalkWarning)
+
+ def ignore(msg):
+ pass
+
+ @classmethod
+ def _resolve(cls, param):
+ if not callable(param) and param not in vars(Handlers):
+ raise ValueError("invalid errors parameter")
+ return vars(cls).get(param, param)
diff --git a/contrib/python/path/path/classes.py b/contrib/python/path/path/classes.py
new file mode 100644
index 0000000000..b6101d0a7e
--- /dev/null
+++ b/contrib/python/path/path/classes.py
@@ -0,0 +1,27 @@
+import functools
+
+
+class ClassProperty(property):
+ def __get__(self, cls, owner):
+ return self.fget.__get__(None, owner)()
+
+
+class multimethod:
+ """
+ Acts like a classmethod when invoked from the class and like an
+ instancemethod when invoked from the instance.
+ """
+
+ def __init__(self, func):
+ self.func = func
+
+ def __get__(self, instance, owner):
+ """
+ If called on an instance, pass the instance as the first
+ argument.
+ """
+ return (
+ functools.partial(self.func, owner)
+ if instance is None
+ else functools.partial(self.func, owner, instance)
+ )
diff --git a/contrib/python/path/path/masks.py b/contrib/python/path/path/masks.py
new file mode 100644
index 0000000000..e7037e9603
--- /dev/null
+++ b/contrib/python/path/path/masks.py
@@ -0,0 +1,159 @@
+import re
+import functools
+import operator
+import itertools
+
+
+# from jaraco.functools
+def compose(*funcs):
+ compose_two = lambda f1, f2: lambda *args, **kwargs: f1(f2(*args, **kwargs)) # noqa
+ return functools.reduce(compose_two, funcs)
+
+
+# from jaraco.structures.binary
+def gen_bit_values(number):
+ """
+ Return a zero or one for each bit of a numeric value up to the most
+ significant 1 bit, beginning with the least significant bit.
+
+ >>> list(gen_bit_values(16))
+ [0, 0, 0, 0, 1]
+ """
+ digits = bin(number)[2:]
+ return map(int, reversed(digits))
+
+
+# from more_itertools
+def padded(iterable, fillvalue=None, n=None, next_multiple=False):
+ """Yield the elements from *iterable*, followed by *fillvalue*, such that
+ at least *n* items are emitted.
+
+ >>> list(padded([1, 2, 3], '?', 5))
+ [1, 2, 3, '?', '?']
+
+ If *next_multiple* is ``True``, *fillvalue* will be emitted until the
+ number of items emitted is a multiple of *n*::
+
+ >>> list(padded([1, 2, 3, 4], n=3, next_multiple=True))
+ [1, 2, 3, 4, None, None]
+
+ If *n* is ``None``, *fillvalue* will be emitted indefinitely.
+
+ """
+ it = iter(iterable)
+ if n is None:
+ yield from itertools.chain(it, itertools.repeat(fillvalue))
+ elif n < 1:
+ raise ValueError('n must be at least 1')
+ else:
+ item_count = 0
+ for item in it:
+ yield item
+ item_count += 1
+
+ remaining = (n - item_count) % n if next_multiple else n - item_count
+ for _ in range(remaining):
+ yield fillvalue
+
+
+def compound(mode):
+ """
+ Support multiple, comma-separated Unix chmod symbolic modes.
+
+ >>> oct(compound('a=r,u+w')(0))
+ '0o644'
+ """
+ return compose(*map(simple, reversed(mode.split(','))))
+
+
+def simple(mode):
+ """
+ Convert a Unix chmod symbolic mode like ``'ugo+rwx'`` to a function
+ suitable for applying to a mask to affect that change.
+
+ >>> mask = simple('ugo+rwx')
+ >>> mask(0o554) == 0o777
+ True
+
+ >>> simple('go-x')(0o777) == 0o766
+ True
+
+ >>> simple('o-x')(0o445) == 0o444
+ True
+
+ >>> simple('a+x')(0) == 0o111
+ True
+
+ >>> simple('a=rw')(0o057) == 0o666
+ True
+
+ >>> simple('u=x')(0o666) == 0o166
+ True
+
+ >>> simple('g=')(0o157) == 0o107
+ True
+
+ >>> simple('gobbledeegook')
+ Traceback (most recent call last):
+ ValueError: ('Unrecognized symbolic mode', 'gobbledeegook')
+ """
+ # parse the symbolic mode
+ parsed = re.match('(?P<who>[ugoa]+)(?P<op>[-+=])(?P<what>[rwx]*)$', mode)
+ if not parsed:
+ raise ValueError("Unrecognized symbolic mode", mode)
+
+ # generate a mask representing the specified permission
+ spec_map = dict(r=4, w=2, x=1)
+ specs = (spec_map[perm] for perm in parsed.group('what'))
+ spec = functools.reduce(operator.or_, specs, 0)
+
+ # now apply spec to each subject in who
+ shift_map = dict(u=6, g=3, o=0)
+ who = parsed.group('who').replace('a', 'ugo')
+ masks = (spec << shift_map[subj] for subj in who)
+ mask = functools.reduce(operator.or_, masks)
+
+ op = parsed.group('op')
+
+ # if op is -, invert the mask
+ if op == '-':
+ mask ^= 0o777
+
+ # if op is =, retain extant values for unreferenced subjects
+ if op == '=':
+ masks = (0o7 << shift_map[subj] for subj in who)
+ retain = functools.reduce(operator.or_, masks) ^ 0o777
+
+ op_map = {
+ '+': operator.or_,
+ '-': operator.and_,
+ '=': lambda mask, target: target & retain ^ mask,
+ }
+ return functools.partial(op_map[op], mask)
+
+
+class Permissions(int):
+ """
+ >>> perms = Permissions(0o764)
+ >>> oct(perms)
+ '0o764'
+ >>> perms.symbolic
+ 'rwxrw-r--'
+ >>> str(perms)
+ 'rwxrw-r--'
+ >>> str(Permissions(0o222))
+ '-w--w--w-'
+ """
+
+ @property
+ def symbolic(self):
+ return ''.join(
+ ['-', val][bit] for val, bit in zip(itertools.cycle('rwx'), self.bits)
+ )
+
+ @property
+ def bits(self):
+ return reversed(tuple(padded(gen_bit_values(self), 0, n=9)))
+
+ def __str__(self):
+ return self.symbolic
diff --git a/contrib/python/path/path/matchers.py b/contrib/python/path/path/matchers.py
new file mode 100644
index 0000000000..63ca218a80
--- /dev/null
+++ b/contrib/python/path/path/matchers.py
@@ -0,0 +1,59 @@
+import ntpath
+import fnmatch
+
+
+def load(param):
+ """
+ If the supplied parameter is a string, assume it's a simple
+ pattern.
+ """
+ return (
+ Pattern(param)
+ if isinstance(param, str)
+ else param
+ if param is not None
+ else Null()
+ )
+
+
+class Base:
+ pass
+
+
+class Null(Base):
+ def __call__(self, path):
+ return True
+
+
+class Pattern(Base):
+ def __init__(self, pattern):
+ self.pattern = pattern
+
+ def get_pattern(self, normcase):
+ try:
+ return self._pattern
+ except AttributeError:
+ pass
+ self._pattern = normcase(self.pattern)
+ return self._pattern
+
+ def __call__(self, path):
+ normcase = getattr(self, 'normcase', path.module.normcase)
+ pattern = self.get_pattern(normcase)
+ return fnmatch.fnmatchcase(normcase(path.name), pattern)
+
+
+class CaseInsensitive(Pattern):
+ """
+ A Pattern with a ``'normcase'`` property, suitable for passing to
+ :meth:`listdir`, :meth:`dirs`, :meth:`files`, :meth:`walk`,
+ :meth:`walkdirs`, or :meth:`walkfiles` to match case-insensitive.
+
+ For example, to get all files ending in .py, .Py, .pY, or .PY in the
+ current directory::
+
+ from path import Path, matchers
+ Path('.').files(matchers.CaseInsensitive('*.py'))
+ """
+
+ normcase = staticmethod(ntpath.normcase)
diff --git a/contrib/python/path/path/py.typed b/contrib/python/path/path/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/path/path/py.typed
diff --git a/contrib/python/path/ya.make b/contrib/python/path/ya.make
new file mode 100644
index 0000000000..aa9cb07250
--- /dev/null
+++ b/contrib/python/path/ya.make
@@ -0,0 +1,30 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(16.7.1)
+
+LICENSE(MIT)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ path/__init__.py
+ path/__init__.pyi
+ path/classes.py
+ path/classes.pyi
+ path/masks.py
+ path/masks.pyi
+ path/matchers.py
+ path/matchers.pyi
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/path/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+ path/py.typed
+)
+
+END()
diff --git a/contrib/python/portalocker/py2/.dist-info/METADATA b/contrib/python/portalocker/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..d01a6203e0
--- /dev/null
+++ b/contrib/python/portalocker/py2/.dist-info/METADATA
@@ -0,0 +1,136 @@
+Metadata-Version: 2.1
+Name: portalocker
+Version: 1.7.1
+Summary: Wraps the portalocker recipe for easy usage
+Home-page: https://github.com/WoLpH/portalocker
+Author: Rick van Hattem
+Author-email: wolph@wol.ph
+License: PSF
+Keywords: locking,locks,with statement,windows,linux,unix
+Platform: any
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Dist: pywin32 (!=226) ; platform_system == "Windows"
+Provides-Extra: docs
+Requires-Dist: sphinx (>=1.7.1) ; extra == 'docs'
+Provides-Extra: tests
+Requires-Dist: pytest (>=4.6.9) ; extra == 'tests'
+Requires-Dist: pytest-cov (>=2.8.1) ; extra == 'tests'
+Requires-Dist: sphinx (>=1.8.5) ; extra == 'tests'
+Requires-Dist: pytest-flake8 (>=1.0.5) ; extra == 'tests'
+
+############################################
+portalocker - Cross-platform locking library
+############################################
+
+.. image:: https://travis-ci.org/WoLpH/portalocker.svg?branch=master
+ :alt: Linux Test Status
+ :target: https://travis-ci.org/WoLpH/portalocker
+
+.. image:: https://ci.appveyor.com/api/projects/status/mgqry98hgpy4prhh?svg=true
+ :alt: Windows Tests Status
+ :target: https://ci.appveyor.com/project/WoLpH/portalocker
+
+.. image:: https://coveralls.io/repos/WoLpH/portalocker/badge.svg?branch=master
+ :alt: Coverage Status
+ :target: https://coveralls.io/r/WoLpH/portalocker?branch=master
+
+Overview
+--------
+
+Portalocker is a library to provide an easy API to file locking.
+
+An important detail to note is that on Linux and Unix systems the locks are
+advisory by default. By specifying the `-o mand` option to the mount command it
+is possible to enable mandatory file locking on Linux. This is generally not
+recommended however. For more information about the subject:
+
+ - https://en.wikipedia.org/wiki/File_locking
+ - http://stackoverflow.com/questions/39292051/portalocker-does-not-seem-to-lock
+ - https://stackoverflow.com/questions/12062466/mandatory-file-lock-on-linux
+
+The module is currently maintained by Rick van Hattem <Wolph@wol.ph>.
+The project resides at https://github.com/WoLpH/portalocker . Bugs and feature
+requests can be submitted there. Patches are also very welcome.
+
+Tips
+----
+
+On some networked filesystems it might be needed to force a `os.fsync()` before
+closing the file so it's actually written before another client reads the file.
+Effectively this comes down to:
+
+::
+
+ with portalocker.Lock('some_file', 'rb+', timeout=60) as fh:
+ # do what you need to do
+ ...
+
+ # flush and sync to filesystem
+ fh.flush()
+ os.fsync(fh.fileno())
+
+Links
+-----
+
+* Documentation
+ - http://portalocker.readthedocs.org/en/latest/
+* Source
+ - https://github.com/WoLpH/portalocker
+* Bug reports
+ - https://github.com/WoLpH/portalocker/issues
+* Package homepage
+ - https://pypi.python.org/pypi/portalocker
+* My blog
+ - http://w.wol.ph/
+
+Examples
+--------
+
+To make sure your cache generation scripts don't race, use the `Lock` class:
+
+>>> import portalocker
+>>> with portalocker.Lock('somefile', timeout=1) as fh:
+ print >>fh, 'writing some stuff to my cache...'
+
+To customize the opening and locking a manual approach is also possible:
+
+>>> import portalocker
+>>> file = open('somefile', 'r+')
+>>> portalocker.lock(file, portalocker.LOCK_EX)
+>>> file.seek(12)
+>>> file.write('foo')
+>>> file.close()
+
+Explicitly unlocking might not be needed in all cases:
+https://github.com/AzureAD/microsoft-authentication-extensions-for-python/issues/42#issuecomment-601108266
+
+But can be done through:
+
+>>> portalocker.unlock(file)
+
+Do note that your data might still be in a buffer so it is possible that your
+data is not available until you `flush()` or `close()`.
+
+More examples can be found in the
+`tests <http://portalocker.readthedocs.io/en/latest/_modules/tests/tests.html>`_.
+
+Changelog
+---------
+
+See the `changelog <http://portalocker.readthedocs.io/en/latest/changelog.html>`_ page.
+
+License
+-------
+
+See the `LICENSE <https://github.com/WoLpH/portalocker/blob/develop/LICENSE>`_ file.
+
+
+
diff --git a/contrib/python/portalocker/py2/.dist-info/top_level.txt b/contrib/python/portalocker/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..7bbc14e6fa
--- /dev/null
+++ b/contrib/python/portalocker/py2/.dist-info/top_level.txt
@@ -0,0 +1 @@
+portalocker
diff --git a/contrib/python/portalocker/py2/LICENSE b/contrib/python/portalocker/py2/LICENSE
new file mode 100644
index 0000000000..adb8038169
--- /dev/null
+++ b/contrib/python/portalocker/py2/LICENSE
@@ -0,0 +1,48 @@
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+Python Software Foundation; All Rights Reserved" are retained in Python alone or
+in any derivative version prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee. This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
diff --git a/contrib/python/portalocker/py2/README.rst b/contrib/python/portalocker/py2/README.rst
new file mode 100644
index 0000000000..c013490c76
--- /dev/null
+++ b/contrib/python/portalocker/py2/README.rst
@@ -0,0 +1,106 @@
+############################################
+portalocker - Cross-platform locking library
+############################################
+
+.. image:: https://travis-ci.org/WoLpH/portalocker.svg?branch=master
+ :alt: Linux Test Status
+ :target: https://travis-ci.org/WoLpH/portalocker
+
+.. image:: https://ci.appveyor.com/api/projects/status/mgqry98hgpy4prhh?svg=true
+ :alt: Windows Tests Status
+ :target: https://ci.appveyor.com/project/WoLpH/portalocker
+
+.. image:: https://coveralls.io/repos/WoLpH/portalocker/badge.svg?branch=master
+ :alt: Coverage Status
+ :target: https://coveralls.io/r/WoLpH/portalocker?branch=master
+
+Overview
+--------
+
+Portalocker is a library to provide an easy API to file locking.
+
+An important detail to note is that on Linux and Unix systems the locks are
+advisory by default. By specifying the `-o mand` option to the mount command it
+is possible to enable mandatory file locking on Linux. This is generally not
+recommended however. For more information about the subject:
+
+ - https://en.wikipedia.org/wiki/File_locking
+ - http://stackoverflow.com/questions/39292051/portalocker-does-not-seem-to-lock
+ - https://stackoverflow.com/questions/12062466/mandatory-file-lock-on-linux
+
+The module is currently maintained by Rick van Hattem <Wolph@wol.ph>.
+The project resides at https://github.com/WoLpH/portalocker . Bugs and feature
+requests can be submitted there. Patches are also very welcome.
+
+Tips
+----
+
+On some networked filesystems it might be needed to force a `os.fsync()` before
+closing the file so it's actually written before another client reads the file.
+Effectively this comes down to:
+
+::
+
+ with portalocker.Lock('some_file', 'rb+', timeout=60) as fh:
+ # do what you need to do
+ ...
+
+ # flush and sync to filesystem
+ fh.flush()
+ os.fsync(fh.fileno())
+
+Links
+-----
+
+* Documentation
+ - http://portalocker.readthedocs.org/en/latest/
+* Source
+ - https://github.com/WoLpH/portalocker
+* Bug reports
+ - https://github.com/WoLpH/portalocker/issues
+* Package homepage
+ - https://pypi.python.org/pypi/portalocker
+* My blog
+ - http://w.wol.ph/
+
+Examples
+--------
+
+To make sure your cache generation scripts don't race, use the `Lock` class:
+
+>>> import portalocker
+>>> with portalocker.Lock('somefile', timeout=1) as fh:
+ print >>fh, 'writing some stuff to my cache...'
+
+To customize the opening and locking a manual approach is also possible:
+
+>>> import portalocker
+>>> file = open('somefile', 'r+')
+>>> portalocker.lock(file, portalocker.LOCK_EX)
+>>> file.seek(12)
+>>> file.write('foo')
+>>> file.close()
+
+Explicitly unlocking might not be needed in all cases:
+https://github.com/AzureAD/microsoft-authentication-extensions-for-python/issues/42#issuecomment-601108266
+
+But can be done through:
+
+>>> portalocker.unlock(file)
+
+Do note that your data might still be in a buffer so it is possible that your
+data is not available until you `flush()` or `close()`.
+
+More examples can be found in the
+`tests <http://portalocker.readthedocs.io/en/latest/_modules/tests/tests.html>`_.
+
+Changelog
+---------
+
+See the `changelog <http://portalocker.readthedocs.io/en/latest/changelog.html>`_ page.
+
+License
+-------
+
+See the `LICENSE <https://github.com/WoLpH/portalocker/blob/develop/LICENSE>`_ file.
+
diff --git a/contrib/python/portalocker/py2/portalocker/__about__.py b/contrib/python/portalocker/py2/portalocker/__about__.py
new file mode 100644
index 0000000000..f16fe0cdf7
--- /dev/null
+++ b/contrib/python/portalocker/py2/portalocker/__about__.py
@@ -0,0 +1,7 @@
+__package_name__ = 'portalocker'
+__author__ = 'Rick van Hattem'
+__email__ = 'wolph@wol.ph'
+__version__ = '1.7.1'
+__description__ = '''Wraps the portalocker recipe for easy usage'''
+__url__ = 'https://github.com/WoLpH/portalocker'
+
diff --git a/contrib/python/portalocker/py2/portalocker/__init__.py b/contrib/python/portalocker/py2/portalocker/__init__.py
new file mode 100644
index 0000000000..9bf27fee0f
--- /dev/null
+++ b/contrib/python/portalocker/py2/portalocker/__init__.py
@@ -0,0 +1,67 @@
+from . import __about__
+from . import constants
+from . import exceptions
+from . import portalocker
+from . import utils
+
+#: The package name on Pypi
+__package_name__ = __about__.__package_name__
+#: Current author and maintainer, view the git history for the previous ones
+__author__ = __about__.__author__
+#: Current author's email address
+__email__ = __about__.__email__
+#: Version number
+__version__ = '1.7.1'
+#: Package description for Pypi
+__description__ = __about__.__description__
+#: Package homepage
+__url__ = __about__.__url__
+
+
+#: Exception thrown when the file is already locked by someone else
+AlreadyLocked = exceptions.AlreadyLocked
+#: Exception thrown if an error occurred during locking
+LockException = exceptions.LockException
+
+
+#: Lock a file. Note that this is an advisory lock on Linux/Unix systems
+lock = portalocker.lock
+#: Unlock a file
+unlock = portalocker.unlock
+
+#: Place an exclusive lock.
+#: Only one process may hold an exclusive lock for a given file at a given
+#: time.
+LOCK_EX = constants.LOCK_EX
+
+#: Place a shared lock.
+#: More than one process may hold a shared lock for a given file at a given
+#: time.
+LOCK_SH = constants.LOCK_SH
+
+#: Acquire the lock in a non-blocking fashion.
+LOCK_NB = constants.LOCK_NB
+
+#: Remove an existing lock held by this process.
+LOCK_UN = constants.LOCK_UN
+
+#: Locking utility class to automatically handle opening with timeouts and
+#: context wrappers
+Lock = utils.Lock
+RLock = utils.RLock
+TemporaryFileLock = utils.TemporaryFileLock
+open_atomic = utils.open_atomic
+
+__all__ = [
+ 'lock',
+ 'unlock',
+ 'LOCK_EX',
+ 'LOCK_SH',
+ 'LOCK_NB',
+ 'LOCK_UN',
+ 'LockException',
+ 'Lock',
+ 'AlreadyLocked',
+ 'open_atomic',
+]
+
diff --git a/contrib/python/portalocker/py2/portalocker/constants.py b/contrib/python/portalocker/py2/portalocker/constants.py
new file mode 100644
index 0000000000..fb0927e2da
--- /dev/null
+++ b/contrib/python/portalocker/py2/portalocker/constants.py
@@ -0,0 +1,39 @@
+'''
+Locking constants
+
+Lock types:
+
+- `LOCK_EX` exclusive lock
+- `LOCK_SH` shared lock
+
+Lock flags:
+
+- `LOCK_NB` non-blocking
+
+Manually unlock, only needed internally
+
+- `LOCK_UN` unlock
+'''
+import os
+
+# The actual tests will execute the code anyhow so the following code can
+# safely be ignored from the coverage tests
+if os.name == 'nt': # pragma: no cover
+ import msvcrt
+
+ LOCK_EX = 0x1 #: exclusive lock
+ LOCK_SH = 0x2 #: shared lock
+ LOCK_NB = 0x4 #: non-blocking
+ LOCK_UN = msvcrt.LK_UNLCK #: unlock
+
+elif os.name == 'posix': # pragma: no cover
+ import fcntl
+
+ LOCK_EX = fcntl.LOCK_EX #: exclusive lock
+ LOCK_SH = fcntl.LOCK_SH #: shared lock
+ LOCK_NB = fcntl.LOCK_NB #: non-blocking
+ LOCK_UN = fcntl.LOCK_UN #: unlock
+
+else: # pragma: no cover
+ raise RuntimeError('PortaLocker only defined for nt and posix platforms')
+
diff --git a/contrib/python/portalocker/py2/portalocker/exceptions.py b/contrib/python/portalocker/py2/portalocker/exceptions.py
new file mode 100644
index 0000000000..bb2b35eb7b
--- /dev/null
+++ b/contrib/python/portalocker/py2/portalocker/exceptions.py
@@ -0,0 +1,19 @@
+class BaseLockException(Exception):
+ # Error codes:
+ LOCK_FAILED = 1
+
+ def __init__(self, *args, **kwargs):
+ self.fh = kwargs.pop('fh', None)
+ Exception.__init__(self, *args, **kwargs)
+
+
+class LockException(BaseLockException):
+ pass
+
+
+class AlreadyLocked(BaseLockException):
+ pass
+
+
+class FileToLarge(BaseLockException):
+ pass
diff --git a/contrib/python/portalocker/py2/portalocker/portalocker.py b/contrib/python/portalocker/py2/portalocker/portalocker.py
new file mode 100644
index 0000000000..460cf06e47
--- /dev/null
+++ b/contrib/python/portalocker/py2/portalocker/portalocker.py
@@ -0,0 +1,148 @@
+import os
+import sys
+from . import exceptions
+from . import constants
+
+
+if os.name == 'nt': # pragma: no cover
+ import win32con
+ import win32file
+ import pywintypes
+ import winerror
+ import msvcrt
+ __overlapped = pywintypes.OVERLAPPED()
+
+ if sys.version_info.major == 2:
+ lock_length = -1
+ else:
+ lock_length = int(2**31 - 1)
+
+ def lock(file_, flags):
+ if flags & constants.LOCK_SH:
+ if sys.version_info.major == 2:
+ if flags & constants.LOCK_NB:
+ mode = win32con.LOCKFILE_FAIL_IMMEDIATELY
+ else:
+ mode = 0
+
+ else:
+ if flags & constants.LOCK_NB:
+ mode = msvcrt.LK_NBRLCK
+ else:
+ mode = msvcrt.LK_RLCK
+
+ # is there any reason not to reuse the following structure?
+ hfile = win32file._get_osfhandle(file_.fileno())
+ try:
+ win32file.LockFileEx(hfile, mode, 0, -0x10000, __overlapped)
+ except pywintypes.error as exc_value:
+ # error: (33, 'LockFileEx', 'The process cannot access the file
+ # because another process has locked a portion of the file.')
+ if exc_value.winerror == winerror.ERROR_LOCK_VIOLATION:
+ raise exceptions.LockException(
+ exceptions.LockException.LOCK_FAILED,
+ exc_value.strerror,
+ fh=file_)
+ else:
+ # Q: Are there exceptions/codes we should be dealing with
+ # here?
+ raise
+ else:
+ mode = win32con.LOCKFILE_EXCLUSIVE_LOCK
+ if flags & constants.LOCK_NB:
+ mode |= win32con.LOCKFILE_FAIL_IMMEDIATELY
+
+ if flags & constants.LOCK_NB:
+ mode = msvcrt.LK_NBLCK
+ else:
+ mode = msvcrt.LK_LOCK
+
+ # windows locks byte ranges, so make sure to lock from file start
+ try:
+ savepos = file_.tell()
+ if savepos:
+ # [ ] test exclusive lock fails on seek here
+ # [ ] test if shared lock passes this point
+ file_.seek(0)
+ # [x] check if 0 param locks entire file (not documented in
+ # Python)
+ # [x] fails with "IOError: [Errno 13] Permission denied",
+ # but -1 seems to do the trick
+
+ try:
+ msvcrt.locking(file_.fileno(), mode, lock_length)
+ except IOError as exc_value:
+ # [ ] be more specific here
+ raise exceptions.LockException(
+ exceptions.LockException.LOCK_FAILED,
+ exc_value.strerror,
+ fh=file_)
+ finally:
+ if savepos:
+ file_.seek(savepos)
+ except IOError as exc_value:
+ raise exceptions.LockException(
+ exceptions.LockException.LOCK_FAILED, exc_value.strerror,
+ fh=file_)
+
+ def unlock(file_):
+ try:
+ savepos = file_.tell()
+ if savepos:
+ file_.seek(0)
+
+ try:
+ msvcrt.locking(file_.fileno(), constants.LOCK_UN, lock_length)
+ except IOError as exc_value:
+ if exc_value.strerror == 'Permission denied':
+ hfile = win32file._get_osfhandle(file_.fileno())
+ try:
+ win32file.UnlockFileEx(
+ hfile, 0, -0x10000, __overlapped)
+ except pywintypes.error as exc_value:
+ if exc_value.winerror == winerror.ERROR_NOT_LOCKED:
+ # error: (158, 'UnlockFileEx',
+ # 'The segment is already unlocked.')
+ # To match the 'posix' implementation, silently
+ # ignore this error
+ pass
+ else:
+ # Q: Are there exceptions/codes we should be
+ # dealing with here?
+ raise
+ else:
+ raise exceptions.LockException(
+ exceptions.LockException.LOCK_FAILED,
+ exc_value.strerror,
+ fh=file_)
+ finally:
+ if savepos:
+ file_.seek(savepos)
+ except IOError as exc_value:
+ raise exceptions.LockException(
+ exceptions.LockException.LOCK_FAILED, exc_value.strerror,
+ fh=file_)
+
+elif os.name == 'posix': # pragma: no cover
+ import fcntl
+
+ def lock(file_, flags):
+ locking_exceptions = IOError,
+ try: # pragma: no cover
+ locking_exceptions += BlockingIOError,
+ except NameError: # pragma: no cover
+ pass
+
+ try:
+ fcntl.flock(file_.fileno(), flags)
+ except locking_exceptions as exc_value:
+ # The exception code varies on different systems so we'll catch
+ # every IO error
+ raise exceptions.LockException(exc_value, fh=file_)
+
+ def unlock(file_):
+ fcntl.flock(file_.fileno(), constants.LOCK_UN)
+
+else: # pragma: no cover
+ raise RuntimeError('PortaLocker only defined for nt and posix platforms')
+
diff --git a/contrib/python/portalocker/py2/portalocker/utils.py b/contrib/python/portalocker/py2/portalocker/utils.py
new file mode 100644
index 0000000000..8baebc2b20
--- /dev/null
+++ b/contrib/python/portalocker/py2/portalocker/utils.py
@@ -0,0 +1,256 @@
+import os
+import time
+import atexit
+import tempfile
+import contextlib
+from . import exceptions
+from . import constants
+from . import portalocker
+
+current_time = getattr(time, "monotonic", time.time)
+
+DEFAULT_TIMEOUT = 5
+DEFAULT_CHECK_INTERVAL = 0.25
+LOCK_METHOD = constants.LOCK_EX | constants.LOCK_NB
+
+__all__ = [
+ 'Lock',
+ 'open_atomic',
+]
+
+
+@contextlib.contextmanager
+def open_atomic(filename, binary=True):
+ '''Open a file for atomic writing. Instead of locking this method allows
+ you to write the entire file and move it to the actual location. Note that
+ this makes the assumption that a rename is atomic on your platform which
+ is generally the case but not a guarantee.
+
+ http://docs.python.org/library/os.html#os.rename
+
+ >>> filename = 'test_file.txt'
+ >>> if os.path.exists(filename):
+ ... os.remove(filename)
+
+ >>> with open_atomic(filename) as fh:
+ ... written = fh.write(b'test')
+ >>> assert os.path.exists(filename)
+ >>> os.remove(filename)
+
+ '''
+ assert not os.path.exists(filename), '%r exists' % filename
+ path, name = os.path.split(filename)
+
+ # Create the parent directory if it doesn't exist
+ if path and not os.path.isdir(path): # pragma: no cover
+ os.makedirs(path)
+
+ temp_fh = tempfile.NamedTemporaryFile(
+ mode=binary and 'wb' or 'w',
+ dir=path,
+ delete=False,
+ )
+ yield temp_fh
+ temp_fh.flush()
+ os.fsync(temp_fh.fileno())
+ temp_fh.close()
+ try:
+ os.rename(temp_fh.name, filename)
+ finally:
+ try:
+ os.remove(temp_fh.name)
+ except Exception:
+ pass
+
+
+class Lock(object):
+
+ def __init__(
+ self, filename, mode='a', timeout=DEFAULT_TIMEOUT,
+ check_interval=DEFAULT_CHECK_INTERVAL, fail_when_locked=False,
+ flags=LOCK_METHOD, **file_open_kwargs):
+ '''Lock manager with build-in timeout
+
+ filename -- filename
+ mode -- the open mode, 'a' or 'ab' should be used for writing
+ truncate -- use truncate to emulate 'w' mode, None is disabled, 0 is
+ truncate to 0 bytes
+ timeout -- timeout when trying to acquire a lock
+ check_interval -- check interval while waiting
+ fail_when_locked -- after the initial lock failed, return an error
+ or lock the file
+ **file_open_kwargs -- The kwargs for the `open(...)` call
+
+ fail_when_locked is useful when multiple threads/processes can race
+ when creating a file. If set to true than the system will wait till
+ the lock was acquired and then return an AlreadyLocked exception.
+
+ Note that the file is opened first and locked later. So using 'w' as
+ mode will result in truncate _BEFORE_ the lock is checked.
+ '''
+
+ if 'w' in mode:
+ truncate = True
+ mode = mode.replace('w', 'a')
+ else:
+ truncate = False
+
+ self.fh = None
+ self.filename = filename
+ self.mode = mode
+ self.truncate = truncate
+ self.timeout = timeout
+ self.check_interval = check_interval
+ self.fail_when_locked = fail_when_locked
+ self.flags = flags
+ self.file_open_kwargs = file_open_kwargs
+
+ def acquire(
+ self, timeout=None, check_interval=None, fail_when_locked=None):
+ '''Acquire the locked filehandle'''
+ if timeout is None:
+ timeout = self.timeout
+ if timeout is None:
+ timeout = 0
+
+ if check_interval is None:
+ check_interval = self.check_interval
+
+ if fail_when_locked is None:
+ fail_when_locked = self.fail_when_locked
+
+ # If we already have a filehandle, return it
+ fh = self.fh
+ if fh:
+ return fh
+
+ # Get a new filehandler
+ fh = self._get_fh()
+ try:
+ # Try to lock
+ fh = self._get_lock(fh)
+ except exceptions.LockException as exception:
+ # Try till the timeout has passed
+ timeoutend = current_time() + timeout
+ while timeoutend > current_time():
+ # Wait a bit
+ time.sleep(check_interval)
+
+ # Try again
+ try:
+
+ # We already tried to the get the lock
+ # If fail_when_locked is true, then stop trying
+ if fail_when_locked:
+ raise exceptions.AlreadyLocked(exception)
+
+ else: # pragma: no cover
+ # We've got the lock
+ fh = self._get_lock(fh)
+ break
+
+ except exceptions.LockException:
+ pass
+
+ else:
+ fh.close()
+ # We got a timeout... reraising
+ raise exceptions.LockException(exception)
+
+ # Prepare the filehandle (truncate if needed)
+ fh = self._prepare_fh(fh)
+
+ self.fh = fh
+ return fh
+
+ def release(self):
+ '''Releases the currently locked file handle'''
+ if self.fh:
+ portalocker.unlock(self.fh)
+ self.fh.close()
+ self.fh = None
+
+ def _get_fh(self):
+ '''Get a new filehandle'''
+ return open(self.filename, self.mode, **self.file_open_kwargs)
+
+ def _get_lock(self, fh):
+ '''
+ Try to lock the given filehandle
+
+ returns LockException if it fails'''
+ portalocker.lock(fh, self.flags)
+ return fh
+
+ def _prepare_fh(self, fh):
+ '''
+ Prepare the filehandle for usage
+
+ If truncate is a number, the file will be truncated to that amount of
+ bytes
+ '''
+ if self.truncate:
+ fh.seek(0)
+ fh.truncate(0)
+
+ return fh
+
+ def __enter__(self):
+ return self.acquire()
+
+ def __exit__(self, type_, value, tb):
+ self.release()
+
+ def __delete__(self, instance): # pragma: no cover
+ instance.release()
+
+
+class RLock(Lock):
+ """
+ A reentrant lock, functions in a similar way to threading.RLock in that it
+ can be acquired multiple times. When the corresponding number of release()
+ calls are made the lock will finally release the underlying file lock.
+ """
+ def __init__(
+ self, filename, mode='a', timeout=DEFAULT_TIMEOUT,
+ check_interval=DEFAULT_CHECK_INTERVAL, fail_when_locked=False,
+ flags=LOCK_METHOD):
+ super(RLock, self).__init__(filename, mode, timeout, check_interval,
+ fail_when_locked, flags)
+ self._acquire_count = 0
+
+ def acquire(
+ self, timeout=None, check_interval=None, fail_when_locked=None):
+ if self._acquire_count >= 1:
+ fh = self.fh
+ else:
+ fh = super(RLock, self).acquire(timeout, check_interval,
+ fail_when_locked)
+ self._acquire_count += 1
+ return fh
+
+ def release(self):
+ if self._acquire_count == 0:
+ raise exceptions.LockException(
+ "Cannot release more times than acquired")
+
+ if self._acquire_count == 1:
+ super(RLock, self).release()
+ self._acquire_count -= 1
+
+
+class TemporaryFileLock(Lock):
+
+ def __init__(self, filename='.lock', timeout=DEFAULT_TIMEOUT,
+ check_interval=DEFAULT_CHECK_INTERVAL, fail_when_locked=True,
+ flags=LOCK_METHOD):
+
+ Lock.__init__(self, filename=filename, mode='w', timeout=timeout,
+ check_interval=check_interval,
+ fail_when_locked=fail_when_locked, flags=flags)
+ atexit.register(self.release)
+
+ def release(self):
+ Lock.release(self)
+ if os.path.isfile(self.filename): # pragma: no branch
+ os.unlink(self.filename)
diff --git a/contrib/python/portalocker/py2/ya.make b/contrib/python/portalocker/py2/ya.make
new file mode 100644
index 0000000000..0cf6513704
--- /dev/null
+++ b/contrib/python/portalocker/py2/ya.make
@@ -0,0 +1,31 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(1.7.1)
+
+LICENSE(PSF-2.0)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ portalocker/__about__.py
+ portalocker/__init__.py
+ portalocker/constants.py
+ portalocker/exceptions.py
+ portalocker/portalocker.py
+ portalocker/utils.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/portalocker/py2/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE(
+ tests
+)
diff --git a/contrib/python/portalocker/py3/.dist-info/METADATA b/contrib/python/portalocker/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..fceba37925
--- /dev/null
+++ b/contrib/python/portalocker/py3/.dist-info/METADATA
@@ -0,0 +1,255 @@
+Metadata-Version: 2.1
+Name: portalocker
+Version: 2.8.2
+Summary: Wraps the portalocker recipe for easy usage
+Author-email: Rick van Hattem <wolph@wol.ph>
+License: BSD-3-Clause
+Project-URL: bugs, https://github.com/wolph/portalocker/issues
+Project-URL: documentation, https://portalocker.readthedocs.io/en/latest/
+Project-URL: repository, https://github.com/wolph/portalocker/
+Keywords: locking,locks,with,statement,windows,linux,unix
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Natural Language :: English
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: MacOS
+Classifier: Operating System :: Microsoft :: MS-DOS
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: Microsoft
+Classifier: Operating System :: POSIX :: BSD :: FreeBSD
+Classifier: Operating System :: POSIX :: BSD
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: POSIX :: SunOS/Solaris
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Unix
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: IronPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python :: Implementation
+Classifier: Programming Language :: Python
+Classifier: Topic :: Education :: Testing
+Classifier: Topic :: Office/Business
+Classifier: Topic :: Other/Nonlisted Topic
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: System :: Monitoring
+Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Requires-Dist: pywin32 >=226 ; platform_system == "Windows"
+Provides-Extra: docs
+Requires-Dist: sphinx >=1.7.1 ; extra == 'docs'
+Provides-Extra: redis
+Requires-Dist: redis ; extra == 'redis'
+Provides-Extra: tests
+Requires-Dist: pytest >=5.4.1 ; extra == 'tests'
+Requires-Dist: pytest-cov >=2.8.1 ; extra == 'tests'
+Requires-Dist: pytest-timeout >=2.1.0 ; extra == 'tests'
+Requires-Dist: sphinx >=6.0.0 ; extra == 'tests'
+Requires-Dist: pytest-mypy >=0.8.0 ; extra == 'tests'
+Requires-Dist: types-redis ; extra == 'tests'
+Requires-Dist: redis ; extra == 'tests'
+
+############################################
+portalocker - Cross-platform locking library
+############################################
+
+.. image:: https://github.com/WoLpH/portalocker/actions/workflows/python-package.yml/badge.svg?branch=master
+ :alt: Linux Test Status
+ :target: https://github.com/WoLpH/portalocker/actions/
+
+.. image:: https://ci.appveyor.com/api/projects/status/mgqry98hgpy4prhh?svg=true
+ :alt: Windows Tests Status
+ :target: https://ci.appveyor.com/project/WoLpH/portalocker
+
+.. image:: https://coveralls.io/repos/WoLpH/portalocker/badge.svg?branch=master
+ :alt: Coverage Status
+ :target: https://coveralls.io/r/WoLpH/portalocker?branch=master
+
+Overview
+--------
+
+Portalocker is a library to provide an easy API to file locking.
+
+An important detail to note is that on Linux and Unix systems the locks are
+advisory by default. By specifying the `-o mand` option to the mount command it
+is possible to enable mandatory file locking on Linux. This is generally not
+recommended however. For more information about the subject:
+
+ - https://en.wikipedia.org/wiki/File_locking
+ - http://stackoverflow.com/questions/39292051/portalocker-does-not-seem-to-lock
+ - https://stackoverflow.com/questions/12062466/mandatory-file-lock-on-linux
+
+The module is currently maintained by Rick van Hattem <Wolph@wol.ph>.
+The project resides at https://github.com/WoLpH/portalocker . Bugs and feature
+requests can be submitted there. Patches are also very welcome.
+
+Security contact information
+------------------------------------------------------------------------------
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
+
+Redis Locks
+-----------
+
+This library now features a lock based on Redis which allows for locks across
+multiple threads, processes and even distributed locks across multiple
+computers.
+
+It is an extremely reliable Redis lock that is based on pubsub.
+
+As opposed to most Redis locking systems based on key/value pairs,
+this locking method is based on the pubsub system. The big advantage is
+that if the connection gets killed due to network issues, crashing
+processes or otherwise, it will still immediately unlock instead of
+waiting for a lock timeout.
+
+First make sure you have everything installed correctly:
+
+::
+
+ pip install "portalocker[redis]"
+
+Usage is really easy:
+
+::
+
+ import portalocker
+
+ lock = portalocker.RedisLock('some_lock_channel_name')
+
+ with lock:
+ print('do something here')
+
+The API is essentially identical to the other ``Lock`` classes so in addition
+to the ``with`` statement you can also use ``lock.acquire(...)``.
+
+Python 2
+--------
+
+Python 2 was supported in versions before Portalocker 2.0. If you are still
+using
+Python 2,
+you can run this to install:
+
+::
+
+ pip install "portalocker<2"
+
+Tips
+----
+
+On some networked filesystems it might be needed to force a `os.fsync()` before
+closing the file so it's actually written before another client reads the file.
+Effectively this comes down to:
+
+::
+
+ with portalocker.Lock('some_file', 'rb+', timeout=60) as fh:
+ # do what you need to do
+ ...
+
+ # flush and sync to filesystem
+ fh.flush()
+ os.fsync(fh.fileno())
+
+Links
+-----
+
+* Documentation
+ - http://portalocker.readthedocs.org/en/latest/
+* Source
+ - https://github.com/WoLpH/portalocker
+* Bug reports
+ - https://github.com/WoLpH/portalocker/issues
+* Package homepage
+ - https://pypi.python.org/pypi/portalocker
+* My blog
+ - http://w.wol.ph/
+
+Examples
+--------
+
+To make sure your cache generation scripts don't race, use the `Lock` class:
+
+>>> import portalocker
+>>> with portalocker.Lock('somefile', timeout=1) as fh:
+... print('writing some stuff to my cache...', file=fh)
+
+To customize the opening and locking a manual approach is also possible:
+
+>>> import portalocker
+>>> file = open('somefile', 'r+')
+>>> portalocker.lock(file, portalocker.LockFlags.EXCLUSIVE)
+>>> file.seek(12)
+>>> file.write('foo')
+>>> file.close()
+
+Explicitly unlocking is not needed in most cases but omitting it has been known
+to cause issues:
+https://github.com/AzureAD/microsoft-authentication-extensions-for-python/issues/42#issuecomment-601108266
+
+If needed, it can be done through:
+
+>>> portalocker.unlock(file)
+
+Do note that your data might still be in a buffer so it is possible that your
+data is not available until you `flush()` or `close()`.
+
+To create a cross platform bounded semaphore across multiple processes you can
+use the `BoundedSemaphore` class which functions somewhat similar to
+`threading.BoundedSemaphore`:
+
+>>> import portalocker
+>>> n = 2
+>>> timeout = 0.1
+
+>>> semaphore_a = portalocker.BoundedSemaphore(n, timeout=timeout)
+>>> semaphore_b = portalocker.BoundedSemaphore(n, timeout=timeout)
+>>> semaphore_c = portalocker.BoundedSemaphore(n, timeout=timeout)
+
+>>> semaphore_a.acquire()
+<portalocker.utils.Lock object at ...>
+>>> semaphore_b.acquire()
+<portalocker.utils.Lock object at ...>
+>>> semaphore_c.acquire()
+Traceback (most recent call last):
+ ...
+portalocker.exceptions.AlreadyLocked
+
+
+More examples can be found in the
+`tests <http://portalocker.readthedocs.io/en/latest/_modules/tests/tests.html>`_.
+
+
+Versioning
+----------
+
+This library follows `Semantic Versioning <http://semver.org/>`_.
+
+
+Changelog
+---------
+
+Every release has a ``git tag`` with a commit message for the tag
+explaining what was added and/or changed. The list of tags/releases
+including the commit messages can be found here:
+https://github.com/WoLpH/portalocker/releases
+
+License
+-------
+
+See the `LICENSE <https://github.com/WoLpH/portalocker/blob/develop/LICENSE>`_ file.
+
diff --git a/contrib/python/portalocker/py3/.dist-info/top_level.txt b/contrib/python/portalocker/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..7bbc14e6fa
--- /dev/null
+++ b/contrib/python/portalocker/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+portalocker
diff --git a/contrib/python/portalocker/py3/LICENSE b/contrib/python/portalocker/py3/LICENSE
new file mode 100644
index 0000000000..b638bda0d3
--- /dev/null
+++ b/contrib/python/portalocker/py3/LICENSE
@@ -0,0 +1,11 @@
+Copyright 2022 Rick van Hattem
+
+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. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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/contrib/python/portalocker/py3/README.rst b/contrib/python/portalocker/py3/README.rst
new file mode 100644
index 0000000000..c5ef42f614
--- /dev/null
+++ b/contrib/python/portalocker/py3/README.rst
@@ -0,0 +1,193 @@
+############################################
+portalocker - Cross-platform locking library
+############################################
+
+.. image:: https://github.com/WoLpH/portalocker/actions/workflows/python-package.yml/badge.svg?branch=master
+ :alt: Linux Test Status
+ :target: https://github.com/WoLpH/portalocker/actions/
+
+.. image:: https://ci.appveyor.com/api/projects/status/mgqry98hgpy4prhh?svg=true
+ :alt: Windows Tests Status
+ :target: https://ci.appveyor.com/project/WoLpH/portalocker
+
+.. image:: https://coveralls.io/repos/WoLpH/portalocker/badge.svg?branch=master
+ :alt: Coverage Status
+ :target: https://coveralls.io/r/WoLpH/portalocker?branch=master
+
+Overview
+--------
+
+Portalocker is a library to provide an easy API to file locking.
+
+An important detail to note is that on Linux and Unix systems the locks are
+advisory by default. By specifying the `-o mand` option to the mount command it
+is possible to enable mandatory file locking on Linux. This is generally not
+recommended however. For more information about the subject:
+
+ - https://en.wikipedia.org/wiki/File_locking
+ - http://stackoverflow.com/questions/39292051/portalocker-does-not-seem-to-lock
+ - https://stackoverflow.com/questions/12062466/mandatory-file-lock-on-linux
+
+The module is currently maintained by Rick van Hattem <Wolph@wol.ph>.
+The project resides at https://github.com/WoLpH/portalocker . Bugs and feature
+requests can be submitted there. Patches are also very welcome.
+
+Security contact information
+------------------------------------------------------------------------------
+
+To report a security vulnerability, please use the
+`Tidelift security contact <https://tidelift.com/security>`_.
+Tidelift will coordinate the fix and disclosure.
+
+Redis Locks
+-----------
+
+This library now features a lock based on Redis which allows for locks across
+multiple threads, processes and even distributed locks across multiple
+computers.
+
+It is an extremely reliable Redis lock that is based on pubsub.
+
+As opposed to most Redis locking systems based on key/value pairs,
+this locking method is based on the pubsub system. The big advantage is
+that if the connection gets killed due to network issues, crashing
+processes or otherwise, it will still immediately unlock instead of
+waiting for a lock timeout.
+
+First make sure you have everything installed correctly:
+
+::
+
+ pip install "portalocker[redis]"
+
+Usage is really easy:
+
+::
+
+ import portalocker
+
+ lock = portalocker.RedisLock('some_lock_channel_name')
+
+ with lock:
+ print('do something here')
+
+The API is essentially identical to the other ``Lock`` classes so in addition
+to the ``with`` statement you can also use ``lock.acquire(...)``.
+
+Python 2
+--------
+
+Python 2 was supported in versions before Portalocker 2.0. If you are still
+using
+Python 2,
+you can run this to install:
+
+::
+
+ pip install "portalocker<2"
+
+Tips
+----
+
+On some networked filesystems it might be needed to force a `os.fsync()` before
+closing the file so it's actually written before another client reads the file.
+Effectively this comes down to:
+
+::
+
+ with portalocker.Lock('some_file', 'rb+', timeout=60) as fh:
+ # do what you need to do
+ ...
+
+ # flush and sync to filesystem
+ fh.flush()
+ os.fsync(fh.fileno())
+
+Links
+-----
+
+* Documentation
+ - http://portalocker.readthedocs.org/en/latest/
+* Source
+ - https://github.com/WoLpH/portalocker
+* Bug reports
+ - https://github.com/WoLpH/portalocker/issues
+* Package homepage
+ - https://pypi.python.org/pypi/portalocker
+* My blog
+ - http://w.wol.ph/
+
+Examples
+--------
+
+To make sure your cache generation scripts don't race, use the `Lock` class:
+
+>>> import portalocker
+>>> with portalocker.Lock('somefile', timeout=1) as fh:
+... print('writing some stuff to my cache...', file=fh)
+
+To customize the opening and locking a manual approach is also possible:
+
+>>> import portalocker
+>>> file = open('somefile', 'r+')
+>>> portalocker.lock(file, portalocker.LockFlags.EXCLUSIVE)
+>>> file.seek(12)
+>>> file.write('foo')
+>>> file.close()
+
+Explicitly unlocking is not needed in most cases but omitting it has been known
+to cause issues:
+https://github.com/AzureAD/microsoft-authentication-extensions-for-python/issues/42#issuecomment-601108266
+
+If needed, it can be done through:
+
+>>> portalocker.unlock(file)
+
+Do note that your data might still be in a buffer so it is possible that your
+data is not available until you `flush()` or `close()`.
+
+To create a cross platform bounded semaphore across multiple processes you can
+use the `BoundedSemaphore` class which functions somewhat similar to
+`threading.BoundedSemaphore`:
+
+>>> import portalocker
+>>> n = 2
+>>> timeout = 0.1
+
+>>> semaphore_a = portalocker.BoundedSemaphore(n, timeout=timeout)
+>>> semaphore_b = portalocker.BoundedSemaphore(n, timeout=timeout)
+>>> semaphore_c = portalocker.BoundedSemaphore(n, timeout=timeout)
+
+>>> semaphore_a.acquire()
+<portalocker.utils.Lock object at ...>
+>>> semaphore_b.acquire()
+<portalocker.utils.Lock object at ...>
+>>> semaphore_c.acquire()
+Traceback (most recent call last):
+ ...
+portalocker.exceptions.AlreadyLocked
+
+
+More examples can be found in the
+`tests <http://portalocker.readthedocs.io/en/latest/_modules/tests/tests.html>`_.
+
+
+Versioning
+----------
+
+This library follows `Semantic Versioning <http://semver.org/>`_.
+
+
+Changelog
+---------
+
+Every release has a ``git tag`` with a commit message for the tag
+explaining what was added and/or changed. The list of tags/releases
+including the commit messages can be found here:
+https://github.com/WoLpH/portalocker/releases
+
+License
+-------
+
+See the `LICENSE <https://github.com/WoLpH/portalocker/blob/develop/LICENSE>`_ file.
+
diff --git a/contrib/python/portalocker/py3/portalocker/__about__.py b/contrib/python/portalocker/py3/portalocker/__about__.py
new file mode 100644
index 0000000000..e45c44327d
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/__about__.py
@@ -0,0 +1,6 @@
+__package_name__ = 'portalocker'
+__author__ = 'Rick van Hattem'
+__email__ = 'wolph@wol.ph'
+__version__ = '2.8.2'
+__description__ = '''Wraps the portalocker recipe for easy usage'''
+__url__ = 'https://github.com/WoLpH/portalocker'
diff --git a/contrib/python/portalocker/py3/portalocker/__init__.py b/contrib/python/portalocker/py3/portalocker/__init__.py
new file mode 100644
index 0000000000..9170e33e9c
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/__init__.py
@@ -0,0 +1,76 @@
+from . import __about__, constants, exceptions, portalocker, utils
+
+try: # pragma: no cover
+ from .redis import RedisLock
+except ImportError: # pragma: no cover
+ RedisLock = None # type: ignore
+
+
+#: The package name on Pypi
+__package_name__ = __about__.__package_name__
+#: Current author and maintainer, view the git history for the previous ones
+__author__ = __about__.__author__
+#: Current author's email address
+__email__ = __about__.__email__
+#: Version number
+__version__ = '2.8.2'
+#: Package description for Pypi
+__description__ = __about__.__description__
+#: Package homepage
+__url__ = __about__.__url__
+
+
+#: Exception thrown when the file is already locked by someone else
+AlreadyLocked = exceptions.AlreadyLocked
+#: Exception thrown if an error occurred during locking
+LockException = exceptions.LockException
+
+
+#: Lock a file. Note that this is an advisory lock on Linux/Unix systems
+lock = portalocker.lock
+#: Unlock a file
+unlock = portalocker.unlock
+
+#: Place an exclusive lock.
+#: Only one process may hold an exclusive lock for a given file at a given
+#: time.
+LOCK_EX: constants.LockFlags = constants.LockFlags.EXCLUSIVE
+
+#: Place a shared lock.
+#: More than one process may hold a shared lock for a given file at a given
+#: time.
+LOCK_SH: constants.LockFlags = constants.LockFlags.SHARED
+
+#: Acquire the lock in a non-blocking fashion.
+LOCK_NB: constants.LockFlags = constants.LockFlags.NON_BLOCKING
+
+#: Remove an existing lock held by this process.
+LOCK_UN: constants.LockFlags = constants.LockFlags.UNBLOCK
+
+#: Locking flags enum
+LockFlags = constants.LockFlags
+
+#: Locking utility class to automatically handle opening with timeouts and
+#: context wrappers
+Lock = utils.Lock
+RLock = utils.RLock
+BoundedSemaphore = utils.BoundedSemaphore
+TemporaryFileLock = utils.TemporaryFileLock
+open_atomic = utils.open_atomic
+
+__all__ = [
+ 'lock',
+ 'unlock',
+ 'LOCK_EX',
+ 'LOCK_SH',
+ 'LOCK_NB',
+ 'LOCK_UN',
+ 'LockFlags',
+ 'LockException',
+ 'Lock',
+ 'RLock',
+ 'AlreadyLocked',
+ 'BoundedSemaphore',
+ 'open_atomic',
+ 'RedisLock',
+]
diff --git a/contrib/python/portalocker/py3/portalocker/__main__.py b/contrib/python/portalocker/py3/portalocker/__main__.py
new file mode 100644
index 0000000000..658a3ec300
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/__main__.py
@@ -0,0 +1,98 @@
+import argparse
+import logging
+import os
+import pathlib
+import re
+
+base_path = pathlib.Path(__file__).parent.parent
+src_path = base_path / 'portalocker'
+dist_path = base_path / 'dist'
+_default_output_path = base_path / 'dist' / 'portalocker.py'
+
+_RELATIVE_IMPORT_RE = re.compile(r'^from \. import (?P<names>.+)$')
+_USELESS_ASSIGNMENT_RE = re.compile(r'^(?P<name>\w+) = \1\n$')
+
+_TEXT_TEMPLATE = """'''
+{}
+'''
+
+"""
+
+logger = logging.getLogger(__name__)
+
+
+def main(argv=None):
+ parser = argparse.ArgumentParser()
+
+ subparsers = parser.add_subparsers(required=True)
+ combine_parser = subparsers.add_parser(
+ 'combine',
+ help='Combine all Python files into a single unified `portalocker.py` '
+ 'file for easy distribution',
+ )
+ combine_parser.add_argument(
+ '--output-file',
+ '-o',
+ type=argparse.FileType('w'),
+ default=str(_default_output_path),
+ )
+
+ combine_parser.set_defaults(func=combine)
+ args = parser.parse_args(argv)
+ args.func(args)
+
+
+def _read_file(path, seen_files):
+ if path in seen_files:
+ return
+
+ names = set()
+ seen_files.add(path)
+ for line in path.open():
+ if match := _RELATIVE_IMPORT_RE.match(line):
+ for name in match.group('names').split(','):
+ name = name.strip()
+ names.add(name)
+ yield from _read_file(src_path / f'{name}.py', seen_files)
+ else:
+ yield _clean_line(line, names)
+
+
+def _clean_line(line, names):
+ # Replace `some_import.spam` with `spam`
+ if names:
+ joined_names = '|'.join(names)
+ line = re.sub(fr'\b({joined_names})\.', '', line)
+
+ # Replace useless assignments (e.g. `spam = spam`)
+ return _USELESS_ASSIGNMENT_RE.sub('', line)
+
+
+def combine(args):
+ output_file = args.output_file
+ pathlib.Path(output_file.name).parent.mkdir(parents=True, exist_ok=True)
+
+ output_file.write(
+ _TEXT_TEMPLATE.format((base_path / 'README.rst').read_text()),
+ )
+ output_file.write(
+ _TEXT_TEMPLATE.format((base_path / 'LICENSE').read_text()),
+ )
+
+ seen_files = set()
+ for line in _read_file(src_path / '__init__.py', seen_files):
+ output_file.write(line)
+
+ output_file.flush()
+ output_file.close()
+
+ logger.info(f'Wrote combined file to {output_file.name}')
+ # Run black and ruff if available. If not then just run the file.
+ os.system(f'black {output_file.name}')
+ os.system(f'ruff --fix {output_file.name}')
+ os.system(f'python3 {output_file.name}')
+
+
+if __name__ == '__main__':
+ logging.basicConfig(level=logging.INFO)
+ main()
diff --git a/contrib/python/portalocker/py3/portalocker/constants.py b/contrib/python/portalocker/py3/portalocker/constants.py
new file mode 100644
index 0000000000..72733c8546
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/constants.py
@@ -0,0 +1,58 @@
+'''
+Locking constants
+
+Lock types:
+
+- `EXCLUSIVE` exclusive lock
+- `SHARED` shared lock
+
+Lock flags:
+
+- `NON_BLOCKING` non-blocking
+
+Manually unlock, only needed internally
+
+- `UNBLOCK` unlock
+'''
+import enum
+import os
+
+# The actual tests will execute the code anyhow so the following code can
+# safely be ignored from the coverage tests
+if os.name == 'nt': # pragma: no cover
+ import msvcrt
+
+ #: exclusive lock
+ LOCK_EX = 0x1
+ #: shared lock
+ LOCK_SH = 0x2
+ #: non-blocking
+ LOCK_NB = 0x4
+ #: unlock
+ LOCK_UN = msvcrt.LK_UNLCK # type: ignore
+
+elif os.name == 'posix': # pragma: no cover
+ import fcntl
+
+ #: exclusive lock
+ LOCK_EX = fcntl.LOCK_EX
+ #: shared lock
+ LOCK_SH = fcntl.LOCK_SH
+ #: non-blocking
+ LOCK_NB = fcntl.LOCK_NB
+ #: unlock
+ LOCK_UN = fcntl.LOCK_UN
+
+else: # pragma: no cover
+ raise RuntimeError('PortaLocker only defined for nt and posix platforms')
+
+
+class LockFlags(enum.IntFlag):
+ #: exclusive lock
+ EXCLUSIVE = LOCK_EX
+ #: shared lock
+ SHARED = LOCK_SH
+ #: non-blocking
+ NON_BLOCKING = LOCK_NB
+ #: unlock
+ UNBLOCK = LOCK_UN
diff --git a/contrib/python/portalocker/py3/portalocker/exceptions.py b/contrib/python/portalocker/py3/portalocker/exceptions.py
new file mode 100644
index 0000000000..e871d13acb
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/exceptions.py
@@ -0,0 +1,27 @@
+import typing
+
+
+class BaseLockException(Exception): # noqa: N818
+ # Error codes:
+ LOCK_FAILED = 1
+
+ def __init__(
+ self,
+ *args: typing.Any,
+ fh: typing.Union[typing.IO, None, int] = None,
+ **kwargs: typing.Any,
+ ) -> None:
+ self.fh = fh
+ Exception.__init__(self, *args)
+
+
+class LockException(BaseLockException):
+ pass
+
+
+class AlreadyLocked(LockException):
+ pass
+
+
+class FileToLarge(LockException):
+ pass
diff --git a/contrib/python/portalocker/py3/portalocker/portalocker.py b/contrib/python/portalocker/py3/portalocker/portalocker.py
new file mode 100644
index 0000000000..90307b76ea
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/portalocker.py
@@ -0,0 +1,117 @@
+import contextlib
+import os
+import typing
+
+from . import constants, exceptions
+
+# Alias for readability. Due to import recursion issues we cannot do:
+# from .constants import LockFlags
+LockFlags = constants.LockFlags
+
+
+if os.name == 'nt': # pragma: no cover
+ import msvcrt
+
+ import pywintypes
+ import win32con
+ import win32file
+ import winerror
+
+ __overlapped = pywintypes.OVERLAPPED()
+
+ def lock(file_: typing.Union[typing.IO, int], flags: LockFlags):
+ # Windows locking does not support locking through `fh.fileno()` so
+ # we cast it to make mypy and pyright happy
+ file_ = typing.cast(typing.IO, file_)
+
+ mode = 0
+ if flags & LockFlags.NON_BLOCKING:
+ mode |= win32con.LOCKFILE_FAIL_IMMEDIATELY
+
+ if flags & LockFlags.EXCLUSIVE:
+ mode |= win32con.LOCKFILE_EXCLUSIVE_LOCK
+
+ # Save the old position so we can go back to that position but
+ # still lock from the beginning of the file
+ savepos = file_.tell()
+ if savepos:
+ file_.seek(0)
+
+ os_fh = msvcrt.get_osfhandle(file_.fileno()) # type: ignore
+ try:
+ win32file.LockFileEx(os_fh, mode, 0, -0x10000, __overlapped)
+ except pywintypes.error as exc_value:
+ # error: (33, 'LockFileEx', 'The process cannot access the file
+ # because another process has locked a portion of the file.')
+ if exc_value.winerror == winerror.ERROR_LOCK_VIOLATION:
+ raise exceptions.AlreadyLocked(
+ exceptions.LockException.LOCK_FAILED,
+ exc_value.strerror,
+ fh=file_,
+ ) from exc_value
+ else:
+ # Q: Are there exceptions/codes we should be dealing with
+ # here?
+ raise
+ finally:
+ if savepos:
+ file_.seek(savepos)
+
+ def unlock(file_: typing.IO):
+ try:
+ savepos = file_.tell()
+ if savepos:
+ file_.seek(0)
+
+ os_fh = msvcrt.get_osfhandle(file_.fileno()) # type: ignore
+ try:
+ win32file.UnlockFileEx(
+ os_fh,
+ 0,
+ -0x10000,
+ __overlapped,
+ )
+ except pywintypes.error as exc:
+ if exc.winerror != winerror.ERROR_NOT_LOCKED:
+ # Q: Are there exceptions/codes we should be
+ # dealing with here?
+ raise
+ finally:
+ if savepos:
+ file_.seek(savepos)
+ except OSError as exc:
+ raise exceptions.LockException(
+ exceptions.LockException.LOCK_FAILED,
+ exc.strerror,
+ fh=file_,
+ ) from exc
+
+elif os.name == 'posix': # pragma: no cover
+ import fcntl
+
+ def lock(file_: typing.Union[typing.IO, int], flags: LockFlags):
+ locking_exceptions = (IOError,)
+ with contextlib.suppress(NameError):
+ locking_exceptions += (BlockingIOError,) # type: ignore
+ # Locking with NON_BLOCKING without EXCLUSIVE or SHARED enabled results
+ # in an error
+ if (flags & LockFlags.NON_BLOCKING) and not flags & (
+ LockFlags.SHARED | LockFlags.EXCLUSIVE
+ ):
+ raise RuntimeError(
+ 'When locking in non-blocking mode the SHARED '
+ 'or EXCLUSIVE flag must be specified as well',
+ )
+
+ try:
+ fcntl.flock(file_, flags)
+ except locking_exceptions as exc_value:
+ # The exception code varies on different systems so we'll catch
+ # every IO error
+ raise exceptions.LockException(exc_value, fh=file_) from exc_value
+
+ def unlock(file_: typing.IO):
+ fcntl.flock(file_.fileno(), LockFlags.UNBLOCK)
+
+else: # pragma: no cover
+ raise RuntimeError('PortaLocker only defined for nt and posix platforms')
diff --git a/contrib/python/portalocker/py3/portalocker/py.typed b/contrib/python/portalocker/py3/portalocker/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/py.typed
diff --git a/contrib/python/portalocker/py3/portalocker/redis.py b/contrib/python/portalocker/py3/portalocker/redis.py
new file mode 100644
index 0000000000..59ee5ff171
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/redis.py
@@ -0,0 +1,236 @@
+import _thread
+import json
+import logging
+import random
+import time
+import typing
+
+from redis import client
+
+from . import exceptions, utils
+
+logger = logging.getLogger(__name__)
+
+DEFAULT_UNAVAILABLE_TIMEOUT = 1
+DEFAULT_THREAD_SLEEP_TIME = 0.1
+
+
+class PubSubWorkerThread(client.PubSubWorkerThread): # type: ignore
+ def run(self):
+ try:
+ super().run()
+ except Exception: # pragma: no cover
+ _thread.interrupt_main()
+ raise
+
+
+class RedisLock(utils.LockBase):
+ '''
+ An extremely reliable Redis lock based on pubsub with a keep-alive thread
+
+ As opposed to most Redis locking systems based on key/value pairs,
+ this locking method is based on the pubsub system. The big advantage is
+ that if the connection gets killed due to network issues, crashing
+ processes or otherwise, it will still immediately unlock instead of
+ waiting for a lock timeout.
+
+ To make sure both sides of the lock know about the connection state it is
+ recommended to set the `health_check_interval` when creating the redis
+ connection..
+
+ Args:
+ channel: the redis channel to use as locking key.
+ connection: an optional redis connection if you already have one
+ or if you need to specify the redis connection
+ timeout: timeout when trying to acquire a lock
+ check_interval: check interval while waiting
+ fail_when_locked: after the initial lock failed, return an error
+ or lock the file. This does not wait for the timeout.
+ thread_sleep_time: sleep time between fetching messages from redis to
+ prevent a busy/wait loop. In the case of lock conflicts this
+ increases the time it takes to resolve the conflict. This should
+ be smaller than the `check_interval` to be useful.
+ unavailable_timeout: If the conflicting lock is properly connected
+ this should never exceed twice your redis latency. Note that this
+ will increase the wait time possibly beyond your `timeout` and is
+ always executed if a conflict arises.
+ redis_kwargs: The redis connection arguments if no connection is
+ given. The `DEFAULT_REDIS_KWARGS` are used as default, if you want
+ to override these you need to explicitly specify a value (e.g.
+ `health_check_interval=0`)
+
+ '''
+
+ redis_kwargs: typing.Dict[str, typing.Any]
+ thread: typing.Optional[PubSubWorkerThread]
+ channel: str
+ timeout: float
+ connection: typing.Optional[client.Redis]
+ pubsub: typing.Optional[client.PubSub] = None
+ close_connection: bool
+
+ DEFAULT_REDIS_KWARGS: typing.ClassVar[typing.Dict[str, typing.Any]] = dict(
+ health_check_interval=10,
+ )
+
+ def __init__(
+ self,
+ channel: str,
+ connection: typing.Optional[client.Redis] = None,
+ timeout: typing.Optional[float] = None,
+ check_interval: typing.Optional[float] = None,
+ fail_when_locked: typing.Optional[bool] = False,
+ thread_sleep_time: float = DEFAULT_THREAD_SLEEP_TIME,
+ unavailable_timeout: float = DEFAULT_UNAVAILABLE_TIMEOUT,
+ redis_kwargs: typing.Optional[typing.Dict] = None,
+ ):
+ # We don't want to close connections given as an argument
+ self.close_connection = not connection
+
+ self.thread = None
+ self.channel = channel
+ self.connection = connection
+ self.thread_sleep_time = thread_sleep_time
+ self.unavailable_timeout = unavailable_timeout
+ self.redis_kwargs = redis_kwargs or dict()
+
+ for key, value in self.DEFAULT_REDIS_KWARGS.items():
+ self.redis_kwargs.setdefault(key, value)
+
+ super().__init__(
+ timeout=timeout,
+ check_interval=check_interval,
+ fail_when_locked=fail_when_locked,
+ )
+
+ def get_connection(self) -> client.Redis:
+ if not self.connection:
+ self.connection = client.Redis(**self.redis_kwargs)
+
+ return self.connection
+
+ def channel_handler(self, message):
+ if message.get('type') != 'message': # pragma: no cover
+ return
+
+ try:
+ data = json.loads(message.get('data'))
+ except TypeError: # pragma: no cover
+ logger.debug('TypeError while parsing: %r', message)
+ return
+
+ assert self.connection is not None
+ self.connection.publish(data['response_channel'], str(time.time()))
+
+ @property
+ def client_name(self):
+ return f'{self.channel}-lock'
+
+ def acquire(
+ self,
+ timeout: typing.Optional[float] = None,
+ check_interval: typing.Optional[float] = None,
+ fail_when_locked: typing.Optional[bool] = None,
+ ):
+ timeout = utils.coalesce(timeout, self.timeout, 0.0)
+ check_interval = utils.coalesce(
+ check_interval,
+ self.check_interval,
+ 0.0,
+ )
+ fail_when_locked = utils.coalesce(
+ fail_when_locked,
+ self.fail_when_locked,
+ )
+
+ assert not self.pubsub, 'This lock is already active'
+ connection = self.get_connection()
+
+ timeout_generator = self._timeout_generator(timeout, check_interval)
+ for _ in timeout_generator: # pragma: no branch
+ subscribers = connection.pubsub_numsub(self.channel)[0][1]
+
+ if subscribers:
+ logger.debug(
+ 'Found %d lock subscribers for %s',
+ subscribers,
+ self.channel,
+ )
+
+ if self.check_or_kill_lock(
+ connection,
+ self.unavailable_timeout,
+ ): # pragma: no branch
+ continue
+ else: # pragma: no cover
+ subscribers = 0
+
+ # Note: this should not be changed to an elif because the if
+ # above can still end up here
+ if not subscribers:
+ connection.client_setname(self.client_name)
+ self.pubsub = connection.pubsub()
+ self.pubsub.subscribe(**{self.channel: self.channel_handler})
+ self.thread = PubSubWorkerThread(
+ self.pubsub,
+ sleep_time=self.thread_sleep_time,
+ )
+ self.thread.start()
+
+ subscribers = connection.pubsub_numsub(self.channel)[0][1]
+ if subscribers == 1: # pragma: no branch
+ return self
+ else: # pragma: no cover
+ # Race condition, let's try again
+ self.release()
+
+ if fail_when_locked: # pragma: no cover
+ raise exceptions.AlreadyLocked(exceptions)
+
+ raise exceptions.AlreadyLocked(exceptions)
+
+ def check_or_kill_lock(self, connection, timeout):
+ # Random channel name to get messages back from the lock
+ response_channel = f'{self.channel}-{random.random()}'
+
+ pubsub = connection.pubsub()
+ pubsub.subscribe(response_channel)
+ connection.publish(
+ self.channel,
+ json.dumps(
+ dict(
+ response_channel=response_channel,
+ message='ping',
+ ),
+ ),
+ )
+
+ check_interval = min(self.thread_sleep_time, timeout / 10)
+ for _ in self._timeout_generator(
+ timeout,
+ check_interval,
+ ): # pragma: no branch
+ if pubsub.get_message(timeout=check_interval):
+ pubsub.close()
+ return True
+
+ for client_ in connection.client_list('pubsub'): # pragma: no cover
+ if client_.get('name') == self.client_name:
+ logger.warning('Killing unavailable redis client: %r', client_)
+ connection.client_kill_filter(client_.get('id'))
+ return None
+
+ def release(self):
+ if self.thread: # pragma: no branch
+ self.thread.stop()
+ self.thread.join()
+ self.thread = None
+ time.sleep(0.01)
+
+ if self.pubsub: # pragma: no branch
+ self.pubsub.unsubscribe(self.channel)
+ self.pubsub.close()
+ self.pubsub = None
+
+ def __del__(self):
+ self.release()
diff --git a/contrib/python/portalocker/py3/portalocker/utils.py b/contrib/python/portalocker/py3/portalocker/utils.py
new file mode 100644
index 0000000000..3b5682e055
--- /dev/null
+++ b/contrib/python/portalocker/py3/portalocker/utils.py
@@ -0,0 +1,563 @@
+import abc
+import atexit
+import contextlib
+import logging
+import os
+import pathlib
+import random
+import tempfile
+import time
+import typing
+import warnings
+
+from . import constants, exceptions, portalocker
+
+logger = logging.getLogger(__name__)
+
+DEFAULT_TIMEOUT = 5
+DEFAULT_CHECK_INTERVAL = 0.25
+DEFAULT_FAIL_WHEN_LOCKED = False
+LOCK_METHOD = constants.LockFlags.EXCLUSIVE | constants.LockFlags.NON_BLOCKING
+
+__all__ = [
+ 'Lock',
+ 'open_atomic',
+]
+
+Filename = typing.Union[str, pathlib.Path]
+
+
+def coalesce(*args: typing.Any, test_value: typing.Any = None) -> typing.Any:
+ '''Simple coalescing function that returns the first value that is not
+ equal to the `test_value`. Or `None` if no value is valid. Usually this
+ means that the last given value is the default value.
+
+ Note that the `test_value` is compared using an identity check
+ (i.e. `value is not test_value`) so changing the `test_value` won't work
+ for all values.
+
+ >>> coalesce(None, 1)
+ 1
+ >>> coalesce()
+
+ >>> coalesce(0, False, True)
+ 0
+ >>> coalesce(0, False, True, test_value=0)
+ False
+
+ # This won't work because of the `is not test_value` type testing:
+ >>> coalesce([], dict(spam='eggs'), test_value=[])
+ []
+ '''
+ return next((arg for arg in args if arg is not test_value), None)
+
+
+@contextlib.contextmanager
+def open_atomic(
+ filename: Filename,
+ binary: bool = True,
+) -> typing.Iterator[typing.IO]:
+ '''Open a file for atomic writing. Instead of locking this method allows
+ you to write the entire file and move it to the actual location. Note that
+ this makes the assumption that a rename is atomic on your platform which
+ is generally the case but not a guarantee.
+
+ http://docs.python.org/library/os.html#os.rename
+
+ >>> filename = 'test_file.txt'
+ >>> if os.path.exists(filename):
+ ... os.remove(filename)
+
+ >>> with open_atomic(filename) as fh:
+ ... written = fh.write(b'test')
+ >>> assert os.path.exists(filename)
+ >>> os.remove(filename)
+
+ >>> import pathlib
+ >>> path_filename = pathlib.Path('test_file.txt')
+
+ >>> with open_atomic(path_filename) as fh:
+ ... written = fh.write(b'test')
+ >>> assert path_filename.exists()
+ >>> path_filename.unlink()
+ '''
+ # `pathlib.Path` cast in case `path` is a `str`
+ path: pathlib.Path = pathlib.Path(filename)
+
+ assert not path.exists(), '%r exists' % path
+
+ # Create the parent directory if it doesn't exist
+ path.parent.mkdir(parents=True, exist_ok=True)
+
+ temp_fh = tempfile.NamedTemporaryFile(
+ mode=binary and 'wb' or 'w',
+ dir=str(path.parent),
+ delete=False,
+ )
+ yield temp_fh
+ temp_fh.flush()
+ os.fsync(temp_fh.fileno())
+ temp_fh.close()
+ try:
+ os.rename(temp_fh.name, path)
+ finally:
+ with contextlib.suppress(Exception):
+ os.remove(temp_fh.name)
+
+
+class LockBase(abc.ABC): # pragma: no cover
+ #: timeout when trying to acquire a lock
+ timeout: float
+ #: check interval while waiting for `timeout`
+ check_interval: float
+ #: skip the timeout and immediately fail if the initial lock fails
+ fail_when_locked: bool
+
+ def __init__(
+ self,
+ timeout: typing.Optional[float] = None,
+ check_interval: typing.Optional[float] = None,
+ fail_when_locked: typing.Optional[bool] = None,
+ ):
+ self.timeout = coalesce(timeout, DEFAULT_TIMEOUT)
+ self.check_interval = coalesce(check_interval, DEFAULT_CHECK_INTERVAL)
+ self.fail_when_locked = coalesce(
+ fail_when_locked,
+ DEFAULT_FAIL_WHEN_LOCKED,
+ )
+
+ @abc.abstractmethod
+ def acquire(
+ self,
+ timeout: typing.Optional[float] = None,
+ check_interval: typing.Optional[float] = None,
+ fail_when_locked: typing.Optional[bool] = None,
+ ):
+ return NotImplemented
+
+ def _timeout_generator(
+ self,
+ timeout: typing.Optional[float],
+ check_interval: typing.Optional[float],
+ ) -> typing.Iterator[int]:
+ f_timeout = coalesce(timeout, self.timeout, 0.0)
+ f_check_interval = coalesce(check_interval, self.check_interval, 0.0)
+
+ yield 0
+ i = 0
+
+ start_time = time.perf_counter()
+ while start_time + f_timeout > time.perf_counter():
+ i += 1
+ yield i
+
+ # Take low lock checks into account to stay within the interval
+ since_start_time = time.perf_counter() - start_time
+ time.sleep(max(0.001, (i * f_check_interval) - since_start_time))
+
+ @abc.abstractmethod
+ def release(self):
+ return NotImplemented
+
+ def __enter__(self):
+ return self.acquire()
+
+ def __exit__(
+ self,
+ exc_type: typing.Optional[typing.Type[BaseException]],
+ exc_value: typing.Optional[BaseException],
+ traceback: typing.Any, # Should be typing.TracebackType
+ ) -> typing.Optional[bool]:
+ self.release()
+ return None
+
+ def __delete__(self, instance):
+ instance.release()
+
+
+class Lock(LockBase):
+ '''Lock manager with built-in timeout
+
+ Args:
+ filename: filename
+ mode: the open mode, 'a' or 'ab' should be used for writing. When mode
+ contains `w` the file will be truncated to 0 bytes.
+ timeout: timeout when trying to acquire a lock
+ check_interval: check interval while waiting
+ fail_when_locked: after the initial lock failed, return an error
+ or lock the file. This does not wait for the timeout.
+ **file_open_kwargs: The kwargs for the `open(...)` call
+
+ fail_when_locked is useful when multiple threads/processes can race
+ when creating a file. If set to true than the system will wait till
+ the lock was acquired and then return an AlreadyLocked exception.
+
+ Note that the file is opened first and locked later. So using 'w' as
+ mode will result in truncate _BEFORE_ the lock is checked.
+ '''
+
+ def __init__(
+ self,
+ filename: Filename,
+ mode: str = 'a',
+ timeout: typing.Optional[float] = None,
+ check_interval: float = DEFAULT_CHECK_INTERVAL,
+ fail_when_locked: bool = DEFAULT_FAIL_WHEN_LOCKED,
+ flags: constants.LockFlags = LOCK_METHOD,
+ **file_open_kwargs,
+ ):
+ if 'w' in mode:
+ truncate = True
+ mode = mode.replace('w', 'a')
+ else:
+ truncate = False
+
+ if timeout is None:
+ timeout = DEFAULT_TIMEOUT
+ elif not (flags & constants.LockFlags.NON_BLOCKING):
+ warnings.warn(
+ 'timeout has no effect in blocking mode',
+ stacklevel=1,
+ )
+
+ self.fh: typing.Optional[typing.IO] = None
+ self.filename: str = str(filename)
+ self.mode: str = mode
+ self.truncate: bool = truncate
+ self.timeout: float = timeout
+ self.check_interval: float = check_interval
+ self.fail_when_locked: bool = fail_when_locked
+ self.flags: constants.LockFlags = flags
+ self.file_open_kwargs = file_open_kwargs
+
+ def acquire(
+ self,
+ timeout: typing.Optional[float] = None,
+ check_interval: typing.Optional[float] = None,
+ fail_when_locked: typing.Optional[bool] = None,
+ ) -> typing.IO:
+ '''Acquire the locked filehandle'''
+
+ fail_when_locked = coalesce(fail_when_locked, self.fail_when_locked)
+
+ if (
+ not (self.flags & constants.LockFlags.NON_BLOCKING)
+ and timeout is not None
+ ):
+ warnings.warn(
+ 'timeout has no effect in blocking mode',
+ stacklevel=1,
+ )
+
+ # If we already have a filehandle, return it
+ fh: typing.Optional[typing.IO] = self.fh
+ if fh:
+ return fh
+
+ # Get a new filehandler
+ fh = self._get_fh()
+
+ def try_close(): # pragma: no cover
+ # Silently try to close the handle if possible, ignore all issues
+ if fh is not None:
+ with contextlib.suppress(Exception):
+ fh.close()
+
+ exception = None
+ # Try till the timeout has passed
+ for _ in self._timeout_generator(timeout, check_interval):
+ exception = None
+ try:
+ # Try to lock
+ fh = self._get_lock(fh)
+ break
+ except exceptions.LockException as exc:
+ # Python will automatically remove the variable from memory
+ # unless you save it in a different location
+ exception = exc
+
+ # We already tried to the get the lock
+ # If fail_when_locked is True, stop trying
+ if fail_when_locked:
+ try_close()
+ raise exceptions.AlreadyLocked(exception) from exc
+
+ # Wait a bit
+
+ if exception:
+ try_close()
+ # We got a timeout... reraising
+ raise exceptions.LockException(exception)
+
+ # Prepare the filehandle (truncate if needed)
+ fh = self._prepare_fh(fh)
+
+ self.fh = fh
+ return fh
+
+ def release(self):
+ '''Releases the currently locked file handle'''
+ if self.fh:
+ portalocker.unlock(self.fh)
+ self.fh.close()
+ self.fh = None
+
+ def _get_fh(self) -> typing.IO:
+ '''Get a new filehandle'''
+ return open( # noqa: SIM115
+ self.filename,
+ self.mode,
+ **self.file_open_kwargs,
+ )
+
+ def _get_lock(self, fh: typing.IO) -> typing.IO:
+ '''
+ Try to lock the given filehandle
+
+ returns LockException if it fails'''
+ portalocker.lock(fh, self.flags)
+ return fh
+
+ def _prepare_fh(self, fh: typing.IO) -> typing.IO:
+ '''
+ Prepare the filehandle for usage
+
+ If truncate is a number, the file will be truncated to that amount of
+ bytes
+ '''
+ if self.truncate:
+ fh.seek(0)
+ fh.truncate(0)
+
+ return fh
+
+
+class RLock(Lock):
+ '''
+ A reentrant lock, functions in a similar way to threading.RLock in that it
+ can be acquired multiple times. When the corresponding number of release()
+ calls are made the lock will finally release the underlying file lock.
+ '''
+
+ def __init__(
+ self,
+ filename,
+ mode='a',
+ timeout=DEFAULT_TIMEOUT,
+ check_interval=DEFAULT_CHECK_INTERVAL,
+ fail_when_locked=False,
+ flags=LOCK_METHOD,
+ ):
+ super().__init__(
+ filename,
+ mode,
+ timeout,
+ check_interval,
+ fail_when_locked,
+ flags,
+ )
+ self._acquire_count = 0
+
+ def acquire(
+ self,
+ timeout: typing.Optional[float] = None,
+ check_interval: typing.Optional[float] = None,
+ fail_when_locked: typing.Optional[bool] = None,
+ ) -> typing.IO:
+ if self._acquire_count >= 1:
+ fh = self.fh
+ else:
+ fh = super().acquire(timeout, check_interval, fail_when_locked)
+ self._acquire_count += 1
+ assert fh
+ return fh
+
+ def release(self):
+ if self._acquire_count == 0:
+ raise exceptions.LockException(
+ 'Cannot release more times than acquired',
+ )
+
+ if self._acquire_count == 1:
+ super().release()
+ self._acquire_count -= 1
+
+
+class TemporaryFileLock(Lock):
+ def __init__(
+ self,
+ filename='.lock',
+ timeout=DEFAULT_TIMEOUT,
+ check_interval=DEFAULT_CHECK_INTERVAL,
+ fail_when_locked=True,
+ flags=LOCK_METHOD,
+ ):
+ Lock.__init__(
+ self,
+ filename=filename,
+ mode='w',
+ timeout=timeout,
+ check_interval=check_interval,
+ fail_when_locked=fail_when_locked,
+ flags=flags,
+ )
+ atexit.register(self.release)
+
+ def release(self):
+ Lock.release(self)
+ if os.path.isfile(self.filename): # pragma: no branch
+ os.unlink(self.filename)
+
+
+class BoundedSemaphore(LockBase):
+ '''
+ Bounded semaphore to prevent too many parallel processes from running
+
+ This method is deprecated because multiple processes that are completely
+ unrelated could end up using the same semaphore. To prevent this,
+ use `NamedBoundedSemaphore` instead. The
+ `NamedBoundedSemaphore` is a drop-in replacement for this class.
+
+ >>> semaphore = BoundedSemaphore(2, directory='')
+ >>> str(semaphore.get_filenames()[0])
+ 'bounded_semaphore.00.lock'
+ >>> str(sorted(semaphore.get_random_filenames())[1])
+ 'bounded_semaphore.01.lock'
+ '''
+
+ lock: typing.Optional[Lock]
+
+ def __init__(
+ self,
+ maximum: int,
+ name: str = 'bounded_semaphore',
+ filename_pattern: str = '{name}.{number:02d}.lock',
+ directory: str = tempfile.gettempdir(),
+ timeout: typing.Optional[float] = DEFAULT_TIMEOUT,
+ check_interval: typing.Optional[float] = DEFAULT_CHECK_INTERVAL,
+ fail_when_locked: typing.Optional[bool] = True,
+ ):
+ self.maximum = maximum
+ self.name = name
+ self.filename_pattern = filename_pattern
+ self.directory = directory
+ self.lock: typing.Optional[Lock] = None
+ super().__init__(
+ timeout=timeout,
+ check_interval=check_interval,
+ fail_when_locked=fail_when_locked,
+ )
+
+ if not name or name == 'bounded_semaphore':
+ warnings.warn(
+ '`BoundedSemaphore` without an explicit `name` '
+ 'argument is deprecated, use NamedBoundedSemaphore',
+ DeprecationWarning,
+ stacklevel=1,
+ )
+
+ def get_filenames(self) -> typing.Sequence[pathlib.Path]:
+ return [self.get_filename(n) for n in range(self.maximum)]
+
+ def get_random_filenames(self) -> typing.Sequence[pathlib.Path]:
+ filenames = list(self.get_filenames())
+ random.shuffle(filenames)
+ return filenames
+
+ def get_filename(self, number) -> pathlib.Path:
+ return pathlib.Path(self.directory) / self.filename_pattern.format(
+ name=self.name,
+ number=number,
+ )
+
+ def acquire(
+ self,
+ timeout: typing.Optional[float] = None,
+ check_interval: typing.Optional[float] = None,
+ fail_when_locked: typing.Optional[bool] = None,
+ ) -> typing.Optional[Lock]:
+ assert not self.lock, 'Already locked'
+
+ filenames = self.get_filenames()
+
+ for n in self._timeout_generator(timeout, check_interval): # pragma:
+ logger.debug('trying lock (attempt %d) %r', n, filenames)
+ # no branch
+ if self.try_lock(filenames): # pragma: no branch
+ return self.lock # pragma: no cover
+
+ if fail_when_locked := coalesce(
+ fail_when_locked,
+ self.fail_when_locked,
+ ):
+ raise exceptions.AlreadyLocked()
+
+ return None
+
+ def try_lock(self, filenames: typing.Sequence[Filename]) -> bool:
+ filename: Filename
+ for filename in filenames:
+ logger.debug('trying lock for %r', filename)
+ self.lock = Lock(filename, fail_when_locked=True)
+ try:
+ self.lock.acquire()
+ except exceptions.AlreadyLocked:
+ self.lock = None
+ else:
+ logger.debug('locked %r', filename)
+ return True
+
+ return False
+
+ def release(self): # pragma: no cover
+ if self.lock is not None:
+ self.lock.release()
+ self.lock = None
+
+
+class NamedBoundedSemaphore(BoundedSemaphore):
+ '''
+ Bounded semaphore to prevent too many parallel processes from running
+
+ It's also possible to specify a timeout when acquiring the lock to wait
+ for a resource to become available. This is very similar to
+ `threading.BoundedSemaphore` but works across multiple processes and across
+ multiple operating systems.
+
+ Because this works across multiple processes it's important to give the
+ semaphore a name. This name is used to create the lock files. If you
+ don't specify a name, a random name will be generated. This means that
+ you can't use the same semaphore in multiple processes unless you pass the
+ semaphore object to the other processes.
+
+ >>> semaphore = NamedBoundedSemaphore(2, name='test')
+ >>> str(semaphore.get_filenames()[0])
+ '...test.00.lock'
+
+ >>> semaphore = NamedBoundedSemaphore(2)
+ >>> 'bounded_semaphore' in str(semaphore.get_filenames()[0])
+ True
+
+ '''
+
+ def __init__(
+ self,
+ maximum: int,
+ name: typing.Optional[str] = None,
+ filename_pattern: str = '{name}.{number:02d}.lock',
+ directory: str = tempfile.gettempdir(),
+ timeout: typing.Optional[float] = DEFAULT_TIMEOUT,
+ check_interval: typing.Optional[float] = DEFAULT_CHECK_INTERVAL,
+ fail_when_locked: typing.Optional[bool] = True,
+ ):
+ if name is None:
+ name = 'bounded_semaphore.%d' % random.randint(0, 1000000)
+ super().__init__(
+ maximum,
+ name,
+ filename_pattern,
+ directory,
+ timeout,
+ check_interval,
+ fail_when_locked,
+ )
diff --git a/contrib/python/portalocker/py3/ya.make b/contrib/python/portalocker/py3/ya.make
new file mode 100644
index 0000000000..8bef0a2613
--- /dev/null
+++ b/contrib/python/portalocker/py3/ya.make
@@ -0,0 +1,38 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(2.8.2)
+
+LICENSE(BSD-3-Clause)
+
+NO_LINT()
+
+NO_CHECK_IMPORTS(
+ portalocker.redis
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ portalocker/__about__.py
+ portalocker/__init__.py
+ portalocker/__main__.py
+ portalocker/constants.py
+ portalocker/exceptions.py
+ portalocker/portalocker.py
+ portalocker/redis.py
+ portalocker/utils.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/portalocker/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+ portalocker/py.typed
+)
+
+END()
+
+RECURSE(
+ tests
+)
diff --git a/contrib/python/portalocker/ya.make b/contrib/python/portalocker/ya.make
new file mode 100644
index 0000000000..4012370b1c
--- /dev/null
+++ b/contrib/python/portalocker/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/portalocker/py2)
+ELSE()
+ PEERDIR(contrib/python/portalocker/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/pygtrie/py2/LICENSE b/contrib/python/pygtrie/py2/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/contrib/python/pygtrie/py2/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/contrib/python/pygtrie/py2/README.rst b/contrib/python/pygtrie/py2/README.rst
new file mode 100644
index 0000000000..41ca83db94
--- /dev/null
+++ b/contrib/python/pygtrie/py2/README.rst
@@ -0,0 +1,66 @@
+pygtrie
+=======
+
+.. image:: https://readthedocs.org/projects/pygtrie/badge/?version=latest
+ :target: http://pygtrie.readthedocs.io/en/latest/
+ :alt: Documentation build status (latest)
+
+.. image:: https://readthedocs.org/projects/pygtrie/badge/?version=stable
+ :target: http://pygtrie.readthedocs.io/en/stable/
+ :alt: Documentation build status (stable)
+
+.. image:: https://api.travis-ci.com/mina86/pygtrie.svg
+ :target: https://travis-ci.com/mina86/pygtrie
+ :alt: Continuous integration status
+
+pygtrie is a pure Python implementation of a trie data structure
+compatible with Python 2.x and Python 3.x.
+
+`Trie data structure <http://en.wikipedia.org/wiki/Trie>`_, also known
+as radix or prefix tree, is a tree associating keys to values where
+all the descendants of a node have a common prefix (associated with
+that node).
+
+The trie module contains ``Trie``, ``CharTrie`` and ``StringTrie``
+classes each implementing a mutable mapping interface, i.e. ``dict``
+interface. As such, in most circumstances, ``Trie`` could be used as
+a drop-in replacement for a ``dict``, but the prefix nature of the
+data structure is trie’s real strength.
+
+The module also contains ``PrefixSet`` class which uses a trie to
+store a set of prefixes such that a key is contained in the set if it
+or its prefix is stored in the set.
+
+Features
+--------
+
+- A full mutable mapping implementation.
+
+- Supports iterating over as well as deleting a subtrie.
+
+- Supports prefix checking as well as shortest and longest prefix
+ look-up.
+
+- Extensible for any kind of user-defined keys.
+
+- A PrefixSet supports “all keys starting with given prefix†logic.
+
+- Can store any value including None.
+
+Installation
+------------
+
+To install pygtrie, simply run::
+
+ pip install pygtrie
+
+or by adding line such as::
+
+ pygtrie == 2.*
+
+to project’s `requirements file
+<https://pip.pypa.io/en/latest/user_guide/#requirements-files>`_.
+Alternatively, if installation from source is desired, it can be
+achieved by executing::
+
+ python setup.py install
diff --git a/contrib/python/pygtrie/py3/.dist-info/METADATA b/contrib/python/pygtrie/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..bb99559336
--- /dev/null
+++ b/contrib/python/pygtrie/py3/.dist-info/METADATA
@@ -0,0 +1,220 @@
+Metadata-Version: 2.1
+Name: pygtrie
+Version: 2.5.0
+Summary: A pure Python trie data structure implementation.
+Home-page: https://github.com/mina86/pygtrie
+Author: Michal Nazarewicz
+Author-email: mina86@mina86.com
+License: Apache-2.0
+Download-URL: https://github.com/mina86/pygtrie/tarball/v2.5.0
+Keywords: trie,prefix tree,data structure
+Platform: Platform Independent
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+License-File: LICENSE
+
+pygtrie
+=======
+
+pygtrie is a pure Python implementation of a trie data structure
+compatible with Python 2.x and Python 3.x.
+
+`Trie data structure <http://en.wikipedia.org/wiki/Trie>`_, also known
+as radix or prefix tree, is a tree associating keys to values where
+all the descendants of a node have a common prefix (associated with
+that node).
+
+The trie module contains ``Trie``, ``CharTrie`` and ``StringTrie``
+classes each implementing a mutable mapping interface, i.e. ``dict``
+interface. As such, in most circumstances, ``Trie`` could be used as
+a drop-in replacement for a ``dict``, but the prefix nature of the
+data structure is trie’s real strength.
+
+The module also contains ``PrefixSet`` class which uses a trie to
+store a set of prefixes such that a key is contained in the set if it
+or its prefix is stored in the set.
+
+Features
+--------
+
+- A full mutable mapping implementation.
+
+- Supports iterating over as well as deleting a subtrie.
+
+- Supports prefix checking as well as shortest and longest prefix
+ look-up.
+
+- Extensible for any kind of user-defined keys.
+
+- A PrefixSet supports “all keys starting with given prefix†logic.
+
+- Can store any value including None.
+
+Installation
+------------
+
+To install pygtrie, simply run::
+
+ pip install pygtrie
+
+or by adding line such as::
+
+ pygtrie == 2.*
+
+to project’s `requirements file
+<https://pip.pypa.io/en/latest/user_guide/#requirements-files>`_.
+Alternatively, if installation from source is desired, it can be
+achieved by executing::
+
+ python setup.py install
+
+Version History
+---------------
+
+2.5: 2022/07/16
+
+- Add ``pygtrie.Trie.merge`` method which merges structures of two
+ tries.
+
+- Add ``pygtrie.Trie.strictly_equals`` method which compares two
+ tries with stricter rules than regular equality operator. It’s not
+ sufficient that keys and values are the same but the structure of
+ the tries must be the same as well. For example:
+
+ >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
+ >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
+ >>> t0 == t1
+ True
+ >>> t0.strictly_equals(t1)
+ False
+
+- Fix ``pygtrie.Trie.__eq__`` implementation such that key values
+ are taken into consideration rather than just looking at trie
+ structure. To see what this means it’s best to look at a few
+ examples. Firstly:
+
+ >>> t0 = StringTrie({'foo/bar': 42}, separator='/')
+ >>> t1 = StringTrie({'foo.bar': 42}, separator='.')
+ >>> t0 == t1
+ False
+
+ This used to be true since the two tries have the same node
+ structure. However, as far as Mapping interface is concerned, they
+ use different keys, i.e. ```set(t0) != set(t1)``. Secondly:
+
+ >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
+ >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
+ >>> t0 == t1
+ True
+
+ This used to be false since the two tries have different node
+ structures (the first one splits key into ``('foo', 'bar.baz')``
+ while the second into ``('foo/bar', 'baz')``). However, their keys
+ are the same, i.e. ```set(t0) == set(t1)``. And lastly:
+
+ >>> t0 = Trie({'foo': 42})
+ >>> t1 = CharTrie({'foo': 42})
+ >>> t0 == t1
+ False
+
+ This used to be true since the two tries have the same node
+ structure. However, the two classes return key as different values.
+ ``pygtrie.Trie`` returns keys as tuples while
+ ``pygtrie.CharTrie`` returns them as strings.
+
+2.4.2: 2021/01/03
+
+- Remove use of ‘super’ in ``setup.py`` to fix compatibility with
+ Python 2.7. This changes build code only; no changes to the library
+ itself.
+
+2.4.1: 2020/11/20
+
+- Remove dependency on ``packaging`` module from ``setup.py`` to fix
+ installation on systems without that package. This changes build
+ code only; no changes to the library itself. [Thanks to Eric
+ McLachlan for reporting]
+
+2.4.0: 2020/11/19 [pulled back from PyPi]
+
+- Change ``children`` argument of the ``node_factory`` passed to
+ ``pygtrie.Trie.traverse`` from a generator to an iterator with
+ a custom bool conversion. This allows checking whether node has
+ children without having to iterate over them (``bool(children)``)
+
+ To test whether this feature is available, one can check whether
+ `Trie.traverse.uses_bool_convertible_children` property is true,
+ e.g.: ``getattr(pygtrie.Trie.traverse,
+ 'uses_bool_convertible_children', False)``.
+
+ [Thanks to Pallab Pain for suggesting the feature]
+
+2.3.3: 2020/04/04
+
+- Fix to ‘``AttributeError``: ``_NoChildren`` object has no
+ attribute ``sorted_items``’ failure when iterating over a trie with
+ sorting enabled. [Thanks to Pallab Pain for reporting]
+
+- Add ``value`` property setter to step objects returned by
+ ``pygtrie.Trie.walk_towards`` et al. This deprecates the
+ ``set`` method.
+
+- The module now exports `pygtrie.__version__` making it possible to
+ determine version of the library at run-time.
+
+2.3.2: 2019/07/18
+
+- Trivial metadata fix
+
+2.3.1: 2019/07/18 [pulled back from PyPi]
+
+- Fix to ``pygtrie.PrefixSet`` initialisation incorrectly storing
+ elements even if their prefixes are also added to the set.
+
+ For example, ``PrefixSet(('foo', 'foobar'))`` incorrectly resulted
+ in a two-element set even though the interface dictates that only
+ ``foo`` is kept (recall that if ``foo`` is member of the set,
+ ``foobar`` is as well). [Thanks to Tal Maimon for reporting]
+
+- Fix to ``pygtrie.Trie.copy`` method not preserving
+ enable-sorting flag and, in case of ``pygtrie.StringTrie``,
+ ``separator`` property.
+
+- Add support for the ``copy`` module so ``copy.copy`` can now be
+ used with trie objects.
+
+- Leafs and nodes with just one child use more memory-optimised
+ representation which reduces overall memory usage of a trie
+ structure.
+
+- Minor performance improvement for adding new elements to
+ a ``pygtrie.PrefixSet``.
+
+- Improvements to string representation of objects which now includes
+ type and, for ``pygtrie.StringTrie`` object, value of separator
+ property.
+
+2.3: 2018/08/10
+
+- New ``pygtrie.Trie.walk_towards`` method allows walking a path
+ towards a node with given key accessing each step of the path.
+ Compared to `pygtrie.Trie.walk_prefixes` method, steps for nodes
+ without assigned values are returned.
+
+- Fix to ``pygtrie.PrefixSet.copy`` not preserving type of backing
+ trie.
+
+- ``pygtrie.StringTrie`` now checks and explicitly rejects empty
+ separators. Previously empty separator would be accepted but lead
+ to confusing errors later on. [Thanks to Waren Long]
+
+- Various documentation improvements, Python 2/3 compatibility and
+ test coverage (python-coverage reports 100%).
+
diff --git a/contrib/python/pygtrie/py3/.dist-info/top_level.txt b/contrib/python/pygtrie/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..5b98eaa2e7
--- /dev/null
+++ b/contrib/python/pygtrie/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+pygtrie
diff --git a/contrib/python/pygtrie/py3/LICENSE b/contrib/python/pygtrie/py3/LICENSE
new file mode 100644
index 0000000000..d645695673
--- /dev/null
+++ b/contrib/python/pygtrie/py3/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/contrib/python/pygtrie/py3/README.rst b/contrib/python/pygtrie/py3/README.rst
new file mode 100644
index 0000000000..41ca83db94
--- /dev/null
+++ b/contrib/python/pygtrie/py3/README.rst
@@ -0,0 +1,66 @@
+pygtrie
+=======
+
+.. image:: https://readthedocs.org/projects/pygtrie/badge/?version=latest
+ :target: http://pygtrie.readthedocs.io/en/latest/
+ :alt: Documentation build status (latest)
+
+.. image:: https://readthedocs.org/projects/pygtrie/badge/?version=stable
+ :target: http://pygtrie.readthedocs.io/en/stable/
+ :alt: Documentation build status (stable)
+
+.. image:: https://api.travis-ci.com/mina86/pygtrie.svg
+ :target: https://travis-ci.com/mina86/pygtrie
+ :alt: Continuous integration status
+
+pygtrie is a pure Python implementation of a trie data structure
+compatible with Python 2.x and Python 3.x.
+
+`Trie data structure <http://en.wikipedia.org/wiki/Trie>`_, also known
+as radix or prefix tree, is a tree associating keys to values where
+all the descendants of a node have a common prefix (associated with
+that node).
+
+The trie module contains ``Trie``, ``CharTrie`` and ``StringTrie``
+classes each implementing a mutable mapping interface, i.e. ``dict``
+interface. As such, in most circumstances, ``Trie`` could be used as
+a drop-in replacement for a ``dict``, but the prefix nature of the
+data structure is trie’s real strength.
+
+The module also contains ``PrefixSet`` class which uses a trie to
+store a set of prefixes such that a key is contained in the set if it
+or its prefix is stored in the set.
+
+Features
+--------
+
+- A full mutable mapping implementation.
+
+- Supports iterating over as well as deleting a subtrie.
+
+- Supports prefix checking as well as shortest and longest prefix
+ look-up.
+
+- Extensible for any kind of user-defined keys.
+
+- A PrefixSet supports “all keys starting with given prefix†logic.
+
+- Can store any value including None.
+
+Installation
+------------
+
+To install pygtrie, simply run::
+
+ pip install pygtrie
+
+or by adding line such as::
+
+ pygtrie == 2.*
+
+to project’s `requirements file
+<https://pip.pypa.io/en/latest/user_guide/#requirements-files>`_.
+Alternatively, if installation from source is desired, it can be
+achieved by executing::
+
+ python setup.py install
diff --git a/contrib/python/pygtrie/py3/pygtrie.py b/contrib/python/pygtrie/py3/pygtrie.py
new file mode 100644
index 0000000000..5e91ef770c
--- /dev/null
+++ b/contrib/python/pygtrie/py3/pygtrie.py
@@ -0,0 +1,1939 @@
+# -*- coding: utf-8 -*-
+"""Pure Python implementation of a trie data structure compatible with Python
+2.x and Python 3.x.
+
+`Trie data structure <http://en.wikipedia.org/wiki/Trie>`_, also known as radix
+or prefix tree, is a tree associating keys to values where all the descendants
+of a node have a common prefix (associated with that node).
+
+The trie module contains :class:`pygtrie.Trie`, :class:`pygtrie.CharTrie` and
+:class:`pygtrie.StringTrie` classes each implementing a mutable mapping
+interface, i.e. :class:`dict` interface. As such, in most circumstances,
+:class:`pygtrie.Trie` could be used as a drop-in replacement for
+a :class:`dict`, but the prefix nature of the data structure is trie’s real
+strength.
+
+The module also contains :class:`pygtrie.PrefixSet` class which uses a trie to
+store a set of prefixes such that a key is contained in the set if it or its
+prefix is stored in the set.
+
+Features
+--------
+
+- A full mutable mapping implementation.
+
+- Supports iterating over as well as deleting of a branch of a trie
+ (i.e. subtrie)
+
+- Supports prefix checking as well as shortest and longest prefix
+ look-up.
+
+- Extensible for any kind of user-defined keys.
+
+- A PrefixSet supports “all keys starting with given prefix†logic.
+
+- Can store any value including None.
+
+For a few simple examples see ``example.py`` file.
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__author__ = 'Michal Nazarewicz <mina86@mina86.com>'
+__copyright__ = ('Copyright 2014-2017 Google LLC',
+ 'Copyright 2018-2020 Michal Nazarewicz <mina86@mina86.com>')
+__version__ = '2.5.0'
+
+
+import copy as _copy
+try:
+ import collections.abc as _abc
+except ImportError: # Python 2 compatibility
+ import collections as _abc
+
+
+class ShortKeyError(KeyError):
+ """Raised when given key is a prefix of an existing longer key
+ but does not have a value associated with itself."""
+
+
+class _NoChildren(object):
+ """Collection representing lack of any children.
+
+ Also acts as an empty iterable and an empty iterator. This isn’t the
+ cleanest designs but it makes various things more concise and avoids object
+ allocations in a few places.
+
+ Don’t create objects of this type directly; instead use _EMPTY singleton.
+ """
+ __slots__ = ()
+
+ def __bool__(self):
+ return False
+ __nonzero__ = __bool__
+ def __len__(self):
+ return 0
+ def __iter__(self):
+ return self
+ iteritems = sorted_items = __iter__
+ def __next__(self):
+ raise StopIteration()
+ next = __next__
+
+ def get(self, _step):
+ return None
+
+ def add(self, parent, step):
+ node = _Node()
+ parent.children = _OneChild(step, node)
+ return node
+
+ require = add
+
+ def copy(self, _make_copy, _queue):
+ return self
+
+ def __deepcopy__(self, memo):
+ return self
+
+ # delete is not implemented on purpose since it should never be called on
+ # a node with no children.
+
+
+_EMPTY = _NoChildren()
+
+
+class _OneChild(object):
+ """Children collection representing a single child."""
+
+ __slots__ = ('step', 'node')
+
+ def __init__(self, step, node):
+ self.step = step
+ self.node = node
+
+ def __bool__(self):
+ return True
+ __nonzero__ = __bool__
+ def __len__(self):
+ return 1
+
+ def sorted_items(self):
+ return [(self.step, self.node)]
+
+ def iteritems(self):
+ return iter(((self.step, self.node),))
+
+ def get(self, step):
+ return self.node if step == self.step else None
+
+ def add(self, parent, step):
+ node = _Node()
+ parent.children = _Children((self.step, self.node), (step, node))
+ return node
+
+ def require(self, parent, step):
+ return self.node if self.step == step else self.add(parent, step)
+
+ def merge(self, other, queue):
+ """Moves children from other into this object."""
+ if type(other) == _OneChild and other.step == self.step:
+ queue.append((self.node, other.node))
+ return self
+ else:
+ children = _Children((self.step, self.node))
+ children.merge(other, queue)
+ return children
+
+ def delete(self, parent, _step):
+ parent.children = _EMPTY
+
+ def copy(self, make_copy, queue):
+ cpy = _OneChild(make_copy(self.step), self.node.shallow_copy(make_copy))
+ queue.append((cpy.node,))
+ return cpy
+
+
+class _Children(dict):
+ """Children collection representing more than one child."""
+
+ __slots__ = ()
+
+ def __init__(self, *items):
+ super(_Children, self).__init__(items)
+
+ if hasattr(dict, 'iteritems'): # Python 2 compatibility
+ def sorted_items(self):
+ items = self.items()
+ items.sort()
+ return items
+ else:
+ def sorted_items(self):
+ return sorted(self.items())
+
+ def iteritems(self):
+ return iter(self.items())
+
+ def add(self, _parent, step):
+ self[step] = node = _Node()
+ return node
+
+ def require(self, _parent, step):
+ return self.setdefault(step, _Node())
+
+ def merge(self, other, queue):
+ """Moves children from other into this object."""
+ for step, other_node in other.iteritems():
+ node = self.setdefault(step, other_node)
+ if node is not other_node:
+ queue.append((node, other_node))
+ return self
+
+ def delete(self, parent, step):
+ del self[step]
+ if len(self) == 1:
+ parent.children = _OneChild(*self.popitem())
+
+ def copy(self, make_copy, queue):
+ cpy = _Children()
+ cpy.update((make_copy(step), node.shallow_copy(make_copy))
+ for step, node in self.items())
+ queue.append(cpy.values())
+ return cpy
+
+
+class _Node(object):
+ """A single node of a trie.
+
+ Stores value associated with the node and dictionary of children.
+ """
+ __slots__ = ('children', 'value')
+
+ def __init__(self):
+ self.children = _EMPTY
+ self.value = _EMPTY
+
+ def merge(self, other, overwrite):
+ """Move children from other node into this one.
+
+ Args:
+ other: Other node to move children and value from.
+ overwrite: Whether to overwrite existing node values.
+ """
+ queue = [(self, other)]
+ while queue:
+ lhs, rhs = queue.pop()
+ if lhs.value is _EMPTY or (overwrite and rhs.value is not _EMPTY):
+ lhs.value = rhs.value
+ if lhs.children is _EMPTY:
+ lhs.children = rhs.children
+ elif rhs.children is not _EMPTY:
+ lhs.children = lhs.children.merge(rhs.children, queue)
+ rhs.children = _EMPTY
+
+ def iterate(self, path, shallow, iteritems):
+ """Yields all the nodes with values associated to them in the trie.
+
+ Args:
+ path: Path leading to this node. Used to construct the key when
+ returning value of this node and as a prefix for children.
+ shallow: Perform a shallow traversal, i.e. do not yield nodes if
+ their prefix has been yielded.
+ iteritems: A callable taking ``node.children`` as sole argument and
+ returning an iterable of children as ``(step, node)`` pair. The
+ callable would typically call ``iteritems`` or ``sorted_items``
+ method on the argument depending on whether sorted output is
+ desired.
+
+ Yields:
+ ``(path, value)`` tuples.
+ """
+ # Use iterative function with stack on the heap so we don't hit Python's
+ # recursion depth limits.
+ node = self
+ stack = []
+ while True:
+ if node.value is not _EMPTY:
+ yield path, node.value
+
+ if (not shallow or node.value is _EMPTY) and node.children:
+ stack.append(iter(iteritems(node.children)))
+ path.append(None)
+
+ while True:
+ try:
+ step, node = next(stack[-1])
+ path[-1] = step
+ break
+ except StopIteration:
+ stack.pop()
+ path.pop()
+ except IndexError:
+ return
+
+ def traverse(self, node_factory, path_conv, path, iteritems):
+ """Traverses the node and returns another type of node from factory.
+
+ Args:
+ node_factory: Callable to construct return value.
+ path_conv: Callable to convert node path to a key.
+ path: Current path for this node.
+ iteritems: A callable taking ``node.children`` as sole argument and
+ returning an iterable of children as ``(step, node)`` pair. The
+ callable would typically call ``iteritems`` or ``sorted_items``
+ method on the argument depending on whether sorted output is
+ desired.
+
+ Returns:
+ An object constructed by calling node_factory(path_conv, path,
+ children, value=...), where children are constructed by node_factory
+ from the children of this node. There doesn't need to be 1:1
+ correspondence between original nodes in the trie and constructed
+ nodes (see make_test_node_and_compress in test.py).
+ """
+ children = self.children and (
+ node.traverse(node_factory, path_conv, path + [step], iteritems)
+ for step, node in iteritems(self.children))
+
+ value_maybe = ()
+ if self.value is not _EMPTY:
+ value_maybe = (self.value,)
+
+ return node_factory(path_conv, tuple(path), children, *value_maybe)
+
+ def equals(self, other):
+ """Returns whether this and other node are recursively equal."""
+ # Like iterate, we don't recurse so this works on deep tries.
+ a, b = self, other
+ stack = []
+ while True:
+ if a.value != b.value or len(a.children) != len(b.children):
+ return False
+ if len(a.children) == 1:
+ # We know a.children and b.children are both _OneChild objects
+ # but pylint doesn’t recognise that: pylint: disable=no-member
+ if a.children.step != b.children.step:
+ return False
+ a = a.children.node
+ b = b.children.node
+ continue
+ if a.children:
+ stack.append((a.children.iteritems(), b.children))
+
+ while True:
+ try:
+ key, a = next(stack[-1][0])
+ b = stack[-1][1][key]
+ break
+ except StopIteration:
+ stack.pop()
+ except IndexError:
+ return True
+ except KeyError:
+ return False
+
+ __bool__ = __nonzero__ = __hash__ = None
+
+ def shallow_copy(self, make_copy):
+ """Returns a copy of the node which shares the children property."""
+ cpy = _Node()
+ cpy.children = self.children
+ cpy.value = make_copy(self.value)
+ return cpy
+
+ def copy(self, make_copy):
+ """Returns a copy of the node structure."""
+ cpy = self.shallow_copy(make_copy)
+ queue = [(cpy,)]
+ while queue:
+ for node in queue.pop():
+ node.children = node.children.copy(make_copy, queue)
+ return cpy
+
+ def __getstate__(self):
+ """Get state used for pickling.
+
+ The state is encoded as a list of simple commands which consist of an
+ integer and some command-dependent number of arguments. The commands
+ modify what the current node is by navigating the trie up and down and
+ setting node values. Possible commands are:
+
+ * [n, step0, step1, ..., stepn-1, value], for n >= 0, specifies step
+ needed to reach the next current node as well as its new value. There
+ is no way to create a child node without setting its (or its
+ descendant's) value.
+
+ * [-n], for -n < 0, specifies to go up n steps in the trie.
+
+ When encoded as a state, the commands are flattened into a single list.
+
+ For example::
+
+ [ 0, 'Root',
+ 2, 'Foo', 'Bar', 'Root/Foo/Bar Node',
+ -1,
+ 1, 'Baz', 'Root/Foo/Baz Node',
+ -2,
+ 1, 'Qux', 'Root/Qux Node' ]
+
+ Creates the following hierarchy::
+
+ -* value: Root
+ +-- Foo --* no value
+ | +-- Bar -- * value: Root/Foo/Bar Node
+ | +-- Baz -- * value: Root/Foo/Baz Node
+ +-- Qux -- * value: Root/Qux Node
+
+ Returns:
+ A pickable state which can be passed to :func:`_Node.__setstate__`
+ to reconstruct the node and its full hierarchy.
+ """
+ # Like iterate, we don't recurse so pickling works on deep tries.
+ state = [] if self.value is _EMPTY else [0]
+ last_cmd = 0
+ node = self
+ stack = []
+ while True:
+ if node.value is not _EMPTY:
+ last_cmd = 0
+ state.append(node.value)
+ stack.append(node.children.iteritems())
+
+ while True:
+ step, node = next(stack[-1], (None, None))
+ if node is not None:
+ break
+
+ if last_cmd < 0:
+ state[-1] -= 1
+ else:
+ last_cmd = -1
+ state.append(-1)
+ stack.pop()
+ if not stack:
+ state.pop() # Final -n command is not necessary
+ return state
+
+ if last_cmd > 0:
+ last_cmd += 1
+ state[-last_cmd] += 1
+ else:
+ last_cmd = 1
+ state.append(1)
+ state.append(step)
+
+ def __setstate__(self, state):
+ """Unpickles node. See :func:`_Node.__getstate__`."""
+ self.__init__()
+ state = iter(state)
+ stack = [self]
+ for cmd in state:
+ if cmd < 0:
+ del stack[cmd:]
+ else:
+ while cmd > 0:
+ parent = stack[-1]
+ stack.append(parent.children.add(parent, next(state)))
+ cmd -= 1
+ stack[-1].value = next(state)
+
+
+class Trie(_abc.MutableMapping):
+ """A trie implementation with dict interface plus some extensions.
+
+ Keys used with the :class:`pygtrie.Trie` class must be iterable which each
+ component being a hashable objects. In other words, for a given key,
+ ``dict.fromkeys(key)`` must be valid expression.
+
+ In particular, strings work well as trie keys, however when getting them
+ back (for example via :func:`Trie.iterkeys` method), instead of strings,
+ tuples of characters are produced. For that reason,
+ :class:`pygtrie.CharTrie` or :class:`pygtrie.StringTrie` classes may be
+ preferred when using string keys.
+ """
+
+ def __init__(self, *args, **kwargs):
+ """Initialises the trie.
+
+ Arguments are interpreted the same way :func:`Trie.update` interprets
+ them.
+ """
+ self._root = _Node()
+ self._iteritems = self._ITERITEMS_CALLBACKS[0]
+ self.update(*args, **kwargs)
+
+ _ITERITEMS_CALLBACKS = (lambda x: x.iteritems(), lambda x: x.sorted_items())
+
+ def enable_sorting(self, enable=True):
+ """Enables sorting of child nodes when iterating and traversing.
+
+ Normally, child nodes are not sorted when iterating or traversing over
+ the trie (just like dict elements are not sorted). This method allows
+ sorting to be enabled (which was the behaviour prior to pygtrie 2.0
+ release).
+
+ For Trie class, enabling sorting of children is identical to simply
+ sorting the list of items since Trie returns keys as tuples. However,
+ for other implementations such as StringTrie the two may behave subtly
+ different. For example, sorting items might produce::
+
+ root/foo-bar
+ root/foo/baz
+
+ even though foo comes before foo-bar.
+
+ Args:
+ enable: Whether to enable sorting of child nodes.
+ """
+ self._iteritems = self._ITERITEMS_CALLBACKS[bool(enable)]
+
+ def __getstate__(self):
+ # encode self._iteritems as self._sorted when pickling
+ state = self.__dict__.copy()
+ callback = state.pop('_iteritems', None)
+ state['_sorted'] = callback is self._ITERITEMS_CALLBACKS[1]
+ return state
+
+ def __setstate__(self, state):
+ # translate self._sorted back to _iteritems when unpickling
+ self.__dict__ = state
+ self.enable_sorting(state.pop('_sorted'))
+
+ def clear(self):
+ """Removes all the values from the trie."""
+ self._root = _Node()
+
+ def update(self, *args, **kwargs): # pylint: disable=arguments-differ
+ """Updates stored values. Works like :meth:`dict.update`."""
+ if len(args) > 1:
+ raise ValueError('update() takes at most one positional argument, '
+ '%d given.' % len(args))
+ # We have this here instead of just letting MutableMapping.update()
+ # handle things because it will iterate over keys and for each key
+ # retrieve the value. With Trie, this may be expensive since the path
+ # to the node would have to be walked twice. Instead, we have our own
+ # implementation where iteritems() is used avoiding the unnecessary
+ # value look-up.
+ if args and isinstance(args[0], Trie):
+ for key, value in args[0].items():
+ self[key] = value
+ args = ()
+ super(Trie, self).update(*args, **kwargs)
+
+ def merge(self, other, overwrite=False):
+ """Moves nodes from other trie into this one.
+
+ The merging happens at trie structure level and as such is different
+ than iterating over items of one trie and setting them in the other
+ trie.
+
+ The merging may happen between different types of tries resulting in
+ different (key, value) pairs in the destination trie compared to the
+ source. For example, merging two :class:`pygtrie.StringTrie` objects
+ each using different separators will work as if the other trie had
+ separator of this trie. Similarly, a :class:`pygtrie.CharTrie` may be
+ merged into a :class:`pygtrie.StringTrie` but when keys are read those
+ will be joined by the separator. For example:
+
+ >>> import pygtrie
+ >>> st = pygtrie.StringTrie(separator='.')
+ >>> st.merge(pygtrie.StringTrie({'foo/bar': 42}))
+ >>> list(st.items())
+ [('foo.bar', 42)]
+ >>> st.merge(pygtrie.CharTrie({'baz': 24}))
+ >>> sorted(st.items())
+ [('b.a.z', 24), ('foo.bar', 42)]
+
+ Not all tries can be merged into other tries. For example,
+ a :class:`pygtrie.StringTrie` may not be merged into
+ a :class:`pygtrie.CharTrie` because the latter imposes a requirement for
+ each component in the key to be exactly one character while in the
+ former components may be arbitrary length.
+
+ Note that the other trie is cleared and any references or iterators over
+ it are invalidated. To preserve other’s value it needs to be copied
+ first.
+
+ Args:
+ other: Other trie to move nodes from.
+ overwrite: Whether to overwrite existing values in this trie.
+ """
+ if isinstance(self, type(other)):
+ self._merge_impl(self, other, overwrite=overwrite)
+ else:
+ other._merge_impl(self, other, overwrite=overwrite) # pylint: disable=protected-access
+ other.clear()
+
+ @classmethod
+ def _merge_impl(cls, dst, src, overwrite):
+ # pylint: disable=protected-access
+ dst._root.merge(src._root, overwrite=overwrite)
+
+ def copy(self, __make_copy=lambda x: x):
+ """Returns a shallow copy of the object."""
+ # pylint: disable=protected-access
+ cpy = self.__class__()
+ cpy.__dict__ = self.__dict__.copy()
+ cpy._root = self._root.copy(__make_copy)
+ return cpy
+
+ def __copy__(self):
+ return self.copy()
+
+ def __deepcopy__(self, memo):
+ return self.copy(lambda x: _copy.deepcopy(x, memo))
+
+ @classmethod
+ def fromkeys(cls, keys, value=None):
+ """Creates a new trie with given keys set.
+
+ This is roughly equivalent to calling the constructor with a ``(key,
+ value) for key in keys`` generator.
+
+ Args:
+ keys: An iterable of keys that should be set in the new trie.
+ value: Value to associate with given keys.
+
+ Returns:
+ A new trie where each key from ``keys`` has been set to the given
+ value.
+ """
+ trie = cls()
+ for key in keys:
+ trie[key] = value
+ return trie
+
+ def _get_node(self, key):
+ """Returns node for given key. Creates it if requested.
+
+ Args:
+ key: A key to look for.
+
+ Returns:
+ ``(node, trace)`` tuple where ``node`` is the node for given key and
+ ``trace`` is a list specifying path to reach the node including all
+ the encountered nodes. Each element of trace is a ``(step, node)``
+ tuple where ``step`` is a step from parent node to given node and
+ ``node`` is node on the path. The first element of the path is
+ always ``(None, self._root)``.
+
+ Raises:
+ KeyError: If there is no node for the key.
+ """
+ node = self._root
+ trace = [(None, node)]
+ for step in self.__path_from_key(key):
+ # pylint thinks node.children is always _NoChildren and thus that
+ # we’re assigning None here; pylint: disable=assignment-from-none
+ node = node.children.get(step)
+ if node is None:
+ raise KeyError(key)
+ trace.append((step, node))
+ return node, trace
+
+ def _set_node(self, key, value, only_if_missing=False):
+ """Sets value for a given key.
+
+ Args:
+ key: Key to set value of.
+ value: Value to set to.
+ only_if_missing: If true, value won't be changed if the key is
+ already associated with a value.
+
+ Returns:
+ The node.
+ """
+ node = self._root
+ for step in self.__path_from_key(key):
+ node = node.children.require(node, step)
+ if node.value is _EMPTY or not only_if_missing:
+ node.value = value
+ return node
+
+ def _set_node_if_no_prefix(self, key):
+ """Sets given key to True but only if none of its prefixes are present.
+
+ If value is set, removes all ancestors of the node.
+
+ This is a method for exclusive use by PrefixSet.
+
+ Args:
+ key: Key to set value of.
+ """
+ steps = iter(self.__path_from_key(key))
+ node = self._root
+ try:
+ while node.value is _EMPTY:
+ node = node.children.require(node, next(steps))
+ except StopIteration:
+ node.value = True
+ node.children = _EMPTY
+
+ def __iter__(self):
+ return self.iterkeys()
+
+ # pylint: disable=arguments-differ
+
+ def iteritems(self, prefix=_EMPTY, shallow=False):
+ """Yields all nodes with associated values with given prefix.
+
+ Only nodes with values are output. For example::
+
+ >>> import pygtrie
+ >>> t = pygtrie.StringTrie()
+ >>> t['foo'] = 'Foo'
+ >>> t['foo/bar/baz'] = 'Baz'
+ >>> t['qux'] = 'Qux'
+ >>> sorted(t.items())
+ [('foo', 'Foo'), ('foo/bar/baz', 'Baz'), ('qux', 'Qux')]
+
+ Items are generated in topological order (i.e. parents before child
+ nodes) but the order of siblings is unspecified. At an expense of
+ efficiency, :func:`Trie.enable_sorting` method can turn deterministic
+ ordering of siblings.
+
+ With ``prefix`` argument, only items with specified prefix are generated
+ (i.e. only given subtrie is traversed) as demonstrated by::
+
+ >>> t.items(prefix='foo')
+ [('foo', 'Foo'), ('foo/bar/baz', 'Baz')]
+
+ With ``shallow`` argument, if a node has value associated with it, it's
+ children are not traversed even if they exist which can be seen in::
+
+ >>> sorted(t.items(shallow=True))
+ [('foo', 'Foo'), ('qux', 'Qux')]
+
+ Args:
+ prefix: Prefix to limit iteration to.
+ shallow: Perform a shallow traversal, i.e. do not yield items if
+ their prefix has been yielded.
+
+ Yields:
+ ``(key, value)`` tuples.
+
+ Raises:
+ KeyError: If ``prefix`` does not match any node.
+ """
+ node, _ = self._get_node(prefix)
+ for path, value in node.iterate(list(self.__path_from_key(prefix)),
+ shallow, self._iteritems):
+ yield (self._key_from_path(path), value)
+
+ def iterkeys(self, prefix=_EMPTY, shallow=False):
+ """Yields all keys having associated values with given prefix.
+
+ This is equivalent to taking first element of tuples generated by
+ :func:`Trie.iteritems` which see for more detailed documentation.
+
+ Args:
+ prefix: Prefix to limit iteration to.
+ shallow: Perform a shallow traversal, i.e. do not yield keys if
+ their prefix has been yielded.
+
+ Yields:
+ All the keys (with given prefix) with associated values in the trie.
+
+ Raises:
+ KeyError: If ``prefix`` does not match any node.
+ """
+ for key, _ in self.iteritems(prefix=prefix, shallow=shallow):
+ yield key
+
+ def itervalues(self, prefix=_EMPTY, shallow=False):
+ """Yields all values associated with keys with given prefix.
+
+ This is equivalent to taking second element of tuples generated by
+ :func:`Trie.iteritems` which see for more detailed documentation.
+
+ Args:
+ prefix: Prefix to limit iteration to.
+ shallow: Perform a shallow traversal, i.e. do not yield values if
+ their prefix has been yielded.
+
+ Yields:
+ All the values associated with keys (with given prefix) in the trie.
+
+ Raises:
+ KeyError: If ``prefix`` does not match any node.
+ """
+ node, _ = self._get_node(prefix)
+ for _, value in node.iterate(list(self.__path_from_key(prefix)),
+ shallow, self._iteritems):
+ yield value
+
+ def items(self, prefix=_EMPTY, shallow=False):
+ """Returns a list of ``(key, value)`` pairs in given subtrie.
+
+ This is equivalent to constructing a list from generator returned by
+ :func:`Trie.iteritems` which see for more detailed documentation.
+ """
+ return list(self.iteritems(prefix=prefix, shallow=shallow))
+
+ def keys(self, prefix=_EMPTY, shallow=False):
+ """Returns a list of all the keys, with given prefix, in the trie.
+
+ This is equivalent to constructing a list from generator returned by
+ :func:`Trie.iterkeys` which see for more detailed documentation.
+ """
+ return list(self.iterkeys(prefix=prefix, shallow=shallow))
+
+ def values(self, prefix=_EMPTY, shallow=False):
+ """Returns a list of values in given subtrie.
+
+ This is equivalent to constructing a list from generator returned by
+ :func:`Trie.itervalues` which see for more detailed documentation.
+ """
+ return list(self.itervalues(prefix=prefix, shallow=shallow))
+
+ def __len__(self):
+ """Returns number of values in a trie.
+
+ Note that this method is expensive as it iterates over the whole trie.
+ """
+ return sum(1 for _ in self.itervalues())
+
+ def __bool__(self):
+ return self._root.value is not _EMPTY or bool(self._root.children)
+
+ __nonzero__ = __bool__
+ __hash__ = None
+
+ HAS_VALUE = 1
+ HAS_SUBTRIE = 2
+
+ def has_node(self, key):
+ """Returns whether given node is in the trie.
+
+ Return value is a bitwise or of ``HAS_VALUE`` and ``HAS_SUBTRIE``
+ constants indicating node has a value associated with it and that it is
+ a prefix of another existing key respectively. Both of those are
+ independent of each other and all of the four combinations are possible.
+ For example::
+
+ >>> import pygtrie
+ >>> t = pygtrie.StringTrie()
+ >>> t['foo/bar'] = 'Bar'
+ >>> t['foo/bar/baz'] = 'Baz'
+ >>> t.has_node('qux') == 0
+ True
+ >>> t.has_node('foo/bar/baz') == pygtrie.Trie.HAS_VALUE
+ True
+ >>> t.has_node('foo') == pygtrie.Trie.HAS_SUBTRIE
+ True
+ >>> t.has_node('foo/bar') == (pygtrie.Trie.HAS_VALUE |
+ ... pygtrie.Trie.HAS_SUBTRIE)
+ True
+
+ There are two higher level methods built on top of this one which give
+ easier interface for the information. :func:`Trie.has_key` returns
+ whether node has a value associated with it and :func:`Trie.has_subtrie`
+ checks whether node is a prefix. Continuing previous example::
+
+ >>> t.has_key('qux'), t.has_subtrie('qux')
+ (False, False)
+ >>> t.has_key('foo/bar/baz'), t.has_subtrie('foo/bar/baz')
+ (True, False)
+ >>> t.has_key('foo'), t.has_subtrie('foo')
+ (False, True)
+ >>> t.has_key('foo/bar'), t.has_subtrie('foo/bar')
+ (True, True)
+
+ Args:
+ key: A key to look for.
+
+ Returns:
+ Non-zero if node exists and if it does a bit-field denoting whether
+ it has a value associated with it and whether it has a subtrie.
+ """
+ try:
+ node, _ = self._get_node(key)
+ except KeyError:
+ return 0
+ return ((self.HAS_VALUE * (node.value is not _EMPTY)) |
+ (self.HAS_SUBTRIE * bool(node.children)))
+
+ def has_key(self, key):
+ """Indicates whether given key has value associated with it.
+
+ See :func:`Trie.has_node` for more detailed documentation.
+ """
+ return bool(self.has_node(key) & self.HAS_VALUE)
+
+ def has_subtrie(self, key):
+ """Returns whether given key is a prefix of another key in the trie.
+
+ See :func:`Trie.has_node` for more detailed documentation.
+ """
+ return bool(self.has_node(key) & self.HAS_SUBTRIE)
+
+ @staticmethod
+ def _slice_maybe(key_or_slice):
+ """Checks whether argument is a slice or a plain key.
+
+ Args:
+ key_or_slice: A key or a slice to test.
+
+ Returns:
+ ``(key, is_slice)`` tuple. ``is_slice`` indicates whether
+ ``key_or_slice`` is a slice and ``key`` is either ``key_or_slice``
+ itself (if it's not a slice) or slice's start position.
+
+ Raises:
+ TypeError: If ``key_or_slice`` is a slice whose stop or step are not
+ ``None`` In other words, only ``[key:]`` slices are valid.
+ """
+ if isinstance(key_or_slice, slice):
+ if key_or_slice.stop is not None or key_or_slice.step is not None:
+ raise TypeError(key_or_slice)
+ return key_or_slice.start, True
+ return key_or_slice, False
+
+ def __getitem__(self, key_or_slice):
+ """Returns value associated with given key or raises KeyError.
+
+ When argument is a single key, value for that key is returned (or
+ :class:`KeyError` exception is thrown if the node does not exist or has
+ no value associated with it).
+
+ When argument is a slice, it must be one with only `start` set in which
+ case the access is identical to :func:`Trie.itervalues` invocation with
+ prefix argument.
+
+ Example:
+
+ >>> import pygtrie
+ >>> t = pygtrie.StringTrie()
+ >>> t['foo/bar'] = 'Bar'
+ >>> t['foo/baz'] = 'Baz'
+ >>> t['qux'] = 'Qux'
+ >>> t['foo/bar']
+ 'Bar'
+ >>> sorted(t['foo':])
+ ['Bar', 'Baz']
+ >>> t['foo'] # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ ...
+ ShortKeyError: 'foo'
+
+ Args:
+ key_or_slice: A key or a slice to look for.
+
+ Returns:
+ If a single key is passed, a value associated with given key. If
+ a slice is passed, a generator of values in specified subtrie.
+
+ Raises:
+ ShortKeyError: If the key has no value associated with it but is
+ a prefix of some key with a value. Note that
+ :class:`ShortKeyError` is subclass of :class:`KeyError`.
+ KeyError: If key has no value associated with it nor is a prefix of
+ an existing key.
+ TypeError: If ``key_or_slice`` is a slice but it's stop or step are
+ not ``None``.
+ """
+ if self._slice_maybe(key_or_slice)[1]:
+ return self.itervalues(key_or_slice.start)
+ node, _ = self._get_node(key_or_slice)
+ if node.value is _EMPTY:
+ raise ShortKeyError(key_or_slice)
+ return node.value
+
+ def __setitem__(self, key_or_slice, value):
+ """Sets value associated with given key.
+
+ If `key_or_slice` is a key, simply associate it with given value. If it
+ is a slice (which must have `start` set only), it in addition clears any
+ subtrie that might have been attached to particular key. For example::
+
+ >>> import pygtrie
+ >>> t = pygtrie.StringTrie()
+ >>> t['foo/bar'] = 'Bar'
+ >>> t['foo/baz'] = 'Baz'
+ >>> sorted(t.keys())
+ ['foo/bar', 'foo/baz']
+ >>> t['foo':] = 'Foo'
+ >>> t.keys()
+ ['foo']
+
+ Args:
+ key_or_slice: A key to look for or a slice. If it is a slice, the
+ whole subtrie (if present) will be replaced by a single node
+ with given value set.
+ value: Value to set.
+
+ Raises:
+ TypeError: If key is a slice whose stop or step are not None.
+ """
+ key, is_slice = self._slice_maybe(key_or_slice)
+ node = self._set_node(key, value)
+ if is_slice:
+ node.children = _EMPTY
+
+ def setdefault(self, key, default=None):
+ """Sets value of a given node if not set already. Also returns it.
+
+ In contrast to :func:`Trie.__setitem__`, this method does not accept
+ slice as a key.
+ """
+ return self._set_node(key, default, only_if_missing=True).value
+
+ @staticmethod
+ def _pop_value(trace):
+ """Removes value from given node and removes any empty nodes.
+
+ Args:
+ trace: Trace to the node to cleanup as returned by
+ :func:`Trie._get_node`. The last element of the trace denotes
+ the node to get value of.
+
+ Returns:
+ Value which was held in the node at the end of specified trace.
+ This may be _EMPTY if the node didn’t have a value in the first
+ place.
+ """
+ i = len(trace) - 1 # len(path) >= 1 since root is always there
+ step, node = trace[i]
+ value, node.value = node.value, _EMPTY
+ while i and node.value is _EMPTY and not node.children:
+ i -= 1
+ parent_step, parent = trace[i]
+ parent.children.delete(parent, step)
+ step, node = parent_step, parent
+ return value
+
+ def pop(self, key, default=_EMPTY):
+ """Deletes value associated with given key and returns it.
+
+ Args:
+ key: A key to look for.
+ default: If specified, value that will be returned if given key has
+ no value associated with it. If not specified, method will
+ throw KeyError in such cases.
+
+ Returns:
+ Removed value, if key had value associated with it, or ``default``
+ (if given).
+
+ Raises:
+ ShortKeyError: If ``default`` has not been specified and the key has
+ no value associated with it but is a prefix of some key with
+ a value. Note that :class:`ShortKeyError` is subclass of
+ :class:`KeyError`.
+ KeyError: If default has not been specified and key has no value
+ associated with it nor is a prefix of an existing key.
+ """
+ try:
+ _, trace = self._get_node(key)
+ except KeyError:
+ if default is not _EMPTY:
+ return default
+ raise
+ value = self._pop_value(trace)
+ if value is not _EMPTY:
+ return value
+ if default is not _EMPTY:
+ return default
+ raise ShortKeyError()
+
+ def popitem(self):
+ """Deletes an arbitrary value from the trie and returns it.
+
+ There is no guarantee as to which item is deleted and returned. Neither
+ in respect to its lexicographical nor topological order.
+
+ Returns:
+ ``(key, value)`` tuple indicating deleted key.
+
+ Raises:
+ KeyError: If the trie is empty.
+ """
+ if not self:
+ raise KeyError()
+ node = self._root
+ trace = [(None, node)]
+ while node.value is _EMPTY:
+ step, node = next(node.children.iteritems())
+ trace.append((step, node))
+ key = self._key_from_path((step for step, _ in trace[1:]))
+ return key, self._pop_value(trace)
+
+ def __delitem__(self, key_or_slice):
+ """Deletes value associated with given key or raises KeyError.
+
+ If argument is a key, value associated with it is deleted. If the key
+ is also a prefix, its descendents are not affected. On the other hand,
+ if the argument is a slice (in which case it must have only start set),
+ the whole subtrie is removed. For example::
+
+ >>> import pygtrie
+ >>> t = pygtrie.StringTrie()
+ >>> t['foo'] = 'Foo'
+ >>> t['foo/bar'] = 'Bar'
+ >>> t['foo/bar/baz'] = 'Baz'
+ >>> del t['foo/bar']
+ >>> t.keys()
+ ['foo', 'foo/bar/baz']
+ >>> del t['foo':]
+ >>> t.keys()
+ []
+
+ Args:
+ key_or_slice: A key to look for or a slice. If key is a slice, the
+ whole subtrie will be removed.
+
+ Raises:
+ ShortKeyError: If the key has no value associated with it but is
+ a prefix of some key with a value. This is not thrown if
+ key_or_slice is a slice -- in such cases, the whole subtrie is
+ removed. Note that :class:`ShortKeyError` is subclass of
+ :class:`KeyError`.
+ KeyError: If key has no value associated with it nor is a prefix of
+ an existing key.
+ TypeError: If key is a slice whose stop or step are not ``None``.
+ """
+ key, is_slice = self._slice_maybe(key_or_slice)
+ node, trace = self._get_node(key)
+ if is_slice:
+ node.children = _EMPTY
+ elif node.value is _EMPTY:
+ raise ShortKeyError(key)
+ self._pop_value(trace)
+
+ class _NoneStep(object):
+ """Representation of a non-existent step towards non-existent node."""
+
+ __slots__ = ()
+
+ def __bool__(self):
+ return False
+ __nonzero__ = __bool__
+
+ def get(self, default=None):
+ return default
+
+ is_set = has_subtrie = property(__bool__)
+ key = value = property(lambda self: None)
+
+ def __getitem__(self, index):
+ """Makes object appear like a (key, value) tuple.
+
+ This is deprecated and for backwards-compatibility only. Prefer
+ using ``key`` and ``value`` properties directly.
+
+ Args:
+ index: Element index to return. Zero for key, one for value.
+
+ Returns:
+ ``self.key`` if index is ``0``, ``self.value`` if it's ``1``.
+ Otherwise raises an IndexError exception.
+
+ Raises:
+ IndexError: if index is not 0 or 1.
+ KeyError: if index is 1 but node has no value assigned.
+ """
+ if index == 0:
+ return self.key
+ if index == 1:
+ return self.value
+ raise IndexError('index out of range')
+
+ def __repr__(self):
+ return '(None Step)'
+
+ class _Step(_NoneStep):
+ """Representation of a single step on a path towards particular node."""
+
+ __slots__ = ('_trie', '_path', '_pos', '_node', '__key')
+
+ def __init__(self, trie, path, pos, node):
+ self._trie = trie
+ self._path = path
+ self._pos = pos
+ self._node = node
+
+ def __bool__(self):
+ return True
+ __nonzero__ = __bool__
+
+ @property
+ def is_set(self):
+ """Returns whether the node has value assigned to it."""
+ return self._node.value is not _EMPTY
+
+ @property
+ def has_subtrie(self):
+ """Returns whether the node has any children."""
+ return bool(self._node.children)
+
+ def get(self, default=None):
+ """Returns node's value or the default if value is not assigned."""
+ v = self._node.value
+ return default if v is _EMPTY else v
+
+ def set(self, value):
+ """Deprecated. Use ``step.value = value`` instead."""
+ self._node.value = value
+
+ def setdefault(self, value):
+ """Assigns value to the node if one is not set then returns it."""
+ if self._node.value is _EMPTY:
+ self._node.value = value
+ return self._node.value
+
+ def __repr__(self):
+ return '(%r: %r)' % (self.key, self.value)
+
+ @property
+ def key(self):
+ """Returns key of the node."""
+ if not hasattr(self, '_Step__key'):
+ # pylint:disable=protected-access,attribute-defined-outside-init
+ self.__key = self._trie._key_from_path(self._path[:self._pos])
+ return self.__key
+
+ @property
+ def value(self):
+ """Returns node's value or raises KeyError."""
+ v = self._node.value
+ if v is _EMPTY:
+ raise ShortKeyError(self.key)
+ return v
+
+ @value.setter
+ def value(self, value):
+ self._node.value = value
+
+ _NONE_STEP = _NoneStep()
+
+ def walk_towards(self, key):
+ """Yields nodes on the path to given node.
+
+ Args:
+ key: Key of the node to look for.
+
+ Yields:
+ :class:`pygtrie.Trie._Step` objects which can be used to extract or
+ set node's value as well as get node's key.
+
+ When representing nodes with assigned values, the objects can be
+ treated as ``(k, value)`` pairs denoting keys with associated values
+ encountered on the way towards the specified key. This is
+ deprecated, prefer using ``key`` and ``value`` properties or ``get``
+ method of the object.
+
+ Raises:
+ KeyError: If node with given key does not exist. It's all right if
+ they value is not assigned to the node provided it has a child
+ node. Because the method is a generator, the exception is
+ raised only once a missing node is encountered.
+ """
+ node = self._root
+ path = self.__path_from_key(key)
+ pos = 0
+ while True:
+ yield self._Step(self, path, pos, node)
+ if pos == len(path):
+ break
+ # pylint thinks node.children is always _NoChildren and thus that
+ # we’re assigning None here; pylint: disable=assignment-from-none
+ node = node.children.get(path[pos])
+ if node is None:
+ raise KeyError(key)
+ pos += 1
+
+ def prefixes(self, key):
+ """Walks towards the node specified by key and yields all found items.
+
+ Example:
+
+ >>> import pygtrie
+ >>> t = pygtrie.StringTrie()
+ >>> t['foo'] = 'Foo'
+ >>> t['foo/bar/baz'] = 'Baz'
+ >>> list(t.prefixes('foo/bar/baz/qux'))
+ [('foo': 'Foo'), ('foo/bar/baz': 'Baz')]
+ >>> list(t.prefixes('does/not/exist'))
+ []
+
+ Args:
+ key: Key to look for.
+
+ Yields:
+ :class:`pygtrie.Trie._Step` objects which can be used to extract or
+ set node's value as well as get node's key.
+
+ The objects can be treated as ``(k, value)`` pairs denoting keys
+ with associated values encountered on the way towards the specified
+ key. This is deprecated, prefer using ``key`` and ``value``
+ properties of the object.
+ """
+ try:
+ for step in self.walk_towards(key):
+ if step.is_set:
+ yield step
+ except KeyError:
+ pass
+
+ def shortest_prefix(self, key):
+ """Finds the shortest prefix of a key with a value.
+
+ This is roughly equivalent to taking the first object yielded by
+ :func:`Trie.prefixes` with additional handling for situations when no
+ prefixes are found.
+
+ Example:
+
+ >>> import pygtrie
+ >>> t = pygtrie.StringTrie()
+ >>> t['foo'] = 'Foo'
+ >>> t['foo/bar/baz'] = 'Baz'
+ >>> t.shortest_prefix('foo/bar/baz/qux')
+ ('foo': 'Foo')
+ >>> t.shortest_prefix('foo/bar/baz/qux').key
+ 'foo'
+ >>> t.shortest_prefix('foo/bar/baz/qux').value
+ 'Foo'
+ >>> t.shortest_prefix('does/not/exist')
+ (None Step)
+ >>> bool(t.shortest_prefix('does/not/exist'))
+ False
+
+ Args:
+ key: Key to look for.
+
+ Returns:
+ :class:`pygtrie.Trie._Step` object (which can be used to extract or
+ set node's value as well as get node's key), or
+ a :class:`pygtrie.Trie._NoneStep` object (which is falsy value
+ simulating a _Step with ``None`` key and value) if no prefix is
+ found.
+
+ The object can be treated as ``(key, value)`` pair denoting key with
+ associated value of the prefix. This is deprecated, prefer using
+ ``key`` and ``value`` properties of the object.
+ """
+ return next(self.prefixes(key), self._NONE_STEP)
+
+ def longest_prefix(self, key):
+ """Finds the longest prefix of a key with a value.
+
+ This is roughly equivalent to taking the last object yielded by
+ :func:`Trie.prefixes` with additional handling for situations when no
+ prefixes are found.
+
+ Example:
+
+ >>> import pygtrie
+ >>> t = pygtrie.StringTrie()
+ >>> t['foo'] = 'Foo'
+ >>> t['foo/bar/baz'] = 'Baz'
+ >>> t.longest_prefix('foo/bar/baz/qux')
+ ('foo/bar/baz': 'Baz')
+ >>> t.longest_prefix('foo/bar/baz/qux').key
+ 'foo/bar/baz'
+ >>> t.longest_prefix('foo/bar/baz/qux').value
+ 'Baz'
+ >>> t.longest_prefix('does/not/exist')
+ (None Step)
+ >>> bool(t.longest_prefix('does/not/exist'))
+ False
+
+ Args:
+ key: Key to look for.
+
+ Returns:
+ :class:`pygtrie.Trie._Step` object (which can be used to extract or
+ set node's value as well as get node's key), or
+ a :class:`pygtrie.Trie._NoneStep` object (which is falsy value
+ simulating a _Step with ``None`` key and value) if no prefix is
+ found.
+
+ The object can be treated as ``(key, value)`` pair denoting key with
+ associated value of the prefix. This is deprecated, prefer using
+ ``key`` and ``value`` properties of the object.
+ """
+ ret = self._NONE_STEP
+ for ret in self.prefixes(key):
+ pass
+ return ret
+
+ def strictly_equals(self, other):
+ """Checks whether tries are equal with the same structure.
+
+ This is stricter comparison than the one performed by equality operator.
+ It not only requires for keys and values to be equal but also for the
+ two tries to be of the same type and have the same structure.
+
+ For example, for two :class:`pygtrie.StringTrie` objects to be equal,
+ they need to have the same structure as well as the same separator as
+ seen below:
+
+ >>> import pygtrie
+ >>> t0 = StringTrie({'foo/bar': 42}, separator='/')
+ >>> t1 = StringTrie({'foo.bar': 42}, separator='.')
+ >>> t0.strictly_equals(t1)
+ False
+
+ >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
+ >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
+ >>> t0 == t1
+ True
+ >>> t0.strictly_equals(t1)
+ False
+
+ Args:
+ other: Other trie to compare to.
+
+ Returns:
+ Whether the two tries are the same type and have the same structure.
+ """
+ if self is other:
+ return True
+ if type(self) != type(other):
+ return False
+ result = self._eq_impl(other)
+ if result is NotImplemented:
+ return False
+ else:
+ return result
+
+ def __eq__(self, other):
+ """Compares this trie’s mapping with another mapping.
+
+ Note that this method doesn’t take trie’s structure into consideration.
+ What matters is whether keys and values in both mappings are the same.
+ This may lead to unexpected results, for example:
+
+ >>> import pygtrie
+ >>> t0 = StringTrie({'foo/bar': 42}, separator='/')
+ >>> t1 = StringTrie({'foo.bar': 42}, separator='.')
+ >>> t0 == t1
+ False
+
+ >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
+ >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
+ >>> t0 == t1
+ True
+
+ >>> t0 = Trie({'foo': 42})
+ >>> t1 = CharTrie({'foo': 42})
+ >>> t0 == t1
+ False
+
+ This behaviour is required to maintain consistency with Mapping
+ interface and its __eq__ method. For example, this implementation
+ maintains transitivity of the comparison:
+
+ >>> t0 = StringTrie({'foo/bar.baz': 42}, separator='/')
+ >>> d = {'foo/bar.baz': 42}
+ >>> t1 = StringTrie({'foo/bar.baz': 42}, separator='.')
+ >>> t0 == d
+ True
+ >>> d == t1
+ True
+ >>> t0 == t1
+ True
+
+ >>> t0 = Trie({'foo': 42})
+ >>> d = {'foo': 42}
+ >>> t1 = CharTrie({'foo': 42})
+ >>> t0 == d
+ False
+ >>> d == t1
+ True
+ >>> t0 == t1
+ False
+
+ Args:
+ other: Other object to compare to.
+
+ Returns:
+ ``NotImplemented`` if this method does not know how to perform the
+ comparison or a ``bool`` denoting whether the two objects are equal
+ or not.
+ """
+ if self is other:
+ return True
+ if type(other) == type(self):
+ result = self._eq_impl(other)
+ if result is not NotImplemented:
+ return result
+ return super(Trie, self).__eq__(other)
+
+ def _eq_impl(self, other):
+ return self._root.equals(other._root) # pylint: disable=protected-access
+
+ def __ne__(self, other):
+ return not self == other
+
+ def _str_items(self, fmt='%s: %s'):
+ return ', '.join(fmt % item for item in self.iteritems())
+
+ def __str__(self):
+ return '%s(%s)' % (type(self).__name__, self._str_items())
+
+ def __repr__(self):
+ return '%s([%s])' % (type(self).__name__, self._str_items('(%r, %r)'))
+
+ def __path_from_key(self, key):
+ """Converts a user visible key object to internal path representation.
+
+ Args:
+ key: User supplied key or ``_EMPTY``.
+
+ Returns:
+ An empty tuple if ``key`` was ``_EMPTY``, otherwise whatever
+ :func:`Trie._path_from_key` returns.
+
+ Raises:
+ TypeError: If ``key`` is of invalid type.
+ """
+ return () if key is _EMPTY else self._path_from_key(key)
+
+ def _path_from_key(self, key):
+ """Converts a user visible key object to internal path representation.
+
+ The default implementation simply returns key.
+
+ Args:
+ key: User supplied key.
+
+ Returns:
+ A path, which is an iterable of steps. Each step must be hashable.
+
+ Raises:
+ TypeError: If key is of invalid type.
+ """
+ return key
+
+ def _key_from_path(self, path):
+ """Converts an internal path into a user visible key object.
+
+ The default implementation creates a tuple from the path.
+
+ Args:
+ path: Internal path representation.
+ Returns:
+ A user visible key object.
+ """
+ return tuple(path)
+
+ def traverse(self, node_factory, prefix=_EMPTY):
+ """Traverses the tree using node_factory object.
+
+ node_factory is a callable which accepts (path_conv, path, children,
+ value=...) arguments, where path_conv is a lambda converting path
+ representation to key, path is the path to this node, children is an
+ iterable of children nodes constructed by node_factory, optional value
+ is the value associated with the path.
+
+ node_factory's children argument is an iterator which has a few
+ consequences:
+
+ * To traverse into node's children, the object must be iterated over.
+ This can by accomplished by a simple ``children = list(children)``
+ statement.
+ * Ignoring the argument allows node_factory to stop the traversal from
+ going into the children of the node. In other words, whole subtries
+ can be removed from traversal if node_factory chooses so.
+ * If children is stored as is (i.e. as a iterator) when it is iterated
+ over later on it may see an inconsistent state of the trie if it has
+ changed between invocation of this method and the iteration.
+
+ However, to allow constant-time determination whether the node has
+ children or not, the iterator implements bool conversion such that
+ ``has_children = bool(children)`` will tell whether node has children
+ without iterating over them. (Note that ``bool(children)`` will
+ continue returning ``True`` even if the iterator has been iterated
+ over).
+
+ :func:`Trie.traverse` has two advantages over :func:`Trie.iteritems` and
+ similar methods:
+
+ 1. it allows subtries to be skipped completely when going through the
+ list of nodes based on the property of the parent node; and
+
+ 2. it represents structure of the trie directly making it easy to
+ convert structure into a different representation.
+
+ For example, the below snippet prints all files in current directory
+ counting how many HTML files were found but ignores hidden files and
+ directories (i.e. those whose names start with a dot)::
+
+ import os
+ import pygtrie
+
+ t = pygtrie.StringTrie(separator=os.sep)
+
+ # Construct a trie with all files in current directory and all
+ # of its sub-directories. Files get set a True value.
+ # Directories are represented implicitly by being prefixes of
+ # files.
+ for root, _, files in os.walk('.'):
+ for name in files: t[os.path.join(root, name)] = True
+
+ def traverse_callback(path_conv, path, children, is_file=False):
+ if path and path[-1] != '.' and path[-1][0] == '.':
+ # Ignore hidden directory (but accept root node and '.')
+ return 0
+ elif is_file:
+ print path_conv(path)
+ return int(path[-1].endswith('.html'))
+ else:
+ # Otherwise, it's a directory. Traverse into children.
+ return sum(children)
+
+ print t.traverse(traverse_callback)
+
+ As documented, ignoring the children argument causes subtrie to be
+ omitted and not walked into.
+
+ In the next example, the trie is converted to a tree representation
+ where child nodes include a pointer to their parent. As before, hidden
+ files and directories are ignored::
+
+ import os
+ import pygtrie
+
+ t = pygtrie.StringTrie(separator=os.sep)
+ for root, _, files in os.walk('.'):
+ for name in files: t[os.path.join(root, name)] = True
+
+ class File(object):
+ def __init__(self, name):
+ self.name = name
+ self.parent = None
+
+ class Directory(File):
+ def __init__(self, name, children):
+ super(Directory, self).__init__(name)
+ self._children = children
+ for child in children:
+ child.parent = self
+
+ def traverse_callback(path_conv, path, children, is_file=False):
+ if not path or path[-1] == '.' or path[-1][0] != '.':
+ if is_file:
+ return File(path[-1])
+ children = filter(None, children)
+ return Directory(path[-1] if path else '', children)
+
+ root = t.traverse(traverse_callback)
+
+ Note: Unlike iterators, when used on a deep trie, traverse method is
+ prone to rising a RuntimeError exception when Python's maximum recursion
+ depth is reached. This can be addressed by not iterating over children
+ inside of the node_factory. For example, the below code converts a trie
+ into an undirected graph using adjacency list representation::
+
+ def undirected_graph_from_trie(t):
+ '''Converts trie into a graph and returns its nodes.'''
+
+ Node = collections.namedtuple('Node', 'path neighbours')
+
+ class Builder(object):
+ def __init__(self, path_conv, path, children, _=None):
+ self.node = Node(path_conv(path), [])
+ self.children = children
+ self.parent = None
+
+ def build(self, queue):
+ for builder in self.children:
+ builder.parent = self.node
+ queue.append(builder)
+ if self.parent:
+ self.parent.neighbours.append(self.node)
+ self.node.neighbours.append(self.parent)
+ return self.node
+
+ nodes = [t.traverse(Builder)]
+ i = 0
+ while i < len(nodes):
+ nodes[i] = nodes[i].build(nodes)
+ i += 1
+ return nodes
+
+ Args:
+ node_factory: Makes opaque objects from the keys and values of the
+ trie.
+ prefix: Prefix for node to start traversal, by default starts at
+ root.
+
+ Returns:
+ Node object constructed by node_factory corresponding to the root
+ node.
+ """
+ node, _ = self._get_node(prefix)
+ return node.traverse(node_factory, self._key_from_path,
+ list(self.__path_from_key(prefix)),
+ self._iteritems)
+
+ traverse.uses_bool_convertible_children = True
+
+class CharTrie(Trie):
+ """A variant of a :class:`pygtrie.Trie` which accepts strings as keys.
+
+ The only difference between :class:`pygtrie.CharTrie` and
+ :class:`pygtrie.Trie` is that when :class:`pygtrie.CharTrie` returns keys
+ back to the client (for instance when :func:`Trie.keys` method is called),
+ those keys are returned as strings.
+
+ Common example where this class can be used is a dictionary of words in
+ a natural language. For example::
+
+ >>> import pygtrie
+ >>> t = pygtrie.CharTrie()
+ >>> t['wombat'] = True
+ >>> t['woman'] = True
+ >>> t['man'] = True
+ >>> t['manhole'] = True
+ >>> t.has_subtrie('wo')
+ True
+ >>> t.has_key('man')
+ True
+ >>> t.has_subtrie('man')
+ True
+ >>> t.has_subtrie('manhole')
+ False
+ """
+
+ def _key_from_path(self, path):
+ return ''.join(path)
+
+
+class StringTrie(Trie):
+ """:class:`pygtrie.Trie` variant accepting strings with a separator as keys.
+
+ The trie accepts strings as keys which are split into components using
+ a separator specified during initialisation (forward slash, i.e. ``/``, by
+ default).
+
+ Common example where this class can be used is when keys are paths. For
+ example, it could map from a path to a request handler::
+
+ import pygtrie
+
+ def handle_root(): pass
+ def handle_admin(): pass
+ def handle_admin_images(): pass
+
+ handlers = pygtrie.StringTrie()
+ handlers[''] = handle_root
+ handlers['/admin'] = handle_admin
+ handlers['/admin/images'] = handle_admin_images
+
+ request_path = '/admin/images/foo'
+
+ handler = handlers.longest_prefix(request_path)
+ """
+
+ def __init__(self, *args, **kwargs): # pylint: disable=differing-param-doc
+ """Initialises the trie.
+
+ Except for a ``separator`` named argument, all other arguments are
+ interpreted the same way :func:`Trie.update` interprets them.
+
+ Args:
+ *args: Passed to super class initialiser.
+ **kwargs: Passed to super class initialiser.
+ separator: A separator to use when splitting keys into paths used by
+ the trie. "/" is used if this argument is not specified. This
+ named argument is not specified on the function's prototype
+ because of Python's limitations.
+
+ Raises:
+ TypeError: If ``separator`` is not a string.
+ ValueError: If ``separator`` is empty.
+ """
+ separator = kwargs.pop('separator', '/')
+ if not isinstance(separator, getattr(__builtins__, 'basestring', str)):
+ raise TypeError('separator must be a string')
+ if not separator:
+ raise ValueError('separator can not be empty')
+ self._separator = separator
+ super(StringTrie, self).__init__(*args, **kwargs)
+
+ @classmethod
+ def fromkeys(cls, keys, value=None, separator='/'): # pylint: disable=arguments-differ
+ trie = cls(separator=separator)
+ for key in keys:
+ trie[key] = value
+ return trie
+
+ @classmethod
+ def _merge_impl(cls, dst, src, overwrite):
+ if not isinstance(dst, StringTrie):
+ raise TypeError('%s cannot be merged into a %s' % (
+ type(src).__name__, type(dst).__name__))
+ super(StringTrie, cls)._merge_impl(dst, src, overwrite=overwrite)
+
+ def __str__(self):
+ if not self:
+ return '%s(separator=%s)' % (type(self).__name__, self._separator)
+ return '%s(%s, separator=%s)' % (
+ type(self).__name__, self._str_items(), self._separator)
+
+ def __repr__(self):
+ return '%s([%s], separator=%r)' % (
+ type(self).__name__, self._str_items('(%r, %r)'), self._separator)
+
+ def _eq_impl(self, other):
+ # If separators differ, fall back to slow generic comparison. This is
+ # because we want StringTrie(foo/bar.baz: 42, separator=/) compare equal
+ # to StringTrie(foo/bar.baz: 42, separator=.) even though they have
+ # different trie structure.
+ if self._separator != other._separator: # pylint: disable=protected-access
+ return NotImplemented
+ return super(StringTrie, self)._eq_impl(other)
+
+ def _path_from_key(self, key):
+ return key.split(self._separator)
+
+ def _key_from_path(self, path):
+ return self._separator.join(path)
+
+
+class PrefixSet(_abc.MutableSet):
+ """A set of prefixes.
+
+ :class:`pygtrie.PrefixSet` works similar to a normal set except it is said
+ to contain a key if the key or it's prefix is stored in the set. For
+ instance, if "foo" is added to the set, the set contains "foo" as well as
+ "foobar".
+
+ The set supports addition of elements but does *not* support removal of
+ elements. This is because there's no obvious consistent and intuitive
+ behaviour for element deletion.
+ """
+
+ def __init__(self, iterable=(), factory=Trie, **kwargs):
+ """Initialises the prefix set.
+
+ Args:
+ iterable: A sequence of keys to add to the set.
+ factory: A function used to create a trie used by the
+ :class:`pygtrie.PrefixSet`.
+ kwargs: Additional keyword arguments passed to the factory function.
+ """
+ super(PrefixSet, self).__init__()
+ self._trie = factory(**kwargs)
+ for key in iterable:
+ self.add(key)
+
+ def copy(self):
+ """Returns a shallow copy of the object."""
+ return self.__copy__()
+
+ def __copy__(self):
+ # pylint: disable=protected-access
+ cpy = self.__class__()
+ cpy.__dict__ = self.__dict__.copy()
+ cpy._trie = self._trie.__copy__()
+ return cpy
+
+ def __deepcopy__(self, memo):
+ # pylint: disable=protected-access
+ cpy = self.__class__()
+ cpy.__dict__ = self.__dict__.copy()
+ cpy._trie = self._trie.__deepcopy__(memo)
+ return cpy
+
+ def clear(self):
+ """Removes all keys from the set."""
+ self._trie.clear()
+
+ def __contains__(self, key):
+ """Checks whether set contains key or its prefix."""
+ return bool(self._trie.shortest_prefix(key)[1])
+
+ def __iter__(self):
+ """Return iterator over all prefixes in the set.
+
+ See :func:`PrefixSet.iter` method for more info.
+ """
+ return self._trie.iterkeys()
+
+ def iter(self, prefix=_EMPTY):
+ """Iterates over all keys in the set optionally starting with a prefix.
+
+ Since a key does not have to be explicitly added to the set to be an
+ element of the set, this method does not iterate over all possible keys
+ that the set contains, but only over the shortest set of prefixes of all
+ the keys the set contains.
+
+ For example, if "foo" has been added to the set, the set contains also
+ "foobar", but this method will *not* iterate over "foobar".
+
+ If ``prefix`` argument is given, method will iterate over keys with
+ given prefix only. The keys yielded from the function if prefix is
+ given does not have to be a subset (in mathematical sense) of the keys
+ yielded when there is not prefix. This happens, if the set contains
+ a prefix of the given prefix.
+
+ For example, if only "foo" has been added to the set, iter method called
+ with no arguments will yield "foo" only. However, when called with
+ "foobar" argument, it will yield "foobar" only.
+ """
+ if prefix is _EMPTY:
+ return iter(self)
+ if self._trie.has_node(prefix):
+ return self._trie.iterkeys(prefix=prefix)
+ if prefix in self:
+ # Make sure the type of returned keys is consistent.
+ # pylint: disable=protected-access
+ return (
+ self._trie._key_from_path(self._trie._path_from_key(prefix)),)
+ return ()
+
+ def __len__(self):
+ """Returns number of keys stored in the set.
+
+ Since a key does not have to be explicitly added to the set to be an
+ element of the set, this method does not count over all possible keys
+ that the set contains (since that would be infinity), but only over the
+ shortest set of prefixes of all the keys the set contains.
+
+ For example, if "foo" has been added to the set, the set contains also
+ "foobar", but this method will *not* count "foobar".
+
+ """
+ return len(self._trie)
+
+ def add(self, value):
+ """Adds given value to the set.
+
+ If the set already contains prefix of the value being added, this
+ operation has no effect. If the value being added is a prefix of some
+ existing values in the set, those values are deleted and replaced by
+ a single entry for the value being added.
+
+ For example, if the set contains value "foo" adding a value "foobar"
+ does not change anything. On the other hand, if the set contains values
+ "foobar" and "foobaz", adding a value "foo" will replace those two
+ values with a single value "foo".
+
+ This makes a difference when iterating over the values or counting
+ number of values. Counter intuitively, adding of a value can *decrease*
+ size of the set.
+
+ Args:
+ value: Value to add.
+ """
+ # We're friends with Trie; pylint: disable=protected-access
+ self._trie._set_node_if_no_prefix(value)
+
+ def discard(self, value):
+ """Raises NotImplementedError."""
+ raise NotImplementedError(
+ 'Removing values from PrefixSet is not implemented.')
+
+ def remove(self, value):
+ """Raises NotImplementedError."""
+ raise NotImplementedError(
+ 'Removing values from PrefixSet is not implemented.')
+
+ def pop(self):
+ """Raises NotImplementedError."""
+ raise NotImplementedError(
+ 'Removing values from PrefixSet is not implemented.')
diff --git a/contrib/python/pygtrie/py3/ya.make b/contrib/python/pygtrie/py3/ya.make
new file mode 100644
index 0000000000..ecb0cffd62
--- /dev/null
+++ b/contrib/python/pygtrie/py3/ya.make
@@ -0,0 +1,26 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(2.5.0)
+
+LICENSE(Apache-2.0)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ pygtrie.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/pygtrie/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/pygtrie/ya.make b/contrib/python/pygtrie/ya.make
new file mode 100644
index 0000000000..03fb697fac
--- /dev/null
+++ b/contrib/python/pygtrie/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/pygtrie/py2)
+ELSE()
+ PEERDIR(contrib/python/pygtrie/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/pylev/py2/.dist-info/METADATA b/contrib/python/pylev/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..0fe37efb0b
--- /dev/null
+++ b/contrib/python/pylev/py2/.dist-info/METADATA
@@ -0,0 +1,106 @@
+Metadata-Version: 2.1
+Name: pylev
+Version: 1.4.0
+Summary: A pure Python Levenshtein implementation that's not freaking GPL'd.
+Home-page: http://github.com/toastdriven/pylev
+Author: Daniel Lindsley
+Author-email: daniel@toastdriven.com
+License: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+License-File: LICENSE
+
+pylev
+=====
+
+A pure Python Levenshtein implementation that's not freaking GPL'd.
+
+Based off the Wikipedia code samples at
+http://en.wikipedia.org/wiki/Levenshtein_distance.
+
+
+Requirements
+------------
+
+* Python 2.7.X, Python 3.3+ or PyPy 1.6.0+
+
+
+Usage
+-----
+
+Usage is fairly straightforward:
+
+.. code-block:: python
+
+ import pylev
+ distance = pylev.levenshtein('kitten', 'sitting')
+ assert distance == 3
+
+
+License
+-------
+
+New BSD.
+
+
+Tests
+-----
+
+Setup::
+
+ $ git clone https://github.com/toastdriven/pylev.git
+ $ cd pylev
+
+Running::
+
+ $ python -m unittest tests
+
+.. image:: https://travis-ci.com/toastdriven/pylev.svg?branch=main
+ :target: http://travis-ci.com/toastdriven/pylev
+
+
+Version History
+---------------
+
+* v1.4.0
+
+ * Updated for current versions of Python
+ * Integrated a better Travis matrix. Thanks to @grainert!
+ * Fixed mistaken docs about the `assert`. Thanks to @adamchainz!
+ * Reorganized the package.
+ * Blacked all the source code.
+
+* v1.3.0
+
+ * Implemented a considerably faster variants (orders of magnitude).
+ * Tested & working on Python 2.7.4, Python 3.3.1 & PyPy 1.9.0.
+
+* v1.2.0
+
+ * Fixed all incorrect spellings of "Levenshtein" (there's no "c" in it).
+ * Old methods are aliased for backward-compatibility.
+
+* v1.1.0
+
+ * Implemented a much faster variant (several orders of magnitude).
+ * The older variant was renamed to ``classic_levenschtein``.
+ * Tested & working on Python 3.3 & PyPy 1.6.0 as well.
+
+* v1.0.2
+
+ * Python packaging is **REALLY** hard. Including the README *this time*.
+
+* v1.0.1
+
+ * Python packaging is hard. Including the README this time.
+
+* v1.0.0
+
+ * Initial release, just the naive implementation of Levenshtein.
+
+
diff --git a/contrib/python/pylev/py2/.dist-info/top_level.txt b/contrib/python/pylev/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..53ea0c31a0
--- /dev/null
+++ b/contrib/python/pylev/py2/.dist-info/top_level.txt
@@ -0,0 +1 @@
+pylev
diff --git a/contrib/python/pylev/py2/LICENSE b/contrib/python/pylev/py2/LICENSE
new file mode 100644
index 0000000000..a049da3295
--- /dev/null
+++ b/contrib/python/pylev/py2/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2012, Daniel Lindsley
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of the pylev nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 pylev 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/contrib/python/pylev/py2/README.rst b/contrib/python/pylev/py2/README.rst
new file mode 100644
index 0000000000..5af9972a75
--- /dev/null
+++ b/contrib/python/pylev/py2/README.rst
@@ -0,0 +1,87 @@
+pylev
+=====
+
+A pure Python Levenshtein implementation that's not freaking GPL'd.
+
+Based off the Wikipedia code samples at
+http://en.wikipedia.org/wiki/Levenshtein_distance.
+
+
+Requirements
+------------
+
+* Python 2.7.X, Python 3.3+ or PyPy 1.6.0+
+
+
+Usage
+-----
+
+Usage is fairly straightforward:
+
+.. code-block:: python
+
+ import pylev
+ distance = pylev.levenshtein('kitten', 'sitting')
+ assert distance == 3
+
+
+License
+-------
+
+New BSD.
+
+
+Tests
+-----
+
+Setup::
+
+ $ git clone https://github.com/toastdriven/pylev.git
+ $ cd pylev
+
+Running::
+
+ $ python -m unittest tests
+
+.. image:: https://travis-ci.com/toastdriven/pylev.svg?branch=main
+ :target: http://travis-ci.com/toastdriven/pylev
+
+
+Version History
+---------------
+
+* v1.4.0
+
+ * Updated for current versions of Python
+ * Integrated a better Travis matrix. Thanks to @grainert!
+ * Fixed mistaken docs about the `assert`. Thanks to @adamchainz!
+ * Reorganized the package.
+ * Blacked all the source code.
+
+* v1.3.0
+
+ * Implemented a considerably faster variants (orders of magnitude).
+ * Tested & working on Python 2.7.4, Python 3.3.1 & PyPy 1.9.0.
+
+* v1.2.0
+
+ * Fixed all incorrect spellings of "Levenshtein" (there's no "c" in it).
+ * Old methods are aliased for backward-compatibility.
+
+* v1.1.0
+
+ * Implemented a much faster variant (several orders of magnitude).
+ * The older variant was renamed to ``classic_levenschtein``.
+ * Tested & working on Python 3.3 & PyPy 1.6.0 as well.
+
+* v1.0.2
+
+ * Python packaging is **REALLY** hard. Including the README *this time*.
+
+* v1.0.1
+
+ * Python packaging is hard. Including the README this time.
+
+* v1.0.0
+
+ * Initial release, just the naive implementation of Levenshtein.
diff --git a/contrib/python/pylev/py2/pylev/__init__.py b/contrib/python/pylev/py2/pylev/__init__.py
new file mode 100644
index 0000000000..a4d62dc6ad
--- /dev/null
+++ b/contrib/python/pylev/py2/pylev/__init__.py
@@ -0,0 +1,44 @@
+"""
+pylev
+=====
+
+A pure Python Levenshtein implementation that's not freaking GPL'd.
+
+Based off the Wikipedia code samples at
+http://en.wikipedia.org/wiki/Levenshtein_distance.
+
+Usage
+-----
+
+Usage is fairly straightforward.::
+
+ import pylev
+ distance = pylev.levenshtein('kitten', 'sitting')
+ assert distance == 3
+
+"""
+from .classic import classic_levenshtein
+from .recursive import recursive_levenshtein
+from .wf import wf_levenshtein, wfi_levenshtein
+from .damerau import damerau_levenshtein
+
+__author__ = "Daniel Lindsley"
+__version__ = (1, 4, 0)
+__license__ = "New BSD"
+
+
+levenshtein = wfi_levenshtein
+
+# Backward-compatibilty because I misspelled.
+classic_levenschtein = classic_levenshtein
+levenschtein = levenshtein
+
+
+__all__ = [
+ "levenshtein",
+ "classic_levenshtein",
+ "recursive_levenshtein",
+ "wf_levenshtein",
+ "wfi_levenshtein",
+ "damerau_levenshtein",
+]
diff --git a/contrib/python/pylev/py2/pylev/classic.py b/contrib/python/pylev/py2/pylev/classic.py
new file mode 100644
index 0000000000..98954e6727
--- /dev/null
+++ b/contrib/python/pylev/py2/pylev/classic.py
@@ -0,0 +1,35 @@
+def classic_levenshtein(string_1, string_2):
+ """
+ Calculates the Levenshtein distance between two strings.
+
+ This version is easier to read, but significantly slower than the version
+ below (up to several orders of magnitude). Useful for learning, less so
+ otherwise.
+
+ Usage::
+
+ >>> classic_levenshtein('kitten', 'sitting')
+ 3
+ >>> classic_levenshtein('kitten', 'kitten')
+ 0
+ >>> classic_levenshtein('', '')
+ 0
+
+ """
+ len_1 = len(string_1)
+ len_2 = len(string_2)
+ cost = 0
+
+ if len_1 and len_2 and string_1[0] != string_2[0]:
+ cost = 1
+
+ if len_1 == 0:
+ return len_2
+ elif len_2 == 0:
+ return len_1
+ else:
+ return min(
+ classic_levenshtein(string_1[1:], string_2) + 1,
+ classic_levenshtein(string_1, string_2[1:]) + 1,
+ classic_levenshtein(string_1[1:], string_2[1:]) + cost,
+ )
diff --git a/contrib/python/pylev/py2/pylev/damerau.py b/contrib/python/pylev/py2/pylev/damerau.py
new file mode 100644
index 0000000000..290212b094
--- /dev/null
+++ b/contrib/python/pylev/py2/pylev/damerau.py
@@ -0,0 +1,80 @@
+import sys
+
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+ range = xrange
+
+
+def damerau_levenshtein(string_1, string_2):
+ """
+ Calculates the Damerau-Levenshtein distance between two strings.
+
+ In addition to insertions, deletions and substitutions,
+ Damerau-Levenshtein considers adjacent transpositions.
+
+ This version is based on an iterative version of the Wagner-Fischer algorithm.
+
+ Usage::
+
+ >>> damerau_levenshtein('kitten', 'sitting')
+ 3
+ >>> damerau_levenshtein('kitten', 'kittne')
+ 1
+ >>> damerau_levenshtein('', '')
+ 0
+
+ """
+ if string_1 == string_2:
+ return 0
+
+ len_1 = len(string_1)
+ len_2 = len(string_2)
+
+ if len_1 == 0:
+ return len_2
+ if len_2 == 0:
+ return len_1
+
+ if len_1 > len_2:
+ string_2, string_1 = string_1, string_2
+ len_2, len_1 = len_1, len_2
+
+ prev_cost = 0
+ d0 = [i for i in range(len_2 + 1)]
+ d1 = [j for j in range(len_2 + 1)]
+ dprev = d0[:]
+
+ s1 = string_1
+ s2 = string_2
+
+ for i in range(len_1):
+ d1[0] = i + 1
+ for j in range(len_2):
+ cost = d0[j]
+
+ if s1[i] != s2[j]:
+ # substitution
+ cost += 1
+
+ # insertion
+ x_cost = d1[j] + 1
+ if x_cost < cost:
+ cost = x_cost
+
+ # deletion
+ y_cost = d0[j + 1] + 1
+ if y_cost < cost:
+ cost = y_cost
+
+ # transposition
+ if i > 0 and j > 0 and s1[i] == s2[j - 1] and s1[i - 1] == s2[j]:
+ transp_cost = dprev[j - 1] + 1
+ if transp_cost < cost:
+ cost = transp_cost
+ d1[j + 1] = cost
+
+ dprev, d0, d1 = d0, d1, dprev
+
+ return d0[-1]
diff --git a/contrib/python/pylev/py2/pylev/recursive.py b/contrib/python/pylev/py2/pylev/recursive.py
new file mode 100644
index 0000000000..6ec34f29da
--- /dev/null
+++ b/contrib/python/pylev/py2/pylev/recursive.py
@@ -0,0 +1,56 @@
+def recursive_levenshtein(
+ string_1, string_2, len_1=None, len_2=None, offset_1=0, offset_2=0, memo=None
+):
+ """
+ Calculates the Levenshtein distance between two strings.
+
+ Usage::
+
+ >>> recursive_levenshtein('kitten', 'sitting')
+ 3
+ >>> recursive_levenshtein('kitten', 'kitten')
+ 0
+ >>> recursive_levenshtein('', '')
+ 0
+
+ """
+ if len_1 is None:
+ len_1 = len(string_1)
+
+ if len_2 is None:
+ len_2 = len(string_2)
+
+ if memo is None:
+ memo = {}
+
+ key = ",".join([str(offset_1), str(len_1), str(offset_2), str(len_2)])
+
+ if memo.get(key) is not None:
+ return memo[key]
+
+ if len_1 == 0:
+ return len_2
+ elif len_2 == 0:
+ return len_1
+
+ cost = 0
+
+ if string_1[offset_1] != string_2[offset_2]:
+ cost = 1
+
+ dist = min(
+ recursive_levenshtein(
+ string_1, string_2, len_1 - 1, len_2, offset_1 + 1, offset_2, memo
+ )
+ + 1,
+ recursive_levenshtein(
+ string_1, string_2, len_1, len_2 - 1, offset_1, offset_2 + 1, memo
+ )
+ + 1,
+ recursive_levenshtein(
+ string_1, string_2, len_1 - 1, len_2 - 1, offset_1 + 1, offset_2 + 1, memo
+ )
+ + cost,
+ )
+ memo[key] = dist
+ return dist
diff --git a/contrib/python/pylev/py2/pylev/wf.py b/contrib/python/pylev/py2/pylev/wf.py
new file mode 100644
index 0000000000..b6b89249e9
--- /dev/null
+++ b/contrib/python/pylev/py2/pylev/wf.py
@@ -0,0 +1,107 @@
+import sys
+
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+ range = xrange
+
+
+def wf_levenshtein(string_1, string_2):
+ """
+ Calculates the Levenshtein distance between two strings.
+
+ This version uses the Wagner-Fischer algorithm.
+
+ Usage::
+
+ >>> wf_levenshtein('kitten', 'sitting')
+ 3
+ >>> wf_levenshtein('kitten', 'kitten')
+ 0
+ >>> wf_levenshtein('', '')
+ 0
+
+ """
+ len_1 = len(string_1) + 1
+ len_2 = len(string_2) + 1
+
+ d = [0] * (len_1 * len_2)
+
+ for i in range(len_1):
+ d[i] = i
+ for j in range(len_2):
+ d[j * len_1] = j
+
+ for j in range(1, len_2):
+ for i in range(1, len_1):
+ if string_1[i - 1] == string_2[j - 1]:
+ d[i + j * len_1] = d[i - 1 + (j - 1) * len_1]
+ else:
+ d[i + j * len_1] = min(
+ d[i - 1 + j * len_1] + 1, # deletion
+ d[i + (j - 1) * len_1] + 1, # insertion
+ d[i - 1 + (j - 1) * len_1] + 1, # substitution
+ )
+
+ return d[-1]
+
+
+def wfi_levenshtein(string_1, string_2):
+ """
+ Calculates the Levenshtein distance between two strings.
+
+ This version uses an iterative version of the Wagner-Fischer algorithm.
+
+ Usage::
+
+ >>> wfi_levenshtein('kitten', 'sitting')
+ 3
+ >>> wfi_levenshtein('kitten', 'kitten')
+ 0
+ >>> wfi_levenshtein('', '')
+ 0
+
+ """
+ if string_1 == string_2:
+ return 0
+
+ len_1 = len(string_1)
+ len_2 = len(string_2)
+
+ if len_1 == 0:
+ return len_2
+ if len_2 == 0:
+ return len_1
+
+ if len_1 > len_2:
+ string_2, string_1 = string_1, string_2
+ len_2, len_1 = len_1, len_2
+
+ d0 = [i for i in range(len_2 + 1)]
+ d1 = [j for j in range(len_2 + 1)]
+
+ for i in range(len_1):
+ d1[0] = i + 1
+ for j in range(len_2):
+ cost = d0[j]
+
+ if string_1[i] != string_2[j]:
+ # substitution
+ cost += 1
+
+ # insertion
+ x_cost = d1[j] + 1
+ if x_cost < cost:
+ cost = x_cost
+
+ # deletion
+ y_cost = d0[j + 1] + 1
+ if y_cost < cost:
+ cost = y_cost
+
+ d1[j + 1] = cost
+
+ d0, d1 = d1, d0
+
+ return d0[-1]
diff --git a/contrib/python/pylev/py2/ya.make b/contrib/python/pylev/py2/ya.make
new file mode 100644
index 0000000000..40ec0bb21e
--- /dev/null
+++ b/contrib/python/pylev/py2/ya.make
@@ -0,0 +1,26 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(1.4.0)
+
+LICENSE(BSD-3-Clause)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ pylev/__init__.py
+ pylev/classic.py
+ pylev/damerau.py
+ pylev/recursive.py
+ pylev/wf.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/pylev/py2/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
diff --git a/contrib/python/pylev/py3/.dist-info/METADATA b/contrib/python/pylev/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..0fe37efb0b
--- /dev/null
+++ b/contrib/python/pylev/py3/.dist-info/METADATA
@@ -0,0 +1,106 @@
+Metadata-Version: 2.1
+Name: pylev
+Version: 1.4.0
+Summary: A pure Python Levenshtein implementation that's not freaking GPL'd.
+Home-page: http://github.com/toastdriven/pylev
+Author: Daniel Lindsley
+Author-email: daniel@toastdriven.com
+License: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+License-File: LICENSE
+
+pylev
+=====
+
+A pure Python Levenshtein implementation that's not freaking GPL'd.
+
+Based off the Wikipedia code samples at
+http://en.wikipedia.org/wiki/Levenshtein_distance.
+
+
+Requirements
+------------
+
+* Python 2.7.X, Python 3.3+ or PyPy 1.6.0+
+
+
+Usage
+-----
+
+Usage is fairly straightforward:
+
+.. code-block:: python
+
+ import pylev
+ distance = pylev.levenshtein('kitten', 'sitting')
+ assert distance == 3
+
+
+License
+-------
+
+New BSD.
+
+
+Tests
+-----
+
+Setup::
+
+ $ git clone https://github.com/toastdriven/pylev.git
+ $ cd pylev
+
+Running::
+
+ $ python -m unittest tests
+
+.. image:: https://travis-ci.com/toastdriven/pylev.svg?branch=main
+ :target: http://travis-ci.com/toastdriven/pylev
+
+
+Version History
+---------------
+
+* v1.4.0
+
+ * Updated for current versions of Python
+ * Integrated a better Travis matrix. Thanks to @grainert!
+ * Fixed mistaken docs about the `assert`. Thanks to @adamchainz!
+ * Reorganized the package.
+ * Blacked all the source code.
+
+* v1.3.0
+
+ * Implemented a considerably faster variants (orders of magnitude).
+ * Tested & working on Python 2.7.4, Python 3.3.1 & PyPy 1.9.0.
+
+* v1.2.0
+
+ * Fixed all incorrect spellings of "Levenshtein" (there's no "c" in it).
+ * Old methods are aliased for backward-compatibility.
+
+* v1.1.0
+
+ * Implemented a much faster variant (several orders of magnitude).
+ * The older variant was renamed to ``classic_levenschtein``.
+ * Tested & working on Python 3.3 & PyPy 1.6.0 as well.
+
+* v1.0.2
+
+ * Python packaging is **REALLY** hard. Including the README *this time*.
+
+* v1.0.1
+
+ * Python packaging is hard. Including the README this time.
+
+* v1.0.0
+
+ * Initial release, just the naive implementation of Levenshtein.
+
+
diff --git a/contrib/python/pylev/py3/.dist-info/top_level.txt b/contrib/python/pylev/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..53ea0c31a0
--- /dev/null
+++ b/contrib/python/pylev/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+pylev
diff --git a/contrib/python/pylev/py3/LICENSE b/contrib/python/pylev/py3/LICENSE
new file mode 100644
index 0000000000..a049da3295
--- /dev/null
+++ b/contrib/python/pylev/py3/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2012, Daniel Lindsley
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of the pylev nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 pylev 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/contrib/python/pylev/py3/README.rst b/contrib/python/pylev/py3/README.rst
new file mode 100644
index 0000000000..5af9972a75
--- /dev/null
+++ b/contrib/python/pylev/py3/README.rst
@@ -0,0 +1,87 @@
+pylev
+=====
+
+A pure Python Levenshtein implementation that's not freaking GPL'd.
+
+Based off the Wikipedia code samples at
+http://en.wikipedia.org/wiki/Levenshtein_distance.
+
+
+Requirements
+------------
+
+* Python 2.7.X, Python 3.3+ or PyPy 1.6.0+
+
+
+Usage
+-----
+
+Usage is fairly straightforward:
+
+.. code-block:: python
+
+ import pylev
+ distance = pylev.levenshtein('kitten', 'sitting')
+ assert distance == 3
+
+
+License
+-------
+
+New BSD.
+
+
+Tests
+-----
+
+Setup::
+
+ $ git clone https://github.com/toastdriven/pylev.git
+ $ cd pylev
+
+Running::
+
+ $ python -m unittest tests
+
+.. image:: https://travis-ci.com/toastdriven/pylev.svg?branch=main
+ :target: http://travis-ci.com/toastdriven/pylev
+
+
+Version History
+---------------
+
+* v1.4.0
+
+ * Updated for current versions of Python
+ * Integrated a better Travis matrix. Thanks to @grainert!
+ * Fixed mistaken docs about the `assert`. Thanks to @adamchainz!
+ * Reorganized the package.
+ * Blacked all the source code.
+
+* v1.3.0
+
+ * Implemented a considerably faster variants (orders of magnitude).
+ * Tested & working on Python 2.7.4, Python 3.3.1 & PyPy 1.9.0.
+
+* v1.2.0
+
+ * Fixed all incorrect spellings of "Levenshtein" (there's no "c" in it).
+ * Old methods are aliased for backward-compatibility.
+
+* v1.1.0
+
+ * Implemented a much faster variant (several orders of magnitude).
+ * The older variant was renamed to ``classic_levenschtein``.
+ * Tested & working on Python 3.3 & PyPy 1.6.0 as well.
+
+* v1.0.2
+
+ * Python packaging is **REALLY** hard. Including the README *this time*.
+
+* v1.0.1
+
+ * Python packaging is hard. Including the README this time.
+
+* v1.0.0
+
+ * Initial release, just the naive implementation of Levenshtein.
diff --git a/contrib/python/pylev/py3/pylev/__init__.py b/contrib/python/pylev/py3/pylev/__init__.py
new file mode 100644
index 0000000000..a4d62dc6ad
--- /dev/null
+++ b/contrib/python/pylev/py3/pylev/__init__.py
@@ -0,0 +1,44 @@
+"""
+pylev
+=====
+
+A pure Python Levenshtein implementation that's not freaking GPL'd.
+
+Based off the Wikipedia code samples at
+http://en.wikipedia.org/wiki/Levenshtein_distance.
+
+Usage
+-----
+
+Usage is fairly straightforward.::
+
+ import pylev
+ distance = pylev.levenshtein('kitten', 'sitting')
+ assert distance == 3
+
+"""
+from .classic import classic_levenshtein
+from .recursive import recursive_levenshtein
+from .wf import wf_levenshtein, wfi_levenshtein
+from .damerau import damerau_levenshtein
+
+__author__ = "Daniel Lindsley"
+__version__ = (1, 4, 0)
+__license__ = "New BSD"
+
+
+levenshtein = wfi_levenshtein
+
+# Backward-compatibilty because I misspelled.
+classic_levenschtein = classic_levenshtein
+levenschtein = levenshtein
+
+
+__all__ = [
+ "levenshtein",
+ "classic_levenshtein",
+ "recursive_levenshtein",
+ "wf_levenshtein",
+ "wfi_levenshtein",
+ "damerau_levenshtein",
+]
diff --git a/contrib/python/pylev/py3/pylev/classic.py b/contrib/python/pylev/py3/pylev/classic.py
new file mode 100644
index 0000000000..98954e6727
--- /dev/null
+++ b/contrib/python/pylev/py3/pylev/classic.py
@@ -0,0 +1,35 @@
+def classic_levenshtein(string_1, string_2):
+ """
+ Calculates the Levenshtein distance between two strings.
+
+ This version is easier to read, but significantly slower than the version
+ below (up to several orders of magnitude). Useful for learning, less so
+ otherwise.
+
+ Usage::
+
+ >>> classic_levenshtein('kitten', 'sitting')
+ 3
+ >>> classic_levenshtein('kitten', 'kitten')
+ 0
+ >>> classic_levenshtein('', '')
+ 0
+
+ """
+ len_1 = len(string_1)
+ len_2 = len(string_2)
+ cost = 0
+
+ if len_1 and len_2 and string_1[0] != string_2[0]:
+ cost = 1
+
+ if len_1 == 0:
+ return len_2
+ elif len_2 == 0:
+ return len_1
+ else:
+ return min(
+ classic_levenshtein(string_1[1:], string_2) + 1,
+ classic_levenshtein(string_1, string_2[1:]) + 1,
+ classic_levenshtein(string_1[1:], string_2[1:]) + cost,
+ )
diff --git a/contrib/python/pylev/py3/pylev/damerau.py b/contrib/python/pylev/py3/pylev/damerau.py
new file mode 100644
index 0000000000..290212b094
--- /dev/null
+++ b/contrib/python/pylev/py3/pylev/damerau.py
@@ -0,0 +1,80 @@
+import sys
+
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+ range = xrange
+
+
+def damerau_levenshtein(string_1, string_2):
+ """
+ Calculates the Damerau-Levenshtein distance between two strings.
+
+ In addition to insertions, deletions and substitutions,
+ Damerau-Levenshtein considers adjacent transpositions.
+
+ This version is based on an iterative version of the Wagner-Fischer algorithm.
+
+ Usage::
+
+ >>> damerau_levenshtein('kitten', 'sitting')
+ 3
+ >>> damerau_levenshtein('kitten', 'kittne')
+ 1
+ >>> damerau_levenshtein('', '')
+ 0
+
+ """
+ if string_1 == string_2:
+ return 0
+
+ len_1 = len(string_1)
+ len_2 = len(string_2)
+
+ if len_1 == 0:
+ return len_2
+ if len_2 == 0:
+ return len_1
+
+ if len_1 > len_2:
+ string_2, string_1 = string_1, string_2
+ len_2, len_1 = len_1, len_2
+
+ prev_cost = 0
+ d0 = [i for i in range(len_2 + 1)]
+ d1 = [j for j in range(len_2 + 1)]
+ dprev = d0[:]
+
+ s1 = string_1
+ s2 = string_2
+
+ for i in range(len_1):
+ d1[0] = i + 1
+ for j in range(len_2):
+ cost = d0[j]
+
+ if s1[i] != s2[j]:
+ # substitution
+ cost += 1
+
+ # insertion
+ x_cost = d1[j] + 1
+ if x_cost < cost:
+ cost = x_cost
+
+ # deletion
+ y_cost = d0[j + 1] + 1
+ if y_cost < cost:
+ cost = y_cost
+
+ # transposition
+ if i > 0 and j > 0 and s1[i] == s2[j - 1] and s1[i - 1] == s2[j]:
+ transp_cost = dprev[j - 1] + 1
+ if transp_cost < cost:
+ cost = transp_cost
+ d1[j + 1] = cost
+
+ dprev, d0, d1 = d0, d1, dprev
+
+ return d0[-1]
diff --git a/contrib/python/pylev/py3/pylev/recursive.py b/contrib/python/pylev/py3/pylev/recursive.py
new file mode 100644
index 0000000000..6ec34f29da
--- /dev/null
+++ b/contrib/python/pylev/py3/pylev/recursive.py
@@ -0,0 +1,56 @@
+def recursive_levenshtein(
+ string_1, string_2, len_1=None, len_2=None, offset_1=0, offset_2=0, memo=None
+):
+ """
+ Calculates the Levenshtein distance between two strings.
+
+ Usage::
+
+ >>> recursive_levenshtein('kitten', 'sitting')
+ 3
+ >>> recursive_levenshtein('kitten', 'kitten')
+ 0
+ >>> recursive_levenshtein('', '')
+ 0
+
+ """
+ if len_1 is None:
+ len_1 = len(string_1)
+
+ if len_2 is None:
+ len_2 = len(string_2)
+
+ if memo is None:
+ memo = {}
+
+ key = ",".join([str(offset_1), str(len_1), str(offset_2), str(len_2)])
+
+ if memo.get(key) is not None:
+ return memo[key]
+
+ if len_1 == 0:
+ return len_2
+ elif len_2 == 0:
+ return len_1
+
+ cost = 0
+
+ if string_1[offset_1] != string_2[offset_2]:
+ cost = 1
+
+ dist = min(
+ recursive_levenshtein(
+ string_1, string_2, len_1 - 1, len_2, offset_1 + 1, offset_2, memo
+ )
+ + 1,
+ recursive_levenshtein(
+ string_1, string_2, len_1, len_2 - 1, offset_1, offset_2 + 1, memo
+ )
+ + 1,
+ recursive_levenshtein(
+ string_1, string_2, len_1 - 1, len_2 - 1, offset_1 + 1, offset_2 + 1, memo
+ )
+ + cost,
+ )
+ memo[key] = dist
+ return dist
diff --git a/contrib/python/pylev/py3/pylev/wf.py b/contrib/python/pylev/py3/pylev/wf.py
new file mode 100644
index 0000000000..b6b89249e9
--- /dev/null
+++ b/contrib/python/pylev/py3/pylev/wf.py
@@ -0,0 +1,107 @@
+import sys
+
+
+PY2 = sys.version_info[0] == 2
+
+if PY2:
+ range = xrange
+
+
+def wf_levenshtein(string_1, string_2):
+ """
+ Calculates the Levenshtein distance between two strings.
+
+ This version uses the Wagner-Fischer algorithm.
+
+ Usage::
+
+ >>> wf_levenshtein('kitten', 'sitting')
+ 3
+ >>> wf_levenshtein('kitten', 'kitten')
+ 0
+ >>> wf_levenshtein('', '')
+ 0
+
+ """
+ len_1 = len(string_1) + 1
+ len_2 = len(string_2) + 1
+
+ d = [0] * (len_1 * len_2)
+
+ for i in range(len_1):
+ d[i] = i
+ for j in range(len_2):
+ d[j * len_1] = j
+
+ for j in range(1, len_2):
+ for i in range(1, len_1):
+ if string_1[i - 1] == string_2[j - 1]:
+ d[i + j * len_1] = d[i - 1 + (j - 1) * len_1]
+ else:
+ d[i + j * len_1] = min(
+ d[i - 1 + j * len_1] + 1, # deletion
+ d[i + (j - 1) * len_1] + 1, # insertion
+ d[i - 1 + (j - 1) * len_1] + 1, # substitution
+ )
+
+ return d[-1]
+
+
+def wfi_levenshtein(string_1, string_2):
+ """
+ Calculates the Levenshtein distance between two strings.
+
+ This version uses an iterative version of the Wagner-Fischer algorithm.
+
+ Usage::
+
+ >>> wfi_levenshtein('kitten', 'sitting')
+ 3
+ >>> wfi_levenshtein('kitten', 'kitten')
+ 0
+ >>> wfi_levenshtein('', '')
+ 0
+
+ """
+ if string_1 == string_2:
+ return 0
+
+ len_1 = len(string_1)
+ len_2 = len(string_2)
+
+ if len_1 == 0:
+ return len_2
+ if len_2 == 0:
+ return len_1
+
+ if len_1 > len_2:
+ string_2, string_1 = string_1, string_2
+ len_2, len_1 = len_1, len_2
+
+ d0 = [i for i in range(len_2 + 1)]
+ d1 = [j for j in range(len_2 + 1)]
+
+ for i in range(len_1):
+ d1[0] = i + 1
+ for j in range(len_2):
+ cost = d0[j]
+
+ if string_1[i] != string_2[j]:
+ # substitution
+ cost += 1
+
+ # insertion
+ x_cost = d1[j] + 1
+ if x_cost < cost:
+ cost = x_cost
+
+ # deletion
+ y_cost = d0[j + 1] + 1
+ if y_cost < cost:
+ cost = y_cost
+
+ d1[j + 1] = cost
+
+ d0, d1 = d1, d0
+
+ return d0[-1]
diff --git a/contrib/python/pylev/py3/ya.make b/contrib/python/pylev/py3/ya.make
new file mode 100644
index 0000000000..97cbb58969
--- /dev/null
+++ b/contrib/python/pylev/py3/ya.make
@@ -0,0 +1,26 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(1.4.0)
+
+LICENSE(BSD-3-Clause)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ pylev/__init__.py
+ pylev/classic.py
+ pylev/damerau.py
+ pylev/recursive.py
+ pylev/wf.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/pylev/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
diff --git a/contrib/python/pylev/ya.make b/contrib/python/pylev/ya.make
new file mode 100644
index 0000000000..ab0c0f77c6
--- /dev/null
+++ b/contrib/python/pylev/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/pylev/py2)
+ELSE()
+ PEERDIR(contrib/python/pylev/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/pyre2/py2/AUTHORS b/contrib/python/pyre2/py2/AUTHORS
new file mode 100644
index 0000000000..0f1a37f2b4
--- /dev/null
+++ b/contrib/python/pyre2/py2/AUTHORS
@@ -0,0 +1,12 @@
+All contributors own the copyright to their own contributions, but agree
+to release each of their contributions under the BSD license included
+in this software.
+
+Michael Axiak <mike@axiak.net>
+
+Contributors
+============
+
+Alec Berryman <alec@thened.net>
+Israel Tsadok <itsadok@gmail.com>
+Alex Willmer <alex@moreati.org.uk>
diff --git a/contrib/python/pyre2/py2/LICENSE b/contrib/python/pyre2/py2/LICENSE
new file mode 100644
index 0000000000..803fbbcd9f
--- /dev/null
+++ b/contrib/python/pyre2/py2/LICENSE
@@ -0,0 +1,9 @@
+Copyright (c) 2010, Michael Axiak <mike@axiak.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+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.
+Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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/contrib/python/pyre2/py2/README.rst b/contrib/python/pyre2/py2/README.rst
new file mode 100644
index 0000000000..3f46ff6eaf
--- /dev/null
+++ b/contrib/python/pyre2/py2/README.rst
@@ -0,0 +1,250 @@
+===============================================================
+ pyre2: Python RE2 wrapper for linear-time regular expressions
+===============================================================
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Build/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Build
+ :alt: Build CI Status
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Release/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Release
+ :alt: Release CI Status
+
+.. image:: https://img.shields.io/github/v/tag/andreasvc/pyre2?color=green&include_prereleases&label=latest%20release
+ :target: https://github.com/andreasvc/pyre2/releases
+ :alt: GitHub tag (latest SemVer, including pre-release)
+
+.. image:: https://badge.fury.io/py/pyre2.svg
+ :target: https://badge.fury.io/py/pyre2
+ :alt: Pypi version
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Conda/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Conda
+ :alt: Conda CI Status
+
+.. image:: https://img.shields.io/github/license/andreasvc/pyre2
+ :target: https://github.com/andreasvc/pyre2/blob/master/LICENSE
+ :alt: License
+
+.. image:: https://img.shields.io/badge/python-3.6+-blue.svg
+ :target: https://www.python.org/downloads/
+ :alt: Python version
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/version.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: version
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/platforms.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: platforms
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/downloads.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: downloads
+
+
+.. contents:: Table of Contents
+ :depth: 2
+ :backlinks: top
+
+
+Summary
+=======
+
+pyre2 is a Python extension that wraps
+`Google's RE2 regular expression library <https://github.com/google/re2>`_.
+The RE2 engine compiles (strictly) regular expressions to
+deterministic finite automata, which guarantees linear-time behavior.
+
+Intended as a drop-in replacement for ``re``. Unicode is supported by encoding
+to UTF-8, and bytes strings are treated as UTF-8 when the UNICODE flag is given.
+For best performance, work with UTF-8 encoded bytes strings.
+
+Installation
+============
+
+Normal usage for Linux/Mac/Windows::
+
+ $ pip install pyre2
+
+Compiling from source
+---------------------
+
+Requirements for building the C++ extension from the repo source:
+
+* A build environment with ``gcc`` or ``clang`` (e.g. ``sudo apt-get install build-essential``)
+* Build tools and libraries: RE2, pybind11, and cmake installed in the build
+ environment.
+
+ + On Ubuntu/Debian: ``sudo apt-get install build-essential cmake ninja-build python3-dev cython3 pybind11-dev libre2-dev``
+ + On Gentoo, install dev-util/cmake, dev-python/pybind11, and dev-libs/re2
+ + For a venv you can install the pybind11, cmake, and cython packages from PyPI
+
+On MacOS, use the ``brew`` package manager::
+
+ $ brew install -s re2 pybind11
+
+On Windows use the ``vcpkg`` package manager::
+
+ $ vcpkg install re2:x64-windows pybind11:x64-windows
+
+You can pass some cmake environment variables to alter the build type or
+pass a toolchain file (the latter is required on Windows) or specify the
+cmake generator. For example::
+
+ $ CMAKE_GENERATOR="Unix Makefiles" CMAKE_TOOLCHAIN_FILE=clang_toolchain.cmake tox -e deploy
+
+For development, get the source::
+
+ $ git clone git://github.com/andreasvc/pyre2.git
+ $ cd pyre2
+ $ make install
+
+
+Platform-agnostic building with conda
+-------------------------------------
+
+An alternative to the above is provided via the `conda`_ recipe (use the
+`miniconda installer`_ if you don't have ``conda`` installed already).
+
+
+.. _conda: https://anaconda.org/conda-forge/pyre2
+.. _miniconda installer: https://docs.conda.io/en/latest/miniconda.html
+
+
+Backwards Compatibility
+=======================
+
+The stated goal of this module is to be a drop-in replacement for ``re``, i.e.::
+
+ try:
+ import re2 as re
+ except ImportError:
+ import re
+
+That being said, there are features of the ``re`` module that this module may
+never have; these will be handled through fallback to the original ``re`` module:
+
+* lookahead assertions ``(?!...)``
+* backreferences (``\\n`` in search pattern)
+* \W and \S not supported inside character classes
+
+On the other hand, unicode character classes are supported (e.g., ``\p{Greek}``).
+Syntax reference: https://github.com/google/re2/wiki/Syntax
+
+However, there are times when you may want to be notified of a failover. The
+function ``set_fallback_notification`` determines the behavior in these cases::
+
+ try:
+ import re2 as re
+ except ImportError:
+ import re
+ else:
+ re.set_fallback_notification(re.FALLBACK_WARNING)
+
+``set_fallback_notification`` takes three values:
+``re.FALLBACK_QUIETLY`` (default), ``re.FALLBACK_WARNING`` (raise a warning),
+and ``re.FALLBACK_EXCEPTION`` (raise an exception).
+
+Documentation
+=============
+
+Consult the docstrings in the source code or interactively
+through ipython or ``pydoc re2`` etc.
+
+Unicode Support
+===============
+
+Python ``bytes`` and ``unicode`` strings are fully supported, but note that
+``RE2`` works with UTF-8 encoded strings under the hood, which means that
+``unicode`` strings need to be encoded and decoded back and forth.
+There are two important factors:
+
+* whether a ``unicode`` pattern and search string is used (will be encoded to UTF-8 internally)
+* the ``UNICODE`` flag: whether operators such as ``\w`` recognize Unicode characters.
+
+To avoid the overhead of encoding and decoding to UTF-8, it is possible to pass
+UTF-8 encoded bytes strings directly but still treat them as ``unicode``::
+
+ In [18]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
+ Out[18]: ['M', '\xc3\xb6', 't', 'l', 'e', 'y', 'C', 'r', '\xc3\xbc', 'e']
+ In [19]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'))
+ Out[19]: ['M', 't', 'l', 'e', 'y', 'C', 'r', 'e']
+
+However, note that the indices in ``Match`` objects will refer to the bytes string.
+The indices of the match in the ``unicode`` string could be computed by
+decoding/encoding, but this is done automatically and more efficiently if you
+pass the ``unicode`` string::
+
+ >>> re2.search(u'ü'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
+ <re2.Match object; span=(10, 12), match='\xc3\xbc'>
+ >>> re2.search(u'ü', u'Mötley Crüe', flags=re2.UNICODE)
+ <re2.Match object; span=(9, 10), match=u'\xfc'>
+
+Finally, if you want to match bytes without regard for Unicode characters,
+pass bytes strings and leave out the ``UNICODE`` flag (this will cause Latin 1
+encoding to be used with ``RE2`` under the hood)::
+
+ >>> re2.findall(br'.', b'\x80\x81\x82')
+ ['\x80', '\x81', '\x82']
+
+Performance
+===========
+
+Performance is of course the point of this module, so it better perform well.
+Regular expressions vary widely in complexity, and the salient feature of ``RE2`` is
+that it behaves well asymptotically. This being said, for very simple substitutions,
+I've found that occasionally python's regular ``re`` module is actually slightly faster.
+However, when the ``re`` module gets slow, it gets *really* slow, while this module
+buzzes along.
+
+In the below example, I'm running the data against 8MB of text from the colossal Wikipedia
+XML file. I'm running them multiple times, being careful to use the ``timeit`` module.
+To see more details, please see the `performance script <http://github.com/andreasvc/pyre2/tree/master/tests/performance.py>`_.
+
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Test |Description |# total runs|``re`` time(s)|``re2`` time(s)|% ``re`` time|``regex`` time(s)|% ``regex`` time|
++=================+===========================================================================+============+==============+===============+=============+=================+================+
+|Findall URI|Email|Find list of '([a-zA-Z][a-zA-Z0-9]*)://([^ /]+)(/[^ ]*)?|([^ @]+)@([^ @]+)'|2 |6.262 |0.131 |2.08% |5.119 |2.55% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Replace WikiLinks|This test replaces links of the form [[Obama|Barack_Obama]] to Obama. |100 |4.374 |0.815 |18.63% |1.176 |69.33% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Remove WikiLinks |This test splits the data by the <page> tag. |100 |4.153 |0.225 |5.43% |0.537 |42.01% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+
+Feel free to add more speed tests to the bottom of the script and send a pull request my way!
+
+Current Status
+==============
+
+The tests show the following differences with Python's ``re`` module:
+
+* The ``$`` operator in Python's ``re`` matches twice if the string ends
+ with ``\n``. This can be simulated using ``\n?$``, except when doing
+ substitutions.
+* The ``pyre2`` module and Python's ``re`` may behave differently with nested groups.
+ See ``tests/test_emptygroups.txt`` for the examples.
+
+Please report any further issues with ``pyre2``.
+
+Tests
+=====
+
+If you would like to help, one thing that would be very useful
+is writing comprehensive tests for this. It's actually really easy:
+
+* Come up with regular expression problems using the regular python 're' module.
+* Write a session in python traceback format `Example <http://github.com/andreasvc/pyre2/blob/master/tests/test_search.txt>`_.
+* Replace your ``import re`` with ``import re2 as re``.
+* Save it with as ``test_<name>.txt`` in the tests directory. You can comment on it however you like and indent the code with 4 spaces.
+
+
+Credits
+=======
+This code builds on the following projects (in chronological order):
+
+- Google's RE2 regular expression library: https://github.com/google/re2
+- Facebook's pyre2 github repository: http://github.com/facebook/pyre2/
+- Mike Axiak's Cython version of this: http://github.com/axiak/pyre2/ (seems not actively maintained)
+- This fork adds Python 3 support and other improvements.
+
diff --git a/contrib/python/pyre2/py2/tests/test_charliterals.txt b/contrib/python/pyre2/py2/tests/test_charliterals.txt
new file mode 100644
index 0000000000..2eaea128a3
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_charliterals.txt
@@ -0,0 +1,47 @@
+ >>> import re2 as re
+ >>> import warnings
+ >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+character literals:
+
+ >>> i = 126
+ >>> re.compile(r"\%03o" % i)
+ re2.compile('\\176')
+ >>> re.compile(r"\%03o" % i)._dump_pattern()
+ '\\176'
+ >>> re.match(r"\%03o" % i, chr(i)) is None
+ False
+ >>> re.match(r"\%03o0" % i, chr(i) + "0") is None
+ False
+ >>> re.match(r"\%03o8" % i, chr(i) + "8") is None
+ False
+ >>> re.match(r"\x%02x" % i, chr(i)) is None
+ False
+ >>> re.match(r"\x%02x0" % i, chr(i) + "0") is None
+ False
+ >>> re.match(r"\x%02xz" % i, chr(i) + "z") is None
+ False
+ >>> re.match("\911", "") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ re.error: invalid escape sequence: \9
+
+character class literals:
+
+ >>> re.match(r"[\%03o]" % i, chr(i)) is None
+ False
+ >>> re.match(r"[\%03o0]" % i, chr(i) + "0") is None
+ False
+ >>> re.match(r"[\%03o8]" % i, chr(i) + "8") is None
+ False
+ >>> re.match(r"[\x%02x]" % i, chr(i)) is None
+ False
+ >>> re.match(r"[\x%02x0]" % i, chr(i) + "0") is None
+ False
+ >>> re.match(r"[\x%02xz]" % i, chr(i) + "z") is None
+ False
+ >>> re.match("[\911]", "") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ re.error: invalid escape sequence: \9
+
diff --git a/contrib/python/pyre2/py2/tests/test_count.txt b/contrib/python/pyre2/py2/tests/test_count.txt
new file mode 100644
index 0000000000..ce3525adc5
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_count.txt
@@ -0,0 +1,40 @@
+count tests
+===========
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+This one is from http://docs.python.org/library/re.html?#finding-all-adverbs:
+
+ >>> re2.count(r"\w+ly", "He was carefully disguised but captured quickly by police.")
+ 2
+
+Groups should not affect count():
+
+ >>> re2.count(r"(\w+)=(\d+)", "foo=1,foo=2")
+ 2
+ >>> re2.count(r"(\w)\w", "fx")
+ 1
+
+Zero matches:
+
+ >>> re2.count("(f)", "gggg")
+ 0
+
+A pattern matching an empty string:
+
+ >>> re2.count(".*", "foo")
+ 2
+
+ >>> re2.count("", "foo")
+ 4
+
+contains tests
+==============
+
+ >>> re2.contains('a', 'bbabb')
+ True
+ >>> re2.contains('a', 'bbbbb')
+ False
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_emptygroups.txt b/contrib/python/pyre2/py2/tests/test_emptygroups.txt
new file mode 100644
index 0000000000..424c8ba25e
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_emptygroups.txt
@@ -0,0 +1,36 @@
+Empty/unused groups
+===================
+
+ >>> import re
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+Unused vs. empty group:
+
+ >>> re.search( '(foo)?((.*).)(bar)?', 'a').groups()
+ (None, 'a', '', None)
+ >>> re2.search('(foo)?((.*).)(bar)?', 'a').groups()
+ (None, 'a', '', None)
+
+ >>> re.search(r'((.*)?.)', 'a').groups()
+ ('a', '')
+ >>> re2.search(r'((.*)?.)', 'a').groups()
+ ('a', '')
+ >>> re.search(r'((.*)+.)', 'a').groups()
+ ('a', '')
+ >>> re2.search(r'((.*)+.)', 'a').groups()
+ ('a', '')
+
+The following show different behavior for re and re2:
+
+ >>> re.search(r'((.*)*.)', 'a').groups()
+ ('a', '')
+ >>> re2.search(r'((.*)*.)', 'a').groups()
+ ('a', None)
+
+ >>> re.search(r'((.*)*.)', 'Hello').groups()
+ ('Hello', '')
+ >>> re2.search(r'((.*)*.)', 'Hello').groups()
+ ('Hello', 'Hell')
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_findall.txt b/contrib/python/pyre2/py2/tests/test_findall.txt
new file mode 100644
index 0000000000..c753b936df
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_findall.txt
@@ -0,0 +1,42 @@
+findall tests
+=============
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+This one is from http://docs.python.org/library/re.html?#finding-all-adverbs:
+
+ >>> re2.findall(r"\w+ly", "He was carefully disguised but captured quickly by police.")
+ ['carefully', 'quickly']
+
+This one makes sure all groups are found:
+
+ >>> re2.findall(r"(\w+)=(\d+)", "foo=1,foo=2")
+ [('foo', '1'), ('foo', '2')]
+
+When there's only one matched group, it should not be returned in a tuple:
+
+ >>> re2.findall(r"(\w)\w", "fx")
+ ['f']
+
+Zero matches is an empty list:
+
+ >>> re2.findall("(f)", "gggg")
+ []
+
+If pattern matches an empty string, do it only once at the end:
+
+ >>> re2.findall(".*", "foo")
+ ['foo', '']
+
+ >>> re2.findall("", "foo")
+ ['', '', '', '']
+
+
+ >>> import re
+ >>> re.findall(r'\b', 'The quick brown fox jumped over the lazy dog')
+ ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
+ >>> re2.findall(r'\b', 'The quick brown fox jumped over the lazy dog')
+ ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_finditer.txt b/contrib/python/pyre2/py2/tests/test_finditer.txt
new file mode 100644
index 0000000000..3d60d199c7
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_finditer.txt
@@ -0,0 +1,28 @@
+Simple tests for the ``finditer`` function.
+===========================================
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+ >>> with open('tests/cnn_homepage.dat') as tmp:
+ ... data = tmp.read()
+ >>> len(list(re2.finditer(r'\w+', data)))
+ 14230
+
+ >>> [m.group(1) for m in re2.finditer(r'\n#hdr-editions(.*?)\n', data)]
+ [' a { text-decoration:none; }', ' li { padding:0 10px; }', ' ul li.no-pad-left span { font-size:12px; }']
+
+ >>> [m.group(1) for m in re2.finditer(r'^#hdr-editions(.*?)$',
+ ... data, re2.M)]
+ [' a { text-decoration:none; }', ' li { padding:0 10px; }', ' ul li.no-pad-left span { font-size:12px; }']
+
+ >>> for a in re2.finditer(r'\b', 'foo bar zed'): print(a)
+ <re2.Match object; span=(0, 0), match=''>
+ <re2.Match object; span=(3, 3), match=''>
+ <re2.Match object; span=(4, 4), match=''>
+ <re2.Match object; span=(7, 7), match=''>
+ <re2.Match object; span=(8, 8), match=''>
+ <re2.Match object; span=(11, 11), match=''>
+
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_match_expand.txt b/contrib/python/pyre2/py2/tests/test_match_expand.txt
new file mode 100644
index 0000000000..b3d5652c76
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_match_expand.txt
@@ -0,0 +1,29 @@
+Match Expand Tests
+==================
+
+Match objects have an .expand() method which allows them to
+expand templates as if the .sub() method was called on the pattern.
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+ >>> m = re2.match("(\\w+) (\\w+)\\W+(?P<title>\\w+)", "Isaac Newton, physicist")
+ >>> m.expand("\\2, \\1")
+ 'Newton, Isaac'
+ >>> m.expand("\\1 \\g<title>")
+ 'Isaac physicist'
+ >>> m.expand("\\2, \\1 \\2")
+ 'Newton, Isaac Newton'
+ >>> m.expand("\\3")
+ 'physicist'
+ >>> m.expand("\\1 \\g<foo>") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ IndexError: no such group 'foo'; available groups: ['title']
+ >>> m.expand("\\0")
+ '\x00'
+ >>> m.expand("\01")
+ '\x01'
+ >>> m.expand('\t\n\x0b\r\x0c\x07\x08\\B\\Z\x07\\A\\w\\W\\s\\S\\d\\D')
+ '\t\n\x0b\r\x0c\x07\x08\\B\\Z\x07\\A\\w\\W\\s\\S\\d\\D'
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_mmap.txt b/contrib/python/pyre2/py2/tests/test_mmap.txt
new file mode 100644
index 0000000000..12ffa97498
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_mmap.txt
@@ -0,0 +1,18 @@
+
+Testing re2 on buffer object
+============================
+
+ >>> import re2
+ >>> import mmap
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+ >>> tmp = open("tests/cnn_homepage.dat", "rb+")
+ >>> data = mmap.mmap(tmp.fileno(), 0)
+
+ >>> len(list(re2.finditer(b'\\w+', data)))
+ 14230
+
+ >>> data.close()
+ >>> tmp.close()
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_namedgroups.txt b/contrib/python/pyre2/py2/tests/test_namedgroups.txt
new file mode 100644
index 0000000000..70f561a39f
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_namedgroups.txt
@@ -0,0 +1,56 @@
+Testing some aspects of named groups
+=================================================
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+ >>> m = re2.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
+ >>> m.start("first_name")
+ 0
+ >>> m.start("last_name")
+ 8
+
+ >>> m.span("last_name")
+ (8, 16)
+ >>> m.regs
+ ((0, 16), (0, 7), (8, 16))
+
+ >>> m = re2.match(u"(?P<first_name>\\w+) (?P<last_name>\\w+)", u"Malcolm Reynolds")
+ >>> m.start(u"first_name")
+ 0
+ >>> m.start(u"last_name")
+ 8
+
+ >>> m.span(u"last_name")
+ (8, 16)
+ >>> m.regs
+ ((0, 16), (0, 7), (8, 16))
+
+Compare patterns with and without unicode
+
+ >>> pattern = re2.compile(br"(?P<first_name>\w+) (?P<last_name>\w+)")
+ >>> print(pattern._dump_pattern().decode('utf8'))
+ (?P<first_name>\w+) (?P<last_name>\w+)
+ >>> pattern = re2.compile(u"(?P<first_name>\\w+) (?P<last_name>\\w+)",
+ ... re2.UNICODE)
+ >>> print(pattern._dump_pattern())
+ (?P<first_name>[_\p{L}\p{Nd}]+) (?P<last_name>[_\p{L}\p{Nd}]+)
+
+Make sure positions are converted properly for unicode
+
+ >>> m = pattern.match(
+ ... u'\u05d9\u05e9\u05e8\u05d0\u05dc \u05e6\u05d3\u05d5\u05e7')
+ >>> m.start(u"first_name")
+ 0
+ >>> m.start(u"last_name")
+ 6
+ >>> m.end(u"last_name")
+ 10
+ >>> m.regs
+ ((0, 10), (0, 5), (6, 10))
+ >>> m.span(2)
+ (6, 10)
+ >>> m.span(u"last_name")
+ (6, 10)
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_pattern.txt b/contrib/python/pyre2/py2/tests/test_pattern.txt
new file mode 100644
index 0000000000..aab47359a2
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_pattern.txt
@@ -0,0 +1,12 @@
+pattern tests
+=============
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+We should be able to get back what we put in.
+
+ >>> re2.compile("(foo|b[a]r?)").pattern
+ '(foo|b[a]r?)'
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_search.txt b/contrib/python/pyre2/py2/tests/test_search.txt
new file mode 100644
index 0000000000..9c1e18f08c
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_search.txt
@@ -0,0 +1,29 @@
+These are simple tests of the ``search`` function
+=================================================
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+ >>> import warnings
+ >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+ >>> re2.search("((?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])", "hello 28.224.2.1 test").group()
+ '28.224.2.1'
+
+ >>> re2.search("(\d{3})\D?(\d{3})\D?(\d{4})", "800-555-1212").groups()
+ ('800', '555', '1212')
+
+ >>> input = 'a' * 999
+ >>> len(re2.search('(?:a{1000})?a{999}', input).group())
+ 999
+
+ >>> with open('tests/cnn_homepage.dat') as tmp:
+ ... data = tmp.read()
+ >>> re2.search(r'\n#hdr-editions(.*?)\n', data).groups()
+ (' a { text-decoration:none; }',)
+
+Verify some sanity checks
+
+ >>> re2.compile(r'x').search('x', 2000)
+ >>> re2.compile(r'x').search('x', 1, -300)
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_split.txt b/contrib/python/pyre2/py2/tests/test_split.txt
new file mode 100644
index 0000000000..a3e44bc605
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_split.txt
@@ -0,0 +1,17 @@
+Split tests
+===========
+
+This one tests to make sure that unicode / utf8 data is parsed correctly.
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+ >>> a = u'\u6211\u5f88\u597d, \u4f60\u5462?'
+
+ >>> re2.split(u' ', a) == [u'\u6211\u5f88\u597d,', u'\u4f60\u5462?']
+ True
+ >>> re2.split(b' ', a.encode('utf8')) == [
+ ... b'\xe6\x88\x91\xe5\xbe\x88\xe5\xa5\xbd,',
+ ... b'\xe4\xbd\xa0\xe5\x91\xa2?']
+ True
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py2/tests/test_sub.txt b/contrib/python/pyre2/py2/tests/test_sub.txt
new file mode 100644
index 0000000000..b41dd30d28
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_sub.txt
@@ -0,0 +1,31 @@
+Tests of substitution
+=====================
+
+This first test is just looking to replace things between parentheses
+with an empty string.
+
+
+ >>> import hashlib
+ >>> import gzip
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+ >>> import warnings
+ >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+ >>> with gzip.open('tests/wikipages.xml.gz', 'rb') as tmp:
+ ... data = tmp.read()
+ >>> print(hashlib.md5(re2.sub(b'\(.*?\)', b'', data)).hexdigest())
+ b7a469f55ab76cd5887c81dbb0cfe6d3
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
+
+Issue #26 re2.sub replacements with a match of "(.*)" hangs forever
+
+ >>> re2.sub('(.*)', r'\1;replacement', 'original')
+ 'original;replacement;replacement'
+
+ >>> re2.sub('(.*)', lambda x: x.group() + ';replacement', 'original')
+ 'original;replacement;replacement'
+
+ >>> re2.subn("b*", lambda x: "X", "xyz", 4)
+ ('XxXyXzX', 4)
diff --git a/contrib/python/pyre2/py2/tests/test_unicode.txt b/contrib/python/pyre2/py2/tests/test_unicode.txt
new file mode 100644
index 0000000000..71d497b80d
--- /dev/null
+++ b/contrib/python/pyre2/py2/tests/test_unicode.txt
@@ -0,0 +1,71 @@
+Here are some tests to make sure that utf-8 works
+=================================================
+
+ >>> import sys
+ >>> import re2 as re
+ >>> re.set_fallback_notification(re.FALLBACK_EXCEPTION)
+ >>> a = u'\u6211\u5f88\u597d'
+ >>> c = re.compile(a[0])
+ >>> c.search(a).group() == u'\u6211'
+ True
+
+Test unicode stickyness
+
+ >>> re.sub(u'x', u'y', u'x') == u'y'
+ True
+ >>> re.sub(r'x', 'y', 'x') == 'y'
+ True
+ >>> re.findall('.', 'x') == ['x']
+ True
+ >>> re.findall(u'.', u'x') == [u'x']
+ True
+ >>> re.split(',', '1,2,3') == ['1', '2', '3']
+ True
+ >>> re.split(u',', u'1,2,3') == [u'1', u'2', u'3']
+ True
+ >>> re.search('(\\d)', '1').group(1) == '1'
+ True
+ >>> re.search(u'(\\d)', u'1').group(1) == u'1'
+ True
+
+Test unicode character groups
+
+ >>> re.search(u'\\d', u'\u0661', re.UNICODE).group(0) == u'\u0661'
+ True
+ >>> int(re.search(u'\\d', u'\u0661', re.UNICODE).group(0)) == 1
+ True
+ >>> (re.search(u'\\w', u'\u0401') is None) == (sys.version_info[0] == 2)
+ True
+ >>> re.search(u'\\w', u'\u0401', re.UNICODE).group(0) == u'\u0401'
+ True
+ >>> re.search(u'\\s', u'\u1680', re.UNICODE).group(0) == u'\u1680'
+ True
+ >>> re.findall(r'[\s\d\w]', 'hey 123', re.UNICODE) == ['h', 'e', 'y', ' ', '1', '2', '3']
+ True
+ >>> re.search(u'\\D', u'\u0661x', re.UNICODE).group(0) == u'x'
+ True
+ >>> re.search(u'\\W', u'\u0401!', re.UNICODE).group(0) == u'!'
+ True
+ >>> re.search(u'\\S', u'\u1680x', re.UNICODE).group(0) == u'x'
+ True
+ >>> re.set_fallback_notification(re.FALLBACK_QUIETLY)
+ >>> re.search(u'[\\W]', u'\u0401!', re.UNICODE).group(0) == u'!'
+ True
+ >>> re.search(u'[\\S]', u'\u1680x', re.UNICODE).group(0) == u'x'
+ True
+ >>> re.set_fallback_notification(re.FALLBACK_EXCEPTION)
+
+
+Positions are translated transparently between unicode and UTF-8
+
+ >>> re.search(u' (.)', u'\U0001d200xxx\u1234 x').span(1)
+ (6, 7)
+ >>> re.search(b' (.)', u'\U0001d200xxx\u1234 x'.encode('utf-8')).span(1)
+ (11, 12)
+ >>> re.compile(u'x').findall(u'\u1234x', 1, 2) == [u'x']
+ True
+ >>> data = u'\U0001d200xxx\u1234 x'
+ >>> re.search(u' (.)', data).string == data
+ True
+
+ >>> re.set_fallback_notification(re.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/.dist-info/METADATA b/contrib/python/pyre2/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..6f4f966e33
--- /dev/null
+++ b/contrib/python/pyre2/py3/.dist-info/METADATA
@@ -0,0 +1,275 @@
+Metadata-Version: 2.1
+Name: pyre2
+Version: 0.3.6
+Summary: Python wrapper for Google\'s RE2 using Cython
+Home-page: https://github.com/andreasvc/pyre2
+Author: Andreas van Cranenburgh
+Author-email: andreas@unstable.nl
+Maintainer: Steve Arnold
+Maintainer-email: nerdboy@gentoo.org
+License: BSD
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Cython
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Intended Audience :: Developers
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst; charset=UTF-8
+Provides-Extra: perf
+Requires-Dist: regex ; extra == 'perf'
+Provides-Extra: test
+Requires-Dist: pytest ; extra == 'test'
+
+===============================================================
+ pyre2: Python RE2 wrapper for linear-time regular expressions
+===============================================================
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Build/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Build
+ :alt: Build CI Status
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Release/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Release
+ :alt: Release CI Status
+
+.. image:: https://img.shields.io/github/v/tag/andreasvc/pyre2?color=green&include_prereleases&label=latest%20release
+ :target: https://github.com/andreasvc/pyre2/releases
+ :alt: GitHub tag (latest SemVer, including pre-release)
+
+.. image:: https://badge.fury.io/py/pyre2.svg
+ :target: https://badge.fury.io/py/pyre2
+ :alt: Pypi version
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Conda/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Conda
+ :alt: Conda CI Status
+
+.. image:: https://img.shields.io/github/license/andreasvc/pyre2
+ :target: https://github.com/andreasvc/pyre2/blob/master/LICENSE
+ :alt: License
+
+.. image:: https://img.shields.io/badge/python-3.6+-blue.svg
+ :target: https://www.python.org/downloads/
+ :alt: Python version
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/version.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: version
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/platforms.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: platforms
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/downloads.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: downloads
+
+
+.. contents:: Table of Contents
+ :depth: 2
+ :backlinks: top
+
+
+Summary
+=======
+
+pyre2 is a Python extension that wraps
+`Google's RE2 regular expression library <https://github.com/google/re2>`_.
+The RE2 engine compiles (strictly) regular expressions to
+deterministic finite automata, which guarantees linear-time behavior.
+
+Intended as a drop-in replacement for ``re``. Unicode is supported by encoding
+to UTF-8, and bytes strings are treated as UTF-8 when the UNICODE flag is given.
+For best performance, work with UTF-8 encoded bytes strings.
+
+Installation
+============
+
+Normal usage for Linux/Mac/Windows::
+
+ $ pip install pyre2
+
+Compiling from source
+---------------------
+
+Requirements for building the C++ extension from the repo source:
+
+* A build environment with ``gcc`` or ``clang`` (e.g. ``sudo apt-get install build-essential``)
+* Build tools and libraries: RE2, pybind11, and cmake installed in the build
+ environment.
+
+ + On Ubuntu/Debian: ``sudo apt-get install build-essential cmake ninja-build python3-dev cython3 pybind11-dev libre2-dev``
+ + On Gentoo, install dev-util/cmake, dev-python/pybind11, and dev-libs/re2
+ + For a venv you can install the pybind11, cmake, and cython packages from PyPI
+
+On MacOS, use the ``brew`` package manager::
+
+ $ brew install -s re2 pybind11
+
+On Windows use the ``vcpkg`` package manager::
+
+ $ vcpkg install re2:x64-windows pybind11:x64-windows
+
+You can pass some cmake environment variables to alter the build type or
+pass a toolchain file (the latter is required on Windows) or specify the
+cmake generator. For example::
+
+ $ CMAKE_GENERATOR="Unix Makefiles" CMAKE_TOOLCHAIN_FILE=clang_toolchain.cmake tox -e deploy
+
+For development, get the source::
+
+ $ git clone git://github.com/andreasvc/pyre2.git
+ $ cd pyre2
+ $ make install
+
+
+Platform-agnostic building with conda
+-------------------------------------
+
+An alternative to the above is provided via the `conda`_ recipe (use the
+`miniconda installer`_ if you don't have ``conda`` installed already).
+
+
+.. _conda: https://anaconda.org/conda-forge/pyre2
+.. _miniconda installer: https://docs.conda.io/en/latest/miniconda.html
+
+
+Backwards Compatibility
+=======================
+
+The stated goal of this module is to be a drop-in replacement for ``re``, i.e.::
+
+ try:
+ import re2 as re
+ except ImportError:
+ import re
+
+That being said, there are features of the ``re`` module that this module may
+never have; these will be handled through fallback to the original ``re`` module:
+
+* lookahead assertions ``(?!...)``
+* backreferences (``\\n`` in search pattern)
+* \W and \S not supported inside character classes
+
+On the other hand, unicode character classes are supported (e.g., ``\p{Greek}``).
+Syntax reference: https://github.com/google/re2/wiki/Syntax
+
+However, there are times when you may want to be notified of a failover. The
+function ``set_fallback_notification`` determines the behavior in these cases::
+
+ try:
+ import re2 as re
+ except ImportError:
+ import re
+ else:
+ re.set_fallback_notification(re.FALLBACK_WARNING)
+
+``set_fallback_notification`` takes three values:
+``re.FALLBACK_QUIETLY`` (default), ``re.FALLBACK_WARNING`` (raise a warning),
+and ``re.FALLBACK_EXCEPTION`` (raise an exception).
+
+Documentation
+=============
+
+Consult the docstrings in the source code or interactively
+through ipython or ``pydoc re2`` etc.
+
+Unicode Support
+===============
+
+Python ``bytes`` and ``unicode`` strings are fully supported, but note that
+``RE2`` works with UTF-8 encoded strings under the hood, which means that
+``unicode`` strings need to be encoded and decoded back and forth.
+There are two important factors:
+
+* whether a ``unicode`` pattern and search string is used (will be encoded to UTF-8 internally)
+* the ``UNICODE`` flag: whether operators such as ``\w`` recognize Unicode characters.
+
+To avoid the overhead of encoding and decoding to UTF-8, it is possible to pass
+UTF-8 encoded bytes strings directly but still treat them as ``unicode``::
+
+ In [18]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
+ Out[18]: ['M', '\xc3\xb6', 't', 'l', 'e', 'y', 'C', 'r', '\xc3\xbc', 'e']
+ In [19]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'))
+ Out[19]: ['M', 't', 'l', 'e', 'y', 'C', 'r', 'e']
+
+However, note that the indices in ``Match`` objects will refer to the bytes string.
+The indices of the match in the ``unicode`` string could be computed by
+decoding/encoding, but this is done automatically and more efficiently if you
+pass the ``unicode`` string::
+
+ >>> re2.search(u'ü'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
+ <re2.Match object; span=(10, 12), match='\xc3\xbc'>
+ >>> re2.search(u'ü', u'Mötley Crüe', flags=re2.UNICODE)
+ <re2.Match object; span=(9, 10), match=u'\xfc'>
+
+Finally, if you want to match bytes without regard for Unicode characters,
+pass bytes strings and leave out the ``UNICODE`` flag (this will cause Latin 1
+encoding to be used with ``RE2`` under the hood)::
+
+ >>> re2.findall(br'.', b'\x80\x81\x82')
+ ['\x80', '\x81', '\x82']
+
+Performance
+===========
+
+Performance is of course the point of this module, so it better perform well.
+Regular expressions vary widely in complexity, and the salient feature of ``RE2`` is
+that it behaves well asymptotically. This being said, for very simple substitutions,
+I've found that occasionally python's regular ``re`` module is actually slightly faster.
+However, when the ``re`` module gets slow, it gets *really* slow, while this module
+buzzes along.
+
+In the below example, I'm running the data against 8MB of text from the colossal Wikipedia
+XML file. I'm running them multiple times, being careful to use the ``timeit`` module.
+To see more details, please see the `performance script <http://github.com/andreasvc/pyre2/tree/master/tests/performance.py>`_.
+
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Test |Description |# total runs|``re`` time(s)|``re2`` time(s)|% ``re`` time|``regex`` time(s)|% ``regex`` time|
++=================+===========================================================================+============+==============+===============+=============+=================+================+
+|Findall URI|Email|Find list of '([a-zA-Z][a-zA-Z0-9]*)://([^ /]+)(/[^ ]*)?|([^ @]+)@([^ @]+)'|2 |6.262 |0.131 |2.08% |5.119 |2.55% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Replace WikiLinks|This test replaces links of the form [[Obama|Barack_Obama]] to Obama. |100 |4.374 |0.815 |18.63% |1.176 |69.33% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Remove WikiLinks |This test splits the data by the <page> tag. |100 |4.153 |0.225 |5.43% |0.537 |42.01% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+
+Feel free to add more speed tests to the bottom of the script and send a pull request my way!
+
+Current Status
+==============
+
+The tests show the following differences with Python's ``re`` module:
+
+* The ``$`` operator in Python's ``re`` matches twice if the string ends
+ with ``\n``. This can be simulated using ``\n?$``, except when doing
+ substitutions.
+* The ``pyre2`` module and Python's ``re`` may behave differently with nested groups.
+ See ``tests/test_emptygroups.txt`` for the examples.
+
+Please report any further issues with ``pyre2``.
+
+Tests
+=====
+
+If you would like to help, one thing that would be very useful
+is writing comprehensive tests for this. It's actually really easy:
+
+* Come up with regular expression problems using the regular python 're' module.
+* Write a session in python traceback format `Example <http://github.com/andreasvc/pyre2/blob/master/tests/test_search.txt>`_.
+* Replace your ``import re`` with ``import re2 as re``.
+* Save it with as ``test_<name>.txt`` in the tests directory. You can comment on it however you like and indent the code with 4 spaces.
+
+
+Credits
+=======
+This code builds on the following projects (in chronological order):
+
+- Google's RE2 regular expression library: https://github.com/google/re2
+- Facebook's pyre2 github repository: http://github.com/facebook/pyre2/
+- Mike Axiak's Cython version of this: http://github.com/axiak/pyre2/ (seems not actively maintained)
+- This fork adds Python 3 support and other improvements.
+
+
+
diff --git a/contrib/python/pyre2/py3/.dist-info/top_level.txt b/contrib/python/pyre2/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..94e9d8fad0
--- /dev/null
+++ b/contrib/python/pyre2/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+re2
diff --git a/contrib/python/pyre2/py3/AUTHORS b/contrib/python/pyre2/py3/AUTHORS
new file mode 100644
index 0000000000..0f1a37f2b4
--- /dev/null
+++ b/contrib/python/pyre2/py3/AUTHORS
@@ -0,0 +1,12 @@
+All contributors own the copyright to their own contributions, but agree
+to release each of their contributions under the BSD license included
+in this software.
+
+Michael Axiak <mike@axiak.net>
+
+Contributors
+============
+
+Alec Berryman <alec@thened.net>
+Israel Tsadok <itsadok@gmail.com>
+Alex Willmer <alex@moreati.org.uk>
diff --git a/contrib/python/pyre2/py3/LICENSE b/contrib/python/pyre2/py3/LICENSE
new file mode 100644
index 0000000000..803fbbcd9f
--- /dev/null
+++ b/contrib/python/pyre2/py3/LICENSE
@@ -0,0 +1,9 @@
+Copyright (c) 2010, Michael Axiak <mike@axiak.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+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.
+Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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/contrib/python/pyre2/py3/README.rst b/contrib/python/pyre2/py3/README.rst
new file mode 100644
index 0000000000..3f46ff6eaf
--- /dev/null
+++ b/contrib/python/pyre2/py3/README.rst
@@ -0,0 +1,250 @@
+===============================================================
+ pyre2: Python RE2 wrapper for linear-time regular expressions
+===============================================================
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Build/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Build
+ :alt: Build CI Status
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Release/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Release
+ :alt: Release CI Status
+
+.. image:: https://img.shields.io/github/v/tag/andreasvc/pyre2?color=green&include_prereleases&label=latest%20release
+ :target: https://github.com/andreasvc/pyre2/releases
+ :alt: GitHub tag (latest SemVer, including pre-release)
+
+.. image:: https://badge.fury.io/py/pyre2.svg
+ :target: https://badge.fury.io/py/pyre2
+ :alt: Pypi version
+
+.. image:: https://github.com/andreasvc/pyre2/workflows/Conda/badge.svg
+ :target: https://github.com/andreasvc/pyre2/actions?query=workflow:Conda
+ :alt: Conda CI Status
+
+.. image:: https://img.shields.io/github/license/andreasvc/pyre2
+ :target: https://github.com/andreasvc/pyre2/blob/master/LICENSE
+ :alt: License
+
+.. image:: https://img.shields.io/badge/python-3.6+-blue.svg
+ :target: https://www.python.org/downloads/
+ :alt: Python version
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/version.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: version
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/platforms.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: platforms
+
+.. image:: https://anaconda.org/conda-forge/pyre2/badges/downloads.svg
+ :target: https://anaconda.org/conda-forge/pyre2
+ :alt: downloads
+
+
+.. contents:: Table of Contents
+ :depth: 2
+ :backlinks: top
+
+
+Summary
+=======
+
+pyre2 is a Python extension that wraps
+`Google's RE2 regular expression library <https://github.com/google/re2>`_.
+The RE2 engine compiles (strictly) regular expressions to
+deterministic finite automata, which guarantees linear-time behavior.
+
+Intended as a drop-in replacement for ``re``. Unicode is supported by encoding
+to UTF-8, and bytes strings are treated as UTF-8 when the UNICODE flag is given.
+For best performance, work with UTF-8 encoded bytes strings.
+
+Installation
+============
+
+Normal usage for Linux/Mac/Windows::
+
+ $ pip install pyre2
+
+Compiling from source
+---------------------
+
+Requirements for building the C++ extension from the repo source:
+
+* A build environment with ``gcc`` or ``clang`` (e.g. ``sudo apt-get install build-essential``)
+* Build tools and libraries: RE2, pybind11, and cmake installed in the build
+ environment.
+
+ + On Ubuntu/Debian: ``sudo apt-get install build-essential cmake ninja-build python3-dev cython3 pybind11-dev libre2-dev``
+ + On Gentoo, install dev-util/cmake, dev-python/pybind11, and dev-libs/re2
+ + For a venv you can install the pybind11, cmake, and cython packages from PyPI
+
+On MacOS, use the ``brew`` package manager::
+
+ $ brew install -s re2 pybind11
+
+On Windows use the ``vcpkg`` package manager::
+
+ $ vcpkg install re2:x64-windows pybind11:x64-windows
+
+You can pass some cmake environment variables to alter the build type or
+pass a toolchain file (the latter is required on Windows) or specify the
+cmake generator. For example::
+
+ $ CMAKE_GENERATOR="Unix Makefiles" CMAKE_TOOLCHAIN_FILE=clang_toolchain.cmake tox -e deploy
+
+For development, get the source::
+
+ $ git clone git://github.com/andreasvc/pyre2.git
+ $ cd pyre2
+ $ make install
+
+
+Platform-agnostic building with conda
+-------------------------------------
+
+An alternative to the above is provided via the `conda`_ recipe (use the
+`miniconda installer`_ if you don't have ``conda`` installed already).
+
+
+.. _conda: https://anaconda.org/conda-forge/pyre2
+.. _miniconda installer: https://docs.conda.io/en/latest/miniconda.html
+
+
+Backwards Compatibility
+=======================
+
+The stated goal of this module is to be a drop-in replacement for ``re``, i.e.::
+
+ try:
+ import re2 as re
+ except ImportError:
+ import re
+
+That being said, there are features of the ``re`` module that this module may
+never have; these will be handled through fallback to the original ``re`` module:
+
+* lookahead assertions ``(?!...)``
+* backreferences (``\\n`` in search pattern)
+* \W and \S not supported inside character classes
+
+On the other hand, unicode character classes are supported (e.g., ``\p{Greek}``).
+Syntax reference: https://github.com/google/re2/wiki/Syntax
+
+However, there are times when you may want to be notified of a failover. The
+function ``set_fallback_notification`` determines the behavior in these cases::
+
+ try:
+ import re2 as re
+ except ImportError:
+ import re
+ else:
+ re.set_fallback_notification(re.FALLBACK_WARNING)
+
+``set_fallback_notification`` takes three values:
+``re.FALLBACK_QUIETLY`` (default), ``re.FALLBACK_WARNING`` (raise a warning),
+and ``re.FALLBACK_EXCEPTION`` (raise an exception).
+
+Documentation
+=============
+
+Consult the docstrings in the source code or interactively
+through ipython or ``pydoc re2`` etc.
+
+Unicode Support
+===============
+
+Python ``bytes`` and ``unicode`` strings are fully supported, but note that
+``RE2`` works with UTF-8 encoded strings under the hood, which means that
+``unicode`` strings need to be encoded and decoded back and forth.
+There are two important factors:
+
+* whether a ``unicode`` pattern and search string is used (will be encoded to UTF-8 internally)
+* the ``UNICODE`` flag: whether operators such as ``\w`` recognize Unicode characters.
+
+To avoid the overhead of encoding and decoding to UTF-8, it is possible to pass
+UTF-8 encoded bytes strings directly but still treat them as ``unicode``::
+
+ In [18]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
+ Out[18]: ['M', '\xc3\xb6', 't', 'l', 'e', 'y', 'C', 'r', '\xc3\xbc', 'e']
+ In [19]: re2.findall(u'\w'.encode('utf8'), u'Mötley Crüe'.encode('utf8'))
+ Out[19]: ['M', 't', 'l', 'e', 'y', 'C', 'r', 'e']
+
+However, note that the indices in ``Match`` objects will refer to the bytes string.
+The indices of the match in the ``unicode`` string could be computed by
+decoding/encoding, but this is done automatically and more efficiently if you
+pass the ``unicode`` string::
+
+ >>> re2.search(u'ü'.encode('utf8'), u'Mötley Crüe'.encode('utf8'), flags=re2.UNICODE)
+ <re2.Match object; span=(10, 12), match='\xc3\xbc'>
+ >>> re2.search(u'ü', u'Mötley Crüe', flags=re2.UNICODE)
+ <re2.Match object; span=(9, 10), match=u'\xfc'>
+
+Finally, if you want to match bytes without regard for Unicode characters,
+pass bytes strings and leave out the ``UNICODE`` flag (this will cause Latin 1
+encoding to be used with ``RE2`` under the hood)::
+
+ >>> re2.findall(br'.', b'\x80\x81\x82')
+ ['\x80', '\x81', '\x82']
+
+Performance
+===========
+
+Performance is of course the point of this module, so it better perform well.
+Regular expressions vary widely in complexity, and the salient feature of ``RE2`` is
+that it behaves well asymptotically. This being said, for very simple substitutions,
+I've found that occasionally python's regular ``re`` module is actually slightly faster.
+However, when the ``re`` module gets slow, it gets *really* slow, while this module
+buzzes along.
+
+In the below example, I'm running the data against 8MB of text from the colossal Wikipedia
+XML file. I'm running them multiple times, being careful to use the ``timeit`` module.
+To see more details, please see the `performance script <http://github.com/andreasvc/pyre2/tree/master/tests/performance.py>`_.
+
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Test |Description |# total runs|``re`` time(s)|``re2`` time(s)|% ``re`` time|``regex`` time(s)|% ``regex`` time|
++=================+===========================================================================+============+==============+===============+=============+=================+================+
+|Findall URI|Email|Find list of '([a-zA-Z][a-zA-Z0-9]*)://([^ /]+)(/[^ ]*)?|([^ @]+)@([^ @]+)'|2 |6.262 |0.131 |2.08% |5.119 |2.55% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Replace WikiLinks|This test replaces links of the form [[Obama|Barack_Obama]] to Obama. |100 |4.374 |0.815 |18.63% |1.176 |69.33% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+|Remove WikiLinks |This test splits the data by the <page> tag. |100 |4.153 |0.225 |5.43% |0.537 |42.01% |
++-----------------+---------------------------------------------------------------------------+------------+--------------+---------------+-------------+-----------------+----------------+
+
+Feel free to add more speed tests to the bottom of the script and send a pull request my way!
+
+Current Status
+==============
+
+The tests show the following differences with Python's ``re`` module:
+
+* The ``$`` operator in Python's ``re`` matches twice if the string ends
+ with ``\n``. This can be simulated using ``\n?$``, except when doing
+ substitutions.
+* The ``pyre2`` module and Python's ``re`` may behave differently with nested groups.
+ See ``tests/test_emptygroups.txt`` for the examples.
+
+Please report any further issues with ``pyre2``.
+
+Tests
+=====
+
+If you would like to help, one thing that would be very useful
+is writing comprehensive tests for this. It's actually really easy:
+
+* Come up with regular expression problems using the regular python 're' module.
+* Write a session in python traceback format `Example <http://github.com/andreasvc/pyre2/blob/master/tests/test_search.txt>`_.
+* Replace your ``import re`` with ``import re2 as re``.
+* Save it with as ``test_<name>.txt`` in the tests directory. You can comment on it however you like and indent the code with 4 spaces.
+
+
+Credits
+=======
+This code builds on the following projects (in chronological order):
+
+- Google's RE2 regular expression library: https://github.com/google/re2
+- Facebook's pyre2 github repository: http://github.com/facebook/pyre2/
+- Mike Axiak's Cython version of this: http://github.com/axiak/pyre2/ (seems not actively maintained)
+- This fork adds Python 3 support and other improvements.
+
diff --git a/contrib/python/pyre2/py3/src/_re2macros.h b/contrib/python/pyre2/py3/src/_re2macros.h
new file mode 100644
index 0000000000..b9ac82af6b
--- /dev/null
+++ b/contrib/python/pyre2/py3/src/_re2macros.h
@@ -0,0 +1,13 @@
+#ifndef __RE2MACROS_H
+#define __RE2MACROS_H
+
+#include <stdio.h>
+#include "re2/stringpiece.h"
+
+static inline re2::StringPiece * new_StringPiece_array(int n)
+{
+ re2::StringPiece * sp = new re2::StringPiece[n];
+ return sp;
+}
+
+#endif
diff --git a/contrib/python/pyre2/py3/src/compile.pxi b/contrib/python/pyre2/py3/src/compile.pxi
new file mode 100644
index 0000000000..887a2778cd
--- /dev/null
+++ b/contrib/python/pyre2/py3/src/compile.pxi
@@ -0,0 +1,234 @@
+
+def compile(pattern, int flags=0, int max_mem=8388608):
+ cachekey = (type(pattern), pattern, flags)
+ if cachekey in _cache:
+ return _cache[cachekey]
+ p = _compile(pattern, flags, max_mem)
+
+ if len(_cache) >= _MAXCACHE:
+ _cache.popitem()
+ _cache[cachekey] = p
+ return p
+
+
+def _compile(object pattern, int flags=0, int max_mem=8388608):
+ """Compile a regular expression pattern, returning a pattern object."""
+ def fallback(pattern, flags, error_msg):
+ """Raise error, warn, or simply return fallback from re module."""
+ if current_notification == FALLBACK_EXCEPTION:
+ raise RegexError(error_msg)
+ elif current_notification == FALLBACK_WARNING:
+ warnings.warn("WARNING: Using re module. Reason: %s" % error_msg)
+ try:
+ result = PythonRePattern(pattern, flags)
+ except re.error as err:
+ raise RegexError(*err.args)
+ return result
+
+ cdef StringPiece * s
+ cdef Options opts
+ cdef int error_code
+ cdef int encoded = 0
+ cdef object original_pattern
+
+ if isinstance(pattern, (Pattern, SREPattern)):
+ if flags:
+ raise ValueError(
+ 'Cannot process flags argument with a compiled pattern')
+ return pattern
+
+ original_pattern = pattern
+ if flags & _L:
+ return fallback(original_pattern, flags, "re.LOCALE not supported")
+ pattern = unicode_to_bytes(pattern, &encoded, -1)
+ newflags = flags
+ if not PY2:
+ if not encoded and flags & _U: # re.UNICODE
+ pass # can use UNICODE with bytes pattern, but assumes valid UTF-8
+ # raise ValueError("can't use UNICODE flag with a bytes pattern")
+ elif encoded and not (flags & ASCII): # re.ASCII (not in Python 2)
+ newflags = flags | _U # re.UNICODE
+ elif encoded and flags & ASCII:
+ newflags = flags & ~_U # re.UNICODE
+ try:
+ pattern = _prepare_pattern(pattern, newflags)
+ except BackreferencesException:
+ return fallback(original_pattern, flags, "Backreferences not supported")
+ except CharClassProblemException:
+ return fallback(original_pattern, flags,
+ "\W and \S not supported inside character classes")
+
+ # Set the options given the flags above.
+ if flags & _I:
+ opts.set_case_sensitive(0);
+
+ opts.set_max_mem(max_mem)
+ opts.set_log_errors(0)
+ if flags & _U or encoded:
+ opts.set_encoding(EncodingUTF8)
+ else: # re.UNICODE flag not passed, and pattern is bytes,
+ # so allow matching of arbitrary byte sequences.
+ opts.set_encoding(EncodingLatin1)
+
+ s = new StringPiece(<char *><bytes>pattern, len(pattern))
+
+ cdef RE2 *re_pattern
+ with nogil:
+ re_pattern = new RE2(s[0], opts)
+
+ if not re_pattern.ok():
+ # Something went wrong with the compilation.
+ del s
+ error_msg = cpp_to_unicode(re_pattern.error())
+ error_code = re_pattern.error_code()
+ del re_pattern
+ if current_notification == FALLBACK_EXCEPTION:
+ # Raise an exception regardless of the type of error.
+ raise RegexError(error_msg)
+ elif error_code not in (ErrorBadPerlOp, ErrorRepeatSize,
+ # ErrorBadEscape,
+ ErrorPatternTooLarge):
+ # Raise an error because these will not be fixed by using the
+ # ``re`` module.
+ raise RegexError(error_msg)
+ elif current_notification == FALLBACK_WARNING:
+ warnings.warn("WARNING: Using re module. Reason: %s" % error_msg)
+ return PythonRePattern(original_pattern, flags)
+
+ cdef Pattern pypattern = Pattern()
+ cdef map[cpp_string, int] named_groups = re_pattern.NamedCapturingGroups()
+ pypattern.pattern = original_pattern
+ pypattern.re_pattern = re_pattern
+ pypattern.groups = re_pattern.NumberOfCapturingGroups()
+ pypattern.encoded = encoded
+ pypattern.flags = flags
+ pypattern.groupindex = {}
+ for it in named_groups:
+ pypattern.groupindex[cpp_to_unicode(it.first)] = it.second
+
+ if flags & DEBUG:
+ print(repr(pypattern._dump_pattern()))
+ del s
+ return pypattern
+
+
+def _prepare_pattern(bytes pattern, int flags):
+ """Translate pattern to RE2 syntax."""
+ cdef bytearray result = bytearray()
+ cdef unsigned char * cstring = pattern
+ cdef unsigned char this, that
+ cdef int size = len(pattern)
+ cdef int n = 0
+
+ if flags & (_S | _M):
+ result.extend(b'(?')
+ if flags & _S:
+ result.extend(b's')
+ if flags & _M:
+ result.extend(b'm')
+ result.extend(b')')
+ while n < size:
+ this = cstring[n]
+ if flags & _X:
+ if this in b' \t\n\r\f\v':
+ n += 1
+ continue
+ elif this == b'#':
+ while True:
+ n += 1
+ if n >= size:
+ break
+ this = cstring[n]
+ if this == b'\n':
+ break
+ n += 1
+ continue
+
+ if this != b'[' and this != b'\\':
+ result.append(this)
+ n += 1
+ continue
+ elif this == b'[':
+ result.append(this)
+ while True:
+ n += 1
+ if n >= size:
+ raise RegexError("unexpected end of regular expression")
+ this = cstring[n]
+ if this == b']':
+ result.append(this)
+ break
+ elif this == b'\\':
+ n += 1
+ that = cstring[n]
+ if that == b'b':
+ result.extend(br'\010')
+ elif flags & _U:
+ if that == b'd':
+ result.extend(br'\p{Nd}')
+ elif that == b'w':
+ result.extend(br'_\p{L}\p{Nd}')
+ elif that == b's':
+ result.extend(br'\s\p{Z}')
+ elif that == b'D':
+ result.extend(br'\P{Nd}')
+ elif that == b'W':
+ # Since \w and \s are made out of several character
+ # groups, I don't see a way to convert their
+ # complements into a group without rewriting the
+ # whole expression, which seems too complicated.
+ raise CharClassProblemException()
+ elif that == b'S':
+ raise CharClassProblemException()
+ else:
+ result.append(this)
+ result.append(that)
+ else:
+ result.append(this)
+ result.append(that)
+ else:
+ result.append(this)
+ elif this == b'\\':
+ n += 1
+ that = cstring[n]
+ if b'8' <= that <= b'9':
+ raise BackreferencesException()
+ elif isoct(that):
+ if (n + 2 < size and isoct(cstring[n + 1])
+ and isoct(cstring[n + 2])):
+ # all clear, this is an octal escape
+ result.extend(cstring[n - 1:n + 3])
+ n += 2
+ else:
+ raise BackreferencesException()
+ elif that == b'x':
+ if (n + 2 < size and ishex(cstring[n + 1])
+ and ishex(cstring[n + 2])):
+ # hex escape
+ result.extend(cstring[n - 1:n + 3])
+ n += 2
+ else:
+ raise BackreferencesException()
+ elif that == b'Z':
+ result.extend(b'\\z')
+ elif flags & _U:
+ if that == b'd':
+ result.extend(br'\p{Nd}')
+ elif that == b'w':
+ result.extend(br'[_\p{L}\p{Nd}]')
+ elif that == b's':
+ result.extend(br'[\s\p{Z}]')
+ elif that == b'D':
+ result.extend(br'[^\p{Nd}]')
+ elif that == b'W':
+ result.extend(br'[^_\p{L}\p{Nd}]')
+ elif that == b'S':
+ result.extend(br'[^\s\p{Z}]')
+ else:
+ result.append(this)
+ result.append(that)
+ else:
+ result.append(this)
+ result.append(that)
+ n += 1
+ return bytes(result)
diff --git a/contrib/python/pyre2/py3/src/includes.pxi b/contrib/python/pyre2/py3/src/includes.pxi
new file mode 100644
index 0000000000..8c35b6d4b2
--- /dev/null
+++ b/contrib/python/pyre2/py3/src/includes.pxi
@@ -0,0 +1,109 @@
+cimport cpython.unicode
+from libcpp.map cimport map
+from libcpp.string cimport string as cpp_string
+from cython.operator cimport postincrement, dereference
+from cpython.buffer cimport Py_buffer, PyBUF_SIMPLE, PyObject_CheckBuffer, \
+ PyObject_GetBuffer, PyBuffer_Release
+from cpython.version cimport PY_MAJOR_VERSION
+
+
+cdef extern from *:
+ cdef void emit_if_narrow_unicode "#if !defined(Py_UNICODE_WIDE) && PY_VERSION_HEX < 0x03030000 //" ()
+ cdef void emit_endif "#endif //" ()
+
+
+cdef extern from "Python.h":
+ int PyObject_CheckReadBuffer(object)
+ int PyObject_AsReadBuffer(object, const void **, Py_ssize_t *)
+
+
+cdef extern from "re2/stringpiece.h" namespace "re2":
+ cdef cppclass StringPiece:
+ StringPiece()
+ StringPiece(const char *)
+ StringPiece(const char *, int)
+ const char * data()
+ int copy(char * buf, size_t n, size_t pos)
+ int length()
+
+
+cdef extern from "re2/re2.h" namespace "re2":
+ cdef enum Anchor:
+ UNANCHORED "RE2::UNANCHORED"
+ ANCHOR_START "RE2::ANCHOR_START"
+ ANCHOR_BOTH "RE2::ANCHOR_BOTH"
+
+ ctypedef Anchor re2_Anchor "RE2::Anchor"
+
+ cdef enum ErrorCode:
+ NoError "RE2::NoError"
+ ErrorInternal "RE2::ErrorInternal"
+ # Parse errors
+ ErrorBadEscape "RE2::ErrorBadEscape" # bad escape sequence
+ ErrorBadCharClass "RE2::ErrorBadCharClass" # bad character class
+ ErrorBadCharRange "RE2::ErrorBadCharRange" # bad character class range
+ ErrorMissingBracket "RE2::ErrorMissingBracket" # missing closing ]
+ ErrorMissingParen "RE2::ErrorMissingParen" # missing closing )
+ ErrorTrailingBackslash "RE2::ErrorTrailingBackslash" # trailing \ at end of regexp
+ ErrorRepeatArgument "RE2::ErrorRepeatArgument" # repeat argument missing, e.g. "*"
+ ErrorRepeatSize "RE2::ErrorRepeatSize" # bad repetition argument
+ ErrorRepeatOp "RE2::ErrorRepeatOp" # bad repetition operator
+ ErrorBadPerlOp "RE2::ErrorBadPerlOp" # bad perl operator
+ ErrorBadUTF8 "RE2::ErrorBadUTF8" # invalid UTF-8 in regexp
+ ErrorBadNamedCapture "RE2::ErrorBadNamedCapture" # bad named capture group
+ ErrorPatternTooLarge "RE2::ErrorPatternTooLarge" # pattern too large (compile failed)
+
+ cdef enum Encoding:
+ EncodingUTF8 "RE2::Options::EncodingUTF8"
+ EncodingLatin1 "RE2::Options::EncodingLatin1"
+
+ ctypedef Encoding re2_Encoding "RE2::Options::Encoding"
+
+ cdef cppclass Options "RE2::Options":
+ Options()
+ void set_posix_syntax(int b)
+ void set_longest_match(int b)
+ void set_log_errors(int b)
+ void set_max_mem(int m)
+ void set_literal(int b)
+ void set_never_nl(int b)
+ void set_case_sensitive(int b)
+ void set_perl_classes(int b)
+ void set_word_boundary(int b)
+ void set_one_line(int b)
+ int case_sensitive()
+ void set_encoding(re2_Encoding encoding)
+
+ cdef cppclass RE2:
+ RE2(const StringPiece pattern, Options option) nogil
+ RE2(const StringPiece pattern) nogil
+ int Match(const StringPiece text, int startpos, int endpos,
+ Anchor anchor, StringPiece * match, int nmatch) nogil
+ int Replace(cpp_string *str, const RE2 pattern,
+ const StringPiece rewrite) nogil
+ int GlobalReplace(cpp_string *str, const RE2 pattern,
+ const StringPiece rewrite) nogil
+ int NumberOfCapturingGroups()
+ int ok()
+ const cpp_string pattern()
+ cpp_string error()
+ ErrorCode error_code()
+ const map[cpp_string, int]& NamedCapturingGroups()
+
+ # hack for static methods
+ cdef int Replace "RE2::Replace"(
+ cpp_string *str, const RE2 pattern,
+ const StringPiece rewrite) nogil
+ cdef int GlobalReplace "RE2::GlobalReplace"(
+ cpp_string *str,
+ const RE2 pattern,
+ const StringPiece rewrite) nogil
+
+
+cdef extern from "_re2macros.h":
+ StringPiece * new_StringPiece_array(int) nogil
+
+
+cdef extern from *:
+ # StringPiece * new_StringPiece_array "new re2::StringPiece[n]" (int) nogil
+ void delete_StringPiece_array "delete[]" (StringPiece *) nogil
diff --git a/contrib/python/pyre2/py3/src/match.pxi b/contrib/python/pyre2/py3/src/match.pxi
new file mode 100644
index 0000000000..3eaae74b47
--- /dev/null
+++ b/contrib/python/pyre2/py3/src/match.pxi
@@ -0,0 +1,280 @@
+cdef class Match:
+ cdef readonly Pattern re
+ cdef readonly object string
+ cdef readonly int pos
+ cdef readonly int endpos
+ cdef readonly tuple regs
+
+ cdef StringPiece * matches
+ cdef int encoded
+ cdef int nmatches
+ cdef int _lastindex
+ cdef tuple _groups
+ cdef dict _named_groups
+
+ property lastindex:
+ def __get__(self):
+ return None if self._lastindex < 1 else self._lastindex
+
+ property lastgroup:
+ def __get__(self):
+ if self._lastindex < 1:
+ return None
+ for name, n in self.re.groupindex.items():
+ if n == self._lastindex:
+ return name
+ return None
+
+ def __init__(self, Pattern pattern_object, int num_groups):
+ self._lastindex = -1
+ self._groups = None
+ self.pos = 0
+ self.endpos = -1
+ self.matches = new_StringPiece_array(num_groups + 1)
+ self.nmatches = num_groups
+ self.re = pattern_object
+
+ cdef _init_groups(self):
+ cdef list groups = []
+ cdef int i
+ cdef const char * last_end = NULL
+ cdef const char * cur_end = NULL
+
+ for i in range(self.nmatches):
+ if self.matches[i].data() == NULL:
+ groups.append(None)
+ else:
+ if i > 0:
+ cur_end = self.matches[i].data() + self.matches[i].length()
+
+ if last_end == NULL:
+ last_end = cur_end
+ self._lastindex = i
+ else:
+ # The rules for last group are a bit complicated:
+ # if two groups end at the same point, the earlier one
+ # is considered last, so we don't switch our selection
+ # unless the end point has moved.
+ if cur_end > last_end:
+ last_end = cur_end
+ self._lastindex = i
+ groups.append(
+ self.matches[i].data()[:self.matches[i].length()])
+ self._groups = tuple(groups)
+
+ cdef bytes _group(self, object groupnum):
+ cdef int idx
+ if isinstance(groupnum, int):
+ idx = groupnum
+ if idx > self.nmatches - 1:
+ raise IndexError("no such group %d; available groups: %r"
+ % (idx, list(range(self.nmatches))))
+ return self._groups[idx]
+ groupdict = self._groupdict()
+ if groupnum not in groupdict:
+ raise IndexError("no such group %r; available groups: %r"
+ % (groupnum, list(groupdict)))
+ return groupdict[groupnum]
+
+ cdef dict _groupdict(self):
+ if self._named_groups is None:
+ self._named_groups = {name: self._groups[n]
+ for name, n in self.re.groupindex.items()}
+ return self._named_groups
+
+ def groups(self, default=None):
+ if self.encoded:
+ return tuple([default if g is None else g.decode('utf8')
+ for g in self._groups[1:]])
+ return tuple([default if g is None else g
+ for g in self._groups[1:]])
+
+ def group(self, *args):
+ if len(args) == 0:
+ groupnum = 0
+ elif len(args) == 1:
+ groupnum = args[0]
+ else: # len(args) > 1:
+ return tuple([self.group(i) for i in args])
+ if self.encoded:
+ result = self._group(groupnum)
+ return None if result is None else result.decode('utf8')
+ return self._group(groupnum)
+
+ def groupdict(self):
+ result = self._groupdict()
+ if self.encoded:
+ return {a: None if b is None else b.decode('utf8')
+ for a, b in result.items()}
+ return result
+
+ def expand(self, object template):
+ """Expand a template with groups."""
+ cdef bytearray result = bytearray()
+ if isinstance(template, unicode):
+ if not PY2 and not self.encoded:
+ raise ValueError(
+ 'cannot expand unicode template on bytes pattern')
+ templ = template.encode('utf8')
+ else:
+ if not PY2 and self.encoded:
+ raise ValueError(
+ 'cannot expand bytes template on unicode pattern')
+ templ = bytes(template)
+ self._expand(templ, result)
+ return result.decode('utf8') if self.encoded else bytes(result)
+
+ cdef _expand(self, bytes templ, bytearray result):
+ """Expand template by appending to an existing bytearray.
+ Everything remains UTF-8 encoded."""
+ cdef char * cstring
+ cdef int n = 0, prev = 0, size
+
+ # NB: cstring is used to get single characters, to avoid difference in
+ # Python 2/3 behavior of bytes objects.
+ cstring = templ
+ size = len(templ)
+ while True:
+ prev = n
+ n = templ.find(b'\\', prev)
+ if n == -1:
+ result.extend(templ[prev:])
+ break
+ result.extend(templ[prev:n])
+ n += 1
+ if (n + 2 < size and cstring[n] == b'x'
+ and ishex(cstring[n + 1]) and ishex(cstring[n + 2])):
+ # hex char reference \x1f
+ result.append(int(templ[n + 1:n + 3], base=16) & 255)
+ n += 3
+ elif (n + 2 < size and isoct(cstring[n]) and isoct(cstring[n + 1])
+ and isoct(cstring[n + 2])):
+ # octal char reference \123
+ result.append(int(templ[n:n + 3], base=8) & 255)
+ n += 3
+ elif cstring[n] == b'0':
+ if n + 1 < size and isoct(cstring[n + 1]):
+ # 2 character octal: \01
+ result.append(int(templ[n:n + 2], base=8))
+ n += 2
+ else: # nul-terminator literal \0
+ result.append(b'\0')
+ n += 1
+ elif b'0' <= cstring[n] <= b'9': # numeric group reference
+ if n + 1 < size and isdigit(cstring[n + 1]):
+ # 2 digit group ref \12
+ groupno = int(templ[n:n + 2])
+ n += 2
+ else:
+ # 1 digit group ref \1
+ groupno = int(templ[n:n + 1])
+ n += 1
+ if groupno <= self.re.groups:
+ groupval = self._group(groupno)
+ if groupval is not None:
+ result.extend(groupval)
+ else:
+ raise RegexError('invalid group reference.')
+ elif cstring[n] == b'g': # named group reference
+ n += 1
+ if n >= size or cstring[n] != b'<':
+ raise RegexError('missing group name')
+ n += 1
+ start = n
+ while cstring[n] != b'>':
+ if not isident(cstring[n]):
+ raise RegexError('bad character in group name')
+ n += 1
+ if n >= size:
+ raise RegexError('unterminated group name')
+ if templ[start:n].isdigit():
+ name = int(templ[start:n])
+ elif isdigit(cstring[start]):
+ raise RegexError('bad character in group name')
+ else:
+ name = templ[start:n]
+ if self.encoded:
+ name = name.decode('utf8')
+ groupval = self._group(name)
+ if groupval is not None:
+ result.extend(groupval)
+ n += 1
+ else:
+ if cstring[n] == b'n':
+ result.append(b'\n')
+ elif cstring[n] == b'r':
+ result.append(b'\r')
+ elif cstring[n] == b't':
+ result.append(b'\t')
+ elif cstring[n] == b'v':
+ result.append(b'\v')
+ elif cstring[n] == b'f':
+ result.append(b'\f')
+ elif cstring[n] == b'a':
+ result.append(b'\a')
+ elif cstring[n] == b'b':
+ result.append(b'\b')
+ elif cstring[n] == b'\\':
+ result.append(b'\\')
+ else: # copy verbatim
+ result.append(b'\\')
+ result.append(cstring[n])
+ n += 1
+ return bytes(result)
+
+ def start(self, group=0):
+ return self.span(group)[0]
+
+ def end(self, group=0):
+ return self.span(group)[1]
+
+ def span(self, group=0):
+ if isinstance(group, int):
+ if group > len(self.regs):
+ raise IndexError("no such group %d; available groups: %r"
+ % (group, list(range(len(self.regs)))))
+ return self.regs[group]
+ else:
+ self._groupdict()
+ if group not in self.re.groupindex:
+ raise IndexError("no such group %r; available groups: %r"
+ % (group, list(self.re.groupindex)))
+ return self.regs[self.re.groupindex[group]]
+
+ cdef _make_spans(self, char * cstring, int size, int * cpos, int * upos):
+ cdef int start, end
+ cdef StringPiece * piece
+
+ spans = []
+ for i in range(self.nmatches):
+ if self.matches[i].data() == NULL:
+ spans.append((-1, -1))
+ else:
+ piece = &self.matches[i]
+ if piece.data() == NULL:
+ return (-1, -1)
+ start = piece.data() - cstring
+ end = start + piece.length()
+ spans.append((start, end))
+
+ if self.encoded == 2:
+ spans = self._convert_spans(spans, cstring, size, cpos, upos)
+
+ self.regs = tuple(spans)
+
+ cdef list _convert_spans(self, spans,
+ char * cstring, int size, int * cpos, int * upos):
+ cdef map[int, int] positions
+ cdef int x, y
+ for x, y in spans:
+ positions[x] = x
+ positions[y] = y
+ unicodeindices(positions, cstring, size, cpos, upos)
+ return [(positions[x], positions[y]) for x, y in spans]
+
+ def __dealloc__(self):
+ delete_StringPiece_array(self.matches)
+
+ def __repr__(self):
+ return '<re2.Match object; span=%r, match=%r>' % (
+ self.span(), self.group())
diff --git a/contrib/python/pyre2/py3/src/pattern.pxi b/contrib/python/pyre2/py3/src/pattern.pxi
new file mode 100644
index 0000000000..b8439d2007
--- /dev/null
+++ b/contrib/python/pyre2/py3/src/pattern.pxi
@@ -0,0 +1,650 @@
+cdef class Pattern:
+ cdef readonly object pattern # original pattern in Python format
+ cdef readonly int flags
+ cdef readonly int groups # number of groups
+ cdef readonly dict groupindex # name => group number
+ cdef object __weakref__
+
+ cdef bint encoded # True if this was originally a Unicode pattern
+ cdef RE2 * re_pattern
+
+ def search(self, object string, int pos=0, int endpos=-1):
+ """Scan through string looking for a match, and return a corresponding
+ Match instance. Return None if no position in the string matches."""
+ return self._search(string, pos, endpos, UNANCHORED)
+
+ def match(self, object string, int pos=0, int endpos=-1):
+ """Matches zero or more characters at the beginning of the string."""
+ return self._search(string, pos, endpos, ANCHOR_START)
+
+ def fullmatch(self, object string, int pos=0, int endpos=-1):
+ """"fullmatch(string[, pos[, endpos]]) --> Match object or None."
+
+ Matches the entire string."""
+ return self._search(string, pos, endpos, ANCHOR_BOTH)
+
+ cdef _search(self, object string, int pos, int endpos,
+ re2_Anchor anchoring):
+ """Scan through string looking for a match, and return a corresponding
+ Match instance. Return None if no position in the string matches."""
+ cdef char * cstring
+ cdef Py_ssize_t size
+ cdef Py_buffer buf
+ cdef int retval
+ cdef int encoded = 0
+ cdef StringPiece * sp
+ cdef Match m = Match(self, self.groups + 1)
+ cdef int cpos = 0, upos = pos
+
+ if 0 <= endpos <= pos:
+ return None
+
+ bytestr = unicode_to_bytes(string, &encoded, self.encoded)
+ if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
+ raise TypeError('expected string or buffer')
+ try:
+ if encoded == 2 and (pos or endpos != -1):
+ utf8indices(cstring, size, &pos, &endpos)
+ cpos = pos
+ if pos > size:
+ return None
+ if 0 <= endpos < size:
+ size = endpos
+
+ sp = new StringPiece(cstring, size)
+ with nogil:
+ retval = self.re_pattern.Match(
+ sp[0],
+ pos,
+ size,
+ anchoring,
+ m.matches,
+ self.groups + 1)
+ del sp
+ if retval == 0:
+ return None
+
+ m.encoded = encoded
+ m.nmatches = self.groups + 1
+ m.string = string
+ m.pos = pos
+ if endpos == -1:
+ m.endpos = size
+ else:
+ m.endpos = endpos
+ m._make_spans(cstring, size, &cpos, &upos)
+ m._init_groups()
+ finally:
+ release_cstring(&buf)
+ return m
+
+ def contains(self, object string, int pos=0, int endpos=-1):
+ """"contains(string[, pos[, endpos]]) --> bool."
+
+ Scan through string looking for a match, and return True or False."""
+ cdef char * cstring
+ cdef Py_ssize_t size
+ cdef Py_buffer buf
+ cdef int retval
+ cdef int encoded = 0
+ cdef StringPiece * sp
+
+ if 0 <= endpos <= pos:
+ return False
+
+ bytestr = unicode_to_bytes(string, &encoded, self.encoded)
+ if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
+ raise TypeError('expected string or buffer')
+ try:
+ if encoded == 2 and (pos or endpos != -1):
+ utf8indices(cstring, size, &pos, &endpos)
+ if pos > size:
+ return False
+ if 0 <= endpos < size:
+ size = endpos
+
+ sp = new StringPiece(cstring, size)
+ with nogil:
+ retval = self.re_pattern.Match(
+ sp[0],
+ pos,
+ size,
+ UNANCHORED,
+ NULL,
+ 0)
+ del sp
+ finally:
+ release_cstring(&buf)
+ return retval != 0
+
+ def count(self, object string, int pos=0, int endpos=-1):
+ """Return number of non-overlapping matches of pattern in string."""
+ cdef char * cstring
+ cdef Py_ssize_t size
+ cdef Py_buffer buf
+ cdef int retval
+ cdef int encoded = 0
+ cdef int result = 0
+ cdef StringPiece * sp = NULL
+ cdef StringPiece * matches = NULL
+
+ bytestr = unicode_to_bytes(string, &encoded, self.encoded)
+ if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
+ raise TypeError('expected string or buffer')
+ try:
+ if encoded == 2 and (pos or endpos != -1):
+ utf8indices(cstring, size, &pos, &endpos)
+ if pos > size:
+ return 0
+ if 0 <= endpos < size:
+ size = endpos
+
+ sp = new StringPiece(cstring, size)
+ matches = new_StringPiece_array(1)
+ try:
+ while True:
+ with nogil:
+ retval = self.re_pattern.Match(
+ sp[0],
+ pos,
+ size,
+ UNANCHORED,
+ matches,
+ 1)
+ if retval == 0:
+ break
+ result += 1
+ if pos == size:
+ break
+ # offset the pos to move to the next point
+ pos = matches[0].data() - cstring + (
+ matches[0].length() or 1)
+ finally:
+ del sp
+ delete_StringPiece_array(matches)
+ finally:
+ release_cstring(&buf)
+ return result
+
+ def findall(self, object string, int pos=0, int endpos=-1):
+ """Return all non-overlapping matches of pattern in string as a list
+ of strings."""
+ cdef char * cstring
+ cdef Py_ssize_t size
+ cdef Py_buffer buf
+ cdef int encoded = 0
+ cdef int retval
+ cdef list resultlist = []
+ cdef StringPiece * sp = NULL
+ cdef StringPiece * matches = NULL
+
+ bytestr = unicode_to_bytes(string, &encoded, self.encoded)
+ if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
+ raise TypeError('expected string or buffer')
+ try:
+ if encoded == 2 and (pos or endpos != -1):
+ utf8indices(cstring, size, &pos, &endpos)
+ if pos > size:
+ return []
+ if 0 <= endpos < size:
+ size = endpos
+
+ sp = new StringPiece(cstring, size)
+ matches = new_StringPiece_array(self.groups + 1)
+
+ while True:
+ with nogil:
+ retval = self.re_pattern.Match(
+ sp[0],
+ pos,
+ size,
+ UNANCHORED,
+ matches,
+ self.groups + 1)
+ if retval == 0:
+ break
+ if self.groups > 1:
+ if encoded:
+ resultlist.append(tuple([
+ '' if matches[i].data() is NULL else
+ matches[i].data()[:matches[i].length()
+ ].decode('utf8')
+ for i in range(1, self.groups + 1)]))
+ else:
+ resultlist.append(tuple([
+ b'' if matches[i].data() is NULL
+ else matches[i].data()[:matches[i].length()]
+ for i in range(1, self.groups + 1)]))
+ else: # 0 or 1 group; return list of strings
+ if encoded:
+ resultlist.append(matches[self.groups].data()[
+ :matches[self.groups].length()].decode('utf8'))
+ else:
+ resultlist.append(matches[self.groups].data()[
+ :matches[self.groups].length()])
+ if pos == size:
+ break
+ # offset the pos to move to the next point
+ pos = matches[0].data() - cstring + (matches[0].length() or 1)
+ finally:
+ del sp
+ delete_StringPiece_array(matches)
+ release_cstring(&buf)
+ return resultlist
+
+ def finditer(self, object string, int pos=0, int endpos=-1):
+ """Yield all non-overlapping matches of pattern in string as Match
+ objects."""
+ result = iter(self._finditer(string, pos, endpos))
+ next(result) # dummy value to raise error before start of generator
+ return result
+
+ def _finditer(self, object string, int pos=0, int endpos=-1):
+ cdef char * cstring
+ cdef Py_ssize_t size
+ cdef Py_buffer buf
+ cdef int retval
+ cdef StringPiece * sp = NULL
+ cdef Match m
+ cdef int encoded = 0
+ cdef int cpos = 0, upos = pos
+
+ bytestr = unicode_to_bytes(string, &encoded, self.encoded)
+ if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
+ raise TypeError('expected string or buffer')
+ try:
+ if encoded == 2 and (pos or endpos != -1):
+ utf8indices(cstring, size, &pos, &endpos)
+ cpos = pos
+ if pos > size:
+ return
+ if 0 <= endpos < size:
+ size = endpos
+
+ sp = new StringPiece(cstring, size)
+
+ yield
+ while True:
+ m = Match(self, self.groups + 1)
+ m.string = string
+ with nogil:
+ retval = self.re_pattern.Match(
+ sp[0],
+ pos,
+ size,
+ UNANCHORED,
+ m.matches,
+ self.groups + 1)
+ if retval == 0:
+ break
+ m.encoded = encoded
+ m.nmatches = self.groups + 1
+ m.pos = pos
+ if endpos == -1:
+ m.endpos = size
+ else:
+ m.endpos = endpos
+ m._make_spans(cstring, size, &cpos, &upos)
+ m._init_groups()
+ yield m
+ if pos == size:
+ break
+ # offset the pos to move to the next point
+ pos = m.matches[0].data() - cstring + (
+ m.matches[0].length() or 1)
+ finally:
+ del sp
+ release_cstring(&buf)
+
+ def split(self, string, int maxsplit=0):
+ """split(string[, maxsplit = 0]) --> list
+
+ Split a string by the occurrences of the pattern."""
+ cdef char * cstring
+ cdef Py_ssize_t size
+ cdef int retval
+ cdef int pos = 0
+ cdef int lookahead = 0
+ cdef int num_split = 0
+ cdef StringPiece * sp
+ cdef StringPiece * matches
+ cdef list resultlist = []
+ cdef int encoded = 0
+ cdef Py_buffer buf
+
+ if maxsplit < 0:
+ maxsplit = 0
+
+ bytestr = unicode_to_bytes(string, &encoded, self.encoded)
+ if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
+ raise TypeError('expected string or buffer')
+ matches = new_StringPiece_array(self.groups + 1)
+ sp = new StringPiece(cstring, size)
+ try:
+
+ while True:
+ with nogil:
+ retval = self.re_pattern.Match(
+ sp[0],
+ pos + lookahead,
+ size,
+ UNANCHORED,
+ matches,
+ self.groups + 1)
+ if retval == 0:
+ break
+
+ match_start = matches[0].data() - cstring
+ match_end = match_start + matches[0].length()
+
+ # If an empty match, just look ahead until you find something
+ if match_start == match_end:
+ if pos + lookahead == size:
+ break
+ lookahead += 1
+ continue
+
+ if encoded:
+ resultlist.append(
+ char_to_unicode(&sp.data()[pos], match_start - pos))
+ else:
+ resultlist.append(sp.data()[pos:match_start])
+ if self.groups > 0:
+ for group in range(self.groups):
+ if matches[group + 1].data() == NULL:
+ resultlist.append(None)
+ else:
+ if encoded:
+ resultlist.append(char_to_unicode(
+ matches[group + 1].data(),
+ matches[group + 1].length()))
+ else:
+ resultlist.append(matches[group + 1].data()[:
+ matches[group + 1].length()])
+
+ # offset the pos to move to the next point
+ pos = match_end
+ lookahead = 0
+
+ num_split += 1
+ if maxsplit and num_split >= maxsplit:
+ break
+
+ if encoded:
+ resultlist.append(
+ char_to_unicode(&sp.data()[pos], sp.length() - pos))
+ else:
+ resultlist.append(sp.data()[pos:])
+ finally:
+ del sp
+ delete_StringPiece_array(matches)
+ release_cstring(&buf)
+ return resultlist
+
+ def sub(self, repl, string, int count=0):
+ """sub(repl, string[, count = 0]) --> newstring
+
+ Return the string obtained by replacing the leftmost non-overlapping
+ occurrences of pattern in string by the replacement repl."""
+ cdef int num_repl = 0
+ return self._subn(repl, string, count, &num_repl)
+
+ def subn(self, repl, string, int count=0):
+ """subn(repl, string[, count = 0]) --> (newstring, number of subs)
+
+ Return the tuple (new_string, number_of_subs_made) found by replacing
+ the leftmost non-overlapping occurrences of pattern with the
+ replacement repl."""
+ cdef int num_repl = 0
+ result = self._subn(repl, string, count, &num_repl)
+ return result, num_repl
+
+ cdef _subn(self, repl, string, int count, int *num_repl):
+ cdef bytes repl_b
+ cdef char * cstring
+ cdef object result
+ cdef Py_ssize_t size
+ cdef StringPiece * sp = NULL
+ cdef cpp_string * input_str = NULL
+ cdef int string_encoded = 0
+ cdef int repl_encoded = 0
+
+ if callable(repl):
+ # This is a callback, so use the custom function
+ return self._subn_callback(repl, string, count, num_repl)
+
+ repl_b = unicode_to_bytes(repl, &repl_encoded, self.encoded)
+ if not repl_encoded and not isinstance(repl, bytes):
+ repl_b = bytes(repl) # coerce buffer to bytes object
+
+ if count > 1 or (b'\\' if PY2 else <char>b'\\') in repl_b:
+ # Limit on number of substitutions or replacement string contains
+ # escape sequences; handle with Match.expand() implementation.
+ # RE2 does support simple numeric group references \1, \2,
+ # but the number of differences with Python behavior is
+ # non-trivial.
+ return self._subn_expand(repl_b, string, count, num_repl)
+ try:
+ cstring = repl_b
+ size = len(repl_b)
+ sp = new StringPiece(cstring, size)
+
+ bytestr = unicode_to_bytes(string, &string_encoded, self.encoded)
+ if not string_encoded and not isinstance(bytestr, bytes):
+ bytestr = bytes(bytestr) # coerce buffer to bytes object
+ input_str = new cpp_string(<char *>bytestr, len(bytestr))
+ # NB: RE2 treats unmatched groups in repl as empty string;
+ # Python raises an error.
+ with nogil:
+ if count == 0:
+ num_repl[0] = GlobalReplace(
+ input_str, self.re_pattern[0], sp[0])
+ elif count == 1:
+ num_repl[0] = Replace(
+ input_str, self.re_pattern[0], sp[0])
+
+ if string_encoded or (repl_encoded and num_repl[0] > 0):
+ result = cpp_to_unicode(input_str[0])
+ else:
+ result = cpp_to_bytes(input_str[0])
+ finally:
+ del input_str, sp
+ return result
+
+ cdef _subn_callback(self, callback, string, int count, int * num_repl):
+ # This function is probably the hardest to implement correctly.
+ # This is my first attempt, but if anybody has a better solution,
+ # please help out.
+ cdef char * cstring
+ cdef Py_ssize_t size
+ cdef Py_buffer buf
+ cdef int retval
+ cdef int prevendpos = -1
+ cdef int endpos = 0
+ cdef int pos = 0
+ cdef int encoded = 0
+ cdef StringPiece * sp
+ cdef Match m
+ cdef bytearray result = bytearray()
+ cdef int cpos = 0, upos = 0
+
+ if count < 0:
+ count = 0
+
+ bytestr = unicode_to_bytes(string, &encoded, self.encoded)
+ if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
+ raise TypeError('expected string or buffer')
+ sp = new StringPiece(cstring, size)
+ try:
+ while True:
+ m = Match(self, self.groups + 1)
+ m.string = string
+ with nogil:
+ retval = self.re_pattern.Match(
+ sp[0],
+ pos,
+ size,
+ UNANCHORED,
+ m.matches,
+ self.groups + 1)
+ if retval == 0:
+ break
+
+ endpos = m.matches[0].data() - cstring
+ if endpos == prevendpos:
+ endpos += 1
+ if endpos > size:
+ break
+ prevendpos = endpos
+ result.extend(sp.data()[pos:endpos])
+ pos = endpos + m.matches[0].length()
+
+ m.encoded = encoded
+ m.nmatches = self.groups + 1
+ m._make_spans(cstring, size, &cpos, &upos)
+ m._init_groups()
+ tmp = callback(m)
+ if tmp:
+ result.extend(tmp.encode('utf8') if encoded else tmp)
+ else:
+ result.extend(b'')
+
+ num_repl[0] += 1
+ if count and num_repl[0] >= count:
+ break
+ result.extend(sp.data()[pos:])
+ finally:
+ del sp
+ release_cstring(&buf)
+ return result.decode('utf8') if encoded else bytes(result)
+
+ cdef _subn_expand(self, bytes repl, string, int count, int * num_repl):
+ """Perform ``count`` substitutions with replacement string and
+ Match.expand."""
+ cdef char * cstring
+ cdef Py_ssize_t size
+ cdef Py_buffer buf
+ cdef int retval
+ cdef int prevendpos = -1
+ cdef int endpos = 0
+ cdef int pos = 0
+ cdef int encoded = 0
+ cdef StringPiece * sp
+ cdef Match m
+ cdef bytearray result = bytearray()
+
+ if count < 0:
+ count = 0
+
+ bytestr = unicode_to_bytes(string, &encoded, self.encoded)
+ if pystring_to_cstring(bytestr, &cstring, &size, &buf) == -1:
+ raise TypeError('expected string or buffer')
+ sp = new StringPiece(cstring, size)
+ try:
+ while True:
+ m = Match(self, self.groups + 1)
+ m.string = string
+ with nogil:
+ retval = self.re_pattern.Match(
+ sp[0],
+ pos,
+ size,
+ UNANCHORED,
+ m.matches,
+ self.groups + 1)
+ if retval == 0:
+ break
+
+ endpos = m.matches[0].data() - cstring
+ if endpos == prevendpos:
+ endpos += 1
+ if endpos > size:
+ break
+ prevendpos = endpos
+ result.extend(sp.data()[pos:endpos])
+ pos = endpos + m.matches[0].length()
+
+ m.encoded = encoded
+ m.nmatches = self.groups + 1
+ m._init_groups()
+ m._expand(repl, result)
+
+ num_repl[0] += 1
+ if count and num_repl[0] >= count:
+ break
+ result.extend(sp.data()[pos:])
+ finally:
+ del sp
+ release_cstring(&buf)
+ return result.decode('utf8') if encoded else bytes(result)
+
+ def scanner(self, arg):
+ return re.compile(self.pattern).scanner(arg)
+ # raise NotImplementedError
+
+ def _dump_pattern(self):
+ cdef cpp_string s = self.re_pattern.pattern()
+ if self.encoded:
+ return cpp_to_bytes(s).decode('utf8')
+ return cpp_to_bytes(s)
+
+ def __repr__(self):
+ if self.flags == 0:
+ return 're2.compile(%r)' % self.pattern
+ return 're2.compile(%r, %r)' % (self.pattern, self.flags)
+
+ def __reduce__(self):
+ return (compile, (self.pattern, self.flags))
+
+ def __dealloc__(self):
+ del self.re_pattern
+
+
+class PythonRePattern:
+ """A wrapper for re.Pattern to support the extra methods defined by re2
+ (contains, count)."""
+ def __init__(self, pattern, flags=None):
+ self._pattern = re.compile(pattern, flags)
+ self.pattern = pattern
+ self.flags = flags
+ self.groupindex = self._pattern.groupindex
+ self.groups = self._pattern.groups
+
+ def contains(self, string):
+ return bool(self._pattern.search(string))
+
+ def count(self, string, pos=0, endpos=9223372036854775807):
+ return len(self._pattern.findall(string, pos, endpos))
+
+ def findall(self, string, pos=0, endpos=9223372036854775807):
+ return self._pattern.findall(string, pos, endpos)
+
+ def finditer(self, string, pos=0, endpos=9223372036854775807):
+ return self._pattern.finditer(string, pos, endpos)
+
+ def fullmatch(self, string, pos=0, endpos=9223372036854775807):
+ return self._pattern.fullmatch(string, pos, endpos)
+
+ def match(self, string, pos=0, endpos=9223372036854775807):
+ return self._pattern.match(string, pos, endpos)
+
+ def scanner(self, string, pos=0, endpos=9223372036854775807):
+ return self._pattern.scanner(string, pos, endpos)
+
+ def search(self, string, pos=0, endpos=9223372036854775807):
+ return self._pattern.search(string, pos, endpos)
+
+ def split(self, string, maxsplit=0):
+ return self._pattern.split(string, maxsplit)
+
+ def sub(self, repl, string, count=0):
+ return self._pattern.sub(repl, string, count)
+
+ def subn(self, repl, string, count=0):
+ return self._pattern.subn(repl, string, count)
+
+ def __repr__(self):
+ return repr(self._pattern)
+
+ def __reduce__(self):
+ return (self, (self.pattern, self.flags))
diff --git a/contrib/python/pyre2/py3/src/re2.pyx b/contrib/python/pyre2/py3/src/re2.pyx
new file mode 100644
index 0000000000..c48101426f
--- /dev/null
+++ b/contrib/python/pyre2/py3/src/re2.pyx
@@ -0,0 +1,458 @@
+# cython: infer_types(False)
+r"""Regular expressions using Google's RE2 engine.
+
+Compared to Python's ``re``, the RE2 engine compiles regular expressions to
+deterministic finite automata, which guarantees linear-time behavior.
+
+Intended as a drop-in replacement for ``re``. Unicode is supported by encoding
+to UTF-8, and bytes strings are treated as UTF-8 when the UNICODE flag is given.
+For best performance, work with UTF-8 encoded bytes strings.
+
+Regular expressions that are not compatible with RE2 are processed with
+fallback to ``re``. Examples of features not supported by RE2:
+
+ - lookahead assertions ``(?!...)``
+ - backreferences (``\\n`` in search pattern)
+ - \W and \S not supported inside character classes
+
+On the other hand, unicode character classes are supported (e.g., ``\p{Greek}``).
+Syntax reference: https://github.com/google/re2/wiki/Syntax
+
+What follows is a reference for the regular expression syntax supported by this
+module (i.e., without requiring fallback to `re`).
+
+Regular expressions can contain both special and ordinary characters.
+Most ordinary characters, like "A", "a", or "0", are the simplest
+regular expressions; they simply match themselves.
+
+The special characters are::
+
+ "." Matches any character except a newline.
+ "^" Matches the start of the string.
+ "$" Matches the end of the string or just before the newline at
+ the end of the string.
+ "*" Matches 0 or more (greedy) repetitions of the preceding RE.
+ Greedy means that it will match as many repetitions as possible.
+ "+" Matches 1 or more (greedy) repetitions of the preceding RE.
+ "?" Matches 0 or 1 (greedy) of the preceding RE.
+ *?,+?,?? Non-greedy versions of the previous three special characters.
+ {m,n} Matches from m to n repetitions of the preceding RE.
+ {m,n}? Non-greedy version of the above.
+ "\\" Either escapes special characters or signals a special sequence.
+ [] Indicates a set of characters.
+ A "^" as the first character indicates a complementing set.
+ "|" A|B, creates an RE that will match either A or B.
+ (...) Matches the RE inside the parentheses.
+ The contents can be retrieved or matched later in the string.
+ (?:...) Non-grouping version of regular parentheses.
+ (?imsux) Set the I, M, S, U, or X flag for the RE (see below).
+
+The special sequences consist of "\\" and a character from the list
+below. If the ordinary character is not on the list, then the
+resulting RE will match the second character::
+
+ \A Matches only at the start of the string.
+ \Z Matches only at the end of the string.
+ \b Matches the empty string, but only at the start or end of a word.
+ \B Matches the empty string, but not at the start or end of a word.
+ \d Matches any decimal digit.
+ \D Matches any non-digit character.
+ \s Matches any whitespace character.
+ \S Matches any non-whitespace character.
+ \w Matches any alphanumeric character.
+ \W Matches the complement of \w.
+ \\ Matches a literal backslash.
+ \pN Unicode character class (one-letter name)
+ \p{Greek} Unicode character class
+ \PN negated Unicode character class (one-letter name)
+ \P{Greek} negated Unicode character class
+
+This module exports the following functions::
+
+ count Count all occurrences of a pattern in a string.
+ match Match a regular expression pattern to the beginning of a string.
+ fullmatch Match a regular expression pattern to all of a string.
+ search Search a string for a pattern and return Match object.
+ contains Same as search, but only return bool.
+ sub Substitute occurrences of a pattern found in a string.
+ subn Same as sub, but also return the number of substitutions made.
+ split Split a string by the occurrences of a pattern.
+ findall Find all occurrences of a pattern in a string.
+ finditer Return an iterator yielding a match object for each match.
+ compile Compile a pattern into a RegexObject.
+ purge Clear the regular expression cache.
+ escape Backslash all non-alphanumerics in a string.
+
+Some of the functions in this module takes flags as optional parameters::
+
+ A ASCII Make \w, \W, \b, \B, \d, \D match the corresponding ASCII
+ character categories (rather than the whole Unicode
+ categories, which is the default).
+ I IGNORECASE Perform case-insensitive matching.
+ M MULTILINE "^" matches the beginning of lines (after a newline)
+ as well as the string.
+ "$" matches the end of lines (before a newline) as well
+ as the end of the string.
+ S DOTALL "." matches any character at all, including the newline.
+ X VERBOSE Ignore whitespace and comments for nicer looking RE's.
+ U UNICODE Enable Unicode character classes and make \w, \W, \b, \B,
+ Unicode-aware (default for unicode patterns).
+
+This module also defines an exception 'RegexError' (also available under the
+alias 'error').
+
+"""
+
+include "includes.pxi"
+
+import re
+import sys
+import warnings
+from re import error as RegexError
+
+error = re.error
+
+# Import re flags to be compatible.
+I, M, S, U, X, L = re.I, re.M, re.S, re.U, re.X, re.L
+IGNORECASE = re.IGNORECASE
+MULTILINE = re.MULTILINE
+DOTALL = re.DOTALL
+UNICODE = re.UNICODE
+VERBOSE = re.VERBOSE
+LOCALE = re.LOCALE
+DEBUG = re.DEBUG
+ASCII = 256 # Python 3
+
+FALLBACK_QUIETLY = 0
+FALLBACK_WARNING = 1
+FALLBACK_EXCEPTION = 2
+
+VERSION = (0, 2, 23)
+VERSION_HEX = 0x000217
+
+cdef int _I = I, _M = M, _S = S, _U = U, _X = X, _L = L
+cdef int current_notification = FALLBACK_QUIETLY
+cdef bint PY2 = PY_MAJOR_VERSION == 2
+
+# Type of compiled re object from Python stdlib
+SREPattern = type(re.compile(''))
+
+_cache = {}
+_cache_repl = {}
+
+_MAXCACHE = 100
+
+
+include "compile.pxi"
+include "pattern.pxi"
+include "match.pxi"
+
+
+def purge():
+ """Clear the regular expression caches."""
+ _cache.clear()
+ _cache_repl.clear()
+
+
+def search(pattern, string, int flags=0):
+ """Scan through string looking for a match to the pattern, returning
+ a ``Match`` object or none if no match was found."""
+ return compile(pattern, flags).search(string)
+
+
+def match(pattern, string, int flags=0):
+ """Try to apply the pattern at the start of the string, returning
+ a ``Match`` object, or ``None`` if no match was found."""
+ return compile(pattern, flags).match(string)
+
+
+def fullmatch(pattern, string, int flags=0):
+ """Try to apply the pattern to the entire string, returning
+ a ``Match`` object, or ``None`` if no match was found."""
+ return compile(pattern, flags).fullmatch(string)
+
+
+def contains(pattern, string, int flags=0):
+ """Scan through string looking for a match to the pattern, returning
+ True or False."""
+ return compile(pattern, flags).contains(string)
+
+
+def finditer(pattern, string, int flags=0):
+ """Yield all non-overlapping matches in the string.
+
+ For each match, the iterator returns a ``Match`` object.
+ Empty matches are included in the result."""
+ return compile(pattern, flags).finditer(string)
+
+
+def findall(pattern, string, int flags=0):
+ """Return a list of all non-overlapping matches in the string.
+
+ Each match is represented as a string or a tuple (when there are two ore
+ more groups). Empty matches are included in the result."""
+ return compile(pattern, flags).findall(string)
+
+
+def count(pattern, string, int flags=0):
+ """Return number of non-overlapping matches in the string.
+
+ Empty matches are included in the count."""
+ return compile(pattern, flags).count(string)
+
+
+def split(pattern, string, int maxsplit=0, int flags=0):
+ """Split the source string by the occurrences of the pattern,
+ returning a list containing the resulting substrings."""
+ return compile(pattern, flags).split(string, maxsplit)
+
+
+def sub(pattern, repl, string, int count=0, int flags=0):
+ """Return the string obtained by replacing the leftmost
+ non-overlapping occurrences of the pattern in string by the
+ replacement ``repl``. ``repl`` can be either a string or a callable;
+ if a string, backslash escapes in it are processed. If it is
+ a callable, it's passed the ``Match`` object and must return
+ a replacement string to be used."""
+ return compile(pattern, flags).sub(repl, string, count)
+
+
+def subn(pattern, repl, string, int count=0, int flags=0):
+ """Return a 2-tuple containing ``(new_string, number)``.
+ new_string is the string obtained by replacing the leftmost
+ non-overlapping occurrences of the pattern in the source
+ string by the replacement ``repl``. ``number`` is the number of
+ substitutions that were made. ``repl`` can be either a string or a
+ callable; if a string, backslash escapes in it are processed.
+ If it is a callable, it's passed the ``Match`` object and must
+ return a replacement string to be used."""
+ return compile(pattern, flags).subn(repl, string, count)
+
+
+def escape(pattern):
+ """Escape all non-alphanumeric characters in pattern."""
+ cdef bint uni = isinstance(pattern, unicode)
+ cdef list s
+ if PY2 or uni:
+ s = list(pattern)
+ else:
+ s = [bytes([c]) for c in pattern]
+ for i in range(len(pattern)):
+ # c = pattern[i]
+ c = s[i]
+ if ord(c) < 0x80 and not c.isalnum():
+ if uni:
+ if c == u'\000':
+ s[i] = u'\\000'
+ else:
+ s[i] = u"\\" + c
+ else:
+ if c == b'\000':
+ s[i] = b'\\000'
+ else:
+ s[i] = b'\\' + c
+ return u''.join(s) if uni else b''.join(s)
+
+
+class BackreferencesException(Exception):
+ """Search pattern contains backreferences."""
+ pass
+
+
+class CharClassProblemException(Exception):
+ """Search pattern contains unsupported character class."""
+ pass
+
+
+def set_fallback_notification(level):
+ """Set the fallback notification to a level; one of:
+ FALLBACK_QUIETLY
+ FALLBACK_WARNING
+ FALLBACK_EXCEPTION
+ """
+ global current_notification
+ level = int(level)
+ if level < 0 or level > 2:
+ raise ValueError("This function expects a valid notification level.")
+ current_notification = level
+
+
+cdef bint ishex(unsigned char c):
+ """Test whether ``c`` is in ``[0-9a-fA-F]``"""
+ return (b'0' <= c <= b'9' or b'a' <= c <= b'f' or b'A' <= c <= b'F')
+
+
+cdef bint isoct(unsigned char c):
+ """Test whether ``c`` is in ``[0-7]``"""
+ return b'0' <= c <= b'7'
+
+
+cdef bint isdigit(unsigned char c):
+ """Test whether ``c`` is in ``[0-9]``"""
+ return b'0' <= c <= b'9'
+
+
+cdef bint isident(unsigned char c):
+ """Test whether ``c`` is in ``[a-zA-Z0-9_]``"""
+ return (b'a' <= c <= b'z' or b'A' <= c <= b'Z'
+ or b'0' <= c <= b'9' or c == b'_')
+
+
+cdef inline bytes cpp_to_bytes(cpp_string input):
+ """Convert from a std::string object to a python string."""
+ # By taking the slice we go to the right size,
+ # despite spurious or missing null characters.
+ return input.data()[:input.length()]
+
+
+cdef inline unicode cpp_to_unicode(cpp_string input):
+ """Convert a std::string object to a unicode string."""
+ return cpython.unicode.PyUnicode_DecodeUTF8(
+ input.data(), input.length(), 'strict')
+
+
+cdef inline unicode char_to_unicode(const char * input, int length):
+ """Convert a C string to a unicode string."""
+ return cpython.unicode.PyUnicode_DecodeUTF8(input, length, 'strict')
+
+
+cdef inline unicode_to_bytes(object pystring, int * encoded,
+ int checkotherencoding):
+ """Convert a unicode string to a utf8 bytes object, if necessary.
+
+ If pystring is a bytes string or a buffer, return unchanged.
+ If checkotherencoding is 0 or 1 and using Python 3, raise an error
+ if its truth value is not equal to that of encoded.
+ encoded is set to 1 if encoded string can be treated as ASCII,
+ and 2 if it contains multibyte unicode characters."""
+ if cpython.unicode.PyUnicode_Check(pystring):
+ origlen = len(pystring)
+ pystring = pystring.encode('utf8')
+ encoded[0] = 1 if origlen == len(pystring) else 2
+ else:
+ encoded[0] = 0
+ if not PY2 and checkotherencoding > 0 and not encoded[0]:
+ raise TypeError("can't use a string pattern on a bytes-like object")
+ elif not PY2 and checkotherencoding == 0 and encoded[0]:
+ raise TypeError("can't use a bytes pattern on a string-like object")
+ return pystring
+
+
+cdef inline int pystring_to_cstring(
+ object pystring, char ** cstring, Py_ssize_t * size,
+ Py_buffer * buf):
+ """Get a pointer from bytes/buffer object ``pystring``.
+
+ On success, return 0, and set ``cstring``, ``size``, and ``buf``."""
+ cdef int result = -1
+ cstring[0] = NULL
+ size[0] = 0
+ if PyObject_CheckBuffer(pystring) == 1: # new-style Buffer interface
+ result = PyObject_GetBuffer(pystring, buf, PyBUF_SIMPLE)
+ if result == 0:
+ cstring[0] = <char *>buf.buf
+ size[0] = buf.len
+ return result
+
+
+cdef inline void release_cstring(Py_buffer *buf):
+ """Release buffer if necessary."""
+ if not PY2:
+ PyBuffer_Release(buf)
+
+
+cdef utf8indices(char * cstring, int size, int *pos, int *endpos):
+ """Convert unicode indices ``pos`` and ``endpos`` to UTF-8 indices.
+
+ If the indices are out of range, leave them unchanged."""
+ cdef unsigned char * data = <unsigned char *>cstring
+ cdef int newpos = pos[0], newendpos = -1
+ cdef int cpos = 0, upos = 0
+ while cpos < size:
+ if data[cpos] < 0x80:
+ cpos += 1
+ upos += 1
+ elif data[cpos] < 0xe0:
+ cpos += 2
+ upos += 1
+ elif data[cpos] < 0xf0:
+ cpos += 3
+ upos += 1
+ else:
+ cpos += 4
+ upos += 1
+ # wide unicode chars get 2 unichars when Python <3.3 is compiled
+ # with --enable-unicode=ucs2
+ emit_if_narrow_unicode()
+ upos += 1
+ emit_endif()
+
+ if upos == pos[0]:
+ newpos = cpos
+ if endpos[0] == -1:
+ break
+ elif upos == endpos[0]:
+ newendpos = cpos
+ break
+ pos[0] = newpos
+ endpos[0] = newendpos
+
+
+cdef void unicodeindices(map[int, int] &positions,
+ char * cstring, int size, int * cpos, int * upos):
+ """Convert UTF-8 byte indices to unicode indices."""
+ cdef unsigned char * s = <unsigned char *>cstring
+ cdef map[int, int].iterator it = positions.begin()
+
+ if dereference(it).first == -1:
+ dereference(it).second = -1
+ postincrement(it)
+ if it == positions.end():
+ return
+ if dereference(it).first == cpos[0]:
+ dereference(it).second = upos[0]
+ postincrement(it)
+ if it == positions.end():
+ return
+
+ while cpos[0] < size:
+ if s[cpos[0]] < 0x80:
+ cpos[0] += 1
+ upos[0] += 1
+ elif s[cpos[0]] < 0xe0:
+ cpos[0] += 2
+ upos[0] += 1
+ elif s[cpos[0]] < 0xf0:
+ cpos[0] += 3
+ upos[0] += 1
+ else:
+ cpos[0] += 4
+ upos[0] += 1
+ # wide unicode chars get 2 unichars when Python <3.3 is compiled
+ # with --enable-unicode=ucs2
+ emit_if_narrow_unicode()
+ upos[0] += 1
+ emit_endif()
+
+ if dereference(it).first == cpos[0]:
+ dereference(it).second = upos[0]
+ postincrement(it)
+ if it == positions.end():
+ break
+
+
+__all__ = [
+ # exceptions
+ 'BackreferencesException', 'CharClassProblemException',
+ 'RegexError', 'error',
+ # constants
+ 'FALLBACK_EXCEPTION', 'FALLBACK_QUIETLY', 'FALLBACK_WARNING', 'DEBUG',
+ 'S', 'DOTALL', 'I', 'IGNORECASE', 'L', 'LOCALE', 'M', 'MULTILINE',
+ 'U', 'UNICODE', 'X', 'VERBOSE', 'VERSION', 'VERSION_HEX',
+ # classes
+ 'Match', 'Pattern', 'SREPattern',
+ # functions
+ 'compile', 'count', 'escape', 'findall', 'finditer', 'fullmatch',
+ 'match', 'purge', 'search', 'split', 'sub', 'subn',
+ 'set_fallback_notification',
+ ]
diff --git a/contrib/python/pyre2/py3/tests/test_charliterals.txt b/contrib/python/pyre2/py3/tests/test_charliterals.txt
new file mode 100644
index 0000000000..2eaea128a3
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_charliterals.txt
@@ -0,0 +1,47 @@
+ >>> import re2 as re
+ >>> import warnings
+ >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+character literals:
+
+ >>> i = 126
+ >>> re.compile(r"\%03o" % i)
+ re2.compile('\\176')
+ >>> re.compile(r"\%03o" % i)._dump_pattern()
+ '\\176'
+ >>> re.match(r"\%03o" % i, chr(i)) is None
+ False
+ >>> re.match(r"\%03o0" % i, chr(i) + "0") is None
+ False
+ >>> re.match(r"\%03o8" % i, chr(i) + "8") is None
+ False
+ >>> re.match(r"\x%02x" % i, chr(i)) is None
+ False
+ >>> re.match(r"\x%02x0" % i, chr(i) + "0") is None
+ False
+ >>> re.match(r"\x%02xz" % i, chr(i) + "z") is None
+ False
+ >>> re.match("\911", "") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ re.error: invalid escape sequence: \9
+
+character class literals:
+
+ >>> re.match(r"[\%03o]" % i, chr(i)) is None
+ False
+ >>> re.match(r"[\%03o0]" % i, chr(i) + "0") is None
+ False
+ >>> re.match(r"[\%03o8]" % i, chr(i) + "8") is None
+ False
+ >>> re.match(r"[\x%02x]" % i, chr(i)) is None
+ False
+ >>> re.match(r"[\x%02x0]" % i, chr(i) + "0") is None
+ False
+ >>> re.match(r"[\x%02xz]" % i, chr(i) + "z") is None
+ False
+ >>> re.match("[\911]", "") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ re.error: invalid escape sequence: \9
+
diff --git a/contrib/python/pyre2/py3/tests/test_count.txt b/contrib/python/pyre2/py3/tests/test_count.txt
new file mode 100644
index 0000000000..ce3525adc5
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_count.txt
@@ -0,0 +1,40 @@
+count tests
+===========
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+This one is from http://docs.python.org/library/re.html?#finding-all-adverbs:
+
+ >>> re2.count(r"\w+ly", "He was carefully disguised but captured quickly by police.")
+ 2
+
+Groups should not affect count():
+
+ >>> re2.count(r"(\w+)=(\d+)", "foo=1,foo=2")
+ 2
+ >>> re2.count(r"(\w)\w", "fx")
+ 1
+
+Zero matches:
+
+ >>> re2.count("(f)", "gggg")
+ 0
+
+A pattern matching an empty string:
+
+ >>> re2.count(".*", "foo")
+ 2
+
+ >>> re2.count("", "foo")
+ 4
+
+contains tests
+==============
+
+ >>> re2.contains('a', 'bbabb')
+ True
+ >>> re2.contains('a', 'bbbbb')
+ False
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_emptygroups.txt b/contrib/python/pyre2/py3/tests/test_emptygroups.txt
new file mode 100644
index 0000000000..424c8ba25e
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_emptygroups.txt
@@ -0,0 +1,36 @@
+Empty/unused groups
+===================
+
+ >>> import re
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+Unused vs. empty group:
+
+ >>> re.search( '(foo)?((.*).)(bar)?', 'a').groups()
+ (None, 'a', '', None)
+ >>> re2.search('(foo)?((.*).)(bar)?', 'a').groups()
+ (None, 'a', '', None)
+
+ >>> re.search(r'((.*)?.)', 'a').groups()
+ ('a', '')
+ >>> re2.search(r'((.*)?.)', 'a').groups()
+ ('a', '')
+ >>> re.search(r'((.*)+.)', 'a').groups()
+ ('a', '')
+ >>> re2.search(r'((.*)+.)', 'a').groups()
+ ('a', '')
+
+The following show different behavior for re and re2:
+
+ >>> re.search(r'((.*)*.)', 'a').groups()
+ ('a', '')
+ >>> re2.search(r'((.*)*.)', 'a').groups()
+ ('a', None)
+
+ >>> re.search(r'((.*)*.)', 'Hello').groups()
+ ('Hello', '')
+ >>> re2.search(r'((.*)*.)', 'Hello').groups()
+ ('Hello', 'Hell')
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_findall.txt b/contrib/python/pyre2/py3/tests/test_findall.txt
new file mode 100644
index 0000000000..c753b936df
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_findall.txt
@@ -0,0 +1,42 @@
+findall tests
+=============
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+This one is from http://docs.python.org/library/re.html?#finding-all-adverbs:
+
+ >>> re2.findall(r"\w+ly", "He was carefully disguised but captured quickly by police.")
+ ['carefully', 'quickly']
+
+This one makes sure all groups are found:
+
+ >>> re2.findall(r"(\w+)=(\d+)", "foo=1,foo=2")
+ [('foo', '1'), ('foo', '2')]
+
+When there's only one matched group, it should not be returned in a tuple:
+
+ >>> re2.findall(r"(\w)\w", "fx")
+ ['f']
+
+Zero matches is an empty list:
+
+ >>> re2.findall("(f)", "gggg")
+ []
+
+If pattern matches an empty string, do it only once at the end:
+
+ >>> re2.findall(".*", "foo")
+ ['foo', '']
+
+ >>> re2.findall("", "foo")
+ ['', '', '', '']
+
+
+ >>> import re
+ >>> re.findall(r'\b', 'The quick brown fox jumped over the lazy dog')
+ ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
+ >>> re2.findall(r'\b', 'The quick brown fox jumped over the lazy dog')
+ ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_finditer.txt b/contrib/python/pyre2/py3/tests/test_finditer.txt
new file mode 100644
index 0000000000..3d60d199c7
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_finditer.txt
@@ -0,0 +1,28 @@
+Simple tests for the ``finditer`` function.
+===========================================
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+ >>> with open('tests/cnn_homepage.dat') as tmp:
+ ... data = tmp.read()
+ >>> len(list(re2.finditer(r'\w+', data)))
+ 14230
+
+ >>> [m.group(1) for m in re2.finditer(r'\n#hdr-editions(.*?)\n', data)]
+ [' a { text-decoration:none; }', ' li { padding:0 10px; }', ' ul li.no-pad-left span { font-size:12px; }']
+
+ >>> [m.group(1) for m in re2.finditer(r'^#hdr-editions(.*?)$',
+ ... data, re2.M)]
+ [' a { text-decoration:none; }', ' li { padding:0 10px; }', ' ul li.no-pad-left span { font-size:12px; }']
+
+ >>> for a in re2.finditer(r'\b', 'foo bar zed'): print(a)
+ <re2.Match object; span=(0, 0), match=''>
+ <re2.Match object; span=(3, 3), match=''>
+ <re2.Match object; span=(4, 4), match=''>
+ <re2.Match object; span=(7, 7), match=''>
+ <re2.Match object; span=(8, 8), match=''>
+ <re2.Match object; span=(11, 11), match=''>
+
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_match_expand.txt b/contrib/python/pyre2/py3/tests/test_match_expand.txt
new file mode 100644
index 0000000000..b3d5652c76
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_match_expand.txt
@@ -0,0 +1,29 @@
+Match Expand Tests
+==================
+
+Match objects have an .expand() method which allows them to
+expand templates as if the .sub() method was called on the pattern.
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+ >>> m = re2.match("(\\w+) (\\w+)\\W+(?P<title>\\w+)", "Isaac Newton, physicist")
+ >>> m.expand("\\2, \\1")
+ 'Newton, Isaac'
+ >>> m.expand("\\1 \\g<title>")
+ 'Isaac physicist'
+ >>> m.expand("\\2, \\1 \\2")
+ 'Newton, Isaac Newton'
+ >>> m.expand("\\3")
+ 'physicist'
+ >>> m.expand("\\1 \\g<foo>") # doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ IndexError: no such group 'foo'; available groups: ['title']
+ >>> m.expand("\\0")
+ '\x00'
+ >>> m.expand("\01")
+ '\x01'
+ >>> m.expand('\t\n\x0b\r\x0c\x07\x08\\B\\Z\x07\\A\\w\\W\\s\\S\\d\\D')
+ '\t\n\x0b\r\x0c\x07\x08\\B\\Z\x07\\A\\w\\W\\s\\S\\d\\D'
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_mmap.txt b/contrib/python/pyre2/py3/tests/test_mmap.txt
new file mode 100644
index 0000000000..12ffa97498
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_mmap.txt
@@ -0,0 +1,18 @@
+
+Testing re2 on buffer object
+============================
+
+ >>> import re2
+ >>> import mmap
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+ >>> tmp = open("tests/cnn_homepage.dat", "rb+")
+ >>> data = mmap.mmap(tmp.fileno(), 0)
+
+ >>> len(list(re2.finditer(b'\\w+', data)))
+ 14230
+
+ >>> data.close()
+ >>> tmp.close()
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_namedgroups.txt b/contrib/python/pyre2/py3/tests/test_namedgroups.txt
new file mode 100644
index 0000000000..70f561a39f
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_namedgroups.txt
@@ -0,0 +1,56 @@
+Testing some aspects of named groups
+=================================================
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+ >>> m = re2.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
+ >>> m.start("first_name")
+ 0
+ >>> m.start("last_name")
+ 8
+
+ >>> m.span("last_name")
+ (8, 16)
+ >>> m.regs
+ ((0, 16), (0, 7), (8, 16))
+
+ >>> m = re2.match(u"(?P<first_name>\\w+) (?P<last_name>\\w+)", u"Malcolm Reynolds")
+ >>> m.start(u"first_name")
+ 0
+ >>> m.start(u"last_name")
+ 8
+
+ >>> m.span(u"last_name")
+ (8, 16)
+ >>> m.regs
+ ((0, 16), (0, 7), (8, 16))
+
+Compare patterns with and without unicode
+
+ >>> pattern = re2.compile(br"(?P<first_name>\w+) (?P<last_name>\w+)")
+ >>> print(pattern._dump_pattern().decode('utf8'))
+ (?P<first_name>\w+) (?P<last_name>\w+)
+ >>> pattern = re2.compile(u"(?P<first_name>\\w+) (?P<last_name>\\w+)",
+ ... re2.UNICODE)
+ >>> print(pattern._dump_pattern())
+ (?P<first_name>[_\p{L}\p{Nd}]+) (?P<last_name>[_\p{L}\p{Nd}]+)
+
+Make sure positions are converted properly for unicode
+
+ >>> m = pattern.match(
+ ... u'\u05d9\u05e9\u05e8\u05d0\u05dc \u05e6\u05d3\u05d5\u05e7')
+ >>> m.start(u"first_name")
+ 0
+ >>> m.start(u"last_name")
+ 6
+ >>> m.end(u"last_name")
+ 10
+ >>> m.regs
+ ((0, 10), (0, 5), (6, 10))
+ >>> m.span(2)
+ (6, 10)
+ >>> m.span(u"last_name")
+ (6, 10)
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_pattern.txt b/contrib/python/pyre2/py3/tests/test_pattern.txt
new file mode 100644
index 0000000000..aab47359a2
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_pattern.txt
@@ -0,0 +1,12 @@
+pattern tests
+=============
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+
+We should be able to get back what we put in.
+
+ >>> re2.compile("(foo|b[a]r?)").pattern
+ '(foo|b[a]r?)'
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_search.txt b/contrib/python/pyre2/py3/tests/test_search.txt
new file mode 100644
index 0000000000..9c1e18f08c
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_search.txt
@@ -0,0 +1,29 @@
+These are simple tests of the ``search`` function
+=================================================
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+ >>> import warnings
+ >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+ >>> re2.search("((?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])", "hello 28.224.2.1 test").group()
+ '28.224.2.1'
+
+ >>> re2.search("(\d{3})\D?(\d{3})\D?(\d{4})", "800-555-1212").groups()
+ ('800', '555', '1212')
+
+ >>> input = 'a' * 999
+ >>> len(re2.search('(?:a{1000})?a{999}', input).group())
+ 999
+
+ >>> with open('tests/cnn_homepage.dat') as tmp:
+ ... data = tmp.read()
+ >>> re2.search(r'\n#hdr-editions(.*?)\n', data).groups()
+ (' a { text-decoration:none; }',)
+
+Verify some sanity checks
+
+ >>> re2.compile(r'x').search('x', 2000)
+ >>> re2.compile(r'x').search('x', 1, -300)
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_split.txt b/contrib/python/pyre2/py3/tests/test_split.txt
new file mode 100644
index 0000000000..a3e44bc605
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_split.txt
@@ -0,0 +1,17 @@
+Split tests
+===========
+
+This one tests to make sure that unicode / utf8 data is parsed correctly.
+
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+ >>> a = u'\u6211\u5f88\u597d, \u4f60\u5462?'
+
+ >>> re2.split(u' ', a) == [u'\u6211\u5f88\u597d,', u'\u4f60\u5462?']
+ True
+ >>> re2.split(b' ', a.encode('utf8')) == [
+ ... b'\xe6\x88\x91\xe5\xbe\x88\xe5\xa5\xbd,',
+ ... b'\xe4\xbd\xa0\xe5\x91\xa2?']
+ True
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/tests/test_sub.txt b/contrib/python/pyre2/py3/tests/test_sub.txt
new file mode 100644
index 0000000000..b41dd30d28
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_sub.txt
@@ -0,0 +1,31 @@
+Tests of substitution
+=====================
+
+This first test is just looking to replace things between parentheses
+with an empty string.
+
+
+ >>> import hashlib
+ >>> import gzip
+ >>> import re2
+ >>> re2.set_fallback_notification(re2.FALLBACK_EXCEPTION)
+ >>> import warnings
+ >>> warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+ >>> with gzip.open('tests/wikipages.xml.gz', 'rb') as tmp:
+ ... data = tmp.read()
+ >>> print(hashlib.md5(re2.sub(b'\(.*?\)', b'', data)).hexdigest())
+ b7a469f55ab76cd5887c81dbb0cfe6d3
+
+ >>> re2.set_fallback_notification(re2.FALLBACK_QUIETLY)
+
+Issue #26 re2.sub replacements with a match of "(.*)" hangs forever
+
+ >>> re2.sub('(.*)', r'\1;replacement', 'original')
+ 'original;replacement;replacement'
+
+ >>> re2.sub('(.*)', lambda x: x.group() + ';replacement', 'original')
+ 'original;replacement;replacement'
+
+ >>> re2.subn("b*", lambda x: "X", "xyz", 4)
+ ('XxXyXzX', 4)
diff --git a/contrib/python/pyre2/py3/tests/test_unicode.txt b/contrib/python/pyre2/py3/tests/test_unicode.txt
new file mode 100644
index 0000000000..71d497b80d
--- /dev/null
+++ b/contrib/python/pyre2/py3/tests/test_unicode.txt
@@ -0,0 +1,71 @@
+Here are some tests to make sure that utf-8 works
+=================================================
+
+ >>> import sys
+ >>> import re2 as re
+ >>> re.set_fallback_notification(re.FALLBACK_EXCEPTION)
+ >>> a = u'\u6211\u5f88\u597d'
+ >>> c = re.compile(a[0])
+ >>> c.search(a).group() == u'\u6211'
+ True
+
+Test unicode stickyness
+
+ >>> re.sub(u'x', u'y', u'x') == u'y'
+ True
+ >>> re.sub(r'x', 'y', 'x') == 'y'
+ True
+ >>> re.findall('.', 'x') == ['x']
+ True
+ >>> re.findall(u'.', u'x') == [u'x']
+ True
+ >>> re.split(',', '1,2,3') == ['1', '2', '3']
+ True
+ >>> re.split(u',', u'1,2,3') == [u'1', u'2', u'3']
+ True
+ >>> re.search('(\\d)', '1').group(1) == '1'
+ True
+ >>> re.search(u'(\\d)', u'1').group(1) == u'1'
+ True
+
+Test unicode character groups
+
+ >>> re.search(u'\\d', u'\u0661', re.UNICODE).group(0) == u'\u0661'
+ True
+ >>> int(re.search(u'\\d', u'\u0661', re.UNICODE).group(0)) == 1
+ True
+ >>> (re.search(u'\\w', u'\u0401') is None) == (sys.version_info[0] == 2)
+ True
+ >>> re.search(u'\\w', u'\u0401', re.UNICODE).group(0) == u'\u0401'
+ True
+ >>> re.search(u'\\s', u'\u1680', re.UNICODE).group(0) == u'\u1680'
+ True
+ >>> re.findall(r'[\s\d\w]', 'hey 123', re.UNICODE) == ['h', 'e', 'y', ' ', '1', '2', '3']
+ True
+ >>> re.search(u'\\D', u'\u0661x', re.UNICODE).group(0) == u'x'
+ True
+ >>> re.search(u'\\W', u'\u0401!', re.UNICODE).group(0) == u'!'
+ True
+ >>> re.search(u'\\S', u'\u1680x', re.UNICODE).group(0) == u'x'
+ True
+ >>> re.set_fallback_notification(re.FALLBACK_QUIETLY)
+ >>> re.search(u'[\\W]', u'\u0401!', re.UNICODE).group(0) == u'!'
+ True
+ >>> re.search(u'[\\S]', u'\u1680x', re.UNICODE).group(0) == u'x'
+ True
+ >>> re.set_fallback_notification(re.FALLBACK_EXCEPTION)
+
+
+Positions are translated transparently between unicode and UTF-8
+
+ >>> re.search(u' (.)', u'\U0001d200xxx\u1234 x').span(1)
+ (6, 7)
+ >>> re.search(b' (.)', u'\U0001d200xxx\u1234 x'.encode('utf-8')).span(1)
+ (11, 12)
+ >>> re.compile(u'x').findall(u'\u1234x', 1, 2) == [u'x']
+ True
+ >>> data = u'\U0001d200xxx\u1234 x'
+ >>> re.search(u' (.)', data).string == data
+ True
+
+ >>> re.set_fallback_notification(re.FALLBACK_QUIETLY)
diff --git a/contrib/python/pyre2/py3/ya.make b/contrib/python/pyre2/py3/ya.make
new file mode 100644
index 0000000000..920808ff57
--- /dev/null
+++ b/contrib/python/pyre2/py3/ya.make
@@ -0,0 +1,39 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(0.3.6)
+
+LICENSE(BSD-3-Clause)
+
+PEERDIR(
+ contrib/libs/re2
+)
+
+ADDINCL(
+ contrib/python/pyre2/py3/src
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_LINT()
+
+SRCDIR(contrib/python/pyre2/py3/src)
+
+PY_SRCS(
+ TOP_LEVEL
+ CYTHON_CPP
+ re2.pyx
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/pyre2/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/pyre2/ya.make b/contrib/python/pyre2/ya.make
new file mode 100644
index 0000000000..52c15dd0c8
--- /dev/null
+++ b/contrib/python/pyre2/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/pyre2/py2)
+ELSE()
+ PEERDIR(contrib/python/pyre2/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/python-libarchive/py2/libarchive/__init__.py b/contrib/python/python-libarchive/py2/libarchive/__init__.py
new file mode 100644
index 0000000000..0c0c63359a
--- /dev/null
+++ b/contrib/python/python-libarchive/py2/libarchive/__init__.py
@@ -0,0 +1,800 @@
+# Copyright (c) 2011, SmartFile <btimby@smartfile.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * 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.
+# * Neither the name of the organization nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+
+import os
+import stat
+import sys
+import math
+import time
+import logging
+import warnings
+
+import contextlib2
+
+from libarchive import _libarchive
+import six
+
+logger = logging.getLogger(__name__)
+
+# Suggested block size for libarchive. Libarchive may adjust it.
+BLOCK_SIZE = 10240
+
+MTIME_FORMAT = ''
+
+# Default encoding scheme.
+ENCODING = 'utf-8'
+
+if six.PY2:
+ def encode(value, encoding):
+ if type(value) == str:
+ value = value.decode(encoding, errors='ignore')
+ return value.encode(encoding)
+else:
+ def encode(value, encoding):
+ return value.encode(encoding)
+
+
+# Functions to initialize read/write for various libarchive supported formats and filters.
+FORMATS = {
+ None: (_libarchive.archive_read_support_format_all, None),
+ 'tar': (_libarchive.archive_read_support_format_tar, _libarchive.archive_write_set_format_ustar),
+ 'pax': (_libarchive.archive_read_support_format_tar, _libarchive.archive_write_set_format_pax),
+ 'gnu': (_libarchive.archive_read_support_format_gnutar, _libarchive.archive_write_set_format_gnutar),
+ 'zip': (_libarchive.archive_read_support_format_zip, _libarchive.archive_write_set_format_zip),
+ 'rar': (_libarchive.archive_read_support_format_rar, None),
+ '7zip': (_libarchive.archive_read_support_format_7zip, None),
+ 'ar': (_libarchive.archive_read_support_format_ar, None),
+ 'cab': (_libarchive.archive_read_support_format_cab, None),
+ 'cpio': (_libarchive.archive_read_support_format_cpio, _libarchive.archive_write_set_format_cpio_newc),
+ 'iso': (_libarchive.archive_read_support_format_iso9660, _libarchive.archive_write_set_format_iso9660),
+ 'lha': (_libarchive.archive_read_support_format_lha, None),
+ 'xar': (_libarchive.archive_read_support_format_xar, _libarchive.archive_write_set_format_xar),
+}
+
+FILTERS = {
+ None: (_libarchive.archive_read_support_filter_all, _libarchive.archive_write_add_filter_none),
+ 'bzip2': (_libarchive.archive_read_support_filter_bzip2, _libarchive.archive_write_add_filter_bzip2),
+ 'gzip': (_libarchive.archive_read_support_filter_gzip, _libarchive.archive_write_add_filter_gzip),
+ 'zstd': (_libarchive.archive_read_support_filter_zstd, _libarchive.archive_write_add_filter_zstd),
+}
+
+# Map file extensions to formats and filters. To support quick detection.
+FORMAT_EXTENSIONS = {
+ '.tar': 'tar',
+ '.zip': 'zip',
+ '.rar': 'rar',
+ '.7z': '7zip',
+ '.ar': 'ar',
+ '.cab': 'cab',
+ '.rpm': 'cpio',
+ '.cpio': 'cpio',
+ '.iso': 'iso',
+ '.lha': 'lha',
+ '.xar': 'xar',
+}
+FILTER_EXTENSIONS = {
+ '.bz2': 'bzip2',
+ '.gz': 'gzip',
+ '.zst': 'zstd',
+}
+
+
+class EOF(Exception):
+ '''Raised by ArchiveInfo.from_archive() when unable to read the next
+ archive header.'''
+ pass
+
+
+def get_error(archive):
+ '''Retrieves the last error description for the given archive instance.'''
+ return _libarchive.archive_error_string(archive)
+
+
+def call_and_check(func, archive, *args):
+ '''Executes a libarchive function and raises an exception when appropriate.'''
+ ret = func(*args)
+ if ret == _libarchive.ARCHIVE_OK:
+ return
+ elif ret == _libarchive.ARCHIVE_WARN:
+ warnings.warn('Warning executing function: %s.' % get_error(archive), RuntimeWarning)
+ elif ret == _libarchive.ARCHIVE_EOF:
+ raise EOF()
+ else:
+ raise Exception('Fatal error executing function, message is: %s.' % get_error(archive))
+
+
+def get_func(name, items, index):
+ item = items.get(name, None)
+ if item is None:
+ return None
+ return item[index]
+
+
+def guess_format(filename):
+ filename, ext = os.path.splitext(filename)
+ filter = FILTER_EXTENSIONS.get(ext)
+ if filter:
+ filename, ext = os.path.splitext(filename)
+ format = FORMAT_EXTENSIONS.get(ext)
+ return format, filter
+
+
+def is_archive_name(filename, formats=None):
+ '''Quick check to see if the given file has an extension indiciating that it is
+ an archive. The format parameter can be used to limit what archive format is acceptable.
+ If omitted, all supported archive formats will be checked.
+
+ This function will return the name of the most likely archive format, None if the file is
+ unlikely to be an archive.'''
+ if formats is None:
+ formats = FORMAT_EXTENSIONS.values()
+ format, filter = guess_format(filename)
+ if format in formats:
+ return format
+
+
+def is_archive(f, formats=(None, ), filters=(None, )):
+ '''Check to see if the given file is actually an archive. The format parameter
+ can be used to specify which archive format is acceptable. If ommitted, all supported
+ archive formats will be checked. It opens the file using libarchive. If no error is
+ received, the file was successfully detected by the libarchive bidding process.
+
+ This procedure is quite costly, so you should avoid calling it unless you are reasonably
+ sure that the given file is an archive. In other words, you may wish to filter large
+ numbers of file names using is_archive_name() before double-checking the positives with
+ this function.
+
+ This function will return True if the file can be opened as an archive using the given
+ format(s)/filter(s).'''
+ with contextlib2.ExitStack() as exit_stack:
+ if isinstance(f, six.string_types):
+ f = exit_stack.enter_context(open(f, 'rb'))
+ a = _libarchive.archive_read_new()
+ for format in formats:
+ format = get_func(format, FORMATS, 0)
+ if format is None:
+ return False
+ format(a)
+ for filter in filters:
+ filter = get_func(filter, FILTERS, 0)
+ if filter is None:
+ return False
+ filter(a)
+ try:
+ try:
+ call_and_check(_libarchive.archive_read_open_fd, a, a, f.fileno(), BLOCK_SIZE)
+ return True
+ except:
+ return False
+ finally:
+ _libarchive.archive_read_close(a)
+ _libarchive.archive_read_free(a)
+
+
+def get_archive_filter_names(filename):
+ with open(filename, 'rb') as afile:
+ a = _libarchive.archive_read_new()
+ try:
+ format_func = get_func(None, FORMATS, 0)
+ format_func(a)
+ filter_func = get_func(None, FILTERS, 0)
+ filter_func(a)
+ if _libarchive.archive_read_open_fd(a, afile.fileno(), BLOCK_SIZE) == _libarchive.ARCHIVE_OK:
+ try:
+ nfilter = _libarchive.archive_filter_count(a)
+ return [_libarchive.archive_filter_name(a, i).decode(ENCODING) for i in range(nfilter)]
+ finally:
+ _libarchive.archive_read_close(a)
+ finally:
+ _libarchive.archive_read_free(a)
+ return []
+
+
+class EntryReadStream(object):
+ '''A file-like object for reading an entry from the archive.'''
+ def __init__(self, archive, size):
+ self.archive = archive
+ self.closed = False
+ self.size = size
+ self.bytes = 0
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ return
+
+ def __iter__(self):
+ if self.closed:
+ return
+ while True:
+ data = self.read(BLOCK_SIZE)
+ if not data:
+ break
+ yield data
+
+ def __len__(self):
+ return self.size
+
+ def tell(self):
+ return self.bytes
+
+ def read(self, bytes=-1):
+ if self.closed:
+ return
+ if self.bytes == self.size:
+ # EOF already reached.
+ return
+ if bytes < 0:
+ bytes = self.size - self.bytes
+ elif self.bytes + bytes > self.size:
+ # Limit read to remaining bytes
+ bytes = self.size - self.bytes
+ # Read requested bytes
+ data = _libarchive.archive_read_data_into_str(self.archive._a, bytes)
+ self.bytes += len(data)
+ return data
+
+ def close(self):
+ if self.closed:
+ return
+ # Call archive.close() with _defer True to let it know we have been
+ # closed and it is now safe to actually close.
+ self.archive.close(_defer=True)
+ self.archive = None
+ self.closed = True
+
+
+class EntryWriteStream(object):
+ '''A file-like object for writing an entry to an archive.
+
+ If the size is known ahead of time and provided, then the file contents
+ are not buffered but flushed directly to the archive. If size is omitted,
+ then the file contents are buffered and flushed in the close() method.'''
+ def __init__(self, archive, pathname, size=None):
+ self.archive = archive
+ self.entry = Entry(pathname=pathname, mtime=time.time(), mode=stat.S_IFREG)
+ if size is None:
+ self.buffer = six.StringIO()
+ else:
+ self.buffer = None
+ self.entry.size = size
+ self.entry.to_archive(self.archive)
+ self.bytes = 0
+ self.closed = False
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
+ def __del__(self):
+ self.close()
+
+ def __len__(self):
+ return self.bytes
+
+ def tell(self):
+ return self.bytes
+
+ def write(self, data):
+ if self.closed:
+ raise Exception('Cannot write to closed stream.')
+ if self.buffer:
+ self.buffer.write(data)
+ else:
+ _libarchive.archive_write_data_from_str(self.archive._a, data)
+ self.bytes += len(data)
+
+ def close(self):
+ if self.closed:
+ return
+ if self.buffer:
+ self.entry.size = self.buffer.tell()
+ self.entry.to_archive(self.archive)
+ _libarchive.archive_write_data_from_str(self.archive._a, self.buffer.getvalue())
+ _libarchive.archive_write_finish_entry(self.archive._a)
+
+ # Call archive.close() with _defer True to let it know we have been
+ # closed and it is now safe to actually close.
+ self.archive.close(_defer=True)
+ self.archive = None
+ self.closed = True
+
+
+class Entry(object):
+ '''An entry within an archive. Represents the header data and it's location within the archive.'''
+ def __init__(self, pathname=None, size=None, mtime=None, mode=None, hpos=None, encoding=ENCODING):
+ self.pathname = pathname
+ self.size = size
+ self.mtime = mtime
+ self.mode = mode
+ self.hpos = hpos
+ self.encoding = encoding
+ self.linkname = None
+ self.id = None
+ self.hardlink = None
+
+ @property
+ def header_position(self):
+ return self.hpos
+
+ @classmethod
+ def from_archive(cls, archive, encoding=ENCODING):
+ '''Instantiates an Entry class and sets all the properties from an archive header.'''
+ e = _libarchive.archive_entry_new()
+ try:
+ call_and_check(_libarchive.archive_read_next_header2, archive._a, archive._a, e)
+ mode = _libarchive.archive_entry_filetype(e)
+ mode |= _libarchive.archive_entry_perm(e)
+ mtime = _libarchive.archive_entry_mtime(e) + _libarchive.archive_entry_mtime_nsec(e) / 1000000000.0
+ # use current time as mtime if stored mtime is equal to 0
+ mtime = mtime or time.time()
+ entry = cls(
+ pathname=_libarchive.archive_entry_pathname(e).decode(encoding),
+ size=_libarchive.archive_entry_size(e),
+ mtime=mtime,
+ mode=mode,
+ hpos=archive.header_position,
+ )
+ # check hardlinkness first to processes hardlinks to the symlinks correctly
+ hardlink = _libarchive.archive_entry_hardlink(e)
+ if hardlink:
+ entry.hardlink = hardlink
+ elif entry.issym():
+ entry.linkname = _libarchive.archive_entry_symlink(e)
+ finally:
+ _libarchive.archive_entry_free(e)
+ return entry
+
+ @classmethod
+ def from_file(cls, f, entry=None, encoding=ENCODING, mtime=None):
+ '''Instantiates an Entry class and sets all the properties from a file on the file system.
+ f can be a file-like object or a path.'''
+ if entry is None:
+ entry = cls(encoding=encoding)
+ if entry.pathname is None:
+ if isinstance(f, six.string_types):
+ st = os.lstat(f)
+ entry.pathname = f
+ entry.size = st.st_size
+ entry.mtime = st.st_mtime if mtime is None else mtime
+ entry.mode = st.st_mode
+ entry.id = cls.get_entry_id(st)
+ if entry.issym():
+ entry.linkname = os.readlink(f)
+ elif hasattr(f, 'fileno'):
+ st = os.fstat(f.fileno())
+ entry.pathname = getattr(f, 'name', None)
+ entry.size = st.st_size
+ entry.mtime = st.st_mtime if mtime is None else mtime
+ entry.mode = st.st_mode
+ entry.id = cls.get_entry_id(st)
+ else:
+ entry.pathname = getattr(f, 'pathname', None)
+ entry.size = getattr(f, 'size', 0)
+ entry.mtime = getattr(f, 'mtime', time.time()) if mtime is None else mtime
+ entry.mode = getattr(f, 'mode', stat.S_IFREG)
+ return entry
+
+ @staticmethod
+ def get_entry_id(st):
+ # windows doesn't have such information
+ if st.st_ino and st.st_dev:
+ return (st.st_dev, st.st_ino)
+ return None
+
+ def to_archive(self, archive):
+ '''Creates an archive header and writes it to the given archive.'''
+ e = _libarchive.archive_entry_new()
+ try:
+ _libarchive.archive_entry_set_pathname(e, encode(self.pathname, self.encoding))
+ _libarchive.archive_entry_set_filetype(e, stat.S_IFMT(self.mode))
+ _libarchive.archive_entry_set_perm(e, stat.S_IMODE(self.mode))
+
+ nsec, sec = math.modf(self.mtime)
+ nsec *= 1000000000
+ _libarchive.archive_entry_set_mtime(e, int(sec), int(nsec))
+
+ if self.ishardlink():
+ _libarchive.archive_entry_set_size(e, 0)
+ _libarchive.archive_entry_set_hardlink(e, encode(self.hardlink, self.encoding))
+ elif self.issym():
+ _libarchive.archive_entry_set_size(e, 0)
+ _libarchive.archive_entry_set_symlink(e, encode(self.linkname, self.encoding))
+ else:
+ _libarchive.archive_entry_set_size(e, self.size)
+ call_and_check(_libarchive.archive_write_header, archive._a, archive._a, e)
+ #self.hpos = archive.header_position
+ finally:
+ _libarchive.archive_entry_free(e)
+
+ def isdir(self):
+ return stat.S_ISDIR(self.mode)
+
+ def isfile(self):
+ return stat.S_ISREG(self.mode)
+
+ def issym(self):
+ return stat.S_ISLNK(self.mode)
+
+ def isfifo(self):
+ return stat.S_ISFIFO(self.mode)
+
+ def ischr(self):
+ return stat.S_ISCHR(self.mode)
+
+ def isblk(self):
+ return stat.S_ISBLK(self.mode)
+
+ def ishardlink(self):
+ return bool(self.hardlink)
+
+
+class Archive(object):
+ '''A low-level archive reader which provides forward-only iteration. Consider
+ this a light-weight pythonic libarchive wrapper.'''
+ def __init__(self, f, mode='rb', format=None, filter=None, entry_class=Entry, encoding=ENCODING, blocksize=BLOCK_SIZE, filter_opts=None, format_opts=None, fsync=False, fixed_mtime=None):
+ if six.PY2:
+ assert mode in ('r', 'rb', 'w', 'wb', 'a', 'ab'), 'Mode should be "r[b]", "w[b]" or "a[b]".'
+ else:
+ assert mode in ('rb', 'wb', 'ab'), 'Mode should be "rb", "wb", or "ab".'
+ self._stream = None
+ self.encoding = encoding
+ self.blocksize = blocksize
+ self.file_handle = None
+ self.fd = None
+ self.filename = None
+ self.fsync = fsync
+ if isinstance(f, six.string_types):
+ self.filename = f
+ self.file_handle = open(f, mode)
+ self.fd = self.file_handle.fileno()
+ # Only close it if we opened it...
+ self._defer_close = True
+ elif hasattr(f, 'fileno'):
+ self.filename = getattr(f, 'name', None)
+ self.file_handle = f
+ self.fd = self.file_handle.fileno()
+ # Leave the fd alone, caller should manage it...
+ self._defer_close = False
+ elif isinstance(f, int):
+ assert f >= 0, f
+ self.fd = f
+ # Leave the fd alone, caller should manage it...
+ self._defer_close = False
+ else:
+ raise Exception('Provided file is not path or open file.')
+ self.mode = mode
+ # Guess the format/filter from file name (if not provided)
+ if self.filename:
+ if format is None:
+ format = guess_format(self.filename)[0]
+ if filter is None:
+ filter = guess_format(self.filename)[1]
+ self.format = format
+ self.filter = filter
+ # The class to use for entries.
+ self.entry_class = entry_class
+ self.fixed_mtime = fixed_mtime
+ # Select filter/format functions.
+ if self.mode.startswith('r'):
+ self.format_func = get_func(self.format, FORMATS, 0)
+ if self.format_func is None:
+ raise Exception('Unsupported format %s' % format)
+ self.filter_func = get_func(self.filter, FILTERS, 0)
+ if self.filter_func is None:
+ raise Exception('Unsupported filter %s' % filter)
+ else:
+ # TODO: how to support appending?
+ if self.format is None:
+ raise Exception('You must specify a format for writing.')
+ self.format_func = get_func(self.format, FORMATS, 1)
+ if self.format_func is None:
+ raise Exception('Unsupported format %s' % format)
+ self.filter_func = get_func(self.filter, FILTERS, 1)
+ if self.filter_func is None:
+ raise Exception('Unsupported filter %s' % filter)
+ # Open the archive, apply filter/format functions.
+ self.filter_opts = filter_opts
+ self.format_opts = format_opts
+ # Stores every added entry's id to handle hardlinks properly
+ self.members = {}
+ self.init()
+
+ def __iter__(self):
+ while True:
+ try:
+ yield self.entry_class.from_archive(self, encoding=self.encoding)
+ except EOF:
+ break
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.close()
+
+ def __del__(self):
+ self.close()
+
+ def init(self):
+ def _apply_opts(f, opts):
+ if opts:
+ for opt_name, opt_val in opts.items():
+ call_and_check(f, self._a, self._a, None, encode(opt_name, self.encoding), encode(opt_val, self.encoding))
+
+ if self.mode.startswith('r'):
+ self._a = _libarchive.archive_read_new()
+ else:
+ self._a = _libarchive.archive_write_new()
+ self.format_func(self._a)
+ self.filter_func(self._a)
+ if self.mode.startswith('r'):
+ _apply_opts(_libarchive.archive_read_set_format_option, self.format_opts)
+ _apply_opts(_libarchive.archive_read_set_filter_option, self.filter_opts)
+ call_and_check(_libarchive.archive_read_open_fd, self._a, self._a, self.fd, self.blocksize)
+ else:
+ _apply_opts(_libarchive.archive_write_set_format_option, self.format_opts)
+ _apply_opts(_libarchive.archive_write_set_filter_option, self.filter_opts)
+ call_and_check(_libarchive.archive_write_open_fd, self._a, self._a, self.fd)
+ # XXX Don't pad the last block to avoid badly formed archive with zstd filter
+ call_and_check(_libarchive.archive_write_set_bytes_in_last_block, self._a, self._a, 1)
+
+ def denit(self):
+ '''Closes and deallocates the archive reader/writer.'''
+ if getattr(self, '_a', None) is None:
+ return
+ try:
+ if self.mode.startswith('r'):
+ _libarchive.archive_read_close(self._a)
+ _libarchive.archive_read_free(self._a)
+ else:
+ _libarchive.archive_write_close(self._a)
+ _libarchive.archive_write_free(self._a)
+ finally:
+ # We only want one try at this...
+ self._a = None
+
+ def close(self, _defer=False):
+ # _defer == True is how a stream can notify Archive that the stream is
+ # now closed. Calling it directly in not recommended.
+ if _defer:
+ # This call came from our open stream.
+ self._stream = None
+ if not self._defer_close:
+ # We are not yet ready to close.
+ return
+ if self._stream is not None:
+ # We have a stream open! don't close, but remember we were asked to.
+ self._defer_close = True
+ return
+ self.denit()
+ # If there is a file attached...
+ if getattr(self, 'file_handle', None):
+ # Make sure it is not already closed...
+ if getattr(self.file_handle, 'closed', False):
+ return
+ # Flush it if not read-only...
+ if not self.file_handle.mode.startswith('r'):
+ self.file_handle.flush()
+ if self.fsync:
+ os.fsync(self.fd)
+ # and then close it, if we opened it...
+ if getattr(self, 'close', None):
+ self.file_handle.close()
+
+ @property
+ def header_position(self):
+ '''The position within the file.'''
+ return _libarchive.archive_read_header_position(self._a)
+
+ def iterpaths(self):
+ for entry in self:
+ yield entry.pathname
+
+ def read(self, size):
+ '''Read current archive entry contents into string.'''
+ return _libarchive.archive_read_data_into_str(self._a, size)
+
+ def readpath(self, f):
+ '''Write current archive entry contents to file. f can be a file-like object or
+ a path.'''
+ with contextlib2.ExitStack() as exit_stack:
+ if isinstance(f, six.string_types):
+ basedir = os.path.basename(f)
+ if not os.path.exists(basedir):
+ os.makedirs(basedir)
+ f = exit_stack.enter_context(open(f, 'wb'))
+ return _libarchive.archive_read_data_into_fd(self._a, f.fileno())
+
+ def readstream(self, size):
+ '''Returns a file-like object for reading current archive entry contents.'''
+ self._stream = EntryReadStream(self, size)
+ return self._stream
+
+ def write(self, member, data=None):
+ '''Writes a string buffer to the archive as the given entry.'''
+ if isinstance(member, six.string_types):
+ if self.fixed_mtime is None:
+ mtime = time.time()
+ else:
+ mtime = self.fixed_mtime
+ # Use default mode
+ member = self.entry_class(pathname=member, encoding=self.encoding, mtime=mtime, mode=stat.S_IFREG | 0o755)
+ if data:
+ member.size = len(data)
+ member.to_archive(self)
+ if data:
+ _libarchive.archive_write_data_from_str(self._a, data)
+ _libarchive.archive_write_finish_entry(self._a)
+
+ def writepath(self, f, pathname=None):
+ '''Writes a file to the archive. f can be a file-like object or a path. Uses
+ write() to do the actual writing.'''
+ member = self.entry_class.from_file(f, encoding=self.encoding, mtime=self.fixed_mtime)
+
+ with contextlib2.ExitStack() as exit_stack:
+ if isinstance(f, six.string_types):
+ if os.path.isfile(f):
+ f = exit_stack.enter_context(open(f, 'rb'))
+ if pathname:
+ member.pathname = pathname
+
+ # hardlinks and symlink has no data to be written
+ if member.id in self.members:
+ member.hardlink = self.members[member.id]
+ self.write(member)
+ return
+ elif member.issym():
+ self.write(member)
+ elif hasattr(f, 'read') and hasattr(f, 'seek') and hasattr(f, 'tell'):
+ self.write_from_file_object(member, f)
+ elif hasattr(f, 'read'):
+ # TODO: optimize this to write directly from f to archive.
+ self.write(member, data=f.read())
+ else:
+ self.write(member)
+
+ if member.id:
+ self.members[member.id] = member.pathname
+
+ def write_from_file_object(self, member, fileobj):
+ if isinstance(member, six.string_types):
+ member = self.entry_class(pathname=member, encoding=self.encoding, mtime=self.fixed_mtime)
+
+ start = fileobj.tell()
+ fileobj.seek(0, os.SEEK_END)
+ size = fileobj.tell() - start
+ fileobj.seek(start, os.SEEK_SET)
+
+ if size:
+ member.size = size
+ member.to_archive(self)
+
+ while size:
+ data = fileobj.read(BLOCK_SIZE)
+ if not data:
+ break
+
+ size -= len(data)
+ if size < 0:
+ msg = "File ({}) size has changed. Can't write more data than was declared in the tar header ({}). " \
+ "(probably file was changed during archiving)".format(member.pathname, member.size)
+ logger.warning(msg)
+ # write rest expected data (size is negative)
+ _libarchive.archive_write_data_from_str(self._a, data[:size])
+ break
+
+ _libarchive.archive_write_data_from_str(self._a, data)
+
+ _libarchive.archive_write_finish_entry(self._a)
+
+ def writestream(self, pathname, size=None):
+ '''Returns a file-like object for writing a new entry.'''
+ self._stream = EntryWriteStream(self, pathname, size)
+ return self._stream
+
+ def printlist(self, s=sys.stdout):
+ for entry in self:
+ s.write(entry.size)
+ s.write('\t')
+ s.write(entry.mtime.strftime(MTIME_FORMAT))
+ s.write('\t')
+ s.write(entry.pathname)
+ s.flush()
+
+
+class SeekableArchive(Archive):
+ '''A class that provides random-access to archive entries. It does this by using one
+ or many Archive instances to seek to the correct location. The best performance will
+ occur when reading archive entries in the order in which they appear in the archive.
+ Reading out of order will cause the archive to be closed and opened each time a
+ reverse seek is needed.'''
+ def __init__(self, f, **kwargs):
+ self._stream = None
+ # Convert file to open file. We need this to reopen the archive.
+ mode = kwargs.setdefault('mode', 'rb')
+ if isinstance(f, six.string_types):
+ f = open(f, mode)
+ super(SeekableArchive, self).__init__(f, **kwargs)
+ self.entries = []
+ self.eof = False
+
+ def __iter__(self):
+ for entry in self.entries:
+ yield entry
+ if not self.eof:
+ try:
+ for entry in super(SeekableArchive, self).__iter__():
+ self.entries.append(entry)
+ yield entry
+ except StopIteration:
+ self.eof = True
+
+ def reopen(self):
+ '''Seeks the underlying fd to 0 position, then opens the archive. If the archive
+ is already open, this will effectively re-open it (rewind to the beginning).'''
+ self.denit()
+ self.file_handle.seek(0)
+ self.init()
+
+ def getentry(self, pathname):
+ '''Take a name or entry object and returns an entry object.'''
+ for entry in self:
+ if entry.pathname == pathname:
+ return entry
+ raise KeyError(pathname)
+
+ def seek(self, entry):
+ '''Seeks the archive to the requested entry. Will reopen if necessary.'''
+ move = entry.header_position - self.header_position
+ if move != 0:
+ if move < 0:
+ # can't move back, re-open archive:
+ self.reopen()
+ # move to proper position in stream
+ for curr in super(SeekableArchive, self).__iter__():
+ if curr.header_position == entry.header_position:
+ break
+
+ def read(self, member):
+ '''Return the requested archive entry contents as a string.'''
+ entry = self.getentry(member)
+ self.seek(entry)
+ return super(SeekableArchive, self).read(entry.size)
+
+ def readpath(self, member, f):
+ entry = self.getentry(member)
+ self.seek(entry)
+ return super(SeekableArchive, self).readpath(f)
+
+ def readstream(self, member):
+ '''Returns a file-like object for reading requested archive entry contents.'''
+ entry = self.getentry(member)
+ self.seek(entry)
+ self._stream = EntryReadStream(self, entry.size)
+ return self._stream
diff --git a/contrib/python/python-libarchive/py2/libarchive/_libarchive.swg b/contrib/python/python-libarchive/py2/libarchive/_libarchive.swg
new file mode 100644
index 0000000000..2fcb05420e
--- /dev/null
+++ b/contrib/python/python-libarchive/py2/libarchive/_libarchive.swg
@@ -0,0 +1,339 @@
+/* Copyright (c) 2011, SmartFile <btimby@smartfile.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of the organization nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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. */
+
+%module _libarchive
+
+%{
+#define SWIG_PYTHON_STRICT_BYTE_CHAR
+
+#include <archive.h>
+#include <archive_entry.h>
+%}
+
+%include "typemaps.i"
+
+%typemap(in) time_t
+{
+ if (PyLong_Check($input))
+ $1 = (time_t) PyLong_AsLong($input);
+ else if (PyInt_Check($input))
+ $1 = (time_t) PyInt_AsLong($input);
+ else if (PyFloat_Check($input))
+ $1 = (time_t) PyFloat_AsDouble($input);
+ else {
+ PyErr_SetString(PyExc_TypeError,"Expected a large number");
+ return NULL;
+ }
+}
+
+%typemap(out) time_t
+{
+ $result = PyLong_FromLong((long)$1);
+}
+
+%typemap(in) int64_t
+{
+ if (PyLong_Check($input))
+ $1 = (int64_t) PyLong_AsLong($input);
+ else if (PyInt_Check($input))
+ $1 = (int64_t) PyInt_AsLong($input);
+ else if (PyFloat_Check($input))
+ $1 = (int64_t) PyFloat_AsDouble($input);
+ else {
+ PyErr_SetString(PyExc_TypeError,"Expected a large number");
+ return NULL;
+ }
+}
+
+%typemap(out) int64_t
+{
+ $result = PyLong_FromLong((long)$1);
+}
+
+#define __LA_INT64_T long long
+#define __LA_MODE_T int
+
+/* STRUCTURES */
+struct archive;
+struct archive_entry;
+
+/* ARCHIVE READING */
+extern struct archive *archive_read_new(void);
+extern int archive_read_free(struct archive *);
+
+/* opening */
+extern int archive_read_open_filename(struct archive *,
+ const char *_filename, size_t _block_size);
+extern int archive_read_open_memory(struct archive *,
+ void * buff, size_t size);
+extern int archive_read_open_memory2(struct archive *a, void *buff,
+ size_t size, size_t read_size);
+extern int archive_read_open_fd(struct archive *, int _fd,
+ size_t _block_size);
+
+/* closing */
+extern int archive_read_close(struct archive *);
+extern int archive_format(struct archive *);
+
+/* headers */
+extern int archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+extern const struct stat *archive_entry_stat(struct archive_entry *);
+extern __LA_INT64_T archive_read_header_position(struct archive *);
+
+/* data */
+extern int archive_read_data_skip(struct archive *);
+extern int archive_read_data_into_fd(struct archive *, int fd);
+
+/* FILTERS */
+extern int archive_read_support_filter_all(struct archive *);
+extern int archive_read_support_filter_bzip2(struct archive *);
+extern int archive_read_support_filter_compress(struct archive *);
+extern int archive_read_support_filter_gzip(struct archive *);
+extern int archive_read_support_filter_lzip(struct archive *);
+extern int archive_read_support_filter_lzma(struct archive *);
+extern int archive_read_support_filter_none(struct archive *);
+extern int archive_read_support_filter_rpm(struct archive *);
+extern int archive_read_support_filter_uu(struct archive *);
+extern int archive_read_support_filter_xz(struct archive *);
+extern int archive_read_support_filter_zstd(struct archive *);
+
+extern int archive_filter_count(struct archive *);
+extern const char * archive_filter_name(struct archive *, int);
+
+/* FORMATS */
+extern int archive_read_support_format_all(struct archive *);
+extern int archive_read_support_format_7zip(struct archive *);
+extern int archive_read_support_format_ar(struct archive *);
+extern int archive_read_support_format_cab(struct archive *);
+extern int archive_read_support_format_cpio(struct archive *);
+extern int archive_read_support_format_empty(struct archive *);
+extern int archive_read_support_format_gnutar(struct archive *);
+extern int archive_read_support_format_iso9660(struct archive *);
+extern int archive_read_support_format_lha(struct archive *);
+/*extern int archive_read_support_format_mtree(struct archive *);*/
+extern int archive_read_support_format_rar(struct archive *);
+extern int archive_read_support_format_raw(struct archive *);
+extern int archive_read_support_format_tar(struct archive *);
+extern int archive_read_support_format_xar(struct archive *);
+extern int archive_read_support_format_zip(struct archive *);
+/*extern int archive_read_support_format_by_code(struct archive *, int);*/
+
+/* OPTIONS */
+extern int archive_write_set_bytes_in_last_block(struct archive *_a, int bytes_in_last_block);
+extern int archive_write_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v);
+extern int archive_write_zip_set_compression_deflate(struct archive *_a);
+extern int archive_write_set_format_option(struct archive *_a, const char *m, const char *o, const char *v);
+extern int archive_read_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v);
+extern int archive_read_set_format_option(struct archive *_a, const char *m, const char *o, const char *v);
+
+/* ARCHIVE WRITING */
+extern struct archive *archive_write_new(void);
+extern int archive_write_free(struct archive *);
+
+/* opening */
+extern int archive_write_open(struct archive *, void *,
+ archive_open_callback *, archive_write_callback *,
+ archive_close_callback *);
+extern int archive_write_open_fd(struct archive *, int _fd);
+extern int archive_write_open_filename(struct archive *, const char *_file);
+extern int archive_write_open_filename_w(struct archive *,
+ const wchar_t *_file);
+extern int archive_write_open_memory(struct archive *,
+ void *_buffer, size_t _buffSize, size_t *_used);
+
+/* closing */
+extern int archive_write_close(struct archive *);
+
+/* headers */
+extern int archive_write_header(struct archive *,
+ struct archive_entry *);
+
+/* data */
+
+/* commit */
+extern int archive_write_finish_entry(struct archive *);
+
+/* FILTERS */
+extern int archive_write_add_filter_bzip2(struct archive *);
+extern int archive_write_add_filter_compress(struct archive *);
+extern int archive_write_add_filter_gzip(struct archive *);
+extern int archive_write_add_filter_lzip(struct archive *);
+extern int archive_write_add_filter_lzma(struct archive *);
+extern int archive_write_add_filter_none(struct archive *);
+extern int archive_write_add_filter_xz(struct archive *);
+extern int archive_write_add_filter_zstd(struct archive *);
+
+
+/* FORMATS */
+/* A convenience function to set the format based on the code or name. */
+extern int archive_write_set_format(struct archive *, int format_code);
+extern int archive_write_set_format_by_name(struct archive *,
+ const char *name);
+/* To minimize link pollution, use one or more of the following. */
+extern int archive_write_set_format_ar_bsd(struct archive *);
+extern int archive_write_set_format_ar_svr4(struct archive *);
+extern int archive_write_set_format_cpio(struct archive *);
+extern int archive_write_set_format_cpio_newc(struct archive *);
+extern int archive_write_set_format_gnutar(struct archive *);
+extern int archive_write_set_format_iso9660(struct archive *);
+/*extern int archive_write_set_format_mtree(struct archive *);*/
+/* TODO: int archive_write_set_format_old_tar(struct archive *); */
+extern int archive_write_set_format_pax(struct archive *);
+extern int archive_write_set_format_pax_restricted(struct archive *);
+extern int archive_write_set_format_shar(struct archive *);
+extern int archive_write_set_format_shar_dump(struct archive *);
+extern int archive_write_set_format_ustar(struct archive *);
+extern int archive_write_set_format_xar(struct archive *);
+extern int archive_write_set_format_zip(struct archive *);
+
+/* ARCHIVE ENTRY */
+extern struct archive_entry *archive_entry_new(void);
+extern void archive_entry_free(struct archive_entry *);
+extern const char *archive_entry_symlink(struct archive_entry *);
+extern void archive_entry_set_symlink(struct archive_entry *, const char *);
+extern const char *archive_entry_hardlink(struct archive_entry *);
+extern void archive_entry_set_hardlink(struct archive_entry *, const char *);
+
+/* ARCHIVE ENTRY PROPERTY ACCESS */
+/* reading */
+extern const char *archive_entry_pathname(struct archive_entry *);
+extern const wchar_t *archive_entry_pathname_w(struct archive_entry *);
+extern __LA_INT64_T archive_entry_size(struct archive_entry *);
+extern time_t archive_entry_mtime(struct archive_entry *);
+extern time_t archive_entry_mtime_nsec(struct archive_entry *);
+extern __LA_MODE_T archive_entry_filetype(struct archive_entry *);
+extern __LA_MODE_T archive_entry_perm(struct archive_entry *);
+
+/* writing */
+extern void archive_entry_set_pathname(struct archive_entry *, const char *);
+extern void archive_entry_set_size(struct archive_entry *, __LA_INT64_T);
+extern void archive_entry_set_mtime(struct archive_entry *, time_t, long);
+extern void archive_entry_set_filetype(struct archive_entry *, unsigned int);
+extern void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
+
+
+/* ERROR HANDLING */
+extern int archive_errno(struct archive *);
+extern const char *archive_error_string(struct archive *);
+
+
+/* CONSTANTS */
+#define ARCHIVE_VERSION_NUMBER 3000001
+#define ARCHIVE_VERSION_STRING "libarchive 3.0.1b"
+#define ARCHIVE_EOF 1 /* Found end of archive. */
+#define ARCHIVE_OK 0 /* Operation was successful. */
+#define ARCHIVE_RETRY (-10) /* Retry might succeed. */
+#define ARCHIVE_WARN (-20) /* Partial success. */
+#define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */
+#define ARCHIVE_FATAL (-30) /* No more operations are possible. */
+
+#define ARCHIVE_FILTER_NONE 0
+#define ARCHIVE_FILTER_GZIP 1
+#define ARCHIVE_FILTER_BZIP2 2
+#define ARCHIVE_FILTER_COMPRESS 3
+#define ARCHIVE_FILTER_PROGRAM 4
+#define ARCHIVE_FILTER_LZMA 5
+#define ARCHIVE_FILTER_XZ 6
+#define ARCHIVE_FILTER_UU 7
+#define ARCHIVE_FILTER_RPM 8
+#define ARCHIVE_FILTER_LZIP 9
+
+#define ARCHIVE_FORMAT_BASE_MASK 0xff0000
+#define ARCHIVE_FORMAT_CPIO 0x10000
+#define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1)
+#define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2)
+#define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3)
+#define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4)
+#define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5)
+#define ARCHIVE_FORMAT_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6)
+#define ARCHIVE_FORMAT_SHAR 0x20000
+#define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1)
+#define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2)
+#define ARCHIVE_FORMAT_TAR 0x30000
+#define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1)
+#define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2)
+#define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3)
+#define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4)
+#define ARCHIVE_FORMAT_ISO9660 0x40000
+#define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1)
+#define ARCHIVE_FORMAT_ZIP 0x50000
+#define ARCHIVE_FORMAT_EMPTY 0x60000
+#define ARCHIVE_FORMAT_AR 0x70000
+#define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1)
+#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2)
+#define ARCHIVE_FORMAT_MTREE 0x80000
+#define ARCHIVE_FORMAT_RAW 0x90000
+#define ARCHIVE_FORMAT_XAR 0xA0000
+#define ARCHIVE_FORMAT_LHA 0xB0000
+#define ARCHIVE_FORMAT_CAB 0xC0000
+#define ARCHIVE_FORMAT_RAR 0xD0000
+#define ARCHIVE_FORMAT_7ZIP 0xE0000
+
+#define ARCHIVE_EXTRACT_OWNER (0x0001)
+#define ARCHIVE_EXTRACT_PERM (0x0002)
+#define ARCHIVE_EXTRACT_TIME (0x0004)
+#define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008)
+#define ARCHIVE_EXTRACT_UNLINK (0x0010)
+#define ARCHIVE_EXTRACT_ACL (0x0020)
+#define ARCHIVE_EXTRACT_FFLAGS (0x0040)
+#define ARCHIVE_EXTRACT_XATTR (0x0080)
+#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100)
+#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200)
+#define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400)
+#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800)
+#define ARCHIVE_EXTRACT_SPARSE (0x1000)
+#define ARCHIVE_EXTRACT_MAC_METADATA (0x2000)
+
+%inline %{
+PyObject *archive_read_data_into_str(struct archive *archive, int len) {
+ PyObject *str = NULL;
+ if (!(str = PyBytes_FromStringAndSize(NULL, len))) {
+ PyErr_SetString(PyExc_MemoryError, "could not allocate string.");
+ return NULL;
+ }
+ if (len != archive_read_data(archive, PyBytes_AS_STRING(str), len)) {
+ PyErr_SetString(PyExc_RuntimeError, "could not read requested data.");
+ return NULL;
+ }
+ return str;
+}
+
+PyObject *archive_write_data_from_str(struct archive *archive, PyObject *str) {
+ int len = PyBytes_Size(str);
+ if (len == 0)
+ return PyInt_FromLong(len);
+ int ret = archive_write_data(archive, PyBytes_AS_STRING(str), len);
+ if (ret == ARCHIVE_FATAL) {
+ PyErr_Format(PyExc_RuntimeError, "Could not write requested data - most likely no space left on device (error code: %d)", ret);
+ return NULL;
+ }
+ else if (ret <= 0) {
+ PyErr_Format(PyExc_RuntimeError, "Could not write requested data (error code: %d)", ret);
+ return NULL;
+ }
+ return PyInt_FromLong(len);
+}
+%}
diff --git a/contrib/python/python-libarchive/py2/libarchive/tar.py b/contrib/python/python-libarchive/py2/libarchive/tar.py
new file mode 100644
index 0000000000..f14149804b
--- /dev/null
+++ b/contrib/python/python-libarchive/py2/libarchive/tar.py
@@ -0,0 +1,135 @@
+# Copyright (c) 2011, SmartFile <btimby@smartfile.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * 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.
+# * Neither the name of the organization nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+
+import time
+from libarchive import is_archive, Entry, SeekableArchive
+from tarfile import DEFAULT_FORMAT, USTAR_FORMAT, GNU_FORMAT, PAX_FORMAT, ENCODING
+from tarfile import REGTYPE, AREGTYPE, LNKTYPE, SYMTYPE, DIRTYPE, FIFOTYPE, CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_SPARSE
+
+FORMAT_CONVERSION = {
+ USTAR_FORMAT: 'tar',
+ GNU_FORMAT: 'gnu',
+ PAX_FORMAT: 'pax',
+}
+
+
+def is_tarfile(filename):
+ return is_archive(filename, formats=('tar', 'gnu', 'pax'))
+
+
+def open(**kwargs):
+ return TarFile(**kwargs)
+
+
+class TarInfo(Entry):
+ def __init__(self, name):
+ super(TarInfo, self).__init__(pathname=name)
+
+ fromtarfile = Entry.from_archive
+
+ def get_name(self):
+ return self.pathname
+
+ def set_name(self, value):
+ self.pathname = value
+
+ name = property(get_name, set_name)
+
+ @property
+ def get_type(self):
+ for attr, type in (
+ ('isdir', DIRTYPE), ('isfile', REGTYPE), ('issym', SYMTYPE),
+ ('isfifo', FIFOTYPE), ('ischr', CHRTYPE), ('isblk', BLKTYPE),
+ ):
+ if getattr(self, attr)():
+ return type
+
+ def _get_missing(self):
+ raise NotImplemented()
+
+ def _set_missing(self, value):
+ raise NotImplemented()
+
+ pax_headers = property(_get_missing, _set_missing)
+
+
+class TarFile(SeekableArchive):
+ def __init__(self, name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, encoding=ENCODING):
+ if name:
+ f = name
+ elif fileobj:
+ f = fileobj
+ try:
+ format = FORMAT_CONVERSON.get(format)
+ except KeyError:
+ raise Exception('Invalid tar format: %s' % format)
+ super(TarFile, self).__init__(f, mode=mode, format=format, entry_class=tarinfo, encoding=encoding)
+
+ getmember = SeekableArchive.getentry
+ list = SeekableArchive.printlist
+ extract = SeekableArchive.readpath
+ extractfile = SeekableArchive.readstream
+
+ def getmembers(self):
+ return list(self)
+
+ def getnames(self):
+ return list(self.iterpaths)
+
+ def next(self):
+ pass # TODO: how to do this?
+
+ def extract(self, member, path=None):
+ if path is None:
+ path = os.getcwd()
+ if isinstance(member, basestring):
+ f = os.path.join(path, member)
+ else:
+ f = os.path.join(path, member.pathname)
+ return self.readpath(member, f)
+
+ def add(self, name, arcname, recursive=True, exclude=None, filter=None):
+ pass # TODO: implement this.
+
+ def addfile(tarinfo, fileobj):
+ return self.writepath(fileobj, tarinfo)
+
+ def gettarinfo(name=None, arcname=None, fileobj=None):
+ if name:
+ f = name
+ elif fileobj:
+ f = fileobj
+ entry = self.entry_class.from_file(f)
+ if arcname:
+ entry.pathname = arcname
+ return entry
+
+ def _get_missing(self):
+ raise NotImplemented()
+
+ def _set_missing(self, value):
+ raise NotImplemented()
+
+ pax_headers = property(_get_missing, _set_missing)
diff --git a/contrib/python/python-libarchive/py2/libarchive/zip.py b/contrib/python/python-libarchive/py2/libarchive/zip.py
new file mode 100644
index 0000000000..539f6dbcc4
--- /dev/null
+++ b/contrib/python/python-libarchive/py2/libarchive/zip.py
@@ -0,0 +1,151 @@
+# Copyright (c) 2011, SmartFile <btimby@smartfile.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * 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.
+# * Neither the name of the organization nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+
+import os, time
+from libarchive import is_archive, Entry, SeekableArchive
+from zipfile import ZIP_STORED, ZIP_DEFLATED
+
+
+def is_zipfile(filename):
+ return is_archive(filename, formats=('zip', ))
+
+
+class ZipEntry(Entry):
+ def __init__(self, *args, **kwargs):
+ super(ZipEntry, self).__init__(*args, **kwargs)
+
+ def get_filename(self):
+ return self.pathname
+
+ def set_filename(self, value):
+ self.pathname = value
+
+ filename = property(get_filename, set_filename)
+
+ def get_file_size(self):
+ return self.size
+
+ def set_file_size(self, value):
+ assert isinstance(size, (int, long)), 'Please provide size as int or long.'
+ self.size = value
+
+ file_size = property(get_file_size, set_file_size)
+
+ def get_date_time(self):
+ return time.localtime(self.mtime)[0:6]
+
+ def set_date_time(self, value):
+ assert isinstance(value, tuple), 'mtime should be tuple (year, month, day, hour, minute, second).'
+ assert len(value) == 6, 'mtime should be tuple (year, month, day, hour, minute, second).'
+ self.mtime = time.mktime(value + (0, 0, 0))
+
+ date_time = property(get_date_time, set_date_time)
+
+ header_offset = Entry.header_position
+
+ def _get_missing(self):
+ raise NotImplemented()
+
+ def _set_missing(self, value):
+ raise NotImplemented()
+
+ compress_type = property(_get_missing, _set_missing)
+ comment = property(_get_missing, _set_missing)
+ extra = property(_get_missing, _set_missing)
+ create_system = property(_get_missing, _set_missing)
+ create_version = property(_get_missing, _set_missing)
+ extract_version = property(_get_missing, _set_missing)
+ reserved = property(_get_missing, _set_missing)
+ flag_bits = property(_get_missing, _set_missing)
+ volume = property(_get_missing, _set_missing)
+ internal_attr = property(_get_missing, _set_missing)
+ external_attr = property(_get_missing, _set_missing)
+ CRC = property(_get_missing, _set_missing)
+ compress_size = property(_get_missing, _set_missing)
+
+
+class ZipFile(SeekableArchive):
+ def __init__(self, f, mode='r', compression=ZIP_DEFLATED, allowZip64=False):
+ super(ZipFile, self).__init__(f, mode=mode, format='zip', entry_class=ZipEntry, encoding='CP437')
+ if mode == 'w' and compression == ZIP_STORED:
+ # Disable compression for writing.
+ _libarchive.archive_write_set_format_option(self.archive._a, "zip", "compression", "store")
+ self.compression = compression
+
+ getinfo = SeekableArchive.getentry
+
+ def namelist(self):
+ return list(self.iterpaths)
+
+ def infolist(self):
+ return list(self)
+
+ def open(self, name, mode, pwd=None):
+ if pwd:
+ raise NotImplemented('Encryption not supported.')
+ if mode == 'r':
+ return self.readstream(name)
+ else:
+ return self.writestream(name)
+
+ def extract(self, name, path=None, pwd=None):
+ if pwd:
+ raise NotImplemented('Encryption not supported.')
+ if not path:
+ path = os.getcwd()
+ return self.readpath(name, os.path.join(path, name))
+
+ def extractall(self, path, names=None, pwd=None):
+ if pwd:
+ raise NotImplemented('Encryption not supported.')
+ if not names:
+ names = self.namelist()
+ if names:
+ for name in names:
+ self.extract(name, path)
+
+ def read(self, name, pwd=None):
+ if pwd:
+ raise NotImplemented('Encryption not supported.')
+ return self.read(name)
+
+ def writestr(self, member, data, compress_type=None):
+ if compress_type != self.compression:
+ raise Exception('Cannot change compression type for individual entries.')
+ return self.write(member, data)
+
+ def setpassword(self, pwd):
+ raise NotImplemented('Encryption not supported.')
+
+ def testzip(self):
+ raise NotImplemented()
+
+ def _get_missing(self):
+ raise NotImplemented()
+
+ def _set_missing(self, value):
+ raise NotImplemented()
+
+ comment = property(_get_missing, _set_missing)
diff --git a/contrib/python/python-libarchive/py2/ya.make b/contrib/python/python-libarchive/py2/ya.make
new file mode 100644
index 0000000000..3e73181b64
--- /dev/null
+++ b/contrib/python/python-libarchive/py2/ya.make
@@ -0,0 +1,28 @@
+PY2_LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+VERSION(3.1.2.post1)
+
+PEERDIR(
+ contrib/libs/libarchive
+ contrib/python/contextlib2
+ contrib/python/six
+)
+
+ADDINCL(
+ contrib/libs/libarchive/libarchive
+)
+
+NO_LINT()
+
+PY_SRCS(
+ SWIG_C
+ TOP_LEVEL
+ libarchive/__init__.py
+ libarchive/tar.py
+ libarchive/zip.py
+ libarchive/_libarchive.swg
+)
+
+END()
diff --git a/contrib/python/python-libarchive/py3/libarchive/__init__.py b/contrib/python/python-libarchive/py3/libarchive/__init__.py
new file mode 100644
index 0000000000..0c0c63359a
--- /dev/null
+++ b/contrib/python/python-libarchive/py3/libarchive/__init__.py
@@ -0,0 +1,800 @@
+# Copyright (c) 2011, SmartFile <btimby@smartfile.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * 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.
+# * Neither the name of the organization nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+
+import os
+import stat
+import sys
+import math
+import time
+import logging
+import warnings
+
+import contextlib2
+
+from libarchive import _libarchive
+import six
+
+logger = logging.getLogger(__name__)
+
+# Suggested block size for libarchive. Libarchive may adjust it.
+BLOCK_SIZE = 10240
+
+MTIME_FORMAT = ''
+
+# Default encoding scheme.
+ENCODING = 'utf-8'
+
+if six.PY2:
+ def encode(value, encoding):
+ if type(value) == str:
+ value = value.decode(encoding, errors='ignore')
+ return value.encode(encoding)
+else:
+ def encode(value, encoding):
+ return value.encode(encoding)
+
+
+# Functions to initialize read/write for various libarchive supported formats and filters.
+FORMATS = {
+ None: (_libarchive.archive_read_support_format_all, None),
+ 'tar': (_libarchive.archive_read_support_format_tar, _libarchive.archive_write_set_format_ustar),
+ 'pax': (_libarchive.archive_read_support_format_tar, _libarchive.archive_write_set_format_pax),
+ 'gnu': (_libarchive.archive_read_support_format_gnutar, _libarchive.archive_write_set_format_gnutar),
+ 'zip': (_libarchive.archive_read_support_format_zip, _libarchive.archive_write_set_format_zip),
+ 'rar': (_libarchive.archive_read_support_format_rar, None),
+ '7zip': (_libarchive.archive_read_support_format_7zip, None),
+ 'ar': (_libarchive.archive_read_support_format_ar, None),
+ 'cab': (_libarchive.archive_read_support_format_cab, None),
+ 'cpio': (_libarchive.archive_read_support_format_cpio, _libarchive.archive_write_set_format_cpio_newc),
+ 'iso': (_libarchive.archive_read_support_format_iso9660, _libarchive.archive_write_set_format_iso9660),
+ 'lha': (_libarchive.archive_read_support_format_lha, None),
+ 'xar': (_libarchive.archive_read_support_format_xar, _libarchive.archive_write_set_format_xar),
+}
+
+FILTERS = {
+ None: (_libarchive.archive_read_support_filter_all, _libarchive.archive_write_add_filter_none),
+ 'bzip2': (_libarchive.archive_read_support_filter_bzip2, _libarchive.archive_write_add_filter_bzip2),
+ 'gzip': (_libarchive.archive_read_support_filter_gzip, _libarchive.archive_write_add_filter_gzip),
+ 'zstd': (_libarchive.archive_read_support_filter_zstd, _libarchive.archive_write_add_filter_zstd),
+}
+
+# Map file extensions to formats and filters. To support quick detection.
+FORMAT_EXTENSIONS = {
+ '.tar': 'tar',
+ '.zip': 'zip',
+ '.rar': 'rar',
+ '.7z': '7zip',
+ '.ar': 'ar',
+ '.cab': 'cab',
+ '.rpm': 'cpio',
+ '.cpio': 'cpio',
+ '.iso': 'iso',
+ '.lha': 'lha',
+ '.xar': 'xar',
+}
+FILTER_EXTENSIONS = {
+ '.bz2': 'bzip2',
+ '.gz': 'gzip',
+ '.zst': 'zstd',
+}
+
+
+class EOF(Exception):
+ '''Raised by ArchiveInfo.from_archive() when unable to read the next
+ archive header.'''
+ pass
+
+
+def get_error(archive):
+ '''Retrieves the last error description for the given archive instance.'''
+ return _libarchive.archive_error_string(archive)
+
+
+def call_and_check(func, archive, *args):
+ '''Executes a libarchive function and raises an exception when appropriate.'''
+ ret = func(*args)
+ if ret == _libarchive.ARCHIVE_OK:
+ return
+ elif ret == _libarchive.ARCHIVE_WARN:
+ warnings.warn('Warning executing function: %s.' % get_error(archive), RuntimeWarning)
+ elif ret == _libarchive.ARCHIVE_EOF:
+ raise EOF()
+ else:
+ raise Exception('Fatal error executing function, message is: %s.' % get_error(archive))
+
+
+def get_func(name, items, index):
+ item = items.get(name, None)
+ if item is None:
+ return None
+ return item[index]
+
+
+def guess_format(filename):
+ filename, ext = os.path.splitext(filename)
+ filter = FILTER_EXTENSIONS.get(ext)
+ if filter:
+ filename, ext = os.path.splitext(filename)
+ format = FORMAT_EXTENSIONS.get(ext)
+ return format, filter
+
+
+def is_archive_name(filename, formats=None):
+ '''Quick check to see if the given file has an extension indiciating that it is
+ an archive. The format parameter can be used to limit what archive format is acceptable.
+ If omitted, all supported archive formats will be checked.
+
+ This function will return the name of the most likely archive format, None if the file is
+ unlikely to be an archive.'''
+ if formats is None:
+ formats = FORMAT_EXTENSIONS.values()
+ format, filter = guess_format(filename)
+ if format in formats:
+ return format
+
+
+def is_archive(f, formats=(None, ), filters=(None, )):
+ '''Check to see if the given file is actually an archive. The format parameter
+ can be used to specify which archive format is acceptable. If ommitted, all supported
+ archive formats will be checked. It opens the file using libarchive. If no error is
+ received, the file was successfully detected by the libarchive bidding process.
+
+ This procedure is quite costly, so you should avoid calling it unless you are reasonably
+ sure that the given file is an archive. In other words, you may wish to filter large
+ numbers of file names using is_archive_name() before double-checking the positives with
+ this function.
+
+ This function will return True if the file can be opened as an archive using the given
+ format(s)/filter(s).'''
+ with contextlib2.ExitStack() as exit_stack:
+ if isinstance(f, six.string_types):
+ f = exit_stack.enter_context(open(f, 'rb'))
+ a = _libarchive.archive_read_new()
+ for format in formats:
+ format = get_func(format, FORMATS, 0)
+ if format is None:
+ return False
+ format(a)
+ for filter in filters:
+ filter = get_func(filter, FILTERS, 0)
+ if filter is None:
+ return False
+ filter(a)
+ try:
+ try:
+ call_and_check(_libarchive.archive_read_open_fd, a, a, f.fileno(), BLOCK_SIZE)
+ return True
+ except:
+ return False
+ finally:
+ _libarchive.archive_read_close(a)
+ _libarchive.archive_read_free(a)
+
+
+def get_archive_filter_names(filename):
+ with open(filename, 'rb') as afile:
+ a = _libarchive.archive_read_new()
+ try:
+ format_func = get_func(None, FORMATS, 0)
+ format_func(a)
+ filter_func = get_func(None, FILTERS, 0)
+ filter_func(a)
+ if _libarchive.archive_read_open_fd(a, afile.fileno(), BLOCK_SIZE) == _libarchive.ARCHIVE_OK:
+ try:
+ nfilter = _libarchive.archive_filter_count(a)
+ return [_libarchive.archive_filter_name(a, i).decode(ENCODING) for i in range(nfilter)]
+ finally:
+ _libarchive.archive_read_close(a)
+ finally:
+ _libarchive.archive_read_free(a)
+ return []
+
+
+class EntryReadStream(object):
+ '''A file-like object for reading an entry from the archive.'''
+ def __init__(self, archive, size):
+ self.archive = archive
+ self.closed = False
+ self.size = size
+ self.bytes = 0
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ return
+
+ def __iter__(self):
+ if self.closed:
+ return
+ while True:
+ data = self.read(BLOCK_SIZE)
+ if not data:
+ break
+ yield data
+
+ def __len__(self):
+ return self.size
+
+ def tell(self):
+ return self.bytes
+
+ def read(self, bytes=-1):
+ if self.closed:
+ return
+ if self.bytes == self.size:
+ # EOF already reached.
+ return
+ if bytes < 0:
+ bytes = self.size - self.bytes
+ elif self.bytes + bytes > self.size:
+ # Limit read to remaining bytes
+ bytes = self.size - self.bytes
+ # Read requested bytes
+ data = _libarchive.archive_read_data_into_str(self.archive._a, bytes)
+ self.bytes += len(data)
+ return data
+
+ def close(self):
+ if self.closed:
+ return
+ # Call archive.close() with _defer True to let it know we have been
+ # closed and it is now safe to actually close.
+ self.archive.close(_defer=True)
+ self.archive = None
+ self.closed = True
+
+
+class EntryWriteStream(object):
+ '''A file-like object for writing an entry to an archive.
+
+ If the size is known ahead of time and provided, then the file contents
+ are not buffered but flushed directly to the archive. If size is omitted,
+ then the file contents are buffered and flushed in the close() method.'''
+ def __init__(self, archive, pathname, size=None):
+ self.archive = archive
+ self.entry = Entry(pathname=pathname, mtime=time.time(), mode=stat.S_IFREG)
+ if size is None:
+ self.buffer = six.StringIO()
+ else:
+ self.buffer = None
+ self.entry.size = size
+ self.entry.to_archive(self.archive)
+ self.bytes = 0
+ self.closed = False
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
+ def __del__(self):
+ self.close()
+
+ def __len__(self):
+ return self.bytes
+
+ def tell(self):
+ return self.bytes
+
+ def write(self, data):
+ if self.closed:
+ raise Exception('Cannot write to closed stream.')
+ if self.buffer:
+ self.buffer.write(data)
+ else:
+ _libarchive.archive_write_data_from_str(self.archive._a, data)
+ self.bytes += len(data)
+
+ def close(self):
+ if self.closed:
+ return
+ if self.buffer:
+ self.entry.size = self.buffer.tell()
+ self.entry.to_archive(self.archive)
+ _libarchive.archive_write_data_from_str(self.archive._a, self.buffer.getvalue())
+ _libarchive.archive_write_finish_entry(self.archive._a)
+
+ # Call archive.close() with _defer True to let it know we have been
+ # closed and it is now safe to actually close.
+ self.archive.close(_defer=True)
+ self.archive = None
+ self.closed = True
+
+
+class Entry(object):
+ '''An entry within an archive. Represents the header data and it's location within the archive.'''
+ def __init__(self, pathname=None, size=None, mtime=None, mode=None, hpos=None, encoding=ENCODING):
+ self.pathname = pathname
+ self.size = size
+ self.mtime = mtime
+ self.mode = mode
+ self.hpos = hpos
+ self.encoding = encoding
+ self.linkname = None
+ self.id = None
+ self.hardlink = None
+
+ @property
+ def header_position(self):
+ return self.hpos
+
+ @classmethod
+ def from_archive(cls, archive, encoding=ENCODING):
+ '''Instantiates an Entry class and sets all the properties from an archive header.'''
+ e = _libarchive.archive_entry_new()
+ try:
+ call_and_check(_libarchive.archive_read_next_header2, archive._a, archive._a, e)
+ mode = _libarchive.archive_entry_filetype(e)
+ mode |= _libarchive.archive_entry_perm(e)
+ mtime = _libarchive.archive_entry_mtime(e) + _libarchive.archive_entry_mtime_nsec(e) / 1000000000.0
+ # use current time as mtime if stored mtime is equal to 0
+ mtime = mtime or time.time()
+ entry = cls(
+ pathname=_libarchive.archive_entry_pathname(e).decode(encoding),
+ size=_libarchive.archive_entry_size(e),
+ mtime=mtime,
+ mode=mode,
+ hpos=archive.header_position,
+ )
+ # check hardlinkness first to processes hardlinks to the symlinks correctly
+ hardlink = _libarchive.archive_entry_hardlink(e)
+ if hardlink:
+ entry.hardlink = hardlink
+ elif entry.issym():
+ entry.linkname = _libarchive.archive_entry_symlink(e)
+ finally:
+ _libarchive.archive_entry_free(e)
+ return entry
+
+ @classmethod
+ def from_file(cls, f, entry=None, encoding=ENCODING, mtime=None):
+ '''Instantiates an Entry class and sets all the properties from a file on the file system.
+ f can be a file-like object or a path.'''
+ if entry is None:
+ entry = cls(encoding=encoding)
+ if entry.pathname is None:
+ if isinstance(f, six.string_types):
+ st = os.lstat(f)
+ entry.pathname = f
+ entry.size = st.st_size
+ entry.mtime = st.st_mtime if mtime is None else mtime
+ entry.mode = st.st_mode
+ entry.id = cls.get_entry_id(st)
+ if entry.issym():
+ entry.linkname = os.readlink(f)
+ elif hasattr(f, 'fileno'):
+ st = os.fstat(f.fileno())
+ entry.pathname = getattr(f, 'name', None)
+ entry.size = st.st_size
+ entry.mtime = st.st_mtime if mtime is None else mtime
+ entry.mode = st.st_mode
+ entry.id = cls.get_entry_id(st)
+ else:
+ entry.pathname = getattr(f, 'pathname', None)
+ entry.size = getattr(f, 'size', 0)
+ entry.mtime = getattr(f, 'mtime', time.time()) if mtime is None else mtime
+ entry.mode = getattr(f, 'mode', stat.S_IFREG)
+ return entry
+
+ @staticmethod
+ def get_entry_id(st):
+ # windows doesn't have such information
+ if st.st_ino and st.st_dev:
+ return (st.st_dev, st.st_ino)
+ return None
+
+ def to_archive(self, archive):
+ '''Creates an archive header and writes it to the given archive.'''
+ e = _libarchive.archive_entry_new()
+ try:
+ _libarchive.archive_entry_set_pathname(e, encode(self.pathname, self.encoding))
+ _libarchive.archive_entry_set_filetype(e, stat.S_IFMT(self.mode))
+ _libarchive.archive_entry_set_perm(e, stat.S_IMODE(self.mode))
+
+ nsec, sec = math.modf(self.mtime)
+ nsec *= 1000000000
+ _libarchive.archive_entry_set_mtime(e, int(sec), int(nsec))
+
+ if self.ishardlink():
+ _libarchive.archive_entry_set_size(e, 0)
+ _libarchive.archive_entry_set_hardlink(e, encode(self.hardlink, self.encoding))
+ elif self.issym():
+ _libarchive.archive_entry_set_size(e, 0)
+ _libarchive.archive_entry_set_symlink(e, encode(self.linkname, self.encoding))
+ else:
+ _libarchive.archive_entry_set_size(e, self.size)
+ call_and_check(_libarchive.archive_write_header, archive._a, archive._a, e)
+ #self.hpos = archive.header_position
+ finally:
+ _libarchive.archive_entry_free(e)
+
+ def isdir(self):
+ return stat.S_ISDIR(self.mode)
+
+ def isfile(self):
+ return stat.S_ISREG(self.mode)
+
+ def issym(self):
+ return stat.S_ISLNK(self.mode)
+
+ def isfifo(self):
+ return stat.S_ISFIFO(self.mode)
+
+ def ischr(self):
+ return stat.S_ISCHR(self.mode)
+
+ def isblk(self):
+ return stat.S_ISBLK(self.mode)
+
+ def ishardlink(self):
+ return bool(self.hardlink)
+
+
+class Archive(object):
+ '''A low-level archive reader which provides forward-only iteration. Consider
+ this a light-weight pythonic libarchive wrapper.'''
+ def __init__(self, f, mode='rb', format=None, filter=None, entry_class=Entry, encoding=ENCODING, blocksize=BLOCK_SIZE, filter_opts=None, format_opts=None, fsync=False, fixed_mtime=None):
+ if six.PY2:
+ assert mode in ('r', 'rb', 'w', 'wb', 'a', 'ab'), 'Mode should be "r[b]", "w[b]" or "a[b]".'
+ else:
+ assert mode in ('rb', 'wb', 'ab'), 'Mode should be "rb", "wb", or "ab".'
+ self._stream = None
+ self.encoding = encoding
+ self.blocksize = blocksize
+ self.file_handle = None
+ self.fd = None
+ self.filename = None
+ self.fsync = fsync
+ if isinstance(f, six.string_types):
+ self.filename = f
+ self.file_handle = open(f, mode)
+ self.fd = self.file_handle.fileno()
+ # Only close it if we opened it...
+ self._defer_close = True
+ elif hasattr(f, 'fileno'):
+ self.filename = getattr(f, 'name', None)
+ self.file_handle = f
+ self.fd = self.file_handle.fileno()
+ # Leave the fd alone, caller should manage it...
+ self._defer_close = False
+ elif isinstance(f, int):
+ assert f >= 0, f
+ self.fd = f
+ # Leave the fd alone, caller should manage it...
+ self._defer_close = False
+ else:
+ raise Exception('Provided file is not path or open file.')
+ self.mode = mode
+ # Guess the format/filter from file name (if not provided)
+ if self.filename:
+ if format is None:
+ format = guess_format(self.filename)[0]
+ if filter is None:
+ filter = guess_format(self.filename)[1]
+ self.format = format
+ self.filter = filter
+ # The class to use for entries.
+ self.entry_class = entry_class
+ self.fixed_mtime = fixed_mtime
+ # Select filter/format functions.
+ if self.mode.startswith('r'):
+ self.format_func = get_func(self.format, FORMATS, 0)
+ if self.format_func is None:
+ raise Exception('Unsupported format %s' % format)
+ self.filter_func = get_func(self.filter, FILTERS, 0)
+ if self.filter_func is None:
+ raise Exception('Unsupported filter %s' % filter)
+ else:
+ # TODO: how to support appending?
+ if self.format is None:
+ raise Exception('You must specify a format for writing.')
+ self.format_func = get_func(self.format, FORMATS, 1)
+ if self.format_func is None:
+ raise Exception('Unsupported format %s' % format)
+ self.filter_func = get_func(self.filter, FILTERS, 1)
+ if self.filter_func is None:
+ raise Exception('Unsupported filter %s' % filter)
+ # Open the archive, apply filter/format functions.
+ self.filter_opts = filter_opts
+ self.format_opts = format_opts
+ # Stores every added entry's id to handle hardlinks properly
+ self.members = {}
+ self.init()
+
+ def __iter__(self):
+ while True:
+ try:
+ yield self.entry_class.from_archive(self, encoding=self.encoding)
+ except EOF:
+ break
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.close()
+
+ def __del__(self):
+ self.close()
+
+ def init(self):
+ def _apply_opts(f, opts):
+ if opts:
+ for opt_name, opt_val in opts.items():
+ call_and_check(f, self._a, self._a, None, encode(opt_name, self.encoding), encode(opt_val, self.encoding))
+
+ if self.mode.startswith('r'):
+ self._a = _libarchive.archive_read_new()
+ else:
+ self._a = _libarchive.archive_write_new()
+ self.format_func(self._a)
+ self.filter_func(self._a)
+ if self.mode.startswith('r'):
+ _apply_opts(_libarchive.archive_read_set_format_option, self.format_opts)
+ _apply_opts(_libarchive.archive_read_set_filter_option, self.filter_opts)
+ call_and_check(_libarchive.archive_read_open_fd, self._a, self._a, self.fd, self.blocksize)
+ else:
+ _apply_opts(_libarchive.archive_write_set_format_option, self.format_opts)
+ _apply_opts(_libarchive.archive_write_set_filter_option, self.filter_opts)
+ call_and_check(_libarchive.archive_write_open_fd, self._a, self._a, self.fd)
+ # XXX Don't pad the last block to avoid badly formed archive with zstd filter
+ call_and_check(_libarchive.archive_write_set_bytes_in_last_block, self._a, self._a, 1)
+
+ def denit(self):
+ '''Closes and deallocates the archive reader/writer.'''
+ if getattr(self, '_a', None) is None:
+ return
+ try:
+ if self.mode.startswith('r'):
+ _libarchive.archive_read_close(self._a)
+ _libarchive.archive_read_free(self._a)
+ else:
+ _libarchive.archive_write_close(self._a)
+ _libarchive.archive_write_free(self._a)
+ finally:
+ # We only want one try at this...
+ self._a = None
+
+ def close(self, _defer=False):
+ # _defer == True is how a stream can notify Archive that the stream is
+ # now closed. Calling it directly in not recommended.
+ if _defer:
+ # This call came from our open stream.
+ self._stream = None
+ if not self._defer_close:
+ # We are not yet ready to close.
+ return
+ if self._stream is not None:
+ # We have a stream open! don't close, but remember we were asked to.
+ self._defer_close = True
+ return
+ self.denit()
+ # If there is a file attached...
+ if getattr(self, 'file_handle', None):
+ # Make sure it is not already closed...
+ if getattr(self.file_handle, 'closed', False):
+ return
+ # Flush it if not read-only...
+ if not self.file_handle.mode.startswith('r'):
+ self.file_handle.flush()
+ if self.fsync:
+ os.fsync(self.fd)
+ # and then close it, if we opened it...
+ if getattr(self, 'close', None):
+ self.file_handle.close()
+
+ @property
+ def header_position(self):
+ '''The position within the file.'''
+ return _libarchive.archive_read_header_position(self._a)
+
+ def iterpaths(self):
+ for entry in self:
+ yield entry.pathname
+
+ def read(self, size):
+ '''Read current archive entry contents into string.'''
+ return _libarchive.archive_read_data_into_str(self._a, size)
+
+ def readpath(self, f):
+ '''Write current archive entry contents to file. f can be a file-like object or
+ a path.'''
+ with contextlib2.ExitStack() as exit_stack:
+ if isinstance(f, six.string_types):
+ basedir = os.path.basename(f)
+ if not os.path.exists(basedir):
+ os.makedirs(basedir)
+ f = exit_stack.enter_context(open(f, 'wb'))
+ return _libarchive.archive_read_data_into_fd(self._a, f.fileno())
+
+ def readstream(self, size):
+ '''Returns a file-like object for reading current archive entry contents.'''
+ self._stream = EntryReadStream(self, size)
+ return self._stream
+
+ def write(self, member, data=None):
+ '''Writes a string buffer to the archive as the given entry.'''
+ if isinstance(member, six.string_types):
+ if self.fixed_mtime is None:
+ mtime = time.time()
+ else:
+ mtime = self.fixed_mtime
+ # Use default mode
+ member = self.entry_class(pathname=member, encoding=self.encoding, mtime=mtime, mode=stat.S_IFREG | 0o755)
+ if data:
+ member.size = len(data)
+ member.to_archive(self)
+ if data:
+ _libarchive.archive_write_data_from_str(self._a, data)
+ _libarchive.archive_write_finish_entry(self._a)
+
+ def writepath(self, f, pathname=None):
+ '''Writes a file to the archive. f can be a file-like object or a path. Uses
+ write() to do the actual writing.'''
+ member = self.entry_class.from_file(f, encoding=self.encoding, mtime=self.fixed_mtime)
+
+ with contextlib2.ExitStack() as exit_stack:
+ if isinstance(f, six.string_types):
+ if os.path.isfile(f):
+ f = exit_stack.enter_context(open(f, 'rb'))
+ if pathname:
+ member.pathname = pathname
+
+ # hardlinks and symlink has no data to be written
+ if member.id in self.members:
+ member.hardlink = self.members[member.id]
+ self.write(member)
+ return
+ elif member.issym():
+ self.write(member)
+ elif hasattr(f, 'read') and hasattr(f, 'seek') and hasattr(f, 'tell'):
+ self.write_from_file_object(member, f)
+ elif hasattr(f, 'read'):
+ # TODO: optimize this to write directly from f to archive.
+ self.write(member, data=f.read())
+ else:
+ self.write(member)
+
+ if member.id:
+ self.members[member.id] = member.pathname
+
+ def write_from_file_object(self, member, fileobj):
+ if isinstance(member, six.string_types):
+ member = self.entry_class(pathname=member, encoding=self.encoding, mtime=self.fixed_mtime)
+
+ start = fileobj.tell()
+ fileobj.seek(0, os.SEEK_END)
+ size = fileobj.tell() - start
+ fileobj.seek(start, os.SEEK_SET)
+
+ if size:
+ member.size = size
+ member.to_archive(self)
+
+ while size:
+ data = fileobj.read(BLOCK_SIZE)
+ if not data:
+ break
+
+ size -= len(data)
+ if size < 0:
+ msg = "File ({}) size has changed. Can't write more data than was declared in the tar header ({}). " \
+ "(probably file was changed during archiving)".format(member.pathname, member.size)
+ logger.warning(msg)
+ # write rest expected data (size is negative)
+ _libarchive.archive_write_data_from_str(self._a, data[:size])
+ break
+
+ _libarchive.archive_write_data_from_str(self._a, data)
+
+ _libarchive.archive_write_finish_entry(self._a)
+
+ def writestream(self, pathname, size=None):
+ '''Returns a file-like object for writing a new entry.'''
+ self._stream = EntryWriteStream(self, pathname, size)
+ return self._stream
+
+ def printlist(self, s=sys.stdout):
+ for entry in self:
+ s.write(entry.size)
+ s.write('\t')
+ s.write(entry.mtime.strftime(MTIME_FORMAT))
+ s.write('\t')
+ s.write(entry.pathname)
+ s.flush()
+
+
+class SeekableArchive(Archive):
+ '''A class that provides random-access to archive entries. It does this by using one
+ or many Archive instances to seek to the correct location. The best performance will
+ occur when reading archive entries in the order in which they appear in the archive.
+ Reading out of order will cause the archive to be closed and opened each time a
+ reverse seek is needed.'''
+ def __init__(self, f, **kwargs):
+ self._stream = None
+ # Convert file to open file. We need this to reopen the archive.
+ mode = kwargs.setdefault('mode', 'rb')
+ if isinstance(f, six.string_types):
+ f = open(f, mode)
+ super(SeekableArchive, self).__init__(f, **kwargs)
+ self.entries = []
+ self.eof = False
+
+ def __iter__(self):
+ for entry in self.entries:
+ yield entry
+ if not self.eof:
+ try:
+ for entry in super(SeekableArchive, self).__iter__():
+ self.entries.append(entry)
+ yield entry
+ except StopIteration:
+ self.eof = True
+
+ def reopen(self):
+ '''Seeks the underlying fd to 0 position, then opens the archive. If the archive
+ is already open, this will effectively re-open it (rewind to the beginning).'''
+ self.denit()
+ self.file_handle.seek(0)
+ self.init()
+
+ def getentry(self, pathname):
+ '''Take a name or entry object and returns an entry object.'''
+ for entry in self:
+ if entry.pathname == pathname:
+ return entry
+ raise KeyError(pathname)
+
+ def seek(self, entry):
+ '''Seeks the archive to the requested entry. Will reopen if necessary.'''
+ move = entry.header_position - self.header_position
+ if move != 0:
+ if move < 0:
+ # can't move back, re-open archive:
+ self.reopen()
+ # move to proper position in stream
+ for curr in super(SeekableArchive, self).__iter__():
+ if curr.header_position == entry.header_position:
+ break
+
+ def read(self, member):
+ '''Return the requested archive entry contents as a string.'''
+ entry = self.getentry(member)
+ self.seek(entry)
+ return super(SeekableArchive, self).read(entry.size)
+
+ def readpath(self, member, f):
+ entry = self.getentry(member)
+ self.seek(entry)
+ return super(SeekableArchive, self).readpath(f)
+
+ def readstream(self, member):
+ '''Returns a file-like object for reading requested archive entry contents.'''
+ entry = self.getentry(member)
+ self.seek(entry)
+ self._stream = EntryReadStream(self, entry.size)
+ return self._stream
diff --git a/contrib/python/python-libarchive/py3/libarchive/_libarchive.swg b/contrib/python/python-libarchive/py3/libarchive/_libarchive.swg
new file mode 100644
index 0000000000..2fcb05420e
--- /dev/null
+++ b/contrib/python/python-libarchive/py3/libarchive/_libarchive.swg
@@ -0,0 +1,339 @@
+/* Copyright (c) 2011, SmartFile <btimby@smartfile.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * 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.
+ * Neither the name of the organization nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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. */
+
+%module _libarchive
+
+%{
+#define SWIG_PYTHON_STRICT_BYTE_CHAR
+
+#include <archive.h>
+#include <archive_entry.h>
+%}
+
+%include "typemaps.i"
+
+%typemap(in) time_t
+{
+ if (PyLong_Check($input))
+ $1 = (time_t) PyLong_AsLong($input);
+ else if (PyInt_Check($input))
+ $1 = (time_t) PyInt_AsLong($input);
+ else if (PyFloat_Check($input))
+ $1 = (time_t) PyFloat_AsDouble($input);
+ else {
+ PyErr_SetString(PyExc_TypeError,"Expected a large number");
+ return NULL;
+ }
+}
+
+%typemap(out) time_t
+{
+ $result = PyLong_FromLong((long)$1);
+}
+
+%typemap(in) int64_t
+{
+ if (PyLong_Check($input))
+ $1 = (int64_t) PyLong_AsLong($input);
+ else if (PyInt_Check($input))
+ $1 = (int64_t) PyInt_AsLong($input);
+ else if (PyFloat_Check($input))
+ $1 = (int64_t) PyFloat_AsDouble($input);
+ else {
+ PyErr_SetString(PyExc_TypeError,"Expected a large number");
+ return NULL;
+ }
+}
+
+%typemap(out) int64_t
+{
+ $result = PyLong_FromLong((long)$1);
+}
+
+#define __LA_INT64_T long long
+#define __LA_MODE_T int
+
+/* STRUCTURES */
+struct archive;
+struct archive_entry;
+
+/* ARCHIVE READING */
+extern struct archive *archive_read_new(void);
+extern int archive_read_free(struct archive *);
+
+/* opening */
+extern int archive_read_open_filename(struct archive *,
+ const char *_filename, size_t _block_size);
+extern int archive_read_open_memory(struct archive *,
+ void * buff, size_t size);
+extern int archive_read_open_memory2(struct archive *a, void *buff,
+ size_t size, size_t read_size);
+extern int archive_read_open_fd(struct archive *, int _fd,
+ size_t _block_size);
+
+/* closing */
+extern int archive_read_close(struct archive *);
+extern int archive_format(struct archive *);
+
+/* headers */
+extern int archive_read_next_header2(struct archive *,
+ struct archive_entry *);
+extern const struct stat *archive_entry_stat(struct archive_entry *);
+extern __LA_INT64_T archive_read_header_position(struct archive *);
+
+/* data */
+extern int archive_read_data_skip(struct archive *);
+extern int archive_read_data_into_fd(struct archive *, int fd);
+
+/* FILTERS */
+extern int archive_read_support_filter_all(struct archive *);
+extern int archive_read_support_filter_bzip2(struct archive *);
+extern int archive_read_support_filter_compress(struct archive *);
+extern int archive_read_support_filter_gzip(struct archive *);
+extern int archive_read_support_filter_lzip(struct archive *);
+extern int archive_read_support_filter_lzma(struct archive *);
+extern int archive_read_support_filter_none(struct archive *);
+extern int archive_read_support_filter_rpm(struct archive *);
+extern int archive_read_support_filter_uu(struct archive *);
+extern int archive_read_support_filter_xz(struct archive *);
+extern int archive_read_support_filter_zstd(struct archive *);
+
+extern int archive_filter_count(struct archive *);
+extern const char * archive_filter_name(struct archive *, int);
+
+/* FORMATS */
+extern int archive_read_support_format_all(struct archive *);
+extern int archive_read_support_format_7zip(struct archive *);
+extern int archive_read_support_format_ar(struct archive *);
+extern int archive_read_support_format_cab(struct archive *);
+extern int archive_read_support_format_cpio(struct archive *);
+extern int archive_read_support_format_empty(struct archive *);
+extern int archive_read_support_format_gnutar(struct archive *);
+extern int archive_read_support_format_iso9660(struct archive *);
+extern int archive_read_support_format_lha(struct archive *);
+/*extern int archive_read_support_format_mtree(struct archive *);*/
+extern int archive_read_support_format_rar(struct archive *);
+extern int archive_read_support_format_raw(struct archive *);
+extern int archive_read_support_format_tar(struct archive *);
+extern int archive_read_support_format_xar(struct archive *);
+extern int archive_read_support_format_zip(struct archive *);
+/*extern int archive_read_support_format_by_code(struct archive *, int);*/
+
+/* OPTIONS */
+extern int archive_write_set_bytes_in_last_block(struct archive *_a, int bytes_in_last_block);
+extern int archive_write_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v);
+extern int archive_write_zip_set_compression_deflate(struct archive *_a);
+extern int archive_write_set_format_option(struct archive *_a, const char *m, const char *o, const char *v);
+extern int archive_read_set_filter_option(struct archive *_a, const char *m, const char *o, const char *v);
+extern int archive_read_set_format_option(struct archive *_a, const char *m, const char *o, const char *v);
+
+/* ARCHIVE WRITING */
+extern struct archive *archive_write_new(void);
+extern int archive_write_free(struct archive *);
+
+/* opening */
+extern int archive_write_open(struct archive *, void *,
+ archive_open_callback *, archive_write_callback *,
+ archive_close_callback *);
+extern int archive_write_open_fd(struct archive *, int _fd);
+extern int archive_write_open_filename(struct archive *, const char *_file);
+extern int archive_write_open_filename_w(struct archive *,
+ const wchar_t *_file);
+extern int archive_write_open_memory(struct archive *,
+ void *_buffer, size_t _buffSize, size_t *_used);
+
+/* closing */
+extern int archive_write_close(struct archive *);
+
+/* headers */
+extern int archive_write_header(struct archive *,
+ struct archive_entry *);
+
+/* data */
+
+/* commit */
+extern int archive_write_finish_entry(struct archive *);
+
+/* FILTERS */
+extern int archive_write_add_filter_bzip2(struct archive *);
+extern int archive_write_add_filter_compress(struct archive *);
+extern int archive_write_add_filter_gzip(struct archive *);
+extern int archive_write_add_filter_lzip(struct archive *);
+extern int archive_write_add_filter_lzma(struct archive *);
+extern int archive_write_add_filter_none(struct archive *);
+extern int archive_write_add_filter_xz(struct archive *);
+extern int archive_write_add_filter_zstd(struct archive *);
+
+
+/* FORMATS */
+/* A convenience function to set the format based on the code or name. */
+extern int archive_write_set_format(struct archive *, int format_code);
+extern int archive_write_set_format_by_name(struct archive *,
+ const char *name);
+/* To minimize link pollution, use one or more of the following. */
+extern int archive_write_set_format_ar_bsd(struct archive *);
+extern int archive_write_set_format_ar_svr4(struct archive *);
+extern int archive_write_set_format_cpio(struct archive *);
+extern int archive_write_set_format_cpio_newc(struct archive *);
+extern int archive_write_set_format_gnutar(struct archive *);
+extern int archive_write_set_format_iso9660(struct archive *);
+/*extern int archive_write_set_format_mtree(struct archive *);*/
+/* TODO: int archive_write_set_format_old_tar(struct archive *); */
+extern int archive_write_set_format_pax(struct archive *);
+extern int archive_write_set_format_pax_restricted(struct archive *);
+extern int archive_write_set_format_shar(struct archive *);
+extern int archive_write_set_format_shar_dump(struct archive *);
+extern int archive_write_set_format_ustar(struct archive *);
+extern int archive_write_set_format_xar(struct archive *);
+extern int archive_write_set_format_zip(struct archive *);
+
+/* ARCHIVE ENTRY */
+extern struct archive_entry *archive_entry_new(void);
+extern void archive_entry_free(struct archive_entry *);
+extern const char *archive_entry_symlink(struct archive_entry *);
+extern void archive_entry_set_symlink(struct archive_entry *, const char *);
+extern const char *archive_entry_hardlink(struct archive_entry *);
+extern void archive_entry_set_hardlink(struct archive_entry *, const char *);
+
+/* ARCHIVE ENTRY PROPERTY ACCESS */
+/* reading */
+extern const char *archive_entry_pathname(struct archive_entry *);
+extern const wchar_t *archive_entry_pathname_w(struct archive_entry *);
+extern __LA_INT64_T archive_entry_size(struct archive_entry *);
+extern time_t archive_entry_mtime(struct archive_entry *);
+extern time_t archive_entry_mtime_nsec(struct archive_entry *);
+extern __LA_MODE_T archive_entry_filetype(struct archive_entry *);
+extern __LA_MODE_T archive_entry_perm(struct archive_entry *);
+
+/* writing */
+extern void archive_entry_set_pathname(struct archive_entry *, const char *);
+extern void archive_entry_set_size(struct archive_entry *, __LA_INT64_T);
+extern void archive_entry_set_mtime(struct archive_entry *, time_t, long);
+extern void archive_entry_set_filetype(struct archive_entry *, unsigned int);
+extern void archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
+
+
+/* ERROR HANDLING */
+extern int archive_errno(struct archive *);
+extern const char *archive_error_string(struct archive *);
+
+
+/* CONSTANTS */
+#define ARCHIVE_VERSION_NUMBER 3000001
+#define ARCHIVE_VERSION_STRING "libarchive 3.0.1b"
+#define ARCHIVE_EOF 1 /* Found end of archive. */
+#define ARCHIVE_OK 0 /* Operation was successful. */
+#define ARCHIVE_RETRY (-10) /* Retry might succeed. */
+#define ARCHIVE_WARN (-20) /* Partial success. */
+#define ARCHIVE_FAILED (-25) /* Current operation cannot complete. */
+#define ARCHIVE_FATAL (-30) /* No more operations are possible. */
+
+#define ARCHIVE_FILTER_NONE 0
+#define ARCHIVE_FILTER_GZIP 1
+#define ARCHIVE_FILTER_BZIP2 2
+#define ARCHIVE_FILTER_COMPRESS 3
+#define ARCHIVE_FILTER_PROGRAM 4
+#define ARCHIVE_FILTER_LZMA 5
+#define ARCHIVE_FILTER_XZ 6
+#define ARCHIVE_FILTER_UU 7
+#define ARCHIVE_FILTER_RPM 8
+#define ARCHIVE_FILTER_LZIP 9
+
+#define ARCHIVE_FORMAT_BASE_MASK 0xff0000
+#define ARCHIVE_FORMAT_CPIO 0x10000
+#define ARCHIVE_FORMAT_CPIO_POSIX (ARCHIVE_FORMAT_CPIO | 1)
+#define ARCHIVE_FORMAT_CPIO_BIN_LE (ARCHIVE_FORMAT_CPIO | 2)
+#define ARCHIVE_FORMAT_CPIO_BIN_BE (ARCHIVE_FORMAT_CPIO | 3)
+#define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4)
+#define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5)
+#define ARCHIVE_FORMAT_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6)
+#define ARCHIVE_FORMAT_SHAR 0x20000
+#define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1)
+#define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2)
+#define ARCHIVE_FORMAT_TAR 0x30000
+#define ARCHIVE_FORMAT_TAR_USTAR (ARCHIVE_FORMAT_TAR | 1)
+#define ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE (ARCHIVE_FORMAT_TAR | 2)
+#define ARCHIVE_FORMAT_TAR_PAX_RESTRICTED (ARCHIVE_FORMAT_TAR | 3)
+#define ARCHIVE_FORMAT_TAR_GNUTAR (ARCHIVE_FORMAT_TAR | 4)
+#define ARCHIVE_FORMAT_ISO9660 0x40000
+#define ARCHIVE_FORMAT_ISO9660_ROCKRIDGE (ARCHIVE_FORMAT_ISO9660 | 1)
+#define ARCHIVE_FORMAT_ZIP 0x50000
+#define ARCHIVE_FORMAT_EMPTY 0x60000
+#define ARCHIVE_FORMAT_AR 0x70000
+#define ARCHIVE_FORMAT_AR_GNU (ARCHIVE_FORMAT_AR | 1)
+#define ARCHIVE_FORMAT_AR_BSD (ARCHIVE_FORMAT_AR | 2)
+#define ARCHIVE_FORMAT_MTREE 0x80000
+#define ARCHIVE_FORMAT_RAW 0x90000
+#define ARCHIVE_FORMAT_XAR 0xA0000
+#define ARCHIVE_FORMAT_LHA 0xB0000
+#define ARCHIVE_FORMAT_CAB 0xC0000
+#define ARCHIVE_FORMAT_RAR 0xD0000
+#define ARCHIVE_FORMAT_7ZIP 0xE0000
+
+#define ARCHIVE_EXTRACT_OWNER (0x0001)
+#define ARCHIVE_EXTRACT_PERM (0x0002)
+#define ARCHIVE_EXTRACT_TIME (0x0004)
+#define ARCHIVE_EXTRACT_NO_OVERWRITE (0x0008)
+#define ARCHIVE_EXTRACT_UNLINK (0x0010)
+#define ARCHIVE_EXTRACT_ACL (0x0020)
+#define ARCHIVE_EXTRACT_FFLAGS (0x0040)
+#define ARCHIVE_EXTRACT_XATTR (0x0080)
+#define ARCHIVE_EXTRACT_SECURE_SYMLINKS (0x0100)
+#define ARCHIVE_EXTRACT_SECURE_NODOTDOT (0x0200)
+#define ARCHIVE_EXTRACT_NO_AUTODIR (0x0400)
+#define ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER (0x0800)
+#define ARCHIVE_EXTRACT_SPARSE (0x1000)
+#define ARCHIVE_EXTRACT_MAC_METADATA (0x2000)
+
+%inline %{
+PyObject *archive_read_data_into_str(struct archive *archive, int len) {
+ PyObject *str = NULL;
+ if (!(str = PyBytes_FromStringAndSize(NULL, len))) {
+ PyErr_SetString(PyExc_MemoryError, "could not allocate string.");
+ return NULL;
+ }
+ if (len != archive_read_data(archive, PyBytes_AS_STRING(str), len)) {
+ PyErr_SetString(PyExc_RuntimeError, "could not read requested data.");
+ return NULL;
+ }
+ return str;
+}
+
+PyObject *archive_write_data_from_str(struct archive *archive, PyObject *str) {
+ int len = PyBytes_Size(str);
+ if (len == 0)
+ return PyInt_FromLong(len);
+ int ret = archive_write_data(archive, PyBytes_AS_STRING(str), len);
+ if (ret == ARCHIVE_FATAL) {
+ PyErr_Format(PyExc_RuntimeError, "Could not write requested data - most likely no space left on device (error code: %d)", ret);
+ return NULL;
+ }
+ else if (ret <= 0) {
+ PyErr_Format(PyExc_RuntimeError, "Could not write requested data (error code: %d)", ret);
+ return NULL;
+ }
+ return PyInt_FromLong(len);
+}
+%}
diff --git a/contrib/python/python-libarchive/py3/libarchive/tar.py b/contrib/python/python-libarchive/py3/libarchive/tar.py
new file mode 100644
index 0000000000..f14149804b
--- /dev/null
+++ b/contrib/python/python-libarchive/py3/libarchive/tar.py
@@ -0,0 +1,135 @@
+# Copyright (c) 2011, SmartFile <btimby@smartfile.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * 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.
+# * Neither the name of the organization nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+
+import time
+from libarchive import is_archive, Entry, SeekableArchive
+from tarfile import DEFAULT_FORMAT, USTAR_FORMAT, GNU_FORMAT, PAX_FORMAT, ENCODING
+from tarfile import REGTYPE, AREGTYPE, LNKTYPE, SYMTYPE, DIRTYPE, FIFOTYPE, CONTTYPE, CHRTYPE, BLKTYPE, GNUTYPE_SPARSE
+
+FORMAT_CONVERSION = {
+ USTAR_FORMAT: 'tar',
+ GNU_FORMAT: 'gnu',
+ PAX_FORMAT: 'pax',
+}
+
+
+def is_tarfile(filename):
+ return is_archive(filename, formats=('tar', 'gnu', 'pax'))
+
+
+def open(**kwargs):
+ return TarFile(**kwargs)
+
+
+class TarInfo(Entry):
+ def __init__(self, name):
+ super(TarInfo, self).__init__(pathname=name)
+
+ fromtarfile = Entry.from_archive
+
+ def get_name(self):
+ return self.pathname
+
+ def set_name(self, value):
+ self.pathname = value
+
+ name = property(get_name, set_name)
+
+ @property
+ def get_type(self):
+ for attr, type in (
+ ('isdir', DIRTYPE), ('isfile', REGTYPE), ('issym', SYMTYPE),
+ ('isfifo', FIFOTYPE), ('ischr', CHRTYPE), ('isblk', BLKTYPE),
+ ):
+ if getattr(self, attr)():
+ return type
+
+ def _get_missing(self):
+ raise NotImplemented()
+
+ def _set_missing(self, value):
+ raise NotImplemented()
+
+ pax_headers = property(_get_missing, _set_missing)
+
+
+class TarFile(SeekableArchive):
+ def __init__(self, name=None, mode='r', fileobj=None, format=DEFAULT_FORMAT, tarinfo=TarInfo, encoding=ENCODING):
+ if name:
+ f = name
+ elif fileobj:
+ f = fileobj
+ try:
+ format = FORMAT_CONVERSON.get(format)
+ except KeyError:
+ raise Exception('Invalid tar format: %s' % format)
+ super(TarFile, self).__init__(f, mode=mode, format=format, entry_class=tarinfo, encoding=encoding)
+
+ getmember = SeekableArchive.getentry
+ list = SeekableArchive.printlist
+ extract = SeekableArchive.readpath
+ extractfile = SeekableArchive.readstream
+
+ def getmembers(self):
+ return list(self)
+
+ def getnames(self):
+ return list(self.iterpaths)
+
+ def next(self):
+ pass # TODO: how to do this?
+
+ def extract(self, member, path=None):
+ if path is None:
+ path = os.getcwd()
+ if isinstance(member, basestring):
+ f = os.path.join(path, member)
+ else:
+ f = os.path.join(path, member.pathname)
+ return self.readpath(member, f)
+
+ def add(self, name, arcname, recursive=True, exclude=None, filter=None):
+ pass # TODO: implement this.
+
+ def addfile(tarinfo, fileobj):
+ return self.writepath(fileobj, tarinfo)
+
+ def gettarinfo(name=None, arcname=None, fileobj=None):
+ if name:
+ f = name
+ elif fileobj:
+ f = fileobj
+ entry = self.entry_class.from_file(f)
+ if arcname:
+ entry.pathname = arcname
+ return entry
+
+ def _get_missing(self):
+ raise NotImplemented()
+
+ def _set_missing(self, value):
+ raise NotImplemented()
+
+ pax_headers = property(_get_missing, _set_missing)
diff --git a/contrib/python/python-libarchive/py3/libarchive/zip.py b/contrib/python/python-libarchive/py3/libarchive/zip.py
new file mode 100644
index 0000000000..539f6dbcc4
--- /dev/null
+++ b/contrib/python/python-libarchive/py3/libarchive/zip.py
@@ -0,0 +1,151 @@
+# Copyright (c) 2011, SmartFile <btimby@smartfile.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * 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.
+# * Neither the name of the organization nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
+
+import os, time
+from libarchive import is_archive, Entry, SeekableArchive
+from zipfile import ZIP_STORED, ZIP_DEFLATED
+
+
+def is_zipfile(filename):
+ return is_archive(filename, formats=('zip', ))
+
+
+class ZipEntry(Entry):
+ def __init__(self, *args, **kwargs):
+ super(ZipEntry, self).__init__(*args, **kwargs)
+
+ def get_filename(self):
+ return self.pathname
+
+ def set_filename(self, value):
+ self.pathname = value
+
+ filename = property(get_filename, set_filename)
+
+ def get_file_size(self):
+ return self.size
+
+ def set_file_size(self, value):
+ assert isinstance(size, (int, long)), 'Please provide size as int or long.'
+ self.size = value
+
+ file_size = property(get_file_size, set_file_size)
+
+ def get_date_time(self):
+ return time.localtime(self.mtime)[0:6]
+
+ def set_date_time(self, value):
+ assert isinstance(value, tuple), 'mtime should be tuple (year, month, day, hour, minute, second).'
+ assert len(value) == 6, 'mtime should be tuple (year, month, day, hour, minute, second).'
+ self.mtime = time.mktime(value + (0, 0, 0))
+
+ date_time = property(get_date_time, set_date_time)
+
+ header_offset = Entry.header_position
+
+ def _get_missing(self):
+ raise NotImplemented()
+
+ def _set_missing(self, value):
+ raise NotImplemented()
+
+ compress_type = property(_get_missing, _set_missing)
+ comment = property(_get_missing, _set_missing)
+ extra = property(_get_missing, _set_missing)
+ create_system = property(_get_missing, _set_missing)
+ create_version = property(_get_missing, _set_missing)
+ extract_version = property(_get_missing, _set_missing)
+ reserved = property(_get_missing, _set_missing)
+ flag_bits = property(_get_missing, _set_missing)
+ volume = property(_get_missing, _set_missing)
+ internal_attr = property(_get_missing, _set_missing)
+ external_attr = property(_get_missing, _set_missing)
+ CRC = property(_get_missing, _set_missing)
+ compress_size = property(_get_missing, _set_missing)
+
+
+class ZipFile(SeekableArchive):
+ def __init__(self, f, mode='r', compression=ZIP_DEFLATED, allowZip64=False):
+ super(ZipFile, self).__init__(f, mode=mode, format='zip', entry_class=ZipEntry, encoding='CP437')
+ if mode == 'w' and compression == ZIP_STORED:
+ # Disable compression for writing.
+ _libarchive.archive_write_set_format_option(self.archive._a, "zip", "compression", "store")
+ self.compression = compression
+
+ getinfo = SeekableArchive.getentry
+
+ def namelist(self):
+ return list(self.iterpaths)
+
+ def infolist(self):
+ return list(self)
+
+ def open(self, name, mode, pwd=None):
+ if pwd:
+ raise NotImplemented('Encryption not supported.')
+ if mode == 'r':
+ return self.readstream(name)
+ else:
+ return self.writestream(name)
+
+ def extract(self, name, path=None, pwd=None):
+ if pwd:
+ raise NotImplemented('Encryption not supported.')
+ if not path:
+ path = os.getcwd()
+ return self.readpath(name, os.path.join(path, name))
+
+ def extractall(self, path, names=None, pwd=None):
+ if pwd:
+ raise NotImplemented('Encryption not supported.')
+ if not names:
+ names = self.namelist()
+ if names:
+ for name in names:
+ self.extract(name, path)
+
+ def read(self, name, pwd=None):
+ if pwd:
+ raise NotImplemented('Encryption not supported.')
+ return self.read(name)
+
+ def writestr(self, member, data, compress_type=None):
+ if compress_type != self.compression:
+ raise Exception('Cannot change compression type for individual entries.')
+ return self.write(member, data)
+
+ def setpassword(self, pwd):
+ raise NotImplemented('Encryption not supported.')
+
+ def testzip(self):
+ raise NotImplemented()
+
+ def _get_missing(self):
+ raise NotImplemented()
+
+ def _set_missing(self, value):
+ raise NotImplemented()
+
+ comment = property(_get_missing, _set_missing)
diff --git a/contrib/python/python-libarchive/py3/ya.make b/contrib/python/python-libarchive/py3/ya.make
new file mode 100644
index 0000000000..a905f47a12
--- /dev/null
+++ b/contrib/python/python-libarchive/py3/ya.make
@@ -0,0 +1,28 @@
+PY3_LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+VERSION(3.1.2.post1)
+
+PEERDIR(
+ contrib/libs/libarchive
+ contrib/python/contextlib2
+ contrib/python/six
+)
+
+ADDINCL(
+ contrib/libs/libarchive/libarchive
+)
+
+NO_LINT()
+
+PY_SRCS(
+ SWIG_C
+ TOP_LEVEL
+ libarchive/__init__.py
+ libarchive/tar.py
+ libarchive/zip.py
+ libarchive/_libarchive.swg
+)
+
+END()
diff --git a/contrib/python/python-libarchive/ya.make b/contrib/python/python-libarchive/ya.make
new file mode 100644
index 0000000000..112b869160
--- /dev/null
+++ b/contrib/python/python-libarchive/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/python-libarchive/py2)
+ELSE()
+ PEERDIR(contrib/python/python-libarchive/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/python-magic/py2/LICENSE b/contrib/python/python-magic/py2/LICENSE
new file mode 100644
index 0000000000..b8ca4b96dd
--- /dev/null
+++ b/contrib/python/python-magic/py2/LICENSE
@@ -0,0 +1,58 @@
+The MIT License (MIT)
+
+Copyright (c) 2001-2014 Adam Hupp
+
+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.
+
+
+====
+
+Portions of this package (magic/compat.py and test/libmagic_test.py)
+are distributed under the following copyright notice:
+
+
+$File: LEGAL.NOTICE,v 1.15 2006/05/03 18:48:33 christos Exp $
+Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995.
+Software written by Ian F. Darwin and others;
+maintained 1994- Christos Zoulas.
+
+This software is not subject to any export provision of the United States
+Department of Commerce, and may be exported to any country or planet.
+
+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 immediately at the beginning of the file, without modification,
+ 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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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/contrib/python/python-magic/py2/README.md b/contrib/python/python-magic/py2/README.md
new file mode 100644
index 0000000000..9eb70e8a30
--- /dev/null
+++ b/contrib/python/python-magic/py2/README.md
@@ -0,0 +1,144 @@
+# python-magic
+[![PyPI version](https://badge.fury.io/py/python-magic.svg)](https://badge.fury.io/py/python-magic)
+[![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic) [![Join the chat at https://gitter.im/ahupp/python-magic](https://badges.gitter.im/ahupp/python-magic.svg)](https://gitter.im/ahupp/python-magic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+python-magic is a Python interface to the libmagic file type
+identification library. libmagic identifies file types by checking
+their headers according to a predefined list of file types. This
+functionality is exposed to the command line by the Unix command
+`file`.
+
+## Usage
+
+```python
+>>> import magic
+>>> magic.from_file("testdata/test.pdf")
+'PDF document, version 1.2'
+# recommend using at least the first 2048 bytes, as less can produce incorrect identification
+>>> magic.from_buffer(open("testdata/test.pdf", "rb").read(2048))
+'PDF document, version 1.2'
+>>> magic.from_file("testdata/test.pdf", mime=True)
+'application/pdf'
+```
+
+There is also a `Magic` class that provides more direct control,
+including overriding the magic database file and turning on character
+encoding detection. This is not recommended for general use. In
+particular, it's not safe for sharing across multiple threads and
+will fail throw if this is attempted.
+
+```python
+>>> f = magic.Magic(uncompress=True)
+>>> f.from_file('testdata/test.gz')
+'ASCII text (gzip compressed data, was "test", last modified: Sat Jun 28
+21:32:52 2008, from Unix)'
+```
+
+You can also combine the flag options:
+
+```python
+>>> f = magic.Magic(mime=True, uncompress=True)
+>>> f.from_file('testdata/test.gz')
+'text/plain'
+```
+
+## Installation
+
+The current stable version of python-magic is available on PyPI and
+can be installed by running `pip install python-magic`.
+
+Other sources:
+
+- PyPI: http://pypi.python.org/pypi/python-magic/
+- GitHub: https://github.com/ahupp/python-magic
+
+This module is a simple wrapper around the libmagic C library, and
+that must be installed as well:
+
+### Debian/Ubuntu
+
+```
+sudo apt-get install libmagic1
+```
+
+### Windows
+
+You'll need DLLs for libmagic. @julian-r maintains a pypi package with the DLLs, you can fetch it with:
+
+```
+pip install python-magic-bin
+```
+
+### OSX
+
+- When using Homebrew: `brew install libmagic`
+- When using macports: `port install file`
+
+### Troubleshooting
+
+- 'MagicException: could not find any magic files!': some
+ installations of libmagic do not correctly point to their magic
+ database file. Try specifying the path to the file explicitly in the
+ constructor: `magic.Magic(magic_file="path_to_magic_file")`.
+
+- 'WindowsError: [Error 193] %1 is not a valid Win32 application':
+ Attempting to run the 32-bit libmagic DLL in a 64-bit build of
+ python will fail with this error. Here are 64-bit builds of libmagic for windows: https://github.com/pidydx/libmagicwin64.
+ Newer version can be found here: https://github.com/nscaife/file-windows.
+
+- 'WindowsError: exception: access violation writing 0x00000000 ' This may indicate you are mixing
+ Windows Python and Cygwin Python. Make sure your libmagic and python builds are consistent.
+
+
+## Bug Reports
+
+python-magic is a thin layer over the libmagic C library.
+Historically, most bugs that have been reported against python-magic
+are actually bugs in libmagic; libmagic bugs can be reported on their
+tracker here: https://bugs.astron.com/my_view_page.php. If you're not
+sure where the bug lies feel free to file an issue on GitHub and I can
+triage it.
+
+## Running the tests
+
+To run the tests across a variety of linux distributions (depends on Docker):
+
+```
+./test_docker.sh
+```
+
+To run tests locally across all available python versions:
+
+```
+./test/run.py
+```
+
+To run against a specific python version:
+
+```
+LC_ALL=en_US.UTF-8 python3 test/test.py
+```
+
+## libmagic python API compatibility
+
+The python bindings shipped with libmagic use a module name that conflicts with this package. To work around this, python-magic includes a compatibility layer for the libmagic API. See [COMPAT.md](COMPAT.md) for a guide to libmagic / python-magic compatibility.
+
+## Versioning
+
+Minor version bumps should be backwards compatible. Major bumps are not.
+
+## Author
+
+Written by Adam Hupp in 2001 for a project that never got off the
+ground. It originally used SWIG for the C library bindings, but
+switched to ctypes once that was part of the python standard library.
+
+You can contact me via my [website](http://hupp.org/adam) or
+[GitHub](http://github.com/ahupp).
+
+## License
+
+python-magic is distributed under the MIT license. See the included
+LICENSE file for details.
+
+I am providing code in the repository to you under an open source license. Because this is my personal repository, the license you receive to my code is from me and not my employer (Facebook).
diff --git a/contrib/python/python-magic/py3/.dist-info/METADATA b/contrib/python/python-magic/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..65b11c5555
--- /dev/null
+++ b/contrib/python/python-magic/py3/.dist-info/METADATA
@@ -0,0 +1,171 @@
+Metadata-Version: 2.1
+Name: python-magic
+Version: 0.4.27
+Summary: File type identification using libmagic
+Home-page: http://github.com/ahupp/python-magic
+Author: Adam Hupp
+Author-email: adam@hupp.org
+License: MIT
+Keywords: mime magic file
+Platform: UNKNOWN
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
+Description-Content-Type: text/markdown
+License-File: LICENSE
+
+# python-magic
+[![PyPI version](https://badge.fury.io/py/python-magic.svg)](https://badge.fury.io/py/python-magic)
+[![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic) [![Join the chat at https://gitter.im/ahupp/python-magic](https://badges.gitter.im/ahupp/python-magic.svg)](https://gitter.im/ahupp/python-magic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+python-magic is a Python interface to the libmagic file type
+identification library. libmagic identifies file types by checking
+their headers according to a predefined list of file types. This
+functionality is exposed to the command line by the Unix command
+`file`.
+
+## Usage
+
+```python
+>>> import magic
+>>> magic.from_file("testdata/test.pdf")
+'PDF document, version 1.2'
+# recommend using at least the first 2048 bytes, as less can produce incorrect identification
+>>> magic.from_buffer(open("testdata/test.pdf", "rb").read(2048))
+'PDF document, version 1.2'
+>>> magic.from_file("testdata/test.pdf", mime=True)
+'application/pdf'
+```
+
+There is also a `Magic` class that provides more direct control,
+including overriding the magic database file and turning on character
+encoding detection. This is not recommended for general use. In
+particular, it's not safe for sharing across multiple threads and
+will fail throw if this is attempted.
+
+```python
+>>> f = magic.Magic(uncompress=True)
+>>> f.from_file('testdata/test.gz')
+'ASCII text (gzip compressed data, was "test", last modified: Sat Jun 28
+21:32:52 2008, from Unix)'
+```
+
+You can also combine the flag options:
+
+```python
+>>> f = magic.Magic(mime=True, uncompress=True)
+>>> f.from_file('testdata/test.gz')
+'text/plain'
+```
+
+## Installation
+
+The current stable version of python-magic is available on PyPI and
+can be installed by running `pip install python-magic`.
+
+Other sources:
+
+- PyPI: http://pypi.python.org/pypi/python-magic/
+- GitHub: https://github.com/ahupp/python-magic
+
+This module is a simple wrapper around the libmagic C library, and
+that must be installed as well:
+
+### Debian/Ubuntu
+
+```
+sudo apt-get install libmagic1
+```
+
+### Windows
+
+You'll need DLLs for libmagic. @julian-r maintains a pypi package with the DLLs, you can fetch it with:
+
+```
+pip install python-magic-bin
+```
+
+### OSX
+
+- When using Homebrew: `brew install libmagic`
+- When using macports: `port install file`
+
+### Troubleshooting
+
+- 'MagicException: could not find any magic files!': some
+ installations of libmagic do not correctly point to their magic
+ database file. Try specifying the path to the file explicitly in the
+ constructor: `magic.Magic(magic_file="path_to_magic_file")`.
+
+- 'WindowsError: [Error 193] %1 is not a valid Win32 application':
+ Attempting to run the 32-bit libmagic DLL in a 64-bit build of
+ python will fail with this error. Here are 64-bit builds of libmagic for windows: https://github.com/pidydx/libmagicwin64.
+ Newer version can be found here: https://github.com/nscaife/file-windows.
+
+- 'WindowsError: exception: access violation writing 0x00000000 ' This may indicate you are mixing
+ Windows Python and Cygwin Python. Make sure your libmagic and python builds are consistent.
+
+
+## Bug Reports
+
+python-magic is a thin layer over the libmagic C library.
+Historically, most bugs that have been reported against python-magic
+are actually bugs in libmagic; libmagic bugs can be reported on their
+tracker here: https://bugs.astron.com/my_view_page.php. If you're not
+sure where the bug lies feel free to file an issue on GitHub and I can
+triage it.
+
+## Running the tests
+
+To run the tests across a variety of linux distributions (depends on Docker):
+
+```
+./test_docker.sh
+```
+
+To run tests locally across all available python versions:
+
+```
+./test/run.py
+```
+
+To run against a specific python version:
+
+```
+LC_ALL=en_US.UTF-8 python3 test/test.py
+```
+
+## libmagic python API compatibility
+
+The python bindings shipped with libmagic use a module name that conflicts with this package. To work around this, python-magic includes a compatibility layer for the libmagic API. See [COMPAT.md](COMPAT.md) for a guide to libmagic / python-magic compatibility.
+
+## Versioning
+
+Minor version bumps should be backwards compatible. Major bumps are not.
+
+## Author
+
+Written by Adam Hupp in 2001 for a project that never got off the
+ground. It originally used SWIG for the C library bindings, but
+switched to ctypes once that was part of the python standard library.
+
+You can contact me via my [website](http://hupp.org/adam) or
+[GitHub](http://github.com/ahupp).
+
+## License
+
+python-magic is distributed under the MIT license. See the included
+LICENSE file for details.
+
+I am providing code in the repository to you under an open source license. Because this is my personal repository, the license you receive to my code is from me and not my employer (Facebook).
+
+
diff --git a/contrib/python/python-magic/py3/.dist-info/top_level.txt b/contrib/python/python-magic/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..71c947b02f
--- /dev/null
+++ b/contrib/python/python-magic/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+magic
diff --git a/contrib/python/python-magic/py3/LICENSE b/contrib/python/python-magic/py3/LICENSE
new file mode 100644
index 0000000000..b8ca4b96dd
--- /dev/null
+++ b/contrib/python/python-magic/py3/LICENSE
@@ -0,0 +1,58 @@
+The MIT License (MIT)
+
+Copyright (c) 2001-2014 Adam Hupp
+
+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.
+
+
+====
+
+Portions of this package (magic/compat.py and test/libmagic_test.py)
+are distributed under the following copyright notice:
+
+
+$File: LEGAL.NOTICE,v 1.15 2006/05/03 18:48:33 christos Exp $
+Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995.
+Software written by Ian F. Darwin and others;
+maintained 1994- Christos Zoulas.
+
+This software is not subject to any export provision of the United States
+Department of Commerce, and may be exported to any country or planet.
+
+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 immediately at the beginning of the file, without modification,
+ 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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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/contrib/python/python-magic/py3/README.md b/contrib/python/python-magic/py3/README.md
new file mode 100644
index 0000000000..9eb70e8a30
--- /dev/null
+++ b/contrib/python/python-magic/py3/README.md
@@ -0,0 +1,144 @@
+# python-magic
+[![PyPI version](https://badge.fury.io/py/python-magic.svg)](https://badge.fury.io/py/python-magic)
+[![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic) [![Join the chat at https://gitter.im/ahupp/python-magic](https://badges.gitter.im/ahupp/python-magic.svg)](https://gitter.im/ahupp/python-magic?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+python-magic is a Python interface to the libmagic file type
+identification library. libmagic identifies file types by checking
+their headers according to a predefined list of file types. This
+functionality is exposed to the command line by the Unix command
+`file`.
+
+## Usage
+
+```python
+>>> import magic
+>>> magic.from_file("testdata/test.pdf")
+'PDF document, version 1.2'
+# recommend using at least the first 2048 bytes, as less can produce incorrect identification
+>>> magic.from_buffer(open("testdata/test.pdf", "rb").read(2048))
+'PDF document, version 1.2'
+>>> magic.from_file("testdata/test.pdf", mime=True)
+'application/pdf'
+```
+
+There is also a `Magic` class that provides more direct control,
+including overriding the magic database file and turning on character
+encoding detection. This is not recommended for general use. In
+particular, it's not safe for sharing across multiple threads and
+will fail throw if this is attempted.
+
+```python
+>>> f = magic.Magic(uncompress=True)
+>>> f.from_file('testdata/test.gz')
+'ASCII text (gzip compressed data, was "test", last modified: Sat Jun 28
+21:32:52 2008, from Unix)'
+```
+
+You can also combine the flag options:
+
+```python
+>>> f = magic.Magic(mime=True, uncompress=True)
+>>> f.from_file('testdata/test.gz')
+'text/plain'
+```
+
+## Installation
+
+The current stable version of python-magic is available on PyPI and
+can be installed by running `pip install python-magic`.
+
+Other sources:
+
+- PyPI: http://pypi.python.org/pypi/python-magic/
+- GitHub: https://github.com/ahupp/python-magic
+
+This module is a simple wrapper around the libmagic C library, and
+that must be installed as well:
+
+### Debian/Ubuntu
+
+```
+sudo apt-get install libmagic1
+```
+
+### Windows
+
+You'll need DLLs for libmagic. @julian-r maintains a pypi package with the DLLs, you can fetch it with:
+
+```
+pip install python-magic-bin
+```
+
+### OSX
+
+- When using Homebrew: `brew install libmagic`
+- When using macports: `port install file`
+
+### Troubleshooting
+
+- 'MagicException: could not find any magic files!': some
+ installations of libmagic do not correctly point to their magic
+ database file. Try specifying the path to the file explicitly in the
+ constructor: `magic.Magic(magic_file="path_to_magic_file")`.
+
+- 'WindowsError: [Error 193] %1 is not a valid Win32 application':
+ Attempting to run the 32-bit libmagic DLL in a 64-bit build of
+ python will fail with this error. Here are 64-bit builds of libmagic for windows: https://github.com/pidydx/libmagicwin64.
+ Newer version can be found here: https://github.com/nscaife/file-windows.
+
+- 'WindowsError: exception: access violation writing 0x00000000 ' This may indicate you are mixing
+ Windows Python and Cygwin Python. Make sure your libmagic and python builds are consistent.
+
+
+## Bug Reports
+
+python-magic is a thin layer over the libmagic C library.
+Historically, most bugs that have been reported against python-magic
+are actually bugs in libmagic; libmagic bugs can be reported on their
+tracker here: https://bugs.astron.com/my_view_page.php. If you're not
+sure where the bug lies feel free to file an issue on GitHub and I can
+triage it.
+
+## Running the tests
+
+To run the tests across a variety of linux distributions (depends on Docker):
+
+```
+./test_docker.sh
+```
+
+To run tests locally across all available python versions:
+
+```
+./test/run.py
+```
+
+To run against a specific python version:
+
+```
+LC_ALL=en_US.UTF-8 python3 test/test.py
+```
+
+## libmagic python API compatibility
+
+The python bindings shipped with libmagic use a module name that conflicts with this package. To work around this, python-magic includes a compatibility layer for the libmagic API. See [COMPAT.md](COMPAT.md) for a guide to libmagic / python-magic compatibility.
+
+## Versioning
+
+Minor version bumps should be backwards compatible. Major bumps are not.
+
+## Author
+
+Written by Adam Hupp in 2001 for a project that never got off the
+ground. It originally used SWIG for the C library bindings, but
+switched to ctypes once that was part of the python standard library.
+
+You can contact me via my [website](http://hupp.org/adam) or
+[GitHub](http://github.com/ahupp).
+
+## License
+
+python-magic is distributed under the MIT license. See the included
+LICENSE file for details.
+
+I am providing code in the repository to you under an open source license. Because this is my personal repository, the license you receive to my code is from me and not my employer (Facebook).
diff --git a/contrib/python/python-magic/py3/magic/__init__.py b/contrib/python/python-magic/py3/magic/__init__.py
new file mode 100644
index 0000000000..bab7c7b122
--- /dev/null
+++ b/contrib/python/python-magic/py3/magic/__init__.py
@@ -0,0 +1,469 @@
+"""
+magic is a wrapper around the libmagic file identification library.
+
+See README for more information.
+
+Usage:
+
+>>> import magic
+>>> magic.from_file("testdata/test.pdf")
+'PDF document, version 1.2'
+>>> magic.from_file("testdata/test.pdf", mime=True)
+'application/pdf'
+>>> magic.from_buffer(open("testdata/test.pdf").read(1024))
+'PDF document, version 1.2'
+>>>
+
+"""
+
+import sys
+import glob
+import ctypes
+import ctypes.util
+import threading
+import logging
+
+from ctypes import c_char_p, c_int, c_size_t, c_void_p, byref, POINTER
+
+# avoid shadowing the real open with the version from compat.py
+_real_open = open
+
+
+class MagicException(Exception):
+ def __init__(self, message):
+ super(Exception, self).__init__(message)
+ self.message = message
+
+
+class Magic:
+ """
+ Magic is a wrapper around the libmagic C library.
+ """
+
+ def __init__(self, mime=False, magic_file=None, mime_encoding=False,
+ keep_going=False, uncompress=False, raw=False, extension=False):
+ """
+ Create a new libmagic wrapper.
+
+ mime - if True, mimetypes are returned instead of textual descriptions
+ mime_encoding - if True, codec is returned
+ magic_file - use a mime database other than the system default
+ keep_going - don't stop at the first match, keep going
+ uncompress - Try to look inside compressed files.
+ raw - Do not try to decode "non-printable" chars.
+ extension - Print a slash-separated list of valid extensions for the file type found.
+ """
+ self.flags = MAGIC_NONE
+ if mime:
+ self.flags |= MAGIC_MIME_TYPE
+ if mime_encoding:
+ self.flags |= MAGIC_MIME_ENCODING
+ if keep_going:
+ self.flags |= MAGIC_CONTINUE
+ if uncompress:
+ self.flags |= MAGIC_COMPRESS
+ if raw:
+ self.flags |= MAGIC_RAW
+ if extension:
+ self.flags |= MAGIC_EXTENSION
+
+ self.cookie = magic_open(self.flags)
+ self.lock = threading.Lock()
+
+ magic_load(self.cookie, magic_file)
+
+ # MAGIC_EXTENSION was added in 523 or 524, so bail if
+ # it doesn't appear to be available
+ if extension and (not _has_version or version() < 524):
+ raise NotImplementedError('MAGIC_EXTENSION is not supported in this version of libmagic')
+
+ # For https://github.com/ahupp/python-magic/issues/190
+ # libmagic has fixed internal limits that some files exceed, causing
+ # an error. We can avoid this (at least for the sample file given)
+ # by bumping the limit up. It's not clear if this is a general solution
+ # or whether other internal limits should be increased, but given
+ # the lack of other reports I'll assume this is rare.
+ if _has_param:
+ try:
+ self.setparam(MAGIC_PARAM_NAME_MAX, 64)
+ except MagicException as e:
+ # some versions of libmagic fail this call,
+ # so rather than fail hard just use default behavior
+ pass
+
+ def from_buffer(self, buf):
+ """
+ Identify the contents of `buf`
+ """
+ with self.lock:
+ try:
+ # if we're on python3, convert buf to bytes
+ # otherwise this string is passed as wchar*
+ # which is not what libmagic expects
+ # NEXTBREAK: only take bytes
+ if type(buf) == str and str != bytes:
+ buf = buf.encode('utf-8', errors='replace')
+ return maybe_decode(magic_buffer(self.cookie, buf))
+ except MagicException as e:
+ return self._handle509Bug(e)
+
+ def from_file(self, filename):
+ # raise FileNotFoundException or IOError if the file does not exist
+ with _real_open(filename):
+ pass
+
+ with self.lock:
+ try:
+ return maybe_decode(magic_file(self.cookie, filename))
+ except MagicException as e:
+ return self._handle509Bug(e)
+
+ def from_descriptor(self, fd):
+ with self.lock:
+ try:
+ return maybe_decode(magic_descriptor(self.cookie, fd))
+ except MagicException as e:
+ return self._handle509Bug(e)
+
+ def _handle509Bug(self, e):
+ # libmagic 5.09 has a bug where it might fail to identify the
+ # mimetype of a file and returns null from magic_file (and
+ # likely _buffer), but also does not return an error message.
+ if e.message is None and (self.flags & MAGIC_MIME_TYPE):
+ return "application/octet-stream"
+ else:
+ raise e
+
+ def setparam(self, param, val):
+ return magic_setparam(self.cookie, param, val)
+
+ def getparam(self, param):
+ return magic_getparam(self.cookie, param)
+
+ def __del__(self):
+ # no _thread_check here because there can be no other
+ # references to this object at this point.
+
+ # during shutdown magic_close may have been cleared already so
+ # make sure it exists before using it.
+
+ # the self.cookie check should be unnecessary and was an
+ # incorrect fix for a threading problem, however I'm leaving
+ # it in because it's harmless and I'm slightly afraid to
+ # remove it.
+ if hasattr(self, 'cookie') and self.cookie and magic_close:
+ magic_close(self.cookie)
+ self.cookie = None
+
+
+_instances = {}
+
+
+def _get_magic_type(mime):
+ i = _instances.get(mime)
+ if i is None:
+ i = _instances[mime] = Magic(mime=mime)
+ return i
+
+
+def from_file(filename, mime=False):
+ """"
+ Accepts a filename and returns the detected filetype. Return
+ value is the mimetype if mime=True, otherwise a human readable
+ name.
+
+ >>> magic.from_file("testdata/test.pdf", mime=True)
+ 'application/pdf'
+ """
+ m = _get_magic_type(mime)
+ return m.from_file(filename)
+
+
+def from_buffer(buffer, mime=False):
+ """
+ Accepts a binary string and returns the detected filetype. Return
+ value is the mimetype if mime=True, otherwise a human readable
+ name.
+
+ >>> magic.from_buffer(open("testdata/test.pdf").read(1024))
+ 'PDF document, version 1.2'
+ """
+ m = _get_magic_type(mime)
+ return m.from_buffer(buffer)
+
+
+def from_descriptor(fd, mime=False):
+ """
+ Accepts a file descriptor and returns the detected filetype. Return
+ value is the mimetype if mime=True, otherwise a human readable
+ name.
+
+ >>> f = open("testdata/test.pdf")
+ >>> magic.from_descriptor(f.fileno())
+ 'PDF document, version 1.2'
+ """
+ m = _get_magic_type(mime)
+ return m.from_descriptor(fd)
+
+from . import loader
+libmagic = loader.load_lib()
+
+magic_t = ctypes.c_void_p
+
+
+def errorcheck_null(result, func, args):
+ if result is None:
+ err = magic_error(args[0])
+ raise MagicException(err)
+ else:
+ return result
+
+
+def errorcheck_negative_one(result, func, args):
+ if result == -1:
+ err = magic_error(args[0])
+ raise MagicException(err)
+ else:
+ return result
+
+
+# return str on python3. Don't want to unconditionally
+# decode because that results in unicode on python2
+def maybe_decode(s):
+ # NEXTBREAK: remove
+ if str == bytes:
+ return s
+ else:
+ # backslashreplace here because sometimes libmagic will return metadata in the charset
+ # of the file, which is unknown to us (e.g the title of a Word doc)
+ return s.decode('utf-8', 'backslashreplace')
+
+
+try:
+ from os import PathLike
+ def unpath(filename):
+ if isinstance(filename, PathLike):
+ return filename.__fspath__()
+ else:
+ return filename
+except ImportError:
+ def unpath(filename):
+ return filename
+
+def coerce_filename(filename):
+ if filename is None:
+ return None
+
+ filename = unpath(filename)
+
+ # ctypes will implicitly convert unicode strings to bytes with
+ # .encode('ascii'). If you use the filesystem encoding
+ # then you'll get inconsistent behavior (crashes) depending on the user's
+ # LANG environment variable
+ # NEXTBREAK: remove
+ is_unicode = (sys.version_info[0] <= 2 and
+ isinstance(filename, unicode)) or \
+ (sys.version_info[0] >= 3 and
+ isinstance(filename, str))
+ if is_unicode:
+ return filename.encode('utf-8', 'surrogateescape')
+ else:
+ return filename
+
+
+magic_open = libmagic.magic_open
+magic_open.restype = magic_t
+magic_open.argtypes = [c_int]
+
+magic_close = libmagic.magic_close
+magic_close.restype = None
+magic_close.argtypes = [magic_t]
+
+magic_error = libmagic.magic_error
+magic_error.restype = c_char_p
+magic_error.argtypes = [magic_t]
+
+magic_errno = libmagic.magic_errno
+magic_errno.restype = c_int
+magic_errno.argtypes = [magic_t]
+
+_magic_file = libmagic.magic_file
+_magic_file.restype = c_char_p
+_magic_file.argtypes = [magic_t, c_char_p]
+_magic_file.errcheck = errorcheck_null
+
+
+def magic_file(cookie, filename):
+ return _magic_file(cookie, coerce_filename(filename))
+
+
+_magic_buffer = libmagic.magic_buffer
+_magic_buffer.restype = c_char_p
+_magic_buffer.argtypes = [magic_t, c_void_p, c_size_t]
+_magic_buffer.errcheck = errorcheck_null
+
+
+def magic_buffer(cookie, buf):
+ return _magic_buffer(cookie, buf, len(buf))
+
+
+magic_descriptor = libmagic.magic_descriptor
+magic_descriptor.restype = c_char_p
+magic_descriptor.argtypes = [magic_t, c_int]
+magic_descriptor.errcheck = errorcheck_null
+
+_magic_descriptor = libmagic.magic_descriptor
+_magic_descriptor.restype = c_char_p
+_magic_descriptor.argtypes = [magic_t, c_int]
+_magic_descriptor.errcheck = errorcheck_null
+
+
+def magic_descriptor(cookie, fd):
+ return _magic_descriptor(cookie, fd)
+
+
+_magic_load = libmagic.magic_load
+_magic_load.restype = c_int
+_magic_load.argtypes = [magic_t, c_char_p]
+_magic_load.errcheck = errorcheck_negative_one
+
+
+def magic_load(cookie, filename):
+ return _magic_load(cookie, coerce_filename(filename))
+
+
+magic_setflags = libmagic.magic_setflags
+magic_setflags.restype = c_int
+magic_setflags.argtypes = [magic_t, c_int]
+
+magic_check = libmagic.magic_check
+magic_check.restype = c_int
+magic_check.argtypes = [magic_t, c_char_p]
+
+magic_compile = libmagic.magic_compile
+magic_compile.restype = c_int
+magic_compile.argtypes = [magic_t, c_char_p]
+
+_has_param = False
+if hasattr(libmagic, 'magic_setparam') and hasattr(libmagic, 'magic_getparam'):
+ _has_param = True
+ _magic_setparam = libmagic.magic_setparam
+ _magic_setparam.restype = c_int
+ _magic_setparam.argtypes = [magic_t, c_int, POINTER(c_size_t)]
+ _magic_setparam.errcheck = errorcheck_negative_one
+
+ _magic_getparam = libmagic.magic_getparam
+ _magic_getparam.restype = c_int
+ _magic_getparam.argtypes = [magic_t, c_int, POINTER(c_size_t)]
+ _magic_getparam.errcheck = errorcheck_negative_one
+
+
+def magic_setparam(cookie, param, val):
+ if not _has_param:
+ raise NotImplementedError("magic_setparam not implemented")
+ v = c_size_t(val)
+ return _magic_setparam(cookie, param, byref(v))
+
+
+def magic_getparam(cookie, param):
+ if not _has_param:
+ raise NotImplementedError("magic_getparam not implemented")
+ val = c_size_t()
+ _magic_getparam(cookie, param, byref(val))
+ return val.value
+
+
+_has_version = False
+if hasattr(libmagic, "magic_version"):
+ _has_version = True
+ magic_version = libmagic.magic_version
+ magic_version.restype = c_int
+ magic_version.argtypes = []
+
+
+def version():
+ if not _has_version:
+ raise NotImplementedError("magic_version not implemented")
+ return magic_version()
+
+
+MAGIC_NONE = 0x000000 # No flags
+MAGIC_DEBUG = 0x000001 # Turn on debugging
+MAGIC_SYMLINK = 0x000002 # Follow symlinks
+MAGIC_COMPRESS = 0x000004 # Check inside compressed files
+MAGIC_DEVICES = 0x000008 # Look at the contents of devices
+MAGIC_MIME_TYPE = 0x000010 # Return a mime string
+MAGIC_MIME_ENCODING = 0x000400 # Return the MIME encoding
+# TODO: should be
+# MAGIC_MIME = MAGIC_MIME_TYPE | MAGIC_MIME_ENCODING
+MAGIC_MIME = 0x000010 # Return a mime string
+MAGIC_EXTENSION = 0x1000000 # Return a /-separated list of extensions
+
+MAGIC_CONTINUE = 0x000020 # Return all matches
+MAGIC_CHECK = 0x000040 # Print warnings to stderr
+MAGIC_PRESERVE_ATIME = 0x000080 # Restore access time on exit
+MAGIC_RAW = 0x000100 # Don't translate unprintable chars
+MAGIC_ERROR = 0x000200 # Handle ENOENT etc as real errors
+
+MAGIC_NO_CHECK_COMPRESS = 0x001000 # Don't check for compressed files
+MAGIC_NO_CHECK_TAR = 0x002000 # Don't check for tar files
+MAGIC_NO_CHECK_SOFT = 0x004000 # Don't check magic entries
+MAGIC_NO_CHECK_APPTYPE = 0x008000 # Don't check application type
+MAGIC_NO_CHECK_ELF = 0x010000 # Don't check for elf details
+MAGIC_NO_CHECK_ASCII = 0x020000 # Don't check for ascii files
+MAGIC_NO_CHECK_TROFF = 0x040000 # Don't check ascii/troff
+MAGIC_NO_CHECK_FORTRAN = 0x080000 # Don't check ascii/fortran
+MAGIC_NO_CHECK_TOKENS = 0x100000 # Don't check ascii/tokens
+
+MAGIC_PARAM_INDIR_MAX = 0 # Recursion limit for indirect magic
+MAGIC_PARAM_NAME_MAX = 1 # Use count limit for name/use magic
+MAGIC_PARAM_ELF_PHNUM_MAX = 2 # Max ELF notes processed
+MAGIC_PARAM_ELF_SHNUM_MAX = 3 # Max ELF program sections processed
+MAGIC_PARAM_ELF_NOTES_MAX = 4 # # Max ELF sections processed
+MAGIC_PARAM_REGEX_MAX = 5 # Length limit for regex searches
+MAGIC_PARAM_BYTES_MAX = 6 # Max number of bytes to read from file
+
+
+# This package name conflicts with the one provided by upstream
+# libmagic. This is a common source of confusion for users. To
+# resolve, We ship a copy of that module, and expose it's functions
+# wrapped in deprecation warnings.
+def _add_compat(to_module):
+ import warnings, re
+ from magic import compat
+
+ def deprecation_wrapper(fn):
+ def _(*args, **kwargs):
+ warnings.warn(
+ "Using compatibility mode with libmagic's python binding. "
+ "See https://github.com/ahupp/python-magic/blob/master/COMPAT.md for details.",
+ PendingDeprecationWarning)
+
+ return fn(*args, **kwargs)
+
+ return _
+
+ fn = ['detect_from_filename',
+ 'detect_from_content',
+ 'detect_from_fobj',
+ 'open']
+ for fname in fn:
+ to_module[fname] = deprecation_wrapper(compat.__dict__[fname])
+
+ # copy constants over, ensuring there's no conflicts
+ is_const_re = re.compile("^[A-Z_]+$")
+ allowed_inconsistent = set(['MAGIC_MIME'])
+ for name, value in compat.__dict__.items():
+ if is_const_re.match(name):
+ if name in to_module:
+ if name in allowed_inconsistent:
+ continue
+ if to_module[name] != value:
+ raise Exception("inconsistent value for " + name)
+ else:
+ continue
+ else:
+ to_module[name] = value
+
+
+_add_compat(globals())
diff --git a/contrib/python/python-magic/py3/magic/compat.py b/contrib/python/python-magic/py3/magic/compat.py
new file mode 100644
index 0000000000..07fad45a8b
--- /dev/null
+++ b/contrib/python/python-magic/py3/magic/compat.py
@@ -0,0 +1,287 @@
+# coding: utf-8
+
+'''
+Python bindings for libmagic
+'''
+
+import ctypes
+
+from collections import namedtuple
+
+from ctypes import *
+from ctypes.util import find_library
+
+
+from . import loader
+
+_libraries = {}
+_libraries['magic'] = loader.load_lib()
+
+# Flag constants for open and setflags
+MAGIC_NONE = NONE = 0
+MAGIC_DEBUG = DEBUG = 1
+MAGIC_SYMLINK = SYMLINK = 2
+MAGIC_COMPRESS = COMPRESS = 4
+MAGIC_DEVICES = DEVICES = 8
+MAGIC_MIME_TYPE = MIME_TYPE = 16
+MAGIC_CONTINUE = CONTINUE = 32
+MAGIC_CHECK = CHECK = 64
+MAGIC_PRESERVE_ATIME = PRESERVE_ATIME = 128
+MAGIC_RAW = RAW = 256
+MAGIC_ERROR = ERROR = 512
+MAGIC_MIME_ENCODING = MIME_ENCODING = 1024
+MAGIC_MIME = MIME = 1040 # MIME_TYPE + MIME_ENCODING
+MAGIC_APPLE = APPLE = 2048
+
+MAGIC_NO_CHECK_COMPRESS = NO_CHECK_COMPRESS = 4096
+MAGIC_NO_CHECK_TAR = NO_CHECK_TAR = 8192
+MAGIC_NO_CHECK_SOFT = NO_CHECK_SOFT = 16384
+MAGIC_NO_CHECK_APPTYPE = NO_CHECK_APPTYPE = 32768
+MAGIC_NO_CHECK_ELF = NO_CHECK_ELF = 65536
+MAGIC_NO_CHECK_TEXT = NO_CHECK_TEXT = 131072
+MAGIC_NO_CHECK_CDF = NO_CHECK_CDF = 262144
+MAGIC_NO_CHECK_TOKENS = NO_CHECK_TOKENS = 1048576
+MAGIC_NO_CHECK_ENCODING = NO_CHECK_ENCODING = 2097152
+
+MAGIC_NO_CHECK_BUILTIN = NO_CHECK_BUILTIN = 4173824
+
+FileMagic = namedtuple('FileMagic', ('mime_type', 'encoding', 'name'))
+
+
+class magic_set(Structure):
+ pass
+
+
+magic_set._fields_ = []
+magic_t = POINTER(magic_set)
+
+_open = _libraries['magic'].magic_open
+_open.restype = magic_t
+_open.argtypes = [c_int]
+
+_close = _libraries['magic'].magic_close
+_close.restype = None
+_close.argtypes = [magic_t]
+
+_file = _libraries['magic'].magic_file
+_file.restype = c_char_p
+_file.argtypes = [magic_t, c_char_p]
+
+_descriptor = _libraries['magic'].magic_descriptor
+_descriptor.restype = c_char_p
+_descriptor.argtypes = [magic_t, c_int]
+
+_buffer = _libraries['magic'].magic_buffer
+_buffer.restype = c_char_p
+_buffer.argtypes = [magic_t, c_void_p, c_size_t]
+
+_error = _libraries['magic'].magic_error
+_error.restype = c_char_p
+_error.argtypes = [magic_t]
+
+_setflags = _libraries['magic'].magic_setflags
+_setflags.restype = c_int
+_setflags.argtypes = [magic_t, c_int]
+
+_load = _libraries['magic'].magic_load
+_load.restype = c_int
+_load.argtypes = [magic_t, c_char_p]
+
+_compile = _libraries['magic'].magic_compile
+_compile.restype = c_int
+_compile.argtypes = [magic_t, c_char_p]
+
+_check = _libraries['magic'].magic_check
+_check.restype = c_int
+_check.argtypes = [magic_t, c_char_p]
+
+_list = _libraries['magic'].magic_list
+_list.restype = c_int
+_list.argtypes = [magic_t, c_char_p]
+
+_errno = _libraries['magic'].magic_errno
+_errno.restype = c_int
+_errno.argtypes = [magic_t]
+
+
+class Magic(object):
+ def __init__(self, ms):
+ self._magic_t = ms
+
+ def close(self):
+ """
+ Closes the magic database and deallocates any resources used.
+ """
+ _close(self._magic_t)
+
+ @staticmethod
+ def __tostr(s):
+ if s is None:
+ return None
+ if isinstance(s, str):
+ return s
+ try: # keep Python 2 compatibility
+ return str(s, 'utf-8')
+ except TypeError:
+ return str(s)
+
+ @staticmethod
+ def __tobytes(b):
+ if b is None:
+ return None
+ if isinstance(b, bytes):
+ return b
+ try: # keep Python 2 compatibility
+ return bytes(b, 'utf-8')
+ except TypeError:
+ return bytes(b)
+
+ def file(self, filename):
+ """
+ Returns a textual description of the contents of the argument passed
+ as a filename or None if an error occurred and the MAGIC_ERROR flag
+ is set. A call to errno() will return the numeric error code.
+ """
+ return Magic.__tostr(_file(self._magic_t, Magic.__tobytes(filename)))
+
+ def descriptor(self, fd):
+ """
+ Returns a textual description of the contents of the argument passed
+ as a file descriptor or None if an error occurred and the MAGIC_ERROR
+ flag is set. A call to errno() will return the numeric error code.
+ """
+ return Magic.__tostr(_descriptor(self._magic_t, fd))
+
+ def buffer(self, buf):
+ """
+ Returns a textual description of the contents of the argument passed
+ as a buffer or None if an error occurred and the MAGIC_ERROR flag
+ is set. A call to errno() will return the numeric error code.
+ """
+ return Magic.__tostr(_buffer(self._magic_t, buf, len(buf)))
+
+ def error(self):
+ """
+ Returns a textual explanation of the last error or None
+ if there was no error.
+ """
+ return Magic.__tostr(_error(self._magic_t))
+
+ def setflags(self, flags):
+ """
+ Set flags on the magic object which determine how magic checking
+ behaves; a bitwise OR of the flags described in libmagic(3), but
+ without the MAGIC_ prefix.
+
+ Returns -1 on systems that don't support utime(2) or utimes(2)
+ when PRESERVE_ATIME is set.
+ """
+ return _setflags(self._magic_t, flags)
+
+ def load(self, filename=None):
+ """
+ Must be called to load entries in the colon separated list of database
+ files passed as argument or the default database file if no argument
+ before any magic queries can be performed.
+
+ Returns 0 on success and -1 on failure.
+ """
+ return _load(self._magic_t, Magic.__tobytes(filename))
+
+ def compile(self, dbs):
+ """
+ Compile entries in the colon separated list of database files
+ passed as argument or the default database file if no argument.
+ The compiled files created are named from the basename(1) of each file
+ argument with ".mgc" appended to it.
+
+ Returns 0 on success and -1 on failure.
+ """
+ return _compile(self._magic_t, Magic.__tobytes(dbs))
+
+ def check(self, dbs):
+ """
+ Check the validity of entries in the colon separated list of
+ database files passed as argument or the default database file
+ if no argument.
+
+ Returns 0 on success and -1 on failure.
+ """
+ return _check(self._magic_t, Magic.__tobytes(dbs))
+
+ def list(self, dbs):
+ """
+ Check the validity of entries in the colon separated list of
+ database files passed as argument or the default database file
+ if no argument.
+
+ Returns 0 on success and -1 on failure.
+ """
+ return _list(self._magic_t, Magic.__tobytes(dbs))
+
+ def errno(self):
+ """
+ Returns a numeric error code. If return value is 0, an internal
+ magic error occurred. If return value is non-zero, the value is
+ an OS error code. Use the errno module or os.strerror() can be used
+ to provide detailed error information.
+ """
+ return _errno(self._magic_t)
+
+
+def open(flags):
+ """
+ Returns a magic object on success and None on failure.
+ Flags argument as for setflags.
+ """
+ return Magic(_open(flags))
+
+
+# Objects used by `detect_from_` functions
+mime_magic = Magic(_open(MAGIC_MIME))
+mime_magic.load()
+none_magic = Magic(_open(MAGIC_NONE))
+none_magic.load()
+
+
+def _create_filemagic(mime_detected, type_detected):
+ splat = mime_detected.split('; ')
+ mime_type = splat[0]
+ if len(splat) == 2:
+ mime_encoding = splat[1]
+ else:
+ mime_encoding = ''
+
+ return FileMagic(name=type_detected, mime_type=mime_type,
+ encoding=mime_encoding.replace('charset=', ''))
+
+
+def detect_from_filename(filename):
+ '''Detect mime type, encoding and file type from a filename
+
+ Returns a `FileMagic` namedtuple.
+ '''
+
+ return _create_filemagic(mime_magic.file(filename),
+ none_magic.file(filename))
+
+
+def detect_from_fobj(fobj):
+ '''Detect mime type, encoding and file type from file-like object
+
+ Returns a `FileMagic` namedtuple.
+ '''
+
+ file_descriptor = fobj.fileno()
+ return _create_filemagic(mime_magic.descriptor(file_descriptor),
+ none_magic.descriptor(file_descriptor))
+
+
+def detect_from_content(byte_content):
+ '''Detect mime type, encoding and file type from bytes
+
+ Returns a `FileMagic` namedtuple.
+ '''
+
+ return _create_filemagic(mime_magic.buffer(byte_content),
+ none_magic.buffer(byte_content))
diff --git a/contrib/python/python-magic/py3/magic/loader.py b/contrib/python/python-magic/py3/magic/loader.py
new file mode 100644
index 0000000000..931f16193e
--- /dev/null
+++ b/contrib/python/python-magic/py3/magic/loader.py
@@ -0,0 +1,50 @@
+from ctypes.util import find_library
+import ctypes
+import sys
+import glob
+import os.path
+
+def _lib_candidates():
+
+ yield find_library('magic')
+
+ if sys.platform == 'darwin':
+
+ paths = [
+ '/opt/local/lib',
+ '/usr/local/lib',
+ '/opt/homebrew/lib',
+ ] + glob.glob('/usr/local/Cellar/libmagic/*/lib')
+
+ for i in paths:
+ yield os.path.join(i, 'libmagic.dylib')
+
+ elif sys.platform in ('win32', 'cygwin'):
+
+ prefixes = ['libmagic', 'magic1', 'cygmagic-1', 'libmagic-1', 'msys-magic-1']
+
+ for i in prefixes:
+ # find_library searches in %PATH% but not the current directory,
+ # so look for both
+ yield './%s.dll' % (i,)
+ yield find_library(i)
+
+ elif sys.platform == 'linux':
+ # This is necessary because alpine is bad
+ yield 'libmagic.so.1'
+
+
+def load_lib():
+
+ for lib in _lib_candidates():
+ # find_library returns None when lib not found
+ if lib is None:
+ continue
+ try:
+ return ctypes.CDLL(lib)
+ except OSError:
+ pass
+ else:
+ # It is better to raise an ImportError since we are importing magic module
+ raise ImportError('failed to find libmagic. Check your installation')
+
diff --git a/contrib/python/python-magic/py3/magic/py.typed b/contrib/python/python-magic/py3/magic/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/python-magic/py3/magic/py.typed
diff --git a/contrib/python/python-magic/py3/ya.make b/contrib/python/python-magic/py3/ya.make
new file mode 100644
index 0000000000..f1aba820d5
--- /dev/null
+++ b/contrib/python/python-magic/py3/ya.make
@@ -0,0 +1,31 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(0.4.27)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/libs/libmagic
+ library/python/symbols/libmagic
+)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ magic/__init__.py
+ magic/__init__.pyi
+ magic/compat.py
+ magic/loader.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/python-magic/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+ magic/py.typed
+)
+
+END()
diff --git a/contrib/python/python-magic/ya.make b/contrib/python/python-magic/ya.make
new file mode 100644
index 0000000000..6ed9425039
--- /dev/null
+++ b/contrib/python/python-magic/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/python-magic/py2)
+ELSE()
+ PEERDIR(contrib/python/python-magic/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/termcolor/py2/.dist-info/METADATA b/contrib/python/termcolor/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..cbcd7281c6
--- /dev/null
+++ b/contrib/python/termcolor/py2/.dist-info/METADATA
@@ -0,0 +1,135 @@
+Metadata-Version: 2.1
+Name: termcolor
+Version: 1.1.0
+Summary: ANSII Color formatting for output in terminal.
+Home-page: http://pypi.python.org/pypi/termcolor
+Author: Konstantin Lepa
+Author-email: konstantin.lepa@gmail.com
+License: MIT
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Terminals
+
+Example
+=======
+ ::
+
+ import sys
+ from termcolor import colored, cprint
+
+ text = colored('Hello, World!', 'red', attrs=['reverse', 'blink'])
+ print(text)
+ cprint('Hello, World!', 'green', 'on_red')
+
+ print_red_on_cyan = lambda x: cprint(x, 'red', 'on_cyan')
+ print_red_on_cyan('Hello, World!')
+ print_red_on_cyan('Hello, Universe!')
+
+ for i in range(10):
+ cprint(i, 'magenta', end=' ')
+
+ cprint("Attention!", 'red', attrs=['bold'], file=sys.stderr)
+
+Text Properties
+===============
+
+ Text colors:
+
+ - grey
+ - red
+ - green
+ - yellow
+ - blue
+ - magenta
+ - cyan
+ - white
+
+ Text highlights:
+
+ - on_grey
+ - on_red
+ - on_green
+ - on_yellow
+ - on_blue
+ - on_magenta
+ - on_cyan
+ - on_white
+
+ Attributes:
+
+ - bold
+ - dark
+ - underline
+ - blink
+ - reverse
+ - concealed
+
+Terminal properties
+===================
+
+ ============ ======= ==== ========= ========== ======= =========
+ Terminal bold dark underline blink reverse concealed
+ ------------ ------- ---- --------- ---------- ------- ---------
+ xterm yes no yes bold yes yes
+ linux yes yes bold yes yes no
+ rxvt yes no yes bold/black yes no
+ dtterm yes yes yes reverse yes yes
+ teraterm reverse no yes rev/red yes no
+ aixterm normal no yes no yes yes
+ PuTTY color no yes no yes no
+ Windows no no no no yes no
+ Cygwin SSH yes no color color color yes
+ Mac Terminal yes no yes yes yes yes
+ ============ ======= ==== ========= ========== ======= =========
+
+
+CHANGES
+=======
+
+1.1.0 (13.01.2011)
+------------------
+
+- Added cprint function.
+
+1.0.1 (13.01.2011)
+------------------
+
+- Updated README.rst.
+
+1.0.0 (13.01.2011)
+------------------
+
+- Changed license to MIT.
+- Updated copyright.
+- Refactored source code.
+
+0.2 (07.09.2010)
+----------------
+
+- Added support of Python 3.x.
+
+0.1.2 (04.06.2009)
+------------------
+
+- Fixed bold characters. (Thanks Tibor Fekete)
+
+0.1.1 (05.03.2009)
+------------------
+
+- Some refactoring.
+- Updated copyright.
+- Fixed reset colors.
+- Updated documentation.
+
+0.1 (09.06.2008)
+----------------
+
+- Initial release.
+
+
+
diff --git a/contrib/python/termcolor/py2/.dist-info/top_level.txt b/contrib/python/termcolor/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..f08cca1411
--- /dev/null
+++ b/contrib/python/termcolor/py2/.dist-info/top_level.txt
@@ -0,0 +1 @@
+termcolor
diff --git a/contrib/python/termcolor/py2/COPYING.txt b/contrib/python/termcolor/py2/COPYING.txt
new file mode 100644
index 0000000000..d5df97633a
--- /dev/null
+++ b/contrib/python/termcolor/py2/COPYING.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2008-2011 Volvox Development Team
+
+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/contrib/python/termcolor/py2/README.rst b/contrib/python/termcolor/py2/README.rst
new file mode 100644
index 0000000000..95aa524367
--- /dev/null
+++ b/contrib/python/termcolor/py2/README.rst
@@ -0,0 +1,72 @@
+Example
+=======
+ ::
+
+ import sys
+ from termcolor import colored, cprint
+
+ text = colored('Hello, World!', 'red', attrs=['reverse', 'blink'])
+ print(text)
+ cprint('Hello, World!', 'green', 'on_red')
+
+ print_red_on_cyan = lambda x: cprint(x, 'red', 'on_cyan')
+ print_red_on_cyan('Hello, World!')
+ print_red_on_cyan('Hello, Universe!')
+
+ for i in range(10):
+ cprint(i, 'magenta', end=' ')
+
+ cprint("Attention!", 'red', attrs=['bold'], file=sys.stderr)
+
+Text Properties
+===============
+
+ Text colors:
+
+ - grey
+ - red
+ - green
+ - yellow
+ - blue
+ - magenta
+ - cyan
+ - white
+
+ Text highlights:
+
+ - on_grey
+ - on_red
+ - on_green
+ - on_yellow
+ - on_blue
+ - on_magenta
+ - on_cyan
+ - on_white
+
+ Attributes:
+
+ - bold
+ - dark
+ - underline
+ - blink
+ - reverse
+ - concealed
+
+Terminal properties
+===================
+
+ ============ ======= ==== ========= ========== ======= =========
+ Terminal bold dark underline blink reverse concealed
+ ------------ ------- ---- --------- ---------- ------- ---------
+ xterm yes no yes bold yes yes
+ linux yes yes bold yes yes no
+ rxvt yes no yes bold/black yes no
+ dtterm yes yes yes reverse yes yes
+ teraterm reverse no yes rev/red yes no
+ aixterm normal no yes no yes yes
+ PuTTY color no yes no yes no
+ Windows no no no no yes no
+ Cygwin SSH yes no color color color yes
+ Mac Terminal yes no yes yes yes yes
+ ============ ======= ==== ========= ========== ======= =========
+
diff --git a/contrib/python/termcolor/py2/termcolor.py b/contrib/python/termcolor/py2/termcolor.py
new file mode 100644
index 0000000000..db63ea0200
--- /dev/null
+++ b/contrib/python/termcolor/py2/termcolor.py
@@ -0,0 +1,217 @@
+# coding: utf-8
+# Copyright (c) 2008-2011 Volvox Development Team
+#
+# 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.
+#
+# Author: Konstantin Lepa <konstantin.lepa@gmail.com>
+
+"""ANSI color formatting for output in terminal."""
+
+from __future__ import print_function
+import os
+import sys
+
+
+__ALL__ = [ "ATTRIBUTES", "COLORS", "HIGHLIGHTS", "RESET", "colored", "cprint" ]
+
+ATTRIBUTES = {
+ "bold": 1,
+ "dark": 2,
+ "underline": 4,
+ "blink": 5,
+ "reverse": 7,
+ "concealed": 8,
+}
+
+
+HIGHLIGHTS = {
+ "on_black": 40,
+ "on_grey": 40, # Actually black but kept for backwards compatibility
+ "on_red": 41,
+ "on_green": 42,
+ "on_yellow": 43,
+ "on_blue": 44,
+ "on_magenta": 45,
+ "on_cyan": 46,
+ "on_light_grey": 47,
+ "on_dark_grey": 100,
+ "on_light_red": 101,
+ "on_light_green": 102,
+ "on_light_yellow": 103,
+ "on_light_blue": 104,
+ "on_light_magenta": 105,
+ "on_light_cyan": 106,
+ "on_white": 107,
+}
+
+COLORS = {
+ "black": 30,
+ "grey": 30, # Actually black but kept for backwards compatibility
+ "red": 31,
+ "green": 32,
+ "yellow": 33,
+ "blue": 34,
+ "magenta": 35,
+ "cyan": 36,
+ "light_grey": 37,
+ "dark_grey": 90,
+ "light_red": 91,
+ "light_green": 92,
+ "light_yellow": 93,
+ "light_blue": 94,
+ "light_magenta": 95,
+ "light_cyan": 96,
+ "white": 97,
+}
+
+
+RESET = "\033[0m"
+
+
+def _can_do_colour(no_color, force_color=None):
+ """Check env vars and for tty/dumb terminal"""
+ # First check overrides:
+ # "User-level configuration files and per-instance command-line arguments should
+ # override $NO_COLOR. A user should be able to export $NO_COLOR in their shell
+ # configuration file as a default, but configure a specific program in its
+ # configuration file to specifically enable color."
+ # https://no-color.org
+ if no_color is not None and no_color:
+ return False
+ if force_color is not None and force_color:
+ return True
+
+ # Then check env vars:
+ if "ANSI_COLORS_DISABLED" in os.environ:
+ return False
+ if "NO_COLOR" in os.environ:
+ return False
+ if "FORCE_COLOR" in os.environ:
+ return True
+ return hasattr(sys.stdout, "isatty") and sys.stdout.isatty() and os.environ.get("TERM") != "dumb"
+
+
+def colored(text, color=None, on_color=None, attrs=None, no_color=None, force_color=None):
+ """Colorize text.
+
+ Available text colors:
+ black, red, green, yellow, blue, magenta, cyan, white,
+ light_grey, dark_grey, light_red, light_green, light_yellow, light_blue,
+ light_magenta, light_cyan.
+
+ Available text highlights:
+ on_black, on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white,
+ on_light_grey, on_dark_grey, on_light_red, on_light_green, on_light_yellow,
+ on_light_blue, on_light_magenta, on_light_cyan.
+
+ Available attributes:
+ bold, dark, underline, blink, reverse, concealed.
+
+ Example:
+ colored('Hello, World!', 'red', 'on_black', ['bold', 'blink'])
+ colored('Hello, World!', 'green')
+ """
+ if not _can_do_colour(no_color=no_color, force_color=force_color):
+ return text
+
+ fmt_str = "\033[%dm%s"
+ if color is not None:
+ text = fmt_str % (COLORS[color], text)
+
+ if on_color is not None:
+ text = fmt_str % (HIGHLIGHTS[on_color], text)
+
+ if attrs is not None:
+ for attr in attrs:
+ text = fmt_str % (ATTRIBUTES[attr], text)
+
+ return text + RESET
+
+
+def cprint(text, color=None, on_color=None, attrs=None, no_color=None, force_color=None, **kwargs):
+ """Print colorized text.
+
+ It accepts arguments of print function.
+ """
+
+ print((colored(text, color, on_color, attrs, no_color=no_color, force_color=force_color)), **kwargs)
+
+
+if __name__ == "__main__":
+ print("Current terminal type: %s" % os.getenv("TERM"))
+ print("Test basic colors:")
+ cprint("Black color", "black")
+ cprint("Red color", "red")
+ cprint("Green color", "green")
+ cprint("Yellow color", "yellow")
+ cprint("Blue color", "blue")
+ cprint("Magenta color", "magenta")
+ cprint("Cyan color", "cyan")
+ cprint("White color", "white")
+ cprint("Light grey color", "light_grey")
+ cprint("Dark grey color", "dark_grey")
+ cprint("Light red color", "light_red")
+ cprint("Light green color", "light_green")
+ cprint("Light yellow color", "light_yellow")
+ cprint("Light blue color", "light_blue")
+ cprint("Light magenta color", "light_magenta")
+ cprint("Light cyan color", "light_cyan")
+ print("-" * 78)
+
+ print("Test highlights:")
+ cprint("On black color", on_color="on_black")
+ cprint("On red color", on_color="on_red")
+ cprint("On green color", on_color="on_green")
+ cprint("On yellow color", on_color="on_yellow")
+ cprint("On blue color", on_color="on_blue")
+ cprint("On magenta color", on_color="on_magenta")
+ cprint("On cyan color", on_color="on_cyan")
+ cprint("On white color", color="black", on_color="on_white")
+ cprint("On light grey color", on_color="on_light_grey")
+ cprint("On dark grey color", on_color="on_dark_grey")
+ cprint("On light red color", on_color="on_light_red")
+ cprint("On light green color", on_color="on_light_green")
+ cprint("On light yellow color", on_color="on_light_yellow")
+ cprint("On light blue color", on_color="on_light_blue")
+ cprint("On light magenta color", on_color="on_light_magenta")
+ cprint("On light cyan color", on_color="on_light_cyan")
+ print("-" * 78)
+
+ print("Test attributes:")
+ cprint("Bold black color", "black", attrs=["bold"])
+ cprint("Dark red color", "red", attrs=["dark"])
+ cprint("Underline green color", "green", attrs=["underline"])
+ cprint("Blink yellow color", "yellow", attrs=["blink"])
+ cprint("Reversed blue color", "blue", attrs=["reverse"])
+ cprint("Concealed Magenta color", "magenta", attrs=["concealed"])
+ cprint(
+ "Bold underline reverse cyan color",
+ "cyan",
+ attrs=["bold", "underline", "reverse"],
+ )
+ cprint(
+ "Dark blink concealed white color",
+ "white",
+ attrs=["dark", "blink", "concealed"],
+ )
+ print("-" * 78)
+
+ print("Test mixing:")
+ cprint("Underline red on black color", "red", "on_black", ["underline"])
+ cprint("Reversed green on red color", "green", "on_red", ["reverse"])
diff --git a/contrib/python/termcolor/py2/ya.make b/contrib/python/termcolor/py2/ya.make
new file mode 100644
index 0000000000..0c30233373
--- /dev/null
+++ b/contrib/python/termcolor/py2/ya.make
@@ -0,0 +1,22 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(1.1.0)
+
+LICENSE(MIT)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ termcolor.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/termcolor/py2/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
diff --git a/contrib/python/termcolor/py3/.dist-info/METADATA b/contrib/python/termcolor/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..d8dea360f4
--- /dev/null
+++ b/contrib/python/termcolor/py3/.dist-info/METADATA
@@ -0,0 +1,126 @@
+Metadata-Version: 2.1
+Name: termcolor
+Version: 2.3.0
+Summary: ANSI color formatting for output in terminal
+Project-URL: Changelog, https://github.com/termcolor/termcolor/releases
+Project-URL: Homepage, https://github.com/termcolor/termcolor
+Project-URL: Source, https://github.com/termcolor/termcolor
+Author-email: Konstantin Lepa <konstantin.lepa@gmail.com>
+Maintainer: Hugo van Kemenade
+License: MIT
+License-File: COPYING.txt
+Keywords: ANSI,ANSI color,ANSI colour,color,colour,formatting,termcolor,terminal
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Terminals
+Requires-Python: >=3.7
+Provides-Extra: tests
+Requires-Dist: pytest; extra == 'tests'
+Requires-Dist: pytest-cov; extra == 'tests'
+Description-Content-Type: text/markdown
+
+# termcolor
+
+[![PyPI version](https://img.shields.io/pypi/v/termcolor.svg?logo=pypi&logoColor=FFE873)](https://pypi.org/project/termcolor)
+[![Supported Python versions](https://img.shields.io/pypi/pyversions/termcolor.svg?logo=python&logoColor=FFE873)](https://pypi.org/project/termcolor)
+[![PyPI downloads](https://img.shields.io/pypi/dm/termcolor.svg)](https://pypistats.org/packages/termcolor)
+[![GitHub Actions status](https://github.com/termcolor/termcolor/workflows/Test/badge.svg)](https://github.com/termcolor/termcolor/actions)
+[![Codecov](https://codecov.io/gh/termcolor/termcolor/branch/main/graph/badge.svg)](https://codecov.io/gh/termcolor/termcolor)
+[![Licence](https://img.shields.io/github/license/termcolor/termcolor.svg)](COPYING.txt)
+[![Code style: Black](https://img.shields.io/badge/code%20style-Black-000000.svg)](https://github.com/psf/black)
+[![Tidelift](https://tidelift.com/badges/package/pypi/termcolor)](https://tidelift.com/subscription/pkg/pypi-termcolor?utm_source=pypi-termcolor&utm_medium=referral&utm_campaign=readme)
+
+## Installation
+
+### From PyPI
+
+```bash
+python3 -m pip install --upgrade termcolor
+```
+
+### From source
+
+```bash
+git clone https://github.com/termcolor/termcolor
+cd termcolor
+python3 -m pip install .
+```
+
+### Demo
+
+To see demo output, run:
+
+```bash
+python3 -m termcolor
+```
+
+## Example
+
+```python
+import sys
+
+from termcolor import colored, cprint
+
+text = colored("Hello, World!", "red", attrs=["reverse", "blink"])
+print(text)
+cprint("Hello, World!", "green", "on_red")
+
+print_red_on_cyan = lambda x: cprint(x, "red", "on_cyan")
+print_red_on_cyan("Hello, World!")
+print_red_on_cyan("Hello, Universe!")
+
+for i in range(10):
+ cprint(i, "magenta", end=" ")
+
+cprint("Attention!", "red", attrs=["bold"], file=sys.stderr)
+```
+
+## Text properties
+
+| Text colors | Text highlights | Attributes |
+| --------------- | ------------------ | ----------- |
+| `black` | `on_black` | `bold` |
+| `red` | `on_red` | `dark` |
+| `green` | `on_green` | `underline` |
+| `yellow` | `on_yellow` | `blink` |
+| `blue` | `on_blue` | `reverse` |
+| `magenta` | `on_magenta` | `concealed` |
+| `cyan` | `on_cyan` | |
+| `white` | `on_white` | |
+| `light_grey` | `on_light_grey` | |
+| `dark_grey` | `on_dark_grey` | |
+| `light_red` | `on_light_red` | |
+| `light_green` | `on_light_green` | |
+| `light_yellow` | `on_light_yellow` | |
+| `light_blue` | `on_light_blue` | |
+| `light_magenta` | `on_light_magenta` | |
+| `light_cyan` | `on_light_cyan` | |
+
+## Terminal properties
+
+| Terminal | bold | dark | underline | blink | reverse | concealed |
+| ------------ | ------- | ---- | --------- | ---------- | ------- | --------- |
+| xterm | yes | no | yes | bold | yes | yes |
+| linux | yes | yes | bold | yes | yes | no |
+| rxvt | yes | no | yes | bold/black | yes | no |
+| dtterm | yes | yes | yes | reverse | yes | yes |
+| teraterm | reverse | no | yes | rev/red | yes | no |
+| aixterm | normal | no | yes | no | yes | yes |
+| PuTTY | color | no | yes | no | yes | no |
+| Windows | no | no | no | no | yes | no |
+| Cygwin SSH | yes | no | color | color | color | yes |
+| Mac Terminal | yes | no | yes | yes | yes | yes |
diff --git a/contrib/python/termcolor/py3/.dist-info/top_level.txt b/contrib/python/termcolor/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..f08cca1411
--- /dev/null
+++ b/contrib/python/termcolor/py3/.dist-info/top_level.txt
@@ -0,0 +1 @@
+termcolor
diff --git a/contrib/python/termcolor/py3/COPYING.txt b/contrib/python/termcolor/py3/COPYING.txt
new file mode 100644
index 0000000000..d0b79705c3
--- /dev/null
+++ b/contrib/python/termcolor/py3/COPYING.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2008-2011 Volvox Development Team
+
+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/contrib/python/termcolor/py3/README.md b/contrib/python/termcolor/py3/README.md
new file mode 100644
index 0000000000..7ccfd9f5b0
--- /dev/null
+++ b/contrib/python/termcolor/py3/README.md
@@ -0,0 +1,91 @@
+# termcolor
+
+[![PyPI version](https://img.shields.io/pypi/v/termcolor.svg?logo=pypi&logoColor=FFE873)](https://pypi.org/project/termcolor)
+[![Supported Python versions](https://img.shields.io/pypi/pyversions/termcolor.svg?logo=python&logoColor=FFE873)](https://pypi.org/project/termcolor)
+[![PyPI downloads](https://img.shields.io/pypi/dm/termcolor.svg)](https://pypistats.org/packages/termcolor)
+[![GitHub Actions status](https://github.com/termcolor/termcolor/workflows/Test/badge.svg)](https://github.com/termcolor/termcolor/actions)
+[![Codecov](https://codecov.io/gh/termcolor/termcolor/branch/main/graph/badge.svg)](https://codecov.io/gh/termcolor/termcolor)
+[![Licence](https://img.shields.io/github/license/termcolor/termcolor.svg)](COPYING.txt)
+[![Code style: Black](https://img.shields.io/badge/code%20style-Black-000000.svg)](https://github.com/psf/black)
+[![Tidelift](https://tidelift.com/badges/package/pypi/termcolor)](https://tidelift.com/subscription/pkg/pypi-termcolor?utm_source=pypi-termcolor&utm_medium=referral&utm_campaign=readme)
+
+## Installation
+
+### From PyPI
+
+```bash
+python3 -m pip install --upgrade termcolor
+```
+
+### From source
+
+```bash
+git clone https://github.com/termcolor/termcolor
+cd termcolor
+python3 -m pip install .
+```
+
+### Demo
+
+To see demo output, run:
+
+```bash
+python3 -m termcolor
+```
+
+## Example
+
+```python
+import sys
+
+from termcolor import colored, cprint
+
+text = colored("Hello, World!", "red", attrs=["reverse", "blink"])
+print(text)
+cprint("Hello, World!", "green", "on_red")
+
+print_red_on_cyan = lambda x: cprint(x, "red", "on_cyan")
+print_red_on_cyan("Hello, World!")
+print_red_on_cyan("Hello, Universe!")
+
+for i in range(10):
+ cprint(i, "magenta", end=" ")
+
+cprint("Attention!", "red", attrs=["bold"], file=sys.stderr)
+```
+
+## Text properties
+
+| Text colors | Text highlights | Attributes |
+| --------------- | ------------------ | ----------- |
+| `black` | `on_black` | `bold` |
+| `red` | `on_red` | `dark` |
+| `green` | `on_green` | `underline` |
+| `yellow` | `on_yellow` | `blink` |
+| `blue` | `on_blue` | `reverse` |
+| `magenta` | `on_magenta` | `concealed` |
+| `cyan` | `on_cyan` | |
+| `white` | `on_white` | |
+| `light_grey` | `on_light_grey` | |
+| `dark_grey` | `on_dark_grey` | |
+| `light_red` | `on_light_red` | |
+| `light_green` | `on_light_green` | |
+| `light_yellow` | `on_light_yellow` | |
+| `light_blue` | `on_light_blue` | |
+| `light_magenta` | `on_light_magenta` | |
+| `light_cyan` | `on_light_cyan` | |
+
+## Terminal properties
+
+| Terminal | bold | dark | underline | blink | reverse | concealed |
+| ------------ | ------- | ---- | --------- | ---------- | ------- | --------- |
+| xterm | yes | no | yes | bold | yes | yes |
+| linux | yes | yes | bold | yes | yes | no |
+| rxvt | yes | no | yes | bold/black | yes | no |
+| dtterm | yes | yes | yes | reverse | yes | yes |
+| teraterm | reverse | no | yes | rev/red | yes | no |
+| aixterm | normal | no | yes | no | yes | yes |
+| PuTTY | color | no | yes | no | yes | no |
+| Windows | no | no | no | no | yes | no |
+| Cygwin SSH | yes | no | color | color | color | yes |
+| Mac Terminal | yes | no | yes | yes | yes | yes |
diff --git a/contrib/python/termcolor/py3/termcolor/__init__.py b/contrib/python/termcolor/py3/termcolor/__init__.py
new file mode 100644
index 0000000000..86ace41518
--- /dev/null
+++ b/contrib/python/termcolor/py3/termcolor/__init__.py
@@ -0,0 +1,13 @@
+"""ANSI color formatting for output in terminal."""
+from __future__ import annotations
+
+from termcolor.termcolor import ATTRIBUTES, COLORS, HIGHLIGHTS, RESET, colored, cprint
+
+__all__ = [
+ "ATTRIBUTES",
+ "COLORS",
+ "HIGHLIGHTS",
+ "RESET",
+ "colored",
+ "cprint",
+]
diff --git a/contrib/python/termcolor/py3/termcolor/__main__.py b/contrib/python/termcolor/py3/termcolor/__main__.py
new file mode 100644
index 0000000000..c0620ab389
--- /dev/null
+++ b/contrib/python/termcolor/py3/termcolor/__main__.py
@@ -0,0 +1,68 @@
+from __future__ import annotations
+
+import os
+
+from termcolor import cprint
+
+if __name__ == "__main__":
+ print(f"Current terminal type: {os.getenv('TERM')}")
+ print("Test basic colors:")
+ cprint("Black color", "black")
+ cprint("Red color", "red")
+ cprint("Green color", "green")
+ cprint("Yellow color", "yellow")
+ cprint("Blue color", "blue")
+ cprint("Magenta color", "magenta")
+ cprint("Cyan color", "cyan")
+ cprint("White color", "white")
+ cprint("Light grey color", "light_grey")
+ cprint("Dark grey color", "dark_grey")
+ cprint("Light red color", "light_red")
+ cprint("Light green color", "light_green")
+ cprint("Light yellow color", "light_yellow")
+ cprint("Light blue color", "light_blue")
+ cprint("Light magenta color", "light_magenta")
+ cprint("Light cyan color", "light_cyan")
+ print("-" * 78)
+
+ print("Test highlights:")
+ cprint("On black color", on_color="on_black")
+ cprint("On red color", on_color="on_red")
+ cprint("On green color", on_color="on_green")
+ cprint("On yellow color", on_color="on_yellow")
+ cprint("On blue color", on_color="on_blue")
+ cprint("On magenta color", on_color="on_magenta")
+ cprint("On cyan color", on_color="on_cyan")
+ cprint("On white color", color="black", on_color="on_white")
+ cprint("On light grey color", on_color="on_light_grey")
+ cprint("On dark grey color", on_color="on_dark_grey")
+ cprint("On light red color", on_color="on_light_red")
+ cprint("On light green color", on_color="on_light_green")
+ cprint("On light yellow color", on_color="on_light_yellow")
+ cprint("On light blue color", on_color="on_light_blue")
+ cprint("On light magenta color", on_color="on_light_magenta")
+ cprint("On light cyan color", on_color="on_light_cyan")
+ print("-" * 78)
+
+ print("Test attributes:")
+ cprint("Bold black color", "black", attrs=["bold"])
+ cprint("Dark red color", "red", attrs=["dark"])
+ cprint("Underline green color", "green", attrs=["underline"])
+ cprint("Blink yellow color", "yellow", attrs=["blink"])
+ cprint("Reversed blue color", "blue", attrs=["reverse"])
+ cprint("Concealed Magenta color", "magenta", attrs=["concealed"])
+ cprint(
+ "Bold underline reverse cyan color",
+ "cyan",
+ attrs=["bold", "underline", "reverse"],
+ )
+ cprint(
+ "Dark blink concealed white color",
+ "white",
+ attrs=["dark", "blink", "concealed"],
+ )
+ print("-" * 78)
+
+ print("Test mixing:")
+ cprint("Underline red on black color", "red", "on_black", ["underline"])
+ cprint("Reversed green on red color", "green", "on_red", ["reverse"])
diff --git a/contrib/python/termcolor/py3/termcolor/py.typed b/contrib/python/termcolor/py3/termcolor/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/termcolor/py3/termcolor/py.typed
diff --git a/contrib/python/termcolor/py3/termcolor/termcolor.py b/contrib/python/termcolor/py3/termcolor/termcolor.py
new file mode 100644
index 0000000000..2c8db2dc4e
--- /dev/null
+++ b/contrib/python/termcolor/py3/termcolor/termcolor.py
@@ -0,0 +1,201 @@
+# Copyright (c) 2008-2011 Volvox Development Team
+#
+# 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.
+#
+# Author: Konstantin Lepa <konstantin.lepa@gmail.com>
+
+"""ANSI color formatting for output in terminal."""
+
+from __future__ import annotations
+
+import os
+import sys
+import warnings
+from typing import Any, Iterable
+
+
+def __getattr__(name: str) -> list[str]:
+ if name == "__ALL__":
+ warnings.warn(
+ "__ALL__ is deprecated and will be removed in termcolor 3. "
+ "Use __all__ instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return ["colored", "cprint"]
+ msg = f"module '{__name__}' has no attribute '{name}'"
+ raise AttributeError(msg)
+
+
+ATTRIBUTES = {
+ "bold": 1,
+ "dark": 2,
+ "underline": 4,
+ "blink": 5,
+ "reverse": 7,
+ "concealed": 8,
+}
+
+
+HIGHLIGHTS = {
+ "on_black": 40,
+ "on_grey": 40, # Actually black but kept for backwards compatibility
+ "on_red": 41,
+ "on_green": 42,
+ "on_yellow": 43,
+ "on_blue": 44,
+ "on_magenta": 45,
+ "on_cyan": 46,
+ "on_light_grey": 47,
+ "on_dark_grey": 100,
+ "on_light_red": 101,
+ "on_light_green": 102,
+ "on_light_yellow": 103,
+ "on_light_blue": 104,
+ "on_light_magenta": 105,
+ "on_light_cyan": 106,
+ "on_white": 107,
+}
+
+COLORS = {
+ "black": 30,
+ "grey": 30, # Actually black but kept for backwards compatibility
+ "red": 31,
+ "green": 32,
+ "yellow": 33,
+ "blue": 34,
+ "magenta": 35,
+ "cyan": 36,
+ "light_grey": 37,
+ "dark_grey": 90,
+ "light_red": 91,
+ "light_green": 92,
+ "light_yellow": 93,
+ "light_blue": 94,
+ "light_magenta": 95,
+ "light_cyan": 96,
+ "white": 97,
+}
+
+
+RESET = "\033[0m"
+
+
+def _can_do_colour(
+ *, no_color: bool | None = None, force_color: bool | None = None
+) -> bool:
+ """Check env vars and for tty/dumb terminal"""
+ # First check overrides:
+ # "User-level configuration files and per-instance command-line arguments should
+ # override $NO_COLOR. A user should be able to export $NO_COLOR in their shell
+ # configuration file as a default, but configure a specific program in its
+ # configuration file to specifically enable color."
+ # https://no-color.org
+ if no_color is not None and no_color:
+ return False
+ if force_color is not None and force_color:
+ return True
+
+ # Then check env vars:
+ if "ANSI_COLORS_DISABLED" in os.environ:
+ return False
+ if "NO_COLOR" in os.environ:
+ return False
+ if "FORCE_COLOR" in os.environ:
+ return True
+ return (
+ hasattr(sys.stdout, "isatty")
+ and sys.stdout.isatty()
+ and os.environ.get("TERM") != "dumb"
+ )
+
+
+def colored(
+ text: str,
+ color: str | None = None,
+ on_color: str | None = None,
+ attrs: Iterable[str] | None = None,
+ *,
+ no_color: bool | None = None,
+ force_color: bool | None = None,
+) -> str:
+ """Colorize text.
+
+ Available text colors:
+ black, red, green, yellow, blue, magenta, cyan, white,
+ light_grey, dark_grey, light_red, light_green, light_yellow, light_blue,
+ light_magenta, light_cyan.
+
+ Available text highlights:
+ on_black, on_red, on_green, on_yellow, on_blue, on_magenta, on_cyan, on_white,
+ on_light_grey, on_dark_grey, on_light_red, on_light_green, on_light_yellow,
+ on_light_blue, on_light_magenta, on_light_cyan.
+
+ Available attributes:
+ bold, dark, underline, blink, reverse, concealed.
+
+ Example:
+ colored('Hello, World!', 'red', 'on_black', ['bold', 'blink'])
+ colored('Hello, World!', 'green')
+ """
+ if not _can_do_colour(no_color=no_color, force_color=force_color):
+ return text
+
+ fmt_str = "\033[%dm%s"
+ if color is not None:
+ text = fmt_str % (COLORS[color], text)
+
+ if on_color is not None:
+ text = fmt_str % (HIGHLIGHTS[on_color], text)
+
+ if attrs is not None:
+ for attr in attrs:
+ text = fmt_str % (ATTRIBUTES[attr], text)
+
+ return text + RESET
+
+
+def cprint(
+ text: str,
+ color: str | None = None,
+ on_color: str | None = None,
+ attrs: Iterable[str] | None = None,
+ *,
+ no_color: bool | None = None,
+ force_color: bool | None = None,
+ **kwargs: Any,
+) -> None:
+ """Print colorized text.
+
+ It accepts arguments of print function.
+ """
+
+ print(
+ (
+ colored(
+ text,
+ color,
+ on_color,
+ attrs,
+ no_color=no_color,
+ force_color=force_color,
+ )
+ ),
+ **kwargs,
+ )
diff --git a/contrib/python/termcolor/py3/ya.make b/contrib/python/termcolor/py3/ya.make
new file mode 100644
index 0000000000..afbfca8bd5
--- /dev/null
+++ b/contrib/python/termcolor/py3/ya.make
@@ -0,0 +1,25 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(2.3.0)
+
+LICENSE(MIT)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ termcolor/__init__.py
+ termcolor/__main__.py
+ termcolor/termcolor.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/termcolor/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+ termcolor/py.typed
+)
+
+END()
diff --git a/contrib/python/termcolor/ya.make b/contrib/python/termcolor/ya.make
new file mode 100644
index 0000000000..7f7866e83f
--- /dev/null
+++ b/contrib/python/termcolor/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/termcolor/py2)
+ELSE()
+ PEERDIR(contrib/python/termcolor/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/toolz/py2/.dist-info/METADATA b/contrib/python/toolz/py2/.dist-info/METADATA
new file mode 100644
index 0000000000..c43bc308d4
--- /dev/null
+++ b/contrib/python/toolz/py2/.dist-info/METADATA
@@ -0,0 +1,159 @@
+Metadata-Version: 2.1
+Name: toolz
+Version: 0.10.0
+Summary: List processing tools and functional utilities
+Home-page: https://github.com/pytoolz/toolz/
+Author: https://raw.github.com/pytoolz/toolz/master/AUTHORS.md
+Maintainer: Matthew Rocklin
+Maintainer-email: mrocklin@gmail.com
+License: BSD
+Keywords: functional utility itertools functools
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+
+Toolz
+=====
+
+|Build Status| |Coverage Status| |Version Status|
+
+A set of utility functions for iterators, functions, and dictionaries.
+
+See the PyToolz documentation at https://toolz.readthedocs.io
+
+LICENSE
+-------
+
+New BSD. See `License File <https://github.com/pytoolz/toolz/blob/master/LICENSE.txt>`__.
+
+Install
+-------
+
+``toolz`` is on the Python Package Index (PyPI):
+
+::
+
+ pip install toolz
+
+Structure and Heritage
+----------------------
+
+``toolz`` is implemented in three parts:
+
+|literal itertoolz|_, for operations on iterables. Examples: ``groupby``,
+``unique``, ``interpose``,
+
+|literal functoolz|_, for higher-order functions. Examples: ``memoize``,
+``curry``, ``compose``,
+
+|literal dicttoolz|_, for operations on dictionaries. Examples: ``assoc``,
+``update-in``, ``merge``.
+
+.. |literal itertoolz| replace:: ``itertoolz``
+.. _literal itertoolz: https://github.com/pytoolz/toolz/blob/master/toolz/itertoolz.py
+
+.. |literal functoolz| replace:: ``functoolz``
+.. _literal functoolz: https://github.com/pytoolz/toolz/blob/master/toolz/functoolz.py
+
+.. |literal dicttoolz| replace:: ``dicttoolz``
+.. _literal dicttoolz: https://github.com/pytoolz/toolz/blob/master/toolz/dicttoolz.py
+
+These functions come from the legacy of functional languages for list
+processing. They interoperate well to accomplish common complex tasks.
+
+Read our `API
+Documentation <https://toolz.readthedocs.io/en/latest/api.html>`__ for
+more details.
+
+Example
+-------
+
+This builds a standard wordcount function from pieces within ``toolz``:
+
+.. code:: python
+
+ >>> def stem(word):
+ ... """ Stem word to primitive form """
+ ... return word.lower().rstrip(",.!:;'-\"").lstrip("'\"")
+
+ >>> from toolz import compose, frequencies, partial
+ >>> from toolz.curried import map
+ >>> wordcount = compose(frequencies, map(stem), str.split)
+
+ >>> sentence = "This cat jumped over this other cat!"
+ >>> wordcount(sentence)
+ {'this': 2, 'cat': 2, 'jumped': 1, 'over': 1, 'other': 1}
+
+Dependencies
+------------
+
+``toolz`` supports Python 2.7 and Python 3.4+ with a common codebase.
+It is pure Python and requires no dependencies beyond the standard
+library.
+
+It is, in short, a lightweight dependency.
+
+
+CyToolz
+-------
+
+The ``toolz`` project has been reimplemented in `Cython <http://cython.org>`__.
+The ``cytoolz`` project is a drop-in replacement for the Pure Python
+implementation.
+See `CyToolz GitHub Page <https://github.com/pytoolz/cytoolz/>`__ for more
+details.
+
+See Also
+--------
+
+- `Underscore.js <https://underscorejs.org/>`__: A similar library for
+ JavaScript
+- `Enumerable <https://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A
+ similar library for Ruby
+- `Clojure <https://clojure.org/>`__: A functional language whose
+ standard library has several counterparts in ``toolz``
+- `itertools <https://docs.python.org/2/library/itertools.html>`__: The
+ Python standard library for iterator tools
+- `functools <https://docs.python.org/2/library/functools.html>`__: The
+ Python standard library for function tools
+
+Contributions Welcome
+---------------------
+
+``toolz`` aims to be a repository for utility functions, particularly
+those that come from the functional programming and list processing
+traditions. We welcome contributions that fall within this scope.
+
+We also try to keep the API small to keep ``toolz`` manageable. The ideal
+contribution is significantly different from existing functions and has
+precedent in a few other functional systems.
+
+Please take a look at our
+`issue page <https://github.com/pytoolz/toolz/issues>`__
+for contribution ideas.
+
+Community
+---------
+
+See our `mailing list <https://groups.google.com/forum/#!forum/pytoolz>`__.
+We're friendly.
+
+.. |Build Status| image:: https://travis-ci.org/pytoolz/toolz.svg?branch=master
+ :target: https://travis-ci.org/pytoolz/toolz
+.. |Coverage Status| image:: https://coveralls.io/repos/pytoolz/toolz/badge.svg?branch=master
+ :target: https://coveralls.io/r/pytoolz/toolz
+.. |Version Status| image:: https://badge.fury.io/py/toolz.svg
+ :target: https://badge.fury.io/py/toolz
+
+
diff --git a/contrib/python/toolz/py2/.dist-info/top_level.txt b/contrib/python/toolz/py2/.dist-info/top_level.txt
new file mode 100644
index 0000000000..e58ef014ac
--- /dev/null
+++ b/contrib/python/toolz/py2/.dist-info/top_level.txt
@@ -0,0 +1,2 @@
+tlz
+toolz
diff --git a/contrib/python/toolz/py2/LICENSE.txt b/contrib/python/toolz/py2/LICENSE.txt
new file mode 100644
index 0000000000..eeb91b202c
--- /dev/null
+++ b/contrib/python/toolz/py2/LICENSE.txt
@@ -0,0 +1,28 @@
+Copyright (c) 2013 Matthew Rocklin
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ a. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ b. 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.
+ c. Neither the name of toolz nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 REGENTS OR CONTRIBUTORS 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/contrib/python/toolz/py2/README.rst b/contrib/python/toolz/py2/README.rst
new file mode 100644
index 0000000000..099c3ff807
--- /dev/null
+++ b/contrib/python/toolz/py2/README.rst
@@ -0,0 +1,132 @@
+Toolz
+=====
+
+|Build Status| |Coverage Status| |Version Status|
+
+A set of utility functions for iterators, functions, and dictionaries.
+
+See the PyToolz documentation at https://toolz.readthedocs.io
+
+LICENSE
+-------
+
+New BSD. See `License File <https://github.com/pytoolz/toolz/blob/master/LICENSE.txt>`__.
+
+Install
+-------
+
+``toolz`` is on the Python Package Index (PyPI):
+
+::
+
+ pip install toolz
+
+Structure and Heritage
+----------------------
+
+``toolz`` is implemented in three parts:
+
+|literal itertoolz|_, for operations on iterables. Examples: ``groupby``,
+``unique``, ``interpose``,
+
+|literal functoolz|_, for higher-order functions. Examples: ``memoize``,
+``curry``, ``compose``,
+
+|literal dicttoolz|_, for operations on dictionaries. Examples: ``assoc``,
+``update-in``, ``merge``.
+
+.. |literal itertoolz| replace:: ``itertoolz``
+.. _literal itertoolz: https://github.com/pytoolz/toolz/blob/master/toolz/itertoolz.py
+
+.. |literal functoolz| replace:: ``functoolz``
+.. _literal functoolz: https://github.com/pytoolz/toolz/blob/master/toolz/functoolz.py
+
+.. |literal dicttoolz| replace:: ``dicttoolz``
+.. _literal dicttoolz: https://github.com/pytoolz/toolz/blob/master/toolz/dicttoolz.py
+
+These functions come from the legacy of functional languages for list
+processing. They interoperate well to accomplish common complex tasks.
+
+Read our `API
+Documentation <https://toolz.readthedocs.io/en/latest/api.html>`__ for
+more details.
+
+Example
+-------
+
+This builds a standard wordcount function from pieces within ``toolz``:
+
+.. code:: python
+
+ >>> def stem(word):
+ ... """ Stem word to primitive form """
+ ... return word.lower().rstrip(",.!:;'-\"").lstrip("'\"")
+
+ >>> from toolz import compose, frequencies, partial
+ >>> from toolz.curried import map
+ >>> wordcount = compose(frequencies, map(stem), str.split)
+
+ >>> sentence = "This cat jumped over this other cat!"
+ >>> wordcount(sentence)
+ {'this': 2, 'cat': 2, 'jumped': 1, 'over': 1, 'other': 1}
+
+Dependencies
+------------
+
+``toolz`` supports Python 2.7 and Python 3.4+ with a common codebase.
+It is pure Python and requires no dependencies beyond the standard
+library.
+
+It is, in short, a lightweight dependency.
+
+
+CyToolz
+-------
+
+The ``toolz`` project has been reimplemented in `Cython <http://cython.org>`__.
+The ``cytoolz`` project is a drop-in replacement for the Pure Python
+implementation.
+See `CyToolz GitHub Page <https://github.com/pytoolz/cytoolz/>`__ for more
+details.
+
+See Also
+--------
+
+- `Underscore.js <https://underscorejs.org/>`__: A similar library for
+ JavaScript
+- `Enumerable <https://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A
+ similar library for Ruby
+- `Clojure <https://clojure.org/>`__: A functional language whose
+ standard library has several counterparts in ``toolz``
+- `itertools <https://docs.python.org/2/library/itertools.html>`__: The
+ Python standard library for iterator tools
+- `functools <https://docs.python.org/2/library/functools.html>`__: The
+ Python standard library for function tools
+
+Contributions Welcome
+---------------------
+
+``toolz`` aims to be a repository for utility functions, particularly
+those that come from the functional programming and list processing
+traditions. We welcome contributions that fall within this scope.
+
+We also try to keep the API small to keep ``toolz`` manageable. The ideal
+contribution is significantly different from existing functions and has
+precedent in a few other functional systems.
+
+Please take a look at our
+`issue page <https://github.com/pytoolz/toolz/issues>`__
+for contribution ideas.
+
+Community
+---------
+
+See our `mailing list <https://groups.google.com/forum/#!forum/pytoolz>`__.
+We're friendly.
+
+.. |Build Status| image:: https://travis-ci.org/pytoolz/toolz.svg?branch=master
+ :target: https://travis-ci.org/pytoolz/toolz
+.. |Coverage Status| image:: https://coveralls.io/repos/pytoolz/toolz/badge.svg?branch=master
+ :target: https://coveralls.io/r/pytoolz/toolz
+.. |Version Status| image:: https://badge.fury.io/py/toolz.svg
+ :target: https://badge.fury.io/py/toolz
diff --git a/contrib/python/toolz/py2/tlz/__init__.py b/contrib/python/toolz/py2/tlz/__init__.py
new file mode 100644
index 0000000000..9c9c84afe1
--- /dev/null
+++ b/contrib/python/toolz/py2/tlz/__init__.py
@@ -0,0 +1,9 @@
+"""``tlz`` mirrors the ``toolz`` API and uses ``cytoolz`` if possible.
+
+The ``tlz`` package is installed when ``toolz`` is installed. It provides
+a convenient way to use functions from ``cytoolz``--a faster Cython
+implementation of ``toolz``--if it is installed, otherwise it uses
+functions from ``toolz``.
+"""
+
+from . import _build_tlz
diff --git a/contrib/python/toolz/py2/tlz/_build_tlz.py b/contrib/python/toolz/py2/tlz/_build_tlz.py
new file mode 100644
index 0000000000..3c017a542c
--- /dev/null
+++ b/contrib/python/toolz/py2/tlz/_build_tlz.py
@@ -0,0 +1,100 @@
+import sys
+import types
+import toolz
+from importlib import import_module
+
+
+class TlzLoader(object):
+ """ Finds and loads ``tlz`` modules when added to sys.meta_path"""
+ def __init__(self):
+ self.always_from_toolz = {
+ toolz.pipe,
+ }
+
+ def _load_toolz(self, fullname):
+ rv = {}
+ package, dot, submodules = fullname.partition('.')
+ try:
+ module_name = ''.join(['cytoolz', dot, submodules])
+ rv['cytoolz'] = import_module(module_name)
+ except ImportError:
+ pass
+ try:
+ module_name = ''.join(['toolz', dot, submodules])
+ rv['toolz'] = import_module(module_name)
+ except ImportError:
+ pass
+ if not rv:
+ raise ImportError(fullname)
+ return rv
+
+ def find_module(self, fullname, path=None): # pragma: py3 no cover
+ package, dot, submodules = fullname.partition('.')
+ if package == 'tlz':
+ return self
+
+ def load_module(self, fullname): # pragma: py3 no cover
+ if fullname in sys.modules: # pragma: no cover
+ return sys.modules[fullname]
+ spec = TlzSpec(fullname, self)
+ module = self.create_module(spec)
+ sys.modules[fullname] = module
+ self.exec_module(module)
+ return module
+
+ def find_spec(self, fullname, path, target=None): # pragma: no cover
+ package, dot, submodules = fullname.partition('.')
+ if package == 'tlz':
+ return TlzSpec(fullname, self)
+
+ def create_module(self, spec):
+ return types.ModuleType(spec.name)
+
+ def exec_module(self, module):
+ toolz_mods = self._load_toolz(module.__name__)
+ fast_mod = toolz_mods.get('cytoolz') or toolz_mods['toolz']
+ slow_mod = toolz_mods.get('toolz') or toolz_mods['cytoolz']
+ module.__dict__.update(toolz.merge(fast_mod.__dict__, module.__dict__))
+ package = fast_mod.__package__
+ if package is not None:
+ package, dot, submodules = package.partition('.')
+ module.__package__ = ''.join(['tlz', dot, submodules])
+ if not module.__doc__:
+ module.__doc__ = fast_mod.__doc__
+
+ # show file from toolz during introspection
+ module.__file__ = slow_mod.__file__
+
+ for k, v in fast_mod.__dict__.items():
+ tv = slow_mod.__dict__.get(k)
+ try:
+ hash(tv)
+ except TypeError:
+ tv = None
+ if tv in self.always_from_toolz:
+ module.__dict__[k] = tv
+ elif (
+ isinstance(v, types.ModuleType)
+ and v.__package__ == fast_mod.__name__
+ ):
+ package, dot, submodules = v.__name__.partition('.')
+ module_name = ''.join(['tlz', dot, submodules])
+ submodule = import_module(module_name)
+ module.__dict__[k] = submodule
+
+
+class TlzSpec(object):
+ def __init__(self, name, loader):
+ self.name = name
+ self.loader = loader
+ self.origin = None
+ self.submodule_search_locations = []
+ self.loader_state = None
+ self.cached = None
+ self.parent = None
+ self.has_location = False
+
+
+tlz_loader = TlzLoader()
+sys.meta_path.append(tlz_loader)
+tlz_loader.exec_module(sys.modules['tlz'])
diff --git a/contrib/python/toolz/py2/toolz/__init__.py b/contrib/python/toolz/py2/toolz/__init__.py
new file mode 100644
index 0000000000..7fa86ab473
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/__init__.py
@@ -0,0 +1,22 @@
+from .itertoolz import *
+
+from .functoolz import *
+
+from .dicttoolz import *
+
+from .recipes import *
+
+from .compatibility import map, filter
+
+from functools import partial, reduce
+
+sorted = sorted
+
+# Aliases
+comp = compose
+
+from . import curried, sandbox
+
+functoolz._sigs.create_signature_registry()
+
+__version__ = '0.10.0'
diff --git a/contrib/python/toolz/py2/toolz/_signatures.py b/contrib/python/toolz/py2/toolz/_signatures.py
new file mode 100644
index 0000000000..c55a778b3b
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/_signatures.py
@@ -0,0 +1,832 @@
+"""Internal module for better introspection of builtins.
+
+The main functions are ``is_builtin_valid_args``, ``is_builtin_partial_args``,
+and ``has_unknown_args``. Other functions in this module support these three.
+
+Notably, we create a ``signatures`` registry to enable introspection of
+builtin functions in any Python version. This includes builtins that
+have more than one valid signature. Currently, the registry includes
+builtins from ``builtins``, ``functools``, ``itertools``, and ``operator``
+modules. More can be added as requested. We don't guarantee full coverage.
+
+Everything in this module should be regarded as implementation details.
+Users should try to not use this module directly.
+"""
+import functools
+import inspect
+import itertools
+import operator
+from importlib import import_module
+
+from .compatibility import PY3
+from .functoolz import (is_partial_args, is_arity, has_varargs,
+ has_keywords, num_required_args)
+
+if PY3: # pragma: py2 no cover
+ import builtins
+else: # pragma: py3 no cover
+ import __builtin__ as builtins
+
+# We mock builtin callables using lists of tuples with lambda functions.
+#
+# The tuple spec is (num_position_args, lambda_func, keyword_only_args).
+#
+# num_position_args:
+# - The number of positional-only arguments. If not specified,
+# all positional arguments are considered positional-only.
+#
+# lambda_func:
+# - lambda function that matches a signature of a builtin, but does
+# not include keyword-only arguments.
+#
+# keyword_only_args: (optional)
+# - Tuple of keyword-only argumemts.
+
+module_info = {}
+
+module_info[builtins] = dict(
+ abs=[
+ lambda x: None],
+ all=[
+ lambda iterable: None],
+ any=[
+ lambda iterable: None],
+ apply=[
+ lambda object: None,
+ lambda object, args: None,
+ lambda object, args, kwargs: None],
+ ascii=[
+ lambda obj: None],
+ bin=[
+ lambda number: None],
+ bool=[
+ lambda x=False: None],
+ buffer=[
+ lambda object: None,
+ lambda object, offset: None,
+ lambda object, offset, size: None],
+ bytearray=[
+ lambda: None,
+ lambda int: None,
+ lambda string, encoding='utf8', errors='strict': None],
+ callable=[
+ lambda obj: None],
+ chr=[
+ lambda i: None],
+ classmethod=[
+ lambda function: None],
+ cmp=[
+ lambda x, y: None],
+ coerce=[
+ lambda x, y: None],
+ complex=[
+ lambda real=0, imag=0: None],
+ delattr=[
+ lambda obj, name: None],
+ dict=[
+ lambda **kwargs: None,
+ lambda mapping, **kwargs: None],
+ dir=[
+ lambda: None,
+ lambda object: None],
+ divmod=[
+ lambda x, y: None],
+ enumerate=[
+ (0, lambda iterable, start=0: None)],
+ eval=[
+ lambda source: None,
+ lambda source, globals: None,
+ lambda source, globals, locals: None],
+ execfile=[
+ lambda filename: None,
+ lambda filename, globals: None,
+ lambda filename, globals, locals: None],
+ file=[
+ (0, lambda name, mode='r', buffering=-1: None)],
+ filter=[
+ lambda function, iterable: None],
+ float=[
+ lambda x=0.0: None],
+ format=[
+ lambda value: None,
+ lambda value, format_spec: None],
+ frozenset=[
+ lambda: None,
+ lambda iterable: None],
+ getattr=[
+ lambda object, name: None,
+ lambda object, name, default: None],
+ globals=[
+ lambda: None],
+ hasattr=[
+ lambda obj, name: None],
+ hash=[
+ lambda obj: None],
+ hex=[
+ lambda number: None],
+ id=[
+ lambda obj: None],
+ input=[
+ lambda: None,
+ lambda prompt: None],
+ int=[
+ lambda x=0: None,
+ (0, lambda x, base=10: None)],
+ intern=[
+ lambda string: None],
+ isinstance=[
+ lambda obj, class_or_tuple: None],
+ issubclass=[
+ lambda cls, class_or_tuple: None],
+ iter=[
+ lambda iterable: None,
+ lambda callable, sentinel: None],
+ len=[
+ lambda obj: None],
+ list=[
+ lambda: None,
+ lambda iterable: None],
+ locals=[
+ lambda: None],
+ long=[
+ lambda x=0: None,
+ (0, lambda x, base=10: None)],
+ map=[
+ lambda func, sequence, *iterables: None],
+ memoryview=[
+ (0, lambda object: None)],
+ next=[
+ lambda iterator: None,
+ lambda iterator, default: None],
+ object=[
+ lambda: None],
+ oct=[
+ lambda number: None],
+ ord=[
+ lambda c: None],
+ pow=[
+ lambda x, y: None,
+ lambda x, y, z: None],
+ property=[
+ lambda fget=None, fset=None, fdel=None, doc=None: None],
+ range=[
+ lambda stop: None,
+ lambda start, stop: None,
+ lambda start, stop, step: None],
+ raw_input=[
+ lambda: None,
+ lambda prompt: None],
+ reduce=[
+ lambda function, sequence: None,
+ lambda function, sequence, initial: None],
+ reload=[
+ lambda module: None],
+ repr=[
+ lambda obj: None],
+ reversed=[
+ lambda sequence: None],
+ round=[
+ (0, lambda number, ndigits=0: None)],
+ set=[
+ lambda: None,
+ lambda iterable: None],
+ setattr=[
+ lambda obj, name, value: None],
+ slice=[
+ lambda stop: None,
+ lambda start, stop: None,
+ lambda start, stop, step: None],
+ staticmethod=[
+ lambda function: None],
+ sum=[
+ lambda iterable: None,
+ lambda iterable, start: None],
+ super=[
+ lambda type: None,
+ lambda type, obj: None],
+ tuple=[
+ lambda: None,
+ lambda iterable: None],
+ type=[
+ lambda object: None,
+ lambda name, bases, dict: None],
+ unichr=[
+ lambda i: None],
+ unicode=[
+ lambda object: None,
+ lambda string='', encoding='utf8', errors='strict': None],
+ vars=[
+ lambda: None,
+ lambda object: None],
+ xrange=[
+ lambda stop: None,
+ lambda start, stop: None,
+ lambda start, stop, step: None],
+ zip=[
+ lambda *iterables: None],
+ __build_class__=[
+ (2, lambda func, name, *bases, **kwds: None, ('metaclass',))],
+ __import__=[
+ (0, lambda name, globals=None, locals=None, fromlist=None,
+ level=None: None)],
+)
+module_info[builtins]['exec'] = [
+ lambda source: None,
+ lambda source, globals: None,
+ lambda source, globals, locals: None]
+
+if PY3: # pragma: py2 no cover
+ module_info[builtins].update(
+ breakpoint=[
+ lambda *args, **kws: None],
+ bytes=[
+ lambda: None,
+ lambda int: None,
+ lambda string, encoding='utf8', errors='strict': None],
+ compile=[
+ (0, lambda source, filename, mode, flags=0,
+ dont_inherit=False, optimize=-1: None)],
+ max=[
+ (1, lambda iterable: None, ('default', 'key',)),
+ (1, lambda arg1, arg2, *args: None, ('key',))],
+ min=[
+ (1, lambda iterable: None, ('default', 'key',)),
+ (1, lambda arg1, arg2, *args: None, ('key',))],
+ open=[
+ (0, lambda file, mode='r', buffering=-1, encoding=None,
+ errors=None, newline=None, closefd=True, opener=None: None)],
+ sorted=[
+ (1, lambda iterable: None, ('key', 'reverse'))],
+ str=[
+ lambda object='', encoding='utf', errors='strict': None],
+ )
+ module_info[builtins]['print'] = [
+ (0, lambda *args: None, ('sep', 'end', 'file', 'flush',))]
+
+else: # pragma: py3 no cover
+ module_info[builtins].update(
+ bytes=[
+ lambda object='': None],
+ compile=[
+ (0, lambda source, filename, mode, flags=0,
+ dont_inherit=False: None)],
+ max=[
+ (1, lambda iterable, *args: None, ('key',))],
+ min=[
+ (1, lambda iterable, *args: None, ('key',))],
+ open=[
+ (0, lambda file, mode='r', buffering=-1: None)],
+ sorted=[
+ lambda iterable, cmp=None, key=None, reverse=False: None],
+ str=[
+ lambda object='': None],
+ )
+ module_info[builtins]['print'] = [
+ (0, lambda *args: None, ('sep', 'end', 'file',))]
+
+module_info[functools] = dict(
+ cmp_to_key=[
+ (0, lambda mycmp: None)],
+ partial=[
+ lambda func, *args, **kwargs: None],
+ partialmethod=[
+ lambda func, *args, **kwargs: None],
+ reduce=[
+ lambda function, sequence: None,
+ lambda function, sequence, initial: None],
+)
+
+module_info[itertools] = dict(
+ accumulate=[
+ (0, lambda iterable, func=None: None)],
+ chain=[
+ lambda *iterables: None],
+ combinations=[
+ (0, lambda iterable, r: None)],
+ combinations_with_replacement=[
+ (0, lambda iterable, r: None)],
+ compress=[
+ (0, lambda data, selectors: None)],
+ count=[
+ lambda start=0, step=1: None],
+ cycle=[
+ lambda iterable: None],
+ dropwhile=[
+ lambda predicate, iterable: None],
+ filterfalse=[
+ lambda function, sequence: None],
+ groupby=[
+ (0, lambda iterable, key=None: None)],
+ ifilter=[
+ lambda function, sequence: None],
+ ifilterfalse=[
+ lambda function, sequence: None],
+ imap=[
+ lambda func, sequence, *iterables: None],
+ islice=[
+ lambda iterable, stop: None,
+ lambda iterable, start, stop: None,
+ lambda iterable, start, stop, step: None],
+ izip=[
+ lambda *iterables: None],
+ izip_longest=[
+ (0, lambda *iterables: None, ('fillvalue',))],
+ permutations=[
+ (0, lambda iterable, r=0: None)],
+ repeat=[
+ (0, lambda object, times=0: None)],
+ starmap=[
+ lambda function, sequence: None],
+ takewhile=[
+ lambda predicate, iterable: None],
+ tee=[
+ lambda iterable: None,
+ lambda iterable, n: None],
+ zip_longest=[
+ (0, lambda *iterables: None, ('fillvalue',))],
+)
+
+if PY3: # pragma: py2 no cover
+ module_info[itertools].update(
+ product=[
+ (0, lambda *iterables: None, ('repeat',))],
+ )
+else: # pragma: py3 no cover
+ module_info[itertools].update(
+ product=[
+ lambda *iterables: None],
+ )
+
+module_info[operator] = dict(
+ __abs__=[
+ lambda a: None],
+ __add__=[
+ lambda a, b: None],
+ __and__=[
+ lambda a, b: None],
+ __concat__=[
+ lambda a, b: None],
+ __contains__=[
+ lambda a, b: None],
+ __delitem__=[
+ lambda a, b: None],
+ __delslice__=[
+ lambda a, b, c: None],
+ __div__=[
+ lambda a, b: None],
+ __eq__=[
+ lambda a, b: None],
+ __floordiv__=[
+ lambda a, b: None],
+ __ge__=[
+ lambda a, b: None],
+ __getitem__=[
+ lambda a, b: None],
+ __getslice__=[
+ lambda a, b, c: None],
+ __gt__=[
+ lambda a, b: None],
+ __iadd__=[
+ lambda a, b: None],
+ __iand__=[
+ lambda a, b: None],
+ __iconcat__=[
+ lambda a, b: None],
+ __idiv__=[
+ lambda a, b: None],
+ __ifloordiv__=[
+ lambda a, b: None],
+ __ilshift__=[
+ lambda a, b: None],
+ __imatmul__=[
+ lambda a, b: None],
+ __imod__=[
+ lambda a, b: None],
+ __imul__=[
+ lambda a, b: None],
+ __index__=[
+ lambda a: None],
+ __inv__=[
+ lambda a: None],
+ __invert__=[
+ lambda a: None],
+ __ior__=[
+ lambda a, b: None],
+ __ipow__=[
+ lambda a, b: None],
+ __irepeat__=[
+ lambda a, b: None],
+ __irshift__=[
+ lambda a, b: None],
+ __isub__=[
+ lambda a, b: None],
+ __itruediv__=[
+ lambda a, b: None],
+ __ixor__=[
+ lambda a, b: None],
+ __le__=[
+ lambda a, b: None],
+ __lshift__=[
+ lambda a, b: None],
+ __lt__=[
+ lambda a, b: None],
+ __matmul__=[
+ lambda a, b: None],
+ __mod__=[
+ lambda a, b: None],
+ __mul__=[
+ lambda a, b: None],
+ __ne__=[
+ lambda a, b: None],
+ __neg__=[
+ lambda a: None],
+ __not__=[
+ lambda a: None],
+ __or__=[
+ lambda a, b: None],
+ __pos__=[
+ lambda a: None],
+ __pow__=[
+ lambda a, b: None],
+ __repeat__=[
+ lambda a, b: None],
+ __rshift__=[
+ lambda a, b: None],
+ __setitem__=[
+ lambda a, b, c: None],
+ __setslice__=[
+ lambda a, b, c, d: None],
+ __sub__=[
+ lambda a, b: None],
+ __truediv__=[
+ lambda a, b: None],
+ __xor__=[
+ lambda a, b: None],
+ _abs=[
+ lambda x: None],
+ _compare_digest=[
+ lambda a, b: None],
+ abs=[
+ lambda a: None],
+ add=[
+ lambda a, b: None],
+ and_=[
+ lambda a, b: None],
+ attrgetter=[
+ lambda attr, *args: None],
+ concat=[
+ lambda a, b: None],
+ contains=[
+ lambda a, b: None],
+ countOf=[
+ lambda a, b: None],
+ delitem=[
+ lambda a, b: None],
+ delslice=[
+ lambda a, b, c: None],
+ div=[
+ lambda a, b: None],
+ eq=[
+ lambda a, b: None],
+ floordiv=[
+ lambda a, b: None],
+ ge=[
+ lambda a, b: None],
+ getitem=[
+ lambda a, b: None],
+ getslice=[
+ lambda a, b, c: None],
+ gt=[
+ lambda a, b: None],
+ iadd=[
+ lambda a, b: None],
+ iand=[
+ lambda a, b: None],
+ iconcat=[
+ lambda a, b: None],
+ idiv=[
+ lambda a, b: None],
+ ifloordiv=[
+ lambda a, b: None],
+ ilshift=[
+ lambda a, b: None],
+ imatmul=[
+ lambda a, b: None],
+ imod=[
+ lambda a, b: None],
+ imul=[
+ lambda a, b: None],
+ index=[
+ lambda a: None],
+ indexOf=[
+ lambda a, b: None],
+ inv=[
+ lambda a: None],
+ invert=[
+ lambda a: None],
+ ior=[
+ lambda a, b: None],
+ ipow=[
+ lambda a, b: None],
+ irepeat=[
+ lambda a, b: None],
+ irshift=[
+ lambda a, b: None],
+ is_=[
+ lambda a, b: None],
+ is_not=[
+ lambda a, b: None],
+ isCallable=[
+ lambda a: None],
+ isMappingType=[
+ lambda a: None],
+ isNumberType=[
+ lambda a: None],
+ isSequenceType=[
+ lambda a: None],
+ isub=[
+ lambda a, b: None],
+ itemgetter=[
+ lambda item, *args: None],
+ itruediv=[
+ lambda a, b: None],
+ ixor=[
+ lambda a, b: None],
+ le=[
+ lambda a, b: None],
+ length_hint=[
+ lambda obj: None,
+ lambda obj, default: None],
+ lshift=[
+ lambda a, b: None],
+ lt=[
+ lambda a, b: None],
+ matmul=[
+ lambda a, b: None],
+ methodcaller=[
+ lambda name, *args, **kwargs: None],
+ mod=[
+ lambda a, b: None],
+ mul=[
+ lambda a, b: None],
+ ne=[
+ lambda a, b: None],
+ neg=[
+ lambda a: None],
+ not_=[
+ lambda a: None],
+ or_=[
+ lambda a, b: None],
+ pos=[
+ lambda a: None],
+ pow=[
+ lambda a, b: None],
+ repeat=[
+ lambda a, b: None],
+ rshift=[
+ lambda a, b: None],
+ sequenceIncludes=[
+ lambda a, b: None],
+ setitem=[
+ lambda a, b, c: None],
+ setslice=[
+ lambda a, b, c, d: None],
+ sub=[
+ lambda a, b: None],
+ truediv=[
+ lambda a, b: None],
+ truth=[
+ lambda a: None],
+ xor=[
+ lambda a, b: None],
+)
+
+module_info['toolz'] = dict(
+ curry=[
+ (0, lambda *args, **kwargs: None)],
+ excepts=[
+ (0, lambda exc, func, handler=None: None)],
+ flip=[
+ (0, lambda func=None, a=None, b=None: None)],
+ juxt=[
+ (0, lambda *funcs: None)],
+ memoize=[
+ (0, lambda func=None, cache=None, key=None: None)],
+)
+
+module_info['toolz.functoolz'] = dict(
+ Compose=[
+ (0, lambda funcs: None)],
+ InstanceProperty=[
+ (0, lambda fget=None, fset=None, fdel=None, doc=None,
+ classval=None: None)],
+)
+
+if PY3: # pragma: py2 no cover
+ def num_pos_args(sigspec):
+ """ Return the number of positional arguments. ``f(x, y=1)`` has 1"""
+ return sum(1 for x in sigspec.parameters.values()
+ if x.kind == x.POSITIONAL_OR_KEYWORD
+ and x.default is x.empty)
+
+ def get_exclude_keywords(num_pos_only, sigspec):
+ """ Return the names of position-only arguments if func has **kwargs"""
+ if num_pos_only == 0:
+ return ()
+ has_kwargs = any(x.kind == x.VAR_KEYWORD
+ for x in sigspec.parameters.values())
+ if not has_kwargs:
+ return ()
+ pos_args = list(sigspec.parameters.values())[:num_pos_only]
+ return tuple(x.name for x in pos_args)
+
+ def signature_or_spec(func):
+ try:
+ return inspect.signature(func)
+ except (ValueError, TypeError):
+ return None
+
+else: # pragma: py3 no cover
+ def num_pos_args(sigspec):
+ """ Return the number of positional arguments. ``f(x, y=1)`` has 1"""
+ if sigspec.defaults:
+ return len(sigspec.args) - len(sigspec.defaults)
+ return len(sigspec.args)
+
+ def get_exclude_keywords(num_pos_only, sigspec):
+ """ Return the names of position-only arguments if func has **kwargs"""
+ if num_pos_only == 0:
+ return ()
+ has_kwargs = sigspec.keywords is not None
+ if not has_kwargs:
+ return ()
+ return tuple(sigspec.args[:num_pos_only])
+
+ def signature_or_spec(func):
+ try:
+ return inspect.getargspec(func)
+ except TypeError:
+ return None
+
+
+def expand_sig(sig):
+ """ Convert the signature spec in ``module_info`` to add to ``signatures``
+
+ The input signature spec is one of:
+ - ``lambda_func``
+ - ``(num_position_args, lambda_func)``
+ - ``(num_position_args, lambda_func, keyword_only_args)``
+
+ The output signature spec is:
+ ``(num_position_args, lambda_func, keyword_exclude, sigspec)``
+
+ where ``keyword_exclude`` includes keyword only arguments and, if variadic
+ keywords is present, the names of position-only argument. The latter is
+ included to support builtins such as ``partial(func, *args, **kwargs)``,
+ which allows ``func=`` to be used as a keyword even though it's the name
+ of a positional argument.
+ """
+ if isinstance(sig, tuple):
+ if len(sig) == 3:
+ num_pos_only, func, keyword_only = sig
+ assert isinstance(sig[-1], tuple)
+ else:
+ num_pos_only, func = sig
+ keyword_only = ()
+ sigspec = signature_or_spec(func)
+ else:
+ func = sig
+ sigspec = signature_or_spec(func)
+ num_pos_only = num_pos_args(sigspec)
+ keyword_only = ()
+ keyword_exclude = get_exclude_keywords(num_pos_only, sigspec)
+ return num_pos_only, func, keyword_only + keyword_exclude, sigspec
+
+
+signatures = {}
+
+
+def create_signature_registry(module_info=module_info, signatures=signatures):
+ for module, info in module_info.items():
+ if isinstance(module, str):
+ module = import_module(module)
+ for name, sigs in info.items():
+ if hasattr(module, name):
+ new_sigs = tuple(expand_sig(sig) for sig in sigs)
+ signatures[getattr(module, name)] = new_sigs
+
+
+def check_valid(sig, args, kwargs):
+ """ Like ``is_valid_args`` for the given signature spec"""
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ if len(args) < num_pos_only:
+ return False
+ if keyword_exclude:
+ kwargs = dict(kwargs)
+ for item in keyword_exclude:
+ kwargs.pop(item, None)
+ try:
+ func(*args, **kwargs)
+ return True
+ except TypeError:
+ return False
+
+
+def _is_valid_args(func, args, kwargs):
+ """ Like ``is_valid_args`` for builtins in our ``signatures`` registry"""
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ return any(check_valid(sig, args, kwargs) for sig in sigs)
+
+
+def check_partial(sig, args, kwargs):
+ """ Like ``is_partial_args`` for the given signature spec"""
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ if len(args) < num_pos_only:
+ pad = (None,) * (num_pos_only - len(args))
+ args = args + pad
+ if keyword_exclude:
+ kwargs = dict(kwargs)
+ for item in keyword_exclude:
+ kwargs.pop(item, None)
+ return is_partial_args(func, args, kwargs, sigspec)
+
+
+def _is_partial_args(func, args, kwargs):
+ """ Like ``is_partial_args`` for builtins in our ``signatures`` registry"""
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ return any(check_partial(sig, args, kwargs) for sig in sigs)
+
+
+def check_arity(n, sig):
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ if keyword_exclude or num_pos_only > n:
+ return False
+ return is_arity(n, func, sigspec)
+
+
+def _is_arity(n, func):
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ checks = [check_arity(n, sig) for sig in sigs]
+ if all(checks):
+ return True
+ elif any(checks):
+ return None
+ return False
+
+
+def check_varargs(sig):
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ return has_varargs(func, sigspec)
+
+
+def _has_varargs(func):
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ checks = [check_varargs(sig) for sig in sigs]
+ if all(checks):
+ return True
+ elif any(checks): # pragma: py2 no cover
+ return None
+ return False
+
+
+def check_keywords(sig):
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ if keyword_exclude:
+ return True
+ return has_keywords(func, sigspec)
+
+
+def _has_keywords(func):
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ checks = [check_keywords(sig) for sig in sigs]
+ if all(checks):
+ return True
+ elif any(checks):
+ return None
+ return False
+
+
+def check_required_args(sig):
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ return num_required_args(func, sigspec)
+
+
+def _num_required_args(func):
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ vals = [check_required_args(sig) for sig in sigs]
+ val = vals[0]
+ if all(x == val for x in vals):
+ return val
+ return None
diff --git a/contrib/python/toolz/py2/toolz/compatibility.py b/contrib/python/toolz/py2/toolz/compatibility.py
new file mode 100644
index 0000000000..51e3673fad
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/compatibility.py
@@ -0,0 +1,34 @@
+import operator
+import sys
+PY3 = sys.version_info[0] > 2
+PY34 = sys.version_info[0] == 3 and sys.version_info[1] == 4
+PYPY = hasattr(sys, 'pypy_version_info')
+
+__all__ = ('map', 'filter', 'range', 'zip', 'reduce', 'zip_longest',
+ 'iteritems', 'iterkeys', 'itervalues', 'filterfalse',
+ 'PY3', 'PY34', 'PYPY')
+
+if PY3:
+ map = map
+ filter = filter
+ range = range
+ zip = zip
+ from functools import reduce
+ from itertools import zip_longest
+ from itertools import filterfalse
+ iteritems = operator.methodcaller('items')
+ iterkeys = operator.methodcaller('keys')
+ itervalues = operator.methodcaller('values')
+ from collections.abc import Sequence
+else:
+ range = xrange
+ reduce = reduce
+ from itertools import imap as map
+ from itertools import ifilter as filter
+ from itertools import ifilterfalse as filterfalse
+ from itertools import izip as zip
+ from itertools import izip_longest as zip_longest
+ iteritems = operator.methodcaller('iteritems')
+ iterkeys = operator.methodcaller('iterkeys')
+ itervalues = operator.methodcaller('itervalues')
+ from collections import Sequence
diff --git a/contrib/python/toolz/py2/toolz/curried/__init__.py b/contrib/python/toolz/py2/toolz/curried/__init__.py
new file mode 100644
index 0000000000..356eddbd3b
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/curried/__init__.py
@@ -0,0 +1,103 @@
+"""
+Alternate namespace for toolz such that all functions are curried
+
+Currying provides implicit partial evaluation of all functions
+
+Example:
+
+ Get usually requires two arguments, an index and a collection
+ >>> from toolz.curried import get
+ >>> get(0, ('a', 'b'))
+ 'a'
+
+ When we use it in higher order functions we often want to pass a partially
+ evaluated form
+ >>> data = [(1, 2), (11, 22), (111, 222)]
+ >>> list(map(lambda seq: get(0, seq), data))
+ [1, 11, 111]
+
+ The curried version allows simple expression of partial evaluation
+ >>> list(map(get(0), data))
+ [1, 11, 111]
+
+See Also:
+ toolz.functoolz.curry
+"""
+import toolz
+from . import operator
+from toolz import (
+ apply,
+ comp,
+ complement,
+ compose,
+ compose_left,
+ concat,
+ concatv,
+ count,
+ curry,
+ diff,
+ first,
+ flip,
+ frequencies,
+ identity,
+ interleave,
+ isdistinct,
+ isiterable,
+ juxt,
+ last,
+ memoize,
+ merge_sorted,
+ peek,
+ pipe,
+ second,
+ thread_first,
+ thread_last,
+)
+from .exceptions import merge, merge_with
+
+accumulate = toolz.curry(toolz.accumulate)
+assoc = toolz.curry(toolz.assoc)
+assoc_in = toolz.curry(toolz.assoc_in)
+cons = toolz.curry(toolz.cons)
+countby = toolz.curry(toolz.countby)
+dissoc = toolz.curry(toolz.dissoc)
+do = toolz.curry(toolz.do)
+drop = toolz.curry(toolz.drop)
+excepts = toolz.curry(toolz.excepts)
+filter = toolz.curry(toolz.filter)
+get = toolz.curry(toolz.get)
+get_in = toolz.curry(toolz.get_in)
+groupby = toolz.curry(toolz.groupby)
+interpose = toolz.curry(toolz.interpose)
+itemfilter = toolz.curry(toolz.itemfilter)
+itemmap = toolz.curry(toolz.itemmap)
+iterate = toolz.curry(toolz.iterate)
+join = toolz.curry(toolz.join)
+keyfilter = toolz.curry(toolz.keyfilter)
+keymap = toolz.curry(toolz.keymap)
+map = toolz.curry(toolz.map)
+mapcat = toolz.curry(toolz.mapcat)
+nth = toolz.curry(toolz.nth)
+partial = toolz.curry(toolz.partial)
+partition = toolz.curry(toolz.partition)
+partition_all = toolz.curry(toolz.partition_all)
+partitionby = toolz.curry(toolz.partitionby)
+peekn = toolz.curry(toolz.peekn)
+pluck = toolz.curry(toolz.pluck)
+random_sample = toolz.curry(toolz.random_sample)
+reduce = toolz.curry(toolz.reduce)
+reduceby = toolz.curry(toolz.reduceby)
+remove = toolz.curry(toolz.remove)
+sliding_window = toolz.curry(toolz.sliding_window)
+sorted = toolz.curry(toolz.sorted)
+tail = toolz.curry(toolz.tail)
+take = toolz.curry(toolz.take)
+take_nth = toolz.curry(toolz.take_nth)
+topk = toolz.curry(toolz.topk)
+unique = toolz.curry(toolz.unique)
+update_in = toolz.curry(toolz.update_in)
+valfilter = toolz.curry(toolz.valfilter)
+valmap = toolz.curry(toolz.valmap)
+
+del exceptions
+del toolz
diff --git a/contrib/python/toolz/py2/toolz/curried/exceptions.py b/contrib/python/toolz/py2/toolz/curried/exceptions.py
new file mode 100644
index 0000000000..75a52bbbf2
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/curried/exceptions.py
@@ -0,0 +1,18 @@
+import toolz
+
+
+__all__ = ['merge_with', 'merge']
+
+
+@toolz.curry
+def merge_with(func, d, *dicts, **kwargs):
+ return toolz.merge_with(func, d, *dicts, **kwargs)
+
+
+@toolz.curry
+def merge(d, *dicts, **kwargs):
+ return toolz.merge(d, *dicts, **kwargs)
+
+
+merge_with.__doc__ = toolz.merge_with.__doc__
+merge.__doc__ = toolz.merge.__doc__
diff --git a/contrib/python/toolz/py2/toolz/curried/operator.py b/contrib/python/toolz/py2/toolz/curried/operator.py
new file mode 100644
index 0000000000..8bc9e52317
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/curried/operator.py
@@ -0,0 +1,23 @@
+from __future__ import absolute_import
+
+import operator
+
+from toolz.functoolz import curry, num_required_args, has_keywords
+
+
+def should_curry(f):
+ num = num_required_args(f)
+ return num is None or num > 1 or num == 1 and has_keywords(f) is not False
+
+
+locals().update(
+ {name: curry(f) if should_curry(f) else f
+ for name, f in vars(operator).items() if callable(f)},
+)
+
+# Clean up the namespace.
+del curry
+del num_required_args
+del has_keywords
+del operator
+del should_curry
diff --git a/contrib/python/toolz/py2/toolz/dicttoolz.py b/contrib/python/toolz/py2/toolz/dicttoolz.py
new file mode 100644
index 0000000000..91bff23cef
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/dicttoolz.py
@@ -0,0 +1,337 @@
+import operator
+from toolz.compatibility import (map, zip, iteritems, iterkeys, itervalues,
+ reduce)
+
+__all__ = ('merge', 'merge_with', 'valmap', 'keymap', 'itemmap',
+ 'valfilter', 'keyfilter', 'itemfilter',
+ 'assoc', 'dissoc', 'assoc_in', 'update_in', 'get_in')
+
+
+def _get_factory(f, kwargs):
+ factory = kwargs.pop('factory', dict)
+ if kwargs:
+ raise TypeError("{}() got an unexpected keyword argument "
+ "'{}'".format(f.__name__, kwargs.popitem()[0]))
+ return factory
+
+
+def merge(*dicts, **kwargs):
+ """ Merge a collection of dictionaries
+
+ >>> merge({1: 'one'}, {2: 'two'})
+ {1: 'one', 2: 'two'}
+
+ Later dictionaries have precedence
+
+ >>> merge({1: 2, 3: 4}, {3: 3, 4: 4})
+ {1: 2, 3: 3, 4: 4}
+
+ See Also:
+ merge_with
+ """
+ if len(dicts) == 1 and not isinstance(dicts[0], dict):
+ dicts = dicts[0]
+ factory = _get_factory(merge, kwargs)
+
+ rv = factory()
+ for d in dicts:
+ rv.update(d)
+ return rv
+
+
+def merge_with(func, *dicts, **kwargs):
+ """ Merge dictionaries and apply function to combined values
+
+ A key may occur in more than one dict, and all values mapped from the key
+ will be passed to the function as a list, such as func([val1, val2, ...]).
+
+ >>> merge_with(sum, {1: 1, 2: 2}, {1: 10, 2: 20})
+ {1: 11, 2: 22}
+
+ >>> merge_with(first, {1: 1, 2: 2}, {2: 20, 3: 30}) # doctest: +SKIP
+ {1: 1, 2: 2, 3: 30}
+
+ See Also:
+ merge
+ """
+ if len(dicts) == 1 and not isinstance(dicts[0], dict):
+ dicts = dicts[0]
+ factory = _get_factory(merge_with, kwargs)
+
+ result = factory()
+ for d in dicts:
+ for k, v in iteritems(d):
+ if k not in result:
+ result[k] = [v]
+ else:
+ result[k].append(v)
+ return valmap(func, result, factory)
+
+
+def valmap(func, d, factory=dict):
+ """ Apply function to values of dictionary
+
+ >>> bills = {"Alice": [20, 15, 30], "Bob": [10, 35]}
+ >>> valmap(sum, bills) # doctest: +SKIP
+ {'Alice': 65, 'Bob': 45}
+
+ See Also:
+ keymap
+ itemmap
+ """
+ rv = factory()
+ rv.update(zip(iterkeys(d), map(func, itervalues(d))))
+ return rv
+
+
+def keymap(func, d, factory=dict):
+ """ Apply function to keys of dictionary
+
+ >>> bills = {"Alice": [20, 15, 30], "Bob": [10, 35]}
+ >>> keymap(str.lower, bills) # doctest: +SKIP
+ {'alice': [20, 15, 30], 'bob': [10, 35]}
+
+ See Also:
+ valmap
+ itemmap
+ """
+ rv = factory()
+ rv.update(zip(map(func, iterkeys(d)), itervalues(d)))
+ return rv
+
+
+def itemmap(func, d, factory=dict):
+ """ Apply function to items of dictionary
+
+ >>> accountids = {"Alice": 10, "Bob": 20}
+ >>> itemmap(reversed, accountids) # doctest: +SKIP
+ {10: "Alice", 20: "Bob"}
+
+ See Also:
+ keymap
+ valmap
+ """
+ rv = factory()
+ rv.update(map(func, iteritems(d)))
+ return rv
+
+
+def valfilter(predicate, d, factory=dict):
+ """ Filter items in dictionary by value
+
+ >>> iseven = lambda x: x % 2 == 0
+ >>> d = {1: 2, 2: 3, 3: 4, 4: 5}
+ >>> valfilter(iseven, d)
+ {1: 2, 3: 4}
+
+ See Also:
+ keyfilter
+ itemfilter
+ valmap
+ """
+ rv = factory()
+ for k, v in iteritems(d):
+ if predicate(v):
+ rv[k] = v
+ return rv
+
+
+def keyfilter(predicate, d, factory=dict):
+ """ Filter items in dictionary by key
+
+ >>> iseven = lambda x: x % 2 == 0
+ >>> d = {1: 2, 2: 3, 3: 4, 4: 5}
+ >>> keyfilter(iseven, d)
+ {2: 3, 4: 5}
+
+ See Also:
+ valfilter
+ itemfilter
+ keymap
+ """
+ rv = factory()
+ for k, v in iteritems(d):
+ if predicate(k):
+ rv[k] = v
+ return rv
+
+
+def itemfilter(predicate, d, factory=dict):
+ """ Filter items in dictionary by item
+
+ >>> def isvalid(item):
+ ... k, v = item
+ ... return k % 2 == 0 and v < 4
+
+ >>> d = {1: 2, 2: 3, 3: 4, 4: 5}
+ >>> itemfilter(isvalid, d)
+ {2: 3}
+
+ See Also:
+ keyfilter
+ valfilter
+ itemmap
+ """
+ rv = factory()
+ for item in iteritems(d):
+ if predicate(item):
+ k, v = item
+ rv[k] = v
+ return rv
+
+
+def assoc(d, key, value, factory=dict):
+ """ Return a new dict with new key value pair
+
+ New dict has d[key] set to value. Does not modify the initial dictionary.
+
+ >>> assoc({'x': 1}, 'x', 2)
+ {'x': 2}
+ >>> assoc({'x': 1}, 'y', 3) # doctest: +SKIP
+ {'x': 1, 'y': 3}
+ """
+ d2 = factory()
+ d2.update(d)
+ d2[key] = value
+ return d2
+
+
+def dissoc(d, *keys, **kwargs):
+ """ Return a new dict with the given key(s) removed.
+
+ New dict has d[key] deleted for each supplied key.
+ Does not modify the initial dictionary.
+
+ >>> dissoc({'x': 1, 'y': 2}, 'y')
+ {'x': 1}
+ >>> dissoc({'x': 1, 'y': 2}, 'y', 'x')
+ {}
+ >>> dissoc({'x': 1}, 'y') # Ignores missing keys
+ {'x': 1}
+ """
+ factory = _get_factory(dissoc, kwargs)
+ d2 = factory()
+
+ if len(keys) < len(d) * .6:
+ d2.update(d)
+ for key in keys:
+ if key in d2:
+ del d2[key]
+ else:
+ remaining = set(d)
+ remaining.difference_update(keys)
+ for k in remaining:
+ d2[k] = d[k]
+ return d2
+
+
+def assoc_in(d, keys, value, factory=dict):
+ """ Return a new dict with new, potentially nested, key value pair
+
+ >>> purchase = {'name': 'Alice',
+ ... 'order': {'items': ['Apple', 'Orange'],
+ ... 'costs': [0.50, 1.25]},
+ ... 'credit card': '5555-1234-1234-1234'}
+ >>> assoc_in(purchase, ['order', 'costs'], [0.25, 1.00]) # doctest: +SKIP
+ {'credit card': '5555-1234-1234-1234',
+ 'name': 'Alice',
+ 'order': {'costs': [0.25, 1.00], 'items': ['Apple', 'Orange']}}
+ """
+ return update_in(d, keys, lambda x: value, value, factory)
+
+
+def update_in(d, keys, func, default=None, factory=dict):
+ """ Update value in a (potentially) nested dictionary
+
+ inputs:
+ d - dictionary on which to operate
+ keys - list or tuple giving the location of the value to be changed in d
+ func - function to operate on that value
+
+ If keys == [k0,..,kX] and d[k0]..[kX] == v, update_in returns a copy of the
+ original dictionary with v replaced by func(v), but does not mutate the
+ original dictionary.
+
+ If k0 is not a key in d, update_in creates nested dictionaries to the depth
+ specified by the keys, with the innermost value set to func(default).
+
+ >>> inc = lambda x: x + 1
+ >>> update_in({'a': 0}, ['a'], inc)
+ {'a': 1}
+
+ >>> transaction = {'name': 'Alice',
+ ... 'purchase': {'items': ['Apple', 'Orange'],
+ ... 'costs': [0.50, 1.25]},
+ ... 'credit card': '5555-1234-1234-1234'}
+ >>> update_in(transaction, ['purchase', 'costs'], sum) # doctest: +SKIP
+ {'credit card': '5555-1234-1234-1234',
+ 'name': 'Alice',
+ 'purchase': {'costs': 1.75, 'items': ['Apple', 'Orange']}}
+
+ >>> # updating a value when k0 is not in d
+ >>> update_in({}, [1, 2, 3], str, default="bar")
+ {1: {2: {3: 'bar'}}}
+ >>> update_in({1: 'foo'}, [2, 3, 4], inc, 0)
+ {1: 'foo', 2: {3: {4: 1}}}
+ """
+ ks = iter(keys)
+ k = next(ks)
+
+ rv = inner = factory()
+ rv.update(d)
+
+ for key in ks:
+ if k in d:
+ d = d[k]
+ dtemp = factory()
+ dtemp.update(d)
+ else:
+ d = dtemp = factory()
+
+ inner[k] = inner = dtemp
+ k = key
+
+ if k in d:
+ inner[k] = func(d[k])
+ else:
+ inner[k] = func(default)
+ return rv
+
+
+def get_in(keys, coll, default=None, no_default=False):
+ """ Returns coll[i0][i1]...[iX] where [i0, i1, ..., iX]==keys.
+
+ If coll[i0][i1]...[iX] cannot be found, returns ``default``, unless
+ ``no_default`` is specified, then it raises KeyError or IndexError.
+
+ ``get_in`` is a generalization of ``operator.getitem`` for nested data
+ structures such as dictionaries and lists.
+
+ >>> transaction = {'name': 'Alice',
+ ... 'purchase': {'items': ['Apple', 'Orange'],
+ ... 'costs': [0.50, 1.25]},
+ ... 'credit card': '5555-1234-1234-1234'}
+ >>> get_in(['purchase', 'items', 0], transaction)
+ 'Apple'
+ >>> get_in(['name'], transaction)
+ 'Alice'
+ >>> get_in(['purchase', 'total'], transaction)
+ >>> get_in(['purchase', 'items', 'apple'], transaction)
+ >>> get_in(['purchase', 'items', 10], transaction)
+ >>> get_in(['purchase', 'total'], transaction, 0)
+ 0
+ >>> get_in(['y'], {}, no_default=True)
+ Traceback (most recent call last):
+ ...
+ KeyError: 'y'
+
+ See Also:
+ itertoolz.get
+ operator.getitem
+ """
+ try:
+ return reduce(operator.getitem, keys, coll)
+ except (KeyError, IndexError, TypeError):
+ if no_default:
+ raise
+ return default
diff --git a/contrib/python/toolz/py2/toolz/functoolz.py b/contrib/python/toolz/py2/toolz/functoolz.py
new file mode 100644
index 0000000000..01d3857a19
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/functoolz.py
@@ -0,0 +1,1152 @@
+from functools import reduce, partial
+import inspect
+import operator
+from operator import attrgetter
+from importlib import import_module
+from textwrap import dedent
+from types import MethodType
+
+from .compatibility import PY3, PY34, PYPY
+from .utils import no_default
+
+
+__all__ = ('identity', 'apply', 'thread_first', 'thread_last', 'memoize',
+ 'compose', 'compose_left', 'pipe', 'complement', 'juxt', 'do',
+ 'curry', 'flip', 'excepts')
+
+
+def identity(x):
+ """ Identity function. Return x
+
+ >>> identity(3)
+ 3
+ """
+ return x
+
+
+def apply(*func_and_args, **kwargs):
+ """ Applies a function and returns the results
+
+ >>> def double(x): return 2*x
+ >>> def inc(x): return x + 1
+ >>> apply(double, 5)
+ 10
+
+ >>> tuple(map(apply, [double, inc, double], [10, 500, 8000]))
+ (20, 501, 16000)
+ """
+ if not func_and_args:
+ raise TypeError('func argument is required')
+ func, args = func_and_args[0], func_and_args[1:]
+ return func(*args, **kwargs)
+
+
+def thread_first(val, *forms):
+ """ Thread value through a sequence of functions/forms
+
+ >>> def double(x): return 2*x
+ >>> def inc(x): return x + 1
+ >>> thread_first(1, inc, double)
+ 4
+
+ If the function expects more than one input you can specify those inputs
+ in a tuple. The value is used as the first input.
+
+ >>> def add(x, y): return x + y
+ >>> def pow(x, y): return x**y
+ >>> thread_first(1, (add, 4), (pow, 2)) # pow(add(1, 4), 2)
+ 25
+
+ So in general
+ thread_first(x, f, (g, y, z))
+ expands to
+ g(f(x), y, z)
+
+ See Also:
+ thread_last
+ """
+ def evalform_front(val, form):
+ if callable(form):
+ return form(val)
+ if isinstance(form, tuple):
+ func, args = form[0], form[1:]
+ args = (val,) + args
+ return func(*args)
+ return reduce(evalform_front, forms, val)
+
+
+def thread_last(val, *forms):
+ """ Thread value through a sequence of functions/forms
+
+ >>> def double(x): return 2*x
+ >>> def inc(x): return x + 1
+ >>> thread_last(1, inc, double)
+ 4
+
+ If the function expects more than one input you can specify those inputs
+ in a tuple. The value is used as the last input.
+
+ >>> def add(x, y): return x + y
+ >>> def pow(x, y): return x**y
+ >>> thread_last(1, (add, 4), (pow, 2)) # pow(2, add(4, 1))
+ 32
+
+ So in general
+ thread_last(x, f, (g, y, z))
+ expands to
+ g(y, z, f(x))
+
+ >>> def iseven(x):
+ ... return x % 2 == 0
+ >>> list(thread_last([1, 2, 3], (map, inc), (filter, iseven)))
+ [2, 4]
+
+ See Also:
+ thread_first
+ """
+ def evalform_back(val, form):
+ if callable(form):
+ return form(val)
+ if isinstance(form, tuple):
+ func, args = form[0], form[1:]
+ args = args + (val,)
+ return func(*args)
+ return reduce(evalform_back, forms, val)
+
+
+def instanceproperty(fget=None, fset=None, fdel=None, doc=None, classval=None):
+ """ Like @property, but returns ``classval`` when used as a class attribute
+
+ >>> class MyClass(object):
+ ... '''The class docstring'''
+ ... @instanceproperty(classval=__doc__)
+ ... def __doc__(self):
+ ... return 'An object docstring'
+ ... @instanceproperty
+ ... def val(self):
+ ... return 42
+ ...
+ >>> MyClass.__doc__
+ 'The class docstring'
+ >>> MyClass.val is None
+ True
+ >>> obj = MyClass()
+ >>> obj.__doc__
+ 'An object docstring'
+ >>> obj.val
+ 42
+ """
+ if fget is None:
+ return partial(instanceproperty, fset=fset, fdel=fdel, doc=doc,
+ classval=classval)
+ return InstanceProperty(fget=fget, fset=fset, fdel=fdel, doc=doc,
+ classval=classval)
+
+
+class InstanceProperty(property):
+ """ Like @property, but returns ``classval`` when used as a class attribute
+
+ Should not be used directly. Use ``instanceproperty`` instead.
+ """
+ def __init__(self, fget=None, fset=None, fdel=None, doc=None,
+ classval=None):
+ self.classval = classval
+ property.__init__(self, fget=fget, fset=fset, fdel=fdel, doc=doc)
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self.classval
+ return property.__get__(self, obj, type)
+
+ def __reduce__(self):
+ state = (self.fget, self.fset, self.fdel, self.__doc__, self.classval)
+ return InstanceProperty, state
+
+
+class curry(object):
+ """ Curry a callable function
+
+ Enables partial application of arguments through calling a function with an
+ incomplete set of arguments.
+
+ >>> def mul(x, y):
+ ... return x * y
+ >>> mul = curry(mul)
+
+ >>> double = mul(2)
+ >>> double(10)
+ 20
+
+ Also supports keyword arguments
+
+ >>> @curry # Can use curry as a decorator
+ ... def f(x, y, a=10):
+ ... return a * (x + y)
+
+ >>> add = f(a=1)
+ >>> add(2, 3)
+ 5
+
+ See Also:
+ toolz.curried - namespace of curried functions
+ https://toolz.readthedocs.io/en/latest/curry.html
+ """
+ def __init__(self, *args, **kwargs):
+ if not args:
+ raise TypeError('__init__() takes at least 2 arguments (1 given)')
+ func, args = args[0], args[1:]
+ if not callable(func):
+ raise TypeError("Input must be callable")
+
+ # curry- or functools.partial-like object? Unpack and merge arguments
+ if (
+ hasattr(func, 'func')
+ and hasattr(func, 'args')
+ and hasattr(func, 'keywords')
+ and isinstance(func.args, tuple)
+ ):
+ _kwargs = {}
+ if func.keywords:
+ _kwargs.update(func.keywords)
+ _kwargs.update(kwargs)
+ kwargs = _kwargs
+ args = func.args + args
+ func = func.func
+
+ if kwargs:
+ self._partial = partial(func, *args, **kwargs)
+ else:
+ self._partial = partial(func, *args)
+
+ self.__doc__ = getattr(func, '__doc__', None)
+ self.__name__ = getattr(func, '__name__', '<curry>')
+ self.__module__ = getattr(func, '__module__', None)
+ self.__qualname__ = getattr(func, '__qualname__', None)
+ self._sigspec = None
+ self._has_unknown_args = None
+
+ @instanceproperty
+ def func(self):
+ return self._partial.func
+
+ if PY3: # pragma: py2 no cover
+ @instanceproperty
+ def __signature__(self):
+ sig = inspect.signature(self.func)
+ args = self.args or ()
+ keywords = self.keywords or {}
+ if is_partial_args(self.func, args, keywords, sig) is False:
+ raise TypeError('curry object has incorrect arguments')
+
+ params = list(sig.parameters.values())
+ skip = 0
+ for param in params[:len(args)]:
+ if param.kind == param.VAR_POSITIONAL:
+ break
+ skip += 1
+
+ kwonly = False
+ newparams = []
+ for param in params[skip:]:
+ kind = param.kind
+ default = param.default
+ if kind == param.VAR_KEYWORD:
+ pass
+ elif kind == param.VAR_POSITIONAL:
+ if kwonly:
+ continue
+ elif param.name in keywords:
+ default = keywords[param.name]
+ kind = param.KEYWORD_ONLY
+ kwonly = True
+ else:
+ if kwonly:
+ kind = param.KEYWORD_ONLY
+ if default is param.empty:
+ default = no_default
+ newparams.append(param.replace(default=default, kind=kind))
+
+ return sig.replace(parameters=newparams)
+
+ @instanceproperty
+ def args(self):
+ return self._partial.args
+
+ @instanceproperty
+ def keywords(self):
+ return self._partial.keywords
+
+ @instanceproperty
+ def func_name(self):
+ return self.__name__
+
+ def __str__(self):
+ return str(self.func)
+
+ def __repr__(self):
+ return repr(self.func)
+
+ def __hash__(self):
+ return hash((self.func, self.args,
+ frozenset(self.keywords.items()) if self.keywords
+ else None))
+
+ def __eq__(self, other):
+ return (isinstance(other, curry) and self.func == other.func and
+ self.args == other.args and self.keywords == other.keywords)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __call__(self, *args, **kwargs):
+ try:
+ return self._partial(*args, **kwargs)
+ except TypeError as exc:
+ if self._should_curry(args, kwargs, exc):
+ return self.bind(*args, **kwargs)
+ raise
+
+ def _should_curry(self, args, kwargs, exc=None):
+ func = self.func
+ args = self.args + args
+ if self.keywords:
+ kwargs = dict(self.keywords, **kwargs)
+ if self._sigspec is None:
+ sigspec = self._sigspec = _sigs.signature_or_spec(func)
+ self._has_unknown_args = has_varargs(func, sigspec) is not False
+ else:
+ sigspec = self._sigspec
+
+ if is_partial_args(func, args, kwargs, sigspec) is False:
+ # Nothing can make the call valid
+ return False
+ elif self._has_unknown_args:
+ # The call may be valid and raised a TypeError, but we curry
+ # anyway because the function may have `*args`. This is useful
+ # for decorators with signature `func(*args, **kwargs)`.
+ return True
+ elif not is_valid_args(func, args, kwargs, sigspec):
+ # Adding more arguments may make the call valid
+ return True
+ else:
+ # There was a genuine TypeError
+ return False
+
+ def bind(self, *args, **kwargs):
+ return type(self)(self, *args, **kwargs)
+
+ def call(self, *args, **kwargs):
+ return self._partial(*args, **kwargs)
+
+ def __get__(self, instance, owner):
+ if instance is None:
+ return self
+ return curry(self, instance)
+
+ def __reduce__(self):
+ func = self.func
+ modname = getattr(func, '__module__', None)
+ qualname = getattr(func, '__qualname__', None)
+ if qualname is None: # pragma: py3 no cover
+ qualname = getattr(func, '__name__', None)
+ is_decorated = None
+ if modname and qualname:
+ attrs = []
+ obj = import_module(modname)
+ for attr in qualname.split('.'):
+ if isinstance(obj, curry): # pragma: py2 no cover
+ attrs.append('func')
+ obj = obj.func
+ obj = getattr(obj, attr, None)
+ if obj is None:
+ break
+ attrs.append(attr)
+ if isinstance(obj, curry) and obj.func is func:
+ is_decorated = obj is self
+ qualname = '.'.join(attrs)
+ func = '%s:%s' % (modname, qualname)
+
+ # functools.partial objects can't be pickled
+ userdict = tuple((k, v) for k, v in self.__dict__.items()
+ if k not in ('_partial', '_sigspec'))
+ state = (type(self), func, self.args, self.keywords, userdict,
+ is_decorated)
+ return _restore_curry, state
+
+
+def _restore_curry(cls, func, args, kwargs, userdict, is_decorated):
+ if isinstance(func, str):
+ modname, qualname = func.rsplit(':', 1)
+ obj = import_module(modname)
+ for attr in qualname.split('.'):
+ obj = getattr(obj, attr)
+ if is_decorated:
+ return obj
+ func = obj.func
+ obj = cls(func, *args, **(kwargs or {}))
+ obj.__dict__.update(userdict)
+ return obj
+
+
+@curry
+def memoize(func, cache=None, key=None):
+ """ Cache a function's result for speedy future evaluation
+
+ Considerations:
+ Trades memory for speed.
+ Only use on pure functions.
+
+ >>> def add(x, y): return x + y
+ >>> add = memoize(add)
+
+ Or use as a decorator
+
+ >>> @memoize
+ ... def add(x, y):
+ ... return x + y
+
+ Use the ``cache`` keyword to provide a dict-like object as an initial cache
+
+ >>> @memoize(cache={(1, 2): 3})
+ ... def add(x, y):
+ ... return x + y
+
+ Note that the above works as a decorator because ``memoize`` is curried.
+
+ It is also possible to provide a ``key(args, kwargs)`` function that
+ calculates keys used for the cache, which receives an ``args`` tuple and
+ ``kwargs`` dict as input, and must return a hashable value. However,
+ the default key function should be sufficient most of the time.
+
+ >>> # Use key function that ignores extraneous keyword arguments
+ >>> @memoize(key=lambda args, kwargs: args)
+ ... def add(x, y, verbose=False):
+ ... if verbose:
+ ... print('Calculating %s + %s' % (x, y))
+ ... return x + y
+ """
+ if cache is None:
+ cache = {}
+
+ try:
+ may_have_kwargs = has_keywords(func) is not False
+ # Is unary function (single arg, no variadic argument or keywords)?
+ is_unary = is_arity(1, func)
+ except TypeError: # pragma: no cover
+ may_have_kwargs = True
+ is_unary = False
+
+ if key is None:
+ if is_unary:
+ def key(args, kwargs):
+ return args[0]
+ elif may_have_kwargs:
+ def key(args, kwargs):
+ return (
+ args or None,
+ frozenset(kwargs.items()) if kwargs else None,
+ )
+ else:
+ def key(args, kwargs):
+ return args
+
+ def memof(*args, **kwargs):
+ k = key(args, kwargs)
+ try:
+ return cache[k]
+ except TypeError:
+ raise TypeError("Arguments to memoized function must be hashable")
+ except KeyError:
+ cache[k] = result = func(*args, **kwargs)
+ return result
+
+ try:
+ memof.__name__ = func.__name__
+ except AttributeError:
+ pass
+ memof.__doc__ = func.__doc__
+ memof.__wrapped__ = func
+ return memof
+
+
+class Compose(object):
+ """ A composition of functions
+
+ See Also:
+ compose
+ """
+ __slots__ = 'first', 'funcs'
+
+ def __init__(self, funcs):
+ funcs = tuple(reversed(funcs))
+ self.first = funcs[0]
+ self.funcs = funcs[1:]
+
+ def __call__(self, *args, **kwargs):
+ ret = self.first(*args, **kwargs)
+ for f in self.funcs:
+ ret = f(ret)
+ return ret
+
+ def __getstate__(self):
+ return self.first, self.funcs
+
+ def __setstate__(self, state):
+ self.first, self.funcs = state
+
+ @instanceproperty(classval=__doc__)
+ def __doc__(self):
+ def composed_doc(*fs):
+ """Generate a docstring for the composition of fs.
+ """
+ if not fs:
+ # Argument name for the docstring.
+ return '*args, **kwargs'
+
+ return '{f}({g})'.format(f=fs[0].__name__, g=composed_doc(*fs[1:]))
+
+ try:
+ return (
+ 'lambda *args, **kwargs: ' +
+ composed_doc(*reversed((self.first,) + self.funcs))
+ )
+ except AttributeError:
+ # One of our callables does not have a `__name__`, whatever.
+ return 'A composition of functions'
+
+ @property
+ def __name__(self):
+ try:
+ return '_of_'.join(
+ (f.__name__ for f in reversed((self.first,) + self.funcs))
+ )
+ except AttributeError:
+ return type(self).__name__
+
+ def __repr__(self):
+ return '{.__class__.__name__}{!r}'.format(
+ self, tuple(reversed((self.first, ) + self.funcs)))
+
+ def __eq__(self, other):
+ if isinstance(other, Compose):
+ return other.first == self.first and other.funcs == self.funcs
+ return NotImplemented
+
+ def __ne__(self, other):
+ equality = self.__eq__(other)
+ return NotImplemented if equality is NotImplemented else not equality
+
+ def __hash__(self):
+ return hash(self.first) ^ hash(self.funcs)
+
+ # Mimic the descriptor behavior of python functions.
+ # i.e. let Compose be called as a method when bound to a class.
+ if PY3: # pragma: py2 no cover
+ # adapted from
+ # docs.python.org/3/howto/descriptor.html#functions-and-methods
+ def __get__(self, obj, objtype=None):
+ return self if obj is None else MethodType(self, obj)
+ else: # pragma: py3 no cover
+ # adapted from
+ # docs.python.org/2/howto/descriptor.html#functions-and-methods
+ def __get__(self, obj, objtype=None):
+ return self if obj is None else MethodType(self, obj, objtype)
+
+ # introspection with Signature is only possible from py3.3+
+ if PY3: # pragma: py2 no cover
+ @instanceproperty
+ def __signature__(self):
+ base = inspect.signature(self.first)
+ last = inspect.signature(self.funcs[-1])
+ return base.replace(return_annotation=last.return_annotation)
+
+ __wrapped__ = instanceproperty(attrgetter('first'))
+
+
+def compose(*funcs):
+ """ Compose functions to operate in series.
+
+ Returns a function that applies other functions in sequence.
+
+ Functions are applied from right to left so that
+ ``compose(f, g, h)(x, y)`` is the same as ``f(g(h(x, y)))``.
+
+ If no arguments are provided, the identity function (f(x) = x) is returned.
+
+ >>> inc = lambda i: i + 1
+ >>> compose(str, inc)(3)
+ '4'
+
+ See Also:
+ compose_left
+ pipe
+ """
+ if not funcs:
+ return identity
+ if len(funcs) == 1:
+ return funcs[0]
+ else:
+ return Compose(funcs)
+
+
+def compose_left(*funcs):
+ """ Compose functions to operate in series.
+
+ Returns a function that applies other functions in sequence.
+
+ Functions are applied from left to right so that
+ ``compose_left(f, g, h)(x, y)`` is the same as ``h(g(f(x, y)))``.
+
+ If no arguments are provided, the identity function (f(x) = x) is returned.
+
+ >>> inc = lambda i: i + 1
+ >>> compose_left(inc, str)(3)
+ '4'
+
+ See Also:
+ compose
+ pipe
+ """
+ return compose(*reversed(funcs))
+
+
+def pipe(data, *funcs):
+ """ Pipe a value through a sequence of functions
+
+ I.e. ``pipe(data, f, g, h)`` is equivalent to ``h(g(f(data)))``
+
+ We think of the value as progressing through a pipe of several
+ transformations, much like pipes in UNIX
+
+ ``$ cat data | f | g | h``
+
+ >>> double = lambda i: 2 * i
+ >>> pipe(3, double, str)
+ '6'
+
+ See Also:
+ compose
+ compose_left
+ thread_first
+ thread_last
+ """
+ for func in funcs:
+ data = func(data)
+ return data
+
+
+def complement(func):
+ """ Convert a predicate function to its logical complement.
+
+ In other words, return a function that, for inputs that normally
+ yield True, yields False, and vice-versa.
+
+ >>> def iseven(n): return n % 2 == 0
+ >>> isodd = complement(iseven)
+ >>> iseven(2)
+ True
+ >>> isodd(2)
+ False
+ """
+ return compose(operator.not_, func)
+
+
+class juxt(object):
+ """ Creates a function that calls several functions with the same arguments
+
+ Takes several functions and returns a function that applies its arguments
+ to each of those functions then returns a tuple of the results.
+
+ Name comes from juxtaposition: the fact of two things being seen or placed
+ close together with contrasting effect.
+
+ >>> inc = lambda x: x + 1
+ >>> double = lambda x: x * 2
+ >>> juxt(inc, double)(10)
+ (11, 20)
+ >>> juxt([inc, double])(10)
+ (11, 20)
+ """
+ __slots__ = ['funcs']
+
+ def __init__(self, *funcs):
+ if len(funcs) == 1 and not callable(funcs[0]):
+ funcs = funcs[0]
+ self.funcs = tuple(funcs)
+
+ def __call__(self, *args, **kwargs):
+ return tuple(func(*args, **kwargs) for func in self.funcs)
+
+ def __getstate__(self):
+ return self.funcs
+
+ def __setstate__(self, state):
+ self.funcs = state
+
+
+def do(func, x):
+ """ Runs ``func`` on ``x``, returns ``x``
+
+ Because the results of ``func`` are not returned, only the side
+ effects of ``func`` are relevant.
+
+ Logging functions can be made by composing ``do`` with a storage function
+ like ``list.append`` or ``file.write``
+
+ >>> from toolz import compose
+ >>> from toolz.curried import do
+
+ >>> log = []
+ >>> inc = lambda x: x + 1
+ >>> inc = compose(inc, do(log.append))
+ >>> inc(1)
+ 2
+ >>> inc(11)
+ 12
+ >>> log
+ [1, 11]
+ """
+ func(x)
+ return x
+
+
+@curry
+def flip(func, a, b):
+ """ Call the function call with the arguments flipped
+
+ This function is curried.
+
+ >>> def div(a, b):
+ ... return a // b
+ ...
+ >>> flip(div, 2, 6)
+ 3
+ >>> div_by_two = flip(div, 2)
+ >>> div_by_two(4)
+ 2
+
+ This is particularly useful for built in functions and functions defined
+ in C extensions that accept positional only arguments. For example:
+ isinstance, issubclass.
+
+ >>> data = [1, 'a', 'b', 2, 1.5, object(), 3]
+ >>> only_ints = list(filter(flip(isinstance, int), data))
+ >>> only_ints
+ [1, 2, 3]
+ """
+ return func(b, a)
+
+
+def return_none(exc):
+ """ Returns None.
+ """
+ return None
+
+
+class excepts(object):
+ """A wrapper around a function to catch exceptions and
+ dispatch to a handler.
+
+ This is like a functional try/except block, in the same way that
+ ifexprs are functional if/else blocks.
+
+ Examples
+ --------
+ >>> excepting = excepts(
+ ... ValueError,
+ ... lambda a: [1, 2].index(a),
+ ... lambda _: -1,
+ ... )
+ >>> excepting(1)
+ 0
+ >>> excepting(3)
+ -1
+
+ Multiple exceptions and default except clause.
+ >>> excepting = excepts((IndexError, KeyError), lambda a: a[0])
+ >>> excepting([])
+ >>> excepting([1])
+ 1
+ >>> excepting({})
+ >>> excepting({0: 1})
+ 1
+ """
+ def __init__(self, exc, func, handler=return_none):
+ self.exc = exc
+ self.func = func
+ self.handler = handler
+
+ def __call__(self, *args, **kwargs):
+ try:
+ return self.func(*args, **kwargs)
+ except self.exc as e:
+ return self.handler(e)
+
+ @instanceproperty(classval=__doc__)
+ def __doc__(self):
+ exc = self.exc
+ try:
+ if isinstance(exc, tuple):
+ exc_name = '(%s)' % ', '.join(
+ map(attrgetter('__name__'), exc),
+ )
+ else:
+ exc_name = exc.__name__
+
+ return dedent(
+ """\
+ A wrapper around {inst.func.__name__!r} that will except:
+ {exc}
+ and handle any exceptions with {inst.handler.__name__!r}.
+
+ Docs for {inst.func.__name__!r}:
+ {inst.func.__doc__}
+
+ Docs for {inst.handler.__name__!r}:
+ {inst.handler.__doc__}
+ """
+ ).format(
+ inst=self,
+ exc=exc_name,
+ )
+ except AttributeError:
+ return type(self).__doc__
+
+ @property
+ def __name__(self):
+ exc = self.exc
+ try:
+ if isinstance(exc, tuple):
+ exc_name = '_or_'.join(map(attrgetter('__name__'), exc))
+ else:
+ exc_name = exc.__name__
+ return '%s_excepting_%s' % (self.func.__name__, exc_name)
+ except AttributeError:
+ return 'excepting'
+
+
+if PY3: # pragma: py2 no cover
+ def _check_sigspec(sigspec, func, builtin_func, *builtin_args):
+ if sigspec is None:
+ try:
+ sigspec = inspect.signature(func)
+ except (ValueError, TypeError) as e:
+ sigspec = e
+ if isinstance(sigspec, ValueError):
+ return None, builtin_func(*builtin_args)
+ elif not isinstance(sigspec, inspect.Signature):
+ if (
+ func in _sigs.signatures
+ and ((
+ hasattr(func, '__signature__')
+ and hasattr(func.__signature__, '__get__')
+ ))
+ ): # pragma: no cover (not covered in Python 3.4)
+ val = builtin_func(*builtin_args)
+ return None, val
+ return None, False
+ return sigspec, None
+
+else: # pragma: py3 no cover
+ def _check_sigspec(sigspec, func, builtin_func, *builtin_args):
+ if sigspec is None:
+ try:
+ sigspec = inspect.getargspec(func)
+ except TypeError as e:
+ sigspec = e
+ if isinstance(sigspec, TypeError):
+ if not callable(func):
+ return None, False
+ return None, builtin_func(*builtin_args)
+ return sigspec, None
+
+
+if PY34 or PYPY: # pragma: no cover
+ _check_sigspec_orig = _check_sigspec
+
+ def _check_sigspec(sigspec, func, builtin_func, *builtin_args):
+ # Python 3.4 and PyPy may lie, so use our registry for builtins instead
+ if func in _sigs.signatures:
+ val = builtin_func(*builtin_args)
+ return None, val
+ return _check_sigspec_orig(sigspec, func, builtin_func, *builtin_args)
+
+_check_sigspec.__doc__ = """ \
+Private function to aid in introspection compatibly across Python versions.
+
+If a callable doesn't have a signature (Python 3) or an argspec (Python 2),
+the signature registry in toolz._signatures is used.
+"""
+
+if PY3: # pragma: py2 no cover
+ def num_required_args(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args,
+ func)
+ if sigspec is None:
+ return rv
+ return sum(1 for p in sigspec.parameters.values()
+ if p.default is p.empty
+ and p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY))
+
+ def has_varargs(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func)
+ if sigspec is None:
+ return rv
+ return any(p.kind == p.VAR_POSITIONAL
+ for p in sigspec.parameters.values())
+
+ def has_keywords(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func)
+ if sigspec is None:
+ return rv
+ return any(p.default is not p.empty
+ or p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD)
+ for p in sigspec.parameters.values())
+
+ def is_valid_args(func, args, kwargs, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args,
+ func, args, kwargs)
+ if sigspec is None:
+ return rv
+ try:
+ sigspec.bind(*args, **kwargs)
+ except TypeError:
+ return False
+ return True
+
+ def is_partial_args(func, args, kwargs, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args,
+ func, args, kwargs)
+ if sigspec is None:
+ return rv
+ try:
+ sigspec.bind_partial(*args, **kwargs)
+ except TypeError:
+ return False
+ return True
+
+else: # pragma: py3 no cover
+ def num_required_args(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args,
+ func)
+ if sigspec is None:
+ return rv
+ num_defaults = len(sigspec.defaults) if sigspec.defaults else 0
+ return len(sigspec.args) - num_defaults
+
+ def has_varargs(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func)
+ if sigspec is None:
+ return rv
+ return sigspec.varargs is not None
+
+ def has_keywords(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func)
+ if sigspec is None:
+ return rv
+ return sigspec.defaults is not None or sigspec.keywords is not None
+
+ def is_valid_args(func, args, kwargs, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args,
+ func, args, kwargs)
+ if sigspec is None:
+ return rv
+ spec = sigspec
+ defaults = spec.defaults or ()
+ num_pos = len(spec.args) - len(defaults)
+ missing_pos = spec.args[len(args):num_pos]
+ if any(arg not in kwargs for arg in missing_pos):
+ return False
+
+ if spec.varargs is None:
+ num_extra_pos = max(0, len(args) - num_pos)
+ else:
+ num_extra_pos = 0
+
+ kwargs = dict(kwargs)
+
+ # Add missing keyword arguments (unless already included in `args`)
+ missing_kwargs = spec.args[num_pos + num_extra_pos:]
+ kwargs.update(zip(missing_kwargs, defaults[num_extra_pos:]))
+
+ # Convert call to use positional arguments
+ args = args + tuple(kwargs.pop(key) for key in spec.args[len(args):])
+
+ if (
+ not spec.keywords and kwargs
+ or not spec.varargs and len(args) > len(spec.args)
+ or set(spec.args[:len(args)]) & set(kwargs)
+ ):
+ return False
+ else:
+ return True
+
+ def is_partial_args(func, args, kwargs, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args,
+ func, args, kwargs)
+ if sigspec is None:
+ return rv
+ spec = sigspec
+ defaults = spec.defaults or ()
+ num_pos = len(spec.args) - len(defaults)
+ if spec.varargs is None:
+ num_extra_pos = max(0, len(args) - num_pos)
+ else:
+ num_extra_pos = 0
+
+ kwargs = dict(kwargs)
+
+ # Add missing keyword arguments (unless already included in `args`)
+ missing_kwargs = spec.args[num_pos + num_extra_pos:]
+ kwargs.update(zip(missing_kwargs, defaults[num_extra_pos:]))
+
+ # Add missing position arguments as keywords (may already be in kwargs)
+ missing_args = spec.args[len(args):num_pos + num_extra_pos]
+ kwargs.update((x, None) for x in missing_args)
+
+ # Convert call to use positional arguments
+ args = args + tuple(kwargs.pop(key) for key in spec.args[len(args):])
+
+ if (
+ not spec.keywords and kwargs
+ or not spec.varargs and len(args) > len(spec.args)
+ or set(spec.args[:len(args)]) & set(kwargs)
+ ):
+ return False
+ else:
+ return True
+
+
+def is_arity(n, func, sigspec=None):
+ """ Does a function have only n positional arguments?
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def f(x):
+ ... return x
+ >>> is_arity(1, f)
+ True
+ >>> def g(x, y=1):
+ ... return x + y
+ >>> is_arity(1, g)
+ False
+ """
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_arity, n, func)
+ if sigspec is None:
+ return rv
+ num = num_required_args(func, sigspec)
+ if num is not None:
+ num = num == n
+ if not num:
+ return False
+ varargs = has_varargs(func, sigspec)
+ if varargs:
+ return False
+ keywords = has_keywords(func, sigspec)
+ if keywords:
+ return False
+ if num is None or varargs is None or keywords is None: # pragma: no cover
+ return None
+ return True
+
+
+num_required_args.__doc__ = """ \
+Number of required positional arguments
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def f(x, y, z=3):
+ ... return x + y + z
+ >>> num_required_args(f)
+ 2
+ >>> def g(*args, **kwargs):
+ ... pass
+ >>> num_required_args(g)
+ 0
+ """
+
+has_varargs.__doc__ = """ \
+Does a function have variadic positional arguments?
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def f(*args):
+ ... return args
+ >>> has_varargs(f)
+ True
+ >>> def g(**kwargs):
+ ... return kwargs
+ >>> has_varargs(g)
+ False
+ """
+
+has_keywords.__doc__ = """ \
+Does a function have keyword arguments?
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def f(x, y=0):
+ ... return x + y
+
+ >>> has_keywords(f)
+ True
+ """
+
+is_valid_args.__doc__ = """ \
+Is ``func(*args, **kwargs)`` a valid function call?
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def add(x, y):
+ ... return x + y
+
+ >>> is_valid_args(add, (1,), {})
+ False
+ >>> is_valid_args(add, (1, 2), {})
+ True
+ >>> is_valid_args(map, (), {})
+ False
+
+ **Implementation notes**
+ Python 2 relies on ``inspect.getargspec``, which only works for
+ user-defined functions. Python 3 uses ``inspect.signature``, which
+ works for many more types of callables.
+
+ Many builtins in the standard library are also supported.
+ """
+
+is_partial_args.__doc__ = """ \
+Can partial(func, *args, **kwargs)(*args2, **kwargs2) be a valid call?
+
+ Returns True *only* if the call is valid or if it is possible for the
+ call to become valid by adding more positional or keyword arguments.
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def add(x, y):
+ ... return x + y
+
+ >>> is_partial_args(add, (1,), {})
+ True
+ >>> is_partial_args(add, (1, 2), {})
+ True
+ >>> is_partial_args(add, (1, 2, 3), {})
+ False
+ >>> is_partial_args(map, (), {})
+ True
+
+ **Implementation notes**
+ Python 2 relies on ``inspect.getargspec``, which only works for
+ user-defined functions. Python 3 uses ``inspect.signature``, which
+ works for many more types of callables.
+
+ Many builtins in the standard library are also supported.
+ """
+
+from . import _signatures as _sigs
diff --git a/contrib/python/toolz/py2/toolz/itertoolz.py b/contrib/python/toolz/py2/toolz/itertoolz.py
new file mode 100644
index 0000000000..e71f1eeef0
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/itertoolz.py
@@ -0,0 +1,1056 @@
+import itertools
+import heapq
+import collections
+import operator
+from functools import partial
+from random import Random
+from toolz.compatibility import (map, filterfalse, zip, zip_longest, iteritems,
+ filter, Sequence)
+from toolz.utils import no_default
+
+
+__all__ = ('remove', 'accumulate', 'groupby', 'merge_sorted', 'interleave',
+ 'unique', 'isiterable', 'isdistinct', 'take', 'drop', 'take_nth',
+ 'first', 'second', 'nth', 'last', 'get', 'concat', 'concatv',
+ 'mapcat', 'cons', 'interpose', 'frequencies', 'reduceby', 'iterate',
+ 'sliding_window', 'partition', 'partition_all', 'count', 'pluck',
+ 'join', 'tail', 'diff', 'topk', 'peek', 'peekn', 'random_sample')
+
+
+def remove(predicate, seq):
+ """ Return those items of sequence for which predicate(item) is False
+
+ >>> def iseven(x):
+ ... return x % 2 == 0
+ >>> list(remove(iseven, [1, 2, 3, 4]))
+ [1, 3]
+ """
+ return filterfalse(predicate, seq)
+
+
+def accumulate(binop, seq, initial=no_default):
+ """ Repeatedly apply binary function to a sequence, accumulating results
+
+ >>> from operator import add, mul
+ >>> list(accumulate(add, [1, 2, 3, 4, 5]))
+ [1, 3, 6, 10, 15]
+ >>> list(accumulate(mul, [1, 2, 3, 4, 5]))
+ [1, 2, 6, 24, 120]
+
+ Accumulate is similar to ``reduce`` and is good for making functions like
+ cumulative sum:
+
+ >>> from functools import partial, reduce
+ >>> sum = partial(reduce, add)
+ >>> cumsum = partial(accumulate, add)
+
+ Accumulate also takes an optional argument that will be used as the first
+ value. This is similar to reduce.
+
+ >>> list(accumulate(add, [1, 2, 3], -1))
+ [-1, 0, 2, 5]
+ >>> list(accumulate(add, [], 1))
+ [1]
+
+ See Also:
+ itertools.accumulate : In standard itertools for Python 3.2+
+ """
+ seq = iter(seq)
+ if initial == no_default:
+ try:
+ result = next(seq)
+ except StopIteration:
+ return
+ else:
+ result = initial
+ yield result
+ for elem in seq:
+ result = binop(result, elem)
+ yield result
+
+
+def groupby(key, seq):
+ """ Group a collection by a key function
+
+ >>> names = ['Alice', 'Bob', 'Charlie', 'Dan', 'Edith', 'Frank']
+ >>> groupby(len, names) # doctest: +SKIP
+ {3: ['Bob', 'Dan'], 5: ['Alice', 'Edith', 'Frank'], 7: ['Charlie']}
+
+ >>> iseven = lambda x: x % 2 == 0
+ >>> groupby(iseven, [1, 2, 3, 4, 5, 6, 7, 8]) # doctest: +SKIP
+ {False: [1, 3, 5, 7], True: [2, 4, 6, 8]}
+
+ Non-callable keys imply grouping on a member.
+
+ >>> groupby('gender', [{'name': 'Alice', 'gender': 'F'},
+ ... {'name': 'Bob', 'gender': 'M'},
+ ... {'name': 'Charlie', 'gender': 'M'}]) # doctest:+SKIP
+ {'F': [{'gender': 'F', 'name': 'Alice'}],
+ 'M': [{'gender': 'M', 'name': 'Bob'},
+ {'gender': 'M', 'name': 'Charlie'}]}
+
+ Not to be confused with ``itertools.groupby``
+
+ See Also:
+ countby
+ """
+ if not callable(key):
+ key = getter(key)
+ d = collections.defaultdict(lambda: [].append)
+ for item in seq:
+ d[key(item)](item)
+ rv = {}
+ for k, v in iteritems(d):
+ rv[k] = v.__self__
+ return rv
+
+
+def merge_sorted(*seqs, **kwargs):
+ """ Merge and sort a collection of sorted collections
+
+ This works lazily and only keeps one value from each iterable in memory.
+
+ >>> list(merge_sorted([1, 3, 5], [2, 4, 6]))
+ [1, 2, 3, 4, 5, 6]
+
+ >>> ''.join(merge_sorted('abc', 'abc', 'abc'))
+ 'aaabbbccc'
+
+ The "key" function used to sort the input may be passed as a keyword.
+
+ >>> list(merge_sorted([2, 3], [1, 3], key=lambda x: x // 3))
+ [2, 1, 3, 3]
+ """
+ if len(seqs) == 0:
+ return iter([])
+ elif len(seqs) == 1:
+ return iter(seqs[0])
+
+ key = kwargs.get('key', None)
+ if key is None:
+ return _merge_sorted_binary(seqs)
+ else:
+ return _merge_sorted_binary_key(seqs, key)
+
+
+def _merge_sorted_binary(seqs):
+ mid = len(seqs) // 2
+ L1 = seqs[:mid]
+ if len(L1) == 1:
+ seq1 = iter(L1[0])
+ else:
+ seq1 = _merge_sorted_binary(L1)
+ L2 = seqs[mid:]
+ if len(L2) == 1:
+ seq2 = iter(L2[0])
+ else:
+ seq2 = _merge_sorted_binary(L2)
+
+ try:
+ val2 = next(seq2)
+ except StopIteration:
+ for val1 in seq1:
+ yield val1
+ return
+
+ for val1 in seq1:
+ if val2 < val1:
+ yield val2
+ for val2 in seq2:
+ if val2 < val1:
+ yield val2
+ else:
+ yield val1
+ break
+ else:
+ break
+ else:
+ yield val1
+ else:
+ yield val2
+ for val2 in seq2:
+ yield val2
+ return
+ yield val1
+ for val1 in seq1:
+ yield val1
+
+
+def _merge_sorted_binary_key(seqs, key):
+ mid = len(seqs) // 2
+ L1 = seqs[:mid]
+ if len(L1) == 1:
+ seq1 = iter(L1[0])
+ else:
+ seq1 = _merge_sorted_binary_key(L1, key)
+ L2 = seqs[mid:]
+ if len(L2) == 1:
+ seq2 = iter(L2[0])
+ else:
+ seq2 = _merge_sorted_binary_key(L2, key)
+
+ try:
+ val2 = next(seq2)
+ except StopIteration:
+ for val1 in seq1:
+ yield val1
+ return
+ key2 = key(val2)
+
+ for val1 in seq1:
+ key1 = key(val1)
+ if key2 < key1:
+ yield val2
+ for val2 in seq2:
+ key2 = key(val2)
+ if key2 < key1:
+ yield val2
+ else:
+ yield val1
+ break
+ else:
+ break
+ else:
+ yield val1
+ else:
+ yield val2
+ for val2 in seq2:
+ yield val2
+ return
+ yield val1
+ for val1 in seq1:
+ yield val1
+
+
+def interleave(seqs):
+ """ Interleave a sequence of sequences
+
+ >>> list(interleave([[1, 2], [3, 4]]))
+ [1, 3, 2, 4]
+
+ >>> ''.join(interleave(('ABC', 'XY')))
+ 'AXBYC'
+
+ Both the individual sequences and the sequence of sequences may be infinite
+
+ Returns a lazy iterator
+ """
+ iters = itertools.cycle(map(iter, seqs))
+ while True:
+ try:
+ for itr in iters:
+ yield next(itr)
+ return
+ except StopIteration:
+ predicate = partial(operator.is_not, itr)
+ iters = itertools.cycle(itertools.takewhile(predicate, iters))
+
+
+def unique(seq, key=None):
+ """ Return only unique elements of a sequence
+
+ >>> tuple(unique((1, 2, 3)))
+ (1, 2, 3)
+ >>> tuple(unique((1, 2, 1, 3)))
+ (1, 2, 3)
+
+ Uniqueness can be defined by key keyword
+
+ >>> tuple(unique(['cat', 'mouse', 'dog', 'hen'], key=len))
+ ('cat', 'mouse')
+ """
+ seen = set()
+ seen_add = seen.add
+ if key is None:
+ for item in seq:
+ if item not in seen:
+ seen_add(item)
+ yield item
+ else: # calculate key
+ for item in seq:
+ val = key(item)
+ if val not in seen:
+ seen_add(val)
+ yield item
+
+
+def isiterable(x):
+ """ Is x iterable?
+
+ >>> isiterable([1, 2, 3])
+ True
+ >>> isiterable('abc')
+ True
+ >>> isiterable(5)
+ False
+ """
+ try:
+ iter(x)
+ return True
+ except TypeError:
+ return False
+
+
+def isdistinct(seq):
+ """ All values in sequence are distinct
+
+ >>> isdistinct([1, 2, 3])
+ True
+ >>> isdistinct([1, 2, 1])
+ False
+
+ >>> isdistinct("Hello")
+ False
+ >>> isdistinct("World")
+ True
+ """
+ if iter(seq) is seq:
+ seen = set()
+ seen_add = seen.add
+ for item in seq:
+ if item in seen:
+ return False
+ seen_add(item)
+ return True
+ else:
+ return len(seq) == len(set(seq))
+
+
+def take(n, seq):
+ """ The first n elements of a sequence
+
+ >>> list(take(2, [10, 20, 30, 40, 50]))
+ [10, 20]
+
+ See Also:
+ drop
+ tail
+ """
+ return itertools.islice(seq, n)
+
+
+def tail(n, seq):
+ """ The last n elements of a sequence
+
+ >>> tail(2, [10, 20, 30, 40, 50])
+ [40, 50]
+
+ See Also:
+ drop
+ take
+ """
+ try:
+ return seq[-n:]
+ except (TypeError, KeyError):
+ return tuple(collections.deque(seq, n))
+
+
+def drop(n, seq):
+ """ The sequence following the first n elements
+
+ >>> list(drop(2, [10, 20, 30, 40, 50]))
+ [30, 40, 50]
+
+ See Also:
+ take
+ tail
+ """
+ return itertools.islice(seq, n, None)
+
+
+def take_nth(n, seq):
+ """ Every nth item in seq
+
+ >>> list(take_nth(2, [10, 20, 30, 40, 50]))
+ [10, 30, 50]
+ """
+ return itertools.islice(seq, 0, None, n)
+
+
+def first(seq):
+ """ The first element in a sequence
+
+ >>> first('ABC')
+ 'A'
+ """
+ return next(iter(seq))
+
+
+def second(seq):
+ """ The second element in a sequence
+
+ >>> second('ABC')
+ 'B'
+ """
+ seq = iter(seq)
+ next(seq)
+ return next(seq)
+
+
+def nth(n, seq):
+ """ The nth element in a sequence
+
+ >>> nth(1, 'ABC')
+ 'B'
+ """
+ if isinstance(seq, (tuple, list, Sequence)):
+ return seq[n]
+ else:
+ return next(itertools.islice(seq, n, None))
+
+
+def last(seq):
+ """ The last element in a sequence
+
+ >>> last('ABC')
+ 'C'
+ """
+ return tail(1, seq)[0]
+
+
+rest = partial(drop, 1)
+
+
+def _get(ind, seq, default):
+ try:
+ return seq[ind]
+ except (KeyError, IndexError):
+ return default
+
+
+def get(ind, seq, default=no_default):
+ """ Get element in a sequence or dict
+
+ Provides standard indexing
+
+ >>> get(1, 'ABC') # Same as 'ABC'[1]
+ 'B'
+
+ Pass a list to get multiple values
+
+ >>> get([1, 2], 'ABC') # ('ABC'[1], 'ABC'[2])
+ ('B', 'C')
+
+ Works on any value that supports indexing/getitem
+ For example here we see that it works with dictionaries
+
+ >>> phonebook = {'Alice': '555-1234',
+ ... 'Bob': '555-5678',
+ ... 'Charlie':'555-9999'}
+ >>> get('Alice', phonebook)
+ '555-1234'
+
+ >>> get(['Alice', 'Bob'], phonebook)
+ ('555-1234', '555-5678')
+
+ Provide a default for missing values
+
+ >>> get(['Alice', 'Dennis'], phonebook, None)
+ ('555-1234', None)
+
+ See Also:
+ pluck
+ """
+ try:
+ return seq[ind]
+ except TypeError: # `ind` may be a list
+ if isinstance(ind, list):
+ if default == no_default:
+ if len(ind) > 1:
+ return operator.itemgetter(*ind)(seq)
+ elif ind:
+ return seq[ind[0]],
+ else:
+ return ()
+ else:
+ return tuple(_get(i, seq, default) for i in ind)
+ elif default != no_default:
+ return default
+ else:
+ raise
+ except (KeyError, IndexError): # we know `ind` is not a list
+ if default == no_default:
+ raise
+ else:
+ return default
+
+
+def concat(seqs):
+ """ Concatenate zero or more iterables, any of which may be infinite.
+
+ An infinite sequence will prevent the rest of the arguments from
+ being included.
+
+ We use chain.from_iterable rather than ``chain(*seqs)`` so that seqs
+ can be a generator.
+
+ >>> list(concat([[], [1], [2, 3]]))
+ [1, 2, 3]
+
+ See also:
+ itertools.chain.from_iterable equivalent
+ """
+ return itertools.chain.from_iterable(seqs)
+
+
+def concatv(*seqs):
+ """ Variadic version of concat
+
+ >>> list(concatv([], ["a"], ["b", "c"]))
+ ['a', 'b', 'c']
+
+ See also:
+ itertools.chain
+ """
+ return concat(seqs)
+
+
+def mapcat(func, seqs):
+ """ Apply func to each sequence in seqs, concatenating results.
+
+ >>> list(mapcat(lambda s: [c.upper() for c in s],
+ ... [["a", "b"], ["c", "d", "e"]]))
+ ['A', 'B', 'C', 'D', 'E']
+ """
+ return concat(map(func, seqs))
+
+
+def cons(el, seq):
+ """ Add el to beginning of (possibly infinite) sequence seq.
+
+ >>> list(cons(1, [2, 3]))
+ [1, 2, 3]
+ """
+ return itertools.chain([el], seq)
+
+
+def interpose(el, seq):
+ """ Introduce element between each pair of elements in seq
+
+ >>> list(interpose("a", [1, 2, 3]))
+ [1, 'a', 2, 'a', 3]
+ """
+ inposed = concat(zip(itertools.repeat(el), seq))
+ next(inposed)
+ return inposed
+
+
+def frequencies(seq):
+ """ Find number of occurrences of each value in seq
+
+ >>> frequencies(['cat', 'cat', 'ox', 'pig', 'pig', 'cat']) #doctest: +SKIP
+ {'cat': 3, 'ox': 1, 'pig': 2}
+
+ See Also:
+ countby
+ groupby
+ """
+ d = collections.defaultdict(int)
+ for item in seq:
+ d[item] += 1
+ return dict(d)
+
+
+def reduceby(key, binop, seq, init=no_default):
+ """ Perform a simultaneous groupby and reduction
+
+ The computation:
+
+ >>> result = reduceby(key, binop, seq, init) # doctest: +SKIP
+
+ is equivalent to the following:
+
+ >>> def reduction(group): # doctest: +SKIP
+ ... return reduce(binop, group, init) # doctest: +SKIP
+
+ >>> groups = groupby(key, seq) # doctest: +SKIP
+ >>> result = valmap(reduction, groups) # doctest: +SKIP
+
+ But the former does not build the intermediate groups, allowing it to
+ operate in much less space. This makes it suitable for larger datasets
+ that do not fit comfortably in memory
+
+ The ``init`` keyword argument is the default initialization of the
+ reduction. This can be either a constant value like ``0`` or a callable
+ like ``lambda : 0`` as might be used in ``defaultdict``.
+
+ Simple Examples
+ ---------------
+
+ >>> from operator import add, mul
+ >>> iseven = lambda x: x % 2 == 0
+
+ >>> data = [1, 2, 3, 4, 5]
+
+ >>> reduceby(iseven, add, data) # doctest: +SKIP
+ {False: 9, True: 6}
+
+ >>> reduceby(iseven, mul, data) # doctest: +SKIP
+ {False: 15, True: 8}
+
+ Complex Example
+ ---------------
+
+ >>> projects = [{'name': 'build roads', 'state': 'CA', 'cost': 1000000},
+ ... {'name': 'fight crime', 'state': 'IL', 'cost': 100000},
+ ... {'name': 'help farmers', 'state': 'IL', 'cost': 2000000},
+ ... {'name': 'help farmers', 'state': 'CA', 'cost': 200000}]
+
+ >>> reduceby('state', # doctest: +SKIP
+ ... lambda acc, x: acc + x['cost'],
+ ... projects, 0)
+ {'CA': 1200000, 'IL': 2100000}
+
+ Example Using ``init``
+ ----------------------
+
+ >>> def set_add(s, i):
+ ... s.add(i)
+ ... return s
+
+ >>> reduceby(iseven, set_add, [1, 2, 3, 4, 1, 2, 3], set) # doctest: +SKIP
+ {True: set([2, 4]),
+ False: set([1, 3])}
+ """
+ is_no_default = init == no_default
+ if not is_no_default and not callable(init):
+ _init = init
+ init = lambda: _init
+ if not callable(key):
+ key = getter(key)
+ d = {}
+ for item in seq:
+ k = key(item)
+ if k not in d:
+ if is_no_default:
+ d[k] = item
+ continue
+ else:
+ d[k] = init()
+ d[k] = binop(d[k], item)
+ return d
+
+
+def iterate(func, x):
+ """ Repeatedly apply a function func onto an original input
+
+ Yields x, then func(x), then func(func(x)), then func(func(func(x))), etc..
+
+ >>> def inc(x): return x + 1
+ >>> counter = iterate(inc, 0)
+ >>> next(counter)
+ 0
+ >>> next(counter)
+ 1
+ >>> next(counter)
+ 2
+
+ >>> double = lambda x: x * 2
+ >>> powers_of_two = iterate(double, 1)
+ >>> next(powers_of_two)
+ 1
+ >>> next(powers_of_two)
+ 2
+ >>> next(powers_of_two)
+ 4
+ >>> next(powers_of_two)
+ 8
+ """
+ while True:
+ yield x
+ x = func(x)
+
+
+def sliding_window(n, seq):
+ """ A sequence of overlapping subsequences
+
+ >>> list(sliding_window(2, [1, 2, 3, 4]))
+ [(1, 2), (2, 3), (3, 4)]
+
+ This function creates a sliding window suitable for transformations like
+ sliding means / smoothing
+
+ >>> mean = lambda seq: float(sum(seq)) / len(seq)
+ >>> list(map(mean, sliding_window(2, [1, 2, 3, 4])))
+ [1.5, 2.5, 3.5]
+ """
+ return zip(*(collections.deque(itertools.islice(it, i), 0) or it
+ for i, it in enumerate(itertools.tee(seq, n))))
+
+
+no_pad = '__no__pad__'
+
+
+def partition(n, seq, pad=no_pad):
+ """ Partition sequence into tuples of length n
+
+ >>> list(partition(2, [1, 2, 3, 4]))
+ [(1, 2), (3, 4)]
+
+ If the length of ``seq`` is not evenly divisible by ``n``, the final tuple
+ is dropped if ``pad`` is not specified, or filled to length ``n`` by pad:
+
+ >>> list(partition(2, [1, 2, 3, 4, 5]))
+ [(1, 2), (3, 4)]
+
+ >>> list(partition(2, [1, 2, 3, 4, 5], pad=None))
+ [(1, 2), (3, 4), (5, None)]
+
+ See Also:
+ partition_all
+ """
+ args = [iter(seq)] * n
+ if pad is no_pad:
+ return zip(*args)
+ else:
+ return zip_longest(*args, fillvalue=pad)
+
+
+def partition_all(n, seq):
+ """ Partition all elements of sequence into tuples of length at most n
+
+ The final tuple may be shorter to accommodate extra elements.
+
+ >>> list(partition_all(2, [1, 2, 3, 4]))
+ [(1, 2), (3, 4)]
+
+ >>> list(partition_all(2, [1, 2, 3, 4, 5]))
+ [(1, 2), (3, 4), (5,)]
+
+ See Also:
+ partition
+ """
+ args = [iter(seq)] * n
+ it = zip_longest(*args, fillvalue=no_pad)
+ try:
+ prev = next(it)
+ except StopIteration:
+ return
+ for item in it:
+ yield prev
+ prev = item
+ if prev[-1] is no_pad:
+ try:
+ # If seq defines __len__, then
+ # we can quickly calculate where no_pad starts
+ yield prev[:len(seq) % n]
+ except TypeError:
+ # Get first index of no_pad without using .index()
+ # https://github.com/pytoolz/toolz/issues/387
+ # Binary search from CPython's bisect module,
+ # modified for identity testing.
+ lo, hi = 0, n
+ while lo < hi:
+ mid = (lo + hi) // 2
+ if prev[mid] is no_pad:
+ hi = mid
+ else:
+ lo = mid + 1
+ yield prev[:lo]
+ else:
+ yield prev
+
+
+def count(seq):
+ """ Count the number of items in seq
+
+ Like the builtin ``len`` but works on lazy sequencies.
+
+ Not to be confused with ``itertools.count``
+
+ See also:
+ len
+ """
+ if hasattr(seq, '__len__'):
+ return len(seq)
+ return sum(1 for i in seq)
+
+
+def pluck(ind, seqs, default=no_default):
+ """ plucks an element or several elements from each item in a sequence.
+
+ ``pluck`` maps ``itertoolz.get`` over a sequence and returns one or more
+ elements of each item in the sequence.
+
+ This is equivalent to running `map(curried.get(ind), seqs)`
+
+ ``ind`` can be either a single string/index or a list of strings/indices.
+ ``seqs`` should be sequence containing sequences or dicts.
+
+ e.g.
+
+ >>> data = [{'id': 1, 'name': 'Cheese'}, {'id': 2, 'name': 'Pies'}]
+ >>> list(pluck('name', data))
+ ['Cheese', 'Pies']
+ >>> list(pluck([0, 1], [[1, 2, 3], [4, 5, 7]]))
+ [(1, 2), (4, 5)]
+
+ See Also:
+ get
+ map
+ """
+ if default == no_default:
+ get = getter(ind)
+ return map(get, seqs)
+ elif isinstance(ind, list):
+ return (tuple(_get(item, seq, default) for item in ind)
+ for seq in seqs)
+ return (_get(ind, seq, default) for seq in seqs)
+
+
+def getter(index):
+ if isinstance(index, list):
+ if len(index) == 1:
+ index = index[0]
+ return lambda x: (x[index],)
+ elif index:
+ return operator.itemgetter(*index)
+ else:
+ return lambda x: ()
+ else:
+ return operator.itemgetter(index)
+
+
+def join(leftkey, leftseq, rightkey, rightseq,
+ left_default=no_default, right_default=no_default):
+ """ Join two sequences on common attributes
+
+ This is a semi-streaming operation. The LEFT sequence is fully evaluated
+ and placed into memory. The RIGHT sequence is evaluated lazily and so can
+ be arbitrarily large.
+ (Note: If right_default is defined, then unique keys of rightseq
+ will also be stored in memory.)
+
+ >>> friends = [('Alice', 'Edith'),
+ ... ('Alice', 'Zhao'),
+ ... ('Edith', 'Alice'),
+ ... ('Zhao', 'Alice'),
+ ... ('Zhao', 'Edith')]
+
+ >>> cities = [('Alice', 'NYC'),
+ ... ('Alice', 'Chicago'),
+ ... ('Dan', 'Syndey'),
+ ... ('Edith', 'Paris'),
+ ... ('Edith', 'Berlin'),
+ ... ('Zhao', 'Shanghai')]
+
+ >>> # Vacation opportunities
+ >>> # In what cities do people have friends?
+ >>> result = join(second, friends,
+ ... first, cities)
+ >>> for ((a, b), (c, d)) in sorted(unique(result)):
+ ... print((a, d))
+ ('Alice', 'Berlin')
+ ('Alice', 'Paris')
+ ('Alice', 'Shanghai')
+ ('Edith', 'Chicago')
+ ('Edith', 'NYC')
+ ('Zhao', 'Chicago')
+ ('Zhao', 'NYC')
+ ('Zhao', 'Berlin')
+ ('Zhao', 'Paris')
+
+ Specify outer joins with keyword arguments ``left_default`` and/or
+ ``right_default``. Here is a full outer join in which unmatched elements
+ are paired with None.
+
+ >>> identity = lambda x: x
+ >>> list(join(identity, [1, 2, 3],
+ ... identity, [2, 3, 4],
+ ... left_default=None, right_default=None))
+ [(2, 2), (3, 3), (None, 4), (1, None)]
+
+ Usually the key arguments are callables to be applied to the sequences. If
+ the keys are not obviously callable then it is assumed that indexing was
+ intended, e.g. the following is a legal change.
+ The join is implemented as a hash join and the keys of leftseq must be
+ hashable. Additionally, if right_default is defined, then keys of rightseq
+ must also be hashable.
+
+ >>> # result = join(second, friends, first, cities)
+ >>> result = join(1, friends, 0, cities) # doctest: +SKIP
+ """
+ if not callable(leftkey):
+ leftkey = getter(leftkey)
+ if not callable(rightkey):
+ rightkey = getter(rightkey)
+
+ d = groupby(leftkey, leftseq)
+
+ if left_default == no_default and right_default == no_default:
+ # Inner Join
+ for item in rightseq:
+ key = rightkey(item)
+ if key in d:
+ for left_match in d[key]:
+ yield (left_match, item)
+ elif left_default != no_default and right_default == no_default:
+ # Right Join
+ for item in rightseq:
+ key = rightkey(item)
+ if key in d:
+ for left_match in d[key]:
+ yield (left_match, item)
+ else:
+ yield (left_default, item)
+ elif right_default != no_default:
+ seen_keys = set()
+ seen = seen_keys.add
+
+ if left_default == no_default:
+ # Left Join
+ for item in rightseq:
+ key = rightkey(item)
+ seen(key)
+ if key in d:
+ for left_match in d[key]:
+ yield (left_match, item)
+ else:
+ # Full Join
+ for item in rightseq:
+ key = rightkey(item)
+ seen(key)
+ if key in d:
+ for left_match in d[key]:
+ yield (left_match, item)
+ else:
+ yield (left_default, item)
+
+ for key, matches in iteritems(d):
+ if key not in seen_keys:
+ for match in matches:
+ yield (match, right_default)
+
+
+def diff(*seqs, **kwargs):
+ """ Return those items that differ between sequences
+
+ >>> list(diff([1, 2, 3], [1, 2, 10, 100]))
+ [(3, 10)]
+
+ Shorter sequences may be padded with a ``default`` value:
+
+ >>> list(diff([1, 2, 3], [1, 2, 10, 100], default=None))
+ [(3, 10), (None, 100)]
+
+ A ``key`` function may also be applied to each item to use during
+ comparisons:
+
+ >>> list(diff(['apples', 'bananas'], ['Apples', 'Oranges'], key=str.lower))
+ [('bananas', 'Oranges')]
+ """
+ N = len(seqs)
+ if N == 1 and isinstance(seqs[0], list):
+ seqs = seqs[0]
+ N = len(seqs)
+ if N < 2:
+ raise TypeError('Too few sequences given (min 2 required)')
+ default = kwargs.get('default', no_default)
+ if default == no_default:
+ iters = zip(*seqs)
+ else:
+ iters = zip_longest(*seqs, fillvalue=default)
+ key = kwargs.get('key', None)
+ if key is None:
+ for items in iters:
+ if items.count(items[0]) != N:
+ yield items
+ else:
+ for items in iters:
+ vals = tuple(map(key, items))
+ if vals.count(vals[0]) != N:
+ yield items
+
+
+def topk(k, seq, key=None):
+ """ Find the k largest elements of a sequence
+
+ Operates lazily in ``n*log(k)`` time
+
+ >>> topk(2, [1, 100, 10, 1000])
+ (1000, 100)
+
+ Use a key function to change sorted order
+
+ >>> topk(2, ['Alice', 'Bob', 'Charlie', 'Dan'], key=len)
+ ('Charlie', 'Alice')
+
+ See also:
+ heapq.nlargest
+ """
+ if key is not None and not callable(key):
+ key = getter(key)
+ return tuple(heapq.nlargest(k, seq, key=key))
+
+
+def peek(seq):
+ """ Retrieve the next element of a sequence
+
+ Returns the first element and an iterable equivalent to the original
+ sequence, still having the element retrieved.
+
+ >>> seq = [0, 1, 2, 3, 4]
+ >>> first, seq = peek(seq)
+ >>> first
+ 0
+ >>> list(seq)
+ [0, 1, 2, 3, 4]
+ """
+ iterator = iter(seq)
+ item = next(iterator)
+ return item, itertools.chain((item,), iterator)
+
+
+def peekn(n, seq):
+ """ Retrieve the next n elements of a sequence
+
+ Returns a tuple of the first n elements and an iterable equivalent
+ to the original, still having the elements retrieved.
+
+ >>> seq = [0, 1, 2, 3, 4]
+ >>> first_two, seq = peekn(2, seq)
+ >>> first_two
+ (0, 1)
+ >>> list(seq)
+ [0, 1, 2, 3, 4]
+ """
+ iterator = iter(seq)
+ peeked = tuple(take(n, iterator))
+ return peeked, itertools.chain(iter(peeked), iterator)
+
+
+def random_sample(prob, seq, random_state=None):
+ """ Return elements from a sequence with probability of prob
+
+ Returns a lazy iterator of random items from seq.
+
+ ``random_sample`` considers each item independently and without
+ replacement. See below how the first time it returned 13 items and the
+ next time it returned 6 items.
+
+ >>> seq = list(range(100))
+ >>> list(random_sample(0.1, seq)) # doctest: +SKIP
+ [6, 9, 19, 35, 45, 50, 58, 62, 68, 72, 78, 86, 95]
+ >>> list(random_sample(0.1, seq)) # doctest: +SKIP
+ [6, 44, 54, 61, 69, 94]
+
+ Providing an integer seed for ``random_state`` will result in
+ deterministic sampling. Given the same seed it will return the same sample
+ every time.
+
+ >>> list(random_sample(0.1, seq, random_state=2016))
+ [7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
+ >>> list(random_sample(0.1, seq, random_state=2016))
+ [7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
+
+ ``random_state`` can also be any object with a method ``random`` that
+ returns floats between 0.0 and 1.0 (exclusive).
+
+ >>> from random import Random
+ >>> randobj = Random(2016)
+ >>> list(random_sample(0.1, seq, random_state=randobj))
+ [7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
+ """
+ if not hasattr(random_state, 'random'):
+ random_state = Random(random_state)
+ return filter(lambda _: random_state.random() < prob, seq)
diff --git a/contrib/python/toolz/py2/toolz/recipes.py b/contrib/python/toolz/py2/toolz/recipes.py
new file mode 100644
index 0000000000..08c6c8c1e2
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/recipes.py
@@ -0,0 +1,47 @@
+import itertools
+from .itertoolz import frequencies, pluck, getter
+from .compatibility import map
+
+
+__all__ = ('countby', 'partitionby')
+
+
+def countby(key, seq):
+ """ Count elements of a collection by a key function
+
+ >>> countby(len, ['cat', 'mouse', 'dog'])
+ {3: 2, 5: 1}
+
+ >>> def iseven(x): return x % 2 == 0
+ >>> countby(iseven, [1, 2, 3]) # doctest:+SKIP
+ {True: 1, False: 2}
+
+ See Also:
+ groupby
+ """
+ if not callable(key):
+ key = getter(key)
+ return frequencies(map(key, seq))
+
+
+def partitionby(func, seq):
+ """ Partition a sequence according to a function
+
+ Partition `s` into a sequence of lists such that, when traversing
+ `s`, every time the output of `func` changes a new list is started
+ and that and subsequent items are collected into that list.
+
+ >>> is_space = lambda c: c == " "
+ >>> list(partitionby(is_space, "I have space"))
+ [('I',), (' ',), ('h', 'a', 'v', 'e'), (' ',), ('s', 'p', 'a', 'c', 'e')]
+
+ >>> is_large = lambda x: x > 10
+ >>> list(partitionby(is_large, [1, 2, 1, 99, 88, 33, 99, -1, 5]))
+ [(1, 2, 1), (99, 88, 33, 99), (-1, 5)]
+
+ See also:
+ partition
+ groupby
+ itertools.groupby
+ """
+ return map(tuple, pluck(1, itertools.groupby(seq, key=func)))
diff --git a/contrib/python/toolz/py2/toolz/sandbox/__init__.py b/contrib/python/toolz/py2/toolz/sandbox/__init__.py
new file mode 100644
index 0000000000..0abda1cb42
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/sandbox/__init__.py
@@ -0,0 +1,2 @@
+from .core import EqualityHashKey, unzip
+from .parallel import fold
diff --git a/contrib/python/toolz/py2/toolz/sandbox/core.py b/contrib/python/toolz/py2/toolz/sandbox/core.py
new file mode 100644
index 0000000000..915f06c213
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/sandbox/core.py
@@ -0,0 +1,133 @@
+from toolz.itertoolz import getter, cons, pluck
+from itertools import tee, starmap
+
+
+# See #166: https://github.com/pytoolz/toolz/issues/166
+# See #173: https://github.com/pytoolz/toolz/pull/173
+class EqualityHashKey(object):
+ """ Create a hash key that uses equality comparisons between items.
+
+ This may be used to create hash keys for otherwise unhashable types:
+
+ >>> from toolz import curry
+ >>> EqualityHashDefault = curry(EqualityHashKey, None)
+ >>> set(map(EqualityHashDefault, [[], (), [1], [1]])) # doctest: +SKIP
+ {=[]=, =()=, =[1]=}
+
+ **Caution:** adding N ``EqualityHashKey`` items to a hash container
+ may require O(N**2) operations, not O(N) as for typical hashable types.
+ Therefore, a suitable key function such as ``tuple`` or ``frozenset``
+ is usually preferred over using ``EqualityHashKey`` if possible.
+
+ The ``key`` argument to ``EqualityHashKey`` should be a function or
+ index that returns a hashable object that effectively distinguishes
+ unequal items. This helps avoid the poor scaling that occurs when
+ using the default key. For example, the above example can be improved
+ by using a key function that distinguishes items by length or type:
+
+ >>> EqualityHashLen = curry(EqualityHashKey, len)
+ >>> EqualityHashType = curry(EqualityHashKey, type) # this works too
+ >>> set(map(EqualityHashLen, [[], (), [1], [1]])) # doctest: +SKIP
+ {=[]=, =()=, =[1]=}
+
+ ``EqualityHashKey`` is convenient to use when a suitable key function
+ is complicated or unavailable. For example, the following returns all
+ unique values based on equality:
+
+ >>> from toolz import unique
+ >>> vals = [[], [], (), [1], [1], [2], {}, {}, {}]
+ >>> list(unique(vals, key=EqualityHashDefault))
+ [[], (), [1], [2], {}]
+
+ **Warning:** don't change the equality value of an item already in a hash
+ containter. Unhashable types are unhashable for a reason. For example:
+
+ >>> L1 = [1] ; L2 = [2]
+ >>> s = set(map(EqualityHashDefault, [L1, L2]))
+ >>> s # doctest: +SKIP
+ {=[1]=, =[2]=}
+
+ >>> L1[0] = 2 # Don't do this! ``s`` now has duplicate items!
+ >>> s # doctest: +SKIP
+ {=[2]=, =[2]=}
+
+ Although this may appear problematic, immutable data types is a common
+ idiom in functional programming, and``EqualityHashKey`` easily allows
+ the same idiom to be used by convention rather than strict requirement.
+
+ See Also:
+ identity
+ """
+ __slots__ = ['item', 'key']
+ _default_hashkey = '__default__hashkey__'
+
+ def __init__(self, key, item):
+ if key is None:
+ self.key = self._default_hashkey
+ elif not callable(key):
+ self.key = getter(key)
+ else:
+ self.key = key
+ self.item = item
+
+ def __hash__(self):
+ if self.key == self._default_hashkey:
+ val = self.key
+ else:
+ val = self.key(self.item)
+ return hash(val)
+
+ def __eq__(self, other):
+ try:
+ return (self._default_hashkey == other._default_hashkey and
+ self.item == other.item)
+ except AttributeError:
+ return False
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __str__(self):
+ return '=%s=' % str(self.item)
+
+ def __repr__(self):
+ return '=%s=' % repr(self.item)
+
+
+# See issue #293: https://github.com/pytoolz/toolz/issues/239
+def unzip(seq):
+ """Inverse of ``zip``
+
+ >>> a, b = unzip([('a', 1), ('b', 2)])
+ >>> list(a)
+ ['a', 'b']
+ >>> list(b)
+ [1, 2]
+
+ Unlike the naive implementation ``def unzip(seq): zip(*seq)`` this
+ implementation can handle an infinite sequence ``seq``.
+
+ Caveats:
+
+ * The implementation uses ``tee``, and so can use a significant amount
+ of auxiliary storage if the resulting iterators are consumed at
+ different times.
+
+ * The inner sequence cannot be infinite. In Python 3 ``zip(*seq)`` can be
+ used if ``seq`` is a finite sequence of infinite sequences.
+
+ """
+
+ seq = iter(seq)
+
+ # Check how many iterators we need
+ try:
+ first = tuple(next(seq))
+ except StopIteration:
+ return tuple()
+
+ # and create them
+ niters = len(first)
+ seqs = tee(cons(first, seq), niters)
+
+ return tuple(starmap(pluck, enumerate(seqs)))
diff --git a/contrib/python/toolz/py2/toolz/sandbox/parallel.py b/contrib/python/toolz/py2/toolz/sandbox/parallel.py
new file mode 100644
index 0000000000..ef8ed39dbd
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/sandbox/parallel.py
@@ -0,0 +1,76 @@
+import functools
+from toolz.itertoolz import partition_all
+from toolz.compatibility import reduce, map
+from toolz.utils import no_default
+
+
+def _reduce(func, seq, initial=None):
+ if initial is None:
+ return functools.reduce(func, seq)
+ else:
+ return functools.reduce(func, seq, initial)
+
+
+def fold(binop, seq, default=no_default, map=map, chunksize=128, combine=None):
+ """
+ Reduce without guarantee of ordered reduction.
+
+ inputs:
+
+ ``binop`` - associative operator. The associative property allows us to
+ leverage a parallel map to perform reductions in parallel.
+ ``seq`` - a sequence to be aggregated
+ ``default`` - an identity element like 0 for ``add`` or 1 for mul
+
+ ``map`` - an implementation of ``map``. This may be parallel and
+ determines how work is distributed.
+ ``chunksize`` - Number of elements of ``seq`` that should be handled
+ within a single function call
+ ``combine`` - Binary operator to combine two intermediate results.
+ If ``binop`` is of type (total, item) -> total
+ then ``combine`` is of type (total, total) -> total
+ Defaults to ``binop`` for common case of operators like add
+
+ Fold chunks up the collection into blocks of size ``chunksize`` and then
+ feeds each of these to calls to ``reduce``. This work is distributed
+ with a call to ``map``, gathered back and then refolded to finish the
+ computation. In this way ``fold`` specifies only how to chunk up data but
+ leaves the distribution of this work to an externally provided ``map``
+ function. This function can be sequential or rely on multithreading,
+ multiprocessing, or even distributed solutions.
+
+ If ``map`` intends to serialize functions it should be prepared to accept
+ and serialize lambdas. Note that the standard ``pickle`` module fails
+ here.
+
+ Example
+ -------
+
+ >>> # Provide a parallel map to accomplish a parallel sum
+ >>> from operator import add
+ >>> fold(add, [1, 2, 3, 4], chunksize=2, map=map)
+ 10
+ """
+ assert chunksize > 1
+
+ if combine is None:
+ combine = binop
+
+ chunks = partition_all(chunksize, seq)
+
+ # Evaluate sequence in chunks via map
+ if default == no_default:
+ results = map(
+ functools.partial(_reduce, binop),
+ chunks)
+ else:
+ results = map(
+ functools.partial(_reduce, binop, initial=default),
+ chunks)
+
+ results = list(results) # TODO: Support complete laziness
+
+ if len(results) == 1: # Return completed result
+ return results[0]
+ else: # Recurse to reaggregate intermediate results
+ return fold(combine, results, map=map, chunksize=chunksize)
diff --git a/contrib/python/toolz/py2/toolz/utils.py b/contrib/python/toolz/py2/toolz/utils.py
new file mode 100644
index 0000000000..1002c4649f
--- /dev/null
+++ b/contrib/python/toolz/py2/toolz/utils.py
@@ -0,0 +1,9 @@
+def raises(err, lamda):
+ try:
+ lamda()
+ return False
+ except err:
+ return True
+
+
+no_default = '__no__default__'
diff --git a/contrib/python/toolz/py2/ya.make b/contrib/python/toolz/py2/ya.make
new file mode 100644
index 0000000000..f64a13da67
--- /dev/null
+++ b/contrib/python/toolz/py2/ya.make
@@ -0,0 +1,41 @@
+# Generated by devtools/yamaker (pypi).
+
+PY2_LIBRARY()
+
+VERSION(0.10.0)
+
+LICENSE(BSD-3-Clause)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ tlz/__init__.py
+ tlz/_build_tlz.py
+ toolz/__init__.py
+ toolz/_signatures.py
+ toolz/compatibility.py
+ toolz/curried/__init__.py
+ toolz/curried/exceptions.py
+ toolz/curried/operator.py
+ toolz/dicttoolz.py
+ toolz/functoolz.py
+ toolz/itertoolz.py
+ toolz/recipes.py
+ toolz/sandbox/__init__.py
+ toolz/sandbox/core.py
+ toolz/sandbox/parallel.py
+ toolz/utils.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/toolz/py2/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/toolz/py3/.dist-info/METADATA b/contrib/python/toolz/py3/.dist-info/METADATA
new file mode 100644
index 0000000000..0af60db4cf
--- /dev/null
+++ b/contrib/python/toolz/py3/.dist-info/METADATA
@@ -0,0 +1,159 @@
+Metadata-Version: 2.1
+Name: toolz
+Version: 0.12.0
+Summary: List processing tools and functional utilities
+Home-page: https://github.com/pytoolz/toolz/
+Author: https://raw.github.com/pytoolz/toolz/master/AUTHORS.md
+Maintainer: Erik Welch
+Maintainer-email: erik.n.welch@gmail.com
+License: BSD
+Keywords: functional utility itertools functools
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=3.5
+
+Toolz
+=====
+
+|Build Status| |Coverage Status| |Version Status|
+
+A set of utility functions for iterators, functions, and dictionaries.
+
+See the PyToolz documentation at https://toolz.readthedocs.io
+
+LICENSE
+-------
+
+New BSD. See `License File <https://github.com/pytoolz/toolz/blob/master/LICENSE.txt>`__.
+
+Install
+-------
+
+``toolz`` is on the Python Package Index (PyPI):
+
+::
+
+ pip install toolz
+
+Structure and Heritage
+----------------------
+
+``toolz`` is implemented in three parts:
+
+|literal itertoolz|_, for operations on iterables. Examples: ``groupby``,
+``unique``, ``interpose``,
+
+|literal functoolz|_, for higher-order functions. Examples: ``memoize``,
+``curry``, ``compose``,
+
+|literal dicttoolz|_, for operations on dictionaries. Examples: ``assoc``,
+``update-in``, ``merge``.
+
+.. |literal itertoolz| replace:: ``itertoolz``
+.. _literal itertoolz: https://github.com/pytoolz/toolz/blob/master/toolz/itertoolz.py
+
+.. |literal functoolz| replace:: ``functoolz``
+.. _literal functoolz: https://github.com/pytoolz/toolz/blob/master/toolz/functoolz.py
+
+.. |literal dicttoolz| replace:: ``dicttoolz``
+.. _literal dicttoolz: https://github.com/pytoolz/toolz/blob/master/toolz/dicttoolz.py
+
+These functions come from the legacy of functional languages for list
+processing. They interoperate well to accomplish common complex tasks.
+
+Read our `API
+Documentation <https://toolz.readthedocs.io/en/latest/api.html>`__ for
+more details.
+
+Example
+-------
+
+This builds a standard wordcount function from pieces within ``toolz``:
+
+.. code:: python
+
+ >>> def stem(word):
+ ... """ Stem word to primitive form """
+ ... return word.lower().rstrip(",.!:;'-\"").lstrip("'\"")
+
+ >>> from toolz import compose, frequencies
+ >>> from toolz.curried import map
+ >>> wordcount = compose(frequencies, map(stem), str.split)
+
+ >>> sentence = "This cat jumped over this other cat!"
+ >>> wordcount(sentence)
+ {'this': 2, 'cat': 2, 'jumped': 1, 'over': 1, 'other': 1}
+
+Dependencies
+------------
+
+``toolz`` supports Python 3.5+ with a common codebase.
+It is pure Python and requires no dependencies beyond the standard
+library.
+
+It is, in short, a lightweight dependency.
+
+
+CyToolz
+-------
+
+The ``toolz`` project has been reimplemented in `Cython <http://cython.org>`__.
+The ``cytoolz`` project is a drop-in replacement for the Pure Python
+implementation.
+See `CyToolz GitHub Page <https://github.com/pytoolz/cytoolz/>`__ for more
+details.
+
+See Also
+--------
+
+- `Underscore.js <https://underscorejs.org/>`__: A similar library for
+ JavaScript
+- `Enumerable <https://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A
+ similar library for Ruby
+- `Clojure <https://clojure.org/>`__: A functional language whose
+ standard library has several counterparts in ``toolz``
+- `itertools <https://docs.python.org/2/library/itertools.html>`__: The
+ Python standard library for iterator tools
+- `functools <https://docs.python.org/2/library/functools.html>`__: The
+ Python standard library for function tools
+
+Contributions Welcome
+---------------------
+
+``toolz`` aims to be a repository for utility functions, particularly
+those that come from the functional programming and list processing
+traditions. We welcome contributions that fall within this scope.
+
+We also try to keep the API small to keep ``toolz`` manageable. The ideal
+contribution is significantly different from existing functions and has
+precedent in a few other functional systems.
+
+Please take a look at our
+`issue page <https://github.com/pytoolz/toolz/issues>`__
+for contribution ideas.
+
+Community
+---------
+
+See our `mailing list <https://groups.google.com/forum/#!forum/pytoolz>`__.
+We're friendly.
+
+.. |Build Status| image:: https://github.com/pytoolz/toolz/workflows/Test/badge.svg
+ :target: https://github.com/pytoolz/toolz/actions
+.. |Coverage Status| image:: https://coveralls.io/repos/pytoolz/toolz/badge.svg?branch=master
+ :target: https://coveralls.io/r/pytoolz/toolz
+.. |Version Status| image:: https://badge.fury.io/py/toolz.svg
+ :target: https://badge.fury.io/py/toolz
+
+
diff --git a/contrib/python/toolz/py3/.dist-info/top_level.txt b/contrib/python/toolz/py3/.dist-info/top_level.txt
new file mode 100644
index 0000000000..e58ef014ac
--- /dev/null
+++ b/contrib/python/toolz/py3/.dist-info/top_level.txt
@@ -0,0 +1,2 @@
+tlz
+toolz
diff --git a/contrib/python/toolz/py3/AUTHORS.md b/contrib/python/toolz/py3/AUTHORS.md
new file mode 100644
index 0000000000..bd4a563d9b
--- /dev/null
+++ b/contrib/python/toolz/py3/AUTHORS.md
@@ -0,0 +1,33 @@
+[Matthew Rocklin](http://matthewrocklin.com) [@mrocklin](http://github.com/mrocklin/)
+
+[John Jacobsen](http://eigenhombre.com) [@eigenhombre](http://github.com/eigenhombre/)
+
+Erik Welch [@eriknw](https://github.com/eriknw/)
+
+John Crichton [@jcrichton](https://github.com/jcrichton/)
+
+Han Semaj [@microamp](https://github.com/microamp/)
+
+[Graeme Coupar](https://twitter.com/obmarg) [@obmarg](https://github.com/obmarg/)
+
+[Leonid Shvechikov](http://brainstorage.me/shvechikov) [@shvechikov](https://github.com/shvechikov)
+
+Lars Buitinck [@larsmans](http://github.com/larsmans)
+
+José Ricardo [@josericardo](https://github.com/josericardo)
+
+Tom Prince [@tomprince](https://github.com/tomprince)
+
+Bart van Merriënboer [@bartvm](https://github.com/bartvm)
+
+Nikolaos-Digenis Karagiannis [@digenis](https://github.com/digenis/)
+
+[Antonio Lima](https://twitter.com/themiurgo) [@themiurgo](https://github.com/themiurgo/)
+
+Joe Jevnik [@llllllllll](https://github.com/llllllllll)
+
+Rory Kirchner [@roryk](https://github.com/roryk)
+
+[Steven Cutting](http://steven-cutting.github.io) [@steven_cutting](https://github.com/steven-cutting)
+
+Aric Coady [@coady](https://github.com/coady)
diff --git a/contrib/python/toolz/py3/LICENSE.txt b/contrib/python/toolz/py3/LICENSE.txt
new file mode 100644
index 0000000000..eeb91b202c
--- /dev/null
+++ b/contrib/python/toolz/py3/LICENSE.txt
@@ -0,0 +1,28 @@
+Copyright (c) 2013 Matthew Rocklin
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ a. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ b. 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.
+ c. Neither the name of toolz nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 REGENTS OR CONTRIBUTORS 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/contrib/python/toolz/py3/README.rst b/contrib/python/toolz/py3/README.rst
new file mode 100644
index 0000000000..e62ac7917e
--- /dev/null
+++ b/contrib/python/toolz/py3/README.rst
@@ -0,0 +1,132 @@
+Toolz
+=====
+
+|Build Status| |Coverage Status| |Version Status|
+
+A set of utility functions for iterators, functions, and dictionaries.
+
+See the PyToolz documentation at https://toolz.readthedocs.io
+
+LICENSE
+-------
+
+New BSD. See `License File <https://github.com/pytoolz/toolz/blob/master/LICENSE.txt>`__.
+
+Install
+-------
+
+``toolz`` is on the Python Package Index (PyPI):
+
+::
+
+ pip install toolz
+
+Structure and Heritage
+----------------------
+
+``toolz`` is implemented in three parts:
+
+|literal itertoolz|_, for operations on iterables. Examples: ``groupby``,
+``unique``, ``interpose``,
+
+|literal functoolz|_, for higher-order functions. Examples: ``memoize``,
+``curry``, ``compose``,
+
+|literal dicttoolz|_, for operations on dictionaries. Examples: ``assoc``,
+``update-in``, ``merge``.
+
+.. |literal itertoolz| replace:: ``itertoolz``
+.. _literal itertoolz: https://github.com/pytoolz/toolz/blob/master/toolz/itertoolz.py
+
+.. |literal functoolz| replace:: ``functoolz``
+.. _literal functoolz: https://github.com/pytoolz/toolz/blob/master/toolz/functoolz.py
+
+.. |literal dicttoolz| replace:: ``dicttoolz``
+.. _literal dicttoolz: https://github.com/pytoolz/toolz/blob/master/toolz/dicttoolz.py
+
+These functions come from the legacy of functional languages for list
+processing. They interoperate well to accomplish common complex tasks.
+
+Read our `API
+Documentation <https://toolz.readthedocs.io/en/latest/api.html>`__ for
+more details.
+
+Example
+-------
+
+This builds a standard wordcount function from pieces within ``toolz``:
+
+.. code:: python
+
+ >>> def stem(word):
+ ... """ Stem word to primitive form """
+ ... return word.lower().rstrip(",.!:;'-\"").lstrip("'\"")
+
+ >>> from toolz import compose, frequencies
+ >>> from toolz.curried import map
+ >>> wordcount = compose(frequencies, map(stem), str.split)
+
+ >>> sentence = "This cat jumped over this other cat!"
+ >>> wordcount(sentence)
+ {'this': 2, 'cat': 2, 'jumped': 1, 'over': 1, 'other': 1}
+
+Dependencies
+------------
+
+``toolz`` supports Python 3.5+ with a common codebase.
+It is pure Python and requires no dependencies beyond the standard
+library.
+
+It is, in short, a lightweight dependency.
+
+
+CyToolz
+-------
+
+The ``toolz`` project has been reimplemented in `Cython <http://cython.org>`__.
+The ``cytoolz`` project is a drop-in replacement for the Pure Python
+implementation.
+See `CyToolz GitHub Page <https://github.com/pytoolz/cytoolz/>`__ for more
+details.
+
+See Also
+--------
+
+- `Underscore.js <https://underscorejs.org/>`__: A similar library for
+ JavaScript
+- `Enumerable <https://ruby-doc.org/core-2.0.0/Enumerable.html>`__: A
+ similar library for Ruby
+- `Clojure <https://clojure.org/>`__: A functional language whose
+ standard library has several counterparts in ``toolz``
+- `itertools <https://docs.python.org/2/library/itertools.html>`__: The
+ Python standard library for iterator tools
+- `functools <https://docs.python.org/2/library/functools.html>`__: The
+ Python standard library for function tools
+
+Contributions Welcome
+---------------------
+
+``toolz`` aims to be a repository for utility functions, particularly
+those that come from the functional programming and list processing
+traditions. We welcome contributions that fall within this scope.
+
+We also try to keep the API small to keep ``toolz`` manageable. The ideal
+contribution is significantly different from existing functions and has
+precedent in a few other functional systems.
+
+Please take a look at our
+`issue page <https://github.com/pytoolz/toolz/issues>`__
+for contribution ideas.
+
+Community
+---------
+
+See our `mailing list <https://groups.google.com/forum/#!forum/pytoolz>`__.
+We're friendly.
+
+.. |Build Status| image:: https://github.com/pytoolz/toolz/workflows/Test/badge.svg
+ :target: https://github.com/pytoolz/toolz/actions
+.. |Coverage Status| image:: https://coveralls.io/repos/pytoolz/toolz/badge.svg?branch=master
+ :target: https://coveralls.io/r/pytoolz/toolz
+.. |Version Status| image:: https://badge.fury.io/py/toolz.svg
+ :target: https://badge.fury.io/py/toolz
diff --git a/contrib/python/toolz/py3/tlz/__init__.py b/contrib/python/toolz/py3/tlz/__init__.py
new file mode 100644
index 0000000000..9c9c84afe1
--- /dev/null
+++ b/contrib/python/toolz/py3/tlz/__init__.py
@@ -0,0 +1,9 @@
+"""``tlz`` mirrors the ``toolz`` API and uses ``cytoolz`` if possible.
+
+The ``tlz`` package is installed when ``toolz`` is installed. It provides
+a convenient way to use functions from ``cytoolz``--a faster Cython
+implementation of ``toolz``--if it is installed, otherwise it uses
+functions from ``toolz``.
+"""
+
+from . import _build_tlz
diff --git a/contrib/python/toolz/py3/tlz/_build_tlz.py b/contrib/python/toolz/py3/tlz/_build_tlz.py
new file mode 100644
index 0000000000..3ac783699e
--- /dev/null
+++ b/contrib/python/toolz/py3/tlz/_build_tlz.py
@@ -0,0 +1,92 @@
+import sys
+import types
+import toolz
+from importlib import import_module
+from importlib.machinery import ModuleSpec
+
+
+class TlzLoader:
+ """ Finds and loads ``tlz`` modules when added to sys.meta_path"""
+ def __init__(self):
+ self.always_from_toolz = {
+ toolz.pipe,
+ }
+
+ def _load_toolz(self, fullname):
+ rv = {}
+ package, dot, submodules = fullname.partition('.')
+ try:
+ module_name = ''.join(['cytoolz', dot, submodules])
+ rv['cytoolz'] = import_module(module_name)
+ except ImportError:
+ pass
+ try:
+ module_name = ''.join(['toolz', dot, submodules])
+ rv['toolz'] = import_module(module_name)
+ except ImportError:
+ pass
+ if not rv:
+ raise ImportError(fullname)
+ return rv
+
+ def find_module(self, fullname, path=None): # pragma: py3 no cover
+ package, dot, submodules = fullname.partition('.')
+ if package == 'tlz':
+ return self
+
+ def load_module(self, fullname): # pragma: py3 no cover
+ if fullname in sys.modules: # pragma: no cover
+ return sys.modules[fullname]
+ spec = ModuleSpec(fullname, self)
+ module = self.create_module(spec)
+ sys.modules[fullname] = module
+ self.exec_module(module)
+ return module
+
+ def find_spec(self, fullname, path, target=None): # pragma: no cover
+ package, dot, submodules = fullname.partition('.')
+ if package == 'tlz':
+ return ModuleSpec(fullname, self)
+
+ def create_module(self, spec):
+ return types.ModuleType(spec.name)
+
+ def exec_module(self, module):
+ toolz_mods = self._load_toolz(module.__name__)
+ fast_mod = toolz_mods.get('cytoolz') or toolz_mods['toolz']
+ slow_mod = toolz_mods.get('toolz') or toolz_mods['cytoolz']
+ module.__dict__.update(toolz.merge(fast_mod.__dict__, module.__dict__))
+ package = fast_mod.__package__
+ if package is not None:
+ package, dot, submodules = package.partition('.')
+ module.__package__ = ''.join(['tlz', dot, submodules])
+ if not module.__doc__:
+ module.__doc__ = fast_mod.__doc__
+
+ # show file from toolz during introspection
+ try:
+ module.__file__ = slow_mod.__file__
+ except AttributeError:
+ pass
+
+ for k, v in fast_mod.__dict__.items():
+ tv = slow_mod.__dict__.get(k)
+ try:
+ hash(tv)
+ except TypeError:
+ tv = None
+ if tv in self.always_from_toolz:
+ module.__dict__[k] = tv
+ elif (
+ isinstance(v, types.ModuleType)
+ and v.__package__ == fast_mod.__name__
+ ):
+ package, dot, submodules = v.__name__.partition('.')
+ module_name = ''.join(['tlz', dot, submodules])
+ submodule = import_module(module_name)
+ module.__dict__[k] = submodule
+
+
+tlz_loader = TlzLoader()
+sys.meta_path.append(tlz_loader)
+tlz_loader.exec_module(sys.modules['tlz'])
diff --git a/contrib/python/toolz/py3/toolz/__init__.py b/contrib/python/toolz/py3/toolz/__init__.py
new file mode 100644
index 0000000000..ba49a662fc
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/__init__.py
@@ -0,0 +1,26 @@
+from .itertoolz import *
+
+from .functoolz import *
+
+from .dicttoolz import *
+
+from .recipes import *
+
+from functools import partial, reduce
+
+sorted = sorted
+
+map = map
+
+filter = filter
+
+# Aliases
+comp = compose
+
+from . import curried, sandbox
+
+functoolz._sigs.create_signature_registry()
+
+from ._version import get_versions
+__version__ = get_versions()['version']
+del get_versions
diff --git a/contrib/python/toolz/py3/toolz/_signatures.py b/contrib/python/toolz/py3/toolz/_signatures.py
new file mode 100644
index 0000000000..3ce1616a85
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/_signatures.py
@@ -0,0 +1,785 @@
+"""Internal module for better introspection of builtins.
+
+The main functions are ``is_builtin_valid_args``, ``is_builtin_partial_args``,
+and ``has_unknown_args``. Other functions in this module support these three.
+
+Notably, we create a ``signatures`` registry to enable introspection of
+builtin functions in any Python version. This includes builtins that
+have more than one valid signature. Currently, the registry includes
+builtins from ``builtins``, ``functools``, ``itertools``, and ``operator``
+modules. More can be added as requested. We don't guarantee full coverage.
+
+Everything in this module should be regarded as implementation details.
+Users should try to not use this module directly.
+"""
+import functools
+import inspect
+import itertools
+import operator
+from importlib import import_module
+
+from .functoolz import (is_partial_args, is_arity, has_varargs,
+ has_keywords, num_required_args)
+
+import builtins
+
+# We mock builtin callables using lists of tuples with lambda functions.
+#
+# The tuple spec is (num_position_args, lambda_func, keyword_only_args).
+#
+# num_position_args:
+# - The number of positional-only arguments. If not specified,
+# all positional arguments are considered positional-only.
+#
+# lambda_func:
+# - lambda function that matches a signature of a builtin, but does
+# not include keyword-only arguments.
+#
+# keyword_only_args: (optional)
+# - Tuple of keyword-only argumemts.
+
+module_info = {}
+
+module_info[builtins] = dict(
+ abs=[
+ lambda x: None],
+ all=[
+ lambda iterable: None],
+ anext=[
+ lambda aiterator: None,
+ lambda aiterator, default: None],
+ any=[
+ lambda iterable: None],
+ apply=[
+ lambda object: None,
+ lambda object, args: None,
+ lambda object, args, kwargs: None],
+ ascii=[
+ lambda obj: None],
+ bin=[
+ lambda number: None],
+ bool=[
+ lambda x=False: None],
+ buffer=[
+ lambda object: None,
+ lambda object, offset: None,
+ lambda object, offset, size: None],
+ bytearray=[
+ lambda: None,
+ lambda int: None,
+ lambda string, encoding='utf8', errors='strict': None],
+ callable=[
+ lambda obj: None],
+ chr=[
+ lambda i: None],
+ classmethod=[
+ lambda function: None],
+ cmp=[
+ lambda x, y: None],
+ coerce=[
+ lambda x, y: None],
+ complex=[
+ lambda real=0, imag=0: None],
+ delattr=[
+ lambda obj, name: None],
+ dict=[
+ lambda **kwargs: None,
+ lambda mapping, **kwargs: None],
+ dir=[
+ lambda: None,
+ lambda object: None],
+ divmod=[
+ lambda x, y: None],
+ enumerate=[
+ (0, lambda iterable, start=0: None)],
+ eval=[
+ lambda source: None,
+ lambda source, globals: None,
+ lambda source, globals, locals: None],
+ execfile=[
+ lambda filename: None,
+ lambda filename, globals: None,
+ lambda filename, globals, locals: None],
+ file=[
+ (0, lambda name, mode='r', buffering=-1: None)],
+ filter=[
+ lambda function, iterable: None],
+ float=[
+ lambda x=0.0: None],
+ format=[
+ lambda value: None,
+ lambda value, format_spec: None],
+ frozenset=[
+ lambda: None,
+ lambda iterable: None],
+ getattr=[
+ lambda object, name: None,
+ lambda object, name, default: None],
+ globals=[
+ lambda: None],
+ hasattr=[
+ lambda obj, name: None],
+ hash=[
+ lambda obj: None],
+ hex=[
+ lambda number: None],
+ id=[
+ lambda obj: None],
+ input=[
+ lambda: None,
+ lambda prompt: None],
+ int=[
+ lambda x=0: None,
+ (0, lambda x, base=10: None)],
+ intern=[
+ lambda string: None],
+ isinstance=[
+ lambda obj, class_or_tuple: None],
+ issubclass=[
+ lambda cls, class_or_tuple: None],
+ iter=[
+ lambda iterable: None,
+ lambda callable, sentinel: None],
+ len=[
+ lambda obj: None],
+ list=[
+ lambda: None,
+ lambda iterable: None],
+ locals=[
+ lambda: None],
+ long=[
+ lambda x=0: None,
+ (0, lambda x, base=10: None)],
+ map=[
+ lambda func, sequence, *iterables: None],
+ memoryview=[
+ (0, lambda object: None)],
+ next=[
+ lambda iterator: None,
+ lambda iterator, default: None],
+ object=[
+ lambda: None],
+ oct=[
+ lambda number: None],
+ ord=[
+ lambda c: None],
+ pow=[
+ lambda x, y: None,
+ lambda x, y, z: None],
+ property=[
+ lambda fget=None, fset=None, fdel=None, doc=None: None],
+ range=[
+ lambda stop: None,
+ lambda start, stop: None,
+ lambda start, stop, step: None],
+ raw_input=[
+ lambda: None,
+ lambda prompt: None],
+ reduce=[
+ lambda function, sequence: None,
+ lambda function, sequence, initial: None],
+ reload=[
+ lambda module: None],
+ repr=[
+ lambda obj: None],
+ reversed=[
+ lambda sequence: None],
+ round=[
+ (0, lambda number, ndigits=0: None)],
+ set=[
+ lambda: None,
+ lambda iterable: None],
+ setattr=[
+ lambda obj, name, value: None],
+ slice=[
+ lambda stop: None,
+ lambda start, stop: None,
+ lambda start, stop, step: None],
+ staticmethod=[
+ lambda function: None],
+ sum=[
+ lambda iterable: None,
+ lambda iterable, start: None],
+ super=[
+ lambda type: None,
+ lambda type, obj: None],
+ tuple=[
+ lambda: None,
+ lambda iterable: None],
+ type=[
+ lambda object: None,
+ lambda name, bases, dict: None],
+ unichr=[
+ lambda i: None],
+ unicode=[
+ lambda object: None,
+ lambda string='', encoding='utf8', errors='strict': None],
+ vars=[
+ lambda: None,
+ lambda object: None],
+ xrange=[
+ lambda stop: None,
+ lambda start, stop: None,
+ lambda start, stop, step: None],
+ zip=[
+ lambda *iterables: None],
+ __build_class__=[
+ (2, lambda func, name, *bases, **kwds: None, ('metaclass',))],
+ __import__=[
+ (0, lambda name, globals=None, locals=None, fromlist=None,
+ level=None: None)],
+)
+module_info[builtins]['exec'] = [
+ lambda source: None,
+ lambda source, globals: None,
+ lambda source, globals, locals: None]
+
+module_info[builtins].update(
+ breakpoint=[
+ lambda *args, **kws: None],
+ bytes=[
+ lambda: None,
+ lambda int: None,
+ lambda string, encoding='utf8', errors='strict': None],
+ compile=[
+ (0, lambda source, filename, mode, flags=0,
+ dont_inherit=False, optimize=-1: None)],
+ max=[
+ (1, lambda iterable: None, ('default', 'key',)),
+ (1, lambda arg1, arg2, *args: None, ('key',))],
+ min=[
+ (1, lambda iterable: None, ('default', 'key',)),
+ (1, lambda arg1, arg2, *args: None, ('key',))],
+ open=[
+ (0, lambda file, mode='r', buffering=-1, encoding=None,
+ errors=None, newline=None, closefd=True, opener=None: None)],
+ sorted=[
+ (1, lambda iterable: None, ('key', 'reverse'))],
+ str=[
+ lambda object='', encoding='utf', errors='strict': None],
+)
+module_info[builtins]['print'] = [
+ (0, lambda *args: None, ('sep', 'end', 'file', 'flush',))]
+
+
+module_info[functools] = dict(
+ cmp_to_key=[
+ (0, lambda mycmp: None)],
+ partial=[
+ lambda func, *args, **kwargs: None],
+ partialmethod=[
+ lambda func, *args, **kwargs: None],
+ reduce=[
+ lambda function, sequence: None,
+ lambda function, sequence, initial: None],
+)
+
+module_info[itertools] = dict(
+ accumulate=[
+ (0, lambda iterable, func=None: None)],
+ chain=[
+ lambda *iterables: None],
+ combinations=[
+ (0, lambda iterable, r: None)],
+ combinations_with_replacement=[
+ (0, lambda iterable, r: None)],
+ compress=[
+ (0, lambda data, selectors: None)],
+ count=[
+ lambda start=0, step=1: None],
+ cycle=[
+ lambda iterable: None],
+ dropwhile=[
+ lambda predicate, iterable: None],
+ filterfalse=[
+ lambda function, sequence: None],
+ groupby=[
+ (0, lambda iterable, key=None: None)],
+ ifilter=[
+ lambda function, sequence: None],
+ ifilterfalse=[
+ lambda function, sequence: None],
+ imap=[
+ lambda func, sequence, *iterables: None],
+ islice=[
+ lambda iterable, stop: None,
+ lambda iterable, start, stop: None,
+ lambda iterable, start, stop, step: None],
+ izip=[
+ lambda *iterables: None],
+ izip_longest=[
+ (0, lambda *iterables: None, ('fillvalue',))],
+ permutations=[
+ (0, lambda iterable, r=0: None)],
+ repeat=[
+ (0, lambda object, times=0: None)],
+ starmap=[
+ lambda function, sequence: None],
+ takewhile=[
+ lambda predicate, iterable: None],
+ tee=[
+ lambda iterable: None,
+ lambda iterable, n: None],
+ zip_longest=[
+ (0, lambda *iterables: None, ('fillvalue',))],
+)
+
+module_info[itertools].update(
+ product=[
+ (0, lambda *iterables: None, ('repeat',))],
+)
+
+
+module_info[operator] = dict(
+ __abs__=[
+ lambda a: None],
+ __add__=[
+ lambda a, b: None],
+ __and__=[
+ lambda a, b: None],
+ __concat__=[
+ lambda a, b: None],
+ __contains__=[
+ lambda a, b: None],
+ __delitem__=[
+ lambda a, b: None],
+ __delslice__=[
+ lambda a, b, c: None],
+ __div__=[
+ lambda a, b: None],
+ __eq__=[
+ lambda a, b: None],
+ __floordiv__=[
+ lambda a, b: None],
+ __ge__=[
+ lambda a, b: None],
+ __getitem__=[
+ lambda a, b: None],
+ __getslice__=[
+ lambda a, b, c: None],
+ __gt__=[
+ lambda a, b: None],
+ __iadd__=[
+ lambda a, b: None],
+ __iand__=[
+ lambda a, b: None],
+ __iconcat__=[
+ lambda a, b: None],
+ __idiv__=[
+ lambda a, b: None],
+ __ifloordiv__=[
+ lambda a, b: None],
+ __ilshift__=[
+ lambda a, b: None],
+ __imatmul__=[
+ lambda a, b: None],
+ __imod__=[
+ lambda a, b: None],
+ __imul__=[
+ lambda a, b: None],
+ __index__=[
+ lambda a: None],
+ __inv__=[
+ lambda a: None],
+ __invert__=[
+ lambda a: None],
+ __ior__=[
+ lambda a, b: None],
+ __ipow__=[
+ lambda a, b: None],
+ __irepeat__=[
+ lambda a, b: None],
+ __irshift__=[
+ lambda a, b: None],
+ __isub__=[
+ lambda a, b: None],
+ __itruediv__=[
+ lambda a, b: None],
+ __ixor__=[
+ lambda a, b: None],
+ __le__=[
+ lambda a, b: None],
+ __lshift__=[
+ lambda a, b: None],
+ __lt__=[
+ lambda a, b: None],
+ __matmul__=[
+ lambda a, b: None],
+ __mod__=[
+ lambda a, b: None],
+ __mul__=[
+ lambda a, b: None],
+ __ne__=[
+ lambda a, b: None],
+ __neg__=[
+ lambda a: None],
+ __not__=[
+ lambda a: None],
+ __or__=[
+ lambda a, b: None],
+ __pos__=[
+ lambda a: None],
+ __pow__=[
+ lambda a, b: None],
+ __repeat__=[
+ lambda a, b: None],
+ __rshift__=[
+ lambda a, b: None],
+ __setitem__=[
+ lambda a, b, c: None],
+ __setslice__=[
+ lambda a, b, c, d: None],
+ __sub__=[
+ lambda a, b: None],
+ __truediv__=[
+ lambda a, b: None],
+ __xor__=[
+ lambda a, b: None],
+ _abs=[
+ lambda x: None],
+ _compare_digest=[
+ lambda a, b: None],
+ abs=[
+ lambda a: None],
+ add=[
+ lambda a, b: None],
+ and_=[
+ lambda a, b: None],
+ attrgetter=[
+ lambda attr, *args: None],
+ concat=[
+ lambda a, b: None],
+ contains=[
+ lambda a, b: None],
+ countOf=[
+ lambda a, b: None],
+ delitem=[
+ lambda a, b: None],
+ delslice=[
+ lambda a, b, c: None],
+ div=[
+ lambda a, b: None],
+ eq=[
+ lambda a, b: None],
+ floordiv=[
+ lambda a, b: None],
+ ge=[
+ lambda a, b: None],
+ getitem=[
+ lambda a, b: None],
+ getslice=[
+ lambda a, b, c: None],
+ gt=[
+ lambda a, b: None],
+ iadd=[
+ lambda a, b: None],
+ iand=[
+ lambda a, b: None],
+ iconcat=[
+ lambda a, b: None],
+ idiv=[
+ lambda a, b: None],
+ ifloordiv=[
+ lambda a, b: None],
+ ilshift=[
+ lambda a, b: None],
+ imatmul=[
+ lambda a, b: None],
+ imod=[
+ lambda a, b: None],
+ imul=[
+ lambda a, b: None],
+ index=[
+ lambda a: None],
+ indexOf=[
+ lambda a, b: None],
+ inv=[
+ lambda a: None],
+ invert=[
+ lambda a: None],
+ ior=[
+ lambda a, b: None],
+ ipow=[
+ lambda a, b: None],
+ irepeat=[
+ lambda a, b: None],
+ irshift=[
+ lambda a, b: None],
+ is_=[
+ lambda a, b: None],
+ is_not=[
+ lambda a, b: None],
+ isCallable=[
+ lambda a: None],
+ isMappingType=[
+ lambda a: None],
+ isNumberType=[
+ lambda a: None],
+ isSequenceType=[
+ lambda a: None],
+ isub=[
+ lambda a, b: None],
+ itemgetter=[
+ lambda item, *args: None],
+ itruediv=[
+ lambda a, b: None],
+ ixor=[
+ lambda a, b: None],
+ le=[
+ lambda a, b: None],
+ length_hint=[
+ lambda obj: None,
+ lambda obj, default: None],
+ lshift=[
+ lambda a, b: None],
+ lt=[
+ lambda a, b: None],
+ matmul=[
+ lambda a, b: None],
+ methodcaller=[
+ lambda name, *args, **kwargs: None],
+ mod=[
+ lambda a, b: None],
+ mul=[
+ lambda a, b: None],
+ ne=[
+ lambda a, b: None],
+ neg=[
+ lambda a: None],
+ not_=[
+ lambda a: None],
+ or_=[
+ lambda a, b: None],
+ pos=[
+ lambda a: None],
+ pow=[
+ lambda a, b: None],
+ repeat=[
+ lambda a, b: None],
+ rshift=[
+ lambda a, b: None],
+ sequenceIncludes=[
+ lambda a, b: None],
+ setitem=[
+ lambda a, b, c: None],
+ setslice=[
+ lambda a, b, c, d: None],
+ sub=[
+ lambda a, b: None],
+ truediv=[
+ lambda a, b: None],
+ truth=[
+ lambda a: None],
+ xor=[
+ lambda a, b: None],
+)
+
+module_info['toolz'] = dict(
+ curry=[
+ (0, lambda *args, **kwargs: None)],
+ excepts=[
+ (0, lambda exc, func, handler=None: None)],
+ flip=[
+ (0, lambda func=None, a=None, b=None: None)],
+ juxt=[
+ (0, lambda *funcs: None)],
+ memoize=[
+ (0, lambda func=None, cache=None, key=None: None)],
+)
+
+module_info['toolz.functoolz'] = dict(
+ Compose=[
+ (0, lambda funcs: None)],
+ InstanceProperty=[
+ (0, lambda fget=None, fset=None, fdel=None, doc=None,
+ classval=None: None)],
+)
+
+
+def num_pos_args(sigspec):
+ """ Return the number of positional arguments. ``f(x, y=1)`` has 1"""
+ return sum(1 for x in sigspec.parameters.values()
+ if x.kind == x.POSITIONAL_OR_KEYWORD
+ and x.default is x.empty)
+
+
+def get_exclude_keywords(num_pos_only, sigspec):
+ """ Return the names of position-only arguments if func has **kwargs"""
+ if num_pos_only == 0:
+ return ()
+ has_kwargs = any(x.kind == x.VAR_KEYWORD
+ for x in sigspec.parameters.values())
+ if not has_kwargs:
+ return ()
+ pos_args = list(sigspec.parameters.values())[:num_pos_only]
+ return tuple(x.name for x in pos_args)
+
+
+def signature_or_spec(func):
+ try:
+ return inspect.signature(func)
+ except (ValueError, TypeError):
+ return None
+
+
+def expand_sig(sig):
+ """ Convert the signature spec in ``module_info`` to add to ``signatures``
+
+ The input signature spec is one of:
+ - ``lambda_func``
+ - ``(num_position_args, lambda_func)``
+ - ``(num_position_args, lambda_func, keyword_only_args)``
+
+ The output signature spec is:
+ ``(num_position_args, lambda_func, keyword_exclude, sigspec)``
+
+ where ``keyword_exclude`` includes keyword only arguments and, if variadic
+ keywords is present, the names of position-only argument. The latter is
+ included to support builtins such as ``partial(func, *args, **kwargs)``,
+ which allows ``func=`` to be used as a keyword even though it's the name
+ of a positional argument.
+ """
+ if isinstance(sig, tuple):
+ if len(sig) == 3:
+ num_pos_only, func, keyword_only = sig
+ assert isinstance(sig[-1], tuple)
+ else:
+ num_pos_only, func = sig
+ keyword_only = ()
+ sigspec = signature_or_spec(func)
+ else:
+ func = sig
+ sigspec = signature_or_spec(func)
+ num_pos_only = num_pos_args(sigspec)
+ keyword_only = ()
+ keyword_exclude = get_exclude_keywords(num_pos_only, sigspec)
+ return num_pos_only, func, keyword_only + keyword_exclude, sigspec
+
+
+signatures = {}
+
+
+def create_signature_registry(module_info=module_info, signatures=signatures):
+ for module, info in module_info.items():
+ if isinstance(module, str):
+ module = import_module(module)
+ for name, sigs in info.items():
+ if hasattr(module, name):
+ new_sigs = tuple(expand_sig(sig) for sig in sigs)
+ signatures[getattr(module, name)] = new_sigs
+
+
+def check_valid(sig, args, kwargs):
+ """ Like ``is_valid_args`` for the given signature spec"""
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ if len(args) < num_pos_only:
+ return False
+ if keyword_exclude:
+ kwargs = dict(kwargs)
+ for item in keyword_exclude:
+ kwargs.pop(item, None)
+ try:
+ func(*args, **kwargs)
+ return True
+ except TypeError:
+ return False
+
+
+def _is_valid_args(func, args, kwargs):
+ """ Like ``is_valid_args`` for builtins in our ``signatures`` registry"""
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ return any(check_valid(sig, args, kwargs) for sig in sigs)
+
+
+def check_partial(sig, args, kwargs):
+ """ Like ``is_partial_args`` for the given signature spec"""
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ if len(args) < num_pos_only:
+ pad = (None,) * (num_pos_only - len(args))
+ args = args + pad
+ if keyword_exclude:
+ kwargs = dict(kwargs)
+ for item in keyword_exclude:
+ kwargs.pop(item, None)
+ return is_partial_args(func, args, kwargs, sigspec)
+
+
+def _is_partial_args(func, args, kwargs):
+ """ Like ``is_partial_args`` for builtins in our ``signatures`` registry"""
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ return any(check_partial(sig, args, kwargs) for sig in sigs)
+
+
+def check_arity(n, sig):
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ if keyword_exclude or num_pos_only > n:
+ return False
+ return is_arity(n, func, sigspec)
+
+
+def _is_arity(n, func):
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ checks = [check_arity(n, sig) for sig in sigs]
+ if all(checks):
+ return True
+ elif any(checks):
+ return None
+ return False
+
+
+def check_varargs(sig):
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ return has_varargs(func, sigspec)
+
+
+def _has_varargs(func):
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ checks = [check_varargs(sig) for sig in sigs]
+ if all(checks):
+ return True
+ elif any(checks):
+ return None
+ return False
+
+
+def check_keywords(sig):
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ if keyword_exclude:
+ return True
+ return has_keywords(func, sigspec)
+
+
+def _has_keywords(func):
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ checks = [check_keywords(sig) for sig in sigs]
+ if all(checks):
+ return True
+ elif any(checks):
+ return None
+ return False
+
+
+def check_required_args(sig):
+ num_pos_only, func, keyword_exclude, sigspec = sig
+ return num_required_args(func, sigspec)
+
+
+def _num_required_args(func):
+ if func not in signatures:
+ return None
+ sigs = signatures[func]
+ vals = [check_required_args(sig) for sig in sigs]
+ val = vals[0]
+ if all(x == val for x in vals):
+ return val
+ return None
diff --git a/contrib/python/toolz/py3/toolz/_version.py b/contrib/python/toolz/py3/toolz/_version.py
new file mode 100644
index 0000000000..6e979d1048
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/_version.py
@@ -0,0 +1,21 @@
+
+# This file was generated by 'versioneer.py' (0.18) from
+# revision-control system data, or from the parent directory name of an
+# unpacked source archive. Distribution tarballs contain a pre-generated copy
+# of this file.
+
+import json
+
+version_json = '''
+{
+ "date": "2022-07-09T23:15:45-0500",
+ "dirty": false,
+ "error": null,
+ "full-revisionid": "245b78e6320c41a4a9cdd15c6123681fbfb62843",
+ "version": "0.12.0"
+}
+''' # END VERSION_JSON
+
+
+def get_versions():
+ return json.loads(version_json)
diff --git a/contrib/python/toolz/py3/toolz/compatibility.py b/contrib/python/toolz/py3/toolz/compatibility.py
new file mode 100644
index 0000000000..28bef91dc8
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/compatibility.py
@@ -0,0 +1,30 @@
+import warnings
+warnings.warn("The toolz.compatibility module is no longer "
+ "needed in Python 3 and has been deprecated. Please "
+ "import these utilities directly from the standard library. "
+ "This module will be removed in a future release.",
+ category=DeprecationWarning, stacklevel=2)
+
+import operator
+import sys
+
+PY3 = sys.version_info[0] > 2
+PY34 = sys.version_info[0] == 3 and sys.version_info[1] == 4
+PYPY = hasattr(sys, 'pypy_version_info') and PY3
+
+__all__ = ('map', 'filter', 'range', 'zip', 'reduce', 'zip_longest',
+ 'iteritems', 'iterkeys', 'itervalues', 'filterfalse',
+ 'PY3', 'PY34', 'PYPY')
+
+
+map = map
+filter = filter
+range = range
+zip = zip
+from functools import reduce
+from itertools import zip_longest
+from itertools import filterfalse
+iteritems = operator.methodcaller('items')
+iterkeys = operator.methodcaller('keys')
+itervalues = operator.methodcaller('values')
+from collections.abc import Sequence
diff --git a/contrib/python/toolz/py3/toolz/curried/__init__.py b/contrib/python/toolz/py3/toolz/curried/__init__.py
new file mode 100644
index 0000000000..356eddbd3b
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/curried/__init__.py
@@ -0,0 +1,103 @@
+"""
+Alternate namespace for toolz such that all functions are curried
+
+Currying provides implicit partial evaluation of all functions
+
+Example:
+
+ Get usually requires two arguments, an index and a collection
+ >>> from toolz.curried import get
+ >>> get(0, ('a', 'b'))
+ 'a'
+
+ When we use it in higher order functions we often want to pass a partially
+ evaluated form
+ >>> data = [(1, 2), (11, 22), (111, 222)]
+ >>> list(map(lambda seq: get(0, seq), data))
+ [1, 11, 111]
+
+ The curried version allows simple expression of partial evaluation
+ >>> list(map(get(0), data))
+ [1, 11, 111]
+
+See Also:
+ toolz.functoolz.curry
+"""
+import toolz
+from . import operator
+from toolz import (
+ apply,
+ comp,
+ complement,
+ compose,
+ compose_left,
+ concat,
+ concatv,
+ count,
+ curry,
+ diff,
+ first,
+ flip,
+ frequencies,
+ identity,
+ interleave,
+ isdistinct,
+ isiterable,
+ juxt,
+ last,
+ memoize,
+ merge_sorted,
+ peek,
+ pipe,
+ second,
+ thread_first,
+ thread_last,
+)
+from .exceptions import merge, merge_with
+
+accumulate = toolz.curry(toolz.accumulate)
+assoc = toolz.curry(toolz.assoc)
+assoc_in = toolz.curry(toolz.assoc_in)
+cons = toolz.curry(toolz.cons)
+countby = toolz.curry(toolz.countby)
+dissoc = toolz.curry(toolz.dissoc)
+do = toolz.curry(toolz.do)
+drop = toolz.curry(toolz.drop)
+excepts = toolz.curry(toolz.excepts)
+filter = toolz.curry(toolz.filter)
+get = toolz.curry(toolz.get)
+get_in = toolz.curry(toolz.get_in)
+groupby = toolz.curry(toolz.groupby)
+interpose = toolz.curry(toolz.interpose)
+itemfilter = toolz.curry(toolz.itemfilter)
+itemmap = toolz.curry(toolz.itemmap)
+iterate = toolz.curry(toolz.iterate)
+join = toolz.curry(toolz.join)
+keyfilter = toolz.curry(toolz.keyfilter)
+keymap = toolz.curry(toolz.keymap)
+map = toolz.curry(toolz.map)
+mapcat = toolz.curry(toolz.mapcat)
+nth = toolz.curry(toolz.nth)
+partial = toolz.curry(toolz.partial)
+partition = toolz.curry(toolz.partition)
+partition_all = toolz.curry(toolz.partition_all)
+partitionby = toolz.curry(toolz.partitionby)
+peekn = toolz.curry(toolz.peekn)
+pluck = toolz.curry(toolz.pluck)
+random_sample = toolz.curry(toolz.random_sample)
+reduce = toolz.curry(toolz.reduce)
+reduceby = toolz.curry(toolz.reduceby)
+remove = toolz.curry(toolz.remove)
+sliding_window = toolz.curry(toolz.sliding_window)
+sorted = toolz.curry(toolz.sorted)
+tail = toolz.curry(toolz.tail)
+take = toolz.curry(toolz.take)
+take_nth = toolz.curry(toolz.take_nth)
+topk = toolz.curry(toolz.topk)
+unique = toolz.curry(toolz.unique)
+update_in = toolz.curry(toolz.update_in)
+valfilter = toolz.curry(toolz.valfilter)
+valmap = toolz.curry(toolz.valmap)
+
+del exceptions
+del toolz
diff --git a/contrib/python/toolz/py3/toolz/curried/exceptions.py b/contrib/python/toolz/py3/toolz/curried/exceptions.py
new file mode 100644
index 0000000000..75a52bbbf2
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/curried/exceptions.py
@@ -0,0 +1,18 @@
+import toolz
+
+
+__all__ = ['merge_with', 'merge']
+
+
+@toolz.curry
+def merge_with(func, d, *dicts, **kwargs):
+ return toolz.merge_with(func, d, *dicts, **kwargs)
+
+
+@toolz.curry
+def merge(d, *dicts, **kwargs):
+ return toolz.merge(d, *dicts, **kwargs)
+
+
+merge_with.__doc__ = toolz.merge_with.__doc__
+merge.__doc__ = toolz.merge.__doc__
diff --git a/contrib/python/toolz/py3/toolz/curried/operator.py b/contrib/python/toolz/py3/toolz/curried/operator.py
new file mode 100644
index 0000000000..35979a6851
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/curried/operator.py
@@ -0,0 +1,22 @@
+from __future__ import absolute_import
+
+import operator
+
+from toolz.functoolz import curry
+
+
+# Tests will catch if/when this needs updated
+IGNORE = {
+ "__abs__", "__index__", "__inv__", "__invert__", "__neg__", "__not__",
+ "__pos__", "_abs", "abs", "attrgetter", "index", "inv", "invert",
+ "itemgetter", "neg", "not_", "pos", "truth"
+}
+locals().update(
+ {name: f if name in IGNORE else curry(f)
+ for name, f in vars(operator).items() if callable(f)}
+)
+
+# Clean up the namespace.
+del IGNORE
+del curry
+del operator
diff --git a/contrib/python/toolz/py3/toolz/dicttoolz.py b/contrib/python/toolz/py3/toolz/dicttoolz.py
new file mode 100644
index 0000000000..457bc26928
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/dicttoolz.py
@@ -0,0 +1,339 @@
+import operator
+import collections
+from functools import reduce
+from collections.abc import Mapping
+
+__all__ = ('merge', 'merge_with', 'valmap', 'keymap', 'itemmap',
+ 'valfilter', 'keyfilter', 'itemfilter',
+ 'assoc', 'dissoc', 'assoc_in', 'update_in', 'get_in')
+
+
+def _get_factory(f, kwargs):
+ factory = kwargs.pop('factory', dict)
+ if kwargs:
+ raise TypeError("{}() got an unexpected keyword argument "
+ "'{}'".format(f.__name__, kwargs.popitem()[0]))
+ return factory
+
+
+def merge(*dicts, **kwargs):
+ """ Merge a collection of dictionaries
+
+ >>> merge({1: 'one'}, {2: 'two'})
+ {1: 'one', 2: 'two'}
+
+ Later dictionaries have precedence
+
+ >>> merge({1: 2, 3: 4}, {3: 3, 4: 4})
+ {1: 2, 3: 3, 4: 4}
+
+ See Also:
+ merge_with
+ """
+ if len(dicts) == 1 and not isinstance(dicts[0], Mapping):
+ dicts = dicts[0]
+ factory = _get_factory(merge, kwargs)
+
+ rv = factory()
+ for d in dicts:
+ rv.update(d)
+ return rv
+
+
+def merge_with(func, *dicts, **kwargs):
+ """ Merge dictionaries and apply function to combined values
+
+ A key may occur in more than one dict, and all values mapped from the key
+ will be passed to the function as a list, such as func([val1, val2, ...]).
+
+ >>> merge_with(sum, {1: 1, 2: 2}, {1: 10, 2: 20})
+ {1: 11, 2: 22}
+
+ >>> merge_with(first, {1: 1, 2: 2}, {2: 20, 3: 30}) # doctest: +SKIP
+ {1: 1, 2: 2, 3: 30}
+
+ See Also:
+ merge
+ """
+ if len(dicts) == 1 and not isinstance(dicts[0], Mapping):
+ dicts = dicts[0]
+ factory = _get_factory(merge_with, kwargs)
+
+ values = collections.defaultdict(lambda: [].append)
+ for d in dicts:
+ for k, v in d.items():
+ values[k](v)
+
+ result = factory()
+ for k, v in values.items():
+ result[k] = func(v.__self__)
+ return result
+
+
+def valmap(func, d, factory=dict):
+ """ Apply function to values of dictionary
+
+ >>> bills = {"Alice": [20, 15, 30], "Bob": [10, 35]}
+ >>> valmap(sum, bills) # doctest: +SKIP
+ {'Alice': 65, 'Bob': 45}
+
+ See Also:
+ keymap
+ itemmap
+ """
+ rv = factory()
+ rv.update(zip(d.keys(), map(func, d.values())))
+ return rv
+
+
+def keymap(func, d, factory=dict):
+ """ Apply function to keys of dictionary
+
+ >>> bills = {"Alice": [20, 15, 30], "Bob": [10, 35]}
+ >>> keymap(str.lower, bills) # doctest: +SKIP
+ {'alice': [20, 15, 30], 'bob': [10, 35]}
+
+ See Also:
+ valmap
+ itemmap
+ """
+ rv = factory()
+ rv.update(zip(map(func, d.keys()), d.values()))
+ return rv
+
+
+def itemmap(func, d, factory=dict):
+ """ Apply function to items of dictionary
+
+ >>> accountids = {"Alice": 10, "Bob": 20}
+ >>> itemmap(reversed, accountids) # doctest: +SKIP
+ {10: "Alice", 20: "Bob"}
+
+ See Also:
+ keymap
+ valmap
+ """
+ rv = factory()
+ rv.update(map(func, d.items()))
+ return rv
+
+
+def valfilter(predicate, d, factory=dict):
+ """ Filter items in dictionary by value
+
+ >>> iseven = lambda x: x % 2 == 0
+ >>> d = {1: 2, 2: 3, 3: 4, 4: 5}
+ >>> valfilter(iseven, d)
+ {1: 2, 3: 4}
+
+ See Also:
+ keyfilter
+ itemfilter
+ valmap
+ """
+ rv = factory()
+ for k, v in d.items():
+ if predicate(v):
+ rv[k] = v
+ return rv
+
+
+def keyfilter(predicate, d, factory=dict):
+ """ Filter items in dictionary by key
+
+ >>> iseven = lambda x: x % 2 == 0
+ >>> d = {1: 2, 2: 3, 3: 4, 4: 5}
+ >>> keyfilter(iseven, d)
+ {2: 3, 4: 5}
+
+ See Also:
+ valfilter
+ itemfilter
+ keymap
+ """
+ rv = factory()
+ for k, v in d.items():
+ if predicate(k):
+ rv[k] = v
+ return rv
+
+
+def itemfilter(predicate, d, factory=dict):
+ """ Filter items in dictionary by item
+
+ >>> def isvalid(item):
+ ... k, v = item
+ ... return k % 2 == 0 and v < 4
+
+ >>> d = {1: 2, 2: 3, 3: 4, 4: 5}
+ >>> itemfilter(isvalid, d)
+ {2: 3}
+
+ See Also:
+ keyfilter
+ valfilter
+ itemmap
+ """
+ rv = factory()
+ for item in d.items():
+ if predicate(item):
+ k, v = item
+ rv[k] = v
+ return rv
+
+
+def assoc(d, key, value, factory=dict):
+ """ Return a new dict with new key value pair
+
+ New dict has d[key] set to value. Does not modify the initial dictionary.
+
+ >>> assoc({'x': 1}, 'x', 2)
+ {'x': 2}
+ >>> assoc({'x': 1}, 'y', 3) # doctest: +SKIP
+ {'x': 1, 'y': 3}
+ """
+ d2 = factory()
+ d2.update(d)
+ d2[key] = value
+ return d2
+
+
+def dissoc(d, *keys, **kwargs):
+ """ Return a new dict with the given key(s) removed.
+
+ New dict has d[key] deleted for each supplied key.
+ Does not modify the initial dictionary.
+
+ >>> dissoc({'x': 1, 'y': 2}, 'y')
+ {'x': 1}
+ >>> dissoc({'x': 1, 'y': 2}, 'y', 'x')
+ {}
+ >>> dissoc({'x': 1}, 'y') # Ignores missing keys
+ {'x': 1}
+ """
+ factory = _get_factory(dissoc, kwargs)
+ d2 = factory()
+
+ if len(keys) < len(d) * .6:
+ d2.update(d)
+ for key in keys:
+ if key in d2:
+ del d2[key]
+ else:
+ remaining = set(d)
+ remaining.difference_update(keys)
+ for k in remaining:
+ d2[k] = d[k]
+ return d2
+
+
+def assoc_in(d, keys, value, factory=dict):
+ """ Return a new dict with new, potentially nested, key value pair
+
+ >>> purchase = {'name': 'Alice',
+ ... 'order': {'items': ['Apple', 'Orange'],
+ ... 'costs': [0.50, 1.25]},
+ ... 'credit card': '5555-1234-1234-1234'}
+ >>> assoc_in(purchase, ['order', 'costs'], [0.25, 1.00]) # doctest: +SKIP
+ {'credit card': '5555-1234-1234-1234',
+ 'name': 'Alice',
+ 'order': {'costs': [0.25, 1.00], 'items': ['Apple', 'Orange']}}
+ """
+ return update_in(d, keys, lambda x: value, value, factory)
+
+
+def update_in(d, keys, func, default=None, factory=dict):
+ """ Update value in a (potentially) nested dictionary
+
+ inputs:
+ d - dictionary on which to operate
+ keys - list or tuple giving the location of the value to be changed in d
+ func - function to operate on that value
+
+ If keys == [k0,..,kX] and d[k0]..[kX] == v, update_in returns a copy of the
+ original dictionary with v replaced by func(v), but does not mutate the
+ original dictionary.
+
+ If k0 is not a key in d, update_in creates nested dictionaries to the depth
+ specified by the keys, with the innermost value set to func(default).
+
+ >>> inc = lambda x: x + 1
+ >>> update_in({'a': 0}, ['a'], inc)
+ {'a': 1}
+
+ >>> transaction = {'name': 'Alice',
+ ... 'purchase': {'items': ['Apple', 'Orange'],
+ ... 'costs': [0.50, 1.25]},
+ ... 'credit card': '5555-1234-1234-1234'}
+ >>> update_in(transaction, ['purchase', 'costs'], sum) # doctest: +SKIP
+ {'credit card': '5555-1234-1234-1234',
+ 'name': 'Alice',
+ 'purchase': {'costs': 1.75, 'items': ['Apple', 'Orange']}}
+
+ >>> # updating a value when k0 is not in d
+ >>> update_in({}, [1, 2, 3], str, default="bar")
+ {1: {2: {3: 'bar'}}}
+ >>> update_in({1: 'foo'}, [2, 3, 4], inc, 0)
+ {1: 'foo', 2: {3: {4: 1}}}
+ """
+ ks = iter(keys)
+ k = next(ks)
+
+ rv = inner = factory()
+ rv.update(d)
+
+ for key in ks:
+ if k in d:
+ d = d[k]
+ dtemp = factory()
+ dtemp.update(d)
+ else:
+ d = dtemp = factory()
+
+ inner[k] = inner = dtemp
+ k = key
+
+ if k in d:
+ inner[k] = func(d[k])
+ else:
+ inner[k] = func(default)
+ return rv
+
+
+def get_in(keys, coll, default=None, no_default=False):
+ """ Returns coll[i0][i1]...[iX] where [i0, i1, ..., iX]==keys.
+
+ If coll[i0][i1]...[iX] cannot be found, returns ``default``, unless
+ ``no_default`` is specified, then it raises KeyError or IndexError.
+
+ ``get_in`` is a generalization of ``operator.getitem`` for nested data
+ structures such as dictionaries and lists.
+
+ >>> transaction = {'name': 'Alice',
+ ... 'purchase': {'items': ['Apple', 'Orange'],
+ ... 'costs': [0.50, 1.25]},
+ ... 'credit card': '5555-1234-1234-1234'}
+ >>> get_in(['purchase', 'items', 0], transaction)
+ 'Apple'
+ >>> get_in(['name'], transaction)
+ 'Alice'
+ >>> get_in(['purchase', 'total'], transaction)
+ >>> get_in(['purchase', 'items', 'apple'], transaction)
+ >>> get_in(['purchase', 'items', 10], transaction)
+ >>> get_in(['purchase', 'total'], transaction, 0)
+ 0
+ >>> get_in(['y'], {}, no_default=True)
+ Traceback (most recent call last):
+ ...
+ KeyError: 'y'
+
+ See Also:
+ itertoolz.get
+ operator.getitem
+ """
+ try:
+ return reduce(operator.getitem, keys, coll)
+ except (KeyError, IndexError, TypeError):
+ if no_default:
+ raise
+ return default
diff --git a/contrib/python/toolz/py3/toolz/functoolz.py b/contrib/python/toolz/py3/toolz/functoolz.py
new file mode 100644
index 0000000000..2c75d3a42a
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/functoolz.py
@@ -0,0 +1,1048 @@
+from functools import reduce, partial
+import inspect
+import sys
+from operator import attrgetter, not_
+from importlib import import_module
+from types import MethodType
+
+from .utils import no_default
+
+PYPY = hasattr(sys, 'pypy_version_info') and sys.version_info[0] > 2
+
+
+__all__ = ('identity', 'apply', 'thread_first', 'thread_last', 'memoize',
+ 'compose', 'compose_left', 'pipe', 'complement', 'juxt', 'do',
+ 'curry', 'flip', 'excepts')
+
+PYPY = hasattr(sys, 'pypy_version_info')
+
+
+def identity(x):
+ """ Identity function. Return x
+
+ >>> identity(3)
+ 3
+ """
+ return x
+
+
+def apply(*func_and_args, **kwargs):
+ """ Applies a function and returns the results
+
+ >>> def double(x): return 2*x
+ >>> def inc(x): return x + 1
+ >>> apply(double, 5)
+ 10
+
+ >>> tuple(map(apply, [double, inc, double], [10, 500, 8000]))
+ (20, 501, 16000)
+ """
+ if not func_and_args:
+ raise TypeError('func argument is required')
+ func, args = func_and_args[0], func_and_args[1:]
+ return func(*args, **kwargs)
+
+
+def thread_first(val, *forms):
+ """ Thread value through a sequence of functions/forms
+
+ >>> def double(x): return 2*x
+ >>> def inc(x): return x + 1
+ >>> thread_first(1, inc, double)
+ 4
+
+ If the function expects more than one input you can specify those inputs
+ in a tuple. The value is used as the first input.
+
+ >>> def add(x, y): return x + y
+ >>> def pow(x, y): return x**y
+ >>> thread_first(1, (add, 4), (pow, 2)) # pow(add(1, 4), 2)
+ 25
+
+ So in general
+ thread_first(x, f, (g, y, z))
+ expands to
+ g(f(x), y, z)
+
+ See Also:
+ thread_last
+ """
+ def evalform_front(val, form):
+ if callable(form):
+ return form(val)
+ if isinstance(form, tuple):
+ func, args = form[0], form[1:]
+ args = (val,) + args
+ return func(*args)
+ return reduce(evalform_front, forms, val)
+
+
+def thread_last(val, *forms):
+ """ Thread value through a sequence of functions/forms
+
+ >>> def double(x): return 2*x
+ >>> def inc(x): return x + 1
+ >>> thread_last(1, inc, double)
+ 4
+
+ If the function expects more than one input you can specify those inputs
+ in a tuple. The value is used as the last input.
+
+ >>> def add(x, y): return x + y
+ >>> def pow(x, y): return x**y
+ >>> thread_last(1, (add, 4), (pow, 2)) # pow(2, add(4, 1))
+ 32
+
+ So in general
+ thread_last(x, f, (g, y, z))
+ expands to
+ g(y, z, f(x))
+
+ >>> def iseven(x):
+ ... return x % 2 == 0
+ >>> list(thread_last([1, 2, 3], (map, inc), (filter, iseven)))
+ [2, 4]
+
+ See Also:
+ thread_first
+ """
+ def evalform_back(val, form):
+ if callable(form):
+ return form(val)
+ if isinstance(form, tuple):
+ func, args = form[0], form[1:]
+ args = args + (val,)
+ return func(*args)
+ return reduce(evalform_back, forms, val)
+
+
+def instanceproperty(fget=None, fset=None, fdel=None, doc=None, classval=None):
+ """ Like @property, but returns ``classval`` when used as a class attribute
+
+ >>> class MyClass(object):
+ ... '''The class docstring'''
+ ... @instanceproperty(classval=__doc__)
+ ... def __doc__(self):
+ ... return 'An object docstring'
+ ... @instanceproperty
+ ... def val(self):
+ ... return 42
+ ...
+ >>> MyClass.__doc__
+ 'The class docstring'
+ >>> MyClass.val is None
+ True
+ >>> obj = MyClass()
+ >>> obj.__doc__
+ 'An object docstring'
+ >>> obj.val
+ 42
+ """
+ if fget is None:
+ return partial(instanceproperty, fset=fset, fdel=fdel, doc=doc,
+ classval=classval)
+ return InstanceProperty(fget=fget, fset=fset, fdel=fdel, doc=doc,
+ classval=classval)
+
+
+class InstanceProperty(property):
+ """ Like @property, but returns ``classval`` when used as a class attribute
+
+ Should not be used directly. Use ``instanceproperty`` instead.
+ """
+ def __init__(self, fget=None, fset=None, fdel=None, doc=None,
+ classval=None):
+ self.classval = classval
+ property.__init__(self, fget=fget, fset=fset, fdel=fdel, doc=doc)
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self.classval
+ return property.__get__(self, obj, type)
+
+ def __reduce__(self):
+ state = (self.fget, self.fset, self.fdel, self.__doc__, self.classval)
+ return InstanceProperty, state
+
+
+class curry(object):
+ """ Curry a callable function
+
+ Enables partial application of arguments through calling a function with an
+ incomplete set of arguments.
+
+ >>> def mul(x, y):
+ ... return x * y
+ >>> mul = curry(mul)
+
+ >>> double = mul(2)
+ >>> double(10)
+ 20
+
+ Also supports keyword arguments
+
+ >>> @curry # Can use curry as a decorator
+ ... def f(x, y, a=10):
+ ... return a * (x + y)
+
+ >>> add = f(a=1)
+ >>> add(2, 3)
+ 5
+
+ See Also:
+ toolz.curried - namespace of curried functions
+ https://toolz.readthedocs.io/en/latest/curry.html
+ """
+ def __init__(self, *args, **kwargs):
+ if not args:
+ raise TypeError('__init__() takes at least 2 arguments (1 given)')
+ func, args = args[0], args[1:]
+ if not callable(func):
+ raise TypeError("Input must be callable")
+
+ # curry- or functools.partial-like object? Unpack and merge arguments
+ if (
+ hasattr(func, 'func')
+ and hasattr(func, 'args')
+ and hasattr(func, 'keywords')
+ and isinstance(func.args, tuple)
+ ):
+ _kwargs = {}
+ if func.keywords:
+ _kwargs.update(func.keywords)
+ _kwargs.update(kwargs)
+ kwargs = _kwargs
+ args = func.args + args
+ func = func.func
+
+ if kwargs:
+ self._partial = partial(func, *args, **kwargs)
+ else:
+ self._partial = partial(func, *args)
+
+ self.__doc__ = getattr(func, '__doc__', None)
+ self.__name__ = getattr(func, '__name__', '<curry>')
+ self.__module__ = getattr(func, '__module__', None)
+ self.__qualname__ = getattr(func, '__qualname__', None)
+ self._sigspec = None
+ self._has_unknown_args = None
+
+ @instanceproperty
+ def func(self):
+ return self._partial.func
+
+ @instanceproperty
+ def __signature__(self):
+ sig = inspect.signature(self.func)
+ args = self.args or ()
+ keywords = self.keywords or {}
+ if is_partial_args(self.func, args, keywords, sig) is False:
+ raise TypeError('curry object has incorrect arguments')
+
+ params = list(sig.parameters.values())
+ skip = 0
+ for param in params[:len(args)]:
+ if param.kind == param.VAR_POSITIONAL:
+ break
+ skip += 1
+
+ kwonly = False
+ newparams = []
+ for param in params[skip:]:
+ kind = param.kind
+ default = param.default
+ if kind == param.VAR_KEYWORD:
+ pass
+ elif kind == param.VAR_POSITIONAL:
+ if kwonly:
+ continue
+ elif param.name in keywords:
+ default = keywords[param.name]
+ kind = param.KEYWORD_ONLY
+ kwonly = True
+ else:
+ if kwonly:
+ kind = param.KEYWORD_ONLY
+ if default is param.empty:
+ default = no_default
+ newparams.append(param.replace(default=default, kind=kind))
+
+ return sig.replace(parameters=newparams)
+
+ @instanceproperty
+ def args(self):
+ return self._partial.args
+
+ @instanceproperty
+ def keywords(self):
+ return self._partial.keywords
+
+ @instanceproperty
+ def func_name(self):
+ return self.__name__
+
+ def __str__(self):
+ return str(self.func)
+
+ def __repr__(self):
+ return repr(self.func)
+
+ def __hash__(self):
+ return hash((self.func, self.args,
+ frozenset(self.keywords.items()) if self.keywords
+ else None))
+
+ def __eq__(self, other):
+ return (isinstance(other, curry) and self.func == other.func and
+ self.args == other.args and self.keywords == other.keywords)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __call__(self, *args, **kwargs):
+ try:
+ return self._partial(*args, **kwargs)
+ except TypeError as exc:
+ if self._should_curry(args, kwargs, exc):
+ return self.bind(*args, **kwargs)
+ raise
+
+ def _should_curry(self, args, kwargs, exc=None):
+ func = self.func
+ args = self.args + args
+ if self.keywords:
+ kwargs = dict(self.keywords, **kwargs)
+ if self._sigspec is None:
+ sigspec = self._sigspec = _sigs.signature_or_spec(func)
+ self._has_unknown_args = has_varargs(func, sigspec) is not False
+ else:
+ sigspec = self._sigspec
+
+ if is_partial_args(func, args, kwargs, sigspec) is False:
+ # Nothing can make the call valid
+ return False
+ elif self._has_unknown_args:
+ # The call may be valid and raised a TypeError, but we curry
+ # anyway because the function may have `*args`. This is useful
+ # for decorators with signature `func(*args, **kwargs)`.
+ return True
+ elif not is_valid_args(func, args, kwargs, sigspec):
+ # Adding more arguments may make the call valid
+ return True
+ else:
+ # There was a genuine TypeError
+ return False
+
+ def bind(self, *args, **kwargs):
+ return type(self)(self, *args, **kwargs)
+
+ def call(self, *args, **kwargs):
+ return self._partial(*args, **kwargs)
+
+ def __get__(self, instance, owner):
+ if instance is None:
+ return self
+ return curry(self, instance)
+
+ def __reduce__(self):
+ func = self.func
+ modname = getattr(func, '__module__', None)
+ qualname = getattr(func, '__qualname__', None)
+ if qualname is None: # pragma: no cover
+ qualname = getattr(func, '__name__', None)
+ is_decorated = None
+ if modname and qualname:
+ attrs = []
+ obj = import_module(modname)
+ for attr in qualname.split('.'):
+ if isinstance(obj, curry):
+ attrs.append('func')
+ obj = obj.func
+ obj = getattr(obj, attr, None)
+ if obj is None:
+ break
+ attrs.append(attr)
+ if isinstance(obj, curry) and obj.func is func:
+ is_decorated = obj is self
+ qualname = '.'.join(attrs)
+ func = '%s:%s' % (modname, qualname)
+
+ # functools.partial objects can't be pickled
+ userdict = tuple((k, v) for k, v in self.__dict__.items()
+ if k not in ('_partial', '_sigspec'))
+ state = (type(self), func, self.args, self.keywords, userdict,
+ is_decorated)
+ return _restore_curry, state
+
+
+def _restore_curry(cls, func, args, kwargs, userdict, is_decorated):
+ if isinstance(func, str):
+ modname, qualname = func.rsplit(':', 1)
+ obj = import_module(modname)
+ for attr in qualname.split('.'):
+ obj = getattr(obj, attr)
+ if is_decorated:
+ return obj
+ func = obj.func
+ obj = cls(func, *args, **(kwargs or {}))
+ obj.__dict__.update(userdict)
+ return obj
+
+
+@curry
+def memoize(func, cache=None, key=None):
+ """ Cache a function's result for speedy future evaluation
+
+ Considerations:
+ Trades memory for speed.
+ Only use on pure functions.
+
+ >>> def add(x, y): return x + y
+ >>> add = memoize(add)
+
+ Or use as a decorator
+
+ >>> @memoize
+ ... def add(x, y):
+ ... return x + y
+
+ Use the ``cache`` keyword to provide a dict-like object as an initial cache
+
+ >>> @memoize(cache={(1, 2): 3})
+ ... def add(x, y):
+ ... return x + y
+
+ Note that the above works as a decorator because ``memoize`` is curried.
+
+ It is also possible to provide a ``key(args, kwargs)`` function that
+ calculates keys used for the cache, which receives an ``args`` tuple and
+ ``kwargs`` dict as input, and must return a hashable value. However,
+ the default key function should be sufficient most of the time.
+
+ >>> # Use key function that ignores extraneous keyword arguments
+ >>> @memoize(key=lambda args, kwargs: args)
+ ... def add(x, y, verbose=False):
+ ... if verbose:
+ ... print('Calculating %s + %s' % (x, y))
+ ... return x + y
+ """
+ if cache is None:
+ cache = {}
+
+ try:
+ may_have_kwargs = has_keywords(func) is not False
+ # Is unary function (single arg, no variadic argument or keywords)?
+ is_unary = is_arity(1, func)
+ except TypeError: # pragma: no cover
+ may_have_kwargs = True
+ is_unary = False
+
+ if key is None:
+ if is_unary:
+ def key(args, kwargs):
+ return args[0]
+ elif may_have_kwargs:
+ def key(args, kwargs):
+ return (
+ args or None,
+ frozenset(kwargs.items()) if kwargs else None,
+ )
+ else:
+ def key(args, kwargs):
+ return args
+
+ def memof(*args, **kwargs):
+ k = key(args, kwargs)
+ try:
+ return cache[k]
+ except TypeError:
+ raise TypeError("Arguments to memoized function must be hashable")
+ except KeyError:
+ cache[k] = result = func(*args, **kwargs)
+ return result
+
+ try:
+ memof.__name__ = func.__name__
+ except AttributeError:
+ pass
+ memof.__doc__ = func.__doc__
+ memof.__wrapped__ = func
+ return memof
+
+
+class Compose(object):
+ """ A composition of functions
+
+ See Also:
+ compose
+ """
+ __slots__ = 'first', 'funcs'
+
+ def __init__(self, funcs):
+ funcs = tuple(reversed(funcs))
+ self.first = funcs[0]
+ self.funcs = funcs[1:]
+
+ def __call__(self, *args, **kwargs):
+ ret = self.first(*args, **kwargs)
+ for f in self.funcs:
+ ret = f(ret)
+ return ret
+
+ def __getstate__(self):
+ return self.first, self.funcs
+
+ def __setstate__(self, state):
+ self.first, self.funcs = state
+
+ @instanceproperty(classval=__doc__)
+ def __doc__(self):
+ def composed_doc(*fs):
+ """Generate a docstring for the composition of fs.
+ """
+ if not fs:
+ # Argument name for the docstring.
+ return '*args, **kwargs'
+
+ return '{f}({g})'.format(f=fs[0].__name__, g=composed_doc(*fs[1:]))
+
+ try:
+ return (
+ 'lambda *args, **kwargs: ' +
+ composed_doc(*reversed((self.first,) + self.funcs))
+ )
+ except AttributeError:
+ # One of our callables does not have a `__name__`, whatever.
+ return 'A composition of functions'
+
+ @property
+ def __name__(self):
+ try:
+ return '_of_'.join(
+ (f.__name__ for f in reversed((self.first,) + self.funcs))
+ )
+ except AttributeError:
+ return type(self).__name__
+
+ def __repr__(self):
+ return '{.__class__.__name__}{!r}'.format(
+ self, tuple(reversed((self.first, ) + self.funcs)))
+
+ def __eq__(self, other):
+ if isinstance(other, Compose):
+ return other.first == self.first and other.funcs == self.funcs
+ return NotImplemented
+
+ def __ne__(self, other):
+ equality = self.__eq__(other)
+ return NotImplemented if equality is NotImplemented else not equality
+
+ def __hash__(self):
+ return hash(self.first) ^ hash(self.funcs)
+
+ # Mimic the descriptor behavior of python functions.
+ # i.e. let Compose be called as a method when bound to a class.
+ # adapted from
+ # docs.python.org/3/howto/descriptor.html#functions-and-methods
+ def __get__(self, obj, objtype=None):
+ return self if obj is None else MethodType(self, obj)
+
+ # introspection with Signature is only possible from py3.3+
+ @instanceproperty
+ def __signature__(self):
+ base = inspect.signature(self.first)
+ last = inspect.signature(self.funcs[-1])
+ return base.replace(return_annotation=last.return_annotation)
+
+ __wrapped__ = instanceproperty(attrgetter('first'))
+
+
+def compose(*funcs):
+ """ Compose functions to operate in series.
+
+ Returns a function that applies other functions in sequence.
+
+ Functions are applied from right to left so that
+ ``compose(f, g, h)(x, y)`` is the same as ``f(g(h(x, y)))``.
+
+ If no arguments are provided, the identity function (f(x) = x) is returned.
+
+ >>> inc = lambda i: i + 1
+ >>> compose(str, inc)(3)
+ '4'
+
+ See Also:
+ compose_left
+ pipe
+ """
+ if not funcs:
+ return identity
+ if len(funcs) == 1:
+ return funcs[0]
+ else:
+ return Compose(funcs)
+
+
+def compose_left(*funcs):
+ """ Compose functions to operate in series.
+
+ Returns a function that applies other functions in sequence.
+
+ Functions are applied from left to right so that
+ ``compose_left(f, g, h)(x, y)`` is the same as ``h(g(f(x, y)))``.
+
+ If no arguments are provided, the identity function (f(x) = x) is returned.
+
+ >>> inc = lambda i: i + 1
+ >>> compose_left(inc, str)(3)
+ '4'
+
+ See Also:
+ compose
+ pipe
+ """
+ return compose(*reversed(funcs))
+
+
+def pipe(data, *funcs):
+ """ Pipe a value through a sequence of functions
+
+ I.e. ``pipe(data, f, g, h)`` is equivalent to ``h(g(f(data)))``
+
+ We think of the value as progressing through a pipe of several
+ transformations, much like pipes in UNIX
+
+ ``$ cat data | f | g | h``
+
+ >>> double = lambda i: 2 * i
+ >>> pipe(3, double, str)
+ '6'
+
+ See Also:
+ compose
+ compose_left
+ thread_first
+ thread_last
+ """
+ for func in funcs:
+ data = func(data)
+ return data
+
+
+def complement(func):
+ """ Convert a predicate function to its logical complement.
+
+ In other words, return a function that, for inputs that normally
+ yield True, yields False, and vice-versa.
+
+ >>> def iseven(n): return n % 2 == 0
+ >>> isodd = complement(iseven)
+ >>> iseven(2)
+ True
+ >>> isodd(2)
+ False
+ """
+ return compose(not_, func)
+
+
+class juxt(object):
+ """ Creates a function that calls several functions with the same arguments
+
+ Takes several functions and returns a function that applies its arguments
+ to each of those functions then returns a tuple of the results.
+
+ Name comes from juxtaposition: the fact of two things being seen or placed
+ close together with contrasting effect.
+
+ >>> inc = lambda x: x + 1
+ >>> double = lambda x: x * 2
+ >>> juxt(inc, double)(10)
+ (11, 20)
+ >>> juxt([inc, double])(10)
+ (11, 20)
+ """
+ __slots__ = ['funcs']
+
+ def __init__(self, *funcs):
+ if len(funcs) == 1 and not callable(funcs[0]):
+ funcs = funcs[0]
+ self.funcs = tuple(funcs)
+
+ def __call__(self, *args, **kwargs):
+ return tuple(func(*args, **kwargs) for func in self.funcs)
+
+ def __getstate__(self):
+ return self.funcs
+
+ def __setstate__(self, state):
+ self.funcs = state
+
+
+def do(func, x):
+ """ Runs ``func`` on ``x``, returns ``x``
+
+ Because the results of ``func`` are not returned, only the side
+ effects of ``func`` are relevant.
+
+ Logging functions can be made by composing ``do`` with a storage function
+ like ``list.append`` or ``file.write``
+
+ >>> from toolz import compose
+ >>> from toolz.curried import do
+
+ >>> log = []
+ >>> inc = lambda x: x + 1
+ >>> inc = compose(inc, do(log.append))
+ >>> inc(1)
+ 2
+ >>> inc(11)
+ 12
+ >>> log
+ [1, 11]
+ """
+ func(x)
+ return x
+
+
+@curry
+def flip(func, a, b):
+ """ Call the function call with the arguments flipped
+
+ This function is curried.
+
+ >>> def div(a, b):
+ ... return a // b
+ ...
+ >>> flip(div, 2, 6)
+ 3
+ >>> div_by_two = flip(div, 2)
+ >>> div_by_two(4)
+ 2
+
+ This is particularly useful for built in functions and functions defined
+ in C extensions that accept positional only arguments. For example:
+ isinstance, issubclass.
+
+ >>> data = [1, 'a', 'b', 2, 1.5, object(), 3]
+ >>> only_ints = list(filter(flip(isinstance, int), data))
+ >>> only_ints
+ [1, 2, 3]
+ """
+ return func(b, a)
+
+
+def return_none(exc):
+ """ Returns None.
+ """
+ return None
+
+
+class excepts(object):
+ """A wrapper around a function to catch exceptions and
+ dispatch to a handler.
+
+ This is like a functional try/except block, in the same way that
+ ifexprs are functional if/else blocks.
+
+ Examples
+ --------
+ >>> excepting = excepts(
+ ... ValueError,
+ ... lambda a: [1, 2].index(a),
+ ... lambda _: -1,
+ ... )
+ >>> excepting(1)
+ 0
+ >>> excepting(3)
+ -1
+
+ Multiple exceptions and default except clause.
+ >>> excepting = excepts((IndexError, KeyError), lambda a: a[0])
+ >>> excepting([])
+ >>> excepting([1])
+ 1
+ >>> excepting({})
+ >>> excepting({0: 1})
+ 1
+ """
+ def __init__(self, exc, func, handler=return_none):
+ self.exc = exc
+ self.func = func
+ self.handler = handler
+
+ def __call__(self, *args, **kwargs):
+ try:
+ return self.func(*args, **kwargs)
+ except self.exc as e:
+ return self.handler(e)
+
+ @instanceproperty(classval=__doc__)
+ def __doc__(self):
+ from textwrap import dedent
+
+ exc = self.exc
+ try:
+ if isinstance(exc, tuple):
+ exc_name = '(%s)' % ', '.join(
+ map(attrgetter('__name__'), exc),
+ )
+ else:
+ exc_name = exc.__name__
+
+ return dedent(
+ """\
+ A wrapper around {inst.func.__name__!r} that will except:
+ {exc}
+ and handle any exceptions with {inst.handler.__name__!r}.
+
+ Docs for {inst.func.__name__!r}:
+ {inst.func.__doc__}
+
+ Docs for {inst.handler.__name__!r}:
+ {inst.handler.__doc__}
+ """
+ ).format(
+ inst=self,
+ exc=exc_name,
+ )
+ except AttributeError:
+ return type(self).__doc__
+
+ @property
+ def __name__(self):
+ exc = self.exc
+ try:
+ if isinstance(exc, tuple):
+ exc_name = '_or_'.join(map(attrgetter('__name__'), exc))
+ else:
+ exc_name = exc.__name__
+ return '%s_excepting_%s' % (self.func.__name__, exc_name)
+ except AttributeError:
+ return 'excepting'
+
+
+def _check_sigspec(sigspec, func, builtin_func, *builtin_args):
+ if sigspec is None:
+ try:
+ sigspec = inspect.signature(func)
+ except (ValueError, TypeError) as e:
+ sigspec = e
+ if isinstance(sigspec, ValueError):
+ return None, builtin_func(*builtin_args)
+ elif not isinstance(sigspec, inspect.Signature):
+ if (
+ func in _sigs.signatures
+ and ((
+ hasattr(func, '__signature__')
+ and hasattr(func.__signature__, '__get__')
+ ))
+ ):
+ val = builtin_func(*builtin_args)
+ return None, val
+ return None, False
+ return sigspec, None
+
+
+if PYPY: # pragma: no cover
+ _check_sigspec_orig = _check_sigspec
+
+ def _check_sigspec(sigspec, func, builtin_func, *builtin_args):
+ # PyPy may lie, so use our registry for builtins instead
+ if func in _sigs.signatures:
+ val = builtin_func(*builtin_args)
+ return None, val
+ return _check_sigspec_orig(sigspec, func, builtin_func, *builtin_args)
+
+
+_check_sigspec.__doc__ = """ \
+Private function to aid in introspection compatibly across Python versions.
+
+If a callable doesn't have a signature (Python 3) or an argspec (Python 2),
+the signature registry in toolz._signatures is used.
+"""
+
+
+def num_required_args(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args,
+ func)
+ if sigspec is None:
+ return rv
+ return sum(1 for p in sigspec.parameters.values()
+ if p.default is p.empty
+ and p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY))
+
+
+def has_varargs(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func)
+ if sigspec is None:
+ return rv
+ return any(p.kind == p.VAR_POSITIONAL
+ for p in sigspec.parameters.values())
+
+
+def has_keywords(func, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func)
+ if sigspec is None:
+ return rv
+ return any(p.default is not p.empty
+ or p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD)
+ for p in sigspec.parameters.values())
+
+
+def is_valid_args(func, args, kwargs, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args,
+ func, args, kwargs)
+ if sigspec is None:
+ return rv
+ try:
+ sigspec.bind(*args, **kwargs)
+ except TypeError:
+ return False
+ return True
+
+
+def is_partial_args(func, args, kwargs, sigspec=None):
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args,
+ func, args, kwargs)
+ if sigspec is None:
+ return rv
+ try:
+ sigspec.bind_partial(*args, **kwargs)
+ except TypeError:
+ return False
+ return True
+
+
+def is_arity(n, func, sigspec=None):
+ """ Does a function have only n positional arguments?
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def f(x):
+ ... return x
+ >>> is_arity(1, f)
+ True
+ >>> def g(x, y=1):
+ ... return x + y
+ >>> is_arity(1, g)
+ False
+ """
+ sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_arity, n, func)
+ if sigspec is None:
+ return rv
+ num = num_required_args(func, sigspec)
+ if num is not None:
+ num = num == n
+ if not num:
+ return False
+ varargs = has_varargs(func, sigspec)
+ if varargs:
+ return False
+ keywords = has_keywords(func, sigspec)
+ if keywords:
+ return False
+ if num is None or varargs is None or keywords is None: # pragma: no cover
+ return None
+ return True
+
+
+num_required_args.__doc__ = """ \
+Number of required positional arguments
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def f(x, y, z=3):
+ ... return x + y + z
+ >>> num_required_args(f)
+ 2
+ >>> def g(*args, **kwargs):
+ ... pass
+ >>> num_required_args(g)
+ 0
+ """
+
+has_varargs.__doc__ = """ \
+Does a function have variadic positional arguments?
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def f(*args):
+ ... return args
+ >>> has_varargs(f)
+ True
+ >>> def g(**kwargs):
+ ... return kwargs
+ >>> has_varargs(g)
+ False
+ """
+
+has_keywords.__doc__ = """ \
+Does a function have keyword arguments?
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def f(x, y=0):
+ ... return x + y
+
+ >>> has_keywords(f)
+ True
+ """
+
+is_valid_args.__doc__ = """ \
+Is ``func(*args, **kwargs)`` a valid function call?
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def add(x, y):
+ ... return x + y
+
+ >>> is_valid_args(add, (1,), {})
+ False
+ >>> is_valid_args(add, (1, 2), {})
+ True
+ >>> is_valid_args(map, (), {})
+ False
+
+ **Implementation notes**
+ Python 2 relies on ``inspect.getargspec``, which only works for
+ user-defined functions. Python 3 uses ``inspect.signature``, which
+ works for many more types of callables.
+
+ Many builtins in the standard library are also supported.
+ """
+
+is_partial_args.__doc__ = """ \
+Can partial(func, *args, **kwargs)(*args2, **kwargs2) be a valid call?
+
+ Returns True *only* if the call is valid or if it is possible for the
+ call to become valid by adding more positional or keyword arguments.
+
+ This function relies on introspection and does not call the function.
+ Returns None if validity can't be determined.
+
+ >>> def add(x, y):
+ ... return x + y
+
+ >>> is_partial_args(add, (1,), {})
+ True
+ >>> is_partial_args(add, (1, 2), {})
+ True
+ >>> is_partial_args(add, (1, 2, 3), {})
+ False
+ >>> is_partial_args(map, (), {})
+ True
+
+ **Implementation notes**
+ Python 2 relies on ``inspect.getargspec``, which only works for
+ user-defined functions. Python 3 uses ``inspect.signature``, which
+ works for many more types of callables.
+
+ Many builtins in the standard library are also supported.
+ """
+
+from . import _signatures as _sigs
diff --git a/contrib/python/toolz/py3/toolz/itertoolz.py b/contrib/python/toolz/py3/toolz/itertoolz.py
new file mode 100644
index 0000000000..5049e5eb4b
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/itertoolz.py
@@ -0,0 +1,1057 @@
+import itertools
+import heapq
+import collections
+import operator
+from functools import partial
+from itertools import filterfalse, zip_longest
+from collections.abc import Sequence
+from toolz.utils import no_default
+
+
+__all__ = ('remove', 'accumulate', 'groupby', 'merge_sorted', 'interleave',
+ 'unique', 'isiterable', 'isdistinct', 'take', 'drop', 'take_nth',
+ 'first', 'second', 'nth', 'last', 'get', 'concat', 'concatv',
+ 'mapcat', 'cons', 'interpose', 'frequencies', 'reduceby', 'iterate',
+ 'sliding_window', 'partition', 'partition_all', 'count', 'pluck',
+ 'join', 'tail', 'diff', 'topk', 'peek', 'peekn', 'random_sample')
+
+
+def remove(predicate, seq):
+ """ Return those items of sequence for which predicate(item) is False
+
+ >>> def iseven(x):
+ ... return x % 2 == 0
+ >>> list(remove(iseven, [1, 2, 3, 4]))
+ [1, 3]
+ """
+ return filterfalse(predicate, seq)
+
+
+def accumulate(binop, seq, initial=no_default):
+ """ Repeatedly apply binary function to a sequence, accumulating results
+
+ >>> from operator import add, mul
+ >>> list(accumulate(add, [1, 2, 3, 4, 5]))
+ [1, 3, 6, 10, 15]
+ >>> list(accumulate(mul, [1, 2, 3, 4, 5]))
+ [1, 2, 6, 24, 120]
+
+ Accumulate is similar to ``reduce`` and is good for making functions like
+ cumulative sum:
+
+ >>> from functools import partial, reduce
+ >>> sum = partial(reduce, add)
+ >>> cumsum = partial(accumulate, add)
+
+ Accumulate also takes an optional argument that will be used as the first
+ value. This is similar to reduce.
+
+ >>> list(accumulate(add, [1, 2, 3], -1))
+ [-1, 0, 2, 5]
+ >>> list(accumulate(add, [], 1))
+ [1]
+
+ See Also:
+ itertools.accumulate : In standard itertools for Python 3.2+
+ """
+ seq = iter(seq)
+ if initial == no_default:
+ try:
+ result = next(seq)
+ except StopIteration:
+ return
+ else:
+ result = initial
+ yield result
+ for elem in seq:
+ result = binop(result, elem)
+ yield result
+
+
+def groupby(key, seq):
+ """ Group a collection by a key function
+
+ >>> names = ['Alice', 'Bob', 'Charlie', 'Dan', 'Edith', 'Frank']
+ >>> groupby(len, names) # doctest: +SKIP
+ {3: ['Bob', 'Dan'], 5: ['Alice', 'Edith', 'Frank'], 7: ['Charlie']}
+
+ >>> iseven = lambda x: x % 2 == 0
+ >>> groupby(iseven, [1, 2, 3, 4, 5, 6, 7, 8]) # doctest: +SKIP
+ {False: [1, 3, 5, 7], True: [2, 4, 6, 8]}
+
+ Non-callable keys imply grouping on a member.
+
+ >>> groupby('gender', [{'name': 'Alice', 'gender': 'F'},
+ ... {'name': 'Bob', 'gender': 'M'},
+ ... {'name': 'Charlie', 'gender': 'M'}]) # doctest:+SKIP
+ {'F': [{'gender': 'F', 'name': 'Alice'}],
+ 'M': [{'gender': 'M', 'name': 'Bob'},
+ {'gender': 'M', 'name': 'Charlie'}]}
+
+ Not to be confused with ``itertools.groupby``
+
+ See Also:
+ countby
+ """
+ if not callable(key):
+ key = getter(key)
+ d = collections.defaultdict(lambda: [].append)
+ for item in seq:
+ d[key(item)](item)
+ rv = {}
+ for k, v in d.items():
+ rv[k] = v.__self__
+ return rv
+
+
+def merge_sorted(*seqs, **kwargs):
+ """ Merge and sort a collection of sorted collections
+
+ This works lazily and only keeps one value from each iterable in memory.
+
+ >>> list(merge_sorted([1, 3, 5], [2, 4, 6]))
+ [1, 2, 3, 4, 5, 6]
+
+ >>> ''.join(merge_sorted('abc', 'abc', 'abc'))
+ 'aaabbbccc'
+
+ The "key" function used to sort the input may be passed as a keyword.
+
+ >>> list(merge_sorted([2, 3], [1, 3], key=lambda x: x // 3))
+ [2, 1, 3, 3]
+ """
+ if len(seqs) == 0:
+ return iter([])
+ elif len(seqs) == 1:
+ return iter(seqs[0])
+
+ key = kwargs.get('key', None)
+ if key is None:
+ return _merge_sorted_binary(seqs)
+ else:
+ return _merge_sorted_binary_key(seqs, key)
+
+
+def _merge_sorted_binary(seqs):
+ mid = len(seqs) // 2
+ L1 = seqs[:mid]
+ if len(L1) == 1:
+ seq1 = iter(L1[0])
+ else:
+ seq1 = _merge_sorted_binary(L1)
+ L2 = seqs[mid:]
+ if len(L2) == 1:
+ seq2 = iter(L2[0])
+ else:
+ seq2 = _merge_sorted_binary(L2)
+
+ try:
+ val2 = next(seq2)
+ except StopIteration:
+ for val1 in seq1:
+ yield val1
+ return
+
+ for val1 in seq1:
+ if val2 < val1:
+ yield val2
+ for val2 in seq2:
+ if val2 < val1:
+ yield val2
+ else:
+ yield val1
+ break
+ else:
+ break
+ else:
+ yield val1
+ else:
+ yield val2
+ for val2 in seq2:
+ yield val2
+ return
+ yield val1
+ for val1 in seq1:
+ yield val1
+
+
+def _merge_sorted_binary_key(seqs, key):
+ mid = len(seqs) // 2
+ L1 = seqs[:mid]
+ if len(L1) == 1:
+ seq1 = iter(L1[0])
+ else:
+ seq1 = _merge_sorted_binary_key(L1, key)
+ L2 = seqs[mid:]
+ if len(L2) == 1:
+ seq2 = iter(L2[0])
+ else:
+ seq2 = _merge_sorted_binary_key(L2, key)
+
+ try:
+ val2 = next(seq2)
+ except StopIteration:
+ for val1 in seq1:
+ yield val1
+ return
+ key2 = key(val2)
+
+ for val1 in seq1:
+ key1 = key(val1)
+ if key2 < key1:
+ yield val2
+ for val2 in seq2:
+ key2 = key(val2)
+ if key2 < key1:
+ yield val2
+ else:
+ yield val1
+ break
+ else:
+ break
+ else:
+ yield val1
+ else:
+ yield val2
+ for val2 in seq2:
+ yield val2
+ return
+ yield val1
+ for val1 in seq1:
+ yield val1
+
+
+def interleave(seqs):
+ """ Interleave a sequence of sequences
+
+ >>> list(interleave([[1, 2], [3, 4]]))
+ [1, 3, 2, 4]
+
+ >>> ''.join(interleave(('ABC', 'XY')))
+ 'AXBYC'
+
+ Both the individual sequences and the sequence of sequences may be infinite
+
+ Returns a lazy iterator
+ """
+ iters = itertools.cycle(map(iter, seqs))
+ while True:
+ try:
+ for itr in iters:
+ yield next(itr)
+ return
+ except StopIteration:
+ predicate = partial(operator.is_not, itr)
+ iters = itertools.cycle(itertools.takewhile(predicate, iters))
+
+
+def unique(seq, key=None):
+ """ Return only unique elements of a sequence
+
+ >>> tuple(unique((1, 2, 3)))
+ (1, 2, 3)
+ >>> tuple(unique((1, 2, 1, 3)))
+ (1, 2, 3)
+
+ Uniqueness can be defined by key keyword
+
+ >>> tuple(unique(['cat', 'mouse', 'dog', 'hen'], key=len))
+ ('cat', 'mouse')
+ """
+ seen = set()
+ seen_add = seen.add
+ if key is None:
+ for item in seq:
+ if item not in seen:
+ seen_add(item)
+ yield item
+ else: # calculate key
+ for item in seq:
+ val = key(item)
+ if val not in seen:
+ seen_add(val)
+ yield item
+
+
+def isiterable(x):
+ """ Is x iterable?
+
+ >>> isiterable([1, 2, 3])
+ True
+ >>> isiterable('abc')
+ True
+ >>> isiterable(5)
+ False
+ """
+ try:
+ iter(x)
+ return True
+ except TypeError:
+ return False
+
+
+def isdistinct(seq):
+ """ All values in sequence are distinct
+
+ >>> isdistinct([1, 2, 3])
+ True
+ >>> isdistinct([1, 2, 1])
+ False
+
+ >>> isdistinct("Hello")
+ False
+ >>> isdistinct("World")
+ True
+ """
+ if iter(seq) is seq:
+ seen = set()
+ seen_add = seen.add
+ for item in seq:
+ if item in seen:
+ return False
+ seen_add(item)
+ return True
+ else:
+ return len(seq) == len(set(seq))
+
+
+def take(n, seq):
+ """ The first n elements of a sequence
+
+ >>> list(take(2, [10, 20, 30, 40, 50]))
+ [10, 20]
+
+ See Also:
+ drop
+ tail
+ """
+ return itertools.islice(seq, n)
+
+
+def tail(n, seq):
+ """ The last n elements of a sequence
+
+ >>> tail(2, [10, 20, 30, 40, 50])
+ [40, 50]
+
+ See Also:
+ drop
+ take
+ """
+ try:
+ return seq[-n:]
+ except (TypeError, KeyError):
+ return tuple(collections.deque(seq, n))
+
+
+def drop(n, seq):
+ """ The sequence following the first n elements
+
+ >>> list(drop(2, [10, 20, 30, 40, 50]))
+ [30, 40, 50]
+
+ See Also:
+ take
+ tail
+ """
+ return itertools.islice(seq, n, None)
+
+
+def take_nth(n, seq):
+ """ Every nth item in seq
+
+ >>> list(take_nth(2, [10, 20, 30, 40, 50]))
+ [10, 30, 50]
+ """
+ return itertools.islice(seq, 0, None, n)
+
+
+def first(seq):
+ """ The first element in a sequence
+
+ >>> first('ABC')
+ 'A'
+ """
+ return next(iter(seq))
+
+
+def second(seq):
+ """ The second element in a sequence
+
+ >>> second('ABC')
+ 'B'
+ """
+ seq = iter(seq)
+ next(seq)
+ return next(seq)
+
+
+def nth(n, seq):
+ """ The nth element in a sequence
+
+ >>> nth(1, 'ABC')
+ 'B'
+ """
+ if isinstance(seq, (tuple, list, Sequence)):
+ return seq[n]
+ else:
+ return next(itertools.islice(seq, n, None))
+
+
+def last(seq):
+ """ The last element in a sequence
+
+ >>> last('ABC')
+ 'C'
+ """
+ return tail(1, seq)[0]
+
+
+rest = partial(drop, 1)
+
+
+def _get(ind, seq, default):
+ try:
+ return seq[ind]
+ except (KeyError, IndexError):
+ return default
+
+
+def get(ind, seq, default=no_default):
+ """ Get element in a sequence or dict
+
+ Provides standard indexing
+
+ >>> get(1, 'ABC') # Same as 'ABC'[1]
+ 'B'
+
+ Pass a list to get multiple values
+
+ >>> get([1, 2], 'ABC') # ('ABC'[1], 'ABC'[2])
+ ('B', 'C')
+
+ Works on any value that supports indexing/getitem
+ For example here we see that it works with dictionaries
+
+ >>> phonebook = {'Alice': '555-1234',
+ ... 'Bob': '555-5678',
+ ... 'Charlie':'555-9999'}
+ >>> get('Alice', phonebook)
+ '555-1234'
+
+ >>> get(['Alice', 'Bob'], phonebook)
+ ('555-1234', '555-5678')
+
+ Provide a default for missing values
+
+ >>> get(['Alice', 'Dennis'], phonebook, None)
+ ('555-1234', None)
+
+ See Also:
+ pluck
+ """
+ try:
+ return seq[ind]
+ except TypeError: # `ind` may be a list
+ if isinstance(ind, list):
+ if default == no_default:
+ if len(ind) > 1:
+ return operator.itemgetter(*ind)(seq)
+ elif ind:
+ return seq[ind[0]],
+ else:
+ return ()
+ else:
+ return tuple(_get(i, seq, default) for i in ind)
+ elif default != no_default:
+ return default
+ else:
+ raise
+ except (KeyError, IndexError): # we know `ind` is not a list
+ if default == no_default:
+ raise
+ else:
+ return default
+
+
+def concat(seqs):
+ """ Concatenate zero or more iterables, any of which may be infinite.
+
+ An infinite sequence will prevent the rest of the arguments from
+ being included.
+
+ We use chain.from_iterable rather than ``chain(*seqs)`` so that seqs
+ can be a generator.
+
+ >>> list(concat([[], [1], [2, 3]]))
+ [1, 2, 3]
+
+ See also:
+ itertools.chain.from_iterable equivalent
+ """
+ return itertools.chain.from_iterable(seqs)
+
+
+def concatv(*seqs):
+ """ Variadic version of concat
+
+ >>> list(concatv([], ["a"], ["b", "c"]))
+ ['a', 'b', 'c']
+
+ See also:
+ itertools.chain
+ """
+ return concat(seqs)
+
+
+def mapcat(func, seqs):
+ """ Apply func to each sequence in seqs, concatenating results.
+
+ >>> list(mapcat(lambda s: [c.upper() for c in s],
+ ... [["a", "b"], ["c", "d", "e"]]))
+ ['A', 'B', 'C', 'D', 'E']
+ """
+ return concat(map(func, seqs))
+
+
+def cons(el, seq):
+ """ Add el to beginning of (possibly infinite) sequence seq.
+
+ >>> list(cons(1, [2, 3]))
+ [1, 2, 3]
+ """
+ return itertools.chain([el], seq)
+
+
+def interpose(el, seq):
+ """ Introduce element between each pair of elements in seq
+
+ >>> list(interpose("a", [1, 2, 3]))
+ [1, 'a', 2, 'a', 3]
+ """
+ inposed = concat(zip(itertools.repeat(el), seq))
+ next(inposed)
+ return inposed
+
+
+def frequencies(seq):
+ """ Find number of occurrences of each value in seq
+
+ >>> frequencies(['cat', 'cat', 'ox', 'pig', 'pig', 'cat']) #doctest: +SKIP
+ {'cat': 3, 'ox': 1, 'pig': 2}
+
+ See Also:
+ countby
+ groupby
+ """
+ d = collections.defaultdict(int)
+ for item in seq:
+ d[item] += 1
+ return dict(d)
+
+
+def reduceby(key, binop, seq, init=no_default):
+ """ Perform a simultaneous groupby and reduction
+
+ The computation:
+
+ >>> result = reduceby(key, binop, seq, init) # doctest: +SKIP
+
+ is equivalent to the following:
+
+ >>> def reduction(group): # doctest: +SKIP
+ ... return reduce(binop, group, init) # doctest: +SKIP
+
+ >>> groups = groupby(key, seq) # doctest: +SKIP
+ >>> result = valmap(reduction, groups) # doctest: +SKIP
+
+ But the former does not build the intermediate groups, allowing it to
+ operate in much less space. This makes it suitable for larger datasets
+ that do not fit comfortably in memory
+
+ The ``init`` keyword argument is the default initialization of the
+ reduction. This can be either a constant value like ``0`` or a callable
+ like ``lambda : 0`` as might be used in ``defaultdict``.
+
+ Simple Examples
+ ---------------
+
+ >>> from operator import add, mul
+ >>> iseven = lambda x: x % 2 == 0
+
+ >>> data = [1, 2, 3, 4, 5]
+
+ >>> reduceby(iseven, add, data) # doctest: +SKIP
+ {False: 9, True: 6}
+
+ >>> reduceby(iseven, mul, data) # doctest: +SKIP
+ {False: 15, True: 8}
+
+ Complex Example
+ ---------------
+
+ >>> projects = [{'name': 'build roads', 'state': 'CA', 'cost': 1000000},
+ ... {'name': 'fight crime', 'state': 'IL', 'cost': 100000},
+ ... {'name': 'help farmers', 'state': 'IL', 'cost': 2000000},
+ ... {'name': 'help farmers', 'state': 'CA', 'cost': 200000}]
+
+ >>> reduceby('state', # doctest: +SKIP
+ ... lambda acc, x: acc + x['cost'],
+ ... projects, 0)
+ {'CA': 1200000, 'IL': 2100000}
+
+ Example Using ``init``
+ ----------------------
+
+ >>> def set_add(s, i):
+ ... s.add(i)
+ ... return s
+
+ >>> reduceby(iseven, set_add, [1, 2, 3, 4, 1, 2, 3], set) # doctest: +SKIP
+ {True: set([2, 4]),
+ False: set([1, 3])}
+ """
+ is_no_default = init == no_default
+ if not is_no_default and not callable(init):
+ _init = init
+ init = lambda: _init
+ if not callable(key):
+ key = getter(key)
+ d = {}
+ for item in seq:
+ k = key(item)
+ if k not in d:
+ if is_no_default:
+ d[k] = item
+ continue
+ else:
+ d[k] = init()
+ d[k] = binop(d[k], item)
+ return d
+
+
+def iterate(func, x):
+ """ Repeatedly apply a function func onto an original input
+
+ Yields x, then func(x), then func(func(x)), then func(func(func(x))), etc..
+
+ >>> def inc(x): return x + 1
+ >>> counter = iterate(inc, 0)
+ >>> next(counter)
+ 0
+ >>> next(counter)
+ 1
+ >>> next(counter)
+ 2
+
+ >>> double = lambda x: x * 2
+ >>> powers_of_two = iterate(double, 1)
+ >>> next(powers_of_two)
+ 1
+ >>> next(powers_of_two)
+ 2
+ >>> next(powers_of_two)
+ 4
+ >>> next(powers_of_two)
+ 8
+ """
+ while True:
+ yield x
+ x = func(x)
+
+
+def sliding_window(n, seq):
+ """ A sequence of overlapping subsequences
+
+ >>> list(sliding_window(2, [1, 2, 3, 4]))
+ [(1, 2), (2, 3), (3, 4)]
+
+ This function creates a sliding window suitable for transformations like
+ sliding means / smoothing
+
+ >>> mean = lambda seq: float(sum(seq)) / len(seq)
+ >>> list(map(mean, sliding_window(2, [1, 2, 3, 4])))
+ [1.5, 2.5, 3.5]
+ """
+ return zip(*(collections.deque(itertools.islice(it, i), 0) or it
+ for i, it in enumerate(itertools.tee(seq, n))))
+
+
+no_pad = '__no__pad__'
+
+
+def partition(n, seq, pad=no_pad):
+ """ Partition sequence into tuples of length n
+
+ >>> list(partition(2, [1, 2, 3, 4]))
+ [(1, 2), (3, 4)]
+
+ If the length of ``seq`` is not evenly divisible by ``n``, the final tuple
+ is dropped if ``pad`` is not specified, or filled to length ``n`` by pad:
+
+ >>> list(partition(2, [1, 2, 3, 4, 5]))
+ [(1, 2), (3, 4)]
+
+ >>> list(partition(2, [1, 2, 3, 4, 5], pad=None))
+ [(1, 2), (3, 4), (5, None)]
+
+ See Also:
+ partition_all
+ """
+ args = [iter(seq)] * n
+ if pad is no_pad:
+ return zip(*args)
+ else:
+ return zip_longest(*args, fillvalue=pad)
+
+
+def partition_all(n, seq):
+ """ Partition all elements of sequence into tuples of length at most n
+
+ The final tuple may be shorter to accommodate extra elements.
+
+ >>> list(partition_all(2, [1, 2, 3, 4]))
+ [(1, 2), (3, 4)]
+
+ >>> list(partition_all(2, [1, 2, 3, 4, 5]))
+ [(1, 2), (3, 4), (5,)]
+
+ See Also:
+ partition
+ """
+ args = [iter(seq)] * n
+ it = zip_longest(*args, fillvalue=no_pad)
+ try:
+ prev = next(it)
+ except StopIteration:
+ return
+ for item in it:
+ yield prev
+ prev = item
+ if prev[-1] is no_pad:
+ try:
+ # If seq defines __len__, then
+ # we can quickly calculate where no_pad starts
+ yield prev[:len(seq) % n]
+ except TypeError:
+ # Get first index of no_pad without using .index()
+ # https://github.com/pytoolz/toolz/issues/387
+ # Binary search from CPython's bisect module,
+ # modified for identity testing.
+ lo, hi = 0, n
+ while lo < hi:
+ mid = (lo + hi) // 2
+ if prev[mid] is no_pad:
+ hi = mid
+ else:
+ lo = mid + 1
+ yield prev[:lo]
+ else:
+ yield prev
+
+
+def count(seq):
+ """ Count the number of items in seq
+
+ Like the builtin ``len`` but works on lazy sequences.
+
+ Not to be confused with ``itertools.count``
+
+ See also:
+ len
+ """
+ if hasattr(seq, '__len__'):
+ return len(seq)
+ return sum(1 for i in seq)
+
+
+def pluck(ind, seqs, default=no_default):
+ """ plucks an element or several elements from each item in a sequence.
+
+ ``pluck`` maps ``itertoolz.get`` over a sequence and returns one or more
+ elements of each item in the sequence.
+
+ This is equivalent to running `map(curried.get(ind), seqs)`
+
+ ``ind`` can be either a single string/index or a list of strings/indices.
+ ``seqs`` should be sequence containing sequences or dicts.
+
+ e.g.
+
+ >>> data = [{'id': 1, 'name': 'Cheese'}, {'id': 2, 'name': 'Pies'}]
+ >>> list(pluck('name', data))
+ ['Cheese', 'Pies']
+ >>> list(pluck([0, 1], [[1, 2, 3], [4, 5, 7]]))
+ [(1, 2), (4, 5)]
+
+ See Also:
+ get
+ map
+ """
+ if default == no_default:
+ get = getter(ind)
+ return map(get, seqs)
+ elif isinstance(ind, list):
+ return (tuple(_get(item, seq, default) for item in ind)
+ for seq in seqs)
+ return (_get(ind, seq, default) for seq in seqs)
+
+
+def getter(index):
+ if isinstance(index, list):
+ if len(index) == 1:
+ index = index[0]
+ return lambda x: (x[index],)
+ elif index:
+ return operator.itemgetter(*index)
+ else:
+ return lambda x: ()
+ else:
+ return operator.itemgetter(index)
+
+
+def join(leftkey, leftseq, rightkey, rightseq,
+ left_default=no_default, right_default=no_default):
+ """ Join two sequences on common attributes
+
+ This is a semi-streaming operation. The LEFT sequence is fully evaluated
+ and placed into memory. The RIGHT sequence is evaluated lazily and so can
+ be arbitrarily large.
+ (Note: If right_default is defined, then unique keys of rightseq
+ will also be stored in memory.)
+
+ >>> friends = [('Alice', 'Edith'),
+ ... ('Alice', 'Zhao'),
+ ... ('Edith', 'Alice'),
+ ... ('Zhao', 'Alice'),
+ ... ('Zhao', 'Edith')]
+
+ >>> cities = [('Alice', 'NYC'),
+ ... ('Alice', 'Chicago'),
+ ... ('Dan', 'Syndey'),
+ ... ('Edith', 'Paris'),
+ ... ('Edith', 'Berlin'),
+ ... ('Zhao', 'Shanghai')]
+
+ >>> # Vacation opportunities
+ >>> # In what cities do people have friends?
+ >>> result = join(second, friends,
+ ... first, cities)
+ >>> for ((a, b), (c, d)) in sorted(unique(result)):
+ ... print((a, d))
+ ('Alice', 'Berlin')
+ ('Alice', 'Paris')
+ ('Alice', 'Shanghai')
+ ('Edith', 'Chicago')
+ ('Edith', 'NYC')
+ ('Zhao', 'Chicago')
+ ('Zhao', 'NYC')
+ ('Zhao', 'Berlin')
+ ('Zhao', 'Paris')
+
+ Specify outer joins with keyword arguments ``left_default`` and/or
+ ``right_default``. Here is a full outer join in which unmatched elements
+ are paired with None.
+
+ >>> identity = lambda x: x
+ >>> list(join(identity, [1, 2, 3],
+ ... identity, [2, 3, 4],
+ ... left_default=None, right_default=None))
+ [(2, 2), (3, 3), (None, 4), (1, None)]
+
+ Usually the key arguments are callables to be applied to the sequences. If
+ the keys are not obviously callable then it is assumed that indexing was
+ intended, e.g. the following is a legal change.
+ The join is implemented as a hash join and the keys of leftseq must be
+ hashable. Additionally, if right_default is defined, then keys of rightseq
+ must also be hashable.
+
+ >>> # result = join(second, friends, first, cities)
+ >>> result = join(1, friends, 0, cities) # doctest: +SKIP
+ """
+ if not callable(leftkey):
+ leftkey = getter(leftkey)
+ if not callable(rightkey):
+ rightkey = getter(rightkey)
+
+ d = groupby(leftkey, leftseq)
+
+ if left_default == no_default and right_default == no_default:
+ # Inner Join
+ for item in rightseq:
+ key = rightkey(item)
+ if key in d:
+ for left_match in d[key]:
+ yield (left_match, item)
+ elif left_default != no_default and right_default == no_default:
+ # Right Join
+ for item in rightseq:
+ key = rightkey(item)
+ if key in d:
+ for left_match in d[key]:
+ yield (left_match, item)
+ else:
+ yield (left_default, item)
+ elif right_default != no_default:
+ seen_keys = set()
+ seen = seen_keys.add
+
+ if left_default == no_default:
+ # Left Join
+ for item in rightseq:
+ key = rightkey(item)
+ seen(key)
+ if key in d:
+ for left_match in d[key]:
+ yield (left_match, item)
+ else:
+ # Full Join
+ for item in rightseq:
+ key = rightkey(item)
+ seen(key)
+ if key in d:
+ for left_match in d[key]:
+ yield (left_match, item)
+ else:
+ yield (left_default, item)
+
+ for key, matches in d.items():
+ if key not in seen_keys:
+ for match in matches:
+ yield (match, right_default)
+
+
+def diff(*seqs, **kwargs):
+ """ Return those items that differ between sequences
+
+ >>> list(diff([1, 2, 3], [1, 2, 10, 100]))
+ [(3, 10)]
+
+ Shorter sequences may be padded with a ``default`` value:
+
+ >>> list(diff([1, 2, 3], [1, 2, 10, 100], default=None))
+ [(3, 10), (None, 100)]
+
+ A ``key`` function may also be applied to each item to use during
+ comparisons:
+
+ >>> list(diff(['apples', 'bananas'], ['Apples', 'Oranges'], key=str.lower))
+ [('bananas', 'Oranges')]
+ """
+ N = len(seqs)
+ if N == 1 and isinstance(seqs[0], list):
+ seqs = seqs[0]
+ N = len(seqs)
+ if N < 2:
+ raise TypeError('Too few sequences given (min 2 required)')
+ default = kwargs.get('default', no_default)
+ if default == no_default:
+ iters = zip(*seqs)
+ else:
+ iters = zip_longest(*seqs, fillvalue=default)
+ key = kwargs.get('key', None)
+ if key is None:
+ for items in iters:
+ if items.count(items[0]) != N:
+ yield items
+ else:
+ for items in iters:
+ vals = tuple(map(key, items))
+ if vals.count(vals[0]) != N:
+ yield items
+
+
+def topk(k, seq, key=None):
+ """ Find the k largest elements of a sequence
+
+ Operates lazily in ``n*log(k)`` time
+
+ >>> topk(2, [1, 100, 10, 1000])
+ (1000, 100)
+
+ Use a key function to change sorted order
+
+ >>> topk(2, ['Alice', 'Bob', 'Charlie', 'Dan'], key=len)
+ ('Charlie', 'Alice')
+
+ See also:
+ heapq.nlargest
+ """
+ if key is not None and not callable(key):
+ key = getter(key)
+ return tuple(heapq.nlargest(k, seq, key=key))
+
+
+def peek(seq):
+ """ Retrieve the next element of a sequence
+
+ Returns the first element and an iterable equivalent to the original
+ sequence, still having the element retrieved.
+
+ >>> seq = [0, 1, 2, 3, 4]
+ >>> first, seq = peek(seq)
+ >>> first
+ 0
+ >>> list(seq)
+ [0, 1, 2, 3, 4]
+ """
+ iterator = iter(seq)
+ item = next(iterator)
+ return item, itertools.chain((item,), iterator)
+
+
+def peekn(n, seq):
+ """ Retrieve the next n elements of a sequence
+
+ Returns a tuple of the first n elements and an iterable equivalent
+ to the original, still having the elements retrieved.
+
+ >>> seq = [0, 1, 2, 3, 4]
+ >>> first_two, seq = peekn(2, seq)
+ >>> first_two
+ (0, 1)
+ >>> list(seq)
+ [0, 1, 2, 3, 4]
+ """
+ iterator = iter(seq)
+ peeked = tuple(take(n, iterator))
+ return peeked, itertools.chain(iter(peeked), iterator)
+
+
+def random_sample(prob, seq, random_state=None):
+ """ Return elements from a sequence with probability of prob
+
+ Returns a lazy iterator of random items from seq.
+
+ ``random_sample`` considers each item independently and without
+ replacement. See below how the first time it returned 13 items and the
+ next time it returned 6 items.
+
+ >>> seq = list(range(100))
+ >>> list(random_sample(0.1, seq)) # doctest: +SKIP
+ [6, 9, 19, 35, 45, 50, 58, 62, 68, 72, 78, 86, 95]
+ >>> list(random_sample(0.1, seq)) # doctest: +SKIP
+ [6, 44, 54, 61, 69, 94]
+
+ Providing an integer seed for ``random_state`` will result in
+ deterministic sampling. Given the same seed it will return the same sample
+ every time.
+
+ >>> list(random_sample(0.1, seq, random_state=2016))
+ [7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
+ >>> list(random_sample(0.1, seq, random_state=2016))
+ [7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
+
+ ``random_state`` can also be any object with a method ``random`` that
+ returns floats between 0.0 and 1.0 (exclusive).
+
+ >>> from random import Random
+ >>> randobj = Random(2016)
+ >>> list(random_sample(0.1, seq, random_state=randobj))
+ [7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
+ """
+ if not hasattr(random_state, 'random'):
+ from random import Random
+
+ random_state = Random(random_state)
+ return filter(lambda _: random_state.random() < prob, seq)
diff --git a/contrib/python/toolz/py3/toolz/recipes.py b/contrib/python/toolz/py3/toolz/recipes.py
new file mode 100644
index 0000000000..89de88db2b
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/recipes.py
@@ -0,0 +1,46 @@
+import itertools
+from .itertoolz import frequencies, pluck, getter
+
+
+__all__ = ('countby', 'partitionby')
+
+
+def countby(key, seq):
+ """ Count elements of a collection by a key function
+
+ >>> countby(len, ['cat', 'mouse', 'dog'])
+ {3: 2, 5: 1}
+
+ >>> def iseven(x): return x % 2 == 0
+ >>> countby(iseven, [1, 2, 3]) # doctest:+SKIP
+ {True: 1, False: 2}
+
+ See Also:
+ groupby
+ """
+ if not callable(key):
+ key = getter(key)
+ return frequencies(map(key, seq))
+
+
+def partitionby(func, seq):
+ """ Partition a sequence according to a function
+
+ Partition `s` into a sequence of lists such that, when traversing
+ `s`, every time the output of `func` changes a new list is started
+ and that and subsequent items are collected into that list.
+
+ >>> is_space = lambda c: c == " "
+ >>> list(partitionby(is_space, "I have space"))
+ [('I',), (' ',), ('h', 'a', 'v', 'e'), (' ',), ('s', 'p', 'a', 'c', 'e')]
+
+ >>> is_large = lambda x: x > 10
+ >>> list(partitionby(is_large, [1, 2, 1, 99, 88, 33, 99, -1, 5]))
+ [(1, 2, 1), (99, 88, 33, 99), (-1, 5)]
+
+ See also:
+ partition
+ groupby
+ itertools.groupby
+ """
+ return map(tuple, pluck(1, itertools.groupby(seq, key=func)))
diff --git a/contrib/python/toolz/py3/toolz/sandbox/__init__.py b/contrib/python/toolz/py3/toolz/sandbox/__init__.py
new file mode 100644
index 0000000000..0abda1cb42
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/sandbox/__init__.py
@@ -0,0 +1,2 @@
+from .core import EqualityHashKey, unzip
+from .parallel import fold
diff --git a/contrib/python/toolz/py3/toolz/sandbox/core.py b/contrib/python/toolz/py3/toolz/sandbox/core.py
new file mode 100644
index 0000000000..55e09d74e9
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/sandbox/core.py
@@ -0,0 +1,133 @@
+from toolz.itertoolz import getter, cons, pluck
+from itertools import tee, starmap
+
+
+# See #166: https://github.com/pytoolz/toolz/issues/166
+# See #173: https://github.com/pytoolz/toolz/pull/173
+class EqualityHashKey(object):
+ """ Create a hash key that uses equality comparisons between items.
+
+ This may be used to create hash keys for otherwise unhashable types:
+
+ >>> from toolz import curry
+ >>> EqualityHashDefault = curry(EqualityHashKey, None)
+ >>> set(map(EqualityHashDefault, [[], (), [1], [1]])) # doctest: +SKIP
+ {=[]=, =()=, =[1]=}
+
+ **Caution:** adding N ``EqualityHashKey`` items to a hash container
+ may require O(N**2) operations, not O(N) as for typical hashable types.
+ Therefore, a suitable key function such as ``tuple`` or ``frozenset``
+ is usually preferred over using ``EqualityHashKey`` if possible.
+
+ The ``key`` argument to ``EqualityHashKey`` should be a function or
+ index that returns a hashable object that effectively distinguishes
+ unequal items. This helps avoid the poor scaling that occurs when
+ using the default key. For example, the above example can be improved
+ by using a key function that distinguishes items by length or type:
+
+ >>> EqualityHashLen = curry(EqualityHashKey, len)
+ >>> EqualityHashType = curry(EqualityHashKey, type) # this works too
+ >>> set(map(EqualityHashLen, [[], (), [1], [1]])) # doctest: +SKIP
+ {=[]=, =()=, =[1]=}
+
+ ``EqualityHashKey`` is convenient to use when a suitable key function
+ is complicated or unavailable. For example, the following returns all
+ unique values based on equality:
+
+ >>> from toolz import unique
+ >>> vals = [[], [], (), [1], [1], [2], {}, {}, {}]
+ >>> list(unique(vals, key=EqualityHashDefault))
+ [[], (), [1], [2], {}]
+
+ **Warning:** don't change the equality value of an item already in a hash
+ container. Unhashable types are unhashable for a reason. For example:
+
+ >>> L1 = [1] ; L2 = [2]
+ >>> s = set(map(EqualityHashDefault, [L1, L2]))
+ >>> s # doctest: +SKIP
+ {=[1]=, =[2]=}
+
+ >>> L1[0] = 2 # Don't do this! ``s`` now has duplicate items!
+ >>> s # doctest: +SKIP
+ {=[2]=, =[2]=}
+
+ Although this may appear problematic, immutable data types is a common
+ idiom in functional programming, and``EqualityHashKey`` easily allows
+ the same idiom to be used by convention rather than strict requirement.
+
+ See Also:
+ identity
+ """
+ __slots__ = ['item', 'key']
+ _default_hashkey = '__default__hashkey__'
+
+ def __init__(self, key, item):
+ if key is None:
+ self.key = self._default_hashkey
+ elif not callable(key):
+ self.key = getter(key)
+ else:
+ self.key = key
+ self.item = item
+
+ def __hash__(self):
+ if self.key == self._default_hashkey:
+ val = self.key
+ else:
+ val = self.key(self.item)
+ return hash(val)
+
+ def __eq__(self, other):
+ try:
+ return (self._default_hashkey == other._default_hashkey and
+ self.item == other.item)
+ except AttributeError:
+ return False
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __str__(self):
+ return '=%s=' % str(self.item)
+
+ def __repr__(self):
+ return '=%s=' % repr(self.item)
+
+
+# See issue #293: https://github.com/pytoolz/toolz/issues/239
+def unzip(seq):
+ """Inverse of ``zip``
+
+ >>> a, b = unzip([('a', 1), ('b', 2)])
+ >>> list(a)
+ ['a', 'b']
+ >>> list(b)
+ [1, 2]
+
+ Unlike the naive implementation ``def unzip(seq): zip(*seq)`` this
+ implementation can handle an infinite sequence ``seq``.
+
+ Caveats:
+
+ * The implementation uses ``tee``, and so can use a significant amount
+ of auxiliary storage if the resulting iterators are consumed at
+ different times.
+
+ * The inner sequence cannot be infinite. In Python 3 ``zip(*seq)`` can be
+ used if ``seq`` is a finite sequence of infinite sequences.
+
+ """
+
+ seq = iter(seq)
+
+ # Check how many iterators we need
+ try:
+ first = tuple(next(seq))
+ except StopIteration:
+ return tuple()
+
+ # and create them
+ niters = len(first)
+ seqs = tee(cons(first, seq), niters)
+
+ return tuple(starmap(pluck, enumerate(seqs)))
diff --git a/contrib/python/toolz/py3/toolz/sandbox/parallel.py b/contrib/python/toolz/py3/toolz/sandbox/parallel.py
new file mode 100644
index 0000000000..114077d2ba
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/sandbox/parallel.py
@@ -0,0 +1,75 @@
+import functools
+from toolz.itertoolz import partition_all
+from toolz.utils import no_default
+
+
+def _reduce(func, seq, initial=None):
+ if initial is None:
+ return functools.reduce(func, seq)
+ else:
+ return functools.reduce(func, seq, initial)
+
+
+def fold(binop, seq, default=no_default, map=map, chunksize=128, combine=None):
+ """
+ Reduce without guarantee of ordered reduction.
+
+ inputs:
+
+ ``binop`` - associative operator. The associative property allows us to
+ leverage a parallel map to perform reductions in parallel.
+ ``seq`` - a sequence to be aggregated
+ ``default`` - an identity element like 0 for ``add`` or 1 for mul
+
+ ``map`` - an implementation of ``map``. This may be parallel and
+ determines how work is distributed.
+ ``chunksize`` - Number of elements of ``seq`` that should be handled
+ within a single function call
+ ``combine`` - Binary operator to combine two intermediate results.
+ If ``binop`` is of type (total, item) -> total
+ then ``combine`` is of type (total, total) -> total
+ Defaults to ``binop`` for common case of operators like add
+
+ Fold chunks up the collection into blocks of size ``chunksize`` and then
+ feeds each of these to calls to ``reduce``. This work is distributed
+ with a call to ``map``, gathered back and then refolded to finish the
+ computation. In this way ``fold`` specifies only how to chunk up data but
+ leaves the distribution of this work to an externally provided ``map``
+ function. This function can be sequential or rely on multithreading,
+ multiprocessing, or even distributed solutions.
+
+ If ``map`` intends to serialize functions it should be prepared to accept
+ and serialize lambdas. Note that the standard ``pickle`` module fails
+ here.
+
+ Example
+ -------
+
+ >>> # Provide a parallel map to accomplish a parallel sum
+ >>> from operator import add
+ >>> fold(add, [1, 2, 3, 4], chunksize=2, map=map)
+ 10
+ """
+ assert chunksize > 1
+
+ if combine is None:
+ combine = binop
+
+ chunks = partition_all(chunksize, seq)
+
+ # Evaluate sequence in chunks via map
+ if default == no_default:
+ results = map(
+ functools.partial(_reduce, binop),
+ chunks)
+ else:
+ results = map(
+ functools.partial(_reduce, binop, initial=default),
+ chunks)
+
+ results = list(results) # TODO: Support complete laziness
+
+ if len(results) == 1: # Return completed result
+ return results[0]
+ else: # Recurse to reaggregate intermediate results
+ return fold(combine, results, map=map, chunksize=chunksize)
diff --git a/contrib/python/toolz/py3/toolz/utils.py b/contrib/python/toolz/py3/toolz/utils.py
new file mode 100644
index 0000000000..1002c4649f
--- /dev/null
+++ b/contrib/python/toolz/py3/toolz/utils.py
@@ -0,0 +1,9 @@
+def raises(err, lamda):
+ try:
+ lamda()
+ return False
+ except err:
+ return True
+
+
+no_default = '__no__default__'
diff --git a/contrib/python/toolz/py3/ya.make b/contrib/python/toolz/py3/ya.make
new file mode 100644
index 0000000000..bc99c48e44
--- /dev/null
+++ b/contrib/python/toolz/py3/ya.make
@@ -0,0 +1,42 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(0.12.0)
+
+LICENSE(BSD-3-Clause)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ tlz/__init__.py
+ tlz/_build_tlz.py
+ toolz/__init__.py
+ toolz/_signatures.py
+ toolz/_version.py
+ toolz/compatibility.py
+ toolz/curried/__init__.py
+ toolz/curried/exceptions.py
+ toolz/curried/operator.py
+ toolz/dicttoolz.py
+ toolz/functoolz.py
+ toolz/itertoolz.py
+ toolz/recipes.py
+ toolz/sandbox/__init__.py
+ toolz/sandbox/core.py
+ toolz/sandbox/parallel.py
+ toolz/utils.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/toolz/py3/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+)
+
+END()
+
+RECURSE_FOR_TESTS(
+ tests
+)
diff --git a/contrib/python/toolz/ya.make b/contrib/python/toolz/ya.make
new file mode 100644
index 0000000000..0c05eedeac
--- /dev/null
+++ b/contrib/python/toolz/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/toolz/py2)
+ELSE()
+ PEERDIR(contrib/python/toolz/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/python/ujson/py2/lib/ultrajson.h b/contrib/python/ujson/py2/lib/ultrajson.h
new file mode 100644
index 0000000000..92902b4910
--- /dev/null
+++ b/contrib/python/ujson/py2/lib/ultrajson.h
@@ -0,0 +1,333 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+/*
+Ultra fast JSON encoder and decoder
+Developed by Jonas Tarnstrom (jonas@esn.me).
+
+Encoder notes:
+------------------
+
+:: Cyclic references ::
+Cyclic referenced objects are not detected.
+Set JSONObjectEncoder.recursionMax to suitable value or make sure input object
+tree doesn't have cyclic references.
+
+*/
+
+#ifndef __ULTRAJSON_H__
+#define __ULTRAJSON_H__
+
+#include <stdio.h>
+#include <wchar.h>
+
+// Don't output any extra whitespaces when encoding
+#define JSON_NO_EXTRA_WHITESPACE
+
+// Max decimals to encode double floating point numbers with
+#ifndef JSON_DOUBLE_MAX_DECIMALS
+#define JSON_DOUBLE_MAX_DECIMALS 15
+#endif
+
+// Max recursion depth, default for encoder
+#ifndef JSON_MAX_RECURSION_DEPTH
+#define JSON_MAX_RECURSION_DEPTH 1024
+#endif
+
+// Max recursion depth, default for decoder
+#ifndef JSON_MAX_OBJECT_DEPTH
+#define JSON_MAX_OBJECT_DEPTH 1024
+#endif
+
+/*
+Dictates and limits how much stack space for buffers UltraJSON will use before resorting to provided heap functions */
+#ifndef JSON_MAX_STACK_BUFFER_SIZE
+#define JSON_MAX_STACK_BUFFER_SIZE 131072
+#endif
+
+#ifdef _WIN32
+
+typedef __int64 JSINT64;
+typedef unsigned __int64 JSUINT64;
+
+typedef __int32 JSINT32;
+typedef unsigned __int32 JSUINT32;
+typedef unsigned __int8 JSUINT8;
+typedef unsigned __int16 JSUTF16;
+typedef unsigned __int32 JSUTF32;
+typedef __int64 JSLONG;
+
+#define EXPORTFUNCTION __declspec(dllexport)
+
+#define FASTCALL_MSVC __fastcall
+#define FASTCALL_ATTR
+#define INLINE_PREFIX __inline
+
+#else
+
+#include <stdint.h>
+typedef int64_t JSINT64;
+typedef uint64_t JSUINT64;
+
+typedef int32_t JSINT32;
+typedef uint32_t JSUINT32;
+
+#define FASTCALL_MSVC
+
+#if !defined __x86_64__
+#define FASTCALL_ATTR __attribute__((fastcall))
+#else
+#define FASTCALL_ATTR
+#endif
+
+
+#if defined(__clang__)
+#define INLINE_PREFIX inline __attribute__((always_inline))
+#else
+#define INLINE_PREFIX static inline
+#endif
+
+typedef uint8_t JSUINT8;
+typedef uint16_t JSUTF16;
+typedef uint32_t JSUTF32;
+
+typedef int64_t JSLONG;
+
+#define EXPORTFUNCTION
+#endif
+
+#if !(defined(__LITTLE_ENDIAN__) || defined(__BIG_ENDIAN__))
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define __LITTLE_ENDIAN__
+#else
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define __BIG_ENDIAN__
+#endif
+
+#endif
+
+#endif
+
+#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
+#error "Endianess not supported"
+#endif
+
+enum JSTYPES
+{
+ JT_NULL, // NULL
+ JT_TRUE, // boolean true
+ JT_FALSE, // boolean false
+ JT_INT, // (JSINT32 (signed 32-bit))
+ JT_LONG, // (JSINT64 (signed 64-bit))
+ JT_ULONG, // (JSUINT64 (unsigned 64-bit))
+ JT_DOUBLE, // (double)
+ JT_UTF8, // (char 8-bit)
+ JT_RAW, // (raw char 8-bit)
+ JT_ARRAY, // Array structure
+ JT_OBJECT, // Key/Value structure
+ JT_INVALID, // Internal, do not return nor expect
+};
+
+typedef void * JSOBJ;
+typedef void * JSITER;
+
+typedef struct __JSONTypeContext
+{
+ int type;
+ void *prv;
+ void *encoder_prv;
+} JSONTypeContext;
+
+/*
+Function pointer declarations, suitable for implementing UltraJSON */
+typedef int (*JSPFN_ITERNEXT)(JSOBJ obj, JSONTypeContext *tc);
+typedef void (*JSPFN_ITEREND)(JSOBJ obj, JSONTypeContext *tc);
+typedef JSOBJ (*JSPFN_ITERGETVALUE)(JSOBJ obj, JSONTypeContext *tc);
+typedef char *(*JSPFN_ITERGETNAME)(JSOBJ obj, JSONTypeContext *tc, size_t *outLen);
+typedef void *(*JSPFN_MALLOC)(size_t size);
+typedef void (*JSPFN_FREE)(void *pptr);
+typedef void *(*JSPFN_REALLOC)(void *base, size_t size);
+
+
+struct __JSONObjectEncoder;
+
+typedef struct __JSONObjectEncoder
+{
+ void (*beginTypeContext)(JSOBJ obj, JSONTypeContext *tc, struct __JSONObjectEncoder *enc);
+ void (*endTypeContext)(JSOBJ obj, JSONTypeContext *tc);
+ const char *(*getStringValue)(JSOBJ obj, JSONTypeContext *tc, size_t *_outLen);
+ JSINT64 (*getLongValue)(JSOBJ obj, JSONTypeContext *tc);
+ JSUINT64 (*getUnsignedLongValue)(JSOBJ obj, JSONTypeContext *tc);
+ JSINT32 (*getIntValue)(JSOBJ obj, JSONTypeContext *tc);
+ double (*getDoubleValue)(JSOBJ obj, JSONTypeContext *tc);
+
+ /*
+ Retrieve next object in an iteration. Should return 0 to indicate iteration has reached end or 1 if there are more items.
+ Implementor is responsible for keeping state of the iteration. Use ti->prv fields for this
+ */
+ JSPFN_ITERNEXT iterNext;
+
+ /*
+ Ends the iteration of an iteratable object.
+ Any iteration state stored in ti->prv can be freed here
+ */
+ JSPFN_ITEREND iterEnd;
+
+ /*
+ Returns a reference to the value object of an iterator
+ The is responsible for the life-cycle of the returned string. Use iterNext/iterEnd and ti->prv to keep track of current object
+ */
+ JSPFN_ITERGETVALUE iterGetValue;
+
+ /*
+ Return name of iterator.
+ The is responsible for the life-cycle of the returned string. Use iterNext/iterEnd and ti->prv to keep track of current object
+ */
+ JSPFN_ITERGETNAME iterGetName;
+
+ /*
+ Release a value as indicated by setting ti->release = 1 in the previous getValue call.
+ The ti->prv array should contain the necessary context to release the value
+ */
+ void (*releaseObject)(JSOBJ obj);
+
+ /* Library functions
+ Set to NULL to use STDLIB malloc,realloc,free */
+ JSPFN_MALLOC malloc;
+ JSPFN_REALLOC realloc;
+ JSPFN_FREE free;
+
+ /*
+ Configuration for max recursion, set to 0 to use default (see JSON_MAX_RECURSION_DEPTH)*/
+ int recursionMax;
+
+ /*
+ Configuration for max decimals of double floating point numbers to encode (0-9) */
+ int doublePrecision;
+
+ /*
+ If true output will be ASCII with all characters above 127 encoded as \uXXXX. If false output will be UTF-8 or what ever charset strings are brought as */
+ int forceASCII;
+
+ /*
+ If true, '<', '>', and '&' characters will be encoded as \u003c, \u003e, and \u0026, respectively. If false, no special encoding will be used. */
+ int encodeHTMLChars;
+
+ /*
+ If true, '/' will be encoded as \/. If false, no escaping. */
+ int escapeForwardSlashes;
+
+ /*
+ If true, dictionaries are iterated through in sorted key order. */
+ int sortKeys;
+
+ /*
+ Configuration for spaces of indent */
+ int indent;
+
+ /*
+ Private pointer to be used by the caller. Passed as encoder_prv in JSONTypeContext */
+ void *prv;
+
+ /*
+ Set to an error message if error occured */
+ const char *errorMsg;
+ JSOBJ errorObj;
+
+ /* Buffer stuff */
+ char *start;
+ char *offset;
+ char *end;
+ int heap;
+ int level;
+
+} JSONObjectEncoder;
+
+
+/*
+Encode an object structure into JSON.
+
+Arguments:
+obj - An anonymous type representing the object
+enc - Function definitions for querying JSOBJ type
+buffer - Preallocated buffer to store result in. If NULL function allocates own buffer
+cbBuffer - Length of buffer (ignored if buffer is NULL)
+
+Returns:
+Encoded JSON object as a null terminated char string.
+
+NOTE:
+If the supplied buffer wasn't enough to hold the result the function will allocate a new buffer.
+Life cycle of the provided buffer must still be handled by caller.
+
+If the return value doesn't equal the specified buffer caller must release the memory using
+JSONObjectEncoder.free or free() as specified when calling this function.
+*/
+EXPORTFUNCTION char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *enc, char *buffer, size_t cbBuffer);
+
+
+
+typedef struct __JSONObjectDecoder
+{
+ JSOBJ (*newString)(void *prv, wchar_t *start, wchar_t *end);
+ void (*objectAddKey)(void *prv, JSOBJ obj, JSOBJ name, JSOBJ value);
+ void (*arrayAddItem)(void *prv, JSOBJ obj, JSOBJ value);
+ JSOBJ (*newTrue)(void *prv);
+ JSOBJ (*newFalse)(void *prv);
+ JSOBJ (*newNull)(void *prv);
+ JSOBJ (*newObject)(void *prv);
+ JSOBJ (*newArray)(void *prv);
+ JSOBJ (*newInt)(void *prv, JSINT32 value);
+ JSOBJ (*newLong)(void *prv, JSINT64 value);
+ JSOBJ (*newUnsignedLong)(void *prv, JSUINT64 value);
+ JSOBJ (*newDouble)(void *prv, double value);
+ void (*releaseObject)(void *prv, JSOBJ obj);
+ JSPFN_MALLOC malloc;
+ JSPFN_FREE free;
+ JSPFN_REALLOC realloc;
+ char *errorStr;
+ char *errorOffset;
+ int preciseFloat;
+ void *prv;
+} JSONObjectDecoder;
+
+EXPORTFUNCTION JSOBJ JSON_DecodeObject(JSONObjectDecoder *dec, const char *buffer, size_t cbBuffer);
+
+#endif
diff --git a/contrib/python/ujson/py2/lib/ultrajsondec.c b/contrib/python/ujson/py2/lib/ultrajsondec.c
new file mode 100644
index 0000000000..21a732eceb
--- /dev/null
+++ b/contrib/python/ujson/py2/lib/ultrajsondec.c
@@ -0,0 +1,907 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+* Copyright (c) 1988-1993 The Regents of the University of California.
+* Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "ultrajson.h"
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include <limits.h>
+#include <wchar.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+
+struct DecoderState
+{
+ char *start;
+ char *end;
+ wchar_t *escStart;
+ wchar_t *escEnd;
+ int escHeap;
+ int lastType;
+ JSUINT32 objDepth;
+ void *prv;
+ JSONObjectDecoder *dec;
+};
+
+JSOBJ FASTCALL_MSVC decode_any( struct DecoderState *ds) FASTCALL_ATTR;
+typedef JSOBJ (*PFN_DECODER)( struct DecoderState *ds);
+
+static JSOBJ SetError( struct DecoderState *ds, int offset, const char *message)
+{
+ ds->dec->errorOffset = ds->start + offset;
+ ds->dec->errorStr = (char *) message;
+ return NULL;
+}
+
+double createDouble(double intNeg, double intValue, double frcValue, int frcDecimalCount)
+{
+ static const double g_pow10[] = {1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001,0.0000001, 0.00000001, 0.000000001, 0.0000000001, 0.00000000001, 0.000000000001, 0.0000000000001, 0.00000000000001, 0.000000000000001};
+ return (intValue + (frcValue * g_pow10[frcDecimalCount])) * intNeg;
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decodePreciseFloat(struct DecoderState *ds)
+{
+ char *end;
+ double value;
+ errno = 0;
+
+ value = strtod(ds->start, &end);
+
+ if (errno == ERANGE)
+ {
+ return SetError(ds, -1, "Range error when decoding numeric as double");
+ }
+
+ ds->start = end;
+ return ds->dec->newDouble(ds->prv, value);
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_numeric (struct DecoderState *ds)
+{
+ int intNeg = 1;
+ int mantSize = 0;
+ JSUINT64 intValue;
+ JSUINT64 prevIntValue;
+ int chr;
+ int decimalCount = 0;
+ double frcValue = 0.0;
+ double expNeg;
+ double expValue;
+ char *offset = ds->start;
+
+ JSUINT64 overflowLimit = LLONG_MAX;
+
+ if (*(offset) == '-')
+ {
+ offset ++;
+ intNeg = -1;
+ overflowLimit = LLONG_MIN;
+ }
+
+ // Scan integer part
+ intValue = 0;
+
+ while (1)
+ {
+ chr = (int) (unsigned char) *(offset);
+
+ switch (chr)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ //PERF: Don't do 64-bit arithmetic here unless we know we have to
+ prevIntValue = intValue;
+ intValue = intValue * 10ULL + (JSLONG) (chr - 48);
+
+ if (intNeg == 1 && prevIntValue > intValue)
+ {
+ return SetError(ds, -1, "Value is too big!");
+ }
+ else if (intNeg == -1 && intValue > overflowLimit)
+ {
+ return SetError(ds, -1, overflowLimit == LLONG_MAX ? "Value is too big!" : "Value is too small");
+ }
+
+ offset ++;
+ mantSize ++;
+ break;
+ }
+ case '.':
+ {
+ offset ++;
+ goto DECODE_FRACTION;
+ break;
+ }
+ case 'e':
+ case 'E':
+ {
+ offset ++;
+ goto DECODE_EXPONENT;
+ break;
+ }
+
+ default:
+ {
+ goto BREAK_INT_LOOP;
+ break;
+ }
+ }
+ }
+
+BREAK_INT_LOOP:
+
+ ds->lastType = JT_INT;
+ ds->start = offset;
+
+ if (intNeg == 1 && (intValue & 0x8000000000000000ULL) != 0)
+ {
+ return ds->dec->newUnsignedLong(ds->prv, intValue);
+ }
+ else if ((intValue >> 31))
+ {
+ return ds->dec->newLong(ds->prv, (JSINT64) (intValue * (JSINT64) intNeg));
+ }
+ else
+ {
+ return ds->dec->newInt(ds->prv, (JSINT32) (intValue * intNeg));
+ }
+
+DECODE_FRACTION:
+
+ if (ds->dec->preciseFloat)
+ {
+ return decodePreciseFloat(ds);
+ }
+
+ // Scan fraction part
+ frcValue = 0.0;
+ for (;;)
+ {
+ chr = (int) (unsigned char) *(offset);
+
+ switch (chr)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ if (decimalCount < JSON_DOUBLE_MAX_DECIMALS)
+ {
+ frcValue = frcValue * 10.0 + (double) (chr - 48);
+ decimalCount ++;
+ }
+ offset ++;
+ break;
+ }
+ case 'e':
+ case 'E':
+ {
+ offset ++;
+ goto DECODE_EXPONENT;
+ break;
+ }
+ default:
+ {
+ goto BREAK_FRC_LOOP;
+ }
+ }
+ }
+
+BREAK_FRC_LOOP:
+ //FIXME: Check for arithemtic overflow here
+ ds->lastType = JT_DOUBLE;
+ ds->start = offset;
+ return ds->dec->newDouble (ds->prv, createDouble( (double) intNeg, (double) intValue, frcValue, decimalCount));
+
+DECODE_EXPONENT:
+ if (ds->dec->preciseFloat)
+ {
+ return decodePreciseFloat(ds);
+ }
+
+ expNeg = 1.0;
+
+ if (*(offset) == '-')
+ {
+ expNeg = -1.0;
+ offset ++;
+ }
+ else
+ if (*(offset) == '+')
+ {
+ expNeg = +1.0;
+ offset ++;
+ }
+
+ expValue = 0.0;
+
+ for (;;)
+ {
+ chr = (int) (unsigned char) *(offset);
+
+ switch (chr)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ expValue = expValue * 10.0 + (double) (chr - 48);
+ offset ++;
+ break;
+ }
+ default:
+ {
+ goto BREAK_EXP_LOOP;
+ }
+ }
+ }
+
+BREAK_EXP_LOOP:
+ //FIXME: Check for arithemtic overflow here
+ ds->lastType = JT_DOUBLE;
+ ds->start = offset;
+ return ds->dec->newDouble (ds->prv, createDouble( (double) intNeg, (double) intValue , frcValue, decimalCount) * pow(10.0, expValue * expNeg));
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_true ( struct DecoderState *ds)
+{
+ char *offset = ds->start;
+ offset ++;
+
+ if (*(offset++) != 'r')
+ goto SETERROR;
+ if (*(offset++) != 'u')
+ goto SETERROR;
+ if (*(offset++) != 'e')
+ goto SETERROR;
+
+ ds->lastType = JT_TRUE;
+ ds->start = offset;
+ return ds->dec->newTrue(ds->prv);
+
+SETERROR:
+ return SetError(ds, -1, "Unexpected character found when decoding 'true'");
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_false ( struct DecoderState *ds)
+{
+ char *offset = ds->start;
+ offset ++;
+
+ if (*(offset++) != 'a')
+ goto SETERROR;
+ if (*(offset++) != 'l')
+ goto SETERROR;
+ if (*(offset++) != 's')
+ goto SETERROR;
+ if (*(offset++) != 'e')
+ goto SETERROR;
+
+ ds->lastType = JT_FALSE;
+ ds->start = offset;
+ return ds->dec->newFalse(ds->prv);
+
+SETERROR:
+ return SetError(ds, -1, "Unexpected character found when decoding 'false'");
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_null ( struct DecoderState *ds)
+{
+ char *offset = ds->start;
+ offset ++;
+
+ if (*(offset++) != 'u')
+ goto SETERROR;
+ if (*(offset++) != 'l')
+ goto SETERROR;
+ if (*(offset++) != 'l')
+ goto SETERROR;
+
+ ds->lastType = JT_NULL;
+ ds->start = offset;
+ return ds->dec->newNull(ds->prv);
+
+SETERROR:
+ return SetError(ds, -1, "Unexpected character found when decoding 'null'");
+}
+
+FASTCALL_ATTR void FASTCALL_MSVC SkipWhitespace(struct DecoderState *ds)
+{
+ char *offset = ds->start;
+
+ for (;;)
+ {
+ switch (*offset)
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ offset ++;
+ break;
+
+ default:
+ ds->start = offset;
+ return;
+ }
+ }
+}
+
+enum DECODESTRINGSTATE
+{
+ DS_ISNULL = 0x32,
+ DS_ISQUOTE,
+ DS_ISESCAPE,
+ DS_UTFLENERROR,
+
+};
+
+static const JSUINT8 g_decoderLookup[256] =
+{
+ /* 0x00 */ DS_ISNULL, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x10 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x20 */ 1, 1, DS_ISQUOTE, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, DS_ISESCAPE, 1, 1, 1,
+ /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x90 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0xa0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0xb0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0xc0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ /* 0xd0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ /* 0xe0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0xf0 */ 4, 4, 4, 4, 4, 4, 4, 4, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR,
+};
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_string ( struct DecoderState *ds)
+{
+ JSUTF16 sur[2] = { 0 };
+ int iSur = 0;
+ int index;
+ wchar_t *escOffset;
+ wchar_t *escStart;
+ size_t escLen = (ds->escEnd - ds->escStart);
+ JSUINT8 *inputOffset;
+ JSUINT8 oct;
+ JSUTF32 ucs;
+ ds->lastType = JT_INVALID;
+ ds->start ++;
+
+ if ( (size_t) (ds->end - ds->start) > escLen)
+ {
+ size_t newSize = (ds->end - ds->start);
+
+ if (ds->escHeap)
+ {
+ if (newSize > (SIZE_MAX / sizeof(wchar_t)))
+ {
+ return SetError(ds, -1, "Could not reserve memory block");
+ }
+ escStart = (wchar_t *)ds->dec->realloc(ds->escStart, newSize * sizeof(wchar_t));
+ if (!escStart)
+ {
+ ds->dec->free(ds->escStart);
+ return SetError(ds, -1, "Could not reserve memory block");
+ }
+ ds->escStart = escStart;
+ }
+ else
+ {
+ wchar_t *oldStart = ds->escStart;
+ if (newSize > (SIZE_MAX / sizeof(wchar_t)))
+ {
+ return SetError(ds, -1, "Could not reserve memory block");
+ }
+ ds->escStart = (wchar_t *) ds->dec->malloc(newSize * sizeof(wchar_t));
+ if (!ds->escStart)
+ {
+ return SetError(ds, -1, "Could not reserve memory block");
+ }
+ ds->escHeap = 1;
+ memcpy(ds->escStart, oldStart, escLen * sizeof(wchar_t));
+ }
+
+ ds->escEnd = ds->escStart + newSize;
+ }
+
+ escOffset = ds->escStart;
+ inputOffset = (JSUINT8 *) ds->start;
+
+ for (;;)
+ {
+ switch (g_decoderLookup[(JSUINT8)(*inputOffset)])
+ {
+ case DS_ISNULL:
+ {
+ return SetError(ds, -1, "Unmatched ''\"' when when decoding 'string'");
+ }
+ case DS_ISQUOTE:
+ {
+ ds->lastType = JT_UTF8;
+ inputOffset ++;
+ ds->start += ( (char *) inputOffset - (ds->start));
+ return ds->dec->newString(ds->prv, ds->escStart, escOffset);
+ }
+ case DS_UTFLENERROR:
+ {
+ return SetError (ds, -1, "Invalid UTF-8 sequence length when decoding 'string'");
+ }
+ case DS_ISESCAPE:
+ inputOffset ++;
+ switch (*inputOffset)
+ {
+ case '\\': *(escOffset++) = L'\\'; inputOffset++; continue;
+ case '\"': *(escOffset++) = L'\"'; inputOffset++; continue;
+ case '/': *(escOffset++) = L'/'; inputOffset++; continue;
+ case 'b': *(escOffset++) = L'\b'; inputOffset++; continue;
+ case 'f': *(escOffset++) = L'\f'; inputOffset++; continue;
+ case 'n': *(escOffset++) = L'\n'; inputOffset++; continue;
+ case 'r': *(escOffset++) = L'\r'; inputOffset++; continue;
+ case 't': *(escOffset++) = L'\t'; inputOffset++; continue;
+
+ case 'u':
+ {
+ int index;
+ inputOffset ++;
+
+ for (index = 0; index < 4; index ++)
+ {
+ switch (*inputOffset)
+ {
+ case '\0': return SetError (ds, -1, "Unterminated unicode escape sequence when decoding 'string'");
+ default: return SetError (ds, -1, "Unexpected character in unicode escape sequence when decoding 'string'");
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ sur[iSur] = (sur[iSur] << 4) + (JSUTF16) (*inputOffset - '0');
+ break;
+
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ sur[iSur] = (sur[iSur] << 4) + 10 + (JSUTF16) (*inputOffset - 'a');
+ break;
+
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ sur[iSur] = (sur[iSur] << 4) + 10 + (JSUTF16) (*inputOffset - 'A');
+ break;
+ }
+
+ inputOffset ++;
+ }
+
+ if (iSur == 0)
+ {
+ if((sur[iSur] & 0xfc00) == 0xd800)
+ {
+ // First of a surrogate pair, continue parsing
+ iSur ++;
+ break;
+ }
+ (*escOffset++) = (wchar_t) sur[iSur];
+ iSur = 0;
+ }
+ else
+ {
+ // Decode pair
+ if ((sur[1] & 0xfc00) != 0xdc00)
+ {
+ return SetError (ds, -1, "Unpaired high surrogate when decoding 'string'");
+ }
+#if WCHAR_MAX == 0xffff
+ (*escOffset++) = (wchar_t) sur[0];
+ (*escOffset++) = (wchar_t) sur[1];
+#else
+ (*escOffset++) = (wchar_t) 0x10000 + (((sur[0] - 0xd800) << 10) | (sur[1] - 0xdc00));
+#endif
+ iSur = 0;
+ }
+ break;
+ }
+
+ case '\0': return SetError(ds, -1, "Unterminated escape sequence when decoding 'string'");
+ default: return SetError(ds, -1, "Unrecognized escape sequence when decoding 'string'");
+ }
+ break;
+
+ case 1:
+ {
+ *(escOffset++) = (wchar_t) (*inputOffset++);
+ break;
+ }
+
+ case 2:
+ {
+ ucs = (*inputOffset++) & 0x1f;
+ ucs <<= 6;
+ if (((*inputOffset) & 0x80) != 0x80)
+ {
+ return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
+ }
+ ucs |= (*inputOffset++) & 0x3f;
+ if (ucs < 0x80) return SetError (ds, -1, "Overlong 2 byte UTF-8 sequence detected when decoding 'string'");
+ *(escOffset++) = (wchar_t) ucs;
+ break;
+ }
+
+ case 3:
+ {
+ JSUTF32 ucs = 0;
+ ucs |= (*inputOffset++) & 0x0f;
+
+ for (index = 0; index < 2; index ++)
+ {
+ ucs <<= 6;
+ oct = (*inputOffset++);
+
+ if ((oct & 0x80) != 0x80)
+ {
+ return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
+ }
+
+ ucs |= oct & 0x3f;
+ }
+
+ if (ucs < 0x800) return SetError (ds, -1, "Overlong 3 byte UTF-8 sequence detected when encoding string");
+ *(escOffset++) = (wchar_t) ucs;
+ break;
+ }
+
+ case 4:
+ {
+ JSUTF32 ucs = 0;
+ ucs |= (*inputOffset++) & 0x07;
+
+ for (index = 0; index < 3; index ++)
+ {
+ ucs <<= 6;
+ oct = (*inputOffset++);
+
+ if ((oct & 0x80) != 0x80)
+ {
+ return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
+ }
+
+ ucs |= oct & 0x3f;
+ }
+
+ if (ucs < 0x10000) return SetError (ds, -1, "Overlong 4 byte UTF-8 sequence detected when decoding 'string'");
+
+#if WCHAR_MAX == 0xffff
+ if (ucs >= 0x10000)
+ {
+ ucs -= 0x10000;
+ *(escOffset++) = (wchar_t) (ucs >> 10) + 0xd800;
+ *(escOffset++) = (wchar_t) (ucs & 0x3ff) + 0xdc00;
+ }
+ else
+ {
+ *(escOffset++) = (wchar_t) ucs;
+ }
+#else
+ *(escOffset++) = (wchar_t) ucs;
+#endif
+ break;
+ }
+ }
+ }
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_array(struct DecoderState *ds)
+{
+ JSOBJ itemValue;
+ JSOBJ newObj;
+ int len;
+ ds->objDepth++;
+ if (ds->objDepth > JSON_MAX_OBJECT_DEPTH) {
+ return SetError(ds, -1, "Reached object decoding depth limit");
+ }
+
+ newObj = ds->dec->newArray(ds->prv);
+ len = 0;
+
+ ds->lastType = JT_INVALID;
+ ds->start ++;
+
+ for (;;)
+ {
+ SkipWhitespace(ds);
+
+ if ((*ds->start) == ']')
+ {
+ ds->objDepth--;
+ if (len == 0)
+ {
+ ds->start ++;
+ return newObj;
+ }
+
+ ds->dec->releaseObject(ds->prv, newObj);
+ return SetError(ds, -1, "Unexpected character found when decoding array value (1)");
+ }
+
+ itemValue = decode_any(ds);
+
+ if (itemValue == NULL)
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ return NULL;
+ }
+
+ ds->dec->arrayAddItem (ds->prv, newObj, itemValue);
+
+ SkipWhitespace(ds);
+
+ switch (*(ds->start++))
+ {
+ case ']':
+ {
+ ds->objDepth--;
+ return newObj;
+ }
+ case ',':
+ break;
+
+ default:
+ ds->dec->releaseObject(ds->prv, newObj);
+ return SetError(ds, -1, "Unexpected character found when decoding array value (2)");
+ }
+
+ len ++;
+ }
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_object( struct DecoderState *ds)
+{
+ JSOBJ itemName;
+ JSOBJ itemValue;
+ JSOBJ newObj;
+
+ ds->objDepth++;
+ if (ds->objDepth > JSON_MAX_OBJECT_DEPTH) {
+ return SetError(ds, -1, "Reached object decoding depth limit");
+ }
+
+ newObj = ds->dec->newObject(ds->prv);
+
+ ds->start ++;
+
+ for (;;)
+ {
+ SkipWhitespace(ds);
+
+ if ((*ds->start) == '}')
+ {
+ ds->objDepth--;
+ ds->start ++;
+ return newObj;
+ }
+
+ ds->lastType = JT_INVALID;
+ itemName = decode_any(ds);
+
+ if (itemName == NULL)
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ return NULL;
+ }
+
+ if (ds->lastType != JT_UTF8)
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ ds->dec->releaseObject(ds->prv, itemName);
+ return SetError(ds, -1, "Key name of object must be 'string' when decoding 'object'");
+ }
+
+ SkipWhitespace(ds);
+
+ if (*(ds->start++) != ':')
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ ds->dec->releaseObject(ds->prv, itemName);
+ return SetError(ds, -1, "No ':' found when decoding object value");
+ }
+
+ SkipWhitespace(ds);
+
+ itemValue = decode_any(ds);
+
+ if (itemValue == NULL)
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ ds->dec->releaseObject(ds->prv, itemName);
+ return NULL;
+ }
+
+ ds->dec->objectAddKey (ds->prv, newObj, itemName, itemValue);
+
+ SkipWhitespace(ds);
+
+ switch (*(ds->start++))
+ {
+ case '}':
+ {
+ ds->objDepth--;
+ return newObj;
+ }
+ case ',':
+ break;
+
+ default:
+ ds->dec->releaseObject(ds->prv, newObj);
+ return SetError(ds, -1, "Unexpected character in found when decoding object value");
+ }
+ }
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_any(struct DecoderState *ds)
+{
+ for (;;)
+ {
+ switch (*ds->start)
+ {
+ case '\"':
+ return decode_string (ds);
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ return decode_numeric (ds);
+
+ case '[': return decode_array (ds);
+ case '{': return decode_object (ds);
+ case 't': return decode_true (ds);
+ case 'f': return decode_false (ds);
+ case 'n': return decode_null (ds);
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ // White space
+ ds->start ++;
+ break;
+
+ default:
+ return SetError(ds, -1, "Expected object or value");
+ }
+ }
+}
+
+JSOBJ JSON_DecodeObject(JSONObjectDecoder *dec, const char *buffer, size_t cbBuffer)
+{
+ /*
+ FIXME: Base the size of escBuffer of that of cbBuffer so that the unicode escaping doesn't run into the wall each time */
+ struct DecoderState ds;
+ wchar_t escBuffer[(JSON_MAX_STACK_BUFFER_SIZE / sizeof(wchar_t))];
+ JSOBJ ret;
+
+ ds.start = (char *) buffer;
+ ds.end = ds.start + cbBuffer;
+
+ ds.escStart = escBuffer;
+ ds.escEnd = ds.escStart + (JSON_MAX_STACK_BUFFER_SIZE / sizeof(wchar_t));
+ ds.escHeap = 0;
+ ds.prv = dec->prv;
+ ds.dec = dec;
+ ds.dec->errorStr = NULL;
+ ds.dec->errorOffset = NULL;
+ ds.objDepth = 0;
+
+ ds.dec = dec;
+
+ ret = decode_any (&ds);
+
+ if (ds.escHeap)
+ {
+ dec->free(ds.escStart);
+ }
+
+ if (!(dec->errorStr))
+ {
+ if ((ds.end - ds.start) > 0)
+ {
+ SkipWhitespace(&ds);
+ }
+
+ if (ds.start != ds.end && ret)
+ {
+ dec->releaseObject(ds.prv, ret);
+ return SetError(&ds, -1, "Trailing data");
+ }
+ }
+
+ return ret;
+}
diff --git a/contrib/python/ujson/py2/lib/ultrajsonenc.c b/contrib/python/ujson/py2/lib/ultrajsonenc.c
new file mode 100644
index 0000000000..ed6645a760
--- /dev/null
+++ b/contrib/python/ujson/py2/lib/ultrajsonenc.c
@@ -0,0 +1,1029 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "ultrajson.h"
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <float.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#if ( (defined(_WIN32) || defined(WIN32) ) && ( defined(_MSC_VER) ) )
+#define snprintf sprintf_s
+#endif
+
+/*
+Worst cases being:
+
+Control characters (ASCII < 32)
+0x00 (1 byte) input => \u0000 output (6 bytes)
+1 * 6 => 6 (6 bytes required)
+
+or UTF-16 surrogate pairs
+4 bytes input in UTF-8 => \uXXXX\uYYYY (12 bytes).
+
+4 * 6 => 24 bytes (12 bytes required)
+
+The extra 2 bytes are for the quotes around the string
+
+*/
+#define RESERVE_STRING(_len) (2 + ((_len) * 6))
+
+static const double g_pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000};
+static const char g_hexChars[] = "0123456789abcdef";
+static const char g_escapeChars[] = "0123456789\\b\\t\\n\\f\\r\\\"\\\\\\/";
+
+/*
+FIXME: While this is fine dandy and working it's a magic value mess which probably only the author understands.
+Needs a cleanup and more documentation */
+
+/*
+Table for pure ascii output escaping all characters above 127 to \uXXXX */
+static const JSUINT8 g_asciiOutputTable[256] =
+{
+/* 0x00 */ 0, 30, 30, 30, 30, 30, 30, 30, 10, 12, 14, 30, 16, 18, 30, 30,
+/* 0x10 */ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+/* 0x20 */ 1, 1, 20, 1, 1, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, 24,
+/* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, 1, 29, 1,
+/* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, 1, 1, 1,
+/* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0x80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0x90 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0xa0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0xb0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0xc0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* 0xd0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* 0xe0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+/* 0xf0 */ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
+};
+
+static void SetError (JSOBJ obj, JSONObjectEncoder *enc, const char *message)
+{
+ enc->errorMsg = message;
+ enc->errorObj = obj;
+}
+
+/*
+FIXME: Keep track of how big these get across several encoder calls and try to make an estimate
+That way we won't run our head into the wall each call */
+void Buffer_Realloc (JSONObjectEncoder *enc, size_t cbNeeded)
+{
+ size_t curSize = enc->end - enc->start;
+ size_t newSize = curSize * 2;
+ size_t offset = enc->offset - enc->start;
+
+ while (newSize < curSize + cbNeeded)
+ {
+ newSize *= 2;
+ }
+
+ if (enc->heap)
+ {
+ enc->start = (char *) enc->realloc (enc->start, newSize);
+ if (!enc->start)
+ {
+ SetError (NULL, enc, "Could not reserve memory block");
+ return;
+ }
+ }
+ else
+ {
+ char *oldStart = enc->start;
+ enc->heap = 1;
+ enc->start = (char *) enc->malloc (newSize);
+ if (!enc->start)
+ {
+ SetError (NULL, enc, "Could not reserve memory block");
+ return;
+ }
+ memcpy (enc->start, oldStart, offset);
+ }
+ enc->offset = enc->start + offset;
+ enc->end = enc->start + newSize;
+}
+
+FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC Buffer_AppendShortHexUnchecked (char *outputOffset, unsigned short value)
+{
+ *(outputOffset++) = g_hexChars[(value & 0xf000) >> 12];
+ *(outputOffset++) = g_hexChars[(value & 0x0f00) >> 8];
+ *(outputOffset++) = g_hexChars[(value & 0x00f0) >> 4];
+ *(outputOffset++) = g_hexChars[(value & 0x000f) >> 0];
+}
+
+int Buffer_EscapeStringUnvalidated (JSONObjectEncoder *enc, const char *io, const char *end)
+{
+ char *of = (char *) enc->offset;
+
+ for (;;)
+ {
+ switch (*io)
+ {
+ case 0x00:
+ {
+ if (io < end)
+ {
+ *(of++) = '\\';
+ *(of++) = 'u';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = '0';
+ break;
+ }
+ else
+ {
+ enc->offset += (of - enc->offset);
+ return TRUE;
+ }
+ }
+ case '\"': (*of++) = '\\'; (*of++) = '\"'; break;
+ case '\\': (*of++) = '\\'; (*of++) = '\\'; break;
+ case '\b': (*of++) = '\\'; (*of++) = 'b'; break;
+ case '\f': (*of++) = '\\'; (*of++) = 'f'; break;
+ case '\n': (*of++) = '\\'; (*of++) = 'n'; break;
+ case '\r': (*of++) = '\\'; (*of++) = 'r'; break;
+ case '\t': (*of++) = '\\'; (*of++) = 't'; break;
+
+ case '/':
+ {
+ if (enc->escapeForwardSlashes)
+ {
+ (*of++) = '\\';
+ (*of++) = '/';
+ }
+ else
+ {
+ // Same as default case below.
+ (*of++) = (*io);
+ }
+ break;
+ }
+ case 0x26: // '&'
+ case 0x3c: // '<'
+ case 0x3e: // '>'
+ {
+ if (enc->encodeHTMLChars)
+ {
+ // Fall through to \u00XX case below.
+ }
+ else
+ {
+ // Same as default case below.
+ (*of++) = (*io);
+ break;
+ }
+ }
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x0b:
+ 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:
+ {
+ *(of++) = '\\';
+ *(of++) = 'u';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = g_hexChars[ (unsigned char) (((*io) & 0xf0) >> 4)];
+ *(of++) = g_hexChars[ (unsigned char) ((*io) & 0x0f)];
+ break;
+ }
+ default: (*of++) = (*io); break;
+ }
+ io++;
+ }
+}
+
+int Buffer_EscapeStringValidated (JSOBJ obj, JSONObjectEncoder *enc, const char *io, const char *end)
+{
+ JSUTF32 ucs;
+ char *of = (char *) enc->offset;
+
+ for (;;)
+ {
+ JSUINT8 utflen = g_asciiOutputTable[(unsigned char) *io];
+
+ switch (utflen)
+ {
+ case 0:
+ {
+ if (io < end)
+ {
+ *(of++) = '\\';
+ *(of++) = 'u';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = '0';
+ io ++;
+ continue;
+ }
+ else
+ {
+ enc->offset += (of - enc->offset);
+ return TRUE;
+ }
+ }
+
+ case 1:
+ {
+ *(of++)= (*io++);
+ continue;
+ }
+
+ case 2:
+ {
+ JSUTF32 in;
+ JSUTF16 in16;
+
+ if (end - io < 1)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
+ return FALSE;
+ }
+
+ memcpy(&in16, io, sizeof(JSUTF16));
+ in = (JSUTF32) in16;
+
+#ifdef __LITTLE_ENDIAN__
+ ucs = ((in & 0x1f) << 6) | ((in >> 8) & 0x3f);
+#else
+ ucs = ((in & 0x1f00) >> 2) | (in & 0x3f);
+#endif
+
+ if (ucs < 0x80)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Overlong 2 byte UTF-8 sequence detected when encoding string");
+ return FALSE;
+ }
+
+ io += 2;
+ break;
+ }
+
+ case 3:
+ {
+ JSUTF32 in;
+ JSUTF16 in16;
+ JSUINT8 in8;
+
+ if (end - io < 2)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
+ return FALSE;
+ }
+
+ memcpy(&in16, io, sizeof(JSUTF16));
+ memcpy(&in8, io + 2, sizeof(JSUINT8));
+#ifdef __LITTLE_ENDIAN__
+ in = (JSUTF32) in16;
+ in |= in8 << 16;
+ ucs = ((in & 0x0f) << 12) | ((in & 0x3f00) >> 2) | ((in & 0x3f0000) >> 16);
+#else
+ in = in16 << 8;
+ in |= in8;
+ ucs = ((in & 0x0f0000) >> 4) | ((in & 0x3f00) >> 2) | (in & 0x3f);
+#endif
+
+ if (ucs < 0x800)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Overlong 3 byte UTF-8 sequence detected when encoding string");
+ return FALSE;
+ }
+
+ io += 3;
+ break;
+ }
+ case 4:
+ {
+ JSUTF32 in;
+
+ if (end - io < 3)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
+ return FALSE;
+ }
+
+ memcpy(&in, io, sizeof(JSUTF32));
+#ifdef __LITTLE_ENDIAN__
+ ucs = ((in & 0x07) << 18) | ((in & 0x3f00) << 4) | ((in & 0x3f0000) >> 10) | ((in & 0x3f000000) >> 24);
+#else
+ ucs = ((in & 0x07000000) >> 6) | ((in & 0x3f0000) >> 4) | ((in & 0x3f00) >> 2) | (in & 0x3f);
+#endif
+ if (ucs < 0x10000)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Overlong 4 byte UTF-8 sequence detected when encoding string");
+ return FALSE;
+ }
+
+ io += 4;
+ break;
+ }
+
+
+ case 5:
+ case 6:
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Unsupported UTF-8 sequence length when encoding string");
+ return FALSE;
+ }
+
+ case 29:
+ {
+ if (enc->encodeHTMLChars)
+ {
+ // Fall through to \u00XX case 30 below.
+ }
+ else
+ {
+ // Same as case 1 above.
+ *(of++) = (*io++);
+ continue;
+ }
+ }
+
+ case 30:
+ {
+ // \uXXXX encode
+ *(of++) = '\\';
+ *(of++) = 'u';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = g_hexChars[ (unsigned char) (((*io) & 0xf0) >> 4)];
+ *(of++) = g_hexChars[ (unsigned char) ((*io) & 0x0f)];
+ io ++;
+ continue;
+ }
+ case 10:
+ case 12:
+ case 14:
+ case 16:
+ case 18:
+ case 20:
+ case 22:
+ {
+ *(of++) = *( (char *) (g_escapeChars + utflen + 0));
+ *(of++) = *( (char *) (g_escapeChars + utflen + 1));
+ io ++;
+ continue;
+ }
+ case 24:
+ {
+ if (enc->escapeForwardSlashes)
+ {
+ *(of++) = *( (char *) (g_escapeChars + utflen + 0));
+ *(of++) = *( (char *) (g_escapeChars + utflen + 1));
+ io ++;
+ }
+ else
+ {
+ // Same as case 1 above.
+ *(of++) = (*io++);
+ }
+ continue;
+ }
+ // This can never happen, it's here to make L4 VC++ happy
+ default:
+ {
+ ucs = 0;
+ break;
+ }
+ }
+
+ /*
+ If the character is a UTF8 sequence of length > 1 we end up here */
+ if (ucs >= 0x10000)
+ {
+ ucs -= 0x10000;
+ *(of++) = '\\';
+ *(of++) = 'u';
+ Buffer_AppendShortHexUnchecked(of, (unsigned short) (ucs >> 10) + 0xd800);
+ of += 4;
+
+ *(of++) = '\\';
+ *(of++) = 'u';
+ Buffer_AppendShortHexUnchecked(of, (unsigned short) (ucs & 0x3ff) + 0xdc00);
+ of += 4;
+ }
+ else
+ {
+ *(of++) = '\\';
+ *(of++) = 'u';
+ Buffer_AppendShortHexUnchecked(of, (unsigned short) ucs);
+ of += 4;
+ }
+ }
+}
+
+#define Buffer_Reserve(__enc, __len) \
+ if ( (size_t) ((__enc)->end - (__enc)->offset) < (size_t) (__len)) \
+ { \
+ Buffer_Realloc((__enc), (__len));\
+ } \
+
+
+#define Buffer_AppendCharUnchecked(__enc, __chr) \
+ *((__enc)->offset++) = __chr; \
+
+FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC strreverse(char* begin, char* end)
+{
+ char aux;
+ while (end > begin)
+ aux = *end, *end-- = *begin, *begin++ = aux;
+}
+
+void Buffer_AppendIndentNewlineUnchecked(JSONObjectEncoder *enc)
+{
+ if (enc->indent > 0) Buffer_AppendCharUnchecked(enc, '\n');
+}
+
+void Buffer_AppendIndentUnchecked(JSONObjectEncoder *enc, JSINT32 value)
+{
+ int i;
+ if (enc->indent > 0)
+ while (value-- > 0)
+ for (i = 0; i < enc->indent; i++)
+ Buffer_AppendCharUnchecked(enc, ' ');
+}
+
+void Buffer_AppendIntUnchecked(JSONObjectEncoder *enc, JSINT32 value)
+{
+ char* wstr;
+ JSUINT32 uvalue = (value < 0) ? -value : value;
+
+ wstr = enc->offset;
+ // Conversion. Number is reversed.
+
+ do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10);
+ if (value < 0) *wstr++ = '-';
+
+ // Reverse string
+ strreverse(enc->offset,wstr - 1);
+ enc->offset += (wstr - (enc->offset));
+}
+
+void Buffer_AppendLongUnchecked(JSONObjectEncoder *enc, JSINT64 value)
+{
+ char* wstr;
+ JSUINT64 uvalue = (value < 0) ? -value : value;
+
+ wstr = enc->offset;
+ // Conversion. Number is reversed.
+
+ do *wstr++ = (char)(48 + (uvalue % 10ULL)); while(uvalue /= 10ULL);
+ if (value < 0) *wstr++ = '-';
+
+ // Reverse string
+ strreverse(enc->offset,wstr - 1);
+ enc->offset += (wstr - (enc->offset));
+}
+
+void Buffer_AppendUnsignedLongUnchecked(JSONObjectEncoder *enc, JSUINT64 value)
+{
+ char* wstr;
+ JSUINT64 uvalue = value;
+
+ wstr = enc->offset;
+ // Conversion. Number is reversed.
+
+ do *wstr++ = (char)(48 + (uvalue % 10ULL)); while(uvalue /= 10ULL);
+
+ // Reverse string
+ strreverse(enc->offset,wstr - 1);
+ enc->offset += (wstr - (enc->offset));
+}
+
+int Buffer_AppendDoubleUnchecked(JSOBJ obj, JSONObjectEncoder *enc, double value)
+{
+ /* if input is larger than thres_max, revert to exponential */
+ const double thres_max = (double) 1e16 - 1;
+ int count;
+ double diff = 0.0;
+ char* str = enc->offset;
+ char* wstr = str;
+ unsigned long long whole;
+ double tmp;
+ unsigned long long frac;
+ int neg;
+ double pow10;
+
+ if (value == HUGE_VAL || value == -HUGE_VAL)
+ {
+ SetError (obj, enc, "Invalid Inf value when encoding double");
+ return FALSE;
+ }
+
+ if (!(value == value))
+ {
+ SetError (obj, enc, "Invalid Nan value when encoding double");
+ return FALSE;
+ }
+
+ /* we'll work in positive values and deal with the
+ negative sign issue later */
+ neg = 0;
+ if (value < 0)
+ {
+ neg = 1;
+ value = -value;
+ }
+
+ pow10 = g_pow10[enc->doublePrecision];
+
+ whole = (unsigned long long) value;
+ tmp = (value - whole) * pow10;
+ frac = (unsigned long long)(tmp);
+ diff = tmp - frac;
+
+ if (diff > 0.5)
+ {
+ ++frac;
+ /* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
+ if (frac >= pow10)
+ {
+ frac = 0;
+ ++whole;
+ }
+ }
+ else
+ if (diff == 0.5 && ((frac == 0) || (frac & 1)))
+ {
+ /* if halfway, round up if odd, OR
+ if last digit is 0. That last part is strange */
+ ++frac;
+ }
+
+ /* for very large numbers switch back to native sprintf for exponentials.
+ anyone want to write code to replace this? */
+ /*
+ normal printf behavior is to print EVERY whole number digit
+ which can be 100s of characters overflowing your buffers == bad
+ */
+ if (value > thres_max)
+ {
+ enc->offset += snprintf(str, enc->end - enc->offset, "%.15e", neg ? -value : value);
+ return TRUE;
+ }
+
+ if (enc->doublePrecision == 0)
+ {
+ diff = value - whole;
+
+ if (diff > 0.5)
+ {
+ /* greater than 0.5, round up, e.g. 1.6 -> 2 */
+ ++whole;
+ }
+ else
+ if (diff == 0.5 && (whole & 1))
+ {
+ /* exactly 0.5 and ODD, then round up */
+ /* 1.5 -> 2, but 2.5 -> 2 */
+ ++whole;
+ }
+
+ //vvvvvvvvvvvvvvvvvvv Diff from modp_dto2
+ }
+ else
+ if (frac)
+ {
+ count = enc->doublePrecision;
+ // now do fractional part, as an unsigned number
+ // we know it is not 0 but we can have leading zeros, these
+ // should be removed
+ while (!(frac % 10))
+ {
+ --count;
+ frac /= 10;
+ }
+ //^^^^^^^^^^^^^^^^^^^ Diff from modp_dto2
+
+ // now do fractional part, as an unsigned number
+ do
+ {
+ --count;
+ *wstr++ = (char)(48 + (frac % 10));
+ } while (frac /= 10);
+ // add extra 0s
+ while (count-- > 0)
+ {
+ *wstr++ = '0';
+ }
+ // add decimal
+ *wstr++ = '.';
+ }
+ else
+ {
+ *wstr++ = '0';
+ *wstr++ = '.';
+ }
+
+ // do whole part
+ // Take care of sign
+ // Conversion. Number is reversed.
+ do *wstr++ = (char)(48 + (whole % 10)); while (whole /= 10);
+
+ if (neg)
+ {
+ *wstr++ = '-';
+ }
+ strreverse(str, wstr-1);
+ enc->offset += (wstr - (enc->offset));
+
+ return TRUE;
+}
+
+/*
+FIXME:
+Handle integration functions returning NULL here */
+
+/*
+FIXME:
+Perhaps implement recursion detection */
+
+void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName)
+{
+ const char *value;
+ char *objName;
+ int count;
+ JSOBJ iterObj;
+ size_t szlen;
+ JSONTypeContext tc;
+
+ if (enc->level > enc->recursionMax)
+ {
+ SetError (obj, enc, "Maximum recursion level reached");
+ return;
+ }
+
+ /*
+ This reservation must hold
+
+ length of _name as encoded worst case +
+ maxLength of double to string OR maxLength of JSLONG to string
+ */
+
+ Buffer_Reserve(enc, 256 + RESERVE_STRING(cbName));
+ if (enc->errorMsg)
+ {
+ return;
+ }
+
+ if (name)
+ {
+ Buffer_AppendCharUnchecked(enc, '\"');
+
+ if (enc->forceASCII)
+ {
+ if (!Buffer_EscapeStringValidated(obj, enc, name, name + cbName))
+ {
+ return;
+ }
+ }
+ else
+ {
+ if (!Buffer_EscapeStringUnvalidated(enc, name, name + cbName))
+ {
+ return;
+ }
+ }
+
+ Buffer_AppendCharUnchecked(enc, '\"');
+
+ Buffer_AppendCharUnchecked (enc, ':');
+#ifdef JSON_NO_EXTRA_WHITESPACE
+ if (enc->indent)
+ {
+ Buffer_AppendCharUnchecked (enc, ' ');
+ }
+#else
+ Buffer_AppendCharUnchecked (enc, ' ');
+#endif
+ }
+
+ tc.encoder_prv = enc->prv;
+ enc->beginTypeContext(obj, &tc, enc);
+
+ switch (tc.type)
+ {
+ case JT_INVALID:
+ {
+ return;
+ }
+
+ case JT_ARRAY:
+ {
+ count = 0;
+
+ Buffer_AppendCharUnchecked (enc, '[');
+ Buffer_AppendIndentNewlineUnchecked (enc);
+
+ while (enc->iterNext(obj, &tc))
+ {
+ if (count > 0)
+ {
+ Buffer_AppendCharUnchecked (enc, ',');
+#ifndef JSON_NO_EXTRA_WHITESPACE
+ Buffer_AppendCharUnchecked (buffer, ' ');
+#endif
+ Buffer_AppendIndentNewlineUnchecked (enc);
+ }
+
+ iterObj = enc->iterGetValue(obj, &tc);
+
+ enc->level ++;
+ Buffer_AppendIndentUnchecked (enc, enc->level);
+ encode (iterObj, enc, NULL, 0);
+ count ++;
+ }
+
+ enc->iterEnd(obj, &tc);
+ Buffer_AppendIndentNewlineUnchecked (enc);
+ Buffer_AppendIndentUnchecked (enc, enc->level);
+ Buffer_AppendCharUnchecked (enc, ']');
+ break;
+ }
+
+ case JT_OBJECT:
+ {
+ count = 0;
+
+ Buffer_AppendCharUnchecked (enc, '{');
+ Buffer_AppendIndentNewlineUnchecked (enc);
+
+ while (enc->iterNext(obj, &tc))
+ {
+ if (count > 0)
+ {
+ Buffer_AppendCharUnchecked (enc, ',');
+#ifndef JSON_NO_EXTRA_WHITESPACE
+ Buffer_AppendCharUnchecked (enc, ' ');
+#endif
+ Buffer_AppendIndentNewlineUnchecked (enc);
+ }
+
+ iterObj = enc->iterGetValue(obj, &tc);
+ objName = enc->iterGetName(obj, &tc, &szlen);
+
+ enc->level ++;
+ Buffer_AppendIndentUnchecked (enc, enc->level);
+ encode (iterObj, enc, objName, szlen);
+ count ++;
+ }
+
+ enc->iterEnd(obj, &tc);
+ Buffer_AppendIndentNewlineUnchecked (enc);
+ Buffer_AppendIndentUnchecked (enc, enc->level);
+ Buffer_AppendCharUnchecked (enc, '}');
+ break;
+ }
+
+ case JT_LONG:
+ {
+ Buffer_AppendLongUnchecked (enc, enc->getLongValue(obj, &tc));
+ break;
+ }
+
+ case JT_ULONG:
+ {
+ Buffer_AppendUnsignedLongUnchecked (enc, enc->getUnsignedLongValue(obj, &tc));
+ break;
+ }
+
+ case JT_INT:
+ {
+ Buffer_AppendIntUnchecked (enc, enc->getIntValue(obj, &tc));
+ break;
+ }
+
+ case JT_TRUE:
+ {
+ Buffer_AppendCharUnchecked (enc, 't');
+ Buffer_AppendCharUnchecked (enc, 'r');
+ Buffer_AppendCharUnchecked (enc, 'u');
+ Buffer_AppendCharUnchecked (enc, 'e');
+ break;
+ }
+
+ case JT_FALSE:
+ {
+ Buffer_AppendCharUnchecked (enc, 'f');
+ Buffer_AppendCharUnchecked (enc, 'a');
+ Buffer_AppendCharUnchecked (enc, 'l');
+ Buffer_AppendCharUnchecked (enc, 's');
+ Buffer_AppendCharUnchecked (enc, 'e');
+ break;
+ }
+
+
+ case JT_NULL:
+ {
+ Buffer_AppendCharUnchecked (enc, 'n');
+ Buffer_AppendCharUnchecked (enc, 'u');
+ Buffer_AppendCharUnchecked (enc, 'l');
+ Buffer_AppendCharUnchecked (enc, 'l');
+ break;
+ }
+
+ case JT_DOUBLE:
+ {
+ if (!Buffer_AppendDoubleUnchecked (obj, enc, enc->getDoubleValue(obj, &tc)))
+ {
+ enc->endTypeContext(obj, &tc);
+ enc->level --;
+ return;
+ }
+ break;
+ }
+
+ case JT_UTF8:
+ {
+ value = enc->getStringValue(obj, &tc, &szlen);
+ if(!value)
+ {
+ SetError(obj, enc, "utf-8 encoding error");
+ return;
+ }
+
+ Buffer_Reserve(enc, RESERVE_STRING(szlen));
+ if (enc->errorMsg)
+ {
+ enc->endTypeContext(obj, &tc);
+ return;
+ }
+ Buffer_AppendCharUnchecked (enc, '\"');
+
+ if (enc->forceASCII)
+ {
+ if (!Buffer_EscapeStringValidated(obj, enc, value, value + szlen))
+ {
+ enc->endTypeContext(obj, &tc);
+ enc->level --;
+ return;
+ }
+ }
+ else
+ {
+ if (!Buffer_EscapeStringUnvalidated(enc, value, value + szlen))
+ {
+ enc->endTypeContext(obj, &tc);
+ enc->level --;
+ return;
+ }
+ }
+
+ Buffer_AppendCharUnchecked (enc, '\"');
+ break;
+ }
+
+ case JT_RAW:
+ {
+ value = enc->getStringValue(obj, &tc, &szlen);
+ if(!value)
+ {
+ SetError(obj, enc, "utf-8 encoding error");
+ return;
+ }
+
+ Buffer_Reserve(enc, RESERVE_STRING(szlen));
+ if (enc->errorMsg)
+ {
+ enc->endTypeContext(obj, &tc);
+ return;
+ }
+
+ memcpy(enc->offset, value, szlen);
+ enc->offset += szlen;
+
+ break;
+ }
+ }
+
+ enc->endTypeContext(obj, &tc);
+ enc->level --;
+}
+
+char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *enc, char *_buffer, size_t _cbBuffer)
+{
+ enc->malloc = enc->malloc ? enc->malloc : malloc;
+ enc->free = enc->free ? enc->free : free;
+ enc->realloc = enc->realloc ? enc->realloc : realloc;
+ enc->errorMsg = NULL;
+ enc->errorObj = NULL;
+ enc->level = 0;
+
+ if (enc->recursionMax < 1)
+ {
+ enc->recursionMax = JSON_MAX_RECURSION_DEPTH;
+ }
+
+ if (enc->doublePrecision < 0 ||
+ enc->doublePrecision > JSON_DOUBLE_MAX_DECIMALS)
+ {
+ enc->doublePrecision = JSON_DOUBLE_MAX_DECIMALS;
+ }
+
+ if (_buffer == NULL)
+ {
+ _cbBuffer = 32768;
+ enc->start = (char *) enc->malloc (_cbBuffer);
+ if (!enc->start)
+ {
+ SetError(obj, enc, "Could not reserve memory block");
+ return NULL;
+ }
+ enc->heap = 1;
+ }
+ else
+ {
+ enc->start = _buffer;
+ enc->heap = 0;
+ }
+
+ enc->end = enc->start + _cbBuffer;
+ enc->offset = enc->start;
+
+ encode (obj, enc, NULL, 0);
+
+ Buffer_Reserve(enc, 1);
+ if (enc->errorMsg)
+ {
+ return NULL;
+ }
+ Buffer_AppendCharUnchecked(enc, '\0');
+
+ return enc->start;
+}
diff --git a/contrib/python/ujson/py2/python/JSONtoObj.c b/contrib/python/ujson/py2/python/JSONtoObj.c
new file mode 100644
index 0000000000..79d9f1af6e
--- /dev/null
+++ b/contrib/python/ujson/py2/python/JSONtoObj.c
@@ -0,0 +1,252 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "py_defines.h"
+#include <ultrajson.h>
+
+
+//#define PRINTMARK() fprintf(stderr, "%s: MARK(%d)\n", __FILE__, __LINE__)
+#define PRINTMARK()
+
+void Object_objectAddKey(void *prv, JSOBJ obj, JSOBJ name, JSOBJ value)
+{
+ PyDict_SetItem (obj, name, value);
+ Py_DECREF( (PyObject *) name);
+ Py_DECREF( (PyObject *) value);
+ return;
+}
+
+void Object_arrayAddItem(void *prv, JSOBJ obj, JSOBJ value)
+{
+ PyList_Append(obj, value);
+ Py_DECREF( (PyObject *) value);
+ return;
+}
+
+JSOBJ Object_newString(void *prv, wchar_t *start, wchar_t *end)
+{
+ return PyUnicode_FromWideChar (start, (end - start));
+}
+
+JSOBJ Object_newTrue(void *prv)
+{
+ Py_RETURN_TRUE;
+}
+
+JSOBJ Object_newFalse(void *prv)
+{
+ Py_RETURN_FALSE;
+}
+
+JSOBJ Object_newNull(void *prv)
+{
+ Py_RETURN_NONE;
+}
+
+JSOBJ Object_newObject(void *prv)
+{
+ return PyDict_New();
+}
+
+JSOBJ Object_newArray(void *prv)
+{
+ return PyList_New(0);
+}
+
+JSOBJ Object_newInteger(void *prv, JSINT32 value)
+{
+ return PyInt_FromLong( (long) value);
+}
+
+JSOBJ Object_newLong(void *prv, JSINT64 value)
+{
+ return PyLong_FromLongLong (value);
+}
+
+JSOBJ Object_newUnsignedLong(void *prv, JSUINT64 value)
+{
+ return PyLong_FromUnsignedLongLong (value);
+}
+
+JSOBJ Object_newDouble(void *prv, double value)
+{
+ return PyFloat_FromDouble(value);
+}
+
+static void Object_releaseObject(void *prv, JSOBJ obj)
+{
+ Py_DECREF( ((PyObject *)obj));
+}
+
+static char *g_kwlist[] = {"obj", "precise_float", NULL};
+
+PyObject* JSONToObj(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *ret;
+ PyObject *sarg;
+ PyObject *arg;
+ PyObject *opreciseFloat = NULL;
+ JSONObjectDecoder decoder =
+ {
+ Object_newString,
+ Object_objectAddKey,
+ Object_arrayAddItem,
+ Object_newTrue,
+ Object_newFalse,
+ Object_newNull,
+ Object_newObject,
+ Object_newArray,
+ Object_newInteger,
+ Object_newLong,
+ Object_newUnsignedLong,
+ Object_newDouble,
+ Object_releaseObject,
+ PyObject_Malloc,
+ PyObject_Free,
+ PyObject_Realloc
+ };
+
+ decoder.preciseFloat = 0;
+ decoder.prv = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", g_kwlist, &arg, &opreciseFloat))
+ {
+ return NULL;
+ }
+
+ if (opreciseFloat && PyObject_IsTrue(opreciseFloat))
+ {
+ decoder.preciseFloat = 1;
+ }
+
+ if (PyString_Check(arg))
+ {
+ sarg = arg;
+ }
+ else
+ if (PyUnicode_Check(arg))
+ {
+ sarg = PyUnicode_AsUTF8String(arg);
+ if (sarg == NULL)
+ {
+ //Exception raised above us by codec according to docs
+ return NULL;
+ }
+ }
+ else
+ {
+ PyErr_Format(PyExc_TypeError, "Expected String or Unicode");
+ return NULL;
+ }
+
+ decoder.errorStr = NULL;
+ decoder.errorOffset = NULL;
+
+ ret = JSON_DecodeObject(&decoder, PyString_AS_STRING(sarg), PyString_GET_SIZE(sarg));
+
+ if (sarg != arg)
+ {
+ Py_DECREF(sarg);
+ }
+
+ if (decoder.errorStr)
+ {
+ /*
+ FIXME: It's possible to give a much nicer error message here with actual failing element in input etc*/
+
+ PyErr_Format (PyExc_ValueError, "%s", decoder.errorStr);
+
+ if (ret)
+ {
+ Py_DECREF( (PyObject *) ret);
+ }
+
+ return NULL;
+ }
+
+ return ret;
+}
+
+PyObject* JSONFileToObj(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *read;
+ PyObject *string;
+ PyObject *result;
+ PyObject *file = NULL;
+ PyObject *argtuple;
+
+ if (!PyArg_ParseTuple (args, "O", &file))
+ {
+ return NULL;
+ }
+
+ if (!PyObject_HasAttrString (file, "read"))
+ {
+ PyErr_Format (PyExc_TypeError, "expected file");
+ return NULL;
+ }
+
+ read = PyObject_GetAttrString (file, "read");
+
+ if (!PyCallable_Check (read)) {
+ Py_XDECREF(read);
+ PyErr_Format (PyExc_TypeError, "expected file");
+ return NULL;
+ }
+
+ string = PyObject_CallObject (read, NULL);
+ Py_XDECREF(read);
+
+ if (string == NULL)
+ {
+ return NULL;
+ }
+
+ argtuple = PyTuple_Pack(1, string);
+
+ result = JSONToObj (self, argtuple, kwargs);
+
+ Py_XDECREF(argtuple);
+ Py_XDECREF(string);
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ return result;
+}
diff --git a/contrib/python/ujson/py2/python/objToJSON.c b/contrib/python/ujson/py2/python/objToJSON.c
new file mode 100644
index 0000000000..b3a821e7ae
--- /dev/null
+++ b/contrib/python/ujson/py2/python/objToJSON.c
@@ -0,0 +1,1168 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+* Copyright (c) 1988-1993 The Regents of the University of California.
+* Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "py_defines.h"
+#include <stdio.h>
+#include <datetime.h>
+#include <ultrajson.h>
+
+#define EPOCH_ORD 719163
+static PyObject* type_decimal = NULL;
+
+typedef void *(*PFN_PyTypeToJSON)(JSOBJ obj, JSONTypeContext *ti, void *outValue, size_t *_outLen);
+
+#if (PY_VERSION_HEX < 0x02050000)
+typedef ssize_t Py_ssize_t;
+#endif
+
+typedef struct __TypeContext
+{
+ JSPFN_ITEREND iterEnd;
+ JSPFN_ITERNEXT iterNext;
+ JSPFN_ITERGETNAME iterGetName;
+ JSPFN_ITERGETVALUE iterGetValue;
+ PFN_PyTypeToJSON PyTypeToJSON;
+ PyObject *newObj;
+ PyObject *dictObj;
+ Py_ssize_t index;
+ Py_ssize_t size;
+ PyObject *itemValue;
+ PyObject *itemName;
+ PyObject *attrList;
+ PyObject *iterator;
+
+ union
+ {
+ PyObject *rawJSONValue;
+ JSINT64 longValue;
+ JSUINT64 unsignedLongValue;
+ };
+} TypeContext;
+
+#define GET_TC(__ptrtc) ((TypeContext *)((__ptrtc)->prv))
+
+struct PyDictIterState
+{
+ PyObject *keys;
+ size_t i;
+ size_t sz;
+};
+
+//#define PRINTMARK() fprintf(stderr, "%s: MARK(%d)\n", __FILE__, __LINE__)
+#define PRINTMARK()
+
+void initObjToJSON(void)
+{
+ PyObject* mod_decimal = PyImport_ImportModule("decimal");
+ if (mod_decimal)
+ {
+ type_decimal = PyObject_GetAttrString(mod_decimal, "Decimal");
+ Py_INCREF(type_decimal);
+ Py_DECREF(mod_decimal);
+ }
+ else
+ PyErr_Clear();
+
+ PyDateTime_IMPORT;
+}
+
+#ifdef _LP64
+static void *PyIntToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ *((JSINT64 *) outValue) = PyInt_AS_LONG (obj);
+ return NULL;
+}
+#else
+static void *PyIntToINT32(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ *((JSINT32 *) outValue) = PyInt_AS_LONG (obj);
+ return NULL;
+}
+#endif
+
+static void *PyLongToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ *((JSINT64 *) outValue) = GET_TC(tc)->longValue;
+ return NULL;
+}
+
+static void *PyLongToUINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ *((JSUINT64 *) outValue) = GET_TC(tc)->unsignedLongValue;
+ return NULL;
+}
+
+static void *PyFloatToDOUBLE(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ *((double *) outValue) = PyFloat_AsDouble (obj);
+ return NULL;
+}
+
+static void *PyStringToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ *_outLen = PyString_GET_SIZE(obj);
+ return PyString_AS_STRING(obj);
+}
+
+static void *PyUnicodeToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ PyObject *newObj;
+#if (PY_VERSION_HEX >= 0x03030000)
+ if(PyUnicode_IS_COMPACT_ASCII(obj))
+ {
+ Py_ssize_t len;
+ char *data = PyUnicode_AsUTF8AndSize(obj, &len);
+ *_outLen = len;
+ return data;
+ }
+#endif
+ newObj = PyUnicode_AsUTF8String(obj);
+ if(!newObj)
+ {
+ return NULL;
+ }
+
+ GET_TC(tc)->newObj = newObj;
+
+ *_outLen = PyString_GET_SIZE(newObj);
+ return PyString_AS_STRING(newObj);
+}
+
+static void *PyRawJSONToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = GET_TC(tc)->rawJSONValue;
+ if (PyUnicode_Check(obj)) {
+ return PyUnicodeToUTF8(obj, tc, outValue, _outLen);
+ }
+ else {
+ return PyStringToUTF8(obj, tc, outValue, _outLen);
+ }
+}
+
+static void *PyDateTimeToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ PyObject *date, *ord, *utcoffset;
+ int y, m, d, h, mn, s, days;
+
+ utcoffset = PyObject_CallMethod(obj, "utcoffset", NULL);
+ if(utcoffset != Py_None){
+ obj = PyNumber_Subtract(obj, utcoffset);
+ }
+
+ y = PyDateTime_GET_YEAR(obj);
+ m = PyDateTime_GET_MONTH(obj);
+ d = PyDateTime_GET_DAY(obj);
+ h = PyDateTime_DATE_GET_HOUR(obj);
+ mn = PyDateTime_DATE_GET_MINUTE(obj);
+ s = PyDateTime_DATE_GET_SECOND(obj);
+
+ date = PyDate_FromDate(y, m, 1);
+ ord = PyObject_CallMethod(date, "toordinal", NULL);
+ days = PyInt_AS_LONG(ord) - EPOCH_ORD + d - 1;
+ Py_DECREF(date);
+ Py_DECREF(ord);
+ *( (JSINT64 *) outValue) = (((JSINT64) ((days * 24 + h) * 60 + mn)) * 60 + s);
+ return NULL;
+}
+
+static void *PyDateToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ PyObject *date, *ord;
+ int y, m, d, days;
+
+ y = PyDateTime_GET_YEAR(obj);
+ m = PyDateTime_GET_MONTH(obj);
+ d = PyDateTime_GET_DAY(obj);
+
+ date = PyDate_FromDate(y, m, 1);
+ ord = PyObject_CallMethod(date, "toordinal", NULL);
+ days = PyInt_AS_LONG(ord) - EPOCH_ORD + d - 1;
+ Py_DECREF(date);
+ Py_DECREF(ord);
+ *( (JSINT64 *) outValue) = ((JSINT64) days * 86400);
+
+ return NULL;
+}
+
+int Tuple_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ PyObject *item;
+
+ if (GET_TC(tc)->index >= GET_TC(tc)->size)
+ {
+ return 0;
+ }
+
+ item = PyTuple_GET_ITEM (obj, GET_TC(tc)->index);
+
+ GET_TC(tc)->itemValue = item;
+ GET_TC(tc)->index ++;
+ return 1;
+}
+
+void Tuple_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+}
+
+JSOBJ Tuple_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *Tuple_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ return NULL;
+}
+
+int Iter_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ PyObject *item;
+
+ if (GET_TC(tc)->itemValue)
+ {
+ Py_DECREF(GET_TC(tc)->itemValue);
+ GET_TC(tc)->itemValue = NULL;
+ }
+
+ if (GET_TC(tc)->iterator == NULL)
+ {
+ return 0;
+ }
+
+ item = PyIter_Next(GET_TC(tc)->iterator);
+
+ if (item == NULL)
+ {
+ return 0;
+ }
+
+ GET_TC(tc)->itemValue = item;
+ return 1;
+}
+
+void Iter_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ if (GET_TC(tc)->itemValue)
+ {
+ Py_DECREF(GET_TC(tc)->itemValue);
+ GET_TC(tc)->itemValue = NULL;
+ }
+
+ if (GET_TC(tc)->iterator)
+ {
+ Py_DECREF(GET_TC(tc)->iterator);
+ GET_TC(tc)->iterator = NULL;
+ }
+}
+
+JSOBJ Iter_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *Iter_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ return NULL;
+}
+
+void Dir_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ if (GET_TC(tc)->itemValue)
+ {
+ Py_DECREF(GET_TC(tc)->itemValue);
+ GET_TC(tc)->itemValue = NULL;
+ }
+
+ if (GET_TC(tc)->itemName)
+ {
+ Py_DECREF(GET_TC(tc)->itemName);
+ GET_TC(tc)->itemName = NULL;
+ }
+
+ Py_DECREF( (PyObject *) GET_TC(tc)->attrList);
+ PRINTMARK();
+}
+
+int Dir_iterNext(JSOBJ _obj, JSONTypeContext *tc)
+{
+ PyObject *obj = (PyObject *) _obj;
+ PyObject *itemValue = GET_TC(tc)->itemValue;
+ PyObject *itemName = GET_TC(tc)->itemName;
+ PyObject* attr;
+ PyObject* attrName;
+ char* attrStr;
+
+ if (itemValue)
+ {
+ Py_DECREF(GET_TC(tc)->itemValue);
+ GET_TC(tc)->itemValue = itemValue = NULL;
+ }
+
+ if (itemName)
+ {
+ Py_DECREF(GET_TC(tc)->itemName);
+ GET_TC(tc)->itemName = itemName = NULL;
+ }
+
+ for (; GET_TC(tc)->index < GET_TC(tc)->size; GET_TC(tc)->index ++)
+ {
+ attrName = PyList_GET_ITEM(GET_TC(tc)->attrList, GET_TC(tc)->index);
+#if PY_MAJOR_VERSION >= 3
+ attr = PyUnicode_AsUTF8String(attrName);
+#else
+ attr = attrName;
+ Py_INCREF(attr);
+#endif
+ attrStr = PyString_AS_STRING(attr);
+
+ if (attrStr[0] == '_')
+ {
+ PRINTMARK();
+ Py_DECREF(attr);
+ continue;
+ }
+
+ itemValue = PyObject_GetAttr(obj, attrName);
+ if (itemValue == NULL)
+ {
+ PyErr_Clear();
+ Py_DECREF(attr);
+ PRINTMARK();
+ continue;
+ }
+
+ if (PyCallable_Check(itemValue))
+ {
+ Py_DECREF(itemValue);
+ Py_DECREF(attr);
+ PRINTMARK();
+ continue;
+ }
+
+ PRINTMARK();
+ itemName = attr;
+ break;
+ }
+
+ if (itemName == NULL)
+ {
+ GET_TC(tc)->index = GET_TC(tc)->size;
+ GET_TC(tc)->itemValue = NULL;
+ return 0;
+ }
+
+ GET_TC(tc)->itemName = itemName;
+ GET_TC(tc)->itemValue = itemValue;
+ GET_TC(tc)->index ++;
+
+ PRINTMARK();
+ return 1;
+}
+
+JSOBJ Dir_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ PRINTMARK();
+ return GET_TC(tc)->itemValue;
+}
+
+char *Dir_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ PRINTMARK();
+ *outLen = PyString_GET_SIZE(GET_TC(tc)->itemName);
+ return PyString_AS_STRING(GET_TC(tc)->itemName);
+}
+
+int List_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ if (GET_TC(tc)->index >= GET_TC(tc)->size)
+ {
+ PRINTMARK();
+ return 0;
+ }
+
+ GET_TC(tc)->itemValue = PyList_GET_ITEM (obj, GET_TC(tc)->index);
+ GET_TC(tc)->index ++;
+ return 1;
+}
+
+void List_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+}
+
+JSOBJ List_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *List_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ return NULL;
+}
+
+//=============================================================================
+// Dict iteration functions
+// itemName might converted to string (Python_Str). Do refCounting
+// itemValue is borrowed from object (which is dict). No refCounting
+//=============================================================================
+
+int Dict_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject* itemNameTmp;
+#endif
+
+ if (GET_TC(tc)->itemName)
+ {
+ Py_DECREF(GET_TC(tc)->itemName);
+ GET_TC(tc)->itemName = NULL;
+ }
+
+
+ if (!PyDict_Next ( (PyObject *)GET_TC(tc)->dictObj, &GET_TC(tc)->index, &GET_TC(tc)->itemName, &GET_TC(tc)->itemValue))
+ {
+ PRINTMARK();
+ return 0;
+ }
+
+ if (PyUnicode_Check(GET_TC(tc)->itemName))
+ {
+ GET_TC(tc)->itemName = PyUnicode_AsUTF8String (GET_TC(tc)->itemName);
+ }
+ else
+ if (!PyString_Check(GET_TC(tc)->itemName))
+ {
+ GET_TC(tc)->itemName = PyObject_Str(GET_TC(tc)->itemName);
+#if PY_MAJOR_VERSION >= 3
+ itemNameTmp = GET_TC(tc)->itemName;
+ GET_TC(tc)->itemName = PyUnicode_AsUTF8String (GET_TC(tc)->itemName);
+ Py_DECREF(itemNameTmp);
+#endif
+ }
+ else
+ {
+ Py_INCREF(GET_TC(tc)->itemName);
+ }
+ PRINTMARK();
+ return 1;
+}
+
+void Dict_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ if (GET_TC(tc)->itemName)
+ {
+ Py_DECREF(GET_TC(tc)->itemName);
+ GET_TC(tc)->itemName = NULL;
+ }
+ Py_DECREF(GET_TC(tc)->dictObj);
+ PRINTMARK();
+}
+
+JSOBJ Dict_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *Dict_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ *outLen = PyString_GET_SIZE(GET_TC(tc)->itemName);
+ return PyString_AS_STRING(GET_TC(tc)->itemName);
+}
+
+int SortedDict_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ PyObject *items = NULL, *item = NULL, *key = NULL, *value = NULL;
+ Py_ssize_t i, nitems;
+#if PY_MAJOR_VERSION >= 3
+ PyObject* keyTmp;
+#endif
+
+ // Upon first call, obtain a list of the keys and sort them. This follows the same logic as the
+ // stanard library's _json.c sort_keys handler.
+ if (GET_TC(tc)->newObj == NULL)
+ {
+ // Obtain the list of keys from the dictionary.
+ items = PyMapping_Keys(GET_TC(tc)->dictObj);
+ if (items == NULL)
+ {
+ goto error;
+ }
+ else if (!PyList_Check(items))
+ {
+ PyErr_SetString(PyExc_ValueError, "keys must return list");
+ goto error;
+ }
+
+ // Sort the list.
+ if (PyList_Sort(items) < 0)
+ {
+ goto error;
+ }
+
+ // Obtain the value for each key, and pack a list of (key, value) 2-tuples.
+ nitems = PyList_GET_SIZE(items);
+ for (i = 0; i < nitems; i++)
+ {
+ key = PyList_GET_ITEM(items, i);
+ value = PyDict_GetItem(GET_TC(tc)->dictObj, key);
+
+ // Subject the key to the same type restrictions and conversions as in Dict_iterGetValue.
+ if (PyUnicode_Check(key))
+ {
+ key = PyUnicode_AsUTF8String(key);
+ }
+ else if (!PyString_Check(key))
+ {
+ key = PyObject_Str(key);
+#if PY_MAJOR_VERSION >= 3
+ keyTmp = key;
+ key = PyUnicode_AsUTF8String(key);
+ Py_DECREF(keyTmp);
+#endif
+ }
+ else
+ {
+ Py_INCREF(key);
+ }
+
+ item = PyTuple_Pack(2, key, value);
+ if (item == NULL)
+ {
+ goto error;
+ }
+ if (PyList_SetItem(items, i, item))
+ {
+ goto error;
+ }
+ Py_DECREF(key);
+ }
+
+ // Store the sorted list of tuples in the newObj slot.
+ GET_TC(tc)->newObj = items;
+ GET_TC(tc)->size = nitems;
+ }
+
+ if (GET_TC(tc)->index >= GET_TC(tc)->size)
+ {
+ PRINTMARK();
+ return 0;
+ }
+
+ item = PyList_GET_ITEM(GET_TC(tc)->newObj, GET_TC(tc)->index);
+ GET_TC(tc)->itemName = PyTuple_GET_ITEM(item, 0);
+ GET_TC(tc)->itemValue = PyTuple_GET_ITEM(item, 1);
+ GET_TC(tc)->index++;
+ return 1;
+
+error:
+ Py_XDECREF(item);
+ Py_XDECREF(key);
+ Py_XDECREF(value);
+ Py_XDECREF(items);
+ return -1;
+}
+
+void SortedDict_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ GET_TC(tc)->itemName = NULL;
+ GET_TC(tc)->itemValue = NULL;
+ Py_DECREF(GET_TC(tc)->dictObj);
+ PRINTMARK();
+}
+
+JSOBJ SortedDict_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *SortedDict_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ *outLen = PyString_GET_SIZE(GET_TC(tc)->itemName);
+ return PyString_AS_STRING(GET_TC(tc)->itemName);
+}
+
+
+void SetupDictIter(PyObject *dictObj, TypeContext *pc, JSONObjectEncoder *enc)
+{
+ if (enc->sortKeys) {
+ pc->iterEnd = SortedDict_iterEnd;
+ pc->iterNext = SortedDict_iterNext;
+ pc->iterGetValue = SortedDict_iterGetValue;
+ pc->iterGetName = SortedDict_iterGetName;
+ }
+ else {
+ pc->iterEnd = Dict_iterEnd;
+ pc->iterNext = Dict_iterNext;
+ pc->iterGetValue = Dict_iterGetValue;
+ pc->iterGetName = Dict_iterGetName;
+ }
+ pc->dictObj = dictObj;
+ pc->index = 0;
+}
+
+void Object_beginTypeContext (JSOBJ _obj, JSONTypeContext *tc, JSONObjectEncoder *enc)
+{
+ PyObject *obj, *exc, *iter;
+ TypeContext *pc;
+ PRINTMARK();
+ if (!_obj) {
+ tc->type = JT_INVALID;
+ return;
+ }
+
+ obj = (PyObject*) _obj;
+
+ tc->prv = PyObject_Malloc(sizeof(TypeContext));
+ pc = (TypeContext *) tc->prv;
+ if (!pc)
+ {
+ tc->type = JT_INVALID;
+ PyErr_NoMemory();
+ return;
+ }
+ pc->newObj = NULL;
+ pc->dictObj = NULL;
+ pc->itemValue = NULL;
+ pc->itemName = NULL;
+ pc->iterator = NULL;
+ pc->attrList = NULL;
+ pc->index = 0;
+ pc->size = 0;
+ pc->longValue = 0;
+ pc->rawJSONValue = NULL;
+
+ if (PyIter_Check(obj))
+ {
+ PRINTMARK();
+ goto ISITERABLE;
+ }
+
+ if (PyBool_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = (obj == Py_True) ? JT_TRUE : JT_FALSE;
+ return;
+ }
+ else
+ if (PyLong_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyLongToINT64;
+ tc->type = JT_LONG;
+ GET_TC(tc)->longValue = PyLong_AsLongLong(obj);
+
+ exc = PyErr_Occurred();
+ if (!exc)
+ {
+ return;
+ }
+
+ if (exc && PyErr_ExceptionMatches(PyExc_OverflowError))
+ {
+ PyErr_Clear();
+ pc->PyTypeToJSON = PyLongToUINT64;
+ tc->type = JT_ULONG;
+ GET_TC(tc)->unsignedLongValue = PyLong_AsUnsignedLongLong(obj);
+
+ exc = PyErr_Occurred();
+ if (exc && PyErr_ExceptionMatches(PyExc_OverflowError))
+ {
+ PRINTMARK();
+ goto INVALID;
+ }
+ }
+
+ return;
+ }
+ else
+ if (PyInt_Check(obj))
+ {
+ PRINTMARK();
+#ifdef _LP64
+ pc->PyTypeToJSON = PyIntToINT64; tc->type = JT_LONG;
+#else
+ pc->PyTypeToJSON = PyIntToINT32; tc->type = JT_INT;
+#endif
+ return;
+ }
+ else
+ if (PyString_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyStringToUTF8; tc->type = JT_UTF8;
+ return;
+ }
+ else
+ if (PyUnicode_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyUnicodeToUTF8; tc->type = JT_UTF8;
+ return;
+ }
+ else
+ if (PyFloat_Check(obj) || (type_decimal && PyObject_IsInstance(obj, type_decimal)))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyFloatToDOUBLE; tc->type = JT_DOUBLE;
+ return;
+ }
+ else
+ if (PyDateTime_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyDateTimeToINT64; tc->type = JT_LONG;
+ return;
+ }
+ else
+ if (PyDate_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyDateToINT64; tc->type = JT_LONG;
+ return;
+ }
+ else
+ if (obj == Py_None)
+ {
+ PRINTMARK();
+ tc->type = JT_NULL;
+ return;
+ }
+
+ISITERABLE:
+ if (PyDict_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = JT_OBJECT;
+ SetupDictIter(obj, pc, enc);
+ Py_INCREF(obj);
+ return;
+ }
+ else
+ if (PyList_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = JT_ARRAY;
+ pc->iterEnd = List_iterEnd;
+ pc->iterNext = List_iterNext;
+ pc->iterGetValue = List_iterGetValue;
+ pc->iterGetName = List_iterGetName;
+ GET_TC(tc)->index = 0;
+ GET_TC(tc)->size = PyList_GET_SIZE( (PyObject *) obj);
+ return;
+ }
+ else
+ if (PyTuple_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = JT_ARRAY;
+ pc->iterEnd = Tuple_iterEnd;
+ pc->iterNext = Tuple_iterNext;
+ pc->iterGetValue = Tuple_iterGetValue;
+ pc->iterGetName = Tuple_iterGetName;
+ GET_TC(tc)->index = 0;
+ GET_TC(tc)->size = PyTuple_GET_SIZE( (PyObject *) obj);
+ GET_TC(tc)->itemValue = NULL;
+
+ return;
+ }
+ /*
+ else
+ if (PyAnySet_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = JT_ARRAY;
+ pc->iterBegin = NULL;
+ pc->iterEnd = Iter_iterEnd;
+ pc->iterNext = Iter_iterNext;
+ pc->iterGetValue = Iter_iterGetValue;
+ pc->iterGetName = Iter_iterGetName;
+ return;
+ }
+ */
+
+ if (PyObject_HasAttrString(obj, "toDict"))
+ {
+ PyObject* toDictFunc = PyObject_GetAttrString(obj, "toDict");
+ PyObject* tuple = PyTuple_New(0);
+ PyObject* toDictResult = PyObject_Call(toDictFunc, tuple, NULL);
+ Py_DECREF(tuple);
+ Py_DECREF(toDictFunc);
+
+ if (toDictResult == NULL)
+ {
+ goto INVALID;
+ }
+
+ if (!PyDict_Check(toDictResult))
+ {
+ Py_DECREF(toDictResult);
+ tc->type = JT_NULL;
+ return;
+ }
+
+ PRINTMARK();
+ tc->type = JT_OBJECT;
+ SetupDictIter(toDictResult, pc, enc);
+ return;
+ }
+ else
+ if (PyObject_HasAttrString(obj, "__json__"))
+ {
+ PyObject* toJSONFunc = PyObject_GetAttrString(obj, "__json__");
+ PyObject* tuple = PyTuple_New(0);
+ PyObject* toJSONResult = PyObject_Call(toJSONFunc, tuple, NULL);
+ Py_DECREF(tuple);
+ Py_DECREF(toJSONFunc);
+
+ if (toJSONResult == NULL)
+ {
+ goto INVALID;
+ }
+
+ if (PyErr_Occurred())
+ {
+ Py_DECREF(toJSONResult);
+ goto INVALID;
+ }
+
+ if (!PyString_Check(toJSONResult) && !PyUnicode_Check(toJSONResult))
+ {
+ Py_DECREF(toJSONResult);
+ PyErr_Format (PyExc_TypeError, "expected string");
+ goto INVALID;
+ }
+
+ PRINTMARK();
+ pc->PyTypeToJSON = PyRawJSONToUTF8;
+ tc->type = JT_RAW;
+ GET_TC(tc)->rawJSONValue = toJSONResult;
+ return;
+ }
+
+ PRINTMARK();
+ PyErr_Clear();
+
+ iter = PyObject_GetIter(obj);
+
+ if (iter != NULL)
+ {
+ PRINTMARK();
+ tc->type = JT_ARRAY;
+ pc->iterator = iter;
+ pc->iterEnd = Iter_iterEnd;
+ pc->iterNext = Iter_iterNext;
+ pc->iterGetValue = Iter_iterGetValue;
+ pc->iterGetName = Iter_iterGetName;
+ return;
+ }
+
+ PRINTMARK();
+ PyErr_Clear();
+
+ PRINTMARK();
+ tc->type = JT_OBJECT;
+ GET_TC(tc)->attrList = PyObject_Dir(obj);
+
+ if (GET_TC(tc)->attrList == NULL)
+ {
+ PyErr_Clear();
+ goto INVALID;
+ }
+
+ GET_TC(tc)->index = 0;
+ GET_TC(tc)->size = PyList_GET_SIZE(GET_TC(tc)->attrList);
+ PRINTMARK();
+
+ pc->iterEnd = Dir_iterEnd;
+ pc->iterNext = Dir_iterNext;
+ pc->iterGetValue = Dir_iterGetValue;
+ pc->iterGetName = Dir_iterGetName;
+ return;
+
+INVALID:
+ PRINTMARK();
+ tc->type = JT_INVALID;
+ PyObject_Free(tc->prv);
+ tc->prv = NULL;
+ return;
+}
+
+void Object_endTypeContext(JSOBJ obj, JSONTypeContext *tc)
+{
+ Py_XDECREF(GET_TC(tc)->newObj);
+
+ if (tc->type == JT_RAW)
+ {
+ Py_XDECREF(GET_TC(tc)->rawJSONValue);
+ }
+ PyObject_Free(tc->prv);
+ tc->prv = NULL;
+}
+
+const char *Object_getStringValue(JSOBJ obj, JSONTypeContext *tc, size_t *_outLen)
+{
+ return GET_TC(tc)->PyTypeToJSON (obj, tc, NULL, _outLen);
+}
+
+JSINT64 Object_getLongValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ JSINT64 ret;
+ GET_TC(tc)->PyTypeToJSON (obj, tc, &ret, NULL);
+ return ret;
+}
+
+JSUINT64 Object_getUnsignedLongValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ JSUINT64 ret;
+ GET_TC(tc)->PyTypeToJSON (obj, tc, &ret, NULL);
+ return ret;
+}
+
+JSINT32 Object_getIntValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ JSINT32 ret;
+ GET_TC(tc)->PyTypeToJSON (obj, tc, &ret, NULL);
+ return ret;
+}
+
+double Object_getDoubleValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ double ret;
+ GET_TC(tc)->PyTypeToJSON (obj, tc, &ret, NULL);
+ return ret;
+}
+
+static void Object_releaseObject(JSOBJ _obj)
+{
+ Py_DECREF( (PyObject *) _obj);
+}
+
+int Object_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->iterNext(obj, tc);
+}
+
+void Object_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ GET_TC(tc)->iterEnd(obj, tc);
+}
+
+JSOBJ Object_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->iterGetValue(obj, tc);
+}
+
+char *Object_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ return GET_TC(tc)->iterGetName(obj, tc, outLen);
+}
+
+PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "obj", "ensure_ascii", "double_precision", "encode_html_chars", "escape_forward_slashes", "sort_keys", "indent", NULL };
+
+ char buffer[65536];
+ char *ret;
+ PyObject *newobj;
+ PyObject *oinput = NULL;
+ PyObject *oensureAscii = NULL;
+ PyObject *oencodeHTMLChars = NULL;
+ PyObject *oescapeForwardSlashes = NULL;
+ PyObject *osortKeys = NULL;
+
+ JSONObjectEncoder encoder =
+ {
+ Object_beginTypeContext,
+ Object_endTypeContext,
+ Object_getStringValue,
+ Object_getLongValue,
+ Object_getUnsignedLongValue,
+ Object_getIntValue,
+ Object_getDoubleValue,
+ Object_iterNext,
+ Object_iterEnd,
+ Object_iterGetValue,
+ Object_iterGetName,
+ Object_releaseObject,
+ PyObject_Malloc,
+ PyObject_Realloc,
+ PyObject_Free,
+ -1, //recursionMax
+ 10, // default double precision setting
+ 1, //forceAscii
+ 0, //encodeHTMLChars
+ 1, //escapeForwardSlashes
+ 0, //sortKeys
+ 0, //indent
+ NULL, //prv
+ };
+
+
+ PRINTMARK();
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OiOOOi", kwlist, &oinput, &oensureAscii, &encoder.doublePrecision, &oencodeHTMLChars, &oescapeForwardSlashes, &osortKeys, &encoder.indent))
+ {
+ return NULL;
+ }
+
+ if (oensureAscii != NULL && !PyObject_IsTrue(oensureAscii))
+ {
+ encoder.forceASCII = 0;
+ }
+
+ if (oencodeHTMLChars != NULL && PyObject_IsTrue(oencodeHTMLChars))
+ {
+ encoder.encodeHTMLChars = 1;
+ }
+
+ if (oescapeForwardSlashes != NULL && !PyObject_IsTrue(oescapeForwardSlashes))
+ {
+ encoder.escapeForwardSlashes = 0;
+ }
+
+ if (osortKeys != NULL && PyObject_IsTrue(osortKeys))
+ {
+ encoder.sortKeys = 1;
+ }
+
+ PRINTMARK();
+ ret = JSON_EncodeObject (oinput, &encoder, buffer, sizeof (buffer));
+ PRINTMARK();
+
+ if (PyErr_Occurred())
+ {
+ return NULL;
+ }
+
+ if (encoder.errorMsg)
+ {
+ if (ret != buffer)
+ {
+ encoder.free (ret);
+ }
+
+ PyErr_Format (PyExc_OverflowError, "%s", encoder.errorMsg);
+ return NULL;
+ }
+
+ newobj = PyString_FromString (ret);
+
+ if (ret != buffer)
+ {
+ encoder.free (ret);
+ }
+
+ PRINTMARK();
+
+ return newobj;
+}
+
+PyObject* objToJSONFile(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *data;
+ PyObject *file;
+ PyObject *string;
+ PyObject *write;
+ PyObject *argtuple;
+ PyObject *write_result;
+
+ PRINTMARK();
+
+ if (!PyArg_ParseTuple (args, "OO", &data, &file))
+ {
+ return NULL;
+ }
+
+ if (!PyObject_HasAttrString (file, "write"))
+ {
+ PyErr_Format (PyExc_TypeError, "expected file");
+ return NULL;
+ }
+
+ write = PyObject_GetAttrString (file, "write");
+
+ if (!PyCallable_Check (write))
+ {
+ Py_XDECREF(write);
+ PyErr_Format (PyExc_TypeError, "expected file");
+ return NULL;
+ }
+
+ argtuple = PyTuple_Pack(1, data);
+
+ string = objToJSON (self, argtuple, kwargs);
+
+ if (string == NULL)
+ {
+ Py_XDECREF(write);
+ Py_XDECREF(argtuple);
+ return NULL;
+ }
+
+ Py_XDECREF(argtuple);
+
+ argtuple = PyTuple_Pack (1, string);
+ if (argtuple == NULL)
+ {
+ Py_XDECREF(write);
+ return NULL;
+ }
+
+ write_result = PyObject_CallObject (write, argtuple);
+ if (write_result == NULL)
+ {
+ Py_XDECREF(write);
+ Py_XDECREF(argtuple);
+ return NULL;
+ }
+
+ Py_DECREF(write_result);
+ Py_XDECREF(write);
+ Py_DECREF(argtuple);
+ Py_XDECREF(string);
+
+ PRINTMARK();
+
+ Py_RETURN_NONE;
+}
diff --git a/contrib/python/ujson/py2/python/py_defines.h b/contrib/python/ujson/py2/python/py_defines.h
new file mode 100644
index 0000000000..2b38b41bdb
--- /dev/null
+++ b/contrib/python/ujson/py2/python/py_defines.h
@@ -0,0 +1,53 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include <Python.h>
+
+#if PY_MAJOR_VERSION >= 3
+
+#define PyInt_Check PyLong_Check
+#define PyInt_AS_LONG PyLong_AsLong
+#define PyInt_FromLong PyLong_FromLong
+
+#define PyString_Check PyBytes_Check
+#define PyString_GET_SIZE PyBytes_GET_SIZE
+#define PyString_AS_STRING PyBytes_AS_STRING
+
+#define PyString_FromString PyUnicode_FromString
+
+#endif
diff --git a/contrib/python/ujson/py2/python/ujson.c b/contrib/python/ujson/py2/python/ujson.c
new file mode 100644
index 0000000000..d0b15c65cf
--- /dev/null
+++ b/contrib/python/ujson/py2/python/ujson.c
@@ -0,0 +1,113 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+* Copyright (c) 1988-1993 The Regents of the University of California.
+* Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "py_defines.h"
+#include "version.h"
+
+/* objToJSON */
+PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs);
+void initObjToJSON(void);
+
+/* JSONToObj */
+PyObject* JSONToObj(PyObject* self, PyObject *args, PyObject *kwargs);
+
+/* objToJSONFile */
+PyObject* objToJSONFile(PyObject* self, PyObject *args, PyObject *kwargs);
+
+/* JSONFileToObj */
+PyObject* JSONFileToObj(PyObject* self, PyObject *args, PyObject *kwargs);
+
+
+#define ENCODER_HELP_TEXT "Use ensure_ascii=false to output UTF-8. Pass in double_precision to alter the maximum digit precision of doubles. Set encode_html_chars=True to encode < > & as unicode escape sequences. Set escape_forward_slashes=False to prevent escaping / characters."
+
+static PyMethodDef ujsonMethods[] = {
+ {"encode", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON. " ENCODER_HELP_TEXT},
+ {"decode", (PyCFunction) JSONToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as string to dict object structure. Use precise_float=True to use high precision float decoder."},
+ {"dumps", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON. " ENCODER_HELP_TEXT},
+ {"loads", (PyCFunction) JSONToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as string to dict object structure. Use precise_float=True to use high precision float decoder."},
+ {"dump", (PyCFunction) objToJSONFile, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON file. " ENCODER_HELP_TEXT},
+ {"load", (PyCFunction) JSONFileToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as file to dict object structure. Use precise_float=True to use high precision float decoder."},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "ujson",
+ 0, /* m_doc */
+ -1, /* m_size */
+ ujsonMethods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
+};
+
+#define PYMODINITFUNC PyObject *PyInit_ujson(void)
+#define PYMODULE_CREATE() PyModule_Create(&moduledef)
+#define MODINITERROR return NULL
+
+#else
+
+#define PYMODINITFUNC PyMODINIT_FUNC initujson(void)
+#define PYMODULE_CREATE() Py_InitModule("ujson", ujsonMethods)
+#define MODINITERROR return
+
+#endif
+
+PYMODINITFUNC
+{
+ PyObject *module;
+ PyObject *version_string;
+
+ initObjToJSON();
+ module = PYMODULE_CREATE();
+
+ if (module == NULL)
+ {
+ MODINITERROR;
+ }
+
+ version_string = PyString_FromString (UJSON_VERSION);
+ PyModule_AddObject (module, "__version__", version_string);
+
+#if PY_MAJOR_VERSION >= 3
+ return module;
+#endif
+}
diff --git a/contrib/python/ujson/py2/python/version.h b/contrib/python/ujson/py2/python/version.h
new file mode 100644
index 0000000000..f0ce6bb733
--- /dev/null
+++ b/contrib/python/ujson/py2/python/version.h
@@ -0,0 +1,39 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#define UJSON_VERSION "1.35"
diff --git a/contrib/python/ujson/py2/ya.make b/contrib/python/ujson/py2/ya.make
new file mode 100644
index 0000000000..65971c8e9b
--- /dev/null
+++ b/contrib/python/ujson/py2/ya.make
@@ -0,0 +1,30 @@
+PY2_LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+VERSION(1.35+dev)
+
+NO_COMPILER_WARNINGS()
+NO_UTIL()
+
+PY_REGISTER(ujson)
+
+ADDINCL(
+ contrib/python/ujson/py2/lib
+ contrib/python/ujson/py2/python
+)
+
+SRCS(
+ lib/ultrajsondec.c
+ lib/ultrajsonenc.c
+ python/JSONtoObj.c
+ python/objToJSON.c
+ python/ujson.c
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ ujson.pyi
+)
+
+END()
diff --git a/contrib/python/ujson/py3/lib/ultrajson.h b/contrib/python/ujson/py3/lib/ultrajson.h
new file mode 100644
index 0000000000..92902b4910
--- /dev/null
+++ b/contrib/python/ujson/py3/lib/ultrajson.h
@@ -0,0 +1,333 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+/*
+Ultra fast JSON encoder and decoder
+Developed by Jonas Tarnstrom (jonas@esn.me).
+
+Encoder notes:
+------------------
+
+:: Cyclic references ::
+Cyclic referenced objects are not detected.
+Set JSONObjectEncoder.recursionMax to suitable value or make sure input object
+tree doesn't have cyclic references.
+
+*/
+
+#ifndef __ULTRAJSON_H__
+#define __ULTRAJSON_H__
+
+#include <stdio.h>
+#include <wchar.h>
+
+// Don't output any extra whitespaces when encoding
+#define JSON_NO_EXTRA_WHITESPACE
+
+// Max decimals to encode double floating point numbers with
+#ifndef JSON_DOUBLE_MAX_DECIMALS
+#define JSON_DOUBLE_MAX_DECIMALS 15
+#endif
+
+// Max recursion depth, default for encoder
+#ifndef JSON_MAX_RECURSION_DEPTH
+#define JSON_MAX_RECURSION_DEPTH 1024
+#endif
+
+// Max recursion depth, default for decoder
+#ifndef JSON_MAX_OBJECT_DEPTH
+#define JSON_MAX_OBJECT_DEPTH 1024
+#endif
+
+/*
+Dictates and limits how much stack space for buffers UltraJSON will use before resorting to provided heap functions */
+#ifndef JSON_MAX_STACK_BUFFER_SIZE
+#define JSON_MAX_STACK_BUFFER_SIZE 131072
+#endif
+
+#ifdef _WIN32
+
+typedef __int64 JSINT64;
+typedef unsigned __int64 JSUINT64;
+
+typedef __int32 JSINT32;
+typedef unsigned __int32 JSUINT32;
+typedef unsigned __int8 JSUINT8;
+typedef unsigned __int16 JSUTF16;
+typedef unsigned __int32 JSUTF32;
+typedef __int64 JSLONG;
+
+#define EXPORTFUNCTION __declspec(dllexport)
+
+#define FASTCALL_MSVC __fastcall
+#define FASTCALL_ATTR
+#define INLINE_PREFIX __inline
+
+#else
+
+#include <stdint.h>
+typedef int64_t JSINT64;
+typedef uint64_t JSUINT64;
+
+typedef int32_t JSINT32;
+typedef uint32_t JSUINT32;
+
+#define FASTCALL_MSVC
+
+#if !defined __x86_64__
+#define FASTCALL_ATTR __attribute__((fastcall))
+#else
+#define FASTCALL_ATTR
+#endif
+
+
+#if defined(__clang__)
+#define INLINE_PREFIX inline __attribute__((always_inline))
+#else
+#define INLINE_PREFIX static inline
+#endif
+
+typedef uint8_t JSUINT8;
+typedef uint16_t JSUTF16;
+typedef uint32_t JSUTF32;
+
+typedef int64_t JSLONG;
+
+#define EXPORTFUNCTION
+#endif
+
+#if !(defined(__LITTLE_ENDIAN__) || defined(__BIG_ENDIAN__))
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define __LITTLE_ENDIAN__
+#else
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define __BIG_ENDIAN__
+#endif
+
+#endif
+
+#endif
+
+#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
+#error "Endianess not supported"
+#endif
+
+enum JSTYPES
+{
+ JT_NULL, // NULL
+ JT_TRUE, // boolean true
+ JT_FALSE, // boolean false
+ JT_INT, // (JSINT32 (signed 32-bit))
+ JT_LONG, // (JSINT64 (signed 64-bit))
+ JT_ULONG, // (JSUINT64 (unsigned 64-bit))
+ JT_DOUBLE, // (double)
+ JT_UTF8, // (char 8-bit)
+ JT_RAW, // (raw char 8-bit)
+ JT_ARRAY, // Array structure
+ JT_OBJECT, // Key/Value structure
+ JT_INVALID, // Internal, do not return nor expect
+};
+
+typedef void * JSOBJ;
+typedef void * JSITER;
+
+typedef struct __JSONTypeContext
+{
+ int type;
+ void *prv;
+ void *encoder_prv;
+} JSONTypeContext;
+
+/*
+Function pointer declarations, suitable for implementing UltraJSON */
+typedef int (*JSPFN_ITERNEXT)(JSOBJ obj, JSONTypeContext *tc);
+typedef void (*JSPFN_ITEREND)(JSOBJ obj, JSONTypeContext *tc);
+typedef JSOBJ (*JSPFN_ITERGETVALUE)(JSOBJ obj, JSONTypeContext *tc);
+typedef char *(*JSPFN_ITERGETNAME)(JSOBJ obj, JSONTypeContext *tc, size_t *outLen);
+typedef void *(*JSPFN_MALLOC)(size_t size);
+typedef void (*JSPFN_FREE)(void *pptr);
+typedef void *(*JSPFN_REALLOC)(void *base, size_t size);
+
+
+struct __JSONObjectEncoder;
+
+typedef struct __JSONObjectEncoder
+{
+ void (*beginTypeContext)(JSOBJ obj, JSONTypeContext *tc, struct __JSONObjectEncoder *enc);
+ void (*endTypeContext)(JSOBJ obj, JSONTypeContext *tc);
+ const char *(*getStringValue)(JSOBJ obj, JSONTypeContext *tc, size_t *_outLen);
+ JSINT64 (*getLongValue)(JSOBJ obj, JSONTypeContext *tc);
+ JSUINT64 (*getUnsignedLongValue)(JSOBJ obj, JSONTypeContext *tc);
+ JSINT32 (*getIntValue)(JSOBJ obj, JSONTypeContext *tc);
+ double (*getDoubleValue)(JSOBJ obj, JSONTypeContext *tc);
+
+ /*
+ Retrieve next object in an iteration. Should return 0 to indicate iteration has reached end or 1 if there are more items.
+ Implementor is responsible for keeping state of the iteration. Use ti->prv fields for this
+ */
+ JSPFN_ITERNEXT iterNext;
+
+ /*
+ Ends the iteration of an iteratable object.
+ Any iteration state stored in ti->prv can be freed here
+ */
+ JSPFN_ITEREND iterEnd;
+
+ /*
+ Returns a reference to the value object of an iterator
+ The is responsible for the life-cycle of the returned string. Use iterNext/iterEnd and ti->prv to keep track of current object
+ */
+ JSPFN_ITERGETVALUE iterGetValue;
+
+ /*
+ Return name of iterator.
+ The is responsible for the life-cycle of the returned string. Use iterNext/iterEnd and ti->prv to keep track of current object
+ */
+ JSPFN_ITERGETNAME iterGetName;
+
+ /*
+ Release a value as indicated by setting ti->release = 1 in the previous getValue call.
+ The ti->prv array should contain the necessary context to release the value
+ */
+ void (*releaseObject)(JSOBJ obj);
+
+ /* Library functions
+ Set to NULL to use STDLIB malloc,realloc,free */
+ JSPFN_MALLOC malloc;
+ JSPFN_REALLOC realloc;
+ JSPFN_FREE free;
+
+ /*
+ Configuration for max recursion, set to 0 to use default (see JSON_MAX_RECURSION_DEPTH)*/
+ int recursionMax;
+
+ /*
+ Configuration for max decimals of double floating point numbers to encode (0-9) */
+ int doublePrecision;
+
+ /*
+ If true output will be ASCII with all characters above 127 encoded as \uXXXX. If false output will be UTF-8 or what ever charset strings are brought as */
+ int forceASCII;
+
+ /*
+ If true, '<', '>', and '&' characters will be encoded as \u003c, \u003e, and \u0026, respectively. If false, no special encoding will be used. */
+ int encodeHTMLChars;
+
+ /*
+ If true, '/' will be encoded as \/. If false, no escaping. */
+ int escapeForwardSlashes;
+
+ /*
+ If true, dictionaries are iterated through in sorted key order. */
+ int sortKeys;
+
+ /*
+ Configuration for spaces of indent */
+ int indent;
+
+ /*
+ Private pointer to be used by the caller. Passed as encoder_prv in JSONTypeContext */
+ void *prv;
+
+ /*
+ Set to an error message if error occured */
+ const char *errorMsg;
+ JSOBJ errorObj;
+
+ /* Buffer stuff */
+ char *start;
+ char *offset;
+ char *end;
+ int heap;
+ int level;
+
+} JSONObjectEncoder;
+
+
+/*
+Encode an object structure into JSON.
+
+Arguments:
+obj - An anonymous type representing the object
+enc - Function definitions for querying JSOBJ type
+buffer - Preallocated buffer to store result in. If NULL function allocates own buffer
+cbBuffer - Length of buffer (ignored if buffer is NULL)
+
+Returns:
+Encoded JSON object as a null terminated char string.
+
+NOTE:
+If the supplied buffer wasn't enough to hold the result the function will allocate a new buffer.
+Life cycle of the provided buffer must still be handled by caller.
+
+If the return value doesn't equal the specified buffer caller must release the memory using
+JSONObjectEncoder.free or free() as specified when calling this function.
+*/
+EXPORTFUNCTION char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *enc, char *buffer, size_t cbBuffer);
+
+
+
+typedef struct __JSONObjectDecoder
+{
+ JSOBJ (*newString)(void *prv, wchar_t *start, wchar_t *end);
+ void (*objectAddKey)(void *prv, JSOBJ obj, JSOBJ name, JSOBJ value);
+ void (*arrayAddItem)(void *prv, JSOBJ obj, JSOBJ value);
+ JSOBJ (*newTrue)(void *prv);
+ JSOBJ (*newFalse)(void *prv);
+ JSOBJ (*newNull)(void *prv);
+ JSOBJ (*newObject)(void *prv);
+ JSOBJ (*newArray)(void *prv);
+ JSOBJ (*newInt)(void *prv, JSINT32 value);
+ JSOBJ (*newLong)(void *prv, JSINT64 value);
+ JSOBJ (*newUnsignedLong)(void *prv, JSUINT64 value);
+ JSOBJ (*newDouble)(void *prv, double value);
+ void (*releaseObject)(void *prv, JSOBJ obj);
+ JSPFN_MALLOC malloc;
+ JSPFN_FREE free;
+ JSPFN_REALLOC realloc;
+ char *errorStr;
+ char *errorOffset;
+ int preciseFloat;
+ void *prv;
+} JSONObjectDecoder;
+
+EXPORTFUNCTION JSOBJ JSON_DecodeObject(JSONObjectDecoder *dec, const char *buffer, size_t cbBuffer);
+
+#endif
diff --git a/contrib/python/ujson/py3/lib/ultrajsondec.c b/contrib/python/ujson/py3/lib/ultrajsondec.c
new file mode 100644
index 0000000000..21a732eceb
--- /dev/null
+++ b/contrib/python/ujson/py3/lib/ultrajsondec.c
@@ -0,0 +1,907 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+* Copyright (c) 1988-1993 The Regents of the University of California.
+* Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "ultrajson.h"
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+#include <limits.h>
+#include <wchar.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+
+struct DecoderState
+{
+ char *start;
+ char *end;
+ wchar_t *escStart;
+ wchar_t *escEnd;
+ int escHeap;
+ int lastType;
+ JSUINT32 objDepth;
+ void *prv;
+ JSONObjectDecoder *dec;
+};
+
+JSOBJ FASTCALL_MSVC decode_any( struct DecoderState *ds) FASTCALL_ATTR;
+typedef JSOBJ (*PFN_DECODER)( struct DecoderState *ds);
+
+static JSOBJ SetError( struct DecoderState *ds, int offset, const char *message)
+{
+ ds->dec->errorOffset = ds->start + offset;
+ ds->dec->errorStr = (char *) message;
+ return NULL;
+}
+
+double createDouble(double intNeg, double intValue, double frcValue, int frcDecimalCount)
+{
+ static const double g_pow10[] = {1.0, 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001,0.0000001, 0.00000001, 0.000000001, 0.0000000001, 0.00000000001, 0.000000000001, 0.0000000000001, 0.00000000000001, 0.000000000000001};
+ return (intValue + (frcValue * g_pow10[frcDecimalCount])) * intNeg;
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decodePreciseFloat(struct DecoderState *ds)
+{
+ char *end;
+ double value;
+ errno = 0;
+
+ value = strtod(ds->start, &end);
+
+ if (errno == ERANGE)
+ {
+ return SetError(ds, -1, "Range error when decoding numeric as double");
+ }
+
+ ds->start = end;
+ return ds->dec->newDouble(ds->prv, value);
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_numeric (struct DecoderState *ds)
+{
+ int intNeg = 1;
+ int mantSize = 0;
+ JSUINT64 intValue;
+ JSUINT64 prevIntValue;
+ int chr;
+ int decimalCount = 0;
+ double frcValue = 0.0;
+ double expNeg;
+ double expValue;
+ char *offset = ds->start;
+
+ JSUINT64 overflowLimit = LLONG_MAX;
+
+ if (*(offset) == '-')
+ {
+ offset ++;
+ intNeg = -1;
+ overflowLimit = LLONG_MIN;
+ }
+
+ // Scan integer part
+ intValue = 0;
+
+ while (1)
+ {
+ chr = (int) (unsigned char) *(offset);
+
+ switch (chr)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ //PERF: Don't do 64-bit arithmetic here unless we know we have to
+ prevIntValue = intValue;
+ intValue = intValue * 10ULL + (JSLONG) (chr - 48);
+
+ if (intNeg == 1 && prevIntValue > intValue)
+ {
+ return SetError(ds, -1, "Value is too big!");
+ }
+ else if (intNeg == -1 && intValue > overflowLimit)
+ {
+ return SetError(ds, -1, overflowLimit == LLONG_MAX ? "Value is too big!" : "Value is too small");
+ }
+
+ offset ++;
+ mantSize ++;
+ break;
+ }
+ case '.':
+ {
+ offset ++;
+ goto DECODE_FRACTION;
+ break;
+ }
+ case 'e':
+ case 'E':
+ {
+ offset ++;
+ goto DECODE_EXPONENT;
+ break;
+ }
+
+ default:
+ {
+ goto BREAK_INT_LOOP;
+ break;
+ }
+ }
+ }
+
+BREAK_INT_LOOP:
+
+ ds->lastType = JT_INT;
+ ds->start = offset;
+
+ if (intNeg == 1 && (intValue & 0x8000000000000000ULL) != 0)
+ {
+ return ds->dec->newUnsignedLong(ds->prv, intValue);
+ }
+ else if ((intValue >> 31))
+ {
+ return ds->dec->newLong(ds->prv, (JSINT64) (intValue * (JSINT64) intNeg));
+ }
+ else
+ {
+ return ds->dec->newInt(ds->prv, (JSINT32) (intValue * intNeg));
+ }
+
+DECODE_FRACTION:
+
+ if (ds->dec->preciseFloat)
+ {
+ return decodePreciseFloat(ds);
+ }
+
+ // Scan fraction part
+ frcValue = 0.0;
+ for (;;)
+ {
+ chr = (int) (unsigned char) *(offset);
+
+ switch (chr)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ if (decimalCount < JSON_DOUBLE_MAX_DECIMALS)
+ {
+ frcValue = frcValue * 10.0 + (double) (chr - 48);
+ decimalCount ++;
+ }
+ offset ++;
+ break;
+ }
+ case 'e':
+ case 'E':
+ {
+ offset ++;
+ goto DECODE_EXPONENT;
+ break;
+ }
+ default:
+ {
+ goto BREAK_FRC_LOOP;
+ }
+ }
+ }
+
+BREAK_FRC_LOOP:
+ //FIXME: Check for arithemtic overflow here
+ ds->lastType = JT_DOUBLE;
+ ds->start = offset;
+ return ds->dec->newDouble (ds->prv, createDouble( (double) intNeg, (double) intValue, frcValue, decimalCount));
+
+DECODE_EXPONENT:
+ if (ds->dec->preciseFloat)
+ {
+ return decodePreciseFloat(ds);
+ }
+
+ expNeg = 1.0;
+
+ if (*(offset) == '-')
+ {
+ expNeg = -1.0;
+ offset ++;
+ }
+ else
+ if (*(offset) == '+')
+ {
+ expNeg = +1.0;
+ offset ++;
+ }
+
+ expValue = 0.0;
+
+ for (;;)
+ {
+ chr = (int) (unsigned char) *(offset);
+
+ switch (chr)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ expValue = expValue * 10.0 + (double) (chr - 48);
+ offset ++;
+ break;
+ }
+ default:
+ {
+ goto BREAK_EXP_LOOP;
+ }
+ }
+ }
+
+BREAK_EXP_LOOP:
+ //FIXME: Check for arithemtic overflow here
+ ds->lastType = JT_DOUBLE;
+ ds->start = offset;
+ return ds->dec->newDouble (ds->prv, createDouble( (double) intNeg, (double) intValue , frcValue, decimalCount) * pow(10.0, expValue * expNeg));
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_true ( struct DecoderState *ds)
+{
+ char *offset = ds->start;
+ offset ++;
+
+ if (*(offset++) != 'r')
+ goto SETERROR;
+ if (*(offset++) != 'u')
+ goto SETERROR;
+ if (*(offset++) != 'e')
+ goto SETERROR;
+
+ ds->lastType = JT_TRUE;
+ ds->start = offset;
+ return ds->dec->newTrue(ds->prv);
+
+SETERROR:
+ return SetError(ds, -1, "Unexpected character found when decoding 'true'");
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_false ( struct DecoderState *ds)
+{
+ char *offset = ds->start;
+ offset ++;
+
+ if (*(offset++) != 'a')
+ goto SETERROR;
+ if (*(offset++) != 'l')
+ goto SETERROR;
+ if (*(offset++) != 's')
+ goto SETERROR;
+ if (*(offset++) != 'e')
+ goto SETERROR;
+
+ ds->lastType = JT_FALSE;
+ ds->start = offset;
+ return ds->dec->newFalse(ds->prv);
+
+SETERROR:
+ return SetError(ds, -1, "Unexpected character found when decoding 'false'");
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_null ( struct DecoderState *ds)
+{
+ char *offset = ds->start;
+ offset ++;
+
+ if (*(offset++) != 'u')
+ goto SETERROR;
+ if (*(offset++) != 'l')
+ goto SETERROR;
+ if (*(offset++) != 'l')
+ goto SETERROR;
+
+ ds->lastType = JT_NULL;
+ ds->start = offset;
+ return ds->dec->newNull(ds->prv);
+
+SETERROR:
+ return SetError(ds, -1, "Unexpected character found when decoding 'null'");
+}
+
+FASTCALL_ATTR void FASTCALL_MSVC SkipWhitespace(struct DecoderState *ds)
+{
+ char *offset = ds->start;
+
+ for (;;)
+ {
+ switch (*offset)
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ offset ++;
+ break;
+
+ default:
+ ds->start = offset;
+ return;
+ }
+ }
+}
+
+enum DECODESTRINGSTATE
+{
+ DS_ISNULL = 0x32,
+ DS_ISQUOTE,
+ DS_ISESCAPE,
+ DS_UTFLENERROR,
+
+};
+
+static const JSUINT8 g_decoderLookup[256] =
+{
+ /* 0x00 */ DS_ISNULL, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x10 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x20 */ 1, 1, DS_ISQUOTE, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, DS_ISESCAPE, 1, 1, 1,
+ /* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0x90 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0xa0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0xb0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 0xc0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ /* 0xd0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ /* 0xe0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ /* 0xf0 */ 4, 4, 4, 4, 4, 4, 4, 4, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR,
+};
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_string ( struct DecoderState *ds)
+{
+ JSUTF16 sur[2] = { 0 };
+ int iSur = 0;
+ int index;
+ wchar_t *escOffset;
+ wchar_t *escStart;
+ size_t escLen = (ds->escEnd - ds->escStart);
+ JSUINT8 *inputOffset;
+ JSUINT8 oct;
+ JSUTF32 ucs;
+ ds->lastType = JT_INVALID;
+ ds->start ++;
+
+ if ( (size_t) (ds->end - ds->start) > escLen)
+ {
+ size_t newSize = (ds->end - ds->start);
+
+ if (ds->escHeap)
+ {
+ if (newSize > (SIZE_MAX / sizeof(wchar_t)))
+ {
+ return SetError(ds, -1, "Could not reserve memory block");
+ }
+ escStart = (wchar_t *)ds->dec->realloc(ds->escStart, newSize * sizeof(wchar_t));
+ if (!escStart)
+ {
+ ds->dec->free(ds->escStart);
+ return SetError(ds, -1, "Could not reserve memory block");
+ }
+ ds->escStart = escStart;
+ }
+ else
+ {
+ wchar_t *oldStart = ds->escStart;
+ if (newSize > (SIZE_MAX / sizeof(wchar_t)))
+ {
+ return SetError(ds, -1, "Could not reserve memory block");
+ }
+ ds->escStart = (wchar_t *) ds->dec->malloc(newSize * sizeof(wchar_t));
+ if (!ds->escStart)
+ {
+ return SetError(ds, -1, "Could not reserve memory block");
+ }
+ ds->escHeap = 1;
+ memcpy(ds->escStart, oldStart, escLen * sizeof(wchar_t));
+ }
+
+ ds->escEnd = ds->escStart + newSize;
+ }
+
+ escOffset = ds->escStart;
+ inputOffset = (JSUINT8 *) ds->start;
+
+ for (;;)
+ {
+ switch (g_decoderLookup[(JSUINT8)(*inputOffset)])
+ {
+ case DS_ISNULL:
+ {
+ return SetError(ds, -1, "Unmatched ''\"' when when decoding 'string'");
+ }
+ case DS_ISQUOTE:
+ {
+ ds->lastType = JT_UTF8;
+ inputOffset ++;
+ ds->start += ( (char *) inputOffset - (ds->start));
+ return ds->dec->newString(ds->prv, ds->escStart, escOffset);
+ }
+ case DS_UTFLENERROR:
+ {
+ return SetError (ds, -1, "Invalid UTF-8 sequence length when decoding 'string'");
+ }
+ case DS_ISESCAPE:
+ inputOffset ++;
+ switch (*inputOffset)
+ {
+ case '\\': *(escOffset++) = L'\\'; inputOffset++; continue;
+ case '\"': *(escOffset++) = L'\"'; inputOffset++; continue;
+ case '/': *(escOffset++) = L'/'; inputOffset++; continue;
+ case 'b': *(escOffset++) = L'\b'; inputOffset++; continue;
+ case 'f': *(escOffset++) = L'\f'; inputOffset++; continue;
+ case 'n': *(escOffset++) = L'\n'; inputOffset++; continue;
+ case 'r': *(escOffset++) = L'\r'; inputOffset++; continue;
+ case 't': *(escOffset++) = L'\t'; inputOffset++; continue;
+
+ case 'u':
+ {
+ int index;
+ inputOffset ++;
+
+ for (index = 0; index < 4; index ++)
+ {
+ switch (*inputOffset)
+ {
+ case '\0': return SetError (ds, -1, "Unterminated unicode escape sequence when decoding 'string'");
+ default: return SetError (ds, -1, "Unexpected character in unicode escape sequence when decoding 'string'");
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ sur[iSur] = (sur[iSur] << 4) + (JSUTF16) (*inputOffset - '0');
+ break;
+
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ sur[iSur] = (sur[iSur] << 4) + 10 + (JSUTF16) (*inputOffset - 'a');
+ break;
+
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ sur[iSur] = (sur[iSur] << 4) + 10 + (JSUTF16) (*inputOffset - 'A');
+ break;
+ }
+
+ inputOffset ++;
+ }
+
+ if (iSur == 0)
+ {
+ if((sur[iSur] & 0xfc00) == 0xd800)
+ {
+ // First of a surrogate pair, continue parsing
+ iSur ++;
+ break;
+ }
+ (*escOffset++) = (wchar_t) sur[iSur];
+ iSur = 0;
+ }
+ else
+ {
+ // Decode pair
+ if ((sur[1] & 0xfc00) != 0xdc00)
+ {
+ return SetError (ds, -1, "Unpaired high surrogate when decoding 'string'");
+ }
+#if WCHAR_MAX == 0xffff
+ (*escOffset++) = (wchar_t) sur[0];
+ (*escOffset++) = (wchar_t) sur[1];
+#else
+ (*escOffset++) = (wchar_t) 0x10000 + (((sur[0] - 0xd800) << 10) | (sur[1] - 0xdc00));
+#endif
+ iSur = 0;
+ }
+ break;
+ }
+
+ case '\0': return SetError(ds, -1, "Unterminated escape sequence when decoding 'string'");
+ default: return SetError(ds, -1, "Unrecognized escape sequence when decoding 'string'");
+ }
+ break;
+
+ case 1:
+ {
+ *(escOffset++) = (wchar_t) (*inputOffset++);
+ break;
+ }
+
+ case 2:
+ {
+ ucs = (*inputOffset++) & 0x1f;
+ ucs <<= 6;
+ if (((*inputOffset) & 0x80) != 0x80)
+ {
+ return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
+ }
+ ucs |= (*inputOffset++) & 0x3f;
+ if (ucs < 0x80) return SetError (ds, -1, "Overlong 2 byte UTF-8 sequence detected when decoding 'string'");
+ *(escOffset++) = (wchar_t) ucs;
+ break;
+ }
+
+ case 3:
+ {
+ JSUTF32 ucs = 0;
+ ucs |= (*inputOffset++) & 0x0f;
+
+ for (index = 0; index < 2; index ++)
+ {
+ ucs <<= 6;
+ oct = (*inputOffset++);
+
+ if ((oct & 0x80) != 0x80)
+ {
+ return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
+ }
+
+ ucs |= oct & 0x3f;
+ }
+
+ if (ucs < 0x800) return SetError (ds, -1, "Overlong 3 byte UTF-8 sequence detected when encoding string");
+ *(escOffset++) = (wchar_t) ucs;
+ break;
+ }
+
+ case 4:
+ {
+ JSUTF32 ucs = 0;
+ ucs |= (*inputOffset++) & 0x07;
+
+ for (index = 0; index < 3; index ++)
+ {
+ ucs <<= 6;
+ oct = (*inputOffset++);
+
+ if ((oct & 0x80) != 0x80)
+ {
+ return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
+ }
+
+ ucs |= oct & 0x3f;
+ }
+
+ if (ucs < 0x10000) return SetError (ds, -1, "Overlong 4 byte UTF-8 sequence detected when decoding 'string'");
+
+#if WCHAR_MAX == 0xffff
+ if (ucs >= 0x10000)
+ {
+ ucs -= 0x10000;
+ *(escOffset++) = (wchar_t) (ucs >> 10) + 0xd800;
+ *(escOffset++) = (wchar_t) (ucs & 0x3ff) + 0xdc00;
+ }
+ else
+ {
+ *(escOffset++) = (wchar_t) ucs;
+ }
+#else
+ *(escOffset++) = (wchar_t) ucs;
+#endif
+ break;
+ }
+ }
+ }
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_array(struct DecoderState *ds)
+{
+ JSOBJ itemValue;
+ JSOBJ newObj;
+ int len;
+ ds->objDepth++;
+ if (ds->objDepth > JSON_MAX_OBJECT_DEPTH) {
+ return SetError(ds, -1, "Reached object decoding depth limit");
+ }
+
+ newObj = ds->dec->newArray(ds->prv);
+ len = 0;
+
+ ds->lastType = JT_INVALID;
+ ds->start ++;
+
+ for (;;)
+ {
+ SkipWhitespace(ds);
+
+ if ((*ds->start) == ']')
+ {
+ ds->objDepth--;
+ if (len == 0)
+ {
+ ds->start ++;
+ return newObj;
+ }
+
+ ds->dec->releaseObject(ds->prv, newObj);
+ return SetError(ds, -1, "Unexpected character found when decoding array value (1)");
+ }
+
+ itemValue = decode_any(ds);
+
+ if (itemValue == NULL)
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ return NULL;
+ }
+
+ ds->dec->arrayAddItem (ds->prv, newObj, itemValue);
+
+ SkipWhitespace(ds);
+
+ switch (*(ds->start++))
+ {
+ case ']':
+ {
+ ds->objDepth--;
+ return newObj;
+ }
+ case ',':
+ break;
+
+ default:
+ ds->dec->releaseObject(ds->prv, newObj);
+ return SetError(ds, -1, "Unexpected character found when decoding array value (2)");
+ }
+
+ len ++;
+ }
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_object( struct DecoderState *ds)
+{
+ JSOBJ itemName;
+ JSOBJ itemValue;
+ JSOBJ newObj;
+
+ ds->objDepth++;
+ if (ds->objDepth > JSON_MAX_OBJECT_DEPTH) {
+ return SetError(ds, -1, "Reached object decoding depth limit");
+ }
+
+ newObj = ds->dec->newObject(ds->prv);
+
+ ds->start ++;
+
+ for (;;)
+ {
+ SkipWhitespace(ds);
+
+ if ((*ds->start) == '}')
+ {
+ ds->objDepth--;
+ ds->start ++;
+ return newObj;
+ }
+
+ ds->lastType = JT_INVALID;
+ itemName = decode_any(ds);
+
+ if (itemName == NULL)
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ return NULL;
+ }
+
+ if (ds->lastType != JT_UTF8)
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ ds->dec->releaseObject(ds->prv, itemName);
+ return SetError(ds, -1, "Key name of object must be 'string' when decoding 'object'");
+ }
+
+ SkipWhitespace(ds);
+
+ if (*(ds->start++) != ':')
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ ds->dec->releaseObject(ds->prv, itemName);
+ return SetError(ds, -1, "No ':' found when decoding object value");
+ }
+
+ SkipWhitespace(ds);
+
+ itemValue = decode_any(ds);
+
+ if (itemValue == NULL)
+ {
+ ds->dec->releaseObject(ds->prv, newObj);
+ ds->dec->releaseObject(ds->prv, itemName);
+ return NULL;
+ }
+
+ ds->dec->objectAddKey (ds->prv, newObj, itemName, itemValue);
+
+ SkipWhitespace(ds);
+
+ switch (*(ds->start++))
+ {
+ case '}':
+ {
+ ds->objDepth--;
+ return newObj;
+ }
+ case ',':
+ break;
+
+ default:
+ ds->dec->releaseObject(ds->prv, newObj);
+ return SetError(ds, -1, "Unexpected character in found when decoding object value");
+ }
+ }
+}
+
+FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_any(struct DecoderState *ds)
+{
+ for (;;)
+ {
+ switch (*ds->start)
+ {
+ case '\"':
+ return decode_string (ds);
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ return decode_numeric (ds);
+
+ case '[': return decode_array (ds);
+ case '{': return decode_object (ds);
+ case 't': return decode_true (ds);
+ case 'f': return decode_false (ds);
+ case 'n': return decode_null (ds);
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ // White space
+ ds->start ++;
+ break;
+
+ default:
+ return SetError(ds, -1, "Expected object or value");
+ }
+ }
+}
+
+JSOBJ JSON_DecodeObject(JSONObjectDecoder *dec, const char *buffer, size_t cbBuffer)
+{
+ /*
+ FIXME: Base the size of escBuffer of that of cbBuffer so that the unicode escaping doesn't run into the wall each time */
+ struct DecoderState ds;
+ wchar_t escBuffer[(JSON_MAX_STACK_BUFFER_SIZE / sizeof(wchar_t))];
+ JSOBJ ret;
+
+ ds.start = (char *) buffer;
+ ds.end = ds.start + cbBuffer;
+
+ ds.escStart = escBuffer;
+ ds.escEnd = ds.escStart + (JSON_MAX_STACK_BUFFER_SIZE / sizeof(wchar_t));
+ ds.escHeap = 0;
+ ds.prv = dec->prv;
+ ds.dec = dec;
+ ds.dec->errorStr = NULL;
+ ds.dec->errorOffset = NULL;
+ ds.objDepth = 0;
+
+ ds.dec = dec;
+
+ ret = decode_any (&ds);
+
+ if (ds.escHeap)
+ {
+ dec->free(ds.escStart);
+ }
+
+ if (!(dec->errorStr))
+ {
+ if ((ds.end - ds.start) > 0)
+ {
+ SkipWhitespace(&ds);
+ }
+
+ if (ds.start != ds.end && ret)
+ {
+ dec->releaseObject(ds.prv, ret);
+ return SetError(&ds, -1, "Trailing data");
+ }
+ }
+
+ return ret;
+}
diff --git a/contrib/python/ujson/py3/lib/ultrajsonenc.c b/contrib/python/ujson/py3/lib/ultrajsonenc.c
new file mode 100644
index 0000000000..ed6645a760
--- /dev/null
+++ b/contrib/python/ujson/py3/lib/ultrajsonenc.c
@@ -0,0 +1,1029 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "ultrajson.h"
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <float.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#if ( (defined(_WIN32) || defined(WIN32) ) && ( defined(_MSC_VER) ) )
+#define snprintf sprintf_s
+#endif
+
+/*
+Worst cases being:
+
+Control characters (ASCII < 32)
+0x00 (1 byte) input => \u0000 output (6 bytes)
+1 * 6 => 6 (6 bytes required)
+
+or UTF-16 surrogate pairs
+4 bytes input in UTF-8 => \uXXXX\uYYYY (12 bytes).
+
+4 * 6 => 24 bytes (12 bytes required)
+
+The extra 2 bytes are for the quotes around the string
+
+*/
+#define RESERVE_STRING(_len) (2 + ((_len) * 6))
+
+static const double g_pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000};
+static const char g_hexChars[] = "0123456789abcdef";
+static const char g_escapeChars[] = "0123456789\\b\\t\\n\\f\\r\\\"\\\\\\/";
+
+/*
+FIXME: While this is fine dandy and working it's a magic value mess which probably only the author understands.
+Needs a cleanup and more documentation */
+
+/*
+Table for pure ascii output escaping all characters above 127 to \uXXXX */
+static const JSUINT8 g_asciiOutputTable[256] =
+{
+/* 0x00 */ 0, 30, 30, 30, 30, 30, 30, 30, 10, 12, 14, 30, 16, 18, 30, 30,
+/* 0x10 */ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+/* 0x20 */ 1, 1, 20, 1, 1, 1, 29, 1, 1, 1, 1, 1, 1, 1, 1, 24,
+/* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, 1, 29, 1,
+/* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, 1, 1, 1,
+/* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0x80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0x90 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0xa0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0xb0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 0xc0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* 0xd0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* 0xe0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+/* 0xf0 */ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
+};
+
+static void SetError (JSOBJ obj, JSONObjectEncoder *enc, const char *message)
+{
+ enc->errorMsg = message;
+ enc->errorObj = obj;
+}
+
+/*
+FIXME: Keep track of how big these get across several encoder calls and try to make an estimate
+That way we won't run our head into the wall each call */
+void Buffer_Realloc (JSONObjectEncoder *enc, size_t cbNeeded)
+{
+ size_t curSize = enc->end - enc->start;
+ size_t newSize = curSize * 2;
+ size_t offset = enc->offset - enc->start;
+
+ while (newSize < curSize + cbNeeded)
+ {
+ newSize *= 2;
+ }
+
+ if (enc->heap)
+ {
+ enc->start = (char *) enc->realloc (enc->start, newSize);
+ if (!enc->start)
+ {
+ SetError (NULL, enc, "Could not reserve memory block");
+ return;
+ }
+ }
+ else
+ {
+ char *oldStart = enc->start;
+ enc->heap = 1;
+ enc->start = (char *) enc->malloc (newSize);
+ if (!enc->start)
+ {
+ SetError (NULL, enc, "Could not reserve memory block");
+ return;
+ }
+ memcpy (enc->start, oldStart, offset);
+ }
+ enc->offset = enc->start + offset;
+ enc->end = enc->start + newSize;
+}
+
+FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC Buffer_AppendShortHexUnchecked (char *outputOffset, unsigned short value)
+{
+ *(outputOffset++) = g_hexChars[(value & 0xf000) >> 12];
+ *(outputOffset++) = g_hexChars[(value & 0x0f00) >> 8];
+ *(outputOffset++) = g_hexChars[(value & 0x00f0) >> 4];
+ *(outputOffset++) = g_hexChars[(value & 0x000f) >> 0];
+}
+
+int Buffer_EscapeStringUnvalidated (JSONObjectEncoder *enc, const char *io, const char *end)
+{
+ char *of = (char *) enc->offset;
+
+ for (;;)
+ {
+ switch (*io)
+ {
+ case 0x00:
+ {
+ if (io < end)
+ {
+ *(of++) = '\\';
+ *(of++) = 'u';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = '0';
+ break;
+ }
+ else
+ {
+ enc->offset += (of - enc->offset);
+ return TRUE;
+ }
+ }
+ case '\"': (*of++) = '\\'; (*of++) = '\"'; break;
+ case '\\': (*of++) = '\\'; (*of++) = '\\'; break;
+ case '\b': (*of++) = '\\'; (*of++) = 'b'; break;
+ case '\f': (*of++) = '\\'; (*of++) = 'f'; break;
+ case '\n': (*of++) = '\\'; (*of++) = 'n'; break;
+ case '\r': (*of++) = '\\'; (*of++) = 'r'; break;
+ case '\t': (*of++) = '\\'; (*of++) = 't'; break;
+
+ case '/':
+ {
+ if (enc->escapeForwardSlashes)
+ {
+ (*of++) = '\\';
+ (*of++) = '/';
+ }
+ else
+ {
+ // Same as default case below.
+ (*of++) = (*io);
+ }
+ break;
+ }
+ case 0x26: // '&'
+ case 0x3c: // '<'
+ case 0x3e: // '>'
+ {
+ if (enc->encodeHTMLChars)
+ {
+ // Fall through to \u00XX case below.
+ }
+ else
+ {
+ // Same as default case below.
+ (*of++) = (*io);
+ break;
+ }
+ }
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x0b:
+ 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:
+ {
+ *(of++) = '\\';
+ *(of++) = 'u';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = g_hexChars[ (unsigned char) (((*io) & 0xf0) >> 4)];
+ *(of++) = g_hexChars[ (unsigned char) ((*io) & 0x0f)];
+ break;
+ }
+ default: (*of++) = (*io); break;
+ }
+ io++;
+ }
+}
+
+int Buffer_EscapeStringValidated (JSOBJ obj, JSONObjectEncoder *enc, const char *io, const char *end)
+{
+ JSUTF32 ucs;
+ char *of = (char *) enc->offset;
+
+ for (;;)
+ {
+ JSUINT8 utflen = g_asciiOutputTable[(unsigned char) *io];
+
+ switch (utflen)
+ {
+ case 0:
+ {
+ if (io < end)
+ {
+ *(of++) = '\\';
+ *(of++) = 'u';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = '0';
+ io ++;
+ continue;
+ }
+ else
+ {
+ enc->offset += (of - enc->offset);
+ return TRUE;
+ }
+ }
+
+ case 1:
+ {
+ *(of++)= (*io++);
+ continue;
+ }
+
+ case 2:
+ {
+ JSUTF32 in;
+ JSUTF16 in16;
+
+ if (end - io < 1)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
+ return FALSE;
+ }
+
+ memcpy(&in16, io, sizeof(JSUTF16));
+ in = (JSUTF32) in16;
+
+#ifdef __LITTLE_ENDIAN__
+ ucs = ((in & 0x1f) << 6) | ((in >> 8) & 0x3f);
+#else
+ ucs = ((in & 0x1f00) >> 2) | (in & 0x3f);
+#endif
+
+ if (ucs < 0x80)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Overlong 2 byte UTF-8 sequence detected when encoding string");
+ return FALSE;
+ }
+
+ io += 2;
+ break;
+ }
+
+ case 3:
+ {
+ JSUTF32 in;
+ JSUTF16 in16;
+ JSUINT8 in8;
+
+ if (end - io < 2)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
+ return FALSE;
+ }
+
+ memcpy(&in16, io, sizeof(JSUTF16));
+ memcpy(&in8, io + 2, sizeof(JSUINT8));
+#ifdef __LITTLE_ENDIAN__
+ in = (JSUTF32) in16;
+ in |= in8 << 16;
+ ucs = ((in & 0x0f) << 12) | ((in & 0x3f00) >> 2) | ((in & 0x3f0000) >> 16);
+#else
+ in = in16 << 8;
+ in |= in8;
+ ucs = ((in & 0x0f0000) >> 4) | ((in & 0x3f00) >> 2) | (in & 0x3f);
+#endif
+
+ if (ucs < 0x800)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Overlong 3 byte UTF-8 sequence detected when encoding string");
+ return FALSE;
+ }
+
+ io += 3;
+ break;
+ }
+ case 4:
+ {
+ JSUTF32 in;
+
+ if (end - io < 3)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
+ return FALSE;
+ }
+
+ memcpy(&in, io, sizeof(JSUTF32));
+#ifdef __LITTLE_ENDIAN__
+ ucs = ((in & 0x07) << 18) | ((in & 0x3f00) << 4) | ((in & 0x3f0000) >> 10) | ((in & 0x3f000000) >> 24);
+#else
+ ucs = ((in & 0x07000000) >> 6) | ((in & 0x3f0000) >> 4) | ((in & 0x3f00) >> 2) | (in & 0x3f);
+#endif
+ if (ucs < 0x10000)
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Overlong 4 byte UTF-8 sequence detected when encoding string");
+ return FALSE;
+ }
+
+ io += 4;
+ break;
+ }
+
+
+ case 5:
+ case 6:
+ {
+ enc->offset += (of - enc->offset);
+ SetError (obj, enc, "Unsupported UTF-8 sequence length when encoding string");
+ return FALSE;
+ }
+
+ case 29:
+ {
+ if (enc->encodeHTMLChars)
+ {
+ // Fall through to \u00XX case 30 below.
+ }
+ else
+ {
+ // Same as case 1 above.
+ *(of++) = (*io++);
+ continue;
+ }
+ }
+
+ case 30:
+ {
+ // \uXXXX encode
+ *(of++) = '\\';
+ *(of++) = 'u';
+ *(of++) = '0';
+ *(of++) = '0';
+ *(of++) = g_hexChars[ (unsigned char) (((*io) & 0xf0) >> 4)];
+ *(of++) = g_hexChars[ (unsigned char) ((*io) & 0x0f)];
+ io ++;
+ continue;
+ }
+ case 10:
+ case 12:
+ case 14:
+ case 16:
+ case 18:
+ case 20:
+ case 22:
+ {
+ *(of++) = *( (char *) (g_escapeChars + utflen + 0));
+ *(of++) = *( (char *) (g_escapeChars + utflen + 1));
+ io ++;
+ continue;
+ }
+ case 24:
+ {
+ if (enc->escapeForwardSlashes)
+ {
+ *(of++) = *( (char *) (g_escapeChars + utflen + 0));
+ *(of++) = *( (char *) (g_escapeChars + utflen + 1));
+ io ++;
+ }
+ else
+ {
+ // Same as case 1 above.
+ *(of++) = (*io++);
+ }
+ continue;
+ }
+ // This can never happen, it's here to make L4 VC++ happy
+ default:
+ {
+ ucs = 0;
+ break;
+ }
+ }
+
+ /*
+ If the character is a UTF8 sequence of length > 1 we end up here */
+ if (ucs >= 0x10000)
+ {
+ ucs -= 0x10000;
+ *(of++) = '\\';
+ *(of++) = 'u';
+ Buffer_AppendShortHexUnchecked(of, (unsigned short) (ucs >> 10) + 0xd800);
+ of += 4;
+
+ *(of++) = '\\';
+ *(of++) = 'u';
+ Buffer_AppendShortHexUnchecked(of, (unsigned short) (ucs & 0x3ff) + 0xdc00);
+ of += 4;
+ }
+ else
+ {
+ *(of++) = '\\';
+ *(of++) = 'u';
+ Buffer_AppendShortHexUnchecked(of, (unsigned short) ucs);
+ of += 4;
+ }
+ }
+}
+
+#define Buffer_Reserve(__enc, __len) \
+ if ( (size_t) ((__enc)->end - (__enc)->offset) < (size_t) (__len)) \
+ { \
+ Buffer_Realloc((__enc), (__len));\
+ } \
+
+
+#define Buffer_AppendCharUnchecked(__enc, __chr) \
+ *((__enc)->offset++) = __chr; \
+
+FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC strreverse(char* begin, char* end)
+{
+ char aux;
+ while (end > begin)
+ aux = *end, *end-- = *begin, *begin++ = aux;
+}
+
+void Buffer_AppendIndentNewlineUnchecked(JSONObjectEncoder *enc)
+{
+ if (enc->indent > 0) Buffer_AppendCharUnchecked(enc, '\n');
+}
+
+void Buffer_AppendIndentUnchecked(JSONObjectEncoder *enc, JSINT32 value)
+{
+ int i;
+ if (enc->indent > 0)
+ while (value-- > 0)
+ for (i = 0; i < enc->indent; i++)
+ Buffer_AppendCharUnchecked(enc, ' ');
+}
+
+void Buffer_AppendIntUnchecked(JSONObjectEncoder *enc, JSINT32 value)
+{
+ char* wstr;
+ JSUINT32 uvalue = (value < 0) ? -value : value;
+
+ wstr = enc->offset;
+ // Conversion. Number is reversed.
+
+ do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10);
+ if (value < 0) *wstr++ = '-';
+
+ // Reverse string
+ strreverse(enc->offset,wstr - 1);
+ enc->offset += (wstr - (enc->offset));
+}
+
+void Buffer_AppendLongUnchecked(JSONObjectEncoder *enc, JSINT64 value)
+{
+ char* wstr;
+ JSUINT64 uvalue = (value < 0) ? -value : value;
+
+ wstr = enc->offset;
+ // Conversion. Number is reversed.
+
+ do *wstr++ = (char)(48 + (uvalue % 10ULL)); while(uvalue /= 10ULL);
+ if (value < 0) *wstr++ = '-';
+
+ // Reverse string
+ strreverse(enc->offset,wstr - 1);
+ enc->offset += (wstr - (enc->offset));
+}
+
+void Buffer_AppendUnsignedLongUnchecked(JSONObjectEncoder *enc, JSUINT64 value)
+{
+ char* wstr;
+ JSUINT64 uvalue = value;
+
+ wstr = enc->offset;
+ // Conversion. Number is reversed.
+
+ do *wstr++ = (char)(48 + (uvalue % 10ULL)); while(uvalue /= 10ULL);
+
+ // Reverse string
+ strreverse(enc->offset,wstr - 1);
+ enc->offset += (wstr - (enc->offset));
+}
+
+int Buffer_AppendDoubleUnchecked(JSOBJ obj, JSONObjectEncoder *enc, double value)
+{
+ /* if input is larger than thres_max, revert to exponential */
+ const double thres_max = (double) 1e16 - 1;
+ int count;
+ double diff = 0.0;
+ char* str = enc->offset;
+ char* wstr = str;
+ unsigned long long whole;
+ double tmp;
+ unsigned long long frac;
+ int neg;
+ double pow10;
+
+ if (value == HUGE_VAL || value == -HUGE_VAL)
+ {
+ SetError (obj, enc, "Invalid Inf value when encoding double");
+ return FALSE;
+ }
+
+ if (!(value == value))
+ {
+ SetError (obj, enc, "Invalid Nan value when encoding double");
+ return FALSE;
+ }
+
+ /* we'll work in positive values and deal with the
+ negative sign issue later */
+ neg = 0;
+ if (value < 0)
+ {
+ neg = 1;
+ value = -value;
+ }
+
+ pow10 = g_pow10[enc->doublePrecision];
+
+ whole = (unsigned long long) value;
+ tmp = (value - whole) * pow10;
+ frac = (unsigned long long)(tmp);
+ diff = tmp - frac;
+
+ if (diff > 0.5)
+ {
+ ++frac;
+ /* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
+ if (frac >= pow10)
+ {
+ frac = 0;
+ ++whole;
+ }
+ }
+ else
+ if (diff == 0.5 && ((frac == 0) || (frac & 1)))
+ {
+ /* if halfway, round up if odd, OR
+ if last digit is 0. That last part is strange */
+ ++frac;
+ }
+
+ /* for very large numbers switch back to native sprintf for exponentials.
+ anyone want to write code to replace this? */
+ /*
+ normal printf behavior is to print EVERY whole number digit
+ which can be 100s of characters overflowing your buffers == bad
+ */
+ if (value > thres_max)
+ {
+ enc->offset += snprintf(str, enc->end - enc->offset, "%.15e", neg ? -value : value);
+ return TRUE;
+ }
+
+ if (enc->doublePrecision == 0)
+ {
+ diff = value - whole;
+
+ if (diff > 0.5)
+ {
+ /* greater than 0.5, round up, e.g. 1.6 -> 2 */
+ ++whole;
+ }
+ else
+ if (diff == 0.5 && (whole & 1))
+ {
+ /* exactly 0.5 and ODD, then round up */
+ /* 1.5 -> 2, but 2.5 -> 2 */
+ ++whole;
+ }
+
+ //vvvvvvvvvvvvvvvvvvv Diff from modp_dto2
+ }
+ else
+ if (frac)
+ {
+ count = enc->doublePrecision;
+ // now do fractional part, as an unsigned number
+ // we know it is not 0 but we can have leading zeros, these
+ // should be removed
+ while (!(frac % 10))
+ {
+ --count;
+ frac /= 10;
+ }
+ //^^^^^^^^^^^^^^^^^^^ Diff from modp_dto2
+
+ // now do fractional part, as an unsigned number
+ do
+ {
+ --count;
+ *wstr++ = (char)(48 + (frac % 10));
+ } while (frac /= 10);
+ // add extra 0s
+ while (count-- > 0)
+ {
+ *wstr++ = '0';
+ }
+ // add decimal
+ *wstr++ = '.';
+ }
+ else
+ {
+ *wstr++ = '0';
+ *wstr++ = '.';
+ }
+
+ // do whole part
+ // Take care of sign
+ // Conversion. Number is reversed.
+ do *wstr++ = (char)(48 + (whole % 10)); while (whole /= 10);
+
+ if (neg)
+ {
+ *wstr++ = '-';
+ }
+ strreverse(str, wstr-1);
+ enc->offset += (wstr - (enc->offset));
+
+ return TRUE;
+}
+
+/*
+FIXME:
+Handle integration functions returning NULL here */
+
+/*
+FIXME:
+Perhaps implement recursion detection */
+
+void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName)
+{
+ const char *value;
+ char *objName;
+ int count;
+ JSOBJ iterObj;
+ size_t szlen;
+ JSONTypeContext tc;
+
+ if (enc->level > enc->recursionMax)
+ {
+ SetError (obj, enc, "Maximum recursion level reached");
+ return;
+ }
+
+ /*
+ This reservation must hold
+
+ length of _name as encoded worst case +
+ maxLength of double to string OR maxLength of JSLONG to string
+ */
+
+ Buffer_Reserve(enc, 256 + RESERVE_STRING(cbName));
+ if (enc->errorMsg)
+ {
+ return;
+ }
+
+ if (name)
+ {
+ Buffer_AppendCharUnchecked(enc, '\"');
+
+ if (enc->forceASCII)
+ {
+ if (!Buffer_EscapeStringValidated(obj, enc, name, name + cbName))
+ {
+ return;
+ }
+ }
+ else
+ {
+ if (!Buffer_EscapeStringUnvalidated(enc, name, name + cbName))
+ {
+ return;
+ }
+ }
+
+ Buffer_AppendCharUnchecked(enc, '\"');
+
+ Buffer_AppendCharUnchecked (enc, ':');
+#ifdef JSON_NO_EXTRA_WHITESPACE
+ if (enc->indent)
+ {
+ Buffer_AppendCharUnchecked (enc, ' ');
+ }
+#else
+ Buffer_AppendCharUnchecked (enc, ' ');
+#endif
+ }
+
+ tc.encoder_prv = enc->prv;
+ enc->beginTypeContext(obj, &tc, enc);
+
+ switch (tc.type)
+ {
+ case JT_INVALID:
+ {
+ return;
+ }
+
+ case JT_ARRAY:
+ {
+ count = 0;
+
+ Buffer_AppendCharUnchecked (enc, '[');
+ Buffer_AppendIndentNewlineUnchecked (enc);
+
+ while (enc->iterNext(obj, &tc))
+ {
+ if (count > 0)
+ {
+ Buffer_AppendCharUnchecked (enc, ',');
+#ifndef JSON_NO_EXTRA_WHITESPACE
+ Buffer_AppendCharUnchecked (buffer, ' ');
+#endif
+ Buffer_AppendIndentNewlineUnchecked (enc);
+ }
+
+ iterObj = enc->iterGetValue(obj, &tc);
+
+ enc->level ++;
+ Buffer_AppendIndentUnchecked (enc, enc->level);
+ encode (iterObj, enc, NULL, 0);
+ count ++;
+ }
+
+ enc->iterEnd(obj, &tc);
+ Buffer_AppendIndentNewlineUnchecked (enc);
+ Buffer_AppendIndentUnchecked (enc, enc->level);
+ Buffer_AppendCharUnchecked (enc, ']');
+ break;
+ }
+
+ case JT_OBJECT:
+ {
+ count = 0;
+
+ Buffer_AppendCharUnchecked (enc, '{');
+ Buffer_AppendIndentNewlineUnchecked (enc);
+
+ while (enc->iterNext(obj, &tc))
+ {
+ if (count > 0)
+ {
+ Buffer_AppendCharUnchecked (enc, ',');
+#ifndef JSON_NO_EXTRA_WHITESPACE
+ Buffer_AppendCharUnchecked (enc, ' ');
+#endif
+ Buffer_AppendIndentNewlineUnchecked (enc);
+ }
+
+ iterObj = enc->iterGetValue(obj, &tc);
+ objName = enc->iterGetName(obj, &tc, &szlen);
+
+ enc->level ++;
+ Buffer_AppendIndentUnchecked (enc, enc->level);
+ encode (iterObj, enc, objName, szlen);
+ count ++;
+ }
+
+ enc->iterEnd(obj, &tc);
+ Buffer_AppendIndentNewlineUnchecked (enc);
+ Buffer_AppendIndentUnchecked (enc, enc->level);
+ Buffer_AppendCharUnchecked (enc, '}');
+ break;
+ }
+
+ case JT_LONG:
+ {
+ Buffer_AppendLongUnchecked (enc, enc->getLongValue(obj, &tc));
+ break;
+ }
+
+ case JT_ULONG:
+ {
+ Buffer_AppendUnsignedLongUnchecked (enc, enc->getUnsignedLongValue(obj, &tc));
+ break;
+ }
+
+ case JT_INT:
+ {
+ Buffer_AppendIntUnchecked (enc, enc->getIntValue(obj, &tc));
+ break;
+ }
+
+ case JT_TRUE:
+ {
+ Buffer_AppendCharUnchecked (enc, 't');
+ Buffer_AppendCharUnchecked (enc, 'r');
+ Buffer_AppendCharUnchecked (enc, 'u');
+ Buffer_AppendCharUnchecked (enc, 'e');
+ break;
+ }
+
+ case JT_FALSE:
+ {
+ Buffer_AppendCharUnchecked (enc, 'f');
+ Buffer_AppendCharUnchecked (enc, 'a');
+ Buffer_AppendCharUnchecked (enc, 'l');
+ Buffer_AppendCharUnchecked (enc, 's');
+ Buffer_AppendCharUnchecked (enc, 'e');
+ break;
+ }
+
+
+ case JT_NULL:
+ {
+ Buffer_AppendCharUnchecked (enc, 'n');
+ Buffer_AppendCharUnchecked (enc, 'u');
+ Buffer_AppendCharUnchecked (enc, 'l');
+ Buffer_AppendCharUnchecked (enc, 'l');
+ break;
+ }
+
+ case JT_DOUBLE:
+ {
+ if (!Buffer_AppendDoubleUnchecked (obj, enc, enc->getDoubleValue(obj, &tc)))
+ {
+ enc->endTypeContext(obj, &tc);
+ enc->level --;
+ return;
+ }
+ break;
+ }
+
+ case JT_UTF8:
+ {
+ value = enc->getStringValue(obj, &tc, &szlen);
+ if(!value)
+ {
+ SetError(obj, enc, "utf-8 encoding error");
+ return;
+ }
+
+ Buffer_Reserve(enc, RESERVE_STRING(szlen));
+ if (enc->errorMsg)
+ {
+ enc->endTypeContext(obj, &tc);
+ return;
+ }
+ Buffer_AppendCharUnchecked (enc, '\"');
+
+ if (enc->forceASCII)
+ {
+ if (!Buffer_EscapeStringValidated(obj, enc, value, value + szlen))
+ {
+ enc->endTypeContext(obj, &tc);
+ enc->level --;
+ return;
+ }
+ }
+ else
+ {
+ if (!Buffer_EscapeStringUnvalidated(enc, value, value + szlen))
+ {
+ enc->endTypeContext(obj, &tc);
+ enc->level --;
+ return;
+ }
+ }
+
+ Buffer_AppendCharUnchecked (enc, '\"');
+ break;
+ }
+
+ case JT_RAW:
+ {
+ value = enc->getStringValue(obj, &tc, &szlen);
+ if(!value)
+ {
+ SetError(obj, enc, "utf-8 encoding error");
+ return;
+ }
+
+ Buffer_Reserve(enc, RESERVE_STRING(szlen));
+ if (enc->errorMsg)
+ {
+ enc->endTypeContext(obj, &tc);
+ return;
+ }
+
+ memcpy(enc->offset, value, szlen);
+ enc->offset += szlen;
+
+ break;
+ }
+ }
+
+ enc->endTypeContext(obj, &tc);
+ enc->level --;
+}
+
+char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *enc, char *_buffer, size_t _cbBuffer)
+{
+ enc->malloc = enc->malloc ? enc->malloc : malloc;
+ enc->free = enc->free ? enc->free : free;
+ enc->realloc = enc->realloc ? enc->realloc : realloc;
+ enc->errorMsg = NULL;
+ enc->errorObj = NULL;
+ enc->level = 0;
+
+ if (enc->recursionMax < 1)
+ {
+ enc->recursionMax = JSON_MAX_RECURSION_DEPTH;
+ }
+
+ if (enc->doublePrecision < 0 ||
+ enc->doublePrecision > JSON_DOUBLE_MAX_DECIMALS)
+ {
+ enc->doublePrecision = JSON_DOUBLE_MAX_DECIMALS;
+ }
+
+ if (_buffer == NULL)
+ {
+ _cbBuffer = 32768;
+ enc->start = (char *) enc->malloc (_cbBuffer);
+ if (!enc->start)
+ {
+ SetError(obj, enc, "Could not reserve memory block");
+ return NULL;
+ }
+ enc->heap = 1;
+ }
+ else
+ {
+ enc->start = _buffer;
+ enc->heap = 0;
+ }
+
+ enc->end = enc->start + _cbBuffer;
+ enc->offset = enc->start;
+
+ encode (obj, enc, NULL, 0);
+
+ Buffer_Reserve(enc, 1);
+ if (enc->errorMsg)
+ {
+ return NULL;
+ }
+ Buffer_AppendCharUnchecked(enc, '\0');
+
+ return enc->start;
+}
diff --git a/contrib/python/ujson/py3/python/JSONtoObj.c b/contrib/python/ujson/py3/python/JSONtoObj.c
new file mode 100644
index 0000000000..79d9f1af6e
--- /dev/null
+++ b/contrib/python/ujson/py3/python/JSONtoObj.c
@@ -0,0 +1,252 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "py_defines.h"
+#include <ultrajson.h>
+
+
+//#define PRINTMARK() fprintf(stderr, "%s: MARK(%d)\n", __FILE__, __LINE__)
+#define PRINTMARK()
+
+void Object_objectAddKey(void *prv, JSOBJ obj, JSOBJ name, JSOBJ value)
+{
+ PyDict_SetItem (obj, name, value);
+ Py_DECREF( (PyObject *) name);
+ Py_DECREF( (PyObject *) value);
+ return;
+}
+
+void Object_arrayAddItem(void *prv, JSOBJ obj, JSOBJ value)
+{
+ PyList_Append(obj, value);
+ Py_DECREF( (PyObject *) value);
+ return;
+}
+
+JSOBJ Object_newString(void *prv, wchar_t *start, wchar_t *end)
+{
+ return PyUnicode_FromWideChar (start, (end - start));
+}
+
+JSOBJ Object_newTrue(void *prv)
+{
+ Py_RETURN_TRUE;
+}
+
+JSOBJ Object_newFalse(void *prv)
+{
+ Py_RETURN_FALSE;
+}
+
+JSOBJ Object_newNull(void *prv)
+{
+ Py_RETURN_NONE;
+}
+
+JSOBJ Object_newObject(void *prv)
+{
+ return PyDict_New();
+}
+
+JSOBJ Object_newArray(void *prv)
+{
+ return PyList_New(0);
+}
+
+JSOBJ Object_newInteger(void *prv, JSINT32 value)
+{
+ return PyInt_FromLong( (long) value);
+}
+
+JSOBJ Object_newLong(void *prv, JSINT64 value)
+{
+ return PyLong_FromLongLong (value);
+}
+
+JSOBJ Object_newUnsignedLong(void *prv, JSUINT64 value)
+{
+ return PyLong_FromUnsignedLongLong (value);
+}
+
+JSOBJ Object_newDouble(void *prv, double value)
+{
+ return PyFloat_FromDouble(value);
+}
+
+static void Object_releaseObject(void *prv, JSOBJ obj)
+{
+ Py_DECREF( ((PyObject *)obj));
+}
+
+static char *g_kwlist[] = {"obj", "precise_float", NULL};
+
+PyObject* JSONToObj(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *ret;
+ PyObject *sarg;
+ PyObject *arg;
+ PyObject *opreciseFloat = NULL;
+ JSONObjectDecoder decoder =
+ {
+ Object_newString,
+ Object_objectAddKey,
+ Object_arrayAddItem,
+ Object_newTrue,
+ Object_newFalse,
+ Object_newNull,
+ Object_newObject,
+ Object_newArray,
+ Object_newInteger,
+ Object_newLong,
+ Object_newUnsignedLong,
+ Object_newDouble,
+ Object_releaseObject,
+ PyObject_Malloc,
+ PyObject_Free,
+ PyObject_Realloc
+ };
+
+ decoder.preciseFloat = 0;
+ decoder.prv = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", g_kwlist, &arg, &opreciseFloat))
+ {
+ return NULL;
+ }
+
+ if (opreciseFloat && PyObject_IsTrue(opreciseFloat))
+ {
+ decoder.preciseFloat = 1;
+ }
+
+ if (PyString_Check(arg))
+ {
+ sarg = arg;
+ }
+ else
+ if (PyUnicode_Check(arg))
+ {
+ sarg = PyUnicode_AsUTF8String(arg);
+ if (sarg == NULL)
+ {
+ //Exception raised above us by codec according to docs
+ return NULL;
+ }
+ }
+ else
+ {
+ PyErr_Format(PyExc_TypeError, "Expected String or Unicode");
+ return NULL;
+ }
+
+ decoder.errorStr = NULL;
+ decoder.errorOffset = NULL;
+
+ ret = JSON_DecodeObject(&decoder, PyString_AS_STRING(sarg), PyString_GET_SIZE(sarg));
+
+ if (sarg != arg)
+ {
+ Py_DECREF(sarg);
+ }
+
+ if (decoder.errorStr)
+ {
+ /*
+ FIXME: It's possible to give a much nicer error message here with actual failing element in input etc*/
+
+ PyErr_Format (PyExc_ValueError, "%s", decoder.errorStr);
+
+ if (ret)
+ {
+ Py_DECREF( (PyObject *) ret);
+ }
+
+ return NULL;
+ }
+
+ return ret;
+}
+
+PyObject* JSONFileToObj(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *read;
+ PyObject *string;
+ PyObject *result;
+ PyObject *file = NULL;
+ PyObject *argtuple;
+
+ if (!PyArg_ParseTuple (args, "O", &file))
+ {
+ return NULL;
+ }
+
+ if (!PyObject_HasAttrString (file, "read"))
+ {
+ PyErr_Format (PyExc_TypeError, "expected file");
+ return NULL;
+ }
+
+ read = PyObject_GetAttrString (file, "read");
+
+ if (!PyCallable_Check (read)) {
+ Py_XDECREF(read);
+ PyErr_Format (PyExc_TypeError, "expected file");
+ return NULL;
+ }
+
+ string = PyObject_CallObject (read, NULL);
+ Py_XDECREF(read);
+
+ if (string == NULL)
+ {
+ return NULL;
+ }
+
+ argtuple = PyTuple_Pack(1, string);
+
+ result = JSONToObj (self, argtuple, kwargs);
+
+ Py_XDECREF(argtuple);
+ Py_XDECREF(string);
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ return result;
+}
diff --git a/contrib/python/ujson/py3/python/objToJSON.c b/contrib/python/ujson/py3/python/objToJSON.c
new file mode 100644
index 0000000000..b3a821e7ae
--- /dev/null
+++ b/contrib/python/ujson/py3/python/objToJSON.c
@@ -0,0 +1,1168 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+* Copyright (c) 1988-1993 The Regents of the University of California.
+* Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "py_defines.h"
+#include <stdio.h>
+#include <datetime.h>
+#include <ultrajson.h>
+
+#define EPOCH_ORD 719163
+static PyObject* type_decimal = NULL;
+
+typedef void *(*PFN_PyTypeToJSON)(JSOBJ obj, JSONTypeContext *ti, void *outValue, size_t *_outLen);
+
+#if (PY_VERSION_HEX < 0x02050000)
+typedef ssize_t Py_ssize_t;
+#endif
+
+typedef struct __TypeContext
+{
+ JSPFN_ITEREND iterEnd;
+ JSPFN_ITERNEXT iterNext;
+ JSPFN_ITERGETNAME iterGetName;
+ JSPFN_ITERGETVALUE iterGetValue;
+ PFN_PyTypeToJSON PyTypeToJSON;
+ PyObject *newObj;
+ PyObject *dictObj;
+ Py_ssize_t index;
+ Py_ssize_t size;
+ PyObject *itemValue;
+ PyObject *itemName;
+ PyObject *attrList;
+ PyObject *iterator;
+
+ union
+ {
+ PyObject *rawJSONValue;
+ JSINT64 longValue;
+ JSUINT64 unsignedLongValue;
+ };
+} TypeContext;
+
+#define GET_TC(__ptrtc) ((TypeContext *)((__ptrtc)->prv))
+
+struct PyDictIterState
+{
+ PyObject *keys;
+ size_t i;
+ size_t sz;
+};
+
+//#define PRINTMARK() fprintf(stderr, "%s: MARK(%d)\n", __FILE__, __LINE__)
+#define PRINTMARK()
+
+void initObjToJSON(void)
+{
+ PyObject* mod_decimal = PyImport_ImportModule("decimal");
+ if (mod_decimal)
+ {
+ type_decimal = PyObject_GetAttrString(mod_decimal, "Decimal");
+ Py_INCREF(type_decimal);
+ Py_DECREF(mod_decimal);
+ }
+ else
+ PyErr_Clear();
+
+ PyDateTime_IMPORT;
+}
+
+#ifdef _LP64
+static void *PyIntToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ *((JSINT64 *) outValue) = PyInt_AS_LONG (obj);
+ return NULL;
+}
+#else
+static void *PyIntToINT32(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ *((JSINT32 *) outValue) = PyInt_AS_LONG (obj);
+ return NULL;
+}
+#endif
+
+static void *PyLongToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ *((JSINT64 *) outValue) = GET_TC(tc)->longValue;
+ return NULL;
+}
+
+static void *PyLongToUINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ *((JSUINT64 *) outValue) = GET_TC(tc)->unsignedLongValue;
+ return NULL;
+}
+
+static void *PyFloatToDOUBLE(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ *((double *) outValue) = PyFloat_AsDouble (obj);
+ return NULL;
+}
+
+static void *PyStringToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ *_outLen = PyString_GET_SIZE(obj);
+ return PyString_AS_STRING(obj);
+}
+
+static void *PyUnicodeToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ PyObject *newObj;
+#if (PY_VERSION_HEX >= 0x03030000)
+ if(PyUnicode_IS_COMPACT_ASCII(obj))
+ {
+ Py_ssize_t len;
+ char *data = PyUnicode_AsUTF8AndSize(obj, &len);
+ *_outLen = len;
+ return data;
+ }
+#endif
+ newObj = PyUnicode_AsUTF8String(obj);
+ if(!newObj)
+ {
+ return NULL;
+ }
+
+ GET_TC(tc)->newObj = newObj;
+
+ *_outLen = PyString_GET_SIZE(newObj);
+ return PyString_AS_STRING(newObj);
+}
+
+static void *PyRawJSONToUTF8(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = GET_TC(tc)->rawJSONValue;
+ if (PyUnicode_Check(obj)) {
+ return PyUnicodeToUTF8(obj, tc, outValue, _outLen);
+ }
+ else {
+ return PyStringToUTF8(obj, tc, outValue, _outLen);
+ }
+}
+
+static void *PyDateTimeToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ PyObject *date, *ord, *utcoffset;
+ int y, m, d, h, mn, s, days;
+
+ utcoffset = PyObject_CallMethod(obj, "utcoffset", NULL);
+ if(utcoffset != Py_None){
+ obj = PyNumber_Subtract(obj, utcoffset);
+ }
+
+ y = PyDateTime_GET_YEAR(obj);
+ m = PyDateTime_GET_MONTH(obj);
+ d = PyDateTime_GET_DAY(obj);
+ h = PyDateTime_DATE_GET_HOUR(obj);
+ mn = PyDateTime_DATE_GET_MINUTE(obj);
+ s = PyDateTime_DATE_GET_SECOND(obj);
+
+ date = PyDate_FromDate(y, m, 1);
+ ord = PyObject_CallMethod(date, "toordinal", NULL);
+ days = PyInt_AS_LONG(ord) - EPOCH_ORD + d - 1;
+ Py_DECREF(date);
+ Py_DECREF(ord);
+ *( (JSINT64 *) outValue) = (((JSINT64) ((days * 24 + h) * 60 + mn)) * 60 + s);
+ return NULL;
+}
+
+static void *PyDateToINT64(JSOBJ _obj, JSONTypeContext *tc, void *outValue, size_t *_outLen)
+{
+ PyObject *obj = (PyObject *) _obj;
+ PyObject *date, *ord;
+ int y, m, d, days;
+
+ y = PyDateTime_GET_YEAR(obj);
+ m = PyDateTime_GET_MONTH(obj);
+ d = PyDateTime_GET_DAY(obj);
+
+ date = PyDate_FromDate(y, m, 1);
+ ord = PyObject_CallMethod(date, "toordinal", NULL);
+ days = PyInt_AS_LONG(ord) - EPOCH_ORD + d - 1;
+ Py_DECREF(date);
+ Py_DECREF(ord);
+ *( (JSINT64 *) outValue) = ((JSINT64) days * 86400);
+
+ return NULL;
+}
+
+int Tuple_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ PyObject *item;
+
+ if (GET_TC(tc)->index >= GET_TC(tc)->size)
+ {
+ return 0;
+ }
+
+ item = PyTuple_GET_ITEM (obj, GET_TC(tc)->index);
+
+ GET_TC(tc)->itemValue = item;
+ GET_TC(tc)->index ++;
+ return 1;
+}
+
+void Tuple_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+}
+
+JSOBJ Tuple_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *Tuple_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ return NULL;
+}
+
+int Iter_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ PyObject *item;
+
+ if (GET_TC(tc)->itemValue)
+ {
+ Py_DECREF(GET_TC(tc)->itemValue);
+ GET_TC(tc)->itemValue = NULL;
+ }
+
+ if (GET_TC(tc)->iterator == NULL)
+ {
+ return 0;
+ }
+
+ item = PyIter_Next(GET_TC(tc)->iterator);
+
+ if (item == NULL)
+ {
+ return 0;
+ }
+
+ GET_TC(tc)->itemValue = item;
+ return 1;
+}
+
+void Iter_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ if (GET_TC(tc)->itemValue)
+ {
+ Py_DECREF(GET_TC(tc)->itemValue);
+ GET_TC(tc)->itemValue = NULL;
+ }
+
+ if (GET_TC(tc)->iterator)
+ {
+ Py_DECREF(GET_TC(tc)->iterator);
+ GET_TC(tc)->iterator = NULL;
+ }
+}
+
+JSOBJ Iter_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *Iter_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ return NULL;
+}
+
+void Dir_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ if (GET_TC(tc)->itemValue)
+ {
+ Py_DECREF(GET_TC(tc)->itemValue);
+ GET_TC(tc)->itemValue = NULL;
+ }
+
+ if (GET_TC(tc)->itemName)
+ {
+ Py_DECREF(GET_TC(tc)->itemName);
+ GET_TC(tc)->itemName = NULL;
+ }
+
+ Py_DECREF( (PyObject *) GET_TC(tc)->attrList);
+ PRINTMARK();
+}
+
+int Dir_iterNext(JSOBJ _obj, JSONTypeContext *tc)
+{
+ PyObject *obj = (PyObject *) _obj;
+ PyObject *itemValue = GET_TC(tc)->itemValue;
+ PyObject *itemName = GET_TC(tc)->itemName;
+ PyObject* attr;
+ PyObject* attrName;
+ char* attrStr;
+
+ if (itemValue)
+ {
+ Py_DECREF(GET_TC(tc)->itemValue);
+ GET_TC(tc)->itemValue = itemValue = NULL;
+ }
+
+ if (itemName)
+ {
+ Py_DECREF(GET_TC(tc)->itemName);
+ GET_TC(tc)->itemName = itemName = NULL;
+ }
+
+ for (; GET_TC(tc)->index < GET_TC(tc)->size; GET_TC(tc)->index ++)
+ {
+ attrName = PyList_GET_ITEM(GET_TC(tc)->attrList, GET_TC(tc)->index);
+#if PY_MAJOR_VERSION >= 3
+ attr = PyUnicode_AsUTF8String(attrName);
+#else
+ attr = attrName;
+ Py_INCREF(attr);
+#endif
+ attrStr = PyString_AS_STRING(attr);
+
+ if (attrStr[0] == '_')
+ {
+ PRINTMARK();
+ Py_DECREF(attr);
+ continue;
+ }
+
+ itemValue = PyObject_GetAttr(obj, attrName);
+ if (itemValue == NULL)
+ {
+ PyErr_Clear();
+ Py_DECREF(attr);
+ PRINTMARK();
+ continue;
+ }
+
+ if (PyCallable_Check(itemValue))
+ {
+ Py_DECREF(itemValue);
+ Py_DECREF(attr);
+ PRINTMARK();
+ continue;
+ }
+
+ PRINTMARK();
+ itemName = attr;
+ break;
+ }
+
+ if (itemName == NULL)
+ {
+ GET_TC(tc)->index = GET_TC(tc)->size;
+ GET_TC(tc)->itemValue = NULL;
+ return 0;
+ }
+
+ GET_TC(tc)->itemName = itemName;
+ GET_TC(tc)->itemValue = itemValue;
+ GET_TC(tc)->index ++;
+
+ PRINTMARK();
+ return 1;
+}
+
+JSOBJ Dir_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ PRINTMARK();
+ return GET_TC(tc)->itemValue;
+}
+
+char *Dir_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ PRINTMARK();
+ *outLen = PyString_GET_SIZE(GET_TC(tc)->itemName);
+ return PyString_AS_STRING(GET_TC(tc)->itemName);
+}
+
+int List_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ if (GET_TC(tc)->index >= GET_TC(tc)->size)
+ {
+ PRINTMARK();
+ return 0;
+ }
+
+ GET_TC(tc)->itemValue = PyList_GET_ITEM (obj, GET_TC(tc)->index);
+ GET_TC(tc)->index ++;
+ return 1;
+}
+
+void List_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+}
+
+JSOBJ List_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *List_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ return NULL;
+}
+
+//=============================================================================
+// Dict iteration functions
+// itemName might converted to string (Python_Str). Do refCounting
+// itemValue is borrowed from object (which is dict). No refCounting
+//=============================================================================
+
+int Dict_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject* itemNameTmp;
+#endif
+
+ if (GET_TC(tc)->itemName)
+ {
+ Py_DECREF(GET_TC(tc)->itemName);
+ GET_TC(tc)->itemName = NULL;
+ }
+
+
+ if (!PyDict_Next ( (PyObject *)GET_TC(tc)->dictObj, &GET_TC(tc)->index, &GET_TC(tc)->itemName, &GET_TC(tc)->itemValue))
+ {
+ PRINTMARK();
+ return 0;
+ }
+
+ if (PyUnicode_Check(GET_TC(tc)->itemName))
+ {
+ GET_TC(tc)->itemName = PyUnicode_AsUTF8String (GET_TC(tc)->itemName);
+ }
+ else
+ if (!PyString_Check(GET_TC(tc)->itemName))
+ {
+ GET_TC(tc)->itemName = PyObject_Str(GET_TC(tc)->itemName);
+#if PY_MAJOR_VERSION >= 3
+ itemNameTmp = GET_TC(tc)->itemName;
+ GET_TC(tc)->itemName = PyUnicode_AsUTF8String (GET_TC(tc)->itemName);
+ Py_DECREF(itemNameTmp);
+#endif
+ }
+ else
+ {
+ Py_INCREF(GET_TC(tc)->itemName);
+ }
+ PRINTMARK();
+ return 1;
+}
+
+void Dict_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ if (GET_TC(tc)->itemName)
+ {
+ Py_DECREF(GET_TC(tc)->itemName);
+ GET_TC(tc)->itemName = NULL;
+ }
+ Py_DECREF(GET_TC(tc)->dictObj);
+ PRINTMARK();
+}
+
+JSOBJ Dict_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *Dict_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ *outLen = PyString_GET_SIZE(GET_TC(tc)->itemName);
+ return PyString_AS_STRING(GET_TC(tc)->itemName);
+}
+
+int SortedDict_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ PyObject *items = NULL, *item = NULL, *key = NULL, *value = NULL;
+ Py_ssize_t i, nitems;
+#if PY_MAJOR_VERSION >= 3
+ PyObject* keyTmp;
+#endif
+
+ // Upon first call, obtain a list of the keys and sort them. This follows the same logic as the
+ // stanard library's _json.c sort_keys handler.
+ if (GET_TC(tc)->newObj == NULL)
+ {
+ // Obtain the list of keys from the dictionary.
+ items = PyMapping_Keys(GET_TC(tc)->dictObj);
+ if (items == NULL)
+ {
+ goto error;
+ }
+ else if (!PyList_Check(items))
+ {
+ PyErr_SetString(PyExc_ValueError, "keys must return list");
+ goto error;
+ }
+
+ // Sort the list.
+ if (PyList_Sort(items) < 0)
+ {
+ goto error;
+ }
+
+ // Obtain the value for each key, and pack a list of (key, value) 2-tuples.
+ nitems = PyList_GET_SIZE(items);
+ for (i = 0; i < nitems; i++)
+ {
+ key = PyList_GET_ITEM(items, i);
+ value = PyDict_GetItem(GET_TC(tc)->dictObj, key);
+
+ // Subject the key to the same type restrictions and conversions as in Dict_iterGetValue.
+ if (PyUnicode_Check(key))
+ {
+ key = PyUnicode_AsUTF8String(key);
+ }
+ else if (!PyString_Check(key))
+ {
+ key = PyObject_Str(key);
+#if PY_MAJOR_VERSION >= 3
+ keyTmp = key;
+ key = PyUnicode_AsUTF8String(key);
+ Py_DECREF(keyTmp);
+#endif
+ }
+ else
+ {
+ Py_INCREF(key);
+ }
+
+ item = PyTuple_Pack(2, key, value);
+ if (item == NULL)
+ {
+ goto error;
+ }
+ if (PyList_SetItem(items, i, item))
+ {
+ goto error;
+ }
+ Py_DECREF(key);
+ }
+
+ // Store the sorted list of tuples in the newObj slot.
+ GET_TC(tc)->newObj = items;
+ GET_TC(tc)->size = nitems;
+ }
+
+ if (GET_TC(tc)->index >= GET_TC(tc)->size)
+ {
+ PRINTMARK();
+ return 0;
+ }
+
+ item = PyList_GET_ITEM(GET_TC(tc)->newObj, GET_TC(tc)->index);
+ GET_TC(tc)->itemName = PyTuple_GET_ITEM(item, 0);
+ GET_TC(tc)->itemValue = PyTuple_GET_ITEM(item, 1);
+ GET_TC(tc)->index++;
+ return 1;
+
+error:
+ Py_XDECREF(item);
+ Py_XDECREF(key);
+ Py_XDECREF(value);
+ Py_XDECREF(items);
+ return -1;
+}
+
+void SortedDict_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ GET_TC(tc)->itemName = NULL;
+ GET_TC(tc)->itemValue = NULL;
+ Py_DECREF(GET_TC(tc)->dictObj);
+ PRINTMARK();
+}
+
+JSOBJ SortedDict_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->itemValue;
+}
+
+char *SortedDict_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ *outLen = PyString_GET_SIZE(GET_TC(tc)->itemName);
+ return PyString_AS_STRING(GET_TC(tc)->itemName);
+}
+
+
+void SetupDictIter(PyObject *dictObj, TypeContext *pc, JSONObjectEncoder *enc)
+{
+ if (enc->sortKeys) {
+ pc->iterEnd = SortedDict_iterEnd;
+ pc->iterNext = SortedDict_iterNext;
+ pc->iterGetValue = SortedDict_iterGetValue;
+ pc->iterGetName = SortedDict_iterGetName;
+ }
+ else {
+ pc->iterEnd = Dict_iterEnd;
+ pc->iterNext = Dict_iterNext;
+ pc->iterGetValue = Dict_iterGetValue;
+ pc->iterGetName = Dict_iterGetName;
+ }
+ pc->dictObj = dictObj;
+ pc->index = 0;
+}
+
+void Object_beginTypeContext (JSOBJ _obj, JSONTypeContext *tc, JSONObjectEncoder *enc)
+{
+ PyObject *obj, *exc, *iter;
+ TypeContext *pc;
+ PRINTMARK();
+ if (!_obj) {
+ tc->type = JT_INVALID;
+ return;
+ }
+
+ obj = (PyObject*) _obj;
+
+ tc->prv = PyObject_Malloc(sizeof(TypeContext));
+ pc = (TypeContext *) tc->prv;
+ if (!pc)
+ {
+ tc->type = JT_INVALID;
+ PyErr_NoMemory();
+ return;
+ }
+ pc->newObj = NULL;
+ pc->dictObj = NULL;
+ pc->itemValue = NULL;
+ pc->itemName = NULL;
+ pc->iterator = NULL;
+ pc->attrList = NULL;
+ pc->index = 0;
+ pc->size = 0;
+ pc->longValue = 0;
+ pc->rawJSONValue = NULL;
+
+ if (PyIter_Check(obj))
+ {
+ PRINTMARK();
+ goto ISITERABLE;
+ }
+
+ if (PyBool_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = (obj == Py_True) ? JT_TRUE : JT_FALSE;
+ return;
+ }
+ else
+ if (PyLong_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyLongToINT64;
+ tc->type = JT_LONG;
+ GET_TC(tc)->longValue = PyLong_AsLongLong(obj);
+
+ exc = PyErr_Occurred();
+ if (!exc)
+ {
+ return;
+ }
+
+ if (exc && PyErr_ExceptionMatches(PyExc_OverflowError))
+ {
+ PyErr_Clear();
+ pc->PyTypeToJSON = PyLongToUINT64;
+ tc->type = JT_ULONG;
+ GET_TC(tc)->unsignedLongValue = PyLong_AsUnsignedLongLong(obj);
+
+ exc = PyErr_Occurred();
+ if (exc && PyErr_ExceptionMatches(PyExc_OverflowError))
+ {
+ PRINTMARK();
+ goto INVALID;
+ }
+ }
+
+ return;
+ }
+ else
+ if (PyInt_Check(obj))
+ {
+ PRINTMARK();
+#ifdef _LP64
+ pc->PyTypeToJSON = PyIntToINT64; tc->type = JT_LONG;
+#else
+ pc->PyTypeToJSON = PyIntToINT32; tc->type = JT_INT;
+#endif
+ return;
+ }
+ else
+ if (PyString_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyStringToUTF8; tc->type = JT_UTF8;
+ return;
+ }
+ else
+ if (PyUnicode_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyUnicodeToUTF8; tc->type = JT_UTF8;
+ return;
+ }
+ else
+ if (PyFloat_Check(obj) || (type_decimal && PyObject_IsInstance(obj, type_decimal)))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyFloatToDOUBLE; tc->type = JT_DOUBLE;
+ return;
+ }
+ else
+ if (PyDateTime_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyDateTimeToINT64; tc->type = JT_LONG;
+ return;
+ }
+ else
+ if (PyDate_Check(obj))
+ {
+ PRINTMARK();
+ pc->PyTypeToJSON = PyDateToINT64; tc->type = JT_LONG;
+ return;
+ }
+ else
+ if (obj == Py_None)
+ {
+ PRINTMARK();
+ tc->type = JT_NULL;
+ return;
+ }
+
+ISITERABLE:
+ if (PyDict_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = JT_OBJECT;
+ SetupDictIter(obj, pc, enc);
+ Py_INCREF(obj);
+ return;
+ }
+ else
+ if (PyList_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = JT_ARRAY;
+ pc->iterEnd = List_iterEnd;
+ pc->iterNext = List_iterNext;
+ pc->iterGetValue = List_iterGetValue;
+ pc->iterGetName = List_iterGetName;
+ GET_TC(tc)->index = 0;
+ GET_TC(tc)->size = PyList_GET_SIZE( (PyObject *) obj);
+ return;
+ }
+ else
+ if (PyTuple_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = JT_ARRAY;
+ pc->iterEnd = Tuple_iterEnd;
+ pc->iterNext = Tuple_iterNext;
+ pc->iterGetValue = Tuple_iterGetValue;
+ pc->iterGetName = Tuple_iterGetName;
+ GET_TC(tc)->index = 0;
+ GET_TC(tc)->size = PyTuple_GET_SIZE( (PyObject *) obj);
+ GET_TC(tc)->itemValue = NULL;
+
+ return;
+ }
+ /*
+ else
+ if (PyAnySet_Check(obj))
+ {
+ PRINTMARK();
+ tc->type = JT_ARRAY;
+ pc->iterBegin = NULL;
+ pc->iterEnd = Iter_iterEnd;
+ pc->iterNext = Iter_iterNext;
+ pc->iterGetValue = Iter_iterGetValue;
+ pc->iterGetName = Iter_iterGetName;
+ return;
+ }
+ */
+
+ if (PyObject_HasAttrString(obj, "toDict"))
+ {
+ PyObject* toDictFunc = PyObject_GetAttrString(obj, "toDict");
+ PyObject* tuple = PyTuple_New(0);
+ PyObject* toDictResult = PyObject_Call(toDictFunc, tuple, NULL);
+ Py_DECREF(tuple);
+ Py_DECREF(toDictFunc);
+
+ if (toDictResult == NULL)
+ {
+ goto INVALID;
+ }
+
+ if (!PyDict_Check(toDictResult))
+ {
+ Py_DECREF(toDictResult);
+ tc->type = JT_NULL;
+ return;
+ }
+
+ PRINTMARK();
+ tc->type = JT_OBJECT;
+ SetupDictIter(toDictResult, pc, enc);
+ return;
+ }
+ else
+ if (PyObject_HasAttrString(obj, "__json__"))
+ {
+ PyObject* toJSONFunc = PyObject_GetAttrString(obj, "__json__");
+ PyObject* tuple = PyTuple_New(0);
+ PyObject* toJSONResult = PyObject_Call(toJSONFunc, tuple, NULL);
+ Py_DECREF(tuple);
+ Py_DECREF(toJSONFunc);
+
+ if (toJSONResult == NULL)
+ {
+ goto INVALID;
+ }
+
+ if (PyErr_Occurred())
+ {
+ Py_DECREF(toJSONResult);
+ goto INVALID;
+ }
+
+ if (!PyString_Check(toJSONResult) && !PyUnicode_Check(toJSONResult))
+ {
+ Py_DECREF(toJSONResult);
+ PyErr_Format (PyExc_TypeError, "expected string");
+ goto INVALID;
+ }
+
+ PRINTMARK();
+ pc->PyTypeToJSON = PyRawJSONToUTF8;
+ tc->type = JT_RAW;
+ GET_TC(tc)->rawJSONValue = toJSONResult;
+ return;
+ }
+
+ PRINTMARK();
+ PyErr_Clear();
+
+ iter = PyObject_GetIter(obj);
+
+ if (iter != NULL)
+ {
+ PRINTMARK();
+ tc->type = JT_ARRAY;
+ pc->iterator = iter;
+ pc->iterEnd = Iter_iterEnd;
+ pc->iterNext = Iter_iterNext;
+ pc->iterGetValue = Iter_iterGetValue;
+ pc->iterGetName = Iter_iterGetName;
+ return;
+ }
+
+ PRINTMARK();
+ PyErr_Clear();
+
+ PRINTMARK();
+ tc->type = JT_OBJECT;
+ GET_TC(tc)->attrList = PyObject_Dir(obj);
+
+ if (GET_TC(tc)->attrList == NULL)
+ {
+ PyErr_Clear();
+ goto INVALID;
+ }
+
+ GET_TC(tc)->index = 0;
+ GET_TC(tc)->size = PyList_GET_SIZE(GET_TC(tc)->attrList);
+ PRINTMARK();
+
+ pc->iterEnd = Dir_iterEnd;
+ pc->iterNext = Dir_iterNext;
+ pc->iterGetValue = Dir_iterGetValue;
+ pc->iterGetName = Dir_iterGetName;
+ return;
+
+INVALID:
+ PRINTMARK();
+ tc->type = JT_INVALID;
+ PyObject_Free(tc->prv);
+ tc->prv = NULL;
+ return;
+}
+
+void Object_endTypeContext(JSOBJ obj, JSONTypeContext *tc)
+{
+ Py_XDECREF(GET_TC(tc)->newObj);
+
+ if (tc->type == JT_RAW)
+ {
+ Py_XDECREF(GET_TC(tc)->rawJSONValue);
+ }
+ PyObject_Free(tc->prv);
+ tc->prv = NULL;
+}
+
+const char *Object_getStringValue(JSOBJ obj, JSONTypeContext *tc, size_t *_outLen)
+{
+ return GET_TC(tc)->PyTypeToJSON (obj, tc, NULL, _outLen);
+}
+
+JSINT64 Object_getLongValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ JSINT64 ret;
+ GET_TC(tc)->PyTypeToJSON (obj, tc, &ret, NULL);
+ return ret;
+}
+
+JSUINT64 Object_getUnsignedLongValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ JSUINT64 ret;
+ GET_TC(tc)->PyTypeToJSON (obj, tc, &ret, NULL);
+ return ret;
+}
+
+JSINT32 Object_getIntValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ JSINT32 ret;
+ GET_TC(tc)->PyTypeToJSON (obj, tc, &ret, NULL);
+ return ret;
+}
+
+double Object_getDoubleValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ double ret;
+ GET_TC(tc)->PyTypeToJSON (obj, tc, &ret, NULL);
+ return ret;
+}
+
+static void Object_releaseObject(JSOBJ _obj)
+{
+ Py_DECREF( (PyObject *) _obj);
+}
+
+int Object_iterNext(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->iterNext(obj, tc);
+}
+
+void Object_iterEnd(JSOBJ obj, JSONTypeContext *tc)
+{
+ GET_TC(tc)->iterEnd(obj, tc);
+}
+
+JSOBJ Object_iterGetValue(JSOBJ obj, JSONTypeContext *tc)
+{
+ return GET_TC(tc)->iterGetValue(obj, tc);
+}
+
+char *Object_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
+{
+ return GET_TC(tc)->iterGetName(obj, tc, outLen);
+}
+
+PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "obj", "ensure_ascii", "double_precision", "encode_html_chars", "escape_forward_slashes", "sort_keys", "indent", NULL };
+
+ char buffer[65536];
+ char *ret;
+ PyObject *newobj;
+ PyObject *oinput = NULL;
+ PyObject *oensureAscii = NULL;
+ PyObject *oencodeHTMLChars = NULL;
+ PyObject *oescapeForwardSlashes = NULL;
+ PyObject *osortKeys = NULL;
+
+ JSONObjectEncoder encoder =
+ {
+ Object_beginTypeContext,
+ Object_endTypeContext,
+ Object_getStringValue,
+ Object_getLongValue,
+ Object_getUnsignedLongValue,
+ Object_getIntValue,
+ Object_getDoubleValue,
+ Object_iterNext,
+ Object_iterEnd,
+ Object_iterGetValue,
+ Object_iterGetName,
+ Object_releaseObject,
+ PyObject_Malloc,
+ PyObject_Realloc,
+ PyObject_Free,
+ -1, //recursionMax
+ 10, // default double precision setting
+ 1, //forceAscii
+ 0, //encodeHTMLChars
+ 1, //escapeForwardSlashes
+ 0, //sortKeys
+ 0, //indent
+ NULL, //prv
+ };
+
+
+ PRINTMARK();
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OiOOOi", kwlist, &oinput, &oensureAscii, &encoder.doublePrecision, &oencodeHTMLChars, &oescapeForwardSlashes, &osortKeys, &encoder.indent))
+ {
+ return NULL;
+ }
+
+ if (oensureAscii != NULL && !PyObject_IsTrue(oensureAscii))
+ {
+ encoder.forceASCII = 0;
+ }
+
+ if (oencodeHTMLChars != NULL && PyObject_IsTrue(oencodeHTMLChars))
+ {
+ encoder.encodeHTMLChars = 1;
+ }
+
+ if (oescapeForwardSlashes != NULL && !PyObject_IsTrue(oescapeForwardSlashes))
+ {
+ encoder.escapeForwardSlashes = 0;
+ }
+
+ if (osortKeys != NULL && PyObject_IsTrue(osortKeys))
+ {
+ encoder.sortKeys = 1;
+ }
+
+ PRINTMARK();
+ ret = JSON_EncodeObject (oinput, &encoder, buffer, sizeof (buffer));
+ PRINTMARK();
+
+ if (PyErr_Occurred())
+ {
+ return NULL;
+ }
+
+ if (encoder.errorMsg)
+ {
+ if (ret != buffer)
+ {
+ encoder.free (ret);
+ }
+
+ PyErr_Format (PyExc_OverflowError, "%s", encoder.errorMsg);
+ return NULL;
+ }
+
+ newobj = PyString_FromString (ret);
+
+ if (ret != buffer)
+ {
+ encoder.free (ret);
+ }
+
+ PRINTMARK();
+
+ return newobj;
+}
+
+PyObject* objToJSONFile(PyObject* self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *data;
+ PyObject *file;
+ PyObject *string;
+ PyObject *write;
+ PyObject *argtuple;
+ PyObject *write_result;
+
+ PRINTMARK();
+
+ if (!PyArg_ParseTuple (args, "OO", &data, &file))
+ {
+ return NULL;
+ }
+
+ if (!PyObject_HasAttrString (file, "write"))
+ {
+ PyErr_Format (PyExc_TypeError, "expected file");
+ return NULL;
+ }
+
+ write = PyObject_GetAttrString (file, "write");
+
+ if (!PyCallable_Check (write))
+ {
+ Py_XDECREF(write);
+ PyErr_Format (PyExc_TypeError, "expected file");
+ return NULL;
+ }
+
+ argtuple = PyTuple_Pack(1, data);
+
+ string = objToJSON (self, argtuple, kwargs);
+
+ if (string == NULL)
+ {
+ Py_XDECREF(write);
+ Py_XDECREF(argtuple);
+ return NULL;
+ }
+
+ Py_XDECREF(argtuple);
+
+ argtuple = PyTuple_Pack (1, string);
+ if (argtuple == NULL)
+ {
+ Py_XDECREF(write);
+ return NULL;
+ }
+
+ write_result = PyObject_CallObject (write, argtuple);
+ if (write_result == NULL)
+ {
+ Py_XDECREF(write);
+ Py_XDECREF(argtuple);
+ return NULL;
+ }
+
+ Py_DECREF(write_result);
+ Py_XDECREF(write);
+ Py_DECREF(argtuple);
+ Py_XDECREF(string);
+
+ PRINTMARK();
+
+ Py_RETURN_NONE;
+}
diff --git a/contrib/python/ujson/py3/python/py_defines.h b/contrib/python/ujson/py3/python/py_defines.h
new file mode 100644
index 0000000000..2b38b41bdb
--- /dev/null
+++ b/contrib/python/ujson/py3/python/py_defines.h
@@ -0,0 +1,53 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include <Python.h>
+
+#if PY_MAJOR_VERSION >= 3
+
+#define PyInt_Check PyLong_Check
+#define PyInt_AS_LONG PyLong_AsLong
+#define PyInt_FromLong PyLong_FromLong
+
+#define PyString_Check PyBytes_Check
+#define PyString_GET_SIZE PyBytes_GET_SIZE
+#define PyString_AS_STRING PyBytes_AS_STRING
+
+#define PyString_FromString PyUnicode_FromString
+
+#endif
diff --git a/contrib/python/ujson/py3/python/ujson.c b/contrib/python/ujson/py3/python/ujson.c
new file mode 100644
index 0000000000..d0b15c65cf
--- /dev/null
+++ b/contrib/python/ujson/py3/python/ujson.c
@@ -0,0 +1,113 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+* Copyright (c) 1988-1993 The Regents of the University of California.
+* Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#include "py_defines.h"
+#include "version.h"
+
+/* objToJSON */
+PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs);
+void initObjToJSON(void);
+
+/* JSONToObj */
+PyObject* JSONToObj(PyObject* self, PyObject *args, PyObject *kwargs);
+
+/* objToJSONFile */
+PyObject* objToJSONFile(PyObject* self, PyObject *args, PyObject *kwargs);
+
+/* JSONFileToObj */
+PyObject* JSONFileToObj(PyObject* self, PyObject *args, PyObject *kwargs);
+
+
+#define ENCODER_HELP_TEXT "Use ensure_ascii=false to output UTF-8. Pass in double_precision to alter the maximum digit precision of doubles. Set encode_html_chars=True to encode < > & as unicode escape sequences. Set escape_forward_slashes=False to prevent escaping / characters."
+
+static PyMethodDef ujsonMethods[] = {
+ {"encode", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON. " ENCODER_HELP_TEXT},
+ {"decode", (PyCFunction) JSONToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as string to dict object structure. Use precise_float=True to use high precision float decoder."},
+ {"dumps", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON. " ENCODER_HELP_TEXT},
+ {"loads", (PyCFunction) JSONToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as string to dict object structure. Use precise_float=True to use high precision float decoder."},
+ {"dump", (PyCFunction) objToJSONFile, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON file. " ENCODER_HELP_TEXT},
+ {"load", (PyCFunction) JSONFileToObj, METH_VARARGS | METH_KEYWORDS, "Converts JSON as file to dict object structure. Use precise_float=True to use high precision float decoder."},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "ujson",
+ 0, /* m_doc */
+ -1, /* m_size */
+ ujsonMethods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
+};
+
+#define PYMODINITFUNC PyObject *PyInit_ujson(void)
+#define PYMODULE_CREATE() PyModule_Create(&moduledef)
+#define MODINITERROR return NULL
+
+#else
+
+#define PYMODINITFUNC PyMODINIT_FUNC initujson(void)
+#define PYMODULE_CREATE() Py_InitModule("ujson", ujsonMethods)
+#define MODINITERROR return
+
+#endif
+
+PYMODINITFUNC
+{
+ PyObject *module;
+ PyObject *version_string;
+
+ initObjToJSON();
+ module = PYMODULE_CREATE();
+
+ if (module == NULL)
+ {
+ MODINITERROR;
+ }
+
+ version_string = PyString_FromString (UJSON_VERSION);
+ PyModule_AddObject (module, "__version__", version_string);
+
+#if PY_MAJOR_VERSION >= 3
+ return module;
+#endif
+}
diff --git a/contrib/python/ujson/py3/python/version.h b/contrib/python/ujson/py3/python/version.h
new file mode 100644
index 0000000000..f0ce6bb733
--- /dev/null
+++ b/contrib/python/ujson/py3/python/version.h
@@ -0,0 +1,39 @@
+/*
+Developed by ESN, an Electronic Arts Inc. studio.
+Copyright (c) 2014, Electronic Arts Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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.
+* Neither the name of ESN, Electronic Arts Inc. nor the
+names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 ELECTRONIC ARTS INC. 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.
+
+
+Portions of code from MODP_ASCII - Ascii transformations (upper/lower, etc)
+http://code.google.com/p/stringencoders/
+Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
+
+Numeric decoder derived from from TCL library
+http://www.opensource.apple.com/source/tcl/tcl-14/tcl/license.terms
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+*/
+
+#define UJSON_VERSION "1.35"
diff --git a/contrib/python/ujson/py3/ya.make b/contrib/python/ujson/py3/ya.make
new file mode 100644
index 0000000000..668463b313
--- /dev/null
+++ b/contrib/python/ujson/py3/ya.make
@@ -0,0 +1,30 @@
+PY3_LIBRARY()
+
+LICENSE(BSD-3-Clause)
+
+VERSION(1.35+dev)
+
+NO_COMPILER_WARNINGS()
+NO_UTIL()
+
+PY_REGISTER(ujson)
+
+ADDINCL(
+ contrib/python/ujson/py3/lib
+ contrib/python/ujson/py3/python
+)
+
+SRCS(
+ lib/ultrajsondec.c
+ lib/ultrajsonenc.c
+ python/JSONtoObj.c
+ python/objToJSON.c
+ python/ujson.c
+)
+
+PY_SRCS(
+ TOP_LEVEL
+ ujson.pyi
+)
+
+END()
diff --git a/contrib/python/ujson/ya.make b/contrib/python/ujson/ya.make
new file mode 100644
index 0000000000..0503b6b21b
--- /dev/null
+++ b/contrib/python/ujson/ya.make
@@ -0,0 +1,18 @@
+PY23_LIBRARY()
+
+LICENSE(Service-Py23-Proxy)
+
+IF (PYTHON2)
+ PEERDIR(contrib/python/ujson/py2)
+ELSE()
+ PEERDIR(contrib/python/ujson/py3)
+ENDIF()
+
+NO_LINT()
+
+END()
+
+RECURSE(
+ py2
+ py3
+)
diff --git a/contrib/tools/ragel5/common/buffer.h b/contrib/tools/ragel5/common/buffer.h
new file mode 100644
index 0000000000..99c4e82d49
--- /dev/null
+++ b/contrib/tools/ragel5/common/buffer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2003 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _BUFFER_H
+#define _BUFFER_H
+
+#define BUFFER_INITIAL_SIZE 4096
+
+/* An automatically grown buffer for collecting tokens. Always reuses space;
+ * never down resizes. */
+struct Buffer
+{
+ Buffer()
+ {
+ data = (char*) malloc( BUFFER_INITIAL_SIZE );
+ allocated = BUFFER_INITIAL_SIZE;
+ length = 0;
+ }
+ ~Buffer() { free(data); }
+
+ void append( char p )
+ {
+ if ( length == allocated ) {
+ allocated *= 2;
+ data = (char*) realloc( data, allocated );
+ }
+ data[length++] = p;
+ }
+
+ void clear() { length = 0; }
+
+ char *data;
+ int allocated;
+ int length;
+};
+
+#endif /* _BUFFER_H */
diff --git a/contrib/tools/ragel5/common/common.cpp b/contrib/tools/ragel5/common/common.cpp
new file mode 100644
index 0000000000..4484dcbd73
--- /dev/null
+++ b/contrib/tools/ragel5/common/common.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2006-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "pcheck.h"
+#include "common.h"
+#include <string.h>
+#include <assert.h>
+
+#ifdef _WIN32
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+
+HostType hostTypesC[] =
+{
+ { "char", 0, true, CHAR_MIN, CHAR_MAX, sizeof(char) },
+ { "unsigned", "char", false, 0, UCHAR_MAX, sizeof(unsigned char) },
+ { "short", 0, true, SHRT_MIN, SHRT_MAX, sizeof(short) },
+ { "unsigned", "short", false, 0, USHRT_MAX, sizeof(unsigned short) },
+ { "int", 0, true, INT_MIN, INT_MAX, sizeof(int) },
+ { "unsigned", "int", false, 0, UINT_MAX, sizeof(unsigned int) },
+ { "long", 0, true, LONG_MIN, LONG_MAX, sizeof(long) },
+ { "unsigned", "long", false, 0, (long long)ULONG_MAX, sizeof(unsigned long) }
+};
+
+HostType hostTypesD[] =
+{
+ { "byte", 0, true, CHAR_MIN, CHAR_MAX, 1 },
+ { "ubyte", 0, false, 0, UCHAR_MAX, 1 },
+ { "char", 0, false, 0, UCHAR_MAX, 1 },
+ { "short", 0, true, SHRT_MIN, SHRT_MAX, 2 },
+ { "ushort", 0, false, 0, USHRT_MAX, 2 },
+ { "wchar", 0, false, 0, USHRT_MAX, 2 },
+ { "int", 0, true, INT_MIN, INT_MAX, 4 },
+ { "uint", 0, false, 0, UINT_MAX, 4 },
+ { "dchar", 0, false, 0, UINT_MAX, 4 }
+};
+
+HostType hostTypesJava[] =
+{
+ { "byte", 0, true, CHAR_MIN, CHAR_MAX, 1 },
+ { "short", 0, true, SHRT_MIN, SHRT_MAX, 2 },
+ { "char", 0, false, 0, USHRT_MAX, 2 },
+ { "int", 0, true, INT_MIN, INT_MAX, 4 },
+};
+
+HostType hostTypesRuby[] =
+{
+ { "byte", 0, true, CHAR_MIN, CHAR_MAX, 1 },
+ { "short", 0, true, SHRT_MIN, SHRT_MAX, 2 },
+ { "char", 0, false, 0, USHRT_MAX, 2 },
+ { "int", 0, true, INT_MIN, INT_MAX, 4 },
+};
+
+HostLang hostLangC = { hostTypesC, 8, hostTypesC+0, true };
+HostLang hostLangD = { hostTypesD, 9, hostTypesD+2, true };
+HostLang hostLangJava = { hostTypesJava, 4, hostTypesJava+2, false };
+HostLang hostLangRuby = { hostTypesRuby, 4, hostTypesRuby+2, false };
+
+HostLang *hostLang = &hostLangC;
+HostLangType hostLangType = CCode;
+
+/* Construct a new parameter checker with for paramSpec. */
+ParamCheck::ParamCheck(const char *paramSpec, int argc, char **argv)
+:
+ state(noparam),
+ argOffset(0),
+ curArg(0),
+ iCurArg(1),
+ paramSpec(paramSpec),
+ argc(argc),
+ argv(argv)
+{
+}
+
+/* Check a single option. Returns the index of the next parameter. Sets p to
+ * the arg character if valid, 0 otherwise. Sets parg to the parameter arg if
+ * there is one, NULL otherwise. */
+bool ParamCheck::check()
+{
+ bool requiresParam;
+
+ if ( iCurArg >= argc ) { /* Off the end of the arg list. */
+ state = noparam;
+ return false;
+ }
+
+ if ( argOffset != 0 && *argOffset == 0 ) {
+ /* We are at the end of an arg string. */
+ iCurArg += 1;
+ if ( iCurArg >= argc ) {
+ state = noparam;
+ return false;
+ }
+ argOffset = 0;
+ }
+
+ if ( argOffset == 0 ) {
+ /* Set the current arg. */
+ curArg = argv[iCurArg];
+
+ /* We are at the beginning of an arg string. */
+ if ( argv[iCurArg] == 0 || /* Argv[iCurArg] is null. */
+ argv[iCurArg][0] != '-' || /* Not a param. */
+ argv[iCurArg][1] == 0 ) { /* Only a dash. */
+ parameter = 0;
+ parameterArg = 0;
+
+ iCurArg += 1;
+ state = noparam;
+ return true;
+ }
+ argOffset = argv[iCurArg] + 1;
+ }
+
+ /* Get the arg char. */
+ char argChar = *argOffset;
+
+ /* Loop over all the parms and look for a match. */
+ const char *pSpec = paramSpec;
+ while ( *pSpec != 0 ) {
+ char pSpecChar = *pSpec;
+
+ /* If there is a ':' following the char then
+ * it requires a parm. If a parm is required
+ * then move ahead two in the parmspec. Otherwise
+ * move ahead one in the parm spec. */
+ if ( pSpec[1] == ':' ) {
+ requiresParam = true;
+ pSpec += 2;
+ }
+ else {
+ requiresParam = false;
+ pSpec += 1;
+ }
+
+ /* Do we have a match. */
+ if ( argChar == pSpecChar ) {
+ if ( requiresParam ) {
+ if ( argOffset[1] == 0 ) {
+ /* The param must follow. */
+ if ( iCurArg + 1 == argc ) {
+ /* We are the last arg so there
+ * cannot be a parameter to it. */
+ parameter = argChar;
+ parameterArg = 0;
+ iCurArg += 1;
+ argOffset = 0;
+ state = invalid;
+ return true;
+ }
+ else {
+ /* the parameter to the arg is the next arg. */
+ parameter = pSpecChar;
+ parameterArg = argv[iCurArg + 1];
+ iCurArg += 2;
+ argOffset = 0;
+ state = match;
+ return true;
+ }
+ }
+ else {
+ /* The param for the arg is built in. */
+ parameter = pSpecChar;
+ parameterArg = argOffset + 1;
+ iCurArg += 1;
+ argOffset = 0;
+ state = match;
+ return true;
+ }
+ }
+ else {
+ /* Good, we matched the parm and no
+ * arg is required. */
+ parameter = pSpecChar;
+ parameterArg = 0;
+ argOffset += 1;
+ state = match;
+ return true;
+ }
+ }
+ }
+
+ /* We did not find a match. Bad Argument. */
+ parameter = argChar;
+ parameterArg = 0;
+ argOffset += 1;
+ state = invalid;
+ return true;
+}
+
+void NormalizeWinPath(char* input) {
+ const size_t len = strlen(input);
+ char* res = static_cast<char*>(alloca(len + 1));
+ for (size_t i = 0, j = 0; i <= len; ++i, ++j) {
+ if (input[i] == '\\') {
+ res[j] = '/';
+ if (i < len - 2 && input[i + 1] == '\\')
+ ++i;
+ } else {
+ res[j] = input[i];
+ }
+ }
+ strcpy(input, res);
+}
+
+/* Counts newlines before sending sync. */
+int output_filter::sync( )
+{
+ line += 1;
+ return std::filebuf::sync();
+}
+
+/* Counts newlines before sending data out to file. */
+std::streamsize output_filter::xsputn( const char *s, std::streamsize n )
+{
+ for ( int i = 0; i < n; i++ ) {
+ if ( s[i] == '\n' )
+ line += 1;
+ }
+ return std::filebuf::xsputn( s, n );
+}
+
+/* Scans a string looking for the file extension. If there is a file
+ * extension then pointer returned points to inside the string
+ * passed in. Otherwise returns null. */
+char *findFileExtension( char *stemFile )
+{
+ char *ppos = stemFile + strlen(stemFile) - 1;
+
+ /* Scan backwards from the end looking for the first dot.
+ * If we encounter a '/' before the first dot, then stop the scan. */
+ while ( 1 ) {
+ /* If we found a dot or got to the beginning of the string then
+ * we are done. */
+ if ( ppos == stemFile || *ppos == '.' )
+ break;
+
+ /* If we hit a / then there is no extension. Done. */
+ if ( *ppos == '/' ) {
+ ppos = stemFile;
+ break;
+ }
+ ppos--;
+ }
+
+ /* If we got to the front of the string then bail we
+ * did not find an extension */
+ if ( ppos == stemFile )
+ ppos = 0;
+
+ return ppos;
+}
+
+/* Make a file name from a stem. Removes the old filename suffix and
+ * replaces it with a new one. Returns a newed up string. */
+char *fileNameFromStem( char *stemFile, const char *suffix )
+{
+ int len = strlen( stemFile );
+ assert( len > 0 );
+
+ /* Get the extension. */
+ char *ppos = findFileExtension( stemFile );
+
+ /* If an extension was found, then shorten what we think the len is. */
+ if ( ppos != 0 )
+ len = ppos - stemFile;
+
+ /* Make the return string from the stem and the suffix. */
+ char *retVal = new char[ len + strlen( suffix ) + 1 ];
+ strncpy( retVal, stemFile, len );
+ strcpy( retVal + len, suffix );
+
+ return retVal;
+}
+
+
diff --git a/contrib/tools/ragel5/common/common.h b/contrib/tools/ragel5/common/common.h
new file mode 100644
index 0000000000..aae6f85add
--- /dev/null
+++ b/contrib/tools/ragel5/common/common.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#include <fstream>
+#include <climits>
+#include "dlist.h"
+
+typedef unsigned long long Size;
+
+struct Key
+{
+private:
+ long key;
+
+public:
+ friend inline Key operator+(const Key key1, const Key key2);
+ friend inline Key operator-(const Key key1, const Key key2);
+ friend inline Key operator/(const Key key1, const Key key2);
+ friend inline long operator&(const Key key1, const Key key2);
+
+ friend inline bool operator<( const Key key1, const Key key2 );
+ friend inline bool operator<=( const Key key1, const Key key2 );
+ friend inline bool operator>( const Key key1, const Key key2 );
+ friend inline bool operator>=( const Key key1, const Key key2 );
+ friend inline bool operator==( const Key key1, const Key key2 );
+ friend inline bool operator!=( const Key key1, const Key key2 );
+
+ friend struct KeyOps;
+
+ Key( ) {}
+ Key( const Key &key ) : key(key.key) {}
+ Key( long key ) : key(key) {}
+
+ /* Returns the value used to represent the key. This value must be
+ * interpreted based on signedness. */
+ long getVal() const { return key; };
+
+ /* Returns the key casted to a long long. This form of the key does not
+ * require and signedness interpretation. */
+ long long getLongLong() const;
+
+ bool isUpper() const { return ( 'A' <= key && key <= 'Z' ); }
+ bool isLower() const { return ( 'a' <= key && key <= 'z' ); }
+ bool isPrintable() const
+ {
+ return ( 7 <= key && key <= 13 ) || ( 32 <= key && key < 127 );
+ }
+
+ Key toUpper() const
+ { return Key( 'A' + ( key - 'a' ) ); }
+ Key toLower() const
+ { return Key( 'a' + ( key - 'A' ) ); }
+
+ void operator+=( const Key other )
+ {
+ /* FIXME: must be made aware of isSigned. */
+ key += other.key;
+ }
+
+ void operator-=( const Key other )
+ {
+ /* FIXME: must be made aware of isSigned. */
+ key -= other.key;
+ }
+
+ void operator|=( const Key other )
+ {
+ /* FIXME: must be made aware of isSigned. */
+ key |= other.key;
+ }
+
+ /* Decrement. Needed only for ranges. */
+ inline void decrement();
+ inline void increment();
+};
+
+struct HostType
+{
+ const char *data1;
+ const char *data2;
+ bool isSigned;
+ long long minVal;
+ long long maxVal;
+ unsigned int size;
+};
+
+struct HostLang
+{
+ HostType *hostTypes;
+ int numHostTypes;
+ HostType *defaultAlphType;
+ bool explicitUnsigned;
+};
+
+
+/* Target language. */
+enum HostLangType
+{
+ CCode,
+ DCode,
+ JavaCode,
+ RubyCode
+};
+
+extern HostLang *hostLang;
+extern HostLangType hostLangType;
+
+extern HostLang hostLangC;
+extern HostLang hostLangD;
+extern HostLang hostLangJava;
+extern HostLang hostLangRuby;
+
+/* An abstraction of the key operators that manages key operations such as
+ * comparison and increment according the signedness of the key. */
+struct KeyOps
+{
+ /* Default to signed alphabet. */
+ KeyOps() :
+ isSigned(true),
+ alphType(0)
+ {}
+
+ /* Default to signed alphabet. */
+ KeyOps( bool isSigned )
+ :isSigned(isSigned) {}
+
+ bool isSigned;
+ Key minKey, maxKey;
+ HostType *alphType;
+
+ void setAlphType( HostType *alphType )
+ {
+ this->alphType = alphType;
+ isSigned = alphType->isSigned;
+ if ( isSigned ) {
+ minKey = (long) alphType->minVal;
+ maxKey = (long) alphType->maxVal;
+ }
+ else {
+ minKey = (long) (unsigned long) alphType->minVal;
+ maxKey = (long) (unsigned long) alphType->maxVal;
+ }
+ }
+
+ /* Compute the distance between two keys. */
+ Size span( Key key1, Key key2 )
+ {
+ return isSigned ?
+ (unsigned long long)(
+ (long long)key2.key -
+ (long long)key1.key + 1) :
+ (unsigned long long)(
+ (unsigned long)key2.key) -
+ (unsigned long long)((unsigned long)key1.key) + 1;
+ }
+
+ Size alphSize()
+ { return span( minKey, maxKey ); }
+
+ HostType *typeSubsumes( long long maxVal )
+ {
+ for ( int i = 0; i < hostLang->numHostTypes; i++ ) {
+ if ( maxVal <= hostLang->hostTypes[i].maxVal )
+ return hostLang->hostTypes + i;
+ }
+ return 0;
+ }
+
+ HostType *typeSubsumes( bool isSigned, long long maxVal )
+ {
+ for ( int i = 0; i < hostLang->numHostTypes; i++ ) {
+ if ( ( isSigned == hostLang->hostTypes[i].isSigned ) &&
+ maxVal <= hostLang->hostTypes[i].maxVal )
+ return hostLang->hostTypes + i;
+ }
+ return 0;
+ }
+};
+
+extern KeyOps *keyOps;
+
+inline bool operator<( const Key key1, const Key key2 )
+{
+ return keyOps->isSigned ? key1.key < key2.key :
+ (unsigned long)key1.key < (unsigned long)key2.key;
+}
+
+inline bool operator<=( const Key key1, const Key key2 )
+{
+ return keyOps->isSigned ? key1.key <= key2.key :
+ (unsigned long)key1.key <= (unsigned long)key2.key;
+}
+
+inline bool operator>( const Key key1, const Key key2 )
+{
+ return keyOps->isSigned ? key1.key > key2.key :
+ (unsigned long)key1.key > (unsigned long)key2.key;
+}
+
+inline bool operator>=( const Key key1, const Key key2 )
+{
+ return keyOps->isSigned ? key1.key >= key2.key :
+ (unsigned long)key1.key >= (unsigned long)key2.key;
+}
+
+inline bool operator==( const Key key1, const Key key2 )
+{
+ return key1.key == key2.key;
+}
+
+inline bool operator!=( const Key key1, const Key key2 )
+{
+ return key1.key != key2.key;
+}
+
+/* Decrement. Needed only for ranges. */
+inline void Key::decrement()
+{
+ key = keyOps->isSigned ? key - 1 : ((unsigned long)key)-1;
+}
+
+/* Increment. Needed only for ranges. */
+inline void Key::increment()
+{
+ key = keyOps->isSigned ? key+1 : ((unsigned long)key)+1;
+}
+
+inline long long Key::getLongLong() const
+{
+ return keyOps->isSigned ? (long long)key : (long long)(unsigned long)key;
+}
+
+inline Key operator+(const Key key1, const Key key2)
+{
+ /* FIXME: must be made aware of isSigned. */
+ return Key( key1.key + key2.key );
+}
+
+inline Key operator-(const Key key1, const Key key2)
+{
+ /* FIXME: must be made aware of isSigned. */
+ return Key( key1.key - key2.key );
+}
+
+inline long operator&(const Key key1, const Key key2)
+{
+ /* FIXME: must be made aware of isSigned. */
+ return key1.key & key2.key;
+}
+
+inline Key operator/(const Key key1, const Key key2)
+{
+ /* FIXME: must be made aware of isSigned. */
+ return key1.key / key2.key;
+}
+
+/* Filter on the output stream that keeps track of the number of lines
+ * output. */
+class output_filter : public std::filebuf
+{
+public:
+ output_filter( char *fileName ) : fileName(fileName), line(1) { }
+
+ virtual int sync();
+ virtual std::streamsize xsputn(const char* s, std::streamsize n);
+
+ char *fileName;
+ int line;
+};
+
+char *findFileExtension( char *stemFile );
+char *fileNameFromStem( char *stemFile, const char *suffix );
+
+struct Export
+{
+ Export(const char *name, Key key )
+ : name(name), key(key) {}
+
+ const char *name;
+ Key key;
+
+ Export *prev, *next;
+};
+
+typedef DList<Export> ExportList;
+
+#endif /* _COMMON_H */
diff --git a/contrib/tools/ragel5/common/config.h b/contrib/tools/ragel5/common/config.h
new file mode 100644
index 0000000000..405cfd6c3b
--- /dev/null
+++ b/contrib/tools/ragel5/common/config.h
@@ -0,0 +1,39 @@
+/* common/config.h. Generated by configure. */
+/*
+ * Copyright 2001 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+/* Programs. */
+/* #undef GDC */
+#define GOBJC gcc -x objective-c
+#define CXX c++
+#define CC cc
+/* #undef JAVAC */
+/* #undef TXL */
+/* #undef RUBY */
+
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#endif /* _CONFIG_H */
diff --git a/contrib/tools/ragel5/common/pcheck.h b/contrib/tools/ragel5/common/pcheck.h
new file mode 100644
index 0000000000..5f95dc3c12
--- /dev/null
+++ b/contrib/tools/ragel5/common/pcheck.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2001, 2002 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PCHECK_H
+#define _PCHECK_H
+
+class ParamCheck
+{
+public:
+ ParamCheck(const char *paramSpec, int argc, char **argv);
+
+ bool check();
+
+ char *parameterArg; /* The argument to the parameter. */
+ char parameter; /* The parameter matched. */
+ enum { match, invalid, noparam } state;
+
+ char *argOffset; /* If we are reading params inside an
+ * arg this points to the offset. */
+
+ char *curArg; /* Pointer to the current arg. */
+ int iCurArg; /* Index to the current arg. */
+
+private:
+ const char *paramSpec; /* Parameter spec supplied by the coder. */
+ int argc; /* Arguement data from the command line. */
+ char **argv;
+
+};
+
+void NormalizeWinPath(char* input);
+
+#endif /* _PCHECK_H */
diff --git a/contrib/tools/ragel5/common/version.h b/contrib/tools/ragel5/common/version.h
new file mode 100644
index 0000000000..dba4eb2154
--- /dev/null
+++ b/contrib/tools/ragel5/common/version.h
@@ -0,0 +1,2 @@
+#define VERSION "5.19"
+#define PUBDATE "March 2007"
diff --git a/contrib/tools/ragel5/common/ya.make b/contrib/tools/ragel5/common/ya.make
new file mode 100644
index 0000000000..7448cd2af3
--- /dev/null
+++ b/contrib/tools/ragel5/common/ya.make
@@ -0,0 +1,20 @@
+LIBRARY()
+
+LICENSE(GPL-2.0-or-later)
+
+NO_UTIL()
+NO_COMPILER_WARNINGS()
+
+ADDINCL(
+ GLOBAL contrib/tools/ragel5/common
+)
+
+PEERDIR(
+ contrib/tools/ragel5/aapl
+)
+
+SRCS(
+ common.cpp
+)
+
+END()
diff --git a/contrib/tools/ragel5/ragel/fsmap.cpp b/contrib/tools/ragel5/ragel/fsmap.cpp
new file mode 100644
index 0000000000..551aea0391
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/fsmap.cpp
@@ -0,0 +1,840 @@
+/*
+ * Copyright 2002-2004 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "fsmgraph.h"
+#include <iostream>
+using std::cerr;
+using std::endl;
+
+CondData *condData = 0;
+KeyOps *keyOps = 0;
+
+/* Insert an action into an action table. */
+void ActionTable::setAction( int ordering, Action *action )
+{
+ /* Multi-insert in case specific instances of an action appear in a
+ * transition more than once. */
+ insertMulti( ordering, action );
+}
+
+/* Set all the action from another action table in this table. */
+void ActionTable::setActions( const ActionTable &other )
+{
+ for ( ActionTable::Iter action = other; action.lte(); action++ )
+ insertMulti( action->key, action->value );
+}
+
+void ActionTable::setActions( int *orderings, Action **actions, int nActs )
+{
+ for ( int a = 0; a < nActs; a++ )
+ insertMulti( orderings[a], actions[a] );
+}
+
+bool ActionTable::hasAction( Action *action )
+{
+ for ( int a = 0; a < length(); a++ ) {
+ if ( data[a].value == action )
+ return true;
+ }
+ return false;
+}
+
+/* Insert an action into an action table. */
+void LmActionTable::setAction( int ordering, LongestMatchPart *action )
+{
+ /* Multi-insert in case specific instances of an action appear in a
+ * transition more than once. */
+ insertMulti( ordering, action );
+}
+
+/* Set all the action from another action table in this table. */
+void LmActionTable::setActions( const LmActionTable &other )
+{
+ for ( LmActionTable::Iter action = other; action.lte(); action++ )
+ insertMulti( action->key, action->value );
+}
+
+void ErrActionTable::setAction( int ordering, Action *action, int transferPoint )
+{
+ insertMulti( ErrActionTableEl( action, ordering, transferPoint ) );
+}
+
+void ErrActionTable::setActions( const ErrActionTable &other )
+{
+ for ( ErrActionTable::Iter act = other; act.lte(); act++ )
+ insertMulti( ErrActionTableEl( act->action, act->ordering, act->transferPoint ) );
+}
+
+/* Insert a priority into this priority table. Looks out for priorities on
+ * duplicate keys. */
+void PriorTable::setPrior( int ordering, PriorDesc *desc )
+{
+ PriorEl *lastHit = 0;
+ PriorEl *insed = insert( PriorEl(ordering, desc), &lastHit );
+ if ( insed == 0 ) {
+ /* This already has a priority on the same key as desc. Overwrite the
+ * priority if the ordering is larger (later in time). */
+ if ( ordering >= lastHit->ordering )
+ *lastHit = PriorEl( ordering, desc );
+ }
+}
+
+/* Set all the priorities from a priorTable in this table. */
+void PriorTable::setPriors( const PriorTable &other )
+{
+ /* Loop src priorities once to overwrite duplicates. */
+ PriorTable::Iter priorIt = other;
+ for ( ; priorIt.lte(); priorIt++ )
+ setPrior( priorIt->ordering, priorIt->desc );
+}
+
+/* Set the priority of starting transitions. Isolates the start state so it has
+ * no other entry points, then sets the priorities of all the transitions out
+ * of the start state. If the start state is final, then the outPrior of the
+ * start state is also set. The idea is that a machine that accepts the null
+ * string can still specify the starting trans prior for when it accepts the
+ * null word. */
+void FsmAp::startFsmPrior( int ordering, PriorDesc *prior )
+{
+ /* Make sure the start state has no other entry points. */
+ isolateStartState();
+
+ /* Walk all transitions out of the start state. */
+ for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) {
+ if ( trans->toState != 0 )
+ trans->priorTable.setPrior( ordering, prior );
+ }
+}
+
+/* Set the priority of all transitions in a graph. Walks all transition lists
+ * and all def transitions. */
+void FsmAp::allTransPrior( int ordering, PriorDesc *prior )
+{
+ /* Walk the list of all states. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ /* Walk the out list of the state. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ if ( trans->toState != 0 )
+ trans->priorTable.setPrior( ordering, prior );
+ }
+ }
+}
+
+/* Set the priority of all transitions that go into a final state. Note that if
+ * any entry states are final, we will not be setting the priority of any
+ * transitions that may go into those states in the future. The graph does not
+ * support pending in transitions in the same way pending out transitions are
+ * supported. */
+void FsmAp::finishFsmPrior( int ordering, PriorDesc *prior )
+{
+ /* Walk all final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) {
+ /* Walk all in transitions of the final state. */
+ for ( TransInList::Iter trans = (*state)->inList; trans.lte(); trans++ )
+ trans->priorTable.setPrior( ordering, prior );
+ }
+}
+
+/* Set the priority of any future out transitions that may be made going out of
+ * this state machine. */
+void FsmAp::leaveFsmPrior( int ordering, PriorDesc *prior )
+{
+ /* Set priority in all final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ )
+ (*state)->outPriorTable.setPrior( ordering, prior );
+}
+
+
+/* Set actions to execute on starting transitions. Isolates the start state
+ * so it has no other entry points, then adds to the transition functions
+ * of all the transitions out of the start state. If the start state is final,
+ * then the func is also added to the start state's out func list. The idea is
+ * that a machine that accepts the null string can execute a start func when it
+ * matches the null word, which can only be done when leaving the start/final
+ * state. */
+void FsmAp::startFsmAction( int ordering, Action *action )
+{
+ /* Make sure the start state has no other entry points. */
+ isolateStartState();
+
+ /* Walk the start state's transitions, setting functions. */
+ for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) {
+ if ( trans->toState != 0 )
+ trans->actionTable.setAction( ordering, action );
+ }
+}
+
+/* Set functions to execute on all transitions. Walks the out lists of all
+ * states. */
+void FsmAp::allTransAction( int ordering, Action *action )
+{
+ /* Walk all states. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ /* Walk the out list of the state. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ if ( trans->toState != 0 )
+ trans->actionTable.setAction( ordering, action );
+ }
+ }
+}
+
+/* Specify functions to execute upon entering final states. If the start state
+ * is final we can't really specify a function to execute upon entering that
+ * final state the first time. So function really means whenever entering a
+ * final state from within the same fsm. */
+void FsmAp::finishFsmAction( int ordering, Action *action )
+{
+ /* Walk all final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) {
+ /* Walk the final state's in list. */
+ for ( TransInList::Iter trans = (*state)->inList; trans.lte(); trans++ )
+ trans->actionTable.setAction( ordering, action );
+ }
+}
+
+/* Add functions to any future out transitions that may be made going out of
+ * this state machine. */
+void FsmAp::leaveFsmAction( int ordering, Action *action )
+{
+ /* Insert the action in the outActionTable of all final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ )
+ (*state)->outActionTable.setAction( ordering, action );
+}
+
+/* Add functions to the longest match action table for constructing scanners. */
+void FsmAp::longMatchAction( int ordering, LongestMatchPart *lmPart )
+{
+ /* Walk all final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ ) {
+ /* Walk the final state's in list. */
+ for ( TransInList::Iter trans = (*state)->inList; trans.lte(); trans++ )
+ trans->lmActionTable.setAction( ordering, lmPart );
+ }
+}
+
+void FsmAp::fillGaps( StateAp *state )
+{
+ if ( state->outList.length() == 0 ) {
+ /* Add the range on the lower and upper bound. */
+ attachNewTrans( state, 0, keyOps->minKey, keyOps->maxKey );
+ }
+ else {
+ TransList srcList;
+ srcList.transfer( state->outList );
+
+ /* Check for a gap at the beginning. */
+ TransList::Iter trans = srcList, next;
+ if ( keyOps->minKey < trans->lowKey ) {
+ /* Make the high key and append. */
+ Key highKey = trans->lowKey;
+ highKey.decrement();
+
+ attachNewTrans( state, 0, keyOps->minKey, highKey );
+ }
+
+ /* Write the transition. */
+ next = trans.next();
+ state->outList.append( trans );
+
+ /* Keep the last high end. */
+ Key lastHigh = trans->highKey;
+
+ /* Loop each source range. */
+ for ( trans = next; trans.lte(); trans = next ) {
+ /* Make the next key following the last range. */
+ Key nextKey = lastHigh;
+ nextKey.increment();
+
+ /* Check for a gap from last up to here. */
+ if ( nextKey < trans->lowKey ) {
+ /* Make the high end of the range that fills the gap. */
+ Key highKey = trans->lowKey;
+ highKey.decrement();
+
+ attachNewTrans( state, 0, nextKey, highKey );
+ }
+
+ /* Reduce the transition. If it reduced to anything then add it. */
+ next = trans.next();
+ state->outList.append( trans );
+
+ /* Keep the last high end. */
+ lastHigh = trans->highKey;
+ }
+
+ /* Now check for a gap on the end to fill. */
+ if ( lastHigh < keyOps->maxKey ) {
+ /* Get a copy of the default. */
+ lastHigh.increment();
+
+ attachNewTrans( state, 0, lastHigh, keyOps->maxKey );
+ }
+ }
+}
+
+void FsmAp::setErrorAction( StateAp *state, int ordering, Action *action )
+{
+ /* Fill any gaps in the out list with an error transition. */
+ fillGaps( state );
+
+ /* Set error transitions in the transitions that go to error. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ if ( trans->toState == 0 )
+ trans->actionTable.setAction( ordering, action );
+ }
+}
+
+
+/* Give a target state for error transitions. */
+void FsmAp::setErrorTarget( StateAp *state, StateAp *target, int *orderings,
+ Action **actions, int nActs )
+{
+ /* Fill any gaps in the out list with an error transition. */
+ fillGaps( state );
+
+ /* Set error target in the transitions that go to error. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ if ( trans->toState == 0 ) {
+ /* The trans goes to error, redirect it. */
+ redirectErrorTrans( trans->fromState, target, trans );
+ trans->actionTable.setActions( orderings, actions, nActs );
+ }
+ }
+}
+
+void FsmAp::transferErrorActions( StateAp *state, int transferPoint )
+{
+ for ( int i = 0; i < state->errActionTable.length(); ) {
+ ErrActionTableEl *act = state->errActionTable.data + i;
+ if ( act->transferPoint == transferPoint ) {
+ /* Transfer the error action and remove it. */
+ setErrorAction( state, act->ordering, act->action );
+ state->errActionTable.vremove( i );
+ }
+ else {
+ /* Not transfering and deleting, skip over the item. */
+ i += 1;
+ }
+ }
+}
+
+/* Set error actions in the start state. */
+void FsmAp::startErrorAction( int ordering, Action *action, int transferPoint )
+{
+ /* Make sure the start state has no other entry points. */
+ isolateStartState();
+
+ /* Add the actions. */
+ startState->errActionTable.setAction( ordering, action, transferPoint );
+}
+
+/* Set error actions in all states where there is a transition out. */
+void FsmAp::allErrorAction( int ordering, Action *action, int transferPoint )
+{
+ /* Insert actions in the error action table of all states. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ )
+ state->errActionTable.setAction( ordering, action, transferPoint );
+}
+
+/* Set error actions in final states. */
+void FsmAp::finalErrorAction( int ordering, Action *action, int transferPoint )
+{
+ /* Add the action to the error table of final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ )
+ (*state)->errActionTable.setAction( ordering, action, transferPoint );
+}
+
+void FsmAp::notStartErrorAction( int ordering, Action *action, int transferPoint )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( state != startState )
+ state->errActionTable.setAction( ordering, action, transferPoint );
+ }
+}
+
+void FsmAp::notFinalErrorAction( int ordering, Action *action, int transferPoint )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( ! state->isFinState() )
+ state->errActionTable.setAction( ordering, action, transferPoint );
+ }
+}
+
+/* Set error actions in the states that have transitions into a final state. */
+void FsmAp::middleErrorAction( int ordering, Action *action, int transferPoint )
+{
+ /* Isolate the start state in case it is reachable from in inside the
+ * machine, in which case we don't want it set. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( state != startState && ! state->isFinState() )
+ state->errActionTable.setAction( ordering, action, transferPoint );
+ }
+}
+
+/* Set EOF actions in the start state. */
+void FsmAp::startEOFAction( int ordering, Action *action )
+{
+ /* Make sure the start state has no other entry points. */
+ isolateStartState();
+
+ /* Add the actions. */
+ startState->eofActionTable.setAction( ordering, action );
+}
+
+/* Set EOF actions in all states where there is a transition out. */
+void FsmAp::allEOFAction( int ordering, Action *action )
+{
+ /* Insert actions in the EOF action table of all states. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ )
+ state->eofActionTable.setAction( ordering, action );
+}
+
+/* Set EOF actions in final states. */
+void FsmAp::finalEOFAction( int ordering, Action *action )
+{
+ /* Add the action to the error table of final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ )
+ (*state)->eofActionTable.setAction( ordering, action );
+}
+
+void FsmAp::notStartEOFAction( int ordering, Action *action )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( state != startState )
+ state->eofActionTable.setAction( ordering, action );
+ }
+}
+
+void FsmAp::notFinalEOFAction( int ordering, Action *action )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( ! state->isFinState() )
+ state->eofActionTable.setAction( ordering, action );
+ }
+}
+
+/* Set EOF actions in the states that have transitions into a final state. */
+void FsmAp::middleEOFAction( int ordering, Action *action )
+{
+ /* Set the actions in all states that are not the start state and not final. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( state != startState && ! state->isFinState() )
+ state->eofActionTable.setAction( ordering, action );
+ }
+}
+
+/*
+ * Set To State Actions.
+ */
+
+/* Set to state actions in the start state. */
+void FsmAp::startToStateAction( int ordering, Action *action )
+{
+ /* Make sure the start state has no other entry points. */
+ isolateStartState();
+ startState->toStateActionTable.setAction( ordering, action );
+}
+
+/* Set to state actions in all states. */
+void FsmAp::allToStateAction( int ordering, Action *action )
+{
+ /* Insert the action on all states. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ )
+ state->toStateActionTable.setAction( ordering, action );
+}
+
+/* Set to state actions in final states. */
+void FsmAp::finalToStateAction( int ordering, Action *action )
+{
+ /* Add the action to the error table of final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ )
+ (*state)->toStateActionTable.setAction( ordering, action );
+}
+
+void FsmAp::notStartToStateAction( int ordering, Action *action )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( state != startState )
+ state->toStateActionTable.setAction( ordering, action );
+ }
+}
+
+void FsmAp::notFinalToStateAction( int ordering, Action *action )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( ! state->isFinState() )
+ state->toStateActionTable.setAction( ordering, action );
+ }
+}
+
+/* Set to state actions in states that are not final and not the start state. */
+void FsmAp::middleToStateAction( int ordering, Action *action )
+{
+ /* Set the action in all states that are not the start state and not final. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( state != startState && ! state->isFinState() )
+ state->toStateActionTable.setAction( ordering, action );
+ }
+}
+
+/*
+ * Set From State Actions.
+ */
+
+void FsmAp::startFromStateAction( int ordering, Action *action )
+{
+ /* Make sure the start state has no other entry points. */
+ isolateStartState();
+ startState->fromStateActionTable.setAction( ordering, action );
+}
+
+void FsmAp::allFromStateAction( int ordering, Action *action )
+{
+ /* Insert the action on all states. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ )
+ state->fromStateActionTable.setAction( ordering, action );
+}
+
+void FsmAp::finalFromStateAction( int ordering, Action *action )
+{
+ /* Add the action to the error table of final states. */
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ )
+ (*state)->fromStateActionTable.setAction( ordering, action );
+}
+
+void FsmAp::notStartFromStateAction( int ordering, Action *action )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( state != startState )
+ state->fromStateActionTable.setAction( ordering, action );
+ }
+}
+
+void FsmAp::notFinalFromStateAction( int ordering, Action *action )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( ! state->isFinState() )
+ state->fromStateActionTable.setAction( ordering, action );
+ }
+}
+
+void FsmAp::middleFromStateAction( int ordering, Action *action )
+{
+ /* Set the action in all states that are not the start state and not final. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ if ( state != startState && ! state->isFinState() )
+ state->fromStateActionTable.setAction( ordering, action );
+ }
+}
+
+/* Shift the function ordering of the start transitions to start
+ * at fromOrder and increase in units of 1. Useful before staring.
+ * Returns the maximum number of order numbers used. */
+int FsmAp::shiftStartActionOrder( int fromOrder )
+{
+ int maxUsed = 0;
+
+ /* Walk the start state's transitions, shifting function ordering. */
+ for ( TransList::Iter trans = startState->outList; trans.lte(); trans++ ) {
+ /* Walk the function data for the transition and set the keys to
+ * increasing values starting at fromOrder. */
+ int curFromOrder = fromOrder;
+ ActionTable::Iter action = trans->actionTable;
+ for ( ; action.lte(); action++ )
+ action->key = curFromOrder++;
+
+ /* Keep track of the max number of orders used. */
+ if ( curFromOrder - fromOrder > maxUsed )
+ maxUsed = curFromOrder - fromOrder;
+ }
+
+ return maxUsed;
+}
+
+/* Remove all priorities. */
+void FsmAp::clearAllPriorities()
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ /* Clear out priority data. */
+ state->outPriorTable.empty();
+
+ /* Clear transition data from the out transitions. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ )
+ trans->priorTable.empty();
+ }
+}
+
+/* Zeros out the function ordering keys. This may be called before minimization
+ * when it is known that no more fsm operations are going to be done. This
+ * will achieve greater reduction as states will not be separated on the basis
+ * of function ordering. */
+void FsmAp::nullActionKeys( )
+{
+ /* For each state... */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ /* Walk the transitions for the state. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ /* Walk the action table for the transition. */
+ for ( ActionTable::Iter action = trans->actionTable;
+ action.lte(); action++ )
+ action->key = 0;
+
+ /* Walk the action table for the transition. */
+ for ( LmActionTable::Iter action = trans->lmActionTable;
+ action.lte(); action++ )
+ action->key = 0;
+ }
+
+ /* Null the action keys of the to state action table. */
+ for ( ActionTable::Iter action = state->toStateActionTable;
+ action.lte(); action++ )
+ action->key = 0;
+
+ /* Null the action keys of the from state action table. */
+ for ( ActionTable::Iter action = state->fromStateActionTable;
+ action.lte(); action++ )
+ action->key = 0;
+
+ /* Null the action keys of the out transtions. */
+ for ( ActionTable::Iter action = state->outActionTable;
+ action.lte(); action++ )
+ action->key = 0;
+
+ /* Null the action keys of the error action table. */
+ for ( ErrActionTable::Iter action = state->errActionTable;
+ action.lte(); action++ )
+ action->ordering = 0;
+
+ /* Null the action keys eof action table. */
+ for ( ActionTable::Iter action = state->eofActionTable;
+ action.lte(); action++ )
+ action->key = 0;
+ }
+}
+
+/* Walk the list of states and verify that non final states do not have out
+ * data, that all stateBits are cleared, and that there are no states with
+ * zero foreign in transitions. */
+void FsmAp::verifyStates()
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ /* Non final states should not have leaving data. */
+ if ( ! (state->stateBits & SB_ISFINAL) ) {
+ assert( state->outActionTable.length() == 0 );
+ assert( state->outCondSet.length() == 0 );
+ assert( state->outPriorTable.length() == 0 );
+ }
+
+ /* Data used in algorithms should be cleared. */
+ assert( (state->stateBits & SB_BOTH) == 0 );
+ assert( state->foreignInTrans > 0 );
+ }
+}
+
+/* Compare two transitions according to their relative priority. Since the
+ * base transition has no priority associated with it, the default is to
+ * return equal. */
+int FsmAp::comparePrior( const PriorTable &priorTable1, const PriorTable &priorTable2 )
+{
+ /* Looking for differing priorities on same keys. Need to concurrently
+ * scan the priority lists. */
+ PriorTable::Iter pd1 = priorTable1;
+ PriorTable::Iter pd2 = priorTable2;
+ while ( pd1.lte() && pd2.lte() ) {
+ /* Check keys. */
+ if ( pd1->desc->key < pd2->desc->key )
+ pd1.increment();
+ else if ( pd1->desc->key > pd2->desc->key )
+ pd2.increment();
+ /* Keys are the same, check priorities. */
+ else if ( pd1->desc->priority < pd2->desc->priority )
+ return -1;
+ else if ( pd1->desc->priority > pd2->desc->priority )
+ return 1;
+ else {
+ /* Keys and priorities are equal, advance both. */
+ pd1.increment();
+ pd2.increment();
+ }
+ }
+
+ /* No differing priorities on the same key. */
+ return 0;
+}
+
+/* Compares two transitions according to priority and functions. Pointers
+ * should not be null. Does not consider to state or from state. Compare two
+ * transitions according to the data contained in the transitions. Data means
+ * any properties added to user transitions that may differentiate them. Since
+ * the base transition has no data, the default is to return equal. */
+int FsmAp::compareTransData( TransAp *trans1, TransAp *trans2 )
+{
+ /* Compare the prior table. */
+ int cmpRes = CmpPriorTable::compare( trans1->priorTable,
+ trans2->priorTable );
+ if ( cmpRes != 0 )
+ return cmpRes;
+
+ /* Compare longest match action tables. */
+ cmpRes = CmpLmActionTable::compare(trans1->lmActionTable,
+ trans2->lmActionTable);
+ if ( cmpRes != 0 )
+ return cmpRes;
+
+ /* Compare action tables. */
+ return CmpActionTable::compare(trans1->actionTable,
+ trans2->actionTable);
+}
+
+/* Callback invoked when another trans (or possibly this) is added into this
+ * transition during the merging process. Draw in any properties of srcTrans
+ * into this transition. AddInTrans is called when a new transitions is made
+ * that will be a duplicate of another transition or a combination of several
+ * other transitions. AddInTrans will be called for each transition that the
+ * new transition is to represent. */
+void FsmAp::addInTrans( TransAp *destTrans, TransAp *srcTrans )
+{
+ /* Protect against adding in from ourselves. */
+ if ( srcTrans == destTrans ) {
+ /* Adding in ourselves, need to make a copy of the source transitions.
+ * The priorities are not copied in as that would have no effect. */
+ destTrans->lmActionTable.setActions( LmActionTable(srcTrans->lmActionTable) );
+ destTrans->actionTable.setActions( ActionTable(srcTrans->actionTable) );
+ }
+ else {
+ /* Not a copy of ourself, get the functions and priorities. */
+ destTrans->lmActionTable.setActions( srcTrans->lmActionTable );
+ destTrans->actionTable.setActions( srcTrans->actionTable );
+ destTrans->priorTable.setPriors( srcTrans->priorTable );
+ }
+}
+
+/* Compare the properties of states that are embedded by users. Compares out
+ * priorities, out transitions, to, from, out, error and eof action tables. */
+int FsmAp::compareStateData( const StateAp *state1, const StateAp *state2 )
+{
+ /* Compare the out priority table. */
+ int cmpRes = CmpPriorTable::
+ compare( state1->outPriorTable, state2->outPriorTable );
+ if ( cmpRes != 0 )
+ return cmpRes;
+
+ /* Test to state action tables. */
+ cmpRes = CmpActionTable::compare( state1->toStateActionTable,
+ state2->toStateActionTable );
+ if ( cmpRes != 0 )
+ return cmpRes;
+
+ /* Test from state action tables. */
+ cmpRes = CmpActionTable::compare( state1->fromStateActionTable,
+ state2->fromStateActionTable );
+ if ( cmpRes != 0 )
+ return cmpRes;
+
+ /* Test out action tables. */
+ cmpRes = CmpActionTable::compare( state1->outActionTable,
+ state2->outActionTable );
+ if ( cmpRes != 0 )
+ return cmpRes;
+
+ /* Test out condition sets. */
+ cmpRes = CmpActionSet::compare( state1->outCondSet,
+ state2->outCondSet );
+ if ( cmpRes != 0 )
+ return cmpRes;
+
+ /* Test out error action tables. */
+ cmpRes = CmpErrActionTable::compare( state1->errActionTable,
+ state2->errActionTable );
+ if ( cmpRes != 0 )
+ return cmpRes;
+
+ /* Test eof action tables. */
+ return CmpActionTable::compare( state1->eofActionTable,
+ state2->eofActionTable );
+}
+
+
+/* Invoked when a state looses its final state status and the leaving
+ * transition embedding data should be deleted. */
+void FsmAp::clearOutData( StateAp *state )
+{
+ /* Kill the out actions and priorities. */
+ state->outActionTable.empty();
+ state->outCondSet.empty();
+ state->outPriorTable.empty();
+}
+
+bool FsmAp::hasOutData( StateAp *state )
+{
+ return ( state->outActionTable.length() > 0 ||
+ state->outCondSet.length() > 0 ||
+ state->outPriorTable.length() > 0 );
+}
+
+/*
+ * Setting Conditions.
+ */
+
+
+void logNewExpansion( Expansion *exp );
+void logCondSpace( CondSpace *condSpace );
+
+CondSpace *FsmAp::addCondSpace( const CondSet &condSet )
+{
+ CondSpace *condSpace = condData->condSpaceMap.find( condSet );
+ if ( condSpace == 0 ) {
+ Key baseKey = condData->nextCondKey;
+ condData->nextCondKey += (1 << condSet.length() ) * keyOps->alphSize();
+
+ condSpace = new CondSpace( condSet );
+ condSpace->baseKey = baseKey;
+ condData->condSpaceMap.insert( condSpace );
+
+ #ifdef LOG_CONDS
+ cerr << "adding new condition space" << endl;
+ cerr << " condition set: ";
+ logCondSpace( condSpace );
+ cerr << endl;
+ cerr << " baseKey: " << baseKey.getVal() << endl;
+ #endif
+ }
+ return condSpace;
+}
+
+void FsmAp::startFsmCondition( Action *condAction )
+{
+ /* Make sure the start state has no other entry points. */
+ isolateStartState();
+ embedCondition( startState, condAction );
+}
+
+void FsmAp::allTransCondition( Action *condAction )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ )
+ embedCondition( state, condAction );
+}
+
+void FsmAp::leaveFsmCondition( Action *condAction )
+{
+ for ( StateSet::Iter state = finStateSet; state.lte(); state++ )
+ (*state)->outCondSet.insert( condAction );
+}
diff --git a/contrib/tools/ragel5/ragel/fsmattach.cpp b/contrib/tools/ragel5/ragel/fsmattach.cpp
new file mode 100644
index 0000000000..6a90df658a
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/fsmattach.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright 2001 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <assert.h>
+#include "fsmgraph.h"
+
+#include <iostream>
+using namespace std;
+
+/* Insert a transition into an inlist. The head must be supplied. */
+void FsmAp::attachToInList( StateAp *from, StateAp *to,
+ TransAp *&head, TransAp *trans )
+{
+ trans->ilnext = head;
+ trans->ilprev = 0;
+
+ /* If in trans list is not empty, set the head->prev to trans. */
+ if ( head != 0 )
+ head->ilprev = trans;
+
+ /* Now insert ourselves at the front of the list. */
+ head = trans;
+
+ /* Keep track of foreign transitions for from and to. */
+ if ( from != to ) {
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions is about to go up to 1 then
+ * move it from the misfit list to the main list. */
+ if ( to->foreignInTrans == 0 )
+ stateList.append( misfitList.detach( to ) );
+ }
+
+ to->foreignInTrans += 1;
+ }
+};
+
+/* Detach a transition from an inlist. The head of the inlist must be supplied. */
+void FsmAp::detachFromInList( StateAp *from, StateAp *to,
+ TransAp *&head, TransAp *trans )
+{
+ /* Detach in the inTransList. */
+ if ( trans->ilprev == 0 )
+ head = trans->ilnext;
+ else
+ trans->ilprev->ilnext = trans->ilnext;
+
+ if ( trans->ilnext != 0 )
+ trans->ilnext->ilprev = trans->ilprev;
+
+ /* Keep track of foreign transitions for from and to. */
+ if ( from != to ) {
+ to->foreignInTrans -= 1;
+
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions goes down to 0 then move it
+ * from the main list to the misfit list. */
+ if ( to->foreignInTrans == 0 )
+ misfitList.append( stateList.detach( to ) );
+ }
+ }
+}
+
+/* Attach states on the default transition, range list or on out/in list key.
+ * First makes a new transition. If there is already a transition out from
+ * fromState on the default, then will assertion fail. */
+TransAp *FsmAp::attachNewTrans( StateAp *from, StateAp *to, Key lowKey, Key highKey )
+{
+ /* Make the new transition. */
+ TransAp *retVal = new TransAp();
+
+ /* The transition is now attached. Remember the parties involved. */
+ retVal->fromState = from;
+ retVal->toState = to;
+
+ /* Make the entry in the out list for the transitions. */
+ from->outList.append( retVal );
+
+ /* Set the the keys of the new trans. */
+ retVal->lowKey = lowKey;
+ retVal->highKey = highKey;
+
+ /* Attach using inList as the head pointer. */
+ if ( to != 0 )
+ attachToInList( from, to, to->inList.head, retVal );
+
+ return retVal;
+}
+
+/* Attach for range lists or for the default transition. This attach should
+ * be used when a transition already is allocated and must be attached to a
+ * target state. Does not handle adding the transition into the out list. */
+void FsmAp::attachTrans( StateAp *from, StateAp *to, TransAp *trans )
+{
+ assert( trans->fromState == 0 && trans->toState == 0 );
+ trans->fromState = from;
+ trans->toState = to;
+
+ if ( to != 0 ) {
+ /* Attach using the inList pointer as the head pointer. */
+ attachToInList( from, to, to->inList.head, trans );
+ }
+}
+
+/* Redirect a transition away from error and towards some state. This is just
+ * like attachTrans except it requires fromState to be set and does not touch
+ * it. */
+void FsmAp::redirectErrorTrans( StateAp *from, StateAp *to, TransAp *trans )
+{
+ assert( trans->fromState != 0 && trans->toState == 0 );
+ trans->toState = to;
+
+ if ( to != 0 ) {
+ /* Attach using the inList pointer as the head pointer. */
+ attachToInList( from, to, to->inList.head, trans );
+ }
+}
+
+/* Detach for out/in lists or for default transition. */
+void FsmAp::detachTrans( StateAp *from, StateAp *to, TransAp *trans )
+{
+ assert( trans->fromState == from && trans->toState == to );
+ trans->fromState = 0;
+ trans->toState = 0;
+
+ if ( to != 0 ) {
+ /* Detach using to's inList pointer as the head. */
+ detachFromInList( from, to, to->inList.head, trans );
+ }
+}
+
+
+/* Detach a state from the graph. Detaches and deletes transitions in and out
+ * of the state. Empties inList and outList. Removes the state from the final
+ * state set. A detached state becomes useless and should be deleted. */
+void FsmAp::detachState( StateAp *state )
+{
+ /* Detach the in transitions from the inList list of transitions. */
+ while ( state->inList.head != 0 ) {
+ /* Get pointers to the trans and the state. */
+ TransAp *trans = state->inList.head;
+ StateAp *fromState = trans->fromState;
+
+ /* Detach the transitions from the source state. */
+ detachTrans( fromState, state, trans );
+
+ /* Ok to delete the transition. */
+ fromState->outList.detach( trans );
+ delete trans;
+ }
+
+ /* Remove the entry points in on the machine. */
+ while ( state->entryIds.length() > 0 )
+ unsetEntry( state->entryIds[0], state );
+
+ /* Detach out range transitions. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); ) {
+ TransList::Iter next = trans.next();
+ detachTrans( state, trans->toState, trans );
+ delete trans;
+ trans = next;
+ }
+
+ /* Delete all of the out range pointers. */
+ state->outList.abandon();
+
+ /* Unset final stateness before detaching from graph. */
+ if ( state->stateBits & SB_ISFINAL )
+ finStateSet.remove( state );
+}
+
+
+/* Duplicate a transition. Makes a new transition that is attached to the same
+ * dest as srcTrans. The new transition has functions and priority taken from
+ * srcTrans. Used for merging a transition in to a free spot. The trans can
+ * just be dropped in. It does not conflict with an existing trans and need
+ * not be crossed. Returns the new transition. */
+TransAp *FsmAp::dupTrans( StateAp *from, TransAp *srcTrans )
+{
+ /* Make a new transition. */
+ TransAp *newTrans = new TransAp();
+
+ /* We can attach the transition, one does not exist. */
+ attachTrans( from, srcTrans->toState, newTrans );
+
+ /* Call the user callback to add in the original source transition. */
+ addInTrans( newTrans, srcTrans );
+
+ return newTrans;
+}
+
+/* In crossing, src trans and dest trans both go to existing states. Make one
+ * state from the sets of states that src and dest trans go to. */
+TransAp *FsmAp::fsmAttachStates( MergeData &md, StateAp *from,
+ TransAp *destTrans, TransAp *srcTrans )
+{
+ /* The priorities are equal. We must merge the transitions. Does the
+ * existing trans go to the state we are to attach to? ie, are we to
+ * simply double up the transition? */
+ StateAp *toState = srcTrans->toState;
+ StateAp *existingState = destTrans->toState;
+
+ if ( existingState == toState ) {
+ /* The transition is a double up to the same state. Copy the src
+ * trans into itself. We don't need to merge in the from out trans
+ * data, that was done already. */
+ addInTrans( destTrans, srcTrans );
+ }
+ else {
+ /* The trans is not a double up. Dest trans cannot be the same as src
+ * trans. Set up the state set. */
+ StateSet stateSet;
+
+ /* We go to all the states the existing trans goes to, plus... */
+ if ( existingState->stateDictEl == 0 )
+ stateSet.insert( existingState );
+ else
+ stateSet.insert( existingState->stateDictEl->stateSet );
+
+ /* ... all the states that we have been told to go to. */
+ if ( toState->stateDictEl == 0 )
+ stateSet.insert( toState );
+ else
+ stateSet.insert( toState->stateDictEl->stateSet );
+
+ /* Look for the state. If it is not there already, make it. */
+ StateDictEl *lastFound;
+ if ( md.stateDict.insert( stateSet, &lastFound ) ) {
+ /* Make a new state representing the combination of states in
+ * stateSet. It gets added to the fill list. This means that we
+ * need to fill in it's transitions sometime in the future. We
+ * don't do that now (ie, do not recurse). */
+ StateAp *combinState = addState();
+
+ /* Link up the dict element and the state. */
+ lastFound->targState = combinState;
+ combinState->stateDictEl = lastFound;
+
+ /* Add to the fill list. */
+ md.fillListAppend( combinState );
+ }
+
+ /* Get the state insertted/deleted. */
+ StateAp *targ = lastFound->targState;
+
+ /* Detach the state from existing state. */
+ detachTrans( from, existingState, destTrans );
+
+ /* Re-attach to the new target. */
+ attachTrans( from, targ, destTrans );
+
+ /* Add in src trans to the existing transition that we redirected to
+ * the new state. We don't need to merge in the from out trans data,
+ * that was done already. */
+ addInTrans( destTrans, srcTrans );
+ }
+
+ return destTrans;
+}
+
+/* Two transitions are to be crossed, handle the possibility of either going
+ * to the error state. */
+TransAp *FsmAp::mergeTrans( MergeData &md, StateAp *from,
+ TransAp *destTrans, TransAp *srcTrans )
+{
+ TransAp *retTrans = 0;
+ if ( destTrans->toState == 0 && srcTrans->toState == 0 ) {
+ /* Error added into error. */
+ addInTrans( destTrans, srcTrans );
+ retTrans = destTrans;
+ }
+ else if ( destTrans->toState == 0 && srcTrans->toState != 0 ) {
+ /* Non error added into error we need to detach and reattach, */
+ detachTrans( from, destTrans->toState, destTrans );
+ attachTrans( from, srcTrans->toState, destTrans );
+ addInTrans( destTrans, srcTrans );
+ retTrans = destTrans;
+ }
+ else if ( srcTrans->toState == 0 ) {
+ /* Dest goes somewhere but src doesn't, just add it it in. */
+ addInTrans( destTrans, srcTrans );
+ retTrans = destTrans;
+ }
+ else {
+ /* Both go somewhere, run the actual cross. */
+ retTrans = fsmAttachStates( md, from, destTrans, srcTrans );
+ }
+
+ return retTrans;
+}
+
+/* Find the trans with the higher priority. If src is lower priority then dest then
+ * src is ignored. If src is higher priority than dest, then src overwrites dest. If
+ * the priorities are equal, then they are merged. */
+TransAp *FsmAp::crossTransitions( MergeData &md, StateAp *from,
+ TransAp *destTrans, TransAp *srcTrans )
+{
+ TransAp *retTrans;
+
+ /* Compare the priority of the dest and src transitions. */
+ int compareRes = comparePrior( destTrans->priorTable, srcTrans->priorTable );
+ if ( compareRes < 0 ) {
+ /* Src trans has a higher priority than dest, src overwrites dest.
+ * Detach dest and return a copy of src. */
+ detachTrans( from, destTrans->toState, destTrans );
+ retTrans = dupTrans( from, srcTrans );
+ }
+ else if ( compareRes > 0 ) {
+ /* The dest trans has a higher priority, use dest. */
+ retTrans = destTrans;
+ }
+ else {
+ /* Src trans and dest trans have the same priority, they must be merged. */
+ retTrans = mergeTrans( md, from, destTrans, srcTrans );
+ }
+
+ /* Return the transition that resulted from the cross. */
+ return retTrans;
+}
+
+/* Copy the transitions in srcList to the outlist of dest. The srcList should
+ * not be the outList of dest, otherwise you would be copying the contents of
+ * srcList into itself as it's iterated: bad news. */
+void FsmAp::outTransCopy( MergeData &md, StateAp *dest, TransAp *srcList )
+{
+ /* The destination list. */
+ TransList destList;
+
+ /* Set up an iterator to stop at breaks. */
+ PairIter<TransAp> outPair( dest->outList.head, srcList );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+ case RangeInS1: {
+ /* The pair iter is the authority on the keys. It may have needed
+ * to break the dest range. */
+ TransAp *destTrans = outPair.s1Tel.trans;
+ destTrans->lowKey = outPair.s1Tel.lowKey;
+ destTrans->highKey = outPair.s1Tel.highKey;
+ destList.append( destTrans );
+ break;
+ }
+ case RangeInS2: {
+ /* Src range may get crossed with dest's default transition. */
+ TransAp *newTrans = dupTrans( dest, outPair.s2Tel.trans );
+
+ /* Set up the transition's keys and append to the dest list. */
+ newTrans->lowKey = outPair.s2Tel.lowKey;
+ newTrans->highKey = outPair.s2Tel.highKey;
+ destList.append( newTrans );
+ break;
+ }
+ case RangeOverlap: {
+ /* Exact overlap, cross them. */
+ TransAp *newTrans = crossTransitions( md, dest,
+ outPair.s1Tel.trans, outPair.s2Tel.trans );
+
+ /* Set up the transition's keys and append to the dest list. */
+ newTrans->lowKey = outPair.s1Tel.lowKey;
+ newTrans->highKey = outPair.s1Tel.highKey;
+ destList.append( newTrans );
+ break;
+ }
+ case BreakS1: {
+ /* Since we are always writing to the dest trans, the dest needs
+ * to be copied when it is broken. The copy goes into the first
+ * half of the break to "break it off". */
+ outPair.s1Tel.trans = dupTrans( dest, outPair.s1Tel.trans );
+ break;
+ }
+ case BreakS2:
+ break;
+ }
+ }
+
+ /* Abandon the old outList and transfer destList into it. */
+ dest->outList.transfer( destList );
+}
+
+
+/* Move all the transitions that go into src so that they go into dest. */
+void FsmAp::inTransMove( StateAp *dest, StateAp *src )
+{
+ /* Do not try to move in trans to and from the same state. */
+ assert( dest != src );
+
+ /* If src is the start state, dest becomes the start state. */
+ if ( src == startState ) {
+ unsetStartState();
+ setStartState( dest );
+ }
+
+ /* For each entry point into, create an entry point into dest, when the
+ * state is detached, the entry points to src will be removed. */
+ for ( EntryIdSet::Iter enId = src->entryIds; enId.lte(); enId++ )
+ changeEntry( *enId, dest, src );
+
+ /* Move the transitions in inList. */
+ while ( src->inList.head != 0 ) {
+ /* Get trans and from state. */
+ TransAp *trans = src->inList.head;
+ StateAp *fromState = trans->fromState;
+
+ /* Detach from src, reattach to dest. */
+ detachTrans( fromState, src, trans );
+ attachTrans( fromState, dest, trans );
+ }
+}
diff --git a/contrib/tools/ragel5/ragel/fsmbase.cpp b/contrib/tools/ragel5/ragel/fsmbase.cpp
new file mode 100644
index 0000000000..f1d7141c09
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/fsmbase.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <assert.h>
+#include "fsmgraph.h"
+
+/* Simple singly linked list append routine for the fill list. The new state
+ * goes to the end of the list. */
+void MergeData::fillListAppend( StateAp *state )
+{
+ state->alg.next = 0;
+
+ if ( stfillHead == 0 ) {
+ /* List is empty, state becomes head and tail. */
+ stfillHead = state;
+ stfillTail = state;
+ }
+ else {
+ /* List is not empty, state goes after last element. */
+ stfillTail->alg.next = state;
+ stfillTail = state;
+ }
+}
+
+/* Graph constructor. */
+FsmAp::FsmAp()
+:
+ /* No start state. */
+ startState(0),
+ errState(0),
+
+ /* Misfit accounting is a switch, turned on only at specific times. It
+ * controls what happens when states have no way in from the outside
+ * world.. */
+ misfitAccounting(false)
+{
+}
+
+/* Copy all graph data including transitions. */
+FsmAp::FsmAp( const FsmAp &graph )
+:
+ /* Lists start empty. Will be filled by copy. */
+ stateList(),
+ misfitList(),
+
+ /* Copy in the entry points,
+ * pointers will be resolved later. */
+ entryPoints(graph.entryPoints),
+ startState(graph.startState),
+ errState(0),
+
+ /* Will be filled by copy. */
+ finStateSet(),
+
+ /* Misfit accounting is only on during merging. */
+ misfitAccounting(false)
+{
+ /* Create the states and record their map in the original state. */
+ StateList::Iter origState = graph.stateList;
+ for ( ; origState.lte(); origState++ ) {
+ /* Make the new state. */
+ StateAp *newState = new StateAp( *origState );
+
+ /* Add the state to the list. */
+ stateList.append( newState );
+
+ /* Set the mapsTo item of the old state. */
+ origState->alg.stateMap = newState;
+ }
+
+ /* Derefernce all the state maps. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ /* The points to the original in the src machine. The taget's duplicate
+ * is in the statemap. */
+ StateAp *toState = trans->toState != 0 ? trans->toState->alg.stateMap : 0;
+
+ /* Attach The transition to the duplicate. */
+ trans->toState = 0;
+ attachTrans( state, toState, trans );
+ }
+ }
+
+ /* Fix the state pointers in the entry points array. */
+ EntryMapEl *eel = entryPoints.data;
+ for ( int e = 0; e < entryPoints.length(); e++, eel++ ) {
+ /* Get the duplicate of the state. */
+ eel->value = eel->value->alg.stateMap;
+
+ /* Foreign in transitions must be built up when duping machines so
+ * increment it here. */
+ eel->value->foreignInTrans += 1;
+ }
+
+ /* Fix the start state pointer and the new start state's count of in
+ * transiions. */
+ startState = startState->alg.stateMap;
+ startState->foreignInTrans += 1;
+
+ /* Build the final state set. */
+ StateSet::Iter st = graph.finStateSet;
+ for ( ; st.lte(); st++ )
+ finStateSet.insert((*st)->alg.stateMap);
+}
+
+/* Deletes all transition data then deletes each state. */
+FsmAp::~FsmAp()
+{
+ /* Delete all the transitions. */
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ /* Iterate the out transitions, deleting them. */
+ state->outList.empty();
+ }
+
+ /* Delete all the states. */
+ stateList.empty();
+}
+
+/* Set a state final. The state has its isFinState set to true and the state
+ * is added to the finStateSet. */
+void FsmAp::setFinState( StateAp *state )
+{
+ /* Is it already a fin state. */
+ if ( state->stateBits & SB_ISFINAL )
+ return;
+
+ state->stateBits |= SB_ISFINAL;
+ finStateSet.insert( state );
+}
+
+/* Set a state non-final. The has its isFinState flag set false and the state
+ * is removed from the final state set. */
+void FsmAp::unsetFinState( StateAp *state )
+{
+ /* Is it already a non-final state? */
+ if ( ! (state->stateBits & SB_ISFINAL) )
+ return;
+
+ /* When a state looses its final state status it must relinquish all the
+ * properties that are allowed only for final states. */
+ clearOutData( state );
+
+ state->stateBits &= ~ SB_ISFINAL;
+ finStateSet.remove( state );
+}
+
+/* Set and unset a state as the start state. */
+void FsmAp::setStartState( StateAp *state )
+{
+ /* Sould change from unset to set. */
+ assert( startState == 0 );
+ startState = state;
+
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions is about to go up to 1 then
+ * take it off the misfit list and put it on the head list. */
+ if ( state->foreignInTrans == 0 )
+ stateList.append( misfitList.detach( state ) );
+ }
+
+ /* Up the foreign in transitions to the state. */
+ state->foreignInTrans += 1;
+}
+
+void FsmAp::unsetStartState()
+{
+ /* Should change from set to unset. */
+ assert( startState != 0 );
+
+ /* Decrement the entry's count of foreign entries. */
+ startState->foreignInTrans -= 1;
+
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions just went down to 0 then take
+ * it off the main list and put it on the misfit list. */
+ if ( startState->foreignInTrans == 0 )
+ misfitList.append( stateList.detach( startState ) );
+ }
+
+ startState = 0;
+}
+
+/* Associate an id with a state. Makes the state a named entry point. Has no
+ * effect if the entry point is already mapped to the state. */
+void FsmAp::setEntry( int id, StateAp *state )
+{
+ /* Insert the id into the state. If the state is already labelled with id,
+ * nothing to do. */
+ if ( state->entryIds.insert( id ) ) {
+ /* Insert the entry and assert that it succeeds. */
+ entryPoints.insertMulti( id, state );
+
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions is about to go up to 1 then
+ * take it off the misfit list and put it on the head list. */
+ if ( state->foreignInTrans == 0 )
+ stateList.append( misfitList.detach( state ) );
+ }
+
+ /* Up the foreign in transitions to the state. */
+ state->foreignInTrans += 1;
+ }
+}
+
+/* Remove the association of an id with a state. The state looses it's entry
+ * point status. Assumes that the id is indeed mapped to state. */
+void FsmAp::unsetEntry( int id, StateAp *state )
+{
+ /* Find the entry point in on id. */
+ EntryMapEl *enLow = 0, *enHigh = 0;
+ entryPoints.findMulti( id, enLow, enHigh );
+ while ( enLow->value != state )
+ enLow += 1;
+
+ /* Remove the record from the map. */
+ entryPoints.remove( enLow );
+
+ /* Remove the state's sense of the link. */
+ state->entryIds.remove( id );
+ state->foreignInTrans -= 1;
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions just went down to 0 then take
+ * it off the main list and put it on the misfit list. */
+ if ( state->foreignInTrans == 0 )
+ misfitList.append( stateList.detach( state ) );
+ }
+}
+
+/* Remove all association of an id with states. Assumes that the id is indeed
+ * mapped to a state. */
+void FsmAp::unsetEntry( int id )
+{
+ /* Find the entry point in on id. */
+ EntryMapEl *enLow = 0, *enHigh = 0;
+ entryPoints.findMulti( id, enLow, enHigh );
+ for ( EntryMapEl *mel = enLow; mel <= enHigh; mel++ ) {
+ /* Remove the state's sense of the link. */
+ mel->value->entryIds.remove( id );
+ mel->value->foreignInTrans -= 1;
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions just went down to 0
+ * then take it off the main list and put it on the misfit list. */
+ if ( mel->value->foreignInTrans == 0 )
+ misfitList.append( stateList.detach( mel->value ) );
+ }
+ }
+
+ /* Remove the records from the entry points map. */
+ entryPoints.removeMulti( enLow, enHigh );
+}
+
+
+void FsmAp::changeEntry( int id, StateAp *to, StateAp *from )
+{
+ /* Find the entry in the entry map. */
+ EntryMapEl *enLow = 0, *enHigh = 0;
+ entryPoints.findMulti( id, enLow, enHigh );
+ while ( enLow->value != from )
+ enLow += 1;
+
+ /* Change it to the new target. */
+ enLow->value = to;
+
+ /* Remove from's sense of the link. */
+ from->entryIds.remove( id );
+ from->foreignInTrans -= 1;
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions just went down to 0 then take
+ * it off the main list and put it on the misfit list. */
+ if ( from->foreignInTrans == 0 )
+ misfitList.append( stateList.detach( from ) );
+ }
+
+ /* Add to's sense of the link. */
+ if ( to->entryIds.insert( id ) != 0 ) {
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions is about to go up to 1 then
+ * take it off the misfit list and put it on the head list. */
+ if ( to->foreignInTrans == 0 )
+ stateList.append( misfitList.detach( to ) );
+ }
+
+ /* Up the foreign in transitions to the state. */
+ to->foreignInTrans += 1;
+ }
+}
+
+
+/* Clear all entry points from a machine. */
+void FsmAp::unsetAllEntryPoints()
+{
+ for ( EntryMap::Iter en = entryPoints; en.lte(); en++ ) {
+ /* Kill all the state's entry points at once. */
+ if ( en->value->entryIds.length() > 0 ) {
+ en->value->foreignInTrans -= en->value->entryIds.length();
+
+ if ( misfitAccounting ) {
+ /* If the number of foreign in transitions just went down to 0
+ * then take it off the main list and put it on the misfit
+ * list. */
+ if ( en->value->foreignInTrans == 0 )
+ misfitList.append( stateList.detach( en->value ) );
+ }
+
+ /* Clear the set of ids out all at once. */
+ en->value->entryIds.empty();
+ }
+ }
+
+ /* Now clear out the entry map all at once. */
+ entryPoints.empty();
+}
+
+/* Assigning an epsilon transition into final states. */
+void FsmAp::epsilonTrans( int id )
+{
+ for ( StateSet::Iter fs = finStateSet; fs.lte(); fs++ )
+ (*fs)->epsilonTrans.append( id );
+}
+
+/* Mark all states reachable from state. Traverses transitions forward. Used
+ * for removing states that have no path into them. */
+void FsmAp::markReachableFromHere( StateAp *state )
+{
+ /* Base case: return; */
+ if ( state->stateBits & SB_ISMARKED )
+ return;
+
+ /* Set this state as processed. We are going to visit all states that this
+ * state has a transition to. */
+ state->stateBits |= SB_ISMARKED;
+
+ /* Recurse on all out transitions. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ if ( trans->toState != 0 )
+ markReachableFromHere( trans->toState );
+ }
+}
+
+void FsmAp::markReachableFromHereStopFinal( StateAp *state )
+{
+ /* Base case: return; */
+ if ( state->stateBits & SB_ISMARKED )
+ return;
+
+ /* Set this state as processed. We are going to visit all states that this
+ * state has a transition to. */
+ state->stateBits |= SB_ISMARKED;
+
+ /* Recurse on all out transitions. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ StateAp *toState = trans->toState;
+ if ( toState != 0 && !toState->isFinState() )
+ markReachableFromHereStopFinal( toState );
+ }
+}
+
+/* Mark all states reachable from state. Traverse transitions backwards. Used
+ * for removing dead end paths in graphs. */
+void FsmAp::markReachableFromHereReverse( StateAp *state )
+{
+ /* Base case: return; */
+ if ( state->stateBits & SB_ISMARKED )
+ return;
+
+ /* Set this state as processed. We are going to visit all states with
+ * transitions into this state. */
+ state->stateBits |= SB_ISMARKED;
+
+ /* Recurse on all items in transitions. */
+ for ( TransInList::Iter trans = state->inList; trans.lte(); trans++ )
+ markReachableFromHereReverse( trans->fromState );
+}
+
+/* Determine if there are any entry points into a start state other than the
+ * start state. Setting starting transitions requires that the start state be
+ * isolated. In most cases a start state will already be isolated. */
+bool FsmAp::isStartStateIsolated()
+{
+ /* If there are any in transitions then the state is not isolated. */
+ if ( startState->inList.head != 0 )
+ return false;
+
+ /* If there are any entry points then isolated. */
+ if ( startState->entryIds.length() > 0 )
+ return false;
+
+ return true;
+}
+
+/* Bring in other's entry points. Assumes others states are going to be
+ * copied into this machine. */
+void FsmAp::copyInEntryPoints( FsmAp *other )
+{
+ /* Use insert multi because names are not unique. */
+ for ( EntryMap::Iter en = other->entryPoints; en.lte(); en++ )
+ entryPoints.insertMulti( en->key, en->value );
+}
+
+
+void FsmAp::unsetAllFinStates()
+{
+ for ( StateSet::Iter st = finStateSet; st.lte(); st++ )
+ (*st)->stateBits &= ~ SB_ISFINAL;
+ finStateSet.empty();
+}
+
+void FsmAp::setFinBits( int finStateBits )
+{
+ for ( int s = 0; s < finStateSet.length(); s++ )
+ finStateSet.data[s]->stateBits |= finStateBits;
+}
+
+
+/* Tests the integrity of the transition lists and the fromStates. */
+void FsmAp::verifyIntegrity()
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ ) {
+ /* Walk the out transitions and assert fromState is correct. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ )
+ assert( trans->fromState == state );
+
+ /* Walk the inlist and assert toState is correct. */
+ for ( TransInList::Iter trans = state->inList; trans.lte(); trans++ )
+ assert( trans->toState == state );
+ }
+}
+
+void FsmAp::verifyReachability()
+{
+ /* Mark all the states that can be reached
+ * through the set of entry points. */
+ markReachableFromHere( startState );
+ for ( EntryMap::Iter en = entryPoints; en.lte(); en++ )
+ markReachableFromHere( en->value );
+
+ /* Check that everything got marked. */
+ for ( StateList::Iter st = stateList; st.lte(); st++ ) {
+ /* Assert it got marked and then clear the mark. */
+ assert( st->stateBits & SB_ISMARKED );
+ st->stateBits &= ~ SB_ISMARKED;
+ }
+}
+
+void FsmAp::verifyNoDeadEndStates()
+{
+ /* Mark all states that have paths to the final states. */
+ for ( StateSet::Iter pst = finStateSet; pst.lte(); pst++ )
+ markReachableFromHereReverse( *pst );
+
+ /* Start state gets honorary marking. Must be done AFTER recursive call. */
+ startState->stateBits |= SB_ISMARKED;
+
+ /* Make sure everything got marked. */
+ for ( StateList::Iter st = stateList; st.lte(); st++ ) {
+ /* Assert the state got marked and unmark it. */
+ assert( st->stateBits & SB_ISMARKED );
+ st->stateBits &= ~ SB_ISMARKED;
+ }
+}
+
+void FsmAp::depthFirstOrdering( StateAp *state )
+{
+ /* Nothing to do if the state is already on the list. */
+ if ( state->stateBits & SB_ONLIST )
+ return;
+
+ /* Doing depth first, put state on the list. */
+ state->stateBits |= SB_ONLIST;
+ stateList.append( state );
+
+ /* Recurse on everything ranges. */
+ for ( TransList::Iter tel = state->outList; tel.lte(); tel++ ) {
+ if ( tel->toState != 0 )
+ depthFirstOrdering( tel->toState );
+ }
+}
+
+/* Ordering states by transition connections. */
+void FsmAp::depthFirstOrdering()
+{
+ /* Init on state list flags. */
+ for ( StateList::Iter st = stateList; st.lte(); st++ )
+ st->stateBits &= ~SB_ONLIST;
+
+ /* Clear out the state list, we will rebuild it. */
+ int stateListLen = stateList.length();
+ stateList.abandon();
+
+ /* Add back to the state list from the start state and all other entry
+ * points. */
+ if ( errState != 0 )
+ depthFirstOrdering( errState );
+ depthFirstOrdering( startState );
+ for ( EntryMap::Iter en = entryPoints; en.lte(); en++ )
+ depthFirstOrdering( en->value );
+
+ /* Make sure we put everything back on. */
+ assert( stateListLen == stateList.length() );
+}
+
+/* Stable sort the states by final state status. */
+void FsmAp::sortStatesByFinal()
+{
+ /* Move forward through the list and throw final states onto the end. */
+ StateAp *state = 0;
+ StateAp *next = stateList.head;
+ StateAp *last = stateList.tail;
+ while ( state != last ) {
+ /* Move forward and load up the next. */
+ state = next;
+ next = state->next;
+
+ /* Throw to the end? */
+ if ( state->isFinState() ) {
+ stateList.detach( state );
+ stateList.append( state );
+ }
+ }
+}
+
+void FsmAp::setStateNumbers( int base )
+{
+ for ( StateList::Iter state = stateList; state.lte(); state++ )
+ state->alg.stateNum = base++;
+}
+
+
+bool FsmAp::checkErrTrans( StateAp *state, TransAp *trans )
+{
+ /* Might go directly to error state. */
+ if ( trans->toState == 0 )
+ return true;
+
+ if ( trans->prev == 0 ) {
+ /* If this is the first transition. */
+ if ( keyOps->minKey < trans->lowKey )
+ return true;
+ }
+ else {
+ /* Not the first transition. Compare against the prev. */
+ TransAp *prev = trans->prev;
+ Key nextKey = prev->highKey;
+ nextKey.increment();
+ if ( nextKey < trans->lowKey )
+ return true;
+ }
+ return false;
+}
+
+bool FsmAp::checkErrTransFinish( StateAp *state )
+{
+ /* Check if there are any ranges already. */
+ if ( state->outList.length() == 0 )
+ return true;
+ else {
+ /* Get the last and check for a gap on the end. */
+ TransAp *last = state->outList.tail;
+ if ( last->highKey < keyOps->maxKey )
+ return true;
+ }
+ return 0;
+}
+
+bool FsmAp::hasErrorTrans()
+{
+ bool result;
+ for ( StateList::Iter st = stateList; st.lte(); st++ ) {
+ for ( TransList::Iter tr = st->outList; tr.lte(); tr++ ) {
+ result = checkErrTrans( st, tr );
+ if ( result )
+ return true;
+ }
+ result = checkErrTransFinish( st );
+ if ( result )
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/tools/ragel5/ragel/fsmgraph.cpp b/contrib/tools/ragel5/ragel/fsmgraph.cpp
new file mode 100644
index 0000000000..d7d0ba4fe2
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/fsmgraph.cpp
@@ -0,0 +1,1426 @@
+/*
+ * Copyright 2001, 2002, 2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <iostream>
+
+#include "fsmgraph.h"
+#include "mergesort.h"
+#include "parsedata.h"
+
+using std::cerr;
+using std::endl;
+
+/* Make a new state. The new state will be put on the graph's
+ * list of state. The new state can be created final or non final. */
+StateAp *FsmAp::addState()
+{
+ /* Make the new state to return. */
+ StateAp *state = new StateAp();
+
+ if ( misfitAccounting ) {
+ /* Create the new state on the misfit list. All states are created
+ * with no foreign in transitions. */
+ misfitList.append( state );
+ }
+ else {
+ /* Create the new state. */
+ stateList.append( state );
+ }
+
+ return state;
+}
+
+/* Construct an FSM that is the concatenation of an array of characters. A new
+ * machine will be made that has len+1 states with one transition between each
+ * state for each integer in str. IsSigned determines if the integers are to
+ * be considered as signed or unsigned ints. */
+void FsmAp::concatFsm( Key *str, int len )
+{
+ /* Make the first state and set it as the start state. */
+ StateAp *last = addState();
+ setStartState( last );
+
+ /* Attach subsequent states. */
+ for ( int i = 0; i < len; i++ ) {
+ StateAp *newState = addState();
+ attachNewTrans( last, newState, str[i], str[i] );
+ last = newState;
+ }
+
+ /* Make the last state the final state. */
+ setFinState( last );
+}
+
+/* Case insensitive version of concatFsm. */
+void FsmAp::concatFsmCI( Key *str, int len )
+{
+ /* Make the first state and set it as the start state. */
+ StateAp *last = addState();
+ setStartState( last );
+
+ /* Attach subsequent states. */
+ for ( int i = 0; i < len; i++ ) {
+ StateAp *newState = addState();
+
+ KeySet keySet;
+ if ( str[i].isLower() )
+ keySet.insert( str[i].toUpper() );
+ if ( str[i].isUpper() )
+ keySet.insert( str[i].toLower() );
+ keySet.insert( str[i] );
+
+ for ( int i = 0; i < keySet.length(); i++ )
+ attachNewTrans( last, newState, keySet[i], keySet[i] );
+
+ last = newState;
+ }
+
+ /* Make the last state the final state. */
+ setFinState( last );
+}
+
+/* Construct a machine that matches one character. A new machine will be made
+ * that has two states with a single transition between the states. IsSigned
+ * determines if the integers are to be considered as signed or unsigned ints. */
+void FsmAp::concatFsm( Key chr )
+{
+ /* Two states first start, second final. */
+ setStartState( addState() );
+
+ StateAp *end = addState();
+ setFinState( end );
+
+ /* Attach on the character. */
+ attachNewTrans( startState, end, chr, chr );
+}
+
+/* Construct a machine that matches any character in set. A new machine will
+ * be made that has two states and len transitions between the them. The set
+ * should be ordered correctly accroding to KeyOps and should not contain
+ * any duplicates. */
+void FsmAp::orFsm( Key *set, int len )
+{
+ /* Two states first start, second final. */
+ setStartState( addState() );
+
+ StateAp *end = addState();
+ setFinState( end );
+
+ for ( int i = 1; i < len; i++ )
+ assert( set[i-1] < set[i] );
+
+ /* Attach on all the integers in the given string of ints. */
+ for ( int i = 0; i < len; i++ )
+ attachNewTrans( startState, end, set[i], set[i] );
+}
+
+/* Construct a machine that matches a range of characters. A new machine will
+ * be made with two states and a range transition between them. The range will
+ * match any characters from low to high inclusive. Low should be less than or
+ * equal to high otherwise undefined behaviour results. IsSigned determines
+ * if the integers are to be considered as signed or unsigned ints. */
+void FsmAp::rangeFsm( Key low, Key high )
+{
+ /* Two states first start, second final. */
+ setStartState( addState() );
+
+ StateAp *end = addState();
+ setFinState( end );
+
+ /* Attach using the range of characters. */
+ attachNewTrans( startState, end, low, high );
+}
+
+/* Construct a machine that a repeated range of characters. */
+void FsmAp::rangeStarFsm( Key low, Key high)
+{
+ /* One state which is final and is the start state. */
+ setStartState( addState() );
+ setFinState( startState );
+
+ /* Attach start to start using range of characters. */
+ attachNewTrans( startState, startState, low, high );
+}
+
+/* Construct a machine that matches the empty string. A new machine will be
+ * made with only one state. The new state will be both a start and final
+ * state. IsSigned determines if the machine has a signed or unsigned
+ * alphabet. Fsm operations must be done on machines with the same alphabet
+ * signedness. */
+void FsmAp::lambdaFsm( )
+{
+ /* Give it one state with no transitions making it
+ * the start state and final state. */
+ setStartState( addState() );
+ setFinState( startState );
+}
+
+/* Construct a machine that matches nothing at all. A new machine will be
+ * made with only one state. It will not be final. */
+void FsmAp::emptyFsm( )
+{
+ /* Give it one state with no transitions making it
+ * the start state and final state. */
+ setStartState( addState() );
+}
+
+void FsmAp::transferOutData( StateAp *destState, StateAp *srcState )
+{
+ for ( TransList::Iter trans = destState->outList; trans.lte(); trans++ ) {
+ if ( trans->toState != 0 ) {
+ /* Get the actions data from the outActionTable. */
+ trans->actionTable.setActions( srcState->outActionTable );
+
+ /* Get the priorities from the outPriorTable. */
+ trans->priorTable.setPriors( srcState->outPriorTable );
+ }
+ }
+}
+
+/* Kleene star operator. Makes this machine the kleene star of itself. Any
+ * transitions made going out of the machine and back into itself will be
+ * notified that they are leaving transitions by having the leavingFromState
+ * callback invoked. */
+void FsmAp::starOp( )
+{
+ /* For the merging process. */
+ MergeData md;
+
+ /* Turn on misfit accounting to possibly catch the old start state. */
+ setMisfitAccounting( true );
+
+ /* Create the new new start state. It will be set final after the merging
+ * of the final states with the start state is complete. */
+ StateAp *prevStartState = startState;
+ unsetStartState();
+ setStartState( addState() );
+
+ /* Merge the new start state with the old one to isolate it. */
+ mergeStates( md, startState, prevStartState );
+
+ /* Merge the start state into all final states. Except the start state on
+ * the first pass. If the start state is set final we will be doubling up
+ * its transitions, which will get transfered to any final states that
+ * follow it in the final state set. This will be determined by the order
+ * of items in the final state set. To prevent this we just merge with the
+ * start on a second pass. */
+ for ( StateSet::Iter st = finStateSet; st.lte(); st++ ) {
+ if ( *st != startState )
+ mergeStatesLeaving( md, *st, startState );
+ }
+
+ /* Now it is safe to merge the start state with itself (provided it
+ * is set final). */
+ if ( startState->isFinState() )
+ mergeStatesLeaving( md, startState, startState );
+
+ /* Now ensure the new start state is a final state. */
+ setFinState( startState );
+
+ /* Fill in any states that were newed up as combinations of others. */
+ fillInStates( md );
+
+ /* Remove the misfits and turn off misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+}
+
+void FsmAp::repeatOp( int times )
+{
+ /* Must be 1 and up. 0 produces null machine and requires deleting this. */
+ assert( times > 0 );
+
+ /* A repeat of one does absolutely nothing. */
+ if ( times == 1 )
+ return;
+
+ /* Make a machine to make copies from. */
+ FsmAp *copyFrom = new FsmAp( *this );
+
+ /* Concatentate duplicates onto the end up until before the last. */
+ for ( int i = 1; i < times-1; i++ ) {
+ FsmAp *dup = new FsmAp( *copyFrom );
+ doConcat( dup, 0, false );
+ }
+
+ /* Now use the copyFrom on the end. */
+ doConcat( copyFrom, 0, false );
+}
+
+void FsmAp::optionalRepeatOp( int times )
+{
+ /* Must be 1 and up. 0 produces null machine and requires deleting this. */
+ assert( times > 0 );
+
+ /* A repeat of one optional merely allows zero string. */
+ if ( times == 1 ) {
+ setFinState( startState );
+ return;
+ }
+
+ /* Make a machine to make copies from. */
+ FsmAp *copyFrom = new FsmAp( *this );
+
+ /* The state set used in the from end of the concatentation. Starts with
+ * the initial final state set, then after each concatenation, gets set to
+ * the the final states that come from the the duplicate. */
+ StateSet lastFinSet( finStateSet );
+
+ /* Set the initial state to zero to allow zero copies. */
+ setFinState( startState );
+
+ /* Concatentate duplicates onto the end up until before the last. */
+ for ( int i = 1; i < times-1; i++ ) {
+ /* Make a duplicate for concating and set the fin bits to graph 2 so we
+ * can pick out it's final states after the optional style concat. */
+ FsmAp *dup = new FsmAp( *copyFrom );
+ dup->setFinBits( SB_GRAPH2 );
+ doConcat( dup, &lastFinSet, true );
+
+ /* Clear the last final state set and make the new one by taking only
+ * the final states that come from graph 2.*/
+ lastFinSet.empty();
+ for ( int i = 0; i < finStateSet.length(); i++ ) {
+ /* If the state came from graph 2, add it to the last set and clear
+ * the bits. */
+ StateAp *fs = finStateSet[i];
+ if ( fs->stateBits & SB_GRAPH2 ) {
+ lastFinSet.insert( fs );
+ fs->stateBits &= ~SB_GRAPH2;
+ }
+ }
+ }
+
+ /* Now use the copyFrom on the end, no bits set, no bits to clear. */
+ doConcat( copyFrom, &lastFinSet, true );
+}
+
+
+/* Fsm concatentation worker. Supports treating the concatentation as optional,
+ * which essentially leaves the final states of machine one as final. */
+void FsmAp::doConcat( FsmAp *other, StateSet *fromStates, bool optional )
+{
+ /* For the merging process. */
+ StateSet finStateSetCopy, startStateSet;
+ MergeData md;
+
+ /* Turn on misfit accounting for both graphs. */
+ setMisfitAccounting( true );
+ other->setMisfitAccounting( true );
+
+ /* Get the other's start state. */
+ StateAp *otherStartState = other->startState;
+
+ /* Unset other's start state before bringing in the entry points. */
+ other->unsetStartState();
+
+ /* Bring in the rest of other's entry points. */
+ copyInEntryPoints( other );
+ other->entryPoints.empty();
+
+ /* Bring in other's states into our state lists. */
+ stateList.append( other->stateList );
+ misfitList.append( other->misfitList );
+
+ /* If from states is not set, then get a copy of our final state set before
+ * we clobber it and use it instead. */
+ if ( fromStates == 0 ) {
+ finStateSetCopy = finStateSet;
+ fromStates = &finStateSetCopy;
+ }
+
+ /* Unset all of our final states and get the final states from other. */
+ if ( !optional )
+ unsetAllFinStates();
+ finStateSet.insert( other->finStateSet );
+
+ /* Since other's lists are empty, we can delete the fsm without
+ * affecting any states. */
+ delete other;
+
+ /* Merge our former final states with the start state of other. */
+ for ( int i = 0; i < fromStates->length(); i++ ) {
+ StateAp *state = fromStates->data[i];
+
+ /* Merge the former final state with other's start state. */
+ mergeStatesLeaving( md, state, otherStartState );
+
+ /* If the former final state was not reset final then we must clear
+ * the state's out trans data. If it got reset final then it gets to
+ * keep its out trans data. This must be done before fillInStates gets
+ * called to prevent the data from being sourced. */
+ if ( ! state->isFinState() )
+ clearOutData( state );
+ }
+
+ /* Fill in any new states made from merging. */
+ fillInStates( md );
+
+ /* Remove the misfits and turn off misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+}
+
+/* Concatenates other to the end of this machine. Other is deleted. Any
+ * transitions made leaving this machine and entering into other are notified
+ * that they are leaving transitions by having the leavingFromState callback
+ * invoked. */
+void FsmAp::concatOp( FsmAp *other )
+{
+ /* Assert same signedness and return graph concatenation op. */
+ doConcat( other, 0, false );
+}
+
+
+void FsmAp::doOr( FsmAp *other )
+{
+ /* For the merging process. */
+ MergeData md;
+
+ /* Build a state set consisting of both start states */
+ StateSet startStateSet;
+ startStateSet.insert( startState );
+ startStateSet.insert( other->startState );
+
+ /* Both of the original start states loose their start state status. */
+ unsetStartState();
+ other->unsetStartState();
+
+ /* Bring in the rest of other's entry points. */
+ copyInEntryPoints( other );
+ other->entryPoints.empty();
+
+ /* Merge the lists. This will move all the states from other
+ * into this. No states will be deleted. */
+ stateList.append( other->stateList );
+ misfitList.append( other->misfitList );
+
+ /* Move the final set data from other into this. */
+ finStateSet.insert(other->finStateSet);
+ other->finStateSet.empty();
+
+ /* Since other's list is empty, we can delete the fsm without
+ * affecting any states. */
+ delete other;
+
+ /* Create a new start state. */
+ setStartState( addState() );
+
+ /* Merge the start states. */
+ mergeStates( md, startState, startStateSet.data, startStateSet.length() );
+
+ /* Fill in any new states made from merging. */
+ fillInStates( md );
+}
+
+/* Unions other with this machine. Other is deleted. */
+void FsmAp::unionOp( FsmAp *other )
+{
+ /* Turn on misfit accounting for both graphs. */
+ setMisfitAccounting( true );
+ other->setMisfitAccounting( true );
+
+ /* Call Worker routine. */
+ doOr( other );
+
+ /* Remove the misfits and turn off misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+}
+
+/* Intersects other with this machine. Other is deleted. */
+void FsmAp::intersectOp( FsmAp *other )
+{
+ /* Turn on misfit accounting for both graphs. */
+ setMisfitAccounting( true );
+ other->setMisfitAccounting( true );
+
+ /* Set the fin bits on this and other to want each other. */
+ setFinBits( SB_GRAPH1 );
+ other->setFinBits( SB_GRAPH2 );
+
+ /* Call worker Or routine. */
+ doOr( other );
+
+ /* Unset any final states that are no longer to
+ * be final due to final bits. */
+ unsetIncompleteFinals();
+
+ /* Remove the misfits and turn off misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+
+ /* Remove states that have no path to a final state. */
+ removeDeadEndStates();
+}
+
+/* Set subtracts other machine from this machine. Other is deleted. */
+void FsmAp::subtractOp( FsmAp *other )
+{
+ /* Turn on misfit accounting for both graphs. */
+ setMisfitAccounting( true );
+ other->setMisfitAccounting( true );
+
+ /* Set the fin bits of other to be killers. */
+ other->setFinBits( SB_GRAPH1 );
+
+ /* Call worker Or routine. */
+ doOr( other );
+
+ /* Unset any final states that are no longer to
+ * be final due to final bits. */
+ unsetKilledFinals();
+
+ /* Remove the misfits and turn off misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+
+ /* Remove states that have no path to a final state. */
+ removeDeadEndStates();
+}
+
+bool FsmAp::inEptVect( EptVect *eptVect, StateAp *state )
+{
+ if ( eptVect != 0 ) {
+ /* Vect is there, walk it looking for state. */
+ for ( int i = 0; i < eptVect->length(); i++ ) {
+ if ( eptVect->data[i].targ == state )
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Fill epsilon vectors in a root state from a given starting point. Epmploys
+ * a depth first search through the graph of epsilon transitions. */
+void FsmAp::epsilonFillEptVectFrom( StateAp *root, StateAp *from, bool parentLeaving )
+{
+ /* Walk the epsilon transitions out of the state. */
+ for ( EpsilonTrans::Iter ep = from->epsilonTrans; ep.lte(); ep++ ) {
+ /* Find the entry point, if the it does not resove, ignore it. */
+ EntryMapEl *enLow, *enHigh;
+ if ( entryPoints.findMulti( *ep, enLow, enHigh ) ) {
+ /* Loop the targets. */
+ for ( EntryMapEl *en = enLow; en <= enHigh; en++ ) {
+ /* Do not add the root or states already in eptVect. */
+ StateAp *targ = en->value;
+ if ( targ != from && !inEptVect(root->eptVect, targ) ) {
+ /* Maybe need to create the eptVect. */
+ if ( root->eptVect == 0 )
+ root->eptVect = new EptVect();
+
+ /* If moving to a different graph or if any parent is
+ * leaving then we are leaving. */
+ bool leaving = parentLeaving ||
+ root->owningGraph != targ->owningGraph;
+
+ /* All ok, add the target epsilon and recurse. */
+ root->eptVect->append( EptVectEl(targ, leaving) );
+ epsilonFillEptVectFrom( root, targ, leaving );
+ }
+ }
+ }
+ }
+}
+
+void FsmAp::shadowReadWriteStates( MergeData &md )
+{
+ /* Init isolatedShadow algorithm data. */
+ for ( StateList::Iter st = stateList; st.lte(); st++ )
+ st->isolatedShadow = 0;
+
+ /* Any states that may be both read from and written to must
+ * be shadowed. */
+ for ( StateList::Iter st = stateList; st.lte(); st++ ) {
+ /* Find such states by looping through stateVect lists, which give us
+ * the states that will be read from. May cause us to visit the states
+ * that we are interested in more than once. */
+ if ( st->eptVect != 0 ) {
+ /* For all states that will be read from. */
+ for ( EptVect::Iter ept = *st->eptVect; ept.lte(); ept++ ) {
+ /* Check for read and write to the same state. */
+ StateAp *targ = ept->targ;
+ if ( targ->eptVect != 0 ) {
+ /* State is to be written to, if the shadow is not already
+ * there, create it. */
+ if ( targ->isolatedShadow == 0 ) {
+ StateAp *shadow = addState();
+ mergeStates( md, shadow, targ );
+ targ->isolatedShadow = shadow;
+ }
+
+ /* Write shadow into the state vector so that it is the
+ * state that the epsilon transition will read from. */
+ ept->targ = targ->isolatedShadow;
+ }
+ }
+ }
+ }
+}
+
+void FsmAp::resolveEpsilonTrans( MergeData &md )
+{
+ /* Walk the state list and invoke recursive worker on each state. */
+ for ( StateList::Iter st = stateList; st.lte(); st++ )
+ epsilonFillEptVectFrom( st, st, false );
+
+ /* Prevent reading from and writing to of the same state. */
+ shadowReadWriteStates( md );
+
+ /* For all states that have epsilon transitions out, draw the transitions,
+ * clear the epsilon transitions. */
+ for ( StateList::Iter st = stateList; st.lte(); st++ ) {
+ /* If there is a state vector, then create the pre-merge state. */
+ if ( st->eptVect != 0 ) {
+ /* Merge all the epsilon targets into the state. */
+ for ( EptVect::Iter ept = *st->eptVect; ept.lte(); ept++ ) {
+ if ( ept->leaving )
+ mergeStatesLeaving( md, st, ept->targ );
+ else
+ mergeStates( md, st, ept->targ );
+ }
+
+ /* Clean up the target list. */
+ delete st->eptVect;
+ st->eptVect = 0;
+ }
+
+ /* Clear the epsilon transitions vector. */
+ st->epsilonTrans.empty();
+ }
+}
+
+void FsmAp::epsilonOp()
+{
+ /* For merging process. */
+ MergeData md;
+
+ setMisfitAccounting( true );
+
+ for ( StateList::Iter st = stateList; st.lte(); st++ )
+ st->owningGraph = 0;
+
+ /* Perform merges. */
+ resolveEpsilonTrans( md );
+
+ /* Epsilons can caused merges which leave behind unreachable states. */
+ fillInStates( md );
+
+ /* Remove the misfits and turn off misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+}
+
+/* Make a new maching by joining together a bunch of machines without making
+ * any transitions between them. A negative finalId results in there being no
+ * final id. */
+void FsmAp::joinOp( int startId, int finalId, FsmAp **others, int numOthers )
+{
+ /* For the merging process. */
+ MergeData md;
+
+ /* Set the owning machines. Start at one. Zero is reserved for the start
+ * and final states. */
+ for ( StateList::Iter st = stateList; st.lte(); st++ )
+ st->owningGraph = 1;
+ for ( int m = 0; m < numOthers; m++ ) {
+ for ( StateList::Iter st = others[m]->stateList; st.lte(); st++ )
+ st->owningGraph = 2+m;
+ }
+
+ /* All machines loose start state status. */
+ unsetStartState();
+ for ( int m = 0; m < numOthers; m++ )
+ others[m]->unsetStartState();
+
+ /* Bring the other machines into this. */
+ for ( int m = 0; m < numOthers; m++ ) {
+ /* Bring in the rest of other's entry points. */
+ copyInEntryPoints( others[m] );
+ others[m]->entryPoints.empty();
+
+ /* Merge the lists. This will move all the states from other into
+ * this. No states will be deleted. */
+ stateList.append( others[m]->stateList );
+ assert( others[m]->misfitList.length() == 0 );
+
+ /* Move the final set data from other into this. */
+ finStateSet.insert( others[m]->finStateSet );
+ others[m]->finStateSet.empty();
+
+ /* Since other's list is empty, we can delete the fsm without
+ * affecting any states. */
+ delete others[m];
+ }
+
+ /* Look up the start entry point. */
+ EntryMapEl *enLow = 0, *enHigh = 0;
+ bool findRes = entryPoints.findMulti( startId, enLow, enHigh );
+ if ( ! findRes ) {
+ /* No start state. Set a default one and proceed with the join. Note
+ * that the result of the join will be a very uninteresting machine. */
+ setStartState( addState() );
+ }
+ else {
+ /* There is at least one start state, create a state that will become
+ * the new start state. */
+ StateAp *newStart = addState();
+ setStartState( newStart );
+
+ /* The start state is in an owning machine class all it's own. */
+ newStart->owningGraph = 0;
+
+ /* Create the set of states to merge from. */
+ StateSet stateSet;
+ for ( EntryMapEl *en = enLow; en <= enHigh; en++ )
+ stateSet.insert( en->value );
+
+ /* Merge in the set of start states into the new start state. */
+ mergeStates( md, newStart, stateSet.data, stateSet.length() );
+ }
+
+ /* Take a copy of the final state set, before unsetting them all. This
+ * will allow us to call clearOutData on the states that don't get
+ * final state status back back. */
+ StateSet finStateSetCopy = finStateSet;
+
+ /* Now all final states are unset. */
+ unsetAllFinStates();
+
+ if ( finalId >= 0 ) {
+ /* Create the implicit final state. */
+ StateAp *finState = addState();
+ setFinState( finState );
+
+ /* Assign an entry into the final state on the final state entry id. Note
+ * that there may already be an entry on this id. That's ok. Also set the
+ * final state owning machine id. It's in a class all it's own. */
+ setEntry( finalId, finState );
+ finState->owningGraph = 0;
+ }
+
+ /* Hand over to workers for resolving epsilon trans. This will merge states
+ * with the targets of their epsilon transitions. */
+ resolveEpsilonTrans( md );
+
+ /* Invoke the relinquish final callback on any states that did not get
+ * final state status back. */
+ for ( StateSet::Iter st = finStateSetCopy; st.lte(); st++ ) {
+ if ( !((*st)->stateBits & SB_ISFINAL) )
+ clearOutData( *st );
+ }
+
+ /* Fill in any new states made from merging. */
+ fillInStates( md );
+
+ /* Joining can be messy. Instead of having misfit accounting on (which is
+ * tricky here) do a full cleaning. */
+ removeUnreachableStates();
+}
+
+void FsmAp::globOp( FsmAp **others, int numOthers )
+{
+ /* All other machines loose start states status. */
+ for ( int m = 0; m < numOthers; m++ )
+ others[m]->unsetStartState();
+
+ /* Bring the other machines into this. */
+ for ( int m = 0; m < numOthers; m++ ) {
+ /* Bring in the rest of other's entry points. */
+ copyInEntryPoints( others[m] );
+ others[m]->entryPoints.empty();
+
+ /* Merge the lists. This will move all the states from other into
+ * this. No states will be deleted. */
+ stateList.append( others[m]->stateList );
+ assert( others[m]->misfitList.length() == 0 );
+
+ /* Move the final set data from other into this. */
+ finStateSet.insert( others[m]->finStateSet );
+ others[m]->finStateSet.empty();
+
+ /* Since other's list is empty, we can delete the fsm without
+ * affecting any states. */
+ delete others[m];
+ }
+}
+
+void FsmAp::deterministicEntry()
+{
+ /* For the merging process. */
+ MergeData md;
+
+ /* States may loose their entry points, turn on misfit accounting. */
+ setMisfitAccounting( true );
+
+ /* Get a copy of the entry map then clear all the entry points. As we
+ * iterate the old entry map finding duplicates we will add the entry
+ * points for the new states that we create. */
+ EntryMap prevEntry = entryPoints;
+ unsetAllEntryPoints();
+
+ for ( int enId = 0; enId < prevEntry.length(); ) {
+ /* Count the number of states on this entry key. */
+ int highId = enId;
+ while ( highId < prevEntry.length() && prevEntry[enId].key == prevEntry[highId].key )
+ highId += 1;
+
+ int numIds = highId - enId;
+ if ( numIds == 1 ) {
+ /* Only a single entry point, just set the entry. */
+ setEntry( prevEntry[enId].key, prevEntry[enId].value );
+ }
+ else {
+ /* Multiple entry points, need to create a new state and merge in
+ * all the targets of entry points. */
+ StateAp *newEntry = addState();
+ for ( int en = enId; en < highId; en++ )
+ mergeStates( md, newEntry, prevEntry[en].value );
+
+ /* Add the new state as the single entry point. */
+ setEntry( prevEntry[enId].key, newEntry );
+ }
+
+ enId += numIds;
+ }
+
+ /* The old start state may be unreachable. Remove the misfits and turn off
+ * misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+}
+
+/* Unset any final states that are no longer to be final due to final bits. */
+void FsmAp::unsetKilledFinals()
+{
+ /* Duplicate the final state set before we begin modifying it. */
+ StateSet fin( finStateSet );
+
+ for ( int s = 0; s < fin.length(); s++ ) {
+ /* Check for killing bit. */
+ StateAp *state = fin.data[s];
+ if ( state->stateBits & SB_GRAPH1 ) {
+ /* One final state is a killer, set to non-final. */
+ unsetFinState( state );
+ }
+
+ /* Clear all killing bits. Non final states should never have had those
+ * state bits set in the first place. */
+ state->stateBits &= ~SB_GRAPH1;
+ }
+}
+
+/* Unset any final states that are no longer to be final due to final bits. */
+void FsmAp::unsetIncompleteFinals()
+{
+ /* Duplicate the final state set before we begin modifying it. */
+ StateSet fin( finStateSet );
+
+ for ( int s = 0; s < fin.length(); s++ ) {
+ /* Check for one set but not the other. */
+ StateAp *state = fin.data[s];
+ if ( state->stateBits & SB_BOTH &&
+ (state->stateBits & SB_BOTH) != SB_BOTH )
+ {
+ /* One state wants the other but it is not there. */
+ unsetFinState( state );
+ }
+
+ /* Clear wanting bits. Non final states should never have had those
+ * state bits set in the first place. */
+ state->stateBits &= ~SB_BOTH;
+ }
+}
+
+/* Ensure that the start state is free of entry points (aside from the fact
+ * that it is the start state). If the start state has entry points then Make a
+ * new start state by merging with the old one. Useful before modifying start
+ * transitions. If the existing start state has any entry points other than the
+ * start state entry then modifying its transitions changes more than the start
+ * transitions. So isolate the start state by separating it out such that it
+ * only has start stateness as it's entry point. */
+void FsmAp::isolateStartState( )
+{
+ /* For the merging process. */
+ MergeData md;
+
+ /* Bail out if the start state is already isolated. */
+ if ( isStartStateIsolated() )
+ return;
+
+ /* Turn on misfit accounting to possibly catch the old start state. */
+ setMisfitAccounting( true );
+
+ /* This will be the new start state. The existing start
+ * state is merged with it. */
+ StateAp *prevStartState = startState;
+ unsetStartState();
+ setStartState( addState() );
+
+ /* Merge the new start state with the old one to isolate it. */
+ mergeStates( md, startState, prevStartState );
+
+ /* Stfil and stateDict will be empty because the merging of the old start
+ * state into the new one will not have any conflicting transitions. */
+ assert( md.stateDict.treeSize == 0 );
+ assert( md.stfillHead == 0 );
+
+ /* The old start state may be unreachable. Remove the misfits and turn off
+ * misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+}
+
+#ifdef LOG_CONDS
+void logCondSpace( CondSpace *condSpace )
+{
+ if ( condSpace == 0 )
+ cerr << "<empty>";
+ else {
+ for ( CondSet::Iter csi = condSpace->condSet.last(); csi.gtb(); csi-- ) {
+ if ( ! csi.last() )
+ cerr << ',';
+ (*csi)->actionName( cerr );
+ }
+ }
+}
+
+void logNewExpansion( Expansion *exp )
+{
+ cerr << "created expansion:" << endl;
+ cerr << " range: " << exp->lowKey.getVal() << " .. " <<
+ exp->highKey.getVal() << endl;
+
+ cerr << " fromCondSpace: ";
+ logCondSpace( exp->fromCondSpace );
+ cerr << endl;
+ cerr << " fromVals: " << exp->fromVals << endl;
+
+ cerr << " toCondSpace: ";
+ logCondSpace( exp->toCondSpace );
+ cerr << endl;
+ cerr << " toValsList: ";
+ for ( LongVect::Iter to = exp->toValsList; to.lte(); to++ )
+ cerr << " " << *to;
+ cerr << endl;
+}
+#endif
+
+
+void FsmAp::findTransExpansions( ExpansionList &expansionList,
+ StateAp *destState, StateAp *srcState )
+{
+ PairIter<TransAp, StateCond> transCond( destState->outList.head,
+ srcState->stateCondList.head );
+ for ( ; !transCond.end(); transCond++ ) {
+ if ( transCond.userState == RangeOverlap ) {
+ Expansion *expansion = new Expansion( transCond.s1Tel.lowKey,
+ transCond.s1Tel.highKey );
+ expansion->fromTrans = new TransAp(*transCond.s1Tel.trans);
+ expansion->fromTrans->fromState = 0;
+ expansion->fromTrans->toState = transCond.s1Tel.trans->toState;
+ expansion->fromCondSpace = 0;
+ expansion->fromVals = 0;
+ CondSpace *srcCS = transCond.s2Tel.trans->condSpace;
+ expansion->toCondSpace = srcCS;
+
+ long numTargVals = (1 << srcCS->condSet.length());
+ for ( long targVals = 0; targVals < numTargVals; targVals++ )
+ expansion->toValsList.append( targVals );
+
+ #ifdef LOG_CONDS
+ logNewExpansion( expansion );
+ #endif
+ expansionList.append( expansion );
+ }
+ }
+}
+
+void FsmAp::findCondExpInTrans( ExpansionList &expansionList, StateAp *state,
+ Key lowKey, Key highKey, CondSpace *fromCondSpace, CondSpace *toCondSpace,
+ long fromVals, LongVect &toValsList )
+{
+ TransAp searchTrans;
+ searchTrans.lowKey = fromCondSpace->baseKey + fromVals * keyOps->alphSize() +
+ (lowKey - keyOps->minKey);
+ searchTrans.highKey = fromCondSpace->baseKey + fromVals * keyOps->alphSize() +
+ (highKey - keyOps->minKey);
+ searchTrans.prev = searchTrans.next = 0;
+
+ PairIter<TransAp> pairIter( state->outList.head, &searchTrans );
+ for ( ; !pairIter.end(); pairIter++ ) {
+ if ( pairIter.userState == RangeOverlap ) {
+ Expansion *expansion = new Expansion( lowKey, highKey );
+ expansion->fromTrans = new TransAp(*pairIter.s1Tel.trans);
+ expansion->fromTrans->fromState = 0;
+ expansion->fromTrans->toState = pairIter.s1Tel.trans->toState;
+ expansion->fromCondSpace = fromCondSpace;
+ expansion->fromVals = fromVals;
+ expansion->toCondSpace = toCondSpace;
+ expansion->toValsList = toValsList;
+
+ expansionList.append( expansion );
+ #ifdef LOG_CONDS
+ logNewExpansion( expansion );
+ #endif
+ }
+ }
+}
+
+void FsmAp::findCondExpansions( ExpansionList &expansionList,
+ StateAp *destState, StateAp *srcState )
+{
+ PairIter<StateCond, StateCond> condCond( destState->stateCondList.head,
+ srcState->stateCondList.head );
+ for ( ; !condCond.end(); condCond++ ) {
+ if ( condCond.userState == RangeOverlap ) {
+ /* Loop over all existing condVals . */
+ CondSet &destCS = condCond.s1Tel.trans->condSpace->condSet;
+ long destLen = destCS.length();
+
+ /* Find the items in src cond set that are not in dest
+ * cond set. These are the items that we must expand. */
+ CondSet srcOnlyCS = condCond.s2Tel.trans->condSpace->condSet;
+ for ( CondSet::Iter dcsi = destCS; dcsi.lte(); dcsi++ )
+ srcOnlyCS.remove( *dcsi );
+ long srcOnlyLen = srcOnlyCS.length();
+
+ if ( srcOnlyCS.length() > 0 ) {
+ #ifdef LOG_CONDS
+ cerr << "there are " << srcOnlyCS.length() << " item(s) that are "
+ "only in the srcCS" << endl;
+ #endif
+
+ CondSet mergedCS = destCS;
+ mergedCS.insert( condCond.s2Tel.trans->condSpace->condSet );
+
+ CondSpace *fromCondSpace = addCondSpace( destCS );
+ CondSpace *toCondSpace = addCondSpace( mergedCS );
+
+ /* Loop all values in the dest space. */
+ for ( long destVals = 0; destVals < (1 << destLen); destVals++ ) {
+ long basicVals = 0;
+ for ( CondSet::Iter csi = destCS; csi.lte(); csi++ ) {
+ if ( destVals & (1 << csi.pos()) ) {
+ Action **cim = mergedCS.find( *csi );
+ long bitPos = (cim - mergedCS.data);
+ basicVals |= 1 << bitPos;
+ }
+ }
+
+ /* Loop all new values. */
+ LongVect expandToVals;
+ for ( long soVals = 0; soVals < (1 << srcOnlyLen); soVals++ ) {
+ long targVals = basicVals;
+ for ( CondSet::Iter csi = srcOnlyCS; csi.lte(); csi++ ) {
+ if ( soVals & (1 << csi.pos()) ) {
+ Action **cim = mergedCS.find( *csi );
+ long bitPos = (cim - mergedCS.data);
+ targVals |= 1 << bitPos;
+ }
+ }
+ expandToVals.append( targVals );
+ }
+
+ findCondExpInTrans( expansionList, destState,
+ condCond.s1Tel.lowKey, condCond.s1Tel.highKey,
+ fromCondSpace, toCondSpace, destVals, expandToVals );
+ }
+ }
+ }
+ }
+}
+
+void FsmAp::doExpand( MergeData &md, StateAp *destState, ExpansionList &expList1 )
+{
+ for ( ExpansionList::Iter exp = expList1; exp.lte(); exp++ ) {
+ for ( LongVect::Iter to = exp->toValsList; to.lte(); to++ ) {
+ long targVals = *to;
+
+ /* We will use the copy of the transition that was made when the
+ * expansion was created. It will get used multiple times. Each
+ * time we must set up the keys, everything else is constant and
+ * and already prepared. */
+ TransAp *srcTrans = exp->fromTrans;
+
+ srcTrans->lowKey = exp->toCondSpace->baseKey +
+ targVals * keyOps->alphSize() + (exp->lowKey - keyOps->minKey);
+ srcTrans->highKey = exp->toCondSpace->baseKey +
+ targVals * keyOps->alphSize() + (exp->highKey - keyOps->minKey);
+
+ TransList srcList;
+ srcList.append( srcTrans );
+ outTransCopy( md, destState, srcList.head );
+ srcList.abandon();
+ }
+ }
+}
+
+
+void FsmAp::doRemove( MergeData &md, StateAp *destState, ExpansionList &expList1 )
+{
+ for ( ExpansionList::Iter exp = expList1; exp.lte(); exp++ ) {
+ Removal removal;
+ if ( exp->fromCondSpace == 0 ) {
+ removal.lowKey = exp->lowKey;
+ removal.highKey = exp->highKey;
+ }
+ else {
+ removal.lowKey = exp->fromCondSpace->baseKey +
+ exp->fromVals * keyOps->alphSize() + (exp->lowKey - keyOps->minKey);
+ removal.highKey = exp->fromCondSpace->baseKey +
+ exp->fromVals * keyOps->alphSize() + (exp->highKey - keyOps->minKey);
+ }
+ removal.next = 0;
+
+ TransList destList;
+ PairIter<TransAp, Removal> pairIter( destState->outList.head, &removal );
+ for ( ; !pairIter.end(); pairIter++ ) {
+ switch ( pairIter.userState ) {
+ case RangeInS1: {
+ TransAp *destTrans = pairIter.s1Tel.trans;
+ destTrans->lowKey = pairIter.s1Tel.lowKey;
+ destTrans->highKey = pairIter.s1Tel.highKey;
+ destList.append( destTrans );
+ break;
+ }
+ case RangeInS2:
+ break;
+ case RangeOverlap: {
+ TransAp *trans = pairIter.s1Tel.trans;
+ detachTrans( trans->fromState, trans->toState, trans );
+ delete trans;
+ break;
+ }
+ case BreakS1: {
+ pairIter.s1Tel.trans = dupTrans( destState,
+ pairIter.s1Tel.trans );
+ break;
+ }
+ case BreakS2:
+ break;
+ }
+ }
+ destState->outList.transfer( destList );
+ }
+}
+
+void FsmAp::mergeStateConds( StateAp *destState, StateAp *srcState )
+{
+ StateCondList destList;
+ PairIter<StateCond> pairIter( destState->stateCondList.head,
+ srcState->stateCondList.head );
+ for ( ; !pairIter.end(); pairIter++ ) {
+ switch ( pairIter.userState ) {
+ case RangeInS1: {
+ StateCond *destCond = pairIter.s1Tel.trans;
+ destCond->lowKey = pairIter.s1Tel.lowKey;
+ destCond->highKey = pairIter.s1Tel.highKey;
+ destList.append( destCond );
+ break;
+ }
+ case RangeInS2: {
+ StateCond *newCond = new StateCond( *pairIter.s2Tel.trans );
+ newCond->lowKey = pairIter.s2Tel.lowKey;
+ newCond->highKey = pairIter.s2Tel.highKey;
+ destList.append( newCond );
+ break;
+ }
+ case RangeOverlap: {
+ StateCond *destCond = pairIter.s1Tel.trans;
+ StateCond *srcCond = pairIter.s2Tel.trans;
+ CondSet mergedCondSet;
+ mergedCondSet.insert( destCond->condSpace->condSet );
+ mergedCondSet.insert( srcCond->condSpace->condSet );
+ destCond->condSpace = addCondSpace( mergedCondSet );
+
+ destCond->lowKey = pairIter.s1Tel.lowKey;
+ destCond->highKey = pairIter.s1Tel.highKey;
+ destList.append( destCond );
+ break;
+ }
+ case BreakS1:
+ pairIter.s1Tel.trans = new StateCond( *pairIter.s1Tel.trans );
+ break;
+
+ case BreakS2:
+ break;
+ }
+ }
+ destState->stateCondList.transfer( destList );
+}
+
+/* A state merge which represents the drawing in of leaving transitions. If
+ * there is any out data then we duplicate the souce state, transfer the out
+ * data, then merge in the state. The new state will be reaped because it will
+ * not be given any in transitions. */
+void FsmAp::mergeStatesLeaving( MergeData &md, StateAp *destState, StateAp *srcState )
+{
+ if ( !hasOutData( destState ) )
+ mergeStates( md, destState, srcState );
+ else {
+ StateAp *ssMutable = addState();
+ mergeStates( md, ssMutable, srcState );
+ transferOutData( ssMutable, destState );
+
+ for ( ActionSet::Iter cond = destState->outCondSet; cond.lte(); cond++ )
+ embedCondition( md, ssMutable, *cond );
+
+ mergeStates( md, destState, ssMutable );
+ }
+}
+
+void FsmAp::mergeStates( MergeData &md, StateAp *destState,
+ StateAp **srcStates, int numSrc )
+{
+ for ( int s = 0; s < numSrc; s++ )
+ mergeStates( md, destState, srcStates[s] );
+}
+
+void FsmAp::mergeStates( MergeData &md, StateAp *destState, StateAp *srcState )
+{
+ ExpansionList expList1;
+ ExpansionList expList2;
+
+ findTransExpansions( expList1, destState, srcState );
+ findCondExpansions( expList1, destState, srcState );
+ findTransExpansions( expList2, srcState, destState );
+ findCondExpansions( expList2, srcState, destState );
+
+ mergeStateConds( destState, srcState );
+
+ outTransCopy( md, destState, srcState->outList.head );
+
+ doExpand( md, destState, expList1 );
+ doExpand( md, destState, expList2 );
+
+ doRemove( md, destState, expList1 );
+ doRemove( md, destState, expList2 );
+
+ expList1.empty();
+ expList2.empty();
+
+ /* Get its bits and final state status. */
+ destState->stateBits |= ( srcState->stateBits & ~SB_ISFINAL );
+ if ( srcState->isFinState() )
+ setFinState( destState );
+
+ /* Draw in any properties of srcState into destState. */
+ if ( srcState == destState ) {
+ /* Duplicate the list to protect against write to source. The
+ * priorities sets are not copied in because that would have no
+ * effect. */
+ destState->epsilonTrans.append( EpsilonTrans( srcState->epsilonTrans ) );
+
+ /* Get all actions, duplicating to protect against write to source. */
+ destState->toStateActionTable.setActions(
+ ActionTable( srcState->toStateActionTable ) );
+ destState->fromStateActionTable.setActions(
+ ActionTable( srcState->fromStateActionTable ) );
+ destState->outActionTable.setActions( ActionTable( srcState->outActionTable ) );
+ destState->outCondSet.insert( ActionSet( srcState->outCondSet ) );
+ destState->errActionTable.setActions( ErrActionTable( srcState->errActionTable ) );
+ destState->eofActionTable.setActions( ActionTable( srcState->eofActionTable ) );
+ }
+ else {
+ /* Get the epsilons, out priorities. */
+ destState->epsilonTrans.append( srcState->epsilonTrans );
+ destState->outPriorTable.setPriors( srcState->outPriorTable );
+
+ /* Get all actions. */
+ destState->toStateActionTable.setActions( srcState->toStateActionTable );
+ destState->fromStateActionTable.setActions( srcState->fromStateActionTable );
+ destState->outActionTable.setActions( srcState->outActionTable );
+ destState->outCondSet.insert( srcState->outCondSet );
+ destState->errActionTable.setActions( srcState->errActionTable );
+ destState->eofActionTable.setActions( srcState->eofActionTable );
+ }
+}
+
+void FsmAp::fillInStates( MergeData &md )
+{
+ /* Merge any states that are awaiting merging. This will likey cause
+ * other states to be added to the stfil list. */
+ StateAp *state = md.stfillHead;
+ while ( state != 0 ) {
+ StateSet *stateSet = &state->stateDictEl->stateSet;
+ mergeStates( md, state, stateSet->data, stateSet->length() );
+ state = state->alg.next;
+ }
+
+ /* Delete the state sets of all states that are on the fill list. */
+ state = md.stfillHead;
+ while ( state != 0 ) {
+ /* Delete and reset the state set. */
+ delete state->stateDictEl;
+ state->stateDictEl = 0;
+
+ /* Next state in the stfill list. */
+ state = state->alg.next;
+ }
+
+ /* StateDict will still have its ptrs/size set but all of it's element
+ * will be deleted so we don't need to clean it up. */
+}
+
+void FsmAp::findEmbedExpansions( ExpansionList &expansionList,
+ StateAp *destState, Action *condAction )
+{
+ StateCondList destList;
+ PairIter<TransAp, StateCond> transCond( destState->outList.head,
+ destState->stateCondList.head );
+ for ( ; !transCond.end(); transCond++ ) {
+ switch ( transCond.userState ) {
+ case RangeInS1: {
+ if ( transCond.s1Tel.lowKey <= keyOps->maxKey ) {
+ assert( transCond.s1Tel.highKey <= keyOps->maxKey );
+
+ /* Make a new state cond. */
+ StateCond *newStateCond = new StateCond( transCond.s1Tel.lowKey,
+ transCond.s1Tel.highKey );
+ newStateCond->condSpace = addCondSpace( CondSet( condAction ) );
+ destList.append( newStateCond );
+
+ /* Create the expansion. */
+ Expansion *expansion = new Expansion( transCond.s1Tel.lowKey,
+ transCond.s1Tel.highKey );
+ expansion->fromTrans = new TransAp(*transCond.s1Tel.trans);
+ expansion->fromTrans->fromState = 0;
+ expansion->fromTrans->toState = transCond.s1Tel.trans->toState;
+ expansion->fromCondSpace = 0;
+ expansion->fromVals = 0;
+ expansion->toCondSpace = newStateCond->condSpace;
+ expansion->toValsList.append( 1 );
+ #ifdef LOG_CONDS
+ logNewExpansion( expansion );
+ #endif
+ expansionList.append( expansion );
+ }
+ break;
+ }
+ case RangeInS2: {
+ /* Enhance state cond and find the expansion. */
+ StateCond *stateCond = transCond.s2Tel.trans;
+ stateCond->lowKey = transCond.s2Tel.lowKey;
+ stateCond->highKey = transCond.s2Tel.highKey;
+
+ CondSet &destCS = stateCond->condSpace->condSet;
+ long destLen = destCS.length();
+ CondSpace *fromCondSpace = stateCond->condSpace;
+
+ CondSet mergedCS = destCS;
+ mergedCS.insert( condAction );
+ CondSpace *toCondSpace = addCondSpace( mergedCS );
+ stateCond->condSpace = toCondSpace;
+ destList.append( stateCond );
+
+ /* Loop all values in the dest space. */
+ for ( long destVals = 0; destVals < (1 << destLen); destVals++ ) {
+ long basicVals = 0;
+ for ( CondSet::Iter csi = destCS; csi.lte(); csi++ ) {
+ if ( destVals & (1 << csi.pos()) ) {
+ Action **cim = mergedCS.find( *csi );
+ long bitPos = (cim - mergedCS.data);
+ basicVals |= 1 << bitPos;
+ }
+ }
+
+ long targVals = basicVals;
+ Action **cim = mergedCS.find( condAction );
+ long bitPos = (cim - mergedCS.data);
+ targVals |= 1 << bitPos;
+
+ LongVect expandToVals( targVals );
+ findCondExpInTrans( expansionList, destState,
+ transCond.s2Tel.lowKey, transCond.s2Tel.highKey,
+ fromCondSpace, toCondSpace, destVals, expandToVals );
+ }
+ break;
+ }
+
+
+ case RangeOverlap:
+ case BreakS1:
+ case BreakS2:
+ assert( false );
+ break;
+ }
+ }
+
+ destState->stateCondList.transfer( destList );
+}
+
+void FsmAp::embedCondition( StateAp *state, Action *condAction )
+{
+ MergeData md;
+ ExpansionList expList;
+
+ /* Turn on misfit accounting to possibly catch the old start state. */
+ setMisfitAccounting( true );
+
+ /* Worker. */
+ embedCondition( md, state, condAction );
+
+ /* Fill in any states that were newed up as combinations of others. */
+ fillInStates( md );
+
+ /* Remove the misfits and turn off misfit accounting. */
+ removeMisfits();
+ setMisfitAccounting( false );
+}
+
+void FsmAp::embedCondition( MergeData &md, StateAp *state, Action *condAction )
+{
+ ExpansionList expList;
+
+ findEmbedExpansions( expList, state, condAction );
+ doExpand( md, state, expList );
+ doRemove( md, state, expList );
+ expList.empty();
+}
+
+/* Check if a machine defines a single character. This is useful in validating
+ * ranges and machines to export. */
+bool FsmAp::checkSingleCharMachine()
+{
+ /* Must have two states. */
+ if ( stateList.length() != 2 )
+ return false;
+ /* The start state cannot be final. */
+ if ( startState->isFinState() )
+ return false;
+ /* There should be only one final state. */
+ if ( finStateSet.length() != 1 )
+ return false;
+ /* The final state cannot have any transitions out. */
+ if ( finStateSet[0]->outList.length() != 0 )
+ return false;
+ /* The start state should have only one transition out. */
+ if ( startState->outList.length() != 1 )
+ return false;
+ /* The singe transition out of the start state should not be a range. */
+ TransAp *startTrans = startState->outList.head;
+ if ( startTrans->lowKey != startTrans->highKey )
+ return false;
+ return true;
+}
+
diff --git a/contrib/tools/ragel5/ragel/fsmgraph.h b/contrib/tools/ragel5/ragel/fsmgraph.h
new file mode 100644
index 0000000000..062031c3aa
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/fsmgraph.h
@@ -0,0 +1,1482 @@
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FSMGRAPH_H
+#define _FSMGRAPH_H
+
+#include <assert.h>
+#include <iostream>
+#include "common.h"
+#include "vector.h"
+#include "bstset.h"
+#include "compare.h"
+#include "avltree.h"
+#include "dlist.h"
+#include "bstmap.h"
+#include "sbstmap.h"
+#include "sbstset.h"
+#include "sbsttable.h"
+#include "avlset.h"
+#include "avlmap.h"
+#include "ragel.h"
+
+//#define LOG_CONDS
+
+/* Flags that control merging. */
+#define SB_GRAPH1 0x01
+#define SB_GRAPH2 0x02
+#define SB_BOTH 0x03
+#define SB_ISFINAL 0x04
+#define SB_ISMARKED 0x08
+#define SB_ONLIST 0x10
+
+using std::ostream;
+
+struct TransAp;
+struct StateAp;
+struct FsmAp;
+struct Action;
+struct LongestMatchPart;
+
+/* State list element for unambiguous access to list element. */
+struct FsmListEl
+{
+ StateAp *prev, *next;
+};
+
+/* This is the marked index for a state pair. Used in minimization. It keeps
+ * track of whether or not the state pair is marked. */
+struct MarkIndex
+{
+ MarkIndex(int states);
+ ~MarkIndex();
+
+ void markPair(int state1, int state2);
+ bool isPairMarked(int state1, int state2);
+
+private:
+ int numStates;
+ bool *array;
+};
+
+extern KeyOps *keyOps;
+
+/* Transistion Action Element. */
+typedef SBstMapEl< int, Action* > ActionTableEl;
+
+/* Nodes in the tree that use this action. */
+struct NameInst;
+struct InlineList;
+typedef Vector<NameInst*> ActionRefs;
+
+/* Element in list of actions. Contains the string for the code to exectute. */
+struct Action
+:
+ public DListEl<Action>,
+ public AvlTreeEl<Action>
+{
+public:
+
+ Action( const InputLoc &loc, const char *name, InlineList *inlineList, int condId )
+ :
+ loc(loc),
+ name(name),
+ inlineList(inlineList),
+ actionId(-1),
+ numTransRefs(0),
+ numToStateRefs(0),
+ numFromStateRefs(0),
+ numEofRefs(0),
+ numCondRefs(0),
+ anyCall(false),
+ isLmAction(false),
+ condId(condId)
+ {
+ }
+
+ /* Key for action dictionary. */
+ const char *getKey() const { return name; }
+
+ /* Data collected during parse. */
+ InputLoc loc;
+ const char *name;
+ InlineList *inlineList;
+ int actionId;
+
+ void actionName( ostream &out )
+ {
+ if ( name != 0 )
+ out << name;
+ else
+ out << loc.line << ":" << loc.col;
+ }
+
+ /* Places in the input text that reference the action. */
+ ActionRefs actionRefs;
+
+ /* Number of references in the final machine. */
+ int numRefs()
+ { return numTransRefs + numToStateRefs + numFromStateRefs + numEofRefs; }
+ int numTransRefs;
+ int numToStateRefs;
+ int numFromStateRefs;
+ int numEofRefs;
+ int numCondRefs;
+ bool anyCall;
+
+ bool isLmAction;
+ int condId;
+};
+
+struct CmpCondId
+{
+ static inline int compare( const Action *cond1, const Action *cond2 )
+ {
+ if ( cond1->condId < cond2->condId )
+ return -1;
+ else if ( cond1->condId > cond2->condId )
+ return 1;
+ return 0;
+ }
+};
+
+/* A list of actions. */
+typedef DList<Action> ActionList;
+typedef AvlTree<Action, char *, CmpStr> ActionDict;
+
+/* Structure for reverse action mapping. */
+struct RevActionMapEl
+{
+ char *name;
+ InputLoc location;
+};
+
+
+/* Transition Action Table. */
+struct ActionTable
+ : public SBstMap< int, Action*, CmpOrd<int> >
+{
+ void setAction( int ordering, Action *action );
+ void setActions( int *orderings, Action **actions, int nActs );
+ void setActions( const ActionTable &other );
+
+ bool hasAction( Action *action );
+};
+
+typedef SBstSet< Action*, CmpOrd<Action*> > ActionSet;
+typedef CmpSTable< Action*, CmpOrd<Action*> > CmpActionSet;
+
+/* Transistion Action Element. */
+typedef SBstMapEl< int, LongestMatchPart* > LmActionTableEl;
+
+/* Transition Action Table. */
+struct LmActionTable
+ : public SBstMap< int, LongestMatchPart*, CmpOrd<int> >
+{
+ void setAction( int ordering, LongestMatchPart *action );
+ void setActions( const LmActionTable &other );
+};
+
+/* Compare of a whole action table element (key & value). */
+struct CmpActionTableEl
+{
+ static int compare( const ActionTableEl &action1,
+ const ActionTableEl &action2 )
+ {
+ if ( action1.key < action2.key )
+ return -1;
+ else if ( action1.key > action2.key )
+ return 1;
+ else if ( action1.value < action2.value )
+ return -1;
+ else if ( action1.value > action2.value )
+ return 1;
+ return 0;
+ }
+};
+
+/* Compare for ActionTable. */
+typedef CmpSTable< ActionTableEl, CmpActionTableEl > CmpActionTable;
+
+/* Compare of a whole lm action table element (key & value). */
+struct CmpLmActionTableEl
+{
+ static int compare( const LmActionTableEl &lmAction1,
+ const LmActionTableEl &lmAction2 )
+ {
+ if ( lmAction1.key < lmAction2.key )
+ return -1;
+ else if ( lmAction1.key > lmAction2.key )
+ return 1;
+ else if ( lmAction1.value < lmAction2.value )
+ return -1;
+ else if ( lmAction1.value > lmAction2.value )
+ return 1;
+ return 0;
+ }
+};
+
+/* Compare for ActionTable. */
+typedef CmpSTable< LmActionTableEl, CmpLmActionTableEl > CmpLmActionTable;
+
+/* Action table element for error action tables. Adds the encoding of transfer
+ * point. */
+struct ErrActionTableEl
+{
+ ErrActionTableEl( Action *action, int ordering, int transferPoint )
+ : ordering(ordering), action(action), transferPoint(transferPoint) { }
+
+ /* Ordering and id of the action embedding. */
+ int ordering;
+ Action *action;
+
+ /* Id of point of transfere from Error action table to transtions and
+ * eofActionTable. */
+ int transferPoint;
+
+ int getKey() const { return ordering; }
+};
+
+struct ErrActionTable
+ : public SBstTable< ErrActionTableEl, int, CmpOrd<int> >
+{
+ void setAction( int ordering, Action *action, int transferPoint );
+ void setActions( const ErrActionTable &other );
+};
+
+/* Compare of an error action table element (key & value). */
+struct CmpErrActionTableEl
+{
+ static int compare( const ErrActionTableEl &action1,
+ const ErrActionTableEl &action2 )
+ {
+ if ( action1.ordering < action2.ordering )
+ return -1;
+ else if ( action1.ordering > action2.ordering )
+ return 1;
+ else if ( action1.action < action2.action )
+ return -1;
+ else if ( action1.action > action2.action )
+ return 1;
+ else if ( action1.transferPoint < action2.transferPoint )
+ return -1;
+ else if ( action1.transferPoint > action2.transferPoint )
+ return 1;
+ return 0;
+ }
+};
+
+/* Compare for ErrActionTable. */
+typedef CmpSTable< ErrActionTableEl, CmpErrActionTableEl > CmpErrActionTable;
+
+
+/* Descibe a priority, shared among PriorEls.
+ * Has key and whether or not used. */
+struct PriorDesc
+{
+ int key;
+ int priority;
+};
+
+/* Element in the arrays of priorities for transitions and arrays. Ordering is
+ * unique among instantiations of machines, desc is shared. */
+struct PriorEl
+{
+ PriorEl( int ordering, PriorDesc *desc )
+ : ordering(ordering), desc(desc) { }
+
+ int ordering;
+ PriorDesc *desc;
+};
+
+/* Compare priority elements, which are ordered by the priority descriptor
+ * key. */
+struct PriorElCmp
+{
+ static inline int compare( const PriorEl &pel1, const PriorEl &pel2 )
+ {
+ if ( pel1.desc->key < pel2.desc->key )
+ return -1;
+ else if ( pel1.desc->key > pel2.desc->key )
+ return 1;
+ else
+ return 0;
+ }
+};
+
+
+/* Priority Table. */
+struct PriorTable
+ : public SBstSet< PriorEl, PriorElCmp >
+{
+ void setPrior( int ordering, PriorDesc *desc );
+ void setPriors( const PriorTable &other );
+};
+
+/* Compare of prior table elements for distinguising state data. */
+struct CmpPriorEl
+{
+ static inline int compare( const PriorEl &pel1, const PriorEl &pel2 )
+ {
+ if ( pel1.desc < pel2.desc )
+ return -1;
+ else if ( pel1.desc > pel2.desc )
+ return 1;
+ else if ( pel1.ordering < pel2.ordering )
+ return -1;
+ else if ( pel1.ordering > pel2.ordering )
+ return 1;
+ return 0;
+ }
+};
+
+/* Compare of PriorTable distinguising state data. Using a compare of the
+ * pointers is a little more strict than it needs be. It requires that
+ * prioritiy tables have the exact same set of priority assignment operators
+ * (from the input lang) to be considered equal.
+ *
+ * Really only key-value pairs need be tested and ordering be merged. However
+ * this would require that in the fuseing of states, priority descriptors be
+ * chosen for the new fused state based on priority. Since the out transition
+ * lists and ranges aren't necessarily going to line up, this is more work for
+ * little gain. Final compression resets all priorities first, so this would
+ * only be useful for compression at every operator, which is only an
+ * undocumented test feature.
+ */
+typedef CmpSTable<PriorEl, CmpPriorEl> CmpPriorTable;
+
+/* Plain action list that imposes no ordering. */
+typedef Vector<int> TransFuncList;
+
+/* Comparison for TransFuncList. */
+typedef CmpTable< int, CmpOrd<int> > TransFuncListCompare;
+
+/* Transition class that implements actions and priorities. */
+struct TransAp
+{
+ TransAp() : fromState(0), toState(0) {}
+ TransAp( const TransAp &other ) :
+ lowKey(other.lowKey),
+ highKey(other.highKey),
+ fromState(0), toState(0),
+ actionTable(other.actionTable),
+ priorTable(other.priorTable)
+ {
+ assert( lmActionTable.length() == 0 && other.lmActionTable.length() == 0 );
+ }
+
+ Key lowKey, highKey;
+ StateAp *fromState;
+ StateAp *toState;
+
+ /* Pointers for outlist. */
+ TransAp *prev, *next;
+
+ /* Pointers for in-list. */
+ TransAp *ilprev, *ilnext;
+
+ /* The function table and priority for the transition. */
+ ActionTable actionTable;
+ PriorTable priorTable;
+
+ LmActionTable lmActionTable;
+};
+
+/* In transition list. Like DList except only has head pointers, which is all
+ * that is required. Insertion and deletion is handled by the graph. This
+ * class provides the iterator of a single list. */
+struct TransInList
+{
+ TransInList() : head(0) { }
+
+ TransAp *head;
+
+ struct Iter
+ {
+ /* Default construct. */
+ Iter() : ptr(0) { }
+
+ /* Construct, assign from a list. */
+ Iter( const TransInList &il ) : ptr(il.head) { }
+ Iter &operator=( const TransInList &dl ) { ptr = dl.head; return *this; }
+
+ /* At the end */
+ bool lte() const { return ptr != 0; }
+ bool end() const { return ptr == 0; }
+
+ /* At the first, last element. */
+ bool first() const { return ptr && ptr->ilprev == 0; }
+ bool last() const { return ptr && ptr->ilnext == 0; }
+
+ /* Cast, dereference, arrow ops. */
+ operator TransAp*() const { return ptr; }
+ TransAp &operator *() const { return *ptr; }
+ TransAp *operator->() const { return ptr; }
+
+ /* Increment, decrement. */
+ inline void operator++(int) { ptr = ptr->ilnext; }
+ inline void operator--(int) { ptr = ptr->ilprev; }
+
+ /* The iterator is simply a pointer. */
+ TransAp *ptr;
+ };
+};
+
+typedef DList<TransAp> TransList;
+
+/* Set of states, list of states. */
+typedef BstSet<StateAp*> StateSet;
+typedef DList<StateAp> StateList;
+
+/* A element in a state dict. */
+struct StateDictEl
+:
+ public AvlTreeEl<StateDictEl>
+{
+ StateDictEl(const StateSet &stateSet)
+ : stateSet(stateSet) { }
+
+ const StateSet &getKey() { return stateSet; }
+ StateSet stateSet;
+ StateAp *targState;
+};
+
+/* Dictionary mapping a set of states to a target state. */
+typedef AvlTree< StateDictEl, StateSet, CmpTable<StateAp*> > StateDict;
+
+/* Data needed for a merge operation. */
+struct MergeData
+{
+ MergeData()
+ : stfillHead(0), stfillTail(0) { }
+
+ StateDict stateDict;
+
+ StateAp *stfillHead;
+ StateAp *stfillTail;
+
+ void fillListAppend( StateAp *state );
+};
+
+struct TransEl
+{
+ /* Constructors. */
+ TransEl() { }
+ TransEl( Key lowKey, Key highKey )
+ : lowKey(lowKey), highKey(highKey) { }
+ TransEl( Key lowKey, Key highKey, TransAp *value )
+ : lowKey(lowKey), highKey(highKey), value(value) { }
+
+ Key lowKey, highKey;
+ TransAp *value;
+};
+
+struct CmpKey
+{
+ static int compare( const Key key1, const Key key2 )
+ {
+ if ( key1 < key2 )
+ return -1;
+ else if ( key1 > key2 )
+ return 1;
+ else
+ return 0;
+ }
+};
+
+/* Vector based set of key items. */
+typedef BstSet<Key, CmpKey> KeySet;
+
+struct MinPartition
+{
+ MinPartition() : active(false) { }
+
+ StateList list;
+ bool active;
+
+ MinPartition *prev, *next;
+};
+
+/* Epsilon transition stored in a state. Specifies the target */
+typedef Vector<int> EpsilonTrans;
+
+/* List of states that are to be drawn into this. */
+struct EptVectEl
+{
+ EptVectEl( StateAp *targ, bool leaving )
+ : targ(targ), leaving(leaving) { }
+
+ StateAp *targ;
+ bool leaving;
+};
+typedef Vector<EptVectEl> EptVect;
+
+/* Set of entry ids that go into this state. */
+typedef BstSet<int> EntryIdSet;
+
+/* Set of longest match items that may be active in a given state. */
+typedef BstSet<LongestMatchPart*> LmItemSet;
+
+/* Conditions. */
+typedef BstSet< Action*, CmpCondId > CondSet;
+typedef CmpTable< Action*, CmpCondId > CmpCondSet;
+
+struct CondSpace
+ : public AvlTreeEl<CondSpace>
+{
+ CondSpace( const CondSet &condSet )
+ : condSet(condSet) {}
+
+ const CondSet &getKey() { return condSet; }
+
+ CondSet condSet;
+ Key baseKey;
+ long condSpaceId;
+};
+
+typedef Vector<CondSpace*> CondSpaceVect;
+
+typedef AvlTree<CondSpace, CondSet, CmpCondSet> CondSpaceMap;
+
+struct StateCond
+{
+ StateCond( Key lowKey, Key highKey ) :
+ lowKey(lowKey), highKey(highKey) {}
+
+ Key lowKey;
+ Key highKey;
+ CondSpace *condSpace;
+
+ StateCond *prev, *next;
+};
+
+typedef DList<StateCond> StateCondList;
+typedef Vector<long> LongVect;
+
+struct Expansion
+{
+ Expansion( Key lowKey, Key highKey ) :
+ lowKey(lowKey), highKey(highKey),
+ fromTrans(0), fromCondSpace(0),
+ toCondSpace(0) {}
+
+ ~Expansion()
+ {
+ if ( fromTrans != 0 )
+ delete fromTrans;
+ }
+
+ Key lowKey;
+ Key highKey;
+
+ TransAp *fromTrans;
+ CondSpace *fromCondSpace;
+ long fromVals;
+
+ CondSpace *toCondSpace;
+ LongVect toValsList;
+
+ Expansion *prev, *next;
+};
+
+typedef DList<Expansion> ExpansionList;
+
+struct Removal
+{
+ Key lowKey;
+ Key highKey;
+
+ Removal *next;
+};
+
+struct CondData
+{
+ CondData() : nextCondKey(0) {}
+
+ /* Condition info. */
+ Key nextCondKey;
+
+ CondSpaceMap condSpaceMap;
+};
+
+extern CondData *condData;
+
+/* State class that implements actions and priorities. */
+struct StateAp
+{
+ StateAp();
+ StateAp(const StateAp &other);
+ ~StateAp();
+
+ /* Is the state final? */
+ bool isFinState() { return stateBits & SB_ISFINAL; }
+
+ /* Out transition list and the pointer for the default out trans. */
+ TransList outList;
+
+ /* In transition Lists. */
+ TransInList inList;
+
+ /* Entry points into the state. */
+ EntryIdSet entryIds;
+
+ /* Epsilon transitions. */
+ EpsilonTrans epsilonTrans;
+
+ /* Condition info. */
+ StateCondList stateCondList;
+
+ /* Number of in transitions from states other than ourselves. */
+ int foreignInTrans;
+
+ /* Temporary data for various algorithms. */
+ union {
+ /* When duplicating the fsm we need to map each
+ * state to the new state representing it. */
+ StateAp *stateMap;
+
+ /* When minimizing machines by partitioning, this maps to the group
+ * the state is in. */
+ MinPartition *partition;
+
+ /* When merging states (state machine operations) this next pointer is
+ * used for the list of states that need to be filled in. */
+ StateAp *next;
+
+ /* Identification for printing and stable minimization. */
+ int stateNum;
+
+ } alg;
+
+ /* Data used in epsilon operation, maybe fit into alg? */
+ StateAp *isolatedShadow;
+ int owningGraph;
+
+ /* A pointer to a dict element that contains the set of states this state
+ * represents. This cannot go into alg, because alg.next is used during
+ * the merging process. */
+ StateDictEl *stateDictEl;
+
+ /* When drawing epsilon transitions, holds the list of states to merge
+ * with. */
+ EptVect *eptVect;
+
+ /* Bits controlling the behaviour of the state during collapsing to dfa. */
+ int stateBits;
+
+ /* State list elements. */
+ StateAp *next, *prev;
+
+ /*
+ * Priority and Action data.
+ */
+
+ /* Out priorities transfered to out transitions. */
+ PriorTable outPriorTable;
+
+ /* The following two action tables are distinguished by the fact that when
+ * toState actions are executed immediatly after transition actions of
+ * incoming transitions and the current character will be the same as the
+ * one available then. The fromState actions are executed immediately
+ * before the transition actions of outgoing transitions and the current
+ * character is same as the one available then. */
+
+ /* Actions to execute upon entering into a state. */
+ ActionTable toStateActionTable;
+
+ /* Actions to execute when going from the state to the transition. */
+ ActionTable fromStateActionTable;
+
+ /* Actions to add to any future transitions that leave via this state. */
+ ActionTable outActionTable;
+
+ /* Conditions to add to any future transiions that leave via this sttate. */
+ ActionSet outCondSet;
+
+ /* Error action tables. */
+ ErrActionTable errActionTable;
+
+ /* Actions to execute on eof. */
+ ActionTable eofActionTable;
+
+ /* Set of longest match items that may be active in this state. */
+ LmItemSet lmItemSet;
+};
+
+template <class ListItem> struct NextTrans
+{
+ Key lowKey, highKey;
+ ListItem *trans;
+ ListItem *next;
+
+ void load() {
+ if ( trans == 0 )
+ next = 0;
+ else {
+ next = trans->next;
+ lowKey = trans->lowKey;
+ highKey = trans->highKey;
+ }
+ }
+
+ void set( ListItem *t ) {
+ trans = t;
+ load();
+ }
+
+ void increment() {
+ trans = next;
+ load();
+ }
+};
+
+
+/* Encodes the different states that are meaningful to the of the iterator. */
+enum PairIterUserState
+{
+ RangeInS1, RangeInS2,
+ RangeOverlap,
+ BreakS1, BreakS2
+};
+
+template <class ListItem1, class ListItem2 = ListItem1> struct PairIter
+{
+ /* Encodes the different states that an fsm iterator can be in. */
+ enum IterState {
+ Begin,
+ ConsumeS1Range, ConsumeS2Range,
+ OnlyInS1Range, OnlyInS2Range,
+ S1SticksOut, S1SticksOutBreak,
+ S2SticksOut, S2SticksOutBreak,
+ S1DragsBehind, S1DragsBehindBreak,
+ S2DragsBehind, S2DragsBehindBreak,
+ ExactOverlap, End
+ };
+
+ PairIter( ListItem1 *list1, ListItem2 *list2 );
+
+ /* Query iterator. */
+ bool lte() { return itState != End; }
+ bool end() { return itState == End; }
+ void operator++(int) { findNext(); }
+ void operator++() { findNext(); }
+
+ /* Iterator state. */
+ ListItem1 *list1;
+ ListItem2 *list2;
+ IterState itState;
+ PairIterUserState userState;
+
+ NextTrans<ListItem1> s1Tel;
+ NextTrans<ListItem2> s2Tel;
+ Key bottomLow, bottomHigh;
+ ListItem1 *bottomTrans1;
+ ListItem2 *bottomTrans2;
+
+private:
+ void findNext();
+};
+
+/* Init the iterator by advancing to the first item. */
+template <class ListItem1, class ListItem2> PairIter<ListItem1, ListItem2>::PairIter(
+ ListItem1 *list1, ListItem2 *list2 )
+:
+ list1(list1),
+ list2(list2),
+ itState(Begin)
+{
+ findNext();
+}
+
+/* Return and re-entry for the co-routine iterators. This should ALWAYS be
+ * used inside of a block. */
+#define CO_RETURN(label) \
+ itState = label; \
+ return; \
+ entry##label: backIn = true
+
+/* Return and re-entry for the co-routine iterators. This should ALWAYS be
+ * used inside of a block. */
+#define CO_RETURN2(label, uState) \
+ itState = label; \
+ userState = uState; \
+ return; \
+ entry##label: backIn = true
+
+/* Advance to the next transition. When returns, trans points to the next
+ * transition, unless there are no more, in which case end() returns true. */
+template <class ListItem1, class ListItem2> void PairIter<ListItem1, ListItem2>::findNext()
+{
+ /* This variable is used in dummy statements that follow the entry
+ * goto labels. The compiler needs some statement to follow the label. */
+ bool backIn;
+
+ /* Jump into the iterator routine base on the iterator state. */
+ switch ( itState ) {
+ case Begin: goto entryBegin;
+ case ConsumeS1Range: goto entryConsumeS1Range;
+ case ConsumeS2Range: goto entryConsumeS2Range;
+ case OnlyInS1Range: goto entryOnlyInS1Range;
+ case OnlyInS2Range: goto entryOnlyInS2Range;
+ case S1SticksOut: goto entryS1SticksOut;
+ case S1SticksOutBreak: goto entryS1SticksOutBreak;
+ case S2SticksOut: goto entryS2SticksOut;
+ case S2SticksOutBreak: goto entryS2SticksOutBreak;
+ case S1DragsBehind: goto entryS1DragsBehind;
+ case S1DragsBehindBreak: goto entryS1DragsBehindBreak;
+ case S2DragsBehind: goto entryS2DragsBehind;
+ case S2DragsBehindBreak: goto entryS2DragsBehindBreak;
+ case ExactOverlap: goto entryExactOverlap;
+ case End: goto entryEnd;
+ }
+
+entryBegin:
+ /* Set up the next structs at the head of the transition lists. */
+ s1Tel.set( list1 );
+ s2Tel.set( list2 );
+
+ /* Concurrently scan both out ranges. */
+ while ( true ) {
+ if ( s1Tel.trans == 0 ) {
+ /* We are at the end of state1's ranges. Process the rest of
+ * state2's ranges. */
+ while ( s2Tel.trans != 0 ) {
+ /* Range is only in s2. */
+ CO_RETURN2( ConsumeS2Range, RangeInS2 );
+ s2Tel.increment();
+ }
+ break;
+ }
+ else if ( s2Tel.trans == 0 ) {
+ /* We are at the end of state2's ranges. Process the rest of
+ * state1's ranges. */
+ while ( s1Tel.trans != 0 ) {
+ /* Range is only in s1. */
+ CO_RETURN2( ConsumeS1Range, RangeInS1 );
+ s1Tel.increment();
+ }
+ break;
+ }
+ /* Both state1's and state2's transition elements are good.
+ * The signiture of no overlap is a back key being in front of a
+ * front key. */
+ else if ( s1Tel.highKey < s2Tel.lowKey ) {
+ /* A range exists in state1 that does not overlap with state2. */
+ CO_RETURN2( OnlyInS1Range, RangeInS1 );
+ s1Tel.increment();
+ }
+ else if ( s2Tel.highKey < s1Tel.lowKey ) {
+ /* A range exists in state2 that does not overlap with state1. */
+ CO_RETURN2( OnlyInS2Range, RangeInS2 );
+ s2Tel.increment();
+ }
+ /* There is overlap, must mix the ranges in some way. */
+ else if ( s1Tel.lowKey < s2Tel.lowKey ) {
+ /* Range from state1 sticks out front. Must break it into
+ * non-overlaping and overlaping segments. */
+ bottomLow = s2Tel.lowKey;
+ bottomHigh = s1Tel.highKey;
+ s1Tel.highKey = s2Tel.lowKey;
+ s1Tel.highKey.decrement();
+ bottomTrans1 = s1Tel.trans;
+
+ /* Notify the caller that we are breaking s1. This gives them a
+ * chance to duplicate s1Tel[0,1].value. */
+ CO_RETURN2( S1SticksOutBreak, BreakS1 );
+
+ /* Broken off range is only in s1. */
+ CO_RETURN2( S1SticksOut, RangeInS1 );
+
+ /* Advance over the part sticking out front. */
+ s1Tel.lowKey = bottomLow;
+ s1Tel.highKey = bottomHigh;
+ s1Tel.trans = bottomTrans1;
+ }
+ else if ( s2Tel.lowKey < s1Tel.lowKey ) {
+ /* Range from state2 sticks out front. Must break it into
+ * non-overlaping and overlaping segments. */
+ bottomLow = s1Tel.lowKey;
+ bottomHigh = s2Tel.highKey;
+ s2Tel.highKey = s1Tel.lowKey;
+ s2Tel.highKey.decrement();
+ bottomTrans2 = s2Tel.trans;
+
+ /* Notify the caller that we are breaking s2. This gives them a
+ * chance to duplicate s2Tel[0,1].value. */
+ CO_RETURN2( S2SticksOutBreak, BreakS2 );
+
+ /* Broken off range is only in s2. */
+ CO_RETURN2( S2SticksOut, RangeInS2 );
+
+ /* Advance over the part sticking out front. */
+ s2Tel.lowKey = bottomLow;
+ s2Tel.highKey = bottomHigh;
+ s2Tel.trans = bottomTrans2;
+ }
+ /* Low ends are even. Are the high ends even? */
+ else if ( s1Tel.highKey < s2Tel.highKey ) {
+ /* Range from state2 goes longer than the range from state1. We
+ * must break the range from state2 into an evenly overlaping
+ * segment. */
+ bottomLow = s1Tel.highKey;
+ bottomLow.increment();
+ bottomHigh = s2Tel.highKey;
+ s2Tel.highKey = s1Tel.highKey;
+ bottomTrans2 = s2Tel.trans;
+
+ /* Notify the caller that we are breaking s2. This gives them a
+ * chance to duplicate s2Tel[0,1].value. */
+ CO_RETURN2( S2DragsBehindBreak, BreakS2 );
+
+ /* Breaking s2 produces exact overlap. */
+ CO_RETURN2( S2DragsBehind, RangeOverlap );
+
+ /* Advance over the front we just broke off of range 2. */
+ s2Tel.lowKey = bottomLow;
+ s2Tel.highKey = bottomHigh;
+ s2Tel.trans = bottomTrans2;
+
+ /* Advance over the entire s1Tel. We have consumed it. */
+ s1Tel.increment();
+ }
+ else if ( s2Tel.highKey < s1Tel.highKey ) {
+ /* Range from state1 goes longer than the range from state2. We
+ * must break the range from state1 into an evenly overlaping
+ * segment. */
+ bottomLow = s2Tel.highKey;
+ bottomLow.increment();
+ bottomHigh = s1Tel.highKey;
+ s1Tel.highKey = s2Tel.highKey;
+ bottomTrans1 = s1Tel.trans;
+
+ /* Notify the caller that we are breaking s1. This gives them a
+ * chance to duplicate s2Tel[0,1].value. */
+ CO_RETURN2( S1DragsBehindBreak, BreakS1 );
+
+ /* Breaking s1 produces exact overlap. */
+ CO_RETURN2( S1DragsBehind, RangeOverlap );
+
+ /* Advance over the front we just broke off of range 1. */
+ s1Tel.lowKey = bottomLow;
+ s1Tel.highKey = bottomHigh;
+ s1Tel.trans = bottomTrans1;
+
+ /* Advance over the entire s2Tel. We have consumed it. */
+ s2Tel.increment();
+ }
+ else {
+ /* There is an exact overlap. */
+ CO_RETURN2( ExactOverlap, RangeOverlap );
+
+ s1Tel.increment();
+ s2Tel.increment();
+ }
+ }
+
+ /* Done, go into end state. */
+ CO_RETURN( End );
+}
+
+
+/* Compare lists of epsilon transitions. Entries are name ids of targets. */
+typedef CmpTable< int, CmpOrd<int> > CmpEpsilonTrans;
+
+/* Compare class for the Approximate minimization. */
+class ApproxCompare
+{
+public:
+ ApproxCompare() { }
+ int compare( const StateAp *pState1, const StateAp *pState2 );
+};
+
+/* Compare class for the initial partitioning of a partition minimization. */
+class InitPartitionCompare
+{
+public:
+ InitPartitionCompare() { }
+ int compare( const StateAp *pState1, const StateAp *pState2 );
+};
+
+/* Compare class for the regular partitioning of a partition minimization. */
+class PartitionCompare
+{
+public:
+ PartitionCompare() { }
+ int compare( const StateAp *pState1, const StateAp *pState2 );
+};
+
+/* Compare class for a minimization that marks pairs. Provides the shouldMark
+ * routine. */
+class MarkCompare
+{
+public:
+ MarkCompare() { }
+ bool shouldMark( MarkIndex &markIndex, const StateAp *pState1,
+ const StateAp *pState2 );
+};
+
+/* List of partitions. */
+typedef DList< MinPartition > PartitionList;
+
+/* List of transtions out of a state. */
+typedef Vector<TransEl> TransListVect;
+
+/* Entry point map used for keeping track of entry points in a machine. */
+typedef BstSet< int > EntryIdSet;
+typedef BstMapEl< int, StateAp* > EntryMapEl;
+typedef BstMap< int, StateAp* > EntryMap;
+typedef Vector<EntryMapEl> EntryMapBase;
+
+/* Graph class that implements actions and priorities. */
+struct FsmAp
+{
+ /* Constructors/Destructors. */
+ FsmAp( );
+ FsmAp( const FsmAp &graph );
+ ~FsmAp();
+
+ /* The list of states. */
+ StateList stateList;
+ StateList misfitList;
+
+ /* The map of entry points. */
+ EntryMap entryPoints;
+
+ /* The start state. */
+ StateAp *startState;
+
+ /* Error state, possibly created only when the final machine has been
+ * created and the XML machine is about to be written. No transitions
+ * point to this state. */
+ StateAp *errState;
+
+ /* The set of final states. */
+ StateSet finStateSet;
+
+ /* Misfit Accounting. Are misfits put on a separate list. */
+ bool misfitAccounting;
+
+ /*
+ * Transition actions and priorities.
+ */
+
+ /* Set priorities on transtions. */
+ void startFsmPrior( int ordering, PriorDesc *prior );
+ void allTransPrior( int ordering, PriorDesc *prior );
+ void finishFsmPrior( int ordering, PriorDesc *prior );
+ void leaveFsmPrior( int ordering, PriorDesc *prior );
+
+ /* Action setting support. */
+ void transferErrorActions( StateAp *state, int transferPoint );
+ void setErrorAction( StateAp *state, int ordering, Action *action );
+
+ /* Fill all spaces in a transition list with an error transition. */
+ void fillGaps( StateAp *state );
+
+ /* Similar to setErrorAction, instead gives a state to go to on error. */
+ void setErrorTarget( StateAp *state, StateAp *target, int *orderings,
+ Action **actions, int nActs );
+
+ /* Set actions to execute. */
+ void startFsmAction( int ordering, Action *action );
+ void allTransAction( int ordering, Action *action );
+ void finishFsmAction( int ordering, Action *action );
+ void leaveFsmAction( int ordering, Action *action );
+ void longMatchAction( int ordering, LongestMatchPart *lmPart );
+
+ /* Set conditions. */
+ CondSpace *addCondSpace( const CondSet &condSet );
+
+ void findEmbedExpansions( ExpansionList &expansionList,
+ StateAp *destState, Action *condAction );
+ void embedCondition( MergeData &md, StateAp *state, Action *condAction );
+ void embedCondition( StateAp *state, Action *condAction );
+
+ void startFsmCondition( Action *condAction );
+ void allTransCondition( Action *condAction );
+ void leaveFsmCondition( Action *condAction );
+
+ /* Set error actions to execute. */
+ void startErrorAction( int ordering, Action *action, int transferPoint );
+ void allErrorAction( int ordering, Action *action, int transferPoint );
+ void finalErrorAction( int ordering, Action *action, int transferPoint );
+ void notStartErrorAction( int ordering, Action *action, int transferPoint );
+ void notFinalErrorAction( int ordering, Action *action, int transferPoint );
+ void middleErrorAction( int ordering, Action *action, int transferPoint );
+
+ /* Set EOF actions. */
+ void startEOFAction( int ordering, Action *action );
+ void allEOFAction( int ordering, Action *action );
+ void finalEOFAction( int ordering, Action *action );
+ void notStartEOFAction( int ordering, Action *action );
+ void notFinalEOFAction( int ordering, Action *action );
+ void middleEOFAction( int ordering, Action *action );
+
+ /* Set To State actions. */
+ void startToStateAction( int ordering, Action *action );
+ void allToStateAction( int ordering, Action *action );
+ void finalToStateAction( int ordering, Action *action );
+ void notStartToStateAction( int ordering, Action *action );
+ void notFinalToStateAction( int ordering, Action *action );
+ void middleToStateAction( int ordering, Action *action );
+
+ /* Set From State actions. */
+ void startFromStateAction( int ordering, Action *action );
+ void allFromStateAction( int ordering, Action *action );
+ void finalFromStateAction( int ordering, Action *action );
+ void notStartFromStateAction( int ordering, Action *action );
+ void notFinalFromStateAction( int ordering, Action *action );
+ void middleFromStateAction( int ordering, Action *action );
+
+ /* Shift the action ordering of the start transitions to start at
+ * fromOrder and increase in units of 1. Useful before kleene star
+ * operation. */
+ int shiftStartActionOrder( int fromOrder );
+
+ /* Clear all priorities from the fsm to so they won't affcet minimization
+ * of the final fsm. */
+ void clearAllPriorities();
+
+ /* Zero out all the function keys. */
+ void nullActionKeys();
+
+ /* Walk the list of states and verify state properties. */
+ void verifyStates();
+
+ /* Misfit Accounting. Are misfits put on a separate list. */
+ void setMisfitAccounting( bool val )
+ { misfitAccounting = val; }
+
+ /* Set and Unset a state as final. */
+ void setFinState( StateAp *state );
+ void unsetFinState( StateAp *state );
+
+ void setStartState( StateAp *state );
+ void unsetStartState( );
+
+ /* Set and unset a state as an entry point. */
+ void setEntry( int id, StateAp *state );
+ void changeEntry( int id, StateAp *to, StateAp *from );
+ void unsetEntry( int id, StateAp *state );
+ void unsetEntry( int id );
+ void unsetAllEntryPoints();
+
+ /* Epsilon transitions. */
+ void epsilonTrans( int id );
+ void shadowReadWriteStates( MergeData &md );
+
+ /*
+ * Basic attaching and detaching.
+ */
+
+ /* Common to attaching/detaching list and default. */
+ void attachToInList( StateAp *from, StateAp *to, TransAp *&head, TransAp *trans );
+ void detachFromInList( StateAp *from, StateAp *to, TransAp *&head, TransAp *trans );
+
+ /* Attach with a new transition. */
+ TransAp *attachNewTrans( StateAp *from, StateAp *to,
+ Key onChar1, Key onChar2 );
+
+ /* Attach with an existing transition that already in an out list. */
+ void attachTrans( StateAp *from, StateAp *to, TransAp *trans );
+
+ /* Redirect a transition away from error and towards some state. */
+ void redirectErrorTrans( StateAp *from, StateAp *to, TransAp *trans );
+
+ /* Detach a transition from a target state. */
+ void detachTrans( StateAp *from, StateAp *to, TransAp *trans );
+
+ /* Detach a state from the graph. */
+ void detachState( StateAp *state );
+
+ /*
+ * NFA to DFA conversion routines.
+ */
+
+ /* Duplicate a transition that will dropin to a free spot. */
+ TransAp *dupTrans( StateAp *from, TransAp *srcTrans );
+
+ /* In crossing, two transitions both go to real states. */
+ TransAp *fsmAttachStates( MergeData &md, StateAp *from,
+ TransAp *destTrans, TransAp *srcTrans );
+
+ /* Two transitions are to be crossed, handle the possibility of either
+ * going to the error state. */
+ TransAp *mergeTrans( MergeData &md, StateAp *from,
+ TransAp *destTrans, TransAp *srcTrans );
+
+ /* Compare deterimne relative priorities of two transition tables. */
+ int comparePrior( const PriorTable &priorTable1, const PriorTable &priorTable2 );
+
+ /* Cross a src transition with one that is already occupying a spot. */
+ TransAp *crossTransitions( MergeData &md, StateAp *from,
+ TransAp *destTrans, TransAp *srcTrans );
+
+ void outTransCopy( MergeData &md, StateAp *dest, TransAp *srcList );
+
+ void doRemove( MergeData &md, StateAp *destState, ExpansionList &expList1 );
+ void doExpand( MergeData &md, StateAp *destState, ExpansionList &expList1 );
+ void findCondExpInTrans( ExpansionList &expansionList, StateAp *state,
+ Key lowKey, Key highKey, CondSpace *fromCondSpace, CondSpace *toCondSpace,
+ long destVals, LongVect &toValsList );
+ void findTransExpansions( ExpansionList &expansionList,
+ StateAp *destState, StateAp *srcState );
+ void findCondExpansions( ExpansionList &expansionList,
+ StateAp *destState, StateAp *srcState );
+ void mergeStateConds( StateAp *destState, StateAp *srcState );
+
+ /* Merge a set of states into newState. */
+ void mergeStates( MergeData &md, StateAp *destState,
+ StateAp **srcStates, int numSrc );
+ void mergeStatesLeaving( MergeData &md, StateAp *destState, StateAp *srcState );
+ void mergeStates( MergeData &md, StateAp *destState, StateAp *srcState );
+
+ /* Make all states that are combinations of other states and that
+ * have not yet had their out transitions filled in. This will
+ * empty out stateDict and stFil. */
+ void fillInStates( MergeData &md );
+
+ /*
+ * Transition Comparison.
+ */
+
+ /* Compare transition data. Either of the pointers may be null. */
+ static inline int compareDataPtr( TransAp *trans1, TransAp *trans2 );
+
+ /* Compare target state and transition data. Either pointer may be null. */
+ static inline int compareFullPtr( TransAp *trans1, TransAp *trans2 );
+
+ /* Compare target partitions. Either pointer may be null. */
+ static inline int comparePartPtr( TransAp *trans1, TransAp *trans2 );
+
+ /* Check marked status of target states. Either pointer may be null. */
+ static inline bool shouldMarkPtr( MarkIndex &markIndex,
+ TransAp *trans1, TransAp *trans2 );
+
+ /*
+ * Callbacks.
+ */
+
+ /* Compare priority and function table of transitions. */
+ static int compareTransData( TransAp *trans1, TransAp *trans2 );
+
+ /* Add in the properties of srcTrans into this. */
+ void addInTrans( TransAp *destTrans, TransAp *srcTrans );
+
+ /* Compare states on data stored in the states. */
+ static int compareStateData( const StateAp *state1, const StateAp *state2 );
+
+ /* Out transition data. */
+ void clearOutData( StateAp *state );
+ bool hasOutData( StateAp *state );
+ void transferOutData( StateAp *destState, StateAp *srcState );
+
+ /*
+ * Allocation.
+ */
+
+ /* New up a state and add it to the graph. */
+ StateAp *addState();
+
+ /*
+ * Building basic machines
+ */
+
+ void concatFsm( Key c );
+ void concatFsm( Key *str, int len );
+ void concatFsmCI( Key *str, int len );
+ void orFsm( Key *set, int len );
+ void rangeFsm( Key low, Key high );
+ void rangeStarFsm( Key low, Key high );
+ void emptyFsm( );
+ void lambdaFsm( );
+
+ /*
+ * Fsm operators.
+ */
+
+ void starOp( );
+ void repeatOp( int times );
+ void optionalRepeatOp( int times );
+ void concatOp( FsmAp *other );
+ void unionOp( FsmAp *other );
+ void intersectOp( FsmAp *other );
+ void subtractOp( FsmAp *other );
+ void epsilonOp();
+ void joinOp( int startId, int finalId, FsmAp **others, int numOthers );
+ void globOp( FsmAp **others, int numOthers );
+ void deterministicEntry();
+
+ /*
+ * Operator workers
+ */
+
+ /* Determine if there are any entry points into a start state other than
+ * the start state. */
+ bool isStartStateIsolated();
+
+ /* Make a new start state that has no entry points. Will not change the
+ * identity of the fsm. */
+ void isolateStartState();
+
+ /* Workers for resolving epsilon transitions. */
+ bool inEptVect( EptVect *eptVect, StateAp *targ );
+ void epsilonFillEptVectFrom( StateAp *root, StateAp *from, bool parentLeaving );
+ void resolveEpsilonTrans( MergeData &md );
+
+ /* Workers for concatenation and union. */
+ void doConcat( FsmAp *other, StateSet *fromStates, bool optional );
+ void doOr( FsmAp *other );
+
+ /*
+ * Final states
+ */
+
+ /* Unset any final states that are no longer to be final
+ * due to final bits. */
+ void unsetIncompleteFinals();
+ void unsetKilledFinals();
+
+ /* Bring in other's entry points. Assumes others states are going to be
+ * copied into this machine. */
+ void copyInEntryPoints( FsmAp *other );
+
+ /* Ordering states. */
+ void depthFirstOrdering( StateAp *state );
+ void depthFirstOrdering();
+ void sortStatesByFinal();
+
+ /* Set sqequential state numbers starting at 0. */
+ void setStateNumbers( int base );
+
+ /* Unset all final states. */
+ void unsetAllFinStates();
+
+ /* Set the bits of final states and clear the bits of non final states. */
+ void setFinBits( int finStateBits );
+
+ /*
+ * Self-consistency checks.
+ */
+
+ /* Run a sanity check on the machine. */
+ void verifyIntegrity();
+
+ /* Verify that there are no unreachable states, or dead end states. */
+ void verifyReachability();
+ void verifyNoDeadEndStates();
+
+ /*
+ * Path pruning
+ */
+
+ /* Mark all states reachable from state. */
+ void markReachableFromHereReverse( StateAp *state );
+
+ /* Mark all states reachable from state. */
+ void markReachableFromHere( StateAp *state );
+ void markReachableFromHereStopFinal( StateAp *state );
+
+ /* Removes states that cannot be reached by any path in the fsm and are
+ * thus wasted silicon. */
+ void removeDeadEndStates();
+
+ /* Removes states that cannot be reached by any path in the fsm and are
+ * thus wasted silicon. */
+ void removeUnreachableStates();
+
+ /* Remove error actions from states on which the error transition will
+ * never be taken. */
+ bool outListCovers( StateAp *state );
+ bool anyErrorRange( StateAp *state );
+
+ /* Remove states that are on the misfit list. */
+ void removeMisfits();
+
+ /*
+ * FSM Minimization
+ */
+
+ /* Minimization by partitioning. */
+ void minimizePartition1();
+ void minimizePartition2();
+
+ /* Minimize the final state Machine. The result is the minimal fsm. Slow
+ * but stable, correct minimization. Uses n^2 space (lookout) and average
+ * n^2 time. Worst case n^3 time, but a that is a very rare case. */
+ void minimizeStable();
+
+ /* Minimize the final state machine. Does not find the minimal fsm, but a
+ * pretty good approximation. Does not use any extra space. Average n^2
+ * time. Worst case n^3 time, but a that is a very rare case. */
+ void minimizeApproximate();
+
+ /* This is the worker for the minimize approximate solution. It merges
+ * states that have identical out transitions. */
+ bool minimizeRound( );
+
+ /* Given an intial partioning of states, split partitions that have out trans
+ * to differing partitions. */
+ int partitionRound( StateAp **statePtrs, MinPartition *parts, int numParts );
+
+ /* Split partitions that have a transition to a previously split partition, until
+ * there are no more partitions to split. */
+ int splitCandidates( StateAp **statePtrs, MinPartition *parts, int numParts );
+
+ /* Fuse together states in the same partition. */
+ void fusePartitions( MinPartition *parts, int numParts );
+
+ /* Mark pairs where out final stateness differs, out trans data differs,
+ * trans pairs go to a marked pair or trans data differs. Should get
+ * alot of pairs. */
+ void initialMarkRound( MarkIndex &markIndex );
+
+ /* One marking round on all state pairs. Considers if trans pairs go
+ * to a marked state only. Returns whether or not a pair was marked. */
+ bool markRound( MarkIndex &markIndex );
+
+ /* Move the in trans into src into dest. */
+ void inTransMove(StateAp *dest, StateAp *src);
+
+ /* Make state src and dest the same state. */
+ void fuseEquivStates(StateAp *dest, StateAp *src);
+
+ /* Find any states that didn't get marked by the marking algorithm and
+ * merge them into the primary states of their equivalence class. */
+ void fuseUnmarkedPairs( MarkIndex &markIndex );
+
+ /* Merge neighboring transitions go to the same state and have the same
+ * transitions data. */
+ void compressTransitions();
+
+ /* Returns true if there is a transtion (either explicit or by a gap) to
+ * the error state. */
+ bool checkErrTrans( StateAp *state, TransAp *trans );
+ bool checkErrTransFinish( StateAp *state );
+ bool hasErrorTrans();
+
+ /* Check if a machine defines a single character. This is useful in
+ * validating ranges and machines to export. */
+ bool checkSingleCharMachine( );
+};
+
+
+#endif /* _FSMGRAPH_H */
diff --git a/contrib/tools/ragel5/ragel/fsmmin.cpp b/contrib/tools/ragel5/ragel/fsmmin.cpp
new file mode 100644
index 0000000000..046d11afa6
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/fsmmin.cpp
@@ -0,0 +1,732 @@
+/*
+ * Copyright 2002 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "fsmgraph.h"
+#include "mergesort.h"
+
+int FsmAp::partitionRound( StateAp **statePtrs, MinPartition *parts, int numParts )
+{
+ /* Need a mergesort object and a single partition compare. */
+ MergeSort<StateAp*, PartitionCompare> mergeSort;
+ PartitionCompare partCompare;
+
+ /* For each partition. */
+ for ( int p = 0; p < numParts; p++ ) {
+ /* Fill the pointer array with the states in the partition. */
+ StateList::Iter state = parts[p].list;
+ for ( int s = 0; state.lte(); state++, s++ )
+ statePtrs[s] = state;
+
+ /* Sort the states using the partitioning compare. */
+ int numStates = parts[p].list.length();
+ mergeSort.sort( statePtrs, numStates );
+
+ /* Assign the states into partitions based on the results of the sort. */
+ int destPart = p, firstNewPart = numParts;
+ for ( int s = 1; s < numStates; s++ ) {
+ /* If this state differs from the last then move to the next partition. */
+ if ( partCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) {
+ /* The new partition is the next avail spot. */
+ destPart = numParts;
+ numParts += 1;
+ }
+
+ /* If the state is not staying in the first partition, then
+ * transfer it to its destination partition. */
+ if ( destPart != p ) {
+ StateAp *state = parts[p].list.detach( statePtrs[s] );
+ parts[destPart].list.append( state );
+ }
+ }
+
+ /* Fix the partition pointer for all the states that got moved to a new
+ * partition. This must be done after the states are transfered so the
+ * result of the sort is not altered. */
+ for ( int newPart = firstNewPart; newPart < numParts; newPart++ ) {
+ StateList::Iter state = parts[newPart].list;
+ for ( ; state.lte(); state++ )
+ state->alg.partition = &parts[newPart];
+ }
+ }
+
+ return numParts;
+}
+
+/**
+ * \brief Minimize by partitioning version 1.
+ *
+ * Repeatedly tries to split partitions until all partitions are unsplittable.
+ * Produces the most minimal FSM possible.
+ */
+void FsmAp::minimizePartition1()
+{
+ /* Need one mergesort object and partition compares. */
+ MergeSort<StateAp*, InitPartitionCompare> mergeSort;
+ InitPartitionCompare initPartCompare;
+
+ /* Nothing to do if there are no states. */
+ if ( stateList.length() == 0 )
+ return;
+
+ /*
+ * First thing is to partition the states by final state status and
+ * transition functions. This gives us an initial partitioning to work
+ * with.
+ */
+
+ /* Make a array of pointers to states. */
+ int numStates = stateList.length();
+ StateAp** statePtrs = new StateAp*[numStates];
+
+ /* Fill up an array of pointers to the states for easy sorting. */
+ StateList::Iter state = stateList;
+ for ( int s = 0; state.lte(); state++, s++ )
+ statePtrs[s] = state;
+
+ /* Sort the states using the array of states. */
+ mergeSort.sort( statePtrs, numStates );
+
+ /* An array of lists of states is used to partition the states. */
+ MinPartition *parts = new MinPartition[numStates];
+
+ /* Assign the states into partitions. */
+ int destPart = 0;
+ for ( int s = 0; s < numStates; s++ ) {
+ /* If this state differs from the last then move to the next partition. */
+ if ( s > 0 && initPartCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) {
+ /* Move to the next partition. */
+ destPart += 1;
+ }
+
+ /* Put the state into its partition. */
+ statePtrs[s]->alg.partition = &parts[destPart];
+ parts[destPart].list.append( statePtrs[s] );
+ }
+
+ /* We just moved all the states from the main list into partitions without
+ * taking them off the main list. So clean up the main list now. */
+ stateList.abandon();
+
+ /* Split partitions. */
+ int numParts = destPart + 1;
+ while ( true ) {
+ /* Test all partitions for splitting. */
+ int newNum = partitionRound( statePtrs, parts, numParts );
+
+ /* When no partitions can be split, stop. */
+ if ( newNum == numParts )
+ break;
+
+ numParts = newNum;
+ }
+
+ /* Fuse states in the same partition. The states will end up back on the
+ * main list. */
+ fusePartitions( parts, numParts );
+
+ /* Cleanup. */
+ delete[] statePtrs;
+ delete[] parts;
+}
+
+/* Split partitions that need splittting, decide which partitions might need
+ * to be split as a result, continue until there are no more that might need
+ * to be split. */
+int FsmAp::splitCandidates( StateAp **statePtrs, MinPartition *parts, int numParts )
+{
+ /* Need a mergesort and a partition compare. */
+ MergeSort<StateAp*, PartitionCompare> mergeSort;
+ PartitionCompare partCompare;
+
+ /* The lists of unsplitable (partList) and splitable partitions.
+ * Only partitions in the splitable list are check for needing splitting. */
+ PartitionList partList, splittable;
+
+ /* Initially, all partitions are born from a split (the initial
+ * partitioning) and can cause other partitions to be split. So any
+ * partition with a state with a transition out to another partition is a
+ * candidate for splitting. This will make every partition except possibly
+ * partitions of final states split candidates. */
+ for ( int p = 0; p < numParts; p++ ) {
+ /* Assume not active. */
+ parts[p].active = false;
+
+ /* Look for a trans out of any state in the partition. */
+ for ( StateList::Iter state = parts[p].list; state.lte(); state++ ) {
+ /* If there is at least one transition out to another state then
+ * the partition becomes splittable. */
+ if ( state->outList.length() > 0 ) {
+ parts[p].active = true;
+ break;
+ }
+ }
+
+ /* If it was found active then it goes on the splittable list. */
+ if ( parts[p].active )
+ splittable.append( &parts[p] );
+ else
+ partList.append( &parts[p] );
+ }
+
+ /* While there are partitions that are splittable, pull one off and try
+ * to split it. If it splits, determine which partitions may now be split
+ * as a result of the newly split partition. */
+ while ( splittable.length() > 0 ) {
+ MinPartition *partition = splittable.detachFirst();
+
+ /* Fill the pointer array with the states in the partition. */
+ StateList::Iter state = partition->list;
+ for ( int s = 0; state.lte(); state++, s++ )
+ statePtrs[s] = state;
+
+ /* Sort the states using the partitioning compare. */
+ int numStates = partition->list.length();
+ mergeSort.sort( statePtrs, numStates );
+
+ /* Assign the states into partitions based on the results of the sort. */
+ MinPartition *destPart = partition;
+ int firstNewPart = numParts;
+ for ( int s = 1; s < numStates; s++ ) {
+ /* If this state differs from the last then move to the next partition. */
+ if ( partCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) {
+ /* The new partition is the next avail spot. */
+ destPart = &parts[numParts];
+ numParts += 1;
+ }
+
+ /* If the state is not staying in the first partition, then
+ * transfer it to its destination partition. */
+ if ( destPart != partition ) {
+ StateAp *state = partition->list.detach( statePtrs[s] );
+ destPart->list.append( state );
+ }
+ }
+
+ /* Fix the partition pointer for all the states that got moved to a new
+ * partition. This must be done after the states are transfered so the
+ * result of the sort is not altered. */
+ int newPart;
+ for ( newPart = firstNewPart; newPart < numParts; newPart++ ) {
+ StateList::Iter state = parts[newPart].list;
+ for ( ; state.lte(); state++ )
+ state->alg.partition = &parts[newPart];
+ }
+
+ /* Put the partition we just split and any new partitions that came out
+ * of the split onto the inactive list. */
+ partition->active = false;
+ partList.append( partition );
+ for ( newPart = firstNewPart; newPart < numParts; newPart++ ) {
+ parts[newPart].active = false;
+ partList.append( &parts[newPart] );
+ }
+
+ if ( destPart == partition )
+ continue;
+
+ /* Now determine which partitions are splittable as a result of
+ * splitting partition by walking the in lists of the states in
+ * partitions that got split. Partition is the faked first item in the
+ * loop. */
+ MinPartition *causalPart = partition;
+ newPart = firstNewPart - 1;
+ while ( newPart < numParts ) {
+ /* Loop all states in the causal partition. */
+ StateList::Iter state = causalPart->list;
+ for ( ; state.lte(); state++ ) {
+ /* Walk all transition into the state and put the partition
+ * that the from state is in onto the splittable list. */
+ for ( TransInList::Iter trans = state->inList; trans.lte(); trans++ ) {
+ MinPartition *fromPart = trans->fromState->alg.partition;
+ if ( ! fromPart->active ) {
+ fromPart->active = true;
+ partList.detach( fromPart );
+ splittable.append( fromPart );
+ }
+ }
+ }
+
+ newPart += 1;
+ causalPart = &parts[newPart];
+ }
+ }
+ return numParts;
+}
+
+
+/**
+ * \brief Minimize by partitioning version 2 (best alg).
+ *
+ * Repeatedly tries to split partitions that may splittable until there are no
+ * more partitions that might possibly need splitting. Runs faster than
+ * version 1. Produces the most minimal fsm possible.
+ */
+void FsmAp::minimizePartition2()
+{
+ /* Need a mergesort and an initial partition compare. */
+ MergeSort<StateAp*, InitPartitionCompare> mergeSort;
+ InitPartitionCompare initPartCompare;
+
+ /* Nothing to do if there are no states. */
+ if ( stateList.length() == 0 )
+ return;
+
+ /*
+ * First thing is to partition the states by final state status and
+ * transition functions. This gives us an initial partitioning to work
+ * with.
+ */
+
+ /* Make a array of pointers to states. */
+ int numStates = stateList.length();
+ StateAp** statePtrs = new StateAp*[numStates];
+
+ /* Fill up an array of pointers to the states for easy sorting. */
+ StateList::Iter state = stateList;
+ for ( int s = 0; state.lte(); state++, s++ )
+ statePtrs[s] = state;
+
+ /* Sort the states using the array of states. */
+ mergeSort.sort( statePtrs, numStates );
+
+ /* An array of lists of states is used to partition the states. */
+ MinPartition *parts = new MinPartition[numStates];
+
+ /* Assign the states into partitions. */
+ int destPart = 0;
+ for ( int s = 0; s < numStates; s++ ) {
+ /* If this state differs from the last then move to the next partition. */
+ if ( s > 0 && initPartCompare.compare( statePtrs[s-1], statePtrs[s] ) < 0 ) {
+ /* Move to the next partition. */
+ destPart += 1;
+ }
+
+ /* Put the state into its partition. */
+ statePtrs[s]->alg.partition = &parts[destPart];
+ parts[destPart].list.append( statePtrs[s] );
+ }
+
+ /* We just moved all the states from the main list into partitions without
+ * taking them off the main list. So clean up the main list now. */
+ stateList.abandon();
+
+ /* Split partitions. */
+ int numParts = splitCandidates( statePtrs, parts, destPart+1 );
+
+ /* Fuse states in the same partition. The states will end up back on the
+ * main list. */
+ fusePartitions( parts, numParts );
+
+ /* Cleanup. */
+ delete[] statePtrs;
+ delete[] parts;
+}
+
+void FsmAp::initialMarkRound( MarkIndex &markIndex )
+{
+ /* P and q for walking pairs. */
+ StateAp *p = stateList.head, *q;
+
+ /* Need an initial partition compare. */
+ InitPartitionCompare initPartCompare;
+
+ /* Walk all unordered pairs of (p, q) where p != q.
+ * The second depth of the walk stops before reaching p. This
+ * gives us all unordered pairs of states (p, q) where p != q. */
+ while ( p != 0 ) {
+ q = stateList.head;
+ while ( q != p ) {
+ /* If the states differ on final state status, out transitions or
+ * any transition data then they should be separated on the initial
+ * round. */
+ if ( initPartCompare.compare( p, q ) != 0 )
+ markIndex.markPair( p->alg.stateNum, q->alg.stateNum );
+
+ q = q->next;
+ }
+ p = p->next;
+ }
+}
+
+bool FsmAp::markRound( MarkIndex &markIndex )
+{
+ /* P an q for walking pairs. Take note if any pair gets marked. */
+ StateAp *p = stateList.head, *q;
+ bool pairWasMarked = false;
+
+ /* Need a mark comparison. */
+ MarkCompare markCompare;
+
+ /* Walk all unordered pairs of (p, q) where p != q.
+ * The second depth of the walk stops before reaching p. This
+ * gives us all unordered pairs of states (p, q) where p != q. */
+ while ( p != 0 ) {
+ q = stateList.head;
+ while ( q != p ) {
+ /* Should we mark the pair? */
+ if ( !markIndex.isPairMarked( p->alg.stateNum, q->alg.stateNum ) ) {
+ if ( markCompare.shouldMark( markIndex, p, q ) ) {
+ markIndex.markPair( p->alg.stateNum, q->alg.stateNum );
+ pairWasMarked = true;
+ }
+ }
+ q = q->next;
+ }
+ p = p->next;
+ }
+
+ return pairWasMarked;
+}
+
+
+/**
+ * \brief Minimize by pair marking.
+ *
+ * Decides if each pair of states is distinct or not. Uses O(n^2) memory and
+ * should only be used on small graphs. Produces the most minmimal FSM
+ * possible.
+ */
+void FsmAp::minimizeStable()
+{
+ /* Set the state numbers. */
+ setStateNumbers( 0 );
+
+ /* This keeps track of which pairs have been marked. */
+ MarkIndex markIndex( stateList.length() );
+
+ /* Mark pairs where final stateness, out trans, or trans data differ. */
+ initialMarkRound( markIndex );
+
+ /* While the last round of marking succeeded in marking a state
+ * continue to do another round. */
+ int modified = markRound( markIndex );
+ while (modified)
+ modified = markRound( markIndex );
+
+ /* Merge pairs that are unmarked. */
+ fuseUnmarkedPairs( markIndex );
+}
+
+bool FsmAp::minimizeRound()
+{
+ /* Nothing to do if there are no states. */
+ if ( stateList.length() == 0 )
+ return false;
+
+ /* Need a mergesort on approx compare and an approx compare. */
+ MergeSort<StateAp*, ApproxCompare> mergeSort;
+ ApproxCompare approxCompare;
+
+ /* Fill up an array of pointers to the states. */
+ StateAp **statePtrs = new StateAp*[stateList.length()];
+ StateList::Iter state = stateList;
+ for ( int s = 0; state.lte(); state++, s++ )
+ statePtrs[s] = state;
+
+ bool modified = false;
+
+ /* Sort The list. */
+ mergeSort.sort( statePtrs, stateList.length() );
+
+ /* Walk the list looking for duplicates next to each other,
+ * merge in any duplicates. */
+ StateAp **pLast = statePtrs;
+ StateAp **pState = statePtrs + 1;
+ for ( int i = 1; i < stateList.length(); i++, pState++ ) {
+ if ( approxCompare.compare( *pLast, *pState ) == 0 ) {
+ /* Last and pState are the same, so fuse together. Move forward
+ * with pState but not with pLast. If any more are identical, we
+ * must */
+ fuseEquivStates( *pLast, *pState );
+ modified = true;
+ }
+ else {
+ /* Last and this are different, do not set to merge them. Move
+ * pLast to the current (it may be way behind from merging many
+ * states) and pState forward one to consider the next pair. */
+ pLast = pState;
+ }
+ }
+ delete[] statePtrs;
+ return modified;
+}
+
+/**
+ * \brief Minmimize by an approximation.
+ *
+ * Repeatedly tries to find states with transitions out to the same set of
+ * states on the same set of keys until no more identical states can be found.
+ * Does not produce the most minimial FSM possible.
+ */
+void FsmAp::minimizeApproximate()
+{
+ /* While the last minimization round succeeded in compacting states,
+ * continue to try to compact states. */
+ while ( true ) {
+ bool modified = minimizeRound();
+ if ( ! modified )
+ break;
+ }
+}
+
+
+/* Remove states that have no path to them from the start state. Recursively
+ * traverses the graph marking states that have paths into them. Then removes
+ * all states that did not get marked. */
+void FsmAp::removeUnreachableStates()
+{
+ /* Misfit accounting should be off and there should be no states on the
+ * misfit list. */
+ assert( !misfitAccounting && misfitList.length() == 0 );
+
+ /* Mark all the states that can be reached
+ * through the existing set of entry points. */
+ markReachableFromHere( startState );
+ for ( EntryMap::Iter en = entryPoints; en.lte(); en++ )
+ markReachableFromHere( en->value );
+
+ /* Delete all states that are not marked
+ * and unmark the ones that are marked. */
+ StateAp *state = stateList.head;
+ while ( state ) {
+ StateAp *next = state->next;
+
+ if ( state->stateBits & SB_ISMARKED )
+ state->stateBits &= ~ SB_ISMARKED;
+ else {
+ detachState( state );
+ stateList.detach( state );
+ delete state;
+ }
+
+ state = next;
+ }
+}
+
+bool FsmAp::outListCovers( StateAp *state )
+{
+ /* Must be at least one range to cover. */
+ if ( state->outList.length() == 0 )
+ return false;
+
+ /* The first must start at the lower bound. */
+ TransList::Iter trans = state->outList.first();
+ if ( keyOps->minKey < trans->lowKey )
+ return false;
+
+ /* Loop starts at second el. */
+ trans.increment();
+
+ /* Loop checks lower against prev upper. */
+ for ( ; trans.lte(); trans++ ) {
+ /* Lower end of the trans must be one greater than the
+ * previous' high end. */
+ Key lowKey = trans->lowKey;
+ lowKey.decrement();
+ if ( trans->prev->highKey < lowKey )
+ return false;
+ }
+
+ /* Require that the last range extends to the upper bound. */
+ trans = state->outList.last();
+ if ( trans->highKey < keyOps->maxKey )
+ return false;
+
+ return true;
+}
+
+/* Remove states that that do not lead to a final states. Works recursivly traversing
+ * the graph in reverse (starting from all final states) and marking seen states. Then
+ * removes states that did not get marked. */
+void FsmAp::removeDeadEndStates()
+{
+ /* Misfit accounting should be off and there should be no states on the
+ * misfit list. */
+ assert( !misfitAccounting && misfitList.length() == 0 );
+
+ /* Mark all states that have paths to the final states. */
+ StateAp **st = finStateSet.data;
+ int nst = finStateSet.length();
+ for ( int i = 0; i < nst; i++, st++ )
+ markReachableFromHereReverse( *st );
+
+ /* Start state gets honorary marking. If the machine accepts nothing we
+ * still want the start state to hang around. This must be done after the
+ * recursive call on all the final states so that it does not cause the
+ * start state in transitions to be skipped when the start state is
+ * visited by the traversal. */
+ startState->stateBits |= SB_ISMARKED;
+
+ /* Delete all states that are not marked
+ * and unmark the ones that are marked. */
+ StateAp *state = stateList.head;
+ while ( state != 0 ) {
+ StateAp *next = state->next;
+
+ if ( state->stateBits & SB_ISMARKED )
+ state->stateBits &= ~ SB_ISMARKED;
+ else {
+ detachState( state );
+ stateList.detach( state );
+ delete state;
+ }
+
+ state = next;
+ }
+}
+
+/* Remove states on the misfit list. To work properly misfit accounting should
+ * be on when this is called. The detaching of a state will likely cause
+ * another misfit to be collected and it can then be removed. */
+void FsmAp::removeMisfits()
+{
+ while ( misfitList.length() > 0 ) {
+ /* Get the first state. */
+ StateAp *state = misfitList.head;
+
+ /* Detach and delete. */
+ detachState( state );
+
+ /* The state was previously on the misfit list and detaching can only
+ * remove in transitions so the state must still be on the misfit
+ * list. */
+ misfitList.detach( state );
+ delete state;
+ }
+}
+
+/* Fuse src into dest because they have been deemed equivalent states.
+ * Involves moving transitions into src to go into dest and invoking
+ * callbacks. Src is deleted detached from the graph and deleted. */
+void FsmAp::fuseEquivStates( StateAp *dest, StateAp *src )
+{
+ /* This would get ugly. */
+ assert( dest != src );
+
+ /* Cur is a duplicate. We can merge it with trail. */
+ inTransMove( dest, src );
+
+ detachState( src );
+ stateList.detach( src );
+ delete src;
+}
+
+void FsmAp::fuseUnmarkedPairs( MarkIndex &markIndex )
+{
+ StateAp *p = stateList.head, *nextP, *q;
+
+ /* Definition: The primary state of an equivalence class is the first state
+ * encounterd that belongs to the equivalence class. All equivalence
+ * classes have primary state including equivalence classes with one state
+ * in it. */
+
+ /* For each unmarked pair merge p into q and delete p. q is always the
+ * primary state of it's equivalence class. We wouldn't have landed on it
+ * here if it were not, because it would have been deleted.
+ *
+ * Proof that q is the primaray state of it's equivalence class: Assume q
+ * is not the primary state of it's equivalence class, then it would be
+ * merged into some state that came before it and thus p would be
+ * equivalent to that state. But q is the first state that p is equivalent
+ * to so we have a contradiction. */
+
+ /* Walk all unordered pairs of (p, q) where p != q.
+ * The second depth of the walk stops before reaching p. This
+ * gives us all unordered pairs of states (p, q) where p != q. */
+ while ( p != 0 ) {
+ nextP = p->next;
+
+ q = stateList.head;
+ while ( q != p ) {
+ /* If one of p or q is a final state then mark. */
+ if ( ! markIndex.isPairMarked( p->alg.stateNum, q->alg.stateNum ) ) {
+ fuseEquivStates( q, p );
+ break;
+ }
+ q = q->next;
+ }
+ p = nextP;
+ }
+}
+
+void FsmAp::fusePartitions( MinPartition *parts, int numParts )
+{
+ /* For each partition, fuse state 2, 3, ... into state 1. */
+ for ( int p = 0; p < numParts; p++ ) {
+ /* Assume that there will always be at least one state. */
+ StateAp *first = parts[p].list.head, *toFuse = first->next;
+
+ /* Put the first state back onto the main state list. Don't bother
+ * removing it from the partition list first. */
+ stateList.append( first );
+
+ /* Fuse the rest of the state into the first. */
+ while ( toFuse != 0 ) {
+ /* Save the next. We will trash it before it is needed. */
+ StateAp *next = toFuse->next;
+
+ /* Put the state to be fused in to the first back onto the main
+ * list before it is fuse. the graph. The state needs to be on
+ * the main list for the detach from the graph to work. Don't
+ * bother removing the state from the partition list first. We
+ * need not maintain it. */
+ stateList.append( toFuse );
+
+ /* Now fuse to the first. */
+ fuseEquivStates( first, toFuse );
+
+ /* Go to the next that we saved before trashing the next pointer. */
+ toFuse = next;
+ }
+
+ /* We transfered the states from the partition list into the main list without
+ * removing the states from the partition list first. Clean it up. */
+ parts[p].list.abandon();
+ }
+}
+
+
+/* Merge neighboring transitions go to the same state and have the same
+ * transitions data. */
+void FsmAp::compressTransitions()
+{
+ for ( StateList::Iter st = stateList; st.lte(); st++ ) {
+ if ( st->outList.length() > 1 ) {
+ for ( TransList::Iter trans = st->outList, next = trans.next(); next.lte(); ) {
+ Key nextLow = next->lowKey;
+ nextLow.decrement();
+ if ( trans->highKey == nextLow && trans->toState == next->toState &&
+ CmpActionTable::compare( trans->actionTable, next->actionTable ) == 0 )
+ {
+ trans->highKey = next->highKey;
+ st->outList.detach( next );
+ detachTrans( next->fromState, next->toState, next );
+ delete next;
+ next = trans.next();
+ }
+ else {
+ trans.increment();
+ next.increment();
+ }
+ }
+ }
+ }
+}
diff --git a/contrib/tools/ragel5/ragel/fsmstate.cpp b/contrib/tools/ragel5/ragel/fsmstate.cpp
new file mode 100644
index 0000000000..4322c1060f
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/fsmstate.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2002 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+#include <assert.h>
+#include "fsmgraph.h"
+
+#include <iostream>
+using namespace std;
+
+/* Construct a mark index for a specified number of states. Must new up
+ * an array that is states^2 in size. */
+MarkIndex::MarkIndex( int states ) : numStates(states)
+{
+ /* Total pairs is states^2. Actually only use half of these, but we allocate
+ * them all to make indexing into the array easier. */
+ int total = states * states;
+
+ /* New up chars so that individual DListEl constructors are
+ * not called. Zero out the mem manually. */
+ array = new bool[total];
+ memset( array, 0, sizeof(bool) * total );
+}
+
+/* Free the array used to store state pairs. */
+MarkIndex::~MarkIndex()
+{
+ delete[] array;
+}
+
+/* Mark a pair of states. States are specified by their number. The
+ * marked states are moved from the unmarked list to the marked list. */
+void MarkIndex::markPair(int state1, int state2)
+{
+ int pos = ( state1 >= state2 ) ?
+ ( state1 * numStates ) + state2 :
+ ( state2 * numStates ) + state1;
+
+ array[pos] = true;
+}
+
+/* Returns true if the pair of states are marked. Returns false otherwise.
+ * Ordering of states given does not matter. */
+bool MarkIndex::isPairMarked(int state1, int state2)
+{
+ int pos = ( state1 >= state2 ) ?
+ ( state1 * numStates ) + state2 :
+ ( state2 * numStates ) + state1;
+
+ return array[pos];
+}
+
+/* Create a new fsm state. State has not out transitions or in transitions, not
+ * out out transition data and not number. */
+StateAp::StateAp()
+:
+ /* No out or in transitions. */
+ outList(),
+ inList(),
+
+ /* No entry points, or epsilon trans. */
+ entryIds(),
+ epsilonTrans(),
+
+ /* Conditions. */
+ stateCondList(),
+
+ /* No transitions in from other states. */
+ foreignInTrans(0),
+
+ /* Only used during merging. Normally null. */
+ stateDictEl(0),
+ eptVect(0),
+
+ /* No state identification bits. */
+ stateBits(0),
+
+ /* No Priority data. */
+ outPriorTable(),
+
+ /* No Action data. */
+ toStateActionTable(),
+ fromStateActionTable(),
+ outActionTable(),
+ outCondSet(),
+ errActionTable(),
+ eofActionTable()
+{
+}
+
+/* Copy everything except actual the transitions. That is left up to the
+ * FsmAp copy constructor. */
+StateAp::StateAp(const StateAp &other)
+:
+ /* All lists are cleared. They will be filled in when the
+ * individual transitions are duplicated and attached. */
+ outList(),
+ inList(),
+
+ /* Duplicate the entry id set and epsilon transitions. These
+ * are sets of integers and as such need no fixing. */
+ entryIds(other.entryIds),
+ epsilonTrans(other.epsilonTrans),
+
+ /* Copy in the elements of the conditions. */
+ stateCondList( other.stateCondList ),
+
+ /* No transitions in from other states. */
+ foreignInTrans(0),
+
+ /* This is only used during merging. Normally null. */
+ stateDictEl(0),
+ eptVect(0),
+
+ /* Fsm state data. */
+ stateBits(other.stateBits),
+
+ /* Copy in priority data. */
+ outPriorTable(other.outPriorTable),
+
+ /* Copy in action data. */
+ toStateActionTable(other.toStateActionTable),
+ fromStateActionTable(other.fromStateActionTable),
+ outActionTable(other.outActionTable),
+ outCondSet(other.outCondSet),
+ errActionTable(other.errActionTable),
+ eofActionTable(other.eofActionTable)
+{
+ /* Duplicate all the transitions. */
+ for ( TransList::Iter trans = other.outList; trans.lte(); trans++ ) {
+ /* Dupicate and store the orginal target in the transition. This will
+ * be corrected once all the states have been created. */
+ TransAp *newTrans = new TransAp(*trans);
+ newTrans->toState = trans->toState;
+ outList.append( newTrans );
+ }
+}
+
+/* If there is a state dict element, then delete it. Everything else is left
+ * up to the FsmGraph destructor. */
+StateAp::~StateAp()
+{
+ if ( stateDictEl != 0 )
+ delete stateDictEl;
+}
+
+/* Compare two states using pointers to the states. With the approximate
+ * compare the idea is that if the compare finds them the same, they can
+ * immediately be merged. */
+int ApproxCompare::compare( const StateAp *state1 , const StateAp *state2 )
+{
+ int compareRes;
+
+ /* Test final state status. */
+ if ( (state1->stateBits & SB_ISFINAL) && !(state2->stateBits & SB_ISFINAL) )
+ return -1;
+ else if ( !(state1->stateBits & SB_ISFINAL) && (state2->stateBits & SB_ISFINAL) )
+ return 1;
+
+ /* Test epsilon transition sets. */
+ compareRes = CmpEpsilonTrans::compare( state1->epsilonTrans,
+ state2->epsilonTrans );
+ if ( compareRes != 0 )
+ return compareRes;
+
+ /* Compare the out transitions. */
+ compareRes = FsmAp::compareStateData( state1, state2 );
+ if ( compareRes != 0 )
+ return compareRes;
+
+ /* Use a pair iterator to get the transition pairs. */
+ PairIter<TransAp> outPair( state1->outList.head, state2->outList.head );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+
+ case RangeInS1:
+ compareRes = FsmAp::compareFullPtr( outPair.s1Tel.trans, 0 );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangeInS2:
+ compareRes = FsmAp::compareFullPtr( 0, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangeOverlap:
+ compareRes = FsmAp::compareFullPtr(
+ outPair.s1Tel.trans, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case BreakS1:
+ case BreakS2:
+ break;
+ }
+ }
+
+ /* Got through the entire state comparison, deem them equal. */
+ return 0;
+}
+
+/* Compare class for the sort that does the intial partition of compaction. */
+int InitPartitionCompare::compare( const StateAp *state1 , const StateAp *state2 )
+{
+ int compareRes;
+
+ /* Test final state status. */
+ if ( (state1->stateBits & SB_ISFINAL) && !(state2->stateBits & SB_ISFINAL) )
+ return -1;
+ else if ( !(state1->stateBits & SB_ISFINAL) && (state2->stateBits & SB_ISFINAL) )
+ return 1;
+
+ /* Test epsilon transition sets. */
+ compareRes = CmpEpsilonTrans::compare( state1->epsilonTrans,
+ state2->epsilonTrans );
+ if ( compareRes != 0 )
+ return compareRes;
+
+ /* Compare the out transitions. */
+ compareRes = FsmAp::compareStateData( state1, state2 );
+ if ( compareRes != 0 )
+ return compareRes;
+
+ /* Use a pair iterator to test the condition pairs. */
+ PairIter<StateCond> condPair( state1->stateCondList.head, state2->stateCondList.head );
+ for ( ; !condPair.end(); condPair++ ) {
+ switch ( condPair.userState ) {
+ case RangeInS1:
+ return 1;
+ case RangeInS2:
+ return -1;
+
+ case RangeOverlap: {
+ CondSpace *condSpace1 = condPair.s1Tel.trans->condSpace;
+ CondSpace *condSpace2 = condPair.s2Tel.trans->condSpace;
+ if ( condSpace1 < condSpace2 )
+ return -1;
+ else if ( condSpace1 > condSpace2 )
+ return 1;
+ break;
+ }
+ case BreakS1:
+ case BreakS2:
+ break;
+ }
+ }
+
+ /* Use a pair iterator to test the transition pairs. */
+ PairIter<TransAp> outPair( state1->outList.head, state2->outList.head );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+
+ case RangeInS1:
+ compareRes = FsmAp::compareDataPtr( outPair.s1Tel.trans, 0 );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangeInS2:
+ compareRes = FsmAp::compareDataPtr( 0, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangeOverlap:
+ compareRes = FsmAp::compareDataPtr(
+ outPair.s1Tel.trans, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case BreakS1:
+ case BreakS2:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* Compare class for the sort that does the partitioning. */
+int PartitionCompare::compare( const StateAp *state1, const StateAp *state2 )
+{
+ int compareRes;
+
+ /* Use a pair iterator to get the transition pairs. */
+ PairIter<TransAp> outPair( state1->outList.head, state2->outList.head );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+
+ case RangeInS1:
+ compareRes = FsmAp::comparePartPtr( outPair.s1Tel.trans, 0 );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangeInS2:
+ compareRes = FsmAp::comparePartPtr( 0, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case RangeOverlap:
+ compareRes = FsmAp::comparePartPtr(
+ outPair.s1Tel.trans, outPair.s2Tel.trans );
+ if ( compareRes != 0 )
+ return compareRes;
+ break;
+
+ case BreakS1:
+ case BreakS2:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* Compare class for the sort that does the partitioning. */
+bool MarkCompare::shouldMark( MarkIndex &markIndex, const StateAp *state1,
+ const StateAp *state2 )
+{
+ /* Use a pair iterator to get the transition pairs. */
+ PairIter<TransAp> outPair( state1->outList.head, state2->outList.head );
+ for ( ; !outPair.end(); outPair++ ) {
+ switch ( outPair.userState ) {
+
+ case RangeInS1:
+ if ( FsmAp::shouldMarkPtr( markIndex, outPair.s1Tel.trans, 0 ) )
+ return true;
+ break;
+
+ case RangeInS2:
+ if ( FsmAp::shouldMarkPtr( markIndex, 0, outPair.s2Tel.trans ) )
+ return true;
+ break;
+
+ case RangeOverlap:
+ if ( FsmAp::shouldMarkPtr( markIndex,
+ outPair.s1Tel.trans, outPair.s2Tel.trans ) )
+ return true;
+ break;
+
+ case BreakS1:
+ case BreakS2:
+ break;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Transition Comparison.
+ */
+
+/* Compare target partitions. Either pointer may be null. */
+int FsmAp::comparePartPtr( TransAp *trans1, TransAp *trans2 )
+{
+ if ( trans1 != 0 ) {
+ /* If trans1 is set then so should trans2. The initial partitioning
+ * guarantees this for us. */
+ if ( trans1->toState == 0 && trans2->toState != 0 )
+ return -1;
+ else if ( trans1->toState != 0 && trans2->toState == 0 )
+ return 1;
+ else if ( trans1->toState != 0 ) {
+ /* Both of targets are set. */
+ return CmpOrd< MinPartition* >::compare(
+ trans1->toState->alg.partition, trans2->toState->alg.partition );
+ }
+ }
+ return 0;
+}
+
+
+/* Compares two transition pointers according to priority and functions.
+ * Either pointer may be null. Does not consider to state or from state. */
+int FsmAp::compareDataPtr( TransAp *trans1, TransAp *trans2 )
+{
+ if ( trans1 == 0 && trans2 != 0 )
+ return -1;
+ else if ( trans1 != 0 && trans2 == 0 )
+ return 1;
+ else if ( trans1 != 0 ) {
+ /* Both of the transition pointers are set. */
+ int compareRes = compareTransData( trans1, trans2 );
+ if ( compareRes != 0 )
+ return compareRes;
+ }
+ return 0;
+}
+
+/* Compares two transitions according to target state, priority and functions.
+ * Does not consider from state. Either of the pointers may be null. */
+int FsmAp::compareFullPtr( TransAp *trans1, TransAp *trans2 )
+{
+ if ( (trans1 != 0) ^ (trans2 != 0) ) {
+ /* Exactly one of the transitions is set. */
+ if ( trans1 != 0 )
+ return -1;
+ else
+ return 1;
+ }
+ else if ( trans1 != 0 ) {
+ /* Both of the transition pointers are set. Test target state,
+ * priority and funcs. */
+ if ( trans1->toState < trans2->toState )
+ return -1;
+ else if ( trans1->toState > trans2->toState )
+ return 1;
+ else if ( trans1->toState != 0 ) {
+ /* Test transition data. */
+ int compareRes = compareTransData( trans1, trans2 );
+ if ( compareRes != 0 )
+ return compareRes;
+ }
+ }
+ return 0;
+}
+
+
+bool FsmAp::shouldMarkPtr( MarkIndex &markIndex, TransAp *trans1,
+ TransAp *trans2 )
+{
+ if ( (trans1 != 0) ^ (trans2 != 0) ) {
+ /* Exactly one of the transitions is set. The initial mark round
+ * should rule out this case. */
+ assert( false );
+ }
+ else if ( trans1 != 0 ) {
+ /* Both of the transitions are set. If the target pair is marked, then
+ * the pair we are considering gets marked. */
+ return markIndex.isPairMarked( trans1->toState->alg.stateNum,
+ trans2->toState->alg.stateNum );
+ }
+
+ /* Neither of the transitiosn are set. */
+ return false;
+}
+
+
diff --git a/contrib/tools/ragel5/ragel/main.cpp b/contrib/tools/ragel5/ragel/main.cpp
new file mode 100644
index 0000000000..a22a34f1b0
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/main.cpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+#include <sstream>
+
+/* Parsing. */
+#include "ragel.h"
+#include "rlscan.h"
+
+/* Parameters and output. */
+#include "pcheck.h"
+#include "vector.h"
+#include "version.h"
+#include "common.h"
+
+#ifdef _MSC_VER
+# define strncasecmp _strnicmp
+# define strcasecmp _stricmp
+#endif
+
+using std::istream;
+using std::ostream;
+using std::ifstream;
+using std::ofstream;
+using std::cin;
+using std::cout;
+using std::cerr;
+using std::endl;
+
+/* Controls minimization. */
+MinimizeLevel minimizeLevel = MinimizePartition2;
+MinimizeOpt minimizeOpt = MinimizeMostOps;
+
+/* Graphviz dot file generation. */
+char *machineSpec = 0, *machineName = 0;
+bool machineSpecFound = false;
+
+bool printStatistics = false;
+
+/* Print a summary of the options. */
+void usage()
+{
+ cout <<
+"usage: ragel [options] file\n"
+"general:\n"
+" -h, -H, -?, --help Print this usage and exit\n"
+" -v, --version Print version information and exit\n"
+" -o <file> Write output to <file>\n"
+" -s Print some statistics on stderr\n"
+"fsm minimization:\n"
+" -n Do not perform minimization\n"
+" -m Minimize at the end of the compilation\n"
+" -l Minimize after most operations (default)\n"
+" -e Minimize after every operation\n"
+"machine selection:\n"
+" -S <spec> FSM specification to output for -V\n"
+" -M <machine> Machine definition/instantiation to output for -V\n"
+"host language:\n"
+" -C The host language is C, C++, Obj-C or Obj-C++ (default)\n"
+" -D The host language is D\n"
+" -J The host language is Java\n"
+" -R The host language is Ruby\n"
+ ;
+}
+
+/* Print version information. */
+void version()
+{
+ cout << "Ragel State Machine Compiler version " VERSION << " " PUBDATE << endl <<
+ "Copyright (c) 2001-2006 by Adrian Thurston" << endl;
+}
+
+/* Total error count. */
+int gblErrorCount = 0;
+
+/* Print the opening to a warning in the input, then return the error ostream. */
+ostream &warning( const InputLoc &loc )
+{
+ assert( loc.fileName != 0 );
+ cerr << loc.fileName << ":" << loc.line << ":" <<
+ loc.col << ": warning: ";
+ return cerr;
+}
+
+/* Print the opening to a program error, then return the error stream. */
+ostream &error()
+{
+ gblErrorCount += 1;
+ cerr << PROGNAME ": ";
+ return cerr;
+}
+
+ostream &error( const InputLoc &loc )
+{
+ gblErrorCount += 1;
+ assert( loc.fileName != 0 );
+ cerr << loc.fileName << ":" << loc.line << ": ";
+ return cerr;
+}
+
+void escapeLineDirectivePath( std::ostream &out, char *path )
+{
+ for ( char *pc = path; *pc != 0; pc++ ) {
+ if ( *pc == '\\' )
+ out << "\\\\";
+ else
+ out << *pc;
+ }
+}
+
+/* Main, process args and call yyparse to start scanning input. */
+int main(int argc, char **argv)
+{
+ ParamCheck pc("o:nmleabjkS:M:CDJRvHh?-:s", argc, argv);
+ char *inputFileName = 0;
+ char inputFileNameArr[] = "<stdin>";
+ char *outputFileName = 0;
+
+ while ( pc.check() ) {
+ switch ( pc.state ) {
+ case ParamCheck::match:
+ switch ( pc.parameter ) {
+ /* Output. */
+ case 'o':
+ if ( *pc.parameterArg == 0 )
+ error() << "a zero length output file name was given" << endl;
+ else if ( outputFileName != 0 )
+ error() << "more than one output file name was given" << endl;
+ else {
+ /* Ok, remember the output file name. */
+ outputFileName = pc.parameterArg;
+ }
+ break;
+
+ /* Minimization, mostly hidden options. */
+ case 'n':
+ minimizeOpt = MinimizeNone;
+ break;
+ case 'm':
+ minimizeOpt = MinimizeEnd;
+ break;
+ case 'l':
+ minimizeOpt = MinimizeMostOps;
+ break;
+ case 'e':
+ minimizeOpt = MinimizeEveryOp;
+ break;
+ case 'a':
+ minimizeLevel = MinimizeApprox;
+ break;
+ case 'b':
+ minimizeLevel = MinimizeStable;
+ break;
+ case 'j':
+ minimizeLevel = MinimizePartition1;
+ break;
+ case 'k':
+ minimizeLevel = MinimizePartition2;
+ break;
+
+ /* Machine spec. */
+ case 'S':
+ if ( *pc.parameterArg == 0 )
+ error() << "please specify an argument to -S" << endl;
+ else if ( machineSpec != 0 )
+ error() << "more than one -S argument was given" << endl;
+ else {
+ /* Ok, remember the path to the machine to generate. */
+ machineSpec = pc.parameterArg;
+ }
+ break;
+
+ /* Machine path. */
+ case 'M':
+ if ( *pc.parameterArg == 0 )
+ error() << "please specify an argument to -M" << endl;
+ else if ( machineName != 0 )
+ error() << "more than one -M argument was given" << endl;
+ else {
+ /* Ok, remember the machine name to generate. */
+ machineName = pc.parameterArg;
+ }
+ break;
+
+ /* Host language types. */
+ case 'C':
+ hostLangType = CCode;
+ hostLang = &hostLangC;
+ break;
+ case 'D':
+ hostLangType = DCode;
+ hostLang = &hostLangD;
+ break;
+ case 'J':
+ hostLangType = JavaCode;
+ hostLang = &hostLangJava;
+ break;
+ case 'R':
+ hostLangType = RubyCode;
+ hostLang = &hostLangRuby;
+ break;
+
+ /* Version and help. */
+ case 'v':
+ version();
+ exit(0);
+ case 'H': case 'h': case '?':
+ usage();
+ exit(0);
+ case 's':
+ printStatistics = true;
+ break;
+ case '-':
+ if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
+ usage();
+ exit(0);
+ }
+ else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
+ version();
+ exit(0);
+ }
+ else {
+ error() << "--" << pc.parameterArg <<
+ " is an invalid argument" << endl;
+ }
+ }
+ break;
+
+ case ParamCheck::invalid:
+ error() << "-" << pc.parameter << " is an invalid argument" << endl;
+ break;
+
+ case ParamCheck::noparam:
+ /* It is interpreted as an input file. */
+ if ( *pc.curArg == 0 )
+ error() << "a zero length input file name was given" << endl;
+ else if ( inputFileName != 0 )
+ error() << "more than one input file name was given" << endl;
+ else {
+ /* OK, Remember the filename. */
+ inputFileName = pc.curArg;
+ }
+ break;
+ }
+ }
+
+ /* Bail on above errors. */
+ if ( gblErrorCount > 0 )
+ exit(1);
+
+ /* Make sure we are not writing to the same file as the input file. */
+ if ( inputFileName != 0 && outputFileName != 0 &&
+ strcmp( inputFileName, outputFileName ) == 0 )
+ {
+ error() << "output file \"" << outputFileName <<
+ "\" is the same as the input file" << endl;
+ }
+
+ /* Open the input file for reading. */
+ istream *inStream;
+ if ( inputFileName != 0 ) {
+ /* Open the input file for reading. */
+ ifstream *inFile = new ifstream( inputFileName );
+ inStream = inFile;
+ if ( ! inFile->is_open() )
+ error() << "could not open " << inputFileName << " for reading" << endl;
+ }
+ else {
+ inStream = &cin;
+ }
+
+
+ /* Bail on above errors. */
+ if ( gblErrorCount > 0 )
+ exit(1);
+
+ std::ostringstream outputBuffer;
+
+ if ( machineSpec == 0 && machineName == 0 )
+ outputBuffer << "<host line=\"1\" col=\"1\">";
+
+#if defined _WIN32 || defined _WIN64
+ if (inputFileName != 0) {
+ NormalizeWinPath(inputFileName);
+ }
+#endif
+ if (inputFileName == 0) {
+ inputFileName = inputFileNameArr;
+ }
+
+ if (strrchr(inputFileName, '/') == NULL) {
+ error() << "input file path should be absolute: " << inputFileName << endl;
+ exit(1);
+ }
+
+ Scanner scanner( inputFileName, *inStream, outputBuffer, 0, 0, 0, false );
+ scanner.do_scan();
+
+ /* Finished, final check for errors.. */
+ if ( gblErrorCount > 0 )
+ return 1;
+
+ /* Now send EOF to all parsers. */
+ terminateAllParsers();
+
+ /* Finished, final check for errors.. */
+ if ( gblErrorCount > 0 )
+ return 1;
+
+ if ( machineSpec == 0 && machineName == 0 )
+ outputBuffer << "</host>\n";
+
+ if ( gblErrorCount > 0 )
+ return 1;
+
+ ostream *outputFile = 0;
+ if ( outputFileName != 0 )
+ outputFile = new ofstream( outputFileName );
+ else
+ outputFile = &cout;
+
+ /* Write the machines, then the surrounding code. */
+ writeMachines( *outputFile, outputBuffer.str(), inputFileName );
+
+ if ( outputFileName != 0 )
+ delete outputFile;
+
+ return 0;
+}
diff --git a/contrib/tools/ragel5/ragel/parsedata.cpp b/contrib/tools/ragel5/ragel/parsedata.cpp
new file mode 100644
index 0000000000..3e14cc618a
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/parsedata.cpp
@@ -0,0 +1,1505 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "ragel.h"
+#include "rlparse.h"
+#include "parsedata.h"
+#include "parsetree.h"
+#include "mergesort.h"
+#include "xmlcodegen.h"
+
+using namespace std;
+
+char mainMachine[] = "main";
+
+void Token::set(const char *str, int len )
+{
+ length = len;
+ data = new char[len+1];
+ memcpy( data, str, len );
+ data[len] = 0;
+}
+
+void Token::append( const Token &other )
+{
+ int newLength = length + other.length;
+ char *newString = new char[newLength+1];
+ memcpy( newString, data, length );
+ memcpy( newString + length, other.data, other.length );
+ newString[newLength] = 0;
+ data = newString;
+ length = newLength;
+}
+
+/* Perform minimization after an operation according
+ * to the command line args. */
+void afterOpMinimize( FsmAp *fsm, bool lastInSeq )
+{
+ /* Switch on the prefered minimization algorithm. */
+ if ( minimizeOpt == MinimizeEveryOp || minimizeOpt == MinimizeMostOps && lastInSeq ) {
+ /* First clean up the graph. FsmAp operations may leave these
+ * lying around. There should be no dead end states. The subtract
+ * intersection operators are the only places where they may be
+ * created and those operators clean them up. */
+ fsm->removeUnreachableStates();
+
+ switch ( minimizeLevel ) {
+ case MinimizeApprox:
+ fsm->minimizeApproximate();
+ break;
+ case MinimizePartition1:
+ fsm->minimizePartition1();
+ break;
+ case MinimizePartition2:
+ fsm->minimizePartition2();
+ break;
+ case MinimizeStable:
+ fsm->minimizeStable();
+ break;
+ }
+ }
+}
+
+/* Count the transitions in the fsm by walking the state list. */
+int countTransitions( FsmAp *fsm )
+{
+ int numTrans = 0;
+ StateAp *state = fsm->stateList.head;
+ while ( state != 0 ) {
+ numTrans += state->outList.length();
+ state = state->next;
+ }
+ return numTrans;
+}
+
+Key makeFsmKeyHex( char *str, const InputLoc &loc, ParseData *pd )
+{
+ /* Reset errno so we can check for overflow or underflow. In the event of
+ * an error, sets the return val to the upper or lower bound being tested
+ * against. */
+ errno = 0;
+ unsigned int size = keyOps->alphType->size;
+ bool unusedBits = size < sizeof(unsigned long);
+
+ unsigned long ul = strtoul( str, 0, 16 );
+
+ if ( errno == ERANGE || unusedBits && ul >> (size * 8) ) {
+ error(loc) << "literal " << str << " overflows the alphabet type" << endl;
+ ul = 1 << (size * 8);
+ }
+
+ if ( unusedBits && keyOps->alphType->isSigned && ul >> (size * 8 - 1) )
+ ul |= (0xffffffff >> (size*8 ) ) << (size*8);
+
+ return Key( (long)ul );
+}
+
+#ifdef _MSC_VER
+# define strtoll _strtoi64
+#endif
+
+Key makeFsmKeyDec( char *str, const InputLoc &loc, ParseData *pd )
+{
+ /* Convert the number to a decimal. First reset errno so we can check
+ * for overflow or underflow. */
+ errno = 0;
+ long long minVal = keyOps->alphType->minVal;
+ long long maxVal = keyOps->alphType->maxVal;
+
+ long long ll = strtoll( str, 0, 10 );
+
+ /* Check for underflow. */
+ if ( errno == ERANGE && ll < 0 || ll < minVal) {
+ error(loc) << "literal " << str << " underflows the alphabet type" << endl;
+ ll = minVal;
+ }
+ /* Check for overflow. */
+ else if ( errno == ERANGE && ll > 0 || ll > maxVal ) {
+ error(loc) << "literal " << str << " overflows the alphabet type" << endl;
+ ll = maxVal;
+ }
+
+ if ( keyOps->alphType->isSigned )
+ return Key( (long)ll );
+ else
+ return Key( (unsigned long)ll );
+}
+
+/* Make an fsm key in int format (what the fsm graph uses) from an alphabet
+ * number returned by the parser. Validates that the number doesn't overflow
+ * the alphabet type. */
+Key makeFsmKeyNum( char *str, const InputLoc &loc, ParseData *pd )
+{
+ /* Switch on hex/decimal format. */
+ if ( str[0] == '0' && str[1] == 'x' )
+ return makeFsmKeyHex( str, loc, pd );
+ else
+ return makeFsmKeyDec( str, loc, pd );
+}
+
+/* Make an fsm int format (what the fsm graph uses) from a single character.
+ * Performs proper conversion depending on signed/unsigned property of the
+ * alphabet. */
+Key makeFsmKeyChar( char c, ParseData *pd )
+{
+ if ( keyOps->isSigned ) {
+ /* Copy from a char type. */
+ return Key( c );
+ }
+ else {
+ /* Copy from an unsigned byte type. */
+ return Key( (unsigned char)c );
+ }
+}
+
+/* Make an fsm key array in int format (what the fsm graph uses) from a string
+ * of characters. Performs proper conversion depending on signed/unsigned
+ * property of the alphabet. */
+void makeFsmKeyArray( Key *result, char *data, int len, ParseData *pd )
+{
+ if ( keyOps->isSigned ) {
+ /* Copy from a char star type. */
+ char *src = data;
+ for ( int i = 0; i < len; i++ )
+ result[i] = Key(src[i]);
+ }
+ else {
+ /* Copy from an unsigned byte ptr type. */
+ unsigned char *src = (unsigned char*) data;
+ for ( int i = 0; i < len; i++ )
+ result[i] = Key(src[i]);
+ }
+}
+
+/* Like makeFsmKeyArray except the result has only unique keys. They ordering
+ * will be changed. */
+void makeFsmUniqueKeyArray( KeySet &result, char *data, int len,
+ bool caseInsensitive, ParseData *pd )
+{
+ /* Use a transitions list for getting unique keys. */
+ if ( keyOps->isSigned ) {
+ /* Copy from a char star type. */
+ char *src = data;
+ for ( int si = 0; si < len; si++ ) {
+ Key key( src[si] );
+ result.insert( key );
+ if ( caseInsensitive ) {
+ if ( key.isLower() )
+ result.insert( key.toUpper() );
+ else if ( key.isUpper() )
+ result.insert( key.toLower() );
+ }
+ }
+ }
+ else {
+ /* Copy from an unsigned byte ptr type. */
+ unsigned char *src = (unsigned char*) data;
+ for ( int si = 0; si < len; si++ ) {
+ Key key( src[si] );
+ result.insert( key );
+ if ( caseInsensitive ) {
+ if ( key.isLower() )
+ result.insert( key.toUpper() );
+ else if ( key.isUpper() )
+ result.insert( key.toLower() );
+ }
+ }
+ }
+}
+
+FsmAp *dotFsm( ParseData *pd )
+{
+ FsmAp *retFsm = new FsmAp();
+ retFsm->rangeFsm( keyOps->minKey, keyOps->maxKey );
+ return retFsm;
+}
+
+FsmAp *dotStarFsm( ParseData *pd )
+{
+ FsmAp *retFsm = new FsmAp();
+ retFsm->rangeStarFsm( keyOps->minKey, keyOps->maxKey );
+ return retFsm;
+}
+
+/* Make a builtin type. Depends on the signed nature of the alphabet type. */
+FsmAp *makeBuiltin( BuiltinMachine builtin, ParseData *pd )
+{
+ /* FsmAp created to return. */
+ FsmAp *retFsm = 0;
+ bool isSigned = keyOps->isSigned;
+
+ switch ( builtin ) {
+ case BT_Any: {
+ /* All characters. */
+ retFsm = dotFsm( pd );
+ break;
+ }
+ case BT_Ascii: {
+ /* Ascii characters 0 to 127. */
+ retFsm = new FsmAp();
+ retFsm->rangeFsm( 0, 127 );
+ break;
+ }
+ case BT_Extend: {
+ /* Ascii extended characters. This is the full byte range. Dependent
+ * on signed, vs no signed. If the alphabet is one byte then just use
+ * dot fsm. */
+ if ( isSigned ) {
+ retFsm = new FsmAp();
+ retFsm->rangeFsm( -128, 127 );
+ }
+ else {
+ retFsm = new FsmAp();
+ retFsm->rangeFsm( 0, 255 );
+ }
+ break;
+ }
+ case BT_Alpha: {
+ /* Alpha [A-Za-z]. */
+ FsmAp *upper = new FsmAp(), *lower = new FsmAp();
+ upper->rangeFsm( 'A', 'Z' );
+ lower->rangeFsm( 'a', 'z' );
+ upper->unionOp( lower );
+ upper->minimizePartition2();
+ retFsm = upper;
+ break;
+ }
+ case BT_Digit: {
+ /* Digits [0-9]. */
+ retFsm = new FsmAp();
+ retFsm->rangeFsm( '0', '9' );
+ break;
+ }
+ case BT_Alnum: {
+ /* Alpha numerics [0-9A-Za-z]. */
+ FsmAp *digit = new FsmAp(), *lower = new FsmAp();
+ FsmAp *upper = new FsmAp();
+ digit->rangeFsm( '0', '9' );
+ upper->rangeFsm( 'A', 'Z' );
+ lower->rangeFsm( 'a', 'z' );
+ digit->unionOp( upper );
+ digit->unionOp( lower );
+ digit->minimizePartition2();
+ retFsm = digit;
+ break;
+ }
+ case BT_Lower: {
+ /* Lower case characters. */
+ retFsm = new FsmAp();
+ retFsm->rangeFsm( 'a', 'z' );
+ break;
+ }
+ case BT_Upper: {
+ /* Upper case characters. */
+ retFsm = new FsmAp();
+ retFsm->rangeFsm( 'A', 'Z' );
+ break;
+ }
+ case BT_Cntrl: {
+ /* Control characters. */
+ FsmAp *cntrl = new FsmAp();
+ FsmAp *highChar = new FsmAp();
+ cntrl->rangeFsm( 0, 31 );
+ highChar->concatFsm( 127 );
+ cntrl->unionOp( highChar );
+ cntrl->minimizePartition2();
+ retFsm = cntrl;
+ break;
+ }
+ case BT_Graph: {
+ /* Graphical ascii characters [!-~]. */
+ retFsm = new FsmAp();
+ retFsm->rangeFsm( '!', '~' );
+ break;
+ }
+ case BT_Print: {
+ /* Printable characters. Same as graph except includes space. */
+ retFsm = new FsmAp();
+ retFsm->rangeFsm( ' ', '~' );
+ break;
+ }
+ case BT_Punct: {
+ /* Punctuation. */
+ FsmAp *range1 = new FsmAp();
+ FsmAp *range2 = new FsmAp();
+ FsmAp *range3 = new FsmAp();
+ FsmAp *range4 = new FsmAp();
+ range1->rangeFsm( '!', '/' );
+ range2->rangeFsm( ':', '@' );
+ range3->rangeFsm( '[', '`' );
+ range4->rangeFsm( '{', '~' );
+ range1->unionOp( range2 );
+ range1->unionOp( range3 );
+ range1->unionOp( range4 );
+ range1->minimizePartition2();
+ retFsm = range1;
+ break;
+ }
+ case BT_Space: {
+ /* Whitespace: [\t\v\f\n\r ]. */
+ FsmAp *cntrl = new FsmAp();
+ FsmAp *space = new FsmAp();
+ cntrl->rangeFsm( '\t', '\r' );
+ space->concatFsm( ' ' );
+ cntrl->unionOp( space );
+ cntrl->minimizePartition2();
+ retFsm = cntrl;
+ break;
+ }
+ case BT_Xdigit: {
+ /* Hex digits [0-9A-Fa-f]. */
+ FsmAp *digit = new FsmAp();
+ FsmAp *upper = new FsmAp();
+ FsmAp *lower = new FsmAp();
+ digit->rangeFsm( '0', '9' );
+ upper->rangeFsm( 'A', 'F' );
+ lower->rangeFsm( 'a', 'f' );
+ digit->unionOp( upper );
+ digit->unionOp( lower );
+ digit->minimizePartition2();
+ retFsm = digit;
+ break;
+ }
+ case BT_Lambda: {
+ retFsm = new FsmAp();
+ retFsm->lambdaFsm();
+ break;
+ }
+ case BT_Empty: {
+ retFsm = new FsmAp();
+ retFsm->emptyFsm();
+ break;
+ }}
+
+ return retFsm;
+}
+
+/* Check if this name inst or any name inst below is referenced. */
+bool NameInst::anyRefsRec()
+{
+ if ( numRefs > 0 )
+ return true;
+
+ /* Recurse on children until true. */
+ for ( NameVect::Iter ch = childVect; ch.lte(); ch++ ) {
+ if ( (*ch)->anyRefsRec() )
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * ParseData
+ */
+
+/* Initialize the structure that will collect info during the parse of a
+ * machine. */
+ParseData::ParseData(const char *fileName, char *sectionName,
+ const InputLoc &sectionLoc )
+:
+ sectionGraph(0),
+ generatingSectionSubset(false),
+ nextPriorKey(0),
+ /* 0 is reserved for global error actions. */
+ nextLocalErrKey(1),
+ nextNameId(0),
+ nextCondId(0),
+ alphTypeSet(false),
+ getKeyExpr(0),
+ accessExpr(0),
+ curStateExpr(0),
+ lowerNum(0),
+ upperNum(0),
+ fileName(fileName),
+ sectionName(sectionName),
+ sectionLoc(sectionLoc),
+ errorCount(0),
+ curActionOrd(0),
+ curPriorOrd(0),
+ rootName(0),
+ exportsRootName(0),
+ nextEpsilonResolvedLink(0),
+ nextLongestMatchId(1),
+ lmRequiresErrorState(false)
+{
+ /* Initialize the dictionary of graphs. This is our symbol table. The
+ * initialization needs to be done on construction which happens at the
+ * beginning of a machine spec so any assignment operators can reference
+ * the builtins. */
+ initGraphDict();
+}
+
+/* Clean up the data collected during a parse. */
+ParseData::~ParseData()
+{
+ /* Delete all the nodes in the action list. Will cause all the
+ * string data that represents the actions to be deallocated. */
+ actionList.empty();
+}
+
+/* Make a name id in the current name instantiation scope if it is not
+ * already there. */
+NameInst *ParseData::addNameInst( const InputLoc &loc, const char *data, bool isLabel )
+{
+ /* Create the name instantitaion object and insert it. */
+ NameInst *newNameInst = new NameInst( loc, curNameInst, data, nextNameId++, isLabel );
+ curNameInst->childVect.append( newNameInst );
+ if ( data != 0 )
+ curNameInst->children.insertMulti( data, newNameInst );
+ return newNameInst;
+}
+
+void ParseData::initNameWalk()
+{
+ curNameInst = rootName;
+ curNameChild = 0;
+}
+
+void ParseData::initExportsNameWalk()
+{
+ curNameInst = exportsRootName;
+ curNameChild = 0;
+}
+
+/* Goes into the next child scope. The number of the child is already set up.
+ * We need this for the syncronous name tree and parse tree walk to work
+ * properly. It is reset on entry into a scope and advanced on poping of a
+ * scope. A call to enterNameScope should be accompanied by a corresponding
+ * popNameScope. */
+NameFrame ParseData::enterNameScope( bool isLocal, int numScopes )
+{
+ /* Save off the current data. */
+ NameFrame retFrame;
+ retFrame.prevNameInst = curNameInst;
+ retFrame.prevNameChild = curNameChild;
+ retFrame.prevLocalScope = localNameScope;
+
+ /* Enter into the new name scope. */
+ for ( int i = 0; i < numScopes; i++ ) {
+ curNameInst = curNameInst->childVect[curNameChild];
+ curNameChild = 0;
+ }
+
+ if ( isLocal )
+ localNameScope = curNameInst;
+
+ return retFrame;
+}
+
+/* Return from a child scope to a parent. The parent info must be specified as
+ * an argument and is obtained from the corresponding call to enterNameScope.
+ * */
+void ParseData::popNameScope( const NameFrame &frame )
+{
+ /* Pop the name scope. */
+ curNameInst = frame.prevNameInst;
+ curNameChild = frame.prevNameChild+1;
+ localNameScope = frame.prevLocalScope;
+}
+
+void ParseData::resetNameScope( const NameFrame &frame )
+{
+ /* Pop the name scope. */
+ curNameInst = frame.prevNameInst;
+ curNameChild = frame.prevNameChild;
+ localNameScope = frame.prevLocalScope;
+}
+
+
+void ParseData::unsetObsoleteEntries( FsmAp *graph )
+{
+ /* Loop the reference names and increment the usage. Names that are no
+ * longer needed will be unset in graph. */
+ for ( NameVect::Iter ref = curNameInst->referencedNames; ref.lte(); ref++ ) {
+ /* Get the name. */
+ NameInst *name = *ref;
+ name->numUses += 1;
+
+ /* If the name is no longer needed unset its corresponding entry. */
+ if ( name->numUses == name->numRefs ) {
+ assert( graph->entryPoints.find( name->id ) != 0 );
+ graph->unsetEntry( name->id );
+ assert( graph->entryPoints.find( name->id ) == 0 );
+ }
+ }
+}
+
+NameSet ParseData::resolvePart( NameInst *refFrom, const char *data, bool recLabelsOnly )
+{
+ /* Queue needed for breadth-first search, load it with the start node. */
+ NameInstList nameQueue;
+ nameQueue.append( refFrom );
+
+ NameSet result;
+ while ( nameQueue.length() > 0 ) {
+ /* Pull the next from location off the queue. */
+ NameInst *from = nameQueue.detachFirst();
+
+ /* Look for the name. */
+ NameMapEl *low, *high;
+ if ( from->children.findMulti( data, low, high ) ) {
+ /* Record all instances of the name. */
+ for ( ; low <= high; low++ )
+ result.insert( low->value );
+ }
+
+ /* Name not there, do breadth-first operation of appending all
+ * childrent to the processing queue. */
+ for ( NameVect::Iter name = from->childVect; name.lte(); name++ ) {
+ if ( !recLabelsOnly || (*name)->isLabel )
+ nameQueue.append( *name );
+ }
+ }
+
+ /* Queue exhausted and name never found. */
+ return result;
+}
+
+void ParseData::resolveFrom( NameSet &result, NameInst *refFrom,
+ const NameRef &nameRef, int namePos )
+{
+ /* Look for the name in the owning scope of the factor with aug. */
+ NameSet partResult = resolvePart( refFrom, nameRef[namePos], false );
+
+ /* If there are more parts to the name then continue on. */
+ if ( ++namePos < nameRef.length() ) {
+ /* There are more components to the name, search using all the part
+ * results as the base. */
+ for ( NameSet::Iter name = partResult; name.lte(); name++ )
+ resolveFrom( result, *name, nameRef, namePos );
+ }
+ else {
+ /* This is the last component, append the part results to the final
+ * results. */
+ result.insert( partResult );
+ }
+}
+
+/* Write out a name reference. */
+ostream &operator<<( ostream &out, const NameRef &nameRef )
+{
+ int pos = 0;
+ if ( nameRef[pos] == 0 ) {
+ out << "::";
+ pos += 1;
+ }
+ out << nameRef[pos++];
+ for ( ; pos < nameRef.length(); pos++ )
+ out << "::" << nameRef[pos];
+ return out;
+}
+
+ostream &operator<<( ostream &out, const NameInst &nameInst )
+{
+ /* Count the number fully qualified name parts. */
+ int numParents = 0;
+ NameInst *curParent = nameInst.parent;
+ while ( curParent != 0 ) {
+ numParents += 1;
+ curParent = curParent->parent;
+ }
+
+ /* Make an array and fill it in. */
+ curParent = nameInst.parent;
+ NameInst **parents = new NameInst*[numParents];
+ for ( int p = numParents-1; p >= 0; p-- ) {
+ parents[p] = curParent;
+ curParent = curParent->parent;
+ }
+
+ /* Write the parents out, skip the root. */
+ for ( int p = 1; p < numParents; p++ )
+ out << "::" << ( parents[p]->name != 0 ? parents[p]->name : "<ANON>" );
+
+ /* Write the name and cleanup. */
+ out << "::" << ( nameInst.name != 0 ? nameInst.name : "<ANON>" );
+ delete[] parents;
+ return out;
+}
+
+struct CmpNameInstLoc
+{
+ static int compare( const NameInst *ni1, const NameInst *ni2 )
+ {
+ if ( ni1->loc.line < ni2->loc.line )
+ return -1;
+ else if ( ni1->loc.line > ni2->loc.line )
+ return 1;
+ else if ( ni1->loc.col < ni2->loc.col )
+ return -1;
+ else if ( ni1->loc.col > ni2->loc.col )
+ return 1;
+ return 0;
+ }
+};
+
+void errorStateLabels( const NameSet &resolved )
+{
+ MergeSort<NameInst*, CmpNameInstLoc> mergeSort;
+ mergeSort.sort( resolved.data, resolved.length() );
+ for ( NameSet::Iter res = resolved; res.lte(); res++ )
+ error((*res)->loc) << " -> " << **res << endl;
+}
+
+
+NameInst *ParseData::resolveStateRef( const NameRef &nameRef, InputLoc &loc, Action *action )
+{
+ NameInst *nameInst = 0;
+
+ /* Do the local search if the name is not strictly a root level name
+ * search. */
+ if ( nameRef[0] != 0 ) {
+ /* If the action is referenced, resolve all of them. */
+ if ( action != 0 && action->actionRefs.length() > 0 ) {
+ /* Look for the name in all referencing scopes. */
+ NameSet resolved;
+ for ( ActionRefs::Iter actRef = action->actionRefs; actRef.lte(); actRef++ )
+ resolveFrom( resolved, *actRef, nameRef, 0 );
+
+ if ( resolved.length() > 0 ) {
+ /* Take the first one. */
+ nameInst = resolved[0];
+ if ( resolved.length() > 1 ) {
+ /* Complain about the multiple references. */
+ error(loc) << "state reference " << nameRef <<
+ " resolves to multiple entry points" << endl;
+ errorStateLabels( resolved );
+ }
+ }
+ }
+ }
+
+ /* If not found in the local scope, look in global. */
+ if ( nameInst == 0 ) {
+ NameSet resolved;
+ int fromPos = nameRef[0] != 0 ? 0 : 1;
+ resolveFrom( resolved, rootName, nameRef, fromPos );
+
+ if ( resolved.length() > 0 ) {
+ /* Take the first. */
+ nameInst = resolved[0];
+ if ( resolved.length() > 1 ) {
+ /* Complain about the multiple references. */
+ error(loc) << "state reference " << nameRef <<
+ " resolves to multiple entry points" << endl;
+ errorStateLabels( resolved );
+ }
+ }
+ }
+
+ if ( nameInst == 0 ) {
+ /* If not found then complain. */
+ error(loc) << "could not resolve state reference " << nameRef << endl;
+ }
+ return nameInst;
+}
+
+void ParseData::resolveNameRefs( InlineList *inlineList, Action *action )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case InlineItem::Entry: case InlineItem::Goto:
+ case InlineItem::Call: case InlineItem::Next: {
+ /* Resolve, pass action for local search. */
+ NameInst *target = resolveStateRef( *item->nameRef, item->loc, action );
+
+ /* Check if the target goes into a longest match. */
+ NameInst *search = target->parent;
+ while ( search != 0 ) {
+ if ( search->isLongestMatch ) {
+ error(item->loc) << "cannot enter inside a longest "
+ "match construction as an entry point" << endl;
+ break;
+ }
+ search = search->parent;
+ }
+
+ /* Note the reference in the name. This will cause the entry
+ * point to survive to the end of the graph generating walk. */
+ if ( target != 0 )
+ target->numRefs += 1;
+ item->nameTarg = target;
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Some of the item types may have children. */
+ if ( item->children != 0 )
+ resolveNameRefs( item->children, action );
+ }
+}
+
+/* Resolve references to labels in actions. */
+void ParseData::resolveActionNameRefs()
+{
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Only care about the actions that are referenced. */
+ if ( act->actionRefs.length() > 0 )
+ resolveNameRefs( act->inlineList, act );
+ }
+}
+
+/* Walk a name tree starting at from and fill the name index. */
+void ParseData::fillNameIndex( NameInst *from )
+{
+ /* Fill the value for from in the name index. */
+ nameIndex[from->id] = from;
+
+ /* Recurse on the implicit final state and then all children. */
+ if ( from->final != 0 )
+ fillNameIndex( from->final );
+ for ( NameVect::Iter name = from->childVect; name.lte(); name++ )
+ fillNameIndex( *name );
+}
+
+void ParseData::makeRootNames()
+{
+ /* Create the root name. */
+ rootName = new NameInst( InputLoc(), 0, 0, nextNameId++, false );
+ exportsRootName = new NameInst( InputLoc(), 0, 0, nextNameId++, false );
+}
+
+/* Build the name tree and supporting data structures. */
+void ParseData::makeNameTree( GraphDictEl *dictEl )
+{
+ /* Set up curNameInst for the walk. */
+ initNameWalk();
+
+ if ( dictEl != 0 ) {
+ /* A start location has been specified. */
+ dictEl->value->makeNameTree( dictEl->loc, this );
+ }
+ else {
+ /* First make the name tree. */
+ for ( GraphList::Iter glel = instanceList; glel.lte(); glel++ ) {
+ /* Recurse on the instance. */
+ glel->value->makeNameTree( glel->loc, this );
+ }
+ }
+
+ /* The number of nodes in the tree can now be given by nextNameId */
+ nameIndex = new NameInst*[nextNameId];
+ memset( nameIndex, 0, sizeof(NameInst*)*nextNameId );
+ fillNameIndex( rootName );
+ fillNameIndex( exportsRootName );
+}
+
+
+void ParseData::createBuiltin(const char *name, BuiltinMachine builtin )
+{
+ Expression *expression = new Expression( builtin );
+ Join *join = new Join( expression );
+ JoinOrLm *joinOrLm = new JoinOrLm( join );
+ VarDef *varDef = new VarDef( name, joinOrLm );
+ GraphDictEl *graphDictEl = new GraphDictEl( name, varDef );
+ graphDict.insert( graphDictEl );
+}
+
+/* Initialize the graph dict with builtin types. */
+void ParseData::initGraphDict( )
+{
+ createBuiltin( "any", BT_Any );
+ createBuiltin( "ascii", BT_Ascii );
+ createBuiltin( "extend", BT_Extend );
+ createBuiltin( "alpha", BT_Alpha );
+ createBuiltin( "digit", BT_Digit );
+ createBuiltin( "alnum", BT_Alnum );
+ createBuiltin( "lower", BT_Lower );
+ createBuiltin( "upper", BT_Upper );
+ createBuiltin( "cntrl", BT_Cntrl );
+ createBuiltin( "graph", BT_Graph );
+ createBuiltin( "print", BT_Print );
+ createBuiltin( "punct", BT_Punct );
+ createBuiltin( "space", BT_Space );
+ createBuiltin( "xdigit", BT_Xdigit );
+ createBuiltin( "null", BT_Lambda );
+ createBuiltin( "zlen", BT_Lambda );
+ createBuiltin( "empty", BT_Empty );
+}
+
+/* Set the alphabet type. If the types are not valid returns false. */
+bool ParseData::setAlphType( char *s1, char *s2 )
+{
+ bool valid = false;
+ for ( int i = 0; i < hostLang->numHostTypes; i++ ) {
+ if ( strcmp( s1, hostLang->hostTypes[i].data1 ) == 0 &&
+ hostLang->hostTypes[i].data2 != 0 &&
+ strcmp( s2, hostLang->hostTypes[i].data2 ) == 0 )
+ {
+ valid = true;
+ userAlphType = hostLang->hostTypes + i;
+ break;
+ }
+ }
+
+ alphTypeSet = true;
+ return valid;
+}
+
+/* Set the alphabet type. If the types are not valid returns false. */
+bool ParseData::setAlphType( char *s1 )
+{
+ bool valid = false;
+ for ( int i = 0; i < hostLang->numHostTypes; i++ ) {
+ if ( strcmp( s1, hostLang->hostTypes[i].data1 ) == 0 &&
+ hostLang->hostTypes[i].data2 == 0 )
+ {
+ valid = true;
+ userAlphType = hostLang->hostTypes + i;
+ break;
+ }
+ }
+
+ alphTypeSet = true;
+ return valid;
+}
+
+/* Initialize the key operators object that will be referenced by all fsms
+ * created. */
+void ParseData::initKeyOps( )
+{
+ /* Signedness and bounds. */
+ HostType *alphType = alphTypeSet ? userAlphType : hostLang->defaultAlphType;
+ thisKeyOps.setAlphType( alphType );
+
+ if ( lowerNum != 0 ) {
+ /* If ranges are given then interpret the alphabet type. */
+ thisKeyOps.minKey = makeFsmKeyNum( lowerNum, rangeLowLoc, this );
+ thisKeyOps.maxKey = makeFsmKeyNum( upperNum, rangeHighLoc, this );
+ }
+
+ thisCondData.nextCondKey = thisKeyOps.maxKey;
+ thisCondData.nextCondKey.increment();
+}
+
+void ParseData::printNameInst( NameInst *nameInst, int level )
+{
+ for ( int i = 0; i < level; i++ )
+ cerr << " ";
+ cerr << (nameInst->name != 0 ? nameInst->name : "<ANON>") <<
+ " id: " << nameInst->id <<
+ " refs: " << nameInst->numRefs <<
+ " uses: " << nameInst->numUses << endl;
+ for ( NameVect::Iter name = nameInst->childVect; name.lte(); name++ )
+ printNameInst( *name, level+1 );
+}
+
+/* Remove duplicates of unique actions from an action table. */
+void ParseData::removeDups( ActionTable &table )
+{
+ /* Scan through the table looking for unique actions to
+ * remove duplicates of. */
+ for ( int i = 0; i < table.length(); i++ ) {
+ /* Remove any duplicates ahead of i. */
+ for ( int r = i+1; r < table.length(); ) {
+ if ( table[r].value == table[i].value )
+ table.vremove(r);
+ else
+ r += 1;
+ }
+ }
+}
+
+/* Remove duplicates from action lists. This operates only on transition and
+ * eof action lists and so should be called once all actions have been
+ * transfered to their final resting place. */
+void ParseData::removeActionDups( FsmAp *graph )
+{
+ /* Loop all states. */
+ for ( StateList::Iter state = graph->stateList; state.lte(); state++ ) {
+ /* Loop all transitions. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ )
+ removeDups( trans->actionTable );
+ removeDups( state->toStateActionTable );
+ removeDups( state->fromStateActionTable );
+ removeDups( state->eofActionTable );
+ }
+}
+
+Action *ParseData::newAction(const char *name, InlineList *inlineList )
+{
+ InputLoc loc;
+ loc.line = 1;
+ loc.col = 1;
+ loc.fileName = "<NONE>";
+
+ Action *action = new Action( loc, name, inlineList, nextCondId++ );
+ action->actionRefs.append( rootName );
+ actionList.append( action );
+ return action;
+}
+
+void ParseData::initLongestMatchData()
+{
+ if ( lmList.length() > 0 ) {
+ /* The initTokStart action resets the token start. */
+ InlineList *il1 = new InlineList;
+ il1->append( new InlineItem( InputLoc(), InlineItem::LmInitTokStart ) );
+ initTokStart = newAction( "initts", il1 );
+ initTokStart->isLmAction = true;
+
+ /* The initActId action gives act a default value. */
+ InlineList *il4 = new InlineList;
+ il4->append( new InlineItem( InputLoc(), InlineItem::LmInitAct ) );
+ initActId = newAction( "initact", il4 );
+ initActId->isLmAction = true;
+
+ /* The setTokStart action sets tokstart. */
+ InlineList *il5 = new InlineList;
+ il5->append( new InlineItem( InputLoc(), InlineItem::LmSetTokStart ) );
+ setTokStart = newAction( "tokstart", il5 );
+ setTokStart->isLmAction = true;
+
+ /* The setTokEnd action sets tokend. */
+ InlineList *il3 = new InlineList;
+ il3->append( new InlineItem( InputLoc(), InlineItem::LmSetTokEnd ) );
+ setTokEnd = newAction( "tokend", il3 );
+ setTokEnd->isLmAction = true;
+
+ /* The action will also need an ordering: ahead of all user action
+ * embeddings. */
+ initTokStartOrd = curActionOrd++;
+ initActIdOrd = curActionOrd++;
+ setTokStartOrd = curActionOrd++;
+ setTokEndOrd = curActionOrd++;
+ }
+}
+
+/* After building the graph, do some extra processing to ensure the runtime
+ * data of the longest mactch operators is consistent. */
+void ParseData::setLongestMatchData( FsmAp *graph )
+{
+ if ( lmList.length() > 0 ) {
+ /* Make sure all entry points (targets of fgoto, fcall, fnext, fentry)
+ * init the tokstart. */
+ for ( EntryMap::Iter en = graph->entryPoints; en.lte(); en++ ) {
+ /* This is run after duplicates are removed, we must guard against
+ * inserting a duplicate. */
+ ActionTable &actionTable = en->value->toStateActionTable;
+ if ( ! actionTable.hasAction( initTokStart ) )
+ actionTable.setAction( initTokStartOrd, initTokStart );
+ }
+
+ /* Find the set of states that are the target of transitions with
+ * actions that have calls. These states will be targeted by fret
+ * statements. */
+ StateSet states;
+ for ( StateList::Iter state = graph->stateList; state.lte(); state++ ) {
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ for ( ActionTable::Iter ati = trans->actionTable; ati.lte(); ati++ ) {
+ if ( ati->value->anyCall && trans->toState != 0 )
+ states.insert( trans->toState );
+ }
+ }
+ }
+
+
+ /* Init tokstart upon entering the above collected states. */
+ for ( StateSet::Iter ps = states; ps.lte(); ps++ ) {
+ /* This is run after duplicates are removed, we must guard against
+ * inserting a duplicate. */
+ ActionTable &actionTable = (*ps)->toStateActionTable;
+ if ( ! actionTable.hasAction( initTokStart ) )
+ actionTable.setAction( initTokStartOrd, initTokStart );
+ }
+ }
+}
+
+/* Make the graph from a graph dict node. Does minimization and state sorting. */
+FsmAp *ParseData::makeInstance( GraphDictEl *gdNode )
+{
+ /* Build the graph from a walk of the parse tree. */
+ FsmAp *graph = gdNode->value->walk( this );
+
+ /* Resolve any labels that point to multiple states. Any labels that are
+ * still around are referenced only by gotos and calls and they need to be
+ * made into deterministic entry points. */
+ graph->deterministicEntry();
+
+ /*
+ * All state construction is now complete.
+ */
+
+ /* Transfer global error actions. */
+ for ( StateList::Iter state = graph->stateList; state.lte(); state++ )
+ graph->transferErrorActions( state, 0 );
+
+ removeActionDups( graph );
+
+ /* Remove unreachable states. There should be no dead end states. The
+ * subtract and intersection operators are the only places where they may
+ * be created and those operators clean them up. */
+ graph->removeUnreachableStates();
+
+ /* No more fsm operations are to be done. Action ordering numbers are
+ * no longer of use and will just hinder minimization. Clear them. */
+ graph->nullActionKeys();
+
+ /* Transition priorities are no longer of use. We can clear them
+ * because they will just hinder minimization as well. Clear them. */
+ graph->clearAllPriorities();
+
+ if ( minimizeOpt != MinimizeNone ) {
+ /* Minimize here even if we minimized at every op. Now that function
+ * keys have been cleared we may get a more minimal fsm. */
+ switch ( minimizeLevel ) {
+ case MinimizeApprox:
+ graph->minimizeApproximate();
+ break;
+ case MinimizeStable:
+ graph->minimizeStable();
+ break;
+ case MinimizePartition1:
+ graph->minimizePartition1();
+ break;
+ case MinimizePartition2:
+ graph->minimizePartition2();
+ break;
+ }
+ }
+
+ graph->compressTransitions();
+
+ return graph;
+}
+
+void ParseData::printNameTree()
+{
+ /* Print the name instance map. */
+ for ( NameVect::Iter name = rootName->childVect; name.lte(); name++ )
+ printNameInst( *name, 0 );
+
+ cerr << "name index:" << endl;
+ /* Show that the name index is correct. */
+ for ( int ni = 0; ni < nextNameId; ni++ ) {
+ cerr << ni << ": ";
+ const char *name = nameIndex[ni]->name;
+ cerr << ( name != 0 ? name : "<ANON>" ) << endl;
+ }
+}
+
+FsmAp *ParseData::makeSpecific( GraphDictEl *gdNode )
+{
+ /* Build the name tree and supporting data structures. */
+ makeNameTree( gdNode );
+
+ /* Resove name references from gdNode. */
+ initNameWalk();
+ gdNode->value->resolveNameRefs( this );
+
+ /* Do not resolve action references. Since we are not building the entire
+ * graph there's a good chance that many name references will fail. This
+ * is okay since generating part of the graph is usually only done when
+ * inspecting the compiled machine. */
+
+ /* Same story for extern entry point references. */
+
+ /* Flag this case so that the XML code generator is aware that we haven't
+ * looked up name references in actions. It can then avoid segfaulting. */
+ generatingSectionSubset = true;
+
+ /* Just building the specified graph. */
+ initNameWalk();
+ FsmAp *mainGraph = makeInstance( gdNode );
+
+ return mainGraph;
+}
+
+FsmAp *ParseData::makeAll()
+{
+ /* Build the name tree and supporting data structures. */
+ makeNameTree( 0 );
+
+ /* Resove name references in the tree. */
+ initNameWalk();
+ for ( GraphList::Iter glel = instanceList; glel.lte(); glel++ )
+ glel->value->resolveNameRefs( this );
+
+ /* Resolve action code name references. */
+ resolveActionNameRefs();
+
+ /* Force name references to the top level instantiations. */
+ for ( NameVect::Iter inst = rootName->childVect; inst.lte(); inst++ )
+ (*inst)->numRefs += 1;
+
+ FsmAp *mainGraph = 0;
+ FsmAp **graphs = new FsmAp*[instanceList.length()];
+ int numOthers = 0;
+
+ /* Make all the instantiations, we know that main exists in this list. */
+ initNameWalk();
+ for ( GraphList::Iter glel = instanceList; glel.lte(); glel++ ) {
+ if ( strcmp( glel->key, mainMachine ) == 0 ) {
+ /* Main graph is always instantiated. */
+ mainGraph = makeInstance( glel );
+ }
+ else {
+ /* Instantiate and store in others array. */
+ graphs[numOthers++] = makeInstance( glel );
+ }
+ }
+
+ if ( mainGraph == 0 )
+ mainGraph = graphs[--numOthers];
+
+ if ( numOthers > 0 ) {
+ /* Add all the other graphs into main. */
+ mainGraph->globOp( graphs, numOthers );
+ }
+
+ delete[] graphs;
+ return mainGraph;
+}
+
+void ParseData::analyzeAction( Action *action, InlineList *inlineList )
+{
+ /* FIXME: Actions used as conditions should be very constrained. */
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ if ( item->type == InlineItem::Call || item->type == InlineItem::CallExpr )
+ action->anyCall = true;
+
+ /* Need to recurse into longest match items. */
+ if ( item->type == InlineItem::LmSwitch ) {
+ LongestMatch *lm = item->longestMatch;
+ for ( LmPartList::Iter lmi = *lm->longestMatchList; lmi.lte(); lmi++ ) {
+ if ( lmi->action != 0 )
+ analyzeAction( action, lmi->action->inlineList );
+ }
+ }
+
+ if ( item->type == InlineItem::LmOnLast ||
+ item->type == InlineItem::LmOnNext ||
+ item->type == InlineItem::LmOnLagBehind )
+ {
+ LongestMatchPart *lmi = item->longestMatchPart;
+ if ( lmi->action != 0 )
+ analyzeAction( action, lmi->action->inlineList );
+ }
+
+ if ( item->children != 0 )
+ analyzeAction( action, item->children );
+ }
+}
+
+
+/* Check actions for bad uses of fsm directives. We don't go inside longest
+ * match items in actions created by ragel, since we just want the user
+ * actions. */
+void ParseData::checkInlineList( Action *act, InlineList *inlineList )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ /* EOF checks. */
+ if ( act->numEofRefs > 0 ) {
+ switch ( item->type ) {
+ case InlineItem::PChar:
+ error(item->loc) << "pointer to current element does not exist in "
+ "EOF action code" << endl;
+ break;
+ case InlineItem::Char:
+ error(item->loc) << "current element does not exist in "
+ "EOF action code" << endl;
+ break;
+ case InlineItem::Hold:
+ error(item->loc) << "changing the current element not possible in "
+ "EOF action code" << endl;
+ break;
+ case InlineItem::Exec:
+ error(item->loc) << "changing the current element not possible in "
+ "EOF action code" << endl;
+ break;
+ case InlineItem::Goto: case InlineItem::Call:
+ case InlineItem::Next: case InlineItem::GotoExpr:
+ case InlineItem::CallExpr: case InlineItem::NextExpr:
+ case InlineItem::Ret:
+ error(item->loc) << "changing the current state not possible in "
+ "EOF action code" << endl;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Recurse. */
+ if ( item->children != 0 )
+ checkInlineList( act, item->children );
+ }
+}
+
+void ParseData::checkAction( Action *action )
+{
+ /* Check for actions with calls that are embedded within a longest match
+ * machine. */
+ if ( !action->isLmAction && action->numRefs() > 0 && action->anyCall ) {
+ for ( ActionRefs::Iter ar = action->actionRefs; ar.lte(); ar++ ) {
+ NameInst *check = *ar;
+ while ( check != 0 ) {
+ if ( check->isLongestMatch ) {
+ error(action->loc) << "within a scanner, fcall is permitted"
+ " only in pattern actions" << endl;
+ break;
+ }
+ check = check->parent;
+ }
+ }
+ }
+
+ checkInlineList( action, action->inlineList );
+}
+
+
+void ParseData::analyzeGraph( FsmAp *graph )
+{
+ for ( ActionList::Iter act = actionList; act.lte(); act++ )
+ analyzeAction( act, act->inlineList );
+
+ for ( StateList::Iter st = graph->stateList; st.lte(); st++ ) {
+ /* The transition list. */
+ for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) {
+ for ( ActionTable::Iter at = trans->actionTable; at.lte(); at++ )
+ at->value->numTransRefs += 1;
+ }
+
+ for ( ActionTable::Iter at = st->toStateActionTable; at.lte(); at++ )
+ at->value->numToStateRefs += 1;
+
+ for ( ActionTable::Iter at = st->fromStateActionTable; at.lte(); at++ )
+ at->value->numFromStateRefs += 1;
+
+ for ( ActionTable::Iter at = st->eofActionTable; at.lte(); at++ )
+ at->value->numEofRefs += 1;
+
+ for ( StateCondList::Iter sc = st->stateCondList; sc.lte(); sc++ ) {
+ for ( CondSet::Iter sci = sc->condSpace->condSet; sci.lte(); sci++ )
+ (*sci)->numCondRefs += 1;
+ }
+ }
+
+ /* Checks for bad usage of directives in action code. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ )
+ checkAction( act );
+}
+
+void ParseData::makeExportsNameTree()
+{
+ /* Make a name tree for the exports. */
+ initExportsNameWalk();
+
+ /* First make the name tree. */
+ for ( GraphDict::Iter gdel = graphDict; gdel.lte(); gdel++ ) {
+ if ( gdel->value->isExport ) {
+ /* Recurse on the instance. */
+ gdel->value->makeNameTree( gdel->loc, this );
+ }
+ }
+}
+
+void ParseData::makeExports()
+{
+ makeExportsNameTree();
+
+ /* Resove name references in the tree. */
+ initExportsNameWalk();
+ for ( GraphDict::Iter gdel = graphDict; gdel.lte(); gdel++ ) {
+ if ( gdel->value->isExport )
+ gdel->value->resolveNameRefs( this );
+ }
+
+ /* Make all the instantiations, we know that main exists in this list. */
+ initExportsNameWalk();
+ for ( GraphDict::Iter gdel = graphDict; gdel.lte(); gdel++ ) {
+ /* Check if this var def is an export. */
+ if ( gdel->value->isExport ) {
+ /* Build the graph from a walk of the parse tree. */
+ FsmAp *graph = gdel->value->walk( this );
+
+ /* Build the graph from a walk of the parse tree. */
+ if ( !graph->checkSingleCharMachine() ) {
+ error(gdel->loc) << "bad export machine, must define "
+ "a single character" << endl;
+ }
+ else {
+ /* Safe to extract the key and declare the export. */
+ Key exportKey = graph->startState->outList.head->lowKey;
+ exportList.append( new Export( gdel->value->name, exportKey ) );
+ }
+ }
+ }
+
+}
+
+void ParseData::prepareMachineGen( GraphDictEl *graphDictEl )
+{
+ beginProcessing();
+ initKeyOps();
+ makeRootNames();
+ initLongestMatchData();
+
+ /* Make the graph, do minimization. */
+ if ( graphDictEl == 0 )
+ sectionGraph = makeAll();
+ else
+ sectionGraph = makeSpecific( graphDictEl );
+
+ /* Compute exports from the export definitions. */
+ makeExports();
+
+ /* If any errors have occured in the input file then don't write anything. */
+ if ( gblErrorCount > 0 )
+ return;
+
+ analyzeGraph( sectionGraph );
+
+ /* Depends on the graph analysis. */
+ setLongestMatchData( sectionGraph );
+
+ /* Decide if an error state is necessary.
+ * 1. There is an error transition
+ * 2. There is a gap in the transitions
+ * 3. The longest match operator requires it. */
+ if ( lmRequiresErrorState || sectionGraph->hasErrorTrans() )
+ sectionGraph->errState = sectionGraph->addState();
+
+ /* State numbers need to be assigned such that all final states have a
+ * larger state id number than all non-final states. This enables the
+ * first_final mechanism to function correctly. We also want states to be
+ * ordered in a predictable fashion. So we first apply a depth-first
+ * search, then do a stable sort by final state status, then assign
+ * numbers. */
+
+ sectionGraph->depthFirstOrdering();
+ sectionGraph->sortStatesByFinal();
+ sectionGraph->setStateNumbers( 0 );
+}
+
+void ParseData::generateXML( ostream &out )
+{
+ beginProcessing();
+
+ /* Make the generator. */
+ XMLCodeGen codeGen( sectionName, this, sectionGraph, out );
+
+ /* Write out with it. */
+ codeGen.writeXML();
+
+ if ( printStatistics ) {
+ cerr << "fsm name : " << sectionName << endl;
+ cerr << "num states: " << sectionGraph->stateList.length() << endl;
+ cerr << endl;
+ }
+}
+
+/* Send eof to all parsers. */
+void terminateAllParsers( )
+{
+ /* FIXME: a proper token is needed here. Suppose we should use the
+ * location of EOF in the last file that the parser was referenced in. */
+ InputLoc loc;
+ loc.fileName = "<EOF>";
+ loc.line = 0;
+ loc.col = 0;
+ for ( ParserDict::Iter pdel = parserDict; pdel.lte(); pdel++ )
+ pdel->value->token( loc, _eof, 0, 0 );
+}
+
+void writeLanguage( std::ostream &out )
+{
+ out << " lang=\"";
+ switch ( hostLangType ) {
+ case CCode: out << "C"; break;
+ case DCode: out << "D"; break;
+ case JavaCode: out << "Java"; break;
+ case RubyCode: out << "Ruby"; break;
+ }
+ out << "\"";
+
+}
+
+void writeMachines( std::ostream &out, std::string hostData, const char *inputFileName )
+{
+ if ( machineSpec == 0 && machineName == 0 ) {
+ /* No machine spec or machine name given. Generate everything. */
+ for ( ParserDict::Iter parser = parserDict; parser.lte(); parser++ ) {
+ ParseData *pd = parser->value->pd;
+ if ( pd->instanceList.length() > 0 )
+ pd->prepareMachineGen( 0 );
+ }
+
+ if ( gblErrorCount == 0 ) {
+ out << "<ragel filename=\"" << inputFileName << "\"";
+ writeLanguage( out );
+ out << ">\n";
+ for ( ParserDict::Iter parser = parserDict; parser.lte(); parser++ ) {
+ ParseData *pd = parser->value->pd;
+ if ( pd->instanceList.length() > 0 )
+ pd->generateXML( out );
+ }
+ out << hostData;
+ out << "</ragel>\n";
+ }
+ }
+ else if ( parserDict.length() > 0 ) {
+ /* There is either a machine spec or machine name given. */
+ ParseData *parseData = 0;
+ GraphDictEl *graphDictEl = 0;
+
+ /* Traverse the sections, break out when we find a section/machine
+ * that matches the one specified. */
+ for ( ParserDict::Iter parser = parserDict; parser.lte(); parser++ ) {
+ ParseData *checkPd = parser->value->pd;
+ if ( machineSpec == 0 || strcmp( checkPd->sectionName, machineSpec ) == 0 ) {
+ GraphDictEl *checkGdEl = 0;
+ if ( machineName == 0 || (checkGdEl =
+ checkPd->graphDict.find( machineName )) != 0 )
+ {
+ /* Have a machine spec and/or machine name that matches
+ * the -M/-S options. */
+ parseData = checkPd;
+ graphDictEl = checkGdEl;
+ break;
+ }
+ }
+ }
+
+ if ( parseData == 0 )
+ error() << "could not locate machine specified with -S and/or -M" << endl;
+ else {
+ /* Section/Machine to emit was found. Prepare and emit it. */
+ parseData->prepareMachineGen( graphDictEl );
+ if ( gblErrorCount == 0 ) {
+ out << "<ragel filename=\"" << inputFileName << "\"";
+ writeLanguage( out );
+ out << ">\n";
+ parseData->generateXML( out );
+ out << hostData;
+ out << "</ragel>\n";
+ }
+ }
+ }
+}
diff --git a/contrib/tools/ragel5/ragel/parsedata.h b/contrib/tools/ragel5/ragel/parsedata.h
new file mode 100644
index 0000000000..2baa7373d2
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/parsedata.h
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PARSEDATA_H
+#define _PARSEDATA_H
+
+#include <iostream>
+#include <limits.h>
+#include "avlmap.h"
+#include "bstmap.h"
+#include "vector.h"
+#include "dlist.h"
+#include "fsmgraph.h"
+#include "compare.h"
+#include "vector.h"
+#include "common.h"
+#include "parsetree.h"
+
+/* Forwards. */
+using std::ostream;
+
+struct VarDef;
+struct Join;
+struct Expression;
+struct Term;
+struct FactorWithAug;
+struct FactorWithLabel;
+struct FactorWithRep;
+struct FactorWithNeg;
+struct Factor;
+struct Literal;
+struct Range;
+struct RegExpr;
+struct ReItem;
+struct ReOrBlock;
+struct ReOrItem;
+struct LongestMatch;
+typedef DList<LongestMatch> LmList;
+
+/* Graph dictionary. */
+struct GraphDictEl
+:
+ public AvlTreeEl<GraphDictEl>,
+ public DListEl<GraphDictEl>
+{
+ GraphDictEl(const char *k )
+ : key(k), value(0), isInstance(false) { }
+ GraphDictEl(const char *k, VarDef *value )
+ : key(k), value(value), isInstance(false) { }
+
+ const char *getKey() { return key; }
+
+ const char *key;
+ VarDef *value;
+ bool isInstance;
+
+ /* Location info of graph definition. Points to variable name of assignment. */
+ InputLoc loc;
+};
+
+typedef AvlTree<GraphDictEl, char*, CmpStr> GraphDict;
+typedef DList<GraphDictEl> GraphList;
+
+/* Priority name dictionary. */
+typedef AvlMapEl<char*, int> PriorDictEl;
+typedef AvlMap<char*, int, CmpStr> PriorDict;
+
+/* Local error name dictionary. */
+typedef AvlMapEl<const char*, int> LocalErrDictEl;
+typedef AvlMap<const char*, int, CmpStr> LocalErrDict;
+
+/* Tree of instantiated names. */
+typedef BstMapEl<const char*, NameInst*> NameMapEl;
+typedef BstMap<const char*, NameInst*, CmpStr> NameMap;
+typedef Vector<NameInst*> NameVect;
+typedef BstSet<NameInst*> NameSet;
+
+/* Node in the tree of instantiated names. */
+struct NameInst
+{
+ NameInst( const InputLoc &loc, NameInst *parent, const char *name, int id, bool isLabel ) :
+ loc(loc), parent(parent), name(name), id(id), isLabel(isLabel),
+ isLongestMatch(false), numRefs(0), numUses(0), start(0), final(0) {}
+
+ InputLoc loc;
+
+ /* Keep parent pointers in the name tree to retrieve
+ * fully qulified names. */
+ NameInst *parent;
+
+ const char *name;
+ int id;
+ bool isLabel;
+ bool isLongestMatch;
+
+ int numRefs;
+ int numUses;
+
+ /* Names underneath us, excludes anonymous names. */
+ NameMap children;
+
+ /* All names underneath us in order of appearance. */
+ NameVect childVect;
+
+ /* Join scopes need an implicit "final" target. */
+ NameInst *start, *final;
+
+ /* During a fsm generation walk, lists the names that are referenced by
+ * epsilon operations in the current scope. After the link is made by the
+ * epsilon reference and the join operation is complete, the label can
+ * have its refcount decremented. Once there are no more references the
+ * entry point can be removed from the fsm returned. */
+ NameVect referencedNames;
+
+ /* Pointers for the name search queue. */
+ NameInst *prev, *next;
+
+ /* Check if this name inst or any name inst below is referenced. */
+ bool anyRefsRec();
+};
+
+typedef DList<NameInst> NameInstList;
+
+/* Stack frame used in walking the name tree. */
+struct NameFrame
+{
+ NameInst *prevNameInst;
+ int prevNameChild;
+ NameInst *prevLocalScope;
+};
+
+/* Class to collect information about the machine during the
+ * parse of input. */
+struct ParseData
+{
+ /* Create a new parse data object. This is done at the beginning of every
+ * fsm specification. */
+ ParseData(const char *fileName, char *sectionName, const InputLoc &sectionLoc );
+ ~ParseData();
+
+ /*
+ * Setting up the graph dict.
+ */
+
+ /* Initialize a graph dict with the basic fsms. */
+ void initGraphDict();
+ void createBuiltin(const char *name, BuiltinMachine builtin );
+
+ /* Make a name id in the current name instantiation scope if it is not
+ * already there. */
+ NameInst *addNameInst( const InputLoc &loc, const char *data, bool isLabel );
+ void makeRootNames();
+ void makeNameTree( GraphDictEl *gdNode );
+ void makeExportsNameTree();
+ void fillNameIndex( NameInst *from );
+ void printNameTree();
+
+ /* Increments the usage count on entry names. Names that are no longer
+ * needed will have their entry points unset. */
+ void unsetObsoleteEntries( FsmAp *graph );
+
+ /* Resove name references in action code and epsilon transitions. */
+ NameSet resolvePart( NameInst *refFrom, const char *data, bool recLabelsOnly );
+ void resolveFrom( NameSet &result, NameInst *refFrom,
+ const NameRef &nameRef, int namePos );
+ NameInst *resolveStateRef( const NameRef &nameRef, InputLoc &loc, Action *action );
+ void resolveNameRefs( InlineList *inlineList, Action *action );
+ void resolveActionNameRefs();
+
+ /* Set the alphabet type. If type types are not valid returns false. */
+ bool setAlphType( char *s1, char *s2 );
+ bool setAlphType( char *s1 );
+
+ /* Unique actions. */
+ void removeDups( ActionTable &actionTable );
+ void removeActionDups( FsmAp *graph );
+
+ /* Dumping the name instantiation tree. */
+ void printNameInst( NameInst *nameInst, int level );
+
+ /* Make the graph from a graph dict node. Does minimization. */
+ FsmAp *makeInstance( GraphDictEl *gdNode );
+ FsmAp *makeSpecific( GraphDictEl *gdNode );
+ FsmAp *makeAll();
+
+ /* Checking the contents of actions. */
+ void checkAction( Action *action );
+ void checkInlineList( Action *act, InlineList *inlineList );
+
+ void analyzeAction( Action *action, InlineList *inlineList );
+ void analyzeGraph( FsmAp *graph );
+ void makeExports();
+
+ void prepareMachineGen( GraphDictEl *graphDictEl );
+ void generateXML( ostream &out );
+ FsmAp *sectionGraph;
+ bool generatingSectionSubset;
+
+ void initKeyOps();
+
+ /*
+ * Data collected during the parse.
+ */
+
+ /* Dictionary of graphs. Both instances and non-instances go here. */
+ GraphDict graphDict;
+
+ /* The list of instances. */
+ GraphList instanceList;
+
+ /* Dictionary of actions. Lets actions be defined and then referenced. */
+ ActionDict actionDict;
+
+ /* Dictionary of named priorities. */
+ PriorDict priorDict;
+
+ /* Dictionary of named local errors. */
+ LocalErrDict localErrDict;
+
+ /* List of actions. Will be pasted into a switch statement. */
+ ActionList actionList;
+
+ /* The id of the next priority name and label. */
+ int nextPriorKey, nextLocalErrKey, nextNameId, nextCondId;
+
+ /* The default priority number key for a machine. This is active during
+ * the parse of the rhs of a machine assignment. */
+ int curDefPriorKey;
+
+ int curDefLocalErrKey;
+
+ /* Alphabet type. */
+ HostType *userAlphType;
+ bool alphTypeSet;
+
+ /* Element type and get key expression. */
+ InlineList *getKeyExpr;
+ InlineList *accessExpr;
+ InlineList *curStateExpr;
+
+ /* The alphabet range. */
+ char *lowerNum, *upperNum;
+ Key lowKey, highKey;
+ InputLoc rangeLowLoc, rangeHighLoc;
+
+ /* The name of the file the fsm is from, and the spec name. */
+ const char *fileName;
+ char *sectionName;
+ InputLoc sectionLoc;
+
+ /* Number of errors encountered parsing the fsm spec. */
+ int errorCount;
+
+ /* Counting the action and priority ordering. */
+ int curActionOrd;
+ int curPriorOrd;
+
+ /* Root of the name tree. One root is for the instantiated machines. The
+ * other root is for exported definitions. */
+ NameInst *rootName;
+ NameInst *exportsRootName;
+
+ /* Name tree walking. */
+ NameInst *curNameInst;
+ int curNameChild;
+
+ /* The place where resolved epsilon transitions go. These cannot go into
+ * the parse tree because a single epsilon op can resolve more than once
+ * to different nameInsts if the machine it's in is used more than once. */
+ NameVect epsilonResolvedLinks;
+ int nextEpsilonResolvedLink;
+
+ /* Root of the name tree used for doing local name searches. */
+ NameInst *localNameScope;
+
+ void setLmInRetLoc( InlineList *inlineList );
+ void initLongestMatchData();
+ void setLongestMatchData( FsmAp *graph );
+ void initNameWalk();
+ void initExportsNameWalk();
+ NameInst *nextNameScope() { return curNameInst->childVect[curNameChild]; }
+ NameFrame enterNameScope( bool isLocal, int numScopes );
+ void popNameScope( const NameFrame &frame );
+ void resetNameScope( const NameFrame &frame );
+
+ /* Make name ids to name inst pointers. */
+ NameInst **nameIndex;
+
+ /* Counter for assigning ids to longest match items. */
+ int nextLongestMatchId;
+ bool lmRequiresErrorState;
+
+ /* List of all longest match parse tree items. */
+ LmList lmList;
+
+ Action *newAction(const char *name, InlineList *inlineList );
+
+ Action *initTokStart;
+ int initTokStartOrd;
+
+ Action *setTokStart;
+ int setTokStartOrd;
+
+ Action *initActId;
+ int initActIdOrd;
+
+ Action *setTokEnd;
+ int setTokEndOrd;
+
+ void beginProcessing()
+ {
+ ::condData = &thisCondData;
+ ::keyOps = &thisKeyOps;
+ }
+
+ CondData thisCondData;
+ KeyOps thisKeyOps;
+
+ ExportList exportList;
+};
+
+void afterOpMinimize( FsmAp *fsm, bool lastInSeq = true );
+Key makeFsmKeyHex( char *str, const InputLoc &loc, ParseData *pd );
+Key makeFsmKeyDec( char *str, const InputLoc &loc, ParseData *pd );
+Key makeFsmKeyNum( char *str, const InputLoc &loc, ParseData *pd );
+Key makeFsmKeyChar( char c, ParseData *pd );
+void makeFsmKeyArray( Key *result, char *data, int len, ParseData *pd );
+void makeFsmUniqueKeyArray( KeySet &result, char *data, int len,
+ bool caseInsensitive, ParseData *pd );
+FsmAp *makeBuiltin( BuiltinMachine builtin, ParseData *pd );
+FsmAp *dotFsm( ParseData *pd );
+FsmAp *dotStarFsm( ParseData *pd );
+
+void errorStateLabels( const NameSet &locations );
+
+/* Data used by the parser specific to the current file. Supports the include
+ * system, since a new parser is executed for each included file. */
+struct InputData
+{
+ InputData( char *fileName, char *includeSpec, char *includeTo ) :
+ pd(0), sectionName(0), defaultParseData(0),
+ first_line(1), first_column(1),
+ last_line(1), last_column(0),
+ fileName(fileName), includeSpec(includeSpec),
+ includeTo(includeTo), active(true)
+ {}
+
+ /* For collecting a name references. */
+ NameRef nameRef;
+ NameRefList nameRefList;
+
+ /* The parse data. For each fsm spec, the parser collects things that it parses
+ * in data structures in here. */
+ ParseData *pd;
+
+ char *sectionName;
+ ParseData *defaultParseData;
+
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+
+ char *fileName;
+
+ /* If this is an included file, this contains the specification to search
+ * for. IncludeTo will contain the spec name that does the includng. */
+ char *includeSpec;
+ char *includeTo;
+
+ bool active;
+ InputLoc sectionLoc;
+};
+
+struct Parser;
+
+typedef AvlMap<char*, Parser *, CmpStr> ParserDict;
+typedef AvlMapEl<char*, Parser *> ParserDictEl;
+
+extern ParserDict parserDict;
+
+
+#endif /* _PARSEDATA_H */
diff --git a/contrib/tools/ragel5/ragel/parsetree.cpp b/contrib/tools/ragel5/ragel/parsetree.cpp
new file mode 100644
index 0000000000..4755e3085b
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/parsetree.cpp
@@ -0,0 +1,2089 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+/* Parsing. */
+#include "ragel.h"
+#include "rlparse.h"
+#include "parsetree.h"
+
+using namespace std;
+ostream &operator<<( ostream &out, const NameRef &nameRef );
+ostream &operator<<( ostream &out, const NameInst &nameInst );
+
+/* Convert the literal string which comes in from the scanner into an array of
+ * characters with escapes and options interpreted. Also null terminates the
+ * string. Though this null termination should not be relied on for
+ * interpreting literals in the parser because the string may contain a
+ * literal string with \0 */
+void Token::prepareLitString( Token &result, bool &caseInsensitive )
+{
+ result.data = new char[this->length+1];
+ caseInsensitive = false;
+
+ char *src = this->data + 1;
+ char *end = this->data + this->length - 1;
+
+ while ( *end != '\'' && *end != '\"' ) {
+ if ( *end == 'i' )
+ caseInsensitive = true;
+ else {
+ error( this->loc ) << "literal string '" << *end <<
+ "' option not supported" << endl;
+ }
+ end -= 1;
+ }
+
+ char *dest = result.data;
+ int len = 0;
+ while ( src != end ) {
+ if ( *src == '\\' ) {
+ switch ( src[1] ) {
+ case '0': dest[len++] = '\0'; break;
+ case 'a': dest[len++] = '\a'; break;
+ case 'b': dest[len++] = '\b'; break;
+ case 't': dest[len++] = '\t'; break;
+ case 'n': dest[len++] = '\n'; break;
+ case 'v': dest[len++] = '\v'; break;
+ case 'f': dest[len++] = '\f'; break;
+ case 'r': dest[len++] = '\r'; break;
+ case '\n': break;
+ default: dest[len++] = src[1]; break;
+ }
+ src += 2;
+ }
+ else {
+ dest[len++] = *src++;
+ }
+ }
+ result.length = len;
+ result.data[result.length] = 0;
+}
+
+
+FsmAp *VarDef::walk( ParseData *pd )
+{
+ /* We enter into a new name scope. */
+ NameFrame nameFrame = pd->enterNameScope( true, 1 );
+
+ /* Recurse on the expression. */
+ FsmAp *rtnVal = joinOrLm->walk( pd );
+
+ /* Do the tranfer of local error actions. */
+ LocalErrDictEl *localErrDictEl = pd->localErrDict.find( name );
+ if ( localErrDictEl != 0 ) {
+ for ( StateList::Iter state = rtnVal->stateList; state.lte(); state++ )
+ rtnVal->transferErrorActions( state, localErrDictEl->value );
+ }
+
+ /* If the expression below is a join operation with multiple expressions
+ * then it just had epsilon transisions resolved. If it is a join
+ * with only a single expression then run the epsilon op now. */
+ if ( joinOrLm->type == JoinOrLm::JoinType && joinOrLm->join->exprList.length() == 1 )
+ rtnVal->epsilonOp();
+
+ /* We can now unset entry points that are not longer used. */
+ pd->unsetObsoleteEntries( rtnVal );
+
+ /* If the name of the variable is referenced then add the entry point to
+ * the graph. */
+ if ( pd->curNameInst->numRefs > 0 )
+ rtnVal->setEntry( pd->curNameInst->id, rtnVal->startState );
+
+ /* Pop the name scope. */
+ pd->popNameScope( nameFrame );
+ return rtnVal;
+}
+
+void VarDef::makeNameTree( const InputLoc &loc, ParseData *pd )
+{
+ /* The variable definition enters a new scope. */
+ NameInst *prevNameInst = pd->curNameInst;
+ pd->curNameInst = pd->addNameInst( loc, name, false );
+
+ if ( joinOrLm->type == JoinOrLm::LongestMatchType )
+ pd->curNameInst->isLongestMatch = true;
+
+ /* Recurse. */
+ joinOrLm->makeNameTree( pd );
+
+ /* The name scope ends, pop the name instantiation. */
+ pd->curNameInst = prevNameInst;
+}
+
+void VarDef::resolveNameRefs( ParseData *pd )
+{
+ /* Entering into a new scope. */
+ NameFrame nameFrame = pd->enterNameScope( true, 1 );
+
+ /* Recurse. */
+ joinOrLm->resolveNameRefs( pd );
+
+ /* The name scope ends, pop the name instantiation. */
+ pd->popNameScope( nameFrame );
+}
+
+InputLoc LongestMatchPart::getLoc()
+{
+ return action != 0 ? action->loc : semiLoc;
+}
+
+/*
+ * If there are any LMs then all of the following entry points must reset
+ * tokstart:
+ *
+ * 1. fentry(StateRef)
+ * 2. ftoto(StateRef), fcall(StateRef), fnext(StateRef)
+ * 3. targt of any transition that has an fcall (the return loc).
+ * 4. start state of all longest match routines.
+ */
+
+Action *LongestMatch::newAction( ParseData *pd, const InputLoc &loc,
+ const char *name, InlineList *inlineList )
+{
+ Action *action = new Action( loc, name, inlineList, pd->nextCondId++ );
+ action->actionRefs.append( pd->curNameInst );
+ pd->actionList.append( action );
+ action->isLmAction = true;
+ return action;
+}
+
+void LongestMatch::makeActions( ParseData *pd )
+{
+ /* Make actions that set the action id. */
+ for ( LmPartList::Iter lmi = *longestMatchList; lmi.lte(); lmi++ ) {
+ /* For each part create actions for setting the match type. We need
+ * to do this so that the actions will go into the actionIndex. */
+ InlineList *inlineList = new InlineList;
+ inlineList->append( new InlineItem( lmi->getLoc(), this, lmi, InlineItem::LmSetActId ) );
+ char *actName = new char[50];
+ sprintf( actName, "store%i", lmi->longestMatchId );
+ lmi->setActId = newAction( pd, lmi->getLoc(), actName, inlineList );
+ }
+
+ /* Make actions that execute the user action and restart on the last character. */
+ for ( LmPartList::Iter lmi = *longestMatchList; lmi.lte(); lmi++ ) {
+ /* For each part create actions for setting the match type. We need
+ * to do this so that the actions will go into the actionIndex. */
+ InlineList *inlineList = new InlineList;
+ inlineList->append( new InlineItem( lmi->getLoc(), this, lmi,
+ InlineItem::LmOnLast ) );
+ char *actName = new char[50];
+ sprintf( actName, "imm%i", lmi->longestMatchId );
+ lmi->actOnLast = newAction( pd, lmi->getLoc(), actName, inlineList );
+ }
+
+ /* Make actions that execute the user action and restart on the next
+ * character. These actions will set tokend themselves (it is the current
+ * char). */
+ for ( LmPartList::Iter lmi = *longestMatchList; lmi.lte(); lmi++ ) {
+ /* For each part create actions for setting the match type. We need
+ * to do this so that the actions will go into the actionIndex. */
+ InlineList *inlineList = new InlineList;
+ inlineList->append( new InlineItem( lmi->getLoc(), this, lmi,
+ InlineItem::LmOnNext ) );
+ char *actName = new char[50];
+ sprintf( actName, "lagh%i", lmi->longestMatchId );
+ lmi->actOnNext = newAction( pd, lmi->getLoc(), actName, inlineList );
+ }
+
+ /* Make actions that execute the user action and restart at tokend. These
+ * actions execute some time after matching the last char. */
+ for ( LmPartList::Iter lmi = *longestMatchList; lmi.lte(); lmi++ ) {
+ /* For each part create actions for setting the match type. We need
+ * to do this so that the actions will go into the actionIndex. */
+ InlineList *inlineList = new InlineList;
+ inlineList->append( new InlineItem( lmi->getLoc(), this, lmi,
+ InlineItem::LmOnLagBehind ) );
+ char *actName = new char[50];
+ sprintf( actName, "lag%i", lmi->longestMatchId );
+ lmi->actLagBehind = newAction( pd, lmi->getLoc(), actName, inlineList );
+ }
+
+ InputLoc loc;
+ loc.line = 1;
+ loc.col = 1;
+
+ /* Create the error action. */
+ InlineList *il6 = new InlineList;
+ il6->append( new InlineItem( loc, this, 0, InlineItem::LmSwitch ) );
+ lmActSelect = newAction( pd, loc, "lagsel", il6 );
+}
+
+void LongestMatch::findName( ParseData *pd )
+{
+ NameInst *nameInst = pd->curNameInst;
+ while ( nameInst->name == 0 ) {
+ nameInst = nameInst->parent;
+ /* Since every machine must must have a name, we should always find a
+ * name for the longest match. */
+ assert( nameInst != 0 );
+ }
+ name = nameInst->name;
+}
+
+void LongestMatch::makeNameTree( ParseData *pd )
+{
+ /* Create an anonymous scope for the longest match. Will be used for
+ * restarting machine after matching a token. */
+ NameInst *prevNameInst = pd->curNameInst;
+ pd->curNameInst = pd->addNameInst( loc, 0, false );
+
+ /* Recurse into all parts of the longest match operator. */
+ for ( LmPartList::Iter lmi = *longestMatchList; lmi.lte(); lmi++ )
+ lmi->join->makeNameTree( pd );
+
+ /* Traverse the name tree upwards to find a name for this lm. */
+ findName( pd );
+
+ /* Also make the longest match's actions at this point. */
+ makeActions( pd );
+
+ /* The name scope ends, pop the name instantiation. */
+ pd->curNameInst = prevNameInst;
+}
+
+void LongestMatch::resolveNameRefs( ParseData *pd )
+{
+ /* The longest match gets its own name scope. */
+ NameFrame nameFrame = pd->enterNameScope( true, 1 );
+
+ /* Take an action reference for each longest match item and recurse. */
+ for ( LmPartList::Iter lmi = *longestMatchList; lmi.lte(); lmi++ ) {
+ /* Record the reference if the item has an action. */
+ if ( lmi->action != 0 )
+ lmi->action->actionRefs.append( pd->localNameScope );
+
+ /* Recurse down the join. */
+ lmi->join->resolveNameRefs( pd );
+ }
+
+ /* The name scope ends, pop the name instantiation. */
+ pd->popNameScope( nameFrame );
+}
+
+void LongestMatch::restart( FsmAp *graph, TransAp *trans )
+{
+ StateAp *fromState = trans->fromState;
+ graph->detachTrans( fromState, trans->toState, trans );
+ graph->attachTrans( fromState, graph->startState, trans );
+}
+
+void LongestMatch::runLonestMatch( ParseData *pd, FsmAp *graph )
+{
+ graph->markReachableFromHereStopFinal( graph->startState );
+ for ( StateList::Iter ms = graph->stateList; ms.lte(); ms++ ) {
+ if ( ms->stateBits & SB_ISMARKED ) {
+ ms->lmItemSet.insert( 0 );
+ ms->stateBits &= ~ SB_ISMARKED;
+ }
+ }
+
+ /* Transfer the first item of non-empty lmAction tables to the item sets
+ * of the states that follow. Exclude states that have no transitions out.
+ * This must happen on a separate pass so that on each iteration of the
+ * next pass we have the item set entries from all lmAction tables. */
+ for ( StateList::Iter st = graph->stateList; st.lte(); st++ ) {
+ for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) {
+ if ( trans->lmActionTable.length() > 0 ) {
+ LmActionTableEl *lmAct = trans->lmActionTable.data;
+ StateAp *toState = trans->toState;
+ assert( toState );
+
+ /* Check if there are transitions out, this may be a very
+ * close approximation? Out transitions going nowhere?
+ * FIXME: Check. */
+ if ( toState->outList.length() > 0 ) {
+ /* Fill the item sets. */
+ graph->markReachableFromHereStopFinal( toState );
+ for ( StateList::Iter ms = graph->stateList; ms.lte(); ms++ ) {
+ if ( ms->stateBits & SB_ISMARKED ) {
+ ms->lmItemSet.insert( lmAct->value );
+ ms->stateBits &= ~ SB_ISMARKED;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* The lmItem sets are now filled, telling us which longest match rules
+ * can succeed in which states. First determine if we need to make sure
+ * act is defaulted to zero. We need to do this if there are any states
+ * with lmItemSet.length() > 1 and NULL is included. That is, that the
+ * switch may get called when in fact nothing has been matched. */
+ int maxItemSetLength = 0;
+ graph->markReachableFromHereStopFinal( graph->startState );
+ for ( StateList::Iter ms = graph->stateList; ms.lte(); ms++ ) {
+ if ( ms->stateBits & SB_ISMARKED ) {
+ if ( ms->lmItemSet.length() > maxItemSetLength )
+ maxItemSetLength = ms->lmItemSet.length();
+ ms->stateBits &= ~ SB_ISMARKED;
+ }
+ }
+
+ /* The actions executed on starting to match a token. */
+ graph->startState->toStateActionTable.setAction( pd->initTokStartOrd, pd->initTokStart );
+ graph->startState->fromStateActionTable.setAction( pd->setTokStartOrd, pd->setTokStart );
+ if ( maxItemSetLength > 1 ) {
+ /* The longest match action switch may be called when tokens are
+ * matched, in which case act must be initialized, there must be a
+ * case to handle the error, and the generated machine will require an
+ * error state. */
+ lmSwitchHandlesError = true;
+ pd->lmRequiresErrorState = true;
+ graph->startState->toStateActionTable.setAction( pd->initActIdOrd, pd->initActId );
+ }
+
+ /* The place to store transitions to restart. It maybe possible for the
+ * restarting to affect the searching through the graph that follows. For
+ * now take the safe route and save the list of transitions to restart
+ * until after all searching is done. */
+ Vector<TransAp*> restartTrans;
+
+ /* Set actions that do immediate token recognition, set the longest match part
+ * id and set the token ending. */
+ for ( StateList::Iter st = graph->stateList; st.lte(); st++ ) {
+ for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) {
+ if ( trans->lmActionTable.length() > 0 ) {
+ LmActionTableEl *lmAct = trans->lmActionTable.data;
+ StateAp *toState = trans->toState;
+ assert( toState );
+
+ /* Check if there are transitions out, this may be a very
+ * close approximation? Out transitions going nowhere?
+ * FIXME: Check. */
+ if ( toState->outList.length() == 0 ) {
+ /* Can execute the immediate action for the longest match
+ * part. Redirect the action to the start state. */
+ trans->actionTable.setAction( lmAct->key,
+ lmAct->value->actOnLast );
+ restartTrans.append( trans );
+ }
+ else {
+ /* Look for non final states that have a non-empty item
+ * set. If these are present then we need to record the
+ * end of the token. Also Find the highest item set
+ * length reachable from here (excluding at transtions to
+ * final states). */
+ bool nonFinalNonEmptyItemSet = false;
+ maxItemSetLength = 0;
+ graph->markReachableFromHereStopFinal( toState );
+ for ( StateList::Iter ms = graph->stateList; ms.lte(); ms++ ) {
+ if ( ms->stateBits & SB_ISMARKED ) {
+ if ( ms->lmItemSet.length() > 0 && !ms->isFinState() )
+ nonFinalNonEmptyItemSet = true;
+ if ( ms->lmItemSet.length() > maxItemSetLength )
+ maxItemSetLength = ms->lmItemSet.length();
+ ms->stateBits &= ~ SB_ISMARKED;
+ }
+ }
+
+ /* If there are reachable states that are not final and
+ * have non empty item sets or that have an item set
+ * length greater than one then we need to set tokend
+ * because the error action that matches the token will
+ * require it. */
+ if ( nonFinalNonEmptyItemSet || maxItemSetLength > 1 )
+ trans->actionTable.setAction( pd->setTokEndOrd, pd->setTokEnd );
+
+ /* Some states may not know which longest match item to
+ * execute, must set it. */
+ if ( maxItemSetLength > 1 ) {
+ /* There are transitions out, another match may come. */
+ trans->actionTable.setAction( lmAct->key,
+ lmAct->value->setActId );
+ }
+ }
+ }
+ }
+ }
+
+ /* Now that all graph searching is done it certainly safe set the
+ * restarting. It may be safe above, however this must be verified. */
+ for ( Vector<TransAp*>::Iter pt = restartTrans; pt.lte(); pt++ )
+ restart( graph, *pt );
+
+ int lmErrActionOrd = pd->curActionOrd++;
+
+ /* Embed the error for recognizing a char. */
+ for ( StateList::Iter st = graph->stateList; st.lte(); st++ ) {
+ if ( st->lmItemSet.length() == 1 && st->lmItemSet[0] != 0 ) {
+ if ( st->isFinState() ) {
+ /* On error execute the onActNext action, which knows that
+ * the last character of the token was one back and restart. */
+ graph->setErrorTarget( st, graph->startState, &lmErrActionOrd,
+ &st->lmItemSet[0]->actOnNext, 1 );
+ }
+ else {
+ graph->setErrorTarget( st, graph->startState, &lmErrActionOrd,
+ &st->lmItemSet[0]->actLagBehind, 1 );
+ }
+ }
+ else if ( st->lmItemSet.length() > 1 ) {
+ /* Need to use the select. Take note of the which items the select
+ * is needed for so only the necessary actions are included. */
+ for ( LmItemSet::Iter plmi = st->lmItemSet; plmi.lte(); plmi++ ) {
+ if ( *plmi != 0 )
+ (*plmi)->inLmSelect = true;
+ }
+ /* On error, execute the action select and go to the start state. */
+ graph->setErrorTarget( st, graph->startState, &lmErrActionOrd,
+ &lmActSelect, 1 );
+ }
+ }
+
+ /* Finally, the start state should be made final. */
+ graph->setFinState( graph->startState );
+}
+
+FsmAp *LongestMatch::walk( ParseData *pd )
+{
+ /* The longest match has it's own name scope. */
+ NameFrame nameFrame = pd->enterNameScope( true, 1 );
+
+ /* Make each part of the longest match. */
+ FsmAp **parts = new FsmAp*[longestMatchList->length()];
+ LmPartList::Iter lmi = *longestMatchList;
+ for ( int i = 0; lmi.lte(); lmi++, i++ ) {
+ /* Create the machine and embed the setting of the longest match id. */
+ parts[i] = lmi->join->walk( pd );
+ parts[i]->longMatchAction( pd->curActionOrd++, lmi );
+ }
+
+ /* Union machines one and up with machine zero. The grammar dictates that
+ * there will always be at least one part. */
+ FsmAp *rtnVal = parts[0];
+ for ( int i = 1; i < longestMatchList->length(); i++ ) {
+ rtnVal->unionOp( parts[i] );
+ afterOpMinimize( rtnVal );
+ }
+
+ runLonestMatch( pd, rtnVal );
+
+ /* Pop the name scope. */
+ pd->popNameScope( nameFrame );
+
+ delete[] parts;
+ return rtnVal;
+}
+
+FsmAp *JoinOrLm::walk( ParseData *pd )
+{
+ FsmAp *rtnVal = 0;
+ switch ( type ) {
+ case JoinType:
+ rtnVal = join->walk( pd );
+ break;
+ case LongestMatchType:
+ rtnVal = longestMatch->walk( pd );
+ break;
+ }
+ return rtnVal;
+}
+
+void JoinOrLm::makeNameTree( ParseData *pd )
+{
+ switch ( type ) {
+ case JoinType:
+ join->makeNameTree( pd );
+ break;
+ case LongestMatchType:
+ longestMatch->makeNameTree( pd );
+ break;
+ }
+}
+
+void JoinOrLm::resolveNameRefs( ParseData *pd )
+{
+ switch ( type ) {
+ case JoinType:
+ join->resolveNameRefs( pd );
+ break;
+ case LongestMatchType:
+ longestMatch->resolveNameRefs( pd );
+ break;
+ }
+}
+
+
+/* Construct with a location and the first expression. */
+Join::Join( const InputLoc &loc, Expression *expr )
+:
+ loc(loc)
+{
+ exprList.append( expr );
+}
+
+/* Construct with a location and the first expression. */
+Join::Join( Expression *expr )
+:
+ loc(loc)
+{
+ exprList.append( expr );
+}
+
+/* Walk an expression node. */
+FsmAp *Join::walk( ParseData *pd )
+{
+ if ( exprList.length() > 1 )
+ return walkJoin( pd );
+ else
+ return exprList.head->walk( pd );
+}
+
+/* There is a list of expressions to join. */
+FsmAp *Join::walkJoin( ParseData *pd )
+{
+ /* We enter into a new name scope. */
+ NameFrame nameFrame = pd->enterNameScope( true, 1 );
+
+ /* Evaluate the machines. */
+ FsmAp **fsms = new FsmAp*[exprList.length()];
+ ExprList::Iter expr = exprList;
+ for ( int e = 0; e < exprList.length(); e++, expr++ )
+ fsms[e] = expr->walk( pd );
+
+ /* Get the start and final names. Final is
+ * guaranteed to exist, start is not. */
+ NameInst *startName = pd->curNameInst->start;
+ NameInst *finalName = pd->curNameInst->final;
+
+ int startId = -1;
+ if ( startName != 0 ) {
+ /* Take note that there was an implicit link to the start machine. */
+ pd->localNameScope->referencedNames.append( startName );
+ startId = startName->id;
+ }
+
+ /* A final id of -1 indicates there is no epsilon that references the
+ * final state, therefor do not create one or set an entry point to it. */
+ int finalId = -1;
+ if ( finalName->numRefs > 0 )
+ finalId = finalName->id;
+
+ /* Join machines 1 and up onto machine 0. */
+ FsmAp *retFsm = fsms[0];
+ retFsm->joinOp( startId, finalId, fsms+1, exprList.length()-1 );
+
+ /* We can now unset entry points that are not longer used. */
+ pd->unsetObsoleteEntries( retFsm );
+
+ /* Pop the name scope. */
+ pd->popNameScope( nameFrame );
+
+ delete[] fsms;
+ return retFsm;
+}
+
+void Join::makeNameTree( ParseData *pd )
+{
+ if ( exprList.length() > 1 ) {
+ /* Create the new anonymous scope. */
+ NameInst *prevNameInst = pd->curNameInst;
+ pd->curNameInst = pd->addNameInst( loc, 0, false );
+
+ /* Join scopes need an implicit "final" target. */
+ pd->curNameInst->final = new NameInst( InputLoc(), pd->curNameInst, "final",
+ pd->nextNameId++, false );
+
+ /* Recurse into all expressions in the list. */
+ for ( ExprList::Iter expr = exprList; expr.lte(); expr++ )
+ expr->makeNameTree( pd );
+
+ /* The name scope ends, pop the name instantiation. */
+ pd->curNameInst = prevNameInst;
+ }
+ else {
+ /* Recurse into the single expression. */
+ exprList.head->makeNameTree( pd );
+ }
+}
+
+
+void Join::resolveNameRefs( ParseData *pd )
+{
+ /* Branch on whether or not there is to be a join. */
+ if ( exprList.length() > 1 ) {
+ /* The variable definition enters a new scope. */
+ NameFrame nameFrame = pd->enterNameScope( true, 1 );
+
+ /* The join scope must contain a start label. */
+ NameSet resolved = pd->resolvePart( pd->localNameScope, "start", true );
+ if ( resolved.length() > 0 ) {
+ /* Take the first. */
+ pd->curNameInst->start = resolved[0];
+ if ( resolved.length() > 1 ) {
+ /* Complain about the multiple references. */
+ error(loc) << "multiple start labels" << endl;
+ errorStateLabels( resolved );
+ }
+ }
+
+ /* Make sure there is a start label. */
+ if ( pd->curNameInst->start != 0 ) {
+ /* There is an implicit reference to start name. */
+ pd->curNameInst->start->numRefs += 1;
+ }
+ else {
+ /* No start label. Complain and recover by adding a label to the
+ * adding one. Recover ignoring the problem. */
+ error(loc) << "no start label" << endl;
+ }
+
+ /* Recurse into all expressions in the list. */
+ for ( ExprList::Iter expr = exprList; expr.lte(); expr++ )
+ expr->resolveNameRefs( pd );
+
+ /* The name scope ends, pop the name instantiation. */
+ pd->popNameScope( nameFrame );
+ }
+ else {
+ /* Recurse into the single expression. */
+ exprList.head->resolveNameRefs( pd );
+ }
+}
+
+/* Clean up after an expression node. */
+Expression::~Expression()
+{
+ switch ( type ) {
+ case OrType: case IntersectType: case SubtractType:
+ case StrongSubtractType:
+ delete expression;
+ delete term;
+ break;
+ case TermType:
+ delete term;
+ break;
+ case BuiltinType:
+ break;
+ }
+}
+
+/* Evaluate a single expression node. */
+FsmAp *Expression::walk( ParseData *pd, bool lastInSeq )
+{
+ FsmAp *rtnVal = 0;
+ switch ( type ) {
+ case OrType: {
+ /* Evaluate the expression. */
+ rtnVal = expression->walk( pd, false );
+ /* Evaluate the term. */
+ FsmAp *rhs = term->walk( pd );
+ /* Perform union. */
+ rtnVal->unionOp( rhs );
+ afterOpMinimize( rtnVal, lastInSeq );
+ break;
+ }
+ case IntersectType: {
+ /* Evaluate the expression. */
+ rtnVal = expression->walk( pd );
+ /* Evaluate the term. */
+ FsmAp *rhs = term->walk( pd );
+ /* Perform intersection. */
+ rtnVal->intersectOp( rhs );
+ afterOpMinimize( rtnVal, lastInSeq );
+ break;
+ }
+ case SubtractType: {
+ /* Evaluate the expression. */
+ rtnVal = expression->walk( pd );
+ /* Evaluate the term. */
+ FsmAp *rhs = term->walk( pd );
+ /* Perform subtraction. */
+ rtnVal->subtractOp( rhs );
+ afterOpMinimize( rtnVal, lastInSeq );
+ break;
+ }
+ case StrongSubtractType: {
+ /* Evaluate the expression. */
+ rtnVal = expression->walk( pd );
+
+ /* Evaluate the term and pad it with any* machines. */
+ FsmAp *rhs = dotStarFsm( pd );
+ FsmAp *termFsm = term->walk( pd );
+ FsmAp *trailAnyStar = dotStarFsm( pd );
+ rhs->concatOp( termFsm );
+ rhs->concatOp( trailAnyStar );
+
+ /* Perform subtraction. */
+ rtnVal->subtractOp( rhs );
+ afterOpMinimize( rtnVal, lastInSeq );
+ break;
+ }
+ case TermType: {
+ /* Return result of the term. */
+ rtnVal = term->walk( pd );
+ break;
+ }
+ case BuiltinType: {
+ /* Duplicate the builtin. */
+ rtnVal = makeBuiltin( builtin, pd );
+ break;
+ }
+ }
+
+ return rtnVal;
+}
+
+void Expression::makeNameTree( ParseData *pd )
+{
+ switch ( type ) {
+ case OrType:
+ case IntersectType:
+ case SubtractType:
+ case StrongSubtractType:
+ expression->makeNameTree( pd );
+ term->makeNameTree( pd );
+ break;
+ case TermType:
+ term->makeNameTree( pd );
+ break;
+ case BuiltinType:
+ break;
+ }
+}
+
+void Expression::resolveNameRefs( ParseData *pd )
+{
+ switch ( type ) {
+ case OrType:
+ case IntersectType:
+ case SubtractType:
+ case StrongSubtractType:
+ expression->resolveNameRefs( pd );
+ term->resolveNameRefs( pd );
+ break;
+ case TermType:
+ term->resolveNameRefs( pd );
+ break;
+ case BuiltinType:
+ break;
+ }
+}
+
+/* Clean up after a term node. */
+Term::~Term()
+{
+ switch ( type ) {
+ case ConcatType:
+ case RightStartType:
+ case RightFinishType:
+ case LeftType:
+ delete term;
+ delete factorWithAug;
+ break;
+ case FactorWithAugType:
+ delete factorWithAug;
+ break;
+ }
+}
+
+/* Evaluate a term node. */
+FsmAp *Term::walk( ParseData *pd, bool lastInSeq )
+{
+ FsmAp *rtnVal = 0;
+ switch ( type ) {
+ case ConcatType: {
+ /* Evaluate the Term. */
+ rtnVal = term->walk( pd, false );
+ /* Evaluate the FactorWithRep. */
+ FsmAp *rhs = factorWithAug->walk( pd );
+ /* Perform concatenation. */
+ rtnVal->concatOp( rhs );
+ afterOpMinimize( rtnVal, lastInSeq );
+ break;
+ }
+ case RightStartType: {
+ /* Evaluate the Term. */
+ rtnVal = term->walk( pd );
+
+ /* Evaluate the FactorWithRep. */
+ FsmAp *rhs = factorWithAug->walk( pd );
+
+ /* Set up the priority descriptors. The left machine gets the
+ * lower priority where as the right get the higher start priority. */
+ priorDescs[0].key = pd->nextPriorKey++;
+ priorDescs[0].priority = 0;
+ rtnVal->allTransPrior( pd->curPriorOrd++, &priorDescs[0] );
+
+ /* The start transitions right machine get the higher priority.
+ * Use the same unique key. */
+ priorDescs[1].key = priorDescs[0].key;
+ priorDescs[1].priority = 1;
+ rhs->startFsmPrior( pd->curPriorOrd++, &priorDescs[1] );
+
+ /* Perform concatenation. */
+ rtnVal->concatOp( rhs );
+ afterOpMinimize( rtnVal, lastInSeq );
+ break;
+ }
+ case RightFinishType: {
+ /* Evaluate the Term. */
+ rtnVal = term->walk( pd );
+
+ /* Evaluate the FactorWithRep. */
+ FsmAp *rhs = factorWithAug->walk( pd );
+
+ /* Set up the priority descriptors. The left machine gets the
+ * lower priority where as the finishing transitions to the right
+ * get the higher priority. */
+ priorDescs[0].key = pd->nextPriorKey++;
+ priorDescs[0].priority = 0;
+ rtnVal->allTransPrior( pd->curPriorOrd++, &priorDescs[0] );
+
+ /* The finishing transitions of the right machine get the higher
+ * priority. Use the same unique key. */
+ priorDescs[1].key = priorDescs[0].key;
+ priorDescs[1].priority = 1;
+ rhs->finishFsmPrior( pd->curPriorOrd++, &priorDescs[1] );
+
+ /* Perform concatenation. */
+ rtnVal->concatOp( rhs );
+ afterOpMinimize( rtnVal, lastInSeq );
+ break;
+ }
+ case LeftType: {
+ /* Evaluate the Term. */
+ rtnVal = term->walk( pd );
+
+ /* Evaluate the FactorWithRep. */
+ FsmAp *rhs = factorWithAug->walk( pd );
+
+ /* Set up the priority descriptors. The left machine gets the
+ * higher priority. */
+ priorDescs[0].key = pd->nextPriorKey++;
+ priorDescs[0].priority = 1;
+ rtnVal->allTransPrior( pd->curPriorOrd++, &priorDescs[0] );
+
+ /* The right machine gets the lower priority. Since
+ * startTransPrior might unnecessarily increase the number of
+ * states during the state machine construction process (due to
+ * isolation), we use allTransPrior instead, which has the same
+ * effect. */
+ priorDescs[1].key = priorDescs[0].key;
+ priorDescs[1].priority = 0;
+ rhs->allTransPrior( pd->curPriorOrd++, &priorDescs[1] );
+
+ /* Perform concatenation. */
+ rtnVal->concatOp( rhs );
+ afterOpMinimize( rtnVal, lastInSeq );
+ break;
+ }
+ case FactorWithAugType: {
+ rtnVal = factorWithAug->walk( pd );
+ break;
+ }
+ }
+ return rtnVal;
+}
+
+void Term::makeNameTree( ParseData *pd )
+{
+ switch ( type ) {
+ case ConcatType:
+ case RightStartType:
+ case RightFinishType:
+ case LeftType:
+ term->makeNameTree( pd );
+ factorWithAug->makeNameTree( pd );
+ break;
+ case FactorWithAugType:
+ factorWithAug->makeNameTree( pd );
+ break;
+ }
+}
+
+void Term::resolveNameRefs( ParseData *pd )
+{
+ switch ( type ) {
+ case ConcatType:
+ case RightStartType:
+ case RightFinishType:
+ case LeftType:
+ term->resolveNameRefs( pd );
+ factorWithAug->resolveNameRefs( pd );
+ break;
+ case FactorWithAugType:
+ factorWithAug->resolveNameRefs( pd );
+ break;
+ }
+}
+
+/* Clean up after a factor with augmentation node. */
+FactorWithAug::~FactorWithAug()
+{
+ delete factorWithRep;
+
+ /* Walk the vector of parser actions, deleting function names. */
+
+ /* Clean up priority descriptors. */
+ if ( priorDescs != 0 )
+ delete[] priorDescs;
+}
+
+void FactorWithAug::assignActions( ParseData *pd, FsmAp *graph, int *actionOrd )
+{
+ /* Assign actions. */
+ for ( int i = 0; i < actions.length(); i++ ) {
+ switch ( actions[i].type ) {
+ /* Transition actions. */
+ case at_start:
+ graph->startFsmAction( actionOrd[i], actions[i].action );
+ afterOpMinimize( graph );
+ break;
+ case at_all:
+ graph->allTransAction( actionOrd[i], actions[i].action );
+ break;
+ case at_finish:
+ graph->finishFsmAction( actionOrd[i], actions[i].action );
+ break;
+ case at_leave:
+ graph->leaveFsmAction( actionOrd[i], actions[i].action );
+ break;
+
+ /* Global error actions. */
+ case at_start_gbl_error:
+ graph->startErrorAction( actionOrd[i], actions[i].action, 0 );
+ afterOpMinimize( graph );
+ break;
+ case at_all_gbl_error:
+ graph->allErrorAction( actionOrd[i], actions[i].action, 0 );
+ break;
+ case at_final_gbl_error:
+ graph->finalErrorAction( actionOrd[i], actions[i].action, 0 );
+ break;
+ case at_not_start_gbl_error:
+ graph->notStartErrorAction( actionOrd[i], actions[i].action, 0 );
+ break;
+ case at_not_final_gbl_error:
+ graph->notFinalErrorAction( actionOrd[i], actions[i].action, 0 );
+ break;
+ case at_middle_gbl_error:
+ graph->middleErrorAction( actionOrd[i], actions[i].action, 0 );
+ break;
+
+ /* Local error actions. */
+ case at_start_local_error:
+ graph->startErrorAction( actionOrd[i], actions[i].action,
+ actions[i].localErrKey );
+ afterOpMinimize( graph );
+ break;
+ case at_all_local_error:
+ graph->allErrorAction( actionOrd[i], actions[i].action,
+ actions[i].localErrKey );
+ break;
+ case at_final_local_error:
+ graph->finalErrorAction( actionOrd[i], actions[i].action,
+ actions[i].localErrKey );
+ break;
+ case at_not_start_local_error:
+ graph->notStartErrorAction( actionOrd[i], actions[i].action,
+ actions[i].localErrKey );
+ break;
+ case at_not_final_local_error:
+ graph->notFinalErrorAction( actionOrd[i], actions[i].action,
+ actions[i].localErrKey );
+ break;
+ case at_middle_local_error:
+ graph->middleErrorAction( actionOrd[i], actions[i].action,
+ actions[i].localErrKey );
+ break;
+
+ /* EOF actions. */
+ case at_start_eof:
+ graph->startEOFAction( actionOrd[i], actions[i].action );
+ afterOpMinimize( graph );
+ break;
+ case at_all_eof:
+ graph->allEOFAction( actionOrd[i], actions[i].action );
+ break;
+ case at_final_eof:
+ graph->finalEOFAction( actionOrd[i], actions[i].action );
+ break;
+ case at_not_start_eof:
+ graph->notStartEOFAction( actionOrd[i], actions[i].action );
+ break;
+ case at_not_final_eof:
+ graph->notFinalEOFAction( actionOrd[i], actions[i].action );
+ break;
+ case at_middle_eof:
+ graph->middleEOFAction( actionOrd[i], actions[i].action );
+ break;
+
+ /* To State Actions. */
+ case at_start_to_state:
+ graph->startToStateAction( actionOrd[i], actions[i].action );
+ afterOpMinimize( graph );
+ break;
+ case at_all_to_state:
+ graph->allToStateAction( actionOrd[i], actions[i].action );
+ break;
+ case at_final_to_state:
+ graph->finalToStateAction( actionOrd[i], actions[i].action );
+ break;
+ case at_not_start_to_state:
+ graph->notStartToStateAction( actionOrd[i], actions[i].action );
+ break;
+ case at_not_final_to_state:
+ graph->notFinalToStateAction( actionOrd[i], actions[i].action );
+ break;
+ case at_middle_to_state:
+ graph->middleToStateAction( actionOrd[i], actions[i].action );
+ break;
+
+ /* From State Actions. */
+ case at_start_from_state:
+ graph->startFromStateAction( actionOrd[i], actions[i].action );
+ afterOpMinimize( graph );
+ break;
+ case at_all_from_state:
+ graph->allFromStateAction( actionOrd[i], actions[i].action );
+ break;
+ case at_final_from_state:
+ graph->finalFromStateAction( actionOrd[i], actions[i].action );
+ break;
+ case at_not_start_from_state:
+ graph->notStartFromStateAction( actionOrd[i], actions[i].action );
+ break;
+ case at_not_final_from_state:
+ graph->notFinalFromStateAction( actionOrd[i], actions[i].action );
+ break;
+ case at_middle_from_state:
+ graph->middleFromStateAction( actionOrd[i], actions[i].action );
+ break;
+
+ /* Remaining cases, prevented by the parser. */
+ default:
+ assert( false );
+ break;
+ }
+ }
+}
+
+void FactorWithAug::assignPriorities( FsmAp *graph, int *priorOrd )
+{
+ /* Assign priorities. */
+ for ( int i = 0; i < priorityAugs.length(); i++ ) {
+ switch ( priorityAugs[i].type ) {
+ case at_start:
+ graph->startFsmPrior( priorOrd[i], &priorDescs[i]);
+ /* Start fsm priorities are a special case that may require
+ * minimization afterwards. */
+ afterOpMinimize( graph );
+ break;
+ case at_all:
+ graph->allTransPrior( priorOrd[i], &priorDescs[i] );
+ break;
+ case at_finish:
+ graph->finishFsmPrior( priorOrd[i], &priorDescs[i] );
+ break;
+ case at_leave:
+ graph->leaveFsmPrior( priorOrd[i], &priorDescs[i] );
+ break;
+
+ default:
+ /* Parser Prevents this case. */
+ break;
+ }
+ }
+}
+
+void FactorWithAug::assignConditions( FsmAp *graph )
+{
+ for ( int i = 0; i < conditions.length(); i++ ) {
+ switch ( conditions[i].type ) {
+ /* Transition actions. */
+ case at_start:
+ graph->startFsmCondition( conditions[i].action );
+ afterOpMinimize( graph );
+ break;
+ case at_all:
+ graph->allTransCondition( conditions[i].action );
+ break;
+ case at_leave:
+ graph->leaveFsmCondition( conditions[i].action );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+/* Evaluate a factor with augmentation node. */
+FsmAp *FactorWithAug::walk( ParseData *pd )
+{
+ /* Enter into the scopes created for the labels. */
+ NameFrame nameFrame = pd->enterNameScope( false, labels.length() );
+
+ /* Make the array of function orderings. */
+ int *actionOrd = 0;
+ if ( actions.length() > 0 )
+ actionOrd = new int[actions.length()];
+
+ /* First walk the list of actions, assigning order to all starting
+ * actions. */
+ for ( int i = 0; i < actions.length(); i++ ) {
+ if ( actions[i].type == at_start ||
+ actions[i].type == at_start_gbl_error ||
+ actions[i].type == at_start_local_error ||
+ actions[i].type == at_start_to_state ||
+ actions[i].type == at_start_from_state ||
+ actions[i].type == at_start_eof )
+ actionOrd[i] = pd->curActionOrd++;
+ }
+
+ /* Evaluate the factor with repetition. */
+ FsmAp *rtnVal = factorWithRep->walk( pd );
+
+ /* Compute the remaining action orderings. */
+ for ( int i = 0; i < actions.length(); i++ ) {
+ if ( actions[i].type != at_start &&
+ actions[i].type != at_start_gbl_error &&
+ actions[i].type != at_start_local_error &&
+ actions[i].type != at_start_to_state &&
+ actions[i].type != at_start_from_state &&
+ actions[i].type != at_start_eof )
+ actionOrd[i] = pd->curActionOrd++;
+ }
+
+ /* Embed conditions. */
+ assignConditions( rtnVal );
+
+ /* Embed actions. */
+ assignActions( pd, rtnVal , actionOrd );
+
+ /* Make the array of priority orderings. Orderings are local to this walk
+ * of the factor with augmentation. */
+ int *priorOrd = 0;
+ if ( priorityAugs.length() > 0 )
+ priorOrd = new int[priorityAugs.length()];
+
+ /* Walk all priorities, assigning the priority ordering. */
+ for ( int i = 0; i < priorityAugs.length(); i++ )
+ priorOrd[i] = pd->curPriorOrd++;
+
+ /* If the priority descriptors have not been made, make them now. Make
+ * priority descriptors for each priority asignment that will be passed to
+ * the fsm. Used to keep track of the key, value and used bit. */
+ if ( priorDescs == 0 && priorityAugs.length() > 0 ) {
+ priorDescs = new PriorDesc[priorityAugs.length()];
+ for ( int i = 0; i < priorityAugs.length(); i++ ) {
+ /* Init the prior descriptor for the priority setting. */
+ priorDescs[i].key = priorityAugs[i].priorKey;
+ priorDescs[i].priority = priorityAugs[i].priorValue;
+ }
+ }
+
+ /* Assign priorities into the machine. */
+ assignPriorities( rtnVal, priorOrd );
+
+ /* Assign epsilon transitions. */
+ for ( int e = 0; e < epsilonLinks.length(); e++ ) {
+ /* Get the name, which may not exist. If it doesn't then silently
+ * ignore it because an error has already been reported. */
+ NameInst *epTarg = pd->epsilonResolvedLinks[pd->nextEpsilonResolvedLink++];
+ if ( epTarg != 0 ) {
+ /* Make the epsilon transitions. */
+ rtnVal->epsilonTrans( epTarg->id );
+
+ /* Note that we have made a link to the name. */
+ pd->localNameScope->referencedNames.append( epTarg );
+ }
+ }
+
+ /* Set entry points for labels. */
+ if ( labels.length() > 0 ) {
+ /* Pop the names. */
+ pd->resetNameScope( nameFrame );
+
+ /* Make labels that are referenced into entry points. */
+ for ( int i = 0; i < labels.length(); i++ ) {
+ pd->enterNameScope( false, 1 );
+
+ /* Will always be found. */
+ NameInst *name = pd->curNameInst;
+
+ /* If the name is referenced then set the entry point. */
+ if ( name->numRefs > 0 )
+ rtnVal->setEntry( name->id, rtnVal->startState );
+ }
+
+ pd->popNameScope( nameFrame );
+ }
+
+ if ( priorOrd != 0 )
+ delete[] priorOrd;
+ if ( actionOrd != 0 )
+ delete[] actionOrd;
+ return rtnVal;
+}
+
+void FactorWithAug::makeNameTree( ParseData *pd )
+{
+ /* Add the labels to the tree of instantiated names. Each label
+ * makes a new scope. */
+ NameInst *prevNameInst = pd->curNameInst;
+ for ( int i = 0; i < labels.length(); i++ )
+ pd->curNameInst = pd->addNameInst( labels[i].loc, labels[i].data, true );
+
+ /* Recurse, then pop the names. */
+ factorWithRep->makeNameTree( pd );
+ pd->curNameInst = prevNameInst;
+}
+
+
+void FactorWithAug::resolveNameRefs( ParseData *pd )
+{
+ /* Enter into the name scope created by any labels. */
+ NameFrame nameFrame = pd->enterNameScope( false, labels.length() );
+
+ /* Note action references. */
+ for ( int i = 0; i < actions.length(); i++ )
+ actions[i].action->actionRefs.append( pd->localNameScope );
+
+ /* Recurse first. IMPORTANT: we must do the exact same traversal as when
+ * the tree is constructed. */
+ factorWithRep->resolveNameRefs( pd );
+
+ /* Resolve epsilon transitions. */
+ for ( int ep = 0; ep < epsilonLinks.length(); ep++ ) {
+ /* Get the link. */
+ EpsilonLink &link = epsilonLinks[ep];
+ NameInst *resolvedName = 0;
+
+ if ( link.target.length() == 1 && strcmp( link.target.data[0], "final" ) == 0 ) {
+ /* Epsilon drawn to an implicit final state. An implicit final is
+ * only available in join operations. */
+ resolvedName = pd->localNameScope->final;
+ }
+ else {
+ /* Do an search for the name. */
+ NameSet resolved;
+ pd->resolveFrom( resolved, pd->localNameScope, link.target, 0 );
+ if ( resolved.length() > 0 ) {
+ /* Take the first one. */
+ resolvedName = resolved[0];
+ if ( resolved.length() > 1 ) {
+ /* Complain about the multiple references. */
+ error(link.loc) << "state reference " << link.target <<
+ " resolves to multiple entry points" << endl;
+ errorStateLabels( resolved );
+ }
+ }
+ }
+
+ /* This is tricky, we stuff resolved epsilon transitions into one long
+ * vector in the parse data structure. Since the name resolution and
+ * graph generation both do identical walks of the parse tree we
+ * should always find the link resolutions in the right place. */
+ pd->epsilonResolvedLinks.append( resolvedName );
+
+ if ( resolvedName != 0 ) {
+ /* Found the name, bump of the reference count on it. */
+ resolvedName->numRefs += 1;
+ }
+ else {
+ /* Complain, no recovery action, the epsilon op will ignore any
+ * epsilon transitions whose names did not resolve. */
+ error(link.loc) << "could not resolve label " << link.target << endl;
+ }
+ }
+
+ if ( labels.length() > 0 )
+ pd->popNameScope( nameFrame );
+}
+
+
+/* Clean up after a factor with repetition node. */
+FactorWithRep::~FactorWithRep()
+{
+ switch ( type ) {
+ case StarType: case StarStarType: case OptionalType: case PlusType:
+ case ExactType: case MaxType: case MinType: case RangeType:
+ delete factorWithRep;
+ break;
+ case FactorWithNegType:
+ delete factorWithNeg;
+ break;
+ }
+}
+
+/* Evaluate a factor with repetition node. */
+FsmAp *FactorWithRep::walk( ParseData *pd )
+{
+ FsmAp *retFsm = 0;
+
+ switch ( type ) {
+ case StarType: {
+ /* Evaluate the FactorWithRep. */
+ retFsm = factorWithRep->walk( pd );
+ if ( retFsm->startState->isFinState() ) {
+ warning(loc) << "applying kleene star to a machine that "
+ "accepts zero length word" << endl;
+ }
+
+ /* Shift over the start action orders then do the kleene star. */
+ pd->curActionOrd += retFsm->shiftStartActionOrder( pd->curActionOrd );
+ retFsm->starOp( );
+ afterOpMinimize( retFsm );
+ break;
+ }
+ case StarStarType: {
+ /* Evaluate the FactorWithRep. */
+ retFsm = factorWithRep->walk( pd );
+ if ( retFsm->startState->isFinState() ) {
+ warning(loc) << "applying kleene star to a machine that "
+ "accepts zero length word" << endl;
+ }
+
+ /* Set up the prior descs. All gets priority one, whereas leaving gets
+ * priority zero. Make a unique key so that these priorities don't
+ * interfere with any priorities set by the user. */
+ priorDescs[0].key = pd->nextPriorKey++;
+ priorDescs[0].priority = 1;
+ retFsm->allTransPrior( pd->curPriorOrd++, &priorDescs[0] );
+
+ /* Leaveing gets priority 0. Use same unique key. */
+ priorDescs[1].key = priorDescs[0].key;
+ priorDescs[1].priority = 0;
+ retFsm->leaveFsmPrior( pd->curPriorOrd++, &priorDescs[1] );
+
+ /* Shift over the start action orders then do the kleene star. */
+ pd->curActionOrd += retFsm->shiftStartActionOrder( pd->curActionOrd );
+ retFsm->starOp( );
+ afterOpMinimize( retFsm );
+ break;
+ }
+ case OptionalType: {
+ /* Make the null fsm. */
+ FsmAp *nu = new FsmAp();
+ nu->lambdaFsm( );
+
+ /* Evaluate the FactorWithRep. */
+ retFsm = factorWithRep->walk( pd );
+
+ /* Perform the question operator. */
+ retFsm->unionOp( nu );
+ afterOpMinimize( retFsm );
+ break;
+ }
+ case PlusType: {
+ /* Evaluate the FactorWithRep. */
+ retFsm = factorWithRep->walk( pd );
+ if ( retFsm->startState->isFinState() ) {
+ warning(loc) << "applying plus operator to a machine that "
+ "accpets zero length word" << endl;
+ }
+
+ /* Need a duplicated for the star end. */
+ FsmAp *dup = new FsmAp( *retFsm );
+
+ /* The start func orders need to be shifted before doing the star. */
+ pd->curActionOrd += dup->shiftStartActionOrder( pd->curActionOrd );
+
+ /* Star the duplicate. */
+ dup->starOp( );
+ afterOpMinimize( dup );
+
+ retFsm->concatOp( dup );
+ afterOpMinimize( retFsm );
+ break;
+ }
+ case ExactType: {
+ /* Get an int from the repetition amount. */
+ if ( lowerRep == 0 ) {
+ /* No copies. Don't need to evaluate the factorWithRep.
+ * This Defeats the purpose so give a warning. */
+ warning(loc) << "exactly zero repetitions results "
+ "in the null machine" << endl;
+
+ retFsm = new FsmAp();
+ retFsm->lambdaFsm();
+ }
+ else {
+ /* Evaluate the first FactorWithRep. */
+ retFsm = factorWithRep->walk( pd );
+ if ( retFsm->startState->isFinState() ) {
+ warning(loc) << "applying repetition to a machine that "
+ "accepts zero length word" << endl;
+ }
+
+ /* The start func orders need to be shifted before doing the
+ * repetition. */
+ pd->curActionOrd += retFsm->shiftStartActionOrder( pd->curActionOrd );
+
+ /* Do the repetition on the machine. Already guarded against n == 0 */
+ retFsm->repeatOp( lowerRep );
+ afterOpMinimize( retFsm );
+ }
+ break;
+ }
+ case MaxType: {
+ /* Get an int from the repetition amount. */
+ if ( upperRep == 0 ) {
+ /* No copies. Don't need to evaluate the factorWithRep.
+ * This Defeats the purpose so give a warning. */
+ warning(loc) << "max zero repetitions results "
+ "in the null machine" << endl;
+
+ retFsm = new FsmAp();
+ retFsm->lambdaFsm();
+ }
+ else {
+ /* Evaluate the first FactorWithRep. */
+ retFsm = factorWithRep->walk( pd );
+ if ( retFsm->startState->isFinState() ) {
+ warning(loc) << "applying max repetition to a machine that "
+ "accepts zero length word" << endl;
+ }
+
+ /* The start func orders need to be shifted before doing the
+ * repetition. */
+ pd->curActionOrd += retFsm->shiftStartActionOrder( pd->curActionOrd );
+
+ /* Do the repetition on the machine. Already guarded against n == 0 */
+ retFsm->optionalRepeatOp( upperRep );
+ afterOpMinimize( retFsm );
+ }
+ break;
+ }
+ case MinType: {
+ /* Evaluate the repeated machine. */
+ retFsm = factorWithRep->walk( pd );
+ if ( retFsm->startState->isFinState() ) {
+ warning(loc) << "applying min repetition to a machine that "
+ "accepts zero length word" << endl;
+ }
+
+ /* The start func orders need to be shifted before doing the repetition
+ * and the kleene star. */
+ pd->curActionOrd += retFsm->shiftStartActionOrder( pd->curActionOrd );
+
+ if ( lowerRep == 0 ) {
+ /* Acts just like a star op on the machine to return. */
+ retFsm->starOp( );
+ afterOpMinimize( retFsm );
+ }
+ else {
+ /* Take a duplicate for the plus. */
+ FsmAp *dup = new FsmAp( *retFsm );
+
+ /* Do repetition on the first half. */
+ retFsm->repeatOp( lowerRep );
+ afterOpMinimize( retFsm );
+
+ /* Star the duplicate. */
+ dup->starOp( );
+ afterOpMinimize( dup );
+
+ /* Tak on the kleene star. */
+ retFsm->concatOp( dup );
+ afterOpMinimize( retFsm );
+ }
+ break;
+ }
+ case RangeType: {
+ /* Check for bogus range. */
+ if ( upperRep - lowerRep < 0 ) {
+ error(loc) << "invalid range repetition" << endl;
+
+ /* Return null machine as recovery. */
+ retFsm = new FsmAp();
+ retFsm->lambdaFsm();
+ }
+ else if ( lowerRep == 0 && upperRep == 0 ) {
+ /* No copies. Don't need to evaluate the factorWithRep. This
+ * defeats the purpose so give a warning. */
+ warning(loc) << "zero to zero repetitions results "
+ "in the null machine" << endl;
+
+ retFsm = new FsmAp();
+ retFsm->lambdaFsm();
+ }
+ else {
+ /* Now need to evaluate the repeated machine. */
+ retFsm = factorWithRep->walk( pd );
+ if ( retFsm->startState->isFinState() ) {
+ warning(loc) << "applying range repetition to a machine that "
+ "accepts zero length word" << endl;
+ }
+
+ /* The start func orders need to be shifted before doing both kinds
+ * of repetition. */
+ pd->curActionOrd += retFsm->shiftStartActionOrder( pd->curActionOrd );
+
+ if ( lowerRep == 0 ) {
+ /* Just doing max repetition. Already guarded against n == 0. */
+ retFsm->optionalRepeatOp( upperRep );
+ afterOpMinimize( retFsm );
+ }
+ else if ( lowerRep == upperRep ) {
+ /* Just doing exact repetition. Already guarded against n == 0. */
+ retFsm->repeatOp( lowerRep );
+ afterOpMinimize( retFsm );
+ }
+ else {
+ /* This is the case that 0 < lowerRep < upperRep. Take a
+ * duplicate for the optional repeat. */
+ FsmAp *dup = new FsmAp( *retFsm );
+
+ /* Do repetition on the first half. */
+ retFsm->repeatOp( lowerRep );
+ afterOpMinimize( retFsm );
+
+ /* Do optional repetition on the second half. */
+ dup->optionalRepeatOp( upperRep - lowerRep );
+ afterOpMinimize( dup );
+
+ /* Tak on the duplicate machine. */
+ retFsm->concatOp( dup );
+ afterOpMinimize( retFsm );
+ }
+ }
+ break;
+ }
+ case FactorWithNegType: {
+ /* Evaluate the Factor. Pass it up. */
+ retFsm = factorWithNeg->walk( pd );
+ break;
+ }}
+ return retFsm;
+}
+
+void FactorWithRep::makeNameTree( ParseData *pd )
+{
+ switch ( type ) {
+ case StarType:
+ case StarStarType:
+ case OptionalType:
+ case PlusType:
+ case ExactType:
+ case MaxType:
+ case MinType:
+ case RangeType:
+ factorWithRep->makeNameTree( pd );
+ break;
+ case FactorWithNegType:
+ factorWithNeg->makeNameTree( pd );
+ break;
+ }
+}
+
+void FactorWithRep::resolveNameRefs( ParseData *pd )
+{
+ switch ( type ) {
+ case StarType:
+ case StarStarType:
+ case OptionalType:
+ case PlusType:
+ case ExactType:
+ case MaxType:
+ case MinType:
+ case RangeType:
+ factorWithRep->resolveNameRefs( pd );
+ break;
+ case FactorWithNegType:
+ factorWithNeg->resolveNameRefs( pd );
+ break;
+ }
+}
+
+/* Clean up after a factor with negation node. */
+FactorWithNeg::~FactorWithNeg()
+{
+ switch ( type ) {
+ case NegateType:
+ case CharNegateType:
+ delete factorWithNeg;
+ break;
+ case FactorType:
+ delete factor;
+ break;
+ }
+}
+
+/* Evaluate a factor with negation node. */
+FsmAp *FactorWithNeg::walk( ParseData *pd )
+{
+ FsmAp *retFsm = 0;
+
+ switch ( type ) {
+ case NegateType: {
+ /* Evaluate the factorWithNeg. */
+ FsmAp *toNegate = factorWithNeg->walk( pd );
+
+ /* Negation is subtract from dot-star. */
+ retFsm = dotStarFsm( pd );
+ retFsm->subtractOp( toNegate );
+ afterOpMinimize( retFsm );
+ break;
+ }
+ case CharNegateType: {
+ /* Evaluate the factorWithNeg. */
+ FsmAp *toNegate = factorWithNeg->walk( pd );
+
+ /* CharNegation is subtract from dot. */
+ retFsm = dotFsm( pd );
+ retFsm->subtractOp( toNegate );
+ afterOpMinimize( retFsm );
+ break;
+ }
+ case FactorType: {
+ /* Evaluate the Factor. Pass it up. */
+ retFsm = factor->walk( pd );
+ break;
+ }}
+ return retFsm;
+}
+
+void FactorWithNeg::makeNameTree( ParseData *pd )
+{
+ switch ( type ) {
+ case NegateType:
+ case CharNegateType:
+ factorWithNeg->makeNameTree( pd );
+ break;
+ case FactorType:
+ factor->makeNameTree( pd );
+ break;
+ }
+}
+
+void FactorWithNeg::resolveNameRefs( ParseData *pd )
+{
+ switch ( type ) {
+ case NegateType:
+ case CharNegateType:
+ factorWithNeg->resolveNameRefs( pd );
+ break;
+ case FactorType:
+ factor->resolveNameRefs( pd );
+ break;
+ }
+}
+
+/* Clean up after a factor node. */
+Factor::~Factor()
+{
+ switch ( type ) {
+ case LiteralType:
+ delete literal;
+ break;
+ case RangeType:
+ delete range;
+ break;
+ case OrExprType:
+ delete reItem;
+ break;
+ case RegExprType:
+ delete regExpr;
+ break;
+ case ReferenceType:
+ break;
+ case ParenType:
+ delete join;
+ break;
+ case LongestMatchType:
+ delete longestMatch;
+ break;
+ }
+}
+
+/* Evaluate a factor node. */
+FsmAp *Factor::walk( ParseData *pd )
+{
+ FsmAp *rtnVal = 0;
+ switch ( type ) {
+ case LiteralType:
+ rtnVal = literal->walk( pd );
+ break;
+ case RangeType:
+ rtnVal = range->walk( pd );
+ break;
+ case OrExprType:
+ rtnVal = reItem->walk( pd, 0 );
+ break;
+ case RegExprType:
+ rtnVal = regExpr->walk( pd, 0 );
+ break;
+ case ReferenceType:
+ rtnVal = varDef->walk( pd );
+ break;
+ case ParenType:
+ rtnVal = join->walk( pd );
+ break;
+ case LongestMatchType:
+ rtnVal = longestMatch->walk( pd );
+ break;
+ }
+
+ return rtnVal;
+}
+
+void Factor::makeNameTree( ParseData *pd )
+{
+ switch ( type ) {
+ case LiteralType:
+ case RangeType:
+ case OrExprType:
+ case RegExprType:
+ break;
+ case ReferenceType:
+ varDef->makeNameTree( loc, pd );
+ break;
+ case ParenType:
+ join->makeNameTree( pd );
+ break;
+ case LongestMatchType:
+ longestMatch->makeNameTree( pd );
+ break;
+ }
+}
+
+void Factor::resolveNameRefs( ParseData *pd )
+{
+ switch ( type ) {
+ case LiteralType:
+ case RangeType:
+ case OrExprType:
+ case RegExprType:
+ break;
+ case ReferenceType:
+ varDef->resolveNameRefs( pd );
+ break;
+ case ParenType:
+ join->resolveNameRefs( pd );
+ break;
+ case LongestMatchType:
+ longestMatch->resolveNameRefs( pd );
+ break;
+ }
+}
+
+/* Clean up a range object. Must delete the two literals. */
+Range::~Range()
+{
+ delete lowerLit;
+ delete upperLit;
+}
+
+/* Evaluate a range. Gets the lower an upper key and makes an fsm range. */
+FsmAp *Range::walk( ParseData *pd )
+{
+ /* Construct and verify the suitability of the lower end of the range. */
+ FsmAp *lowerFsm = lowerLit->walk( pd );
+ if ( !lowerFsm->checkSingleCharMachine() ) {
+ error(lowerLit->token.loc) <<
+ "bad range lower end, must be a single character" << endl;
+ }
+
+ /* Construct and verify the upper end. */
+ FsmAp *upperFsm = upperLit->walk( pd );
+ if ( !upperFsm->checkSingleCharMachine() ) {
+ error(upperLit->token.loc) <<
+ "bad range upper end, must be a single character" << endl;
+ }
+
+ /* Grab the keys from the machines, then delete them. */
+ Key lowKey = lowerFsm->startState->outList.head->lowKey;
+ Key highKey = upperFsm->startState->outList.head->lowKey;
+ delete lowerFsm;
+ delete upperFsm;
+
+ /* Validate the range. */
+ if ( lowKey > highKey ) {
+ /* Recover by setting upper to lower; */
+ error(lowerLit->token.loc) << "lower end of range is greater then upper end" << endl;
+ highKey = lowKey;
+ }
+
+ /* Return the range now that it is validated. */
+ FsmAp *retFsm = new FsmAp();
+ retFsm->rangeFsm( lowKey, highKey );
+ return retFsm;
+}
+
+/* Evaluate a literal object. */
+FsmAp *Literal::walk( ParseData *pd )
+{
+ /* FsmAp to return, is the alphabet signed. */
+ FsmAp *rtnVal = 0;
+
+ switch ( type ) {
+ case Number: {
+ /* Make the fsm key in int format. */
+ Key fsmKey = makeFsmKeyNum( token.data, token.loc, pd );
+ /* Make the new machine. */
+ rtnVal = new FsmAp();
+ rtnVal->concatFsm( fsmKey );
+ break;
+ }
+ case LitString: {
+ /* Make the array of keys in int format. */
+ Token interp;
+ bool caseInsensitive;
+ token.prepareLitString( interp, caseInsensitive );
+ Key *arr = new Key[interp.length];
+ makeFsmKeyArray( arr, interp.data, interp.length, pd );
+
+ /* Make the new machine. */
+ rtnVal = new FsmAp();
+ if ( caseInsensitive )
+ rtnVal->concatFsmCI( arr, interp.length );
+ else
+ rtnVal->concatFsm( arr, interp.length );
+ delete[] interp.data;
+ delete[] arr;
+ break;
+ }}
+ return rtnVal;
+}
+
+/* Clean up after a regular expression object. */
+RegExpr::~RegExpr()
+{
+ switch ( type ) {
+ case RecurseItem:
+ delete regExpr;
+ delete item;
+ break;
+ case Empty:
+ break;
+ }
+}
+
+/* Evaluate a regular expression object. */
+FsmAp *RegExpr::walk( ParseData *pd, RegExpr *rootRegex )
+{
+ /* This is the root regex, pass down a pointer to this. */
+ if ( rootRegex == 0 )
+ rootRegex = this;
+
+ FsmAp *rtnVal = 0;
+ switch ( type ) {
+ case RecurseItem: {
+ /* Walk both items. */
+ rtnVal = regExpr->walk( pd, rootRegex );
+ FsmAp *fsm2 = item->walk( pd, rootRegex );
+ rtnVal->concatOp( fsm2 );
+ break;
+ }
+ case Empty: {
+ rtnVal = new FsmAp();
+ rtnVal->lambdaFsm();
+ break;
+ }
+ }
+ return rtnVal;
+}
+
+/* Clean up after an item in a regular expression. */
+ReItem::~ReItem()
+{
+ switch ( type ) {
+ case Data:
+ case Dot:
+ break;
+ case OrBlock:
+ case NegOrBlock:
+ delete orBlock;
+ break;
+ }
+}
+
+/* Evaluate a regular expression object. */
+FsmAp *ReItem::walk( ParseData *pd, RegExpr *rootRegex )
+{
+ /* The fsm to return, is the alphabet signed? */
+ FsmAp *rtnVal = 0;
+
+ switch ( type ) {
+ case Data: {
+ /* Move the data into an integer array and make a concat fsm. */
+ Key *arr = new Key[token.length];
+ makeFsmKeyArray( arr, token.data, token.length, pd );
+
+ /* Make the concat fsm. */
+ rtnVal = new FsmAp();
+ if ( rootRegex != 0 && rootRegex->caseInsensitive )
+ rtnVal->concatFsmCI( arr, token.length );
+ else
+ rtnVal->concatFsm( arr, token.length );
+ delete[] arr;
+ break;
+ }
+ case Dot: {
+ /* Make the dot fsm. */
+ rtnVal = dotFsm( pd );
+ break;
+ }
+ case OrBlock: {
+ /* Get the or block and minmize it. */
+ rtnVal = orBlock->walk( pd, rootRegex );
+ if ( rtnVal == 0 ) {
+ rtnVal = new FsmAp();
+ rtnVal->lambdaFsm();
+ }
+ rtnVal->minimizePartition2();
+ break;
+ }
+ case NegOrBlock: {
+ /* Get the or block and minimize it. */
+ FsmAp *fsm = orBlock->walk( pd, rootRegex );
+ fsm->minimizePartition2();
+
+ /* Make a dot fsm and subtract from it. */
+ rtnVal = dotFsm( pd );
+ rtnVal->subtractOp( fsm );
+ rtnVal->minimizePartition2();
+ break;
+ }
+ }
+
+ /* If the item is followed by a star, then apply the star op. */
+ if ( star ) {
+ if ( rtnVal->startState->isFinState() ) {
+ warning(loc) << "applying kleene star to a machine that "
+ "accpets zero length word" << endl;
+ }
+
+ rtnVal->starOp();
+ rtnVal->minimizePartition2();
+ }
+ return rtnVal;
+}
+
+/* Clean up after an or block of a regular expression. */
+ReOrBlock::~ReOrBlock()
+{
+ switch ( type ) {
+ case RecurseItem:
+ delete orBlock;
+ delete item;
+ break;
+ case Empty:
+ break;
+ }
+}
+
+
+/* Evaluate an or block of a regular expression. */
+FsmAp *ReOrBlock::walk( ParseData *pd, RegExpr *rootRegex )
+{
+ FsmAp *rtnVal = 0;
+ switch ( type ) {
+ case RecurseItem: {
+ /* Evaluate the two fsm. */
+ FsmAp *fsm1 = orBlock->walk( pd, rootRegex );
+ FsmAp *fsm2 = item->walk( pd, rootRegex );
+ if ( fsm1 == 0 )
+ rtnVal = fsm2;
+ else {
+ fsm1->unionOp( fsm2 );
+ rtnVal = fsm1;
+ }
+ break;
+ }
+ case Empty: {
+ rtnVal = 0;
+ break;
+ }
+ }
+ return rtnVal;;
+}
+
+/* Evaluate an or block item of a regular expression. */
+FsmAp *ReOrItem::walk( ParseData *pd, RegExpr *rootRegex )
+{
+ /* The return value, is the alphabet signed? */
+ FsmAp *rtnVal = 0;
+ switch ( type ) {
+ case Data: {
+ /* Make the or machine. */
+ rtnVal = new FsmAp();
+
+ /* Put the or data into an array of ints. Note that we find unique
+ * keys. Duplicates are silently ignored. The alternative would be to
+ * issue warning or an error but since we can't with [a0-9a] or 'a' |
+ * 'a' don't bother here. */
+ KeySet keySet;
+ makeFsmUniqueKeyArray( keySet, token.data, token.length,
+ rootRegex != 0 ? rootRegex->caseInsensitive : false, pd );
+
+ /* Run the or operator. */
+ rtnVal->orFsm( keySet.data, keySet.length() );
+ break;
+ }
+ case Range: {
+ /* Make the upper and lower keys. */
+ Key lowKey = makeFsmKeyChar( lower, pd );
+ Key highKey = makeFsmKeyChar( upper, pd );
+
+ /* Validate the range. */
+ if ( lowKey > highKey ) {
+ /* Recover by setting upper to lower; */
+ error(loc) << "lower end of range is greater then upper end" << endl;
+ highKey = lowKey;
+ }
+
+ /* Make the range machine. */
+ rtnVal = new FsmAp();
+ rtnVal->rangeFsm( lowKey, highKey );
+
+ if ( rootRegex != 0 && rootRegex->caseInsensitive ) {
+ if ( lowKey <= 'Z' && 'A' <= highKey ) {
+ Key otherLow = lowKey < 'A' ? Key('A') : lowKey;
+ Key otherHigh = 'Z' < highKey ? Key('Z') : highKey;
+
+ otherLow = 'a' + ( otherLow - 'A' );
+ otherHigh = 'a' + ( otherHigh - 'A' );
+
+ FsmAp *otherRange = new FsmAp();
+ otherRange->rangeFsm( otherLow, otherHigh );
+ rtnVal->unionOp( otherRange );
+ rtnVal->minimizePartition2();
+ }
+ else if ( lowKey <= 'z' && 'a' <= highKey ) {
+ Key otherLow = lowKey < 'a' ? Key('a') : lowKey;
+ Key otherHigh = 'z' < highKey ? Key('z') : highKey;
+
+ otherLow = 'A' + ( otherLow - 'a' );
+ otherHigh = 'A' + ( otherHigh - 'a' );
+
+ FsmAp *otherRange = new FsmAp();
+ otherRange->rangeFsm( otherLow, otherHigh );
+ rtnVal->unionOp( otherRange );
+ rtnVal->minimizePartition2();
+ }
+ }
+
+ break;
+ }}
+ return rtnVal;
+}
diff --git a/contrib/tools/ragel5/ragel/parsetree.h b/contrib/tools/ragel5/ragel/parsetree.h
new file mode 100644
index 0000000000..4f398683a9
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/parsetree.h
@@ -0,0 +1,755 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PARSETREE_H
+#define _PARSETREE_H
+
+#include "ragel.h"
+#include "avlmap.h"
+#include "bstmap.h"
+#include "vector.h"
+#include "dlist.h"
+
+struct NameInst;
+
+/* Types of builtin machines. */
+enum BuiltinMachine
+{
+ BT_Any,
+ BT_Ascii,
+ BT_Extend,
+ BT_Alpha,
+ BT_Digit,
+ BT_Alnum,
+ BT_Lower,
+ BT_Upper,
+ BT_Cntrl,
+ BT_Graph,
+ BT_Print,
+ BT_Punct,
+ BT_Space,
+ BT_Xdigit,
+ BT_Lambda,
+ BT_Empty
+};
+
+
+struct ParseData;
+
+/* Leaf type. */
+struct Literal;
+
+/* Tree nodes. */
+
+struct Term;
+struct FactorWithAug;
+struct FactorWithRep;
+struct FactorWithNeg;
+struct Factor;
+struct Expression;
+struct Join;
+struct JoinOrLm;
+struct LongestMatch;
+struct LongestMatchPart;
+struct LmPartList;
+struct Range;
+
+/* Type of augmentation. Describes locations in the machine. */
+enum AugType
+{
+ /* Transition actions/priorities. */
+ at_start,
+ at_all,
+ at_finish,
+ at_leave,
+
+ /* Global error actions. */
+ at_start_gbl_error,
+ at_all_gbl_error,
+ at_final_gbl_error,
+ at_not_start_gbl_error,
+ at_not_final_gbl_error,
+ at_middle_gbl_error,
+
+ /* Local error actions. */
+ at_start_local_error,
+ at_all_local_error,
+ at_final_local_error,
+ at_not_start_local_error,
+ at_not_final_local_error,
+ at_middle_local_error,
+
+ /* To State Action embedding. */
+ at_start_to_state,
+ at_all_to_state,
+ at_final_to_state,
+ at_not_start_to_state,
+ at_not_final_to_state,
+ at_middle_to_state,
+
+ /* From State Action embedding. */
+ at_start_from_state,
+ at_all_from_state,
+ at_final_from_state,
+ at_not_start_from_state,
+ at_not_final_from_state,
+ at_middle_from_state,
+
+ /* EOF Action embedding. */
+ at_start_eof,
+ at_all_eof,
+ at_final_eof,
+ at_not_start_eof,
+ at_not_final_eof,
+ at_middle_eof
+};
+
+/* IMPORTANT: These must follow the same order as the state augs in AugType
+ * since we will be using this to compose AugType. */
+enum StateAugType
+{
+ sat_start = 0,
+ sat_all,
+ sat_final,
+ sat_not_start,
+ sat_not_final,
+ sat_middle
+};
+
+struct Action;
+struct PriorDesc;
+struct RegExpr;
+struct ReItem;
+struct ReOrBlock;
+struct ReOrItem;
+struct ExplicitMachine;
+struct InlineItem;
+struct InlineList;
+
+/* Reference to a named state. */
+typedef Vector<char*> NameRef;
+typedef Vector<NameRef*> NameRefList;
+typedef Vector<NameInst*> NameTargList;
+
+/* Structure for storing location of epsilon transitons. */
+struct EpsilonLink
+{
+ EpsilonLink( const InputLoc &loc, NameRef &target )
+ : loc(loc), target(target) { }
+
+ InputLoc loc;
+ NameRef target;
+};
+
+struct Label
+{
+ Label( const InputLoc &loc, char *data )
+ : loc(loc), data(data) { }
+
+ InputLoc loc;
+ char *data;
+};
+
+/* Structrue represents an action assigned to some FactorWithAug node. The
+ * factor with aug will keep an array of these. */
+struct ParserAction
+{
+ ParserAction( const InputLoc &loc, AugType type, int localErrKey, Action *action )
+ : loc(loc), type(type), localErrKey(localErrKey), action(action) { }
+
+ InputLoc loc;
+ AugType type;
+ int localErrKey;
+ Action *action;
+};
+
+struct Token
+{
+ char *data;
+ int length;
+ InputLoc loc;
+
+ void prepareLitString( Token &result, bool &caseInsensitive );
+ void append( const Token &other );
+ void set(const char *str, int len );
+};
+
+/* Store the value and type of a priority augmentation. */
+struct PriorityAug
+{
+ PriorityAug( AugType type, int priorKey, int priorValue ) :
+ type(type), priorKey(priorKey), priorValue(priorValue) { }
+
+ AugType type;
+ int priorKey;
+ int priorValue;
+};
+
+/*
+ * A Variable Definition
+ */
+struct VarDef
+{
+ VarDef(const char *name, JoinOrLm *joinOrLm )
+ : name(name), joinOrLm(joinOrLm), isExport(false) { }
+
+ /* Parse tree traversal. */
+ FsmAp *walk( ParseData *pd );
+ void makeNameTree( const InputLoc &loc, ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ const char *name;
+ JoinOrLm *joinOrLm;
+ bool isExport;
+};
+
+
+/*
+ * LongestMatch
+ *
+ * Wherever possible the item match will execute on the character. If not
+ * possible the item match will execute on a lookahead character and either
+ * hold the current char (if one away) or backup.
+ *
+ * How to handle the problem of backing up over a buffer break?
+ *
+ * Don't want to use pending out transitions for embedding item match because
+ * the role of item match action is different: it may sometimes match on the
+ * final transition, or may match on a lookahead character.
+ *
+ * Don't want to invent a new operator just for this. So just trail action
+ * after machine, this means we can only use literal actions.
+ *
+ * The item action may
+ *
+ * What states of the machine will be final. The item actions that wrap around
+ * on the last character will go straight to the start state.
+ *
+ * Some transitions will be lookahead transitions, they will hold the current
+ * character. Crossing them with regular transitions must be restricted
+ * because it does not make sense. The transition cannot simultaneously hold
+ * and consume the current character.
+ */
+struct LongestMatchPart
+{
+ LongestMatchPart( Join *join, Action *action,
+ InputLoc &semiLoc, int longestMatchId )
+ :
+ join(join), action(action), semiLoc(semiLoc),
+ longestMatchId(longestMatchId), inLmSelect(false) { }
+
+ InputLoc getLoc();
+
+ Join *join;
+ Action *action;
+ InputLoc semiLoc;
+
+ Action *setActId;
+ Action *actOnLast;
+ Action *actOnNext;
+ Action *actLagBehind;
+ int longestMatchId;
+ bool inLmSelect;
+ LongestMatch *longestMatch;
+
+ LongestMatchPart *prev, *next;
+};
+
+/* Declare a new type so that ptreetypes.h need not include dlist.h. */
+struct LmPartList : DList<LongestMatchPart> {};
+
+struct LongestMatch
+{
+ /* Construct with a list of joins */
+ LongestMatch( const InputLoc &loc, LmPartList *longestMatchList ) :
+ loc(loc), longestMatchList(longestMatchList), name(0),
+ lmSwitchHandlesError(false) { }
+
+ /* Tree traversal. */
+ FsmAp *walk( ParseData *pd );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+ void runLonestMatch( ParseData *pd, FsmAp *graph );
+ Action *newAction( ParseData *pd, const InputLoc &loc, const char *name,
+ InlineList *inlineList );
+ void makeActions( ParseData *pd );
+ void findName( ParseData *pd );
+ void restart( FsmAp *graph, TransAp *trans );
+
+ InputLoc loc;
+ LmPartList *longestMatchList;
+ const char *name;
+
+ Action *lmActSelect;
+ bool lmSwitchHandlesError;
+
+ LongestMatch *next, *prev;
+};
+
+
+/* List of Expressions. */
+typedef DList<Expression> ExprList;
+
+struct JoinOrLm
+{
+ enum Type {
+ JoinType,
+ LongestMatchType
+ };
+
+ JoinOrLm( Join *join ) :
+ join(join), type(JoinType) {}
+ JoinOrLm( LongestMatch *longestMatch ) :
+ longestMatch(longestMatch), type(LongestMatchType) {}
+
+ FsmAp *walk( ParseData *pd );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ Join *join;
+ LongestMatch *longestMatch;
+ Type type;
+};
+
+/*
+ * Join
+ */
+struct Join
+{
+ /* Construct with the first expression. */
+ Join( Expression *expr );
+ Join( const InputLoc &loc, Expression *expr );
+
+ /* Tree traversal. */
+ FsmAp *walk( ParseData *pd );
+ FsmAp *walkJoin( ParseData *pd );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ /* Data. */
+ InputLoc loc;
+ ExprList exprList;
+};
+
+/*
+ * Expression
+ */
+struct Expression
+{
+ enum Type {
+ OrType,
+ IntersectType,
+ SubtractType,
+ StrongSubtractType,
+ TermType,
+ BuiltinType
+ };
+
+ /* Construct with an expression on the left and a term on the right. */
+ Expression( Expression *expression, Term *term, Type type ) :
+ expression(expression), term(term),
+ builtin(builtin), type(type), prev(this), next(this) { }
+
+ /* Construct with only a term. */
+ Expression( Term *term ) :
+ expression(0), term(term), builtin(builtin),
+ type(TermType) , prev(this), next(this) { }
+
+ /* Construct with a builtin type. */
+ Expression( BuiltinMachine builtin ) :
+ expression(0), term(0), builtin(builtin),
+ type(BuiltinType), prev(this), next(this) { }
+
+ ~Expression();
+
+ /* Tree traversal. */
+ FsmAp *walk( ParseData *pd, bool lastInSeq = true );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ /* Node data. */
+ Expression *expression;
+ Term *term;
+ BuiltinMachine builtin;
+ Type type;
+
+ Expression *prev, *next;
+};
+
+/*
+ * Term
+ */
+struct Term
+{
+ enum Type {
+ ConcatType,
+ RightStartType,
+ RightFinishType,
+ LeftType,
+ FactorWithAugType
+ };
+
+ Term( Term *term, FactorWithAug *factorWithAug ) :
+ term(term), factorWithAug(factorWithAug), type(ConcatType) { }
+
+ Term( Term *term, FactorWithAug *factorWithAug, Type type ) :
+ term(term), factorWithAug(factorWithAug), type(type) { }
+
+ Term( FactorWithAug *factorWithAug ) :
+ term(0), factorWithAug(factorWithAug), type(FactorWithAugType) { }
+
+ ~Term();
+
+ FsmAp *walk( ParseData *pd, bool lastInSeq = true );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ Term *term;
+ FactorWithAug *factorWithAug;
+ Type type;
+
+ /* Priority descriptor for RightFinish type. */
+ PriorDesc priorDescs[2];
+};
+
+
+/* Third level of precedence. Augmenting nodes with actions and priorities. */
+struct FactorWithAug
+{
+ FactorWithAug( FactorWithRep *factorWithRep ) :
+ priorDescs(0), factorWithRep(factorWithRep) { }
+ ~FactorWithAug();
+
+ /* Tree traversal. */
+ FsmAp *walk( ParseData *pd );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ void assignActions( ParseData *pd, FsmAp *graph, int *actionOrd );
+ void assignPriorities( FsmAp *graph, int *priorOrd );
+
+ void assignConditions( FsmAp *graph );
+
+ /* Actions and priorities assigned to the factor node. */
+ Vector<ParserAction> actions;
+ Vector<PriorityAug> priorityAugs;
+ PriorDesc *priorDescs;
+ Vector<Label> labels;
+ Vector<EpsilonLink> epsilonLinks;
+ Vector<ParserAction> conditions;
+
+ FactorWithRep *factorWithRep;
+};
+
+/* Fourth level of precedence. Trailing unary operators. Provide kleen star,
+ * optional and plus. */
+struct FactorWithRep
+{
+ enum Type {
+ StarType,
+ StarStarType,
+ OptionalType,
+ PlusType,
+ ExactType,
+ MaxType,
+ MinType,
+ RangeType,
+ FactorWithNegType
+ };
+
+ FactorWithRep( const InputLoc &loc, FactorWithRep *factorWithRep,
+ int lowerRep, int upperRep, Type type ) :
+ loc(loc), factorWithRep(factorWithRep),
+ factorWithNeg(0), lowerRep(lowerRep),
+ upperRep(upperRep), type(type) { }
+
+ FactorWithRep( FactorWithNeg *factorWithNeg )
+ : factorWithNeg(factorWithNeg), type(FactorWithNegType) { }
+
+ ~FactorWithRep();
+
+ /* Tree traversal. */
+ FsmAp *walk( ParseData *pd );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ InputLoc loc;
+ FactorWithRep *factorWithRep;
+ FactorWithNeg *factorWithNeg;
+ int lowerRep, upperRep;
+ Type type;
+
+ /* Priority descriptor for StarStar type. */
+ PriorDesc priorDescs[2];
+};
+
+/* Fifth level of precedence. Provides Negation. */
+struct FactorWithNeg
+{
+ enum Type {
+ NegateType,
+ CharNegateType,
+ FactorType
+ };
+
+ FactorWithNeg( const InputLoc &loc, FactorWithNeg *factorWithNeg, Type type) :
+ loc(loc), factorWithNeg(factorWithNeg), factor(0), type(type) { }
+
+ FactorWithNeg( Factor *factor ) :
+ factorWithNeg(0), factor(factor), type(FactorType) { }
+
+ ~FactorWithNeg();
+
+ /* Tree traversal. */
+ FsmAp *walk( ParseData *pd );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ InputLoc loc;
+ FactorWithNeg *factorWithNeg;
+ Factor *factor;
+ Type type;
+};
+
+/*
+ * Factor
+ */
+struct Factor
+{
+ /* Language elements a factor node can be. */
+ enum Type {
+ LiteralType,
+ RangeType,
+ OrExprType,
+ RegExprType,
+ ReferenceType,
+ ParenType,
+ LongestMatchType,
+ };
+
+ /* Construct with a literal fsm. */
+ Factor( Literal *literal ) :
+ literal(literal), type(LiteralType) { }
+
+ /* Construct with a range. */
+ Factor( Range *range ) :
+ range(range), type(RangeType) { }
+
+ /* Construct with the or part of a regular expression. */
+ Factor( ReItem *reItem ) :
+ reItem(reItem), type(OrExprType) { }
+
+ /* Construct with a regular expression. */
+ Factor( RegExpr *regExpr ) :
+ regExpr(regExpr), type(RegExprType) { }
+
+ /* Construct with a reference to a var def. */
+ Factor( const InputLoc &loc, VarDef *varDef ) :
+ loc(loc), varDef(varDef), type(ReferenceType) {}
+
+ /* Construct with a parenthesized join. */
+ Factor( Join *join ) :
+ join(join), type(ParenType) {}
+
+ /* Construct with a longest match operator. */
+ Factor( LongestMatch *longestMatch ) :
+ longestMatch(longestMatch), type(LongestMatchType) {}
+
+ /* Cleanup. */
+ ~Factor();
+
+ /* Tree traversal. */
+ FsmAp *walk( ParseData *pd );
+ void makeNameTree( ParseData *pd );
+ void resolveNameRefs( ParseData *pd );
+
+ InputLoc loc;
+ Literal *literal;
+ Range *range;
+ ReItem *reItem;
+ RegExpr *regExpr;
+ VarDef *varDef;
+ Join *join;
+ LongestMatch *longestMatch;
+ int lower, upper;
+ Type type;
+};
+
+/* A range machine. Only ever composed of two literals. */
+struct Range
+{
+ Range( Literal *lowerLit, Literal *upperLit )
+ : lowerLit(lowerLit), upperLit(upperLit) { }
+
+ ~Range();
+ FsmAp *walk( ParseData *pd );
+
+ Literal *lowerLit;
+ Literal *upperLit;
+};
+
+/* Some literal machine. Can be a number or literal string. */
+struct Literal
+{
+ enum LiteralType { Number, LitString };
+
+ Literal( const Token &token, LiteralType type )
+ : token(token), type(type) { }
+
+ FsmAp *walk( ParseData *pd );
+
+ Token token;
+ LiteralType type;
+};
+
+/* Regular expression. */
+struct RegExpr
+{
+ enum RegExpType { RecurseItem, Empty };
+
+ /* Constructors. */
+ RegExpr() :
+ type(Empty), caseInsensitive(false) { }
+ RegExpr(RegExpr *regExpr, ReItem *item) :
+ regExpr(regExpr), item(item),
+ type(RecurseItem), caseInsensitive(false) { }
+
+ ~RegExpr();
+ FsmAp *walk( ParseData *pd, RegExpr *rootRegex );
+
+ RegExpr *regExpr;
+ ReItem *item;
+ RegExpType type;
+ bool caseInsensitive;
+};
+
+/* An item in a regular expression. */
+struct ReItem
+{
+ enum ReItemType { Data, Dot, OrBlock, NegOrBlock };
+
+ ReItem( const InputLoc &loc, const Token &token )
+ : loc(loc), token(token), star(false), type(Data) { }
+ ReItem( const InputLoc &loc, ReItemType type )
+ : loc(loc), star(false), type(type) { }
+ ReItem( const InputLoc &loc, ReOrBlock *orBlock, ReItemType type )
+ : loc(loc), orBlock(orBlock), star(false), type(type) { }
+
+ ~ReItem();
+ FsmAp *walk( ParseData *pd, RegExpr *rootRegex );
+
+ InputLoc loc;
+ Token token;
+ ReOrBlock *orBlock;
+ bool star;
+ ReItemType type;
+};
+
+/* An or block item. */
+struct ReOrBlock
+{
+ enum ReOrBlockType { RecurseItem, Empty };
+
+ /* Constructors. */
+ ReOrBlock()
+ : type(Empty) { }
+ ReOrBlock(ReOrBlock *orBlock, ReOrItem *item)
+ : orBlock(orBlock), item(item), type(RecurseItem) { }
+
+ ~ReOrBlock();
+ FsmAp *walk( ParseData *pd, RegExpr *rootRegex );
+
+ ReOrBlock *orBlock;
+ ReOrItem *item;
+ ReOrBlockType type;
+};
+
+/* An item in an or block. */
+struct ReOrItem
+{
+ enum ReOrItemType { Data, Range };
+
+ ReOrItem( const InputLoc &loc, const Token &token )
+ : loc(loc), token(token), type(Data) {}
+ ReOrItem( const InputLoc &loc, char lower, char upper )
+ : loc(loc), lower(lower), upper(upper), type(Range) { }
+
+ FsmAp *walk( ParseData *pd, RegExpr *rootRegex );
+
+ InputLoc loc;
+ Token token;
+ char lower;
+ char upper;
+ ReOrItemType type;
+};
+
+
+/*
+ * Inline code tree
+ */
+struct InlineList;
+struct InlineItem
+{
+ enum Type
+ {
+ Text, Goto, Call, Next, GotoExpr, CallExpr, NextExpr, Ret, PChar,
+ Char, Hold, Curs, Targs, Entry, Exec, LmSwitch, LmSetActId,
+ LmSetTokEnd, LmOnLast, LmOnNext, LmOnLagBehind, LmInitAct,
+ LmInitTokStart, LmSetTokStart, Break
+ };
+
+ InlineItem( const InputLoc &loc, char *data, Type type ) :
+ loc(loc), data(data), nameRef(0), children(0), type(type) { }
+
+ InlineItem( const InputLoc &loc, NameRef *nameRef, Type type ) :
+ loc(loc), data(0), nameRef(nameRef), children(0), type(type) { }
+
+ InlineItem( const InputLoc &loc, LongestMatch *longestMatch,
+ LongestMatchPart *longestMatchPart, Type type ) : loc(loc), data(0),
+ nameRef(0), children(0), longestMatch(longestMatch),
+ longestMatchPart(longestMatchPart), type(type) { }
+
+ InlineItem( const InputLoc &loc, NameInst *nameTarg, Type type ) :
+ loc(loc), data(0), nameRef(0), nameTarg(nameTarg), children(0),
+ type(type) { }
+
+ InlineItem( const InputLoc &loc, Type type ) :
+ loc(loc), data(0), nameRef(0), children(0), type(type) { }
+
+ InputLoc loc;
+ char *data;
+ NameRef *nameRef;
+ NameInst *nameTarg;
+ InlineList *children;
+ LongestMatch *longestMatch;
+ LongestMatchPart *longestMatchPart;
+ Type type;
+
+ InlineItem *prev, *next;
+};
+
+/* Normally this would be atypedef, but that would entail including DList from
+ * ptreetypes, which should be just typedef forwards. */
+struct InlineList : public DList<InlineItem> { };
+
+
+
+#endif /* _PARSETREE_H */
diff --git a/contrib/tools/ragel5/ragel/ragel.h b/contrib/tools/ragel5/ragel/ragel.h
new file mode 100644
index 0000000000..736369c0ce
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/ragel.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _RAGEL_H
+#define _RAGEL_H
+
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#include <string>
+#include "config.h"
+
+#define PROGNAME "ragel"
+
+/* To what degree are machine minimized. */
+enum MinimizeLevel {
+ MinimizeApprox,
+ MinimizeStable,
+ MinimizePartition1,
+ MinimizePartition2
+};
+
+enum MinimizeOpt {
+ MinimizeNone,
+ MinimizeEnd,
+ MinimizeMostOps,
+ MinimizeEveryOp
+};
+
+/* Options. */
+extern MinimizeLevel minimizeLevel;
+extern MinimizeOpt minimizeOpt;
+extern char *machineSpec, *machineName;
+extern bool printStatistics;
+
+extern int gblErrorCount;
+extern char mainMachine[];
+
+/* Location in an input file. */
+struct InputLoc
+{
+ const char *fileName;
+ int line;
+ int col;
+};
+
+/* Error reporting. */
+std::ostream &error();
+std::ostream &error( const InputLoc &loc );
+std::ostream &warning( const InputLoc &loc );
+
+void terminateAllParsers( );
+void writeMachines( std::ostream &out, std::string hostData, const char *inputFileName );
+void xmlEscapeHost( std::ostream &out, char *data, int len );
+
+#endif /* _RAGEL_H */
diff --git a/contrib/tools/ragel5/ragel/rlparse.cpp b/contrib/tools/ragel5/ragel/rlparse.cpp
new file mode 100644
index 0000000000..cd6fbde218
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/rlparse.cpp
@@ -0,0 +1,6088 @@
+/* Automatically generated by Kelbt from "rlparse.kl".
+ *
+ * Parts of this file are copied from Kelbt source covered by the GNU
+ * GPL. As a special exception, you may use the parts of this file copied
+ * from Kelbt source without restriction. The remainder is derived from
+ * "rlparse.kl" and inherits the copyright status of that file.
+ */
+
+#line 1 "rlparse.kl"
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlparse.h"
+#include "ragel.h"
+#include <iostream>
+#include <errno.h>
+
+#include <stdlib.h>
+//#include <malloc.h>
+
+using std::cout;
+using std::cerr;
+using std::endl;
+
+ParserDict parserDict;
+
+#line 93 "rlparse.kh"
+#line 96 "rlparse.kh"
+#line 126 "rlparse.kh"
+#line 1370 "rlparse.kl"
+
+
+#line 50 "rlparse.cpp"
+struct Parser_Lel_action_ref
+{
+#line 682 "rlparse.kl"
+
+ Action *action;
+
+
+#line 57 "rlparse.cpp"
+};
+
+struct Parser_Lel_aug_type
+{
+#line 475 "rlparse.kl"
+
+ InputLoc loc;
+ AugType augType;
+
+
+#line 68 "rlparse.cpp"
+};
+
+struct Parser_Lel_expression
+{
+#line 297 "rlparse.kl"
+
+ Expression *expression;
+
+
+#line 78 "rlparse.cpp"
+};
+
+struct Parser_Lel_factor
+{
+#line 907 "rlparse.kl"
+
+ Factor *factor;
+
+
+#line 88 "rlparse.cpp"
+};
+
+struct Parser_Lel_factor_rep_num
+{
+#line 861 "rlparse.kl"
+
+ int rep;
+
+
+#line 98 "rlparse.cpp"
+};
+
+struct Parser_Lel_factor_with_aug
+{
+#line 392 "rlparse.kl"
+
+ FactorWithAug *factorWithAug;
+
+
+#line 108 "rlparse.cpp"
+};
+
+struct Parser_Lel_factor_with_ep
+{
+#line 376 "rlparse.kl"
+
+ FactorWithAug *factorWithAug;
+
+
+#line 118 "rlparse.cpp"
+};
+
+struct Parser_Lel_factor_with_label
+{
+#line 360 "rlparse.kl"
+
+ FactorWithAug *factorWithAug;
+
+
+#line 128 "rlparse.cpp"
+};
+
+struct Parser_Lel_factor_with_neg
+{
+#line 887 "rlparse.kl"
+
+ FactorWithNeg *factorWithNeg;
+
+
+#line 138 "rlparse.cpp"
+};
+
+struct Parser_Lel_factor_with_rep
+{
+#line 811 "rlparse.kl"
+
+ FactorWithRep *factorWithRep;
+
+
+#line 148 "rlparse.cpp"
+};
+
+struct Parser_Lel_inline_item
+{
+#line 1160 "rlparse.kl"
+
+ InlineItem *inlineItem;
+
+
+#line 158 "rlparse.cpp"
+};
+
+struct Parser_Lel_inline_list
+{
+#line 1139 "rlparse.kl"
+
+ InlineList *inlineList;
+
+
+#line 168 "rlparse.cpp"
+};
+
+struct Parser_Lel_join
+{
+#line 281 "rlparse.kl"
+
+ Join *join;
+
+
+#line 178 "rlparse.cpp"
+};
+
+struct Parser_Lel_join_or_lm
+{
+#line 204 "rlparse.kl"
+
+ JoinOrLm *joinOrLm;
+
+
+#line 188 "rlparse.cpp"
+};
+
+struct Parser_Lel_lm_part_list
+{
+#line 224 "rlparse.kl"
+
+ LmPartList *lmPartList;
+
+
+#line 198 "rlparse.cpp"
+};
+
+struct Parser_Lel_local_err_name
+{
+#line 790 "rlparse.kl"
+
+ int error_name;
+
+
+#line 208 "rlparse.cpp"
+};
+
+struct Parser_Lel_longest_match_part
+{
+#line 243 "rlparse.kl"
+
+ LongestMatchPart *lmPart;
+
+
+#line 218 "rlparse.cpp"
+};
+
+struct Parser_Lel_opt_export
+{
+#line 64 "rlparse.kl"
+
+ bool isSet;
+
+
+#line 228 "rlparse.cpp"
+};
+
+struct Parser_Lel_opt_lm_part_action
+{
+#line 262 "rlparse.kl"
+
+ Action *action;
+
+
+#line 238 "rlparse.cpp"
+};
+
+struct Parser_Lel_priority_aug
+{
+#line 741 "rlparse.kl"
+
+ int priorityNum;
+
+
+#line 248 "rlparse.cpp"
+};
+
+struct Parser_Lel_priority_name
+{
+#line 723 "rlparse.kl"
+
+ int priorityName;
+
+
+#line 258 "rlparse.cpp"
+};
+
+struct Parser_Lel_range_lit
+{
+#line 975 "rlparse.kl"
+
+ Literal *literal;
+
+
+#line 268 "rlparse.cpp"
+};
+
+struct Parser_Lel_regular_expr
+{
+#line 1013 "rlparse.kl"
+
+ RegExpr *regExpr;
+
+
+#line 278 "rlparse.cpp"
+};
+
+struct Parser_Lel_regular_expr_char
+{
+#line 1062 "rlparse.kl"
+
+ ReItem *reItem;
+
+
+#line 288 "rlparse.cpp"
+};
+
+struct Parser_Lel_regular_expr_item
+{
+#line 1046 "rlparse.kl"
+
+ ReItem *reItem;
+
+
+#line 298 "rlparse.cpp"
+};
+
+struct Parser_Lel_regular_expr_or_char
+{
+#line 1121 "rlparse.kl"
+
+ ReOrItem *reOrItem;
+
+
+#line 308 "rlparse.cpp"
+};
+
+struct Parser_Lel_regular_expr_or_data
+{
+#line 1088 "rlparse.kl"
+
+ ReOrBlock *reOrBlock;
+
+
+#line 318 "rlparse.cpp"
+};
+
+struct Parser_Lel_term
+{
+#line 329 "rlparse.kl"
+
+ Term *term;
+
+
+#line 328 "rlparse.cpp"
+};
+
+struct Parser_Lel_token_type
+{
+#line 104 "rlparse.kl"
+
+ Token token;
+
+
+#line 338 "rlparse.cpp"
+};
+
+union Parser_UserData
+{
+ struct Parser_Lel_action_ref action_ref;
+ struct Parser_Lel_aug_type aug_type;
+ struct Parser_Lel_expression expression;
+ struct Parser_Lel_factor factor;
+ struct Parser_Lel_factor_rep_num factor_rep_num;
+ struct Parser_Lel_factor_with_aug factor_with_aug;
+ struct Parser_Lel_factor_with_ep factor_with_ep;
+ struct Parser_Lel_factor_with_label factor_with_label;
+ struct Parser_Lel_factor_with_neg factor_with_neg;
+ struct Parser_Lel_factor_with_rep factor_with_rep;
+ struct Parser_Lel_inline_item inline_item;
+ struct Parser_Lel_inline_list inline_list;
+ struct Parser_Lel_join join;
+ struct Parser_Lel_join_or_lm join_or_lm;
+ struct Parser_Lel_lm_part_list lm_part_list;
+ struct Parser_Lel_local_err_name local_err_name;
+ struct Parser_Lel_longest_match_part longest_match_part;
+ struct Parser_Lel_opt_export opt_export;
+ struct Parser_Lel_opt_lm_part_action opt_lm_part_action;
+ struct Parser_Lel_priority_aug priority_aug;
+ struct Parser_Lel_priority_name priority_name;
+ struct Parser_Lel_range_lit range_lit;
+ struct Parser_Lel_regular_expr regular_expr;
+ struct Parser_Lel_regular_expr_char regular_expr_char;
+ struct Parser_Lel_regular_expr_item regular_expr_item;
+ struct Parser_Lel_regular_expr_or_char regular_expr_or_char;
+ struct Parser_Lel_regular_expr_or_data regular_expr_or_data;
+ struct Parser_Lel_term term;
+ struct Parser_Lel_token_type token_type;
+ struct Token token;
+};
+
+struct Parser_LangEl
+{
+ char *file;
+ int line;
+ int type;
+ int reduction;
+ int state;
+ union Parser_UserData user;
+ unsigned int retry;
+ struct Parser_LangEl *next, *child;
+};
+
+#line 388 "rlparse.cpp"
+unsigned int Parser_startState = 0;
+
+short Parser_indicies[] = {
+ 151, -1, -1, -1, -1, -1, 151, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 151, 151, 151, 151, -1, -1,
+ -1, -1, -1, -1, 151, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 151, 151, -1, 151, 1, 0, 393,
+ 153, -1, -1, -1, -1, -1, 153, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 153, 153, 153, 153, -1, -1,
+ -1, -1, -1, -1, 153, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 153, 153, -1, 149, -1, -1, 2,
+ 157, -1, -1, -1, -1, -1, 150, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 4, 5, 6, 7, -1, -1,
+ -1, -1, -1, -1, 154, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 9, 8, -1, -1, -1, -1, -1,
+ 152, 384, 385, 386, 387, 388, 389, 390,
+ 391, 392, 10, 3, 161, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 24, 11, 12, 14, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 318, 320, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 13, 356, 356, 356, -1, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 356, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 356,
+ -1, -1, -1, -1, -1, -1, 356, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 356, 356, 356,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 356,
+ 356, -1, -1, -1, 356, 356, 356, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 20, 356, 356, 356, -1, 356, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 356, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 356, -1, -1,
+ -1, -1, -1, -1, 356, 356, -1, -1,
+ -1, -1, -1, -1, -1, -1, 356, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 356, 356, 356, 356, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 356, 356, -1,
+ -1, -1, 356, 356, 356, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 22, 170,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 170, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 21, 23, -1, -1, -1, -1, -1,
+ -1, -1, -1, 155, 25, 164, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 26, 14,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 318, 320, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 27, 319,
+ 368, 369, 370, -1, 367, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 166, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 366, -1, -1, -1,
+ -1, -1, -1, 364, 365, -1, -1, -1,
+ -1, -1, -1, -1, -1, 371, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 360, 361, 362, 363, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 372, 373, -1, -1,
+ -1, 374, 375, 28, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 357, -1, 359, -1, 355, 358,
+ 29, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 169, 368,
+ 369, 370, -1, 367, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 167, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 366, -1, -1, -1, -1,
+ -1, -1, 364, 365, -1, -1, -1, -1,
+ -1, -1, -1, -1, 371, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 360, 361, 362, 363, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 372, 373, -1, -1, -1,
+ 374, 375, 28, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 357, -1, 359, -1, 355, 358, 153,
+ -1, -1, -1, -1, -1, -1, 153, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 153, 153, 153, 153, -1, -1, -1, -1,
+ -1, -1, 153, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 153,
+ 153, -1, -1, -1, -1, 30, 31, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 32, 334, 334, 334, -1, 334,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 334, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 334, -1, -1, -1, -1, -1, -1, 334,
+ -1, -1, -1, -1, -1, -1, 334, 334,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 334, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 334, 334, 334,
+ 334, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 334, 334, 334, 334,
+ 334, 334, 334, 334, 334, 334, 334, 334,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 33, 163,
+ 165, 34, 356, 356, 356, -1, 356, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 356, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 356, -1,
+ -1, -1, -1, -1, -1, 356, 356, -1,
+ -1, -1, -1, -1, -1, -1, -1, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 356, 356, 356, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 356, 356,
+ -1, -1, -1, 356, 356, 356, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 35,
+ 158, -1, -1, -1, -1, -1, -1, 157,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 4, 5, 6, 7, -1, -1, -1,
+ -1, -1, -1, 154, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 8, -1, -1, -1, -1, -1, 152,
+ 384, 385, 386, 387, 388, 389, 390, 391,
+ 392, 10, 3, 44, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, 14,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 39, 46,
+ -1, -1, -1, -1, -1, 318, 320, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 50, 48, 49, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 36, -1, -1, 47, -1,
+ -1, -1, -1, -1, -1, -1, 37, 38,
+ 193, 41, -1, 42, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 43, -1,
+ -1, -1, 300, 304, -1, -1, 51, 44,
+ -1, -1, -1, -1, -1, -1, 52, -1,
+ -1, -1, -1, 14, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 45, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 39, 46, -1, -1, -1, -1,
+ -1, 318, 320, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 55, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 50, 48, 49, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 54,
+ 53, -1, 47, -1, -1, -1, -1, -1,
+ -1, -1, 37, 38, 193, 41, -1, 42,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 43, -1, -1, -1, 300, 304,
+ -1, -1, 51, 340, 341, 342, -1, 338,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 339, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 162, -1, -1, -1, -1, -1, -1, 366,
+ -1, -1, -1, -1, -1, -1, 364, 365,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 343, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 360, 361, 362,
+ 363, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 62, 57, 56, 372,
+ 373, 58, 60, 61, 374, 375, 28, 59,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 333, 337, 335, 336, 344,
+ 381, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 380, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 63, -1, -1, -1, -1, 64, 368,
+ 369, 370, -1, 367, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 168, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 366, -1, -1, -1, -1,
+ -1, -1, 364, 365, -1, -1, -1, -1,
+ -1, -1, -1, -1, 371, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 360, 361, 362, 363, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 372, 373, -1, -1, -1,
+ 374, 375, 28, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 357, -1, 359, -1, 355, 358, 70,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 159, 72,
+ -1, -1, 182, -1, -1, 182, 73, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 182, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 182, 71, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 182, -1, -1, -1,
+ 74, 44, -1, -1, -1, -1, 187, -1,
+ 52, 187, -1, -1, 187, 19, 75, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 187, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 187, 187, -1, -1, -1,
+ -1, -1, -1, -1, 39, 46, -1, -1,
+ -1, -1, -1, 318, 320, -1, -1, 76,
+ 77, 78, -1, 187, -1, -1, -1, 187,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 48, 49, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 188, 41,
+ -1, 42, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, -1, -1, -1,
+ 300, 304, -1, -1, 51, 307, -1, -1,
+ 307, 307, 307, -1, 307, 307, 307, 307,
+ 307, 307, 307, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 66, 307,
+ 307, -1, 307, 307, 307, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 307, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 307,
+ 307, -1, -1, -1, -1, -1, -1, -1,
+ 307, 307, -1, -1, -1, -1, -1, 307,
+ 307, -1, -1, 307, 307, 307, 307, 307,
+ 307, -1, -1, 307, 307, 307, 307, 307,
+ 307, 307, 307, 307, 307, 307, 307, 307,
+ 307, 307, 307, 307, 307, 307, 307, 307,
+ 307, 307, 307, 307, 307, 307, 307, 307,
+ 307, 307, 307, 307, 307, 307, 307, 307,
+ 307, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 307, 195,
+ -1, -1, -1, -1, 195, -1, 195, 195,
+ -1, -1, 195, 195, 195, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 195, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 195, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 195, 195, -1, -1, -1, -1, -1,
+ -1, -1, 195, 195, -1, -1, -1, -1,
+ -1, 195, 195, -1, -1, 195, 195, 195,
+ 79, 195, -1, -1, -1, 195, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 195, 195, 195, 197, -1, -1, 89, 88,
+ 197, -1, 197, 197, -1, -1, 197, 197,
+ 197, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 197, 91, -1,
+ 90, -1, 87, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 197, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 197, 197, -1,
+ -1, -1, -1, -1, -1, -1, 197, 197,
+ -1, -1, -1, -1, -1, 197, 197, -1,
+ -1, 197, 197, 197, 197, 197, -1, -1,
+ -1, 197, 213, 215, 217, 92, 256, 260,
+ 262, 264, 258, 266, 268, 272, 274, 276,
+ 270, 278, 244, 248, 250, 252, 246, 254,
+ 220, 224, 226, 228, 222, 230, 232, 236,
+ 238, 240, 234, 242, 197, 197, 197, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 219, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 80, -1, -1, 81,
+ 82, 83, 84, 85, 86, 208, -1, -1,
+ 208, 208, 208, -1, 208, 208, 292, 295,
+ 208, 208, 208, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 208,
+ 208, -1, 208, 294, 208, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 208, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 93,
+ 208, -1, -1, -1, -1, -1, -1, -1,
+ 208, 208, -1, -1, -1, -1, -1, 208,
+ 208, -1, -1, 208, 208, 208, 208, 208,
+ 293, -1, -1, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 208, 44,
+ -1, -1, -1, -1, -1, -1, 52, -1,
+ -1, -1, -1, 14, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 45, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 308, 46, -1, -1, -1, -1,
+ -1, 318, 320, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 50, 48, 49, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 47, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 302, 304,
+ -1, -1, 51, 44, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, 14,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 308, 46,
+ -1, -1, -1, -1, -1, 318, 320, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 50, 48, 49, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 47, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 303, 304, -1, -1, 51, 305,
+ -1, -1, 305, 305, 305, -1, 305, 305,
+ 305, 305, 305, 305, 305, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 305, 305, -1, 305, 305, 305, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 305, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 305, 305, -1, -1, -1, -1, -1,
+ -1, -1, 305, 305, -1, -1, -1, -1,
+ -1, 305, 305, -1, 314, 305, 305, 305,
+ 305, 305, 305, -1, -1, 305, 305, 305,
+ 305, 305, 305, 305, 305, 305, 305, 305,
+ 305, 305, 305, 305, 305, 305, 305, 305,
+ 305, 305, 305, 305, 305, 305, 305, 305,
+ 305, 305, 305, 305, 305, 305, 305, 305,
+ 305, 305, 305, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 305, 306, -1, -1, 306, 306, 306, -1,
+ 306, 306, 306, 306, 306, 306, 306, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 306, 306, -1, 306, 306,
+ 306, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 306, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 306, 306, -1, -1, -1,
+ -1, -1, -1, -1, 306, 306, -1, -1,
+ -1, -1, -1, 306, 306, -1, 316, 306,
+ 306, 306, 306, 306, 306, -1, -1, 306,
+ 306, 306, 306, 306, 306, 306, 306, 306,
+ 306, 306, 306, 306, 306, 306, 306, 306,
+ 306, 306, 306, 306, 306, 306, 306, 306,
+ 306, 306, 306, 306, 306, 306, 306, 306,
+ 306, 306, 306, 306, 306, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 306, 330, -1, -1, -1, 330,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 68, 330, -1, -1, -1, 330, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 69, 322,
+ 322, 322, -1, 322, -1, -1, 322, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 67, 94, 44, -1, -1, -1, -1, -1,
+ -1, 52, -1, -1, -1, -1, 14, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 45,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 39, 46, -1,
+ -1, -1, -1, -1, 318, 320, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 50, 48, 49, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 65, -1, -1, 47, -1, -1,
+ -1, -1, -1, -1, -1, 37, 38, 193,
+ 41, -1, 42, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 43, -1, -1,
+ -1, 300, 304, -1, -1, 51, 160, 70,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 171, 44,
+ -1, -1, -1, -1, -1, -1, 52, -1,
+ -1, -1, -1, 14, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 45, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 40, 46, -1, -1, -1, -1,
+ -1, 318, 320, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 50, 48, 49, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 4, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 154, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 176, -1, 175, -1, -1,
+ -1, -1, -1, -1, 156, 97, -1, 96,
+ -1, -1, 47, -1, -1, 95, 174, -1,
+ -1, -1, 37, 38, 193, 41, -1, 42,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 43, -1, -1, -1, 300, 304,
+ -1, -1, 51, 345, 356, 356, 356, -1,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 356, -1, -1, -1, -1, -1, -1, 356,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, 356, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 356, 356,
+ 356, 356, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 356, 356, -1, -1, -1, 356, 356, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 98, 100, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 381, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 380, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 99, -1, -1,
+ -1, -1, 64, 104, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 381, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 380, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 103, -1,
+ -1, -1, -1, 64, 102, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 381, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 380,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 101,
+ -1, -1, -1, -1, 64, 353, 354, 376,
+ 383, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 105, 313, -1,
+ -1, 70, 44, -1, -1, -1, -1, -1,
+ -1, 52, -1, -1, -1, -1, 14, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 45,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 39, 46, -1,
+ -1, -1, -1, -1, 318, 320, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 50, 48, 49, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 47, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 194,
+ 41, -1, 42, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 43, -1, -1,
+ -1, 300, 304, -1, -1, 51, 311, 107,
+ 108, -1, 327, -1, -1, 328, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 321, 106, 309, -1, -1, -1, 109,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 329, 310, -1,
+ -1, -1, 109, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 329, 44, -1, -1, -1, -1, -1, -1,
+ 52, -1, -1, -1, -1, 14, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 39, 46, -1, -1,
+ -1, -1, -1, 318, 320, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 48, 49, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, 110, 38, 193, 41,
+ -1, 42, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, -1, -1, -1,
+ 300, 304, -1, -1, 51, 44, -1, -1,
+ -1, -1, -1, -1, 52, -1, -1, -1,
+ -1, 14, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 39, 46, -1, -1, -1, -1, -1, 318,
+ 320, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 50, 48,
+ 49, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1,
+ -1, 113, 193, 41, -1, 42, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 43, -1, -1, -1, 300, 304, -1, -1,
+ 51, 44, -1, -1, -1, -1, -1, -1,
+ 52, -1, -1, -1, -1, 14, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 39, 46, -1, -1,
+ -1, -1, -1, 318, 320, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 48, 49, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, -1, 111, 193, 41,
+ -1, 42, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, -1, -1, -1,
+ 300, 304, -1, -1, 51, 44, -1, -1,
+ -1, -1, -1, -1, 52, -1, -1, -1,
+ -1, 14, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 39, 46, -1, -1, -1, -1, -1, 318,
+ 320, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 50, 48,
+ 49, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1,
+ -1, 112, 193, 41, -1, 42, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 43, -1, -1, -1, 300, 304, -1, -1,
+ 51, 44, -1, -1, -1, -1, -1, -1,
+ 52, -1, -1, -1, -1, 14, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 39, 46, -1, -1,
+ -1, -1, -1, 318, 320, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 48, 49, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, -1, 114, 193, 41,
+ -1, 42, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, -1, -1, -1,
+ 300, 304, -1, -1, 51, 44, -1, -1,
+ -1, -1, -1, -1, 52, -1, -1, -1,
+ -1, 14, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 39, 46, -1, -1, -1, -1, -1, 318,
+ 320, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 50, 48,
+ 49, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 189, 41, -1, 42, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 43, -1, -1, -1, 300, 304, -1, -1,
+ 51, 44, -1, -1, -1, -1, -1, -1,
+ 52, -1, -1, -1, -1, 14, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 39, 46, -1, -1,
+ -1, -1, -1, 318, 320, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 48, 49, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 190, 41,
+ -1, 42, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, -1, -1, -1,
+ 300, 304, -1, -1, 51, 44, -1, -1,
+ -1, -1, -1, -1, 52, -1, -1, -1,
+ -1, 14, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 39, 46, -1, -1, -1, -1, -1, 318,
+ 320, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 50, 48,
+ 49, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 191, 41, -1, 42, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 43, -1, -1, -1, 300, 304, -1, -1,
+ 51, 44, -1, -1, -1, -1, -1, -1,
+ 52, -1, -1, -1, -1, 14, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 39, 46, -1, -1,
+ -1, -1, -1, 318, 320, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 48, 49, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 192, 41,
+ -1, 42, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, -1, -1, -1,
+ 300, 304, -1, -1, 51, 378, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 196, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 115, 116, -1, -1, 118, -1, 119,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 117, -1, -1,
+ -1, -1, -1, -1, -1, -1, 284, -1,
+ -1, -1, -1, -1, -1, 288, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 198, 282, -1, -1,
+ -1, -1, -1, -1, -1, 199, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 280,
+ 287, 120, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 117, -1, -1, -1,
+ -1, -1, -1, -1, -1, 284, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 201, 282, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 280, 120,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 117, -1, -1, -1, -1, -1,
+ -1, -1, -1, 284, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 202, 282, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 280, 120, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 117, -1, -1, -1, -1, -1, -1, -1,
+ -1, 284, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 203,
+ 282, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 280, 120, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 117, -1,
+ -1, -1, -1, -1, -1, -1, -1, 284,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 204, 282, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 280, 120, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 117, -1, -1, -1,
+ -1, -1, -1, -1, -1, 284, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 205, 282, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 280, 121,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 117, -1, -1, -1, -1, -1,
+ -1, -1, -1, 284, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 206, 282, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 280, 209, -1, -1,
+ 209, -1, 209, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 209, -1, -1, -1, -1, -1, -1, -1,
+ -1, 209, -1, -1, -1, -1, -1, -1,
+ 209, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 253, 265, 277, 229, 241, 210, -1, -1,
+ 210, -1, 210, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 210, -1, -1, -1, -1, -1, -1, -1,
+ -1, 210, -1, -1, -1, -1, -1, -1,
+ 210, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 218,
+ 251, 263, 275, 227, 239, 211, -1, -1,
+ 211, -1, 211, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 211, -1, -1, -1, -1, -1, -1, -1,
+ -1, 211, -1, -1, -1, -1, -1, -1,
+ 211, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 216,
+ 249, 261, 273, 225, 237, 212, -1, -1,
+ 212, -1, 212, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 212, -1, -1, -1, -1, -1, -1, -1,
+ -1, 212, -1, -1, -1, -1, -1, -1,
+ 212, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 214,
+ 245, 257, 269, 221, 233, 247, 259, 271,
+ 223, 235, 255, 267, 279, 231, 243, 123,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 301, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 122, 14, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 315, -1, -1, -1, -1,
+ -1, 318, 320, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 317, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 312, 44, -1, -1, -1, -1,
+ -1, -1, 52, -1, 127, -1, -1, 14,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 45, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 40, 46,
+ -1, -1, -1, -1, -1, 318, 320, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 50, 48, 49, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 154, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 176,
+ -1, 175, -1, -1, -1, -1, -1, -1,
+ 156, 97, -1, 96, -1, -1, 47, -1,
+ -1, -1, 173, -1, -1, -1, 37, 38,
+ 193, 41, -1, 42, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 43, -1,
+ -1, -1, 300, 304, -1, -1, 51, 70,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 180, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 117, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 126, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 125, -1, 179, 161,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 124, 368, 369, 370, -1,
+ 367, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 346,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 366, -1, -1, -1, -1, -1, -1, 364,
+ 365, -1, -1, -1, -1, -1, -1, -1,
+ -1, 371, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 360, 361,
+ 362, 363, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 372, 373, -1, -1, -1, 374, 375, 28,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 357, -1,
+ 359, -1, 355, 358, 347, 356, 356, 356,
+ -1, 356, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 356, -1, -1, -1, -1, -1, -1,
+ 356, 356, -1, -1, -1, -1, -1, -1,
+ -1, -1, 356, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 356,
+ 356, 356, 356, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 356, 356, -1, -1, -1, 356, 356,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 128, 351, 356, 356, 356, -1,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 356, -1, -1, -1, -1, -1, -1, 356,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, 356, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 356, 356,
+ 356, 356, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 356, 356, -1, -1, -1, 356, 356, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 129, 349, 356, 356, 356, -1, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 356, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 356,
+ -1, -1, -1, -1, -1, -1, 356, 356,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 356, 356, 356,
+ 356, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 356,
+ 356, -1, -1, -1, 356, 356, 356, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 130, 379, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 379, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 131, 324, 324,
+ 324, -1, 324, 323, -1, 324, 330, -1,
+ -1, -1, 330, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 132, 330, -1, -1, -1,
+ 330, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 133, 331, -1, -1, 134, 331, 72,
+ -1, -1, 181, -1, -1, 181, 73, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 181, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 181, 71, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 181, -1, -1, -1,
+ 74, 44, -1, -1, -1, -1, 184, -1,
+ 52, 184, -1, -1, 184, 16, 75, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 184, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 184, 184, -1, -1, -1,
+ -1, -1, -1, -1, 39, 46, -1, -1,
+ -1, -1, -1, 318, 320, -1, -1, 76,
+ 77, 78, -1, 184, -1, -1, -1, 184,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 48, 49, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 188, 41,
+ -1, 42, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, -1, -1, -1,
+ 300, 304, -1, -1, 51, 44, -1, -1,
+ -1, -1, 185, -1, 52, 185, -1, -1,
+ 185, 17, 75, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 185,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 185,
+ 185, -1, -1, -1, -1, -1, -1, -1,
+ 39, 46, -1, -1, -1, -1, -1, 318,
+ 320, -1, -1, 76, 77, 78, -1, 185,
+ -1, -1, -1, 185, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 50, 48,
+ 49, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 188, 41, -1, 42, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 43, -1, -1, -1, 300, 304, -1, -1,
+ 51, 44, -1, -1, -1, -1, 183, -1,
+ 52, 183, -1, -1, 183, 15, 75, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 183, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 45, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 183, 183, -1, -1, -1,
+ -1, -1, -1, -1, 39, 46, -1, -1,
+ -1, -1, -1, 318, 320, -1, -1, 76,
+ 77, 78, -1, 183, -1, -1, -1, 183,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 50, 48, 49, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 47, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 188, 41,
+ -1, 42, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 43, -1, -1, -1,
+ 300, 304, -1, -1, 51, 44, -1, -1,
+ -1, -1, 186, -1, 52, 186, -1, -1,
+ 186, 18, 75, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 186,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 186,
+ 186, -1, -1, -1, -1, -1, -1, -1,
+ 39, 46, -1, -1, -1, -1, -1, 318,
+ 320, -1, -1, 76, 77, 78, -1, 186,
+ -1, -1, -1, 186, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 50, 48,
+ 49, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 188, 41, -1, 42, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 43, -1, -1, -1, 300, 304, -1, -1,
+ 51, 383, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 135, 138,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 136,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 137, 334, 334, 334, -1, 334, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 334, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 334, -1,
+ -1, -1, -1, -1, -1, 334, -1, -1,
+ -1, -1, -1, -1, 334, 334, -1, -1,
+ -1, -1, -1, -1, -1, -1, 334, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 334, 334, 334, 334, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 334, 334, 334, 334, 334, 334,
+ 334, 334, 334, 334, 334, 334, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 139, 289, 290, 284,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 137, 141, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 140, -1, 137, 143, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 296, 301, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 142, 31, 177, 120, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 117,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 284, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 178, 282,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 280, 172, 368, 369, 370, -1, 367,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 348, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 366,
+ -1, -1, -1, -1, -1, -1, 364, 365,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 371, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 360, 361, 362,
+ 363, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 372,
+ 373, -1, -1, -1, 374, 375, 28, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 357, -1, 359,
+ -1, 355, 358, 368, 369, 370, -1, 367,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 352, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 366,
+ -1, -1, -1, -1, -1, -1, 364, 365,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 371, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 360, 361, 362,
+ 363, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 372,
+ 373, -1, -1, -1, 374, 375, 28, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 357, -1, 359,
+ -1, 355, 358, 368, 369, 370, -1, 367,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 350, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 366,
+ -1, -1, -1, -1, -1, -1, 364, 365,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 371, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 360, 361, 362,
+ 363, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 372,
+ 373, -1, -1, -1, 374, 375, 28, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 357, -1, 359,
+ -1, 355, 358, 382, 325, -1, -1, -1,
+ 109, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 329, 326,
+ -1, -1, -1, 109, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 329, 332, 377, -1, -1, -1, -1,
+ 377, -1, 377, 377, -1, -1, 377, 377,
+ 377, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 377, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 377, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 377, 377, -1,
+ -1, -1, -1, -1, -1, -1, 377, 377,
+ -1, -1, -1, -1, -1, 377, 377, -1,
+ -1, 377, 377, 377, 377, 377, -1, 131,
+ -1, 377, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 377, 377, 377, 144,
+ 281, 283, -1, -1, 286, 340, 341, 342,
+ -1, 338, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 339, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 285, -1, -1, -1, -1, -1,
+ -1, 366, -1, -1, -1, -1, -1, -1,
+ 364, 365, -1, -1, -1, -1, -1, -1,
+ -1, -1, 343, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 360,
+ 361, 362, 363, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 62, 57,
+ 56, 372, 373, 58, 60, 61, 374, 375,
+ 28, 59, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 333, 337, 335,
+ 336, 344, 145, 283, -1, -1, 291, 297,
+ 298, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 301, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 146, 118, -1, 119, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 288, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 147, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 287, 120,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 117, -1, -1, -1, -1, -1,
+ -1, -1, -1, 284, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 148, 282, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 280, 299, 200, 207,
+
+};
+
+unsigned short Parser_keys[] = {
+ 132, 226, 224, 224, 132, 227, 132, 239,
+ 132, 240, 132, 132, 132, 132, 45, 244,
+ 40, 245, 40, 245, 132, 246, 123, 132,
+ 123, 123, 59, 132, 45, 244, 139, 139,
+ 40, 287, 132, 194, 40, 287, 125, 227,
+ 61, 137, 40, 243, 59, 59, 59, 59,
+ 40, 40, 40, 245, 125, 239, 33, 276,
+ 33, 276, 40, 284, 132, 290, 40, 287,
+ 44, 59, 38, 151, 33, 276, 33, 202,
+ 33, 188, 33, 266, 33, 202, 33, 276,
+ 33, 276, 33, 202, 33, 202, 189, 274,
+ 189, 274, 186, 275, 142, 142, 33, 276,
+ 59, 59, 44, 59, 33, 276, 59, 59,
+ 40, 245, 42, 290, 42, 290, 42, 290,
+ 59, 59, 59, 59, 41, 41, 132, 289,
+ 41, 44, 33, 276, 186, 278, 189, 279,
+ 189, 279, 33, 276, 33, 276, 33, 276,
+ 33, 276, 33, 276, 33, 276, 33, 276,
+ 33, 276, 33, 276, 132, 288, 40, 270,
+ 40, 269, 40, 269, 40, 269, 40, 269,
+ 40, 269, 40, 269, 40, 207, 40, 207,
+ 40, 207, 40, 207, 203, 207, 203, 207,
+ 44, 271, 45, 276, 33, 276, 44, 251,
+ 132, 240, 40, 287, 59, 59, 40, 245,
+ 59, 59, 40, 245, 59, 59, 40, 245,
+ 41, 149, 186, 193, 189, 274, 189, 274,
+ 189, 193, 38, 151, 33, 276, 33, 276,
+ 33, 276, 33, 276, 132, 289, 132, 269,
+ 40, 243, 139, 139, 139, 139, 132, 269,
+ 132, 269, 44, 125, 139, 271, 61, 61,
+ 59, 59, 40, 269, 124, 124, 40, 287,
+ 40, 287, 40, 287, 132, 132, 189, 279,
+ 189, 279, 193, 193, 33, 188, 44, 44,
+ 41, 41, 41, 44, 40, 284, 44, 44,
+ 41, 44, 125, 125, 125, 271, 43, 270,
+ 40, 269, 125, 125, 41, 41, 41, 41,
+ 0, 0
+};
+
+unsigned int Parser_offsets[] = {
+ 0, 95, 96, 192, 300, 409, 410, 411,
+ 611, 817, 1023, 1138, 1148, 1149, 1223, 1423,
+ 1424, 1672, 1735, 1983, 2086, 2163, 2367, 2368,
+ 2369, 2370, 2576, 2691, 2935, 3179, 3424, 3583,
+ 3831, 3847, 3961, 4205, 4375, 4531, 4765, 4935,
+ 5179, 5423, 5593, 5763, 5849, 5935, 6025, 6026,
+ 6270, 6271, 6287, 6531, 6532, 6738, 6987, 7236,
+ 7485, 7486, 7487, 7488, 7646, 7650, 7894, 7987,
+ 8078, 8169, 8413, 8657, 8901, 9145, 9389, 9633,
+ 9877, 10121, 10365, 10522, 10753, 10983, 11213, 11443,
+ 11673, 11903, 12133, 12301, 12469, 12637, 12805, 12810,
+ 12815, 13043, 13275, 13519, 13727, 13836, 14084, 14085,
+ 14291, 14292, 14498, 14499, 14705, 14814, 14822, 14908,
+ 14994, 14999, 15113, 15357, 15601, 15845, 16089, 16247,
+ 16385, 16589, 16590, 16591, 16729, 16867, 16949, 17082,
+ 17083, 17084, 17314, 17315, 17563, 17811, 18059, 18060,
+ 18151, 18242, 18243, 18399, 18400, 18401, 18405, 18650,
+ 18651, 18655, 18656, 18803, 19031, 19261, 19262, 19263,
+ 19264
+};
+
+unsigned short Parser_targs[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 15,
+ 15, 15, 15, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 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, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144
+};
+
+unsigned int Parser_actInds[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 33, 36, 39, 42, 45, 47, 49, 51,
+ 53, 55, 57, 59, 61, 63, 65, 67,
+ 69, 71, 73, 75, 77, 79, 81, 83,
+ 85, 88, 90, 92, 94, 96, 98, 100,
+ 102, 104, 106, 108, 110, 112, 114, 116,
+ 118, 120, 122, 124, 126, 128, 130, 132,
+ 134, 136, 138, 140, 142, 144, 146, 148,
+ 150, 152, 154, 156, 158, 160, 162, 164,
+ 166, 168, 170, 172, 174, 176, 178, 180,
+ 182, 184, 186, 188, 190, 192, 195, 197,
+ 199, 201, 203, 205, 207, 209, 211, 213,
+ 215, 217, 219, 221, 223, 225, 227, 229,
+ 231, 233, 235, 237, 239, 241, 243, 245,
+ 247, 249, 251, 253, 255, 257, 259, 261,
+ 263, 265, 267, 269, 271, 273, 275, 277,
+ 279, 281, 283, 285, 287, 289, 291, 293,
+ 295, 297, 299, 301, 303, 305, 307, 309,
+ 311, 313, 315, 317, 319, 321, 323, 325,
+ 327, 329, 331, 333, 335, 337, 339, 341,
+ 343, 345, 347, 349, 351, 353, 355, 357,
+ 359, 361, 363, 365, 367, 369, 371, 373,
+ 375, 377, 379, 381, 383, 385, 387, 389,
+ 391, 393, 395, 397, 399, 401, 403, 405,
+ 407, 409, 411, 413, 415, 417, 419, 421,
+ 423, 425, 427, 429, 431, 433, 435, 437,
+ 439, 441, 443, 445, 447, 449, 451, 453,
+ 455, 457, 459, 461, 463, 465, 467, 469,
+ 471, 473, 475, 477, 479, 481, 483, 485,
+ 487, 489, 491, 493, 495, 497, 499, 501,
+ 503, 505, 507, 509, 511, 513, 515, 517,
+ 519, 521, 523, 525, 527, 529, 531, 533,
+ 535, 537, 539, 541, 543, 545, 547, 549,
+ 551, 553, 555, 557, 559, 561, 563, 565,
+ 567, 569, 571, 573, 575, 577, 579, 581,
+ 583, 585, 587, 589, 591, 593, 595, 597,
+ 599, 601, 603, 605, 607, 609, 611, 613,
+ 615, 617, 619, 621, 623, 625, 627, 629,
+ 631, 633, 635, 637, 639, 641, 643, 645,
+ 647, 649, 651, 653, 655, 657, 659, 661,
+ 663, 665, 667, 669, 671, 673, 675, 677,
+ 679, 681, 683, 685, 687, 689, 691, 693,
+ 695, 697, 699, 701, 703, 705, 707, 709,
+ 711, 713, 715, 717, 719, 721, 723, 725,
+ 727, 729, 731, 733, 735, 737, 739, 741,
+ 743, 745, 747, 749, 751, 753, 755, 757,
+ 759, 761, 763, 765, 767, 769, 771, 773,
+ 775, 777, 779, 781, 783, 785, 787, 789,
+ 791, 793
+};
+
+unsigned int Parser_actions[] = {
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 170, 1,
+ 0, 174, 1, 0, 178, 1, 0, 182,
+ 1, 0, 186, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 66, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 270, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 2, 0, 7, 0, 10, 0, 15,
+ 0, 18, 0, 59, 0, 62, 0, 63,
+ 0, 66, 0, 71, 0, 75, 0, 79,
+ 0, 83, 0, 87, 0, 91, 0, 95,
+ 0, 99, 0, 103, 0, 107, 0, 111,
+ 0, 115, 0, 118, 0, 122, 0, 127,
+ 0, 131, 0, 135, 0, 139, 0, 143,
+ 0, 147, 0, 151, 0, 155, 0, 158,
+ 0, 162, 0, 166, 0, 170, 0, 174,
+ 0, 178, 0, 182, 0, 186, 0, 191,
+ 0, 195, 0, 199, 0, 203, 0, 207,
+ 0, 211, 0, 215, 0, 218, 0, 223,
+ 0, 226, 0, 231, 0, 235, 0, 239,
+ 0, 243, 0, 247, 0, 251, 0, 255,
+ 0, 259, 0, 263, 0, 267, 0, 270,
+ 0, 274, 0, 278, 0, 282, 0, 286,
+ 0, 291, 0, 295, 0, 299, 0, 303,
+ 0, 307, 0, 311, 0, 315, 0, 319,
+ 0, 323, 0, 327, 0, 331, 0, 335,
+ 0, 339, 0, 343, 0, 347, 0, 351,
+ 0, 355, 0, 359, 0, 363, 0, 367,
+ 0, 371, 0, 375, 0, 379, 0, 383,
+ 0, 387, 0, 391, 0, 395, 0, 399,
+ 0, 403, 0, 407, 0, 411, 0, 415,
+ 0, 419, 0, 423, 0, 427, 0, 431,
+ 0, 435, 0, 439, 0, 443, 0, 447,
+ 0, 451, 0, 455, 0, 459, 0, 463,
+ 0, 467, 0, 471, 0, 475, 0, 479,
+ 0, 483, 0, 487, 0, 491, 0, 495,
+ 0, 499, 0, 503, 0, 507, 0, 511,
+ 0, 515, 0, 519, 0, 523, 0, 527,
+ 0, 531, 0, 535, 0, 539, 0, 543,
+ 0, 547, 0, 551, 0, 555, 0, 559,
+ 0, 563, 0, 567, 0, 570, 0, 571,
+ 0, 575, 0, 578, 0, 583, 0, 587,
+ 0, 591, 0, 595, 0, 598, 0, 603,
+ 0, 607, 0, 611, 0, 615, 0, 619,
+ 0, 623, 0, 627, 0, 631, 0, 635,
+ 0, 639, 0, 643, 0, 647, 0, 651,
+ 0, 654, 0, 658, 0, 662, 0, 663,
+ 0, 667, 0, 671, 0, 675, 0, 679,
+ 0, 683, 0, 686, 0, 687, 0, 690,
+ 0, 691, 0, 695, 0, 699, 0, 703,
+ 0, 707, 0, 710, 0, 715, 0, 718,
+ 0, 723, 0, 727, 0, 731, 0, 735,
+ 0, 739, 0, 742, 0, 746, 0, 751,
+ 0, 755, 0, 758, 0, 763, 0, 767,
+ 0, 771, 0, 775, 0, 779, 0, 783,
+ 0, 787, 0, 791, 0, 795, 0, 799,
+ 0, 803, 0, 807, 0, 811, 0, 815,
+ 0, 819, 0, 823, 0, 827, 0, 831,
+ 0, 835, 0, 839, 0, 843, 0, 846,
+ 0, 851, 0, 855, 0, 859, 0, 863,
+ 0, 867, 0, 871, 0, 875, 0, 879,
+ 0, 883, 0, 887, 0, 891, 0, 895,
+ 0, 899, 0, 903, 0, 907, 0, 911,
+ 0, 915, 0, 919, 0, 923, 0, 927,
+ 0, 930, 0, 934, 0, 938, 0, 943,
+ 0, 946, 0, 951, 0, 955, 0, 23,
+ 0, 27, 0, 31, 0, 35, 0, 39,
+ 0, 43, 0, 47, 0, 51, 0, 55,
+ 0, 1, 0
+};
+
+int Parser_commitLen[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 1, 1, 1, 1, 1, 1,
+ 1, 2
+};
+
+unsigned int Parser_fssProdIdIndex[] = {
+ 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, 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, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 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
+};
+
+char Parser_fssProdLengths[] = {
+ 1, 3, 0, 2, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 4, 5, 5, 1, 5, 4, 3,
+ 4, 3, 3, 5, 2, 0, 1, 4,
+ 2, 1, 1, 1, 3, 2, 1, 0,
+ 3, 1, 3, 3, 3, 3, 1, 2,
+ 3, 3, 3, 3, 1, 3, 1, 3,
+ 1, 3, 3, 7, 3, 3, 3, 3,
+ 3, 3, 7, 1, 1, 1, 1, 1,
+ 1, 2, 1, 2, 1, 2, 1, 1,
+ 2, 1, 2, 1, 2, 1, 2, 1,
+ 2, 1, 2, 1, 2, 1, 2, 1,
+ 2, 1, 2, 1, 2, 1, 2, 1,
+ 2, 1, 2, 1, 2, 1, 2, 1,
+ 2, 1, 2, 1, 2, 1, 2, 1,
+ 2, 1, 2, 1, 2, 1, 2, 1,
+ 2, 1, 2, 1, 2, 1, 2, 1,
+ 2, 1, 2, 1, 3, 1, 1, 3,
+ 1, 1, 1, 2, 2, 1, 2, 2,
+ 2, 2, 4, 5, 5, 6, 1, 1,
+ 2, 2, 1, 1, 1, 1, 3, 3,
+ 3, 3, 3, 1, 1, 1, 2, 1,
+ 2, 0, 2, 1, 3, 3, 1, 1,
+ 2, 0, 1, 3, 2, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 3, 3, 4, 3, 4, 3, 4,
+ 2, 2, 2, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 4,
+ 2, 0, 2, 1, 0, 3, 1, 1
+};
+
+unsigned short Parser_prodLhsIds[] = {
+ 226, 225, 225, 227, 227, 228, 228, 228,
+ 228, 228, 228, 228, 228, 228, 238, 239,
+ 239, 237, 229, 230, 240, 231, 232, 232,
+ 233, 234, 235, 236, 246, 246, 242, 242,
+ 247, 247, 248, 248, 248, 249, 249, 249,
+ 241, 241, 252, 252, 252, 252, 252, 253,
+ 253, 253, 253, 253, 253, 254, 254, 255,
+ 255, 257, 257, 257, 257, 257, 257, 257,
+ 257, 257, 257, 257, 258, 258, 258, 258,
+ 261, 261, 261, 261, 261, 261, 261, 262,
+ 262, 262, 262, 262, 262, 262, 262, 262,
+ 262, 262, 262, 263, 263, 263, 263, 263,
+ 263, 263, 263, 263, 263, 263, 263, 264,
+ 264, 264, 264, 264, 264, 264, 264, 264,
+ 264, 264, 264, 265, 265, 265, 265, 265,
+ 265, 265, 265, 265, 265, 265, 265, 266,
+ 266, 266, 266, 266, 266, 266, 266, 266,
+ 266, 266, 266, 250, 250, 250, 269, 251,
+ 260, 259, 270, 270, 270, 267, 268, 268,
+ 268, 268, 268, 268, 268, 268, 268, 271,
+ 272, 272, 272, 273, 273, 273, 273, 273,
+ 273, 273, 273, 276, 276, 244, 244, 244,
+ 275, 275, 277, 277, 278, 278, 278, 278,
+ 274, 274, 279, 279, 243, 243, 280, 280,
+ 280, 283, 283, 283, 283, 283, 283, 281,
+ 281, 281, 281, 281, 281, 281, 281, 281,
+ 281, 281, 245, 245, 286, 286, 286, 282,
+ 282, 282, 282, 282, 282, 282, 287, 287,
+ 287, 287, 287, 284, 284, 284, 284, 284,
+ 256, 288, 285, 290, 290, 289, 289, 291
+};
+
+const char *Parser_prodNames[] = {
+ "start-1",
+ "section_list-1",
+ "section_list-2",
+ "statement_list-1",
+ "statement_list-2",
+ "statement-1",
+ "statement-2",
+ "statement-3",
+ "statement-4",
+ "statement-5",
+ "statement-6",
+ "statement-7",
+ "statement-8",
+ "statement-9",
+ "export_open-1",
+ "opt_export-1",
+ "opt_export-2",
+ "export_block-1",
+ "assignment-1",
+ "instantiation-1",
+ "machine_name-1",
+ "action_spec-1",
+ "alphtype_spec-1",
+ "alphtype_spec-2",
+ "range_spec-1",
+ "getkey_spec-1",
+ "access_spec-1",
+ "variable_spec-1",
+ "opt_whitespace-1",
+ "opt_whitespace-2",
+ "join_or_lm-1",
+ "join_or_lm-2",
+ "lm_part_list-1",
+ "lm_part_list-2",
+ "longest_match_part-1",
+ "longest_match_part-2",
+ "longest_match_part-3",
+ "opt_lm_part_action-1",
+ "opt_lm_part_action-2",
+ "opt_lm_part_action-3",
+ "join-1",
+ "join-2",
+ "expression-1",
+ "expression-2",
+ "expression-3",
+ "expression-4",
+ "expression-5",
+ "term-1",
+ "term-2",
+ "term-3",
+ "term-4",
+ "term-5",
+ "term-6",
+ "factor_with_label-1",
+ "factor_with_label-2",
+ "factor_with_ep-1",
+ "factor_with_ep-2",
+ "factor_with_aug-1",
+ "factor_with_aug-2",
+ "factor_with_aug-3",
+ "factor_with_aug-4",
+ "factor_with_aug-5",
+ "factor_with_aug-6",
+ "factor_with_aug-7",
+ "factor_with_aug-8",
+ "factor_with_aug-9",
+ "factor_with_aug-10",
+ "factor_with_aug-11",
+ "aug_type_base-1",
+ "aug_type_base-2",
+ "aug_type_base-3",
+ "aug_type_base-4",
+ "aug_type_cond-1",
+ "aug_type_cond-2",
+ "aug_type_cond-3",
+ "aug_type_cond-4",
+ "aug_type_cond-5",
+ "aug_type_cond-6",
+ "aug_type_cond-7",
+ "aug_type_to_state-1",
+ "aug_type_to_state-2",
+ "aug_type_to_state-3",
+ "aug_type_to_state-4",
+ "aug_type_to_state-5",
+ "aug_type_to_state-6",
+ "aug_type_to_state-7",
+ "aug_type_to_state-8",
+ "aug_type_to_state-9",
+ "aug_type_to_state-10",
+ "aug_type_to_state-11",
+ "aug_type_to_state-12",
+ "aug_type_from_state-1",
+ "aug_type_from_state-2",
+ "aug_type_from_state-3",
+ "aug_type_from_state-4",
+ "aug_type_from_state-5",
+ "aug_type_from_state-6",
+ "aug_type_from_state-7",
+ "aug_type_from_state-8",
+ "aug_type_from_state-9",
+ "aug_type_from_state-10",
+ "aug_type_from_state-11",
+ "aug_type_from_state-12",
+ "aug_type_eof-1",
+ "aug_type_eof-2",
+ "aug_type_eof-3",
+ "aug_type_eof-4",
+ "aug_type_eof-5",
+ "aug_type_eof-6",
+ "aug_type_eof-7",
+ "aug_type_eof-8",
+ "aug_type_eof-9",
+ "aug_type_eof-10",
+ "aug_type_eof-11",
+ "aug_type_eof-12",
+ "aug_type_gbl_error-1",
+ "aug_type_gbl_error-2",
+ "aug_type_gbl_error-3",
+ "aug_type_gbl_error-4",
+ "aug_type_gbl_error-5",
+ "aug_type_gbl_error-6",
+ "aug_type_gbl_error-7",
+ "aug_type_gbl_error-8",
+ "aug_type_gbl_error-9",
+ "aug_type_gbl_error-10",
+ "aug_type_gbl_error-11",
+ "aug_type_gbl_error-12",
+ "aug_type_local_error-1",
+ "aug_type_local_error-2",
+ "aug_type_local_error-3",
+ "aug_type_local_error-4",
+ "aug_type_local_error-5",
+ "aug_type_local_error-6",
+ "aug_type_local_error-7",
+ "aug_type_local_error-8",
+ "aug_type_local_error-9",
+ "aug_type_local_error-10",
+ "aug_type_local_error-11",
+ "aug_type_local_error-12",
+ "action_embed-1",
+ "action_embed-2",
+ "action_embed-3",
+ "action_embed_word-1",
+ "action_embed_block-1",
+ "priority_name-1",
+ "priority_aug-1",
+ "priority_aug_num-1",
+ "priority_aug_num-2",
+ "priority_aug_num-3",
+ "local_err_name-1",
+ "factor_with_rep-1",
+ "factor_with_rep-2",
+ "factor_with_rep-3",
+ "factor_with_rep-4",
+ "factor_with_rep-5",
+ "factor_with_rep-6",
+ "factor_with_rep-7",
+ "factor_with_rep-8",
+ "factor_with_rep-9",
+ "factor_rep_num-1",
+ "factor_with_neg-1",
+ "factor_with_neg-2",
+ "factor_with_neg-3",
+ "factor-1",
+ "factor-2",
+ "factor-3",
+ "factor-4",
+ "factor-5",
+ "factor-6",
+ "factor-7",
+ "factor-8",
+ "range_lit-1",
+ "range_lit-2",
+ "alphabet_num-1",
+ "alphabet_num-2",
+ "alphabet_num-3",
+ "regular_expr-1",
+ "regular_expr-2",
+ "regular_expr_item-1",
+ "regular_expr_item-2",
+ "regular_expr_char-1",
+ "regular_expr_char-2",
+ "regular_expr_char-3",
+ "regular_expr_char-4",
+ "regular_expr_or_data-1",
+ "regular_expr_or_data-2",
+ "regular_expr_or_char-1",
+ "regular_expr_or_char-2",
+ "inline_block-1",
+ "inline_block-2",
+ "inline_block_item-1",
+ "inline_block_item-2",
+ "inline_block_item-3",
+ "inline_block_symbol-1",
+ "inline_block_symbol-2",
+ "inline_block_symbol-3",
+ "inline_block_symbol-4",
+ "inline_block_symbol-5",
+ "inline_block_symbol-6",
+ "inline_block_interpret-1",
+ "inline_block_interpret-2",
+ "inline_block_interpret-3",
+ "inline_block_interpret-4",
+ "inline_block_interpret-5",
+ "inline_block_interpret-6",
+ "inline_block_interpret-7",
+ "inline_block_interpret-8",
+ "inline_block_interpret-9",
+ "inline_block_interpret-10",
+ "inline_block_interpret-11",
+ "inline_expr-1",
+ "inline_expr-2",
+ "inline_expr_item-1",
+ "inline_expr_item-2",
+ "inline_expr_item-3",
+ "inline_expr_any-1",
+ "inline_expr_any-2",
+ "inline_expr_any-3",
+ "inline_expr_any-4",
+ "inline_expr_any-5",
+ "inline_expr_any-6",
+ "inline_expr_any-7",
+ "inline_expr_symbol-1",
+ "inline_expr_symbol-2",
+ "inline_expr_symbol-3",
+ "inline_expr_symbol-4",
+ "inline_expr_symbol-5",
+ "inline_expr_interpret-1",
+ "inline_expr_interpret-2",
+ "inline_expr_interpret-3",
+ "inline_expr_interpret-4",
+ "inline_expr_interpret-5",
+ "local_state_ref-1",
+ "no_name_sep-1",
+ "state_ref-1",
+ "opt_name_sep-1",
+ "opt_name_sep-2",
+ "state_ref_names-1",
+ "state_ref_names-2",
+ "_start-1"
+};
+
+const char *Parser_lelNames[] = {
+ "D-0",
+ "D-1",
+ "D-2",
+ "D-3",
+ "D-4",
+ "D-5",
+ "D-6",
+ "D-7",
+ "D-8",
+ "D-9",
+ "D-10",
+ "D-11",
+ "D-12",
+ "D-13",
+ "D-14",
+ "D-15",
+ "D-16",
+ "D-17",
+ "D-18",
+ "D-19",
+ "D-20",
+ "D-21",
+ "D-22",
+ "D-23",
+ "D-24",
+ "D-25",
+ "D-26",
+ "D-27",
+ "D-28",
+ "D-29",
+ "D-30",
+ "D-31",
+ "D-32",
+ "!",
+ "\"",
+ "#",
+ "$",
+ "%",
+ "&",
+ "'",
+ "(",
+ ")",
+ "*",
+ "+",
+ ",",
+ "-",
+ ".",
+ "/",
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ ":",
+ ";",
+ "<",
+ "=",
+ ">",
+ "?",
+ "@",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "[",
+ "\\",
+ "]",
+ "^",
+ "_",
+ "`",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "{",
+ "|",
+ "}",
+ "~",
+ "D-127",
+ "KW_Machine",
+ "KW_Include",
+ "KW_Import",
+ "KW_Write",
+ "TK_Word",
+ "TK_Literal",
+ "TK_Number",
+ "TK_Inline",
+ "TK_Reference",
+ "TK_ColonEquals",
+ "TK_EndSection",
+ "TK_UInt",
+ "TK_Hex",
+ "TK_BaseClause",
+ "TK_DotDot",
+ "TK_ColonGt",
+ "TK_ColonGtGt",
+ "TK_LtColon",
+ "TK_Arrow",
+ "TK_DoubleArrow",
+ "TK_StarStar",
+ "TK_NameSep",
+ "TK_BarStar",
+ "TK_DashDash",
+ "TK_StartCond",
+ "TK_AllCond",
+ "TK_LeavingCond",
+ "TK_Middle",
+ "TK_StartGblError",
+ "TK_AllGblError",
+ "TK_FinalGblError",
+ "TK_NotFinalGblError",
+ "TK_NotStartGblError",
+ "TK_MiddleGblError",
+ "TK_StartLocalError",
+ "TK_AllLocalError",
+ "TK_FinalLocalError",
+ "TK_NotFinalLocalError",
+ "TK_NotStartLocalError",
+ "TK_MiddleLocalError",
+ "TK_StartEOF",
+ "TK_AllEOF",
+ "TK_FinalEOF",
+ "TK_NotFinalEOF",
+ "TK_NotStartEOF",
+ "TK_MiddleEOF",
+ "TK_StartToState",
+ "TK_AllToState",
+ "TK_FinalToState",
+ "TK_NotFinalToState",
+ "TK_NotStartToState",
+ "TK_MiddleToState",
+ "TK_StartFromState",
+ "TK_AllFromState",
+ "TK_FinalFromState",
+ "TK_NotFinalFromState",
+ "TK_NotStartFromState",
+ "TK_MiddleFromState",
+ "RE_Slash",
+ "RE_SqOpen",
+ "RE_SqOpenNeg",
+ "RE_SqClose",
+ "RE_Dot",
+ "RE_Star",
+ "RE_Dash",
+ "RE_Char",
+ "IL_WhiteSpace",
+ "IL_Comment",
+ "IL_Literal",
+ "IL_Symbol",
+ "KW_Action",
+ "KW_AlphType",
+ "KW_Range",
+ "KW_GetKey",
+ "KW_When",
+ "KW_Eof",
+ "KW_Err",
+ "KW_Lerr",
+ "KW_To",
+ "KW_From",
+ "KW_Export",
+ "KW_Break",
+ "KW_Exec",
+ "KW_Hold",
+ "KW_PChar",
+ "KW_Char",
+ "KW_Goto",
+ "KW_Call",
+ "KW_Ret",
+ "KW_CurState",
+ "KW_TargState",
+ "KW_Entry",
+ "KW_Next",
+ "KW_Variable",
+ "KW_Access",
+ "TK_Semi",
+ "_eof",
+ "section_list",
+ "start",
+ "statement_list",
+ "statement",
+ "assignment",
+ "instantiation",
+ "action_spec",
+ "alphtype_spec",
+ "range_spec",
+ "getkey_spec",
+ "access_spec",
+ "variable_spec",
+ "export_block",
+ "export_open",
+ "opt_export",
+ "machine_name",
+ "join",
+ "join_or_lm",
+ "inline_block",
+ "alphabet_num",
+ "inline_expr",
+ "opt_whitespace",
+ "lm_part_list",
+ "longest_match_part",
+ "opt_lm_part_action",
+ "action_embed",
+ "action_embed_block",
+ "expression",
+ "term",
+ "factor_with_label",
+ "factor_with_ep",
+ "local_state_ref",
+ "factor_with_aug",
+ "aug_type_base",
+ "priority_aug",
+ "priority_name",
+ "aug_type_cond",
+ "aug_type_to_state",
+ "aug_type_from_state",
+ "aug_type_eof",
+ "aug_type_gbl_error",
+ "aug_type_local_error",
+ "local_err_name",
+ "factor_with_rep",
+ "action_embed_word",
+ "priority_aug_num",
+ "factor_rep_num",
+ "factor_with_neg",
+ "factor",
+ "regular_expr_or_data",
+ "regular_expr",
+ "range_lit",
+ "regular_expr_item",
+ "regular_expr_char",
+ "regular_expr_or_char",
+ "inline_block_item",
+ "inline_block_interpret",
+ "inline_expr_any",
+ "inline_block_symbol",
+ "inline_expr_interpret",
+ "state_ref",
+ "inline_expr_item",
+ "inline_expr_symbol",
+ "no_name_sep",
+ "state_ref_names",
+ "opt_name_sep",
+ "_start"
+};
+
+#line 1375 "rlparse.kl"
+
+
+void Parser::init()
+{
+ #line 3769 "rlparse.cpp"
+ curs = Parser_startState;
+ pool = 0;
+ freshEl = (struct Parser_LangEl*) malloc( sizeof(struct Parser_LangEl)*8128);
+ #ifdef LOG_ACTIONS
+ cerr << "allocating 8128 LangEls" << endl;
+ #endif
+ stackTop = freshEl;
+ stackTop->type = 0;
+ stackTop->state = -1;
+ stackTop->next = 0;
+ stackTop->child = 0;
+ freshPos = 1;
+ lastFinal = stackTop;
+ numRetry = 0;
+ numNodes = 0;
+ errCount = 0;
+#line 1380 "rlparse.kl"
+}
+
+int Parser::parseLangEl( int type, const Token *token )
+{
+ #line 3791 "rlparse.cpp"
+#define reject() induceReject = 1
+
+ int pos, targState;
+ unsigned int *action;
+ int rhsLen;
+ struct Parser_LangEl *rhs[32];
+ struct Parser_LangEl *lel;
+ struct Parser_LangEl *input;
+ char induceReject;
+
+ if ( curs < 0 )
+ return 0;
+
+ if ( pool == 0 ) {
+ if ( freshPos == 8128 ) {
+ freshEl = (struct Parser_LangEl*) malloc(
+ sizeof(struct Parser_LangEl)*8128);
+ #ifdef LOG_ACTIONS
+ cerr << "allocating 8128 LangEls" << endl;
+ #endif
+ freshPos = 0;
+ }
+ input = freshEl + freshPos++;
+ }
+ else {
+ input = pool;
+ pool = pool->next;
+ }
+ numNodes += 1;
+ input->type = type;
+ input->user.token = *token;
+ input->next = 0;
+ input->retry = 0;
+ input->child = 0;
+
+again:
+ if ( input == 0 )
+ goto _out;
+
+ lel = input;
+ if ( lel->type < Parser_keys[curs<<1] || lel->type > Parser_keys[(curs<<1)+1] )
+ goto parseError;
+
+ pos = Parser_indicies[Parser_offsets[curs] + (lel->type - Parser_keys[curs<<1])];
+ if ( pos < 0 )
+ goto parseError;
+
+ induceReject = 0;
+ targState = Parser_targs[pos];
+ action = Parser_actions + Parser_actInds[pos];
+ if ( lel->retry & 0x0000ffff )
+ action += (lel->retry & 0x0000ffff);
+
+ if ( *action & 0x1 ) {
+ #ifdef LOG_ACTIONS
+ cerr << "shifted: " << Parser_lelNames[lel->type];
+ #endif
+ input = input->next;
+ lel->state = curs;
+ lel->next = stackTop;
+ stackTop = lel;
+
+ if ( action[1] == 0 )
+ lel->retry &= 0xffff0000;
+ else {
+ lel->retry += 1;
+ numRetry += 1;
+ #ifdef LOG_ACTIONS
+ cerr << " retry: " << stackTop;
+ #endif
+ }
+ #ifdef LOG_ACTIONS
+ cerr << endl;
+ #endif
+ }
+
+ if ( Parser_commitLen[pos] != 0 ) {
+ struct Parser_LangEl *commitHead = stackTop;
+ int absCommitLen = Parser_commitLen[pos];
+
+ #ifdef LOG_ACTIONS
+ cerr << "running commit of length: " << Parser_commitLen[pos] << endl;
+ #endif
+
+ if ( absCommitLen < 0 ) {
+ commitHead = commitHead->next;
+ absCommitLen = -1 * absCommitLen;
+ }
+ {
+ struct Parser_LangEl *lel = commitHead;
+ struct Parser_LangEl **cmStack = (struct Parser_LangEl**) malloc( sizeof(struct Parser_LangEl) * numNodes);
+ int n = absCommitLen, depth = 0, sp = 0;
+
+commit_head:
+ if ( lel->retry > 0 ) {
+ if ( lel->retry & 0x0000ffff )
+ numRetry -= 1;
+ if ( lel->retry & 0xffff0000 )
+ numRetry -= 1;
+ lel->retry = 0;
+ }
+
+ /* If depth is > 0 then move over lel freely, otherwise, make
+ * sure that we have not already done n steps down the line. */
+ if ( lel->next != 0 && ( depth > 0 || n > 1 ) ) {
+ cmStack[sp++] = lel;
+ lel = lel->next;
+
+ /* If we are at the top level count the steps down the line. */
+ if ( depth == 0 )
+ n -= 1;
+ goto commit_head;
+ }
+
+commit_reverse:
+ if ( lel->child != 0 ) {
+ cmStack[sp++] = lel;
+ lel = lel->child;
+
+ /* When we move down we need to increment the depth. */
+ depth += 1;
+ goto commit_head;
+ }
+
+commit_upwards:
+ if ( sp > 0 ) {
+ /* Figure out which place to return to. */
+ if ( cmStack[sp-1]->next == lel ) {
+ lel = cmStack[--sp];
+ goto commit_reverse;
+ }
+ else {
+ /* Going back up, adjust the depth. */
+ lel = cmStack[--sp];
+ depth -= 1;
+ goto commit_upwards;
+ }
+ }
+ free( cmStack );
+ }
+ if ( numRetry == 0 ) {
+ #ifdef LOG_ACTIONS
+ cerr << "number of retries is zero, "
+ "executing final actions" << endl;
+ #endif
+ {
+ struct Parser_LangEl *lel = commitHead;
+ struct Parser_LangEl **cmStack = (struct Parser_LangEl**) malloc( sizeof( struct Parser_LangEl) * numNodes);
+ int sp = 0;
+ char doExec = 0;
+
+final_head:
+ if ( lel == lastFinal ) {
+ doExec = 1;
+ goto hit_final;
+ }
+
+ if ( lel->next != 0 ) {
+ cmStack[sp++] = lel;
+ lel = lel->next;
+ goto final_head;
+ }
+
+final_reverse:
+
+ if ( lel->child != 0 ) {
+ cmStack[sp++] = lel;
+ lel = lel->child;
+ goto final_head;
+ }
+
+final_upwards:
+
+ if ( doExec ) {
+{
+ if ( lel->type < 225 ) {
+ }
+ else {
+ struct Parser_LangEl *redLel = lel;
+ if ( redLel->child != 0 ) {
+ int r = Parser_fssProdLengths[redLel->reduction] - 1;
+ struct Parser_LangEl *rhsEl = redLel->child;
+ while ( rhsEl != 0 ) {
+ rhs[r--] = rhsEl;
+ rhsEl = rhsEl->next;
+ }
+ }
+switch ( lel->reduction ) {
+case 14: {
+#line 59 "rlparse.kl"
+
+ exportContext.append( true );
+
+
+#line 3985 "rlparse.cpp"
+} break;
+case 15: {
+#line 68 "rlparse.kl"
+ (&redLel->user.opt_export)->isSet = true;
+
+#line 3991 "rlparse.cpp"
+} break;
+case 16: {
+#line 69 "rlparse.kl"
+ (&redLel->user.opt_export)->isSet = false;
+
+#line 3997 "rlparse.cpp"
+} break;
+case 17: {
+#line 72 "rlparse.kl"
+
+ exportContext.remove( exportContext.length()-1 );
+
+
+#line 4005 "rlparse.cpp"
+} break;
+case 18: {
+#line 77 "rlparse.kl"
+
+ /* Main machine must be an instance. */
+ bool isInstance = false;
+ if ( strcmp((&rhs[1]->user.token_type)->token.data, mainMachine) == 0 ) {
+ warning((&rhs[1]->user.token_type)->token.loc) <<
+ "main machine will be implicitly instantiated" << endl;
+ isInstance = true;
+ }
+
+ /* Generic creation of machine for instantiation and assignment. */
+ JoinOrLm *joinOrLm = new JoinOrLm( (&rhs[3]->user.join)->join );
+ tryMachineDef( (&rhs[1]->user.token_type)->token.loc, (&rhs[1]->user.token_type)->token.data, joinOrLm, isInstance );
+
+ if ( (&rhs[0]->user.opt_export)->isSet )
+ exportContext.remove( exportContext.length()-1 );
+
+
+#line 4026 "rlparse.cpp"
+} break;
+case 19: {
+#line 95 "rlparse.kl"
+
+ /* Generic creation of machine for instantiation and assignment. */
+ tryMachineDef( (&rhs[1]->user.token_type)->token.loc, (&rhs[1]->user.token_type)->token.data, (&rhs[3]->user.join_or_lm)->joinOrLm, true );
+
+ if ( (&rhs[0]->user.opt_export)->isSet )
+ exportContext.remove( exportContext.length()-1 );
+
+
+#line 4038 "rlparse.cpp"
+} break;
+case 20: {
+#line 111 "rlparse.kl"
+
+ /* Make/get the priority key. The name may have already been referenced
+ * and therefore exist. */
+ PriorDictEl *priorDictEl;
+ if ( pd->priorDict.insert( (&rhs[0]->user.token)->data, pd->nextPriorKey, &priorDictEl ) )
+ pd->nextPriorKey += 1;
+ pd->curDefPriorKey = priorDictEl->value;
+
+ /* Make/get the local error key. */
+ LocalErrDictEl *localErrDictEl;
+ if ( pd->localErrDict.insert( (&rhs[0]->user.token)->data, pd->nextLocalErrKey, &localErrDictEl ) )
+ pd->nextLocalErrKey += 1;
+ pd->curDefLocalErrKey = localErrDictEl->value;
+
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+
+#line 4059 "rlparse.cpp"
+} break;
+case 21: {
+#line 129 "rlparse.kl"
+
+ if ( pd->actionDict.find( (&rhs[1]->user.token)->data ) ) {
+ /* Recover by just ignoring the duplicate. */
+ error((&rhs[1]->user.token)->loc) << "action \"" << (&rhs[1]->user.token)->data << "\" already defined" << endl;
+ }
+ else {
+ //cerr << "NEW ACTION " << $2->data << " " << $4->inlineList << endl;
+ /* Add the action to the list of actions. */
+ Action *newAction = new Action( (&rhs[2]->user.token)->loc, (&rhs[1]->user.token)->data,
+ (&rhs[3]->user.inline_list)->inlineList, pd->nextCondId++ );
+
+ /* Insert to list and dict. */
+ pd->actionList.append( newAction );
+ pd->actionDict.insert( newAction );
+ }
+
+
+#line 4080 "rlparse.cpp"
+} break;
+case 22: {
+#line 149 "rlparse.kl"
+
+ if ( ! pd->setAlphType( (&rhs[1]->user.token)->data, (&rhs[2]->user.token)->data ) ) {
+ // Recover by ignoring the alphtype statement.
+ error((&rhs[1]->user.token)->loc) << "\"" << (&rhs[1]->user.token)->data <<
+ " " << (&rhs[2]->user.token)->data << "\" is not a valid alphabet type" << endl;
+ }
+
+
+#line 4092 "rlparse.cpp"
+} break;
+case 23: {
+#line 158 "rlparse.kl"
+
+ if ( ! pd->setAlphType( (&rhs[1]->user.token)->data ) ) {
+ // Recover by ignoring the alphtype statement.
+ error((&rhs[1]->user.token)->loc) << "\"" << (&rhs[1]->user.token)->data <<
+ "\" is not a valid alphabet type" << endl;
+ }
+
+
+#line 4104 "rlparse.cpp"
+} break;
+case 24: {
+#line 168 "rlparse.kl"
+
+ // Save the upper and lower ends of the range and emit the line number.
+ pd->lowerNum = (&rhs[1]->user.token_type)->token.data;
+ pd->upperNum = (&rhs[2]->user.token_type)->token.data;
+ pd->rangeLowLoc = (&rhs[1]->user.token_type)->token.loc;
+ pd->rangeHighLoc = (&rhs[2]->user.token_type)->token.loc;
+
+
+#line 4116 "rlparse.cpp"
+} break;
+case 25: {
+#line 177 "rlparse.kl"
+
+ pd->getKeyExpr = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 4124 "rlparse.cpp"
+} break;
+case 26: {
+#line 182 "rlparse.kl"
+
+ pd->accessExpr = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 4132 "rlparse.cpp"
+} break;
+case 27: {
+#line 187 "rlparse.kl"
+
+ /* FIXME: Need to implement the rest of this. */
+ if ( strcmp( (&rhs[2]->user.token)->data, "curstate" ) == 0 )
+ pd->curStateExpr = (&rhs[3]->user.inline_list)->inlineList;
+ else {
+ error((&rhs[2]->user.token)->loc) << "sorry, unimplementd" << endl;
+ }
+
+
+#line 4145 "rlparse.cpp"
+} break;
+case 30: {
+#line 209 "rlparse.kl"
+
+ (&redLel->user.join_or_lm)->joinOrLm = new JoinOrLm( (&rhs[0]->user.join)->join );
+
+
+#line 4153 "rlparse.cpp"
+} break;
+case 31: {
+#line 213 "rlparse.kl"
+
+ /* Create a new factor going to a longest match structure. Record
+ * in the parse data that we have a longest match. */
+ LongestMatch *lm = new LongestMatch( (&rhs[0]->user.token)->loc, (&rhs[1]->user.lm_part_list)->lmPartList );
+ pd->lmList.append( lm );
+ for ( LmPartList::Iter lmp = *((&rhs[1]->user.lm_part_list)->lmPartList); lmp.lte(); lmp++ )
+ lmp->longestMatch = lm;
+ (&redLel->user.join_or_lm)->joinOrLm = new JoinOrLm( lm );
+
+
+#line 4167 "rlparse.cpp"
+} break;
+case 32: {
+#line 229 "rlparse.kl"
+
+ if ( (&rhs[1]->user.longest_match_part)->lmPart != 0 )
+ (&rhs[0]->user.lm_part_list)->lmPartList->append( (&rhs[1]->user.longest_match_part)->lmPart );
+ (&redLel->user.lm_part_list)->lmPartList = (&rhs[0]->user.lm_part_list)->lmPartList;
+
+
+#line 4177 "rlparse.cpp"
+} break;
+case 33: {
+#line 235 "rlparse.kl"
+
+ /* Create a new list with the part. */
+ (&redLel->user.lm_part_list)->lmPartList = new LmPartList;
+ if ( (&rhs[0]->user.longest_match_part)->lmPart != 0 )
+ (&redLel->user.lm_part_list)->lmPartList->append( (&rhs[0]->user.longest_match_part)->lmPart );
+
+
+#line 4188 "rlparse.cpp"
+} break;
+case 34: {
+#line 248 "rlparse.kl"
+ (&redLel->user.longest_match_part)->lmPart = 0;
+
+#line 4194 "rlparse.cpp"
+} break;
+case 35: {
+#line 250 "rlparse.kl"
+ (&redLel->user.longest_match_part)->lmPart = 0;
+
+#line 4200 "rlparse.cpp"
+} break;
+case 36: {
+#line 252 "rlparse.kl"
+
+ (&redLel->user.longest_match_part)->lmPart = 0;
+ Action *action = (&rhs[1]->user.opt_lm_part_action)->action;
+ if ( action != 0 )
+ action->isLmAction = true;
+ (&redLel->user.longest_match_part)->lmPart = new LongestMatchPart( (&rhs[0]->user.join)->join, action,
+ (&rhs[2]->user.token)->loc, pd->nextLongestMatchId++ );
+
+
+#line 4213 "rlparse.cpp"
+} break;
+case 37: {
+#line 267 "rlparse.kl"
+
+ (&redLel->user.opt_lm_part_action)->action = (&rhs[1]->user.action_ref)->action;
+
+
+#line 4221 "rlparse.cpp"
+} break;
+case 38: {
+#line 271 "rlparse.kl"
+
+ (&redLel->user.opt_lm_part_action)->action = (&rhs[0]->user.action_ref)->action;
+
+
+#line 4229 "rlparse.cpp"
+} break;
+case 39: {
+#line 275 "rlparse.kl"
+
+ (&redLel->user.opt_lm_part_action)->action = 0;
+
+
+#line 4237 "rlparse.cpp"
+} break;
+case 40: {
+#line 286 "rlparse.kl"
+
+ /* Append the expression to the list and return it. */
+ (&rhs[0]->user.join)->join->exprList.append( (&rhs[2]->user.expression)->expression );
+ (&redLel->user.join)->join = (&rhs[0]->user.join)->join;
+
+
+#line 4247 "rlparse.cpp"
+} break;
+case 41: {
+#line 292 "rlparse.kl"
+
+ (&redLel->user.join)->join = new Join( (&rhs[0]->user.expression)->expression );
+
+
+#line 4255 "rlparse.cpp"
+} break;
+case 42: {
+#line 302 "rlparse.kl"
+
+ (&redLel->user.expression)->expression = new Expression( (&rhs[0]->user.expression)->expression,
+ (&rhs[2]->user.term)->term, Expression::OrType );
+
+
+#line 4264 "rlparse.cpp"
+} break;
+case 43: {
+#line 307 "rlparse.kl"
+
+ (&redLel->user.expression)->expression = new Expression( (&rhs[0]->user.expression)->expression,
+ (&rhs[2]->user.term)->term, Expression::IntersectType );
+
+
+#line 4273 "rlparse.cpp"
+} break;
+case 44: {
+#line 314 "rlparse.kl"
+
+ (&redLel->user.expression)->expression = new Expression( (&rhs[0]->user.expression)->expression,
+ (&rhs[2]->user.term)->term, Expression::SubtractType );
+
+
+#line 4282 "rlparse.cpp"
+} break;
+case 45: {
+#line 319 "rlparse.kl"
+
+ (&redLel->user.expression)->expression = new Expression( (&rhs[0]->user.expression)->expression,
+ (&rhs[2]->user.term)->term, Expression::StrongSubtractType );
+
+
+#line 4291 "rlparse.cpp"
+} break;
+case 46: {
+#line 324 "rlparse.kl"
+
+ (&redLel->user.expression)->expression = new Expression( (&rhs[0]->user.term)->term );
+
+
+#line 4299 "rlparse.cpp"
+} break;
+case 47: {
+#line 334 "rlparse.kl"
+
+ (&redLel->user.term)->term = new Term( (&rhs[0]->user.term)->term, (&rhs[1]->user.factor_with_label)->factorWithAug );
+
+
+#line 4307 "rlparse.cpp"
+} break;
+case 48: {
+#line 338 "rlparse.kl"
+
+ (&redLel->user.term)->term = new Term( (&rhs[0]->user.term)->term, (&rhs[2]->user.factor_with_label)->factorWithAug );
+
+
+#line 4315 "rlparse.cpp"
+} break;
+case 49: {
+#line 342 "rlparse.kl"
+
+ (&redLel->user.term)->term = new Term( (&rhs[0]->user.term)->term, (&rhs[2]->user.factor_with_label)->factorWithAug, Term::RightStartType );
+
+
+#line 4323 "rlparse.cpp"
+} break;
+case 50: {
+#line 346 "rlparse.kl"
+
+ (&redLel->user.term)->term = new Term( (&rhs[0]->user.term)->term, (&rhs[2]->user.factor_with_label)->factorWithAug, Term::RightFinishType );
+
+
+#line 4331 "rlparse.cpp"
+} break;
+case 51: {
+#line 350 "rlparse.kl"
+
+ (&redLel->user.term)->term = new Term( (&rhs[0]->user.term)->term,
+ (&rhs[2]->user.factor_with_label)->factorWithAug, Term::LeftType );
+
+
+#line 4340 "rlparse.cpp"
+} break;
+case 52: {
+#line 355 "rlparse.kl"
+
+ (&redLel->user.term)->term = new Term( (&rhs[0]->user.factor_with_label)->factorWithAug );
+
+
+#line 4348 "rlparse.cpp"
+} break;
+case 53: {
+#line 365 "rlparse.kl"
+
+ /* Add the label to the list and pass the factor up. */
+ (&rhs[2]->user.factor_with_label)->factorWithAug->labels.prepend( Label((&rhs[0]->user.token)->loc, (&rhs[0]->user.token)->data) );
+ (&redLel->user.factor_with_label)->factorWithAug = (&rhs[2]->user.factor_with_label)->factorWithAug;
+
+
+#line 4358 "rlparse.cpp"
+} break;
+case 54: {
+#line 371 "rlparse.kl"
+
+ (&redLel->user.factor_with_label)->factorWithAug = (&rhs[0]->user.factor_with_ep)->factorWithAug;
+
+
+#line 4366 "rlparse.cpp"
+} break;
+case 55: {
+#line 381 "rlparse.kl"
+
+ /* Add the target to the list and return the factor object. */
+ (&rhs[0]->user.factor_with_ep)->factorWithAug->epsilonLinks.append( EpsilonLink( (&rhs[1]->user.token)->loc, nameRef ) );
+ (&redLel->user.factor_with_ep)->factorWithAug = (&rhs[0]->user.factor_with_ep)->factorWithAug;
+
+
+#line 4376 "rlparse.cpp"
+} break;
+case 56: {
+#line 387 "rlparse.kl"
+
+ (&redLel->user.factor_with_ep)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4384 "rlparse.cpp"
+} break;
+case 57: {
+#line 397 "rlparse.kl"
+
+ /* Append the action to the factorWithAug, record the refernce from
+ * factorWithAug to the action and pass up the factorWithAug. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->actions.append(
+ ParserAction( (&rhs[1]->user.aug_type)->loc, (&rhs[1]->user.aug_type)->augType, 0, (&rhs[2]->user.action_ref)->action ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4396 "rlparse.cpp"
+} break;
+case 58: {
+#line 405 "rlparse.kl"
+
+ /* Append the named priority to the factorWithAug and pass it up. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->priorityAugs.append(
+ PriorityAug( (&rhs[1]->user.aug_type)->augType, pd->curDefPriorKey, (&rhs[2]->user.priority_aug)->priorityNum ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4407 "rlparse.cpp"
+} break;
+case 59: {
+#line 412 "rlparse.kl"
+
+ /* Append the priority using a default name. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->priorityAugs.append(
+ PriorityAug( (&rhs[1]->user.aug_type)->augType, (&rhs[3]->user.priority_name)->priorityName, (&rhs[5]->user.priority_aug)->priorityNum ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4418 "rlparse.cpp"
+} break;
+case 60: {
+#line 419 "rlparse.kl"
+
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->conditions.append( ParserAction( (&rhs[1]->user.aug_type)->loc,
+ (&rhs[1]->user.aug_type)->augType, 0, (&rhs[2]->user.action_ref)->action ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4428 "rlparse.cpp"
+} break;
+case 61: {
+#line 425 "rlparse.kl"
+
+ /* Append the action, pass it up. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->actions.append( ParserAction( (&rhs[1]->user.aug_type)->loc,
+ (&rhs[1]->user.aug_type)->augType, 0, (&rhs[2]->user.action_ref)->action ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4439 "rlparse.cpp"
+} break;
+case 62: {
+#line 432 "rlparse.kl"
+
+ /* Append the action, pass it up. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->actions.append( ParserAction( (&rhs[1]->user.aug_type)->loc,
+ (&rhs[1]->user.aug_type)->augType, 0, (&rhs[2]->user.action_ref)->action ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4450 "rlparse.cpp"
+} break;
+case 63: {
+#line 439 "rlparse.kl"
+
+ /* Append the action, pass it up. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->actions.append( ParserAction( (&rhs[1]->user.aug_type)->loc,
+ (&rhs[1]->user.aug_type)->augType, 0, (&rhs[2]->user.action_ref)->action ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4461 "rlparse.cpp"
+} break;
+case 64: {
+#line 446 "rlparse.kl"
+
+ /* Append the action to the factorWithAug, record the refernce from
+ * factorWithAug to the action and pass up the factorWithAug. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->actions.append( ParserAction( (&rhs[1]->user.aug_type)->loc,
+ (&rhs[1]->user.aug_type)->augType, pd->curDefLocalErrKey, (&rhs[2]->user.action_ref)->action ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4473 "rlparse.cpp"
+} break;
+case 65: {
+#line 454 "rlparse.kl"
+
+ /* Append the action to the factorWithAug, record the refernce from
+ * factorWithAug to the action and pass up the factorWithAug. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->actions.append( ParserAction( (&rhs[1]->user.aug_type)->loc,
+ (&rhs[1]->user.aug_type)->augType, pd->curDefLocalErrKey, (&rhs[2]->user.action_ref)->action ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4485 "rlparse.cpp"
+} break;
+case 66: {
+#line 462 "rlparse.kl"
+
+ /* Append the action to the factorWithAug, record the refernce from
+ * factorWithAug to the action and pass up the factorWithAug. */
+ (&rhs[0]->user.factor_with_aug)->factorWithAug->actions.append( ParserAction( (&rhs[1]->user.aug_type)->loc,
+ (&rhs[1]->user.aug_type)->augType, (&rhs[3]->user.local_err_name)->error_name, (&rhs[5]->user.action_ref)->action ) );
+ (&redLel->user.factor_with_aug)->factorWithAug = (&rhs[0]->user.factor_with_aug)->factorWithAug;
+
+
+#line 4497 "rlparse.cpp"
+} break;
+case 67: {
+#line 470 "rlparse.kl"
+
+ (&redLel->user.factor_with_aug)->factorWithAug = new FactorWithAug( (&rhs[0]->user.factor_with_rep)->factorWithRep );
+
+
+#line 4505 "rlparse.cpp"
+} break;
+case 68: {
+#line 483 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_finish;
+
+#line 4511 "rlparse.cpp"
+} break;
+case 69: {
+#line 484 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_leave;
+
+#line 4517 "rlparse.cpp"
+} break;
+case 70: {
+#line 485 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all;
+
+#line 4523 "rlparse.cpp"
+} break;
+case 71: {
+#line 486 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start;
+
+#line 4529 "rlparse.cpp"
+} break;
+case 72: {
+#line 491 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start;
+
+#line 4535 "rlparse.cpp"
+} break;
+case 73: {
+#line 492 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start;
+
+#line 4541 "rlparse.cpp"
+} break;
+case 74: {
+#line 493 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all;
+
+#line 4547 "rlparse.cpp"
+} break;
+case 75: {
+#line 494 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all;
+
+#line 4553 "rlparse.cpp"
+} break;
+case 76: {
+#line 495 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_leave;
+
+#line 4559 "rlparse.cpp"
+} break;
+case 77: {
+#line 496 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_leave;
+
+#line 4565 "rlparse.cpp"
+} break;
+case 78: {
+#line 497 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all;
+
+#line 4571 "rlparse.cpp"
+} break;
+case 79: {
+#line 506 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_to_state;
+
+#line 4577 "rlparse.cpp"
+} break;
+case 80: {
+#line 508 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_to_state;
+
+#line 4583 "rlparse.cpp"
+} break;
+case 81: {
+#line 511 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_to_state;
+
+#line 4589 "rlparse.cpp"
+} break;
+case 82: {
+#line 513 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_to_state;
+
+#line 4595 "rlparse.cpp"
+} break;
+case 83: {
+#line 516 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_to_state;
+
+#line 4601 "rlparse.cpp"
+} break;
+case 84: {
+#line 518 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_to_state;
+
+#line 4607 "rlparse.cpp"
+} break;
+case 85: {
+#line 521 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_to_state;
+
+#line 4613 "rlparse.cpp"
+} break;
+case 86: {
+#line 523 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_to_state;
+
+#line 4619 "rlparse.cpp"
+} break;
+case 87: {
+#line 526 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_to_state;
+
+#line 4625 "rlparse.cpp"
+} break;
+case 88: {
+#line 528 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_to_state;
+
+#line 4631 "rlparse.cpp"
+} break;
+case 89: {
+#line 531 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_to_state;
+
+#line 4637 "rlparse.cpp"
+} break;
+case 90: {
+#line 533 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_to_state;
+
+#line 4643 "rlparse.cpp"
+} break;
+case 91: {
+#line 542 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_from_state;
+
+#line 4649 "rlparse.cpp"
+} break;
+case 92: {
+#line 544 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_from_state;
+
+#line 4655 "rlparse.cpp"
+} break;
+case 93: {
+#line 547 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_from_state;
+
+#line 4661 "rlparse.cpp"
+} break;
+case 94: {
+#line 549 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_from_state;
+
+#line 4667 "rlparse.cpp"
+} break;
+case 95: {
+#line 552 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_from_state;
+
+#line 4673 "rlparse.cpp"
+} break;
+case 96: {
+#line 554 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_from_state;
+
+#line 4679 "rlparse.cpp"
+} break;
+case 97: {
+#line 557 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_from_state;
+
+#line 4685 "rlparse.cpp"
+} break;
+case 98: {
+#line 559 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_from_state;
+
+#line 4691 "rlparse.cpp"
+} break;
+case 99: {
+#line 562 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_from_state;
+
+#line 4697 "rlparse.cpp"
+} break;
+case 100: {
+#line 564 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_from_state;
+
+#line 4703 "rlparse.cpp"
+} break;
+case 101: {
+#line 567 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_from_state;
+
+#line 4709 "rlparse.cpp"
+} break;
+case 102: {
+#line 569 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_from_state;
+
+#line 4715 "rlparse.cpp"
+} break;
+case 103: {
+#line 578 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_eof;
+
+#line 4721 "rlparse.cpp"
+} break;
+case 104: {
+#line 580 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_eof;
+
+#line 4727 "rlparse.cpp"
+} break;
+case 105: {
+#line 583 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_eof;
+
+#line 4733 "rlparse.cpp"
+} break;
+case 106: {
+#line 585 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_eof;
+
+#line 4739 "rlparse.cpp"
+} break;
+case 107: {
+#line 588 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_eof;
+
+#line 4745 "rlparse.cpp"
+} break;
+case 108: {
+#line 590 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_eof;
+
+#line 4751 "rlparse.cpp"
+} break;
+case 109: {
+#line 593 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_eof;
+
+#line 4757 "rlparse.cpp"
+} break;
+case 110: {
+#line 595 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_eof;
+
+#line 4763 "rlparse.cpp"
+} break;
+case 111: {
+#line 598 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_eof;
+
+#line 4769 "rlparse.cpp"
+} break;
+case 112: {
+#line 600 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_eof;
+
+#line 4775 "rlparse.cpp"
+} break;
+case 113: {
+#line 603 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_eof;
+
+#line 4781 "rlparse.cpp"
+} break;
+case 114: {
+#line 605 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_eof;
+
+#line 4787 "rlparse.cpp"
+} break;
+case 115: {
+#line 614 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_gbl_error;
+
+#line 4793 "rlparse.cpp"
+} break;
+case 116: {
+#line 616 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_gbl_error;
+
+#line 4799 "rlparse.cpp"
+} break;
+case 117: {
+#line 619 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_gbl_error;
+
+#line 4805 "rlparse.cpp"
+} break;
+case 118: {
+#line 621 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_gbl_error;
+
+#line 4811 "rlparse.cpp"
+} break;
+case 119: {
+#line 624 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_gbl_error;
+
+#line 4817 "rlparse.cpp"
+} break;
+case 120: {
+#line 626 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_gbl_error;
+
+#line 4823 "rlparse.cpp"
+} break;
+case 121: {
+#line 629 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_gbl_error;
+
+#line 4829 "rlparse.cpp"
+} break;
+case 122: {
+#line 631 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_gbl_error;
+
+#line 4835 "rlparse.cpp"
+} break;
+case 123: {
+#line 634 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_gbl_error;
+
+#line 4841 "rlparse.cpp"
+} break;
+case 124: {
+#line 636 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_gbl_error;
+
+#line 4847 "rlparse.cpp"
+} break;
+case 125: {
+#line 639 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_gbl_error;
+
+#line 4853 "rlparse.cpp"
+} break;
+case 126: {
+#line 641 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_gbl_error;
+
+#line 4859 "rlparse.cpp"
+} break;
+case 127: {
+#line 651 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_local_error;
+
+#line 4865 "rlparse.cpp"
+} break;
+case 128: {
+#line 653 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_start_local_error;
+
+#line 4871 "rlparse.cpp"
+} break;
+case 129: {
+#line 656 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_local_error;
+
+#line 4877 "rlparse.cpp"
+} break;
+case 130: {
+#line 658 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_start_local_error;
+
+#line 4883 "rlparse.cpp"
+} break;
+case 131: {
+#line 661 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_local_error;
+
+#line 4889 "rlparse.cpp"
+} break;
+case 132: {
+#line 663 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_all_local_error;
+
+#line 4895 "rlparse.cpp"
+} break;
+case 133: {
+#line 666 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_local_error;
+
+#line 4901 "rlparse.cpp"
+} break;
+case 134: {
+#line 668 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_final_local_error;
+
+#line 4907 "rlparse.cpp"
+} break;
+case 135: {
+#line 671 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_local_error;
+
+#line 4913 "rlparse.cpp"
+} break;
+case 136: {
+#line 673 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_not_final_local_error;
+
+#line 4919 "rlparse.cpp"
+} break;
+case 137: {
+#line 676 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_local_error;
+
+#line 4925 "rlparse.cpp"
+} break;
+case 138: {
+#line 678 "rlparse.kl"
+ (&redLel->user.aug_type)->loc = (&rhs[0]->user.token)->loc; (&redLel->user.aug_type)->augType = at_middle_local_error;
+
+#line 4931 "rlparse.cpp"
+} break;
+case 139: {
+#line 691 "rlparse.kl"
+ (&redLel->user.action_ref)->action = (&rhs[0]->user.action_ref)->action;
+
+#line 4937 "rlparse.cpp"
+} break;
+case 140: {
+#line 692 "rlparse.kl"
+ (&redLel->user.action_ref)->action = (&rhs[1]->user.action_ref)->action;
+
+#line 4943 "rlparse.cpp"
+} break;
+case 141: {
+#line 693 "rlparse.kl"
+ (&redLel->user.action_ref)->action = (&rhs[0]->user.action_ref)->action;
+
+#line 4949 "rlparse.cpp"
+} break;
+case 142: {
+#line 698 "rlparse.kl"
+
+ /* Set the name in the actionDict. */
+ Action *action = pd->actionDict.find( (&rhs[0]->user.token)->data );
+ if ( action != 0 ) {
+ /* Pass up the action element */
+ (&redLel->user.action_ref)->action = action;
+ }
+ else {
+ /* Will recover by returning null as the action. */
+ error((&rhs[0]->user.token)->loc) << "action lookup of \"" << (&rhs[0]->user.token)->data << "\" failed" << endl;
+ (&redLel->user.action_ref)->action = 0;
+ }
+
+
+#line 4967 "rlparse.cpp"
+} break;
+case 143: {
+#line 715 "rlparse.kl"
+
+ /* Create the action, add it to the list and pass up. */
+ Action *newAction = new Action( (&rhs[0]->user.token)->loc, 0, (&rhs[1]->user.inline_list)->inlineList, pd->nextCondId++ );
+ pd->actionList.append( newAction );
+ (&redLel->user.action_ref)->action = newAction;
+
+
+#line 4978 "rlparse.cpp"
+} break;
+case 144: {
+#line 730 "rlparse.kl"
+
+ // Lookup/create the priority key.
+ PriorDictEl *priorDictEl;
+ if ( pd->priorDict.insert( (&rhs[0]->user.token)->data, pd->nextPriorKey, &priorDictEl ) )
+ pd->nextPriorKey += 1;
+
+ // Use the inserted/found priority key.
+ (&redLel->user.priority_name)->priorityName = priorDictEl->value;
+
+
+#line 4992 "rlparse.cpp"
+} break;
+case 145: {
+#line 747 "rlparse.kl"
+
+ // Convert the priority number to a long. Check for overflow.
+ errno = 0;
+ //cerr << "PRIOR AUG: " << $1->token.data << endl;
+ long aug = strtol( (&rhs[0]->user.token_type)->token.data, 0, 10 );
+ if ( errno == ERANGE && aug == LONG_MAX ) {
+ /* Priority number too large. Recover by setting the priority to 0. */
+ error((&rhs[0]->user.token_type)->token.loc) << "priority number " << (&rhs[0]->user.token_type)->token.data <<
+ " overflows" << endl;
+ (&redLel->user.priority_aug)->priorityNum = 0;
+ }
+ else if ( errno == ERANGE && aug == LONG_MIN ) {
+ /* Priority number too large in the neg. Recover by using 0. */
+ error((&rhs[0]->user.token_type)->token.loc) << "priority number " << (&rhs[0]->user.token_type)->token.data <<
+ " underflows" << endl;
+ (&redLel->user.priority_aug)->priorityNum = 0;
+ }
+ else {
+ /* No overflow or underflow. */
+ (&redLel->user.priority_aug)->priorityNum = aug;
+ }
+
+
+#line 5019 "rlparse.cpp"
+} break;
+case 146: {
+#line 773 "rlparse.kl"
+
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+
+#line 5027 "rlparse.cpp"
+} break;
+case 147: {
+#line 777 "rlparse.kl"
+
+ (&redLel->user.token_type)->token.set( "+", 1 );
+ (&redLel->user.token_type)->token.loc = (&rhs[0]->user.token)->loc;
+ (&redLel->user.token_type)->token.append( *(&rhs[1]->user.token) );
+
+
+#line 5037 "rlparse.cpp"
+} break;
+case 148: {
+#line 783 "rlparse.kl"
+
+ (&redLel->user.token_type)->token.set( "-", 1 );
+ (&redLel->user.token_type)->token.loc = (&rhs[0]->user.token)->loc;
+ (&redLel->user.token_type)->token.append( *(&rhs[1]->user.token) );
+
+
+#line 5047 "rlparse.cpp"
+} break;
+case 149: {
+#line 795 "rlparse.kl"
+
+ /* Lookup/create the priority key. */
+ LocalErrDictEl *localErrDictEl;
+ if ( pd->localErrDict.insert( (&rhs[0]->user.token)->data, pd->nextLocalErrKey, &localErrDictEl ) )
+ pd->nextLocalErrKey += 1;
+
+ /* Use the inserted/found priority key. */
+ (&redLel->user.local_err_name)->error_name = localErrDictEl->value;
+
+
+#line 5061 "rlparse.cpp"
+} break;
+case 150: {
+#line 816 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[1]->user.token)->loc, (&rhs[0]->user.factor_with_rep)->factorWithRep,
+ 0, 0, FactorWithRep::StarType );
+
+
+#line 5070 "rlparse.cpp"
+} break;
+case 151: {
+#line 821 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[1]->user.token)->loc, (&rhs[0]->user.factor_with_rep)->factorWithRep,
+ 0, 0, FactorWithRep::StarStarType );
+
+
+#line 5079 "rlparse.cpp"
+} break;
+case 152: {
+#line 826 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[1]->user.token)->loc, (&rhs[0]->user.factor_with_rep)->factorWithRep,
+ 0, 0, FactorWithRep::OptionalType );
+
+
+#line 5088 "rlparse.cpp"
+} break;
+case 153: {
+#line 831 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[1]->user.token)->loc, (&rhs[0]->user.factor_with_rep)->factorWithRep,
+ 0, 0, FactorWithRep::PlusType );
+
+
+#line 5097 "rlparse.cpp"
+} break;
+case 154: {
+#line 836 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[1]->user.token)->loc, (&rhs[0]->user.factor_with_rep)->factorWithRep,
+ (&rhs[2]->user.factor_rep_num)->rep, 0, FactorWithRep::ExactType );
+
+
+#line 5106 "rlparse.cpp"
+} break;
+case 155: {
+#line 841 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[1]->user.token)->loc, (&rhs[0]->user.factor_with_rep)->factorWithRep,
+ 0, (&rhs[3]->user.factor_rep_num)->rep, FactorWithRep::MaxType );
+
+
+#line 5115 "rlparse.cpp"
+} break;
+case 156: {
+#line 846 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[1]->user.token)->loc, (&rhs[0]->user.factor_with_rep)->factorWithRep,
+ (&rhs[2]->user.factor_rep_num)->rep, 0, FactorWithRep::MinType );
+
+
+#line 5124 "rlparse.cpp"
+} break;
+case 157: {
+#line 851 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[1]->user.token)->loc, (&rhs[0]->user.factor_with_rep)->factorWithRep,
+ (&rhs[2]->user.factor_rep_num)->rep, (&rhs[4]->user.factor_rep_num)->rep, FactorWithRep::RangeType );
+
+
+#line 5133 "rlparse.cpp"
+} break;
+case 158: {
+#line 856 "rlparse.kl"
+
+ (&redLel->user.factor_with_rep)->factorWithRep = new FactorWithRep( (&rhs[0]->user.factor_with_neg)->factorWithNeg );
+
+
+#line 5141 "rlparse.cpp"
+} break;
+case 159: {
+#line 866 "rlparse.kl"
+
+ // Convert the priority number to a long. Check for overflow.
+ errno = 0;
+ long rep = strtol( (&rhs[0]->user.token)->data, 0, 10 );
+ if ( errno == ERANGE && rep == LONG_MAX ) {
+ // Repetition too large. Recover by returing repetition 1. */
+ error((&rhs[0]->user.token)->loc) << "repetition number " << (&rhs[0]->user.token)->data << " overflows" << endl;
+ (&redLel->user.factor_rep_num)->rep = 1;
+ }
+ else {
+ // Cannot be negative, so no overflow.
+ (&redLel->user.factor_rep_num)->rep = rep;
+ }
+
+
+#line 5160 "rlparse.cpp"
+} break;
+case 160: {
+#line 892 "rlparse.kl"
+
+ (&redLel->user.factor_with_neg)->factorWithNeg = new FactorWithNeg( (&rhs[0]->user.token)->loc,
+ (&rhs[1]->user.factor_with_neg)->factorWithNeg, FactorWithNeg::NegateType );
+
+
+#line 5169 "rlparse.cpp"
+} break;
+case 161: {
+#line 897 "rlparse.kl"
+
+ (&redLel->user.factor_with_neg)->factorWithNeg = new FactorWithNeg( (&rhs[0]->user.token)->loc,
+ (&rhs[1]->user.factor_with_neg)->factorWithNeg, FactorWithNeg::CharNegateType );
+
+
+#line 5178 "rlparse.cpp"
+} break;
+case 162: {
+#line 902 "rlparse.kl"
+
+ (&redLel->user.factor_with_neg)->factorWithNeg = new FactorWithNeg( (&rhs[0]->user.factor)->factor );
+
+
+#line 5186 "rlparse.cpp"
+} break;
+case 163: {
+#line 912 "rlparse.kl"
+
+ /* Create a new factor node going to a concat literal. */
+ (&redLel->user.factor)->factor = new Factor( new Literal( *(&rhs[0]->user.token), Literal::LitString ) );
+
+
+#line 5195 "rlparse.cpp"
+} break;
+case 164: {
+#line 917 "rlparse.kl"
+
+ /* Create a new factor node going to a literal number. */
+ (&redLel->user.factor)->factor = new Factor( new Literal( (&rhs[0]->user.token_type)->token, Literal::Number ) );
+
+
+#line 5204 "rlparse.cpp"
+} break;
+case 165: {
+#line 922 "rlparse.kl"
+
+ /* Find the named graph. */
+ GraphDictEl *gdNode = pd->graphDict.find( (&rhs[0]->user.token)->data );
+ if ( gdNode == 0 ) {
+ /* Recover by returning null as the factor node. */
+ error((&rhs[0]->user.token)->loc) << "graph lookup of \"" << (&rhs[0]->user.token)->data << "\" failed" << endl;
+ (&redLel->user.factor)->factor = 0;
+ }
+ else if ( gdNode->isInstance ) {
+ /* Recover by retuning null as the factor node. */
+ error((&rhs[0]->user.token)->loc) << "references to graph instantiations not allowed "
+ "in expressions" << endl;
+ (&redLel->user.factor)->factor = 0;
+ }
+ else {
+ /* Create a factor node that is a lookup of an expression. */
+ (&redLel->user.factor)->factor = new Factor( (&rhs[0]->user.token)->loc, gdNode->value );
+ }
+
+
+#line 5228 "rlparse.cpp"
+} break;
+case 166: {
+#line 942 "rlparse.kl"
+
+ /* Create a new factor node going to an OR expression. */
+ (&redLel->user.factor)->factor = new Factor( new ReItem( (&rhs[0]->user.token)->loc, (&rhs[1]->user.regular_expr_or_data)->reOrBlock, ReItem::OrBlock ) );
+
+
+#line 5237 "rlparse.cpp"
+} break;
+case 167: {
+#line 947 "rlparse.kl"
+
+ /* Create a new factor node going to a negated OR expression. */
+ (&redLel->user.factor)->factor = new Factor( new ReItem( (&rhs[0]->user.token)->loc, (&rhs[1]->user.regular_expr_or_data)->reOrBlock, ReItem::NegOrBlock ) );
+
+
+#line 5246 "rlparse.cpp"
+} break;
+case 168: {
+#line 952 "rlparse.kl"
+
+ if ( (&rhs[2]->user.token)->length > 1 ) {
+ for ( char *p = (&rhs[2]->user.token)->data; *p != 0; p++ ) {
+ if ( *p == 'i' )
+ (&rhs[1]->user.regular_expr)->regExpr->caseInsensitive = true;
+ }
+ }
+
+ /* Create a new factor node going to a regular exp. */
+ (&redLel->user.factor)->factor = new Factor( (&rhs[1]->user.regular_expr)->regExpr );
+
+
+#line 5262 "rlparse.cpp"
+} break;
+case 169: {
+#line 964 "rlparse.kl"
+
+ /* Create a new factor node going to a range. */
+ (&redLel->user.factor)->factor = new Factor( new Range( (&rhs[0]->user.range_lit)->literal, (&rhs[2]->user.range_lit)->literal ) );
+
+
+#line 5271 "rlparse.cpp"
+} break;
+case 170: {
+#line 969 "rlparse.kl"
+
+ /* Create a new factor going to a parenthesized join. */
+ (&redLel->user.factor)->factor = new Factor( (&rhs[1]->user.join)->join );
+
+
+#line 5280 "rlparse.cpp"
+} break;
+case 171: {
+#line 981 "rlparse.kl"
+
+ /* Range literas must have only one char. We restrict this in the parse tree. */
+ (&redLel->user.range_lit)->literal = new Literal( *(&rhs[0]->user.token), Literal::LitString );
+
+
+#line 5289 "rlparse.cpp"
+} break;
+case 172: {
+#line 986 "rlparse.kl"
+
+ /* Create a new literal number. */
+ (&redLel->user.range_lit)->literal = new Literal( (&rhs[0]->user.token_type)->token, Literal::Number );
+
+
+#line 5298 "rlparse.cpp"
+} break;
+case 173: {
+#line 995 "rlparse.kl"
+
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+
+#line 5306 "rlparse.cpp"
+} break;
+case 174: {
+#line 999 "rlparse.kl"
+
+ (&redLel->user.token_type)->token.set( "-", 1 );
+ (&redLel->user.token_type)->token.loc = (&rhs[0]->user.token)->loc;
+ (&redLel->user.token_type)->token.append( *(&rhs[1]->user.token) );
+
+
+#line 5316 "rlparse.cpp"
+} break;
+case 175: {
+#line 1005 "rlparse.kl"
+
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+
+#line 5324 "rlparse.cpp"
+} break;
+case 176: {
+#line 1020 "rlparse.kl"
+
+ /* An optimization to lessen the tree size. If a non-starred char is
+ * directly under the left side on the right and the right side is
+ * another non-starred char then paste them together and return the
+ * left side. Otherwise just put the two under a new reg exp node. */
+ if ( (&rhs[1]->user.regular_expr_item)->reItem->type == ReItem::Data && !(&rhs[1]->user.regular_expr_item)->reItem->star &&
+ (&rhs[0]->user.regular_expr)->regExpr->type == RegExpr::RecurseItem &&
+ (&rhs[0]->user.regular_expr)->regExpr->item->type == ReItem::Data && !(&rhs[0]->user.regular_expr)->regExpr->item->star )
+ {
+ /* Append the right side to the right side of the left and toss the
+ * right side. */
+ (&rhs[0]->user.regular_expr)->regExpr->item->token.append( (&rhs[1]->user.regular_expr_item)->reItem->token );
+ delete (&rhs[1]->user.regular_expr_item)->reItem;
+ (&redLel->user.regular_expr)->regExpr = (&rhs[0]->user.regular_expr)->regExpr;
+ }
+ else {
+ (&redLel->user.regular_expr)->regExpr = new RegExpr( (&rhs[0]->user.regular_expr)->regExpr, (&rhs[1]->user.regular_expr_item)->reItem );
+ }
+
+
+#line 5348 "rlparse.cpp"
+} break;
+case 177: {
+#line 1040 "rlparse.kl"
+
+ /* Can't optimize the tree. */
+ (&redLel->user.regular_expr)->regExpr = new RegExpr();
+
+
+#line 5357 "rlparse.cpp"
+} break;
+case 178: {
+#line 1052 "rlparse.kl"
+
+ (&rhs[0]->user.regular_expr_char)->reItem->star = true;
+ (&redLel->user.regular_expr_item)->reItem = (&rhs[0]->user.regular_expr_char)->reItem;
+
+
+#line 5366 "rlparse.cpp"
+} break;
+case 179: {
+#line 1057 "rlparse.kl"
+
+ (&redLel->user.regular_expr_item)->reItem = (&rhs[0]->user.regular_expr_char)->reItem;
+
+
+#line 5374 "rlparse.cpp"
+} break;
+case 180: {
+#line 1069 "rlparse.kl"
+
+ (&redLel->user.regular_expr_char)->reItem = new ReItem( (&rhs[0]->user.token)->loc, (&rhs[1]->user.regular_expr_or_data)->reOrBlock, ReItem::OrBlock );
+
+
+#line 5382 "rlparse.cpp"
+} break;
+case 181: {
+#line 1073 "rlparse.kl"
+
+ (&redLel->user.regular_expr_char)->reItem = new ReItem( (&rhs[0]->user.token)->loc, (&rhs[1]->user.regular_expr_or_data)->reOrBlock, ReItem::NegOrBlock );
+
+
+#line 5390 "rlparse.cpp"
+} break;
+case 182: {
+#line 1077 "rlparse.kl"
+
+ (&redLel->user.regular_expr_char)->reItem = new ReItem( (&rhs[0]->user.token)->loc, ReItem::Dot );
+
+
+#line 5398 "rlparse.cpp"
+} break;
+case 183: {
+#line 1081 "rlparse.kl"
+
+ (&redLel->user.regular_expr_char)->reItem = new ReItem( (&rhs[0]->user.token)->loc, *(&rhs[0]->user.token) );
+
+
+#line 5406 "rlparse.cpp"
+} break;
+case 184: {
+#line 1093 "rlparse.kl"
+
+ /* An optimization to lessen the tree size. If an or char is directly
+ * under the left side on the right and the right side is another or
+ * char then paste them together and return the left side. Otherwise
+ * just put the two under a new or data node. */
+ if ( (&rhs[1]->user.regular_expr_or_char)->reOrItem->type == ReOrItem::Data &&
+ (&rhs[0]->user.regular_expr_or_data)->reOrBlock->type == ReOrBlock::RecurseItem &&
+ (&rhs[0]->user.regular_expr_or_data)->reOrBlock->item->type == ReOrItem::Data )
+ {
+ /* Append the right side to right side of the left and toss the
+ * right side. */
+ (&rhs[0]->user.regular_expr_or_data)->reOrBlock->item->token.append( (&rhs[1]->user.regular_expr_or_char)->reOrItem->token );
+ delete (&rhs[1]->user.regular_expr_or_char)->reOrItem;
+ (&redLel->user.regular_expr_or_data)->reOrBlock = (&rhs[0]->user.regular_expr_or_data)->reOrBlock;
+ }
+ else {
+ /* Can't optimize, put the left and right under a new node. */
+ (&redLel->user.regular_expr_or_data)->reOrBlock = new ReOrBlock( (&rhs[0]->user.regular_expr_or_data)->reOrBlock, (&rhs[1]->user.regular_expr_or_char)->reOrItem );
+ }
+
+
+#line 5431 "rlparse.cpp"
+} break;
+case 185: {
+#line 1114 "rlparse.kl"
+
+ (&redLel->user.regular_expr_or_data)->reOrBlock = new ReOrBlock();
+
+
+#line 5439 "rlparse.cpp"
+} break;
+case 186: {
+#line 1126 "rlparse.kl"
+
+ (&redLel->user.regular_expr_or_char)->reOrItem = new ReOrItem( (&rhs[0]->user.token)->loc, *(&rhs[0]->user.token) );
+
+
+#line 5447 "rlparse.cpp"
+} break;
+case 187: {
+#line 1130 "rlparse.kl"
+
+ (&redLel->user.regular_expr_or_char)->reOrItem = new ReOrItem( (&rhs[1]->user.token)->loc, (&rhs[0]->user.token)->data[0], (&rhs[2]->user.token)->data[0] );
+
+
+#line 5455 "rlparse.cpp"
+} break;
+case 188: {
+#line 1147 "rlparse.kl"
+
+ /* Append the item to the list, return the list. */
+ (&redLel->user.inline_list)->inlineList = (&rhs[0]->user.inline_list)->inlineList;
+ (&redLel->user.inline_list)->inlineList->append( (&rhs[1]->user.inline_item)->inlineItem );
+
+
+#line 5465 "rlparse.cpp"
+} break;
+case 189: {
+#line 1154 "rlparse.kl"
+
+ /* Start with empty list. */
+ (&redLel->user.inline_list)->inlineList = new InlineList;
+
+
+#line 5474 "rlparse.cpp"
+} break;
+case 190: {
+#line 1169 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token_type)->token.loc, (&rhs[0]->user.token_type)->token.data, InlineItem::Text );
+
+
+#line 5482 "rlparse.cpp"
+} break;
+case 191: {
+#line 1175 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token_type)->token.loc, (&rhs[0]->user.token_type)->token.data, InlineItem::Text );
+
+
+#line 5490 "rlparse.cpp"
+} break;
+case 192: {
+#line 1181 "rlparse.kl"
+
+ /* Pass the inline item up. */
+ (&redLel->user.inline_item)->inlineItem = (&rhs[0]->user.inline_item)->inlineItem;
+
+
+#line 5499 "rlparse.cpp"
+} break;
+case 193: {
+#line 1188 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5505 "rlparse.cpp"
+} break;
+case 194: {
+#line 1189 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5511 "rlparse.cpp"
+} break;
+case 195: {
+#line 1190 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5517 "rlparse.cpp"
+} break;
+case 196: {
+#line 1191 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5523 "rlparse.cpp"
+} break;
+case 197: {
+#line 1192 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5529 "rlparse.cpp"
+} break;
+case 198: {
+#line 1193 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5535 "rlparse.cpp"
+} break;
+case 199: {
+#line 1197 "rlparse.kl"
+
+ /* Pass up interpreted items of inline expressions. */
+ (&redLel->user.inline_item)->inlineItem = (&rhs[0]->user.inline_item)->inlineItem;
+
+
+#line 5544 "rlparse.cpp"
+} break;
+case 200: {
+#line 1202 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::Hold );
+
+
+#line 5552 "rlparse.cpp"
+} break;
+case 201: {
+#line 1206 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::Exec );
+ (&redLel->user.inline_item)->inlineItem->children = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 5561 "rlparse.cpp"
+} break;
+case 202: {
+#line 1211 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc,
+ new NameRef(nameRef), InlineItem::Goto );
+
+
+#line 5570 "rlparse.cpp"
+} break;
+case 203: {
+#line 1216 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::GotoExpr );
+ (&redLel->user.inline_item)->inlineItem->children = (&rhs[2]->user.inline_list)->inlineList;
+
+
+#line 5579 "rlparse.cpp"
+} break;
+case 204: {
+#line 1221 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, new NameRef(nameRef), InlineItem::Next );
+
+
+#line 5587 "rlparse.cpp"
+} break;
+case 205: {
+#line 1225 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::NextExpr );
+ (&redLel->user.inline_item)->inlineItem->children = (&rhs[2]->user.inline_list)->inlineList;
+
+
+#line 5596 "rlparse.cpp"
+} break;
+case 206: {
+#line 1230 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, new NameRef(nameRef), InlineItem::Call );
+
+
+#line 5604 "rlparse.cpp"
+} break;
+case 207: {
+#line 1234 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::CallExpr );
+ (&redLel->user.inline_item)->inlineItem->children = (&rhs[2]->user.inline_list)->inlineList;
+
+
+#line 5613 "rlparse.cpp"
+} break;
+case 208: {
+#line 1239 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::Ret );
+
+
+#line 5621 "rlparse.cpp"
+} break;
+case 209: {
+#line 1243 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::Break );
+
+
+#line 5629 "rlparse.cpp"
+} break;
+case 210: {
+#line 1251 "rlparse.kl"
+
+ (&redLel->user.inline_list)->inlineList = (&rhs[0]->user.inline_list)->inlineList;
+ (&redLel->user.inline_list)->inlineList->append( (&rhs[1]->user.inline_item)->inlineItem );
+
+
+#line 5638 "rlparse.cpp"
+} break;
+case 211: {
+#line 1256 "rlparse.kl"
+
+ /* Init the list used for this expr. */
+ (&redLel->user.inline_list)->inlineList = new InlineList;
+
+
+#line 5647 "rlparse.cpp"
+} break;
+case 212: {
+#line 1265 "rlparse.kl"
+
+ /* Return a text segment. */
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token_type)->token.loc, (&rhs[0]->user.token_type)->token.data, InlineItem::Text );
+
+
+#line 5656 "rlparse.cpp"
+} break;
+case 213: {
+#line 1271 "rlparse.kl"
+
+ /* Return a text segment, must heap alloc the text. */
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token_type)->token.loc, (&rhs[0]->user.token_type)->token.data, InlineItem::Text );
+
+
+#line 5665 "rlparse.cpp"
+} break;
+case 214: {
+#line 1277 "rlparse.kl"
+
+ /* Pass the inline item up. */
+ (&redLel->user.inline_item)->inlineItem = (&rhs[0]->user.inline_item)->inlineItem;
+
+
+#line 5674 "rlparse.cpp"
+} break;
+case 227: {
+#line 1307 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::PChar );
+
+
+#line 5682 "rlparse.cpp"
+} break;
+case 228: {
+#line 1312 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::Char );
+
+
+#line 5690 "rlparse.cpp"
+} break;
+case 229: {
+#line 1317 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::Curs );
+
+
+#line 5698 "rlparse.cpp"
+} break;
+case 230: {
+#line 1322 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc, InlineItem::Targs );
+
+
+#line 5706 "rlparse.cpp"
+} break;
+case 231: {
+#line 1327 "rlparse.kl"
+
+ (&redLel->user.inline_item)->inlineItem = new InlineItem( (&rhs[0]->user.token)->loc,
+ new NameRef(nameRef), InlineItem::Entry );
+
+
+#line 5715 "rlparse.cpp"
+} break;
+case 233: {
+#line 1338 "rlparse.kl"
+
+ nameRef.empty();
+
+
+#line 5723 "rlparse.cpp"
+} break;
+case 235: {
+#line 1348 "rlparse.kl"
+
+ /* Insert an initial null pointer val to indicate the existence of the
+ * initial name seperator. */
+ nameRef.setAs( 0 );
+
+
+#line 5733 "rlparse.cpp"
+} break;
+case 236: {
+#line 1354 "rlparse.kl"
+
+ nameRef.empty();
+
+
+#line 5741 "rlparse.cpp"
+} break;
+case 237: {
+#line 1361 "rlparse.kl"
+
+ nameRef.append( (&rhs[2]->user.token)->data );
+
+
+#line 5749 "rlparse.cpp"
+} break;
+case 238: {
+#line 1366 "rlparse.kl"
+
+ nameRef.append( (&rhs[0]->user.token)->data );
+
+
+#line 5757 "rlparse.cpp"
+} break;
+}
+ }
+}
+
+ if ( lel->child != 0 ) {
+ struct Parser_LangEl *first = lel->child;
+ struct Parser_LangEl *child = lel->child;
+ numNodes -= 1;
+ lel->child = 0;
+ while ( child->next != 0 ) {
+ child = child->next;
+ numNodes -= 1;
+ }
+ child->next = pool;
+ pool = first;
+ }
+ }
+
+hit_final:
+ if ( sp > 0 ) {
+ /* Figure out which place to return to. */
+ if ( cmStack[sp-1]->next == lel ) {
+ lel = cmStack[--sp];
+ goto final_reverse;
+ }
+ else {
+ lel = cmStack[--sp];
+ goto final_upwards;
+ }
+ }
+
+ lastFinal = lel;
+ free( cmStack );
+ }
+ }
+ }
+
+ if ( *action & 0x2 ) {
+ int fssRed = *action >> 2;
+ int reduction = Parser_fssProdIdIndex[fssRed];
+ struct Parser_LangEl *redLel;
+ if ( pool == 0 ) {
+ if ( freshPos == 8128 ) {
+ freshEl = (struct Parser_LangEl*) malloc(
+ sizeof(struct Parser_LangEl)*8128);
+ #ifdef LOG_ACTIONS
+ cerr << "allocating 8128 LangEls" << endl;
+ #endif
+ freshPos = 0;
+ }
+ redLel = freshEl + freshPos++;
+ }
+ else {
+ redLel = pool;
+ pool = pool->next;
+ }
+ numNodes += 1;
+ redLel->type = Parser_prodLhsIds[reduction];
+ redLel->reduction = reduction;
+ redLel->child = 0;
+ redLel->next = 0;
+ redLel->retry = (lel->retry << 16);
+ lel->retry &= 0xffff0000;
+
+ rhsLen = Parser_fssProdLengths[fssRed];
+ if ( rhsLen > 0 ) {
+ int r;
+ for ( r = rhsLen-1; r > 0; r-- ) {
+ rhs[r] = stackTop;
+ stackTop = stackTop->next;
+ }
+ rhs[0] = stackTop;
+ stackTop = stackTop->next;
+ rhs[0]->next = 0;
+ }
+switch ( reduction ) {
+case 215: {
+#line 1284 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5839 "rlparse.cpp"
+} break;
+case 216: {
+#line 1285 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5845 "rlparse.cpp"
+} break;
+case 217: {
+#line 1286 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5851 "rlparse.cpp"
+} break;
+case 218: {
+#line 1287 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5857 "rlparse.cpp"
+} break;
+case 219: {
+#line 1288 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5863 "rlparse.cpp"
+} break;
+case 220: {
+#line 1289 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5869 "rlparse.cpp"
+} break;
+case 221: {
+#line 1290 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5875 "rlparse.cpp"
+} break;
+case 222: {
+#line 1297 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5881 "rlparse.cpp"
+} break;
+case 223: {
+#line 1298 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5887 "rlparse.cpp"
+} break;
+case 224: {
+#line 1299 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5893 "rlparse.cpp"
+} break;
+case 225: {
+#line 1300 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5899 "rlparse.cpp"
+} break;
+case 226: {
+#line 1301 "rlparse.kl"
+ (&redLel->user.token_type)->token = *(&rhs[0]->user.token);
+
+#line 5905 "rlparse.cpp"
+} break;
+}
+ #ifdef LOG_ACTIONS
+ cerr << "reduced: "
+ << Parser_prodNames[reduction]
+ << " rhsLen: " << rhsLen;
+ #endif
+ if ( action[1] == 0 )
+ redLel->retry = 0;
+ else {
+ redLel->retry += 0x10000;
+ numRetry += 1;
+ #ifdef LOG_ACTIONS
+ cerr << " retry: " << redLel;
+ #endif
+ }
+
+ #ifdef LOG_ACTIONS
+ cerr << endl;
+ #endif
+
+ if ( rhsLen == 0 ) {
+ redLel->file = lel->file;
+ redLel->line = lel->line;
+ targState = curs;
+ }
+ else {
+ redLel->child = rhs[rhsLen-1];
+ redLel->file = rhs[0]->file;
+ redLel->line = rhs[0]->line;
+ targState = rhs[0]->state;
+ }
+
+ if ( induceReject ) {
+ #ifdef LOG_ACTIONS
+ cerr << "error induced during reduction of " <<
+ Parser_lelNames[redLel->type] << endl;
+ #endif
+ redLel->state = curs;
+ redLel->next = stackTop;
+ stackTop = redLel;
+ curs = targState;
+ goto parseError;
+ }
+ else {
+ redLel->next = input;
+ input = redLel;
+ }
+ }
+
+
+ curs = targState;
+ goto again;
+
+parseError:
+ #ifdef LOG_BACKTRACK
+ cerr << "hit error" << endl;
+ #endif
+ if ( numRetry > 0 ) {
+ while ( 1 ) {
+ struct Parser_LangEl *redLel = stackTop;
+ if ( stackTop->type < 225 ) {
+ #ifdef LOG_BACKTRACK
+ cerr << "backing up over terminal: " <<
+ Parser_lelNames[stackTop->type] << endl;
+ #endif
+ stackTop = stackTop->next;
+ redLel->next = input;
+ input = redLel;
+ }
+ else {
+ #ifdef LOG_BACKTRACK
+ cerr << "backing up over non-terminal: " <<
+ Parser_lelNames[stackTop->type] << endl;
+ #endif
+ stackTop = stackTop->next;
+ struct Parser_LangEl *first = redLel->child;
+ if ( first == 0 )
+ rhsLen = 0;
+ else {
+ rhsLen = 1;
+ while ( first->next != 0 ) {
+ first = first->next;
+ rhsLen += 1;
+ }
+ first->next = stackTop;
+ stackTop = redLel->child;
+
+ struct Parser_LangEl *rhsEl = stackTop;
+ int p = rhsLen;
+ while ( p > 0 ) {
+ rhs[--p] = rhsEl;
+ rhsEl = rhsEl->next;
+ }
+ }
+ redLel->next = pool;
+ pool = redLel;
+ numNodes -= 1;
+ }
+
+ if ( redLel->retry > 0 ) {
+ #ifdef LOG_BACKTRACK
+ cerr << "found retry targ: " << redLel << endl;
+ #endif
+ numRetry -= 1;
+ #ifdef LOG_BACKTRACK
+ cerr << "found retry: " << redLel << endl;
+ #endif
+ if ( redLel->retry & 0x0000ffff )
+ curs = input->state;
+ else {
+ input->retry = redLel->retry >> 16;
+ if ( stackTop->state < 0 )
+ curs = Parser_startState;
+ else {
+ curs = Parser_targs[(int)Parser_indicies[Parser_offsets[stackTop->state] + (stackTop->type - Parser_keys[stackTop->state<<1])]];
+ }
+ }
+ goto again;
+ }
+ }
+ }
+ curs = -1;
+ errCount += 1;
+_out: {}
+#line 1385 "rlparse.kl"
+ return errCount == 0 ? 0 : -1;
+}
+
+void Parser::tryMachineDef( InputLoc &loc, char *name,
+ JoinOrLm *joinOrLm, bool isInstance )
+{
+ GraphDictEl *newEl = pd->graphDict.insert( name );
+ if ( newEl != 0 ) {
+ /* New element in the dict, all good. */
+ newEl->value = new VarDef( name, joinOrLm );
+ newEl->isInstance = isInstance;
+ newEl->loc = loc;
+ newEl->value->isExport = exportContext[exportContext.length()-1];
+
+ /* It it is an instance, put on the instance list. */
+ if ( isInstance )
+ pd->instanceList.append( newEl );
+ }
+ else {
+ // Recover by ignoring the duplicate.
+ error(loc) << "fsm \"" << name << "\" previously defined" << endl;
+ }
+}
+
+ostream &Parser::parse_error( int tokId, Token &token )
+{
+ /* Maintain the error count. */
+ gblErrorCount += 1;
+
+ cerr << token.loc.fileName << ":" << token.loc.line << ":" << token.loc.col << ": ";
+ cerr << "at token ";
+ if ( tokId < 128 )
+ cerr << "\"" << Parser_lelNames[tokId] << "\"";
+ else
+ cerr << Parser_lelNames[tokId];
+ if ( token.data != 0 )
+ cerr << " with data \"" << token.data << "\"";
+ cerr << ": ";
+
+ return cerr;
+}
+
+int Parser::token( InputLoc &loc, int tokId, char *tokstart, int toklen )
+{
+ Token token;
+ token.data = tokstart;
+ token.length = toklen;
+ token.loc = loc;
+ int res = parseLangEl( tokId, &token );
+ if ( res < 0 ) {
+ parse_error(tokId, token) << "parse error" << endl;
+ exit(1);
+ }
+ return res;
+}
diff --git a/contrib/tools/ragel5/ragel/rlparse.h b/contrib/tools/ragel5/ragel/rlparse.h
new file mode 100644
index 0000000000..957db0fd69
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/rlparse.h
@@ -0,0 +1,184 @@
+/* Automatically generated by Kelbt from "rlparse.kh".
+ *
+ * Parts of this file are copied from Kelbt source covered by the GNU
+ * GPL. As a special exception, you may use the parts of this file copied
+ * from Kelbt source without restriction. The remainder is derived from
+ * "rlparse.kh" and inherits the copyright status of that file.
+ */
+
+#line 1 "rlparse.kh"
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef RLPARSE_H
+#define RLPARSE_H
+
+#include <iostream>
+#include "avltree.h"
+#include "parsedata.h"
+
+struct Parser
+{
+#line 93 "rlparse.kh"
+
+
+ #line 44 "rlparse.h"
+ struct Parser_LangEl *freshEl;
+ int freshPos;
+ struct Parser_LangEl *pool;
+ int numRetry;
+ int numNodes;
+ struct Parser_LangEl *stackTop;
+ struct Parser_LangEl *lastFinal;
+ int errCount;
+ int curs;
+#line 96 "rlparse.kh"
+
+ void init();
+ int parseLangEl( int type, const Token *token );
+
+ Parser(const char *fileName, char *sectionName, InputLoc &sectionLoc )
+ : sectionName(sectionName)
+ {
+ pd = new ParseData( fileName, sectionName, sectionLoc );
+ exportContext.append( false );
+ }
+
+ int token( InputLoc &loc, int tokId, char *tokstart, int toklen );
+ void tryMachineDef( InputLoc &loc, char *name,
+ JoinOrLm *joinOrLm, bool isInstance );
+
+ /* Report an error encountered by the parser. */
+ ostream &parse_error( int tokId, Token &token );
+
+ ParseData *pd;
+
+ /* The name of the root section, this does not change during an include. */
+ char *sectionName;
+
+ NameRef nameRef;
+ NameRefList nameRefList;
+
+ Vector<bool> exportContext;
+};
+
+#line 84 "rlparse.h"
+#define KW_Machine 128
+#define KW_Include 129
+#define KW_Import 130
+#define KW_Write 131
+#define TK_Word 132
+#define TK_Literal 133
+#define TK_Number 134
+#define TK_Inline 135
+#define TK_Reference 136
+#define TK_ColonEquals 137
+#define TK_EndSection 138
+#define TK_UInt 139
+#define TK_Hex 140
+#define TK_BaseClause 141
+#define TK_DotDot 142
+#define TK_ColonGt 143
+#define TK_ColonGtGt 144
+#define TK_LtColon 145
+#define TK_Arrow 146
+#define TK_DoubleArrow 147
+#define TK_StarStar 148
+#define TK_NameSep 149
+#define TK_BarStar 150
+#define TK_DashDash 151
+#define TK_StartCond 152
+#define TK_AllCond 153
+#define TK_LeavingCond 154
+#define TK_Middle 155
+#define TK_StartGblError 156
+#define TK_AllGblError 157
+#define TK_FinalGblError 158
+#define TK_NotFinalGblError 159
+#define TK_NotStartGblError 160
+#define TK_MiddleGblError 161
+#define TK_StartLocalError 162
+#define TK_AllLocalError 163
+#define TK_FinalLocalError 164
+#define TK_NotFinalLocalError 165
+#define TK_NotStartLocalError 166
+#define TK_MiddleLocalError 167
+#define TK_StartEOF 168
+#define TK_AllEOF 169
+#define TK_FinalEOF 170
+#define TK_NotFinalEOF 171
+#define TK_NotStartEOF 172
+#define TK_MiddleEOF 173
+#define TK_StartToState 174
+#define TK_AllToState 175
+#define TK_FinalToState 176
+#define TK_NotFinalToState 177
+#define TK_NotStartToState 178
+#define TK_MiddleToState 179
+#define TK_StartFromState 180
+#define TK_AllFromState 181
+#define TK_FinalFromState 182
+#define TK_NotFinalFromState 183
+#define TK_NotStartFromState 184
+#define TK_MiddleFromState 185
+#define RE_Slash 186
+#define RE_SqOpen 187
+#define RE_SqOpenNeg 188
+#define RE_SqClose 189
+#define RE_Dot 190
+#define RE_Star 191
+#define RE_Dash 192
+#define RE_Char 193
+#define IL_WhiteSpace 194
+#define IL_Comment 195
+#define IL_Literal 196
+#define IL_Symbol 197
+#define KW_Action 198
+#define KW_AlphType 199
+#define KW_Range 200
+#define KW_GetKey 201
+#define KW_When 202
+#define KW_Eof 203
+#define KW_Err 204
+#define KW_Lerr 205
+#define KW_To 206
+#define KW_From 207
+#define KW_Export 208
+#define KW_Break 209
+#define KW_Exec 210
+#define KW_Hold 211
+#define KW_PChar 212
+#define KW_Char 213
+#define KW_Goto 214
+#define KW_Call 215
+#define KW_Ret 216
+#define KW_CurState 217
+#define KW_TargState 218
+#define KW_Entry 219
+#define KW_Next 220
+#define KW_Variable 221
+#define KW_Access 222
+#define TK_Semi 223
+#define _eof 224
+
+#line 126 "rlparse.kh"
+
+#endif
diff --git a/contrib/tools/ragel5/ragel/rlscan.cpp b/contrib/tools/ragel5/ragel/rlscan.cpp
new file mode 100644
index 0000000000..47a7f02148
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/rlscan.cpp
@@ -0,0 +1,4876 @@
+#line 1 "rlscan.rl"
+/*
+ * Copyright 2006-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <fstream>
+#include <string.h>
+
+#include "ragel.h"
+#include "rlscan.h"
+
+//#define LOG_TOKENS
+
+using std::ifstream;
+using std::istream;
+using std::ostream;
+using std::cout;
+using std::cerr;
+using std::endl;
+
+enum InlineBlockType
+{
+ CurlyDelimited,
+ SemiTerminated
+};
+
+
+/*
+ * The Scanner for Importing
+ */
+
+#define IMP_Word 128
+#define IMP_Literal 129
+#define IMP_UInt 130
+#define IMP_Define 131
+
+#line 124 "rlscan.rl"
+
+
+
+#line 60 "rlscan.cpp"
+static const int inline_token_scan_start = 2;
+
+static const int inline_token_scan_first_final = 2;
+
+static const int inline_token_scan_error = -1;
+
+#line 127 "rlscan.rl"
+
+void Scanner::flushImport()
+{
+ int *p = token_data;
+ int *pe = token_data + cur_token;
+
+
+#line 75 "rlscan.cpp"
+ {
+ tok_cs = inline_token_scan_start;
+ tok_tokstart = 0;
+ tok_tokend = 0;
+ tok_act = 0;
+ }
+#line 134 "rlscan.rl"
+
+#line 84 "rlscan.cpp"
+ {
+ if ( p == pe )
+ goto _out;
+ switch ( tok_cs )
+ {
+tr0:
+#line 122 "rlscan.rl"
+ {{p = (( tok_tokend))-1;}}
+ goto st2;
+tr1:
+#line 108 "rlscan.rl"
+ { tok_tokend = p+1;{
+ int base = tok_tokstart - token_data;
+ int nameOff = 0;
+ int litOff = 2;
+
+ directToParser( inclToParser, fileName, line, column, TK_Word,
+ token_strings[base+nameOff], token_lens[base+nameOff] );
+ directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
+ directToParser( inclToParser, fileName, line, column, TK_Literal,
+ token_strings[base+litOff], token_lens[base+litOff] );
+ directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
+ }{p = (( tok_tokend))-1;}}
+ goto st2;
+tr2:
+#line 80 "rlscan.rl"
+ { tok_tokend = p+1;{
+ int base = tok_tokstart - token_data;
+ int nameOff = 0;
+ int numOff = 2;
+
+ directToParser( inclToParser, fileName, line, column, TK_Word,
+ token_strings[base+nameOff], token_lens[base+nameOff] );
+ directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
+ directToParser( inclToParser, fileName, line, column, TK_UInt,
+ token_strings[base+numOff], token_lens[base+numOff] );
+ directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
+ }{p = (( tok_tokend))-1;}}
+ goto st2;
+tr3:
+#line 94 "rlscan.rl"
+ { tok_tokend = p+1;{
+ int base = tok_tokstart - token_data;
+ int nameOff = 1;
+ int litOff = 2;
+
+ directToParser( inclToParser, fileName, line, column, TK_Word,
+ token_strings[base+nameOff], token_lens[base+nameOff] );
+ directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
+ directToParser( inclToParser, fileName, line, column, TK_Literal,
+ token_strings[base+litOff], token_lens[base+litOff] );
+ directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
+ }{p = (( tok_tokend))-1;}}
+ goto st2;
+tr4:
+#line 66 "rlscan.rl"
+ { tok_tokend = p+1;{
+ int base = tok_tokstart - token_data;
+ int nameOff = 1;
+ int numOff = 2;
+
+ directToParser( inclToParser, fileName, line, column, TK_Word,
+ token_strings[base+nameOff], token_lens[base+nameOff] );
+ directToParser( inclToParser, fileName, line, column, '=', 0, 0 );
+ directToParser( inclToParser, fileName, line, column, TK_UInt,
+ token_strings[base+numOff], token_lens[base+numOff] );
+ directToParser( inclToParser, fileName, line, column, ';', 0, 0 );
+ }{p = (( tok_tokend))-1;}}
+ goto st2;
+tr5:
+#line 122 "rlscan.rl"
+ { tok_tokend = p+1;{p = (( tok_tokend))-1;}}
+ goto st2;
+tr8:
+#line 122 "rlscan.rl"
+ { tok_tokend = p;{p = (( tok_tokend))-1;}}
+ goto st2;
+st2:
+#line 1 "rlscan.rl"
+ { tok_tokstart = 0;}
+ if ( ++p == pe )
+ goto _out2;
+case 2:
+#line 1 "rlscan.rl"
+ { tok_tokstart = p;}
+#line 170 "rlscan.cpp"
+ switch( (*p) ) {
+ case 128: goto tr6;
+ case 131: goto tr7;
+ }
+ goto tr5;
+tr6:
+#line 1 "rlscan.rl"
+ { tok_tokend = p+1;}
+ goto st3;
+st3:
+ if ( ++p == pe )
+ goto _out3;
+case 3:
+#line 184 "rlscan.cpp"
+ if ( (*p) == 61 )
+ goto st0;
+ goto tr8;
+st0:
+ if ( ++p == pe )
+ goto _out0;
+case 0:
+ switch( (*p) ) {
+ case 129: goto tr1;
+ case 130: goto tr2;
+ }
+ goto tr0;
+tr7:
+#line 1 "rlscan.rl"
+ { tok_tokend = p+1;}
+ goto st4;
+st4:
+ if ( ++p == pe )
+ goto _out4;
+case 4:
+#line 205 "rlscan.cpp"
+ if ( (*p) == 128 )
+ goto st1;
+ goto tr8;
+st1:
+ if ( ++p == pe )
+ goto _out1;
+case 1:
+ switch( (*p) ) {
+ case 129: goto tr3;
+ case 130: goto tr4;
+ }
+ goto tr0;
+ }
+ _out2: tok_cs = 2; goto _out;
+ _out3: tok_cs = 3; goto _out;
+ _out0: tok_cs = 0; goto _out;
+ _out4: tok_cs = 4; goto _out;
+ _out1: tok_cs = 1; goto _out;
+
+ _out: {}
+ }
+#line 135 "rlscan.rl"
+
+ if ( tok_tokstart == 0 )
+ cur_token = 0;
+ else {
+ cur_token = pe - tok_tokstart;
+ int ts_offset = tok_tokstart - token_data;
+ memmove( token_data, token_data+ts_offset, cur_token*sizeof(token_data[0]) );
+ memmove( token_strings, token_strings+ts_offset, cur_token*sizeof(token_strings[0]) );
+ memmove( token_lens, token_lens+ts_offset, cur_token*sizeof(token_lens[0]) );
+ }
+}
+
+void Scanner::directToParser( Parser *toParser, const char *tokFileName, int tokLine,
+ int tokColumn, int type, char *tokdata, int toklen )
+{
+ InputLoc loc;
+
+ #ifdef LOG_TOKENS
+ cerr << "scanner:" << tokLine << ":" << tokColumn <<
+ ": sending token to the parser " << Parser_lelNames[type];
+ cerr << " " << toklen;
+ if ( tokdata != 0 )
+ cerr << " " << tokdata;
+ cerr << endl;
+ #endif
+
+ loc.fileName = tokFileName;
+ loc.line = tokLine;
+ loc.col = tokColumn;
+
+ toParser->token( loc, type, tokdata, toklen );
+}
+
+void Scanner::importToken( int token, char *start, char *end )
+{
+ if ( cur_token == max_tokens )
+ flushImport();
+
+ token_data[cur_token] = token;
+ if ( start == 0 ) {
+ token_strings[cur_token] = 0;
+ token_lens[cur_token] = 0;
+ }
+ else {
+ int toklen = end-start;
+ token_lens[cur_token] = toklen;
+ token_strings[cur_token] = new char[toklen+1];
+ memcpy( token_strings[cur_token], start, toklen );
+ token_strings[cur_token][toklen] = 0;
+ }
+ cur_token++;
+}
+
+void Scanner::pass( int token, char *start, char *end )
+{
+ if ( importMachines )
+ importToken( token, start, end );
+ pass();
+}
+
+void Scanner::pass()
+{
+ updateCol();
+
+ /* If no errors and we are at the bottom of the include stack (the
+ * source file listed on the command line) then write out the data. */
+ if ( includeDepth == 0 && machineSpec == 0 && machineName == 0 )
+ xmlEscapeHost( output, tokstart, tokend-tokstart );
+}
+
+/*
+ * The scanner for processing sections, includes, imports, etc.
+ */
+
+
+#line 303 "rlscan.cpp"
+static const int section_parse_start = 10;
+
+static const int section_parse_first_final = 10;
+
+static const int section_parse_error = 0;
+
+#line 213 "rlscan.rl"
+
+
+
+void Scanner::init( )
+{
+
+#line 317 "rlscan.cpp"
+ {
+ cs = section_parse_start;
+ }
+#line 219 "rlscan.rl"
+}
+
+bool Scanner::active()
+{
+ if ( ignoreSection )
+ return false;
+
+ if ( parser == 0 && ! parserExistsError ) {
+ scan_error() << "there is no previous specification name" << endl;
+ parserExistsError = true;
+ }
+
+ if ( parser == 0 )
+ return false;
+
+ return true;
+}
+
+ostream &Scanner::scan_error()
+{
+ /* Maintain the error count. */
+ gblErrorCount += 1;
+ cerr << fileName << ":" << line << ":" << column << ": ";
+ return cerr;
+}
+
+bool Scanner::recursiveInclude(const char *inclFileName, char *inclSectionName )
+{
+ for ( IncludeStack::Iter si = includeStack; si.lte(); si++ ) {
+ if ( strcmp( si->fileName, inclFileName ) == 0 &&
+ strcmp( si->sectionName, inclSectionName ) == 0 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Scanner::updateCol()
+{
+ char *from = lastnl;
+ if ( from == 0 )
+ from = tokstart;
+ //cerr << "adding " << tokend - from << " to column" << endl;
+ column += tokend - from;
+ lastnl = 0;
+}
+
+#line 442 "rlscan.rl"
+
+
+void Scanner::token( int type, char c )
+{
+ token( type, &c, &c + 1 );
+}
+
+void Scanner::token( int type )
+{
+ token( type, 0, 0 );
+}
+
+void Scanner::token( int type, char *start, char *end )
+{
+ char *tokdata = 0;
+ int toklen = 0;
+ if ( start != 0 ) {
+ toklen = end-start;
+ tokdata = new char[toklen+1];
+ memcpy( tokdata, start, toklen );
+ tokdata[toklen] = 0;
+ }
+
+ processToken( type, tokdata, toklen );
+}
+
+void Scanner::processToken( int type, char *tokdata, int toklen )
+{
+ int *p = &type;
+ int *pe = &type + 1;
+
+
+#line 403 "rlscan.cpp"
+ {
+ if ( p == pe )
+ goto _out;
+ switch ( cs )
+ {
+tr2:
+#line 289 "rlscan.rl"
+ {
+ /* Assign a name to the machine. */
+ char *machine = word;
+
+ if ( !importMachines && inclSectionTarg == 0 ) {
+ ignoreSection = false;
+
+ ParserDictEl *pdEl = parserDict.find( machine );
+ if ( pdEl == 0 ) {
+ pdEl = new ParserDictEl( machine );
+ pdEl->value = new Parser( fileName, machine, sectionLoc );
+ pdEl->value->init();
+ parserDict.insert( pdEl );
+ }
+
+ parser = pdEl->value;
+ }
+ else if ( !importMachines && strcmp( inclSectionTarg, machine ) == 0 ) {
+ /* found include target */
+ ignoreSection = false;
+ parser = inclToParser;
+ }
+ else {
+ /* ignoring section */
+ ignoreSection = true;
+ parser = 0;
+ }
+ }
+ goto st10;
+tr6:
+#line 323 "rlscan.rl"
+ {
+ if ( active() ) {
+ char *inclSectionName = word;
+ const char *inclFileName = 0;
+
+ /* Implement defaults for the input file and section name. */
+ if ( inclSectionName == 0 )
+ inclSectionName = parser->sectionName;
+
+ if ( lit != 0 )
+ inclFileName = prepareFileName( lit, lit_len );
+ else
+ inclFileName = fileName;
+
+ /* Check for a recursive include structure. Add the current file/section
+ * name then check if what we are including is already in the stack. */
+ includeStack.append( IncludeStackItem( fileName, parser->sectionName ) );
+
+ if ( recursiveInclude( inclFileName, inclSectionName ) )
+ scan_error() << "include: this is a recursive include operation" << endl;
+ else {
+ /* Open the input file for reading. */
+ ifstream *inFile = new ifstream( inclFileName );
+ if ( ! inFile->is_open() ) {
+ scan_error() << "include: could not open " <<
+ inclFileName << " for reading" << endl;
+ }
+
+ Scanner scanner( inclFileName, *inFile, output, parser,
+ inclSectionName, includeDepth+1, false );
+ scanner.do_scan( );
+ delete inFile;
+ }
+
+ /* Remove the last element (len-1) */
+ includeStack.remove( -1 );
+ }
+ }
+ goto st10;
+tr10:
+#line 372 "rlscan.rl"
+ {
+ if ( active() ) {
+ char *importFileName = prepareFileName( lit, lit_len );
+
+ /* Open the input file for reading. */
+ ifstream *inFile = new ifstream( importFileName );
+ if ( ! inFile->is_open() ) {
+ scan_error() << "import: could not open " <<
+ importFileName << " for reading" << endl;
+ }
+
+ Scanner scanner( importFileName, *inFile, output, parser,
+ 0, includeDepth+1, true );
+ scanner.do_scan( );
+ scanner.importToken( 0, 0, 0 );
+ scanner.flushImport();
+ delete inFile;
+ }
+ }
+ goto st10;
+tr13:
+#line 414 "rlscan.rl"
+ {
+ if ( active() && machineSpec == 0 && machineName == 0 )
+ output << "</write>\n";
+ }
+ goto st10;
+tr14:
+#line 425 "rlscan.rl"
+ {
+ /* Send the token off to the parser. */
+ if ( active() )
+ directToParser( parser, fileName, line, column, type, tokdata, toklen );
+ }
+ goto st10;
+st10:
+ if ( ++p == pe )
+ goto _out10;
+case 10:
+#line 522 "rlscan.cpp"
+ switch( (*p) ) {
+ case 128: goto st1;
+ case 129: goto st3;
+ case 130: goto st6;
+ case 131: goto tr18;
+ }
+ goto tr14;
+st1:
+ if ( ++p == pe )
+ goto _out1;
+case 1:
+ if ( (*p) == 132 )
+ goto tr1;
+ goto tr0;
+tr0:
+#line 283 "rlscan.rl"
+ { scan_error() << "bad machine statement" << endl; }
+ goto st0;
+tr3:
+#line 284 "rlscan.rl"
+ { scan_error() << "bad include statement" << endl; }
+ goto st0;
+tr8:
+#line 285 "rlscan.rl"
+ { scan_error() << "bad import statement" << endl; }
+ goto st0;
+tr11:
+#line 286 "rlscan.rl"
+ { scan_error() << "bad write statement" << endl; }
+ goto st0;
+#line 553 "rlscan.cpp"
+st0:
+ goto _out0;
+tr1:
+#line 280 "rlscan.rl"
+ { word = tokdata; word_len = toklen; }
+ goto st2;
+st2:
+ if ( ++p == pe )
+ goto _out2;
+case 2:
+#line 564 "rlscan.cpp"
+ if ( (*p) == 59 )
+ goto tr2;
+ goto tr0;
+st3:
+ if ( ++p == pe )
+ goto _out3;
+case 3:
+ switch( (*p) ) {
+ case 132: goto tr4;
+ case 133: goto tr5;
+ }
+ goto tr3;
+tr4:
+#line 279 "rlscan.rl"
+ { word = lit = 0; word_len = lit_len = 0; }
+#line 280 "rlscan.rl"
+ { word = tokdata; word_len = toklen; }
+ goto st4;
+st4:
+ if ( ++p == pe )
+ goto _out4;
+case 4:
+#line 587 "rlscan.cpp"
+ switch( (*p) ) {
+ case 59: goto tr6;
+ case 133: goto tr7;
+ }
+ goto tr3;
+tr5:
+#line 279 "rlscan.rl"
+ { word = lit = 0; word_len = lit_len = 0; }
+#line 281 "rlscan.rl"
+ { lit = tokdata; lit_len = toklen; }
+ goto st5;
+tr7:
+#line 281 "rlscan.rl"
+ { lit = tokdata; lit_len = toklen; }
+ goto st5;
+st5:
+ if ( ++p == pe )
+ goto _out5;
+case 5:
+#line 607 "rlscan.cpp"
+ if ( (*p) == 59 )
+ goto tr6;
+ goto tr3;
+st6:
+ if ( ++p == pe )
+ goto _out6;
+case 6:
+ if ( (*p) == 133 )
+ goto tr9;
+ goto tr8;
+tr9:
+#line 281 "rlscan.rl"
+ { lit = tokdata; lit_len = toklen; }
+ goto st7;
+st7:
+ if ( ++p == pe )
+ goto _out7;
+case 7:
+#line 626 "rlscan.cpp"
+ if ( (*p) == 59 )
+ goto tr10;
+ goto tr8;
+tr18:
+#line 397 "rlscan.rl"
+ {
+ if ( active() && machineSpec == 0 && machineName == 0 ) {
+ output << "<write"
+ " def_name=\"" << parser->sectionName << "\""
+ " line=\"" << line << "\""
+ " col=\"" << column << "\""
+ ">";
+ }
+ }
+ goto st8;
+st8:
+ if ( ++p == pe )
+ goto _out8;
+case 8:
+#line 646 "rlscan.cpp"
+ if ( (*p) == 132 )
+ goto tr12;
+ goto tr11;
+tr12:
+#line 408 "rlscan.rl"
+ {
+ if ( active() && machineSpec == 0 && machineName == 0 )
+ output << "<arg>" << tokdata << "</arg>";
+ }
+ goto st9;
+st9:
+ if ( ++p == pe )
+ goto _out9;
+case 9:
+#line 661 "rlscan.cpp"
+ switch( (*p) ) {
+ case 59: goto tr13;
+ case 132: goto tr12;
+ }
+ goto tr11;
+ }
+ _out10: cs = 10; goto _out;
+ _out1: cs = 1; goto _out;
+ _out0: cs = 0; goto _out;
+ _out2: cs = 2; goto _out;
+ _out3: cs = 3; goto _out;
+ _out4: cs = 4; goto _out;
+ _out5: cs = 5; goto _out;
+ _out6: cs = 6; goto _out;
+ _out7: cs = 7; goto _out;
+ _out8: cs = 8; goto _out;
+ _out9: cs = 9; goto _out;
+
+ _out: {}
+ }
+#line 476 "rlscan.rl"
+
+
+ updateCol();
+
+ /* Record the last token for use in controlling the scan of subsequent
+ * tokens. */
+ lastToken = type;
+}
+
+void Scanner::startSection( )
+{
+ parserExistsError = false;
+
+ if ( includeDepth == 0 ) {
+ if ( machineSpec == 0 && machineName == 0 )
+ output << "</host>\n";
+ }
+
+ sectionLoc.fileName = fileName;
+ sectionLoc.line = line;
+ sectionLoc.col = 0;
+}
+
+void Scanner::endSection( )
+{
+ /* Execute the eof actions for the section parser. */
+
+#line 710 "rlscan.cpp"
+ {
+ switch ( cs ) {
+ case 1:
+ case 2:
+#line 283 "rlscan.rl"
+ { scan_error() << "bad machine statement" << endl; }
+ break;
+ case 3:
+ case 4:
+ case 5:
+#line 284 "rlscan.rl"
+ { scan_error() << "bad include statement" << endl; }
+ break;
+ case 6:
+ case 7:
+#line 285 "rlscan.rl"
+ { scan_error() << "bad import statement" << endl; }
+ break;
+ case 8:
+ case 9:
+#line 286 "rlscan.rl"
+ { scan_error() << "bad write statement" << endl; }
+ break;
+#line 734 "rlscan.cpp"
+ }
+ }
+
+#line 505 "rlscan.rl"
+
+
+ /* Close off the section with the parser. */
+ if ( active() ) {
+ InputLoc loc;
+ loc.fileName = fileName;
+ loc.line = line;
+ loc.col = 0;
+
+ parser->token( loc, TK_EndSection, 0, 0 );
+ }
+
+ if ( includeDepth == 0 ) {
+ if ( machineSpec == 0 && machineName == 0 ) {
+ /* The end section may include a newline on the end, so
+ * we use the last line, which will count the newline. */
+ output << "<host line=\"" << line << "\">";
+ }
+ }
+}
+
+#line 917 "rlscan.rl"
+
+
+
+#line 764 "rlscan.cpp"
+static const int rlscan_start = 23;
+
+static const int rlscan_first_final = 23;
+
+static const int rlscan_error = 0;
+
+#line 920 "rlscan.rl"
+
+void Scanner::do_scan()
+{
+ int bufsize = 8;
+ char *buf = new char[bufsize];
+ const char last_char = 0;
+ int cs, act, have = 0;
+ int top, stack[1];
+ int curly_count = 0;
+ bool execute = true;
+ bool singleLineSpec = false;
+ InlineBlockType inlineBlockType = CurlyDelimited;
+
+ /* Init the section parser and the character scanner. */
+ init();
+
+#line 788 "rlscan.cpp"
+ {
+ cs = rlscan_start;
+ top = 0;
+ tokstart = 0;
+ tokend = 0;
+ act = 0;
+ }
+#line 936 "rlscan.rl"
+
+ while ( execute ) {
+ char *p = buf + have;
+ int space = bufsize - have;
+
+ if ( space == 0 ) {
+ /* We filled up the buffer trying to scan a token. Grow it. */
+ bufsize = bufsize * 2;
+ char *newbuf = new char[bufsize];
+
+ /* Recompute p and space. */
+ p = newbuf + have;
+ space = bufsize - have;
+
+ /* Patch up pointers possibly in use. */
+ if ( tokstart != 0 )
+ tokstart = newbuf + ( tokstart - buf );
+ tokend = newbuf + ( tokend - buf );
+
+ /* Copy the new buffer in. */
+ memcpy( newbuf, buf, have );
+ delete[] buf;
+ buf = newbuf;
+ }
+
+ input.read( p, space );
+ int len = input.gcount();
+
+ /* If we see eof then append the EOF char. */
+ if ( len == 0 ) {
+ p[0] = last_char, len = 1;
+ execute = false;
+ }
+
+ char *pe = p + len;
+
+#line 833 "rlscan.cpp"
+ {
+ if ( p == pe )
+ goto _out;
+ goto _resume;
+
+_again:
+ switch ( cs ) {
+ case 23: goto st23;
+ case 24: goto st24;
+ case 25: goto st25;
+ case 1: goto st1;
+ case 2: goto st2;
+ case 26: goto st26;
+ case 27: goto st27;
+ case 28: goto st28;
+ case 3: goto st3;
+ case 4: goto st4;
+ case 29: goto st29;
+ case 5: goto st5;
+ case 6: goto st6;
+ case 7: goto st7;
+ case 30: goto st30;
+ case 31: goto st31;
+ case 32: goto st32;
+ case 33: goto st33;
+ case 34: goto st34;
+ case 35: goto st35;
+ case 36: goto st36;
+ case 37: goto st37;
+ case 38: goto st38;
+ case 39: goto st39;
+ case 8: goto st8;
+ case 9: goto st9;
+ case 40: goto st40;
+ case 10: goto st10;
+ case 11: goto st11;
+ case 41: goto st41;
+ case 12: goto st12;
+ case 13: goto st13;
+ case 14: goto st14;
+ case 42: goto st42;
+ case 43: goto st43;
+ case 15: goto st15;
+ case 44: goto st44;
+ case 45: goto st45;
+ case 46: goto st46;
+ case 47: goto st47;
+ case 48: goto st48;
+ case 49: goto st49;
+ case 50: goto st50;
+ case 51: goto st51;
+ case 52: goto st52;
+ case 53: goto st53;
+ case 54: goto st54;
+ case 55: goto st55;
+ case 56: goto st56;
+ case 57: goto st57;
+ case 58: goto st58;
+ case 59: goto st59;
+ case 60: goto st60;
+ case 61: goto st61;
+ case 62: goto st62;
+ case 63: goto st63;
+ case 64: goto st64;
+ case 65: goto st65;
+ case 66: goto st66;
+ case 67: goto st67;
+ case 68: goto st68;
+ case 69: goto st69;
+ case 70: goto st70;
+ case 71: goto st71;
+ case 72: goto st72;
+ case 73: goto st73;
+ case 74: goto st74;
+ case 75: goto st75;
+ case 76: goto st76;
+ case 77: goto st77;
+ case 78: goto st78;
+ case 79: goto st79;
+ case 80: goto st80;
+ case 81: goto st81;
+ case 82: goto st82;
+ case 83: goto st83;
+ case 84: goto st84;
+ case 85: goto st85;
+ case 0: goto st0;
+ case 86: goto st86;
+ case 87: goto st87;
+ case 88: goto st88;
+ case 89: goto st89;
+ case 90: goto st90;
+ case 16: goto st16;
+ case 91: goto st91;
+ case 17: goto st17;
+ case 92: goto st92;
+ case 18: goto st18;
+ case 93: goto st93;
+ case 94: goto st94;
+ case 95: goto st95;
+ case 19: goto st19;
+ case 20: goto st20;
+ case 96: goto st96;
+ case 97: goto st97;
+ case 98: goto st98;
+ case 99: goto st99;
+ case 100: goto st100;
+ case 21: goto st21;
+ case 101: goto st101;
+ case 102: goto st102;
+ case 103: goto st103;
+ case 104: goto st104;
+ case 105: goto st105;
+ case 106: goto st106;
+ case 107: goto st107;
+ case 108: goto st108;
+ case 109: goto st109;
+ case 110: goto st110;
+ case 111: goto st111;
+ case 112: goto st112;
+ case 113: goto st113;
+ case 114: goto st114;
+ case 115: goto st115;
+ case 116: goto st116;
+ case 117: goto st117;
+ case 118: goto st118;
+ case 119: goto st119;
+ case 120: goto st120;
+ case 121: goto st121;
+ case 122: goto st122;
+ case 123: goto st123;
+ case 124: goto st124;
+ case 125: goto st125;
+ case 126: goto st126;
+ case 127: goto st127;
+ case 128: goto st128;
+ case 129: goto st129;
+ case 130: goto st130;
+ case 131: goto st131;
+ case 132: goto st132;
+ case 133: goto st133;
+ case 134: goto st134;
+ case 135: goto st135;
+ case 136: goto st136;
+ case 137: goto st137;
+ case 138: goto st138;
+ case 139: goto st139;
+ case 140: goto st140;
+ case 141: goto st141;
+ case 142: goto st142;
+ case 143: goto st143;
+ case 144: goto st144;
+ case 145: goto st145;
+ case 146: goto st146;
+ case 147: goto st147;
+ case 148: goto st148;
+ case 149: goto st149;
+ case 150: goto st150;
+ case 151: goto st151;
+ case 152: goto st152;
+ case 153: goto st153;
+ case 154: goto st154;
+ case 155: goto st155;
+ case 156: goto st156;
+ case 157: goto st157;
+ case 158: goto st158;
+ case 159: goto st159;
+ case 160: goto st160;
+ case 161: goto st161;
+ case 162: goto st162;
+ case 163: goto st163;
+ case 164: goto st164;
+ case 165: goto st165;
+ case 166: goto st166;
+ case 167: goto st167;
+ case 168: goto st168;
+ case 169: goto st169;
+ case 170: goto st170;
+ case 171: goto st171;
+ case 172: goto st172;
+ case 173: goto st173;
+ case 174: goto st174;
+ case 22: goto st22;
+ default: break;
+ }
+
+ if ( ++p == pe )
+ goto _out;
+_resume:
+ switch ( cs )
+ {
+tr2:
+#line 899 "rlscan.rl"
+ {tokend = p+1;{ pass( IMP_Literal, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st23;
+tr10:
+#line 898 "rlscan.rl"
+ {tokend = p+1;{ pass(); }{p = ((tokend))-1;}}
+ goto st23;
+tr12:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+#line 898 "rlscan.rl"
+ {tokend = p+1;{ pass(); }{p = ((tokend))-1;}}
+ goto st23;
+tr41:
+#line 915 "rlscan.rl"
+ {tokend = p+1;{ pass( *tokstart, 0, 0 ); }{p = ((tokend))-1;}}
+ goto st23;
+tr42:
+#line 914 "rlscan.rl"
+ {tokend = p+1;{p = ((tokend))-1;}}
+ goto st23;
+tr52:
+#line 913 "rlscan.rl"
+ {tokend = p;{ pass(); }{p = ((tokend))-1;}}
+ goto st23;
+tr53:
+#line 915 "rlscan.rl"
+ {tokend = p;{ pass( *tokstart, 0, 0 ); }{p = ((tokend))-1;}}
+ goto st23;
+tr55:
+#line 907 "rlscan.rl"
+ {tokend = p;{
+ updateCol();
+ singleLineSpec = true;
+ startSection();
+ {{p = ((tokend))-1;}{goto st88;}}
+ }{p = ((tokend))-1;}}
+ goto st23;
+tr56:
+#line 901 "rlscan.rl"
+ {tokend = p+1;{
+ updateCol();
+ singleLineSpec = false;
+ startSection();
+ {{p = ((tokend))-1;}{goto st88;}}
+ }{p = ((tokend))-1;}}
+ goto st23;
+tr57:
+#line 897 "rlscan.rl"
+ {tokend = p;{ pass( IMP_UInt, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st23;
+tr58:
+#line 1 "rlscan.rl"
+ { switch( act ) {
+ case 137:
+ { pass( IMP_Define, 0, 0 ); }
+ break;
+ case 138:
+ { pass( IMP_Word, tokstart, tokend ); }
+ break;
+ default: break;
+ }
+ {p = ((tokend))-1;}}
+ goto st23;
+tr59:
+#line 896 "rlscan.rl"
+ {tokend = p;{ pass( IMP_Word, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st23;
+st23:
+#line 1 "rlscan.rl"
+ {tokstart = 0;}
+ if ( ++p == pe )
+ goto _out23;
+case 23:
+#line 1 "rlscan.rl"
+ {tokstart = p;}
+#line 1105 "rlscan.cpp"
+ switch( (*p) ) {
+ case 0: goto tr42;
+ case 9: goto st24;
+ case 10: goto tr44;
+ case 32: goto st24;
+ case 34: goto tr45;
+ case 37: goto st26;
+ case 39: goto tr47;
+ case 47: goto tr48;
+ case 95: goto tr50;
+ case 100: goto st32;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st30;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr50;
+ } else
+ goto tr50;
+ goto tr41;
+tr44:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st24;
+st24:
+ if ( ++p == pe )
+ goto _out24;
+case 24:
+#line 1139 "rlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto st24;
+ case 10: goto tr44;
+ case 32: goto st24;
+ }
+ goto tr52;
+tr45:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st25;
+st25:
+ if ( ++p == pe )
+ goto _out25;
+case 25:
+#line 1154 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr1;
+ case 34: goto tr2;
+ case 92: goto st2;
+ }
+ goto st1;
+tr1:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st1;
+st1:
+ if ( ++p == pe )
+ goto _out1;
+case 1:
+#line 1173 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr1;
+ case 34: goto tr2;
+ case 92: goto st2;
+ }
+ goto st1;
+st2:
+ if ( ++p == pe )
+ goto _out2;
+case 2:
+ if ( (*p) == 10 )
+ goto tr1;
+ goto st1;
+st26:
+ if ( ++p == pe )
+ goto _out26;
+case 26:
+ if ( (*p) == 37 )
+ goto st27;
+ goto tr53;
+st27:
+ if ( ++p == pe )
+ goto _out27;
+case 27:
+ if ( (*p) == 123 )
+ goto tr56;
+ goto tr55;
+tr47:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st28;
+st28:
+ if ( ++p == pe )
+ goto _out28;
+case 28:
+#line 1209 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr5;
+ case 39: goto tr2;
+ case 92: goto st4;
+ }
+ goto st3;
+tr5:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st3;
+st3:
+ if ( ++p == pe )
+ goto _out3;
+case 3:
+#line 1228 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr5;
+ case 39: goto tr2;
+ case 92: goto st4;
+ }
+ goto st3;
+st4:
+ if ( ++p == pe )
+ goto _out4;
+case 4:
+ if ( (*p) == 10 )
+ goto tr5;
+ goto st3;
+tr48:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st29;
+st29:
+ if ( ++p == pe )
+ goto _out29;
+case 29:
+#line 1250 "rlscan.cpp"
+ switch( (*p) ) {
+ case 42: goto st5;
+ case 47: goto st7;
+ }
+ goto tr53;
+tr8:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st5;
+st5:
+ if ( ++p == pe )
+ goto _out5;
+case 5:
+#line 1268 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr8;
+ case 42: goto st6;
+ }
+ goto st5;
+st6:
+ if ( ++p == pe )
+ goto _out6;
+case 6:
+ switch( (*p) ) {
+ case 10: goto tr8;
+ case 42: goto st6;
+ case 47: goto tr10;
+ }
+ goto st5;
+st7:
+ if ( ++p == pe )
+ goto _out7;
+case 7:
+ if ( (*p) == 10 )
+ goto tr12;
+ goto st7;
+st30:
+ if ( ++p == pe )
+ goto _out30;
+case 30:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st30;
+ goto tr57;
+tr50:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 896 "rlscan.rl"
+ {act = 138;}
+ goto st31;
+tr64:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 895 "rlscan.rl"
+ {act = 137;}
+ goto st31;
+st31:
+ if ( ++p == pe )
+ goto _out31;
+case 31:
+#line 1314 "rlscan.cpp"
+ if ( (*p) == 95 )
+ goto tr50;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr50;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr50;
+ } else
+ goto tr50;
+ goto tr58;
+st32:
+ if ( ++p == pe )
+ goto _out32;
+case 32:
+ switch( (*p) ) {
+ case 95: goto tr50;
+ case 101: goto st33;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr50;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr50;
+ } else
+ goto tr50;
+ goto tr59;
+st33:
+ if ( ++p == pe )
+ goto _out33;
+case 33:
+ switch( (*p) ) {
+ case 95: goto tr50;
+ case 102: goto st34;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr50;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr50;
+ } else
+ goto tr50;
+ goto tr59;
+st34:
+ if ( ++p == pe )
+ goto _out34;
+case 34:
+ switch( (*p) ) {
+ case 95: goto tr50;
+ case 105: goto st35;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr50;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr50;
+ } else
+ goto tr50;
+ goto tr59;
+st35:
+ if ( ++p == pe )
+ goto _out35;
+case 35:
+ switch( (*p) ) {
+ case 95: goto tr50;
+ case 110: goto st36;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr50;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr50;
+ } else
+ goto tr50;
+ goto tr59;
+st36:
+ if ( ++p == pe )
+ goto _out36;
+case 36:
+ switch( (*p) ) {
+ case 95: goto tr50;
+ case 101: goto tr64;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr50;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr50;
+ } else
+ goto tr50;
+ goto tr59;
+tr15:
+#line 606 "rlscan.rl"
+ {tokend = p+1;{ token( IL_Literal, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr23:
+#line 612 "rlscan.rl"
+ {tokend = p+1;{ token( IL_Comment, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr25:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+#line 612 "rlscan.rl"
+ {tokend = p+1;{ token( IL_Comment, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr26:
+#line 602 "rlscan.rl"
+ {{ token( TK_UInt, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr65:
+#line 659 "rlscan.rl"
+ {tokend = p+1;{ token( IL_Symbol, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr66:
+#line 654 "rlscan.rl"
+ {tokend = p+1;{
+ scan_error() << "unterminated code block" << endl;
+ }{p = ((tokend))-1;}}
+ goto st37;
+tr71:
+#line 634 "rlscan.rl"
+ {tokend = p+1;{ token( *tokstart, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr72:
+#line 629 "rlscan.rl"
+ {tokend = p+1;{
+ whitespaceOn = true;
+ token( *tokstart, tokstart, tokend );
+ }{p = ((tokend))-1;}}
+ goto st37;
+tr77:
+#line 622 "rlscan.rl"
+ {tokend = p+1;{
+ whitespaceOn = true;
+ token( *tokstart, tokstart, tokend );
+ if ( inlineBlockType == SemiTerminated )
+ {{p = ((tokend))-1;}{goto st88;}}
+ }{p = ((tokend))-1;}}
+ goto st37;
+tr80:
+#line 636 "rlscan.rl"
+ {tokend = p+1;{
+ token( IL_Symbol, tokstart, tokend );
+ curly_count += 1;
+ }{p = ((tokend))-1;}}
+ goto st37;
+tr81:
+#line 641 "rlscan.rl"
+ {tokend = p+1;{
+ if ( --curly_count == 0 && inlineBlockType == CurlyDelimited ) {
+ /* Inline code block ends. */
+ token( '}' );
+ {{p = ((tokend))-1;}{goto st88;}}
+ }
+ else {
+ /* Either a semi terminated inline block or only the closing
+ * brace of some inner scope, not the block's closing brace. */
+ token( IL_Symbol, tokstart, tokend );
+ }
+ }{p = ((tokend))-1;}}
+ goto st37;
+tr82:
+#line 608 "rlscan.rl"
+ {tokend = p;{
+ if ( whitespaceOn )
+ token( IL_WhiteSpace, tokstart, tokend );
+ }{p = ((tokend))-1;}}
+ goto st37;
+tr83:
+#line 659 "rlscan.rl"
+ {tokend = p;{ token( IL_Symbol, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr84:
+#line 602 "rlscan.rl"
+ {tokend = p;{ token( TK_UInt, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr86:
+#line 603 "rlscan.rl"
+ {tokend = p;{ token( TK_Hex, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr87:
+#line 614 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NameSep, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr88:
+#line 1 "rlscan.rl"
+ { switch( act ) {
+ case 1:
+ { token( KW_PChar ); }
+ break;
+ case 3:
+ { token( KW_CurState ); }
+ break;
+ case 4:
+ { token( KW_TargState ); }
+ break;
+ case 5:
+ {
+ whitespaceOn = false;
+ token( KW_Entry );
+ }
+ break;
+ case 6:
+ {
+ whitespaceOn = false;
+ token( KW_Hold );
+ }
+ break;
+ case 7:
+ { token( KW_Exec, 0, 0 ); }
+ break;
+ case 8:
+ {
+ whitespaceOn = false;
+ token( KW_Goto );
+ }
+ break;
+ case 9:
+ {
+ whitespaceOn = false;
+ token( KW_Next );
+ }
+ break;
+ case 10:
+ {
+ whitespaceOn = false;
+ token( KW_Call );
+ }
+ break;
+ case 11:
+ {
+ whitespaceOn = false;
+ token( KW_Ret );
+ }
+ break;
+ case 12:
+ {
+ whitespaceOn = false;
+ token( KW_Break );
+ }
+ break;
+ case 13:
+ { token( TK_Word, tokstart, tokend ); }
+ break;
+ default: break;
+ }
+ {p = ((tokend))-1;}}
+ goto st37;
+tr89:
+#line 600 "rlscan.rl"
+ {tokend = p;{ token( TK_Word, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st37;
+tr103:
+#line 565 "rlscan.rl"
+ {tokend = p;{ token( KW_Char ); }{p = ((tokend))-1;}}
+ goto st37;
+st37:
+#line 1 "rlscan.rl"
+ {tokstart = 0;}
+ if ( ++p == pe )
+ goto _out37;
+case 37:
+#line 1 "rlscan.rl"
+ {tokstart = p;}
+#line 1588 "rlscan.cpp"
+ switch( (*p) ) {
+ case 0: goto tr66;
+ case 9: goto st38;
+ case 10: goto tr68;
+ case 32: goto st38;
+ case 34: goto tr69;
+ case 39: goto tr70;
+ case 40: goto tr71;
+ case 44: goto tr71;
+ case 47: goto tr73;
+ case 48: goto tr74;
+ case 58: goto st45;
+ case 59: goto tr77;
+ case 95: goto tr78;
+ case 102: goto st47;
+ case 123: goto tr80;
+ case 125: goto tr81;
+ }
+ if ( (*p) < 49 ) {
+ if ( 41 <= (*p) && (*p) <= 42 )
+ goto tr72;
+ } else if ( (*p) > 57 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else if ( (*p) >= 65 )
+ goto tr78;
+ } else
+ goto st43;
+ goto tr65;
+tr68:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st38;
+st38:
+ if ( ++p == pe )
+ goto _out38;
+case 38:
+#line 1631 "rlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto st38;
+ case 10: goto tr68;
+ case 32: goto st38;
+ }
+ goto tr82;
+tr69:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st39;
+st39:
+ if ( ++p == pe )
+ goto _out39;
+case 39:
+#line 1646 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr14;
+ case 34: goto tr15;
+ case 92: goto st9;
+ }
+ goto st8;
+tr14:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st8;
+st8:
+ if ( ++p == pe )
+ goto _out8;
+case 8:
+#line 1665 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr14;
+ case 34: goto tr15;
+ case 92: goto st9;
+ }
+ goto st8;
+st9:
+ if ( ++p == pe )
+ goto _out9;
+case 9:
+ if ( (*p) == 10 )
+ goto tr14;
+ goto st8;
+tr70:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st40;
+st40:
+ if ( ++p == pe )
+ goto _out40;
+case 40:
+#line 1687 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr18;
+ case 39: goto tr15;
+ case 92: goto st11;
+ }
+ goto st10;
+tr18:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st10;
+st10:
+ if ( ++p == pe )
+ goto _out10;
+case 10:
+#line 1706 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr18;
+ case 39: goto tr15;
+ case 92: goto st11;
+ }
+ goto st10;
+st11:
+ if ( ++p == pe )
+ goto _out11;
+case 11:
+ if ( (*p) == 10 )
+ goto tr18;
+ goto st10;
+tr73:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st41;
+st41:
+ if ( ++p == pe )
+ goto _out41;
+case 41:
+#line 1728 "rlscan.cpp"
+ switch( (*p) ) {
+ case 42: goto st12;
+ case 47: goto st14;
+ }
+ goto tr83;
+tr21:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st12;
+st12:
+ if ( ++p == pe )
+ goto _out12;
+case 12:
+#line 1746 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr21;
+ case 42: goto st13;
+ }
+ goto st12;
+st13:
+ if ( ++p == pe )
+ goto _out13;
+case 13:
+ switch( (*p) ) {
+ case 10: goto tr21;
+ case 42: goto st13;
+ case 47: goto tr23;
+ }
+ goto st12;
+st14:
+ if ( ++p == pe )
+ goto _out14;
+case 14:
+ if ( (*p) == 10 )
+ goto tr25;
+ goto st14;
+tr74:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st42;
+st42:
+ if ( ++p == pe )
+ goto _out42;
+case 42:
+#line 1777 "rlscan.cpp"
+ if ( (*p) == 120 )
+ goto st15;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st43;
+ goto tr84;
+st43:
+ if ( ++p == pe )
+ goto _out43;
+case 43:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st43;
+ goto tr84;
+st15:
+ if ( ++p == pe )
+ goto _out15;
+case 15:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st44;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st44;
+ } else
+ goto st44;
+ goto tr26;
+st44:
+ if ( ++p == pe )
+ goto _out44;
+case 44:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st44;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st44;
+ } else
+ goto st44;
+ goto tr86;
+st45:
+ if ( ++p == pe )
+ goto _out45;
+case 45:
+ if ( (*p) == 58 )
+ goto tr87;
+ goto tr83;
+tr78:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 600 "rlscan.rl"
+ {act = 13;}
+ goto st46;
+tr102:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 595 "rlscan.rl"
+ {act = 12;}
+ goto st46;
+tr107:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 587 "rlscan.rl"
+ {act = 10;}
+ goto st46;
+tr109:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 566 "rlscan.rl"
+ {act = 3;}
+ goto st46;
+tr114:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 568 "rlscan.rl"
+ {act = 5;}
+ goto st46;
+tr116:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 578 "rlscan.rl"
+ {act = 7;}
+ goto st46;
+tr119:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 579 "rlscan.rl"
+ {act = 8;}
+ goto st46;
+tr122:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 574 "rlscan.rl"
+ {act = 6;}
+ goto st46;
+tr125:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 583 "rlscan.rl"
+ {act = 9;}
+ goto st46;
+tr126:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 564 "rlscan.rl"
+ {act = 1;}
+ goto st46;
+tr128:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 591 "rlscan.rl"
+ {act = 11;}
+ goto st46;
+tr132:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 567 "rlscan.rl"
+ {act = 4;}
+ goto st46;
+st46:
+ if ( ++p == pe )
+ goto _out46;
+case 46:
+#line 1899 "rlscan.cpp"
+ if ( (*p) == 95 )
+ goto tr78;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr88;
+st47:
+ if ( ++p == pe )
+ goto _out47;
+case 47:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 98: goto st48;
+ case 99: goto st52;
+ case 101: goto st57;
+ case 103: goto st63;
+ case 104: goto st66;
+ case 110: goto st69;
+ case 112: goto st72;
+ case 114: goto st73;
+ case 116: goto st75;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st48:
+ if ( ++p == pe )
+ goto _out48;
+case 48:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 114: goto st49;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st49:
+ if ( ++p == pe )
+ goto _out49;
+case 49:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 101: goto st50;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st50:
+ if ( ++p == pe )
+ goto _out50;
+case 50:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 97: goto st51;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 98 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st51:
+ if ( ++p == pe )
+ goto _out51;
+case 51:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 107: goto tr102;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st52:
+ if ( ++p == pe )
+ goto _out52;
+case 52:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 97: goto st53;
+ case 117: goto st55;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 98 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr103;
+st53:
+ if ( ++p == pe )
+ goto _out53;
+case 53:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 108: goto st54;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st54:
+ if ( ++p == pe )
+ goto _out54;
+case 54:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 108: goto tr107;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st55:
+ if ( ++p == pe )
+ goto _out55;
+case 55:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 114: goto st56;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st56:
+ if ( ++p == pe )
+ goto _out56;
+case 56:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 115: goto tr109;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st57:
+ if ( ++p == pe )
+ goto _out57;
+case 57:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 110: goto st58;
+ case 120: goto st61;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st58:
+ if ( ++p == pe )
+ goto _out58;
+case 58:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 116: goto st59;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st59:
+ if ( ++p == pe )
+ goto _out59;
+case 59:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 114: goto st60;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st60:
+ if ( ++p == pe )
+ goto _out60;
+case 60:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 121: goto tr114;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st61:
+ if ( ++p == pe )
+ goto _out61;
+case 61:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 101: goto st62;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st62:
+ if ( ++p == pe )
+ goto _out62;
+case 62:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 99: goto tr116;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st63:
+ if ( ++p == pe )
+ goto _out63;
+case 63:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 111: goto st64;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st64:
+ if ( ++p == pe )
+ goto _out64;
+case 64:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 116: goto st65;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st65:
+ if ( ++p == pe )
+ goto _out65;
+case 65:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 111: goto tr119;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st66:
+ if ( ++p == pe )
+ goto _out66;
+case 66:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 111: goto st67;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st67:
+ if ( ++p == pe )
+ goto _out67;
+case 67:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 108: goto st68;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st68:
+ if ( ++p == pe )
+ goto _out68;
+case 68:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 100: goto tr122;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st69:
+ if ( ++p == pe )
+ goto _out69;
+case 69:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 101: goto st70;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st70:
+ if ( ++p == pe )
+ goto _out70;
+case 70:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 120: goto st71;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st71:
+ if ( ++p == pe )
+ goto _out71;
+case 71:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 116: goto tr125;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st72:
+ if ( ++p == pe )
+ goto _out72;
+case 72:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 99: goto tr126;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st73:
+ if ( ++p == pe )
+ goto _out73;
+case 73:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 101: goto st74;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st74:
+ if ( ++p == pe )
+ goto _out74;
+case 74:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 116: goto tr128;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st75:
+ if ( ++p == pe )
+ goto _out75;
+case 75:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 97: goto st76;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 98 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st76:
+ if ( ++p == pe )
+ goto _out76;
+case 76:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 114: goto st77;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st77:
+ if ( ++p == pe )
+ goto _out77;
+case 77:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 103: goto st78;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+st78:
+ if ( ++p == pe )
+ goto _out78;
+case 78:
+ switch( (*p) ) {
+ case 95: goto tr78;
+ case 115: goto tr132;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr78;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr78;
+ } else
+ goto tr78;
+ goto tr89;
+tr133:
+#line 686 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st79;
+tr134:
+#line 681 "rlscan.rl"
+ {tokend = p+1;{
+ scan_error() << "unterminated OR literal" << endl;
+ }{p = ((tokend))-1;}}
+ goto st79;
+tr135:
+#line 676 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Dash, 0, 0 ); }{p = ((tokend))-1;}}
+ goto st79;
+tr137:
+#line 679 "rlscan.rl"
+ {tokend = p+1;{ token( RE_SqClose ); {{p = ((tokend))-1;}{cs = stack[--top]; goto _again;}} }{p = ((tokend))-1;}}
+ goto st79;
+tr138:
+#line 673 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, tokstart+1, tokend ); }{p = ((tokend))-1;}}
+ goto st79;
+tr139:
+#line 672 "rlscan.rl"
+ {tokend = p+1;{ updateCol(); }{p = ((tokend))-1;}}
+ goto st79;
+tr140:
+#line 664 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\0' ); }{p = ((tokend))-1;}}
+ goto st79;
+tr141:
+#line 665 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\a' ); }{p = ((tokend))-1;}}
+ goto st79;
+tr142:
+#line 666 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\b' ); }{p = ((tokend))-1;}}
+ goto st79;
+tr143:
+#line 670 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\f' ); }{p = ((tokend))-1;}}
+ goto st79;
+tr144:
+#line 668 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\n' ); }{p = ((tokend))-1;}}
+ goto st79;
+tr145:
+#line 671 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\r' ); }{p = ((tokend))-1;}}
+ goto st79;
+tr146:
+#line 667 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\t' ); }{p = ((tokend))-1;}}
+ goto st79;
+tr147:
+#line 669 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\v' ); }{p = ((tokend))-1;}}
+ goto st79;
+st79:
+#line 1 "rlscan.rl"
+ {tokstart = 0;}
+ if ( ++p == pe )
+ goto _out79;
+case 79:
+#line 1 "rlscan.rl"
+ {tokstart = p;}
+#line 2531 "rlscan.cpp"
+ switch( (*p) ) {
+ case 0: goto tr134;
+ case 45: goto tr135;
+ case 92: goto st80;
+ case 93: goto tr137;
+ }
+ goto tr133;
+st80:
+ if ( ++p == pe )
+ goto _out80;
+case 80:
+ switch( (*p) ) {
+ case 10: goto tr139;
+ case 48: goto tr140;
+ case 97: goto tr141;
+ case 98: goto tr142;
+ case 102: goto tr143;
+ case 110: goto tr144;
+ case 114: goto tr145;
+ case 116: goto tr146;
+ case 118: goto tr147;
+ }
+ goto tr138;
+tr148:
+#line 721 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st81;
+tr149:
+#line 716 "rlscan.rl"
+ {tokend = p+1;{
+ scan_error() << "unterminated regular expression" << endl;
+ }{p = ((tokend))-1;}}
+ goto st81;
+tr150:
+#line 711 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Star ); }{p = ((tokend))-1;}}
+ goto st81;
+tr151:
+#line 710 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Dot ); }{p = ((tokend))-1;}}
+ goto st81;
+tr155:
+#line 704 "rlscan.rl"
+ {tokend = p;{
+ token( RE_Slash, tokstart, tokend );
+ {{p = ((tokend))-1;}{goto st88;}}
+ }{p = ((tokend))-1;}}
+ goto st81;
+tr156:
+#line 704 "rlscan.rl"
+ {tokend = p+1;{
+ token( RE_Slash, tokstart, tokend );
+ {{p = ((tokend))-1;}{goto st88;}}
+ }{p = ((tokend))-1;}}
+ goto st81;
+tr157:
+#line 713 "rlscan.rl"
+ {tokend = p;{ token( RE_SqOpen ); {{p = ((tokend))-1;}{stack[top++] = 81; goto st79;}} }{p = ((tokend))-1;}}
+ goto st81;
+tr158:
+#line 714 "rlscan.rl"
+ {tokend = p+1;{ token( RE_SqOpenNeg ); {{p = ((tokend))-1;}{stack[top++] = 81; goto st79;}} }{p = ((tokend))-1;}}
+ goto st81;
+tr159:
+#line 701 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, tokstart+1, tokend ); }{p = ((tokend))-1;}}
+ goto st81;
+tr160:
+#line 700 "rlscan.rl"
+ {tokend = p+1;{ updateCol(); }{p = ((tokend))-1;}}
+ goto st81;
+tr161:
+#line 692 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\0' ); }{p = ((tokend))-1;}}
+ goto st81;
+tr162:
+#line 693 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\a' ); }{p = ((tokend))-1;}}
+ goto st81;
+tr163:
+#line 694 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\b' ); }{p = ((tokend))-1;}}
+ goto st81;
+tr164:
+#line 698 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\f' ); }{p = ((tokend))-1;}}
+ goto st81;
+tr165:
+#line 696 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\n' ); }{p = ((tokend))-1;}}
+ goto st81;
+tr166:
+#line 699 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\r' ); }{p = ((tokend))-1;}}
+ goto st81;
+tr167:
+#line 695 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\t' ); }{p = ((tokend))-1;}}
+ goto st81;
+tr168:
+#line 697 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Char, '\v' ); }{p = ((tokend))-1;}}
+ goto st81;
+st81:
+#line 1 "rlscan.rl"
+ {tokstart = 0;}
+ if ( ++p == pe )
+ goto _out81;
+case 81:
+#line 1 "rlscan.rl"
+ {tokstart = p;}
+#line 2643 "rlscan.cpp"
+ switch( (*p) ) {
+ case 0: goto tr149;
+ case 42: goto tr150;
+ case 46: goto tr151;
+ case 47: goto st82;
+ case 91: goto st83;
+ case 92: goto st84;
+ }
+ goto tr148;
+st82:
+ if ( ++p == pe )
+ goto _out82;
+case 82:
+ if ( (*p) == 105 )
+ goto tr156;
+ goto tr155;
+st83:
+ if ( ++p == pe )
+ goto _out83;
+case 83:
+ if ( (*p) == 94 )
+ goto tr158;
+ goto tr157;
+st84:
+ if ( ++p == pe )
+ goto _out84;
+case 84:
+ switch( (*p) ) {
+ case 10: goto tr160;
+ case 48: goto tr161;
+ case 97: goto tr162;
+ case 98: goto tr163;
+ case 102: goto tr164;
+ case 110: goto tr165;
+ case 114: goto tr166;
+ case 116: goto tr167;
+ case 118: goto tr168;
+ }
+ goto tr159;
+tr169:
+#line 730 "rlscan.rl"
+ {tokend = p+1;{
+ scan_error() << "unterminated write statement" << endl;
+ }{p = ((tokend))-1;}}
+ goto st85;
+tr172:
+#line 728 "rlscan.rl"
+ {tokend = p+1;{ token( ';' ); {{p = ((tokend))-1;}{goto st88;}} }{p = ((tokend))-1;}}
+ goto st85;
+tr174:
+#line 727 "rlscan.rl"
+ {tokend = p;{ updateCol(); }{p = ((tokend))-1;}}
+ goto st85;
+tr175:
+#line 726 "rlscan.rl"
+ {tokend = p;{ token( TK_Word, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st85;
+st85:
+#line 1 "rlscan.rl"
+ {tokstart = 0;}
+ if ( ++p == pe )
+ goto _out85;
+case 85:
+#line 1 "rlscan.rl"
+ {tokstart = p;}
+#line 2709 "rlscan.cpp"
+ switch( (*p) ) {
+ case 0: goto tr169;
+ case 32: goto st86;
+ case 59: goto tr172;
+ case 95: goto st87;
+ }
+ if ( (*p) < 65 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st86;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto st87;
+ } else
+ goto st87;
+ goto st0;
+st0:
+ goto _out0;
+st86:
+ if ( ++p == pe )
+ goto _out86;
+case 86:
+ if ( (*p) == 32 )
+ goto st86;
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto st86;
+ goto tr174;
+st87:
+ if ( ++p == pe )
+ goto _out87;
+case 87:
+ if ( (*p) == 95 )
+ goto st87;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st87;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto st87;
+ } else
+ goto st87;
+ goto tr175;
+tr33:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+#line 790 "rlscan.rl"
+ {tokend = p+1;{ updateCol(); }{p = ((tokend))-1;}}
+ goto st88;
+tr37:
+#line 777 "rlscan.rl"
+ {{ token( TK_UInt, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st88;
+tr39:
+#line 890 "rlscan.rl"
+ {{ token( *tokstart ); }{p = ((tokend))-1;}}
+ goto st88;
+tr40:
+#line 858 "rlscan.rl"
+ {tokend = p+1;{
+ updateCol();
+ endSection();
+ {{p = ((tokend))-1;}{goto st23;}}
+ }{p = ((tokend))-1;}}
+ goto st88;
+tr176:
+#line 890 "rlscan.rl"
+ {tokend = p+1;{ token( *tokstart ); }{p = ((tokend))-1;}}
+ goto st88;
+tr177:
+#line 886 "rlscan.rl"
+ {tokend = p+1;{
+ scan_error() << "unterminated ragel section" << endl;
+ }{p = ((tokend))-1;}}
+ goto st88;
+tr179:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+#line 867 "rlscan.rl"
+ {tokend = p+1;{
+ updateCol();
+ if ( singleLineSpec ) {
+ endSection();
+ {{p = ((tokend))-1;}{goto st23;}}
+ }
+ }{p = ((tokend))-1;}}
+ goto st88;
+tr188:
+#line 787 "rlscan.rl"
+ {tokend = p+1;{ token( RE_Slash ); {{p = ((tokend))-1;}{goto st81;}} }{p = ((tokend))-1;}}
+ goto st88;
+tr208:
+#line 875 "rlscan.rl"
+ {tokend = p+1;{
+ if ( lastToken == KW_Export || lastToken == KW_Entry )
+ token( '{' );
+ else {
+ token( '{' );
+ curly_count = 1;
+ inlineBlockType = CurlyDelimited;
+ {{p = ((tokend))-1;}{goto st37;}}
+ }
+ }{p = ((tokend))-1;}}
+ goto st88;
+tr211:
+#line 864 "rlscan.rl"
+ {tokend = p;{ updateCol(); }{p = ((tokend))-1;}}
+ goto st88;
+tr212:
+#line 782 "rlscan.rl"
+ {tokend = p;{ token( TK_Literal, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st88;
+tr213:
+#line 782 "rlscan.rl"
+ {tokend = p+1;{ token( TK_Literal, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st88;
+tr214:
+#line 890 "rlscan.rl"
+ {tokend = p;{ token( *tokstart ); }{p = ((tokend))-1;}}
+ goto st88;
+tr215:
+#line 820 "rlscan.rl"
+ {tokend = p+1;{ token( TK_AllGblError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr216:
+#line 804 "rlscan.rl"
+ {tokend = p+1;{ token( TK_AllFromState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr217:
+#line 812 "rlscan.rl"
+ {tokend = p+1;{ token( TK_AllEOF ); }{p = ((tokend))-1;}}
+ goto st88;
+tr218:
+#line 839 "rlscan.rl"
+ {tokend = p+1;{ token( TK_AllCond ); }{p = ((tokend))-1;}}
+ goto st88;
+tr219:
+#line 828 "rlscan.rl"
+ {tokend = p+1;{ token( TK_AllLocalError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr220:
+#line 796 "rlscan.rl"
+ {tokend = p+1;{ token( TK_AllToState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr221:
+#line 821 "rlscan.rl"
+ {tokend = p+1;{ token( TK_FinalGblError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr222:
+#line 805 "rlscan.rl"
+ {tokend = p+1;{ token( TK_FinalFromState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr223:
+#line 813 "rlscan.rl"
+ {tokend = p+1;{ token( TK_FinalEOF ); }{p = ((tokend))-1;}}
+ goto st88;
+tr224:
+#line 840 "rlscan.rl"
+ {tokend = p+1;{ token( TK_LeavingCond ); }{p = ((tokend))-1;}}
+ goto st88;
+tr225:
+#line 829 "rlscan.rl"
+ {tokend = p+1;{ token( TK_FinalLocalError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr226:
+#line 797 "rlscan.rl"
+ {tokend = p+1;{ token( TK_FinalToState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr227:
+#line 843 "rlscan.rl"
+ {tokend = p+1;{ token( TK_StarStar ); }{p = ((tokend))-1;}}
+ goto st88;
+tr228:
+#line 844 "rlscan.rl"
+ {tokend = p+1;{ token( TK_DashDash ); }{p = ((tokend))-1;}}
+ goto st88;
+tr229:
+#line 845 "rlscan.rl"
+ {tokend = p+1;{ token( TK_Arrow ); }{p = ((tokend))-1;}}
+ goto st88;
+tr230:
+#line 842 "rlscan.rl"
+ {tokend = p+1;{ token( TK_DotDot ); }{p = ((tokend))-1;}}
+ goto st88;
+tr231:
+#line 777 "rlscan.rl"
+ {tokend = p;{ token( TK_UInt, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st88;
+tr233:
+#line 778 "rlscan.rl"
+ {tokend = p;{ token( TK_Hex, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st88;
+tr234:
+#line 856 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NameSep, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st88;
+tr235:
+#line 792 "rlscan.rl"
+ {tokend = p+1;{ token( TK_ColonEquals ); }{p = ((tokend))-1;}}
+ goto st88;
+tr237:
+#line 848 "rlscan.rl"
+ {tokend = p;{ token( TK_ColonGt ); }{p = ((tokend))-1;}}
+ goto st88;
+tr238:
+#line 849 "rlscan.rl"
+ {tokend = p+1;{ token( TK_ColonGtGt ); }{p = ((tokend))-1;}}
+ goto st88;
+tr239:
+#line 822 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotStartGblError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr240:
+#line 806 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotStartFromState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr241:
+#line 814 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotStartEOF ); }{p = ((tokend))-1;}}
+ goto st88;
+tr242:
+#line 850 "rlscan.rl"
+ {tokend = p+1;{ token( TK_LtColon ); }{p = ((tokend))-1;}}
+ goto st88;
+tr244:
+#line 830 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotStartLocalError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr245:
+#line 798 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotStartToState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr246:
+#line 835 "rlscan.rl"
+ {tokend = p;{ token( TK_Middle ); }{p = ((tokend))-1;}}
+ goto st88;
+tr247:
+#line 824 "rlscan.rl"
+ {tokend = p+1;{ token( TK_MiddleGblError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr248:
+#line 808 "rlscan.rl"
+ {tokend = p+1;{ token( TK_MiddleFromState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr249:
+#line 816 "rlscan.rl"
+ {tokend = p+1;{ token( TK_MiddleEOF ); }{p = ((tokend))-1;}}
+ goto st88;
+tr250:
+#line 832 "rlscan.rl"
+ {tokend = p+1;{ token( TK_MiddleLocalError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr251:
+#line 800 "rlscan.rl"
+ {tokend = p+1;{ token( TK_MiddleToState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr252:
+#line 846 "rlscan.rl"
+ {tokend = p+1;{ token( TK_DoubleArrow ); }{p = ((tokend))-1;}}
+ goto st88;
+tr253:
+#line 819 "rlscan.rl"
+ {tokend = p+1;{ token( TK_StartGblError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr254:
+#line 803 "rlscan.rl"
+ {tokend = p+1;{ token( TK_StartFromState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr255:
+#line 811 "rlscan.rl"
+ {tokend = p+1;{ token( TK_StartEOF ); }{p = ((tokend))-1;}}
+ goto st88;
+tr256:
+#line 838 "rlscan.rl"
+ {tokend = p+1;{ token( TK_StartCond ); }{p = ((tokend))-1;}}
+ goto st88;
+tr257:
+#line 827 "rlscan.rl"
+ {tokend = p+1;{ token( TK_StartLocalError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr258:
+#line 795 "rlscan.rl"
+ {tokend = p+1;{ token( TK_StartToState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr259:
+#line 823 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotFinalGblError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr260:
+#line 807 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotFinalFromState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr261:
+#line 815 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotFinalEOF ); }{p = ((tokend))-1;}}
+ goto st88;
+tr262:
+#line 831 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotFinalLocalError ); }{p = ((tokend))-1;}}
+ goto st88;
+tr263:
+#line 799 "rlscan.rl"
+ {tokend = p+1;{ token( TK_NotFinalToState ); }{p = ((tokend))-1;}}
+ goto st88;
+tr264:
+#line 1 "rlscan.rl"
+ { switch( act ) {
+ case 62:
+ { token( KW_Machine ); }
+ break;
+ case 63:
+ { token( KW_Include ); }
+ break;
+ case 64:
+ { token( KW_Import ); }
+ break;
+ case 65:
+ {
+ token( KW_Write );
+ {{p = ((tokend))-1;}{goto st85;}}
+ }
+ break;
+ case 66:
+ { token( KW_Action ); }
+ break;
+ case 67:
+ { token( KW_AlphType ); }
+ break;
+ case 68:
+ {
+ token( KW_GetKey );
+ inlineBlockType = SemiTerminated;
+ {{p = ((tokend))-1;}{goto st37;}}
+ }
+ break;
+ case 69:
+ {
+ token( KW_Access );
+ inlineBlockType = SemiTerminated;
+ {{p = ((tokend))-1;}{goto st37;}}
+ }
+ break;
+ case 70:
+ {
+ token( KW_Variable );
+ inlineBlockType = SemiTerminated;
+ {{p = ((tokend))-1;}{goto st37;}}
+ }
+ break;
+ case 71:
+ { token( KW_When ); }
+ break;
+ case 72:
+ { token( KW_Eof ); }
+ break;
+ case 73:
+ { token( KW_Err ); }
+ break;
+ case 74:
+ { token( KW_Lerr ); }
+ break;
+ case 75:
+ { token( KW_To ); }
+ break;
+ case 76:
+ { token( KW_From ); }
+ break;
+ case 77:
+ { token( KW_Export ); }
+ break;
+ case 78:
+ { token( TK_Word, tokstart, tokend ); }
+ break;
+ default: break;
+ }
+ {p = ((tokend))-1;}}
+ goto st88;
+tr265:
+#line 784 "rlscan.rl"
+ {tokend = p;{ token( RE_SqOpen ); {{p = ((tokend))-1;}{stack[top++] = 88; goto st79;}} }{p = ((tokend))-1;}}
+ goto st88;
+tr266:
+#line 785 "rlscan.rl"
+ {tokend = p+1;{ token( RE_SqOpenNeg ); {{p = ((tokend))-1;}{stack[top++] = 88; goto st79;}} }{p = ((tokend))-1;}}
+ goto st88;
+tr267:
+#line 774 "rlscan.rl"
+ {tokend = p;{ token( TK_Word, tokstart, tokend ); }{p = ((tokend))-1;}}
+ goto st88;
+tr336:
+#line 853 "rlscan.rl"
+ {tokend = p+1;{ token( TK_BarStar ); }{p = ((tokend))-1;}}
+ goto st88;
+st88:
+#line 1 "rlscan.rl"
+ {tokstart = 0;}
+ if ( ++p == pe )
+ goto _out88;
+case 88:
+#line 1 "rlscan.rl"
+ {tokstart = p;}
+#line 3117 "rlscan.cpp"
+ switch( (*p) ) {
+ case 0: goto tr177;
+ case 9: goto st89;
+ case 10: goto tr179;
+ case 13: goto st89;
+ case 32: goto st89;
+ case 34: goto tr180;
+ case 35: goto tr181;
+ case 36: goto st93;
+ case 37: goto st94;
+ case 39: goto tr184;
+ case 42: goto st96;
+ case 45: goto st97;
+ case 46: goto st98;
+ case 47: goto tr188;
+ case 48: goto tr189;
+ case 58: goto st102;
+ case 60: goto st104;
+ case 61: goto st106;
+ case 62: goto st107;
+ case 64: goto st108;
+ case 91: goto st110;
+ case 95: goto tr196;
+ case 97: goto st111;
+ case 101: goto st125;
+ case 102: goto st132;
+ case 103: goto st135;
+ case 105: goto st140;
+ case 108: goto st150;
+ case 109: goto st153;
+ case 116: goto st159;
+ case 118: goto st160;
+ case 119: goto st167;
+ case 123: goto tr208;
+ case 124: goto st173;
+ case 125: goto tr210;
+ }
+ if ( (*p) < 65 ) {
+ if ( 49 <= (*p) && (*p) <= 57 )
+ goto st100;
+ } else if ( (*p) > 90 ) {
+ if ( 98 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr176;
+st89:
+ if ( ++p == pe )
+ goto _out89;
+case 89:
+ switch( (*p) ) {
+ case 9: goto st89;
+ case 13: goto st89;
+ case 32: goto st89;
+ }
+ goto tr211;
+tr180:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st90;
+st90:
+ if ( ++p == pe )
+ goto _out90;
+case 90:
+#line 3182 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr29;
+ case 34: goto st91;
+ case 92: goto st17;
+ }
+ goto st16;
+tr29:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st16;
+st16:
+ if ( ++p == pe )
+ goto _out16;
+case 16:
+#line 3201 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr29;
+ case 34: goto st91;
+ case 92: goto st17;
+ }
+ goto st16;
+st91:
+ if ( ++p == pe )
+ goto _out91;
+case 91:
+ if ( (*p) == 105 )
+ goto tr213;
+ goto tr212;
+st17:
+ if ( ++p == pe )
+ goto _out17;
+case 17:
+ if ( (*p) == 10 )
+ goto tr29;
+ goto st16;
+tr181:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st92;
+st92:
+ if ( ++p == pe )
+ goto _out92;
+case 92:
+#line 3230 "rlscan.cpp"
+ if ( (*p) == 10 )
+ goto tr33;
+ goto st18;
+st18:
+ if ( ++p == pe )
+ goto _out18;
+case 18:
+ if ( (*p) == 10 )
+ goto tr33;
+ goto st18;
+st93:
+ if ( ++p == pe )
+ goto _out93;
+case 93:
+ switch( (*p) ) {
+ case 33: goto tr215;
+ case 42: goto tr216;
+ case 47: goto tr217;
+ case 63: goto tr218;
+ case 94: goto tr219;
+ case 126: goto tr220;
+ }
+ goto tr214;
+st94:
+ if ( ++p == pe )
+ goto _out94;
+case 94:
+ switch( (*p) ) {
+ case 33: goto tr221;
+ case 42: goto tr222;
+ case 47: goto tr223;
+ case 63: goto tr224;
+ case 94: goto tr225;
+ case 126: goto tr226;
+ }
+ goto tr214;
+tr184:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st95;
+st95:
+ if ( ++p == pe )
+ goto _out95;
+case 95:
+#line 3275 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr35;
+ case 39: goto st91;
+ case 92: goto st20;
+ }
+ goto st19;
+tr35:
+#line 532 "rlscan.rl"
+ {
+ lastnl = p;
+ column = 0;
+ line++;
+ }
+ goto st19;
+st19:
+ if ( ++p == pe )
+ goto _out19;
+case 19:
+#line 3294 "rlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr35;
+ case 39: goto st91;
+ case 92: goto st20;
+ }
+ goto st19;
+st20:
+ if ( ++p == pe )
+ goto _out20;
+case 20:
+ if ( (*p) == 10 )
+ goto tr35;
+ goto st19;
+st96:
+ if ( ++p == pe )
+ goto _out96;
+case 96:
+ if ( (*p) == 42 )
+ goto tr227;
+ goto tr214;
+st97:
+ if ( ++p == pe )
+ goto _out97;
+case 97:
+ switch( (*p) ) {
+ case 45: goto tr228;
+ case 62: goto tr229;
+ }
+ goto tr214;
+st98:
+ if ( ++p == pe )
+ goto _out98;
+case 98:
+ if ( (*p) == 46 )
+ goto tr230;
+ goto tr214;
+tr189:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st99;
+st99:
+ if ( ++p == pe )
+ goto _out99;
+case 99:
+#line 3339 "rlscan.cpp"
+ if ( (*p) == 120 )
+ goto st21;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st100;
+ goto tr231;
+st100:
+ if ( ++p == pe )
+ goto _out100;
+case 100:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st100;
+ goto tr231;
+st21:
+ if ( ++p == pe )
+ goto _out21;
+case 21:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st101;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st101;
+ } else
+ goto st101;
+ goto tr37;
+st101:
+ if ( ++p == pe )
+ goto _out101;
+case 101:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto st101;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto st101;
+ } else
+ goto st101;
+ goto tr233;
+st102:
+ if ( ++p == pe )
+ goto _out102;
+case 102:
+ switch( (*p) ) {
+ case 58: goto tr234;
+ case 61: goto tr235;
+ case 62: goto st103;
+ }
+ goto tr214;
+st103:
+ if ( ++p == pe )
+ goto _out103;
+case 103:
+ if ( (*p) == 62 )
+ goto tr238;
+ goto tr237;
+st104:
+ if ( ++p == pe )
+ goto _out104;
+case 104:
+ switch( (*p) ) {
+ case 33: goto tr239;
+ case 42: goto tr240;
+ case 47: goto tr241;
+ case 58: goto tr242;
+ case 62: goto st105;
+ case 94: goto tr244;
+ case 126: goto tr245;
+ }
+ goto tr214;
+st105:
+ if ( ++p == pe )
+ goto _out105;
+case 105:
+ switch( (*p) ) {
+ case 33: goto tr247;
+ case 42: goto tr248;
+ case 47: goto tr249;
+ case 94: goto tr250;
+ case 126: goto tr251;
+ }
+ goto tr246;
+st106:
+ if ( ++p == pe )
+ goto _out106;
+case 106:
+ if ( (*p) == 62 )
+ goto tr252;
+ goto tr214;
+st107:
+ if ( ++p == pe )
+ goto _out107;
+case 107:
+ switch( (*p) ) {
+ case 33: goto tr253;
+ case 42: goto tr254;
+ case 47: goto tr255;
+ case 63: goto tr256;
+ case 94: goto tr257;
+ case 126: goto tr258;
+ }
+ goto tr214;
+st108:
+ if ( ++p == pe )
+ goto _out108;
+case 108:
+ switch( (*p) ) {
+ case 33: goto tr259;
+ case 42: goto tr260;
+ case 47: goto tr261;
+ case 94: goto tr262;
+ case 126: goto tr263;
+ }
+ goto tr214;
+tr196:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 774 "rlscan.rl"
+ {act = 78;}
+ goto st109;
+tr274:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 755 "rlscan.rl"
+ {act = 69;}
+ goto st109;
+tr277:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 744 "rlscan.rl"
+ {act = 66;}
+ goto st109;
+tr283:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 745 "rlscan.rl"
+ {act = 67;}
+ goto st109;
+tr287:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 766 "rlscan.rl"
+ {act = 72;}
+ goto st109;
+tr288:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 767 "rlscan.rl"
+ {act = 73;}
+ goto st109;
+tr292:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 771 "rlscan.rl"
+ {act = 77;}
+ goto st109;
+tr295:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 770 "rlscan.rl"
+ {act = 76;}
+ goto st109;
+tr300:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 750 "rlscan.rl"
+ {act = 68;}
+ goto st109;
+tr306:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 739 "rlscan.rl"
+ {act = 64;}
+ goto st109;
+tr311:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 738 "rlscan.rl"
+ {act = 63;}
+ goto st109;
+tr314:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 768 "rlscan.rl"
+ {act = 74;}
+ goto st109;
+tr320:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 737 "rlscan.rl"
+ {act = 62;}
+ goto st109;
+tr321:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 769 "rlscan.rl"
+ {act = 75;}
+ goto st109;
+tr328:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 760 "rlscan.rl"
+ {act = 70;}
+ goto st109;
+tr332:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 765 "rlscan.rl"
+ {act = 71;}
+ goto st109;
+tr335:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+#line 740 "rlscan.rl"
+ {act = 65;}
+ goto st109;
+st109:
+ if ( ++p == pe )
+ goto _out109;
+case 109:
+#line 3559 "rlscan.cpp"
+ if ( (*p) == 95 )
+ goto tr196;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr264;
+st110:
+ if ( ++p == pe )
+ goto _out110;
+case 110:
+ if ( (*p) == 94 )
+ goto tr266;
+ goto tr265;
+st111:
+ if ( ++p == pe )
+ goto _out111;
+case 111:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 99: goto st112;
+ case 108: goto st119;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st112:
+ if ( ++p == pe )
+ goto _out112;
+case 112:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 99: goto st113;
+ case 116: goto st116;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st113:
+ if ( ++p == pe )
+ goto _out113;
+case 113:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto st114;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st114:
+ if ( ++p == pe )
+ goto _out114;
+case 114:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 115: goto st115;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st115:
+ if ( ++p == pe )
+ goto _out115;
+case 115:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 115: goto tr274;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st116:
+ if ( ++p == pe )
+ goto _out116;
+case 116:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 105: goto st117;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st117:
+ if ( ++p == pe )
+ goto _out117;
+case 117:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 111: goto st118;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st118:
+ if ( ++p == pe )
+ goto _out118;
+case 118:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 110: goto tr277;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st119:
+ if ( ++p == pe )
+ goto _out119;
+case 119:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 112: goto st120;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st120:
+ if ( ++p == pe )
+ goto _out120;
+case 120:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 104: goto st121;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st121:
+ if ( ++p == pe )
+ goto _out121;
+case 121:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 116: goto st122;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st122:
+ if ( ++p == pe )
+ goto _out122;
+case 122:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 121: goto st123;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st123:
+ if ( ++p == pe )
+ goto _out123;
+case 123:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 112: goto st124;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st124:
+ if ( ++p == pe )
+ goto _out124;
+case 124:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto tr283;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st125:
+ if ( ++p == pe )
+ goto _out125;
+case 125:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 111: goto st126;
+ case 114: goto st127;
+ case 120: goto st128;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st126:
+ if ( ++p == pe )
+ goto _out126;
+case 126:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 102: goto tr287;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st127:
+ if ( ++p == pe )
+ goto _out127;
+case 127:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 114: goto tr288;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st128:
+ if ( ++p == pe )
+ goto _out128;
+case 128:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 112: goto st129;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st129:
+ if ( ++p == pe )
+ goto _out129;
+case 129:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 111: goto st130;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st130:
+ if ( ++p == pe )
+ goto _out130;
+case 130:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 114: goto st131;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st131:
+ if ( ++p == pe )
+ goto _out131;
+case 131:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 116: goto tr292;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st132:
+ if ( ++p == pe )
+ goto _out132;
+case 132:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 114: goto st133;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st133:
+ if ( ++p == pe )
+ goto _out133;
+case 133:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 111: goto st134;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st134:
+ if ( ++p == pe )
+ goto _out134;
+case 134:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 109: goto tr295;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st135:
+ if ( ++p == pe )
+ goto _out135;
+case 135:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto st136;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st136:
+ if ( ++p == pe )
+ goto _out136;
+case 136:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 116: goto st137;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st137:
+ if ( ++p == pe )
+ goto _out137;
+case 137:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 107: goto st138;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st138:
+ if ( ++p == pe )
+ goto _out138;
+case 138:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto st139;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st139:
+ if ( ++p == pe )
+ goto _out139;
+case 139:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 121: goto tr300;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st140:
+ if ( ++p == pe )
+ goto _out140;
+case 140:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 109: goto st141;
+ case 110: goto st145;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st141:
+ if ( ++p == pe )
+ goto _out141;
+case 141:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 112: goto st142;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st142:
+ if ( ++p == pe )
+ goto _out142;
+case 142:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 111: goto st143;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st143:
+ if ( ++p == pe )
+ goto _out143;
+case 143:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 114: goto st144;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st144:
+ if ( ++p == pe )
+ goto _out144;
+case 144:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 116: goto tr306;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st145:
+ if ( ++p == pe )
+ goto _out145;
+case 145:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 99: goto st146;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st146:
+ if ( ++p == pe )
+ goto _out146;
+case 146:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 108: goto st147;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st147:
+ if ( ++p == pe )
+ goto _out147;
+case 147:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 117: goto st148;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st148:
+ if ( ++p == pe )
+ goto _out148;
+case 148:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 100: goto st149;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st149:
+ if ( ++p == pe )
+ goto _out149;
+case 149:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto tr311;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st150:
+ if ( ++p == pe )
+ goto _out150;
+case 150:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto st151;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st151:
+ if ( ++p == pe )
+ goto _out151;
+case 151:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 114: goto st152;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st152:
+ if ( ++p == pe )
+ goto _out152;
+case 152:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 114: goto tr314;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st153:
+ if ( ++p == pe )
+ goto _out153;
+case 153:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 97: goto st154;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 98 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st154:
+ if ( ++p == pe )
+ goto _out154;
+case 154:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 99: goto st155;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st155:
+ if ( ++p == pe )
+ goto _out155;
+case 155:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 104: goto st156;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st156:
+ if ( ++p == pe )
+ goto _out156;
+case 156:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 105: goto st157;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st157:
+ if ( ++p == pe )
+ goto _out157;
+case 157:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 110: goto st158;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st158:
+ if ( ++p == pe )
+ goto _out158;
+case 158:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto tr320;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st159:
+ if ( ++p == pe )
+ goto _out159;
+case 159:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 111: goto tr321;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st160:
+ if ( ++p == pe )
+ goto _out160;
+case 160:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 97: goto st161;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 98 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st161:
+ if ( ++p == pe )
+ goto _out161;
+case 161:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 114: goto st162;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st162:
+ if ( ++p == pe )
+ goto _out162;
+case 162:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 105: goto st163;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st163:
+ if ( ++p == pe )
+ goto _out163;
+case 163:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 97: goto st164;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 98 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st164:
+ if ( ++p == pe )
+ goto _out164;
+case 164:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 98: goto st165;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st165:
+ if ( ++p == pe )
+ goto _out165;
+case 165:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 108: goto st166;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st166:
+ if ( ++p == pe )
+ goto _out166;
+case 166:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto tr328;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st167:
+ if ( ++p == pe )
+ goto _out167;
+case 167:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 104: goto st168;
+ case 114: goto st170;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st168:
+ if ( ++p == pe )
+ goto _out168;
+case 168:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto st169;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st169:
+ if ( ++p == pe )
+ goto _out169;
+case 169:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 110: goto tr332;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st170:
+ if ( ++p == pe )
+ goto _out170;
+case 170:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 105: goto st171;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st171:
+ if ( ++p == pe )
+ goto _out171;
+case 171:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 116: goto st172;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st172:
+ if ( ++p == pe )
+ goto _out172;
+case 172:
+ switch( (*p) ) {
+ case 95: goto tr196;
+ case 101: goto tr335;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr196;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr196;
+ } else
+ goto tr196;
+ goto tr267;
+st173:
+ if ( ++p == pe )
+ goto _out173;
+case 173:
+ if ( (*p) == 42 )
+ goto tr336;
+ goto tr214;
+tr210:
+#line 1 "rlscan.rl"
+ {tokend = p+1;}
+ goto st174;
+st174:
+ if ( ++p == pe )
+ goto _out174;
+case 174:
+#line 4653 "rlscan.cpp"
+ if ( (*p) == 37 )
+ goto st22;
+ goto tr214;
+st22:
+ if ( ++p == pe )
+ goto _out22;
+case 22:
+ if ( (*p) == 37 )
+ goto tr40;
+ goto tr39;
+ }
+ _out23: cs = 23; goto _out;
+ _out24: cs = 24; goto _out;
+ _out25: cs = 25; goto _out;
+ _out1: cs = 1; goto _out;
+ _out2: cs = 2; goto _out;
+ _out26: cs = 26; goto _out;
+ _out27: cs = 27; goto _out;
+ _out28: cs = 28; goto _out;
+ _out3: cs = 3; goto _out;
+ _out4: cs = 4; goto _out;
+ _out29: cs = 29; goto _out;
+ _out5: cs = 5; goto _out;
+ _out6: cs = 6; goto _out;
+ _out7: cs = 7; goto _out;
+ _out30: cs = 30; goto _out;
+ _out31: cs = 31; goto _out;
+ _out32: cs = 32; goto _out;
+ _out33: cs = 33; goto _out;
+ _out34: cs = 34; goto _out;
+ _out35: cs = 35; goto _out;
+ _out36: cs = 36; goto _out;
+ _out37: cs = 37; goto _out;
+ _out38: cs = 38; goto _out;
+ _out39: cs = 39; goto _out;
+ _out8: cs = 8; goto _out;
+ _out9: cs = 9; goto _out;
+ _out40: cs = 40; goto _out;
+ _out10: cs = 10; goto _out;
+ _out11: cs = 11; goto _out;
+ _out41: cs = 41; goto _out;
+ _out12: cs = 12; goto _out;
+ _out13: cs = 13; goto _out;
+ _out14: cs = 14; goto _out;
+ _out42: cs = 42; goto _out;
+ _out43: cs = 43; goto _out;
+ _out15: cs = 15; goto _out;
+ _out44: cs = 44; goto _out;
+ _out45: cs = 45; goto _out;
+ _out46: cs = 46; goto _out;
+ _out47: cs = 47; goto _out;
+ _out48: cs = 48; goto _out;
+ _out49: cs = 49; goto _out;
+ _out50: cs = 50; goto _out;
+ _out51: cs = 51; goto _out;
+ _out52: cs = 52; goto _out;
+ _out53: cs = 53; goto _out;
+ _out54: cs = 54; goto _out;
+ _out55: cs = 55; goto _out;
+ _out56: cs = 56; goto _out;
+ _out57: cs = 57; goto _out;
+ _out58: cs = 58; goto _out;
+ _out59: cs = 59; goto _out;
+ _out60: cs = 60; goto _out;
+ _out61: cs = 61; goto _out;
+ _out62: cs = 62; goto _out;
+ _out63: cs = 63; goto _out;
+ _out64: cs = 64; goto _out;
+ _out65: cs = 65; goto _out;
+ _out66: cs = 66; goto _out;
+ _out67: cs = 67; goto _out;
+ _out68: cs = 68; goto _out;
+ _out69: cs = 69; goto _out;
+ _out70: cs = 70; goto _out;
+ _out71: cs = 71; goto _out;
+ _out72: cs = 72; goto _out;
+ _out73: cs = 73; goto _out;
+ _out74: cs = 74; goto _out;
+ _out75: cs = 75; goto _out;
+ _out76: cs = 76; goto _out;
+ _out77: cs = 77; goto _out;
+ _out78: cs = 78; goto _out;
+ _out79: cs = 79; goto _out;
+ _out80: cs = 80; goto _out;
+ _out81: cs = 81; goto _out;
+ _out82: cs = 82; goto _out;
+ _out83: cs = 83; goto _out;
+ _out84: cs = 84; goto _out;
+ _out85: cs = 85; goto _out;
+ _out0: cs = 0; goto _out;
+ _out86: cs = 86; goto _out;
+ _out87: cs = 87; goto _out;
+ _out88: cs = 88; goto _out;
+ _out89: cs = 89; goto _out;
+ _out90: cs = 90; goto _out;
+ _out16: cs = 16; goto _out;
+ _out91: cs = 91; goto _out;
+ _out17: cs = 17; goto _out;
+ _out92: cs = 92; goto _out;
+ _out18: cs = 18; goto _out;
+ _out93: cs = 93; goto _out;
+ _out94: cs = 94; goto _out;
+ _out95: cs = 95; goto _out;
+ _out19: cs = 19; goto _out;
+ _out20: cs = 20; goto _out;
+ _out96: cs = 96; goto _out;
+ _out97: cs = 97; goto _out;
+ _out98: cs = 98; goto _out;
+ _out99: cs = 99; goto _out;
+ _out100: cs = 100; goto _out;
+ _out21: cs = 21; goto _out;
+ _out101: cs = 101; goto _out;
+ _out102: cs = 102; goto _out;
+ _out103: cs = 103; goto _out;
+ _out104: cs = 104; goto _out;
+ _out105: cs = 105; goto _out;
+ _out106: cs = 106; goto _out;
+ _out107: cs = 107; goto _out;
+ _out108: cs = 108; goto _out;
+ _out109: cs = 109; goto _out;
+ _out110: cs = 110; goto _out;
+ _out111: cs = 111; goto _out;
+ _out112: cs = 112; goto _out;
+ _out113: cs = 113; goto _out;
+ _out114: cs = 114; goto _out;
+ _out115: cs = 115; goto _out;
+ _out116: cs = 116; goto _out;
+ _out117: cs = 117; goto _out;
+ _out118: cs = 118; goto _out;
+ _out119: cs = 119; goto _out;
+ _out120: cs = 120; goto _out;
+ _out121: cs = 121; goto _out;
+ _out122: cs = 122; goto _out;
+ _out123: cs = 123; goto _out;
+ _out124: cs = 124; goto _out;
+ _out125: cs = 125; goto _out;
+ _out126: cs = 126; goto _out;
+ _out127: cs = 127; goto _out;
+ _out128: cs = 128; goto _out;
+ _out129: cs = 129; goto _out;
+ _out130: cs = 130; goto _out;
+ _out131: cs = 131; goto _out;
+ _out132: cs = 132; goto _out;
+ _out133: cs = 133; goto _out;
+ _out134: cs = 134; goto _out;
+ _out135: cs = 135; goto _out;
+ _out136: cs = 136; goto _out;
+ _out137: cs = 137; goto _out;
+ _out138: cs = 138; goto _out;
+ _out139: cs = 139; goto _out;
+ _out140: cs = 140; goto _out;
+ _out141: cs = 141; goto _out;
+ _out142: cs = 142; goto _out;
+ _out143: cs = 143; goto _out;
+ _out144: cs = 144; goto _out;
+ _out145: cs = 145; goto _out;
+ _out146: cs = 146; goto _out;
+ _out147: cs = 147; goto _out;
+ _out148: cs = 148; goto _out;
+ _out149: cs = 149; goto _out;
+ _out150: cs = 150; goto _out;
+ _out151: cs = 151; goto _out;
+ _out152: cs = 152; goto _out;
+ _out153: cs = 153; goto _out;
+ _out154: cs = 154; goto _out;
+ _out155: cs = 155; goto _out;
+ _out156: cs = 156; goto _out;
+ _out157: cs = 157; goto _out;
+ _out158: cs = 158; goto _out;
+ _out159: cs = 159; goto _out;
+ _out160: cs = 160; goto _out;
+ _out161: cs = 161; goto _out;
+ _out162: cs = 162; goto _out;
+ _out163: cs = 163; goto _out;
+ _out164: cs = 164; goto _out;
+ _out165: cs = 165; goto _out;
+ _out166: cs = 166; goto _out;
+ _out167: cs = 167; goto _out;
+ _out168: cs = 168; goto _out;
+ _out169: cs = 169; goto _out;
+ _out170: cs = 170; goto _out;
+ _out171: cs = 171; goto _out;
+ _out172: cs = 172; goto _out;
+ _out173: cs = 173; goto _out;
+ _out174: cs = 174; goto _out;
+ _out22: cs = 22; goto _out;
+
+ _out: {}
+ }
+#line 972 "rlscan.rl"
+
+ /* Check if we failed. */
+ if ( cs == rlscan_error ) {
+ /* Machine failed before finding a token. I'm not yet sure if this
+ * is reachable. */
+ scan_error() << "scanner error" << endl;
+ exit(1);
+ }
+
+ /* Decide if we need to preserve anything. */
+ char *preserve = tokstart;
+
+ /* Now set up the prefix. */
+ if ( preserve == 0 )
+ have = 0;
+ else {
+ /* There is data that needs to be shifted over. */
+ have = pe - preserve;
+ memmove( buf, preserve, have );
+ unsigned int shiftback = preserve - buf;
+ if ( tokstart != 0 )
+ tokstart -= shiftback;
+ tokend -= shiftback;
+
+ preserve = buf;
+ }
+ }
+
+ delete[] buf;
+}
+
+void scan( char *fileName, istream &input, ostream &output )
+{
+}
diff --git a/contrib/tools/ragel5/ragel/rlscan.h b/contrib/tools/ragel5/ragel/rlscan.h
new file mode 100644
index 0000000000..e6302aa4c9
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/rlscan.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _RLSCAN_H
+#define _RLSCAN_H
+
+#include <iostream>
+#include "rlscan.h"
+#include "vector.h"
+#include "rlparse.h"
+#include "parsedata.h"
+#include "avltree.h"
+#include "vector.h"
+
+using std::istream;
+using std::ostream;
+
+extern char *Parser_lelNames[];
+
+/* This is used for tracking the current stack of include file/machine pairs. It is
+ * is used to detect and recursive include structure. */
+struct IncludeStackItem
+{
+ IncludeStackItem(const char *fileName, char *sectionName )
+ : fileName(fileName), sectionName(sectionName) {}
+
+ const char *fileName;
+ char *sectionName;
+};
+
+typedef Vector<IncludeStackItem> IncludeStack;
+
+inline char* resolvePath(const char* rel, const char* abs) {
+ const size_t l1 = strlen(rel);
+ const size_t l2 = strlen(abs);
+ char* ret = new char[l1 + l2 + 1];
+
+ const char* p = strrchr(abs, '/') + 1;
+ const size_t l3 = p - abs;
+
+ memcpy(ret, abs, l3);
+ strcpy(ret + l3, rel);
+
+ return ret;
+}
+
+struct Scanner
+{
+ Scanner(const char *fileName, istream &input, ostream &output,
+ Parser *inclToParser, char *inclSectionTarg,
+ int includeDepth, bool importMachines )
+ :
+ fileName(fileName), input(input), output(output),
+ inclToParser(inclToParser),
+ inclSectionTarg(inclSectionTarg),
+ includeDepth(includeDepth),
+ importMachines(importMachines),
+ cur_token(0),
+ line(1), column(1), lastnl(0),
+ parser(0), ignoreSection(false),
+ parserExistsError(false),
+ whitespaceOn(true),
+ lastToken(0)
+ {}
+
+ bool recursiveInclude(const char *inclFileName, char *inclSectionName );
+
+ char *prepareFileName( char *inclFileName, int len )
+ {
+ if (*inclFileName == '\"') {
+ inclFileName[len - 1] = 0;
+ ++inclFileName;
+ }
+ char* res = resolvePath(inclFileName, fileName); // there was a memory leek in the original too
+ return res;
+ }
+
+ void init();
+ void token( int type, char *start, char *end );
+ void token( int type, char c );
+ void token( int type );
+ void processToken( int type, char *tokdata, int toklen );
+ void directToParser( Parser *toParser, const char *tokFileName, int tokLine,
+ int tokColumn, int type, char *tokdata, int toklen );
+ void flushImport( );
+ void importToken( int type, char *start, char *end );
+ void pass( int token, char *start, char *end );
+ void pass();
+ void updateCol();
+ void startSection();
+ void endSection();
+ void do_scan();
+ bool active();
+ ostream &scan_error();
+
+ const char *fileName;
+ istream &input;
+ ostream &output;
+ Parser *inclToParser;
+ char *inclSectionTarg;
+ int includeDepth;
+ bool importMachines;
+
+ /* For import parsing. */
+ int tok_cs, tok_act;
+ int *tok_tokstart, *tok_tokend;
+ int cur_token;
+ static const int max_tokens = 32;
+ int token_data[max_tokens];
+ char *token_strings[max_tokens];
+ int token_lens[max_tokens];
+
+ /* For section processing. */
+ int cs;
+ char *word, *lit;
+ int word_len, lit_len;
+
+ /* For character scanning. */
+ int line;
+ InputLoc sectionLoc;
+ char *tokstart, *tokend;
+ int column;
+ char *lastnl;
+
+ /* Set by machine statements, these persist from section to section
+ * allowing for unnamed sections. */
+ Parser *parser;
+ bool ignoreSection;
+ IncludeStack includeStack;
+
+ /* This is set if ragel has already emitted an error stating that
+ * no section name has been seen and thus no parser exists. */
+ bool parserExistsError;
+
+ /* This is for inline code. By default it is on. It goes off for
+ * statements and values in inline blocks which are parsed. */
+ bool whitespaceOn;
+
+ /* Keeps a record of the previous token sent to the section parser. */
+ int lastToken;
+};
+
+#endif /* _RLSCAN_H */
diff --git a/contrib/tools/ragel5/ragel/xmlcodegen.cpp b/contrib/tools/ragel5/ragel/xmlcodegen.cpp
new file mode 100644
index 0000000000..021c97e87d
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/xmlcodegen.cpp
@@ -0,0 +1,713 @@
+/*
+ * Copyright 2005, 2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "ragel.h"
+#include "xmlcodegen.h"
+#include "parsedata.h"
+#include "fsmgraph.h"
+#include <string.h>
+
+using namespace std;
+
+XMLCodeGen::XMLCodeGen( char *fsmName, ParseData *pd, FsmAp *fsm,
+ std::ostream &out )
+:
+ fsmName(fsmName),
+ pd(pd),
+ fsm(fsm),
+ out(out),
+ nextActionTableId(0)
+{
+}
+
+
+void XMLCodeGen::writeActionList()
+{
+ /* Determine which actions to write. */
+ int nextActionId = 0;
+ for ( ActionList::Iter act = pd->actionList; act.lte(); act++ ) {
+ if ( act->numRefs() > 0 || act->numCondRefs > 0 )
+ act->actionId = nextActionId++;
+ }
+
+ /* Write the list. */
+ out << " <action_list length=\"" << nextActionId << "\">\n";
+ for ( ActionList::Iter act = pd->actionList; act.lte(); act++ ) {
+ if ( act->actionId >= 0 )
+ writeAction( act );
+ }
+ out << " </action_list>\n";
+}
+
+void XMLCodeGen::writeActionTableList()
+{
+ /* Must first order the action tables based on their id. */
+ int numTables = nextActionTableId;
+ RedActionTable **tables = new RedActionTable*[numTables];
+ for ( ActionTableMap::Iter at = actionTableMap; at.lte(); at++ )
+ tables[at->id] = at;
+
+ out << " <action_table_list length=\"" << numTables << "\">\n";
+ for ( int t = 0; t < numTables; t++ ) {
+ out << " <action_table id=\"" << t << "\" length=\"" <<
+ tables[t]->key.length() << "\">";
+ for ( ActionTable::Iter atel = tables[t]->key; atel.lte(); atel++ ) {
+ out << atel->value->actionId;
+ if ( ! atel.last() )
+ out << " ";
+ }
+ out << "</action_table>\n";
+ }
+ out << " </action_table_list>\n";
+
+ delete[] tables;
+}
+
+void XMLCodeGen::reduceActionTables()
+{
+ /* Reduce the actions tables to a set. */
+ for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) {
+ RedActionTable *actionTable = 0;
+
+ /* Reduce To State Actions. */
+ if ( st->toStateActionTable.length() > 0 ) {
+ if ( actionTableMap.insert( st->toStateActionTable, &actionTable ) )
+ actionTable->id = nextActionTableId++;
+ }
+
+ /* Reduce From State Actions. */
+ if ( st->fromStateActionTable.length() > 0 ) {
+ if ( actionTableMap.insert( st->fromStateActionTable, &actionTable ) )
+ actionTable->id = nextActionTableId++;
+ }
+
+ /* Reduce EOF actions. */
+ if ( st->eofActionTable.length() > 0 ) {
+ if ( actionTableMap.insert( st->eofActionTable, &actionTable ) )
+ actionTable->id = nextActionTableId++;
+ }
+
+ /* Loop the transitions and reduce their actions. */
+ for ( TransList::Iter trans = st->outList; trans.lte(); trans++ ) {
+ if ( trans->actionTable.length() > 0 ) {
+ if ( actionTableMap.insert( trans->actionTable, &actionTable ) )
+ actionTable->id = nextActionTableId++;
+ }
+ }
+ }
+}
+
+void XMLCodeGen::appendTrans( TransListVect &outList, Key lowKey,
+ Key highKey, TransAp *trans )
+{
+ if ( trans->toState != 0 || trans->actionTable.length() > 0 )
+ outList.append( TransEl( lowKey, highKey, trans ) );
+}
+
+void XMLCodeGen::writeKey( Key key )
+{
+ if ( keyOps->isSigned )
+ out << key.getVal();
+ else
+ out << (unsigned long) key.getVal();
+}
+
+void XMLCodeGen::writeTrans( Key lowKey, Key highKey, TransAp *trans )
+{
+ /* First reduce the action. */
+ RedActionTable *actionTable = 0;
+ if ( trans->actionTable.length() > 0 )
+ actionTable = actionTableMap.find( trans->actionTable );
+
+ /* Write the transition. */
+ out << " <t>";
+ writeKey( lowKey );
+ out << " ";
+ writeKey( highKey );
+
+ if ( trans->toState != 0 )
+ out << " " << trans->toState->alg.stateNum;
+ else
+ out << " x";
+
+ if ( actionTable != 0 )
+ out << " " << actionTable->id;
+ else
+ out << " x";
+ out << "</t>\n";
+}
+
+void XMLCodeGen::writeTransList( StateAp *state )
+{
+ TransListVect outList;
+
+ /* If there is only are no ranges the task is simple. */
+ if ( state->outList.length() > 0 ) {
+ /* Loop each source range. */
+ for ( TransList::Iter trans = state->outList; trans.lte(); trans++ ) {
+ /* Reduce the transition. If it reduced to anything then add it. */
+ appendTrans( outList, trans->lowKey, trans->highKey, trans );
+ }
+ }
+
+ out << " <trans_list length=\"" << outList.length() << "\">\n";
+ for ( TransListVect::Iter tvi = outList; tvi.lte(); tvi++ )
+ writeTrans( tvi->lowKey, tvi->highKey, tvi->value );
+ out << " </trans_list>\n";
+}
+
+void XMLCodeGen::writeLmSwitch( InlineItem *item )
+{
+ LongestMatch *longestMatch = item->longestMatch;
+
+ out << "<lm_switch";
+ if ( longestMatch->lmSwitchHandlesError )
+ out << " handles_error=\"t\"";
+ out << ">\n";
+
+ for ( LmPartList::Iter lmi = *longestMatch->longestMatchList; lmi.lte(); lmi++ ) {
+ if ( lmi->inLmSelect && lmi->action != 0 ) {
+ /* Open the action. Write it with the context that sets up _p
+ * when doing control flow changes from inside the machine. */
+ out << " <sub_action id=\"" << lmi->longestMatchId << "\">";
+ writeInlineList( lmi->action->inlineList, item );
+ out << "</sub_action>\n";
+ }
+ }
+
+ out << " </lm_switch><exec><get_tokend></get_tokend></exec>";
+}
+
+void XMLCodeGen::writeText( InlineItem *item )
+{
+ if ( item->prev == 0 || item->prev->type != InlineItem::Text )
+ out << "<text>";
+ xmlEscapeHost( out, item->data, strlen(item->data) );
+ if ( item->next == 0 || item->next->type != InlineItem::Text )
+ out << "</text>";
+}
+
+void XMLCodeGen::writeCtrlFlow( InlineItem *item, InlineItem *context )
+{
+ if ( context != 0 ) {
+ out << "<sub_action>";
+
+ switch ( context->type ) {
+ case InlineItem::LmOnLast:
+ out << "<exec><get_tokend></get_tokend></exec>";
+ break;
+ case InlineItem::LmOnNext:
+ out << "<exec><get_tokend></get_tokend></exec>";
+ break;
+ case InlineItem::LmOnLagBehind:
+ out << "<exec><get_tokend></get_tokend></exec>";
+ break;
+ case InlineItem::LmSwitch:
+ out << "<exec><get_tokend></get_tokend></exec>";
+ break;
+ default: break;
+ }
+ }
+
+ switch ( item->type ) {
+ case InlineItem::Goto:
+ writeGoto( item, context );
+ break;
+ case InlineItem::GotoExpr:
+ writeGotoExpr( item, context );
+ break;
+ case InlineItem::Call:
+ writeCall( item, context );
+ break;
+ case InlineItem::CallExpr:
+ writeCallExpr( item, context );
+ break;
+ case InlineItem::Next:
+ writeNext( item, context );
+ break;
+ case InlineItem::NextExpr:
+ writeNextExpr( item, context );
+ break;
+ case InlineItem::Break:
+ out << "<break></break>";
+ break;
+ case InlineItem::Ret:
+ out << "<ret></ret>";
+ break;
+ default: break;
+ }
+
+ if ( context != 0 )
+ out << "</sub_action>";
+}
+
+void XMLCodeGen::writePtrMod( InlineItem *item, InlineItem *context )
+{
+ if ( context != 0 && ( context->type == InlineItem::LmOnNext ||
+ context->type == InlineItem::LmOnLagBehind ||
+ context->type == InlineItem::LmSwitch ) )
+ {
+ switch ( item->type ) {
+ case InlineItem::Hold:
+ out << "<holdte></holdte>";
+ break;
+ case InlineItem::Exec:
+ writeActionExecTE( item );
+ break;
+ default: break;
+ }
+ }
+ else {
+ switch ( item->type ) {
+ case InlineItem::Hold:
+ out << "<hold></hold>";
+ break;
+ case InlineItem::Exec:
+ writeActionExec( item );
+ break;
+ default: break;
+ }
+ }
+}
+
+
+void XMLCodeGen::writeGoto( InlineItem *item, InlineItem *context )
+{
+ if ( pd->generatingSectionSubset )
+ out << "<goto>-1</goto>";
+ else {
+ EntryMapEl *targ = fsm->entryPoints.find( item->nameTarg->id );
+ out << "<goto>" << targ->value->alg.stateNum << "</goto>";
+ }
+}
+
+void XMLCodeGen::writeCall( InlineItem *item, InlineItem *context )
+{
+ if ( pd->generatingSectionSubset )
+ out << "<call>-1</call>";
+ else {
+ EntryMapEl *targ = fsm->entryPoints.find( item->nameTarg->id );
+ out << "<call>" << targ->value->alg.stateNum << "</call>";
+ }
+}
+
+void XMLCodeGen::writeNext( InlineItem *item, InlineItem *context )
+{
+ if ( pd->generatingSectionSubset )
+ out << "<next>-1</next>";
+ else {
+ EntryMapEl *targ = fsm->entryPoints.find( item->nameTarg->id );
+ out << "<next>" << targ->value->alg.stateNum << "</next>";
+ }
+}
+
+void XMLCodeGen::writeGotoExpr( InlineItem *item, InlineItem *context )
+{
+ out << "<goto_expr>";
+ writeInlineList( item->children, 0 );
+ out << "</goto_expr>";
+}
+
+void XMLCodeGen::writeCallExpr( InlineItem *item, InlineItem *context )
+{
+ out << "<call_expr>";
+ writeInlineList( item->children, 0 );
+ out << "</call_expr>";
+}
+
+void XMLCodeGen::writeNextExpr( InlineItem *item, InlineItem *context )
+{
+ out << "<next_expr>";
+ writeInlineList( item->children, 0 );
+ out << "</next_expr>";
+}
+
+void XMLCodeGen::writeEntry( InlineItem * item )
+{
+ if ( pd->generatingSectionSubset )
+ out << "<entry>-1</entry>";
+ else {
+ EntryMapEl *targ = fsm->entryPoints.find( item->nameTarg->id );
+ out << "<entry>" << targ->value->alg.stateNum << "</entry>";
+ }
+}
+
+void XMLCodeGen::writeActionExec( InlineItem *item )
+{
+ out << "<exec>";
+ writeInlineList( item->children, 0 );
+ out << "</exec>";
+}
+
+void XMLCodeGen::writeActionExecTE( InlineItem *item )
+{
+ out << "<execte>";
+ writeInlineList( item->children, 0 );
+ out << "</execte>";
+}
+
+void XMLCodeGen::writeLmOnLast( InlineItem *item )
+{
+ out << "<set_tokend>1</set_tokend>";
+ if ( item->longestMatchPart->action != 0 ) {
+ out << "<sub_action>";
+ writeInlineList( item->longestMatchPart->action->inlineList, item );
+ out << "</sub_action>";
+ }
+ out << "<exec><get_tokend></get_tokend></exec>";
+}
+
+void XMLCodeGen::writeLmOnNext( InlineItem *item )
+{
+ out << "<set_tokend>0</set_tokend>";
+ if ( item->longestMatchPart->action != 0 ) {
+ out << "<sub_action>";
+ writeInlineList( item->longestMatchPart->action->inlineList, item );
+ out << "</sub_action>";
+ }
+ out << "<exec><get_tokend></get_tokend></exec>";
+}
+
+void XMLCodeGen::writeLmOnLagBehind( InlineItem *item )
+{
+ if ( item->longestMatchPart->action != 0 ) {
+ out << "<sub_action>";
+ writeInlineList( item->longestMatchPart->action->inlineList, item );
+ out << "</sub_action>";
+ }
+ out << "<exec><get_tokend></get_tokend></exec>";
+}
+
+
+void XMLCodeGen::writeInlineList( InlineList *inlineList, InlineItem *context )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case InlineItem::Text:
+ writeText( item );
+ break;
+ case InlineItem::Goto: case InlineItem::GotoExpr:
+ case InlineItem::Call: case InlineItem::CallExpr:
+ case InlineItem::Next: case InlineItem::NextExpr:
+ case InlineItem::Break: case InlineItem::Ret:
+ writeCtrlFlow( item, context );
+ break;
+ case InlineItem::PChar:
+ out << "<pchar></pchar>";
+ break;
+ case InlineItem::Char:
+ out << "<char></char>";
+ break;
+ case InlineItem::Curs:
+ out << "<curs></curs>";
+ break;
+ case InlineItem::Targs:
+ out << "<targs></targs>";
+ break;
+ case InlineItem::Entry:
+ writeEntry( item );
+ break;
+
+ case InlineItem::Hold:
+ case InlineItem::Exec:
+ writePtrMod( item, context );
+ break;
+
+ case InlineItem::LmSwitch:
+ writeLmSwitch( item );
+ break;
+ case InlineItem::LmSetActId:
+ out << "<set_act>" <<
+ item->longestMatchPart->longestMatchId <<
+ "</set_act>";
+ break;
+ case InlineItem::LmSetTokEnd:
+ out << "<set_tokend>1</set_tokend>";
+ break;
+ case InlineItem::LmOnLast:
+ writeLmOnLast( item );
+ break;
+ case InlineItem::LmOnNext:
+ writeLmOnNext( item );
+ break;
+ case InlineItem::LmOnLagBehind:
+ writeLmOnLagBehind( item );
+ break;
+ case InlineItem::LmInitAct:
+ out << "<init_act></init_act>";
+ break;
+ case InlineItem::LmInitTokStart:
+ out << "<init_tokstart></init_tokstart>";
+ break;
+ case InlineItem::LmSetTokStart:
+ out << "<set_tokstart></set_tokstart>";
+ break;
+ }
+ }
+}
+
+void XMLCodeGen::writeAction( Action *action )
+{
+ out << " <action id=\"" << action->actionId << "\"";
+ if ( action->name != 0 )
+ out << " name=\"" << action->name << "\"";
+ out << " line=\"" << action->loc.line << "\" col=\"" << action->loc.col << "\">";
+ writeInlineList( action->inlineList, 0 );
+ out << "</action>\n";
+}
+
+void xmlEscapeHost( std::ostream &out, char *data, int len )
+{
+ char *end = data + len;
+ while ( data != end ) {
+ switch ( *data ) {
+ case '<': out << "&lt;"; break;
+ case '>': out << "&gt;"; break;
+ case '&': out << "&amp;"; break;
+ default: out << *data; break;
+ }
+ data += 1;
+ }
+}
+
+void XMLCodeGen::writeStateActions( StateAp *state )
+{
+ RedActionTable *toStateActions = 0;
+ if ( state->toStateActionTable.length() > 0 )
+ toStateActions = actionTableMap.find( state->toStateActionTable );
+
+ RedActionTable *fromStateActions = 0;
+ if ( state->fromStateActionTable.length() > 0 )
+ fromStateActions = actionTableMap.find( state->fromStateActionTable );
+
+ RedActionTable *eofActions = 0;
+ if ( state->eofActionTable.length() > 0 )
+ eofActions = actionTableMap.find( state->eofActionTable );
+
+ if ( toStateActions != 0 || fromStateActions != 0 || eofActions != 0 ) {
+ out << " <state_actions>";
+ if ( toStateActions != 0 )
+ out << toStateActions->id;
+ else
+ out << "x";
+
+ if ( fromStateActions != 0 )
+ out << " " << fromStateActions->id;
+ else
+ out << " x";
+
+ if ( eofActions != 0 )
+ out << " " << eofActions->id;
+ else
+ out << " x"; out << "</state_actions>\n";
+ }
+}
+
+void XMLCodeGen::writeStateConditions( StateAp *state )
+{
+ if ( state->stateCondList.length() > 0 ) {
+ out << " <cond_list length=\"" << state->stateCondList.length() << "\">\n";
+ for ( StateCondList::Iter scdi = state->stateCondList; scdi.lte(); scdi++ ) {
+ out << " <c>";
+ writeKey( scdi->lowKey );
+ out << " ";
+ writeKey( scdi->highKey );
+ out << " ";
+ out << scdi->condSpace->condSpaceId;
+ out << "</c>\n";
+ }
+ out << " </cond_list>\n";
+ }
+}
+
+void XMLCodeGen::writeStateList()
+{
+ /* Write the list of states. */
+ out << " <state_list length=\"" << fsm->stateList.length() << "\">\n";
+ for ( StateList::Iter st = fsm->stateList; st.lte(); st++ ) {
+ out << " <state id=\"" << st->alg.stateNum << "\"";
+ if ( st->isFinState() )
+ out << " final=\"t\"";
+ out << ">\n";
+
+ writeStateActions( st );
+ writeStateConditions( st );
+ writeTransList( st );
+
+ out << " </state>\n";
+
+ if ( !st.last() )
+ out << "\n";
+ }
+ out << " </state_list>\n";
+}
+
+bool XMLCodeGen::writeNameInst( NameInst *nameInst )
+{
+ bool written = false;
+ if ( nameInst->parent != 0 )
+ written = writeNameInst( nameInst->parent );
+
+ if ( nameInst->name != 0 ) {
+ if ( written )
+ out << '_';
+ out << nameInst->name;
+ written = true;
+ }
+
+ return written;
+}
+
+void XMLCodeGen::writeEntryPoints()
+{
+ /* List of entry points other than start state. */
+ if ( fsm->entryPoints.length() > 0 || pd->lmRequiresErrorState ) {
+ out << " <entry_points";
+ if ( pd->lmRequiresErrorState )
+ out << " error=\"t\"";
+ out << ">\n";
+ for ( EntryMap::Iter en = fsm->entryPoints; en.lte(); en++ ) {
+ /* Get the name instantiation from nameIndex. */
+ NameInst *nameInst = pd->nameIndex[en->key];
+ StateAp *state = en->value;
+ out << " <entry name=\"";
+ writeNameInst( nameInst );
+ out << "\">" << state->alg.stateNum << "</entry>\n";
+ }
+ out << " </entry_points>\n";
+ }
+}
+
+void XMLCodeGen::writeMachine()
+{
+ /* Open the machine. */
+ out << " <machine>\n";
+
+ /* Action tables. */
+ reduceActionTables();
+
+ writeActionList();
+ writeActionTableList();
+ writeConditions();
+
+ /* Start state. */
+ GraphDictEl *mainEl = pd->graphDict.find( mainMachine );
+ if ( mainEl != 0 ) {
+ out << " <start_state>" << fsm->startState->alg.stateNum <<
+ "</start_state>\n";
+ }
+
+ /* Error state. */
+ if ( fsm->errState != 0 ) {
+ out << " <error_state>" << fsm->errState->alg.stateNum <<
+ "</error_state>\n";
+ }
+
+ writeEntryPoints();
+ writeStateList();
+
+ out << " </machine>\n";
+}
+
+void XMLCodeGen::writeAlphType()
+{
+ out << " <alphtype>" <<
+ (keyOps->alphType - hostLang->hostTypes) << "</alphtype>\n";
+}
+
+void XMLCodeGen::writeGetKeyExpr()
+{
+ out << " <getkey>";
+ writeInlineList( pd->getKeyExpr, 0 );
+ out << "</getkey>\n";
+}
+
+void XMLCodeGen::writeAccessExpr()
+{
+ out << " <access>";
+ writeInlineList( pd->accessExpr, 0 );
+ out << "</access>\n";
+}
+
+void XMLCodeGen::writeCurStateExpr()
+{
+ out << " <curstate>";
+ writeInlineList( pd->curStateExpr, 0 );
+ out << "</curstate>\n";
+}
+
+void XMLCodeGen::writeConditions()
+{
+ if ( condData->condSpaceMap.length() > 0 ) {
+ long nextCondSpaceId = 0;
+ for ( CondSpaceMap::Iter cs = condData->condSpaceMap; cs.lte(); cs++ )
+ cs->condSpaceId = nextCondSpaceId++;
+
+ out << " <cond_space_list length=\"" << condData->condSpaceMap.length() << "\">\n";
+ for ( CondSpaceMap::Iter cs = condData->condSpaceMap; cs.lte(); cs++ ) {
+ out << " <cond_space id=\"" << cs->condSpaceId <<
+ "\" length=\"" << cs->condSet.length() << "\">";
+ writeKey( cs->baseKey );
+ for ( CondSet::Iter csi = cs->condSet; csi.lte(); csi++ )
+ out << " " << (*csi)->actionId;
+ out << "</cond_space>\n";
+ }
+ out << " </cond_space_list>\n";
+ }
+}
+
+void XMLCodeGen::writeExports()
+{
+ if ( pd->exportList.length() > 0 ) {
+ out << " <exports>\n";
+ for ( ExportList::Iter exp = pd->exportList; exp.lte(); exp++ ) {
+ out << " <ex name=\"" << exp->name << "\">";
+ writeKey( exp->key );
+ out << "</ex>\n";
+ }
+ out << " </exports>\n";
+ }
+}
+
+void XMLCodeGen::writeXML()
+{
+ /* Open the definition. */
+ out << "<ragel_def name=\"" << fsmName << "\">\n";
+ writeAlphType();
+
+ if ( pd->getKeyExpr != 0 )
+ writeGetKeyExpr();
+
+ if ( pd->accessExpr != 0 )
+ writeAccessExpr();
+
+ if ( pd->curStateExpr != 0 )
+ writeCurStateExpr();
+
+ writeExports();
+
+ writeMachine();
+
+ out <<
+ "</ragel_def>\n";
+}
+
diff --git a/contrib/tools/ragel5/ragel/xmlcodegen.h b/contrib/tools/ragel5/ragel/xmlcodegen.h
new file mode 100644
index 0000000000..99b985395a
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/xmlcodegen.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2005, 2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XMLDOTGEN_H
+#define _XMLDOTGEN_H
+
+#include <iostream>
+#include "avltree.h"
+#include "fsmgraph.h"
+#include "parsedata.h"
+
+/* Forwards. */
+struct TransAp;
+struct FsmAp;
+struct ParseData;
+
+struct RedActionTable
+:
+ public AvlTreeEl<RedActionTable>
+{
+ RedActionTable( const ActionTable &key )
+ :
+ key(key),
+ id(0)
+ { }
+
+ const ActionTable &getKey()
+ { return key; }
+
+ ActionTable key;
+ int id;
+};
+
+typedef AvlTree<RedActionTable, ActionTable, CmpActionTable> ActionTableMap;
+
+struct NextRedTrans
+{
+ Key lowKey, highKey;
+ TransAp *trans;
+ TransAp *next;
+
+ void load() {
+ if ( trans != 0 ) {
+ next = trans->next;
+ lowKey = trans->lowKey;
+ highKey = trans->highKey;
+ }
+ }
+
+ NextRedTrans( TransAp *t ) {
+ trans = t;
+ load();
+ }
+
+ void increment() {
+ trans = next;
+ load();
+ }
+};
+
+class XMLCodeGen
+{
+public:
+ XMLCodeGen( char *fsmName, ParseData *pd, FsmAp *fsm, std::ostream &out );
+ void writeXML( );
+
+private:
+ void appendTrans( TransListVect &outList, Key lowKey, Key highKey, TransAp *trans );
+ void writeStateActions( StateAp *state );
+ void writeStateList();
+ void writeStateConditions( StateAp *state );
+
+ void writeKey( Key key );
+ void writeText( InlineItem *item );
+ void writeCtrlFlow( InlineItem *item, InlineItem *context );
+ void writePtrMod( InlineItem *item, InlineItem *context );
+ void writeGoto( InlineItem *item, InlineItem *context );
+ void writeGotoExpr( InlineItem *item, InlineItem *context );
+ void writeCall( InlineItem *item, InlineItem *context );
+ void writeCallExpr( InlineItem *item, InlineItem *context );
+ void writeNext( InlineItem *item, InlineItem *context );
+ void writeNextExpr( InlineItem *item, InlineItem *context );
+ void writeEntry( InlineItem *item );
+ void writeLmSetActId( InlineItem *item );
+ void writeLmOnLast( InlineItem *item );
+ void writeLmOnNext( InlineItem *item );
+ void writeLmOnLagBehind( InlineItem *item );
+
+ void writeExports();
+ bool writeNameInst( NameInst *nameInst );
+ void writeEntryPoints();
+ void writeGetKeyExpr();
+ void writeAccessExpr();
+ void writeCurStateExpr();
+ void writeConditions();
+ void writeInlineList( InlineList *inlineList, InlineItem *context );
+ void writeAlphType();
+ void writeActionList();
+ void writeActionTableList();
+ void reduceTrans( TransAp *trans );
+ void reduceActionTables();
+ void writeTransList( StateAp *state );
+ void writeTrans( Key lowKey, Key highKey, TransAp *defTrans );
+ void writeAction( Action *action );
+ void writeLmSwitch( InlineItem *item );
+ void writeMachine();
+ void writeActionExec( InlineItem *item );
+ void writeActionExecTE( InlineItem *item );
+
+ char *fsmName;
+ ParseData *pd;
+ FsmAp *fsm;
+ std::ostream &out;
+ ActionTableMap actionTableMap;
+ int nextActionTableId;
+};
+
+
+#endif /* _XMLDOTGEN_H */
diff --git a/contrib/tools/ragel5/ragel/ya.make b/contrib/tools/ragel5/ragel/ya.make
new file mode 100644
index 0000000000..6966321b7c
--- /dev/null
+++ b/contrib/tools/ragel5/ragel/ya.make
@@ -0,0 +1,26 @@
+PROGRAM(ragel5)
+
+NO_UTIL()
+NO_COMPILER_WARNINGS()
+
+PEERDIR(
+ contrib/tools/ragel5/aapl
+ contrib/tools/ragel5/common
+)
+
+SRCS(
+ fsmap.cpp
+ fsmattach.cpp
+ fsmbase.cpp
+ fsmgraph.cpp
+ fsmmin.cpp
+ fsmstate.cpp
+ main.cpp
+ parsedata.cpp
+ parsetree.cpp
+ rlparse.cpp
+ rlscan.cpp
+ xmlcodegen.cpp
+)
+
+END()
diff --git a/contrib/tools/ragel5/redfsm/gendata.cpp b/contrib/tools/ragel5/redfsm/gendata.cpp
new file mode 100644
index 0000000000..b0893ccdc2
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/gendata.cpp
@@ -0,0 +1,717 @@
+/*
+ * Copyright 2005-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "gendata.h"
+#include <iostream>
+
+using std::cerr;
+using std::endl;
+
+CodeGenData::CodeGenData( ostream &out )
+:
+ sourceFileName(0),
+ fsmName(0),
+ out(out),
+ redFsm(0),
+ allActions(0),
+ allActionTables(0),
+ allConditions(0),
+ allCondSpaces(0),
+ allStates(0),
+ nameIndex(0),
+ startState(-1),
+ errState(-1),
+ getKeyExpr(0),
+ accessExpr(0),
+ curStateExpr(0),
+ wantComplete(0),
+ hasLongestMatch(false),
+ codeGenErrCount(0),
+ hasEnd(true),
+ dataPrefix(true),
+ writeFirstFinal(true),
+ writeErr(true)
+{}
+
+
+void CodeGenData::createMachine()
+{
+ redFsm = new RedFsmAp();
+}
+
+void CodeGenData::initActionList( unsigned long length )
+{
+ allActions = new Action[length];
+ for ( unsigned long a = 0; a < length; a++ )
+ actionList.append( allActions+a );
+}
+
+void CodeGenData::newAction( int anum, char *name, int line,
+ int col, InlineList *inlineList )
+{
+ allActions[anum].actionId = anum;
+ allActions[anum].name = name;
+ allActions[anum].loc.line = line;
+ allActions[anum].loc.col = col;
+ allActions[anum].inlineList = inlineList;
+}
+
+void CodeGenData::initActionTableList( unsigned long length )
+{
+ allActionTables = new RedAction[length];
+}
+
+void CodeGenData::initStateList( unsigned long length )
+{
+ allStates = new RedStateAp[length];
+ for ( unsigned long s = 0; s < length; s++ )
+ redFsm->stateList.append( allStates+s );
+
+ /* We get the start state as an offset, set the pointer now. */
+ if ( startState >= 0 )
+ redFsm->startState = allStates + startState;
+ if ( errState >= 0 )
+ redFsm->errState = allStates + errState;
+ for ( EntryIdVect::Iter en = entryPointIds; en.lte(); en++ )
+ redFsm->entryPoints.insert( allStates + *en );
+
+ /* The nextStateId is no longer used to assign state ids (they come in set
+ * from the frontend now), however generation code still depends on it.
+ * Should eventually remove this variable. */
+ redFsm->nextStateId = redFsm->stateList.length();
+}
+
+void CodeGenData::setStartState( unsigned long startState )
+{
+ this->startState = startState;
+}
+
+void CodeGenData::setErrorState( unsigned long errState )
+{
+ this->errState = errState;
+}
+
+void CodeGenData::addEntryPoint( char *name, unsigned long entryState )
+{
+ entryPointIds.append( entryState );
+ entryPointNames.append( name );
+}
+
+void CodeGenData::initTransList( int snum, unsigned long length )
+{
+ /* Could preallocate the out range to save time growing it. For now do
+ * nothing. */
+}
+
+void CodeGenData::newTrans( int snum, int tnum, Key lowKey,
+ Key highKey, long targ, long action )
+{
+ /* Get the current state and range. */
+ RedStateAp *curState = allStates + snum;
+ RedTransList &destRange = curState->outRange;
+
+ if ( curState == redFsm->errState )
+ return;
+
+ /* Make the new transitions. */
+ RedStateAp *targState = targ >= 0 ? (allStates + targ) :
+ wantComplete ? redFsm->getErrorState() : 0;
+ RedAction *actionTable = action >= 0 ? (allActionTables + action) : 0;
+ RedTransAp *trans = redFsm->allocateTrans( targState, actionTable );
+ RedTransEl transEl( lowKey, highKey, trans );
+
+ if ( wantComplete ) {
+ /* If the machine is to be complete then we need to fill any gaps with
+ * the error transitions. */
+ if ( destRange.length() == 0 ) {
+ /* Range is currently empty. */
+ if ( keyOps->minKey < lowKey ) {
+ /* The first range doesn't start at the low end. */
+ Key fillHighKey = lowKey;
+ fillHighKey.decrement();
+
+ /* Create the filler with the state's error transition. */
+ RedTransEl newTel( keyOps->minKey, fillHighKey, redFsm->getErrorTrans() );
+ destRange.append( newTel );
+ }
+ }
+ else {
+ /* The range list is not empty, get the the last range. */
+ RedTransEl *last = &destRange[destRange.length()-1];
+ Key nextKey = last->highKey;
+ nextKey.increment();
+ if ( nextKey < lowKey ) {
+ /* There is a gap to fill. Make the high key. */
+ Key fillHighKey = lowKey;
+ fillHighKey.decrement();
+
+ /* Create the filler with the state's error transtion. */
+ RedTransEl newTel( nextKey, fillHighKey, redFsm->getErrorTrans() );
+ destRange.append( newTel );
+ }
+ }
+ }
+
+ /* Filler taken care of. Append the range. */
+ destRange.append( RedTransEl( lowKey, highKey, trans ) );
+}
+
+void CodeGenData::finishTransList( int snum )
+{
+ /* Get the current state and range. */
+ RedStateAp *curState = allStates + snum;
+ RedTransList &destRange = curState->outRange;
+
+ if ( curState == redFsm->errState )
+ return;
+
+ /* If building a complete machine we may need filler on the end. */
+ if ( wantComplete ) {
+ /* Check if there are any ranges already. */
+ if ( destRange.length() == 0 ) {
+ /* Fill with the whole alphabet. */
+ /* Add the range on the lower and upper bound. */
+ RedTransEl newTel( keyOps->minKey, keyOps->maxKey, redFsm->getErrorTrans() );
+ destRange.append( newTel );
+ }
+ else {
+ /* Get the last and check for a gap on the end. */
+ RedTransEl *last = &destRange[destRange.length()-1];
+ if ( last->highKey < keyOps->maxKey ) {
+ /* Make the high key. */
+ Key fillLowKey = last->highKey;
+ fillLowKey.increment();
+
+ /* Create the new range with the error trans and append it. */
+ RedTransEl newTel( fillLowKey, keyOps->maxKey, redFsm->getErrorTrans() );
+ destRange.append( newTel );
+ }
+ }
+ }
+}
+
+void CodeGenData::setId( int snum, int id )
+{
+ RedStateAp *curState = allStates + snum;
+ curState->id = id;
+}
+
+void CodeGenData::setFinal( int snum )
+{
+ RedStateAp *curState = allStates + snum;
+ curState->isFinal = true;
+}
+
+
+void CodeGenData::setStateActions( int snum, long toStateAction,
+ long fromStateAction, long eofAction )
+{
+ RedStateAp *curState = allStates + snum;
+ if ( toStateAction >= 0 )
+ curState->toStateAction = allActionTables + toStateAction;
+ if ( fromStateAction >= 0 )
+ curState->fromStateAction = allActionTables + fromStateAction;
+ if ( eofAction >= 0 )
+ curState->eofAction = allActionTables + eofAction;
+}
+
+void CodeGenData::resolveTargetStates( InlineList *inlineList )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case InlineItem::Goto: case InlineItem::Call:
+ case InlineItem::Next: case InlineItem::Entry:
+ item->targState = allStates + item->targId;
+ break;
+ default:
+ break;
+ }
+
+ if ( item->children != 0 )
+ resolveTargetStates( item->children );
+ }
+}
+
+void CodeGenData::closeMachine()
+{
+ for ( ActionList::Iter a = actionList; a.lte(); a++ )
+ resolveTargetStates( a->inlineList );
+
+ /* Note that even if we want a complete graph we do not give the error
+ * state a default transition. All machines break out of the processing
+ * loop when in the error state. */
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ for ( StateCondList::Iter sci = st->stateCondList; sci.lte(); sci++ )
+ st->stateCondVect.append( sci );
+ }
+}
+
+
+bool CodeGenData::setAlphType( char *data )
+{
+ /* FIXME: This should validate the alphabet type selection. */
+ HostType *alphType = hostLang->hostTypes + atoi(data);
+ thisKeyOps.setAlphType( alphType );
+ return true;
+}
+
+void CodeGenData::initCondSpaceList( ulong length )
+{
+ allCondSpaces = new CondSpace[length];
+ for ( ulong c = 0; c < length; c++ )
+ condSpaceList.append( allCondSpaces + c );
+}
+
+void CodeGenData::newCondSpace( int cnum, int condSpaceId, Key baseKey )
+{
+ CondSpace *cond = allCondSpaces + cnum;
+ cond->condSpaceId = condSpaceId;
+ cond->baseKey = baseKey;
+}
+
+void CodeGenData::condSpaceItem( int cnum, long condActionId )
+{
+ CondSpace *cond = allCondSpaces + cnum;
+ cond->condSet.append( allActions + condActionId );
+}
+
+void CodeGenData::initStateCondList( int snum, ulong length )
+{
+ /* Could preallocate these, as we could with transitions. */
+}
+
+void CodeGenData::addStateCond( int snum, Key lowKey, Key highKey, long condNum )
+{
+ RedStateAp *curState = allStates + snum;
+
+ /* Create the new state condition. */
+ StateCond *stateCond = new StateCond;
+ stateCond->lowKey = lowKey;
+ stateCond->highKey = highKey;
+
+ /* Assign it a cond space. */
+ CondSpace *condSpace = allCondSpaces + condNum;
+ stateCond->condSpace = condSpace;
+
+ curState->stateCondList.append( stateCond );
+}
+
+
+CondSpace *CodeGenData::findCondSpace( Key lowKey, Key highKey )
+{
+ for ( CondSpaceList::Iter cs = condSpaceList; cs.lte(); cs++ ) {
+ Key csHighKey = cs->baseKey;
+ csHighKey += keyOps->alphSize() * (1 << cs->condSet.length());
+
+ if ( lowKey >= cs->baseKey && highKey <= csHighKey )
+ return cs;
+ }
+ return 0;
+}
+
+Condition *CodeGenData::findCondition( Key key )
+{
+ for ( ConditionList::Iter cond = conditionList; cond.lte(); cond++ ) {
+ Key upperKey = cond->baseKey + (1 << cond->condSet.length());
+ if ( cond->baseKey <= key && key <= upperKey )
+ return cond;
+ }
+ return 0;
+}
+
+Key CodeGenData::findMaxKey()
+{
+ Key maxKey = keyOps->maxKey;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ assert( st->outSingle.length() == 0 );
+ assert( st->defTrans == 0 );
+
+ long rangeLen = st->outRange.length();
+ if ( rangeLen > 0 ) {
+ Key highKey = st->outRange[rangeLen-1].highKey;
+ if ( highKey > maxKey )
+ maxKey = highKey;
+ }
+ }
+ return maxKey;
+}
+
+void CodeGenData::findFinalActionRefs()
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Rerence count out of single transitions. */
+ for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) {
+ if ( rtel->value->action != 0 ) {
+ rtel->value->action->numTransRefs += 1;
+ for ( ActionTable::Iter item = rtel->value->action->key; item.lte(); item++ )
+ item->value->numTransRefs += 1;
+ }
+ }
+
+ /* Reference count out of range transitions. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ if ( rtel->value->action != 0 ) {
+ rtel->value->action->numTransRefs += 1;
+ for ( ActionTable::Iter item = rtel->value->action->key; item.lte(); item++ )
+ item->value->numTransRefs += 1;
+ }
+ }
+
+ /* Reference count default transition. */
+ if ( st->defTrans != 0 && st->defTrans->action != 0 ) {
+ st->defTrans->action->numTransRefs += 1;
+ for ( ActionTable::Iter item = st->defTrans->action->key; item.lte(); item++ )
+ item->value->numTransRefs += 1;
+ }
+
+ /* Reference count to state actions. */
+ if ( st->toStateAction != 0 ) {
+ st->toStateAction->numToStateRefs += 1;
+ for ( ActionTable::Iter item = st->toStateAction->key; item.lte(); item++ )
+ item->value->numToStateRefs += 1;
+ }
+
+ /* Reference count from state actions. */
+ if ( st->fromStateAction != 0 ) {
+ st->fromStateAction->numFromStateRefs += 1;
+ for ( ActionTable::Iter item = st->fromStateAction->key; item.lte(); item++ )
+ item->value->numFromStateRefs += 1;
+ }
+
+ /* Reference count EOF actions. */
+ if ( st->eofAction != 0 ) {
+ st->eofAction->numEofRefs += 1;
+ for ( ActionTable::Iter item = st->eofAction->key; item.lte(); item++ )
+ item->value->numEofRefs += 1;
+ }
+ }
+}
+
+void CodeGenData::analyzeAction( Action *act, InlineList *inlineList )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ /* Only consider actions that are referenced. */
+ if ( act->numRefs() > 0 ) {
+ if ( item->type == InlineItem::Goto || item->type == InlineItem::GotoExpr )
+ redFsm->bAnyActionGotos = true;
+ else if ( item->type == InlineItem::Call || item->type == InlineItem::CallExpr )
+ redFsm->bAnyActionCalls = true;
+ else if ( item->type == InlineItem::Ret )
+ redFsm->bAnyActionRets = true;
+ }
+
+ /* Check for various things in regular actions. */
+ if ( act->numTransRefs > 0 || act->numToStateRefs > 0 || act->numFromStateRefs > 0 ) {
+ /* Any returns in regular actions? */
+ if ( item->type == InlineItem::Ret )
+ redFsm->bAnyRegActionRets = true;
+
+ /* Any next statements in the regular actions? */
+ if ( item->type == InlineItem::Next || item->type == InlineItem::NextExpr )
+ redFsm->bAnyRegNextStmt = true;
+
+ /* Any by value control in regular actions? */
+ if ( item->type == InlineItem::CallExpr || item->type == InlineItem::GotoExpr )
+ redFsm->bAnyRegActionByValControl = true;
+
+ /* Any references to the current state in regular actions? */
+ if ( item->type == InlineItem::Curs )
+ redFsm->bAnyRegCurStateRef = true;
+
+ if ( item->type == InlineItem::Break )
+ redFsm->bAnyRegBreak = true;
+
+ if ( item->type == InlineItem::LmSwitch && item->handlesError )
+ redFsm->bAnyLmSwitchError = true;
+ }
+
+ if ( item->children != 0 )
+ analyzeAction( act, item->children );
+ }
+}
+
+void CodeGenData::analyzeActionList( RedAction *redAct, InlineList *inlineList )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ /* Any next statements in the action table? */
+ if ( item->type == InlineItem::Next || item->type == InlineItem::NextExpr )
+ redAct->bAnyNextStmt = true;
+
+ /* Any references to the current state. */
+ if ( item->type == InlineItem::Curs )
+ redAct->bAnyCurStateRef = true;
+
+ if ( item->type == InlineItem::Break )
+ redAct->bAnyBreakStmt = true;
+
+ if ( item->children != 0 )
+ analyzeActionList( redAct, item->children );
+ }
+}
+
+/* Assign ids to referenced actions. */
+void CodeGenData::assignActionIds()
+{
+ int nextActionId = 0;
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Only ever interested in referenced actions. */
+ if ( act->numRefs() > 0 )
+ act->actionId = nextActionId++;
+ }
+}
+
+void CodeGenData::setValueLimits()
+{
+ redFsm->maxSingleLen = 0;
+ redFsm->maxRangeLen = 0;
+ redFsm->maxKeyOffset = 0;
+ redFsm->maxIndexOffset = 0;
+ redFsm->maxActListId = 0;
+ redFsm->maxActionLoc = 0;
+ redFsm->maxActArrItem = 0;
+ redFsm->maxSpan = 0;
+ redFsm->maxCondSpan = 0;
+ redFsm->maxFlatIndexOffset = 0;
+ redFsm->maxCondOffset = 0;
+ redFsm->maxCondLen = 0;
+ redFsm->maxCondSpaceId = 0;
+ redFsm->maxCondIndexOffset = 0;
+
+ /* In both of these cases the 0 index is reserved for no value, so the max
+ * is one more than it would be if they started at 0. */
+ redFsm->maxIndex = redFsm->transSet.length();
+ redFsm->maxCond = condSpaceList.length();
+
+ /* The nextStateId - 1 is the last state id assigned. */
+ redFsm->maxState = redFsm->nextStateId - 1;
+
+ for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) {
+ if ( csi->condSpaceId > redFsm->maxCondSpaceId )
+ redFsm->maxCondSpaceId = csi->condSpaceId;
+ }
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Maximum cond length. */
+ if ( st->stateCondList.length() > redFsm->maxCondLen )
+ redFsm->maxCondLen = st->stateCondList.length();
+
+ /* Maximum single length. */
+ if ( st->outSingle.length() > redFsm->maxSingleLen )
+ redFsm->maxSingleLen = st->outSingle.length();
+
+ /* Maximum range length. */
+ if ( st->outRange.length() > redFsm->maxRangeLen )
+ redFsm->maxRangeLen = st->outRange.length();
+
+ /* The key offset index offset for the state after last is not used, skip it.. */
+ if ( ! st.last() ) {
+ redFsm->maxCondOffset += st->stateCondList.length();
+ redFsm->maxKeyOffset += st->outSingle.length() + st->outRange.length()*2;
+ redFsm->maxIndexOffset += st->outSingle.length() + st->outRange.length() + 1;
+ }
+
+ /* Max cond span. */
+ if ( st->condList != 0 ) {
+ unsigned long long span = keyOps->span( st->condLowKey, st->condHighKey );
+ if ( span > redFsm->maxCondSpan )
+ redFsm->maxCondSpan = span;
+ }
+
+ /* Max key span. */
+ if ( st->transList != 0 ) {
+ unsigned long long span = keyOps->span( st->lowKey, st->highKey );
+ if ( span > redFsm->maxSpan )
+ redFsm->maxSpan = span;
+ }
+
+ /* Max cond index offset. */
+ if ( ! st.last() ) {
+ if ( st->condList != 0 )
+ redFsm->maxCondIndexOffset += keyOps->span( st->condLowKey, st->condHighKey );
+ }
+
+ /* Max flat index offset. */
+ if ( ! st.last() ) {
+ if ( st->transList != 0 )
+ redFsm->maxFlatIndexOffset += keyOps->span( st->lowKey, st->highKey );
+ redFsm->maxFlatIndexOffset += 1;
+ }
+ }
+
+ for ( ActionTableMap::Iter at = redFsm->actionMap; at.lte(); at++ ) {
+ /* Maximum id of action lists. */
+ if ( at->actListId+1 > redFsm->maxActListId )
+ redFsm->maxActListId = at->actListId+1;
+
+ /* Maximum location of items in action array. */
+ if ( at->location+1 > redFsm->maxActionLoc )
+ redFsm->maxActionLoc = at->location+1;
+
+ /* Maximum values going into the action array. */
+ if ( at->key.length() > redFsm->maxActArrItem )
+ redFsm->maxActArrItem = at->key.length();
+ for ( ActionTable::Iter item = at->key; item.lte(); item++ ) {
+ if ( item->value->actionId > redFsm->maxActArrItem )
+ redFsm->maxActArrItem = item->value->actionId;
+ }
+ }
+}
+
+
+
+/* Gather various info on the machine. */
+void CodeGenData::analyzeMachine()
+{
+ /* Find the true count of action references. */
+ findFinalActionRefs();
+
+ /* Check if there are any calls in action code. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Record the occurrence of various kinds of actions. */
+ if ( act->numToStateRefs > 0 )
+ redFsm->bAnyToStateActions = true;
+ if ( act->numFromStateRefs > 0 )
+ redFsm->bAnyFromStateActions = true;
+ if ( act->numEofRefs > 0 )
+ redFsm->bAnyEofActions = true;
+ if ( act->numTransRefs > 0 )
+ redFsm->bAnyRegActions = true;
+
+ /* Recurse through the action's parse tree looking for various things. */
+ analyzeAction( act, act->inlineList );
+ }
+
+ /* Analyze reduced action lists. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ for ( ActionTable::Iter act = redAct->key; act.lte(); act++ )
+ analyzeActionList( redAct, act->value->inlineList );
+ }
+
+ /* Find states that have transitions with actions that have next
+ * statements. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Check any actions out of outSinge. */
+ for ( RedTransList::Iter rtel = st->outSingle; rtel.lte(); rtel++ ) {
+ if ( rtel->value->action != 0 && rtel->value->action->anyCurStateRef() )
+ st->bAnyRegCurStateRef = true;
+ }
+
+ /* Check any actions out of outRange. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ if ( rtel->value->action != 0 && rtel->value->action->anyCurStateRef() )
+ st->bAnyRegCurStateRef = true;
+ }
+
+ /* Check any action out of default. */
+ if ( st->defTrans != 0 && st->defTrans->action != 0 &&
+ st->defTrans->action->anyCurStateRef() )
+ st->bAnyRegCurStateRef = true;
+
+ if ( st->stateCondList.length() > 0 )
+ redFsm->bAnyConditions = true;
+ }
+
+ /* Assign ids to actions that are referenced. */
+ assignActionIds();
+
+ /* Set the maximums of various values used for deciding types. */
+ setValueLimits();
+}
+
+void CodeGenData::writeStatement( InputLoc &loc, int nargs, char **args )
+{
+ /* FIXME: This should be moved to the virtual functions in the code
+ * generators.
+ *
+ * Force a newline. */
+ out << "\n";
+ genLineDirective( out );
+
+ if ( strcmp( args[0], "data" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ if ( strcmp( args[i], "noerror" ) == 0 )
+ writeErr = false;
+ else if ( strcmp( args[i], "noprefix" ) == 0 )
+ dataPrefix = false;
+ else if ( strcmp( args[i], "nofinal" ) == 0 )
+ writeFirstFinal = false;
+ else {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ }
+ writeData();
+ }
+ else if ( strcmp( args[0], "init" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ writeInit();
+ }
+ else if ( strcmp( args[0], "exec" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ if ( strcmp( args[i], "noend" ) == 0 )
+ hasEnd = false;
+ else {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ }
+ writeExec();
+ }
+ else if ( strcmp( args[0], "eof" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ writeEOF();
+ }
+ else if ( strcmp( args[0], "exports" ) == 0 ) {
+ for ( int i = 1; i < nargs; i++ ) {
+ source_warning(loc) << "unrecognized write option \"" <<
+ args[i] << "\"" << endl;
+ }
+ writeExports();
+ }
+ else {
+ /* EMIT An error here. */
+ source_error(loc) << "unrecognized write command \"" <<
+ args[0] << "\"" << endl;
+ }
+}
+
+ostream &CodeGenData::source_warning( const InputLoc &loc )
+{
+ cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": warning: ";
+ return cerr;
+}
+
+ostream &CodeGenData::source_error( const InputLoc &loc )
+{
+ codeGenErrCount += 1;
+ assert( sourceFileName != 0 );
+ cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": ";
+ return cerr;
+}
+
+
diff --git a/contrib/tools/ragel5/redfsm/gendata.h b/contrib/tools/ragel5/redfsm/gendata.h
new file mode 100644
index 0000000000..855e0710a7
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/gendata.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2005-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _GENDATA_H
+#define _GENDATA_H
+
+#include <iostream>
+#include "redfsm.h"
+#include "common.h"
+
+using std::ostream;
+
+struct NameInst;
+typedef DList<Action> ActionList;
+
+typedef unsigned long ulong;
+
+struct FsmCodeGen;
+struct CodeGenData;
+
+typedef AvlMap<char *, CodeGenData*, CmpStr> CodeGenMap;
+typedef AvlMapEl<char *, CodeGenData*> CodeGenMapEl;
+
+/*
+ * The interface to the parser
+ */
+
+/* These functions must be implemented by the code generation executable.
+ * The openOutput function is invoked when the root element is opened. The
+ * makeCodeGen function is invoked when a ragel_def element is opened. */
+std::ostream *openOutput( char *inputFile );
+CodeGenData *makeCodeGen( char *sourceFileName,
+ char *fsmName, ostream &out, bool wantComplete );
+
+void lineDirective( ostream &out, char *fileName, int line );
+void genLineDirective( ostream &out );
+
+/*********************************/
+
+struct CodeGenData
+{
+ /*
+ * The interface to the code generator.
+ */
+ virtual void finishRagelDef() {}
+
+ /* These are invoked by the corresponding write statements. */
+ virtual void writeData() {};
+ virtual void writeInit() {};
+ virtual void writeExec() {};
+ virtual void writeEOF() {};
+ virtual void writeExports() {};
+
+ /* This can also be overwridden to modify the processing of write
+ * statements. */
+ virtual void writeStatement( InputLoc &loc, int nargs, char **args );
+
+ /********************/
+
+ CodeGenData( ostream &out );
+ virtual ~CodeGenData() {}
+
+ /*
+ * Collecting the machine.
+ */
+
+ char *sourceFileName;
+ char *fsmName;
+ ostream &out;
+ RedFsmAp *redFsm;
+ Action *allActions;
+ RedAction *allActionTables;
+ Condition *allConditions;
+ CondSpace *allCondSpaces;
+ RedStateAp *allStates;
+ NameInst **nameIndex;
+ int startState;
+ int errState;
+ ActionList actionList;
+ ConditionList conditionList;
+ CondSpaceList condSpaceList;
+ InlineList *getKeyExpr;
+ InlineList *accessExpr;
+ InlineList *curStateExpr;
+ KeyOps thisKeyOps;
+ bool wantComplete;
+ EntryIdVect entryPointIds;
+ EntryNameVect entryPointNames;
+ bool hasLongestMatch;
+ int codeGenErrCount;
+ ExportList exportList;
+
+ /* Write options. */
+ bool hasEnd;
+ bool dataPrefix;
+ bool writeFirstFinal;
+ bool writeErr;
+
+ void createMachine();
+ void initActionList( unsigned long length );
+ void newAction( int anum, char *name, int line, int col, InlineList *inlineList );
+ void initActionTableList( unsigned long length );
+ void initStateList( unsigned long length );
+ void setStartState( unsigned long startState );
+ void setErrorState( unsigned long errState );
+ void addEntryPoint( char *name, unsigned long entryState );
+ void setId( int snum, int id );
+ void setFinal( int snum );
+ void initTransList( int snum, unsigned long length );
+ void newTrans( int snum, int tnum, Key lowKey, Key highKey,
+ long targ, long act );
+ void finishTransList( int snum );
+ void setStateActions( int snum, long toStateAction,
+ long fromStateAction, long eofAction );
+ void setForcedErrorState()
+ { redFsm->forcedErrorState = true; }
+
+
+ void initCondSpaceList( ulong length );
+ void condSpaceItem( int cnum, long condActionId );
+ void newCondSpace( int cnum, int condSpaceId, Key baseKey );
+
+ void initStateCondList( int snum, ulong length );
+ void addStateCond( int snum, Key lowKey, Key highKey, long condNum );
+
+ CondSpace *findCondSpace( Key lowKey, Key highKey );
+ Condition *findCondition( Key key );
+
+ bool setAlphType( char *data );
+
+ void resolveTargetStates( InlineList *inlineList );
+ Key findMaxKey();
+
+ /* Gather various info on the machine. */
+ void analyzeActionList( RedAction *redAct, InlineList *inlineList );
+ void analyzeAction( Action *act, InlineList *inlineList );
+ void findFinalActionRefs();
+ void analyzeMachine();
+
+ void closeMachine();
+ void setValueLimits();
+ void assignActionIds();
+
+ ostream &source_warning( const InputLoc &loc );
+ ostream &source_error( const InputLoc &loc );
+};
+
+
+#endif /* _GENDATA_H */
diff --git a/contrib/tools/ragel5/redfsm/phash.h b/contrib/tools/ragel5/redfsm/phash.h
new file mode 100644
index 0000000000..11ce7502a6
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/phash.h
@@ -0,0 +1,10 @@
+#pragma once
+
+class Perfect_Hash
+{
+private:
+ static inline unsigned int hash (const char *str, unsigned int len);
+
+public:
+ static struct XMLTagHashPair *in_word_set (const char *str, unsigned int len);
+};
diff --git a/contrib/tools/ragel5/redfsm/redfsm.cpp b/contrib/tools/ragel5/redfsm/redfsm.cpp
new file mode 100644
index 0000000000..6a55b22ec7
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/redfsm.cpp
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "redfsm.h"
+#include "avlmap.h"
+#include <iostream>
+#include <sstream>
+
+using std::ostringstream;
+
+KeyOps *keyOps = 0;
+
+string Action::nameOrLoc()
+{
+ if ( name != 0 )
+ return string(name);
+ else {
+ ostringstream ret;
+ ret << loc.line << ":" << loc.col;
+ return ret.str();
+ }
+}
+
+RedFsmAp::RedFsmAp()
+:
+ wantComplete(false),
+ forcedErrorState(false),
+ nextActionId(0),
+ nextTransId(0),
+ startState(0),
+ errState(0),
+ errTrans(0),
+ firstFinState(0),
+ numFinStates(0),
+ bAnyToStateActions(false),
+ bAnyFromStateActions(false),
+ bAnyRegActions(false),
+ bAnyEofActions(false),
+ bAnyActionGotos(false),
+ bAnyActionCalls(false),
+ bAnyActionRets(false),
+ bAnyRegActionRets(false),
+ bAnyRegActionByValControl(false),
+ bAnyRegNextStmt(false),
+ bAnyRegCurStateRef(false),
+ bAnyRegBreak(false),
+ bAnyLmSwitchError(false),
+ bAnyConditions(false)
+{
+}
+
+/* Does the machine have any actions. */
+bool RedFsmAp::anyActions()
+{
+ return actionMap.length() > 0;
+}
+
+void RedFsmAp::depthFirstOrdering( RedStateAp *state )
+{
+ /* Nothing to do if the state is already on the list. */
+ if ( state->onStateList )
+ return;
+
+ /* Doing depth first, put state on the list. */
+ state->onStateList = true;
+ stateList.append( state );
+
+ /* At this point transitions should only be in ranges. */
+ assert( state->outSingle.length() == 0 );
+ assert( state->defTrans == 0 );
+
+ /* Recurse on everything ranges. */
+ for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
+ if ( rtel->value->targ != 0 )
+ depthFirstOrdering( rtel->value->targ );
+ }
+}
+
+/* Ordering states by transition connections. */
+void RedFsmAp::depthFirstOrdering()
+{
+ /* Init on state list flags. */
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ )
+ st->onStateList = false;
+
+ /* Clear out the state list, we will rebuild it. */
+ int stateListLen = stateList.length();
+ stateList.abandon();
+
+ /* Add back to the state list from the start state and all other entry
+ * points. */
+ if ( startState != 0 )
+ depthFirstOrdering( startState );
+ for ( RedStateSet::Iter en = entryPoints; en.lte(); en++ )
+ depthFirstOrdering( *en );
+ if ( forcedErrorState )
+ depthFirstOrdering( errState );
+
+ /* Make sure we put everything back on. */
+ assert( stateListLen == stateList.length() );
+}
+
+/* Assign state ids by appearance in the state list. */
+void RedFsmAp::sequentialStateIds()
+{
+ /* Table based machines depend on the state numbers starting at zero. */
+ nextStateId = 0;
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ )
+ st->id = nextStateId++;
+}
+
+/* Stable sort the states by final state status. */
+void RedFsmAp::sortStatesByFinal()
+{
+ /* Move forward through the list and throw final states onto the end. */
+ RedStateAp *state = 0;
+ RedStateAp *next = stateList.head;
+ RedStateAp *last = stateList.tail;
+ while ( state != last ) {
+ /* Move forward and load up the next. */
+ state = next;
+ next = state->next;
+
+ /* Throw to the end? */
+ if ( state->isFinal ) {
+ stateList.detach( state );
+ stateList.append( state );
+ }
+ }
+}
+
+/* Assign state ids by final state state status. */
+void RedFsmAp::sortStateIdsByFinal()
+{
+ /* Table based machines depend on this starting at zero. */
+ nextStateId = 0;
+
+ /* First pass to assign non final ids. */
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ if ( ! st->isFinal )
+ st->id = nextStateId++;
+ }
+
+ /* Second pass to assign final ids. */
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ if ( st->isFinal )
+ st->id = nextStateId++;
+ }
+}
+
+void RedFsmAp::sortByStateId()
+{
+ /* FIXME: Implement. */
+}
+
+/* Find the final state with the lowest id. */
+void RedFsmAp::findFirstFinState()
+{
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ if ( st->isFinal && (firstFinState == 0 || st->id < firstFinState->id) )
+ firstFinState = st;
+ }
+}
+
+void RedFsmAp::assignActionLocs()
+{
+ int nextLocation = 0;
+ for ( ActionTableMap::Iter act = actionMap; act.lte(); act++ ) {
+ /* Store the loc, skip over the array and a null terminator. */
+ act->location = nextLocation;
+ nextLocation += act->key.length() + 1;
+ }
+}
+
+/* Check if we can extend the current range by displacing any ranges
+ * ahead to the singles. */
+bool RedFsmAp::canExtend( const RedTransList &list, int pos )
+{
+ /* Get the transition that we want to extend. */
+ RedTransAp *extendTrans = list[pos].value;
+
+ /* Look ahead in the transition list. */
+ for ( int next = pos + 1; next < list.length(); pos++, next++ ) {
+ /* If they are not continuous then cannot extend. */
+ Key nextKey = list[next].lowKey;
+ nextKey.decrement();
+ if ( list[pos].highKey != nextKey )
+ break;
+
+ /* Check for the extenstion property. */
+ if ( extendTrans == list[next].value )
+ return true;
+
+ /* If the span of the next element is more than one, then don't keep
+ * checking, it won't be moved to single. */
+ unsigned long long nextSpan = keyOps->span( list[next].lowKey, list[next].highKey );
+ if ( nextSpan > 1 )
+ break;
+ }
+ return false;
+}
+
+/* Move ranges to the singles list. */
+void RedFsmAp::moveTransToSingle( RedStateAp *state )
+{
+ RedTransList &range = state->outRange;
+ RedTransList &single = state->outSingle;
+ for ( int rpos = 0; rpos < range.length(); ) {
+ /* Check if this is a range we can extend. */
+ if ( canExtend( range, rpos ) ) {
+ /* Transfer singles over. */
+ while ( range[rpos].value != range[rpos+1].value ) {
+ /* Transfer the range to single. */
+ single.append( range[rpos+1] );
+ range.remove( rpos+1 );
+ }
+
+ /* Extend. */
+ range[rpos].highKey = range[rpos+1].highKey;
+ range.remove( rpos+1 );
+ }
+ /* Maybe move it to the singles. */
+ else if ( keyOps->span( range[rpos].lowKey, range[rpos].highKey ) == 1 ) {
+ single.append( range[rpos] );
+ range.remove( rpos );
+ }
+ else {
+ /* Keeping it in the ranges. */
+ rpos += 1;
+ }
+ }
+}
+
+/* Look through ranges and choose suitable single character transitions. */
+void RedFsmAp::chooseSingle()
+{
+ /* Loop the states. */
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ /* Rewrite the transition list taking out the suitable single
+ * transtions. */
+ moveTransToSingle( st );
+ }
+}
+
+void RedFsmAp::makeFlat()
+{
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ if ( st->stateCondList.length() == 0 ) {
+ st->condLowKey = 0;
+ st->condHighKey = 0;
+ }
+ else {
+ st->condLowKey = st->stateCondList.head->lowKey;
+ st->condHighKey = st->stateCondList.tail->highKey;
+
+ unsigned long long span = keyOps->span( st->condLowKey, st->condHighKey );
+ st->condList = new CondSpace*[ span ];
+ memset( st->condList, 0, sizeof(CondSpace*)*span );
+
+ for ( StateCondList::Iter sci = st->stateCondList; sci.lte(); sci++ ) {
+ unsigned long long base, trSpan;
+ base = keyOps->span( st->condLowKey, sci->lowKey )-1;
+ trSpan = keyOps->span( sci->lowKey, sci->highKey );
+ for ( unsigned long long pos = 0; pos < trSpan; pos++ )
+ st->condList[base+pos] = sci->condSpace;
+ }
+ }
+
+ if ( st->outRange.length() == 0 ) {
+ st->lowKey = st->highKey = 0;
+ st->transList = 0;
+ }
+ else {
+ st->lowKey = st->outRange[0].lowKey;
+ st->highKey = st->outRange[st->outRange.length()-1].highKey;
+ unsigned long long span = keyOps->span( st->lowKey, st->highKey );
+ st->transList = new RedTransAp*[ span ];
+ memset( st->transList, 0, sizeof(RedTransAp*)*span );
+
+ for ( RedTransList::Iter trans = st->outRange; trans.lte(); trans++ ) {
+ unsigned long long base, trSpan;
+ base = keyOps->span( st->lowKey, trans->lowKey )-1;
+ trSpan = keyOps->span( trans->lowKey, trans->highKey );
+ for ( unsigned long long pos = 0; pos < trSpan; pos++ )
+ st->transList[base+pos] = trans->value;
+ }
+
+ /* Fill in the gaps with the default transition. */
+ for ( unsigned long long pos = 0; pos < span; pos++ ) {
+ if ( st->transList[pos] == 0 )
+ st->transList[pos] = st->defTrans;
+ }
+ }
+ }
+}
+
+
+/* A default transition has been picked, move it from the outRange to the
+ * default pointer. */
+void RedFsmAp::moveToDefault( RedTransAp *defTrans, RedStateAp *state )
+{
+ /* Rewrite the outRange, omitting any ranges that use
+ * the picked default. */
+ RedTransList outRange;
+ for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
+ /* If it does not take the default, copy it over. */
+ if ( rtel->value != defTrans )
+ outRange.append( *rtel );
+ }
+
+ /* Save off the range we just created into the state's range. */
+ state->outRange.transfer( outRange );
+
+ /* Store the default. */
+ state->defTrans = defTrans;
+}
+
+bool RedFsmAp::alphabetCovered( RedTransList &outRange )
+{
+ /* Cannot cover without any out ranges. */
+ if ( outRange.length() == 0 )
+ return false;
+
+ /* If the first range doesn't start at the the lower bound then the
+ * alphabet is not covered. */
+ RedTransList::Iter rtel = outRange;
+ if ( keyOps->minKey < rtel->lowKey )
+ return false;
+
+ /* Check that every range is next to the previous one. */
+ rtel.increment();
+ for ( ; rtel.lte(); rtel++ ) {
+ Key highKey = rtel[-1].highKey;
+ highKey.increment();
+ if ( highKey != rtel->lowKey )
+ return false;
+ }
+
+ /* The last must extend to the upper bound. */
+ RedTransEl *last = &outRange[outRange.length()-1];
+ if ( last->highKey < keyOps->maxKey )
+ return false;
+
+ return true;
+}
+
+RedTransAp *RedFsmAp::chooseDefaultSpan( RedStateAp *state )
+{
+ /* Make a set of transitions from the outRange. */
+ RedTransSet stateTransSet;
+ for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ )
+ stateTransSet.insert( rtel->value );
+
+ /* For each transition in the find how many alphabet characters the
+ * transition spans. */
+ unsigned long long *span = new unsigned long long[stateTransSet.length()];
+ memset( span, 0, sizeof(unsigned long long) * stateTransSet.length() );
+ for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
+ /* Lookup the transition in the set. */
+ RedTransAp **inSet = stateTransSet.find( rtel->value );
+ int pos = inSet - stateTransSet.data;
+ span[pos] += keyOps->span( rtel->lowKey, rtel->highKey );
+ }
+
+ /* Find the max span, choose it for making the default. */
+ RedTransAp *maxTrans = 0;
+ unsigned long long maxSpan = 0;
+ for ( RedTransSet::Iter rtel = stateTransSet; rtel.lte(); rtel++ ) {
+ if ( span[rtel.pos()] > maxSpan ) {
+ maxSpan = span[rtel.pos()];
+ maxTrans = *rtel;
+ }
+ }
+
+ delete[] span;
+ return maxTrans;
+}
+
+/* Pick default transitions from ranges for the states. */
+void RedFsmAp::chooseDefaultSpan()
+{
+ /* Loop the states. */
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ /* Only pick a default transition if the alphabet is covered. This
+ * avoids any transitions in the out range that go to error and avoids
+ * the need for an ERR state. */
+ if ( alphabetCovered( st->outRange ) ) {
+ /* Pick a default transition by largest span. */
+ RedTransAp *defTrans = chooseDefaultSpan( st );
+
+ /* Rewrite the transition list taking out the transition we picked
+ * as the default and store the default. */
+ moveToDefault( defTrans, st );
+ }
+ }
+}
+
+RedTransAp *RedFsmAp::chooseDefaultGoto( RedStateAp *state )
+{
+ /* Make a set of transitions from the outRange. */
+ RedTransSet stateTransSet;
+ for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
+ if ( rtel->value->targ == state->next )
+ return rtel->value;
+ }
+ return 0;
+}
+
+void RedFsmAp::chooseDefaultGoto()
+{
+ /* Loop the states. */
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ /* Pick a default transition. */
+ RedTransAp *defTrans = chooseDefaultGoto( st );
+ if ( defTrans == 0 )
+ defTrans = chooseDefaultSpan( st );
+
+ /* Rewrite the transition list taking out the transition we picked
+ * as the default and store the default. */
+ moveToDefault( defTrans, st );
+ }
+}
+
+RedTransAp *RedFsmAp::chooseDefaultNumRanges( RedStateAp *state )
+{
+ /* Make a set of transitions from the outRange. */
+ RedTransSet stateTransSet;
+ for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ )
+ stateTransSet.insert( rtel->value );
+
+ /* For each transition in the find how many ranges use the transition. */
+ int *numRanges = new int[stateTransSet.length()];
+ memset( numRanges, 0, sizeof(int) * stateTransSet.length() );
+ for ( RedTransList::Iter rtel = state->outRange; rtel.lte(); rtel++ ) {
+ /* Lookup the transition in the set. */
+ RedTransAp **inSet = stateTransSet.find( rtel->value );
+ numRanges[inSet - stateTransSet.data] += 1;
+ }
+
+ /* Find the max number of ranges. */
+ RedTransAp *maxTrans = 0;
+ int maxNumRanges = 0;
+ for ( RedTransSet::Iter rtel = stateTransSet; rtel.lte(); rtel++ ) {
+ if ( numRanges[rtel.pos()] > maxNumRanges ) {
+ maxNumRanges = numRanges[rtel.pos()];
+ maxTrans = *rtel;
+ }
+ }
+
+ delete[] numRanges;
+ return maxTrans;
+}
+
+void RedFsmAp::chooseDefaultNumRanges()
+{
+ /* Loop the states. */
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ /* Pick a default transition. */
+ RedTransAp *defTrans = chooseDefaultNumRanges( st );
+
+ /* Rewrite the transition list taking out the transition we picked
+ * as the default and store the default. */
+ moveToDefault( defTrans, st );
+ }
+}
+
+RedTransAp *RedFsmAp::getErrorTrans( )
+{
+ /* If the error trans has not been made aready, make it. */
+ if ( errTrans == 0 ) {
+ /* This insert should always succeed since no transition created by
+ * the user can point to the error state. */
+ errTrans = new RedTransAp( getErrorState(), 0, nextTransId++ );
+ RedTransAp *inRes = transSet.insert( errTrans );
+ assert( inRes != 0 );
+ }
+ return errTrans;
+}
+
+RedStateAp *RedFsmAp::getErrorState()
+{
+ /* Something went wrong. An error state is needed but one was not supplied
+ * by the frontend. */
+ assert( errState != 0 );
+ return errState;
+}
+
+
+RedTransAp *RedFsmAp::allocateTrans( RedStateAp *targ, RedAction *action )
+{
+ /* Create a reduced trans and look for it in the transiton set. */
+ RedTransAp redTrans( targ, action, 0 );
+ RedTransAp *inDict = transSet.find( &redTrans );
+ if ( inDict == 0 ) {
+ inDict = new RedTransAp( targ, action, nextTransId++ );
+ transSet.insert( inDict );
+ }
+ return inDict;
+}
+
+void RedFsmAp::partitionFsm( int nparts )
+{
+ /* At this point the states are ordered by a depth-first traversal. We
+ * will allocate to partitions based on this ordering. */
+ this->nParts = nparts;
+ int partSize = stateList.length() / nparts;
+ int remainder = stateList.length() % nparts;
+ int numInPart = partSize;
+ int partition = 0;
+ if ( remainder-- > 0 )
+ numInPart += 1;
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ st->partition = partition;
+
+ numInPart -= 1;
+ if ( numInPart == 0 ) {
+ partition += 1;
+ numInPart = partSize;
+ if ( remainder-- > 0 )
+ numInPart += 1;
+ }
+ }
+}
+
+void RedFsmAp::setInTrans()
+{
+ /* First pass counts the number of transitions. */
+ for ( TransApSet::Iter trans = transSet; trans.lte(); trans++ )
+ trans->targ->numInTrans += 1;
+
+ /* Pass over states to allocate the needed memory. Reset the counts so we
+ * can use them as the current size. */
+ for ( RedStateList::Iter st = stateList; st.lte(); st++ ) {
+ st->inTrans = new RedTransAp*[st->numInTrans];
+ st->numInTrans = 0;
+ }
+
+ /* Second pass over transitions copies pointers into the in trans list. */
+ for ( TransApSet::Iter trans = transSet; trans.lte(); trans++ )
+ trans->targ->inTrans[trans->targ->numInTrans++] = trans;
+}
diff --git a/contrib/tools/ragel5/redfsm/redfsm.h b/contrib/tools/ragel5/redfsm/redfsm.h
new file mode 100644
index 0000000000..515b1b621b
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/redfsm.h
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _REDFSM_H
+#define _REDFSM_H
+
+#include <assert.h>
+#include <string.h>
+#include <string>
+#include "common.h"
+#include "vector.h"
+#include "dlist.h"
+#include "compare.h"
+#include "bstmap.h"
+#include "bstset.h"
+#include "avlmap.h"
+#include "avltree.h"
+#include "avlbasic.h"
+#include "mergesort.h"
+#include "sbstmap.h"
+#include "sbstset.h"
+#include "sbsttable.h"
+
+#define TRANS_ERR_TRANS 0
+#define STATE_ERR_STATE 0
+#define FUNC_NO_FUNC 0
+
+using std::string;
+
+struct RedStateAp;
+struct InlineList;
+struct Action;
+
+/* Location in an input file. */
+struct InputLoc
+{
+ int line;
+ int col;
+};
+
+/*
+ * Inline code tree
+ */
+struct InlineItem
+{
+ enum Type
+ {
+ Text, Goto, Call, Next, GotoExpr, CallExpr, NextExpr, Ret,
+ PChar, Char, Hold, Exec, HoldTE, ExecTE, Curs, Targs, Entry,
+ LmSwitch, LmSetActId, LmSetTokEnd, LmGetTokEnd, LmInitTokStart,
+ LmInitAct, LmSetTokStart, SubAction, Break
+ };
+
+ InlineItem( const InputLoc &loc, Type type ) :
+ loc(loc), data(0), targId(0), targState(0),
+ lmId(0), children(0), offset(0),
+ handlesError(false), type(type) { }
+
+ InputLoc loc;
+ char *data;
+ int targId;
+ RedStateAp *targState;
+ int lmId;
+ InlineList *children;
+ int offset;
+ bool handlesError;
+ Type type;
+
+ InlineItem *prev, *next;
+};
+
+/* Normally this would be atypedef, but that would entail including DList from
+ * ptreetypes, which should be just typedef forwards. */
+struct InlineList : public DList<InlineItem> { };
+
+/* Element in list of actions. Contains the string for the code to exectute. */
+struct Action
+:
+ public DListEl<Action>
+{
+ Action( )
+ :
+ name(0),
+ inlineList(0),
+ actionId(0),
+ numTransRefs(0),
+ numToStateRefs(0),
+ numFromStateRefs(0),
+ numEofRefs(0)
+ {
+ }
+
+ /* Data collected during parse. */
+ InputLoc loc;
+ char *name;
+ InlineList *inlineList;
+ int actionId;
+
+ string nameOrLoc();
+
+ /* Number of references in the final machine. */
+ int numRefs()
+ { return numTransRefs + numToStateRefs + numFromStateRefs + numEofRefs; }
+ int numTransRefs;
+ int numToStateRefs;
+ int numFromStateRefs;
+ int numEofRefs;
+};
+
+
+/* Forwards. */
+struct RedStateAp;
+struct StateAp;
+
+/* Transistion Action Element. */
+typedef SBstMapEl< int, Action* > ActionTableEl;
+
+/* Transition Action Table. */
+struct ActionTable
+ : public SBstMap< int, Action*, CmpOrd<int> >
+{
+ void setAction( int ordering, Action *action );
+ void setActions( int *orderings, Action **actions, int nActs );
+ void setActions( const ActionTable &other );
+};
+
+/* Compare of a whole action table element (key & value). */
+struct CmpActionTableEl
+{
+ static int compare( const ActionTableEl &action1,
+ const ActionTableEl &action2 )
+ {
+ if ( action1.key < action2.key )
+ return -1;
+ else if ( action1.key > action2.key )
+ return 1;
+ else if ( action1.value < action2.value )
+ return -1;
+ else if ( action1.value > action2.value )
+ return 1;
+ return 0;
+ }
+};
+
+/* Compare for ActionTable. */
+typedef CmpSTable< ActionTableEl, CmpActionTableEl > CmpActionTable;
+
+/* Set of states. */
+typedef BstSet<RedStateAp*> RedStateSet;
+typedef BstSet<int> IntSet;
+
+/* Reduced action. */
+struct RedAction
+:
+ public AvlTreeEl<RedAction>
+{
+ RedAction( )
+ :
+ key(),
+ eofRefs(0),
+ numTransRefs(0),
+ numToStateRefs(0),
+ numFromStateRefs(0),
+ numEofRefs(0),
+ bAnyNextStmt(false),
+ bAnyCurStateRef(false),
+ bAnyBreakStmt(false)
+ { }
+
+ const ActionTable &getKey()
+ { return key; }
+
+ ActionTable key;
+ int actListId;
+ int location;
+ IntSet *eofRefs;
+
+ /* Number of references in the final machine. */
+ int numRefs()
+ { return numTransRefs + numToStateRefs + numFromStateRefs + numEofRefs; }
+ int numTransRefs;
+ int numToStateRefs;
+ int numFromStateRefs;
+ int numEofRefs;
+
+ bool anyNextStmt() { return bAnyNextStmt; }
+ bool anyCurStateRef() { return bAnyCurStateRef; }
+ bool anyBreakStmt() { return bAnyBreakStmt; }
+
+ bool bAnyNextStmt;
+ bool bAnyCurStateRef;
+ bool bAnyBreakStmt;
+};
+typedef AvlTree<RedAction, ActionTable, CmpActionTable> ActionTableMap;
+
+/* Reduced transition. */
+struct RedTransAp
+:
+ public AvlTreeEl<RedTransAp>
+{
+ RedTransAp( RedStateAp *targ, RedAction *action, int id )
+ : targ(targ), action(action), id(id), labelNeeded(true) { }
+
+ RedStateAp *targ;
+ RedAction *action;
+ int id;
+ bool partitionBoundary;
+ bool labelNeeded;
+};
+
+/* Compare of transitions for the final reduction of transitions. Comparison
+ * is on target and the pointer to the shared action table. It is assumed that
+ * when this is used the action tables have been reduced. */
+struct CmpRedTransAp
+{
+ static int compare( const RedTransAp &t1, const RedTransAp &t2 )
+ {
+ if ( t1.targ < t2.targ )
+ return -1;
+ else if ( t1.targ > t2.targ )
+ return 1;
+ else if ( t1.action < t2.action )
+ return -1;
+ else if ( t1.action > t2.action )
+ return 1;
+ else
+ return 0;
+ }
+};
+
+typedef AvlBasic<RedTransAp, CmpRedTransAp> TransApSet;
+
+/* Element in out range. */
+struct RedTransEl
+{
+ /* Constructors. */
+ RedTransEl( Key lowKey, Key highKey, RedTransAp *value )
+ : lowKey(lowKey), highKey(highKey), value(value) { }
+
+ Key lowKey, highKey;
+ RedTransAp *value;
+};
+
+typedef Vector<RedTransEl> RedTransList;
+typedef Vector<RedStateAp*> RedStateVect;
+
+typedef BstMapEl<RedStateAp*, unsigned long long> RedSpanMapEl;
+typedef BstMap<RedStateAp*, unsigned long long> RedSpanMap;
+
+/* Compare used by span map sort. Reverse sorts by the span. */
+struct CmpRedSpanMapEl
+{
+ static int compare( const RedSpanMapEl &smel1, const RedSpanMapEl &smel2 )
+ {
+ if ( smel1.value > smel2.value )
+ return -1;
+ else if ( smel1.value < smel2.value )
+ return 1;
+ else
+ return 0;
+ }
+};
+
+/* Sorting state-span map entries by span. */
+typedef MergeSort<RedSpanMapEl, CmpRedSpanMapEl> RedSpanMapSort;
+
+/* Set of entry ids that go into this state. */
+typedef Vector<int> EntryIdVect;
+typedef Vector<char*> EntryNameVect;
+
+typedef Vector< Action* > CondSet;
+
+struct Condition
+{
+ Condition( )
+ : key(0), baseKey(0) {}
+
+ Key key;
+ Key baseKey;
+ CondSet condSet;
+
+ Condition *next, *prev;
+};
+typedef DList<Condition> ConditionList;
+
+struct CondSpace
+{
+ Key baseKey;
+ CondSet condSet;
+ int condSpaceId;
+
+ CondSpace *next, *prev;
+};
+typedef DList<CondSpace> CondSpaceList;
+
+struct StateCond
+{
+ Key lowKey;
+ Key highKey;
+
+ CondSpace *condSpace;
+
+ StateCond *prev, *next;
+};
+typedef DList<StateCond> StateCondList;
+typedef Vector<StateCond*> StateCondVect;
+
+/* Reduced state. */
+struct RedStateAp
+{
+ RedStateAp()
+ :
+ defTrans(0),
+ condList(0),
+ transList(0),
+ isFinal(false),
+ labelNeeded(false),
+ outNeeded(false),
+ onStateList(false),
+ toStateAction(0),
+ fromStateAction(0),
+ eofAction(0),
+ id(0),
+ bAnyRegCurStateRef(false),
+ partitionBoundary(false),
+ inTrans(0),
+ numInTrans(0)
+ { }
+
+ /* Transitions out. */
+ RedTransList outSingle;
+ RedTransList outRange;
+ RedTransAp *defTrans;
+
+ /* For flat conditions. */
+ Key condLowKey, condHighKey;
+ CondSpace **condList;
+
+ /* For flat keys. */
+ Key lowKey, highKey;
+ RedTransAp **transList;
+
+ /* The list of states that transitions from this state go to. */
+ RedStateVect targStates;
+
+ bool isFinal;
+ bool labelNeeded;
+ bool outNeeded;
+ bool onStateList;
+ RedAction *toStateAction;
+ RedAction *fromStateAction;
+ RedAction *eofAction;
+ int id;
+ StateCondList stateCondList;
+ StateCondVect stateCondVect;
+
+ /* Pointers for the list of states. */
+ RedStateAp *prev, *next;
+
+ bool anyRegCurStateRef() { return bAnyRegCurStateRef; }
+ bool bAnyRegCurStateRef;
+
+ int partition;
+ bool partitionBoundary;
+
+ RedTransAp **inTrans;
+ int numInTrans;
+};
+
+/* List of states. */
+typedef DList<RedStateAp> RedStateList;
+
+/* Set of reduced transitons. Comparison is by pointer. */
+typedef BstSet< RedTransAp*, CmpOrd<RedTransAp*> > RedTransSet;
+
+/* Next version of the fsm machine. */
+struct RedFsmAp
+{
+ RedFsmAp();
+
+ bool wantComplete;
+ bool forcedErrorState;
+
+ int nextActionId;
+ int nextTransId;
+
+ /* Next State Id doubles as the total number of state ids. */
+ int nextStateId;
+
+ TransApSet transSet;
+ ActionTableMap actionMap;
+ RedStateList stateList;
+ RedStateSet entryPoints;
+ RedStateAp *startState;
+ RedStateAp *errState;
+ RedTransAp *errTrans;
+ RedTransAp *errActionTrans;
+ RedStateAp *firstFinState;
+ int numFinStates;
+ int nParts;
+
+ bool bAnyToStateActions;
+ bool bAnyFromStateActions;
+ bool bAnyRegActions;
+ bool bAnyEofActions;
+ bool bAnyActionGotos;
+ bool bAnyActionCalls;
+ bool bAnyActionRets;
+ bool bAnyRegActionRets;
+ bool bAnyRegActionByValControl;
+ bool bAnyRegNextStmt;
+ bool bAnyRegCurStateRef;
+ bool bAnyRegBreak;
+ bool bAnyLmSwitchError;
+ bool bAnyConditions;
+
+ int maxState;
+ int maxSingleLen;
+ int maxRangeLen;
+ int maxKeyOffset;
+ int maxIndexOffset;
+ int maxIndex;
+ int maxActListId;
+ int maxActionLoc;
+ int maxActArrItem;
+ unsigned long long maxSpan;
+ unsigned long long maxCondSpan;
+ int maxFlatIndexOffset;
+ Key maxKey;
+ int maxCondOffset;
+ int maxCondLen;
+ int maxCondSpaceId;
+ int maxCondIndexOffset;
+ int maxCond;
+
+ bool anyActions();
+ bool anyToStateActions() { return bAnyToStateActions; }
+ bool anyFromStateActions() { return bAnyFromStateActions; }
+ bool anyRegActions() { return bAnyRegActions; }
+ bool anyEofActions() { return bAnyEofActions; }
+ bool anyActionGotos() { return bAnyActionGotos; }
+ bool anyActionCalls() { return bAnyActionCalls; }
+ bool anyActionRets() { return bAnyActionRets; }
+ bool anyRegActionRets() { return bAnyRegActionRets; }
+ bool anyRegActionByValControl() { return bAnyRegActionByValControl; }
+ bool anyRegNextStmt() { return bAnyRegNextStmt; }
+ bool anyRegCurStateRef() { return bAnyRegCurStateRef; }
+ bool anyRegBreak() { return bAnyRegBreak; }
+ bool anyLmSwitchError() { return bAnyLmSwitchError; }
+ bool anyConditions() { return bAnyConditions; }
+
+
+ /* Is is it possible to extend a range by bumping ranges that span only
+ * one character to the singles array. */
+ bool canExtend( const RedTransList &list, int pos );
+
+ /* Pick single transitions from the ranges. */
+ void moveTransToSingle( RedStateAp *state );
+ void chooseSingle();
+
+ void makeFlat();
+
+ /* Move a selected transition from ranges to default. */
+ void moveToDefault( RedTransAp *defTrans, RedStateAp *state );
+
+ /* Pick a default transition by largest span. */
+ RedTransAp *chooseDefaultSpan( RedStateAp *state );
+ void chooseDefaultSpan();
+
+ /* Pick a default transition by most number of ranges. */
+ RedTransAp *chooseDefaultNumRanges( RedStateAp *state );
+ void chooseDefaultNumRanges();
+
+ /* Pick a default transition tailored towards goto driven machine. */
+ RedTransAp *chooseDefaultGoto( RedStateAp *state );
+ void chooseDefaultGoto();
+
+ /* Ordering states by transition connections. */
+ void optimizeStateOrdering( RedStateAp *state );
+ void optimizeStateOrdering();
+
+ /* Ordering states by transition connections. */
+ void depthFirstOrdering( RedStateAp *state );
+ void depthFirstOrdering();
+
+ /* Set state ids. */
+ void sequentialStateIds();
+ void sortStateIdsByFinal();
+
+ /* Arrange states in by final id. This is a stable sort. */
+ void sortStatesByFinal();
+
+ /* Sorting states by id. */
+ void sortByStateId();
+
+ /* Locating the first final state. This is the final state with the lowest
+ * id. */
+ void findFirstFinState();
+
+ void assignActionLocs();
+
+ RedTransAp *getErrorTrans();
+ RedStateAp *getErrorState();
+
+ /* Is every char in the alphabet covered? */
+ bool alphabetCovered( RedTransList &outRange );
+
+ RedTransAp *allocateTrans( RedStateAp *targState, RedAction *actionTable );
+
+ void partitionFsm( int nParts );
+
+ void setInTrans();
+};
+
+
+#endif /* _REDFSM_H */
diff --git a/contrib/tools/ragel5/redfsm/xmlparse.cpp b/contrib/tools/ragel5/redfsm/xmlparse.cpp
new file mode 100644
index 0000000000..6da8c50e91
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/xmlparse.cpp
@@ -0,0 +1,3549 @@
+/* Automatically generated by Kelbt from "xmlparse.kl".
+ *
+ * Parts of this file are copied from Kelbt source covered by the GNU
+ * GPL. As a special exception, you may use the parts of this file copied
+ * from Kelbt source without restriction. The remainder is derived from
+ * "xmlparse.kl" and inherits the copyright status of that file.
+ */
+
+#line 1 "xmlparse.kl"
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "xmlparse.h"
+#include "common.h"
+#include "gendata.h"
+#include <iostream>
+
+#include <stdlib.h>
+//#include <malloc.h>
+
+using std::cout;
+using std::ostream;
+using std::istream;
+using std::cerr;
+using std::endl;
+
+Key readKey( char *td, char **end );
+long readOffsetPtr( char *td, char **end );
+unsigned long readLength( char *td );
+
+#line 117 "xmlparse.kh"
+#line 120 "xmlparse.kh"
+#line 163 "xmlparse.kh"
+#line 846 "xmlparse.kl"
+
+
+#line 54 "xmlparse.cpp"
+struct Parser_Lel_inline_item_type
+{
+#line 499 "xmlparse.kl"
+
+ InlineItem *inlineItem;
+
+
+#line 61 "xmlparse.cpp"
+};
+
+struct Parser_Lel_inline_list
+{
+#line 480 "xmlparse.kl"
+
+ InlineList *inlineList;
+
+
+#line 71 "xmlparse.cpp"
+};
+
+struct Parser_Lel_lm_action_list
+{
+#line 716 "xmlparse.kl"
+
+ InlineList *inlineList;
+
+
+#line 81 "xmlparse.cpp"
+};
+
+struct Parser_Lel_tag_arg
+{
+#line 256 "xmlparse.kl"
+
+ char *option;
+
+
+#line 91 "xmlparse.cpp"
+};
+
+struct Parser_Lel_tag_write_head
+{
+#line 220 "xmlparse.kl"
+
+ InputLoc loc;
+
+
+#line 101 "xmlparse.cpp"
+};
+
+union Parser_UserData
+{
+ struct Parser_Lel_inline_item_type inline_item_type;
+ struct Parser_Lel_inline_list inline_list;
+ struct Parser_Lel_lm_action_list lm_action_list;
+ struct Parser_Lel_tag_arg tag_arg;
+ struct Parser_Lel_tag_write_head tag_write_head;
+ struct Token token;
+};
+
+struct Parser_LangEl
+{
+ char *file;
+ int line;
+ int type;
+ int reduction;
+ int state;
+ union Parser_UserData user;
+ unsigned int retry;
+ struct Parser_LangEl *next, *child;
+};
+
+#line 127 "xmlparse.cpp"
+unsigned int Parser_startState = 0;
+
+short Parser_indicies[] = {
+ 142, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 140, 139, 0, 1, 283, 144, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 144, 144, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 144, -1, -1, -1, -1, -1,
+ -1, -1, -1, 2, 146, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 151,
+ 146, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 146, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 3, 143, -1, -1, -1,
+ 4, 5, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 6, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 169, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 145, 147, 148, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 7, 153, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 153, -1, -1, -1, -1,
+ -1, -1, 153, -1, 153, -1, -1, -1,
+ -1, -1, -1, -1, 153, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 153, 153, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 8,
+ 141, 9, 171, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 171, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 10, 11, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 174, -1, -1,
+ -1, -1, -1, -1, 12, -1, 13, -1,
+ -1, -1, -1, -1, -1, -1, 16, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 15, 14, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 152, 154, 155, 156, 157, 158,
+ 159, -1, -1, -1, -1, -1, -1, 17,
+ 149, 18, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 19, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 170, 150, 20, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 217, -1, -1, -1, -1,
+ -1, -1, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, -1, 217, 217, 217, 217,
+ 217, 217, 217, -1, -1, -1, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 21, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 217, -1, -1, -1, -1,
+ -1, -1, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, -1, 217, 217, 217, 217,
+ 217, 217, 217, -1, -1, -1, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 24, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 217, -1, -1, -1, -1,
+ -1, -1, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, -1, 217, 217, 217, 217,
+ 217, 217, 217, -1, -1, -1, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 23, 162, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 162, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 22, 176, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 176, -1, -1, -1, -1, 176, 176,
+ 176, 176, -1, -1, -1, -1, -1, -1,
+ 176, -1, 176, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 25, 168, 26, 164, 27, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 52, -1, -1, -1, -1, -1, -1,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, -1, 53, 47, 51, 50, 48, 46,
+ 49, -1, -1, -1, 36, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 216, -1, 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,
+ 54, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 55, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 161, 56,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 52, -1, -1, -1,
+ -1, -1, -1, 28, 29, 30, 31, 32,
+ 33, 34, 35, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, -1, 53, 47, 51,
+ 50, 48, 46, 49, -1, -1, -1, 36,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 216,
+ -1, 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, 57, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 52, -1, -1, -1, -1, -1, -1, 28,
+ 29, 30, 31, 32, 33, 34, 35, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45,
+ -1, 53, 47, 51, 50, 48, 46, 49,
+ -1, -1, -1, 36, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 216, -1, 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, 58,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 191, -1, -1, -1,
+ -1, 59, 60, 212, 274, -1, -1, -1,
+ -1, -1, -1, 61, -1, 279, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 175, 177, 178, 179,
+ 180, 181, 182, 183, -1, -1, 62, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 63, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 64, -1, -1,
+ 65, 172, 165, 67, 68, 69, 70, 217,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 217, -1, -1, -1,
+ -1, -1, -1, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, -1, 217, 217, 217,
+ 217, 217, 217, 217, -1, -1, -1, 217,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 71, 217,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 217, -1, -1, -1,
+ -1, -1, -1, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, -1, 217, 217, 217,
+ 217, 217, 217, 217, -1, -1, -1, 217,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 72, 217,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 217, -1, -1, -1,
+ -1, -1, -1, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, -1, 217, 217, 217,
+ 217, 217, 217, 217, -1, -1, -1, 217,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 73, 74,
+ 91, 75, 76, 77, 217, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 217, -1, -1, -1, -1, -1, -1,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, -1, 217, 217, 217, 217, 217, 217,
+ 217, -1, -1, -1, 217, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 78, 79, 217, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 217, -1, -1, -1, -1, -1,
+ -1, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, -1, 217, 217, 217, 217, 217,
+ 217, 217, -1, -1, -1, 217, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 80, 81, 82, 83,
+ 89, 85, 88, 90, 87, 86, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 217, -1, -1, -1, -1,
+ -1, -1, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, -1, 217, 217, 217, 217,
+ 217, 217, 217, -1, -1, -1, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 66, 271, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 271, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 84, 160, 92, 167, 166, 173,
+ 93, 94, 188, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 188, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 95,
+ 193, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 193, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 96, 214, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 214,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 97,
+ 276, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 276,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 98,
+ 100, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 99, 281, 101, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1,
+ -1, 28, 29, 30, 31, 32, 33, 34,
+ 35, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, -1, 53, 47, 51, 50, 48,
+ 46, 49, -1, -1, -1, 36, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 216, -1, 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, 102, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1,
+ -1, 28, 29, 30, 31, 32, 33, 34,
+ 35, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, -1, 53, 47, 51, 50, 48,
+ 46, 49, -1, -1, -1, 36, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 216, -1, 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, 103, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 52, -1,
+ -1, -1, -1, -1, -1, 28, 29, 30,
+ 31, 32, 33, 34, 35, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, -1, 53,
+ 47, 51, 50, 48, 46, 49, -1, -1,
+ -1, 36, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 216, -1, 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, 104, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1,
+ -1, 28, 29, 30, 31, 32, 33, 34,
+ 35, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, -1, 53, 47, 51, 50, 48,
+ 46, 49, -1, -1, -1, 36, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 216, -1, 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, 251, 253, 254, 255, 105, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 52, -1, -1, -1, -1, -1,
+ -1, 28, 29, 30, 31, 32, 33, 34,
+ 35, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, -1, 53, 47, 51, 50, 48,
+ 46, 49, -1, -1, -1, 36, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 216, -1, 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, 257, 106, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 52,
+ -1, -1, -1, -1, -1, -1, 28, 29,
+ 30, 31, 32, 33, 34, 35, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, -1,
+ 53, 47, 51, 50, 48, 46, 49, -1,
+ -1, -1, 36, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 216, -1, 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, 259, 260,
+ 261, 107, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 108, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 270, 263,
+ 267, 266, 264, 262, 265, 252, 163, 184,
+ 185, 109, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 110, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 187,
+ 111, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 112, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 192, 113, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 114, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 213, 115, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 116, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 275, 118, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 100, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 280, 117,
+ 268, 248, 249, 250, 256, 258, 269, 217,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 217, -1, -1, -1,
+ -1, -1, -1, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, -1, 217, 217, 217,
+ 217, 217, 217, 217, -1, -1, -1, 217,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 119, 186,
+ 120, 190, 196, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 196, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 196, -1, -1,
+ -1, -1, 196, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 121, 211, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 217, -1, -1, -1, -1,
+ -1, -1, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, 217, 217, 217, 217, 217,
+ 217, 217, 217, -1, 217, 217, 217, 217,
+ 217, 217, 217, -1, -1, -1, 217, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 122, 273, 123,
+ 282, 278, 124, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 52,
+ -1, -1, -1, -1, -1, -1, 28, 29,
+ 30, 31, 32, 33, 34, 35, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, -1,
+ 53, 47, 51, 50, 48, 46, 49, -1,
+ -1, -1, 36, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 216, -1, 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, 189, 125,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 207, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 126, -1, -1, -1, -1, 202,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 195, 197, 198, 199, 127, -1,
+ -1, 128, 129, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 52,
+ -1, -1, -1, -1, -1, -1, 28, 29,
+ 30, 31, 32, 33, 34, 35, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, -1,
+ 53, 47, 51, 50, 48, 46, 49, -1,
+ -1, -1, 36, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 216, -1, 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, 277, 272,
+ 194, 130, 204, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 204, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 131, 209, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 209, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 132, 215,
+ 200, 133, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 134, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 203, 135, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 136, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 208,
+ 201, 137, 206, 138, 205, 210,
+};
+
+unsigned short Parser_keys[] = {
+ 129, 188, 185, 185, 47, 189, 47, 195,
+ 47, 207, 47, 196, 129, 129, 47, 47,
+ 47, 208, 47, 210, 131, 131, 47, 209,
+ 130, 130, 47, 47, 47, 206, 47, 206,
+ 47, 206, 47, 204, 47, 211, 180, 180,
+ 47, 47, 143, 143, 47, 266, 47, 205,
+ 47, 266, 47, 266, 47, 272, 184, 184,
+ 145, 145, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 206, 47, 206, 47, 206,
+ 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 206, 47, 47, 47, 206,
+ 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 206, 47, 267, 153, 153,
+ 47, 47, 181, 181, 182, 182, 136, 136,
+ 47, 47, 47, 47, 47, 220, 47, 223,
+ 47, 237, 47, 270, 150, 274, 47, 266,
+ 155, 155, 156, 156, 157, 157, 158, 158,
+ 47, 266, 47, 266, 47, 266, 162, 162,
+ 163, 163, 164, 164, 165, 165, 47, 266,
+ 167, 167, 47, 266, 169, 169, 170, 170,
+ 171, 171, 47, 268, 174, 174, 175, 175,
+ 176, 176, 177, 177, 178, 178, 179, 179,
+ 183, 183, 154, 154, 137, 137, 138, 138,
+ 47, 221, 47, 224, 47, 238, 47, 271,
+ 47, 274, 47, 47, 148, 148, 159, 159,
+ 160, 160, 161, 161, 166, 166, 168, 168,
+ 173, 173, 47, 206, 147, 147, 47, 47,
+ 132, 132, 47, 225, 139, 139, 47, 206,
+ 140, 140, 47, 47, 150, 150, 149, 149,
+ 47, 266, 171, 171, 47, 233, 47, 266,
+ 142, 142, 148, 148, 133, 133, 47, 47,
+ 47, 231, 47, 234, 141, 141, 146, 146,
+ 47, 232, 47, 235, 151, 151, 47, 47,
+ 134, 134, 47, 47, 152, 152, 135, 135,
+ 0, 0
+};
+
+unsigned int Parser_offsets[] = {
+ 0, 60, 61, 204, 353, 514, 664, 665,
+ 666, 828, 992, 993, 1156, 1157, 1158, 1318,
+ 1478, 1638, 1796, 1961, 1962, 1963, 1964, 2184,
+ 2343, 2563, 2783, 3009, 3010, 3011, 3012, 3013,
+ 3014, 3015, 3175, 3335, 3495, 3496, 3497, 3498,
+ 3499, 3500, 3660, 3661, 3821, 3822, 3823, 3824,
+ 3825, 3826, 3827, 3828, 3829, 3830, 3990, 4211,
+ 4212, 4213, 4214, 4215, 4216, 4217, 4218, 4392,
+ 4569, 4760, 4984, 5109, 5329, 5330, 5331, 5332,
+ 5333, 5553, 5773, 5993, 5994, 5995, 5996, 5997,
+ 6217, 6218, 6438, 6439, 6440, 6441, 6663, 6664,
+ 6665, 6666, 6667, 6668, 6669, 6670, 6671, 6672,
+ 6673, 6848, 7026, 7218, 7443, 7671, 7672, 7673,
+ 7674, 7675, 7676, 7677, 7678, 7679, 7839, 7840,
+ 7841, 7842, 8021, 8022, 8182, 8183, 8184, 8185,
+ 8186, 8406, 8407, 8594, 8814, 8815, 8816, 8817,
+ 8818, 9003, 9191, 9192, 9193, 9379, 9568, 9569,
+ 9570, 9571, 9572, 9573, 9574
+};
+
+unsigned short Parser_targs[] = {
+ 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,
+ 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, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140, 140, 140, 140, 140,
+ 140, 140, 140, 140
+};
+
+unsigned int Parser_actInds[] = {
+ 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 60, 62,
+ 64, 66, 68, 70, 72, 74, 76, 78,
+ 80, 82, 84, 86, 88, 90, 92, 94,
+ 96, 98, 100, 102, 104, 106, 108, 110,
+ 112, 114, 116, 118, 120, 122, 124, 126,
+ 128, 130, 132, 134, 136, 138, 140, 142,
+ 144, 146, 148, 150, 152, 154, 156, 158,
+ 160, 162, 164, 166, 168, 170, 172, 174,
+ 176, 178, 180, 182, 184, 186, 188, 190,
+ 192, 194, 196, 198, 200, 202, 204, 206,
+ 208, 210, 212, 214, 216, 218, 220, 222,
+ 224, 226, 228, 230, 232, 234, 236, 238,
+ 240, 242, 244, 246, 248, 250, 252, 254,
+ 256, 258, 260, 262, 264, 266, 268, 270,
+ 272, 274, 276, 278, 280, 282, 284, 286,
+ 288, 290, 292, 294, 296, 298, 300, 302,
+ 304, 306, 308, 310, 312, 314, 316, 318,
+ 320, 322, 324, 326, 328, 330, 332, 334,
+ 336, 338, 340, 342, 344, 346, 348, 350,
+ 352, 354, 356, 358, 360, 362, 364, 366,
+ 368, 370, 372, 374, 376, 378, 380, 382,
+ 384, 386, 388, 390, 392, 394, 396, 398,
+ 400, 402, 404, 406, 408, 410, 412, 414,
+ 416, 418, 420, 422, 424, 426, 428, 430,
+ 432, 434, 436, 438, 440, 442, 444, 446,
+ 448, 450, 452, 454, 456, 458, 460, 462,
+ 464, 466, 468, 470, 472, 474, 476, 478,
+ 480, 482, 484, 486, 488, 490, 492, 494,
+ 496, 498, 500, 502, 504, 506, 508, 510,
+ 512, 514, 516, 518, 520, 522, 524, 526,
+ 528, 530, 532, 534, 536, 538, 540, 542,
+ 544, 546, 548, 550, 552, 554, 556, 558,
+ 560, 562, 564, 566
+};
+
+unsigned int Parser_actions[] = {
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 3, 0,
+ 6, 0, 11, 0, 15, 0, 19, 0,
+ 22, 0, 27, 0, 30, 0, 35, 0,
+ 39, 0, 43, 0, 47, 0, 51, 0,
+ 55, 0, 58, 0, 63, 0, 67, 0,
+ 71, 0, 75, 0, 79, 0, 83, 0,
+ 87, 0, 91, 0, 94, 0, 99, 0,
+ 103, 0, 107, 0, 111, 0, 115, 0,
+ 119, 0, 123, 0, 127, 0, 130, 0,
+ 135, 0, 139, 0, 143, 0, 147, 0,
+ 150, 0, 155, 0, 159, 0, 163, 0,
+ 167, 0, 171, 0, 175, 0, 179, 0,
+ 183, 0, 187, 0, 191, 0, 195, 0,
+ 198, 0, 203, 0, 207, 0, 211, 0,
+ 215, 0, 218, 0, 223, 0, 227, 0,
+ 230, 0, 235, 0, 239, 0, 243, 0,
+ 247, 0, 251, 0, 255, 0, 259, 0,
+ 262, 0, 267, 0, 271, 0, 275, 0,
+ 279, 0, 282, 0, 287, 0, 291, 0,
+ 295, 0, 299, 0, 302, 0, 307, 0,
+ 311, 0, 314, 0, 319, 0, 323, 0,
+ 327, 0, 331, 0, 335, 0, 339, 0,
+ 343, 0, 347, 0, 351, 0, 355, 0,
+ 359, 0, 363, 0, 367, 0, 371, 0,
+ 375, 0, 379, 0, 383, 0, 387, 0,
+ 391, 0, 395, 0, 399, 0, 403, 0,
+ 407, 0, 411, 0, 415, 0, 419, 0,
+ 423, 0, 427, 0, 431, 0, 435, 0,
+ 439, 0, 443, 0, 447, 0, 451, 0,
+ 455, 0, 459, 0, 463, 0, 467, 0,
+ 471, 0, 475, 0, 479, 0, 483, 0,
+ 487, 0, 491, 0, 495, 0, 499, 0,
+ 503, 0, 507, 0, 511, 0, 515, 0,
+ 519, 0, 523, 0, 527, 0, 530, 0,
+ 535, 0, 539, 0, 543, 0, 547, 0,
+ 550, 0, 555, 0, 559, 0, 563, 0,
+ 567, 0, 571, 0, 575, 0, 1, 0
+};
+
+int Parser_commitLen[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2
+};
+
+unsigned int Parser_fssProdIdIndex[] = {
+ 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, 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, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143,
+ 144
+};
+
+char Parser_fssProdLengths[] = {
+ 1, 0, 5, 1, 2, 0, 2, 0,
+ 1, 1, 3, 4, 1, 2, 0, 1,
+ 1, 1, 1, 1, 1, 4, 2, 0,
+ 3, 3, 4, 4, 4, 4, 1, 2,
+ 0, 3, 4, 1, 2, 0, 1, 1,
+ 1, 1, 1, 1, 1, 3, 3, 4,
+ 2, 0, 3, 4, 1, 2, 0, 4,
+ 2, 0, 1, 1, 1, 3, 4, 1,
+ 2, 0, 3, 4, 1, 2, 0, 3,
+ 4, 1, 2, 0, 4, 2, 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, 3, 3, 3, 3, 4, 4, 4,
+ 3, 3, 3, 3, 3, 4, 3, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 4, 4, 2, 0, 4, 4, 1,
+ 2, 0, 3, 4, 1, 2, 1, 3,
+ 1
+};
+
+unsigned short Parser_prodLhsIds[] = {
+ 187, 187, 186, 188, 189, 189, 190, 190,
+ 192, 192, 193, 191, 195, 196, 196, 197,
+ 197, 197, 197, 197, 197, 202, 204, 204,
+ 205, 198, 199, 200, 201, 194, 207, 208,
+ 208, 209, 203, 210, 211, 211, 212, 212,
+ 212, 212, 212, 212, 212, 213, 214, 215,
+ 220, 220, 221, 216, 222, 223, 223, 224,
+ 225, 225, 226, 226, 226, 227, 228, 230,
+ 231, 231, 232, 229, 233, 234, 234, 235,
+ 217, 236, 237, 237, 238, 206, 206, 239,
+ 239, 239, 239, 239, 239, 239, 239, 239,
+ 239, 239, 239, 239, 239, 239, 239, 239,
+ 239, 239, 239, 239, 239, 239, 239, 239,
+ 239, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263,
+ 264, 265, 266, 267, 267, 268, 218, 269,
+ 270, 270, 271, 219, 272, 273, 273, 274,
+ 275
+};
+
+const char *Parser_prodNames[] = {
+ "start-1",
+ "start-2",
+ "tag_ragel-1",
+ "tag_ragel_head-1",
+ "ragel_def_list-1",
+ "ragel_def_list-2",
+ "host_or_write_list-1",
+ "host_or_write_list-2",
+ "host_or_write-1",
+ "host_or_write-2",
+ "tag_host-1",
+ "ragel_def-1",
+ "tag_ragel_def_head-1",
+ "ragel_def_item_list-1",
+ "ragel_def_item_list-2",
+ "ragel_def_item-1",
+ "ragel_def_item-2",
+ "ragel_def_item-3",
+ "ragel_def_item-4",
+ "ragel_def_item-5",
+ "ragel_def_item-6",
+ "tag_export_list-1",
+ "export_list-1",
+ "export_list-2",
+ "tag_export-1",
+ "tag_alph_type-1",
+ "tag_getkey_expr-1",
+ "tag_access_expr-1",
+ "tag_curstate_expr-1",
+ "tag_write-1",
+ "tag_write_head-1",
+ "write_option_list-1",
+ "write_option_list-2",
+ "tag_arg-1",
+ "tag_machine-1",
+ "tag_machine_head-1",
+ "machine_item_list-1",
+ "machine_item_list-2",
+ "machine_item-1",
+ "machine_item-2",
+ "machine_item-3",
+ "machine_item-4",
+ "machine_item-5",
+ "machine_item-6",
+ "machine_item-7",
+ "tag_start_state-1",
+ "tag_error_state-1",
+ "tag_entry_points-1",
+ "entry_point_list-1",
+ "entry_point_list-2",
+ "tag_entry-1",
+ "tag_state_list-1",
+ "tag_state_list_head-1",
+ "state_list-1",
+ "state_list-2",
+ "tag_state-1",
+ "state_item_list-1",
+ "state_item_list-2",
+ "state_item-1",
+ "state_item-2",
+ "state_item-3",
+ "tag_state_actions-1",
+ "tag_state_cond_list-1",
+ "tag_state_cond_list_head-1",
+ "state_cond_list-1",
+ "state_cond_list-2",
+ "state_cond-1",
+ "tag_trans_list-1",
+ "tag_trans_list_head-1",
+ "trans_list-1",
+ "trans_list-2",
+ "tag_trans-1",
+ "tag_action_list-1",
+ "tag_action_list_head-1",
+ "action_list-1",
+ "action_list-2",
+ "tag_action-1",
+ "inline_list-1",
+ "inline_list-2",
+ "inline_item-1",
+ "inline_item-2",
+ "inline_item-3",
+ "inline_item-4",
+ "inline_item-5",
+ "inline_item-6",
+ "inline_item-7",
+ "inline_item-8",
+ "inline_item-9",
+ "inline_item-10",
+ "inline_item-11",
+ "inline_item-12",
+ "inline_item-13",
+ "inline_item-14",
+ "inline_item-15",
+ "inline_item-16",
+ "inline_item-17",
+ "inline_item-18",
+ "inline_item-19",
+ "inline_item-20",
+ "inline_item-21",
+ "inline_item-22",
+ "inline_item-23",
+ "inline_item-24",
+ "inline_item-25",
+ "inline_item-26",
+ "tag_text-1",
+ "tag_goto-1",
+ "tag_call-1",
+ "tag_next-1",
+ "tag_goto_expr-1",
+ "tag_call_expr-1",
+ "tag_next_expr-1",
+ "tag_ret-1",
+ "tag_break-1",
+ "tag_pchar-1",
+ "tag_char-1",
+ "tag_hold-1",
+ "tag_exec-1",
+ "tag_holdte-1",
+ "tag_execte-1",
+ "tag_curs-1",
+ "tag_targs-1",
+ "tag_il_entry-1",
+ "tag_init_tokstart-1",
+ "tag_init_act-1",
+ "tag_get_tokend-1",
+ "tag_set_tokstart-1",
+ "tag_set_tokend-1",
+ "tag_set_act-1",
+ "tag_sub_action-1",
+ "tag_lm_switch-1",
+ "lm_action_list-1",
+ "lm_action_list-2",
+ "tag_inline_action-1",
+ "tag_action_table_list-1",
+ "tag_action_table_list_head-1",
+ "action_table_list-1",
+ "action_table_list-2",
+ "tag_action_table-1",
+ "tag_cond_space_list-1",
+ "tag_cond_space_list_head-1",
+ "cond_space_list-1",
+ "cond_space_list-2",
+ "tag_cond_space-1",
+ "_start-1"
+};
+
+const char *Parser_lelNames[] = {
+ "D-0",
+ "D-1",
+ "D-2",
+ "D-3",
+ "D-4",
+ "D-5",
+ "D-6",
+ "D-7",
+ "D-8",
+ "D-9",
+ "D-10",
+ "D-11",
+ "D-12",
+ "D-13",
+ "D-14",
+ "D-15",
+ "D-16",
+ "D-17",
+ "D-18",
+ "D-19",
+ "D-20",
+ "D-21",
+ "D-22",
+ "D-23",
+ "D-24",
+ "D-25",
+ "D-26",
+ "D-27",
+ "D-28",
+ "D-29",
+ "D-30",
+ "D-31",
+ "D-32",
+ "!",
+ "\"",
+ "#",
+ "$",
+ "%",
+ "&",
+ "'",
+ "(",
+ ")",
+ "*",
+ "+",
+ ",",
+ "-",
+ ".",
+ "/",
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ ":",
+ ";",
+ "<",
+ "=",
+ ">",
+ "?",
+ "@",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "[",
+ "\\",
+ "]",
+ "^",
+ "_",
+ "`",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "{",
+ "|",
+ "}",
+ "~",
+ "D-127",
+ "TAG_unknown",
+ "TAG_ragel",
+ "TAG_ragel_def",
+ "TAG_host",
+ "TAG_state_list",
+ "TAG_state",
+ "TAG_trans_list",
+ "TAG_t",
+ "TAG_machine",
+ "TAG_start_state",
+ "TAG_error_state",
+ "TAG_action_list",
+ "TAG_action_table_list",
+ "TAG_action",
+ "TAG_action_table",
+ "TAG_alphtype",
+ "TAG_element",
+ "TAG_getkey",
+ "TAG_state_actions",
+ "TAG_entry_points",
+ "TAG_sub_action",
+ "TAG_cond_space_list",
+ "TAG_cond_space",
+ "TAG_cond_list",
+ "TAG_c",
+ "TAG_exports",
+ "TAG_ex",
+ "TAG_text",
+ "TAG_goto",
+ "TAG_call",
+ "TAG_next",
+ "TAG_goto_expr",
+ "TAG_call_expr",
+ "TAG_next_expr",
+ "TAG_ret",
+ "TAG_pchar",
+ "TAG_char",
+ "TAG_hold",
+ "TAG_exec",
+ "TAG_holdte",
+ "TAG_execte",
+ "TAG_curs",
+ "TAG_targs",
+ "TAG_entry",
+ "TAG_data",
+ "TAG_lm_switch",
+ "TAG_init_act",
+ "TAG_set_act",
+ "TAG_set_tokend",
+ "TAG_get_tokend",
+ "TAG_init_tokstart",
+ "TAG_set_tokstart",
+ "TAG_write",
+ "TAG_curstate",
+ "TAG_access",
+ "TAG_break",
+ "TAG_arg",
+ "_eof",
+ "tag_ragel",
+ "start",
+ "tag_ragel_head",
+ "ragel_def_list",
+ "host_or_write_list",
+ "ragel_def",
+ "host_or_write",
+ "tag_host",
+ "tag_write",
+ "tag_ragel_def_head",
+ "ragel_def_item_list",
+ "ragel_def_item",
+ "tag_alph_type",
+ "tag_getkey_expr",
+ "tag_access_expr",
+ "tag_curstate_expr",
+ "tag_export_list",
+ "tag_machine",
+ "export_list",
+ "tag_export",
+ "inline_list",
+ "tag_write_head",
+ "write_option_list",
+ "tag_arg",
+ "tag_machine_head",
+ "machine_item_list",
+ "machine_item",
+ "tag_start_state",
+ "tag_error_state",
+ "tag_entry_points",
+ "tag_state_list",
+ "tag_action_list",
+ "tag_action_table_list",
+ "tag_cond_space_list",
+ "entry_point_list",
+ "tag_entry",
+ "tag_state_list_head",
+ "state_list",
+ "tag_state",
+ "state_item_list",
+ "state_item",
+ "tag_state_actions",
+ "tag_state_cond_list",
+ "tag_trans_list",
+ "tag_state_cond_list_head",
+ "state_cond_list",
+ "state_cond",
+ "tag_trans_list_head",
+ "trans_list",
+ "tag_trans",
+ "tag_action_list_head",
+ "action_list",
+ "tag_action",
+ "inline_item",
+ "inline_item_type",
+ "tag_text",
+ "tag_goto",
+ "tag_call",
+ "tag_next",
+ "tag_goto_expr",
+ "tag_call_expr",
+ "tag_next_expr",
+ "tag_ret",
+ "tag_break",
+ "tag_pchar",
+ "tag_char",
+ "tag_hold",
+ "tag_exec",
+ "tag_holdte",
+ "tag_execte",
+ "tag_curs",
+ "tag_targs",
+ "tag_il_entry",
+ "tag_init_tokstart",
+ "tag_init_act",
+ "tag_get_tokend",
+ "tag_set_tokstart",
+ "tag_set_tokend",
+ "tag_set_act",
+ "tag_sub_action",
+ "tag_lm_switch",
+ "lm_action_list",
+ "tag_inline_action",
+ "tag_action_table_list_head",
+ "action_table_list",
+ "tag_action_table",
+ "tag_cond_space_list_head",
+ "cond_space_list",
+ "tag_cond_space",
+ "_start"
+};
+
+#line 851 "xmlparse.kl"
+
+
+void Parser::init()
+{
+ #line 2079 "xmlparse.cpp"
+ curs = Parser_startState;
+ pool = 0;
+ freshEl = (struct Parser_LangEl*) malloc( sizeof(struct Parser_LangEl)*8128);
+ #ifdef LOG_ACTIONS
+ cerr << "allocating 8128 LangEls" << endl;
+ #endif
+ stackTop = freshEl;
+ stackTop->type = 0;
+ stackTop->state = -1;
+ stackTop->next = 0;
+ stackTop->child = 0;
+ freshPos = 1;
+ lastFinal = stackTop;
+ numRetry = 0;
+ numNodes = 0;
+ errCount = 0;
+#line 856 "xmlparse.kl"
+}
+
+int Parser::parseLangEl( int type, const Token *token )
+{
+ #line 2101 "xmlparse.cpp"
+#define reject() induceReject = 1
+
+ int pos, targState;
+ unsigned int *action;
+ int rhsLen;
+ struct Parser_LangEl *rhs[32];
+ struct Parser_LangEl *lel;
+ struct Parser_LangEl *input;
+ char induceReject;
+
+ if ( curs < 0 )
+ return 0;
+
+ if ( pool == 0 ) {
+ if ( freshPos == 8128 ) {
+ freshEl = (struct Parser_LangEl*) malloc(
+ sizeof(struct Parser_LangEl)*8128);
+ #ifdef LOG_ACTIONS
+ cerr << "allocating 8128 LangEls" << endl;
+ #endif
+ freshPos = 0;
+ }
+ input = freshEl + freshPos++;
+ }
+ else {
+ input = pool;
+ pool = pool->next;
+ }
+ numNodes += 1;
+ input->type = type;
+ input->user.token = *token;
+ input->next = 0;
+ input->retry = 0;
+ input->child = 0;
+
+again:
+ if ( input == 0 )
+ goto _out;
+
+ lel = input;
+ if ( lel->type < Parser_keys[curs<<1] || lel->type > Parser_keys[(curs<<1)+1] )
+ goto parseError;
+
+ pos = Parser_indicies[Parser_offsets[curs] + (lel->type - Parser_keys[curs<<1])];
+ if ( pos < 0 )
+ goto parseError;
+
+ induceReject = 0;
+ targState = Parser_targs[pos];
+ action = Parser_actions + Parser_actInds[pos];
+ if ( lel->retry & 0x0000ffff )
+ action += (lel->retry & 0x0000ffff);
+
+ if ( *action & 0x1 ) {
+ #ifdef LOG_ACTIONS
+ cerr << "shifted: " << Parser_lelNames[lel->type];
+ #endif
+ input = input->next;
+ lel->state = curs;
+ lel->next = stackTop;
+ stackTop = lel;
+
+ if ( action[1] == 0 )
+ lel->retry &= 0xffff0000;
+ else {
+ lel->retry += 1;
+ numRetry += 1;
+ #ifdef LOG_ACTIONS
+ cerr << " retry: " << stackTop;
+ #endif
+ }
+ #ifdef LOG_ACTIONS
+ cerr << endl;
+ #endif
+ }
+
+ if ( Parser_commitLen[pos] != 0 ) {
+ struct Parser_LangEl *commitHead = stackTop;
+ int absCommitLen = Parser_commitLen[pos];
+
+ #ifdef LOG_ACTIONS
+ cerr << "running commit of length: " << Parser_commitLen[pos] << endl;
+ #endif
+
+ if ( absCommitLen < 0 ) {
+ commitHead = commitHead->next;
+ absCommitLen = -1 * absCommitLen;
+ }
+ {
+ struct Parser_LangEl *lel = commitHead;
+ struct Parser_LangEl **cmStack = (struct Parser_LangEl**) malloc( sizeof(struct Parser_LangEl) * numNodes);
+ int n = absCommitLen, depth = 0, sp = 0;
+
+commit_head:
+ if ( lel->retry > 0 ) {
+ if ( lel->retry & 0x0000ffff )
+ numRetry -= 1;
+ if ( lel->retry & 0xffff0000 )
+ numRetry -= 1;
+ lel->retry = 0;
+ }
+
+ /* If depth is > 0 then move over lel freely, otherwise, make
+ * sure that we have not already done n steps down the line. */
+ if ( lel->next != 0 && ( depth > 0 || n > 1 ) ) {
+ cmStack[sp++] = lel;
+ lel = lel->next;
+
+ /* If we are at the top level count the steps down the line. */
+ if ( depth == 0 )
+ n -= 1;
+ goto commit_head;
+ }
+
+commit_reverse:
+ if ( lel->child != 0 ) {
+ cmStack[sp++] = lel;
+ lel = lel->child;
+
+ /* When we move down we need to increment the depth. */
+ depth += 1;
+ goto commit_head;
+ }
+
+commit_upwards:
+ if ( sp > 0 ) {
+ /* Figure out which place to return to. */
+ if ( cmStack[sp-1]->next == lel ) {
+ lel = cmStack[--sp];
+ goto commit_reverse;
+ }
+ else {
+ /* Going back up, adjust the depth. */
+ lel = cmStack[--sp];
+ depth -= 1;
+ goto commit_upwards;
+ }
+ }
+ free( cmStack );
+ }
+ if ( numRetry == 0 ) {
+ #ifdef LOG_ACTIONS
+ cerr << "number of retries is zero, "
+ "executing final actions" << endl;
+ #endif
+ {
+ struct Parser_LangEl *lel = commitHead;
+ struct Parser_LangEl **cmStack = (struct Parser_LangEl**) malloc( sizeof( struct Parser_LangEl) * numNodes);
+ int sp = 0;
+ char doExec = 0;
+
+final_head:
+ if ( lel == lastFinal ) {
+ doExec = 1;
+ goto hit_final;
+ }
+
+ if ( lel->next != 0 ) {
+ cmStack[sp++] = lel;
+ lel = lel->next;
+ goto final_head;
+ }
+
+final_reverse:
+
+ if ( lel->child != 0 ) {
+ cmStack[sp++] = lel;
+ lel = lel->child;
+ goto final_head;
+ }
+
+final_upwards:
+
+ if ( doExec ) {
+{
+ if ( lel->type < 186 ) {
+ }
+ else {
+ struct Parser_LangEl *redLel = lel;
+ if ( redLel->child != 0 ) {
+ int r = Parser_fssProdLengths[redLel->reduction] - 1;
+ struct Parser_LangEl *rhsEl = redLel->child;
+ while ( rhsEl != 0 ) {
+ rhs[r--] = rhsEl;
+ rhsEl = rhsEl->next;
+ }
+ }
+switch ( lel->reduction ) {
+case 1: {
+#line 46 "xmlparse.kl"
+
+ /* If we get no input the assumption is that the frontend died and
+ * emitted an error. */
+ errCount += 1;
+
+
+#line 2297 "xmlparse.cpp"
+} break;
+case 3: {
+#line 55 "xmlparse.kl"
+
+ Attribute *fileNameAttr = (&rhs[0]->user.token)->tag->findAttr( "filename" );
+ if ( fileNameAttr == 0 ) {
+ error((&rhs[0]->user.token)->loc) << "tag <ragel> requires a filename attribute" << endl;
+ exit(1);
+ }
+ else {
+ sourceFileName = fileNameAttr->value;
+
+ Attribute *langAttr = (&rhs[0]->user.token)->tag->findAttr( "lang" );
+ if ( langAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <ragel> requires a lang attribute" << endl;
+ else {
+ if ( strcmp( langAttr->value, "C" ) == 0 ) {
+ hostLangType = CCode;
+ hostLang = &hostLangC;
+ }
+ else if ( strcmp( langAttr->value, "D" ) == 0 ) {
+ hostLangType = DCode;
+ hostLang = &hostLangD;
+ }
+ else if ( strcmp( langAttr->value, "Java" ) == 0 ) {
+ hostLangType = JavaCode;
+ hostLang = &hostLangJava;
+ }
+ else if ( strcmp( langAttr->value, "Ruby" ) == 0 ) {
+ hostLangType = RubyCode;
+ hostLang = &hostLangRuby;
+ }
+ else {
+ error((&rhs[0]->user.token)->loc) << "expecting lang attribute to be "
+ "one of C, D, Java or Ruby" << endl;
+ }
+
+ outStream = openOutput( sourceFileName );
+ }
+ }
+
+
+#line 2340 "xmlparse.cpp"
+} break;
+case 10: {
+#line 105 "xmlparse.kl"
+
+ Attribute *lineAttr = (&rhs[0]->user.token)->tag->findAttr( "line" );
+ if ( lineAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <host> requires a line attribute" << endl;
+ else {
+ int line = atoi( lineAttr->value );
+ if ( outputActive )
+ lineDirective( *outStream, sourceFileName, line );
+ }
+
+ if ( outputActive )
+ *outStream << (&rhs[2]->user.token)->tag->content;
+
+
+#line 2358 "xmlparse.cpp"
+} break;
+case 11: {
+#line 121 "xmlparse.kl"
+
+ /* Do this before distributing transitions out to singles and defaults
+ * makes life easier. */
+ cgd->redFsm->maxKey = cgd->findMaxKey();
+
+ cgd->redFsm->assignActionLocs();
+
+ /* Find the first final state (The final state with the lowest id). */
+ cgd->redFsm->findFirstFinState();
+
+ /* Call the user's callback. */
+ cgd->finishRagelDef();
+
+
+#line 2376 "xmlparse.cpp"
+} break;
+case 12: {
+#line 136 "xmlparse.kl"
+
+ char *fsmName = 0;
+ Attribute *nameAttr = (&rhs[0]->user.token)->tag->findAttr( "name" );
+ if ( nameAttr != 0 ) {
+ fsmName = nameAttr->value;
+
+ CodeGenMapEl *mapEl = codeGenMap.find( fsmName );
+ if ( mapEl != 0 )
+ cgd = mapEl->value;
+ else {
+ cgd = makeCodeGen( sourceFileName, fsmName, *outStream, wantComplete );
+ codeGenMap.insert( fsmName, cgd );
+ }
+ }
+ else {
+ cgd = makeCodeGen( sourceFileName, fsmName,
+ *outStream, wantComplete );
+ }
+
+ ::keyOps = &cgd->thisKeyOps;
+
+
+#line 2402 "xmlparse.cpp"
+} break;
+case 24: {
+#line 174 "xmlparse.kl"
+
+ Attribute *nameAttr = (&rhs[0]->user.token)->tag->findAttr( "name" );
+ if ( nameAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <ex> requires a name attribute" << endl;
+ else {
+ char *td = (&rhs[2]->user.token)->tag->content;
+ Key exportKey = readKey( td, &td );
+ cgd->exportList.append( new Export( nameAttr->value, exportKey ) );
+ }
+
+
+#line 2417 "xmlparse.cpp"
+} break;
+case 25: {
+#line 186 "xmlparse.kl"
+
+ if ( ! cgd->setAlphType( (&rhs[2]->user.token)->tag->content ) )
+ error((&rhs[0]->user.token)->loc) << "tag <alphtype> specifies unknown alphabet type" << endl;
+
+
+#line 2426 "xmlparse.cpp"
+} break;
+case 26: {
+#line 192 "xmlparse.kl"
+
+ cgd->getKeyExpr = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 2434 "xmlparse.cpp"
+} break;
+case 27: {
+#line 197 "xmlparse.kl"
+
+ cgd->accessExpr = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 2442 "xmlparse.cpp"
+} break;
+case 28: {
+#line 202 "xmlparse.kl"
+
+ cgd->curStateExpr = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 2450 "xmlparse.cpp"
+} break;
+case 29: {
+#line 207 "xmlparse.kl"
+
+ /* Terminate the options list and call the write statement handler. */
+ writeOptions.append(0);
+ cgd->writeStatement( (&rhs[0]->user.tag_write_head)->loc, writeOptions.length()-1, writeOptions.data );
+
+ /* CodeGenData may have issued an error. */
+ errCount += cgd->codeGenErrCount;
+
+ /* Clear the options in prep for the next write statement. */
+ writeOptions.empty();
+
+
+#line 2466 "xmlparse.cpp"
+} break;
+case 30: {
+#line 225 "xmlparse.kl"
+
+ Attribute *nameAttr = (&rhs[0]->user.token)->tag->findAttr( "def_name" );
+ Attribute *lineAttr = (&rhs[0]->user.token)->tag->findAttr( "line" );
+ Attribute *colAttr = (&rhs[0]->user.token)->tag->findAttr( "col" );
+
+ if ( nameAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <write> requires a def_name attribute" << endl;
+ if ( lineAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <write> requires a line attribute" << endl;
+ if ( colAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <write> requires a col attribute" << endl;
+
+ if ( nameAttr != 0 && lineAttr != 0 && colAttr != 0 ) {
+ CodeGenMapEl *mapEl = codeGenMap.find( nameAttr->value );
+ if ( mapEl == 0 )
+ error((&rhs[0]->user.token)->loc) << "internal error: cannot find codeGen" << endl;
+ else {
+ cgd = mapEl->value;
+ ::keyOps = &cgd->thisKeyOps;
+ }
+
+ (&redLel->user.tag_write_head)->loc.line = atoi(lineAttr->value);
+ (&redLel->user.tag_write_head)->loc.col = atoi(colAttr->value);
+ }
+
+
+#line 2496 "xmlparse.cpp"
+} break;
+case 33: {
+#line 261 "xmlparse.kl"
+
+ writeOptions.append( (&rhs[2]->user.token)->tag->content );
+
+
+#line 2504 "xmlparse.cpp"
+} break;
+case 34: {
+#line 266 "xmlparse.kl"
+
+ cgd->closeMachine();
+
+
+#line 2512 "xmlparse.cpp"
+} break;
+case 35: {
+#line 271 "xmlparse.kl"
+
+ cgd->createMachine();
+
+
+#line 2520 "xmlparse.cpp"
+} break;
+case 45: {
+#line 291 "xmlparse.kl"
+
+ unsigned long startState = strtoul( (&rhs[2]->user.token)->tag->content, 0, 10 );
+ cgd->setStartState( startState );
+
+
+#line 2529 "xmlparse.cpp"
+} break;
+case 46: {
+#line 297 "xmlparse.kl"
+
+ unsigned long errorState = strtoul( (&rhs[2]->user.token)->tag->content, 0, 10 );
+ cgd->setErrorState( errorState );
+
+
+#line 2538 "xmlparse.cpp"
+} break;
+case 47: {
+#line 303 "xmlparse.kl"
+
+ Attribute *errorAttr = (&rhs[0]->user.token)->tag->findAttr( "error" );
+ if ( errorAttr != 0 )
+ cgd->setForcedErrorState();
+
+
+#line 2548 "xmlparse.cpp"
+} break;
+case 50: {
+#line 313 "xmlparse.kl"
+
+ Attribute *nameAttr = (&rhs[0]->user.token)->tag->findAttr( "name" );
+ if ( nameAttr == 0 ) {
+ error((&rhs[0]->user.token)->loc) << "tag <entry_points>::<entry> "
+ "requires a name attribute" << endl;
+ }
+ else {
+ char *data = (&rhs[2]->user.token)->tag->content;
+ unsigned long entry = strtoul( data, &data, 10 );
+ cgd->addEntryPoint( nameAttr->value, entry );
+ }
+
+
+#line 2565 "xmlparse.cpp"
+} break;
+case 52: {
+#line 329 "xmlparse.kl"
+
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "length" );
+ if ( lengthAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <state_list> requires a length attribute" << endl;
+ else {
+ unsigned long length = strtoul( lengthAttr->value, 0, 10 );
+ cgd->initStateList( length );
+ curState = 0;
+ }
+
+
+#line 2580 "xmlparse.cpp"
+} break;
+case 55: {
+#line 344 "xmlparse.kl"
+
+ Attribute *idAttr = (&rhs[0]->user.token)->tag->findAttr( "id" );
+ if ( idAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <state> requires an id attribute" << endl;
+ else {
+ int id = atoi( idAttr->value );
+ cgd->setId( curState, id );
+ }
+
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "final" );
+ if ( lengthAttr != 0 )
+ cgd->setFinal( curState );
+ curState += 1;
+
+
+#line 2599 "xmlparse.cpp"
+} break;
+case 61: {
+#line 367 "xmlparse.kl"
+
+ char *ad = (&rhs[2]->user.token)->tag->content;
+
+ long toStateAction = readOffsetPtr( ad, &ad );
+ long fromStateAction = readOffsetPtr( ad, &ad );
+ long eofAction = readOffsetPtr( ad, &ad );
+
+ cgd->setStateActions( curState, toStateAction,
+ fromStateAction, eofAction );
+
+
+#line 2614 "xmlparse.cpp"
+} break;
+case 63: {
+#line 381 "xmlparse.kl"
+
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "length" );
+ if ( lengthAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <cond_list> requires a length attribute" << endl;
+ else {
+ ulong length = readLength( lengthAttr->value );
+ cgd->initStateCondList( curState, length );
+ curStateCond = 0;
+ }
+
+
+#line 2629 "xmlparse.cpp"
+} break;
+case 66: {
+#line 396 "xmlparse.kl"
+
+ char *td = (&rhs[2]->user.token)->tag->content;
+ Key lowKey = readKey( td, &td );
+ Key highKey = readKey( td, &td );
+ long condId = readOffsetPtr( td, &td );
+ cgd->addStateCond( curState, lowKey, highKey, condId );
+
+
+#line 2641 "xmlparse.cpp"
+} break;
+case 67: {
+#line 405 "xmlparse.kl"
+
+ cgd->finishTransList( curState );
+
+
+#line 2649 "xmlparse.cpp"
+} break;
+case 68: {
+#line 410 "xmlparse.kl"
+
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "length" );
+ if ( lengthAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <trans_list> requires a length attribute" << endl;
+ else {
+ unsigned long length = strtoul( lengthAttr->value, 0, 10 );
+ cgd->initTransList( curState, length );
+ curTrans = 0;
+ }
+
+
+#line 2664 "xmlparse.cpp"
+} break;
+case 71: {
+#line 425 "xmlparse.kl"
+
+ char *td = (&rhs[2]->user.token)->tag->content;
+ Key lowKey = readKey( td, &td );
+ Key highKey = readKey( td, &td );
+ long targ = readOffsetPtr( td, &td );
+ long action = readOffsetPtr( td, &td );
+
+ cgd->newTrans( curState, curTrans++, lowKey, highKey, targ, action );
+
+
+#line 2678 "xmlparse.cpp"
+} break;
+case 73: {
+#line 442 "xmlparse.kl"
+
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "length" );
+ if ( lengthAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <action_list> requires a length attribute" << endl;
+ else {
+ unsigned long length = strtoul( lengthAttr->value, 0, 10 );
+ cgd->initActionList( length );
+ curAction = 0;
+ }
+
+
+#line 2693 "xmlparse.cpp"
+} break;
+case 76: {
+#line 461 "xmlparse.kl"
+
+ Attribute *lineAttr = (&rhs[0]->user.token)->tag->findAttr( "line" );
+ Attribute *colAttr = (&rhs[0]->user.token)->tag->findAttr( "col" );
+ Attribute *nameAttr = (&rhs[0]->user.token)->tag->findAttr( "name" );
+ if ( lineAttr == 0 || colAttr == 0)
+ error((&rhs[0]->user.token)->loc) << "tag <action> requires a line and col attributes" << endl;
+ else {
+ unsigned long line = strtoul( lineAttr->value, 0, 10 );
+ unsigned long col = strtoul( colAttr->value, 0, 10 );
+
+ char *name = 0;
+ if ( nameAttr != 0 )
+ name = nameAttr->value;
+
+ cgd->newAction( curAction++, name, line, col, (&rhs[1]->user.inline_list)->inlineList );
+ }
+
+
+#line 2715 "xmlparse.cpp"
+} break;
+case 77: {
+#line 486 "xmlparse.kl"
+
+ /* Append the item to the list, return the list. */
+ (&rhs[0]->user.inline_list)->inlineList->append( (&rhs[1]->user.inline_item_type)->inlineItem );
+ (&redLel->user.inline_list)->inlineList = (&rhs[0]->user.inline_list)->inlineList;
+
+
+#line 2725 "xmlparse.cpp"
+} break;
+case 78: {
+#line 493 "xmlparse.kl"
+
+ /* Start with empty list. */
+ (&redLel->user.inline_list)->inlineList = new InlineList;
+
+
+#line 2734 "xmlparse.cpp"
+} break;
+case 79: {
+#line 505 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2740 "xmlparse.cpp"
+} break;
+case 80: {
+#line 506 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2746 "xmlparse.cpp"
+} break;
+case 81: {
+#line 507 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2752 "xmlparse.cpp"
+} break;
+case 82: {
+#line 508 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2758 "xmlparse.cpp"
+} break;
+case 83: {
+#line 509 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2764 "xmlparse.cpp"
+} break;
+case 84: {
+#line 510 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2770 "xmlparse.cpp"
+} break;
+case 85: {
+#line 511 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2776 "xmlparse.cpp"
+} break;
+case 86: {
+#line 512 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2782 "xmlparse.cpp"
+} break;
+case 87: {
+#line 513 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2788 "xmlparse.cpp"
+} break;
+case 88: {
+#line 514 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2794 "xmlparse.cpp"
+} break;
+case 89: {
+#line 515 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2800 "xmlparse.cpp"
+} break;
+case 90: {
+#line 516 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2806 "xmlparse.cpp"
+} break;
+case 91: {
+#line 517 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2812 "xmlparse.cpp"
+} break;
+case 92: {
+#line 518 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2818 "xmlparse.cpp"
+} break;
+case 93: {
+#line 519 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2824 "xmlparse.cpp"
+} break;
+case 94: {
+#line 520 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2830 "xmlparse.cpp"
+} break;
+case 95: {
+#line 521 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2836 "xmlparse.cpp"
+} break;
+case 96: {
+#line 522 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2842 "xmlparse.cpp"
+} break;
+case 97: {
+#line 523 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2848 "xmlparse.cpp"
+} break;
+case 98: {
+#line 524 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2854 "xmlparse.cpp"
+} break;
+case 99: {
+#line 525 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2860 "xmlparse.cpp"
+} break;
+case 100: {
+#line 526 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2866 "xmlparse.cpp"
+} break;
+case 101: {
+#line 527 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2872 "xmlparse.cpp"
+} break;
+case 102: {
+#line 528 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2878 "xmlparse.cpp"
+} break;
+case 103: {
+#line 529 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2884 "xmlparse.cpp"
+} break;
+case 104: {
+#line 530 "xmlparse.kl"
+ (&redLel->user.inline_item_type)->inlineItem = (&rhs[0]->user.inline_item_type)->inlineItem;
+
+#line 2890 "xmlparse.cpp"
+} break;
+case 105: {
+#line 560 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Text );
+ (&redLel->user.inline_item_type)->inlineItem->data = (&rhs[2]->user.token)->tag->content;
+
+
+#line 2899 "xmlparse.cpp"
+} break;
+case 106: {
+#line 566 "xmlparse.kl"
+
+ int targ = strtol( (&rhs[2]->user.token)->tag->content, 0, 10 );
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Goto );
+ (&redLel->user.inline_item_type)->inlineItem->targId = targ;
+
+
+#line 2909 "xmlparse.cpp"
+} break;
+case 107: {
+#line 573 "xmlparse.kl"
+
+ int targ = strtol( (&rhs[2]->user.token)->tag->content, 0, 10 );
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Call );
+ (&redLel->user.inline_item_type)->inlineItem->targId = targ;
+
+
+#line 2919 "xmlparse.cpp"
+} break;
+case 108: {
+#line 580 "xmlparse.kl"
+
+ int targ = strtol( (&rhs[2]->user.token)->tag->content, 0, 10 );
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Next );
+ (&redLel->user.inline_item_type)->inlineItem->targId = targ;
+
+
+#line 2929 "xmlparse.cpp"
+} break;
+case 109: {
+#line 587 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::GotoExpr );
+ (&redLel->user.inline_item_type)->inlineItem->children = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 2938 "xmlparse.cpp"
+} break;
+case 110: {
+#line 593 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::CallExpr );
+ (&redLel->user.inline_item_type)->inlineItem->children = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 2947 "xmlparse.cpp"
+} break;
+case 111: {
+#line 599 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::NextExpr );
+ (&redLel->user.inline_item_type)->inlineItem->children = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 2956 "xmlparse.cpp"
+} break;
+case 112: {
+#line 605 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Ret );
+
+
+#line 2964 "xmlparse.cpp"
+} break;
+case 113: {
+#line 610 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Break );
+
+
+#line 2972 "xmlparse.cpp"
+} break;
+case 114: {
+#line 615 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::PChar );
+
+
+#line 2980 "xmlparse.cpp"
+} break;
+case 115: {
+#line 620 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Char );
+
+
+#line 2988 "xmlparse.cpp"
+} break;
+case 116: {
+#line 625 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Hold );
+
+
+#line 2996 "xmlparse.cpp"
+} break;
+case 117: {
+#line 630 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Exec );
+ (&redLel->user.inline_item_type)->inlineItem->children = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 3005 "xmlparse.cpp"
+} break;
+case 118: {
+#line 636 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::HoldTE );
+
+
+#line 3013 "xmlparse.cpp"
+} break;
+case 119: {
+#line 641 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::ExecTE );
+ (&redLel->user.inline_item_type)->inlineItem->children = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 3022 "xmlparse.cpp"
+} break;
+case 120: {
+#line 647 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Curs );
+
+
+#line 3030 "xmlparse.cpp"
+} break;
+case 121: {
+#line 652 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Targs );
+
+
+#line 3038 "xmlparse.cpp"
+} break;
+case 122: {
+#line 657 "xmlparse.kl"
+
+ int targ = strtol( (&rhs[2]->user.token)->tag->content, 0, 10 );
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::Entry );
+ (&redLel->user.inline_item_type)->inlineItem->targId = targ;
+
+
+#line 3048 "xmlparse.cpp"
+} break;
+case 123: {
+#line 664 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::LmInitTokStart );
+
+
+#line 3056 "xmlparse.cpp"
+} break;
+case 124: {
+#line 669 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::LmInitAct );
+
+
+#line 3064 "xmlparse.cpp"
+} break;
+case 125: {
+#line 674 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::LmGetTokEnd );
+
+
+#line 3072 "xmlparse.cpp"
+} break;
+case 126: {
+#line 679 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::LmSetTokStart );
+ cgd->hasLongestMatch = true;
+
+
+#line 3081 "xmlparse.cpp"
+} break;
+case 127: {
+#line 685 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::LmSetTokEnd );
+ (&redLel->user.inline_item_type)->inlineItem->offset = strtol( (&rhs[2]->user.token)->tag->content, 0, 10 );
+
+
+#line 3090 "xmlparse.cpp"
+} break;
+case 128: {
+#line 691 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::LmSetActId );
+ (&redLel->user.inline_item_type)->inlineItem->lmId = strtol( (&rhs[2]->user.token)->tag->content, 0, 10 );
+
+
+#line 3099 "xmlparse.cpp"
+} break;
+case 129: {
+#line 697 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::SubAction );
+ (&redLel->user.inline_item_type)->inlineItem->children = (&rhs[1]->user.inline_list)->inlineList;
+
+
+#line 3108 "xmlparse.cpp"
+} break;
+case 130: {
+#line 704 "xmlparse.kl"
+
+ bool handlesError = false;
+ Attribute *handlesErrorAttr = (&rhs[0]->user.token)->tag->findAttr( "handles_error" );
+ if ( handlesErrorAttr != 0 )
+ handlesError = true;
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::LmSwitch );
+ (&redLel->user.inline_item_type)->inlineItem->children = (&rhs[1]->user.lm_action_list)->inlineList;
+ (&redLel->user.inline_item_type)->inlineItem->handlesError = handlesError;
+
+
+#line 3123 "xmlparse.cpp"
+} break;
+case 131: {
+#line 721 "xmlparse.kl"
+
+ (&redLel->user.lm_action_list)->inlineList = (&rhs[0]->user.lm_action_list)->inlineList;
+ (&redLel->user.lm_action_list)->inlineList->append( (&rhs[1]->user.inline_item_type)->inlineItem );
+
+
+#line 3132 "xmlparse.cpp"
+} break;
+case 132: {
+#line 726 "xmlparse.kl"
+
+ (&redLel->user.lm_action_list)->inlineList = new InlineList;
+
+
+#line 3140 "xmlparse.cpp"
+} break;
+case 133: {
+#line 733 "xmlparse.kl"
+
+ (&redLel->user.inline_item_type)->inlineItem = new InlineItem( InputLoc(), InlineItem::SubAction );
+ (&redLel->user.inline_item_type)->inlineItem->children = (&rhs[1]->user.inline_list)->inlineList;
+
+ Attribute *idAttr = (&rhs[0]->user.token)->tag->findAttr( "id" );
+ if ( idAttr != 0 ) {
+ unsigned long id = strtoul( idAttr->value, 0, 10 );
+ (&redLel->user.inline_item_type)->inlineItem->lmId = id;
+ }
+
+
+#line 3155 "xmlparse.cpp"
+} break;
+case 135: {
+#line 752 "xmlparse.kl"
+
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "length" );
+ if ( lengthAttr == 0 ) {
+ error((&rhs[0]->user.token)->loc) << "tag <action_table_list> requires "
+ "a length attribute" << endl;
+ }
+ else {
+ unsigned long length = strtoul( lengthAttr->value, 0, 10 );
+ cgd->initActionTableList( length );
+ curActionTable = 0;
+ }
+
+
+#line 3172 "xmlparse.cpp"
+} break;
+case 138: {
+#line 769 "xmlparse.kl"
+
+ /* Find the length of the action table. */
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "length" );
+ if ( lengthAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <at> requires a length attribute" << endl;
+ else {
+ unsigned long length = strtoul( lengthAttr->value, 0, 10 );
+
+ /* Collect the action table. */
+ RedAction *redAct = cgd->allActionTables + curActionTable;
+ redAct->actListId = curActionTable;
+ redAct->key.setAsNew( length );
+ char *ptr = (&rhs[2]->user.token)->tag->content;
+ int pos = 0;
+ while ( *ptr != 0 ) {
+ unsigned long actionId = strtoul( ptr, &ptr, 10 );
+ redAct->key[pos].key = 0;
+ redAct->key[pos].value = cgd->allActions+actionId;
+ pos += 1;
+ }
+
+ /* Insert into the action table map. */
+ cgd->redFsm->actionMap.insert( redAct );
+ }
+
+ curActionTable += 1;
+
+
+#line 3204 "xmlparse.cpp"
+} break;
+case 140: {
+#line 804 "xmlparse.kl"
+
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "length" );
+ if ( lengthAttr == 0 ) {
+ error((&rhs[0]->user.token)->loc) << "tag <cond_space_list> "
+ "requires a length attribute" << endl;
+ }
+ else {
+ ulong length = readLength( lengthAttr->value );
+ cgd->initCondSpaceList( length );
+ curCondSpace = 0;
+ }
+
+
+#line 3221 "xmlparse.cpp"
+} break;
+case 143: {
+#line 821 "xmlparse.kl"
+
+ Attribute *lengthAttr = (&rhs[0]->user.token)->tag->findAttr( "length" );
+ Attribute *idAttr = (&rhs[0]->user.token)->tag->findAttr( "id" );
+ if ( lengthAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <cond_space> requires a length attribute" << endl;
+ else {
+ if ( lengthAttr == 0 )
+ error((&rhs[0]->user.token)->loc) << "tag <cond_space> requires an id attribute" << endl;
+ else {
+ unsigned long condSpaceId = strtoul( idAttr->value, 0, 10 );
+ ulong length = readLength( lengthAttr->value );
+
+ char *td = (&rhs[2]->user.token)->tag->content;
+ Key baseKey = readKey( td, &td );
+
+ cgd->newCondSpace( curCondSpace, condSpaceId, baseKey );
+ for ( ulong a = 0; a < length; a++ ) {
+ long actionOffset = readOffsetPtr( td, &td );
+ cgd->condSpaceItem( curCondSpace, actionOffset );
+ }
+ curCondSpace += 1;
+ }
+ }
+
+
+#line 3250 "xmlparse.cpp"
+} break;
+}
+ }
+}
+
+ if ( lel->child != 0 ) {
+ struct Parser_LangEl *first = lel->child;
+ struct Parser_LangEl *child = lel->child;
+ numNodes -= 1;
+ lel->child = 0;
+ while ( child->next != 0 ) {
+ child = child->next;
+ numNodes -= 1;
+ }
+ child->next = pool;
+ pool = first;
+ }
+ }
+
+hit_final:
+ if ( sp > 0 ) {
+ /* Figure out which place to return to. */
+ if ( cmStack[sp-1]->next == lel ) {
+ lel = cmStack[--sp];
+ goto final_reverse;
+ }
+ else {
+ lel = cmStack[--sp];
+ goto final_upwards;
+ }
+ }
+
+ lastFinal = lel;
+ free( cmStack );
+ }
+ }
+ }
+
+ if ( *action & 0x2 ) {
+ int fssRed = *action >> 2;
+ int reduction = Parser_fssProdIdIndex[fssRed];
+ struct Parser_LangEl *redLel;
+ if ( pool == 0 ) {
+ if ( freshPos == 8128 ) {
+ freshEl = (struct Parser_LangEl*) malloc(
+ sizeof(struct Parser_LangEl)*8128);
+ #ifdef LOG_ACTIONS
+ cerr << "allocating 8128 LangEls" << endl;
+ #endif
+ freshPos = 0;
+ }
+ redLel = freshEl + freshPos++;
+ }
+ else {
+ redLel = pool;
+ pool = pool->next;
+ }
+ numNodes += 1;
+ redLel->type = Parser_prodLhsIds[reduction];
+ redLel->reduction = reduction;
+ redLel->child = 0;
+ redLel->next = 0;
+ redLel->retry = (lel->retry << 16);
+ lel->retry &= 0xffff0000;
+
+ rhsLen = Parser_fssProdLengths[fssRed];
+ if ( rhsLen > 0 ) {
+ int r;
+ for ( r = rhsLen-1; r > 0; r-- ) {
+ rhs[r] = stackTop;
+ stackTop = stackTop->next;
+ }
+ rhs[0] = stackTop;
+ stackTop = stackTop->next;
+ rhs[0]->next = 0;
+ }
+ #ifdef LOG_ACTIONS
+ cerr << "reduced: "
+ << Parser_prodNames[reduction]
+ << " rhsLen: " << rhsLen;
+ #endif
+ if ( action[1] == 0 )
+ redLel->retry = 0;
+ else {
+ redLel->retry += 0x10000;
+ numRetry += 1;
+ #ifdef LOG_ACTIONS
+ cerr << " retry: " << redLel;
+ #endif
+ }
+
+ #ifdef LOG_ACTIONS
+ cerr << endl;
+ #endif
+
+ if ( rhsLen == 0 ) {
+ redLel->file = lel->file;
+ redLel->line = lel->line;
+ targState = curs;
+ }
+ else {
+ redLel->child = rhs[rhsLen-1];
+ redLel->file = rhs[0]->file;
+ redLel->line = rhs[0]->line;
+ targState = rhs[0]->state;
+ }
+
+ if ( induceReject ) {
+ #ifdef LOG_ACTIONS
+ cerr << "error induced during reduction of " <<
+ Parser_lelNames[redLel->type] << endl;
+ #endif
+ redLel->state = curs;
+ redLel->next = stackTop;
+ stackTop = redLel;
+ curs = targState;
+ goto parseError;
+ }
+ else {
+ redLel->next = input;
+ input = redLel;
+ }
+ }
+
+
+ curs = targState;
+ goto again;
+
+parseError:
+ #ifdef LOG_BACKTRACK
+ cerr << "hit error" << endl;
+ #endif
+ if ( numRetry > 0 ) {
+ while ( 1 ) {
+ struct Parser_LangEl *redLel = stackTop;
+ if ( stackTop->type < 186 ) {
+ #ifdef LOG_BACKTRACK
+ cerr << "backing up over terminal: " <<
+ Parser_lelNames[stackTop->type] << endl;
+ #endif
+ stackTop = stackTop->next;
+ redLel->next = input;
+ input = redLel;
+ }
+ else {
+ #ifdef LOG_BACKTRACK
+ cerr << "backing up over non-terminal: " <<
+ Parser_lelNames[stackTop->type] << endl;
+ #endif
+ stackTop = stackTop->next;
+ struct Parser_LangEl *first = redLel->child;
+ if ( first == 0 )
+ rhsLen = 0;
+ else {
+ rhsLen = 1;
+ while ( first->next != 0 ) {
+ first = first->next;
+ rhsLen += 1;
+ }
+ first->next = stackTop;
+ stackTop = redLel->child;
+
+ struct Parser_LangEl *rhsEl = stackTop;
+ int p = rhsLen;
+ while ( p > 0 ) {
+ rhs[--p] = rhsEl;
+ rhsEl = rhsEl->next;
+ }
+ }
+ redLel->next = pool;
+ pool = redLel;
+ numNodes -= 1;
+ }
+
+ if ( redLel->retry > 0 ) {
+ #ifdef LOG_BACKTRACK
+ cerr << "found retry targ: " << redLel << endl;
+ #endif
+ numRetry -= 1;
+ #ifdef LOG_BACKTRACK
+ cerr << "found retry: " << redLel << endl;
+ #endif
+ if ( redLel->retry & 0x0000ffff )
+ curs = input->state;
+ else {
+ input->retry = redLel->retry >> 16;
+ if ( stackTop->state < 0 )
+ curs = Parser_startState;
+ else {
+ curs = Parser_targs[(int)Parser_indicies[Parser_offsets[stackTop->state] + (stackTop->type - Parser_keys[stackTop->state<<1])]];
+ }
+ }
+ goto again;
+ }
+ }
+ }
+ curs = -1;
+ errCount += 1;
+_out: {}
+#line 861 "xmlparse.kl"
+ return errCount == 0 ? 0 : -1;
+}
+
+
+unsigned long readLength( char *td )
+{
+ return strtoul( td, 0, 10 );
+}
+
+Key readKey( char *td, char **end )
+{
+ if ( keyOps->isSigned )
+ return Key( strtol( td, end, 10 ) );
+ else
+ return Key( strtoul( td, end, 10 ) );
+}
+
+long readOffsetPtr( char *td, char **end )
+{
+ while ( *td == ' ' || *td == '\t' )
+ td++;
+
+ if ( *td == 'x' ) {
+ if ( end != 0 )
+ *end = td + 1;
+ return -1;
+ }
+
+ return strtol( td, end, 10 );
+}
+
+ostream &Parser::warning( const InputLoc &loc )
+{
+ cerr << fileName << ":" << loc.line << ":" << loc.col << ": warning: ";
+ return cerr;
+}
+
+ostream &Parser::error( const InputLoc &loc )
+{
+ errCount += 1;
+ assert( fileName != 0 );
+ cerr << fileName << ":" << loc.line << ":" << loc.col << ": ";
+ return cerr;
+}
+
+
+ostream &Parser::parser_error( int tokId, Token &token )
+{
+ errCount += 1;
+ assert( fileName != 0 );
+ cerr << fileName << ":" << token.loc.line << ":" << token.loc.col;
+ if ( token.tag != 0 ) {
+ if ( token.tag->tagId == 0 )
+ cerr << ": at unknown tag";
+ else
+ cerr << ": at tag <" << token.tag->tagId->name << ">";
+ }
+ cerr << ": ";
+
+ return cerr;
+}
+
+int Parser::token( int tokenId, Token &tok )
+{
+ int res = parseLangEl( tokenId, &tok );
+ if ( res < 0 ) {
+ parser_error( tokenId, tok ) << "parse error" << endl;
+ exit(1);
+ }
+ return res;
+}
+
+int Parser::token( int tokenId, int col, int line )
+{
+ Token tok;
+ tok.loc.col = col;
+ tok.loc.line = line;
+ tok.tag = 0;
+ return token( tokenId, tok );
+}
+
+int Parser::token( XMLTag *tag, int col, int line )
+{
+ Token tok;
+ tok.loc.col = col;
+ tok.loc.line = line;
+ tok.tag = tag;
+
+ if ( tag->type == XMLTag::Close ) {
+ int res = token( '/', tok );
+ if ( res < 0 )
+ return res;
+ }
+
+ tok.tag = tag;
+ return token( tag->tagId != 0 ? tag->tagId->id : TAG_unknown, tok );
+}
diff --git a/contrib/tools/ragel5/redfsm/xmlparse.h b/contrib/tools/ragel5/redfsm/xmlparse.h
new file mode 100644
index 0000000000..b51a7cd67a
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/xmlparse.h
@@ -0,0 +1,228 @@
+/* Automatically generated by Kelbt from "xmlparse.kh".
+ *
+ * Parts of this file are copied from Kelbt source covered by the GNU
+ * GPL. As a special exception, you may use the parts of this file copied
+ * from Kelbt source without restriction. The remainder is derived from
+ * "xmlparse.kh" and inherits the copyright status of that file.
+ */
+
+#line 1 "xmlparse.kh"
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _XMLPARSE_H
+#define _XMLPARSE_H
+
+#include "vector.h"
+#include "gendata.h"
+#include <iostream>
+
+using std::ostream;
+
+struct AttrMarker
+{
+ char *id;
+ int idLen;
+ char *value;
+ int valueLen;
+};
+
+struct Attribute
+{
+ char *id;
+ char *value;
+};
+
+typedef Vector<AttrMarker> AttrMkList;
+typedef Vector<Attribute> AttrList;
+struct XMLTagHashPair;
+
+struct XMLTag
+{
+ enum TagType { Open, Close };
+
+ XMLTag( XMLTagHashPair *tagId, TagType type ) :
+ tagId(tagId), type(type),
+ content(0), attrList(0) {}
+
+ Attribute *findAttr(const char *id )
+ {
+ if ( attrList != 0 ) {
+ for ( AttrList::Iter attr = *attrList; attr.lte(); attr++ ) {
+ if ( strcmp( id, attr->id ) == 0 )
+ return attr;
+ }
+ }
+ return 0;
+ }
+
+ XMLTagHashPair *tagId;
+ TagType type;
+
+ /* Content is associtated with closing tags. */
+ char *content;
+
+ /* Attribute lists are associated with opening tags. */
+ AttrList *attrList;
+};
+
+
+struct XMLTagHashPair
+{
+ const char *name;
+ int id;
+};
+
+struct Token
+{
+ XMLTag *tag;
+ InputLoc loc;
+};
+
+struct InlineItem;
+struct InlineList;
+
+struct LmSwitchVect;
+struct LmSwitchAction;
+
+struct Parser
+{
+ #line 117 "xmlparse.kh"
+
+
+ #line 111 "xmlparse.h"
+ struct Parser_LangEl *freshEl;
+ int freshPos;
+ struct Parser_LangEl *pool;
+ int numRetry;
+ int numNodes;
+ struct Parser_LangEl *stackTop;
+ struct Parser_LangEl *lastFinal;
+ int errCount;
+ int curs;
+#line 120 "xmlparse.kh"
+
+ void init();
+ int parseLangEl( int type, const Token *token );
+
+ Parser(const char *fileName, bool outputActive, bool wantComplete ) :
+ fileName(fileName), sourceFileName(0), outStream(0),
+ outputActive(outputActive), wantComplete(wantComplete),
+ cgd(0) { }
+
+ int token( int tokenId, Token &token );
+ int token( int tokenId, int col, int line );
+ int token( XMLTag *tag, int col, int line );
+
+ /* Report an error encountered by the parser. */
+ ostream &warning( const InputLoc &loc );
+ ostream &error();
+ ostream &error( const InputLoc &loc );
+ ostream &parser_error( int tokId, Token &token );
+
+ /* The name of the root section, this does not change during an include. */
+ const char *fileName;
+ char *sourceFileName;
+ ostream *outStream;
+ bool outputActive;
+ bool wantComplete;
+
+ /* Collected during parsing. */
+ char *attrKey;
+ char *attrValue;
+ int curAction;
+ int curActionTable;
+ int curTrans;
+ int curState;
+ int curCondSpace;
+ int curStateCond;
+
+ CodeGenData *cgd;
+ CodeGenMap codeGenMap;
+
+ Vector <char*> writeOptions;
+};
+
+#line 164 "xmlparse.h"
+#define TAG_unknown 128
+#define TAG_ragel 129
+#define TAG_ragel_def 130
+#define TAG_host 131
+#define TAG_state_list 132
+#define TAG_state 133
+#define TAG_trans_list 134
+#define TAG_t 135
+#define TAG_machine 136
+#define TAG_start_state 137
+#define TAG_error_state 138
+#define TAG_action_list 139
+#define TAG_action_table_list 140
+#define TAG_action 141
+#define TAG_action_table 142
+#define TAG_alphtype 143
+#define TAG_element 144
+#define TAG_getkey 145
+#define TAG_state_actions 146
+#define TAG_entry_points 147
+#define TAG_sub_action 148
+#define TAG_cond_space_list 149
+#define TAG_cond_space 150
+#define TAG_cond_list 151
+#define TAG_c 152
+#define TAG_exports 153
+#define TAG_ex 154
+#define TAG_text 155
+#define TAG_goto 156
+#define TAG_call 157
+#define TAG_next 158
+#define TAG_goto_expr 159
+#define TAG_call_expr 160
+#define TAG_next_expr 161
+#define TAG_ret 162
+#define TAG_pchar 163
+#define TAG_char 164
+#define TAG_hold 165
+#define TAG_exec 166
+#define TAG_holdte 167
+#define TAG_execte 168
+#define TAG_curs 169
+#define TAG_targs 170
+#define TAG_entry 171
+#define TAG_data 172
+#define TAG_lm_switch 173
+#define TAG_init_act 174
+#define TAG_set_act 175
+#define TAG_set_tokend 176
+#define TAG_get_tokend 177
+#define TAG_init_tokstart 178
+#define TAG_set_tokstart 179
+#define TAG_write 180
+#define TAG_curstate 181
+#define TAG_access 182
+#define TAG_break 183
+#define TAG_arg 184
+#define _eof 185
+
+#line 163 "xmlparse.kh"
+
+int xml_parse( std::istream &input, const char *fileName,
+ bool outputActive, bool wantComplete );
+
+#endif /* _XMLPARSE_H */
diff --git a/contrib/tools/ragel5/redfsm/xmlscan.cpp b/contrib/tools/ragel5/redfsm/xmlscan.cpp
new file mode 100644
index 0000000000..a3d979a0ff
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/xmlscan.cpp
@@ -0,0 +1,925 @@
+#line 1 "xmlscan.rl"
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <iostream>
+#include <string.h>
+#include "vector.h"
+#include "xmlparse.h"
+#include "buffer.h"
+
+using std::istream;
+using std::cout;
+using std::cerr;
+using std::endl;
+
+#define BUFSIZE 4096
+
+
+#line 37 "xmlscan.cpp"
+static const int Scanner_start = 20;
+
+static const int Scanner_first_final = 20;
+
+static const int Scanner_error = 0;
+
+#line 37 "xmlscan.rl"
+
+#include "phash.h"
+
+struct Scanner
+{
+ Scanner(const char *fileName, istream &input ) :
+ fileName(fileName),
+ input(input),
+ curline(1),
+ curcol(1),
+ p(0), pe(0),
+ done(false),
+ data(0), data_len(0),
+ value(0)
+ {
+
+#line 69 "xmlscan.cpp"
+ {
+ cs = Scanner_start;
+ tokstart = 0;
+ tokend = 0;
+ act = 0;
+ }
+#line 63 "xmlscan.rl"
+
+ }
+
+ int scan();
+ void adjustAttrPointers( int distance );
+ std::ostream &error();
+
+ const char *fileName;
+ istream &input;
+
+ /* Scanner State. */
+ int cs, act, have, curline, curcol;
+ char *tokstart, *tokend;
+ char *p, *pe;
+ int done;
+
+ /* Token data */
+ char *data;
+ int data_len;
+ int value;
+ AttrMkList attrMkList;
+ Buffer buffer;
+ char *tag_id_start;
+ int tag_id_len;
+ int token_col, token_line;
+
+ char buf[BUFSIZE];
+};
+
+
+#define TK_NO_TOKEN (-1)
+#define TK_ERR 1
+#define TK_SPACE 2
+#define TK_EOF 3
+#define TK_OpenTag 4
+#define TK_CloseTag 5
+
+#define ret_tok( _tok ) token = (_tok); data = tokstart
+
+void Scanner::adjustAttrPointers( int distance )
+{
+ for ( AttrMkList::Iter attr = attrMkList; attr.lte(); attr++ ) {
+ attr->id -= distance;
+ attr->value -= distance;
+ }
+}
+
+/* There is no claim that this is a proper XML parser, but it is good
+ * enough for our purposes. */
+#line 178 "xmlscan.rl"
+
+
+int Scanner::scan( )
+{
+ int token = TK_NO_TOKEN;
+ int space = 0, readlen = 0;
+ char *attr_id_start = 0;
+ char *attr_value_start = 0;
+ int attr_id_len = 0;
+ int attr_value_len = 0;
+
+ attrMkList.empty();
+ buffer.clear();
+
+ while ( 1 ) {
+ if ( p == pe ) {
+ //printf("scanner: need more data\n");
+
+ if ( tokstart == 0 )
+ have = 0;
+ else {
+ /* There is data that needs to be shifted over. */
+ //printf("scanner: buffer broken mid token\n");
+ have = pe - tokstart;
+ memmove( buf, tokstart, have );
+
+ int distance = tokstart - buf;
+ tokend -= distance;
+ tag_id_start -= distance;
+ attr_id_start -= distance;
+ attr_value_start -= distance;
+ adjustAttrPointers( distance );
+ tokstart = buf;
+ }
+
+ p = buf + have;
+ space = BUFSIZE - have;
+
+ if ( space == 0 ) {
+ /* We filled up the buffer trying to scan a token. */
+ return TK_SPACE;
+ }
+
+ if ( done ) {
+ //printf("scanner: end of file\n");
+ p[0] = 0;
+ readlen = 1;
+ }
+ else {
+ input.read( p, space );
+ readlen = input.gcount();
+ if ( input.eof() ) {
+ //printf("scanner: setting done flag\n");
+ done = 1;
+ }
+ }
+
+ pe = p + readlen;
+ }
+
+
+#line 188 "xmlscan.cpp"
+ {
+ if ( p == pe )
+ goto _out;
+ switch ( cs )
+ {
+tr6:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 168 "xmlscan.rl"
+ {tokend = p+1;{ buffer.append( '&' ); }{p = ((tokend))-1;}}
+ goto st20;
+tr8:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 172 "xmlscan.rl"
+ {tokend = p+1;{ buffer.append( '>' ); }{p = ((tokend))-1;}}
+ goto st20;
+tr10:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 170 "xmlscan.rl"
+ {tokend = p+1;{ buffer.append( '<' ); }{p = ((tokend))-1;}}
+ goto st20;
+tr20:
+#line 150 "xmlscan.rl"
+ { tag_id_len = p - tag_id_start; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 160 "xmlscan.rl"
+ {tokend = p+1;{ ret_tok( TK_CloseTag ); {{p = ((tokend))-1;}goto _out20;} }{p = ((tokend))-1;}}
+ goto st20;
+tr23:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 160 "xmlscan.rl"
+ {tokend = p+1;{ ret_tok( TK_CloseTag ); {{p = ((tokend))-1;}goto _out20;} }{p = ((tokend))-1;}}
+ goto st20;
+tr27:
+#line 150 "xmlscan.rl"
+ { tag_id_len = p - tag_id_start; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 157 "xmlscan.rl"
+ {tokend = p+1;{ ret_tok( TK_OpenTag ); {{p = ((tokend))-1;}goto _out20;} }{p = ((tokend))-1;}}
+ goto st20;
+tr30:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 157 "xmlscan.rl"
+ {tokend = p+1;{ ret_tok( TK_OpenTag ); {{p = ((tokend))-1;}goto _out20;} }{p = ((tokend))-1;}}
+ goto st20;
+tr46:
+#line 132 "xmlscan.rl"
+ {
+ attr_value_len = p - attr_value_start;
+
+ AttrMarker newAttr;
+ newAttr.id = attr_id_start;
+ newAttr.idLen = attr_id_len;
+ newAttr.value = attr_value_start;
+ newAttr.valueLen = attr_value_len;
+ attrMkList.append( newAttr );
+ }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 157 "xmlscan.rl"
+ {tokend = p+1;{ ret_tok( TK_OpenTag ); {{p = ((tokend))-1;}goto _out20;} }{p = ((tokend))-1;}}
+ goto st20;
+tr48:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 164 "xmlscan.rl"
+ {tokend = p+1;{ buffer.append( *p ); }{p = ((tokend))-1;}}
+ goto st20;
+tr49:
+#line 116 "xmlscan.rl"
+ { token_col = curcol; token_line = curline; }
+#line 175 "xmlscan.rl"
+ {tokend = p+1;{ ret_tok( TK_EOF ); {{p = ((tokend))-1;}goto _out20;} }{p = ((tokend))-1;}}
+ goto st20;
+tr50:
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+#line 164 "xmlscan.rl"
+ {tokend = p+1;{ buffer.append( *p ); }{p = ((tokend))-1;}}
+ goto st20;
+st20:
+#line 1 "xmlscan.rl"
+ {tokstart = 0;}
+ if ( ++p == pe )
+ goto _out20;
+case 20:
+#line 1 "xmlscan.rl"
+ {tokstart = p;}
+#line 285 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 0: goto tr49;
+ case 10: goto tr50;
+ case 38: goto tr51;
+ case 60: goto tr52;
+ }
+ goto tr48;
+tr51:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st1;
+st1:
+ if ( ++p == pe )
+ goto _out1;
+case 1:
+#line 301 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 97: goto tr0;
+ case 103: goto tr2;
+ case 108: goto tr3;
+ }
+ goto st0;
+st0:
+ goto _out0;
+tr0:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st2;
+st2:
+ if ( ++p == pe )
+ goto _out2;
+case 2:
+#line 318 "xmlscan.cpp"
+ if ( (*p) == 109 )
+ goto tr4;
+ goto st0;
+tr4:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st3;
+st3:
+ if ( ++p == pe )
+ goto _out3;
+case 3:
+#line 330 "xmlscan.cpp"
+ if ( (*p) == 112 )
+ goto tr5;
+ goto st0;
+tr5:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st4;
+st4:
+ if ( ++p == pe )
+ goto _out4;
+case 4:
+#line 342 "xmlscan.cpp"
+ if ( (*p) == 59 )
+ goto tr6;
+ goto st0;
+tr2:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st5;
+st5:
+ if ( ++p == pe )
+ goto _out5;
+case 5:
+#line 354 "xmlscan.cpp"
+ if ( (*p) == 116 )
+ goto tr7;
+ goto st0;
+tr7:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st6;
+st6:
+ if ( ++p == pe )
+ goto _out6;
+case 6:
+#line 366 "xmlscan.cpp"
+ if ( (*p) == 59 )
+ goto tr8;
+ goto st0;
+tr3:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st7;
+st7:
+ if ( ++p == pe )
+ goto _out7;
+case 7:
+#line 378 "xmlscan.cpp"
+ if ( (*p) == 116 )
+ goto tr9;
+ goto st0;
+tr9:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st8;
+st8:
+ if ( ++p == pe )
+ goto _out8;
+case 8:
+#line 390 "xmlscan.cpp"
+ if ( (*p) == 59 )
+ goto tr10;
+ goto st0;
+tr11:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st9;
+tr12:
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st9;
+tr52:
+#line 116 "xmlscan.rl"
+ { token_col = curcol; token_line = curline; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st9;
+st9:
+ if ( ++p == pe )
+ goto _out9;
+case 9:
+#line 414 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr11;
+ case 10: goto tr12;
+ case 13: goto tr11;
+ case 32: goto tr11;
+ case 47: goto tr13;
+ case 95: goto tr14;
+ }
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr14;
+ } else if ( (*p) >= 65 )
+ goto tr14;
+ goto st0;
+tr13:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st10;
+tr15:
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st10;
+st10:
+ if ( ++p == pe )
+ goto _out10;
+case 10:
+#line 443 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr13;
+ case 10: goto tr15;
+ case 13: goto tr13;
+ case 32: goto tr13;
+ case 95: goto tr16;
+ }
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr16;
+ } else if ( (*p) >= 65 )
+ goto tr16;
+ goto st0;
+tr19:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st11;
+tr16:
+#line 149 "xmlscan.rl"
+ { tag_id_start = p; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st11;
+st11:
+ if ( ++p == pe )
+ goto _out11;
+case 11:
+#line 471 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr17;
+ case 10: goto tr18;
+ case 13: goto tr17;
+ case 32: goto tr17;
+ case 62: goto tr20;
+ case 95: goto tr19;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr19;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr19;
+ } else
+ goto tr19;
+ goto st0;
+tr21:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st12;
+tr22:
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st12;
+tr17:
+#line 150 "xmlscan.rl"
+ { tag_id_len = p - tag_id_start; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st12;
+tr18:
+#line 150 "xmlscan.rl"
+ { tag_id_len = p - tag_id_start; }
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st12;
+st12:
+ if ( ++p == pe )
+ goto _out12;
+case 12:
+#line 517 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr21;
+ case 10: goto tr22;
+ case 13: goto tr21;
+ case 32: goto tr21;
+ case 62: goto tr23;
+ }
+ goto st0;
+tr26:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st13;
+tr14:
+#line 149 "xmlscan.rl"
+ { tag_id_start = p; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st13;
+st13:
+ if ( ++p == pe )
+ goto _out13;
+case 13:
+#line 540 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr24;
+ case 10: goto tr25;
+ case 13: goto tr24;
+ case 32: goto tr24;
+ case 62: goto tr27;
+ case 95: goto tr26;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr26;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr26;
+ } else
+ goto tr26;
+ goto st0;
+tr28:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st14;
+tr29:
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st14;
+tr24:
+#line 150 "xmlscan.rl"
+ { tag_id_len = p - tag_id_start; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st14;
+tr25:
+#line 150 "xmlscan.rl"
+ { tag_id_len = p - tag_id_start; }
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st14;
+tr44:
+#line 132 "xmlscan.rl"
+ {
+ attr_value_len = p - attr_value_start;
+
+ AttrMarker newAttr;
+ newAttr.id = attr_id_start;
+ newAttr.idLen = attr_id_len;
+ newAttr.value = attr_value_start;
+ newAttr.valueLen = attr_value_len;
+ attrMkList.append( newAttr );
+ }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st14;
+tr45:
+#line 132 "xmlscan.rl"
+ {
+ attr_value_len = p - attr_value_start;
+
+ AttrMarker newAttr;
+ newAttr.id = attr_id_start;
+ newAttr.idLen = attr_id_len;
+ newAttr.value = attr_value_start;
+ newAttr.valueLen = attr_value_len;
+ attrMkList.append( newAttr );
+ }
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st14;
+st14:
+ if ( ++p == pe )
+ goto _out14;
+case 14:
+#line 618 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr28;
+ case 10: goto tr29;
+ case 13: goto tr28;
+ case 32: goto tr28;
+ case 62: goto tr30;
+ case 95: goto tr31;
+ }
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr31;
+ } else if ( (*p) >= 65 )
+ goto tr31;
+ goto st0;
+tr34:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st15;
+tr31:
+#line 124 "xmlscan.rl"
+ { attr_id_start = p; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st15;
+tr47:
+#line 132 "xmlscan.rl"
+ {
+ attr_value_len = p - attr_value_start;
+
+ AttrMarker newAttr;
+ newAttr.id = attr_id_start;
+ newAttr.idLen = attr_id_len;
+ newAttr.value = attr_value_start;
+ newAttr.valueLen = attr_value_len;
+ attrMkList.append( newAttr );
+ }
+#line 124 "xmlscan.rl"
+ { attr_id_start = p; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st15;
+st15:
+ if ( ++p == pe )
+ goto _out15;
+case 15:
+#line 664 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr32;
+ case 10: goto tr33;
+ case 13: goto tr32;
+ case 32: goto tr32;
+ case 61: goto tr35;
+ case 95: goto tr34;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr34;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr34;
+ } else
+ goto tr34;
+ goto st0;
+tr36:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st16;
+tr37:
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st16;
+tr32:
+#line 125 "xmlscan.rl"
+ { attr_id_len = p - attr_id_start; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st16;
+tr33:
+#line 125 "xmlscan.rl"
+ { attr_id_len = p - attr_id_start; }
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st16;
+st16:
+ if ( ++p == pe )
+ goto _out16;
+case 16:
+#line 710 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr36;
+ case 10: goto tr37;
+ case 13: goto tr36;
+ case 32: goto tr36;
+ case 61: goto tr38;
+ }
+ goto st0;
+tr38:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st17;
+tr39:
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st17;
+tr35:
+#line 125 "xmlscan.rl"
+ { attr_id_len = p - attr_id_start; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st17;
+st17:
+ if ( ++p == pe )
+ goto _out17;
+case 17:
+#line 739 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr38;
+ case 10: goto tr39;
+ case 13: goto tr38;
+ case 32: goto tr38;
+ case 34: goto tr40;
+ }
+ goto st0;
+tr41:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st18;
+tr42:
+#line 117 "xmlscan.rl"
+ { curcol = 0; curline++; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st18;
+tr40:
+#line 130 "xmlscan.rl"
+ { attr_value_start = p; }
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st18;
+st18:
+ if ( ++p == pe )
+ goto _out18;
+case 18:
+#line 768 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 10: goto tr42;
+ case 34: goto tr43;
+ }
+ goto tr41;
+tr43:
+#line 115 "xmlscan.rl"
+ { curcol++; }
+ goto st19;
+st19:
+ if ( ++p == pe )
+ goto _out19;
+case 19:
+#line 782 "xmlscan.cpp"
+ switch( (*p) ) {
+ case 9: goto tr44;
+ case 10: goto tr45;
+ case 13: goto tr44;
+ case 32: goto tr44;
+ case 62: goto tr46;
+ case 95: goto tr47;
+ }
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr47;
+ } else if ( (*p) >= 65 )
+ goto tr47;
+ goto st0;
+ }
+ _out20: cs = 20; goto _out;
+ _out1: cs = 1; goto _out;
+ _out0: cs = 0; goto _out;
+ _out2: cs = 2; goto _out;
+ _out3: cs = 3; goto _out;
+ _out4: cs = 4; goto _out;
+ _out5: cs = 5; goto _out;
+ _out6: cs = 6; goto _out;
+ _out7: cs = 7; goto _out;
+ _out8: cs = 8; goto _out;
+ _out9: cs = 9; goto _out;
+ _out10: cs = 10; goto _out;
+ _out11: cs = 11; goto _out;
+ _out12: cs = 12; goto _out;
+ _out13: cs = 13; goto _out;
+ _out14: cs = 14; goto _out;
+ _out15: cs = 15; goto _out;
+ _out16: cs = 16; goto _out;
+ _out17: cs = 17; goto _out;
+ _out18: cs = 18; goto _out;
+ _out19: cs = 19; goto _out;
+
+ _out: {}
+ }
+#line 239 "xmlscan.rl"
+
+ if ( cs == Scanner_error )
+ return TK_ERR;
+
+ if ( token != TK_NO_TOKEN ) {
+ /* fbreak does not advance p, so we do it manually. */
+ p = p + 1;
+ data_len = p - data;
+ return token;
+ }
+ }
+}
+
+int xml_parse( std::istream &input, const char *fileName,
+ bool outputActive, bool wantComplete )
+{
+ Scanner scanner( fileName, input );
+ Parser parser( fileName, outputActive, wantComplete );
+
+ parser.init();
+
+ while ( 1 ) {
+ int token = scanner.scan();
+ if ( token == TK_NO_TOKEN ) {
+ cerr << "xmlscan: interal error: scanner returned NO_TOKEN" << endl;
+ exit(1);
+ }
+ else if ( token == TK_EOF ) {
+ parser.token( _eof, scanner.token_col, scanner.token_line );
+ break;
+ }
+ else if ( token == TK_ERR ) {
+ scanner.error() << "scanner error" << endl;
+ break;
+ }
+ else if ( token == TK_SPACE ) {
+ scanner.error() << "scanner is out of buffer space" << endl;
+ break;
+ }
+ else {
+ /* All other tokens are either open or close tags. */
+ XMLTagHashPair *tagId = Perfect_Hash::in_word_set(
+ scanner.tag_id_start, scanner.tag_id_len );
+
+ XMLTag *tag = new XMLTag( tagId, token == TK_OpenTag ?
+ XMLTag::Open : XMLTag::Close );
+
+ if ( tagId != 0 ) {
+ /* Get attributes for open tags. */
+ if ( token == TK_OpenTag && scanner.attrMkList.length() > 0 ) {
+ tag->attrList = new AttrList;
+ for ( AttrMkList::Iter attr = scanner.attrMkList;
+ attr.lte(); attr++ )
+ {
+ Attribute newAttr;
+ newAttr.id = new char[attr->idLen+1];
+ memcpy( newAttr.id, attr->id, attr->idLen );
+ newAttr.id[attr->idLen] = 0;
+
+ /* Exclude the surrounding quotes. */
+ newAttr.value = new char[attr->valueLen-1];
+ memcpy( newAttr.value, attr->value+1, attr->valueLen-2 );
+ newAttr.value[attr->valueLen-2] = 0;
+
+ tag->attrList->append( newAttr );
+ }
+ }
+
+ /* Get content for closing tags. */
+ if ( token == TK_CloseTag ) {
+ switch ( tagId->id ) {
+ case TAG_host: case TAG_arg:
+ case TAG_t: case TAG_alphtype:
+ case TAG_text: case TAG_goto:
+ case TAG_call: case TAG_next:
+ case TAG_entry: case TAG_set_tokend:
+ case TAG_set_act: case TAG_start_state:
+ case TAG_error_state: case TAG_state_actions:
+ case TAG_action_table: case TAG_cond_space:
+ case TAG_c: case TAG_ex:
+ tag->content = new char[scanner.buffer.length+1];
+ memcpy( tag->content, scanner.buffer.data,
+ scanner.buffer.length );
+ tag->content[scanner.buffer.length] = 0;
+ break;
+ }
+ }
+ }
+
+ #if 0
+ cerr << "parser_driver: " << (tag->type == XMLTag::Open ? "open" : "close") <<
+ ": " << (tag->tagId != 0 ? tag->tagId->name : "<unknown>") << endl;
+ if ( tag->attrList != 0 ) {
+ for ( AttrList::Iter attr = *tag->attrList; attr.lte(); attr++ )
+ cerr << " " << attr->id << ": " << attr->value << endl;
+ }
+ if ( tag->content != 0 )
+ cerr << " content: " << tag->content << endl;
+ #endif
+
+ parser.token( tag, scanner.token_col, scanner.token_line );
+ }
+ }
+
+ return 0;
+}
+
+std::ostream &Scanner::error()
+{
+ cerr << fileName << ":" << curline << ":" << curcol << ": ";
+ return cerr;
+}
diff --git a/contrib/tools/ragel5/redfsm/xmltags.cpp b/contrib/tools/ragel5/redfsm/xmltags.cpp
new file mode 100644
index 0000000000..5fbfabab1d
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/xmltags.cpp
@@ -0,0 +1,244 @@
+/* C++ code produced by gperf version 3.0.1 */
+/* Command-line: gperf -L C++ -t xmltags.gperf */
+/* Computed positions: -k'1,3' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+#line 23 "xmltags.gperf"
+
+#include <string.h>
+#include "xmlparse.h"
+#line 28 "xmltags.gperf"
+struct XMLTagHashPair;
+
+#define TOTAL_KEYWORDS 55
+#define MIN_WORD_LENGTH 1
+#define MAX_WORD_LENGTH 17
+#define MIN_HASH_VALUE 5
+#define MAX_HASH_VALUE 84
+/* maximum key range = 80, duplicates = 0 */
+
+#include "phash.h"
+
+inline unsigned int
+Perfect_Hash::hash (register const char *str, register unsigned int len)
+{
+ static const unsigned char asso_values[] =
+ {
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 20, 85, 5, 41, 35,
+ 5, 35, 85, 15, 10, 0, 85, 85, 40, 0,
+ 15, 85, 40, 85, 25, 0, 10, 85, 85, 0,
+ 56, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85
+ };
+ int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[2]];
+ /*FALLTHROUGH*/
+ case 2:
+ case 1:
+ hval += asso_values[(unsigned char)str[0]];
+ break;
+ }
+ return hval;
+}
+
+struct XMLTagHashPair *
+Perfect_Hash::in_word_set (register const char *str, register unsigned int len)
+{
+ static struct XMLTagHashPair wordlist[] =
+ {
+ {""}, {""}, {""}, {""}, {""},
+#line 74 "xmltags.gperf"
+ {"write", TAG_write},
+ {""}, {""},
+#line 68 "xmltags.gperf"
+ {"init_act", TAG_init_act},
+ {""},
+#line 34 "xmltags.gperf"
+ {"state", TAG_state},
+#line 36 "xmltags.gperf"
+ {"t", TAG_t},
+ {""},
+#line 72 "xmltags.gperf"
+ {"init_tokstart", TAG_init_tokstart},
+#line 32 "xmltags.gperf"
+ {"host", TAG_host},
+#line 33 "xmltags.gperf"
+ {"state_list", TAG_state_list},
+#line 38 "xmltags.gperf"
+ {"start_state", TAG_start_state},
+#line 69 "xmltags.gperf"
+ {"set_act", TAG_set_act},
+#line 46 "xmltags.gperf"
+ {"state_actions", TAG_state_actions},
+#line 65 "xmltags.gperf"
+ {"data", TAG_data},
+#line 71 "xmltags.gperf"
+ {"set_tokend", TAG_set_tokend},
+#line 41 "xmltags.gperf"
+ {"action", TAG_action},
+#line 73 "xmltags.gperf"
+ {"set_tokstart", TAG_set_tokstart},
+#line 78 "xmltags.gperf"
+ {"arg", TAG_arg},
+ {""},
+#line 35 "xmltags.gperf"
+ {"trans_list", TAG_trans_list},
+#line 40 "xmltags.gperf"
+ {"action_list", TAG_action_list},
+#line 43 "xmltags.gperf"
+ {"action_table", TAG_action_table},
+ {""},
+#line 49 "xmltags.gperf"
+ {"goto", TAG_goto},
+ {""},
+#line 45 "xmltags.gperf"
+ {"getkey", TAG_getkey},
+#line 42 "xmltags.gperf"
+ {"action_table_list", TAG_action_table_list},
+ {""},
+#line 52 "xmltags.gperf"
+ {"goto_expr", TAG_goto_expr},
+#line 70 "xmltags.gperf"
+ {"get_tokend", TAG_get_tokend},
+#line 82 "xmltags.gperf"
+ {"c", TAG_c},
+#line 84 "xmltags.gperf"
+ {"ex", TAG_ex},
+#line 55 "xmltags.gperf"
+ {"ret", TAG_ret},
+ {""},
+#line 63 "xmltags.gperf"
+ {"targs", TAG_targs},
+ {""},
+#line 37 "xmltags.gperf"
+ {"machine", TAG_machine},
+ {""},
+#line 57 "xmltags.gperf"
+ {"char", TAG_char},
+#line 30 "xmltags.gperf"
+ {"ragel", TAG_ragel},
+#line 76 "xmltags.gperf"
+ {"access", TAG_access},
+ {""}, {""},
+#line 31 "xmltags.gperf"
+ {"ragel_def", TAG_ragel_def},
+#line 64 "xmltags.gperf"
+ {"entry", TAG_entry},
+#line 67 "xmltags.gperf"
+ {"sub_action", TAG_sub_action},
+ {""},
+#line 44 "xmltags.gperf"
+ {"alphtype", TAG_alphtype},
+#line 58 "xmltags.gperf"
+ {"hold", TAG_hold},
+#line 56 "xmltags.gperf"
+ {"pchar", TAG_pchar},
+#line 60 "xmltags.gperf"
+ {"holdte", TAG_holdte},
+#line 47 "xmltags.gperf"
+ {"entry_points", TAG_entry_points},
+ {""},
+#line 81 "xmltags.gperf"
+ {"cond_list", TAG_cond_list},
+#line 80 "xmltags.gperf"
+ {"cond_space", TAG_cond_space},
+ {""}, {""}, {""},
+#line 62 "xmltags.gperf"
+ {"curs", TAG_curs},
+#line 79 "xmltags.gperf"
+ {"cond_space_list", TAG_cond_space_list},
+ {""}, {""},
+#line 75 "xmltags.gperf"
+ {"curstate", TAG_curstate},
+#line 66 "xmltags.gperf"
+ {"lm_switch", TAG_lm_switch},
+#line 48 "xmltags.gperf"
+ {"text", TAG_text},
+#line 39 "xmltags.gperf"
+ {"error_state", TAG_error_state},
+ {""}, {""},
+#line 59 "xmltags.gperf"
+ {"exec", TAG_exec},
+#line 51 "xmltags.gperf"
+ {"next", TAG_next},
+#line 61 "xmltags.gperf"
+ {"execte", TAG_execte},
+ {""}, {""},
+#line 50 "xmltags.gperf"
+ {"call", TAG_call},
+#line 54 "xmltags.gperf"
+ {"next_expr", TAG_next_expr},
+#line 77 "xmltags.gperf"
+ {"break", TAG_break},
+#line 83 "xmltags.gperf"
+ {"exports", TAG_exports},
+ {""},
+#line 53 "xmltags.gperf"
+ {"call_expr", TAG_call_expr}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ const char *s = wordlist[key].name;
+
+ if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
diff --git a/contrib/tools/ragel5/redfsm/ya.make b/contrib/tools/ragel5/redfsm/ya.make
new file mode 100644
index 0000000000..8bb2b97d44
--- /dev/null
+++ b/contrib/tools/ragel5/redfsm/ya.make
@@ -0,0 +1,25 @@
+LIBRARY()
+
+LICENSE(GPL-2.0-or-later)
+
+NO_UTIL()
+NO_COMPILER_WARNINGS()
+
+ADDINCL(
+ GLOBAL contrib/tools/ragel5/redfsm
+)
+
+PEERDIR(
+ contrib/tools/ragel5/aapl
+ contrib/tools/ragel5/common
+)
+
+SRCS(
+ gendata.cpp
+ redfsm.cpp
+ xmlparse.cpp
+ xmlscan.cpp
+ xmltags.cpp
+)
+
+END()
diff --git a/contrib/tools/ragel5/rlgen-cd/fflatcodegen.cpp b/contrib/tools/ragel5/rlgen-cd/fflatcodegen.cpp
new file mode 100644
index 0000000000..813347fd2b
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/fflatcodegen.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2004-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlgen-cd.h"
+#include "fflatcodegen.h"
+#include "redfsm.h"
+#include "gendata.h"
+
+std::ostream &FFlatCodeGen::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->actListId+1;
+ out << act;
+ return out;
+}
+
+std::ostream &FFlatCodeGen::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->actListId+1;
+ out << act;
+ return out;
+}
+
+std::ostream &FFlatCodeGen::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->actListId+1;
+ out << act;
+ return out;
+}
+
+/* Write out the function for a transition. */
+std::ostream &FFlatCodeGen::TRANS_ACTION( RedTransAp *trans )
+{
+ int action = 0;
+ if ( trans->action != 0 )
+ action = trans->action->actListId+1;
+ out << action;
+ return out;
+}
+
+/* Write out the function switch. This switch is keyed on the values
+ * of the func index. */
+std::ostream &FFlatCodeGen::TO_STATE_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numToStateRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+/* Write out the function switch. This switch is keyed on the values
+ * of the func index. */
+std::ostream &FFlatCodeGen::FROM_STATE_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numFromStateRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &FFlatCodeGen::EOF_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numEofRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, true );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+/* Write out the function switch. This switch is keyed on the values
+ * of the func index. */
+std::ostream &FFlatCodeGen::ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numTransRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+void FFlatCodeGen::writeData()
+{
+ if ( redFsm->anyConditions() ) {
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), CK() );
+ COND_KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondSpan), CSP() );
+ COND_KEY_SPANS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCond), C() );
+ CONDS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondIndexOffset), CO() );
+ COND_INDEX_OFFSET();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), K() );
+ KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxSpan), SP() );
+ KEY_SPANS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxFlatIndexOffset), IO() );
+ FLAT_INDEX_OFFSET();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxIndex), I() );
+ INDICIES();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxState), TT() );
+ TRANS_TARGS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActListId), TA() );
+ TRANS_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyToStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TSA() );
+ TO_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), FSA() );
+ FROM_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyEofActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActListId), EA() );
+ EOF_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ STATE_IDS();
+}
+
+void FFlatCodeGen::writeExec()
+{
+ outLabelUsed = false;
+
+ out <<
+ " {\n"
+ " int _slen";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << ", _ps";
+
+ out << ";\n";
+ out << " int _trans";
+
+ if ( redFsm->anyConditions() )
+ out << ", _cond";
+
+ out << ";\n";
+
+ out <<
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_keys;\n"
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxIndex) << POINTER() << "_inds;\n";
+
+ if ( redFsm->anyConditions() ) {
+ out <<
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxCond) << POINTER() << "_conds;\n"
+ " " << WIDE_ALPH_TYPE() << " _widec;\n";
+ }
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+
+ out << "_resume:\n";
+
+ if ( redFsm->errState != 0 ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << CS() << " == " << redFsm->errState->id << " )\n"
+ " goto _out;\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ out <<
+ " switch ( " << FSA() << "[" << CS() << "] ) {\n";
+ FROM_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ }
+
+ if ( redFsm->anyConditions() )
+ COND_TRANSLATE();
+
+ LOCATE_TRANS();
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << " _ps = " << CS() << ";\n";
+
+ out <<
+ " " << CS() << " = " << TT() << "[_trans];\n\n";
+
+ if ( redFsm->anyRegActions() ) {
+ out <<
+ " if ( " << TA() << "[_trans] == 0 )\n"
+ " goto _again;\n"
+ "\n"
+ " switch ( " << TA() << "[_trans] ) {\n";
+ ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ }
+
+ if ( redFsm->anyRegActions() || redFsm->anyActionGotos() ||
+ redFsm->anyActionCalls() || redFsm->anyActionRets() )
+ out << "_again:\n";
+
+ if ( redFsm->anyToStateActions() ) {
+ out <<
+ " switch ( " << TSA() << "[" << CS() << "] ) {\n";
+ TO_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ }
+
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " != " << PE() << " )\n"
+ " goto _resume;\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n"
+ " goto _resume;\n";
+ }
+
+ if ( outLabelUsed )
+ out << " _out: {}\n";
+
+ out << " }\n";
+}
+
+void FFlatCodeGen::writeEOF()
+{
+ if ( redFsm->anyEofActions() ) {
+ out <<
+ " {\n"
+ " switch ( " << EA() << "[" << CS() << "] ) {\n";
+ EOF_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+}
diff --git a/contrib/tools/ragel5/rlgen-cd/fflatcodegen.h b/contrib/tools/ragel5/rlgen-cd/fflatcodegen.h
new file mode 100644
index 0000000000..cf92fd9baf
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/fflatcodegen.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2004-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FFLATCODEGEN_H
+#define _FFLATCODEGEN_H
+
+#include <iostream>
+#include "flatcodegen.h"
+
+/* Forwards. */
+struct CodeGenData;
+
+/*
+ * FFlatCodeGen
+ */
+class FFlatCodeGen : public FlatCodeGen
+{
+protected:
+ FFlatCodeGen( ostream &out ) : FsmCodeGen(out), FlatCodeGen(out) {}
+
+ std::ostream &TO_STATE_ACTION_SWITCH();
+ std::ostream &FROM_STATE_ACTION_SWITCH();
+ std::ostream &EOF_ACTION_SWITCH();
+ std::ostream &ACTION_SWITCH();
+
+ virtual std::ostream &TO_STATE_ACTION( RedStateAp *state );
+ virtual std::ostream &FROM_STATE_ACTION( RedStateAp *state );
+ virtual std::ostream &EOF_ACTION( RedStateAp *state );
+ virtual std::ostream &TRANS_ACTION( RedTransAp *trans );
+
+ virtual void writeData();
+ virtual void writeEOF();
+ virtual void writeExec();
+};
+
+/*
+ * CFFlatCodeGen
+ */
+struct CFFlatCodeGen
+ : public FFlatCodeGen, public CCodeGen
+{
+ CFFlatCodeGen( ostream &out ) :
+ FsmCodeGen(out), FFlatCodeGen(out), CCodeGen(out) {}
+};
+
+/*
+ * DFFlatCodeGen
+ */
+struct DFFlatCodeGen
+ : public FFlatCodeGen, public DCodeGen
+{
+ DFFlatCodeGen( ostream &out ) :
+ FsmCodeGen(out), FFlatCodeGen(out), DCodeGen(out) {}
+};
+
+#endif /* _FFLATCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/fgotocodegen.cpp b/contrib/tools/ragel5/rlgen-cd/fgotocodegen.cpp
new file mode 100644
index 0000000000..9c4f039f39
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/fgotocodegen.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlgen-cd.h"
+#include "fgotocodegen.h"
+#include "redfsm.h"
+#include "gendata.h"
+#include "bstmap.h"
+
+std::ostream &FGotoCodeGen::EXEC_ACTIONS()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numTransRefs > 0 ) {
+ /* We are at the start of a glob, write the case. */
+ out << "f" << redAct->actListId << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tgoto _again;\n";
+ }
+ }
+ return out;
+}
+
+/* Write out the function switch. This switch is keyed on the values
+ * of the func index. */
+std::ostream &FGotoCodeGen::TO_STATE_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numToStateRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+/* Write out the function switch. This switch is keyed on the values
+ * of the func index. */
+std::ostream &FGotoCodeGen::FROM_STATE_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numFromStateRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &FGotoCodeGen::EOF_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numEofRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, true );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+
+std::ostream &FGotoCodeGen::FINISH_CASES()
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* States that are final and have an out action need a case. */
+ if ( st->eofAction != 0 ) {
+ /* Write the case label. */
+ out << "\t\tcase " << st->id << ": ";
+
+ /* Jump to the func. */
+ out << "goto f" << st->eofAction->actListId << ";\n";
+ }
+ }
+
+ return out;
+}
+
+unsigned int FGotoCodeGen::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->actListId+1;
+ return act;
+}
+
+unsigned int FGotoCodeGen::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->actListId+1;
+ return act;
+}
+
+unsigned int FGotoCodeGen::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->actListId+1;
+ return act;
+}
+
+void FGotoCodeGen::writeData()
+{
+ if ( redFsm->anyToStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TSA() );
+ TO_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), FSA() );
+ FROM_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyEofActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), EA() );
+ EOF_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ STATE_IDS();
+}
+
+void FGotoCodeGen::writeExec()
+{
+ outLabelUsed = false;
+
+ out << " {\n";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << " int _ps = 0;\n";
+
+ if ( redFsm->anyConditions() )
+ out << " " << WIDE_ALPH_TYPE() << " _widec;\n";
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+
+ out << "_resume:\n";
+
+ if ( redFsm->anyFromStateActions() ) {
+ out <<
+ " switch ( " << FSA() << "[" << CS() << "] ) {\n";
+ FROM_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ }
+
+ out <<
+ " switch ( " << CS() << " ) {\n";
+ STATE_GOTOS();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ TRANSITIONS() <<
+ "\n";
+
+ if ( redFsm->anyRegActions() )
+ EXEC_ACTIONS() << "\n";
+
+ out << "_again:\n";
+
+ if ( redFsm->anyToStateActions() ) {
+ out <<
+ " switch ( " << TSA() << "[" << CS() << "] ) {\n";
+ TO_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ }
+
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " != " << PE() << " )\n"
+ " goto _resume;\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n"
+ " goto _resume;\n";
+ }
+
+
+ if ( outLabelUsed )
+ out << " _out: {}\n";
+
+ out << " }\n";
+}
+
+void FGotoCodeGen::writeEOF()
+{
+ if ( redFsm->anyEofActions() ) {
+ out <<
+ " {\n"
+ " switch ( " << EA() << "[" << CS() << "] ) {\n";
+ EOF_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+}
diff --git a/contrib/tools/ragel5/rlgen-cd/fgotocodegen.h b/contrib/tools/ragel5/rlgen-cd/fgotocodegen.h
new file mode 100644
index 0000000000..076f5c4f7f
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/fgotocodegen.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FGOTOCODEGEN_H
+#define _FGOTOCODEGEN_H
+
+#include <iostream>
+#include "gotocodegen.h"
+
+/* Forwards. */
+struct CodeGenData;
+
+
+/*
+ * class FGotoCodeGen
+ */
+class FGotoCodeGen : public GotoCodeGen
+{
+public:
+ FGotoCodeGen( ostream &out ) : FsmCodeGen(out), GotoCodeGen(out) {}
+
+ std::ostream &EXEC_ACTIONS();
+ std::ostream &TO_STATE_ACTION_SWITCH();
+ std::ostream &FROM_STATE_ACTION_SWITCH();
+ std::ostream &FINISH_CASES();
+ std::ostream &EOF_ACTION_SWITCH();
+ unsigned int TO_STATE_ACTION( RedStateAp *state );
+ unsigned int FROM_STATE_ACTION( RedStateAp *state );
+ unsigned int EOF_ACTION( RedStateAp *state );
+
+ virtual void writeData();
+ virtual void writeEOF();
+ virtual void writeExec();
+};
+
+/*
+ * class CFGotoCodeGen
+ */
+struct CFGotoCodeGen
+ : public FGotoCodeGen, public CCodeGen
+{
+ CFGotoCodeGen( ostream &out ) :
+ FsmCodeGen(out), FGotoCodeGen(out), CCodeGen(out) {}
+};
+
+/*
+ * class DFGotoCodeGen
+ */
+struct DFGotoCodeGen
+ : public FGotoCodeGen, public DCodeGen
+{
+ DFGotoCodeGen( ostream &out ) :
+ FsmCodeGen(out), FGotoCodeGen(out), DCodeGen(out) {}
+};
+
+#endif /* _FGOTOCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/flatcodegen.cpp b/contrib/tools/ragel5/rlgen-cd/flatcodegen.cpp
new file mode 100644
index 0000000000..117f3798c9
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/flatcodegen.cpp
@@ -0,0 +1,766 @@
+/*
+ * Copyright 2004-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlgen-cd.h"
+#include "flatcodegen.h"
+#include "redfsm.h"
+#include "gendata.h"
+
+std::ostream &FlatCodeGen::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->location+1;
+ out << act;
+ return out;
+}
+
+std::ostream &FlatCodeGen::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->location+1;
+ out << act;
+ return out;
+}
+
+std::ostream &FlatCodeGen::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->location+1;
+ out << act;
+ return out;
+}
+
+std::ostream &FlatCodeGen::TRANS_ACTION( RedTransAp *trans )
+{
+ /* If there are actions, emit them. Otherwise emit zero. */
+ int act = 0;
+ if ( trans->action != 0 )
+ act = trans->action->location+1;
+ out << act;
+ return out;
+}
+
+std::ostream &FlatCodeGen::TO_STATE_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numToStateRefs > 0 ) {
+ /* Write the case label, the action and the case break */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &FlatCodeGen::FROM_STATE_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numFromStateRefs > 0 ) {
+ /* Write the case label, the action and the case break */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &FlatCodeGen::EOF_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numEofRefs > 0 ) {
+ /* Write the case label, the action and the case break */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, true );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+
+std::ostream &FlatCodeGen::ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numTransRefs > 0 ) {
+ /* Write the case label, the action and the case break */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+
+std::ostream &FlatCodeGen::FLAT_INDEX_OFFSET()
+{
+ out << "\t";
+ int totalStateNum = 0, curIndOffset = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write the index offset. */
+ out << curIndOffset;
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Move the index offset ahead. */
+ if ( st->transList != 0 )
+ curIndOffset += keyOps->span( st->lowKey, st->highKey );
+
+ if ( st->defTrans != 0 )
+ curIndOffset += 1;
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::KEY_SPANS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write singles length. */
+ unsigned long long span = 0;
+ if ( st->transList != 0 )
+ span = keyOps->span( st->lowKey, st->highKey );
+ out << span;
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::TO_STATE_ACTIONS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ TO_STATE_ACTION(st);
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::FROM_STATE_ACTIONS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ FROM_STATE_ACTION(st);
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::EOF_ACTIONS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ EOF_ACTION(st);
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::COND_KEYS()
+{
+ out << '\t';
+ int totalTrans = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Emit just cond low key and cond high key. */
+ out << KEY( st->condLowKey ) << ", ";
+ out << KEY( st->condHighKey ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::COND_KEY_SPANS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write singles length. */
+ unsigned long long span = 0;
+ if ( st->condList != 0 )
+ span = keyOps->span( st->condLowKey, st->condHighKey );
+ out << span;
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::CONDS()
+{
+ int totalTrans = 0;
+ out << '\t';
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->condList != 0 ) {
+ /* Walk the singles. */
+ unsigned long long span = keyOps->span( st->condLowKey, st->condHighKey );
+ for ( unsigned long long pos = 0; pos < span; pos++ ) {
+ if ( st->condList[pos] != 0 )
+ out << st->condList[pos]->condSpaceId + 1 << ", ";
+ else
+ out << "0, ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::COND_INDEX_OFFSET()
+{
+ out << "\t";
+ int totalStateNum = 0, curIndOffset = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write the index offset. */
+ out << curIndOffset;
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Move the index offset ahead. */
+ if ( st->condList != 0 )
+ curIndOffset += keyOps->span( st->condLowKey, st->condHighKey );
+ }
+ out << "\n";
+ return out;
+}
+
+
+std::ostream &FlatCodeGen::KEYS()
+{
+ out << '\t';
+ int totalTrans = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Emit just low key and high key. */
+ out << KEY( st->lowKey ) << ", ";
+ out << KEY( st->highKey ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::INDICIES()
+{
+ int totalTrans = 0;
+ out << '\t';
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->transList != 0 ) {
+ /* Walk the singles. */
+ unsigned long long span = keyOps->span( st->lowKey, st->highKey );
+ for ( unsigned long long pos = 0; pos < span; pos++ ) {
+ out << st->transList[pos]->id << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+
+ /* The state's default index goes next. */
+ if ( st->defTrans != 0 )
+ out << st->defTrans->id << ", ";
+
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &FlatCodeGen::TRANS_TARGS()
+{
+ /* Transitions must be written ordered by their id. */
+ RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()];
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ )
+ transPtrs[trans->id] = trans;
+
+ /* Keep a count of the num of items in the array written. */
+ out << '\t';
+ int totalStates = 0;
+ for ( int t = 0; t < redFsm->transSet.length(); t++ ) {
+ /* Write out the target state. */
+ RedTransAp *trans = transPtrs[t];
+ out << trans->targ->id;
+ if ( t < redFsm->transSet.length()-1 ) {
+ out << ", ";
+ if ( ++totalStates % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ delete[] transPtrs;
+ return out;
+}
+
+
+std::ostream &FlatCodeGen::TRANS_ACTIONS()
+{
+ /* Transitions must be written ordered by their id. */
+ RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()];
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ )
+ transPtrs[trans->id] = trans;
+
+ /* Keep a count of the num of items in the array written. */
+ out << '\t';
+ int totalAct = 0;
+ for ( int t = 0; t < redFsm->transSet.length(); t++ ) {
+ /* Write the function for the transition. */
+ RedTransAp *trans = transPtrs[t];
+ TRANS_ACTION( trans );
+ if ( t < redFsm->transSet.length()-1 ) {
+ out << ", ";
+ if ( ++totalAct % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ delete[] transPtrs;
+ return out;
+}
+
+void FlatCodeGen::LOCATE_TRANS()
+{
+ out <<
+ " _keys = " << ARR_OFF( K(), "(" + CS() + "<<1)" ) << ";\n"
+ " _inds = " << ARR_OFF( I(), IO() + "[" + CS() + "]" ) << ";\n"
+ "\n"
+ " _slen = " << SP() << "[" << CS() << "];\n"
+ " _trans = _inds[ _slen > 0 && _keys[0] <=" << GET_WIDE_KEY() << " &&\n"
+ " " << GET_WIDE_KEY() << " <= _keys[1] ?\n"
+ " " << GET_WIDE_KEY() << " - _keys[0] : _slen ];\n"
+ "\n";
+}
+
+void FlatCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish )
+{
+ ret << "{" << CS() << " = " << gotoDest << "; " <<
+ CTRL_FLOW() << "goto _again;}";
+}
+
+void FlatCodeGen::GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << "{" << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << "); " << CTRL_FLOW() << "goto _again;}";
+}
+
+void FlatCodeGen::CURS( ostream &ret, bool inFinish )
+{
+ ret << "(_ps)";
+}
+
+void FlatCodeGen::TARGS( ostream &ret, bool inFinish, int targState )
+{
+ ret << "(" << CS() << ")";
+}
+
+void FlatCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
+{
+ ret << CS() << " = " << nextDest << ";";
+}
+
+void FlatCodeGen::NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << ");";
+}
+
+void FlatCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
+{
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = " <<
+ callDest << "; " << CTRL_FLOW() << "goto _again;}";
+}
+
+
+void FlatCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
+{
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, targState, inFinish );
+ ret << "); " << CTRL_FLOW() << "goto _again;}";
+}
+
+
+void FlatCodeGen::RET( ostream &ret, bool inFinish )
+{
+ ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "]; " <<
+ CTRL_FLOW() << "goto _again;}";
+}
+
+void FlatCodeGen::BREAK( ostream &ret, int targState )
+{
+ outLabelUsed = true;
+ ret << CTRL_FLOW() << "goto _out;";
+}
+
+void FlatCodeGen::writeData()
+{
+ /* If there are any transtion functions then output the array. If there
+ * are none, don't bother emitting an empty array that won't be used. */
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActArrItem), A() );
+ ACTIONS_ARRAY();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyConditions() ) {
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), CK() );
+ COND_KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondSpan), CSP() );
+ COND_KEY_SPANS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCond), C() );
+ CONDS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondIndexOffset), CO() );
+ COND_INDEX_OFFSET();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), K() );
+ KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxSpan), SP() );
+ KEY_SPANS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxFlatIndexOffset), IO() );
+ FLAT_INDEX_OFFSET();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxIndex), I() );
+ INDICIES();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxState), TT() );
+ TRANS_TARGS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TA() );
+ TRANS_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyToStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TSA() );
+ TO_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), FSA() );
+ FROM_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyEofActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), EA() );
+ EOF_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ STATE_IDS();
+}
+
+void FlatCodeGen::COND_TRANSLATE()
+{
+ out <<
+ " _widec = " << GET_KEY() << ";\n";
+
+ out <<
+ " _keys = " << ARR_OFF( CK(), "(" + CS() + "<<1)" ) << ";\n"
+ " _conds = " << ARR_OFF( C(), CO() + "[" + CS() + "]" ) << ";\n"
+ "\n"
+ " _slen = " << CSP() << "[" << CS() << "];\n"
+ " _cond = _slen > 0 && _keys[0] <=" << GET_WIDE_KEY() << " &&\n"
+ " " << GET_WIDE_KEY() << " <= _keys[1] ?\n"
+ " _conds[" << GET_WIDE_KEY() << " - _keys[0]] : 0;\n"
+ "\n";
+
+ out <<
+ " switch ( _cond ) {\n";
+ for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) {
+ CondSpace *condSpace = csi;
+ out << " case " << condSpace->condSpaceId + 1 << ": {\n";
+ out << TABS(2) << "_widec = " << CAST(WIDE_ALPH_TYPE()) << "(" <<
+ KEY(condSpace->baseKey) << " + (" << GET_KEY() <<
+ " - " << KEY(keyOps->minKey) << "));\n";
+
+ for ( CondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) {
+ out << TABS(2) << "if ( ";
+ CONDITION( out, *csi );
+ Size condValOffset = ((1 << csi.pos()) * keyOps->alphSize());
+ out << " ) _widec += " << condValOffset << ";\n";
+ }
+
+ out << " }\n";
+ out << " break;\n";
+ }
+
+ SWITCH_DEFAULT();
+
+ out <<
+ " }\n";
+}
+
+void FlatCodeGen::writeExec()
+{
+ outLabelUsed = false;
+
+ out <<
+ " {\n"
+ " int _slen";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << ", _ps";
+
+ out <<
+ ";\n"
+ " int _trans";
+
+ if ( redFsm->anyConditions() )
+ out << ", _cond";
+ out << ";\n";
+
+ if ( redFsm->anyToStateActions() ||
+ redFsm->anyRegActions() || redFsm->anyFromStateActions() )
+ {
+ out <<
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxActArrItem) << POINTER() << "_acts;\n"
+ " " << UINT() << " _nacts;\n";
+ }
+
+ out <<
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_keys;\n"
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxIndex) << POINTER() << "_inds;\n";
+
+ if ( redFsm->anyConditions() ) {
+ out <<
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxCond) << POINTER() << "_conds;\n"
+ " " << WIDE_ALPH_TYPE() << " _widec;\n";
+ }
+
+ out << "\n";
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+
+ out << "_resume:\n";
+
+ if ( redFsm->errState != 0 ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << CS() << " == " << redFsm->errState->id << " )\n"
+ " goto _out;\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ out <<
+ " _acts = " << ARR_OFF( A(), FSA() + "[" + CS() + "]" ) << ";\n"
+ " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ FROM_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+
+ if ( redFsm->anyConditions() )
+ COND_TRANSLATE();
+
+ LOCATE_TRANS();
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << " _ps = " << CS() << ";\n";
+
+ out <<
+ " " << CS() << " = " << TT() << "[_trans];\n"
+ "\n";
+
+ if ( redFsm->anyRegActions() ) {
+ out <<
+ " if ( " << TA() << "[_trans] == 0 )\n"
+ " goto _again;\n"
+ "\n"
+ " _acts = " << ARR_OFF( A(), TA() + "[_trans]" ) << ";\n"
+ " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *(_acts++) )\n {\n";
+ ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+
+ if ( redFsm->anyRegActions() || redFsm->anyActionGotos() ||
+ redFsm->anyActionCalls() || redFsm->anyActionRets() )
+ out << "_again:\n";
+
+ if ( redFsm->anyToStateActions() ) {
+ out <<
+ " _acts = " << ARR_OFF( A(), TSA() + "[" + CS() + "]" ) << ";\n"
+ " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ TO_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " != " << PE() << " )\n"
+ " goto _resume;\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n"
+ " goto _resume;\n";
+ }
+
+ if ( outLabelUsed )
+ out << " _out: {}\n";
+
+ out << " }\n";
+}
+
+void FlatCodeGen::writeEOF()
+{
+ if ( redFsm->anyEofActions() ) {
+ out <<
+ " {\n"
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxActArrItem) << POINTER() << "_acts = " <<
+ ARR_OFF( A(), EA() + "[" + CS() + "]" ) << ";\n"
+ " " << UINT() << " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ EOF_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n";
+ }
+}
diff --git a/contrib/tools/ragel5/rlgen-cd/flatcodegen.h b/contrib/tools/ragel5/rlgen-cd/flatcodegen.h
new file mode 100644
index 0000000000..27dee2ef92
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/flatcodegen.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2004-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FLATCODEGEN_H
+#define _FLATCODEGEN_H
+
+#include <iostream>
+#include "fsmcodegen.h"
+
+/* Forwards. */
+struct CodeGenData;
+struct NameInst;
+struct RedTransAp;
+struct RedStateAp;
+
+/*
+ * FlatCodeGen
+ */
+class FlatCodeGen : virtual public FsmCodeGen
+{
+public:
+ FlatCodeGen( ostream &out ) : FsmCodeGen(out) {}
+ virtual ~FlatCodeGen() { }
+
+protected:
+ std::ostream &TO_STATE_ACTION_SWITCH();
+ std::ostream &FROM_STATE_ACTION_SWITCH();
+ std::ostream &EOF_ACTION_SWITCH();
+ std::ostream &ACTION_SWITCH();
+ std::ostream &KEYS();
+ std::ostream &INDICIES();
+ std::ostream &FLAT_INDEX_OFFSET();
+ std::ostream &KEY_SPANS();
+ std::ostream &TO_STATE_ACTIONS();
+ std::ostream &FROM_STATE_ACTIONS();
+ std::ostream &EOF_ACTIONS();
+ std::ostream &TRANS_TARGS();
+ std::ostream &TRANS_ACTIONS();
+ void LOCATE_TRANS();
+
+ std::ostream &COND_INDEX_OFFSET();
+ void COND_TRANSLATE();
+ std::ostream &CONDS();
+ std::ostream &COND_KEYS();
+ std::ostream &COND_KEY_SPANS();
+
+ void GOTO( ostream &ret, int gotoDest, bool inFinish );
+ void CALL( ostream &ret, int callDest, int targState, bool inFinish );
+ void NEXT( ostream &ret, int nextDest, bool inFinish );
+ void GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+ void NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+ void CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish );
+ void CURS( ostream &ret, bool inFinish );
+ void TARGS( ostream &ret, bool inFinish, int targState );
+ void RET( ostream &ret, bool inFinish );
+ void BREAK( ostream &ret, int targState );
+
+ virtual std::ostream &TO_STATE_ACTION( RedStateAp *state );
+ virtual std::ostream &FROM_STATE_ACTION( RedStateAp *state );
+ virtual std::ostream &EOF_ACTION( RedStateAp *state );
+ virtual std::ostream &TRANS_ACTION( RedTransAp *trans );
+
+ virtual void writeData();
+ virtual void writeEOF();
+ virtual void writeExec();
+};
+
+/*
+ * CFlatCodeGen
+ */
+struct CFlatCodeGen
+ : public FlatCodeGen, public CCodeGen
+{
+ CFlatCodeGen( ostream &out ) :
+ FsmCodeGen(out), FlatCodeGen(out), CCodeGen(out) {}
+};
+
+/*
+ * DFlatCodeGen
+ */
+struct DFlatCodeGen
+ : public FlatCodeGen, public DCodeGen
+{
+ DFlatCodeGen( ostream &out ) :
+ FsmCodeGen(out), FlatCodeGen(out), DCodeGen(out) {}
+};
+
+#endif /* _FLATCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/fsmcodegen.cpp b/contrib/tools/ragel5/rlgen-cd/fsmcodegen.cpp
new file mode 100644
index 0000000000..c0fc4b00f5
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/fsmcodegen.cpp
@@ -0,0 +1,749 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlgen-cd.h"
+#include "fsmcodegen.h"
+#include "redfsm.h"
+#include "gendata.h"
+#include <sstream>
+#include <string>
+#include <assert.h>
+
+
+using std::ostream;
+using std::ostringstream;
+using std::string;
+using std::cerr;
+using std::endl;
+
+void lineDirective( ostream &out, char *fileName, int line )
+{
+ if ( noLineDirectives )
+ out << "/* ";
+
+ /* Write the preprocessor line info for to the input file. */
+ out << "#line " << line << " \"";
+ for ( char *pc = fileName; *pc != 0; pc++ ) {
+ if ( *pc == '\\' )
+ out << "\\\\";
+ else
+ out << *pc;
+ }
+ out << '"';
+
+ if ( noLineDirectives )
+ out << " */";
+
+ out << '\n';
+}
+
+void genLineDirective( ostream &out )
+{
+ std::streambuf *sbuf = out.rdbuf();
+ output_filter *filter = static_cast<output_filter*>(sbuf);
+ lineDirective( out, filter->fileName, filter->line + 1 );
+}
+
+
+/* Init code gen with in parameters. */
+FsmCodeGen::FsmCodeGen( ostream &out )
+:
+ CodeGenData(out)
+{
+}
+
+unsigned int FsmCodeGen::arrayTypeSize( unsigned long maxVal )
+{
+ long long maxValLL = (long long) maxVal;
+ HostType *arrayType = keyOps->typeSubsumes( maxValLL );
+ assert( arrayType != 0 );
+ return arrayType->size;
+}
+
+string FsmCodeGen::ARRAY_TYPE( unsigned long maxVal )
+{
+ long long maxValLL = (long long) maxVal;
+ HostType *arrayType = keyOps->typeSubsumes( maxValLL );
+ assert( arrayType != 0 );
+
+ string ret = arrayType->data1;
+ if ( arrayType->data2 != 0 ) {
+ ret += " ";
+ ret += arrayType->data2;
+ }
+ return ret;
+}
+
+
+/* Write out the fsm name. */
+string FsmCodeGen::FSM_NAME()
+{
+ return fsmName;
+}
+
+/* Emit the offset of the start state as a decimal integer. */
+string FsmCodeGen::START_STATE_ID()
+{
+ ostringstream ret;
+ ret << redFsm->startState->id;
+ return ret.str();
+};
+
+/* Write out the array of actions. */
+std::ostream &FsmCodeGen::ACTIONS_ARRAY()
+{
+ out << "\t0, ";
+ int totalActions = 1;
+ for ( ActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) {
+ /* Write out the length, which will never be the last character. */
+ out << act->key.length() << ", ";
+ /* Put in a line break every 8 */
+ if ( totalActions++ % 8 == 7 )
+ out << "\n\t";
+
+ for ( ActionTable::Iter item = act->key; item.lte(); item++ ) {
+ out << item->value->actionId;
+ if ( ! (act.last() && item.last()) )
+ out << ", ";
+
+ /* Put in a line break every 8 */
+ if ( totalActions++ % 8 == 7 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+
+string FsmCodeGen::CS()
+{
+ ostringstream ret;
+ if ( curStateExpr != 0 ) {
+ /* Emit the user supplied method of retrieving the key. */
+ ret << "(";
+ INLINE_LIST( ret, curStateExpr, 0, false );
+ ret << ")";
+ }
+ else {
+ /* Expression for retrieving the key, use simple dereference. */
+ ret << ACCESS() << "cs";
+ }
+ return ret.str();
+}
+
+string FsmCodeGen::ACCESS()
+{
+ ostringstream ret;
+ if ( accessExpr != 0 )
+ INLINE_LIST( ret, accessExpr, 0, false );
+ return ret.str();
+}
+
+string FsmCodeGen::GET_WIDE_KEY()
+{
+ if ( redFsm->anyConditions() )
+ return "_widec";
+ else
+ return GET_KEY();
+}
+
+string FsmCodeGen::GET_WIDE_KEY( RedStateAp *state )
+{
+ if ( state->stateCondList.length() > 0 )
+ return "_widec";
+ else
+ return GET_KEY();
+}
+
+string FsmCodeGen::GET_KEY()
+{
+ ostringstream ret;
+ if ( getKeyExpr != 0 ) {
+ /* Emit the user supplied method of retrieving the key. */
+ ret << "(";
+ INLINE_LIST( ret, getKeyExpr, 0, false );
+ ret << ")";
+ }
+ else {
+ /* Expression for retrieving the key, use simple dereference. */
+ ret << "(*" << P() << ")";
+ }
+ return ret.str();
+}
+
+/* Write out level number of tabs. Makes the nested binary search nice
+ * looking. */
+string FsmCodeGen::TABS( int level )
+{
+ string result;
+ while ( level-- > 0 )
+ result += "\t";
+ return result;
+}
+
+/* Write out a key from the fsm code gen. Depends on wether or not the key is
+ * signed. */
+string FsmCodeGen::KEY( Key key )
+{
+ ostringstream ret;
+ if ( keyOps->isSigned || !hostLang->explicitUnsigned )
+ ret << key.getVal();
+ else
+ ret << (unsigned long) key.getVal() << 'u';
+ return ret.str();
+}
+
+void FsmCodeGen::EXEC( ostream &ret, InlineItem *item, int targState, int inFinish )
+{
+ /* The parser gives fexec two children. The double brackets are for D
+ * code. If the inline list is a single word it will get interpreted as a
+ * C-style cast by the D compiler. */
+ ret << "{" << P() << " = ((";
+ INLINE_LIST( ret, item->children, targState, inFinish );
+ ret << "))-1;}";
+}
+
+void FsmCodeGen::EXECTE( ostream &ret, InlineItem *item, int targState, int inFinish )
+{
+ /* Tokend version of exec. */
+
+ /* The parser gives fexec two children. The double brackets are for D
+ * code. If the inline list is a single word it will get interpreted as a
+ * C-style cast by the D compiler. */
+ ret << "{" << TOKEND() << " = ((";
+ INLINE_LIST( ret, item->children, targState, inFinish );
+ ret << "));}";
+}
+
+
+void FsmCodeGen::LM_SWITCH( ostream &ret, InlineItem *item,
+ int targState, int inFinish )
+{
+ ret <<
+ " switch( " << ACT() << " ) {\n";
+
+ /* If the switch handles error then we also forced the error state. It
+ * will exist. */
+ if ( item->handlesError ) {
+ ret << " case 0: " << TOKEND() << " = " << TOKSTART() << "; ";
+ GOTO( ret, redFsm->errState->id, inFinish );
+ ret << "\n";
+ }
+
+ for ( InlineList::Iter lma = *item->children; lma.lte(); lma++ ) {
+ /* Write the case label, the action and the case break. */
+ ret << " case " << lma->lmId << ":\n";
+
+ /* Write the block and close it off. */
+ ret << " {";
+ INLINE_LIST( ret, lma->children, targState, inFinish );
+ ret << "}\n";
+
+ ret << " break;\n";
+ }
+ /* Default required for D code. */
+ ret <<
+ " default: break;\n"
+ " }\n"
+ "\t";
+}
+
+void FsmCodeGen::SET_ACT( ostream &ret, InlineItem *item )
+{
+ ret << ACT() << " = " << item->lmId << ";";
+}
+
+void FsmCodeGen::SET_TOKEND( ostream &ret, InlineItem *item )
+{
+ /* The tokend action sets tokend. */
+ ret << TOKEND() << " = " << P();
+ if ( item->offset != 0 )
+ out << "+" << item->offset;
+ out << ";";
+}
+
+void FsmCodeGen::GET_TOKEND( ostream &ret, InlineItem *item )
+{
+ ret << TOKEND();
+}
+
+void FsmCodeGen::INIT_TOKSTART( ostream &ret, InlineItem *item )
+{
+ ret << TOKSTART() << " = " << NULL_ITEM() << ";";
+}
+
+void FsmCodeGen::INIT_ACT( ostream &ret, InlineItem *item )
+{
+ ret << ACT() << " = 0;";
+}
+
+void FsmCodeGen::SET_TOKSTART( ostream &ret, InlineItem *item )
+{
+ ret << TOKSTART() << " = " << P() << ";";
+}
+
+void FsmCodeGen::SUB_ACTION( ostream &ret, InlineItem *item,
+ int targState, bool inFinish )
+{
+ if ( item->children->length() > 0 ) {
+ /* Write the block and close it off. */
+ ret << "{";
+ INLINE_LIST( ret, item->children, targState, inFinish );
+ ret << "}";
+ }
+}
+
+
+/* Write out an inline tree structure. Walks the list and possibly calls out
+ * to virtual functions than handle language specific items in the tree. */
+void FsmCodeGen::INLINE_LIST( ostream &ret, InlineList *inlineList,
+ int targState, bool inFinish )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case InlineItem::Text:
+ ret << item->data;
+ break;
+ case InlineItem::Goto:
+ GOTO( ret, item->targState->id, inFinish );
+ break;
+ case InlineItem::Call:
+ CALL( ret, item->targState->id, targState, inFinish );
+ break;
+ case InlineItem::Next:
+ NEXT( ret, item->targState->id, inFinish );
+ break;
+ case InlineItem::Ret:
+ RET( ret, inFinish );
+ break;
+ case InlineItem::PChar:
+ ret << P();
+ break;
+ case InlineItem::Char:
+ ret << GET_KEY();
+ break;
+ case InlineItem::Hold:
+ ret << P() << "--;";
+ break;
+ case InlineItem::Exec:
+ EXEC( ret, item, targState, inFinish );
+ break;
+ case InlineItem::HoldTE:
+ ret << TOKEND() << "--;";
+ break;
+ case InlineItem::ExecTE:
+ EXECTE( ret, item, targState, inFinish );
+ break;
+ case InlineItem::Curs:
+ CURS( ret, inFinish );
+ break;
+ case InlineItem::Targs:
+ TARGS( ret, inFinish, targState );
+ break;
+ case InlineItem::Entry:
+ ret << item->targState->id;
+ break;
+ case InlineItem::GotoExpr:
+ GOTO_EXPR( ret, item, inFinish );
+ break;
+ case InlineItem::CallExpr:
+ CALL_EXPR( ret, item, targState, inFinish );
+ break;
+ case InlineItem::NextExpr:
+ NEXT_EXPR( ret, item, inFinish );
+ break;
+ case InlineItem::LmSwitch:
+ LM_SWITCH( ret, item, targState, inFinish );
+ break;
+ case InlineItem::LmSetActId:
+ SET_ACT( ret, item );
+ break;
+ case InlineItem::LmSetTokEnd:
+ SET_TOKEND( ret, item );
+ break;
+ case InlineItem::LmGetTokEnd:
+ GET_TOKEND( ret, item );
+ break;
+ case InlineItem::LmInitTokStart:
+ INIT_TOKSTART( ret, item );
+ break;
+ case InlineItem::LmInitAct:
+ INIT_ACT( ret, item );
+ break;
+ case InlineItem::LmSetTokStart:
+ SET_TOKSTART( ret, item );
+ break;
+ case InlineItem::SubAction:
+ SUB_ACTION( ret, item, targState, inFinish );
+ break;
+ case InlineItem::Break:
+ BREAK( ret, targState );
+ break;
+ }
+ }
+}
+/* Write out paths in line directives. Escapes any special characters. */
+string FsmCodeGen::LDIR_PATH( char *path )
+{
+ ostringstream ret;
+ for ( char *pc = path; *pc != 0; pc++ ) {
+ if ( *pc == '\\' )
+ ret << "\\\\";
+ else
+ ret << *pc;
+ }
+ return ret.str();
+}
+
+void FsmCodeGen::ACTION( ostream &ret, Action *action, int targState, bool inFinish )
+{
+ /* Write the preprocessor line info for going into the source file. */
+ lineDirective( ret, sourceFileName, action->loc.line );
+
+ /* Write the block and close it off. */
+ ret << "\t{";
+ INLINE_LIST( ret, action->inlineList, targState, inFinish );
+ ret << "}\n";
+}
+
+void FsmCodeGen::CONDITION( ostream &ret, Action *condition )
+{
+ ret << "\n";
+ lineDirective( ret, sourceFileName, condition->loc.line );
+ INLINE_LIST( ret, condition->inlineList, 0, false );
+}
+
+string FsmCodeGen::ERROR_STATE()
+{
+ ostringstream ret;
+ if ( redFsm->errState != 0 )
+ ret << redFsm->errState->id;
+ else
+ ret << "-1";
+ return ret.str();
+}
+
+string FsmCodeGen::FIRST_FINAL_STATE()
+{
+ ostringstream ret;
+ if ( redFsm->firstFinState != 0 )
+ ret << redFsm->firstFinState->id;
+ else
+ ret << redFsm->nextStateId;
+ return ret.str();
+}
+
+void FsmCodeGen::writeInit()
+{
+ out << " {\n";
+
+ if ( redFsm->startState != 0 )
+ out << "\t" << CS() << " = " << START() << ";\n";
+
+ /* If there are any calls, then the stack top needs initialization. */
+ if ( redFsm->anyActionCalls() || redFsm->anyActionRets() )
+ out << "\t" << TOP() << " = 0;\n";
+
+ if ( hasLongestMatch ) {
+ out <<
+ " " << TOKSTART() << " = " << NULL_ITEM() << ";\n"
+ " " << TOKEND() << " = " << NULL_ITEM() << ";\n"
+ " " << ACT() << " = 0;\n";
+ }
+ out << " }\n";
+}
+
+string FsmCodeGen::DATA_PREFIX()
+{
+ if ( dataPrefix )
+ return FSM_NAME() + "_";
+ return "";
+}
+
+/* Emit the alphabet data type. */
+string FsmCodeGen::ALPH_TYPE()
+{
+ string ret = keyOps->alphType->data1;
+ if ( keyOps->alphType->data2 != 0 ) {
+ ret += " ";
+ ret += + keyOps->alphType->data2;
+ }
+ return ret;
+}
+
+/* Emit the alphabet data type. */
+string FsmCodeGen::WIDE_ALPH_TYPE()
+{
+ string ret;
+ if ( redFsm->maxKey <= keyOps->maxKey )
+ ret = ALPH_TYPE();
+ else {
+ long long maxKeyVal = redFsm->maxKey.getLongLong();
+ HostType *wideType = keyOps->typeSubsumes( keyOps->isSigned, maxKeyVal );
+ assert( wideType != 0 );
+
+ ret = wideType->data1;
+ if ( wideType->data2 != 0 ) {
+ ret += " ";
+ ret += wideType->data2;
+ }
+ }
+ return ret;
+}
+
+void FsmCodeGen::STATE_IDS()
+{
+ if ( redFsm->startState != 0 )
+ STATIC_VAR( "int", START() ) << " = " << START_STATE_ID() << "};\n";
+
+ if ( writeFirstFinal )
+ STATIC_VAR( "int" , FIRST_FINAL() ) << " = " << FIRST_FINAL_STATE() << "};\n";
+
+ if ( writeErr )
+ STATIC_VAR( "int", ERROR() ) << " = " << ERROR_STATE() << "};\n";
+
+ out << "\n";
+
+ if ( entryPointNames.length() > 0 ) {
+ for ( EntryNameVect::Iter en = entryPointNames; en.lte(); en++ ) {
+ STATIC_VAR( "int", DATA_PREFIX() + "en_" + *en ) <<
+ " = " << entryPointIds[en.pos()] << "};\n";
+ }
+ out << "\n";
+ }
+}
+
+
+/*
+ * Language specific, but style independent code generators functions.
+ */
+
+string CCodeGen::PTR_CONST()
+{
+ return "const ";
+}
+
+std::ostream &CCodeGen::OPEN_ARRAY( const string& type, const string& name )
+{
+ out << "#if defined(__GNUC__)\n";
+ out << "static __attribute__((used)) const " << type << " " << name << "[] = {\n";
+ out << "#else\n";
+ out << "static const " << type << " " << name << "[] = {\n";
+ out << "#endif\n";
+ return out;
+}
+
+std::ostream &CCodeGen::CLOSE_ARRAY()
+{
+ return out << "};\n";
+}
+
+std::ostream &CCodeGen::STATIC_VAR( const string& type, const string& name )
+{
+ out << "enum {" << name;
+ return out;
+}
+
+string CCodeGen::UINT( )
+{
+ return "unsigned int";
+}
+
+string CCodeGen::ARR_OFF( const string& ptr, const string& offset )
+{
+ return ptr + " + " + offset;
+}
+
+string CCodeGen::CAST( const string& type )
+{
+ return "(" + type + ")";
+}
+
+string CCodeGen::NULL_ITEM()
+{
+ return "0";
+}
+
+string CCodeGen::POINTER()
+{
+ return " *";
+}
+
+std::ostream &CCodeGen::SWITCH_DEFAULT()
+{
+ return out;
+}
+
+string CCodeGen::CTRL_FLOW()
+{
+ return "";
+}
+
+void CCodeGen::writeExports()
+{
+ if ( exportList.length() > 0 ) {
+ for ( ExportList::Iter ex = exportList; ex.lte(); ex++ ) {
+ out << "#define " << DATA_PREFIX() << "ex_" << ex->name << " " <<
+ KEY(ex->key) << "\n";
+ }
+ out << "\n";
+ }
+}
+
+/*
+ * D Specific
+ */
+
+string DCodeGen::NULL_ITEM()
+{
+ return "null";
+}
+
+string DCodeGen::POINTER()
+{
+ // multiple items seperated by commas can also be pointer types.
+ return "* ";
+}
+
+string DCodeGen::PTR_CONST()
+{
+ return "";
+}
+
+std::ostream &DCodeGen::OPEN_ARRAY( const string& type, const string& name )
+{
+ out << "static const " << type << "[] " << name << " = [\n";
+ return out;
+}
+
+std::ostream &DCodeGen::CLOSE_ARRAY()
+{
+ return out << "];\n";
+}
+
+std::ostream &DCodeGen::STATIC_VAR( const string& type, const string& name )
+{
+ out << "static const " << type << " " << name;
+ return out;
+}
+
+string DCodeGen::ARR_OFF( const string& ptr, const string& offset )
+{
+ return "&" + ptr + "[" + offset + "]";
+}
+
+string DCodeGen::CAST( const string& type )
+{
+ return "cast(" + type + ")";
+}
+
+string DCodeGen::UINT( )
+{
+ return "uint";
+}
+
+std::ostream &DCodeGen::SWITCH_DEFAULT()
+{
+ out << " default: break;\n";
+ return out;
+}
+
+string DCodeGen::CTRL_FLOW()
+{
+ return "if (true) ";
+}
+
+void DCodeGen::writeExports()
+{
+ if ( exportList.length() > 0 ) {
+ for ( ExportList::Iter ex = exportList; ex.lte(); ex++ ) {
+ out << "static const " << ALPH_TYPE() << " " << DATA_PREFIX() <<
+ "ex_" << ex->name << " = " << KEY(ex->key) << ";\n";
+ }
+ out << "\n";
+ }
+}
+
+/*
+ * End D-specific code.
+ */
+
+void FsmCodeGen::finishRagelDef()
+{
+ if ( codeStyle == GenGoto || codeStyle == GenFGoto ||
+ codeStyle == GenIpGoto || codeStyle == GenSplit )
+ {
+ /* For directly executable machines there is no required state
+ * ordering. Choose a depth-first ordering to increase the
+ * potential for fall-throughs. */
+ redFsm->depthFirstOrdering();
+ }
+ else {
+ /* The frontend will do this for us, but it may be a good idea to
+ * force it if the intermediate file is edited. */
+ redFsm->sortByStateId();
+ }
+
+ /* Choose default transitions and the single transition. */
+ redFsm->chooseDefaultSpan();
+
+ /* Maybe do flat expand, otherwise choose single. */
+ if ( codeStyle == GenFlat || codeStyle == GenFFlat )
+ redFsm->makeFlat();
+ else
+ redFsm->chooseSingle();
+
+ /* If any errors have occured in the input file then don't write anything. */
+ if ( gblErrorCount > 0 )
+ return;
+
+ if ( codeStyle == GenSplit )
+ redFsm->partitionFsm( numSplitPartitions );
+
+ if ( codeStyle == GenIpGoto || codeStyle == GenSplit )
+ redFsm->setInTrans();
+
+ /* Anlayze Machine will find the final action reference counts, among
+ * other things. We will use these in reporting the usage
+ * of fsm directives in action code. */
+ analyzeMachine();
+
+ /* Determine if we should use indicies. */
+ calcIndexSize();
+}
+
+ostream &FsmCodeGen::source_warning( const InputLoc &loc )
+{
+ cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": warning: ";
+ return cerr;
+}
+
+ostream &FsmCodeGen::source_error( const InputLoc &loc )
+{
+ gblErrorCount += 1;
+ assert( sourceFileName != 0 );
+ cerr << sourceFileName << ":" << loc.line << ":" << loc.col << ": ";
+ return cerr;
+}
+
diff --git a/contrib/tools/ragel5/rlgen-cd/fsmcodegen.h b/contrib/tools/ragel5/rlgen-cd/fsmcodegen.h
new file mode 100644
index 0000000000..77c76f1b1a
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/fsmcodegen.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FSMCODEGEN_H
+#define _FSMCODEGEN_H
+
+#include <iostream>
+#include <string>
+#include <stdio.h>
+#include "common.h"
+#include "gendata.h"
+
+using std::string;
+using std::ostream;
+
+/* Integer array line length. */
+#define IALL 8
+
+/* Forwards. */
+struct RedFsmAp;
+struct RedStateAp;
+struct CodeGenData;
+struct Action;
+struct NameInst;
+struct InlineItem;
+struct InlineList;
+struct RedAction;
+struct LongestMatch;
+struct LongestMatchPart;
+
+inline string itoa( int i )
+{
+ char buf[16];
+ sprintf( buf, "%i", i );
+ return buf;
+}
+
+/*
+ * class FsmCodeGen
+ */
+class FsmCodeGen : public CodeGenData
+{
+public:
+ FsmCodeGen( ostream &out );
+ virtual ~FsmCodeGen() {}
+
+ virtual void finishRagelDef();
+ virtual void writeInit();
+
+protected:
+ string FSM_NAME();
+ string START_STATE_ID();
+ ostream &ACTIONS_ARRAY();
+ string GET_WIDE_KEY();
+ string GET_WIDE_KEY( RedStateAp *state );
+ string TABS( int level );
+ string KEY( Key key );
+ string LDIR_PATH( char *path );
+ void ACTION( ostream &ret, Action *action, int targState, bool inFinish );
+ void CONDITION( ostream &ret, Action *condition );
+ string ALPH_TYPE();
+ string WIDE_ALPH_TYPE();
+ string ARRAY_TYPE( unsigned long maxVal );
+
+ virtual string ARR_OFF( const string& ptr, const string& offset ) = 0;
+ virtual string CAST( const string& type ) = 0;
+ virtual string UINT() = 0;
+ virtual string NULL_ITEM() = 0;
+ virtual string POINTER() = 0;
+ virtual string GET_KEY();
+ virtual ostream &SWITCH_DEFAULT() = 0;
+
+ string P() { return "p"; }
+ string PE() { return "pe"; }
+
+ string ACCESS();
+ string CS();
+ string STACK() { return ACCESS() + "stack"; }
+ string TOP() { return ACCESS() + "top"; }
+ string TOKSTART() { return ACCESS() + "tokstart"; }
+ string TOKEND() { return ACCESS() + "tokend"; }
+ string ACT() { return ACCESS() + "act"; }
+
+ string DATA_PREFIX();
+ string PM() { return "_" + DATA_PREFIX() + "partition_map"; }
+ string C() { return "_" + DATA_PREFIX() + "cond_spaces"; }
+ string CK() { return "_" + DATA_PREFIX() + "cond_keys"; }
+ string K() { return "_" + DATA_PREFIX() + "trans_keys"; }
+ string I() { return "_" + DATA_PREFIX() + "indicies"; }
+ string CO() { return "_" + DATA_PREFIX() + "cond_offsets"; }
+ string KO() { return "_" + DATA_PREFIX() + "key_offsets"; }
+ string IO() { return "_" + DATA_PREFIX() + "index_offsets"; }
+ string CL() { return "_" + DATA_PREFIX() + "cond_lengths"; }
+ string SL() { return "_" + DATA_PREFIX() + "single_lengths"; }
+ string RL() { return "_" + DATA_PREFIX() + "range_lengths"; }
+ string A() { return "_" + DATA_PREFIX() + "actions"; }
+ string TA() { return "_" + DATA_PREFIX() + "trans_actions_wi"; }
+ string TT() { return "_" + DATA_PREFIX() + "trans_targs_wi"; }
+ string TSA() { return "_" + DATA_PREFIX() + "to_state_actions"; }
+ string FSA() { return "_" + DATA_PREFIX() + "from_state_actions"; }
+ string EA() { return "_" + DATA_PREFIX() + "eof_actions"; }
+ string SP() { return "_" + DATA_PREFIX() + "key_spans"; }
+ string CSP() { return "_" + DATA_PREFIX() + "cond_key_spans"; }
+ string START() { return DATA_PREFIX() + "start"; }
+ string ERROR() { return DATA_PREFIX() + "error"; }
+ string FIRST_FINAL() { return DATA_PREFIX() + "first_final"; }
+ string CTXDATA() { return DATA_PREFIX() + "ctxdata"; }
+
+ void INLINE_LIST( ostream &ret, InlineList *inlineList, int targState, bool inFinish );
+ virtual void GOTO( ostream &ret, int gotoDest, bool inFinish ) = 0;
+ virtual void CALL( ostream &ret, int callDest, int targState, bool inFinish ) = 0;
+ virtual void NEXT( ostream &ret, int nextDest, bool inFinish ) = 0;
+ virtual void GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish ) = 0;
+ virtual void NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish ) = 0;
+ virtual void CALL_EXPR( ostream &ret, InlineItem *ilItem,
+ int targState, bool inFinish ) = 0;
+ virtual void RET( ostream &ret, bool inFinish ) = 0;
+ virtual void BREAK( ostream &ret, int targState ) = 0;
+ virtual void CURS( ostream &ret, bool inFinish ) = 0;
+ virtual void TARGS( ostream &ret, bool inFinish, int targState ) = 0;
+ void EXEC( ostream &ret, InlineItem *item, int targState, int inFinish );
+ void EXECTE( ostream &ret, InlineItem *item, int targState, int inFinish );
+ void LM_SWITCH( ostream &ret, InlineItem *item, int targState, int inFinish );
+ void SET_ACT( ostream &ret, InlineItem *item );
+ void INIT_TOKSTART( ostream &ret, InlineItem *item );
+ void INIT_ACT( ostream &ret, InlineItem *item );
+ void SET_TOKSTART( ostream &ret, InlineItem *item );
+ void SET_TOKEND( ostream &ret, InlineItem *item );
+ void GET_TOKEND( ostream &ret, InlineItem *item );
+ void SUB_ACTION( ostream &ret, InlineItem *item,
+ int targState, bool inFinish );
+ void STATE_IDS();
+
+ string ERROR_STATE();
+ string FIRST_FINAL_STATE();
+
+ virtual string PTR_CONST() = 0;
+ virtual ostream &OPEN_ARRAY( const string& type, const string& name ) = 0;
+ virtual ostream &CLOSE_ARRAY() = 0;
+ virtual ostream &STATIC_VAR( const string& type, const string& name ) = 0;
+
+ virtual string CTRL_FLOW() = 0;
+
+ ostream &source_warning(const InputLoc &loc);
+ ostream &source_error(const InputLoc &loc);
+
+ unsigned int arrayTypeSize( unsigned long maxVal );
+
+ bool outLabelUsed;
+ bool againLabelUsed;
+ bool useIndicies;
+
+public:
+ /* Determine if we should use indicies. */
+ virtual void calcIndexSize() {}
+};
+
+class CCodeGen : virtual public FsmCodeGen
+{
+public:
+ CCodeGen( ostream &out ) : FsmCodeGen(out) {}
+
+ virtual string NULL_ITEM();
+ virtual string POINTER();
+ virtual ostream &SWITCH_DEFAULT();
+ virtual ostream &OPEN_ARRAY( const string& type, const string& name );
+ virtual ostream &CLOSE_ARRAY();
+ virtual ostream &STATIC_VAR( const string& type, const string& name );
+ virtual string ARR_OFF( const string& ptr, const string& offset );
+ virtual string CAST( const string& type );
+ virtual string UINT();
+ virtual string PTR_CONST();
+ virtual string CTRL_FLOW();
+
+ virtual void writeExports();
+};
+
+class DCodeGen : virtual public FsmCodeGen
+{
+public:
+ DCodeGen( ostream &out ) : FsmCodeGen(out) {}
+
+ virtual string NULL_ITEM();
+ virtual string POINTER();
+ virtual ostream &SWITCH_DEFAULT();
+ virtual ostream &OPEN_ARRAY( const string& type, const string& name );
+ virtual ostream &CLOSE_ARRAY();
+ virtual ostream &STATIC_VAR( const string& type, const string& name );
+ virtual string ARR_OFF( const string& ptr, const string& offset );
+ virtual string CAST( const string& type );
+ virtual string UINT();
+ virtual string PTR_CONST();
+ virtual string CTRL_FLOW();
+
+ virtual void writeExports();
+};
+
+#endif /* _FSMCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/ftabcodegen.cpp b/contrib/tools/ragel5/rlgen-cd/ftabcodegen.cpp
new file mode 100644
index 0000000000..1d65e7102c
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/ftabcodegen.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlgen-cd.h"
+#include "ftabcodegen.h"
+#include "redfsm.h"
+#include "gendata.h"
+
+/* Determine if we should use indicies or not. */
+void FTabCodeGen::calcIndexSize()
+{
+ int sizeWithInds = 0, sizeWithoutInds = 0;
+
+ /* Calculate cost of using with indicies. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ int totalIndex = st->outSingle.length() + st->outRange.length() +
+ (st->defTrans == 0 ? 0 : 1);
+ sizeWithInds += arrayTypeSize(redFsm->maxIndex) * totalIndex;
+ }
+ sizeWithInds += arrayTypeSize(redFsm->maxState) * redFsm->transSet.length();
+ if ( redFsm->anyActions() )
+ sizeWithInds += arrayTypeSize(redFsm->maxActListId) * redFsm->transSet.length();
+
+ /* Calculate the cost of not using indicies. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ int totalIndex = st->outSingle.length() + st->outRange.length() +
+ (st->defTrans == 0 ? 0 : 1);
+ sizeWithoutInds += arrayTypeSize(redFsm->maxState) * totalIndex;
+ if ( redFsm->anyActions() )
+ sizeWithoutInds += arrayTypeSize(redFsm->maxActListId) * totalIndex;
+ }
+
+ /* If using indicies reduces the size, use them. */
+ useIndicies = sizeWithInds < sizeWithoutInds;
+}
+
+std::ostream &FTabCodeGen::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->actListId+1;
+ out << act;
+ return out;
+}
+
+std::ostream &FTabCodeGen::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->actListId+1;
+ out << act;
+ return out;
+}
+
+std::ostream &FTabCodeGen::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->actListId+1;
+ out << act;
+ return out;
+}
+
+
+/* Write out the function for a transition. */
+std::ostream &FTabCodeGen::TRANS_ACTION( RedTransAp *trans )
+{
+ int action = 0;
+ if ( trans->action != 0 )
+ action = trans->action->actListId+1;
+ out << action;
+ return out;
+}
+
+/* Write out the function switch. This switch is keyed on the values
+ * of the func index. */
+std::ostream &FTabCodeGen::TO_STATE_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numToStateRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+/* Write out the function switch. This switch is keyed on the values
+ * of the func index. */
+std::ostream &FTabCodeGen::FROM_STATE_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numFromStateRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &FTabCodeGen::EOF_ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numEofRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, true );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+/* Write out the function switch. This switch is keyed on the values
+ * of the func index. */
+std::ostream &FTabCodeGen::ACTION_SWITCH()
+{
+ /* Loop the actions. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numTransRefs > 0 ) {
+ /* Write the entry label. */
+ out << "\tcase " << redAct->actListId+1 << ":\n";
+
+ /* Write each action in the list of action items. */
+ for ( ActionTable::Iter item = redAct->key; item.lte(); item++ )
+ ACTION( out, item->value, 0, false );
+
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+void FTabCodeGen::writeData()
+{
+ if ( redFsm->anyConditions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondOffset), CO() );
+ COND_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondLen), CL() );
+ COND_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), CK() );
+ COND_KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondSpaceId), C() );
+ COND_SPACES();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxKeyOffset), KO() );
+ KEY_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), K() );
+ KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxSingleLen), SL() );
+ SINGLE_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxRangeLen), RL() );
+ RANGE_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxIndexOffset), IO() );
+ INDEX_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( useIndicies ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxIndex), I() );
+ INDICIES();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxState), TT() );
+ TRANS_TARGS_WI();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActListId), TA() );
+ TRANS_ACTIONS_WI();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+ }
+ else {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxState), TT() );
+ TRANS_TARGS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActListId), TA() );
+ TRANS_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+ }
+
+ if ( redFsm->anyToStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TSA() );
+ TO_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), FSA() );
+ FROM_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyEofActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActListId), EA() );
+ EOF_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ STATE_IDS();
+}
+
+void FTabCodeGen::writeExec()
+{
+ outLabelUsed = false;
+
+ out <<
+ " {\n"
+ " int _klen";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << ", _ps";
+
+ out <<
+ ";\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_keys;\n"
+ " int _trans;\n";
+
+ if ( redFsm->anyConditions() )
+ out << " " << WIDE_ALPH_TYPE() << " _widec;\n";
+
+ out << "\n";
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+
+ out << "_resume:\n";
+
+ if ( redFsm->errState != 0 ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << CS() << " == " << redFsm->errState->id << " )\n"
+ " goto _out;\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ out <<
+ " switch ( " << FSA() << "[" << CS() << "] ) {\n";
+ FROM_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ }
+
+ if ( redFsm->anyConditions() )
+ COND_TRANSLATE();
+
+ LOCATE_TRANS();
+
+ out << "_match:\n";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << " _ps = " << CS() << ";\n";
+
+ if ( useIndicies )
+ out << " _trans = " << I() << "[_trans];\n";
+
+ out <<
+ " " << CS() << " = " << TT() << "[_trans];\n"
+ "\n";
+
+ if ( redFsm->anyRegActions() ) {
+ out <<
+ " if ( " << TA() << "[_trans] == 0 )\n"
+ " goto _again;\n"
+ "\n"
+ " switch ( " << TA() << "[_trans] ) {\n";
+ ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ }
+
+ if ( redFsm->anyRegActions() || redFsm->anyActionGotos() ||
+ redFsm->anyActionCalls() || redFsm->anyActionRets() )
+ out << "_again:\n";
+
+ if ( redFsm->anyToStateActions() ) {
+ out <<
+ " switch ( " << TSA() << "[" << CS() << "] ) {\n";
+ TO_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ }
+
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " != " << PE() << " )\n"
+ " goto _resume;\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n"
+ " goto _resume;\n";
+ }
+
+
+ if ( outLabelUsed )
+ out << " _out: {}\n";
+
+ out << " }\n";
+}
+
+
+void FTabCodeGen::writeEOF()
+{
+ if ( redFsm->anyEofActions() ) {
+ out <<
+ " {\n"
+ " switch ( " << EA() << "[" << CS() << "] ) {\n";
+ EOF_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+}
diff --git a/contrib/tools/ragel5/rlgen-cd/ftabcodegen.h b/contrib/tools/ragel5/rlgen-cd/ftabcodegen.h
new file mode 100644
index 0000000000..9d26d1cadd
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/ftabcodegen.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FTABCODEGEN_H
+#define _FTABCODEGEN_H
+
+#include <iostream>
+#include "tabcodegen.h"
+
+/* Forwards. */
+struct CodeGenData;
+
+
+/*
+ * FTabCodeG\verb|e
+ */
+class FTabCodeGen : public TabCodeGen
+{
+protected:
+ FTabCodeGen( ostream &out ) : FsmCodeGen(out), TabCodeGen(out) {}
+
+ std::ostream &TO_STATE_ACTION_SWITCH();
+ std::ostream &FROM_STATE_ACTION_SWITCH();
+ std::ostream &EOF_ACTION_SWITCH();
+ std::ostream &ACTION_SWITCH();
+
+ virtual std::ostream &TO_STATE_ACTION( RedStateAp *state );
+ virtual std::ostream &FROM_STATE_ACTION( RedStateAp *state );
+ virtual std::ostream &EOF_ACTION( RedStateAp *state );
+ virtual std::ostream &TRANS_ACTION( RedTransAp *trans );
+ virtual void writeData();
+ virtual void writeEOF();
+ virtual void writeExec();
+ virtual void calcIndexSize();
+};
+
+
+/*
+ * CFTabCodeGen
+ */
+struct CFTabCodeGen
+ : public FTabCodeGen, public CCodeGen
+{
+ CFTabCodeGen( ostream &out ) :
+ FsmCodeGen(out), FTabCodeGen(out), CCodeGen(out) {}
+};
+
+/*
+ * class DFTabCodeGen
+ */
+struct DFTabCodeGen
+ : public FTabCodeGen, public DCodeGen
+{
+ DFTabCodeGen( ostream &out ) :
+ FsmCodeGen(out), FTabCodeGen(out), DCodeGen(out) {}
+};
+
+#endif /* _FTABCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/gotocodegen.cpp b/contrib/tools/ragel5/rlgen-cd/gotocodegen.cpp
new file mode 100644
index 0000000000..13be67d097
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/gotocodegen.cpp
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlgen-cd.h"
+#include "gotocodegen.h"
+#include "redfsm.h"
+#include "bstmap.h"
+#include "gendata.h"
+
+/* Emit the goto to take for a given transition. */
+std::ostream &GotoCodeGen::TRANS_GOTO( RedTransAp *trans, int level )
+{
+ out << TABS(level) << "goto tr" << trans->id << ";";
+ return out;
+}
+
+std::ostream &GotoCodeGen::TO_STATE_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numToStateRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &GotoCodeGen::FROM_STATE_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numFromStateRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &GotoCodeGen::EOF_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numEofRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, true );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &GotoCodeGen::ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numTransRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+void GotoCodeGen::GOTO_HEADER( RedStateAp *state )
+{
+ /* Label the state. */
+ out << "case " << state->id << ":\n";
+}
+
+
+void GotoCodeGen::emitSingleSwitch( RedStateAp *state )
+{
+ /* Load up the singles. */
+ int numSingles = state->outSingle.length();
+ RedTransEl *data = state->outSingle.data;
+
+ if ( numSingles == 1 ) {
+ /* If there is a single single key then write it out as an if. */
+ out << "\tif ( " << GET_WIDE_KEY(state) << " == " <<
+ KEY(data[0].lowKey) << " )\n\t\t";
+
+ /* Virtual function for writing the target of the transition. */
+ TRANS_GOTO(data[0].value, 0) << "\n";
+ }
+ else if ( numSingles > 1 ) {
+ /* Write out single keys in a switch if there is more than one. */
+ out << "\tswitch( " << GET_WIDE_KEY(state) << " ) {\n";
+
+ /* Write out the single indicies. */
+ for ( int j = 0; j < numSingles; j++ ) {
+ out << "\t\tcase " << KEY(data[j].lowKey) << ": ";
+ TRANS_GOTO(data[j].value, 0) << "\n";
+ }
+
+ /* Emits a default case for D code. */
+ SWITCH_DEFAULT();
+
+ /* Close off the transition switch. */
+ out << "\t}\n";
+ }
+}
+
+void GotoCodeGen::emitRangeBSearch( RedStateAp *state, int level, int low, int high )
+{
+ /* Get the mid position, staying on the lower end of the range. */
+ int mid = (low + high) >> 1;
+ RedTransEl *data = state->outRange.data;
+
+ /* Determine if we need to look higher or lower. */
+ bool anyLower = mid > low;
+ bool anyHigher = mid < high;
+
+ /* Determine if the keys at mid are the limits of the alphabet. */
+ bool limitLow = data[mid].lowKey == keyOps->minKey;
+ bool limitHigh = data[mid].highKey == keyOps->maxKey;
+
+ if ( anyLower && anyHigher ) {
+ /* Can go lower and higher than mid. */
+ out << TABS(level) << "if ( " << GET_WIDE_KEY(state) << " < " <<
+ KEY(data[mid].lowKey) << " ) {\n";
+ emitRangeBSearch( state, level+1, low, mid-1 );
+ out << TABS(level) << "} else if ( " << GET_WIDE_KEY(state) << " > " <<
+ KEY(data[mid].highKey) << " ) {\n";
+ emitRangeBSearch( state, level+1, mid+1, high );
+ out << TABS(level) << "} else\n";
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ else if ( anyLower && !anyHigher ) {
+ /* Can go lower than mid but not higher. */
+ out << TABS(level) << "if ( " << GET_WIDE_KEY(state) << " < " <<
+ KEY(data[mid].lowKey) << " ) {\n";
+ emitRangeBSearch( state, level+1, low, mid-1 );
+
+ /* if the higher is the highest in the alphabet then there is no
+ * sense testing it. */
+ if ( limitHigh ) {
+ out << TABS(level) << "} else\n";
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ else {
+ out << TABS(level) << "} else if ( " << GET_WIDE_KEY(state) << " <= " <<
+ KEY(data[mid].highKey) << " )\n";
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ }
+ else if ( !anyLower && anyHigher ) {
+ /* Can go higher than mid but not lower. */
+ out << TABS(level) << "if ( " << GET_WIDE_KEY(state) << " > " <<
+ KEY(data[mid].highKey) << " ) {\n";
+ emitRangeBSearch( state, level+1, mid+1, high );
+
+ /* If the lower end is the lowest in the alphabet then there is no
+ * sense testing it. */
+ if ( limitLow ) {
+ out << TABS(level) << "} else\n";
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ else {
+ out << TABS(level) << "} else if ( " << GET_WIDE_KEY(state) << " >= " <<
+ KEY(data[mid].lowKey) << " )\n";
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ }
+ else {
+ /* Cannot go higher or lower than mid. It's mid or bust. What
+ * tests to do depends on limits of alphabet. */
+ if ( !limitLow && !limitHigh ) {
+ out << TABS(level) << "if ( " << KEY(data[mid].lowKey) << " <= " <<
+ GET_WIDE_KEY(state) << " && " << GET_WIDE_KEY(state) << " <= " <<
+ KEY(data[mid].highKey) << " )\n";
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ else if ( limitLow && !limitHigh ) {
+ out << TABS(level) << "if ( " << GET_WIDE_KEY(state) << " <= " <<
+ KEY(data[mid].highKey) << " )\n";
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ else if ( !limitLow && limitHigh ) {
+ out << TABS(level) << "if ( " << KEY(data[mid].lowKey) << " <= " <<
+ GET_WIDE_KEY(state) << " )\n";
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ else {
+ /* Both high and low are at the limit. No tests to do. */
+ TRANS_GOTO(data[mid].value, level+1) << "\n";
+ }
+ }
+}
+
+void GotoCodeGen::STATE_GOTO_ERROR()
+{
+ /* Label the state and bail immediately. */
+ outLabelUsed = true;
+ RedStateAp *state = redFsm->errState;
+ out << "case " << state->id << ":\n";
+ out << " goto _out;\n";
+}
+
+void GotoCodeGen::COND_TRANSLATE( StateCond *stateCond, int level )
+{
+ CondSpace *condSpace = stateCond->condSpace;
+ out << TABS(level) << "_widec = " << CAST(WIDE_ALPH_TYPE()) << "(" <<
+ KEY(condSpace->baseKey) << " + (" << GET_KEY() <<
+ " - " << KEY(keyOps->minKey) << "));\n";
+
+ for ( CondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) {
+ out << TABS(level) << "if ( ";
+ CONDITION( out, *csi );
+ Size condValOffset = ((1 << csi.pos()) * keyOps->alphSize());
+ out << " ) _widec += " << condValOffset << ";\n";
+ }
+}
+
+void GotoCodeGen::emitCondBSearch( RedStateAp *state, int level, int low, int high )
+{
+ /* Get the mid position, staying on the lower end of the range. */
+ int mid = (low + high) >> 1;
+ StateCond **data = state->stateCondVect.data;
+
+ /* Determine if we need to look higher or lower. */
+ bool anyLower = mid > low;
+ bool anyHigher = mid < high;
+
+ /* Determine if the keys at mid are the limits of the alphabet. */
+ bool limitLow = data[mid]->lowKey == keyOps->minKey;
+ bool limitHigh = data[mid]->highKey == keyOps->maxKey;
+
+ if ( anyLower && anyHigher ) {
+ /* Can go lower and higher than mid. */
+ out << TABS(level) << "if ( " << GET_KEY() << " < " <<
+ KEY(data[mid]->lowKey) << " ) {\n";
+ emitCondBSearch( state, level+1, low, mid-1 );
+ out << TABS(level) << "} else if ( " << GET_KEY() << " > " <<
+ KEY(data[mid]->highKey) << " ) {\n";
+ emitCondBSearch( state, level+1, mid+1, high );
+ out << TABS(level) << "} else {\n";
+ COND_TRANSLATE(data[mid], level+1);
+ out << TABS(level) << "}\n";
+ }
+ else if ( anyLower && !anyHigher ) {
+ /* Can go lower than mid but not higher. */
+ out << TABS(level) << "if ( " << GET_KEY() << " < " <<
+ KEY(data[mid]->lowKey) << " ) {\n";
+ emitCondBSearch( state, level+1, low, mid-1 );
+
+ /* if the higher is the highest in the alphabet then there is no
+ * sense testing it. */
+ if ( limitHigh ) {
+ out << TABS(level) << "} else {\n";
+ COND_TRANSLATE(data[mid], level+1);
+ out << TABS(level) << "}\n";
+ }
+ else {
+ out << TABS(level) << "} else if ( " << GET_KEY() << " <= " <<
+ KEY(data[mid]->highKey) << " ) {\n";
+ COND_TRANSLATE(data[mid], level+1);
+ out << TABS(level) << "}\n";
+ }
+ }
+ else if ( !anyLower && anyHigher ) {
+ /* Can go higher than mid but not lower. */
+ out << TABS(level) << "if ( " << GET_KEY() << " > " <<
+ KEY(data[mid]->highKey) << " ) {\n";
+ emitCondBSearch( state, level+1, mid+1, high );
+
+ /* If the lower end is the lowest in the alphabet then there is no
+ * sense testing it. */
+ if ( limitLow ) {
+ out << TABS(level) << "} else {\n";
+ COND_TRANSLATE(data[mid], level+1);
+ out << TABS(level) << "}\n";
+ }
+ else {
+ out << TABS(level) << "} else if ( " << GET_KEY() << " >= " <<
+ KEY(data[mid]->lowKey) << " ) {\n";
+ COND_TRANSLATE(data[mid], level+1);
+ out << TABS(level) << "}\n";
+ }
+ }
+ else {
+ /* Cannot go higher or lower than mid. It's mid or bust. What
+ * tests to do depends on limits of alphabet. */
+ if ( !limitLow && !limitHigh ) {
+ out << TABS(level) << "if ( " << KEY(data[mid]->lowKey) << " <= " <<
+ GET_KEY() << " && " << GET_KEY() << " <= " <<
+ KEY(data[mid]->highKey) << " ) {\n";
+ COND_TRANSLATE(data[mid], level+1);
+ out << TABS(level) << "}\n";
+ }
+ else if ( limitLow && !limitHigh ) {
+ out << TABS(level) << "if ( " << GET_KEY() << " <= " <<
+ KEY(data[mid]->highKey) << " ) {\n";
+ COND_TRANSLATE(data[mid], level+1);
+ out << TABS(level) << "}\n";
+ }
+ else if ( !limitLow && limitHigh ) {
+ out << TABS(level) << "if ( " << KEY(data[mid]->lowKey) << " <= " <<
+ GET_KEY() << " )\n {";
+ COND_TRANSLATE(data[mid], level+1);
+ out << TABS(level) << "}\n";
+ }
+ else {
+ /* Both high and low are at the limit. No tests to do. */
+ COND_TRANSLATE(data[mid], level);
+ }
+ }
+}
+
+std::ostream &GotoCodeGen::STATE_GOTOS()
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st == redFsm->errState )
+ STATE_GOTO_ERROR();
+ else {
+ /* Writing code above state gotos. */
+ GOTO_HEADER( st );
+
+ if ( st->stateCondVect.length() > 0 ) {
+ out << " _widec = " << GET_KEY() << ";\n";
+ emitCondBSearch( st, 1, 0, st->stateCondVect.length() - 1 );
+ }
+
+ /* Try singles. */
+ if ( st->outSingle.length() > 0 )
+ emitSingleSwitch( st );
+
+ /* Default case is to binary search for the ranges, if that fails then */
+ if ( st->outRange.length() > 0 )
+ emitRangeBSearch( st, 1, 0, st->outRange.length() - 1 );
+
+ /* Write the default transition. */
+ TRANS_GOTO( st->defTrans, 1 ) << "\n";
+ }
+ }
+ return out;
+}
+
+std::ostream &GotoCodeGen::TRANSITIONS()
+{
+ /* Emit any transitions that have functions and that go to
+ * this state. */
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
+ /* Write the label for the transition so it can be jumped to. */
+ out << " tr" << trans->id << ": ";
+
+ /* Destination state. */
+ if ( trans->action != 0 && trans->action->anyCurStateRef() )
+ out << "_ps = " << CS() << ";";
+ out << CS() << " = " << trans->targ->id << "; ";
+
+ if ( trans->action != 0 ) {
+ /* Write out the transition func. */
+ out << "goto f" << trans->action->actListId << ";\n";
+ }
+ else {
+ /* No code to execute, just loop around. */
+ out << "goto _again;\n";
+ }
+ }
+ return out;
+}
+
+std::ostream &GotoCodeGen::EXEC_FUNCS()
+{
+ /* Make labels that set acts and jump to execFuncs. Loop func indicies. */
+ for ( ActionTableMap::Iter redAct = redFsm->actionMap; redAct.lte(); redAct++ ) {
+ if ( redAct->numTransRefs > 0 ) {
+ out << " f" << redAct->actListId << ": " <<
+ "_acts = " << ARR_OFF(A(), itoa( redAct->location+1 ) ) << ";"
+ " goto execFuncs;\n";
+ }
+ }
+
+ out <<
+ "\n"
+ "execFuncs:\n"
+ " _nacts = *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ " goto _again;\n";
+ return out;
+}
+
+unsigned int GotoCodeGen::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->location+1;
+ return act;
+}
+
+unsigned int GotoCodeGen::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->location+1;
+ return act;
+}
+
+unsigned int GotoCodeGen::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->location+1;
+ return act;
+}
+
+std::ostream &GotoCodeGen::TO_STATE_ACTIONS()
+{
+ /* Take one off for the psuedo start state. */
+ int numStates = redFsm->stateList.length();
+ unsigned int *vals = new unsigned int[numStates];
+ memset( vals, 0, sizeof(unsigned int)*numStates );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ vals[st->id] = TO_STATE_ACTION(st);
+
+ out << "\t";
+ for ( int st = 0; st < redFsm->nextStateId; st++ ) {
+ /* Write any eof action. */
+ out << vals[st];
+ if ( st < numStates-1 ) {
+ out << ", ";
+ if ( (st+1) % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ delete[] vals;
+ return out;
+}
+
+std::ostream &GotoCodeGen::FROM_STATE_ACTIONS()
+{
+ /* Take one off for the psuedo start state. */
+ int numStates = redFsm->stateList.length();
+ unsigned int *vals = new unsigned int[numStates];
+ memset( vals, 0, sizeof(unsigned int)*numStates );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ vals[st->id] = FROM_STATE_ACTION(st);
+
+ out << "\t";
+ for ( int st = 0; st < redFsm->nextStateId; st++ ) {
+ /* Write any eof action. */
+ out << vals[st];
+ if ( st < numStates-1 ) {
+ out << ", ";
+ if ( (st+1) % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ delete[] vals;
+ return out;
+}
+
+std::ostream &GotoCodeGen::EOF_ACTIONS()
+{
+ /* Take one off for the psuedo start state. */
+ int numStates = redFsm->stateList.length();
+ unsigned int *vals = new unsigned int[numStates];
+ memset( vals, 0, sizeof(unsigned int)*numStates );
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ vals[st->id] = EOF_ACTION(st);
+
+ out << "\t";
+ for ( int st = 0; st < redFsm->nextStateId; st++ ) {
+ /* Write any eof action. */
+ out << vals[st];
+ if ( st < numStates-1 ) {
+ out << ", ";
+ if ( (st+1) % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ delete[] vals;
+ return out;
+}
+
+std::ostream &GotoCodeGen::FINISH_CASES()
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* States that are final and have an out action need a case. */
+ if ( st->eofAction != 0 ) {
+ /* Write the case label. */
+ out << "\t\tcase " << st->id << ": ";
+
+ /* Write the goto func. */
+ out << "goto f" << st->eofAction->actListId << ";\n";
+ }
+ }
+
+ return out;
+}
+
+void GotoCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish )
+{
+ ret << "{" << CS() << " = " << gotoDest << "; " <<
+ CTRL_FLOW() << "goto _again;}";
+}
+
+void GotoCodeGen::GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << "{" << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << "); " << CTRL_FLOW() << "goto _again;}";
+}
+
+void GotoCodeGen::CURS( ostream &ret, bool inFinish )
+{
+ ret << "(_ps)";
+}
+
+void GotoCodeGen::TARGS( ostream &ret, bool inFinish, int targState )
+{
+ ret << "(" << CS() << ")";
+}
+
+void GotoCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
+{
+ ret << CS() << " = " << nextDest << ";";
+}
+
+void GotoCodeGen::NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << ");";
+}
+
+void GotoCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
+{
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = " <<
+ callDest << "; " << CTRL_FLOW() << "goto _again;}";
+}
+
+void GotoCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
+{
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, targState, inFinish );
+ ret << "); " << CTRL_FLOW() << "goto _again;}";
+}
+
+void GotoCodeGen::RET( ostream &ret, bool inFinish )
+{
+ ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "]; " <<
+ CTRL_FLOW() << "goto _again;}";
+}
+
+void GotoCodeGen::BREAK( ostream &ret, int targState )
+{
+ outLabelUsed = true;
+ ret << CTRL_FLOW() << "goto _out;";
+}
+
+void GotoCodeGen::writeData()
+{
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActArrItem), A() );
+ ACTIONS_ARRAY();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyToStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TSA() );
+ TO_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), FSA() );
+ FROM_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyEofActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), EA() );
+ EOF_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ STATE_IDS();
+}
+
+void GotoCodeGen::writeExec()
+{
+ outLabelUsed = false;
+
+ out << " {\n";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << " int _ps = 0;\n";
+
+ if ( redFsm->anyToStateActions() || redFsm->anyRegActions()
+ || redFsm->anyFromStateActions() )
+ {
+ out <<
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxActArrItem) << POINTER() << "_acts;\n"
+ " " << UINT() << " _nacts;\n";
+ }
+
+ if ( redFsm->anyConditions() )
+ out << " " << WIDE_ALPH_TYPE() << " _widec;\n";
+
+ out << "\n";
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+
+ out << "_resume:\n";
+
+ if ( redFsm->anyFromStateActions() ) {
+ out <<
+ " _acts = " << ARR_OFF( A(), FSA() + "[" + CS() + "]" ) << ";\n"
+ " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ FROM_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+
+ out <<
+ " switch ( " << CS() << " ) {\n";
+ STATE_GOTOS();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ "\n";
+ TRANSITIONS() <<
+ "\n";
+
+ if ( redFsm->anyRegActions() )
+ EXEC_FUNCS() << "\n";
+
+ out << "_again:\n";
+
+ if ( redFsm->anyToStateActions() ) {
+ out <<
+ " _acts = " << ARR_OFF( A(), TSA() + "[" + CS() + "]" ) << ";\n"
+ " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ TO_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " != " << PE() << " )\n"
+ " goto _resume;\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n"
+ " goto _resume;\n";
+ }
+
+ if ( outLabelUsed )
+ out << " _out: {}\n";
+
+ out << " }\n";
+}
+
+void GotoCodeGen::writeEOF()
+{
+ if ( redFsm->anyEofActions() ) {
+ out <<
+ " {\n"
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxActArrItem) << POINTER() << "_acts = " <<
+ ARR_OFF( A(), EA() + "[" + CS() + "]" ) << ";\n"
+ " " << UINT() << " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ EOF_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n";
+ }
+}
diff --git a/contrib/tools/ragel5/rlgen-cd/gotocodegen.h b/contrib/tools/ragel5/rlgen-cd/gotocodegen.h
new file mode 100644
index 0000000000..625c2c23bd
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/gotocodegen.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _GOTOCODEGEN_H
+#define _GOTOCODEGEN_H
+
+#include <iostream>
+#include "fsmcodegen.h"
+
+/* Forwards. */
+struct CodeGenData;
+struct NameInst;
+struct RedTransAp;
+struct RedStateAp;
+struct StateCond;
+
+/*
+ * Goto driven fsm.
+ */
+class GotoCodeGen : virtual public FsmCodeGen
+{
+public:
+ GotoCodeGen( ostream &out ) : FsmCodeGen(out) {}
+ std::ostream &TO_STATE_ACTION_SWITCH();
+ std::ostream &FROM_STATE_ACTION_SWITCH();
+ std::ostream &EOF_ACTION_SWITCH();
+ std::ostream &ACTION_SWITCH();
+ std::ostream &STATE_GOTOS();
+ std::ostream &TRANSITIONS();
+ std::ostream &EXEC_FUNCS();
+ std::ostream &FINISH_CASES();
+
+ void GOTO( ostream &ret, int gotoDest, bool inFinish );
+ void CALL( ostream &ret, int callDest, int targState, bool inFinish );
+ void NEXT( ostream &ret, int nextDest, bool inFinish );
+ void GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+ void NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+ void CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish );
+ void CURS( ostream &ret, bool inFinish );
+ void TARGS( ostream &ret, bool inFinish, int targState );
+ void RET( ostream &ret, bool inFinish );
+ void BREAK( ostream &ret, int targState );
+
+ virtual unsigned int TO_STATE_ACTION( RedStateAp *state );
+ virtual unsigned int FROM_STATE_ACTION( RedStateAp *state );
+ virtual unsigned int EOF_ACTION( RedStateAp *state );
+
+ std::ostream &TO_STATE_ACTIONS();
+ std::ostream &FROM_STATE_ACTIONS();
+ std::ostream &EOF_ACTIONS();
+
+ void COND_TRANSLATE( StateCond *stateCond, int level );
+ void emitCondBSearch( RedStateAp *state, int level, int low, int high );
+ void STATE_CONDS( RedStateAp *state, bool genDefault );
+
+ virtual std::ostream &TRANS_GOTO( RedTransAp *trans, int level );
+
+ void emitSingleSwitch( RedStateAp *state );
+ void emitRangeBSearch( RedStateAp *state, int level, int low, int high );
+
+ /* Called from STATE_GOTOS just before writing the gotos */
+ virtual void GOTO_HEADER( RedStateAp *state );
+ virtual void STATE_GOTO_ERROR();
+
+ virtual void writeData();
+ virtual void writeEOF();
+ virtual void writeExec();
+};
+
+/*
+ * class CGotoCodeGen
+ */
+struct CGotoCodeGen
+ : public GotoCodeGen, public CCodeGen
+{
+ CGotoCodeGen( ostream &out ) :
+ FsmCodeGen(out), GotoCodeGen(out), CCodeGen(out) {}
+};
+
+/*
+ * class DGotoCodeGen
+ */
+struct DGotoCodeGen
+ : public GotoCodeGen, public DCodeGen
+{
+ DGotoCodeGen( ostream &out ) :
+ FsmCodeGen(out), GotoCodeGen(out), DCodeGen(out) {}
+};
+
+
+#endif /* _GOTOCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/ipgotocodegen.cpp b/contrib/tools/ragel5/rlgen-cd/ipgotocodegen.cpp
new file mode 100644
index 0000000000..ed65be5fe0
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/ipgotocodegen.cpp
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlgen-cd.h"
+#include "ipgotocodegen.h"
+#include "redfsm.h"
+#include "gendata.h"
+#include "bstmap.h"
+
+bool IpGotoCodeGen::useAgainLabel()
+{
+ return redFsm->anyRegActionRets() ||
+ redFsm->anyRegActionByValControl() ||
+ redFsm->anyRegNextStmt();
+}
+
+void IpGotoCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish )
+{
+ ret << "{" << CTRL_FLOW() << "goto st" << gotoDest << ";}";
+}
+
+void IpGotoCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
+{
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << targState <<
+ "; " << CTRL_FLOW() << "goto st" << callDest << ";}";
+}
+
+void IpGotoCodeGen::RET( ostream &ret, bool inFinish )
+{
+ ret << "{" << CS() << " = " << STACK() << "[--" << TOP() << "]; " <<
+ CTRL_FLOW() << "goto _again;}";
+}
+
+void IpGotoCodeGen::GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << "{" << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << "); " << CTRL_FLOW() << "goto _again;}";
+}
+
+void IpGotoCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
+{
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << targState << "; " << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << "); " << CTRL_FLOW() << "goto _again;}";
+}
+
+void IpGotoCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
+{
+ ret << CS() << " = " << nextDest << ";";
+}
+
+void IpGotoCodeGen::NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << ");";
+}
+
+void IpGotoCodeGen::CURS( ostream &ret, bool inFinish )
+{
+ ret << "(_ps)";
+}
+
+void IpGotoCodeGen::TARGS( ostream &ret, bool inFinish, int targState )
+{
+ ret << targState;
+}
+
+void IpGotoCodeGen::BREAK( ostream &ret, int targState )
+{
+ ret << CTRL_FLOW() << "goto _out" << targState << ";";
+}
+
+bool IpGotoCodeGen::IN_TRANS_ACTIONS( RedStateAp *state )
+{
+ bool anyWritten = false;
+
+ /* Emit any transitions that have actions and that go to this state. */
+ for ( int it = 0; it < state->numInTrans; it++ ) {
+ RedTransAp *trans = state->inTrans[it];
+ if ( trans->action != 0 && trans->labelNeeded ) {
+ /* Remember that we wrote an action so we know to write the
+ * line directive for going back to the output. */
+ anyWritten = true;
+
+ /* Write the label for the transition so it can be jumped to. */
+ out << "tr" << trans->id << ":\n";
+
+ /* If the action contains a next, then we must preload the current
+ * state since the action may or may not set it. */
+ if ( trans->action->anyNextStmt() )
+ out << " " << CS() << " = " << trans->targ->id << ";\n";
+
+ /* Write each action in the list. */
+ for ( ActionTable::Iter item = trans->action->key; item.lte(); item++ )
+ ACTION( out, item->value, trans->targ->id, false );
+
+ /* If the action contains a next then we need to reload, otherwise
+ * jump directly to the target state. */
+ if ( trans->action->anyNextStmt() )
+ out << "\tgoto _again;\n";
+ else
+ out << "\tgoto st" << trans->targ->id << ";\n";
+ }
+ }
+
+ return anyWritten;
+}
+
+/* Called from GotoCodeGen::STATE_GOTOS just before writing the gotos for each
+ * state. */
+void IpGotoCodeGen::GOTO_HEADER( RedStateAp *state )
+{
+ bool anyWritten = IN_TRANS_ACTIONS( state );
+
+ if ( state->labelNeeded )
+ out << "st" << state->id << ":\n";
+
+ if ( state->toStateAction != 0 ) {
+ /* Remember that we wrote an action. Write every action in the list. */
+ anyWritten = true;
+ for ( ActionTable::Iter item = state->toStateAction->key; item.lte(); item++ )
+ ACTION( out, item->value, state->id, false );
+ }
+
+ /* Advance and test buffer pos. */
+ if ( state->labelNeeded ) {
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " == " << PE() << " )\n"
+ " goto _out" << state->id << ";\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n";
+ }
+ }
+
+ /* Give the state a switch case. */
+ out << "case " << state->id << ":\n";
+
+ if ( state->fromStateAction != 0 ) {
+ /* Remember that we wrote an action. Write every action in the list. */
+ anyWritten = true;
+ for ( ActionTable::Iter item = state->fromStateAction->key; item.lte(); item++ )
+ ACTION( out, item->value, state->id, false );
+ }
+
+ if ( anyWritten )
+ genLineDirective( out );
+
+ /* Record the prev state if necessary. */
+ if ( state->anyRegCurStateRef() )
+ out << " _ps = " << state->id << ";\n";
+}
+
+void IpGotoCodeGen::STATE_GOTO_ERROR()
+{
+ /* In the error state we need to emit some stuff that usually goes into
+ * the header. */
+ RedStateAp *state = redFsm->errState;
+ bool anyWritten = IN_TRANS_ACTIONS( state );
+
+ /* No case label needed since we don't switch on the error state. */
+ if ( anyWritten )
+ genLineDirective( out );
+
+ if ( state->labelNeeded )
+ out << "st" << state->id << ":\n";
+
+ /* Break out here. */
+ out << " goto _out" << state->id << ";\n";
+}
+
+
+/* Emit the goto to take for a given transition. */
+std::ostream &IpGotoCodeGen::TRANS_GOTO( RedTransAp *trans, int level )
+{
+ if ( trans->action != 0 ) {
+ /* Go to the transition which will go to the state. */
+ out << TABS(level) << "goto tr" << trans->id << ";";
+ }
+ else {
+ /* Go directly to the target state. */
+ out << TABS(level) << "goto st" << trans->targ->id << ";";
+ }
+ return out;
+}
+
+std::ostream &IpGotoCodeGen::EXIT_STATES()
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->outNeeded ) {
+ outLabelUsed = true;
+ out << " _out" << st->id << ": " << CS() << " = " <<
+ st->id << "; goto _out; \n";
+ }
+ }
+ return out;
+}
+
+std::ostream &IpGotoCodeGen::AGAIN_CASES()
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ out <<
+ " case " << st->id << ": goto st" << st->id << ";\n";
+ }
+ return out;
+}
+
+std::ostream &IpGotoCodeGen::FINISH_CASES()
+{
+ bool anyWritten = false;
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->eofAction != 0 ) {
+ if ( st->eofAction->eofRefs == 0 )
+ st->eofAction->eofRefs = new IntSet;
+ st->eofAction->eofRefs->insert( st->id );
+ }
+ }
+
+ for ( ActionTableMap::Iter act = redFsm->actionMap; act.lte(); act++ ) {
+ if ( act->eofRefs != 0 ) {
+ for ( IntSet::Iter pst = *act->eofRefs; pst.lte(); pst++ )
+ out << " case " << *pst << ": \n";
+
+ /* Remember that we wrote a trans so we know to write the
+ * line directive for going back to the output. */
+ anyWritten = true;
+
+ /* Write each action in the eof action list. */
+ for ( ActionTable::Iter item = act->key; item.lte(); item++ )
+ ACTION( out, item->value, STATE_ERR_STATE, true );
+ out << "\tbreak;\n";
+ }
+ }
+
+ if ( anyWritten )
+ genLineDirective( out );
+ return out;
+}
+
+void IpGotoCodeGen::setLabelsNeeded( InlineList *inlineList )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case InlineItem::Goto: case InlineItem::Call: {
+ /* Mark the target as needing a label. */
+ item->targState->labelNeeded = true;
+ break;
+ }
+ default: break;
+ }
+
+ if ( item->children != 0 )
+ setLabelsNeeded( item->children );
+ }
+}
+
+/* Set up labelNeeded flag for each state. */
+void IpGotoCodeGen::setLabelsNeeded()
+{
+ /* If we use the _again label, then we the _again switch, which uses all
+ * labels. */
+ if ( useAgainLabel() ) {
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->labelNeeded = true;
+ }
+ else {
+ /* Do not use all labels by default, init all labelNeeded vars to false. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->labelNeeded = false;
+
+ if ( redFsm->errState != 0 && redFsm->anyLmSwitchError() )
+ redFsm->errState->labelNeeded = true;
+
+ /* Walk all transitions and set only those that have targs. */
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
+ /* If there is no action with a next statement, then the label will be
+ * needed. */
+ if ( trans->action == 0 || !trans->action->anyNextStmt() )
+ trans->targ->labelNeeded = true;
+
+ /* Need labels for states that have goto or calls in action code
+ * invoked on characters (ie, not from out action code). */
+ if ( trans->action != 0 ) {
+ /* Loop the actions. */
+ for ( ActionTable::Iter act = trans->action->key; act.lte(); act++ ) {
+ /* Get the action and walk it's tree. */
+ setLabelsNeeded( act->value->inlineList );
+ }
+ }
+ }
+ }
+
+ if ( hasEnd ) {
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->outNeeded = st->labelNeeded;
+ }
+ else {
+ if ( redFsm->errState != 0 )
+ redFsm->errState->outNeeded = true;
+
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
+ /* Any state with a transition in that has a break will need an
+ * out label. */
+ if ( trans->action != 0 && trans->action->anyBreakStmt() )
+ trans->targ->outNeeded = true;
+ }
+ }
+}
+
+void IpGotoCodeGen::writeData()
+{
+ STATE_IDS();
+}
+
+void IpGotoCodeGen::writeExec()
+{
+ /* Must set labels immediately before writing because we may depend on the
+ * noend write option. */
+ setLabelsNeeded();
+ outLabelUsed = false;
+
+ out << " {\n";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << " int _ps = 0;\n";
+
+ if ( redFsm->anyConditions() )
+ out << " " << WIDE_ALPH_TYPE() << " _widec;\n";
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+
+ if ( useAgainLabel() ) {
+ out <<
+ " goto _resume;\n"
+ "\n"
+ "_again:\n"
+ " switch ( " << CS() << " ) {\n";
+ AGAIN_CASES() <<
+ " default: break;\n"
+ " }\n"
+ "\n";
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( ++" << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n";
+ }
+
+ out << "_resume:\n";
+ }
+
+ out <<
+ " switch ( " << CS() << " )\n {\n";
+ STATE_GOTOS();
+ SWITCH_DEFAULT() <<
+ " }\n";
+ EXIT_STATES() <<
+ "\n";
+
+ if ( outLabelUsed )
+ out << " _out: {}\n";
+
+ out <<
+ " }\n";
+}
+
+void IpGotoCodeGen::writeEOF()
+{
+ if ( redFsm->anyEofActions() ) {
+ out <<
+ " {\n"
+ " switch ( " << CS() << " ) {\n";
+ FINISH_CASES();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+}
diff --git a/contrib/tools/ragel5/rlgen-cd/ipgotocodegen.h b/contrib/tools/ragel5/rlgen-cd/ipgotocodegen.h
new file mode 100644
index 0000000000..f32678baba
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/ipgotocodegen.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _IPGCODEGEN_H
+#define _IPGCODEGEN_H
+
+#include <iostream>
+#include "gotocodegen.h"
+
+/* Forwards. */
+struct CodeGenData;
+
+/*
+ * class FGotoCodeGen
+ */
+class IpGotoCodeGen : public GotoCodeGen
+{
+public:
+ IpGotoCodeGen( ostream &out ) : FsmCodeGen(out), GotoCodeGen(out) {}
+
+ std::ostream &EXIT_STATES();
+ std::ostream &TRANS_GOTO( RedTransAp *trans, int level );
+ std::ostream &FINISH_CASES();
+ std::ostream &AGAIN_CASES();
+
+ void GOTO( ostream &ret, int gotoDest, bool inFinish );
+ void CALL( ostream &ret, int callDest, int targState, bool inFinish );
+ void NEXT( ostream &ret, int nextDest, bool inFinish );
+ void GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+ void NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+ void CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish );
+ void RET( ostream &ret, bool inFinish );
+ void CURS( ostream &ret, bool inFinish );
+ void TARGS( ostream &ret, bool inFinish, int targState );
+ void BREAK( ostream &ret, int targState );
+
+ virtual void writeData();
+ virtual void writeEOF();
+ virtual void writeExec();
+
+protected:
+ bool useAgainLabel();
+
+ /* Called from GotoCodeGen::STATE_GOTOS just before writing the gotos for
+ * each state. */
+ bool IN_TRANS_ACTIONS( RedStateAp *state );
+ void GOTO_HEADER( RedStateAp *state );
+ void STATE_GOTO_ERROR();
+
+ /* Set up labelNeeded flag for each state. */
+ void setLabelsNeeded( InlineList *inlineList );
+ void setLabelsNeeded();
+};
+
+
+/*
+ * class CIpGotoCodeGen
+ */
+struct CIpGotoCodeGen
+ : public IpGotoCodeGen, public CCodeGen
+{
+ CIpGotoCodeGen( ostream &out ) :
+ FsmCodeGen(out), IpGotoCodeGen(out), CCodeGen(out) {}
+};
+
+/*
+ * class DIpGotoCodeGen
+ */
+struct DIpGotoCodeGen
+ : public IpGotoCodeGen, public DCodeGen
+{
+ DIpGotoCodeGen( ostream &out ) :
+ FsmCodeGen(out), IpGotoCodeGen(out), DCodeGen(out) {}
+};
+
+
+#endif /* _IPGCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/main.cpp b/contrib/tools/ragel5/rlgen-cd/main.cpp
new file mode 100644
index 0000000000..cabe4bd97d
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/main.cpp
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2001-2007 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+
+#include "common.h"
+#include "rlgen-cd.h"
+#include "xmlparse.h"
+#include "pcheck.h"
+#include "vector.h"
+#include "version.h"
+
+/* Code generators. */
+#include "tabcodegen.h"
+#include "ftabcodegen.h"
+#include "flatcodegen.h"
+#include "fflatcodegen.h"
+#include "gotocodegen.h"
+#include "fgotocodegen.h"
+#include "ipgotocodegen.h"
+#include "splitcodegen.h"
+
+using std::istream;
+using std::ifstream;
+using std::ostream;
+using std::ios;
+using std::cin;
+using std::cout;
+using std::cerr;
+using std::endl;
+
+/* Target language and output style. */
+CodeStyleEnum codeStyle = GenTables;
+
+/* Io globals. */
+istream *inStream = 0;
+ostream *outStream = 0;
+output_filter *outFilter = 0;
+char *outputFileName = 0;
+
+/* Graphviz dot file generation. */
+bool graphvizDone = false;
+
+int numSplitPartitions = 0;
+bool noLineDirectives = false;
+bool printPrintables = false;
+
+/* Print a summary of the options. */
+void usage()
+{
+ cout <<
+"usage: " PROGNAME " [options] file\n"
+"general:\n"
+" -h, -H, -?, --help Print this usage and exit\n"
+" -v, --version Print version information and exit\n"
+" -o <file> Write output to <file>\n"
+"code generation options:\n"
+" -l Inhibit writing of #line directives\n"
+"generated code style:\n"
+" -T0 Table driven FSM (default)\n"
+" -T1 Faster table driven FSM\n"
+" -F0 Flat table driven FSM\n"
+" -F1 Faster flat table-driven FSM\n"
+" -G0 Goto-driven FSM\n"
+" -G1 Faster goto-driven FSM\n"
+" -G2 Really fast goto-driven FSM\n"
+" -P<N> N-Way Split really fast goto-driven FSM\n"
+ ;
+}
+
+/* Print version information. */
+void version()
+{
+ cout << "Ragel Code Generator for C, C++, Objective-C and D" << endl <<
+ "Version " VERSION << ", " PUBDATE << endl <<
+ "Copyright (c) 2001-2007 by Adrian Thurston" << endl;
+}
+
+/* Total error count. */
+int gblErrorCount = 0;
+
+ostream &error()
+{
+ gblErrorCount += 1;
+ cerr << PROGNAME ": ";
+ return cerr;
+}
+
+/*
+ * Callbacks invoked by the XML data parser.
+ */
+
+/* Invoked by the parser when the root element is opened. */
+ostream *openOutput( char *inputFile )
+{
+ if ( hostLangType != CCode && hostLangType != DCode ) {
+ error() << "this code generator is for C and D only" << endl;
+ exit(1);
+ }
+
+ /* If the output format is code and no output file name is given, then
+ * make a default. */
+ if ( outputFileName == 0 ) {
+ char *ext = findFileExtension( inputFile );
+ if ( ext != 0 && strcmp( ext, ".rh" ) == 0 )
+ outputFileName = fileNameFromStem( inputFile, ".h" );
+ else {
+ const char *defExtension = 0;
+ switch ( hostLangType ) {
+ case CCode: defExtension = ".c"; break;
+ case DCode: defExtension = ".d"; break;
+ default: break;
+ }
+ outputFileName = fileNameFromStem( inputFile, defExtension );
+ }
+ }
+
+ /* Make sure we are not writing to the same file as the input file. */
+ if ( outputFileName != 0 && strcmp( inputFile, outputFileName ) == 0 ) {
+ error() << "output file \"" << outputFileName <<
+ "\" is the same as the input file" << endl;
+ }
+
+ if ( outputFileName != 0 ) {
+ /* Create the filter on the output and open it. */
+ outFilter = new output_filter( outputFileName );
+ outFilter->open( outputFileName, ios::out|ios::trunc );
+ if ( !outFilter->is_open() ) {
+ error() << "error opening " << outputFileName << " for writing" << endl;
+ exit(1);
+ }
+
+ /* Open the output stream, attaching it to the filter. */
+ outStream = new ostream( outFilter );
+ }
+ else {
+ /* Writing out ot std out. */
+ outStream = &cout;
+ }
+ return outStream;
+}
+
+/* Invoked by the parser when a ragel definition is opened. */
+CodeGenData *makeCodeGen( char *sourceFileName, char *fsmName,
+ ostream &out, bool wantComplete )
+{
+ CodeGenData *codeGen = 0;
+ switch ( hostLangType ) {
+ case CCode:
+ switch ( codeStyle ) {
+ case GenTables:
+ codeGen = new CTabCodeGen(out);
+ break;
+ case GenFTables:
+ codeGen = new CFTabCodeGen(out);
+ break;
+ case GenFlat:
+ codeGen = new CFlatCodeGen(out);
+ break;
+ case GenFFlat:
+ codeGen = new CFFlatCodeGen(out);
+ break;
+ case GenGoto:
+ codeGen = new CGotoCodeGen(out);
+ break;
+ case GenFGoto:
+ codeGen = new CFGotoCodeGen(out);
+ break;
+ case GenIpGoto:
+ codeGen = new CIpGotoCodeGen(out);
+ break;
+ case GenSplit:
+ codeGen = new CSplitCodeGen(out);
+ break;
+ }
+ break;
+
+ case DCode:
+ switch ( codeStyle ) {
+ case GenTables:
+ codeGen = new DTabCodeGen(out);
+ break;
+ case GenFTables:
+ codeGen = new DFTabCodeGen(out);
+ break;
+ case GenFlat:
+ codeGen = new DFlatCodeGen(out);
+ break;
+ case GenFFlat:
+ codeGen = new DFFlatCodeGen(out);
+ break;
+ case GenGoto:
+ codeGen = new DGotoCodeGen(out);
+ break;
+ case GenFGoto:
+ codeGen = new DFGotoCodeGen(out);
+ break;
+ case GenIpGoto:
+ codeGen = new DIpGotoCodeGen(out);
+ break;
+ case GenSplit:
+ codeGen = new DSplitCodeGen(out);
+ break;
+ }
+ break;
+
+ default: break;
+ }
+
+ codeGen->sourceFileName = sourceFileName;
+ codeGen->fsmName = fsmName;
+ codeGen->wantComplete = wantComplete;
+
+ return codeGen;
+}
+
+
+
+/* Main, process args and call yyparse to start scanning input. */
+int main(int argc, char **argv)
+{
+ ParamCheck pc("-:Hh?vlo:T:F:G:P:", argc, argv);
+ const char *xmlInputFileName = 0;
+
+ while ( pc.check() ) {
+ switch ( pc.state ) {
+ case ParamCheck::match:
+ switch ( pc.parameter ) {
+ /* Output. */
+ case 'o':
+ if ( *pc.parameterArg == 0 )
+ error() << "a zero length output file name was given" << endl;
+ else if ( outputFileName != 0 )
+ error() << "more than one output file name was given" << endl;
+ else {
+ /* Ok, remember the output file name. */
+ outputFileName = pc.parameterArg;
+ }
+ break;
+
+ case 'l':
+ noLineDirectives = true;
+ break;
+
+ /* Code style. */
+ case 'T':
+ if ( pc.parameterArg[0] == '0' )
+ codeStyle = GenTables;
+ else if ( pc.parameterArg[0] == '1' )
+ codeStyle = GenFTables;
+ else {
+ error() << "-T" << pc.parameterArg[0] <<
+ " is an invalid argument" << endl;
+ exit(1);
+ }
+ break;
+ case 'F':
+ if ( pc.parameterArg[0] == '0' )
+ codeStyle = GenFlat;
+ else if ( pc.parameterArg[0] == '1' )
+ codeStyle = GenFFlat;
+ else {
+ error() << "-F" << pc.parameterArg[0] <<
+ " is an invalid argument" << endl;
+ exit(1);
+ }
+ break;
+ case 'G':
+ if ( pc.parameterArg[0] == '0' )
+ codeStyle = GenGoto;
+ else if ( pc.parameterArg[0] == '1' )
+ codeStyle = GenFGoto;
+ else if ( pc.parameterArg[0] == '2' )
+ codeStyle = GenIpGoto;
+ else {
+ error() << "-G" << pc.parameterArg[0] <<
+ " is an invalid argument" << endl;
+ exit(1);
+ }
+ break;
+ case 'P':
+ codeStyle = GenSplit;
+ numSplitPartitions = atoi( pc.parameterArg );
+ break;
+
+ /* Version and help. */
+ case 'v':
+ version();
+ exit(0);
+ case 'H': case 'h': case '?':
+ usage();
+ exit(0);
+ case '-':
+ if ( strcasecmp(pc.parameterArg, "help") == 0 ) {
+ usage();
+ exit(0);
+ }
+ else if ( strcasecmp(pc.parameterArg, "version") == 0 ) {
+ version();
+ exit(0);
+ }
+ else {
+ error() << "--" << pc.parameterArg <<
+ " is an invalid argument" << endl;
+ break;
+ }
+ }
+ break;
+
+ case ParamCheck::invalid:
+ error() << "-" << pc.parameter << " is an invalid argument" << endl;
+ break;
+
+ case ParamCheck::noparam:
+ if ( *pc.curArg == 0 )
+ error() << "a zero length input file name was given" << endl;
+ else if ( xmlInputFileName != 0 )
+ error() << "more than one input file name was given" << endl;
+ else {
+ /* OK, Remember the filename. */
+ xmlInputFileName = pc.curArg;
+ }
+ break;
+ }
+ }
+
+ /* Bail on above errors. */
+ if ( gblErrorCount > 0 )
+ exit(1);
+
+ /* Open the input file for reading. */
+ if ( xmlInputFileName != 0 ) {
+ /* Open the input file for reading. */
+ ifstream *inFile = new ifstream( xmlInputFileName );
+ inStream = inFile;
+ if ( ! inFile->is_open() )
+ error() << "could not open " << xmlInputFileName << " for reading" << endl;
+ }
+ else {
+ xmlInputFileName = "<stdin>";
+ inStream = &cin;
+ }
+
+ /* Bail on above errors. */
+ if ( gblErrorCount > 0 )
+ exit(1);
+
+ bool wantComplete = true;
+ bool outputActive = true;
+
+ /* Parse the input! */
+ xml_parse( *inStream, xmlInputFileName, outputActive, wantComplete );
+
+ /* If writing to a file, delete the ostream, causing it to flush.
+ * Standard out is flushed automatically. */
+ if ( outputFileName != 0 ) {
+ delete outStream;
+ delete outFilter;
+ }
+
+ /* Finished, final check for errors.. */
+ if ( gblErrorCount > 0 ) {
+ /* If we opened an output file, remove it. */
+ if ( outputFileName != 0 )
+ unlink( outputFileName );
+ exit(1);
+ }
+ return 0;
+}
diff --git a/contrib/tools/ragel5/rlgen-cd/rlgen-cd.h b/contrib/tools/ragel5/rlgen-cd/rlgen-cd.h
new file mode 100644
index 0000000000..93acd99bae
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/rlgen-cd.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _RLCODEGEN_H
+#define _RLCODEGEN_H
+
+#include <stdio.h>
+#include <iostream>
+#include "avltree.h"
+#include "vector.h"
+#include "config.h"
+
+#define PROGNAME "rlgen-cd"
+
+/* Target output style. */
+enum CodeStyleEnum
+{
+ GenTables,
+ GenFTables,
+ GenFlat,
+ GenFFlat,
+ GenGoto,
+ GenFGoto,
+ GenIpGoto,
+ GenSplit
+};
+
+extern CodeStyleEnum codeStyle;
+
+
+/* IO filenames and stream. */
+extern bool graphvizDone;
+
+extern int gblErrorCount;
+
+/* Options. */
+extern int numSplitPartitions;
+extern bool noLineDirectives;
+
+std::ostream &error();
+
+#endif /* _RLCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/splitcodegen.cpp b/contrib/tools/ragel5/rlgen-cd/splitcodegen.cpp
new file mode 100644
index 0000000000..d703b37eea
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/splitcodegen.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright 2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "rlgen-cd.h"
+#include "splitcodegen.h"
+#include "gendata.h"
+#include <assert.h>
+
+using std::ostream;
+using std::ios;
+using std::endl;
+
+/* Emit the goto to take for a given transition. */
+std::ostream &SplitCodeGen::TRANS_GOTO( RedTransAp *trans, int level )
+{
+ if ( trans->targ->partition == currentPartition ) {
+ if ( trans->action != 0 ) {
+ /* Go to the transition which will go to the state. */
+ out << TABS(level) << "goto tr" << trans->id << ";";
+ }
+ else {
+ /* Go directly to the target state. */
+ out << TABS(level) << "goto st" << trans->targ->id << ";";
+ }
+ }
+ else {
+ if ( trans->action != 0 ) {
+ /* Go to the transition which will go to the state. */
+ out << TABS(level) << "goto ptr" << trans->id << ";";
+ trans->partitionBoundary = true;
+ }
+ else {
+ /* Go directly to the target state. */
+ out << TABS(level) << "goto pst" << trans->targ->id << ";";
+ trans->targ->partitionBoundary = true;
+ }
+ }
+ return out;
+}
+
+/* Called from before writing the gotos for each state. */
+void SplitCodeGen::GOTO_HEADER( RedStateAp *state, bool stateInPartition )
+{
+ bool anyWritten = IN_TRANS_ACTIONS( state );
+
+ if ( state->labelNeeded )
+ out << "st" << state->id << ":\n";
+
+ if ( state->toStateAction != 0 ) {
+ /* Remember that we wrote an action. Write every action in the list. */
+ anyWritten = true;
+ for ( ActionTable::Iter item = state->toStateAction->key; item.lte(); item++ )
+ ACTION( out, item->value, state->id, false );
+ }
+
+ /* Advance and test buffer pos. */
+ if ( state->labelNeeded ) {
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " == " << PE() << " )\n"
+ " goto _out" << state->id << ";\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n";
+ }
+ }
+
+ /* Give the state a switch case. */
+ out << "case " << state->id << ":\n";
+
+ if ( state->fromStateAction != 0 ) {
+ /* Remember that we wrote an action. Write every action in the list. */
+ anyWritten = true;
+ for ( ActionTable::Iter item = state->fromStateAction->key; item.lte(); item++ )
+ ACTION( out, item->value, state->id, false );
+ }
+
+ if ( anyWritten )
+ genLineDirective( out );
+
+ /* Record the prev state if necessary. */
+ if ( state->anyRegCurStateRef() )
+ out << " _ps = " << state->id << ";\n";
+}
+
+std::ostream &SplitCodeGen::STATE_GOTOS( int partition )
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->partition == partition ) {
+ if ( st == redFsm->errState )
+ STATE_GOTO_ERROR();
+ else {
+ /* We call into the base of the goto which calls back into us
+ * using virtual functions. Set the current partition rather
+ * than coding parameter passing throughout. */
+ currentPartition = partition;
+
+ /* Writing code above state gotos. */
+ GOTO_HEADER( st, st->partition == partition );
+
+ if ( st->stateCondVect.length() > 0 ) {
+ out << " _widec = " << GET_KEY() << ";\n";
+ emitCondBSearch( st, 1, 0, st->stateCondVect.length() - 1 );
+ }
+
+ /* Try singles. */
+ if ( st->outSingle.length() > 0 )
+ emitSingleSwitch( st );
+
+ /* Default case is to binary search for the ranges, if that fails then */
+ if ( st->outRange.length() > 0 )
+ emitRangeBSearch( st, 1, 0, st->outRange.length() - 1 );
+
+ /* Write the default transition. */
+ TRANS_GOTO( st->defTrans, 1 ) << "\n";
+ }
+ }
+ }
+ return out;
+}
+
+
+std::ostream &SplitCodeGen::PART_TRANS( int partition )
+{
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
+ if ( trans->partitionBoundary ) {
+ out <<
+ "ptr" << trans->id << ":\n";
+
+ if ( trans->action != 0 ) {
+ /* If the action contains a next, then we must preload the current
+ * state since the action may or may not set it. */
+ if ( trans->action->anyNextStmt() )
+ out << " " << CS() << " = " << trans->targ->id << ";\n";
+
+ /* Write each action in the list. */
+ for ( ActionTable::Iter item = trans->action->key; item.lte(); item++ )
+ ACTION( out, item->value, trans->targ->id, false );
+ }
+
+ out <<
+ " goto pst" << trans->targ->id << ";\n";
+ trans->targ->partitionBoundary = true;
+ }
+ }
+
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->partitionBoundary ) {
+ out <<
+ " pst" << st->id << ":\n"
+ " " << CS() << " = " << st->id << ";\n";
+
+ if ( st->toStateAction != 0 ) {
+ /* Remember that we wrote an action. Write every action in the list. */
+ for ( ActionTable::Iter item = st->toStateAction->key; item.lte(); item++ )
+ ACTION( out, item->value, st->id, false );
+ genLineDirective( out );
+ }
+
+ ptOutLabelUsed = true;
+ out << " goto _pt_out; \n";
+ }
+ }
+ return out;
+}
+
+std::ostream &SplitCodeGen::EXIT_STATES( int partition )
+{
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ if ( st->partition == partition && st->outNeeded ) {
+ outLabelUsed = true;
+ out << " _out" << st->id << ": " << CS() << " = " <<
+ st->id << "; goto _out; \n";
+ }
+ }
+ return out;
+}
+
+
+std::ostream &SplitCodeGen::PARTITION( int partition )
+{
+ outLabelUsed = false;
+ ptOutLabelUsed = false;
+
+ /* Initialize the partition boundaries, which get set during the writing
+ * of states. After the state writing we will */
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ )
+ trans->partitionBoundary = false;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->partitionBoundary = false;
+
+ out << " " << ALPH_TYPE() << " *p = *_pp, *pe = *_ppe;\n";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << " int _ps = 0;\n";
+
+ if ( redFsm->anyConditions() )
+ out << " " << WIDE_ALPH_TYPE() << " _widec;\n";
+
+ if ( useAgainLabel() ) {
+ out <<
+ " goto _resume;\n"
+ "\n"
+ "_again:\n"
+ " switch ( " << CS() << " ) {\n";
+ AGAIN_CASES() <<
+ " default: break;\n"
+ " }\n"
+ "\n";
+
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( ++" << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n";
+ }
+
+ out <<
+ "_resume:\n";
+ }
+
+ out <<
+ " switch ( " << CS() << " )\n {\n";
+ STATE_GOTOS( partition );
+ SWITCH_DEFAULT() <<
+ " }\n";
+ PART_TRANS( partition );
+ EXIT_STATES( partition );
+
+ if ( outLabelUsed ) {
+ out <<
+ "\n"
+ " _out:\n"
+ " *_pp = p;\n"
+ " *_ppe = pe;\n"
+ " return 0;\n";
+ }
+
+ if ( ptOutLabelUsed ) {
+ out <<
+ "\n"
+ " _pt_out:\n"
+ " *_pp = p;\n"
+ " *_ppe = pe;\n"
+ " return 1;\n";
+ }
+
+ return out;
+}
+
+std::ostream &SplitCodeGen::PART_MAP()
+{
+ int *partMap = new int[redFsm->stateList.length()];
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ partMap[st->id] = st->partition;
+
+ out << "\t";
+ int totalItem = 0;
+ for ( int i = 0; i < redFsm->stateList.length(); i++ ) {
+ out << partMap[i];
+ if ( i != redFsm->stateList.length() - 1 ) {
+ out << ", ";
+ if ( ++totalItem % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+
+ delete[] partMap;
+ return out;
+}
+
+void SplitCodeGen::writeData()
+{
+ out <<
+ "static const int " << START() << " = " << START_STATE_ID() << ";\n"
+ "\n";
+
+ if ( writeFirstFinal ) {
+ out <<
+ "static const int " << FIRST_FINAL() << " = " << FIRST_FINAL_STATE() << ";\n"
+ "\n";
+ }
+
+ if ( writeErr ) {
+ out <<
+ "static const int " << ERROR() << " = " << ERROR_STATE() << ";\n"
+ "\n";
+ }
+
+
+ OPEN_ARRAY( ARRAY_TYPE(numSplitPartitions), PM() );
+ PART_MAP();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ for ( int p = 0; p < redFsm->nParts; p++ ) {
+ out << "int partition" << p << "( " << ALPH_TYPE() << " **_pp, " << ALPH_TYPE() <<
+ " **_ppe, struct " << FSM_NAME() << " *fsm );\n";
+ }
+ out << "\n";
+}
+
+std::ostream &SplitCodeGen::ALL_PARTITIONS()
+{
+ /* compute the format string. */
+ int width = 0, high = redFsm->nParts - 1;
+ while ( high > 0 ) {
+ width++;
+ high /= 10;
+ }
+ assert( width <= 8 );
+ char suffFormat[] = "_%6.6d.c";
+ suffFormat[2] = suffFormat[4] = ( '0' + width );
+
+ for ( int p = 0; p < redFsm->nParts; p++ ) {
+ char suffix[10];
+ sprintf( suffix, suffFormat, p );
+ char *fn = fileNameFromStem( sourceFileName, suffix );
+ char *include = fileNameFromStem( sourceFileName, ".h" );
+
+ /* Create the filter on the output and open it. */
+ output_filter *partFilter = new output_filter( fn );
+ partFilter->open( fn, ios::out|ios::trunc );
+ if ( !partFilter->is_open() ) {
+ error() << "error opening " << fn << " for writing" << endl;
+ exit(1);
+ }
+
+ /* Attach the new file to the output stream. */
+ std::streambuf *prev_rdbuf = out.rdbuf( partFilter );
+
+ out <<
+ "#include \"" << include << "\"\n"
+ "int partition" << p << "( " << ALPH_TYPE() << " **_pp, " << ALPH_TYPE() <<
+ " **_ppe, struct " << FSM_NAME() << " *fsm )\n"
+ "{\n";
+ PARTITION( p ) <<
+ "}\n\n";
+ out.flush();
+
+ /* Fix the output stream. */
+ out.rdbuf( prev_rdbuf );
+ }
+ return out;
+}
+
+
+void SplitCodeGen::writeExec()
+{
+ /* Must set labels immediately before writing because we may depend on the
+ * noend write option. */
+ setLabelsNeeded();
+ out <<
+ " {\n"
+ " int _stat = 0;\n";
+
+ if ( hasEnd ) {
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+
+ out << " goto _resume;\n";
+
+ /* In this reentry, to-state actions have already been executed on the
+ * partition-switch exit from the last partition. */
+ out << "_reenter:\n";
+
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n";
+ }
+
+ out << "_resume:\n";
+
+ out <<
+ " switch ( " << PM() << "[" << CS() << "] ) {\n";
+ for ( int p = 0; p < redFsm->nParts; p++ ) {
+ out <<
+ " case " << p << ":\n"
+ " _stat = partition" << p << "( &p, &pe, fsm );\n"
+ " break;\n";
+ }
+ out <<
+ " }\n"
+ " if ( _stat )\n"
+ " goto _reenter;\n";
+
+ if ( hasEnd )
+ out << " _out: {}\n";
+
+ out <<
+ " }\n";
+
+ ALL_PARTITIONS();
+}
+
+void SplitCodeGen::setLabelsNeeded( RedStateAp *fromState, InlineList *inlineList )
+{
+ for ( InlineList::Iter item = *inlineList; item.lte(); item++ ) {
+ switch ( item->type ) {
+ case InlineItem::Goto: case InlineItem::Call: {
+ /* In split code gen we only need labels for transitions across
+ * partitions. */
+ if ( fromState->partition == item->targState->partition ){
+ /* Mark the target as needing a label. */
+ item->targState->labelNeeded = true;
+ }
+ break;
+ }
+ default: break;
+ }
+
+ if ( item->children != 0 )
+ setLabelsNeeded( fromState, item->children );
+ }
+}
+
+void SplitCodeGen::setLabelsNeeded( RedStateAp *fromState, RedTransAp *trans )
+{
+ /* In the split code gen we don't need labels for transitions across
+ * partitions. */
+ if ( fromState->partition == trans->targ->partition ) {
+ /* If there is no action with a next statement, then the label will be
+ * needed. */
+ trans->labelNeeded = true;
+ if ( trans->action == 0 || !trans->action->anyNextStmt() )
+ trans->targ->labelNeeded = true;
+ }
+
+ /* Need labels for states that have goto or calls in action code
+ * invoked on characters (ie, not from out action code). */
+ if ( trans->action != 0 ) {
+ /* Loop the actions. */
+ for ( ActionTable::Iter act = trans->action->key; act.lte(); act++ ) {
+ /* Get the action and walk it's tree. */
+ setLabelsNeeded( fromState, act->value->inlineList );
+ }
+ }
+}
+
+/* Set up labelNeeded flag for each state. */
+void SplitCodeGen::setLabelsNeeded()
+{
+ /* If we use the _again label, then we the _again switch, which uses all
+ * labels. */
+ if ( useAgainLabel() ) {
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->labelNeeded = true;
+ }
+ else {
+ /* Do not use all labels by default, init all labelNeeded vars to false. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->labelNeeded = false;
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ )
+ trans->labelNeeded = false;
+
+ if ( redFsm->errState != 0 && redFsm->anyLmSwitchError() )
+ redFsm->errState->labelNeeded = true;
+
+ /* Walk all transitions and set only those that have targs. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ for ( RedTransList::Iter tel = st->outRange; tel.lte(); tel++ )
+ setLabelsNeeded( st, tel->value );
+
+ for ( RedTransList::Iter tel = st->outSingle; tel.lte(); tel++ )
+ setLabelsNeeded( st, tel->value );
+
+ if ( st->defTrans != 0 )
+ setLabelsNeeded( st, st->defTrans );
+ }
+ }
+
+ if ( hasEnd ) {
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ )
+ st->outNeeded = st->labelNeeded;
+ }
+ else {
+ if ( redFsm->errState != 0 )
+ redFsm->errState->outNeeded = true;
+
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ ) {
+ /* Any state with a transition in that has a break will need an
+ * out label. */
+ if ( trans->action != 0 && trans->action->anyBreakStmt() )
+ trans->targ->outNeeded = true;
+ }
+ }
+}
+
diff --git a/contrib/tools/ragel5/rlgen-cd/splitcodegen.h b/contrib/tools/ragel5/rlgen-cd/splitcodegen.h
new file mode 100644
index 0000000000..82fc37150e
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/splitcodegen.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2006 Adrian Thurston <thurston@cs.queensu.ca>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _SPLITCODEGEN_H
+#define _SPLITCODEGEN_H
+
+#include "ipgotocodegen.h"
+
+class SplitCodeGen : public IpGotoCodeGen
+{
+public:
+ SplitCodeGen( ostream &out ) : FsmCodeGen(out), IpGotoCodeGen(out) {}
+
+ bool ptOutLabelUsed;
+
+ std::ostream &PART_MAP();
+ std::ostream &EXIT_STATES( int partition );
+ std::ostream &PART_TRANS( int partition );
+ std::ostream &TRANS_GOTO( RedTransAp *trans, int level );
+ void GOTO_HEADER( RedStateAp *state, bool stateInPartition );
+ std::ostream &STATE_GOTOS( int partition );
+ std::ostream &PARTITION( int partition );
+ std::ostream &ALL_PARTITIONS();
+ void writeData();
+ void writeExec();
+ void writeParts();
+
+ void setLabelsNeeded( RedStateAp *fromState, InlineList *inlineList );
+ void setLabelsNeeded( RedStateAp *fromState, RedTransAp *trans );
+ void setLabelsNeeded();
+
+ int currentPartition;
+};
+
+struct CSplitCodeGen
+ : public SplitCodeGen, public CCodeGen
+{
+ CSplitCodeGen( ostream &out ) :
+ FsmCodeGen(out), SplitCodeGen(out), CCodeGen(out) {}
+};
+
+/*
+ * class DIpGotoCodeGen
+ */
+struct DSplitCodeGen
+ : public SplitCodeGen, public DCodeGen
+{
+ DSplitCodeGen( ostream &out ) :
+ FsmCodeGen(out), SplitCodeGen(out), DCodeGen(out) {}
+};
+
+
+#endif /* _SPLITCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/tabcodegen.cpp b/contrib/tools/ragel5/rlgen-cd/tabcodegen.cpp
new file mode 100644
index 0000000000..22f09534b2
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/tabcodegen.cpp
@@ -0,0 +1,988 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "rlgen-cd.h"
+#include "tabcodegen.h"
+#include "redfsm.h"
+#include "gendata.h"
+
+/* Determine if we should use indicies or not. */
+void TabCodeGen::calcIndexSize()
+{
+ int sizeWithInds = 0, sizeWithoutInds = 0;
+
+ /* Calculate cost of using with indicies. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ int totalIndex = st->outSingle.length() + st->outRange.length() +
+ (st->defTrans == 0 ? 0 : 1);
+ sizeWithInds += arrayTypeSize(redFsm->maxIndex) * totalIndex;
+ }
+ sizeWithInds += arrayTypeSize(redFsm->maxState) * redFsm->transSet.length();
+ if ( redFsm->anyActions() )
+ sizeWithInds += arrayTypeSize(redFsm->maxActionLoc) * redFsm->transSet.length();
+
+ /* Calculate the cost of not using indicies. */
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ int totalIndex = st->outSingle.length() + st->outRange.length() +
+ (st->defTrans == 0 ? 0 : 1);
+ sizeWithoutInds += arrayTypeSize(redFsm->maxState) * totalIndex;
+ if ( redFsm->anyActions() )
+ sizeWithoutInds += arrayTypeSize(redFsm->maxActionLoc) * totalIndex;
+ }
+
+ /* If using indicies reduces the size, use them. */
+ useIndicies = sizeWithInds < sizeWithoutInds;
+}
+
+std::ostream &TabCodeGen::TO_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->toStateAction != 0 )
+ act = state->toStateAction->location+1;
+ out << act;
+ return out;
+}
+
+std::ostream &TabCodeGen::FROM_STATE_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->fromStateAction != 0 )
+ act = state->fromStateAction->location+1;
+ out << act;
+ return out;
+}
+
+std::ostream &TabCodeGen::EOF_ACTION( RedStateAp *state )
+{
+ int act = 0;
+ if ( state->eofAction != 0 )
+ act = state->eofAction->location+1;
+ out << act;
+ return out;
+}
+
+
+std::ostream &TabCodeGen::TRANS_ACTION( RedTransAp *trans )
+{
+ /* If there are actions, emit them. Otherwise emit zero. */
+ int act = 0;
+ if ( trans->action != 0 )
+ act = trans->action->location+1;
+ out << act;
+ return out;
+}
+
+std::ostream &TabCodeGen::TO_STATE_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numToStateRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &TabCodeGen::FROM_STATE_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numFromStateRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &TabCodeGen::EOF_ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numEofRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, true );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+
+std::ostream &TabCodeGen::ACTION_SWITCH()
+{
+ /* Walk the list of functions, printing the cases. */
+ for ( ActionList::Iter act = actionList; act.lte(); act++ ) {
+ /* Write out referenced actions. */
+ if ( act->numTransRefs > 0 ) {
+ /* Write the case label, the action and the case break. */
+ out << "\tcase " << act->actionId << ":\n";
+ ACTION( out, act, 0, false );
+ out << "\tbreak;\n";
+ }
+ }
+
+ genLineDirective( out );
+ return out;
+}
+
+std::ostream &TabCodeGen::COND_OFFSETS()
+{
+ out << "\t";
+ int totalStateNum = 0, curKeyOffset = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write the key offset. */
+ out << curKeyOffset;
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Move the key offset ahead. */
+ curKeyOffset += st->stateCondList.length();
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::KEY_OFFSETS()
+{
+ out << "\t";
+ int totalStateNum = 0, curKeyOffset = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write the key offset. */
+ out << curKeyOffset;
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Move the key offset ahead. */
+ curKeyOffset += st->outSingle.length() + st->outRange.length()*2;
+ }
+ out << "\n";
+ return out;
+}
+
+
+std::ostream &TabCodeGen::INDEX_OFFSETS()
+{
+ out << "\t";
+ int totalStateNum = 0, curIndOffset = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write the index offset. */
+ out << curIndOffset;
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Move the index offset ahead. */
+ curIndOffset += st->outSingle.length() + st->outRange.length();
+ if ( st->defTrans != 0 )
+ curIndOffset += 1;
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::COND_LENS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write singles length. */
+ out << st->stateCondList.length();
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+
+std::ostream &TabCodeGen::SINGLE_LENS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write singles length. */
+ out << st->outSingle.length();
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::RANGE_LENS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Emit length of range index. */
+ out << st->outRange.length();
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::TO_STATE_ACTIONS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ TO_STATE_ACTION(st);
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::FROM_STATE_ACTIONS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ FROM_STATE_ACTION(st);
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::EOF_ACTIONS()
+{
+ out << "\t";
+ int totalStateNum = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Write any eof action. */
+ EOF_ACTION(st);
+ if ( !st.last() ) {
+ out << ", ";
+ if ( ++totalStateNum % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::COND_KEYS()
+{
+ out << '\t';
+ int totalTrans = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Loop the state's transitions. */
+ for ( StateCondList::Iter sc = st->stateCondList; sc.lte(); sc++ ) {
+ /* Lower key. */
+ out << KEY( sc->lowKey ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+
+ /* Upper key. */
+ out << KEY( sc->highKey ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::COND_SPACES()
+{
+ out << '\t';
+ int totalTrans = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Loop the state's transitions. */
+ for ( StateCondList::Iter sc = st->stateCondList; sc.lte(); sc++ ) {
+ /* Cond Space id. */
+ out << sc->condSpace->condSpaceId << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::KEYS()
+{
+ out << '\t';
+ int totalTrans = 0;
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Loop the singles. */
+ for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) {
+ out << KEY( stel->lowKey ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Loop the state's transitions. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ /* Lower key. */
+ out << KEY( rtel->lowKey ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+
+ /* Upper key. */
+ out << KEY( rtel->highKey ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::INDICIES()
+{
+ int totalTrans = 0;
+ out << '\t';
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Walk the singles. */
+ for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) {
+ out << stel->value->id << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Walk the ranges. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ out << rtel->value->id << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* The state's default index goes next. */
+ if ( st->defTrans != 0 ) {
+ out << st->defTrans->id << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::TRANS_TARGS()
+{
+ int totalTrans = 0;
+ out << '\t';
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Walk the singles. */
+ for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) {
+ RedTransAp *trans = stel->value;
+ out << trans->targ->id << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Walk the ranges. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ RedTransAp *trans = rtel->value;
+ out << trans->targ->id << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* The state's default target state. */
+ if ( st->defTrans != 0 ) {
+ RedTransAp *trans = st->defTrans;
+ out << trans->targ->id << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+
+std::ostream &TabCodeGen::TRANS_ACTIONS()
+{
+ int totalTrans = 0;
+ out << '\t';
+ for ( RedStateList::Iter st = redFsm->stateList; st.lte(); st++ ) {
+ /* Walk the singles. */
+ for ( RedTransList::Iter stel = st->outSingle; stel.lte(); stel++ ) {
+ RedTransAp *trans = stel->value;
+ TRANS_ACTION( trans ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* Walk the ranges. */
+ for ( RedTransList::Iter rtel = st->outRange; rtel.lte(); rtel++ ) {
+ RedTransAp *trans = rtel->value;
+ TRANS_ACTION( trans ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+
+ /* The state's default index goes next. */
+ if ( st->defTrans != 0 ) {
+ RedTransAp *trans = st->defTrans;
+ TRANS_ACTION( trans ) << ", ";
+ if ( ++totalTrans % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+
+ /* Output one last number so we don't have to figure out when the last
+ * entry is and avoid writing a comma. */
+ out << 0 << "\n";
+ return out;
+}
+
+std::ostream &TabCodeGen::TRANS_TARGS_WI()
+{
+ /* Transitions must be written ordered by their id. */
+ RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()];
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ )
+ transPtrs[trans->id] = trans;
+
+ /* Keep a count of the num of items in the array written. */
+ out << '\t';
+ int totalStates = 0;
+ for ( int t = 0; t < redFsm->transSet.length(); t++ ) {
+ /* Write out the target state. */
+ RedTransAp *trans = transPtrs[t];
+ out << trans->targ->id;
+ if ( t < redFsm->transSet.length()-1 ) {
+ out << ", ";
+ if ( ++totalStates % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ delete[] transPtrs;
+ return out;
+}
+
+
+std::ostream &TabCodeGen::TRANS_ACTIONS_WI()
+{
+ /* Transitions must be written ordered by their id. */
+ RedTransAp **transPtrs = new RedTransAp*[redFsm->transSet.length()];
+ for ( TransApSet::Iter trans = redFsm->transSet; trans.lte(); trans++ )
+ transPtrs[trans->id] = trans;
+
+ /* Keep a count of the num of items in the array written. */
+ out << '\t';
+ int totalAct = 0;
+ for ( int t = 0; t < redFsm->transSet.length(); t++ ) {
+ /* Write the function for the transition. */
+ RedTransAp *trans = transPtrs[t];
+ TRANS_ACTION( trans );
+ if ( t < redFsm->transSet.length()-1 ) {
+ out << ", ";
+ if ( ++totalAct % IALL == 0 )
+ out << "\n\t";
+ }
+ }
+ out << "\n";
+ delete[] transPtrs;
+ return out;
+}
+
+void TabCodeGen::LOCATE_TRANS()
+{
+ out <<
+ " _keys = " << ARR_OFF( K(), KO() + "[" + CS() + "]" ) << ";\n"
+ " _trans = " << IO() << "[" << CS() << "];\n"
+ "\n"
+ " _klen = " << SL() << "[" << CS() << "];\n"
+ " if ( _klen > 0 ) {\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_lower = _keys;\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_mid;\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_upper = _keys + _klen - 1;\n"
+ " while (1) {\n"
+ " if ( _upper < _lower )\n"
+ " break;\n"
+ "\n"
+ " _mid = _lower + ((_upper-_lower) >> 1);\n"
+ " if ( " << GET_WIDE_KEY() << " < *_mid )\n"
+ " _upper = _mid - 1;\n"
+ " else if ( " << GET_WIDE_KEY() << " > *_mid )\n"
+ " _lower = _mid + 1;\n"
+ " else {\n"
+ " _trans += (_mid - _keys);\n"
+ " goto _match;\n"
+ " }\n"
+ " }\n"
+ " _keys += _klen;\n"
+ " _trans += _klen;\n"
+ " }\n"
+ "\n"
+ " _klen = " << RL() << "[" << CS() << "];\n"
+ " if ( _klen > 0 ) {\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_lower = _keys;\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_mid;\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_upper = _keys + (_klen<<1) - 2;\n"
+ " while (1) {\n"
+ " if ( _upper < _lower )\n"
+ " break;\n"
+ "\n"
+ " _mid = _lower + (((_upper-_lower) >> 1) & ~1);\n"
+ " if ( " << GET_WIDE_KEY() << " < _mid[0] )\n"
+ " _upper = _mid - 2;\n"
+ " else if ( " << GET_WIDE_KEY() << " > _mid[1] )\n"
+ " _lower = _mid + 2;\n"
+ " else {\n"
+ " _trans += ((_mid - _keys)>>1);\n"
+ " goto _match;\n"
+ " }\n"
+ " }\n"
+ " _trans += _klen;\n"
+ " }\n"
+ "\n";
+}
+
+void TabCodeGen::GOTO( ostream &ret, int gotoDest, bool inFinish )
+{
+ ret << "{" << CS() << " = " << gotoDest << "; " <<
+ CTRL_FLOW() << "goto _again;}";
+}
+
+void TabCodeGen::GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << "{" << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << "); " << CTRL_FLOW() << "goto _again;}";
+}
+
+void TabCodeGen::CURS( ostream &ret, bool inFinish )
+{
+ ret << "(_ps)";
+}
+
+void TabCodeGen::TARGS( ostream &ret, bool inFinish, int targState )
+{
+ ret << "(" << CS() << ")";
+}
+
+void TabCodeGen::NEXT( ostream &ret, int nextDest, bool inFinish )
+{
+ ret << CS() << " = " << nextDest << ";";
+}
+
+void TabCodeGen::NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish )
+{
+ ret << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, 0, inFinish );
+ ret << ");";
+}
+
+void TabCodeGen::CALL( ostream &ret, int callDest, int targState, bool inFinish )
+{
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = " <<
+ callDest << "; " << CTRL_FLOW() << "goto _again;}";
+}
+
+void TabCodeGen::CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish )
+{
+ ret << "{" << STACK() << "[" << TOP() << "++] = " << CS() << "; " << CS() << " = (";
+ INLINE_LIST( ret, ilItem->children, targState, inFinish );
+ ret << "); " << CTRL_FLOW() << "goto _again;}";
+}
+
+void TabCodeGen::RET( ostream &ret, bool inFinish )
+{
+ ret << "{" << CS() << " = " << STACK() << "[--" <<
+ TOP() << "]; " << CTRL_FLOW() << "goto _again;}";
+}
+
+void TabCodeGen::BREAK( ostream &ret, int targState )
+{
+ outLabelUsed = true;
+ ret << CTRL_FLOW() << "goto _out;";
+}
+
+void TabCodeGen::writeData()
+{
+ /* If there are any transtion functions then output the array. If there
+ * are none, don't bother emitting an empty array that won't be used. */
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActArrItem), A() );
+ ACTIONS_ARRAY();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyConditions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondOffset), CO() );
+ COND_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondLen), CL() );
+ COND_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), CK() );
+ COND_KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxCondSpaceId), C() );
+ COND_SPACES();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxKeyOffset), KO() );
+ KEY_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( WIDE_ALPH_TYPE(), K() );
+ KEYS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxSingleLen), SL() );
+ SINGLE_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxRangeLen), RL() );
+ RANGE_LENS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxIndexOffset), IO() );
+ INDEX_OFFSETS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( useIndicies ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxIndex), I() );
+ INDICIES();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxState), TT() );
+ TRANS_TARGS_WI();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TA() );
+ TRANS_ACTIONS_WI();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+ }
+ else {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxState), TT() );
+ TRANS_TARGS();
+ CLOSE_ARRAY() <<
+ "\n";
+
+ if ( redFsm->anyActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TA() );
+ TRANS_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+ }
+
+ if ( redFsm->anyToStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), TSA() );
+ TO_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), FSA() );
+ FROM_STATE_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ if ( redFsm->anyEofActions() ) {
+ OPEN_ARRAY( ARRAY_TYPE(redFsm->maxActionLoc), EA() );
+ EOF_ACTIONS();
+ CLOSE_ARRAY() <<
+ "\n";
+ }
+
+ STATE_IDS();
+}
+
+void TabCodeGen::COND_TRANSLATE()
+{
+ out <<
+ " _widec = " << GET_KEY() << ";\n"
+ " _klen = " << CL() << "[" << CS() << "];\n"
+ " _keys = " << ARR_OFF( CK(), "(" + CO() + "[" + CS() + "]*2)" ) << ";\n"
+ " if ( _klen > 0 ) {\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_lower = _keys;\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_mid;\n"
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_upper = _keys + (_klen<<1) - 2;\n"
+ " while (1) {\n"
+ " if ( _upper < _lower )\n"
+ " break;\n"
+ "\n"
+ " _mid = _lower + (((_upper-_lower) >> 1) & ~1);\n"
+ " if ( " << GET_WIDE_KEY() << " < _mid[0] )\n"
+ " _upper = _mid - 2;\n"
+ " else if ( " << GET_WIDE_KEY() << " > _mid[1] )\n"
+ " _lower = _mid + 2;\n"
+ " else {\n"
+ " switch ( " << C() << "[" << CO() << "[" << CS() << "]"
+ " + ((_mid - _keys)>>1)] ) {\n";
+
+ for ( CondSpaceList::Iter csi = condSpaceList; csi.lte(); csi++ ) {
+ CondSpace *condSpace = csi;
+ out << " case " << condSpace->condSpaceId << ": {\n";
+ out << TABS(2) << "_widec = " << CAST(WIDE_ALPH_TYPE()) << "(" <<
+ KEY(condSpace->baseKey) << " + (" << GET_KEY() <<
+ " - " << KEY(keyOps->minKey) << "));\n";
+
+ for ( CondSet::Iter csi = condSpace->condSet; csi.lte(); csi++ ) {
+ out << TABS(2) << "if ( ";
+ CONDITION( out, *csi );
+ Size condValOffset = ((1 << csi.pos()) * keyOps->alphSize());
+ out << " ) _widec += " << condValOffset << ";\n";
+ }
+
+ out <<
+ " break;\n"
+ " }\n";
+ }
+
+ SWITCH_DEFAULT();
+
+ out <<
+ " }\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n";
+}
+
+void TabCodeGen::writeExec()
+{
+ outLabelUsed = false;
+
+ out <<
+ " {\n"
+ " int _klen";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << ", _ps";
+
+ out <<
+ ";\n"
+ " " << UINT() << " _trans;\n";
+
+ if ( redFsm->anyConditions() )
+ out << " " << WIDE_ALPH_TYPE() << " _widec;\n";
+
+ if ( redFsm->anyToStateActions() || redFsm->anyRegActions()
+ || redFsm->anyFromStateActions() )
+ {
+ out <<
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxActArrItem) << POINTER() << "_acts;\n"
+ " " << UINT() << " _nacts;\n";
+ }
+
+ out <<
+ " " << PTR_CONST() << WIDE_ALPH_TYPE() << POINTER() << "_keys;\n"
+ "\n";
+
+ if ( hasEnd ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << P() << " == " << PE() << " )\n"
+ " goto _out;\n";
+ }
+
+ out << "_resume:\n";
+
+ if ( redFsm->errState != 0 ) {
+ outLabelUsed = true;
+ out <<
+ " if ( " << CS() << " == " << redFsm->errState->id << " )\n"
+ " goto _out;\n";
+ }
+
+ if ( redFsm->anyFromStateActions() ) {
+ out <<
+ " _acts = " << ARR_OFF( A(), FSA() + "[" + CS() + "]" ) << ";\n"
+ " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ FROM_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+
+ if ( redFsm->anyConditions() )
+ COND_TRANSLATE();
+
+ LOCATE_TRANS();
+
+ out << "_match:\n";
+
+ if ( redFsm->anyRegCurStateRef() )
+ out << " _ps = " << CS() << ";\n";
+
+ if ( useIndicies )
+ out << " _trans = " << I() << "[_trans];\n";
+
+ out <<
+ " " << CS() << " = " << TT() << "[_trans];\n"
+ "\n";
+
+ if ( redFsm->anyRegActions() ) {
+ out <<
+ " if ( " << TA() << "[_trans] == 0 )\n"
+ " goto _again;\n"
+ "\n"
+ " _acts = " << ARR_OFF( A(), TA() + "[_trans]" ) << ";\n"
+ " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 )\n {\n"
+ " switch ( *_acts++ )\n {\n";
+ ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+
+ if ( redFsm->anyRegActions() || redFsm->anyActionGotos() ||
+ redFsm->anyActionCalls() || redFsm->anyActionRets() )
+ out << "_again:\n";
+
+ if ( redFsm->anyToStateActions() ) {
+ out <<
+ " _acts = " << ARR_OFF( A(), TSA() + "[" + CS() + "]" ) << ";\n"
+ " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ TO_STATE_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ "\n";
+ }
+
+ if ( hasEnd ) {
+ out <<
+ " if ( ++" << P() << " != " << PE() << " )\n"
+ " goto _resume;\n";
+ }
+ else {
+ out <<
+ " " << P() << " += 1;\n"
+ " goto _resume;\n";
+ }
+
+ if ( outLabelUsed )
+ out << " _out: {}\n";
+
+ out << " }\n";
+}
+
+
+void TabCodeGen::writeEOF()
+{
+ if ( redFsm->anyEofActions() ) {
+ out <<
+ " {\n"
+ " " << PTR_CONST() << ARRAY_TYPE(redFsm->maxActArrItem) << POINTER() << "_acts = " <<
+ ARR_OFF( A(), EA() + "[" + CS() + "]" ) << ";\n"
+ " " << UINT() << " _nacts = " << CAST(UINT()) << " *_acts++;\n"
+ " while ( _nacts-- > 0 ) {\n"
+ " switch ( *_acts++ ) {\n";
+ EOF_ACTION_SWITCH();
+ SWITCH_DEFAULT() <<
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n";
+ }
+}
diff --git a/contrib/tools/ragel5/rlgen-cd/tabcodegen.h b/contrib/tools/ragel5/rlgen-cd/tabcodegen.h
new file mode 100644
index 0000000000..745eb18d81
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/tabcodegen.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2001-2006 Adrian Thurston <thurston@cs.queensu.ca>
+ * 2004 Erich Ocean <eric.ocean@ampede.com>
+ * 2005 Alan West <alan@alanz.com>
+ */
+
+/* This file is part of Ragel.
+ *
+ * Ragel is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Ragel 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 Ragel; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TABCODEGEN_H
+#define _TABCODEGEN_H
+
+#include <iostream>
+#include "fsmcodegen.h"
+
+/* Forwards. */
+struct CodeGenData;
+struct NameInst;
+struct RedTransAp;
+struct RedStateAp;
+
+/*
+ * TabCodeGen
+ */
+class TabCodeGen : virtual public FsmCodeGen
+{
+public:
+ TabCodeGen( ostream &out ) : FsmCodeGen(out) {}
+ virtual ~TabCodeGen() { }
+ virtual void writeData();
+ virtual void writeExec();
+
+protected:
+ std::ostream &TO_STATE_ACTION_SWITCH();
+ std::ostream &FROM_STATE_ACTION_SWITCH();
+ std::ostream &EOF_ACTION_SWITCH();
+ std::ostream &ACTION_SWITCH();
+
+ std::ostream &COND_KEYS();
+ std::ostream &COND_SPACES();
+ std::ostream &KEYS();
+ std::ostream &INDICIES();
+ std::ostream &COND_OFFSETS();
+ std::ostream &KEY_OFFSETS();
+ std::ostream &INDEX_OFFSETS();
+ std::ostream &COND_LENS();
+ std::ostream &SINGLE_LENS();
+ std::ostream &RANGE_LENS();
+ std::ostream &TO_STATE_ACTIONS();
+ std::ostream &FROM_STATE_ACTIONS();
+ std::ostream &EOF_ACTIONS();
+ std::ostream &TRANS_TARGS();
+ std::ostream &TRANS_ACTIONS();
+ std::ostream &TRANS_TARGS_WI();
+ std::ostream &TRANS_ACTIONS_WI();
+ void LOCATE_TRANS();
+
+ void COND_TRANSLATE();
+
+ void GOTO( ostream &ret, int gotoDest, bool inFinish );
+ void CALL( ostream &ret, int callDest, int targState, bool inFinish );
+ void NEXT( ostream &ret, int nextDest, bool inFinish );
+ void GOTO_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+ void NEXT_EXPR( ostream &ret, InlineItem *ilItem, bool inFinish );
+ void CALL_EXPR( ostream &ret, InlineItem *ilItem, int targState, bool inFinish );
+ void CURS( ostream &ret, bool inFinish );
+ void TARGS( ostream &ret, bool inFinish, int targState );
+ void RET( ostream &ret, bool inFinish );
+ void BREAK( ostream &ret, int targState );
+
+ virtual std::ostream &TO_STATE_ACTION( RedStateAp *state );
+ virtual std::ostream &FROM_STATE_ACTION( RedStateAp *state );
+ virtual std::ostream &EOF_ACTION( RedStateAp *state );
+ virtual std::ostream &TRANS_ACTION( RedTransAp *trans );
+ virtual void calcIndexSize();
+ virtual void writeEOF();
+};
+
+
+/*
+ * CTabCodeGen
+ */
+struct CTabCodeGen
+ : public TabCodeGen, public CCodeGen
+{
+ CTabCodeGen( ostream &out ) :
+ FsmCodeGen(out), TabCodeGen(out), CCodeGen(out) {}
+};
+
+/*
+ * DTabCodeGen
+ */
+struct DTabCodeGen
+ : public TabCodeGen, public DCodeGen
+{
+ DTabCodeGen( ostream &out ) :
+ FsmCodeGen(out), TabCodeGen(out), DCodeGen(out) {}
+};
+
+
+#endif /* _TABCODEGEN_H */
diff --git a/contrib/tools/ragel5/rlgen-cd/ya.make b/contrib/tools/ragel5/rlgen-cd/ya.make
new file mode 100644
index 0000000000..ef2a59f8c2
--- /dev/null
+++ b/contrib/tools/ragel5/rlgen-cd/ya.make
@@ -0,0 +1,25 @@
+PROGRAM()
+
+NO_UTIL()
+NO_COMPILER_WARNINGS()
+
+PEERDIR(
+ contrib/tools/ragel5/aapl
+ contrib/tools/ragel5/common
+ contrib/tools/ragel5/redfsm
+)
+
+SRCS(
+ fflatcodegen.cpp
+ fgotocodegen.cpp
+ flatcodegen.cpp
+ fsmcodegen.cpp
+ ftabcodegen.cpp
+ gotocodegen.cpp
+ ipgotocodegen.cpp
+ main.cpp
+ splitcodegen.cpp
+ tabcodegen.cpp
+)
+
+END()
diff --git a/contrib/tools/swig/CHANGES b/contrib/tools/swig/CHANGES
new file mode 100644
index 0000000000..9001d01813
--- /dev/null
+++ b/contrib/tools/swig/CHANGES
@@ -0,0 +1,27992 @@
+SWIG (Simplified Wrapper and Interface Generator)
+
+See the CHANGES.current file for changes in the current version.
+See the RELEASENOTES file for a summary of changes in each release.
+Issue # numbers mentioned below can be found on Github. For more details, add
+the issue number to the end of the URL: https://github.com/swig/swig/issues/
+
+Version 4.1.0 (24 Oct 2022)
+===========================
+
+2022-10-24: wsfulton, AndLLA
+ [R] #2386 Fix problems in shared_ptr wrappers where the class names
+ were not consistent when using the shared_ptr template or the actual
+ underlying type.
+
+2022-10-24: wsfulton
+ [R] Add support for special variable replacement in the $typemap()
+ special variable macro for R specific typemaps (rtype, rtypecheck,
+ scoercein, scoereout).
+
+2022-10-24: wsfulton
+ [R] Polymorphism in the wrappers was only working for C++ classes,
+ now this works for C++ structs too.
+
+2022-10-19: olly
+ [Lua] #2126 Fix type resolution between multiple SWIG-wrapped
+ modules.
+
+2022-10-17: wsfulton
+ [R] #2385 Add support for std::vector<std::vector<std::string>>.
+
+2022-10-14: murillo128
+ [Javascript] #2109 Tweak unsigned long and unsigned long long typemaps
+ to create a v8::Number instead of v8::Integer if the value exceeds
+ the size of v8::Integer. Note that the v8::Number value will be
+ imprecise if the value is > MAX_SAFE_INTEGER.
+
+2022-10-14: olly
+ [R] Arrange that destructors of local C++ objects in the wrapper
+ function get run on SWIG_fail (which calls Rf_error() which calls
+ longjmp()).
+
+2022-10-14: olly
+ [Lua] Arrange that destructors of local C++ objects in the wrapper
+ function get run on SWIG_fail (which calls lua_error() which calls
+ longjmp()).
+
+2022-10-13: wsfulton
+ [R] Add missing SWIGTYPE *const& typemaps for supporting pointers
+ by const reference.
+
+2022-10-10: wsfulton
+ #2160 Fix compile error when using templates with more than one template
+ parameter and used as an input parameter in a virtual method in a
+ director class (problem affecting most of the scripting languages).
+
+2022-10-10: treitmayr, wsfulton
+ [Python, Ruby] #1811 #1823 Fix invalid code generated in some cases when
+ returning a pointer or reference to a director-enabled class instance.
+ This previously only worked in very simple cases, now return types are
+ resolved to fix. A bug in template instantiations using pointers also
+ works now.
+
+2022-10-06: wsfulton
+ [CFFI] #1966 #2200 Remove code for Common Lisp CFFI. We dropped support
+ for it in SWIG 4.0.0 by disabling it as the first stage. This is the
+ final stage for complete removal as there has been no meaningful
+ progress to revive it to the status of experimental language.
+
+2022-10-06: olly
+ [Python] #2390 Remove deprecated and apparently useless defarg.swg
+
+ The only documentation is in the file itself and describes a Python
+ wrapper around the C function defined here, but digging though the
+ git history this Python wrapper doesn't seem to have ever actually
+ been generated by SWIG.
+
+ This file was also marked as deprecated in 2005.
+
+2022-10-06: wsfulton
+ [Java] #2048 Fix quoting for doxygen \image command to quote the output
+ file name generated into the html src attribute.
+
+2022-10-05: benjamin-sch
+ [Python] added an interpreter counter to fix deinitialization
+ issues if multiple subinterpreters are used
+
+2022-10-05: olly, wsfulton
+ #672 Add support for parsing C++11 final classes such as:
+
+ class X final {};
+
+ This no longer gives a syntax error.
+
+2022-10-05: wsfulton
+ [OCaml] Fix %rename for enum items. Previously the rename had no effect.
+
+2022-10-05: olly
+ #1465 Report errors in preprocessor expressions by default
+
+ Until now SWIG quietly ignored such errors unless -Wextra (or -Wall
+ which implies -Wextra) was passed, but this is unhelpful as it tends
+ to hide genuine problems. To illustrate this point, enabling this
+ warning by default revealed a typo in the preproc_defined.i
+ testcase in SWIG's own testsuite.
+
+ If you really don't want to see this warning, you can suppress it
+ with command line option -w202 or by using this in your interface
+ file:
+
+ %warnfilter(SWIGWARN_PP_EVALUATION);
+
+ Both will work with older versions of SWIG too.
+
+2022-10-04: olly
+ #1050 Consistently define SWIG_VERSION both at SWIG-time and in
+ the generated wrapper. Best practice remains to check at SWIG-time
+ where possible because that results in smaller generated wrapper
+ sources.
+
+ SWIGGO and SWIGJAVASCRIPT are now defined in the generated wrappers
+ to match behaviour for all other target languages.
+
+ The undocumented SWIGVERSION macro is no longer defined.
+
+2022-09-29: olly
+ #2303 SWIG's internal hash tables now use a better hash function.
+
+ The old hash function only considerd the last five characters
+ plus the least significant bit of the last-but-sixth character,
+ which as you might guess generated a lot of many-way collisions.
+
+ This change seems to give about a 4% reduction in wallclock time
+ for processing li_std_list_wrap.i from the testsuite for Python.
+ The hash collision rate for this example drops from 39% to 0!
+
+2022-09-29: wsfulton
+ #2303 Type tables are now output in a fixed order whereas previously
+ the order may change with any minor input code change. This shouldn't
+ affect users except SWIG_TypePrettyName may output a different C/C++
+ typedef to a type - it's used mostly for showing errors when the type
+ passed to a function is wrong.
+
+2022-09-29: olly
+ [PHP] Dynamic class properties are no longer supported by default.
+
+ Historically PHP has supported dynamic class properties and SWIG
+ has implemented them too (because we implement the magic __get(),
+ __set() and __isset() methods we need to include explicit
+ handling).
+
+ PHP 8.2 deprecates dynamic class properties - initially they'll
+ warn, and apparently they'll not work by default in PHP 9.0:
+ https://wiki.php.net/rfc/deprecate_dynamic_properties
+
+ In PHP code dynamic properties can be enabled for a class by
+ marking that class with the attribute `#[AllowDynamicProperties]`.
+
+ To follow this PHP change, in SWIG you now need to specify
+ `%feature("php:allowdynamicproperties", 1) Foo;` (or
+ `%feature("php:allowdynamicproperties", 1)` to enable it for
+ all wrapped classes). Unknown features are ignored, so you can add
+ it unconditionally and it'll work with older SWIG too.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-09-19: wsfulton
+ #1484 Fixes for class inheritance with the same name in different namespaces
+ such as:
+
+ namespace A { class Bar {}; }
+ namespace B { template<typename T, typename U> class Bar : public A::Bar {}; }
+
+2022-09-19: wsfulton
+ #2316 Remove swig.spec file and srcrpm makefile target. These are very out of date
+ and don't seem to be used by RPM based Linux distributions which have their
+ own version of swig.spec.
+
+2022-09-17: wsfulton
+ [Go, Guile, Racket, Scilab] Add throws typemaps for std::string so that thrown
+ string exception messages can be seen.
+
+2022-09-17: wsfulton
+ [Racket] Add throws typemaps for char * so that thrown string exception
+ messages can be seen from Racket.
+
+2022-09-17: wsfulton
+ [Javascript, Octave, R] Improve exceptions for %catches and exception
+ specifications for native types. String exception messages are shown as
+ the exception message instead of just the type of the exception.
+
+2022-09-17: wsfulton
+ Add missing typecheck typemaps for std::auto_ptr and std::unique_ptr to
+ fix overloading when using these types.
+
+2022-09-17: wsfulton
+ [Guile] Add error checking to SWIGTYPE and SWIGTYPE & in typemaps to prevent
+ seg faults when passing #nil to these parameter types.
+
+2022-09-16: wsfulton
+ #999 Provide SWIGTYPE MOVE typemaps in swigmove.i for implementing full
+ move semantics when passing parameters by value.
+
+2022-08-31: wsfulton
+ #999 Improve move semantics when using rvalue references.
+ The SWIGTYPE && input typemaps now assume the object has been moved.
+
+ These typemaps have been changed assuming that after the function call,
+ the rvalue reference parameter has been moved. The parameter's proxy class
+ that owns the C++ object thus has the underlying pointer set to null
+ so that the (moved from, but still valid) C++ object cannot be used again
+ and the object is additionally deleted.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-08-28: wsfulton
+ [Octave] SWIG now marshals a C/C++ NULL pointer into the null matrix, [].
+ SWIG has always marshalled the null matrix into a NULL pointer; this remains
+ and now we have consistency in representing a NULL pointer.
+
+2022-08-26: wsfulton
+ [Racket] SWIG now marshals a C/C++ NULL pointer into a null value by calling
+ scheme_make_null(), so that scheme's null? is true for a NULL C/C++ pointer value.
+
+2022-08-18: wsfulton
+ [Racket] Add support for std::unique_ptr in std_unique_ptr.i.
+ Add support for std::auto_ptr in std_auto_ptr.i.
+
+2022-08-13: wsfulton
+ [Guile] Add support for std::unique_ptr in std_unique_ptr.i.
+ Add support for std::auto_ptr in std_auto_ptr.i.
+
+2022-08-11: wsfulton
+ [Lua] Add support for std::unique_ptr in std_unique_ptr.i.
+ Add support for std::auto_ptr in std_auto_ptr.i.
+
+2022-08-05: wsfulton
+ [D] Fix occasional undefined behaviour with inheritance hierarchies, particularly
+ when using virtual inheritance as the pointers weren't correctly upcast from derived
+ class to base class when stored in the base's proxy class.
+
+2022-08-05: wsfulton
+ [D] Add support for std::unique_ptr in std_unique_ptr.i.
+ Add support for std::auto_ptr in std_auto_ptr.i.
+
+2022-08-03: wsfulton
+ [Javascript] Add support for std::unique_ptr in std_unique_ptr.i.
+ Add support for std::auto_ptr in std_auto_ptr.i.
+
+2022-08-02: wsfulton
+ [Octave] Add support for std::unique_ptr in std_unique_ptr.i.
+ Add support for std::auto_ptr in std_auto_ptr.i.
+
+2022-08-01: wsfulton
+ [Python] Add initialisers for additional members in PyHeapTypeObject
+ (builtin mode) for Python-3.11 - _ht_tpname, _spec_cache.
+
+2022-07-30: wsfulton
+ C++20 has deprecated std::basic_string<>::reserve() and the C++11 method
+ std::basic_string<>::shrink_to_fit() is a replacement that can be used.
+ std_string.i and std_wstring.i provided wrappers for reserve with the following
+ template instantiations:
+
+ %template(string) std::basic_string<char>;
+ %template(wstring) std::basic_string<wchar_t>;
+
+ The reserve method is no longer wrapped, however the shrink_to_fit() method
+ can be used as an alternative from the target language (the generated wrappers
+ call reserve() instead if C++<=20).
+
+ Note that std::basic_string<>::reserve(size_t n) is still wrapped unchanged.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-07-30: wsfulton
+ [Tcl] Add support for std::unique_ptr in std_unique_ptr.i.
+ Add support for std::auto_ptr in std_auto_ptr.i.
+
+2022-07-27: ZackerySpytz, olly
+ #1678 Support parsing C++20 templated lambdas.
+
+2022-07-27: ZackerySpytz, olly
+ #1622 Add support for the C++20 spaceship operator (<=>).
+
+2022-07-26: olly
+ [Tcl] https://sourceforge.net/p/swig/bugs/977/
+ Fix handling of long long on 32-bit platforms. This fix raises
+ SWIG's minimum supported Tcl version to 8.4.0 (which was released
+ just under 20 years ago).
+
+2022-07-26: olly
+ Fix incorrect operator precedence in preprocessor expressions.
+
+2022-07-25: olly
+ Support for C++14 binary integer literals in preprocessor expressions.
+
+2022-07-20: wsfulton
+ [C#, Java] Ensure the order of interfaces generated in proxy interfaces for the
+ %interface family of macros is the same as that parsed from the bases in C++.
+
+2022-07-20: jicks, Ingener74, olly
+ #422 [Python] Fix mishandling of a Python class inheriting from
+ multiple SWIG-wrapped director classes.
+
+2022-07-19: wsfulton
+ #692 [C#, Java, Perl, Python, Ruby] std::unique_ptr and std::auto_ptr typemaps
+ provided for inputs types in std_unique_ptr.i and std_auto_ptr.i.
+
+ Now these smart pointers can be used as input parameters to functions. A proxy
+ class instance transfers memory ownership of the underlying C++ object from the
+ proxy class to a smart pointer instance passed to the wrapped function.
+
+2022-07-19: jschueller
+ [Python] #2314 Drop support for Python 3.2.
+
+2022-07-19: olly
+ Remove remaining support code for classic macos, which has not been
+ supported by Apple for over 20 years now.
+
+2022-07-12: wsfulton
+ #999 Performance optimisation for parameters passed by value that are C++11 movable.
+ The C++ wrappers create a temporary variable for a parameter to be passed to a
+ function. This is initially default constructed and then copy assigned from the
+ instance being passed in from the target language. This is unchanged, however,
+ when the temporary variable is passed to the wrapped function, it is now done using
+ std::move. If the type is move constructible, the move constructor will be used
+ instead of the copy constructor.
+
+2022-07-12: wsfulton
+ [Perl] Add std::auto_ptr support in std_auto_ptr.i library file.
+
+2022-07-12: erezgeva
+ [Perl] Add std::unique_ptr support in std_unique_ptr.i library file.
+
+2022-07-07: jmarrec
+ #1158 #2286 Add basic support for C++11 attributes. These are now
+ crudely ignored by SWIG's parser's tokeniser, which is better that
+ failing with a parse error.
+
+2022-07-05: ianlancetaylor
+ [Go] #2245 Handle NULL pointers for string* conversions.
+ Rearrange generation of director methods and rename
+ receiver argument from p to swig_p.
+
+2022-07-03: wsfulton
+ #999 Performance optimisation for directors for classes passed by value. The directorin
+ typemaps in the director methods now use std::move on the input parameter when
+ copying the object from the stack to the heap prior to the callback into the target
+ language, thereby taking advantage of move semantics if available.
+
+2022-07-02: wsfulton
+ #1722 [C#, Java, Python, Ruby] Add std::unique_ptr support. Ported from std::auto_ptr.
+ Use the %unique_ptr(T) macro as follows for usage std::unique_ptr<T>. For example, for
+ a class called Klass:
+
+ %include "std_unique_ptr.i"
+ %unique_ptr(Klass)
+
+ Support is currently limited to only returning a std::unique_ptr from a function.
+
+2022-06-29: wsfulton
+ #999 #1044 Enhance SWIGTYPE "out" typemaps to use std::move when copying
+ objects, thereby making use of move semantics when wrapping a function returning
+ by value if the returned type supports move semantics.
+
+ Wrapping functions that return move only types 'by value' now work out the box
+ without having to provide custom typemaps.
+
+ The implementation removed all casts in the "out" typemaps to allow the compiler to
+ appropriately choose calling a move constructor, where possible, otherwise a copy
+ constructor. The implementation also required modifying SwigValueWrapper to
+ change a cast operator from:
+
+ SwigValueWrapper::operator T&() const;
+
+ to
+
+ #if __cplusplus >=201103L
+ SwigValueWrapper::operator T&&() const;
+ #else
+ SwigValueWrapper::operator T&() const;
+ #endif
+
+ This is not backwards compatible for C++11 and later when using the valuewrapper feature
+ if a cast is explicitly being made in user supplied "out" typemaps. Suggested change
+ in custom "out" typemaps for C++11 and later code:
+
+ 1. Try remove the cast altogether to let the compiler use an appropriate implicit cast.
+ 2. Change the cast, for example, from static_cast<X &> to static_cast<X &&>, using the
+ __cplusplus macro if all versions of C++ need to be supported.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-06-15: wsfulton
+ #2039 Add move assignment operator to SwigValueWrapper used by the
+ valuewrapper feature.
+
+2022-06-04: sethrj
+ Enhance $typemap to support typemap attributes.
+
+ $typemap(method:attribute, typepattern)
+
+ For example:
+
+ %typemap(cstype, out="object") XClass "XClass"
+ %typemap(cscode) BarClass %{
+ $typemap(cstype:out, XClass) bar() {
+ return null;
+ }
+
+ which expands to
+
+ object bar() {
+ return null;
+ }
+
+2022-05-30: wsfulton
+ [C#, D] Add new special variable expansion: $imfuncname.
+ Expands to the function name called in the intermediary class.
+
+2022-05-30: LindleyF
+ [Java] #2042 Add new special variable expansion: $imfuncname.
+ Expands to the function name called in the intermediary class.
+
+2022-05-28: jkuebart
+ [Java] On some versions of Android, specifically Android 6,
+ detaching the current thread from the JVM after every invocation
+ causes a memory leak.
+
+ Offer SWIG_JAVA_DETACH_ON_THREAD_END to configure a behaviour
+ where the JVM is only detached in the thread destructor.
+
+ See https://developer.android.com/training/articles/perf-jni#threads.
+
+2022-05-27: xypron
+ [Python] #2277 Define PY_SSIZE_T_CLEAN macro before #include "Python.h" as
+ recommended in Python 3.7 and later.
+
+ To avoid this macro definition, add the following to your interface file so
+ that SWIG_NO_PY_SSIZE_T_CLEAN is defined at the beginning of the C++ wrappers:
+
+ %begin %{
+ #define SWIG_NO_PY_SSIZE_T_CLEAN
+ %}
+
+2022-05-26: rokups
+ [C#] #1323 Modify SwigDerivedClassHasMethod for a more efficient director
+ implementation when calling virtual methods that are not overridden.
+
+2022-05-15: erezgeva, eiselekd
+ [Lua, Perl, Octave, PHP, Tcl] #2275 #2276 #2283 Add argcargv.i library containing
+ (int ARGC, char **ARGV) multi-argument typemaps.
+
+ Document this library in Typemaps.html.
+
+2022-05-07: KrisThielemans
+ [Python] Fix "too many initializers for 'PyHeapTypeObject'" errors
+ using PyPy 3.8 and later.
+
+2022-05-04: wsfulton
+ [C#] Add C# wchar_t * director typemaps
+
+2022-04-20: cminyard
+ Fix an issue where newlines were not properly generated
+ for godirectorin typemaps. If you have a virtual function
+ not assigned to zero, in some cases it won't generate a
+ newline and you will see errors:
+ example.go:1508:3: expected ';', found swig_r
+ when compiling the go code.
+
+ Also add an example of using goin and godirectorin and add
+ a test for this situation.
+
+2022-04-29: jason-daly, JerryJoyce, wsfulton
+ [C#] #1233 Add wchar_t * and std::wstring Unicode string support on Linux.
+
+2022-04-11: robinst
+ #2257 Fix new Ruby 3.2 warning "undefining the allocator of T_DATA
+ class swig_runtime_data".
+
+2022-04-07: olly
+ #1750 SWIG now recognises and ignores Doxygen group commands `@{` and `@}`.
+
+2022-04-06: wsfulton
+ ./configure now enables C++11 and later C++ standards testing by default (when
+ running: 'make check').
+
+ The options to control this testing are the same:
+
+ ./configure --enable-cpp11-testing
+ ./configure --disable-cpp11-testing
+
+ But the former is now the default and the latter can be used to turn off C++11 and
+ later C++ standards testing.
+
+2022-04-06: wsfulton
+ [Python] #1635 The "autodoc" feature no longer overrides Doxygen comments
+ in the generated docstring.
+
+ If a "docstring" feature is present it will still override a Doxygen comment.
+ If the "autodoc" feature is also present, the combined "autodoc" and "docstring"
+ will override the Doxygen comment. If no "docstring" is present then the
+ "autodoc" feature will not be generated when there is a Doxygen comment.
+
+ This way the "autodoc" feature can be specified and used to provide documentation
+ for 'missing' Doxygen comments.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-04-01: olly
+ Remove undocumented and non-functional -browse command line option.
+
+2022-03-26: eltoder
+ [Python] #1684 Use different capsule names with and without -builtin
+
+ Types generated with and without -builtin are not compatible. Mixing
+ them in a common type list leads to crashes. Avoid this by using
+ different capsule names: "type_pointer_capsule" without -builtin and
+ "type_pointer_capsule_builtin" with.
+
+2022-03-25: wsfulton
+ The debug command line options that display parse tree nodes
+ (-debug-module, -debug-top, -debug-symtabs) now display previously hidden
+ linked list pointers which are useful for debugging parse trees.
+
+ Added new command line option -debug-quiet. This suppresses the display
+ of most linked list pointers and symbol table pointers in the parse tree nodes.
+
+ The keys in the parse tree node are now shown in alphabetical order.
+
+2022-03-24: wsfulton
+ #2244 Fix using declaration in derived class bugs when all the base
+ class's overloaded methods were overridden in the derived class -
+ fixes "multiply defined" errors.
+
+2022-03-23: wsfulton
+ [Python] #1779 The -py3 option is deprecated and now has no effect on the
+ code generated. Use of this option results in a deprecated warning.
+ The related SWIGPYTHON_PY3 macro that this option defined is no longer generated.
+
+ Note that %pythonnondynamic feature generates a metaclass that works on both
+ Python 2 and Python 3.
+
+2022-03-21: wsfulton
+ [Python] #1779 pyabc.i for abstract base classes now supports versions of
+ Python prior to 3.3 by using the collection module for these older versions.
+ Python-3.3 and later continue to use the collections.abc module.
+ The -py3 option no longer has any effect on the %pythonabc feature.
+
+2022-03-21: jschueller, jim-easterbrook, wsfulton
+ [Python] #2137 C++ static member functions no longer generate a "flattened"
+ name in the Python module. For example:
+
+ s = example.Spam()
+ s.foo() # Spam::foo() via an instance
+ example.Spam.foo() # Spam::foo() using class method
+ example.Spam_foo() # Spam::foo() "flattened" name
+
+ The "flattened" name is no longer generated, but can be generated
+ by using the new -flatstaticmethod option.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-03-18: ianlancetaylor
+ [Go] #337 Implement %extend base methods in child classes.
+
+2022-03-17: olly
+ [Python] #1779 SWIG's Python test-suite and examples are now
+ run with Python 3 by default. To run them with Python 2, set
+ PY2 to a non-empty value, e.g.:
+
+ make check-python-test-suite PY2=1
+
+2022-03-16: olly
+ [Go] #683 -intgosize is now optional - if not specified the
+ generated C/C++ wrapper code will use ptrdiff_t for intgo and
+ size_t for uintgo.
+
+2022-03-15: ianlancetaylor
+ [Go] Add typemaps for std::string*.
+
+2022-03-15: ianlancetaylor
+ [Go] Don't convert arrays to pointers if there is a "gotype"
+ typemap entry.
+
+2022-03-15: ianlancetaylor
+ [Go] Add documentation note about Go and C++ exceptions.
+
+2022-03-12: wsfulton
+ #1524 %interface family of macros no longer contain the getter/setter
+ methods for wrapping variables. The interface only contains
+ virtual and non-virtual instance methods, that is, no static methods.
+ Enums are also no longer added to the interface (affects Java only where
+ they were missing from the proxy class, C# never had them in the interface).
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-03-12: wsfulton
+ #1277 Fixes for the family of %interface macros, %interface,
+ %interface_impl and %interface_custom fixes for overloaded methods
+ in an inheritance chain.
+
+ When C++ methods are not able to be overloaded in a derived class,
+ such as when they differ by just const, or the target language
+ parameters types are identical even when the C++ parameter types
+ are different, SWIG will ignore one of the overloaded methods with
+ a warning. A %ignore is required to explicitly ignore one of the
+ overloaded methods to avoid the warning message. Methods added
+ in the derived classes due to one of the %interface macros are now
+ similarly ignored/not added to the derived class.
+
+ The methods added to the derived classes can now also be modified
+ via %feature and %rename.
+
+2022-03-08: ianlancetaylor
+ [Go] Treat a nil argument as a NULL pointer.
+
+2022-03-08: ianlancetaylor
+ [Go] Add documentation notes about thread local storage.
+
+2022-03-08: olly
+ #1006 SWIG now copes with an interface filename specified on the
+ command line which contains a closing parenthesis `)`, and more
+ generally with attributes to `%include` and `%import` which
+ are quoted and contain parentheses.
+
+2022-03-07: Omar Medina
+ [Tcl] https://sourceforge.net/p/swig/bugs/1290/
+ Fix SWIG_AsWCharPtrAndSize() to actually assign to result
+ variable. It looks like SWIG/Tcl wide character handling is
+ currently fundamentally broken except on systems which use wide
+ characters as the system encoding, but this should fix wrapping
+ functions which take a wide string as a parameter on Microsoft
+ Windows.
+
+2022-03-07: olly
+ [Javascript] #682 Fix handling of functions which take void*.
+
+2022-03-06: olly
+ SWIG should now reliably exit with status 0 if the run was
+ successful and status 1 if there was an error (or a warning and
+ -Werror was in effect).
+
+ Previously in some situations SWIG would try to exit with the
+ status set to the number of errors encountered, but that's
+ problematic - for example if there were 256 errors this would
+ result in exit status 0 on most platforms. Also some error
+ statuses have special meanings e.g. those defined by <sysexits.h>.
+ Also SWIG/Javascript tried to exit with status -1 in a few places
+ (which typically results in exit status 255).
+
+2022-03-05: wsfulton
+ #1441 Fix using declaration in derived class incorrectly introducing a method
+ from a base class when the using declaration is declared before the method
+ declaration. Problem occurred when within a namespace and the parameter types
+ in the method signatures were not fully qualified.
+2022-03-05: ianlancetaylor
+ [Go] Treat non-const references as pointers.
+
+2022-03-05: ianlancetaylor
+ In SWIG Go testsuite, fail test if "go build" fails.
+
+2022-03-03: olly
+ #1901 #2223 SWIG should now always exit cleanly if memory
+ allocation fails, including removing any output files created
+ during the current run.
+
+ Previously most places in the code didn't check for a NULL return
+ from malloc()/realloc()/calloc() at all, typically resulting in
+ undefined behaviour; some places used assert() to check for a NULL
+ return (which is a misuse of assert() and such checks disappear if
+ built with NDEBUG defined leaving us back with undefined
+ behaviour).
+
+2022-03-03: olly
+ #891 Report errors for typemap attributes without a value
+ (previously SWIG segfaulted) and for typemap types with a value
+ (previously the value was quietly ignored).
+
+ The old way of specifying a language name in the typemap attributes
+ is no longer supported (it has been deprecated for 16 years).
+
+2022-03-02: geographika, wsfulton
+ [Python] #1951 Add Python variable annotations support.
+
+ Both function annotations and variable annotations are turned on using the
+ "python:annotations" feature. Example:
+
+ %feature("python:annotations", "c");
+
+ struct V {
+ float val;
+ };
+
+ The generated code contains a variable annotation containing the C float type:
+
+ class V(object):
+ val: "float" = property(_example.V_val_get, _example.V_val_set)
+ ...
+
+ Python 3.5 and earlier do not support variable annotations, so variable
+ annotations can be turned off with a "python:annotations:novar" feature flag.
+ Example turning on function annotations but not variable annotations globally:
+
+ %feature("python:annotations", "c");
+ %feature("python:annotations:novar");
+
+ or via the command line:
+
+ -features python:annotations=c,python:annotations:novar
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-02-27: wsfulton
+ [Python] #735 #1561 Function annotations containing C/C++ types are no longer
+ generated when using the -py3 option. Function annotations support has been
+ moved to a feature to provide finer grained control. It can be turned on
+ globally by adding:
+
+ %feature("python:annotations", "c");
+
+ or by using the command line argument:
+
+ -features python:annotations=c
+
+ Also see entry dated 2022-03-02, regarding variable annotations.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-02-26: wsfulton
+ #655 #1840 Add new warning WARN_LANG_USING_NAME_DIFFERENT to warn when a
+ method introduced by a using declaration in a derived class cannot
+ be used due to a conflict in names.
+
+2022-02-24: olly
+ #1465 An invalid preprocessor expression is reported as a pair of
+ warnings with the second giving a more detailed message from the
+ expression evaluator. Previously SWIG prefixed the second message
+ with "Error:" - that was confusing as it's actually only a warning
+ by default so we've now dropped this prefix.
+
+ Before:
+
+ x.i:1: Warning 202: Could not evaluate expression '1.2'
+ x.i:1: Warning 202: Error: 'Floating point constant in preprocessor expression'
+
+ Now:
+
+ x.i:1: Warning 202: Could not evaluate expression '1.2'
+ x.i:1: Warning 202: Floating point constant in preprocessor expression
+
+2022-02-23: olly
+ #1384 Fix a preprocessor expression evaluation bug. A
+ subexpression in parentheses lost its string/int type flag and
+ instead used whatever type was left in the stack entry from
+ previous use. In practice we mostly got away with this because
+ most preprocessor expressions are integer, but it could have
+ resulted in a preprocessor expression incorrectly evaluating as
+ zero. If -Wextra was in use you got a warning:
+
+ Warning 202: Error: 'Can't mix strings and integers in expression'
+
+2022-02-21: davidcl
+ [Scilab] Improve 5.5.2, 6.0.0 and 6.1.0 support.
+
+ For Scilab 5, long names are reduced to small names preserving the
+ class prefix and accessor suffix (get or set).
+
+ For Scilab 6, long names with the class prefix and accessor suffix
+ should be used on the user code.
+
+ The `-targetversion` option has been removed as the generated code
+ now detects the Scilab version in loader.sce or builder.sce.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-02-20: wsfulton
+ Fix %warnfilter warning suppress for warning 315 SWIGWARN_PARSE_USING_UNDEF.
+
+2022-02-17: olly
+ [PHP] https://sourceforge.net/p/swig/bugs/1211/
+ Fix to call cleanup code in exception situations and not to invoke
+ the freearg typemap twice in certain situations.
+
+2022-02-15: olly
+ #300 #368 Improve parser handling of % followed immediately by
+ an identifier. If it's not a recognised directive the scanner
+ now emits MODULO and then rescans what follows, and if the parser
+ then gives a syntax error we report it as an unknown directive.
+ This means that `a%b` is now allowed in an expression, and that
+ things like `%std::vector<std::string>` now give an error rather
+ than being quietly ignored.
+
+2022-02-11: adr26
+ [Python] #2154 Fix memory leak.
+
+ SWIG python objects were being freed after the corresponding SWIG
+ module information was destroyed in Python 3, causing leaks when as
+ a result the object destructor could not be invoked. To prevent this
+ misordering, SWIG python objects now obtain a reference to the
+ Python capsule wrapping the module information, so that the module
+ information is correctly destroyed after all SWIG python objects
+ have been freed (and corresponding destructors invoked).
+
+2022-02-10: olly
+ [Tcl] https://sourceforge.net/p/swig/bugs/1207/
+ https://sourceforge.net/p/swig/bugs/1213/
+
+ Fix Tcl generic input typemap for std::vector.
+
+2022-02-07: sethrj
+ #2196 Add alternative syntax for specifying fragments in typemaps.
+
+ New syntax:
+ %typemap("in", fragment="frag1", fragment="frag2", fragment="frag3") {...}
+ which is equivalent to:
+ %typemap(in, fragment="frag1,frag2,frag3") {...}
+
+
+2022-02-07: olly
+ #1806 Remove support for the "command" encoder, which was mostly
+ intended for use in `%rename` - most uses can be achieved using
+ the "regex" encoder, so we recommend using that instead.
+
+ The "command" encoder suffers from a number of issues - as the
+ documentation for it admitted, "[it] is extremely slow compared to
+ all the other [encoders] as it involves spawning a separate process
+ and using it for many declarations is not recommended" and that it
+ "should generally be avoided because of performance
+ considerations".
+
+ But it's also not portable. The design assumes that `/bin/sh`
+ supports `<<<` but that's a bash-specific feature so it doesn't
+ work on platforms where `/bin/sh` is not bash - it fails on
+ Debian, Ubuntu and probably some other Linux distros, plus most
+ non-Linux platforms. Microsoft Windows doesn't even have a
+ /bin/sh as standard.
+
+ Finally, no escaping of the passed string is done, so it has
+ potential security issues (though at least with %rename the input
+ is limited to valid C/C++ symbol names).
+
+2022-02-06: olly
+ #2193 -DFOO on the SWIG command line now sets FOO to 1 for
+ consistency with C/C++ compiler preprocessors. Previously
+ SWIG set FOO to an empty value.
+
+ Existing invocations of SWIG with `-DFOO` where the empty value
+ matters can be updated to `-DFOO=` which should work with both
+ old and new releases of SWIG.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2022-02-06: sethrj
+ #2194 Classes that are non-assignable due to const data or const
+ reference members are now automatically detected.
+
+2022-02-04: friedrichatgc
+ [Octave] #1672 Fix for isobject for Octave 4.4 - 6.0.
+
+2022-02-03: olly
+ [C#] #283 #998 Fix memory leak in directorin typemap for
+ std::string.
+
+2022-02-03: olly
+ [Python] #967 Make `self` parameter available to user typemaps.
+
+2022-02-03: teythoon
+ [Python] #801 Fix -Wunused-parameter warnings with builtin,
+
+2022-02-03: teythoon
+ #801 Fix -Wstrict-prototypes warnings in generated pointer
+ functions.
+
+2022-02-03: olly
+ #660 https://sourceforge.net/p/swig/bugs/1081/
+ Default parameter values containing method calls are now parsed and
+ handled - e.g. `x->foo(3,4)` and `y.z()`.
+
+2022-02-02: olly
+ [Ruby] https://sourceforge.net/p/swig/bugs/1136/ Fix remove of prefix
+ from method name to only remove it at the start.
+
+2022-02-01: olly
+ #231 Handle returning an object by reference in a C++ trailing
+ return type.
+
+2022-02-01: davidcl
+ [Scilab] #745 use SWIG_<module>_Init() as a C module init function.
+
+2022-02-01: olly
+ [OCaml] #2083 Fix to work when CAML_SAFE_STRING is on, which it is
+ by default in recent Ocaml releases.
+
+2022-01-31: mreeez
+ https://sourceforge.net/p/swig/bugs/1147/
+ Fix copyToR() generated for a struct in a namespace.
+
+2022-01-29: fschlimb
+ #655 Better handling of using declarations.
+
+2022-01-29: dontpanic92
+ [Go] #676 Fix code generated for a C++ class with a non-capitalised
+ name.
+
+2022-01-26: trex58
+ #1919 #1921 #1923 Various fixes for AIX portability.
+
+2022-01-26: olly
+ #1935 Don't crash on an unclosed HTML tag in a doxygen comment
+ when -doxygen is specified.
+
+2022-01-25: olly
+ Constant expressions now support member access with `.` such as
+ `foo.bar`. Previous this only worked in a case like `x->foo.bar`.
+
+2022-01-25: olly
+ #2091 Support most cases of `sizeof` applied to an expression
+ in constant expressions. Previously there was only support for
+ `sizeof(<type>)` and expressions which syntactically look like a
+ type (such as `sizeof(foo)`).
+
+2022-01-25: olly
+ #80 #635 https://sourceforge.net/p/swig/bugs/1139/
+ Add support for parsing common cases of `<` and `>` comparisons
+ in constant expressions. Adding full support for these seems hard
+ to do without introducing conflicts into the parser grammar, but in
+ fact all reported cases have had parentheses around the comparison
+ and we can support that with a few restrictions on the left side of
+ `<`.
+
+2022-01-25: wsfulton
+ New warning 327 for extern templates, eg:
+
+ extern template class std::vector<int>;
+ extern template void Func<int>();
+
+ results in warning
+
+ example.i:3: Warning 327: Extern template ignored.
+ example.i:4: Warning 327: Extern template ignored.
+
+ Extern template classes previously resulted in warning 320.
+
+2022-01-24: romintomasetti
+ #2131 #2157 C++11 extern function template parsing error fix.
+
+2022-01-21: wsfulton
+ #2120 #2138 Replace legacy PCRE dependency with PCRE2.
+ This requires changes for building SWIG from source. See updated
+ html documentation in Preface.html and Windows.html. Updated
+ instructions are also shown when running ./configure if PCRE2 is not
+ found. Note that debian based systems can install PCRE2 using:
+
+ apt install libpcre2-dev
+
+ Note that https://github.com/swig/swig/wiki/Getting-Started also has
+ updated information for building from source.
+
+2022-01-19: olly
+ [PHP] #2027 Automatically generate PHP type declarations for PHP 8.
+ The generate code still compiles for PHP 7.x, but without type
+ declarations since PHP 7.x has much more limited type declaration
+ support.
+
+2022-01-18: olly
+ [Perl] #1629 Perl 5.8.0 is now the oldest version we aim to support.
+
+2022-01-14: wsfulton
+ [Python] Fix %callback and specifying the callback function as a
+ static member function using Python staticmethod syntax, such as
+ Klass.memberfunction instead of Klass_memberfunction when using
+ -builtin and -fastproxy.
+
+2022-01-11: wsfulton
+ [Python] Accept keyword arguments accessing static member functions when
+ using -builtin and kwargs feature and Python class staticmethod syntax.
+ The missing keyword argument support was only when using the
+ class staticmethod syntax, such as Klass.memberfunction, and not when
+ using the flat static method syntax, such as Klass_memberfunction.
+
+2022-01-04: juierror
+ [Go] #2045 Add support for std::array in std_array.i.
+
+2021-12-18: olly
+ [PHP] Add PHP keyword 'readonly' (added in 8.1) to the list SWIG
+ knows to automatically rename. This keyword is special in that PHP
+ allows it to be used as a function (or method) name.
+
+2021-12-07: vstinner
+ [Python] #2116 Python 3.11 support: use Py_SET_TYPE()
+
+2021-12-05: rwf1
+ [Octave] #2020 #1893 Add support for Octave 6 up to and including 6.4.
+ Also add support for compiling with -Bsymbolic which is used by default
+ by mkoctfile.
+
+2021-12-02: jsenn
+ [Python] #2102 Fixed crashes when using embedded Python interpreters.
+
+2021-11-12: wsfulton
+ [Javascript] v8 and node only. Fix mismatched new char[] and free()
+ when wrapping C code char arrays. Now calloc is now used instead of
+ new char[] in SWIG_AsCharPtrAndSize.
+
+2021-10-03: ajrh1
+ [Perl] #2074: Avoid -Wmisleading-indentation in generated code
+ when using gcc11.
+
+2021-10-03: jschueller
+ [CMake] #2065: Add option to enable or disable PCRE support.
+
+2021-09-16: ianlancetaylor
+ [Go] Improved _cgo_panic implementation.
+
+2021-09-16: ianlancetaylor
+ [Go] Don't use crosscall2 for panicking. Instead rely on documented
+ and exported interfaces.
+
+2021-09-14: ianlancetaylor
+ [Go] Remove -no-cgo option (long unsupported in Go)
+
+2021-05-04: olly
+ [PHP] #2014 Throw PHP exceptions instead of using PHP errors
+
+ PHP exceptions can be caught and handled if desired, but if they
+ aren't caught then PHP exits in much the same way as it does for a
+ PHP error.
+
+ In particular this means parameter type errors and some other cases
+ in SWIG-generated wrappers now throw a PHP exception, which matches
+ how PHP's native parameter handling deals with similar situations.
+
+ `SWIG_ErrorCode()`, `SWIG_ErrorMsg()`, `SWIG_FAIL()` and `goto thrown;`
+ are no longer supported (these are really all internal implementation
+ details and none are documented aside from brief mentions in CHANGES
+ for the first three). I wasn't able to find any uses in user interface
+ files at least in FOSS code via code search tools.
+
+ If you are using these:
+
+ Use `SWIG_PHP_Error(code,msg);` instead of `SWIG_ErrorCode(code);
+ SWIG_ErrorMsg(msg);` (which will throw a PHP exception in SWIG >= 4.1
+ and do the same as the individual calls in older SWIG).
+
+ `SWIG_FAIL();` and `goto thrown;` can typically be replaced with
+ `SWIG_fail;`. This will probably also work with older SWIG, but
+ please test with your wrappers if this is important to you.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2021-05-17: adr26
+ [Python] #1985 Fix memory leaks:
+
+ 1. Python object references were being incorrectly retained by
+ SwigPyClientData, causing swig_varlink_dealloc() never to run / free
+ memory. SwigPyClientData_New() / SwigPyClientData_Del() were updated
+ to fix the object reference counting, causing swig_varlink_dealloc()
+ to run and the memory swig_varlink owns to be freed.
+
+ 2. SwigPyClientData itself was not freed by SwigPyClientData_Del(),
+ causing another heap leak. The required free() was added to
+ SwigPyClientData_Del()
+
+ 3. Fix reference counting/leak of python cached type query
+
+ 4. Fix reference counting/leak of SwigPyObject dict (-builtin)
+
+ 5. Python object reference counting fixes for out-of-memory
+ scenarios were added to: SWIG_Python_RaiseOrModifyTypeError(),
+ SWIG_Python_AppendOutput(), SwigPyClientData_New(),
+ SwigPyObject_get___dict__() and SwigPyObject_format()
+
+ 6. Add error handling for PyModule_AddObject() to
+ SWIG_Python_SetModule() (failure could be caused by OOM or a name
+ clash caused by malicious code)
+
+2021-05-13: olly
+ [UFFI] #2009 Remove code for Common Lisp UFFI. We dropped support
+ for it in SWIG 4.0.0 and nobody has stepped forward to revive it in
+ over 2 years.
+
+2021-05-13: olly
+ [S-EXP] #2009 Remove code for Common Lisp S-Exp. We dropped
+ support for it in SWIG 4.0.0 and nobody has stepped forward to
+ revive it in over 2 years.
+
+2021-05-13: olly
+ [Pike] #2009 Remove code for Pike. We dropped support for it in
+ SWIG 4.0.0 and nobody has stepped forward to revive it in over 2
+ years.
+
+2021-05-13: olly
+ [Modula3] #2009 Remove code for Modula3. We dropped support for it
+ in SWIG 4.0.0 and nobody has stepped forward to revive it in over 2
+ years.
+
+2021-05-13: olly
+ [CLISP] #2009 Remove code for GNU Common Lisp. We dropped support
+ for it in SWIG 4.0.0 and nobody has stepped forward to revive it in
+ over 2 years.
+
+2021-05-13: olly
+ [Chicken] #2009 Remove code for Chicken. We dropped support for it
+ in SWIG 4.0.0 and nobody has stepped forward to revive it in over 2
+ years.
+
+2021-05-13: olly
+ [Allegrocl] #2009 Remove code for Allegro Common Lisp. We dropped
+ support for it in SWIG 4.0.0 and nobody has stepped forward to
+ revive it in over 2 years.
+
+2021-05-04: olly
+ [PHP] #1982 #1457 https://sourceforge.net/p/swig/bugs/1339/
+ SWIG now only use PHP's C API to implement its wrappers, and no
+ longer generates PHP code to define classes. The wrappers should
+ be almost entirely compatible with those generated before, but
+ faster and without some previously hard-to-fix bugs.
+
+ The main notable difference is SWIG no longer generates a .php
+ wrapper at all by default (only if %pragma(php) code=... or
+ %pragma(php) include=... are specified in the interface file).
+ This also means you need to load the module via extension=...
+ in php.ini, rather than letting the dl() in the generated
+ .php wrapper load it (but dl() has only worked for command-line
+ PHP for some years now).
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2021-04-30: olly
+ #1984 Remove support for $source and $target.
+ These were officially deprecated in 2001, and attempts to use them have
+ resulted in a warning (including a pointer to what to update them to)
+ for most if not all of that time.
+
+2021-04-27: wsfulton
+ #1987 [Java] Fix %interface family of macros for returning by const
+ pointer reference.
+
+2021-04-19: olly
+ Fix use of uninitialised variable in the generated code for an
+ empty typecheck typemap, such as the dummy one we include for
+ std::initializer_list.
+
+2021-04-12: olly
+ #1777 [Python] Specifying -py3 now generates a check for Python
+ version >= 3.0.
+
+2021-03-26: olly
+ [PHP] Add PHP keywords 'fn' (added in 7.4) and 'match' (added in
+ 8.0) to the list SWIG knows to automatically rename.
+
+2021-03-23: wsfulton
+ #1942 [Python] Fix compilation error in wrappers when using -builtin
+ and wrapping varargs in constructors.
+
+2021-03-22: goto40
+ #1977 Fix handling of template template parameters.
+
+2021-03-21: olly
+ #1929, #1978 [PHP] Add support for PHP 8.
+
+2021-03-19: wsfulton
+ #1610 Remove -ansi from default compilation flags.
+
+2021-03-19: dot-asm
+ #1934 [Java] Clean up typemaps for long long arrays.
+
+2021-03-19: olly
+ #1527 [PHP] Improve PHP object creation in directorin case.
+ Reportedly the code we were using in this case gave segfaults in
+ PHP 7.2 and later - we've been unable to reproduce these, but the
+ new approach is also simpler and should be bit faster too.
+
+2021-03-18: olly
+ #1655 [PHP] Fix char* typecheck typemap to accept PHP Null like the
+ corresponding in typemap does.
+
+2021-03-18: olly
+ #1900, #1905 [PHP] Fix wrapping of overloaded directed methods with
+ non-void return.
+
+2021-03-11: murillo128
+ #1498 [Javascript] Support type conversion.
+
+2021-03-06: nshmyrev
+ #872 [Javascript] Various typemap issues in arrays_javascript.i fixed.
+
+2021-03-03: vaughamhong
+ #577 [Javascript] Implemented SetModule/GetModule for JSC to allow type sharing
+ across modules.
+
+2021-03-01: xantares, Oliver Buchtala, geographika
+ #1040 Add support for building SWIG with CMake. See documentation in Windows.html.
+
+2021-03-01: vadz
+ #1952 Fix incorrect warning "Unknown Doxygen command: ."
+
+2021-02-28: p2k
+ #969 [Javascript] v8/node - prevent crash calling a constructor without new keyword.
+
+2021-02-28: alecmev
+ #405 #1121 [Javascript] Fix OUTPUT typemaps on methods that don't return void.
+ The output value is appended to the return value.
+
+2021-02-26: murillo128, wsfulton
+ #1269 [Javascript] Fix handling of large positive unsigned long and
+ unsigned long long values.
+
+2021-02-24: tomleavy, yegorich, tungntpham
+ #1746 [Javascript] Add support for Node v12, v14 and v16.
+ SWIG support for Node is now for v6 and later only.
+
+2020-02-09: ZackerySpytz
+ #1872 Fix typos in attribute2ref macros.
+
+2020-10-10: wsfulton
+ [Javascript] Fix so that ccomplex.i interface to file can be used.
+
+2020-10-10: wsfulton
+ #252 complex can now be used as a C identifier and doesn't give a syntax error.
+
+2020-10-10: lpsinger
+ #1770 Correct C complex support.
+ _Complex is now parsed as a keyword rather than complex as per the C99 standard.
+ The complex macro is available in the ccomplex.i library file along with other
+ complex number handling provided by the complex.h header.
+
+2020-10-07: ZackerySpytz
+ [Python] #1812 Fix the error handling for the PyObject_GetBuffer() calls in
+ pybuffer.i.
+
+2020-10-07: treitmayr
+ #1824 Add missing space in director method declaration returning
+ const pointer.
+
+2020-10-07: adelva1984
+ #1859 Remove all (two) exceptions from SWIG executable.
+
+2020-09-25: wsfulton
+ [C#, Java] #1874 Add ability to change the modifiers for the interface
+ generated when using the %interface macros.
+
+ For C# use the 'csinterfacemodifiers' typemap.
+ For Java use the 'javainterfacemodifiers' typemap.
+
+ For example:
+
+ %typemap(csinterfacemodifiers) X "internal interface"
+
+2020-09-24: geefr
+ [C#] #1868 Fix wchar_t* csvarout typemap for member variable wrappers.
+
+2020-08-28: wsfulton
+ [Java] #1862 Fix crashes in swig_connect_director during director class construction
+ when using the director class from multiple threads - a race condition initialising
+ block scope static variables. The fix is guaranteed when using C++11, but most
+ compilers also fix it when using C++03/C++98.
+
+2020-08-16: wsfulton
+ [Python] Add missing initializer for member '_heaptypeobject::ht_module' when using
+ -builtin to complete Python 3.9 support.
+
+2020-08-16: wsfulton
+ [Python] Remove PyEval_InitThreads() call for Python 3.7 and later as Python calls
+ it automatically now. This removes a deprecation warning when using Python 3.9.
+
+2020-08-15: wsfulton
+ [Python] All Python examples and tests are written to be Python 2 and Python 3
+ compatible, removing the need for 2to3 to run the examples or test-suite.
+
+2020-08-13: wsfulton
+ [C#] Add support for void *VOID_INT_PTR for member variables.
+
+2020-07-29: chrisburr
+ #1843 [Python] Compilation error fix in SwigPyBuiltin_SetMetaType when using PyPy.
+
+2020-06-14: ZackerySpytz
+ #1642 #1809 Fix virtual comparison operators in director classes by removing an
+ incorrect space in the function name (for example, operator= = is now operator==).
+
+Version 4.0.2 (8 Jun 2020)
+==========================
+
+2020-06-07 vigsterkr
+ [Ruby] #1717 Nil fix mangling strings
+
+2020-06-07 vadz
+ #1748 Fix doxygen comments quoting issue
+
+2020-06-07 munoah
+ #1800 Escape spaces in file paths for dependencies (-M -MM etc)
+
+2020-06-06 andreas-schwab
+ [Ruby] #1801 Fix encoding on big endian systems when wrapping std::wstring.
+
+2020-05-31 kwwette
+ [Octave] #1789 error handling improvements and return error code on exit for SWIG wrapped modules.
+
+2020-05-30 msteinbeck
+ [D] #1593 Replace broken imports when using newer versions of D.
+
+2020-05-29: ZackerySpytz
+ [Python] #1716 Performance improvements when converting strings when using Python >= 3.3.
+
+2020-05-28: ZackerySpytz
+ #1776 Quite dramatically decrease run times when generating very large interface files by changing
+ some internal memory pool sizes.
+
+2020-05-28: mcfarljm
+ #1788 Fix handling of Doxygen \endlink command.
+
+2020-05-24: vapier
+ [Javascript] #1796 Fix pkg-config invocation in configure.
+
+2020-04-30: kwwette
+ [Octave] Fix exception raising for newer Octave versions
+ Since (at least) Octave 5.1.0, the Octave error() function now raises a C++ exception,
+ which if uncaught immediately exits a SWIG wrapper function, bypassing any cleanup code
+ that may appear after a "fail:" label. This patch adds a "try { ... } catch(...) { }"
+ block around the contents of SWIG wrapper functions to first execute the cleanup code
+ before rethrowing any exception raised. It is backward compatible with earlier versions
+ of Octave where error() does not raise an exception, which will still branch to the
+ "fail:" block to execute cleanup code if an error is encountered.
+
+ Note that the new "try { ... } catch(...) { }" block will localise any local variables
+ used in typemaps that were NOT declared through SWIG's %typemap(...) syntax, so it's
+ possible this could break existing SWIG wrappers which were implicitly sharing local
+ variables between typemaps. This can be fixed, however, by declaring local variables
+ which need to be shared between typemaps through SWIG's %typemap(...) syntax.
+
+2020-02-18: ryannevell
+ [Lua] #1728 Add support for LUA lightuserdata to SWIG_Lua_ConvertPtr.
+
+2020-02-18: dmach
+ [Ruby] #1725 Fix gcc -Wcatch-value warnings.
+
+2020-02-14: treitmayr
+ #1724 Fix wrapping of abstract user-defined conversion operators.
+
+2020-02-13: ddurham2
+ [Python] #1512 Fix memleak when using STL containers of shared_ptr objects.
+
+2020-02-06: wsfulton
+ [Python] #1673 #1674 Fix setting 'this' when extending a proxy class with __slots__.
+
+2020-01-31: vadz
+ [Ruby] #1651 Add std::auto_ptr<> typemaps.
+
+2020-01-31: ZackerySpytz
+ [Python] #1700 The Python C API functions PyBytes_AsStringAndSize() and
+ PyString_AsStringAndSize() are now checked for failure.
+
+2020-01-31: vadz
+ [Python] #1710 Fix crash parsing empty docstrings.
+
+2020-01-30: Alzathar
+ [R] #910 #914 Fix R memory leak on exception.
+
+2020-01-30: richardbeare
+ [R] #1511 Fix bug wrapping functions. These were previously incorrectly wrapped as if
+ they were variables. This happened when 'get' or 'set' was in the name of the function
+ or method, but sometimes also in some other circumstances. If you were using R
+ attribute syntax to access these methods, you'll need to switch to calling them as R
+ methods.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2020-01-24: etse-dignitas, wsfulton
+ [C#, D, Java] #1533 Fix upcasting for shared_ptr's of templated types.
+
+2020-01-16: mcfarljm
+ #1643 #1654 When using -doxygen, fix segfault when nameless parameters or vararg parameters
+ are used.
+
+2020-01-16: mcfarljm
+ #1632 #1659 Fix newline handling for doxygen "///" comments.
+
+2020-01-14: mcfarljm
+ #1647 #1656 Fix crash handling empty doxygen comments.
+
+2020-01-14: mcfarljm
+ #1608 Improve doxygen support.
+ - Add support for \param[] commands such as: \param[in].
+ - Optional arguments are marked as 'optional' in pydoc.
+ - Improve support for \code commands so that other languages are supported as code blocks.
+ Support added for java, c and py. For example Python: \code{.py} ... \endcode
+ - Fix doxygen handling of \em and \p tags for Python.
+
+2020-01-13: wsfulton
+ [Python] #1595 Python -builtin constructors silently ignored keyword arguments.
+ Instead of silently ignoring them, now a "TypeError: f() takes no keyword arguments"
+ exception is thrown if keyword arguments are used. Hence constructors and normal methods/
+ functions behave in the same way. Note, -keyword should be used with -builtin to obtain
+ keyword argument support.
+
+2020-01-05: jschueller shadchin
+ [Python] #1670 #1696 Add missing field initializers introduced in python 3.8:
+ tp_vectorcall and tp_print.
+
+2020-01-05: friedrichatgc
+ [Octave] #1688 Change swig_this() to use size_t instead of long for compatibility
+ with Windows 64 bit.
+
+2020-01-05: treitmayr
+ [Ruby] #1692 #1689 Add support for Ruby 2.7
+
+2019-12-30: treitmayr
+ [Ruby] #1653 #1668 Fix code generated when using -globalmodule option.
+
+2019-12-29: ZackerySpytz
+ [OCaml] #1686 Fix compilation errors with OCaml 4.09.0.
+
+2019-12-10: wsfulton
+ #1679 Fix parsing of C++11 identifiers with special meaning (final and override) when
+ they are used as part of the scope name of an identifier, such as a namespace name.
+
+2019-11-26: wsfulton
+ [C#] #1628 'out' or 'ref' used in a cstype typemap was not always stripped out in parts
+ of director code generation.
+
+2019-11-01: wsfulton
+ [Python] #1595 Fix bug in support for keyword arguments (kwargs feature or -keyword)
+ when using -builtin. The fix is in the argument error checking when wrapping zero
+ argument constructors only.
+
+Version 4.0.1 (21 Aug 2019)
+===========================
+
+2019-08-20: TekuConcept
+ [Javascript] #1535 Add %native support to Javascript.
+
+2019-08-20: bkotzz
+ [Java] #1616 Add SWIG_JavaIllegalStateException to support throwing
+ java.lang.IllegalStateException from JNI code.
+
+2019-08-19: sjml
+ [Lua] #1596 tostring output changes to show the underlying C/C++ pointer.
+
+2019-08-08: rokups
+ [C#, Java] #1601 Fix invalid code generated for "%constant enum EnumType.
+
+2019-08-07: wsfulton
+ [Python] Fix method overloading of methods that take STL containers of different
+ types. The following usage (using std::vector) would fail when using -builtin:
+
+ %include <std_string.i>
+ %include <std_vector.i>
+
+ %inline %{
+ struct X {};
+ %}
+
+ %template(VectorX) std::vector<X>;
+ %template(VectorInt) std::vector<int>;
+
+ %inline %{
+ using namespace std;
+ string VectorOverload(vector<X> v);
+ string VectorOverload(vector<int> v);
+ %}
+
+ The following would incorrectly fail:
+
+ s = VectorOverload([1, 2, 3])
+
+ With:
+
+ Traceback (most recent call last):
+ File "runme3.py", line 20, in <module>
+ ret = VectorOverload([1, 2, 3])
+ TypeError: Wrong number or type of arguments for overloaded function 'VectorOverload'.
+ Possible C/C++ prototypes are:
+ VectorOverload(std::vector< Number,std::allocator< Number > >)
+ VectorOverload(std::vector< int,std::allocator< int > >)
+
+ The problem was due to some error handling that was not cleared during typechecking.
+ In this case an error was not cleared when the elements in the list failed the
+ typecheck for converting to X. Only occurs in Python 3+.
+
+ In some combinations of overloaded methods, the following type of error message would
+ occur:
+
+ RuntimeError: in sequence element 0
+
+ The above exception was the direct cause of the following exception:
+
+ Traceback (most recent call last):
+ File "runme3.py", line 23, in <module>
+ check(VectorOverload(v), "vector<X>")
+ SystemError: <built-in function VectorOverload> returned a result with an error set
+
+2019-08-01: wsfulton
+ #1602 Fix regression in 4.0.0 where a template function containing a parameter
+ with the same name as the function name led to the parameter name used in the
+ target language being incorrectly modified.
+
+2019-07-29: wsfulton
+ Remove all generated files on error. Previously generated files were not removed,
+ potentially breaking Makefiles using file dependencies, especially when -Werror
+ (warnings as errors) was used.
+
+2019-07-23: smithx
+ [C#] #1530 #1532 Fix marshalling of std::wstring to C#.
+
+2019-07-18: gicmo
+ [Python] #1587 Python 3.8 support - remove use of deprecated PyObject_GC_UnTrack.
+
+2019-07-18: cher-nov
+ [Python] #1573 Generated Python code uses consistent string quoting style - double
+ quotes.
+
+2019-07-16: geefr
+ [C#] #616 #1576 Fix C# bool INPUT[], bool OUTPUT[], bool INOUT[] typemaps to marshall
+ as 1-byte.
+
+2019-07-12: vadz
+ [C#, Java] #1568 #1583 Fix std::set<> typemaps for primitive types.
+
+2019-07-12: vadz
+ #1566 #1584 Regression in 4.0.0 - fix missing value for first item of enums with
+ trailing comma.
+
+2019-07-11: mcfarljm
+ #1548 #1578 Fix segfault in Doxygen parser parsing empty lines in some commands like
+ \code.
+
+2019-07-09: IsaacPascual
+ [C#, Java] #1570 Fix name of generated C#/Java classes for %interface macros
+ in swiginterface.i when wrapping nested C++ classes.
+
+2019-07-05: wsfulton
+ [Python] #1547 Whitespace fixes in Doxygen translated comments into pydoc comments
+ for Sphinx compatibility.
+
+2019-06-28: wsfulton
+ [MzScheme, OCaml] #1559 $arg and $input were incorrectly substituted in the
+ argout typemap when two or more arguments were present.
+
+2019-06-24: wsfulton
+ [Python, Ruby] #1538 Remove the UnknownExceptionHandler class in order to be
+ C++17 compliant as it uses std::unexpected_handler which was removed in C++17.
+ This class was intended for director exception handling but was never used by
+ SWIG and was never documented.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2019-06-06: bkotzz
+ [Java] #1552 Improve performance in Java std::vector constructor wrapper that takes
+ a native Java array as input.
+
+2019-06-03: olly
+ [Python] Fix regression in implicit_conv handling of tuples,
+ introduced in SWIG 4.0.0. Fixes #1553, reported by Alexandre
+ Duret-Lutz.
+
+2019-05-24: wsfulton
+ [Octave] Fix detection of Octave on MacOS.
+
+2019-05-24: opoplawski
+ [Octave] #1522 Adapt OCTAVE_LDFLAGS for Octave 5.1.
+
+2019-05-22: ferdynator
+ [PHP] #1528 Don't add a closing '?>' PHP tag to generated files.
+ PSR-2 says it MUST be omitted for files containing only PHP.
+
+Version 4.0.0 (27 Apr 2019)
+===========================
+
+2019-04-24: vadz
+ #1517 Fix crash if "@return" Doxygen tag was used on a node without any return type.
+
+2019-04-24: vadz
+ #1515 Fix parsing of enums with trailing comma when using -doxygen.
+
+2019-04-19: ianlancetaylor
+ [Go] #1055 When generating Go code, make -cgo the default. Add new -no-cgo option
+ to disable the default.
+
+2019-04-19: pbecherer
+ [Tcl] #1508 Fix Visual Studio 2015 and later compilation errors due to snprintf macro
+ definition.
+
+2019-04-09: wsfulton
+ [C#] Fix FxCop warning CA2002 in SWIGPendingException - a lock on a reference of
+ type 'Type'.
+
+2019-03-30: wsfulton
+ [Java, D] Add the parameters typemap attribute to the javadestruct,
+ javadestruct_derived, ddispose, ddispose_derived typemaps to mirror enhanced
+ flexibility in the csdisposing and csdisposing_derived (C#) typemaps. If provided
+ the contents are generated as the delete/dispose method's parameters declaration.
+
+2019-03-30: wsfulton
+ [C#] #421 Fix FxCop warning CA1063 by implementing the recommended Dispose methods for
+ the IDisposable interface. Previously just the Dispose() method was generated.
+ Now the Dispose() and Dispose(bool disposing) methods are generated.
+ Changes are required if custom "csfinalize", "csdestruct" or "csdestruct_derived"
+ typemaps are being used. Details in #421 on Github. SWIG will error out if one of
+ the "csfinalize, "csdestruct" or "csdestruct_derived" typemaps are found. Example
+ error message:
+
+ foo.h:60: Error: A deprecated csfinalize typemap was found for Foo, please remove
+ it and replace all csdestruct, csdestruct_derived and csfinalize typemaps by the
+ csdispose, csdispose_derived, csdisposing and csdisposing_derived typemaps.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2019-03-25: Liryna
+ [C#] #1143 Add std_list.i for std::list support.
+ The C# std::list<T> wrappers are made to look and feel like a C#
+ System.Collections.Generic.LinkedList<> collection.
+ The IEnumerable<> interface is implemented in the proxy class.
+ The ICollection<> interface can also be implemented to provide enhanced functionality
+ whenever a C++ operator== is available. This is the case for when T is a
+ primitive type or a pointer. If T does define an operator==, then use the
+ SWIG_STD_LIST_ENHANCED macro to obtain this enhanced functionality, for example:
+
+ SWIG_STD_LIST_ENHANCED(SomeNamespace::Klass)
+ %template(ListKlass) std::list<SomeNamespace::Klass>;
+
+2019-03-18: richardbeare
+ [R] #1328 Non-trivial enums are working now. The enum values are now obtained from
+ the C/C++ layer. const reference enums and C++11 enum classes are also now working.
+
+2019-03-14: mochizk
+ [Javascript] #1500 Fix compilation errors due to deprecating V8 API in Node.js.
+ New V8 API is used if node.js >= v10.12, or if V8 >= v7.0.
+
+2019-03-12: vadz
+ [C#] #1495 Add std_set.i for std::set support.
+
+2019-03-11: dirteat,opoplawski
+ [Octave] Fix compilation errors in Octave 5.1.
+
+ error: format not a string literal and no format arguments [-Werror=format-security]
+
+2019-02-28: wsfulton
+ [Java] std::vector improvements for types that do not have a default constructor.
+
+ The std::vector wrappers have been changed to work by default for elements that are
+ not default insertable, i.e. have no default constructor. This has been achieved by
+ not wrapping:
+
+ vector(size_type n);
+
+ Previously the above had to be ignored via %ignore.
+
+ If the above constructor is still required it can be added back in again via %extend:
+
+ %extend std::vector {
+ vector(size_type count) { return new std::vector< T >(count); }
+ }
+
+ Alternatively, the following wrapped constructor could be used as it provides near-enough
+ equivalent functionality:
+
+ vector(jint count, const value_type& value);
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2019-02-25: wsfulton
+ [Python] Fix compile errors wrapping overloaded functions/constructors where a vararg
+ function is declared after a non-vararg function.
+
+2019-02-23: zphensley42
+ Use fully qualified name 'java.lang.Object' instead of 'Object' in generated code to
+ avoid clashes with wrapped C++ classes called 'Object'.
+
+2019-02-23: gtbX
+ [Java] #1035 Add (const char *STRING, size_t LENGTH) typemaps in addition to the non-const
+ typemaps (char *STRING, size_t LENGTH) which does not attempt to write back to the const
+ string.
+
+2019-02-22: tamuratak
+ [Ruby] #984 Add support for RTypedData introduced in Ruby 1.9.3.
+
+2019-02-22: ZackerySpytz
+ #1483 Fix compilation failures when a director class has final methods.
+
+2019-02-21: wsfulton
+ [Java] #1240 Suppress Java 9 deprecation warnings on finalize method.
+
+2019-02-21: ZackerySpytz
+ #1480 Fix some rejections of valid floating-point literals.
+
+2019-02-19: wsfulton
+ #1475 Fix regression parsing gcc preprocessor linemarkers in the form:
+
+ # linenum filename flags
+
+2019-02-18: jakecobb
+ [Python] #945 #1234 Elements in std::vector memory access fix.
+
+ Accessing an element in a std::vector obtains a reference to the element via an
+ iterator pointing to the element in the container. If the vector is garbage collected,
+ the SWIG wrapper containing the pointer to the element becomes invalid. The fix is
+ to obtain a back-reference to the container by the wrapper to the element in the Python
+ layer to prevent the garbage collector from destroying the underlying container.
+
+2019-02-17: wsfulton
+ Fix typemap matching to expand template parameters when the name contains
+ template parameters. In the %typemap below the type is T and the name is X<T>::make
+ and the name now expands correctly to X< int >::make
+
+ template<typename T> struct X {
+ %typemap(out) T X<T>::make "..."
+ T make();
+ };
+
+ %template(Xint) X<int>;
+
+2019-02-16: wsfulton
+ Fix parser error containing multiple #define statements inside an enum.
+
+ The second #define fails to parse:
+
+ enum FooEnum {
+ ENUM1 = 0,
+ ENUM2 = 1,
+
+ #define MACRO_DEF1 "Hello"
+ #define MACRO_DEF2 "World!"
+
+ ENUM3 = 2,
+ ENUM4 = 3,
+ };
+
+ Bug mentioned at https://sourceforge.net/p/swig/patches/333/
+
+2019-02-14: wsfulton
+ Add some missing copy constructors into STL containers.
+
+2019-02-14: bkotzz
+ [Java] #1356 Add STL containers:
+ std::unordered_map
+ std::unordered_set
+ std::set
+
+2019-02-14: bkotzz
+ [Java] #1356 std::map wrappers have been modified. Now the Java proxy class
+ extends java.util.AbstractMap. The std::map container looks and feels much like
+ a java.util.HashMap from Java.
+
+ A few members have changed their names. If the old method signatures are needed,
+ then copy std_map.i from swig-3.0.12 and use that instead. Alternatively,
+ add the old missing methods to the new methods by using the following %proxycode:
+
+ %extend std::map {
+ %proxycode %{
+ // Old API
+ public boolean empty() {
+ return isEmpty();
+ }
+ public void set($typemap(jboxtype, K) key, $typemap(jboxtype, T) x) {
+ put(key, x);
+ }
+ public void del($typemap(jboxtype, K) key) {
+ remove(key);
+ }
+ public boolean has_key($typemap(jboxtype, K) key) {
+ return containsKey(key);
+ }
+ %}
+ }
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2019-02-13: ZackerySpytz
+ #1469 Add support for C++17 hexadecimal floating literals.
+
+2019-02-11: wsfulton
+ [OCaml] #1437 OCaml has been give the 'Experimental' language status. The examples work
+ and most of the test-suite is also working, so it is quite close to being a 'Supported' language.
+
+2019-02-10: ZackerySpytz
+ #1464 Add support for C++14 binary integer literals.
+
+2019-02-10: ZackerySpytz
+ #1450 Add support for C++11 UCS-2 and UCS-4 character literals. Also, add support for
+ C++17 UTF-8 character literals.
+
+2019-02-10: wsfulton
+ [MzScheme] #1437 MzScheme/Racket is now an 'Experimental' language. The examples work
+ and a large portion of the test-suite is also working.
+
+2019-02-10: wsfulton
+ [MzScheme] Destructor wrappers were not being generated.
+
+2019-02-10: wsfulton
+ [MzScheme] Static variable wrappers fixed - $argnum was not expanded.
+
+2019-02-10: sethrj
+ #1452 Fix %apply for anonymous template instantiations
+
+2019-02-09: olly
+ [PHP] Fix access to already released memory during PHP module
+ shutdown, which often didn't cause visible problems, but could
+ result in segmentation faults, bus errors, etc. Fixes #1170,
+ reported by Jitka Plesníková.
+
+2019-02-09: olly
+ [PHP] A renamed constructor is now wrapped as a static method in
+ PHP.
+
+2019-02-08: olly
+ [PHP] Don't generate code which references $r when $r hasn't been
+ defined. This could happen in overloaded methods which returned
+ void and took at least one const std::string& parameter.
+
+2019-02-08: olly
+ [PHP] The generated code is now compatible with PHP 7.3, and the
+ testsuite now runs cleanly with this version too.
+
+2019-02-05: wsfulton
+ #1437 SWIG now classifies the status of target languages into either 'Experimental' or
+ 'Supported'. This status is provided to indicate the level of maturity to expect when using
+ a particular target language as not all target languages are fully developed. Details are
+ in the Introduction.html chapter of the documentation.
+
+2019-02-04: wsfulton
+ [CFFI] #1447 Common Lisp CFFI has been disabled as a target language in SWIG as part of a
+ clean up to remove target languages that have been neglected/not functional.
+
+2019-02-04: wsfulton
+ [Allegrocl] #1447 Allegro Common Lisp has been disabled as a target language in SWIG as part of a
+ clean up to remove target languages that have been neglected/not functional.
+
+2019-02-04: wsfulton
+ [Chicken] #1447 CHICKEN has been disabled as a target language in SWIG as part of a
+ clean up to remove target languages that have been neglected/not functional.
+
+2019-02-04: wsfulton
+ [CLISP] #1447 GNU Common Lisp has been disabled as a target language in SWIG as part of a
+ clean up to remove target languages that have been neglected/not functional.
+
+2019-02-04: wsfulton
+ [S-EXP] #1447 Common Lisp S-Exp has been disabled as a target language in SWIG as part of a
+ clean up to remove target languages that have been neglected/not functional.
+
+2019-02-04: wsfulton
+ [UFFI] #1447 Common Lisp UFFI has been disabled as a target language in SWIG as part of a
+ clean up to remove target languages that have been neglected/not functional.
+
+2019-02-04: wsfulton
+ [Pike] #1447 Pike has been disabled as a target language in SWIG as part of a
+ clean up to remove target languages that have been neglected/not functional.
+
+2019-02-04: wsfulton
+ [Modula3] #1447 Modula3 has been disabled as a target language in SWIG as part of a
+ clean up to remove target languages that have been neglected/not functional.
+
+2019-02-02: ahnolds
+ [Python] Documentation enhancements for Python:
+
+ #728 Fixed the handling of autodoc when using -fastproxy.
+
+ #1367 Added documentation to wrapped member variables using the
+ property(... doc="...") construct.
+
+ Only show a single documentation entry for functions with default arguments when
+ using autodoc.
+
+ Fixed a bug where a cached doxygen docstring could be deleted while still in use,
+ causing swig to segfault.
+
+2019-01-31: olly
+ SWIG now requires a target language to be specified instead of
+ defaulting to wrapping for Tcl. Specifying swig --help without
+ a target language now just shows the generic help. The -nolang
+ option has been removed.
+
+2019-01-28: ZackerySpytz
+ [OCaml] #1429 Remove support for OCaml versions < 3.12.0.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2019-01-22: vadz
+ [Ruby, Octave] #1424 Improve autodoc parameter naming.
+
+2019-01-22: vadz
+ [Python] #1271 #1423 Always include default parameter values in autodoc strings.
+
+2019-01-19: vadz
+ #1272, #1421 When a function's parameter is a keyword, the name of the paramater is
+ no longer simply changed to argN, where N is the argument number. Instead the
+ parameter name is changed to the renaming rules for keywords that normally apply to
+ symbols such as classes/functions etc. Note that unlike other symbol renaming,
+ parameter renaming does not issue a warning when the parameter is renamed. This
+ change only affects languages where the parameter names are actually used, for example,
+ Java function parameter lists in the proxy class or Python documentation comments.
+
+2019-01-18: wsfulton
+ #1420 Fix gdb debugger functions 'swigprint' and 'locswigprint' from swig.gdb to
+ work with newer versions of gdb-8. Fixes errors when debugging SWIG source with gdb:
+
+ (gdb) swigprint n
+ Undefined command: "Printf". Try "help".
+
+2019-01-16: wsfulton
+ Python static method wrapper changes
+
+ - Static method wrappers were using the 'fastproxy' approach by default.
+ This is inconsistent with instance method wrappers. The fastproxy approach
+ is now turned off by default to be consistent with instance methods.
+ Static method wrappers can now also be controlled using the -fastproxy and
+ -olddefs options.
+
+ Example:
+
+ struct Klass {
+ static int statmethod(int a = 2);
+ };
+
+ generates by default:
+
+ class Klass(object):
+ ...
+ @staticmethod
+ def statmethod(a=2):
+ return _example.Klass_statmethod(a)
+
+ instead of the following (which can be restored by using -fastproxy):
+
+ class Klass(object):
+ ...
+ statmethod = staticmethod(_example.Klass_statmethod)
+
+ - Modernise wrappers for static methods to use decorator syntax - @staticmethod.
+
+ - Add missing runtime test for static class methods and using the actual class method.
+
+2019-01-12: ZackerySpytz
+ [OCaml] #1403 #1194 Fix compilation problems for OCaml >= 4.03.0 due to OCaml using
+ int64_t instead of int64.
+
+2019-01-11: ZackerySpytz
+ [OCaml] #1400 Fix the getters and setters of non-static member variables.
+
+2019-01-07: wsfulton
+ #358 Add VOID to windows.i
+
+2019-01-05: wsfulton
+ #948 #1019 #1273 Fix for C++11 raw strings where the delimiters were mistakenly left
+ in the string contents in situations where the string was copied into generated code.
+ For example, %constant, the "docstring" feature and for C#/Java/D constants turned on
+ with %javaconst/%csconst/%dmanifestconst.
+
+2019-01-05: wsfulton
+ [Ruby] #538. Fix Ruby support for %feature("docstring").
+
+2019-01-03: wsfulton
+ #1202 Fix overloading of non-pointer class types in scripting languages when overloaded
+ with a pointer and a NULL scripting language equivalent is used, eg None in Python.
+
+ The implementation changes the SWIGTYPE, SWIGTYPE& and SWIGTYPE&& typecheck typemaps to
+ prevent accepting a conversion to a NULL pointer.
+
+2019-01-03: ZackerySpytz
+ [OCaml] #1386 Fix the OCaml examples and test suite for out-of-source builds.
+
+2019-01-01: wsfulton
+ [Python] #639 remove duplicate proxy method definitions for global function wrappers.
+
+ Global functions previously generated two definitions, eg:
+
+ def foo():
+ return _example.foo()
+ foo = _example.foo
+
+ The first definition is replaced by the second definition and so the second definition
+ is the one used when the method is actually called. Now just the first definition is
+ generated by default and if the -fastproxy command line option is used, just the second
+ definition is generated. The second definition is faster as it avoids the proxy Python
+ method as it calls the low-level C wrapper directly. Using both -fastproxy and -olddefs
+ command line options will restore the previously generated code as it will generate both
+ method definitions.
+
+ With this change, the wrappers for global C/C++ functions and C++ class methods now work
+ in the same way wrt to generating just a proxy method by default and control via
+ -fastproxy/-olddefs options.
+
+2018-12-20: hasinoff,wsfulton
+ [Java] #1334 Set Java thread name to native thread name when using directors.
+
+ Default is to use name "Thread-XXX" and is still works like this by default. However,
+ adding the following will turn on the thread name setting (works for more recent
+ versions of Linux and MacOS):
+
+ %begin %{
+ #define SWIG_JAVA_USE_THREAD_NAME
+ %}
+
+2018-12-20: chlandsi
+ [Python] #1357. Fix overriding __new__ in Python 3.6.
+
+ Fixes SystemError: Objects/tupleobject.c:81: bad argument to internal function"
+
+2018-12-16: wsfulton
+ [Python] #848 #1343 The module import logic has changed to stop obfuscating real ImportError
+ problems. Only one import of the low-level C/C++ module from the pure Python module is
+ attempted now. Previously a second import of the low-level C/C++ module was attempted
+ after an ImportError occurred and was done to support 'split modules'. A 'split module' is
+ a configuration where the pure Python module is a module within a Python package and the
+ low-level C/C++ module is a global Python module. Now a 'split module' configuration is
+ no longer supported by default. This configuration can be supported with a simple
+ customization, such as:
+
+ %module(package="mypackage", moduleimport="import $module") foo
+
+ or if using -builtin:
+
+ %module(package="mypackage", moduleimport="from $module import *") foo
+
+ instead of
+
+ %module(package="mypackage") foo
+
+ See the updated Python chapter titled "Location of modules" in the documentation.
+
+2018-12-11: tlby
+ [Perl] #1374 repair EXTEND() handling in typemaps
+
+2018-12-06: vadz
+ #1359 #1364 Add missing nested class destructor wrapper when the nested class is
+ inside a template. Removes associated bogus 'Illegal destructor name' warning. Only
+ occurred when the nested class' destructor is explicitly specified.
+
+2018-12-04: adr26
+ [Python] #1368 #1369 Access Violation in tp_print caused by mismatched Python/extension
+ CRT usage
+
+ Remove all use of tp_print, as this API uses a FILE*, which can be
+ mismatched when modules are built with different C libraries from
+ the main python executable.
+
+ This change also brings consistent output between Python 2 and 3 for the 'cvar' SWIG
+ object (that contains the global variables) and SWIG packed objects (such as callback
+ constants).
+
+2018-12-04: wsfulton
+ [Python] #1282 Fix running 'python -m' when using 'swig -builtin'
+
+ Similar to the earlier PEP 366 conforming fix for non-builtin.
+
+2018-11-29: adr26
+ [Python] #1360 Leak of SWIG var link object
+
+ Fix reference counting on _SWIG_globals to allow var link to be freed on module unload.
+
+2018-11-28: wsfulton
+ [Python] When using -builtin, the two step C-extension module import is now
+ one step and the wrapped API is only available once and not in an underlying
+ module attribute like it is without -builtin. To understand this, consider a
+ module named 'example' (using: %module example). The C-extension is compiled into
+ a Python module called '_example' and a pure Python module provides the actual
+ API from the module called 'example'. It was previously possible to additionally
+ access the API from the module attribute 'example._example'. The latter was an
+ implementation detail and is no longer available. It shouldn't have been used, but
+ if necessary it can be resurrected using the moduleimport attribute described in the
+ Python chapter of the documentation. If both modules are provided in a Python
+ package, try:
+
+ %module(moduleimport="from . import _example\nfrom ._example import *") example
+ or more generically:
+ %module(moduleimport="from . import $module\nfrom .$module import *") example
+
+ and if both are provided as global modules, try:
+
+ %module(moduleimport="import _example\nfrom _example import *") example
+ or more generically:
+ %module(moduleimport="import $module\nfrom $module import *") example
+
+ The module import code shown will appear in the example.py file.
+
+2018-11-24: vadz
+ #1358 Fix handling of abstract base classes nested inside templates
+
+ Correct detecting of whether a derived class method overrides a pure virtual
+ base class method when both classes are nested inside a template class: this
+ notably didn't work correctly for methods taking parameters of the base class
+ type.
+
+2018-11-22: rupertnash
+ [Python] #1282 Make generated module runnable via python -m (PEP 366 conforming)
+
+ Previously any SWIG generated modules in a package would fail with an ImportError
+ when using 'python -m' for example 'python -m mypkg.mymodule'.
+
+ This fix also allows the SWIG generated module to be placed into a directory and
+ then renamed __init__.py to convert the module into a package again. This ability
+ stopped working in swig-3.0.9. However, only Python 2.7 or 3.3 and later work. If
+ Python 3.2 support is needed, use moduleimport in %module to customise the import
+ code.
+
+2018-11-13: wsfulton
+ #1340 Remove -cppcast and -nocppcast command line options (this was an option
+ available to the scripting language targets).
+
+ The -cppcast option is still turned on by default. The -nocppcast option
+ to turn off the use of c++ casts (const_cast, static_cast etc) has been
+ removed. However, defining SWIG_NO_CPLUSPLUS_CAST will still generate C casts
+ instead of C++ casts for C++ wrappers.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-11-13: wsfulton
+ [Python] #1340 Remove -outputtuple and -nooutputtuple command line options.
+
+ Both the command line and %module options of the same name have been
+ removed. These were undocumented. The -outputtuple option returned a
+ Python tuple instead of a list, mostly typically in the OUTPUT
+ typemap implementations.
+
+ It unclear why a tuple instead of a list return type is needed and
+ hence this option has been removed as part of the simplification of
+ the SWIG Python command line options for SWIG 4.
+
+2018-11-13: wsfulton
+ [Python] #1340 Remove -noproxyimport command line option.
+
+ This option turned off the insertion of Python import statements
+ derived from a %import directive. For example given:
+
+ %module module_b
+ %import "module_a.i"
+
+ then module_b.py will contain:
+
+ import module_a
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-10-29: AlexanderGabriel
+ [PHP] The following PHP7 reserved keywords are now only renamed by
+ SWIG when used as function names in the API being wrapper:
+ __halt_compiler array die echo empty eval exit include include_once
+ isset list print require require_once return unset
+
+2018-10-22: olly,wsfulton
+ [Python] #1261 #1340 Turn on many optimisation options by default and rationalise the
+ number of command line options.
+
+ There were an unnecessary number of command line options and many of these have now
+ been removed in a drive for simplification. Some were needed to support older versions
+ of Python (2.6 and earlier).
+
+ Many of the options could be turned on individually and when using -O. Previously -O
+ resulted in turning on a set of options:
+
+ -modern -fastdispatch -nosafecstrings -fvirtual -noproxydel
+ -fastproxy -fastinit -fastunpack -fastquery -modernargs -nobuildnone
+
+ Now -O results in turning on this reduced set:
+
+ -fastdispatch -fastproxy -fvirtual
+
+ The following options are now on by default, a deprecated warning is displayed if they
+ are used:
+ -fastinit Class initialisation code done in C/C++ rather than in Python code.
+ -fastquery Python dictionary used for lookup of types.
+ -fastunpack Faster unpacking of function arguments in C/C++ wrappers.
+ -modern Use Python 2.3 features such as object and property.
+ -modernargs Use Python 2.3 C APIs for unpacking arguments in tuples.
+ -noproxydel Stop generating a proxy __del__ method for backwards compatiblity.
+ -safecstrings No discernable difference
+
+ The following options have been removed altogether:
+ -aliasobj0
+ -buildnone
+ -classptr
+ -new_repr
+ -newrepr
+ -noaliasobj0
+ -nobuildnone
+ -nocastmode
+ -nodirvtable
+ -noextranative
+ -nofastinit
+ -nofastproxy
+ -nofastquery
+ -nomodern
+ -nomodernargs
+ -nooutputtuple
+ -nosafecstrings
+ -old_repr
+ -oldrepr
+ -proxydel
+
+ -new_vwm is no longer supported. Use the -newvwm alias instead.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-10-22: olly
+ [Python] #1261 Remove command line option no longer needed as Python 2.3 and earlier
+ are no longer supported:
+
+ -classic
+
+2018-10-09: wsfulton
+ [D, Go, Guile, Lua, Mzscheme, Ocaml, Perl5, Php, Scilab, Tcl]
+ Allow wrapping of std::map using non-default comparison function.
+
+2018-10-09: vadz
+ [Java] #1274 Allow wrapping of std::map using non-default comparison function.
+
+2018-10-04: wsfulton
+ [Python] #1126 Fix C default arguments with -builtin and -fastunpack and -modernargs.
+ Problem occurred when there is just one (defaulted) parameter in the parameter list.
+
+2018-09-24: wsfulton
+ [Python] #1319 C++11 hash tables implementation is finished now (including for -builtin):
+ std::unordered_map
+ std::unordered_set
+ std::unordered_multimap
+ std::unordered_multiset
+
+2018-09-21: wsfulton
+ [Python] Fix when using -builtin and wrapping std::map, std::set, std::unordered_map or
+ std::unordered_set to ensure __contains__ is called. This is a wrapper for the STL
+ container's find method. Without it, Python will do its own slower sequence search.
+
+2018-09-19: wsfulton
+ [Python] Fix functors (wrapped as __call__) when using -builtin -modern -fastunpack.
+
+2018-09-02: andreas.gaeer,tkrasnukha
+ [Python] #1321 Fix assert in PyTuple_GET_SIZE in debug interpreter builds of python-3.7
+ when calling tp_new.
+
+2018-09-01: ChristopherHogan
+ [Guile] #1288 Fix garbage collection for guile >= 2.0.12.
+
+2018-08-31: wsfulton
+ [Python] #1319 C++11 hash tables support:
+ std::unordered_map
+ std::unordered_set
+ std::unordered_multimap
+ std::unordered_multiset
+ is now compiling and working (sorting using -builtin not fully functional yet though).
+
+2018-08-20: wkalinin
+ #1305 Fix nested structure symbol tables in C mode to fix member name conflicts
+ in different structs with the same nested struct member name.
+
+2018-08-18: wsfulton
+ [Python] #688 Fix makefile recursion when running python test-suite.
+
+2018-08-18: wsfulton
+ [Python] #1310 Re-implement Python -fastproxy option.
+
+ The previous implementation failed with Python 3 and abstract base clases.
+ The new implementation replaces the Python 2 implementation using
+ new.instancemethod with the C API PyMethod_New to match the equivalent Python 3
+ implementation which uses PyInstanceMethod_New.
+
+ The new approach runs slightly faster. See #1310.
+
+2018-08-12: gmazzamuto
+ [Python] #1283 Update pybuffer.i library to use new-style Python buffer C API.
+
+2018-08-12: brianhatwood,wsfulton
+ [Java] #1303 #1304 Fix crash in directors when using OUTPUT and INOUT typemaps in typemaps.i and
+ passing NULL pointers in C++ to director method overloaded and implemented in Java.
+
+2018-08-10: wsfulton
+ [Python] #1293 Improve TypeError message inconsistencies between default and fastdispatch
+ mode when handling overloaded C++ functions. Previously the error message did not always
+ display the possible C/C++ prototypes in fastdispatch mode.
+
+2018-08-02: furylynx,jacobwgillespie,p2k
+ [Javascript] #1290, #968. Add support for NodeJS versions 2-10.
+
+2018-07-31: wsfulton
+ [Python] #1293 Overloaded C++ function wrappers now raise a TypeError instead
+ of NotImplementedError when the types passed are incorrect. This change means
+ there is now consistency with non-overloaded function wrappers which have always
+ raised TypeError when the incorrect types are passed. The error message remains
+ the same and is for example now:
+
+ TypeError: Wrong number or type of arguments for overloaded function 'f'.
+ Possible C/C++ prototypes are:
+ f(int)
+ f(char const *)
+
+ instead of:
+
+ NotImplementedError: Wrong number or type of arguments for overloaded function 'f'.
+ Possible C/C++ prototypes are:
+ f(int)
+ f(char const *)
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-06-23: wsfulton
+ [Python] #718 Fix pythonnondynamic feature for modern classes
+
+ Fixes nondynamic mode when an instance variable is set with the same
+ name as a class variable in a class derived from a SWIG proxy class.
+ This corner case set an instance variable instead of raising an AttributeError.
+
+ Also fix %pythonnondynamic in Python 3 with -modern. The metaclass
+ containing the implementation was previously not being applied in Python 3.
+
+2018-07-17: petrmitrichev,wsfulton
+ [Python] #1275 #1279 Initialize function-local statics (singletons) that call Python
+ code during Python module initialization in order to avoid deadlocks with subsequent
+ multi-threaded usage.
+
+2018-06-15: wsfulton
+ [Python] Fix seg fault using Python 2 when passing a Python string, containing
+ invalid utf-8 content, to a wstring or wchar * parameter. A TypeError is thrown instead, eg:
+
+ %include <std_wstring.i>
+ void instring(const std::wstring& s);
+
+ instring(b"h\xe9llooo") # Python
+
+2018-06-15: wsfulton
+ [Python] Python 3.7 support: Replace use of deprecated PyUnicode_GetSize with
+ PyUnicode_GetLength to remove deprecated warnings compiling the C/C++ wrappers.
+
+2018-06-12: wsfulton
+ [Python] Python 3.7 support: The %pythonabc feature in pyabc.i now uses base classes
+ collections.abc.MutableSequence
+ collections.abc.MutableMapping
+ collections.abc.MutableSet
+ instead of
+ collections.MutableSequence
+ collections.MutableMapping
+ collections.MutableSet
+ as the latter are deprecated in Python 3.7 and are due to be removed in Python 3.8.
+ The classes in collections.abc.* are available from Python 3.3 onwards. If you
+ require support for Python 3.2, then copy the pyabc.i file and modify by removing
+ the few instances of the .abc sub-module.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-06-12: olly,wsfulton
+ [Python] #701 Remove support for Python versions < 2.7 and 3.0 and 3.1.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-06-11: olly
+ [Python] Fix new GCC8 warnings in generated code by avoiding casts
+ between incompatible function types where possible, and by
+ suppressing the warning when it's due to the design of Python's C
+ API. Fixes #1259.
+
+2018-06-08: philippkraft
+ [Python] Stop exposing <CLASS>_swigregister to Python. It's not
+ useful for user Python code to call this, and it just clutters the
+ API unnecessarily. Fixes #1225.
+
+2018-06-07: cmfoil, kabbi, Jamie Kirkpatrick, markok314, vadz, wsfulton, Yann Diorcet
+ #170 Doxygen documentation support added. This allows translation of Doxygen comments
+ into JavaDoc and PyDoc documentation. It is enabled via the -doxygen command line
+ option. See the Doxygen.html chapter in the documentation for further information.
+
+2018-06-07: olly
+ [PHP] We've finally removed support for %pragma(php4) which was
+ deprecated back in 2008. Use %pragma(php) instead, which has been
+ supported since at least 2005.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-06-07: olly
+ [PHP5] Support for PHP5 has been removed. PHP5 is no longer
+ actively supported by the PHP developers and security support for
+ it ends completely at the end of 2018, so it doesn't make sense
+ to include support for it in the upcoming SWIG 4.0.0 release.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-06-06: olly
+ [Lua] Improve configure probes for Lua headers and libs used in testsuite.
+
+2018-05-15: kwwette
+ [Octave] add support for version 4.4
+ - Should not introduce any user-visible incompatibilities
+
+2018-05-15: wsfulton
+ [C#, D, Java] Fix lookup of csconstruct, dconstruct and javaconstruct typemaps.
+ The C++ namespace was previously ignored when looking up the typemap.
+
+2018-05-15: wsfulton
+ [Javascript] Fix generated C++ code when using %nspace on namespaces that are more
+ than two levels deep.
+
+2018-05-14: wsfulton
+ Issue #1251 Add support for C++17 nested namespace definitions,
+ for example:
+ namespace A::B { ... }
+
+2018-05-11: wsfulton
+ [C#, D, Java] Add support so that the %csmethodmodifiers, %dmethodmodifiers,
+ %javamethodmodifiers can modify the method modifiers for the destructor wrappers
+ in the proxy class: dispose, Dispose, delete. With this feature, it is now possible
+ to make a C# proxy class sealed, eg when wrapping a class X, the virtual method modifiers
+ can be removed using:
+
+ %typemap(csclassmodifiers) X "public sealed class"
+ %csmethodmodifiers X::~X "public /*virtual*/";
+
+2018-04-18: olly
+ [Python] Suppress new pycodestyle warning:
+ E252 missing whitespace around parameter equals
+
+2018-04-07: goatshriek
+ [Ruby] #1213 Fix ruby %alias directive for global C/C++ functions.
+
+2018-04-03: olly
+ [Ruby] Fix to pass Qnil instead of NULL to rb_funcall(), which silences GCC
+ -Wconversion-null warning (on by default with recent GCC).
+
+2018-03-09: wsfulton
+ [Java] #1184 Fix swigReleaseOwnership() and swigTakeOwnership() regression
+ for non-director classes. Restores a dynamic_cast which was previously removed.
+
+2018-03-07: llongi
+ Github PR #1166 - Fix preprocessor handling of macros with commas
+ in a // comment.
+
+2018-02-18: JPEWdev
+ Patch #1164 - Add support for a command-line options file, also sometimes
+ called a response file. This is useful if the command-line options exceed
+ the system command-line length limit. To use, put the command-line options
+ into a file, then provide the file name prefixed with @, for example using
+ a file called args.txt:
+
+ swig @args.txt
+
+2018-02-11: wsfulton
+ [Javascript] #1187 Fix compilation error wrapping std::complex via
+ std_complex.i.
+
+2018-01-30: smarchetto
+ [Scilab] add type name argument in SWIG_ptr() function to cast from pointer address to typed pointers
+
+2018-01-16: wsfulton
+ Expressions following a preprocessor directive must now be separated by whitespace
+ or non-numeric characters. This syntax change makes the SWIG preprocessor work like
+ the C preprocessor in this area.
+
+ For example, the following code used be accepted as valid syntax:
+ #if1
+ #define ABC 123
+ #endif
+
+ Now you get an error:
+ example.h:1: Error: Unknown SWIG preprocessor directive: if1 (if this is a block of
+ target language code, delimit it with %{ and %})
+ example.h:3: Error: Extraneous #endif.
+
+ The following is the correct syntax:
+ #if 1
+ #define ABC 123
+ #endif
+
+ The following of course also works:
+ #if(1)
+ #define ABC 123
+ #endif
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2018-01-15: wsfulton
+ Fix issue #1183. Floating point exception evaluating preprocessor expressions
+ resulting in division by zero.
+
+2018-01-14: wsfulton
+ Fix issue #1172. Seg fault parsing invalid exponents in the preprocessor.
+
+2018-01-12: Liryna
+ [C#] Patch #1128. Add ToArray function to std::vector wrappers.
+
+2018-01-12: wsfulton
+ [Java] Fix issue #1156. Add missing throws clause for interfaces when using the
+ %interface family of macros.
+
+2018-01-05: wsfulton
+ Fix default arguments using expressions containing -> syntax error. Problem reported on
+ swig-user mailing list.
+
+2017-12-30: wsfulton
+ [Python] Replace pep8 with pycodestyle for checking the Python code style when
+ running Python tests.
+
+2017-12-30: davedissian
+ Fixed a symbol lookup issue when encountering a typedef of a symbol from the tag
+ namespace to the global namespace when the names are identical, such as 'typedef
+ struct Foo Foo;'.
+
+2017-12-13: wsfulton
+ [Perl] add missing support for directorfree typemaps.
+
+2017-12-13: wsfulton
+ Issue #1167 Fix directorout typemaps which were causing undefined behaviour when
+ returning pointers by reference.
+
+2017-12-08: olly
+ [PHP] Use ZEND_MODULE_GLOBALS_ACCESSOR to access globals so the
+ generated code builds when PHP was built with ZTS enabled.
+
+2017-12-04: wsfulton
+ [Python] Add missing checks for failures in calls to PyUnicode_AsUTF8String. Previously a
+ seg fault could occur when passing invalid UTF8 strings (low surrogates), eg passing
+ u"\udcff" to the C layer (Python 3).
+
+2017-11-24: joequant
+ [R] Fix #1124 and return R_NilValue for null pointers
+
+2017-11-29: wsfulton
+ [Java] director exception handling improvements.
+
+ When a director method throws an exception and it is caught by DirectorException
+ and passed back to Java using Swig::DirectorException::throwException, the Java
+ stack trace now contains the original source line that threw the exception.
+
+ Deprecate Swig::DirectorException::raiseJavaException, please replace usage with
+ Swig::DirectorException::throwException.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2017-10-26: wsfulton
+ Add support for C++11 ref-qualifiers when using directors.
+
+2017-10-26: wsfulton
+ Fix generated code when using directors and methods returning const ref pointers.
+
+2017-10-26: wsfulton
+ [C#, D, Java, Octave, R, Scilab] Port director typemaps to these additional languages.
+ Issue #700.
+
+2017-10-26: radarsat1
+ [Ruby Python] Patch #1029 - Correct handling of null using directors and shared_ptr.
+
+2017-10-10: joequant
+ [R] pass enum expressions to R. This will generate
+ incorrect files when there is an arithmetic expression
+ in the enum, but this is better than silently generating
+ incorrect code
+
+2017-10-09: olly
+ [PHP] Fix incorrect wrapper code generated when there's a
+ combination of overloading, parameters with a default value
+ and %newobject. Fixes https://sourceforge.net/p/swig/bugs/1350/
+
+2017-10-09: olly
+ Remove GCJ support. It isn't in a good state and doesn't seem to
+ be used, and GCC7 dropped GCJ. Closes
+ https://sourceforge.net/p/swig/bugs/823/
+
+2017-10-07: olly
+ Fix preprocessor handling of empty macro arguments to match that of
+ C/C++ compilers. Fixes issue #1111 and
+ https://sourceforge.net/p/swig/bugs/826/
+
+2017-10-06: wsfulton
+ [Python] Issue #1108. Fix platform inconsistency in Python default argument handling.
+ 32 bit and 64 bit compiled versions of SWIG generated different Python files
+ when default arguments were outside the range of 32 bit signed integers.
+ The default arguments specified in Python are now only those that are in the
+ range of a 32 bit signed integer, otherwise the default is obtained from C/C++ code.
+
+2017-10-02: wsfulton
+ [C#] Fix std::complex types passed by value.
+
+2017-10-02: wsfulton
+ [Javascript, Python, Ruby] Issue #732 - Missing type information for std::complex
+ in std_complex.i meant that previously std::complex always had to be fully qualified
+ in order to be wrapped with the appropriate typemaps.
+
+2017-10-01: joequant
+ allow R package names with docs
+ allowing multiple get accessors in R
+ fix smart-pointer and NAMESPACE support
+ constructors now returning smart pointers (if class
+ declared as such)
+ smart-pointer classes deriving from parent smart-pointers
+
+2017-09-29: wsfulton
+ Issue #1100 - Allow an instantiated template to have the same name in the target
+ language as the C++ template name, for example, this is now possible:
+
+ template<typename T> struct X { ... };
+ %template(X) X<int>;
+
+2017-09-23: wsfulton
+ Issue #1098. Fix overloading of shared_ptr with underlying pointer types, eg:
+
+ void m(std::shared_ptr<T> p);
+ void m(T &p);
+ void m(T *p);
+
+ Only the first method is wrapped and the others are ignored/shadowed.
+ The implementation is done via a new attribute in the 'typecheck' typemap called
+ 'equivalent'. If specified, it must contain the equivalent pointer type for overloading
+ and can only be used for the special SWIG_TYPECHECK_POINTER precedence level.
+ The shared_ptr 'typecheck' typemaps have been modified accordingly.
+ Here is a simplified version:
+
+ %typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER, equivalent="T *")
+ T,
+ T CONST &,
+ T CONST *,
+ T *CONST&,
+ std::shared_ptr< T >,
+ std::shared_ptr< T > &,
+ std::shared_ptr< T > *,
+ std::shared_ptr< T > *&
+ { ... }
+
+ Overloading with any of these types will result in SWIG ignoring all but the first
+ overloaded method by default. Without the 'equivalent' attribute, wrapping the overloaded
+ methods resulted in types being shadowed (scripting languages) or code that did not
+ compile (statically typed languages).
+
+2017-09-19: futatuki
+ [Python] #1003 Add --with-2to3=/path/to/2to3 option to configure.
+
+2017-09-18: wsfulton
+ Fix type promotion wrapping constant expressions of the form:
+ # define EXPR_MIXED1 (0x80 + 11.1) - 1
+ This was previously an integral type instead of a floating point type.
+
+2017-09-17: wsfulton
+ Fix generated code for constant expressions containing wchar_t L literals such as:
+ # define __WCHAR_MAX (0x7fffffff + L'\0')
+ # define __WCHAR_MIN (-__WCHAR_MAX - 1)
+
+2017-09-10: mlamarre
+ [Python] Patch #1083. Define_DEBUG to 1 to do exactly like Visual Studio
+ /LDd, /MDd or /MTd compiler options.
+
+2017-08-25: wsfulton
+ Issue #1059. Add support for C++11 ref-qualifiers on non-static member functions.
+ Members with lvalue ref-qualifiers such as:
+
+ struct RQ {
+ void m1(int x) &;
+ void m2(int x) const &;
+ };
+
+ are wrapped like any other member function. Member functions with rvalue ref-qualifiers
+ are ignored by default, such as:
+
+ struct RQ {
+ void m3(int x) &&;
+ void m4(int x) const &&;
+ };
+
+ example.i:7: Warning 405: Method with rvalue ref-qualifier m3(int) && ignored.
+ example.i:8: Warning 405: Method with rvalue ref-qualifier m4(int) const && ignored.
+
+ These can be unignored and exposed to the target language, see further documentation in
+ CPlusPlus11.html.
+
+2017-08-16: wsfulton
+ Fix #1063. Add using declarations to templates into typedef table.
+
+ Using declarations to templates were missing in SWIG's internal typedef tables.
+ This led to a few problems, such as, templates that did not instantiate and generated
+ C++ code that did not compile as SWIG did not know what scope the template was
+ in. This happened mostly when a using declaration was used on a template type in a
+ completely unrelated namespace.
+
+2017-08-16: wsfulton
+ Fix type lookup in the presence of using directives and using declarations.
+
+ Fix some cases of type lookup failure via a combination of both using directives and
+ using declarations resulting in C++ code that did not compile as the generated type was
+ not fully qualified for use in the global namespace. Example below:
+
+ namespace Space5 {
+ namespace SubSpace5 {
+ namespace SubSubSpace5 {
+ struct F {};
+ }
+ }
+ using namespace SubSpace5;
+ using SubSubSpace5::F;
+ void func(SubSubSpace5::F f);
+ }
+
+2017-08-16: wsfulton
+ Issue #1051. %template scope enforcement and class definition fixes.
+
+ The scoping rules around %template have been specified and enforced.
+ The %template directive for a class template is the equivalent to an
+ explicit instantiation of a C++ class template. The scope for a valid
+ %template instantiation is now the same as the scope required for a
+ valid explicit instantiation of a C++ template. A definition of the
+ template for the explicit instantiation must be in scope where the
+ instantiation is declared and must not be enclosed within a different
+ namespace.
+
+ For example, a few %template and C++ explicit instantiations of std::vector
+ are shown below:
+
+ // valid
+ namespace std {
+ %template(vin) vector<int>;
+ template class vector<int>;
+ }
+
+ // valid
+ using namespace std;
+ %template(vin) vector<int>;
+ template class vector<int>;
+
+ // valid
+ using std::vector;
+ %template(vin) vector<int>;
+ template class vector<int>;
+
+ // ill-formed
+ namespace unrelated {
+ using std::vector;
+ %template(vin) vector<int>;
+ template class vector<int>;
+ }
+
+ // ill-formed
+ namespace unrelated {
+ using namespace std;
+ %template(vin) vector<int>;
+ template class vector<int>;
+ }
+
+ // ill-formed
+ namespace unrelated {
+ namespace std {
+ %template(vin) vector<int>;
+ template class vector<int>;
+ }
+ }
+
+ // ill-formed
+ namespace unrelated {
+ %template(vin) std::vector<int>;
+ template class std::vector<int>;
+ }
+
+ When the scope is incorrect, an error now occurs such as:
+
+ cpp_template_scope.i:34: Error: 'vector' resolves to 'std::vector' and
+ was incorrectly instantiated in scope 'unrelated' instead of within scope 'std'.
+
+ Previously SWIG accepted the ill-formed examples above but this led to
+ numerous subtle template scope problems especially in the presence of
+ using declarations and using directives as well as with %feature and %typemap.
+
+ Actually, a valid instantiation is one which conforms to the C++03
+ standard as C++11 made a change to disallow using declarations and
+ using directives to find a template.
+
+ // valid C++03, ill-formed C++11
+ using std::vector;
+ template class vector<int>;
+
+ Similar fixes for defining classes using forward class references have
+ also been put in place. For example:
+
+ namespace Space1 {
+ struct A;
+ }
+ namespace Space2 {
+ struct Space1::A {
+ void x();
+ }
+ }
+
+ will now error out with:
+
+ cpp_class_definition.i:5: Error: 'Space1::A' resolves to 'Space1::A' and
+ was incorrectly instantiated in scope 'Space2' instead of within scope 'Space1'.
+
+ Previously some symbols would have been instantiated in the wrong scope and led
+ to lots of scope problems involving SWIG typemaps, features, renames etc.
+ You will need to correct the scope used in other SWIG directives which do not
+ support 'using declarations' and 'using directives'. For example, if you previously had:
+
+ %rename(Zap) vector<int>::clear;
+ using namespace std;
+ %template(VectorInt) vector<int>;
+
+ Prior versions of SWIG incorrectly instantiated vector<int> in the global namespace
+ and so the %rename matched. Now the template is instantiated in the correct namespace,
+ so is fully qualified as std::vector<int>. The other SWIG directives need correcting as
+ they do not follow 'using declarations' and 'using directives'. Change it to:
+
+ %rename(Zap) std::vector<int>::clear;
+ using namespace std;
+ %template(vin) vector<int>;
+
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2017-08-16: wsfulton
+ Fix scope lookup for template parameters containing unary scope operators.
+
+ Fixes cases like:
+
+ namespace Alloc {
+ template<typename T> struct Rebind {
+ typedef int Integer;
+ };
+ }
+ %template(RebindBucket) Alloc::Rebind< Bucket >;
+ OR
+ %template(RebindBucket) Alloc::Rebind< ::Bucket >;
+
+ Alloc::Rebind< Bucket >::Integer Bucket1();
+ Alloc::Rebind< ::Bucket >::Integer Bucket2();
+ Alloc::Rebind<::template TemplateBucket<double>>::Integer Bucket3();
+
+2017-08-16: wsfulton
+ For templates only, the template parameters are fully resolved when
+ handling typemaps. Without this, it is too hard to have decent rules
+ to apply typemaps when parameter types are typedef'd and template
+ parameters have default values.
+
+ Fixes %clear for typedefs in templates, eg:
+
+ %typemap("in") XXX<int>::Long "..."
+ template typename<T> struct XXX {
+ typedef long Long;
+ };
+ %clear XXX<int>::Long;
+
+ as the typemap was previously incorrectly stored as a typemap for long
+ instead of XXX<int>::Long.
+
+2017-08-05: olly
+ [C++11] Allow static_assert at the top level (and disallow it right
+ after template<T>). Fixes issue 1031 reported by Artem V L.
+
+2017-08-02: wsfulton
+ Fix incorrectly shown warning when an empty template instantiation was used on a
+ class used as a base class and that base class was explicitly ignored with %ignore.
+ Example of the warning which will no longer appear:
+
+ Warning 401: Base class 'Functor< int,int >' has no name as it is an empty
+ template instantiated with '%template()'. Ignored.
+
+2017-07-17: fflexo
+ [Java] #674 Add std_list.i to add support for std::list containers. The Java proxy
+ extends java.util.AbstractSequentialList and makes the C++ std::list container look
+ and feel much like a java.util.LinkedList from Java.
+
+2017-07-07: wsfulton
+ [Python] Fix display of documented template types when using the autodoc
+ feature. For example when wrapping:
+
+ %feature("autodoc");
+ template<typename X> struct T {};
+ %template(TInteger) T<int>;
+
+ the generated documentation contains:
+ """Proxy of C++ T< int > class."""
+ instead of:
+ """Proxy of C++ T<(int)> class."""
+ and
+ """__init__(TInteger self) -> TInteger"""
+ instead of
+ """__init__(T<(int)> self) -> TInteger"""
+
+2017-06-27: nihaln
+ [PHP] Update the OUTPUT Typemap to add return statement to the
+ PHP Wrapper.
+
+2017-06-27: nihaln
+ [PHP] Update the enum and value examples to use the OO wrappers
+ rather than the flat functions produced with -noproxy. There's
+ not been a good reason to use -noproxy for since PHP5 OO wrapping
+ was fixed back in 2005.
+
+2017-06-23: m7thon
+ [Python] fix and improve default argument handling:
+
+ 1. Fix negative octals. Currently not handled correctly by `-py3`
+ (unusual case, but incorrect).
+ 2. Fix arguments of type "octal + something" (e.g. `0640 | 04`).
+ Currently drops everything after the first octal. Nasty!
+ 3. Fix bool arguments "0 + something" (e.g. `0 | 1`) are always
+ "False" (unusual case, but incorrect).
+ 4. Remove special handling of "TRUE" and "FALSE" from
+ `convertValue` since there's no reason these have to match
+ "true" and "false".
+ 5. Remove the Python 2 vs. Python 3 distinction based on the
+ `-py3` flag. Now the same python code is produced for default
+ arguments for Python 2 and Python 3. For this, octal default
+ arguments, e.g. 0644, are now wrapped as `int('644', 8)`. This
+ is required, as Python 2 and Python 3 have incompatible syntax
+ for octal literals.
+
+ Fixes #707
+
+2017-06-21: futatuki
+ #1004 - Fix ccache-swig executable name to respect configure's --program-prefix and
+ --program-suffix values if used.
+
+2017-06-21: tamuratak
+ [Ruby] #911 - Add std::wstring support.
+
+2017-06-19: wsfulton
+ [Python] Fix handling of rich comparisons when wrapping overloaded operators:
+
+ operator< operator<= operator> operator>= operator== operator!=
+
+ Previously a TypeError was always thrown if the type was not correct. NotImplemented
+ is now returned from these wrapped functions if the type being compared with is
+ not correct. The subsequent behaviour varies between different versions of Python
+ and the comparison function being used, but is now consistent with normal Python
+ behaviour. For example, for the first 4 operator overloads above, a TypeError
+ 'unorderable types' is thrown in Python 3, but Python 2 will return True or False.
+ NotImplemented should be returned when the comparison cannot be done, see PEP 207 and
+ https://docs.python.org/3/library/constants.html#NotImplemented
+
+ Note that the bug was only present when overloaded operators did not also have a
+ function overload.
+
+ Fixes SF bug #1208 (3441262) and SF patch #303.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2017-06-17: fabrice102
+ [Go] Fix Go callback example. Fixes github #600, #955, #1000.
+
+2017-06-16: wsfulton
+ Make sure warning and error messages are not split up by other processes writing to
+ stdout at the same time.
+
+2017-06-16: wsfulton
+ [R] Fix wrapping function pointers containing rvalue and lvalue reference parameters.
+
+2017-06-13: olly
+ [Perl] Fix testsuite to work without . in @INC - it was removed in
+ Perl 5.26 for security reasons, and has also been removed from
+ older versions in some distros. Fixes #997 reported by lfam.
+
+2017-06-03: wsfulton
+ Fix %import on a file containing a file scope %fragment forced inclusion to not
+ generate the fragment contents as %import should not result in code being generated.
+ The behaviour is now the same as importing code insertion blocks.
+ Wrapping FileC.i in the following example will result in no generated code, whereas
+ previously "#include <limits.h>" was generated:
+
+ // FileA.i
+ %fragment("<limits.h>", "header") %{
+ #include <limits.h>
+ %}
+
+ %{
+ #include <stdio.h>
+ %}
+ %fragment("<limits.h>");
+
+ // FileC.i
+ %import "FileA.i"
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2017-05-26: Volker Diels-Grabsch, vadz
+ [Java] #842 Extend from java.util.AbstractList<> and implement java.util.RandomAccess for
+ std::vector wrappers. This notably allows to iterate over wrapped vectors in a natural way.
+ Note that boxed types are now used in the Java layer when wrapping vector of C primitive
+ types, for example. This may introduce some subtle incompatibilities due to some
+ differences in how Java converts boxed types and unboxed types. For example,
+
+ int i=0;
+ double d1 = i; // ok
+ Double d2 = i; // error: incompatible types: int cannot be converted to Double
+
+ This can be a problem when calling the add and set functions. A suggested backwards
+ compatible workaround is to use something like (shown for std::vector<double>:
+
+ #if defined(SWIGJAVA)
+ // Add in old api that uses non-boxed types
+ %extend std::vector<double> {
+ %proxycode %{
+ public void add(double x) {
+ add(Double.valueOf(x));
+ }
+ public void set(int i, double val) {
+ set(i, Double.valueOf(val));
+ }
+ %}
+ }
+ #endif
+
+ %include "std_vector.i"
+ %template(VectorDouble) std::vector<double>;
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2017-05-30: davidcl
+ [Scilab] #994 Undefined symbol error when loading in Scilab 6
+
+2017-05-25: asibross
+ [Java] #370 #417 Missing smart pointer handling in Java director extra methods
+ swigReleaseOwnership() and swigTakeOwnership().
+
+2017-05-23: wsfulton
+ [Java] #230 #759 Fix Java shared_ptr and directors for derived classes java compilation
+ error.
+
+ For shared_ptr proxy proxy classes, add a protected method swigSetCMemOwn for modifying
+ the swigCMemOwn and swigCMemOwnDerived member variables which are used by various other
+ methods for controlling memory ownership.
+
+2017-05-21: Sghirate
+ [Java, C#, D] #449 Remove unnecessary use of dynamic_cast in directors to enable
+ non-RTTI compilation.
+
+2017-05-21: wsfulton
+ [Python] #993 Fix handling of default -ve unsigned values, such as:
+ void f(unsigned = -1U);
+
+2017-05-20: jschueller
+ [Python] #991 Fix E731 PEP8 warning: do not assign a lambda expression
+
+2017-05-16: nihal95
+ [PHP] Add %pragma version directive to allow the version of the
+ extension to be set. Patch #970, fixes #360.
+
+2017-05-13: yag00
+ Patch #975 - Add support for noexcept on director methods.
+
+2017-04-27: redbrain
+ Issue #974, Patch #976 - Fix preprocessor handling of macros with commas in a comment.
+
+2017-04-25: jleveque
+ [Lua] #959 - Fix Visual Studio C4244 conversion warnings in Lua wrappers.
+
+2017-04-21: tamuratak
+ [Ruby] #964 - Add shared_ptr director typemaps.
+
+2017-04-20: wsfulton
+ [Ruby] #586, #935 Add assert for invalid NULL type parameter when calling SWIG_Ruby_NewPointerObj.
+
+2017-04-20: tamuratak
+ [Ruby] #930, #937 - Fix containers of std::shared_ptr.
+ Upcasting, const types (eg vector<shared_ptr<const T>>) and NULL/nullptr support added.
+
+2017-04-12: smarchetto
+ [Scilab] New parameter targetversion to specify the Scilab target version (5, 6, ..) for code generation
+ With Scilab 6 target specified, identifier names truncation is disabled (no longer necessary)
+
+2017-03-24: tamuratak
+ [Ruby] Fix #939 - Wrapping std::vector<bool> fix due to incorrect null checks
+ on VALUE obj.
+
+2017-03-17: vadz
+ [C#] #947 Add support for std::complex<T>
+
+2017-03-17: wsfulton
+ [Go] Fix handling of typedef'd function pointers and typedef'd member function pointers
+ such as:
+
+ typedef int (*FnPtr_td)(int, int);
+ int do_op(int x, int y, FnPtr_td op);
+
+2017-03-16: wsfulton
+ Add support for member const function pointers such as:
+
+ int fn(short (Funcs::* parm)(bool)) const;
+
+ Also fix parsing of references/pointers and qualifiers to member
+ pointers such as:
+
+ int fn(short (Funcs::* const parm)(bool));
+ int fn(short (Funcs::* & parm)(bool));
+
+2017-03-10: wsfulton
+ Extend C++11 alternate function syntax parsing to support const and noexcept, such as:
+
+ auto sum1(int x, int y) const -> int { return x + y; }
+ auto sum2(int x, int y) noexcept -> int { return x + y; }
+
+2017-02-29: tamuratak
+ [Ruby] #917 - Add Enumerable module to all container class wrappers. It was missing
+ for std::list, std::multiset, std::unordered_multiset and std::unordered_map.
+
+2017-02-27: assambar
+ [C++11] Extend parser to support throw specifier in combination
+ with override and/or final.
+
+2017-02-10: tamuratak
+ [Ruby] #883 - Add support for C++11 hash tables:
+ std::unordered_map
+ std::unordered_set
+ std::unordered_multimap
+ std::unordered_multiset
+
+2017-02-08: jcsharp
+ [C#] #887 Improve std::vector<T> wrapper constructors -
+ Replace constructor taking ICollection with IEnumerable and also add IEnumerable<T>
+ constructor to avoid the boxing and unboxing overhead of the original constructor,
+ when the type parameter is a value type.
+
+Version 3.0.12 (27 Jan 2017)
+============================
+
+2017-01-27: wsfulton
+ [C#] #882 Fix missing filename in error messages when there is a problem
+ writing out C# files.
+
+2017-01-27: briancaine
+ [Guile] #744 Fix compilation errors in Guile wrappers - regression
+ introduced in swig-3.0.11.
+
+2017-01-24: andrey-starodubtsev
+ [Java] Apply #704 - director typemap improvements.
+ Memory leak fixes, add support for "directorargout" typemap and
+ add director support to typemaps.i.
+
+2017-01-24: wsfulton
+ Enhance %extend to extend a class with template constructors, eg:
+
+ struct Foo {
+ %extend {
+ template<typename T>
+ Foo(int a, T b) {
+ ...
+ }
+ }
+ };
+ %template(Foo) Foo::Foo<double>;
+
+2017-01-22: wsfulton
+ Issue #876 Enhance %extend to extend a class with template methods, eg:
+
+ struct Foo {
+ %extend {
+ template<typename T>
+ void do_stuff(int a, T b) {
+ ...
+ }
+ }
+ };
+ %template(do_stuff_inst) Foo::do_stuff<double>;
+
+ Similarly for static template methods.
+
+2017-01-22: kwwette
+ [Octave] add support for version 4.2
+ - The Octave API now uses some C++11 features. It is recommended to use
+ the mkoctfile program supplied by Octave to compile the SWIG-generated
+ wrapper code, as mkoctfile will ensure the correct C++ compiler/options
+ are used. Otherwise, the value of `mkoctfile -p CXX` should be parsed
+ for any -std=* flags which might be present.
+ - Octave has dropped support for << and >> operators, so SWIG now
+ ignores them.
+ - The Octave error() function now raises C++ exceptions to propagate
+ Octave errors, so %exception directives may need to be modified.
+ For convenience the SWIG_RETHROW_OCTAVE_EXCEPTIONS macro can be used
+ to rethrow any Octave exceptions for Octave itself to handle, e.g.:
+
+ try {
+ $action // may call error()
+ }
+ SWIG_RETHROW_OCTAVE_EXCEPTIONS // error() exceptions are rethrown
+ catch(...) {
+ ... // all other exceptions
+ }
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2017-01-16: wkalinin
+ [C#] Fix #733 regression introduced in swig-3.0.9.
+ Missing virtual function override in C# layer when using %import.
+
+2017-01-16: fschlimb
+ Fix #813 template symbol name lookup bug when typedef names are the same but in different
+ namespaces.
+
+2017-01-15: wsfulton
+ [C# D Java]
+ The SWIG library no longer uses the javatype, dtype or cstype typemaps, thereby
+ completely freeing them up for users to use without having to replicate the library
+ code that they previously added. The code previously generated by these typemaps
+ has been replaced by the new %proxycode directive. Their use in the library code
+ was fairly minimal:
+
+ C# cstype: std_array.i std_map.i std_vector.i
+ D dtype: std_vector.i
+ Java javatype: arrays_java.i
+
+2017-01-14: wsfulton
+ The %extend directive can now optionally support one of the 'class', 'struct' or 'union'
+ keywords before the identifier name, for example:
+
+ struct X { ... };
+ %extend struct X { ... }
+
+ Previously this had to specified as:
+
+ struct X { ... };
+ %extend X { ... }
+
+2017-01-13: wsfulton
+ [C# D Java] Add new %proxycode directive which is a macro for %insert("proxycode").
+ This is a way of adding pure C#/D/Java code into the appropriate proxy class, eg:
+
+ %extend Proxy2 {
+ %proxycode %{
+ public int proxycode2(int i) {
+ return i+2;
+ }
+ %}
+ }
+
+ %inline %{
+ struct Proxy2 {};
+ %}
+
+ There will then be a pure Java/C#/D method called proxycode2 in the Proxy2 class.
+
+2016-12-31: ajrheading1
+ Issue #860 - Remove use of std::unary_function and std::binary_function
+ which is deprecated in C++11.
+
+2016-12-30: olly
+ [PHP7] Register internal 'swig_runtime_data_type_pointer' constant
+ as "CONST_PERSISTENT" to avoid segmentation fault on module unload.
+ Fixes #859 reported by Timotheus Pokorra. Thanks also to Javier Torres
+ for a minimal reproducer.
+
+Version 3.0.11 (29 Dec 2016)
+============================
+
+2016-12-24: wsfulton
+ [C#] Add %feature("csdirectordelegatemodifiers") to enable customization
+ of the delegate access modifiers generated in director classes.
+ Fixes issue #748.
+
+2016-12-23: wsfulton
+ [Python] Fix builtin "python:slot" feature failing for tp_hash when using
+ hashfunc closure with a "Wrong type for hash function" for Python 2.
+ Issue #843.
+
+2016-12-21: joequamt
+ Changed generation of functions so that only functions
+ that end in _set generate accessor functions rather than
+ looking for "set".
+ Change generation of operators to not have underscores
+ to start in R. Users need to provide custom names for these operator overloads.
+
+2016-12-21: olly
+ Fix isfinite() checks to work with all C++11 compilers.
+ Fixes issues #615, #788 and #849.
+
+2016-12-20: wsfulton
+ %namewarn unnecessarily caused keyword warnings for non-instantiated template classes
+ and duplicate warnings for instantiated template classes when keywords were used.
+ Issue #845.
+
+2016-12-18: ezralanglois
+ [Python, Ruby, Octave] Memory leak fix on error in std::pair wrappers.
+ Issue #851.
+
+2016-12-18: wsfulton
+ Zero initialize arrays when using %array_class and %array_functions.
+
+2016-12-18: t-ikegami
+ [Python] Fix #446
+ Python %array_class of carrays.i failed with -builtin option.
+
+2016-12-16: briancaine
+ [Guile] Patch #744 Added support for Guile's native pointer functionality
+
+2016-12-01: wsfulton
+ [Python] Issue #769.
+ Add optional moduleimport attribute to %module so that the
+ default module import code can be overridden. See the "Searching for the wrapper module"
+ documentation in Python.html. Example:
+
+ %module(moduleimport="import _foo") foo
+
+ $module also expands to the low-level C/C++ module name, so the following is the
+ same as above
+
+ %module(moduleimport="import $module") foo
+
+2016-11-30: olly
+ [PHP] Add support for PHP7. PHP5's C extension API has changed
+ substantially so you need to use -php7 to specify you want PHP7
+ compatible wrappers. The default extension for generated wrappers
+ is now .cxx (to match SWIG's default for every other language - to
+ generate foo_wrap.cpp you can run SWIG with -cppext cpp). Fixes
+ issue #571.
+
+ As part of this change, the language subdirectory for PHP5 has
+ changed from "php" to "php5" - if you are making use of the search
+ path feature where the language subdirectory of each directory
+ is also searched, you'll need to update your bindings. A simple
+ fix which works for older and newer SWIG is to add a symlink:
+ ln -s php php5
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2016-11-30: olly
+ [PHP] Only emit one copy of each distinct arginfo. Previously we
+ emitted a separate one for every wrapped function, but typically
+ many functions have the same number of parameters and combinations
+ of parameters passed by reference or not.
+
+ This change significantly reduces both the size of the generated
+ wrapper, and of the compiled PHP extension module (e.g. by ~6% for
+ the stripped extension module for Xapian's PHP7 bindings).
+
+2016-11-28: wsfulton
+ Fix %rename override of wildcard %rename for templates. For example:
+
+ %rename(GlobalIntOperator) *::operator bool; // wildcard %rename
+
+ %rename(XIntOperator) X::operator bool; // fix now overrides first %rename above
+ OR
+ %rename(XIntOperator) X<int>::operator bool; // fix now overrides first %rename above
+
+ template<typename T> struct X {
+ operator bool();
+ ...
+ };
+ %template(Xint) X<int>;
+
+ This also fixes %rename override of global %rename for templates. For example:
+
+ // Global rename to make all functions start with a lower case letter
+ %rename("%(firstlowercase)s", %$isfunction ) "";
+ %rename(woohoo) W::Woo; // fix now overrides above %rename
+
+ template<typename T> struct W {
+ W Woo();
+ ...
+ };
+ %template(Wint) W<int>;
+
+ The above also introduces a possibly unexpected change. Many of the STL containers
+ provided by SWIG use %rename to rename some methods, eg in std::vector, push_back
+ is renamed to add in Java. Previously this intended rename did not happen when using
+ using global %rename rules and the method would remain as push_back, but is now
+ renamed to add. Some more info in issue #856.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2016-11-26: m7thon
+ [Python] Issue #709 - improved wrapping of division operators
+ 'from __future__ import division' now works in Python 2 whether or not the
+ -py3 flag is used.
+
+2016-11-12: joequant
+ [R] Issue #697 - fix comma issue with overload methods
+
+2016-11-12: joequant
+ [R] Issue #555 - R runtime needs stdio.h
+
+2016-11-02: wsfulton
+ [Python] Issue #816 - fix compilation error when using -extranative and -builtin.
+
+2016-11-02: liorgold
+ Patch #741 - Add support for C++11 alias templates, see updated CPlusPlus11.html
+ documentation.
+
+2016-10-30: myd7349
+ [C#] Patch #740 Add std_array.i for C# for wrapping std::array.
+
+ Patch also enhances std::vector<std::wstring> C# wrappers with additional functions
+ (Contains, IndexOf, LastIndexOf and Remove).
+
+2016-10-30: tobilau
+ [Java] Fix wrappers for wstring parameters in director methods to cleanup local
+ ref after director callback has finished.
+
+2016-10-23: wsfulton
+ [C#] Add missing csdirectorin VOID_INT_PTR and csdirectorout VOID_INT_PTR typemaps.
+
+2016-10-23: jiulongw
+ Patch #781 - Fix wrapping of C compound expressions containing char constants
+ in quotes such as:
+
+ #define H_SUPPRESS_SCALING_MAGIC (('s'<<24) | ('u'<<16) | ('p'<<8) | 'p')
+
+ enum DifferentTypes {
+ typecharcompound='A'+1,
+ typecharcompound2='B' << 2
+ };
+
+2016-10-13: wsfulton
+ [Python] Issue #808 - fix Python pickling and metaclass for builtin wrappers.
+
+ The metaclass (SwigPyObjectType) for SWIG objects was not defined in
+ a way that let importlib successfully import the Python wrappers.
+ The pickle module previously failed to pickle objects because it couldn't
+ determine what module the SWIG wrapped objects were in.
+
+2016-09-29: wsfulton
+ [Allegrocl, CFFI, GO, Javascript, Ocaml, R, Scilab]
+ Add missing support for the "ret" typemap in a few target languages.
+ The documentation also now has info on the "ret" typemap.
+
+2016-09-27: ahmed-usman
+ [xml] Handle template parameters correctly.
+
+2016-09-27: dontpanic92
+ [Go] Fix argument names in inherited functions taking more than 8
+ parameters. Fixes #795.
+
+2016-09-26: smarchetto
+ [Scilab] mlists that map pointers can be given a custom type name.
+
+2016-09-25: wsfulton
+ Patch #793 from q-p to expand exception handling to include std::bad_cast
+ in std_except.i.
+
+2016-09-24: olly
+ [PHP] Fix code generated for feature("director:except") -
+ previously the return value of call_user_function() was ignored and
+ we checked an uninitialised value instead. Fixes #627. Based on
+ patch from Sergey Seroshtan.
+
+2016-09-22: wsfulton
+ [Python] More flexible python builtin slots for overloaded C++ function.
+
+ The closure names used for builtin slots are mangled with their functype so
+ that overloaded C++ method names can be used for multiple slots.
+ For example:
+
+ %feature("python:slot", "mp_subscript", functype="binaryfunc") SimpleArray::__getitem__;
+ %feature("python:slot", "sq_item", functype="ssizeargfunc") SimpleArray::__getitem__(Py_ssize_t n);
+
+ will generate closures:
+
+ SWIGPY_SSIZEARGFUNC_CLOSURE(_wrap_SimpleArray___getitem__) /* defines _wrap_SimpleArray___getitem___ssizeargfunc_closure */
+ SWIGPY_BINARYFUNC_CLOSURE(_wrap_SimpleArray___getitem__) /* defines _wrap_SimpleArray___getitem___binaryfunc_closure */
+
+ Previously only one name was defined: _wrap_SimpleArray___getitem___closure.
+ Hence the overloaded __getitem__ method can be used to support both mp_subscript and sq_item slots.
+
+2016-09-17: wsfulton
+ [Python] Fix iterators for containers of NULL pointers (or Python None) when using
+ -builtin. Previously iteration would stop at the first element that was NULL.
+
+2016-09-16: olly
+ [Javascript] Fix SWIG_exception() macro to return from the current
+ function. Fixes #789, reported by Julien Dutriaux.
+
+2016-09-16: olly
+ [PHP] Fix SWIG_exception() macro to return from the current function.
+ Fixes #240, reported by Sergey Seroshtan.
+
+2016-09-12: xypron
+ [C#] Patch #786 Keyword rename to be CLS compliant by adding an underscore
+ suffix instead of an underscore prefix to the C symbol name. Please use an explicit
+ %rename to rename the symbol with a _ prefix if you want the old symbol name.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2016-09-09: olly
+ [Python] Fix import handling for Python 2.6 to work in a frozen
+ application. Fixes #145, reported by Thomas Kluyver.
+
+2016-09-02: smarchetto
+ [Scilab] Pointers are mapped to mlist instead of tlist
+ (mlist better for scilab overloading)
+
+2016-09-02: olly
+ [PHP] Fix "out" typemap for member function pointers and "in"
+ typemap for char INPUT[ANY].
+
+2016-09-01: wsfulton
+ [Python] More efficient Python slicing.
+ Call reserve for container types that support it to avoid repeated
+ memory reallocations for new slices or slices that grow in size.
+
+2016-09-01: wsfulton
+ [Python] #771 - Make builtin types hashable by default.
+ Default hash is the underlying C/C++ pointer. This matches up with testing for
+ equivalence (Py_EQ in SwigPyObject_richcompare) which compares the pointers.
+
+2016-08-22: wsfulton
+ [Python] The following builtin slots can be customized like other slots via the
+ "python:<x>" and "python:slot" features where <x> is the appropriate slot name:
+ tp_allocs
+ tp_bases
+ tp_basicsize
+ tp_cache
+ tp_del
+ tp_dealloc
+ tp_flags
+ tp_frees
+ tp_getset
+ tp_is_gc
+ tp_maxalloc
+ tp_methods
+ tp_mro
+ tp_new
+ tp_next
+ tp_prev
+ tp_richcompare
+ tp_subclasses
+ tp_weaklist
+ was_sq_ass_slice
+ was_sq_slice
+
+ A few documentation improvements for slot customization.
+
+2016-08-09: joequant
+ [R] Patch #765 Fix extern "C" header includes for C++ code.
+
+2016-08-05: olly
+ [xml] Fix how the output filename is built to avoid problems when
+ it contains the embedded strings ".c", ".cpp" or ".cxx".
+ Fixes #540 reported by djack42.
+
+2016-07-01: wsfulton
+ Fix corner case of wrapping std::vector of T pointers where a pointer to a pointer of T
+ also exists in the wrapped code. SF Bug 2359417 (967).
+
+2016-06-26: wkalinin
+ [Java, C#] Patch #681 Fix seg fault when ignoring nested classes.
+
+2016-06-25: mromberg
+ [Python] #711 Fix -castmode and conversion of signed and unsigned integer types.
+ See 2015-12-23 CHANGES entry for details of these improvements when they were
+ implemented for the default options (ie not using -castmode).
+
+2016-06-25: ahnolds
+ Patch #730 - Fix %implicitconv for overloaded functions when using
+ -castmode or -fastdispatch options.
+
+ The result is that in all overload cases where there are multiple possibilities
+ with the same number of arguments, the dispatch function will first check for
+ exact (aka non implicit) matches, and then subsequently check for implicit
+ casting matches. This was already happening in the normal dispatch situation,
+ and in the -fastdispatch case two passes through the candidates were happening,
+ just with SWIG_POINTER_IMPLICIT_CONV always set. After this patch, it is not set
+ on the first pass, and then set on the second pass.
+
+2016-06-25: liorgold
+ Patch #727 - Add support for C++11 type aliasing.
+
+Version 3.0.10 (12 Jun 2016)
+============================
+
+2016-06-06: mromberg
+ [Python] Patch #698. Add support for -relativeimport for python 2.7, so -py3 is no
+ longer also required for relative import support.
+
+2016-06-05: mromberg
+ [Python] Patch #694 - Fix package import regressions introduced in swig-3.0.9.
+
+ 1) The code in 3.0.9 did not fall back to 'import _foo' if 'import bar._foo' failed
+ (assuming bar.foo was the main module). Every place _foo is imported now first tries
+ it from the package where foo was found and if that fails tries _foo as a global module.
+
+ 2) The separate block of Python code that injected code to pull in the attributes
+ from _foo when -builtin is used made use of the -py3 switch to either do
+ 'from ._foo import *' or "from _foo import *". This block of code no longer does this
+ and instead checks the Python version at runtime to switch between the two syntaxes.
+
+ In summary, swig-3.0.10 has been modified to ease the creation of wrapper modules
+ that can be fully made part of a Python package. SWIG no longer
+ assumes the dynamically linked C module is a global module.
+ The dynamic module can now be placed into either the same package as the pure Python
+ module or as a global module. Both locations are used by the Python wrapper to
+ locate the C module.
+
+ However, this could cause a backwards incompatibility with some code
+ that was relying on the ability of "from package import _module" to
+ pull attributes out of the package directly. If your code populates a
+ module (which is also a package) with attributes that are SWIG
+ generated modules which were not loaded in a conventional way,
+ swig-3.0.8 and earlier may have worked due to 'from package import
+ _module' bypassing a real import and pulling your module in as an
+ attribute. This will no longer work. Since this is not a common (or
+ even recommended) practice, most folk should not be affected.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2016-05-31: wsfulton
+ Fix #690 - Smart pointer to %ignored class doesn't expose inherited methods.
+ Regression introduced in swig-3.0.9.
+
+Version 3.0.9 (29 May 2016)
+===========================
+
+2016-05-24: mromberg
+ [Python] Patch #612 - Add support for Python's implicit namespace packages.
+
+2016-05-23: wsfulton
+ [Ruby] Fix #602 - Error handling regression of opaque pointers introduced
+ in swig-3.0.8 when C functions explicitly reset a pointer using 'DATA_PTR(self) = 0'.
+ An ObjectPreviouslyDeleted error was incorrectly thrown when the pointer was used
+ as a parameter.
+
+2016-05-17: tamuratak
+ [Ruby] Patch #651 - Correct overloaded function error message when function is
+ using %newobject.
+
+2016-05-17: aurelj
+ [Ruby] Patch #582 - add support for docstring option in %module()
+
+2016-05-14: wsfulton
+ Fix #434 - Passing classes by value as parameters in director methods did not create
+ a copy of the argument leading to invalid memory accesses if the object was used
+ after the upcall into the target language. Passing arguments by value shouldn't give
+ rise to these sorts of memory problems and so the objects are now copied and ownership
+ of their lifetime is controlled by the target language.
+
+2016-05-07: wsfulton
+ Fix #611. Fix assertion handling defaultargs when using %extend for a template
+ class and the extended methods contain default arguments.
+
+2016-05-05: ejulian
+ [Python] Patch #617. Fix operator/ wrappers.
+
+2016-05-02: wsfulton
+ Fix #669. Don't issue warning about ignoring base classes when the derived class is
+ itself ignored.
+
+2016-04-18: ianlancetaylor
+ [Go] Fix use of goout typemap when calling base method by
+ forcing the "type" attribute to the value we need.
+
+2016-04-17: ianlancetaylor
+ [Go] Fixes for Go 1.6: avoid returning Go pointers from
+ directors that return string values; add a trailing 0 byte
+ when treating Go string as C char*.
+
+2016-04-06: smarchetto
+ [Scilab] #552 Make Scilab runtime keep track of pointer types
+ Instead of a Scilab pointer which has no type, SWIG Scilab maps a
+ pointer to a structure tlist containing the pointer adress and its type.
+
+2016-04-02: ahnolds
+ [Python] Apply #598. Fix misleading error message when attempting to read a non-existent
+ attribute. The previous cryptic error message:
+ AttributeError: type object 'object' has no attribute '__getattr__'
+ is now replaced with one mentioning the attribute name, eg:
+ AttributeError: 'Foo' object has no attribute 'bar'
+
+2016-04-02: derkuci
+ [Python] Patch #610 to fix #607.
+ Fix single arguments when using python -builtin -O with %feature("compactdefaultargs")
+
+2016-03-31: wsfulton
+ Fixes #594. Fix assertion for some languages when wrapping a C++11 enum class that
+ is private in a class.
+
+ Also don't wrap private enums for a few languages that attempted to do so.
+
+2016-03-31: wsfulton
+ [Java] unsigned long long marshalling improvements when a negative number
+ is passed from Java to C. A cast to signed long long in the C layer will now
+ result in the expected value. No change for positive numbers passed to C.
+ Fixes #623.
+
+2016-03-22: alexwarg
+ [Lua] #398 Fix lua __getitem + inheritance
+ The new handling of classes in Lua (not merging methods into the derived classes)
+ breaks for classes that provide a __getitem function. The __getitem function
+ prevents method calls to any method defined in a base class. This fix calls
+ __getitem only if the member is not found using recursive lookup.
+
+2016-03-18: ptomulik
+ [Python] #563 Stop generating unnecessary _swigconstant helpers.
+
+2016-03-16: richardbeare
+ [R] #636 Add extra std::vector numeric types
+
+2016-03-14: wsfulton
+ [Java] Add std_array.i for C++11 std::array support.
+
+2016-03-12: wsfulton
+ [Java, C#, D] Fix static const char member variables wrappers with %javaconst(1)
+ %csconst(1) or %dmanifestconst.
+ This fixes the case when an integer is used as the initializer, such as:
+
+ struct W { static const char w = 100; };
+
+ Fix generated code parsing enum values using char escape sequences
+ when these values appear in the Java code (usually when using %javaconst(1))
+ such as:
+
+ enum X { x1 = '\n', x2 = '\1' };
+
+ Similarly for static const member char variables such as:
+
+ struct Y { static const char y = '\n'; }
+
+ Likewise for D and %dmanifestconstant. For C# and %csconst(1), char
+ values in C# are now hex escaped as C# doesn't support C octal escaping.
+
+2016-03-11: wsfulton
+ [Java C#] Add support for treating C++ base classes as Java interfaces
+ instead of Java proxy classes. This enable some sort of support for
+ multiple inheritance. The implementation is in swiginterface.i and
+ provides additional macros (see Java.html for full documentation):
+
+ %interface(CTYPE)
+ %interface_impl(CTYPE)
+ %interface_custom("PROXY", "INTERFACE", CTYPE)
+
+2016-03-01: wsfulton
+ Add rstrip encoder for use in %rename. This is like the strip encoder but
+ strips the symbol's suffix instead of the prefix. The example below
+ will rename SomeThingCls to SomeThing and AnotherThingCls to AnotherThing:
+
+ %rename("%(rstrip:[Cls])s") "";
+
+ class SomeThingCls {};
+ struct AnotherThingCls {};
+
+2016-03-01: olly
+ Fix isfinite() check to work with GCC6. Fixes
+ issue #615 reported by jplesnik.
+
+2016-02-17: olly
+ [Python] Add missing keywords 'as' and 'with' to pythonkw.swg.
+
+2016-02-07: kwwette
+ [Octave] recognise various unary functions
+ * Use __float__() for numeric conversions, e.g. when calling double()
+ * Map various unary functions, e.g. abs() to __abs__(), see full list
+ in section 32.3.10 of manual; only available in Octave 3.8.0 or later
+
+2016-02-07: kwwette
+ [Octave] export function swig_octave_prereq() for testing Octave version
+
+2016-02-06: pjohangustavsson
+ [C#] Fix duplicate symbol problems when linking the source generated
+ from multiple SWIG modules into one shared library for the -namespace
+ option. The namespace is now mangled into the global PInvoke function
+ names.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2016-01-27: ahnolds
+ [Python] Added support for differentiating between Python Bytes
+ and Unicode objects using by defining SWIG_PYTHON_STRICT_BYTE_CHAR
+ and SWIG_PYTHON_STRICT_UNICODE_WCHAR.
+
+2016-01-27: steeve
+ [Go] Ensure structs are properly packed between gc and GCC/clang.
+
+2016-01-25: ahnolds
+ [Python] Support the full Python test suite in -classic mode
+ * Convert long/unsigned long/long long/unsigned long long to PyInt
+ rather than PyLong when possible. Certain python functions like
+ len() require a PyInt when operating on old-style classes.
+ * Add support for static methods in classic mode, including support
+ for pythonappend, pythonprepend, and docstrings.
+ * Removing the use of __swig_getmethods__ for static member methods
+ since they will always be found by the standard argument lookup
+ * Fix a bug where the wrong type of exception was caught when
+ checking for new-style class support
+
+2016-01-23: ahnolds
+ [Go] Enable support for the Go test-suite on OSX:
+ * The linker on OSX requires that all symbols (even weak symbols)
+ are defined at link time. Because the function _cgo_topofstack is
+ only defined starting in Go version 1.4, we explicitly mark it as
+ undefined for older versions of Go on OSX.
+ * Avoid writing empty swigargs structs, since empty structs are not
+ allowed in extern "C" blocks.
+
+2016-01-12: olly
+ [Javascript] Look for "nodejs" as well as "node", as it's packaged
+ as the former on Debian.
+
+2016-01-12: olly
+ [Javascript] For v8 >= 4.3.0, use V8_MAJOR_VERSION.
+ Fixes issue 561.
+
+2016-01-10: ahnolds
+ Improved size_t and ptrdiff_t typemaps to support large values
+ on platforms where sizeof(size_t) > sizeof(unsigned long) and
+ sizeof(ptrdiff_t) > sizeof(long).
+
+Version 3.0.8 (31 Dec 2015)
+===========================
+
+2015-12-30: wsfulton
+ The pdf documentation is now generated by wkhtmltopdf and has colour
+ for the code snippets just like the html documentation!
+
+2015-12-23: ahnolds
+ [Python] Fixes for conversion of signed and unsigned integer types:
+
+ No longer check for PyInt objects in Python3. Because PyInt_Check
+ and friends are #defined to the corresponding PyLong methods, this
+ had caused errors in Python3 where values greater than what could be
+ stored in a long were incorrectly interpreted as the value -1 with
+ the Python error indicator set to OverflowError. This applies to
+ both the conversions PyLong->long and PyLong->double.
+
+ Conversion from PyLong to long, unsigned long, long long, and
+ unsigned long long now raise OverflowError instead of TypeError in
+ both Python2 and Python3 for PyLong values outside the range
+ expressible by the corresponding C type. This matches the existing
+ behavior for other integral types (signed and unsigned ints, shorts,
+ and chars), as well as the conversion for PyInt to all numeric
+ types. This also indirectly applies to the size_t and ptrdiff_t
+ types, which depend on the conversions for unsigned long and long.
+
+2015-12-19: wsfulton
+ [Python] Python 2 Unicode UTF-8 strings can be used as inputs to char * or
+ std::string types if the generated C/C++ code has SWIG_PYTHON_2_UNICODE defined.
+
+2015-12-17: wsfulton
+ Issues #286, #128
+ Remove ccache-swig.1 man page - please use the CCache.html docs instead.
+ The yodl2man and yodl2html tools are no longer used and so SWIG no
+ longer has a dependency on these packages which were required when
+ building from git.
+
+2015-12-16: zturner/coleb
+ [Python] Fix Python3.5 interpreter assertions when objects are being
+ deleted due to an existing exception. Most notably in generators
+ which terminate using a StopIteration exception. Fixes #559 #560 #573.
+ If a further exception is raised during an object destruction,
+ PyErr_WriteUnraisable is used on this second exception and the
+ original exception bubbles through.
+
+2015-12-14: ahnolds/wsfulton
+ [Python] Add in missing initializers for tp_finalize,
+ nb_matrix_multiply, nb_inplace_matrix_multiply, ht_qualname
+ ht_cached_keys and tp_prev.
+
+2015-12-12: wsfulton
+ Fix STL wrappers to not generate <: digraphs.
+ For example std::vector<::X::Y> was sometimes generated, now
+ corrected to std::vector< ::X::Y >.
+
+2015-11-25: wsfulton
+ [Ruby] STL ranges and slices fixes.
+
+ Ruby STL container setting slices fixes:
+
+ Setting an STL container wrapper slice better matches the way Ruby
+ arrays work. The behaviour is now the same as Ruby arrays. The only
+ exception is the default value used when expanding a container
+ cannot be nil as this is not a valid type/value for C++ container
+ elements.
+
+ Obtaining a Ruby STL container ranges and slices fixes:
+
+ Access via ranges and slices now behave identically to Ruby arrays.
+ The fixes are mostly for out of range indices and lengths.
+ - Zero length slice requests return an empty container instead of nil.
+ - Slices which request a length greater than the size of the container
+ no longer chop off the last element.
+ - Ranges which used to return nil now return an empty array when the
+ the start element is a valid index.
+
+ Ruby STL container negative indexing support improved.
+
+ Using negative indexes to set values works the same as Ruby arrays, eg
+
+ %template(IntVector) std::vector<int>;
+
+ iv = IntVector.new([1,2,3,4])
+ iv[-4] = 9 # => [1,2,3,9]
+ iv[-5] = 9 # => IndexError
+
+2015-11-21: wsfulton
+ [Ruby, Python] Add std::array container wrappers.
+
+ These work much like any of the other STL containers except Python/Ruby slicing
+ is somewhat limited because the array is a fixed size. Only slices of
+ the full size are supported.
+
+2015-10-10: wsfulton
+ [Python] #539 - Support Python 3.5 and -builtin. PyAsyncMethods is a new
+ member in PyHeapTypeObject.
+
+2015-10-06: ianlancetaylor
+ [Go] Don't emit a constructor function for a director
+ class with an abstract method, since the function will
+ always panic.
+
+2015-10-01: wsfulton
+ Fix %shared_ptr support for private and protected inheritance.
+ - Remove unnecessary Warning 520: Derived class 'Derived' of 'Base'
+ is not similarly marked as a smart pointer
+ - Do not generate code that attempts to cast up the inheritance chain in the
+ type system runtime in such cases as it doesn't compile and can't be used.
+ Remove unnecessary warning 520 for %shared_ptr when the base class is ignored.
+
+2015-10-01: vkalinin
+ Fix #508: Fix segfault parsing anonymous typedef nested classes.
+
+2015-09-26: wsfulton
+ [Ruby] Add shared_ptr support
+
+2015-09-13: kkaempf
+ [Ruby] Resolve tracking bug - issue #225.
+ The bug is that the tracking code uses a ruby hash and thus may
+ allocate objects (Bignum) while running the GC. This was tolerated in
+ 1.8 but is invalid (raises an exception) in 1.9.
+ The patch uses a C hash (also used by ruby) instead.
+
+2015-09-09: lyze
+ [CFFI] Extend the "export" feature in the CFFI module to support
+ exporting to a specified package.
+
+2015-09-04: olly
+ [Python] Fix docstrings for %callback functions.
+
+2015-09-03: demi-rluddy
+ [Go] Removed golang stringing for signed/unsigned char
+
+ Changed default handling of signed char* and unsigned char* to be
+ opaque pointers rather than strings, similarly to how other
+ languages work.
+
+ Any existing code relying on treating signed char* or unsigned
+ char* as a string can restore the old behavior with typemaps.i by
+ using %apply to copy the [unchanged] char* behavior.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2015-08-07: talby
+ [Perl] tidy -Wtautological-constant-out-of-range-compare warnings when building generated code under clang
+
+2015-08-07: xantares
+ [Python] pep257 & numpydoc conforming docstrings:
+ - Mono-line module docsstring
+ - Rewrite autodoc parameters section in numpydoc style:
+ https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
+ - One line summary should end with "."
+ - Adds a blank line after class docstring
+
+2015-08-05: vadz
+ [Java] Make (char* STRING, size_t LENGTH) typemaps usable for
+ strings of other types, e.g. "unsigned char*".
+
+Version 3.0.7 (3 Aug 2015)
+==========================
+
+2015-08-02: wsfulton
+ [Java] Fix potential security exploit in generated Java classes.
+ The swigCPtr and swigCMemOwn member variables in the generated Java
+ classes are now declared 'transient' by default. Further details of the exploit
+ in Android is being published in an academic paper as part of USENIX WOOT '15:
+ https://www.usenix.org/conference/woot15/workshop-program/presentation/peles.
+
+ In the unlikely event that you are relying on these members being serializable,
+ then you will need to override the default javabody and javabody_derived typemaps
+ to generate the old generated code. The relevant typemaps are in the Lib directory
+ in the java.swg, boost_shared_ptr.i and boost_intrusive_ptr.i files. Copy the
+ relevant default typemaps into your interface file and remove the 'transient' keyword.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2015-08-01: vadz
+ Make configure --without-alllang option more useful: it can now be overridden by the following
+ --with-xxx options, allowing to easily enable just one or two languages.
+
+2015-07-30: wsfulton
+ Fix #440 - Initialise all newly created arrays when using %array_functions and %array_class
+ in the carrays.i library - bug is only relevant when using C++.
+
+2015-07-29: wsfulton
+ [Python] Improve indentation warning and error messages for code in the following directives:
+
+ %pythonprepend
+ %pythonappend
+ %pythoncode
+ %pythonbegin
+ %feature("shadow")
+
+ Old error example:
+ Error: Line indented less than expected (line 3 of pythoncode)
+
+ New error example:
+ Error: Line indented less than expected (line 3 of %pythoncode or %insert("python") block)
+ as no line should be indented less than the indentation in line 1
+
+ Old warning example:
+ Warning 740: Whitespace prefix doesn't match (line 2 of %pythoncode or %insert("python") block)
+
+ New warning example:
+ Warning 740: Whitespace indentation is inconsistent compared to earlier lines (line 3 of
+ %pythoncode or %insert("python") block)
+
+
+2015-07-28: wsfulton
+ [Python] Fix #475. Improve docstring indentation handling.
+
+ SWIG-3.0.5 and earlier sometimes truncated text provided in the docstring feature.
+ This occurred when the indentation (whitespace) in the docstring was less in the
+ second or later lines when compared to the first line.
+ SWIG-3.0.6 gave a 'Line indented less than expected' error instead of truncating
+ the docstring text.
+ Now the indentation for the 'docstring' feature is smarter and is appropriately
+ adjusted so that no truncation occurs.
+
+2015-07-22: wsfulton
+ Support for special variable expansion in typemap attributes. Example usage expansion
+ in the 'out' attribute (C# specific):
+
+ %typemap(ctype, out="$*1_ltype") unsigned int& "$*1_ltype"
+
+ is equivalent to the following as $*1_ltype expands to 'unsigned int':
+
+ %typemap(ctype, out="unsigned int") unsigned int& "unsigned int"
+
+ Special variables can be used within special variable macros too. Example usage expansion:
+
+ %typemap(cstype) unsigned int "uint"
+ %typemap(cstype, out="$typemap(cstype, $*1_ltype)") unsigned int& "$typemap(cstype, $*1_ltype)"
+
+ Special variables are expanded first and hence the above is equivalent to:
+
+ %typemap(cstype, out="$typemap(cstype, unsigned int)") unsigned int& "$typemap(cstype, unsigned int)"
+
+ which then expands to:
+
+ %typemap(cstype, out="uint") unsigned int& "uint"
+
+2015-07-22: lindleyf
+ Apply patch #439 - support for $typemap() (aka embedded typemaps or special variable
+ macros) in typemap attributes. A simple example where $typemap() is expanded in the
+ 'out' attribute (C# specific):
+
+ %typemap(cstype) unsigned int "uint"
+ %typemap(cstype, out="$typemap(cstype, unsigned int)") unsigned int& "$typemap(cstype, unsigned int)"
+
+ is equivalent to:
+
+ %typemap(cstype, out="uint") unsigned int& "uint"
+
+2015-07-18: m7thon
+ [Python] Docstrings provided via %feature("docstring") are now quoted and added to
+ the tp_doc slot when using python builtin classes (-builtin). When no docstring is
+ provided, the tp_doc slot is set to the fully qualified C/C++ class name.
+ Github issues #445 and #461.
+
+2015-07-17: kwwette
+ [octave] Support Octave version 4.0.0 (thanks to patches from Orion Poplawski).
+
+2015-07-07: wsfulton
+ SWIG no longer generates a wrapper for a class' constructor if that class has
+ any base class with a private destructor. This is because your compiler should
+ not allow a class to be instantiated if a base has a private destructor. Some
+ compilers do, so if you need the old behaviour, use the "notabstract" feature, eg:
+
+ %feature("notabstract") Derived;
+ class Base {
+ ~Base() {}
+ };
+ struct Derived : Base {};
+
+Version 3.0.6 (5 Jul 2015)
+==========================
+
+2015-07-02: wsfulton
+ Fix syntax error when the template keyword is used in types, eg:
+
+ std::template vector<int> v;
+
+2015-07-02: ngladitz
+ [Lua] Push characters as unformatted 1-character strings to avoid
+ unprintable characters such as (char)127 being converted to
+ "<\127>" with Lua 5.3 and later. (github PR #452)
+
+2015-06-29: olly
+ [Python] Improve handling of whitespace in %pythoncode.
+
+ Previously SWIG looked at the indentation of the first line and
+ removed that many characters from each subsequent line, regardless
+ of what those characters were. This was made worse because SWIG's
+ preprocessor removes any whitespace before a '#'. Fixes github
+ issue #379, reported by Joe Orton.
+
+2015-06-12: wsfulton
+ [R] Fix #430 - call to SWIG_createNewRef in copyToC was incorrectly named.
+
+2015-06-11: sghirate
+ [C#] Patch #427 adds in new command line option -outfile to combine all the
+ generated C# code into a single file.
+
+2015-06-09: wsfulton
+ Fix seg fault processing C++11 type aliasing. Issue #424.
+
+2015-05-28: wsfulton
+ [Python] Add new feature "python:cdefaultargs" to control default argument
+ code generation. By default, SWIG attempts to convert C/C++ default argument values
+ into Python values and generates code into the Python layer with these values.
+ Recent versions of SWIG are able to convert more of these values, however, the
+ new behaviour can be circumvented if desired via this new feature, such that
+ the default argument values are obtained from the C layer and not the Python layer.
+ For example:
+
+ struct CDA {
+ int fff(int a = 1, bool b = false);
+ };
+
+ The default code generation in the Python layer is:
+
+ class CDA(_object):
+ ...
+ def fff(self, a=1, b=False):
+ return _default_args.CDA_fff(self, a, b)
+
+ Adding the feature:
+
+ %feature("python:cdefaultargs") CDA::fff;
+
+ Results in:
+
+ class CDA(_object):
+ ...
+ def fff(self, *args):
+ return _default_args.CDA_fff(self, *args)
+
+ Some code generation modes, eg -builtin and -fastproxy, are unaffected by this as
+ the default values are always obtained from the C layer.
+
+2015-05-27: wsfulton
+ [Python] Deal with an integer as the default value of a typedef to bool
+ parameter in the C++ prototype. See #327. Regression from 3.0.0 onwards.
+
+2015-05-19: olly
+ [Python] Fix warning when compiling generated code with MSVC.
+ (Fixes https://sourceforge.net/p/swig/patches/351/ reported by
+ Mateusz Szyma¿ski).
+
+2015-05-14: wsfulton
+ Fix seg fault wrapping shared_ptr of classes with private constructors and destructors.
+ This also fixes the "unref" feature when used on classes with private destructors.
+
+2015-05-10: wsfulton
+ [Java] Fix multi-argument typemaps (char *STRING, size_t LENGTH)
+ so that they can be applied to a wider range of types. Fixes #385.
+
+2015-05-07: olly
+ [Python] Deal with an integer as the default value of a bool
+ parameter in the C++ prototype. Fixes github #327, reported by
+ Greg Allen.
+
+2015-05-07: LindleyF
+ [Java] Allow feature("director") and feature("ref") to be used
+ together. Github PR#403.
+
+2015-05-05: olly
+ Suppress warning 325 "Nested class not currently supported (Foo
+ ignored)" when Foo has already been explicitly ignored with "%ignore".
+
+2015-05-04: wsfulton
+ Add support for friend templates, including operator overloading - fixes #196. Considering
+ the example below, previously the operator gave a syntax error and friendfunc incorrectly
+ warned with:
+
+ "Warning 503: Can't wrap 'friendfunc<(Type)>' unless renamed to a valid identifier."
+
+ template <class Type> class MyClass {
+ friend int friendfunc <Type>(double is, MyClass <Type> & x);
+ friend int operator<< <Type>(double un, const MyClass <Type> &x);
+ };
+
+ The following also previously incorrectly warned with:
+
+ "Warning 302: Identifier 'template_friend' redefined (ignored),"
+
+ template<typename T> T template_friend(T);
+ struct MyTemplate {
+ template<typename T> friend T template_friend(T);
+ };
+
+2015-05-01: wsfulton
+ Fix handling of conversion operators where the operator is split over multiple
+ lines or has comments within the operator type. Fixes #401.
+
+ Also fix similar problem with normal operators which gave a syntax error if split over
+ multiple lines or had a comment within the operator declaration.
+
+2015-04-30: olly
+ Ignore unknown preprocessor directives which are inside an inactive
+ conditional (github issue #394, reported by Dan Wilcox).
+ Regression introduced in 3.0.3.
+
+2015-04-27: vadz
+ [Python] Fix "default" typemap used after an argument with "numinputs=0" (#377).
+
+2015-04-24: wsfulton
+ [Python] Fix #256. Code generated with '-builtin -modernargs' segfaults for any
+ method taking zero arguments.
+
+ Also fixes: "SystemError: error return without exception set" during error checking
+ when using just -builtin and the incorrect number of arguments is passed to a class
+ method expecting zero arguments.
+
+2015-04-23: wsfulton
+ [Java] Bug #386 - Memory leak fix in (char *STRING, size_t LENGTH) typemaps.
+
+2015-04-23: vadz
+ [Python] Make "default" typemap work again (#330, #377).
+
+2015-04-23: vadz
+ [Python] Fix the use of default values for the pointer types (#365, #376).
+
+2015-04-23: wsfulton
+ Fix 'make check-ccache' which is part of 'make check' when one of the CCACHE_
+ environment variables, for example CCACHE_DISABLE, is set.
+
+2015-04-14: wsfulton
+ Clearer warning message for badly constructed typecheck typemaps. For example, was:
+
+ example.i:3: Warning 467: Overloaded foo(int) not supported (no type checking
+ rule for 'int').
+
+ Now:
+
+ example.i:3: Warning 467: Overloaded foo(int) not supported (incomplete type checking
+ rule - no precedence level in typecheck typemap for 'int').
+
+2015-04-11: wsfulton
+ [Java] Fix #353 - Linker multiple definition of 'ExceptionMatches' when
+ using directors and multiple modules.
+
+2015-04-11: wsfulton
+ Merge #320 - Make __dict__ accessible for Python builtin classes.
+
+2015-04-07: wsfulton
+ Fix #375 - parsing of extern "C" and typedef for example:
+ extern "C" typedef void (*Hook2_t)(int, const char *);
+ extern "C" typedef int Integer;
+
+2015-03-12: olly
+ -DSWIG_DIRECTOR_STATIC is now supported for all languages with
+ director support, not only Python and PHP.
+
+2015-03-02: ianlancetaylor
+ [Go] Add -cgo option, required for Go versions 1.5 and
+ later.
+
+2015-02-26: olly
+ Fix segmentation fault when top==NULL, introduced by nested class
+ handling (reported in issue#346 by Pawe¿ Tomulik).
+
+2015-02-09: wsfulton
+ [Guile] Fix generated code for static const char member variables when
+ defined and declared inline.
+
+2015-02-09: mishas
+ [Go] Fix %import of files in sub directories.
+
+2015-02-05: ianlancetaylor
+ [Go] Ignore Go specific type maps (goin, goout, etc.) if they are empty.
+
+2015-02-05: ianlancetaylor
+ [Go] Generated Go code no longer calls _swig_goallocate or
+ _swig_makegostring, as they will no longer work as of Go 1.5.
+
+Version 3.0.5 (31 Jan 2015)
+===========================
+
+2015-01-30: wsfulton
+ [Python] Fix Python -classic and property setting. Setting properties on classic classes
+ was broken in swig-3.0.3 by attempting to use __setattr__. This regression is fixed now
+ by using __dict__ again when using -classic.
+ Fixes patch #232.
+
+2015-01-27: smarchetto
+ [Scilab] Support for the Scilab language has been added
+
+2015-01-23: olly
+ [PHP] When wrapping a returned resource as an object, check if all
+ cases wrap it in the same class, and if so eliminate the pointless
+ switch statement wrapper we previously generated.
+
+2015-01-22: wsfulton
+ [Octave] Merge patch #297 for SF bug #1277 - Octave shared_ptr support
+
+2015-01-15: wsfulton
+ [Python] Merge patch #250 - Fixes for using %constant and objects (non-primitive types)
+
+2015-01-15: wsfulton
+ [C# Go] Merge patch #308 and fix #307 - C++11 strongly typed enum support
+ in directors
+
+2015-01-15: wsfulton
+ [Python] Second fix for #294 #296 - Regression introduced in SWIG-3.0.3 when
+ wrapping functions with default arguments, this time when using kwargs.
+
+Version 3.0.4 (14 Jan 2015)
+===========================
+
+2015-01-12: olly
+ [PHP] Fix segfault in director upcall check when using PHP built with
+ ZTS enabled. Fixes #155, reported by Pierre Labastie.
+
+2015-01-12: vadz
+ [Python] Fix #294 #296 - Regression introduced in SWIG-3.0.3 when
+ wrapping functions with default arguments. Invalid or missing default
+ arguments were sometimes being generated into the python layer.
+
+2015-01-08: olly
+ Allow C++11 "explicit constexpr". Fixes github issue #284 reported
+ by Pawel Tomulik. Also handle "constexpr explicit" and "constexpr
+ static".
+
+2015-01-08: olly
+ When reporting an error for a construct which hasn't been
+ terminated when the end of the file is reached, report it at the
+ start line rather than "EOF" as then tools like editors and IDEs
+ will take you to a generally more useful place for fixing the
+ problem.
+
+2015-01-08: olly
+ Improve error messages for a few cases which previously gave the
+ one of the cryptic catch-all errors "Syntax error in input".
+
+2015-01-08: olly
+ Provide -cppext as a general command line option for setting the
+ extension used for generated C++ files (previously it was specific
+ to the PHP backend). Deprecate the equivalent -suffix option
+ provided by the Ocaml backend, but continue to support that for
+ now.
+
+Version 3.0.3 (30 Dec 2014)
+===========================
+
+2014-12-27: wsfulton
+ Fix #280 - abort using all default template parameters within other template
+ parameters.
+
+2014-12-27: talby
+ [Perl] Issue #282 perl5 archlib vs archlibexp
+ [Perl] tidy "warning: duplicate 'extern' declaration specifier" when building generated code
+ under clang
+
+2014-12-18: wsfulton
+ Add support for %constant and structs/classes - issue #272
+
+2014-12-09: wsfulton
+ Fix #245 - regression (since swig-3.0.0) in templated constructors.
+ Templated constructors could not be instantiated - they were incorrectly ignored with a warning 504:
+ "Function: xyz must have a return type. Ignored."
+
+2014-12-07: wsfulton
+ Add support for C++11 strongly typed enumerations.
+
+2014-11-21: wsfulton
+ [Java C#] Fix multiply defined error when using %rename of enum items when using the "simple enum"
+ wrappers.
+
+2014-10-28: vadz
+ [Python] Patch #201 The generated .py file no longer uses *args for all Python parameters.
+ Instead, the parameters are named using the C++ parameter names.
+
+ "compactdefaultargs" feature can be enabled to restore the old behaviour.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-10-24: timotheecour
+ [D] Patch #204 Use core.atomic.atomicOp to mutate shared variables
+
+2014-10-21: wsfulton
+ Fix issue #242 - Use of the "kwargs" feature no longer automatically turns on the
+ "compactdefaultargs" feature if the target language does not support kwargs.
+ This change affects all languages except Python and Ruby.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-10-10: diorcety
+ [Python] Patch #232 Fix property access using directors
+
+2014-10-06: wsfulton
+ [Python] Fixes when using -builtin and std::vector/std::list wrappers to allow deletion
+ of single elements, such as 'del vec[0]'.
+
+2014-09-30: oliverb
+ [Javascript] Merge patch #216 by Richie765 - Added support for many versions of v8 javascript.
+
+2014-09-30: oliverb
+ [Javascript] Merge patch #195 by zittix - Fixed JSClassRef declaration not using the static one.
+
+2014-09-30: ianlancetaylor
+ [Go] In configure script, require Go 1.1 or later.
+
+2014-09-30: wsfulton
+ [Python] Patch #207 - Fix No module error with -relativeimport when using single
+ header file import.
+
+2014-09-27: wsfulton
+ Patch #208 - Initialise newly created array when using array_functions in the
+ carrays.i library (C++ usage).
+
+2014-09-27: wsfulton
+ [Ruby] Patch #187 - Fix crash on shutdown of the Ruby interpreter if more than one
+ module was loaded at a time when data is being shared between modules.
+
+2014-09-27: wsfulton
+ [Java] Patch #168 - Fix leak in Java director string handling after the Java
+ upcall when called from a native thread.
+
+2014-09-25: ianlancetaylor
+ [Go] Adjust generated code to work with upcoming Go 1.4
+ release.
+
+2014-09-23: wsfulton
+ [Python] Add patch from Thomas Maslach to fix crash in wrappers when using -threads in
+ the STL iterators (SwigPyIterator destructor).
+
+2014-09-17: wsfulton
+ [C#] Merge patch #229 from contre - Add bool array types to arrays_csharp.i
+
+2014-09-12: olly
+ [PHP] Add support for specifying any PHP interfaces a wrapped class
+ implements, e.g.: %typemap("phpinterfaces") MyIterator "Iterator"
+
+2014-09-11: olly
+ [PHP] Fix throwing a PHP exception through C++ from a subclassed
+ director method - PHP NULL gets returned by the subclassed method
+ in this case, so the directorout typemap needs to allow that (at
+ least if an exception is active).
+
+2014-09-09: ianlancetaylor
+ [Go] Add goargout typemap.
+
+2014-09-09: olly
+ [PHP] Fix segmentation faults with directors in PHP >= 5.4, and
+ reenable runme tests for director_basic testcase. Fix from
+ pavel-charvat in issue#164.
+
+2014-09-05: ianlancetaylor
+ [Go] Add imtype, goin, goout, godirectorin, and
+ godirectorout typemaps, to support writing Go code to
+ convert between types.
+
+2014-09-02: olly
+ [Python] Fix regression in indentation of python code produced with
+ -modern, introduced by changes in #188. Reported by fabiencastan
+ in #218.
+
+2014-09-01: olly
+ Issue an error for unknown SWIG preprocessor directives, rather
+ than quietly ignoring them. Reported by jrhelsey in issue#217.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-08-15: talby
+ [Perl] Include guard fix for nested modules from Anthony Heading (SF Patch #350).
+
+2014-08-04: wsfulton
+ [C#] Merge patch #200 from gpetrou - Changed CSharp license header to include auto-generated
+ tag so that StyleCop ignores the files.
+
+2014-08-04: wsfulton
+ [Java] Merge patch #198 from Yuval Kashtan - Support for java.nio.ByteBuffer mapping to
+ unsigned char * in various.i in NIOBUFFER typemaps.
+
+2014-07-14: ianlancetaylor
+ [Go] Change struct definition to use void *, not uint8, so
+ that the type is recorded as possibly containing
+ pointers. This ensures that the 1.3 garbage collector
+ does not collect pointers passed to C++ code.
+
+2014-07-01: wsfulton
+ Fix SF Bug #1375 - Expansion of the $parentclassname special variable incorrectly contains
+ brackets in the expanded name.
+
+Version 3.0.2 (4 Jun 2014)
+==========================
+
+2014-06-02: v-for-vandal
+ [Lua] Pull request #176:
+ If class has no __eq implemented, then default __eq is generated.
+ Default __eq compares actual pointers stored inside Lua userdata.
+
+2014-06-02: vkalinin
+ Fix #183 - %extend and unnamed nested structs
+
+2014-05-28: kwwette
+ Fix install failure when using an 'out of source' build using the shipped
+ tarball - regression introduced in swig-3.0.1.
+
+2014-05-24: kwwette
+ [Octave] Remove deprecated -global/-noglobal command-line arguments
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+Version 3.0.1 (27 May 2014)
+===========================
+
+2014-05-25: hfalcic
+ [Python] Python 3 byte string output: use errors="surrogateescape"
+ if available on the version of Python that's in use. This allows
+ obtaining the original byte string (and potentially trying a fallback
+ encoding) if the bytes can't be decoded as UTF-8.
+
+ Previously, a UnicodeDecodeError would be raised with no way to treat
+ the data as bytes or try another codec.
+
+2014-05-18: vkalinin
+ Bug #175 - Restore %extend to work for unnamed nested structures by using a C
+ symbol comprising the outer structure name and unnamed variable instance name.
+
+2014-05-15: kwwette
+ Add #166 - 'make check' now works out of source. This required the examples to build
+ out of source. The main languages have been tested - C#, Go, Guile, Java, Javascript,
+ Lua, Octave, Perl, PHP, Python, Ruby and Tcl.
+
+2014-05-01: Oliver Buchtala
+ Javascript support added, see Javascript chapter in the documentation.
+
+2014-05-01: olly
+ [PHP] The generated __isset() method now returns true for read-only properties.
+
+2014-04-24: kwwette
+ [Go] Fix go ./configure parsing of gccgo --version, and
+ goruntime.swg typo in __GNUC_PATCHLEVEL__ (SF Bug #1298)
+
+2014-04-24: kwwette
+ Fix {python|perl5|ruby|tcl}/java examples
+
+ In Lib/gcj/cni.i, for compatibility with newer gcj versions:
+
+ - remove JvAllocObject() which gcj no longer defines, from gcj Changelog:
+ 2004-04-16 Bryce McKinlay <mckinlay@redhat.com>
+ * gcj/cni.h (JvAllocObject): Remove these obsolete,
+ undocumented CNI calls.
+
+ - change JvCreateJavaVM() argument from void* to JvVMInitArgs*, from gcj Changelog:
+ 2005-02-23 Thomas Fitzsimmons <fitzsim@redhat.com>
+ PR libgcj/16923
+ ...
+ (JvCreateJavaVM): Declare vm_args as JvVMInitArgs* rather than void*.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-04-08: wsfulton
+ SF Bug #1366 - Remove duplicate declarations of strtoimax and strtoumax in inttypes.i
+
+2014-04-08: wsfulton
+ [Java C#] Enums which have been ignored via %ignore and are subsequently
+ used are handled slightly differently. Type wrapper classes are now generated
+ which are effectively a wrapper of an empty enum. Previously in Java uncompilable
+ code was generated and in C# an int was used.
+
+2014-04-04: wsfulton
+ Fix regression in 3.0.0 where legal code following an operator<< definition might
+ give a syntax error. SF Bug #1365.
+
+2014-04-03: olly
+ [PHP] Fix wrapping director constructors with default parameters
+ with a ZTS-enabled build of PHP.
+
+2014-04-02: olly
+ [PHP] Pass the ZTS context we already have to avoid needing to
+ call TSRMLS_FETCH, which is relatively expensive.
+
+2014-04-02: olly
+ [PHP] Pass ZTS context through to t_output_helper() so it works
+ with a ZTS-enabled build of PHP. Reported by Pierre Labastie in
+ github PR#155.
+
+2014-03-28: wsfulton
+ [Java C# D Go] Fixes for C enums used in an API and the definition of the enum
+ has not been parsed. For D, this fixes a segfault in SWIG. The other languages
+ now produce code that compiles, although the definition of the enum is needed
+ in order to use the enum properly from the target language.
+
+2014-03-23: v-for-vandal
+ [Lua] Fix for usage of snprintf in Lua runtime which Visual Studio does not have.
+
+Version 3.0.0 (16 Mar 2014)
+===========================
+
+2014-03-16: wsfulton
+ C++11 support initially developed as C++0x support by Matevz Jekovec as a Google Summer of Code
+ project has been further extended. The C++11 support is comprehensive, but by no means complete
+ or without limitations. Full details for each new feature in C++11 is covered in the
+ CPlusPlus11.html chapter in the documentation which is included in SWIG and also available
+ online at https://www.swig.org/Doc3.0/CPlusPlus11.html.
+
+2014-03-14: v-for-vandal
+ [Lua] Numerous Lua improvements:
+ 1. %nspace support has been added. Namespaces are mapped to tables in the module, with the same
+ name as the C++ namespace.
+ 2. Inheritance is now handled differently. Each class metatable keeps a list of class bases instead
+ of merging all members of all bases into the derived class.
+ 3. The new metatables result in differences in accessing class members. For example:
+
+ %module example
+ struct Test {
+ enum { TEST1 = 10, TEST2 = 20 };
+ static const int ICONST = 12;
+ };
+
+ Now this can be used as follows:
+ print(example.Test.TEST1)
+ print(example.Test.ICONST)
+ The old way was:
+ print(example.Test_TEST1)
+ print(example.Test_ICONST)
+
+ 4. The special class metatable member ".constructor" was removed. Now SWIG generates the proxy
+ function by itself and assigns it directly to the class table "__call" method.
+ 5. eLua should also now support inheritance.
+ 6. 'const' subtable in eLua is considered deprecated.
+
+ Changes in behaviour:
+ a. You can no longer assign to non-existing class members in classes without a __setitem__ method.
+ It will cause a Lua error.
+ b. You can no longer iterate over a module table and copy everything into the global namespace.
+ Actually, this was never the case, but it is now explicitly prohibited.
+ c. Now changing a base class will immediately affect all derived classes.
+ d. There might be some issues with inheritance. Although the bases iteration scheme is the same
+ as was used for merging base classes into derived one, some unknown issues may arise.
+
+ The old metatable behaviour can be restored by using the -no-old-metatable-bindings option.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-03-06: wsfulton
+ [Python] Change in default behaviour wrapping C++ bool. Only a Python True or False
+ will now work for C++ bool parameters. This fixes overloading bool with other types.
+ Python 2.3 minimum is now required for wrapping bool.
+
+ When wrapping:
+
+ const char* overloaded(bool value) { return "bool"; }
+ const char* overloaded(int value) { return "int"; }
+
+ Previous behaviour:
+ >>> overloaded(False)
+ 'int'
+ >>> overloaded(True)
+ 'int'
+ >>> overloaded(0)
+ 'int'
+
+ Now we get the expected behaviour:
+ >>> overloaded(False)
+ 'bool'
+ >>> overloaded(0)
+ 'int'
+
+ The consequence is when wrapping bool in non-overloaded functions:
+
+ const char* boolfunction(bool value) { return value ? "true" : "false"; }
+
+ The previous behaviour was very Pythonic:
+ >>> boolfunction("")
+ 'false'
+ >>> boolfunction("hi")
+ 'true'
+ >>> boolfunction(12.34)
+ 'true'
+ >>> boolfunction(0)
+ 'false'
+ >>> boolfunction(1)
+ 'true'
+
+ Now the new behaviour more along the lines of C++ due to stricter type checking. The
+ above calls result in an exception and need to be explicitly converted into a bool as
+ follows:
+ >>> boolfunction(0)
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ TypeError: in method 'boolfunction', argument 1 of type 'bool'
+ >>> boolfunction(bool(0))
+ 'false'
+
+ The old behaviour can be resurrected by passing the -DSWIG_PYTHON_LEGACY_BOOL command line
+ parameter when executing SWIG. Typemaps can of course be written to customise the behaviour
+ for specific parameters.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-03-06: wsfulton
+ Fix SF Bug #1363 - Problem with method overloading when some methods are added by %extend
+ and others are real methods and using template default parameters with smart pointers.
+ This is noticeable as a regression since 2.0.12 when using the default smart pointer
+ handling for some languages when the smart pointer wraps std::map and other STL containers.
+
+2014-03-02: wsfulton
+ [Python] SF Patch #346 from Jens Krueger. Correct exception thrown attempting to
+ access a non-existent C/C++ global variable on the 'cvar' object. The exception thrown
+ used to be a NameError. However, as this access is via a primary, an AttributeError
+ is more correct and so the exception thrown now is an AttributeError. Reference:
+ http://docs.python.org/2/reference/expressions.html#attribute-references
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-03-01: wsfulton
+ [Python] Patch #143 Fix type shown when using type() to include the module and package
+ name when using -builtin.
+
+2014-03-01: wsfulton
+ [Python] SF patch #347 Fix missing argument count checking with -modern.
+ Fixes regression introduced when builtin changes were introduced in SWIG-2.0.3.
+
+2014-02-21: wsfulton
+ [PHP] Fix warning suppression using %warnfilter for PHP reserved class names.
+
+2014-02-19: olly
+ [Lua] Add keyword warnings for Lua keywords and Basic Functions.
+
+2014-02-19: olly
+ -Wallkw now includes keywords for all languages with keyword
+ warnings (previously Go and R were missing).
+
+2014-02-19: olly
+ [PHP] Update the lists of PHP keywords with new ones from PHP 5.4
+ and newer (and some missing ones from 5.3). Reserved PHP constants
+ names are now checked against enum values and constants, instead
+ of against function and method names. Built-in PHP function names
+ no longer match methods added by %extend. Functions and methods
+ named '__sleep', '__wakeup', 'not', 'parent', or 'virtual' are no
+ longer needlessly renamed.
+
+2014-02-15: wsfulton
+ Fix the %$ismember %rename predicates to also apply to members added via %extend.
+
+ Add %$isextendmember for %rename of members added via %extend. This can be used to
+ distinguish between normal class/struct members and %extend members. For example
+ '%$ismember, %$not %$isextendmember' will now identify just class/struct members.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-02-16: hfalcic
+ [Python] Patch #137 - fix crashes/exceptions in exception handling in Python 3.3
+
+2014-02-15: wsfulton
+ [Java] Add support for the cdata library.
+
+2014-02-08: vkalinin
+ Nested class support added. This primarily allows SWIG to properly parse nested
+ classes and keep the nested class information in the parse tree. Java and C#
+ have utilised this information wrapping the C++ nested classes as Java/C#
+ nested classes. The remaining target languages ignore nested classes as in
+ previous versions. Help is needed by users of these remaining languages to
+ design how C++ nested classes can be best wrapped. Please talk to us on the
+ swig-devel mailing list if you think you can help.
+
+ Previously, there was limited nested class support. Nested classes were treated
+ as opaque pointers. However, the "nestedworkaround" feature provided a way to
+ wrap a nested class as if it was a global class. This feature no longer exists
+ and is replaced by the new "flatnested" feature. This effectively does the same
+ thing with less manual code to be written. Please see the 'Nested classes'
+ section in the documentation in SWIGPlus.html if you were previously using this
+ feature.
+
+ SWIG now parses the contents of nested classes where previously it did not. You
+ may find that you will need to make adjustments to your interface file as
+ effectively extra code is being wrapped.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2014-02-06: gjanssens
+ [Guile] Patch #133. Make scm to string conversion work with non-ascii strings.
+ Guile 2 has a completely rewritten string implementation. SWIG made some assumptions
+ that are no longer valid as to the internals of guile's string representation.
+
+2014-01-30: wsfulton
+ [C#] Add new swigtype_inout.i library containing SWIGTYPE *& OUTPUT typemaps.
+
+ Example usage wrapping:
+
+ void f(XXX *& x) { x = new XXX(111); }
+
+ would be:
+
+ XXX x = null;
+ f(out x);
+ // use x
+ x.Dispose(); // manually clear memory or otherwise leave out and leave it to the garbage collector
+
+2014-01-21: ianlancetaylor
+ [Go] Add %go_import directive.
+
+2014-01-21: ianlancetaylor
+ [Go] Add support for Go 1.3, not yet released.
+
+2014-01-20: wsfulton
+ Director exceptions (Swig::DirectorException) now derive from std::exception
+ and hence provide the what() method. In Python and Ruby, this replaces the now
+ deprecated DirectorException::getMessage() method.
+
+2014-01-14: diorcety
+ Patch #112 - Fix symbol resolution involving scopes that have multiple levels
+ of typedefs - fixes some template resolutions as well as some typemap searches.
+
+2014-01-11: wsfulton
+ Fix and document the naturalvar feature override behaviour - the naturalvar
+ feature attached to a variable name has precedence over the naturalvar
+ feature attached to the variable's type. The overriding was not working
+ when turning the feature off on the variable's name.
+
+ Fix so that any use of the naturalvar feature will override the global
+ setting. Previously when set globally by -naturalvar or %module(naturalvar=1),
+ use of the naturalvar feature was not always honoured.
+
+2014-01-06: ianlancetaylor
+ [Go] Fix bug that broke using directors from a thread not
+ created by Go.
+
+2013-12-24: ptomulik
+ [Python] SF Bug #1297
+
+ Resolve several issues related to python imports.
+ For example, it's now possible to import modules having the same module
+ names, but belonging in different packages.
+
+ From the user's viewpoint, this patch gives a little bit more control on
+ import statements generated by SWIG. The user may choose to use relative
+ or absolute imports.
+
+ Some details:
+ - we (still) generate import statements in the form 'import a.b.c' which
+ corresponds to absolute imports in python3 and (the only available)
+ ambiguous one in python2.
+ - added -relativeimport option to use explicit relative import syntax
+ (python3),
+
+ The "Python Packages" section in the documentation discusses how to work
+ with importing packages including the new -relativeimport command line option.
+
+2013-12-23: vadz
+ [Octave, Perl, Python, R, Ruby, Tcl] Change the length of strings created from fixed-size char
+ buffers in C code.
+
+ This is a potential backwards compatibility break: a "char buf[5]" containing "ho\0la" was
+ returned as a string of length 5 before, but is returned as a string of length 2 now. Also,
+ it was possible to assign a (non-NUL-terminated) string "hello" to such a buffer before but
+ now this fails and only "helo" can fit.
+
+ Apply "char FIXSIZE[ANY]" typemaps to explicitly choose the old behaviour.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2013-12-23: talby
+ [Perl] Add support for directors.
+
+2013-12-18: ianlancetaylor
+ [Go] Don't require that Go environment variables be set
+ when running examples or testsuite when using Go 1 or
+ later.
+
+2013-12-17: ianlancetaylor
+ [Go] Remove -longsize option (for backward compatibility,
+ ignore it if seen).
+
+2013-12-17: ianlancetaylor
+ [Go] Add -go-pkgpath option.
+
+2013-12-16: ianlancetaylor
+ [Go] Update for Go 1.2 release. Add support for linking
+ SWIG code directly into executable, rather than using a
+ shared library.
+
+2013-12-13: ianlancetaylor
+ [Go] Add SWIG source file name as comments in generated
+ files. This can be used by Go documentation tools.
+
+2013-12-12: jleveque
+ [Lua] Fix typo (wchar instead of wchar_t) which made wchar.i
+ for Lua useless.
+
+2013-12-12: vmiklos
+ [PHP] PHP's peculiar call-time pass-by-reference feature was
+ deprecated in PHP 5.3 and removed in PHP 5.4, so update the REF
+ typemaps in phppointers.i to specify pass-by-reference in the
+ function definition. Examples/php/pointer has been updated
+ accordingly.
+
+2013-12-12: olly
+ [PHP] The usage of $input in PHP directorout typemaps has been
+ changed to be consistent with other languages. The typemaps
+ provided by SWIG have been updated accordingly, but if you
+ have written your own directorout typemaps, you'll need to
+ update $input to &$input (or make equivalent changes).
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2013-11-27: vadz
+ [C#, Java, Python] Add std_auto_ptr.i defining typemaps for returning std::auto_ptr<>.
+
+2013-11-09: wsfulton
+ [C#] Apply patch #79 from Brant Kyser
+ - Remove using directives from the generated C# code and fully qualify the use of all .NET
+ framework types in order to minimize potential name collisions from input files defining
+ types, namespace, etc with the same name as .NET framework members.
+ - Globally qualify the use of .NET framework types in the System namespace
+ - Remove .NET 1.1 support, .NET 2 is the minimum for the C# module
+
+ This is a potential backwards compatibility break if code has been added relying on these using
+ statements that used to be generated:
+
+ using System;
+ using System.Runtime.InteropServices;
+
+ The quick fix to add these back in is to add the -DSWIG2_CSHARP command line option when
+ executing SWIG. See CSharp.html documentation for more info.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2013-11-05: wsfulton
+ [Java] Fix some corner cases for the $packagepath/$javaclassname special variable substitution.
+
+2013-11-05: wsfulton
+ [Java] Apply patch #91 from Marvin Greenberg - Add director:except feature for improved
+ exception handling in director methods for Java.
+
+2013-10-15: vadz
+ Allow using \l, \L, \u, \U and \E in the substitution part of %(regex:/pattern/subst/)
+ inside %rename to change the case of the text being replaced.
+
+2013-10-12: wsfulton
+ [CFFI] Apply #96 - superclass not lispify
+
+2013-10-12: wsfulton
+ Merge in C++11 support from the gsoc2009-matevz branch where Matevz Jekovec first
+ started the C++0x additions. Documentation of the C++11 features supported is in a
+ new Chapter of the documentation, "SWIG and C++11" in Doc/Manual/CPlusPlus11.html.
+
+2013-10-04: wsfulton
+ Fix %naturalvar not having any affect on templated classes instantiated with an
+ enum as the template parameter type. Problem reported by Vadim Zeitlin.
+
+2013-09-20: wsfulton
+ [Java] Fix a memory leak for the java char **STRING_ARRAY typemaps.
+
+Version 2.0.12 (9 Feb 2014)
+===========================
+
+2014-01-16: wsfulton
+ [PHP] Fix compilation error in ZTS mode (64 bit windows) due to incorrect placement
+ of TSRMLS_FETCH() in SWIG_Php_GetModule() as reported by Mark Dawson-Butterworth.
+
+2014-01-13: kwwette
+ [Octave] update support to Octave version 3.8.0
+
+ - Octave 3.8.0 no longer defines OCTAVE_API_VERSION_NUMBER, but 3.8.1
+ will define OCTAVE_{MAJOR,MINOR,PATCH}_VERSION instead: see
+ http://hg.savannah.gnu.org/hgweb/octave/rev/b6b6e0dc700e
+ So we now use a new macro SWIG_OCTAVE_PREREQ(major,minor,patch) to
+ enable features requiring Octave version major.minor.patch or later.
+
+ For Octave versions prior to 3.8.1, we reconstruct values for
+ OCTAVE_{MAJOR,MINOR,PATCH}_VERSION based on OCTAVE_API_VERSION_NUMBER,
+ extracted from Octave's ChangeLogs. An additional hack is needed to
+ distinguish between Octave <= 3.2.x and 3.8.0, neither of which define
+ OCTAVE_API_VERSION_NUMBER.
+
+ - Octave 3.8.0 deprecates symbol_table::varref(), so remove its use
+ for this and future versions of Octave.
+
+ - Octave 3.8.0 removes octave_value::is_real_nd_array(), used in
+ octave_swig_type::dims(). Its use is not required here, so remove it.
+
+ - Retested against Octave versions 3.0.5, 3.2.4, 3.4.3, 3.6.4, and 3.8.0.
+
+ - Updated Octave documentation with tested Octave versions, and added a
+ warning against using versions <= 3.x.x, which are no longer tested.
+
+2013-12-22: wsfulton
+ C++11 support for new versions of erase and insert in the STL containers.
+
+ The erase and insert methods in the containers use const_iterator instead
+ of iterator in C++11. There are times when the methods wrapped must match
+ the parameters exactly. Specifically when full type information for
+ template types is missing or SWIG fails to look up the type correctly,
+ for example:
+
+ %include <std_vector.i>
+ typedef float Real;
+ %template(RealVector) std::vector<Real>;
+
+ SWIG does not find std::vector<Real>::iterator because %template using
+ typedefs does not always work and so SWIG doesn't know if the type is
+ copyable and so uses SwigValueWrapper<iterator> which does
+ not support conversion to another type (const_iterator). This resulted
+ in compilation errors when using the C++11 version of the containers.
+
+ Closes #73
+
+2013-10-17: wsfulton
+ [R] Fix SF #1340 - Visual Studio compile error in C++ wrappers due to #include <exception>
+ within extern "C" block.
+
+2013-10-17: wsfulton
+ [Python] Fix SF #1345 - Missing #include <stddef.h> for offsetof when using -builtin.
+
+2013-10-12: wsfulton
+ [Lua] Apply #92 - missing return statements for SWIG_Lua_add_namespace_details()
+ and SWIG_Lua_namespace_register().
+
+Version 2.0.11 (15 Sep 2013)
+============================
+
+2013-09-15: wsfulton
+ [R] Fix attempt to free a non-heap object in OUTPUT typemaps for:
+ unsigned short *OUTPUT
+ unsigned long *OUTPUT
+ signed long long *OUTPUT
+ char *OUTPUT
+ signed char*OUTPUT
+ unsigned char*OUTPUT
+
+2013-09-12: wsfulton
+ [Lua] Pull Git patch #62.
+ 1) Static members and static functions inside class can be accessed as
+ ModuleName.ClassName.FunctionName (MemberName respectively). Old way such as
+ ModuleName.ClassName_FunctionName still works.
+ 2) Same goes for enums inside classes: ModuleName.ClassName.EnumValue1 etc.
+
+2013-09-12: wsfulton
+ [UTL] Infinity is now by default an acceptable value for type 'float'. This fix makes
+ the handling of type 'float' and 'double' the same. The implementation requires the
+ C99 isfinite() macro, or otherwise some platform dependent equivalents, to be available.
+
+ Users requiring the old behaviour of not accepting infinity, can define a 'check' typemap
+ wherever a float is used, such as:
+
+ %typemap(check,fragment="<float.h>") float, const float & %{
+ if ($1 < -FLT_MAX || $1 > FLT_MAX) {
+ SWIG_exception_fail(SWIG_TypeError, "Overflow in type float");
+ }
+ %}
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2013-08-30: wsfulton
+ [Lua] Pull Git patch #81: Include Lua error locus in SWIG error messages.
+ This is standard information in Lua error messages, and makes it much
+ easier to find bugs.
+
+2013-08-29: wsfulton
+ Pull Git patch #75: Handle UTF-8 files with BOM at beginning of file. Was giving an
+ 'Illegal token' syntax error.
+
+2013-08-29: wsfulton
+ [C#] Pull Git patch #77: Allow exporting std::map using non-default comparison function.
+
+2013-08-28: wsfulton
+ [Python] %implicitconv is improved for overloaded functions. Like in C++, the methods
+ with the actual types are considered before trying implicit conversions. Example:
+
+ %implicitconv A;
+ struct A {
+ A(int i);
+ };
+ class CCC {
+ public:
+ int xx(int i) { return 11; }
+ int xx(const A& i) { return 22; }
+ };
+
+ The following python code:
+
+ CCC().xx(-1)
+
+ will now return 11 instead of 22 - the implicit conversion is not done.
+
+2013-08-23: olly
+ [Python] Fix clang++ warning in generated wrapper code.
+
+2013-08-16: wsfulton
+ [Python] %implicitconv will now accept None where the implicit conversion takes a C/C++ pointer.
+ Problem highlighted by Bo Peng. Closes SF patch #230.
+
+2013-08-07: wsfulton
+ [Python] SF Patch #326 from Kris Thielemans - Remove SwigPyObject_print and SwigPyObject_str and
+ make the generated wrapper use the default python implementations, which will fall back to repr
+ (for -builtin option).
+
+ Advantages:
+ - it avoids the swig user having to jump through hoops to get print to work as expected when
+ redefining repr/str slots.
+ - typing the name of a variable on the python prompt now prints the result of a (possibly redefined)
+ repr, without the swig user having to do any extra work.
+ - when redefining repr, the swig user doesn't necessarily have to redefine str as it will call the
+ redefined repr
+ - the behaviour is exactly the same as without the -builtin option while requiring no extra work
+ by the user (aside from adding the %feature("python:slot...) statements of course)
+
+ Disadvantage:
+ - default str() will give different (but clearer?) output on swigged classes
+
+2013-07-30: wsfulton
+ [Python, Ruby] Fix #64 #65: Missing code in std::multimap wrappers. Previously an instantiation
+ of a std::map was erroneously required in addition to an instantiation of std::multimap with the
+ same template parameters to prevent compilation errors for the wrappers of a std::multimap.
+
+2013-07-14: joequant
+ [R] Change types file to allow for SEXP return values
+
+2013-07-05: wsfulton
+ [Python] Add %pythonbegin directive which works like %pythoncode, except the specified code is
+ added at the beginning of the generated .py file. This is primarily needed for importing from
+ __future__ statements required to be at the very beginning of the file. Example:
+
+ %pythonbegin %{
+ from __future__ import print_function
+ print("Loading", "Whizz", "Bang", sep=' ... ')
+ %}
+
+2013-07-01: wsfulton
+ [Python] Apply SF patch #340 - Uninitialized variable fix in SWIG_Python_NonDynamicSetAttr
+ when using -builtin.
+
+2013-07-01: wsfulton
+ [Python, Ruby, Ocaml] Apply SF patch #341 - fix a const_cast in generated code that was generating
+ a <:: digraph when using the unary scope operator (::) (global scope) in a template type.
+
+2013-07-01: wsfulton
+ [Python] Add SF patch #342 from Christian Delbaere to fix some director classes crashing on
+ object deletion when using -builtin. Fixes SF bug #1301.
+
+2013-06-11: wsfulton
+ [Python] Add SWIG_PYTHON_INTERPRETER_NO_DEBUG macro which can be defined to use the Release version
+ of the Python interpreter in Debug builds of the wrappers. The Visual Studio .dsp example
+ files have been modified to use this so that Debug builds will now work without having
+ to install or build a Debug build of the interpreter.
+
+2013-06-07: wsfulton
+ [Ruby] Git issue #52. Fix regression with missing rb_complex_new function for Ruby
+ versions prior to 1.9 using std::complex wrappers if just using std::complex as an output type.
+ Also fix the Complex helper functions external visibility (to static by default).
+
+2013-06-04: olly
+ [PHP] Fix SWIG_ZTS_ConvertResourcePtr() not to dereference NULL
+ if the type lookup fails.
+
+Version 2.0.10 (27 May 2013)
+============================
+
+2013-05-25: wsfulton
+ [Python] Fix Python 3 inconsistency when negative numbers are passed
+ where a parameter expects an unsigned C type. An OverFlow error is
+ now consistently thrown instead of a TypeError.
+
+2013-05-25: Artem Serebriyskiy
+ SVN Patch ticket #338 - fixes to %attribute macros for template usage
+ with %arg.
+
+2013-05-19: wsfulton
+ Fix ccache-swig internal error bug due to premature file cleanup.
+
+ Fixes SF bug 1319 which shows up as a failure in the ccache tests on
+ Debian 64 bit Wheezy, possibly because ENABLE_ZLIB is defined.
+
+ This is a corner case which will be hit when the maximum number of files
+ in the cache is set to be quite low (-F option), resulting in a cache miss.
+
+2013-05-09: kwwette
+ [Octave] Fix bugs in Octave module loading:
+ - fix a memory leak in setting of global variables
+ - install functions only once, to speed up module loads
+
+2013-04-28: gjanssens
+ [Guile] Updates in guile module:
+ - Add support for guile 2.0
+ - Drop support for guile 1.6
+ - Drop support for generating wrappers using guile's gh interface.
+ All generated wrappers will use the scm interface from now on.
+ - Deprecate -gh and -scm options. They are no longer needed.
+ A warning will be issued when these options are still used.
+ - Fix all tests and examples to have a successful travis test
+
+2013-04-18: wsfulton
+ Apply Patch #36 from Jesus Lopez to add support for $descriptor() special variable macro expansion
+ in fragments. For example:
+
+ %fragment("nameDescriptor", "header")
+ %{
+ static const char *nameDescriptor = "$descriptor(Name)";
+ %}
+
+ which will generate into the wrapper if the fragment is used:
+
+ static const char *nameDescriptor = "SWIGTYPE_Name";
+
+2013-04-18: wsfulton
+ Fix SF Bug #428 - Syntax error when preprocessor macros are defined inside of enum lists, such as:
+
+ typedef enum {
+ eZero = 0
+ #define ONE 1
+ } EFoo;
+
+ The macros are silently ignored.
+
+2013-04-17: wsfulton
+ [C#] Pull patch #34 from BrantKyser to fix smart pointers in conjunction with directors.
+
+2013-04-15: kwwette
+ [Octave] Fix bugs in output of cleanup code.
+ - Cleanup code is now written also after the "fail:" label, so it will be called if
+ a SWIG_exception is raised by the wrapping function, consistent with other modules.
+ - Octave module now also recognises the "$cleanup" special variable, if needed.
+
+2013-04-08: kwwette
+ Add -MP option to SWIG for generating phony targets for all dependencies.
+ - Prevents make from complaining if header files have been deleted before
+ the dependency file has been updated.
+ - Modelled on similar option in GCC.
+
+2013-04-09: olly
+ [PHP] Add missing directorin typemap for char* and char[] which
+ fixes director_string testcase failure.
+
+2013-04-05: wsfulton
+ [Ruby] SF Bug #1292 - Runtime fixes for Proc changes in ruby-1.9 when using STL
+ wrappers that override the default predicate, such as:
+
+ %template(Map) std::map<swig::LANGUAGE_OBJ, swig::LANGUAGE_OBJ, swig::BinaryPredicate<> >;
+
+2013-04-05: wsfulton
+ [Ruby] SF Bug #1159 - Correctly check rb_respond_to call return values to fix some
+ further 1.9 problems with functors and use of Complex wrappers.
+
+2013-04-02: wsfulton
+ [Ruby] Runtime fixes for std::complex wrappers for ruby-1.9 - new native Ruby complex numbers are used.
+
+2013-03-30: wsfulton
+ [Ruby] Fix seg fault when using STL containers of generic Ruby types, GC_VALUE or LANGUAGE_OBJECT,
+ on exit of the Ruby interpreter. More frequently observed in ruby-1.9.
+
+2013-03-29: wsfulton
+ [Ruby] Fix delete_if (reject!) for the STL container wrappers which previously would
+ sometimes seg fault or not work.
+
+2013-03-25: wsfulton
+ [Python] Fix some undefined behaviour deleting slices in the STL containers.
+
+2013-03-19: wsfulton
+ [C#, Java, D] Fix seg fault in SWIG using directors when class and virtual method names are
+ the same except being in different namespaces when the %nspace feature is not being used.
+
+2013-02-19: kwwette
+ Fix bug in SWIG's handling of qualified (e.g. const) variables of array type. Given the typedef
+ a(7).q(volatile).double myarray // typedef volatile double[7] myarray;
+ the type
+ q(const).myarray // const myarray
+ becomes
+ a(7).q(const volatile).double // const volatile double[7]
+ Previously, SwigType_typedef_resolve() produces the type
+ q(const).a(7).q(volatile).double // non-sensical type
+ which would never match %typemap declarations, whose types were parsed correctly.
+ Add typemap_array_qualifiers.i to the test suite which checks for the correct behaviour.
+
+2013-02-18: wsfulton
+ Deprecate typedef names used as constructor and destructor names in %extend. The real
+ class/struct name should be used.
+
+ typedef struct tagEStruct {
+ int ivar;
+ } EStruct;
+
+ %extend tagEStruct {
+ EStruct() // illegal name, should be tagEStruct()
+ {
+ EStruct *s = new EStruct();
+ s->ivar = ivar0;
+ return s;
+ }
+ ~EStruct() // illegal name, should be ~tagEStruct()
+ {
+ delete $self;
+ }
+ }
+
+ For now these trigger a warning:
+
+ extend_constructor_destructor.i:107: Warning 522: Use of an illegal constructor name 'EStruct' in
+ %extend is deprecated, the constructor name should be 'tagEStruct'.
+ extend_constructor_destructor.i:111: Warning 523: Use of an illegal destructor name 'EStruct' in
+ %extend is deprecated, the destructor name should be 'tagEStruct'.
+
+ These %extend destructor and constructor names were valid up to swig-2.0.4, however swig-2.0.5 ignored
+ them altogether for C code as reported in SF bug #1306. The old behaviour of using them has been
+ restored for now, but is officially deprecated. This does not apply to anonymously defined typedef
+ classes/structs such as:
+
+ typedef struct {...} X;
+
+2013-02-17: kwwette
+ When generating functions provided by %extend, use "(void)" for no-argument functions
+ instead of "()". This prevents warnings when compiling with "gcc -Wstrict-prototypes".
+
+2013-02-17: kwwette
+ [Octave] Minor fix to autodoc generation: get the right type for functions returning structs.
+
+2013-02-15: wsfulton
+ Deprecate typedef names used in %extend that are not the real class/struct name. For example:
+
+ typedef struct StructBName {
+ int myint;
+ } StructB;
+
+ %extend StructB {
+ void method() {}
+ }
+
+ will now trigger a warning:
+
+ swig_extend.i:19: Warning 326: Deprecated %extend name used - the struct name StructBName
+ should be used instead of the typedef name StructB.
+
+ This is only partially working anyway (the %extend only worked if placed after the class
+ definition).
+
+2013-02-09: wsfulton
+ [CFFI] Apply patch #22 - Fix missing package before &body
+
+2013-01-29: wsfulton
+ [Java] Ensure 'javapackage' typemap is used as it stopped working from version 2.0.5.
+
+2013-01-28: wsfulton
+ [Python] Apply patch SF #334 - Fix default value conversions "TRUE"->True, "FALSE"->False.
+
+2013-01-28: wsfulton
+ [Java] Apply patch SF #335 - Truly ignore constructors in directors with %ignore.
+
+2013-01-18: Brant Kyser
+ [Java] Patch #15 - Allow the use of the nspace feature without the -package commandline option.
+ This works as long and the new jniclasspackage pragma is used to place the JNI intermediate class
+ into a package and the nspace feature is used to place all exposed types into a package.
+
+2013-01-15: wsfulton
+ Fix Visual Studio examples to work when SWIG is unzipped into a directory containing spaces.
+
+2013-01-15: wsfulton
+ [C#] Fix cstype typemap lookup for member variables so that a fully qualified variable name
+ matches. For example:
+ %typemap(cstype) bool MVar::mvar "MyBool"
+ struct MVar {
+ bool mvar;
+ };
+
+2013-01-11: Brant Kyser
+ [Java, C#, D] SF Bug #1299 - Fix generated names for when %nspace is used on
+ classes with the same name in two different namespaces.
+
+2013-01-11: Vladimir Kalinin
+ [C#] Add support for csdirectorin 'pre', 'post' and 'terminator' attributes.
+
+2013-01-08: olly
+ [PHP] Fix to work with a ZTS build of PHP (broken in 2.0.7).
+
+2013-01-07: olly
+ Fix bashism in configure, introduced in 2.0.9.
+
+2013-01-06: wsfulton
+ Pull patch #4 from ptomulik to fix SF Bug #1296 - Fix incorrect warning for virtual destructors
+ in templates, such as:
+ Warning 521: Illegal destructor name B< A >::~B(). Ignored.
+
+2013-01-05: wsfulton
+ [Python] Pull patch #3 from ptomulik to fix SF Bug #1295 - standard exceptions as
+ classes using the SWIG_STD_EXCEPTIONS_AS_CLASSES macro.
+
+2013-01-04: wsfulton
+ [Java] Pull patch #2 from BrantKyser to fix SF Bug #1283 - fix smart pointers in conjuction
+ with directors.
+
+2013-01-03: wsfulton
+ [Java] Pull patch #1 from BrantKyser to fix SF Bug #1278 - fix directors and nspace feature when
+ multilevel namespaces are used.
+
+Version 2.0.9 (16 December 2012)
+================================
+
+2012-12-16: wsfulton
+ Fix garbage line number / empty file name reporting for some missing
+ '}' or ')' error messages.
+
+2012-12-15: kkaempf
+ [Ruby] Apply patch 3530444, Class#methods and Class#constants returns array of
+ symbols in Ruby 1.9+
+
+2012-12-14: kkaempf
+ [Ruby] Apply patch 3530439 and finally replace all occurrences of the STR2CSTR() macro
+ with StringValuePtr(). STR2CSTR was deprecated since years and got removed in Ruby 1.9
+
+2012-12-14: kkaempf
+ [Ruby] Applied patches #3530442 and 3530443 to adapt compile and runtime include
+ paths to match Ruby 1.9+
+
+2012-12-14: wsfulton
+ [CFFI] Fix #3161614 - Some string constants are incorrect
+
+2012-12-13: wsfulton
+ [CFFI] Fix #3529690 - Fix incorrect constant names.
+
+2012-12-12: drjoe
+ [R] add fix to finalizer that was missed earlier
+
+2012-12-11: wsfulton
+ [Python] Apply patch #3590522 - fully qualified package paths for Python 3 even if a module is in the
+ same package.
+
+2012-12-08: wsfulton
+ [Python] Bug #3563647 - PyInt_FromSize_t unavailable prior to Python 2.5 for unsigned int types.
+
+2012-12-08: wsfulton
+ [Perl] Fix bug #3571361 - C++ comment in C wrappers.
+
+2012-12-07: wsfulton
+ [C#] Apply patch #3571029 which adds missing director support for const unsigned long long &.
+
+2012-11-28: kwwette
+ [Octave] Simplified module loading: now just the syntax
+ $ example;
+ is accepted, which loads functions globally but constants and variables relative to the current scope.
+ This make module loading behaviour reliably consistent, and reduces problems when loading modules which
+ depend on other modules which may not have been previously loaded.
+
+2012-11-27: wsfulton
+ [cffi] Fix junk output when wrapping single character literal constants.
+
+2012-11-17: wsfulton
+ [Tcl, Modula3] Add missing support for -outdir.
+
+2012-11-17: wsfulton
+ Fix segfaults when using filename paths greater than 1024 characters in length.
+
+2012-11-14: wsfulton
+ [ccache-swig] Apply patch #3586392 from Frederik Deweerdt to fix some error cases - incorrectly using
+ memory after it has been deleted.
+
+2012-11-09: vzeitlin
+ [Python] Fix overflow when passing values greater than LONG_MAX from Python 3 for parameters with unsigned long C type.
+
+2012-11-09: wsfulton
+ Fix some feature matching issues for implicit destructors and implicit constructors and implicit
+ copy constructors added with %copyctor. Previously a feature for these had to be fully qualified
+ in order to match. Now the following will also match:
+
+ %feature("xyz") ~XXX();
+ struct XXX {};
+
+2012-11-09: wsfulton
+ Further consistency in named output typemap lookups for implicit constructors and destructors and
+ implicit copy constructors added with %copyctor. Previously only the fully qualified name was being
+ used, now the unqualified name will also be used. For example, previously:
+
+ example.i:38: Searching for a suitable 'out' typemap for: void Space::More::~More
+ Looking for: void Space::More::~More
+ Looking for: void
+
+ Now the unqualified name is also used:
+
+ example.i:38: Searching for a suitable 'out' typemap for: void Space::More::~More
+ Looking for: void Space::More::~More
+ Looking for: void ~More
+ Looking for: void
+
+2012-11-02: wsfulton
+ Fix some subtle named output typemap lookup misses, the fully qualified name was not always being
+ used for variables, for example:
+
+ struct Glob {
+ int MyVar;
+ };
+
+ Previously the search rules (as shown by -debug-tmsearch) for the getter wrapper were:
+
+ example.i:44: Searching for a suitable 'out' typemap for: int MyVar
+ Looking for: int MyVar
+ Looking for: int
+
+ Now the scope is named correctly:
+
+ example.i:44: Searching for a suitable 'out' typemap for: int Glob::MyVar
+ Looking for: int Glob::MyVar
+ Looking for: int MyVar
+ Looking for: int
+
+2012-10-26: wsfulton
+ Fix director typemap searching so that a typemap specified with a name will be correctly matched. Previously
+ the name was ignored during the typemap search. Applies to the following list of typemaps:
+ directorout, csdirectorout, cstype, imtype, ctype, ddirectorout, dtype, gotype, jtype, jni, javadirectorout.
+
+2012-10-11: wsfulton
+ Most of the special variables available for use in %exception are now also available for expansion in
+ %extend blocks. These are: $name $symname $overname $decl $fulldecl $parentclassname $parentclasssymname, see docs
+ on "Class extension" in SWIGPlus.html. Patch based on submission from Kris Thielemans.
+
+2012-10-10: wsfulton
+ Additional new special variables in %exception are expanded as follows:
+ $parentclassname - The parent class name (if any) for a method.
+ $parentclasssymname - The target language parent class name (if any) for a method.
+
+2012-10-08: iant
+ [Go] Generating Go code now requires using the -intgosize option to
+ indicate the size of the 'int' type in Go. This is because the
+ size of the type is changing from Go 1.0 to Go 1.1 for x86_64.
+
+2012-09-14: wsfulton
+ Add new warning if the empty template instantiation is used as a base class, for example:
+
+ template <typename T> class Base {};
+ %template() Base<int>;
+ class Derived : public Base<int> {};
+
+ gives the following warning instead of silently ignoring the base:
+
+ cpp_inherit.i:52: Warning 401: Base class 'Base< int >' has no name as it is an empty template instantiated with '%template()'. Ignored.
+ cpp_inherit.i:51: Warning 401: The %template directive must be written before 'Base< int >' is used as a base class and be declared with a name.
+
+
+2012-09-11: wsfulton
+ [Java] Fix #3535304 - Direct use of a weak global reference in directors
+ sometimes causing seg faults especially on Android.
+
+2012-09-06: wsfulton
+ [Java] Fix (char *STRING, size_t LENGTH) typemaps to accept NULL string.
+
+2012-08-26: drjoe
+ [R] make ExternalReference slot ref to contain reference
+
+2012-08-26: drjoe
+ [R] fix Examples/Makefile to use C in $(CC) rather than $(CXX)
+
+Version 2.0.8 (20 August 2012)
+==============================
+
+2012-08-15: wsfulton
+ [Perl] Add size_type, value_type, const_reference to the STL containers.
+
+2012-08-15: wsfulton
+ [Python] Add discard and add methods to std::set wrappers so that pyabc.i can be used ensuring
+ MutableSet is a valid abstract base class for std::set. As reported by Alexey Sokolov.
+ Similarly for std::multiset.
+
+2012-08-15: wsfulton
+ [Python] Fix #3541744 - Missing PyInt_FromSize_t calls for Python 3.
+
+2012-08-13: wsfulton
+ [Java] Patch from David Baum to add the assumeoverride feature for Java directors to
+ improve performance when all overridden methods can be assumed to be overridden.
+
+2012-08-05: wsfulton
+ [Python] #3530021 Fix unused variable warning.
+
+2012-08-05: wsfulton
+ [C#] Fix #3536360 - Invalid code sometimes being generated for director methods
+ with many arguments.
+
+2012-08-05: wsfulton
+ [Perl] #3545877 - Don't undefine bool if defined by C99 stdbool.h - problem using
+ Perl 5.16 and later.
+
+2012-08-04: wsfulton
+ Remove incorrect warning (314) about target language keywords which were triggered
+ by using declarations and using directives. For example 'string' is a keyword in C#:
+ namespace std { class string; }
+ using std::string;
+
+2012-07-21: wsfulton
+ Fix display of pointers in various places on 64 bit systems - only 32 bits were being shown.
+
+2012-07-21: wsfulton
+ Fix gdb debugger functions 'swigprint' and 'locswigprint' to display to the gdb output window
+ rather than stdout. This fixes display problems in gdbtui and the ensures the output
+ appears where expected in other gdb based debuggers such as Eclipse CDT.
+
+2012-07-20: kwwette
+ [Octave] segfault-on-exit prevention hack now preserves exit status, and uses C99 _Exit().
+
+2012-07-02: wsfulton
+ Fix Debian bug http://bugs.debian.org/672035, typemap copy failure - regression introduced
+ in swig-2.0.5:
+ %include<stl.i>
+ using std::pair;
+ %template(StrPair) pair<std::string, std::string>;
+
+2012-07-02: wsfulton
+ Fix using declarations combined with using directives with forward class declarations so that
+ types are correctly found in scope for templates. Example:
+
+ namespace Outer2 {
+ namespace Space2 {
+ template<typename T> class Thing2;
+ }
+ }
+ using namespace Outer2;
+ using Space2::Thing2;
+ template<typename T> class Thing2 {};
+ // STILL BROKEN void useit2(Thing2<int> t) {}
+ void useit2a(Outer2::Space2::Thing2<int> t) {}
+ void useit2b(::Outer2::Space2::Thing2<int> t) {}
+ void useit2c(Space2::Thing2<int> t) {}
+ namespace Outer2 {
+ void useit2d(Space2::Thing2<int> t) {}
+ }
+
+ %template(Thing2Int) Thing2<int>;
+
+
+2012-06-30: wsfulton
+ Fix template namespace problems for symbols declared with a forward class declarations, such as:
+
+ namespace Space1 {
+ namespace Space2 {
+ template<typename T> struct YYY;
+ }
+ template<typename T> struct Space2::YYY {
+ T yyy(T h) {
+ return h;
+ }
+ };
+ void testYYY1(Space1::Space2::YYY<int> yy) {}
+ void testYYY2(Space2::YYY<int> yy) {}
+ void testYYY3(::Space1::Space2::YYY<int> yy) {}
+ }
+
+ %template(YYYInt) Space1::Space2::YYY<int>;
+
+2012-06-30: wsfulton
+ Fix namespace problems for symbols declared with a forward class declarations, such as:
+
+ namespace Space1 {
+ namespace Space2 {
+ struct XXX;
+ struct YYY;
+ }
+
+ struct Space2::YYY {};
+ struct Space1::Space2::XXX {};
+
+ void testXXX2(Space2::XXX xx) {}
+ void testYYY2(Space2::YYY yy) {}
+ }
+
+ where xx and yy were not recognised as the proxy classes XXX and YYY.
+
+2012-06-30: wsfulton
+ Fix using declarations combined with using directives with forward class declarations so that
+ types are correctly found in scope.
+
+ namespace Outer2 {
+ namespace Space2 {
+ class Thing2;
+ }
+ }
+ using namespace Outer2;
+ using Space2::Thing2;
+ class Thing2 {};
+ // None of the methods below correctly used the Thing2 proxy class
+ void useit2(Thing2 t) {}
+ void useit2a(Outer2::Space2::Thing2 t) {}
+ void useit2b(::Outer2::Space2::Thing2 t) {}
+ void useit2c(Space2::Thing2 t) {}
+ namespace Outer2 {
+ void useit2d(Space2::Thing2 t) {}
+ }
+
+2012-06-25: wsfulton
+ Fix using declarations combined with using directives so that types are correctly found in scope.
+ Example:
+
+ namespace Outer2 {
+ namespace Space2 {
+ class Thing2 {};
+ }
+ }
+ using namespace Outer2; // using directive
+ using Space2::Thing2; // using declaration
+ void useit2(Thing2 t) {}
+
+ Similarly for templated classes.
+
+2012-05-29: wsfulton
+ Fix #3529601 - seg fault when a protected method has the "director"
+ feature but the parent class does not. Also fix similar problems with
+ the allprotected feature.
+
+2012-05-28: wsfulton
+ Fix seg fault when attempting to warn about an illegal destructor - #3530055, 3530078 and #3530118.
+
+Version 2.0.7 (26 May 2012)
+===========================
+2012-05-26: wsfulton
+ std::string typemap modifications so they can be used with %apply for other string
+ classes.
+
+2012-05-25: wsfulton
+ [Lua] Fixes for -external-runtime to work again.
+
+2012-05-22: szager
+ [python] Disambiguate SWIG_From_unsigned_SS_int and SWIG_From_unsigned_SS_long.
+
+2012-05-18: olly
+ [PHP] Fix getters for template members. (SF#3428833, SF#3528035)
+
+2012-05-14: wsfulton
+ Fix some language's std::map wrappers to recognise difference_type, size_type, key_type
+ and mapped_type.
+
+2012-05-14: kwwette (signed off by xavier98)
+ [Octave] Prevent Octave from seg-faulting at exit when SWIG
+ modules are loaded, due to bugs in Octave's cleanup code:
+ * Wrapping functions now declared with Octave DEFUN_DLD macro,
+ and loaded through Octave's dynamic module loader
+ * Global variables of swigref type are now assigned a new()
+ copy of the swigref class, to prevent double-free errors
+ * SWIG module at-exit cleanup function now created in Octave
+ through eval(), so not dependent on loaded .oct library
+ * For Octave versions 3.1.* to 3.3.*, register C-level at-exit
+ function which terminates Octave immediately (with correct
+ status code) without performing memory cleanup. This function
+ can be controlled with macros in Lib/octave/octruntime.swg
+
+ [Octave] New syntax for determing whether SWIG module should be
+ loaded globally or non-globally. To load module "example" globally,
+ type the module name
+ $ example;
+ as before; to load module non-globally, assign it to a variable:
+ $ example = example;
+ or
+ $ ex = example;
+ for a shorter (local) module name. -global/-noglobal command-line
+ options and module command line are deprecated. Added usage info
+ to module, so typing
+ $ help example
+ or incorrect usage should display proper usage, with examples.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2012-05-12: olly
+ [PHP] Fix memory leak in code generated for a callback. Patch from
+ SF bug #3510806.
+
+2012-05-12: olly
+ [PHP] Avoid using zend_error_noreturn() as it doesn't work with all
+ builds of PHP (SF bug #3166423). Instead we now wrap it in a
+ SWIG_FAIL() function which we annotate as "noreturn" for GCC to
+ avoids warnings. This also reduces the size of the compiled
+ wrapper (e.g. the stripped size is reduced by 6% for Xapian's PHP
+ bindings).
+
+2012-05-11: wsfulton
+ [Java] SF patch #3522855 Fix unintended uninitialised memory access in OUTPUT typemaps.
+
+2012-05-11: wsfulton
+ [Java] SF patch #3522674 Fix possible uninitialised memory access in char **STRING_OUT
+ typemap.
+
+2012-05-11: wsfulton
+ [Java] SF patch #3522611 Fix uninitialised size regression in char **STRING_ARRAY
+ introduced in swig-2.0.6.
+
+2012-05-11: wsfulton
+ SF bug #3525050 - Fix regression introduced in swig-2.0.5 whereby defining one typemap
+ method such as an 'out' typemap may hide another typemap method such as an 'in' typemap -
+ only occurs when the type is a template type where the template parameters are the same
+ via a typedef.
+
+2012-05-10: olly
+ [PHP] Fix the constant typemaps for SWIGTYPE, etc - previously
+ these used the wrong name for renamed constants. Add
+ autodoc_runme.php to the testsuite as a regression test for this.
+
+2012-05-02: ianlancetaylor
+ [Go] Remove compatibility support for gccgo 4.6. Using
+ SWIG with gccgo will now require gccgo 4.7. Using SWIG
+ with the more commonly used gc compiler is unaffected.
+
+2012-05-01: wsfulton
+ Fix generated code for C forward enum declarations in some languages.
+
+Version 2.0.6 (30 April 2012)
+=============================
+
+2012-04-25: wsfulton
+ [Lua] Fix uninitialised variable in SWIGTYPE **OUTPUT typemaps as reported by Jim Anderson.
+
+2012-04-28: wsfulton
+ [Python] Fix compilation errors when wrapping STL containers on Mac OS X and possibly other systems.
+
+2012-04-28: wsfulton
+ [Java] Patch 3521811 from Leo Davis - char **STRING_ARRAY typemaps fixed to handle
+ null pointers.
+
+Version 2.0.5 (19 April 2012)
+=============================
+
+2012-04-14: wsfulton
+ [Lua] Apply patch #3517435 from Miles Bader - prefer to use Lua_pushglobaltable
+
+2012-04-14: wsfulton
+ [Ruby] Apply patch #3517769 from Robin Stocker to fix compile error on MacRuby using RSTRING_PTR.
+
+2012-04-13: wsfulton
+ Apply patch #3511009 from Leif Middelschulte for slightly optimised char * variable wrappers.
+
+2012-04-13: wsfulton
+ [Lua] Apply #3219676 from Shane Liesegang which adds:
+ - support for %factory
+ - a __tostring method
+ - a __disown method
+
+2012-04-13: wsfulton
+ [Xml] Apply #3513569 which adds a catchlist to the xml output.
+
+2012-04-05: olly
+ [Lua] Add support for Lua 5.2 (patch SF#3514593 from Miles Bader)
+
+2012-03-26: xavier98
+ [octave] Apply patch #3425993 from jgillis: add extra logic to the octave_swig_type::dims(void) method: it checks if the user has defined a __dims__ method and uses this in stead of returning (1,1)
+ [octave] Apply patch #3424833 from jgillis: make is_object return true for swig types
+
+2012-03-24: wsfulton
+ [D] Apply #3502431 to fix duplicate symbols in multiple modules.
+
+2012-03-21: wsfulton
+ Fix #3494791 - %$isglobal for %rename matching.
+
+2012-03-20: wsfulton
+ Fix #3487706 and #3391906 - missing stddef.h include for ptrdiff_t when using %import
+ for STL containers and compiling with g++-4.6. An include of stddef.h is now only
+ generated when SWIG generates STL helper templates which require ptrdiff_t. If you
+ were previously relying on "#include <stddef.h>" always being generated when using a
+ %include of an STL header, you may now need to add this in manually.
+
+2012-03-16: wsfulton
+ Apply patch #3392264 from Sebastien Bine to parse (unsigned) long long types in enum value assignment.
+
+2012-03-16: wsfulton
+ Apply patch #3505530 from Karl Wette to allow custom allocators in STL string classes for the UTL languages.
+
+2012-03-13: wsfulton
+ Apply patch #3468362 from Karl Wette to fix %include inside %define.
+
+2012-03-13: wsfulton
+ [Python, Ruby, Octave, R] Fix #3475492 - iterating through std::vector wrappers of enumerations.
+
+2012-02-27: xavier98 (patches from Karl Wette)
+ [Octave] Use -globals . to load global variables in module namespace
+ [Octave] Comment declaration of unimplemented function swig_register_director
+ [Octave] Fix OCTAVE_PATH in octave Makefiles
+ [Octave] Add support for std::list - fix li_std_containers_int test
+ [Octave] Fix imports test
+
+2012-02-16: wsfulton
+ [Java] Make generated support functions in arrays_java.i static so that generated code
+ from multiple instances of SWIG can be compiled and linked together - problem reported by
+ Evan Krause.
+
+2012-01-24: wsfulton
+ Fix crash with bad regex - bug #3474250.
+
+2012-01-24: wsfulton
+ [Python] Add Python stepped slicing support to the STL wrappers (std::vector, std::list).
+ Assigning to a slice, reading a slice and deleting a slice with steps now work.
+ For example:
+
+ %template(vector_i) std::vector<int>
+
+ vi = vector_i(range(10))
+ print list(vi)
+ vi[1:4:2] = [111, 333]
+ print list(vi)
+ del vi[3:10:3]
+ print list(vi)
+ print list(vi[::-1])
+
+ gives (same behaviour as native Python sequences such as list):
+
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ [0, 111, 2, 333, 4, 5, 6, 7, 8, 9]
+ [0, 111, 2, 4, 5, 7, 8]
+ [8, 7, 5, 4, 2, 111, 0]
+
+2012-01-23: klickverbot
+ [D] Correctly annotate function pointers with C linkage.
+ [D] Exception and Error have become blessed names; removed d_exception_name test case.
+
+2012-01-20: wsfulton
+ [Python] Fix some indexing bugs in Python STL wrappers when the index is negative, eg:
+
+ %template(vector_i) std::vector<int>
+
+ iv=vector_i([0,1,2,3,4,5])
+ iv[-7:]
+
+ now returns [0, 1, 2, 3, 4, 5] instead of [5].
+
+ vv[7:9] = [22,33]
+
+ now returns [0, 1, 2, 3, 4, 5, 22, 33] instead of "index out range" error.
+
+ Also fix some segfaults when replacing ranges, eg when il is a std::list wrapper:
+
+ il[0:2] = [11]
+
+2012-01-17: wsfulton
+ [Go] Fix forward class declaration within a class when used as a base.
+
+2012-01-07: wsfulton
+ [C#] Add support for %nspace when using directors.
+
+2012-01-06: wsfulton
+ [Java] Patch #3452560 from Brant Kyser - add support for %nspace when using directors.
+
+2011-12-21: wsfulton
+ The 'directorin' typemap now accepts $1, $2 etc expansions instead of having to use workarounds -
+ $1_name, $2_name etc.
+
+2011-12-20: wsfulton
+ [Java] Add (char *STRING, size_t LENGTH) director typemaps.
+
+2011-12-20: wsfulton
+ [C#, Go, Java, D] Add support for the 'directorargout' typemap.
+
+2011-12-20: wsfulton
+ [Ocaml, Octave, PHP, Python, Ruby] Correct special variables in 'directorargout' typemap.
+ This change will break any 'directorargout' typemaps you may have written. Please change:
+ $result to $1
+ $input to $result
+
+ Also fix the named 'directorargout' DIRECTOROUT typemaps for these languages which didn't
+ previously compile and add in $1, $2 etc expansion.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2011-12-10: talby
+ [perl5] SWIG_error() now gets decorated with perl source file/line number.
+ [perl5] error handling now conforms to public XS api (fixes perl v5.14 issue).
+
+2011-12-10: wsfulton
+ [Android/Java] Fix directors to compile on Android.
+
+ Added documentation and examples for Android.
+
+2011-12-08: vadz
+ Bug fix: Handle methods renamed or ignored in the base class correctly in the derived classes
+ (they could be sometimes mysteriously not renamed or ignored there before).
+
+2011-12-03: klickverbot
+ [D] Fix exception glue code for newer DMD 2 versions.
+ [D] Do not default to 32 bit glue code for DMD anymore.
+ [D] Use stdc.config.c_long/c_ulong to represent C long types.
+
+2011-12-01: szager
+ [python] Fixed bug 3447426: memory leak in vector.__getitem__.
+
+2011-11-30: wsfulton
+ [R] Remove C++ comments from generated C code.
+
+2011-11-27: olly
+ [Python] Fix some warnings when compiling generated wrappers with
+ certain GCC warning options (Debian bug #650246).
+
+2011-11-28: wsfulton
+ Fix #3433541 %typemap(in, numinputs=0) with 10+ arguments.
+
+2011-11-28: olly
+ [Perl] Fix warnings when compiling generated wrappers with certain
+ GCC warning options (Debian bug #436711).
+
+2011-11-28: olly
+ [PHP] Update keyword list to include keywords added in PHP releases up to 5.3.
+
+2011-11-25: wsfulton
+ [C#] Provide an easy way to override the default visibility for the proxy class pointer
+ constructors and getCPtr() method. The visibility is 'internal' by default and if multiple
+ SWIG modules are being used and compiled into different assemblies, then they need to be
+ 'public' in order to use the constructor or getCPtr() method from a different assembly.
+ Use the following macros to change the visibilities in the proxy and type wrapper class:
+
+ SWIG_CSBODY_PROXY(public, public, SWIGTYPE)
+ SWIG_CSBODY_TYPEWRAPPER(public, public, public, SWIGTYPE)
+
+ [Java] Provide an easy way to override the default visibility for the proxy class pointer
+ constructors and getCPtr() method. The visibility is 'protected' by default and if multiple
+ SWIG modules are being used and compiled into different packages, then they need to be
+ 'public' in order to use the constructor or getCPtr() method from a different package.
+ Use the following macros to change the visibilities in the proxy and type wrapper class:
+
+ SWIG_JAVABODY_PROXY(public, public, SWIGTYPE)
+ SWIG_JAVABODY_TYPEWRAPPER(public, public, public, SWIGTYPE)
+
+ The default for Java has changed from public to protected for the proxy classes. Use the
+ SWIG_JAVABODY_PROXY macro above to restore to the previous visibilities.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2011-11-22: szager
+ [python] Bug 3440044: #ifdef out SWIG_Python_NonDynamicSetAttr if -builtin
+ isn't being used, to avoid unnecessary binary incompatibilities between
+ python installations.
+
+2011-11-17: wsfulton
+ Bug fix: Remove root directory from directory search list in Windows.
+
+2011-11-13: wsfulton
+ [Ruby] Apply patch #3421876 from Robin Stocker to fix #3416818 - same class name in
+ different namespaces confusion when using multiple modules.
+
+2011-11-11: wsfulton
+ Fix pcre-build.sh to work with non-compressed tarballs - problem reported by Adrian Blakely.
+
+2011-11-03: wsfulton
+ Expand special variables in typemap warnings, eg:
+
+ %typemap(in, warning="1000:Test warning for 'in' typemap for $1_type $1_name") int "..."
+
+2011-11-01: wsfulton
+ Fix named output typemaps not being used when the symbol uses a qualifier and contains
+ a number, eg:
+
+ %typemap(out) double ABC::m1 "..."
+
+2011-10-24: talby
+ [perl5] SF bug #3423119 - overload dispatch stack corruption fix. Better, but more research
+ is needed on a stable path for tail calls in XS.
+
+ Also, fix for large long longs in 32 bit perl.
+
+2011-10-13: xavier98
+ [octave] Allow Octave modules to be re-loaded after a "clear all".
+
+2011-09-19: wsfulton
+ Fix regression introduced in swig-2.0.1 reported by Teemu Ikonone leading to uncompilable code
+ when using typedef and function pointer references, for example:
+
+ typedef int FN(const int &a, int b);
+ void *typedef_call1(FN *& precallback, FN * postcallback);
+
+2011-09-14: wsfulton
+ [Lua] Patch #3408012 from Raman Gopalan - add support for embedded Lua (eLua)
+ including options for targeting Lua Tiny RAM (LTR).
+
+2011-09-14: wsfulton
+ [C#] Add boost_intrusive_ptr.i library contribution from patch #3401571.
+
+2011-09-13: wsfulton
+ Add warnings for badly named destructors, eg:
+
+ struct KStruct {
+ ~NOT_KStruct() {}
+ };
+
+ cpp_extend_destructors.i:92: Warning 521: Illegal destructor name ~NOT_KStruct. Ignored.
+
+2011-09-13: wsfulton
+ Fix %extend and destructors for templates. The destructor in %extend was not always wrapped,
+ for example:
+
+ %extend FooT {
+ ~FooT() { delete $self; } // was not wrapped as expected
+ };
+ template<class T> class FooT {};
+ %template(FooTi) FooT<int>;
+
+2011-09-13: wsfulton
+ Fix special variables such as "$decl" and "$fulldecl" in destructors to include the ~ character.
+
+2011-09-10: talby
+ [perl5] SF bug #1481958 - Improve range checking for integer types.
+ Enhance li_typemaps_runme.pl
+
+2011-09-08: wsfulton
+ Fix %extend on typedef classes in a namespace using the typedef name, for example:
+ namespace Space {
+ %extend CStruct {
+ ...
+ }
+ typedef struct tagCStruct { ... } CStruct;
+ }
+
+2011-08-31: xavier98
+ [octave] patches from Karl Wette: improvements to module loading behavior;
+ added example of friend operator to operator example; fixed octave panic/crash in 3.0.5;
+ documentation improvements
+
+2011-08-30: szager
+ [python] Bug 3400486, fix error signalling for built-in constructors.
+
+2011-08-26: wsfulton
+ [Go] Fix file/line number display for "gotype" when using typemap debugging options
+ -tmsearch and -tmused.
+
+2011-08-26: wsfulton
+ [C#, D] Fix %callback which was generating uncompilable code.
+
+2011-08-25: wsfulton
+ Fix constructors in named typedef class declarations as reported by Gregory Bronner:
+
+ typedef struct A {
+ A(){} // Constructor which was not accepted by SWIG
+ B(){} // NOT a constructor --illegal, but was accepted by SWIG
+ } B;
+
+ For C code, the fix now results in the use of 'struct A *' instead of just 'B *' in
+ the generated code when wrapping members in A, but ultimately this does not matter, as
+ they are the same thing.
+
+2011-08-23: wsfulton
+ Fix %newobject when used in conjunction with %feature("ref") as reported by Jan Becker. The
+ code from the "ref" feature was not always being generated for the function specified by %newobject.
+ Documentation for "ref" and "unref" moved from Python to the C++ chapter.
+
+2011-08-22: szager
+ [python] Fixed memory leak with --builtin option (bug 3385089).
+
+2011-08-22: wsfulton
+ [Lua] SF patch #3394339 from Torsten Landschoff - new option -nomoduleglobal to disable installing
+ the module table into the global namespace. Require call also returns the module table instead
+ of a string.
+
+2011-08-09: xavier98
+ Fix bug 3387394; Octave patches for 3.4.0 compatibility, etc. (from Karl Wette)
+
+2011-08-04: wsfulton
+ Add in $symname expansion for director methods.
+
+2011-07-29: olly
+ [PHP] Don't generate "return $r;" in cases where $r hasn't been set.
+ This was basically harmless, except it generated a PHP E_NOTICE if
+ the calling code had enabled them.
+
+2011-07-26: wsfulton
+ Fix scoping of forward class declarations nested within a class (for C++). Previously the symbol
+ was incorrectly put into the outer namespace, eg
+
+ namespace std {
+ template<class Key, class T> struct map {
+ class iterator;
+ };
+ }
+
+ iterator was scoped as std::iterator, but now it is correctly std::map<Key, T>::iterator;
+
+ Also fixed is %template and template parameters that are a typedef when the template contains
+ default template parameters, eg:
+
+ namespace Std {
+ template<class Key, class T, class C = int> struct Map {
+ typedef Key key_type;
+ typedef T mapped_type;
+ };
+ }
+ typedef double DOUBLE;
+ %template(MM) Std::Map<int, DOUBLE>;
+
+ All symbols within Map will be resolved correctly, eg key_type and mapped_type no matter if the
+ wrapped code uses Std::Map<int, double> or std::Map<int, DOUBLE> or Std::Map<int, double, int>
+
+ Also fixes bug #3378145 - regression introduced in 2.0.4 - %template using traits.
+
+2011-07-20 szager
+ [python] Fix closure for tp_call slot.
+
+2011-07-16: wsfulton
+ [python] Fix director typemap using PyObject *.
+
+2011-07-13: szager
+ [python] SF patch #3365908 - Add all template parameters to map support code in std_map.i
+
+2011-07-13: szager
+ [python] Fix for bug 3324753: %rename member variables with -builtin.
+
+2011-07-01: wsfulton
+ Fix some scope and symbol lookup problems when template default parameters are being
+ used with typedef. For example:
+
+ template<typename XX, typename TT = SomeType> struct Foo {
+ typedef XX X;
+ typedef TT T;
+ };
+ template<typename TT> struct UsesFoo {
+ void x(typename Foo<TT>::T, typename Foo<TT>::X);
+ };
+
+ Also fixes use of std::vector<int>::size_type for Python as reported by Aubrey Barnard.
+
+2011-06-23: olly
+ [PHP] Fix director code to work when PHP is built with ZTS enabled,
+ which is the standard configuration on Microsoft Windows.
+
+2011-06-21: mutandiz
+ [allegrocl]
+ - various small tweaks and bug fixes.
+ - Avoid name conflicts between smart pointer wrappers and the wrappers for
+ the actual class.
+ - Fix default typemaps for C bindings, which were incorrectly attempting to
+ call non-existent destructors on user-defined types.
+ - New feature, feature:aclmixins, for adding superclass to the foreign class
+ wrappers.
+ - Improve longlong typemaps.
+
+2011-06-19: wsfulton
+ Fix incorrect typemaps being used for a symbol within a templated type, eg:
+ A<int>::value_type would incorrectly use a typemap for type A.
+
+2011-06-18: olly
+ [Tcl] Fix variable declarations in middle of blocks which isn't
+ permitted in C90 (issue probably introduced in 2.0.3 by patch #3224663).
+ Reported by Paul Obermeier in SF#3288586.
+
+2011-06-17: wsfulton
+ [Java] SF #3312505 - slightly easier to wrap char[] or char[ANY] with a Java byte[]
+ using arrays_java.i.
+
+2011-06-13: wsfulton
+ [Ruby, Octave] SF #3310528 Autodoc fixes similar to those described below for Python.
+
+2011-06-10: wsfulton
+ [Python] Few subtle bugfixes in autodoc documentation generation,
+ - Unnamed argument names fix for autodoc levels > 0.
+ - Display of template types fixed for autodoc levels > 1.
+ - Fix SF #3310528 - display of typedef structs for autodoc levels > 1.
+ - Add missing type for self for autodoc levels 1 and 3.
+ - autodoc levels 2 and 3 documented.
+ - Minor tweaks to autodoc style to conform with PEP8.
+
+2011-05-30: olly
+ [PHP] Fix handling of directors when -prefix is used.
+
+2011-05-24: olly
+ [PHP] Fix handling of methods of classes with a virtual base class (SF#3124665).
+
+Version 2.0.4 (21 May 2011)
+===========================
+
+2011-05-19: wsfulton
+ [Guile] Patch #3191625 fixing overloading of integer types.
+
+2011-05-19: wsfulton
+ [Perl] Patch #3260265 fixing overloading of non-primitive types and integers in
+ Perl 5.12 and later.
+
+2011-05-19: wsfulton
+ [Ruby] Fix %import where one of the imported files %include one of the STL include
+ files such as std_vector.i.
+
+2011-05-17: wsfulton
+ [Java] Apply #3289851 from Alan Harder to fix memory leak in directors when checking
+ for pending exceptions.
+
+2011-05-17: wsfulton
+ [Tcl] Apply #3300072 from Christian Delbaere to fix multiple module loading not
+ always sharing variables across modules.
+
+2011-05-16: xavier98
+ [octave] Fix an incompatibility with never versions of Octave. Case on Octave
+ API >= 40 to handle rename of Octave_map to octave_map.
+ [octave] Add support for y.__rop__(x) operators when x.__op__(y) doesn't exist.
+ [octave] Allow global operators to be defined by SWIG-wrapped functions.
+ [octave] Fix several bugs around module namespaces; add -global, -noglobal,
+ -globals <name> command line options to the module.
+
+2011-05-14: wsfulton
+ %varargs when used with a numeric argument used to create an additional argument
+ which was intended to provide a guaranteed sentinel value. This never worked and now
+ the additional argument is not generated.
+
+2011-05-13: wsfulton
+ [python] Additional fixes for python3.2 support.
+
+2011-05-07: szager
+ [python] Fixed PyGetSetDescr for python3.2.
+
+2011-05-05: wsfulton
+ [Lua, Python, Tcl] C/C++ prototypes shown in error message when calling an overloaded
+ method with incorrect arguments improved to show always show fully qualified name
+ and if a const method.
+
+ Also fixed other Lua error messages in generated code which weren't consistently
+ using the fully qualified C++ name - requested by Gedalia Pasternak.
+
+2011-04-29: szager
+ Bug 2635919: Convenience method to convert std::map to a python dict.
+
+2011-04-29: szager
+ [Python] Fixed bug 2811549: return non-const iterators from STL
+ methods begin(), end(), rbegin(), rend().
+
+2011-04-25: szager
+ [Python] Fixed bug 1498929: Access to member fields in map elements
+
+2011-04-23: klickverbot
+ [D] nspace: Correctly generate identifiers for base classes when
+ not in split proxy mode.
+
+2011-04-13: szager
+ Fixed bug 3286333: infinite recursion with mutual 'using namespace' clauses.
+
+2011-04-12: szager
+ Fixed bug 1163440: vararg typemaps.
+
+2011-04-12: szager
+ Fixed bug #3285386: parse error from 'operator T*&()'. Added operator_pointer_ref
+ test case to demonstrate.
+
+2011-04-11: szager
+ [Python] Fixed PyVarObject_HEAD_INIT to eliminate VC++ compiler errors about
+ static initialization of struct members with pointers.
+
+2011-04-11: wsfulton
+ [Tcl] Apply patch #3284326 from Colin McDonald to fix some compiler warnings.
+
+2011-04-11: szager
+ [Python] Fixed PyVarObject_HEAD_INIT to eliminate VC++ compiler errors about
+ static initialization of struct members with pointers.
+
+2011-04-10: klickverbot
+ [D] Fixed wrapping of enums that are type char, for example:
+ enum { X = 'X'; } (this was already in 2.0.3 for C# and Java)
+
+2011-04-10: klickverbot
+ [D] nspace: Fixed referencing types in the root namespace when
+ not in split proxy mode.
+
+2011-04-09: szager
+ [Python] Applied patch #1932484: migrate PyCObject to PyCapsule.
+
+2011-04-09: szager
+ [Python] Added preprocessor guards for python functions PyUnicode_AsWideChar and
+ PySlice_GetIndices, which changed signatures in python3.2.
+
+2011-04-07: wsfulton
+ Fix wrapping of const array typedefs which were generating uncompilable code as
+ reported by Karl Wette.
+
+2011-04-03: szager
+ [Python] Fixed the behavior of %pythonnondynamic to conform to the spec in Lib/pyuserdir.swg.
+
+2011-04-03: szager
+ [Python] Merged in the szager-python-builtin branch, adding the -builtin feature
+ for python. The -builtin option may provide a significant performance gain
+ in python wrappers. For full details and limitations, refer to Doc/Manual/Python.html.
+ A small test suite designed to demonstrate the performance gain is in
+ Examples/python/performance.
+
+2011-04-01: wsfulton
+ Add in missing wrappers for friend functions for some target languages, mostly
+ the non-scripting languages like Java and C#.
+
+Version 2.0.3 (29 March 2011)
+=============================
+
+2011-03-29: wsfulton
+ [R] Apply patch #3239076 from Marie White fixing strings for R >= 2.7.0
+
+2011-03-29: wsfulton
+ [Tcl] Apply patch #3248280 from Christian Delbaere which adds better error messages when
+ the incorrect number or type of arguments are passed to overloaded methods.
+
+2011-03-29: wsfulton
+ [Tcl] Apply patch #3224663 from Christian Delbaere.
+ 1. Fix when function returns a NULL value, a "NULL" command will be created in the Tcl interpreter
+ and calling this command will cause a segmentation fault.
+
+ 2. Previous implementation searches for class methods using a linear search causing performance issues
+ in wrappers for classes with many member functions. The patch adds a method hash table to classes and
+ changes method name lookup to use the hash table instead of doing a linear search.
+
+2011-03-26: wsfulton
+ [C#, Java] SF bug #3195112 - fix wrapping of enums that are type char, for example:
+ enum { X = 'X'; }
+
+2011-03-21: vadz
+ Allow setting PCRE_CFLAGS and PCRE_LIBS during configuration to override the values returned by
+ pcre-config, e.g. to allow using a static version of PCRE library.
+
+2011-03-17: wsfulton
+ [UTL] Add missing headers in generated STL wrappers to fix compilation with gcc-4.6.
+
+2011-03-17: wsfulton
+ Fix regression introduced in swig-2.0.2 where filenames with spaces were not found
+ when used with %include and %import. Reported by Shane Liesegang.
+
+2011-03-15: wsfulton
+ [UTL] Fix overloading when using const char[], problem reported by David Maxwell.
+ Similarly for char[ANY] and const char[ANY].
+
+2011-03-15: wsfulton
+ [C#] Apply patch #3212624 fixing std::map Keys property.
+
+2011-03-14: olly
+ [PHP] Fix handling of overloaded methods/functions where some
+ return void and others don't - whether this worked or not depended
+ on the order they were encountered in (SF#3208299).
+
+2011-03-13: klickverbot
+ [D] Extended support for C++ namespaces (nspace feature).
+
+2011-03-12: olly
+ [PHP] Fix sharing of type information between multiple SWIG-wrapped
+ modules (SF#3202463).
+
+2011-03-09: wsfulton
+ [Python] Fix SF #3194294 - corner case bug when 'NULL' is used as the default value
+ for a primitive type parameter in a method declaration.
+
+2011-03-07: olly
+ [PHP] Don't use zend_error_noreturn() for cases where the function
+ returns void - now this issue can only matter if you have a function
+ or method which is directed and returns non-void.
+
+2011-03-06: olly
+ [PHP] Add casts to the typemaps for long long and unsigned long
+ long to avoid issues when they are used with shorter types via
+ %apply.
+
+2011-03-02: wsfulton
+ Templated smart pointers overloaded with both const and non const operator-> generated uncompilable
+ code when the pointee was a class with either public member variables or static methods.
+ Regression in 2.0.x reported as working in 1.3.40 by xantares on swig-user mailing list.
+
+Version 2.0.2 (20 February 2011)
+================================
+
+2011-02-19: wsfulton
+ [PHP] Add missing INPUT, OUTPUT and INOUT typemaps in the typemaps.i library
+ for primitive reference types as well as signed char * and bool *.
+
+2011-02-19: olly
+ [PHP] Address bug in PHP on some platforms/architectures which
+ results in zend_error_noreturn() not being available using
+ SWIG_ZEND_ERROR_NORETURN which defaults to zend_error_noreturn but
+ can be overridden when building the module by passing
+ -DSWIG_ZEND_ERROR_NORETURN=zend_error to the compiler. This may
+ result in compiler warnings, but should at least allow a module
+ to be built on those platforms/architectures (SF#3166423).
+
+2011-02-18: wsfulton
+ Fix #3184549 - vararg functions and function overloading when using the -fastdispatch option.
+
+2011-02-18: olly
+ [PHP] An overloaded method which can return an object or a
+ primitive type no longer causes SWIG to segfault. Reported by Paul
+ Colby in SF#3168531.
+
+2011-02-18: olly
+ [PHP] Fix invalid erase during iteration of std::map in generated
+ director code. Reported by Cory Bennett in SF#3175820.
+
+2011-02-17: wsfulton
+ Preprocessing now warns if extra tokens appear after #else and #end.
+
+2011-02-16: wsfulton
+ Fix #1653092 Preprocessor does not error out when #elif is missing an expression.
+ This and other cases of missing preprocessor expressions now result in an error.
+
+2011-02-14: wsfulton
+ [Ocaml] Apply patch #3151788 from Joel Reymont. Brings Ocaml support up to date
+ (ver 3.11 and 3.12), including std::string.
+
+2011-02-13: wsfulton
+ [Ruby] Apply patch #3176274 from James Masters - typecheck typemap for time_t.
+
+2011-02-13: wsfulton
+ Apply patch #3171793 from szager - protected director methods failing when -fvirtual is used.
+
+2011-02-13: wsfulton
+ Fix #1927852 - #include directives don't preprocess the file passed to it. The fix is for
+ #include with -importall or -includeall, %include and %import, for example:
+ #define FILENAME "abc.h"
+ %include FILENAME
+
+2011-02-12: wsfulton
+ Fix #1940536, overactive preprocessor which was expanding defined(...) outside of #if and #elif
+ preprocessor directives.
+
+2011-02-05: wsfulton
+ [MzScheme] SF #2942899 Add user supplied documentation to help getting started with MzScheme.
+ Update chapter name to MzScheme/Racket accounting for the rename of MzScheme to Racket.
+
+2011-02-05: wsfulton
+ [C#] SF #3085906 - Possible fix running test-suite on Mac OS X.
+
+2011-02-05: wsfulton
+ SF #3173367 Better information during configure about Boost prerequisite for running
+ the test-suite.
+
+2011-02-05: wsfulton
+ SF #3127633 Fix infinite loop in recursive typedef resolution.
+
+2011-02-04: wsfulton
+ [R] SF #3168676 Fix %rename not working for member variables and methods.
+
+2011-02-04: wsfulton
+ [clisp] SF #3148200 Fix segfault parsing nested unions.
+
+2011-02-01: wsfulton
+ [C#] Directors - a call to a method being defined in the base class, not
+ overridden in a subclass, but again overridden in a class derived from
+ the first subclass was not being dispatched correctly to the most derived class.
+ See director_alternating.i for an example.
+
+2011-02-01: wsfulton
+ [C#, Java] Any 'using' statements in the protected section of a class were previously
+ ignored with director protected (dirprot) mode.
+
+2011-01-30: wsfulton
+ Fix overloading with const pointer reference (SWIGTYPE *const&) parameters for a
+ number of scripting languages.
+
+2011-01-17: wsfulton
+ New warning for smart pointers if only some of the classes in the inheritance
+ chain are marked as smart pointer, eg, %shared_ptr should be used for all classes
+ in an inheritance hierarchy, so this new warning highlights code where this is
+ not the case.
+
+ example.i:12: Warning 520: Base class 'A' of 'B' is not similarly marked as a smart pointer.
+ example.i:16: Warning 520: Derived class 'C' of 'B' is not similarly marked as a smart pointer.
+
+2011-01-14: wsfulton
+ Added some missing multi-argument typemaps: (char *STRING, size_t LENGTH) and
+ (char *STRING, int LENGTH). Documentation for this updated. Java patch from
+ Volker Grabsch.
+
+2011-01-11: iant
+ Require Go version 7077 or later.
+
+2010-12-30: klickverbot
+ [C#, D, Java] Check for collision of parameter names with target
+ language keywords when generating the director glue code.
+
+ The situation in which the generated could would previously be
+ invalid is illustrated in the new 'director_keywords' test case.
+
+2010-12-23: wsfulton
+ [C#] Fix $csinput special variable not being expanded for csvarin typemaps
+ when used for global variables. Reported by Vadim Zeitlin.
+
+2010-12-14: wsfulton
+ Fix $basemangle expansion in array typemaps. For example if type is int *[3],
+ $basemangle expands to _p_int.
+
+2010-12-07: iant
+ Check that we are using a sufficiently new version of the
+ 6g or 8g Go compiler during configure time. If not, disable Go.
+ Minimum version is now 6707.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2010-12-06: wsfulton
+ Fix #3127394 - use of network paths on Windows/MSys.
+
+2010-11-18: klickverbot
+ [D] Added the D language module.
+
+2010-11-12: vadz
+ Fix handling of multiple regex-using %renames attached to the same
+ declaration. For example, now
+
+ %rename("%(regex:/^Set(.*)/put\\1/)s") "";
+ %rename("%(regex:/^Get(.*)/get\\1/)s") "";
+
+ works as expected whereas before only the last anonymous rename was
+ taken into account.
+
+2010-10-17: drjoe
+ [R] Fix failure in overloaded functions which was breaking
+ QuantLib-SWIG
+
+2010-10-14: olly
+ [PHP] Allow compilation on non-conforming Microsoft C++ compilers
+ which don't accept: return function_returning_void();
+ Reported by Frank Vanden Berghen on the SWIG mailing list.
+
+2010-10-12: wsfulton
+ Fix unary scope operator (::) (global scope) regression introduced in 2.0.0, reported by
+ Ben Walker. The mangled symbol names were incorrect, sometimes resulting in types being
+ incorrectly treated as opaque types.
+
+ Also fixes #2958781 and some other type problems due to better typedef resolution, eg
+ std::vector<T *>::value_type didn't resolve to T * when it should have. The mangled type
+ was incorrectly SWIGTYPE_std__vectorT_Test_p_std__allocatorT_Test_p_t_t__value_type and now
+ it is correctly SWIGTYPE_p_Test.
+
+Version 2.0.1 (4 October 2010)
+==============================
+
+2010-10-03: wsfulton
+ Apply patch #3066958 from Mikael Johansson to fix default smart pointer
+ handling when the smart pointer contains both a const and non-const operator->.
+
+2010-10-01: wsfulton
+ Add -pcreversion option to display PCRE version information.
+
+2010-10-01: olly
+ [Ruby] Avoid segfault when a method node has no parentNode
+ (SF#3034054).
+
+2010-10-01: olly
+ [Python] Allow reinitialisation to work with an embedded Python
+ interpreter (patch from Jim Carroll in SF#3075178).
+
+2010-09-28: wsfulton
+ [C#] Apply patch from Tomas Dirvanauskas for std::map wrappers to avoid
+ throwing exceptions with normal usage of iterators.
+
+2010-09-27: olly
+ [Python] Improve error message given when a parameter of the wrong
+ type is passed to an overloaded method (SF#3027355).
+
+2010-09-25: wsfulton
+ Apply SF patch #3075150 - Java directors using static variables in
+ named namespace.
+
+2010-09-24: wsfulton
+ More file and line error/warning reporting fixes where SWIG macros
+ are used within {} braces (where the preprocessor expands macros),
+ for example macros within %inline {...} and %fragment(...) {...}
+ and nested structs.
+
+2010-09-18: wsfulton
+ More file and line error/warning reporting fixes for various inherited
+ class problems.
+
+2010-09-15: wsfulton
+ A much improved debugging of SWIG source experience is now available and
+ documented in the "Debugging SWIG" section in the Doc/Devel/internals.html
+ file, including a swig.dbg support file for the gdb debugger.
+
+2010-09-11: wsfulton
+ Fix incorrect line number reporting in errors/warnings when a macro
+ definition ends with '/' and it is not the end of a C comment.
+
+2010-09-11: wsfulton
+ Fix incorrect line number reporting in errors/warnings after parsing
+ macro invocations with parameters given over more than one line.
+
+2010-09-10: wsfulton
+ Remove extraneous extra line in preprocessed output after including files
+ which would sometimes lead to error/warning messages two lines after the
+ end of the file.
+
+2010-09-10: wsfulton
+ Fix #2149523 - Incorrect line number reporting in errors after parsing macros
+ containing C++ comments.
+
+2010-09-08: olly
+ [PHP] Fix handling of OUTPUT typemaps (Patch from Ryan in SF#3058394).
+
+2010-09-03: wsfulton
+ Fix erroneous line numbers in error messages for macro expansions, for example,
+ the error message now points to instantiation of the macro, ie the last line here:
+
+ #define MACRO2(a, b)
+
+ #define MACRO1(NAME) MACRO2(NAME,2,3)
+
+ MACRO1(abc)
+
+2010-09-02: wsfulton
+ Fix line numbers in error and warning messages for preprocessor messages within
+ %inline, for example:
+
+ %inline %{
+ #define FOOBAR 1
+ #define FOOBAR "hi"
+ %}
+
+2010-09-02: wsfulton
+ Fix line numbers in error and warning messages which were cumulatively one
+ less than they should have been after parsing each %include/%import - bug
+ introduced in swig-1.3.32. Also fix line numbers in error and warning messages
+ when new line characters appear between the %include / %import statement and
+ the filename.
+
+2010-08-30: wsfulton
+ Fix line number and file name reporting for some macro preprocessor warnings.
+ The line number of the macro argument has been corrected and the line number
+ of the start of the macro instead of one past the end of the macro is used.
+ Some examples:
+ file.h:11: Error: Illegal macro argument name '..'
+ file.h:19: Error: Macro 'DUPLICATE' redefined,
+ file.h:15: Error: previous definition of 'DUPLICATE'.
+ file.h:25: Error: Variable-length macro argument must be last parameter
+ file.h:32: Error: Illegal character in macro argument name
+ file.i:37: Error: Macro 'SIT' expects 2 arguments
+
+2010-08-26: wsfulton
+ Fix __LINE__ and __FILE__ expansion reported by Camille Gillot. Mostly this
+ did not work at all. Also fixes SF #2822822.
+
+2010-08-17: wsfulton
+ [Perl] Fix corner case marshalling of doubles - errno was not being correctly
+ set before calling strtod - patch from Justin Vallon - SF Bug #3038936.
+
+2010-08-17: wsfulton
+ Fix make distclean when some of the more obscure languages are detected by
+ configure - fixes from Torsten Landschoff.
+
+2010-07-28: wsfulton
+ Restore configuring out of source for the test-suite since it broke in 1.3.37.
+ As previously, if running 'make check-test-suite' out of source, it needs to be
+ done by invoking configure with a relative path. Invoking configure with an
+ absolute path will not work. Running the full 'make check' still needs to be
+ done in the source tree.
+
+2010-07-16: wsfulton
+ Fix wrapping of function pointers and member function pointers when the function
+ returns by reference.
+
+2010-07-13: vadz
+ Removed support for the old experimental "rxspencer" encoder and
+ "[not]rxsmatch" in %rename (see the 01/16/2006 entry). The new and
+ officially supported "regex" encoder and "[not]regexmatch" checks
+ should be used instead (see the two previous entries). Please
+ replace "%(rxspencer:[pat][subst])s" with "%(regex:/pat/subst/)s"
+ when upgrading. Notice that you will also need to replace the back-
+ references of form "@1" with the more standard "\\1" and may need to
+ adjust your regular expressions syntax as the new regex encoder uses
+ Perl-compatible syntax and not (extended) POSIX syntax as the old one.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2010-07-13: vadz
+ Add "regexmatch", "regextarget" and "notregexmatch" which can be
+ used to apply %rename directives to the declarations matching the
+ specified regular expression only. The first two can be used
+ interchangeably, both of the %renames below do the same thing:
+
+ %rename("$ignore", regexmatch$name="Old$") "";
+ %rename("$ignore", regextarget=1) "Old$";
+
+ (namely ignore the declarations having "Old" suffix).
+
+ "notregexmatch" restricts the match to only the declarations which
+ do not match the regular expression, e.g. here is how to rename to
+ lower case versions all declarations except those consisting from
+ capital letters only:
+
+ %rename("$(lowercase)s", notregexmatch$name="^[A-Z]+$") "";
+
+2010-07-13: vadz
+ Add the new "regex" encoder that can be used in %rename, e.g.
+
+ %rename("regex:/(\\w+)_(.*)/\\2/") "";
+
+ to remove any alphabetical prefix from all identifiers. The syntax
+ of the regular expressions is Perl-like and PCRE library
+ (http://www.pcre.org/) is used to implement this feature but notice
+ that backslashes need to be escaped as usual inside C strings.
+
+ Original patch from Torsten Landschoff.
+
+2010-07-08: wsfulton
+ Fix #3024875 - shared_ptr of classes with non-public destructors. This also fixes
+ the "unref" feature when used on classes with non-public destructors.
+
+2010-06-17: ianlancetaylor
+ [Go] Add the Go language module.
+
+2010-06-10: wsfulton
+ [Lua] Fix SWIG_lua_isnilstring multiply defined when using multiple
+ modules and wrapping strings. Patch from 'Number Cruncher'.
+
+2010-06-10: olly
+ [PHP] Fix directors to correctly call a method with has a
+ different name in PHP to C++ (we were always using the C++ name
+ in this case).
+
+2010-06-03: wsfulton
+ Fix uncompilable code when %rename results in two enum items
+ with the same name. Reported by Vadim Zeitlin.
+
+Version 2.0.0 (2 June 2010)
+===========================
+
+2010-06-02: wsfulton
+ [C#] Fix SWIG_STD_VECTOR_ENHANCED macro used in std::vector to work with
+ types containing commas, for example:
+
+ SWIG_STD_VECTOR_ENHANCED(std::pair< double, std::string >)
+
+2010-06-01: wsfulton
+ Add in std_shared_ptr.i for wrapping std::shared_ptr. Requires the %shared_ptr
+ macro like in the boost_shared_ptr.i library. std::tr1::shared_ptr can also be
+ wrapped if the following macro is defined:
+
+ #define SWIG_SHARED_PTR_SUBNAMESPACE tr1
+ %include <std_shared_ptr.i>
+
+ shared_ptr is also documented in Library.html now.
+
+2010-05-27: wsfulton
+ Add the ability for $typemap special variable macros to call other $typemap
+ special variable macros, for example:
+
+ %typemap(cstype) CC "CC"
+ %typemap(cstype) BB "$typemap(cstype, CC)"
+ %typemap(cstype) AA "$typemap(cstype, BB)"
+ void hah(AA aa);
+
+ This also fixes C# std::vector containers of shared_ptr and %shared_ptr.
+
+ Also added diagnostics for $typemap with -debug-tmsearch, for example, the
+ above displays additional diagnostic lines starting "Containing: ":
+
+ example.i:34: Searching for a suitable 'cstype' typemap for: AA aa
+ Looking for: AA aa
+ Looking for: AA
+ Using: %typemap(cstype) AA
+ Containing: $typemap(cstype, BB)
+ example.i:31: Searching for a suitable 'cstype' typemap for: BB
+ Looking for: BB
+ Using: %typemap(cstype) BB
+ Containing: $typemap(cstype, CC)
+ example.i:29: Searching for a suitable 'cstype' typemap for: CC
+ Looking for: CC
+ Using: %typemap(cstype) CC
+
+2010-05-26: olly
+ Fix %attribute2ref not to produce a syntax error if the last
+ argument (AccessorMethod) is omitted. Patch from David Piepgras
+ in SF#2235756.
+
+2010-05-26: olly
+ [PHP] When using %throws or %catches, SWIG-generated PHP5 wrappers
+ now throw PHP Exception objects instead of giving a PHP error of
+ type E_ERROR.
+
+ This change shouldn't cause incompatibility issues, since you can't
+ set an error handler for E_ERROR, so previously PHP would just exit
+ which also happens for unhandled exceptions. The benefit is you can
+ now catch them if you want to.
+
+ Fixes SF#2545578 and SF#2955522.
+
+2010-05-25: olly
+ [PHP] Add missing directorin typemap for const std::string &.
+ Fixes SF#3006404 reported by t-Legiaw.
+
+2010-05-23: wsfulton
+ [C#] Fix #2957375 - SWIGStringHelper and SWIGExceptionHelper not always being
+ initialized before use in .NET 4 as the classes were not marked beforefieldinit.
+ A static constructor has been added to the intermediary class like this:
+
+ %pragma(csharp) imclasscode=%{
+ static $imclassname() {
+ }
+ %}
+
+ If you had added your own custom static constructor to the intermediary class in
+ the same way as above, you will have to modify your approach to use static variable
+ initialization or define SWIG_CSHARP_NO_IMCLASS_STATIC_CONSTRUCTOR - See csharphead.swg.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2010-05-23: wsfulton
+ Fix #2408232. Improve shared_ptr and intrusive_ptr wrappers for classes in an
+ inheritance hierarchy. No special treatment is needed for derived classes.
+ The proxy class also no longer needs to be specified, it is automatically
+ deduced. The following macros are deprecated:
+ SWIG_SHARED_PTR(PROXYCLASS, TYPE)
+ SWIG_SHARED_PTR_DERIVED(PROXYCLASS, BASECLASSTYPE, TYPE)
+ and have been replaced by
+ %shared_ptr(TYPE)
+ Similarly for intrusive_ptr wrappers, the following macro is deprecated:
+ SWIG_INTRUSIVE_PTR(PROXYCLASS, TYPE)
+ SWIG_INTRUSIVE_PTR_DERIVED(PROXYCLASS, BASECLASSTYPE, TYPE)
+ and have been replaced by
+ %intrusive_ptr(TYPE)
+
+2010-05-21: olly
+ [PHP] Stop generating a bogus line of code in certain constructors.
+ This was mostly harmless, but caused a PHP notice to be issued, if
+ enabled (SF#2985684).
+
+2010-05-18: wsfulton
+ [Java] Fix member pointers on 64 bit platforms.
+
+2010-05-14: wsfulton
+ Fix wrapping of C++ enum boolean values reported by Torsten Landschoff:
+ typedef enum { PLAY = true, STOP = false } play_state;
+
+2010-05-14: olly
+ [PHP] Fix wrapping of global variables which was producing
+ uncompilable code in some cases.
+
+2010-05-12: drjoe
+ [R] Add two more changes from Wil Nolan. Get garbage
+ collection to work. Implement newfree
+
+2010-05-09: drjoe
+ Fix bug reported by Wil Nolan change creation of string so
+ that R 2.7.0+ can use char hashes
+
+2010-05-07: wsfulton
+ Apply patch #2955146 from Sergey Satskiy to fix expressions containing divide by
+ operator in constructor initialization lists.
+
+2010-05-05: wsfulton
+ [R] Memory leak fix handling const std::string & inputs, reported by Will Nolan.
+
+2010-05-01: wsfulton
+ Typemap matching enhancement for non-default typemaps. Previously all
+ qualifiers were stripped in one step, now they are stripped one at a time
+ starting with the left most qualifier. For example, int const*const
+ is first stripped to int *const then int *.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2010-04-25: bhy
+ [Python] Fix #2985655 - broken constructor renaming.
+
+2010-04-14: wsfulton
+ Typemap fragments are now official and documented in Typemaps.html.
+
+2010-04-09: wsfulton
+ [Ruby] Fix #2048064 and #2408020.
+ Apply Ubuntu patch to fix Ruby and std::vector wrappers with -minherit.
+ https://bugs.launchpad.net/ubuntu/+source/swig1.3/+bug/522874
+
+2010-04-09: wsfulton
+ [Mzscheme] Apply Ubuntu patch to fix std::map wrappers:
+ https://bugs.launchpad.net/ubuntu/+source/swig1.3/+bug/203876
+
+2010-04-09: wsfulton
+ [Python] Apply patch #2952374 - fix directors and the -nortti option.
+
+2010-04-09: wsfulton
+ [Lua] Fix #2887254 and #2946032 - SWIG_Lua_typename using wrong stack index.
+
+2010-04-03: wsfulton
+ [Python] Fix exceptions being thrown with the -threads option based on patch from Arto Vuori.
+ Fixes bug #2818499.
+
+2010-04-03: wsfulton
+ Fix Makefile targets: distclean and maintainer-clean
+
+2010-04-02: wsfulton
+ [Lua] Fix char pointers, wchar_t pointers and char arrays so that nil can be passed as a
+ valid value. Bug reported by Gedalia Pasternak.
+
+2010-04-01: wsfulton
+ Numerous subtle typemap matching rule fixes when using the default type. The typemap
+ matching rules are to take a type and find the best default typemap (SWIGTYPE, SWIGTYPE* etc),
+ then look for the next best match by reducing the chosen default type. The type deduction
+ now follows C++ class template partial specialization matching rules.
+
+ Below are the set of changes made showing the default type deduction
+ along with the old reduced type and the new version of the reduced type:
+
+ SWIGTYPE const &[ANY]
+ new: SWIGTYPE const &[]
+ old: SWIGTYPE (&)[ANY]
+
+ SWIGTYPE *const [ANY]
+ new: SWIGTYPE const [ANY]
+ old: SWIGTYPE *[ANY]
+
+ SWIGTYPE const *const [ANY]
+ new: SWIGTYPE *const [ANY]
+ old: SWIGTYPE const *[ANY]
+
+ SWIGTYPE const *const &
+ new: SWIGTYPE *const &
+ old: SWIGTYPE const *&
+
+ SWIGTYPE *const *
+ new: SWIGTYPE const *
+ old: SWIGTYPE **
+
+ SWIGTYPE *const &
+ new: SWIGTYPE const &
+ old: SWIGTYPE *&
+
+ Additionally, a const SWIGTYPE lookup is used now for any constant type. Some examples, where
+ T is some reduced type, eg int, struct Foo:
+
+ T const
+ new: SWIGTYPE const
+ old: SWIGTYPE
+
+ T *const
+ new: SWIGTYPE *const
+ old: SWIGTYPE *
+
+ T const[]
+ new: SWIGTYPE const[]
+ old: SWIGTYPE[]
+
+ enum T const
+ new: enum SWIGTYPE const
+ old: enum SWIGTYPE
+
+ T (*const )[]
+ new: SWIGTYPE (*const )[]
+ old: SWIGTYPE (*)[]
+
+ Reminder: the typemap matching rules can now be seen for any types being wrapped by using
+ either the -debug-tmsearch or -debug-tmused options.
+
+ In practice this leads to some subtle matching rule changes and the majority of users
+ won't notice any changes, except in the prime area of motivation for this change: Improve
+ STL containers of const pointers and passing const pointers by reference. This is fixed
+ because many of the STL containers use a type 'T const&' as parameters and when T is
+ a const pointer, for example, 'K const*', then the full type is 'K const*const&'. This
+ means that the 'SWIGTYPE *const&' typemaps now match when T is either a non-const or
+ const pointer. Furthermore, some target languages incorrectly had 'SWIGTYPE *&' typemaps
+ when these should have been 'SWIGTYPE *const&'. These have been corrected (Java, C#, Lua, PHP).
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2010-03-13: wsfulton
+ [Java] Some very old deprecated pragma warnings are now errors.
+
+2010-03-13: wsfulton
+ Improve handling of file names and directories containing double/multiple path separators.
+
+2010-03-10: mutandiz (Mikel Bancroft)
+ [allegrocl] Use fully qualified symbol name of cl::identity in emit_defun().
+
+2010-03-06: wsfulton
+ [Java] The intermediary JNI class modifiers are now public by default meaning these
+ intermediary low level functions are now accessible by default from outside any package
+ used. The proxy class pointer constructor and getCPtr() methods are also now public.
+ These are needed in order for the nspace option to work without any other mods.
+ The previous default of protected access can be restored using:
+
+ SWIG_JAVABODY_METHODS(protected, protected, SWIGTYPE)
+ %pragma(java) jniclassclassmodifiers = "class"
+
+2010-03-06: wsfulton
+ [C#] Added the nspace feature for C#. Documentation for the nspace feature is now available.
+
+2010-03-04: wsfulton
+ Added the nspace feature. This adds some improved namespace support. Currently only Java
+ is supported for target languages, where C++ namespaces are automatically translated into
+ Java packages. The feature only applies to classes,struct,unions and enums declared within
+ a namespace. Methods and variables declared in namespaces still effectively have their
+ namespaces flattened. Example usage:
+
+ %feature(nspace) Outer::Inner1::Color;
+ %feature(nspace) Outer::Inner2::Color;
+
+ namespace Outer {
+ namespace Inner1 {
+ struct Color {
+ ...
+ };
+ }
+ namespace Inner2 {
+ struct Color {
+ ...
+ };
+ }
+ }
+
+ For Java, the -package option is also required when using the nspace feature. Say
+ we use -package com.myco, the two classes can then be accessed as follows from Java:
+
+ com.myco.Outer.Inner1.Color and com.myco.Outer.Inner2.Color.
+
+2010-02-27: wsfulton
+ [Python] Remove -dirvtable from the optimizations included by -O as it this option
+ currently leads to memory leaks as reported by Johan Blake.
+
+2010-02-27: wsfulton
+ License code changes: SWIG Source is GPL-v3 and library code license is now clearer
+ and is provided under a very permissive license. See https://www.swig.org/legal.html.
+
+2010-02-13: wsfulton
+ [Ruby] A few fixes for compiling under ruby-1.9.x including patch from 'Nibble'.
+
+2010-02-13: wsfulton
+ [Ruby] Apply patch from Patrick Bennett to fix RARRAY_LEN and RARRAY_PTR usage for Ruby 1.9.x
+ used in various STL wrappers.
+
+2010-02-13: wsfulton
+ [C#, Java] Fix incorrect multiply defined symbol name error when an enum item
+ and class name have the same name, as reported by Nathan Krieger. Example:
+
+ class Vector {};
+ namespace Text {
+ enum Preference { Vector };
+ }
+
+ This also fixes other incorrect corner case target language symbol name clashes.
+
+2010-02-11: wsfulton
+ Add the -debug-lsymbols option for displaying the target language layer symbols.
+
+2010-02-09: wsfulton
+ Fix -MM and -MMD options on Windows. They were not omitting files in the SWIG library as
+ they should be.
+
+2010-02-08: wsfulton
+ Fix #1807329 - When Makefile dependencies are being generated using the -M family of options
+ on Windows, the file paths have been corrected to use single backslashes rather than double
+ backslashes as path separators.
+
+2010-02-06: wsfulton
+ Fix #2918902 - language specific files not being generated in correct directory on
+ Windows when using forward slashes for -o, for example:
+ swig -python -c++ -o subdirectory/theinterface_wrap.cpp subdirectory/theinterface.i
+
+2010-02-05: wsfulton
+ Fix #2894405 - assertion when using -xmlout.
+
+2010-01-28: wsfulton
+ Fix typemap matching bug when a templated type has a typemap both specialized and not
+ specialized. For example:
+
+ template<typename T> struct XX { ... };
+ %typemap(in) const XX & "..."
+ %typemap(in) const XX< int > & "..."
+
+ resulted in the 2nd typemap being applied for all T in XX< T >.
+
+2010-01-22: wsfulton
+ Fix #2933129 - typemaps not being found when the unary scope operator (::) is used to denote
+ global scope, the typemap is now used in situations like this:
+
+ struct X {};
+ %typemap(in) const X & "..."
+ void m(const ::X &);
+
+ and this:
+
+ struct X {};
+ %typemap(in) const ::X & "..."
+ void m(const X &);
+
+2010-01-20: wsfulton
+ Fix some unary scope operator (::) denoting global scope problems in the types generated
+ into the C++ layer. Previously the unary scope operator was dropped in the generated code
+ if the type had any sort of qualifier, for example when using pointers, references, like
+ ::foo*, ::foo&, bar< ::foo* >.
+
+2010-01-13: olly
+ [PHP] Add datetime to the list of PHP predefined classes (patch
+ from David Fletcher in SF#2931042).
+
+2010-01-11: wsfulton
+ Slight change to warning, error and diagnostic reporting. The warning number is no
+ longer shown within brackets. This is to help default parsing of warning messages by
+ other tools, vim on Unix in particular.
+
+ Example original display using -Fstandard:
+ example.i:20: Warning(401): Nothing known about base class 'B'. Ignored.
+ New display:
+ example.i:20: Warning 401: Nothing known about base class 'B'. Ignored.
+
+ Also subtle fix to -Fmicrosoft format adding in missing space. Example original display:
+ example.i(20): Warning(401): Nothing known about base class 'Base'. Ignored.
+ New display:
+ example.i(20) : Warning 401: Nothing known about base class 'Base'. Ignored.
+
+2010-01-10: wsfulton
+ Fix a few inconsistencies in reporting of file/line numberings including modifying
+ the overload warnings 509, 512, 516, 474, 475 to now be two line warnings.
+
+2010-01-10: wsfulton
+ Modify -debug-tags output to use standard file name/line reporting so that editors
+ can easily navigate to the appropriate lines.
+ Was typically:
+ . top . include . include (/usr/share/swig/temp/trunk/Lib/swig.swg:312)
+ . top . include . include . include (/usr/share/swig/temp/trunk/Lib/swigwarnings.swg:39)
+ now:
+ /usr/share/swig/temp/trunk/Lib/swig.swg:312: . top . include . include
+ /usr/share/swig/temp/trunk/Lib/swigwarnings.swg:39: . top . include . include . include
+
+2010-01-03: wsfulton
+ Fix missing file/line numbers for typemap warnings and in output from the
+ -debug-tmsearch/-debug-tmused options.
+
+2010-01-03: wsfulton
+ Add typemaps used debugging option (-debug-tmused). When used each line displays
+ the typemap used for each type for which code is being generated including the file
+ and line number related to the type. This is effectively a condensed form of the
+ -debug-tmsearch option. Documented in Typemaps.html.
+
+2009-12-23: wsfulton
+ Fix for %javaexception and directors so that all the appropriate throws clauses
+ are generated. Problem reported by Peter Greenwood.
+
+2009-12-20: wsfulton
+ Add -debug-tmsearch option for debugging the typemap pattern matching rules.
+ Documented in Typemaps.html.
+
+2009-12-12: wsfulton
+ [Octave] Remove the -api option and use the new OCTAVE_API_VERSION_NUMBER
+ macro provided in the octave headers for determining the api version instead.
+
+2009-12-04: olly
+ [Ruby] Improve support for Ruby 1.9 under GCC. Addresses part of
+ SF#2859614.
+
+2009-12-04: olly
+ Fix handling of modulo operator (%) in constant expressions
+ (SF#2818562).
+
+2009-12-04: olly
+ [PHP] "empty" is a reserved word in PHP, so rename empty() method
+ on STL classes to "is_empty()" (previously this was automatically
+ renamed to "c_empty()").
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2009-12-03: olly
+ [PHP] Add typemaps for long long and unsigned long long, and for
+ pointer to method.
+
+2009-12-02: olly
+ [PHP] Fix warning and rename of reserved class name to be case
+ insensitive.
+
+2009-12-01: wsfulton
+ Revert support for %extend and memberin typemaps added in swig-1.3.39. The
+ memberin typemaps are ignored again for member variables within a %extend block.
+ Documentation inconsistency reported by Torsten Landschoff.
+
+2009-11-29: wsfulton
+ [Java, C#] Fix generated quoting when using %javaconst(1)/%csconst(1) for
+ static const char member variables.
+
+ %javaconst(1) A;
+ %csconst(1) A;
+ struct X {
+ static const char A = 'A';
+ };
+
+2009-11-26: wsfulton
+ [Java, C#] Fix %javaconst(1)/%csconst(1) for static const member variables to
+ use the actual constant value if it is specified, rather than the C++ code to
+ access the member.
+
+ %javaconst(1) EN;
+ %csconst(1) EN;
+ struct X {
+ static const int EN = 2;
+ };
+
+2009-11-23: wsfulton
+ C++ nested typedef classes can now be handled too, for example:
+ struct Outer {
+ typedef Foo { } FooTypedef1, FooTypedef2;
+ };
+
+2009-11-18: wsfulton
+ The wrappers for C nested structs are now generated in the same order as declared
+ in the parsed code.
+
+2009-11-18: wsfulton
+ Fix #491476 - multiple declarations of nested structs, for example:
+ struct Outer {
+ struct {
+ int val;
+ } inner1, inner2, *inner3, inner4[1];
+ } outer;
+
+2009-11-17: wsfulton
+ Fix parsing of enum declaration and initialization, for example:
+
+ enum ABC {
+ a,
+ b,
+ c
+ } A = a, *pC = &C, array[3] = {a, b, c};
+
+2009-11-17: wsfulton
+ Fix parsing of struct declaration and initialization, for example:
+
+ struct S {
+ int x;
+ } instance = { 10 };
+
+2009-11-15: wsfulton
+ Fix #1960977 - Syntax error parsing derived nested class declaration and member
+ variable instance.
+
+2009-11-14: wsfulton
+ Fix #2310483 - function pointer typedef within extern "C" block.
+
+2009-11-13: wsfulton
+ Fix usage of nested template classes within templated classes so that compilable code
+ is generated.
+
+2009-11-13: olly
+ [php] Fix place where class prefix (as specified with -prefix)
+ wasn't being used. Patch from gverbruggen in SF#2892647.
+
+2009-11-12: wsfulton
+ Fix usage of nested template classes so that compilable code is generated - the nested
+ template class is now treated like a normal nested classes, that is, as an opaque type
+ unless the nestedworkaround feature is used.
+
+2009-11-12: wsfulton
+ Replace SWIGWARN_PARSE_NESTED_CLASS with SWIGWARN_PARSE_NAMED_NESTED_CLASS and
+ SWIGWARN_PARSE_UNNAMED_NESTED_CLASS for named and unnamed nested classes respectively.
+
+ Named nested class ignored warnings can now be suppressed by name using %warnfilter, eg:
+
+ %warnfilter(SWIGWARN_PARSE_NAMED_NESTED_CLASS) Outer::Inner;
+
+ but clearly unnamed nested classes cannot and the global suppression is still required, eg:
+
+ #pragma SWIG nowarn=SWIGWARN_PARSE_UNNAMED_NESTED_CLASS
+
+2009-11-11: wsfulton
+ Added the nestedworkaround feature as a way to use the full functionality of a nested class
+ (C++ mode only). It removes the nested class from SWIG's type information so it is as if SWIG
+ had never parsed the nested class. The documented nested class workarounds using a global
+ fake class stopped working when SWIG treated the nested class as an opaque pointer, and
+ this feature reverts this behaviour. The documentation has been updated with details of how
+ to use and implement it, see the "Nested classes" section in SWIGPlus.html.
+
+2009-11-11: wsfulton
+ There were a number of C++ cases where nested classes/structs/unions were being handled
+ as if C code was being parsed which would oftentimes lead to uncompilable code as an
+ attempt was made to wrap the nested structs like it is documented for C code. Now all
+ nested structs/classes/unions are ignored in C++ mode, as was always documented. However,
+ there is an improvement as usage of nested structs/classes/unions is now always treated
+ as an opaque type by default, resulting in generated code that should always compile.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2009-11-09: drjoe
+ Fix R for -fcompact and add std_map.i
+
+2009-11-08: wsfulton
+ Fix inconsistency for nested structs/unions/classes. Uncompilable code was being
+ generated when inner struct and union declarations were used as types within the
+ inner struct. The inner struct/union is now treated as a forward declaration making the
+ behaviour the same as an inner class. (C++ code), eg:
+
+ struct Outer {
+ struct InnerStruct { int x; };
+ InnerStruct* getInnerStruct();
+ };
+
+2009-11-08: wsfulton
+ Ignored nested class/struct warnings now display the name of the ignored class/struct.
+
+2009-11-07: wsfulton
+ Bug #1514681 - Fix nested template classes within a namespace generated uncompilable
+ code and introduced strange side effects to other wrapper code especially code
+ after the nested template class. Note that nested template classes are still ignored.
+
+2009-11-07: wsfulton
+ Add new debug options:
+ -debug-symtabs - Display symbol tables information
+ -debug-symbols - Display target language symbols in the symbol tables
+ -debug-csymbols - Display C symbols in the symbol tables
+
+2009-11-03: wsfulton
+ Fix some usage of unary scope operator (::) denoting global scope, for example:
+
+ namespace AA { /* ... */ }
+ using namespace ::AA;
+
+ and bug #1816802 - SwigValueWrapper should be used:
+
+ struct CC {
+ CC(int); // no default constructor
+ };
+ ::CC x();
+
+ and in template parameter specializations:
+
+ struct S {};
+ template <typename T> struct X { void a() {} };
+ template <> struct X<S> { void b() {} };
+ %template(MyTConcrete) X< ::S >;
+
+ plus probably some other corner case usage of ::.
+
+2009-11-02: olly
+ [Python] Fix potential memory leak in initialisation code for the
+ generated module.
+
+2009-10-23: wsfulton
+ Fix seg fault when using a named nested template instantiation using %template(name)
+ within a class. A warning that these are not supported is now issued plus processing
+ continues as if no name was given.
+
+2009-10-20: wsfulton
+ [Python] Fix std::vector<const T*>. This would previously compile, but not run correctly.
+
+2009-10-20: wsfulton
+ Fixed previously fairly poor template partial specialization and explicit
+ specialization support. Numerous bugs in this area have been fixed including:
+
+ - Template argument deduction implemented for template type arguments, eg this now
+ works:
+ template<typename T> class X {};
+ template<typename T> class X<T *> {};
+ %template(X1) X<const int *>; // Chooses T * specialization
+
+ and more complex cases with multiple parameters and a mix of template argument
+ deduction and explicitly specialised parameters, eg:
+ template <typename T1, typename T2> struct TwoParm { void a() {} };
+ template <typename T1> struct TwoParm<T1 *, int *> { void e() {} };
+ %template(E) TwoParm<int **, int *>;
+
+ Note that the primary template must now be in scope, like in C++, when
+ an explicit or partial specialization is instantiated with %template.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2009-09-14: wsfulton
+ [C#] Add %csattributes for adding C# attributes to enum values, see docs for example.
+
+2009-09-11: wsfulton
+ Fix memmove regression in cdata.i as reported by Adriaan Renting.
+
+2009-09-07: wsfulton
+ Fix constant expressions containing <= or >=.
+
+2009-09-02: wsfulton
+ The following operators in constant expressions now result in type bool for C++
+ wrappers and remain as type int for C wrappers, as per each standard:
+
+ && || == != < > <= >= (Actually the last 4 are still broken). For example:
+
+ #define A 10
+ #define B 10
+ #define A_EQ_B A == B // now wrapped as type bool for C++
+ #define A_AND_B A && B // now wrapped as type bool for C++
+
+2009-09-02: wsfulton
+ Fix #2845746. true and false are now recognised keywords (only when wrapping C++).
+ Constants such as the following are now wrapped (as type bool):
+ #define FOO true
+ #define BAR FOO && false
+
+Version 1.3.40 (18 August 2009)
+===============================
+
+2009-08-17: olly
+ [Perl] Add "#undef do_exec" to our clean up of Perl global
+ namespace pollution.
+
+2009-08-17: olly
+ [PHP] Fix to wrap a resource returned by __get() in a PHP object (SF#2549217).
+
+2009-08-17: wsfulton
+ Fix #2797485 After doing a 'make clean', install fails if yodl2man or yodl2html
+ is not available.
+
+2009-08-16: wsfulton
+ [Octave] Caught exceptions display the type of the C++ exception instead of the
+ generic "c++-side threw an exception" message.
+
+2009-08-16: wsfulton
+ [Java] When %catches is used, fix so that any classes specified in the "throws"
+ attribute of the "throws" typemap are generated into the Java method's throws clause.
+
+2009-08-16: wsfulton
+ [C#] Fix exception handling when %catches is used, reported by Juan Manuel Alvarez.
+
+2009-08-15: wsfulton
+ Fix %template seg fault on some cases of overloading the templated method.
+ Bug reported by Jan Kupec.
+
+2009-08-15: wsfulton
+ [Ruby] Add numerous missing wrapped methods for std::vector<bool> specialization
+ as reported by Youssef Jones.
+
+2009-08-14: wsfulton
+ [Perl] Add SWIG_ConvertPtrAndOwn() method into the runtime for smart pointer
+ memory ownership control. shared_ptr support still to be added. Patch from
+ David Fletcher.
+
+2009-08-14: olly
+ [PHP] PHP5 now wraps static member variables as documented.
+
+2009-08-14: olly
+ [PHP] Update the PHP "class" example to work with PHP5 and use
+ modern wrapping features.
+
+2009-08-13: wsfulton
+ [PHP] std::vector wrappers overhaul. They no longer require the
+ specialize_std_vector() macro. Added wrappers for capacity() and reserve().
+
+2009-08-13: wsfulton
+ [PHP] Add const reference typemaps. const reference primitive types are
+ now passed by value rather than pointer like the other target languages.
+ Fixes SF#2524029.
+
+2009-08-08: wsfulton
+ [Python] More user friendly AttributeError is raised when there are
+ no constructors generated for the proxy class in the event that the
+ class is abstract - the error message is now
+ "No constructor defined - class is abstract" whereas if there are no
+ public constructors for any other reason and the class is not abstract,
+ the message remains
+ "No constructor defined".
+ [tcl] Similarly for tcl when using -itcl.
+
+2009-08-04: olly
+ [PHP] Fix generated code to work with PHP 5.3.
+
+2009-08-04: vmiklos
+ [PHP] Various mathematical functions (which would conflict
+ with the built-in PHP ones) are now automatically handled by
+ adding a 'c_' prefix.
+
+2009-08-03: wsfulton
+ [C#] The std::vector<T> implementation is improved and now uses $typemap such
+ that the proxy class for T no longer has to be specified in some macros
+ for correct C# compilation; the following macros are deprecated, where
+ CSTYPE was the C# type for the C++ class CTYPE:
+
+ SWIG_STD_VECTOR_SPECIALIZE_MINIMUM(CSTYPE, CTYPE)
+ usage should be removed altogether
+
+ SWIG_STD_VECTOR_SPECIALIZE(CSTYPE, CTYPE)
+ should be replaced with:
+ SWIG_STD_VECTOR_ENHANCED(CTYPE)
+
+ Some more details in csharp/std_vector.i
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2009-07-31: olly
+ [Python] Fix indentation so that we give a useful error if the
+ module can't be loaded. Patch from Gaetan Lehmann in SF#2829853.
+
+2009-07-29: wsfulton
+ Add $typemap(method, typelist) special variable macro. This allows
+ the contents of a typemap to be inserted within another typemap.
+ Fully documented in Typemaps.html.
+
+2009-07-29: vmiklos
+ [PHP] Static member variables are now prefixed with the
+ class name. This allows static member variables with the
+ same name in different classes.
+
+2009-07-29: olly
+ [Python] Add missing locks to std::map wrappers. Patch from
+ Paul Hampson in SF#2813836.
+
+2009-07-29: olly
+ [PHP] Fix memory leak in PHP OUTPUT typemaps. Reported by Hitoshi
+ Amano in SF#2826322.
+
+2009-07-29: olly
+ [PHP] Fix memory leak in PHP resource destructor for classes
+ without a destructor and non-class types. Patch from Hitoshi Amano
+ in SF#2825303.
+
+2009-07-28: olly
+ [PHP] Update warnings about clashes between identifiers and PHP
+ keywords and automatic renaming to work with the PHP5 class
+ wrappers. Fixes SF#1613679.
+
+2009-07-28: vmiklos
+ [PHP] If a member function is not public but it has a base
+ which is public, then now a warning is issued and the member
+ function will be public, as PHP requires this.
+
+2009-07-21: vmiklos
+ [PHP] Director support added.
+
+2009-07-15: olly
+ [Perl] Don't specify Perl prototype "()" for a constructor with a
+ different name to the class, as such constructors can still take
+ parameters.
+
+2009-07-12: xavier98
+ [Octave] Add support for Octave 3.2 API
+
+2009-07-05: olly
+ [PHP] Update the list of PHP keywords - "cfunction" is no longer a
+ keyword in PHP5 and PHP 5.3 added "goto", "namespace", "__DIR__",
+ and "__NAMESPACE__".
+
+2009-07-03: olly
+ [Tcl] To complement USE_TCL_STUBS, add support for USE_TK_STUBS
+ and SWIG_TCL_STUBS_VERSION. Document all three in the Tcl chapter
+ of the manual. Based on patch from SF#2810380 by Christian
+ Gollwitzer.
+
+2009-07-02: vmiklos
+ [PHP] Added factory.i for PHP, see the li_factory testcase
+ for more info on how to use it.
+
+2009-07-02: wsfulton
+ Fix -Wallkw option as reported by Solomon Gibbs.
+
+2009-07-02: wsfulton
+ Fix syntax error when a nested struct contains a comment containing a * followed
+ eventually by a /. Regression from 1.3.37, reported by Solomon Gibbs.
+
+2009-07-01: vmiklos
+ [PHP] Unknown properties are no longer ignored in proxy
+ classes.
+
+2009-07-01: vmiklos
+ [PHP] Fixed %newobject behaviour, previously any method
+ marked with %newobject was handled as a constructor.
+
+2009-06-30: olly
+ [Ruby] Undefine close and connect macros defined by Ruby API
+ headers as we don't need them and they can clash with C++ methods
+ being wrapped. Patch from Vit Ondruch in SF#2814430.
+
+2009-06-26: olly
+ [Ruby] Fix to handle FIXNUM values greater than MAXINT passed for a
+ double parameter.
+
+2009-06-24: wsfulton
+ Fix wrapping methods with default arguments and the compactdefaultargs feature
+ where a class is passed by value and is assigned a default value. The SwigValueWrapper
+ template workaround for a missing default constructor is no longer used as the code
+ generated does not call the default constructor.
+
+2009-06-16: wsfulton
+ [Java,C#] Fix enum marshalling when %ignore is used on one of the enum items.
+ Incorrect enum values were being passed to the C++ layer or compilation errors resulted.
+
+2009-06-02: talby
+ [Perl] Resolved reference.i overload support problem
+ identified by John Potowsky.
+
+2009-05-26: wsfulton
+ [C#] Improved std::map wrappers based on patch from Yuval Baror. The C# proxy
+ now implements System.Collections.Generic.IDictionary<>.
+
+ These std:map wrappers have a non-backwards compatible overhaul to make them
+ like a .NET IDictionary. Some method names have changed as following:
+ set -> setitem (use this[] property now)
+ get -> getitem (use this[] property now)
+ has_key -> ContainsKey
+ del -> Remove
+ clear -> Clear
+
+ The following macros used for std::map wrappers are deprecated and will no longer work:
+ specialize_std_map_on_key
+ specialize_std_map_on_value
+ specialize_std_map_on_both
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2009-05-20: vmiklos
+ [PHP] Add the 'thisown' member to classes. The usage of it
+ is the same as the Python thisown one: it's 1 by default and
+ you can set it to 0 if you want to prevent freeing it. (For
+ example to prevent a double free.)
+
+2009-05-14: bhy
+ [Python] Fix the wrong pointer value returned by SwigPyObject_repr().
+
+2009-05-13: mutandiz (Mikel Bancroft)
+ [allegrocl] Minor tweak when wrapping in -nocwrap mode.
+
+2009-05-11: wsfulton
+ [C#] Improved std::vector wrappers on the C# proxy side from Yuval Baror. These
+ implement IList<> instead of IEnumerable<> where possible.
+
+2009-04-29: wsfulton
+ [Java, C#] Add the 'notderived' attribute to the javabase and csbase typemaps.
+ When this attribute is set, the typemap will not apply to classes that are derived
+ from a C++ base class, eg
+ %typemap(csbase, notderived="1") SWIGTYPE "CommonBase"
+
+2009-04-29: olly
+ [Python] Don't attempt to acquire the GIL in situations where we
+ know that it will already be locked. This avoids some dead-locks
+ with mod_python (due to mod_python bugs which are apparently
+ unlikely to ever be fixed), and results in smaller wrappers which
+ run a little faster (in tests with Xapian on x86-64 Ubuntu 9.04,
+ the stripped wrapper library was 11% smaller and ran 2.7% faster).
+
+2009-04-21: wsfulton
+ [C#] Fix #2753469 - bool &OUTPUT and bool *OUTPUT typemaps initialisation.
+
+2009-04-09: wsfulton
+ Fix #2746858 - C macro expression using floating point numbers
+
+2009-03-30: olly
+ [PHP] The default out typemap for char[ANY] now returns the string up to a
+ zero byte, or the end of the array if there is no zero byte. This
+ is the same as Python does, and seems more generally useful than
+ the previous behaviour of returning the whole contents of the array
+ including any zero bytes. If you want the old behaviour, you can provide
+ your own typemap to do this:
+
+ %typemap(out) char [ANY]
+ %{
+ RETVAL_STRINGL($1, $1_dim0, 1);
+ %}
+
+Version 1.3.39 (21 March 2009)
+==============================
+
+2009-03-19: bhy
+ [Python] Fix the memory leak related to Python 3 unicode and C char* conversion,
+ which can be shown in the following example before this fix:
+
+ from li_cstring import *
+ i=0
+ while True:
+ i += 1
+ n = str(i)*10
+ test3(n)
+
+ This fix affected SWIG_AsCharPtrAndSize() so you cannot call this function with
+ a null alloc and non-null cptr argument in Python 3, otherwise a runtime error
+ will be raised.
+
+2009-03-18: wsfulton
+ [C#] std::vector<T> wrapper improvements for .NET 2 and also providing the
+ necessary machinery to use the std::vector<T> wrappers with more advanced features such
+ as LINQ - the C# proxy class now derives from IEnumerable<>. The default is now to
+ generate code requiring .NET 2 as a minimum, although the C# code can be compiled
+ for .NET 1 by defining the SWIG_DOTNET_1 C# preprocessor constant. See the
+ std_vector.i file for more details.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2009-03-12: wsfulton
+ [Ruby] Fix #2676738 SWIG generated symbol name clashes.
+
+2009-03-01: bhy
+ [Python] Some fixes for Python 3.0.1 and higher support. In 3.0.1, the C API function
+ PyObject_Compare is removed, so PyObject_RichCompareBool is used for replacement.
+ Struct initilization of SwigPyObject and SwigPyObject_as_number changed to reflect
+ the drop of tp_compare and nb_long.
+
+2009-03-01: bhy
+ [Python] Fix SF#2583160. Now the importer in Python shadow wrapper take care of the
+ case that module already imported at other place.
+
+2009-02-28: bhy
+ [Python] Fix SF#2637352. Move struct declaration of SWIG_module in pyinit.swg before
+ the method calls, since some C compiler don't allow declaration in middle of function
+ body.
+
+2009-02-21: wsfulton
+ [Allegrocl] Fix seg fault wrapping some constant variable (%constant) types.
+
+2009-02-20: wsfulton
+ [CFFI] Fix seg faults when for %extend and using statements.
+
+2009-02-20: wsfulton
+ Fix SF #2605955: -co option which broke in 1.3.37.
+
+2009-02-20: wsfulton
+ New %insert("begin") section added. Also can be used as %begin. This is a new
+ code section reserved entirely for users and the code within the section is generated
+ at the top of the C/C++ wrapper file and so provides a means to put custom code
+ into the wrapper file before anything else that SWIG generates.
+
+2009-02-17: wsfulton
+ 'make clean-test-suite' will now run clean on ALL languages. Previously it only
+ ran the correctly configured languages. This way it is now possible to clean up
+ properly after running 'make partialcheck-test-suite'.
+
+2009-02-14: wsfulton
+ Extend attribute library support for structs/classes and the accessor functions use
+ pass/return by value semantics. Two new macros are available and usage is identical
+ to %attribute. These are %attributeval for structs/classes and %attributestring for
+ string classes, like std::string. See attribute.swg for more details.
+
+2009-02-13: wsfulton
+ Add support for %extend and memberin typemaps. Previously the memberin typemaps were
+ ignored for member variables within a %extend block.
+
+2009-02-12: wsfulton
+ Remove unnecessary temporary variable when wrapping return values that are references.
+ Example of generated code for wrapping:
+
+ struct XYZ {
+ std::string& refReturn();
+ };
+
+ used to be:
+
+ std::string *result = 0 ;
+ ...
+ {
+ std::string &_result_ref = (arg1)->refReturn();
+ result = (std::string *) &_result_ref;
+ }
+
+ Now it is:
+
+ std::string *result = 0 ;
+ ...
+ result = (std::string *) &(arg1)->refReturn();
+
+2009-02-08: bhy
+ Change the SIZE mapped by %pybuffer_mutable_binary and %pybuffer_binary in pybuffer.i from
+ the length of the buffer to the number of items in the buffer.
+
+2009-02-08: wsfulton
+ Fix %feature not working for conversion operators, reported by Matt Sprague, for example:
+ %feature("cs:methodmodifiers") operator bool "protected";
+
+2009-02-07: wsfulton
+ [MzScheme] Apply #2081967 configure changes for examples to build with recent PLT versions.
+ Also fixes Makefile errors building SWIG executable when mzscheme package is installed
+ (version 3.72 approx and later).
+
+2009-02-04: talby
+ [Perl] Fix SF#2564192 reported by David Kolovratnk.
+ SWIG_AsCharPtrAndSize() now handles "get" magic.
+
+Version 1.3.38 (31 January 2009)
+================================
+
+2009-01-31: bhy
+ [Python] Fix SF#2552488 reported by Gaetan Lehmann. Now %pythonprepend
+ and %pythonappend have correct indentation.
+
+2009-01-31: bhy
+ [Python] Fix SF#2552048 reported by Gaetan Lehmann. The parameter list
+ of static member function in generated proxy code should not have the
+ 'self' parameter.
+
+2009-01-29: wsfulton
+ Fix regression introduced in 1.3.37 where the default output directory
+ for target language specific files (in the absence of -outdir) was no
+ longer the same directory as the generated c/c++ file.
+
+2009-01-28: wsfulton
+ [Java, C#] Fix proxy class not being used when the global scope operator
+ was used for parameters passed by value. Reported by David Piepgrass.
+
+2009-01-15: wsfulton
+ [Perl] Fix seg fault when running with -v option, reported by John Ky.
+
+Version 1.3.37 (13 January 2009)
+================================
+
+2009-01-13: mgossage
+ [Lua] Added contract support for requiring that unsigned numbers are >=0
+ Rewrote much of Examples/Lua/embed3.
+ Added a lot to the Lua documentation.
+
+2009-01-13: wsfulton
+ Fix compilation error when using directors on protected virtual overloaded
+ methods reported by Sam Hendley.
+
+2009-01-12: drjoe
+ [R] Fixed handling of integer arrays
+
+2009-01-10: drjoe
+ [R] Fix integer handling in r to deal correctly with signed
+ and unsigned issues
+
+2009-01-10: wsfulton
+ Patch #1992756 from Colin McDonald - %contract not working for classes
+ in namespace
+
+2009-01-05: olly
+ Mark SWIGPERL5, SWIGPHP5, and SWIGTCL8 as deprecated in the source
+ code and remove documentation of them.
+
+2008-12-30: wsfulton
+ Bug #2430756. All the languages now define a macro in the generated C/C++
+ wrapper file indicating which language is being wrapped. The macro name is the
+ same as those defined when SWIG is run, eg SWIGJAVA, SWIGOCTAVE, SWIGCSHARP etc
+ and are listed in the "Conditional Compilation" section in the documentation.
+
+2008-12-23: wsfulton
+ [Java] Fix #2153773 - %nojavaexception was clearing the exception feature
+ instead of disabling it. Clearing checked Java exceptions also didn't work.
+ The new %clearjavaexception can be used for clearing the exception feature.
+
+2008-12-22: wsfulton
+ Fix #2432801 - Make SwigValueWrapper exception safe for when copy constructors
+ throw exceptions.
+
+2008-12-21: wsfulton
+ Apply patch #2440046 which fixes possible seg faults for member and global
+ variable char arrays when the strings are larger than the string array size.
+
+2008-12-20: wsfulton
+ The ccache compiler cache has been adapted to work with SWIG and
+ named ccache-swig. It now works with C/C++ compilers as well as SWIG
+ and can result in impressive speedups when used to recompile unchanged
+ code with either a C/C++ compiler or SWIG. Documentation is in CCache.html
+ or the installed ccache-swig man page.
+
+2008-12-12: wsfulton
+ Apply patch from Kalyanov Dmitry which fixes parsing of nested structs
+ containing comments.
+
+2008-12-12: wsfulton
+ Fix error message in some nested struct and %inline parsing error situations
+ such as unterminated strings and comments.
+
+2008-12-07: olly
+ [PHP] Fix warnings when compiling generated wrapper with GCC 4.3.
+
+2008-12-06: wsfulton
+ [PHP] Deprecate %pragma(php4). Please use %pragma(php) instead.
+ The following two warnings have been renamed:
+ WARN_PHP4_MULTIPLE_INHERITANCE -> WARN_PHP_MULTIPLE_INHERITANCE
+ WARN_PHP4_UNKNOWN_PRAGMA -> WARN_PHP_UNKNOWN_PRAGMA
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2008-12-04: bhy
+ [Python] Applied patch SF#2158938: all the SWIG symbol names started with Py
+ are changed, since they are inappropriate and discouraged in Python
+ documentation (from http://www.python.org/doc/2.5.2/api/includes.html):
+
+ "All user visible names defined by Python.h (except those defined by
+ the included standard headers) have one of the prefixes "Py" or "_Py".
+ Names beginning with "_Py" are for internal use by the Python implementation
+ and should not be used by extension writers. Structure member names do
+ not have a reserved prefix.
+
+ Important: user code should never define names that begin with "Py" or "_Py".
+ This confuses the reader, and jeopardizes the portability of the user
+ code to future Python versions, which may define additional names beginning
+ with one of these prefixes."
+
+ Here is a brief list of what changed:
+
+ PySwig* -> SwigPy*
+ PyObject_ptr -> SwigPtr_PyObject
+ PyObject_var -> SwigVar_PyObject
+ PySequence_Base, PySequence_Cont, PySequence_Ref ->
+ SwigPySequence_Base, SwigPySequence_Cont, SwigPySequence_Ref
+ PyMap* -> SwigPyMap*
+
+ We provided a pyname_compat.i for backward compatibility. Users whose code having
+ these symbols and do not want to change it could simply include this file
+ at front of your code. A better solution is to run the converting tool on
+ your code, which has been put in SWIG's SVN trunk (Tools/pyname_patch.py) and
+ you can download it here:
+ https://swig.svn.sourceforge.net/svnroot/swig/trunk/Tools/pyname_patch.py
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+2008-12-02: wsfulton
+ [Python] Apply patch #2143727 from Serge Monkewitz to fix importing base classes
+ when the package option is specified in %module and that module is %import'ed.
+
+2008-11-28: wsfulton
+ [UTL] Fix #2080497. Some incorrect acceptance of types in the STL, eg a double * element
+ passed into a vector<int *> constructor would be accepted, but the ensuing behaviour
+ was undefined. Now the type conversion correctly raises an exception.
+
+2008-11-24: wsfulton
+ Add -outcurrentdir option. This sets the default output directory to the current
+ directory instead of the path specified by the input file. This option enables
+ behaviour similar to c/c++ compilers. Note that this controls the output directory,
+ but only in the absence of the -o and/or -outdir options.
+
+2008-11-23: wsfulton
+ [ruby] Apply patch #2263850 to fix ruby/file.i ... rubyio.h filename change in
+ ruby 1.9.
+
+2008-11-23: wsfulton
+ Apply patch #2319790 from Johan Hake to fix shared_ptr usage in std::tr1 namespace.
+
+2008-11-21: wsfulton
+ The use of the include path to find the input file is now deprecated.
+ This makes the behaviour of SWIG the same as C/C++ compilers in preparation
+ for use with ccache.
+
+2008-11-16: wsfulton
+ Fix -nopreprocess option to:
+ - correctly report file names in warning and error messages.
+ - use the original input filename that created the preprocessed output when
+ determining the C++ wrapper file name (in the absence of -o). Previously
+ the name of the input file containing the preprocessed output was used.
+
+2008-11-11: wsfulton
+ [Java] Add patch #2152691 from MATSUURA Takanori which fixes compiles using the
+ Intel compiler
+
+2008-11-01: wsfulton
+ Add patch #2128249 from Anatoly Techtonik which corrects the C/C++ proxy
+ class being reported for Python docstrings when %rename is used.
+
+2008-11-01: wsfulton
+ Add the strip encoder patch from Anatoly Techtonik #2130016. This enables an
+ easy way to rename symbols by stripping a commonly used prefix in all the
+ function/struct names. It works in the same way as the other encoders, such as
+ title, lower, command etc outlined in CHANGES file dated 12/30/2005. Example
+ below will rename wxAnotherWidget to AnotherWidget and wxDoSomething to
+ DoSomething:
+
+ %rename("%(strip:[wx])s") "";
+
+ struct wxAnotherWidget {
+ void wxDoSomething();
+ };
+
+2008-09-26: mutandiz
+ [allegrocl]
+ Lots of test-suite work.
+ - Fix ordering of wrapper output and %{ %} header output.
+ - Fix declarations of local vars in C wrappers.
+ - Fix declaration of defined constants in C wrappers.
+ - Fix declaration of EnumValues in C wrappers.
+ - add some const typemaps to allegrocl.swg
+ - add rename for operator bool() overloads.
+
+2008-09-25: olly
+ [PHP5] Fill in typemaps for SWIGTYPE and void * (SF#2095186).
+
+2008-09-22: mutandiz (Mikel Bancroft)
+ [allegrocl]
+ - Support wrapping of types whose definitions are not seen by
+ SWIG. They are treated as forward-referenced classes and if a
+ definition is not seen are treated as (* :void).
+ - Don't wrap the contents of unnamed namespaces.
+ - More code cleanup. Removed some extraneous warnings.
+ - start work on having the allegrocl mod pass the cpp test-suite.
+
+2008-09-19: olly
+ [PHP5] Add typemaps for long long and unsigned long long.
+
+2008-09-18: wsfulton
+ [C#] Added C# array typemaps provided by Antti Karanta.
+ The arrays provide a way to use MarshalAs(UnmanagedType.LPArray)
+ and pinning the array using 'fixed'. See arrays_csharp.i library file
+ for details.
+
+2008-09-18: wsfulton
+ Document the optional module attribute in the %import directive,
+ see Modules.html. Add a warning for Python wrappers when the
+ module name for an imported base class is missing, requiring the
+ module attribute to be added to %import, eg
+
+ %import(module="FooModule") foo.h
+
+2008-09-18: olly
+ [PHP5] Change the default input typemap for char * to turn PHP
+ Null into C NULL (previously it was converted to an empty string).
+ The new behaviour is consistent with how the corresponding output
+ typemap works (SF#2025719).
+
+ If you want to keep the old behaviour, add the following typemap
+ to your interface file (PHP's convert_to_string_ex() function does
+ the converting from PHP Null to an empty string):
+
+ %typemap(in) char * {
+ convert_to_string_ex($input);
+ $1 = Z_STRVAL_PP($input);
+ }
+
+2008-09-18: olly
+ [PHP5] Fix extra code added to proxy class constructors in the case
+ where the only constructor takes no arguments.
+
+2008-09-18: olly
+ [PHP5] Fix wrapping of a renamed enumerated value of an enum class
+ member (SF#2095273).
+
+2008-09-17: mutandiz (Mikel Bancroft)
+ [allegrocl]
+ - Fix how forward reference typedefs are handled, so as not to conflict
+ with other legit typedefs.
+ - Don't (for now) perform an ffitype typemap lookup when trying to
+ when calling compose_foreign_type(). This is actually a useful thing
+ to do in certain cases, the test cases for which I can't currently
+ locate :/. It's breaking some wrapping behavior that is more commonly
+ seen, however. I'll readd in a more appropriate way when I can
+ recreate the needed test case, or a user complains (which means
+ they probably have a test case).
+ - document the -isolate command-line arg in the 'swig -help' output.
+ It was in the html docs, but not there.
+ - small amount of code cleanup, removed some unused code.
+ - some minor aesthetic changes.
+
+2008-09-12: bhy
+ [Python] Python 3.0 support branch merged into SWIG trunk. Thanks to
+ Google Summer of Code 2008 for supporting this project! By default
+ SWIG will generate interface files compatible with both Python 2.x
+ and 3.0. And there's also some Python 3 new features that can be
+ enabled by passing a "-py3" command line option to SWIG. These
+ features are:
+
+ - Function annotation support
+ Also, the parameter list of proxy function will be generated,
+ even without the "-py3" option. However, the parameter list
+ will fallback to *args if the function (or method) is overloaded.
+ - Buffer interface support
+ - Abstract base class support
+
+ For details of Python 3 support and these features, please see the
+ "Python 3 Support" section in the "SWIG and Python" chapter of the SWIG
+ documentation.
+
+ The "-apply" command line option and support of generating codes
+ using apply() is removed. Since this is only required by very old
+ Python.
+
+ This merge also patched SWIG's parser to solve a bug. By this patch,
+ SWIG features able to be correctly applied on C++ conversion operator,
+ such like this:
+
+ %feature("shadow") *::operator bool %{ ... %}
+
+2008-09-02: richardb
+ [Python] Commit patch #2089149: Director exception handling mangles
+ returned exception. Exceptions raised by Python code in directors
+ are now passed through to the caller without change. Also, remove
+ the ": " prefix which used to be added to other director exceptions
+ (eg, those due to incorrect return types).
+
+2008-09-02: wsfulton
+ [Python] Commit patch #1988296 GCItem multiple module linking issue when using
+ directors.
+
+2008-09-02: wsfulton
+ [C#] Support for 'using' and 'fixed' blocks in the 'csin' typemap is now
+ possible through the use of the pre attribute and the new terminator attribute, eg
+
+ %typemap(csin,
+ pre=" using (CDate temp$csinput = new CDate($csinput)) {",
+ terminator=" } // terminate temp$csinput using block",
+ ) const CDate &
+ "$csclassname.getCPtr(temp$csinput)"
+
+ See CSharp.html for more info.
+
+2008-09-01: wsfulton
+ [CFFI] Commit patch #2079381 submitted by Boris Smilga - constant exprs put into
+ no-eval context in DEFCENUM
+
+2008-08-02: wuzzeb
+ [Chicken,Allegro] Commit Patch 2019314
+ Fixes a build error in chicken, and several build errors and other errors
+ in Allegro CL
+
+2008-07-19: wsfulton
+ Fix building of Tcl examples/test-suite on Mac OS X reported by Gideon Simpson.
+
+2008-07-17: wsfulton
+ Fix SF #2019156 Configuring with --without-octave or --without-alllang
+ did not disable octave.
+
+2008-07-14: wsfulton
+ [Java, C#] Fix director typemaps for pointers so that NULL pointers are correctly
+ marshalled to C#/Java null in director methods.
+
+2008-07-04: olly
+ [PHP] For std_vector.i and std_map.i, rename empty() to is_empty()
+ since "empty" is a PHP reserved word. Based on patch from Mark Klein
+ in SF#1943417.
+
+2008-07-04: olly
+ [PHP] The deprecated command line option "-make" has been removed.
+ Searches on Google codesearch suggest that nobody is using it now
+ anyway.
+
+2008-07-04: olly
+ [PHP] The SWIG cdata.i library module is now supported.
+
+2008-07-03: olly
+ [PHP] The deprecated command line option "-phpfull" has been
+ removed. We recommend building your extension as a dynamically
+ loadable module.
+
+2008-07-02: olly
+ [PHP4] Support for PHP4 has been removed. The PHP developers are
+ no longer making new PHP4 releases, and won't even be providing
+ patches for critical security issues after 2008-08-08.
+
+2008-07-02: olly
+ [Python] Import the C extension differently for Python 2.6 and
+ later so that an implicit relative import doesn't produce a
+ deprecation warning for 2.6 and a failure for 2.7 and later.
+ Patch from Richard Boulton in SF#2008229, plus follow-up patches
+ from Richard and Haoyu Bai.
+
+Version 1.3.36 (24 June 2008)
+=============================
+
+06/24/2008: wsfulton
+ Remove deprecated -c commandline option (runtime library generation).
+
+06/24/2008: olly
+ [PHP] Fix assertion failure when handling %typemap(in,numinputs=0)
+ (testcase ignore_parameter).
+
+06/24/2008: olly
+ [PHP] Fix segfault when wrapping a non-class function marked with
+ %newobject (testcase char_strings).
+
+06/22/2008: wsfulton
+ [Java] Add a way to use AttachCurrentThreadAsDaemon instead of AttachCurrentThread
+ in director code. Define the SWIG_JAVA_ATTACH_CURRENT_THREAD_AS_DAEMON macro, see
+ Lib/java/director.swg.
+
+06/21/2008: wsfulton
+ [Ruby] Fix crashing in the STL wrappers (reject! and delete_if methods)
+
+06/19/2008: wsfulton
+ [Java, C#] C# and Java keywords will be renamed instead of just issuing a warning
+ and then generating uncompilable code. Warning 314 gives the new name when a
+ keyword is found.
+
+06/19/2008: wsfulton
+ [R] Keyword handling added. R Keywords will be renamed as necessary.
+ Warning 314 gives the new name when a keyword is found.
+
+06/17/2008: mgossage
+ [Lua] Added missing support for bool& and bool*. Added runtest for li_typemaps testcase.
+ (Bug #1938142)
+
+06/07/2008: bhy
+ Added test case keyword_rename, then made the keyword renaming works properly
+ by fixing Swig_name_make() for a incomplete condition checking.
+
+06/02/2008: wsfulton
+ [Java, C#] Fix enum wrappers when using -noproxy.
+
+05/30/2008: bhy
+ Added std::wstring into Lib/typemaps/primtypes.swg, since it is also a primitive
+ type in SWIG - fixed SF #1976978.
+
+05/29/2008: wsfulton
+ [Java, C#] Fix variable wrappers when using -noproxy.
+
+05/29/2008: bhy
+ [Python] Fixed a typo of %#ifdef in Lib/python/pycontainer.swg, which is related
+ to -extranative SWIG option - SF #1971977.
+
+05/20/2008: wsfulton
+ New partialcheck makefile targets for partial testing of the test-suite. These
+ just invoke SWIG, ie no compilation and no runtime testing. It can be faster
+ when developing by just doing a directory diff of the files SWIG generates
+ against those from a previous run. Example usage from the top level directory:
+
+ make partialcheck-test-suite
+ make partialcheck-java-test-suite
+
+ This change also encompasses more flexibility in running the test-suite, eg
+ it is possible to prefix the command line which runs any target language test
+ with a tool. See the RUNTOOL, COMPILETOOL and SWIGTOOL targets in the common.mk
+ file and makefiles in the test-suite directory. For example it is possible to
+ run the runtime tests through valgrind using:
+
+ make check RUNTOOL="valgrind --leak-check=full"
+
+ or invoke SWIG under valgrind using:
+
+ make check SWIGTOOL="valgrind --tool=memcheck"
+
+05/19/2008: drjoe
+ [R] Fixed define that was breaking pre-2.7. Checked in
+ patch from Soren Sonnenburg that creates strings in
+ version independent way
+
+05/15/2008: wsfulton
+ [Java] Fix variable name clash in directors - SF #1963316 reported by Tristan.
+
+05/14/2008: wsfulton
+ Add an optimisation for functions that return objects by value, reducing
+ the number of copies of the object that are made. Implemented using an
+ optional attribute in the "out" typemap called "optimal". Details in
+ Typemaps.html.
+
+05/11/2008: olly
+ [PHP] Check for %feature("notabstract") when generating PHP5 class
+ wrapper.
+
+05/11/2008: wsfulton
+ Fix SF #1943608 - $self substitution in %contract, patch submitted by
+ Toon Verstraelen.
+
+05/09/2008: olly
+ [PHP] Fix char * typemaps to work when applied to signed char * and
+ unsigned char * (uncovered by testcase apply_strings).
+
+05/09/2008: wsfulton
+ Fix wrapping of char * member variables when using allprotected mode.
+ Bug reported by Warren Wang.
+
+05/09/2008: olly
+ [PHP] Fix bad PHP code generated when wrapping an enum in a
+ namespace (uncovered by testcase arrays_scope).
+
+05/09/2008: olly
+ [PHP] SWIG now runs the PHP testsuite using PHP5, not PHP4. PHP4
+ is essentially obsolete now, so we care much more about solid PHP5
+ support.
+
+05/07/2008: wsfulton
+ STL fixes when using %import rather than %include and the Solaris Workshop
+ compiler and the Roguewave STL.
+
+05/07/2008: wsfulton
+ Fix wrapping of overloaded protected methods when using allprotected mode.
+ Bug reported by Warren Wang.
+
+05/03/2008: wsfulton
+ Commit patch #1956607 to add -MT support from Richard Boulton.
+ This patch mirrors the gcc -MT option which allows one to change the default
+ Makefile target being generated when generating makefiles with the -M family
+ of options. For example:
+
+ $ swig -java -MM -MT overriddenname -c++ example.i
+ overriddenname: \
+ example.i \
+ example.h
+
+04/30/2008: mgossage
+ [Lua] Removed generation of _wrap_delete_XXXXX (wrappered destructor)
+ which was unused and causing warning with g++ -Wall.
+ Removed other unused warning in typemaps.i and other places.
+ Added Examples/lua/embed3, and run tests a few test cases.
+
+04/24/2008: olly
+ [Python] Fix generated code for IBM's C++ compiler on AIX (patch
+ from Goeran Uddeborg in SF#1928048).
+
+04/24/2008: olly
+ Rename BSIZE in Examples/test-suite/arrays_scope.i to BBSIZE to
+ avoid a clash with BSIZE defined by headers on AIX with Perl
+ (reported in SF#1928048).
+
+04/20/2008: wsfulton
+ Add the ability to wrap all protected members when using directors.
+ Previously only the virtual methods were available to the target language.
+ Now all protected members, (static and non-static variables, non-virtual methods
+ and static methods) are wrapped when using the allprotected mode. The allprotected
+ mode is turned on in the module declaration:
+
+ %module(directors="1", allprotected="1") modulename
+
+Version 1.3.35 (7 April 2008)
+=============================
+
+04/07/2008: wsfulton
+ [Lua] Add missing pointer reference typemaps
+
+04/06/2008: wsfulton
+ Fix stack overflow when using typemap warning suppression, eg
+ %warnfilter(SWIGWARN_TYPEMAP_CHARLEAK_MSG)
+
+04/05/2008: wsfulton
+ [Python] Fix shared_ptr typemaps so that %pythonnondynamic can be used. Also corrects
+ display of the proxy class type. Reported by Robert Lupton.
+
+04/04/2008: olly
+ [Python] Add %newobject reference to python memory management subsection of manual
+ (patch from mdbeachy in SF#1894610).
+
+03/27/2008: wsfulton
+ [Python] Fix shared_ptr typemaps where the pointer type is a templated type with
+ with more than one parameter. Reported by Robert Lupton.
+
+03/27/2008: mgossage
+ [Lua] Added a typemap DISOWN for SWIGTYPE* and SWIGTYPE[], and support for %delobject feature.
+ Added Examples/lua/owner which demonstrates the use of the memory management.
+
+03/26/2008: wsfulton
+ [Java] Apply patch #1844301 from Monty Taylor to suppress enum constructor
+ unused warnings.
+
+03/26/2008: wsfulton
+ [Python] Apply patch #1924524 from Casey Raymondson which ensures the
+ "No constructor defined" message is displayed when attempting to call a
+ constructor on a class that doesn't have a constructor wrapper, eg if
+ the C++ class is abstract.
+
+03/26/2008: wsfulton
+ [Python] Apply patch #1925702 from Casey Raymondson which removes warning 512
+ for std::vector wrappers.
+
+03/26/2008: olly
+ [Python] Apply GCC 4.3 warnings patch from Philipp Thomas
+ (SF#1925122).
+
+03/21/2008: wsfulton
+ [Python] Thread safety patch for STL iterators from Abhinandan Jain.
+
+03/17/2008: mgossage
+ [Lua] Added %luacode feature to add source code into wrappers.
+ Updated documentation to document this.
+ Added Examples/lua/arrays to show its use (and typemaps)
+
+03/17/2008: olly
+ Fix nonportable sed usage which failed on Mac OS X (and probably
+ other platforms). Fixes SF#1903612.
+
+03/17/2008: olly
+ Fix memory leak in SWIG's parser (based on patch from Russell
+ Bryant in SF#1914023).
+
+03/12/2008: wsfulton
+ Fix bug #1878285 - unnecessary cast for C struct creation wrappers.
+
+03/12/2008: wsfulton
+ [Python] Remove debugging info when using shared_ptr support
+
+03/06/2008: mgossage
+ [Lua] Updated documentation for Lua exceptions.
+ Added Examples/lua/exception and Examples/lua/embed2.
+ Small updates to the typemaps.
+
+03/04/2008: wsfulton
+ [Java, C#] Add char *& typemaps.
+
+03/04/2008: wsfulton
+ Fix occasional seg fault when attempting to report overloaded methods as being ignored.
+
+02/29/2008: wsfulton
+ [Perl] Fix #1904537 Swig causes a Perl warning "x used only once" in Perl 5.10
+ reported by Ari Jolma
+
+02/29/2008: wsfulton
+ [Python] Add shared_ptr varin/varout typemaps for wrapping global variables.
+
+02/25/2008: wsfulton
+ Fix $wrapname to work in %exception (fixes some wrap:name assertions)
+
+Version 1.3.34 (27 February 2008)
+=================================
+
+02/13/2008: wsfulton
+ [R] Fix wrapping of global function pointer variables.
+
+02/13/2008: wsfulton
+ Add new special variables for use within %exception:
+ $wrapname - language specific wrapper name
+ $overname - if a method is overloaded this contains the extra mangling used on
+ the overloaded method
+ $decl - the fully qualified C/C++ declaration of the method being wrapped
+ without the return type
+ $fulldecl - the fully qualified C/C++ declaration of the method being wrapped
+ including the return type
+
+02/12/2008: drjoe
+ [R] Now setting S4 flag in SWIG created objects. This
+ fixes R-SWIG for 2.6 and warning for 2.6 failure has been removed.
+
+02/11/2008: mgossage
+ [Lua] Added a patch by Torsten Landschoff to fix the unary minus issue
+ Ran 'astyle --style=kr -2' across lua.cxx to neaten it up
+
+02/10/2008: wsfulton
+ Bump SWIG_RUNTIME_VERSION to 4. This is because of the recently introduced API
+ change in the conversion functions, ie change in definition of swig_converter_func.
+ Anyone calling SWIG_TypeCast must pass in a valid value for the new additional
+ (third) parameter and then handle the newly created memory if the returned value
+ is set to SWIG_CAST_NEW_MEMORY else a memory leak will ensue.
+
+02/09/2008: wsfulton
+ [Python] Experimental shared_ptr typemaps added. Usage is the same as the recently
+ added Java and C# shared_ptr typemaps. Two macros are available, although these
+ may well change in a future version:
+
+ For base classes or classes not in an inheritance chain:
+ SWIG_SHARED_PTR(PROXYCLASS, TYPE)
+ For derived classes:
+ SWIG_SHARED_PTR_DERIVED(PROXYCLASS, BASECLASSTYPE, TYPE)
+
+ The PROXYCLASS is the name of the proxy class, but is only required for Java/C#.
+ Example usage:
+
+ %include "boost_shared_ptr.i"
+
+ SWIG_SHARED_PTR(Klass, Space::Klass)
+ SWIG_SHARED_PTR_DERIVED(KlassDerived, Space::Klass, Space::KlassDerived)
+
+ namespace Space {
+ struct Klass { ... };
+ struct KlassDerived : Klass { ... };
+ }
+
+ Further details to follow in future documentation, but the following features
+ should be noted:
+
+ - Not restricted to boost::shared_ptr, eg std::tr1::shared_ptr can also be used.
+ - Available typemap groups:
+ (a) Typemaps for shared_ptr passed by value, reference, pointer and pointer
+ reference.
+ - (b) Typemaps for passing by raw value, raw pointer, raw reference, raw pointer
+ reference.
+ - The code being wrapped does not even have to use shared_ptr, SWIG can use
+ shared_ptr as the underlying storage mechanism instead of a raw pointer due to
+ the typemaps in group (b) above.
+ - No array support as shared_ptr does not support arrays.
+ - This works quite differently to the usual SWIG smart pointer support when
+ operator-> is parsed by SWIG:
+ - An additional smart pointer class is not generated reducing code bloat in
+ the wrappers.
+ - Using smart pointers and raw pointers can be mixed seamlessly.
+ - Missing constructors for the smart pointers is no longer a problem and so
+ separate factory type functions do not have to be written and wrapped.
+ - The implicit C++ shared_ptr< derived class > to shared_ptr< base class >
+ cast also works in the target language. This negates the necessity to write
+ an explicit helper cast function providing the upcast which would need
+ calling prior to passing a derived class to a method taking a shared_ptr to
+ a base class.
+
+02/09/2008: wsfulton
+ [Python] Add support for overriding the class registration function via a new
+ "smartptr" feature. This is a very low level of customisation most users
+ would never need to know. The feature will typically be used for intrusive
+ smart pointers along with additional typemaps. Example usage of the feature:
+
+ %feature("smartptr", noblock=1) Foo { boost::shared_ptr< Foo > }
+ class Foo {};
+
+ The generated Foo_swigregister function will then register boost::shared < Foo >
+ (SWIGTYPE_p_boost__shared_ptrTFoo_t instead of SWIGTYPE_p_Foo) as the underlying
+ type for instantiations of Foo.
+
+02/09/2008: wsfulton
+ Features now supports the optional 'noblock' attribute for all usage of %feature.
+ When specified, the { } braces are removed from the feature code. This is identical
+ in behaviour to usage of 'noblock' in typemaps and is used when the preprocessor
+ is required to operate on the code in the feature and the enclosing { } braces
+ are not required. Example:
+
+ #define FOO foo
+ %feature("smartptr", noblock="1") { FOO::bar }
+
+ The preprocessor then reduces this as if this had been used instead:
+
+ %feature("smartptr") "foo::bar"
+
+02/01/2008: olly
+ [Python] Fix format string bug (SF#1882220).
+
+01/31/2008: wsfulton
+ Additions to the %types directive. Now the conversion / casting code can be
+ overridden to some custom code in the %types directive, like so:
+
+ %types(fromtype = totype) %{
+ ... code to convert fromtype to totype and return ...
+ %}
+
+ The special variable $from will be replaced by the name of the parameter of the
+ type being converted from. The code must return the totype cast to void *. Example:
+
+ class Time;
+ class Date;
+ Date &Time::dateFromTime();
+
+ %types(Time = Date) %{
+ Time *t = (Time *)$from;
+ Date &d = t->dateFromTime();
+ return (void *) &d;
+ %}
+
+ resulting in the conversion / casting code looking something like:
+
+ static void *_p_TimeTo_p_Date(void *x) {
+ Time *t = (Time *)x;
+ Date &d = t->dateFromTime();
+ return (void *) &d;
+ }
+
+ This is advanced usage, please use only if you understand the runtime type system.
+
+01/30/2008: mgossage
+ Small update to documentation in Typemaps.html, to warn about use of local
+ variables in typemaps for multiple types.
+
+01/25/2008: wsfulton
+ [Java] Fix bug reported by Kevin Mills in ARRAYSOFCLASSES typemaps where any
+ changes made to an array element passed from Java to C are not reflected back
+ into Java.
+
+01/24/2008: mgossage
+ More updates to the configure script for detecting lua.
+ Also looks in /usr/include/lua*
+ Also changed typemaps.i not to check for NULL before freeing a pointer
+
+01/21/2008: wsfulton
+ [Python] For STL containers, SWIG no longer attempts to convert from one
+ STL container to another, eg from std::vector<int> to std::vector<double>
+ or std::list<int> to std::vector<int> or even std::vector<Foo> to
+ std::vector<Bar> as it previously did. In fact SWIG no longer attempts to
+ convert any SWIG wrapped C++ proxy class that is also a Python sequence,
+ whereas previously it would. Any non-SWIG Python sequence will still be
+ accepted wherever an STL container is accepted. Overloaded methods using
+ containers should be faster.
+
+01/18/2008: wsfulton
+ [C#] Add 'directorinattributes' and 'directoroutattributes' typemap attributes
+ for the imtype typemap. These should contain C# attributes which will
+ be generated into the C# director delegate methods.
+
+01/18/2008: olly
+ Fix handling of byte value 255 in input files on platforms where
+ char is signed (it was getting mapped to EOF). Fixes SF#1518219.
+
+01/16/2008: wsfulton
+ Fix template member variables wrapped by a smart pointer. Bug reported
+ by Robert Lupton.
+
+01/14/2008: mgossage
+ Substantial changes to configure script for detecting lua.
+ Code can now link to liblua.a, liblua50.a or liblua51.a
+ It's also a lot neater now.
+
+12/16/2007: wsfulton
+ [Perl] Backed out #1798728 - numbers can be passed to functions taking char *
+
+12/16/2007: wsfulton
+ Fix #1832613 - Templates and some typedefs involving pointers or function pointers
+
+12/12/2007: wsfulton
+ [Java] Fix #1632625 - Compilation errors on Visual C++ 6 when using directors.
+
+12/12/2007: wsfulton
+ [Perl] Fix #1798728 - numbers can be passed to functions taking char *.
+
+12/12/2007: wsfulton
+ Fix #1819847 %template with just one default template parameter
+
+ template<typename T = int> class Foo {...};
+ %template(FooDefault) Foo<>;
+
+12/12/2007: mgossage
+ [Lua] Small correction on Lua.html
+
+12/09/2007: wsfulton
+ Apply patch #1838248 from Monty Taylor for vpath builds of SWIG.
+
+12/08/2007: wsfulton
+ [Lua] Fixes to remove gcc-4.2 warnings
+
+12/06/2007: wsfulton
+ Fix #1734415 - template template parameters with default arguments such as:
+
+ template<typename t_item, template<typename> class t_alloc = pfc::alloc_fast >
+ class list_t : public list_impl_t<t_item,pfc::array_t<t_item,t_alloc> > { ... };
+
+12/04/2007: mgossage
+ [lua] Fix a bug in the class hierachy code, where the methods were not propagated,
+ if the name ordering was in a certain order.
+ Added new example programs (dual, embed) and runtime tests for test-suite.
+
+11/30/2007: wsfulton
+ Fix using statements using a base class method where the methods were overloaded.
+ Depending on the order of the using statements and method declarations, these
+ were previously generating uncompilable wrappers, eg:
+
+ struct Derived : Base {
+ virtual void funk();
+ using Base::funk;
+ };
+
+Version 1.3.33 (November 23, 2007)
+==================================
+
+11/21/2007: mikel
+ [allegrocl] omit private slot type info in the classes/types
+ defined on the lisp side. Fix bug in mapping of C/++ types
+ to lisp types. Fix typo in modules generated defpackage form.
+ Have std::string *'s automatically marshalled between foreign
+ and lisp strings.
+
+11/20/2007: olly
+ [Python] Fill in Python Dictionary functions list (patch from
+ Jelmer Vernooij posted to swig-devel).
+
+11/20/2007: beazley
+ Fixed a bug in the C scanner related to backslash characters.
+
+11/19/2007: wsfulton
+ [Perl] Fix broken compilation of C++ wrappers on some compilers.
+
+11/16/2007: olly
+ [Python] Don't pass Py_ssize_t for a %d printf-like format as
+ that's undefined behaviour when sizeof(Py_ssize_t) != sizeof(int).
+
+Version 1.3.32 (November 15, 2007)
+==================================
+
+11/14/2007: wsfulton
+ [R] Package name and dll name is now the same as the SWIG module
+ name. It used to be the module name with _wrap as a suffix. The package
+ and dll names can be modified using the -package and -dll commandline
+ options.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/11/2007: wsfulton
+ [R] Add support for Windows (Visual C++ 8 tested)
+
+11/10/2007: olly
+ [php] Fix makefile generated by -make (SF#1633679). Update
+ documentation to mark "-make" as deprecated (none of the other
+ SWIG backends seem to offer such a feature, it can't realistically
+ generate a fully portable makefile, and the commands to build an
+ extension are easy enough to write for the user's preferred build
+ tool). Also recommend against the use of "-phpfull" (it's only
+ really useful when static linking, and a dynamically loadable
+ module is virtually always the better approach).
+
+11/09/2007: olly
+ Fix --help output to note that `export SWIG_FEATURES' is required.
+
+10/29/2007: wsfulton
+ [R] Fix seg fault on Windows
+ [R] Examples R scripts are now platform independent
+
+10/30/2007: mgossage
+ [lua] fixed bug in template classes which cases template_default2
+ and template_specialization_defarg to fail.
+ Added several warning filters into the overload's test cases.
+ Added runtime tests for several codes.
+ You can now make check-lua-test-suite with no errors and only a few warnings.
+
+10/30/2007: olly
+ [guile] Fix the configure test to put GUILELINK in LIBS not LDFLAGS
+ (SF#1822430).
+
+10/30/2007: olly
+ [guile] Fix the guile examples on 64-bit platforms.
+
+10/29/2007: wsfulton
+ [C#] Fix member pointers on 64 bit platforms.
+
+10/28/2007: olly
+ [lua] Fix swig_lua_class instances to be static to allow multiple
+ SWIG wrappers to be compiled into the same executable statically.
+ Patch from Andreas Fredriksson (posted to the swig mailing list).
+
+10/28/2007: olly
+ [lua] Fix Examples/lua to pass SRCS for C tests rather than CXXSRCS.
+ The code as it was happened to work on x86, but broke on x86_64 (and
+ probably any other platforms which require -fPIC).
+
+10/28/2007: wsfulton
+ [Java, C#] New approach for fixing uninitialised variable usage on error in director
+ methods using the new templated initialisation function SwigValueInit().
+
+10/28/2007: wsfulton
+ [Perl] Use more efficient SvPV_nolen(x) instead of SvPV(x,PL_na) if SvPV_nolen is
+ supported.
+
+10/26/2007: wuzzeb
+ [Chicken] Fix global variables of class member function pointers.
+ Other minor fixes, so all tests in the chicken test suite now pass
+
+10/25/2007: olly
+ Fix UTL typecheck macro for a function taking char[] or const
+ char[] (SF#1820132).
+
+10/22/2007: mkoeppe
+ [Guile] Filter out -ansi -pedantic from CFLAGS while compiling test programs for Guile
+ in configure. This enables running the test suite for Guile if it is installed and
+ usable.
+
+10/22/2007: mkoeppe
+ [Guile -scm] Fix testcases apply_signed_char and apply_strings
+ by adding explicit casts to the appropriate $ltype.
+
+10/22/2007: wsfulton
+ [Java, C#] Fix uninitialised variable usage on error in director methods.
+
+10/19/2007: wsfulton
+ [Java, C#] Bug #1794247 - fix generated code for derived classes when csbase or javabase
+ typemaps are used with the replace="1" attribute.
+
+10/19/2007: wsfulton
+ [Python] Docs updated to suggest using distutils. Patch #1796681 from Christopher Barker.
+
+10/19/2007: olly
+ [perl5] Clear errno before calls to strtol(), strtoul(), strtoll()
+ and strtoull() which we check errno after to avoid seeing a junk
+ value of errno if there isn't an error in the call.
+
+10/16/2007: wsfulton
+ Deprecate %attribute_ref and replace with %attributeref. There is just an argument
+ order change in order to maintain consistency with %attribute, from:
+
+ %attribute_ref(Class, AttributeType, AccessorMethod, AttributeName)
+ to
+ %attributeref(Class, AttributeType, AttributeName, AccessorMethod)
+
+10/16/2007: olly
+ [Tcl] Fix several occurrences of "warning: deprecated conversion
+ from string constant to 'char*'" from GCC 4.2 in generated C/C++
+ code.
+
+10/16/2007: olly
+ [PHP] Fix many occurrences of "warning: deprecated conversion from
+ string constant to 'char*'" from GCC 4.2 in generated C/C++ code
+ when compiling with a new enough version of PHP 5 (tested with
+ PHP 5.2.3, but PHP 5.2.1 is probably the minimum requirement).
+
+10/15/2007: wsfulton
+ Patch #1797133 from David Piepgrass fixes %attribute when the getter has the same name
+ as the attribute name and no longer generate non-functional setter for read-only attributes.
+
+10/15/2007: olly
+ [Tcl] Prevent SWIG_Tcl_ConvertPtr from calling the unknown proc.
+ Add Examples/tcl/std_vector/ which this change fixes. Patch
+ is from "Cliff C" in SF#1809819.
+
+10/12/2007: wsfulton
+ [Java] Add DetachCurrentThread back in for directors. See entry dated 08/11/2006 and
+ search for DetachCurrentThread on the mailing lists for details. The crashes on Solaris
+ seem to be only present in jdk-1.4.2 and lower (jdk-1.5.0 and jdk-1.6.0 are okay), so
+ anyone using directors should use a recent jdk on Solaris, or define (see director.swg)
+ SWIG_JAVA_NO_DETACH_CURRENT_THREAD to the C++ compiler to get old behaviour.
+
+10/12/2007: wsfulton
+ [Java] Ensure the premature garbage collection prevention parameter (pgcpp) is generated
+ when there are C comments in the jtype and jstype typemaps.
+
+10/12/2007: wuzzeb
+ Added a testsuite entry for Bug #1735931
+
+10/09/2007: olly
+ Automatically rerun autogen.sh if configure.in is modified.
+
+10/09/2007: olly
+ Enhance check-%-test-suite rule and friends to give a more helpful
+ error message if you try them for a language which doesn't exist
+ (e.g. "make check-php-test-suite" rather than the correct
+ "make check-php4-test-suite").
+
+10/09/2007: olly
+ Add make rule to regenerate Makefile from Makefile.in if it has
+ changed.
+
+10/09/2007: olly
+ [php] Fix long-standing memory leak in wrapped constructors and
+ wrapped functions/methods which return an object.
+
+10/08/2007: olly
+ Fix Makefile.in to read check.list files correctly in a VPATH
+ build.
+
+10/07/2007: wsfulton
+ [C#, Java] Experimental shared_ptr typemaps added
+
+09/27/2007: mgossage
+ [lua] added more verbose error messages for incorrect typechecks.
+ Added a routine which checks the exact number of parameters passed to a function
+ (breaks operator_overloading for unary minus operator, currently disabled).
+ Reorganised the luatypemaps.swg to tidy it up.
+ Added a lot of %ignores on the operators not supported by lua.
+ Added support for constant member function pointers & runtest for member_pointer.i
+ Added first version of wchar.i
+
+09/25/2007: wsfulton
+ [C#, Java] throws typemaps for std::wstring using C# patch #1799064 from David Piepgrass
+
+09/24/2007: wsfulton
+ [Tcl] Apply #1771313 to fix bug #1650229 - fixes long long and unsigned long long
+ handling.
+
+09/20/2007: olly
+ [Java] Eliminate some unnecessary uses of a temporary buffer
+ allocated using new[]. SF#1796609.
+
+09/19/2007: wsfulton
+ [C#] The $csinput special variable can be used in the csvarin typemap where it is always
+ expanded to 'value'.
+
+09/19/2007: wsfulton
+ [C#] Fix bug reported by Glenn A Watson and #1795260 where the cstype typemap used the 'ref'
+ keyword in the typemap body, it produced uncompilable C# properties (variable wrappers).
+ The type for the property now correctly comes from the 'out' attribute in the cstype typemap.
+
+09/19/2007: wsfulton
+ [Java] Fix const std::wstring& typemaps
+
+09/19/2007: wsfulton
+ [Java] Ensure the premature garbage collection prevention parameter (pgcpp) is generated
+ where a parameter is passed by pointer reference, eg in the std::vector wrappers. The pgcpp
+ is also generated now when user's custom typemaps use a proxy class in the jstype typemap
+ and a 'long' in the jtype typemap.
+
+09/18/2007: olly
+ [php] Add typemaps for handling parameters of type std::string &
+ which are modified by the wrapped function.
+
+09/17/2007: olly
+ [python] Split potentially long string literals to avoid hitting
+ MSVC's low fixed limit on string literal length - patch from
+ SF#1723770, also reported as SF#1630855.
+
+09/17/2007: olly
+ [ocaml] Fix renaming of overloaded methods in the method_table -
+ my patch from SF#940399.
+
+09/17/2007: olly
+ [python] Simpler code for SWIG_AsVal_bool() which fixes a "strict
+ aliasing" warning from GCC - patch from SF#1724581 by Andrew
+ Baumann.
+
+09/17/2007: olly
+ [perl5] Use sv_setpvn() to set a scalar from a pointer and length
+ - patch from SF#174460 by "matsubaray".
+
+09/17/2007: olly
+ When wrapping C++ code, generate code which uses
+ std::string::assign(PTR, LEN) rather than assigning
+ std::string(PTR, LEN). Using assign generates more efficient code
+ (tested with GCC 4.1.2).
+
+09/07/2007: wsfulton
+ Fix %ignore on constructors which are not explicitly declared [SF #1777712]
+
+09/05/2007: wuzzeb (John Lenz)
+ - Change r_ltype in typesys.c to store a hashtable instead of a single value.
+ several very subtle bugs were being caused by multiple ltypes being mapped
+ to a single mangled type, mostly when using typedefed template parameters.
+ Now, r_ltype stores a hashtable of possible ltypes, and when generating the
+ type table, all the ltypes are added into the swig_type_info structure.
+
+08/31/2007: wsfulton
+ SF #1754967 from James Bigler.
+ - Fix bug in turning on warnings that were turned off by default. Eg 'swig -w+309' will now
+ turn on the normally suppressed warning 309.
+
+ - New -Wextra commandline option which enables the extra warning numbers:
+ 202,309,403,512,321,322 (this is the list of warnings that have always been suppressed by
+ default). By specifying -Wextra, all warnings will be turned on, but unlike -Wall,
+ warnings can still be selectively turned on/off using %warnfilter,
+ #pragma SWIG nowarn or further -w commandline options, eg:
+ swig -Wextra -w309
+ will turn on all warnings except 309.
+
+08/28/2007: wsfulton
+ - New debugging options, -debug-module <n> and -debug-top <n> to display the parse tree at
+ various stages, where <n> is a comma separated list of stages 1-4.For example, to
+ display top of parse tree at stages 1 and 3:
+ swig -debug-top 1,3
+
+ - Deprecate the following options which have equivalents below:
+ -dump_parse_module => -debug-module 1
+ -dump_module => -debug-module 4
+ -dump_parse_top => -debug-top 1
+ -dump_top => -debug-top 4
+
+ - Renamed some commandline options for naming consistency across all options:
+ -debug_template => -debug-template
+ -debug_typemap => -debug-typemap
+ -dump_classes => -debug-classes
+ -dump_tags => -debug-tags
+ -dump_typedef => -debug-typedef
+ -dump_memory => -debug-memory
+
+08/25/2007: olly
+ [PHP5] Fix handling of double or float parameters with an integer
+ default value.
+
+08/25/2007: olly
+ [PHP5] Generate __isset() methods for setters for PHP 5.1 and later.
+
+08/20/2007: wsfulton
+ [Java C#] Fix director bug #1776651 reported by Stephane Routelous which occurred when
+ the director class name is the same as the start of some other symbols used within
+ the director class.
+
+08/17/2007: wsfulton
+ Correct behaviour for templated methods used with %rename or %ignore and the empty
+ template declaration - %template(). A warning is issued if the method has not been
+ renamed.
+
+08/16/2007: mutandiz (Mikel Bancroft)
+ [allegrocl] Name generated cl file based on input file rather than by
+ module name. It was possible to end up with a mypackage.cl and a test_wrap.c
+ when parsing a test.i input file. Confusing. Also, include external-format
+ templates for :fat and :fat-le automatically to avoid these being compiled
+ at runtime.
+
+08/15/2007: efuzzyone
+ [cffi] Apply patch #1766076 from Leigh Smith adding support for newly introduced
+ in cffi :long-long and :unsigned-long-long.
+
+08/10/2007: wsfulton
+ [Java] Add documentation patch #1743573 from Jeffrey Sorensen. It contains a neat
+ idea with respect to better memory management by the JVM of C++ allocated memory.
+
+08/10/2007: wsfulton
+ [Perl] Apply patch #1771410 from Wade Brainerd to fix typedef XS(SwigPerlWrapper) in
+ perlrun.swg for ActiveState Perl build 822 and Perl 5.8.9 and 5.10 branches.
+
+08/10/2007: wsfulton
+ [Lua] const enum reference typemaps fixed.
+
+08/09/2007: wsfulton
+ [C#] Added missing support for C++ class member pointers.
+
+08/09/2007: wsfulton
+ [C#, Java] Add support for $owner in the "out" typemaps like in the scripting
+ language modules. Note that $owner has always been supported in the "javaout" / "csout"
+ typemaps.
+
+08/01/2007: wsfulton
+ Fix smart pointer handling for classes that have templated methods within the smart
+ pointer type. Problem reported by craigdo at ee.washington.edu.
+
+07/31/2007: efuzzyone
+ [cffi] fixed memory access after being freed bug. thanks to Martin Percossi.
+ package name clos changed to cl. thanks to Ralf Mattes
+
+07/24/2007: wsfulton
+ Parallel make support added for the examples and test-suite for developers who have
+ more than one CPU. Now parallel make can be used for checking in addition to building
+ the SWIG executable. Some typical checking examples:
+
+ make -j8 -k check
+ make -j4 check-java-test-suite
+ make -j2 check-java-examples
+
+07/19/2007: mgossage
+ Fixed bug that stopped configure working on mingw (applied dos2unix to configure.in)
+
+07/10/2007: mgossage
+ [lua] Extra compatibility with Lua 5.1 (updated SWIG_init, docs, examples, test suite)
+ Removed name clash for static link of multiple modules
+
+07/05/2007: mgossage
+ [lua] Fix a bug in SWIG_ALLOC_ARRAY()
+ improved the error messages for incorrect arguments.
+ Changed the output of swig_type() to use the human readable form of the type,
+ rather than the raw swig type.
+
+07/03/2007: wsfulton
+ [C#] Fix directors for some overloaded methods where the imtype resulted in identical
+ methods being generated in the C# director class, eg void foo(int *) and void foo(double *)
+ used to generated two of these:
+
+ private void SwigDirectorfoo(IntPtr p) { ... }
+
+06/25/2007: wsfulton
+ [Java, C#] Some parameter name changes in std_vector.i allowing better targeting
+ of typemaps for method parameters (for memory management of containers of pointers).
+
+06/07/2007: mutandiz (Mikel Bancroft)
+ [allegrocl]
+ fix foreign-type constructor to properly look for ffitype typemap
+ bindings. fix inout_typemaps.i for strings.
+
+06/06/2007: olly
+ [Ruby]
+ Use whichever of "long" or "long long" is the same size as "void*"
+ to hold pointers as integers, rather than whichever matches off_t.
+ Fixes compilation on OS X and GCC warnings on platforms where
+ sizeof(void*) < sizeof(off_t) (SF patch #1731979).
+
+06/06/2007: olly
+ [PHP5]
+ Fix handling of a particular case involving overloaded functions
+ with default parameters.
+
+06/05/2007: mutandiz (Mikel Bancroft)
+ [allegrocl]
+ Fix case where we'd pass fully qualified identifiers
+ (i.e. NS1::NS2::FOO) to swig-insert-id. All namespaces
+ should be stripped.
+
+ Fix bug in TypedefHandler introduced by last fix.
+
+06/05/2007: olly
+ Fix reporting of filenames in errors after %include (patch from
+ Leigh Smith in #1731040; also reported as #1699940).
+
+05/31/2007: olly
+ [Python]
+ Fix "missing initialiser" warning when compiling generated C/C++
+ wrapper code with Python 2.5 with warnings enabled (patch from
+ bug#1727668 from Luke Moore).
+
+05/29/2007: olly
+ [Python]
+ Split docstrings into separate string literals at each newline when
+ generating C/C++ wrapper code (the C/C++ compiler will just combine
+ them back into a single string literal). This avoids MSVC
+ complaining that the strings are too long (problem reported by
+ Bo Peng on the mailing list).
+
+05/28/2007: olly
+ [Python]
+ Escape backslashes in docstrings.
+
+05/26/2007: olly
+ [Python]
+ Fix autodoc generation of enums to be more consistent with how the
+ enums are wrapped - patch #1697226 from Josh Cherry.
+
+05/26/2007: olly
+ [PHP5]
+ Fix wrapping of methods and functions which return a pointer to a
+ class (bug#1700788) and those which have overloaded forms returning
+ both classes and non-classes (bug#1712717, thanks to Simon
+ Berthiaume for the patch).
+
+05/25/2007: wsfulton
+ Fixed %rename inconsistency in conversion operators as reported by Zhong Ren. The matching
+ is now done on the operator name in the same way as it is done for parameters. For example:
+
+ %rename(opABC) Space::ABC::operator ABC() const;
+ %rename(methodABC) Space::ABC::method(ABC a) const;
+ namespace Space {
+ class ABC {
+ public:
+ void method(ABC a) const {}
+ operator ABC() const { ABC a; return a; }
+ };
+ }
+
+ Note that qualifying the conversion operator previously may or may not have matched.
+ Now it definitely won't, so this will not match:
+
+ %rename(opABC) Space::ABC::operator Space::ABC() const;
+
+ in the same way that this does not match:
+
+ %rename(methodABC) Space::ABC::method(Space::ABC a) const;
+
+ The documentation has been improved with respect to %rename, namespaces and templates.
+ Conversion operators documentation too.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+05/16/2007: mutandiz
+ [allegrocl]
+ Fix bad generation of local var ltype's in functionWrapper().
+ Try to work better with the backward order in which swig
+ unrolls nested class definitions.
+ cleaned up a little unnecessary code/debug printf's.
+ Remove warning when replacing $ldestructor for ff:foreign-pointer
+
+05/12/2007: olly
+ [Python]
+ swig -python -threads now generates C/C++ code which uses Python's
+ own threading abstraction (from pythread.h) rather than OS specific
+ code. The old code failed to compile on MS Windows. (See SF patch
+ tracker #1710341).
+
+05/04/2007: gga
+ [Ruby]
+ Changed STL renames to be global renames. This fixes
+ STL functions not being renamed when autorename is on.
+ This is a not a totally perfect work-around, but better.
+ Someone really needs to fix the template renaming code.
+ (See bug #1545634)
+
+05/04/2007 gga
+ [All]
+ Changed %rename("%(undercase)s") a little so that single
+ numbers at the end of a function are not undercased. That is:
+ getSomething -> get_something
+ get2D -> get_2d
+ get234 -> get_234
+ BUT:
+ asFloat2 -> as_float2
+ (Bug #1699714)
+
+05/03/2007: gga
+ [Ruby]
+ Made __swigtype__ => @__swigtype__ so it can be accessed
+ from the scripting language (and follows Ruby's official
+ documentation, just in case).
+ Made tracking => @__trackings__ for same reason.
+ Currently storing ivars without the @ seems valid, but
+ the PickAxe says this is not correct, so just in case...
+
+05/03/2007: gga
+ [Ruby]
+ Applied patch for -minherit bug and exception classes.
+ This issue should be revisited more closely, as Multiple
+ Inheritance in Ruby is still problematic.
+ (patch/bug #1604878)
+
+05/03/2007: gga
+ [Ruby]
+ Overloaded functions in ruby will now report to the user
+ the possible prototypes when the user mistypes the number or
+ type of a parameter.
+
+05/03/2007: gga
+ [Ruby]
+ Forgot to document the bug fixing of an old bug regarding
+ exceptions.
+ (bug #1458247)
+
+05/03/2007: gga
+ [Ruby]
+ Fixed Ruby documentation to use the proper css styles for
+ each section. Added autodoc section to Ruby's docs to
+ document the features supported by Ruby in documenting its modules.
+ Made rdoc documentation spit out the full name of the class +
+ method name. Albeit this will make the current rdoc not recognize
+ the method, this is still needed to disambiguate between different
+ classes with similar methods (rdoc was created to document the
+ ruby source which only contains one class per c file, unlike swig)
+ I have patched rdoc to make it more friendly to swig. This
+ patch needs to be merged in the ruby std library now.
+
+05/03/2007: gga
+ [Ruby]
+ Changed flag -feature to be -init_name to better reflect its
+ purpose and avoid confusion with -features.
+
+05/03/2007: gga
+ [Ruby]
+ Improved autodoc generation.
+ Added autodoc .swg files to Ruby library for easily adding
+ documentation to common Ruby methods and STL methods.
+ Fixed autodoc documenting of getters and setters and module.
+ Made test suite always generate autodocs.
+
+05/03/2007: gga
+ [Ruby]
+ Removed some warnings from STL and test suite.
+
+05/02/2007: mgossage
+ [Lua] Fixed issues with C++ classes and hierachies across multiple
+ source files. Fixed imports test case & added run test.
+ Added Examples/imports.
+ Added typename for raw lua_State*
+ Added documentation on native functions.
+
+05/02/2007: gga
+ [Ruby]
+ Docstrings are now supported.
+ %feature("autodoc") and %feature("docstring") are now
+ properly supported in Ruby. These features will generate
+ a _wrap.cxx file with rdoc comments in them.
+
+05/02/2007: gga
+ [Ruby]
+ STL files have been upgraded to follow the new swig/python
+ Lib/std conventions.
+ This means std::vector, std::set, std::map, set::multimap,
+ std::multiset, std::deque and std::string are now properly
+ supported, including their iterators, support for containing
+ ruby objects (swig::GC_VALUE) and several other ruby
+ enhancements.
+ std::complex, std::ios, std::iostream, std::iostreambuf and
+ std::sstream are now also supported.
+ std::wstring, std::wios, std::wiostream, std::wiostreambuf
+ and std::wsstream are supported verbatim with no unicode
+ conversion.
+
+ std_vector.i now mimics the behavior of Ruby Arrays much more
+ closely, supporting slicing, shifting, unshifting,
+ multiple indexing and proper return values on assignment.
+
+ COMPATABILITY NOTE: this changes the older api a little bit in
+ that improper indexing would previously (incorrectly) raise
+ exceptions. Now, nil is returned instead, following ruby's
+ standard Array behavior.
+
+05/02/2007: gga
+ [Ruby]
+ Changed the value of SWIG_TYPECHECK_BOOL to be 10000 (ie. higher
+ than that of all integers).
+ This is because Ruby allows typecasting
+ integers down to booleans which can make overloaded functions on
+ bools and integers to fail.
+ (bug# 1488142)
+
+05/02/2007: gga
+ [Ruby]
+ Fixed a subtle bug in multiple argouts that could get triggered if
+ the user returned two or more arguments and the first one was an
+ array.
+
+05/01/2007: gga
+ [Ruby]
+ Improved the documentation to document the new features
+ added, add directorin/out/argout typemaps, etc.
+
+05/01/2007: gga
+ [Ruby]
+ Added %initstack and %ignorestack directives for director
+ functions. These allow you to control whether a director
+ function should re-init the Ruby stack.
+ This is sometimes needed for an embedded Ruby where the
+ director method is used as a C++ callback and not called
+ by the user from ruby code.
+ Explanation:
+ Ruby's GC needs to be aware of the running OS stack in order to
+ mark any VALUE (Ruby objects) it finds there to avoid collection
+ of them. This allows the ruby API to be very simple and allows
+ you to write code like "VALUE a = sth" anywhere without needing
+ to do things like refcounting like python.
+ By default, the start of the stack is set when ruby_init() is
+ called. If ruby is inited within main(), as it usually is the
+ case with the main ruby executable, ruby will be able to calculate
+ its stack properly. However, when this is not possible, as when
+ ruby is embedded as a plugin to an application where main is not
+ available, ruby_init() will be called in the wrong place, and
+ ruby will be incorrectly tracking the stack from the function
+ that called ruby_init() forwards only, which can lead to
+ all sorts of weird crashes or to ruby thinking it has run out of
+ stack space incorrectly.
+ To avoid this, director (callback) functions can now be tagged
+ to try to reset the ruby stack, which will solve the issues.
+ NOTE: ruby1.8.6 still contains a bug in it in that its function
+ to reset the stack will not always do so. This bug is triggered
+ very rarely, when ruby is called from two very distinct places
+ in memory, like a branch of main() and another dso. This bug
+ has now been reported to ruby-core and is pending further
+ investigation.
+ (bug #1700535 and patch #1702907)
+
+04/30/2007: wsfulton
+ Fix #1707582 - Restore building from read-only source directories.
+
+04/30/2007: gga
+ [Ruby]
+ Ruby will now report the parameter index properly on type
+ errors as well as the class and value of the incorrect
+ argument passed.
+ (feature request #1699670)
+
+04/30/2007: gga
+ [Ruby]
+ Ruby no longer creates the free_Class function if the class
+ contains its own user defined free function (%freefunc).
+ (bug #1702882)
+
+04/30/2007: gga
+ [Ruby]
+ Made directors raise a ruby exception for incorrect argout
+ returned values if RUBY_EMBEDDED is set, instead of throwing
+ an actual SwigDirector exception.
+ This will prevent crashes when ruby is embedded and unaware
+ of the SwigDirector exception.
+
+04/30/2007: gga
+ [Ruby]
+ Removed the need for -DSWIGEXTERN.
+ Changed swig_ruby_trackings to be a static variable, but also
+ be kept within a hidden instance variable in the SWIG module.
+ This allows properly dealing with trackings across multiple
+ DSOs, which was previously broken.
+ (bug #1700535 and improvement to patch #1702907)
+
+04/29/2007: gga
+ [Ruby] Fixed GC memory issues with trackings that could lead
+ to segfaults when dealing, mainly, with static variables.
+ (bug #1700535 and patch #1702907)
+
+04/29/2007: gga
+ [Ruby]
+ Fixed String conversion using old ruby1.6 macros. Now
+ StringValuePtr() is used if available. This removes warnings
+ when converting strings with \0 in them.
+ (bug #1700535 and patch #1702907)
+
+04/29/2007: gga
+ [Ruby]
+ Fixed the argout count in directors for Ruby. Previously,
+ ignored or "numinputs=0" typemaps would incorrectly not get
+ counted towards the argout count.
+ (bug/patch #1545585)
+
+04/29/2007: gga
+ [Ruby]
+ Upgraded Ruby converter to recognize "numinputs=0". Previously,
+ only the old "ignore" flag was checked (which would currently
+ still work properly, but is deprecated).
+
+04/29/2007: gga
+ [Ruby - but should be made generic]
+
+ %feature("numoutputs","0") added.
+
+ This feature allows you to ignore the output of a function so
+ that it is not added to a list of output values
+ ( ie. argouts ).
+ This should also become a feature of %typemap(directorout)
+ as "numoutputs"=0, just like "numinputs"=0 exists.
+
+ %feature("directors"=1)
+
+ %include <typemaps.i>
+
+ %feature("numoutputs","0") { Class::member_function1 };
+ %typemap(out) MStatus { // some code, like check mstatus
+ // and raise exception if wrong };
+
+ %inline %{
+ typedef int MStatus;
+ class Class {
+
+ // one argument returned, but director out code added
+ // MStatus is discarded as a return (out) parameter.
+ virtual MStatus member_function1( int& OUTPUT );
+
+ // two arguments returned, director out code added
+ // MStatus is not discarded
+ virtual MStatus member_function2( int& OUTPUT );
+ };
+ %}
+
+
+04/21/2007: olly
+ Fix parsing of float constants with an exponent (e.g. 1e-02f)
+ (bug #1699646).
+
+04/20/2007: olly
+ [Python] Fix lack of generation of docstrings when -O is used.
+ Also, fix generation of docstrings containing a double quote
+ character. Patch from Richard Boulton in bug#1700146.
+
+04/17/2007: wsfulton
+ [Java, C#] Support for adding in Java/C# code before and after the intermediary call,
+ specifically related to the marshalling of the proxy type to the intermediary type.
+ The javain/csin typemap now supports the 'pre' and 'post' attributes to achieve this.
+ The javain typemap also supports an optional 'pgcppname' attribute for premature garbage
+ collection prevention parameter naming and the csin typemap supports an optional 'cshin'
+ attribute for the parameter type used in a constructor helper generated when the type is used
+ in a constructor. Details in the Java.html and CSharp.html documentation.
+
+04/16/2007: olly
+ Don't treat `restrict' as a reserved identifier in C++ mode
+ (bug#1685534).
+
+04/16/2007: olly
+ [PHP5] Fix how zend_throw_exception() is called (bug #1700785).
+
+04/10/2007: olly
+ Define SWIGTEMPLATEDISAMBIGUATOR to template for aCC (reported on
+ swig-user that this is needed).
+
+04/04/2007: olly
+ [PHP5] If ZTS is enabled, release <module>_globals_id in MSHUTDOWN
+ to avoid PHP interpreter crash on shutdown. This solution was
+ suggested here: http://bugs.php.net/bug.php?id=40985
+
+04/03/2007: olly
+ [PHP4] Add missing ZTS annotations to generated C++ wrapper code
+ to fix compilation failures when using ZTS enabled SWIG (Linux
+ distributions tend to disable ZTS, but notably the Windows build
+ uses it by default).
+
+04/01/2007: efuzzyone
+ [CFFI] Patch #1684261: fixes handling of unsigned int literals, thanks Leigh Smith.
+ Also, improved documentation.
+
+03/30/2007: olly
+ Avoid generating '<:' token when using SwigValueWrapper<> on a type
+ which starts with '::' (patch #1690948).
+
+03/25/2007: wuzzeb (John Lenz)
+ [perl5] Add SWIG_fail to the SWIG_exception macro. Fixes a few problems reported
+ on the mailing list.
+
+03/23/2007: wsfulton
+ String copying patch from Josh Cherry reducing memory consumption by about 25%.
+
+03/21/2007: wsfulton
+ [Java] Apply patch #1631987 from Ulrik Peterson - bool INOUT typemaps
+ fail on big endian machines.
+
+03/16/2007: wsfulton
+ Fix seg fault given dodgy C++ code: namespace abc::def { }
+
+03/16/2007: wsfulton
+ [Java] Fixes so that ARRAYSOFCLASSES and ARRAYSOFENUMS in arrays_java.i can be applied
+ to pointer types.
+
+03/03/2007: olly
+ [PHP5] When we know the literal numeric value for a constant, use
+ that to initialise the const member in the PHP wrapper class.
+
+03/02/2007: olly
+ [PHP5] Fix PHP wrapper code generated for certain cases of
+ overloaded forms with default arguments.
+
+02/26/2007: efuzzyone
+ [CFFI] Patch #1656395: fixed hex and octal values bug, thanks to Arthur Smyles.
+
+02/22/2007: mgossage
+ [Lua] Fixed bug in typemaps which caused derived_byvalue and rname test cases to fail.
+ Updated derived_byvalue.i to explain how to find and fix the problem
+
+01/25/2007: wsfulton
+ Fix #1538522 and #1338527, forward templated class declarations without a
+ name for the templated class parameters, such as:
+
+ template <typename, class> class X;
+
+01/23/2007: mgossage
+ [Lua] Patch #1640862: <malloc.h> replaced by <stdlib.h>
+ Patch #1598063 Typo in typemaps.i
+
+01/22/2007: mgossage
+ [Lua] Added a lua specific carrays.i which adds the operator[] support.
+ modified the main code to make it not emit all the class member functions & accessors
+ Note: C structs are created using new_XXX() while C++ classes use XXX() (should be standardised)
+ Updated test case: li_carrays
+ Updated the documentation.
+
+01/12/2007: wsfulton
+ [Php] Add support for newfree typemaps (sometimes used by %newobject)
+
+01/12/2007: beazley
+ New command line option -macroerrors. When supplied, this will force
+ the C scanner/parser to report proper location information for code contained
+ inside SWIG macros (defined with %define). By default, SWIG merely reports
+ errors on the line at which a macro is used. With this option, you
+ can expand the error back to its source---something which may simplify
+ debugging.
+
+01/12/2007: beazley
+ [Internals] Major overhaul of C/C++ scanning implementation. For quite
+ some time, SWIG contained two completely independent C/C++ tokenizers--
+ the legacy scanner in CParse/cscanner.c and a general purpose scanner
+ in Swig/scanner.c. SWIG still has two scanning modules, but the C parser
+ scanner (CParse/cscanner.c) now relies upon the general purpose
+ scanner found in Swig/scanner.c. As a result, it is much smaller and
+ less complicated. This change also makes it possible to maintain all
+ of the low-level C tokenizing in one central location instead of two
+ places as before.
+
+ ***POTENTIAL FLAKINESS***
+ This change may cause problems with accurate line number reporting
+ as well as error reporting more generally. I have tried to resolve this
+ as much as possible, but there might be some corner cases.
+
+01/12/2007: mgossage
+ [Lua] Added typemap throws for std::string*, typemap for SWIGTYPE DYNAMIC,
+ changed the existing throws typemap to throw a string instead of making a copy of
+ the object (updating a few test cases to deal with the change).
+ fixed test case: dynamic_casts, exception_partial_info, li_std_string, size_t
+
+01/03/2007: beazley
+ [Internals]. Use of swigkeys.c/.h variables is revoked. Please use
+ simple strings for attribute names.
+
+12/30/2006: beazley
+ Internal API functions HashGetAttr() and HashCheckAttr() have been revoked.
+ Please use Getattr() to retrieve attributes. The function Checkattr() can
+ be used to check attributes. Note: These functions have been revoked
+ because they only added a marginal performance improvement at the expense
+ code clarity.
+
+12/26/2006: mgossage
+ [Lua] Added more STL (more exceptions, map, size_t),
+ fixed test case: conversion_ns_template.
+
+12/21/2006: mgossage
+ [Lua] Update to throw errors when setting immutables,
+ and allowing user addition of module variables.
+
+12/20/2006: wsfulton
+ Fix typedef'd variable wrappers that use %naturalvar, eg, std::string.
+
+12/14/2006: wsfulton
+ [C#] Add std::wstring and wchar_t typemaps
+
+12/14/2006: olly
+ [php] Fix bug #1613673 (bad PHP5 code generated for getters and
+ setters).
+
+12/02/2006: wsfulton, John Lenz, Dave Beazley
+ Move from cvs to Subversion for source control
+
+11/30/2006: beazley
+ Cleaned up swigwarnings.swg file not to use nested macro
+ definitions.
+
+11/12/2006: wsfulton
+ [Java, C#] Fix for %extend to work for static member variables.
+
+Version 1.3.31 (November 20, 2006)
+==================================
+
+11/12/2006: Luigi Ballabio
+ [Python] Alternate fix for Python exceptions bug #1578346 (the previous one broke Python
+ properties in modern classes)
+
+11/12/2006: wsfulton
+ -fakeversion commandline option now generates the fake version into the generated wrappers
+ as well as displaying it when the -version commandline option is used.
+
+14/11/2006: mgossage
+ [lua] update to typemap for object by value, to make it c89 compliant
+
+Version 1.3.30 (November 13, 2006)
+==================================
+
+11/12/2006: wsfulton
+ [java] Remove DetachCurrentThread patch from 08/11/2006 - it causes segfaults
+ on some systems.
+
+11/12/2006: wsfulton
+ [python] Fix #1578346 - Python exceptions with -modern
+
+11/10/2006: wsfulton
+ Fix #1593291 - Smart pointers and inheriting from templates
+
+11/09/2006: wsfulton
+ Fix director operator pointer/reference casts - #1592173.
+
+11/07/2006: wsfulton
+ Add $self special variable for %extend methods. Please use this instead of just 'self'
+ as the C++ 'this' pointer.
+
+11/07/2006: mutandiz
+ [allegrocl]
+ allegrocl.swg: swig-defvar updated to allow specifying of
+ non-default foreign type (via :ftype keyword arg).
+ allegrocl.cxx: Specify proper access type for enum values.
+
+11/03/2006: wsfulton
+ [Java/C#] Fix const std::string& return types for directors as reported by
+ Mark Donselzmann
+
+10/29/2006: wsfulton
+ [Java] Remove DeleteLocalRef from end of director methods for now as it is causing a
+ seg fault when run on Solaris 8.
+
+10/29/2006: wuzzeb (John Lenz)
+ [Guile] Patch from Chris Shoemaker to clean up some warnings in the generated code.
+
+10/29/2006: wsfulton
+ [Java] Important fix to prevent early garbage collection of the Java proxy class
+ while it is being used in a native method. The finalizer could destroy the underlying
+ C++ object while it was being used. The problem occurs when the proxy class is no
+ longer strongly reachable after a native call. The problem seems to occur in
+ memory stress situations on some JVMs. It does not seem to occur on the
+ Sun client JVM up to jdk 1.5. However the 1.6 client jdk has a more aggressive garbage
+ collector and so the problem does occur. It does occur on the Sun server
+ JVMs (certainly 1.4 onwards). The fix entails passing the proxy class into the native
+ method in addition to the C++ pointer in the long parameter, as Java classes are not
+ collected when they are passed into JNI methods. The extra parameter can be suppressed
+ by setting the nopgcpp attribute in the jtype typemap to "1" or using the new -nopgcpp
+ commandline option.
+
+ See Java.html#java_pgcpp for further details on this topic.
+
+10/24/2006: wsfulton
+ [C#] Fix smart pointer wrappers. The virtual/override/new keyword is not generated
+ for each method as the smart pointer class does not mirror the underlying pointer
+ class inheritance hierarchy. SF #1496535
+
+10/24/2006: mgossage
+ [lua] added support for native methods & member function pointers.
+ fixed test cases arrays_dimensionless & cpp_basic. Added new example (functor).
+ tidied up a little of the code (around classHandler).
+
+10/17/2006: wsfulton
+ [C#, Java] directorout typemap changes to fall in line with the other director
+ languages. $result is now used where $1 used to be used. Please change your typemaps
+ if you have a custom directorout typemap.
+
+10/18/2006: wsfulton
+ Some fixes for applying the char array typemaps to unsigned char arrays.
+
+10/17/2006: wsfulton
+ [C#, Java] Add in const size_t& and const std::size_t& typemaps.
+
+10/15/2006: efuzzyone
+ [CFFI] Suppress generating defctype for enums, thanks to Arthur Smyles. Patch 1560983.
+
+10/14/2006: wuzzeb (John Lenz)
+ [Chicken] Minor fix to make SWIG work with the (as yet unreleased) chicken 2.5
+
+ [Guile,Chicken] Fix SF Bug 1573892. Added an ext_test to the test suite to test
+ this bug, but this test can not really be made generic because the external code must
+ plug into the target language interpreter directly.
+ See Examples/test-suite/chicken/ext_test.i and ext_test_external.cxx
+
+ Added a %.externaltest to common.mk, and any interested language modules can
+ copy and slightly modify either the chicken or the guile ext_test.i
+
+10/14/2006: mgossage
+ [Lua] added OUTPUT& for all number types, added a long long type
+ fixed several test cases.
+ update: changed typemaps to use SWIG_ConvertPtr rather than SWIG_MustGetPointer
+ started spliting lua.swg into smaller parts to make it neater
+
+10/13/2006: wsfulton
+ [C#, Java] Marginally better support for multiple inheritance only in that you can
+ control what the base class is. This is done using the new 'replace' attribute in the
+ javabase/csbase typemap, eg in the following, 'Me' will be the base class,
+ no matter what Foo is really derived from in the C++ layer.
+
+ %typemap(javabase, replace="1") Foo "Me"
+ %typemap(csbase, replace="1") Foo "Me"
+
+ Previously it was not possible for the javabase/csbase typemaps to override the C++ base.
+
+10/12/2006: wsfulton
+ [Java] Remove potential race condition on the proxy class' delete() method
+ (it is now a synchronized method, but is now customisable by changing the
+ methodmodifiers attribute in the javadestruct or javadestruct_derived typemap)
+
+ [C#] Remove potential race condition on the proxy class' Dispose() method,
+ similar to Java's delete() above.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+10/12/2006: wsfulton
+ [Ruby, Python] Remove redundant director code in %extend methods (%extend
+ methods cannot be director methods)
+
+10/12/2006: wsfulton
+ [Ruby, Python] Fix #1505594 - director objects not returned as director objects
+ in %extend methods.
+
+10/11/2006: wsfulton
+ [Java] Fix #1238798 - Directors using unsigned long long or any other type
+ marshalled across the JNI boundary using a Java class (where the jni typemap
+ contains jobject).
+
+10/06/2006: wsfulton
+ Fix #1162194 - #include/%include within a structure
+
+10/06/2006: wsfulton
+ Fix #1450661, string truncation in String_seek truncating Java/C# enums.
+
+10/06/2006: mgossage
+ [Lua] Fix #1569587. The name is now correct.
+
+10/04/2006: wsfulton
+ Director fixes for virtual conversion operators
+
+10/04/2006: olly
+ [php] Fix #1569587 for PHP. Don't use sizeof() except with string
+ literals. Change some "//" comments to "/* */" for portability.
+
+10/04/2006: mgossage
+ [Lua] Partial Fix #1569587. The type is now correct, but the name is still not correct.
+
+10/03/2006: wsfulton
+ [Ruby] Fix #1527885 - Overloaded director virtual methods sometimes produced
+ uncompilable code when used with the director:except feature.
+
+10/03/2006: wsfulton
+ Directors: Directors are output in the order in which they are declared in
+ the C++ class rather than in some pseudo-random order.
+
+10/03/2006: mmatus
+ Fix #1486281 and #1471039.
+
+10/03/2006: olly
+ [Perl] Fix for handling strings with zero bytes from Stephen Hutsal.
+
+09/30/2006: efuzzyone
+ [CFFI] Bitfield support and vararg support due to Arthur Smyles.
+ C expression to Lisp conversion, thanks to Arthur Smyles for the initial
+ idea, it now supports conversion for a whole range of C expressions.
+
+09/28/2006: wsfulton
+ Fix #1508327 - Overloaded methods are hidden when using -fvirtual optimisation.
+ Overloaded methods are no longer candidates for elimination - this mimics
+ C++ behaviour where all overloaded methods must be defined and implemented
+ in a derived class in order for them to be available.
+
+09/25/2006: wsfulton
+ [Ruby, Python, Ocaml] Fix #1505591 Throwing exceptions in extended directors
+
+09/25/2006: wsfulton
+ Fix #1056100 - virtual operators.
+
+09/24/2006: olly
+ Don't accidentally create a "<:" token (which is the same as "[" in C++).
+ Fixes bug # 1521788.
+
+09/23/2006: olly
+ [Ruby] Support building with recent versions of the Ruby 1.9
+ development branch. Fixes bug #1560092.
+
+09/23/2006: olly
+ Templates can now be instantiated using negative numbers and
+ constant expressions, e.g.:
+
+ template<int q> class x {};
+ %template(x_minus1) x<-1>;
+ %template(x_1plus2) x<1+2>;
+
+ Also, constant expressions can now include comparisons (>, <, >=,
+ <=, !=, ==), modulus (%), and ternary conditionals (a ? b : c).
+
+ Fixes bugs #646275, #925555, #956282, #994301.
+
+09/22/2006: wsfulton
+ Fix %ignore on director methods - Bugs #1546254, #1543533
+
+09/20/2006: wsfulton
+ Fix %ignore on director constructors
+
+09/20/2006: wsfulton
+ Fix seg faults and asserts when director methods are ignored (#1543533)
+
+09/20/2006: wsfulton
+ Fix out of source builds - bug #1544718
+
+09/20/2006: olly
+ Treat a nested class definition as a forward declaration rather
+ than ignoring it completely, so that we generate correct code for
+ passing opaque pointers to the nested class (fixes SF bug #909387).
+
+09/20/2006: olly
+ *** POTENTIAL INCOMPATIBILITY ***
+ [php] Overload resolution now works. However to allow this, SWIG
+ generated wrappers no longer coerce PHP types (which reverts a change
+ made in 1.3.26). So for example, if a method takes a string, you
+ can no longer pass a number without explicitly converting it to a
+ string in PHP using: (string)x
+
+09/18/2006: mgossage
+ [ALL] fix on swiginit.swg, has been reported to crash on several test cases
+ found and fixed problem in imports under python (mingw)
+
+09/16/2006: wsfulton
+ [Python] Patch from Michal Marek for Python 2.5 to fix 64 bit array indexes on
+ 64 bit machines.
+
+09/13/2006: wsfulton
+ The explicitcall feature has been scrapped. This feature was introduced primarily
+ to solve recursive director method calls. Director upcall improvements made instead:
+
+ [Python, Ruby, Ocaml] The swig_up flag is no longer used. The required mutexes
+ wrapping this flag are also no longer needed. The recursive calls going from C++
+ to the target language and back again etc are now avoided by a subtlely different
+ approach. Instead of using the swig_up flag in each director method to indicate
+ whether the explicit C++ call to the appropriate base class method or a normal
+ polymorphic C++ call should be made, the new approach makes one of these calls
+ directly from the wrapper method.
+
+ [Java, C#] The recursive call problem when calling a C++ base class method from
+ Java/C# is now fixed. The implementation is slightly different to the other languages
+ as the detection as to whether the explicit call or a normal polymorphic call is made
+ in the Java/C# layer rather than in the C++ layer.
+
+09/11/2006: mgossage
+ [ALL] updated swiginit.swg to allow multiple interpreters to use multiple
+ swig modules at once. This has been tested in Lua (mingw & linux),
+ perl5 & python (linux) only.
+
+09/11/2006: mgossage
+ [lua] added support for passing function pointers as well as native lua object
+ into wrappered function.
+ Added example funcptr3 to demonstrate this feature
+
+09/05/2006: olly
+ [php] Rename ErrorCode and ErrorMsg #define-s to SWIG_ErrorCode
+ and SWIG_ErrorMsg to avoid clashes with code the user might be
+ wrapping (patch from Darren Warner in SF bug #1466086). Any
+ user typemaps which use ErrorCode and/or ErrorMsg directly will
+ need adjusting - you can easily fix them to work with both old
+ and new SWIG by changing to use SWIG_ErrorMsg and adding:
+
+ #ifndef SWIG_ErrorMsg
+ #define SWIG_ErrorMsg() ErrorMsg()
+ #endif
+
+08/29/2006: olly
+ [php] Move constant initialisation from RINIT to MINIT to fix a
+ warning when using Apache and mod_php. We only need to create
+ PHP constants once when we're first initialised, not for every HTTP
+ request.
+
+08/21/2006: mgossage
+ [Lua]
+ Bugfix #1542466 added code to allow mapping Lua nil's <-> C/C++ NULL's
+ updated various typemaps to work correctly with the changes
+ added voidtest_runme.lua to show the features working
+
+08/19/2006: wuzzeb (John Lenz)
+ [Guile] Add feature:constasvar to export constants as variables instead of functions
+ that return the constant value.
+
+08/11/2006: wsfulton
+ [Java] DetachCurrentThread calls have been added so that natively created threads
+ no longer prevent the JVM from exiting. Bug reported by Thomas Dudziak and
+ Paul Noll.
+
+08/10/2006: wsfulton
+ [C#] Fix director protected methods so they work
+
+07/25/2006: mutandiz
+ [allegrocl]
+ more additions to std::string, some tweaks and small bug fixes
+ -nocwrap mode.
+
+07/21/2006: mgossage
+ [Lua]
+ Bugfix #1526022 pdated std::string to support strings with '\0' inside them
+ updated typemaps.i to add support for pointer to pointers
+
+07/19/2006: mutandiz
+ [allegrocl]
+ - Add std_string.i support.
+ - Add newobject patch submitted by mkoeppe (thanks!)
+ - Fix type name mismatch issue for nested type definitions.
+ specifically typedefs in templated class defns.
+
+07/18/2006: mgossage
+ Bugfix #1522858
+ updated lua.cxx to support -external-runtime command
+
+07/14/2006: wuzzeb (John Lenz)
+ Increment the SWIG_RUNTIME_VERSION to 3, because of the
+ addition of the owndata member in swig_type_info.
+ Reported by: Prabhu Ramachandran
+
+07/05/2006: wsfulton
+ Search path fixes:
+ - Fix search path for library files to behave as documented in Library.html.
+ - Fix mingw/msys builds which did not find the SWIG library when installed.
+ - Windows builds also output the mingw/msys install location when running
+ swig -swiglib.
+ - The non-existent and undocumented config directory in the search path has
+ been removed.
+
+07/05/2006: wsfulton
+ Fix $symname special variable expansion.
+
+07/04/2006: wuzzeb (John Lenz)
+ [Chicken]
+ Add %feature("constasvar"), which instead of exporting a constant as a
+ scheme function, exports the constant as a scheme variable. Update the
+ documentation as well.
+
+07/04/2006: wsfulton
+ [See entry of 09/13/2006 - explicitcall feature and documentation to it removed]
+ New explicitcall feature which generates additional wrappers for virtual methods
+ that call the method explicitly, not relying on polymorphism to make the method
+ call. The feature is a feature flag and is enabled like any other feature flag.
+ It also recognises an attribute, "suffix" for mangling the feature name, see
+ SWIGPlus.html#SWIGPlus_explicitcall documentation for more details.
+
+ [Java, C#]
+ The explicitcall feature is also a workaround for solving the recursive calls
+ problem when a director method makes a call to a base class method. See
+ Java.html#java_directors_explicitcall for updated documentation.
+
+06/28/2006: joe (Joseph Wang)
+ [r] Initial support for R
+
+06/20/2006: wuzzeb (John Lenz)
+ [Chicken]
+ Minor fixes to get apply_strings.i testsuite to pass
+ Remove integers_runme.scm from the testsuite, because SWIG and Chicken does
+ handle overflows.
+
+06/19/2005: olly
+ [php] Add support for generating PHP5 class wrappers for C++
+ classes (use "swig -php5").
+
+06/17/2006: olly
+ [php] Added some missing keywords to the PHP4 keyword list, and
+ fixed __LINE__ and __FILE__ which were in the wrong category.
+ Also added all the keywords new in PHP5, and added comments
+ noting the PHP4 keywords which aren't keywords in PHP5.
+
+06/17/2006: olly
+ [php] Don't segfault if PHP Null is passed as this pointer (e.g.
+ Class_method(Null)) - give a PHP Error instead.
+
+06/15/2006: mutandiz
+ [allegrocl]
+ Add initial support for std::list container class.
+ Fix a few bugs in helper functions.
+
+05/13/2006: wsfulton
+ [Java] Replace JNIEXPORT with SWIGEXPORT, thereby enabling the possibility
+ of using gcc -fvisibility=hidden for potentially smaller faster loading wrappers.
+
+05/13/2006: wsfulton
+ Fix for Makefiles for autoconf-2.60 beta
+
+05/13/2006: wsfulton
+ Vladimir Menshakov patch for compiling wrappers with python-2.5 alpha.
+
+05/12/2006: wsfulton
+ Fix buffer overflow error when using large %feature(docstring) reported
+ by Joseph Winston.
+
+05/12/2006: wsfulton
+ [Perl] Operator overload fix from Daniel Moore.
+
+05/25/2006: mutandiz
+ [allegrocl]
+ Fix bug in generation of CLOS type declarations for unions
+ and equivalent types.
+
+05/24/2006: mutandiz
+ [allegrocl]
+ Don't require a full class definition to generate a CLOS wrapper.
+
+05/20/2006: olly
+ [php] GCC Visibility support now works with PHP.
+
+05/19/2006: olly
+ [php] Removed support for -dlname (use -module instead). Fixed
+ naming of PHP extension module to be consistent with PHP
+ conventions (no "php_" prefix on Unix; on PHP >= 4.3.0, handle Unix
+ platforms which use something other than ".so" as the extension.)
+
+05/13/2006: wsfulton
+ [C#] Director support added
+
+05/07/2006: olly
+ [php] Don't segfault if PHP Null is passed where a C++ reference
+ is wanted.
+
+05/05/2006: olly
+ [php] Fix wrappers generated for global 'char' variables to not
+ include a terminating zero byte in the PHP string.
+
+05/03/2006: wsfulton
+ Modify typemaps so that char * can be applied to unsigned char * or signed char *
+ types and visa versa.
+
+05/03/2006: efuzzyone
+ [cffi]Thanks to Luke J Crook for this idea.
+ - a struct/enum/union is replaced with :pointer only if
+ that slot is actually a pointer to that type. So,:
+ struct a_struct { int x; } and
+ struct b_struct { a_struct struct_1; };
+ will be converted as:
+ (cffi:defcstruct b_struct
+ (struct_1 a_struct))
+ - Other minor fixes in lispifying names.
+
+05/02/2006: wsfulton
+ Fix possible redefinition of _CRT_SECURE_NO_DEPRECATE for VC++.
+
+04/14/2006: efuzzyone
+ [cffi]
+ Thanks to Thomas Weidner for the patch.
+ - when feature export is set (export 'foo) is
+ generated for every symbol
+ - when feature inline is set (declaim (inline foo)) is
+ generated before every function definition
+ - when feature intern_function is set
+ #.(value-of-intern-function "name" "nodeType" package)
+ is emitted instead of the plain symbol. A sample swig-lispify
+ is provided.
+ - every symbol is prefixed by it's package.
+
+04/13/2006: efuzzyone
+ [cffi]
+ Fixed the generation of wrappers for global variables.
+ Added the option [no]swig-lisp which turns on/off generation
+ of code for swig helper lisp macro, functions, etc.
+
+Version 1.3.29 (March 21, 2006)
+===============================
+
+04/05/2006: mutandiz
+ [allegrocl]
+ Fix output typemap of char so it produces a character instead
+ of an integer. Also adds input/output typemaps for 'char *'.
+
+ add command-line argument -isolate to generate an interface
+ file that won't interfere with other SWIG generated files that
+ may be used in the same application.
+
+03/20/2005: mutandiz
+ [allegrocl]
+ More tweaks to INPUT/OUTPUT typemaps for bool.
+
+ Fix constantWrapper for char and string literals.
+
+ find-definition keybindings should work in ELI/SLIME.
+ Output (in-package <module-name>) to lisp wrapper
+ instead of (in-package #.*swig-module-name*).
+
+ slight rework of multiple return values.
+
+ doc updates.
+
+03/17/2005: mutandiz
+ [allegrocl]
+ mangle names of constants generated via constantWrapper.
+
+ When using OUTPUT typemaps and the function has a non-void
+ return value, it should be first in the values-list, followed
+ by the OUTPUT mapped values.
+
+ Fix bug with boolean parameters, which needed to be
+ passed in as int values, rather than T or NIL.
+
+03/15/2006: mutandiz
+ [allegrocl]
+ Generate wrappers for constants when in C++ or -cwrap mode.
+ Make -cwrap the default, since it is most correct. Users
+ can use the -nocwrap option to avoid the creation of a .cxx
+ file when interfacing to C code.
+
+ When in -nocwrap mode, improve the handling of converting
+ infix literals to prefix notation for lisp. This is very
+ basic and not likely to be improved upon since this only
+ applies to the -nocwrap case. Literals we can't figure out
+ will result in a warning and be included in the generated
+ code.
+
+ validIdentifier now more closely approximates what may be
+ a legal common lisp symbol.
+
+ Fix typemap error in allegrocl.swg
+
+03/12/2006: mutandiz
+ [allegrocl]
+ fix up INPUT/OUTPUT typemaps for bool.
+ Generate c++ style wrapper functions for struct/union members
+ when -cwrap option specified.
+
+03/10/2006: mutandiz
+ [allegrocl]
+ Fix bug in C wrapper generation introduced by last allegrocl
+ commit.
+
+03/10/2006: wsfulton
+ [Java]
+ Commit #1447337 - Delete LocalRefs at the end of director methods to fix potential leak
+
+03/10/2006: wsfulton
+ Fix #1444949 - configure does not honor --program-prefix.
+ Removed non-standard configure option --with-release-suffix. Fix the autoconf standard
+ options --program-prefix and --program-suffix which were being shown in the help,
+ but were being ignored. Use --program-suffix instead of --with-release-suffix now.
+
+03/10/2006: wsfulton
+ [Java]
+ Fix #1446319 with patch from andreasth - more than one wstring parameter in director methods
+
+03/07/2006: mkoeppe
+ [Guile]
+ Fix for module names containing a "-" in non-"shadow" mode.
+ Patch from Aaron VanDevender (#1441474).
+
+03/04/2006: mmatus
+ - Add -O to the main program, which now enables -fastdispatch
+
+ [Python]
+
+ - Add the -fastinit option to enable faster __init__
+ methods. Setting 'this' as 'self.this.append(this)' in the python
+ code confuses PyLucene. Now the initialization is done in the
+ the C++ side, as reported by Andi and Robin.
+
+ - Add the -fastquery option to enable faster SWIG_TypeQuery via a
+ python dict cache, as proposed by Andi Vajda
+
+ - Avoid to call PyObject_GetAttr inside SWIG_Python_GetSwigThis,
+ since this confuses PyLucene, as reported by Andi Vajda.
+
+03/02/2006: wsfulton
+ [Java]
+ Removed extra (void *) cast when casting pointers to and from jlong as this
+ was suppressing gcc's "dereferencing type-punned pointer will break strict-aliasing rules"
+ warning. This warning could be ignored in versions of gcc prior to 4.0, but now the
+ warning is useful as gcc -O2 and higher optimisation levels includes -fstrict-aliasing which
+ generates code that doesn't work with these casts. The assignment is simply never made.
+ Please use -fno-strict-aliasing to both suppress the warning and fix the bad assembly
+ code generated. Note that the warning is only generated by the C compiler, but not
+ the C++ compiler, yet the C++ compiler will also generate broken code. Alternatively use
+ -Wno-strict-aliasing to suppress the warning for gcc-3.x. The typemaps affected
+ are the "in" and "out" typemaps in java.swg and arrays_java.swg. Users ought to fix
+ their own typemaps to do the same. Note that removal of the void * cast simply prevents
+ suppression of the warning for the C compiler and nothing else. Typical change:
+
+ From:
+ %typemap(in) SWIGTYPE * %{ $1 = *($&1_ltype)(void *)&$input; %}
+ To:
+ %typemap(in) SWIGTYPE * %{ $1 = *($&1_ltype)&$input; %}
+
+ From:
+ %typemap(out) SWIGTYPE * %{ *($&1_ltype)(void *)&$result = $1; %}
+ To:
+ %typemap(out) SWIGTYPE * %{ *($&1_ltype)&$result = $1; %}
+
+03/02/2006: mkoeppe
+ [Guile -scm]
+ Add typemaps for "long long"; whether the generated code compiles, however, depends
+ on the version and configuration of Guile.
+
+03/02/2006: wsfulton
+ [C#]
+ Add support for inner exceptions. If any of the delegates are called which construct
+ a pending exception and there is already a pending exception, it will create the new
+ exception with the pending exception as an inner exception.
+
+03/02/2006: wsfulton
+ [Php]
+ Added support for Php5 exceptions if compiling against Php5 (patch from Olly Betts).
+
+03/01/2006: mmatus
+ Use the GCC visibility attribute in SWIGEXPORT.
+
+ Now you can compile (with gcc 3.4 or later) using
+ CFLAGS="-fvisibility=hidden".
+
+ Check the difference for the 'std_containers.i' python
+ test case:
+
+ Sizes:
+
+ 3305432 _std_containers.so
+ 2383992 _std_containers.so.hidden
+
+ Exported symbols (nm -D <file>.so | wc -l):
+
+ 6146 _std_containers.so
+ 174 _std_containers.so.hidden
+
+ Execution times:
+
+ real 0m0.050s user 0m0.039s sys 0m0.005s _std_containers.so
+ real 0m0.039s user 0m0.026s sys 0m0.007s _std_containers.so.hidden
+
+ Read http://gcc.gnu.org/wiki/Visibility for more details.
+
+
+02/27/2006: mutandiz
+ [allegrocl]
+ Add support for INPUT, OUTPUT, and INOUT typemaps.
+ For OUTPUT variables, the lisp wrapper returns multiple
+ values.
+
+02/26/2006: mmatus
+
+ [Ruby] add argcargv.i library file.
+
+ Use it as follow:
+
+ %include argcargv.i
+
+ %apply (int ARGC, char **ARGV) { (size_t argc, const char **argv) }
+
+ %inline {
+ int mainApp(size_t argc, const char **argv)
+ {
+ return argc;
+ }
+ }
+
+ then in the ruby side:
+
+ args = ["asdf", "asdf2"]
+ n = mainApp(args);
+
+
+ This is the similar to the python version Lib/python/argcargv.i
+
+02/24/2006: mgossage
+
+ Small update Lua documents on troubleshooting problems
+
+02/22/2006: mmatus
+
+ Fix all the errors reported for 1.3.28.
+ - fix bug #1158178
+ - fix bug #1060789
+ - fix bug #1263457
+ - fix 'const char*&' typemap in the UTL, reported by Geoff Hutchison
+ - fixes for python 2.1 and the runtime library
+ - fix copyctor + template bug #1432125
+ - fix [ 1432152 ] %rename friend operators in namespace
+ - fix gcc warning reported by R. Bernstein
+ - avoid assert when finding a recursive scope inheritance,
+ emit a warning in the worst case, reported by Nitro
+ - fix premature object deletion reported by Paul in tcl3d
+ - fix warning reported by Nitro in VC7
+ - more fixes for old Solaris compiler
+ - fix for python 2.3 and gc_refs issue reported by Luigi
+ - fix fastproxy for methods using kwargs
+ - fix overload + protected member issue reported by Colin McDonald
+ - fix seterrormsg as reported by Colin McDonald
+ - fix directors, now the test-suite runs again using -directors
+ - fix for friend operator and Visual studio and bug 1432152
+ - fix bug #1435090
+ - fix using + %extend as reported by William
+ - fix bug #1094964
+ - fix for Py_NotImplemented as reported by Olly and Amaury
+ - fix nested namespace issue reported by Charlie
+
+ and also:
+
+ - allow director protected members by default
+ - delete extra new lines in swigmacros[UTL]
+ - cosmetic for generated python code
+ - add the factory.i library for UTL
+ - add swigregister proxy method and move __repr__ to a
+ single global module [python]
+
+02/22/2006: mmatus
+
+ When using directors, now swig will emit all the virtual
+ protected methods by default.
+
+ In previous releases, you needed to use the 'dirprot'
+ option to achieve the same.
+
+ If you want, you can disable the new default behaviour,
+ use the 'nodirprot' option:
+
+ swig -nodirprot ...
+
+ and/or the %nodirector feature for specific methods, i.e.:
+
+ %nodirector Foo::bar;
+
+ struct Foo {
+ virtual ~Foo();
+
+ protected:
+ virtual void bar();
+ };
+
+
+ As before, pure abstract protected members are allways
+ emitted, independent of the 'dirprot/nodirprot' options.
+
+
+02/22/2006: mmatus
+ Add the factory.i library for languages using the UTL (python,tcl,ruby,perl).
+
+ factory.i implements a more natural wrap for factory methods.
+
+ For example, if you have:
+
+ ---- geometry.h --------
+ struct Geometry {
+ enum GeomType{
+ POINT,
+ CIRCLE
+ };
+
+ virtual ~Geometry() {}
+ virtual int draw() = 0;
+
+ //
+ // Factory method for all the Geometry objects
+ //
+ static Geometry *create(GeomType i);
+ };
+
+ struct Point : Geometry {
+ int draw() { return 1; }
+ double width() { return 1.0; }
+ };
+
+ struct Circle : Geometry {
+ int draw() { return 2; }
+ double radius() { return 1.5; }
+ };
+
+ //
+ // Factory method for all the Geometry objects
+ //
+ Geometry *Geometry::create(GeomType type) {
+ switch (type) {
+ case POINT: return new Point();
+ case CIRCLE: return new Circle();
+ default: return 0;
+ }
+ }
+ ---- geometry.h --------
+
+
+ You can use the %factory with the Geometry::create method as follows:
+
+ %newobject Geometry::create;
+ %factory(Geometry *Geometry::create, Point, Circle);
+ %include "geometry.h"
+
+ and Geometry::create will return a 'Point' or 'Circle' instance
+ instead of the plain 'Geometry' type. For example, in python:
+
+ circle = Geometry.create(Geometry.CIRCLE)
+ r = circle.radius()
+
+ where 'circle' now is a Circle proxy instance.
+
+
+02/17/2006: mkoeppe
+ [MzScheme] Typemaps for all integral types now accept the full range of integral
+ values, and they signal an error when a value outside the valid range is passed.
+ [Guile] Typemaps for all integral types now signal an error when a value outside
+ the valid range is passed.
+
+02/13/2006: mgossage
+ [Documents] updated the extending documents to give a skeleton swigging code
+ with a few typemaps.
+ [Lua] added an extra typemap for void* [in], so a function which requires a void*
+ can take any kind of pointer
+
+Version 1.3.28 (February 12, 2006)
+==================================
+
+02/11/2006: mmatus
+ Fix many issues with line counting and error reports.
+
+02/11/2006: mmatus
+ [Python] Better static data member support, if you have
+
+ struct Foo {
+ static int bar;
+ };
+
+ then now is valid to access the static data member, ie:
+
+ f = Foo()
+ f.bar = 3
+
+ just as in C++.
+
+
+02/11/2006: wsfulton
+ [Perl]
+ Fixed code generation to work again with old versions of Perl
+ (5.004 and later tested)
+
+02/04/2006: mmatus
+ [Python] Add the %extend_smart_pointer() directive to extend
+ SWIG smart pointer support in python.
+
+ For example, if you have a smart pointer as:
+
+ template <class Type> class RCPtr {
+ public:
+ ...
+ RCPtr(Type *p);
+ Type * operator->() const;
+ ...
+ };
+
+ you use the %extend_smart_pointer directive as:
+
+ %extend_smart_pointer(RCPtr<A>);
+ %template(RCPtr_A) RCPtr<A>;
+
+ then, if you have something like:
+
+ RCPtr<A> make_ptr();
+ int foo(A *);
+
+ you can do the following:
+
+ a = make_ptr();
+ b = foo(a);
+
+ ie, swig will accept a RCPtr<A> object where a 'A *' is
+ expected.
+
+ Also, when using vectors
+
+ %extend_smart_pointer(RCPtr<A>);
+ %template(RCPtr_A) RCPtr<A>;
+ %template(vector_A) std::vector<RCPtr<A> >;
+
+ you can type
+
+ a = A();
+ v = vector_A(2)
+ v[0] = a
+
+ ie, an 'A *' object is accepted, via implicit conversion,
+ where a RCPtr<A> object is expected. Additionally
+
+ x = v[0]
+
+ returns (and sets 'x' as) a copy of v[0], making reference
+ counting possible and consistent.
+
+ %extend_smart_pointer is just a collections of new/old
+ tricks, including %typemaps and the new %implicitconv
+ directive.
+
+02/02/2006: mgossage
+ bugfix #1356577, changed double=>lua_number in a few places.
+ added the std::pair wrapping
+
+01/30/2006: wsfulton
+ std::string and std::wstring member variables and global variables now use
+ %naturalvar by default, meaning they will now be wrapped as expected in all languages.
+ Previously these were wrapped as a pointer rather than a target language string.
+ It is no longer necessary to add the following workaround to wrap these as strings:
+
+ %apply const std::string & { std::string *}
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+01/28/2006: mkoeppe
+ [Guile -scm]
+ Add typemaps for handling of member function pointers.
+
+01/24/2006: mmatus
+ - Better support for the %naturalvar directive, now it
+ works with the scripting languages as well as
+ Java/C#.
+
+ Now, it can also be applied to class types:
+
+ %naturalvar std::string;
+ %include <std_string.i>
+
+ that will tell swig to use the 'natural' wrapping
+ mechanism to all std::string global and member
+ variables.
+
+ - Add support for the %allowexcept feature along the
+ scripting languages, which allows the %exception feature
+ to be applied to the variable access methods. Also, add
+ the %exceptionvar directive to specify a distintic
+ exception mechanism only for variables.
+
+
+ - Add more docs for the %delobject directive to mark a method as a
+ destructor, 'disowning' the first argument. For example:
+
+ %newobject create_foo;
+ %delobject destroy_foo;
+
+ Foo *create_foo() { return new Foo(); }
+ void destroy_foo(Foo *foo) { delete foo; }
+
+ or in a member method as:
+
+ %delobject Foo::destroy;
+
+ class Foo {
+ public:
+ void destroy() { delete this;}
+
+ private:
+ ~Foo();
+ };
+
+
+01/24/2006: mgossage
+ [Lua]
+ - Removed the type swig_lua_command_info & replace with luaL_reg
+ (which then broke the code), fixed this
+ - added an additional cast in the typemaps for enum's
+ due to the issue that VC.Net will not allow casting of
+ a double to an enum directly. Therefore cast to int then to enum
+ (thanks to Jason Rego for this observation)
+
+01/16/2006: mmatus (Change disabled... will be back in CVS soon)
+ Add initial support for regexp via the external library
+ RxSpencer. SWIG doesn't require this library to compile
+ and/or run. But if you specify --with-rxspencer, and the
+ library is found during installation, then swig will use
+ it in three places:
+
+ - In %renames rules, via the new rxsmatch rules, for example:
+
+ %rename("%(lowercase)",rxsmatch$name="GSL_.*") "";
+ %rename("%(lowercase)",rxsmatch$nodeType="enum GSL_.*") "";
+
+ rxsmatch is similar to the match rule, it just uses
+ the RxSpencer regexp library to decide if there is a
+ match with the provided regexp. As with the match
+ rule, you can also use the negate rule notrxsmatch.
+
+ - In the %rename target name via the rxstarget option, for example:
+
+ %rename("%(lowercase)",rxstarget=1) "GSL_.*";
+
+ where the target name "GSL.*" is now understood as a
+ regexp to be matched.
+
+ - In the new encoder "rxspencer", which looks like:
+
+ %(rxspencer:[regexp][replace])s
+
+ where "regexp" is the regular expression and "replace"
+ is a string used as a replacement, where the @0,@1,...,@9
+ pseudo arguments are used to represent the
+ corresponding matching items in the reg expression.
+
+ For example:
+
+ %(rxspencer:[GSL.*][@0])s <- Hello ->
+ %(rxspencer:[GSL.*][@0])s <- GSLHello -> GSLHello
+ %(rxspencer:[GSL(.*)][@1])s <- GSLHello -> Hello
+ %(rxspencer:[GSL(.*)][gsl@1])s <- GSLHello -> gslHello
+
+ Another example could be:
+
+ %rename("%(lowercase)s",sourcefmt="%(rxspencer:[GSL_(.*)][@1])s",%$isfunction) "";
+
+ which take out the prefix "GSL_" and returns all the
+ function names in lower cases, as following:
+
+ void GSL_Hello(); -> hello();
+ void GSL_Hi(); -> hi();
+ const int GSL_MAX; -> GSL_MAX; // no change, is not a function
+
+ We use the RxSpencer as an initial test bed to
+ implemention while we decide which library will be
+ finally added to swig.
+
+ You can obtain the RxSpencer library from
+
+ http://arglist.com/regex (Unix)
+
+ or
+
+ http://gnuwin32.sourceforge.net/packages.html (Windows)
+
+ Once installed, use "man rxspencer" to get more info
+ about the regexp format, or just google rxspencer.
+
+ Since now you can enable the rxsmatch rules (see above),
+ the simple or '|' support for the match rules
+ (01/12/2006: mmatus) is disabled. Still, if you have
+ problems with the rxspencer library, you can re-enable
+ the simple 'match or' support using
+ -DSWIG_USE_SIMPLE_MATCHOR.
+
+
+01/16/2006: mmatus
+ Change the %rename predicates to use the prefix '%$', as in:
+
+ %rename("%(utitle)s",%$isfunction,%$ismember) "";
+
+ to avoid clashes with other swig macros/directives.
+
+01/14/2006: cfisavage
+ [Ruby]
+ Added support for Ruby bang! methods via a new %bang feature.
+ Bang methods end in exclamation points and indicate that the
+ object being processed will be modified in-place as
+ opposed to being copied.
+
+01/12/2006: cfisavage
+ [Ruby]
+ Updated the Ruby module to automatically convert
+ method names to lower_case_with_underscores using the
+ new %rename functionality.
+
+01/12/2006: mmatus
+ - Add aliases for 'case' encoders used with %rename/%namewarn
+
+ %(uppercase)s hello_world -> HELLO_WORLD
+ %(lowercase)s HelloWorld -> helloworld
+ %(camelcase)s hello_world -> HelloWorld
+ %(undercase)s HelloWorld -> hello_world
+
+
+01/12/2006: mmatus
+ - Add the -dump_parse_module and -dump_parse_top options,
+ which are similar to -dump_module and -dump_top, but they
+ dump the node trees just after parsing, showing only the
+ attributes visible at the parsing stage, and not the added
+ later in typemap.cxx, allocate.cxx, lang.cxx or elsewhere.
+
+ Besides debugging porpuses, these options are very useful
+ if you plan to use %rename in an "advance way", since it
+ shows only and all the node's attributes you can use
+ inside the match rules.
+
+
+01/12/2006: mmatus
+ - Add predicates to %rename, so, you don't need to
+ remember, for example, how to match a member function.
+
+ Now it is easy, for example to use the 'utitle' encoder
+ in all the member methods, you type:
+
+ %rename("%(utitle)s",%isfunction,%ismember) "";
+
+ or to ignore all the enumitems in a given class:
+
+ %rename("$ignore", %isenumitem, %classname="MyClass") "";
+
+ Available predicates are (see swig.swg):
+
+ %isenum
+ %isenumitem
+ %isaccess
+ %isclass
+ %isextend
+ %isextend
+ %isconstructor
+ %isdestructor
+ %isnamespace
+ %istemplate
+ %isconstant
+
+ %isunion
+ %isfunction
+ %isvariable
+ %isimmutable
+
+ %isstatic
+ %isfriend
+ %istypedef
+ %isvirtual
+ %isexplicit
+ %isextern
+
+ %ismember
+ %isglobal
+ %innamespace
+
+ %ispublic
+ %isprotected
+ %isprivate
+
+ %classname
+
+ These predicates correspond to specific 'match'
+ declarations, which sometimes are not as evident as the
+ predicates names.
+
+
+ - Add the or '|' operation in %rename match, for
+ example to capitalize all the constants (%constant or
+ const cdecl):
+
+ %rename("%(upper)s",match="cdecl|constant",%isimmutable) "";
+
+
+
+01/12/2006: mgossage
+ - Partial fixed of errors under C89, bug #1356574
+ (converted C++ style comments to C style)
+ - Added patches from neomantra@users.sf.net #1379988 and #1388343
+ missing a 'return' statement for error conditions
+ also updated the %init block bug #1356586
+
+01/10/2006: mmatus
+ - Add the 'utitle' encoder, as an example of how to add
+ your own encoder. I added the encoder method in misc.c
+ but developers can add others, the same way, inside any
+ target language.
+
+ Well, 'utitle' is the reverse of 'ctitle', ie:
+
+ %rename("%(ctitle)s") camel_case; -> CamelCase;
+ %rename("%(utitle)s") CamelCase; -> camel_case;
+
+
+01/10/2006: cfisavage
+ [Ruby]
+ Updated Ruby Exception handling. Classes that are specified in throws clauses,
+ or are marked as %exceptionclass, are now inherited from rb_eRuntimeError.
+ This allows instances of these classes to be returned to Ruby as exceptions.
+ Thus if a C++ method throws an instance of MyException, the calling Ruby
+ method will get back a MyException object. To see an example,
+ look at ruby/examples/exception_class.
+
+01/10/2006: mmatus
+
+ - Add the %catches directive, which complements the %exception
+ directive in a more automatic way. For example, if you have
+
+ int foo() throw(E1);
+
+ swig generates the proper try/catch code to dispatch E1.
+
+ But if you have:
+
+
+ int barfoo(int i) {
+ if (i == 1) {
+ throw E1();
+ } else {
+ throw E2();
+ }
+ return 0;
+ }
+
+ ie, where there is no explicit exception specification in the decl, you
+ end up doing:
+
+ %exception barfoo {
+ try {
+ $action
+ } catch(E1) { ... }
+ } catch(E2) { ... }
+ }
+
+ which is very tedious. Well, the %catches directive defines
+ the list of exceptions to catch, and from swig:
+
+ %catches(E1,E2) barfoo(int i);
+ int barfoo(int i);
+
+ is equivalent to
+
+ int barfoo(int i) throw(E1,E2);
+
+ Note, however, that the %catches list doesn't have to
+ correspond to the C++ exception specification. For example, if you
+ have:
+
+ struct E {};
+ struct E1 : E {};
+ struct E2 : E {};
+
+ int barfoo(int i) throw(E1,E2);
+
+ you can define
+
+ %catches(E) barfoo(int i);
+
+ and swig will generate an action code equivalent to
+
+ try {
+ $action
+ } catch(E &_e) {
+ <raise _e>;
+ }
+
+ Of course, you still have to satisfy the C++ restrictions,
+ and the catches list must be compatible (not the same)
+ as the original list of types in the exception specification.
+
+ Also, you can now specify that you want to catch the
+ unknown exception '...', for example:
+
+ %catches(E1,E2,...) barfoo(int);
+
+ In any case, the %catches directive will emit the
+ code to convert into the target language error/exception
+ using the 'throws' typemap.
+
+ For the '...' case to work, you need to
+ write the proper typemap in your target language. In the
+ UTL, this looks like:
+
+ %typemap(throws) (...) {
+ SWIG_exception(SWIG_RuntimeError,"unknown exception");
+ }
+
+01/09/2006: mutandiz
+ [Allegrocl]
+
+ Fixes a number of SEGVs primarily in the handling of
+ various anonymous types. Found in a pass through the
+ swig test-suite. Still more to do here, but this is a
+ good checkpoint.
+
+ Adds -cwrap and -nocwrap as an allegrocl specific
+ command-line argument. Controls generating of a C
+ wrapper file when wrapping C code. By default only a
+ lisp file is created for C code wrapping.
+
+ Doc updates for the command-line arguments and fixes as
+ pointed out on swig-devel
+
+01/05/2006: wsfulton
+ [Java] Fix unsigned long long and const unsigned long long & typemaps
+ - Bug #1398394 with patch from Dries Decock
+
+01/06/2006: mmatus
+ Add 'named' warning codes, now in addition to:
+
+ %warnfilter(813);
+
+ you can use
+
+ %warnfilter(SWIGWARN_JAVA_MULTIPLE_INHERITANCE);
+
+ just use the same code name found in Source/Include/swigwarn.h
+ plus the 'SWIG' prefix.
+
+ If a developer adds a new warning code, the Lib/swigwarn.swg file
+ will be generated when running the top level make.
+
+01/05/2006: cfisavage
+ [Ruby]
+ Reimplemented object tracking for Ruby. The new implementation works
+ by expanding the swig_class structure for Ruby by adding a trackObjects
+ field. This field can be set/unset via %trackobjects as explained
+ in the Ruby documentation. The new implementation is more robust
+ and takes less code to implement.
+
+01/05/2006: wsfulton
+ Fix for %extend and static const integral types, eg:
+
+ class Foo {
+ public:
+ %extend {
+ static const int bar = 42;
+ }
+ };
+
+12/30/2005: mmatus
+
+ - Add info for old and new debug options:
+
+ -dump_top - Print information of the entire node tree, including system nodes
+ -dump_module - Print information of the module node tree, avoiding system nodes
+ -dump_classes - Print information about the classes found in the interface
+ -dump_typedef - Print information about the types and typedefs found in the interface
+ -dump_tags - Print information about the tags found in the interface
+ -debug_typemap - Print information for debugging typemaps
+ -debug_template - Print information for debugging templates
+
+ - Add the fakeversion. If you have a project that uses
+ configure/setup.py, or another automatic building system
+ and requires a specific swig version, let say 1.3.22
+ you can use:
+
+ SWIG_FEATURES="-fakeversion 1.3.22"
+
+ or
+
+ swig -fakeversion 1.3.22
+
+ and then swig -version will report 1.3.22 instead of the
+ current version.
+
+ Typical use would be
+
+ SWIG_FEATURES="-fakeversion 1.3.22" ./configure
+
+12/30/2005: mmatus
+
+ - Add option/format support to %rename and %namewarn.
+ Now %namewarn can force renaming, for example:
+
+ %namewarn("314: import is a keyword",rename="_%s") "import";
+
+ and rename can also support format forms:
+
+ %rename("swig_%s") import;
+
+ Now, since the format is processed via swig Printf, you
+ can use encoders as follows:
+
+ %rename("%(title)s") import; -> Import
+ %rename("%(upper)s") import; -> IMPORT
+ %rename("%(lower)s") Import; -> import
+ %rename("%(ctitle)s") camel_case; -> CamelCase
+
+ This will allow us to add more encoders, as the
+ expected one for regular expressions.
+
+ - Add the above 'ctitle' encoder, which does the camel case:
+
+ camel_case -> CamelCase
+
+ - Also, while we get the regexp support, add the 'command' encoder,
+ you can use it as follows
+
+ %rename("%(command:sed -e 's/\([a-z]\)/\U\\1/' <<< )s") import;
+
+ then swig will popen the command
+
+ "sed -e 's/\([a-z]\)/\U\\1/' <<< import"
+
+ see below for anonymous renames for better examples.
+
+ - The rename directive now also allows:
+
+ - simple match: only apply the rename if a type match
+ happen, for example
+
+ %rename(%(title)s,match="enumitem") hello;
+
+ enum Hello {
+ hi, hello -> hi, Hello
+ };
+ int hello() -> hello;
+
+ - extended match: only apply the rename if the 'extended attribute' match
+ occurred, for example:
+
+ // same as simple match
+ %rename(%(title)s,match$nodeType="enumitem") hello;
+
+ enum Hello {
+ hi, hello -> hi, Hello
+ };
+
+ Note that the symbol '$' is used to define the attribute name in
+ a 'recursive' way, for example:
+
+ // match only hello in 'enum Hello'
+ %rename(%(title)s,match$parentNode$type="enum Hello") hello;
+
+ enum Hello {
+ hi, hello -> hi, Hello // match
+ };
+
+ enum Hi {
+ hi, hello -> hi, hello // no match
+ };
+
+ here, for Hello::hi, the "parentNode" is "Hello", and its "type"
+ is "enum Hello".
+
+
+ - Anonymous renames: you can use 'anonymous' rename directives, for example:
+
+ // rename all the enum items in Hello
+ %rename(%(title)s,match$parentNode$type="enum Hello") "";
+
+ enum Hello {
+ hi, hello -> Hi, Hello // match both
+ };
+
+ enum Hi {
+ hi, hello -> hi, hello // no match
+ };
+
+ // rename all the enum items
+ %rename(%(title)s,match$nodeType="enumitem") "";
+
+ // rename all the items in given command (sloooow, but...)
+ %rename(%(command:<my external cmd>)s) "";
+
+
+ Anonymous renames with commands can be very powerful, since you
+ can 'outsource' all the renaming mechanism (or part of it) to an
+ external program:
+
+ // Uppercase all (and only) the names that start with 'i'
+ %rename("%(command:awk '/^i/{print toupper($1)}' <<<)s") "";
+
+ int imported() -> IMPORTED;
+ int hello() -> hello
+
+ Note that if the 'command' encoder returns an empty string, swig
+ understands that no rename is necessary.
+
+ Also note that %rename 'passes' the matched name. For example, in
+ this case
+
+ namespace ns1 {
+ int foo();
+ }
+
+ namespace ns2 {
+ int bar();
+ }
+
+ the external program only receives "foo" and "bar". If needed,
+ however, you can request the 'fullname'
+
+ %rename("%(command:awk 'awk '/ns1::/{l=split($1,a,"::"); print toupper(a[l])}'' <<<)s",fullname=1) "";
+
+ ns1::foo -> FOO
+ ns2::bar -> bar
+
+ - Mixing encoders and matching: of course, you can do mix commands
+ and match fields, for example:
+
+ %rename("%(<my encoder for fncs>)",match="cdecl") "";
+ %rename("%(<my encoder for enums>)",match="enumitem") "";
+ %rename("%(<my encoder for enums inside a class>)",match="enumitem",
+ match$parentNode$parentNode$nodeType="class") "";
+
+ Use "swig -dump_parse_module" to see the attribute names you can use to
+ match a specific case.
+
+ - 'sourcefmt' and 'targetfmt': sometimes you need to
+ process the 'source' name before comparing, for example
+
+ %namewarn("314: empty is a keyword",sourcefmt="%(lower)s") "empty";
+
+ then if you have
+
+ int Empty(); // "Empty" is the source
+
+ you will get the keyword warning since 'Empty' will be
+ lower cased, via the sourcefmt="%(lower)s" option,
+ before been compared to the 'target' "empty".
+
+ There is an additional 'targetfmt' option to process the
+ 'target' before comparing.
+
+ - complementing 'match': you can use 'notmatch', for example
+
+ %namewarn("314: empty is a keyword",sourcefmt="%(lower)s",notmatch="namespace") "empty";
+
+ here, the name warning will be applied to all the symbols except namespaces.
+
+
+12/30/2005: mmatus
+
+ - Add initial support for gcj and Java -> <target language> mechanism.
+
+ See examples in:
+
+ Examples/python/java
+ Examples/ruby/java
+ Examples/tcl/java
+
+ to see how to use gcj+swig to export java classes into
+ python/ruby/tcl.
+
+ The idea is to put all the common code for gcj inside
+
+ Lib/gcj
+
+ and localize specific types such as jstring, as can be found
+ in
+
+ Lib/python/jstring.i
+ Lib/ruby/jstring.i
+ Lib/tcl/jstring.i
+
+ Using the UTL, this is very easy, and the perl version for
+ jstring.i will be next.
+
+
+12/29/2005: mmatus
+ - Add the copyctor feature/directive/option to enable the automatic
+ generation of copy constructors. Use as in:
+
+ %copyctor A;
+
+ struct A {
+
+ };
+
+ then this will work
+
+ a1 = A();
+ a2 = A(a1);
+
+ Also, since it is a feature, if you just type
+
+ %copyctor;
+
+ that will enable the automatic generation for all the
+ classes. It is also equivalent to
+
+ swig -copyctor -c++ ...
+
+ Notes:
+
+ 1.- The feature only works in C++ mode.
+
+ 2.- The automatic creation of the copy constructor will
+ usually produce overloading. Hence, if the target
+ language doesn't support overloading, a special name
+ will be used (A_copy).
+
+ 3.- For the overloading reasons above, it is probably not
+ a good idea to use the flag when, for example, you are
+ using keywords in Python.
+
+ 4.- The copyctor automatic mechanism follows more or less
+ the same rules as the default constructor mechanism,
+ i.e., a copy constructor will not be added if the
+ class is abstract or if there is a pertinent non-public
+ copy ctor in the class or its hierarchy.
+
+ Hence, it might be necessary for you to complete the
+ class declaration with the proper non-public copy ctor
+ to avoid a wrong constructor addition.
+
+ - Fix features/rename for templates ctor/dtor and other
+ things around while adding the copyctor mechanism.
+
+
+12/27/2005: mmatus
+ - Add the 'match' option to typemaps. Assume you have:
+
+ %typemap(in) SWIGTYPE * (int res) {..}
+ %typemap(freearg) SWIGTYPE * { if (res$argnum) ...}
+
+ then if you do
+
+ %typemap(in) A * {...}
+
+ swig will 'overload the 'in' typemap, but the 'freearg'
+ typemap will be also applied, even when this is wrong. The old
+ solutions is to write:
+
+ %typemap(in) A * {...}
+ %typemap(freeag) A * ""
+
+ overload 'freearg' with an empty definition.
+
+ The problem is, however, there is no way to know you need
+ to do that until you start getting broken C++ code, or
+ worse, broken runtime code.
+
+ The same applies to the infamous 'typecheck' typemap,
+ which always confuses people, since the first thing you do
+ is to just write the 'in' typemap.
+
+ The 'match' option solves the problem, and if instead you write:
+
+ %typemap(in) SWIGTYPE * (int res) {..}
+ %typemap(freearg,match="in") SWIGTYPE * { if (res$argnum) ...}
+ %typemap(typecheck,match="in",precedence...) SWIGTYPE * {...}
+
+ it will tell swig to apply the 'freearg/typecheck'
+ typemaps only if they 'match' the type of the 'in'
+ typemap. The same can be done with other typemaps as:
+
+ %typemap(directorout) SWIGTYPE * {...}
+ %typemap(directorfree,match="directorout") SWIGTYPE * {...}
+
+
+12/27/2005: mmatus
+ - Add the 'naturalvar' option/mode/feature to treat member
+ variables in a more natural way, ie, similar to the global
+ variable behavior.
+
+ You can use it in a global way via the command line
+
+ swig -naturalvar ...
+
+ or the module mode option
+
+ %module(naturalvar=1)
+
+ both forms make swig treat all the member variables in the
+ same way it treats global variables.
+
+ Also, you can use it in a case by case approach for
+ specific member variables using the directive form:
+
+ %naturalvar Bar::s;
+
+ Then, in the following case for example:
+
+ std::string s;
+ struct Bar {
+ std::string s;
+ };
+
+ you can do:
+
+ b = Bar()
+ b.s ="hello"
+ cvar.s = "hello"
+
+ if (b.s != cvar.s):
+ raise RuntimeError
+
+
+ This is valid for all the languages, and the
+ implementation is based on forcing the use of the
+ const SWIGTYPE& (C++)/SWIGTYPE (C) typemaps for the
+ get/set methods instead of the SWIGTYPE * typemaps.
+ Hence, for 'naturalvar' to work, each target language
+ must implement 'typemap(in/out) const Type&' properly.
+
+ The 'naturalvar' option replaces or makes workarounds such as:
+
+ %apply const std::string & { std::string *}
+
+ unnecessary.
+
+ Note1: If your interface has other kinds of workarounds to
+ deal with the old 'unnatural' way to deal with member
+ variables (returning/expecting pointers), the
+ 'naturalvar' option could break them.
+
+ Note2: the option has no effect on unnamed types, such
+ as unnamed nested unions.
+
+
+12/27/2005: mmatus
+ - Add more 'expressive' result states for the typemap
+ libraries.
+
+ In the past, for scripting languages, you would do checking something like:
+
+ if (ConvertPtr(obj,&vptr,ty,flags) != -1) {
+ // success
+ } else {
+ // error
+ }
+
+ Now the result state can carry more information,
+ including:
+
+ - Error state: like the old -1/0, but with error codes from swigerrors.swg.
+
+ int res = ConvertPtr(obj,&vptr,ty,flags);
+ if (SWIG_IsOK(res)) {
+ // success code
+ } else {
+ SWIG_Error(res); // res carries the error code
+ }
+
+ - Cast rank: when returning a simple successful
+ conversion, you just return SWIG_OK, but if you need
+ to do a 'cast', you can add the casting rank, ie:
+
+ if (PyFloat_Check(obj)) {
+ value = PyFloat_AsDouble(obj);
+ return SWIG_OK;
+ } else if (PyInt_Check(obj)) {
+ value = (double) PyInt_AsLong(obj);
+ return SWIG_AddCast(SWIG_OK);
+ }
+
+ later, the casting rank is used to properly dispatch
+ the overloaded function, for example. This of course
+ requires your language to support and use the new
+ dispatch cast/rank mechanism (Now mainly supported in
+ perl and python, and easily expandable to ruby and tcl).
+
+ - [UTL] Add support for the new 'expressive' result states.
+
+12/27/2005: mmatus
+ - Add support for the C++ implicit conversion mechanism, which
+ required some modifications in parser.y (to recognize
+ 'explicit') and overload.cxx (to replace $implicitconv as
+ needed).
+
+ Still, real support in each target language requires each
+ target language to be modified. Python provides an example,
+ see below.
+
+
+ - Add support for native C++ implicit conversions, ie, if you
+ have
+
+ %implicitconv A;
+
+ struct A {
+ int ii;
+ A() {ii = 1;}
+ A(int) {ii = 2;}
+ A(double) {ii = 3;}
+ explicit A(char *s) {ii = 4;}
+ };
+
+ int get(const A& a) {return a.ii;}
+
+ you can call:
+
+ a = A()
+ ai = A(1)
+ ad = A(1.0)
+ as = A("hello")
+
+ # old forms
+ get(a) -> 1
+ get(ai) -> 2
+ get(ad) -> 3
+ get(as) -> 4
+
+ #implicit conversions
+ get(1) -> 2
+ get(1.0) -> 3
+ get("hello") -> Error, explicit constructor
+
+ Also, as in C++, now implicit conversions are supported in
+ variable assigments, and if you have:
+
+ A ga;
+ struct Bar {
+ A a;
+ };
+
+ you can do:
+
+ cvar.ga = A(1)
+ cvar.ga = 1
+ cvar.ga = 1.0
+ cvar.ga = A("hello")
+ cvar.ga = "hello" -> error, explicit constructor
+
+ b = Bar()
+ b.a = A("hello")
+ b.a = 1
+ b.a = 1.0
+ b.a = "hello" -> error, explicit constructor
+
+ Note that the last case, assigning a member var directly,
+ also requires the 'naturalvar' option.
+
+ This support now makes the old '%implicit' macro, which
+ was found in 'implicit.i' and it was fragile in many ways,
+ obsolete, and you should use the new '%implicitconv'
+ directive instead.
+
+ Note that we follow the C++ conventions, ie, in the
+ following the implicit conversion is allowed:
+
+ int get(A a) {return a.ii;}
+ int get(const A& a) {return a.ii;}
+
+ but not in these cases:
+
+ int get(A *a) {return a->ii;}
+ int get(A& a) {return a.ii;}
+
+ Also, it works for director methods that return a by value
+ result, ie, the following will work:
+
+ virtual A get_a() = 0;
+
+ def get_a(self):
+ return 1
+
+ but not in this case:
+
+ virtual const A& get_a() = 0;
+ virtual A& get_a() = 0;
+ virtual A* get_a() = 0;
+
+ Notes:
+
+ - the implicitconv mechanism is implemented by directly
+ calling/dispatching the python constructor, triggering a
+ call to the __init__method. Hence, if you expanded the
+ __init__ method, like in:
+
+ class A:
+ def __init__(self,args):
+ <swig code>
+ <my code here>
+
+ then 'my code' will also be executed.
+
+ - Since the %implicitconv directive is a SWIG feature, if you type:
+
+ %implicitconv;
+
+ that will enable implicit conversion for all the classes in
+ your module.
+
+ But if you are worried about performance, maybe that will be
+ too much, especially if you have overloaded methods, since
+ to resolve the dispatching problem, python will efectively
+ try to call all the implicit constructors as needed.
+
+ - For the same reason, it is highly recommended that you use
+ the new 'castmode' when mixing implicit conversion and
+ overloading.
+
+ - [python] The %implicit directive is declared obsolete, and
+ you should use %implicitconv instead. If you include
+ the implicit.i file, a warning will remind you of this.
+
+ Note: Since %implicit is fragile, just replacing it by
+ %implicitconv could lead to different behavior. Hence, we
+ don't automatically switch from to the other, and the user
+ must migrate to the new %implicitconv directive manually.
+
+
+12/26/2005: wsfulton
+ [C#]
+ Modify std::vector wrappers to use std::vector::value_type as this is
+ closer to the real STL declarations for some methods, eg for push_back().
+ Fixes some compilation errors for some compilers eg when the templated
+ type is a pointer.
+
+ [Java]
+ std::vector improvements - a few more methods are wrapped and specializations are
+ no longer required. The specialize_std_vector macro is no longer needed (a
+ warning is issued if an attempt is made to use it).
+
+12/26/2005: wsfulton
+ [Java, C#]
+ Add in pointer reference typemaps. This also enables one to easily wrap
+ std::vector<T> where T is a pointer.
+
+12/24/2005: efuzzyone
+ [CFFI] The cffi module for SWIG:
+ - Fully supports C, but provides limited supports for C++, in
+ particular C++ support for templates and overloading needs to
+ be worked upon.
+
+12/23/2005: mmatus
+ [python] Add the castmode that allows the python
+ type casting to occur.
+
+ For example, if you have 'int foo(int)', now
+
+ class Ai():
+ def __init__(self,x):
+ self.x = x
+ def __int__(self):
+ return self.x
+
+ foo(1) // Ok
+ foo(1.0) // Ok
+ foo(1.3) // Error
+ a = Ai(4)
+ foo(ai) // Ok
+
+ The castmode, which can be enabled either with the
+ '-castmode' option or the %module("castmode") option, uses
+ the new cast/rank dispatch mechanism. Hence, now if you
+ have 'int foo(int); int foo(double);', the following works
+ as expected:
+
+ foo(1) -> foo(int)
+ foo(1.0) -> foo(double)
+ ai = Ai(4)
+ foo(ai) -> foo(int)
+
+ Note1: the 'castmode' could disrupt some specialized
+ typemaps. In particular, the "implicit.i" library seems to
+ have problem with the castmode. But besides that one, the
+ entire test-suite compiles fine with and without the
+ castmode.
+
+ Note2: the cast mode can't be combined with the fast
+ dispatch mode, ie, the -fastdispatch option has no effect
+ when the cast mode is selected. The penalties, however,
+ are minimum since the cast dispatch code is already based
+ on the same fast dispatch mechanism.
+
+ See the file overload_dispatch_cast_runme.py file for
+ new cases and examples.
+
+12/22/2005: mmatus
+ Add the cast and rank mechanism to dispatch overloading
+ functions. The UTF supports it now, but for each language
+ it must be decided how to implement and/or when to use it.
+
+ [perl] Now perl uses the new cast and rank dispatch
+ mechanism, which solves all the past problems known
+ in perl, such as the old '+ 1' problem:
+
+ int foo(int);
+
+ $n = 1
+ $n = $n + 1
+ $r = foo(n)
+
+ also works:
+
+ foo(1);
+ foo("1");
+ foo(1.0);
+ foo("1.0");
+
+ but fails
+
+ foo("l");
+
+ and when overloading foo(int) and foo(double);
+
+ foo(1) -> foo(int)
+ foo(1.0) -> foo(double)
+ foo("1") -> foo(int)
+ foo("1.0") -> foo(double)
+ foo("l") -> error
+ foo($n) -> foo(int) for good perl versions
+ foo($n) -> foo(double) for old bad perl versions
+
+ when overloading foo(int), foo(char*) and foo(double):
+
+ foo(1) -> foo(int)
+ foo(1.0) -> foo(double)
+ foo("1") -> foo(char*)
+ foo("1.0") -> foo(char*)
+ foo("l") -> foo(char*)
+
+ Note: In perl the old dispatch mechanism was broken,
+ so, we don't provide an option to enable the old one
+ since, again, it was really really broken.
+
+ See 'overload_simple_runme.pl' for more cases and tests.
+
+ PS: all the old known issues are declared resolved, any
+ new "problem" that could be discovered is declared,
+ a priori, as "features" of the new dispatch mechanism
+ (until we find another solution at least).
+
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ As with the introduction of the UTF, some things could
+ now start to work as expected, and people used to deal or
+ workaround previous bugs related to the dispatch
+ mechanism, could see now a difference in perl behavior.
+
+12/21/2005: mmatus
+ - The '-nodefault' flag (pragma and feature) now generates
+ a warning, and recommends to use the explicit
+ -nodefaultctor and -nodefaultdtor options.
+
+ The reason to split the 'nodefault' behavior is that, in
+ general, ignoring the default destructor generates memory
+ leaks in the target language. Hence, is too risky just to
+ disable both the default constructor and destructor
+ at the same time.
+
+ If you need to disable the default destructor, it is
+ also recommended you use the directive form:
+
+ %nodefaultdtor MyVerySpecialClass;
+
+ for specific classes, and always avoid using the global
+ -nodefault and -nodefaultdtor options.
+
+12/21/2005: wsfulton
+ [Java, C#]
+ Fix incorrect code generation when the intermediary classname is changed
+ in the module directive from its default. For example:
+
+ %module(jniclassname="myimclassnewname") "mymodule" // Java
+ %module(imclassname="myimclassnewname") "mymodule" // C#
+
+ Add in new special variable $imclassname. See docs.
+
+12/17/2005: mmatus
+ [Python]
+ - Add the -aliasobj0/-noaliasobj0 options to use with
+ -fastunpack and/or -O and old typemaps that use 'obj0'
+ directly.
+
+ So, if you compile your code using -O and get errors about
+ the undeclared 'obj0' variable, run again using
+
+ swig -O -aliasobj0 -python ....
+
+ For new typemaps, never use 'obj0' directly, if needed,
+ use the '$self' name that will be properly expanded to
+ 'obj0' (nofastunpack) or 'swig_obj[0]' (fastunpack).
+
+ If you have no idea what I am talking about, better, that
+ means you have no typemap with this problem.
+
+
+12/14/2005: mmatus
+ [Python]
+ - Add the -fastunpack/-nofastunpack options to enable/disable
+ the use of the internal UnpackTuple method, instead of
+ calling the one from the python C API.
+
+ The option -O now also implies -fastunpack.
+
+
+12/11/2005: mmatus
+ [Python]
+ - Add the -proxydel/-noproxydel options to enable/disable
+ the generation of proxy/shadow __del__ methods, even
+ when now they are redundant, since they are empty.
+ However, old interfaces could rely on calling them.
+
+ The default behavior is to generate the __del__ methods
+ as in 1.3.27 or older swig versions.
+
+ The option -O now also implies -noproxydel.
+
+12/10/2005: mmatus
+ [UTF]
+ - Fix unnecessary calls to SWIG_TypeQuery for 'char *'
+ and 'wchar_t *', problem found by Clay Culver while
+ profiling the PyOgre project.
+
+
+ [Python]
+ - Add the -dirvtable/-nodirvtable to enable/disable
+ a pseudo virtual table used for directors, avoiding
+ the need to resolve the python method at each call.
+
+ - Add the -safecstrings/-nosafecstrings options to
+ enable/disable the use of safe conversions from PyString
+ to char *. Python requires you to never change the internal
+ buffer directly, and hence 'safectrings' warranties that
+ but returning a copy of the internal python string buffer.
+
+ The default, as in previous releases, is to return a
+ pointer to the buffer (nosafecstrings), so, it is the user's
+ responsibility to avoid its modification.
+
+ - Add the -O option to enable all the optimization options
+ at once, initially equivalent to
+
+ -modern -fastdispatch -dirvtable -nosafecstrings -fvirtual
+
+12/08/2005: mmatus
+
+ - Add the -fastdispatch option (fastdispatch feature). This
+ enables the "fast dispatch" mechanism for overloaded
+ methods provided by Salvador Fandi~no Garc'ia (#930586).
+
+ The resulting code is smaller and faster since less type
+ checking is performed. However, the error messages you
+ get when the overloading is not resolved could be
+ different from what the traditional method returns.
+
+ With the old method you always get an error such as
+
+ "No matching function for overloaded ..."
+
+ with the new method you can also get errors such as
+
+ "Type error in argument 1 of type ..."
+
+ See bug report #930586 for more details.
+
+ So, this optimization must be explicitly enabled by users.
+
+ The new mechanism can be used as:
+
+ swig -fastdispatch
+
+ or using the feature form
+
+ %feature("fastdispatch") method;
+ or
+ %fastdispatch method;
+
+
+12/06/2005: mmatus
+
+ - Several memory and speed improvements, specially for
+ templates. Now swig is up to 20 faster than before for
+ large template interfaces, such as the std_containers.i
+ and template_matrix.i files in the python test-suite.
+
+ Memory footprint is also reduced in consideration of small
+ pcs/architectures.
+
+ - add commandline options -cpperraswarn and -nocpperraswarn" to force
+ the swig preprocessor to treat the #error directive as a #warning.
+
+ the pragmas
+
+ #pragma SWIG cpperraswarn=1
+ #pragma SWIG cpperraswarn=0
+
+ are equivalent to the command line options, respectively.
+
+
+12/06/2005: mmatus
+ [Python] The generated code is now more portable, especially
+ for Windows. Following
+
+ http://www.python.org/doc/faq/windows.html
+
+ Py_None is never accessed as a structure, plus other
+ tricks mentioned there.
+
+12/06/2005: mmatus
+ [Python] Added initial support for threads based in the
+ proposal by Joseph Winston.
+
+ The user interface is as follows:
+
+ 1.- the module thread support is enable via the "threads" module
+ option, i.e.
+
+ %module("threads"=1)
+
+ 2.- Equivalent to that, is the new '-threads' swig option
+
+ swig -threads -python ...
+
+ 3.- You can partially disable thread support for a given
+ method using:
+
+ %feature("nothread") method;
+ or
+ %nothread method;
+
+ also, you can disable sections of the thread support,
+ for example
+
+ %feature("nothreadblock") method;
+ or
+ %nothreadblock method;
+
+ %feature("nothreadallow") method;
+ or
+ %nothreadallow method;
+
+ the first disables the C++/python thread protection, and the
+ second disables the python/C++ thread protection.
+
+ 4.- The current thread support is based in the PyGIL
+ extension present in python version 2.3 or later, but
+ you can provide the thread code for older versions by
+ defining the macros in pythreads.swg.
+
+ If you get a working implementation for older versions,
+ please send us a patch.
+
+ For the curious about performance, here are some numbers
+ for the profiletest.i test, which is used to check the speed
+ of the wrapped code:
+
+ nothread 9.6s (no thread code)
+ nothreadblock 12.2s (only 'allow' code)
+ nothreadallow 13.6s (only 'block' code)
+ full thread 15.5s ('allow' + 'block' code)
+
+ i.e., full thread code decreases the wrapping performance by
+ around 60%. If that is important to your application, you
+ can tune each method using the different 'nothread',
+ 'nothreadblock' or 'nothreadallow' features as
+ needed. Note that for some methods deactivating the
+ 'thread block' or 'thread allow' code is not an option,
+ so, be careful.
+
+
+11/26/2005: wsfulton
+ SWIG library files use system angle brackets everywhere for %include, eg
+ %include "std_common.i"
+ becomes
+ %include <std_common.i>
+
+11/26/2005: wsfulton
+ [Java, C#]
+ Typesafe enums and proper enums have an extra constructor so that enum item values that
+ are initialised by another enum item value can be wrapped without having to use %javaconstvalue/
+ %csconstvalue for when using %javaconst(1)/%csconst(1). Suggestion by
+ Bob Marinier/Douglas Pearson.
+ For example:
+
+ typedef enum
+ {
+ xyz,
+ last = xyz
+ } repeat;
+
+11/21/2005: mmatus
+ [ruby + python]
+
+ Fixes for directors + pointers. This is an ugly problem without an easy
+ solution. Before we identified this case as problematic:
+
+ virtual const MyClass& my_method();
+
+ but it turns out that all the cases where a pointer, array or
+ reference is returned, are problematic, even for
+ primitive types (as int, double, char*, etc).
+
+ To try to fix the issue, a new typemap was added,
+ 'directorfree', which is used to 'free' the resources
+ allocated during the 'directorout' phase. At the same
+ time, a primitive garbage collector engine was added to
+ deal with orphaned addresses, when needed.
+
+ The situation is much better now, but still it is possible to have
+ memory exhaustation if recursion is used.
+
+ So, still you need to avoid returning pointers, arrays or
+ references when using director methods.
+
+ - Added stdint.i - typemaps for latest C99 integral types found in stdint.h.
+
+11/14/2005: wsfulton
+ More types added to windows.i, eg UINT8, WORD, BYTE etc.
+ Including windows.i will also enable SWIG to parse the __declspec Microsoft
+ extension, eg __declspec(dllimport). Also other Windows calling conventions
+ such as __stdcall.
+
+11/10/2005: wsfulton
+ New library file for Windows - windows.i. This file will contain useful type
+ information for users who include windows.h. Initial support is for the
+ non ISO integral types: __int8, __int16, __int32, __int64 and unsigned versions.
+ The unsigned versions previously could not be parsed by SWIG. SF #872013.
+
+11/09/2005: wsfulton
+ [Java, C#] Portability warning for files which will overwrite each other on case
+ insensitive file systems such as FAT32/NTFS. This will occur, for example, when two
+ class names are the same barring case. The warning is issued on all platforms and
+ can be suppressed with the usual warning suppression techniques. SF bug #1084507.
+
+11/09/2005: wsfulton
+ ./configure --with-python --with-ruby --with-perl5 etc enable these languages,
+ ie the --with-xxxx options, where no path is specified, work the same as if
+ the option was not specified at all. Based on patches #1335042 #1329048 #1329047.
+
+11/09/2005: dancy
+
+ [Allegrocl]
+ Add C++ support to the Allegrocl module. Further
+ enhances the C support as well. Some of the
+ features:
+
+ - MUCH better generation of foreign types based on
+ the C/C++ types for use in defining the FFI on
+ the lisp side. We don't pass everything as a (* :void)
+ any longer.
+
+ - Uses typemaps for better control of type conversions
+ and code generation in the generated lisp and c++ wrapper
+ code.
+
+ - CLOS wrapping of pointers returned from foreign space
+ makes it easier to differentiate pointers in user code.
+ The wrapping objects can be passed directly to FF calls.
+
+ - Defun wrapping of FF calls, allowing for more lispy
+ interface. Conversion, GCing, of lisp objects to
+ foreign objects can be done in the wrapping defun via
+ the use of typemaps.
+
+ - overload dispatching implemented on the lisp side
+ using generic functions.
+
+ - Templates and synonymous types supported.
+
+11/07/2005: mmatus
+
+ [Python] Adding proper support for multi-inheritance in
+ the python side, ie, if you have two C++ wrapped class, Foo
+ and Bar, now:
+
+ class MyPythonClass(Foo,Bar):
+ ....
+
+ will properly work, even with directors, and the
+ deallocation of Foo.this and Bar.this will follow
+ correctly. Before, a class could only have one 'this'
+ instance (unlike C++), only the last base class was
+ properly deleted, or detected with directors.
+
+ Now 'self.this' can be a list, which will contain the C++
+ instance pointers for all the base classes.
+
+ Also, swig.this is responsible for deallocating the C++
+ instance(s), and the __del__ method is not emitted unless
+ the user preppend/append some code to it.
+
+ - Swig can now detect memory leaks, ie, if you still
+ don't use proxy/shadow classes, and type something like
+
+ import _example
+ f = _example.new_Foo()
+
+ and forget to call _example.delete_Foo(f), then swig will
+ tell you that there is a memory leak.
+
+ Otherwise, if you always use the proxy classes, you probably
+ you will never ever see this warning unless there is
+ something wrong inside the swig wrapping code.
+
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ If you overloaded the __del__ method, and call the base
+ one without a try block, as in
+
+ class MyClass(SwigClass):
+
+ def __del__(self):
+ <your code here>
+ SwigClass.__del__(self)
+
+ python could complain that the method SwigClass.__del__ is
+ undefined. Try to use instead:
+
+ def __del__(self):
+ <your code here>
+ try: SwigClass.__del__(self)
+ except: pass
+
+ or simply
+
+ def __del__(self):
+ <your code here>
+
+11/02/2005: mmatus
+
+ [Python] Adding more fun to STL/STD containers, now you
+ can do
+
+ %template(pyset) std::set<PyObject *>;
+ %template(pyvector) std::vector<PyObject *>;
+ %template() std::pair<PyObject *,PyObject *>;
+ %template(pyvector) std::map<PyObject *,PyObject *>;
+ ....
+
+ The same applies to std::list, std::deque, std::multiset, etc.
+
+ Then, at the python side you can do now:
+
+ # C++ std::vector as native python sequence
+ v = pyvector([1,"hello",(1,2)])
+ print v[1]
+ >> 'hello'
+ print v[2]
+ >> (1,2)
+
+ # C++ std::set as native python sequence
+ s = pyset()
+ s.insert((1,2))
+ s.insert(1)
+ s.insert("hello")
+ sum=()
+ for i in s:
+ sum +=(i,)
+ print sum
+ >>> (1, 'hello', (1, 2))
+
+ # C++ std::map as native python sequence
+ m = pymap()
+ m["foo"] = "hello"
+ m[1] = (1,2)
+ pm = {}
+ for k in m:
+ pm[k] = m[k]
+ print pm
+ >>> {1: (1, 2), 'foo': 'hello'}
+
+ ie, the STD/STL containers work as real native python
+ container, with arbitrary item types and so.
+
+ But since normal C++ containers do not properly ref/unref
+ their items, you should use the safer versions:
+
+ %template(pyset) std::set<swig::PyObject_ptr>;
+ %template(pyvector) std::vector<swig::PyObject_ptr>;
+ %template() std::pair<swig::PyObject_ptr, swig::PyObject_ptr>;
+ %template(pyvector) std::map<swig::PyObject_ptr,swig::PyObject_ptr>;
+ ....
+
+ where swig::PyObject_ptr is a PyObject * envelope class provided
+ to safely incref/decref the python object.
+
+ So, now you can use all the STL/STD containers as native
+ Python containers.
+
+ Note 1: std::map, std::set and the other 'ordered'
+ containers will properly use PyObject_Compare for sorting,
+ when needed.
+
+ Note 2: all the STL/STD containers have a limit size of
+ SIZE_MAX, ie, you can have manage containers larger than
+ INT_MAX, the python limit.
+
+
+11/02/2005: mmatus
+
+ [Python]
+ - add 'iterator()' method for all sequences and additionally
+ 'key_iterator()' for maps.
+
+ 'iterator()' will always return the native C++ iterator.
+ Additionally, in maps, 'key_iterator()' will return a python
+ iterator using only the map keys.
+
+ In general the sequence method __iter__ will call
+ 'iterator()', returning the native C++ iterator, but in
+ maps it will call 'key_iterator()', maintaining
+ backward compatibility.
+
+ Hence, for std::maps, you can play then with the native
+ C++ iterator, which value is a (key, value) pair, by
+ calling map.iterator(), as with map.begin(), map.end(), etc.
+
+ The difference is that map.iterator() returns a safe
+ 'closed' iterator, while map.begin() and map.end() are
+ 'open' iterators.
+
+ A 'closed' iterator knows the begin and the end of the
+ sequence, and it never can seg. fault. An 'open'
+ iterator, as in C++, can seg. fault at the C++ side.
+
+ # a closed iterator is safe in the following example.
+ # the next() method will throw a StopIteration
+ # exception as needed
+
+ i = seq.iterator()
+ try:
+ while True:
+ sum += i.next()
+ except: pass
+
+ # an open iterator always need to be checked,
+ # or it will crash at the C++ side
+
+ current = seq.begin()
+ end = seq.end()
+ while (current != end):
+ sum += current.next()
+
+
+ [Python]
+ - Finally, when we call
+
+ f = Foo()
+
+ the construction is 'one-way'. Before construction was done
+ something like
+
+ Foo() (python) -> _new_Foo() (C++)
+ new_Foo() (C++) -> FooPtr() (python)
+ FooPtr() (python) -> Foo() (python)
+
+ and returning a pointer was done like
+
+ NewPointerObj() (C++) -> FooPtr() (python)
+ FooPtr(python) -> Foo() (python)
+
+
+ ie, we when going back and forward between the C++ and
+ python side.
+
+ Now since there is no FooPtr the construction process is
+
+ Foo() (python) -> _new_Foo() (C++)
+ _new_Foo() (C++) -> NewPointerObj() (C++) (no shadow class)
+
+ and returning a pointer is done
+
+ NewPointerObj() (C++) (with shadow class) -> NewInstaceObj() (C++)
+
+ where NewInstanceObj creates a new instance without
+ calling __init__ and it doesn't go 'back' to python, is
+ 'pure' C API.
+
+ - With this change, and the other ones in the
+ PySwigObject type, which now carries the thisown and
+ swig_type_info pointer, the generated code should be as
+ fast as boost::Python and/or the other python wrappers
+ based in pure Python/C API calls.
+
+ As a reference, the profiletest_runme.py example, which
+ does a simple call function many times, such as this code:
+
+ import profiletest
+
+ a = profiletest.A()
+ b = profiletest.B()
+ for i in range(0,1000000)
+ a = b.fn(a)
+
+
+ where fn is defined as 'A* B::fn(A *a) {return a;}',
+ produces the following times
+
+ nomodern modern
+ swig-1.3.26 19.70s 5.98s
+ swig-CVS 0.99s 0.98s
+
+
+ Clearly, there is a large improvement for the python
+ 'nomodern' mode. Still, the 'modern' mode is around
+ 6 times faster than before. For the same test, but
+ using the non-shadow version of the module, we get
+
+ _profiletest (non-shadow)
+ swig-1.3.26 0.80s
+ swig-CVS 0.60s
+
+ Hence, now for practical purposes, the proxy overhead
+ is insignificant.
+
+ Note that the performance numbers we are showing is for
+ a simple module (two types) and a simple function (one
+ argument). For real situations, for modules with many
+ more types and/or functions with many more parameters,
+ you will see even better results.
+
+
+10/31/2005: mmatus
+ [Python]
+
+ - Finally, no more ClassPtr proxy classes. You will see
+ only a clean Class proxy class in the .py file.
+
+ - No more 'real' thisown attribute either, the PySwigObject now
+ carries the ownership info.
+
+ You can also do something like
+
+ print self.this.own()
+ >>> True
+
+ self.this.disown()
+ self.this.own(0)
+ print self.this.own()
+ >>> False
+
+ self.this.acquire()
+ self.this.own(1)
+ print self.this.own()
+ >>> True
+
+ Still the old way,
+
+ print self.thisown
+ >>> True
+
+ self.thisown = 0
+ print self.thisown
+ >>> False
+
+ self.thisown = 1
+ print self.thisown
+ >>> True
+
+ is supported, and python dispatches the proper method
+ calls as needed.
+
+
+ - Support for iterators in STL/STD containers, for example, if you have
+
+ %template<set_string> std::set<std::string>;
+
+ you can use the C++ iterators as:
+
+ s = set_string()
+
+ s.append("c")
+ s.append("a")
+ s.append("b")
+
+ b = s.begin()
+ e = s.end()
+ sum = ""
+ while (b != e):
+ sum += b.next()
+ print sum
+
+ >>> "abc"
+
+ advance the iterator as in C++
+
+ current = s.begin()
+ current += 1
+ print current.value()
+ >>> "b"
+
+ now using the reverse operators
+
+ b = s.rbegin()
+ e = s.rend()
+ sum = ""
+ while (b != e):
+ sum += b.next()
+ print sum
+
+ >>> "cba"
+
+ or the 'previous' method
+
+ b = s.begin()
+ e = s.end()
+ sum = ""
+ while (b != e):
+ sum += e.previous()
+ print sum
+
+ >>> "cba"
+
+ or just as in a python fashion
+
+ for i in s:
+ sum += i
+
+ Note 1: Iterators in C++ are very powerful, but
+ dangerous too. And in python you can shoot yourself in the foot
+ just like in C++, so, be careful.
+
+ Note 2: the iterators are 'light', ie, they do not
+ convert sequence elements until you request to do so, via
+ next(), value() or previous(). If you just increment/decrement one
+ no conversion is performed, for example:
+
+
+ b = s.begin()
+ b += 1
+ b.incr()
+ b.incr(2)
+ b.decr(2)
+ b.decr()
+ b -= 1
+
+ only the iterator is modified, and not value wrapper
+ is generated. Other typical C++ operations are also
+ available, such as:
+
+ print s.end() - s.begin()
+ >>> 3
+ f = s.begin() + 1
+ print f.value()
+ >>> "b"
+ l = s.end() - 1
+ print l.value()
+ >>> "c"
+
+ etc. Of course, the 'find', 'insert', 'erase', and
+ so on methods also supports iterators now, ie:
+
+ i = s.begin()
+ i += 1
+ s.erase(i)
+ for i in s:
+ sum += i
+ print sum
+ >>> "ac"
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ There is no more 'thisown' attribute. If you use it, python
+ will translate the following code as follows:
+
+ if (self.thisown): ==> if (self.this.own()):
+ self.thisown = 1 ==> self.this.own(1)
+ self.thisown = 0 ==> self.this.own(0)
+
+ Still, maybe in some unusual cases the translation will not be
+ 100% correct, so if you have a problem, please report it
+ and/or use the new 'self.this.own()' accessor.
+
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ There is no more ClassPtr classes in the python code. Hence,
+ if in the past you needed to resort to some kind of trickery
+ with them, or overcome their presence, it is no longer
+ required, but the extra code you added could now break
+ things.
+
+ If needed, you can use the option -classptr, i.e.,
+
+ swig -classptr -python ...
+
+ to generate the old ClassPtr classes.
+
+
+10/30/2005: mkoeppe
+ [Guile] Make declared and defined linkage of SWIG_init consistent.
+ Reported by Steven G. Johnson (SF patch 1315498).
+
+10/26/2005: mmatus
+
+ - Added the attribute.i file to the global library director.
+ Now it can be used from other languages that do not use
+ the unified typemap library as well.
+
+ So, if you have something like:
+
+ %include attribute.i
+
+ %attribute(A, int, a, get_a, set_a);
+
+ struct A
+ {
+ int get_a() const;
+ void set_a(int aa);
+ };
+
+ %attribute_ref(B, int, c);
+
+ struct B
+ {
+ int& c();
+ };
+
+ then in the target language the 'A.a' and 'B.c' attributes will
+ be visible, ie, you can access them as plain variables:
+
+ f = A()
+ f.a = 3
+ g = B()
+ g.c = 3
+
+ h = f.a + g.c
+
+ and the proper get/set methods will be dispatched. See
+ attribute.i for more info.
+
+ - More cleanups around and adding more test-cases. The
+ DISOWN typemap now is tested and working in all the
+ languages that use the unified typemap library, ie, tcl,
+ ruby, perl and python.
+
+
+10/25/2005: mmatus
+
+ - Perl, complete the DISOWN typemap.
+
+ - added the attribute.i file to the unified typemap
+ library (before was only usable from python).
+
+ - unify the names for the setter and getter methods in
+ perl,tcl,ruby and python, so, the attribute.i library
+ can work across them.
+
+ - see the li_attribute.i test-case or the library file
+
+ Lib/typemaps/attribute.swg
+
+ for more info about how to use it.
+
+
+
+
+10/24/2005: mmatus
+
+ - Perl now uses the unified typemap library.
+
+ - Changes in ruby to use the $track option in typemaps.
+
+ - Changes in the unified typemap library to follow the
+ convention that all macros that are not used in the
+ C/C++ side starts with %, such as
+
+ %delete
+ %new_array
+
+ etc.
+
+ - Documenting fragments, see fragments.swg.
+
+ - Cleaner way to use the unified typemap library, include
+ just <typemaps/swigtypes.swg>.
+
+ Check some of the supported languages: perl, tcl, ruby,
+ python.
+
+ Always start with the head file, such as
+
+ python/python.swg
+ tcl/tcl8.swg
+ ruby/ruby.swg
+ perl5/perl5.swg
+
+ and the principal file that invokes the unified library, such as
+
+ python/pytypemaps.swg
+ tcl/tcltypemaps.swg
+ ruby/rubytypemaps.swg
+ perl/perltypemaps.swg
+
+ The file that provide the specialization for each
+ language are the one that provides the basic types:
+
+ python/pyprimtypes.swg
+ ruby/rubyprimtypes.swg
+ tcl/tclprimtypes.swg
+ perl5/perlprimtypes.swg
+
+ and the string manipulation:
+
+ python/pystrings.swg
+ ruby/rubystrings.swg
+ tcl/tclstrings.swg
+ perl5/perlstrings.swg
+
+
+ The rest of the files, such as carray.i, are mostly one
+ line files that include the proper typemap library
+ version.
+
+ *** POTENTIAL INCOMPATIBILITY in Perl ***
+
+ Some missing/wrong typemaps could start working properly,
+ and change the old expected behavior in Perl.
+
+10/23/2005: wuzzeb
+ Chicken:
+ + pointers to member functions finally work properly
+ + add test of member function pointers to cpp_basic.i
+
+10/20/2005: dancy
+ [allegrocl] Added C++ support. Large update, many changes. See
+ newly added Allegro Common Lisp section in lisp.html
+
+10/20/2005: mmatus
+ Ruby, Tcl, Python:
+
+ - Uniform way to fail (label fail:), now finally
+ SWIG_exception works across the three languages and all
+ the typemaps.
+
+ - Add proper cleanup code to ruby
+
+ - More valgrind fixes
+
+ - Simplify the inline use, it seems a small interface of
+ 20,000 lines (plus many many templates) can break
+ gcc -O3 easily.
+
+ - Finalize the typemaps library. All the old *.i files
+ (carray.i, cpointer.i, exception.i) had been implemented
+ in the new typemaps library.
+
+
+10/19/2005: wuzzeb
+ Update the Runtime Typemap documentation in Typemaps.html
+
+10/18/2005: wuzzeb
+ Chicken:
+ - Correctly handle %ignored classes
+ - Correctly convert long, long long, unsigned long, etc
+ to chicken primitives. (Thanks to Felix Winkelmann)
+ - Using argout parameters when the return value was a
+ wrapped pointer caused a memory corruption. The chicken
+ garbage collector moved a pointer out from under us.
+ This is now fixed by running all the proxy creation
+ functions as continuations after the wrapper function
+ returns. As part of this, we no longer need the
+ chickenfastproxy flag on output typemaps.
+ - using -proxy and -nocollection together works now
+ Before, it was not exporting the destructor in the proxy
+ wrapper.
+
+10/18/2005: mmatus
+
+ Added the Unified Typemap Library (UTL). It unifies the typemaps for
+
+ python, ruby, tcl
+
+ and in the process, fixes several problems in each of the three
+ languages to work in a "canonical" way now established in
+ the typemap library
+
+ SWIG/Lib/typempas
+
+ The current status of the unification is that everything
+ compiles and runs inside the test-suite and examples
+ directories. And for the first time we have three
+ languages than pass the primitive_types.i case.
+
+ Also, we have a uniform way to treat the errors, for example
+ if you do something like
+
+ >>> from primitive_types import *
+ >>> print val_uchar(10)
+ 10
+ >>> print val_uchar(1000)
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ OverflowError: in argument 1 of type 'unsigned char'
+
+ you get the same exception in all the three languages.
+
+ And well, many more good things will come from this
+ unification, for example, proper support of the STL/STD classes
+ for all the languages, and hopefully, we can keep
+ adding other languages.
+
+ The hardest part, writing a common typemap library
+ that suites the three different languages, is done,
+ and adding another language should now be easy.
+
+ Still the global unification is not complete, the STL/STD
+ part is next, and probably as well as adding one or two more
+ languages.
+
+ If you are curious, look at the python, ruby and/or tcl
+ directories to see what is needed to support the new
+ common typemaps library. Still, the final way to
+ integrate a new language could change as we move to
+ integrate the STD/STL.
+
+ *** POTENTIAL INCOMPATIBILITY in Ruby/Tcl ***
+
+ Some missing/wrong typemaps could start working properly,
+ and change the old behavior, specially in ruby and tcl.
+
+Version 1.3.27 (October 15, 2005)
+=================================
+
+10/15/2005: wsfulton
+ [Java] Fix for typesafe enum wrapping so that it is possible to
+ overload a method with 2 different enum types.
+
+10/15/2005: wsfulton
+ Fix for %feature("immutable","0") attempting to generate setters
+ for constants.
+
+ Restored %immutable and %makedefault to clear the feature as it
+ behaved in SWIG-1.3.25 and earlier.
+
+10/14/2005: mmatus
+ Fix bug in anonymous typedef structures which was leading to
+ strange behaviour.
+
+10/13/2005: mmatus
+ Several minor changes:
+
+ - Improve the wchar_t type support
+ - Add a warning for when you define the 'in' typemap but
+ you don't define the 'typecheck' one. Very common mistake.
+ - Add proper default rule for function pointers, now you
+ can define a typemap such as:
+
+ %typemap(in) SWIGTYPE ((*)(ANY)) {...}
+
+ That will apply to all the pointer to functions. The
+ rule in C++ also apply to the function 'reference', ie,
+ in both cases
+
+ typedef int (*fptr)(int a);
+ typedef int (func)(int a);
+
+ This was needed since it seems to be 'illegal' in C++ to
+ do something like:
+
+ void *ptr = static_cast<void *>(fptr);
+
+ and probably, as for member functions, it is not
+ warrantied that the pointer sizes will match.
+
+ - Add the #error/#warning directives to swig's cpp.
+
+ - Add the noblock option for typemaps, which is used as
+ follows: supposed you a typemap, like this
+
+
+ %typemap(in,noblock=1) Hello {
+ ....
+ }
+
+ then the typemap will be inserted without the block
+ imposed by the brackets, similar to
+
+ %typemap(in) Hello "..."
+
+ So, why you don't just use the quote style?, because:
+
+ 1.- The quote style doesn't get preprocessed, for example
+
+ %typemap(in) Hello "$1= SWIG_macro($1);"
+
+ here, SWIG_macro doesn't get expanded
+
+ 2.- Inside a quote typemap, you have to use
+ quotes carefully
+
+ %typemap(in) Hello "$1 = \"hello\" "
+
+ 3.- You can't make emacs and/or other editors
+ to indent inside a string!.
+
+
+ So, why do you want to remove the block?, because an extra
+ block when not needed (no local variables in it):
+
+ 1.- makes the code harder to read
+ 2.- makes the code larger
+ 3.- or in short, for the same reason we have the quote style.
+
+Version 1.3.26 (October 9, 2005)
+================================
+
+10/08/2005: wsfulton
+ [Php] Added 'throws' typemaps.
+
+10/08/2005: wsfulton
+ Fixes for languages that don't support multiple inheritance. The
+ first non-ignored class in the public base class list is used for inheritance.
+ by the proxy class. Previously, if the first class in the list was ignored, then
+ the proxy class wouldn't have any base classes.
+
+10/07/2005: mmatus
+ Update more features to follow new convention, including:
+
+ callback
+ ref/unref
+ except
+
+ All of them use not only the feature as a flag, but also
+ as code value. To deal with those features, we use now
+ GetFlagAttr, which is similar to GetFlag, but instead or
+ returning 1 or 0, it returns the attr value, if happens
+ to be different of "0" of course.
+
+ Now there are also more uniform directive names for the
+ ones based in features, for example, for the old
+ %newobject directive now we have tree directives defined:
+
+
+ #define %newobject %feature("new")
+ #define %nonewobject %feature("new","0")
+ #define %clearnewobject %feature("new","")
+
+ and so on for all the other feature directives.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+09/30/2005: wsfulton
+ Subtle change to some features. Previously it was not possible to disable many
+ features once they had been enabled. This was for most features that behave as
+ flags. These features now work as follows:
+
+ %feature("name") // enables the feature
+ %feature("name", "1") // enables the feature
+ %feature("name", "0") // disables the feature
+ %feature("name", "") // clears the feature
+
+ In fact any non-empty value other than "0" will enable the feature (like C boolean logic).
+ Previously "1", "0" or any other non-empty value would enable the feature and it would
+ only be possible to disable the feature by clearing it (assuming there was no global enable).
+
+ The following features are affected:
+
+ allowexcept
+ compactdefaultargs
+ classic (Python)
+ cs:const (C#)
+ director
+ exceptionclass (Python)
+ ignore
+ immutable
+ java:const (Java)
+ java:downcast (Java)
+ kwargs
+ modern (Python)
+ new
+ noautodoc (Python)
+ nodefault
+ nodirector
+ noref
+ notabstract
+ nounref
+ novaluewrapper
+ python:maybecall (Python)
+ python:nondynamic (Python)
+ modula3:multiretval (Modula3)
+ predicate (Ruby)
+ trackobjects (Ruby)
+ valuewrapper
+
+ It is now possible, for example to ignore all methods/classes in a header file, except for a
+ few targetted methods, for example:
+
+ %feature("ignore"); // ignore all methods/classes
+ %feature("ignore","0") some_function(int, double); // do not ignore this function
+ %feature("ignore","0") SomeClass; // do not ignore this Class
+ %feature("ignore","0") SomeClass::method; // do not ignore this method
+ %include "bigheader.h"
+
+ Removed %pythondynamic - it never worked properly. Use %pythonnondynamic instead.
+ Removed %feature("nokwargs") - it wasn't fully implemented - use %feature("kwargs","0") instead.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+09/25/2005: mkoeppe
+ [Guile] Add "throws" typemaps.
+
+09/24/2005: cfisavage
+ [Ruby] Adds new %trackobjects functionality that maps C++ objects to
+ Ruby objects. This functionality makes it much easier to implement
+ mark functions for the garbage collector. For more information
+ refer to the update documentation and examples.
+
+09/20/2005: wsfulton
+ [Perl] Patch 1116431 from Josh Cherry. Fixes non member functions inadvertently
+ being called instead of member functions.
+
+09/20/2005: wsfulton
+ [Lua] Patch from Mark Gossage to add support for Lua-5.1, std::string,
+ std::vector, std::exception and documentation.
+
+09/14/2005: mmatus
+ [Python] Add -nocppcast. Now the default behavior is to
+ always use the cppcast operators. Before that was the case
+ only when you used the -cppcast option.
+
+ If this seems to break your code... your welcome!, it
+ means it was broken before, and you never notice.
+
+ If you thing the error is due to one of the SWIG typemaps,
+ send us an example.
+
+ Use -nocppcast only with very old C++ compilers that
+ do not support the cppcast operations.
+
+ So, here applies:
+
+ This change doesn't break compatibility, it was broken before.
+
+09/13/2005: wsfulton
+ [Java] Fix for director methods when a class is passed by value as a
+ parameter.
+
+09/11/2005: mmatus
+ Adding the module option to the %import directive. Now you
+ can use it as
+
+ %import(module="BigModule") foo.i
+
+ where subfile could (or not) define the module name via
+ the %module directive. The module option take precedence
+ and it has the same effects than having the directive
+
+ %module BigModule
+
+ inside the imported file foo.i.
+
+ You can use the option in mainly two cases:
+
+ 1.- You used the -module option when you generated the
+ module to be imported, and hence the module name in
+ the imported %module directive is not really useful.
+
+ 2.- The module you want to import is very large, and it
+ has several .i/.h files. Then, if you just one to
+ import a class or so from the module, says 'foo', and
+ not the entire module via importing the main
+ BigModule.i file, then you just do:
+
+ %import(module="BigModule") foo.h
+
+ or
+
+ %import(module="BigModule") foo.i
+
+ where foo.i contains the 'foo' declaration and maybe a
+ couple of extra %include directives, as needed.
+
+
+09/11/2005: mmatus
+ Fix bug #1282637, about the -module option not having effect
+ in places where it was needed.
+
+09/11/2005: wsfulton
+ When wrapping variables, ensure that none of the typemaps used for the
+ set wrappers are used when generating the get wrappers. I doubt this was a
+ problem for any languages except for the recently introduced null attribute
+ in the out typemap (C# only).
+
+09/08/2005: wsfulton
+ More descriptive error messages when files fail to open.
+
+09/06/2005: mmatus
+
+ Allow a %define a macro inside another %define macro, for example
+
+ %define hello(name, Type)
+ %define name ## a(Type)
+ %typemap(in) Type "hello;"
+ %enddef
+ %enddef
+
+ To learn how to use this new features in your own typemaps library, see
+ python/cstring.i, python/cwstring.i and python/cwstrbase.i.
+
+ [Python] Normalize the cstring.i implementation to use fragments, and add
+ cwstring.i, which implements the same typemaps but for wchar_t strings.
+
+ [Python] Bug fixed: 1247477, 1245591, 1249878 and others.
+
+08/18/2005: wsfulton
+ [Ruby] Implement support for SWIGTYPE* DISOWN typemap (like in Python) for
+ better control of memory management, eg when adding an object created in Ruby
+ to a C++ container. Patch #1261692 from Charlie Savage.
+
+08/18/2005: wsfulton
+ [Tcl] 64 bit platform fixes for the varargs handling in SWIG_GetArgs. This is an
+ improved fix for bug #1011604 as suggested by Jeremy Lin.
+
+08/18/2005: wsfulton
+ [Tcl] Bug #1240469 - %newobject support for Tcl. Patch from Bob Marinier.
+
+08/16/2005: wsfulton
+ [Perl] Bug #1254494 - Fix for global namespace pollution by perl header files
+ (bool define) prevented STL headers from being used on some systems, eg
+ Windows with Visual Studio.
+
+08/16/2005: wsfulton
+ [Java] Bug #1240937 - Redefinition of __int64 typedef for Intel compilers.
+
+08/15/2005: wsfulton
+ [Xml] Bug #1251832 - C++ template may generate invalid XML file
+
+08/15/2005: wsfulton
+ [Lua] Support added for Lua. Patch #1242772 from Mark Gossage.
+ It supports most C/C++ features (functions, struct, classes, arrays, pointers,
+ exceptions), as well as lots of documentation and a few test cases & examples.
+
+08/14/2005: wsfulton
+ [Xml] Fix incorrect xml escaping in base class name when base class is a template.
+
+08/13/2005: efuzzyone
+ [CLISP] Added support for handling enums. Does not adds the return type declaration
+ to the function definition, if a function returns void.
+
+08/09/2005: mkoeppe
+ New language module, Common Lisp with UFFI, from Utz-Uwe Haus.
+
+08/09/2005: mkoeppe
+ Fix the Lisp s-expression output module; it no longer complains about "unknown targets".
+
+07/27/2005: wsfulton
+ Modifications to STL wrappers so that it is possible for a user's %exception directive
+ to be applied to the STL wrapper methods. Previously the following global %exception
+ directive would not be used on the wrapper methods:
+
+ %exception {
+ try {
+ $action
+ } catch (...) {
+ // handle uncaught exceptions
+ }
+ }
+
+ This has been implemented by replacing %exception directives for specific STL wrapper
+ methods with an exception specification declared on the wrapper methods. throws typemaps
+ are now supplied for handling the STL exception specification. These can also be easily
+ overridden, for example the std::out_of_range exception, which is used a lot in the STL
+ wrappers, can be customised easily:
+
+ %include "std_vector.i"
+ %typemap(throws) std::out_of_range {
+ // custom exception handler
+ }
+ %template(VectInt) std::vector<int>;
+
+07/22/2005: efuzzyone
+ [CLISP] The clisp module for SWIG:
+ - It can only handle C, clisp currently does not supports ffi bindings to C++.
+ - It has two options, (a) -extern-all this will generate wrappers for all functions
+ and variablestions, (b) -generate-typedef this will generate wrappers "def-c-type"
+ wrappers for typedefs
+ - Can handle pointers to functions, complex types such as n-dimensional arrays of
+ pointers of depth d
+ - Generates wrappers for constants as well as variables
+ - Correctly distinguishes between the declaration of variables in structures and functions
+ - Creates a defpackage "declaration" with the module name as the package name, the created
+ package exports both functions and variables
+ - tries to guess when should a pointer variable be declared as c-ptr or c-pointer
+
+07/22/2005: wsfulton
+ [C#] Changes to support C# structs returned by value. The changes required are:
+ - Using an optional 'null' attribute in the out typemap. If this attribute is specified,
+ then it is used for the $null special variable substitution.
+ - The ctype used in the C/C++ wrappers is no longer initialised to 0 on declaration.
+ Both of these changes fix the situations where an attempt was made to assign 0 to the
+ returned struct. Marshalling structs as value types still requires user defined typemaps.
+ See documentation for an example.
+
+07/22/2005: wsfulton
+ [C#, Java] Fix SWIG_exception usage to work with compilers that don't support empty macro
+ arguments. Unfortunately this fix will stop usage of SWIG_exception being used within typemaps
+ that use "" or %{ %} delimiters, but continues to work with typemaps using {} delimiters.
+ Please use the SWIG_CSharpSetPendingExceptionArgument or SWIG_JavaThrowException methods instead
+ as SWIG_exception is really intended as a platform independent macro for the SWIG library writers.
+
+07/16/2005: mkoeppe
+ [Allegro CL] Use specific foreign types rather than (* :void).
+ Use *swig-identifier-converter*.
+
+06/27/2005: wsfulton
+ Functions declared as 'extern' no longer have an additional function declaration added to the
+ wrapper files. There are some cases where SWIG does not get this right, eg bug #1205859 (extern
+ functions with default arguments declared in a namespace). Also SWIG cannot get non-standard
+ calling conventions correct, eg Windows calling conventions are usually handled like this:
+
+ %{
+ #define DLLIMPORT __declspec(dllimport)
+ #define STDCALL __stdcall
+ %}
+ #define DLLIMPORT
+ #define STDCALL
+ %inline %{
+ DLLIMPORT extern STDCALL void function(int);
+ %}
+
+ SWIG incorrectly generates:
+
+ extern void function(int);
+
+ To which there is no solution as SWIG doesn't handle non-standard calling conventions. The extra
+ 'extern' function that SWIG generates is superfluous unless a user has forgotten to add the function
+ declaration into the wrappers.
+
+ The -noextern commandline argument is now redundant and a new commandline argument -addextern can
+ be used to obtain the original behaviour. This shouldn't be necessary unless the header file
+ containing the function declaration was inadvertently not added to the wrappers. To fix this
+ add the function declaration into your wrappers, For example, replace:
+
+ extern void foo(int);
+
+ with:
+
+ %inline %{
+ extern void foo(int);
+ %}
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+06/22/2005: wsfulton
+ [C#, Java, Modula3, Ocaml]
+ The intermediary function names have been changed when wrapping variables to
+ match the other language modules so that %extend for a member variable works
+ uniformly across all language modules, eg:
+
+ %extend ExtendMe {
+ Var;
+ };
+
+ %{
+ void ExtendMe_Var_set(ExtendMe *, double) {...}
+ double ExtendMe_Var_get(ExtendMe *) {...}
+ %}
+
+ The methods implementing the get/set used to be:
+
+ %{
+ void set_ExtendMe_Var(ExtendMe *, double) {...}
+ double get_ExtendMe_Var(ExtendMe *) {...}
+ %}
+
+ This also changes the name of variable wrapper functions when using -noproxy.
+ The original names can be generated with the -oldvarnames commandline option.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+Version 1.3.25 (June 11, 2005)
+==============================
+
+06/11/2006: mkoeppe
+ [Guile] Fix handling of anonymous-enum variables.
+
+06/10/2005: mkoeppe
+ [Guile] Fix for function arguments that are passed by
+ copy-of-value. Fix for global "const char *" variables.
+ Fix testcases arrays_dimensionless, arrays_global.
+
+06/08/2005: wsfulton
+ Fix for when a base class defines a symbol as a member variable and a derived class defines
+ the same symbol as a member method.
+
+06/08/2005: wsfulton
+ [C#] More fixes for virtual/new/override modifiers - when a method has protected access
+ in base and public access in derived class.
+
+06/02/2005: wsfulton
+ Fix #1066363 - Follow convention of release tarball name matching directory name.
+
+06/02/2005: wsfulton
+ [C#, Java] Fix #1211353 - typesafe enums (and Java proper enums) wrappers when enum value
+ is negative.
+
+05/27/2005: wsfulton
+ Modernised and tidied up Windows macros --> SWIGEXPORT, SWIGSTDCALL. They can be overridden
+ by users via -D compiler directives if need be.
+
+05/26/2005: wsfulton
+ %csmethodmodifiers can be applied to variables as well as methods now.
+
+ In addition to the default 'public' modifier that SWIG generates, %csmethodmodifiers will also
+ replace the virtual/new/override modifiers that SWIG thinks is appropriate. This feature is
+ useful for some obscure cases where SWIG might get the modifiers incorrect, for example
+ with multiple inheritance and overriding a method in the base class.
+
+ *** POTENTIAL INCOMPATIBILITY FOR C# MODULE ***
+
+05/25/2005: wsfulton
+ Added missing constructors to std::pair wrappers (std_pair.i) for all languages.
+
+05/25/2005: wsfulton
+ [C#] Added std::pair wrappers in std_pair.i
+
+05/25/2005: wsfulton
+ [C#] The C# 'new' and 'override' modifiers will be generated when a C++ class inherits methods
+ via a C++ 'using' declaration.
+
+05/25/2005: wsfulton
+ Fix for exception specifications previously being ignored in classes that inherited methods
+ from 'using' declarations, eg calls to Derived::bar below will convert C++ exceptions into
+ a target language exception/error, like it always has done for Base::Bar.
+
+ class Base {
+ virtual bar() throw (std::string);
+ };
+ class Derived : public Base {
+ using Base::bar;
+ };
+
+05/23/2005: wsfulton
+ Fixes for detecting virtual methods in %extend for the -fvirtual option and C# override and new
+ method modifiers.
+
+05/23/2005: wsfulton
+ [C#] The 'new' modifier is now generated on the proxy method when a method in a derived
+ class is not polymorphic and the same method exists in the derived class (ie it hides
+ the base class' non-virtual method).
+
+05/23/2005: wsfulton
+ [Java, C#] Fixes to detection of covariant return types - when the class hierarchy is more
+ than 2 classes deep.
+
+05/21/2005: wsfulton
+ [Java] std::wstring typemaps moved from std_string.i to std_wstring.i
+
+05/21/2005: wsfulton
+ Fix for crash in DohStrstr, bug #1190921
+
+05/21/2005: wsfulton
+ [TCL] Fix for methods with similar names when showing list of names on error - bug #1191828.
+ Patch from Jeroen Dobbelaere.
+
+05/21/2005: wsfulton
+ [TCL] long long overloading fix - bug #1191835, patch from Jeroen Dobbelaere.
+
+05/21/2005: wsfulton
+ Fix bug #1196755 to remove debug from swigtcl8.swg.
+
+05/19/2005: wsfulton
+ [C# and -fvirtual option] Fix for the override key not being generated in the derived class when a
+ virtual method's return type was a typedef in either the base or derived class. Also ensures the
+ method is eliminated when using the -fvirtual option. For example, Derived.method now has the C#
+ override keyword generated:
+
+ typedef int* IntegerPtr;
+
+ struct Base {
+ virtual IntegerPtr method();
+ };
+
+ struct Derived : Base {
+ int * method() const;
+ };
+
+ [C#] Fix for the override key being incorrectly generated for virtual methods when a base class
+ is ignored with %ignore.
+
+05/13/2005: wsfulton
+ [Java] Fixes to remove "dereferencing type-punned pointer will break strict-aliasing rules"
+ warnings in C wrappers when compiling C code with 'gcc -Wall -fstrict-aliasing'. Patch from
+ Michael Cahill. This modifies many of the casts slightly, for example
+ arg1 = *(DB_ENV **)&jarg1;
+ to
+ arg1 = *(DB_ENV **)(void *)&jarg1;
+
+05/12/2005: wsfulton
+ [C#] Support for C# attributes. C# attributes can be generated:
+ 1) On a C/C++ type basis by specifying an inattributes and/or outattributes typemap attribute
+ in the imtype or cstype typemaps (for C# return type or C# parameter type attributes).
+ 2) On a wrapped method or variable by specifying a csattributes feature (%feature).
+ 3) On a wrapped proxy class or enum by specifying a csattributes typemap.
+
+ Examples are in the C# documentation (CSharp.html).
+
+04/29/2005: wsfulton
+ New configure option to turn off the default maximum compiler warning as
+ they couldn't be removed even when overriding CFLAGS and CXXFLAGS with configure
+ (./configure CFLAGS= CXXFLAGS=). To turn the maximum warnings off, run:
+
+ ./configure --without-maximum-compile-warnings
+
+04/28/2005: wsfulton
+ Patch from Scott Michel which reworks the Java constructor and finalize/destructor typemaps,
+ for directors to reduce the number of overall Java typemaps. Added the director_take and
+ director_release typemaps to emulate other modules' __disown__ functionality.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA DIRECTORS ***
+
+04/28/2005: wsfulton
+ [C#] Fixed problems due to the over eager garbage collector. Occasionally the
+ garbage collector would collect a C# proxy class instance while it was being used
+ in unmanaged code if the object was passed as a parameter to a wrapped function.
+ Needless to say this caused havoc as the C# proxy class calls the C++ destructor
+ when it is collected. Proxy classes and type wrapper classes now use a HandleRef,
+ which holds an IntPtr, instead of a plain IntPtr to marshal the C++ pointer to unmanaged
+ code. There doesn't appear to be any performance degradation as a result of this
+ modification.
+
+ The changes are in the proxy and type wrapper classes. The swigCPtr is now of type HandleRef
+ instead of IntPtr and consequently the getCPtr method return type has also changed. The net
+ effect is that any custom written typemaps might have to be modified to suite. Affected users
+ should note that the implementation uses the new 'out' attribute in the imtype typemap as the
+ input type is now a HandleRef and the output type is still an IntPtr.
+
+ *** POTENTIAL INCOMPATIBILITY FOR C# MODULE ***
+
+04/28/2005: wsfulton
+ [C#] Support for asymmetric type marshalling added. Sometimes the output type needs to be
+ different to the input type. Support for this comes in the form of a new optional 'out'
+ attribute for the ctype, imtype and cstype typemaps. If this typemap attribute is not
+ specified, then the type used for both input and output is the type specified in the
+ typemap, as has always previously been the case. If this typemap attribute is specified,
+ then the type specified in the attribute is used for output types and the type specified
+ in the typemap itself is used for the input type. An output type is a return value from
+ a wrapped method or wrapped constant and an input type is a parameter in a wrapped method.
+
+ An example shows that char * could be marshalled in different ways,
+
+ %typemap(imtype, out="IntPtr") char * "string"
+ char * function(char *);
+
+ The output type is thus IntPtr and the input type is string. The resulting intermediary C# code is:
+
+ public static extern IntPtr function(string jarg1);
+
+04/22/2005: mkoeppe (Matthias Koeppe)
+ [Guile] Fix generation of "define-method" for methods of
+ classes with a constructor. Reported by Luigi Ballabio.
+
+04/15/2005: wuzzeb (John Lenz)
+ [Chicken]
+ For wrapped functions that return multiple values (using argout),
+ SWIG CHICKEN now returns them as multiple values instead of as
+ a list. They can then be accessed using (call-with-values).
+
+04/14/2005: wuzzeb (John Lenz)
+ [Chicken]
+ + Added a whole bunch of new _runme scripts into the chicken test
+ suite. Also fix some bugs these new scripts turned up.
+
+ + Added optimization when returning a wrapped proxy class. Before,
+ a minor garbage collection was invoked every time a function returned.
+
+ + All the chicken Examples should now run correctly
+
+04/14/2005: wsfulton
+ [C#] More fixes for typemap matching when wrapping variables, in particular
+ std::string, so that std::string variables can be easily marshalled with
+ a C# string property using:
+
+ %include "std_string.i"
+ %apply const std::string & { std::string *variable_name };
+ std::string variable_name;
+
+ (Recall that all class variables are wrapped using pointers)
+
+04/05/2005: wuzzeb (John Lenz)
+ [Chicken]
+ + Added Examples/chicken/egg, an example on how to build a chicken
+ extension library in the form of an egg. Also updated the
+ documentation on the different linking options.
+
+ + chicken test-suite now has support to check SWIG with the -proxy
+ argument if there exists a _proxy_runme.ss file.
+
+ + More fixes for overloaded functions and -proxy
+
+03/31/2005: wsfulton
+ Turned on extra template features for all languages which were
+ previously only available to Python.
+
+ This enables typemaps defined within a templated class to be used as
+ expected. Requires %template on the templated class, %template() will
+ also pick up the typemaps. Example:
+
+ template <typename T> struct Foo {
+ ...
+ %typemap(in) Foo "in typemap for Foo<T> "
+ or
+ %typemap(in) Foo<T> "in typemap for Foo<T> "
+ };
+
+ %template(Foo_i) Foo<int>;
+ %template() Foo<double>;
+
+ will generate the proper 'in' typemaps wherever Foo<int> and Foo<double>
+ are used.
+
+03/30/2005: mkoeppe (Matthias Koeppe)
+ [MzScheme] Patch from Hans Oesterholt for supporting MzScheme 30x.
+
+03/29/2005: wuzzeb (John Lenz)
+ [Chicken]
+ + Reallow older versions of chicken (1.40 to 1.89) by passing -nocollection
+ argument to SWIG
+ + %import now works correctly with tinyclos. (declare (uses ...)) will be
+ exported correctly.
+ + TinyCLOS proxy classes now work correctly with overloaded functions
+ and constructors.
+
+03/29/2005: wsfulton
+ [Java] Patch from Scott Michel for directorout typemaps. Java directors
+ require the directorout typemaps like the other languages now. The new
+ typemaps provide fixes for methods where the return type is returned
+ by reference (this cannot automatically be made thread safe though).
+
+03/22/2005: wsfulton
+ Enum casting fixes. Visual C++ didn't like the C type casting SWIG produced
+ when wrapping C++ enum references, as reported by Admire Kandawasvika.
+
+03/21/2005: wsfulton
+ [Perl] SF #1124490. Fix Perl macro clashes when using Visual Studio's STL string,
+ so now projects can #include <string>.
+
+03/21/2005: wsfulton
+ Fixed %varargs which got broken with the recent default argument changes.
+ Also works for Java and C# for the first time now.
+
+03/17/2005: wuzzeb (John Lenz)
+ [Chicken]
+ + Fix a whole bunch of bugs in the chicken module. The entire
+ test suite now compiles, with the exception of the tests that require
+ std_vector.i, std_deque.i, and so on, which chicken does not have yet.
+
+ + Add support for %exception and %typemap(exceptions). Exceptions are
+ thrown with a call to (abort) and can be handled by (handle-exceptions)
+
+03/15/2005: wsfulton
+ [Java] Patch from Scott Michel for directors. Modifications to the typemaps
+ giving users fine control over memory ownership and lifetime of director classes.
+ Director classes no longer live forever by default as they are now collectable
+ by the GC.
+
+03/15/2005: wuzzeb (John Lenz)
+ [Chicken] Add support for adding finalizers garbage collected objects.
+ Functions that return new objects should be marked with %newobject and
+ input arguments which consume (or take ownership) of a pointer should
+ be marked with the DISOWN typemap.
+
+ Also add support for correctly checking the number of arguments passed
+ to a function, and raising an error if the wrong number are passed.
+
+03/14/2005: wuzzeb (John Lenz)
+ Add --without-alllang option to configure.in, which is the same as
+ passing all the --without-python --without-perl5 etc... that Matthias added.
+
+03/09/2005: wsfulton
+ [Php] Memory leak fix for functions returning classes/structs by value.
+
+03/08/2005: wsfulton
+ [Perl] Fix for Perl incorrectly taking memory ownership for return types that
+ are typedefs to a struct/class pointer. Reported by Josh Cherry.
+
+03/07/2005: wsfulton
+ [C#] Various exception changes for the std::vector wrappers. These now more
+ accurately mirror the same exceptions that System.Collections.ArrayList throw.
+
+03/07/2005: wsfulton
+ [C#] Fix undefined behaviour after any of the std::vector methods
+ throw an exception.
+
+03/07/2005: wsfulton
+ [C#] When null is passed for a C++ reference or value parameter, the
+ exception thrown has been corrected to an ArgumentNullException instead
+ of NullReferenceException as recommended in the .NET Framework documentation.
+
+ The default throws typemaps turn a C++ exception into an ApplicationException,
+ not a SystemException now.
+
+03/07/2005: wsfulton
+ [C#] Numerous changes in C# exception handling have been made over the past
+ few weeks. A summary follows:
+
+ The way in which C++ exceptions are mapped to C# exceptions is quite different.
+ The change is to fix C# exceptions so that the C++ exception stack is correctly
+ unwound as previously C++ exceptions were being thrown across the C PInvoke layer
+ into the managed world.
+
+ New typemap attributes (canthrow and excode) have been introduced to control the
+ mapping of C++ to C# exceptions. Essentially a callback into the unmanaged world
+ is made to set a pending exception. The exception to throw is stored in thread local
+ storage (so the approach is thread-safe). The typemaps are expected to return
+ from unmanaged code as soon as the pending exception is set. Any pending exceptions
+ are checked for and thrown once managed code starts executing. There should
+ be minimal impact on execution speed during normal behaviour. Full details will be
+ documented in CSharp.html.
+
+ The SWIG_CSharpThrowException() function has been removed and replaced with the
+ SWIG_CSharpSetPendingExceptionArgument() and SWIG_CSharpSetPendingException()
+ functions. The original name has been deliberately changed to break old code as
+ the old approach was somewhat flawed. Any user defined exceptions that follow the
+ same pattern as the old approach should also be fixed.
+
+ Numerous new .NET framework exceptions are now available for easy throwing from
+ unmanaged code. The complete list is:
+
+ ApplicationException, ArithmeticException, DivideByZeroException,
+ IndexOutOfRangeException, InvalidOperationException, IOException,
+ NullReferenceException, OutOfMemoryException, OverflowException,
+ SystemException, ArgumentException, ArgumentNullException and
+ ArgumentOutOfRangeException.
+
+ *** POTENTIAL INCOMPATIBILITY FOR C# MODULE ***
+
+05/05/2005: mmatus
+
+ Fix several memory leaks around. Even when we survive knowning
+ swig is a memory leak factory, it was a little out of
+ control. To run std_containers.i in the python test-suite,
+ swig was using ~260MB, now it uses 'only' ~40MB, which is
+ the same ammount that g++ uses, so, is not that bad.
+ In the process, I found a couple of extra Deletes, which
+ in some cases could trigger seg. faults and/or
+ DOH/asserts.
+
+ [python] Better support for directors + exception. More
+ verbose errors and added an unexpected exception handler.
+
+ [python] Fix memory leak for the
+
+ std::vector<std::vector<int> >
+
+ case,reported by Bo Peng.
+
+ [python] Fix SwigPyObject compare problem reporte by
+ Cameron Patrick.
+
+ [python] Fix several warnings in the generated code
+ for gnu-gcc, Intel and VC7.1 compilers.
+
+
+02/25/2005: wuzzeb (John Lenz)
+ Update documentation to use CSS and <div> instead of <blockquote>
+ I used a script to convert the docs, and it set all the box classes
+ to be "code". There are actually 4 different classes,
+ "shell", "code", "targetlang", and "diagram". We need to go through
+ and convert the divs depending on what they contain.
+
+02/23/2005: mmatus
+
+ [Python] Added option -nortti to disable the use of native
+ C++ RTTI with directors (dynamic_cast<> is not used).
+
+ Add more code for directors to detect and report errors in
+ the python side.
+
+ Extend the use of SWIGINTERN whenever is possible.
+
+ Remove template warnings reported by VC7.1.
+
+ Remove warnings reported by gcc/g++. Finally you can
+ compile using
+
+ g++ -W -Wall -c mymodule_wrap.cxx
+
+ and no spurious errors will be generated in the wrapper
+ code.
+
+02/23/2005: wuzzeb (John Lenz)
+ Added -external-runtime argument. This argument is used to dump
+ out all the code needed for external access to the runtime system,
+ and it replaces including the files directly. This change adds
+ two new virtual functions to the Language class, which are used
+ to find the language specific runtime code. I also updated
+ all languages that use the runtime to implement these two functions.
+
+02/22/2005: mmatus
+ Fix %template + private error SF#1099976.
+
+02/21/2005: mmatus
+
+ Fix swigrun.swg warnings reported when using "gcc -W -Wall"
+ (static/inline not used in front of a function
+ declaration), and add SWIGUNUSED attribute to avoid
+ unused warnings elsewhere.
+
+ Fix unused variable warnings.
+
+ [Python] Use new SWIGUNUSED attribute to avoid warnings in
+ SWIGINTERN methods.
+
+ [Python] Fix PyOS_snprintf for python versions < 2.2 (SF #1104919).
+
+ [Python] Fix map/multimap to allow empty maps (reported by
+ Philippe Hetroy).
+
+ [Docs] Add some documentation to Python.html and
+ SWIGPlus.html, including for example the fact that
+ 'friends' are now supported.
+
+02/21/2005: wsfulton
+ [PHP] Patch from Olly Betts, so that wrappers compile with Zend thread safety enabled.
+
+02/17/2005: wsfulton
+ Memory leak fix in some of the scripting language modules when using default
+ arguments in constructors. The scripting language was not taking ownership of the
+ C++ object memory when any of the constructors that use default arguments was called.
+
+02/16/2005: wsfulton
+ SF #1115055: Failed make install. Patch from Rob Stone.
+
+02/16/2005: wsfulton
+ [Java] SF #1123416 from Paul Moore. Correct memory allocation for STRINGARRAY
+ typemaps in various.i.
+
+02/15/2005: wsfulton
+ Disabled typemap search changes for now (see entry 19/12/2004). It breaks
+ old typemaps, lengthens the execution time by about 25% and introduces
+ inconsistencies.
+
+02/15/2005: wsfulton
+ swig -help follows other software by printing to stdout instead of stderr now.
+ swig -version also displays to stdout instead of stderr now.
+ Behaviour reported by Torsten Landschoff.
+
+02/15/2005: wsfulton
+ [Ruby] Fix for the less commonly used ordering of %include and #include, so
+ that the generated code compiles. Bug reported by reported by Max Bowsher.
+ %include foo.h
+ %{
+ #include foo.h
+ %}
+
+02/15/2005: wsfulton
+ [C#, Java] SWIG_exception macro will now return from unmanaged code / native code
+ as soon as it is called. Fixes possible JVM crashes and other code unexpectedly
+ being executed. Note SWIG_exception is only occasionally used by SWIG library
+ writers, and is best avoided by SWIG users.
+
+02/15/2005: wsfulton
+ [C#, Java] Typemaps can now be targeted at global variable names
+ and static member variable names. Previously the typemaps for
+ the setters were ignored, for example:
+
+ %typemap(in) int globalint "..."
+ int globalint;
+
+02/13/2005: mkoeppe (Matthias Koeppe)
+ [Guile] Add %typecheck for SWIGTYPE, add %typecheck for ptrdiff_t, fix
+ typemaps for size_t.
+
+ [Pike] Merge patch from Torsten Landschoff for improved Pike configuration.
+
+02/12/2005: mkoeppe (Matthias Koeppe)
+ New configure switches --without-tcl, --without-python etc. allow to
+ disable the search for installed languages.
+
+01/31/2005: wuzzeb (John Lenz)
+ - Add DohSortList to DOH
+
+ - Improve the runtime type system:
+ + Speed. Type loading is now O(n log n) instead of O(N^2), which
+ for large modules is a huge improvement.
+ + A whole bunch of functions in swigrun.swg no longer need the
+ swig_type_list_handle passed to them. The only one left is
+ TypeQuery. This also makes runtime.swg a lot smaller.
+ + Split up swig_type_info structure into two structures
+ (swig_type_info and swig_cast_info)
+ + Store a pointer to a swig_type_info rather than just the type
+ name string in the linked list of casts. First off, this makes
+ the guile module a little faster, and second, the
+ SWIG_TypeClientData() function is faster too.
+ + Add the idea of a module into the type system. Before, all the
+ types were stored in one huge linked list. Now, another level is
+ added, and the type system stores a linked list of modules, each
+ of which stores an array of types associated with it.
+ + For more information of how the runtime type system now works,
+ please see Doc/Manual/typemaps.html and Doc/Devel/runtime.txt
+
+ - Update all language modules to use the new type system. The changes
+ to each language module are minor. All languages are now able to
+ use runtime.swg for external access to the type system. Before
+ only python and perl did.
+
+ - [guile, mzscheme, ocaml, and php4] These languages opened up the
+ init function inside the .cxx code, and any code in the .swg files
+ in the init section was inside this function. This was a problem
+ for swiginit.swg, which needs to be inserted before the SWIG_init
+ function is opened. Thus I changed these languages to be like
+ python or perl, where the init function is declared in the .swg
+ file.
+
+ - [Ruby] Instead of moving the init function to the .swg file, I
+ added a new section initbeforefunc, and then added
+ %insert(initbeforefunc) "swiginit.swg"
+
+ - [MzScheme] Fix enums and fix Examples/Makefile.in so that if
+ multiple -I arguments are specified in the INCLUDES variable, each
+ gets a ++ccf.
+
+ - [Guile GH] Update Guile GH to use the new type system. See
+ Doc/Manual/Guile.html for how smobs are now used.
+
+01/11/2005: wsfulton
+ [C#] New typemap called 'csconstruct'. The code in this typemaps was previously hard
+ coded and could not be customised by a user. This typemap contains the code that is
+ generated into a proxy class's constructor.
+
+ [Java] New typemap called 'javaconstruct'. The code in this typemaps was previously hard
+ coded and could not be customised by a user. This typemap contains the code that is
+ generated into a proxy class's constructor. Another typemap named 'javaconstruct_director'
+ is used instead when the proxy class is a director class.
+
+ [C#, Java] If a C++ class did not have a default constructor, a protected default constructor
+ was automatically generated by SWIG. This seems is unnecessary and has been removed
+ and thereby giving the user almost complete control over the generated code along with the
+ new typemaps above.
+
+19/12/2004: mmatus
+ [Disabled, see entry 02/15/2004]
+ - Fix typemap search, now the "out" typemap search is done as follows
+
+ int *Foo::foo(int bar) -> int *Foo::foo(int bar)
+ -> int *Foo::foo
+ -> int *foo(int bar)
+ -> int *foo
+ -> int *
+
+ then, now you can be more specific, and define
+
+ /* apply only for 'Foo::foo' method */
+ %typemap(out) int * Foo::foo(int *bar) ...;
+
+ /* apply for all 'foo' functions/methods */
+ %typemap(out) int * foo(int *bar) ...;
+
+ %inline {
+ struct Foo {
+ int *foo(int *bar);
+ };
+ }
+
+
+15/12/2004: mmatus
+ - More fixes for templates and template default args.
+ See template_default.i for scary cases that now are
+ supported, besides the already ugly STL/std cases.
+
+ - Cosmetics and more use of 'const' where it was implicit.
+ - Other fixes for OSS, which is now working again with 1.3.25.
+
+Version 1.3.24 (December 14, 2004)
+==================================
+
+12/12/2004: wuzzeb (John Lenz)
+ [Chicken] Fix a bunch of bugs relating to -proxy support
+ + non-class variables now export properly using -proxy
+ + static member functions now export properly using -proxy
+ + member class variables now export properly using -proxy
+ + added a -nounit argument, which does not export the (declare (unit ...))
+ + correctly install swigclosprefix.scm
+ + constants (enums, defines) now correcly export when using -proxy
+
+12/11/2004: wsfulton
+ configure fix for when more than one version of jni_md.h is found
+ in the Java include directory (was generating lots of sed error
+ messages).
+
+12/08/2004: wsfulton
+ [Java] Fixes to arrays_java.i so that one can apply the array
+ typemaps to functions taking pointers as input, eg
+
+ %include "arrays_java.i"
+ %apply int[] {int*};
+ void foo(int *a);
+
+12/05/2004: wsfulton
+ [Java] Director mods contributed by Scott Michel. New typemaps
+ directordisconnect and directordisconnect_derived for the
+ swigDirectorDisconnect() method. Also fix to get the javapackage
+ typemap working again.
+
+12/05/2004: mmatus
+ - Finishing the fixes for templates + default template
+ args + specializations.
+
+ - [Python] Now we use the new templates + default template
+ args in the std/STL library. That means the internal
+ swig files are getting uglier since we now support the
+ original declarations:
+
+ template<class _Tp, class _Alloc = std::allocator< _Tp > >
+ class vector {
+ ....
+ };
+
+ template<class _Key, class _Tp, class _Compare = std::less<_Key >,
+ class _Alloc = std::allocator<std::pair<const _Key, _Tp > > >
+ class map {
+ ....
+ };
+
+ and the user can use the %template directive as
+
+ %template() std::vector<int>;
+ %template() std::vector<int, std::allocator<int> >;
+ %template() std::vector<int, MyAllocator<int> >;
+
+ Now we are closer to the cleaning/rewriting of the
+ python std/STL support, such that we recover support for
+ MSVC++ 6.0, and we can add support for other languages
+ too.
+
+
+12/02/2004: wsfulton
+ [Java] Fix for directors when wrapping methods using a member enum
+ and typesafe/proper enums enabled.
+
+12/01/2004: mmatus
+ - Fix typemaps to work with templates and default template
+ args, ie
+
+ template <class A, class B = int>
+ struct Foo {
+ };
+
+ %typemap(in) Foo<int> *{...}
+ %typemap(out) Foo<int,int> *{...}
+
+ Foo<int> * foo( Foo<int> *f1, Foo<int,int> *f2);
+
+ now 'f1', 'f2' and the return value resolve the provided
+ typemaps properly.
+
+ This is highly needed for proper STL support, see new
+ std_basic_string.i, std_sstream.i, etc.
+
+ - Added std_sstream.i, and fix std_basic_string.i to use
+ the new typemaps + template def. arg mechanism. Also,
+ added the needed std_alloc.i. Now, all the containers
+ can be modified to support std::allocator, like in:
+
+ template<class T, class A = std::allocator<T > >
+ class vector {
+ public:
+ ....
+ };
+
+ This change is only completed by now for basic_string.
+
+ - Fix for smart pointers + members + extensions:
+
+ %extend Foo {
+ int extension(int i, int j) { return i; }
+ int extension() { return 1; }
+ }
+
+ %inline %{
+
+ class Foo {
+ public:
+ int y;
+ static const int z;
+ };
+
+ class Bar {
+ Foo *f;
+ public:
+ Bar(Foo *f) : f(f) { }
+ Foo *operator->() {
+ return f;
+ }
+ };
+
+ now you can
+
+ f = Foo()
+ f.y = 3
+ a = f.z
+ f->extension()
+
+ b = Bar(f)
+ b.y = 3
+ a = b.z
+ b->extension()
+
+ - Other small errors fixes, mostly python.
+
+11/25/2004: wsfulton
+ [Java] Numerous director bug fixes so that the correct java types
+ and canonicalized types in the JNI code are emitted. Use of the
+ $javaclassname special variables in the director typemaps now
+ consistent with the non-director typemaps. The types used for
+ typemap lookups are also corrected in a few places. If you
+ previously had your own director typemaps, ensure they are using the
+ correct C++ type.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA DIRECTORS ***
+
+11/25/2004: wsfulton
+ const enum SWIGTYPE & typemaps added. These wrap const enum references
+ as if they were passed by value. Const enum references thus work the
+ same as const reference primitive types such as const double &,
+ const int & etc. Typemaps added for Java, C#, Ruby, Tcl, Perl and Pike.
+
+11/25/2004: wsfulton
+ [Java, C#] New special variable: $*javaclassname, similar to $javaclassname
+ and $&javaclassname. The new one removes a pointer from the C type before
+ obtaining the Java class name. One or more of $javaclassname,
+ $&javaclassname or $*javaclassname may now appear in a typemap. Likewise for
+ C# using csclassname instead of javaclassname.
+
+11/25/2004: wsfulton
+ The last vestiges of enums being handled as integers removed from the
+ internals. The wrapper methods use the enum type rather than an int
+ now. The net result is added type safety for enums when handled as
+ pointers, references etc. Previously in situations such as a method
+ taking a pointer to an enum, a pointer to an int or a pointer to an
+ enum of some other type could inadvertantly be passed to the method.
+ This is now fixed as the descriptor for an enum is no longer based on
+ an int, but the enum type instead. Anonymous enums are still handled
+ as integers.
+
+ The consequence for scripting language users in correct usage of enums
+ should not be noticeable. There is no change for any of the languages
+ where enums are passed by value - most of the scripting languages will
+ still accept an integer for an enum value and the strongly typed
+ languages still use either typesafe enums, integers or proper enums
+ depending on what the user configures. For Java and C# users a change
+ in the typewrapper class name has occurred (for enum pointers,
+ references etc). For example:
+
+ enum Numbers { one=1, two };
+ enum Numbers* number();
+
+ In Java and C# this must now be coded as
+
+ SWIGTYPE_p_Numbers n = modulename.number();
+
+ rather than
+
+ SWIGTYPE_p_int n = modulename.number();
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/21/2004: wsfulton/mmatus
+ Added missing deprecated warning for %name and remove remaining %name
+ usage in the SWIG libraries.
+
+11/21/04: mmatus
+ - [Python] Adding the PySwigObject to be used for carrying
+ the instance C/C++ pointers. This is used instead of
+ string and PyCObjects.
+
+ The new PySwigObject is even safer than PyCObject, and
+ more friendly than plain strings:
+
+ now you can do
+
+ print a.this
+ <Swig Object at _00691608_p_A>
+
+ print str(a.this)
+ _00691608_p_A
+
+ print long(a.this)
+ 135686400
+
+ print "%s 0x%x" % (a.this, a.this)
+ _00691608_p_A 0x8166900
+
+ the last one is very useful when debugging the C/C++
+ side, since is the pointer value you will usually get
+ from the debugger.
+
+ Also, if you have some old code that uses the string
+ representation "_00691608_p_A", you can use it now again
+ using 'str(ptr)', or by calling 'str = PyObject_Str(obj)'
+ in the C/C++ side.
+
+ This change is mainly for nostalgic swig users that miss
+ the string representation, but also allows to say again
+
+ if a.this == b.this:
+ return "a is b"
+
+ and well, since the change were really simple, maybe in
+ the future we will be able to do
+
+ next = a.this + 1
+
+ or add native python iteration over native C/C++ arrays,
+ ie, no need to create/copy new tuples when returning and
+ array or vector.
+
+ Also, a PySwigPacked object was adding to carry a member
+ method pointer, but this is probably a temporal solution
+ until a more general object for methods is added.
+
+ Be aware that to simplify maintaining and compatibility
+ with other tools, the old string and PyCObjects
+ representation could disappear very soon, and the
+ SWIG_COBJECTS_TYPES or SWIG_NO_OBJECT_TYPES macros will
+ have no effect at compilation time. Still, the three
+ mechanisms are present in the code just for testing,
+ debugging and comparison purposes.
+
+11/21/04: mmatus
+
+ - [Python] Adding back support for using the swig runtime code
+ inside the user code. We just allow the user to include
+ the minimal code needed to implement the runtime
+ mechanism statically, just as in done in the swig
+ modules.
+
+ To use the swig runtime code, for example with python,
+ the user needs include the following:
+
+ #include <Python.h> // or using your favorite language
+ #include <swigrun.swg>
+ #include <python/pyrun.swg> // or using your favorite language
+ #include <runtime.swg>
+
+ the files swigrun.swg, pyrun.swg and runtime.swg can
+ be checked out by using swig -co, or they can simply
+ be found by adding the swig lib directory to the
+ compiler include directory list, for example
+
+ SWIGLIB=`swig -swiglib`
+ c++ -I${SWIGLIB} ..
+
+ of better, using the CPPFLAGS, but that depends on your
+ environment.
+
+ This change can be ported to the other languages too,
+ you just need to isolate the needed runtime code in
+ a single file like 'pyrun.swg', and provide the
+ SWIG_Runtime_GetTypeList() method. Look at the
+ Lib/python/pyrun.swg file and the Examples/python/swigrun
+ example.
+
+11/15/04: mmatus
+ - Fix mixed_types.i + gcc-3.4, ie, arrays + references +
+ typedefs
+
+ - Fix multidim arrays + typedefs,ie
+
+ typedef char character[1];
+ typedef character word[64];
+
+ - Process protected/private bases in the same way before
+ we process protected/private members, ie, we check
+ for constructors, operator new, virtual members, etc.
+
+ - Fix Ruby/Java to work (or ignore) multi-inheritance +
+ directors. Allow other languages to define if it is
+ supported or not.
+
+ - Now you can run
+
+ SWIG_FEATURES="-directors -dirprot"
+ make check-ruby-test-suite
+ make check-python-test-suite
+ make check-java-test-suite
+ make check-ocaml-test-suite
+
+ and you will get only 'real' errors. ruby and python
+ compile with no errors, java shows some problems.
+
+Version 1.3.23 (November 11, 2004)
+==================================
+
+11/05/2004: wsfulton
+ Patch #982753 from Fabrice Salvaire: Adds dependencies generation for
+ constructing makefiles. New command line options -MF -MD -MMD to work
+ with the current options -M and -MM. These options are named the same
+ and work the same as in gcc.
+
+11/05/2004: wsfulton
+ %ignore/%rename changes for methods with default arguments to mirror
+ %feature behaviour. See previous entry.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/04/2004: wsfulton
+ %feature improvements for fine tuning when wrapping methods with
+ default arguments. Any %feature targeting a method with default arguments
+ will apply to all the extra overloaded methods that SWIG generates if the
+ default arguments are specified in the feature. If the default arguments are
+ not specified in the feature, then the feature will match that exact
+ wrapper method only and not the extra overloaded methods that SWIG generates.
+ For example:
+
+ %feature("except") hello(int i=0, double d=0.0);
+ void hello(int i=0, double d=0.0);
+
+ will apply the feature to all three wrapper methods, that is:
+
+ void hello(int i, double d);
+ void hello(int i);
+ void hello();
+
+ If the default arguments are not specified in the feature:
+
+ %feature("except") hello(int i, double d);
+ void hello(int i=0, double d=0.0);
+
+ then the feature will only apply to this wrapper method:
+
+ void hello(int i, double d);
+
+ and not these wrapper methods:
+
+ void hello(int i);
+ void hello();
+
+ This has been introduced to make %feature more powerful to ease the migration
+ to new default arguments wrapping approach.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ If you previously had a %feature and didn't specify the default arguments,
+ you will have to add them in now or you can obtain the original behaviour
+ by using %feature("compactdefaultargs").
+
+11/04/2004: wsfulton
+ [C#] Typemaps for std::vector added into std_vector.i. The proxy classes
+ generated are modelled on the .NET ArrayList class. This isn't quite
+ ready for general consumption yet, but will work with vectors of primitive
+ types and some classes.
+
+10/3/2004: wuzzeb (John Lenz)
+ [GUILE] The -scm interface is now the default. The old GH interface can
+ still be enabled by passing -gh to SWIG.
+
+10/2/2004: mmatus
+
+ - More fixes for namespace + class declarations.
+ As an extra bonus, we get %template support for static/members class
+ methods, ie, now you can say:
+
+ namespace space {
+ struct A
+ {
+ template <class Y>
+ static void fooT(Y y) { }
+ };
+ }
+
+ struct B
+ {
+ template <class Y>
+ void barT(Y y) {}
+ };
+
+ %template(foo) space::A::fooT<double>;
+ %template(foo) space::A::fooT<int>;
+ %template(foo) space::A::fooT<char>;
+
+ %template(bar) B::barT<double>;
+ %template(bar) B::barT<int>;
+ %template(bar) B::barT<char>;
+
+ and call
+
+ A.foo(1)
+ b = B()
+ b.bar(1)
+
+ note the methods are emitted inside the classes,
+ and hence, the %template name refers to the 'member'
+ method name, not a global namespace name.
+
+10/31/2004: mmatus
+ - Solve namespace + class declarations, as in
+
+ namespace foo {
+ struct Bar;
+ struct Foo {
+ };
+ }
+
+ struct foo::Bar : Foo {
+ };
+
+ see namespace_class.i for more examples.
+
+ - Fix %template directive to properly use namespaces,
+ including the case:
+
+ namespace one
+ {
+ template <typename T>
+ struct Ptr {};
+ }
+
+ namespace one
+ {
+ struct Obj1 {};
+ typedef Ptr<Obj1> Obj1_ptr;
+ %template(Obj1_ptr) Ptr<Obj1>;
+ }
+
+ namespace two
+ {
+ struct Obj2 {};
+ typedef one::Ptr<Obj2> Obj2_ptr;
+ %template(Obj2_ptr) one::Ptr<Obj2>;
+ }
+
+ this is done by using the namespace name 'one' to create
+ a namespace node to emit the template instantiation,
+ just as before, but the template parameters are resolved
+ and qualified in the current namespace ('one' or 'two').
+ This is same way that typedef works.
+
+ This resolve the smart_pointer_namespace2.i case, and at
+ the same time, several other ones where before swig was
+ generating the
+
+ "Can't instantiate template 'xx' inside namespace 'yy'"
+
+ error message. In fact, that error doesn't exist
+ anymore. You can only get an error if you use a bad
+ namespace name or so.
+
+10/30/2004: mmatus
+ - [ruby] Directors fixes:
+ - enums and std::strings are working now (several
+ reports in bug track system)
+ - added patch 1025861 for director + exceptions
+
+ *** Attention ***: ruby with directors + protected
+ members work with version 1.7+. Older versions seems to
+ have a broken signature for'rb_protect'.
+
+ If you need to use an old version, look at
+
+ http://excruby.sourceforge.net/docs/html/ruby__hacks_8hpp-source.html
+ for workarounds.
+
+ - [ruby] Fix memory allocation problem in typemap (bug 1037259)
+
+ - [tcl] Fix (enums|constants) + namespace option
+ (reported by jason.m.surprise@intel.com).
+
+ - [perl] Add patch 962168 for multiple inheretance
+
+ - Fix 'defined' as variable name.
+
+10/29/2004: wsfulton
+ Seg fault fix for global scope operator used for friend methods:
+
+ class B {
+ friend void ::globalscope();
+ ...
+ };
+
+10/28/2004:mmatus
+ - Added module and swig option "templatereduce" to force swig
+ to reduce any type needed with templates, ie, in these cases
+
+ %module("templatereduce") test
+
+ template <class T> struct A { };
+
+ typedef int Int;
+ %template(A_Int) A<Int> ==> %template(A_Int) A<int>
+
+ typedef B* Bp;
+ %template(A_Bp) A<Bp> ==> %template(A_Bp) A<B*>
+
+ swig reduces the types Int and Bp to their primitives
+ int and B*. This is closer to the usual compiler
+ resolution mechanism, and it is really needed sometimes
+ when you mix templates + typedefs + specializations.
+
+ Don't use it if you don't have any problem already,
+ since the type reduction can interfere with some
+ user typemaps, specially if you defined something like
+
+ typedef int Int;
+ %typemap(in) Int ...;
+
+ in this case, when you use the "templatereduce" option,
+ swig will ignore the user typemap, since the "typedef int Int"
+ will take precedence, and the usual "int" typemap will be
+ applied.
+
+ Note that the previous case is not common, and should be
+ avoided, ie, is not recommended to use a typedef and a
+ typemap at the same time, specially if you are going to
+ use templates + specializations.
+
+ - Directors:
+
+ virtual destructor is always emitted now, this doesn't
+ cause any harm, and could solve some nasty and
+ mysterious errors, like the one mentioned by Scott.
+
+ also the destructor is not in-lined, so, that can solve
+ some other mysterious errors when mixing directors +
+ imports + embedded applications + some specific compilers.
+
+10/27/2004: wsfulton
+ [C#] typemaps.i library file with INPUT, OUTPUT and INOUT typemaps added.
+
+10/27/2004: wsfulton
+ [Java] std::wstring typemap fixes in std_string.i from Russell Keith-Magee.
+
+10/25/2004: mmatus
+
+ - Using + namespace is working now (using_namespace.i).
+
+ - Derived + nested classes is working now
+ (deriver_nested.i), but of course, we are still waiting
+ for the nested class support.
+
+ - Directors:
+ - unnamed parameters support,
+
+ - protected constructor support (automatic and with
+ dirprot mode),
+
+ - detection of really needed protected declarations
+ (members and constructors) now is done automatically.
+ Even if you don't use the 'dirprot' mode, swig will
+ wrap what is minimally needed (and protected) for the
+ code to compile.
+
+ what is public, as usual, is always wrapped, and if
+ you use the 'dirport'
+
+
+ - Final fixes for the OSS to compile with SWIG 1.3.23 (my
+ very very ugly C++ + templates + everything mounters wrap).
+
+10/25/2004: wsfulton
+ [C#] New commandline option -dllimport. This enables one to specify
+ the name of the DLL for the DllImport attribute. Normally this name
+ comes from the module name, so now it is possible to override this:
+
+ swig -csharp -dllimport xyz example.i
+
+ will generate for all the wrapped PInvoke methods:
+
+ [DllImport("xyz", EntryPoint="...")]
+ public static extern ...
+
+ The wrappers from many different SWIG invocations can thus be compiled
+ into one DLL.
+
+ A new special variable $dllimport can also be used in typemaps, pragmas,
+ features etc. This will get translated into the value specified by -dllimport
+ if specified, otherwise the module name.
+
+10/22/2004: wsfulton
+ [Java] Patch #1049496 from Scott Michel fixes directors methods with
+ enums when wrapped with typesafe or proper Java enums.
+
+10/21/2004: wsfulton
+ Fixes for default arguments in director constructors (Python, Ruby, Ocaml).
+
+10/21/2004: mmatus
+ - [Python] Add the '-cpluscast' option to enable the 'new'
+ C++ casting operators, such as 'static_cast', inside the
+ typemaps. By default swig use the old C cast style, even
+ when parsing C++.
+
+ - [Python] Add the '-new_vwm' option to enable the new
+ SwigValueWrapper mode. Now this is mainly for testing
+ that the typemaps are really safe for any future
+ solution, but you can use it if you have a very strange
+ error with default cosntructors missing + %apply +
+ %typemap, and if everything else fails (see
+ valuwrapper_opaque.i for alternative and current
+ solutions). If you are a user that don't know what is
+ SwigValueWrapper, don't even try it.
+
+ - [Python] Add the '-noh' option to be used with directors
+ and when you prefer to disable the generation of the
+ director header file. If not used, swig will work as
+ usual generating both the wrap.cxx and wrap.h files. If
+ you use it, swig will only generate wrap.cxx.
+
+10/21/2004: wuzzeb (John Lenz)
+ - If you define SWIG_TYPE_TABLE when compiling a wrapper file,
+ the runtime types will be stored in the given type table name.
+ Using this, you can seperate different modules to share their
+ own type systems. -DSWIG_TYPE_TABLE=Mytable
+
+ - [Python] If you define SWIG_STATIC_RUNTIME then the type information
+ will be static to this wrapper. Nothing will be shared with any
+ other modules
+
+ - [Python] If you define SWIG_LINK_RUNTIME, then instead of using
+ the new way of sharing type information, the wrapper will expect
+ to be linked against the Lib/linkruntime.c file. Any modules compiled
+ with SWIG_LINK_RUNTIME and linked against linkruntime.c will all
+ share type information.
+
+10/20/2004: mmatus
+ - [Python] Initial fix for python/import example. Please
+ update the Makefile (autoconf, configure, etc, expert),
+ since now probably is only working with g++, icc and a
+ few other compilers that have the -shared option.
+
+ We need to create additional shared libraries for the
+ virtual destructors. Old and usually forgotten C++
+ requirement.
+
+ Same fix need to be used in perl, I think.
+
+ - [Python] Fix generation of header file for directors,
+ now directors.swg is also included, so, it can be really
+ used from C++, and it solves some problem with compiler
+ that require that, even with the simple swig inclusion.
+
+ - [Python] Reordering the methods and moving some bodies
+ outside the class declaration. This is needed due to
+ some gcc-2.96 internal compiler errors. It seems the
+ PYTHON class is getting too large to been declared and
+ defined at the same time.
+
+ - Add the -oh option to change the output header file name
+ if needed:
+
+ swig -c++ -python test.i -o test.CC -oh test.HH
+
+ this is mainly needed when using directors, and if the
+ current default header file name is not good for you,
+ which is generated as follow:
+
+ swig -c++ -python test.i => test_wrap.h
+ swig -c++ -python test.i -o test.CC => test.h
+
+
+10/20/2004: wsfulton
+ 1) Compact default arguments feature added. This feature allows one
+ to use the default argument code generation that was used in
+ SWIG-1.3.22 and earlier versions. It produces more compact wrappers
+ as only one wrapper method is generated for any method with default
+ arguments. So the advantage is it generates less code but has the
+ original limitations, like it it does not work with all default arguments
+ and default arguments cannot be taken advantage of in the strongly typed
+ languages (C# and Java). It is implemented via the usual %feature mechanism:
+
+ %feature("compactdefaultargs");
+
+ 2) Keyword arguments (kwargs) are working again for default arguments
+ in the languages that support it, ie, Python and Ruby. The new default
+ argument wrapping approach using overloaded methods cannot support kwargs
+ so the compact default argument feature is automatically turned on when
+ kwargs are specified, by %feature("kwargs").
+
+ 3) Compact default arguments are also automatically turned on when wrapping
+ C (not C++) code. This is to support the bizarre notion of default arguments
+ for C code.
+
+10/20/2004: wsfulton
+ Overloaded templated functions in namespaces also working now.
+ Templated functions with default arguments in namespaces too.
+
+10/19/2004: mmatus
+
+ - Allow to disable the new SwigValueWrapper mechanism,
+ if you add the following line in your language main.
+
+ /* Turn on safe value wrapper use mode */
+ Swig_value_wrapper_mode(1);
+
+
+ Now is only active in python. All the other languages
+ are using the old resolution, but they can also use the
+ "valuewrapper"/"novaluewrapper" features to fix some
+ of the old broken cases. Note, however, that not all
+ the broken cases can be solved in that way.
+
+ The new mechanism seems to be working fine in perl, ruby
+ and tcl, but failing in some typemaps in java.
+
+ Hence, is upto the language maintainer to test it, and
+ decide to enable it or not.
+
+ Look at the valuewrapper_opaque.i for examples.
+
+ - Fix more SwigValueWrapper cases when the new mechanism
+ is active. Now it also check for local typemap
+ variables, see valuewrapper_opaque.i for an example when
+ this is needed. But again, this extra checking will only
+ be activated when using the new value wrapper mode.
+
+ - [Python] Fix variable wrapping of classes with private
+ assign operators. It should be easy to fix in all the
+ other modules, instead of checking
+
+ if (!Getattr(n,"immutable")) ...
+
+ you need to verify
+
+ if (is_assignable(n)) ...
+
+ Look at the private_assign.i for an example.
+
+10/18/2004: mmatus
+ - %features "director"/"nodirector" now work as expected.
+ - General fixes in %feature to resolve function decl
+ properly,
+
+ %feature("hello") foo();
+ char foo() -> f() // was working
+ char *foo() -> f().p // it wasn't
+
+
+ - Template + specialization + default template args now is
+ working, (don't confuse with template + default arg
+ values, that was solved before), now this ugly case is
+ working:
+
+ template <class T, class A = Alloc<T> >
+ struct Vector
+ {
+ Vector(T a){}
+ };
+
+ template <>
+ struct Vector<double>
+ {
+ Vector(){}
+ int foo() { return 0; }
+ };
+
+ %template(V_c) Vector<char, Alloc<char> >;
+ %template(V_i) Vector<int>; // picks Vector<int,Alloc<int> >
+ %template(V_d) Vector<double>; // picks the specialization
+
+ this is needed for automatic STL support (later will
+ be).
+
+ - Fix the template + typedef errors in test-suite, which
+ probably will fix another group of strange template +
+ namespaces + typedefs errors.
+
+ - %warnfilter is working better now, parser.y tries to use
+ them when needed.
+
+ - **** New default type resolution method (stype.c) *****
+
+ It preserves the original mixed types, then it goes
+ 'backward' first deleting the qualifier, then the inner
+ types, for example:
+
+ typedef A *Aptr;
+ const Aptr&;
+ r.q(const).Aptr -> r.q(const).p.SWIGTYPE
+ r.q(const).p.SWIGTYPE -> r.p.SWIGTYPE
+ r.p.SWIGTYPE -> r.SWIGTYPE
+ r.SWIGTYPE -> SWIGTYPE
+
+ enum Hello {};
+ const Hello& hi;
+ r.q(const).Hello -> r.q(const).enum SWIGTYPE
+ r.q(const).enum SWIGTYPE -> r.enum SWIGTYPE
+ r.enum SWIGTYPE -> r.SWIGTYPE
+ r.SWIGTYPE -> SWIGTYPE
+
+ int a[2][4];
+ a(2).a(4).int -> a(ANY).a(ANY).SWIGTYPE
+ a(ANY).a(ANY).SWIGTYPE -> a(ANY).a().SWIGTYPE
+ a(ANY).a().SWIGTYPE -> a(ANY).p.SWIGTYPE
+ a(ANY).p.SWIGTYPE -> a(ANY).SWIGTYPE
+ a(ANY).SWIGTYPE -> a().SWIGTYPE
+ a().SWIGTYPE -> p.SWIGTYPE
+ p.SWIGTYPE -> SWIGTYPE
+
+ before it always stops after finding ref/pointer/enum/array/etc.
+
+ Now, then, you can define (use and apply) 'higher' typemaps such as:
+
+ %typemap(in) SWIGTYPE* const&
+ %typemap(out) char FIXSIZE[ANY]
+ %typemap(in) SWIGTYPE* const&
+ %typemap(in) const enum SWIGTYPE&
+ %typemap(in) SWIGTYPE[ANY][ANY]
+ %typemap(in) const char (&)[ANY]
+
+ It is possible with this change that previous typemaps
+ that were defined (but ignored), now will start to work.
+
+ Also, it is necessary check for the '%typemap(varin) SWIGTYPE[]',
+ before it was usually not defined (but char[] was),
+ and that can produce some inconsistencies.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ This change was needed for STL, since std::vector<enum Hello>
+ std::vector<A*>, etc, will always generate methods that
+ mix const references with the vector type.
+
+ Now that is working, all the std::container<T*>
+ specialization will not be needed anymore, well, in
+ theory.
+
+ In the practice, everythin is working as before until
+ the proper mixed types are defined and the libraries
+ simplified to use them.
+
+ - Change the behavior of extern "java"/"fortran"/"etc",
+ now swig produces a warning, and use extern "C" instead.
+ The warning can also be disable with the "-w 313" flag.
+ (WARN_PARSE_UNDEFINED_EXTERN).
+
+ - SwigValueWrapper is now more selective (lang.cxx).
+
+ [Perl/Tcl]
+ - Fix some typemaps (perl/tcl) to work properly with
+ SwigValueWrapper. This was not a problem with
+ SwigValueWrapper, but with the typemaps that now are
+ safe to use with %apply.
+
+ [Python]
+
+ - Fix %callback/%pythoncallback work now as before after
+ the def args changes. Also, %callback now is an alias
+ for %pythoncallback, so, they do the same.
+
+ [Python/Ruby]
+ - %callback is more usable and uniform:
+
+ %callback("%s_cb") foo(); // for both, python/ruby
+ %callback("%s_cb"); // for both, python/ruby
+ %callback(1) foo(); // only in python.
+
+10/17/2004: arty
+ [OCAML]
+ - Tweak to enum typing for soundness in the presence of multiple
+ modules.
+ - global functions are now unambiguous in multiple loaded modules.
+ - Fixed test case code to build multimodule test cases correctly.
+ - There is no way to share overload resolution across modules
+ because of soundness issues. If the user wants to call some
+ function foo from an arbitrary module bar, they will have to
+ use Bar._foo to call it correctly. Later I will fix the
+ camlp4 module to do something clever in this case.
+ - Promided performance overhaul of class mechanism.
+ - Removed symbol hack for ocaml-3.07 and below which is not needed
+ for ocaml-3.08 and above.
+
+10/16/2004: wuzzeb (John Lenz)
+ [CHICKEN]
+ - Completly change how chicken.cxx handles CLOS and generic code.
+ chicken no longer exports -clos.scm and -generic.scm. The clos
+ code is exported directly into the module.scm file if -proxy is passed.
+ - The code now always exports a unit. Running the test-suite is now
+ majorly broken, and needs to be fixed.
+ - CLOS now generates virtual slots for member variables similar to how
+ GOOPS support works in the guile module.
+ - chicken no longer prefixes symbols by the module name, and no longer
+ forces all names to lower case. It now has -useclassprefix and -closprefix
+ similar to how guile handles GOOPS names.
+
+10/16/2004: wsfulton
+ Templated functions with default arguments working with new default argument
+ wrapping approach. The new approach no longer fails with the following default
+ argument pattern (previously failed with some primitive types, like
+ unsigned primitive types):
+
+ template<typename T> int foo(const T& u = T());
+ %template(foo) foo<unsigned int>;
+
+ This relies on the templated function overloading support just added, so all
+ the combinations of overloading by template parameters and normal parameters
+ as well as overloading with default parameters works.
+
+10/16/2004: wsfulton
+ Added support for the large range of templated function overloading that C++
+ supports.
+
+ - Overloaded templated functions, eg
+
+ template<typename T> int overload(T t);
+ template<typename T> int overload(T t, const T &r);
+
+ - Fixes where the templated type is not used in the parameter list, eg
+
+ template<typename T> void xyz();
+ template<> void xyz<double>();
+
+ - Fixes for overloading of plain functions by a templated function:
+
+ void abc(double d);
+ template<typename T> void abc(T t);
+
+ - Overloading by templated parameters fixed:
+
+ template<typename T> void foo(T t) {}
+ template<typename T, typename U> void foo(T t, U u) {}
+
+ %template(foo) foo<double, double>;
+
+ - All combinations of the above also working including specializations, eg:
+
+ void abc(double d);
+ template<typename T> void abc(T t);
+ template<> void abc<double>(double t);
+ template<> void abc(int t);
+
+10/16/2004: wuzzeb (John Lenz)
+ - Remove the ability to share type information by using c linking.
+ All type sharing happens through a global variable in the target language.
+ + Remove SWIG_NOIMPORT, SWIG_RUNTIME, and related defines.
+ + Deprecate -runtime, -noruntime command line options
+ + Update test-suite common.mk to correctly build multicpptest
+ + Remove reference to precommon.swg
+ + Update the guile_gh interface to share data by a global var instead
+ of c linkage.
+
+ - Remove Advanced.html, since everything in it is now obsolete
+
+10/09/2004: mmatus
+ - Split the python std/STL C++ library files, now
+ all the language independent definitions are under
+ the directory
+
+ Lib/std
+
+ and hence, can be used from other languages.
+
+ - Add more documentation to the Python STL, and
+ clean unnecessary code.
+
+ - Add initial C99 complex support, and some fixes
+ for long double.
+
+10/08/2004: mmatus
+ - Fix the SwigValueWrapper for opaque types, now it is
+ applied for opaque templates and classes, for which we
+ don't know if there is or not a default constructor, ie
+
+ struct A {
+ A(int);
+ };
+
+ Still, if you know that you class has a default
+ constructor, and for some very very particular reason
+ you want to avoid the SwigValueWrapper, and you don't
+ want or can't expose the class to swig, now you can
+ say
+
+ %feature("novaluewrapper") A;
+ class A;
+
+ or the other way around, if the class has a default
+ constructor, but you want to use the value wrapper, you
+ can say
+
+ %feature("valuewrapper") A;
+ struct A {
+ A();
+ ....
+ };
+
+ - Fix for char > 128, ie
+
+ const char tilde_a = '\341';
+
+ - Add patch 1041858 for $lextype, which carries the
+ literal type of a symbol. See lextype.i in the
+ test-suite for more details.
+
+
+
+
+10/07/2004: wsfulton
+ {Ruby, Java] Fix director + 'empty' throws
+
+ struct A {
+ A() throw();
+ virtual ~A() throw();
+ int foo() throw();
+ };
+
+
+10/06/2004: wuzzeb (John Lenz)
+ [TCL]
+ - Fix bug reported by William A. Hoffman propagating clientdata
+ between modules. Added clientdata_prop.multicpptest to check for
+ this bug. The fix involved the following changes:
+ + SwigType_clientdata_collect does not need to check
+ types in r_resolved because we only want to propagate clientdata
+ to typedefed classes, and r_mangled already takes care of typedefs.
+
+ + SWIG_TypeRegister now copies the clientdata field correctly
+
+ + Move SWIG_Guile_PropagateClientData function from guile module
+ into common.swg, because we need to call it from both guile and tcl.
+
+ + Add base_names to swig_class to delay the lookup of bases. SWIG
+ now exports the base names and only when the base swig_class is
+ needed is SWIG_TypeQuery(name)->clientdata looked up.
+
+ - conversion_ns_template testsuite test was failing because
+ the name of the wrapped constructor function was not calculated
+ correctly for structs. Fixed.
+
+10/06/2004: wsfulton
+ Fixes for default arguments used in directors - in virtual
+ methods and director constructors.
+
+10/06/2004: mmatus
+ Fix the __cplusplus macro, and bug 1041170.
+ Now it is working as supposed, ie, you can safely use
+
+ #ifdef __cplusplus
+ ...
+
+ all over swig, including inside %defines and %{ %} bodies.
+
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ The old trick of using
+
+ #if __cplusplus
+
+ doesn't work any more. So, if you have your own typemaps
+ using that syntax, you will need to migrate them to use
+ "#ifdef __cplusplus".
+
+10/05/2004: wuzzeb (John Lenz)
+ - Reorganize how runtime type information is stored and shared
+ between modules. For chicken and mzscheme, I removed
+ the ability to use runtime libraries, while perl, tcl, python, and
+ ruby default to using the new method but can go back to the old
+ method by declaring SWIG_ALLOW_RUNTIME.
+
+ - line 582 in mzscheme.cxx was generating a segfault on
+ imports.multicpptest, so I fixed it.
+
+10/05/2004: wsfulton
+ Fixes for %extend and overloaded static methods with default
+ arguments.
+
+10/05/2004: mmatus
+ - [python] Fix director + method with 'empty' throw, ie
+
+ struct A {
+ virtual int foo() throw();
+ };
+
+ other languages should also easy to fix, look for
+ Getattr(n,"throw") in python.cxx.
+
+ - Fix director + destructor with 'empty' throw
+
+ struct A {
+ virtual ~A() throw();
+ };
+
+ - Now SWIG_FEATURES parse all and the same options you
+ can pass to swig in the command line.
+
+ - New command line flag: -features <list>, as in
+
+ swig -features autodoc=3,director
+
+ ie, any global feature can be initialized from the
+ command line. This is mainly for testing, but users
+ can also take advantage of it.
+
+10/04/2004: mmatus
+ - Properly qualify type in syntax as 'long(2)' or 'Foo()',
+ this solve old problem with default args, and probably
+ other problems around. However, the default arg problem
+ was also already solved by William (see below).
+
+ - Fix feature_set and feature_get methods. Before
+ they look from particular to general and keep the first
+ feature found. This didn't work well with templates.
+ Now the methods look from general to particular, and
+ override any found feature.
+
+ - Previously a feature could not be applied to constructors
+ or destructors that weren't explicitly declared in the class.
+ This is now fixed, for example:
+
+ %feature("featurename") Foo() "..."
+ %feature("featurename") ~Foo() "..."
+ class Foo {
+ // implicit Foo() and ~Foo()
+ };
+
+ - Fix missing features for default const/dest, by really
+ 'creating' the methods and applying the features.
+
+ - Fix return_const_value.i case by adding SwigValueWrapper<const T>
+ specialization.
+
+ - Fix %extend + overload, including overloading actual
+ class methods.
+
+ - Adding more cases in related files in the test-suite.
+
+10/04/2004: wsfulton
+ Changes to the way default arguments are wrapped. Previously a single
+ method was generated for each method that had default arguments. If
+ a method had 5 arguments, say, of which 1 had a default argument
+ then the call to the wrapped method would pass 5 arguments. The default
+ value was copied into the wrapper method and used if the scripting
+ language passed just 4 arguments. However, this was flawed as the
+ default argument sometimes does not have global access, for example
+ SWIG would generate code that couldn't compile when wrapping:
+
+ class Tricky {
+ public:
+ void foo(int val = privatevalue);
+ void bar(int val = Tricky::getDefault());
+ private:
+ static int getDefault();
+ enum { privatevalue = 200 };
+ };
+
+ Also bugs in resolving symbols generated code that wouldn't compile, for example
+ (probably fixable though):
+
+ namespace Space {
+ class Klass {
+ };
+ Klass constructorcall(const Klass& k = Klass());
+ }
+
+ The approach also does not work for statically typed languages (C# and Java)
+ as these languages do not allow methods to have variable number of arguments.
+ Although C# has a mechanism to pass a variable number of arguments they
+ must be of the same type and are more like varargs.
+
+ The new approach solves the above problems and wraps methods with default
+ arguments as if the method was overloaded. So SWIG will now treat
+
+ void foo(int val=0);
+
+ as if it had parsed:
+
+ void foo(int);
+ void foo();
+
+ The code generated is then exactly the same as if SWIG had parsed the two
+ overloaded methods. The scripting languages count the arguments passed and call
+ the appropriate method, just like overloaded methods. C# and Java are now able
+ to properly wrap methods with default arguments by generating extra methods,
+ again as if the method was overloaded, so for:
+
+ void bar(string s="hello", double d=10.0, int i=0);
+
+ the following proxy methods are generated:
+
+ void bar(string s, double d, int i);
+ void bar(string s, double d);
+ void bar(string s);
+ void bar();
+
+ The new approach comes with a couple of minor knock on effects.
+
+ 1) SWIG support for default arguments for C (not C++) code no longer works.
+ Previously you could have this interface:
+
+ %{
+ void foo(int val);
+ %}
+ void foo(int val=0);
+
+ and call the wrapped method from a scripting language and pass no arguments
+ whereupon the default of 0 was used. You can get the same behaviour for C
+ code by using the "default" typemap:
+
+ %typemap(default) int val "$1 = 0;"
+ %{
+ void foo(int val);
+ %}
+ void foo(int val);
+
+ or you could of course compile your code as C++ if you want C++ features :) :
+
+ %{
+ void foo(int val=0);
+ %}
+ void foo(int val=0);
+
+ A couple of SWIG's libraries used this C extension and these have been modified
+ to use the "default" typemap. The "default" typemap is thus unchanged (and still
+ is not and is not fully supported by C# and Java, and is likely to remain so).
+
+
+ 2) All features (%feature, %rename, %ignore etc) no longer work as if the method
+ with default arguments is just one method. For example, previously
+
+ %ignore foo(int);
+
+ would have ignored the method completely. Now it will only ignore foo(int) but
+ not the extra foo() method. Instead use:
+
+ %ignore foo;
+
+ to ignore them all. or
+
+ %ignore foo(int);
+ %ignore foo();
+
+ This of course allows one to fine tune the wrapping, for example one could use:
+
+ %rename(fooint) foo(int);
+ %rename(foodefaults) foo();
+ void foo(int val=0);
+
+ and call them from any language like so:
+
+ fooint(200)
+ foodefaults()
+
+ or for example ignore the extra overloaded method, so the defaults cannot be used:
+
+ %ignore foo();
+ void foo(int val=0);
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+10/2/2004: mmatus
+ [Python]
+ - More cleaning up and uniformation on the Python Lib
+
+ - Added Robin's docstring patch, plus some fixes, plus
+ some extensions, see autodoc.i example in the test-suite,
+ and try using %feature("autodoc","extended").
+
+ This patch is not a complete solution for the
+ documentation problem, just enough to inform python about
+ the parameter list.
+
+ The expected swig documentation support is far far away yet.
+
+
+10/1/2004: mmatus
+ - Fix the %callback feature (only used in ruby and python examples,
+ by now, but it should be generic), now member callbacks
+ are working again
+
+ - Fix wrapping of functions pointers like
+
+ std::ostream& std::endl(std::ostream&);
+
+ ie, the ones that return references or enums.
+
+ [Python] Add the %pythoncallback directive, which is
+ an improved version of %callback, ie,
+
+ %pythoncallback(1) foo;
+ %pythoncallback(1) A::bar;
+ %pythoncallback(1) A::barm;
+
+ int foo(int a) {
+ return a;
+ }
+
+ struct A
+ {
+ static int bar(int a);
+ int barm(int a);
+
+ };
+
+ int foobar(int a, int (*pf)(int a));
+
+ in python you can use
+
+ foo(2)
+ foobar(2,foo)
+ A.bar(2)
+ foobar(2,A.bar)
+
+ ie, no additional pointer elements are created, and
+ the original 'foo' and 'A.bar' can be used as parameters.
+
+ In the case of member function however, still you need
+ to use the special variable Class::<fnc_name>_cb_ptr, ie:
+
+ foobarm(3, a, A.barm_cb_ptr)
+
+ we will try to fix this situation also, but later.
+
+ [Python] Add more elements from the STL library, now
+ you can use
+
+ import std
+ std.cout << "hello " << 123 << std.endl
+
+ [Python] Fix in/out return mechanism, now swig will behave
+ as 1.3.21 but using a python list when needed. The problem
+ is that the types std::pair,std::vector,etc, use tuples,
+ and they interfer with the previous inout tuple type.
+
+ By using lists we solve the conflicts, swig acts as before,
+ but returns a list when more than one parameter are using
+ the OUT typemap. See the new inout.i example in the
+ test-suite.
+
+ *** POTENTIAL INCOMPATIBILITY FOR PYTHON MODULE ***
+
+ [Python] Much better error messages for bad arguments, now
+ you always get the argument number where the error occurred.
+
+09/27/2004: wsfulton
+ Patch from Bill Clarke -
+ 1) Warning emitted when -importall and -includeall is used together,
+ with -includeall taking precedence.
+ 2) Ensure SWIGIMPORTED is always defined when a file is being
+ imported with %import. Note that this is not the same as SWIGIMPORT,
+ which gets defined in all generated wrapper files.
+
+09/26/2004: mmatus
+
+ - add %feature("exceptionclass") to identify a class used
+ as exception. Before swig identified and marked a class
+ using the "cplus:exceptionclass" attribute. However, the
+ class needed to appear on an throw() statement. Now
+ swig keeps trying to identify the exception classes, as
+ before, but it also allows the user to mark a class by
+ using the %feature explicitly. (mostly relevant for
+ python and chicken)
+
+ [Python]
+
+ - fix -modern option + exceptions, which mix old class
+ style with the new one. So, we always need to emit
+ the "nonmodern" python code.
+
+ - add the "python:nondynamic" feature and its handler
+
+ now if you have
+
+ %pythonnondynamic A;
+
+ struct A {
+ int a;
+ int b;
+ };
+
+ then, in the python side
+
+ aa = A()
+
+ aa.a = 1 # ok
+ aa.b = 2 # ok
+ aa.c = 3 # error, the class can not be extended dynamically.
+
+
+ Since this is a feature, you can use
+
+ %pythonnondynamic;
+
+ or
+
+ %pythondynamic; [ Note: %pythondynamic since deprecated ]
+
+ to force all the wrapped classes to be "nondynamic" ones.
+
+ The default, as in regular python, is that all the wrapped
+ classes are dynamics. So, careful with your spelling.
+
+09/14/2004: mmatus
+ - Support the -I- option.
+
+ - Differentiate between %include <file> and %include "file".
+ This fix several corner cases.
+
+
+ [Python] Several patches:
+
+ - Normalize the Lib file names:
+ *.swg internal files,
+ *.i user files.
+
+ - Fix Char[ANY] typemaps, so they also delete any extra '\0' chars,
+ now they behave as before (1.3.21). Still, you can use
+ the SWIG_PRESERVE_CARRAY_SIZE macro if you need to
+ preserve the original size (see pystrbase.swg).
+
+ - Add the Char FIXSIZE[ANY] typemaps, to preserve the
+ original C array sizes (see above). Though, you can't
+ use them yet since %apply and arrays are not working
+ together.
+
+ - Add pyfragments.swg, now the user can add fragments
+ to override the default ones.
+
+09/10/2004: wsfulton
+ Patch from Bill Clarke which fixes spurious preprocessor bug which
+ shows on Solaris and gcc, eg:
+ Warning(202): Could not evaluate '!defined(SWIGJAVA) &&
+ !(defined(SWIGCSHARP)'
+ Also fixes a bug where '#if "a" == "b" == 1' wouldn't have worked
+
+09/10/2004: wsfulton
+ Restored multiple build directories for the test-suite. Patch from
+ Bill Clarke.
+
+09/06/2004: wsfulton
+ Added the missing runtime.dsp Visual Studio project files for the
+ import examples to work.
+
+
+Version 1.3.22 (September 4, 2004)
+==================================
+
+09/03/2004: wsfulton
+ The swig.m4 macro for use with the Autoconf/Automake/Libtool has
+ been removed and is no longer installed. Please use the new and better
+ maintained version derived from swig.m4 in the Autoconf macro archive.
+ See http://www.gnu.org/software/ac-archive/htmldoc/ac_pkg_swig.html and
+ http://www.gnu.org/software/ac-archive/htmldoc/ac_python_devel.html.
+
+09/01/2004: wsfulton
+ [Perl] Applied patch #1019669 from Christoph Flamm. Adds support
+ for %feature("shadow") in the same way as it works in Python. This
+ enables one to override the generated shadow/proxy methods, including
+ constructors and destructors. For example:
+
+ /* Let's make the constructor of the class Square more verbose */
+
+ %feature("shadow") Square(double w)
+ %{
+ sub new {
+ my $pkg = shift;
+ my $self = examplec::new_Square(@_);
+ print STDERR "Constructed an @{[ref($self)]}\n";
+ bless $self, $pkg if defined($self);
+ }
+ %}
+
+ class Square {
+ public:
+ Square(double w);
+ ...
+ };
+
+08/31/2004: mmatus
+ [Python] Incompatibility reported by Bill Clarke (llib@computer.org):
+
+ If you are using Sun Studio 8 (and possibly earlier
+ versions) to compile the output produced by swig
+ 1.3.22rc1, and you are using C++ and STL templates then
+ you need to use either "-runtime" or "-noruntime". If you
+ use neither of these options then you will probably get
+ compiler errors when trying to compile the wrapper file;
+ the error message will be like this: The name
+ SWIG_Python_ConvertPtr[...] is unusable in static
+ swigpy::traits_asptr[...] If you get this error message,
+ you need to regenerate your wrapper file using 'swig
+ -runtime' or 'swig -noruntime'.
+
+ You shouldn't get this problem with Sun Studio 9.
+
+ *** POTENTIAL INCOMPATIBILITY FOR PYTHON MODULE ***
+
+08/26/2004: wsfulton
+ [Perl] Applied #932333 from Ikegami Tsutomu. Fixes long long *OUTPUT
+ and unsigned long long *OUTPUT typemaps in typemaps.i.
+
+08/26/2004: wsfulton
+ Applied patch #857344 from Art Yerkes. Workaround for autoconf bug when
+ running 'make install'.
+
+08/26/2004: wsfulton
+ [Perl] Part of patch #982753 applied. This implements a %perlcode directive.
+ It allows one to add Perl code to the generated .pm file. Works the same
+ as %pythoncode.
+
+08/26/2004: wsfulton
+ [Java] Fix for directors when wrapping virtual methods with exception
+ specifications that were not simple types. Previously code was generated that
+ didn't compile, for example when the exception specification was a pointer.
+
+08/25/2004: wsfulton
+ [C#] Typemap fix for methods that return char *. The CLR would incorrectly
+ delete the memory pointed to by char *. Also applied the same correction to
+ the char array typemaps.
+
+08/24/2004: wsfulton
+ Fixes for -fmicrosoft error/warning message display:
+ - End of file (EOF) warning messages not displaying in correct format
+ - Some messages containing a file path were displaying a double backslash
+ instead of a single backslash
+
+08/23/2004: wsfulton
+ Applied patch #1011604 submitted by Charles Schwieters. Fix for 64 bit tcl
+ interpreters.
+
+08/23/2004: wsfulton
+ Fix for bug #875583 - enum forward declarations previously gave a syntax error.
+
+08/23/2004: mkoeppe
+ [Allegro CL] Use typemaps "ffitype" and "lisptype" to determine the FFI type
+ specifiers from the C type. This makes it possible, for instance, to control
+ whether a C "char" argument takes a Lisp character or a Lisp integer value.
+ The default (taking Lisp characters) is done by these built-in typemaps:
+ %typemap(ffitype) char ":char"
+ %typemap(lisptype) char "character"
+ If char means an integer instead, use these typemaps:
+ %typemap(ffitype) char ":char"
+ %typemap(lisptype) char "integer"
+
+08/22/2004: wsfulton
+ As discussed in bug #772453, the SWIG library directory is now installed
+ into a different default directory. The library used to be installed to
+ /usr/local/lib/swig1.3. It is now in the more usual architecture independent
+ directory and I have additionally used a version specific subdirectory as
+ the library will rarely work with older versions of SWIG. This release
+ will thus use /usr/local/share/swig/1.3.22 by default, which can be
+ tailored as before using './configure --swiglibdir'.
+
+08/17/2004: mkoeppe
+ [MzScheme] Add support to create native MzScheme structures from C structures.
+ To convert a C structure to an MzScheme structure, use the new runtime macro
+ SWIG_NewStructFromPtr in a typemap. Patch from Dmitriy Zavin.
+
+08/12/2004: wsfulton
+ Patch #837715 from Ben Reser to correctly detect Python lib directory
+ on 64 bit systems.
+
+08/12/2004: wsfulton
+ [C# and Java] Prevent memory leaks in the case of early return
+ from wrapper methods using const std::string & parameters. Modified
+ Mark Traudt patch #951565.
+
+08/12/2004: wsfulton
+ Bug #943783 with patch fixes php char * out typemap NULL values.
+
+08/03/2004: Ahmon Dancy <dancy@dancy>
+
+ [allegrocl] Additional case mode fixes. Also, make sure
+ foreign types are exported.
+
+07/24/2004: mkoeppe
+ [Guile] In -scm mode, SWIG modules now exchange their pointer type
+ information via the Guile interpreter. It is no longer necessary to build a
+ runtime library or to use -noruntime and -runtime etc.
+
+ The module (Swig swigrun) which was introduced in the change of 05/17/2004 is
+ no longer automatically built. If you need it, run SWIG on the interface file
+ swigrun.i.
+
+07/23/2004: wsfulton
+ [C#] Bug #917601 Mapping C++ bool fix from Mark Traudt
+
+07/23/2004: wsfulton
+ RPM fixes for latest CVS version including removal of runtime
+ library.
+
+07/23/2004: wsfulton
+ Patch #908955 from Robert H De Vries.
+ RPM file generation fix for Fedore Core 1 and Redhat AS2.1.
+
+07/12/2004: wsfulton
+ Patch #864689 from Robin Dunn:
+
+ This patch corrects two problems in the XML output of SWIG:
+
+ 1. There were often extra '/>\n' in the output.
+
+ 2. value attributes were output with '\n' in them but
+ since that is not technically legal most (all?) XML
+ parsers will strip them out. Replacing the '\n' with
+ the '&#10;' entity reference solves this as that is
+ legal and XML parsers will convert it to a '\n' when
+ reading the values back in.
+
+ This patch also adds a new global command line option
+ that will allow the parse tree to be written out in XML
+ *after* some other language module has been run, in
+ order to be able to get extra info that the language
+ module puts in the tree. In this way the XML is a
+ post-processed version of the tree rather than a
+ pre-processed version.
+
+ Command line option is -dump_xml or -xmlout <file>
+
+07/12/2004: wsfulton
+ [Java] Patch from Scott Michel to fix typesafe enums and proper enums
+ with directors.
+
+07/12/2004: wsfulton
+ HTML documentation (makechap.py) file generator missing end of line
+ patch #908951 from Robert de Vries.
+
+07/08/2004: wsfulton
+ The deprecated runtime library build has been removed. This also removes
+ the dependency on Libtool. Libtool is no longer required to build SWIG.
+ The associated -ldflags SWIG commandline option has also been removed.
+
+ The examples and test-suite testcases that used the runtime library have
+ been updated to use the replacement approach to using SWIG across
+ multiple modules, that is they use the -noruntime and -runtime commandline
+ options, see Modules.html. Effectively they build their own runtime
+ libraries using -runtime. The examples are import and import_template.
+ The test cases are in the imports and template_typedef_import directories.
+
+ Anyone who wants the original runtime libraries can either run the test-suite
+ or build the examples and use the appropriate shared object/DLL that is
+ generated with the -runtime commandline option. For example libimports_runtime.so
+ (Python calls it lib_imports_runtime.so) is generated after running the
+ 'make imports.multicpptest' testcase in the Examples/test-suite/<lang>
+ directory. Or use libruntime.so / runtime.dll after building the import
+ examples in Examples/<lang>/import.
+
+07/07/2004: mkoeppe
+ [Allegro CL] Convert character and string literals in constants to
+ CL syntax. Fix FF:DEF-FOREIGN-CALL for mixed-case C functions.
+
+06/27/2004: wsfulton
+ [Java] New feature for Java exceptions with format %javaexception(exceptionclasses).
+ This feature is a slight enhancement to %exception and the only difference is the
+ addition of the exception classes which are generated into a throws clause.
+ The 'exceptionclasses' is a comma separated list of classes which will be
+ added to the associated proxy method's throws clause. The 'exceptionclasses'
+ are specified like the exception classes in the 'throws' attribute in the
+ typemaps. This feature should be used for correctly handling checked exceptions
+ thrown from JNI code. For example:
+
+ %javaexception("java.lang.Exception") throwException %{
+ ... convert a std::logic_error into a java.lang.Exception using JNI code ...
+ %}
+
+ #include <stdexcept>
+ void throwException() {
+ throw std::logic_error("Logic error!");
+ }
+
+ will generate a method with a throws clause in the module class:
+
+ public static void throwException() throws java.lang.Exception { ... }
+
+06/27/2004: wsfulton
+ [C#] New %csconstvalue(value) feature directive for use with constants and
+ enums. This works the same way as %javaconstvalue. For C#, this directive
+ is the only way that one can fix wrapping of C/C++ enums with proper C#
+ enums if the enum item's initialiser cannot compile as C# code. This is
+ because Java enums can use a call into C code to initialise the enum item,
+ whereas in C#, the enum value must be a compile time constant. That is,
+ using %csconst(0) cannot be used in C# to initialise the C# enum item via
+ a PINVOKE call.
+
+06/27/2004: wsfulton
+ [Java] New %javaconstvalue(value) feature directive for use with constants and
+ enums. Sometimes the use of %javaconst(1) will produce code that won't compile
+ under Java. If a compile time constant is required, %javaconst(0) is not an
+ option. The %javaconstvalue directive achieves this goal and the value specified
+ is generated as Java code to initialise the constant. For example:
+
+ %javaconst(1);
+ %javaconstvalue(1000) BIG;
+ %javaconstvalue("new java.math.BigInteger(\"2000\")") LARGE;
+ %javaconstvalue(10) bar;
+ %{
+ const int bar = 10;
+ %}
+ %inline %{
+ #define BIG 1000LL
+ #define LARGE 2000ULL
+ enum Foo { BAR = ::bar };
+ %}
+
+ Generates:
+
+ public interface exampleConstants {
+ public final static long BIG = 1000;
+ public final static java.math.BigInteger LARGE = new java.math.BigInteger("2000");
+ }
+ public final class Foo {
+ public final static Foo BAR = new Foo("BAR", 10);
+ ...
+ }
+
+ Previously, none of BIG, LARGE or BAR would have produced compilable code
+ when using %javaconst(1).
+
+06/27/2004: wsfulton
+ %feature enhancements. Features can now take an unlimited number of attributes
+ in addition to the feature name and feature value. The attributes are optional
+ and are much the same as the typemap attributes. For example, the following
+ specifies two optional attributes, attrib1 and attrib2:
+
+ %feature(featurename, attrib1="attribval1", attrib2="attribval2") name "val";
+ %feature(featurename, val, attrib1="attribval1", attrib2="attribval2") name;
+
+06/27/2004: wsfulton
+ %feature improvements for the syntax that takes the feature value within the
+ %feature() brackets. The value specified is no longer restricted to being just
+ a string. It can be a string or a number. For example, this is now acceptable
+ syntax:
+ %feature("featurename",20.0);
+ whereas previously it would have to have been:
+ %feature("featurename","20.0");
+ Useful for features that are implemented as a macro, for example:
+ #define %somefeature(value) %feature("somefeature",value)
+ These will now work accepting either a string or a number:
+ %somefeature("Fred");
+ %somefeature(4);
+
+06/06/2004: wuzzeb (John Lenz)
+ [Chicken, Guile]
+ - Created the Examples/test-suite/schemerunme directory, which holds all the
+ runme scripts for guile and chicken (and possibly mzscheme...). The guile
+ and chicken _runme files then (load "../schemerunme/foo.scm").
+ - In chicken module, fix a few bugs invlolving dynamic casts.
+
+06/03/2004: wsfulton
+ Patch to fix wrapping of templated methods. ISO compliant compilers, like
+ Comeau and GCC-3.4.0, don't like the template specifier that SWIG was generating
+ when calling the method. This fix may break some non standard compliant compilers,
+ for example, Sun workshop compilers prior to version 6.2.p2. Patch submitted
+ by Bill Clarke.
+
+06/03/2004: wsfulton
+ [Java, C#] Undocumented special variable $imclassname removed.
+ New special variable $module is replaced by the module name, as specified
+ by %module or -module commandline option. $imclassname can be created from $module.
+
+06/03/2004: wsfulton
+ [C#] Same as for Java below. The new typemaps are named differently, namely,
+ csbody and csbody_derived. The deprecated typemaps are csgetcptr and
+ csptrconstructormodifiers.
+
+ *** POTENTIAL INCOMPATIBILITY FOR C# MODULE ***
+
+06/03/2004: wsfulton
+ [Java] Typemap changes for the Java proxy / typewrapper and enum classes. A new
+ typemap called javabody contains the essential support code for generation into the body
+ of these classes. There is also a new javabody_derived typemap which is used instead for
+ wrapped classes that have a wrapped base class. The code is basically, the getCPtr()
+ method and swigCPtr and swigCMemOwn member variables. These used to be hard coded
+ with no way to modify the code. The introduction of this typemap makes it possible for
+ the user to tailor nearly every aspect of the code generation.
+ The exception now is the code for director classes.
+
+ The javagetcptr and javaptrconstructormodifiers typemaps are deprecated and are
+ no longer used as the code that these generated can be put in the more flexible
+ javabody and javabody_derived typemaps.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+ The following macros contributed by Scott Michel may help you upgrade if you have used
+ the javagetcptr typemap:
+
+ /* Utility macro for manipulating the Java body code method attributes */
+ %define SWIGJAVA_ATTRIBS(TYPENAME, CTOR_ATTRIB, GETCPTR_ATTRIB)
+ %typemap(javabody) TYPENAME %{
+ private long swigCPtr;
+ protected boolean swigCMemOwn;
+
+ CTOR_ATTRIB $javaclassname(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ GETCPTR_ATTRIB static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+ %}
+
+ %typemap(javabody_derived) TYPENAME %{
+ private long swigCPtr;
+
+ CTOR_ATTRIB $javaclassname(long cPtr, boolean cMemoryOwn) {
+ super($moduleJNI.SWIG$javaclassnameUpcast(cPtr), cMemoryOwn);
+ swigCPtr = cPtr;
+ }
+
+ GETCPTR_ATTRIB static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+ %}
+ %enddef
+
+ /* The default is protected getCPtr, protected constructor */
+ SWIGJAVA_ATTRIBS(SWIGTYPE, protected, protected)
+
+ /* Public getCPtr method, protected constructor */
+ %define PUBLIC_GETCPTR(TYPENAME)
+ SWIGJAVA_ATTRIBS(TYPENAME, protected, public)
+ %enddef
+
+ /* Public getCPtr method, public constructor */
+ %define PUBLIC_BODYMETHODS(TYPENAME)
+ SWIGJAVA_ATTRIBS(TYPENAME, public, public)
+ %enddef
+
+06/03/2004: wsfulton
+ [Java, C#] The contents of the class modifier typemaps and pragmas have changed.
+ They must now include the class type. Previously 'class' was hard coded.
+ This change enables flexibility into what type of class is generated,
+ for example the proxy class could be an interface instead of a class.
+
+ For Java this affects the javaclassmodifiers typemap and the jniclassclassmodifiers
+ and moduleclassmodifiers pragmas.
+
+ For C# this affects the csclassmodifiers typemap and the imclassclassmodifiers
+ and moduleclassmodifiers pragmas.
+
+ Unless you have overridden the default versions of these typemaps or pragmas, you
+ shouldn't be affected. However, if you have, upgrading is easy, for example
+
+ class Foo {};
+ %typemap(javaclassmodifiers) Foo "public final"
+
+ must now be:
+
+ class Foo {};
+ %typemap(javaclassmodifiers) Foo "public final class"
+
+
+ *** POTENTIAL INCOMPATIBILITY FOR C# MODULE ***
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+05/31/2004: wsfulton
+ Fix for C++ exception specifications that are references. Problem reported by
+ Oren Miller. Also improves the generated exception declarations in the
+ catch handler for pointers - a pointer is used instead of a reference to
+ a pointer. Added default throws typemaps for SWIGTYPE &, SWIGTYPE * and
+ SWIGTYPE[ANY] (Java and C#).
+
+05/31/2004: wsfulton
+ [Java, C#] Some minor typesafe enum improvements, including storing the name of
+ the enum item. The toSring() / ToString() methods are overridden to return this name.
+
+05/30/2004: wuzzeb (John Lenz)
+ [Chicken]
+ - Update how examples and the test suite are built.
+ - Symbol names are no longer converted to lower case
+ - Added union_runme.ss, which was copied and modified from the guile module
+
+05/26/2004: lballabio (Luigi Ballabio)
+ Committed on behalf of Marcelo (who still has problems with
+ the SourceForge CVS.)
+
+ Added Python typemaps for FILE* with (Python-only) test.
+
+5/24/2004: dancy
+
+ * Allegro CL module: Now using some macros (defined in
+ Lib/allegrocl/allegrocl.swg), swig-defconstant and swig-defun, for
+ defining constants and foreign functions. This makes the
+ generated file a bit neater.
+
+ Now strips a layer of parenthesis from constants.
+
+ Uses (* :void) instead of :foreign-address now.
+
+05/20/2004: wsfulton
+ Unnamed enum global variables are now supported in addition
+ to the recently added support for unnamed enum member variables.
+ For example:
+
+ struct Foo {
+ enum { enum1, enum2 } MemberInstance;
+ };
+ enum { enum3, enum4 } GlobalInstance;
+
+ The int typemaps are used for wrapping the get/set accessor methods.
+ If the sizeof an enum is not the same size as an int then setting the
+ variable will silently do nothing as the casts cannot be easily and portably
+ generated. If you need to solve this highly obscure situation, write
+ the assignment using the %exception feature.
+
+05/20/2004: wsfulton
+ [C#] C# enum wrapping mods. Similar to the Java module, enums can be wrapped using
+ one of 3 approaches:
+
+ 1) Proper C# enums - use %include "enums.swg"
+ 2) Typesafe enums - use %include "enumtypesafe.swg"
+ 3) Simple constant integers (original approach) - use %include "enumsimple.swg"
+
+ See each of these files for further details. Each of these files use typemaps
+ and a new feature to control the generated code. The feature is:
+
+ %csenum(wrapapproach);
+
+ where wrapapproach should be one of: "proper", "typesafe", "typeunsafe" or "simple".
+ [No implementation deemed necessary for type unsafe enums].
+
+ The default approach is proper C# enums. Anonymous enums are always wrapped by
+ constant integers.
+
+ *** POTENTIAL INCOMPATIBILITY FOR C# MODULE ***
+
+05/20/2004: wsfulton
+ [Java] Java enum support added. There are now 4 ways in which enums can be wrapped:
+
+ 1) Proper Java enums - use %include "enums.swg"
+ 2) Typesafe enums - use %include "enumtypesafe.swg"
+ 3) Type unsafe enums (constant integers) - use %include "enumtypeunsafe.swg"
+ 4) Simple constant integers (original approach) - use %include "enumsimple.swg"
+
+ See each of these files for further details. Each of these files use typemaps
+ and a new feature to control the generated code. The feature is:
+
+ %javaenum(wrapapproach);
+
+ where wrapapproach should be one of: "proper", "typesafe", "typeunsafe" or "simple".
+ The default typemaps will handle enums that may or may not have specified initial
+ values, for example ten is specified:
+
+ enum Numbers { zero, ten(10) };
+
+ However, the amount of generated Java code can be cut down, by modifying these typemaps
+ if none of the enums have initial values (proper Java enums and typesafe enums approach).
+
+ The default approach is typesafe enums. Anonymous enums are always wrapped by
+ constant integers.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+05/11/2004: wsfulton
+ [Java, C#] Fix bug using %rename on enum items and when using
+ %javaconst(1) / %csconst(1)
+ For example, the following used to generate code that wouldn't compile:
+
+ %rename(Obj) Object;
+ enum Grammar { Subject, Object };
+
+04/28/2004: wsfulton
+ [Java, C#] Minor fixes when using combinations of the
+ javainterfaces, javabase, csinterfaces and csbase typemaps.
+
+05/18/2004: wsfulton
+ [Java] JVM link failure on some systems fixed when using std_vector.i.
+ Also adds default vector constructor for use from Java.
+
+05/17/2004: mkoeppe (Matthias Koeppe)
+
+ [Guile] New runtime functions SWIG_PointerAddress,
+ SWIG_PointerType, SWIG_IsPointerOfType, SWIG_IsPointer.
+
+ [Guile] In -scm mode, wrap several SWIG runtime functions
+ and export them into the module (Swig swigrun). The
+ runtime module is now built with "module" linkage.
+
+ [Guile] GOOPS proxy objects now also print the pointer
+ address of the C object.
+
+05/14/2004: lyle
+ Added Kou's patch for the Ruby %import directive so that modules
+ with "nested" names are handled properly. Consider an interface
+ file foo.i that has this %module declaration at its top:
+
+ %module "misc::text::foo"
+
+ Now consider another interface file spam.i that imports foo.i:
+
+ %import foo.i
+
+ Before this patch, this would result in the following code being
+ generated for spam_wrap.c:
+
+ rb_require("misc::text::foo");
+
+ With this patch, however, you'll get the correct path name
+ for the call to rb_require(), e.g.
+
+ rb_require("misc/text/foo");
+
+ See SourceForge Bug #928299.
+
+05/12/2004: wsfulton
+ Patch for emitting directors when %feature("director") specified
+ for a class with no virtual methods, but does have a virtual destructor.
+ Submitted by Kevin Smith.
+
+05/06/2004: mkoeppe (Matthias Koeppe)
+ New SWIG runtime function SWIG_TypePrettyName, which
+ returns an unmangled type name for a swig_type_info
+ object.
+
+ [Guile]: Use it for printing pointer objects.
+
+05/03/2004: dancy (Ahmon Dancy)
+
+ * Lib/allegrocl/allegrocl.swg: Updated comments about identifer
+ conversion.
+
+ * Sources/Modules/allegrocl.cxx: Register /dev/null for "header"
+ target. Also, disregard "const" qualifiers during type
+ conversion.
+
+
+05/02/2004: wuzzeb (John Lenz)
+ [Chicken] Fix bug 782468.
+ To fix this bug, the runtime code has been rewritten, and
+ pointers are now represented as a C_SWIG_POINTER_TYPE.
+
+ Chicken version > 1.40 is now required!
+
+ * Typemap incompatibility: typemaps no longer use chicken_words.
+ If a typemap needs some space, it should just call C_alloc
+
+ * argout typemaps no longer use the /* if ONE */ construct to
+ build an output list. A SWIG_APPEND_VALUE macro, exactly like
+ guile and mzscheme is now used.
+
+04/25/2004: mkoeppe (Matthias Koeppe)
+ [Guile] In the generated GOOPS code, don't create methods
+ that would not specialize any arguments; simply re-export
+ the primitive functions. (This is a performance
+ optimization which reduces load time and execution time.)
+
+ [Guile] In -gh mode, fix the "too many initializers" error
+ which was caused by an incompatible swig_type_info layout.
+
+ [Guile] The typemap for FILE * in ports.i now also accepts
+ a regular FILE * pointer object. Also a bug with Scheme
+ file ports that are open for input and output has been
+ fixed.
+
+04/25/2004: wsfulton
+ Change entry 03/21/2004 revoked. The change introduced another
+ inconsistency (reference typemaps beings used instead of
+ pointer typemaps for member variables as well as static
+ member variables and global variables for some languages,
+ but only for C++ and not C). This would break user's current
+ typemaps and introduce further inconsistencies. Alternative
+ solution required and being discussed.
+
+04/10/2004: mmatus (Marcelo Matus)
+
+ Added the -directors flag. This enables the director
+ mode for the interface and all the classes that
+ don't set the "feature:nodirector" explicitly.
+
+ You can use this in your module if you want to use the
+ director feature in all your classes, but it is most
+ intended for testing purposes, like:
+
+ make check-python-test-suite SWIG="../../../swig -directors"
+ make check-ruby-test-suite SWIG="../../../swig -directors"
+ make check-java-test-suite SWIG="../../../../swig -directors"
+
+ These commands will run the entire test-suite using
+ directors, and not only the specific 'directors_*'
+ cases. This should be done from time to time.
+
+04/10/2004: mmatus (Marcelo Matus)
+
+ [python] Added support for std::wstring and wchar_t,
+ for compiler and python versions that support them.
+
+ When needed, use
+
+ %inlcude std_string.i // 'char' strings
+ %inlcude std_wstring.i // 'wchar_t' strings
+
+
+04/10/2004: mmatus (Marcelo Matus)
+
+ [python] Fix the default behaviour (seg. fault) when an
+ inplace operator (+=,-=,...) was wrapped, as reported by
+ Lucriz (lucriz@sitilandia.it), when the most common
+ form was used:
+
+ A& A::operator+=(int i) { ...; return *this; }
+ ^^^^ ^^^^^^
+
+
+ ie, an object is returned and its contains the same 'this'
+ value than the input object, which is deleted after the
+ operation "a += b", leaving the result with no real
+ object, but a seg. fault.
+
+ To fix it, we needed to introduce a new feature and use an
+ old one:
+
+ %feature("self:disown") A::operator+=;
+ %feature("new") A::operator+=;
+
+ here, "self:disown" disable the ownership of the 'self'
+ or input object, and the "new" feature transfers the
+ ownership to the result object.
+
+ The feature/solution could also be used in other languages
+ that use gc and implement the inplace operators, or other
+ operators, in a similar way.
+
+ *** POTENTIAL INCOMPATIBILITY FOR Python MODULE ***
+
+ If you already are using the inplace operators in python,
+ and you implemented some kind of workaround to the problem
+ fixed here, it is possible you could end with 'free'
+ objects that never get deleted. If that is the case, and
+ you want to disable the current fix, use:
+
+ %feature("self:disown","") A::operator+=;
+ %feature("new","") A::operator+=;
+
+
+04/07/2004: cheetah (William Fulton)
+ [C#] C++ enums are no longer wrapped by integers, they are now wrapped by
+ C# enums. For Example, given C++:
+
+ enum AnEnum { foo, bar };
+ typedef AnEnum AnEnumeration;
+ void something(AnEnum e, AnEnumeration f);
+
+ The following is generated:
+
+ public enum AnEnum {
+ foo,
+ bar
+ }
+ public static void something(AnEnum e, AnEnum f) {...}
+
+ Note that a global enum like AnEnum above is generated into its own
+ file called AnEnum.cs. Enums defined within a C++ class are defined
+ within the C# proxy class. Some of the typemaps for modifying C# proxy
+ classes also work for enums. For example global enums can use
+
+ %typemap(csimports) to add in extra using statements.
+
+ Global enums and class enums can use
+
+ %typemap(csclassmodifiers) to make the enum private, public etc.
+ %typemap(csbase) to change the underlying enum type (enum base)
+
+ If we add this for the above example:
+
+ %typemap(csclassmodifiers) AnEnum "protected"
+ %typemap(csbase) AnEnum "long"
+
+ the following is generated:
+
+ protected enum AnEnum : long {
+ foo,
+ bar
+ }
+
+ *** POTENTIAL INCOMPATIBILITY FOR C# MODULE ***
+
+04/07/2004: cheetah (William Fulton)
+ Seg fault fix for empty enums, like
+ enum Foo {};
+
+03/21/2004: mmatus
+ [Note: this change revoked on 04/25/2004]
+ [Python] Makes the following 'var' cases more uniform:
+
+ std::string ga;
+
+ struct A {
+ static std::string sa;
+ std::string ma;
+ };
+
+
+ now the three variables (ga, sa, ma) can be assigned as:
+
+
+ cvar.ga = "hello";
+ A.sa = "hello";
+ a.ma = "hello";
+
+ ie, now 'ma' will also use a std::string typemap 'in' if
+ defined, before it was only accepting a 'p_std_string'
+ pointer. Note, however, that 'ma' will not use the
+ 'varin/varout' typemaps (that probably could be more
+ natural), but it will pick up the 'in' typemap for const
+ std::string& (which is easier).
+
+ The changes in cwrap.c and lang.cxx will probably fix the
+ behaviour in other languages that do not overload the
+ membervarHandler method "too much".
+
+
+03/21/2004: mmatus
+ [Python] Disabling the default instantiations like:
+
+ %template() std::pair<int,int>;
+
+ for all the primitive types and STL containers/classes.
+ They are expensive, specially for pair and map, and the
+ previous behaviour also requires the user to perform
+ manual instantiations. Still, if the speed difference is
+ not important, it can be re-enabled by defining the macro
+ SWIG_STD_DEFAULT_INSTANTIATION (see std_common.i).
+
+ Also, normalizing the INPUT/OUTPUT/INOUT typemaps. Now
+ they use the same conversors than the rest of the
+ typemaps, and you can use them for std::pair, std::string
+ and all the other STL types, like in:
+
+ void p_inoutd(std::pair<double, double> *INOUT);
+
+ Added the attribute.i and implicit.i files with macros to
+ transform functions pairs like 'set_x'/'get_x'
+ (or 'T& x()'/'const T& x() const') into an attribute,
+ and allowing the use of implicit constructors in typemaps
+ (see the files for more details).
+
+03/21/2004: mkoeppe
+ [Guile] Fix the documentation strings of functions with
+ anonymous arguments.
+
+03/18/2004: mmatus
+ [Python] More general std_string.i interface.
+ Now you can wrap it using
+
+ %template(string) std::basic_string<char>;
+
+ and use the std::string as a base class:
+
+ struct A : std::string {
+ };
+
+ But more important, swig will recognize
+ both std::basic_string<char> and std::string as
+ the same type.
+
+03/16/2004: mmatus
+ Previously added, but not mentioned before:
+
+ - friend declaration support, swig now emits a global
+ function in the same class scope.
+
+ - ref/unref features: to mix ref counting C++ classes
+ and native script ref counting mechanisms (like in python).
+
+ Use it like:
+
+ %feature("ref") RCObj "$this->ref();"
+ %feature("unref") RCObj "$this->unref();"
+
+ And the class RCObj, and all the derived ones, will
+ perform the right ref/unref calls when a new pointer
+ is returned to the target language, or when the target
+ language attempts to delete the object.
+
+ See the refcount.i file in the test-suite for more
+ details.
+
+
+03/16/2004: mmatus
+ [Python] Using the new %fragment support, major rewrote
+ of the python swig library, including:
+
+ - Almost automatic template/typemap instantiation for
+ the STL components. For example, now you can write:
+
+ %template(vector_i) std::vector<int>;
+
+ and a specialized vector_i class is emitted with all
+ the needed typemaps. No need to use the old
+ 'specialize_vector' macros.
+
+ Note you can also define
+
+ %template(matrix_i) std::vector<std::vector<int> >;
+ %template(vector_pii) std::vector<std::pair<int,int> >;
+
+ - The empty template instantiation
+
+ %template() std::vector<int>;
+
+ defines the vector typemaps, but no proxy class. For all the
+ fundamental types, the empty template instantiation are
+ defined, so, you can say
+
+ %include std_vector
+
+ int func(const std::vector<int>& a);
+
+ where the proper typemap is applied to 'a', but no
+ std::vector<int> proxy is generated.
+
+
+ - All the STL containers present a more uniform behavior and
+ more complete interface declaration. The following are
+ now supported:
+
+ std::vector<T>
+ std::list<T>
+ std::deque<T>
+ std::set<T>
+ std::multiset<T>
+ std::map<T>
+ std::multimap<T>
+
+ not a container, but also supported:
+
+ std::pair<T,U>
+
+ also, more typemaps are defined for all of them,
+ including varin, varout, typecheck, etc.
+
+ - Initial attempt to implement the STL containers
+ considering allocators, ie:
+
+ std::vector<T,A>
+
+ it is partially working, but it is just a workaround
+ while swig improves its template type support.
+
+
+ Please test with your particular setup. It seems to be
+ working with g++ 3.2.2, g++ 2.96, Intel icc and SGI CC
+ compilers, plus python 1.5.2, 2.0 and 2.3, but since
+ we are using templates, there is a chance you can find
+ some problems when using with an old C++ compiler.
+
+03/16/2004: mmatus
+
+ - Allowing the empty %template directive, such as
+
+ %template() std::vector<int>;
+
+ to process the class "typedef"s and "typemap"s. Before
+ only the internal "typedef"s were processed.
+
+ This makes possible to emit the default in/out
+ typemaps without the need of wrapping an specialized
+ vector instance.
+
+ - Adding the preprocessor extension #@ which mangles the
+ following macro argument, like in:
+
+ #define macro(X) #@X
+ macro(int) -> int
+ macro(std::string) -> std_s_s_string
+
+ - Fragments can now be "type specialized", as the typemaps. The
+ syntax is as follows
+
+ %fragment("name","header")
+ { /* a type independent fragment (old syntax) */ }
+ %fragment("name" {Type}, "header")
+ { /* the fragment is type dependent */}
+
+ Now fragments can also be used inside templates:
+
+ template <class T>
+ struct A {
+ %fragment("incode"{A<T>},"header") {
+ /* 'incode' specialized fragment */
+ }
+
+ %typemap(in,fragment="incode"{A<T>}) {
+ /*
+ here we use the 'type specialized'
+ fragment "incode"{A<T>}
+ */
+ }
+ };
+
+
+03/11/2004: cheetah (William Fulton)
+ [Java] Director bug which meant that some virtual functions overridden in
+ Java were not being called on some operating systems. Bug reported and fixed
+ by Robert de Vries and Scott Michel.
+
+03/02/2004: mkoeppe (Matthias Koeppe)
+ [Guile] In -scm mode, don't forget to check the type of string arguments.
+
+02/24/2004: cheetah (William Fulton)
+ [C#] New commandline option -namespace <name>. This allows one to specify
+ a C# namespace into which all C# classes are generated.
+
+02/23/2004: mkoeppe (Matthias Koeppe)
+ [MzScheme] Use FUNC_NAME rather than a bogus typemap variable for signalling
+ errors. Call scheme_wrong_type with a zero-based argument number.
+ Reported by Ondrej Pacovsky, SF #902621.
+
+ [Guile] Define FUNC_NAME also in the dispatch wrapper for overloaded
+ functions. Patch by John Lenz, SF #896255.
+
+02/22/2004: mkoeppe (Matthias Koeppe)
+ [Guile] In -scm mode, don't try to invoke a null destructor function.
+
+02/20/2004: cheetah (William Fulton)
+ Fixes so that the SWIG source will compile using the Digital Mars Compiler
+ (formerly Symantic compiler) on Windows. Submitted by Scott Michel.
+
+02/13/2004: mkoeppe (Matthias Koeppe)
+ [MzScheme] New command-line argument -noinit. Use it for building
+ the runtime library, where we don't want to define the functions
+ scheme_initialize etc. Reported by Tim Brown, SF #891754.
+
+ [MzScheme] Don't produce invalid C code when invoked with the
+ -declaremodule option. Reported by Tim Brown, SF #891108.
+
+ [Guile] Build the runtime library with passive linkage, to rename
+ the SWIG_init function uniquely.
+
+02/12/2004: cheetah (William Fulton)
+ [Java, C#] Patch submitted by Bill Hoffman which prevents SWIG from crashing
+ when a file for the typewrapper class cannot be opened.
+
+02/11/2004: cheetah (William Fulton)
+ [Java, C#] Overloading changes:
+ - Methods which are overloaded in const only no longer generate Java
+ code that won't compile - the first method parsed is used and a
+ warning is displayed. Note that this behaviour is slightly different
+ to the scripting languages which always uses the non-const method.
+ - Warning messages 509 and 512 replaced by new warning number 516, which
+ is more relevant to these statically typed languages as the overloaded
+ methods aren't 'shadowed', they are ignored.
+
+01/23/2004: mkoeppe (Matthias Koeppe)
+ [Guile] Replace the "known_classes" hash table by a node
+ attribute. Methods of classes in C++ namespaces now get
+ the proper specializer in the GOOPS declaration.
+ Reported by rm@mh-freiburg.de.
+
+01/23/2004: mkoeppe (Matthias Koeppe)
+ [Guile] Uniquify the argument names in GOOPS shadow method
+ declarations. Reported by rm@mh-freiburg.de.
+
+01/21/2004: sunshine (Eric Sunshine)
+ Revived the NextStep port of SWIG.
+
+ Fixed fatal problem in DohStrstr() caused by difference in strstr()
+ implementation which made %apply become entirely dysfunctional. On
+ NextStep, strstr("foo","") evaluates to NULL; whereas, on modern
+ platforms, it evaluates to "foo". %apply relies extensively upon
+ strstr("foo","") evaluating to non-NULL, therefore it failed
+ catastrophically when faced with NextStep's strstr().
+
+ Added `bool' check to configure.in since NextStep's C++ compiler
+ does not supply this type. swig.h now fakes up `bool' if needed.
+
+ Worked around NextStep C++ compiler bug in which C++ code is
+ disallowed inside extern "C" functions. This problem affected all
+ language modules, since they publish hook functions of the form:
+ extern "C" Language *swig_foo(void) { return new FOO(); }
+ Fixed by creating a C++ wrapper:
+ static Language *new_swig_foo() { return new FOO(); }
+ extern "C" Language *swig_foo(void) { return new_swig_foo(); }
+
+ Ensured that Swig_copy_string() is used in place of strdup() since
+ NextStep does not supply strdup().
+
+ Fixed detection of Ruby library name and location in configure.in.
+ Problem 1: Assumed that library always resided in Ruby's "archdir",
+ which was correct for Ruby 1.6.x, but which is incorrect for Ruby
+ 1.8.x, in which case the library normally resides in Ruby's
+ "libdir". Problem 2: Assumed that the library could always be
+ linked via "-l"+RUBY_INSTALL_NAME (where RUBY_INSTALL_NAME
+ typically is "ruby"), however this failed for platforms, such as
+ NextStep, which do not support shared libraries. In this case, the
+ static library name in 1.8.x is libruby-static.a, thus
+ -lruby-static is required. The new logic works correctly for
+ static and shared libraries for 1.6.x and 1.8.x.
+
+ Fixed detection of Perl CFLAGS in configure.in for NextStep.
+ Detection code extracted CFLAGS from Perl's %Config hash but
+ neglected to add a newline to the value before passing it through
+ `sed'. NextStep's ancient `sed' discards input which is not
+ terminated with a newline, thus Perl CFLAGS always evaluated to the
+ empty string.
+
+01/16/2004: cheetah (William Fulton)
+ Tidy up in the exception handling code that is generated when
+ C++ exception specifications are wrapped with the throws typemap.
+ This redundant code is no longer generated:
+
+ catch(...) {
+ throw;
+ }
+
+01/12/2004: wsfulton on behalf of mmatus (marcelo matus)
+ if a method uses %exception and the method requires the use
+ of the throws typemap, the code in a throws typemap will be
+ generated inside the try body. For example:
+
+ %exception method {
+ try {
+ // method action
+ $action
+ } catch (int i) {
+ // method int catch handler
+ } catch (...) {
+ // method generic catch handler
+ }
+ }
+ %typemap(throws) Except %{
+ // throws typemap Except catch handler
+ %}
+
+ %inline %{
+ class Except {};
+ void method(int i) throw (Except);
+
+ Will generate:
+
+ {
+ try {
+ // method action
+ try {
+ method(arg1);
+ }
+ catch(Except &_e) {
+ // throws typemap Except catch handler
+
+ }
+
+ } catch (int i) {
+ // method int catch handler
+ } catch (...) {
+ // method generic catch handler
+ }
+ }
+
+
+ As can be seen, the inner try catch block is for the throws typemaps.
+ Previously, this was reversed so that the inner try catch block
+ was the %exception code. In the example above, it would have been
+ impossible to catch Except as the catch all (...) would catch the
+ exception instead.
+
+Version 1.3.21 (January 11, 2004)
+=================================
+
+01/10/2004: cheetah (William Fulton)
+ The output format for both warnings and errors can be selected for
+ integration with your favourite IDE/editor. Editors and IDEs can usually
+ parse error messages and if in the appropriate format will easily take you
+ directly to the source of the error. The standard format is used by
+ default except on Windows where the Microsoft format is used by default.
+ These can be overridden using command line options, for example:
+
+ $ swig -python -Fstandard example.i
+ example.i:4: Syntax error in input.
+ $ swig -python -Fmicrosoft example.i
+ example.i(4): Syntax error in input.
+
+01/09/2004: beazley
+ Fixed [ 871909 ] simple namespace problem.
+ This was a problem using anonymous structures in a namespace.
+ For example:
+
+ namespace ns {
+ typedef struct {
+ int n;
+ } S;
+ };
+
+ Reported by Josh Cherry.
+
+01/09/2004: beazley
+ Fixed some broken Perl examples.
+
+12/28/2003: cheetah (William Fulton)
+ [Java and C#] Fixes for wrapping covariant (polymorphic) return types.
+ For example:
+
+ struct Base {
+ virtual ~Base();
+ virtual Base* copy() const = 0;
+ };
+ struct Derived : Base {
+ virtual Derived* copy() const;
+ };
+
+ The Derived::copy proxy method returns Base not Derived. A warning is issued
+ about this. Previously the pointer used by the proxy class was incorrectly
+ treated as a Base* instead of a Derived*.
+
+12/18/2003: cheetah (William Fulton)
+ Fix so that Windows paths are displayed correctly when reporting errors.
+ An error previously would have been shown something like:
+
+ .?xample.i:14: Syntax error in input.
+
+ instead of:
+
+ .\example.i:14: Syntax error in input.
+
+
+Version 1.3.20 (December 17, 2003)
+==================================
+
+12/17/2003: beazley
+ Last minute modifications. Perl5 module now generates shadow classes
+ by default like all of the other modules. PHP4 wrappers no longer
+ include "config.h".
+
+12/14/2003: beazley
+ Weakened warning message related to constructor names so that an
+ unusual nested-class wrapping technique would work again (apparently
+ it worked in some older SWIG releases). For example:
+
+ class Scope {
+ class ClassA;
+ class ClassB;
+ };
+ class Scope::ClassA {
+ ...
+ };
+ class Scope::ClassB {
+ ...
+ }
+
+ Note: There is still some odd interaction with the SWIG symbol
+ table/type system that will need to be looked at in a future release.
+ Reported by Gustavo Niemeyer.
+
+
+12/11/2003: cheetah (William Fulton)
+ [Java] Protected class methods are wrapped as protected Java methods
+ when using the dirprot director feature. This can be changed using
+ %javamethodmodifiers to something else should the need arise, for
+ example, private or package access.
+
+12/11/2003: cheetah (William Fulton)
+ [Java, C#]
+ %javamethodmodifiers (Java) and %csmethodmodifiers (C#) operate slightly
+ differently. Previously this feature had to be present to set the method
+ modifiers. Now it is only used if it exists for the method being wrapped.
+ The default is "public" as previous however, when wrapping protected
+ director methods it is "protected". This change will not affect existing
+ use of the %javamethodmodifiers or %csmethodmodifiers.
+
+12/11/2003: mmatus (Marcelo Matus)
+
+ This fix some recurring reports about keywords not been
+ properly identified and warned, and it solves the problem
+ of how to add a test file to the test-suite such that it
+ doesn't use any keyword of all the supported languages
+ (and doing it without compiling the test for all the
+ supported languages, thing that is not always possible,
+ and without requiring you to know all the supported
+ language keywords, thing that is always impossible).
+
+ So these are the changes globally speaking:
+
+ - Uniform the definition of the keyword warnings through
+ the supported languages: all the languages has now a
+ separate file that defines the keywords or bad names:
+
+ python/pythonkw.swg
+ chicken/chickenkw.swg
+ ....
+
+ - Added keyword list for most of the languages that didn't
+ have one (using the new separated file).
+
+ - Added the "All keywords" warning support: -Wallkw option.
+
+ This option allows you to include all the known keywords
+ for all the supported languages, and can be used as:
+
+ swig -Wallkw ....
+
+ This will help to the process of adding a test-suite
+ file that can be compiled in all the swig supported
+ languages, and it will be also helpful for users who
+ want to create multi-language libraries.
+
+ And these are the detailed changes (mostly file addition):
+
+ - For the languages that already have some sort of keyword
+ warning list, move it to an external languagekw.swg
+ file, ie:
+
+ move keywords from python.swg -> pythonkw.swg
+ move keywords from chicken.swg -> chickenkw.swg
+ move keywords from tcl8.swg -> tclkw.swg
+
+ and re-include languagekw.swg from language.swg.
+
+ - For the language that didn't have a keyword list, and
+ for the ones that I could find a list, add the
+ languagekw.swg file, ie:
+
+ csharp/csharpkw.swg
+ java/javakw.swg
+ php4/phpkw.swg
+ pike/pikekw.swg
+ ruby/rubykw.swg
+
+
+ also add a line in language.swg to include
+ languagekw.swg, but now it is commented!!!, like in
+ java.swg:
+
+ /* java keywords */
+ /* please test and activate */
+ //%include "javakw.swg"
+
+ ie, there will be no change in how swig runs normally
+ until the language maintainer test and uncomment that
+ line.
+
+ So, please check each languagekw.swg file (I left the
+ link to the keyword list source for checking), and after
+ testing, uncomment the %include line.
+
+ - Added the file allkw.swg, which includes all the
+ languagekw.swg files.
+
+ For the languages that has no languagekw.swg file right
+ now, and if they need one, add the file into the
+ language directory, and add the corresponding include
+ line into the allkw.swg file.
+
+ - Added the -Wallkw that includes the allkw.swg file.
+ Note that the old -lallkw.swg option couldn't be used
+ since it include the file after it would be needed.
+
+
+ Hopefully, the -Wallkw option will be added to the default
+ rules in the related test-suite Makefiles, so, when
+ creating a new test, or adding a new swig library file
+ (like _std_deque.i), swig will warn you if you are using a
+ bad name, considering all the language where it needs to
+ run.
+
+ Right now you can test it by using:
+
+ make check-python-test-suite SWIG="swig -Wallkw"
+
+ or using your favorite target language, it doesn't matter.
+
+ And yes, there are several examples that are using
+ reserved keywords, specially from csharp.
+
+ *** Remember ****: the new keyword warning lists are not
+ included by default in any of language that before didn't
+ have one. To enable the keyword warnings as the default
+ behavior, the inclusion of the languagekw.swg file has to
+ be uncommented at each language.swg file.
+
+ So, all the language maintainers, please check the
+ keywords list.
+
+ Also, you can add buit-in names, and not only keywords, like
+ 'True/False' in python. Remember that you can be more
+ specific and refer only to member names, like *::configure
+ or *::cget (see an example in the tcl8/tcl8kw.swg file),
+ or only global names, like ::range (see an example in the
+ python/pythonkw.swg file.
+
+ Just to be consistent, use the following codes:
+
+ - Use code 314 for keyword and/or fatal bad names.
+ - Use code 321 for buit-in and/or not fatal bad names.
+
+ so, they can't be disabled/enabled independently (see
+ python/pyhtonkw.swg for examples).
+
+ **** And don't add any new test file without checking it
+ with the -Wallkw option!! (that includes me) *****.
+
+
+12/11/2003: cheetah (William Fulton)
+ SF bug #854634
+ Added support for accepting the Unix directory separator '/' on
+ Windows and the Mac in addition to the native one ( '\' on
+ Windows). This can be used in %import, %include and commandline
+ options taking a path, for example -I. On Cygwin, both the Windows
+ and Unix directory separator can now be used (was '/' only).
+
+12/10/2003: mmatus (Marcelo Matus)
+
+ [python] Implementing the runtime "reprotected" director
+ members, if you have:
+
+ %feature("director") B;
+
+ class Bar {
+ public:
+ virtual ~Bar();
+ virtual int hello() { return do_hello();)
+
+ protected:
+ virtual int do_hi() {return 0;}
+ virtual int do_hello() {return 0;}
+ };
+
+ then, at the python side
+
+ import my_module
+
+ class Foo(my_module.Bar):
+ def do_hello(self):
+ return 1
+ pass
+
+ b = Bar() # Pure C++ Director class
+ f = Foo() # C++ Director + python methods
+
+ b.hello() # Ok, and it calls C++ Bar::do_hello()
+ f.hello() # Ok, and it calls Python Foo::do_hello()
+
+ b.do_hi() # RuntimeError, do_hi() is protected!!
+ f.do_hi() # RuntimeError, do_hi() is protected!!
+
+ b.do_hello() # RuntimeError, do_hello() is protected!!
+ f.do_hello() # Ok, since it its redefined in python.
+
+ Here Bar.do_hello is always protected, but Foo.do_hello
+ is "public", because it is redefined in python. Before,
+ all the 'do_hello' methods were public.
+
+ This seems to be a good compromise between C++ and python
+ philosophies, ie, all the director protected methods keep
+ protected at the user side (C++ way) until they are
+ redefined (python way, were all defined methods are always
+ public). And this is not only a good compromise, it also
+ seems to be the only way to do it :).
+
+ Now ruby has native director protected members, and python
+ pure runtime support. I guess these are the two possible
+ extreme cases. And hopefully, they could be used as
+ templates to modify the other languages that support
+ directors, so they can "reprotect" the protected director
+ members at the target language side.
+
+ This finished the director protected support for the
+ python language. Ocalm will need to add the
+ "reprotection" later.
+
+12/10/2003: mmatus (Marcelo Matus)
+
+ The following case (reported by Lyle Johnson) was fixed:
+
+ %rename(x) Foo::y();
+
+ class Foo {
+ public:
+ void y();
+
+ protected:
+ int x;
+ };
+
+ swig warned that the symbol 'x' was already defined, and
+ the renaming fails. 'x' was not emitted, since it is
+ protected, but it was kept in the symbol table with too
+ much information.
+
+ Now swig works for all the cases (plain, director and
+ dirprot) again. This was fixed by allowing the parser.y to
+ decide much closer what to do with 'x'. Before all the
+ discarding or generation was resolved at the lang.cxx
+ stage. Also the changes in parser.y to implement the
+ director protected mode are now much more encapsulated, and
+ they get disabled if the mode is not enabled. Before the
+ deactivation was done at the generation stage (lang.cxx).
+
+ By the other hand, if the director mode is enabled, and
+ %rename is done, reusing a protected member name, there is
+ a pathological case:
+
+ %rename(x) Foo::y();
+
+ class Foo : public A {
+ public:
+ void y();
+
+ protected:
+ int x; /* works */
+ static int x; /* works */
+ static void x(); /* works */
+ typedef void x(); /* works */
+
+ virtual void x(); /* always fails, as it should, since
+ Foo::x() will be emitted in the
+ director */
+
+ void x(); /* always fails, but sometimes it shouldn't,
+ since the Foo::x() will not be emitted if
+ it is not virtual */
+
+ };
+
+ The last case is not always right because at the parser.py
+ stage it is not possible to decide if the protected member
+ Foo::x() could or not conflict with the renamed Foo::y(),
+ since Foo::x() could be virtual by inheritance.
+
+ I guess this just an intrinsic limitation, and no much can
+ be done about it without resorting into larger changes to
+ postpone, under certain conditions, the multiply symbol
+ detection (lang.cxx stage).
+
+ So, by now, it is just considered a well known "feature" in
+ the director protected mode. The good news is that it seems
+ to be a rare case, and it can be avoided by the user by
+ hiding 'x' before renaming 'y':
+
+ %rename(_x) Foo::x();
+ %rename(x) Foo::y();
+
+
+12/08/2003: mmatus (Marcelo Matus)
+ The virtual method detections now properly
+ treats the following cases:
+
+ namespace foo { typedef int Int; }
+ struct A {};
+ typedef A B;
+
+ struct Foo {
+ virtual ~Foo() {}
+
+ virtual Foo* cloner() = 0;
+ virtual int get_value() = 0;
+ virtual A* get_class() = 0;
+ virtual void just_do_it() = 0;
+ };
+
+ struct Bar : Foo
+ {
+ Bar* cloner();
+ foo::Int get_value();
+ B* get_class();
+ void just_do_it();
+ };
+
+ All the Foo and Bar methods are virtual. A new attribute
+ "virtual:type" record the base polymorphic type. In the
+ previous cases we have:
+
+ type : Bar virtual:type : Foo
+ type : foo::Int virtual:type : int
+ type : B virtual:type : A
+ type : void virtual:type : void
+
+ This attribute is useful in languages (java+directors)
+ that could have problems redefining Bar* Bar::cloner().
+
+ If you never had code like the above, you will see no
+ effects. But if you have some code like that, you
+ will see some effects since some methods that
+ before were not properly treated as virtual,
+ will start to act like that. This could enlarge
+ your director classes.
+
+
+12/08/2003: mmatus (Marcelo Matus)
+ The director protected member support (dirprot)
+ is disabled by default.
+
+ It can be enable by using '-dirprot' or by adding
+ the option to the module declaration, like:
+
+ %module(directors="1",dirprot="1") my_module
+
+ This module option was added to properly compile the
+ director_protected.i and director_nested.i examples.
+
+ The feature has been tested with python[2.2,2.3]
+ and ruby[1.6.7], both at compilation and runtime, and
+ java[j2sdk1.4.1_01], but only at compilation (my java
+ installation doesn't run any of the director examples,
+ olds nor news).
+
+ Please test for ocaml and java.
+
+ The errors reported by William and Scott were fixed,
+ except for a warning about SWIG_JavaThrowExecption()
+ multiply defined. I can't reproduce this error with my
+ examples. We will wait for Scott to send us a minimal
+ case.
+
+
+12/07/2003: mmatus (Marcelo Matus)
+ The director protected member support has been
+ completly moved out from python.cxx, and now
+ resides in the common lang.cxx, emit.cxx and
+ allocate.cxx files.
+
+ This means it should work for all the other languages
+ that currently support directors, ie, python, java, ocalm
+ and ruby.
+
+ The change has been tested with python (compilation+runtime)
+ and java (just compilation).
+
+ Please add runtime tests for the missing languages
+ and test it.
+
+ The '-nodirprot' option was moved to the principal main,
+ and can be used from all the languages.
+
+12/07/2003: cheetah (William Fulton)
+ [Java] Fixed and improved error checking of STRING_OUT typemaps in
+ various.i.
+
+12/04/2003: mmatus (Marcelo Matus)
+
+ - Now the virtual members with no explicit declarator
+ are properly identified:
+
+ struct A {
+ virtual int f() = 0;
+ };
+
+ struct B : A {
+ int f();
+ };
+
+ Here, B::f() is virtual, and the director and the
+ virtual elimination mechanism now recognize that.
+
+ - [C#] This fix also fixes the problem where 'override' was not being
+ used on any overridden virtual method, so for struct B above,
+ this C# code is generated:
+
+ public class B : A {
+ ...
+ public override int f() {
+ ...
+ }
+ ...
+ }
+
+ - Initial support for protected virtual methods. They are now
+ properly emitted when using with director (python only by
+ now).
+
+ %feature("director") A;
+ struct A {
+ protected:
+ virtual int f1() = 0;
+ };
+
+ %feature("director") B;
+ struct B : A{
+ protected:
+ int f1();
+ virtual f2();
+ };
+
+ This can be dissabled by using the '-nodirprot' option.
+
+ - The feature 'nodirector' is working now at the top level,
+ so, it must work for all the languages:
+
+ %feature("director") A;
+ %feature("nodirector") A::f2;
+
+ struct A {
+ virtual int f1();
+ virtual int f2();
+ };
+
+ in this case, only 'f1' is exported to the director class.
+
+ - Added director support for const TYPE& arguments (python).
+
+12/02/2003: cheetah (William Fulton)
+ [Java] Fix for INOUT and OUTPUT typemaps in typemaps.i for when the JNI type
+ is bigger than the C type. For example, unsigned long (32bits on most systems)
+ is mapped to jlong (64bits). Returned value was incorrect. Bug reported by
+ Brian Hawley.
+
+12/02/2003: cheetah (William Fulton)
+ [C# and Java] Better fix for entry dated 05/11/2003. Fixes the following
+ typemaps:
+
+ Java: javabase, javainterfaces, javaimports, javaclassmodifiers,
+ javaptrconstructormodifiers, javafinalize, javagetcptr & javacode.
+ C#: csbase, csinterfaces, csimports, csclassmodifiers,
+ csptrconstructormodifiers, csfinalize, csgetcptr & cscode.
+
+ It also fixes bug in using arrays of C structs with arrays_java.i
+ as reported Scott Michel.
+
+12/02/2003: beazley
+ [Perl] Fixed [ 852119 ] recursive inheritance in output .pm, perl5.
+ Reported by William Dowling.
+
+12/02/2003: beazley
+ [Tcl] Fixed [ 755382 ] calling func(const vector<T>& p) evaluates p[0] in interp.
+ The Tcl type checker was improperly handling the interpreter result when
+ type violations were supposed to be ignored.
+ Reported by Flaviu Popp-Nowak.
+
+11/30/2003: cheetah (William Fulton)
+ Fixed [ 545058 ] configure's --with-tclincl has no effect
+
+11/30/2003: cheetah (William Fulton)
+ [Java] Fixed [ 766409 ] missing symbol SWIG_JavaThrowException during module load
+ SWIG's internal functions are all static as there is no need for different SWIG
+ generated modules to share any code at runtime.
+
+11/30/2003: beazley
+ [Tcl] Added support for C++ pointers to members.
+
+11/28/2003: cheetah (William Fulton)
+ Fixed [ 848335 ] Directors: #include wrapper .h file - was incorrectly
+ adding a directory to the generated #include "foo_wrap.h" statement
+ in some situations.
+
+11/28/2003: cheetah (William Fulton)
+ [Java] Fixed [ 849064 ] JAVA : Access modifier for derived class wrong.
+ The delete() method is always public now. It used to be protected whenever a
+ destructor was non public. An UnsupportedOperationException runtime
+ exception is thrown instead of making delete() protected now.
+
+11/28/2003: beazley
+ [Perl5] Added support for C++ pointers to members.
+
+11/28/2003: beazley
+ Fixed [ 850151 ] PYVERSION with python2.3 in configure of SWIG 1.3.19 (Maybe).
+
+11/28/2003: beazley
+ Fixed [ 850666 ] #include extra line added.
+ This should fix some problems with getting correct line numbers on
+ error messages.
+
+11/26/2003: beazley
+ Fixed another one of Marcelo's evil template bugs (infinite
+ recursion). [ 849504 ] template and typedef -> inf. recursion.
+
+11/26/2003: beazley
+ Fixed parsing problem with declarations like this:
+
+ int *x = &somearray[0];
+
+11/25/2003: beazley
+ Fixed [ 756552 ] missing default argument class scope with "|".
+ This is really only a band-aid fix for use of class-enums in
+ expressions. For example:
+
+ class A {
+ public:
+ enum Flag { flag1 = 0x1, flag2 = 0x2 };
+ void foo(int x = flag1 | flag2);
+ };
+
+ Note: there are still some (more subtle) cases that are broken,
+ but hard to fix due to an issue with template expansion. Will
+ address later.
+ Reported by Dmitry Mironov.
+
+11/25/2003: beazley
+ Incorporated [ 840878 ] support for %inline { ... } (PATCH).
+ This adds support for the following:
+
+ %inline {
+ ... some code ...
+ }
+
+ The difference between this and %inline %{ ... %} is that the
+ enclosed text is processed by the SWIG preprocessor. This
+ allows special macros and other processing to be used in
+ conjunction with %inline.
+ Contributed by Salvador Fandino Garcia.
+
+11/25/2003: beazley
+ Fixed [ 836903 ] C++ inconsistency (with void arguments).
+ SWIG was having difficulty with f() vs f(void) in C++ programs.
+ For instance:
+
+ class A {
+ public:
+ virtual void f(void) = 0;
+ };
+
+ class B {
+ public:
+ virtual void f(); // Not matched to f(void) correctly
+ };
+
+ The parser now normalizes all declarations of the form f(void)
+ in C++ classes to f(). This should fix a variety of subtle
+ problems with inheritance, optimizations, overloading, etc.
+ Problem reported by Partho Bhowmick.
+
+11/25/2003: beazley
+ [Perl5] Incorporated [ 841074 ] better croaking (PATCH). This fixes some problems
+ with strings and provides some new error functions.
+ Contributed by Salvador Fandino Garcia.
+
+11/25/2003: beazley
+ Fixed [ 791835 ] Default argument with cast: txt = (char *)"txt" syntax Error.
+ The parser should now accept things like this:
+
+ void foo(char *s = (char *) "Hello");
+
+ Problem reported by Claudius Schnorr.
+
+11/24/2003: beazley
+ [Tcl] Fixed problem with cross module linking. Previously modules referred
+ to base classes through a global variable. Now, the module looks up base
+ classes through the type system itself---avoiding the need to link to a global
+ like before. Caveat: modules with base classes must be loaded before
+ modules with derived classes.
+
+11/24/2003: mkoeppe (Matthias Koeppe)
+ [Guile] In -scm mode, use () to represent null pointers,
+ as it is done in -gh mode.
+
+11/23/2003: mkoeppe (Matthias Koeppe)
+ Add a generated script "preinst-swig", which can be used
+ to invoke SWIG before it has been installed. It arranges
+ that the runtime libraries from the source directory are
+ used.
+
+11/23/2003: mkoeppe (Matthias Koeppe)
+ [Guile] In -gh mode, don't forget to call SWIG_Guile_Init.
+ Add a SWIG_contract_assert macro.
+
+11/23/2003: mkoeppe (Matthias Koeppe)
+ [MzScheme] Update the configure check for the dynext object to work
+ with MzScheme 205.
+
+11/20/2003: mmatus
+ Fixed the include/import error reported by Kerim Borchaev,
+ where two files with names like
+
+ 'dir1/hello.i'
+ 'dir2/hello.i'
+
+ can not be include at the same time. Swig was including
+ just the first one, assuming the second one was not a
+ different one, since it was checking/keeping just the
+ basename 'hello.i'.
+
+11/19/2003: beazley
+ Changes to the SWIG runtime library support.
+ - The -c command line option has been renamed to -noruntime
+ - New command line option: -runtime. When supplied, this
+ inserts the symbol SWIG_GLOBAL into the wrapper code. This,
+ in turn, makes all of the runtime support functions globally
+ visible.
+ - New library file: swigrun.i. Used to create modules
+ for runtime library (if needed).
+
+11/18/2003: cheetah (William Fulton)
+ 'make srcrpm' rpmbuild fix - patch from Joe Cooper
+
+11/18/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Change meaning of configure option --with-guile to
+ the name of the Guile executable. The new option --with-guile-prefix
+ can be used to specify the tree where Guile is
+ installed. (However, usually it suffices to use the
+ single option --with-guile-config.)
+ When running the run tests test-suite, make sure to use the
+ version of Guile that SWIG was configured for.
+
+11/17/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Improvements to object-ownership management in
+ "-scm" mode. (They do not apply to the default "-gh" mode.)
+ * Renamed the smob type that indicates that the object can
+ be garbage collected from "collected swig" to "collectable
+ swig", which is more precise.
+ * Export the destructor functions again. It is now
+ allowed to explicitly call destructors, even for
+ garbage-collected pointer objects. A pointer object
+ that has been passed to a destructor is marked in a
+ special way using a new smob type, "destroyed swig".
+ (This helps avoid nasty memory bugs, where references to
+ dead C objects are still held in Scheme. Moreover, the
+ garbage collector will not try to free a destroyed
+ object once more.)
+ * Destructor-like functions can also mark their arguments
+ as destroyed by applying the typemap SWIGTYPE *DESTROYED.
+ (It calls the function SWIG_Guile_MarkPointerDestroyed.)
+ * Functions that "consume" their objects (or that "own"
+ them after the call) can mark their arguments as
+ not garbage collectable. This can be done by applying
+ the typemap SWIGTYPE *CONSUMED. (It calls the function
+ SWIG_Guile_MarkPointerNoncollectable.)
+ * The macro TYPEMAP_POINTER_INPUT_OUTPUT from library
+ pointer-in-out.i creates additional typemaps
+ PTRTYPE *INPUT_CONSUMED, PTRTYPE *INPUT_DESTROYED.
+ They mark the passed pointer object likewise.
+ The typemap PTRTYPE *OUTPUT creates a garbage-collectable
+ pointer object, like %newobject does for a returned
+ pointer. Use the new typemap PTRTYPE *OUTPUT_NONCOLLECTABLE
+ to create a pointer object that will not be garbage collected.
+
+11/17/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Handle $input in "freearg" typemaps.
+ Never qualify GOOPS slot names with the class name.
+ Handle optional arguments properly in the GOOPS methods.
+
+11/16/2003: cheetah (William Fulton)
+ Fixes for installation to work with the upcoming Automake-1.8.
+ mkinstalldirs was being used by a non-Automake makefile.
+ mkinstalldirs is being phased out and so was not being
+ created by Automake. install-sh used instead.
+
+11/16/2003: cheetah (William Fulton)
+ [Java] Numerous director improvements, tweaks and bug fixes since
+ the initial implementation have been contributed by Scott Michel.
+
+11/12/2003: beazley
+ [Python] When %feature("shadow") is used to add code to shadow
+ classes, the special variable $action expands to the name of the
+ underlying wrapper function that would have been called normally.
+
+11/12/2003: beazley
+ [Python] When generating proxy class code, SWIG emits a few
+ default methods for __repr__() and other Python special
+ methods. Some of these methods are emitted after all of the
+ contents of a class. However, this makes it hard to override
+ the methods using %pythoncode and some other directives that
+ allow code to be inserted into a class. These special methods
+ are now emitted into the code *before* all of the other methods.
+ Suggested by Eric Jones.
+
+11/11/2003: beazley
+ Preprocessor enhancement. For include statements like this:
+
+ %include "foo/bar.i"
+
+ the directory "foo" is now added to the search path while
+ processing the contents of bar.i. Thus, if bar.i includes other
+ files in the same directory, they will be found. Previously,
+ you would have to add additional directories using -I to make this
+ work correctly. Note: the C preprocessor seems to behave in
+ an identical manner on many (most? all?) systems.
+ Suggested by Kerim Borchaev.
+
+11/11/2003: beazley
+ Configuration changes to make SWIG work on Mac OS X 10.3.x (Panther).
+ Tested with Python, Tcl, Perl, and Ruby---all of which seem to work.
+
+11/08/2003: cheetah (William Fulton)
+ [Java] Fixed the typemaps in various.i which were mostly broken.
+ char **STRING_IN and char **STRING_RET typemaps replaced with
+ STRING_ARRAY. float *FLOAT_ARRAY_RETURN typemap removed.
+
+11/08/2003: beazley
+ [Tcl] Tcl module now emits a safe module initialization function by
+ default. It can be removed by running 'swig -nosafe'.
+
+11/04/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Only use the SCM_ API when the function
+ `scm_slot_exists_p' exists (needed for GOOPS support).
+ This function was renamed during the Guile 1.5 series
+ from `scm_slots_exists_p'.
+ Report the right runtime library when invoked with
+ -scm -ldflags.
+
+11/03/2003: mkoeppe (Matthias Koeppe)
+ [Chicken] Fix #782052. The --with-chickencfg configure
+ option (and others) were not accepted.
+
+11/02/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Merge new set of GOOPS changes by John Lenz.
+ GOOPS objects are now manipulated directly by the C code.
+ Some fixes to typemap-GOOPS interaction.
+
+11/02/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Remove the file argument to -scmstub and -goops.
+ The Scheme files are now always called MODULE.scm or
+ MODULE-primitive.scm, where MODULE is the module name and
+ "primitive" can be changed by the -primsuffix option.
+ The Scheme files are now placed in the directory given by
+ the -outdir option, or the current directory.
+ (Patch by John Lenz, slightly modified.)
+
+ *** INCOMPATIBILITY [Guile] ***
+
+11/02/2003: mkoeppe (Matthias Koeppe)
+ Unify the pointer-conversion runtime API. The standard
+ functions are:
+ * SWIG_NewPointerObj (POINTER, TYPE, FLAGS)
+ -- Create an scripting object that represents a typed
+ pointer. FLAGS are language specific.
+ * SWIG_ConvertPtr (INPUT, RESULT, TYPE, FLAGS)
+ -- Get a pointer from the scripting object INPUT and
+ store it in the place RESULT. When a type mismatch
+ occurs, return nonzero.
+ * SWIG_MustGetPtr (INPUT, TYPE, ARGNUM, FLAGS)
+ -- Get a pointer from the scripting object INPUT and
+ return it. When a type mismatch occurs, throw an
+ exception. If ARGNUM > 0, report it as the
+ argument number that has the type mismatch.
+ [Guile]: No changes.
+ [MzScheme]: No changes.
+ [Perl]: Add the function SWIG_NewPointerObj.
+ The function SWIG_MakePtr is kept.
+ The function SWIG_MustGetPtr is currently not
+ supported.
+ [Python]: Add the function SWIG_MustGetPtr.
+ [Ruby]: Add the function SWIG_MustGetPtr.
+ [Tcl]: Remove the "interp" argument of
+ SWIG_NewInstanceObj, SWIG_ConvertPtr,
+ SWIG_ConvertPacked, and SWIG_ConvertPtrFromString.
+ The function SWIG_MustGetPtr is currently
+ not supported.
+ No changes to Pike because its pointer conversion code did
+ not look complete. No changes to PHP4, because I did not
+ understand its runtime code. No changes to Chicken
+ because major changes are expected soon anyway. No
+ changes to Java, OCaml, C# because they do not seem to
+ have a pointer-conversion runtime API.
+
+ *** INCOMPATIBILITY [Tcl] ***
+
+11/02/2003: mkoeppe (Matthias Koeppe)
+ [Perl5, PHP4, Pike, Python, Ruby, Tcl]: Use the
+ preprocessor to rename external functions of the SWIG
+ runtime API to follow the naming convention
+ SWIG_<language>_<function>. This should allow linking
+ more than one interpreter into a program.
+
+10/31/2003: cheetah (William Fulton)
+ [C#] Fix since introducing the exception and std::string delegates.
+ The fix overcomes linker errors when using more than one SWIG module.
+ Problem reported by Andreas Schörk.
+
+10/31/2003: beazley
+ Incorporated patch: [ 823302 ] Incr Tcl support.
+ Contributed by Alexey Dyachenko.
+ Note: needs documentation.
+
+10/31/2003: beazley
+ Incorporated patch: [ 829325 ] new Python Module options and features.
+ Robin Dunn writes:
+
+ This patch makes a number of changes to the SWIG python module.
+
+ 1. Add -apply option, and change the default code
+ output to use the foo(*args, **kw) calling syntax
+ instead of using apply(). If the -apply option is
+ given then code is generated as before. This is very
+ similar to Patch #737281 but the new -modern option
+ makes the second half of that patch unnecessary so it
+ is not included here.
+
+ 2. Add -new_repr option. This is the same as my Patch
+ #797002 which I will mark as closed since it is no
+ longer needed. When this new option is used then the
+ __repr__ methods that are generated for proxy classes
+ will be more informative and give details about the
+ python class and the C++ class.
+
+ 3. Add %feature("addtofunc"). It allows you to insert
+ one or more lines of code inside the shadow method or
+ function that is already generated, instead of
+ replacing the whole thing like %feature("shadow") does.
+ For __init__ it goes at the end, for __del__ it goes
+ at the begining and for all others the code generated
+ is expanded out to be like
+
+ def Bar(*args, **kwargs):
+ val = _module.Foo_Bar(*args, **kwargs)
+ return val
+
+ and the "addtofunc" code is inserted just before the
+ return statement. If the feature is not used for a
+ particular method or function then the shorter code is
+ generated just like before.
+
+ 4. A little bit of refactoring to make implementing
+ addtofunc a little easier.
+
+ 5. Added a -modern command-line flag that will cause
+ SWIG to omit the cruft in the proxy modules that allows
+ it to work with versions of Python prior to 2.2. The
+ result is a simpler, cleaner and faster python proxy
+ module, but one that requires Python 2.2 or greater.
+
+10/31/2003: beazley
+ Incorporated patch: [ 829319 ] XML module tweaks.
+ This adds a new command line option -xmllite that
+ greatly reduces the amount of emitted XML code by
+ eliminating some fields mostly used in SWIG's
+ internal processing. Contributed by Robin Dunn.
+
+10/31/2003: beazley
+ Incorporated patch: [ 829317 ] Adds DohSplitLines function.
+ Contributed by Robin Dunn.
+
+10/29/2003: beazley
+ Fixed [ 827907 ] argout objects not being wrapped properly (PATH).
+ Patch contributed by Salvador Fandiño García.
+
+10/29/2003: beazley
+ Fixed [ 826996 ] perl type checking ignores perl subclasses.
+ This enhancement makes it so wrapped classes and structs can
+ be subclassed in Perl and used normally.
+ Patch contributed by Salvador Fandiño García.
+
+10/16/2003: cheetah (William Fulton)
+ [C#] IntPtr marshalled with a void* instead of int in C function
+ declarations. The casts thus look more conventional, for example:
+
+ // old
+ DllExport double SWIGSTDCALL CSharp_get_Shape_x(int jarg1) {
+ ...
+ Shape *arg1 = (Shape *) 0 ;
+ arg1 = *(Shape **)&jarg1;
+ ...
+ }
+ // new
+ DllExport double SWIGSTDCALL CSharp_get_Shape_x(void * jarg1) {
+ ...
+ Shape *arg1 = (Shape *) 0 ;
+ arg1 = (Shape *)jarg1;
+ ...
+ }
+
+
+10/14/2003: beazley
+ Fixed a subtle problem with overloaded methods and smart pointers.
+ If a class has overloaded methods like this:
+
+ class Foo {
+ public:
+ int bar(int x);
+ static int bar(int x, int y);
+ };
+
+ and the class is used as a smart pointer:
+
+ class FooPtr {
+ public:
+ Foo *operator->();
+ };
+
+ The SWIG would try to expose the static member Foo::bar
+ through FooPtr---resulting bogus wrapper code and a compiler
+ error.
+
+ Due to the way in which overloading is handled, it is
+ extremely difficult to eliminate the static method in
+ this case. Therefore, it is still exposed. However,
+ the generated code now compiles and works.
+
+10/05/2003: mkoeppe (Matthias Koeppe)
+ [Guile, MzScheme, Chicken]: Remove symbol clashes between
+ the runtime libraries by renaming all extern common.swg
+ functions with the preprocessor.
+
+10/05/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Added basic GOOPS support, contributed by John Lenz.
+ See the documentation for details.
+
+ *** NEW FEATURE ***
+
+10/04/2003: mkoeppe (Matthias Koeppe)
+ [Guile] New option, -only-setters, which disables
+ traditional getter and setter procedures for structure slots.
+
+10/03/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Added run test for reference_global_vars by John Lenz.
+
+09/30/2003: beazley
+ Partial solution to [ 792180 ] C++ smart-pointer/namespace mixup revisited.
+ The problem is not easy to fix (at least it doesn't seem so), but is
+ related to the instantiation of qualified templates inside of other
+ namespaces. SWIG now generates an error message in this case rather
+ than generating broken wrappers.
+
+09/30/2003: beazley
+ Fixed [ 800012 ] ENTER macro from CORE/scope.h clashes with libc search.h.
+ Reported by Britton Leo Kerin.
+
+09/30/2003: beazley
+ Fixed [ 811518 ] Casting ints to doubles (w/ solution?)
+ Addresses a problem with overloading in the Perl module.
+ Reported by Gerald Dalley.
+
+09/28/2003: mkoeppe
+ [Guile with -scm option] Fix typo in generated code for
+ procedures-with-setters. Reported by John Lenz.
+
+09/26/2003: beazley
+ Fixed [ 812528 ] externs not correct when throw is in signature.
+ Reported by Joseph Winston.
+
+09/23/2003: cheetah (William Fulton)
+ SWIG was generating a number of symbols that didn't comply with
+ the ISO C/C++ standard, in particular ISO/IEC 14882:1998(E) 17.4.3.1.2
+ where double underscores are forbidden as well as symbols starting with
+ an underscore followed by an upper case letter. Most of these have
+ been rooted out. See new section added to internals.html development
+ manual 'Symbol Naming Guidelines for Generated C/C++ Code'.
+
+09/23/2003: cheetah (William Fulton)
+ Director typemap name changes:
+ inv => directorin
+ outv => directorout
+ argoutv => directorargout
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+09/19/2003: mrose (Mark Rose)
+ [Python] Director constructors now default to __disown = 0,
+ which is the intended behavior and fixes the director_finalizer
+ test case under python.
+
+09/12/2003: cheetah (William Fulton)
+ [C#] - Typemaps added for std::string and const std::string &.
+ - New delegate for creating a C# string given a char *. It
+ can be used by calling SWIG_csharp_string_callback as shown
+ in the std::string 'out' typemap. Useful if the return type is
+ mapped to a C# string and the calling function is responsible
+ for cleaning up memory as the C# garbage collector doesn't
+ free the memory created in C/C++ and then returned as a C# string.
+ - The exception delegates have moved into an inner class in the
+ intermediate class, thereby freeing up the static constructor.
+
+09/11/2003: beazley
+ (Internals)
+ Major refactoring of iteration over lists and hashes. The
+ DOH library now uses iterators. They work like this:
+
+ List *l = (some list);
+
+ Iterator i;
+ for (i = First(l); i.item; i = Next(i)) {
+ // i.item contains the actual list item.
+ // i.item is NULL at end of list
+ ...
+ }
+
+ Hash *h = (some hash);
+ Iterator j;
+ for (j = First(h); j.item; j = Next(j)) {
+ // j.item contains hash table item
+ // j.key contains hash table key
+ // Both j.item and j.key are NULL at end
+ ...
+ }
+
+ The old iteration functions Firstitem(), Nextitem(), Firstkey(),
+ and Nextkey() are gone.
+
+ The new iterators are simpler, result in better memory use,
+ and may be faster. Also, there are no longer any problems
+ iterating over the same list/hash in multiple places at
+ the same time. For example, this is fine:
+
+ Iterator i,j;
+ for (i = First(l); i.item; i = Next(i)) {
+ for (j = First(l); j.item; j = Next(j)) {
+ ...
+ }
+ }
+
+ (This never worked in previous versions).
+ *** POTENTIAL INCOMPATIBILITY ***. This will probably break
+ third party extensions to SWIG (or give them further encouragement
+ to join the SWIG CVS-tree :-).
+
+09/10/2003: mkoeppe (Matthias Koeppe)
+ [Guile] Fix memory leaks in the "list-vector.i" typemaps.
+
+09/09/2003: mkoeppe (Matthias Koeppe)
+ [Chicken] Use C_mk_bool rather than C_mkbool. This fixes
+ the wrapping of boolean values for Chicken 1.10 and newer.
+ Reported by Dave <hundo@yahoo.com> / Felix Winkelmann
+ <felix@proxima-mt.de>.
+
+09/05/2003: cheetah (William Fulton)
+ [Java] Directors implemented for Java. In summary this is a big new feature
+ which supports upcalls from C++ to Java. Code is generated to support C++
+ callbacks to call into Java and true polymorphic behaviour for Java classes
+ derived from C++ classes. See java.html for details. Contributed by
+ Scott Michel.
+
+09/05/2003: Tiger
+ Created contract example directory at /SWIG/Examples/contract
+ Added simple contract examples (simple_c & simple_cxx)
+ Modified contract module's output format
+
+ *** NEW FEATURE ***
+
+09/01/2003: cheetah (William Fulton)
+ Test-suite build improvements:
+ - Multiple build directories working for the test suite, so it is now
+ possible to run configure in multiple subdirectories and run the test
+ suite in each of these sub directories.
+ - 'make distclean' fixed so it doesn't bomb out on the Examples directory
+ when using multiple subdiretory builds. Required the following directories
+ to be moved:
+ Examples/GIFPlot/Perl -> Examples/GIFPlot/Perl5
+ Examples/GIFPlot/Php -> Examples/GIFPlot/Php4
+ These new directories used to be symbolic links to the old directory.
+ Also the Examples/test-suite/Perl symbolic link has been removed.
+ - Running the test-suite, other than from the root directory, say
+ in Examples/test-suite/python will now display all the code being
+ executed.
+ - The following 3 C# compilers are detected during configure and work with
+ the test-suite: Mono, Portable.NET and Microsoft.
+
+09/01/2003: Tiger
+ Added inheritance support for design by contract feature.
+
+09/01/2003: beazley
+ Fixed [ 794914 ] Wrong types in template specialization.
+ SWIG was not handling arguments correctly in template
+ partial specialization. For example,
+
+ template<class T> class Foo<T *> {
+ public:
+ T *blah();
+ };
+
+ %template(FooInt) Foo<int *>;
+
+ in this class, the return type of blah was set to
+ 'int **', but it should really be 'int *'. This has been
+ fixed, but it will affect all prior uses of partial
+ specialization.
+
+09/01/2003: beazley
+ Fixed [ 786394 ] Patch for generated perl code does not compile under RedHat9.
+ Reported by Scott Finneran.
+
+09/01/2003: beazley
+ Fixed [ 791579 ] (unsigned) long long handled incorrectly (Tcl).
+ This was an error in the Tcl typemaps.i file.
+ Reported by Kjell Wooding.
+
+09/01/2003: beazley
+ Fixed [ 797573 ] no way to rename classes coming from C structures.
+ This problem relates to renaming of anonymous structures with a
+ typedef. For example:
+
+ %rename(Bar) Foo;
+ typedef struct {
+ ...
+ } Foo;
+
+ Reported by Britton Leo Kerin.
+
+09/01/2003: beazley
+ Fixed [ 797576 ] -help seems to imply that only tcl-specific options exist.
+ Added a comment to alert user to other options.
+ Reported by Britton Leo Kerin.
+
+09/01/2003: beazley
+ Fixed [ 798205 ] Segfault in SWIG_ConvertPtr.
+ Reported by Prabhu Ramachandran.
+
+08/30/2003: mrose (Mark Rose)
+ Modified the director typemaps in python/std_complex.i to use the
+ new-style macro and conversion functions, which eliminated some
+ redundant code. Fixed a few bugs in these typemaps as well, although
+ more testing is needed.
+
+08/29/2003: mrose (Mark Rose)
+ Completed initial support for wrapping abstract classes with directors.
+ Constructor wrappers will be generated for abstract classes that have
+ directors, and instances of the director classes will be created regardless
+ of whether the proxy class has been subclassed in the target language.
+ No checks are made during construction to ensure that all pure virtual
+ methods are implemented in the target language. Instead, calls to
+ unimplemented methods will throw SWIG_DIRECTOR_PURE_VIRTUAL_EXCEPTION
+ exceptions in C++.
+
+ Integrated Prabhu Ramachandran's typemap patches, which provide director
+ typemap support for enums and std::size_t, and fix a couple bugs in the
+ director std::vector<> typemaps.
+
+08/29/2003: cheetah (William Fulton)
+ [C#] Implemented exception handling for throwing C# exceptions from C/C++ code.
+ A few delegate functions are available for calling which then throw the C#
+ exception. Use the SWIG_CSharpThrowException function from C/C++ typemaps.
+ See the generated wrapper code or csharphead.swg for all available exceptions.
+ Example:
+
+ SWIG_CSharpThrowException(SWIG_CSharpException, "exception description");
+
+ The 'throws' typemaps are also now implemented, so code is automatically
+ generated to convert any C++ exception into a C# System.Exception when the C++
+ method declares an exception specification such as:
+
+ int foo() throw(Bar);
+
+ Also any parameters that are references to a C++ class or a class passed by value
+ and are passed as a C# null will now throw a C# NullReferenceException.
+
+08/29/2003: cheetah (William Fulton)
+ [C#] Fix to match the calling convention of all pinvoke methods so that they
+ match the calling convention used by default in the C# 'static extern' declarations
+ (__stdcall is used on Windows).
+
+08/19/2003: cheetah (William Fulton)
+ [Java] Reworked std::string typemaps. Fixes a number of string in std namespace
+ problems. For example %template vector<string>. The templated class' get method
+ wasn't returning a Java String, but a SWIGTYPE_p_string. Reported
+ by Zach Baum.
+
+08/15/2003: beazley
+ Fixed [ 763522 ] 1.3.19 segfault in SwigType_add_pointer/DohInsertitem.
+ Related to problem with unnamed class handling in Perl module.
+
+08/15/2003: beazley
+ Fixed [ 763563 ] Missing indication of optional arguments.
+ Tcl module. Reported by Krzysztof Kozminski.
+
+08/15/2003: beazley
+ Fixed [ 787432 ] long param handled as int. Tcl module
+ now uses Tcl_GetLongFromObj to convert integer values.
+
+08/11/2003: beazley
+ Fixed [ 775989 ] numeric template parameters. There were
+ some errors in template expansion related to the use of
+ arrays where the array dimension was a template parameter.
+ It should work now. Reported by Bryan Green.
+
+08/10/2003: mrose (Mark Rose)
+ Added a director typemap (outv) for return by value and cleaned up up a few
+ of the commented director typemaps.
+
+08/10/2003: mrose (Mark Rose)
+ Fixed constructor generation for director classes to ignore private
+ constructors. Protected constructors are also ignored for now, pending
+ a solution to the problem of wrapping classes that only define protected
+ constructors.
+
+08/07/2003: cheetah (William Fulton)
+ New commandline option -outdir <dir> to specify where the language specific
+ files are to be generated. This is useful for target languages like Python,
+ Java etc which generate proxy files in the appropriate language.
+ This option does not apply to the C/C++ wrapper file.
+
+08/07/2003: cheetah (William Fulton)
+ On Windows the generated files (other than the _wrap.c or _wrap.cxx files)
+ were sometimes incorrectly being generated into the current directory unless
+ the input file used the Unix path separator. The Windows path separator
+ should now be used. Bug reported by Robert Davies.
+
+08/07/2003: beazley
+ Added array variable set typemap to Perl module.
+
+08/07/2003: beazley
+ Fixed [ 775677 ] Array init causes codegen bug..
+
+08/07/2003: beazley
+ Fixed [ 779062 ] Class"\n"::foo not supported. SWIG
+ should now correctly handle whitespace in between
+ namespace qualifiers. For example "A :: Foo :: Bar".
+
+07/31/2003: cheetah (William Fulton)
+ Fixes for parameters which are classes that are passed by value and have
+ a default value. A copy constructor for SwigValueWrapper is required
+ (SF #780056). Also fixed memory leak in these circumstances. These mods
+ also fix SF #780054.
+
+07/28/2003: beazley
+ Improved run-time error message for pointers in Python module.
+ Contributed by Zooko.
+
+07/10/2003: ballabio (Luigi Ballabio)
+ [Almost all languages] Wrappers for std::pair added.
+ Typemaps for Python, Ruby, Guile and MzScheme.
+
+07/01/2003: mkoeppe (Matthias Koeppe)
+ [Chicken] Handle the case of more than one argout typemap
+ per function.
+
+06/29/2003: cheetah (William Fulton)
+ [Java, C#] SF #670949 request. The destructor wrapper function name is now
+ configurable. A new attribute called methodname in the
+ javadestruct/javadestruct_derived (Java) or csdestruct/csdestruct_derived (C#)
+ typemaps specifies the method name. For example in Java the destructor is
+ wrapped by default with the delete method:
+
+ %typemap(javadestruct, methodname="delete") SWIGTYPE {...}
+
+06/27/2003: cheetah (William Fulton)
+ [Java, C#] The throws attribute for adding exception classes to the throws
+ clause also now works with the following typemaps:
+ newfree
+ javain, javaout (Java)
+ csin, csout (C#)
+
+ For example, the 'AnException' will be added to the throws clause in the
+ proxy function:
+
+ %typemap(javaout, throws="AnException") int {
+ int returnValue=$jnicall;
+ if (returnValue==0) throw new AnException("Value must not be zero");
+ return returnValue;
+ }
+
+06/25/2003: mrose (Mark Rose)
+ [Python] Director typemap marshalling checks for null pointers when
+ walking the parameter list instead of relying soley on the parameter
+ count. Cures a segfault that occurred for multiple argument inv typemaps.
+ Someone with more Swig experience should probably review this code.
+
+06/24/2003: mkoeppe (Matthias Koeppe)
+ [Chicken] Don't emit calls to "C_check_for_interrupt",
+ which may result in an endless loop. Patch by felix@proxima-mt.de.
+
+06/20/2003: cheetah (William Fulton)
+ [C#] Finalizers now use destructor syntax as the override which was used in
+ the Finalize method is not in the ECMA standards, spotted by the MS compiler.
+
+06/10/2003: cheetah (William Fulton)
+ [C#] A number of changes have been made to remove the Java naming
+ that was used in the C# module.
+
+ Typemap name changes:
+ jni -> ctype
+ jtype -> imtype
+ jstype -> cstype
+ javain -> csin
+ javaout -> csout
+ javainterfaces -> csinterfaces
+ javabase -> csbase
+ javaclassmodifiers -> csclassmodifiers
+ javacode -> cscode
+ javaimports -> csimports
+ javaptrconstructormodifiers -> csptrconstructormodifiers
+ javagetcptr -> csgetcptr
+ javafinalize -> csfinalize
+
+ Feature name changes:
+ javaconst -> csconst
+ javamethodmodifiers -> csmethodmodifiers
+
+ Pragma changes:
+ pragma(java) -> pragma(csharp)
+ jniclassbase -> imclassbase
+ jniclassclassmodifiers -> imclassclassmodifiers
+ jniclasscode -> imclasscode
+ jniclassimports -> imclassimports
+ jniclassinterfaces -> imclassinterfaces
+
+ Special variable name changes:
+ $javaclassname -> $csclassname
+ $javainput -> $csinput
+ $jnicall -> $imcall
+
+ This will break SWIG interface files that use these typemaps, features
+ and pragmas. Please update your code or use macros for backwards
+ compatibility.
+
+ *** POTENTIAL INCOMPATIBILITY FOR C# MODULE ***
+
+06/10/2003: mkoeppe (Matthias Koeppe)
+ [MzScheme] Applied MzScheme module updates contributed by
+ John Lenz <jelenz@students.wisc.edu>.
+
+ - Updated mzscheme to use SWIG's common runtime type
+ system from common.swg.
+
+ - The Lib/mzscheme directory has been reorganized to
+ standardize names across the language modules:
+ mzscheme.i was moved to mzscheme.swg, mzscheme.swg and
+ mzschemedec.swg have been removed, mzrun.swg (which
+ contains the runtime code) has been added.
+
+ - The swig_proxy structure was renamed to swig_mz_proxy.
+ swig_mz_proxy now contains a pointer to a swig_type_info
+ structure.
+
+ - Added varin and varout typemaps for SWIGTYPE [] and
+ SWIGTYPE &.
+
+ - Garbage collection by calling scheme_add_finalizer() has
+ been added.
+
+ *** NEW FEATURE [MzScheme] ***
+
+06/10/2003: cheetah (William Fulton)
+ [Java] New typemaps: javadestruct and javadestruct_derived
+ for the C++ destructor wrapper. The javadestruct version gets used by
+ classes at the top of an inheritance chain and the javadestruct_derived
+ version gets used by other classes.
+
+ [C#] cildispose and cildisposeoverride typemaps replaced by
+ csdestruct and csdestruct_derived typemaps. The delete()
+ method has been removed and its functionality put into these
+ typemaps designed for the Dispose() method.
+
+ - New typemaps csinterfaces and csinterfaces_derived replace
+ the javainterfaces typemap. Also fixes the peculiarity of all classes
+ in an inheritance chain individually deriving from the IDisposable
+ interface.
+
+ - New typemap csfinalize for finalizers. C++ destructors are now called
+ by garbage collector during finalization. Problem reported by
+ Andreas Schörk.
+
+06/10/2003: Tiger
+ Modified contract code for error message output.
+ Contract code can now print out simple error message.
+ Modified contract code to prepare for inheritance
+
+06/03/2003: mkoeppe
+ [Guile] Applied Guile module updates contributed by
+ John Lenz <jelenz@students.wisc.edu>.
+
+ - SWIG currently uses Guile's gh_ API, which is marked as
+ deprecated in Guile 1.6 and will be removed in Guile
+ 1.9. This change introduces a command-line flag "-scm"
+ which causes SWIG to generate wrappers that use Guile's
+ SCM API instead; this requires Guile >= 1.6.
+
+ - The Lib/guile directory has been reorganized to
+ standardize names across language modules: guiledec.swg
+ and guile.swg have been moved into guile_gh_run.swg,
+ guile.i has been moved to guile_gh.swg, guile_scm.swg
+ and guile_scm_run.swg which contain the SCM API stuff
+ have been added
+
+ - ghinterface.i, which contains the defines from the gh_
+ functions to the scm_functions has been added
+
+ - The API for dealing with pointer objects is now
+ SWIG_ConvertPtr, SWIG_MustGetPtr, SWIG_NewPointerObj.
+
+ - Added varin and varout typemaps for SWIGTYPE [] and SWIGTYPE &
+
+ - Garbage collection has been added.
+
+ *** NEW FEATURE [Guile] ***
+
+06/01/2003: cheetah (William Fulton)
+ Dimensionless arrays such as
+
+ int foo[] = {1, 2};
+ extern int bar[];
+
+ produce a warning that the variable is read-only. Depending on the target
+ language, this used to cause compile errors or generate a setter that
+ generated a runtime error. A setter cannot be automatically generated
+ because the array size cannot be determined by SWIG. A varin, globalin
+ or memberin typemap (depending on the target language) must be written
+ by the user.
+
+05/29/2003: beazley
+ Refinement to default typemap matching and arrays. When an
+ array is declared like this:
+
+ int foo[4];
+
+ The default typemap now resolves to
+
+ SWIGTYPE [ANY]
+
+ If no match is found for that, it then resolves to
+
+ SWIGTYPE []
+
+ If no array dimension is specified in the original declaration,
+ the SWIGTYPE [] is used right away.
+
+ Note: This change has been made to resolve problems related to
+ arrays with and without dimensions. For example, sometimes SWIG
+ was generating setter functions for array variables with no dimensions
+ (an error). Likewise, SWIG sometimes made arrays with dimensions
+ read-only (also an error). This fixes the arrays_global test
+ problem.
+
+05/28/2003: beazley
+ Fixed subtle type handling bug with references and pointers.
+ If you had functions like this:
+
+ typedef Foo Bar;
+
+ Foo *func1();
+ void func2(Bar &x);
+
+ Then func2() wouldn't accept objects returned by func1()
+ because of a type error. It should work now.
+ Reported by Brian Yang.
+
+05/21/2003: cheetah (William Fulton)
+ Fixes to some of the Visual C++ example project files which would not
+ work with spaces in the paths held in the environment variables used to
+ point to the target language's library / include directory.
+ SF bug #740769
+
+05/21/2003: songyanf (Tiger)
+ Added -contracts option.
+ First try of the idea of "Wrap by Contract":
+ build up realiable cross-language module by wrapping with SWIG.
+ Implemented basic assertion
+ (preassertion & postassertion & invariant)
+ for simple C/C++ functions.
+
+ Current format of contracts are:
+ %contract class_name :: func_name (paras...) {
+ require:
+ boolean exprs;
+ exprs;
+ ensure:
+ boolean expr;
+ exprs;
+ invariant:
+ boolean expr;
+ exprs;
+ }
+
+ *** NEW FEATURE ***
+
+05/19/2003: cheetah (William Fulton)
+ Build tweaks. There were a few preprocessor definitions which were
+ specified in the Makefile for passing on the commandline when compiling.
+ These are now all defined in swigconfig.h. Autoconf doesn't normally
+ allow installation directories to be defined in this config header file,
+ but an autoconf archive macro enables this. This macro along with future
+ autoconf macros are going to be put in the Tools/config directory.
+
+ 'swig -version' now reports the target build platform.
+
+05/11/2003: cheetah (William Fulton)
+ [C# and Java] Fix to the following typemaps:
+
+ javabase, javainterfaces, javaimports, javaclassmodifiers,
+ javaptrconstructormodifiers, javafinalize, javagetcptr & javacode.
+
+ These are the typemaps for modifying/generating proxy classes.
+ Previously the typemaps would use the proxy class name and not the
+ C++ type, which was inconsistent with all other typemaps.
+
+ In most circumstances the proxy class name and the C++ class name/type
+ is the same except for classes in namespace, templated classes etc. so
+ this shouldn't affect most cases.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA and C# MODULES ***
+
+05/09/2003: cheetah (William Fulton)
+ Visual C++ Project files have been added so that the runtime libraries
+ can be built on Windows (for Tcl, Perl, Python and Ruby).
+
+05/01/2003: beazley
+ Fixed problem with return by value, const, and private constructors.
+ For example:
+
+ class B {
+ private:
+ B();
+ public:
+ B(const B&);
+ };
+
+ class A {
+ ...
+ const B returnB() const;
+ ...
+ };
+
+ Problem and patch suggestion reported by Bill Hoffman.
+
+04/29/2003: cheetah (William Fulton)
+ Build changes:
+ - Single autoconf invocation - autoconf in the Tools directory has gone.
+
+ - Libtool bootstrapped when running autogen.sh. This requires anyone
+ using the cvs version of SWIG to have libtool installed on their
+ machine. Suggest version 1.4.2 or higher, preferably the latest - 1.5.
+
+ - Automake is now used to build the runtime libraries in conjunction
+ with libtool.
+
+ - Runtime libraries are now successfully built as DLLs on Cygwin.
+
+ - Skipping languages is no longer just determined in the top level
+ makefile but in configure.in. This info is used for building
+ the runtime libraries and for running the examples and test-suite.
+
+ - These changes have fixed multiple build directory builds, that is
+ building from directories other than the top level directory.
+ Installation from multiple build directories also working. An initial
+ configure in the top level directory is no longer needed as described
+ in 04/02/2003 entry. A 'make distclean' will be needed before building
+ in a directory other than the top level directory if the autotools
+ have been run from this top level directory at some point, but
+ autoconf will tell you this. Note that 'make check' only works from
+ the top level directory at the moment.
+
+04/28/2003: beazley
+ Fixed [ 723471 ] Wrapper_print() fails with preprocessor directives.
+
+04/28/2003: beazley
+ Minor refinement of const static member variable handling
+ described in CHANGES 08/11/2002. Previously, SWIG merely
+ checked to see if there was an initializer in the declaration.
+ Now, SWIG additionally checks to make sure the static member
+ is const.
+
+04/25/2003: ljohnson (Lyle Johnson)
+ [Ruby] Added a kind of limited support for multiple inheritance,
+ activated using the -minherit command-line option. I've also updated
+ the "C++ Inheritance" section of the Ruby documentation to discuss
+ how this works, and its limitations. Also also modified the minherit.i
+ test case to run against this.
+
+04/25/2003: ljohnson (Lyle Johnson)
+ [Ruby] Added the -globalmodule command-line option for the Ruby
+ module, for wrapping stuff into the global module (Kernel) instead
+ of a nested module. Updated documentation accordingly.
+
+04/23/2003: mrose (Mark Rose)
+ Fixed symname error in director calls to Python methods
+ that extend C++ operators.
+
+ Stopped director destructor wrappers from calling __set_up,
+ which was leaving the director flag in an inconsistent state.
+
+04/23/2003: beazley
+ Fixed problem with namespace resolution and nested namespaces.
+ Reported by Alfred Lorber (and Marcelo Matus).
+
+04/16/2003: cheetah (William Fulton)
+ Patch for Java examples and test-suite to run on Mac OS X.
+
+04/15/2003: ljohnson (Lyle Johnson)
+ [Ruby] Incorporated Nobu Nakada's patches for supporting the Ruby
+ 1.8 allocation framework.
+
+04/15/2003: ljohnson (Lyle Johnson)
+ [Ruby] Replaced all uses of the deprecated STR2CSTR() macro with the
+ safer StringValuePtr() macro. For more information, see ruby-talk:67059
+ and follow-ups to that post.
+
+04/11/2003: beazley
+ Fixed problem with preprocessor macro expansion. For example:
+
+ #define min(x,y) ((x) < (y)) ? (x) : (y)
+ int f(int min);
+
+ Reported by Sebastien Recio.
+
+04/10/2003: cheetah (William Fulton)
+ [Java] Added a runtime check to typemaps in arrays_java.i library to check
+ that the Java array passed in is the same size as the C array and throw an
+ exception if not.
+
+ Also fix to use delete instead of free for arrays created using new.
+
+04/07/2003: cheetah (William Fulton)
+ Remove GCC3 warning when compiling the examples and test-suite:
+
+ cc1plus: warning: changing search order for system directory "/usr/include"
+ cc1plus: warning: as it has already been specified as a non-system directory
+
+ See SF patch #715531 submitted by Gerald Williams
+
+04/03/2003: cheetah (William Fulton)
+ [C#] Improved wrapping of enums and constants. These were previously
+ wrapped as C# variables rather than constants. Either these are wrapped
+ as readonly (runtime) constants or compile time constants, depending on
+ the %javaconst directive (The directive is likely to change name soon).
+ For example wrapping:
+ %javaconst(0);
+ #define ABC 22
+ %javaconst(1) XYZ;
+ #define XYZ 33
+ is now:
+ public static readonly int ABC = examplePINVOKE.get_ABC();
+ public const int XYZ = 33;
+
+04/03/2003: cheetah (William Fulton)
+ [Java] Global constants and enums are put in their own interface called
+ xxxConstants, where xxx is the module name. This is an improvement as
+ it is possible to derive (implement) a Java class from the xxxConstants
+ interface to improve the syntax; namely when wrapping:
+ enum {ONE=1, TWO, THREE};
+ accessing these from a Java class implementing xxxConstants is neater:
+ int number = ONE;
+ than the previous:
+ int number = xxx.ONE;
+
+ Patch submitted by Dave Dribin.
+
+04/02/2003: cheetah (William Fulton)
+ Build improvements for multiple builds. This allows one to build
+ the SWIG executable and runtime libraries for different platforms/compilers
+ etc by running configure in different directories. This isn't 100% just
+ yet and won't be until libtool is better configured... a 'configure' and
+ 'make distclean' needs to be run in the root directory before it all works.
+ For example:
+ $ ./configure
+ $ make distclean
+ $ mkdir config1; cd config1; ../configure CC=gcc CXX=g++; make; cd ..
+ $ mkdir config2; cd config2; ../configure CC=cc CXX=c++; make; cd ..
+
+ To be improved. A 'make check' does not work yet either.
+
+04/01/2003: beazley
+ Fixed template partial specialization argument expansion bug.
+ This showed up when trying to use std_vector.i with vectors
+ of pointers.
+
+03/31/2003: cheetah (William Fulton)
+ Fix for parallel make builds of SWIG, for example
+ make -j 4
+ Build failure reported by Bill Clarke.
+
+03/28/2003: beazley
+ Released 1.3.19.
+
+
+
+Version 1.3.19 (March 28, 2003)
+===============================
+
+03/28/2003: beazley
+ Variety of minor bug fixes to the 1.3.18 release including:
+
+ - Segmentation fault with %extend directive.
+ - Typemap variable substitution bug.
+ - Expression evaluation bug.
+ - Large memory leak with template expansion.
+
+Version 1.3.18 (March 23, 2003)
+===============================
+
+03/21/2003: beazley
+ Fixed two problems with the %extend directive, overloading, and
+ template expansion. See the 'template_extend_overload' and
+ 'template_extend_overload_2' tests in Examples/test-suite for
+ details.
+
+03/20/2003: cheetah (William Fulton)
+ [C#] Added some typemaps as suggested by Andreas Schoerk for handling
+ parameters that are passed as pointers or by reference. These have
+ been put in typemaps.i.
+
+03/20/2003: beazley
+ Fixed a C++ scoping bug related to code like this:
+
+ class Foo {
+ public:
+ int Foo::bar();
+ };
+
+ Previously, SWIG just tossed out the Foo::bar() declaration. Now,
+ the declaration is wrapped provided that the prefix is exactly the
+ same as the current scope (including any enclosing namespaces).
+ Reported by Bruce Lowery.
+
+03/20/2003: beazley
+ Incorporated [ 696516 ] Enabling exception processing for data member access.
+ In some compilers, attribute access can generate exceptions. However,
+ SWIG ordinarily assumes that no exceptions will be raised. To disable this,
+ use the %feature("allowexcept"). For example:
+
+ %feature("allowexcept") Foo::x;
+ ...
+ class Foo {
+ public:
+ int x; /* Exception handling enabled */
+ ...
+ };
+
+ Patch contributed by Yakov Markovitch.
+
+03/20/2003: beazley
+ Incorporated Patch. [ 701860 ] Improve Performance (python proxies).
+ Gives a performance boost to proxy class code and the management of the
+ .this and .thisown attributes. Contributed by Mike Romberg.
+
+03/19/2003: cheetah (William Fulton)
+ [C# and Java] Added missing vararg support.
+
+03/18/2003: mrose (Mark Rose)
+ Removed code related to tagging individual methods for directors.
+ The concept of having directors for some but not all virtual methods
+ of a class is deeply flawed. The %feature("nodirector") tag is also
+ gone.
+
+ Directors are off by default. To enable them for a class, issue
+ %feature("director") classname; which will create director methods
+ for every virtual method in the hierarchy of the class.
+
+03/17/2003: beazley
+ Fixed a subtle problem with passing arguments of type function. For
+ example:
+
+ int foo(int x(int, int));
+
+ or
+
+ typedef int binop_t(int, int);
+ int foo(binop_t x);
+
+ In old versions, this would produce code that wouldn't compile. Now,
+ SWIG merely adds an extra pointer, making these declarations the same
+ as:
+
+ int foo(int (*x)(int, int));
+
+ typedef int binop_t(int, int);
+ int foo(binop_t *x);
+
+ Reported by Garth Bushell.
+
+03/17/2003: mrose (Mark Rose)
+ Fixed the return statement for director base class calls that have no
+ return value.
+
+03/15/2003: beazley
+ Fixed a problem with const smart-pointer wrapping. For example:
+
+ class Foo {
+ public:
+ int x;
+ void bar() const;
+ void spam();
+ };
+
+ class Blah {
+ ...
+ const Foo *operator->();
+ ...
+ };
+
+ In this case, only "x" and "bar" are visible from Blah (since application
+ of spam violates constness). Moreover, access to "x" is read-only.
+
+03/15/2003: mrose (Mark Rose)
+ Cleaned up two signed versus unsigned comparisons in python/std_vector.i.
+
+03/15/2003: cheetah (William Fulton)
+ [C#] Global variables are wrapped using properties instead of get and set methods.
+ Member variable wrapping bug fixes, for example wrapping pointers work now.
+ Typemaps are used for all variable wrapping to generate the property code.
+
+03/13/2003: mrose (Mark Rose)
+ Fixed a bug in the virtual method unrolling for directors.
+ The order of unrolling is now from base to derived, to ensure
+ that the most derived implementation of a director method is
+ found.
+
+ Director methods for pure virtual methods now throw
+ DIRECTOR_PURE_VIRTUAL_EXCEPTION if _up is set.
+
+03/12/2003: cheetah (William Fulton)
+ [C#] Polymorphism fix: virtual functions now use the appropriate
+ keyword in the C# proxy class, virtual or override.
+ Some 'using System;' statement fixes needed by the Mono compiler.
+
+03/11/2003: beazley
+ Fixed subtle bug in the application of SwigValueWrapper<> to
+ template classes with default constructors. Reported by
+ Bruce Lowery.
+
+03/11/2003: beazley
+ The $descriptor(type) variable is now expanded in code supplied to
+ %extend. This is useful for certain kinds of advanced wrapping
+ (especially container classes).
+
+03/11/2003: luigi
+ Support for std::map.
+ (a) Integration with scripting language (a la std::vector) for
+ Python, Ruby, MzScheme, and Guile;
+ (b) Simple wrapper for other languages
+
+03/10/2003: beazley
+ Fixed problem with escape sequences in string and character constants. SWIG
+ wasn't parsing certain octal codes correctly.
+
+03/07/2003: beazley
+ Fixed a variety of subtle preprocessor problems reported by
+ Sebastien Recio.
+
+ (a) Empty preprocessor values no longer generate "bad constant
+ value" errors. For example:
+
+ #define FOO
+ #define FOO BAR
+
+ (b) Macro names can now span multiple lines (technically valid,
+ although questionable practice). For example:
+
+ #define A_LONG_MACRO_\
+ NAME 42
+
+ (c) Whitespace is no longer required before certain macro values.
+ For example:
+
+ #define FOO"Hello"
+ #define BAR\
+ "Hello"
+
+03/07/2003: ljohnson (Lyle Johnson)
+ [Ruby] Added missing long long and unsigned long long typemaps
+ in the Lib/ruby/typemaps.i library file.
+
+03/07/2003: mrose (Mark Rose)
+ Added Examples/python/callback to demostrate how directors can
+ be used to implement callbacks in Python
+ Added Examples/python/extend to demonstrate virtual method
+ calls from C++ to Python (really the same as the callback
+ example, just a different context).
+ Added four tests for very basic director functionality. These
+ have runtime tests under python.
+ The Python module now emits #define SWIG_DIRECTORS near the
+ top of the output file if directors are enabled. This is useful
+ for disabling parts of tests in target languages that don't
+ support directors.
+
+03/06/2003: mrose (Mark Rose)
+ Added a section to Doc/Manual/Python.html on cross language
+ polymorphism (directors).
+
+03/06/2003: mrose (Mark Rose)
+ The short-lived "-fdirectors" command line option has been
+ removed. To enable directors, instead use the extended %module
+ directive as follows:
+
+ %module(directors="1") modulename
+
+03/06/2003: cheetah (William Fulton)
+ The long long typemaps have been rewritten so that they can be more
+ easily used with non ISO compilers, like Visual C++. For example
+ if you are wrapping the Windows 64 bit type __int64 the long long
+ typemaps can be used with %apply:
+
+ %apply long long { __int64 };
+ __int64 value1(__int64 x);
+
+ __int64 will now appear in the generated code instead of long long.
+
+03/06/2003: beazley
+ *** DEVELOPER CHANGE ***
+ Swig module mutation has been changed slightly. When a language
+ class method wants to save node attributes, it now uses one of the
+ following functions:
+
+ Swig_require()
+ Swig_save()
+
+ The first argument to these functions is a namespace in which
+ saved attributes are placed. For example,this code
+
+ Node *n;
+ Swig_save("cDeclaration",n,"type","parms","name",NIL);
+
+ saves the attributes as "cDeclaration:type", "cDeclaration:parms",
+ and so forth. If necessary, a language module can refer to
+ old values by using this special namespace qualifier.
+
+ In addition to this, a special attribute "view" contains the name
+ of the last namespace used to save attributes. In the above
+ example, "view" would have the value "cDeclaration". The value
+ of "cDeclaration:view" would have the previous view and so forth.
+
+ Swig_restore(n) restores a node to the state before the last
+ Swig_require() or Swig_save() call.
+
+ Note: This change makes it easier for language modules to refer
+ to old values of attributes.
+
+
+03/06/2003: mrose (Mark Rose)
+ Merged the cross-language polymorphism patch. When enabled, C++
+ "proxy" classes (called directors) are generated for each specified
+ C++ class. Directors pass method calls from C++ to Python, similar
+ to the way the usual proxy (shadow) classes pass method calls from
+ Python to C++. Together, these two types of proxies allow C++
+ classes that are extended in Python to behave just like ordinary
+ C++ classes and be used in C++ like native objects.
+
+ This feature is still very experimental and is disabled by default.
+ To enable director support, specify '-fdirectors' on the SWIG command
+ line or in the SWIG_FEATURES environment variable. In the interface
+ file, add %feature("director") to generate directors for all classes
+ that have virtual methods.
+
+ See http://stm.lbl.gov/~tm2/swig/ProxyDoc.html for more details.
+
+
+03/03/2003: beazley
+ Fixed a small glitch in typemap local variable replacement. If you had
+ a typemap like this:
+
+ %typemap(in) type ($1_type temp) {
+ ...
+ temp = ...;
+ ...
+ }
+
+ and no occurrence of "$1_type" appeared in the body, then the local
+ variable type wouldn't be substituted.
+
+03/03/2003: cheetah (William Fulton)
+ [C#] New version of the CSharp module which is typemap based.
+ It also uses ECMA C# and no longer uses Microsoft Visual C++.NET
+ glue. This means that it will work on non-Windows platforms.
+ Contributed by Neil Cawse.
+
+02/27/2003: beazley
+ Fixed [ 653548 ] error parsing casting operator definition.
+ SWIG now ignores casting operators declared outside of a class.
+ For example:
+
+ inline A::operator char *() { ... }
+
+ Bug reported by Martin Casado.
+
+02/27/2003: beazley
+ Added support for anonymous bit-fields. For example:
+
+ struct Foo {
+ int x : 4;
+ int : 4;
+ int y : 8;
+ };
+
+ Anonymous bit-fields are ignored by SWIG. Problem
+ reported by Franz Höpfinger.
+
+02/26/2003: cheetah (William Fulton)
+ [Java] Better typemaps in the Examples/java/typemap example and also
+ fixes subtle bug when using the StringBuffer typemaps more than once.
+
+02/26/2003: beazley
+ Fixed [ 642112 ] Constants char bug.
+
+02/26/2003: beazley
+ Fixed [ 675337 ] Partial template specialization not entirely working.
+ There was a subtle problem related to the naming and ordering of
+ template partial specialization arguments. Matching worked okay,
+ the resulting templates weren't expanded correctly.
+
+02/25/2003: beazley
+ Fixed problem with parsing (and generating code) for
+ references to arrays. For example:
+
+ int foo(int (&x)[10]);
+
+02/25/2003: beazley
+ Fixed [ 635347 ] Compilation warning from libpy.c.
+ Reported by Daniel L. Rall.
+
+02/25/2003: beazley
+ Fixed a subtle problem with virtual method implementation
+ checking and typedef.
+
+ typedef int *intptr;
+
+ struct A {
+ virtual int *foo() = 0;
+ };
+ struct B : public A {
+ virtual intptr foo() { };
+ };
+
+ SWIG was treating these declarations as different even though
+ they are the same (via typedef).
+
+02/25/2003: ljohnson (Lyle Johnson)
+ [Ruby] Added range checking for the NUM2USHRT macro, per [ 675353 ].
+
+02/24/2003: beazley
+ Fixed a subtle problem with the code that determined if a class is abstract
+ and can be instantiated. If you had classes like this:
+
+ struct A {
+ virtual int foo(int) = 0;
+ };
+ struct B : virtual A {
+ virtual int foo(int);
+ };
+
+ struct C : virtual A {
+ };
+
+ /* Note order of base classes */
+ struct D : B, C { }; /* Ok */
+ struct E : C, B { }; /* Broken */
+
+ then SWIG determined that it could instantiate D(), but not E().
+ This inconsistency arose from the depth-first search of the
+ inheritance hierarchy to locate the implementations of virtual
+ methods. This problem should now be fixed---SWIG will attempt
+ to locate any valid implementation of a virtual method by
+ traversing over the entire hierarchy.
+
+02/22/2003: cheetah (William Fulton)
+ [Java] Fix for using enum typemaps. The Java final static variable type
+ can be set using the jstype typemap, enabling enums to be mapped to
+ something other than int. Bug reported by Heiner Petith.
+
+02/21/2003: songyanf (Tiger)
+ Added CSharp (C#) module prototype
+ i.e. csharp.cxx & csharp.h at Source/Modules/.
+ They are for test usage only now and need improvement.
+ The interface also need to be modified.
+
+ *** NEW FEATURE ***
+
+02/20/2003: songyanf (Tiger)
+ Fixed problem with typedef with -fvirtual.
+ Similar as beazley's modification today.
+
+02/20/2003: beazley
+ Added support for gcc-style variadic preprocessor macros.
+ Patch [ 623258 ] GCC-style vararg macro support.
+ Contributed by Joe Mason.
+
+02/20/2003: beazley
+ Fixed [ 605162 ] Typemap local variables.
+ Reported by Lyle Johnson.
+
+02/20/2003: beazley
+ Fixed problem with abstract classes and typedef. For example:
+
+ class Foo {
+ public:
+ virtual void foo(int x) = 0;
+ };
+
+ typedef int Integer;
+ class Bar : public Foo {
+ public:
+ virtual void foo(Integer x);
+ };
+
+ SWIG was getting confused about the latter method---making Bar
+ abstract. Reported by Marcelo Matus.
+
+02/19/2003: cheetah (William Fulton)
+ [Java] %javaconst(flag) can also be used on enums as well as constants.
+ This feature enables true Java compiler constants so that they can be
+ used in Java switch statements. Thanks to Heiner Petith for patches.
+
+02/19/2003: songyanf (Tiger)
+ Modified -fcompact feature to deal with PP lines
+
+02/18/2003: beazley
+ Fixed [ 689040 ] Missing return value in std_vector.i.
+ Reported by Robert H. de Vries.
+
+02/18/2003: beazley
+ Fixed a few evil scoping problems with templates, namespaces, and the
+ %extend directive. Problem reported by Luigi Ballabio.
+
+
+02/18/2003: cheetah (William Fulton)
+ [Ruby] Improved support for Visual C++ and other native Windows compilers.
+ It is no longer necessary to specify "/EXPORT:Init_<module>", where <module> is the
+ swig module name when linking using these native Windows compilers.
+
+02/15/2003: songyanf (Tiger)
+ Added -fvirtual option.
+ Reduce the lines and size of the wrapper file
+ by omitting redifined virtual function in children classes.
+
+ Modified -compact option to -fcompact option
+
+ Added -small option.
+ -small = -fvirtual -fcompact
+ And it can be extended by future feature options,
+ which are used to reduce wrapper file szie.
+
+ Added SWIG_FEATURES environment variable check.
+ To dynamically set the feature options such as -fcompact & -fvirtual
+ *** NEW FEATURE ***
+
+02/13/2003: lenz
+ Updated Doc/Manual/Perl5.html to talk about C++ compile problems
+ configure.in now checks for PERL5_CCFLAGS
+ Runtime/Makefile.in and Example/Makefile.in now use PERL5_CCFLAGS
+ Added Lib/perl5/noembed.h which contains all the known macro conflicts
+
+02/12/2003: beazley
+ Fixed [ 685410 ] C++ Explicit template instantiation causes SWIG to exit.
+ Fixes a syntax error with declarations similar to this:
+
+ template class std::vector<int>;
+
+ SWIG now ignores the instantiation and generates a warning message.
+ We might do more later. Reported by Thomas Williamson.
+
+02/11/2003: cheetah (William Fulton)
+ Rewrote bool typemaps to remove performance warning for compiling generated code
+ under Visual C++.
+
+02/11/2003: cheetah (William Fulton)
+ Fix for wrapping reference variables (const non-primitive and all non-const types)
+ for example:
+ int& i;
+ Class& c;
+ const Class& c;
+
+02/11/2003: beazley
+ Fixed more very subtle preprocessor corner cases related to recursive
+ macro expansion. For example:
+
+ #define cat(x,y) x ## y
+
+ cat(cat(1,2),3) // Produces: cat(1,2)3
+
+ #define xcat(x,y) cat(x,y)
+
+ xcat(xcat(1,2),3) // Produces 123
+
+ See K&R, 2nd Ed. p. 231.
+
+02/10/2003: cheetah (William Fulton)
+ Fixed [ 683882 ] - patch submitted by F. Postma for SWIG to compile on HP-UX.
+
+02/10/2003: beazley
+ Fixed subtle preprocessor argument expansion bug. Reported by Marcelo Matus.
+
+02/10/2003: songyanf
+ Added -compact option.
+ Reduce the lines and size of the wrapper file
+ by omitting comments and combining short lines.
+ *** NEW FEATURE ***
+
+02/07/2003: beazley
+ Fixed [ 651355 ] Syntax error with cstring.i
+ Reported by Omri Barel.
+
+02/07/2003: beazley
+ Fixed [ 663632 ] incompatibility with standard cpp.
+ This is a refinement that fixes this problem:
+
+ // Some macro with an argument
+ #define FOO(x) x
+
+ int FOO; /* Not a macro---no arguments */
+
+02/05/2003: beazley
+ Fixed [ 675491 ] parse error with global namespace qualification.
+ Submitted by Jeremy Yallop.
+
+02/04/2003: beazley
+ Fixed bug in varargs processing introduced by the numinputs typemap parameter.
+
+01/08/2003: ttn
+ [xml] Fix string-replacement ordering buglet.
+ Thanks to Gary Herron.
+
+12/23/2002: cheetah (William Fulton)
+ Further build changes:
+ - The SWIG executable is now built using a single Makefile.
+ - This makefile is generated by Automake (Source/Makefile.am).
+ - Dependency tracking and tags support are in this makefile.
+ - Automake 1.7.2 and Autoconf 2.54 minimum versions are needed to build SWIG from CVS.
+ - Running ./autogen.sh now installs Autoconf/Automake support files into
+ Tools/config and these files are no longer stored in CVS.
+ - Bug fixes in 'make install' for systems using .exe executable extension and
+ ./configure --with-release-suffix=whatever
+
+12/16/2002: cheetah (William Fulton)
+ More build changes:
+ - Autoconf's AC_CANONICAL_HOST replaces proprietary approach for detecting build host.
+ - Autoconf support files moved to Tools/config.
+
+12/16/2002: cheetah (William Fulton)
+ Modifications to run on MacOS, submitted by Bernard Desgraupes.
+ Mainly ensuring generated files are output in the appropriate directory for
+ some modules.
+
+12/11/2002: cheetah (William Fulton)
+ Various build modifications and bug fixes:
+ - Simplification of version string. Use autoconf's PACKAGE_VERSION instead.
+ - Build time removed from SWIG version.
+ - Using standard autoconf config header generation.
+ - Updated old autoconf macros as reported by autoupdate.
+ - Removed $prefix in autoconf from search paths as autoconf won't expand them.
+ - Subtle bug fix where 'make prefix=/somewhere; make clean; make prefix=/somwhere/else'
+ produced an executable using the incorrect library directories.
+ - Added -ldflags commandline option for MzScheme, Ocaml, Pike and PHP.
+ - Fixed reporting of compiler used when using -version commandline option.
+ - SWIG web address added to -version commandline option.
+
+12/11/2002: beazley
+ Minor fix to Tcl dynamic cast typemaps. Reported by
+ Kristopher Blom.
+
+12/10/2002: beazley
+ Fixed subtle template argument replace bug. Reported by
+ Chris Flatley.
+
+12/10/2002: beazley
+ Reverted CHANGES 09/03/2002, preprocessor argument evaluation. Arguments
+ are not evaluated during collection, K&R, p. 230.
+
+12/06/2002: beazley
+ Fixed [ 649022 ] Compilation problems with KAI/KCC
+
+12/02/2002: beazley
+ SWIG 'rel-1-3' CVS branch merged back into the main branch.
+
+
+Version 1.3.17 (November 22, 2002)
+==================================
+
+11/19/2002: beazley
+ Fixed [ 613922 ] preprocessor errors with HAVE_LONG_LONG.
+
+11/19/2002: beazley
+ Fixed [ 615480 ] mzscheme SWIG_MustGetPtr_.
+
+11/19/2002: beazley
+ Fixed [ 635119 ] SWIG_croak causes compiler warning.
+
+11/16/2002: cheetah (William Fulton)
+ [Java] Added typemaps for pointers to class members.
+
+11/15/2002: cheetah (William Fulton)
+ [Java] Bug fix: Overloaded C++ functions which cannot be overloaded in Java
+ once again issue a warning.
+
+11/14/2002: cheetah (William Fulton)
+ [Java] Handling of NULL pointers is improved. A java null object will now
+ be translated to and from a NULL C/C++ pointer by default. Previously when
+ wrapping:
+
+ class SomeClass {...};
+ void foo(SomeClass *s);
+
+ and it was called from Java with null:
+
+ modulename.foo(null)
+
+ a Java NullPointerException was thrown. Extra typemaps had to be written in
+ order to obtain a NULL pointer to pass to functions like this one. Now the
+ default wrapping will detect 'null' and translate it into a NULL pointer.
+ Also if a function returns a NULL pointer, eg:
+
+ SomeClass *bar() { return NULL; }
+
+ Then this used to be wrapped with a SomeClass proxy class holding a NULL
+ pointer. Now null is returned instead. These changes are subtle but useful.
+ The original behaviour can be obtained by using the original typemaps:
+
+ %typemap(javaout) SWIGTYPE {
+ return new $&javaclassname($jnicall, true);
+ }
+ %typemap(javaout) SWIGTYPE *, SWIGTYPE &, SWIGTYPE [] {
+ return new $javaclassname($jnicall, $owner);
+ }
+ %typemap(javagetcptr) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE [] %{
+ protected static long getCPtr($javaclassname obj) {
+ return obj.swigCPtr;
+ }
+ %}
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+
+11/12/2002: beazley
+ Fixed problem with abstract methods and signatures. For example:
+
+ class abstract_foo {
+ public:
+ virtual int meth(int meth_param) = 0;
+ };
+
+
+ class abstract_bar : public abstract_foo {
+ public:
+ int meth(int meth_param_1, int meth_param_2) { return 0; }
+ };
+
+ In this case, abstract_bar is still abstract.
+
+ Fixes [ 628438 ] Derived abstract class not abstract.
+ Reported and patched by Scott Michel.
+
+11/11/2002: beazley
+ Fixed a matching problem with typemaps and array dimensions. For example, if you
+ had this:
+
+ typedef char blah[20];
+
+ and a typemap:
+
+ %typemap() char [ANY] {
+ ... $1_dim0 ...
+ }
+
+ then $1_dim* variables weren't be expanded properly. It should work now.
+ Problem reported by Pankaj Kumar Goel.
+
+11/07/2002: mkoeppe
+ Added an experimental new module that dumps SWIG's parse
+ tree as (Common) Lisp s-expressions. The module is
+ invoked with SWIG's -sexp command-line switch. The output
+ can be read into Common Lisp. There is (prototype)
+ example Lisp code that generates Foreign Function Interface
+ definitions for use with Kevin Rosenberg's UFFI.
+
+ *** EXPERIMENTAL NEW FEATURE ***
+
+11/07/2002: mkoeppe
+ Removed duplicate declaration of "cpp_template_decl" in
+ parser.y; bison 1.75 complained.
+
+11/06/2002: cheetah (William Fulton)
+ [Java] Default primitive array handling has changed like arrays of classes.
+ C primitive arrays are no longer wrapped by a Java array but with a pointer
+ (type wrapper class). Again the changes have been made for efficiency reasons.
+ The original typemaps have been moved into arrays_java.i, so the original
+ behaviour can be obtained merely including this file:
+
+ %include "arrays_java.i"
+
+ The array support functions are no longer generated by default. They are only
+ generated when including this file, thus this often unused code is only
+ generated when specifically requiring this type of array support.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+11/05/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added support for nested module declarations (as was
+ previously added for the Perl module). So a %module directive
+ of the form:
+
+ %module "Outer::Inner::Foo"
+
+ will nest everything as (in Ruby code):
+
+ module Outer
+ module Inner
+ module Foo
+ # stuff goes here
+ end
+ end
+ end
+
+11/05/2002: mkoeppe
+ [MzScheme] Add an argument (-declaremodule) that generates
+ code to correctly declare a primitive module extension.
+ Patch submitted by Bruce Butterfield.
+
+11/02/2002: cheetah (William Fulton)
+ [Java] Added patch submitted by Michael Cahill to remove unused parameter
+ warnings for the jenv and cls parameters. This patch also also allows one
+ to use "void" in the jni typemap for any type without code being generated
+ attempting to return a value.
+
+10/29/2002: cheetah (William Fulton)
+ [Java] Array handling is different. Arrays of classes are no longer wrapped
+ with proxy arrays, eg wrapping
+
+ class X {...};
+ X foo[10];
+
+ used to be wrapped with these Java getters and setters:
+
+ public static void setFoo(X[] value) {...}
+ public static X[] getFoo() {...}
+
+ This approach is very inefficient as the entire array is copied numerous
+ times on each invocation of the getter or setter. These arrays are now
+ wrapped with a pointer so it is only possible to access the first array element
+ using a proxy class:
+
+ public static void setFoo(X value) {...}
+ public static X getFoo() {...}
+
+ Arrays of enums have also been similarly changed. This behaviour is now like the
+ other SWIG language's implementation and the array library should be used to
+ access the other elements. The original behaviour can be achieved using the
+ macros and typemaps in arrays_java.i, for example:
+
+ %include "arrays_java.i"
+ JAVA_ARRAYSOFCLASSES(X)
+ class X {...};
+ X foo[10];
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+10/29/2002: cheetah (William Fulton)
+ [Java] Two new typemaps javain and javaout for generating the proxy class
+ and type wrapper class method calls to the JNI class. The new typemaps are
+ really used for transforming the jstype (used in proxy class and type wrapper
+ classes) to the jtype (used in the JNI class) and visa versa. A javain typemap
+ is required whenever an in typemap is written and similarly javaout for an out
+ typemap. An example is probably best to show them working:
+
+ %typemap(javain) Class "Class.getCPtr($javainput)"
+ %typemap(javain) unsigned short "$javainput"
+ %typemap(javaout) Class * {
+ return new Class($jnicall, $owner);
+ }
+
+ %inline %{
+ class Class {};
+ Class * bar(Class cls, unsigned short ush) { return new Class(); };
+ %}
+
+ The generated proxy code is then:
+
+ public static Class bar(Class cls, int ush) {
+ return new Class(exampleJNI.bar(Class.getCPtr(cls), ush), false);
+ }
+
+
+ Some new special variables have been introduced in order to use these typemaps.
+ Here $javainput has been replaced by 'cls' and 'ush'. $jnicall has been replaced by
+ the native method call, 'exampleJNI.bar(...)' and $owner has been replaced by 'false'.
+ $javainput is analogous to the $input special variable. It is replaced by the parameter name.
+ $jnicall is analogous to $action in %exception. It is replaced by the call to the native
+ method in the JNI class.
+ $owner is replaced by either true if %newobject has been used otherwise false.
+
+ The java.swg file contains default javain and javout typemaps which will produce the same code
+ as previously. This change is only of concern to those who have written their own typemaps as
+ you will then most likely have to write your own javain and javaout typemaps.
+
+ The javaout typemap also makes it possible to use a Java downcast to be used on abstract
+ proxy base classes. See the Java documentation on dynamic_cast.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+10/24/2002: ttn
+ [Methodology] Upgaded to libtool 1.4.3, presumably w/ better
+ support for newish platforms (like MacOS X).
+
+10/21/2002: ttn
+ Fixed Runtime/Makefile.in bug -- thanks to Richard Calmbach.
+
+10/18/2002: ttn
+ Fixed typo in doh.h -- thanks to Max Horn.
+
+Version 1.3.16 (October 14, 2002)
+=================================
+
+10/13/2002: beazley
+ Fixed bug with %extend directive and %feature reported
+ by William Fulton.
+
+10/13/2002: beazley
+ Added OpenVMS build directory (vms). Contributed by
+ Jean-François Pieronne.
+
+10/09/2002: cheetah (William Fulton)
+ [Java] Added throws clause to the native functions in the JNI class.
+ The throws clause is the same as the one generated for proxy functions
+ and module class functions.
+
+09/27/2002: beazley
+ Fixed some problems with the %import directive and classes that
+ were defined but not wrapped. Problem reported by Leslie Brooks,
+ Gerry Woods, and others.
+
+09/23/2002: cheetah (William Fulton)
+ [Java] Some error checking added:
+ 1) OutOfMemoryException check in the char * typemaps.
+ 2) As SWIG treats pointers, references and passing by value all the
+ same, it is possible to pass a NULL pointer to a function that expects
+ an object passed by value or by reference. A NullPointerException is
+ now thrown under this scenario.
+
+09/20/2002: ttn
+ [Methodology] Reworked "make clean" and "make install"
+ to be more table driven.
+ [Docs] Explain how to extend "make install" w/ extra-install.list.
+
+09/15/2002: beazley
+ Deprecation of the "ignore" typemap. The "ignore" typemap has
+ been deprecated in favor of a generalization of the "in" typemap.
+ To ignore an argument, use something like this instead:
+
+ %typemap(in,numinputs=0) int *output (int temp) {
+ $1 = &temp;
+ }
+
+ This change fixes a number of subtle bugs related to the interaction
+ of the "in" and "ignore" typemaps (which were supposed to be
+ mutually exclusive).
+
+ The use of the numinputs argument is reserved for future expansion.
+ Currently, values >1 will generate an error. However, future
+ releases of SWIG may utilize that to support multi-input typemaps.
+
+ %typemap(ignore) still works, but generates a warning message and is
+ translated to %typemap(in,numinputs=0).
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ *** NEW FEATURE ***
+
+09/15/2002: beazley
+ Fixed segmentation fault for unnamed structures. For example:
+
+ typedef struct {
+ } *blah;
+
+
+ Reported by Roger Gibson.
+ Note: we might be able to generate wrappers in special cases.
+
+09/13/2002: beazley
+ Minor modification to generated wrapper functions. Pointer arguments are now
+ always set to an initial value of 0. Simplifies typemap writing and cleanup
+ code (since you can rely on zero-value initialization). This also greatly
+ reduces the need to ever write an "arginit" typemap.
+
+09/12/2002: beazley
+ Minor enhancement to smart-pointer support. If operator->()
+ is part of an ignored base class like this,
+
+ %ignore Bar;
+
+ class Foo {
+ public:
+ int blah();
+ };
+
+ class Bar { /* Ignored */
+ public:
+ ...
+ Foo *operator->();
+ ...
+ };
+
+ class Spam : public Bar { };
+
+ then methods from Foo are still available. For example,
+
+ >>> s = Spam()
+ >>> s.blah()
+ 0
+ >>>
+
+ The only catch is that the operator->() itself is not available
+ (since it wasn't wrapped). Therefore, there won't be any
+ __deref__() operation unless it is explicitly added to Spam
+ (either using %extend or just placing operator->() in the
+ definition of Spam).
+
+09/11/2002: ttn
+ [Methodology] Reworked "make check" to be more table driven.
+ [Docs] Docuemented methodology in Manual/Extending.html.
+
+09/11/2002: ttn
+ [Docs] Prefixed Manual/*.html with "<!DOCTYPE html ...>" to
+ pander dotingly to (over-)sensitive editors.
+
+09/10/2002: ttn
+ [Guile] Converted Examples/guile/simple "make check"
+ behavior to actually check execution results. Reduced
+ iteration counts so that the test doesn't take too long.
+
+09/10/2002: beazley
+ SWIG-1.3.15 released.
+
+
+Version 1.3.15 (September 9, 2002)
+==================================
+
+09/09/2002: beazley
+ Fixed nasty runtime type checking bug with subtypes and inheritance
+ and templates.
+
+09/09/2002: cheetah (William Fulton)
+ [Java] Java exception classes for a method's throws clause can be generated by
+ specifying them in a comma separated list in the throws attribute in any one
+ of the following typemaps: in, out, check, freearg, argout and throws. A classic
+ example would be to convert C++ exceptions into a standard Java exception:
+
+ %typemap(throws, throws="java.io.IOException") file_exception {
+ jclass excep = jenv->FindClass("java/io/IOException");
+ if (excep)
+ jenv->ThrowNew(excep, $1.what());
+ return $null; // or use SWIG_fail
+ }
+
+ class file_exception {...};
+ void open(const char *filename) throw(file_exception);
+
+ The Java method will then be declared with a throws clause:
+
+ public static void open(String filename) throws java.io.IOException {...}
+
+09/08/2002: mkoeppe
+ * [Guile] Improved the documentation system. The arglist no
+ longer gets cluttered with type specification, making it
+ more readable. (Also the ILISP function C-u M-x
+ `arglist-lisp' RET works better this way.) The types of
+ arguments are explained in an extra sentence after the
+ arglist.
+
+ There are now two documentation-related typemap arguments:
+
+ %typemap(in, doc="$NAME is a vector of integers",
+ arglist="$name") int *VECTOR { ... }
+
+ The "arglist" texts of all arguments of a function make up
+ its arglist in the documentation. The "doc" texts of all
+ arguments are collected to make a sentence that describes
+ the types of the arguments. Reasonable defaults are
+ provided.
+
+ As usual, $name is substituted by the name of the
+ argument. The new typemap variable $NAME is like $name,
+ but marked-up as a variable. This means that it is
+ upper-cased; in TeXinfo mode ("-procdocformat texinfo") it
+ comes out as @var{name}.
+
+ The directives %values_as_list, %values_as_vector,
+ %multiple_values now also have an effect on the
+ documentation. (This is achieved via the new pragmas
+ return_nothing_doc, return_one_doc, return_multi_doc.)
+
+ Documentation has also improved for variables that are
+ wrapped as procedures-with-setters (command-line switch
+ "-emit-setters").
+
+ * [Guile] Emit constants as _immutable_ variables. (This
+ was broken recently.)
+
+09/07/2002: mkoeppe
+ [Guile] Updated the typemaps in list-vector.i.
+
+09/07/2002: mkoeppe
+ Short-circuit the typechecks for overloaded functions.
+ (The changes in code generation are visible in the new
+ testcase "overload_complicated".)
+
+09/06/2002: cheetah (William Fulton)
+ [Java] Solution for [ 596413 ]
+ New typemap so that the Java proxy classes and type wrapper classes
+ wrapper constructor modifier can be tailored by users. The default value is
+ protected. Normally SWIG generates a constructor like this which can only
+ be accessed within one package:
+
+ protected Bar(long cPtr, boolean cMemoryOwn) {
+ ...
+ }
+
+ If you are using SWIG across multiple packages or want to use this constructor
+ anyway, it can now be accessed outside the package. To modify use for example:
+
+ %typemap(javaptrconstructormodifiers) SWIGTYPE "public"
+
+ to change to public for all proxy classes and similarly for all type wrapper classes:
+
+ %typemap(javaptrconstructormodifiers) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE [] "public"
+
+09/06/2002: cheetah (William Fulton)
+ [Java] Added throws typemaps for the Java module. C++ exceptions get converted into
+ java.lang.RuntimeException Java exceptions.
+
+ Warning: This may change from java.lang.Runtime exception in the future.
+
+09/05/2002: cheetah (William Fulton)
+ [Java] Fix for variables declared as references.
+
+09/05/2002: beazley
+ Fixed [ 605162 ] Typemap local variables. Reported by Lyle Johnson.
+
+09/05/2002: ljohnson (Lyle Johnson)
+ [Ruby] More updates to the Ruby module documentation, including
+ a new typemap example that demonstrates how to collect key-value
+ pairs from an argument list into a Hash.
+
+09/05/2002: beazley
+ Fixed bug with template expansion and constructors.
+
+ template<class T> class Foo {
+ public:
+ Foo<T>() { }
+ };
+
+ The extra <T> in the constructor was carried through in the
+ name--causing runtime problems in generated modules.
+ Reported by Jordi Arnabat Benedicto.
+
+09/05/2002: mkoeppe
+ [Guile] Support overloading.
+
+09/04/2002: ljohnson (Lyle Johnson)
+ [Ruby] Updated typemaps for long long and unsigned long long types
+ to use Ruby 1.7 support for these types when available.
+
+09/04/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added output typemaps for const reference to primitive
+ types.
+
+09/04/2002: mkoeppe
+ [Guile] Fix pass-by-value typemaps. Reported by Arno
+ Peters via Debian bugtracking (#156902), patch by Torsten
+ Landschoff <torsten@debian.org>.
+
+09/03/2002: samjam (Sam Liddicott)
+ Better reference support.
+ Functions that want a void** can take a NULL by reference and
+ the void* will be made for you and then passed-by-reference
+
+ Also all integer-class native types can be passed by reference
+ where an int* or int& etc is needed
+
+09/03/2002: beazley
+ Changed the evaluation order of preprocessor macro arguments.
+ Arguments are now expanded by the preprocessor *before* they
+ are passed to macro expansion. This fixes a subtle expansion
+ bug reported by Anthony Heading.
+
+09/03/2002: beazley
+ Fixed the file include order (again, apparently). See 2/27/99.
+
+09/02/2002: beazley
+ [Perl] Better exception handling support. Since Perl error handling
+ relies on setjmp/longjmp, wrapper functions have been modified slightly
+ to provide an extra block scope:
+
+ XS(foo) {
+ char _swigmsg[SWIG_MAX_ERRMSG] = "";
+ const char *_swigerr = _swigmsg;
+ {
+ /* Normal wrapper function here */
+ ...
+ SWIG_croak("An error occurred\n");
+ ...
+ XSRETURN(argvi); /* Successful return */
+ fail:
+ /* cleanup code */
+ }
+ croak(_swig_err);
+ }
+
+ The macro SWIG_croak(x) sets the value of _swigerr to x and
+ executes a "goto fail". The whole wrapper function is enclosed
+ block scope to provide proper cleanup of C++ objects. Since
+ croak executes a longjmp(), there is no way to properly reclaim
+ resources if this executes in the same scope as the wrapper
+ function.
+
+ The _swigmsg[] variable is normally unused, but can be used
+ to store small error messages using sprintf or snprintf. It
+ has a capacity of at least 256 bytes (SWIG_MAX_ERRMSG).
+
+09/02/2002: beazley
+ [Tcl] Added better support for exceptions. Instead of returning TCL_ERROR,
+ use the macro SWIG_fail to return with an error. This ensures that
+ arguments are properly cleaned up. Exception specifiers are now
+ handled by default.
+
+09/02/2002: ljohnson (Lyle Johnson)
+ [Ruby] The type-checking system for the Ruby module has had a flaw
+ in that some types which should be considered equivalent
+ weren't. This bug was best demonstrated by the inherit_missing.i
+ test suite case, which defines a base class "Foo" that is
+ subclassed by "Bar". The "Foo" class isn't actually wrapped (i.e.
+ it's not directly accessible from Ruby) but we'd still like to be
+ able to pass "Bar" instances to functions expecting Foos and have
+ that work; it wasn't. The revised implementation (similar to that
+ used for some other language modules) adds a new instance variable
+ (__swigtype__) to each object that indicates its SWIG type;
+ that is, each "Bar" instance will now have a string instance
+ variable called "__swigtype__" whose value is "_p_Bar".
+
+ Unless developers were taking advantage of this low-level
+ implementation detail, they shouldn't notice any compatibility
+ problems; nevertheless, I'm marking it as a "potential
+ incompatibility".
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+09/01/2002: ljohnson (Lyle Johnson)
+ [Ruby] Fixed SF Bug #603199.
+
+08/08/2002: cheetah (William Fulton)
+ [Java] Added OUTPUT, INPUT and INOUT typemaps in typemaps.i for C++
+ references.
+
+08/27/2002: mkoeppe
+ [Guile] Fixed error in "lib_std_vector" testcase and
+ compiler warning in "lib_cdata" testcase.
+
+08/27/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added the "%mixin" directive, which allows the user to
+ specify a comma-separated list of module names to mix-in to a
+ class. So, for example, if you'd like to specify that Ruby's
+ Enumerable module should be mixed-in to your class Foo, you'd
+ write:
+
+ %mixin Foo "Enumerable";
+
+ or to specify that the modules Fee, Fie and Fo should be mixed
+ in to Foo:
+
+ %mixin Foo "Fee,Fie,Fo";
+
+ *** NEW FEATURE ***
+
+08/27/2002: ljohnson (Lyle Johnson)
+ [Ruby] Modified the %alias directive so that multiple aliases
+ can be specified for an instance method by using a comma-separated
+ list of aliases.
+
+08/27/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added "throw" typemaps for the Ruby module.
+
+08/26/2002: beazley
+ Two new command line options for printing dependencies.
+ 'swig -M' lists all file dependencies. 'swig -MM' lists
+ dependencies, but excludes files in the SWIG library.
+ Example:
+
+ % swig -M -python example.i
+ example_wrap.cxx: \
+ /u0/beazley/Projects/lib/swig1.3/swig.swg \
+ /u0/beazley/Projects/lib/swig1.3/python/python.swg \
+ example.i \
+ example.h
+
+ % swig -MM -python example.i
+ example_wrap.cxx: \
+ example.i \
+ example.h
+
+ *** NEW FEATURE ***
+
+08/26/2002: beazley
+ Fixed [ 597599 ] union in class: incorrect scope.
+ Reported by Art Yerkes.
+
+08/26/2002: beazley
+ Fixed [ 600132 ] Default argument with namespace.
+ Reported by Shibukawa Yoshiki.
+
+08/24/2002: beazley
+ Automatic C++ exception handling enabled for all language modules. This is
+ pretty simple. If you have a class like this:
+
+ class Foo {
+ };
+ class Bar {
+ public:
+ void blah() throw(Foo);
+ }
+
+ then the generated wrapper code looks like this:
+
+ wrap_Bar_blah() {
+ ...
+ try {
+ arg1->blah();
+ }
+ catch (Foo &_e) {
+ /* "throw" typemap code inserted. $1 = _e */
+ }
+ catch (...) {
+ throw;
+ }
+ }
+ The "throw" typemap can be used to raise an error in the target
+ language. It can do anything. Here is a very simple example:
+
+ %typemap("throw") Foo {
+ PyErr_SetString(PyExc_RuntimeError, "Foo exception");
+ return NULL;
+ }
+
+ To make this work in each language module, simply define a few default
+ "throw" typemaps for SWIGTYPE, SWIGTYPE *, int, const char *, and a
+ few common exception types. That's all there is to it.
+
+ Automatic exception handling can be disabled using -noexcept or
+ setting the NoExcept global variable to 1.
+ *** NEW FEATURE ***
+
+08/23/2002: beazley
+ [ Python ]
+ Automatic translation of C++ exception specifications into error handling code.
+ For example:
+
+ class Foo {
+ };
+ class Bar {
+ public:
+ void blah() throw(Foo);
+ }
+
+ In this case, Foo is wrapped as a classic-style class (compatible
+ with exception handling). Furthermore, you can write Python code
+ like this:
+
+ b = Bar()
+ try:
+ b.blah();
+ except Foo,e: # Note use of exception class here!
+ # Handle Foo error
+ ...
+
+ The object "e" in the exception handler is just a wrapped Foo
+ object. Access it like a normal object.
+
+ If an exception is not wrapped as a class, a RuntimeError
+ exception is raised. The argument to this exception is
+ the exception object. For example:
+
+ class Bar {
+ public:
+ void blah() throw(int);
+ }
+
+ b = Bar()
+ try:
+ b.blah();
+ except RuntimeError,e:
+ print e.args[0] # Integer exception value
+
+ Comments:
+
+ - If a class is used as an exception, it *must* be wrapped
+ as a Python classic-style class (new classes don't work).
+
+ - Automatic exception handling is compatible with %exception.
+
+ - Use -noexcept to turn off this feature.
+
+ - The newly introduced "throw" typemap is used to raise
+ Python errors (naturally).
+
+ *** EXPERIMENTAL NEW FEATURE ***
+
+08/23/2002: beazley
+ Information from throw() specifiers is now stored in the parse
+ tree. For example:
+
+ class Foo {
+ public:
+ int blah() throw(spam,bar);
+ }
+
+ The stored information is fully corrected for namespaces and works
+ with templates. Uses will follow.
+
+08/22/2002: beazley
+ Exception handling code is no longer applied to member access
+ function. For example, in this code
+
+ %exception {
+ try {
+ $action
+ } catch(whatever) {
+ ...
+ }
+ }
+
+ class Foo {
+ public:
+ int x;
+ ...
+ }
+
+ The exception handling code is not applied to accessor functions
+ for Foo::x. This should reduce the amount of extra code
+ generated.
+
+ Caveat: Exception handling code *is* used when attributes are
+ accessed through a smart-pointer or a synthesized attributed
+ added with %extend is used.
+
+08/22/2002: beazley
+ Made more patches to hopefully eliminate problems when compiling SWIG
+ as a 64-bit executable.
+
+08/22/2002: beazley
+ Fixed a bug with const reference members, variables, and static members.
+ For example:
+
+ class Foo {
+ public:
+ static const int &ref;
+ };
+
+ SWIG was trying to generate "set" functions which wouldn't compile.
+
+08/21/2002: beazley
+ Made the warning message for "Class X might abstract" off by default.
+ Enable with -Wall.
+
+08/21/2002: beazley
+ Refined handling of const and non-const overloaded methods. If
+ a class defines a method like this:
+
+ class Foo {
+ public:
+ int bar(int);
+ int bar(int) const;
+ }
+
+ Then the non-const method is *always* selected in overloading and
+ the const method silently discarded. If running with -Wall, a warning
+ message will be generated.
+
+08/19/2002: beazley
+ Better support for using declarations and inheritance. Consider this:
+
+ class Foo {
+ public:
+ int blah(int x);
+ };
+
+ class Bar {
+ public:
+ double blah(double x);
+ };
+
+ class FooBar : public Foo, public Bar {
+ public:
+ char *blah(char *x);
+ using Foo::blah;
+ using Bar::blah;
+ };
+
+ Now SWIG wraps FooBar::blah as an overloaded method that uses all
+ accessible versions of blah(). See section 15.2.2 in Stroustrup, 3rd Ed.
+
+ SWIG also supports access change through using declarations. For example:
+
+ class Foo {
+ protected:
+ int x;
+ int blah(int x);
+ };
+
+ class Bar : public Foo {
+ public:
+ using Foo::x;
+ using Foo::blah;
+ };
+
+
+ Caveat: SWIG does not actually check to see if declarations imported
+ via 'using' are in the inheritance hierarchy. If this occurs, the
+ wrapper code won't compile anyways---not sure it's worth worrying about.
+
+08/18/2002: beazley
+ Modified overloading dispatch to not include nodes with an "error" attribute.
+ A language module can set this if a node couldn't be wrapped and you don't want
+ it included in the dispatch function.
+
+08/18/2002: beazley
+ Enhancement to overloaded function dispatch. The dispatcher is now aware of
+ inheritance relationships. For example:
+
+ class Foo { };
+ class Bar : public Foo { };
+
+ void spam(Foo *f);
+ void spam(Bar *b);
+
+ In this case, the dispatcher re-orders the functions so that spam(Bar *b) is
+ checked first---it is more specific than spam(Foo *f).
+
+08/17/2002: beazley
+ Added -Werror command line option. If supplied, warning messages are treated
+ as errors and SWIG will return a non-zero exit code.
+
+08/17/2002: beazley
+ Fixed [ 596135 ] Typedef of reference can't compile. For example:
+
+ typedef int &IntRef;
+ void foo(IntRef i);
+
+ SWIG-1.3.14 generated code that wouldn't compile.
+
+Version 1.3.14 (August 12, 2002)
+================================
+
+08/11/2002: mmatus
+ Static const members initialized during declaration, and
+ only them, ie:
+
+ struct A
+ {
+ static const int a = 1 ; // this one
+ static const int b; // not this one
+ };
+
+ are emitted like constants (equivalent to enums or
+ explicit %constant).
+
+ This is because they cannot be added directly to 'cvar'
+ since they lack the needed reference (well, you can force
+ them to have a real reference, but in an ugly way which
+ goes completely again the original purpose of initialize
+ them during declaration, you also have to deal with extra
+ linking matters, and it take a while to figure out what is
+ the problem and how to solve it).
+
+ Please test it with your preferred target language, and
+ not only the code generation, but really run the example
+ in the test-suite (static-const-member-2.i) because the
+ problem and the solution cannot be "fully" appreciated
+ until you try to load the module and run it.
+
+ In some target languages (python specially), this can
+ produces a difference in the way that the static constant
+ members 'a' and 'b' are internally wrapped. Hopefully,
+ they still can be accessed in the same way.
+
+
+08/11/2002: mmatus
+ [python] Now static const members can be accessed in a more
+ natural way, ie, if you have
+
+ struct A
+ {
+ typedef unsigned int viewflags;
+ static const viewflags forward_field = 0;
+ static const viewflags backward_field;
+ };
+
+ now you can do:
+
+ print A.backward_field
+
+ and also
+
+ a = A()
+ print a.forward_field
+
+ Note that if the static const members don't have an
+ initializer (like backward_field), still you can access
+ them in the same way in the python side, but the
+ implementation is a quite different: backward_field will
+ still appear in the cvar entity, and also, you are
+ responsible to initialize it in some code unit, and link it
+ properly. forward_field, by the other hand, will not
+ appear in the cvar entity but only as a A member, similar
+ to what happen with enum or %constant members.
+
+08/11/2002: mmatus
+ [python] Common code in the __setattr__/__getattr__ now
+ goes to two "free" methods at the beginning of the proxy
+ file, from where each class use it. This change reduces
+ the size of the proxy file, specially if you wrap a lot of
+ small classes in one module (up to 33% in some cases),
+ making it faster to load too.
+
+08/09/2002: beazley
+ [Perl5] If a function that returns char * returns NULL,
+ undef is returned to the Perl interpreter.
+
+08/09/2002: beazley
+ Fix to conversion operators and namespaces. For example:
+
+ namespace ns {
+ struct Foo { };
+ struct Bar {
+ operator Foo*();
+ };
+ }
+
+ In the wrapper code, SWIG was using ->operator Foo*()
+ when it should have been using ->operator ns::Foo*().
+
+ Note: if using %rename with a conversion operator, you
+ might have to do this:
+
+ %rename(toFooPtr) ns::operator ns::Foo*();
+ // ^^^^ note extra qualifier
+ namespace ns {
+ ...
+
+
+08/09/2002: beazley
+ [Python] Minor enhancement to 'const' variable declarations.
+ Normally const declarations are wrapped as read-only variables
+ accessible only through the cvar attribute (see SWIG.html for
+ a discussion of why). However, in many programs, "const"
+ declarations may just be constants---making the cvar. access
+ awkward. To fix this, "const" declarations are now available
+ both through cvar. and as a simple name. For example:
+
+ const int FOO = 42;
+
+ In Python:
+
+ >>> print example.cvar.FOO
+ 42
+ >>> print example.FOO
+ 42
+
+ Note: There are cases where the value of a "const" variable
+ might change. For example:
+
+ char *const BAR = "Hello World";
+
+ In this case, the pointer itself can not change, but the
+ data being pointed to could be modified. In these situations,
+ cvar.BAR should be accessed to obtained the current value.
+
+08/08/2002: beazley
+ [Python] Fixed generation of the proxy code (.py files) to more
+ closely follow the order of declarations as they appear in
+ the .i file. In the past, all of the class wrappers appeared
+ first, followed by function stubs, inserted Python code, and
+ other details.
+
+08/08/2002: cheetah (William Fulton)
+ [Java] Proxy method _delete() changed to delete(). There shouldn't ever
+ be a wrapped function called delete() as it is a C++ keyword and there
+ is no such thing as a member function in C.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+ Backwards compatibility can be achieved by adding the function back in
+ for all proxy classes:
+ %typemap(javacode) SWIGTYPE %{
+ public void _delete() {
+ delete();
+ }
+ %}
+
+ Java backwards compatibility summary
+ ------------------------------------
+
+ There are a number of changes that have been made in improving the Java module
+ for ver 1.3.14. If at all possible change your code to take advantages of the
+ improvements. If you were using proxy classes you may not notice any backwards
+ compatibility issues. Here is an example which will help with most backwards
+ compatibility problems where it is not possible to modify the code that uses
+ the generated output:
+
+ Replace:
+ %module modulename
+
+ With:
+ %module (jniclassname="modulename") modulename;
+ %typemap(javacode) SWIGTYPE %{
+ public long getCPtr$javaclassname() {
+ return swigCPtr;
+ }
+ public void _delete() {
+ delete();
+ }
+ %}
+ %pragma(java) jniclassclassmodifiers="public";
+
+ The proxy constructors that took parameters (long cPtr, boolean cMemoryOwn)
+ were public and are now protected. If you were making use of these then you'll
+ have to modify your code and the best solution would be to use the new type
+ wrapper classes.
+
+ The other main areas are the pragmas and global variable wrapping. Replace
+ the pragmas with one of the new directives or typemaps mentioned below and use
+ %rename on the variables.
+
+ If you were not using proxy classes, you will have to define a jstype typemap
+ as well as a jtype typemap.
+
+08/08/2002: cheetah (William Fulton)
+ [Java] Fix for wrapping two dimension array variables.
+
+08/07/2002: beazley
+ [Python,Tcl]
+ Object management now has a much better sense of ownership.
+ Ownership bits is changed whenever an object is stored in a
+ global variable or structure member. For example:
+
+ struct Foo {
+ int val;
+ Foo *next;
+ };
+
+ Now in Python
+
+ >>> f = Foo()
+ >>> f.thisown
+ 1
+ >>> g = Foo()
+ >>> g.next = f # Assign a pointer
+ >>> f.thisown # Notice ownership change
+ 0
+ >>>
+
+ This scheme is mostly a conservative heuristic designed to
+ provide segmentation faults. It could cause a memory leak
+ if ownership is changed unnecessarily. In this case, you can
+ either write a typemap (that doesn't change ownership), or
+ manually set the thisown attribute back to 1.
+
+08/07/2002: beazley
+ [Tcl] Major usability improvements to the object interface.
+ Suppose you had code like this:
+
+ struct Foo {
+ int x;
+ int spam();
+ };
+
+ void blah(Foo *f);
+
+ In past versions of SWIG, you could create objects and use
+ them like this:
+
+ % Foo f
+ % f configure -x 3
+ % f spam
+ 37
+
+ The only problem is that if you tried to call blah(), it didn't
+ work:
+
+ % blah f
+ Type Error. Expected _p_Foo
+ %
+
+ Instead, you had to do this:
+
+ % blah [f cget -this]
+
+ SWIG now automatically extracts the -this pointer, avoiding this
+ problem. This means that saying "blah f" is perfectly legal and
+ everything will still work normally.
+
+ Caveat: Since pointer strings start with a leading underscore (_),
+ don't use this in object names. For example:
+
+ % Foo _f
+ % blah _f # Potential crash
+
+ Objects now have a -thisown attribute that shows the ownership.
+ This builds upon the CHANGES 11/24/2001 entry.
+
+08/07/2002: samjam, Sam Liddicott
+ Properly implemented pointer system using php resources.
+ Still need to work out whether or not to let script-users call
+ destructors directly
+
+08/06/2002: beazley
+ Upgraded mzscheme module to support version 201 and added
+ overloading support.
+
+08/05/2002: beazley
+ Added parsing support for extra grouping (in very limited cases).
+ For example:
+
+ typedef int (FuncPtr)(int, double);
+
+ *** EXPERIMENTAL ***
+
+08/03/2002: ljohnson (Lyle Johnson)
+ [Ruby] Updates to typemaps.i as those done previously for Perl,
+ Python and Tcl modules. Now supports reference types with INPUT,
+ OUTPUT and INOUT typemaps.
+
+08/02/2002: beazley
+ New library file cstring.i added. Provides macros for
+ manipulating char * data.
+
+08/02/2002: beazley
+ Deprecated the %new directive. Use %newobject instead. For
+ example:
+
+ %newobject foo;
+ ...
+ char *foo();
+
+ %newobject follows the same rules as %rename, %ignore, %feature,
+ etc.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+08/01/2002: cheetah (William Fulton)
+ [Java] New attribute 'jniclassname' for the module directive allows a way of
+ changing the JNI class name from the default which uses the modulename with JNI
+ appended after it.
+
+ %module (jniclassname="name") modulename
+
+ If 'name' is the same as 'modulename' then the module class name gets changed
+ from 'modulename' to modulenameModule.
+
+08/01/2002: beazley
+ Fixed problem with file include order. Language specific
+ directories should take precedence over generic directories.
+ For example: "swig_lib/python/foo.i" should be loaded before
+ "swig_lib/foo.i". I thought this was the case already, but
+ apparently it has been broken for quite some time.
+
+08/01/2002: beazley
+ Added std_deque.i library file. Work in progress.
+
+08/01/2002: beazley
+ [Python,Tcl,Perl]
+ Improvements to typemaps.i. INPUT/INOUT typemaps perform better
+ error checking. Typemaps are now supplied for references like
+ int &OUTPUT, double &INOUT, etc.
+
+08/01/2002: beazley
+ [Python] Deprecated the T_* and L_* typemaps in typemaps.i.
+ Multiple return values are always placed in a tuple. Deprecated
+ the BOTH typemaps. This is now INOUT (e.g., int *INOUT).
+
+ *** POTENTIAL INCOMPATIBILITY FOR PYTHON MODULE ***
+
+08/01/2002: beazley
+ Deprecated the array.i, carray.i, and timer.i library files.
+
+08/01/2002: beazley
+ Deprecated the pointer.i library file. Use cpointer.i instead.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+08/01/2002: cheetah (William Fulton)
+ [Java] For consistency the global variable getters and setters use the JavaBean
+ property design pattern like member variables always have. This means if you are
+ wrapping a variable called foo, the getter is called getFoo() and the setter is
+ called setFoo(). Before the recent changes to the Java module the getters and
+ setters were called get_foo() and set_foo(). If you really want the original
+ function names use the %rename directive like this: %rename(_foo) Foo;
+
+07/31/2002: beazley
+ Fixed casting problem with multiple inheritance. If you had this,
+
+ class foo {};
+ class bar : public foo {};
+ class baz : public foo {};
+ class spam : public bar, public baz {};
+
+ then the wrappers wouldn't compile due to an ambiguous cast.
+ Reported by Art Yerkes.
+
+07/30/2002: cheetah (William Fulton)
+ [Java] Due to new static typechecking all pointers held in a Java long are part of
+ the internal workings and this pointer value in the Java long has become abstracted
+ data. The type wrapper constructor and getCPtr() methods are as such protected.
+ If you need to mess around with pointers from Java or for example create a proxy
+ class or type wrapper class around a null pointer, add a function/constructor
+ to do so with the %javacode typemap. You can also make getCPtr() public again with
+ the %javagetcptr typemap.
+
+07/30/2002: cheetah (William Fulton)
+ [Java] Fixes for %typemap(ignore). In particular when ignoring the last parameter
+ in a function. Also for all parameters in constructors. These mods have also fixed
+ multi-argument typemaps for proxy classes - SF 581791.
+
+07/30/2002: cheetah (William Fulton)
+ [Java] %newobject (replacement for %new) now implemented for Java.
+
+07/29/2002: beazley
+ Fixed problem with typemap copies, %apply, and %clear inside
+ C++ namespaces.
+
+07/28/2002: cheetah (William Fulton)
+ [Java] The JNI class now has package access as the class modifier
+ has been changed from "public" to nothing. This has been done
+ as this class is now more for the internal workings of SWIG since the module
+ class has static type checking for all types.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+ Backwards compatibility can be achieved by using the %jniclassclassmodifier
+ pragma to change it back to "public".
+
+07/28/2002: cheetah (William Fulton)
+ [Java] Proxy/Shadow classes are generated by default. The -proxy and
+ -shadow command line options are deprecated. If you want to use the
+ low-level functional interface then use the new -noproxy commandline option.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+07/28/2002: cheetah (William Fulton)
+ [Java] Remaining pragmas shakeup. These were the remaining pragmas and their
+ new names where changed:
+
+ modulebase
+ modulecode
+ moduleclassmodifiers
+ moduleimport => moduleimports
+ moduleinterface => moduleinterfaces
+
+ The moduleimports works slightly differently to how the moduleimport pragma worked.
+ Now it actually takes code which gets placed before the class definition so the
+ whole import statement has to be given, for example:
+
+ %pragma(java) moduleimports=%{
+ import java.io.*;
+ import java.math.*;
+ %}
+
+ The moduleinterfaces is slightly different to the old moduleinterface in that if
+ more than one interface is required they must be comma separated in one use of
+ the pragma, for example:
+
+ %pragma(java) moduleinterfaces="Serializable, MyInterface"
+
+ These last two pragmas are consistent with the javainterfaces and javaimports
+ typemap.
+
+ A similar set of pragmas has been introduced, namely:
+
+ jniclassbase
+ jniclasscode
+ jniclassclassmodifiers
+ jniclassimport
+ jniclassinterface
+
+ These work in the same way as their module counterparts. Note that previously
+ the moduleXXX pragmas worked on the old module class which is now called the
+ JNI class (the class with the native functions). The jniclassXXX pragmas now
+ work on the new module class (the class that has all the global functions and
+ global variable getters and setters when using proxy classes, plus all other
+ remaining functions when using the low-level procedural interface).
+
+ In summary the contents of the pragmas make up a class like this:
+
+ <jniclassimports>
+ <jniclassmodifiers> class modulename extends <jniclassbase> implements <jniclassinterfaces> {
+ <jniclasscode>
+ ... SWIG generated functions ...
+ }
+}
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+07/28/2002: cheetah (William Fulton)
+ [Java] Deprecated modulemethodmodifiers pragma and replaced with
+ a better %feature based directive called %javamethodmodifiers.
+ A useful example would be for synchronisation in multi-threaded apps:
+
+ %javamethodmodifiers foo(int a) "public synchronized";
+
+ Changes this function from the default ("public") to "public synchronized".
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+07/26/2002: beazley
+ Several directives now allow optional configuration parameters.
+ These include:
+
+ %module(name="value", name="value", ...) modulename
+ %import(name="value", ...) "filename.i"
+ %extend(name="value", ...) classname {
+ ...
+ }
+
+ These currently have no effect and are reserved for
+ future expansion.
+
+07/26/2002: beazley
+ Enhancements to smart-pointer handling. SWIG only provides
+ extra support for a smart-pointer if operator->() returns
+ a proper pointer. For example:
+
+ Foo *operator->();
+
+ If operator->() returns an object by value or reference,
+ then SWIG examines the returned object to see if it also
+ implements operator->(). If so, SWIG chases operator->()
+ until it can find one that returns a pointer. This allows
+ cases like this to work:
+
+ class Foo {
+ public:
+ void blah();
+ };
+
+ class Bar {
+ ...
+ Foo *operator->();
+ ...
+ };
+
+ class Spam {
+ ...
+ Bar operator->();
+ ...
+ };
+
+ For example:
+
+ >>> s = Spam()
+ >>> s.blah() # Invokes Foo::blah()
+
+ The s.blah() call actually invokes:
+
+ ((s.operator->()).operator->())->blah();
+
+07/26/2002: beazley
+ Fixed a bug with typedef and references. For example:
+
+ typedef Foo & FooRef;
+ FooRef blah();
+
+ Previous versions of SWIG generated code that wouldn't
+ compile.
+
+07/25/2002: beazley
+ Wrapping of static methods has been improved in proxy classes. In older
+ versions of SWIG, if you had this:
+
+ class Foo {
+ public:
+ static void bar();
+ };
+
+ The static method was only available as a function Foo_bar(). For example:
+
+ >>> Foo_bar()
+
+ Now, the static method can also be invoked through an instance like this:
+
+ >>> f = Foo()
+ >>> f.bar() # Invokes static method
+
+ This works with all versions of Python. Additionally, for Python-2.2,
+ the static method can be invoked as:
+
+ >>> Foo.bar()
+
+ The old-style function is still support for backwards compatibility. If
+ you care about making your code across different versions of Python,
+ either use Foo_bar() or access the method through an instance.
+
+07/25/2002: beazley
+ Changes to the Python module. Proxy classes now utilize new Python-2.2
+ features including properties and static methods. However, these features
+ are supported in a way that provides backwards compatibility with older
+ Python versions. In other words, proxy classes work with all versions
+ of Python and only use new features when running on Python-2.2.
+
+
+07/25/2002: beazley
+ Modified %extend so that overloaded methods can be added. For example:
+
+ %extend Foo {
+ void bar(int x) { };
+ void bar(char *s) { };
+ ...
+ }
+
+ This works with both C++ *and* C.
+
+07/24/2002: cheetah (William Fulton)
+ [Java] More new typemaps so that the Java proxy classes and type wrapper classes
+ can be further tailored by users. These are the default code for generating the
+ finalize() methods (proxy classes only) and the getCPtr() methods for proxy
+ classes and type wrapper classes:
+
+ %typemap(javafinalize) SWIGTYPE %{
+ protected void finalize() {
+ _delete();
+ }
+ %}
+
+ %typemap(javagetcptr) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE [] %{
+ public static long getCPtr($javaclassname obj) {
+ return obj.swigCPtr;
+ }
+ %}
+
+ The javagetcptr typemap will enable users to handle Java null by overriding
+ this typemap - a requested feature.
+
+ The -nofinalize commandline option has been deprecated. The javafinalize
+ typemap is more powerful as it will allow the removal of the finalize methods
+ for all or any one or more particular proxy class.
+
+07/23/2002: cheetah (William Fulton)
+ [Java] The getCPtrXXX() function has been changed to a static function and
+ is now of the form:
+
+ protected static long getCPtr(XXX obj) {...}
+
+ This is a requested change which will allow Java null pointers to be used as null
+ can be passed in for obj. However, to achieve this the appropriate code must be
+ written using the new javagetcptr typemap directive.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+ Backwards compatibility can be achieved by adding this function back in using the
+ new javacode typemap:
+
+ %typemap(javacode) SWIGTYPE %{
+
+ // SWIG-1.3.12 and SWIG-1.3.13
+ public long getCPtr$javaclassname() {
+ return swigCPtr;
+ }
+ // SWIG-1.3.11 and earlier
+ public long getCPtr() {
+ return swigCPtr;
+ }
+
+ %}
+
+
+07/23/2002: cheetah (William Fulton)
+ [Java] New directive to control constant code generation - %javaconst.
+ The default handling for handling constants is to get the value through
+ a JNI call, eg
+
+ #define YELLOW 5
+ #define BIG 1234LL
+
+ results in:
+
+ public final static int YELLOW = modulename.get_YELLOW();
+ public final static long BIG = modulename.get_BIG();
+
+ Earlier versions of the Java module initialised the value using the C value:
+
+ public final static int YELLOW = 5;
+ public final static long BIG = 1234LL;
+
+ This works in most cases, but the value for BIG won't compile as 1234LL is not
+ valid Java code and this is one of the reasons why the default is now to get the
+ values through a JNI call. The side effect is that these 'constants' cannot be used
+ in switch statements. The %javaconst directive allows one to specify the
+ way the constant value is initialised and works like other %feature
+ directives, eg
+
+ %javaconst(0); // all constants from this point on are initialised using the C value
+ %javaconst(1) BIG; // just BIG initialised using JNI call (must be parsed before BIG is defined)
+
+07/23/2002: beazley
+ *** IMPORTANT CHANGES TO THE PYTHON MODULE ***
+
+ (1) The Python module now enables shadow/proxy classes by default.
+ This means that two files are always created by SWIG. For
+ instance, if you have this:
+
+ // file: foo.i
+ %module foo
+ ...
+
+ Then swig generates two files "foo_wrap.c" and "foo.py".
+
+ (2) The name of the low-level C extension module has been changed
+ to start with a leading underscore. This means that you have
+ to compile the module as follows:
+
+ $ cc -c -I/usr/local/include/python2.2 foo_wrap.c
+ $ cc -shared foo_wrap.o $(OBJS) -o _foo.so
+ ^^^^
+ note extra underscore
+
+ This naming scheme is consistent with other Python modules that
+ utilize extension code. For instance, the socket module consists
+ of "_socket.so" and "socket.py". In previous versions of SWIG,
+ the shared object file was named "foocmodule.so".
+
+ (3) A new directive can be used to insert Python code into
+ the corresponding .py file. For example:
+
+ %pythoncode %{
+ def foo():
+ print "Hello World"
+ %}
+
+ This directive allows you to create modules as a mix of C and Python.
+ Python code is seamlessly added to the module.
+
+ (4) The -shadow command line option is deprecated. This is turned on
+ by default.
+
+ (5) To disable the generation of the extra python file, use the "-noproxy"
+ command line option.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ This change will likely break the build environment of projects that
+ utilize shadow classes. To fix this, you probably only need to
+ change the name of the target .so file. For example, if you have
+ Makefile information like this:
+
+ TARGET = examplecmodule.so
+
+ Just change it to:
+
+ TARGET = _example.so
+
+ *** DOCUMENTATION UPDATE ***
+ The file Doc/Manual/Python.html has been updated to describe these changes.
+
+
+07/23/2002: beazley
+ Added -noextern option. If supplied, SWIG will not generate
+ extra extern declarations. This is sometimes an issue on
+ non-unix platforms.
+
+07/23/2002: beazley
+ Added a warning for ignored friend functions.
+
+07/23/2002: beazley
+ Fixed [ 574498 ] -proxy and %include "pointer.i" clash.
+ Reported by David Creasy.
+
+07/23/2002: beazley
+ Fixed [ 576103 ] global destruction warning with shadow.
+ Perl extensions should no longer report the warning
+
+ "Use of uninitialized value during global destruction."
+
+ when running with "perl -w". Reported by
+ Brett Williams.
+
+07/23/2002: beazley
+ In C++ mode, SWIG now always defines namespace std. By default,
+ it's empty. However, this will silence errors from programs
+ that include statements such as "using namespace std;".
+ This fixes Bug [ 584017 ] using namespace std generates error.
+ Reported by Joseph Winston.
+
+07/22/2002: beazley
+ Added a new warning message for %apply. If you use %apply but no typemaps
+ are defined, you will get a warning message. This should help with
+ problems like this:
+
+ %apply char *OUTPUT { ... };
+
+ In old versions of SWIG, this silently did nothing. Now you get an error like this:
+
+ file:line. Warning. Can't apply (char *OUTPUT). No typemaps are defined.
+
+07/22/2002: cheetah (William Fulton)
+ [Java] Started Java pragma deprecation. Replacements use %typemap based
+ directives and enable proxy classes and the new type wrapper classes to be
+ tailored in various ways. These are the new typemaps:
+
+ %typemap(javabase) - base (extends) for Java class
+ %typemap(javaclassmodifiers) - class modifiers for the Java class: default is "public"
+ %typemap(javacode) - java code is copied verbatim to the Java class
+ %typemap(javaimports) - import statements for Java class
+ %typemap(javainterfaces) - interfaces (extends) for Java class
+
+ And these are the %pragma directives being deprecated:
+ allshadowbase
+ allshadowclassmodifiers
+ allshadowcode
+ allshadowimport
+ allshadowinterface
+ shadowbase
+ shadowclassmodifiers
+ shadowcode
+ shadowimport
+ shadowinterface
+
+ Note that it is possible to target a particular proxy class:
+ %typemap(javaimports) Foo "import java.util.*"
+ or a particular type wrapper class:
+ %typemap(javaimports) double* "import java.math.*"
+ Note that $javaclassname in these typemaps are substituted with either the proxy
+ classname when using proxy classes or the SWIGTYPE class name.
+
+07/18/2002: cheetah (William Fulton)
+ [Java] Java module overhaul to implement static type checking of all
+ types.
+
+ 1) Changes when using Java Proxy classes
+ ----------------------------------------
+
+ Previously when wrapping global functions:
+
+ class SomeClass{};
+ void foo(SomeClass* s);
+ SomeClass* bar();
+
+ The native method prototypes used a long for pointers and looked like this:
+
+ public class modulename {
+ ...
+ public final static native void foo(long jarg1);
+ public final static native long bar();
+ }
+
+ and unlike member functions of a C++ class there was no wrapper around the native calls
+ to make the use of them more user friendly. They would be used from Java like this:
+
+ SomeClass s = new SomeClass(modulename.bar(), false);
+ modulename.foo(s.getCPtrSomeClass());
+
+ Note that the following will have the same effect, but then it would not have
+ been possible to call any proxy member functions in SomeClass:
+
+ long s = modulename.bar();
+ modulename.foo(s);
+
+ Now wrapper functions are generated:
+
+ public class modulename {
+ public static void foo(SomeClass s) {
+ // calls the native function
+ }
+
+ public static SomeClass bar() {
+ // calls the native function
+ }
+ }
+
+ Which means these functions can now be used more naturally with proxy classes:
+
+ SomeClass s = modulename.bar();
+ modulename.foo(s);
+
+ 2) Changes when not using Java Proxy classes
+ --------------------------------------------
+
+ The so called low-level interface was rather low-level indeed. The
+ new static type checking implementation makes it less so but it remains a
+ functional interface to the C/C++ world. Proxy classes are the obvious way to use
+ SWIG generated code, but for those who want a functional interface all non-primitive
+ types now have a simple Java class wrapper around the C/C++ type. Pointers and
+ references to primitive types are also wrapped by type wrapper classes. The type
+ wrapper classnames are based on the SWIG descriptors used by the other language
+ modules. For example:
+
+ C/C++ type Java type wrapper class name
+ ---------- ----------------------------
+ int* SWIGTYPE_p_int
+ double** SWIGTYPE_p_p_double
+ SomeClass* SWIGTYPE_p_SomeClass
+ SomeClass& SWIGTYPE_p_SomeClass
+ SomeClass SWIGTYPE_p_SomeClass
+
+ Note that everything wrapped by SWIG is accessed via a pointer even when wrapping
+ functions that pass by value or reference. So the previous example would now be
+ used like this:
+
+ SWIGTYPE_p_SomeClass s = example.bar();
+ example.foo(s);
+
+ Note that typedefs that SWIG knows about are resolved, so that if one has
+
+ class Foo{};
+ typedef Foo Bar;
+
+ then any use of Bar will require one to use SWIGTYPE_p_Foo;
+
+ Some considerations:
+ Make sure you make a firm decision to use either proxy classes or the functional
+ interface early on as the classnames are different.
+
+ 3) Pointers and non-parsed types
+ --------------------------------
+ Sometimes SWIG cannot generate a proxy class. This occurs when the definition of
+ a type is not parsed by SWIG, but is then used as a variable or a parameter.
+ For example,
+
+ void foo(Snazzy sds);
+
+ If SWIG has not parsed Snazzy it handles it simply as a pointer to a Snazzy.
+ The Java module gives it a type wrapper class around the pointer and calls it
+ SWIGTYPE_p_Snazzy. In other words it handles it in the same manner as types are
+ handled in the low-level functional interface. This approach is used for all
+ non-proxy classes, eg all pointer to pointers and pointers to primitive types.
+
+ 4) Backwards compatibility
+ -----------------------
+ Backwards compatibility is not an issue if you have been using proxy classes and
+ no global variables/functions. Otherwise some changes will have to be made.
+ The native methods still exist but they are now in a JNI class, which is called
+ modulenameJNI. As this class is really part of the internal workings,
+ it should not be required so the class has become protected. Some pragmas/directives
+ will hopefully be added to help with backwards compatibility.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+07/18/2002: beazley
+ Modified wrapping of uninstantiated templates returned by
+ value. Just to be safe, they are now wrapped by SwigValueWrapper<>
+ just in case they don't define a default constructor. This
+ would be used if you had code like this
+
+ Foo<int> blah();
+ void moreblah(Foo<int> x);
+
+ but you didn't instantiate Foo<int> using %template.
+ We should probably add a warning for this.
+
+07/17/2002: beazley
+ Added an error check to detect shadowed template paramaters.
+ For example:
+
+ template<class T> class Foo {
+ public:
+ int T;
+ };
+
+ This results in an error, not a warning. This warning is
+ also needed to fix some rather insidious problems like
+ this:
+
+ struct T {
+ int blah;
+ };
+
+ template<class T> class Foo {
+ public:
+ typedef T Traits; // Which T is this????
+ };
+
+ In this case, the template parameter T shadows the outer
+ structure (which is what you want).
+
+07/16/2002: beazley
+ Improved support for templates with integer arguments. SWIG is
+ much more aware of situations such as this:
+
+ const int Size = 100;
+
+ %template(Foo100) Foo<100>;
+ void bar(Foo<Size> *x); // Knows that Foo<Size> is the same as Foo<100>;
+
+07/15/2002: beazley
+ Fixed bug with %feature/%ignore/%rename and namespaces.
+ For example:
+
+ %ignore Foo::Bar
+ namespace Foo {
+ class Bar {
+ ...
+ };
+ }
+
+ Reported by Marcelo Matus.
+
+07/09/2002: beazley
+ Added parsing support for constructors that try to catch
+ exceptions in initializers. For example:
+
+ class Foo {
+ Bar b;
+ public:
+ Foo(int x) try
+ : b(x) { ... }
+ catch(int) {
+ ...
+ }
+ }
+
+ This has no effect on the generated wrappers. However, the try and catch
+ parts of the declaration are ignored. See Stroustrup, 3rd Ed, section
+ 14.4.6.1 for details.
+
+07/06/2002: beazley
+ Fixed bug in template symbol table management. This fixes
+ two bugs. First, mixing abstract methods, templates, and
+ inheritance no longer generates a failed assertion.
+
+ template <class T>
+ class A {
+ public:
+ virtual void foo() = 0;
+ };
+
+ template <class T>
+ class B : public A<T>
+ {
+ };
+ %template(A_int) A<int>;
+ %template(B_int) B<int>;
+
+ This fix also fixes a subtle problem with default values and
+ templates. For example:
+
+ template <class C>
+ struct B {
+ typedef unsigned int size_type;
+ static const size_type nindex = static_cast<size_type>(-1);
+ void foo(size_type index = nindex);
+ };
+
+ Bugs reported by Marcelo Matus.
+
+
+07/05/2002: ljohnson (Lyle Johnson)
+ [Ruby] Changed the definition of the SWIG_ConvertPtr() function
+ for the SWIG/Ruby runtime support so that it looks like the
+ Python version. If the last argument (flags) is non-zero,
+ SWIG_ConvertPtr() will raise an exception for type mismatches
+ as before. If flags is zero, this function will return -1 for
+ type mismatches without raising an exception.
+
+ *** POTENTIAL INCOMPATIBILITY FOR RUBY MODULE ***
+
+07/04/2002: beazley
+ Overloaded functions/methods/constructors now work in many language
+ modules. The support is completely transparent--just call the
+ function normally and SWIG will dispatch to the correct implementation.
+ There are a variety of issues associated with this. Please refer
+ to the overloading section of Doc/Manual/SWIGPlus.html for details.
+ *** NEW FEATURE ***
+
+07/04/2002: beazley
+ Fixed a bug with namespaces, enums, and templates. For example:
+
+ namespace hello {
+ enum Hello { Hi, Hola };
+
+ template <Hello H>
+ struct traits
+ {
+ typedef double value_type;
+ };
+
+ traits<Hi>::value_type say_hi()
+ {
+ return traits<Hi>::value_type(1);
+ }
+ }
+ SWIG wasn't generating wrappers that properly qualified
+ traits<Hi>. Reported by Marcelo Matus.
+
+06/30/2002: beazley
+ Supplied array variable typemaps for Tcl module. If you have a
+ variable like this:
+
+ int foo[10];
+
+ then a set function like this is generated:
+
+ void foo_set(int *x) {
+ memmove(foo,x,10*sizeof(int));
+ }
+
+06/30/2002: beazley
+ New %fragment directive. When writing typemaps, it can be easy to
+ get carried away and write a lot of code. However, doing so causes
+ tremendous code bloat. A common way to solve this is to write
+ helper functions. For example:
+
+ %{
+ void some_helper_function() {
+ ...
+ }
+ %}
+
+ %typemap(in) type {
+ some_helper_function(...);
+ }
+
+ The only problem with this is that the wrapper file gets polluted
+ with helper functions even if they aren't used. To fix this,
+ a new fragment directive is available. For example:
+
+ (corrected typo in line below - 06/26/2008)
+ %fragment("type_header","header") %{
+ void some_helper_function() {
+ ...
+ }
+ %}
+
+ %typemap(in, fragment="type_header") type {
+ some_helper_function(...);
+ }
+
+ In this case, the code fragment is only emitted if the typemap is
+ actually used. A similar capability is provided for declaration
+ annotation and the %feature directive. For example:
+
+ %feature("fragment","type_header") SomeDeclaration;
+
+ The first argument to %fragment is the fragment name. The second argument
+ is the file section where the fragment should be emitted.
+
+ The primary use of this directive is for writers of language modules
+ and advanced users wanting to streamline typemap code.
+
+ *** EXPERIMENTAL NEW FEATURE ***
+
+06/30/2002: beazley
+ Supplied memberin typemaps for all arrays in an attempt to eliminate
+ confusion about their use.
+
+06/29/2002: beazley
+ Experimental support for smart-pointers. When a class defines
+ operator->() like this
+
+ class Foo {
+ ...
+ Bar *operator->();
+ ...
+ };
+
+ SWIG locates class Bar and tries to wrap its member variables and
+ methods as part of Foo. For example, if Bar was defined like this:
+
+ class Bar {
+ public:
+ int x;
+ int spam();
+ };
+
+ You could do this (in the target language):
+
+ f = Foo()
+ f.x = 4 # Accesses Bar::x
+ f.spam() # Accesses Bar::spam
+
+ The primary use of this feature is to emulate the behavior of C++
+ smart-pointers---which allow attributes to accessed transparently
+ through operator->.
+
+ This feature is supported automatically in SWIG---no special directives
+ are needed. To disable this behavior. Use %ignore to ignore
+ operator->.
+ *** NEW FEATURE ***
+
+06/26/2002: beazley
+ Deprecated the %except directive. %exception should be used instead.
+
+06/25/2002: beazley
+ Major cleanup of the modules directory. Eliminated most
+ header files, consolidated module code into single files.
+
+06/24/2002: beazley
+ Reworked the instantiation of language modules. All language
+ modules must now define a factory function similar to this:
+
+ extern "C" Language *
+ swig_python(void) {
+ return new PYTHON();
+ }
+
+ This function is then placed in a table and associated with
+ a command line option in swigmain.cxx.
+
+ This approach has a number of benefits. It decouples the
+ SWIG main program from having to know about the class
+ definitions for each module. Also, by using a factory
+ function, it will be easier to implement dynamic loading
+ of modules (simply load the file and invoke the factory
+ function).
+
+06/24/2002: beazley
+ Fixed syntax error for reference conversions. For example:
+
+ operator Foo &();
+
+06/24/2002: beazley
+ Fixed syntax error for operator new[] and operator delete[].
+
+06/24/2002: beazley
+ Fixed code generation problem for constants and default arguments
+ involving templates.
+
+06/19/2002: ljohnson (Lyle Johnson)
+ [Ruby] Fixed a bug for the '-feature' command line argument;
+ that setting was effectively being ignored and so the feature
+ name was always set equal to the module name.
+
+06/17/2002: beazley
+ Fixed problems with static members and enums in templates.
+
+Version 1.3.13 (June 17, 2002)
+==============================
+
+06/16/2002: beazley
+ Fixed a bug with __FILE__ expansion in the preprocessor. On Windows,
+ the backslash (\) is now converted to (\\) in the string literal
+ used for __FILE__. Reported by Steve Glaser.
+
+06/14/2002: beazley
+ Fixed warning message about 'name private in this context'. The
+ warning is only generated for public methods. Reported by
+ Scott Michel.
+
+06/14/2002: beazley
+ Fixed some problems related to template instantiation
+ and namespaces. When SWIG expands a template, it does
+ so with fully resolved types. For example, if you have this:
+
+ template<class T> class foo { };
+ typedef double Double;
+ %template(foo_d) foo<Double>;
+
+ then, it is handled as foo<double> in the typesystem.
+ This fixes a number of subtle problems with inheritance
+ and templates.
+
+06/14/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added missing bool typemaps for INPUT, OUTPUT and
+ INOUT in Lib/ruby/typemaps.i.
+
+05/29/2002: cheetah (William Fulton)
+ [Java] Fix for a couple of broken pragmas.
+
+05/29/2002: cheetah (William Fulton)
+ Fix for unnecessary cast when wrapping global variable where
+ the type is not parsed by SWIG - Java variables example
+ failure as reported by Larry Virden.
+
+06/10/2002: beazley
+ Modified %template to allow for empty instantiations.
+
+ %template() foo<int,int>;
+
+ This registers foo<int,int> with the type system, but
+ doesn't wrap it (same as %ignore). This may only be a
+ temporary measure. SWIG might be able to automatically
+ instantiate templates in certain cases.
+
+06/10/2002: beazley
+ Fixed function prototype problems with Tcl 8.4
+
+06/09/2002: beazley
+ Fixed problem with templates and location of base classes.
+ This one is a little mind-bending, but here is an example
+ that illustrates:
+
+ template <class ArgType, class ResType>
+ struct traits
+ {
+ typedef ArgType arg_type;
+ typedef ResType res_type;
+ };
+
+ template <class ArgType, class ResType>
+ struct Function
+ {
+ };
+
+ template <class AF, class AG>
+ struct Class : Function<typename traits<AF, AG>::arg_type,
+ typename traits<AF, AG>::res_type>
+ {
+ };
+
+ %template(traits_dd) traits <double, double>;
+ %template(Function_dd) Function <double, double>;
+ %template(Class_dd) Class <double, double>;
+
+
+ In this example, the base class of 'Class' is determined from
+ the Function template, but the types are obtained through typedefs.
+ Because of this, SWIG could not locate the wrapped base class
+ (Function<double,double>). Should be fixed in 1.3.13 even
+ though I can think of a million other things that might
+ also be broken.
+
+06/07/2002: beazley
+ Fixed a problem with conversion operators. If you had an
+ operator like this,
+
+ operator double() const;
+
+ SWIG was ommitting the "const" qualifier. This affected
+ %rename and other directives. Reported by Zhong Ren.
+
+06/07/2002: beazley
+ Lessened the strictness of abstract class checking. If
+ you have code like this:
+
+ class Foo {
+ public:
+ virtual int method() = 0;
+ };
+
+ class Bar : public Foo {
+ public:
+ Bar();
+ ~Bar();
+ };
+
+ SWIG will go ahead and generate constructor/destructors
+ for Bar. However, it will also generate a warning message
+ that "Bar" might be abstract (since method() isn't defined).
+ In SWIG-1.3.12, SWIG refused to generate a constructor at all.
+
+06/07/2002: beazley
+ Change to %template directive. If you specify something like this:
+
+ %template(vi) std::vector<int>;
+
+ It is *exactly* the same as this:
+
+ namespace std {
+ %template(vi) vector<int>;
+ }
+
+ SWIG-1.3.12 tried to instantiate the template outside of the namespace
+ using some trick. However, this was extremely problematic and full
+ holes. This version is safer.
+
+06/07/2002: beazley
+ Fixed bug with scope qualification and templates. For example:
+
+ A<B::C>::DD
+
+ Before, this was separated as scopes A<B, C>, and DD. Fixed now.
+
+06/06/2002: beazley
+ Allow the following syntax:
+
+ class A { };
+ struct B : A { ... };
+
+ A base class without a specifier is assumed to be public for a struct.
+
+06/06/2002: beazley
+ Fixed syntax error with template constructor initializers.
+ Reported by Marcelo Matus.
+
+06/06/2002: beazley
+ Fixed bug with default template arguments.
+ Reported by Marcelo Matus.
+
+06/05/2002: beazley
+ Fixed subtle problems with %rename directive and template
+ expansion.
+
+ Code like this should now work:
+
+ %rename(blah) foo<double>::method;
+ ...
+ template<class T> class foo {
+ public:
+ void method();
+ };
+
+ %template(whatever) foo<double>;
+
+06/05/2002: beazley
+ Resolved some tricky issues of multi-pass compilation and
+ and inheritance. The following situation now generates
+ an error:
+
+ class Foo : public Bar {
+ ...
+ };
+
+ class Bar {
+ ...
+ };
+
+ The following code generates a warning about incomplete classes.
+
+ class Bar;
+ class Foo : public Bar { };
+
+ The following code generates a warning about an undefined class.
+
+ class Foo : public Bar { }; // Bar undefined
+
+ This fixes a failed assertion bug reported by Jason Stewart.
+
+06/05/2002: ljohnson
+ [Ruby] Added a warning message for the Ruby module about the lack
+ of support for multiple inheritance. Only the first base class
+ listed is used and the others are ignored. (Reported by Craig
+ Files).
+
+06/03/2002: beazley
+ Fixed a bug with struct declarations and typedef. For example:
+
+ typedef struct Foo Foo;
+ struct Foo {
+ ...
+ };
+
+ A few other subtle struct related typing problems were
+ also resolved.
+
+Version 1.3.12 (June 2, 2002)
+=============================
+
+05/30/2002: beazley
+ Fixed problem related to forward template class declarations and
+ namespaces. Bug reported by Marcelo Matus.
+
+05/30/2002: beazley
+ Added 'make uninstall' target. Contributed by Joel Reed.
+
+05/29/2002: beazley
+ Fixed rather insidious bug with %rename, %feature and template specialization.
+ For example:
+
+ %exception vector::__getitem__ {
+ ... some exception ...
+ }
+
+ template<class T> class vector {
+ ...
+ T __getitem__(int index); // Fine
+ ...
+ };
+
+ template<> class vector<int> {
+ ...
+ T __getitem__(int index); // Oops.
+ ...
+ };
+
+ Now, the %exception directive (and other features) should correctly apply to
+ both vector and specializations.
+
+05/29/2002: beazley
+ Subtle changes to %template() directive. Template arguments are now
+ reduced to primitive types in template matching. For example:
+
+ template<class T> class vector<T *> {
+ ... partial specialization ...
+ }
+
+ typedef int *IntPtr; // Gross typedef
+
+ // Gets the above partial specialization
+ %template(vectorIntPtr) vector<IntPtr>;
+
+ This change is extremely subtle, but it fixes a number of potential
+ holes in Luigi's STL library modules. For example:
+
+ typedef int Integer;
+ %template(vectori) vector<int>;
+
+05/29/2002: beazley
+ Fixed rather insidious typemap bug related to const. const
+ was being discarded through typedefs.
+
+05/29/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added input typemaps for const references to primitive
+ types (in Lib/ruby/ruby.swg).
+
+05/29/2002: cheetah (William Fulton)
+ [Java] The java arrray support functions are enclosed by
+ a SWIG_NOARRAYS #define. Useful if not using arrays and
+ it is desirable to minimise the amount of compiled code.
+
+05/29/2002: cheetah (William Fulton)
+ [Java] Enums were not renamed when using %name or %rename
+ fix.
+
+05/28/2002: ljohnson
+ [Ruby] Modified the name of the wrapper functions for the
+ "new" singleton method and "initialize" instance method for
+ consistency with the other language modules. The wrapper name
+ for the function that implements "new" is alloc_classname and
+ the wrapper name for the function that implements "initialize"
+ is new_classname.
+
+
+05/27/2002: beazley
+ Changes to runtime. Pointer conversion/creation functions
+ now almost always have an extra "flags" argument. For
+ example:
+
+ SWIG_ConvertPtr(obj, void **, swig_type_info *ty, int flags);
+ ^^^^^^^^^^
+ This extra parameter is reserved for future expansion and will
+ be used for more control over pointers in future versions.
+
+05/27/2002: beazley
+ Fix for C++ classes with private assignment operators. It
+ is now possible to safely return objects like this by value.
+ Caveat: the class must provide a copy constructor.
+
+05/26/2002: beazley
+ -proxy option added to many language modules. This is the
+ same as -shadow. We are merely changing terminology.
+
+05/26/2002: beazley
+ [perl] Fixed some inconsistencies in the -package option.
+ -package merely sets the package name to be used on the
+ wrappers. It does not change the name of the shared library
+ file or the name of the generated .pm file. This was
+ broken at some point, but works again now.
+
+05/25/2002: beazley
+ [perl] Fixed [ 475452 ] memory leak in return-by-value.
+ Problem related to static member variables returning newly
+ allocated objects. Reported by Roy Lecates.
+
+05/25/2002: beazley
+ [perl] Fixed [ 513134 ] %BLESSEDMEMBERS isn't always right.
+ Reported by Fleur Diana Dragan.
+
+05/25/2002: beazley
+ Fixed [ 540735 ] -importall and the -I option.
+
+05/25/2002: beazley
+ [guile] Fixed [ 532723 ] Default arg for char* can SegV.
+ Error in guile module. Reported by Brett Williams.
+
+05/25/2002: beazley
+ Subtle change to typemap application code. The "freearg"
+ typemap must exactly match up with the "in" or "ignore"
+ typemap. For example:
+
+ %typemap(in) (char *data, int len) { ... }
+ %typemap(freearg) char *data { ... }
+
+ void foo(char *data, int len);
+
+ In this case, the "in" typemap is applied, but the
+ freearg typemap is not. This is because the freearg
+ typemap doesn't match up with the input argument sequence.
+
+05/25/2002: beazley
+ Fixed [ 548272 ] Default argument code missing braces.
+ Reported by Brett Williams.
+
+05/25/2002: beazley
+ Fixed [ 547730 ] SwigValueWrapper needed for constructors.
+ Reported by William Fulton.
+
+05/25/2002: beazley
+ Undefined identifiers now evaluate to 0 when evaluating
+ preprocessor expressions. For example:
+
+ #if !FOO
+ ...
+ #endif
+
+ where FOO is undefined or set to some non-numeric value.
+
+ Fixes [ 540868 ] #if defined whatever - not parsed.
+ Reported by Adam Hupp.
+
+
+05/24/2002: beazley
+ SWIG now ignores the C++ 'export' keyword.
+
+05/23/2002: beazley
+ Some refinement of type-name mangling to account for pointers, arrays,
+ references, and other embedded type constructs.
+
+05/23/2002: beazley
+ Initial attempt at supporting template partial specialization. At
+ the very least, it is parsed and the classes are stored. Matching
+ of instantiations to specialized version is more limited and based on
+ the SWIG default typemap rules:
+
+ SWIGTYPE *
+ SWIGTYPE []
+ SWIGTYPE &
+
+ Now, why in the world would you want to use this feature? Other
+ than allowing for slightly modified class APIs, this capability is
+ primarily used to provide advanced wrapping support for STL-like
+ objects. It can also be mixed with typemaps. Here is an example:
+
+
+ /* Generic version */
+ template<class T> class vector {
+ %typemap(in) vector<T> * {
+ // A container of objects
+ }
+ };
+ /* Partial specialization (pointers) */
+ template<class T> class vector<T *> {
+ %typemap(in) vector<T> * {
+ // A container of pointers to objects.
+ }
+ };
+ /* Specialization (integers). */
+ template<> class vector<int> {
+ %typemap(in) vector<int> * {
+ // A container of integers.
+ }
+ };
+
+ *** EXPERIMENTAL FEATURE ***
+
+05/23/2002: beazley
+ Enhancement to typemaps. Normally, typemap variables are
+ renamed to avoid conflicts. For example:
+
+ %typemap(in) int * (int temp) {
+ $1 = &temp;
+ }
+
+ This results in code that creates and uses variables "temp1","temp2",
+ "temp3" and so forth depending on how many times the typemap is used.
+ Sometimes you want a single variable instead. To do that, using
+ the following naming scheme:
+
+ %typemap(in) int *(int _global_temp) {
+ }
+
+ Is this case, a single variable _global_temp is emitted in the
+ wrapper functions. It is shared across all typemaps. Repeated
+ typemaps do not replicate the variable---they use the first one
+ emitted.
+ *** NEW FEATURE ***
+
+05/23/2002: beazley
+ Minor enhancement to typemaps. If you have this code,
+
+ %typemap(in) Foo (int somevar = 3) {
+ ...
+ }
+
+ the default value for somevar is now emitted into the wrapper code.
+
+05/22/2002: beazley
+ Fixed %extend to be better behaved in namespaces. If you have code
+ like this:
+
+ namespace foo {
+ struct bar {
+ %extend {
+ void blah();
+ };
+ };
+ }
+
+ SWIG matches the blah() method to a C function named
+ void foo_bar_blah(foo::bar *self).
+
+ This is consistent with the non-namespace version.
+ Bug reported by Marcelo Matus.
+
+05/22/2002: beazley
+ New library files: cpointer.i, carrays.i, cmalloc.i. These
+ provide access to C pointers and memory allocation functions.
+ See Doc/Manual/Library.html for details.
+
+05/22/2002: cheetah (William Fulton)
+ [Java] C type char no longer maps to Java type byte, but to Java type char.
+ It is now treated as a character rather than a signed number. This fits in
+ with the other language modules and is a more natural mapping as char* is
+ mapped as a string of characters. Note that the C signed char type is still
+ mapped to a Java byte.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+05/22/2002: cheetah (William Fulton)
+ [Java] Improved constants wrapping. Constants (#define and %constant) values
+ are now obtained through a JNI call. Previously the value was compiled as
+ Java code, but this didn't work for all cases, eg #define 123ULL.
+
+05/22/2002: beazley
+ Fixed bogus error message with %extend directive and C++
+ access specifiers. Reported by Marcelo Matus.
+
+05/22/2002: beazley
+ Namespaces and enums now work correctly. For example:
+
+ namespace Foo {
+ enum Bar { A, B };
+ }
+
+ Bug reported by Marcelo Matus.
+
+05/21/2002: beazley
+ The %types directive can now be used to specify inheritance relationships
+ in the runtime type system. For example,
+
+ %types(Foo = Bar);
+
+ specifies that Foo isa Bar. Using this is potentially quite dangerous.
+ However, this is useful in certain cases (and in the SWIG library).
+
+05/20/2002: beazley
+ %nodefault and %makedefault directives now require a trailing semicolon.
+ For example:
+
+ %nodefault;
+ ...
+ %makedefault;
+
+ In addition both directives can take a class name. For example:
+
+ %nodefault Foo;
+
+ class Foo { /* No default constructor/destructor */
+ };
+
+ class Bar { /* Default constructor/destructor generated */
+ };
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ If you don't use the trailing semicolon, things will mysteriously break.
+
+05/20/2002: beazley
+ More improvements to type system handling. SWIG now correctly handles
+ template names and parameters in a namespace. For example:
+
+ namespace foo {
+ template<class T> class bar { };
+ typedef int Integer;
+
+ void blah(bar<Integer> *x);
+ };
+
+ In the generated code, all of the typenames are properly qualified.
+
+05/17/2002: cheetah (William Fulton)
+ [Java] deprecated broken -jnic and -jnicpp commandline options. The C or C++
+ JNI calling convention is now determined from the -c++ commandline option.
+
+05/16/2002: cheetah (William Fulton)
+ [Java] The JCALL macros which exist so that the same typemaps can be used
+ for generating both the C and C++ JNI calling conventions no longer appear
+ in the generated code. This is because the output is now passed through the
+ SWIG preprocessor which does the macro expansion for either C or C++ (depending
+ on whether -c++ is passed on the SWIG commandline).
+
+ The generation of the functions used in the array typemaps have been adjusted
+ to take account of this. The side effect is that any typemaps which contained
+ JCALL macros within %{ %} brackets will have to be moved within {} brackets
+ so that the SWIG preprocessor can expand the macros.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+05/13/2002: beazley
+ Class templates may now be used as template parameters. For example:
+
+ template<class T, template<class> class C> class Foo {
+ ...
+ };
+ template<class T> class Bar {
+ ...
+ };
+
+ %template(Fooi) Foo<int, Bar>;
+
+ SWIG doesn't really do anything special with this---it's just
+ another way of specifying a template parameter.
+
+05/13/2002: beazley
+ Minor refinement of template support. Template parameter names are no longer
+ required for types. For example:
+
+ template<bool> class Foo {
+ };
+
+ Obviously, names are required for template<class T>;
+
+05/12/2002: beazley
+ New macro expansion in typemaps. The sequence:
+
+ $descriptor(type)
+
+ Will expand into the SWIG type descriptor structor for
+ the given type. Type may be any abstract datatype.
+ For example:
+
+ $descriptor(int *)
+ $descriptor(int (*)(int,double))
+ $descriptor(vector<int> *)
+
+ Caveat: It is *NOT* currently legal to use other typemap
+ substitution variables in the macro. For example
+ $descriptor($1_type).
+
+ The primary purpose of this modification is to better
+ support typemaps for container objects or to allow typemaps
+ that might be performing type conversions.
+ *** NEW FEATURE ***
+
+05/11/2002: beazley
+ The wrapping of references to primitive types has been
+ changed as follows:
+
+ Arguments of type 'const primitive &' are now passed
+ by value as opposed to pointers. Return values of
+ type 'const primitive &' are returned as values instead of
+ pointers.
+
+ 'primitive' is any one of int, short, long, long long,
+ char, float, double, bool (as well as unsigned variants).
+
+ This change is being made to better support C++ wrapping--especially
+ code that makes use of templates and the STL.
+
+05/11/2002: beazley
+ The %template directive can now be used to access templates
+ in a namespace. For example:
+
+ namespace std {
+ template<class T> class complex {
+ T re, im;
+ public:
+ complex(T _r = T(), T _i = T()) : re(_r), im(_i) { }
+ T real() { return re; }
+ T imag() { return im; }
+ };
+ }
+
+ %template(complex) std::complex<double>;
+
+ Note: There are some very subtle namespace/symbol table
+ management issues involved in the implementation of this.
+ It may not work in certain cases.
+
+05/10/2002: beazley
+ Member template constructor support added. For example:
+
+ template<typename _T1, typename _T2>
+ struct pair {
+ _T1 first;
+ _T2 second;
+ pair() : first(_T1()), second(_T2()) { }
+ template<class _U1, class _U2> pair(const pair<_U1,_U2> &x);
+ };
+
+ To instantiate the template, use %template and %extend.
+ For example, this expands the constructor into a default
+ copy constructor:
+
+ %extend pair {
+ %template(pair) pair<_T1,_T2>;
+ }
+
+ Highly experimental. Other uses may be broken.
+
+05/10/2002: beazley
+ The %extend (%addmethods) directive no longer works unless
+ it appears in the public section of a class. An error
+ message is now generated (as opposed to a segmentation fault).
+
+05/09/2002: beazley
+ New %warnfilter() directive. This directive attaches a warning
+ filter to specific declarations and has the same semantics as
+ %rename, %ignore, %feature, and so forth. For example:
+
+ %warnfilter(501) foo; // Suppress overloaded warning
+ int foo(int);
+ int foo(double);
+
+ or
+
+ %warnfilter(501) Object::foo(double);
+ class Object {
+ public:
+ int foo(int);
+ int foo(double);
+ };
+
+ This feature only suppresses warnings in later stages of code
+ generation. It does not suppress warnings related to preprocessing
+ or parsing.
+ *** NEW FEATURE ***
+
+05/09/2002: beazley
+ SWIG now supports C99 variadic preprocessor macros. For example:
+
+ #define debugf(fmt,...) fprintf(stderr,fmt,__VA_ARGS__)
+
+ The argument "..." is used to indicate variable arguments which
+ are all placed into the special argument name __VA_ARGS__ in
+ the macro expansion.
+
+ SWIG also implements the GNU (##) extension for swallowing the
+ preceding comma when __VA_ARGS__ is empty. For example:
+
+ #define debugf(fmt,...) fprintf(stderr,fmt, ##__VA_ARGS__)
+
+ Here is how this is expanded:
+
+ debugf("%d", 3) --> fprintf(stderr,"%d",3)
+ debugf("Hello") --> fprintf(stderr,"Hello" )
+
+ (notice the deleted comma).
+ *** NEW FEATURE ***
+
+05/08/2002: samjam (Sam Liddicott)
+ Many changes to php module. Shadow classes are now implemented
+ entirely in native C and no need for php-code shadow wrappers
+ Populated template config.m4 and Makefile.in as needed by
+ phpize are generated.
+
+05/08/2002: ljohnson (Lyle Johnson)
+ [Ruby] A copy constructor is now turned into a "clone"
+ instance method (see Dave's change for copy constructors
+ dated 4/7/2002). This seems like the appropriate thing
+ to do for Ruby code.
+
+05/08/2002: ljohnson (Lyle Johnson)
+ [Ruby] Fixed [ 553864 ] Inline destructor code not written.
+
+05/08/2002: beazley
+ %ignore behaves better with constructors, destructors, and the
+ type system in general. For constructors and destructors,
+ %ignore now suppresses the creation of a default constructor
+ or destructor. For example:
+
+ %ignore ~Foo;
+ class Foo {
+ public:
+ Foo();
+ ~Foo();
+ ...
+ };
+
+ In SWIG-1.3.11, ~Foo() simply "disappeared" and the code generator
+ created a wrapper for a default destructor (as if it was never
+ declared in the interface). In SWIG-1.3.12, %ignore suppresses
+ the creation of a destructor if one is actually defined.
+
+ Similarly, even though a declaration is ignored, information
+ may still be needed to properly handle types. For example, here
+ is a very subtle error that is fixed by this change:
+
+ %ignore std::string; // Prevent class wrapping
+ namespace std {
+ class string {
+ ...
+ };
+ %typemap(in) string * {
+ ...
+ }
+ }
+
+ void foo(std::string *s); // Broken.
+
+ Before this fix, %ignore would cause the class definition to disappear.
+ This, in turn, would cause the typemap to be misapplied.
+
+05/08/2002: beazley
+ Minor changes to %rename, %ignore, %feature, and related directives
+ for better support of destructors. Destructors can now be precisely
+ tagged. For example:
+
+ %ignore Foo::~Foo;
+ %feature("action") ~Bar {
+ ...
+ }
+
+ *Developer warning*
+ Operations such as renaming and feature attachment for classes used to
+ be applied to destructors as well. For instance, if you did this:
+
+ %rename(Bar) Foo;
+
+ The operation applied to the class itself, the constructor, and
+ the destructor. This is no longer the case. Now such operations
+ will only apply to the class and the constructor. Note: if you
+ were relying on this for class renaming, be aware that renamed
+ classes should really only be handled at the level of the class itself
+ and not the level of individual declarations in the class (although
+ they can be renamed individually if needed). As far as I know,
+ the Language class is already taking care of this case correctly.
+
+05/07/2002: beazley
+ New set of tests. The Examples/test-suite/errors directory contains
+ tests that try to exercise all of SWIG's error and warning messages.
+
+05/07/2002: beazley
+ Start of a warning framework. Warning messages are now assigned numeric values
+ that are shown in warning messages. These can be suppressed using the
+ -w option. For example:
+
+ swig -w302 example.i
+ swig -w302,305 example.i
+
+ Alternatively, the #pragma preprocessor directive can be used to disable this:
+
+ #pragma SWIG nowarn=302
+ #pragma SWIG nowarn=302,305
+
+ Note: Since SWIG is a multi-pass compiler, this pragma should
+ only be used to change global settings of the warning filter. It should
+ not be used to selectively enable/disable warnings in an interface file.
+ The handling of #pragma occurs in the C++ preprocoessor and affects all
+ subsequent stages of compilation.
+
+ The -Wall option turns on all warnings and overrides any filters that
+ might have been set.
+
+ Warnings can be issued from an interface using %warn. For example:
+
+ %warn "110:%section is deprecated"
+
+ The first part of a warning message is an optional warning number.
+ A complete set of warning numbers is found in Source/Include/swigwarn.h.
+ *** NEW FEATURE ***
+
+05/07/2002: beazley
+ Internal parsing change. Directives to include files now use brackets [ ... ]
+ instead of { ... }.
+
+ %includefile "foo.i" [
+ ...
+ ]
+
+ The use of { ... } was a bad choice because they were included implicitly by
+ the preprocessor and made it impossible to properly detect legitimate missing '}'
+ errors.
+
+04/16/2002-
+05/02/2002: beazley
+ SWIG European Tour: Paris-Amsterdam-Bath.
+
+04/23/2002: beazley
+ The %addmethods directive has been renamed to %extend.
+ For example:
+
+ class Foo {
+ ...
+ };
+
+ %extend Foo {
+ int blah() { ... };
+ int bar() { ... };
+ ...
+ };
+
+ Motivation: the %addmethods directive can be used for many
+ other tasks including adding synthesized attributes, constructors,
+ and typemaps. Because of this, "addmethods" is somewhat misleading.
+ %extend more precisely describes this operation---extension of a
+ class or structure.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ %addmethods still works via a macro definition. However,
+ a warning message may be generated. Errors involving %addmethods
+ will actually refer to the %extend directive.
+
+04/23/2002: beazley
+ Further refinement of the type system. Typedef now
+ propagates through functions, pointers to functions,
+ and pointers to member functions.
+ For example:
+
+ typedef int Integer;
+ void foo(int (*x)(int), Integer (*y)(Integer));
+
+ In this case, arguments 'x' and 'y' have exactly
+ the same type (and would obviously accept objects
+ of either type).
+
+ Similarly, consider this:
+
+ class Foo {
+ };
+
+ typedef Foo Bar;
+ void bar(int (Foo::*x)(int), int (Bar::*y)(int));
+
+ In this case, arguments x and y are the same
+ type (via typedef).
+
+04/22/2002: beazley
+ SWIG now generates a warning message if any part of
+ an expression involves values from a private part of a class.
+ For example:
+
+ class Foo {
+ private:
+ static int X;
+ public:
+ void blah(int a, int b = X); // Warning
+ };
+
+ In this case, the default argument is ignored. There
+ are workarounds, but they are rather clumsy. For instance,
+ you might do this:
+
+ %feature("action") blah(int,int) {
+ if ($nargs == 1) {
+ result = blah(arg1);
+ } else {
+ result = blah(arg1,arg2);
+ }
+ }
+ void blah(int a, int b = 0);
+
+
+04/21/2002: beazley
+ Use of the %inline directive inside a namespace is
+ forbidden and now generates an error message. This is
+ not allowed since the inlined code that is emitted is
+ not placed inside a namespace. This confuses other
+ stages of parsing.
+
+04/21/2002: beazley
+ Some bug fixes to casting operations and expression
+ parsing. Due to some parsing issues, it is not
+ currently possible to use casts for all possible
+ datatypes. However, the common cases work.
+
+04/20/2002: beazley (Amsterdam)
+ Member templates now work. Simply use the %template
+ directive inside a class or %addmethods to create
+ instantiations (see Doc/Manual/SWIGPlus.html). Supporting
+ this was easy---earlier changes to templates made it
+ possible using only a two-line modification to the parser
+ and a few minor modifications elsewhere. Hmmm, come to
+ think of it, the smoke was rather thick in that Internet "cafe".
+ *** NEW FEATURE ***
+
+04/19/2002: beazley (TGV)
+ Improved handling of non-type template parameters. For example:
+
+ vector<int,100>;
+
+ Simple numbers and strings can be used with the %template
+ directive as well. For example:
+
+ %template(vecint100) vector<int,100>;
+
+ Note: Arithmetic expressions are not currently allowed.
+
+ Default template arguments now work and do not have to
+ be given to %template.
+
+04/18/2002: beazley (Paris)
+ Change in internal template handling. Template
+ parameters are now fully integrated into the type
+ system and are aware of typedefs, etc. This builds
+ upon the change below.
+
+ *** DEVELOPER WARNING ***
+ Word of caution to language module writers. The "name"
+ parameter of certain parse tree nodes (classes, functions, etc.)
+ may be parameterized with types. This parameterization is
+ done using SWIG type-strings and not the underlying C version.
+ For example,
+
+ int max<int *>(int *,int *)
+
+ has a name of "max<(p.int)>". If you use the name directly,
+ you may get syntax errors in the generated code. To fix this,
+ use SwigType_namestr(name) to convert a parameterized name
+ to a C name with valid syntax. The internal version is
+ used to reduce template types to a common representation
+ and to handle issues of typedef.
+
+04/16/2002: beazley (somewhere over the Atlantic)
+ Enhancement of typedef resolution. The type system is now
+ aware of template arguments and typedef. For example:
+
+ typedef int Integer;
+
+ foo(vector<int> *x, vector<Integer> *y);
+
+ In this case, vector<int> and vector<Integer> are
+ the same type. There is some interaction between this
+ mechanism and the implementation of typemaps. For example,
+ a typemap defined for vector<int> * would apply to either type.
+ However, a typemap for vector<Integer> * would only apply to
+ that type.
+
+ Typedefs and typemaps and matched by left-most expansion.
+ For example:
+
+ vector<Integer,Integer> -->
+ vector<int, Integer> -->
+ vector<int, int>
+
+
+04/24/2002: cheetah (William Fulton)
+ [Java] Changes to Java shadow classes.
+ Overcomes a bug where the module assumed that a pointer to a derived
+ class could be used in place of a pointer to a base class. Thanks
+ to Stephen McCaul for analysing the bug and submitting patches.
+
+ A consequence is that the getCPtr() method in each shadow class has
+ disappeared and has been replaced with a getCPtrXXX(), where XXX is the
+ shadow class name. If you have code that previously used getCPtr(),
+ and the associated class is wrapping a C struct or a C++ class that
+ is not involved in an inheritance chain, just use the new method. If
+ however, the class is involved in an inheritance chain, you'll have
+ to choose which pointer you really want. Backwards compatibility
+ has been broken as not using the correct pointer can lead to weird bugs
+ through ill-defined behaviour. If you are sure you want the old methods,
+ you could add them back into all shadow classes by adding this at the
+ beginning of your interface file:
+
+ %pragma(java) allshadowcode=%{
+ public long getCPtr(){
+ return swigCPtr;
+ }
+ %}
+
+ Please see entry dated 07/23/2002 to see how to do this after the deprecation
+ of the allshadowcode pragma.
+
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+04/13/2002: beazley
+ Fixed problem with default arguments and references. Declarations such
+ as this should now work:
+
+ void foo(const string &x = "Hello");
+
+04/12/2002: beazley
+ Added typemap $* substitutions for typemaps involving arrays.
+ Requested by William Fulton.
+
+04/11/2002: beazley
+ Template specialization is now supported. For example:
+
+ template<> class vector<int> {
+ ...
+ };
+
+ When the %template directive is used, it will use a specialization
+ if one is defined. There are still some limitations. Partial
+ specialization is not supported. A template of type <void *> does
+ not match all pointers.
+ *** NEW FEATURE ***
+
+04/11/2002: beazley
+ Major change to template wrapping internals. Template declarations are
+ no longer processed as macros but now result in real parse-tree
+ nodes. The %template directive expands these nodes into a
+ specific instantiation. This change enables a number of
+ new and interesting capabilities:
+
+ Directives such as %rename, %feature, and %addmethods can
+ now be applied to uninstantiated templates. For example:
+
+ %rename(barsize) vector::bar(char *buf, int len);
+ ...
+ template<typename T> class vector {
+ public:
+ ...
+ void bar(char *buf);
+ void bar(char *buf, int len); // Renamed
+ ...
+ };
+
+ %template(intvector) vector<int>; // Renaming carries through
+
+
+ By parsing templates into an internal data structure, it will
+ be possible to support specialization (and maybe partial
+ specialization).
+
+ This is highly experimental and a work in progress.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ In SWIG-1.3.11, template declarations were simply processed
+ as weird macros. No other information was retained. This
+ made it impossible to support more advanced features and
+ complicated many other parts of the implementation.
+
+04/09/2002: beazley
+ Change to template class wrapping. There were a variety of
+ "issues" with the old approach related to parsing, the type
+ system, and namespaces. These changes are meant to rectify
+ some of these problems:
+
+ A specific instantiation of a template can now be specified
+ by including the class inline like this:
+
+ class vector<int> {
+ public:
+ vector();
+ ~vector();
+ ... whatever ...
+ };
+
+ This is template specialization, but partial specialization is
+ not yet implemented.
+
+ The %template directive has been modified to expand roughly as
+ follows:
+
+ %template(vecint) vector<int>;
+
+ becomes
+
+ %rename(vecint> vector<int>;
+ class vector<int> {
+ public:
+ vector();
+ ...
+ };
+
+ Note that this simply builds upon the code above (templates
+ included inline).
+
+ This modified approach to wrapping fixes some subtle type
+ issues. For instance, you can now define typemaps and typedefs
+ like this:
+
+ %typemap(in) vector<int> * {
+ ...
+ }
+ typedef vector<int> intvector;
+ ...
+ void blah(intvector *v); // Gets the above typemap
+
+ This did not work in SWIG-1.3.11 due to a peculiarity of
+ the template implementation.
+
+ %template(name) no longer installs the template as a class
+ with name "name". This might break %addmethods as described
+ in the manual. For example:
+
+ %template(vecint) vector<int>;
+ %addmethods vecint { // Fails. vecint not a class
+ ...
+ };
+
+ To fix this, just use the template name instead:
+
+ %addmethods vector<int> {
+ ...
+ }
+
+ Note: This technique might be a way to implement some bizarre
+ template specialization techniques. For example:
+
+ %addmethods vector<int> {
+ // Only applied if vector<int> instantiated later
+ %typemap(in) vector<int> * {
+ ...
+ }
+ ...
+ };
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+04/08/2002: beazley
+ Fixed [ 540868 ] #if defined whatever - not parsed. SWIG should
+ now correctly handle preprocessor directives like this:
+
+ #if defined __cplusplus
+ ...
+ #endif
+
+ Note: was implemented previously, but there was a minor bug.
+ Reported by Adam Hupp.
+
+04/07/2002: beazley
+ %readonly and %readwrite are deprecated due to a change in the
+ implementation. Instead of being pragmas, mutability is now
+ controlled as a "feature" using the following two directives:
+
+ %immutable;
+ int x; // read-only variable
+ int y; // read-only variable
+ %mutable;
+ int z; // Modifiable
+
+ %immutable and %mutable are much more powerful than their older
+ counterparts. They can now pinpoint a specific declaration like
+ this:
+
+ %immutable x; /* Any x */
+ %immutable Foo::x; /* x in class Foo */
+
+ In fact, the matching algorithm is the same as for %rename,
+ %ignore, and other directives. This means that the declaration
+
+ %immutable Foo::x;
+
+ would not only apply to class Foo but to all derived classes
+ as well.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ %immutable and %mutable must be terminated by a semi-colon. This
+ differs slightly from the older %readonly and %readwrite directives.
+ Since %immutable and %mutable can be applied to declarations the
+ semicolon is needed to distinguish between a global feature and
+ one targeted to a single declaration. Note: this incompatibility is the
+ primary reason for changing the name of the directive.
+
+04/07/2002: beazley
+ New handling of copy constructors. If a class defines
+ constructors like this:
+
+ class Foo {
+ public:
+ Foo();
+ Foo(const Foo &); // Copy constructor
+ ...
+ };
+
+ SWIG now generates a function copy_Foo() for the copy
+ constructor.
+
+ In previous verions, this generated a name-clash and an
+ error message. To preserve backwards compatibility, SWIG
+ does not change the behavior if %rename is used to resolve
+ the name conflict. However, if no name resolution is made,
+ this new approach is used.
+
+ Copy constructors may be handled as a special case in the
+ target language. However, this is up to the language module
+ itself.
+
+04/07/2002: beazley
+ The %template directive is now namespace aware. This allows
+ code like this:
+
+ namespace foo {
+ template<typename T> max(T a, T b) { return a > b ? a : b; }
+ }
+
+ using namespace foo;
+ %template(maxint) max<int>; // Ok
+
+ namespace bar {
+ using foo::max;
+ %template(maxdouble) max<double>; // Ok
+ }
+
+ Caveat: the template name supplied to %template must be defined in the
+ same scope in which the %template directive appears. This code is
+ illegal:
+
+ %template(maxint) foo::max<int>;
+
+04/07/2002: beazley
+ Minor enhancement to preprocessor. The preprocessor can now perform
+ string comparison. For example:
+
+ #define A "hello"
+ ...
+ #if A == "hello"
+ ...
+ #endif
+
+ The primary use of this is in SWIG macros. For example:
+
+ %define FOO(x)
+ #if #x == "int"
+ /* Special handling for int */
+ ...
+ #endif
+ %enddef
+
+ Normal users can probably safely ignore this feature. However, it may
+ be used in parts of the SWIG library.
+
+04/07/2002: beazley
+ Further refinement of default constructor/destructor wrapper generation.
+ SWIG is now much more aware of pure virtual methods. For instance:
+
+ class A { /* Abstract */
+ public:
+ virtual void method1() = 0;
+ virtual void method2() = 0;
+ };
+ class B : public A { /* Abstract */
+ public:
+ virtual void method1() { };
+ };
+
+ class C : public B { /* Ok */
+ public:
+ virtual void method2() { };
+ };
+
+ In this case, SWIG will only generate default constructors for C.
+ Even though B looks fine, it's missing a required method and is abstract.
+
+04/04/2002: beazley
+ Subtle change to structure data member access. If you
+ have a structure like this:
+
+ struct Foo {
+ Bar b;
+ };
+
+ The accessor functions for b are generated as follows:
+
+ (1) If b is *not* defined as a structure or class:
+
+ Bar Foo_b_get(Foo *self) {
+ return self->b;
+ }
+ void Foo_b_set(Foo *self, Bar value) {
+ self->b = value;
+ }
+
+ (2) If b *is* defined as a structure or class:
+
+ Bar *Foo_b_get(Foo *self) {
+ return &self->b;
+ }
+ void Foo_b_set(Foo *self, Bar *value) {
+ self->b = *value;
+ }
+ See the "Structure data members" section of Doc/Manual/SWIG.html
+ for further details.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ This may break interfaces that relied on a lot of a undeclared
+ structure and class names. To get the old behavior, simply
+ use a forward declaration such as "struct Bar;"
+
+04/04/2002: beazley
+ C++ namespace support added. SWIG supports all aspects of
+ namespaces including namespace, using, and namespace alias
+ declarations. The default behavior of SWIG is to flatten
+ namespaces in the target language. However, namespaces are
+ fully supported at the C++ level and in the type system.
+ See Doc/Manual/SWIGPlus.html for details on the implementation.
+
+04/02/2002: cheetah (William Fulton)
+ [Java] Sun has modified javac in jdk1.4 to no longer compile
+ an import of an unnamed namespace. To fix this SWIG no longer
+ generates the import for packageless classes.
+ http://developer.java.sun.com/developer/bugParade/bugs/4361575.html
+ As reported SF #538415.
+
+03/27/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added support for pointer-to-member, similar to that
+ for the Python module. Remarkably similar. Also added a new
+ example for this (Examples/ruby/mpointer), which is remarkably
+ similar to the Python example of the same name.
+
+03/26/2002: ljohnson (Lyle Johnson)
+ [Ruby] Made a few minor edits to the "Advanced Topics"
+ chapter of the SWIG manual and added a new major section
+ about how to create multi-module Ruby packages with SWIG.
+
+03/26/2002: ljohnson (Lyle Johnson)
+ [Ruby] Removed all of the old Ruby pragmas. If any of this
+ functionality is truly missed we can resurrect it, preferably
+ with some kind of feature-based directive.
+
+03/25/2002: ljohnson (Lyle Johnson)
+ [Ruby] Fixed SWIG exception library support for Ruby, which
+ has apparently been broken for some time. Luckily, no one seems
+ to have noticed.
+
+03/23/2002: beazley
+ C++-namespace support in SWIG directives.
+
+ %addmethods:
+
+ The %addmethods directive now accepts a fully qualified classname
+ and can be used inside C++ namespace declarations. For example:
+
+ // Attaches to the class Foo::Bar below
+ %addmethods Foo::Bar {
+ int somemethod() { ... }
+ };
+
+ namespace Foo {
+ class Bar {
+ public:
+ ...
+ };
+
+ // Attaches to the class Bar above
+ %addmethods Bar {
+ int othermethod() { ... };
+ }
+ }
+
+ %feature, %rename, %ignore, %exception, and related directives:
+
+ Namespaces are fully integrated into the renaming and declaration
+ matcher. For example:
+
+ %rename(display) Foo::print; // Rename in namespace Foo
+ %ignore Foo::Bar::blah; // Ignore a declaration
+
+ %rename directives can be placed inside namespace blocks as well. For
+ example:
+
+ namespace Foo {
+ %rename(display) print; // Applies to print below
+
+ void print();
+ };
+
+ Most other SWIG directives should work properly inside namespaces.
+ No other changes are needed.
+
+03/22/2002: beazley
+ Some changes to internal symbol table handling. SWIG no longer
+ manages structures and unions in a separate namespace than normal
+ declarations like ANSI C. This means you can't have a structure
+ with the same name as a function. For example:
+
+ struct Foo {
+ ...
+ }
+
+ int Foo() { ... }
+
+ This approach is more like C++. It's not clear that SWIG ever
+ really supported the ANSI C anyways---using the same name would
+ almost certainly generate a name-clash in the target language.
+
+03/22/2002: ljohnson (Lyle Johnson)
+ [Ruby] Fixed [ 517302 ] for handling of renamed overloaded
+ constructors. Now, renamed overloaded constructors are converted
+ into class singleton methods (basically acting as "factory"
+ methods).
+
+03/21/2002: beazley
+ Fixed [ 532957 ] %ignore parse error and casting operator.
+ Reported by William Fulton.
+
+03/18/2002: beazley (** ADVANCED USERS ONLY **)
+ Added support for dynamic casting in return values. A somewhat
+ common problem in certain C++ programs is functions that hide
+ the identity of underlying objects when they are returned
+ from methods and functions. For example, a program might include
+ some generic method like this:
+
+ Node *getNode();
+
+ However, Node * may just be base class to a whole hierarchy
+ of different objects. Instead of returning this generic Node *,
+ it might be nice to automatically downcast the object into the
+ appropriate type using some kind dynamic cast.
+
+ Assuming you understand the peril involved, a downcast can now
+ be performed using the following function in the run-time type
+ checker:
+
+ swig_type_info *SWIG_TypeDynamicCast(swig_type_info *, void **ptr);
+
+ This function checks to see if the type can be converted to another
+ type. If so, a different type descriptor (for the converted type)
+ is returned. This type descriptor would then be used to create
+ a pointer in the target language.
+
+ To use this, you would write a typemap similar to this:
+
+ %typemap(out) Node * {
+ swig_type_info *ty = SWIG_TypeDynamicCast($1_descriptor, (void **) &$1);
+ $result = SWIG_NewPointerObj($1, ty);
+ }
+
+ Alternatively,
+
+ %typemap(out) Node * = SWIGTYPE *DYNAMIC;
+
+ To make the typemap have any effect, you have to write a supporting
+ function that knows how to perform downcasting. For example:
+
+ %{
+ static swig_type_info *
+ Node_dynamic_cast(void **ptr) {
+ Node **nptr = (Node **) ptr;
+ Element *e = dynamic_cast<Element *>(*nptr);
+ if (e) {
+ *ptr = (void *) e;
+ return SWIGTYPE_p_Element;
+ }
+ Data *d = dynamic_cast<Data *>(*nptr);
+ if (d) {
+ *ptr = (void *) d;
+ return SWIGTYPE_p_Data;
+ }
+ return 0;
+ }
+ %}
+
+ There is no restriction on how types are determined. dynamic_cast<>
+ uses C++ RTTI. However, if you had some other mechanism for determining
+ the type, you could use that here. Note: it is important to save
+ the new pointer value back into the argument as shown. When downcasting,
+ the value of the pointer could change.
+
+ Finally, to make the casting function available, you have to register
+ it with the run-time type checker. Put this macro in your interface file.
+
+ DYNAMIC_CAST(SWIGTYPE_p_Node, Node_dynamic_cast);
+
+ Note: this feature does not introduce a performance penalty on
+ normal SWIG operation. The feature is only enabled by writing
+ a new typemap that explicitly calls SWIG_TypeDynamicCast() to
+ make a conversion.
+
+ Examples/test-suite/dynamic_cast.i contains a simple example.
+ This feature is not supported in the Java module due to differences
+ in the type-checking implementation.
+
+ *** EXPERIMENTAL FEATURE ***
+
+03/17/2002: beazley
+ Small change to type-name handling of unnamed structures and
+ typedef. If a structure of this form appears:
+
+ typedef struct {
+ ...
+ } Foo;
+
+ Then 'Foo' is used as the proper typename for the structure.
+ Furthermore, Foo can now be used as a name in C++ inheritance.
+ SWIG was already kind of doing this, but this modification refines
+ the implementation to more closely follow the C++ ARM, section
+ 7.1.3, p. 106. This fixes a couple of obscure corner cases.
+
+03/16/2002: beazley
+ Modified C++ inheritance with a few enhancements. First, type information
+ needed for casting and type-equivalence is generated even when base-classes
+ aren't defined in the interface. For example:
+
+ class Foo : public Bar { /* Bar unspecified */
+ public:
+ ...
+ };
+
+ void blah(Bar *b);
+
+ In this case, the blah() function still accepts Foo * even though nothing
+ is really known about Bar. Previous SWIG versions would just generate
+ a type error.
+
+ Inheritance has also been modified to work through typedef. For example:
+
+ class Bar {
+ };
+
+ typedef Bar OtherBar;
+ class Foo: public OtherBar {
+ }
+
+ In this case, the base class of OtherBar is correctly resolved back to
+ Bar. The use of the name OtherBar is lost in this resolution (the wrappers
+ will simply use Bar instead of the typedef name OtherBar).
+
+03/13/2002: beazley
+ %typemap, %apply, and related directives can now appear inside
+ class definitions.
+
+03/13/2002: beazley
+ Fixed a variety of problems related to compiling SWIG on 64-bit
+ platforms.
+
+03/12/2002: beazley
+ Fixed problem with "ignore" and "in" typemaps. Local variables
+ associated with "in" were being added to the wrapper function even
+ though they were never used. Mostly harmless, but it would lead
+ to a variety of compilation warnings.
+
+03/12/2002: beazley
+ Some changes to the internal type system and handling of nested C++
+ types. In previous versions of SWIG, if you had the following:
+
+ class Foo {
+ public:
+ typedef int Blah;
+ };
+ class Bar : public Foo {
+ public:
+ void somemethod(Blah x);
+ };
+
+ The argument type in somemethod() would implicitly be set to Bar::Blah.
+ Although this is technically allowed, it breaks typemaps. For example:
+
+ %typemap(in) Foo::Blah { ... }
+
+ doesn't match like you expect. This has been changed in SWIG-1.3.12.
+ Now, types are expanded using the class in which they were defined.
+ So, the argument type in somemethod() will be Foo::Blah---since the
+ type Blah was defined in Foo.
+
+03/10/2002: beazley
+ Fixed some subtle type scoping problems with typedef and C++ classes.
+ For example:
+
+ typedef int Blah;
+ class Bar {
+ public:
+ typedef double Blah;
+ void foo(Blah x, ::Blah y);
+ ...
+ }
+
+03/10/2002: beazley
+ Highly experimental change to handle variable length arguments.
+ First, there is no portable or reliable way to wrap
+ a varargs function in full generality. However, you *can* change
+ the function signature using %varargs.
+
+ %varargs(char *) fprintf;
+ ...
+ void fprintf(FILE *f, char *fmt, ...);
+
+ In this case, the variable length parameter "..." is
+ simply replaced by the parameters given in %varargs. This
+ results in a function like this:
+
+ void fprintf(FILE *f, char *fmt, char *s);
+
+ More than one argument can be used and default values
+ can be defined. For example, this code specifies a
+ maximum of four arguments.
+
+ %varargs(char *x1 = 0, char *x2 = 0, char *x3 = 0, char *x4 = 0) fprintf;
+
+ *** EXPERIMENTAL NEW FEATURE ***
+
+03/10/2002: beazley
+ Change to handling of variable length arguments. varargs
+ is now handled as a proper parameter and is passed to the
+ code generator. However, it still can't be handled correctly
+ (and will generate a typemap warning). This change has been
+ made to better incorporate variable length arguments with other
+ directives such as %ignore, %rename, %feature, and so forth.
+
+03/10/2002: beazley
+ Fixed [ 522555 ] Syntax error parsing "define" construct. SWIG
+ is a little more restrictive in determining #define statements
+ that will be wrapped as constants. Also added a better parser
+ error rule for handling bad constants.
+
+03/08/2002: cheetah (William Fulton)
+ [Java] Bug fix: Classes renamed with %rename that are derived from
+ another class generate more appropriate shadow class code.
+
+03/08/2002: cheetah (William Fulton)
+ [Java] Fixed SF [ #523632 ] and [ #513335 ] both reported by Israel
+ Tanner. Support for types that are used which are in a typedef. The
+ appropriate shadow class name is generated. Also generated correct
+ shadow classname when a templated class is used within another
+ templated class. See the cpp_typedef.i testcase.
+
+03/08/2002: cheetah (William Fulton)
+ [Java] Bug fix: No type was generated in shadow classes for types
+ that weren't wrapped by SWIG. The type is treated as a raw
+ pointer, ie no shadow class.
+
+02/22/2002: beazley
+ Refined the matching algorithm used by %rename, %ignore, and
+ %feature. If a type signature is supplied, it must exactly
+ match that used in the declaration---including any use of
+ const. For example:
+
+ %rename(foo1) foo(int);
+ %rename(bar1) bar(int) const;
+
+ class Blah {
+ public:
+ void foo(int); // Matched --> foo1
+ void foo(int) const; // Not matched
+ void bar(int); // Not matched
+ void bar(int) const; // Matched --> bar1
+ }
+
+ In previous versions, a non-const specification would match
+ both the non-const and const declarations. However, the whole
+ point of %rename and related directives is that they be able
+ to precisely pinpoint exact declarations in an interface. This
+ fixes the problem.
+
+02/21/2002: beazley
+ Reworked the handling of default constructor and destructors.
+ SWIG now makes a preliminary pass over the parse tree to discover
+ which classes support default allocation. This fixes a number
+ of very subtle issues in code generation and call/return by value.
+
+02/18/2002: cheetah (William Fulton)
+ Improved support on Cygwin: Perl, Python, Tcl, Ruby and Java should
+ work out of the box, barring the runtime library. Removed dllwrap
+ and replaced with newly working gcc -shared instead for Cygwin.
+ All this will require the new improved binutils 20010802 and later,
+ but the latest Cygwin is usually the best recommendation.
+
+02/15/2002: beazley
+ Fixed some problems related to wrapping of global variables
+ and Perl shadow classes. Reported by Chia-liang Kao.
+
+02/15/2002: ljohnson (Lyle Johnson)
+ [Ruby] Made a fix to the code generation for C++ class
+ constructors so that we get both a "new" singleton method
+ and an "initialize" instance method for each class. This
+ change enables developers to derive new Ruby classes from
+ SWIG-wrapped C++ classes and then override their initialize
+ methods to provide subclass-specific instance initialization.
+
+02/15/2002: ljohnson (Lyle Johnson)
+ [Ruby] Massive documentation update for the Ruby module,
+ contributed by Craig Files.
+
+02/14/2002: ljohnson (Lyle Johnson)
+ [Ruby] Bug fix: An error in the SWIG runtime support for Ruby
+ was causing several of the examples to fail. Reported by
+ William Fulton.
+
+02/14/2002: ljohnson (Lyle Johnson)
+ [Ruby] Bug fix: Enumerations defined within a class (such
+ as those seen in the Examples/ruby/enum example) were not
+ being exported with the correct names. Reported by William
+ Fulton.
+
+02/13/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added a warning message when we run across overloaded
+ class constructors for C++ code, that this is currently not
+ supported (even if the overloads have been %renamed). For an
+ example of where this doesn't work, see Examples/ruby/operator.
+
+02/13/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added an "ignored" warning message when the parser runs
+ across an operator!=() declaration for C++ code.
+
+02/11/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added the "import", "import_template", "operator" and
+ "template" examples.
+
+02/11/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added multi-module support.
+
+02/09/2002: ljohnson (Lyle Johnson)
+ [Ruby] Added the missing "#define SWIG_NOINCLUDE" at the top of
+ the wrapper code when the '-c' option is used.
+
+02/09/2002: ljohnson (Lyle Johnson)
+ Corrected a minor off-by-one error for the size of the
+ swig_types[] array that's generated in the wrapper code.
+
+02/08/2002: beazley
+ Fixed SF [ #515058 ] Wrong code for C++ templates.
+ Reported by Israel Taller.
+
+Version 1.3.11 (January 31, 2002)
+=================================
+
+01/30/2002: beazley
+ Fix to pass/return by value for C++ objects that define
+ no default constructor. Changes to the typemap system
+ made it impossible to wrap C++ objects with no default
+ constructor. This has been fixed, but the solution
+ involves some clever template magic contributed by
+ William Fulton. Please see the comments in the file
+ Lib/swig.swg for further details. This solution is
+ experimental and may be refined in a future release.
+
+01/30/2002: beazley
+ Global variables and member data of type "const char *"
+ can be set, but the old value is silently discarded without
+ any garbage collection. This may generate a memory leak.
+ This change is needed to more safely handle variables
+ like this:
+
+ const char *foo = "Hello World\n";
+
+ In this case, it's not safe to free the old value. However,
+ SWIG can dynamically allocate a new value and make foo point
+ to it. To fix this memory leak, you can probably do this:
+
+ %clear const char *foo;
+ %apply char * {const char *foo};
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+01/30/2002: beazley
+ Two minor typemap enhancements have been added. First,
+ typemaps can issue a warning message by including a special
+ warning attribute. For example:
+
+ %typemap(in,warning="I'm going to do something dangerous") ...
+
+ The warning message will show up whenever the typemap is
+ applied.
+
+ Second, a typemap can force a no-match by defining
+
+ %typemap(in) sometype "pass"
+
+ If this is used, the typemap system will *not* record a
+ typemap match for "sometype". This can be used to block
+ selected typemaps. For example, if you wanted to disable
+ a typemap feature for some type, you could do this.
+
+ // Do not allow global variables of type 'const char *' to be set.
+ %typemap(varin) const char * "pass"
+
+ It might also be possible to use this to do subtle and
+ strange things with typemaps. For example, if you wanted to
+ make 'blah *' an output value and 'const blah *' an input
+ parameter, you might do this:
+
+ %typemap(ignore) blah *(blah temp) {
+ $1 = &temp;
+ }
+ %typemap(argout) blah * {
+ ... return a value ...
+ }
+ /* Block unqualified typemaps defined above */
+ %typemap(ignore) const blah * "pass"
+ %typemap(argout) const blah * "pass"
+ %typemap(in) const blah * {
+ ... get input value ...
+ }
+
+ (This potential applications of typemaps suggested by Greg Stein).
+ *** NEW FEATURE ***
+
+01/29/2002: cheetah (william fulton)
+ [Java] Bug fix: No enumerations were wrapped when the -shadow
+ commandline option was not specified. Reported by Israel Taller.
+
+01/28/2002: cheetah (william fulton)
+ [Java] Global arrays are successfully wrapped. In fact they started
+ mostly working in SWIG-1.3.10.
+
+01/28/2002:richardp
+ Added first attempt at C++ and -shadow support for PHP4 module,
+ please test and mail me if any problems/ideas on improving it.
+
+ There is a known problem with uninitialized member variables,
+ please see Examples/php4/sync/README for details.
+
+ Also more PHP documentation added to Doc/Manual/Php.html
+
+01/27/2002:beazley
+ The ANSI C size_t type is now recognized as an integer by default.
+
+01/26/2002:beazley
+ long long and unsigned long long support added to many language modules.
+ This is not a portable feature and will require compiler support
+ for the long long type. In target languages that do not support
+ long long (e.g., Tcl and Perl), numbers are converted to a string
+ of digits. This prevents their use in arithmetic calculations, but
+ still allows values to be set from a string.
+
+ long long support requires the use of the strtoll() and strtoull()
+ functions as well as the 'lld' and 'llu' format specifiers
+ of sprintf().
+
+01/26/2002:beazley
+ Fixed [ #501827 ] Delete method is not called. The Tcl
+ module wasn't correctly calling destructors when they
+ were defined using %addmethods. This has been fixed.
+ Reported by Reinhard Fobbe.
+
+01/26/2002: beazley
+ Better support for long long and unsigned long long. Typemaps
+ have been included in a number of modules for handling these types.
+ In addition, the parser has been modified to accept long long
+ literals such as 1234LL and 1234ULL.
+
+01/27/2002: cheetah (william fulton)
+ [Java] A C char[] is mapped to a Java String which is the default
+ SWIG handling of char[] and char*. It used to be mapped to byte[].
+ Note that a C signed char[] array is mapped to byte[].
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+01/25/2002: beazley
+ Fixed a problem with return-by-value, C++, and
+ objects that define no default constructor.
+ Reported by Joel Reed.
+
+01/25/2002: cheetah (william fulton)
+ [Java] Overhaul of the Java module. The C code generation is now
+ done from typemaps.
+
+01/24/2002: cheetah (william fulton)
+ [Java] Support for arrays of enum pointers
+
+01/20/2002: cheetah (william fulton)
+ [Java] Error checking for null Java objects being passed to native
+ functions. Exception thrown now whereas before the JVM crashed.
+
+01/18/2002: cheetah (william fulton)
+ [Java] Corrected behaviour for functions that take arrays. For
+ example, when this c function:
+
+ void arrayfn(int array[]);
+
+ is wrapped the corresponding native function
+
+ public final static native void arrayfn(int[] array);
+
+ is produced. Previously if the C function made any changes to the
+ array elements, these were not reflected back into the Java array.
+ This has now been corrected so that the changes are propogated back
+ to Java and the calling function will see these changes. This is
+ how pure Java functions work, ie arrays are passed by reference.
+
+01/15/2002:mkoeppe
+ [Guile] New file cplusplus.i with C++ typemaps contributed
+ by Marcio Luis Teixeira <marciot@holly.colostate.edu>.
+
+01/11/2002: cheetah (william fulton)
+ [Java] Changed mapping of C long to Java type. Was mapped to Java
+ long, now mapped to Java int. If you want the previous mapping to
+ Java long use this approach in your interface file:
+
+ %clear long;
+ %typemap(jni) long "jlong"
+ %typemap(jtype) long "long"
+ %typemap(jstype) long "long"
+
+ %clear long[ANY];
+ %typemap(jni) long[ANY] "jlongArray"
+ %typemap(jtype) long[ANY] "long[]"
+ %typemap(jstype) long[ANY] "long[]"
+ %typemap(in) long[ANY] {write me for array support}
+ %typemap(out) long[ANY] {write me for array support}
+ %typemap(argout) long[ANY] {write me for array support}
+ %typemap(freearg) long[ANY] {write me for array support}
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+ This new mapping is more appropriate when interfacing to 32 bit
+ applications which are used in the current 32-bit JVMs. For future
+ 64-bit JVMs you may have to change these mappings - eg on Unix LP64
+ systems, but not on Microsoft 64bit Windows which will be using a
+ P64 IL32 model. This may be automated in a future version of SWIG.
+
+01/10/2002:beazley
+ Fixed [ 501677 ] %init block in wrong place. Reported
+ by Luigi Ballabio.
+
+01/09/2002: cheetah (william fulton)
+ [Java] Default support for the long long type. signed long long is
+ mapped to a Java long. unsigned long long is mapped to BigInteger.
+
+01/09/2002:beazley
+ Experimental change to parser to better support mixing of
+ int, long, short, unsigned, float, and double. The parser
+ should now support types like this:
+
+ short unsigned int
+ int unsigned short
+ unsigned short int
+ unsigned int short
+
+ This change also enables a type of 'long double' (previously
+ unsupported) to be used.
+ *** NEW FEATURE ***
+
+01/05/2002: cheetah (william fulton)
+ [Java] Casting fix for when function return type is a pointer as
+ reported by Gary Pennington 2002-01-05. The upper 32bits of the
+ 64 bit jlong will have contained junk for 32bit pointers.
+
+01/05/2002: cheetah (william fulton)
+ [Java] Better pointer handling in Java is possible as the
+ INPUT, OUTPUT and INOUT typemaps have been added into typemaps.i.
+
+01/05/2002: cheetah (william fulton)
+ [Java] $null can be used in input typemaps to return early from JNI
+ functions that have either void or a non-void return type. Example:
+
+ %typemap(check) int * %{
+ if (error) {
+ SWIG_exception(SWIG_IndexError, "Array element error");
+ return $null;
+ }
+ %}
+
+ If the typemap gets put into a function with void as return, $null
+ will expand to nothing:
+
+ void jni_fn(...) {
+ if (error) {
+ SWIG_exception(SWIG_IndexError, "Array element error");
+ return ;
+ }
+ ...
+ }
+
+ otherwise $null expands to zero, where javareturntype is either a
+ pointer or a primitive type:
+
+ javareturntype jni_fn(...) {
+ if (error) {
+ SWIG_exception(SWIG_IndexError, "Array element error");
+ return 0;
+ }
+ ...
+ }
+
+01/02/2002: cheetah (william fulton)
+ [Java] The Java module incorrectly used argout typemaps for
+ strings. This is now corrected and the code now resides
+ in the freearg typemap. The argout array typemaps have been split into
+ argout and freearg typemaps. This correction may require some user
+ written typemaps to be modified.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+12/28/2001: cheetah (william fulton)
+ [Java] Multi typemaps now working for Java see multimap example.
+ [Java] Fix for recently introduced bug - freearg typemap code was appearing
+ before the function call.
+
+12/28/2001: cheetah (william fulton)
+ [Java] JCALL macro for JNI calls that work in both C and C++ typemaps
+ have been replaced with JCALL0, JCALL1, JCALL2, JCALL3 and JCALL4
+ macros.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+12/22/2001:beazley
+ Resolved some inconsistent behavior with %rename and class renaming.
+ If you specify the following:
+
+ %rename(Foo) Bar;
+
+ class Bar {
+ public:
+ Bar();
+ ~Bar();
+ }
+
+ Then the %rename directive applies to the class itself, the constructor,
+ and the destructor (all will be renamed to Foo).
+
+ If a class defines more than one constructor, the overloaded variants
+ can still be renamed by specifying parameters to %rename. For example:
+
+ %rename(Bar_copy) Bar(Bar &);
+ class Bar {
+ public:
+ Bar();
+ Bar(Bar &);
+ ~Bar();
+ };
+
+ There are still some odd corner cases. If you specify
+
+ %rename(Foo) ::Bar;
+
+ then only the name of the class is changed and the constructor/destructor
+ names are left unmodified. If you specify
+
+ %rename(Foo) *::Bar;
+
+ then the names of the constructor/destructor functions are modified but
+ the name of the class is not.
+
+12/21/2001: cheetah (william fulton)
+ [Java] jni, jtype and jstype typemaps no longer hardcoded but real
+ typemaps. New variable substitution, $javaclassname, can be used in
+ the jstype typemaps. It is replaced with the Java shadow class name
+ where applicable.
+ [Java] Fix for recently introduced bug to do with inheritance when
+ using %import.
+ [Java] A few more bug fixes, todo with %rename and using the kind
+ with the type, eg
+ void fn(union uni myuni, struct str mystr, class cl mycl);
+
+12/20/2001:beazley
+ Fixed [ #494524 ] Preprocessor bug - apostrophe and #subst.
+
+12/20/2001:beazley
+ Added SWIG_VERSION preprocessor symbol. This is a hexadecimal
+ integer such as 0x010311 (corresponding to SWIG-1.3.11). This can
+ be used in the interface as follows:
+
+ #if SWIG_VERSION >= 0x010311
+ /* Use some fancy new feature */
+ #endif
+
+ Note: The version symbol is not defined in the generated SWIG
+ wrapper file.
+
+ *** NEW FEATURE ***
+
+12/20/2001:mkoeppe
+ [MzScheme]: Renamed mzswig_make_boolean to
+ swig_make_boolean, as the latter is used in the typemaps.
+ Reported by Luigi Ballabio.
+
+12/17/2001:mkoeppe
+ [Guile]: Rewrote list-vector.i using multi-dispatch
+ typemaps. Updated pointer-in-out.i. Make the
+ deprecated typemap-substitution of "$source" in "argout"
+ work as before.
+
+12/16/2001:mkoeppe
+ [Guile]: Fixed macros %values_as_list, %values_as_vector,
+ %multiple_values to use the proper %pragma syntax. New
+ Guile example/test "multivalue"; new Guile run-test for
+ test-suite item "list-vector" (currently broken).
+
+12/14/2001:mkoeppe
+ [Guile]: Fixed typemap-substition bug for "varin". Relaxed
+ valid-identifier check to allow all R5RS identifiers.
+
+
+Version 1.3.10 (December 10, 2001)
+==================================
+
+12/08/2001:beazley
+ Modified %typemap so that %{ ... %} can also be used as a
+ code block (mostly for completeness). For example:
+
+ %typemap(in) blah %{
+ ...
+ %}
+
+ This form does not introduce a new block scope. Also, the
+ code enclosed in %{ ... %} is not processed by the preprocessor.
+
+12/08/2001:beazley
+ Fixed [ #459614 ] SWIG with multiple TCL interpreters.
+
+12/08/2001:beazley
+ Fixed [ #417141 ] rubydec.swg is wrong
+ Reported by Paul Brannan.
+
+12/08/2001:beazley
+ Fixed [ #410557 ] Problem with %addmethods on NT.
+ Reported by Magnus Ljung.
+
+12/08/2001:beazley
+ Fixed [ #445233 ] Enhancement: handle access change.
+ SWIG now parses (but ignores) C++ access changes for the
+ the following:
+
+ class A {
+ protected:
+ void something() { }
+ public:
+ A() {}
+ };
+
+ class B : private A {
+ public:
+ B() : A() { }
+ protected:
+ A::something; <---- Parsed, but ignored
+ };
+
+ Suggested by Krzysztof Kozminski.
+
+12/08/2001: cheetah (william fulton)
+ Fix for Ruby to work using Visual C++.
+
+12/06/2001:beazley
+ Fixed [ #465687 ] unsigned short parameters fail.
+ Reported by Gerald Williams.
+
+12/06/2001:beazley
+ Fixed SF [ #489594 ] PyString_FromString can't take NULL arg.
+ Reported by John Merritt. SWIG now converts string values
+ to Python using code like this:
+
+ resultobj = result ? PyString_FromString(result) : Py_BuildValue("");
+
+12/06/2001:beazley
+ Fixed SF [ #463561 ] Type conversions not generated.
+ Reported by Gerald Williams.
+
+12/04/2001:beazley
+ Fixed SF [ #470217 ] Tcl default argument handling.
+ Reported by Shaun Lowry.
+
+12/04/2001:beazley
+ Fixed SF [ #472088 ] defined(MACRO) expanded everywhere.
+ Embedded preprocessor directives such as
+
+ %#if defined(FOO)
+
+ are not expanded by the SWIG preprocessor.
+ Reported by Gerald Williams.
+
+12/04/2001:beazley
+ Fixed SF [ #476467 ] Problems with #define & commas.
+
+12/04/2001:beazley
+ Fixed SF [ #477547 ] wrong declaration of pointer functions.
+ Bad prototypes in Lib/tcl/ptrlang.i.
+
+12/04/2001:beazley
+ Fixed SF [ #483182 ] Constants can take args by mistake.
+ When swig -perl5 -const is used, constants are declared
+ with a void prototype. For example:
+
+ sub ICONST () { $examplec::ICONST }
+
+ Patch submitted by Rich Wales.
+
+12/03/2001:beazley
+ New %exception directive. This is intended to replace %except.
+ It works in exactly the same manner except it does not accept a
+ language specifier. For example:
+
+ %exception {
+ try {
+ $action
+ }
+ catch(SomeError) {
+ error
+ }
+ }
+
+ %exception is also name aware---allowing it to be applied to
+ specific declarations in an interface. For example:
+
+ %exception foo {
+ ...
+ exception for any function/method foo
+ ...
+ }
+
+ %exception Foo::bar {
+ ...
+ exception for method bar in class Foo
+ ...
+ }
+
+ %exception Foo::bar(double) {
+ ...
+ exception for method bar(double) in class Foo
+ ...
+ }
+
+ The semantics of this name matching is exactly the same as for %rename.
+ *** NEW FEATURE ***
+
+12/03/2001:beazley
+ Substantial cleanup of the Python shadow class code. Shadow classes
+ used to be created in this rather complicated manner involving about
+ a half-dozen strings created in bits and pieces. Shadow classes
+ are now generated in a more straightforward manner--in the same
+ order that appears in the interface file.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ The order in which declarations appear in the shadow file may differ.
+
+12/03/2001:beazley
+ The %insert directive (%{ ... %}, %runtime, %header, %wrapper, etc.)
+ can now be used inside of a class definition. This has potential
+ uses when generating shadow class code. For example:
+
+ class Foo {
+ ...
+ %insert("shadow") %{
+ # Some python code
+ def blah(self):
+ print "I'm blah!"
+ %}
+ ...
+ };
+
+ The support for class code insertion depends on the language module.
+ However, the intent of this feature is to simplify the task of extending
+ shadow class code. In the Python module, this inserts code with the
+ proper level of indendation (regardless of what was used in the SWIG
+ interface).
+ *** NEW FEATURE ***
+
+11/29/2001: cheetah (william fulton)
+ Modifications for Java and Python modules to work on cygwin.
+ Unfortunately a lot of the python module has started to produces code
+ which cannot be auto-imported using cygwin libtools so most of it is
+ still broken.
+
+11/28/2001:beazley
+ The %rename and %feature directive can now be used inside
+ of a class definition. For example:
+
+ class Foo {
+ %rename(foo_i) foo(int);
+ %rename(foo_d) foo(double);
+ public:
+ ...
+ void foo(int);
+ void foo(double);
+ ...
+ };
+
+ When used in this manner, the %rename directive only applies
+ to members of the class in which it appears as well as all
+ derived classes. In fact, this is really just the same
+ as saying:
+
+ %rename(foo_i) Foo::foo(int);
+ %rename(foo_d) Foo::foo(double);
+ class Foo {
+ ...
+ };
+
+ *** NEW FEATURE ***
+
+11/26/2001:beazley
+ Added the experimental %feature directive. %feature can be
+ used to attach arbitrary string attributes to parse tree nodes.
+ For example:
+
+ %feature("except") blah {
+ try {
+ $function
+ } catch (Error) {
+ whatever;
+ }
+ }
+
+ or
+
+ %feature("set") *::x_set "x";
+
+ or
+
+ %feature("blah") Foo::bar(int,double) const "spam";
+
+ The syntax is borrowed from the %rename directive. In fact, the
+ exact same semantics apply (inheritance, matching, etc.).
+
+ %feature is a very powerful low-level primitive that can be used to
+ customize individual language modules and to provide hints to
+ any stage of code generation. Features are attached to
+ parse tree nodes as attributes with names like "feature:*" where *
+ is replaced by the feature name (e.g., "feature:except", "feature:set",
+ etc.). Language modules can then look for the features using
+ a simple attribute lookup.
+
+ %feature is intended to be a replacement for a number of
+ older SWIG directives including %except and specialized
+ pragmas. It is more powerful (due to its parameterized
+ name matching) and it provides very precise control over
+ how customization features are attached to individual
+ declarations. There are future expansion plans that will
+ build upon this capability as well.
+
+ It's not certain that %feature will ever be used directly
+ by SWIG users. Instead, it may be a low-level primitive
+ that is used in high-level macro definitions. For instance,
+ to support properties, you might define a macro like this:
+
+ %define %property(name, setf, getf)
+ %feature("set") setf #name;
+ %feature("get") getf #name;
+ %enddef
+
+ Which allows a user to specify things like this:
+
+ %property(p, get_p, set_p);
+
+ class Blah {
+ public:
+ int get_p();
+ void set_p(int);
+ };
+
+ *** EXPERIMENTAL NEW FEATURE ***
+
+11/24/2001:beazley
+ The Tcl module has been expanded with some new features for
+ managing object ownership. For example:
+
+ set c [Circle -args 20]
+ $c area # Invoke a method
+ $c -disown # Releases ownership of the object
+ $c -acquire # Acquires ownership of the object
+
+ If Tcl owns the object, its destructor is invoked when the
+ corresponding object command is deleted in Tcl.
+
+ To simplify the destruction of objects, the following syntax
+ can be used:
+
+ $c -delete # Delete an object
+
+ This is an alternative for the more obscure variant of
+
+ rename $c {}
+
+ These features also add functionality at the C API level.
+ The following functions manage ownership from C and
+ can be used in typemaps.
+
+ SWIG_Acquire(void *ptr);
+ SWIG_Disown(void *ptr);
+
+ A new function for constructing instances is also available:
+
+ Tcl_Obj *
+ SWIG_NewInstanceObj(Tcl_Interp *interp, void *ptr,
+ swig_type_info *type, int own);
+
+ When used in a typemap, this creates a pointer object and
+ an interpreter command that can be used to issue methods and
+ access attributes as shown above.
+ *** NEW FEATURE ***
+
+11/23/2001:beazley
+ All Python-related %pragma operations have been eliminated.
+ Most of these were written for older SWIG versions in order to
+ compensate for limitations in earlier releases. In an effort
+ to reduce the amount of code-clutter and potential for errors,
+ it is easier to simply eliminate the pragmas and to start over
+ (if needed). To be honest, I'm not even sure the pragmas
+ worked in 1.3.9 and recent releases.
+
+ Note: If you need to insert code into the shadow class file
+ created by SWIG, simply use the %shadow directive like this:
+
+ %shadow %{
+ def some_python_code():
+ print "blah!"
+ %}
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/22/2001:beazley
+ Sweeping changes to the way in which the Python module handles
+ shadow classes. In early implementations, shadow classes were
+ merely Python wrappers around typed pointer objects. However,
+ some users actually wanted to receive the shadow class object in C.
+ To accommodate this, the dereferencing of the "this" pointer in
+ a shadow class was moved to C as described in CHANGES [8/8/99].
+ However, the process of returning pointers to Python was still
+ somewhat problematic. Specifically, shadow classes never worked
+ in situations such as these:
+
+ - Use of any kind of output typemap ('out' or 'argout')
+ - Global variables (broken as far as I can tell).
+
+ In the past, some users have dealt with this by manually trying
+ to create shadow class objects themselves from C/C++. However,
+ this was difficult because the C wrappers don't really know how
+ to get access to the corresponding Python class.
+
+ The Python module has now been modified to automatically attach
+ shadow class objects to pointers when they are returned to
+ Python. This process occurs in the function SWIG_NewPointerObj()
+ so the process is completely transparent to users. As a result,
+ shadow classes are now more seamlessly integrated with typemaps
+ and other features of SWIG.
+
+ This change may introduce a number of incompatibilities. The
+ SWIG_NewPointerObj() now takes an extra parameter "own" to
+ indicate object ownership. This can be used to return a pointer
+ to Python that Python should destroy. In addition, older code
+ that tries to manually construct shadow class objects or which
+ expects bare pointers may break---such pointers may already be
+ encapsulated by a shadow class.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/20/2001:beazley
+ Modified the %insert directive to accept single braces { ... }.
+ For example:
+
+ %insert("header") {
+ ... some code ...
+ }
+
+ This works exactly like %{ ... %} except that the code in the
+ braces is processed using the preprocessor. This can be useful
+ in certain contexts such as low-level code generation in
+ language modules.
+ *** NEW FEATURE ***
+
+11/20/2001:beazley
+ Command line options are now translated into preprocessor
+ symbols. For example:
+
+ ./swig -python -shadow -module blah interface.i
+
+ Creates the symbols:
+
+ SWIGOPT_PYTHON 1
+ SWIGOPT_SHADOW 1
+ SWIGOPT_MODULE blah
+
+ Modules can look for these symbols to alter their code generation
+ if needed.
+ *** NEW FEATURE ***
+
+11/20/2001:beazley
+ Massive overhaul of the Perl5 module. A lot of code generation is
+ now driven by tables and typemaps. The generated wrapper code
+ also makes use of tables to install constants, variables, and
+ functions instead of inlining a bunch of procedure calls. The
+ separate variable initialization function is gone. Most
+ code generation is controlled via the perl5.swg file in the
+ library.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/13/2001:beazley
+ Added parsing support for the C++ typename keyword. Primarily this
+ is added to better support templates. For example:
+
+ template<typename T> void blah(C& v) {
+ typename C::iterator i = v.begin();
+ }
+
+ Note: typename is supported in the parser in the same way as 'struct'
+ or 'class'. You probably shouldn't use it anywhere except in templates.
+ *** NEW FEATURE ***
+
+11/11/2001:beazley
+ Massive overhaul of the language module API. Most functions now
+ use a common, very simple, API. There are also a number of
+ interesting semantic side-effects of how code is actually generated.
+ Details will be forthcoming in Doc/Manual/Extending.html.
+
+ *** POTENTIAL INCOMPATIBILITY *** Language modules written for
+ previous versions of SWIG will no longer work,
+
+11/10/2001:beazley
+ Fixed a very subtle bug due to unnamed class wrapping. For example, if
+ you did this
+
+ typedef struct {
+ int x,y;
+ } gdPoint, *gdPointPtr;
+
+ void foo(gdPointPtr x);
+
+ Then the foo function would get a type-error. The problem has
+ to do with internal typedef handling and the fact that the typedef
+ declarations after the struct appear later in the parse tree.
+ It should work now. Problem reported by Vin Jovanovic.
+
+11/09/2001:beazley
+ Subtle change to "out" typemaps (and related variations). The name
+ that is attached to the typemap is now the raw C identifier that
+ appears on a declaration. This changes the behavior of
+ member functions. For example:
+
+ %typemap(out) int foo {
+ ...
+ }
+
+ class Blah {
+ public:
+ int foo(); // typemap gets applied
+ }
+
+ Previous versions never really specified how this was supposed to
+ work. In SWIG1.1, you could probably write a typemap for the
+ wrapper name like this:
+
+ %typemap(out) int Blah_foo { ... }
+
+ However, this old behavior is now withdrawn and not supported.
+ Just use the member name without any sort of special prefix.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/06/2001:beazley
+ Changes to Tcl module initialization:
+
+ (1) SWIG now automatically includes the code needed to work with
+ Tcl stubs. Simply compile with -DUSE_TCL_STUBS.
+
+ (2) SWIG now automatically calls Tcl_PkgProvide to register
+ a package name. The package name is the same as the name
+ specified with the %module directive. The version number is
+ set to "0.0" by default. To change the version number, use
+ swig -pkgversion 1.2 interface.i.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ Modules that provided stubs and Tcl_PkgProvide on their own might
+ break. Simply remove that code.
+
+11/05/2001:beazley
+ Changed code generation of constants in the Tcl module. Constants
+ are now stored in a large table that get installed at module startup.
+ There are also no longer any static variables so it should generate
+ somewhat less code.
+
+11/04/2001:beazley
+ The "const" typemap has been renamed to "constant" in many language
+ modules. "const" is a C keyword which made the handling of the typemap
+ directive somewhat awkward in the parser.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/04/2001:beazley
+ %typemap directive can now accept nearly arbitrary keyword parameters.
+ For example:
+
+ %typemap(in,parse="i",doc="integer") int "..."
+
+ The purpose of the keyword parameters is to supply code generation
+ hints to the target language module. The intepretation of the
+ parameters is language specific.
+ *** NEW FEATURE ***
+
+11/04/2001:beazley
+ Slight semantic change to internal call/return by value handling.
+ In previous versions of SWIG, call-by-value was translated
+ into pointers. For example:
+
+ double dot_product(Vector a, Vector b);
+
+ turned into this:
+
+ double wrap_dot_product(Vector *a, Vector *b) {
+ return dot_product(*a,*b);
+ }
+
+ This translation was normally performed by the SWIG core, outside
+ of the control of language modules. However, a side effect
+ of this was a lot of bizarre typemap behavior. For example,
+ if you did something like this:
+
+ %typemap(in) int32 {
+ ...
+ }
+
+ You would find that int32 was transformed into a pointer everywhere!
+ (needless to say, such behavior is unexpected and quite awkward to
+ deal with). To make matters worse, if a typedef was also used,
+ the pointer behavior suddenly disappeared.
+
+ To fix this, the pointer transformation is now pushed to the
+ language modules. This produces wrappers that look roughly
+ like this:
+
+ double wrap_dot_product(Vector *a, Vector *b) {
+ Vector arg1 = *a;
+ Vector arg2 = *b;
+ return dot_product(arg1,arg2);
+ }
+
+ This change also makes it easy to define typemaps for
+ arbitrary undefined types. For example, you can do this (and it
+ will work regardless what int32 is):
+
+ %typemap(in) int32 {
+ $1 = (int32) PyInt_AsLong($input);
+ }
+
+ *** POTENTIAL IMCOMPATIBILITY ***
+ This change may break call/return by value code generation in
+ some language modules.
+
+11/03/2001:beazley
+ Changed the name of the default typemaps to the following:
+
+ %typemap() SWIGTYPE {
+ ... an object ...
+ }
+ %typemap() SWIGTYPE * {
+ ... a pointer ...
+ }
+ %typemap() SWIGTYPE & {
+ ... a reference ...
+ }
+ %typemap() SWIGTYPE [] {
+ ... an array ...
+ }
+ %typemap() enum SWIGTYPE {
+ ... an enum value ...
+ }
+ %typemap() SWIGTYPE (CLASS::*) {
+ ... pointer to member ...
+ }
+
+
+ These types are used as the default for all types that don't match
+ anything else. See CHANGES log entry for 8/27/2000 for the
+ old behavior. The role of these types is also described in
+ Doc/Manual/Typemaps.html
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+10/25/2001:beazley
+ Modified Guile and Mzscheme modules to support
+ multi-argument typemaps.
+
+10/25/2001: cheetah (william fulton)
+ [Java] Fix to handle pointers to arrays.
+
+10/24/2001:beazley
+ Defining a typemap rule for enum SWIGENUM can now be used
+ to define default behavior for enum variables.
+
+10/22/2001:beazley
+ Ruby module modified to support multi-argument typemaps.
+
+10/22/2001:beazley
+ The Ruby module can now handle functions with an arbitrary
+ number of arguments. Previous versions were limited to
+ to functions with only 9 or 16 arguments depending on
+ the use of default arguments. Note: from some inspection
+ of the Ruby interpreter source, the new approach might be
+ a little faster as well.
+
+10/18/2001:beazley
+ Fixed a bug with forward class declarations and
+ templates.
+
+ class Foo <S,T>;
+
+ Bug reported by Irina Kotlova.
+
+10/16/2001:beazley
+ Support for multivalued typemaps added. The typemaps
+ are specified using the syntax below. Within each
+ typemap, variable substitution is handled as follows:
+
+ %typemap(in) (int argc, char *argv[]) {
+ $arg; // The input object in the target language
+ $1; // C local variable for first argument
+ $2; // C local variable for second argument
+
+ // These variables refer to either argument
+ $1_type, $1_ltype, $1_basetype, etc... (argc)
+ $2_type, $2_ltype, $2_basetype, etc... (argv[])
+
+ // Array dimension of argv
+ $2_dim0
+ }
+
+ Basically any variable that was available in normal typemaps
+ is available for either argument by prefacing the variable
+ name by '$n_' where n is the argument position.
+
+ Notes:
+ (1) Multi-valued typemaps can only be applied to a single
+ object in the target scripting language. For example,
+ you can split a string into a (char *, int) pair or
+ split a list into a (int, char []) pair. It is not
+ possible to map multiple objects to multiple arguments.
+
+ (2) To maintain compatibility with older SWIG versions, the
+ variables such as $target and $type are preserved and
+ are mapped onto the first argument only.
+
+ (3) This should not affect compatibility with older code.
+ Multi-valued typemaps are an extension to typemap handling.
+ Single valued typemaps can be specified in the usual
+ way.
+
+ The old $source and $target variables are officially
+ deprecated. Input variables are referenced through
+ $arg$ and output values are reference through $result$.
+
+ *** NEW FEATURE ***
+
+10/16/2001:beazley
+ Added parsing support for multivalued typemaps. The syntax
+ is a little funky, but here goes:
+
+ // Define a multivalued typemap
+ %typemap(in) (int argc, char *argv[]) {
+ ... typemap code ...
+ }
+
+ // Multivalued typemap with locals
+ %typemap(in) (int argc, char *argv[])(int temp) {
+ ... typemap code ...
+ }
+
+ // Copy a multivalued typemap
+ %typemap(in) (int argcount, char **argv) = (int argc, char *argv[]);
+
+ // Apply a multivalued typemap
+ %apply (int argc, char *argv[]) { (int argcount, char **argv) };
+
+ Note: this extra parsing support is added for future extension.
+ No language modules currently support multi-valued typemaps.
+
+10/11/2001:beazley
+ Modified the typemap matching code to discard qualifiers when
+ checking for a match. For example, if you have a declaration
+ like this:
+
+ void blah(const char *x);
+
+ The typemap checker checks for a match in the following order:
+
+ const char *x
+ const char *
+ char *x
+ char *
+
+ If typedef's are involved, qualifier stripping occurs before
+ typedef resolution. So if you had this,
+
+ typedef char *string;
+ void blah(const string x);
+
+ typemap checking would be as follows:
+
+ const string x
+ const string
+ string x
+ string
+ const char *x
+ const char *
+ char *x
+ char *
+
+ The primary reason for this change is to simplify the implementation
+ of language modules. Without qualifier stripping, one has to write
+ seperate typemaps for all variations of const and volatile (which
+ is a pain).
+
+ *** POTENTIAL INCOMPATIBILITY *** Typemaps might be applied in
+ places where they weren't before.
+
+
+10/9/2001: beazley
+ SWIG now generates wrappers that properly disambiguate
+ overloaded methods that only vary in constness. For
+ example:
+
+ class Foo {
+ ...
+ void blah();
+ void blah() const;
+ ...
+ };
+
+ To handle this, the %rename directive can be used normally.
+
+ %rename(blah_const) blah() const;
+
+ In the resulting wrapper code, method calls like this
+ are now generated:
+
+ (obj)->blah() // Non-const version
+ ((Foo const *)obj)->blah() // const version
+
+ This should force the right method to be invoked.
+ Admittedly, this is probably obscure, but we might
+ as well get it right.
+
+10/8/2001: beazley
+ The preprocessor now ignores '\r' in the input.
+ This should fix the following bug:
+ [ #468416 ] SWIG thinks macro defs are declarations?
+
+10/8/2001: beazley
+ Added support for ||, &&, and ! in constants. This
+ fixes SF [ #468988 ] Logical ops break preprocessor.
+ However, at this time, constants using these operators
+ are not supported (the parser will issue a warning).
+
+10/4/2001: beazley
+ Added -show_templates command line option. This makes
+ SWIG display the code it actually parses to generate
+ template wrappers. Mostly useful for debugging.
+ *** NEW FEATURE ***
+
+10/4/2001: beazley
+ Change to semantics of %template directive. When
+ using %template, the template arguments are handled
+ as types by default. For example:
+
+ %template(vecint) vector<int>;
+ %template(vecdouble) vector<double>;
+
+ To specify a template argument that is *not* a type, you
+ need to use default-value syntax. For example:
+
+ %template(vecint) vector<int,int=50>;
+ %template(vecdouble) vector<int,size=100>;
+
+ In this case, the type name doesn't really matter--only
+ the default value (e.g., 50, 100) is used during
+ expansion. This differs from normal C++, but I couldn't
+ figure out a better way to do it in the parser. Might
+ implement an alternative later.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+10/4/2001: beazley
+ Major changes to template handling in order to provide
+ better integration with the C++ type-system. The main
+ problem is as follows:
+
+ Suppose you have a template like this:
+
+ template<class T> void blah(const T x) { stuff };
+
+ Now suppose, that you instantiate the template on a
+ type like this in SWIG:
+
+ %template(blahint) blah<int *>;
+
+ In C++, this is *supposed* to generate code like this:
+
+ void blah(int *const x) { stuff };
+
+ However, in SWIG-1.3.9, the template substitution gets it wrong
+ and produces
+
+ void blah(const int *x) { stuff };
+
+ (notice the bad placement of the 'const' qualifier).
+
+ To fix this, the SWIG parser now generates implicit typedefs
+ for template type arguments that produces code roughly
+ equivalent to doing this:
+
+ typedef int *__swigtmpl1;
+ %template(blahint) blah<__swigtmpl1>;
+
+ which generates code like this:
+
+ void blah(const __swigtmpl1 x) { stuff };
+
+ Since this is correct in both C++ and SWIG, it provides the right
+ semantics and allows everything to compile properly. However,
+ to clean up the generated code a little bit, the parser keeps
+ track of the template types and performs back-substitution to
+ the original type when building the parse tree. Thus, even
+ though the implicit typedef is used in the input and may appear
+ in the generated wrapper file (for proper compilation), the parse
+ tree will hide a lot of these details. For example:
+
+ void blah(const __swigtmpl1 x) { stuff };
+
+ will look like it was declared as follows (which is what
+ you want):
+
+ void blah(int *const x) { stuff }
+
+ The only place you are likely to notice the typedef hack
+ is in bodies of template functions. For example, if you
+ did this,
+
+ template<class T> class blah {
+ ...
+ %addmethods {
+ void spam() {
+ T tempvalue;
+ ...
+ }
+ }
+ }
+
+ you will find that 'T tempvalue' got expanded into some
+ strange typedef type. This *still* compiles correctly
+ so it's not a big deal (other than looking kind of ugly
+ in the wrapper file).
+
+10/4/2001: beazley
+ Fixed some inheritance problems in Tcl Object interface.
+
+10/1/2001: beazley
+ Tcl module has changed to use byte-backed pointer strings. This
+ implementation should be safe on 64-bit platforms. However,
+ the order in which digits appear in pointer values no longer
+ directly corresponds to the actual numerical value of a
+ pointer (on little-endian machines, pairs of digits appear
+ in reverse order).
+
+10/1/2001: beazley
+ Perl5 module is now driven by a configuration file 'perl5.swg'
+ in the SWIG library.
+
+10/1/2001: beazley
+ The perl5 module no longer tries to apply the "out" typemap
+ in code generated for magic variables. I'm surprised that
+ this ever worked at all (since all of the code that was there
+ was wrong anyways). Use the "varout" typemap to handle
+ global variables.
+
+10/1/2001: beazley
+ Fixed a bug related to character array members of structures.
+ For example:
+
+ struct Foo {
+ char name[32];
+ };
+
+ SWIG is normally supposed to return a string, but this was
+ broken in 1.3.9. The reason it was broken was actually
+ due to a subtle new feature of typemaps. When a data member
+ is set to an array like this, the return type of the related
+ accessor function is actually set to an array. This means
+ that you can now write typemaps like this:
+
+ %typemap(python,out) char [ANY] {
+ $target = PyString_FromStringAndSize($source,$dim0);
+ }
+
+ This functionality can be used to replace the defunct
+ memberout typemap in a more elegant manner.
+
+9/29/2001: beazley
+ Some further refinement of qualified C++ member functions.
+ For example:
+
+ class Foo {
+ ...
+ void foo() const;
+ ...
+ };
+
+ (i) The SWIG parser was extended slightly to allow 'volatile'
+ and combinations of 'const' and 'volatile' to be used. This
+ is probably rare, but technically legal. Only added for
+ completeness.
+
+ (ii) For the purposes of overloading, qualified and non-qualified
+ functions are different. Thus, when a class has methods like this:
+
+ void foo();
+ void foo() const;
+
+ Two distinct methods are declared. To deal with this, %rename
+ and similar directives have been extended to recognize const.
+ Thus, one can disambiguate the two functions like this:
+
+ %rename(fooconst) Foo::foo() const;
+
+ or simply ignore the const variant like this:
+
+ %ignore Foo::foo() const;
+
+ Note: SWIG currently has no way to actually invoke the const
+ member since the 'const' is discarded when generating wrappers
+ for objects.
+
+9/27/2001: beazley
+ New directive. %namewarn can be used to issue warning
+ messages for certain declaration names. The name
+ matching is the same as for the %rename directive.
+ The intent of this directive is to issue warnings for
+ possible namespace conflicts. For example:
+
+ %namewarn("print is a python keyword") print;
+
+ The name matching algorithm is performed after a name
+ has been resolved using %rename. Therefore, a
+ declaration like this will not generate a warning:
+
+ %rename("Print") print;
+ ...
+ void print(); /* No warning generated */
+
+ Since the warning mechanism follows %rename semantics, it is
+ also to issue warnings for specific classes or just for
+ certain member function names.
+
+ (Dave - I've been thinking about adding something like this
+ for quite some time. Just never got around to it)
+ *** NEW FEATURE ***
+
+
+9/27/2001: beazley
+ Enhanced the %ignore directive so that warning messages
+ can be issued to users. This is done using %ignorewarn
+ like this:
+
+ %ignorewarn("operator new ignored") operator new;
+
+ The names and semantics of %ignorewarn is exactly the
+ same as %ignore. The primary purpose of this directive
+ is for module writers who want to ignore certain types
+ of declarations, but who also want to alert users about it.
+ A user might also use this for debugging (since messages
+ will appear whenever an ignored declaration appears).
+ *** NEW FEATURE ***
+
+9/26/2001: beazley
+ Super-experimental support for overloaded operators.
+ This implementation consists of a few different parts.
+
+ (i) Operator names such as 'operator+' are now allowed
+ as valid declarator names. Thus the 'operator' syntax
+ can appear *anyplace* a normal declarator name was used
+ before. On the surface, this means that operators can
+ be parsed just like normal functions and methods.
+ However, it also means that operator names can be used
+ in many other SWIG directives like %rename. For example:
+
+ %rename(__add__) Complex::operator+(const Complex &);
+
+ (ii) Operators are wrapped *exactly* like normal functions
+ and methods. Internally, the operator name is used
+ directly meaning that the wrapper code might contain
+ statements like this:
+
+ arg0->operator*((Complex const &)*arg1);
+
+ This all seems to parse and compile correctly (at least
+ on my machine).
+
+ (iii) SWIG will no longer wrap a declaration if its symbol
+ table name contains illegal identifier characters. If
+ illegal characters are detected, you will see an error
+ like this:
+
+ Warning. Can't wrap operator* unless renamed to a valid identifier.
+
+ The only way to fix this is to use %rename or %name to bind
+ the operator to a nice name like "add" or something. Note:
+ the legal identifier characters are determined by the target
+ language.
+
+ There are certain issues with friend functions and operators.
+ Sometimes, friends are used to define mixed operators such
+ as adding a Complex and a double together. Currently, SWIG
+ ignores all friend declarations in a class. A global operator
+ declaration can probably be made to work, but you'll have to
+ rename it and it probably won't work very cleanly in the
+ target language since it's not a class member.
+
+ SWIG doesn't know how to handle operator specifications
+ sometimes used for automatic type conversion. For example:
+
+ class String {
+ ...
+ operator const char*();
+ ...
+ };
+
+ (this doesn't parse correctly and generates a syntax error).
+
+ Also: operators no longer show up as separate parse-tree
+ nodes (instead they are normal 'cdecl' nodes). I may
+ separate them as a special case later.
+
+ See Examples/python/operator for an example.
+
+ *** SUPER-EXPERIMENTAL NEW FEATURE ***
+
+Version 1.3.9 (September 25, 2001)
+==================================
+
+9/25/2001: beazley
+ Fixed parsing problem with type declarations like
+ 'char ** const'. SWIG parsed this correctly, but the
+ internal type was represented incorrectly (the pointers
+ and qualifiers were in the wrong order).
+
+9/25/2001: beazley
+ Withdrew experimental feature (noted below) that was
+ causing serious parsing problems.
+
+Version 1.3.8 (September 23, 2001)
+==================================
+
+9/23/2001: beazley
+ Included improved distutils setup.py file in the Tools
+ directory (look for the setup.py.tmpl file). Contributed by
+ Tony Seward.
+
+9/23/2001: beazley
+ Included two new RPM spec files in the Tools directory. Contributed
+ by Tony Seward and Uwe Steinmann.
+
+9/21/2001: beazley
+ Fixed SF Bug [ #463635 ] Perl5.swg does not compile in Visual C++
+
+9/21/2001: beazley
+ Two new directives control the creation of default
+ constructors and destructors:
+
+ %nodefault
+ %makedefault
+
+ These replace %pragma nodefault and %pragma makedefault.
+ (old code will still work, but documentation will only
+ describe the new directives).
+
+9/21/2001: beazley
+ Fixed SF Bug [ #462354 ] %import broken in 1.3.7.
+
+9/20/2001: beazley
+
+ Parser modified to ignore out-of-class constructor
+ and destructor declarations. For example:
+
+ inline Foo::Foo() :
+ Bar("foo")
+ {
+ }
+
+ inline Foo::~Foo() {
+ }
+
+ Suggested by Jason Stewart.
+ *** EXPERIMENTAL FEATURE ***
+
+9/20/2001: beazley
+ Modified the parser to ignore forward template class
+ declarations. For example:
+
+ template <class V, long S> class MapIter;
+
+ Suggested by an email example from Irina Kotlova.
+
+9/20/2001: beazley
+ Fixed problem with undeclared tcl_result variable in
+ the "out" typemap for Tcl. Reported by Shaun Lowry.
+
+9/20/2001: beazley
+ Incorporated changes to make SWIG work with ActivePerl.
+ Contributed by Joel Reed.
+
+9/20/2001: beazley
+ Slight change to the parsing of C++ constructor initializers.
+ For example:
+
+ class Foo : public Bar {
+ public:
+ Foo() : Bar(...) {...}
+ };
+
+ SWIG now discards the contents of the (...) regardless of
+ what might enclosed (even if syntactically wrong). SWIG
+ doesn't need this information and there is no reason to
+ needless add syntax rules to handle all of the possibilities
+ here.
+
+9/20/2001: beazley
+ Change to typemaps for structure members. If you have a
+ structure like this:
+
+ struct Vector {
+ int *bar;
+ };
+
+ The member name 'bar' is now used in any accessor functions.
+ This allows the "in" typemap to be used when setting the value.
+ For example, this typemap
+
+ %typemap(python,in) int *bar {
+ ...
+ }
+
+ now matches Vector::bar. It should be noted that this will also
+ match any function with an argument of "int *bar" (so you should
+ be careful).
+ *** NEW FEATURE. POTENTIAL INCOMPATIBILITY ***
+
+9/20/2001: beazley
+ Fixed SF bug #462642 setting string values in structures
+
+9/20/2001: beazley
+ Fixed SF bug #462398 problem with nested templates.
+
+9/20/2001: beazley
+ Fixed SF bug #461626 problem with formatting and C++ comments.
+
+9/20/2001: beazley
+ Fixed SF bug #462845 Wrong ownership of returned objects.
+
+9/19/2001: beazley
+ Fixed SF bug #459367. Default constructors for classes
+ with pure virtual methods.
+
+9/19/2001: beazley
+ Fixed problem with default arguments and class scope. For
+ example:
+
+ class Foo {
+ public:
+ enum bar { FOO, BAR };
+ void blah(bar b = FOO);
+ ...
+ }
+
+ SWIG now correctly generates a default value of "Foo::FOO" for
+ the blah() method above. This used to work in 1.1, but was
+ broken in 1.3.7. Bug reported by Mike Romberg.
+
+Version 1.3.7 (September 3, 2001)
+==================================
+
+9/02/2001: beazley
+ Added special %ignore directive to ignore declarations. This
+ feature works exactly like %rename. For example:
+
+ %ignore foo; // Ignore all declarations foo
+ %ignore ::foo; // Only ignore foo in global scope
+ %ignore Spam::foo; // Only ignore in class Spam
+ %ignore *::foo; // Ignore in all classes
+
+ %ignore can also be parameterized. For example:
+
+ %ignore foo(int);
+ %ignore ::foo(int);
+ %ignore Spam::foo(int);
+ %ignore *::foo(int);
+
+ *** NEW FEATURE ***
+
+
+9/02/2001: cheetah (william fulton)
+ [Java] shadowcode pragma modified so that the code that is output
+ in the shadow file is placed relative to where it is placed in the
+ c/c++ code. This allows support for JavaDoc function comments.
+
+9/01/2001: beazley
+ Fixed SF Patch [ #447791 ] Fix for python -interface option.
+ Submitted by Tarn Weisner Burton.
+
+9/01/2001: beazley
+ SWIG no longer generates default constructors/destructors
+ for a class if it only defines a private/protected constructor
+ or destructor or if any one of its base classes only has
+ private constructors/destructors. This was reported in
+ SF Patch [ #444281 ] nonpublic/default/inhereted ctor/dtor
+ by Marcelo Matus.
+
+9/01/2001: beazley
+ Added patch to Perl5 module that allows constants to be
+ wrapped as constants that don't require the leading $.
+ This feature is enabled using the -const option.
+ Patch contributed by Rich Wales.
+ *** NEW FEATURE ***
+
+8/31/2001: beazley
+ Added parsing support for the 'volatile' type qualifier.
+ volatile doesn't mean anything to SWIG, but it is
+ needed to properly generate prototypes for declarations
+ that use it. It's also been added to make the SWIG type
+ system more complete.
+ *** NEW FEATURE ***
+
+8/30/2001: beazley
+ Added support for parameterized %rename directive. *** This
+ new feature can be used to greatly simplify the task of
+ resolving overloaded methods and functions. ***
+
+ In prior versions of SWIG, the %rename directive was
+ used to consistently apply an identifier renaming. For
+ example, if you said this:
+
+ %rename foo bar;
+
+ Every occurrence of 'foo' would be renamed to 'bar'.
+ Although this works fine for resolving a conflict with a
+ target language reserved word, it is useless for
+ for dealing with overloaded methods. This is because
+ all methods are simply renamed to the same thing
+ (generating the same conflict as before).
+
+ Therefore, the only way to deal with overloaded methods
+ was to go through and individually rename them all using
+ %name. For example:
+
+ class Foo {
+ public:
+ virtual void bar(void);
+ %name(bar_i) virtual void bar(int);
+ ...
+ };
+
+ To make matters worse, you had to do this for all
+ derived classes too.
+
+ class Spam : public Foo {
+ public:
+ virtual void bar(void);
+ %name(bar_i) virtual void bar(int);
+ ...
+ };
+
+ Needless to say, this makes it extremely hard to resolve
+ overloading without a lot of work and makes it almost
+ impossible to use SWIG on raw C++ .h files.
+
+ To fix this, %rename now accepts parameter declarators.
+ The syntax has also been changed slightly. For example,
+ the following declaration renames all occurrences of 'bar(int)'
+ to 'bar_i', leaving any other occurrence of 'bar' alone.
+
+ %rename(bar_i) bar(int);
+
+ Using this feature, you can now selectively rename
+ certain declarations in advance. For example:
+
+ %rename(bar_i) bar(int);
+ %rename(bar_d) bar(double);
+
+ // Include raw C++ header
+ %include "header.h"
+
+ When %rename is used in this manner, all occurrence of bar(int)
+ are renamed wherever they might occur. More control is obtained
+ through explicit qualification. For example,
+
+ %rename(bar_i) ::bar(int);
+
+ only applies the renaming if bar(int) is defined in the global scope.
+ The declaration,
+
+ %rename(bar_i) Foo::bar(int);
+
+ applies the renaming if bar(int) is defined in a class Foo.
+ This latter form also supports inheritance. Therefore, if you
+ had a class like this:
+
+ class Spam : public Foo {
+ public:
+ void bar(int);
+ }
+
+ The Spam::bar(int) method would also be renamed (since Spam
+ is a subclass of Foo). This latter feature makes it easy
+ for SWIG to apply a consistent renaming across an entire
+ class hierarchy simply by specifying renaming rules for
+ the base class.
+
+ A class wildcard of * can be used if you want to renaming
+ all matching members of all classes. For example:
+
+ %rename(bar_i) *::bar(int);
+
+ will rename all members bar(int) that are defined in classes.
+ It will not renamed definitions of bar(int) in the global
+ scope.
+
+ The old use of %rename is still supported, but is somewhat
+ enhanced.
+
+ %rename(foo) bar; // Renames all occurrences of 'bar'.
+ %rename(foo) ::bar; // Rename all 'bar' in global scope only.
+ %rename(foo) *::bar; // Rename all 'bar' in classes only.
+ %rename(foo) Foo::bar; // Rename all 'bar' defined in class Foo.
+
+ *** NEW FEATURE ***
+
+8/30/2001: beazley
+ Added support for data-member to member-function
+ transformation. For example, suppose you had a
+ structure like this:
+
+ struct Vector {
+ double x,y;
+ };
+
+ Now suppose that you wanted to access x and y
+ through a member function interface instead
+ of the usual SWIG behavior. For example:
+
+ f.set_x(3.4) # instead of f.x = 3.4
+ x = f.get_x() # instead of x = f.x
+
+ To do this, simply use the new %attributefunc
+ directive. For example:
+
+ %attributefunc(get_%s,set_%s)
+ struct Vector {
+ double x,y;
+ };
+ %noattributefunc
+
+ The arguments to %attributefunc are C-style printf
+ format strings that determine the naming convention
+ to use. %s is replaced with the actual name of the
+ data member. SWIG provides a number of printf
+ extensions that might help. For example, if you
+ wanted to title case all of the attributes, you
+ could do this:
+
+ %attributefunc(get%(title)s,set%(title)s);
+
+ This will turn an attribute 'bar' to 'getBar()' and 'setBar()'.
+
+ (someone requested this long ago, but I finally figured
+ how to implement it in a straightforward manner).
+ *** EXPERIMENTAL NEW FEATURE ***
+
+8/30/2001: beazley
+ SWIG now automatically generates default constructors
+ and destructors if none are defined. This used to be
+ enabled with a command line switch -make_default, but
+ most people want these functions anyways. To turn
+ off this behavior use the -no_default option or include
+ the following pragma in the interface file:
+
+ %pragma no_default;
+
+ This may break certain interfaces that defined their
+ own constructors/destructors using the same naming
+ convention as SWIG. If so, you will get duplicate
+ symbols when compiling the SWIG wrapper file.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+8/29/2001: beazley
+ Changes to Perl5 shadow class code generation. Iterators
+ are no longer supported (FIRSTKEY, NEXTKEY). Also, attribute
+ access has been changed to rely on inheritance in order
+ to provide better behavior across modules.
+
+8/28/2001: beazley
+ Various obscure improvements to the type system and classes.
+ Strange declarations like this are now wrapped correctly
+ (i.e., the generated wrapper code doesn't cause the C++
+ compiler to die with a type error).
+
+ class Foo {
+ public:
+ typedef double Real;
+ Real foo(Real (*op)(Real,Real), Real x, Real y);
+ };
+
+ Inheritance of types is also handled correctly.
+
+8/28/2001: beazley
+ Changes to class wrappers. When SWIG sees two classes like this,
+
+ class X {
+ public:
+ void foo();
+ ...
+ }
+
+ class Y : public X {
+ public:
+ void bar();
+ ...
+ }
+
+ it now only generates two wrapper functions:
+
+ X_foo(X *x) { x->foo(); }
+ Y_bar(Y *y) { y->bar(); }
+
+ Unlike SWIG1.15, the foo() method does *not* propagate to a wrapper
+ function Y_foo(). Instead, the base class method X_foo() must be
+ used.
+
+ This change should not affect modules that use shadow classes, but
+ it might break modules that directly use the low-level C wrappers.
+ This change is being made for a number of reasons:
+
+ - It greatly simplifies the implementation of SWIG--especially
+ with anticipated future changes such as overloaded methods.
+
+ - It results in substantially less wrapper code--especially
+ for big C++ class hierarchies (inherited declarations
+ are no longer copied into every single derived class).
+
+ - It allows for better code generation across multiple
+ SWIG generated modules (code isn't replicated in
+ every single module).
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+8/22/2001: cheetah (william fulton)
+ Provided some Windows documentation in the Win directory and some
+ Visual C++ project files for running examples on Windows.
+
+8/28/2001: mkoeppe
+ [Guile] Handle renamed overloaded functions properly;
+ thanks to Marc Zonzon <Marc.Zonzon@univ-rennes1.fr> for the
+ patch. See the new test case name_cxx.
+
+8/27/2001: mkoeppe
+ [Tcl] Removed lots of warnings issued by the Sun Forte
+ compilers, which were caused by mixing function pointers
+ of different linkages (C++/C).
+
+8/23/2001: mkoeppe
+ Improved the MzScheme module by porting Guile's pointer
+ type checking system and making type dispatch
+ typemap-driven.
+
+8/22/2001: beazley
+ Entirely new symbol table processing. SWIG should be able to
+ report much better error messages for multiple declarations.
+ Also, the new symbol table allows for overloaded functions
+ (although overloading isn't quite supported in the language
+ modules yet).
+
+8/22/2001: cheetah (william fulton)
+ * [Java] %new support added.
+ * [Java] Package JNI name refixed!
+
+8/19/2001: beazley
+ Python module modified to support pointers to C++ members. This
+ is an experimental feature.
+ *** NEW FEATURE ***
+
+8/19/2001: beazley
+ Added limited parsing and full type-system support for pointers to
+ members. None of SWIG's language modules really know how to deal with
+ this so this is really only provided for completeness and future
+ expansion. Note: SWIG does not support pointers to members which
+ are themselves pointers to members, references to pointers to members,
+ or other complicated declarations like this.
+ *** NEW FEATURE ***
+
+8/19/2001: beazley
+ SWIG is much better at parsing certain C++ declarations. Operators and
+ friends generally don't cause anymore syntax errors. However, neither
+ are really supported.
+
+8/18/2001: beazley
+ Added *highly* experimental support for wrapping of C++
+ template declarations. Since C++ templates are essentially
+ glorified macros and SWIG has a fully operational C
+ preprocessor with macro support, the parser now converts
+ template declarations to macros. For example, a function
+ template like this
+
+ template<class T> T max(T a, T b);
+
+ is internally converted into a macro like this:
+
+ %define %_template_max(__name,T)
+ %name(__name) T max(T a, T b);
+ %enddef
+
+ To instantiate a version of the template, a special %template declaration
+ is used like this:
+
+ %template(maxint) max<int>;
+ %template(maxdouble) max<double>;
+
+ The parameter to the %template directive must be proper C identifier that's
+ used to uniquely name the resulting instantiation. When used, the
+ the expanded macro looks like this:
+
+ %name(maxint) int max(int a, int b);
+ %name(maxdouble) double max(double a, double b);
+
+ A similar technique is used for template classes. For instance:
+
+ template<class T> class vector {
+ T *data;
+ int sz;
+ public:
+ vector(int nitems);
+ T *get(int n);
+ ...
+ };
+
+ Gets converted into a macro like this:
+
+ %define %_template_vector(__name, T)
+ %{
+ typedef vector<T> __name;
+ %}
+ class __name {
+ T *data;
+ int sz;
+ public:
+ __name(int nitems);
+ T *get(int n);
+ ...
+ };
+ typedef __name vector<T>;
+ %enddef
+
+ A specific instantiation is created in exactly the same way:
+
+ %template(intvec) vector<int>;
+
+ The resulting code parsed by SWIG is then:
+
+ %{
+ typedef vector<int> intvec;
+ %}
+ class intvec {
+ int *data;
+ int sz;
+ public:
+ intvec(int nitems);
+ int *get(int n);
+ ...
+ };
+ typedef intvec vector<int>;
+
+ Note: the last typedef is non-standard C and is used by SWIG to provide
+ an association between the name "intvec" and the template type
+ "vector<int>".
+
+ CAUTION: This is an experimental feature and the first time SWIG has
+ supported C++ templates. Error reporting is essential non-existent.
+ It will probably break in certain cases.
+ *** EXPERIMENTAL NEW FEATURE ****
+
+8/15/2001: beazley
+ Change to wrapping of multi-dimensional arrays. Arrays
+ are now properly mapped to a pointer to an array of
+ one less dimension. For example:
+
+ int [10]; --> int *
+ int [10][20]; --> int (*)[20];
+ int [10][20][30]; --> int (*)[20][30];
+
+ This change may break certain SWIG extensions because
+ older versions simply mapped all arrays into a single
+ pointer such as "int *". Although possibly unusual,
+ the new version is correct in terms of the C type system.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+8/06/2001: cheetah (william fulton)
+ * [Java] Array setters generated for struct/class array members.
+
+8/13/2001: beazley
+ Many improvements to Tcl/Perl/Python modules to better
+ work with multiple interface files and the %import directive.
+
+8/13/2001: beazley
+ Fixed up the behavior of %import in the Python module.
+ SWIG no longer pollutes the module namespace by using
+ 'from module import *' to refer to the other module.
+ Instead, it does a proper 'import module'. Also, SWIG
+ may work a lot better when importing modules that include
+ references to other imported modules.
+
+8/13/2001: mkoeppe
+ Added new typemap substitutions, generalizing those of the
+ Guile-specific 5/27/2001 changes:
+ * $descriptor is the same as SWIGTYPE$mangle, but also
+ ensures that the type descriptor of this name gets
+ defined.
+ * $*type, $*ltype, $*mangle, $*descriptor are the same as
+ the variants without star, but they REMOVE one level of
+ pointers from the type. (This is only valid for pointer
+ types.)
+ * $&type, $&ltype, $&mangle, $&descriptor are the same as
+ the variants without ampersand, but they ADD one level of
+ pointers to the type.
+ The Guile-specific substitution $basedescriptor was removed
+ because it was useless.
+
+8/12/2001: beazley
+ The %extern directive is now deprecated and withdrawn. The
+ purpose of this directive was to import selected definitions
+ from other interface files and headers. However, the same
+ functionality is better handled through %import. This
+ leaves SWIG with two file inclusion directives:
+
+ %include filename - Inserts into current interface
+ %import filename - Import types and classes from
+ another module
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+8/09/2001: beazley
+ Added new support for wrapping C/C++ callback functions.
+ A common problem with some C libraries is that many
+ functions take a function pointer as an argument. For example:
+
+ int do_op(..., int (*op)(int,int), ...);
+
+ Unfortunately, the only way to call such a function is to
+ pass it a function pointer of some compatible type. In
+ previous versions of SWIG, you had to solve this problem
+ with some really gross hacks. For example, if you wanted to
+ use the following function as a callback,
+
+ int foo(int, int);
+
+ you had to install a pointer to it as a constant. For example:
+
+ %constant int (*FOO)(int,int) = foo;
+
+ or
+
+ const int (*FOO)(int,int) = foo;
+
+ or if you had a really old SWIG version:
+
+ typedef int (*OP_FUNC)(int,int);
+ int do_op(..., OP_FUNC, ...);
+ const OP_FUNC FOO = foo;
+
+
+ Now, you can do one of two things:
+
+ %constant int foo(int,int);
+
+ This creates a constant 'foo' of type int (*)(int,int).
+ Alternatively, you can do this:
+
+ %callback("%s");
+ int foo(int,int);
+ int bar(int,int);
+ %nocallback;
+
+ In this case, the functions are installed as constants where
+ the name is defined by the format string given to %callback().
+ If the names generated by the format string differ from the
+ actual function name, both a function wrapper and a callback
+ constant are created. For example:
+
+ %callback("%(upper)s");
+ int foo(int,int);
+ int bar(int,int);
+ %nocallback;
+
+ Creates two wrapper functions 'foo', 'bar' and additionally
+ creates two callback constants 'FOO', 'BAR'.
+
+ Note: SWIG still does not provide automatic support for
+ writing callback functions in the target language.
+ *** NEW FEATURE ***
+
+8/06/2001: cheetah (william fulton)
+ * struct nesting fixes as per SF bug #447488.
+
+8/03/2001: beazley
+ The %name directive now applies to constants created with
+ #define and %constant. However, most language modules
+ were never written to support this and will have to be
+ modified to make it work. Tcl, Python, and Perl modules
+ are working now.
+ *** NEW FEATURE ***
+
+8/03/2001: beazley
+ Massive changes and simplification of C declaration parsing.
+ Although SWIG is still not a full C parser, its ability
+ to handle complex datatypes including pointers to functions
+ and pointers to arrays has been vastly improved.
+
+8/03/2001: cheetah (william fulton)
+ * Distribution fixes: autoconf no longer needed to install SWIG.
+
+8/02/2001: beazley
+ Removed two undocumented parsing features. SWIG no longer
+ supports out-of-class static function or variable
+ declarations. For example:
+
+ static int Foo::bar;
+
+ This feature may return if there is sufficient demand.
+ However, since SWIG is most often used with header files,
+ it is more likely for these definitions to be included
+ in the class definition.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+8/02/2001: cheetah (william fulton)
+ * Cleanup of the GIFPlot examples. Upgraded Java GIFPlot example.
+
+8/01/2001: cheetah (william fulton)
+ * [Java] Efficiency changes: _cPtr used where possible rather than
+ getCPtr(). Bug fixes for inheritance - derived class sometimes
+ didn't delete the c memory when _delete() was called.
+ * [Java] Abstract c++ classes are wrapped with a java abstract shadow
+ class. Also a pure virtual function is mapped with an abstract method.
+ * The default output file has always been <module>_wrap.c. It is now
+ <module>_wrap.cxx if the -c++ commandline option is passed to swig.
+ This has been done as otherwise c++ code would appear in a c file.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/31/2001: beazley
+ Modified the %constant directive to be more C-like in syntax.
+ The syntax is now:
+
+ %constant NAME = VALUE;
+ %constant TYPE NAME = VALUE;
+
+ For example:
+
+ %constant Foo *Bar = &Spam;
+
+ A more subtle case is as follows:
+
+ %constant int (*FOO)(int,int) = blah;
+
+ *** POTENTIAL INCOMPATIBILITY *** Modules that were using
+ the %constant directive directly will need to be modified.
+
+7/30/2001: beazley
+ Removed obscure and undocumented form of the %inline directive:
+
+ %inline int blah(int a, int b) {
+ ...
+ }
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ (note: this feature was never documented and is withdrawn)
+
+7/30/2001: beazley
+ Removed support for functions with no explicitly declared
+ return type. For example:
+
+ foo(int);
+
+ In C, such functions were implicitly assumed to return an 'int'.
+ In C++, this is illegal. Either way, it's considered bad
+ style. Removing support for this in SWIG will simplify
+ certain issues in parsing.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/30/2001: mkoeppe
+ * Partial merge from the CVS trunk. The Source/DOH directory
+ and most of the Source/Swig directory is up-to-date now.
+ * [Guile] %scheme is now a macro for %insert("scheme").
+ New syntax: %scheme "FILENAME";
+ New syntax: %scheme %{ SCHEME-CODE %}
+ New macros %multiple_values, %values_as_list,
+ %values_as_vector.
+
+7/29/2001: beazley
+ %readonly and %readwrite have been turned into SWIG pragmas.
+ %pragma(swig) readonly and %pragma(swig) readwrite. Macros
+ are used to provide backwards compatibility.
+
+7/29/2001: beazley
+ Minor changes to %pragma directive. %pragma must always
+ be directed to a specific language. For example:
+
+ %pragma(swig) make_default;
+ %pragma(perl5) include = "blah.i";
+
+ Also extended the pragma directive to allow code blocks
+
+ %pragma(foo) code = %{
+ ... some code ...
+ %}
+
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/29/2001: beazley
+ Change to the way 'const' variables are wrapped. In
+ previous versions of SWIG, a 'const' variable was
+ wrapped as a constant. Now, 'const' variables are
+ wrapped as read-only variables. There are several
+ reasons for making this change, mostly pertaining to
+ subtle details of how 'const' actually works.
+
+ This will probably break old interfaces that used 'const'
+ to create constants. As a replacement, consider using this:
+
+ const int a = 4; ===> %constant int a = 4;
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/29/2001: beazley
+ Reorganization and simplification of type parsing.
+ Types with 'const' should work correctly now.
+
+7/29/2001: beazley
+ Most swig directives related to the documentation system
+ are now deprecated.
+
+7/29/2001: beazley
+ Removed support for Objective-C in order to simplify
+ parser reconstruction. Will return if there is sufficient
+ demand.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/29/2001: beazley
+ Code inclusion has been modified in the parser. A common
+ directive %insert is now used for everything. This
+ inserts a file into the output:
+
+ %insert(header) "foo.swg"
+
+ This inserts some inline code into the output
+
+ %insert(header) %{
+ ... some code ...
+ %}
+
+ There are five predefined targets for the insert directive:
+
+ "header" - Header section of wrapper file
+ "runtime" - Runtime section of wrapper file
+ "wrapper" - Wrapper section
+ "init" - Initialization function
+ "null" - Nothing. Discard.
+
+ The following directives are still supported, but are
+ now defined in terms of macros:
+
+ %{ ... %} -> %insert(header) %{ ... %}
+ %init %{ ... %} -> %insert(init) %{ ... %}
+ %wrapper %{ ... %} -> %insert(wrapper) %{ ... %}
+ %runtime %{ ... %} -> %insert(runtime) %{ ... %}
+
+ Language modules can define new named targets by using the
+ C API function Swig_register_filebyname() (see main.cxx).
+ For example, if you wanted to expose a shadow class file,
+ you could do this:
+
+ Swig_register_filebyname("shadow", f_shadow);
+
+ Then in the interface file:
+
+ %insert(shadow) %{ ... %}
+
+ Note: this change should not affect any old interfaces, but
+ does open up new possibilities for enhancements.
+
+7/29/2001: beazley
+ SWIG now always includes a standard library file 'swig.swg'.
+ This file defines a large number of macro definitions
+ that define the behavior of various SWIG directives.
+ Previously, all SWIG directives were handled as special
+ cases in the parser. This made the parser a large
+ bloated mess. Now, the parser is stripped down to a few
+ simple directives and macros are used to handle everything else.
+
+7/26/2001: cheetah (william fulton)
+ * Fixes for Sourceforge bug #444748 - new testcase cpp_static:
+ [TCL] Class with just static member variable/function fix
+ [Java] Fixed static variables support
+ [Ruby] Static variables workaround removed
+
+7/27/2001: mkoeppe
+ * stype.c (SwigType_default): Strip qualifiers first. The
+ default type of "int * const" is now "SWIGPOINTER *".
+ * main.cxx: Define "__cplusplus" in SWIG's preprocessor if
+ in C++ mode.
+ * [Guile]: Added some support for arrays and C++
+ references, fixing the "constant_pointers" test case.
+ * Moved most tests from the old Guile-specific test-suite
+ to the new test-suite. Also moved perl5/pointer-cxx
+ example there.
+
+7/26/2001: cheetah (william fulton)
+ * Test-suite added.
+ * Initial testcases: constant_pointers cpp_enum defines
+ sizeof_pointers unions virtual_destructor
+ * Make clean improvements.
+
+7/24/2001: cheetah (william fulton)
+ * [Java] Underscores in the package name and/or module name
+ no longer give linking problems.
+
+7/17/2001: cheetah (william fulton)
+ * More parser bug fixes for constant pointers
+
+7/19/2001: mkoeppe
+ * [Guile] Aesthetic improvement in variable wrappers.
+
+7/18/2001: beazley
+ * Fixed core-dump problem in pointer library when
+ freeing character arrays.
+ SF Bug [ #415837 ] pointer lib core dump
+
+7/18/2001: beazley
+ * Fixed problem with default destructors and shadow
+ classes. SF bug #221128.
+
+7/18/2001: beazley
+ * To provide better line-number tracking in interfaces
+ with lots of macros, special locator comments are
+ now generated by the SWIG preprocessor. For example:
+
+ /*@foo.i,42,BLAH@*/expanded macro/*@@*/
+
+ The first /*@...@*/ sequence sets the context
+ to point to the macro code. The /*@@*/ comment
+ terminates the context. The SWIG parser should
+ ignore all of the locator comments as should
+ the C compiler (should such comments end up
+ in generated wrapper code).
+
+7/18/2001: mkoeppe
+ * The parser now handles severely constified types in
+ typemaps. This introduced a new shift/reduce conflict, but
+ only with a heuristic function-pointer catch-all rule.
+ * [Guile]: Added typemaps for severely constified types.
+ * Fixed the "template-whitespace" problem by canonicalizing
+ whitespace, especially around angle brackets and commas.
+
+7/17/2001: mkoeppe
+ * [Guile]: A Scheme file is emitted if the -scmstub FILE.SCM
+ command-line option is used. The %scheme directive
+ (implemented as a macro for a pragma) allows to insert
+ arbitrary code here. In "simple" and "passive" linkage,
+ the file gets filled with define-module and export
+ declarations.
+
+7/17/2001: cheetah (william fulton)
+ * Parser bug fix to support constant pointers, eg int* const ptr.
+ Fixed everywhere - variables, parameters, return types etc. Note that
+ when wrapping a constant pointer variable only the getter is generated.
+
+7/17/2001: mkoeppe
+ * Fixed SF bug #441470 (#define X "//" would not be parsed,
+ see test-suite entry "preproc-1"), reported by T. W. Burton
+ <twburton@users.sf.net>.
+ * Changed the type of character constants to "char", rather
+ than "char *". Changed the individual language modules
+ to keep the old behaviour, except for the Guile module,
+ where it is desired to make them Scheme characters. This
+ fixes SF bug #231409, test-suite entry "char-constant".
+ * Applied patch for DOH/Doh/memory.c by Les Schaffer
+ <schaffer@optonline.net> (avoid required side effects in
+ assert).
+
+7/17/2001: cheetah (william fulton)
+ * Bug fix in parser for virtual destructor with void as parameter
+ * Bug fix in parser #defines embedded within classes/structs/unions
+ Consequently %constant can now also be placed within a struct/class/union.
+ * Bug fix in parser to allow sizeof(*I_am_a_pointer) within a #define
+
+7/16/2001: mkoeppe
+ * Added changes for the Macintosh contributed by Luigi
+ Ballabio <ballabio@mac.com>.
+ * Some "const" fixes in the code.
+ * [Guile]: Made the constant-wrapper functions much shorter.
+
+7/13/2001: mkoeppe
+ * [Guile]: Some "const" fixes for Guile version 1.3.4.
+ * Handle anonymous arguments with default values and static
+ array members of classes. Both bugs reported by Annalisa Terracina
+ <annalisa.terracina@datamat.it>; see the files
+ Examples/guile/test-suite/static-array-member.i and
+ anonymous-arg.i.
+
+Version 1.3.6 (July 9, 2001)
+=============================
+
+7/09/2001: cheetah (william fulton)
+ * GIFPlot examples: FOREGROUND and BACKGROUND definition missing
+ after TRANSPARENT #define fix in GIFPlot
+
+7/03/2001: beazley
+ Fixed up the version numbers so that the release is known
+ as 1.3.6. All future releases should have a similar
+ version format.
+
+7/02/2001: mkoeppe
+ * [Python]: Prevent the problem of self.thisown not being
+ defined if the C++ class constructor raised an exception.
+ Thanks to Luigi Ballabio <ballabio@mac.com>.
+
+6/29/2001: mkoeppe
+ * More portability fixes; fixed "gcc -Wall" warnings.
+
+6/29/2001: cheetah (william fulton)
+ * GIFPlot examples: TRANSPARENT #define multiple times on Solaris
+ (clashes with stream.h).
+ * Multiple definition bug fix for shadow classes. The perl and python
+ modules had workarounds which have been replaced with fixes in
+ the core. Many of the Language::cpp_xxxx functions now set a
+ flag which the derived classes can access through
+ is_multiple_definition() to see whether or not code should be
+ generated. The code below would have produced varying degrees
+ of incorrect shadow class code for the various modules:
+ class TestClass
+ {
+ public:
+ TestClass() {}
+ TestClass(int a) {}
+ ~TestClass() {}
+ unsigned long xyz(short k) {}
+ unsigned long xyz(int n) {}
+ static void static_func() {}
+ static void static_func(int a) {}
+ };
+ void delete_TestClass(int a);
+
+6/27/2001: mkoeppe
+ * [Perl] Another const-related portability fix.
+
+6/26/2001: cheetah (william fulton)
+ * [Java] Added in cpp_pragma() support with a host of new pragmas - see
+ jswig.html. These are designed for better mixing of Java and c++. It
+ enables the user to specify pure Java classes as bases and/or interfaces
+ for the wrapped c/c++.
+ * [Java] Old pragmas renamed. Warning given for the moment if used.
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+6/25/2001: mkoeppe
+ * Incorporated more build changes contributed by Wyss Clemens
+ <WYS@helbling.ch> for swig/ruby on cygwin.
+
+6/20/2001: cheetah (william fulton)
+ * Makefile mods so that 'make check' uses the swig options in the makefiles
+ * [Java] Removed Generating wrappers message
+ * [Java] NULL pointer bug fix
+ * [Java] Bug fix for Kaffe JVM
+
+6/20/2001: mkoeppe
+ * SWIG_TypeQuery from common.swg now returns a
+ swig_type_info* rather than a void*. This fixes a problem
+ when using pointer.i and C++, as illustrated by the new
+ test-suite example perl5/pointer-cxx.
+ * Portability fixes (const char *).
+ * Incorporated build changes contributed by Wyss Clemens
+ <WYS@helbling.ch>, which make swig runnable on cygwin.
+
+6/19/2001: cheetah (william fulton)
+ * [Java] Bug fix for SF bug #211144. This fix is a workaround
+ until fixed in the core.
+
+6/19/2001: mkoeppe
+ * [Guile]: Portability fixes for use with the Sun Forte
+ compilers.
+ * [Tcl]: Portability fix (const char *).
+ * [Tcl]: Configure now first tries to find a tclConfig.sh
+ file in order to find the Tcl include directory, library
+ location and library name.
+ * [Python]: Added a few possible library locations.
+
+6/18/2001: mkoeppe
+ * [Guile]: Don't call scm_c_export if nothing is to be
+ exported. Don't warn on %module if module has been set
+ already (this frequently occurs when %import is used).
+
+6/16/2001: mkoeppe
+ * [Guile]: New "passive" linkage, which is appropriate for
+ multi-module extensions without Guile module magic.
+
+6/15/2001: mkoeppe
+ * [Guile]: Fixed printing of smobs (space and angle were
+ missing).
+ * Properly generate type information for base classes
+ imported with the %import directive. Thanks to Marcelo
+ Matus <mmatus@acms.arizona.edu> for the report and the
+ patch; this closes SF bug #231619; see also
+ Examples/guile/test-suite/import*.
+ * [Guile]: Fix casting between class and base class; the
+ runtime type system had it the wrong way around; see
+ Examples/guile/test-suite/casts.i
+ * Make typemaps for SWIGPOINTER * with arg name take
+ precedence over those without arg name, to match normal
+ typemap precedence rules.
+ * Fixed the random-line-numbers problem reported as SF bug
+ #217310; thanks to Michael Scharf <scharf@users.sf.net>.
+ * [Guile]: Handle the %name and %rename directives.
+ * New syntax: %name and %rename now optionally take double
+ quotes around the scripting name. This is to allow scripting
+ names that aren't valid C identifiers.
+
+6/14/2001: beazley
+ Made a minor change to the way files are loaded in
+ order to get file/line number reporting correct in
+ the preprocessor.
+
+6/14/2001: mkoeppe
+ * The parser now understands the (non-standard) "long long"
+ types. It is up to the individual language modules to
+ provide typemaps if needed. Reported by Sam Steingold, SF
+ bug #429176.
+ * The parser now understands arguments like "const int *
+ const i". This fixes SF bug #215649.
+ * Fixed the Guile test-suite.
+
+6/13/2001: mkoeppe
+ Partial merge from the CVS trunk at tag
+ "mkoeppe-merge-1". This covers the following changes:
+
+| 01/16/01: ttn
+| Wrote table of contents for Doc/engineering.html. Added section
+| on CVS tagging conventions. Added copyright to other docs.
+| 9/25/00 : beazley
+| Modified the preprocessor so that macro names can start with a '%'.
+| This may allow new SWIG "directives" to be defined as macros instead
+| of having to be hard-coded into the parser.
+|
+| *** Also a yet-to-be-documented quoting mechanism with backquotes
+| *** has been implemented?
+
+6/13/2001: mkoeppe
+ * When configure does not find a language, don't use default
+ paths like /usr/local/include; this only causes build
+ problems.
+ * New directory: Examples/Guile/test-suite, where a few
+ bugs in 1.3a5 are demonstrated.
+ * Handle C++ methods that have both a "const" and a "throw"
+ directive (see Examples/Guile/test-suite/cplusplus-throw.i);
+ thanks to Scott B. Drummonds for the report and the fix.
+ * Handle C++ pointer-reference arguments (like "int *& arg")
+ (see Examples/Guile/test-suite/pointer-reference.i,
+ reported as SF bug #432224).
+ * [Ruby] Fixed typo in rubydec.swg; thanks to Lyle Johnson!
+ * Don't stop testing when one test fails.
+ * [Guile, MzScheme] Don't print "Generating wrappers...".
+
+6/12/2001: mkoeppe
+ [Guile] VECTORLENINPUT and LISTLENINPUT now have separate
+ list length variables. TYPEMAP_POINTER_INPUT_OUTPUT
+ attaches argument documentation involving SCM_TYPE to the
+ standard pointer typemaps. INOUT is now an alias for BOTH.
+
+6/12/2001: cheetah (william fulton)
+ Some Java documentation added.
+ [Java] Fixed bugs in import pragma and shadow pragma.
+
+6/12/2001: mkoeppe
+ Fix declarations of SWIG_define_class
+ (Lib/ruby/rubydec.swg) and SWIG_TypeQuery
+ (Lib/common.swg). Thanks to Lyle Johnson
+ <ljohnson@resgen.com> for the patches.
+
+6/11/2001: mkoeppe
+ [Guile] Use long instead of scm_bits_t; this makes the
+ generated wrapper code compatible with Guile 1.3.4
+ again. Thanks to Masaki Fukushima for pointing this out.
+
+6/11/2001: cheetah (william fulton)
+ The generic INSTALL file from autoconf added. Few changes to README file.
+
+6/11/2001: mkoeppe
+ Fixed typo in Makefile.in; thanks to Greg Troxel
+ <gdt@ir.bbn.com>.
+
+6/08/2001: cheetah (william fulton)
+ make check works again. Examples/GIFPlot configure generated by
+ top level autoconf now.
+
+6/08/2001: mkoeppe
+ Another build change: The new script autogen.sh runs
+ autoconf in the appropriate directories. The top-level
+ configure also configures in Examples/GIFPlot.
+
+6/07/2001: mkoeppe
+ Made the Makefile work with non-GNU make again.
+
+6/07/2001: cheetah (william fulton)
+ [Java] Class/struct members that are arrays of pointers to classes/structs -
+ Shadow class's get/set accessors now use Java classes instead of longs (pointers).
+ [Java] Shadow classes will now clean up memory if function return type
+ is a class/struct.
+ [Java] New example called reference based on the same example from other modules.
+
+6/06/2001: mkoeppe
+ New configure option --with-release-suffix allows for
+ attaching a suffix to the swig binary and the swig runtime
+ libraries. Minor changes to the build system. "swig
+ -swiglib" works again. If invoked with the new option
+ "-ldflags", SWIG prints a line of linker flags needed to
+ link with the runtime library of the selected language
+ module.
+
+6/06/2001: mkoeppe
+ [Guile] gswig_list_p is an int, not a SCM. This typo
+ caused warnings when compiling with a Guile configured with
+ strict C type checking. In INPUT and BOTH typemaps
+ generated by the SIMPLE_MAP macro, use the SCM_TO_C
+ function to convert from Guile to C (rather than C_TO_SCM).
+ Use scm_intprint to print pointers (rather than
+ sprintf). Allow using "-linkage" instead of "-Linkage".
+
+6/05/2001: cheetah (william fulton)
+ [Java] Mods for using inherited c++ classes from Java
+ [Java] New example called class based on the same example from other modules
+
+6/05/2001: cheetah (william fulton)
+ [Java] destructor (_delete()) was not aware of %name renaming
+ [Java] extends baseclass did not know about %name renaming
+ [Java] extends baseclass did extend even when the baseclass was not known to swig
+ [Java] sometimes enum-declarations occurred before the Java class declaration
+ [Java] unrelated enum initialisations no longer appear in Java class
+ [Java] if module ends in '_' correct JNI names are now produced
+
+6/04/2001: cheetah (william fulton)
+ [Java] Shadow class mods - Modified constructor replaces
+ newInstance(). _delete() now thread safe. getCPtr() replaces
+ _self. _selfClass() removed as now redundant.
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+ [Java] Not all output java files had SWIG banner. New banner.
+
+ [Java] Shadow class finalizers are output by default: Command
+ line option -finalize deprecated and replaced with -nofinalize.
+ *** POTENTIAL INCOMPATIBILITY FOR JAVA MODULE ***
+
+6/ 1/2001: mkoeppe
+ [Guile] Cast SCM_CAR() to scm_bits_t before shifting it.
+ This is required for compiling with a Guile configured with
+ strict C type checking.
+
+6/ 1/2001: mkoeppe
+ Added configure option "--with-swiglibdir".
+
+5/31/2001: mkoeppe
+ [Guile] Support multiple parallel lists or vectors in
+ the typemaps provided by list-vector.i. New typemaps file,
+ pointer-in-out.i.
+
+5/25/2001: cheetah (william fulton)
+ [Java] HTML update for examples.
+
+5/28/2001: mkoeppe
+ Minor changes to the build system. Added subdirectory for
+ Debian package control files.
+
+5/28/2001: mkoeppe
+ [Guile] Build a runtime library, libswigguile.
+
+5/28/2001: mkoeppe
+ [Guile] New typemap substitution $*descriptor. Use the {}
+ syntax, rather than the "" syntax for the standard
+ typemaps, in order to work around strange macro-expansion
+ behavior of the SWIG preprocessor. This introduces some
+ extra braces.
+
+5/27/2001: mkoeppe
+ [Guile] Handle pointer types with typemaps, rather than
+ hard-coded. New typemap substitutions $descriptor,
+ $basedescriptor; see documentation. Some clean-up in the
+ variable/constants wrapper generator code. New convenience
+ macro SWIG_Guile_MustGetPtr, which allows getting pointers
+ from smobs in a functional style. New typemap file
+ "list-vector.i", providing macros that define typemaps for
+ converting between C arrays and Scheme lists and vectors.
+
+5/25/2001: cheetah (william fulton)
+ [Java] STL string moved into its own typemap as it is c++ code and
+ it break any c code using the typemaps.i file.
+ - Fixes for wrappers around global variables - applies to primitive
+ types and user types (class/struct) and pointers to these.
+ - Structure member variables and class public member variables getters
+ and setters pass a pointer to the member as was in 1.3a3 and 1.1
+ (1.3a5 was passing by value)
+ - Parameters that were arrays and return types were incorrectly
+ being passed to create_function() as pointers.
+ - Fix for arrays of enums.
+ [Java] Updated java examples and added two more.
+ [Java] Java module updated from SWIG1.3a3 including code cleanup etc.
+ [Java] enum support added.
+ [Java] Array support implemented
+ [Java] Shadow classes improved - Java objects used rather than
+ longs holding the c pointer to the wrapped structure/c++class
+
+5/22/2001: mkoeppe
+ [Guile] Fixed extern "C" declarations in C++ mode. Thanks
+ to Greg Troxel <gdt@ir.bbn.com>.
+
+5/21/2001: mkoeppe
+ [Guile] New linkage "module" for creating Guile modules for
+ Guile versions >= 1.5.0.
+
+4/18/2001: mkoeppe
+ [MzScheme] Added typemaps for passing through Scheme_Object
+ pointers.
+
+4/9/2001 : mkoeppe
+ [MzScheme] Added typemaps for `bool'. Inclusion of headers
+ and support routines is now data-driven via mzscheme.i.
+ Headers come from the new file mzschemdec.swg. Don't abort
+ immediately when a type-handling error is reported. When
+ searching for typemaps for enums, fall back to using int,
+ like the Guile backend does. Support char constants. Emit
+ correct wrapper code for variables.
+
+3/12/2001: mkoeppe
+ [Guile] Fixed typemaps for char **OUTPUT, char **BOTH.
+
+3/2/2001 : mkoeppe
+ [Guile] Every wrapper function now gets a boolean variable
+ gswig_list_p which indicates whether multiple values are
+ present. The macros GUILE_APPEND_RESULT, GUILE_MAYBE_VALUES
+ and GUILE_MAYBE_VECTOR use this variable, rather than
+ checking whether the current return value is a list. This
+ allows for typemaps returning a list as a single value (a
+ list was erroneously converted into a vector or a
+ multiple-value object in this case).
+
+3/1/2001 : mkoeppe
+ [Guile] Added support for returning multiple values as
+ vectors, or passing them to a muliple-value
+ continuation. By default, multiple values still get
+ returned as a list.
+
+3/1/2001 : mkoeppe
+ [Guile] Added a "beforereturn" pragma. The value of this
+ pragma is inserted just before every return statement.
+
+3/1/2001 : mkoeppe
+ [Guile] Added support for Guile 1.4.1 procedure
+ documentation formats, see internals.html.
+
+2/26/2001: mkoeppe
+ [Guile] Made the wrapper code compile with C++ if the
+ "-c++" command-line switch is given. Thanks to
+ <monkeyiq@dingoblue.net.au>.
+
+2/26/2001: mkoeppe
+ [Guile] Now two type tables, swig_types and
+ swig_types_initial, are used, as all other SWIG language
+ modules do. This removes the need for the tricky
+ construction used before that the broken Redhat 7.0 gcc
+ doesn't parse. Reported by <monkeyiq@dingoblue.net.au>.
+
+2/26/2001: mkoeppe
+ [Guile] Fixed typemaps for char *OUTPUT, char *BOTH; a bad
+ free() would be emitted. Added typemap for SCM.
+
+
+Version 1.3 Alpha 5
+===================
+
+9/19/00 : beazley
+ [Python] Python module generates more efficient code for
+ creating the return value of a wrapper function. Modification
+ suggested by Jon Travis.
+
+9/19/00 : beazley
+ Library files specified with the -l option are now included at the
+ end of the interface file (reverting to the old behavior).
+
+9/19/00 : beazley
+ Fixed some problems with enum handling. enums are now manipulated as
+ 'int', but cast into the enum type when values are passed to the
+ corresponding C function.
+
+9/19/00 : mkoeppe
+ [Guile] Removed "-with-smobs" command-line option, as this is the
+ default now. Added "-emit-setters" command-line option,
+ which turns on generating procedures-with-setters; see
+ internals.html.
+
+9/18/00 : mkoeppe
+ Incorporated patch #101430, fixing bugs in the Guile module:
+ 1. Some arguments were erroneously taken as *optional* arguments when
+ ignored arguments were present.
+ 2. Guile 1.3.4 was not supported since functions introduced in Guile
+ 1.4 were used.
+ 3. Added handling of `const char *'.
+
+9/17/00 : beazley
+ Fixed problem with failed assertion and large files.
+
+9/17/00 : beazley
+ Fixed problem with the '%' character appearing in added methods
+ and function bodies. Preprocessor bug.
+
+Version 1.3 Alpha 4 (September 4, 2000)
+=======================================
+
+9/3/00 : ttn
+ Added instructions for maintainers in Examples/README on how
+ to make examples also be useful in the testing framework.
+ Also, "make check" now uses ./Lib by via env var `SWIG_LIB'.
+ This is overridable like so:
+ make chk-swiglib=/my/experimental/swig/Lib check
+
+9/3/00 : beazley
+ Added $typemap variable to typemaps. This gets replaced with
+ a string indicating the typemap that is applied. Feature
+ request from rsalz.
+
+9/3/00 : beazley
+ Experimental optimization to code generation for virtual
+ member functions. If you have two classes like this:
+
+ class A() {
+ virtual void foo();
+ }
+
+ class B() : public A {
+ virtual void foo();
+ }
+
+ Swig now will generate a single wrapper function for this
+
+ A_foo(A *a) {
+ a->foo();
+ }
+
+ and use it as the implementation of both A_foo() and B_foo().
+ This optimization only takes place if both methods are declared
+ as virtual and both take identical parameters.
+ *** EXPERIMENTAL FEATURE ***
+
+9/3/00 : beazley
+ Restored the "memberin" typemap for setting structure members.
+ Unlike the old version, the new version is expanded inline in the
+ wrapper function allowing access to scripting language
+ internals (a sometimes requested feature). The "memberout" typemap
+ is gone. Use the "out" typemaps instead.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+9/3/00 : beazley
+ Attribute set methods no longer return the value of a member.
+ For example:
+
+ struct Foo {
+ int x;
+ ...
+ }
+
+ now gets set as follows:
+
+ void Foo_x_set(Foo *f, int x) {
+ f->x = x;
+ }
+
+ In SWIG1.1 it used to be this:
+
+ int Foo_x_set(Foo *f, int x) {
+ return (f->x = x);
+ }
+
+ This has been changed due to the complexity created by trying
+ to do this with more exotic datatypes such as arrays. It also
+ complicates inlining and handling of the "memberin" typemap.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+9/2/00 : beazley
+ Removed the ptrcast() and ptrmap() functions from the
+ pointer.i library file. Old implementation is incompatible
+ with new type system.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+9/2/00 : beazley
+ New runtime function SWIG_TypeQuery(const char *name) added.
+ This function can be used to extract the type info structure
+ that is used for type-checking. It works with either the
+ nice C name or mangled version of a datatype. For example:
+
+ swig_type_info *ty = Swig_TypeQuery("int *");
+ swig_type_info *ty = Swig_TypeQuery("_p_int");
+
+ This is an advanced feature that has been added to support some
+ exotic extension modules that need to directly manipulate
+ scripting language objects.
+ *** NEW FEATURE ***
+
+9/2/00 : beazley
+ New directive %types() added. This is used to
+ explicitly list datatypes that should be included in
+ the runtime type-checking code. Normally it is never
+ necessary to use this but sometimes advanced extensions
+ (such as the pointer.i library) may need to manually
+ add types to the type-checker.
+ *** NEW FEATURE ***
+
+8/31/00 : beazley
+ Improved handling of string array variables. For example,
+ a global variable of the form "char name[64]" is automatically
+ managed as a 64 character string. Previously this didn't
+ work at all or required the use of a special typemap.
+ *** NEW FEATURE (Tcl, Perl, Python) ***
+
+8/31/00 : ttn
+ Added Makefile target `check-c++-examples', which uses new
+ files under Examples/C++ contributed by Tal Shalif. Now "make
+ check" also does "make check-c++-examples". Also, expanded
+ actions in `check-gifplot-example' and `check-aliveness'.
+
+8/30/00 : mkoeppe
+ Major clean-up in the Guile module. Added typemap-driven
+ documentation system. Changed to handle more than 10
+ args. Updated and extended examples.
+ *** NEW FEATURE ***
+
+8/29/00 : beazley
+ Added new %insert directive that inserts the contents of a file
+ into a portion of the output wrapper file. This is only intended
+ for use by writers of language modules. Works as follows:
+
+ %insert(headers) "file.swg";
+ %insert(runtime) "file.swg";
+ %insert(wrappers) "file.swg";
+ %insert(init) "file.swg";
+
+ *** NEW FEATURE ***
+
+8/29/00 : beazley
+ Added new %runtime directive which includes code into the runtime
+ portion of the wrapper code. For example:
+
+ %runtime %{
+ ... some internal runtime code ...
+ %}
+
+ There is no practical reason for ordinary users to use this
+ feature (almost everything can be done using %{ ... %}
+ instead). However, writers of language modules may want to
+ use this in language configuration files.
+ *** NEW FEATURE ***
+
+8/28/00 : beazley
+ Typemaps can now be specified using string literals like
+ this:
+
+ %typemap(in) int "$target = SvIV($source);"
+
+ When code is specified like this, it is *NOT* enclosed
+ inside a local scope (as with older typemap declarations).
+ Note: character escape sequences are interpreted in the
+ code string so if you want to include a quote or some
+ other special character, make sure you use a (\).
+ *** NEW FEATURE ***
+
+8/27/00 : beazley
+ Typemaps have been modified to follow typedef declarations.
+ For example, if you have this:
+
+ typedef int Number;
+
+ %typemap(in) int {
+ ... get an integer ...
+ }
+
+ void foo(Number a);
+
+ The typemap for 'int' will be applied to the argument 'Number a'.
+ Of course, if you specify a typemap for 'Number' it will take
+ precedence (nor will it ever be applied to an 'int').
+ *** POTENTIAL INCOMPATIBILITY ***
+
+8/27/00 : beazley
+ Default typemap specification has changed. In older
+ versions of swig, you could do this:
+
+ %typemap(in) int SWIG_DEFAULT_TYPE {
+ ...
+ }
+
+ To specify the default handling of a datatype. Now that
+ SWIG follows typedef declarations, this is unnecessary.
+ Simply specifying a typemap for 'int' will work for all
+ variations of integers that are typedef'd to 'int'.
+
+ Caveat, specifying the default behavior for pointers,
+ references, arrays, and user defined types is a little
+ different. This must be done as follows:
+
+ %typemap() SWIGPOINTER * {
+ ... a pointer ...
+ }
+ %typemap() SWIGREFERENCE & {
+ ... a reference ...
+ }
+ %typemap() SWIGARRAY [] {
+ ... an array ...
+ }
+ %typemap() SWIGTYPE {
+ ... a user-defined type (by value) ...
+ }
+ *** POTENTIAL INCOMPATIBILITY ***
+
+8/15/00 : dustin
+ The file swig-1.3a1-1.spec has been added to the Tools directory.
+ It can be used to build a redhat package for SWIG, although it
+ will need to be updated for the next public release.
+
+8/15/00 : beazley
+ Typemaps have been completely rewritten. Eventually they may be
+ replaced with something better, but for now they stay. However,
+ there are a number of a significant changes that may trip some
+ people up:
+
+ 1. Typemap scoping is currently broken. Because of this, the
+ following code won't work.
+
+ %typemap(in) blah * {
+ ...
+ }
+ class Foo {
+ ...
+ int bar(blah *x);
+ }
+ %typemap(in) blah *; /* Clear typemap */
+
+ (this breaks because the code for the class Foo is actually
+ generated after the entire interface file has been processed).
+ This is only a temporary bug.
+
+ 2. In SWIG1.1, the %apply directive worked by performing a
+ very complex type-aliasing procedure. From this point on,
+ %apply is simply a generalized typemap copy operation.
+ For example,
+
+ %apply double *OUTPUT { double *x, double *y };
+
+ Copies *ALL* currently defined typemaps for 'double *OUTPUT' and
+ copies them to 'double *x' and 'double *y'.
+
+ Most people probably won't even notice this change in
+ %apply. However, where it will break things is in code like
+ this:
+
+ %apply double *OUTPUT { double *x };
+ %typemap(in) double *OUTPUT {
+ ... whatever ...
+ }
+
+ void foo(double *x);
+
+ In SWIG1.1, you will find that 'foo' uses the 'double *OUTPUT' rule
+ even though it was defined after the %apply directive (this is
+ the weird aliasing scheme at work). In SWIG1.3 and later,
+ the 'double *OUTPUT' rule is ignored because it is defined
+ after the %apply directive.
+
+ 3. The %clear directive has been modified to erase all currently
+ defined typemaps for a particular type. This differs from
+ SWIG1.1 where %clear only removed rules that were added using
+ the %apply directive.
+
+ 4. Typemap matching is now performed using *exact* types.
+ This means that things like this
+
+ %typemap(in) char * { }
+ %typemap(in) const char * { }
+
+ are different typemaps. A similar rule applies for pointers,
+ arrays, and references. For example:
+
+ %typemap(in) double * { }
+
+ used to apply to 'double &', 'double []', Now, it only applies
+ to 'double *'. If you want a 'double &', you'll need to handle
+ that separately.
+
+ 5. Array matching has been simplfied. In SWIG1.1, array matching
+ was performed by trying various combinations of dimensions.
+ For example, 'double a[10][20]' was matched as follows:
+
+ double [10][20]
+ double [ANY][20]
+ double [10][ANY]
+ double [ANY][ANY]
+
+ In SWIG1.3, only the following matches are attempted:
+
+ double [10][20]
+ double [ANY][ANY]
+
+ On the positive side, typemap matching is now *significantly* faster
+ than before.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+8/15/00 : beazley
+ Secret developer feature. Since datatypes are now represented as
+ strings internally, you can bypass limitations of the parser and
+ create a wild datatype by simply enclosing the raw string encoding
+ in backticks (``) and sticking it in the interface file anywhere a
+ type is expected. For example, `a(20).a(10).p.f(int,int)`. This
+ feature is only intended for testing (i.e., you want to see what
+ happens to your language module if it gets a reference to a pointer
+ to an array of pointers to functions or something).
+ *** SICK HACK ***
+
+8/14/00 : beazley
+ Completely new type-system added to the implementation.
+ More details later.
+
+8/11/00 : beazley
+ Cleaned up some of the I/O handling. SWIG no longer generates
+ any temporary files such as _wrap.wrap, _wrap.ii, _wrap.init.
+ Instead, these "files" are kept around in memory as strings
+ (although this is transparent to language modules).
+
+8/4/00 : ttn
+ Added Makefile target "check" and variants.
+ This can be used like "make check" or, to explicitly skip a
+ language LANG: "make skip-LANG=true check". LANG is skipped
+ automatically if ./configure determines that LANG support is
+ insufficient.
+
+ Currently, the check is limited to doing the equivalent of
+ "make all" in some of the Examples directories. This should
+ be expanded both horizontally (different types of tests) and
+ vertically (after "make all" in an Examples subdir succeeds,
+ do some additional tests with the resulting interpreter, etc).
+
+8/4/00 : ttn
+ Added Makefile target "distclean", which deletes all the
+ files ./configure creates, including config.status and friends.
+
+8/3/00 : harcoh
+ java changes??? [todo: document changes]
+
+7/23/00 : beazley
+ Typemaps have been modified to key off of the real datatypes
+ used in the interface file. This means that typemaps for
+ "const char *" and "char *" will be difference as will typemaps
+ for "Vector" and "Vector *."
+ *** POTENTIAL INCOMPATIBILITY ***
+ This is likely to break interfaces that rely on the odd type
+ handling behavior of typemaps in SWIG1.1--especially with
+ respect to interfaces involving pass-by-value.
+
+7/23/00 : beazley
+ New %constant directive. This directive can be used to
+ create true constants in the target scripting language.
+ It's most simple form is something like this:
+
+ %constant FOO 42;
+
+ In this case, the type is inferred from the syntax of the
+ value (in reality, all #define macros are translated into
+ directives of this form).
+
+ An expanded version is as follows:
+
+ %constant(Foo *) FOO = &FooObj;
+
+ In this case, an explicit type can be specified. This
+ latter form may be useful for creating constants that
+ used to be specified as
+
+ const Foo *FOO = &FooObj;
+
+ (which are now treated as variables).
+ *** EXPERIMENTAL FEATURE *** The syntax may change in
+ the final release.
+
+7/23/00 : beazley
+ Modified the parser so that variable declarations of the form
+ "const type *a" are handled as variables, not constants.
+ Note: SWIG1.1 handled this case erroneously because
+ const char *a is a pointer variable that can be reassigned.
+ *** POTENTIAL INCOMPATIBILITY ***
+ Note: just because this is the "right" way to do things,
+ doesn't mean it's the most appropriate interpretation.
+ I suspect that many C programmers might use 'const char *'
+ with the intent of creating a constant, without realizing
+ that they've created a reassignable global variable.
+
+7/23/00 : beazley
+ The C/C++ wrapping layer has been completely redesigned and
+ reimplemented. This change should iron out a few rough
+ spots with the handling of datatypes. In addition, the
+ wrapper code is somewhat cleaner.
+ *** POTENTIAL INCOMPATIBILITY ***
+ This change may break interfaces that involve
+ subtle corner-cases with typemaps and the %addmethods
+ directive since some of these features had somewhat
+ type handling behavior in SWIG1.1.
+
+7/23/00 : beazley
+ The "memberin" and "memberout" typemaps are gone for the
+ moment, but they might return as soon as I figure out
+ how to integrate them with some of the streamlined C wrapper
+ functions.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/22/00 : beazley
+ A variety of old type handling functions such as print_type(),
+ print_full(), print_mangle(), etc... are gone and have been
+ replaced with a smaller set of functions. See the file
+ Doc/internals.html for details. This will break all third
+ party language modules.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/20/00 : beazley
+ Deprecated the %val and %out directives. These directives
+ shouldn't really be necessary since typemaps can be used
+ to achieve similar results. This also cleans up the
+ handling of types and parameters quite a bit.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/20/00 : ttn
+ Fixed unspecified-module bug in Guile support and removed
+ more non-"with-smobs" functionality using patches submitted
+ by Matthias Koeppe.
+
+ Re-enable recognition of "-with-smobs" (with no effect since
+ we use smobs by default now) for the time being. After the
+ 1.3a4 release, this option will signal an error.
+
+7/17/00 : ttn
+ Fixed NULL-input bug in parameter list handling.
+ Reported by Matthias Koeppe.
+
+7/12/00 : beazley
+ Fixed memory leak in Python type-checking code. Reported by
+ Keith Davidson. Bug #109379.
+
+7/10/00 : beazley
+ Changed internal data structures related to function parameters.
+
+7/10/00 : beazley
+ Fixed some bugs related to the handling of the %name() directive
+ and classes in the Tcl module. Problem reported by James Bailey.
+
+7/10/00 : beazley
+ Fixed parsing and enum handling problems with character constants.
+ Reported by Greg Kochanski.
+
+7/10/00 : beazley
+ Removed WrapperFunction class from the core and updated the language
+ module. This will break third party modules.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/9/00 : beazley
+ Implementation of SWIG no longer makes use of C++ operator overloading.
+ This will almost certainly break *all* third party language modules
+ that are not part of the main SWIG CVS tree. Sorry.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/8/00 : beazley
+ Removed the experimental and undocumented "build" typemap that
+ was intended to work with multiple arguments. Simply too weird
+ to keep around. Besides, a better replacement is in the works.
+
+7/6/00 : ttn
+ Removed non-"with-smobs" functionality (Guile support), i.e.,
+ "-with-smobs" is now the default and no longer needs to be
+ specified on the command-line.
+
+7/5/00 : ttn
+ Incorporated Ruby support contributed by Masaki Fukushima.
+
+6/28/00 : ttn
+ Applied more-than-10-args bugfix patch contributed
+ by Matthias Koeppe.
+
+6/27/00 : beazley
+ Rewrote some of the string handling and eliminated the C++
+ implementation (which is now just a wrapper).
+
+6/27/00 : ttn
+ Added Doc/index.html and Doc/internals.html. The target
+ audience for the latter is new SWIG developers.
+
+
+Version 1.3 Alpha 3 (June 18, 2000)
+===================================
+
+6/18/00 : beazley
+ Removed the naming.cxx, hash.cxx, and symbol.cxx files from
+ the SWIG1.1 directory. Continued to migrate things away
+ from the C++ base (although there's still a lot of work to do).
+
+6/17/00 : beazley
+ Added a few more examples to the Examples directory. Still
+ need to do a lot of work on this.
+
+6/16/00 : beazley
+ Added -includeall to follow all #include statements in the
+ preprocessor.
+
+6/15/00 : beazley
+ Tried to fix as many C++ warnings as possible when compiling
+ with the Sun Workshop C++ compiler. Unfortunately, this means
+ that there are a lot of statements that contain string literals
+ of the form (char*)"Blah".
+
+6/15/00: beazley
+ A variety of cleanup and performance optimization in the
+ low-level DOH library. This seems to result in a speedup
+ of 50-100% for preprocessing and other related tasks.
+
+5/10/00 : ttn
+ Applied variable-wrapping bugfix patch contributed
+ by Matthias Koeppe.
+
+4/17/00 : ttn
+ Updated MzScheme support contributed by Oleg Tolmatcev.
+ We now use a `Scheme_Type'-based structure to wrap pointers.
+
+4/11/00 : ttn
+ Incorporated further Guile-support patch by Matthias Koeppe.
+ Typemaps previously deleted have been re-added. There is now
+ exception handling (see Doc/engineering.html). `SWIG_init' is now
+ declared extern only for simple linkage. Some bugs were fixed.
+
+4/06/00 : ttn
+ Incorporated MzScheme support contributed by Oleg Tolmatcev.
+ This includes new directories Lib/mzscheme and Examples/mzscheme.
+
+4/03/00 : ttn
+ Added Examples/guile and children. This is an adaptation of
+ the same-named directory from the SWIG-1.1p5 distribution.
+ Added Guile-specific section to Doc/engineering.html.
+
+4/02/00 : ttn
+ Incorporated new guilemain.i by Martin Froehlich.
+ Incorporated Guile-support rewrite patch by Matthias Koeppe.
+ The command line option "-with-smobs" enables implementation of
+ pointer type handling using smobs, the canonical mechanism for
+ defining new types in Guile. Previous implementation (using
+ strings) is at the moment still supported but deprecated. At
+ some point, "-with-smobs" will be the default and no longer
+ required.
+
+3/13/00 : beazley
+ Added purify patches submitted by Ram Bhamidipaty.
+
+3/02/00 : ttn
+ Added support for different Guile "linkage" schemes.
+ Currently, "-Linkage hobbit" works.
+
+
+Version 1.3 Alpha 2 (March 1, 2000)
+===================================
+
+2/29/00 : beazley
+ Made SWIG ignore the 'mutable' keyword.
+
+2/29/00 : beazley
+ Incorporated some patches to the Perl5 module related to
+ the -hide option and the destruction of objects.
+ Patch submitted by Karl Forner.
+
+2/27/00 : ttn
+ Incorporated Guile support contributed by Matthias Koeppe.
+ This includes a cpp macro in Lib/guile/guile.swg and the
+ entire file Lib/guile/typemaps.i.
+
+2/25/00 : ttn
+ Modified configure.in and Makefile.in files to support
+ non-local build (useful in multi-arch environments).
+
+2/24/00 : ttn
+ Incorporated Guile support contributed by Clark McGrew.
+ This works with Guile 1.3, but since it depends heavily
+ on the gh_ interface, it should work for all later versions.
+ It has not been tested with versions before 1.3.
+ WARNING: Code is unstable due to experimentation by ttn.
+
+2/16/00 : beazley
+ A variety of performance improvements to the Python shadow
+ class code generation. Many of these result in substantial
+ runtime performance gains. However, these have come at
+ a cost of requiring the use of Python 1.5.2. For older
+ versions, use 'swig -noopt -python' to turn off these
+ optimization features.
+
+Version 1.3 Alpha 1 (February 11, 2000)
+=======================================
+
+2/11/00 : Added 'void' to prototype of Python module initializer.
+ Reported by Mark Howson (1/20/00).
+
+2/11/00 : beazley
+ Modified the Python shadow class code to discard ownership of an
+ object whenever it is assigned to a member of another object.
+ This problem has been around for awhile, but was most recently
+ reported by Burkhard Kloss (12/30/99).
+
+2/11/00 : beazley
+ Added braces around macros in the exception.i library. Reported
+ by Buck Hodges (12/19/99)
+
+2/11/00 : beazley
+ Fixed bug in the constraints.i library. Reported by Buck
+ Hodges (12/14/99)
+
+2/11/00 : beazley
+ The %native directive now generates Tcl8 object-style command calls.
+ A full solution for Tcl7 and Tcl8 is still needed. Patch suggested
+ by Mike Weiblen (11/29/99)
+
+2/11/00 : beazley
+ Modified the typemap code to include the $ndim variable for arrays.
+ Patch provided by Michel Sanner (11/12/99).
+
+2/11/00 : beazley
+ Modified the Python module to raise a Runtime error if an attempt
+ is made to set a read-only member of a shadow class. Reported by
+ Michel Sanner (11/5/99).
+
+2/10/00 : The documentation system has been removed. However, it is likely
+ to return at some point in the future.
+
+2/1/00 : Added a number of performance enhancements to the Python shadow
+ classing and type-checking code. Contributed by Vadim Chugunov.
+
+ 1. Remove _kwargs argument from the shadow wrappers when -keyword
+ option is not specified. This saves us a construction of keyword
+ dictionary on each method call.
+
+ def method1(self, *_args, **_kwargs):
+ val = apply(test2c.PyClass1_method1, (self,) + _args, _kwargs)
+ return val
+
+ becomes
+
+ def method1(self, *_args):
+ val = apply(test2c.PyClass1_method1, (self,) + _args)
+ return val
+
+ 2. Incorporate self into the _args tuple. This saves at least one tuple
+ allocation per method call.
+
+ def method1(self, *_args):
+ val = apply(test2c.PyClass1_method1, (self,) + _args)
+ return val
+
+ becomes
+
+ def method1(*_args):
+ val = apply(test2c.PyClass1_method1, _args)
+ return val
+
+ 3. Remove *Ptr classes.
+ Assume that we are SWIGging a c++ class CppClass.
+ Currently SWIG will generate both CppClassPtr class
+ that hosts all methods and also CppClass that is derived
+ from the former and contains just the constructor.
+ When CppClass method is called, the interpreter will try
+ to find it in the CppClass's dictionary first, and only then
+ check the base class.
+
+ CppClassPtr functionality may be emulated with:
+
+ import new
+ _new_instance = new.instance
+ def CppClassPtr(this):
+ return _new_instance(CppClass, {"this":this,"thisown":0})
+
+ This saves us one dictionary lookup per call.
+
+ <DB>The new module was first added in Python-1.5.2 so it
+ won't work with older versions. I've implemented an
+ alternative that achieves the same thing</DB>
+
+ 4. Use CObjects instead of strings for pointers.
+
+ Dave: This enhancements result in speedups of up to 50% in some
+ of the preliminary tests I ran.
+
+2/1/00 : Upgraded the Python module to use a new type-checking scheme that
+ is more memory efficient, provides better performance, and
+ is less error prone. Unfortunately, it will break all code that
+ depends on the SWIG_GetPtr() function call in typemaps.
+ These functions should be changed as follows:
+
+ if (SWIG_GetPtr(string,&ptr,"_Foo_p")) {
+ return NULL;
+ }
+
+ becomes
+
+ if (SWIG_ConvertPtr(pyobj, &ptr, SWIG_TYPE_Foo_p) == -1) {
+ return NULL;
+ }
+
+ Note: In the new implementation SWIG_TYPE_Foo_p is no longer
+ a type-signature string, but rather an index into a type
+ encoding table that contains type information.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+1/30/00 : loic
+ Conditionally compile experimental code with --enable-experiment
+ configure flag.
+ Fix .cvsignore to ignore configure & yacc generated files
+
+1/28/00 : loic
+ Apply automake everywhere
+ Keep configure scripts so that people are not *forced* to autoconf
+ Keep sources generated by yacc so that compilation without yacc
+ is possible.
+ Source/LParse/cscanner.c: change lyacc.h into parser.h to please
+ default yacc generation rules.
+ Use AC_CONFIG_SUBDIRS in configure.in instead of hand made script.
+ Update all relevant .cvsignore to include .deps
+ Fixed missing ; line 136 Source/Swig/swig.h
+
+1/13/00 : beazley
+ Fixed a number of minor end-of-file parsing problems in the
+ preprocessor.
+
+1/13/00 : beazley
+ Added -freeze option that forces SWIG to freeze upon exit.
+ This is only used as a debugging tool so that I can more
+ easily examine SWIG's memory footprint.
+
+1/13/00 : beazley
+ Added patch to guile module for supporting optional arguments
+ Patch contributed by Dieter Baron.
+
+1/13/00 : loic
+ Added .cvsignore, Examples/.cvsignore, Source/DOH/Doh/.cvsignore
+ Source/SWIG1.1/main.cxx: Fixed -I handling bug
+ Source/Modules1.1/java.cxx: fixed char* -> const char* warnings that are
+ errors when compiling with gcc-2.95.2
+ Source/SWIG1.1/main.cxx: cast const char* to char* for String_replace
+ token and rep should really be const.
+
+1/12/00 : beazley
+ Added Harco's Java modules.
+
+1/12/00 : beazley
+ Revoked the %ifdef, %ifndef, %endif, %if, %elif, and %else
+ directives. These are no longer needed as SWIG now has a real
+ preprocessor.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+1/12/00 : beazley
+ Moved the documentation modules from the SWIG directory
+ to the Modules directory (where they really should have been
+ to begin with).
+
+1/12/00 : beazley
+ Removed the -stat option for printing statistics. The
+ statistics reporting was inadequate and mostly broken
+ anyway.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+1/12/00 : beazley
+ Removed the -t option for reading a typemap file. More
+ trouble than it's worth. Just include typemaps at the top
+ of the interface file.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+1/12/00 : beazley
+ Removed the %checkout directive.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+1/12/00 : beazley
+ Removed the -ci option for file checkin. Too problematic
+ to implement. Probably better to just put your SWIG library
+ under CVS instead.
+ *** POTENTIAL INCOMPATIBILITY ***.
+
+1/11/00 : beazley
+ Deleted the LATEX module. Sorry... Didn't know anyone
+ who was using it. Besides, I'm looking to simplify
+ the documentation system.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+1/11/00 : beazley
+ Modified the ASCII documentation module to use a .txt
+ suffix for its output file instead of .doc.
+
+1/11/00 : beazley
+ Added the long-lost SWIG preprocessor back to the system.
+ It should be enabled by default. Raw preprocessed output
+ can be viewed using swig -E file.i.
+ *** NEW FEATURE ***
+
+1/11/00 : beazley and djmitche
+ Completely reorganized the SWIG directory structure. The
+ basic organization is now:
+
+ Source/ SWIG source code
+ Lib/ SWIG library files (swig_lib)
+ Doc/ Documentation
+ Examples/ Examples
+
+ More directories will be added as needed.
+
+12/08/99: Loic Dachary (loic@senga.org)
+ Enhanced package handling for perl5 and c++.
+
+ With new option -hide Foo::Bar, every perl5 object (Frob) is
+ qualified by Foo::Bar::Frob. The package name is solely used
+ to encapsulate C/C++ wrappers output in <module>_wrap.c and the
+ corresponding perl package in <module>.pm. Note that a package
+ name may contain :: (Frob::Nitz) and will be relative to the
+ package name provided by -hide (Foo::Bar::Frob::Nitz).
+
+ In *_wrap.c, SWIG_init macro is used. Was previously defined
+ but not used and simplifies code.
+
+ Added typemap(perl5,perl5in) and typemap(perl5,perl5out) that
+ do the equivalent of typemap(perl5,in) and typemap(perl5,out)
+ but contain perl code and applies to wrappers generated by
+ -shadow.
+
+ Lacking proper regression tests I used
+ Examples/perl5/{c++,constraint,defarg,except,
+ graph/graph[1234],multinherit,nested,shadow,simple,tree,
+ typemaps/{argv,argv2,arraymember,database,file,ignore,integer,
+ output,passref,reference,return}}/. I ran swig with and without
+ the patches, diff the generatedsources, run the .pl files
+ and checked that the results are identical. In all those examples
+ I had no error.
+
+11/21/99: Modified the Tcl module to provide full variable linking capabilities
+ to all datatypes. In previous versions, a pair of accessor functions
+ were created for datatypes incompatible with the Tcl_LinkVar() function.
+ Now, we simply use variable traces to support everything. This may
+ break scripts that rely upon the older behavior.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+11/21/99: Added slight tweak to wrapper generator to collect local variables
+ of similar type. Produces somewhat more compact wrapper code.
+
+11/20/99: Modified the Tcl module to use SWIG_GetArgs() to parse
+ arguments. This is a technique borrowed from Python in which
+ arguments are converted using a format string convention similiar
+ to fprintf(). This results in a *substantial* reduction in the
+ size of the resulting wrapper code with only a modest runtime overhead
+ in going through the extra conversion function.
+
+11/13/99: Completely rewrote the class/structure generation code for the
+ Tcl module. Now, a small set of runtime functions are used
+ to implement the functionality for all classes (instead of a
+ massive amount of runtime code being generated for each class).
+ Class specific information is simply encoded in a series of
+ static tables. This results in a *HUGE* reduction in wrapper
+ code size--especially for C++.
+
+11/13/99: Removed the -tcl (Tcl 7.x) module. Tcl 8.0 is now several
+ years old and the defacto standard--no real reason to keep
+ supporting the old version at this point.
+
+11/13/99: Cleaned up -c option for Python module. The pyexp.swg file
+ is now gone.
+
+11/13/99: Fixed external declarations to work better with static linking
+ on Windows. Static linking should now be possible by defining
+ the -DSTATIC_LINK option on the command line. Patch contributed
+ by Alberto Fonseca.
+
+11/5/99 : Fixed an obscure code generation bug related to the generation
+ of default constructors. Bug reported by Brad Clements.
+
+11/5/99 : Fixed a few memory problems found by purify.
+
+11/5/99 : Officially deprecated the -htcl, -htk, and -plugin options
+ from the Tcl and Tcl8 modules.
+
+10/26/99: Removed unused variable from python/typemaps.i. Patch
+ contributed by Keith Davidson.
+
+8/16/99 : Added _WIN32 symbol to libraries to better support Windows.
+
+8/16/99 : Deprecated the Perl4 module. It is no longer included in the
+ distribution and no longer supported. In the entire 3 years SWIG
+ has been around I never received a single comment about it so I'm
+ assuming no one will miss it...
+
+8/16/99 : Modified the type-checking code to register type mappings using a
+ table instead of repeated calls to SWIG_RegisterMapping(). This
+ reduces the size of the module initialization function somewhat.
+
+8/15/99 : Cleaned up the pointer type-checking code in the Tcl module.
+
+8/15/99 : Many changes to the libraries to support runtime libraries.
+
+8/13/99 : Eliminated C++ compiler warning messages about extern "C" linkage.
+
+8/13/99 : Some cleanup of Python .swg files to better support runtime libraries
+ on Windows.
+
+8/13/99 : Modified the %pragma directive to attach pragmas declared inside
+ a class definition to the class itself. For example:
+
+ class foo {
+ ...
+ %pragma(python) addtomethod = "insert:print `hello world'"
+ ...
+ }
+
+ Most people don't need to worry about how this works. For people
+ writing backend modules, class-based pragmas work like this:
+
+ lang->cpp_open_class() // Open a class
+ lang->cpp_pragma() // Supply pragmas
+ ... // Emit members
+
+ lang->cpp_close_class() // Close the class
+
+ All of the pragmas are passed first since they might be used to
+ affect the code generation of other members. Please see
+ the Python module for an example. Patches contributed
+ by Robin Dunn.
+
+8/13/99 : Patch to Python shadow classes to eliminate ignored
+ exception errors in destructors. Patch contributed
+ by Robin Dunn.
+
+8/11/99 : Minor patch to swig_lib/python/swigptr.swg (added SWIGSTATIC
+ declaration). Patch contributed by Lyle Johnson.
+
+8/11/99 : Added FIRSTKEY/NEXTKEY methods to Perl5 shadow classes
+ Patch contributed by Dennis Marsa.
+
+8/11/99 : Modified Python module so that NULL pointers are returned
+ and passed as 'None.' Patch contributed by Tal Shalif.
+
+8/10/99 : Fixed missing 'int' specifiers in various places.
+
+8/10/99 : Added Windows makefile for Runtime libraries. Contributed
+ by Bob Techentin.
+
+8/10/99 : Fixed minor problem in Python runtime makefile introduced
+ by keyword arguments.
+
+8/8/99 : Changed $target of perl5(out) typemap from ST(0) to
+ ST(argvi). Patch contributed by Geoffrey Hort.
+
+8/8/99 : Fixed bug in typemap checking related to the ANY keyword
+ in arrays and ignored arguments. Error reported by
+ Geoffrey Hort.
+
+8/8/99 : %enabledoc and %disabledoc directives can now be used
+ inside class/structure definitions. However, no check
+ is made to see if they are balanced (i.e., a %disabledoc
+ directive inside a class does not have to have a matching
+ %enabledoc in the same class).
+
+8/8/99 : Keyword argument handling is now supported in the Python
+ module. For example:
+
+ int foo(char *bar, int spam, double x);
+
+ Can be called from Python as
+
+ foo(x = 3.4, bar="hello", spam=42)
+
+ To enable this feature, run SWIG with the '-keyword' command
+ line option. Mixing keyword and default arguments
+ should work as well. Unnamed arguments are assigned names
+ such as "arg1", "arg2", etc...
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ Functions with duplicate argument names such as
+ bar(int *OUTPUT, int *OUTPUT) will likely cause problematic
+ wrapper code to be generated. To fix this, use different
+ names or use %apply to map typemaps to alternate names.
+
+8/8/99 : Handling of the 'this' pointer has been changed in Python shadow
+ classes. Previously, dereferencing of '.this' occurred in the
+ Python shadow class itself. Now, this step occurs in the C
+ wrappers using the following function:
+
+ SWIG_GetPtrObj(PyObject *, void **ptr, char *type)
+
+ This function can accept either a string containing a pointer
+ or a shadow class instance with a '.this' attribute of
+ appropriate type. This change allows the following:
+
+ 1. The real shadow class instance for an object is
+ passed to the C wrappers where it can be examined/modified
+ by typemaps.
+
+ 2. Handling of default/keyword arguments is now greatly
+ simplified.
+
+ 3. The Python wrapper code is much more simple.
+
+ Plus, it eliminated more than 300 lines of C++ code in the
+ Python module.
+
+ *** CAVEAT : This requires the abstract object interface.
+ It should work with Python 1.4, but probably nothing older
+ than that.
+
+
+8/8/99 : Fixed handling of "const" and pointers in classes. In particular,
+ declarations such as
+
+ class foo {
+ ...
+ const char *msg;
+ const int *iptr;
+ }
+
+ are handled as assignable variables as opposed to constant
+ values (this is the correct behavior in C/C++). Note:
+ declarations such as "char *const msg" are still unsupported.
+ Constants declared at the global level using const are also
+ broken (because I have a number of interfaces that rely upon
+ this behavior).
+
+ *** POTENTIAL INCOMPATIBILITY *** This may break interfaces that
+ mistakenly treat 'const char *' types as constant values.
+
+8/8/99 : Modified the parser to support bit-fields. For example:
+
+ typedef struct {
+ unsigned int is_keyword : 1;
+ unsigned int is_extern : 1;
+ unsigned int is_static : 1;
+ } flags;
+
+ Bit-fields can only be applied to integer types and their
+ are other restrictions. SWIG performs no such type-checking
+ (although the C compiler will catch problems when it tries to
+ compile the wrapper code).
+
+8/8/99 : Removed trailing space of $basetype substitution in typemaps.
+ This is to allow things like this:
+
+ %typemap(python, argout) spam** OUTPUT{
+ ...
+ char* a = "$basetype_p";
+ ...
+ }
+
+ (Patch suggested by Nathan Dunfield).
+
+6/22/99 : Made a very slight tweak to the Perl5 shadow class
+ code that allows typemaps to alter the return type
+ of objects (to support polymorphic types). Patch
+ contributed by Drake Diedrich.
+
+4/8/99 : Fixed null pointer handling bug in Perl module.
+ Patch contributed by Junio Hamano.
+
+3/17/99 : Fixed bug in perl5ptr.swg for ActiveState Perl.
+ Patch contributed by Greg Anderson.
+
+2/27/99 : Eliminated segmentation fault when Swig runs on
+ empty files.
+
+2/27/99 : Added patch to Guile module to eliminate unused
+ variables. Contributed by Mike Simons.
+
+2/27/99 : Fixed problem with %addmethods returning references.
+
+2/27/99 : Fixed Runtime/Makefile. Patch contributed by
+ Mike Romberg.
+
+2/27/99 : Incorporated patches to the type-checker.
+
+2/27/99 : Fixed problem with -exportall switch and shadow classes
+ in Perl5 module. Patch contributed by Dennis Marsa.
+
+2/27/99 : Modified Perl5 module to recognize 'undef' as a NULL char *.
+ Patch contributed by Junio Hamano.
+
+2/27/99 : Fixed the Perl5 module to support the newer versions of
+ ActiveState Perl for Win32.
+
+2/27/99 : Fixed the include order of files specified with the
+ -I option.
+
+2/5/98- : Dave finishes his dissertation, goes job hunting, moves to
+2/5/99 Chicago and generally thrashes about.
+
+Version 1.1 Patch 5 (February 5, 1998)
+======================================
+
+2/4/98 : Fixed a bug in the configure script when different package
+ locations are specified (--with-tclincl, etc...).
+
+2/2/98 : Fixed name-clash bug related to the switch to C macros for accessor
+ functions. The new scheme did not work correctly for objects
+ with members such as 'obj', 'val', etc... Fixed the bug by
+ appending the word 'swig' to macro argument names. Patch
+ contributed by Rudy Albachten.
+
+2/2/98 : Slight fix to the Perl5 module to eliminate warning messages
+ about 'varname used only once : possible typo'. Fix
+ contributed by Rudy Albachten.
+
+1/9/98 : Fixed a bug in the Perl 5 module related to the creation of
+ constants and shadow classes.
+
+1/9/98 : Fixed linking bug with Python 1.5 embed.i library file.
+
+Version 1.1 Patch 4 (January 4, 1998)
+=====================================
+
+1/4/98 : Changed structured of the Examples directory to be more friendly
+ to Borland C++.
+
+1/4/98 : Added the function Makefile.win.bc for compiling the examples
+ under Borland 5.2.
+
+1/4/98 : Slight change to the perl5 module and C++ compilation. The
+ <math.h> library is now included before any Perl headers
+ because Perl the extern "C" linkage of math.h screws alot
+ of things up (especially on Windows).
+
+1/2/98 : Change to the Python module that reduces the number of constants
+ created by C++ classes, inheritance, and shadow classes. This
+ modification may introduce a few slight incompatibilities if
+ you attempt to use the non-shadow class interface with shadow
+ classes enabled. Patch contributed by Mike Romberg.
+
+1/2/98 : Support for Tcl 8.0 namespaces has been added. This *replaces*
+ the original SWIG mechanism that assumed [incr Tcl] namespaces.
+ To use namespaces, simply run SWIG with the following options
+
+ swig -tcl -namespace foo.i
+
+ This places everything in a namespace that matches
+ the module name
+
+ swig -tcl -namespace -prefix bar foo.i
+
+ This places everything in the namespace 'bar'
+
+ The use of namespaces is new in Tcl 8.0. However, the wrapper code
+ generated by SWIG will still work with all versions of Tcl newer
+ than and including Tcl 7.3/Tk3.6 even if the -namespace option is
+ used.
+
+ *** POTENTIAL INCOMPATIBILITY ***
+ This change may break existing applications that relied on the
+ -prefix and -namespace options.
+
+1/2/98 : Added the following constants to the Tcl wrapper code
+
+ SWIG_name - Name of the SWIG module
+ SWIG_prefix - Prefix/namespace appended to command names
+ SWIG_namespace - Name of the namespace
+
+ SWIG library writers can use these to their advantages.
+
+1/2/98 : Fixed a bug in the Tcl8 module related to the creation of
+ pointer constants (the function SWIG_MakePtr was missing from
+ the wrapper code).
+
+1/2/98 : Added the consthash.i library file to the Tcl and Tcl8 modules.
+
+1/1/98 : Changed and cleaned up the Python typemaps.i file. The following
+ significant changes were made :
+
+ 1. The OUTPUT typemap now returns Python tuples instead of
+ lists. Lists can be returned as before by using the
+ L_OUTPUT type. If compatibility with older versions
+ is needed, run SWIG with the -DOUTPUT_LIST option.
+
+ 2. The BOTH typemap has been renamed to INOUT. For backwards
+ compatibility, the "BOTH" method still exists however.
+
+ 3. Output typemaps now generate less code than before.
+
+ Changes to typemaps.i may break existing Python scripts that assume
+ output in the form of a list.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+12/31/97: Fixed long overdue problems with the testing scripts and certain
+ makefiles that required the use of the bash shell. Everything should
+ work properly with the standard Bourne shell (sh) now.
+
+12/31/97: Modified typemaps to allow $basetype as a valid local variable.
+ This allows for all sorts of bizarre hackish typemaps that
+ do cool things. Patch contributed by Dominique Dumont.
+
+12/31/97: Switched accessor functions generated for member data to
+ C preprocessor macros (except in cases involving typemaps
+ or char *).
+
+12/31/97: Fixed a bug related to C++ member data involving references.
+
+12/31/97: Changed accessor functions for C++ member functions to
+ preprocessor macros. This cleans up the wrapper code
+ and results in fewer function definitions.
+
+12/31/97: Changed the default C constructor to use calloc() instead
+ of malloc()
+
+12/30/97: Changed the creation of constants in the Perl5 module.
+ For all practical purposes, they should work in exactly the
+ same way as before except that they now require much less
+ wrapper code. Modules containing large numbers of
+ constants may see greater than a 50% reduction in wrapper
+ code size.
+
+12/30/97: Modified the Python module to be more intelligent about the
+ creation of constants. SWIG no longer generates redundant
+ global variables and the size of the module initialization
+ function should be reduced. (Many thanks to Jim Fulton).
+
+12/29/97: Fixed a bug in C++ code generation related to member functions,
+ default arguments, and references.
+
+12/29/97: Fixed configure script and a few makefiles to support Python 1.5
+
+12/29/97: Added 'embed15.i' library file. This file should be used to
+ statically link versions of Python 1.5. To make it the default,
+ simply copy 'swig_lib/python/embed15.i' to 'swig_lib/python/embed.i'
+
+Version 1.1 Patch 3 (November 24, 1997)
+========================================
+
+11/23/97: Fixed a bug in the Perl5 module with shadow classes and
+ static class functions that return class instances.
+ Note : The fix for this bug requires a slight restructuring of
+ of the .pm files created by SWIG.
+
+11/23/97: Fixed a bug in the Tcl/Tcl8 modules related to variable linking
+ of character arrays. If you declared a global variable 'char foo[10]',
+ the generated wrapper code would either cause a segmentation fault
+ immediately upon loading or weird memory corruption elsewhere.
+ This should now be fixed although character arrays can only be
+ read-only.
+
+11/23/97: Fixed a bug with the %import directive that caused it to
+ fail if files were imported from directories other than
+ the current working directory.
+
+11/23/97: Fixed incorrect diagnostic message in the ASCII documentation
+ module.
+
+11/23/97: Changed the behavior of the -o option when used with shadow
+ classes. If -o was used to specify both the pathname and filename
+ of SWIG's output such as
+
+ swig -o /home/swig/wrapper.c -shadow -perl5 foo.i
+
+ The wrapper code would be placed the file specified with -o,
+ but the .pm file and documentation would be placed in the
+ directory where SWIG was run. Now, these files are placed
+ in the same directory as the file specified with the -o option.
+ This change is also needed for proper operation on the
+ Macintosh.
+
+11/23/97: Added a 'this()' method to Perl5 shadow classes. This can
+ be used to return the normal pointer value from a shadow
+ class that is represented as a tied hash. To use just
+ invoke as a method like this :
+
+ $l = new List; # Create an object
+ $ptr = $l->this(); # Get the normal pointer value
+
+ *** NEW FEATURE ***
+
+11/23/97: Fixed the Tcl 8 pointer.i library file (which was completely
+ broken in 1.1p2).
+
+11/23/97: Modified the Perl5 type-checker to fix a few problems
+ with global variables of pointer types and to allow
+ tied hashes to be used interchangably with normal
+ pointer values.
+
+11/23/97: Modified the typemap mechanism to allow output
+ typemaps of type 'void'. These were ignored previously,
+ but now if you specify,
+
+ %typemap(lang,out) void {
+ ... return a void ...
+ }
+
+ You can change or assign a return value to the function.
+
+11/23/97: Fixed processing of 'bool' datatypes in the Python module.
+
+11/23/97: Fixed minor parsing error with C++ initializers. For example,
+
+ class B : public A {
+ public:
+ B() : A() { ... };
+ ...
+ }
+
+11/23/97: Fixed the Tcl8 module so that C functions that call back into
+ Tcl don't corrupt the return result object (SWIG was gathering
+ the result object too early which leads to problems if subsequent
+ Tcl calls are made).
+
+11/23/97: Fixed a code generation bug in the Python module when two or
+ more output parameters were used as the first arguments of a
+ function. For example :
+
+ %include typemaps.i
+ void foo(double *OUTPUT, double *OUTPUT, double a);
+
+ Previously, doing this resulted in the creation of an
+ extraneous comma in the output, resulting in a C syntax error.
+
+11/22/97: Fixed a bug when template handling that was stripping whitespace
+ around nested templates. For example :
+
+ Foo<Bar<double> >
+
+ was getting munged into Foo<Bar>> which is a syntax error in
+ in the C++ compiler.
+
+11/22/97: Fixed bugs in the Borland C++ makefiles.
+
+11/22/97: Fixed memory corruption bug when processing integer
+ arguments in Tcl8 module.
+
+11/21/97: Fixed a bug in the Runtime/Makefile related to Tcl 8.
+
+11/21/97: Fixed a bug with the %new directive and Perl5 shadow classes.
+ No longer generates a perl syntax error.
+
+11/9/97 : Changed a strncpy() to strcpy() in the pointer type-checker.
+ This results in a substantial performance improvement in
+ type-checking.
+
+10/29/97: Fixed a bug in the code generation of default arguments and
+ user-defined types. For example :
+
+ void foo(Vector a, Vector b = d);
+
+ should now work properly.
+
+Version 1.1 Patch 2 (September 4, 1997)
+=======================================
+
+9/4/97 : Fixed problem with handling of virtual functions that
+ was introduced by some changes in the C++ module.
+
+Version 1.1 Patch 1 (August 27, 1997)
+=====================================
+
+8/26/97 : Fixed compilation and run-time bugs with Tcl 8.0 final.
+
+8/21/97 : Fixed code generation bug with arrays appearing as arguments
+ to C++ member functions. For example :
+
+ class Foo {
+ public:
+ void Bar(int a[20][20]);
+ };
+
+ There is still a bug using arrays with added methods
+ however.
+
+8/20/97 : Fixed a bug with generating the code for added methods
+ involving pass-by-value.
+
+8/19/97 : Modified the typemapper to substitute the '$arg' value
+ when declaring local variables. For example :
+
+ %typemap(in) double * (double temp_$arg) {
+ ... do something ...
+ }
+
+ When applied to a real function such as the following :
+
+ void foo(double *a, double *b, double *result);
+
+ three local variables will be created as follows :
+
+ double temp_a;
+ double temp_b;
+ double temp_result;
+
+ This can be used when writing multiple typemaps that need
+ to access the same local variables.
+
+
+7/27/97 : Fixed a variety of problems with the %apply directive and arrays.
+ The following types of declarations should now work :
+
+ %apply double [ANY] { Real [ANY] };
+ %apply double [4] { double [10] };
+
+ A generic version of apply like this :
+
+ %apply double { Real };
+
+ should now work--even if arrays involving doubles and Reals are
+ used later.
+
+7/27/97 : Changed warning message about "Array X has been converted to Y" to
+ only appear if running SWIG in verbose mode.
+
+7/27/97 : Added the variables $parmname and $basemangle to the typemap
+ generator. $parmname is the name of the parameter used
+ when the typemap was matched. It may be "" if no parameter
+ was used. $basemangle is a mangled version of the base
+ datatype. Sometimes used for array handling.
+
+7/27/97 : Changed the behavior of output arguments with Python shadow classes.
+ Originally, if a function returned an object 'Foo', the shadow class
+ mechanism would create code like this :
+
+ def return_foo():
+ val = FooPtr(shadowc.return_foo())
+ val.this = 1
+ return val
+
+ The problem with this is that typemaps allow a user to redefine
+ the output behavior of a function--as a result, we can no longer
+ make any assumptions about the return type being a pointer or
+ even being a single value for that matter (it could be a list,
+ tuple, etc...). If SWIG detects the use of output typemaps
+ (either "out" or "argout") it returns the result unmodified like
+ this :
+
+ def return_foo():
+ val = shadowc.return_foo()
+ return val
+
+ In this case, it is up to the user to figure out what to do
+ with the return value (including the possibility of converting it
+ into a Python class).
+
+7/26/97 : Fixed a parsing problem with types like 'unsigned long int',
+ 'unsigned short int', etc...
+
+7/24/97 : Minor bug fix to Tcl 8 module to parse enums properly. Also
+ fixed a memory corruption problem in the type-checker.
+ (patch contributed by Henry Rowley.
+
+7/24/97 : Added Python-tuple typemaps contributed by Robin Dunn.
+
+7/24/97 : Incorporated some changes to the Python module in support of
+ Mark Hammond's COM support. I'm not entirely sure they
+ work yet however. Needs documentation and testing.
+
+7/24/97 : Fixed code generation bugs when structures had array members
+ and typemaps were used. For example :
+
+ %typemap(memberin) double [20][20] {
+ ... get a double [20][20] ...
+ }
+ struct Foo {
+ double a[20][20];
+ }
+
+ Originally, this would generate a compiler-type error when
+ the wrapper code was compiled. Now, a helper function like
+ this is generated :
+
+ double *Foo_a_set(Foo *a, double val[20][20]) {
+ ... memberin typemap here ...
+ return (double *) val;
+ }
+
+ When writing typemaps, one can assume that the source variable
+ is an array of the *same* type as the structure member. This
+ may break some codes that managed to work around the array bug.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+7/13/97 : Fixed bug in Perl5 module when using C global variables that
+ are pointers. When used in function calls and other operations,
+ the value of the pointer would be invalid---causing core
+ dumps and other problems. SWIG implements global variables
+ using Perl magic variables. As it turns out, the error
+ was caused by the fact that the pointer-extraction code
+ was somehow bypassing the procedure used to resolve magical
+ variables (hence, leaving the value undefined). To fix
+ the problem, SWIG now explicitly resolves magic before
+ extracting pointer values.
+
+7/12/97 : Eliminated the last remnants of free() and malloc() from
+ the SWIG compiler.
+
+7/12/97 : Fixed parsing problems with typemaps involving arrays and
+ temporary variables of arrays. Also made it possible for
+ SWIG to handle typemaps like this :
+
+ %typemap(in) double [ANY] (double temp[$dim0]) {
+ ... store data in temp[$dim0] ...
+ }
+
+ Not only does this typemap match any double [] array, it
+ creates a local variable with precisely the right dimensions.
+ (ie. $dim0 gets filled in with the real number of dimensions).
+ Of course, off the record, this will be a way to add more
+ functionality to the typemaps.i libraries.
+
+7/9/97 : Fixed some problems with Perl5, static linking, and shadow
+ classes. When statically linking multiple modules together, write
+ a top-level interface file like this when shadow classes are not
+ used :
+
+ %module swig, foo, bar, glob;
+ %include perlmain.i
+
+ When shadow classes are used, the module names have an extra 'c'
+ appended so it should read as :
+
+ %module swig, fooc, barc, globc;
+ %include perlmain.i
+
+ When linking multiple modules, consider using the SWIG runtime
+ library.
+
+7/8/97 : Incorporated fixed versions of the Borland C++ Makefiles.
+
+7/8/97 : First cut at trying to eliminate excessive compiler warnings.
+ As it turns out, alot of warnings go away if you just make
+ declarations like this
+
+ clientData = clientData;
+
+ in the resulting wrapper code. Most compilers should just
+ ignore this code (at least would can hope).
+
+7/8/97 : Fixed bizarre code generation bug with typemaps and C++ classes.
+ In some cases, typemaps containing printf formatting strings such as
+
+ %typemap(memberout) int * {
+ printf("%d",42);
+ }
+
+ Would generate completely bogus code with garbage replacing
+ the '%d'. Caused by one faulty use of printf (wasn't able to find
+ any other occurrences).
+
+7/7/97 : Fixed bug in Python shadow class generation with non-member
+ functions that are returning more than one value.
+
+7/7/97 : Incorporated modifications to make SWIG work with Guile 1.2.
+ Still need to test it out, but it is rumored to work.
+
+7/2/97 : Fixed some bugs related to output arguments and Python shadow
+ classes. If an output argument is detected, SWIG assumes
+ that the result is a list and handles it appropriately.
+ If the normal return type of an function is an object,
+ it will be converted into a shadow class as before, but
+ with the assumption that it is the first element of a
+ list. *** NOTE : This behavior has been subsequently changed ***
+
+6/29/97 : Changed EXPORT to SWIGEXPORT in all of the language modules.
+ Should provide better compatibility with Windows.
+
+6/29/97 : Modified Python shadow classes so that output arguments
+ work correctly (when typemaps are used).
+
+Version 1.1 (June 24, 1997)
+===========================
+
+6/24/97 : Fixed Objective-C constructor bug when working with Perl5
+ shadow classes.
+
+6/23/97 : Fixed some parsing problems with Objective-C. Declarations
+ such as the following should work now :
+
+ - foo : (int) a with: (int) b;
+
+6/22/97 : Added SWIG Runtime library. This library contains
+ the SWIG pointer type-checker and support functions
+ that are normally included in every module. By using
+ the library, it is easier to work with multiple SWIG
+ generated modules.
+
+6/22/97 : Fixed minor bug in Perl5 module related to static linking
+ of multiple modules.
+
+6/22/97 : Fixed some bugs with the %import directive. When used with
+ Perl5 shadow classes, this generates a 'require' statement
+ to load in external modules.
+
+6/22/97 : Added -swiglib option. This prints out the location of the
+ SWIG library and exits. This option is only really useful to
+ configuration tools that are looking for SWIG and its library
+ location (e.g. autoconf, configure, etc...).
+
+6/21/97 : Fixed export bug with Perl5.004 on Windows-NT.
+
+6/20/97 : Minor change to code generation of class/structure members in
+ order to work better with typemaps. Should have no noticable
+ impact on existing SWIG modules.
+
+6/19/97 : Added -t option. This allows SWIG to load a typemap file before
+ processing any declarations. For example :
+
+ swig -t typemaps.i -python example.i
+
+ At most, only one typemap file can be specified in this manner.
+ *** NEW FEATURE ***
+
+6/18/97 : Need a Makefile fast? Type
+
+ swig [-tcl, -perl5, -python] -co Makefile
+
+ and you will get a Makefile specific to that target language.
+ You just need to modify it for your application and you're
+ ready to run.
+
+6/18/97 : Completed the -ci option. This option checks a file into the
+ SWIG library. It should be used in conjunction with a
+ language option. For example :
+
+ swig -tcl -ci foobar.i
+
+ Checks the file foobar.i into the Tcl part of the library.
+ In order to check a file into the general library (accessible
+ to all languages modules), do the following
+
+ swig -ci -o ../foobar.i foobar.i
+
+ (Admittedly this looks a little strange but is unavoidable).
+ The check-in option is primarily designed for SWIG maintenance
+ and library development. The command will fail if the user does
+ not have write permission to the SWIG library. Third party library
+ extensions can easily install themselves by simply providing
+ a shell script that uses 'swig -ci' to install the appropriate
+ library files. It is not necessary to know where the SWIG library
+ is located if you use this mechanism.
+ *** NEW FEATURE ***
+
+6/16/97 : Fixed a bug in shadow class generation when %name() was applied
+ to a class definition. Unfortunately, fixing the bug required
+ a change in the Language C API by adding an extra argument to
+ the Language::cpp_class_decl() function. This may break
+ SWIG C++ extensions.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+6/15/97 : Added a warning message if no module name is specified with the
+ %module directive or -module option.
+
+6/15/97 : Fixed line number bug when reporting errors for undefined
+ base classes.
+
+6/15/97 : Added new %rename directive. This allows the forward declaration
+ of a renaming. For example :
+
+ %rename OldName NewName;
+
+ .... later ...
+ int OldName(int);
+
+ Unlike %name, %rename will rename any occurrence of the old name.
+ This applies to functions, variables, class members and so forth.
+ There is no way to disable %rename once set, but you can change the
+ name by redeclaring it to something else.
+ *** NEW FEATURE ***
+
+6/15/97 : Improved the implementation of the %name directive so that it
+ could be used with conditional compilation :
+
+ #ifdef SWIG
+ %name(NewName)
+ #endif
+ int OldName(int);
+
+6/15/97 : Added support for functions with no return datatype. In this case,
+ SWIG assumes a return type of 'int'.
+
+6/11/97 : Improved error reporting in the parser. It should be a little
+ less sensitive to errors that occur inside class definitions
+ now. Also reports errors for function pointers.
+
+6/11/97 : Made '$' a legal symbol in identifiers. This is to support
+ some Objective-C libraries. Some compilers (such as gcc) may also
+ allow identifiers to contain a $ in C/C++ code as well (this is
+ an obscure feature of C). When '$' appears in identifier, SWIG
+ remaps it to the string '_S_' when creating the scripting language
+ function. Thus a function 'foo$bar' would be called 'foo_S_bar'.
+
+6/11/97 : Fixed bug in Python shadow classes with __repr__ method. If
+ supplied by the user, it was ignored, but now it should work.
+
+6/9/97 : Fixed the Tcl 8.0 module to work with Tcl 8.0b1. SWIG is no
+ longer compatible with *any* alpha release of Tcl 8.0.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+6/7/97 : Put a maximal error count in (currently set to 20). SWIG will bail out
+ if it generates more errors than this (useful for preventing SWIG
+ from printing 4000 syntax errors when it gets confused).
+
+6/7/97 : Fixed segmentation fault when parsing variable length arguments.
+
+6/7/97 : Minor change to Perl5 module. C++ static functions are now
+ put in the same package as their class when using shadow classes.
+
+6/7/97 : Centralized the naming of functions, members, wrappers etc... By
+ centralizing the naming scheme, it should be possible to make
+ some multi-file optimizations. Also, it should be possible to
+ change SWIG's naming scheme (perhaps a new feature to be added
+ later).
+
+6/2/97 : Added 'arginit' typemap. This can be used to assign initial values
+ to function arguments. Doing so makes it somewhat easier to detect
+ improper argument passing when working with other typemaps.
+
+6/2/97 : Fixed code generation bug when read-only variables were inherited
+ into other classes. Under inheritance, the variables would
+ become writable, but this has now been corrected.
+
+5/30/97 : An empty %name() directive is no longer allowed or supported.
+ This directive was originally used to strip the prefix
+ off of a class or structure. Unfortunately, this never really
+ seemed to work right and it complicated the C++ code generator
+ significantly. As far as I can tell no one uses it, so it
+ is now history. *** POTENTIAL INCOMPATIBILITY ***
+
+5/28/97 : Fixed a parsing bug with #define and C++ comments. Declarations
+ such as the following now work properly :
+
+ #define CONST 4 // A Comment
+
+5/28/97 : Made some performance improvements to the SWIG String class.
+ (only affects the SWIG compiler itself).
+
+5/28/97 : Modified the parser to skip template definitions and issue a
+ warning message.
+
+5/28/97 : Preliminary support for parameterized types added (ie. templates).
+ Types such as the following should pass through the SWIG compiler
+
+ void foo(vector<complex> *a, vector<double> *b);
+
+ When used, the entire name 'vector<complex>' becomes the name
+ of the datatype. Due to space limitations in datatype
+ representations, the name should not exceed 96 characters.
+
+ Note : This is only part of what is needed for template support.
+ Template class definitions are not yet supported by SWIG.
+
+ The template notation above may also be used when specifying
+ Objective-C protocol lists.
+ *** NEW FEATURE ***
+
+5/24/97 : First cut at Objective-C support added. As it turns out, almost
+ everything can be handled with only a few minor modifications to
+ the C++ module.
+ *** NEW FEATURE ***
+
+5/23/97 : Fixed repeated definition bug in multiple inheritance handling
+ when multiple base classes share a common base class (ie.
+ the evil diamond).
+
+5/21/97 : Fixed rather embarrassing typo that worked its way into the
+ Tests/Build directory.
+
+5/19/97 : Fixed code generation bug when using native methods and
+ shadow classes with Python and Perl5 modules.
+
+5/19/97 : Modified the %apply directive slightly so that it would work
+ with pointers a little better. For example :
+
+ %apply unsigned long { DWORD };
+
+ Applies *all* typemaps associated with "unsigned long" to
+ "DWORD". This now includes pointers to the two datatypes.
+ For example, a typemap applied to "unsigned long **" would
+ also be applied to any occurrence of "DWORD **" as well.
+
+5/19/97 : Fixed an ownership assignment bug in the Perl5 module when
+ class members were returning new objects belonging to
+ different classes.
+
+5/17/97 : Added a few more typemap variables.
+
+ $name - Name of function/variable/member
+ $basetype - Base datatype (type without pointers)
+ $argnum - Argument number
+
+5/16/97 : Fixed embarrassing underscore error in local variable
+ allocator.
+
+5/16/97 : Fixed namespace clash bug in parameterized typemaps
+ when creating arrays as new local variables.
+
+5/15/97 : Fixed some bugs with inheritance of added methods across
+ multiple files. SWIG now uses names of base classes
+ when generating such functions.
+
+5/14/97 : Finished support for default typemaps. Primarily used
+ internally, they can be used to match the basic
+ built-in datatypes used inside of SWIG. You can
+ specify them in interface files as well like this :
+
+ %typemap(tcl,in) int SWIG_DEFAULT_TYPE {
+ $target = atoi($target);
+ }
+
+ Unlike normal typemaps, this default map will get applied
+ to *all* integer datatypes encountered, including those
+ renamed with typedef, etc...
+
+5/13/97 : Fixed substring bug in type checker.
+
+5/12/97 : Fixed bug in parameterized typemaps when declaring local
+ variables of structures.
+
+Version 1.1 Beta6 (May 9, 1997)
+===============================
+
+5/9/97 : Fixed bizarre NULL pointer handling bug in Perl5 module.
+
+5/8/97 : Fixed mysterious segmentation fault when running SWIG on an
+ empty file.
+
+5/7/97 : The code generator will now replace the special symbol "$cleanup"
+ with the cleanup code specified with the "freearg" typemap.
+ This change needed to properly manage memory and exceptions.
+
+5/5/97 : Added the 'typemaps.i' library file. This contains a
+ variety of common typemaps for input values, pointers,
+ and so on.
+
+5/5/97 : Changed behavior of "argout" typemap in Python module.
+ Old versions automatically turned the result into a
+ Python list. The new version does nothing, leaving the
+ implementation up to the user. This provides more flexibility
+ but may break older codes that rely on typemaps.
+ *** POTENTIAL INCOMPATIBILITY ***
+
+5/5/97 : Fixed bug in Python module related to the interaction of
+ "argout" and "ignore" typemaps.
+
+5/5/97 : Fixed bug in Python module that would generate incorrect code
+ if all function arguments are "ignored".
+
+5/4/97 : Added %apply and %clear directives. These form a higher level
+ interface to the typemap mechanism. In a nutshell, they
+ can be used to change the processing of various datatypes without
+ ever having to write a typemap. See the SWIG documentation
+ for more details. ** NEW FEATURE **
+
+5/4/97 : Added a local variable extension to the typemap handler.
+ For example :
+
+ %typemap(tcl,in) double *(double temp) {
+ temp = atof($source);
+ $target = &temp;
+ }
+
+ In this case, 'temp' is a local variable that exists
+ in the entire wrapper function (not just the typemap
+ code). This mechanism provides better support for
+ certain types of argument handling and also makes it
+ possible to write thread-safe typemaps. Any number
+ local variables can be declared by supplying a comma
+ separated list. Local variables are guaranteed to be
+ unique, even if the same typemap is applied many times
+ in a given function.
+ ** Not currently supported in Perl4 or Guile modules.
+
+5/2/97 : Fixed processing of %ifdef, %endif, %if, etc... (These are
+ SWIG equivalents of the C preprocessor directives that
+ can pass through the C preprocessor without modification).
+
+5/2/97 : Fixed major (but subtle) bug in the run-time type checker
+ related to searching and type-checking for C++ inheritance.
+ To make a long story short, if you had two classes named
+ "Foo" and "FooObject" the type checker would sometimes
+ get confused and be unable to locate "Foo" in an internal
+ table.
+
+5/2/97 : Fixed some bugs in the -co option.
+
+4/24/97 : Pointer library added to the SWIG library.
+
+4/19/97 : Added the %new directive. This is a "hint" that can be used
+ to tell SWIG that a function is returning a new object. For
+ example :
+
+ %new Foo *create_foo();
+
+ This tells SWIG that create_foo() is creating a new object
+ and returning a pointer to it. Many language modules may
+ choose to ignore the hint, but when working with shadow classes,
+ the %new is used to handle proper ownership of objects.
+
+ %new can also be used with dynamically allocated strings.
+ For example :
+
+ %new char *create_string();
+
+ When used, all of the language modules will automatically cleanup
+ the returned string--eliminating memory leaks.
+ ** NEW FEATURE **
+
+4/19/97 : Added a new typemap "newfree". This is used in conjunction with
+ the %new directive and can be used to change the method by which
+ a new object returned by a function is deleted.
+
+4/19/97 : The symbol "__cplusplus" is now defined in the SWIG interpreter
+ when running with the -c++ option.
+
+4/17/97 : Added support for static member functions when used inside the
+ %addmethods directive.
+
+4/15/97 : Added a special typemap symbol PREVIOUS that can be used to
+ restore a previous typemap. For example :
+
+ %typemap(tcl,in) int * = PREVIOUS;
+
+ This is primarily used in library files.
+
+4/13/97 : Added %pragma directive for Perl5 module. Two new pragmas are
+ available :
+
+ %pragma(perl5) code = "string"
+ %pragma(perl5) include = "file.pl"
+
+ Both insert code into the .pm file created by SWIG. This can
+ be used to automatically customize the .pm file created by SWIG.
+
+4/13/97 : Scanner modified to only recognize C++ keywords when the -c++
+ option has been specified. This provides support for C programs
+ that make use of these keywords for identifiers.
+ SWIG may need to be explicitly run with the -c++ option when
+ compiling C++ code (this was allowed, but not recommended in
+ previous versions). **POTENTIAL INCOMPATIBILITY**
+
+4/11/97 : Fixed a rather nasty bug in the Perl5 module related to using
+ variable linking with complex datatypes and pointers. On Unix,
+ code would work (somehow), but would cause an access violation
+ under Windows-NT. The fix should correct the problem,
+ but there may still be a problem using global variables of
+ complex datatypes in conjunction with shadow classes. Fortunately,
+ this sort of thing seems to be relatively rare (considering
+ that the bug has been around for more than a year - yikes!).
+
+4/11/97 : Fixed bizarre constant evaluation bug in Perl5 code generation
+ when running under Windows-NT.
+
+4/8/97 : Bug when using default arguments and C++ references fixed.
+
+4/8/97 : Fixed code generation bugs in Python and Perl5 modules related to
+ using class renaming (applying the %name directive to a class
+ definition) and shadow classes.
+
+4/7/97 : Fixed minor bugs in swigptr.swg, tcl8ptr.swg, and perl5ptr.swg to
+ prevent infinite loops when weird datatypes are passed.
+
+3/29/97 : 'Makefile.win' added. This is used to build most of the examples
+ in the Examples directory under Windows NT/95.
+
+3/27/97 : Fixes to SWIG's error return codes. SWIG now returns non-zero
+ exit codes for certain kinds of errors (which makes it more
+ friendly to makefiles). An overhaul of the error handling
+ is on the to-do list and will probably show up in a later release.
+
+3/25/97 : Bug fix. "freearg" and "argout" typemaps have been fixed in
+ the Perl5 module. In previous versions, function input parameters
+ and function output parameters shared the same memory space--causing
+ all sorts of nasty problems when trying to pass perl values by
+ reference. SWIG now internally makes a "copy" (which is really
+ just a pointer) of affected parameters and uses that. This
+ is done transparently so there is no noticable impact on any
+ SWIG generated modules. This change is probably only noticable
+ to expert users.
+
+3/25/97 : Added type-check to verbose and stat mode. SWIG will now generate a list
+ of all datatypes that were used but undefined (useful for tracking
+ down weird bugs). This is enabled with the -v option (which
+ is now officially known as "overly verbose" mode) or the -stat option.
+
+3/25/97 : Slight change to the parser to make include guards work correctly.
+ For example :
+
+ #ifndef INTERFACE_I
+ #define INTERFACE_I
+ %module foobar.i
+ ... declarations ...
+ #endif
+
+3/24/97 : %checkout directive added. This allows an interface file to
+ extract files from the SWIG library and place them in the
+ current directory. This can be used to extract scripts and
+ other helper code that might be associated with library files.
+ For example :
+
+ %checkout array.tcl
+
+ Will look for a file "array.tcl" in the library and copy it
+ to the current directory. If the file already exists in the
+ directory, this directive does nothing (it will not overwrite an
+ existing file). This only an experimental feature for now.
+
+3/24/97 : SWIG will now look in the SWIG Library for a file if it can't
+ find it in the current directory. As a result, it is easy to
+ make modules from SWIG library files. For example, if you
+ want to make a Python module from the SWIG timers library, just
+ type this in any directory :
+
+ swig -python timers.i
+
+ You will get the files timers_wrap.c and timers_wrap.doc in
+ the current directory that you can now compile. The file
+ remains in the SWIG library (although you can check it out
+ using the -co option). *** New Feature ***
+
+3/24/97 : -co option added to SWIG to allow easy access to the SWIG library.
+ When used, this instructs SWIG to check out a library file and
+ place it in the current directory. For example :
+
+ unix > swig -co array.i
+ array.i checked out from the SWIG library
+ unix >
+
+ Once in your directory you can customize the file to suit your
+ particular purposes. The checkout option makes it easy to
+ grab library files without knowing anything about the SWIG
+ installation, but it also makes it possible to start
+ including scripts, C code, and other miscellaneous files
+ in the library. For example, you could put a cool script
+ in the library and check it out whenever you wanted to use it.
+ *** New Feature ***
+
+3/24/97 : #pragma export directives added to Tcl output for compiling
+ shared libraries on the Mac.
+
+3/24/97 : Minor changes to wish.i and tclsh.i library files to provide
+ support for the Macintosh.
+
+3/19/97 : SWIG's policy towards NULL pointers has been relaxed. The
+ policy of requiring a special compiler directive -DALLOW_NULL
+ to use NULL pointers is no longer supported. While this may
+ seem "unsafe", it turns out that you can use a "check"
+ typemap to achieve some safety. For example :
+
+ %typemap(perl5,check) Node * {
+ if (!$target)
+ croak("NULL Pointers not allowed.");
+ }
+
+ This prevents any NULL value of a "Node *" pointer to be
+ passed to a function. (I think this is much cleaner
+ than the old -DALLOW_NULL hack anyways).
+
+3/19/97 : Fixed pointer handling errors in Perl5 module. Modules no
+ longer core dump when a Perl reference is inadvertently
+ passed in as a C pointer.
+
+3/18/97 : Added a "check" typemap. This can be used to check the
+ validity of function input values. For example :
+
+ %typemap(perl5,check) int posint {
+ if ($target < 0)
+ croak("Argument is not a positive integer");
+ }
+
+3/18/97 : Added an $arg variable to Tcl typemaps. This makes it easier
+ to return argument values by "reference".
+
+3/18/97 : Fixed a code generation bug when using C++ references and
+ the %addmethods directive.
+
+3/18/97 : Fixed a few glitches in the typemap module with respect to
+ chaining. For example :
+
+ %typemap(tcl,in) int {
+ $in // Inserts prexisting typemap
+ printf("Received a %d\n", $target);
+ }
+
+ This has been allowed for quite some time, but didn't work
+ if no existing typemap was defined. Now, it still doesn't
+ work if no existing typemap is defined, but it issues a
+ warning message. There is some support using default typemaps,
+ but none of the language modules take advantage of it. This
+ should be considered experimental at this time.
+
+Version 1.1b5 Patch 1 (March 16, 1997)
+======================================
+
+3/16/97 : Fixed references bug with C++ code generation.
+
+3/16/97 : Fixed initialization bug in the documentation system that
+ was causing weird problems.
+
+3/16/97 : Fixed fatal bug with -c option in the Python module.
+
+3/13/97 : Fixed bug in the documentation system involving the %text directive
+ and sorting. In the old system, %text entries would float to the
+ top of a section because they were "nameless". Now they are
+ attached to the previous declaration and will stay in the proper
+ location relative to the previous entry.
+
+Version 1.1b5 (March 12, 1997)
+==============================
+
+3/11/97 : Fixed compilation problems introduced by Tcl/Tk 8.0a2.
+ *** INCOMPATIBILITY *** SWIG no longer works with Tcl/Tk 8.0a1.
+
+3/10/97 : Fixed bug with ignored arguments and C++ member functions in
+ the Python module.
+
+3/9/97 : Parsing bugs with nested class definitions and privately
+ declared nested class definitions fixed.
+
+3/9/97 : Fixed a few minor code generation bugs with C++ classes and
+ constructors. In some cases, the resulting wrapper code
+ would not compile properly. SWIG now attempts to use
+ the default copy constructor instead.
+
+3/8/97 : Added a -l option to SWIG that allows additional SWIG library files
+ to be grabbed without having them specified in the interface file.
+ This makes it easier to keep the interface file clean and move certain
+ options into a Makefile. For example :
+
+ swig -tcl example.i # Build a normal Tcl extension
+ swig -tcl -lwish.i example.i # Build it as a wish extension
+ # by including the 'wish.i' file.
+
+ swig -python example.i # Build a dynamically loaded extension
+ swig -python -lembed.i example.i # Build a static extension
+
+ These kinds of options could previously be accomplished with
+ conditional compilation such as :
+
+ %module example
+ ...
+ #ifdef STATIC
+ %include embed.i
+ #endif
+
+3/8/97 : Incorporated changes to Guile module to use the new gh interface
+ in FSF Guile 1.0. The older gscm interface used in Cygnus
+ Guile releases is no longer supported by SWIG.
+
+3/8/97 : Cleaned up the Tcl Netscape plugin example. It should work with
+ version 1.1 of the plugin now.
+
+3/8/97 : Added better array support to the typemap module. The keyword
+ ANY can now be used to match any array dimension. For example :
+
+ %typemap(tcl,in) double [ANY] {
+ ... get an array ...
+ }
+
+ This will match any single-dimensional double array. The array
+ dimension is passed in the variables $dim0, $dim1, ... $dim9. For
+ example :
+
+ %typemap(tcl,in) double [ANY][ANY][ANY] {
+ printf("Received a double[%d][%d][%d]\n",$dim0,$dim1,$dim2);
+ }
+
+ Any typemap involving a specific array dimension will override any
+ specified with the ANY tag. Thus, a %typemap(tcl,in) double [5][4][ANY] {}
+ would override a double [ANY][ANY][ANY]. However, overuse of the ANY
+ tag in arrays of high-dimensions may not work as you expect due to
+ the pattern matching rule used. For example, which of the following
+ typemaps has precedence?
+
+ %typemap(in) double [ANY][5] {} // Avoid this!
+ %typemap(in) double [5][ANY] {}
+
+3/7/97 : Fixed a number of bugs related to multi-dimensional array handling.
+ Typedefs involving multi-dimensional arrays now works correctly.
+ For example :
+
+ typedef double MATRIX[4][4];
+
+ ...
+ extern double foo(MATRIX a);
+
+ Typecasting of pointers into multi-dimensional arrays is now
+ implemented properly when making C/C++ function calls.
+
+3/6/97 : Fixed potentially dangerous bug in the Tcl Object-oriented
+ interface. Well, actually, didn't fix it but issued a
+ Tcl error instead. The bug would manifest itself as follows:
+
+ % set l [List] # Create an object
+ ...
+ % set m [List -this $l] # Make $m into an object assuming $l
+ # contains a pointer.
+ # Since $m == $l, $l gets destroyed
+ # (since its the same command name)
+ % $m insert Foo
+ Segmentation fault # Note : the list no longer exists!
+
+ Now, an error will be generated instead of redefining the command.
+ As in :
+
+ % set l [List]
+ ...
+ % set m [List -this $l]
+ Object name already exists!
+
+ Use catch{} to ignore the error.
+
+3/3/97 : Better support for enums added. Datatypes of 'enum MyEnum'
+ and typedefs such as 'typedef enum MyEnum Foo;' now work.
+
+3/3/97 : Parser modified to ignore constructor initializers such as :
+
+ class Foo : public Bar {
+ int a,b;
+ public:
+ Foo(int i) : a(0), b(i), Bar(i,0) { };
+ };
+
+3/3/97 : Modified parser to ignore C++ exception specifications such as :
+
+ int foo(double) throw(X,Y);
+
+3/3/97 : Added %import directive. This works exactly like %extern
+ except it tells the language module that the declarations are
+ coming from a separate module. This is usually only
+ needed when working with shadow classes.
+
+3/2/97 : Changed pointer type-checker to be significantly more
+ efficient when working with derived datatypes. This
+ has been accomplished by storing type-mappings in sorted
+ order, using binary search schemes, and caching recently
+ used datatypes. For SWIG generated C++ modules that
+ make a large number of C function calls with derived types,
+ this could result in speedups of between 100 and 50000 percent.
+ However, due to the required sorting operation, module
+ loading time may increased slightly when there are lots of
+ datatypes.
+
+3/2/97 : Fixed some C++ compilation problems with Python
+ embed.i library files.
+
+2/27/97 : Slight change to C++ code generation to use copy constructors
+ when returning complex data type by value.
+
+2/26/97 : Fixed bug in Python module with -c option.
+
+2/26/97 : Slight tweak of parser to allow trailing comma in enumerations
+ such as
+
+ enum Value (ALE, STOUT, LAGER, };
+
+2/25/97 : Fixed code generation bug in Tcl module when using the
+ %name() directive on a classname.
+
+2/25/97 : Finished code-size optimization of C++ code generation with
+ inheritance of attributes. Inherited attributes now
+ only generate one set of wrapper functions that are re-used
+ in any derived classes. This could provide big code
+ size improvements in some scripting language interfaces.
+
+2/25/97 : Perl5 module modified to support both the Unix and Windows
+ versions. The windows version has been tested with the
+ Activeware port of Perl 5.003 running under Windows 95.
+ The C source generated by SWIG should compile without
+ modification under both versions of Perl, but is now
+ even more hideous than before.
+
+2/25/97 : Modified parser to allow scope resolution operation to
+ appear in expressions and default arguments as in :
+
+ void foo(int a = Bar::defvalue);
+
+2/25/97 : Fixed bug when resolving symbols inside C++ classes.
+ For example :
+
+ class Foo {
+ public:
+ enum Value {ALE, STOUT, LAGER};
+ ...
+ void defarg(Value v = STOUT);
+
+ };
+
+2/24/97 : Fixed bug with member functions returning void *.
+
+2/23/97 : Modified Python module to be better behaved under Windows
+
+ - Module initialization function is now properly exported.
+ It should not be necessary to explicitly export this function
+ yourself.
+
+ - Bizarre compilation problems when compiling the SWIG wrapper
+ code as ANSI C under Visual C++ 4.x fixed.
+
+ - Tested with both the stock Python-1.4 distribution and Pythonwin
+ running under Win95.
+
+2/19/97 : Fixed typedef handling bug in Perl5 shadow classes.
+
+2/19/97 : Added exception support. To use it, do the following :
+
+ %except(lang) {
+ ... try part of the exception ...
+ $function
+ ... catch part of exception ...
+ }
+
+ $function is a SWIG variable that will be replaced by the
+ actual C/C++ function call in a wrapper function. Thus,
+ a real exception specification might look like this :
+
+ %except(perl5) {
+ try {
+ $function
+ } catch (char *& sz) {
+ ... process an exception ...
+ } catch(...) {
+ croak("Unknown exception. Bailing out...");
+ }
+ }
+
+2/19/97 : Added support for managing generic code fragments (needed
+ for exceptions).
+
+2/19/97 : Fixed some really obscure typemap scoping bugs in the C++
+ handler.
+
+2/18/97 : Cleaned up perlmain.i file by removing some problematic,
+ but seemingly unnecessary declarations.
+
+2/18/97 : Optimized handling of member functions under inheritance.
+ SWIG can now use wrapper functions generated for a
+ base class instead of regenerating wrappers for
+ the same functions in a derived class. This could
+ make a drastic reduction in wrapper code size for C++
+ applications with deep inheritance hierarchies and
+ lots of functions.
+
+2/18/97 : Additional methods specified with %addmethods can now
+ be inherited along with normal C++ member functions.
+
+2/18/97 : Minor internal fixes to make SWIG's string handling a little
+ safer.
+
+2/16/97 : Moved some code generation of Tcl shadow classes to
+ library files.
+
+2/16/97 : Fixed documentation error of '-configure' method in
+ Tcl modules.
+
+2/16/97 : Modified Perl5 module slightly to allow typemaps
+ to use Perl references.
+
+2/12/97 : Fixed argument checking bug that was introduced by
+ default arguments (function calls with too many
+ arguments would still be executed). Functions now
+ must have the same number of arguments as C version
+ (with possibility of default/optional arguments
+ still supported).
+
+2/12/97 : Fixed default argument bug in Perl5 module when
+ generating wrapper functions involving default
+ arguments of complex datatypes.
+
+2/12/97 : Fixed typemap scoping problems. For example :
+
+ %typemap(tcl,in) double {
+ .. get a double ..
+ }
+
+ class Foo {
+ public:
+ double bar(double);
+ }
+
+ %typemap(tcl,in) double {
+ .. new get double ..
+ }
+
+ Would apply the second typemap to all functions in Foo
+ due to delayed generation of C++ wrapper code (clearly this
+ is not the desired effect). Problem has been fixed by
+ assigning unique numerical identifiers to every datatype in
+ an interface file and recording the "range of effect" of each
+ typemap.
+
+2/11/97 : Added support for "ignore" and "default" typemaps. Only use
+ if you absolutely know what you're doing.
+
+2/9/97 : Added automatic creation of constructors and destructors for
+ C structs and C++ classes that do not specify any sort of
+ constructor or destructor. This feature can be enabled by
+ running SWIG with the '-make_default' option or by inserting
+ the following pragma into an interface file :
+
+ %pragma make_default
+
+ The following pragma disables automatic constructor generation
+
+ %pragma no_default
+
+2/9/97 : Added -make_default option for producing default constructors
+ and destructors for classes without them.
+
+2/9/97 : Changed the syntax of the SWIG %pragma directive to
+ %pragma option=value or %pragma(lang) option=value.
+ This change makes the syntax a little more consistent
+ between general pragmas and language-specific pragmas.
+ The old syntax still works, but will probably be phased
+ out (a warning message is currently printed).
+
+2/9/97 : Improved Tcl support of global variables that are of
+ structures, classes, and unions.
+
+2/9/97 : Fixed C++ compilation problem in Python 'embed.i' library file.
+
+2/9/97 : Fixed missing return value in perlmain.i library file.
+
+2/9/97 : Fixed Python shadow classes to return an AttributeError when
+ undefined attributes are accessed (older versions returned
+ a NameError).
+
+2/9/97 : Fixed bug when %addmethods is used after a class definition whose
+ last section is protected or private.
+
+2/8/97 : Made slight changes in include file processing to support
+ the Macintosh.
+
+2/8/97 : Extended swigmain.cxx to provide a rudimentary Macintosh interface.
+ It's a really bad interface, but works until something better
+ is written.
+
+1/29/97 : Fixed type-casting bug introduced by 1.1b4 when setting/getting the
+ value of global variables involving complex data types.
+
+1/29/97 : Removed erroneous white space before an #endif in the code generated
+ by the Python module (was causing errors on DEC Alpha compilers).
+
+1/26/97 : Fixed errors when using default/optional arguments in Python shadow
+ shadow classes.
+
+1/23/97 : Fixed bug with nested %extern declarations.
+
+1/21/97 : Fixed problem with typedef involving const datatypes.
+
+1/21/97 : Somewhat obscure, but serious bug with having multiple levels
+ of typedefs fixed. For example :
+
+ typedef char *String;
+ typedef String Name;
+
+Version 1.1 Beta4 (January 16, 1997)
+====================================
+
+Note : SWIG 1.1b3 crashed and burned shortly after take off due
+to a few major run-time problems that surfaced after release.
+This release should fix most, if not all, of those problems.
+
+1/16/97 : Fixed major memory management bug on Linux
+
+1/14/97 : Fixed bug in functions returning constant C++ references.
+
+1/14/97 : Modified C++ module to handle datatypes better.
+
+1/14/97 : Modified parser to allow a *single* scope resolution
+ operator in datatypes. Ie : Foo::bar. SWIG doesn't
+ yet handle nested classes, so this should be
+ sufficient for now.
+
+1/14/97 : Modified parser to allow typedef inside a C++ class.
+
+1/14/97 : Fixed some problems related to datatypes defined inside
+ a C++ class. SWIG was not generating correct code,
+ but a new scoping mechanism and method for handling
+ datatypes inside a C++ class have been added.
+
+1/14/97 : Changed enumerations to use the value name instead
+ of any values that might have appeared in the interface
+ file. This makes the code a little more friendly to
+ C++ compilers.
+
+1/14/97 : Removed typedef bug that made all enumerations
+ equivalent to each other in the type checker (since
+ it generated alot of unnecessary code).
+
+Version 1.1 Beta3 (January 9, 1997)
+===================================
+
+Note : A *huge* number of changes related to ongoing modifications.
+
+1. Support for C++ multiple inheritance added.
+
+2. Typemaps added.
+
+3. Some support for nested structure definitions added.
+
+4. Default argument handling added.
+
+5. -c option added for building bare wrapper code modules.
+
+6. Rewrote Pointer type-checking to support multiple inheritance
+ correctly.
+
+7. Tcl 8.0 module added.
+
+8. Perl4 and Guile modules resurrected from the dead (well, they
+ at least work again).
+
+9. New Object Oriented Tcl interface added.
+
+10. Bug fixes to Perl5 shadow classes.
+
+11. Cleaned up many of the internal modules of the parser.
+
+12. Tons of examples and testing modules added.
+
+13. Fixed bugs related to use of "const" return values.
+
+14. Fixed bug with C++ member functions returning void *.
+
+15. Changed SWIG configuration script.
+
+Version 1.1 Beta2 (December 3, 1996)
+====================================
+
+1. Completely rewrote the SWIG documentation system. The changes
+ involved are too numerous to mention. Basically, take everything
+ you knew about the old system, throw them out, and read the
+ file Doc/doc.ps.
+
+2. Limited support for #if defined() added.
+
+3. Type casts are now allowed in constant expressions. ie
+
+ #define A (int) 3
+
+4. Added support for typedef lists. For example :
+
+ typedef struct {
+ double x,y,z;
+ } Vector, *VectorPtr;
+
+5. New SWIG directives (related to documentation system)
+
+ %style
+ %localstyle
+ %subsection
+ %subsubsection
+
+6. Reorganized the C++ handling and made it a little easier to
+ work with internally.
+
+7. Fixed problem with inheriting data members in Python
+ shadow classes.
+
+8. Fixed symbol table problems with shadow classes in both
+ Python and Perl.
+
+9. Fixed annoying segmentation fault bug in wrapper code
+ generated for Perl5.
+
+10. Fixed bug with %addmethods directive. Now it can be placed
+ anywhere in a class.
+
+11. More test cases added to the SWIG self-test. Documentation
+ tests are now performed along with other things.
+
+12. Reorganized the SWIG library a little bit and set it up to
+ self-document itself using SWIG.
+
+13. Lots and lots of minor bug fixes (mostly obscure, but bugs
+ nonetheless).
+
+
+Version 1.1 Beta1 (October 30, 1996)
+====================================
+
+1. Added new %extern directive for handling multiple files
+
+2. Perl5 shadow classes added
+
+3. Rewrote conditional compilation to work better
+
+4. Added 'bool' datatype
+
+5. %{,%} block is now optional.
+
+6. Fixed some bugs in the Python shadow class module
+
+7. Rewrote all of the SWIG tests to be more informative
+ (and less scary).
+
+8. Rewrote parameter list handling to be more memory
+ efficient and flexible.
+
+9. Changed parser to ignore 'static' declarations.
+
+10. Initializers are now ignored. For example :
+
+ struct FooBar a = {3,4,5};
+
+11. Somewhat better parsing of arrays (although it's
+ usually just a better error message now).
+
+12. Lot's of minor bug fixes.
+
+
+Version 1.0 Final (August 31, 1996)
+===================================
+
+1. Fixed minor bug in C++ module
+
+2. Fixed minor bug in pointer type-checker when using
+ -DALLOW_NULL.
+
+3. Fixed configure script to work with Python 1.4beta3
+
+4. Changed configure script to allow compilation without
+ yacc or bison.
+
+Version 1.0 Final (August 28, 1996)
+===================================
+
+1. Changed parser to support more C/C++ datatypes (well,
+ more variants). Types like "unsigned", "short int",
+ "long int", etc... now work.
+
+2. "unions" added to parser.
+
+3. Use of "typedef" as in :
+
+ typedef struct {
+ double x,y,z;
+ } Vector;
+
+ Now works correctly. The name of the typedef is used as
+ the structure name.
+
+4. Conditional compilation with #ifdef, #else, #endif, etc...
+ added.
+
+5. New %disabledoc, %enabledoc directives allow documentation
+ to selectively be disabled for certain parts of a wrapper
+ file.
+
+6. New Python module supports better variable linking, constants,
+ and shadow classes.
+
+7. Perl5 module improved with better compatibility with XS
+ and xsubpp. SWIG pointers and now created so that they
+ are compatible with xsubpp pointers.
+
+8. Support for [incr Tcl] namespaces added to Tcl module.
+
+9. %pragma directive added.
+
+10. %addmethods directive added.
+
+11. %native directive added to allow pre-existing wrapper functions
+ to be used.
+
+12. Wrote configure script for SWIG installation.
+
+13. Function pointers now allowed with typedef statements.
+
+14. %typedef modified to insert a corresponding C typedef into
+ the output file.
+
+15. Fixed some problems related to C++ references.
+
+16. New String and WrapperFunction classes add to make generating
+ wrapper code easier.
+
+17. Fixed command line option processing to eliminate core dumps
+ and to allow help messages.
+
+18. Lot's of minor bug fixes to almost all code modules
+
+
+Version 1.0 Beta 3 (Patch 1) July 17, 1996
+==========================================
+
+1.0 Final is not quite ready yet, but this release fixes a
+number of immediate problems :
+
+1. Compiler errors when using -strict 1 type checking have been fixed.
+
+2. Pointer type checker now recognizes pointers of the form
+ _0_Type correctly.
+
+3. A few minor fixes were made in the Makefile
+
+Version 1.0 Beta 3 (June 14, 1996)
+==================================
+
+
+There are lots of changes in this release :
+
+1. SWIG is now invoked using the "swig" command instead of "wrap".
+ Hey, swig sounds cooler.
+
+2. The SWIG_LIB environment variable can be set to change the
+ location where SWIG looks for library files.
+
+3. C++ support has been added. You should use the -c++ option
+ to enable it.
+
+4. The %init directive has been replaced by the %module directive.
+ %module constructs a valid name for the initialization function
+ for whatever target language you're using (actually this makes
+ SWIG files a little cleaner). The old %init directive still works.
+
+5. The syntax of the %name directive has been changed. Use of the
+ old one should generate a warning message, but may still work.
+
+6. To support Tcl/Tk on non-unix platforms, SWIG imports a file called
+ swigtcl.cfg from the $(SWIG_LIB)/tcl directory. I don't have access
+ to an NT machine, but this file is supposedly allows SWIG to
+ produce wrapper code that compiles on both UNIX and non UNIX machines.
+ If this doesn't work, you'll have to edit the file swigtcl.cfg. Please
+ let me know if this doesn't work so I can update the file as
+ necessary.
+
+7. The SWIG run-time typechecker has been improved. You can also
+ now redefine how it works by supplying a file called "swigptr.cfg"
+ in the same directory as your SWIG interface files. By default,
+ SWIG reads this file from $(SWIG_LIB)/config.
+
+8. The documentation system has been changed to support the following :
+
+ - Documentation order is printed in interface file order by
+ default. This can be overridden by putting an %alpha
+ directive in the beginning of the interface file.
+
+ - You can supply additional documentation text using
+
+ %text %{ put your text here %}
+
+ - A few minor bugs were fixed.
+
+9. A few improvements have been made to the handling of command line
+ options (but it's still not finished).
+
+10. Lots of minor bug fixes in most of the language modules have been
+ made.
+
+11. Filenames have been changed to 8.3 for compatibility with a SWIG
+ port to non-unix platforms (work in progress).
+
+12. C++ file suffix is now .cxx (for same reason).
+
+13. The documentation has been upgraded significantly and is now
+ around 100 pages. I added new examples and a section on
+ C++. The documentation now includes a Table of Contents.
+
+14. The SWIG Examples directory is still woefully sparse, but is
+ getting better.
+
+Special notice about C++
+------------------------
+This is the first version of SWIG to support C++ parsing. Currently
+the C++ is far from complete, but seems to work for simple cases.
+No work has been done to add special C++ processing to any of
+the target languages. See the user manual for details about how
+C++ is handled. If you find problems with the C++ implementation,
+please let me know. Expect major improvements in this area.
+
+Note : I have only successfully used SWIG and C++ with Tcl and
+Python.
+
+Notice about Version 1.0Final
+-----------------------------
+
+Version 1.0B3 is the last Beta release before version 1.0 Final is
+released. I have frozen the list of features supported in version 1.0
+and will only fix bugs as they show up. Work on SWIG version 2.0 is
+already in progress, but is going to result in rather significant
+changes to SWIG's internal structure (hopefully for the better). No
+anticipated date for version 2.0 is set, but if you've got an idea,
+let me know.
+
+Version 1.0 Beta 2 (April 26, 1996)
+===================================
+
+This release is identical to Beta1 except a few minor bugs are
+fixed and the SWIG library has been updated to work with Tcl 7.5/Tk 4.1.
+A tcl7.5 examples directory is now included.
+
+- Fixed a bug in the Makefile that didn't install the libraries
+ correctly.
+
+- SWIG Library files are now updated to work with Tcl 7.5 and Tk 4.1.
+
+- Minor bug fixes in other modules.
+
+
+Version 1.0 Beta 1 (April 10, 1996).
+=====================================
+
+This is the first "semi-official" release of SWIG. It has a
+number of substantial improvements over the Alpha release. These
+notes are in no particular order--hope I remembered everything....
+
+1. Tcl/Tk
+
+SWIG is known to work with Tcl7.3, Tk3.6 and later versions.
+I've also tested SWIG with expect-5.19.
+
+Normally SWIG expects to use the header files "tcl.h" and "tk.h".
+Newer versions of Tcl/Tk use version numbers. You can specify these
+in SWIG as follows :
+
+ % wrap -htcl tcl7.4.h -htk tk4.0.h example.i
+
+Of course, I prefer to simply set up symbolic links between "tcl.h" and
+the most recent stable version on the machine.
+
+2. Perl4
+
+This implementation has been based on Perl-4.035. SWIG's interface to
+Perl4 is based on the documentation provided in the "Programming Perl"
+book by Larry Wall, and files located in the "usub" directory of the
+Perl4 distribution.
+
+In order to compile with Perl4, you'll need to link with the uperl.o
+file found in the Perl4 source directory. You may want to move this
+file to a more convenient location.
+
+3. Perl5
+
+This is a somewhat experimental implementation, but is alot less
+buggy than the alpha release. SWIG operates independently of
+the XS language and xsubpp supplied with Perl5. Currently SWIG
+produces the necessary C code and .pm file needed to dynamically
+load a module into Perl5.
+
+To support Perl5's notion of modules and packages (as with xsubpp),
+you can use the following command line options :
+
+ % wrap -perl5 -module MyModule -package MyPackage example.i
+
+Note : In order for dynamic loading to be effective, you need to be
+careful about naming. For a module named "MyModule", you'll need to
+create a shared object file called "MyModule.so" using something like
+
+ % ld -shared my_obj.o -o MyModule.so
+
+The use of the %init directive must match the module name since Perl5
+calls a function "boot_ModuleName" in order to initialize things.
+See the Examples directory for some examples of how to get things
+to work.
+
+4. Python1.3
+
+This is the first release supporting Python. The Python port is
+experimental and may be rewritten. Variable linkage is done through
+functions which is sort of a kludge. I also think it would be nice
+to import SWIG pointers into Python as a new object (instead of strings).
+Of course, this needs a little more work.
+
+5. Guile3
+
+If you really want to live on the edge, pick up a copy of Guile-iii and
+play around with this. This is highly experimental---especially since
+I'm not sure what the official state of Guile is these days. This
+implementation may change at any time should I suddenly figure out better
+ways to do things.
+
+6. Extending SWIG
+
+SWIG is written in C++ although I tend to think of the code as mostly
+being ANSI C with a little inheritance thrown in. Each target language
+is implemented as a C++ class that can be plugged into the system.
+If you want to add your own modifications, see Appendix C of the user
+manual. Then take a look at the "user" directory which contains some
+code for building your own extenions.
+
+7. The SWIG library
+
+The SWIG library is still incomplete. Some of the files mentioned in
+the user manual are unavailable. These files will be made available
+when they are ready. Subscribe to the SWIG mailing list for announcements
+and updates.
+
+8. SWIG Documentation
+
+I have sometimes experienced problems viewing the SWIG documentation in
+some postscript viewers. However, the documentation seems to print
+normally. I'm working on making much of the documentation online,
+but this takes time.
+
+Version 0.1 Alpha (February 9, 1996)
+====================================
+
+1. Run-time type-checking of SWIG pointers. Pointers are now represented
+ as strings with both numeric and encoded type information. This makes
+ it a little harder to shoot yourself in the foot (and it eliminates
+ some segmentation faults and other oddities).
+
+2. Python 1.3 now supported.
+
+3. #define and enum can be used to install constants.
+
+4. Completely rewrote the %include directive and made it alot more powerful.
+
+5. Restructured the SWIG library to make it work better.
+
+6. Various bug fixes to Tcl, Perl4, Perl5, and Guile implementations.
+
+7. Better implementation of %typedef directive.
+
+8. Made some changes to SWIG's class structure to make it easier to expand.
+ SWIG is now built into a library file that you can use to make your
+ own extenions.
+
+9. Made extensive changes to the documentation.
+
+10. Minor changes to the SWIG parser to make it use less memory.
+ Also took out some extraneous rules that were undocumented and
+ didn't work in the first place.
+
+11. The SWIG library files "tclsh", "wish", "expect", etc... in the first
+ release have been restructured and renamed to "tclsh.i", "wish.i",
+ and so on.
diff --git a/contrib/tools/swig/CHANGES.current b/contrib/tools/swig/CHANGES.current
new file mode 100644
index 0000000000..d4883e5d6a
--- /dev/null
+++ b/contrib/tools/swig/CHANGES.current
@@ -0,0 +1,79 @@
+Below are the changes for the current release.
+See the CHANGES file for changes in older releases.
+See the RELEASENOTES file for a summary of changes in each release.
+Issue # numbers mentioned below can be found on Github. For more details, add
+the issue number to the end of the URL: https://github.com/swig/swig/issues/
+
+Version 4.1.1 (30 Nov 2022)
+===========================
+
+2022-11-29: bero
+ Fix mismatch between #pragma GCC diagnostic push and pop statements
+
+2022-11-26: wsfulton
+ #2449 Fix undefined behaviour in ccache-swig calculating md4 hashes and possibly
+ also handling errors when CCACHE_CPP2 is set.
+
+2022-11-25: wsfulton
+ #961 Fix syntax error parsing unnamed template parameters with a default value.
+
+2022-11-25: olly
+ #2447 Fix undefined behaviour in swig's parser when handling
+ default parameter expressions containing method calls.
+
+2022-11-13: olly
+ [PHP] #2419 Update the documentation to reflect that SWIG 4.1.0
+ dropped support for -noproxy when generating PHP wrappers.
+
+2022-11-05: wsfulton
+ #2417 Fix -swiglib for Windows when building with CMake.
+
+2022-11-02: wsfulton
+ #2418 Fix infinite loop handling non-type template parameters.
+
+ Fixes infinite loop due to () brackets in a non-type template
+ parameter containing an expression.
+
+2022-10-28: wsfulton
+ [R] R rtypecheck typemaps
+
+ Further switch to use rtypecheck typemaps instead of hard coded logic.
+ The full switch to typemaps is deferred until swig-4.2 as it can't be fully
+ backwards compatible. For now a warning is provided to help the
+ transition. It provides the full typemap that should be placed into
+ a user's interface file, for example:
+
+ %typemap("rtype") int32_t * "integer"
+ void testmethod(int32_t * i);
+ void testmethod();
+
+ If there is no rtypecheck typemap for int32_t *, the warning shown is:
+
+ example.i:7: Warning 750: Optional rtypecheck code is deprecated. Add the
+ following typemap to fix as the next version of SWIG will not work without it:
+ %typemap("rtypecheck") int32_t * %{ (is.integer($arg) || is.numeric($arg)) %}
+
+ The warning is shown for any code that previously used "numeric", "integer" or
+ "character" for the rtype typemap. Copying the rtypecheck typemap as
+ shown into the user interface file will provide the appropriate fix and
+ the warning will disappear. This is important to do as swig-4.2 will
+ not be able to provide this helpful warning.
+
+2022-10-27: wsfulton
+ [R] Allow NULL to be used in overloaded functions taking shared_ptr.
+ Also fixes special variable $argtype expansion in rtypecheck typemaps.
+
+2022-10-26: wsfulton
+ [R] Improve R wrapper error message when calling overloaded methods
+ when incorrect types passed are passed to the overloaded methods.
+
+ Old unhelpful error message:
+ Error in f(...) : could not find function "f"
+
+ Example of new improved error message:
+ Error in use_count(k) :
+ cannot find overloaded function for use_count with argtypes (NULL)
+
+2022-10-26: wsfulton
+ [R] #2386 Fix memory leak in R shared_ptr wrappers.
+ Fix leak when a cast up a class inheritance chain is required.
diff --git a/contrib/tools/swig/COPYRIGHT b/contrib/tools/swig/COPYRIGHT
new file mode 100644
index 0000000000..e6df73ff82
--- /dev/null
+++ b/contrib/tools/swig/COPYRIGHT
@@ -0,0 +1,113 @@
+SWIG Copyright and Authors
+--------------------------
+
+Copyright (c) 1995-2011 The SWIG Developers
+Copyright (c) 2005-2006 Arizona Board of Regents (University of Arizona).
+Copyright (c) 1998-2005 University of Chicago.
+Copyright (c) 1995-1998 The University of Utah and the Regents of the University of California
+
+Portions also copyrighted by:
+ Network Applied Communication Laboratory, Inc
+ Information-technology Promotion Agency, Japan
+
+Active SWIG Developers:
+ William Fulton (wsf@fultondesigns.co.uk) (SWIG core, Java, C#, Windows, Cygwin)
+ Olly Betts (olly@survex.com) (PHP)
+ Joseph Wang (joequant@gmail.com) (R)
+ Xavier Delacour (xavier.delacour@gmail.com) (Octave)
+ David Nadlinger (code@klickverbot.at) (D)
+ Oliver Buchtala (oliver.buchtala@gmail.com) (Javascript)
+ Neha Narang (narangneha03@gmail.com) (Javascript)
+ Simon Marchetto (simon.marchetto@scilab-enterprises.com) (Scilab)
+ Zackery Spytz (zspytz@gmail.com) (OCaml, SWIG core)
+
+Past SWIG developers and major contributors include:
+ Dave Beazley (dave-swig@dabeaz.com) (SWIG core, Python, Tcl, Perl)
+ Henning Thielemann (swig@henning-thielemann.de) (Modula3)
+ Matthias Köppe (mkoeppe@mail.math.uni-magdeburg.de) (Guile, MzScheme)
+ Luigi Ballabio (luigi.ballabio@fastwebnet.it) (STL wrapping)
+ Mikel Bancroft (mikel@franz.com) (Allegro CL)
+ Surendra Singhi (efuzzyone@netscape.net) (CLISP, CFFI)
+ Marcelo Matus (mmatus@acms.arizona.edu) (SWIG core, Python, UTL[python,perl,tcl,ruby])
+ Art Yerkes (ayerkes@speakeasy.net) (OCaml)
+ Lyle Johnson (lyle@users.sourceforge.net) (Ruby)
+ Charlie Savage (cfis@interserv.com) (Ruby)
+ Thien-Thi Nguyen (ttn@glug.org) (build/test/misc)
+ Richard Palmer (richard@magicality.org) (PHP)
+ Sam Liddicott - Ananova Ltd (saml@liddicott.com) (PHP)
+ Tim Hockin - Sun Microsystems (thockin@sun.com) (PHP)
+ Kevin Ruland (PHP)
+ Shibukawa Yoshiki (Japanese Translation)
+ Jason Stewart (jason@openinformatics.com) (Perl5)
+ Loic Dachary (Perl5)
+ David Fletcher (Perl5)
+ Gary Holt (Perl5)
+ Masaki Fukushima (Ruby)
+ Scott Michel (scottm@cs.ucla.edu) (Java directors)
+ Tiger Feng (songyanf@cs.uchicago.edu) (SWIG core)
+ Mark Rose (mrose@stm.lbl.gov) (Directors)
+ Jonah Beckford (beckford@usermail.com) (CHICKEN)
+ Ahmon Dancy (dancy@franz.com) (Allegro CL)
+ Dirk Gerrits (Allegro CL)
+ Neil Cawse (C#)
+ Harco de Hilster (Java)
+ Alexey Dyachenko (dyachenko@fromru.com) (Tcl)
+ Bob Techentin (Tcl)
+ Martin Froehlich <MartinFroehlich@ACM.org> (Guile)
+ Marcio Luis Teixeira <marciot@holly.colostate.edu> (Guile)
+ Duncan Temple Lang (R)
+ Miklos Vajna <vmiklos@frugalware.org> (PHP directors)
+ Mark Gossage (mark@gossage.cjb.net) (Lua)
+ Raman Gopalan (ramangopalan@gmail.com) (eLua)
+ Gonzalo Garramuno (ggarra@advancedsl.com.ar) (Ruby, Ruby's UTL)
+ John Lenz (Guile, MzScheme updates, Chicken module, runtime system)
+ Baozeng Ding <sploving1@163.com> (Scilab)
+ Ian Lance Taylor (Go)
+ Dmitry Kabak (userdima@gmail.com) (Doxygen)
+ Vadim Zeitlin (PCRE, Python, Doxygen)
+ Stefan Zager (szager@gmail.com) (Python)
+ Vincent Couvert (Scilab)
+ Sylvestre Ledru (Scilab)
+ Wolfgang Frisch (Scilab)
+
+Past contributors include:
+ James Michael DuPont, Clark McGrew, Dustin Mitchell, Ian Cooke, Catalin Dumitrescu, Baran
+ Kovuk, Oleg Tolmatcev, Tal Shalif, Lluis Padro, Chris Seatory, Igor Bely, Robin Dunn,
+ Edward Zimmermann, David Ascher, Dominique Dumont, Pier Giorgio Esposito, Hasan Baran Kovuk,
+ Klaus Wiederänders, Richard Beare, Hans Oesterholt.
+ (See CHANGES and CHANGES.current and the bug tracker for a more complete list).
+
+Past students:
+ Songyan Feng (Chicago).
+ Xinghua Shi (Chicago).
+ Jing Cao (Chicago).
+ Aquinas Hobor (Chicago).
+
+Historically, the following people contributed to early versions of SWIG.
+Peter Lomdahl, Brad Holian, Shujia Zhou, Niels Jensen, and Tim Germann
+at Los Alamos National Laboratory were the first users. Patrick
+Tullmann at the University of Utah suggested the idea of automatic
+documentation generation. John Schmidt and Kurtis Bleeker at the
+University of Utah tested out the early versions. Chris Johnson
+supported SWIG's developed at the University of Utah. John Buckman,
+Larry Virden, and Tom Schwaller provided valuable input on the first
+releases and improving the portability of SWIG. David Fletcher and
+Gary Holt have provided a great deal of input on improving SWIG's
+Perl5 implementation. Kevin Butler contributed the first Windows NT
+port.
+
+Early bug reports and patches:
+Adam Hupp, Arthur Smyles, Brad Clements, Brett Williams, Buck Hodges,
+Burkhard Kloss, Chia-Liang Kao, Craig Files, Dennis Marsa, Dieter Baron,
+Drake Diedrich, Fleur Diana Dragan, Gary Pennington, Geoffrey Hort, Gerald Williams,
+Greg Anderson, Greg Kochanski, Greg Troxel, Henry Rowley, Irina Kotlova,
+Israel Taller, James Bailey, Jim Fulton, Joel Reed, Jon Travis,
+Junio Hamano, Justin Heyes-Jones, Karl Forner, Keith Davidson,
+Krzysztof Kozminski, Larry Virden, Luke J Crook, Magnus Ljung, Marc Zonzon,
+Mark Howson, Micahel Scharf, Michel Sanner, Mike Romberg, Mike Simons,
+Mike Weiblen, Paul Brannan, Ram Bhamidipaty, Reinhard Fobbe, Rich Wales,
+Richard Salz, Roy Lecates, Rudy Albachten, Scott Drummonds
+Scott Michel, Shaun Lowry, Steve Galser, Tarn Weisner Burton,
+Thomas Weidner, Tony Seward, Uwe Steinmann, Vadim Chugunov, Wyss Clemens,
+Zhong Ren.
+
diff --git a/contrib/tools/swig/INSTALL b/contrib/tools/swig/INSTALL
new file mode 100644
index 0000000000..666ffd9f8a
--- /dev/null
+++ b/contrib/tools/swig/INSTALL
@@ -0,0 +1,226 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for variables by setting
+them in the environment. You can do that on the command line like this:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Environment Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it cannot guess the host type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the host
+platform (i.e., that on which the generated programs will eventually be
+run) with `--host=TYPE'. In this case, you should also specify the
+build platform with `--build=TYPE', because, in this case, it may not
+be possible to guess the build platform (it sometimes involves
+compiling and running simple test programs, and this can't be done if
+the compiler is a cross compiler).
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Environment Variables
+=====================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to configure. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/contrib/tools/swig/LICENSE b/contrib/tools/swig/LICENSE
new file mode 100644
index 0000000000..d7a422fda1
--- /dev/null
+++ b/contrib/tools/swig/LICENSE
@@ -0,0 +1,22 @@
+SWIG 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. See the LICENSE-GPL file for
+the full terms of the GNU General Public license version 3.
+
+Portions of SWIG are also licensed under the terms of the licenses
+in the file LICENSE-UNIVERSITIES. You must observe the terms of
+these licenses, as well as the terms of the GNU General Public License,
+when you distribute SWIG.
+
+The SWIG library and examples, under the Lib and Examples top level
+directories, are distributed under the following terms:
+
+ You may copy, modify, distribute, and make derivative works based on
+ this software, in source code or object code form, without
+ restriction. If you distribute the software to others, you may do
+ so according to the terms of your choice. This software is offered as
+ is, without warranty of any kind.
+
+See the COPYRIGHT file for a list of contributors to SWIG and their
+copyright notices.
diff --git a/contrib/tools/swig/LICENSE-GPL b/contrib/tools/swig/LICENSE-GPL
new file mode 100644
index 0000000000..94a9ed024d
--- /dev/null
+++ b/contrib/tools/swig/LICENSE-GPL
@@ -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/contrib/tools/swig/LICENSE-UNIVERSITIES b/contrib/tools/swig/LICENSE-UNIVERSITIES
new file mode 100644
index 0000000000..44fcaa13f6
--- /dev/null
+++ b/contrib/tools/swig/LICENSE-UNIVERSITIES
@@ -0,0 +1,95 @@
+SWIG is distributed under the following terms:
+
+I.
+
+Copyright (c) 1995-1998
+The University of Utah and the Regents of the University of California
+All Rights Reserved
+
+Permission is hereby granted, without written agreement and without
+license or royalty fees, to use, copy, modify, and distribute this
+software and its documentation for any purpose, provided that
+(1) The above copyright notice and the following two paragraphs
+appear in all copies of the source code and (2) redistributions
+including binaries reproduces these notices in the supporting
+documentation. Substantial modifications to this software may be
+copyrighted by their authors and need not follow the licensing terms
+described here, provided that the new terms are clearly indicated in
+all files where they apply.
+
+IN NO EVENT SHALL THE AUTHOR, THE UNIVERSITY OF CALIFORNIA, THE
+UNIVERSITY OF UTAH OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
+PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
+EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHOR, THE UNIVERSITY OF CALIFORNIA, AND THE UNIVERSITY OF UTAH
+SPECIFICALLY DISCLAIM ANY WARRANTIES,INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND
+THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE,
+SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+
+II.
+
+This software includes contributions that are Copyright (c) 1998-2005
+University of Chicago.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer. 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. Neither the name of
+the University of Chicago nor the names of its contributors may be
+used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF CHICAGO AND CONTRIBUTORS
+"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 UNIVERSITY OF
+CHICAGO OR CONTRIBUTORS 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.
+
+
+III.
+
+This software includes contributions that are Copyright (c) 2005-2006
+Arizona Board of Regents (University of Arizona).
+All Rights Reserved
+
+Permission is hereby granted, without written agreement and without
+license or royalty fees, to use, copy, modify, and distribute this
+software and its documentation for any purpose, provided that
+(1) The above copyright notice and the following paragraph
+appear in all copies of the source code and (2) redistributions
+including binaries reproduces these notices in the supporting
+documentation. Substantial modifications to this software may be
+copyrighted by their authors and need not follow the licensing terms
+described here, provided that the new terms are clearly indicated in
+all files where they apply.
+
+THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF ARIZONA AND CONTRIBUTORS
+"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 UNIVERSITY OF
+ARIZONA OR CONTRIBUTORS 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/contrib/tools/swig/Lib/exception.i b/contrib/tools/swig/Lib/exception.i
new file mode 100644
index 0000000000..5cdea58e8b
--- /dev/null
+++ b/contrib/tools/swig/Lib/exception.i
@@ -0,0 +1,332 @@
+/* -----------------------------------------------------------------------------
+ * exception.i
+ *
+ * SWIG library file providing language independent exception handling
+ * ----------------------------------------------------------------------------- */
+
+#if defined(SWIGUTL)
+#error "This version of exception.i should not be used"
+#endif
+
+
+%insert("runtime") "swigerrors.swg"
+
+
+#ifdef SWIGPHP
+%{
+#if PHP_MAJOR_VERSION >= 8
+# define SWIG_HANDLE_VALUE_ERROR_FOR_PHP8(code) code == SWIG_ValueError ? zend_ce_value_error :
+#else
+# define SWIG_HANDLE_VALUE_ERROR_FOR_PHP8(code)
+#endif
+#define SWIG_exception(code, msg) do { zend_throw_exception( \
+ code == SWIG_TypeError ? zend_ce_type_error : \
+ SWIG_HANDLE_VALUE_ERROR_FOR_PHP8(code) \
+ code == SWIG_DivisionByZero ? zend_ce_division_by_zero_error : \
+ code == SWIG_SyntaxError ? zend_ce_parse_error : \
+ code == SWIG_OverflowError ? zend_ce_arithmetic_error : \
+ NULL, msg, code); SWIG_fail; } while (0)
+%}
+#endif
+
+#ifdef SWIGGUILE
+%{
+ SWIGINTERN void SWIG_exception_ (int code, const char *msg,
+ const char *subr) {
+#define ERROR(scmerr) \
+ scm_error(scm_from_locale_string((char *) (scmerr)), \
+ (char *) subr, (char *) msg, \
+ SCM_EOL, SCM_BOOL_F)
+#define MAP(swigerr, scmerr) \
+ case swigerr: \
+ ERROR(scmerr); \
+ break
+ switch (code) {
+ MAP(SWIG_MemoryError, "swig-memory-error");
+ MAP(SWIG_IOError, "swig-io-error");
+ MAP(SWIG_RuntimeError, "swig-runtime-error");
+ MAP(SWIG_IndexError, "swig-index-error");
+ MAP(SWIG_TypeError, "swig-type-error");
+ MAP(SWIG_DivisionByZero, "swig-division-by-zero");
+ MAP(SWIG_OverflowError, "swig-overflow-error");
+ MAP(SWIG_SyntaxError, "swig-syntax-error");
+ MAP(SWIG_ValueError, "swig-value-error");
+ MAP(SWIG_SystemError, "swig-system-error");
+ default:
+ ERROR("swig-error");
+ }
+#undef ERROR
+#undef MAP
+ }
+
+#define SWIG_exception(a,b) SWIG_exception_(a, b, FUNC_NAME)
+%}
+#endif
+
+#ifdef SWIGMZSCHEME
+
+%{
+SWIGINTERN void SWIG_exception_ (int code, const char *msg) {
+#define ERROR(errname) \
+ scheme_signal_error(errname " (%s)", msg);
+#define MAP(swigerr, errname) \
+ case swigerr: \
+ ERROR(errname); \
+ break
+ switch (code) {
+ MAP(SWIG_MemoryError, "swig-memory-error");
+ MAP(SWIG_IOError, "swig-io-error");
+ MAP(SWIG_RuntimeError, "swig-runtime-error");
+ MAP(SWIG_IndexError, "swig-index-error");
+ MAP(SWIG_TypeError, "swig-type-error");
+ MAP(SWIG_DivisionByZero, "swig-division-by-zero");
+ MAP(SWIG_OverflowError, "swig-overflow-error");
+ MAP(SWIG_SyntaxError, "swig-syntax-error");
+ MAP(SWIG_ValueError, "swig-value-error");
+ MAP(SWIG_SystemError, "swig-system-error");
+ default:
+ ERROR("swig-error");
+ }
+#undef ERROR
+#undef MAP
+ }
+
+#define SWIG_exception(a,b) SWIG_exception_(a, b)
+%}
+#endif
+
+#ifdef SWIGJAVA
+%{
+SWIGINTERN void SWIG_JavaException(JNIEnv *jenv, int code, const char *msg) {
+ SWIG_JavaExceptionCodes exception_code = SWIG_JavaUnknownError;
+ switch(code) {
+ case SWIG_MemoryError:
+ exception_code = SWIG_JavaOutOfMemoryError;
+ break;
+ case SWIG_IOError:
+ exception_code = SWIG_JavaIOException;
+ break;
+ case SWIG_SystemError:
+ case SWIG_RuntimeError:
+ exception_code = SWIG_JavaRuntimeException;
+ break;
+ case SWIG_OverflowError:
+ case SWIG_IndexError:
+ exception_code = SWIG_JavaIndexOutOfBoundsException;
+ break;
+ case SWIG_DivisionByZero:
+ exception_code = SWIG_JavaArithmeticException;
+ break;
+ case SWIG_SyntaxError:
+ case SWIG_ValueError:
+ case SWIG_TypeError:
+ exception_code = SWIG_JavaIllegalArgumentException;
+ break;
+ case SWIG_UnknownError:
+ default:
+ exception_code = SWIG_JavaUnknownError;
+ break;
+ }
+ SWIG_JavaThrowException(jenv, exception_code, msg);
+}
+%}
+
+#define SWIG_exception(code, msg)\
+{ SWIG_JavaException(jenv, code, msg); return $null; }
+#endif // SWIGJAVA
+
+#ifdef SWIGOCAML
+%{
+SWIGINTERN void SWIG_OCamlException(int code, const char *msg) {
+ CAMLparam0();
+
+ SWIG_OCamlExceptionCodes exception_code = SWIG_OCamlUnknownError;
+ switch (code) {
+ case SWIG_DivisionByZero:
+ exception_code = SWIG_OCamlArithmeticException;
+ break;
+ case SWIG_IndexError:
+ exception_code = SWIG_OCamlIndexOutOfBoundsException;
+ break;
+ case SWIG_IOError:
+ case SWIG_SystemError:
+ exception_code = SWIG_OCamlSystemException;
+ break;
+ case SWIG_MemoryError:
+ exception_code = SWIG_OCamlOutOfMemoryError;
+ break;
+ case SWIG_OverflowError:
+ exception_code = SWIG_OCamlOverflowException;
+ break;
+ case SWIG_RuntimeError:
+ exception_code = SWIG_OCamlRuntimeException;
+ break;
+ case SWIG_SyntaxError:
+ case SWIG_TypeError:
+ case SWIG_ValueError:
+ exception_code = SWIG_OCamlIllegalArgumentException;
+ break;
+ case SWIG_UnknownError:
+ default:
+ exception_code = SWIG_OCamlUnknownError;
+ break;
+ }
+ SWIG_OCamlThrowException(exception_code, msg);
+ CAMLreturn0;
+}
+#define SWIG_exception(code, msg) SWIG_OCamlException(code, msg)
+%}
+#endif
+
+
+#ifdef SWIGCSHARP
+%{
+SWIGINTERN void SWIG_CSharpException(int code, const char *msg) {
+ if (code == SWIG_ValueError) {
+ SWIG_CSharpExceptionArgumentCodes exception_code = SWIG_CSharpArgumentOutOfRangeException;
+ SWIG_CSharpSetPendingExceptionArgument(exception_code, msg, 0);
+ } else {
+ SWIG_CSharpExceptionCodes exception_code = SWIG_CSharpApplicationException;
+ switch(code) {
+ case SWIG_MemoryError:
+ exception_code = SWIG_CSharpOutOfMemoryException;
+ break;
+ case SWIG_IndexError:
+ exception_code = SWIG_CSharpIndexOutOfRangeException;
+ break;
+ case SWIG_DivisionByZero:
+ exception_code = SWIG_CSharpDivideByZeroException;
+ break;
+ case SWIG_IOError:
+ exception_code = SWIG_CSharpIOException;
+ break;
+ case SWIG_OverflowError:
+ exception_code = SWIG_CSharpOverflowException;
+ break;
+ case SWIG_RuntimeError:
+ case SWIG_TypeError:
+ case SWIG_SyntaxError:
+ case SWIG_SystemError:
+ case SWIG_UnknownError:
+ default:
+ exception_code = SWIG_CSharpApplicationException;
+ break;
+ }
+ SWIG_CSharpSetPendingException(exception_code, msg);
+ }
+}
+%}
+
+#define SWIG_exception(code, msg)\
+{ SWIG_CSharpException(code, msg); return $null; }
+#endif // SWIGCSHARP
+
+#ifdef SWIGLUA
+
+%{
+#define SWIG_exception(a,b)\
+{ lua_pushfstring(L,"%s:%s",#a,b);SWIG_fail; }
+%}
+
+#endif // SWIGLUA
+
+#ifdef SWIGD
+%{
+SWIGINTERN void SWIG_DThrowException(int code, const char *msg) {
+ SWIG_DExceptionCodes exception_code;
+ switch(code) {
+ case SWIG_IndexError:
+ exception_code = SWIG_DNoSuchElementException;
+ break;
+ case SWIG_IOError:
+ exception_code = SWIG_DIOException;
+ break;
+ case SWIG_ValueError:
+ exception_code = SWIG_DIllegalArgumentException;
+ break;
+ case SWIG_DivisionByZero:
+ case SWIG_MemoryError:
+ case SWIG_OverflowError:
+ case SWIG_RuntimeError:
+ case SWIG_TypeError:
+ case SWIG_SyntaxError:
+ case SWIG_SystemError:
+ case SWIG_UnknownError:
+ default:
+ exception_code = SWIG_DException;
+ break;
+ }
+ SWIG_DSetPendingException(exception_code, msg);
+}
+%}
+
+#define SWIG_exception(code, msg)\
+{ SWIG_DThrowException(code, msg); return $null; }
+#endif // SWIGD
+
+#ifdef __cplusplus
+/*
+ You can use the SWIG_CATCH_STDEXCEPT macro with the %exception
+ directive as follows:
+
+ %exception {
+ try {
+ $action
+ }
+ catch (my_except& e) {
+ ...
+ }
+ SWIG_CATCH_STDEXCEPT // catch std::exception
+ catch (...) {
+ SWIG_exception(SWIG_UnknownError, "Unknown exception");
+ }
+ }
+*/
+%{
+#include <typeinfo>
+#include <stdexcept>
+%}
+%define SWIG_CATCH_STDEXCEPT
+ /* catching std::exception */
+ catch (std::invalid_argument& e) {
+ SWIG_exception(SWIG_ValueError, e.what() );
+ } catch (std::domain_error& e) {
+ SWIG_exception(SWIG_ValueError, e.what() );
+ } catch (std::overflow_error& e) {
+ SWIG_exception(SWIG_OverflowError, e.what() );
+ } catch (std::out_of_range& e) {
+ SWIG_exception(SWIG_IndexError, e.what() );
+ } catch (std::length_error& e) {
+ SWIG_exception(SWIG_IndexError, e.what() );
+ } catch (std::runtime_error& e) {
+ SWIG_exception(SWIG_RuntimeError, e.what() );
+ } catch (std::bad_cast& e) {
+ SWIG_exception(SWIG_TypeError, e.what() );
+ } catch (std::exception& e) {
+ SWIG_exception(SWIG_SystemError, e.what() );
+ }
+%enddef
+%define SWIG_CATCH_UNKNOWN
+ catch (std::exception& e) {
+ SWIG_exception(SWIG_SystemError, e.what() );
+ }
+ catch (...) {
+ SWIG_exception(SWIG_UnknownError, "unknown exception");
+ }
+%enddef
+
+/* rethrow the unknown exception */
+
+#if defined(SWIGCSHARP) || defined(SWIGD)
+%typemap(throws,noblock=1, canthrow=1) (...) {
+ SWIG_exception(SWIG_RuntimeError,"unknown exception");
+}
+#else
+%typemap(throws,noblock=1) (...) {
+ SWIG_exception(SWIG_RuntimeError,"unknown exception");
+}
+#endif
+
+#endif /* __cplusplus */
+
+/* exception.i ends here */
diff --git a/contrib/tools/swig/Lib/go/exception.i b/contrib/tools/swig/Lib/go/exception.i
new file mode 100644
index 0000000000..5abd306a4e
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/exception.i
@@ -0,0 +1,7 @@
+%typemap(throws,noblock=1) (...) {
+ SWIG_exception(SWIG_RuntimeError,"unknown exception");
+}
+
+%insert("runtime") %{
+#define SWIG_exception(code, msg) _swig_gopanic(msg)
+%}
diff --git a/contrib/tools/swig/Lib/go/go.swg b/contrib/tools/swig/Lib/go/go.swg
new file mode 100644
index 0000000000..348ae5f0d9
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/go.swg
@@ -0,0 +1,744 @@
+/* ------------------------------------------------------------
+ * go.swg
+ *
+ * Go configuration module.
+ * ------------------------------------------------------------ */
+
+%include <gostring.swg>
+
+/* Code insertion directives */
+#define %go_import(...) %insert(go_imports) %{__VA_ARGS__%}
+
+/* Basic types */
+
+%typemap(gotype) bool, const bool & "bool"
+%typemap(gotype) char, const char & "byte"
+%typemap(gotype) signed char, const signed char & "int8"
+%typemap(gotype) unsigned char, const unsigned char & "byte"
+%typemap(gotype) short, const short & "int16"
+%typemap(gotype) unsigned short, const unsigned short & "uint16"
+%typemap(gotype) int, const int & "int"
+%typemap(gotype) unsigned int, const unsigned int & "uint"
+%typemap(gotype) long, const long & "int64"
+%typemap(gotype) unsigned long, const unsigned long & "uint64"
+%typemap(gotype) long long, const long long & "int64"
+%typemap(gotype) unsigned long long, const unsigned long long & "uint64"
+%typemap(gotype) float, const float & "float32"
+%typemap(gotype) double, const double & "float64"
+
+%typemap(in) bool,
+ char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long,
+ float,
+ double
+%{ $1 = ($1_ltype)$input; %}
+
+%typemap(in) const bool &,
+ const char &,
+ const signed char &,
+ const unsigned char &,
+ const short &,
+ const unsigned short &,
+ const int &,
+ const unsigned int &,
+ const long long &,
+ const unsigned long long &,
+ const float &,
+ const double &
+%{ $1 = ($1_ltype)&$input; %}
+
+%typemap(in) const long & ($*1_ltype temp),
+ const unsigned long & ($*1_ltype temp)
+%{ temp = ($*1_ltype)$input;
+ $1 = ($1_ltype)&temp; %}
+
+%typemap(out) bool,
+ char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long,
+ float,
+ double
+%{ $result = $1; %}
+
+%typemap(goout) bool,
+ char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long,
+ float,
+ double
+""
+
+%typemap(out) const bool &,
+ const char &,
+ const signed char &,
+ const unsigned char &,
+ const short &,
+ const unsigned short &,
+ const int &,
+ const unsigned int &,
+ const long &,
+ const unsigned long &,
+ const long long &,
+ const unsigned long long &,
+ const float &,
+ const double &
+%{ $result = ($*1_ltype)*$1; %}
+
+%typemap(goout) const bool &,
+ const char &,
+ const signed char &,
+ const unsigned char &,
+ const short &,
+ const unsigned short &,
+ const int &,
+ const unsigned int &,
+ const long &,
+ const unsigned long &,
+ const long long &,
+ const unsigned long long &,
+ const float &,
+ const double &
+""
+
+%typemap(out) void ""
+
+%typemap(goout) void ""
+
+%typemap(directorin) bool,
+ char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long,
+ float,
+ double
+%{ $input = ($1_ltype)$1; %}
+
+%typemap(godirectorin) bool,
+ char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long,
+ float,
+ double
+""
+
+%typemap(directorin) const bool &,
+ const char &,
+ const signed char &,
+ const unsigned char &,
+ const short &,
+ const unsigned short &,
+ const int &,
+ const unsigned int &,
+ const long &,
+ const unsigned long &,
+ const long long &,
+ const unsigned long long &,
+ const float &,
+ const double &
+%{ $input = ($*1_ltype)$1; %}
+
+%typemap(godirectorin) const bool &,
+ const char &,
+ const signed char &,
+ const unsigned char &,
+ const short &,
+ const unsigned short &,
+ const int &,
+ const unsigned int &,
+ const long &,
+ const unsigned long &,
+ const long long &,
+ const unsigned long long &,
+ const float &,
+ const double &
+""
+
+%typemap(directorout) bool,
+ char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ unsigned long long,
+ float,
+ double
+%{ $result = ($1_ltype)$input; %}
+
+%typemap(directorout,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) const bool &,
+ const char &,
+ const signed char &,
+ const unsigned char &,
+ const short &,
+ const unsigned short &,
+ const int &,
+ const unsigned int &,
+ const long &,
+ const unsigned long &,
+ const long long &,
+ const unsigned long long &,
+ const float &,
+ const double &
+%{
+ $result = new $*1_ltype($input);
+ swig_acquire_pointer(&swig_mem, $result);
+%}
+
+/* The size_t type. */
+
+%typemap(gotype) size_t, const size_t & %{int64%}
+
+%typemap(in) size_t
+%{ $1 = (size_t)$input; %}
+
+%typemap(in) const size_t &
+%{ $1 = ($1_ltype)&$input; %}
+
+%typemap(out) size_t
+%{ $result = $1; %}
+
+%typemap(goout) size_t ""
+
+%typemap(out) const size_t &
+%{ $result = ($*1_ltype)*$1; %}
+
+%typemap(goout) const size_t & ""
+
+%typemap(directorin) size_t
+%{ $input = (size_t)$1; %}
+
+%typemap(godirectorin) size_t ""
+
+%typemap(directorin) const size_t &
+%{ $input = ($*1_ltype)$1; %}
+
+%typemap(godirectorin) const size_t & ""
+
+%typemap(directorout) size_t
+%{ $result = ($1_ltype)$input; %}
+
+%typemap(directorout,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) const size_t &
+%{
+ $result = new $*1_ltype($input);
+ swig_acquire_pointer(&swig_mem, $result);
+%}
+
+/* Member pointers. */
+
+%typemap(gotype) SWIGTYPE (CLASS::*)
+%{$gotypename%}
+
+%typemap(in) SWIGTYPE (CLASS::*)
+%{ $1 = *($&1_ltype)$input; %}
+
+%typemap(out) SWIGTYPE (CLASS::*)
+%{
+ struct swig_out_type { intgo size; void* val; } *swig_out;
+ swig_out = (struct swig_out_type*)malloc(sizeof(*swig_out));
+ if (swig_out) {
+ swig_out->size = sizeof($1_ltype);
+ swig_out->val = malloc(swig_out->size);
+ if (swig_out->val) {
+ *($&1_ltype)(swig_out->val) = $1;
+ }
+ }
+ $result = swig_out;
+%}
+
+%typemap(goout) SWIGTYPE (CLASS::*)
+%{
+ {
+ type swig_out_type struct { size int; val uintptr }
+ p := (*swig_out_type)(unsafe.Pointer($1))
+ if p == nil || p.val == 0 {
+ $result = nil
+ } else {
+ m := make([]byte, p.size)
+ a := (*[1024]byte)(unsafe.Pointer(p.val))[:p.size]
+ copy(m, a)
+ Swig_free(p.val)
+ Swig_free(uintptr(unsafe.Pointer(p)))
+ $result = &m[0]
+ }
+ }
+%}
+
+%typemap(directorin) SWIGTYPE (CLASS::*)
+%{ $input = *($&1_ltype)$1; %}
+
+%typemap(godirectorin) SWIGTYPE (CLASS::*) ""
+
+%typemap(directorout) SWIGTYPE (CLASS::*)
+%{
+ $result = new $1_ltype($input);
+ swig_acquire_pointer(&swig_mem, $result);
+%}
+
+/* Pointers. */
+
+/* We can't translate pointers using a typemap, so that is handled in
+ the C++ code. */
+%typemap(gotype) SWIGTYPE *
+%{$gotypename%}
+
+%typemap(in) SWIGTYPE *
+%{ $1 = *($&1_ltype)&$input; %}
+
+%typemap(out) SWIGTYPE *
+%{ *($&1_ltype)&$result = ($1_ltype)$1; %}
+
+%typemap(goout) SWIGTYPE * ""
+
+%typemap(directorin) SWIGTYPE *
+%{ *($&1_ltype)&$input = ($1_ltype)$1; %}
+
+%typemap(godirectorin) SWIGTYPE * ""
+
+%typemap(directorout) SWIGTYPE *
+%{ $result = *($&1_ltype)&$input; %}
+
+/* Pointer references. */
+
+%typemap(gotype) SWIGTYPE *const&
+%{$gotypename%}
+
+%typemap(in) SWIGTYPE *const& ($*1_ltype temp = 0)
+%{
+ temp = *($1_ltype)&$input;
+ $1 = ($1_ltype)&temp;
+%}
+
+%typemap(out) SWIGTYPE *const&
+%{ *($1_ltype)&$result = *$1; %}
+
+%typemap(goout) SWIGTYPE *const& ""
+
+/* References. */
+
+/* Converting a C++ reference to Go has to be handled in the C++
+ code. */
+%typemap(gotype) SWIGTYPE &
+%{$gotypename%}
+
+%typemap(in) SWIGTYPE &
+%{ $1 = *($&1_ltype)&$input; %}
+
+%typemap(out) SWIGTYPE &
+%{ *($&1_ltype)&$result = $1; %}
+
+%typemap(goout) SWIGTYPE & ""
+
+%typemap(directorin) SWIGTYPE &
+%{ $input = ($1_ltype)&$1; %}
+
+%typemap(godirectorin) SWIGTYPE & ""
+
+%typemap(directorout) SWIGTYPE &
+%{ *($&1_ltype)&$result = $input; %}
+
+%typemap(directorout, warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) SWIGTYPE *const&
+%{ static $*1_ltype swig_temp;
+ swig_temp = *($1_ltype)&$input;
+ $result = &swig_temp; %}
+
+%typemap(gotype) SWIGTYPE &&
+%{$gotypename%}
+
+%typemap(in, fragment="<memory>") SWIGTYPE && (std::unique_ptr<$*1_ltype> rvrdeleter)
+%{ $1 = *($&1_ltype)&$input;
+rvrdeleter.reset($1); %}
+
+%typemap(out) SWIGTYPE &&
+%{ *($&1_ltype)&$result = $1; %}
+
+%typemap(goout) SWIGTYPE && ""
+
+%typemap(directorin) SWIGTYPE &&
+%{ $input = ($1_ltype)&$1_name; %}
+
+%typemap(godirectorin) SWIGTYPE && ""
+
+%typemap(directorout) SWIGTYPE &&
+%{ *($&1_ltype)&$result = $input; %}
+
+/* C arrays turn into Go pointers. If we know the length we can use a
+ slice. */
+
+%typemap(gotype) SWIGTYPE []
+%{$gotypename%}
+
+%typemap(in) SWIGTYPE []
+%{ $1 = *($&1_ltype)&$input; %}
+
+%typemap(out) SWIGTYPE []
+%{ *($&1_ltype)&$result = $1; %}
+
+%typemap(goout) SWIGTYPE [] ""
+
+%typemap(directorin) SWIGTYPE []
+%{ $input = *($1_ltype)&$1; %}
+
+%typemap(godirectorin) SWIGTYPE [] ""
+
+%typemap(directorout) SWIGTYPE []
+%{ *($&1_ltype)&$result = $input; %}
+
+/* Strings. */
+
+%typemap(gotype)
+ char *, char *&, char[ANY], char[] "string"
+
+/* Needed to avoid confusion with the way the go module handles
+ references. */
+%typemap(gotype) char&, unsigned char& "*byte"
+%typemap(gotype) signed char& "*int8"
+
+%typemap(in)
+ char *, char[ANY], char[]
+%{
+ $1 = ($1_ltype)malloc($input.n + 1);
+ memcpy($1, $input.p, $input.n);
+ $1[$input.n] = '\0';
+%}
+
+%typemap(in) char *& (char *temp)
+%{
+ temp = (char *)malloc($input.n + 1);
+ memcpy(temp, $input.p, $input.n);
+ temp[$input.n] = '\0';
+ $1 = ($1_ltype)&temp;
+%}
+
+%typemap(freearg)
+ char *, char[ANY], char[]
+%{ free($1); %}
+
+%typemap(freearg) char *&
+%{ free(temp$argnum); %}
+
+%typemap(out,fragment="AllocateString")
+ char *, char *&, char[ANY], char[]
+%{ $result = Swig_AllocateString((char*)$1, $1 ? strlen((char*)$1) : 0); %}
+
+%typemap(goout,fragment="CopyString")
+ char *, char *&, char[ANY], char[]
+%{ $result = swigCopyString($1) %}
+
+%typemap(directorin,fragment="AllocateString")
+ char *, char *&, char[ANY], char[]
+%{
+ $input = Swig_AllocateString((char*)$1, $1 ? strlen((char*)$1) : 0);
+%}
+
+%typemap(godirectorin,fragment="CopyString")
+ char *, char *&, char[ANY], char[]
+%{
+ $result = swigCopyString($input)
+%}
+
+%typemap(godirectorout)
+ char *, char *&, char[ANY], char[]
+%{
+ {
+ p := Swig_malloc(len($input) + 1)
+ s := (*[1<<30]byte)(unsafe.Pointer(p))[:len($input) + 1]
+ copy(s, $input)
+ s[len($input)] = 0
+ $result = *(*string)(unsafe.Pointer(&s))
+ }
+%}
+
+%typemap(directorout, warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG)
+ char *, char *&, char[ANY], char[]
+%{ $result = ($1_ltype)$input.p; %}
+
+/* String & length */
+
+%typemap(gotype) (char *STRING, size_t LENGTH) "string"
+
+%typemap(in) (char *STRING, size_t LENGTH)
+%{
+ $1 = ($1_ltype)$input.p;
+ $2 = ($2_ltype)$input.n;
+%}
+
+%typemap(out,fragment="AllocateString") (char *STRING, size_t LENGTH)
+%{ $result = Swig_AllocateString((char*)$1, (size_t)$2); %}
+
+%typemap(goout,fragment="CopyString") (char *STRING, size_t LENGTH)
+%{ $result = swigCopyString($1) %}
+
+%typemap(directorin,fragment="AllocateString") (char *STRING, size_t LENGTH)
+%{ $input = Swig_AllocateString((char*)$1, $2); %}
+
+%typemap(godirectorin,fragment="CopyString") (char *STRING, size_t LENGTH)
+%{ $result = swigCopyString($input) %}
+
+%typemap(directorout) (char *STRING, size_t LENGTH)
+%{
+ $1 = ($1_ltype)$input.p;
+ $2 = ($2_ltype)$input.n;
+%}
+
+/* The int & type needs to convert to intgo. */
+
+%typemap(gotype) int & "*int"
+
+%typemap(in) int & (int e)
+%{
+ e = (int)*$input;
+ $1 = &e;
+%}
+
+%typemap(out) int &
+%{ $result = new intgo(*$1); %}
+
+%typemap(argout) int &
+%{ *$input = (intgo)e$argnum; %}
+
+%typemap(goout) int & ""
+
+%typemap(directorin) int & (intgo e)
+%{
+ e = (intgo)$1;
+ $input = &e;
+%}
+
+%typemap(godirectorin) int & ""
+
+%typemap(directorout) int &
+%{
+ $*1_ltype f = ($*1_ltype)*$input;
+ $result = ($1_ltype)&f;
+%}
+
+%typemap(directorargout) int &
+%{ $1 = (int)*$input; %}
+
+%typemap(argout) const int & ""
+%typemap(directorargout) const int & ""
+
+/* Enums. We can't do the right thing for enums in typemap(gotype) so
+ we deliberately don't define them. The right thing would be to
+ capitalize the name. This is instead done in go.cxx. */
+
+%typemap(gotype) enum SWIGTYPE
+%{$gotypename%}
+
+%typemap(in) enum SWIGTYPE
+%{ $1 = ($1_ltype)$input; %}
+
+%typemap(out) enum SWIGTYPE
+%{ $result = (intgo)$1; %}
+
+%typemap(goout) enum SWIGTYPE ""
+
+%typemap(directorin) enum SWIGTYPE
+%{ $input = (intgo)$1; %}
+
+%typemap(godirectorin) enum SWIGTYPE ""
+
+%typemap(directorout) enum SWIGTYPE
+%{ $result = ($1_ltype)$input; %}
+
+%typemap(directorin) enum SWIGTYPE & (intgo e)
+%{
+ e = (intgo)$1;
+ $input = ($1_ltype)&e;
+%}
+
+%typemap(godirectorin) enum SWIGTYPE & ""
+
+%typemap(directorout) enum SWIGTYPE &
+%{ $result = $input; %}
+
+/* Arbitrary type. This is a type passed by value in the C/C++ code.
+ We convert it to a pointer for the Go code. Note that all basic
+ types are explicitly handled above. */
+
+%typemap(gotype) SWIGTYPE
+%{$gotypename%}
+
+%typemap(in) SWIGTYPE ($&1_type argp)
+%{
+ argp = ($&1_ltype)$input;
+ if (argp == NULL) {
+ _swig_gopanic("Attempt to dereference null $1_type");
+ }
+ $1 = ($1_ltype)*argp;
+%}
+
+%typemap(out) SWIGTYPE
+#ifdef __cplusplus
+%{ *($&1_ltype*)&$result = new $1_ltype($1); %}
+#else
+{
+ $&1_ltype $1ptr = ($&1_ltype)malloc(sizeof($1_ltype));
+ memmove($1ptr, &$1, sizeof($1_type));
+ *($&1_ltype*)&$result = $1ptr;
+}
+#endif
+
+%typemap(goout) SWIGTYPE ""
+
+%typemap(directorin) SWIGTYPE
+%{ $input = new $1_ltype(SWIG_STD_MOVE($1)); %}
+
+%typemap(godirectorin) SWIGTYPE ""
+
+%typemap(directorout) SWIGTYPE
+%{ $result = *($&1_ltype)$input; %}
+
+/* Exception handling */
+
+%typemap(throws) char *
+%{ _swig_gopanic($1); %}
+
+%typemap(throws) SWIGTYPE, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE *, SWIGTYPE [], SWIGTYPE [ANY]
+%{
+ (void)$1;
+ _swig_gopanic("C++ $1_type exception thrown");
+%}
+
+/* Typecheck typemaps. The purpose of these is merely to issue a
+ warning for overloaded C++ functions that cannot be overloaded in
+ Go as more than one C++ type maps to a single Go type. */
+
+%typecheck(SWIG_TYPECHECK_BOOL) /* Go bool */
+ bool,
+ const bool &
+ ""
+
+%typecheck(SWIG_TYPECHECK_CHAR) /* Go byte */
+ char,
+ const char &,
+ unsigned char,
+ const unsigned char &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT8) /* Go int8 */
+ signed char,
+ const signed char &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT16) /* Go int16 */
+ short,
+ const short &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT16) /* Go uint16 */
+ unsigned short,
+ const unsigned short &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT32) /* Go int */
+ int,
+ const int &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT32) /* Go uint */
+ unsigned int,
+ const unsigned int &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT64) /* Go int64 */
+ long,
+ const long &,
+ long long,
+ const long long &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT64) /* Go uint64 */
+ unsigned long,
+ const unsigned long &,
+ unsigned long long,
+ const unsigned long long &
+ ""
+
+%typecheck(SWIG_TYPECHECK_FLOAT) /* Go float32 */
+ float,
+ const float &
+ ""
+
+%typecheck(SWIG_TYPECHECK_DOUBLE) /* Go float64 */
+ double,
+ const double &
+ ""
+
+%typecheck(SWIG_TYPECHECK_STRING) /* Go string */
+ char *,
+ char *&,
+ char[ANY],
+ char [],
+ signed char *,
+ signed char *&,
+ signed char[ANY],
+ signed char [],
+ unsigned char *,
+ unsigned char *&,
+ unsigned char[ANY],
+ unsigned char []
+ ""
+
+%typecheck(SWIG_TYPECHECK_POINTER)
+ SWIGTYPE,
+ SWIGTYPE *,
+ SWIGTYPE &,
+ SWIGTYPE &&,
+ SWIGTYPE *const&,
+ SWIGTYPE [],
+ SWIGTYPE (CLASS::*)
+ ""
+
+%apply SWIGTYPE * { SWIGTYPE *const }
+%apply SWIGTYPE (CLASS::*) { SWIGTYPE (CLASS::*const) }
+%apply SWIGTYPE & { SWIGTYPE (CLASS::*const&) }
+
+/* Go keywords. */
+%include <gokw.swg>
+
+%include <goruntime.swg>
diff --git a/contrib/tools/swig/Lib/go/gokw.swg b/contrib/tools/swig/Lib/go/gokw.swg
new file mode 100644
index 0000000000..3542830024
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/gokw.swg
@@ -0,0 +1,33 @@
+/* Rename keywords. */
+
+#define GOKW(x) %keywordwarn("'" `x` "' is a Go keyword",rename="X%s") `x`
+#define GOBN(x) %builtinwarn("'" `x` "' conflicts with a built-in name in Go") "::"`x`
+
+GOKW(break);
+GOKW(case);
+GOKW(chan);
+GOKW(const);
+GOKW(continue);
+GOKW(default);
+GOKW(defer);
+GOKW(else);
+GOKW(fallthrough);
+GOKW(for);
+GOKW(func);
+GOKW(go);
+GOKW(goto);
+GOKW(if);
+GOKW(import);
+GOKW(interface);
+GOKW(package);
+GOKW(range);
+GOKW(return);
+GOKW(select);
+GOKW(struct);
+GOKW(switch);
+GOKW(type);
+GOKW(var);
+
+GOBN(map);
+
+#undef GOKW
diff --git a/contrib/tools/swig/Lib/go/goruntime.swg b/contrib/tools/swig/Lib/go/goruntime.swg
new file mode 100644
index 0000000000..7bf083bd3d
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/goruntime.swg
@@ -0,0 +1,217 @@
+/* ------------------------------------------------------------
+ * goruntime.swg
+ *
+ * Go runtime code for the various generated files.
+ * ------------------------------------------------------------ */
+
+%inline %{
+static void Swig_free(void* p) {
+ free(p);
+}
+
+static void* Swig_malloc(int c) {
+ return malloc(c);
+}
+%}
+
+%insert(runtime) %{
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+%}
+
+%insert(cgo_comment_typedefs) %{
+#include <stddef.h>
+#include <stdint.h>
+%}
+
+#if SWIGGO_INTGO_SIZE == 32
+%insert(runtime) %{
+typedef int intgo;
+typedef unsigned int uintgo;
+%}
+%insert(cgo_comment_typedefs) %{
+typedef int intgo;
+typedef unsigned int uintgo;
+%}
+#elif SWIGGO_INTGO_SIZE == 64
+%insert(runtime) %{
+typedef long long intgo;
+typedef unsigned long long uintgo;
+%}
+%insert(cgo_comment_typedefs) %{
+typedef long long intgo;
+typedef unsigned long long uintgo;
+%}
+#else
+%insert(runtime) %{
+typedef ptrdiff_t intgo;
+typedef size_t uintgo;
+%}
+%insert(cgo_comment_typedefs) %{
+typedef ptrdiff_t intgo;
+typedef size_t uintgo;
+%}
+#endif
+
+#ifndef SWIGGO_GCCGO
+// Set the host compiler struct attribute that will be
+// used to match gc's struct layout. For example, on 386 Windows,
+// gcc wants to 8-align int64s, but gc does not.
+// Use __gcc_struct__ to work around http://gcc.gnu.org/PR52991 on x86,
+// and https://golang.org/issue/5603.
+// See: https://github.com/golang/go/blob/fcbf04f9b93b4cd8addd05c2ed784118eb50a46c/src/cmd/cgo/out.go#L663
+%insert(runtime) %{
+# if !defined(__clang__) && (defined(__i386__) || defined(__x86_64__))
+# define SWIGSTRUCTPACKED __attribute__((__packed__, __gcc_struct__))
+# else
+# define SWIGSTRUCTPACKED __attribute__((__packed__))
+# endif
+%}
+#else
+# define SWIGSTRUCTPACKED
+#endif
+
+%insert(runtime) %{
+
+typedef struct { char *p; intgo n; } _gostring_;
+typedef struct { void* array; intgo len; intgo cap; } _goslice_;
+
+%}
+
+%insert(cgo_comment_typedefs) %{
+
+typedef struct { char *p; intgo n; } _gostring_;
+typedef struct { void* array; intgo len; intgo cap; } _goslice_;
+
+%}
+
+#ifdef SWIGGO_GCCGO
+
+/* Boilerplate for C/C++ code when using gccgo. */
+%insert(runtime) %{
+#define SWIGGO_GCCGO
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void *_cgo_allocate(size_t);
+extern void _cgo_panic(const char *);
+#ifdef __cplusplus
+}
+#endif
+
+#define _swig_goallocate _cgo_allocate
+#define _swig_gopanic _cgo_panic
+%}
+
+#endif
+
+#ifndef SWIGGO_GCCGO
+
+%go_import("unsafe", _ "runtime/cgo")
+
+#else
+
+%go_import("syscall", "unsafe")
+
+%insert(go_header) %{
+
+type _ syscall.Sockaddr
+
+%}
+
+#endif
+
+%insert(go_header) %{
+
+type _ unsafe.Pointer
+
+%}
+
+/* Swig_always_false is used to conditionally assign parameters to
+ Swig_escape_val so that the compiler thinks that they escape. We
+ only assign them if Swig_always_false is true, which it never is.
+ We export the variable so that the compiler doesn't realize that it
+ is never set. */
+%insert(go_header) %{
+var Swig_escape_always_false bool
+var Swig_escape_val interface{}
+%}
+
+/* Function pointers are translated by the code in go.cxx into
+ _swig_fnptr. Member pointers are translated to _swig_memberptr. */
+
+%insert(go_header) %{
+type _swig_fnptr *byte
+type _swig_memberptr *byte
+%}
+
+/* Convert a Go interface value into a C++ pointer. */
+
+%insert(go_header) %{
+func getSwigcptr(v interface { Swigcptr() uintptr }) uintptr {
+ if v == nil {
+ return 0
+ }
+ return v.Swigcptr()
+}
+%}
+
+/* For directors we need C++ to track a Go pointer. Since we can't
+ pass a Go pointer into C++, we use a map to track the pointers on
+ the Go side. */
+
+%go_import("sync")
+
+%insert(go_header) %{
+type _ sync.Mutex
+%}
+
+%insert(go_director) %{
+
+var swigDirectorTrack struct {
+ sync.Mutex
+ m map[int]interface{}
+ c int
+}
+
+func swigDirectorAdd(v interface{}) int {
+ swigDirectorTrack.Lock()
+ defer swigDirectorTrack.Unlock()
+ if swigDirectorTrack.m == nil {
+ swigDirectorTrack.m = make(map[int]interface{})
+ }
+ swigDirectorTrack.c++
+ ret := swigDirectorTrack.c
+ swigDirectorTrack.m[ret] = v
+ return ret
+}
+
+func swigDirectorLookup(c int) interface{} {
+ swigDirectorTrack.Lock()
+ defer swigDirectorTrack.Unlock()
+ ret := swigDirectorTrack.m[c]
+ if ret == nil {
+ panic("C++ director pointer not found (possible use-after-free)")
+ }
+ return ret
+}
+
+func swigDirectorDelete(c int) {
+ swigDirectorTrack.Lock()
+ defer swigDirectorTrack.Unlock()
+ if swigDirectorTrack.m[c] == nil {
+ if c > swigDirectorTrack.c {
+ panic("C++ director pointer invalid (possible memory corruption")
+ } else {
+ panic("C++ director pointer not found (possible use-after-free)")
+ }
+ }
+ delete(swigDirectorTrack.m, c)
+}
+
+%}
diff --git a/contrib/tools/swig/Lib/go/gostring.swg b/contrib/tools/swig/Lib/go/gostring.swg
new file mode 100644
index 0000000000..d9c47d2858
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/gostring.swg
@@ -0,0 +1,29 @@
+/* ------------------------------------------------------------
+ * gostring.swg
+ *
+ * Support for returning strings from C to Go.
+ * ------------------------------------------------------------ */
+
+// C/C++ code to convert a memory buffer into a Go string allocated in
+// C/C++ memory.
+%fragment("AllocateString", "runtime") %{
+static _gostring_ Swig_AllocateString(const char *p, size_t l) {
+ _gostring_ ret;
+ ret.p = (char*)malloc(l);
+ memcpy(ret.p, p, l);
+ ret.n = l;
+ return ret;
+}
+%}
+
+// Go code to convert a string allocated in C++ memory to one
+// allocated in Go memory.
+%fragment("CopyString", "go_runtime") %{
+type swig_gostring struct { p unsafe.Pointer; n int }
+func swigCopyString(s string) string {
+ p := *(*swig_gostring)(unsafe.Pointer(&s))
+ r := string((*[0x7fffffff]byte)(p.p)[:p.n])
+ Swig_free(uintptr(p.p))
+ return r
+}
+%}
diff --git a/contrib/tools/swig/Lib/go/std_common.i b/contrib/tools/swig/Lib/go/std_common.i
new file mode 100644
index 0000000000..c010facacd
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/std_common.i
@@ -0,0 +1,4 @@
+%include <std_except.i>
+
+%apply size_t { std::size_t };
+%apply const size_t& { const std::size_t& };
diff --git a/contrib/tools/swig/Lib/go/std_except.i b/contrib/tools/swig/Lib/go/std_except.i
new file mode 100644
index 0000000000..4f021a1264
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/std_except.i
@@ -0,0 +1,31 @@
+/* -----------------------------------------------------------------------------
+ * std_except.i
+ *
+ * Typemaps used by the STL wrappers that throw exceptions.
+ * These typemaps are used when methods are declared with an STL exception specification, such as
+ * size_t at() const throw (std::out_of_range);
+ * ----------------------------------------------------------------------------- */
+
+%{
+#include <typeinfo>
+#include <stdexcept>
+%}
+
+namespace std
+{
+ %ignore exception;
+ struct exception {};
+}
+
+%typemap(throws) std::bad_cast %{_swig_gopanic($1.what());%}
+%typemap(throws) std::bad_exception %{_swig_gopanic($1.what());%}
+%typemap(throws) std::domain_error %{_swig_gopanic($1.what());%}
+%typemap(throws) std::exception %{_swig_gopanic($1.what());%}
+%typemap(throws) std::invalid_argument %{_swig_gopanic($1.what());%}
+%typemap(throws) std::length_error %{_swig_gopanic($1.what());%}
+%typemap(throws) std::logic_error %{_swig_gopanic($1.what());%}
+%typemap(throws) std::out_of_range %{_swig_gopanic($1.what());%}
+%typemap(throws) std::overflow_error %{_swig_gopanic($1.what());%}
+%typemap(throws) std::range_error %{_swig_gopanic($1.what());%}
+%typemap(throws) std::runtime_error %{_swig_gopanic($1.what());%}
+%typemap(throws) std::underflow_error %{_swig_gopanic($1.what());%}
diff --git a/contrib/tools/swig/Lib/go/std_string.i b/contrib/tools/swig/Lib/go/std_string.i
new file mode 100644
index 0000000000..35b4a5e46b
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/std_string.i
@@ -0,0 +1,162 @@
+/* -----------------------------------------------------------------------------
+ * std_string.i
+ *
+ * Typemaps for std::string and const std::string&
+ * These are mapped to a Go string and are passed around by value.
+ *
+ * To use non-const std::string references use the following %apply. Note
+ * that they are passed by value.
+ * %apply const std::string & {std::string &};
+ * ----------------------------------------------------------------------------- */
+
+%{
+#include <string>
+%}
+
+namespace std {
+
+%naturalvar string;
+
+class string;
+
+%typemap(gotype) string, const string & "string"
+
+%typemap(in) string
+%{ $1.assign($input.p, $input.n); %}
+
+%typemap(godirectorout) string
+%{
+ {
+ p := Swig_malloc(len($input))
+ s := (*[1<<30]byte)(unsafe.Pointer(p))[:len($input)]
+ copy(s, $input)
+ $result = *(*string)(unsafe.Pointer(&s))
+ }
+%}
+
+%typemap(directorout) string
+%{
+ $result.assign($input.p, $input.n);
+ free($input.p);
+%}
+
+%typemap(out,fragment="AllocateString") string
+%{ $result = Swig_AllocateString($1.data(), $1.length()); %}
+
+%typemap(goout,fragment="CopyString") string
+%{ $result = swigCopyString($1) %}
+
+%typemap(directorin,fragment="AllocateString") string
+%{ $input = Swig_AllocateString($1.data(), $1.length()); %}
+
+%typemap(godirectorin,fragment="CopyString") string
+%{ $result = swigCopyString($input) %}
+
+%typemap(throws) string
+%{ _swig_gopanic($1.c_str()); %}
+
+%typemap(in) const string &
+%{
+ $*1_ltype $1_str($input.p, $input.n);
+ $1 = &$1_str;
+%}
+
+%typemap(godirectorout) const string &
+%{
+ {
+ p := Swig_malloc(len($input))
+ s := (*[1<<30]byte)(unsafe.Pointer(p))[:len($input)]
+ copy(s, $input)
+ $result = *(*string)(unsafe.Pointer(&s))
+ }
+%}
+
+%typemap(directorout,warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) const string &
+%{
+ static $*1_ltype $1_str;
+ $1_str.assign($input.p, $input.n);
+ free($input.p);
+ $result = &$1_str;
+%}
+
+%typemap(out,fragment="AllocateString") const string &
+%{ $result = Swig_AllocateString((*$1).data(), (*$1).length()); %}
+
+%typemap(goout,fragment="CopyString") const string &
+%{ $result = swigCopyString($1) %}
+
+%typemap(directorin,fragment="AllocateString") const string &
+%{ $input = Swig_AllocateString($1.data(), $1.length()); %}
+
+%typemap(godirectorin,fragment="CopyString") const string &
+%{ $result = swigCopyString($input) %}
+
+%typemap(throws) const string &
+%{ _swig_gopanic($1.c_str()); %}
+
+
+%typemap(gotype) string * "*string"
+
+%typemap(in) string * (string temp)
+%{
+ if ($input) {
+ temp.assign($input->p, $input->n);
+ $1 = &temp;
+ } else
+ $1 = 0;
+%}
+
+%typemap(godirectorout) string *
+%{
+ if $input != nil {
+ p := Swig_malloc(len(*$input))
+ s := (*[1<<30]byte)(unsafe.Pointer(p))[:len(*$input)]
+ copy(s, *$input)
+ $result = (*string)(unsafe.Pointer(&s))
+ } else {
+ $result = nil
+ }
+%}
+
+%typemap(directorout) string * (string temp)
+%{
+ temp.assign($input->p, $input->n);
+ $result = &temp;
+ free($input.p);
+%}
+
+%typemap(out,fragment="AllocateString") string * (_gostring_ temp)
+%{
+ temp = Swig_AllocateString($1->data(), $1->length());
+ $result = &temp;
+%}
+
+%typemap(goout,fragment="CopyString") string *
+%{ *$result = swigCopyString(*$1) %}
+
+%typemap(directorin,fragment="AllocateString") string * (_gostring_ temp)
+%{
+ if ($1) {
+ temp = Swig_AllocateString($1->data(), $1->length());
+ $input = &temp;
+ } else
+ $input = 0;
+%}
+
+%typemap(godirectorin,fragment="CopyString") string *
+%{ *$result = swigCopyString(*$input); %}
+
+%typemap(argout,fragment="AllocateString") string *
+%{
+ if ($1)
+ *$input = Swig_AllocateString($1->data(), $1->length());
+%}
+
+%typemap(goargout,fragment="CopyString") string *
+%{
+ if $input != nil {
+ *$1 = swigCopyString(*$input)
+ }
+%}
+
+}
diff --git a/contrib/tools/swig/Lib/go/std_vector.i b/contrib/tools/swig/Lib/go/std_vector.i
new file mode 100644
index 0000000000..679c707596
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/std_vector.i
@@ -0,0 +1,92 @@
+/* -----------------------------------------------------------------------------
+ * std_vector.i
+ * ----------------------------------------------------------------------------- */
+
+%{
+#include <vector>
+#include <stdexcept>
+%}
+
+namespace std {
+
+ template<class T> class vector {
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ vector();
+ vector(size_type n);
+ vector(const vector& other);
+
+ size_type size() const;
+ size_type capacity() const;
+ void reserve(size_type n);
+ %rename(isEmpty) empty;
+ bool empty() const;
+ void clear();
+ %rename(add) push_back;
+ void push_back(const value_type& x);
+ %extend {
+ const_reference get(int i) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ return (*self)[i];
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ void set(int i, const value_type& val) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ (*self)[i] = val;
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ }
+ };
+
+ // bool specialization
+ template<> class vector<bool> {
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef bool value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef bool const_reference;
+
+ vector();
+ vector(size_type n);
+ vector(const vector& other);
+
+ size_type size() const;
+ size_type capacity() const;
+ void reserve(size_type n);
+ %rename(isEmpty) empty;
+ bool empty() const;
+ void clear();
+ %rename(add) push_back;
+ void push_back(const value_type& x);
+ %extend {
+ bool get(int i) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ return (*self)[i];
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ void set(int i, const value_type& val) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ (*self)[i] = val;
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ }
+ };
+}
diff --git a/contrib/tools/swig/Lib/go/typemaps.i b/contrib/tools/swig/Lib/go/typemaps.i
new file mode 100644
index 0000000000..d2e60d37c8
--- /dev/null
+++ b/contrib/tools/swig/Lib/go/typemaps.i
@@ -0,0 +1,298 @@
+/* -----------------------------------------------------------------------------
+ * typemaps.i
+ *
+ * Pointer and reference handling typemap library
+ *
+ * These mappings provide support for input/output arguments and common
+ * uses for C/C++ pointers and C++ references.
+ * ----------------------------------------------------------------------------- */
+
+/*
+INPUT typemaps
+--------------
+
+These typemaps remap a C pointer or C++ reference to be an "INPUT" value which is
+passed by value instead of reference.
+
+The following typemaps can be applied to turn a pointer or reference into a simple
+input value. That is, instead of passing a pointer or reference to an object,
+you would use a real value instead.
+
+ bool *INPUT, bool &INPUT
+ signed char *INPUT, signed char &INPUT
+ unsigned char *INPUT, unsigned char &INPUT
+ short *INPUT, short &INPUT
+ unsigned short *INPUT, unsigned short &INPUT
+ int *INPUT, int &INPUT
+ unsigned int *INPUT, unsigned int &INPUT
+ long *INPUT, long &INPUT
+ unsigned long *INPUT, unsigned long &INPUT
+ long long *INPUT, long long &INPUT
+ unsigned long long *INPUT, unsigned long long &INPUT
+ float *INPUT, float &INPUT
+ double *INPUT, double &INPUT
+
+To use these, suppose you had a C function like this :
+
+ double fadd(double *a, double *b) {
+ return *a+*b;
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ double fadd(double *INPUT, double *INPUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *INPUT { double *a, double *b };
+ double fadd(double *a, double *b);
+
+In Go you could then use it like this:
+ answer := modulename.Fadd(10.0, 20.0)
+
+There are no char *INPUT typemaps, however you can apply the signed
+char * typemaps instead:
+ %include <typemaps.i>
+ %apply signed char *INPUT {char *input};
+ void f(char *input);
+*/
+
+%define INPUT_TYPEMAP(TYPE, GOTYPE)
+%typemap(gotype) TYPE *INPUT, TYPE &INPUT "GOTYPE"
+
+ %typemap(in) TYPE *INPUT, TYPE &INPUT
+%{ $1 = ($1_ltype)&$input; %}
+
+%typemap(out) TYPE *INPUT, TYPE &INPUT ""
+
+%typemap(goout) TYPE *INPUT, TYPE &INPUT ""
+
+%typemap(freearg) TYPE *INPUT, TYPE &INPUT ""
+
+%typemap(argout) TYPE *INPUT, TYPE &INPUT ""
+
+// %typemap(typecheck) TYPE *INPUT = TYPE;
+// %typemap(typecheck) TYPE &INPUT = TYPE;
+%enddef
+
+INPUT_TYPEMAP(bool, bool);
+INPUT_TYPEMAP(signed char, int8);
+INPUT_TYPEMAP(char, byte);
+INPUT_TYPEMAP(unsigned char, byte);
+INPUT_TYPEMAP(short, int16);
+INPUT_TYPEMAP(unsigned short, uint16);
+INPUT_TYPEMAP(int, int);
+INPUT_TYPEMAP(unsigned int, uint);
+INPUT_TYPEMAP(long, int64);
+INPUT_TYPEMAP(unsigned long, uint64);
+INPUT_TYPEMAP(long long, int64);
+INPUT_TYPEMAP(unsigned long long, uint64);
+INPUT_TYPEMAP(float, float32);
+INPUT_TYPEMAP(double, float64);
+
+#undef INPUT_TYPEMAP
+
+// OUTPUT typemaps. These typemaps are used for parameters that
+// are output only. An array replaces the c pointer or reference parameter.
+// The output value is returned in this array passed in.
+
+/*
+OUTPUT typemaps
+---------------
+
+The following typemaps can be applied to turn a pointer or reference
+into an "output" value. When calling a function, no input value would
+be given for a parameter, but an output value would be returned. This
+works by a Go slice being passed as a parameter where a c pointer or
+reference is required. As with any Go function, the array is passed
+by reference so that any modifications to the array will be picked up
+in the calling function. Note that the array passed in MUST have at
+least one element, but as the c function does not require any input,
+the value can be set to anything.
+
+ bool *OUTPUT, bool &OUTPUT
+ signed char *OUTPUT, signed char &OUTPUT
+ unsigned char *OUTPUT, unsigned char &OUTPUT
+ short *OUTPUT, short &OUTPUT
+ unsigned short *OUTPUT, unsigned short &OUTPUT
+ int *OUTPUT, int &OUTPUT
+ unsigned int *OUTPUT, unsigned int &OUTPUT
+ long *OUTPUT, long &OUTPUT
+ unsigned long *OUTPUT, unsigned long &OUTPUT
+ long long *OUTPUT, long long &OUTPUT
+ unsigned long long *OUTPUT, unsigned long long &OUTPUT
+ float *OUTPUT, float &OUTPUT
+ double *OUTPUT, double &OUTPUT
+
+For example, suppose you were trying to wrap the modf() function in the
+C math library which splits x into integral and fractional parts (and
+returns the integer part in one of its parameters):
+
+ double modf(double x, double *ip);
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ double modf(double x, double *OUTPUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *OUTPUT { double *ip };
+ double modf(double x, double *ip);
+
+The Go output of the function would be the function return value and the
+value in the single element array. In Go you would use it like this:
+
+ ptr := []float64{0.0}
+ fraction := modulename.Modf(5.0,ptr)
+
+There are no char *OUTPUT typemaps, however you can apply the signed
+char * typemaps instead:
+ %include <typemaps.i>
+ %apply signed char *OUTPUT {char *output};
+ void f(char *output);
+*/
+
+%define OUTPUT_TYPEMAP(TYPE, GOTYPE)
+%typemap(gotype) TYPE *OUTPUT, TYPE &OUTPUT %{[]GOTYPE%}
+
+%typemap(in) TYPE *OUTPUT($*1_ltype temp), TYPE &OUTPUT($*1_ltype temp)
+{
+ if ($input.len == 0) {
+ _swig_gopanic("array must contain at least 1 element");
+ }
+ $1 = &temp;
+}
+
+%typemap(out) TYPE *OUTPUT, TYPE &OUTPUT ""
+
+%typemap(goout) TYPE *INPUT, TYPE &INPUT ""
+
+%typemap(freearg) TYPE *OUTPUT, TYPE &OUTPUT ""
+
+%typemap(argout) TYPE *OUTPUT, TYPE &OUTPUT
+{
+ TYPE* a = (TYPE *) $input.array;
+ a[0] = temp$argnum;
+}
+
+%enddef
+
+OUTPUT_TYPEMAP(bool, bool);
+OUTPUT_TYPEMAP(signed char, int8);
+OUTPUT_TYPEMAP(char, byte);
+OUTPUT_TYPEMAP(unsigned char, byte);
+OUTPUT_TYPEMAP(short, int16);
+OUTPUT_TYPEMAP(unsigned short, uint16);
+OUTPUT_TYPEMAP(int, int);
+OUTPUT_TYPEMAP(unsigned int, uint);
+OUTPUT_TYPEMAP(long, int64);
+OUTPUT_TYPEMAP(unsigned long, uint64);
+OUTPUT_TYPEMAP(long long, int64);
+OUTPUT_TYPEMAP(unsigned long long, uint64);
+OUTPUT_TYPEMAP(float, float32);
+OUTPUT_TYPEMAP(double, float64);
+
+#undef OUTPUT_TYPEMAP
+
+/*
+INOUT typemaps
+--------------
+
+Mappings for a parameter that is both an input and an output parameter
+
+The following typemaps can be applied to make a function parameter both
+an input and output value. This combines the behavior of both the
+"INPUT" and "OUTPUT" typemaps described earlier. Output values are
+returned as an element in a Go slice.
+
+ bool *INOUT, bool &INOUT
+ signed char *INOUT, signed char &INOUT
+ unsigned char *INOUT, unsigned char &INOUT
+ short *INOUT, short &INOUT
+ unsigned short *INOUT, unsigned short &INOUT
+ int *INOUT, int &INOUT
+ unsigned int *INOUT, unsigned int &INOUT
+ long *INOUT, long &INOUT
+ unsigned long *INOUT, unsigned long &INOUT
+ long long *INOUT, long long &INOUT
+ unsigned long long *INOUT, unsigned long long &INOUT
+ float *INOUT, float &INOUT
+ double *INOUT, double &INOUT
+
+For example, suppose you were trying to wrap the following function :
+
+ void neg(double *x) {
+ *x = -(*x);
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ void neg(double *INOUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *INOUT { double *x };
+ void neg(double *x);
+
+This works similarly to C in that the mapping directly modifies the
+input value - the input must be an array with a minimum of one element.
+The element in the array is the input and the output is the element in
+the array.
+
+ x := []float64{5.0}
+ Neg(x);
+
+The implementation of the OUTPUT and INOUT typemaps is different to
+other languages in that other languages will return the output value
+as part of the function return value. This difference is due to Go
+being a typed language.
+
+There are no char *INOUT typemaps, however you can apply the signed
+char * typemaps instead:
+ %include <typemaps.i>
+ %apply signed char *INOUT {char *inout};
+ void f(char *inout);
+*/
+
+%define INOUT_TYPEMAP(TYPE, GOTYPE)
+%typemap(gotype) TYPE *INOUT, TYPE &INOUT %{[]GOTYPE%}
+
+%typemap(in) TYPE *INOUT, TYPE &INOUT {
+ if ($input.len == 0) {
+ _swig_gopanic("array must contain at least 1 element");
+ }
+ $1 = ($1_ltype) $input.array;
+}
+
+%typemap(out) TYPE *INOUT, TYPE &INOUT ""
+
+%typemap(goout) TYPE *INOUT, TYPE &INOUT ""
+
+%typemap(freearg) TYPE *INOUT, TYPE &INOUT ""
+
+%typemap(argout) TYPE *INOUT, TYPE &INOUT ""
+
+%enddef
+
+INOUT_TYPEMAP(bool, bool);
+INOUT_TYPEMAP(signed char, int8);
+INOUT_TYPEMAP(char, byte);
+INOUT_TYPEMAP(unsigned char, byte);
+INOUT_TYPEMAP(short, int16);
+INOUT_TYPEMAP(unsigned short, uint16);
+INOUT_TYPEMAP(int, int);
+INOUT_TYPEMAP(unsigned int, uint);
+INOUT_TYPEMAP(long, int64);
+INOUT_TYPEMAP(unsigned long, uint64);
+INOUT_TYPEMAP(long long, int64);
+INOUT_TYPEMAP(unsigned long long, uint64);
+INOUT_TYPEMAP(float, float32);
+INOUT_TYPEMAP(double, float64);
+
+#undef INOUT_TYPEMAP
diff --git a/contrib/tools/swig/Lib/java/enumtypesafe.swg b/contrib/tools/swig/Lib/java/enumtypesafe.swg
new file mode 100644
index 0000000000..c2012f568b
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/enumtypesafe.swg
@@ -0,0 +1,117 @@
+/* -----------------------------------------------------------------------------
+ * enumtypesafe.swg
+ *
+ * Include this file in order for C/C++ enums to be wrapped by the so called
+ * typesafe enum pattern. Each enum has an equivalent Java class named after the
+ * enum and each enum item is a static instance of this class.
+ * ----------------------------------------------------------------------------- */
+
+// const enum SWIGTYPE & typemaps
+%typemap(jni) const enum SWIGTYPE & "jint"
+%typemap(jtype) const enum SWIGTYPE & "int"
+%typemap(jstype) const enum SWIGTYPE & "$*javaclassname"
+
+%typemap(in) const enum SWIGTYPE & ($*1_ltype temp)
+%{ temp = ($*1_ltype)$input;
+ $1 = &temp; %}
+%typemap(out) const enum SWIGTYPE & %{ $result = (jint)*$1; %}
+
+%typemap(directorout,warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) const enum SWIGTYPE &
+%{ static $*1_ltype temp = ($*1_ltype)$input;
+ $result = &temp; %}
+%typemap(directorin, descriptor="L$packagepath/$*javaclassname;") const enum SWIGTYPE & "$input = (jint)$1;"
+%typemap(javadirectorin) const enum SWIGTYPE & "$*javaclassname.swigToEnum($jniinput)"
+%typemap(javadirectorout) const enum SWIGTYPE & "($javacall).swigValue()"
+
+%typecheck(SWIG_TYPECHECK_POINTER) const enum SWIGTYPE & ""
+
+%typemap(throws) const enum SWIGTYPE &
+%{ (void)$1;
+ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, "C++ $1_type exception thrown"); %}
+
+%typemap(javain) const enum SWIGTYPE & "$javainput.swigValue()"
+%typemap(javaout) const enum SWIGTYPE & {
+ return $*javaclassname.swigToEnum($jnicall);
+ }
+
+// enum SWIGTYPE typemaps
+%typemap(jni) enum SWIGTYPE "jint"
+%typemap(jtype) enum SWIGTYPE "int"
+%typemap(jstype) enum SWIGTYPE "$javaclassname"
+
+%typemap(in) enum SWIGTYPE %{ $1 = ($1_ltype)$input; %}
+%typemap(out) enum SWIGTYPE %{ $result = (jint)$1; %}
+
+%typemap(directorout) enum SWIGTYPE %{ $result = ($1_ltype)$input; %}
+%typemap(directorin, descriptor="L$packagepath/$javaclassname;") enum SWIGTYPE "$input = (jint) $1;"
+%typemap(javadirectorin) enum SWIGTYPE "$javaclassname.swigToEnum($jniinput)"
+%typemap(javadirectorout) enum SWIGTYPE "($javacall).swigValue()"
+
+%typecheck(SWIG_TYPECHECK_POINTER) enum SWIGTYPE ""
+
+%typemap(throws) enum SWIGTYPE
+%{ (void)$1;
+ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, "C++ $1_type exception thrown"); %}
+
+%typemap(javain) enum SWIGTYPE "$javainput.swigValue()"
+%typemap(javaout) enum SWIGTYPE {
+ return $javaclassname.swigToEnum($jnicall);
+ }
+
+// '$static' will be replaced with either 'static' or nothing depending on whether the enum is an inner Java class or not
+%typemap(javaclassmodifiers) enum SWIGTYPE "public final $static class"
+%typemap(javabase) enum SWIGTYPE ""
+%typemap(javacode) enum SWIGTYPE ""
+%typemap(javaimports) enum SWIGTYPE ""
+%typemap(javainterfaces) enum SWIGTYPE ""
+
+/*
+ * The swigToEnum method is used to find the Java enum from a C++ enum integer value. The default one here takes
+ * advantage of the fact that most enums do not have initial values specified, so the lookup is fast. If initial
+ * values are specified then a lengthy linear search through all possible enums might occur. Specific typemaps could be
+ * written to possibly optimise this lookup by taking advantage of characteristics peculiar to the targeted enum.
+ * The special variable, $enumvalues, is replaced with a comma separated list of all the enum values.
+ */
+%typemap(javabody) enum SWIGTYPE %{
+ public final int swigValue() {
+ return swigValue;
+ }
+
+ public String toString() {
+ return swigName;
+ }
+
+ public static $javaclassname swigToEnum(int swigValue) {
+ if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
+ return swigValues[swigValue];
+ for (int i = 0; i < swigValues.length; i++)
+ if (swigValues[i].swigValue == swigValue)
+ return swigValues[i];
+ throw new IllegalArgumentException("No enum " + $javaclassname.class + " with value " + swigValue);
+ }
+
+ private $javaclassname(String swigName) {
+ this.swigName = swigName;
+ this.swigValue = swigNext++;
+ }
+
+ private $javaclassname(String swigName, int swigValue) {
+ this.swigName = swigName;
+ this.swigValue = swigValue;
+ swigNext = swigValue+1;
+ }
+
+ private $javaclassname(String swigName, $javaclassname swigEnum) {
+ this.swigName = swigName;
+ this.swigValue = swigEnum.swigValue;
+ swigNext = this.swigValue+1;
+ }
+
+ private static $javaclassname[] swigValues = { $enumvalues };
+ private static int swigNext = 0;
+ private final int swigValue;
+ private final String swigName;
+%}
+
+%javaenum(typesafe);
+
diff --git a/contrib/tools/swig/Lib/java/java.swg b/contrib/tools/swig/Lib/java/java.swg
new file mode 100644
index 0000000000..8719818bb8
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/java.swg
@@ -0,0 +1,1458 @@
+/* -----------------------------------------------------------------------------
+ * java.swg
+ *
+ * Java typemaps
+ * ----------------------------------------------------------------------------- */
+
+%include <javahead.swg>
+
+/* The jni, jtype and jstype typemaps work together and so there should be one of each.
+ * The jni typemap contains the JNI type used in the JNI (C/C++) code.
+ * The jtype typemap contains the Java type used in the JNI intermediary class.
+ * The jstype typemap contains the Java type used in the Java proxy classes, type wrapper classes and module class. */
+
+/* Fragments */
+%fragment("SWIG_PackData", "header") {
+/* Pack binary data into a string */
+SWIGINTERN char * SWIG_PackData(char *c, void *ptr, size_t sz) {
+ static const char hex[17] = "0123456789abcdef";
+ const unsigned char *u = (unsigned char *) ptr;
+ const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ unsigned char uu = *u;
+ *(c++) = hex[(uu & 0xf0) >> 4];
+ *(c++) = hex[uu & 0xf];
+ }
+ return c;
+}
+}
+
+%fragment("SWIG_UnPackData", "header") {
+/* Unpack binary data from a string */
+SWIGINTERN const char * SWIG_UnpackData(const char *c, void *ptr, size_t sz) {
+ unsigned char *u = (unsigned char *) ptr;
+ const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ char d = *(c++);
+ unsigned char uu;
+ if ((d >= '0') && (d <= '9'))
+ uu = ((d - '0') << 4);
+ else if ((d >= 'a') && (d <= 'f'))
+ uu = ((d - ('a'-10)) << 4);
+ else
+ return (char *) 0;
+ d = *(c++);
+ if ((d >= '0') && (d <= '9'))
+ uu |= (d - '0');
+ else if ((d >= 'a') && (d <= 'f'))
+ uu |= (d - ('a'-10));
+ else
+ return (char *) 0;
+ *u = uu;
+ }
+ return c;
+}
+}
+
+%fragment("SWIG_JavaIntFromSize_t", "header") {
+/* Check for overflow converting to Java int (always signed 32-bit) from (unsigned variable-bit) size_t */
+SWIGINTERN jint SWIG_JavaIntFromSize_t(size_t size) {
+ static const jint JINT_MAX = 0x7FFFFFFF;
+ return (size > (size_t)JINT_MAX) ? -1 : (jint)size;
+}
+}
+
+/* Primitive types */
+%typemap(jni) bool, const bool & "jboolean"
+%typemap(jni) char, const char & "jchar"
+%typemap(jni) signed char, const signed char & "jbyte"
+%typemap(jni) unsigned char, const unsigned char & "jshort"
+%typemap(jni) short, const short & "jshort"
+%typemap(jni) unsigned short, const unsigned short & "jint"
+%typemap(jni) int, const int & "jint"
+%typemap(jni) unsigned int, const unsigned int & "jlong"
+%typemap(jni) long, const long & "jint"
+%typemap(jni) unsigned long, const unsigned long & "jlong"
+%typemap(jni) long long, const long long & "jlong"
+%typemap(jni) unsigned long long, const unsigned long long & "jobject"
+%typemap(jni) float, const float & "jfloat"
+%typemap(jni) double, const double & "jdouble"
+%typemap(jni) void "void"
+
+%typemap(jtype) bool, const bool & "boolean"
+%typemap(jtype) char, const char & "char"
+%typemap(jtype) signed char, const signed char & "byte"
+%typemap(jtype) unsigned char, const unsigned char & "short"
+%typemap(jtype) short, const short & "short"
+%typemap(jtype) unsigned short, const unsigned short & "int"
+%typemap(jtype) int, const int & "int"
+%typemap(jtype) unsigned int, const unsigned int & "long"
+%typemap(jtype) long, const long & "int"
+%typemap(jtype) unsigned long, const unsigned long & "long"
+%typemap(jtype) long long, const long long & "long"
+%typemap(jtype) unsigned long long, const unsigned long long & "java.math.BigInteger"
+%typemap(jtype) float, const float & "float"
+%typemap(jtype) double, const double & "double"
+%typemap(jtype) void "void"
+
+%typemap(jstype) bool, const bool & "boolean"
+%typemap(jstype) char, const char & "char"
+%typemap(jstype) signed char, const signed char & "byte"
+%typemap(jstype) unsigned char, const unsigned char & "short"
+%typemap(jstype) short, const short & "short"
+%typemap(jstype) unsigned short, const unsigned short & "int"
+%typemap(jstype) int, const int & "int"
+%typemap(jstype) unsigned int, const unsigned int & "long"
+%typemap(jstype) long, const long & "int"
+%typemap(jstype) unsigned long, const unsigned long & "long"
+%typemap(jstype) long long, const long long & "long"
+%typemap(jstype) unsigned long long, const unsigned long long & "java.math.BigInteger"
+%typemap(jstype) float, const float & "float"
+%typemap(jstype) double, const double & "double"
+%typemap(jstype) void "void"
+
+%typemap(jboxtype) bool, const bool & "Boolean"
+%typemap(jboxtype) char, const char & "Character"
+%typemap(jboxtype) signed char, const signed char & "Byte"
+%typemap(jboxtype) unsigned char, const unsigned char & "Short"
+%typemap(jboxtype) short, const short & "Short"
+%typemap(jboxtype) unsigned short, const unsigned short & "Integer"
+%typemap(jboxtype) int, const int & "Integer"
+%typemap(jboxtype) unsigned int, const unsigned int & "Long"
+%typemap(jboxtype) long, const long & "Integer"
+%typemap(jboxtype) unsigned long, const unsigned long & "Long"
+%typemap(jboxtype) long long, const long long & "Long"
+%typemap(jboxtype) unsigned long long, const unsigned long long & "java.math.BigInteger"
+%typemap(jboxtype) float, const float & "Float"
+%typemap(jboxtype) double, const double & "Double"
+
+%typemap(jni) char *, char *&, char[ANY], char[] "jstring"
+%typemap(jtype) char *, char *&, char[ANY], char[] "String"
+%typemap(jstype) char *, char *&, char[ANY], char[] "String"
+
+/* JNI types */
+%typemap(jni) jboolean "jboolean"
+%typemap(jni) jchar "jchar"
+%typemap(jni) jbyte "jbyte"
+%typemap(jni) jshort "jshort"
+%typemap(jni) jint "jint"
+%typemap(jni) jlong "jlong"
+%typemap(jni) jfloat "jfloat"
+%typemap(jni) jdouble "jdouble"
+%typemap(jni) jstring "jstring"
+%typemap(jni) jobject "jobject"
+%typemap(jni) jbooleanArray "jbooleanArray"
+%typemap(jni) jcharArray "jcharArray"
+%typemap(jni) jbyteArray "jbyteArray"
+%typemap(jni) jshortArray "jshortArray"
+%typemap(jni) jintArray "jintArray"
+%typemap(jni) jlongArray "jlongArray"
+%typemap(jni) jfloatArray "jfloatArray"
+%typemap(jni) jdoubleArray "jdoubleArray"
+%typemap(jni) jobjectArray "jobjectArray"
+
+%typemap(jtype) jboolean "boolean"
+%typemap(jtype) jchar "char"
+%typemap(jtype) jbyte "byte"
+%typemap(jtype) jshort "short"
+%typemap(jtype) jint "int"
+%typemap(jtype) jlong "long"
+%typemap(jtype) jfloat "float"
+%typemap(jtype) jdouble "double"
+%typemap(jtype) jstring "String"
+%typemap(jtype) jobject "java.lang.Object"
+%typemap(jtype) jbooleanArray "boolean[]"
+%typemap(jtype) jcharArray "char[]"
+%typemap(jtype) jbyteArray "byte[]"
+%typemap(jtype) jshortArray "short[]"
+%typemap(jtype) jintArray "int[]"
+%typemap(jtype) jlongArray "long[]"
+%typemap(jtype) jfloatArray "float[]"
+%typemap(jtype) jdoubleArray "double[]"
+%typemap(jtype) jobjectArray "java.lang.Object[]"
+
+%typemap(jstype) jboolean "boolean"
+%typemap(jstype) jchar "char"
+%typemap(jstype) jbyte "byte"
+%typemap(jstype) jshort "short"
+%typemap(jstype) jint "int"
+%typemap(jstype) jlong "long"
+%typemap(jstype) jfloat "float"
+%typemap(jstype) jdouble "double"
+%typemap(jstype) jstring "String"
+%typemap(jstype) jobject "java.lang.Object"
+%typemap(jstype) jbooleanArray "boolean[]"
+%typemap(jstype) jcharArray "char[]"
+%typemap(jstype) jbyteArray "byte[]"
+%typemap(jstype) jshortArray "short[]"
+%typemap(jstype) jintArray "int[]"
+%typemap(jstype) jlongArray "long[]"
+%typemap(jstype) jfloatArray "float[]"
+%typemap(jstype) jdoubleArray "double[]"
+%typemap(jstype) jobjectArray "java.lang.Object[]"
+
+/* Non primitive types */
+%typemap(jni) SWIGTYPE "jlong"
+%typemap(jtype) SWIGTYPE "long"
+%typemap(jstype) SWIGTYPE "$&javaclassname"
+%typemap(jboxtype) SWIGTYPE "$typemap(jstype, $1_type)"
+
+%typemap(jni) SWIGTYPE [] "jlong"
+%typemap(jtype) SWIGTYPE [] "long"
+%typemap(jstype) SWIGTYPE [] "$javaclassname"
+
+%typemap(jni) SWIGTYPE * "jlong"
+%typemap(jtype) SWIGTYPE * "long"
+%typemap(jstype) SWIGTYPE * "$javaclassname"
+
+%typemap(jni) SWIGTYPE & "jlong"
+%typemap(jtype) SWIGTYPE & "long"
+%typemap(jstype) SWIGTYPE & "$javaclassname"
+
+%typemap(jni) SWIGTYPE && "jlong"
+%typemap(jtype) SWIGTYPE && "long"
+%typemap(jstype) SWIGTYPE && "$javaclassname"
+
+/* pointer to a class member */
+%typemap(jni) SWIGTYPE (CLASS::*) "jstring"
+%typemap(jtype) SWIGTYPE (CLASS::*) "String"
+%typemap(jstype) SWIGTYPE (CLASS::*) "$javaclassname"
+
+/* The following are the in, out, freearg, argout typemaps. These are the JNI code generating typemaps for converting from Java to C and visa versa. */
+
+/* primitive types */
+%typemap(in) bool
+%{ $1 = $input ? true : false; %}
+
+%typemap(directorout) bool
+%{ $result = $input ? true : false; %}
+
+%typemap(javadirectorin) bool "$jniinput"
+%typemap(javadirectorout) bool "$javacall"
+
+%typemap(in) char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ float,
+ double
+%{ $1 = ($1_ltype)$input; %}
+
+%typemap(directorout) char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ float,
+ double
+%{ $result = ($1_ltype)$input; %}
+
+%typemap(directorin, descriptor="Z") bool "$input = (jboolean) $1;"
+%typemap(directorin, descriptor="C") char "$input = (jint) $1;"
+%typemap(directorin, descriptor="B") signed char "$input = (jbyte) $1;"
+%typemap(directorin, descriptor="S") unsigned char "$input = (jshort) $1;"
+%typemap(directorin, descriptor="S") short "$input = (jshort) $1;"
+%typemap(directorin, descriptor="I") unsigned short "$input = (jint) $1;"
+%typemap(directorin, descriptor="I") int "$input = (jint) $1;"
+%typemap(directorin, descriptor="J") unsigned int "$input = (jlong) $1;"
+%typemap(directorin, descriptor="I") long "$input = (jint) $1;"
+%typemap(directorin, descriptor="J") unsigned long "$input = (jlong) $1;"
+%typemap(directorin, descriptor="J") long long "$input = (jlong) $1;"
+%typemap(directorin, descriptor="F") float "$input = (jfloat) $1;"
+%typemap(directorin, descriptor="D") double "$input = (jdouble) $1;"
+
+%typemap(javadirectorin) char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ float,
+ double
+ "$jniinput"
+
+%typemap(javadirectorout) char,
+ signed char,
+ unsigned char,
+ short,
+ unsigned short,
+ int,
+ unsigned int,
+ long,
+ unsigned long,
+ long long,
+ float,
+ double
+ "$javacall"
+
+%typemap(out) bool %{ $result = (jboolean)$1; %}
+%typemap(out) char %{ $result = (jchar)$1; %}
+%typemap(out) signed char %{ $result = (jbyte)$1; %}
+%typemap(out) unsigned char %{ $result = (jshort)$1; %}
+%typemap(out) short %{ $result = (jshort)$1; %}
+%typemap(out) unsigned short %{ $result = (jint)$1; %}
+%typemap(out) int %{ $result = (jint)$1; %}
+%typemap(out) unsigned int %{ $result = (jlong)$1; %}
+%typemap(out) long %{ $result = (jint)$1; %}
+%typemap(out) unsigned long %{ $result = (jlong)$1; %}
+%typemap(out) long long %{ $result = (jlong)$1; %}
+%typemap(out) float %{ $result = (jfloat)$1; %}
+%typemap(out) double %{ $result = (jdouble)$1; %}
+
+/* unsigned long long */
+/* Convert from BigInteger using the toByteArray member function */
+%typemap(in) unsigned long long {
+ jclass clazz;
+ jmethodID mid;
+ jbyteArray ba;
+ jbyte* bae;
+ jsize sz;
+ int i;
+
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "BigInteger null");
+ return $null;
+ }
+ clazz = JCALL1(GetObjectClass, jenv, $input);
+ mid = JCALL3(GetMethodID, jenv, clazz, "toByteArray", "()[B");
+ ba = (jbyteArray)JCALL2(CallObjectMethod, jenv, $input, mid);
+ bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ sz = JCALL1(GetArrayLength, jenv, ba);
+ $1 = 0;
+ if (sz > 0) {
+ $1 = ($1_type)(signed char)bae[0];
+ for(i=1; i<sz; i++) {
+ $1 = ($1 << 8) | ($1_type)(unsigned char)bae[i];
+ }
+ }
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+}
+
+%typemap(directorout) unsigned long long {
+ jclass clazz;
+ jmethodID mid;
+ jbyteArray ba;
+ jbyte* bae;
+ jsize sz;
+ int i;
+
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "BigInteger null");
+ return $null;
+ }
+ clazz = JCALL1(GetObjectClass, jenv, $input);
+ mid = JCALL3(GetMethodID, jenv, clazz, "toByteArray", "()[B");
+ ba = (jbyteArray)JCALL2(CallObjectMethod, jenv, $input, mid);
+ bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ sz = JCALL1(GetArrayLength, jenv, ba);
+ $result = 0;
+ if (sz > 0) {
+ $result = ($1_type)(signed char)bae[0];
+ for(i=1; i<sz; i++) {
+ $result = ($result << 8) | ($1_type)(unsigned char)bae[i];
+ }
+ }
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+}
+
+
+/* Convert to BigInteger - byte array holds number in 2's complement big endian format */
+%typemap(out) unsigned long long {
+ jbyteArray ba = JCALL1(NewByteArray, jenv, 9);
+ jbyte* bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ jclass clazz = JCALL1(FindClass, jenv, "java/math/BigInteger");
+ jmethodID mid = JCALL3(GetMethodID, jenv, clazz, "<init>", "([B)V");
+ jobject bigint;
+ int i;
+
+ bae[0] = 0;
+ for(i=1; i<9; i++ ) {
+ bae[i] = (jbyte)($1>>8*(8-i));
+ }
+
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+ bigint = JCALL3(NewObject, jenv, clazz, mid, ba);
+ JCALL1(DeleteLocalRef, jenv, ba);
+ $result = bigint;
+}
+
+/* Convert to BigInteger (see out typemap) */
+%typemap(directorin, descriptor="Ljava/math/BigInteger;", noblock=1) unsigned long long, const unsigned long long & {
+{
+ jbyteArray ba = JCALL1(NewByteArray, jenv, 9);
+ jbyte* bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ jclass clazz = JCALL1(FindClass, jenv, "java/math/BigInteger");
+ jmethodID mid = JCALL3(GetMethodID, jenv, clazz, "<init>", "([B)V");
+ jobject bigint;
+ int swig_i;
+
+ bae[0] = 0;
+ for(swig_i=1; swig_i<9; swig_i++ ) {
+ bae[swig_i] = (jbyte)($1>>8*(8-swig_i));
+ }
+
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+ bigint = JCALL3(NewObject, jenv, clazz, mid, ba);
+ JCALL1(DeleteLocalRef, jenv, ba);
+ $input = bigint;
+}
+Swig::LocalRefGuard $1_refguard(jenv, $input); }
+
+%typemap(javadirectorin) unsigned long long "$jniinput"
+%typemap(javadirectorout) unsigned long long "$javacall"
+
+/* char * - treat as String */
+%typemap(in, noblock=1) char * {
+ $1 = 0;
+ if ($input) {
+ $1 = ($1_ltype)JCALL2(GetStringUTFChars, jenv, $input, 0);
+ if (!$1) return $null;
+ }
+}
+
+%typemap(directorout, noblock=1, warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) char * {
+ $1 = 0;
+ if ($input) {
+ $result = ($1_ltype)JCALL2(GetStringUTFChars, jenv, $input, 0);
+ if (!$result) return $null;
+ }
+}
+
+%typemap(directorin, descriptor="Ljava/lang/String;", noblock=1) char * {
+ $input = 0;
+ if ($1) {
+ $input = JCALL1(NewStringUTF, jenv, (const char *)$1);
+ if (!$input) return $null;
+ }
+ Swig::LocalRefGuard $1_refguard(jenv, $input);
+}
+
+%typemap(freearg, noblock=1) char * { if ($1) JCALL2(ReleaseStringUTFChars, jenv, $input, (const char *)$1); }
+%typemap(out, noblock=1) char * { if ($1) $result = JCALL1(NewStringUTF, jenv, (const char *)$1); }
+%typemap(javadirectorin) char * "$jniinput"
+%typemap(javadirectorout) char * "$javacall"
+
+/* char *& - treat as String */
+%typemap(in, noblock=1) char *& ($*1_ltype temp = 0) {
+ $1 = 0;
+ if ($input) {
+ temp = ($*1_ltype)JCALL2(GetStringUTFChars, jenv, $input, 0);
+ if (!temp) return $null;
+ }
+ $1 = &temp;
+}
+%typemap(freearg, noblock=1) char *& { if ($1 && *$1) JCALL2(ReleaseStringUTFChars, jenv, $input, (const char *)*$1); }
+%typemap(out, noblock=1) char *& { if (*$1) $result = JCALL1(NewStringUTF, jenv, (const char *)*$1); }
+
+%typemap(out) void ""
+%typemap(javadirectorin) void "$jniinput"
+%typemap(javadirectorout) void "$javacall"
+%typemap(directorin, descriptor="V") void ""
+
+/* primitive types by reference */
+%typemap(in) const bool & ($*1_ltype temp)
+%{ temp = $input ? true : false;
+ $1 = &temp; %}
+
+%typemap(directorout,warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) const bool &
+%{ static $*1_ltype temp;
+ temp = $input ? true : false;
+ $result = &temp; %}
+
+%typemap(javadirectorin) const bool & "$jniinput"
+%typemap(javadirectorout) const bool & "$javacall"
+
+%typemap(in) const char & ($*1_ltype temp),
+ const signed char & ($*1_ltype temp),
+ const unsigned char & ($*1_ltype temp),
+ const short & ($*1_ltype temp),
+ const unsigned short & ($*1_ltype temp),
+ const int & ($*1_ltype temp),
+ const unsigned int & ($*1_ltype temp),
+ const long & ($*1_ltype temp),
+ const unsigned long & ($*1_ltype temp),
+ const long long & ($*1_ltype temp),
+ const float & ($*1_ltype temp),
+ const double & ($*1_ltype temp)
+%{ temp = ($*1_ltype)$input;
+ $1 = &temp; %}
+
+%typemap(directorout,warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) const char &,
+ const signed char &,
+ const unsigned char &,
+ const short &,
+ const unsigned short &,
+ const int &,
+ const unsigned int &,
+ const long &,
+ const unsigned long &,
+ const long long &,
+ const float &,
+ const double &
+%{ static $*1_ltype temp;
+ temp = ($*1_ltype)$input;
+ $result = &temp; %}
+
+%typemap(directorin, descriptor="Z") const bool & "$input = (jboolean)$1;"
+%typemap(directorin, descriptor="C") const char & "$input = (jchar)$1;"
+%typemap(directorin, descriptor="B") const signed char & "$input = (jbyte)$1;"
+%typemap(directorin, descriptor="S") const unsigned char & "$input = (jshort)$1;"
+%typemap(directorin, descriptor="S") const short & "$input = (jshort)$1;"
+%typemap(directorin, descriptor="I") const unsigned short & "$input = (jint)$1;"
+%typemap(directorin, descriptor="I") const int & "$input = (jint)$1;"
+%typemap(directorin, descriptor="J") const unsigned int & "$input = (jlong)$1;"
+%typemap(directorin, descriptor="I") const long & "$input = (jint)$1;"
+%typemap(directorin, descriptor="J") const unsigned long & "$input = (jlong)$1;"
+%typemap(directorin, descriptor="J") const long long & "$input = (jlong)$1;"
+%typemap(directorin, descriptor="F") const float & "$input = (jfloat)$1;"
+%typemap(directorin, descriptor="D") const double & "$input = (jdouble)$1;"
+
+%typemap(javadirectorin) const char & ($*1_ltype temp),
+ const signed char & ($*1_ltype temp),
+ const unsigned char & ($*1_ltype temp),
+ const short & ($*1_ltype temp),
+ const unsigned short & ($*1_ltype temp),
+ const int & ($*1_ltype temp),
+ const unsigned int & ($*1_ltype temp),
+ const long & ($*1_ltype temp),
+ const unsigned long & ($*1_ltype temp),
+ const long long & ($*1_ltype temp),
+ const float & ($*1_ltype temp),
+ const double & ($*1_ltype temp)
+ "$jniinput"
+
+%typemap(javadirectorout) const char & ($*1_ltype temp),
+ const signed char & ($*1_ltype temp),
+ const unsigned char & ($*1_ltype temp),
+ const short & ($*1_ltype temp),
+ const unsigned short & ($*1_ltype temp),
+ const int & ($*1_ltype temp),
+ const unsigned int & ($*1_ltype temp),
+ const long & ($*1_ltype temp),
+ const unsigned long & ($*1_ltype temp),
+ const long long & ($*1_ltype temp),
+ const float & ($*1_ltype temp),
+ const double & ($*1_ltype temp)
+ "$javacall"
+
+
+%typemap(out) const bool & %{ $result = (jboolean)*$1; %}
+%typemap(out) const char & %{ $result = (jchar)*$1; %}
+%typemap(out) const signed char & %{ $result = (jbyte)*$1; %}
+%typemap(out) const unsigned char & %{ $result = (jshort)*$1; %}
+%typemap(out) const short & %{ $result = (jshort)*$1; %}
+%typemap(out) const unsigned short & %{ $result = (jint)*$1; %}
+%typemap(out) const int & %{ $result = (jint)*$1; %}
+%typemap(out) const unsigned int & %{ $result = (jlong)*$1; %}
+%typemap(out) const long & %{ $result = (jint)*$1; %}
+%typemap(out) const unsigned long & %{ $result = (jlong)*$1; %}
+%typemap(out) const long long & %{ $result = (jlong)*$1; %}
+%typemap(out) const float & %{ $result = (jfloat)*$1; %}
+%typemap(out) const double & %{ $result = (jdouble)*$1; %}
+
+/* const unsigned long long & */
+/* Similar to unsigned long long */
+%typemap(in) const unsigned long long & ($*1_ltype temp) {
+ jclass clazz;
+ jmethodID mid;
+ jbyteArray ba;
+ jbyte* bae;
+ jsize sz;
+ int i;
+
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "BigInteger null");
+ return $null;
+ }
+ clazz = JCALL1(GetObjectClass, jenv, $input);
+ mid = JCALL3(GetMethodID, jenv, clazz, "toByteArray", "()[B");
+ ba = (jbyteArray)JCALL2(CallObjectMethod, jenv, $input, mid);
+ bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ sz = JCALL1(GetArrayLength, jenv, ba);
+ $1 = &temp;
+ temp = 0;
+ if (sz > 0) {
+ temp = ($*1_ltype)(signed char)bae[0];
+ for(i=1; i<sz; i++) {
+ temp = (temp << 8) | ($*1_ltype)(unsigned char)bae[i];
+ }
+ }
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+}
+
+%typemap(directorout,warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) const unsigned long long & {
+ static $*1_ltype temp;
+ jclass clazz;
+ jmethodID mid;
+ jbyteArray ba;
+ jbyte* bae;
+ jsize sz;
+ int i;
+
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "BigInteger null");
+ return $null;
+ }
+ clazz = JCALL1(GetObjectClass, jenv, $input);
+ mid = JCALL3(GetMethodID, jenv, clazz, "toByteArray", "()[B");
+ ba = (jbyteArray)JCALL2(CallObjectMethod, jenv, $input, mid);
+ bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ sz = JCALL1(GetArrayLength, jenv, ba);
+ $result = &temp;
+ temp = 0;
+ if (sz > 0) {
+ temp = ($*1_ltype)(signed char)bae[0];
+ for(i=1; i<sz; i++) {
+ temp = (temp << 8) | ($*1_ltype)(unsigned char)bae[i];
+ }
+ }
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+}
+
+%typemap(out) const unsigned long long & {
+ jbyteArray ba = JCALL1(NewByteArray, jenv, 9);
+ jbyte* bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ jclass clazz = JCALL1(FindClass, jenv, "java/math/BigInteger");
+ jmethodID mid = JCALL3(GetMethodID, jenv, clazz, "<init>", "([B)V");
+ jobject bigint;
+ int i;
+
+ bae[0] = 0;
+ for(i=1; i<9; i++ ) {
+ bae[i] = (jbyte)(*$1>>8*(8-i));
+ }
+
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+ bigint = JCALL3(NewObject, jenv, clazz, mid, ba);
+ JCALL1(DeleteLocalRef, jenv, ba);
+ $result = bigint;
+}
+
+%typemap(javadirectorin) const unsigned long long & "$jniinput"
+%typemap(javadirectorout) const unsigned long long & "$javacall"
+
+/* Default handling. Object passed by value. Convert to a pointer */
+%typemap(in) SWIGTYPE ($&1_type argp)
+%{ argp = *($&1_ltype*)&$input;
+ if (!argp) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "Attempt to dereference null $1_type");
+ return $null;
+ }
+ $1 = *argp; %}
+
+%typemap(directorout) SWIGTYPE ($&1_type argp)
+%{ argp = *($&1_ltype*)&$input;
+ if (!argp) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "Unexpected null return for type $1_type");
+ return $null;
+ }
+ $result = *argp; %}
+
+%typemap(out) SWIGTYPE
+#ifdef __cplusplus
+%{ *($&1_ltype*)&$result = new $1_ltype($1); %}
+#else
+{
+ $&1_ltype $1ptr = ($&1_ltype) malloc(sizeof($1_ltype));
+ memmove($1ptr, &$1, sizeof($1_type));
+ *($&1_ltype*)&$result = $1ptr;
+}
+#endif
+
+%typemap(directorin,descriptor="L$packagepath/$&javaclassname;") SWIGTYPE
+%{ $input = 0;
+ *(($&1_ltype*)&$input) = new $1_ltype(SWIG_STD_MOVE($1)); %}
+%typemap(javadirectorin) SWIGTYPE "new $&javaclassname($jniinput, true)"
+%typemap(javadirectorout) SWIGTYPE "$&javaclassname.getCPtr($javacall)"
+
+/* Generic pointers and references */
+%typemap(in) SWIGTYPE * %{ $1 = *($&1_ltype)&$input; %}
+%typemap(in, fragment="SWIG_UnPackData") SWIGTYPE (CLASS::*) {
+ const char *temp = 0;
+ if ($input) {
+ temp = JCALL2(GetStringUTFChars, jenv, $input, 0);
+ if (!temp) return $null;
+ }
+ SWIG_UnpackData(temp, (void *)&$1, sizeof($1));
+}
+%typemap(in) SWIGTYPE & %{ $1 = *($&1_ltype)&$input;
+ if (!$1) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "$1_type is null");
+ return $null;
+ } %}
+%typemap(in, fragment="<memory>") SWIGTYPE && (std::unique_ptr<$*1_ltype> rvrdeleter) %{ $1 = *($&1_ltype)&$input;
+ if (!$1) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "$1_type is null");
+ return $null;
+ }
+ rvrdeleter.reset($1); %}
+%typemap(out) SWIGTYPE *
+%{ *($&1_ltype)&$result = $1; %}
+%typemap(out, fragment="SWIG_PackData", noblock=1) SWIGTYPE (CLASS::*) {
+ char buf[128];
+ char *data = SWIG_PackData(buf, (void *)&$1, sizeof($1));
+ *data = '\0';
+ $result = JCALL1(NewStringUTF, jenv, buf);
+}
+%typemap(out) SWIGTYPE &
+%{ *($&1_ltype)&$result = $1; %}
+%typemap(out) SWIGTYPE &&
+%{ *($&1_ltype)&$result = $1; %}
+
+%typemap(directorout, warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) SWIGTYPE *
+%{ $result = *($&1_ltype)&$input; %}
+%typemap(directorout, warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) SWIGTYPE (CLASS::*)
+%{ $result = *($&1_ltype)&$input; %}
+
+%typemap(directorin,descriptor="L$packagepath/$javaclassname;") SWIGTYPE *
+%{ *(($&1_ltype)&$input) = ($1_ltype) $1; %}
+%typemap(directorin,descriptor="L$packagepath/$javaclassname;") SWIGTYPE (CLASS::*)
+%{ *(($&1_ltype)&$input) = ($1_ltype) $1; %}
+
+%typemap(directorout, warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) SWIGTYPE &
+%{ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "Unexpected null return for type $1_type");
+ return $null;
+ }
+ $result = *($&1_ltype)&$input; %}
+%typemap(directorout, warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) SWIGTYPE &&
+%{ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "Unexpected null return for type $1_type");
+ return $null;
+ }
+ $result = *($&1_ltype)&$input; %}
+%typemap(directorin,descriptor="L$packagepath/$javaclassname;") SWIGTYPE &
+%{ *($&1_ltype)&$input = ($1_ltype) &$1; %}
+%typemap(directorin,descriptor="L$packagepath/$javaclassname;") SWIGTYPE &&
+%{ *($&1_ltype)&$input = ($1_ltype) &$1; %}
+
+%typemap(javadirectorin) SWIGTYPE *, SWIGTYPE (CLASS::*) "($jniinput == 0) ? null : new $javaclassname($jniinput, false)"
+%typemap(javadirectorin) SWIGTYPE & "new $javaclassname($jniinput, false)"
+%typemap(javadirectorin) SWIGTYPE && "new $javaclassname($jniinput, false)"
+%typemap(javadirectorout) SWIGTYPE *, SWIGTYPE (CLASS::*), SWIGTYPE &, SWIGTYPE && "$javaclassname.getCPtr($javacall)"
+
+/* Default array handling */
+%typemap(in) SWIGTYPE [] %{ $1 = *($&1_ltype)&$input; %}
+%typemap(out) SWIGTYPE [] %{ *($&1_ltype)&$result = $1; %}
+%typemap(freearg) SWIGTYPE [ANY], SWIGTYPE [] ""
+
+/* char arrays - treat as String */
+%typemap(in, noblock=1) char[ANY], char[] {
+ $1 = 0;
+ if ($input) {
+ $1 = ($1_ltype)JCALL2(GetStringUTFChars, jenv, $input, 0);
+ if (!$1) return $null;
+ }
+}
+
+%typemap(directorout, noblock=1) char[ANY], char[] {
+ $1 = 0;
+ if ($input) {
+ $result = ($1_ltype)JCALL2(GetStringUTFChars, jenv, $input, 0);
+ if (!$result) return $null;
+ }
+}
+
+%typemap(directorin, descriptor="Ljava/lang/String;", noblock=1) char[ANY], char[] {
+ $input = 0;
+ if ($1) {
+ $input = JCALL1(NewStringUTF, jenv, (const char *)$1);
+ if (!$input) return $null;
+ }
+ Swig::LocalRefGuard $1_refguard(jenv, $input);
+}
+
+%typemap(argout) char[ANY], char[] ""
+%typemap(freearg, noblock=1) char[ANY], char[] { if ($1) JCALL2(ReleaseStringUTFChars, jenv, $input, (const char *)$1); }
+%typemap(out, noblock=1) char[ANY], char[] { if ($1) $result = JCALL1(NewStringUTF, jenv, (const char *)$1); }
+%typemap(javadirectorin) char[ANY], char[] "$jniinput"
+%typemap(javadirectorout) char[ANY], char[] "$javacall"
+
+/* JNI types */
+%typemap(in) jboolean,
+ jchar,
+ jbyte,
+ jshort,
+ jint,
+ jlong,
+ jfloat,
+ jdouble,
+ jstring,
+ jobject,
+ jbooleanArray,
+ jcharArray,
+ jbyteArray,
+ jshortArray,
+ jintArray,
+ jlongArray,
+ jfloatArray,
+ jdoubleArray,
+ jobjectArray
+%{ $1 = $input; %}
+
+%typemap(directorout) jboolean,
+ jchar,
+ jbyte,
+ jshort,
+ jint,
+ jlong,
+ jfloat,
+ jdouble,
+ jstring,
+ jobject,
+ jbooleanArray,
+ jcharArray,
+ jbyteArray,
+ jshortArray,
+ jintArray,
+ jlongArray,
+ jfloatArray,
+ jdoubleArray,
+ jobjectArray
+%{ $result = $input; %}
+
+%typemap(out) jboolean,
+ jchar,
+ jbyte,
+ jshort,
+ jint,
+ jlong,
+ jfloat,
+ jdouble,
+ jstring,
+ jobject,
+ jbooleanArray,
+ jcharArray,
+ jbyteArray,
+ jshortArray,
+ jintArray,
+ jlongArray,
+ jfloatArray,
+ jdoubleArray,
+ jobjectArray
+%{ $result = $1; %}
+
+%typemap(directorin,descriptor="Z") jboolean "$input = $1;"
+%typemap(directorin,descriptor="C") jchar "$input = $1;"
+%typemap(directorin,descriptor="B") jbyte "$input = $1;"
+%typemap(directorin,descriptor="S") jshort "$input = $1;"
+%typemap(directorin,descriptor="I") jint "$input = $1;"
+%typemap(directorin,descriptor="J") jlong "$input = $1;"
+%typemap(directorin,descriptor="F") jfloat "$input = $1;"
+%typemap(directorin,descriptor="D") jdouble "$input = $1;"
+%typemap(directorin,descriptor="Ljava/lang/String;") jstring "$input = $1;"
+%typemap(directorin,descriptor="Ljava/lang/Object;",nouse="1") jobject "$input = $1;"
+%typemap(directorin,descriptor="[Z") jbooleanArray "$input = $1;"
+%typemap(directorin,descriptor="[C") jcharArray "$input = $1;"
+%typemap(directorin,descriptor="[B") jbyteArray "$input = $1;"
+%typemap(directorin,descriptor="[S") jshortArray "$input = $1;"
+%typemap(directorin,descriptor="[I") jintArray "$input = $1;"
+%typemap(directorin,descriptor="[J") jlongArray "$input = $1;"
+%typemap(directorin,descriptor="[F") jfloatArray "$input = $1;"
+%typemap(directorin,descriptor="[D") jdoubleArray "$input = $1;"
+%typemap(directorin,descriptor="[Ljava/lang/Object;",nouse="1") jobjectArray "$input = $1;"
+
+%typemap(javadirectorin) jboolean,
+ jchar,
+ jbyte,
+ jshort,
+ jint,
+ jlong,
+ jfloat,
+ jdouble,
+ jstring,
+ jobject,
+ jbooleanArray,
+ jcharArray,
+ jbyteArray,
+ jshortArray,
+ jintArray,
+ jlongArray,
+ jfloatArray,
+ jdoubleArray,
+ jobjectArray
+ "$jniinput"
+
+%typemap(javadirectorout) jboolean,
+ jchar,
+ jbyte,
+ jshort,
+ jint,
+ jlong,
+ jfloat,
+ jdouble,
+ jstring,
+ jobject,
+ jbooleanArray,
+ jcharArray,
+ jbyteArray,
+ jshortArray,
+ jintArray,
+ jlongArray,
+ jfloatArray,
+ jdoubleArray,
+ jobjectArray
+ "$javacall"
+
+/* Typecheck typemaps - The purpose of these is merely to issue a warning for overloaded C++ functions
+ * that cannot be overloaded in Java as more than one C++ type maps to a single Java type */
+
+%typecheck(SWIG_TYPECHECK_BOOL) /* Java boolean */
+ jboolean,
+ bool,
+ const bool &
+ ""
+
+%typecheck(SWIG_TYPECHECK_CHAR) /* Java char */
+ jchar,
+ char,
+ const char &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT8) /* Java byte */
+ jbyte,
+ signed char,
+ const signed char &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT16) /* Java short */
+ jshort,
+ unsigned char,
+ short,
+ const unsigned char &,
+ const short &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT32) /* Java int */
+ jint,
+ unsigned short,
+ int,
+ long,
+ const unsigned short &,
+ const int &,
+ const long &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT64) /* Java long */
+ jlong,
+ unsigned int,
+ unsigned long,
+ long long,
+ const unsigned int &,
+ const unsigned long &,
+ const long long &
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT128) /* Java BigInteger */
+ unsigned long long,
+ const unsigned long long &
+ ""
+
+%typecheck(SWIG_TYPECHECK_FLOAT) /* Java float */
+ jfloat,
+ float,
+ const float &
+ ""
+
+%typecheck(SWIG_TYPECHECK_DOUBLE) /* Java double */
+ jdouble,
+ double,
+ const double &
+ ""
+
+%typecheck(SWIG_TYPECHECK_STRING) /* Java String */
+ jstring,
+ char *,
+ char *&,
+ char[ANY],
+ char []
+ ""
+
+%typecheck(SWIG_TYPECHECK_BOOL_ARRAY) /* Java boolean[] */
+ jbooleanArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_CHAR_ARRAY) /* Java char[] */
+ jcharArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT8_ARRAY) /* Java byte[] */
+ jbyteArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT16_ARRAY) /* Java short[] */
+ jshortArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT32_ARRAY) /* Java int[] */
+ jintArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_INT64_ARRAY) /* Java long[] */
+ jlongArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_FLOAT_ARRAY) /* Java float[] */
+ jfloatArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY) /* Java double[] */
+ jdoubleArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_OBJECT_ARRAY) /* Java jobject[] */
+ jobjectArray
+ ""
+
+%typecheck(SWIG_TYPECHECK_POINTER) /* Default */
+ SWIGTYPE,
+ SWIGTYPE *,
+ SWIGTYPE &,
+ SWIGTYPE &&,
+ SWIGTYPE *const&,
+ SWIGTYPE [],
+ SWIGTYPE (CLASS::*)
+ ""
+
+
+/* Exception handling */
+
+%typemap(throws) int,
+ long,
+ short,
+ unsigned int,
+ unsigned long,
+ unsigned short
+%{ char error_msg[256];
+ sprintf(error_msg, "C++ $1_type exception thrown, value: %d", $1);
+ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, error_msg);
+ return $null; %}
+
+%typemap(throws) SWIGTYPE, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE *, SWIGTYPE [], SWIGTYPE [ANY]
+%{ (void)$1;
+ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, "C++ $1_type exception thrown");
+ return $null; %}
+
+%typemap(throws) char *
+%{ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1);
+ return $null; %}
+
+/* For methods to raise/throw the original Java exception thrown in a director method */
+%typemap(throws) Swig::DirectorException
+%{ $1.throwException(jenv);
+ return $null; %}
+
+/* Java to C++ DirectorException should already be handled. Suppress warning and do nothing in the
+ event a user specifies a global: %catches(Swig::DirectorException); */
+%typemap(directorthrows) Swig::DirectorException ""
+
+/* Typemaps for code generation in proxy classes and Java type wrapper classes */
+
+/* The javain typemap is used for converting function parameter types from the type
+ * used in the proxy, module or type wrapper class to the type used in the JNI class. */
+%typemap(javain) bool, const bool &,
+ char, const char &,
+ signed char, const signed char &,
+ unsigned char, const unsigned char &,
+ short, const short &,
+ unsigned short, const unsigned short &,
+ int, const int &,
+ unsigned int, const unsigned int &,
+ long, const long &,
+ unsigned long, const unsigned long &,
+ long long, const long long &,
+ unsigned long long, const unsigned long long &,
+ float, const float &,
+ double, const double &
+ "$javainput"
+%typemap(javain) char *, char *&, char[ANY], char[] "$javainput"
+%typemap(javain) jboolean,
+ jchar,
+ jbyte,
+ jshort,
+ jint,
+ jlong,
+ jfloat,
+ jdouble,
+ jstring,
+ jobject,
+ jbooleanArray,
+ jcharArray,
+ jbyteArray,
+ jshortArray,
+ jintArray,
+ jlongArray,
+ jfloatArray,
+ jdoubleArray,
+ jobjectArray
+ "$javainput"
+%typemap(javain) SWIGTYPE "$&javaclassname.getCPtr($javainput)"
+%typemap(javain) SWIGTYPE *, SWIGTYPE &, SWIGTYPE [] "$javaclassname.getCPtr($javainput)"
+%typemap(javain) SWIGTYPE && "$javaclassname.swigRelease($javainput)"
+%typemap(javain) SWIGTYPE (CLASS::*) "$javaclassname.getCMemberPtr($javainput)"
+
+/* The javaout typemap is used for converting function return types from the return type
+ * used in the JNI class to the type returned by the proxy, module or type wrapper class. */
+%typemap(javaout) bool, const bool &,
+ char, const char &,
+ signed char, const signed char &,
+ unsigned char, const unsigned char &,
+ short, const short &,
+ unsigned short, const unsigned short &,
+ int, const int &,
+ unsigned int, const unsigned int &,
+ long, const long &,
+ unsigned long, const unsigned long &,
+ long long, const long long &,
+ unsigned long long, const unsigned long long &,
+ float, const float &,
+ double, const double & {
+ return $jnicall;
+ }
+%typemap(javaout) char *, char *&, char[ANY], char[] {
+ return $jnicall;
+ }
+%typemap(javaout) jboolean,
+ jchar,
+ jbyte,
+ jshort,
+ jint,
+ jlong,
+ jfloat,
+ jdouble,
+ jstring,
+ jobject,
+ jbooleanArray,
+ jcharArray,
+ jbyteArray,
+ jshortArray,
+ jintArray,
+ jlongArray,
+ jfloatArray,
+ jdoubleArray,
+ jobjectArray {
+ return $jnicall;
+ }
+%typemap(javaout) void {
+ $jnicall;
+ }
+%typemap(javaout) SWIGTYPE {
+ return new $&javaclassname($jnicall, true);
+ }
+%typemap(javaout) SWIGTYPE & {
+ return new $javaclassname($jnicall, $owner);
+ }
+%typemap(javaout) SWIGTYPE && {
+ return new $javaclassname($jnicall, $owner);
+ }
+%typemap(javaout) SWIGTYPE *, SWIGTYPE [] {
+ long cPtr = $jnicall;
+ return (cPtr == 0) ? null : new $javaclassname(cPtr, $owner);
+ }
+%typemap(javaout) SWIGTYPE (CLASS::*) {
+ String cMemberPtr = $jnicall;
+ return (cMemberPtr == null) ? null : new $javaclassname(cMemberPtr, $owner);
+ }
+
+/* Pointer reference typemaps */
+%typemap(jni) SWIGTYPE *const& "jlong"
+%typemap(jtype) SWIGTYPE *const& "long"
+%typemap(jstype) SWIGTYPE *const& "$*javaclassname"
+%typemap(javain) SWIGTYPE *const& "$*javaclassname.getCPtr($javainput)"
+%typemap(javaout) SWIGTYPE *const& {
+ long cPtr = $jnicall;
+ return (cPtr == 0) ? null : new $*javaclassname(cPtr, $owner);
+ }
+%typemap(in) SWIGTYPE *const& ($*1_ltype temp = 0)
+%{ temp = *($1_ltype)&$input;
+ $1 = ($1_ltype)&temp; %}
+%typemap(out) SWIGTYPE *const&
+%{ *($1_ltype)&$result = *$1; %}
+%typemap(directorin,descriptor="L$packagepath/$*javaclassname;") SWIGTYPE *const&
+%{ *(($1_ltype)&$input) = ($*1_ltype) $1; %}
+%typemap(directorout, warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) SWIGTYPE *const&
+%{ static $*1_ltype swig_temp;
+ swig_temp = *($1_ltype)&$input;
+ $result = &swig_temp; %}
+%typemap(javadirectorin) SWIGTYPE *const& "($jniinput == 0) ? null : new $*javaclassname($jniinput, false)"
+%typemap(javadirectorout) SWIGTYPE *const& "$*javaclassname.getCPtr($javacall)"
+
+/* Typemaps used for the generation of proxy and type wrapper class code */
+%typemap(javabase) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [], SWIGTYPE (CLASS::*) ""
+%typemap(javaclassmodifiers) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [], SWIGTYPE (CLASS::*) "public class"
+%typemap(javacode) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [], SWIGTYPE (CLASS::*) ""
+%typemap(javaimports) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [], SWIGTYPE (CLASS::*) ""
+%typemap(javainterfaces) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [], SWIGTYPE (CLASS::*) ""
+%typemap(javainterfacemodifiers) SWIGTYPE, SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [], SWIGTYPE (CLASS::*) "public interface"
+
+/* javabody typemaps */
+
+%define SWIG_JAVABODY_METHODS(PTRCTOR_VISIBILITY, CPTR_VISIBILITY, TYPE...) SWIG_JAVABODY_PROXY(PTRCTOR_VISIBILITY, CPTR_VISIBILITY, TYPE) %enddef // legacy name
+
+%define SWIG_JAVABODY_PROXY(PTRCTOR_VISIBILITY, CPTR_VISIBILITY, TYPE...)
+// Base proxy classes
+%typemap(javabody) TYPE %{
+ private transient long swigCPtr;
+ protected transient boolean swigCMemOwn;
+
+ PTRCTOR_VISIBILITY $javaclassname(long cPtr, boolean cMemoryOwn) {
+ swigCMemOwn = cMemoryOwn;
+ swigCPtr = cPtr;
+ }
+
+ CPTR_VISIBILITY static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ CPTR_VISIBILITY static long swigRelease($javaclassname obj) {
+ long ptr = 0;
+ if (obj != null) {
+ if (!obj.swigCMemOwn)
+ throw new RuntimeException("Cannot release ownership as memory is not owned");
+ ptr = obj.swigCPtr;
+ obj.swigCMemOwn = false;
+ obj.delete();
+ }
+ return ptr;
+ }
+%}
+
+// Derived proxy classes
+%typemap(javabody_derived) TYPE %{
+ private transient long swigCPtr;
+
+ PTRCTOR_VISIBILITY $javaclassname(long cPtr, boolean cMemoryOwn) {
+ super($imclassname.$javaclazznameSWIGUpcast(cPtr), cMemoryOwn);
+ swigCPtr = cPtr;
+ }
+
+ CPTR_VISIBILITY static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ CPTR_VISIBILITY static long swigRelease($javaclassname obj) {
+ long ptr = 0;
+ if (obj != null) {
+ if (!obj.swigCMemOwn)
+ throw new RuntimeException("Cannot release ownership as memory is not owned");
+ ptr = obj.swigCPtr;
+ obj.swigCMemOwn = false;
+ obj.delete();
+ }
+ return ptr;
+ }
+%}
+%enddef
+
+%define SWIG_JAVABODY_TYPEWRAPPER(PTRCTOR_VISIBILITY, DEFAULTCTOR_VISIBILITY, CPTR_VISIBILITY, TYPE...)
+// Typewrapper classes
+%typemap(javabody) TYPE *, TYPE &, TYPE &&, TYPE [] %{
+ private transient long swigCPtr;
+
+ PTRCTOR_VISIBILITY $javaclassname(long cPtr, @SuppressWarnings("unused") boolean futureUse) {
+ swigCPtr = cPtr;
+ }
+
+ DEFAULTCTOR_VISIBILITY $javaclassname() {
+ swigCPtr = 0;
+ }
+
+ CPTR_VISIBILITY static long getCPtr($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+
+ CPTR_VISIBILITY static long swigRelease($javaclassname obj) {
+ return (obj == null) ? 0 : obj.swigCPtr;
+ }
+%}
+
+%typemap(javabody) TYPE (CLASS::*) %{
+ private transient String swigCMemberPtr;
+
+ PTRCTOR_VISIBILITY $javaclassname(String cMemberPtr, @SuppressWarnings("unused") boolean futureUse) {
+ swigCMemberPtr = cMemberPtr;
+ }
+
+ DEFAULTCTOR_VISIBILITY $javaclassname() {
+ swigCMemberPtr = null;
+ }
+
+ CPTR_VISIBILITY static String getCMemberPtr($javaclassname obj) {
+ return obj.swigCMemberPtr;
+ }
+%}
+%enddef
+
+/* Set the default javabody typemaps to use protected visibility.
+ Use the macros to change to public if using multiple modules. */
+SWIG_JAVABODY_PROXY(protected, protected, SWIGTYPE)
+SWIG_JAVABODY_TYPEWRAPPER(protected, protected, protected, SWIGTYPE)
+
+%typemap(javafinalize) SWIGTYPE %{
+ @SuppressWarnings("deprecation")
+ protected void finalize() {
+ delete();
+ }
+%}
+
+/*
+ * Java constructor typemaps:
+ *
+ * The javaconstruct typemap is inserted when a proxy class's constructor is generated.
+ * This typemap allows control over what code is executed in the constructor as
+ * well as specifying who owns the underlying C/C++ object. Normally, Java has
+ * ownership and the underlying C/C++ object is deallocated when the Java object
+ * is finalized (swigCMemOwn is true.) If swigCMemOwn is false, C/C++ is
+ * ultimately responsible for deallocating the underlying object's memory.
+ *
+ * The SWIG_PROXY_CONSTRUCTOR macro defines the javaconstruct typemap for a proxy
+ * class for a particular TYPENAME. OWNERSHIP is passed as the value of
+ * swigCMemOwn to the pointer constructor method. WEAKREF determines which kind
+ * of Java object reference will be used by the C++ director class (WeakGlobalRef
+ * vs. GlobalRef.)
+ *
+ * The SWIG_DIRECTOR_OWNED macro sets the ownership of director-based proxy
+ * classes and the weak reference flag to false, meaning that the underlying C++
+ * object will be reclaimed by C++.
+ */
+
+%define SWIG_PROXY_CONSTRUCTOR(OWNERSHIP, WEAKREF, TYPENAME...)
+%typemap(javaconstruct,directorconnect="\n $imclassname.$javaclazznamedirector_connect(this, swigCPtr, OWNERSHIP, WEAKREF);") TYPENAME {
+ this($imcall, OWNERSHIP);$directorconnect
+ }
+%enddef
+
+%define SWIG_DIRECTOR_OWNED(TYPENAME...)
+SWIG_PROXY_CONSTRUCTOR(true, false, TYPENAME)
+%enddef
+
+// Set the default for SWIGTYPE: Java owns the C/C++ object.
+SWIG_PROXY_CONSTRUCTOR(true, true, SWIGTYPE)
+
+%typemap(javadestruct, methodname="delete", methodmodifiers="public synchronized", parameters="") SWIGTYPE {
+ if (swigCPtr != 0) {
+ if (swigCMemOwn) {
+ swigCMemOwn = false;
+ $jnicall;
+ }
+ swigCPtr = 0;
+ }
+ }
+
+%typemap(javadestruct_derived, methodname="delete", methodmodifiers="public synchronized", parameters="") SWIGTYPE {
+ if (swigCPtr != 0) {
+ if (swigCMemOwn) {
+ swigCMemOwn = false;
+ $jnicall;
+ }
+ swigCPtr = 0;
+ }
+ super.delete();
+ }
+
+%typemap(directordisconnect, methodname="swigDirectorDisconnect") SWIGTYPE %{
+ protected void $methodname() {
+ swigCMemOwn = false;
+ $jnicall;
+ }
+%}
+
+%typemap(directorowner_release, methodname="swigReleaseOwnership") SWIGTYPE %{
+ public void $methodname() {
+ swigCMemOwn = false;
+ $jnicall;
+ }
+%}
+
+%typemap(directorowner_take, methodname="swigTakeOwnership") SWIGTYPE %{
+ public void $methodname() {
+ swigCMemOwn = true;
+ $jnicall;
+ }
+%}
+
+/* Java specific directives */
+#define %javaconst(flag) %feature("java:const","flag")
+#define %javaconstvalue(value) %feature("java:constvalue",value)
+#define %javaenum(wrapapproach) %feature("java:enum","wrapapproach")
+#define %javamethodmodifiers %feature("java:methodmodifiers")
+#define %javaexception(exceptionclasses) %feature("except",throws=exceptionclasses)
+#define %nojavaexception %feature("except","0",throws="")
+#define %clearjavaexception %feature("except","",throws="")
+#define %proxycode %insert("proxycode")
+
+%pragma(java) jniclassclassmodifiers="public class"
+%pragma(java) moduleclassmodifiers="public class"
+
+/* Some ANSI C typemaps */
+
+%apply unsigned long { size_t };
+%apply const unsigned long & { const size_t & };
+
+/* Array reference typemaps */
+%apply SWIGTYPE & { SWIGTYPE ((&)[ANY]) }
+%apply SWIGTYPE && { SWIGTYPE ((&&)[ANY]) }
+
+/* const pointers */
+%apply SWIGTYPE * { SWIGTYPE *const }
+%apply SWIGTYPE (CLASS::*) { SWIGTYPE (CLASS::*const) }
+%apply SWIGTYPE & { SWIGTYPE (CLASS::*const&) }
+
+/* String & length */
+%typemap(jni) (const char *STRING, size_t LENGTH) "jbyteArray"
+%typemap(jtype) (const char *STRING, size_t LENGTH) "byte[]"
+%typemap(jstype) (const char *STRING, size_t LENGTH) "byte[]"
+%typemap(javain) (const char *STRING, size_t LENGTH) "$javainput"
+%typemap(freearg) (const char *STRING, size_t LENGTH) ""
+%typemap(in) (const char *STRING, size_t LENGTH) {
+ if ($input) {
+ $1 = ($1_ltype) JCALL2(GetByteArrayElements, jenv, $input, 0);
+ $2 = ($2_type) JCALL1(GetArrayLength, jenv, $input);
+ } else {
+ $1 = 0;
+ $2 = 0;
+ }
+}
+%typemap(argout) (const char *STRING, size_t LENGTH) {
+ if ($input) JCALL3(ReleaseByteArrayElements, jenv, $input, (jbyte *)$1, JNI_ABORT);
+}
+%typemap(directorin, descriptor="[B", noblock=1) (const char *STRING, size_t LENGTH) {
+ $input = 0;
+ if ($1) {
+ $input = JCALL1(NewByteArray, jenv, (jsize)$2);
+ if (!$input) return $null;
+ JCALL4(SetByteArrayRegion, jenv, $input, 0, (jsize)$2, (jbyte *)$1);
+ }
+ Swig::LocalRefGuard $1_refguard(jenv, $input);
+}
+%typemap(javadirectorin, descriptor="[B") (const char *STRING, size_t LENGTH) "$jniinput"
+%apply (const char *STRING, size_t LENGTH) { (char *STRING, size_t LENGTH) }
+/* Enable write-back for non-const version */
+%typemap(argout) (char *STRING, size_t LENGTH) {
+ if ($input) JCALL3(ReleaseByteArrayElements, jenv, $input, (jbyte *)$1, 0);
+}
+%typemap(directorargout, noblock=1) (char *STRING, size_t LENGTH)
+{ if ($input && $1) JCALL4(GetByteArrayRegion, jenv, $input, 0, (jsize)$2, (jbyte *)$1); }
+%apply (char *STRING, size_t LENGTH) { (char *STRING, int LENGTH) }
+
+/* java keywords */
+%include <javakw.swg>
+
+// Default enum handling
+%include <enumtypesafe.swg>
+
diff --git a/contrib/tools/swig/Lib/java/javahead.swg b/contrib/tools/swig/Lib/java/javahead.swg
new file mode 100644
index 0000000000..758a037d17
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/javahead.swg
@@ -0,0 +1,91 @@
+/* -----------------------------------------------------------------------------
+ * javahead.swg
+ *
+ * Java support code
+ * ----------------------------------------------------------------------------- */
+
+
+/* JNI function calls require different calling conventions for C and C++. These JCALL macros are used so
+ * that the same typemaps can be used for generating code for both C and C++. The SWIG preprocessor can expand
+ * the macros thereby generating the correct calling convention. It is thus essential that all typemaps that
+ * use the macros are not within %{ %} brackets as they won't be run through the SWIG preprocessor. */
+#ifdef __cplusplus
+# define JCALL0(func, jenv) jenv->func()
+# define JCALL1(func, jenv, ar1) jenv->func(ar1)
+# define JCALL2(func, jenv, ar1, ar2) jenv->func(ar1, ar2)
+# define JCALL3(func, jenv, ar1, ar2, ar3) jenv->func(ar1, ar2, ar3)
+# define JCALL4(func, jenv, ar1, ar2, ar3, ar4) jenv->func(ar1, ar2, ar3, ar4)
+# define JCALL5(func, jenv, ar1, ar2, ar3, ar4, ar5) jenv->func(ar1, ar2, ar3, ar4, ar5)
+# define JCALL6(func, jenv, ar1, ar2, ar3, ar4, ar5, ar6) jenv->func(ar1, ar2, ar3, ar4, ar5, ar6)
+# define JCALL7(func, jenv, ar1, ar2, ar3, ar4, ar5, ar6, ar7) jenv->func(ar1, ar2, ar3, ar4, ar5, ar6, ar7)
+#else
+# define JCALL0(func, jenv) (*jenv)->func(jenv)
+# define JCALL1(func, jenv, ar1) (*jenv)->func(jenv, ar1)
+# define JCALL2(func, jenv, ar1, ar2) (*jenv)->func(jenv, ar1, ar2)
+# define JCALL3(func, jenv, ar1, ar2, ar3) (*jenv)->func(jenv, ar1, ar2, ar3)
+# define JCALL4(func, jenv, ar1, ar2, ar3, ar4) (*jenv)->func(jenv, ar1, ar2, ar3, ar4)
+# define JCALL5(func, jenv, ar1, ar2, ar3, ar4, ar5) (*jenv)->func(jenv, ar1, ar2, ar3, ar4, ar5)
+# define JCALL6(func, jenv, ar1, ar2, ar3, ar4, ar5, ar6) (*jenv)->func(jenv, ar1, ar2, ar3, ar4, ar5, ar6)
+# define JCALL7(func, jenv, ar1, ar2, ar3, ar4, ar5, ar6, ar7) (*jenv)->func(jenv, ar1, ar2, ar3, ar4, ar5, ar6, ar7)
+#endif
+
+%insert(runtime) %{
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+%}
+
+%insert(runtime) %{
+/* Support for throwing Java exceptions */
+typedef enum {
+ SWIG_JavaOutOfMemoryError = 1,
+ SWIG_JavaIOException,
+ SWIG_JavaRuntimeException,
+ SWIG_JavaIndexOutOfBoundsException,
+ SWIG_JavaArithmeticException,
+ SWIG_JavaIllegalArgumentException,
+ SWIG_JavaNullPointerException,
+ SWIG_JavaDirectorPureVirtual,
+ SWIG_JavaUnknownError,
+ SWIG_JavaIllegalStateException,
+} SWIG_JavaExceptionCodes;
+
+typedef struct {
+ SWIG_JavaExceptionCodes code;
+ const char *java_exception;
+} SWIG_JavaExceptions_t;
+%}
+
+%insert(runtime) {
+static void SWIGUNUSED SWIG_JavaThrowException(JNIEnv *jenv, SWIG_JavaExceptionCodes code, const char *msg) {
+ jclass excep;
+ static const SWIG_JavaExceptions_t java_exceptions[] = {
+ { SWIG_JavaOutOfMemoryError, "java/lang/OutOfMemoryError" },
+ { SWIG_JavaIOException, "java/io/IOException" },
+ { SWIG_JavaRuntimeException, "java/lang/RuntimeException" },
+ { SWIG_JavaIndexOutOfBoundsException, "java/lang/IndexOutOfBoundsException" },
+ { SWIG_JavaArithmeticException, "java/lang/ArithmeticException" },
+ { SWIG_JavaIllegalArgumentException, "java/lang/IllegalArgumentException" },
+ { SWIG_JavaNullPointerException, "java/lang/NullPointerException" },
+ { SWIG_JavaDirectorPureVirtual, "java/lang/RuntimeException" },
+ { SWIG_JavaUnknownError, "java/lang/UnknownError" },
+ { SWIG_JavaIllegalStateException, "java/lang/IllegalStateException" },
+ { (SWIG_JavaExceptionCodes)0, "java/lang/UnknownError" }
+ };
+ const SWIG_JavaExceptions_t *except_ptr = java_exceptions;
+
+ while (except_ptr->code != code && except_ptr->code)
+ except_ptr++;
+
+ JCALL0(ExceptionClear, jenv);
+ excep = JCALL1(FindClass, jenv, except_ptr->java_exception);
+ if (excep)
+ JCALL2(ThrowNew, jenv, excep, msg);
+}
+}
+
+%insert(runtime) %{
+/* Contract support */
+
+#define SWIG_contract_assert(nullreturn, expr, msg) do { if (!(expr)) {SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, msg); return nullreturn; } } while (0)
+%}
diff --git a/contrib/tools/swig/Lib/java/javakw.swg b/contrib/tools/swig/Lib/java/javakw.swg
new file mode 100644
index 0000000000..8a5b76eef2
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/javakw.swg
@@ -0,0 +1,70 @@
+#ifndef JAVA_JAVAKW_SWG_
+#define JAVA_JAVAKW_SWG_
+
+/* Warnings for Java keywords */
+#define JAVAKW(x) %keywordwarn("'" `x` "' is a java keyword",rename="_%s") `x`
+
+/*
+ from
+ http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
+*/
+
+JAVAKW(abstract);
+JAVAKW(double);
+JAVAKW(int);
+JAVAKW(strictfp);
+JAVAKW(boolean);
+JAVAKW(else);
+JAVAKW(interface);
+JAVAKW(super);
+JAVAKW(break);
+JAVAKW(extends);
+JAVAKW(long);
+JAVAKW(switch);
+JAVAKW(byte);
+JAVAKW(final);
+JAVAKW(native);
+JAVAKW(synchronized);
+JAVAKW(case);
+JAVAKW(finally);
+JAVAKW(new);
+JAVAKW(this);
+JAVAKW(catch);
+JAVAKW(float);
+JAVAKW(package);
+JAVAKW(throw);
+JAVAKW(char);
+JAVAKW(for);
+JAVAKW(private);
+JAVAKW(throws);
+JAVAKW(class);
+JAVAKW(goto);
+JAVAKW(protected);
+JAVAKW(transient);
+JAVAKW(const);
+JAVAKW(if);
+JAVAKW(public);
+JAVAKW(try);
+JAVAKW(continue);
+JAVAKW(implements);
+JAVAKW(return);
+JAVAKW(void);
+JAVAKW(default);
+JAVAKW(import);
+JAVAKW(short);
+JAVAKW(volatile);
+JAVAKW(do);
+JAVAKW(instanceof);
+JAVAKW(static);
+JAVAKW(while);
+
+
+/* others bad names */
+
+/* Note here that only *::clone() is bad, and *::clone(int) is ok */
+%namewarn("321:clone() is a java bad method name") *::clone();
+
+
+#undef JAVAKW
+
+#endif //JAVA_JAVAKW_SWG_
diff --git a/contrib/tools/swig/Lib/java/std_common.i b/contrib/tools/swig/Lib/java/std_common.i
new file mode 100644
index 0000000000..cee11e8caa
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/std_common.i
@@ -0,0 +1,5 @@
+%include <std_except.i>
+
+%apply size_t { std::size_t };
+%apply const size_t& { const std::size_t& };
+
diff --git a/contrib/tools/swig/Lib/java/std_except.i b/contrib/tools/swig/Lib/java/std_except.i
new file mode 100644
index 0000000000..91d2f92cf1
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/std_except.i
@@ -0,0 +1,32 @@
+/* -----------------------------------------------------------------------------
+ * std_except.i
+ *
+ * Typemaps used by the STL wrappers that throw exceptions.
+ * These typemaps are used when methods are declared with an STL exception specification, such as
+ * size_t at() const throw (std::out_of_range);
+ * ----------------------------------------------------------------------------- */
+
+%{
+#include <typeinfo>
+#include <stdexcept>
+%}
+
+namespace std
+{
+ %ignore exception;
+ struct exception {};
+}
+
+%typemap(throws) std::bad_cast "SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1.what());\n return $null;"
+%typemap(throws) std::bad_exception "SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1.what());\n return $null;"
+%typemap(throws) std::domain_error "SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1.what());\n return $null;"
+%typemap(throws) std::exception "SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1.what());\n return $null;"
+%typemap(throws) std::invalid_argument "SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, $1.what());\n return $null;"
+%typemap(throws) std::length_error "SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, $1.what());\n return $null;"
+%typemap(throws) std::logic_error "SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1.what());\n return $null;"
+%typemap(throws) std::out_of_range "SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, $1.what());\n return $null;"
+%typemap(throws) std::overflow_error "SWIG_JavaThrowException(jenv, SWIG_JavaArithmeticException, $1.what());\n return $null;"
+%typemap(throws) std::range_error "SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, $1.what());\n return $null;"
+%typemap(throws) std::runtime_error "SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1.what());\n return $null;"
+%typemap(throws) std::underflow_error "SWIG_JavaThrowException(jenv, SWIG_JavaArithmeticException, $1.what());\n return $null;"
+
diff --git a/contrib/tools/swig/Lib/java/std_string.i b/contrib/tools/swig/Lib/java/std_string.i
new file mode 100644
index 0000000000..830a896119
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/std_string.i
@@ -0,0 +1,121 @@
+/* -----------------------------------------------------------------------------
+ * std_string.i
+ *
+ * Typemaps for std::string and const std::string&
+ * These are mapped to a Java String and are passed around by value.
+ *
+ * To use non-const std::string references use the following %apply. Note
+ * that they are passed by value.
+ * %apply const std::string & {std::string &};
+ * ----------------------------------------------------------------------------- */
+
+%{
+#include <string>
+%}
+
+namespace std {
+
+%naturalvar string;
+
+class string;
+
+// string
+%typemap(jni) string "jstring"
+%typemap(jtype) string "String"
+%typemap(jstype) string "String"
+%typemap(javadirectorin) string "$jniinput"
+%typemap(javadirectorout) string "$javacall"
+
+%typemap(in) string
+%{ if(!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
+ return $null;
+ }
+ const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0);
+ if (!$1_pstr) return $null;
+ $1.assign($1_pstr);
+ jenv->ReleaseStringUTFChars($input, $1_pstr); %}
+
+%typemap(directorout) string
+%{ if(!$input) {
+ if (!jenv->ExceptionCheck()) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
+ }
+ return $null;
+ }
+ const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0);
+ if (!$1_pstr) return $null;
+ $result.assign($1_pstr);
+ jenv->ReleaseStringUTFChars($input, $1_pstr); %}
+
+%typemap(directorin,descriptor="Ljava/lang/String;") string
+%{ $input = jenv->NewStringUTF($1.c_str());
+ Swig::LocalRefGuard $1_refguard(jenv, $input); %}
+
+%typemap(out) string
+%{ $result = jenv->NewStringUTF($1.c_str()); %}
+
+%typemap(javain) string "$javainput"
+
+%typemap(javaout) string {
+ return $jnicall;
+ }
+
+%typemap(typecheck) string = char *;
+
+%typemap(throws) string
+%{ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1.c_str());
+ return $null; %}
+
+// const string &
+%typemap(jni) const string & "jstring"
+%typemap(jtype) const string & "String"
+%typemap(jstype) const string & "String"
+%typemap(javadirectorin) const string & "$jniinput"
+%typemap(javadirectorout) const string & "$javacall"
+
+%typemap(in) const string &
+%{ if(!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
+ return $null;
+ }
+ const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0);
+ if (!$1_pstr) return $null;
+ $*1_ltype $1_str($1_pstr);
+ $1 = &$1_str;
+ jenv->ReleaseStringUTFChars($input, $1_pstr); %}
+
+%typemap(directorout,warning=SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG) const string &
+%{ if(!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null string");
+ return $null;
+ }
+ const char *$1_pstr = (const char *)jenv->GetStringUTFChars($input, 0);
+ if (!$1_pstr) return $null;
+ /* possible thread/reentrant code problem */
+ static $*1_ltype $1_str;
+ $1_str = $1_pstr;
+ $result = &$1_str;
+ jenv->ReleaseStringUTFChars($input, $1_pstr); %}
+
+%typemap(directorin,descriptor="Ljava/lang/String;") const string &
+%{ $input = jenv->NewStringUTF($1.c_str());
+ Swig::LocalRefGuard $1_refguard(jenv, $input); %}
+
+%typemap(out) const string &
+%{ $result = jenv->NewStringUTF($1->c_str()); %}
+
+%typemap(javain) const string & "$javainput"
+
+%typemap(javaout) const string & {
+ return $jnicall;
+ }
+
+%typemap(typecheck) const string & = char *;
+
+%typemap(throws) const string &
+%{ SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, $1.c_str());
+ return $null; %}
+
+}
+
diff --git a/contrib/tools/swig/Lib/java/std_vector.i b/contrib/tools/swig/Lib/java/std_vector.i
new file mode 100644
index 0000000000..60ee23ebbe
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/std_vector.i
@@ -0,0 +1,185 @@
+/* -----------------------------------------------------------------------------
+ * std_vector.i
+ *
+ * SWIG typemaps for std::vector.
+ * The Java proxy class extends java.util.AbstractList and implements
+ * java.util.RandomAccess. The std::vector container looks and feels much like a
+ * java.util.ArrayList from Java.
+ * ----------------------------------------------------------------------------- */
+
+%include <std_common.i>
+
+%{
+#include <vector>
+#include <stdexcept>
+%}
+
+%fragment("SWIG_VectorSize", "header", fragment="SWIG_JavaIntFromSize_t") {
+SWIGINTERN jint SWIG_VectorSize(size_t size) {
+ jint sz = SWIG_JavaIntFromSize_t(size);
+ if (sz == -1)
+ throw std::out_of_range("vector size is too large to fit into a Java int");
+ return sz;
+}
+}
+
+%define SWIG_STD_VECTOR_MINIMUM_INTERNAL(CTYPE, CONST_REFERENCE)
+%typemap(javabase) std::vector< CTYPE > "java.util.AbstractList<$typemap(jboxtype, CTYPE)>"
+%typemap(javainterfaces) std::vector< CTYPE > "java.util.RandomAccess"
+%proxycode %{
+ public $javaclassname($typemap(jstype, CTYPE)[] initialElements) {
+ this();
+ reserve(initialElements.length);
+
+ for ($typemap(jstype, CTYPE) element : initialElements) {
+ add(element);
+ }
+ }
+
+ public $javaclassname(Iterable<$typemap(jboxtype, CTYPE)> initialElements) {
+ this();
+ for ($typemap(jstype, CTYPE) element : initialElements) {
+ add(element);
+ }
+ }
+
+ public $typemap(jboxtype, CTYPE) get(int index) {
+ return doGet(index);
+ }
+
+ public $typemap(jboxtype, CTYPE) set(int index, $typemap(jboxtype, CTYPE) e) {
+ return doSet(index, e);
+ }
+
+ public boolean add($typemap(jboxtype, CTYPE) e) {
+ modCount++;
+ doAdd(e);
+ return true;
+ }
+
+ public void add(int index, $typemap(jboxtype, CTYPE) e) {
+ modCount++;
+ doAdd(index, e);
+ }
+
+ public $typemap(jboxtype, CTYPE) remove(int index) {
+ modCount++;
+ return doRemove(index);
+ }
+
+ protected void removeRange(int fromIndex, int toIndex) {
+ modCount++;
+ doRemoveRange(fromIndex, toIndex);
+ }
+
+ public int size() {
+ return doSize();
+ }
+%}
+
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef CTYPE value_type;
+ typedef CTYPE *pointer;
+ typedef CTYPE const *const_pointer;
+ typedef CTYPE &reference;
+ typedef CONST_REFERENCE const_reference;
+
+ vector();
+ vector(const vector &other);
+
+ size_type capacity() const;
+ void reserve(size_type n) throw (std::length_error);
+ %rename(isEmpty) empty;
+ bool empty() const;
+ void clear();
+ %extend {
+ %fragment("SWIG_VectorSize");
+
+ vector(jint count, const CTYPE &value) throw (std::out_of_range) {
+ if (count < 0)
+ throw std::out_of_range("vector count must be positive");
+ return new std::vector< CTYPE >(static_cast<std::vector< CTYPE >::size_type>(count), value);
+ }
+
+ jint doSize() const throw (std::out_of_range) {
+ return SWIG_VectorSize(self->size());
+ }
+
+ void doAdd(const value_type& x) {
+ self->push_back(x);
+ }
+
+ void doAdd(jint index, const value_type& x) throw (std::out_of_range) {
+ jint size = static_cast<jint>(self->size());
+ if (0 <= index && index <= size) {
+ self->insert(self->begin() + index, x);
+ } else {
+ throw std::out_of_range("vector index out of range");
+ }
+ }
+
+ value_type doRemove(jint index) throw (std::out_of_range) {
+ jint size = static_cast<jint>(self->size());
+ if (0 <= index && index < size) {
+ CTYPE const old_value = (*self)[index];
+ self->erase(self->begin() + index);
+ return old_value;
+ } else {
+ throw std::out_of_range("vector index out of range");
+ }
+ }
+
+ CONST_REFERENCE doGet(jint index) throw (std::out_of_range) {
+ jint size = static_cast<jint>(self->size());
+ if (index >= 0 && index < size)
+ return (*self)[index];
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+
+ value_type doSet(jint index, const value_type& val) throw (std::out_of_range) {
+ jint size = static_cast<jint>(self->size());
+ if (index >= 0 && index < size) {
+ CTYPE const old_value = (*self)[index];
+ (*self)[index] = val;
+ return old_value;
+ }
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+
+ void doRemoveRange(jint fromIndex, jint toIndex) throw (std::out_of_range) {
+ jint size = static_cast<jint>(self->size());
+ if (0 <= fromIndex && fromIndex <= toIndex && toIndex <= size) {
+ self->erase(self->begin() + fromIndex, self->begin() + toIndex);
+ } else {
+ throw std::out_of_range("vector index out of range");
+ }
+ }
+ }
+%enddef
+
+%javamethodmodifiers std::vector::doSize "private";
+%javamethodmodifiers std::vector::doAdd "private";
+%javamethodmodifiers std::vector::doGet "private";
+%javamethodmodifiers std::vector::doSet "private";
+%javamethodmodifiers std::vector::doRemove "private";
+%javamethodmodifiers std::vector::doRemoveRange "private";
+
+namespace std {
+
+ template<class T> class vector {
+ SWIG_STD_VECTOR_MINIMUM_INTERNAL(T, const value_type&)
+ };
+
+ // bool specialization
+ template<> class vector<bool> {
+ SWIG_STD_VECTOR_MINIMUM_INTERNAL(bool, bool)
+ };
+}
+
+%define specialize_std_vector(T)
+#warning "specialize_std_vector - specialization for type T no longer needed"
+%enddef
diff --git a/contrib/tools/swig/Lib/java/typemaps.i b/contrib/tools/swig/Lib/java/typemaps.i
new file mode 100644
index 0000000000..e130c1930b
--- /dev/null
+++ b/contrib/tools/swig/Lib/java/typemaps.i
@@ -0,0 +1,529 @@
+/* -----------------------------------------------------------------------------
+ * typemaps.i
+ *
+ * Pointer and reference handling typemap library
+ *
+ * These mappings provide support for input/output arguments and common
+ * uses for C/C++ pointers and C++ references.
+ * ----------------------------------------------------------------------------- */
+
+/*
+INPUT typemaps
+--------------
+
+These typemaps remap a C pointer or C++ reference to be an "INPUT" value which is
+passed by value instead of reference.
+
+The following typemaps can be applied to turn a pointer or reference into a simple
+input value. That is, instead of passing a pointer or reference to an object,
+you would use a real value instead.
+
+ bool *INPUT, bool &INPUT
+ signed char *INPUT, signed char &INPUT
+ unsigned char *INPUT, unsigned char &INPUT
+ short *INPUT, short &INPUT
+ unsigned short *INPUT, unsigned short &INPUT
+ int *INPUT, int &INPUT
+ unsigned int *INPUT, unsigned int &INPUT
+ long *INPUT, long &INPUT
+ unsigned long *INPUT, unsigned long &INPUT
+ long long *INPUT, long long &INPUT
+ unsigned long long *INPUT, unsigned long long &INPUT
+ float *INPUT, float &INPUT
+ double *INPUT, double &INPUT
+
+To use these, suppose you had a C function like this :
+
+ double fadd(double *a, double *b) {
+ return *a+*b;
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ double fadd(double *INPUT, double *INPUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *INPUT { double *a, double *b };
+ double fadd(double *a, double *b);
+
+In Java you could then use it like this:
+ double answer = modulename.fadd(10.0, 20.0);
+
+There are no char *INPUT typemaps, however you can apply the signed char * typemaps instead:
+ %include <typemaps.i>
+ %apply signed char *INPUT {char *input};
+ void f(char *input);
+*/
+
+%define INPUT_TYPEMAP(TYPE, JNITYPE, JTYPE, JNIDESC)
+%typemap(jni) TYPE *INPUT, TYPE &INPUT "JNITYPE"
+%typemap(jtype) TYPE *INPUT, TYPE &INPUT "JTYPE"
+%typemap(jstype) TYPE *INPUT, TYPE &INPUT "JTYPE"
+%typemap(javain) TYPE *INPUT, TYPE &INPUT "$javainput"
+
+%typemap(in) TYPE *INPUT, TYPE &INPUT
+%{ $1 = ($1_ltype)&$input; %}
+
+%typemap(freearg) TYPE *INPUT, TYPE &INPUT ""
+
+%typemap(typecheck) TYPE *INPUT = TYPE;
+%typemap(typecheck) TYPE &INPUT = TYPE;
+%enddef
+
+INPUT_TYPEMAP(bool, jboolean, boolean, "Z");
+INPUT_TYPEMAP(signed char, jbyte, byte, "B");
+INPUT_TYPEMAP(unsigned char, jshort, short, "S");
+INPUT_TYPEMAP(short, jshort, short, "S");
+INPUT_TYPEMAP(unsigned short, jint, int, "I");
+INPUT_TYPEMAP(int, jint, int, "I");
+INPUT_TYPEMAP(unsigned int, jlong, long, "J");
+INPUT_TYPEMAP(long, jint, int, "I");
+INPUT_TYPEMAP(unsigned long, jlong, long, "J");
+INPUT_TYPEMAP(long long, jlong, long, "J");
+INPUT_TYPEMAP(unsigned long long, jobject, java.math.BigInteger, "Ljava/math/BigInteger;");
+INPUT_TYPEMAP(float, jfloat, float, "F");
+INPUT_TYPEMAP(double, jdouble, double, "D");
+
+#undef INPUT_TYPEMAP
+
+/* Convert from BigInteger using the toByteArray member function */
+/* Overrides the typemap in the INPUT_TYPEMAP macro */
+%typemap(in) unsigned long long *INPUT($*1_ltype temp), unsigned long long &INPUT($*1_ltype temp) {
+ jclass clazz;
+ jmethodID mid;
+ jbyteArray ba;
+ jbyte* bae;
+ jsize sz;
+ int i;
+
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "BigInteger null");
+ return $null;
+ }
+ clazz = JCALL1(GetObjectClass, jenv, $input);
+ mid = JCALL3(GetMethodID, jenv, clazz, "toByteArray", "()[B");
+ ba = (jbyteArray)JCALL2(CallObjectMethod, jenv, $input, mid);
+ bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ sz = JCALL1(GetArrayLength, jenv, ba);
+ temp = 0;
+ if (sz > 0) {
+ temp = ($*1_ltype)(signed char)bae[0];
+ for(i=1; i<sz; i++) {
+ temp = (temp << 8) | ($*1_ltype)(unsigned char)bae[i];
+ }
+ }
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+ $1 = &temp;
+}
+
+// OUTPUT typemaps. These typemaps are used for parameters that
+// are output only. An array replaces the c pointer or reference parameter.
+// The output value is returned in this array passed in.
+
+/*
+OUTPUT typemaps
+---------------
+
+The following typemaps can be applied to turn a pointer or reference into an "output"
+value. When calling a function, no input value would be given for
+a parameter, but an output value would be returned. This works by a
+Java array being passed as a parameter where a c pointer or reference is required.
+As with any Java function, the array is passed by reference so that
+any modifications to the array will be picked up in the calling function.
+Note that the array passed in MUST have at least one element, but as the
+c function does not require any input, the value can be set to anything.
+
+ bool *OUTPUT, bool &OUTPUT
+ signed char *OUTPUT, signed char &OUTPUT
+ unsigned char *OUTPUT, unsigned char &OUTPUT
+ short *OUTPUT, short &OUTPUT
+ unsigned short *OUTPUT, unsigned short &OUTPUT
+ int *OUTPUT, int &OUTPUT
+ unsigned int *OUTPUT, unsigned int &OUTPUT
+ long *OUTPUT, long &OUTPUT
+ unsigned long *OUTPUT, unsigned long &OUTPUT
+ long long *OUTPUT, long long &OUTPUT
+ unsigned long long *OUTPUT, unsigned long long &OUTPUT
+ float *OUTPUT, float &OUTPUT
+ double *OUTPUT, double &OUTPUT
+
+For example, suppose you were trying to wrap the modf() function in the
+C math library which splits x into integral and fractional parts (and
+returns the integer part in one of its parameters):
+
+ double modf(double x, double *ip);
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ double modf(double x, double *OUTPUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *OUTPUT { double *ip };
+ double modf(double x, double *ip);
+
+The Java output of the function would be the function return value and the
+value in the single element array. In Java you would use it like this:
+
+ double[] ptr = {0.0};
+ double fraction = modulename.modf(5.0,ptr);
+
+There are no char *OUTPUT typemaps, however you can apply the signed char * typemaps instead:
+ %include <typemaps.i>
+ %apply signed char *OUTPUT {char *output};
+ void f(char *output);
+*/
+
+/* Java BigInteger[] */
+%typecheck(SWIG_TYPECHECK_INT128_ARRAY) SWIGBIGINTEGERARRAY ""
+
+%define OUTPUT_TYPEMAP(TYPE, JNITYPE, JTYPE, JAVATYPE, JNIDESC, TYPECHECKTYPE)
+%typemap(jni) TYPE *OUTPUT, TYPE &OUTPUT %{JNITYPE##Array%}
+%typemap(jtype) TYPE *OUTPUT, TYPE &OUTPUT "JTYPE[]"
+%typemap(jstype) TYPE *OUTPUT, TYPE &OUTPUT "JTYPE[]"
+%typemap(javain) TYPE *OUTPUT, TYPE &OUTPUT "$javainput"
+%typemap(javadirectorin) TYPE *OUTPUT, TYPE &OUTPUT "$jniinput"
+%typemap(javadirectorout) TYPE *OUTPUT, TYPE &OUTPUT "$javacall"
+
+%typemap(in) TYPE *OUTPUT($*1_ltype temp), TYPE &OUTPUT($*1_ltype temp)
+{
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return $null;
+ }
+ if (JCALL1(GetArrayLength, jenv, $input) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return $null;
+ }
+ temp = ($*1_ltype)0;
+ $1 = &temp;
+}
+
+%typemap(freearg) TYPE *OUTPUT, TYPE &OUTPUT ""
+
+%typemap(argout) TYPE *OUTPUT, TYPE &OUTPUT
+{
+ JNITYPE jvalue = (JNITYPE)temp$argnum;
+ JCALL4(Set##JAVATYPE##ArrayRegion, jenv, $input, 0, 1, &jvalue);
+}
+
+%typemap(directorin,descriptor=JNIDESC) TYPE &OUTPUT %{
+ $input = JCALL1(New##JAVATYPE##Array, jenv, 1);
+ if (!$input) return $null;
+ Swig::LocalRefGuard $1_refguard(jenv, $input); %}
+
+%typemap(directorin,descriptor=JNIDESC) TYPE *OUTPUT %{
+ if ($1) {
+ $input = JCALL1(New##JAVATYPE##Array, jenv, 1);
+ if (!$input) return $null;
+ }
+ Swig::LocalRefGuard $1_refguard(jenv, $input); %}
+
+%typemap(directorargout, noblock=1) TYPE &OUTPUT
+{
+ JNITYPE $1_jvalue;
+ JCALL4(Get##JAVATYPE##ArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ $result = ($*1_ltype)$1_jvalue;
+}
+
+%typemap(directorargout, noblock=1) TYPE *OUTPUT
+{
+ if ($result) {
+ JNITYPE $1_jvalue;
+ JCALL4(Get##JAVATYPE##ArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ *$result = ($*1_ltype)$1_jvalue;
+ }
+}
+
+%typemap(typecheck) TYPE *OUTPUT = TYPECHECKTYPE;
+%typemap(typecheck) TYPE &OUTPUT = TYPECHECKTYPE;
+%enddef
+
+OUTPUT_TYPEMAP(bool, jboolean, boolean, Boolean, "[Z", jbooleanArray);
+OUTPUT_TYPEMAP(signed char, jbyte, byte, Byte, "[B", jbyteArray);
+OUTPUT_TYPEMAP(unsigned char, jshort, short, Short, "[S", jshortArray);
+OUTPUT_TYPEMAP(short, jshort, short, Short, "[S", jshortArray);
+OUTPUT_TYPEMAP(unsigned short, jint, int, Int, "[I", jintArray);
+OUTPUT_TYPEMAP(int, jint, int, Int, "[I", jintArray);
+OUTPUT_TYPEMAP(unsigned int, jlong, long, Long, "[J", jlongArray);
+OUTPUT_TYPEMAP(long, jint, int, Int, "[I", jintArray);
+OUTPUT_TYPEMAP(unsigned long, jlong, long, Long, "[J", jlongArray);
+OUTPUT_TYPEMAP(long long, jlong, long, Long, "[J", jlongArray);
+OUTPUT_TYPEMAP(unsigned long long, jobject, java.math.BigInteger, Object, "[Ljava/math/BigInteger;", jobjectArray);
+OUTPUT_TYPEMAP(float, jfloat, float, Float, "[F", jfloatArray);
+OUTPUT_TYPEMAP(double, jdouble, double, Double, "[D", jdoubleArray);
+
+#undef OUTPUT_TYPEMAP
+
+%typemap(in) bool *OUTPUT($*1_ltype temp), bool &OUTPUT($*1_ltype temp)
+{
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return $null;
+ }
+ if (JCALL1(GetArrayLength, jenv, $input) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return $null;
+ }
+ temp = false;
+ $1 = &temp;
+}
+
+%typemap(directorargout, noblock=1) bool &OUTPUT
+{
+ jboolean $1_jvalue;
+ JCALL4(GetBooleanArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ $result = $1_jvalue ? true : false;
+}
+
+%typemap(directorargout, noblock=1) bool *OUTPUT
+{
+ if ($result) {
+ jboolean $1_jvalue;
+ JCALL4(GetBooleanArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ *$result = $1_jvalue ? true : false;
+ }
+}
+
+
+/* Convert to BigInteger - byte array holds number in 2's complement big endian format */
+/* Use first element in BigInteger array for output */
+/* Overrides the typemap in the OUTPUT_TYPEMAP macro */
+%typemap(argout) unsigned long long *OUTPUT, unsigned long long &OUTPUT {
+ jbyteArray ba = JCALL1(NewByteArray, jenv, 9);
+ jbyte* bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ jclass clazz = JCALL1(FindClass, jenv, "java/math/BigInteger");
+ jmethodID mid = JCALL3(GetMethodID, jenv, clazz, "<init>", "([B)V");
+ jobject bigint;
+ int i;
+
+ bae[0] = 0;
+ for(i=1; i<9; i++ ) {
+ bae[i] = (jbyte)(temp$argnum>>8*(8-i));
+ }
+
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+ bigint = JCALL3(NewObject, jenv, clazz, mid, ba);
+ JCALL1(DeleteLocalRef, jenv, ba);
+ JCALL3(SetObjectArrayElement, jenv, $input, 0, bigint);
+}
+
+/*
+INOUT typemaps
+--------------
+
+Mappings for a parameter that is both an input and an output parameter
+
+The following typemaps can be applied to make a function parameter both
+an input and output value. This combines the behavior of both the
+"INPUT" and "OUTPUT" typemaps described earlier. Output values are
+returned as an element in a Java array.
+
+ bool *INOUT, bool &INOUT
+ signed char *INOUT, signed char &INOUT
+ unsigned char *INOUT, unsigned char &INOUT
+ short *INOUT, short &INOUT
+ unsigned short *INOUT, unsigned short &INOUT
+ int *INOUT, int &INOUT
+ unsigned int *INOUT, unsigned int &INOUT
+ long *INOUT, long &INOUT
+ unsigned long *INOUT, unsigned long &INOUT
+ long long *INOUT, long long &INOUT
+ unsigned long long *INOUT, unsigned long long &INOUT
+ float *INOUT, float &INOUT
+ double *INOUT, double &INOUT
+
+For example, suppose you were trying to wrap the following function :
+
+ void neg(double *x) {
+ *x = -(*x);
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ void neg(double *INOUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *INOUT { double *x };
+ void neg(double *x);
+
+This works similarly to C in that the mapping directly modifies the
+input value - the input must be an array with a minimum of one element.
+The element in the array is the input and the output is the element in
+the array.
+
+ double x[] = {5.0};
+ neg(x);
+
+The implementation of the OUTPUT and INOUT typemaps is different to other
+languages in that other languages will return the output value as part
+of the function return value. This difference is due to Java being a typed language.
+
+There are no char *INOUT typemaps, however you can apply the signed char * typemaps instead:
+ %include <typemaps.i>
+ %apply signed char *INOUT {char *inout};
+ void f(char *inout);
+*/
+
+%define INOUT_TYPEMAP(TYPE, JNITYPE, JTYPE, JAVATYPE, JNIDESC, TYPECHECKTYPE)
+%typemap(jni) TYPE *INOUT, TYPE &INOUT %{JNITYPE##Array%}
+%typemap(jtype) TYPE *INOUT, TYPE &INOUT "JTYPE[]"
+%typemap(jstype) TYPE *INOUT, TYPE &INOUT "JTYPE[]"
+%typemap(javain) TYPE *INOUT, TYPE &INOUT "$javainput"
+%typemap(javadirectorin) TYPE *INOUT, TYPE &INOUT "$jniinput"
+%typemap(javadirectorout) TYPE *INOUT, TYPE &INOUT "$javacall"
+
+%typemap(in) TYPE *INOUT, TYPE &INOUT {
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return $null;
+ }
+ if (JCALL1(GetArrayLength, jenv, $input) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return $null;
+ }
+ $1 = ($1_ltype) JCALL2(Get##JAVATYPE##ArrayElements, jenv, $input, 0);
+}
+
+%typemap(freearg) TYPE *INOUT, TYPE &INOUT ""
+
+%typemap(argout) TYPE *INOUT, TYPE &INOUT
+{ JCALL3(Release##JAVATYPE##ArrayElements, jenv, $input, (JNITYPE *)$1, 0); }
+
+%typemap(directorin,descriptor=JNIDESC) TYPE &INOUT %{
+ $input = JCALL1(New##JAVATYPE##Array, jenv, 1);
+ if (!$input) return $null;
+ JNITYPE $1_jvalue = (JNITYPE)$1;
+ JCALL4(Set##JAVATYPE##ArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ Swig::LocalRefGuard $1_refguard(jenv, $input); %}
+
+%typemap(directorin,descriptor=JNIDESC) TYPE *INOUT %{
+ if ($1) {
+ $input = JCALL1(New##JAVATYPE##Array, jenv, 1);
+ if (!$input) return $null;
+ JNITYPE $1_jvalue = (JNITYPE)*$1;
+ JCALL4(Set##JAVATYPE##ArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ }
+ Swig::LocalRefGuard $1_refguard(jenv, $input); %}
+
+%typemap(directorargout, noblock=1) TYPE &INOUT
+{
+ JCALL4(Get##JAVATYPE##ArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ $result = ($*1_ltype)$1_jvalue;
+}
+
+%typemap(directorargout, noblock=1) TYPE *INOUT
+{
+ if ($result) {
+ JNITYPE $1_jvalue;
+ JCALL4(Get##JAVATYPE##ArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ *$result = ($*1_ltype)$1_jvalue;
+ }
+}
+
+%typemap(typecheck) TYPE *INOUT = TYPECHECKTYPE;
+%typemap(typecheck) TYPE &INOUT = TYPECHECKTYPE;
+%enddef
+
+INOUT_TYPEMAP(bool, jboolean, boolean, Boolean, "[Z", jbooleanArray);
+INOUT_TYPEMAP(signed char, jbyte, byte, Byte, "[B", jbyteArray);
+INOUT_TYPEMAP(unsigned char, jshort, short, Short, "[S", jshortArray);
+INOUT_TYPEMAP(short, jshort, short, Short, "[S", jshortArray);
+INOUT_TYPEMAP(unsigned short, jint, int, Int, "[I", jintArray);
+INOUT_TYPEMAP(int, jint, int, Int, "[I", jintArray);
+INOUT_TYPEMAP(unsigned int, jlong, long, Long, "[J", jlongArray);
+INOUT_TYPEMAP(long, jint, int, Int, "[I", jintArray);
+INOUT_TYPEMAP(unsigned long, jlong, long, Long, "[J", jlongArray);
+INOUT_TYPEMAP(long long, jlong, long, Long, "[J", jlongArray);
+INOUT_TYPEMAP(unsigned long long, jobject, java.math.BigInteger, Object, "[java/math/BigInteger;", jobjectArray);
+INOUT_TYPEMAP(float, jfloat, float, Float, "[F", jfloatArray);
+INOUT_TYPEMAP(double, jdouble, double, Double, "[D", jdoubleArray);
+
+#undef INOUT_TYPEMAP
+
+/* Override typemaps in the INOUT_TYPEMAP macro for booleans to fix casts
+ as a jboolean isn't always the same size as a bool */
+%typemap(in) bool *INOUT (bool btemp, jboolean *jbtemp), bool &INOUT (bool btemp, jboolean *jbtemp) {
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return $null;
+ }
+ if (JCALL1(GetArrayLength, jenv, $input) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return $null;
+ }
+ jbtemp = JCALL2(GetBooleanArrayElements, jenv, $input, 0);
+ btemp = (*jbtemp) ? true : false;
+ $1 = &btemp;
+}
+
+%typemap(argout) bool *INOUT, bool &INOUT {
+ *jbtemp$argnum = btemp$argnum ? (jboolean)1 : (jboolean)0;
+ JCALL3(ReleaseBooleanArrayElements, jenv, $input , (jboolean *)jbtemp$argnum, 0);
+}
+
+%typemap(directorargout, noblock=1) bool &INOUT
+{
+ JCALL4(GetBooleanArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ $result = $1_jvalue ? true : false;
+}
+
+%typemap(directorargout, noblock=1) bool *INOUT
+{
+ if ($result) {
+ jboolean $1_jvalue;
+ JCALL4(GetBooleanArrayRegion, jenv, $input, 0, 1, &$1_jvalue);
+ *$result = $1_jvalue ? true : false;
+ }
+}
+
+
+/* Override the typemap in the INOUT_TYPEMAP macro for unsigned long long */
+%typemap(in) unsigned long long *INOUT ($*1_ltype temp), unsigned long long &INOUT ($*1_ltype temp) {
+ jobject bigint;
+ jclass clazz;
+ jmethodID mid;
+ jbyteArray ba;
+ jbyte* bae;
+ jsize sz;
+ int i;
+
+ if (!$input) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+ return $null;
+ }
+ if (JCALL1(GetArrayLength, jenv, $input) == 0) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+ return $null;
+ }
+ bigint = JCALL2(GetObjectArrayElement, jenv, $input, 0);
+ if (!bigint) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array element null");
+ return $null;
+ }
+ clazz = JCALL1(GetObjectClass, jenv, bigint);
+ mid = JCALL3(GetMethodID, jenv, clazz, "toByteArray", "()[B");
+ ba = (jbyteArray)JCALL2(CallObjectMethod, jenv, bigint, mid);
+ bae = JCALL2(GetByteArrayElements, jenv, ba, 0);
+ sz = JCALL1(GetArrayLength, jenv, ba);
+ temp = 0;
+ if (sz > 0) {
+ temp = ($*1_ltype)(signed char)bae[0];
+ for(i=1; i<sz; i++) {
+ temp = (temp << 8) | ($*1_ltype)(unsigned char)bae[i];
+ }
+ }
+ JCALL3(ReleaseByteArrayElements, jenv, ba, bae, 0);
+ $1 = &temp;
+}
+
+%typemap(argout) unsigned long long *INOUT = unsigned long long *OUTPUT;
+%typemap(argout) unsigned long long &INOUT = unsigned long long &OUTPUT;
diff --git a/contrib/tools/swig/Lib/perl5/exception.i b/contrib/tools/swig/Lib/perl5/exception.i
new file mode 100644
index 0000000000..b786f25e29
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/exception.i
@@ -0,0 +1,5 @@
+%include <typemaps/exception.swg>
+
+%insert("runtime") {
+ %define_as(SWIG_exception(code, msg), %block(%error(code, msg); SWIG_fail; ))
+}
diff --git a/contrib/tools/swig/Lib/perl5/extra-install.list b/contrib/tools/swig/Lib/perl5/extra-install.list
new file mode 100644
index 0000000000..db93830aab
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/extra-install.list
@@ -0,0 +1,2 @@
+# see top-level Makefile.in
+Makefile.pl noembed.h
diff --git a/contrib/tools/swig/Lib/perl5/perl5.swg b/contrib/tools/swig/Lib/perl5/perl5.swg
new file mode 100644
index 0000000000..693c2b9457
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perl5.swg
@@ -0,0 +1,42 @@
+/* ------------------------------------------------------------
+ * perl.swg
+ *
+ * Perl configuration module.
+ * ------------------------------------------------------------ */
+
+/* ------------------------------------------------------------
+ * Inner macros
+ * ------------------------------------------------------------ */
+%include <perlmacros.swg>
+
+/* ------------------------------------------------------------
+ * The runtime part
+ * ------------------------------------------------------------ */
+%include <perlruntime.swg>
+
+/* ------------------------------------------------------------
+ * Special user directives
+ * ------------------------------------------------------------ */
+%include <perluserdir.swg>
+
+/* ------------------------------------------------------------
+ * Typemap specializations
+ * ------------------------------------------------------------ */
+%include <perltypemaps.swg>
+
+/* ------------------------------------------------------------
+ * Overloaded operator support
+ * ------------------------------------------------------------ */
+%include <perlopers.swg>
+
+/* ------------------------------------------------------------
+ * Warnings for Perl keywords
+ * ------------------------------------------------------------ */
+%include <perlkw.swg>
+
+/* ------------------------------------------------------------
+ * The Perl initialization function
+ * ------------------------------------------------------------ */
+%include <perlinit.swg>
+
+
diff --git a/contrib/tools/swig/Lib/perl5/perlfragments.swg b/contrib/tools/swig/Lib/perl5/perlfragments.swg
new file mode 100644
index 0000000000..45d25d1f6c
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perlfragments.swg
@@ -0,0 +1,23 @@
+/*
+
+ Create a file with this name, 'perlfragments.swg', in your working
+ directory and add all the %fragments you want to take precedence
+ over the ones defined by default by swig.
+
+ For example, if you add:
+
+ %fragment(SWIG_AsVal_frag(int),"header") {
+ SWIGINTERNINLINE int
+ SWIG_AsVal(int)(PyObject *obj, int *val)
+ {
+ <your code here>;
+ }
+ }
+
+ this will replace the code used to retrieve an integer value for all
+ the typemaps that need it, including:
+
+ int, std::vector<int>, std::list<std::pair<int,int> >, etc.
+
+
+*/
diff --git a/contrib/tools/swig/Lib/perl5/perlinit.swg b/contrib/tools/swig/Lib/perl5/perlinit.swg
new file mode 100644
index 0000000000..c26b93fadb
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perlinit.swg
@@ -0,0 +1,78 @@
+
+/* Export the SWIG initialization function */
+%header %{
+#ifdef __cplusplus
+extern "C"
+#endif
+#ifndef MULTIPLICITY
+SWIGEXPORT void SWIG_init (CV* cv);
+#else
+SWIGEXPORT void SWIG_init (pTHXo_ CV* cv);
+#endif
+%}
+
+/* Module initialization function */
+
+%insert(init) "swiginit.swg"
+
+%init %{
+
+#if defined(__cplusplus) && ! defined(XSPROTO)
+extern "C"
+#endif
+
+XS(SWIG_init) {
+ dXSARGS;
+ int i;
+ (void)items;
+
+ SWIG_InitializeModule(0);
+
+ /* Install commands */
+ for (i = 0; swig_commands[i].name; i++) {
+ /* Casts only needed for Perl < 5.10. */
+#ifdef __cplusplus
+ newXS(const_cast<char*>(swig_commands[i].name), swig_commands[i].wrapper, const_cast<char*>(__FILE__));
+#else
+ newXS((char*)swig_commands[i].name, swig_commands[i].wrapper, (char*)__FILE__);
+#endif
+ }
+
+ /* Install variables */
+ for (i = 0; swig_variables[i].name; i++) {
+ SV *sv;
+ sv = get_sv(swig_variables[i].name, TRUE | 0x2 | GV_ADDMULTI);
+ if (swig_variables[i].type) {
+ SWIG_MakePtr(sv,(void *)1, *swig_variables[i].type,0);
+ } else {
+ sv_setiv(sv,(IV) 0);
+ }
+ swig_create_magic(sv, swig_variables[i].name, swig_variables[i].set, swig_variables[i].get);
+ }
+
+ /* Install constant */
+ for (i = 0; swig_constants[i].type; i++) {
+ SV *sv;
+ sv = get_sv(swig_constants[i].name, TRUE | 0x2 | GV_ADDMULTI);
+ switch(swig_constants[i].type) {
+ case SWIG_INT:
+ sv_setiv(sv, (IV) swig_constants[i].lvalue);
+ break;
+ case SWIG_FLOAT:
+ sv_setnv(sv, (double) swig_constants[i].dvalue);
+ break;
+ case SWIG_STRING:
+ sv_setpv(sv, (const char *) swig_constants[i].pvalue);
+ break;
+ case SWIG_POINTER:
+ SWIG_MakePtr(sv, swig_constants[i].pvalue, *(swig_constants[i].ptype),0);
+ break;
+ case SWIG_BINARY:
+ SWIG_MakePackedObj(sv, swig_constants[i].pvalue, swig_constants[i].lvalue, *(swig_constants[i].ptype));
+ break;
+ default:
+ break;
+ }
+ SvREADONLY_on(sv);
+ }
+%}
diff --git a/contrib/tools/swig/Lib/perl5/perlkw.swg b/contrib/tools/swig/Lib/perl5/perlkw.swg
new file mode 100644
index 0000000000..00648e0bf3
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perlkw.swg
@@ -0,0 +1,251 @@
+/* Warnings for Perl keywords */
+#define PERLKW(x) %keywordwarn("'" `x` "' is a perl keyword") `x`
+#define PERLBN(x) %builtinwarn("'" `x` "' conflicts with a built-in name in perl") "::" `x`
+
+
+/*
+
+ From http://www.rocketaware.com/perl/perlfunc/
+
+*/
+
+/* Functions for SCALARs or strings*/
+PERLBN(chomp);
+PERLBN(chop);
+PERLBN(chr);
+PERLBN(crypt);
+PERLBN(hex);
+PERLBN(index);
+PERLBN(lc);
+PERLBN(lcfirst);
+PERLBN(length);
+PERLBN(oct);
+PERLBN(ord);
+PERLBN(pack);
+PERLBN(reverse);
+PERLBN(rindex);
+PERLBN(sprintf);
+PERLBN(substr);
+PERLBN(uc);
+PERLBN(ucfirst);
+
+/* Regular expressions and pattern matching */
+PERLBN(m);
+PERLBN(pos);
+PERLBN(quotemeta);
+PERLBN(split);
+PERLBN(study);
+
+/* Numeric functions */
+PERLBN(abs);
+PERLBN(atan2);
+PERLBN(cos);
+PERLBN(exp);
+PERLBN(hex);
+PERLBN(int);
+PERLBN(log);
+PERLBN(oct);
+PERLBN(rand);
+PERLBN(sin);
+PERLBN(sqrt);
+PERLBN(srand);
+
+
+/* Functions for real @ARRAYs*/
+PERLBN(pop);
+PERLBN(push);
+PERLBN(shift);
+PERLBN(splice);
+PERLBN(unshift);
+
+/* Functions for list data*/
+PERLBN(grep);
+PERLBN(join);
+PERLBN(map);
+PERLBN(qw);
+PERLBN(reverse);
+PERLBN(sort);
+PERLBN(unpack);
+
+
+/* Functions for real %HASHes*/
+PERLBN(delete);
+PERLBN(each);
+PERLBN(exists);
+PERLBN(keys);
+PERLBN(values);
+
+
+/* Input and output functions*/
+
+PERLBN(binmode);
+PERLBN(close);
+PERLBN(closedir);
+PERLBN(dbmclose);
+PERLBN(dbmopen);
+PERLBN(die);
+PERLBN(eof);
+PERLBN(fileno);
+PERLBN(flock);
+PERLBN(format);
+PERLBN(getc);
+PERLBN(print);
+PERLBN(printf);
+PERLBN(read);
+PERLBN(readdir);
+PERLBN(rewinddir);
+PERLBN(seek);
+PERLBN(seekdir);
+PERLBN(select);
+PERLBN(syscall);
+PERLBN(sysread);
+PERLBN(sysseek);
+PERLBN(syswrite);
+PERLBN(tell);
+PERLBN(telldir);
+PERLBN(truncate);
+PERLBN(warn);
+PERLBN(write);
+
+
+/* Functions for fixed length data or records*/
+PERLBN(pack);
+PERLBN(read);
+PERLBN(syscall);
+PERLBN(sysread);
+PERLBN(syswrite);
+PERLBN(unpack);
+PERLBN(vec);
+
+
+/* Functions for filehandles, files, or directories */
+PERLBN(chdir);
+PERLBN(chmod);
+PERLBN(chown);
+PERLBN(chroot);
+PERLBN(fcntl);
+PERLBN(glob);
+PERLBN(ioctl);
+PERLBN(link);
+PERLBN(lstat);
+PERLBN(mkdir);
+PERLBN(open);
+PERLBN(opendir);
+PERLBN(readlink);
+PERLBN(rename);
+PERLBN(rmdir);
+PERLBN(stat);
+PERLBN(symlink);
+PERLBN(umask);
+PERLBN(unlink);
+PERLBN(utime);
+
+
+/* Keywords related to the control flow of your perl program */
+PERLKW(caller);
+PERLKW(continue);
+PERLKW(die);
+PERLKW(do);
+PERLKW(dump);
+PERLKW(eval);
+PERLKW(exit);
+PERLKW(goto);
+PERLKW(last);
+PERLKW(next);
+PERLKW(redo);
+PERLKW(return);
+PERLKW(sub);
+PERLKW(wantarray);
+
+
+/* Keywords related to scoping */
+PERLKW(caller);
+PERLKW(import);
+PERLKW(local);
+PERLKW(my);
+PERLKW(package);
+PERLKW(use);
+
+
+/* Miscellaneous functions */
+PERLBN("defined");
+PERLBN(dump);
+PERLBN(eval);
+PERLBN(formline);
+PERLBN(local);
+PERLBN(my);
+PERLBN(reset);
+PERLBN(scalar);
+PERLBN(undef);
+PERLBN(wantarray);
+
+
+/* Functions for processes and process groups */
+PERLBN(alarm);
+PERLBN(exec);
+PERLBN(fork);
+PERLBN(getpgrp);
+PERLBN(getppid);
+PERLBN(getpriority);
+PERLBN(kill);
+PERLBN(pipe);
+PERLBN(setpgrp);
+PERLBN(setpriority);
+PERLBN(sleep);
+PERLBN(system);
+PERLBN(times);
+PERLBN(wait);
+PERLBN(waitpid);
+
+
+/* Keywords related to perl modules */
+PERLKW(do);
+PERLKW(import);
+PERLKW(no);
+PERLKW(package);
+PERLKW(require);
+PERLKW(use);
+
+
+/* Keywords related to classes and object-orientedness */
+PERLKW(bless);
+PERLKW(dbmclose);
+PERLKW(dbmopen);
+PERLKW(package);
+PERLKW(ref);
+PERLKW(tie);
+PERLKW(tied);
+PERLKW(untie);
+PERLKW(use);
+
+/* Functions new in perl5 */
+PERLBN(abs);
+PERLBN(bless);
+PERLBN(chomp);
+PERLBN(chr);
+PERLBN(exists);
+PERLBN(formline);
+PERLBN(glob);
+PERLBN(import);
+PERLBN(lc);
+PERLBN(lcfirst);
+PERLBN(map);
+PERLBN(my);
+PERLBN(no);
+PERLBN(prototype);
+PERLBN(qx);
+PERLBN(qw);
+PERLBN(readline);
+PERLBN(readpipe);
+PERLBN(ref);
+PERLBN(sub);
+PERLBN(sysopen);
+PERLBN(tie);
+PERLBN(tied);
+PERLBN(uc);
+PERLBN(ucfirst);
+PERLBN(untie);
+PERLBN(use);
+
+#undef PERLKW
+#undef PERLBN
diff --git a/contrib/tools/swig/Lib/perl5/perlmacros.swg b/contrib/tools/swig/Lib/perl5/perlmacros.swg
new file mode 100644
index 0000000000..4917f6efc5
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perlmacros.swg
@@ -0,0 +1,2 @@
+%include <typemaps/swigmacros.swg>
+
diff --git a/contrib/tools/swig/Lib/perl5/perlopers.swg b/contrib/tools/swig/Lib/perl5/perlopers.swg
new file mode 100644
index 0000000000..e7d13b678a
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perlopers.swg
@@ -0,0 +1,54 @@
+/* ------------------------------------------------------------
+ * Overloaded operator support
+ * ------------------------------------------------------------ */
+
+#ifdef __cplusplus
+
+// These are auto-supported by the Perl-module
+%rename(__plusplus__) *::operator++;
+%rename(__minmin__) *::operator--;
+%rename(__add__) *::operator+;
+%rename(__sub__) *::operator-;
+%rename(__neg__) *::operator-();
+%rename(__neg__) *::operator-() const;
+%rename(__mul__) *::operator*;
+%rename(__div__) *::operator/;
+%rename(__eq__) *::operator==;
+%rename(__ne__) *::operator!=;
+%rename(__mod__) *::operator%;
+%rename(__gt__) *::operator>;
+%rename(__lt__) *::operator<;
+%rename(__not__) *::operator!;
+%rename(__le__) *::operator<=;
+%rename(__ge__) *::operator>=;
+%rename(__and__) *::operator&;
+%rename(__or__) *::operator|;
+%rename(__iadd__) *::operator+=;
+%rename(__isub__) *::operator-=;
+
+// These are renamed, but no test exists in operator_overload_runme.pl
+%ignoreoperator(EQ) operator=;
+
+// These are renamed, but no 'use overload...' is added
+%rename(__lshift__) *::operator<<;
+%rename(__rshift__) *::operator>>;
+%rename(__xor__) *::operator^;
+%rename(__invert__) *::operator~;
+%rename(__call__) *::operator();
+
+/* Ignored operators */
+%ignoreoperator(LAND) operator&&;
+%ignoreoperator(LOR) operator||;
+%ignoreoperator(MULEQ) operator*=;
+%ignoreoperator(DIVEQ) operator/=;
+%ignoreoperator(MODEQ) operator%=;
+%ignoreoperator(LSHIFTEQ) operator<<=;
+%ignoreoperator(RSHIFTEQ) operator>>=;
+%ignoreoperator(ANDEQ) operator&=;
+%ignoreoperator(OREQ) operator|=;
+%ignoreoperator(XOREQ) operator^=;
+%ignoreoperator(ARROWSTAR) operator->*;
+%ignoreoperator(INDEX) operator[];
+
+
+#endif /* __cplusplus */
diff --git a/contrib/tools/swig/Lib/perl5/perlprimtypes.swg b/contrib/tools/swig/Lib/perl5/perlprimtypes.swg
new file mode 100644
index 0000000000..4cb675671c
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perlprimtypes.swg
@@ -0,0 +1,364 @@
+/* ------------------------------------------------------------
+ * Primitive Types
+ * ------------------------------------------------------------ */
+
+/* bool */
+
+%fragment(SWIG_From_frag(bool),"header") {
+SWIGINTERNINLINE SV *
+SWIG_From_dec(bool)(bool value)
+{
+ return boolSV(value);
+}
+}
+
+%fragment(SWIG_AsVal_frag(bool),"header") {
+SWIGINTERN int
+SWIG_AsVal_dec(bool)(SV *obj, bool* val)
+{
+ if (obj == &PL_sv_yes) {
+ if (val) *val = true;
+ return SWIG_OK;
+ } else if (obj == &PL_sv_no) {
+ if (val) *val = false;
+ return SWIG_OK;
+ } else {
+ if (val) *val = SvTRUE(obj) ? true : false;
+ return SWIG_AddCast(SWIG_OK);
+ }
+}
+}
+
+
+/* long */
+
+%fragment(SWIG_From_frag(long),"header") {
+SWIGINTERNINLINE SV *
+SWIG_From_dec(long)(long value)
+{
+ SV *sv;
+ if (IVSIZE >= sizeof(value) || (value >= IV_MIN && value <= IV_MAX))
+ sv = newSViv(value);
+ else
+ sv = newSVpvf("%ld", value);
+ return sv_2mortal(sv);
+}
+}
+
+%fragment(SWIG_AsVal_frag(long),"header",
+ fragment="<limits.h>",
+ fragment="<stdlib.h>",
+ fragment="SWIG_CanCastAsInteger") {
+SWIGINTERN int
+SWIG_AsVal_dec(long)(SV *obj, long* val)
+{
+ if (SvUOK(obj)) {
+ UV v = SvUV(obj);
+ if (UVSIZE < sizeof(*val) || v <= LONG_MAX) {
+ if (val) *val = v;
+ return SWIG_OK;
+ }
+ return SWIG_OverflowError;
+ } else if (SvIOK(obj)) {
+ IV v = SvIV(obj);
+ if (IVSIZE <= sizeof(*val) || (v >= LONG_MIN && v <= LONG_MAX)) {
+ if(val) *val = v;
+ return SWIG_OK;
+ }
+ return SWIG_OverflowError;
+ } else {
+ int dispatch = 0;
+ const char *nptr = SvPV_nolen(obj);
+ if (nptr) {
+ char *endptr;
+ long v;
+ errno = 0;
+ v = strtol(nptr, &endptr,0);
+ if (errno == ERANGE) {
+ errno = 0;
+ return SWIG_OverflowError;
+ } else {
+ if (*endptr == '\0') {
+ if (val) *val = v;
+ return SWIG_Str2NumCast(SWIG_OK);
+ }
+ }
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) {
+ if (val) *val = (long)(d);
+ return res;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+}
+
+/* unsigned long */
+
+%fragment(SWIG_From_frag(unsigned long),"header") {
+SWIGINTERNINLINE SV *
+SWIG_From_dec(unsigned long)(unsigned long value)
+{
+ SV *sv;
+ if (UVSIZE >= sizeof(value) || value <= UV_MAX)
+ sv = newSVuv(value);
+ else
+ sv = newSVpvf("%lu", value);
+ return sv_2mortal(sv);
+}
+}
+
+%fragment(SWIG_AsVal_frag(unsigned long),"header",
+ fragment="<limits.h>",
+ fragment="<stdlib.h>",
+ fragment="SWIG_CanCastAsInteger") {
+SWIGINTERN int
+SWIG_AsVal_dec(unsigned long)(SV *obj, unsigned long *val)
+{
+ if (SvUOK(obj)) {
+ UV v = SvUV(obj);
+ if (UVSIZE <= sizeof(*val) || v <= ULONG_MAX) {
+ if (val) *val = v;
+ return SWIG_OK;
+ }
+ return SWIG_OverflowError;
+ } else if (SvIOK(obj)) {
+ IV v = SvIV(obj);
+ if (v >= 0 && (IVSIZE <= sizeof(*val) || v <= ULONG_MAX)) {
+ if (val) *val = v;
+ return SWIG_OK;
+ }
+ return SWIG_OverflowError;
+ } else {
+ int dispatch = 0;
+ const char *nptr = SvPV_nolen(obj);
+ if (nptr) {
+ char *endptr;
+ unsigned long v;
+ errno = 0;
+ v = strtoul(nptr, &endptr,0);
+ if (errno == ERANGE) {
+ errno = 0;
+ return SWIG_OverflowError;
+ } else {
+ if (*endptr == '\0') {
+ if (val) *val = v;
+ return SWIG_Str2NumCast(SWIG_OK);
+ }
+ }
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) {
+ if (val) *val = (unsigned long)(d);
+ return res;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+}
+
+/* long long */
+
+%fragment(SWIG_From_frag(long long),"header",
+ fragment="SWIG_LongLongAvailable",
+ fragment="<stdio.h>") {
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERNINLINE SV *
+SWIG_From_dec(long long)(long long value)
+{
+ SV *sv;
+ if (IVSIZE >= sizeof(value) || (value >= IV_MIN && value <= IV_MAX))
+ sv = newSViv((IV)(value));
+ else {
+ //sv = newSVpvf("%lld", value); doesn't work in non 64bit Perl
+ char temp[256];
+ sprintf(temp, "%lld", value);
+ sv = newSVpv(temp, 0);
+ }
+ return sv_2mortal(sv);
+}
+%#endif
+}
+
+%fragment(SWIG_AsVal_frag(long long),"header",
+ fragment="SWIG_LongLongAvailable",
+ fragment="<stdlib.h>",
+ fragment="SWIG_CanCastAsInteger") {
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERN int
+SWIG_AsVal_dec(long long)(SV *obj, long long *val)
+{
+ if (SvUOK(obj)) {
+ UV v = SvUV(obj);
+ /* pretty sure this could allow v == LLONG MAX */
+ if (UVSIZE < sizeof(*val) || v < LLONG_MAX) {
+ if (val) *val = v;
+ return SWIG_OK;
+ }
+ return SWIG_OverflowError;
+ } else if (SvIOK(obj)) {
+ IV v = SvIV(obj);
+ if (IVSIZE <= sizeof(*val) || (v >= LLONG_MIN && v <= LLONG_MAX)) {
+ if (val) *val = v;
+ return SWIG_OK;
+ }
+ return SWIG_OverflowError;
+ } else {
+ int dispatch = 0;
+ const char *nptr = SvPV_nolen(obj);
+ if (nptr) {
+ char *endptr;
+ long long v;
+ errno = 0;
+ v = strtoll(nptr, &endptr,0);
+ if (errno == ERANGE) {
+ errno = 0;
+ return SWIG_OverflowError;
+ } else {
+ if (*endptr == '\0') {
+ if (val) *val = v;
+ return SWIG_Str2NumCast(SWIG_OK);
+ }
+ }
+ }
+ if (!dispatch) {
+ const double mant_max = 1LL << DBL_MANT_DIG;
+ const double mant_min = -mant_max;
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, mant_min, mant_max)) {
+ if (val) *val = (long long)(d);
+ return res;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+%#endif
+}
+
+/* unsigned long long */
+
+%fragment(SWIG_From_frag(unsigned long long),"header",
+ fragment="SWIG_LongLongAvailable",
+ fragment="<stdio.h>") {
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERNINLINE SV *
+SWIG_From_dec(unsigned long long)(unsigned long long value)
+{
+ SV *sv;
+ if (UVSIZE >= sizeof(value) || value <= UV_MAX)
+ sv = newSVuv((UV)(value));
+ else {
+ //sv = newSVpvf("%llu", value); doesn't work in non 64bit Perl
+ char temp[256];
+ sprintf(temp, "%llu", value);
+ sv = newSVpv(temp, 0);
+ }
+ return sv_2mortal(sv);
+}
+%#endif
+}
+
+%fragment(SWIG_AsVal_frag(unsigned long long),"header",
+ fragment="SWIG_LongLongAvailable",
+ fragment="<stdlib.h>",
+ fragment="SWIG_CanCastAsInteger") {
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERN int
+SWIG_AsVal_dec(unsigned long long)(SV *obj, unsigned long long *val)
+{
+ if (SvUOK(obj)) {
+ /* pretty sure this should be conditional on
+ * (UVSIZE <= sizeof(*val) || v <= ULLONG_MAX) */
+ if (val) *val = SvUV(obj);
+ return SWIG_OK;
+ } else if (SvIOK(obj)) {
+ IV v = SvIV(obj);
+ if (v >= 0 && (IVSIZE <= sizeof(*val) || v <= ULLONG_MAX)) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ return SWIG_OverflowError;
+ }
+ } else {
+ int dispatch = 0;
+ const char *nptr = SvPV_nolen(obj);
+ if (nptr) {
+ char *endptr;
+ unsigned long long v;
+ errno = 0;
+ v = strtoull(nptr, &endptr,0);
+ if (errno == ERANGE) {
+ errno = 0;
+ return SWIG_OverflowError;
+ } else {
+ if (*endptr == '\0') {
+ if (val) *val = v;
+ return SWIG_Str2NumCast(SWIG_OK);
+ }
+ }
+ }
+ if (!dispatch) {
+ const double mant_max = 1LL << DBL_MANT_DIG;
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, mant_max)) {
+ if (val) *val = (unsigned long long)(d);
+ return res;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+%#endif
+}
+
+/* double */
+
+%fragment(SWIG_From_frag(double),"header") {
+SWIGINTERNINLINE SV *
+SWIG_From_dec(double)(double value)
+{
+ return sv_2mortal(newSVnv(value));
+}
+}
+
+%fragment(SWIG_AsVal_frag(double),"header") {
+SWIGINTERN int
+SWIG_AsVal_dec(double)(SV *obj, double *val)
+{
+ if (SvNIOK(obj)) {
+ if (val) *val = SvNV(obj);
+ return SWIG_OK;
+ } else if (SvIOK(obj)) {
+ if (val) *val = (double) SvIV(obj);
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ const char *nptr = SvPV_nolen(obj);
+ if (nptr) {
+ char *endptr;
+ double v;
+ errno = 0;
+ v = strtod(nptr, &endptr);
+ if (errno == ERANGE) {
+ errno = 0;
+ return SWIG_OverflowError;
+ } else {
+ if (*endptr == '\0') {
+ if (val) *val = v;
+ return SWIG_Str2NumCast(SWIG_OK);
+ }
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+}
diff --git a/contrib/tools/swig/Lib/perl5/perlruntime.swg b/contrib/tools/swig/Lib/perl5/perlruntime.swg
new file mode 100644
index 0000000000..f948023de7
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perlruntime.swg
@@ -0,0 +1,8 @@
+
+%runtime "swigrun.swg" // Common C API type-checking code
+%runtime "swigerrors.swg" // SWIG errors
+%runtime "perlhead.swg" // Perl includes and fixes
+%runtime "perlerrors.swg" // Perl errors
+%runtime "perlrun.swg" // Perl runtime functions
+%runtime "noembed.h" // undefine Perl5 macros
+
diff --git a/contrib/tools/swig/Lib/perl5/perlstrings.swg b/contrib/tools/swig/Lib/perl5/perlstrings.swg
new file mode 100644
index 0000000000..242a9c9673
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perlstrings.swg
@@ -0,0 +1,59 @@
+/* ------------------------------------------------------------
+ * utility methods for char strings
+ * ------------------------------------------------------------ */
+
+%fragment("SWIG_AsCharPtrAndSize","header",fragment="SWIG_pchar_descriptor") {
+SWIGINTERN int
+SWIG_AsCharPtrAndSize(SV *obj, char** cptr, size_t* psize, int *alloc)
+{
+ if (SvMAGICAL(obj)) {
+ SV *tmp = sv_newmortal();
+ SvSetSV(tmp, obj);
+ obj = tmp;
+ }
+ if (SvPOK(obj)) {
+ STRLEN len = 0;
+ char *cstr = SvPV(obj, len);
+ size_t size = len + 1;
+ if (cptr) {
+ if (alloc) {
+ if (*alloc == SWIG_NEWOBJ) {
+ *cptr = %new_copy_array(cstr, size, char);
+ } else {
+ *cptr = cstr;
+ *alloc = SWIG_OLDOBJ;
+ }
+ }
+ }
+ if (psize) *psize = size;
+ return SWIG_OK;
+ } else {
+ swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
+ if (pchar_descriptor) {
+ char* vptr = 0;
+ if (SWIG_ConvertPtr(obj, (void**)&vptr, pchar_descriptor, 0) == SWIG_OK) {
+ if (cptr) *cptr = vptr;
+ if (psize) *psize = vptr ? (strlen(vptr) + 1) : 0;
+ if (alloc) *alloc = SWIG_OLDOBJ;
+ return SWIG_OK;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+}
+
+%fragment("SWIG_FromCharPtrAndSize","header") {
+SWIGINTERNINLINE SV *
+SWIG_FromCharPtrAndSize(const char* carray, size_t size)
+{
+ SV *obj = sv_newmortal();
+ if (carray) {
+ sv_setpvn(obj, carray, size);
+ } else {
+ sv_setsv(obj, &PL_sv_undef);
+ }
+ return obj;
+}
+}
+
diff --git a/contrib/tools/swig/Lib/perl5/perltypemaps.swg b/contrib/tools/swig/Lib/perl5/perltypemaps.swg
new file mode 100644
index 0000000000..42f8887bef
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perltypemaps.swg
@@ -0,0 +1,104 @@
+/* ------------------------------------------------------------
+ * Typemap specializations for Perl
+ * ------------------------------------------------------------ */
+
+/* ------------------------------------------------------------
+ * Fragment section
+ * ------------------------------------------------------------ */
+
+/*
+ in Perl we need to pass the CPerlObj value, sometimes, so, we define
+ the decl/call macros as needed.
+*/
+
+#define SWIG_AS_DECL_ARGS SWIG_PERL_DECL_ARGS_2
+#define SWIG_AS_CALL_ARGS SWIG_PERL_CALL_ARGS_2
+
+#define SWIG_FROM_DECL_ARGS SWIG_PERL_DECL_ARGS_1
+#define SWIG_FROM_CALL_ARGS SWIG_PERL_CALL_ARGS_1
+
+
+/* Include fundamental fragment definitions */
+%include <typemaps/fragments.swg>
+
+/* Look for user fragments file. */
+%include <perlfragments.swg>
+
+/* Perl fragments for primitive types */
+%include <perlprimtypes.swg>
+
+/* Perl fragments for char* strings */
+%include <perlstrings.swg>
+
+
+/* ------------------------------------------------------------
+ * Unified typemap section
+ * ------------------------------------------------------------ */
+
+/* director support in Perl is experimental */
+#ifndef SWIG_DIRECTOR_TYPEMAPS
+#define SWIG_DIRECTOR_TYPEMAPS
+#endif
+
+
+/* Perl types */
+#define SWIG_Object SV *
+#define VOID_Object &PL_sv_undef
+
+/* Perl $shadow flag */
+#define %newpointer_flags $shadow
+#define %newinstance_flags $shadow
+
+
+/* Complete overload of the output/constant/exception macros */
+
+/* output */
+%define %set_output(obj) $result = obj; argvi++ %enddef
+
+/* append output */
+%define %append_output(obj)
+if (argvi >= items) EXTEND(sp, argvi+1);
+%set_output(obj) %enddef
+
+/* variable output */
+%define %set_varoutput(obj) sv_setsv($result,obj) %enddef
+
+/* constant */
+%define %set_constant(name, obj) %begin_block
+ SV *sv = get_sv((char*) SWIG_prefix name, TRUE | 0x2 | GV_ADDMULTI);
+ sv_setsv(sv, obj);
+ SvREADONLY_on(sv);
+%end_block %enddef
+
+/* raise exception */
+%define %raise(obj, type, desc) sv_setsv(get_sv("@", GV_ADD), obj); SWIG_fail %enddef
+
+/* For directors to raise/throw the original exception */
+%typemap(throws) Swig::DirectorException
+%{ sv_setsv(ERRSV, $1.getNative()); SWIG_fail; %}
+
+/* Include the unified typemap library */
+%include <typemaps/swigtypemaps.swg>
+
+/* ------------------------------------------------------------
+ * Perl extra typemaps / typemap overrides
+ * ------------------------------------------------------------ */
+
+%typemap(varout,type="$1_descriptor") SWIGTYPE *, SWIGTYPE []
+ "sv_setiv(SvRV($result),PTR2IV($1));";
+
+%typemap(varout,type="$1_descriptor") SWIGTYPE &
+ "sv_setiv(SvRV($result),PTR2IV(&$1));";
+
+%typemap(varout,type="$1_descriptor") SWIGTYPE &&
+ "sv_setiv(SvRV($result),PTR2IV(&$1));";
+
+%typemap(varout,type="$&1_descriptor") SWIGTYPE
+ "sv_setiv(SvRV($result), PTR2IV(&$1));";
+
+%typemap(varout,type="$1_descriptor") SWIGTYPE (CLASS::*) {
+ SWIG_MakePackedObj($result, (void *) &$1, sizeof($1), $1_descriptor);
+}
+
+%typemap(varout) SWIGTYPE *const = SWIGTYPE *;
+
diff --git a/contrib/tools/swig/Lib/perl5/perluserdir.swg b/contrib/tools/swig/Lib/perl5/perluserdir.swg
new file mode 100644
index 0000000000..718440e837
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/perluserdir.swg
@@ -0,0 +1,2 @@
+#define %perlcode %insert("perl")
+
diff --git a/contrib/tools/swig/Lib/perl5/reference.i b/contrib/tools/swig/Lib/perl5/reference.i
new file mode 100644
index 0000000000..b424c533b1
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/reference.i
@@ -0,0 +1,261 @@
+/* -----------------------------------------------------------------------------
+ * reference.i
+ *
+ * Accept Perl references as pointers
+ * ----------------------------------------------------------------------------- */
+
+/*
+The following methods make Perl references work like simple C
+pointers. References can only be used for simple input/output
+values, not C arrays however. It should also be noted that
+REFERENCES are specific to Perl and not supported in other
+scripting languages at this time.
+
+ int *REFERENCE
+ short *REFERENCE
+ long *REFERENCE
+ unsigned int *REFERENCE
+ unsigned short *REFERENCE
+ unsigned long *REFERENCE
+ unsigned char *REFERENCE
+ float *REFERENCE
+ double *REFERENCE
+
+For example, suppose you were trying to wrap the following function :
+
+ void neg(double *x) {
+ *x = -(*x);
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include reference.i
+ void neg(double *REFERENCE);
+
+or you can use the %apply directive :
+
+ %include reference.i
+ %apply double *REFERENCE { double *x };
+ void neg(double *x);
+
+Unlike the INOUT mapping described in typemaps.i, this approach directly
+modifies the value of a Perl reference. Thus, you could use it
+as follows :
+
+ $x = 3;
+ neg(\$x);
+ print "$x\n"; # Should print out -3.
+
+*/
+
+%typemap(in) double *REFERENCE (double dvalue), double &REFERENCE(double dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if ((!SvNOK(tempsv)) && (!SvIOK(tempsv))) {
+ printf("Received %d\n", SvTYPE(tempsv));
+ SWIG_croak("Expected a double reference.");
+ }
+ dvalue = SvNV(tempsv);
+ $1 = &dvalue;
+}
+
+%typemap(in) float *REFERENCE (float dvalue), float &REFERENCE(float dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if ((!SvNOK(tempsv)) && (!SvIOK(tempsv))) {
+ SWIG_croak("expected a double reference");
+ }
+ dvalue = (float) SvNV(tempsv);
+ $1 = &dvalue;
+}
+
+%typemap(in) int *REFERENCE (int dvalue), int &REFERENCE (int dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = SvIV(tempsv);
+ $1 = &dvalue;
+}
+
+%typemap(in) short *REFERENCE (short dvalue), short &REFERENCE(short dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = (short) SvIV(tempsv);
+ $1 = &dvalue;
+}
+%typemap(in) long *REFERENCE (long dvalue), long &REFERENCE(long dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = (long) SvIV(tempsv);
+ $1 = &dvalue;
+}
+%typemap(in) unsigned int *REFERENCE (unsigned int dvalue), unsigned int &REFERENCE(unsigned int dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = (unsigned int) SvUV(tempsv);
+ $1 = &dvalue;
+}
+%typemap(in) unsigned short *REFERENCE (unsigned short dvalue), unsigned short &REFERENCE(unsigned short dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = (unsigned short) SvUV(tempsv);
+ $1 = &dvalue;
+}
+%typemap(in) unsigned long *REFERENCE (unsigned long dvalue), unsigned long &REFERENCE(unsigned long dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = (unsigned long) SvUV(tempsv);
+ $1 = &dvalue;
+}
+
+%typemap(in) unsigned char *REFERENCE (unsigned char dvalue), unsigned char &REFERENCE(unsigned char dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = (unsigned char) SvUV(tempsv);
+ $1 = &dvalue;
+}
+
+%typemap(in) signed char *REFERENCE (signed char dvalue), signed char &REFERENCE(signed char dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = (signed char) SvIV(tempsv);
+ $1 = &dvalue;
+}
+
+%typemap(in) bool *REFERENCE (bool dvalue), bool &REFERENCE(bool dvalue)
+{
+ SV *tempsv;
+ if (!SvROK($input)) {
+ SWIG_croak("expected a reference");
+ }
+ tempsv = SvRV($input);
+ if (!SvIOK(tempsv)) {
+ SWIG_croak("expected an integer reference");
+ }
+ dvalue = SvIV(tempsv) ? true : false;
+ $1 = &dvalue;
+}
+
+%typemap(typecheck) int *REFERENCE, int &REFERENCE,
+ short *REFERENCE, short &REFERENCE,
+ long *REFERENCE, long &REFERENCE,
+ signed char *REFERENCE, signed char &REFERENCE,
+ bool *REFERENCE, bool &REFERENCE
+{
+ $1 = SvROK($input) && SvIOK(SvRV($input));
+}
+%typemap(typecheck) double *REFERENCE, double &REFERENCE,
+ float *REFERENCE, float &REFERENCE
+{
+ $1 = SvROK($input);
+ if($1) {
+ SV *tmpsv = SvRV($input);
+ $1 = SvNOK(tmpsv) || SvIOK(tmpsv);
+ }
+}
+%typemap(typecheck) unsigned int *REFERENCE, unsigned int &REFERENCE,
+ unsigned short *REFERENCE, unsigned short &REFERENCE,
+ unsigned long *REFERENCE, unsigned long &REFERENCE,
+ unsigned char *REFERENCE, unsigned char &REFERENCE
+{
+ $1 = SvROK($input);
+ if($1) {
+ SV *tmpsv = SvRV($input);
+ $1 = SvUOK(tmpsv) || SvIOK(tmpsv);
+ }
+}
+
+%typemap(argout) double *REFERENCE, double &REFERENCE,
+ float *REFERENCE, float &REFERENCE
+{
+ SV *tempsv;
+ tempsv = SvRV($arg);
+ if (!$1) SWIG_croak("expected a reference");
+ sv_setnv(tempsv, (double) *$1);
+}
+
+%typemap(argout) int *REFERENCE, int &REFERENCE,
+ short *REFERENCE, short &REFERENCE,
+ long *REFERENCE, long &REFERENCE,
+ signed char *REFERENCE, signed char &REFERENCE,
+ bool *REFERENCE, bool &REFERENCE
+{
+ SV *tempsv;
+ tempsv = SvRV($input);
+ if (!$1) SWIG_croak("expected a reference");
+ sv_setiv(tempsv, (IV) *$1);
+}
+
+%typemap(argout) unsigned int *REFERENCE, unsigned int &REFERENCE,
+ unsigned short *REFERENCE, unsigned short &REFERENCE,
+ unsigned long *REFERENCE, unsigned long &REFERENCE,
+ unsigned char *REFERENCE, unsigned char &REFERENCE
+{
+ SV *tempsv;
+ tempsv = SvRV($input);
+ if (!$1) SWIG_croak("expected a reference");
+ sv_setuv(tempsv, (UV) *$1);
+}
diff --git a/contrib/tools/swig/Lib/perl5/std_common.i b/contrib/tools/swig/Lib/perl5/std_common.i
new file mode 100644
index 0000000000..7c1ff23289
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/std_common.i
@@ -0,0 +1,28 @@
+/* -----------------------------------------------------------------------------
+ * std_common.i
+ *
+ * SWIG typemaps for STL - common utilities
+ * ----------------------------------------------------------------------------- */
+
+%include <std/std_except.i>
+
+%apply size_t { std::size_t };
+
+%fragment("<string>");
+%{
+SWIGINTERN
+double SwigSvToNumber(SV* sv) {
+ return SvIOK(sv) ? double(SvIVX(sv)) : SvNVX(sv);
+}
+SWIGINTERN
+std::string SwigSvToString(SV* sv) {
+ STRLEN len;
+ char *ptr = SvPV(sv, len);
+ return std::string(ptr, len);
+}
+SWIGINTERN
+void SwigSvFromString(SV* sv, const std::string& s) {
+ sv_setpvn(sv,s.data(),s.size());
+}
+%}
+
diff --git a/contrib/tools/swig/Lib/perl5/std_except.i b/contrib/tools/swig/Lib/perl5/std_except.i
new file mode 100644
index 0000000000..af98428f65
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/std_except.i
@@ -0,0 +1 @@
+%include <typemaps/std_except.swg>
diff --git a/contrib/tools/swig/Lib/perl5/std_string.i b/contrib/tools/swig/Lib/perl5/std_string.i
new file mode 100644
index 0000000000..6f34f18475
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/std_string.i
@@ -0,0 +1,2 @@
+%include <perlstrings.swg>
+%include <typemaps/std_string.swg>
diff --git a/contrib/tools/swig/Lib/perl5/std_vector.i b/contrib/tools/swig/Lib/perl5/std_vector.i
new file mode 100644
index 0000000000..5bfd2c5ac8
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/std_vector.i
@@ -0,0 +1,592 @@
+/* -----------------------------------------------------------------------------
+ * std_vector.i
+ *
+ * SWIG typemaps for std::vector types
+ * ----------------------------------------------------------------------------- */
+
+%include <std_common.i>
+
+// ------------------------------------------------------------------------
+// std::vector
+//
+// The aim of all that follows would be to integrate std::vector with
+// Perl as much as possible, namely, to allow the user to pass and
+// be returned Perl arrays.
+// const declarations are used to guess the intent of the function being
+// exported; therefore, the following rationale is applied:
+//
+// -- f(std::vector<T>), f(const std::vector<T>&), f(const std::vector<T>*):
+// the parameter being read-only, either a Perl sequence or a
+// previously wrapped std::vector<T> can be passed.
+// -- f(std::vector<T>&), f(std::vector<T>*):
+// the parameter must be modified; therefore, only a wrapped std::vector
+// can be passed.
+// -- std::vector<T> f():
+// the vector is returned by copy; therefore, a Perl sequence of T:s
+// is returned which is most easily used in other Perl functions
+// -- std::vector<T>& f(), std::vector<T>* f(), const std::vector<T>& f(),
+// const std::vector<T>* f():
+// the vector is returned by reference; therefore, a wrapped std::vector
+// is returned
+// ------------------------------------------------------------------------
+
+%{
+#include <vector>
+%}
+%fragment("<algorithm>");
+%fragment("<stdexcept>");
+
+// exported class
+
+namespace std {
+
+ template<class T> class vector {
+ %typemap(in) vector<T> (std::vector<T>* v) {
+ if (SWIG_ConvertPtr($input,(void **) &v,
+ $&1_descriptor,1) != -1) {
+ $1 = *v;
+ } else if (SvROK($input)) {
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) != SVt_PVAV)
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ SV **tv;
+ I32 len = av_len(av) + 1;
+ T* obj;
+ for (int i=0; i<len; i++) {
+ tv = av_fetch(av, i, 0);
+ if (SWIG_ConvertPtr(*tv, (void **)&obj,
+ $descriptor(T *),0) != -1) {
+ $1.push_back(*obj);
+ } else {
+ SWIG_croak("Type error in argument $argnum of "
+ "$symname. "
+ "Expected an array of " #T);
+ }
+ }
+ } else {
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ }
+ }
+ %typemap(in) const vector<T>& (std::vector<T> temp,
+ std::vector<T>* v),
+ const vector<T>* (std::vector<T> temp,
+ std::vector<T>* v) {
+ if (SWIG_ConvertPtr($input,(void **) &v,
+ $1_descriptor,1) != -1) {
+ $1 = v;
+ } else if (SvROK($input)) {
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) != SVt_PVAV)
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ SV **tv;
+ I32 len = av_len(av) + 1;
+ T* obj;
+ for (int i=0; i<len; i++) {
+ tv = av_fetch(av, i, 0);
+ if (SWIG_ConvertPtr(*tv, (void **)&obj,
+ $descriptor(T *),0) != -1) {
+ temp.push_back(*obj);
+ } else {
+ SWIG_croak("Type error in argument $argnum of "
+ "$symname. "
+ "Expected an array of " #T);
+ }
+ }
+ $1 = &temp;
+ } else {
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ }
+ }
+ %typemap(out) vector<T> {
+ size_t len = $1.size();
+ SV **svs = new SV*[len];
+ for (size_t i=0; i<len; i++) {
+ T* ptr = new T($1[i]);
+ svs[i] = sv_newmortal();
+ SWIG_MakePtr(svs[i], (void*) ptr,
+ $descriptor(T *), $shadow|$owner);
+ }
+ AV *myav = av_make(len, svs);
+ delete[] svs;
+ $result = newRV_noinc((SV*) myav);
+ sv_2mortal($result);
+ argvi++;
+ }
+ %typecheck(SWIG_TYPECHECK_VECTOR) vector<T> {
+ {
+ /* wrapped vector? */
+ std::vector< T >* v;
+ if (SWIG_ConvertPtr($input,(void **) &v,
+ $&1_descriptor,0) != -1) {
+ $1 = 1;
+ } else if (SvROK($input)) {
+ /* native sequence? */
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) == SVt_PVAV) {
+ I32 len = av_len(av) + 1;
+ if (len == 0) {
+ /* an empty sequence can be of any type */
+ $1 = 1;
+ } else {
+ /* check the first element only */
+ T* obj;
+ SV **tv = av_fetch(av, 0, 0);
+ if (SWIG_ConvertPtr(*tv, (void **)&obj,
+ $descriptor(T *),0) != -1)
+ $1 = 1;
+ else
+ $1 = 0;
+ }
+ }
+ } else {
+ $1 = 0;
+ }
+ }
+ }
+ %typecheck(SWIG_TYPECHECK_VECTOR) const vector<T>&,
+ const vector<T>* {
+ {
+ /* wrapped vector? */
+ std::vector< T >* v;
+ if (SWIG_ConvertPtr($input,(void **) &v,
+ $1_descriptor,0) != -1) {
+ $1 = 1;
+ } else if (SvROK($input)) {
+ /* native sequence? */
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) == SVt_PVAV) {
+ I32 len = av_len(av) + 1;
+ if (len == 0) {
+ /* an empty sequence can be of any type */
+ $1 = 1;
+ } else {
+ /* check the first element only */
+ T* obj;
+ SV **tv = av_fetch(av, 0, 0);
+ if (SWIG_ConvertPtr(*tv, (void **)&obj,
+ $descriptor(T *),0) != -1)
+ $1 = 1;
+ else
+ $1 = 0;
+ }
+ }
+ } else {
+ $1 = 0;
+ }
+ }
+ }
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ vector(unsigned int size = 0);
+ vector(unsigned int size, const T& value);
+ vector(const vector& other);
+
+ unsigned int size() const;
+ bool empty() const;
+ void clear();
+ %rename(push) push_back;
+ void push_back(const T& x);
+ %extend {
+ T pop() throw (std::out_of_range) {
+ if (self->size() == 0)
+ throw std::out_of_range("pop from empty vector");
+ T x = self->back();
+ self->pop_back();
+ return x;
+ }
+ T& get(int i) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ return (*self)[i];
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ void set(int i, const T& x) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ (*self)[i] = x;
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ }
+ };
+
+ // specializations for pointers
+ template<class T> class vector<T*> {
+ %typemap(in) vector<T*> (std::vector<T*>* v) {
+ int res = SWIG_ConvertPtr($input,(void **) &v, $&1_descriptor,0);
+ if (SWIG_IsOK(res)){
+ $1 = *v;
+ } else if (SvROK($input)) {
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) != SVt_PVAV)
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ I32 len = av_len(av) + 1;
+ for (int i=0; i<len; i++) {
+ void *v;
+ SV **tv = av_fetch(av, i, 0);
+ int res = SWIG_ConvertPtr(*tv, &v, $descriptor(T *),0);
+ if (SWIG_IsOK(res)) {
+ $1.push_back(%static_cast(v, T *));
+ } else {
+ SWIG_croak("Type error in argument $argnum of "
+ "$symname. "
+ "Expected an array of " #T);
+ }
+ }
+ } else {
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ }
+ }
+ %typemap(in) const vector<T *>& (std::vector<T *> temp,std::vector<T *>* v),
+ const vector<T *>* (std::vector<T *> temp,std::vector<T *>* v) {
+ int res = SWIG_ConvertPtr($input,(void **) &v, $1_descriptor,0);
+ if (SWIG_IsOK(res)) {
+ $1 = v;
+ } else if (SvROK($input)) {
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) != SVt_PVAV)
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ I32 len = av_len(av) + 1;
+ for (int i=0; i<len; i++) {
+ void *v;
+ SV **tv = av_fetch(av, i, 0);
+ int res = SWIG_ConvertPtr(*tv, &v, $descriptor(T *),0);
+ if (SWIG_IsOK(res)) {
+ temp.push_back(%static_cast(v, T *));
+ } else {
+ SWIG_croak("Type error in argument $argnum of "
+ "$symname. "
+ "Expected an array of " #T);
+ }
+ }
+ $1 = &temp;
+ } else {
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ }
+ }
+ %typemap(out) vector<T *> {
+ size_t len = $1.size();
+ SV **svs = new SV*[len];
+ for (size_t i=0; i<len; i++) {
+ T *x = (($1_type &)$1)[i];
+ svs[i] = sv_newmortal();
+ sv_setsv(svs[i], SWIG_NewPointerObj(x, $descriptor(T *), 0));
+ }
+ AV *myav = av_make(len, svs);
+ delete[] svs;
+ $result = newRV_noinc((SV*) myav);
+ sv_2mortal($result);
+ argvi++;
+ }
+ %typecheck(SWIG_TYPECHECK_VECTOR) vector<T *> {
+ {
+ /* wrapped vector? */
+ std::vector< T *>* v;
+ int res = SWIG_ConvertPtr($input,(void **) &v, $&1_descriptor,0);
+ if (SWIG_IsOK(res)) {
+ $1 = 1;
+ } else if (SvROK($input)) {
+ /* native sequence? */
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) == SVt_PVAV) {
+ I32 len = av_len(av) + 1;
+ if (len == 0) {
+ /* an empty sequence can be of any type */
+ $1 = 1;
+ } else {
+ /* check the first element only */
+ void *v;
+ SV **tv = av_fetch(av, 0, 0);
+ int res = SWIG_ConvertPtr(*tv, &v, $descriptor(T *),0);
+ if (SWIG_IsOK(res))
+ $1 = 1;
+ else
+ $1 = 0;
+ }
+ }
+ } else {
+ $1 = 0;
+ }
+ }
+ }
+ %typecheck(SWIG_TYPECHECK_VECTOR) const vector<T *>&,const vector<T *>* {
+ {
+ /* wrapped vector? */
+ std::vector< T *> *v;
+ int res = SWIG_ConvertPtr($input,%as_voidptrptr(&v), $1_descriptor,0);
+ if (SWIG_IsOK(res)) {
+ $1 = 1;
+ } else if (SvROK($input)) {
+ /* native sequence? */
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) == SVt_PVAV) {
+ I32 len = av_len(av) + 1;
+ if (len == 0) {
+ /* an empty sequence can be of any type */
+ $1 = 1;
+ } else {
+ /* check the first element only */
+ void *v;
+ SV **tv = av_fetch(av, 0, 0);
+ int res = SWIG_ConvertPtr(*tv, &v, $descriptor(T *),0);
+ if (SWIG_IsOK(res))
+ $1 = 1;
+ else
+ $1 = 0;
+ }
+ }
+ } else {
+ $1 = 0;
+ }
+ }
+ }
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ vector(unsigned int size = 0);
+ vector(unsigned int size, T *value);
+ vector(const vector& other);
+
+ unsigned int size() const;
+ bool empty() const;
+ void clear();
+ %rename(push) push_back;
+ void push_back(T *x);
+ %extend {
+ T *pop() throw (std::out_of_range) {
+ if (self->size() == 0)
+ throw std::out_of_range("pop from empty vector");
+ T *x = self->back();
+ self->pop_back();
+ return x;
+ }
+ T *get(int i) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ return (*self)[i];
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ void set(int i, T *x) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ (*self)[i] = x;
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ }
+ };
+
+
+ // specializations for built-ins
+
+ %define specialize_std_vector(T,CHECK_T,TO_T,FROM_T)
+ template<> class vector<T> {
+ %typemap(in) vector<T> (std::vector<T>* v) {
+ if (SWIG_ConvertPtr($input,(void **) &v,
+ $&1_descriptor,1) != -1){
+ $1 = *v;
+ } else if (SvROK($input)) {
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) != SVt_PVAV)
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ SV **tv;
+ I32 len = av_len(av) + 1;
+ for (int i=0; i<len; i++) {
+ tv = av_fetch(av, i, 0);
+ if (CHECK_T(*tv)) {
+ $1.push_back((T)TO_T(*tv));
+ } else {
+ SWIG_croak("Type error in argument $argnum of "
+ "$symname. "
+ "Expected an array of " #T);
+ }
+ }
+ } else {
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ }
+ }
+ %typemap(in) const vector<T>& (std::vector<T> temp,
+ std::vector<T>* v),
+ const vector<T>* (std::vector<T> temp,
+ std::vector<T>* v) {
+ if (SWIG_ConvertPtr($input,(void **) &v,
+ $1_descriptor,1) != -1) {
+ $1 = v;
+ } else if (SvROK($input)) {
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) != SVt_PVAV)
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ SV **tv;
+ I32 len = av_len(av) + 1;
+ for (int i=0; i<len; i++) {
+ tv = av_fetch(av, i, 0);
+ if (CHECK_T(*tv)) {
+ temp.push_back((T)TO_T(*tv));
+ } else {
+ SWIG_croak("Type error in argument $argnum of "
+ "$symname. "
+ "Expected an array of " #T);
+ }
+ }
+ $1 = &temp;
+ } else {
+ SWIG_croak("Type error in argument $argnum of $symname. "
+ "Expected an array of " #T);
+ }
+ }
+ %typemap(out) vector<T> {
+ size_t len = $1.size();
+ SV **svs = new SV*[len];
+ for (size_t i=0; i<len; i++) {
+ svs[i] = sv_newmortal();
+ FROM_T(svs[i], $1[i]);
+ }
+ AV *myav = av_make(len, svs);
+ delete[] svs;
+ $result = newRV_noinc((SV*) myav);
+ sv_2mortal($result);
+ argvi++;
+ }
+ %typecheck(SWIG_TYPECHECK_VECTOR) vector<T> {
+ {
+ /* wrapped vector? */
+ std::vector< T >* v;
+ if (SWIG_ConvertPtr($input,(void **) &v,
+ $&1_descriptor,0) != -1) {
+ $1 = 1;
+ } else if (SvROK($input)) {
+ /* native sequence? */
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) == SVt_PVAV) {
+ I32 len = av_len(av) + 1;
+ if (len == 0) {
+ /* an empty sequence can be of any type */
+ $1 = 1;
+ } else {
+ /* check the first element only */
+ SV **tv = av_fetch(av, 0, 0);
+ if (CHECK_T(*tv))
+ $1 = 1;
+ else
+ $1 = 0;
+ }
+ }
+ } else {
+ $1 = 0;
+ }
+ }
+ }
+ %typecheck(SWIG_TYPECHECK_VECTOR) const vector<T>&,
+ const vector<T>* {
+ {
+ /* wrapped vector? */
+ std::vector< T >* v;
+ if (SWIG_ConvertPtr($input,(void **) &v,
+ $1_descriptor,0) != -1) {
+ $1 = 1;
+ } else if (SvROK($input)) {
+ /* native sequence? */
+ AV *av = (AV *)SvRV($input);
+ if (SvTYPE(av) == SVt_PVAV) {
+ I32 len = av_len(av) + 1;
+ if (len == 0) {
+ /* an empty sequence can be of any type */
+ $1 = 1;
+ } else {
+ /* check the first element only */
+ SV **tv = av_fetch(av, 0, 0);
+ if (CHECK_T(*tv))
+ $1 = 1;
+ else
+ $1 = 0;
+ }
+ }
+ } else {
+ $1 = 0;
+ }
+ }
+ }
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ vector(unsigned int size = 0);
+ vector(unsigned int size, T value);
+ vector(const vector& other);
+
+ unsigned int size() const;
+ bool empty() const;
+ void clear();
+ %rename(push) push_back;
+ void push_back(T x);
+ %extend {
+ T pop() throw (std::out_of_range) {
+ if (self->size() == 0)
+ throw std::out_of_range("pop from empty vector");
+ T x = self->back();
+ self->pop_back();
+ return x;
+ }
+ T get(int i) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ return (*self)[i];
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ void set(int i, T x) throw (std::out_of_range) {
+ int size = int(self->size());
+ if (i>=0 && i<size)
+ (*self)[i] = x;
+ else
+ throw std::out_of_range("vector index out of range");
+ }
+ }
+ };
+ %enddef
+
+ specialize_std_vector(bool,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(char,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(int,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(short,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(long,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(unsigned char,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(unsigned int,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(unsigned short,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(unsigned long,SvIOK,SvIVX,sv_setiv);
+ specialize_std_vector(float,SvNIOK,SwigSvToNumber,sv_setnv);
+ specialize_std_vector(double,SvNIOK,SwigSvToNumber,sv_setnv);
+ specialize_std_vector(std::string,SvPOK,SwigSvToString,SwigSvFromString);
+}
+
diff --git a/contrib/tools/swig/Lib/perl5/typemaps.i b/contrib/tools/swig/Lib/perl5/typemaps.i
new file mode 100644
index 0000000000..3e1f60d904
--- /dev/null
+++ b/contrib/tools/swig/Lib/perl5/typemaps.i
@@ -0,0 +1,371 @@
+/* -----------------------------------------------------------------------------
+ * typemaps.i
+ *
+ * The SWIG typemap library provides a language independent mechanism for
+ * supporting output arguments, input values, and other C function
+ * calling mechanisms. The primary use of the library is to provide a
+ * better interface to certain C function--especially those involving
+ * pointers.
+ * ----------------------------------------------------------------------------- */
+
+#if !defined(SWIG_USE_OLD_TYPEMAPS)
+%include <typemaps/typemaps.swg>
+#else
+
+
+// INPUT typemaps.
+// These remap a C pointer to be an "INPUT" value which is passed by value
+// instead of reference.
+
+
+/*
+The following methods can be applied to turn a pointer into a simple
+"input" value. That is, instead of passing a pointer to an object,
+you would use a real value instead.
+
+ int *INPUT
+ short *INPUT
+ long *INPUT
+ long long *INPUT
+ unsigned int *INPUT
+ unsigned short *INPUT
+ unsigned long *INPUT
+ unsigned long long *INPUT
+ unsigned char *INPUT
+ bool *INPUT
+ float *INPUT
+ double *INPUT
+
+To use these, suppose you had a C function like this :
+
+ double fadd(double *a, double *b) {
+ return *a+*b;
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include typemaps.i
+ double fadd(double *INPUT, double *INPUT);
+
+or you can use the %apply directive :
+
+ %include typemaps.i
+ %apply double *INPUT { double *a, double *b };
+ double fadd(double *a, double *b);
+
+*/
+
+%define INPUT_TYPEMAP(type, converter)
+%typemap(in) type *INPUT(type temp), type &INPUT(type temp) {
+ temp = (type) converter($input);
+ $1 = &temp;
+}
+%typemap(typecheck) type *INPUT = type;
+%typemap(typecheck) type &INPUT = type;
+%enddef
+
+INPUT_TYPEMAP(float, SvNV);
+INPUT_TYPEMAP(double, SvNV);
+INPUT_TYPEMAP(int, SvIV);
+INPUT_TYPEMAP(long, SvIV);
+INPUT_TYPEMAP(short, SvIV);
+INPUT_TYPEMAP(signed char, SvIV);
+INPUT_TYPEMAP(unsigned int, SvUV);
+INPUT_TYPEMAP(unsigned long, SvUV);
+INPUT_TYPEMAP(unsigned short, SvUV);
+INPUT_TYPEMAP(unsigned char, SvUV);
+
+%typemap(in) bool *INPUT(bool temp), bool &INPUT(bool temp) {
+ temp = SvIV($input) ? true : false;
+ $1 = &temp;
+}
+%typemap(typecheck) bool *INPUT = bool;
+%typemap(typecheck) bool &INPUT = bool;
+
+%typemap(in) long long *INPUT($*1_ltype temp), long long &INPUT($*1_ltype temp) {
+ temp = strtoll(SvPV_nolen($input), 0, 0);
+ $1 = &temp;
+}
+%typemap(typecheck) long long *INPUT = long long;
+%typemap(typecheck) long long &INPUT = long long;
+
+%typemap(in) unsigned long long *INPUT($*1_ltype temp), unsigned long long &INPUT($*1_ltype temp) {
+ temp = strtoull(SvPV_nolen($input), 0, 0);
+ $1 = &temp;
+}
+%typemap(typecheck) unsigned long long *INPUT = unsigned long long;
+%typemap(typecheck) unsigned long long &INPUT = unsigned long long;
+
+
+#undef INPUT_TYPEMAP
+
+// OUTPUT typemaps. These typemaps are used for parameters that
+// are output only. The output value is appended to the result as
+// a list element.
+
+/*
+The following methods can be applied to turn a pointer into an "output"
+value. When calling a function, no input value would be given for
+a parameter, but an output value would be returned. In the case of
+multiple output values, functions will return a Perl array.
+
+ int *OUTPUT
+ short *OUTPUT
+ long *OUTPUT
+ long long *OUTPUT
+ unsigned int *OUTPUT
+ unsigned short *OUTPUT
+ unsigned long *OUTPUT
+ unsigned long long *OUTPUT
+ unsigned char *OUTPUT
+ bool *OUTPUT
+ float *OUTPUT
+ double *OUTPUT
+
+For example, suppose you were trying to wrap the modf() function in the
+C math library which splits x into integral and fractional parts (and
+returns the integer part in one of its parameters).:
+
+ double modf(double x, double *ip);
+
+You could wrap it with SWIG as follows :
+
+ %include typemaps.i
+ double modf(double x, double *OUTPUT);
+
+or you can use the %apply directive :
+
+ %include typemaps.i
+ %apply double *OUTPUT { double *ip };
+ double modf(double x, double *ip);
+
+The Perl output of the function would be an array containing both
+output values.
+
+*/
+
+// Force the argument to be ignored.
+
+%typemap(in,numinputs=0) int *OUTPUT(int temp), int &OUTPUT(int temp),
+ short *OUTPUT(short temp), short &OUTPUT(short temp),
+ long *OUTPUT(long temp), long &OUTPUT(long temp),
+ unsigned int *OUTPUT(unsigned int temp), unsigned int &OUTPUT(unsigned int temp),
+ unsigned short *OUTPUT(unsigned short temp), unsigned short &OUTPUT(unsigned short temp),
+ unsigned long *OUTPUT(unsigned long temp), unsigned long &OUTPUT(unsigned long temp),
+ unsigned char *OUTPUT(unsigned char temp), unsigned char &OUTPUT(unsigned char temp),
+ signed char *OUTPUT(signed char temp), signed char &OUTPUT(signed char temp),
+ bool *OUTPUT(bool temp), bool &OUTPUT(bool temp),
+ float *OUTPUT(float temp), float &OUTPUT(float temp),
+ double *OUTPUT(double temp), double &OUTPUT(double temp),
+ long long *OUTPUT($*1_ltype temp), long long &OUTPUT($*1_ltype temp),
+ unsigned long long *OUTPUT($*1_ltype temp), unsigned long long &OUTPUT($*1_ltype temp)
+"$1 = &temp;";
+
+%typemap(argout) int *OUTPUT, int &OUTPUT,
+ short *OUTPUT, short &OUTPUT,
+ long *OUTPUT, long &OUTPUT,
+ signed char *OUTPUT, signed char &OUTPUT,
+ bool *OUTPUT, bool &OUTPUT
+{
+ if (argvi >= items) {
+ EXTEND(sp, argvi+1);
+ }
+ $result = sv_newmortal();
+ sv_setiv($result,(IV) *($1));
+ argvi++;
+}
+
+%typemap(argout) unsigned int *OUTPUT, unsigned int &OUTPUT,
+ unsigned short *OUTPUT, unsigned short &OUTPUT,
+ unsigned long *OUTPUT, unsigned long &OUTPUT,
+ unsigned char *OUTPUT, unsigned char &OUTPUT
+{
+ if (argvi >= items) {
+ EXTEND(sp, argvi+1);
+ }
+ $result = sv_newmortal();
+ sv_setuv($result,(UV) *($1));
+ argvi++;
+}
+
+
+
+%typemap(argout) float *OUTPUT, float &OUTPUT,
+ double *OUTPUT, double &OUTPUT
+{
+ if (argvi >= items) {
+ EXTEND(sp, argvi+1);
+ }
+ $result = sv_newmortal();
+ sv_setnv($result,(double) *($1));
+ argvi++;
+}
+
+%typemap(argout) long long *OUTPUT, long long &OUTPUT {
+ char temp[256];
+ if (argvi >= items) {
+ EXTEND(sp, argvi+1);
+ }
+ sprintf(temp,"%lld", (long long)*($1));
+ $result = sv_newmortal();
+ sv_setpv($result,temp);
+ argvi++;
+}
+
+%typemap(argout) unsigned long long *OUTPUT, unsigned long long &OUTPUT {
+ char temp[256];
+ if (argvi >= items) {
+ EXTEND(sp, argvi+1);
+ }
+ sprintf(temp,"%llu", (unsigned long long)*($1));
+ $result = sv_newmortal();
+ sv_setpv($result,temp);
+ argvi++;
+}
+
+// INOUT
+// Mappings for an argument that is both an input and output
+// parameter
+
+/*
+The following methods can be applied to make a function parameter both
+an input and output value. This combines the behavior of both the
+"INPUT" and "OUTPUT" methods described earlier. Output values are
+returned in the form of a Perl array.
+
+ int *INOUT
+ short *INOUT
+ long *INOUT
+ long long *INOUT
+ unsigned int *INOUT
+ unsigned short *INOUT
+ unsigned long *INOUT
+ unsigned long long *INOUT
+ unsigned char *INOUT
+ bool *INOUT
+ float *INOUT
+ double *INOUT
+
+For example, suppose you were trying to wrap the following function :
+
+ void neg(double *x) {
+ *x = -(*x);
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include typemaps.i
+ void neg(double *INOUT);
+
+or you can use the %apply directive :
+
+ %include typemaps.i
+ %apply double *INOUT { double *x };
+ void neg(double *x);
+
+Unlike C, this mapping does not directly modify the input value.
+Rather, the modified input value shows up as the return value of the
+function. Thus, to apply this function to a Perl variable you might
+do this :
+
+ $x = neg($x);
+
+*/
+
+%typemap(in) int *INOUT = int *INPUT;
+%typemap(in) short *INOUT = short *INPUT;
+%typemap(in) long *INOUT = long *INPUT;
+%typemap(in) unsigned *INOUT = unsigned *INPUT;
+%typemap(in) unsigned short *INOUT = unsigned short *INPUT;
+%typemap(in) unsigned long *INOUT = unsigned long *INPUT;
+%typemap(in) unsigned char *INOUT = unsigned char *INPUT;
+%typemap(in) signed char *INOUT = signed char *INPUT;
+%typemap(in) bool *INOUT = bool *INPUT;
+%typemap(in) float *INOUT = float *INPUT;
+%typemap(in) double *INOUT = double *INPUT;
+%typemap(in) long long *INOUT = long long *INPUT;
+%typemap(in) unsigned long long *INOUT = unsigned long long *INPUT;
+
+%typemap(in) int &INOUT = int &INPUT;
+%typemap(in) short &INOUT = short &INPUT;
+%typemap(in) long &INOUT = long &INPUT;
+%typemap(in) unsigned &INOUT = unsigned &INPUT;
+%typemap(in) unsigned short &INOUT = unsigned short &INPUT;
+%typemap(in) unsigned long &INOUT = unsigned long &INPUT;
+%typemap(in) unsigned char &INOUT = unsigned char &INPUT;
+%typemap(in) signed char &INOUT = signed char &INPUT;
+%typemap(in) bool &INOUT = bool &INPUT;
+%typemap(in) float &INOUT = float &INPUT;
+%typemap(in) double &INOUT = double &INPUT;
+%typemap(in) long long &INOUT = long long &INPUT;
+%typemap(in) unsigned long long &INOUT = unsigned long long &INPUT;
+
+
+%typemap(argout) int *INOUT = int *OUTPUT;
+%typemap(argout) short *INOUT = short *OUTPUT;
+%typemap(argout) long *INOUT = long *OUTPUT;
+%typemap(argout) unsigned *INOUT = unsigned *OUTPUT;
+%typemap(argout) unsigned short *INOUT = unsigned short *OUTPUT;
+%typemap(argout) unsigned long *INOUT = unsigned long *OUTPUT;
+%typemap(argout) unsigned char *INOUT = unsigned char *OUTPUT;
+%typemap(argout) signed char *INOUT = signed char *OUTPUT;
+%typemap(argout) bool *INOUT = bool *OUTPUT;
+%typemap(argout) float *INOUT = float *OUTPUT;
+%typemap(argout) double *INOUT = double *OUTPUT;
+%typemap(argout) long long *INOUT = long long *OUTPUT;
+%typemap(argout) unsigned long long *INOUT = unsigned long long *OUTPUT;
+
+
+%typemap(argout) int &INOUT = int &OUTPUT;
+%typemap(argout) short &INOUT = short &OUTPUT;
+%typemap(argout) long &INOUT = long &OUTPUT;
+%typemap(argout) unsigned &INOUT = unsigned &OUTPUT;
+%typemap(argout) unsigned short &INOUT = unsigned short &OUTPUT;
+%typemap(argout) unsigned long &INOUT = unsigned long &OUTPUT;
+%typemap(argout) unsigned char &INOUT = unsigned char &OUTPUT;
+%typemap(argout) signed char &INOUT = signed char &OUTPUT;
+%typemap(argout) bool &INOUT = bool &OUTPUT;
+%typemap(argout) float &INOUT = float &OUTPUT;
+%typemap(argout) double &INOUT = double &OUTPUT;
+%typemap(argout) long long &INOUT = long long &OUTPUT;
+%typemap(argout) unsigned long long &INOUT = unsigned long long &OUTPUT;
+
+
+/* Overloading information */
+
+%typemap(typecheck) double *INOUT = double;
+%typemap(typecheck) bool *INOUT = bool;
+%typemap(typecheck) signed char *INOUT = signed char;
+%typemap(typecheck) unsigned char *INOUT = unsigned char;
+%typemap(typecheck) unsigned long *INOUT = unsigned long;
+%typemap(typecheck) unsigned short *INOUT = unsigned short;
+%typemap(typecheck) unsigned int *INOUT = unsigned int;
+%typemap(typecheck) long *INOUT = long;
+%typemap(typecheck) short *INOUT = short;
+%typemap(typecheck) int *INOUT = int;
+%typemap(typecheck) float *INOUT = float;
+%typemap(typecheck) long long *INOUT = long long;
+%typemap(typecheck) unsigned long long *INOUT = unsigned long long;
+
+%typemap(typecheck) double &INOUT = double;
+%typemap(typecheck) bool &INOUT = bool;
+%typemap(typecheck) signed char &INOUT = signed char;
+%typemap(typecheck) unsigned char &INOUT = unsigned char;
+%typemap(typecheck) unsigned long &INOUT = unsigned long;
+%typemap(typecheck) unsigned short &INOUT = unsigned short;
+%typemap(typecheck) unsigned int &INOUT = unsigned int;
+%typemap(typecheck) long &INOUT = long;
+%typemap(typecheck) short &INOUT = short;
+%typemap(typecheck) int &INOUT = int;
+%typemap(typecheck) float &INOUT = float;
+%typemap(typecheck) long long &INOUT = long long;
+%typemap(typecheck) unsigned long long &INOUT = unsigned long long;
+
+#endif
+
+// --------------------------------------------------------------------
+// Special types
+// --------------------------------------------------------------------
+
+
+%include <reference.i>
diff --git a/contrib/tools/swig/Lib/python/README b/contrib/tools/swig/Lib/python/README
new file mode 100644
index 0000000000..70968e7dd5
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/README
@@ -0,0 +1,103 @@
+/* -----------------------------------------------------------------------------
+ *
+ * User interfaces: include these ones as needed
+ *
+ * ----------------------------------------------------------------------------- */
+
+/* -----------------------------------------------------------------------------
+ * Special types and user helpers
+ * ----------------------------------------------------------------------------- */
+
+argcargv.i Handler for (int argc, char **argv)
+attribute.i Convert a pair of set/get methods into a "native" python attribute
+ccomplex.i C99 complex type
+complex.i C99 or C++ complex type
+cstring.i Various forms of C character string handling
+cwstring.i Various forms of C wchar_t string handling
+embed.i embedding the Python interpreter in something else
+file.i FILE C type
+implicit.i Allow the use of implicit C++ constructors
+wchar.i wchar_t C type
+
+/* -----------------------------------------------------------------------------
+ * C++ STD + STL
+ * ----------------------------------------------------------------------------- */
+
+std_alloc.i allocator
+std_basic_string.i basic string
+std_char_traits.i char traits
+std_complex.i complex
+std_deque.i deque
+std_except.i exceptions
+std_ios.i ios
+std_iostream.i istream/ostream
+std_list.i list
+std_map.i map
+std_multimap.i multimap
+std_multiset.i multiset
+std_pair.i pair
+std_set.i set
+std_sstream.i string stream
+std_streambuf.i streambuf
+std_string.i string
+std_vector.i vector
+std_wios.i wios
+std_wiostream.i wistream/wostream
+std_wsstream.i wstring stream
+std_wstreambuf.i wstreambuf
+std_wstring.i wstring
+
+
+
+/* -----------------------------------------------------------------------------
+/*
+ * Implementation files: don't look at them unless you are really drunk
+ *
+ * ----------------------------------------------------------------------------- */
+
+/* -----------------------------------------------------------------------------
+ * Basic files
+ * ----------------------------------------------------------------------------- */
+
+python.swg Main language file, it just includes what is needed.
+pyuserdir.swg User visible directives (%pythonnondynamic, etc)
+pymacros.swg Internal macros used for typemaps
+pyfragments.swg Allow the user to overload the default fragments
+pyopers.swg Python operations (+=, *=, etc)
+pythonkw.swg Python keywords and special names
+pyinit.swg Python Init method
+
+/* -----------------------------------------------------------------------------
+ * The runtime part
+ * ----------------------------------------------------------------------------- */
+
+pyruntime.swg Main runtime file definition
+pyapi.swg SWIG/Python API declarations
+pyrun.swg Python run-time code
+
+/* -----------------------------------------------------------------------------
+ * Internal typemap specializations
+ * ----------------------------------------------------------------------------- */
+
+pyswigtype.swg SWIGTYPE
+pystrings.swg Char strings (char *)
+pywstrings.swg Wchar Strings (wchar_t *)
+pyprimtypes.swg Primitive types (shot,int,double,etc)
+pycomplex.swg PyComplex and helper for C/C++ complex types
+pydocs.swg Typemaps documentation
+
+/* -----------------------------------------------------------------------------
+ * C++ STD + STL
+ * ----------------------------------------------------------------------------- */
+
+pycontainer.swg python container iterators
+std_common.i general common code for the STD/STL implementation
+std_container.i general common code for the STD/STL containers
+
+
+/*-----------------------------------------------------------------------------
+ * Backward compatibility and deprecated
+ * ----------------------------------------------------------------------------- */
+
+std_vectora.i vector + allocator (allocators are now supported in STD/STL)
+typemaps.i old in/out typemaps (doesn't need to be included)
diff --git a/contrib/tools/swig/Lib/python/builtin.swg b/contrib/tools/swig/Lib/python/builtin.swg
new file mode 100644
index 0000000000..5cff6835f8
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/builtin.swg
@@ -0,0 +1,764 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SWIGINTERN Py_hash_t
+SwigPyObject_hash(PyObject *obj) {
+ SwigPyObject *sobj = (SwigPyObject *)obj;
+ void *ptr = sobj->ptr;
+#if PY_VERSION_HEX < 0x03020000
+ return (Py_hash_t)(Py_ssize_t)ptr;
+#else
+ return (Py_hash_t)ptr;
+#endif
+}
+
+SWIGINTERN Py_hash_t
+SWIG_PyNumber_AsPyHash(PyObject *obj) {
+ Py_hash_t result = -1;
+#if PY_VERSION_HEX < 0x03020000
+ if (PyInt_Check(obj))
+ result = PyInt_AsLong(obj);
+ else if (PyLong_Check(obj))
+ result = PyLong_AsLong(obj);
+#else
+ if (PyNumber_Check(obj))
+ result = PyNumber_AsSsize_t(obj, NULL);
+#endif
+ else
+ PyErr_Format(PyExc_TypeError, "Wrong type for hash function");
+ return PyErr_Occurred() ? -1 : result;
+}
+
+SWIGINTERN int
+SwigPyBuiltin_BadInit(PyObject *self, PyObject *SWIGUNUSEDPARM(args), PyObject *SWIGUNUSEDPARM(kwds)) {
+ PyErr_Format(PyExc_TypeError, "Cannot create new instances of type '%.300s'", self->ob_type->tp_name);
+ return -1;
+}
+
+SWIGINTERN void
+SwigPyBuiltin_BadDealloc(PyObject *obj) {
+ SwigPyObject *sobj = (SwigPyObject *)obj;
+ if (sobj->own) {
+ PyErr_Format(PyExc_TypeError, "Swig detected a memory leak in type '%.300s': no callable destructor found.", obj->ob_type->tp_name);
+ }
+}
+
+typedef struct {
+ PyCFunction get;
+ PyCFunction set;
+} SwigPyGetSet;
+
+SWIGINTERN PyObject *
+SwigPyBuiltin_GetterClosure (PyObject *obj, void *closure) {
+ SwigPyGetSet *getset;
+ PyObject *tuple, *result;
+ if (!closure)
+ return SWIG_Py_Void();
+ getset = (SwigPyGetSet *)closure;
+ if (!getset->get)
+ return SWIG_Py_Void();
+ tuple = PyTuple_New(0);
+ assert(tuple);
+ result = (*getset->get)(obj, tuple);
+ Py_DECREF(tuple);
+ return result;
+}
+
+SWIGINTERN PyObject *
+SwigPyBuiltin_FunpackGetterClosure (PyObject *obj, void *closure) {
+ SwigPyGetSet *getset;
+ PyObject *result;
+ if (!closure)
+ return SWIG_Py_Void();
+ getset = (SwigPyGetSet *)closure;
+ if (!getset->get)
+ return SWIG_Py_Void();
+ result = (*getset->get)(obj, NULL);
+ return result;
+}
+
+SWIGINTERN int
+SwigPyBuiltin_SetterClosure (PyObject *obj, PyObject *val, void *closure) {
+ SwigPyGetSet *getset;
+ PyObject *tuple, *result;
+ if (!closure) {
+ PyErr_Format(PyExc_TypeError, "Missing getset closure");
+ return -1;
+ }
+ getset = (SwigPyGetSet *)closure;
+ if (!getset->set) {
+ PyErr_Format(PyExc_TypeError, "Illegal member variable assignment in type '%.300s'", obj->ob_type->tp_name);
+ return -1;
+ }
+ tuple = PyTuple_New(1);
+ assert(tuple);
+ Py_INCREF(val);
+ PyTuple_SET_ITEM(tuple, 0, val);
+ result = (*getset->set)(obj, tuple);
+ Py_DECREF(tuple);
+ Py_XDECREF(result);
+ return result ? 0 : -1;
+}
+
+SWIGINTERN int
+SwigPyBuiltin_FunpackSetterClosure (PyObject *obj, PyObject *val, void *closure) {
+ SwigPyGetSet *getset;
+ PyObject *result;
+ if (!closure) {
+ PyErr_Format(PyExc_TypeError, "Missing getset closure");
+ return -1;
+ }
+ getset = (SwigPyGetSet *)closure;
+ if (!getset->set) {
+ PyErr_Format(PyExc_TypeError, "Illegal member variable assignment in type '%.300s'", obj->ob_type->tp_name);
+ return -1;
+ }
+ result = (*getset->set)(obj, val);
+ Py_XDECREF(result);
+ return result ? 0 : -1;
+}
+
+SWIGINTERN void
+SwigPyStaticVar_dealloc(PyDescrObject *descr) {
+ PyObject_GC_UnTrack(descr);
+ Py_XDECREF(PyDescr_TYPE(descr));
+ Py_XDECREF(PyDescr_NAME(descr));
+ PyObject_GC_Del(descr);
+}
+
+SWIGINTERN PyObject *
+SwigPyStaticVar_repr(PyGetSetDescrObject *descr) {
+#if PY_VERSION_HEX >= 0x03000000
+
+ return PyUnicode_FromFormat("<class attribute '%S' of type '%s'>", PyDescr_NAME(descr), PyDescr_TYPE(descr)->tp_name);
+#else
+ return PyString_FromFormat("<class attribute '%s' of type '%s'>", PyString_AsString(PyDescr_NAME(descr)), PyDescr_TYPE(descr)->tp_name);
+#endif
+}
+
+SWIGINTERN int
+SwigPyStaticVar_traverse(PyObject *self, visitproc visit, void *arg) {
+ PyDescrObject *descr;
+ descr = (PyDescrObject *)self;
+ Py_VISIT((PyObject*) PyDescr_TYPE(descr));
+ return 0;
+}
+
+SWIGINTERN PyObject *
+SwigPyStaticVar_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *SWIGUNUSEDPARM(type)) {
+ if (descr->d_getset->get != NULL)
+ return descr->d_getset->get(obj, descr->d_getset->closure);
+#if PY_VERSION_HEX >= 0x03000000
+ PyErr_Format(PyExc_AttributeError, "attribute '%.300S' of '%.100s' objects is not readable", PyDescr_NAME(descr), PyDescr_TYPE(descr)->tp_name);
+#else
+ PyErr_Format(PyExc_AttributeError, "attribute '%.300s' of '%.100s' objects is not readable", PyString_AsString(PyDescr_NAME(descr)), PyDescr_TYPE(descr)->tp_name);
+#endif
+ return NULL;
+}
+
+SWIGINTERN int
+SwigPyStaticVar_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) {
+ if (descr->d_getset->set != NULL)
+ return descr->d_getset->set(obj, value, descr->d_getset->closure);
+#if PY_VERSION_HEX >= 0x03000000
+ PyErr_Format(PyExc_AttributeError, "attribute '%.300S' of '%.100s' objects is not writable", PyDescr_NAME(descr), PyDescr_TYPE(descr)->tp_name);
+#else
+ PyErr_Format(PyExc_AttributeError, "attribute '%.300s' of '%.100s' objects is not writable", PyString_AsString(PyDescr_NAME(descr)), PyDescr_TYPE(descr)->tp_name);
+#endif
+ return -1;
+}
+
+SWIGINTERN int
+SwigPyObjectType_setattro(PyObject *typeobject, PyObject *name, PyObject *value) {
+ PyObject *attribute;
+ PyTypeObject *type;
+ descrsetfunc local_set;
+
+ assert(PyType_Check(typeobject));
+ type = (PyTypeObject *)typeobject;
+ attribute = _PyType_Lookup(type, name);
+ if (attribute != NULL) {
+ /* Implement descriptor functionality, if any */
+ local_set = attribute->ob_type->tp_descr_set;
+ if (local_set != NULL)
+ return local_set(attribute, (PyObject *)type, value);
+#if PY_VERSION_HEX >= 0x03000000
+ PyErr_Format(PyExc_AttributeError, "cannot modify read-only attribute '%.50s.%.400S'", type->tp_name, name);
+#else
+ PyErr_Format(PyExc_AttributeError, "cannot modify read-only attribute '%.50s.%.400s'", type->tp_name, PyString_AS_STRING(name));
+#endif
+ } else {
+#if PY_VERSION_HEX >= 0x03000000
+ PyErr_Format(PyExc_AttributeError, "type '%.50s' has no attribute '%.400S'", type->tp_name, name);
+#else
+ PyErr_Format(PyExc_AttributeError, "type '%.50s' has no attribute '%.400s'", type->tp_name, PyString_AS_STRING(name));
+#endif
+ }
+
+ return -1;
+}
+
+SWIGINTERN PyTypeObject*
+SwigPyStaticVar_Type(void) {
+ static PyTypeObject staticvar_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp = {
+#if PY_VERSION_HEX >= 0x03000000
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+#else
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0, /* ob_size */
+#endif
+ "swig_static_var_getset_descriptor", /* tp_name */
+ sizeof(PyGetSetDescrObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)SwigPyStaticVar_dealloc, /* tp_dealloc */
+#if PY_VERSION_HEX < 0x030800b4
+ (printfunc)0, /* tp_print */
+#else
+ (Py_ssize_t)0, /* tp_vectorcall_offset */
+#endif
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)SwigPyStaticVar_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_HAVE_CLASS, /* tp_flags */
+ 0, /* tp_doc */
+ SwigPyStaticVar_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ (descrgetfunc)SwigPyStaticVar_get, /* tp_descr_get */
+ (descrsetfunc)SwigPyStaticVar_set, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+#if PY_VERSION_HEX >= 0x03040000
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+ 0, /* tp_vectorcall */
+#endif
+#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000)
+ 0, /* tp_print */
+#endif
+#ifdef COUNT_ALLOCS
+ 0, /* tp_allocs */
+ 0, /* tp_frees */
+ 0, /* tp_maxalloc */
+ 0, /* tp_prev */
+ 0 /* tp_next */
+#endif
+ };
+ staticvar_type = tmp;
+ type_init = 1;
+ if (PyType_Ready(&staticvar_type) < 0)
+ return NULL;
+ }
+ return &staticvar_type;
+}
+
+SWIGINTERN PyTypeObject*
+SwigPyObjectType(void) {
+ static char swigpyobjecttype_doc[] = "Metaclass for SWIG wrapped types";
+ static PyTypeObject swigpyobjecttype_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp = {
+#if PY_VERSION_HEX >= 0x03000000
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+#else
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0, /* ob_size */
+#endif
+ "SwigPyObjectType", /* tp_name */
+ PyType_Type.tp_basicsize, /* tp_basicsize */
+ 0, /* tp_itemsize */
+ 0, /* tp_dealloc */
+#if PY_VERSION_HEX < 0x030800b4
+ (printfunc)0, /* tp_print */
+#else
+ (Py_ssize_t)0, /* tp_vectorcall_offset */
+#endif
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ SwigPyObjectType_setattro, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_CLASS, /* tp_flags */
+ swigpyobjecttype_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+#if PY_VERSION_HEX >= 0x03040000
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+ 0, /* tp_vectorcall */
+#endif
+#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000)
+ 0, /* tp_print */
+#endif
+#ifdef COUNT_ALLOCS
+ 0, /* tp_allocs */
+ 0, /* tp_frees */
+ 0, /* tp_maxalloc */
+ 0, /* tp_prev */
+ 0 /* tp_next */
+#endif
+ };
+ swigpyobjecttype_type = tmp;
+ type_init = 1;
+ swigpyobjecttype_type.tp_base = &PyType_Type;
+ if (PyType_Ready(&swigpyobjecttype_type) < 0)
+ return NULL;
+ }
+ return &swigpyobjecttype_type;
+}
+
+SWIGINTERN PyGetSetDescrObject *
+SwigPyStaticVar_new_getset(PyTypeObject *type, PyGetSetDef *getset) {
+
+ PyGetSetDescrObject *descr;
+ descr = (PyGetSetDescrObject *)PyType_GenericAlloc(SwigPyStaticVar_Type(), 0);
+ assert(descr);
+ Py_XINCREF(type);
+ PyDescr_TYPE(descr) = type;
+ PyDescr_NAME(descr) = PyString_InternFromString(getset->name);
+ descr->d_getset = getset;
+ if (PyDescr_NAME(descr) == NULL) {
+ Py_DECREF(descr);
+ descr = NULL;
+ }
+ return descr;
+}
+
+SWIGINTERN void
+SwigPyBuiltin_InitBases (PyTypeObject *type, PyTypeObject **bases) {
+ Py_ssize_t base_count = 0;
+ PyTypeObject **b;
+ PyObject *tuple;
+ Py_ssize_t i;
+
+ if (!bases[0]) {
+ bases[0] = SwigPyObject_type();
+ bases[1] = NULL;
+ }
+ type->tp_base = bases[0];
+ Py_INCREF((PyObject *)bases[0]);
+ for (b = bases; *b != NULL; ++b)
+ ++base_count;
+ tuple = PyTuple_New(base_count);
+ for (i = 0; i < base_count; ++i) {
+ Py_INCREF((PyObject *)bases[i]);
+ PyTuple_SET_ITEM(tuple, i, (PyObject *)bases[i]);
+ }
+ type->tp_bases = tuple;
+}
+
+SWIGINTERN PyObject *
+SwigPyBuiltin_ThisClosure (PyObject *self, void *SWIGUNUSEDPARM(closure)) {
+ PyObject *result;
+ result = (PyObject *)SWIG_Python_GetSwigThis(self);
+ Py_XINCREF(result);
+ return result;
+}
+
+SWIGINTERN void
+SwigPyBuiltin_SetMetaType (PyTypeObject *type, PyTypeObject *metatype)
+{
+#if PY_VERSION_HEX >= 0x030900a4
+ Py_SET_TYPE(type, metatype);
+#else
+ Py_TYPE(type) = metatype;
+#endif
+}
+
+
+/* Start of callback function macros for use in PyTypeObject */
+
+typedef PyObject *(*SwigPyWrapperFunction)(PyObject *, PyObject *);
+
+#define SWIGPY_UNARYFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_unaryfunc_closure(PyObject *a) { \
+ return SwigPyBuiltin_unaryfunc_closure(wrapper, a); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_unaryfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) {
+ return wrapper(a, NULL);
+}
+
+#define SWIGPY_DESTRUCTOR_CLOSURE(wrapper) \
+SWIGINTERN void \
+wrapper##_destructor_closure(PyObject *a) { \
+ SwigPyBuiltin_destructor_closure(wrapper, #wrapper, a); \
+}
+SWIGINTERN void
+SwigPyBuiltin_destructor_closure(SwigPyWrapperFunction wrapper, const char *wrappername, PyObject *a) {
+ SwigPyObject *sobj;
+ sobj = (SwigPyObject *)a;
+ Py_XDECREF(sobj->dict);
+ if (sobj->own) {
+ PyObject *o;
+ PyObject *type = 0, *value = 0, *traceback = 0;
+ PyErr_Fetch(&type, &value, &traceback);
+ o = wrapper(a, NULL);
+ if (!o) {
+ PyObject *deallocname = PyString_FromString(wrappername);
+ PyErr_WriteUnraisable(deallocname);
+ Py_DECREF(deallocname);
+ }
+ PyErr_Restore(type, value, traceback);
+ Py_XDECREF(o);
+ }
+ if (PyType_IS_GC(a->ob_type)) {
+ PyObject_GC_Del(a);
+ } else {
+ PyObject_Del(a);
+ }
+}
+
+#define SWIGPY_INQUIRY_CLOSURE(wrapper) \
+SWIGINTERN int \
+wrapper##_inquiry_closure(PyObject *a) { \
+ return SwigPyBuiltin_inquiry_closure(wrapper, a); \
+}
+SWIGINTERN int
+SwigPyBuiltin_inquiry_closure(SwigPyWrapperFunction wrapper, PyObject *a) {
+ PyObject *pyresult;
+ int result;
+ pyresult = wrapper(a, NULL);
+ result = pyresult && PyObject_IsTrue(pyresult) ? 1 : 0;
+ Py_XDECREF(pyresult);
+ return result;
+}
+
+#define SWIGPY_GETITERFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_getiterfunc_closure(PyObject *a) { \
+ return SwigPyBuiltin_getiterfunc_closure(wrapper, a); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_getiterfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) {
+ return wrapper(a, NULL);
+}
+
+#define SWIGPY_BINARYFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_binaryfunc_closure(PyObject *a, PyObject *b) { \
+ return SwigPyBuiltin_binaryfunc_closure(wrapper, a, b); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_binaryfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b) {
+ PyObject *tuple, *result;
+ tuple = PyTuple_New(1);
+ assert(tuple);
+ Py_INCREF(b);
+ PyTuple_SET_ITEM(tuple, 0, b);
+ result = wrapper(a, tuple);
+ Py_DECREF(tuple);
+ return result;
+}
+
+typedef ternaryfunc ternarycallfunc;
+
+#define SWIGPY_TERNARYFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_ternaryfunc_closure(PyObject *a, PyObject *b, PyObject *c) { \
+ return SwigPyBuiltin_ternaryfunc_closure(wrapper, a, b, c); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_ternaryfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b, PyObject *c) {
+ PyObject *tuple, *result;
+ tuple = PyTuple_New(2);
+ assert(tuple);
+ Py_INCREF(b);
+ PyTuple_SET_ITEM(tuple, 0, b);
+ Py_INCREF(c);
+ PyTuple_SET_ITEM(tuple, 1, c);
+ result = wrapper(a, tuple);
+ Py_DECREF(tuple);
+ return result;
+}
+
+#define SWIGPY_TERNARYCALLFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_ternarycallfunc_closure(PyObject *a, PyObject *b, PyObject *c) { \
+ return SwigPyBuiltin_ternarycallfunc_closure(wrapper, a, b, c); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_ternarycallfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b, PyObject *c) {
+ (void) c;
+ return wrapper(a, b);
+}
+
+#define SWIGPY_LENFUNC_CLOSURE(wrapper) \
+SWIGINTERN Py_ssize_t \
+wrapper##_lenfunc_closure(PyObject *a) { \
+ return SwigPyBuiltin_lenfunc_closure(wrapper, a); \
+}
+SWIGINTERN Py_ssize_t
+SwigPyBuiltin_lenfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) {
+ PyObject *resultobj;
+ Py_ssize_t result;
+ resultobj = wrapper(a, NULL);
+ result = PyNumber_AsSsize_t(resultobj, NULL);
+ Py_DECREF(resultobj);
+ return result;
+}
+
+#define SWIGPY_SSIZESSIZEARGFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_ssizessizeargfunc_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c) { \
+ return SwigPyBuiltin_ssizessizeargfunc_closure(wrapper, a, b, c); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_ssizessizeargfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b, Py_ssize_t c) {
+ PyObject *tuple, *result;
+ tuple = PyTuple_New(2);
+ assert(tuple);
+ PyTuple_SET_ITEM(tuple, 0, _PyLong_FromSsize_t(b));
+ PyTuple_SET_ITEM(tuple, 1, _PyLong_FromSsize_t(c));
+ result = wrapper(a, tuple);
+ Py_DECREF(tuple);
+ return result;
+}
+
+#define SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE(wrapper) \
+SWIGINTERN int \
+wrapper##_ssizessizeobjargproc_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c, PyObject *d) { \
+ return SwigPyBuiltin_ssizessizeobjargproc_closure(wrapper, a, b, c, d); \
+}
+SWIGINTERN int
+SwigPyBuiltin_ssizessizeobjargproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b, Py_ssize_t c, PyObject *d) {
+ PyObject *tuple, *resultobj;
+ int result;
+ tuple = PyTuple_New(d ? 3 : 2);
+ assert(tuple);
+ PyTuple_SET_ITEM(tuple, 0, _PyLong_FromSsize_t(b));
+ PyTuple_SET_ITEM(tuple, 1, _PyLong_FromSsize_t(c));
+ if (d) {
+ Py_INCREF(d);
+ PyTuple_SET_ITEM(tuple, 2, d);
+ }
+ resultobj = wrapper(a, tuple);
+ result = resultobj ? 0 : -1;
+ Py_DECREF(tuple);
+ Py_XDECREF(resultobj);
+ return result;
+}
+
+#define SWIGPY_SSIZEARGFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_ssizeargfunc_closure(PyObject *a, Py_ssize_t b) { \
+ return SwigPyBuiltin_funpack_ssizeargfunc_closure(wrapper, a, b); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_funpack_ssizeargfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b) {
+ PyObject *tuple, *result;
+ tuple = PyTuple_New(1);
+ assert(tuple);
+ PyTuple_SET_ITEM(tuple, 0, _PyLong_FromSsize_t(b));
+ result = wrapper(a, tuple);
+ Py_DECREF(tuple);
+ return result;
+}
+
+#define SWIGPY_FUNPACK_SSIZEARGFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_ssizeargfunc_closure(PyObject *a, Py_ssize_t b) { \
+ return SwigPyBuiltin_ssizeargfunc_closure(wrapper, a, b); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_ssizeargfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b) {
+ PyObject *arg, *result;
+ arg = _PyLong_FromSsize_t(b);
+ result = wrapper(a, arg);
+ Py_DECREF(arg);
+ return result;
+}
+
+#define SWIGPY_SSIZEOBJARGPROC_CLOSURE(wrapper) \
+SWIGINTERN int \
+wrapper##_ssizeobjargproc_closure(PyObject *a, Py_ssize_t b, PyObject *c) { \
+ return SwigPyBuiltin_ssizeobjargproc_closure(wrapper, a, b, c); \
+}
+SWIGINTERN int
+SwigPyBuiltin_ssizeobjargproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b, PyObject *c) {
+ PyObject *tuple, *resultobj;
+ int result;
+ tuple = PyTuple_New(2);
+ assert(tuple);
+ PyTuple_SET_ITEM(tuple, 0, _PyLong_FromSsize_t(b));
+ Py_INCREF(c);
+ PyTuple_SET_ITEM(tuple, 1, c);
+ resultobj = wrapper(a, tuple);
+ result = resultobj ? 0 : -1;
+ Py_XDECREF(resultobj);
+ Py_DECREF(tuple);
+ return result;
+}
+
+#define SWIGPY_OBJOBJPROC_CLOSURE(wrapper) \
+SWIGINTERN int \
+wrapper##_objobjproc_closure(PyObject *a, PyObject *b) { \
+ return SwigPyBuiltin_objobjproc_closure(wrapper, a, b); \
+}
+SWIGINTERN int
+SwigPyBuiltin_objobjproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b) {
+ int result;
+ PyObject *pyresult;
+ PyObject *tuple;
+ tuple = PyTuple_New(1);
+ assert(tuple);
+ Py_INCREF(b);
+ PyTuple_SET_ITEM(tuple, 0, b);
+ pyresult = wrapper(a, tuple);
+ result = pyresult ? (PyObject_IsTrue(pyresult) ? 1 : 0) : -1;
+ Py_XDECREF(pyresult);
+ Py_DECREF(tuple);
+ return result;
+}
+
+#define SWIGPY_FUNPACK_OBJOBJPROC_CLOSURE(wrapper) \
+SWIGINTERN int \
+wrapper##_objobjproc_closure(PyObject *a, PyObject *b) { \
+ return SwigPyBuiltin_funpack_objobjproc_closure(wrapper, a, b); \
+}
+SWIGINTERN int
+SwigPyBuiltin_funpack_objobjproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b) {
+ int result;
+ PyObject *pyresult;
+ pyresult = wrapper(a, b);
+ result = pyresult ? (PyObject_IsTrue(pyresult) ? 1 : 0) : -1;
+ Py_XDECREF(pyresult);
+ return result;
+}
+
+#define SWIGPY_OBJOBJARGPROC_CLOSURE(wrapper) \
+SWIGINTERN int \
+wrapper##_objobjargproc_closure(PyObject *a, PyObject *b, PyObject *c) { \
+ return SwigPyBuiltin_objobjargproc_closure(wrapper, a, b, c); \
+}
+SWIGINTERN int
+SwigPyBuiltin_objobjargproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b, PyObject *c) {
+ PyObject *tuple, *resultobj;
+ int result;
+ tuple = PyTuple_New(c ? 2 : 1);
+ assert(tuple);
+ Py_INCREF(b);
+ PyTuple_SET_ITEM(tuple, 0, b);
+ if (c) {
+ Py_INCREF(c);
+ PyTuple_SET_ITEM(tuple, 1, c);
+ }
+ resultobj = wrapper(a, tuple);
+ result = resultobj ? 0 : -1;
+ Py_XDECREF(resultobj);
+ Py_DECREF(tuple);
+ return result;
+}
+
+#define SWIGPY_REPRFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_reprfunc_closure(PyObject *a) { \
+ return SwigPyBuiltin_reprfunc_closure(wrapper, a); \
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_reprfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) {
+ return wrapper(a, NULL);
+}
+
+#define SWIGPY_HASHFUNC_CLOSURE(wrapper) \
+SWIGINTERN Py_hash_t \
+wrapper##_hashfunc_closure(PyObject *a) { \
+ return SwigPyBuiltin_hashfunc_closure(wrapper, a); \
+}
+SWIGINTERN Py_hash_t
+SwigPyBuiltin_hashfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) {
+ PyObject *pyresult;
+ Py_hash_t result;
+ pyresult = wrapper(a, NULL);
+ if (!pyresult)
+ return -1;
+ result = SWIG_PyNumber_AsPyHash(pyresult);
+ Py_DECREF(pyresult);
+ return result;
+}
+
+#define SWIGPY_ITERNEXTFUNC_CLOSURE(wrapper) \
+SWIGINTERN PyObject * \
+wrapper##_iternextfunc_closure(PyObject *a) { \
+ return SwigPyBuiltin_iternextfunc_closure(wrapper, a);\
+}
+SWIGINTERN PyObject *
+SwigPyBuiltin_iternextfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) {
+ return wrapper(a, NULL);
+}
+
+/* End of callback function macros for use in PyTypeObject */
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/contrib/tools/swig/Lib/python/exception.i b/contrib/tools/swig/Lib/python/exception.i
new file mode 100644
index 0000000000..bb0b15c9dd
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/exception.i
@@ -0,0 +1,6 @@
+%include <typemaps/exception.swg>
+
+
+%insert("runtime") {
+ %define_as(SWIG_exception(code, msg), %block(%error(code, msg); SWIG_fail; ))
+}
diff --git a/contrib/tools/swig/Lib/python/pyapi.swg b/contrib/tools/swig/Lib/python/pyapi.swg
new file mode 100644
index 0000000000..19e6979b56
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyapi.swg
@@ -0,0 +1,30 @@
+/* -----------------------------------------------------------------------------
+ * Python API portion that goes into the runtime
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Constant declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Constant Types */
+#define SWIG_PY_POINTER 4
+#define SWIG_PY_BINARY 5
+
+/* Constant information structure */
+typedef struct swig_const_info {
+ int type;
+ const char *name;
+ long lvalue;
+ double dvalue;
+ void *pvalue;
+ swig_type_info **ptype;
+} swig_const_info;
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/contrib/tools/swig/Lib/python/pybackward.swg b/contrib/tools/swig/Lib/python/pybackward.swg
new file mode 100644
index 0000000000..8305fc78b7
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pybackward.swg
@@ -0,0 +1,45 @@
+/*
+ adding backward compatibility macros
+*/
+
+#define SWIG_arg(x...) %arg(x)
+#define SWIG_Mangle(x...) %mangle(x)
+
+#define SWIG_As_frag(Type...) %fragment_name(As, Type)
+#define SWIG_As_name(Type...) %symbol_name(As, Type)
+#define SWIG_As(Type...) SWIG_As_name(Type) SWIG_AS_CALL_ARGS
+
+#define SWIG_Check_frag(Type...) %fragment_name(Check, Type)
+#define SWIG_Check_name(Type...) %symbol_name(Check, Type)
+#define SWIG_Check(Type...) SWIG_Check_name(Type) SWIG_AS_CALL_ARGS
+
+%define %ascheck_methods(Code, Type...)
+%fragment(SWIG_As_frag(Type),"header", fragment=SWIG_AsVal_frag(Type)) {
+SWIGINTERNINLINE Type
+SWIG_As(Type)(PyObject* obj)
+{
+ Type v;
+ int res = SWIG_AsVal(Type)(obj, &v);
+ if (!SWIG_IsOK(res)) {
+ /*
+ this is needed to make valgrind/purify happier.
+ */
+ memset((void*)&v, 0, sizeof(Type));
+ SWIG_Error(res, "");
+ }
+ return v;
+}
+}
+
+%fragment(SWIG_Check_frag(Type),"header",fragment=SWIG_AsVal_frag(Type)) {
+SWIGINTERNINLINE int
+SWIG_Check(Type)(PyObject* obj)
+{
+ int res = SWIG_AsVal(Type)(obj, (Type*)0);
+ return SWIG_IsOK(res);
+}
+}
+%enddef
+
+%apply_checkctypes(%ascheck_methods)
+
diff --git a/contrib/tools/swig/Lib/python/pyclasses.swg b/contrib/tools/swig/Lib/python/pyclasses.swg
new file mode 100644
index 0000000000..31ebdd2a15
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyclasses.swg
@@ -0,0 +1,157 @@
+#ifdef __cplusplus
+
+/*
+ SwigPtr_PyObject is used as a replacement of PyObject *, where
+ the INCREF/DECREF are applied as needed.
+
+ You can use SwigPtr_PyObject in a container, such as
+
+ std::vector<SwigPtr_PyObject>;
+
+ or as a member variable:
+
+ struct A {
+ SwigPtr_PyObject obj;
+ A(PyObject *o) : _obj(o) {
+ }
+ };
+
+ or as a input/output value
+
+ SwigPtr_PyObject func(SwigPtr_PyObject obj) {
+ SwigPtr_PyObject out = PyString_FromFormat("hello %s", PyObject_AsString(obj));
+ Py_DECREF(out);
+ return out;
+ }
+
+ just remember to pair the object creation with the proper DECREF,
+ the same as with plain PyObject *ptr, since SwigPtr_PyObject always add
+ one reference at construction.
+
+ SwigPtr_PyObject is 'visible' at the wrapped side, so you can do:
+
+
+ %template(pyvector) std::vector<swig::SwigPtr_PyObject>;
+
+ and all the proper typemaps will be used.
+
+*/
+
+namespace swig {
+ %ignore SwigPtr_PyObject;
+ struct SwigPtr_PyObject {};
+ %apply PyObject * {SwigPtr_PyObject};
+ %apply PyObject * const& {SwigPtr_PyObject const&};
+
+ %typemap(typecheck,precedence=SWIG_TYPECHECK_SWIGOBJECT,noblock=1) SwigPtr_PyObject const& "$1 = ($input != 0);"
+
+
+ /* For output */
+ %typemap(out,noblock=1) SwigPtr_PyObject {
+ $result = (PyObject *)$1;
+ Py_INCREF($result);
+ }
+
+ %typemap(out,noblock=1) SwigPtr_PyObject const & {
+ $result = (PyObject *)*$1;
+ Py_INCREF($result);
+ }
+
+}
+
+%{
+namespace swig {
+ class SwigPtr_PyObject {
+ protected:
+ PyObject *_obj;
+
+ public:
+ SwigPtr_PyObject() :_obj(0)
+ {
+ }
+
+ SwigPtr_PyObject(const SwigPtr_PyObject& item) : _obj(item._obj)
+ {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ Py_XINCREF(_obj);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+ }
+
+ SwigPtr_PyObject(PyObject *obj, bool initial_ref = true) :_obj(obj)
+ {
+ if (initial_ref) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ Py_XINCREF(_obj);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+ }
+ }
+
+ SwigPtr_PyObject & operator=(const SwigPtr_PyObject& item)
+ {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ Py_XINCREF(item._obj);
+ Py_XDECREF(_obj);
+ _obj = item._obj;
+ SWIG_PYTHON_THREAD_END_BLOCK;
+ return *this;
+ }
+
+ ~SwigPtr_PyObject()
+ {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ Py_XDECREF(_obj);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+ }
+
+ operator PyObject *() const
+ {
+ return _obj;
+ }
+
+ PyObject *operator->() const
+ {
+ return _obj;
+ }
+ };
+}
+%}
+
+/*
+ SwigVar_PyObject is used to manage 'in the scope' PyObject * variables,
+ as in
+
+ int func () {
+ SwigVar_PyObject obj = PyString_FromString("hello");
+ }
+
+ ie, 'obj' is created and destructed in the same scope from
+ a python object that carries at least one reference value.
+
+ SwigVar_PyObject just take care of applying the proper Py_DECREF.
+
+ Hence, this class is purely internal and not visible at the wrapped side.
+ */
+namespace swig {
+ %ignore SwigVar_PyObject;
+ struct SwigVar_PyObject {};
+ %apply PyObject * {SwigVar_PyObject};
+ %apply PyObject * const& {SwigVar_PyObject const&};
+}
+
+%{
+namespace swig {
+ struct SwigVar_PyObject : SwigPtr_PyObject {
+ SwigVar_PyObject(PyObject* obj = 0) : SwigPtr_PyObject(obj, false) { }
+
+ SwigVar_PyObject & operator = (PyObject* obj)
+ {
+ Py_XDECREF(_obj);
+ _obj = obj;
+ return *this;
+ }
+ };
+}
+%}
+
+
+#endif
diff --git a/contrib/tools/swig/Lib/python/pycontainer.swg b/contrib/tools/swig/Lib/python/pycontainer.swg
new file mode 100644
index 0000000000..d6fdff0871
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pycontainer.swg
@@ -0,0 +1,1082 @@
+/* -----------------------------------------------------------------------------
+ * pycontainer.swg
+ *
+ * Python sequence <-> C++ container wrapper
+ *
+ * This wrapper, and its iterator, allows a general use (and reuse) of
+ * the mapping between C++ and Python, thanks to the C++ templates.
+ *
+ * Of course, it needs the C++ compiler to support templates, but
+ * since we will use this wrapper with the STL containers, that should
+ * be the case.
+ * ----------------------------------------------------------------------------- */
+
+%{
+#include <iostream>
+
+#if PY_VERSION_HEX >= 0x03020000
+# define SWIGPY_SLICEOBJECT PyObject
+#else
+# define SWIGPY_SLICEOBJECT PySliceObject
+#endif
+%}
+
+
+#if !defined(SWIG_NO_EXPORT_ITERATOR_METHODS)
+# if !defined(SWIG_EXPORT_ITERATOR_METHODS)
+# define SWIG_EXPORT_ITERATOR_METHODS SWIG_EXPORT_ITERATOR_METHODS
+# endif
+#endif
+
+%include <pyiterators.swg>
+
+/**** The PySequence C++ Wrap ***/
+
+%fragment("<stdexcept>");
+
+%include <std_except.i>
+
+%fragment("container_owner_attribute_init", "init") {
+ // thread safe initialization
+ swig::container_owner_attribute();
+}
+
+%fragment("reference_container_owner", "header", fragment="container_owner_attribute_init") {
+namespace swig {
+ static PyObject* container_owner_attribute() {
+ static PyObject* attr = SWIG_Python_str_FromChar("__swig_container");
+ return attr;
+ }
+
+ template <typename T>
+ struct container_owner {
+ // By default, do not add the back-reference (for value types)
+ // Specialization below will check the reference for pointer types.
+ static bool back_reference(PyObject* /*child*/, PyObject* /*owner*/) {
+ return false;
+ }
+ };
+
+ template <>
+ struct container_owner<swig::pointer_category> {
+ /*
+ * Call to add a back-reference to the owning object when returning a
+ * reference from a container. Will only set the reference if child
+ * is a SWIG wrapper object that does not own the pointer.
+ *
+ * returns whether the reference was set or not
+ */
+ static bool back_reference(PyObject* child, PyObject* owner) {
+ SwigPyObject* swigThis = SWIG_Python_GetSwigThis(child);
+ if (swigThis && (swigThis->own & SWIG_POINTER_OWN) != SWIG_POINTER_OWN) {
+ return PyObject_SetAttr(child, container_owner_attribute(), owner) != -1;
+ }
+ return false;
+ }
+ };
+}
+}
+
+%fragment(SWIG_Traits_frag(swig::SwigPtr_PyObject),"header",fragment="StdTraits") {
+namespace swig {
+ template <> struct traits<SwigPtr_PyObject > {
+ typedef value_category category;
+ static const char* type_name() { return "SwigPtr_PyObject"; }
+ };
+
+ template <> struct traits_from<SwigPtr_PyObject> {
+ typedef SwigPtr_PyObject value_type;
+ static PyObject *from(const value_type& val) {
+ PyObject *obj = static_cast<PyObject *>(val);
+ Py_XINCREF(obj);
+ return obj;
+ }
+ };
+
+ template <>
+ struct traits_check<SwigPtr_PyObject, value_category> {
+ static bool check(SwigPtr_PyObject) {
+ return true;
+ }
+ };
+
+ template <> struct traits_asval<SwigPtr_PyObject > {
+ typedef SwigPtr_PyObject value_type;
+ static int asval(PyObject *obj, value_type *val) {
+ if (val) *val = obj;
+ return SWIG_OK;
+ }
+ };
+}
+}
+
+%fragment(SWIG_Traits_frag(swig::SwigVar_PyObject),"header",fragment="StdTraits") {
+namespace swig {
+ template <> struct traits<SwigVar_PyObject > {
+ typedef value_category category;
+ static const char* type_name() { return "SwigVar_PyObject"; }
+ };
+
+ template <> struct traits_from<SwigVar_PyObject> {
+ typedef SwigVar_PyObject value_type;
+ static PyObject *from(const value_type& val) {
+ PyObject *obj = static_cast<PyObject *>(val);
+ Py_XINCREF(obj);
+ return obj;
+ }
+ };
+
+ template <>
+ struct traits_check<SwigVar_PyObject, value_category> {
+ static bool check(SwigVar_PyObject) {
+ return true;
+ }
+ };
+
+ template <> struct traits_asval<SwigVar_PyObject > {
+ typedef SwigVar_PyObject value_type;
+ static int asval(PyObject *obj, value_type *val) {
+ if (val) *val = obj;
+ return SWIG_OK;
+ }
+ };
+}
+}
+
+%fragment("SwigPySequence_Base","header",fragment="<stddef.h>",fragment="StdTraits")
+{
+%#include <functional>
+
+namespace std {
+ template <>
+ struct less <PyObject *>
+ {
+ bool
+ operator()(PyObject * v, PyObject *w) const
+ {
+ bool res;
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ res = PyObject_RichCompareBool(v, w, Py_LT) ? true : false;
+ /* This may fall into a case of inconsistent
+ eg. ObjA > ObjX > ObjB
+ but ObjA < ObjB
+ */
+ if( PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_TypeError) )
+ {
+ /* Objects can't be compared, this mostly occurred in Python 3.0 */
+ /* Compare their ptr directly for a workaround */
+ res = (v < w);
+ PyErr_Clear();
+ }
+ SWIG_PYTHON_THREAD_END_BLOCK;
+ return res;
+ }
+ };
+
+ template <>
+ struct less <swig::SwigPtr_PyObject>
+ {
+ bool
+ operator()(const swig::SwigPtr_PyObject& v, const swig::SwigPtr_PyObject& w) const
+ {
+ return std::less<PyObject *>()(v, w);
+ }
+ };
+
+ template <>
+ struct less <swig::SwigVar_PyObject>
+ {
+ bool
+ operator()(const swig::SwigVar_PyObject& v, const swig::SwigVar_PyObject& w) const
+ {
+ return std::less<PyObject *>()(v, w);
+ }
+ };
+
+}
+
+namespace swig {
+ template <> struct traits<PyObject *> {
+ typedef value_category category;
+ static const char* type_name() { return "PyObject *"; }
+ };
+
+ template <> struct traits_asval<PyObject * > {
+ typedef PyObject * value_type;
+ static int asval(PyObject *obj, value_type *val) {
+ if (val) *val = obj;
+ return SWIG_OK;
+ }
+ };
+
+ template <>
+ struct traits_check<PyObject *, value_category> {
+ static bool check(PyObject *) {
+ return true;
+ }
+ };
+
+ template <> struct traits_from<PyObject *> {
+ typedef PyObject * value_type;
+ static PyObject *from(const value_type& val) {
+ Py_XINCREF(val);
+ return val;
+ }
+ };
+
+}
+
+namespace swig {
+ template <class Difference>
+ inline size_t
+ check_index(Difference i, size_t size, bool insert = false) {
+ if ( i < 0 ) {
+ if ((size_t) (-i) <= size)
+ return (size_t) (i + size);
+ } else if ( (size_t) i < size ) {
+ return (size_t) i;
+ } else if (insert && ((size_t) i == size)) {
+ return size;
+ }
+ throw std::out_of_range("index out of range");
+ }
+
+ template <class Difference>
+ void
+ slice_adjust(Difference i, Difference j, Py_ssize_t step, size_t size, Difference &ii, Difference &jj, bool insert = false) {
+ if (step == 0) {
+ throw std::invalid_argument("slice step cannot be zero");
+ } else if (step > 0) {
+ // Required range: 0 <= i < size, 0 <= j < size, i <= j
+ if (i < 0) {
+ ii = 0;
+ } else if (i < (Difference)size) {
+ ii = i;
+ } else if (insert && (i >= (Difference)size)) {
+ ii = (Difference)size;
+ }
+ if (j < 0) {
+ jj = 0;
+ } else {
+ jj = (j < (Difference)size) ? j : (Difference)size;
+ }
+ if (jj < ii)
+ jj = ii;
+ } else {
+ // Required range: -1 <= i < size-1, -1 <= j < size-1, i >= j
+ if (i < -1) {
+ ii = -1;
+ } else if (i < (Difference) size) {
+ ii = i;
+ } else if (i >= (Difference)(size-1)) {
+ ii = (Difference)(size-1);
+ }
+ if (j < -1) {
+ jj = -1;
+ } else {
+ jj = (j < (Difference)size ) ? j : (Difference)(size-1);
+ }
+ if (ii < jj)
+ ii = jj;
+ }
+ }
+
+ template <class Sequence, class Difference>
+ inline typename Sequence::iterator
+ getpos(Sequence* self, Difference i) {
+ typename Sequence::iterator pos = self->begin();
+ std::advance(pos, check_index(i,self->size()));
+ return pos;
+ }
+
+ template <class Sequence, class Difference>
+ inline typename Sequence::const_iterator
+ cgetpos(const Sequence* self, Difference i) {
+ typename Sequence::const_iterator pos = self->begin();
+ std::advance(pos, check_index(i,self->size()));
+ return pos;
+ }
+
+ template <class Sequence>
+ inline void
+ erase(Sequence* seq, const typename Sequence::iterator& position) {
+ seq->erase(position);
+ }
+
+ template <class Sequence>
+ struct traits_reserve {
+ static void reserve(Sequence & /*seq*/, typename Sequence::size_type /*n*/) {
+ // This should be specialized for types that support reserve
+ }
+ };
+
+ template <class Sequence, class Difference>
+ inline Sequence*
+ getslice(const Sequence* self, Difference i, Difference j, Py_ssize_t step) {
+ typename Sequence::size_type size = self->size();
+ Difference ii = 0;
+ Difference jj = 0;
+ swig::slice_adjust(i, j, step, size, ii, jj);
+
+ if (step > 0) {
+ typename Sequence::const_iterator sb = self->begin();
+ typename Sequence::const_iterator se = self->begin();
+ std::advance(sb,ii);
+ std::advance(se,jj);
+ if (step == 1) {
+ return new Sequence(sb, se);
+ } else {
+ Sequence *sequence = new Sequence();
+ swig::traits_reserve<Sequence>::reserve(*sequence, (jj - ii + step - 1) / step);
+ typename Sequence::const_iterator it = sb;
+ while (it!=se) {
+ sequence->push_back(*it);
+ for (Py_ssize_t c=0; c<step && it!=se; ++c)
+ it++;
+ }
+ return sequence;
+ }
+ } else {
+ Sequence *sequence = new Sequence();
+ swig::traits_reserve<Sequence>::reserve(*sequence, (ii - jj - step - 1) / -step);
+ typename Sequence::const_reverse_iterator sb = self->rbegin();
+ typename Sequence::const_reverse_iterator se = self->rbegin();
+ std::advance(sb,size-ii-1);
+ std::advance(se,size-jj-1);
+ typename Sequence::const_reverse_iterator it = sb;
+ while (it!=se) {
+ sequence->push_back(*it);
+ for (Py_ssize_t c=0; c<-step && it!=se; ++c)
+ it++;
+ }
+ return sequence;
+ }
+ }
+
+ template <class Sequence, class Difference, class InputSeq>
+ inline void
+ setslice(Sequence* self, Difference i, Difference j, Py_ssize_t step, const InputSeq& is = InputSeq()) {
+ typename Sequence::size_type size = self->size();
+ Difference ii = 0;
+ Difference jj = 0;
+ swig::slice_adjust(i, j, step, size, ii, jj, true);
+ if (step > 0) {
+ if (step == 1) {
+ size_t ssize = jj - ii;
+ if (ssize <= is.size()) {
+ // expanding/staying the same size
+ swig::traits_reserve<Sequence>::reserve(*self, self->size() - ssize + is.size());
+ typename Sequence::iterator sb = self->begin();
+ typename InputSeq::const_iterator isit = is.begin();
+ std::advance(sb,ii);
+ std::advance(isit, jj - ii);
+ self->insert(std::copy(is.begin(), isit, sb), isit, is.end());
+ } else {
+ // shrinking
+ typename Sequence::iterator sb = self->begin();
+ typename Sequence::iterator se = self->begin();
+ std::advance(sb,ii);
+ std::advance(se,jj);
+ self->erase(sb,se);
+ sb = self->begin();
+ std::advance(sb,ii);
+ self->insert(sb, is.begin(), is.end());
+ }
+ } else {
+ size_t replacecount = (jj - ii + step - 1) / step;
+ if (is.size() != replacecount) {
+ char msg[1024];
+ sprintf(msg, "attempt to assign sequence of size %lu to extended slice of size %lu", (unsigned long)is.size(), (unsigned long)replacecount);
+ throw std::invalid_argument(msg);
+ }
+ typename Sequence::const_iterator isit = is.begin();
+ typename Sequence::iterator it = self->begin();
+ std::advance(it,ii);
+ for (size_t rc=0; rc<replacecount && it != self->end(); ++rc) {
+ *it++ = *isit++;
+ for (Py_ssize_t c=0; c<(step-1) && it != self->end(); ++c)
+ it++;
+ }
+ }
+ } else {
+ size_t replacecount = (ii - jj - step - 1) / -step;
+ if (is.size() != replacecount) {
+ char msg[1024];
+ sprintf(msg, "attempt to assign sequence of size %lu to extended slice of size %lu", (unsigned long)is.size(), (unsigned long)replacecount);
+ throw std::invalid_argument(msg);
+ }
+ typename Sequence::const_iterator isit = is.begin();
+ typename Sequence::reverse_iterator it = self->rbegin();
+ std::advance(it,size-ii-1);
+ for (size_t rc=0; rc<replacecount && it != self->rend(); ++rc) {
+ *it++ = *isit++;
+ for (Py_ssize_t c=0; c<(-step-1) && it != self->rend(); ++c)
+ it++;
+ }
+ }
+ }
+
+ template <class Sequence, class Difference>
+ inline void
+ delslice(Sequence* self, Difference i, Difference j, Py_ssize_t step) {
+ typename Sequence::size_type size = self->size();
+ Difference ii = 0;
+ Difference jj = 0;
+ swig::slice_adjust(i, j, step, size, ii, jj, true);
+ if (step > 0) {
+ typename Sequence::iterator sb = self->begin();
+ std::advance(sb,ii);
+ if (step == 1) {
+ typename Sequence::iterator se = self->begin();
+ std::advance(se,jj);
+ self->erase(sb,se);
+ } else {
+ typename Sequence::iterator it = sb;
+ size_t delcount = (jj - ii + step - 1) / step;
+ while (delcount) {
+ it = self->erase(it);
+ for (Py_ssize_t c=0; c<(step-1) && it != self->end(); ++c)
+ it++;
+ delcount--;
+ }
+ }
+ } else {
+ typename Sequence::reverse_iterator sb = self->rbegin();
+ std::advance(sb,size-ii-1);
+ typename Sequence::reverse_iterator it = sb;
+ size_t delcount = (ii - jj - step - 1) / -step;
+ while (delcount) {
+ it = typename Sequence::reverse_iterator(self->erase((++it).base()));
+ for (Py_ssize_t c=0; c<(-step-1) && it != self->rend(); ++c)
+ it++;
+ delcount--;
+ }
+ }
+ }
+}
+}
+
+%fragment("SwigPySequence_Cont","header",
+ fragment="StdTraits",
+ fragment="SwigPySequence_Base",
+ fragment="SwigPyIterator_T")
+{
+namespace swig
+{
+ template <class T>
+ struct SwigPySequence_Ref
+ {
+ SwigPySequence_Ref(PyObject* seq, Py_ssize_t index)
+ : _seq(seq), _index(index)
+ {
+ }
+
+ operator T () const
+ {
+ swig::SwigVar_PyObject item = PySequence_GetItem(_seq, _index);
+ try {
+ return swig::as<T>(item);
+ } catch (const std::invalid_argument& e) {
+ char msg[1024];
+ sprintf(msg, "in sequence element %d ", (int)_index);
+ if (!PyErr_Occurred()) {
+ ::%type_error(swig::type_name<T>());
+ }
+ SWIG_Python_AddErrorMsg(msg);
+ SWIG_Python_AddErrorMsg(e.what());
+ throw;
+ }
+ }
+
+ SwigPySequence_Ref& operator=(const T& v)
+ {
+ PySequence_SetItem(_seq, _index, swig::from<T>(v));
+ return *this;
+ }
+
+ private:
+ PyObject* _seq;
+ Py_ssize_t _index;
+ };
+
+ template <class T>
+ struct SwigPySequence_ArrowProxy
+ {
+ SwigPySequence_ArrowProxy(const T& x): m_value(x) {}
+ const T* operator->() const { return &m_value; }
+ operator const T*() const { return &m_value; }
+ T m_value;
+ };
+
+ template <class T, class Reference >
+ struct SwigPySequence_InputIterator
+ {
+ typedef SwigPySequence_InputIterator<T, Reference > self;
+
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef Reference reference;
+ typedef T value_type;
+ typedef T* pointer;
+ typedef Py_ssize_t difference_type;
+
+ SwigPySequence_InputIterator()
+ {
+ }
+
+ SwigPySequence_InputIterator(PyObject* seq, Py_ssize_t index)
+ : _seq(seq), _index(index)
+ {
+ }
+
+ reference operator*() const
+ {
+ return reference(_seq, _index);
+ }
+
+ SwigPySequence_ArrowProxy<T>
+ operator->() const {
+ return SwigPySequence_ArrowProxy<T>(operator*());
+ }
+
+ bool operator==(const self& ri) const
+ {
+ return (_index == ri._index) && (_seq == ri._seq);
+ }
+
+ bool operator!=(const self& ri) const
+ {
+ return !(operator==(ri));
+ }
+
+ self& operator ++ ()
+ {
+ ++_index;
+ return *this;
+ }
+
+ self& operator -- ()
+ {
+ --_index;
+ return *this;
+ }
+
+ self& operator += (difference_type n)
+ {
+ _index += n;
+ return *this;
+ }
+
+ self operator +(difference_type n) const
+ {
+ return self(_seq, _index + n);
+ }
+
+ self& operator -= (difference_type n)
+ {
+ _index -= n;
+ return *this;
+ }
+
+ self operator -(difference_type n) const
+ {
+ return self(_seq, _index - n);
+ }
+
+ difference_type operator - (const self& ri) const
+ {
+ return _index - ri._index;
+ }
+
+ bool operator < (const self& ri) const
+ {
+ return _index < ri._index;
+ }
+
+ reference
+ operator[](difference_type n) const
+ {
+ return reference(_seq, _index + n);
+ }
+
+ private:
+ PyObject* _seq;
+ difference_type _index;
+ };
+
+ // STL container wrapper around a Python sequence
+ template <class T>
+ struct SwigPySequence_Cont
+ {
+ typedef SwigPySequence_Ref<T> reference;
+ typedef const SwigPySequence_Ref<T> const_reference;
+ typedef T value_type;
+ typedef T* pointer;
+ typedef Py_ssize_t difference_type;
+ typedef size_t size_type;
+ typedef const pointer const_pointer;
+ typedef SwigPySequence_InputIterator<T, reference> iterator;
+ typedef SwigPySequence_InputIterator<T, const_reference> const_iterator;
+
+ SwigPySequence_Cont(PyObject* seq) : _seq(0)
+ {
+ if (!PySequence_Check(seq)) {
+ throw std::invalid_argument("a sequence is expected");
+ }
+ _seq = seq;
+ Py_INCREF(_seq);
+ }
+
+ ~SwigPySequence_Cont()
+ {
+ Py_XDECREF(_seq);
+ }
+
+ size_type size() const
+ {
+ return static_cast<size_type>(PySequence_Size(_seq));
+ }
+
+ bool empty() const
+ {
+ return size() == 0;
+ }
+
+ iterator begin()
+ {
+ return iterator(_seq, 0);
+ }
+
+ const_iterator begin() const
+ {
+ return const_iterator(_seq, 0);
+ }
+
+ iterator end()
+ {
+ return iterator(_seq, size());
+ }
+
+ const_iterator end() const
+ {
+ return const_iterator(_seq, size());
+ }
+
+ reference operator[](difference_type n)
+ {
+ return reference(_seq, n);
+ }
+
+ const_reference operator[](difference_type n) const
+ {
+ return const_reference(_seq, n);
+ }
+
+ bool check() const
+ {
+ Py_ssize_t s = size();
+ for (Py_ssize_t i = 0; i < s; ++i) {
+ swig::SwigVar_PyObject item = PySequence_GetItem(_seq, i);
+ if (!swig::check<value_type>(item))
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ PyObject* _seq;
+ };
+
+}
+}
+
+%define %swig_sequence_iterator(Sequence...)
+ %swig_sequence_iterator_with_making_function(swig::make_output_iterator,Sequence...)
+%enddef
+
+%define %swig_sequence_forward_iterator(Sequence...)
+ %swig_sequence_iterator_with_making_function(swig::make_output_forward_iterator,Sequence...)
+%enddef
+
+%define %swig_sequence_iterator_with_making_function(Make_output_iterator,Sequence...)
+#if defined(SWIG_EXPORT_ITERATOR_METHODS)
+ class iterator;
+ class reverse_iterator;
+ class const_iterator;
+ class const_reverse_iterator;
+
+ %typemap(out,noblock=1,fragment="SwigPySequence_Cont")
+ iterator, reverse_iterator, const_iterator, const_reverse_iterator {
+ $result = SWIG_NewPointerObj(Make_output_iterator(%static_cast($1,const $type &)),
+ swig::SwigPyIterator::descriptor(),SWIG_POINTER_OWN);
+ }
+ %typemap(out,noblock=1,fragment="SwigPySequence_Cont")
+ std::pair<iterator, iterator>, std::pair<const_iterator, const_iterator> {
+ $result = PyTuple_New(2);
+ PyTuple_SetItem($result,0,SWIG_NewPointerObj(Make_output_iterator(%static_cast($1,const $type &).first),
+ swig::SwigPyIterator::descriptor(),SWIG_POINTER_OWN));
+ PyTuple_SetItem($result,1,SWIG_NewPointerObj(Make_output_iterator(%static_cast($1,const $type &).second),
+ swig::SwigPyIterator::descriptor(),SWIG_POINTER_OWN));
+ }
+
+ %fragment("SwigPyPairBoolOutputIterator","header",fragment=SWIG_From_frag(bool),fragment="SwigPySequence_Cont") {}
+
+ %typemap(out,noblock=1,fragment="SwigPyPairBoolOutputIterator")
+ std::pair<iterator, bool>, std::pair<const_iterator, bool> {
+ $result = PyTuple_New(2);
+ PyTuple_SetItem($result,0,SWIG_NewPointerObj(Make_output_iterator(%static_cast($1,const $type &).first),
+ swig::SwigPyIterator::descriptor(),SWIG_POINTER_OWN));
+ PyTuple_SetItem($result,1,SWIG_From(bool)(%static_cast($1,const $type &).second));
+ }
+
+ %typemap(in,noblock=1,fragment="SwigPySequence_Cont")
+ iterator(swig::SwigPyIterator *iter = 0, int res),
+ reverse_iterator(swig::SwigPyIterator *iter = 0, int res),
+ const_iterator(swig::SwigPyIterator *iter = 0, int res),
+ const_reverse_iterator(swig::SwigPyIterator *iter = 0, int res) {
+ res = SWIG_ConvertPtr($input, %as_voidptrptr(&iter), swig::SwigPyIterator::descriptor(), 0);
+ if (!SWIG_IsOK(res) || !iter) {
+ %argument_fail(SWIG_TypeError, "$type", $symname, $argnum);
+ } else {
+ swig::SwigPyIterator_T<$type > *iter_t = dynamic_cast<swig::SwigPyIterator_T<$type > *>(iter);
+ if (iter_t) {
+ $1 = iter_t->get_current();
+ } else {
+ %argument_fail(SWIG_TypeError, "$type", $symname, $argnum);
+ }
+ }
+ }
+
+ %typecheck(%checkcode(ITERATOR),noblock=1,fragment="SwigPySequence_Cont")
+ iterator, reverse_iterator, const_iterator, const_reverse_iterator {
+ swig::SwigPyIterator *iter = 0;
+ int res = SWIG_ConvertPtr($input, %as_voidptrptr(&iter), swig::SwigPyIterator::descriptor(), 0);
+ $1 = (SWIG_IsOK(res) && iter && (dynamic_cast<swig::SwigPyIterator_T<$type > *>(iter) != 0));
+ }
+
+ %fragment("SwigPySequence_Cont");
+
+ %newobject iterator(PyObject **PYTHON_SELF);
+ %extend {
+ swig::SwigPyIterator* iterator(PyObject **PYTHON_SELF) {
+ return Make_output_iterator(self->begin(), self->begin(), self->end(), *PYTHON_SELF);
+ }
+
+#if defined(SWIGPYTHON_BUILTIN)
+ %feature("python:slot", "tp_iter", functype="getiterfunc") iterator;
+#else
+ %pythoncode %{def __iter__(self):
+ return self.iterator()%}
+#endif
+ }
+
+#endif //SWIG_EXPORT_ITERATOR_METHODS
+%enddef
+
+
+/**** The python container methods ****/
+
+%define %swig_container_methods(Container...)
+
+/* deprecated in Python 2 */
+#if 1
+ %newobject __getslice__;
+#endif
+ %newobject __getitem__(SWIGPY_SLICEOBJECT *slice);
+
+#if defined(SWIGPYTHON_BUILTIN)
+ %feature("python:slot", "nb_nonzero", functype="inquiry") __nonzero__;
+ %feature("python:slot", "sq_length", functype="lenfunc") __len__;
+#endif // SWIGPYTHON_BUILTIN
+
+ %extend {
+ bool __nonzero__() const {
+ return !(self->empty());
+ }
+
+ /* Alias for Python 3 compatibility */
+ bool __bool__() const {
+ return !(self->empty());
+ }
+
+ size_type __len__() const {
+ return self->size();
+ }
+
+ // Although __getitem__, front, back actually use a const value_type& return type, the typemaps below
+ // use non-const so that they can be easily overridden by users if necessary.
+ %typemap(ret, fragment="reference_container_owner", noblock=1) value_type& __getitem__, value_type& front, value_type& back {
+ (void)swig::container_owner<swig::traits<$*1_ltype>::category>::back_reference($result, $self);
+ }
+ }
+%enddef
+
+
+
+%define %swig_sequence_methods_common(Sequence...)
+ %swig_sequence_iterator(%arg(Sequence))
+ %swig_container_methods(%arg(Sequence))
+
+ %fragment("SwigPySequence_Base");
+
+#if defined(SWIGPYTHON_BUILTIN)
+ //%feature("python:slot", "sq_item", functype="ssizeargfunc") __getitem__;
+ //%feature("python:slot", "sq_slice", functype="ssizessizeargfunc") __getslice__;
+ //%feature("python:slot", "sq_ass_item", functype="ssizeobjargproc") __setitem__;
+ //%feature("python:slot", "sq_ass_slice", functype="ssizessizeobjargproc") __setslice__;
+ %feature("python:slot", "mp_subscript", functype="binaryfunc") __getitem__;
+ %feature("python:slot", "mp_ass_subscript", functype="objobjargproc") __setitem__;
+#endif // SWIGPYTHON_BUILTIN
+
+ %extend {
+ /* typemap for slice object support */
+ %typemap(in) SWIGPY_SLICEOBJECT* {
+ if (!PySlice_Check($input)) {
+ %argument_fail(SWIG_TypeError, "$type", $symname, $argnum);
+ }
+ $1 = (SWIGPY_SLICEOBJECT *) $input;
+ }
+ %typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER) SWIGPY_SLICEOBJECT* {
+ $1 = PySlice_Check($input);
+ }
+
+/* deprecated in Python 2 */
+#if 1
+ Sequence* __getslice__(difference_type i, difference_type j) throw (std::out_of_range, std::invalid_argument) {
+ return swig::getslice(self, i, j, 1);
+ }
+
+ void __setslice__(difference_type i, difference_type j) throw (std::out_of_range, std::invalid_argument) {
+ swig::setslice(self, i, j, 1, Sequence());
+ }
+
+ void __setslice__(difference_type i, difference_type j, const Sequence& v) throw (std::out_of_range, std::invalid_argument) {
+ swig::setslice(self, i, j, 1, v);
+ }
+
+ void __delslice__(difference_type i, difference_type j) throw (std::out_of_range, std::invalid_argument) {
+ swig::delslice(self, i, j, 1);
+ }
+#endif
+
+ void __delitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) {
+ swig::erase(self, swig::getpos(self, i));
+ }
+
+ /* Overloaded methods for Python 3 compatibility
+ * (Also useful in Python 2.x)
+ */
+ Sequence* __getitem__(SWIGPY_SLICEOBJECT *slice) throw (std::out_of_range, std::invalid_argument) {
+ Py_ssize_t i, j, step;
+ if( !PySlice_Check(slice) ) {
+ SWIG_Error(SWIG_TypeError, "Slice object expected.");
+ return NULL;
+ }
+ PySlice_GetIndices(slice, (Py_ssize_t)self->size(), &i, &j, &step);
+ Sequence::difference_type id = i;
+ Sequence::difference_type jd = j;
+ return swig::getslice(self, id, jd, step);
+ }
+
+ void __setitem__(SWIGPY_SLICEOBJECT *slice, const Sequence& v) throw (std::out_of_range, std::invalid_argument) {
+ Py_ssize_t i, j, step;
+ if( !PySlice_Check(slice) ) {
+ SWIG_Error(SWIG_TypeError, "Slice object expected.");
+ return;
+ }
+ PySlice_GetIndices(slice, (Py_ssize_t)self->size(), &i, &j, &step);
+ Sequence::difference_type id = i;
+ Sequence::difference_type jd = j;
+ swig::setslice(self, id, jd, step, v);
+ }
+
+ void __setitem__(SWIGPY_SLICEOBJECT *slice) throw (std::out_of_range, std::invalid_argument) {
+ Py_ssize_t i, j, step;
+ if( !PySlice_Check(slice) ) {
+ SWIG_Error(SWIG_TypeError, "Slice object expected.");
+ return;
+ }
+ PySlice_GetIndices(slice, (Py_ssize_t)self->size(), &i, &j, &step);
+ Sequence::difference_type id = i;
+ Sequence::difference_type jd = j;
+ swig::delslice(self, id, jd, step);
+ }
+
+ void __delitem__(SWIGPY_SLICEOBJECT *slice) throw (std::out_of_range, std::invalid_argument) {
+ Py_ssize_t i, j, step;
+ if( !PySlice_Check(slice) ) {
+ SWIG_Error(SWIG_TypeError, "Slice object expected.");
+ return;
+ }
+ PySlice_GetIndices(slice, (Py_ssize_t)self->size(), &i, &j, &step);
+ Sequence::difference_type id = i;
+ Sequence::difference_type jd = j;
+ swig::delslice(self, id, jd, step);
+ }
+
+ }
+%enddef
+
+%define %swig_sequence_methods_non_resizable(Sequence...)
+ %swig_sequence_methods_common(%arg(Sequence))
+ %extend {
+ const value_type& __getitem__(difference_type i) const throw (std::out_of_range) {
+ return *(swig::cgetpos(self, i));
+ }
+
+ void __setitem__(difference_type i, const value_type& x) throw (std::out_of_range) {
+ *(swig::getpos(self,i)) = x;
+ }
+
+#if defined(SWIGPYTHON_BUILTIN)
+ // This will be called through the mp_ass_subscript slot to delete an entry.
+ void __setitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) {
+ swig::erase(self, swig::getpos(self, i));
+ }
+#endif
+
+ }
+%enddef
+
+%define %swig_sequence_methods(Sequence...)
+ %swig_sequence_methods_non_resizable(%arg(Sequence))
+ %extend {
+ value_type pop() throw (std::out_of_range) {
+ if (self->size() == 0)
+ throw std::out_of_range("pop from empty container");
+ Sequence::value_type x = self->back();
+ self->pop_back();
+ return x;
+ }
+
+ void append(const value_type& x) {
+ self->push_back(x);
+ }
+ }
+%enddef
+
+%define %swig_sequence_methods_non_resizable_val(Sequence...)
+ %swig_sequence_methods_common(%arg(Sequence))
+ %extend {
+ value_type __getitem__(difference_type i) throw (std::out_of_range) {
+ return *(swig::cgetpos(self, i));
+ }
+
+ void __setitem__(difference_type i, value_type x) throw (std::out_of_range) {
+ *(swig::getpos(self,i)) = x;
+ }
+
+#if defined(SWIGPYTHON_BUILTIN)
+ // This will be called through the mp_ass_subscript slot to delete an entry.
+ void __setitem__(difference_type i) throw (std::out_of_range, std::invalid_argument) {
+ swig::erase(self, swig::getpos(self, i));
+ }
+#endif
+ }
+%enddef
+
+%define %swig_sequence_methods_val(Sequence...)
+ %swig_sequence_methods_non_resizable_val(%arg(Sequence))
+ %extend {
+ value_type pop() throw (std::out_of_range) {
+ if (self->size() == 0)
+ throw std::out_of_range("pop from empty container");
+ Sequence::value_type x = self->back();
+ self->pop_back();
+ return x;
+ }
+
+ void append(value_type x) {
+ self->push_back(x);
+ }
+ }
+%enddef
+
+
+
+//
+// Common fragments
+//
+
+%fragment("StdSequenceTraits","header",
+ fragment="StdTraits",
+ fragment="SwigPySequence_Cont")
+{
+namespace swig {
+ template <class SwigPySeq, class Seq>
+ inline void
+ assign(const SwigPySeq& swigpyseq, Seq* seq) {
+ // seq->assign(swigpyseq.begin(), swigpyseq.end()); // not used as not always implemented
+ typedef typename SwigPySeq::value_type value_type;
+ typename SwigPySeq::const_iterator it = swigpyseq.begin();
+ for (;it != swigpyseq.end(); ++it) {
+ seq->insert(seq->end(),(value_type)(*it));
+ }
+ }
+
+ template <class Seq, class T = typename Seq::value_type >
+ struct traits_asptr_stdseq {
+ typedef Seq sequence;
+ typedef T value_type;
+
+ static int asptr(PyObject *obj, sequence **seq) {
+ if (obj == Py_None || SWIG_Python_GetSwigThis(obj)) {
+ sequence *p;
+ swig_type_info *descriptor = swig::type_info<sequence>();
+ if (descriptor && SWIG_IsOK(::SWIG_ConvertPtr(obj, (void **)&p, descriptor, 0))) {
+ if (seq) *seq = p;
+ return SWIG_OLDOBJ;
+ }
+ } else if (PySequence_Check(obj)) {
+ try {
+ SwigPySequence_Cont<value_type> swigpyseq(obj);
+ if (seq) {
+ sequence *pseq = new sequence();
+ assign(swigpyseq, pseq);
+ *seq = pseq;
+ return SWIG_NEWOBJ;
+ } else {
+ return swigpyseq.check() ? SWIG_OK : SWIG_ERROR;
+ }
+ } catch (std::exception& e) {
+ if (seq) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError, e.what());
+ }
+ }
+ return SWIG_ERROR;
+ }
+ }
+ return SWIG_ERROR;
+ }
+ };
+
+ template <class Seq, class T = typename Seq::value_type >
+ struct traits_from_stdseq {
+ typedef Seq sequence;
+ typedef T value_type;
+ typedef typename Seq::size_type size_type;
+ typedef typename sequence::const_iterator const_iterator;
+
+ static PyObject *from(const sequence& seq) {
+%#ifdef SWIG_PYTHON_EXTRA_NATIVE_CONTAINERS
+ swig_type_info *desc = swig::type_info<sequence>();
+ if (desc && desc->clientdata) {
+ return SWIG_InternalNewPointerObj(new sequence(seq), desc, SWIG_POINTER_OWN);
+ }
+%#endif
+ size_type size = seq.size();
+ if (size <= (size_type)INT_MAX) {
+ PyObject *obj = PyTuple_New((Py_ssize_t)size);
+ Py_ssize_t i = 0;
+ for (const_iterator it = seq.begin(); it != seq.end(); ++it, ++i) {
+ PyTuple_SetItem(obj,i,swig::from<value_type>(*it));
+ }
+ return obj;
+ } else {
+ PyErr_SetString(PyExc_OverflowError,"sequence size not valid in python");
+ return NULL;
+ }
+ }
+ };
+}
+}
diff --git a/contrib/tools/swig/Lib/python/pydocs.swg b/contrib/tools/swig/Lib/python/pydocs.swg
new file mode 100644
index 0000000000..5a25423d4c
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pydocs.swg
@@ -0,0 +1,45 @@
+
+// Documentation for use with the autodoc feature.
+
+#ifdef SWIG_DOC_DOXYGEN_STYLE
+%typemap(doc) SWIGTYPE "@param $1_name $1_type"
+%typemap(doc) SWIGTYPE * "@param $1_name $1_type"
+%typemap(doc) const SWIGTYPE & "@param $1_name $1_type"
+%typemap(doc) const SWIGTYPE && "@param $1_name $1_type"
+%typemap(doc) enum SWIGTYPE "@param $1_name enum $1_type"
+
+%typemap(doc) SWIGTYPE *INOUT, SWIGTYPE &INOUT "@param $1_name $1_type (input/output)"
+%typemap(doc) SWIGTYPE *INPUT, SWIGTYPE &INPUT "@param $1_name $1_type (input)"
+%typemap(doc) SWIGTYPE *OUTPUT, SWIGTYPE &OUTPUT "@param $1_name $1_type (output)"
+#else
+%typemap(doc) SWIGTYPE "$1_name: $1_type"
+%typemap(doc) SWIGTYPE * "$1_name: $1_type"
+%typemap(doc) const SWIGTYPE & "$1_name: $1_type"
+%typemap(doc) const SWIGTYPE && "$1_name: $1_type"
+%typemap(doc) enum SWIGTYPE "$1_name: enum $1_type"
+
+%typemap(doc) SWIGTYPE *INOUT, SWIGTYPE &INOUT "$1_name: $1_type (input/output)"
+%typemap(doc) SWIGTYPE *INPUT, SWIGTYPE &INPUT "$1_name: $1_type (input)"
+%typemap(doc) SWIGTYPE *OUTPUT, SWIGTYPE &OUTPUT "$1_name: $1_type (output)"
+#endif
+
+
+// Types to use in Python documentation for the parameters of the given C++ type.
+%typemap(doctype) bool "boolean"
+
+%define int_doctype_for_cppint_type(cppint_type)
+ %typemap(doctype) cppint_type, unsigned cppint_type "int"
+%enddef
+%formacro(int_doctype_for_cppint_type, short, int, long, long long)
+
+%typemap(doctype) size_t "int"
+
+%typemap(doctype) enum SWIGTYPE "int"
+
+%typemap(doctype) float, double, long double "float"
+
+%typemap(doctype) char*, std::string "string"
+
+%typemap(doctype) SWIGTYPE "$1_basetype"
+%typemap(doctype) SWIGTYPE * "$typemap(doctype, $*1_ltype)"
+%typemap(doctype) SWIGTYPE & "$typemap(doctype, $*1_ltype)"
diff --git a/contrib/tools/swig/Lib/python/pyerrors.swg b/contrib/tools/swig/Lib/python/pyerrors.swg
new file mode 100644
index 0000000000..10b694cde6
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyerrors.swg
@@ -0,0 +1,107 @@
+/* -----------------------------------------------------------------------------
+ * error manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIME PyObject*
+SWIG_Python_ErrorType(int code) {
+ PyObject* type = 0;
+ switch(code) {
+ case SWIG_MemoryError:
+ type = PyExc_MemoryError;
+ break;
+ case SWIG_IOError:
+ type = PyExc_IOError;
+ break;
+ case SWIG_RuntimeError:
+ type = PyExc_RuntimeError;
+ break;
+ case SWIG_IndexError:
+ type = PyExc_IndexError;
+ break;
+ case SWIG_TypeError:
+ type = PyExc_TypeError;
+ break;
+ case SWIG_DivisionByZero:
+ type = PyExc_ZeroDivisionError;
+ break;
+ case SWIG_OverflowError:
+ type = PyExc_OverflowError;
+ break;
+ case SWIG_SyntaxError:
+ type = PyExc_SyntaxError;
+ break;
+ case SWIG_ValueError:
+ type = PyExc_ValueError;
+ break;
+ case SWIG_SystemError:
+ type = PyExc_SystemError;
+ break;
+ case SWIG_AttributeError:
+ type = PyExc_AttributeError;
+ break;
+ default:
+ type = PyExc_RuntimeError;
+ }
+ return type;
+}
+
+
+SWIGRUNTIME void
+SWIG_Python_AddErrorMsg(const char* mesg)
+{
+ PyObject *type = 0;
+ PyObject *value = 0;
+ PyObject *traceback = 0;
+
+ if (PyErr_Occurred())
+ PyErr_Fetch(&type, &value, &traceback);
+ if (value) {
+ PyObject *old_str = PyObject_Str(value);
+ const char *tmp = SWIG_Python_str_AsChar(old_str);
+ PyErr_Clear();
+ Py_XINCREF(type);
+ if (tmp)
+ PyErr_Format(type, "%s %s", tmp, mesg);
+ else
+ PyErr_Format(type, "%s", mesg);
+ Py_DECREF(old_str);
+ Py_DECREF(value);
+ } else {
+ PyErr_SetString(PyExc_RuntimeError, mesg);
+ }
+}
+
+SWIGRUNTIME int
+SWIG_Python_TypeErrorOccurred(PyObject *obj)
+{
+ PyObject *error;
+ if (obj)
+ return 0;
+ error = PyErr_Occurred();
+ return error && PyErr_GivenExceptionMatches(error, PyExc_TypeError);
+}
+
+SWIGRUNTIME void
+SWIG_Python_RaiseOrModifyTypeError(const char *message)
+{
+ if (SWIG_Python_TypeErrorOccurred(NULL)) {
+ /* Use existing TypeError to preserve stacktrace and enhance with given message */
+ PyObject *newvalue;
+ PyObject *type = NULL, *value = NULL, *traceback = NULL;
+ PyErr_Fetch(&type, &value, &traceback);
+#if PY_VERSION_HEX >= 0x03000000
+ newvalue = PyUnicode_FromFormat("%S\nAdditional information:\n%s", value, message);
+#else
+ newvalue = PyString_FromFormat("%s\nAdditional information:\n%s", PyString_AsString(value), message);
+#endif
+ if (newvalue) {
+ Py_XDECREF(value);
+ PyErr_Restore(type, newvalue, traceback);
+ } else {
+ PyErr_Restore(type, value, traceback);
+ }
+ } else {
+ /* Raise TypeError using given message */
+ PyErr_SetString(PyExc_TypeError, message);
+ }
+}
diff --git a/contrib/tools/swig/Lib/python/pyfragments.swg b/contrib/tools/swig/Lib/python/pyfragments.swg
new file mode 100644
index 0000000000..535a45bdf2
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyfragments.swg
@@ -0,0 +1,23 @@
+/*
+
+ Create a file with this name, 'pyfragments.swg', in your working
+ directory and add all the %fragments you want to take precedence
+ over the default ones defined by swig.
+
+ For example, if you add:
+
+ %fragment(SWIG_AsVal_frag(int),"header") {
+ SWIGINTERNINLINE int
+ SWIG_AsVal(int)(PyObject *obj, int *val)
+ {
+ <your code here>;
+ }
+ }
+
+ this will replace the code used to retrieve an integer value for all
+ the typemaps that need it, including:
+
+ int, std::vector<int>, std::list<std::pair<int,int> >, etc.
+
+
+*/
diff --git a/contrib/tools/swig/Lib/python/pyhead.swg b/contrib/tools/swig/Lib/python/pyhead.swg
new file mode 100644
index 0000000000..6f37160bb2
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyhead.swg
@@ -0,0 +1,75 @@
+/* Compatibility macros for Python 3 */
+#if PY_VERSION_HEX >= 0x03000000
+
+#define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type)
+#define PyInt_Check(x) PyLong_Check(x)
+#define PyInt_AsLong(x) PyLong_AsLong(x)
+#define PyInt_FromLong(x) PyLong_FromLong(x)
+#define PyInt_FromSize_t(x) PyLong_FromSize_t(x)
+#define PyString_Check(name) PyBytes_Check(name)
+#define PyString_FromString(x) PyUnicode_FromString(x)
+#define PyString_Format(fmt, args) PyUnicode_Format(fmt, args)
+#define PyString_AsString(str) PyBytes_AsString(str)
+#define PyString_Size(str) PyBytes_Size(str)
+#define PyString_InternFromString(key) PyUnicode_InternFromString(key)
+#define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE
+#define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x)
+
+#endif
+
+#ifndef Py_TYPE
+# define Py_TYPE(op) ((op)->ob_type)
+#endif
+
+/* SWIG APIs for compatibility of both Python 2 & 3 */
+
+#if PY_VERSION_HEX >= 0x03000000
+# define SWIG_Python_str_FromFormat PyUnicode_FromFormat
+#else
+# define SWIG_Python_str_FromFormat PyString_FromFormat
+#endif
+
+
+SWIGINTERN char*
+SWIG_Python_str_AsChar(PyObject *str)
+{
+#if PY_VERSION_HEX >= 0x03030000
+ return (char *)PyUnicode_AsUTF8(str);
+#else
+ return PyString_AsString(str);
+#endif
+}
+
+/* Was useful for Python 3.0.x-3.2.x - now provided only for compatibility
+ * with any uses in user interface files. */
+#define SWIG_Python_str_DelForPy3(x)
+
+
+SWIGINTERN PyObject*
+SWIG_Python_str_FromChar(const char *c)
+{
+#if PY_VERSION_HEX >= 0x03000000
+ return PyUnicode_FromString(c);
+#else
+ return PyString_FromString(c);
+#endif
+}
+
+#ifndef PyObject_DEL
+# define PyObject_DEL PyObject_Del
+#endif
+
+/* SWIGPY_USE_CAPSULE is no longer used within SWIG itself, but some user interface files check for it. */
+# define SWIGPY_USE_CAPSULE
+#ifdef SWIGPYTHON_BUILTIN
+# define SWIGPY_CAPSULE_ATTR_NAME "type_pointer_capsule_builtin" SWIG_TYPE_TABLE_NAME
+#else
+# define SWIGPY_CAPSULE_ATTR_NAME "type_pointer_capsule" SWIG_TYPE_TABLE_NAME
+#endif
+# define SWIGPY_CAPSULE_NAME ("swig_runtime_data" SWIG_RUNTIME_VERSION "." SWIGPY_CAPSULE_ATTR_NAME)
+
+#if PY_VERSION_HEX < 0x03020000
+#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type)
+#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name)
+#define Py_hash_t long
+#endif
diff --git a/contrib/tools/swig/Lib/python/pyinit.swg b/contrib/tools/swig/Lib/python/pyinit.swg
new file mode 100644
index 0000000000..6833b455aa
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyinit.swg
@@ -0,0 +1,325 @@
+/* ------------------------------------------------------------
+ * The start of the Python initialization function
+ * ------------------------------------------------------------ */
+
+%insert(init) "swiginit.swg"
+
+#if defined(SWIGPYTHON_BUILTIN)
+%fragment("<stddef.h>"); // For offsetof
+#endif
+
+#if defined SWIGPYTHON_FASTPROXY && !defined SWIGPYTHON_BUILTIN
+
+%insert(runtime) %{
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Method creation and docstring support functions */
+
+SWIGINTERN PyMethodDef *SWIG_PythonGetProxyDoc(const char *name);
+SWIGINTERN PyObject *SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func);
+SWIGINTERN PyObject *SWIG_PyStaticMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func);
+
+#ifdef __cplusplus
+}
+#endif
+%}
+
+#endif
+
+%init %{
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -----------------------------------------------------------------------------
+ * constants/methods manipulation
+ * ----------------------------------------------------------------------------- */
+
+/* Install Constants */
+SWIGINTERN void
+SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) {
+ PyObject *obj = 0;
+ size_t i;
+ for (i = 0; constants[i].type; ++i) {
+ switch(constants[i].type) {
+ case SWIG_PY_POINTER:
+ obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0);
+ break;
+ case SWIG_PY_BINARY:
+ obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype));
+ break;
+ default:
+ obj = 0;
+ break;
+ }
+ if (obj) {
+ PyDict_SetItemString(d, constants[i].name, obj);
+ Py_DECREF(obj);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Patch %callback methods' docstrings to hold the callback ptrs
+ * -----------------------------------------------------------------------------*/
+
+SWIGINTERN void
+SWIG_Python_FixMethods(PyMethodDef *methods, const swig_const_info *const_table, swig_type_info **types, swig_type_info **types_initial) {
+ size_t i;
+ for (i = 0; methods[i].ml_name; ++i) {
+ const char *c = methods[i].ml_doc;
+ if (!c) continue;
+ c = strstr(c, "swig_ptr: ");
+ if (c) {
+ int j;
+ const swig_const_info *ci = 0;
+ const char *name = c + 10;
+ for (j = 0; const_table[j].type; ++j) {
+ if (strncmp(const_table[j].name, name,
+ strlen(const_table[j].name)) == 0) {
+ ci = &(const_table[j]);
+ break;
+ }
+ }
+ if (ci) {
+ void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0;
+ if (ptr) {
+ size_t shift = (ci->ptype) - types;
+ swig_type_info *ty = types_initial[shift];
+ size_t ldoc = (c - methods[i].ml_doc);
+ size_t lptr = strlen(ty->name)+2*sizeof(void*)+2;
+ char *ndoc = (char*)malloc(ldoc + lptr + 10);
+ if (ndoc) {
+ char *buff = ndoc;
+ memcpy(buff, methods[i].ml_doc, ldoc);
+ buff += ldoc;
+ memcpy(buff, "swig_ptr: ", 10);
+ buff += 10;
+ SWIG_PackVoidPtr(buff, ptr, ty->name, lptr);
+ methods[i].ml_doc = ndoc;
+ }
+ }
+ }
+ }
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+%}
+
+#if defined SWIGPYTHON_FASTPROXY && !defined SWIGPYTHON_BUILTIN
+
+%init %{
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Method creation and docstring support functions
+ * ----------------------------------------------------------------------------- */
+
+/* -----------------------------------------------------------------------------
+ * Function to find the method definition with the correct docstring for the
+ * proxy module as opposed to the low-level API
+ * ----------------------------------------------------------------------------- */
+
+SWIGINTERN PyMethodDef *SWIG_PythonGetProxyDoc(const char *name) {
+ /* Find the function in the modified method table */
+ size_t offset = 0;
+ int found = 0;
+ while (SwigMethods_proxydocs[offset].ml_meth != NULL) {
+ if (strcmp(SwigMethods_proxydocs[offset].ml_name, name) == 0) {
+ found = 1;
+ break;
+ }
+ offset++;
+ }
+ /* Use the copy with the modified docstring if available */
+ return found ? &SwigMethods_proxydocs[offset] : NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper of PyInstanceMethod_New() used in Python 3
+ * It is exported to the generated module, used for -fastproxy
+ * ----------------------------------------------------------------------------- */
+
+SWIGINTERN PyObject *SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func) {
+ if (PyCFunction_Check(func)) {
+ PyCFunctionObject *funcobj = (PyCFunctionObject *)func;
+ PyMethodDef *ml = SWIG_PythonGetProxyDoc(funcobj->m_ml->ml_name);
+ if (ml)
+ func = PyCFunction_NewEx(ml, funcobj->m_self, funcobj->m_module);
+ }
+#if PY_VERSION_HEX >= 0x03000000
+ return PyInstanceMethod_New(func);
+#else
+ return PyMethod_New(func, NULL, NULL);
+#endif
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper of PyStaticMethod_New()
+ * It is exported to the generated module, used for -fastproxy
+ * ----------------------------------------------------------------------------- */
+
+SWIGINTERN PyObject *SWIG_PyStaticMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func) {
+ if (PyCFunction_Check(func)) {
+ PyCFunctionObject *funcobj = (PyCFunctionObject *)func;
+ PyMethodDef *ml = SWIG_PythonGetProxyDoc(funcobj->m_ml->ml_name);
+ if (ml)
+ func = PyCFunction_NewEx(ml, funcobj->m_self, funcobj->m_module);
+ }
+ return PyStaticMethod_New(func);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+%}
+
+#endif
+
+%init %{
+
+/* -----------------------------------------------------------------------------*
+ * Partial Init method
+ * -----------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+
+SWIGEXPORT
+#if PY_VERSION_HEX >= 0x03000000
+ PyObject*
+#else
+ void
+#endif
+SWIG_init(void) {
+ PyObject *m, *d, *md, *globals;
+
+#if PY_VERSION_HEX >= 0x03000000
+ static struct PyModuleDef SWIG_module = {
+ PyModuleDef_HEAD_INIT,
+ SWIG_name,
+ NULL,
+ -1,
+ SwigMethods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+#endif
+
+#if defined(SWIGPYTHON_BUILTIN)
+ static SwigPyClientData SwigPyObject_clientdata = {0, 0, 0, 0, 0, 0, 0};
+ static PyGetSetDef this_getset_def = {
+ (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL
+ };
+ static SwigPyGetSet thisown_getset_closure = {
+ SwigPyObject_own,
+ SwigPyObject_own
+ };
+ static PyGetSetDef thisown_getset_def = {
+ (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure
+ };
+ PyTypeObject *builtin_pytype;
+ int builtin_base_count;
+ swig_type_info *builtin_basetype;
+ PyObject *tuple;
+ PyGetSetDescrObject *static_getset;
+ PyTypeObject *metatype;
+ PyTypeObject *swigpyobject;
+ SwigPyClientData *cd;
+ PyObject *public_interface, *public_symbol;
+ PyObject *this_descr;
+ PyObject *thisown_descr;
+ PyObject *self = 0;
+ int i;
+
+ (void)builtin_pytype;
+ (void)builtin_base_count;
+ (void)builtin_basetype;
+ (void)tuple;
+ (void)static_getset;
+ (void)self;
+
+ /* Metaclass is used to implement static member variables */
+ metatype = SwigPyObjectType();
+ assert(metatype);
+#endif
+
+ (void)globals;
+
+ /* Create singletons now to avoid potential deadlocks with multi-threaded usage after module initialization */
+ SWIG_This();
+ SWIG_Python_TypeCache();
+ SwigPyPacked_type();
+#ifndef SWIGPYTHON_BUILTIN
+ SwigPyObject_type();
+#endif
+
+ /* Fix SwigMethods to carry the callback ptrs when needed */
+ SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial);
+
+#if PY_VERSION_HEX >= 0x03000000
+ m = PyModule_Create(&SWIG_module);
+#else
+ m = Py_InitModule(SWIG_name, SwigMethods);
+#endif
+
+ md = d = PyModule_GetDict(m);
+ (void)md;
+
+ SWIG_InitializeModule(0);
+
+#ifdef SWIGPYTHON_BUILTIN
+ swigpyobject = SwigPyObject_TypeOnce();
+
+ SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject");
+ assert(SwigPyObject_stype);
+ cd = (SwigPyClientData*) SwigPyObject_stype->clientdata;
+ if (!cd) {
+ SwigPyObject_stype->clientdata = &SwigPyObject_clientdata;
+ SwigPyObject_clientdata.pytype = swigpyobject;
+ } else if (swigpyobject->tp_basicsize != cd->pytype->tp_basicsize) {
+ PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules.");
+# if PY_VERSION_HEX >= 0x03000000
+ return NULL;
+# else
+ return;
+# endif
+ }
+
+ /* All objects have a 'this' attribute */
+ this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def);
+ (void)this_descr;
+
+ /* All objects have a 'thisown' attribute */
+ thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def);
+ (void)thisown_descr;
+
+ public_interface = PyList_New(0);
+ public_symbol = 0;
+ (void)public_symbol;
+
+ PyDict_SetItemString(md, "__all__", public_interface);
+ Py_DECREF(public_interface);
+ for (i = 0; SwigMethods[i].ml_name != NULL; ++i)
+ SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name);
+ for (i = 0; swig_const_table[i].name != 0; ++i)
+ SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name);
+#endif
+
+ SWIG_InstallConstants(d,swig_const_table);
+%}
+
diff --git a/contrib/tools/swig/Lib/python/pyiterators.swg b/contrib/tools/swig/Lib/python/pyiterators.swg
new file mode 100644
index 0000000000..cb15e35cda
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyiterators.swg
@@ -0,0 +1,458 @@
+/* -----------------------------------------------------------------------------
+ * pyiterators.swg
+ *
+ * Implement a python 'output' iterator for Python 2.2 or higher.
+ *
+ * Users can derive form the SwigPyIterator to implement their
+ * own iterators. As an example (real one since we use it for STL/STD
+ * containers), the template SwigPyIterator_T does the
+ * implementation for generic C++ iterators.
+ * ----------------------------------------------------------------------------- */
+
+%include <std_common.i>
+
+%fragment("SwigPyIterator","header",fragment="<stddef.h>") {
+namespace swig {
+ struct stop_iteration {
+ };
+
+ struct SwigPyIterator {
+ private:
+ SwigPtr_PyObject _seq;
+
+ protected:
+ SwigPyIterator(PyObject *seq) : _seq(seq)
+ {
+ }
+
+ public:
+ virtual ~SwigPyIterator() {}
+
+ // Access iterator method, required by Python
+ virtual PyObject *value() const = 0;
+
+ // Forward iterator method, required by Python
+ virtual SwigPyIterator *incr(size_t n = 1) = 0;
+
+ // Backward iterator method, very common in C++, but not required in Python
+ virtual SwigPyIterator *decr(size_t /*n*/ = 1)
+ {
+ throw stop_iteration();
+ }
+
+ // Random access iterator methods, but not required in Python
+ virtual ptrdiff_t distance(const SwigPyIterator &/*x*/) const
+ {
+ throw std::invalid_argument("operation not supported");
+ }
+
+ virtual bool equal (const SwigPyIterator &/*x*/) const
+ {
+ throw std::invalid_argument("operation not supported");
+ }
+
+ // C++ common/needed methods
+ virtual SwigPyIterator *copy() const = 0;
+
+ PyObject *next()
+ {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK; // disable threads
+ PyObject *obj = value();
+ incr();
+ SWIG_PYTHON_THREAD_END_BLOCK; // re-enable threads
+ return obj;
+ }
+
+ /* Make an alias for Python 3.x */
+ PyObject *__next__()
+ {
+ return next();
+ }
+
+ PyObject *previous()
+ {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK; // disable threads
+ decr();
+ PyObject *obj = value();
+ SWIG_PYTHON_THREAD_END_BLOCK; // re-enable threads
+ return obj;
+ }
+
+ SwigPyIterator *advance(ptrdiff_t n)
+ {
+ return (n > 0) ? incr(n) : decr(-n);
+ }
+
+ bool operator == (const SwigPyIterator& x) const
+ {
+ return equal(x);
+ }
+
+ bool operator != (const SwigPyIterator& x) const
+ {
+ return ! operator==(x);
+ }
+
+ SwigPyIterator& operator += (ptrdiff_t n)
+ {
+ return *advance(n);
+ }
+
+ SwigPyIterator& operator -= (ptrdiff_t n)
+ {
+ return *advance(-n);
+ }
+
+ SwigPyIterator* operator + (ptrdiff_t n) const
+ {
+ return copy()->advance(n);
+ }
+
+ SwigPyIterator* operator - (ptrdiff_t n) const
+ {
+ return copy()->advance(-n);
+ }
+
+ ptrdiff_t operator - (const SwigPyIterator& x) const
+ {
+ return x.distance(*this);
+ }
+
+ static swig_type_info* descriptor() {
+ static int init = 0;
+ static swig_type_info* desc = 0;
+ if (!init) {
+ desc = SWIG_TypeQuery("swig::SwigPyIterator *");
+ init = 1;
+ }
+ return desc;
+ }
+ };
+
+%#if defined(SWIGPYTHON_BUILTIN)
+ inline PyObject* make_output_iterator_builtin (PyObject *pyself)
+ {
+ Py_INCREF(pyself);
+ return pyself;
+ }
+%#endif
+}
+}
+
+%fragment("SwigPyIterator_T","header",fragment="<stddef.h>",fragment="SwigPyIterator",fragment="StdTraits",fragment="StdIteratorTraits") {
+namespace swig {
+ template<typename OutIterator>
+ class SwigPyIterator_T : public SwigPyIterator
+ {
+ public:
+ typedef OutIterator out_iterator;
+ typedef typename std::iterator_traits<out_iterator>::value_type value_type;
+ typedef SwigPyIterator_T<out_iterator> self_type;
+
+ SwigPyIterator_T(out_iterator curr, PyObject *seq)
+ : SwigPyIterator(seq), current(curr)
+ {
+ }
+
+ const out_iterator& get_current() const
+ {
+ return current;
+ }
+
+
+ bool equal (const SwigPyIterator &iter) const
+ {
+ const self_type *iters = dynamic_cast<const self_type *>(&iter);
+ if (iters) {
+ return (current == iters->get_current());
+ } else {
+ throw std::invalid_argument("bad iterator type");
+ }
+ }
+
+ ptrdiff_t distance(const SwigPyIterator &iter) const
+ {
+ const self_type *iters = dynamic_cast<const self_type *>(&iter);
+ if (iters) {
+ return std::distance(current, iters->get_current());
+ } else {
+ throw std::invalid_argument("bad iterator type");
+ }
+ }
+
+ protected:
+ out_iterator current;
+ };
+
+ template <class ValueType>
+ struct from_oper
+ {
+ typedef const ValueType& argument_type;
+ typedef PyObject *result_type;
+ result_type operator()(argument_type v) const
+ {
+ return swig::from(v);
+ }
+ };
+
+ template<typename OutIterator,
+ typename ValueType = typename std::iterator_traits<OutIterator>::value_type,
+ typename FromOper = from_oper<ValueType> >
+ class SwigPyForwardIteratorOpen_T : public SwigPyIterator_T<OutIterator>
+ {
+ public:
+ FromOper from;
+ typedef OutIterator out_iterator;
+ typedef ValueType value_type;
+ typedef SwigPyIterator_T<out_iterator> base;
+ typedef SwigPyForwardIteratorOpen_T<OutIterator, ValueType, FromOper> self_type;
+
+ SwigPyForwardIteratorOpen_T(out_iterator curr, PyObject *seq)
+ : SwigPyIterator_T<OutIterator>(curr, seq)
+ {
+ }
+
+ PyObject *value() const {
+ return from(static_cast<const value_type&>(*(base::current)));
+ }
+
+ SwigPyIterator *copy() const
+ {
+ return new self_type(*this);
+ }
+
+ SwigPyIterator *incr(size_t n = 1)
+ {
+ while (n--) {
+ ++base::current;
+ }
+ return this;
+ }
+
+ };
+
+ template<typename OutIterator,
+ typename ValueType = typename std::iterator_traits<OutIterator>::value_type,
+ typename FromOper = from_oper<ValueType> >
+ class SwigPyIteratorOpen_T : public SwigPyForwardIteratorOpen_T<OutIterator, ValueType, FromOper>
+ {
+ public:
+ FromOper from;
+ typedef OutIterator out_iterator;
+ typedef ValueType value_type;
+ typedef SwigPyIterator_T<out_iterator> base;
+ typedef SwigPyIteratorOpen_T<OutIterator, ValueType, FromOper> self_type;
+
+ SwigPyIteratorOpen_T(out_iterator curr, PyObject *seq)
+ : SwigPyForwardIteratorOpen_T<OutIterator>(curr, seq)
+ {
+ }
+
+ SwigPyIterator *decr(size_t n = 1)
+ {
+ while (n--) {
+ --base::current;
+ }
+ return this;
+ }
+ };
+
+ template<typename OutIterator,
+ typename ValueType = typename std::iterator_traits<OutIterator>::value_type,
+ typename FromOper = from_oper<ValueType> >
+ class SwigPyForwardIteratorClosed_T : public SwigPyIterator_T<OutIterator>
+ {
+ public:
+ FromOper from;
+ typedef OutIterator out_iterator;
+ typedef ValueType value_type;
+ typedef SwigPyIterator_T<out_iterator> base;
+ typedef SwigPyForwardIteratorClosed_T<OutIterator, ValueType, FromOper> self_type;
+
+ SwigPyForwardIteratorClosed_T(out_iterator curr, out_iterator first, out_iterator last, PyObject *seq)
+ : SwigPyIterator_T<OutIterator>(curr, seq), begin(first), end(last)
+ {
+ }
+
+ PyObject *value() const {
+ if (base::current == end) {
+ throw stop_iteration();
+ } else {
+ return from(static_cast<const value_type&>(*(base::current)));
+ }
+ }
+
+ SwigPyIterator *copy() const
+ {
+ return new self_type(*this);
+ }
+
+ SwigPyIterator *incr(size_t n = 1)
+ {
+ while (n--) {
+ if (base::current == end) {
+ throw stop_iteration();
+ } else {
+ ++base::current;
+ }
+ }
+ return this;
+ }
+
+ protected:
+ out_iterator begin;
+ out_iterator end;
+ };
+
+ template<typename OutIterator,
+ typename ValueType = typename std::iterator_traits<OutIterator>::value_type,
+ typename FromOper = from_oper<ValueType> >
+ class SwigPyIteratorClosed_T : public SwigPyForwardIteratorClosed_T<OutIterator,ValueType,FromOper>
+ {
+ public:
+ FromOper from;
+ typedef OutIterator out_iterator;
+ typedef ValueType value_type;
+ typedef SwigPyIterator_T<out_iterator> base;
+ typedef SwigPyForwardIteratorClosed_T<OutIterator, ValueType, FromOper> base0;
+ typedef SwigPyIteratorClosed_T<OutIterator, ValueType, FromOper> self_type;
+
+ SwigPyIteratorClosed_T(out_iterator curr, out_iterator first, out_iterator last, PyObject *seq)
+ : SwigPyForwardIteratorClosed_T<OutIterator,ValueType,FromOper>(curr, first, last, seq)
+ {
+ }
+
+ SwigPyIterator *decr(size_t n = 1)
+ {
+ while (n--) {
+ if (base::current == base0::begin) {
+ throw stop_iteration();
+ } else {
+ --base::current;
+ }
+ }
+ return this;
+ }
+ };
+
+
+ template<typename OutIter>
+ inline SwigPyIterator*
+ make_output_forward_iterator(const OutIter& current, const OutIter& begin,const OutIter& end, PyObject *seq = 0)
+ {
+ return new SwigPyForwardIteratorClosed_T<OutIter>(current, begin, end, seq);
+ }
+
+ template<typename OutIter>
+ inline SwigPyIterator*
+ make_output_iterator(const OutIter& current, const OutIter& begin,const OutIter& end, PyObject *seq = 0)
+ {
+ return new SwigPyIteratorClosed_T<OutIter>(current, begin, end, seq);
+ }
+
+ template<typename OutIter>
+ inline SwigPyIterator*
+ make_output_forward_iterator(const OutIter& current, PyObject *seq = 0)
+ {
+ return new SwigPyForwardIteratorOpen_T<OutIter>(current, seq);
+ }
+
+ template<typename OutIter>
+ inline SwigPyIterator*
+ make_output_iterator(const OutIter& current, PyObject *seq = 0)
+ {
+ return new SwigPyIteratorOpen_T<OutIter>(current, seq);
+ }
+
+}
+}
+
+
+%fragment("SwigPyIterator");
+namespace swig
+{
+ /*
+ Throw a StopIteration exception
+ */
+ %ignore stop_iteration;
+ struct stop_iteration {};
+
+ %typemap(throws) stop_iteration {
+ (void)$1;
+ SWIG_SetErrorObj(PyExc_StopIteration, SWIG_Py_Void());
+ SWIG_fail;
+ }
+
+ /*
+ Mark methods that return new objects
+ */
+ %newobject SwigPyIterator::copy;
+ %newobject SwigPyIterator::operator + (ptrdiff_t n) const;
+ %newobject SwigPyIterator::operator - (ptrdiff_t n) const;
+
+ %nodirector SwigPyIterator;
+
+#if defined(SWIGPYTHON_BUILTIN)
+ %feature("python:tp_iter") SwigPyIterator "&swig::make_output_iterator_builtin";
+ %feature("python:slot", "tp_iternext", functype="iternextfunc") SwigPyIterator::__next__;
+#else
+ %extend SwigPyIterator {
+ %pythoncode %{def __iter__(self):
+ return self%}
+ }
+#endif
+
+ %catches(swig::stop_iteration) SwigPyIterator::value() const;
+ %catches(swig::stop_iteration) SwigPyIterator::incr(size_t n = 1);
+ %catches(swig::stop_iteration) SwigPyIterator::decr(size_t n = 1);
+ %catches(std::invalid_argument) SwigPyIterator::distance(const SwigPyIterator &x) const;
+ %catches(std::invalid_argument) SwigPyIterator::equal (const SwigPyIterator &x) const;
+ %catches(swig::stop_iteration) SwigPyIterator::__next__();
+ %catches(swig::stop_iteration) SwigPyIterator::next();
+ %catches(swig::stop_iteration) SwigPyIterator::previous();
+ %catches(swig::stop_iteration) SwigPyIterator::advance(ptrdiff_t n);
+ %catches(swig::stop_iteration) SwigPyIterator::operator += (ptrdiff_t n);
+ %catches(swig::stop_iteration) SwigPyIterator::operator -= (ptrdiff_t n);
+ %catches(swig::stop_iteration) SwigPyIterator::operator + (ptrdiff_t n) const;
+ %catches(swig::stop_iteration) SwigPyIterator::operator - (ptrdiff_t n) const;
+
+ struct SwigPyIterator
+ {
+ protected:
+ SwigPyIterator(PyObject *seq);
+
+ public:
+ virtual ~SwigPyIterator();
+
+ // Access iterator method, required by Python
+ virtual PyObject *value() const = 0;
+
+ // Forward iterator method, required by Python
+ virtual SwigPyIterator *incr(size_t n = 1) = 0;
+
+ // Backward iterator method, very common in C++, but not required in Python
+ virtual SwigPyIterator *decr(size_t n = 1);
+
+ // Random access iterator methods, but not required in Python
+ virtual ptrdiff_t distance(const SwigPyIterator &x) const;
+
+ virtual bool equal (const SwigPyIterator &x) const;
+
+ // C++ common/needed methods
+ virtual SwigPyIterator *copy() const = 0;
+
+ PyObject *next();
+ PyObject *__next__();
+ PyObject *previous();
+ SwigPyIterator *advance(ptrdiff_t n);
+
+ bool operator == (const SwigPyIterator& x) const;
+ bool operator != (const SwigPyIterator& x) const;
+ SwigPyIterator& operator += (ptrdiff_t n);
+ SwigPyIterator& operator -= (ptrdiff_t n);
+ SwigPyIterator* operator + (ptrdiff_t n) const;
+ SwigPyIterator* operator - (ptrdiff_t n) const;
+ ptrdiff_t operator - (const SwigPyIterator& x) const;
+ };
+}
+
diff --git a/contrib/tools/swig/Lib/python/pymacros.swg b/contrib/tools/swig/Lib/python/pymacros.swg
new file mode 100644
index 0000000000..ab7bace5ba
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pymacros.swg
@@ -0,0 +1,4 @@
+%include <typemaps/swigmacros.swg>
+
+
+
diff --git a/contrib/tools/swig/Lib/python/pyopers.swg b/contrib/tools/swig/Lib/python/pyopers.swg
new file mode 100644
index 0000000000..fd2fcc5812
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyopers.swg
@@ -0,0 +1,264 @@
+/* ------------------------------------------------------------
+ * Overloaded operator support
+
+ The directives in this file apply whether or not you use the
+ -builtin option to SWIG, but operator overloads are particularly
+ attractive when using -builtin, because they are much faster
+ than named methods.
+
+ If you're using the -builtin option to SWIG, and you want to define
+ python operator overloads beyond the defaults defined in this file,
+ here's what you need to know:
+
+ There are two ways to define a python slot function: dispatch to a
+ statically defined function; or dispatch to a method defined on the
+ operand.
+
+ To dispatch to a statically defined function, use %feature("python:<slot>"),
+ where <slot> is the name of a field in a PyTypeObject, PyNumberMethods,
+ PyMappingMethods, PySequenceMethods, or PyBufferProcs. For example:
+
+ %feature("python:tp_hash") MyClass "myHashFunc";
+
+ class MyClass {
+ public:
+ ...
+ };
+
+ %{
+ // Note: Py_hash_t was introduced in Python 3.2
+ static Py_hash_t myHashFunc(PyObject *pyobj) {
+ MyClass *cobj;
+ // Convert pyobj to cobj
+ return (cobj->field1 * (cobj->field2 << 7));
+ }
+ %}
+
+ NOTE: It is the responsibility of the programmer (that's you) to ensure
+ that a statically defined slot function has the correct signature.
+
+ If, instead, you want to dispatch to an instance method, you can
+ use %feature("python:slot"). For example:
+
+ %feature("python:slot", "tp_hash", functype="hashfunc") MyClass::myHashFunc;
+
+ class MyClass {
+ public:
+ Py_hash_t myHashFunc () const;
+ ...
+ };
+
+ NOTE: Some python slots use a method signature which does not
+ match the signature of SWIG-wrapped methods. For those slots,
+ SWIG will automatically generate a "closure" function to re-marshall
+ the arguments before dispatching to the wrapped method. Setting
+ the "functype" attribute of the feature enables SWIG to generate
+ a correct closure function.
+
+ --------------------------------------------------------------
+
+ The tp_richcompare slot is a special case: SWIG automatically generates
+ a rich compare function for all wrapped types. If a type defines C++
+ operator overloads for comparison (operator==, operator<, etc.), they
+ will be called from the generated rich compare function. If you
+ want to explicitly choose a method to handle a certain comparison
+ operation, you may use a different feature, %feature("python:compare")
+ like this:
+
+ %feature("python:compare", "Py_LT") MyClass::lessThan;
+
+ class MyClass {
+ public:
+ bool lessThan(const MyClass& other) const;
+ ...
+ };
+
+ ... where "Py_LT" is one of the rich comparison opcodes defined in the
+ python header file object.h.
+
+ If there's no method defined to handle a particular comparison operation,
+ the default behavior is to compare pointer values of the wrapped
+ C++ objects.
+
+ --------------------------------------------------------------
+
+
+ For more information about python slots, including their names and
+ signatures, you may refer to the python documentation :
+
+ http://docs.python.org/c-api/typeobj.html
+
+ * ------------------------------------------------------------ */
+
+
+#ifdef __cplusplus
+
+#if defined(SWIGPYTHON_BUILTIN)
+#define %pybinoperator(pyname,oper,functp,slt) %rename(pyname) oper; %pythonmaybecall oper; %feature("python:slot", #slt, functype=#functp) oper; %feature("python:slot", #slt, functype=#functp) pyname;
+#define %pycompare(pyname,oper,comptype) %rename(pyname) oper; %pythonmaybecall oper; %feature("python:compare", #comptype) oper; %feature("python:compare", #comptype) pyname;
+#else
+#define %pybinoperator(pyname,oper,functp,slt) %rename(pyname) oper; %pythonmaybecall oper
+#define %pycompare(pyname,oper,comptype) %pybinoperator(pyname,oper,,comptype)
+#endif
+
+%pybinoperator(__add__, *::operator+, binaryfunc, nb_add);
+%pybinoperator(__pos__, *::operator+(), unaryfunc, nb_positive);
+%pybinoperator(__pos__, *::operator+() const, unaryfunc, nb_positive);
+%pybinoperator(__sub__, *::operator-, binaryfunc, nb_subtract);
+%pybinoperator(__neg__, *::operator-(), unaryfunc, nb_negative);
+%pybinoperator(__neg__, *::operator-() const, unaryfunc, nb_negative);
+%pybinoperator(__mul__, *::operator*, binaryfunc, nb_multiply);
+%pybinoperator(__mod__, *::operator%, binaryfunc, nb_remainder);
+%pybinoperator(__lshift__, *::operator<<, binaryfunc, nb_lshift);
+%pybinoperator(__rshift__, *::operator>>, binaryfunc, nb_rshift);
+%pybinoperator(__and__, *::operator&, binaryfunc, nb_and);
+%pybinoperator(__or__, *::operator|, binaryfunc, nb_or);
+%pybinoperator(__xor__, *::operator^, binaryfunc, nb_xor);
+%pycompare(__lt__, *::operator<, Py_LT);
+%pycompare(__le__, *::operator<=, Py_LE);
+%pycompare(__gt__, *::operator>, Py_GT);
+%pycompare(__ge__, *::operator>=, Py_GE);
+%pycompare(__eq__, *::operator==, Py_EQ);
+%pycompare(__ne__, *::operator!=, Py_NE);
+
+/* Special cases */
+%rename(__invert__) *::operator~;
+%feature("python:slot", "nb_invert", functype="unaryfunc") *::operator~;
+%rename(__call__) *::operator();
+%feature("python:slot", "tp_call", functype="ternarycallfunc") *::operator();
+
+#if defined(SWIGPYTHON_BUILTIN)
+%pybinoperator(__nonzero__, *::operator bool, inquiry, nb_nonzero);
+%pybinoperator(__truediv__, *::operator/ , binaryfunc, nb_divide);
+#else
+%feature("shadow") *::operator bool %{
+def __nonzero__(self):
+ return $action(self)
+__bool__ = __nonzero__
+%};
+%rename(__nonzero__) *::operator bool;
+%feature("shadow") *::operator/ %{
+def __truediv__(self, *args):
+ return $action(self, *args)
+__div__ = __truediv__
+%};
+%rename(__truediv__) *::operator/;
+%pythonmaybecall *::operator/;
+#endif
+
+/* Ignored operators */
+%ignoreoperator(LNOT) operator!;
+%ignoreoperator(LAND) operator&&;
+%ignoreoperator(LOR) operator||;
+%ignoreoperator(EQ) *::operator=;
+%ignoreoperator(PLUSPLUS) *::operator++;
+%ignoreoperator(MINUSMINUS) *::operator--;
+%ignoreoperator(ARROWSTAR) *::operator->*;
+%ignoreoperator(INDEX) *::operator[];
+
+/*
+ Inplace operator declarations.
+
+ They translate the inplace C++ operators (+=, -=, ...) into the
+ corresponding python equivalents(__iadd__,__isub__), etc,
+ disabling the ownership of the input 'this' pointer, and assigning
+ it to the returning object:
+
+ %feature("del") *::Operator; // disables ownership by generating SWIG_POINTER_DISOWN
+ %feature("new") *::Operator; // claims ownership by generating SWIG_POINTER_OWN
+
+ This makes the most common case safe, ie:
+
+ A& A::operator+=(int i) { ...; return *this; }
+ ^^^^ ^^^^^^
+
+ will work fine, even when the resulting python object shares the
+ 'this' pointer with the input one. The input object is usually
+ deleted after the operation, including the shared 'this' pointer,
+ producing 'strange' seg faults, as reported by Lucriz
+ (lucriz@sitilandia.it).
+
+ If you have an interface that already takes care of that, ie, you
+ already are using inplace operators and you are not getting
+ seg. faults, with the new scheme you could end with 'free' elements
+ that never get deleted (maybe, not sure, it depends). But if that is
+ the case, you could recover the old behaviour using
+
+ %feature("del","0") A::operator+=;
+ %feature("new","0") A::operator+=;
+
+ which recovers the old behaviour for the class 'A', or if you are
+ 100% sure your entire system works fine in the old way, use:
+
+ %feature("del","") *::operator+=;
+ %feature("new","") *::operator+=;
+
+ The default behaviour assumes that the 'this' pointer's memory is
+ already owned by the SWIG object; it relinquishes ownership then
+ takes it back. This may not be the case though as the SWIG object
+ might be owned by memory managed elsewhere, eg after calling a
+ function that returns a C++ reference. In such case you will need
+ to use the features above to recover the old behaviour too.
+*/
+
+#if defined(SWIGPYTHON_BUILTIN)
+#define %pyinplaceoper(SwigPyOper, Oper, functp, slt) %delobject Oper; %newobject Oper; %feature("python:slot", #slt, functype=#functp) Oper; %rename(SwigPyOper) Oper
+#else
+#define %pyinplaceoper(SwigPyOper, Oper, functp, slt) %delobject Oper; %newobject Oper; %rename(SwigPyOper) Oper
+#endif
+
+%pyinplaceoper(__iadd__ , *::operator +=, binaryfunc, nb_inplace_add);
+%pyinplaceoper(__isub__ , *::operator -=, binaryfunc, nb_inplace_subtract);
+%pyinplaceoper(__imul__ , *::operator *=, binaryfunc, nb_inplace_multiply);
+%pyinplaceoper(__imod__ , *::operator %=, binaryfunc, nb_inplace_remainder);
+%pyinplaceoper(__iand__ , *::operator &=, binaryfunc, nb_inplace_and);
+%pyinplaceoper(__ior__ , *::operator |=, binaryfunc, nb_inplace_or);
+%pyinplaceoper(__ixor__ , *::operator ^=, binaryfunc, nb_inplace_xor);
+%pyinplaceoper(__ilshift__, *::operator <<=, binaryfunc, nb_inplace_lshift);
+%pyinplaceoper(__irshift__, *::operator >>=, binaryfunc, nb_inplace_rshift);
+
+/* Special cases */
+#if defined(SWIGPYTHON_BUILTIN)
+%pyinplaceoper(__itruediv__ , *::operator /=, binaryfunc, nb_inplace_divide);
+#else
+%delobject *::operator /=;
+%newobject *::operator /=;
+%feature("shadow") *::operator /= %{
+def __itruediv__(self, *args):
+ return $action(self, *args)
+__idiv__ = __itruediv__
+%};
+%rename(__itruediv__) *::operator /=;
+#endif
+
+/* Finally, in python we need to mark the binary operations to fail as
+ 'maybecall' methods */
+
+#define %pybinopermaybecall(oper) %pythonmaybecall __ ## oper ## __; %pythonmaybecall __r ## oper ## __
+
+%pybinopermaybecall(add);
+%pybinopermaybecall(pos);
+%pybinopermaybecall(pos);
+%pybinopermaybecall(sub);
+%pybinopermaybecall(neg);
+%pybinopermaybecall(neg);
+%pybinopermaybecall(mul);
+%pybinopermaybecall(div);
+%pybinopermaybecall(truediv);
+%pybinopermaybecall(mod);
+%pybinopermaybecall(lshift);
+%pybinopermaybecall(rshift);
+%pybinopermaybecall(and);
+%pybinopermaybecall(or);
+%pybinopermaybecall(xor);
+%pybinopermaybecall(lt);
+%pybinopermaybecall(le);
+%pybinopermaybecall(gt);
+%pybinopermaybecall(ge);
+%pybinopermaybecall(eq);
+%pybinopermaybecall(ne);
+
+#endif
+
+
+
diff --git a/contrib/tools/swig/Lib/python/pyprimtypes.swg b/contrib/tools/swig/Lib/python/pyprimtypes.swg
new file mode 100644
index 0000000000..6a01af17cf
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyprimtypes.swg
@@ -0,0 +1,353 @@
+/* ------------------------------------------------------------
+ * Primitive Types
+ * ------------------------------------------------------------ */
+
+/* boolean */
+
+%fragment(SWIG_From_frag(bool),"header") {
+SWIGINTERNINLINE PyObject*
+ SWIG_From_dec(bool)(bool value)
+{
+ return PyBool_FromLong(value ? 1 : 0);
+}
+}
+
+#ifdef SWIG_PYTHON_LEGACY_BOOL
+// Default prior to SWIG 3.0.0
+%fragment(SWIG_AsVal_frag(bool),"header",
+ fragment=SWIG_AsVal_frag(long)) {
+SWIGINTERN int
+SWIG_AsVal_dec(bool)(PyObject *obj, bool *val)
+{
+ int r = PyObject_IsTrue(obj);
+ if (r == -1)
+ return SWIG_ERROR;
+ if (val) *val = r ? true : false;
+ return SWIG_OK;
+}
+}
+#else
+%fragment(SWIG_AsVal_frag(bool),"header",
+ fragment=SWIG_AsVal_frag(long)) {
+SWIGINTERN int
+SWIG_AsVal_dec(bool)(PyObject *obj, bool *val)
+{
+ int r;
+ if (!PyBool_Check(obj))
+ return SWIG_ERROR;
+ r = PyObject_IsTrue(obj);
+ if (r == -1)
+ return SWIG_ERROR;
+ if (val) *val = r ? true : false;
+ return SWIG_OK;
+}
+}
+#endif
+
+/* int */
+
+%fragment(SWIG_From_frag(int),"header") {
+SWIGINTERNINLINE PyObject*
+ SWIG_From_dec(int)(int value)
+{
+ return PyInt_FromLong((long) value);
+}
+}
+
+/* unsigned int */
+
+%fragment(SWIG_From_frag(unsigned int),"header") {
+SWIGINTERNINLINE PyObject*
+ SWIG_From_dec(unsigned int)(unsigned int value)
+{
+ return PyInt_FromSize_t((size_t) value);
+}
+}
+
+/* long */
+
+%fragment(SWIG_From_frag(long),"header") {
+ %define_as(SWIG_From_dec(long), PyInt_FromLong)
+}
+
+%fragment(SWIG_AsVal_frag(long),"header",
+ fragment="SWIG_CanCastAsInteger") {
+SWIGINTERN int
+SWIG_AsVal_dec(long)(PyObject *obj, long* val)
+{
+%#if PY_VERSION_HEX < 0x03000000
+ if (PyInt_Check(obj)) {
+ if (val) *val = PyInt_AsLong(obj);
+ return SWIG_OK;
+ } else
+%#endif
+ if (PyLong_Check(obj)) {
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ return SWIG_OverflowError;
+ }
+ }
+%#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ long v = PyInt_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) {
+ if (val) *val = (long)(d);
+ return res;
+ }
+ }
+ }
+%#endif
+ return SWIG_TypeError;
+}
+}
+
+/* unsigned long */
+
+%fragment(SWIG_From_frag(unsigned long),"header",
+ fragment=SWIG_From_frag(long)) {
+SWIGINTERNINLINE PyObject*
+SWIG_From_dec(unsigned long)(unsigned long value)
+{
+ return (value > LONG_MAX) ?
+ PyLong_FromUnsignedLong(value) : PyInt_FromLong(%numeric_cast(value,long));
+}
+}
+
+%fragment(SWIG_AsVal_frag(unsigned long),"header",
+ fragment="SWIG_CanCastAsInteger") {
+SWIGINTERN int
+SWIG_AsVal_dec(unsigned long)(PyObject *obj, unsigned long *val)
+{
+%#if PY_VERSION_HEX < 0x03000000
+ if (PyInt_Check(obj)) {
+ long v = PyInt_AsLong(obj);
+ if (v >= 0) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ return SWIG_OverflowError;
+ }
+ } else
+%#endif
+ if (PyLong_Check(obj)) {
+ unsigned long v = PyLong_AsUnsignedLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ return SWIG_OverflowError;
+ }
+ }
+%#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ unsigned long v = PyLong_AsUnsignedLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ double d;
+ int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d));
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) {
+ if (val) *val = (unsigned long)(d);
+ return res;
+ }
+ }
+ }
+%#endif
+ return SWIG_TypeError;
+}
+}
+
+/* long long */
+
+%fragment(SWIG_From_frag(long long),"header",
+ fragment="SWIG_LongLongAvailable") {
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERNINLINE PyObject*
+SWIG_From_dec(long long)(long long value)
+{
+ return ((value < LONG_MIN) || (value > LONG_MAX)) ?
+ PyLong_FromLongLong(value) : PyInt_FromLong(%numeric_cast(value,long));
+}
+%#endif
+}
+
+%fragment(SWIG_AsVal_frag(long long),"header",
+ fragment=SWIG_AsVal_frag(long),
+ fragment="SWIG_CanCastAsInteger",
+ fragment="SWIG_LongLongAvailable") {
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERN int
+SWIG_AsVal_dec(long long)(PyObject *obj, long long *val)
+{
+ int res = SWIG_TypeError;
+ if (PyLong_Check(obj)) {
+ long long v = PyLong_AsLongLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ res = SWIG_OverflowError;
+ }
+ } else {
+ long v;
+ res = SWIG_AsVal(long)(obj,&v);
+ if (SWIG_IsOK(res)) {
+ if (val) *val = v;
+ return res;
+ }
+ }
+%#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ const double mant_max = 1LL << DBL_MANT_DIG;
+ const double mant_min = -mant_max;
+ double d;
+ res = SWIG_AsVal(double)(obj,&d);
+ if (SWIG_IsOK(res) && !SWIG_CanCastAsInteger(&d, mant_min, mant_max))
+ return SWIG_OverflowError;
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, mant_min, mant_max)) {
+ if (val) *val = (long long)(d);
+ return SWIG_AddCast(res);
+ }
+ res = SWIG_TypeError;
+ }
+%#endif
+ return res;
+}
+%#endif
+}
+
+/* unsigned long long */
+
+%fragment(SWIG_From_frag(unsigned long long),"header",
+ fragment="SWIG_LongLongAvailable") {
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERNINLINE PyObject*
+SWIG_From_dec(unsigned long long)(unsigned long long value)
+{
+ return (value > LONG_MAX) ?
+ PyLong_FromUnsignedLongLong(value) : PyInt_FromLong(%numeric_cast(value,long));
+}
+%#endif
+}
+
+%fragment(SWIG_AsVal_frag(unsigned long long),"header",
+ fragment=SWIG_AsVal_frag(unsigned long),
+ fragment="SWIG_CanCastAsInteger",
+ fragment="SWIG_LongLongAvailable") {
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERN int
+SWIG_AsVal_dec(unsigned long long)(PyObject *obj, unsigned long long *val)
+{
+ int res = SWIG_TypeError;
+ if (PyLong_Check(obj)) {
+ unsigned long long v = PyLong_AsUnsignedLongLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ res = SWIG_OverflowError;
+ }
+ } else {
+ unsigned long v;
+ res = SWIG_AsVal(unsigned long)(obj,&v);
+ if (SWIG_IsOK(res)) {
+ if (val) *val = v;
+ return res;
+ }
+ }
+%#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ const double mant_max = 1LL << DBL_MANT_DIG;
+ double d;
+ res = SWIG_AsVal(double)(obj,&d);
+ if (SWIG_IsOK(res) && !SWIG_CanCastAsInteger(&d, 0, mant_max))
+ return SWIG_OverflowError;
+ if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, mant_max)) {
+ if (val) *val = (unsigned long long)(d);
+ return SWIG_AddCast(res);
+ }
+ res = SWIG_TypeError;
+ }
+%#endif
+ return res;
+}
+%#endif
+}
+
+/* double */
+
+%fragment(SWIG_From_frag(double),"header") {
+ %define_as(SWIG_From_dec(double), PyFloat_FromDouble)
+}
+
+%fragment(SWIG_AsVal_frag(double),"header") {
+SWIGINTERN int
+SWIG_AsVal_dec(double)(PyObject *obj, double *val)
+{
+ int res = SWIG_TypeError;
+ if (PyFloat_Check(obj)) {
+ if (val) *val = PyFloat_AsDouble(obj);
+ return SWIG_OK;
+%#if PY_VERSION_HEX < 0x03000000
+ } else if (PyInt_Check(obj)) {
+ if (val) *val = (double) PyInt_AsLong(obj);
+ return SWIG_OK;
+%#endif
+ } else if (PyLong_Check(obj)) {
+ double v = PyLong_AsDouble(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_OK;
+ } else {
+ PyErr_Clear();
+ }
+ }
+%#ifdef SWIG_PYTHON_CAST_MODE
+ {
+ int dispatch = 0;
+ double d = PyFloat_AsDouble(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = d;
+ return SWIG_AddCast(SWIG_OK);
+ } else {
+ PyErr_Clear();
+ }
+ if (!dispatch) {
+ long v = PyLong_AsLong(obj);
+ if (!PyErr_Occurred()) {
+ if (val) *val = v;
+ return SWIG_AddCast(SWIG_AddCast(SWIG_OK));
+ } else {
+ PyErr_Clear();
+ }
+ }
+ }
+%#endif
+ return res;
+}
+}
+
+
+
diff --git a/contrib/tools/swig/Lib/python/pyrun.swg b/contrib/tools/swig/Lib/python/pyrun.swg
new file mode 100644
index 0000000000..6b119be1c4
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyrun.swg
@@ -0,0 +1,1913 @@
+/* -----------------------------------------------------------------------------
+ * pyrun.swg
+ *
+ * This file contains the runtime support for Python modules
+ * and includes code for managing global variables and pointer
+ * type checking.
+ *
+ * ----------------------------------------------------------------------------- */
+
+#if PY_VERSION_HEX < 0x02070000 /* 2.7.0 */
+# error "This version of SWIG only supports Python >= 2.7"
+#endif
+
+#if PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03030000
+# error "This version of SWIG only supports Python 3 >= 3.3"
+#endif
+
+/* Common SWIG API */
+
+/* for raw pointers */
+#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0)
+#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own)
+
+#ifdef SWIGPYTHON_BUILTIN
+#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(self, ptr, type, flags)
+#else
+#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags)
+#endif
+
+#define SWIG_InternalNewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags)
+
+#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty)
+#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src)
+#define swig_owntype int
+
+/* for raw packed data */
+#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type)
+
+/* for class or struct pointers */
+#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags)
+
+/* for C or C++ function pointers */
+#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type)
+#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(NULL, ptr, type, 0)
+
+/* for C++ member pointers, ie, member methods */
+#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type)
+
+
+/* Runtime API */
+
+#define SWIG_GetModule(clientdata) SWIG_Python_GetModule(clientdata)
+#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer)
+#define SWIG_NewClientData(obj) SwigPyClientData_New(obj)
+
+#define SWIG_SetErrorObj SWIG_Python_SetErrorObj
+#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg
+#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code)
+#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg)
+#define SWIG_fail goto fail
+
+
+/* Runtime API implementation */
+
+/* Error manipulation */
+
+SWIGINTERN void
+SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PyErr_SetObject(errtype, obj);
+ Py_DECREF(obj);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+SWIGINTERN void
+SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) {
+ SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+ PyErr_SetString(errtype, msg);
+ SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj)
+
+/* Set a constant value */
+
+#if defined(SWIGPYTHON_BUILTIN)
+
+SWIGINTERN void
+SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) {
+ PyObject *s = PyString_InternFromString(key);
+ PyList_Append(seq, s);
+ Py_DECREF(s);
+}
+
+SWIGINTERN void
+SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) {
+ PyDict_SetItemString(d, name, obj);
+ Py_DECREF(obj);
+ if (public_interface)
+ SwigPyBuiltin_AddPublicSymbol(public_interface, name);
+}
+
+#else
+
+SWIGINTERN void
+SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) {
+ PyDict_SetItemString(d, name, obj);
+ Py_DECREF(obj);
+}
+
+#endif
+
+/* Append a value to the result obj */
+
+SWIGINTERN PyObject*
+SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) {
+ if (!result) {
+ result = obj;
+ } else if (result == Py_None) {
+ Py_DECREF(result);
+ result = obj;
+ } else {
+ if (!PyList_Check(result)) {
+ PyObject *o2 = result;
+ result = PyList_New(1);
+ if (result) {
+ PyList_SET_ITEM(result, 0, o2);
+ } else {
+ Py_DECREF(obj);
+ return o2;
+ }
+ }
+ PyList_Append(result,obj);
+ Py_DECREF(obj);
+ }
+ return result;
+}
+
+/* Unpack the argument tuple */
+
+SWIGINTERN Py_ssize_t
+SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs)
+{
+ if (!args) {
+ if (!min && !max) {
+ return 1;
+ } else {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none",
+ name, (min == max ? "" : "at least "), (int)min);
+ return 0;
+ }
+ }
+ if (!PyTuple_Check(args)) {
+ if (min <= 1 && max >= 1) {
+ Py_ssize_t i;
+ objs[0] = args;
+ for (i = 1; i < max; ++i) {
+ objs[i] = 0;
+ }
+ return 2;
+ }
+ PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple");
+ return 0;
+ } else {
+ Py_ssize_t l = PyTuple_GET_SIZE(args);
+ if (l < min) {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+ name, (min == max ? "" : "at least "), (int)min, (int)l);
+ return 0;
+ } else if (l > max) {
+ PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+ name, (min == max ? "" : "at most "), (int)max, (int)l);
+ return 0;
+ } else {
+ Py_ssize_t i;
+ for (i = 0; i < l; ++i) {
+ objs[i] = PyTuple_GET_ITEM(args, i);
+ }
+ for (; l < max; ++l) {
+ objs[l] = 0;
+ }
+ return i + 1;
+ }
+ }
+}
+
+SWIGINTERN int
+SWIG_Python_CheckNoKeywords(PyObject *kwargs, const char *name) {
+ int no_kwargs = 1;
+ if (kwargs) {
+ assert(PyDict_Check(kwargs));
+ if (PyDict_Size(kwargs) > 0) {
+ PyErr_Format(PyExc_TypeError, "%s() does not take keyword arguments", name);
+ no_kwargs = 0;
+ }
+ }
+ return no_kwargs;
+}
+
+/* A functor is a function object with one single object argument */
+#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL);
+
+/*
+ Helper for static pointer initialization for both C and C++ code, for example
+ static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...);
+*/
+#ifdef __cplusplus
+#define SWIG_STATIC_POINTER(var) var
+#else
+#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Python-specific SWIG API */
+#define SWIG_newvarlink() SWIG_Python_newvarlink()
+#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr)
+#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants)
+
+/* -----------------------------------------------------------------------------
+ * global variable support code.
+ * ----------------------------------------------------------------------------- */
+
+typedef struct swig_globalvar {
+ char *name; /* Name of global variable */
+ PyObject *(*get_attr)(void); /* Return the current value */
+ int (*set_attr)(PyObject *); /* Set the value */
+ struct swig_globalvar *next;
+} swig_globalvar;
+
+typedef struct swig_varlinkobject {
+ PyObject_HEAD
+ swig_globalvar *vars;
+} swig_varlinkobject;
+
+SWIGINTERN PyObject *
+swig_varlink_repr(PyObject *SWIGUNUSEDPARM(v)) {
+#if PY_VERSION_HEX >= 0x03000000
+ return PyUnicode_InternFromString("<Swig global variables>");
+#else
+ return PyString_FromString("<Swig global variables>");
+#endif
+}
+
+SWIGINTERN PyObject *
+swig_varlink_str(PyObject *o) {
+ swig_varlinkobject *v = (swig_varlinkobject *) o;
+#if PY_VERSION_HEX >= 0x03000000
+ PyObject *str = PyUnicode_InternFromString("(");
+ PyObject *tail;
+ PyObject *joined;
+ swig_globalvar *var;
+ for (var = v->vars; var; var=var->next) {
+ tail = PyUnicode_FromString(var->name);
+ joined = PyUnicode_Concat(str, tail);
+ Py_DecRef(str);
+ Py_DecRef(tail);
+ str = joined;
+ if (var->next) {
+ tail = PyUnicode_InternFromString(", ");
+ joined = PyUnicode_Concat(str, tail);
+ Py_DecRef(str);
+ Py_DecRef(tail);
+ str = joined;
+ }
+ }
+ tail = PyUnicode_InternFromString(")");
+ joined = PyUnicode_Concat(str, tail);
+ Py_DecRef(str);
+ Py_DecRef(tail);
+ str = joined;
+#else
+ PyObject *str = PyString_FromString("(");
+ swig_globalvar *var;
+ for (var = v->vars; var; var=var->next) {
+ PyString_ConcatAndDel(&str,PyString_FromString(var->name));
+ if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", "));
+ }
+ PyString_ConcatAndDel(&str,PyString_FromString(")"));
+#endif
+ return str;
+}
+
+SWIGINTERN void
+swig_varlink_dealloc(PyObject *o) {
+ swig_varlinkobject *v = (swig_varlinkobject *) o;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ swig_globalvar *n = var->next;
+ free(var->name);
+ free(var);
+ var = n;
+ }
+}
+
+SWIGINTERN PyObject *
+swig_varlink_getattr(PyObject *o, char *n) {
+ swig_varlinkobject *v = (swig_varlinkobject *) o;
+ PyObject *res = NULL;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ if (strcmp(var->name,n) == 0) {
+ res = (*var->get_attr)();
+ break;
+ }
+ var = var->next;
+ }
+ if (res == NULL && !PyErr_Occurred()) {
+ PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n);
+ }
+ return res;
+}
+
+SWIGINTERN int
+swig_varlink_setattr(PyObject *o, char *n, PyObject *p) {
+ swig_varlinkobject *v = (swig_varlinkobject *) o;
+ int res = 1;
+ swig_globalvar *var = v->vars;
+ while (var) {
+ if (strcmp(var->name,n) == 0) {
+ res = (*var->set_attr)(p);
+ break;
+ }
+ var = var->next;
+ }
+ if (res == 1 && !PyErr_Occurred()) {
+ PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n);
+ }
+ return res;
+}
+
+SWIGINTERN PyTypeObject*
+swig_varlink_type(void) {
+ static char varlink__doc__[] = "Swig var link object";
+ static PyTypeObject varlink_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp = {
+#if PY_VERSION_HEX >= 0x03000000
+ PyVarObject_HEAD_INIT(NULL, 0)
+#else
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+#endif
+ "swigvarlink", /* tp_name */
+ sizeof(swig_varlinkobject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) swig_varlink_dealloc, /* tp_dealloc */
+#if PY_VERSION_HEX < 0x030800b4
+ (printfunc)0, /*tp_print*/
+#else
+ (Py_ssize_t)0, /*tp_vectorcall_offset*/
+#endif
+ (getattrfunc) swig_varlink_getattr, /* tp_getattr */
+ (setattrfunc) swig_varlink_setattr, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) swig_varlink_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc) swig_varlink_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ 0, /* tp_flags */
+ varlink__doc__, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+#if PY_VERSION_HEX >= 0x03040000
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+ 0, /* tp_vectorcall */
+#endif
+#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000)
+ 0, /* tp_print */
+#endif
+#ifdef COUNT_ALLOCS
+ 0, /* tp_allocs */
+ 0, /* tp_frees */
+ 0, /* tp_maxalloc */
+ 0, /* tp_prev */
+ 0 /* tp_next */
+#endif
+ };
+ varlink_type = tmp;
+ type_init = 1;
+ if (PyType_Ready(&varlink_type) < 0)
+ return NULL;
+ }
+ return &varlink_type;
+}
+
+/* Create a variable linking object for use later */
+SWIGINTERN PyObject *
+SWIG_Python_newvarlink(void) {
+ swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type());
+ if (result) {
+ result->vars = 0;
+ }
+ return ((PyObject*) result);
+}
+
+SWIGINTERN void
+SWIG_Python_addvarlink(PyObject *p, const char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) {
+ swig_varlinkobject *v = (swig_varlinkobject *) p;
+ swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar));
+ if (gv) {
+ size_t size = strlen(name)+1;
+ gv->name = (char *)malloc(size);
+ if (gv->name) {
+ memcpy(gv->name, name, size);
+ gv->get_attr = get_attr;
+ gv->set_attr = set_attr;
+ gv->next = v->vars;
+ }
+ }
+ v->vars = gv;
+}
+
+
+static PyObject *Swig_Globals_global = NULL;
+
+SWIGINTERN PyObject *
+SWIG_globals(void) {
+ if (Swig_Globals_global == NULL) {
+ Swig_Globals_global = SWIG_newvarlink();
+ }
+ return Swig_Globals_global;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Pointer declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1)
+#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN)
+
+#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1)
+
+#define SWIG_BUILTIN_TP_INIT (SWIG_POINTER_OWN << 2)
+#define SWIG_BUILTIN_INIT (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The python void return value */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Py_Void(void)
+{
+ PyObject *none = Py_None;
+ Py_INCREF(none);
+ return none;
+}
+
+/* SwigPyClientData */
+
+typedef struct {
+ PyObject *klass;
+ PyObject *newraw;
+ PyObject *newargs;
+ PyObject *destroy;
+ int delargs;
+ int implicitconv;
+ PyTypeObject *pytype;
+} SwigPyClientData;
+
+SWIGRUNTIMEINLINE int
+SWIG_Python_CheckImplicit(swig_type_info *ty)
+{
+ SwigPyClientData *data = (SwigPyClientData *)ty->clientdata;
+ int fail = data ? data->implicitconv : 0;
+ if (fail)
+ PyErr_SetString(PyExc_TypeError, "Implicit conversion is prohibited for explicit constructors.");
+ return fail;
+}
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_ExceptionType(swig_type_info *desc) {
+ SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0;
+ PyObject *klass = data ? data->klass : 0;
+ return (klass ? klass : PyExc_RuntimeError);
+}
+
+
+SWIGRUNTIME SwigPyClientData *
+SwigPyClientData_New(PyObject* obj)
+{
+ if (!obj) {
+ return 0;
+ } else {
+ SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData));
+ /* the klass element */
+ data->klass = obj;
+ Py_INCREF(data->klass);
+ /* the newraw method and newargs arguments used to create a new raw instance */
+ if (PyClass_Check(obj)) {
+ data->newraw = 0;
+ Py_INCREF(obj);
+ data->newargs = obj;
+ } else {
+ data->newraw = PyObject_GetAttrString(data->klass, "__new__");
+ if (data->newraw) {
+ data->newargs = PyTuple_New(1);
+ if (data->newargs) {
+ Py_INCREF(obj);
+ PyTuple_SET_ITEM(data->newargs, 0, obj);
+ } else {
+ Py_DECREF(data->newraw);
+ Py_DECREF(data->klass);
+ free(data);
+ return 0;
+ }
+ } else {
+ Py_INCREF(obj);
+ data->newargs = obj;
+ }
+ }
+ /* the destroy method, aka as the C++ delete method */
+ data->destroy = PyObject_GetAttrString(data->klass, "__swig_destroy__");
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ data->destroy = 0;
+ }
+ if (data->destroy) {
+ data->delargs = !(PyCFunction_GET_FLAGS(data->destroy) & METH_O);
+ } else {
+ data->delargs = 0;
+ }
+ data->implicitconv = 0;
+ data->pytype = 0;
+ return data;
+ }
+}
+
+SWIGRUNTIME void
+SwigPyClientData_Del(SwigPyClientData *data)
+{
+ Py_XDECREF(data->klass);
+ Py_XDECREF(data->newraw);
+ Py_XDECREF(data->newargs);
+ Py_XDECREF(data->destroy);
+ free(data);
+}
+
+/* =============== SwigPyObject =====================*/
+
+typedef struct {
+ PyObject_HEAD
+ void *ptr;
+ swig_type_info *ty;
+ int own;
+ PyObject *next;
+#ifdef SWIGPYTHON_BUILTIN
+ PyObject *dict;
+#endif
+} SwigPyObject;
+
+
+#ifdef SWIGPYTHON_BUILTIN
+
+SWIGRUNTIME PyObject *
+SwigPyObject_get___dict__(PyObject *v, PyObject *SWIGUNUSEDPARM(args))
+{
+ SwigPyObject *sobj = (SwigPyObject *)v;
+
+ if (!sobj->dict)
+ sobj->dict = PyDict_New();
+
+ Py_XINCREF(sobj->dict);
+ return sobj->dict;
+}
+
+#endif
+
+SWIGRUNTIME PyObject *
+SwigPyObject_long(SwigPyObject *v)
+{
+ return PyLong_FromVoidPtr(v->ptr);
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_format(const char* fmt, SwigPyObject *v)
+{
+ PyObject *res = NULL;
+ PyObject *args = PyTuple_New(1);
+ if (args) {
+ PyObject *val = SwigPyObject_long(v);
+ if (val) {
+ PyObject *ofmt;
+ PyTuple_SET_ITEM(args, 0, val);
+ ofmt = SWIG_Python_str_FromChar(fmt);
+ if (ofmt) {
+#if PY_VERSION_HEX >= 0x03000000
+ res = PyUnicode_Format(ofmt,args);
+#else
+ res = PyString_Format(ofmt,args);
+#endif
+ Py_DECREF(ofmt);
+ }
+ }
+ Py_DECREF(args);
+ }
+ return res;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_oct(SwigPyObject *v)
+{
+ return SwigPyObject_format("%o",v);
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_hex(SwigPyObject *v)
+{
+ return SwigPyObject_format("%x",v);
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_repr(SwigPyObject *v)
+{
+ const char *name = SWIG_TypePrettyName(v->ty);
+ PyObject *repr = SWIG_Python_str_FromFormat("<Swig Object of type '%s' at %p>", (name ? name : "unknown"), (void *)v);
+ if (repr && v->next) {
+ PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next);
+ if (nrep) {
+# if PY_VERSION_HEX >= 0x03000000
+ PyObject *joined = PyUnicode_Concat(repr, nrep);
+ Py_DecRef(repr);
+ Py_DecRef(nrep);
+ repr = joined;
+# else
+ PyString_ConcatAndDel(&repr,nrep);
+# endif
+ } else {
+ Py_DecRef(repr);
+ repr = NULL;
+ }
+ }
+ return repr;
+}
+
+/* We need a version taking two PyObject* parameters so it's a valid
+ * PyCFunction to use in swigobject_methods[]. */
+SWIGRUNTIME PyObject *
+SwigPyObject_repr2(PyObject *v, PyObject *SWIGUNUSEDPARM(args))
+{
+ return SwigPyObject_repr((SwigPyObject*)v);
+}
+
+SWIGRUNTIME int
+SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w)
+{
+ void *i = v->ptr;
+ void *j = w->ptr;
+ return (i < j) ? -1 : ((i > j) ? 1 : 0);
+}
+
+/* Added for Python 3.x, would it also be useful for Python 2.x? */
+SWIGRUNTIME PyObject*
+SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op)
+{
+ PyObject* res;
+ if( op != Py_EQ && op != Py_NE ) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+ res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0);
+ return res;
+}
+
+
+SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void);
+
+#ifdef SWIGPYTHON_BUILTIN
+static swig_type_info *SwigPyObject_stype = 0;
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_type(void) {
+ SwigPyClientData *cd;
+ assert(SwigPyObject_stype);
+ cd = (SwigPyClientData*) SwigPyObject_stype->clientdata;
+ assert(cd);
+ assert(cd->pytype);
+ return cd->pytype;
+}
+#else
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_type(void) {
+ static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce();
+ return type;
+}
+#endif
+
+SWIGRUNTIMEINLINE int
+SwigPyObject_Check(PyObject *op) {
+#ifdef SWIGPYTHON_BUILTIN
+ PyTypeObject *target_tp = SwigPyObject_type();
+ if (PyType_IsSubtype(op->ob_type, target_tp))
+ return 1;
+ return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0);
+#else
+ return (Py_TYPE(op) == SwigPyObject_type())
+ || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0);
+#endif
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_New(void *ptr, swig_type_info *ty, int own);
+
+static PyObject* Swig_Capsule_global = NULL;
+
+SWIGRUNTIME void
+SwigPyObject_dealloc(PyObject *v)
+{
+ SwigPyObject *sobj = (SwigPyObject *) v;
+ PyObject *next = sobj->next;
+ if (sobj->own == SWIG_POINTER_OWN) {
+ swig_type_info *ty = sobj->ty;
+ SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0;
+ PyObject *destroy = data ? data->destroy : 0;
+ if (destroy) {
+ /* destroy is always a VARARGS method */
+ PyObject *res;
+
+ /* PyObject_CallFunction() has the potential to silently drop
+ the active exception. In cases of unnamed temporary
+ variable or where we just finished iterating over a generator
+ StopIteration will be active right now, and this needs to
+ remain true upon return from SwigPyObject_dealloc. So save
+ and restore. */
+
+ PyObject *type = NULL, *value = NULL, *traceback = NULL;
+ PyErr_Fetch(&type, &value, &traceback);
+
+ if (data->delargs) {
+ /* we need to create a temporary object to carry the destroy operation */
+ PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0);
+ if (tmp) {
+ res = SWIG_Python_CallFunctor(destroy, tmp);
+ } else {
+ res = 0;
+ }
+ Py_XDECREF(tmp);
+ } else {
+ PyCFunction meth = PyCFunction_GET_FUNCTION(destroy);
+ PyObject *mself = PyCFunction_GET_SELF(destroy);
+ res = ((*meth)(mself, v));
+ }
+ if (!res)
+ PyErr_WriteUnraisable(destroy);
+
+ PyErr_Restore(type, value, traceback);
+
+ Py_XDECREF(res);
+ }
+#if !defined(SWIG_PYTHON_SILENT_MEMLEAK)
+ else {
+ const char *name = SWIG_TypePrettyName(ty);
+ printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown"));
+ }
+#endif
+ Py_XDECREF(Swig_Capsule_global);
+ }
+ Py_XDECREF(next);
+#ifdef SWIGPYTHON_BUILTIN
+ Py_XDECREF(sobj->dict);
+#endif
+ PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyObject*
+SwigPyObject_append(PyObject* v, PyObject* next)
+{
+ SwigPyObject *sobj = (SwigPyObject *) v;
+ if (!SwigPyObject_Check(next)) {
+ PyErr_SetString(PyExc_TypeError, "Attempt to append a non SwigPyObject");
+ return NULL;
+ }
+ ((SwigPyObject *)next)->next = sobj->next;
+ sobj->next = next;
+ Py_INCREF(next);
+ return SWIG_Py_Void();
+}
+
+SWIGRUNTIME PyObject*
+SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+{
+ SwigPyObject *sobj = (SwigPyObject *) v;
+ if (sobj->next) {
+ Py_INCREF(sobj->next);
+ return sobj->next;
+ } else {
+ return SWIG_Py_Void();
+ }
+}
+
+SWIGINTERN PyObject*
+SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+{
+ SwigPyObject *sobj = (SwigPyObject *)v;
+ sobj->own = 0;
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+{
+ SwigPyObject *sobj = (SwigPyObject *)v;
+ sobj->own = SWIG_POINTER_OWN;
+ return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+SwigPyObject_own(PyObject *v, PyObject *args)
+{
+ PyObject *val = 0;
+ if (!PyArg_UnpackTuple(args, "own", 0, 1, &val)) {
+ return NULL;
+ } else {
+ SwigPyObject *sobj = (SwigPyObject *)v;
+ PyObject *obj = PyBool_FromLong(sobj->own);
+ if (val) {
+ if (PyObject_IsTrue(val)) {
+ Py_DECREF(SwigPyObject_acquire(v,args));
+ } else {
+ Py_DECREF(SwigPyObject_disown(v,args));
+ }
+ }
+ return obj;
+ }
+}
+
+static PyMethodDef
+swigobject_methods[] = {
+ {"disown", SwigPyObject_disown, METH_NOARGS, "releases ownership of the pointer"},
+ {"acquire", SwigPyObject_acquire, METH_NOARGS, "acquires ownership of the pointer"},
+ {"own", SwigPyObject_own, METH_VARARGS, "returns/sets ownership of the pointer"},
+ {"append", SwigPyObject_append, METH_O, "appends another 'this' object"},
+ {"next", SwigPyObject_next, METH_NOARGS, "returns the next 'this' object"},
+ {"__repr__",SwigPyObject_repr2, METH_NOARGS, "returns object representation"},
+ {0, 0, 0, 0}
+};
+
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_TypeOnce(void) {
+ static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer";
+
+ static PyNumberMethods SwigPyObject_as_number = {
+ (binaryfunc)0, /*nb_add*/
+ (binaryfunc)0, /*nb_subtract*/
+ (binaryfunc)0, /*nb_multiply*/
+ /* nb_divide removed in Python 3 */
+#if PY_VERSION_HEX < 0x03000000
+ (binaryfunc)0, /*nb_divide*/
+#endif
+ (binaryfunc)0, /*nb_remainder*/
+ (binaryfunc)0, /*nb_divmod*/
+ (ternaryfunc)0,/*nb_power*/
+ (unaryfunc)0, /*nb_negative*/
+ (unaryfunc)0, /*nb_positive*/
+ (unaryfunc)0, /*nb_absolute*/
+ (inquiry)0, /*nb_nonzero*/
+ 0, /*nb_invert*/
+ 0, /*nb_lshift*/
+ 0, /*nb_rshift*/
+ 0, /*nb_and*/
+ 0, /*nb_xor*/
+ 0, /*nb_or*/
+#if PY_VERSION_HEX < 0x03000000
+ 0, /*nb_coerce*/
+#endif
+ (unaryfunc)SwigPyObject_long, /*nb_int*/
+#if PY_VERSION_HEX < 0x03000000
+ (unaryfunc)SwigPyObject_long, /*nb_long*/
+#else
+ 0, /*nb_reserved*/
+#endif
+ (unaryfunc)0, /*nb_float*/
+#if PY_VERSION_HEX < 0x03000000
+ (unaryfunc)SwigPyObject_oct, /*nb_oct*/
+ (unaryfunc)SwigPyObject_hex, /*nb_hex*/
+#endif
+#if PY_VERSION_HEX >= 0x03050000 /* 3.5 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_matrix_multiply */
+#elif PY_VERSION_HEX >= 0x03000000 /* 3.0 */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */
+#else
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */
+#endif
+ };
+
+ static PyTypeObject swigpyobject_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp = {
+#if PY_VERSION_HEX >= 0x03000000
+ PyVarObject_HEAD_INIT(NULL, 0)
+#else
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+#endif
+ "SwigPyObject", /* tp_name */
+ sizeof(SwigPyObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)SwigPyObject_dealloc, /* tp_dealloc */
+#if PY_VERSION_HEX < 0x030800b4
+ (printfunc)0, /*tp_print*/
+#else
+ (Py_ssize_t)0, /*tp_vectorcall_offset*/
+#endif
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+#if PY_VERSION_HEX >= 0x03000000
+ 0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */
+#else
+ (cmpfunc)SwigPyObject_compare, /* tp_compare */
+#endif
+ (reprfunc)SwigPyObject_repr, /* tp_repr */
+ &SwigPyObject_as_number, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ swigobject_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ swigobject_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+#if PY_VERSION_HEX >= 0x03040000
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+ 0, /* tp_vectorcall */
+#endif
+#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000)
+ 0, /* tp_print */
+#endif
+#ifdef COUNT_ALLOCS
+ 0, /* tp_allocs */
+ 0, /* tp_frees */
+ 0, /* tp_maxalloc */
+ 0, /* tp_prev */
+ 0 /* tp_next */
+#endif
+ };
+ swigpyobject_type = tmp;
+ type_init = 1;
+ if (PyType_Ready(&swigpyobject_type) != 0)
+ return NULL;
+ }
+ return &swigpyobject_type;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_New(void *ptr, swig_type_info *ty, int own)
+{
+ SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type());
+ if (sobj) {
+ sobj->ptr = ptr;
+ sobj->ty = ty;
+ sobj->own = own;
+ sobj->next = 0;
+#ifdef SWIGPYTHON_BUILTIN
+ sobj->dict = 0;
+#endif
+ if (own == SWIG_POINTER_OWN) {
+ /* Obtain a reference to the Python capsule wrapping the module information, so that the
+ * module information is correctly destroyed after all SWIG python objects have been freed
+ * by the GC (and corresponding destructors invoked) */
+ Py_XINCREF(Swig_Capsule_global);
+ }
+ }
+ return (PyObject *)sobj;
+}
+
+/* -----------------------------------------------------------------------------
+ * Implements a simple Swig Packed type, and use it instead of string
+ * ----------------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ void *pack;
+ swig_type_info *ty;
+ size_t size;
+} SwigPyPacked;
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_repr(SwigPyPacked *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+ return SWIG_Python_str_FromFormat("<Swig Packed at %s%s>", result, v->ty->name);
+ } else {
+ return SWIG_Python_str_FromFormat("<Swig Packed %s>", v->ty->name);
+ }
+}
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_str(SwigPyPacked *v)
+{
+ char result[SWIG_BUFFER_SIZE];
+ if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){
+ return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name);
+ } else {
+ return SWIG_Python_str_FromChar(v->ty->name);
+ }
+}
+
+SWIGRUNTIME int
+SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w)
+{
+ size_t i = v->size;
+ size_t j = w->size;
+ int s = (i < j) ? -1 : ((i > j) ? 1 : 0);
+ return s ? s : strncmp((const char *)v->pack, (const char *)w->pack, 2*v->size);
+}
+
+SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void);
+
+SWIGRUNTIME PyTypeObject*
+SwigPyPacked_type(void) {
+ static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce();
+ return type;
+}
+
+SWIGRUNTIMEINLINE int
+SwigPyPacked_Check(PyObject *op) {
+ return ((op)->ob_type == SwigPyPacked_TypeOnce())
+ || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0);
+}
+
+SWIGRUNTIME void
+SwigPyPacked_dealloc(PyObject *v)
+{
+ if (SwigPyPacked_Check(v)) {
+ SwigPyPacked *sobj = (SwigPyPacked *) v;
+ free(sobj->pack);
+ }
+ PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyTypeObject*
+SwigPyPacked_TypeOnce(void) {
+ static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer";
+ static PyTypeObject swigpypacked_type;
+ static int type_init = 0;
+ if (!type_init) {
+ const PyTypeObject tmp = {
+#if PY_VERSION_HEX>=0x03000000
+ PyVarObject_HEAD_INIT(NULL, 0)
+#else
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+#endif
+ "SwigPyPacked", /* tp_name */
+ sizeof(SwigPyPacked), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)SwigPyPacked_dealloc, /* tp_dealloc */
+#if PY_VERSION_HEX < 0x030800b4
+ (printfunc)0, /*tp_print*/
+#else
+ (Py_ssize_t)0, /*tp_vectorcall_offset*/
+#endif
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+#if PY_VERSION_HEX>=0x03000000
+ 0, /* tp_reserved in 3.0.1 */
+#else
+ (cmpfunc)SwigPyPacked_compare, /* tp_compare */
+#endif
+ (reprfunc)SwigPyPacked_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)SwigPyPacked_str, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ swigpacked_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+ 0, /* tp_free */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+#if PY_VERSION_HEX >= 0x03040000
+ 0, /* tp_finalize */
+#endif
+#if PY_VERSION_HEX >= 0x03080000
+ 0, /* tp_vectorcall */
+#endif
+#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000)
+ 0, /* tp_print */
+#endif
+#ifdef COUNT_ALLOCS
+ 0, /* tp_allocs */
+ 0, /* tp_frees */
+ 0, /* tp_maxalloc */
+ 0, /* tp_prev */
+ 0 /* tp_next */
+#endif
+ };
+ swigpypacked_type = tmp;
+ type_init = 1;
+ if (PyType_Ready(&swigpypacked_type) != 0)
+ return NULL;
+ }
+ return &swigpypacked_type;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty)
+{
+ SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type());
+ if (sobj) {
+ void *pack = malloc(size);
+ if (pack) {
+ memcpy(pack, ptr, size);
+ sobj->pack = pack;
+ sobj->ty = ty;
+ sobj->size = size;
+ } else {
+ PyObject_DEL((PyObject *) sobj);
+ sobj = 0;
+ }
+ }
+ return (PyObject *) sobj;
+}
+
+SWIGRUNTIME swig_type_info *
+SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size)
+{
+ if (SwigPyPacked_Check(obj)) {
+ SwigPyPacked *sobj = (SwigPyPacked *)obj;
+ if (sobj->size != size) return 0;
+ memcpy(ptr, sobj->pack, size);
+ return sobj->ty;
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * pointers/data manipulation
+ * ----------------------------------------------------------------------------- */
+
+static PyObject *Swig_This_global = NULL;
+
+SWIGRUNTIME PyObject *
+SWIG_This(void)
+{
+ if (Swig_This_global == NULL)
+ Swig_This_global = SWIG_Python_str_FromChar("this");
+ return Swig_This_global;
+}
+
+/* #define SWIG_PYTHON_SLOW_GETSET_THIS */
+
+/* TODO: I don't know how to implement the fast getset in Python 3 right now */
+#if PY_VERSION_HEX>=0x03000000
+#define SWIG_PYTHON_SLOW_GETSET_THIS
+#endif
+
+SWIGRUNTIME SwigPyObject *
+SWIG_Python_GetSwigThis(PyObject *pyobj)
+{
+ PyObject *obj;
+
+ if (SwigPyObject_Check(pyobj))
+ return (SwigPyObject *) pyobj;
+
+#ifdef SWIGPYTHON_BUILTIN
+ (void)obj;
+# ifdef PyWeakref_CheckProxy
+ if (PyWeakref_CheckProxy(pyobj)) {
+ pyobj = PyWeakref_GET_OBJECT(pyobj);
+ if (pyobj && SwigPyObject_Check(pyobj))
+ return (SwigPyObject*) pyobj;
+ }
+# endif
+ return NULL;
+#else
+
+ obj = 0;
+
+#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ if (PyInstance_Check(pyobj)) {
+ obj = _PyInstance_Lookup(pyobj, SWIG_This());
+ } else {
+ PyObject **dictptr = _PyObject_GetDictPtr(pyobj);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0;
+ } else {
+#ifdef PyWeakref_CheckProxy
+ if (PyWeakref_CheckProxy(pyobj)) {
+ PyObject *wobj = PyWeakref_GET_OBJECT(pyobj);
+ return wobj ? SWIG_Python_GetSwigThis(wobj) : 0;
+ }
+#endif
+ obj = PyObject_GetAttr(pyobj,SWIG_This());
+ if (obj) {
+ Py_DECREF(obj);
+ } else {
+ if (PyErr_Occurred()) PyErr_Clear();
+ return 0;
+ }
+ }
+ }
+#else
+ obj = PyObject_GetAttr(pyobj,SWIG_This());
+ if (obj) {
+ Py_DECREF(obj);
+ } else {
+ if (PyErr_Occurred()) PyErr_Clear();
+ return 0;
+ }
+#endif
+ if (obj && !SwigPyObject_Check(obj)) {
+ /* a PyObject is called 'this', try to get the 'real this'
+ SwigPyObject from it */
+ return SWIG_Python_GetSwigThis(obj);
+ }
+ return (SwigPyObject *)obj;
+#endif
+}
+
+/* Acquire a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_AcquirePtr(PyObject *obj, int own) {
+ if (own == SWIG_POINTER_OWN) {
+ SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj);
+ if (sobj) {
+ int oldown = sobj->own;
+ sobj->own = own;
+ return oldown;
+ }
+ }
+ return 0;
+}
+
+/* Convert a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) {
+ int res;
+ SwigPyObject *sobj;
+ int implicit_conv = (flags & SWIG_POINTER_IMPLICIT_CONV) != 0;
+
+ if (!obj)
+ return SWIG_ERROR;
+ if (obj == Py_None && !implicit_conv) {
+ if (ptr)
+ *ptr = 0;
+ return (flags & SWIG_POINTER_NO_NULL) ? SWIG_NullReferenceError : SWIG_OK;
+ }
+
+ res = SWIG_ERROR;
+
+ sobj = SWIG_Python_GetSwigThis(obj);
+ if (own)
+ *own = 0;
+ while (sobj) {
+ void *vptr = sobj->ptr;
+ if (ty) {
+ swig_type_info *to = sobj->ty;
+ if (to == ty) {
+ /* no type cast needed */
+ if (ptr) *ptr = vptr;
+ break;
+ } else {
+ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+ if (!tc) {
+ sobj = (SwigPyObject *)sobj->next;
+ } else {
+ if (ptr) {
+ int newmemory = 0;
+ *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+ if (newmemory == SWIG_CAST_NEW_MEMORY) {
+ assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */
+ if (own)
+ *own = *own | SWIG_CAST_NEW_MEMORY;
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ if (ptr) *ptr = vptr;
+ break;
+ }
+ }
+ if (sobj) {
+ if (((flags & SWIG_POINTER_RELEASE) == SWIG_POINTER_RELEASE) && !sobj->own) {
+ res = SWIG_ERROR_RELEASE_NOT_OWNED;
+ } else {
+ if (own)
+ *own = *own | sobj->own;
+ if (flags & SWIG_POINTER_DISOWN) {
+ sobj->own = 0;
+ }
+ if (flags & SWIG_POINTER_CLEAR) {
+ sobj->ptr = 0;
+ }
+ res = SWIG_OK;
+ }
+ } else {
+ if (implicit_conv) {
+ SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0;
+ if (data && !data->implicitconv) {
+ PyObject *klass = data->klass;
+ if (klass) {
+ PyObject *impconv;
+ data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/
+ impconv = SWIG_Python_CallFunctor(klass, obj);
+ data->implicitconv = 0;
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ impconv = 0;
+ }
+ if (impconv) {
+ SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv);
+ if (iobj) {
+ void *vptr;
+ res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0);
+ if (SWIG_IsOK(res)) {
+ if (ptr) {
+ *ptr = vptr;
+ /* transfer the ownership to 'ptr' */
+ iobj->own = 0;
+ res = SWIG_AddCast(res);
+ res = SWIG_AddNewMask(res);
+ } else {
+ res = SWIG_AddCast(res);
+ }
+ }
+ }
+ Py_DECREF(impconv);
+ }
+ }
+ }
+ if (!SWIG_IsOK(res) && obj == Py_None) {
+ if (ptr)
+ *ptr = 0;
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ res = SWIG_OK;
+ }
+ }
+ }
+ return res;
+}
+
+/* Convert a function ptr value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) {
+ if (!PyCFunction_Check(obj)) {
+ return SWIG_ConvertPtr(obj, ptr, ty, 0);
+ } else {
+ void *vptr = 0;
+ swig_cast_info *tc;
+
+ /* here we get the method pointer for callbacks */
+ const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc);
+ const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0;
+ if (desc)
+ desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0;
+ if (!desc)
+ return SWIG_ERROR;
+ tc = SWIG_TypeCheck(desc,ty);
+ if (tc) {
+ int newmemory = 0;
+ *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+ assert(!newmemory); /* newmemory handling not yet implemented */
+ } else {
+ return SWIG_ERROR;
+ }
+ return SWIG_OK;
+ }
+}
+
+/* Convert a packed pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) {
+ swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz);
+ if (!to) return SWIG_ERROR;
+ if (ty) {
+ if (to != ty) {
+ /* check type cast? */
+ swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+ if (!tc) return SWIG_ERROR;
+ }
+ }
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Create a new pointer object
+ * ----------------------------------------------------------------------------- */
+
+/*
+ Create a new instance object, without calling __init__, and set the
+ 'this' attribute.
+*/
+
+SWIGRUNTIME PyObject*
+SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this)
+{
+ PyObject *inst = 0;
+ PyObject *newraw = data->newraw;
+ if (newraw) {
+ inst = PyObject_Call(newraw, data->newargs, NULL);
+ if (inst) {
+#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ if (dict == NULL) {
+ dict = PyDict_New();
+ *dictptr = dict;
+ }
+ if (dict) {
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ } else{
+ Py_DECREF(inst);
+ inst = 0;
+ }
+ }
+#else
+ if (PyObject_SetAttr(inst, SWIG_This(), swig_this) == -1) {
+ Py_DECREF(inst);
+ inst = 0;
+ }
+#endif
+ }
+ } else {
+#if PY_VERSION_HEX >= 0x03000000
+ PyObject *empty_args = PyTuple_New(0);
+ if (empty_args) {
+ PyObject *empty_kwargs = PyDict_New();
+ if (empty_kwargs) {
+ inst = ((PyTypeObject *)data->newargs)->tp_new((PyTypeObject *)data->newargs, empty_args, empty_kwargs);
+ Py_DECREF(empty_kwargs);
+ if (inst) {
+ if (PyObject_SetAttr(inst, SWIG_This(), swig_this) == -1) {
+ Py_DECREF(inst);
+ inst = 0;
+ } else {
+ PyType_Modified(Py_TYPE(inst));
+ }
+ }
+ }
+ Py_DECREF(empty_args);
+ }
+#else
+ PyObject *dict = PyDict_New();
+ if (dict) {
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ inst = PyInstance_NewRaw(data->newargs, dict);
+ Py_DECREF(dict);
+ }
+#endif
+ }
+ return inst;
+}
+
+SWIGRUNTIME int
+SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this)
+{
+#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+ PyObject *dict = *dictptr;
+ if (dict == NULL) {
+ dict = PyDict_New();
+ *dictptr = dict;
+ }
+ if (dict) {
+ return PyDict_SetItem(dict, SWIG_This(), swig_this);
+ } else{
+ return -1;
+ }
+ }
+#endif
+ return PyObject_SetAttr(inst, SWIG_This(), swig_this);
+}
+
+
+SWIGINTERN PyObject *
+SWIG_Python_InitShadowInstance(PyObject *args) {
+ PyObject *obj[2];
+ if (!SWIG_Python_UnpackTuple(args, "swiginit", 2, 2, obj)) {
+ return NULL;
+ } else {
+ SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]);
+ if (sthis) {
+ Py_DECREF(SwigPyObject_append((PyObject*) sthis, obj[1]));
+ } else {
+ if (SWIG_Python_SetSwigThis(obj[0], obj[1]) != 0)
+ return NULL;
+ }
+ return SWIG_Py_Void();
+ }
+}
+
+/* Create a new pointer object */
+
+SWIGRUNTIME PyObject *
+SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) {
+ SwigPyClientData *clientdata;
+ PyObject * robj;
+ int own;
+
+ if (!ptr)
+ return SWIG_Py_Void();
+
+ clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0;
+ own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0;
+ if (clientdata && clientdata->pytype) {
+ SwigPyObject *newobj;
+ if (flags & SWIG_BUILTIN_TP_INIT) {
+ newobj = (SwigPyObject*) self;
+ if (newobj->ptr) {
+ PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0);
+ while (newobj->next)
+ newobj = (SwigPyObject *) newobj->next;
+ newobj->next = next_self;
+ newobj = (SwigPyObject *)next_self;
+#ifdef SWIGPYTHON_BUILTIN
+ newobj->dict = 0;
+#endif
+ }
+ } else {
+ newobj = PyObject_New(SwigPyObject, clientdata->pytype);
+#ifdef SWIGPYTHON_BUILTIN
+ if (newobj) {
+ newobj->dict = 0;
+ }
+#endif
+ }
+ if (newobj) {
+ newobj->ptr = ptr;
+ newobj->ty = type;
+ newobj->own = own;
+ newobj->next = 0;
+ return (PyObject*) newobj;
+ }
+ return SWIG_Py_Void();
+ }
+
+ assert(!(flags & SWIG_BUILTIN_TP_INIT));
+
+ robj = SwigPyObject_New(ptr, type, own);
+ if (robj && clientdata && !(flags & SWIG_POINTER_NOSHADOW)) {
+ PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj);
+ Py_DECREF(robj);
+ robj = inst;
+ }
+ return robj;
+}
+
+/* Create a new packed object */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) {
+ return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void();
+}
+
+/* -----------------------------------------------------------------------------*
+ * Get type list
+ * -----------------------------------------------------------------------------*/
+
+#ifdef SWIG_LINK_RUNTIME
+void *SWIG_ReturnGlobalTypeList(void *);
+#endif
+
+static PyObject *Swig_TypeCache_global = NULL;
+
+/* The python cached type query */
+SWIGRUNTIME PyObject *
+SWIG_Python_TypeCache(void) {
+ if (Swig_TypeCache_global == NULL) {
+ Swig_TypeCache_global = PyDict_New();
+ }
+ return Swig_TypeCache_global;
+}
+
+SWIGRUNTIME swig_module_info *
+SWIG_Python_GetModule(void *SWIGUNUSEDPARM(clientdata)) {
+#ifdef SWIG_LINK_RUNTIME
+ static void *type_pointer = (void *)0;
+ /* first check if module already created */
+ if (!type_pointer) {
+ type_pointer = SWIG_ReturnGlobalTypeList((void *)0);
+ }
+#else
+ void *type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0);
+ if (PyErr_Occurred()) {
+ PyErr_Clear();
+ type_pointer = (void *)0;
+ }
+#endif
+ return (swig_module_info *) type_pointer;
+}
+
+
+static int interpreter_counter = 0; // how many (sub-)interpreters are using swig_module's types
+
+SWIGRUNTIME void
+SWIG_Python_DestroyModule(PyObject *obj)
+{
+ swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME);
+ swig_type_info **types = swig_module->types;
+ size_t i;
+ if (--interpreter_counter != 0) // another sub-interpreter may still be using the swig_module's types
+ return;
+ for (i =0; i < swig_module->size; ++i) {
+ swig_type_info *ty = types[i];
+ if (ty->owndata) {
+ SwigPyClientData *data = (SwigPyClientData *) ty->clientdata;
+ ty->clientdata = 0;
+ if (data) SwigPyClientData_Del(data);
+ }
+ }
+ Py_DECREF(SWIG_This());
+ Swig_This_global = NULL;
+ Py_DECREF(SWIG_globals());
+ Swig_Globals_global = NULL;
+ Py_DECREF(SWIG_Python_TypeCache());
+ Swig_TypeCache_global = NULL;
+ Swig_Capsule_global = NULL;
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetModule(swig_module_info *swig_module) {
+#if PY_VERSION_HEX >= 0x03000000
+ /* Add a dummy module object into sys.modules */
+ PyObject *module = PyImport_AddModule("swig_runtime_data" SWIG_RUNTIME_VERSION);
+#else
+ static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */
+ PyObject *module = Py_InitModule("swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table);
+#endif
+ PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule);
+ if (pointer && module) {
+ if (PyModule_AddObject(module, SWIGPY_CAPSULE_ATTR_NAME, pointer) == 0) {
+ ++interpreter_counter;
+ Swig_Capsule_global = pointer;
+ } else {
+ Py_DECREF(pointer);
+ }
+ } else {
+ Py_XDECREF(pointer);
+ }
+}
+
+SWIGRUNTIME swig_type_info *
+SWIG_Python_TypeQuery(const char *type)
+{
+ PyObject *cache = SWIG_Python_TypeCache();
+ PyObject *key = SWIG_Python_str_FromChar(type);
+ PyObject *obj = PyDict_GetItem(cache, key);
+ swig_type_info *descriptor;
+ if (obj) {
+ descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL);
+ } else {
+ swig_module_info *swig_module = SWIG_GetModule(0);
+ descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type);
+ if (descriptor) {
+ obj = PyCapsule_New((void*) descriptor, NULL, NULL);
+ if (obj) {
+ PyDict_SetItem(cache, key, obj);
+ Py_DECREF(obj);
+ }
+ }
+ }
+ Py_DECREF(key);
+ return descriptor;
+}
+
+/*
+ For backward compatibility only
+*/
+#define SWIG_POINTER_EXCEPTION 0
+#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg)
+#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags)
+
+SWIGRUNTIME int
+SWIG_Python_AddErrMesg(const char* mesg, int infront)
+{
+ if (PyErr_Occurred()) {
+ PyObject *type = 0;
+ PyObject *value = 0;
+ PyObject *traceback = 0;
+ PyErr_Fetch(&type, &value, &traceback);
+ if (value) {
+ PyObject *old_str = PyObject_Str(value);
+ const char *tmp = SWIG_Python_str_AsChar(old_str);
+ const char *errmesg = tmp ? tmp : "Invalid error message";
+ Py_XINCREF(type);
+ PyErr_Clear();
+ if (infront) {
+ PyErr_Format(type, "%s %s", mesg, errmesg);
+ } else {
+ PyErr_Format(type, "%s %s", errmesg, mesg);
+ }
+ Py_DECREF(old_str);
+ }
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+SWIGRUNTIME int
+SWIG_Python_ArgFail(int argnum)
+{
+ if (PyErr_Occurred()) {
+ /* add information about failing argument */
+ char mesg[256];
+ PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum);
+ return SWIG_Python_AddErrMesg(mesg, 1);
+ } else {
+ return 0;
+ }
+}
+
+SWIGRUNTIMEINLINE const char *
+SwigPyObject_GetDesc(PyObject *self)
+{
+ SwigPyObject *v = (SwigPyObject *)self;
+ swig_type_info *ty = v ? v->ty : 0;
+ return ty ? ty->str : "";
+}
+
+SWIGRUNTIME void
+SWIG_Python_TypeError(const char *type, PyObject *obj)
+{
+ if (type) {
+#if defined(SWIG_COBJECT_TYPES)
+ if (obj && SwigPyObject_Check(obj)) {
+ const char *otype = (const char *) SwigPyObject_GetDesc(obj);
+ if (otype) {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received",
+ type, otype);
+ return;
+ }
+ } else
+#endif
+ {
+ const char *otype = (obj ? obj->ob_type->tp_name : 0);
+ if (otype) {
+ PyObject *str = PyObject_Str(obj);
+ const char *cstr = str ? SWIG_Python_str_AsChar(str) : 0;
+ if (cstr) {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received",
+ type, otype, cstr);
+ } else {
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received",
+ type, otype);
+ }
+ Py_XDECREF(str);
+ return;
+ }
+ }
+ PyErr_Format(PyExc_TypeError, "a '%s' is expected", type);
+ } else {
+ PyErr_Format(PyExc_TypeError, "unexpected type is received");
+ }
+}
+
+
+/* Convert a pointer value, signal an exception on a type mismatch */
+SWIGRUNTIME void *
+SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) {
+ void *result;
+ if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) {
+ PyErr_Clear();
+ }
+ return result;
+}
+
+#ifdef SWIGPYTHON_BUILTIN
+SWIGRUNTIME int
+SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) {
+ PyTypeObject *tp = obj->ob_type;
+ PyObject *descr;
+ PyObject *encoded_name;
+ descrsetfunc f;
+ int res = -1;
+
+# ifdef Py_USING_UNICODE
+ if (PyString_Check(name)) {
+ name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL);
+ if (!name)
+ return -1;
+ } else if (!PyUnicode_Check(name))
+# else
+ if (!PyString_Check(name))
+# endif
+ {
+ PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name);
+ return -1;
+ } else {
+ Py_INCREF(name);
+ }
+
+ if (!tp->tp_dict) {
+ if (PyType_Ready(tp) != 0)
+ goto done;
+ }
+
+ descr = _PyType_Lookup(tp, name);
+ f = NULL;
+ if (descr != NULL)
+ f = descr->ob_type->tp_descr_set;
+ if (!f) {
+ if (PyString_Check(name)) {
+ encoded_name = name;
+ Py_INCREF(name);
+ } else {
+ encoded_name = PyUnicode_AsUTF8String(name);
+ if (!encoded_name)
+ goto done;
+ }
+ PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name));
+ Py_DECREF(encoded_name);
+ } else {
+ res = f(descr, obj, value);
+ }
+
+ done:
+ Py_DECREF(name);
+ return res;
+}
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/contrib/tools/swig/Lib/python/pyruntime.swg b/contrib/tools/swig/Lib/python/pyruntime.swg
new file mode 100644
index 0000000000..1d028adaf0
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyruntime.swg
@@ -0,0 +1,49 @@
+%insert(runtime) %{
+#if defined(__GNUC__) && defined(_WIN32) && !defined(SWIG_PYTHON_NO_HYPOT_WORKAROUND)
+/* Workaround for '::hypot' has not been declared', see https://bugs.python.org/issue11566 */
+# include <math.h>
+#endif
+
+#if !defined(PY_SSIZE_T_CLEAN) && !defined(SWIG_NO_PY_SSIZE_T_CLEAN)
+#define PY_SSIZE_T_CLEAN
+#endif
+
+#if __GNUC__ >= 7
+#pragma GCC diagnostic push
+#if defined(__cplusplus) && __cplusplus >=201703L
+#pragma GCC diagnostic ignored "-Wregister" /* For python-2.7 headers that use register */
+#endif
+#endif
+
+#if defined(_DEBUG) && defined(SWIG_PYTHON_INTERPRETER_NO_DEBUG)
+/* Use debug wrappers with the Python release dll */
+
+#if defined(_MSC_VER) && _MSC_VER >= 1929
+/* Workaround compilation errors when redefining _DEBUG in MSVC 2019 version 16.10 and later
+ * See https://github.com/swig/swig/issues/2090 */
+# include <corecrt.h>
+#endif
+
+# undef _DEBUG
+# include <Python.h>
+# define _DEBUG 1
+#else
+# include <Python.h>
+#endif
+
+#if __GNUC__ >= 7
+#pragma GCC diagnostic pop
+#endif
+%}
+
+%insert(runtime) "swigrun.swg"; /* SWIG API */
+%insert(runtime) "swigerrors.swg"; /* SWIG errors */
+%insert(runtime) "pyhead.swg"; /* Python includes and fixes */
+%insert(runtime) "pyerrors.swg"; /* Python errors */
+%insert(runtime) "pythreads.swg"; /* Python thread code */
+%insert(runtime) "pyapi.swg"; /* Python API */
+%insert(runtime) "pyrun.swg"; /* Python run-time code */
+
+#if defined(SWIGPYTHON_BUILTIN)
+%insert(runtime) "builtin.swg"; /* Specialization for classes with single inheritance */
+#endif
diff --git a/contrib/tools/swig/Lib/python/pystdcommon.swg b/contrib/tools/swig/Lib/python/pystdcommon.swg
new file mode 100644
index 0000000000..afa71350a9
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pystdcommon.swg
@@ -0,0 +1,265 @@
+%fragment("StdTraits","header",fragment="StdTraitsCommon")
+{
+namespace swig {
+ /*
+ Traits that provides the from method
+ */
+ template <class Type> struct traits_from_ptr {
+ static PyObject *from(Type *val, int owner = 0) {
+ return SWIG_InternalNewPointerObj(val, type_info<Type>(), owner);
+ }
+ };
+
+ template <class Type> struct traits_from {
+ static PyObject *from(const Type& val) {
+ return traits_from_ptr<Type>::from(new Type(val), 1);
+ }
+ };
+
+ template <class Type> struct traits_from<Type *> {
+ static PyObject *from(Type* val) {
+ return traits_from_ptr<Type>::from(val, 0);
+ }
+ };
+
+ template <class Type> struct traits_from<const Type *> {
+ static PyObject *from(const Type* val) {
+ return traits_from_ptr<Type>::from(const_cast<Type*>(val), 0);
+ }
+ };
+
+
+ template <class Type>
+ inline PyObject *from(const Type& val) {
+ return traits_from<Type>::from(val);
+ }
+
+ template <class Type>
+ inline PyObject *from_ptr(Type* val, int owner) {
+ return traits_from_ptr<Type>::from(val, owner);
+ }
+
+ /*
+ Traits that provides the asval/as/check method
+ */
+ template <class Type>
+ struct traits_asptr {
+ static int asptr(PyObject *obj, Type **val) {
+ int res = SWIG_ERROR;
+ swig_type_info *descriptor = type_info<Type>();
+ if (val) {
+ Type *p = 0;
+ int newmem = 0;
+ res = descriptor ? SWIG_ConvertPtrAndOwn(obj, (void **)&p, descriptor, 0, &newmem) : SWIG_ERROR;
+ if (SWIG_IsOK(res)) {
+ if (newmem & SWIG_CAST_NEW_MEMORY) {
+ res |= SWIG_NEWOBJMASK;
+ }
+ *val = p;
+ }
+ } else {
+ res = descriptor ? SWIG_ConvertPtr(obj, 0, descriptor, 0) : SWIG_ERROR;
+ }
+ return res;
+ }
+ };
+
+ template <class Type>
+ inline int asptr(PyObject *obj, Type **vptr) {
+ return traits_asptr<Type>::asptr(obj, vptr);
+ }
+
+ template <class Type>
+ struct traits_asval {
+ static int asval(PyObject *obj, Type *val) {
+ if (val) {
+ Type *p = 0;
+ int res = traits_asptr<Type>::asptr(obj, &p);
+ if (!SWIG_IsOK(res)) return res;
+ if (p) {
+ typedef typename noconst_traits<Type>::noconst_type noconst_type;
+ *(const_cast<noconst_type*>(val)) = *p;
+ if (SWIG_IsNewObj(res)){
+ %delete(p);
+ res = SWIG_DelNewMask(res);
+ }
+ return res;
+ } else {
+ return SWIG_ERROR;
+ }
+ } else {
+ return traits_asptr<Type>::asptr(obj, (Type **)(0));
+ }
+ }
+ };
+
+ template <class Type> struct traits_asval<Type*> {
+ static int asval(PyObject *obj, Type **val) {
+ if (val) {
+ typedef typename noconst_traits<Type>::noconst_type noconst_type;
+ noconst_type *p = 0;
+ int res = traits_asptr<noconst_type>::asptr(obj, &p);
+ if (SWIG_IsOK(res)) {
+ *(const_cast<noconst_type**>(val)) = p;
+ }
+ return res;
+ } else {
+ return traits_asptr<Type>::asptr(obj, (Type **)(0));
+ }
+ }
+ };
+
+ template <class Type>
+ inline int asval(PyObject *obj, Type *val) {
+ return traits_asval<Type>::asval(obj, val);
+ }
+
+ template <class Type>
+ struct traits_as<Type, value_category> {
+ static Type as(PyObject *obj) {
+ Type v;
+ int res = asval(obj, &v);
+ if (!obj || !SWIG_IsOK(res)) {
+ if (!PyErr_Occurred()) {
+ ::%type_error(swig::type_name<Type>());
+ }
+ throw std::invalid_argument("bad type");
+ }
+ return v;
+ }
+ };
+
+ template <class Type>
+ struct traits_as<Type, pointer_category> {
+ static Type as(PyObject *obj) {
+ Type *v = 0;
+ int res = (obj ? traits_asptr<Type>::asptr(obj, &v) : SWIG_ERROR);
+ if (SWIG_IsOK(res) && v) {
+ if (SWIG_IsNewObj(res)) {
+ Type r(*v);
+ %delete(v);
+ return r;
+ } else {
+ return *v;
+ }
+ } else {
+ if (!PyErr_Occurred()) {
+ %type_error(swig::type_name<Type>());
+ }
+ throw std::invalid_argument("bad type");
+ }
+ }
+ };
+
+ template <class Type>
+ struct traits_as<Type*, pointer_category> {
+ static Type* as(PyObject *obj) {
+ Type *v = 0;
+ int res = (obj ? traits_asptr<Type>::asptr(obj, &v) : SWIG_ERROR);
+ if (SWIG_IsOK(res)) {
+ return v;
+ } else {
+ if (!PyErr_Occurred()) {
+ %type_error(swig::type_name<Type>());
+ }
+ throw std::invalid_argument("bad type");
+ }
+ }
+ };
+
+ template <class Type>
+ inline Type as(PyObject *obj) {
+ return traits_as<Type, typename traits<Type>::category>::as(obj);
+ }
+
+ template <class Type>
+ struct traits_check<Type, value_category> {
+ static bool check(PyObject *obj) {
+ int res = obj ? asval(obj, (Type *)(0)) : SWIG_ERROR;
+ return SWIG_IsOK(res) ? true : false;
+ }
+ };
+
+ template <class Type>
+ struct traits_check<Type, pointer_category> {
+ static bool check(PyObject *obj) {
+ int res = obj ? asptr(obj, (Type **)(0)) : SWIG_ERROR;
+ return SWIG_IsOK(res) ? true : false;
+ }
+ };
+
+ template <class Type>
+ inline bool check(PyObject *obj) {
+ return traits_check<Type, typename traits<Type>::category>::check(obj);
+ }
+}
+}
+
+//
+// Backward compatibility
+//
+
+#ifdef SWIG_PYTHON_BACKWARD_COMP
+%fragment("<string>");
+%{
+PyObject* SwigInt_FromBool(bool b) {
+ return PyInt_FromLong(b ? 1L : 0L);
+}
+double SwigNumber_Check(PyObject* o) {
+ return PyFloat_Check(o) || PyInt_Check(o) || PyLong_Check(o);
+}
+double SwigNumber_AsDouble(PyObject* o) {
+ return PyFloat_Check(o) ? PyFloat_AsDouble(o)
+ : (PyInt_Check(o) ? double(PyInt_AsLong(o))
+ : double(PyLong_AsLong(o)));
+}
+PyObject* SwigString_FromString(const std::string& s) {
+ return PyString_FromStringAndSize(s.data(),s.size());
+}
+std::string SwigString_AsString(PyObject* o) {
+ return std::string(PyString_AsString(o));
+}
+%}
+
+#endif
+
+
+%define %specialize_std_container(Type,Check,As,From)
+%{
+namespace swig {
+ template <> struct traits_asval<Type > {
+ typedef Type value_type;
+ static int asval(PyObject *obj, value_type *val) {
+ if (Check(obj)) {
+ if (val) *val = As(obj);
+ return SWIG_OK;
+ }
+ return SWIG_ERROR;
+ }
+ };
+ template <> struct traits_from<Type > {
+ typedef Type value_type;
+ static PyObject *from(const value_type& val) {
+ return From(val);
+ }
+ };
+
+ template <>
+ struct traits_check<Type, value_category> {
+ static int check(PyObject *obj) {
+ int res = Check(obj);
+ return obj && res ? res : 0;
+ }
+ };
+}
+%}
+%enddef
+
+
+#define specialize_std_vector(Type,Check,As,From) %specialize_std_container(%arg(Type),Check,As,From)
+#define specialize_std_list(Type,Check,As,From) %specialize_std_container(%arg(Type),Check,As,From)
+#define specialize_std_deque(Type,Check,As,From) %specialize_std_container(%arg(Type),Check,As,From)
+#define specialize_std_set(Type,Check,As,From) %specialize_std_container(%arg(Type),Check,As,From)
+#define specialize_std_multiset(Type,Check,As,From) %specialize_std_container(%arg(Type),Check,As,From)
+#define specialize_std_unordered_set(Type,Check,As,From) %specialize_std_container(%arg(Type),Check,As,From)
+#define specialize_std_unordered_multiset(Type,Check,As,From) %specialize_std_container(%arg(Type),Check,As,From)
diff --git a/contrib/tools/swig/Lib/python/pystrings.swg b/contrib/tools/swig/Lib/python/pystrings.swg
new file mode 100644
index 0000000000..64ed685e8c
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pystrings.swg
@@ -0,0 +1,139 @@
+/* ------------------------------------------------------------
+ * utility methods for char strings
+ * ------------------------------------------------------------ */
+%fragment("SWIG_AsCharPtrAndSize","header",fragment="SWIG_pchar_descriptor") {
+SWIGINTERN int
+SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc)
+{
+%#if PY_VERSION_HEX>=0x03000000
+%#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+ if (PyBytes_Check(obj))
+%#else
+ if (PyUnicode_Check(obj))
+%#endif
+%#else
+ if (PyString_Check(obj))
+%#endif
+ {
+ char *cstr; Py_ssize_t len;
+ int ret = SWIG_OK;
+%#if PY_VERSION_HEX>=0x03000000
+%#if !defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+ if (!alloc && cptr) {
+ /* We can't allow converting without allocation, since the internal
+ representation of string in Python 3 is UCS-2/UCS-4 but we require
+ a UTF-8 representation.
+ TODO(bhy) More detailed explanation */
+ return SWIG_RuntimeError;
+ }
+ obj = PyUnicode_AsUTF8String(obj);
+ if (!obj)
+ return SWIG_TypeError;
+ if (alloc)
+ *alloc = SWIG_NEWOBJ;
+%#endif
+ if (PyBytes_AsStringAndSize(obj, &cstr, &len) == -1)
+ return SWIG_TypeError;
+%#else
+ if (PyString_AsStringAndSize(obj, &cstr, &len) == -1)
+ return SWIG_TypeError;
+%#endif
+ if (cptr) {
+ if (alloc) {
+ if (*alloc == SWIG_NEWOBJ) {
+ *cptr = %new_copy_array(cstr, len + 1, char);
+ *alloc = SWIG_NEWOBJ;
+ } else {
+ *cptr = cstr;
+ *alloc = SWIG_OLDOBJ;
+ }
+ } else {
+%#if PY_VERSION_HEX>=0x03000000
+%#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+ *cptr = PyBytes_AsString(obj);
+%#else
+ assert(0); /* Should never reach here with Unicode strings in Python 3 */
+%#endif
+%#else
+ *cptr = SWIG_Python_str_AsChar(obj);
+ if (!*cptr)
+ ret = SWIG_TypeError;
+%#endif
+ }
+ }
+ if (psize) *psize = len + 1;
+%#if PY_VERSION_HEX>=0x03000000 && !defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+ Py_XDECREF(obj);
+%#endif
+ return ret;
+ } else {
+%#if defined(SWIG_PYTHON_2_UNICODE)
+%#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+%#error "Cannot use both SWIG_PYTHON_2_UNICODE and SWIG_PYTHON_STRICT_BYTE_CHAR at once"
+%#endif
+%#if PY_VERSION_HEX<0x03000000
+ if (PyUnicode_Check(obj)) {
+ char *cstr; Py_ssize_t len;
+ if (!alloc && cptr) {
+ return SWIG_RuntimeError;
+ }
+ obj = PyUnicode_AsUTF8String(obj);
+ if (!obj)
+ return SWIG_TypeError;
+ if (PyString_AsStringAndSize(obj, &cstr, &len) != -1) {
+ if (cptr) {
+ if (alloc) *alloc = SWIG_NEWOBJ;
+ *cptr = %new_copy_array(cstr, len + 1, char);
+ }
+ if (psize) *psize = len + 1;
+
+ Py_XDECREF(obj);
+ return SWIG_OK;
+ } else {
+ Py_XDECREF(obj);
+ }
+ }
+%#endif
+%#endif
+
+ swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
+ if (pchar_descriptor) {
+ void* vptr = 0;
+ if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) {
+ if (cptr) *cptr = (char *) vptr;
+ if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0;
+ if (alloc) *alloc = SWIG_OLDOBJ;
+ return SWIG_OK;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+}
+
+%fragment("SWIG_FromCharPtrAndSize","header",fragment="SWIG_pchar_descriptor") {
+SWIGINTERNINLINE PyObject *
+SWIG_FromCharPtrAndSize(const char* carray, size_t size)
+{
+ if (carray) {
+ if (size > INT_MAX) {
+ swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
+ return pchar_descriptor ?
+ SWIG_InternalNewPointerObj(%const_cast(carray,char *), pchar_descriptor, 0) : SWIG_Py_Void();
+ } else {
+%#if PY_VERSION_HEX >= 0x03000000
+%#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+ return PyBytes_FromStringAndSize(carray, %numeric_cast(size, Py_ssize_t));
+%#else
+ return PyUnicode_DecodeUTF8(carray, %numeric_cast(size, Py_ssize_t), "surrogateescape");
+%#endif
+%#else
+ return PyString_FromStringAndSize(carray, %numeric_cast(size, Py_ssize_t));
+%#endif
+ }
+ } else {
+ return SWIG_Py_Void();
+ }
+}
+}
+
diff --git a/contrib/tools/swig/Lib/python/python.swg b/contrib/tools/swig/Lib/python/python.swg
new file mode 100644
index 0000000000..769d9e104a
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/python.swg
@@ -0,0 +1,59 @@
+/* ------------------------------------------------------------
+ * python.swg
+ *
+ * Python configuration module.
+ * ------------------------------------------------------------ */
+
+/* ------------------------------------------------------------
+ * Inner macros
+ * ------------------------------------------------------------ */
+%include <pymacros.swg>
+
+
+/* ------------------------------------------------------------
+ * The runtime part
+ * ------------------------------------------------------------ */
+%include <pyruntime.swg>
+
+/* ------------------------------------------------------------
+ * Special user directives
+ * ------------------------------------------------------------ */
+%include <pyuserdir.swg>
+
+/* ------------------------------------------------------------
+ * Typemap specializations
+ * ------------------------------------------------------------ */
+%include <pytypemaps.swg>
+
+/* ------------------------------------------------------------
+ * Overloaded operator support
+ * ------------------------------------------------------------ */
+%include <pyopers.swg>
+
+/* ------------------------------------------------------------
+ * Warnings for Python keywords
+ * ------------------------------------------------------------ */
+%include <pythonkw.swg>
+
+/* ------------------------------------------------------------
+ * The Python autodoc support
+ * ------------------------------------------------------------ */
+%include <pydocs.swg>
+
+/* ------------------------------------------------------------
+ * The Python classes, for C++
+ * ------------------------------------------------------------ */
+%include <pyclasses.swg>
+
+/* ------------------------------------------------------------
+ * The Python initialization function
+ * ------------------------------------------------------------ */
+%include <pyinit.swg>
+
+
+/* ------------------------------------------------------------
+ * For backward compatibility
+ * ------------------------------------------------------------ */
+%include <pybackward.swg>
+
+
diff --git a/contrib/tools/swig/Lib/python/pythonkw.swg b/contrib/tools/swig/Lib/python/pythonkw.swg
new file mode 100644
index 0000000000..a21034524f
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pythonkw.swg
@@ -0,0 +1,140 @@
+/*
+ Warnings for Python keywords, built-in names and bad names.
+*/
+
+#define PYTHONKW(x) %keywordwarn("'" `x` "' is a python keyword", rename="_%s") `x`
+#define PYTHONBN(x) %builtinwarn("'" `x` "' conflicts with a built-in name in python") `x`
+
+
+/*
+ Warnings for Python keywords
+ https://docs.python.org/2/reference/lexical_analysis.html#keywords
+*/
+
+PYTHONKW(and);
+PYTHONKW(as);
+PYTHONKW(assert);
+PYTHONKW(async);
+PYTHONKW(await);
+PYTHONKW(break);
+PYTHONKW(class);
+PYTHONKW(continue);
+PYTHONKW(def);
+PYTHONKW(del);
+PYTHONKW(elif);
+PYTHONKW(else);
+PYTHONKW(except);
+PYTHONKW(exec);
+PYTHONKW(finally);
+PYTHONKW(for);
+PYTHONKW(from);
+PYTHONKW(global);
+PYTHONKW(if);
+PYTHONKW(import);
+PYTHONKW(in);
+PYTHONKW(is);
+PYTHONKW(lambda);
+PYTHONKW(not);
+PYTHONKW(or);
+PYTHONKW(pass);
+PYTHONKW(print);
+PYTHONKW(raise);
+PYTHONKW(return);
+PYTHONKW(try);
+PYTHONKW(while);
+PYTHONKW(with);
+PYTHONKW(yield);
+
+/*
+ built-in functions
+ https://docs.python.org/2/library/functions.html
+ */
+
+PYTHONBN(abs);
+PYTHONBN(apply);
+PYTHONBN(bool);
+PYTHONBN(buffer);
+PYTHONBN(callable);
+PYTHONBN(chr);
+PYTHONBN(classmethod);
+PYTHONBN(cmp);
+PYTHONBN(coerce);
+PYTHONBN(compile);
+PYTHONBN(complex);
+PYTHONBN(delattr);
+PYTHONBN(dict);
+PYTHONBN(dir);
+PYTHONBN(divmod);
+PYTHONBN(enumerate);
+PYTHONBN(eval);
+PYTHONBN(execfile);
+PYTHONBN(file);
+PYTHONBN(filter);
+PYTHONBN(float);
+PYTHONBN(frozenset);
+PYTHONBN(getattr);
+PYTHONBN(globals);
+PYTHONBN(hasattr);
+PYTHONBN(hash);
+PYTHONBN(hex);
+PYTHONBN(id);
+PYTHONBN(input);
+PYTHONBN(int);
+PYTHONBN(intern);
+PYTHONBN(isinstance);
+PYTHONBN(issubclass);
+PYTHONBN(iter);
+PYTHONBN(len);
+PYTHONBN(list);
+PYTHONBN(locals);
+PYTHONBN(long);
+PYTHONBN(map);
+PYTHONBN(max);
+PYTHONBN(min);
+PYTHONBN(object);
+PYTHONBN(oct);
+PYTHONBN(open);
+PYTHONBN(ord);
+PYTHONBN(pow);
+PYTHONBN(property);
+PYTHONBN(range);
+PYTHONBN(raw_input);
+PYTHONBN(reduce);
+PYTHONBN(reload);
+PYTHONBN(repr);
+PYTHONBN(reversed);
+PYTHONBN(round);
+PYTHONBN(set);
+PYTHONBN(setattr);
+PYTHONBN(slice);
+PYTHONBN(sorted);
+PYTHONBN(staticmethod);
+PYTHONBN(str);
+PYTHONBN(sum);
+PYTHONBN(super);
+PYTHONBN(tuple);
+PYTHONBN(type);
+PYTHONBN(unichr);
+PYTHONBN(unicode);
+PYTHONBN(vars);
+PYTHONBN(xrange);
+PYTHONBN(zip);
+
+
+/*
+ built-in names
+ boolean type and None
+*/
+PYTHONBN(True);
+PYTHONBN(False);
+
+PYTHONKW(None);
+
+
+/*
+ 'self' is also a bad Name
+*/
+PYTHONKW(self);
+
+#undef PYTHONBN
+#undef PYTHONKW
diff --git a/contrib/tools/swig/Lib/python/pythreads.swg b/contrib/tools/swig/Lib/python/pythreads.swg
new file mode 100644
index 0000000000..8d6c5ab49e
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pythreads.swg
@@ -0,0 +1,68 @@
+#if defined(SWIG_PYTHON_NO_THREADS)
+# if defined(SWIG_PYTHON_THREADS)
+# undef SWIG_PYTHON_THREADS
+# endif
+#endif
+#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */
+# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL)
+# define SWIG_PYTHON_USE_GIL
+# endif
+# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */
+# if !defined(SWIG_PYTHON_INITIALIZE_THREADS)
+# if PY_VERSION_HEX < 0x03070000
+# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads()
+# else
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# endif
+# endif
+# ifdef __cplusplus /* C++ code */
+ class SWIG_Python_Thread_Block {
+ bool status;
+ PyGILState_STATE state;
+ public:
+ void end() { if (status) { PyGILState_Release(state); status = false;} }
+ SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {}
+ ~SWIG_Python_Thread_Block() { end(); }
+ };
+ class SWIG_Python_Thread_Allow {
+ bool status;
+ PyThreadState *save;
+ public:
+ void end() { if (status) { PyEval_RestoreThread(save); status = false; }}
+ SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {}
+ ~SWIG_Python_Thread_Allow() { end(); }
+ };
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block
+# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end()
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow
+# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end()
+# else /* C code */
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure()
+# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block)
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread()
+# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow)
+# endif
+# else /* Old thread way, not implemented, user must provide it */
+# if !defined(SWIG_PYTHON_INITIALIZE_THREADS)
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# endif
+# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK)
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+# endif
+# if !defined(SWIG_PYTHON_THREAD_END_BLOCK)
+# define SWIG_PYTHON_THREAD_END_BLOCK
+# endif
+# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW)
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+# endif
+# if !defined(SWIG_PYTHON_THREAD_END_ALLOW)
+# define SWIG_PYTHON_THREAD_END_ALLOW
+# endif
+# endif
+#else /* No thread support */
+# define SWIG_PYTHON_INITIALIZE_THREADS
+# define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+# define SWIG_PYTHON_THREAD_END_BLOCK
+# define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+# define SWIG_PYTHON_THREAD_END_ALLOW
+#endif
diff --git a/contrib/tools/swig/Lib/python/pytypemaps.swg b/contrib/tools/swig/Lib/python/pytypemaps.swg
new file mode 100644
index 0000000000..0ae25a686e
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pytypemaps.swg
@@ -0,0 +1,105 @@
+/* ------------------------------------------------------------
+ * Typemap specializations for Python
+ * ------------------------------------------------------------ */
+
+/* ------------------------------------------------------------
+ * Fragment section
+ * ------------------------------------------------------------ */
+#ifdef SWIG_PYTHON_LEGACY_BOOL
+// Default prior to SWIG 3.0.0
+#undef SWIG_TYPECHECK_BOOL
+%define SWIG_TYPECHECK_BOOL 10000 %enddef
+#endif
+
+/* Include fundamental fragment definitions */
+%include <typemaps/fragments.swg>
+
+/* Look for user fragments file. */
+%include <pyfragments.swg>
+
+/* Python fragments for fundamental types */
+%include <pyprimtypes.swg>
+
+/* Python fragments for char* strings */
+%include <pystrings.swg>
+
+/* Backward compatibility output helper */
+%fragment("t_output_helper","header") %{
+#define t_output_helper SWIG_Python_AppendOutput
+%}
+
+
+/* ------------------------------------------------------------
+ * Unified typemap section
+ * ------------------------------------------------------------ */
+
+/* directors are supported in Python */
+#ifndef SWIG_DIRECTOR_TYPEMAPS
+#define SWIG_DIRECTOR_TYPEMAPS
+#endif
+
+
+/* Python types */
+#define SWIG_Object PyObject *
+#define VOID_Object SWIG_Py_Void()
+
+/* Python allows implicit conversion */
+#define %implicitconv_flag $implicitconv
+
+
+/* Overload of the output/constant/exception/dirout handling */
+
+/* append output */
+#define SWIG_AppendOutput(result, obj) SWIG_Python_AppendOutput(result, obj)
+
+/* set constant */
+#if defined(SWIGPYTHON_BUILTIN)
+#define SWIG_SetConstant(name, obj) SWIG_Python_SetConstant(d, d == md ? public_interface : NULL, name,obj)
+#else
+#define SWIG_SetConstant(name, obj) SWIG_Python_SetConstant(d, name,obj)
+#endif
+
+/* raise */
+#define SWIG_Raise(obj, type, desc) SWIG_Python_Raise(obj, type, desc)
+
+/* Include the unified typemap library */
+%include <typemaps/swigtypemaps.swg>
+
+
+/* ------------------------------------------------------------
+ * Python extra typemaps / typemap overrides
+ * ------------------------------------------------------------ */
+
+/* Get the address of the 'python self' object */
+
+%typemap(in,numinputs=0,noblock=1) PyObject **PYTHON_SELF {
+ $1 = &$self;
+}
+
+
+/* Consttab, needed for callbacks, it should be removed later */
+
+%typemap(consttab) SWIGTYPE ((*)(ANY))
+{ SWIG_PY_POINTER, "$symname", 0, 0, (void *)($value), &$descriptor }
+%typemap(consttab) SWIGTYPE ((* const)(ANY)) = SWIGTYPE ((*)(ANY));
+
+%typemap(constcode) SWIGTYPE ((*)(ANY)) ""
+%typemap(constcode) SWIGTYPE ((* const)(ANY)) = SWIGTYPE ((*)(ANY));
+
+
+/* Smart Pointers */
+%typemap(out,noblock=1) const SWIGTYPE & SMARTPOINTER {
+ $result = SWIG_NewPointerObj(%new_copy(*$1, $*ltype), $descriptor, SWIG_POINTER_OWN | %newpointer_flags);
+}
+
+%typemap(ret,noblock=1) const SWIGTYPE & SMARTPOINTER, SWIGTYPE SMARTPOINTER {
+ if ($result) {
+ PyObject *robj = PyObject_CallMethod($result, (char *)"__deref__", NULL);
+ if (robj && !PyErr_Occurred()) {
+ SwigPyObject_append((PyObject *) SWIG_Python_GetSwigThis($result),
+ (PyObject *) SWIG_Python_GetSwigThis(robj));
+ Py_DECREF(robj);
+ }
+ }
+}
+
diff --git a/contrib/tools/swig/Lib/python/pyuserdir.swg b/contrib/tools/swig/Lib/python/pyuserdir.swg
new file mode 100644
index 0000000000..3110760793
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pyuserdir.swg
@@ -0,0 +1,242 @@
+/* -------------------------------------------------------------------------
+ * Special user directives
+ * ------------------------------------------------------------------------- */
+
+/* ------------------------------------------------------------------------- */
+
+/* shadow code */
+#define %shadow %insert("shadow")
+#define %pythoncode %insert("python")
+#define %pythonbegin %insert("pythonbegin")
+
+
+/* ------------------------------------------------------------------------- */
+/*
+Use the "nondynamic" feature to make a wrapped class behave as a "nondynamic"
+one, ie, a python class that doesn't dynamically add new attributes.
+
+For example, for the class
+
+%pythonnondynamic A;
+struct A
+{
+ int a;
+ int b;
+};
+
+you will get:
+
+ aa = A()
+ aa.a = 1 # Ok
+ aa.b = 1 # Ok
+ aa.c = 3 # error
+
+Since nondynamic is a feature, if you use it like
+
+ %pythonnondynamic;
+
+it will make all the wrapped classes nondynamic ones.
+
+The implementation is based on this recipe:
+
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252158
+
+*/
+
+#define %pythonnondynamic %feature("python:nondynamic", "1")
+#define %nopythonnondynamic %feature("python:nondynamic", "0")
+#define %clearpythonnondynamic %feature("python:nondynamic", "")
+#define %pythondynamic %nopythonnondynamic
+
+
+/* ------------------------------------------------------------------------- */
+/*
+
+Use %pythonmaybecall to flag a method like __add__ or __radd__. These
+don't produce an error when called, they just return NotImplemented.
+
+These methods "may be called" if needed.
+
+*/
+
+#define %pythonmaybecall %feature("python:maybecall", "1")
+#define %nopythonmaybecall %feature("python:maybecall", "0")
+#define %clearpythonmaybecall %feature("python:maybecall", "")
+
+/* ------------------------------------------------------------------------- */
+/*
+ The %pythoncallback feature produce a more natural callback wrapper
+ than the %callback mechanism, ie, it uses the original name for
+ the callback and callable objects.
+
+ Just use it as
+
+ %pythoncallback(1) foo;
+ int foo(int a);
+
+ %pythoncallback(1) A::foo;
+ struct A {
+ static int foo(int a);
+ };
+
+ int bar(int, int (*pf)(int));
+
+ then, you can use it as:
+
+ a = foo(1)
+ b = bar(2, foo)
+
+ c = A.foo(3)
+ d = bar(4, A.foo)
+
+
+ If you use it with a member method
+ %pythoncallback(1) A::foom;
+ struct A {
+ int foom(int a);
+ };
+
+ then you can use it as
+
+ r = a.foom(3) # eval the method
+ mptr = A.foom_cb_ptr # returns the callback pointer
+
+ where the '_cb_ptr' suffix is added for the callback pointer.
+
+*/
+
+#define %pythoncallback %feature("python:callback")
+#define %nopythoncallback %feature("python:callback","0")
+#define %clearpythoncallback %feature("python:callback","")
+
+/* ------------------------------------------------------------------------- */
+/*
+ Support for the old %callback directive name
+*/
+#ifdef %callback
+#undef %callback
+#endif
+
+#ifdef %nocallback
+#undef %nocallback
+#endif
+
+#ifdef %clearcallback
+#undef %clearcallback
+#endif
+
+#define %callback(x) %feature("python:callback",`x`)
+#define %nocallback %nopythoncallback
+#define %clearcallback %clearpythoncallback
+
+/* ------------------------------------------------------------------------- */
+/*
+ Thread support - Advance control
+
+*/
+
+#define %nothread %feature("nothread")
+#define %thread %feature("nothread","0")
+#define %clearnothread %feature("nothread","")
+
+#define %nothreadblock %feature("nothreadblock")
+#define %threadblock %feature("nothreadblock","0")
+#define %clearnothreadblock %feature("nothreadblock","")
+
+#define %nothreadallow %feature("nothreadallow")
+#define %threadallow %feature("nothreadallow","0")
+#define %clearnothreadallow %feature("nothreadallow","")
+
+
+/* ------------------------------------------------------------------------- */
+/*
+ Implicit Conversion using the C++ constructor mechanism
+*/
+
+#define %implicitconv %feature("implicitconv")
+#define %noimplicitconv %feature("implicitconv", "0")
+#define %clearimplicitconv %feature("implicitconv", "")
+
+
+/* ------------------------------------------------------------------------- */
+/*
+ Enable keywords parameters
+*/
+
+#define %kwargs %feature("kwargs")
+#define %nokwargs %feature("kwargs", "0")
+#define %clearkwargs %feature("kwargs", "")
+
+/* ------------------------------------------------------------------------- */
+/*
+ Add python code to the proxy/shadow code
+
+ %pythonprepend - Add code before the C++ function is called
+ %pythonappend - Add code after the C++ function is called
+*/
+
+#define %pythonprepend %feature("pythonprepend")
+#define %clearpythonprepend %feature("pythonprepend","")
+
+#define %pythonappend %feature("pythonappend")
+#define %clearpythonappend %feature("pythonappend","")
+
+
+/* ------------------------------------------------------------------------- */
+/*
+ %extend_smart_pointer extend the smart pointer support.
+
+ For example, if you have a smart pointer as:
+
+ template <class Type> class RCPtr {
+ public:
+ ...
+ RCPtr(Type *p);
+ Type * operator->() const;
+ ...
+ };
+
+ you use the %extend_smart_pointer directive as:
+
+ %extend_smart_pointer(RCPtr<A>);
+ %template(RCPtr_A) RCPtr<A>;
+
+ then, if you have something like:
+
+ RCPtr<A> make_ptr();
+ int foo(A *);
+
+ you can do the following:
+
+ a = make_ptr();
+ b = foo(a);
+
+ ie, swig will accept a RCPtr<A> object where a 'A *' is
+ expected.
+
+ Also, when using vectors
+
+ %extend_smart_pointer(RCPtr<A>);
+ %template(RCPtr_A) RCPtr<A>;
+ %template(vector_A) std::vector<RCPtr<A> >;
+
+ you can type
+
+ a = A();
+ v = vector_A(2)
+ v[0] = a
+
+ ie, an 'A *' object is accepted, via implicit conversion,
+ where a RCPtr<A> object is expected. Additionally
+
+ x = v[0]
+
+ returns (and sets 'x' as) a copy of v[0], making reference
+ counting possible and consistent.
+*/
+
+%define %extend_smart_pointer(Type...)
+%implicitconv Type;
+%apply const SWIGTYPE& SMARTPOINTER { const Type& };
+%apply SWIGTYPE SMARTPOINTER { Type };
+%enddef
diff --git a/contrib/tools/swig/Lib/python/pywstrings.swg b/contrib/tools/swig/Lib/python/pywstrings.swg
new file mode 100644
index 0000000000..0e5a78df5e
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/pywstrings.swg
@@ -0,0 +1,85 @@
+/* ------------------------------------------------------------
+ * utility methods for wchar_t strings
+ * ------------------------------------------------------------ */
+
+%{
+#if PY_VERSION_HEX >= 0x03020000
+# define SWIGPY_UNICODE_ARG(obj) ((PyObject*) (obj))
+#else
+# define SWIGPY_UNICODE_ARG(obj) ((PyUnicodeObject*) (obj))
+#endif
+%}
+
+%fragment("SWIG_AsWCharPtrAndSize","header",fragment="<wchar.h>",fragment="SWIG_pwchar_descriptor") {
+SWIGINTERN int
+SWIG_AsWCharPtrAndSize(PyObject *obj, wchar_t **cptr, size_t *psize, int *alloc)
+{
+ PyObject *tmp = 0;
+ int isunicode = PyUnicode_Check(obj);
+%#if PY_VERSION_HEX < 0x03000000 && !defined(SWIG_PYTHON_STRICT_UNICODE_WCHAR)
+ if (!isunicode && PyString_Check(obj)) {
+ tmp = PyUnicode_FromObject(obj);
+ if (tmp) {
+ isunicode = 1;
+ obj = tmp;
+ } else {
+ PyErr_Clear();
+ return SWIG_TypeError;
+ }
+ }
+%#endif
+ if (isunicode) {
+%#if PY_VERSION_HEX >= 0x03030000
+ Py_ssize_t len = PyUnicode_GetLength(obj);
+%#else
+ Py_ssize_t len = PyUnicode_GetSize(obj);
+%#endif
+ if (cptr) {
+ Py_ssize_t length;
+ *cptr = %new_array(len + 1, wchar_t);
+ length = PyUnicode_AsWideChar(SWIGPY_UNICODE_ARG(obj), *cptr, len);
+ if (length == -1) {
+ PyErr_Clear();
+ Py_XDECREF(tmp);
+ return SWIG_TypeError;
+ }
+ (*cptr)[length] = 0;
+ }
+ if (psize) *psize = (size_t) len + 1;
+ if (alloc) *alloc = cptr ? SWIG_NEWOBJ : 0;
+ Py_XDECREF(tmp);
+ return SWIG_OK;
+ } else {
+ swig_type_info* pwchar_descriptor = SWIG_pwchar_descriptor();
+ if (pwchar_descriptor) {
+ void * vptr = 0;
+ if (SWIG_ConvertPtr(obj, &vptr, pwchar_descriptor, 0) == SWIG_OK) {
+ if (cptr) *cptr = (wchar_t *)vptr;
+ if (psize) *psize = vptr ? (wcslen((wchar_t *)vptr) + 1) : 0;
+ return SWIG_OK;
+ }
+ }
+ }
+ return SWIG_TypeError;
+}
+}
+
+%fragment("SWIG_FromWCharPtrAndSize","header",fragment="<wchar.h>",fragment="SWIG_pwchar_descriptor") {
+SWIGINTERNINLINE PyObject *
+SWIG_FromWCharPtrAndSize(const wchar_t * carray, size_t size)
+{
+ if (carray) {
+ if (size > INT_MAX) {
+ swig_type_info* pwchar_descriptor = SWIG_pwchar_descriptor();
+ return pwchar_descriptor ?
+ SWIG_InternalNewPointerObj(%const_cast(carray,wchar_t *), pwchar_descriptor, 0) : SWIG_Py_Void();
+ } else {
+ return PyUnicode_FromWideChar(carray, %numeric_cast(size, Py_ssize_t));
+ }
+ } else {
+ return SWIG_Py_Void();
+ }
+}
+}
+
+
diff --git a/contrib/tools/swig/Lib/python/std_alloc.i b/contrib/tools/swig/Lib/python/std_alloc.i
new file mode 100644
index 0000000000..35dc051bea
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/std_alloc.i
@@ -0,0 +1 @@
+%include <std/std_alloc.i>
diff --git a/contrib/tools/swig/Lib/python/std_char_traits.i b/contrib/tools/swig/Lib/python/std_char_traits.i
new file mode 100644
index 0000000000..bf4e6c47dd
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/std_char_traits.i
@@ -0,0 +1 @@
+%include <std/std_char_traits.i>
diff --git a/contrib/tools/swig/Lib/python/std_common.i b/contrib/tools/swig/Lib/python/std_common.i
new file mode 100644
index 0000000000..605766238b
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/std_common.i
@@ -0,0 +1,74 @@
+%include <std/std_except.i>
+%include <pystdcommon.swg>
+
+
+/*
+ Generate the traits for a 'primitive' type, such as 'double',
+ for which the SWIG_AsVal and SWIG_From methods are already defined.
+*/
+
+%define %traits_ptypen(Type...)
+ %fragment(SWIG_Traits_frag(Type),"header",
+ fragment=SWIG_AsVal_frag(Type),
+ fragment=SWIG_From_frag(Type),
+ fragment="StdTraits") {
+namespace swig {
+ template <> struct traits< Type > {
+ typedef value_category category;
+ static const char* type_name() { return #Type; }
+ };
+ template <> struct traits_asval< Type > {
+ typedef Type value_type;
+ static int asval(PyObject *obj, value_type *val) {
+ return SWIG_AsVal(Type)(obj, val);
+ }
+ };
+ template <> struct traits_from< Type > {
+ typedef Type value_type;
+ static PyObject *from(const value_type& val) {
+ return SWIG_From(Type)(val);
+ }
+ };
+}
+}
+%enddef
+
+/* Traits for enums. This is bit of a sneaky trick needed because a generic template specialization of enums
+ is not possible (unless using template meta-programming which SWIG doesn't support because of the explicit
+ instantiations required using %template). The STL containers define the 'front' method and the typemap
+ below is used whenever the front method is wrapped returning an enum. This typemap simply picks up the
+ standard enum typemap, but additionally drags in a fragment containing the traits_asval and traits_from
+ required in the generated code for enums. */
+
+%define %traits_enum(Type...)
+ %fragment("SWIG_Traits_enum_"{Type},"header",
+ fragment=SWIG_AsVal_frag(int),
+ fragment=SWIG_From_frag(int),
+ fragment="StdTraits") {
+namespace swig {
+ template <> struct traits_asval< Type > {
+ typedef Type value_type;
+ static int asval(PyObject *obj, value_type *val) {
+ return SWIG_AsVal(int)(obj, (int *)val);
+ }
+ };
+ template <> struct traits_from< Type > {
+ typedef Type value_type;
+ static PyObject *from(const value_type& val) {
+ return SWIG_From(int)((int)val);
+ }
+ };
+}
+}
+%typemap(out, fragment="SWIG_Traits_enum_"{Type}) const enum SWIGTYPE& front %{$typemap(out, const enum SWIGTYPE&)%}
+%enddef
+
+
+%include <std/std_common.i>
+
+//
+// Generates the traits for all the known primitive
+// C++ types (int, double, ...)
+//
+%apply_cpptypes(%traits_ptypen);
+
diff --git a/contrib/tools/swig/Lib/python/std_container.i b/contrib/tools/swig/Lib/python/std_container.i
new file mode 100644
index 0000000000..d24c1570f1
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/std_container.i
@@ -0,0 +1,2 @@
+%include <pycontainer.swg>
+%include <std/std_container.i>
diff --git a/contrib/tools/swig/Lib/python/std_except.i b/contrib/tools/swig/Lib/python/std_except.i
new file mode 100644
index 0000000000..af98428f65
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/std_except.i
@@ -0,0 +1 @@
+%include <typemaps/std_except.swg>
diff --git a/contrib/tools/swig/Lib/python/std_string.i b/contrib/tools/swig/Lib/python/std_string.i
new file mode 100644
index 0000000000..dc1378ae6d
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/std_string.i
@@ -0,0 +1 @@
+%include <typemaps/std_string.swg>
diff --git a/contrib/tools/swig/Lib/python/std_vector.i b/contrib/tools/swig/Lib/python/std_vector.i
new file mode 100644
index 0000000000..2ac41a54d7
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/std_vector.i
@@ -0,0 +1,34 @@
+/*
+ Vectors
+*/
+
+%fragment("StdVectorTraits","header",fragment="StdSequenceTraits")
+%{
+ namespace swig {
+ template <class T>
+ struct traits_reserve<std::vector<T> > {
+ static void reserve(std::vector<T> &seq, typename std::vector<T>::size_type n) {
+ seq.reserve(n);
+ }
+ };
+
+ template <class T>
+ struct traits_asptr<std::vector<T> > {
+ static int asptr(PyObject *obj, std::vector<T> **vec) {
+ return traits_asptr_stdseq<std::vector<T> >::asptr(obj, vec);
+ }
+ };
+
+ template <class T>
+ struct traits_from<std::vector<T> > {
+ static PyObject *from(const std::vector<T>& vec) {
+ return traits_from_stdseq<std::vector<T> >::from(vec);
+ }
+ };
+ }
+%}
+
+#define %swig_vector_methods(Type...) %swig_sequence_methods(Type)
+#define %swig_vector_methods_val(Type...) %swig_sequence_methods_val(Type);
+
+%include <std/std_vector.i>
diff --git a/contrib/tools/swig/Lib/python/typemaps.i b/contrib/tools/swig/Lib/python/typemaps.i
new file mode 100644
index 0000000000..dba63dd59e
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/typemaps.i
@@ -0,0 +1,148 @@
+/* -----------------------------------------------------------------------------
+ * typemaps.i
+ *
+ * Pointer handling
+ * These mappings provide support for input/output arguments and common
+ * uses for C/C++ pointers.
+ * ----------------------------------------------------------------------------- */
+
+// INPUT typemaps.
+// These remap a C pointer to be an "INPUT" value which is passed by value
+// instead of reference.
+
+/*
+The following methods can be applied to turn a pointer into a simple
+"input" value. That is, instead of passing a pointer to an object,
+you would use a real value instead.
+
+ int *INPUT
+ short *INPUT
+ long *INPUT
+ long long *INPUT
+ unsigned int *INPUT
+ unsigned short *INPUT
+ unsigned long *INPUT
+ unsigned long long *INPUT
+ unsigned char *INPUT
+ bool *INPUT
+ float *INPUT
+ double *INPUT
+
+To use these, suppose you had a C function like this :
+
+ double fadd(double *a, double *b) {
+ return *a+*b;
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ double fadd(double *INPUT, double *INPUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *INPUT { double *a, double *b };
+ double fadd(double *a, double *b);
+
+*/
+
+// OUTPUT typemaps. These typemaps are used for parameters that
+// are output only. The output value is appended to the result as
+// a list element.
+
+/*
+The following methods can be applied to turn a pointer into an "output"
+value. When calling a function, no input value would be given for
+a parameter, but an output value would be returned. In the case of
+multiple output values, they are returned in the form of a Python tuple.
+
+ int *OUTPUT
+ short *OUTPUT
+ long *OUTPUT
+ long long *OUTPUT
+ unsigned int *OUTPUT
+ unsigned short *OUTPUT
+ unsigned long *OUTPUT
+ unsigned long long *OUTPUT
+ unsigned char *OUTPUT
+ bool *OUTPUT
+ float *OUTPUT
+ double *OUTPUT
+
+For example, suppose you were trying to wrap the modf() function in the
+C math library which splits x into integral and fractional parts (and
+returns the integer part in one of its parameters) :
+
+ double modf(double x, double *ip);
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ double modf(double x, double *OUTPUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *OUTPUT { double *ip };
+ double modf(double x, double *ip);
+
+The Python output of the function would be a tuple containing both
+output values.
+
+*/
+
+// INOUT
+// Mappings for an argument that is both an input and output
+// parameter
+
+/*
+The following methods can be applied to make a function parameter both
+an input and output value. This combines the behavior of both the
+"INPUT" and "OUTPUT" methods described earlier. Output values are
+returned in the form of a Python tuple.
+
+ int *INOUT
+ short *INOUT
+ long *INOUT
+ long long *INOUT
+ unsigned int *INOUT
+ unsigned short *INOUT
+ unsigned long *INOUT
+ unsigned long long *INOUT
+ unsigned char *INOUT
+ bool *INOUT
+ float *INOUT
+ double *INOUT
+
+For example, suppose you were trying to wrap the following function :
+
+ void neg(double *x) {
+ *x = -(*x);
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ void neg(double *INOUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *INOUT { double *x };
+ void neg(double *x);
+
+Unlike C, this mapping does not directly modify the input value (since
+this makes no sense in Python). Rather, the modified input value shows
+up as the return value of the function. Thus, to apply this function
+to a Python variable you might do this :
+
+ x = neg(x)
+
+Note : previous versions of SWIG used the symbol 'BOTH' to mark
+input/output arguments. This is still supported, but will be slowly
+phased out in future releases.
+
+*/
+
+%include <typemaps/typemaps.swg>
diff --git a/contrib/tools/swig/Lib/python/wchar.i b/contrib/tools/swig/Lib/python/wchar.i
new file mode 100644
index 0000000000..308139a3ab
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/wchar.i
@@ -0,0 +1,21 @@
+#ifdef __cplusplus
+
+%{
+#include <cwchar>
+%}
+
+#else
+
+%{
+#include <wchar.h>
+%}
+
+#endif
+
+%types(wchar_t *);
+%include <pywstrings.swg>
+
+/*
+ Enable swig wchar support.
+*/
+#define SWIG_WCHAR
diff --git a/contrib/tools/swig/Lib/python/ya.make b/contrib/tools/swig/Lib/python/ya.make
new file mode 100644
index 0000000000..e843bff498
--- /dev/null
+++ b/contrib/tools/swig/Lib/python/ya.make
@@ -0,0 +1,22 @@
+# Generated by devtools/yamaker.
+
+LIBRARY()
+
+LICENSE(LicenseRef-scancode-swig)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(4.1.1)
+
+ORIGINAL_SOURCE(https://github.com/swig/swig/archive/v4.1.1.tar.gz)
+
+ADDINCL(
+ GLOBAL FOR
+ swig
+ contrib/tools/swig/Lib/python
+ GLOBAL FOR
+ swig
+ contrib/tools/swig/Lib
+)
+
+END()
diff --git a/contrib/tools/swig/Lib/std/README b/contrib/tools/swig/Lib/std/README
new file mode 100644
index 0000000000..5cd759dda1
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/README
@@ -0,0 +1,22 @@
+/* -----------------------------------------------------------------------------
+ * C++ STD + STL
+ * ----------------------------------------------------------------------------- */
+
+std_common.i general common code
+std_container.i general container code
+std_basic_string.i basic string
+std_char_traits.i char traits
+std_complex.i complex
+std_deque.i deque
+std_except.i exceptions
+std_ios.i ios
+std_iostream.i istream/ostream
+std_list.i list
+std_map.i map
+std_multimap.i multimap
+std_multiset.i multiset
+std_pair.i pair
+std_set.i set
+std_streambuf.i streambuf
+std_vector.i vector
+std_vectora.i vector + allocator
diff --git a/contrib/tools/swig/Lib/std/std_alloc.i b/contrib/tools/swig/Lib/std/std_alloc.i
new file mode 100644
index 0000000000..e460dc3eaf
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/std_alloc.i
@@ -0,0 +1,77 @@
+namespace std
+{
+ /**
+ * @brief The "standard" allocator, as per [20.4].
+ *
+ * The private _Alloc is "SGI" style. (See comments at the top
+ * of stl_alloc.h.)
+ *
+ * The underlying allocator behaves as follows.
+ * - __default_alloc_template is used via two typedefs
+ * - "__single_client_alloc" typedef does no locking for threads
+ * - "__alloc" typedef is threadsafe via the locks
+ * - __new_alloc is used for memory requests
+ *
+ * (See @link Allocators allocators info @endlink for more.)
+ */
+ template<typename _Tp>
+ class allocator
+ {
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef _Tp* pointer;
+ typedef const _Tp* const_pointer;
+ typedef _Tp& reference;
+ typedef const _Tp& const_reference;
+ typedef _Tp value_type;
+
+ template<typename _Tp1>
+ struct rebind;
+
+ allocator() throw();
+
+ allocator(const allocator& other) throw();
+ template<typename _Tp1>
+ allocator(const allocator<_Tp1>& other) throw();
+ ~allocator() throw();
+
+
+ pointer
+ address(reference __x) const;
+
+
+ const_pointer
+ address(const_reference __x) const;
+
+
+ // NB: __n is permitted to be 0. The C++ standard says nothing
+ // about what the return value is when __n == 0.
+ _Tp*
+ allocate(size_type __n, const void* = 0);
+
+ // __p is not permitted to be a null pointer.
+ void
+ deallocate(pointer __p, size_type __n);
+
+ size_type
+ max_size() const throw();
+
+ void construct(pointer __p, const _Tp& __val);
+ void destroy(pointer __p);
+ };
+
+ template<>
+ class allocator<void>
+ {
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef void* pointer;
+ typedef const void* const_pointer;
+ typedef void value_type;
+
+ template<typename _Tp1>
+ struct rebind;
+ };
+} // namespace std
diff --git a/contrib/tools/swig/Lib/std/std_basic_string.i b/contrib/tools/swig/Lib/std/std_basic_string.i
new file mode 100644
index 0000000000..e95cb47653
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/std_basic_string.i
@@ -0,0 +1,276 @@
+%include <exception.i>
+%include <std_container.i>
+%include <std_alloc.i>
+%include <std_char_traits.i>
+
+%fragment("<string>");
+
+namespace std
+{
+ %naturalvar basic_string;
+}
+
+
+namespace std {
+
+ template <class _CharT, class _Traits = char_traits<_CharT>, typename _Alloc = allocator<_CharT> >
+ class basic_string
+ {
+#if !defined(SWIG_STD_MODERN_STL) || defined(SWIG_STD_NOMODERN_STL)
+ %ignore push_back;
+ %ignore clear;
+ %ignore compare;
+ %ignore append;
+#endif
+
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef _CharT value_type;
+ typedef value_type reference;
+ typedef value_type const_reference;
+ typedef _Alloc allocator_type;
+
+ static const size_type npos;
+
+#ifdef SWIG_EXPORT_ITERATOR_METHODS
+ class iterator;
+ class reverse_iterator;
+ class const_iterator;
+ class const_reverse_iterator;
+#endif
+
+
+ %traits_swigtype(_CharT);
+ %fragment(SWIG_Traits_frag(_CharT));
+
+
+ basic_string(const _CharT* __s, size_type __n);
+
+ // Capacity:
+
+ size_type length() const;
+
+ size_type max_size() const;
+
+ size_type capacity() const;
+
+ void reserve(size_type __res_arg);
+ %extend {
+ void shrink_to_fit() {
+ %#if __cplusplus >= 202002L
+ self->shrink_to_fit();
+ %#else
+ self->reserve();
+ %#endif
+ }
+ }
+
+
+ // Modifiers:
+
+ basic_string&
+ append(const basic_string& __str);
+
+ basic_string&
+ append(const basic_string& __str, size_type __pos, size_type __n);
+
+ basic_string&
+ append(const _CharT* __s, size_type __n);
+
+ basic_string&
+ append(size_type __n, _CharT __c);
+
+ basic_string&
+ assign(const basic_string& __str);
+
+ basic_string&
+ assign(const basic_string& __str, size_type __pos, size_type __n);
+
+ basic_string&
+ assign(const _CharT* __s, size_type __n);
+
+ basic_string&
+ insert(size_type __pos1, const basic_string& __str);
+
+ basic_string&
+ insert(size_type __pos1, const basic_string& __str,
+ size_type __pos2, size_type __n);
+
+ basic_string&
+ insert(size_type __pos, const _CharT* __s, size_type __n);
+
+ basic_string&
+ insert(size_type __pos, size_type __n, _CharT __c);
+
+ basic_string&
+ erase(size_type __pos = 0, size_type __n = npos);
+
+ basic_string&
+ replace(size_type __pos, size_type __n, const basic_string& __str);
+
+ basic_string&
+ replace(size_type __pos1, size_type __n1, const basic_string& __str,
+ size_type __pos2, size_type __n2);
+
+ basic_string&
+ replace(size_type __pos, size_type __n1, const _CharT* __s,
+ size_type __n2);
+
+ basic_string&
+ replace(size_type __pos, size_type __n1, size_type __n2, _CharT __c);
+
+
+ size_type
+ copy(_CharT* __s, size_type __n, size_type __pos = 0) const;
+
+ // String operations:
+ const _CharT* c_str() const;
+
+ size_type
+ find(const _CharT* __s, size_type __pos, size_type __n) const;
+
+ size_type
+ find(const basic_string& __str, size_type __pos = 0) const;
+
+ size_type
+ find(_CharT __c, size_type __pos = 0) const;
+
+ size_type
+ rfind(const basic_string& __str, size_type __pos = npos) const;
+
+ size_type
+ rfind(const _CharT* __s, size_type __pos, size_type __n) const;
+
+ size_type
+ rfind(_CharT __c, size_type __pos = npos) const;
+
+ size_type
+ find_first_of(const basic_string& __str, size_type __pos = 0) const;
+
+ size_type
+ find_first_of(const _CharT* __s, size_type __pos, size_type __n) const;
+
+ size_type
+ find_first_of(_CharT __c, size_type __pos = 0) const;
+
+ size_type
+ find_last_of(const basic_string& __str, size_type __pos = npos) const;
+
+ size_type
+ find_last_of(const _CharT* __s, size_type __pos, size_type __n) const;
+
+ size_type
+ find_last_of(_CharT __c, size_type __pos = npos) const;
+
+ size_type
+ find_first_not_of(const basic_string& __str, size_type __pos = 0) const;
+
+ size_type
+ find_first_not_of(const _CharT* __s, size_type __pos,
+ size_type __n) const;
+
+ size_type
+ find_first_not_of(_CharT __c, size_type __pos = 0) const;
+
+ size_type
+ find_last_not_of(const basic_string& __str, size_type __pos = npos) const;
+
+ size_type
+ find_last_not_of(const _CharT* __s, size_type __pos,
+ size_type __n) const;
+
+ size_type
+ find_last_not_of(_CharT __c, size_type __pos = npos) const;
+
+ basic_string
+ substr(size_type __pos = 0, size_type __n = npos) const;
+
+ int
+ compare(const basic_string& __str) const;
+
+ int
+ compare(size_type __pos, size_type __n, const basic_string& __str) const;
+
+ int
+ compare(size_type __pos1, size_type __n1, const basic_string& __str,
+ size_type __pos2, size_type __n2) const;
+
+
+ %ignore pop_back();
+ %ignore front() const;
+ %ignore back() const;
+ %ignore basic_string(size_type n);
+ %std_sequence_methods_val(basic_string);
+
+
+ %ignore pop();
+
+
+#ifdef %swig_basic_string
+ // Add swig/language extra methods
+ %swig_basic_string(std::basic_string< _CharT, _Traits, _Alloc >);
+#endif
+
+#ifdef SWIG_EXPORT_ITERATOR_METHODS
+
+
+ class iterator;
+ class reverse_iterator;
+ class const_iterator;
+ class const_reverse_iterator;
+
+
+ void
+ insert(iterator __p, size_type __n, _CharT __c);
+
+ basic_string&
+ replace(iterator __i1, iterator __i2, const basic_string& __str);
+
+ basic_string&
+ replace(iterator __i1, iterator __i2, const _CharT* __s, size_type __n);
+
+ basic_string&
+ replace(iterator __i1, iterator __i2, size_type __n, _CharT __c);
+
+
+ basic_string&
+ replace(iterator __i1, iterator __i2, const _CharT* __k1, const _CharT* __k2);
+
+ basic_string&
+ replace(iterator __i1, iterator __i2, const_iterator __k1, const_iterator __k2);
+#endif
+
+ basic_string& operator +=(const basic_string& v);
+
+ %newobject __add__;
+ %newobject __radd__;
+ %extend {
+
+ std::basic_string< _CharT,_Traits,_Alloc >* __add__(const basic_string& v) {
+ std::basic_string< _CharT,_Traits,_Alloc >* res = new std::basic_string< _CharT,_Traits,_Alloc >(*self);
+ *res += v;
+ return res;
+ }
+
+ std::basic_string< _CharT,_Traits,_Alloc >* __radd__(const basic_string& v) {
+ std::basic_string< _CharT,_Traits,_Alloc >* res = new std::basic_string< _CharT,_Traits,_Alloc >(v);
+ *res += *self;
+ return res;
+ }
+
+ std::basic_string< _CharT,_Traits,_Alloc > __str__() {
+ return *self;
+ }
+
+ std::basic_ostream<_CharT, std::char_traits<_CharT> >&
+ __rlshift__(std::basic_ostream<_CharT, std::char_traits<_CharT> >& out) {
+ out << *self;
+ return out;
+ }
+ }
+
+ };
+}
+
+
diff --git a/contrib/tools/swig/Lib/std/std_char_traits.i b/contrib/tools/swig/Lib/std/std_char_traits.i
new file mode 100644
index 0000000000..b9b4def32f
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/std_char_traits.i
@@ -0,0 +1,140 @@
+%include <std_common.i>
+#if defined(SWIG_WCHAR)
+%include <wchar.i>
+#endif
+
+namespace std
+{
+
+ /// 21.1.2 Basis for explicit _Traits specialization
+ /// NB: That for any given actual character type this definition is
+ /// probably wrong.
+ template<class _CharT>
+ struct char_traits
+ {
+ };
+
+
+ /// 21.1.4 char_traits specializations
+ template<>
+ struct char_traits<char> {
+ typedef char char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+ typedef mbstate_t state_type;
+
+ static void
+ assign(char_type& __c1, const char_type& __c2);
+
+ static bool
+ eq(const char_type& __c1, const char_type& __c2);
+
+ static bool
+ lt(const char_type& __c1, const char_type& __c2);
+
+ static int
+ compare(const char_type* __s1, const char_type* __s2, size_t __n);
+
+ static size_t
+ length(const char_type* __s);
+
+ static const char_type*
+ find(const char_type* __s, size_t __n, const char_type& __a);
+
+ static char_type*
+ move(char_type* __s1, const char_type* __s2, size_t __n);
+
+ static char_type*
+ copy(char_type* __s1, const char_type* __s2, size_t __n);
+
+ static char_type*
+ assign(char_type* __s, size_t __n, char_type __a);
+
+ static char_type
+ to_char_type(const int_type& __c);
+
+ // To keep both the byte 0xff and the eof symbol 0xffffffff
+ // from ending up as 0xffffffff.
+ static int_type
+ to_int_type(const char_type& __c);
+
+ static bool
+ eq_int_type(const int_type& __c1, const int_type& __c2);
+
+ static int_type
+ eof() ;
+
+ static int_type
+ not_eof(const int_type& __c);
+ };
+
+
+#if defined(SWIG_WCHAR)
+ template<>
+ struct char_traits<wchar_t>
+ {
+ typedef wchar_t char_type;
+ typedef wint_t int_type;
+ typedef streamoff off_type;
+ typedef wstreampos pos_type;
+ typedef mbstate_t state_type;
+
+ static void
+ assign(char_type& __c1, const char_type& __c2);
+
+ static bool
+ eq(const char_type& __c1, const char_type& __c2);
+
+ static bool
+ lt(const char_type& __c1, const char_type& __c2);
+
+ static int
+ compare(const char_type* __s1, const char_type* __s2, size_t __n);
+
+ static size_t
+ length(const char_type* __s);
+
+ static const char_type*
+ find(const char_type* __s, size_t __n, const char_type& __a);
+
+ static char_type*
+ move(char_type* __s1, const char_type* __s2, int_type __n);
+
+ static char_type*
+ copy(char_type* __s1, const char_type* __s2, size_t __n);
+
+ static char_type*
+ assign(char_type* __s, size_t __n, char_type __a);
+
+ static char_type
+ to_char_type(const int_type& __c) ;
+
+ static int_type
+ to_int_type(const char_type& __c) ;
+
+ static bool
+ eq_int_type(const int_type& __c1, const int_type& __c2);
+
+ static int_type
+ eof() ;
+
+ static int_type
+ not_eof(const int_type& __c);
+ };
+#endif
+}
+
+namespace std {
+#ifndef SWIG_STL_WRAP_TRAITS
+%template() char_traits<char>;
+#if defined(SWIG_WCHAR)
+%template() char_traits<wchar_t>;
+#endif
+#else
+%template(char_traits_c) char_traits<char>;
+#if defined(SWIG_WCHAR)
+%template(char_traits_w) char_traits<wchar_t>;
+#endif
+#endif
+}
diff --git a/contrib/tools/swig/Lib/std/std_common.i b/contrib/tools/swig/Lib/std/std_common.i
new file mode 100644
index 0000000000..708f3ceedf
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/std_common.i
@@ -0,0 +1,250 @@
+%include <std/std_except.i>
+
+//
+// Use the following macro with modern STL implementations
+//
+//#define SWIG_STD_MODERN_STL
+//
+// Use this to deactivate the previous definition, when using gcc-2.95
+// or similar old compilers.
+//
+//#define SWIG_STD_NOMODERN_STL
+
+// Here, we identify compilers we know have problems with STL.
+%{
+#if defined(__GNUC__)
+# if __GNUC__ == 2 && __GNUC_MINOR <= 96
+# define SWIG_STD_NOMODERN_STL
+# endif
+#endif
+%}
+
+//
+// Common code for supporting the C++ std namespace
+//
+
+%fragment("<string>");
+%fragment("<stdexcept>");
+%fragment("<stddef.h>");
+
+
+%fragment("StdIteratorTraits","header",fragment="<stddef.h>") %{
+#if defined(__SUNPRO_CC) && defined(_RWSTD_VER)
+# if !defined(SWIG_NO_STD_NOITERATOR_TRAITS_STL)
+# define SWIG_STD_NOITERATOR_TRAITS_STL
+# endif
+#endif
+
+#if !defined(SWIG_STD_NOITERATOR_TRAITS_STL)
+#include <iterator>
+#else
+namespace std {
+ template <class Iterator>
+ struct iterator_traits {
+ typedef ptrdiff_t difference_type;
+ typedef typename Iterator::value_type value_type;
+ };
+
+ template <class Iterator, class Category,class T, class Reference, class Pointer, class Distance>
+ struct iterator_traits<__reverse_bi_iterator<Iterator,Category,T,Reference,Pointer,Distance> > {
+ typedef Distance difference_type;
+ typedef T value_type;
+ };
+
+ template <class T>
+ struct iterator_traits<T*> {
+ typedef T value_type;
+ typedef ptrdiff_t difference_type;
+ };
+
+ template<typename _InputIterator>
+ inline typename iterator_traits<_InputIterator>::difference_type
+ distance(_InputIterator __first, _InputIterator __last)
+ {
+ typename iterator_traits<_InputIterator>::difference_type __n = 0;
+ while (__first != __last) {
+ ++__first; ++__n;
+ }
+ return __n;
+ }
+}
+#endif
+%}
+
+%fragment("StdTraitsCommon","header",fragment="<string>") %{
+namespace swig {
+ template <class Type>
+ struct noconst_traits {
+ typedef Type noconst_type;
+ };
+
+ template <class Type>
+ struct noconst_traits<const Type> {
+ typedef Type noconst_type;
+ };
+
+ /*
+ type categories
+ */
+ struct pointer_category { };
+ struct value_category { };
+
+ /*
+ General traits that provides type_name and type_info
+ */
+ template <class Type> struct traits { };
+
+ template <class Type>
+ inline const char* type_name() {
+ return traits<typename noconst_traits<Type >::noconst_type >::type_name();
+ }
+
+ template <class Type> struct traits_info {
+ static swig_type_info *type_query(std::string name) {
+ name += " *";
+ return SWIG_TypeQuery(name.c_str());
+ }
+ static swig_type_info *type_info() {
+ static swig_type_info *info = type_query(type_name<Type>());
+ return info;
+ }
+ };
+
+ /*
+ Partial specialization for pointers (traits_info)
+ */
+ template <class Type> struct traits_info<Type *> {
+ static swig_type_info *type_query(std::string name) {
+ name += " *";
+ return SWIG_TypeQuery(name.c_str());
+ }
+ static swig_type_info *type_info() {
+ static swig_type_info *info = type_query(type_name<Type>());
+ return info;
+ }
+ };
+
+ template <class Type>
+ inline swig_type_info *type_info() {
+ return traits_info<Type>::type_info();
+ }
+
+ /*
+ Partial specialization for pointers (traits)
+ */
+ template <class Type> struct traits <Type *> {
+ typedef pointer_category category;
+ static std::string make_ptr_name(const char* name) {
+ std::string ptrname = name;
+ ptrname += " *";
+ return ptrname;
+ }
+ static const char* type_name() {
+ static std::string name = make_ptr_name(swig::type_name<Type>());
+ return name.c_str();
+ }
+ };
+
+ template <class Type, class Category>
+ struct traits_as { };
+
+ template <class Type, class Category>
+ struct traits_check { };
+
+}
+%}
+
+/*
+ Generate the traits for a swigtype
+*/
+
+%define %traits_swigtype(Type...)
+%fragment(SWIG_Traits_frag(Type),"header",fragment="StdTraits") {
+ namespace swig {
+ template <> struct traits< Type > {
+ typedef pointer_category category;
+ static const char* type_name() { return #Type; }
+ };
+ }
+}
+%enddef
+
+
+
+/*
+ Generate the typemaps for a class that has 'value' traits
+*/
+
+%define %typemap_traits(Code,Type...)
+ %typemaps_asvalfrom(%arg(Code),
+ %arg(swig::asval< Type >),
+ %arg(swig::from),
+ %arg(SWIG_Traits_frag(Type)),
+ %arg(SWIG_Traits_frag(Type)),
+ Type);
+%enddef
+
+/*
+ Generate the typemaps for a class that behaves more like a 'pointer' or
+ plain wrapped Swigtype.
+*/
+
+%define %typemap_traits_ptr(Code,Type...)
+ %typemaps_asptrfrom(%arg(Code),
+ %arg(swig::asptr),
+ %arg(swig::from),
+ %arg(SWIG_Traits_frag(Type)),
+ %arg(SWIG_Traits_frag(Type)),
+ Type);
+%enddef
+
+
+/*
+ Equality methods
+*/
+%define %std_equal_methods(Type...)
+%extend Type {
+ bool operator == (const Type& v) {
+ return *self == v;
+ }
+
+ bool operator != (const Type& v) {
+ return *self != v;
+ }
+}
+
+%enddef
+
+/*
+ Order methods
+*/
+
+%define %std_order_methods(Type...)
+%extend Type {
+ bool operator > (const Type& v) {
+ return *self > v;
+ }
+
+ bool operator < (const Type& v) {
+ return *self < v;
+ }
+
+ bool operator >= (const Type& v) {
+ return *self >= v;
+ }
+
+ bool operator <= (const Type& v) {
+ return *self <= v;
+ }
+}
+%enddef
+
+/*
+ Comparison methods
+*/
+
+%define %std_comp_methods(Type...)
+%std_equal_methods(Type )
+%std_order_methods(Type )
+%enddef
+
diff --git a/contrib/tools/swig/Lib/std/std_container.i b/contrib/tools/swig/Lib/std/std_container.i
new file mode 100644
index 0000000000..570dfde484
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/std_container.i
@@ -0,0 +1,169 @@
+%include <std_common.i>
+%include <exception.i>
+%include <std_alloc.i>
+
+%{
+#include <algorithm>
+%}
+
+// Common non-resizable container methods
+
+%define %std_container_methods_non_resizable(container...)
+
+ container();
+ container(const container& other);
+
+ bool empty() const;
+ size_type size() const;
+ void swap(container& v);
+
+%enddef
+
+%define %std_container_methods_forward_iterators(container...)
+
+ #ifdef SWIG_EXPORT_ITERATOR_METHODS
+ class iterator;
+ class const_iterator;
+ iterator begin();
+ iterator end();
+ #endif
+
+%enddef
+
+%define %std_container_methods_reverse_iterators(container...)
+
+ #ifdef SWIG_EXPORT_ITERATOR_METHODS
+ class reverse_iterator;
+ class const_reverse_iterator;
+ reverse_iterator rbegin();
+ reverse_iterator rend();
+ #endif
+
+%enddef
+
+// Common container methods
+
+%define %std_container_methods(container...)
+
+ %std_container_methods_non_resizable(%arg(container))
+ %std_container_methods_forward_iterators(%arg(container))
+ %std_container_methods_reverse_iterators(%arg(container))
+
+ void clear();
+ allocator_type get_allocator() const;
+
+%enddef
+
+%define %std_container_methods_without_reverse_iterators(container...)
+
+ %std_container_methods_non_resizable(%arg(container))
+ %std_container_methods_forward_iterators(%arg(container))
+
+ void clear();
+ allocator_type get_allocator() const;
+
+%enddef
+
+// Common sequence
+
+%define %std_sequence_methods_common(sequence)
+
+ %std_container_methods(%arg(sequence));
+
+ sequence(size_type size);
+ void pop_back();
+
+ void resize(size_type new_size);
+
+ #ifdef SWIG_EXPORT_ITERATOR_METHODS
+%extend {
+ // %extend wrapper used for differing definitions of these methods introduced in C++11
+ iterator erase(iterator pos) { return $self->erase(pos); }
+ iterator erase(iterator first, iterator last) { return $self->erase(first, last); }
+}
+ #endif
+
+%enddef
+
+%define %std_sequence_methods_non_resizable(sequence)
+
+ %std_container_methods_non_resizable(%arg(sequence))
+ %std_container_methods_forward_iterators(%arg(container))
+ %std_container_methods_reverse_iterators(%arg(container))
+
+ const value_type& front() const;
+ const value_type& back() const;
+
+%enddef
+
+%define %std_sequence_methods(sequence)
+
+ %std_sequence_methods_common(%arg(sequence));
+
+ sequence(size_type size, const value_type& value);
+ void push_back(const value_type& x);
+
+ const value_type& front() const;
+ const value_type& back() const;
+
+ void assign(size_type n, const value_type& x);
+ void resize(size_type new_size, const value_type& x);
+
+ #ifdef SWIG_EXPORT_ITERATOR_METHODS
+%extend {
+ // %extend wrapper used for differing definitions of these methods introduced in C++11
+ iterator insert(iterator pos, const value_type& x) { return $self->insert(pos, x); }
+ void insert(iterator pos, size_type n, const value_type& x) { $self->insert(pos, n, x); }
+}
+ #endif
+
+%enddef
+
+%define %std_sequence_methods_non_resizable_val(sequence...)
+
+ %std_container_methods_non_resizable(%arg(sequence))
+ %std_container_methods_forward_iterators(%arg(container))
+ %std_container_methods_reverse_iterators(%arg(container))
+
+ value_type front() const;
+ value_type back() const;
+
+#endif
+
+%enddef
+
+%define %std_sequence_methods_val(sequence...)
+
+ %std_sequence_methods_common(%arg(sequence));
+
+ sequence(size_type size, value_type value);
+ void push_back(value_type x);
+
+ value_type front() const;
+ value_type back() const;
+
+ void assign(size_type n, value_type x);
+ void resize(size_type new_size, value_type x);
+
+ #ifdef SWIG_EXPORT_ITERATOR_METHODS
+%extend {
+ // %extend wrapper used for differing definitions of these methods introduced in C++11
+ iterator insert(iterator pos, value_type x) { return $self->insert(pos, x); }
+ void insert(iterator pos, size_type n, value_type x) { $self->insert(pos, n, x); }
+}
+ #endif
+
+%enddef
+
+
+//
+// Ignore member methods for Type with no default constructor
+//
+%define %std_nodefconst_type(Type...)
+%feature("ignore") std::vector< Type >::vector(size_type size);
+%feature("ignore") std::vector< Type >::resize(size_type size);
+%feature("ignore") std::deque< Type >::deque(size_type size);
+%feature("ignore") std::deque< Type >::resize(size_type size);
+%feature("ignore") std::list< Type >::list(size_type size);
+%feature("ignore") std::list< Type >::resize(size_type size);
+%enddef
diff --git a/contrib/tools/swig/Lib/std/std_except.i b/contrib/tools/swig/Lib/std/std_except.i
new file mode 100644
index 0000000000..728b9c8b52
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/std_except.i
@@ -0,0 +1,73 @@
+#if defined(SWIGJAVA) || defined(SWIGCSHARP)
+#error "do not use this version of std_except.i"
+#endif
+
+%{
+#include <typeinfo>
+#include <stdexcept>
+%}
+
+#if defined(SWIG_STD_EXCEPTIONS_AS_CLASSES)
+
+namespace std {
+ struct exception
+ {
+ virtual ~exception() throw();
+ virtual const char* what() const throw();
+ };
+
+ struct bad_cast : exception
+ {
+ };
+
+ struct bad_exception : exception
+ {
+ };
+
+ struct logic_error : exception
+ {
+ logic_error(const string& msg);
+ };
+
+ struct domain_error : logic_error
+ {
+ domain_error(const string& msg);
+ };
+
+ struct invalid_argument : logic_error
+ {
+ invalid_argument(const string& msg);
+ };
+
+ struct length_error : logic_error
+ {
+ length_error(const string& msg);
+ };
+
+ struct out_of_range : logic_error
+ {
+ out_of_range(const string& msg);
+ };
+
+ struct runtime_error : exception
+ {
+ runtime_error(const string& msg);
+ };
+
+ struct range_error : runtime_error
+ {
+ range_error(const string& msg);
+ };
+
+ struct overflow_error : runtime_error
+ {
+ overflow_error(const string& msg);
+ };
+
+ struct underflow_error : runtime_error
+ {
+ underflow_error(const string& msg);
+ };
+}
+
+#endif
diff --git a/contrib/tools/swig/Lib/std/std_string.i b/contrib/tools/swig/Lib/std/std_string.i
new file mode 100644
index 0000000000..35fcdd16c6
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/std_string.i
@@ -0,0 +1,13 @@
+%include <std/std_basic_string.i>
+
+/* plain strings */
+
+namespace std
+{
+ %std_comp_methods(basic_string<char>);
+ %naturalvar string;
+ typedef basic_string<char> string;
+}
+
+
+%template(string) std::basic_string<char>;
diff --git a/contrib/tools/swig/Lib/std/std_vector.i b/contrib/tools/swig/Lib/std/std_vector.i
new file mode 100644
index 0000000000..b35f03bea2
--- /dev/null
+++ b/contrib/tools/swig/Lib/std/std_vector.i
@@ -0,0 +1,225 @@
+//
+// std::vector
+//
+
+%include <std_container.i>
+
+// Vector
+
+%define %std_vector_methods(vector...)
+ %std_sequence_methods(vector)
+
+ void reserve(size_type n);
+ size_type capacity() const;
+%enddef
+
+
+%define %std_vector_methods_val(vector...)
+ %std_sequence_methods_val(vector)
+
+ void reserve(size_type n);
+ size_type capacity() const;
+%enddef
+
+
+// ------------------------------------------------------------------------
+// std::vector
+//
+// The aim of all that follows would be to integrate std::vector with
+// as much as possible, namely, to allow the user to pass and
+// be returned tuples or lists.
+// const declarations are used to guess the intent of the function being
+// exported; therefore, the following rationale is applied:
+//
+// -- f(std::vector<T>), f(const std::vector<T>&):
+// the parameter being read-only, either a sequence or a
+// previously wrapped std::vector<T> can be passed.
+// -- f(std::vector<T>&), f(std::vector<T>*):
+// the parameter may be modified; therefore, only a wrapped std::vector
+// can be passed.
+// -- std::vector<T> f(), const std::vector<T>& f():
+// the vector is returned by copy; therefore, a sequence of T:s
+// is returned which is most easily used in other functions
+// -- std::vector<T>& f(), std::vector<T>* f():
+// the vector is returned by reference; therefore, a wrapped std::vector
+// is returned
+// -- const std::vector<T>* f(), f(const std::vector<T>*):
+// for consistency, they expect and return a plain vector pointer.
+// ------------------------------------------------------------------------
+
+%{
+#include <vector>
+%}
+
+// exported classes
+
+
+namespace std {
+
+ template<class _Tp, class _Alloc = allocator< _Tp > >
+ class vector {
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef _Tp value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef _Alloc allocator_type;
+
+ %traits_swigtype(_Tp);
+ %traits_enum(_Tp);
+
+ %fragment(SWIG_Traits_frag(std::vector< _Tp, _Alloc >), "header",
+ fragment=SWIG_Traits_frag(_Tp),
+ fragment="StdVectorTraits") {
+ namespace swig {
+ template <> struct traits<std::vector< _Tp, _Alloc > > {
+ typedef pointer_category category;
+ static const char* type_name() {
+ return "std::vector<" #_Tp "," #_Alloc " >";
+ }
+ };
+ }
+ }
+
+ %typemap_traits_ptr(SWIG_TYPECHECK_VECTOR, std::vector< _Tp, _Alloc >);
+
+#ifdef %swig_vector_methods
+ // Add swig/language extra methods
+ %swig_vector_methods(std::vector< _Tp, _Alloc >);
+#endif
+
+ %std_vector_methods(vector);
+ };
+
+ // ***
+ // This specialization should disappear or get simplified when
+ // a 'const SWIGTYPE*&' can be defined
+ // ***
+ template<class _Tp, class _Alloc >
+ class vector< _Tp*, _Alloc > {
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef _Tp* value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef _Alloc allocator_type;
+
+ %traits_swigtype(_Tp);
+
+ %fragment(SWIG_Traits_frag(std::vector< _Tp*, _Alloc >), "header",
+ fragment=SWIG_Traits_frag(_Tp),
+ fragment="StdVectorTraits") {
+ namespace swig {
+ template <> struct traits<std::vector< _Tp*, _Alloc > > {
+ typedef value_category category;
+ static const char* type_name() {
+ return "std::vector<" #_Tp " *," #_Alloc " >";
+ }
+ };
+ }
+ }
+
+ %typemap_traits_ptr(SWIG_TYPECHECK_VECTOR, std::vector< _Tp*, _Alloc >);
+
+#ifdef %swig_vector_methods_val
+ // Add swig/language extra methods
+ %swig_vector_methods_val(std::vector< _Tp*, _Alloc >);
+#endif
+
+ %std_vector_methods_val(vector);
+ };
+
+ // ***
+ // const pointer specialization
+ // ***
+ template<class _Tp, class _Alloc >
+ class vector< _Tp const *, _Alloc > {
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef _Tp const * value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef _Alloc allocator_type;
+
+ %traits_swigtype(_Tp);
+
+ %fragment(SWIG_Traits_frag(std::vector< _Tp const*, _Alloc >), "header",
+ fragment=SWIG_Traits_frag(_Tp),
+ fragment="StdVectorTraits") {
+ namespace swig {
+ template <> struct traits<std::vector< _Tp const*, _Alloc > > {
+ typedef value_category category;
+ static const char* type_name() {
+ return "std::vector<" #_Tp " const*," #_Alloc " >";
+ }
+ };
+ }
+ }
+
+ %typemap_traits_ptr(SWIG_TYPECHECK_VECTOR, std::vector< _Tp const*, _Alloc >);
+
+#ifdef %swig_vector_methods_val
+ // Add swig/language extra methods
+ %swig_vector_methods_val(std::vector< _Tp const*, _Alloc >);
+#endif
+
+ %std_vector_methods_val(vector);
+ };
+
+ // ***
+ // bool specialization
+ // ***
+
+ template<class _Alloc >
+ class vector<bool,_Alloc > {
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef bool value_type;
+ typedef value_type* pointer;
+ typedef const value_type* const_pointer;
+ typedef value_type& reference;
+ typedef bool const_reference;
+ typedef _Alloc allocator_type;
+
+ %traits_swigtype(bool);
+
+ %fragment(SWIG_Traits_frag(std::vector<bool, _Alloc >), "header",
+ fragment=SWIG_Traits_frag(bool),
+ fragment="StdVectorTraits") {
+ namespace swig {
+ template <> struct traits<std::vector<bool, _Alloc > > {
+ typedef value_category category;
+ static const char* type_name() {
+ return "std::vector<bool, _Alloc >";
+ }
+ };
+ }
+ }
+
+ %typemap_traits_ptr(SWIG_TYPECHECK_VECTOR, std::vector<bool, _Alloc >);
+
+
+#ifdef %swig_vector_methods_val
+ // Add swig/language extra methods
+ %swig_vector_methods_val(std::vector<bool, _Alloc >);
+#endif
+
+ %std_vector_methods_val(vector);
+
+#if defined(SWIG_STD_MODERN_STL) && !defined(SWIG_STD_NOMODERN_STL)
+ void flip();
+#endif
+
+ };
+
+}
diff --git a/contrib/tools/swig/Lib/swig.swg b/contrib/tools/swig/Lib/swig.swg
new file mode 100644
index 0000000000..9f9d533498
--- /dev/null
+++ b/contrib/tools/swig/Lib/swig.swg
@@ -0,0 +1,729 @@
+/* -----------------------------------------------------------------------------
+ * swig.swg
+ *
+ * Common macro definitions for various SWIG directives. This file is always
+ * included at the top of each input file.
+ * ----------------------------------------------------------------------------- */
+
+/* -----------------------------------------------------------------------------
+ * User Directives
+ * ----------------------------------------------------------------------------- */
+
+/* Deprecated SWIG-1.1 directives */
+
+#define %disabledoc %warn "104:%disabledoc is deprecated"
+#define %enabledoc %warn "105:%enabledoc is deprecated"
+#define %doconly %warn "106:%doconly is deprecated"
+#define %style %warn "107:%style is deprecated" /##/
+#define %localstyle %warn "108:%localstyle is deprecated" /##/
+#define %title %warn "109:%title is deprecated" /##/
+#define %section %warn "110:%section is deprecated" /##/
+#define %subsection %warn "111:%subsection is deprecated" /##/
+#define %subsubsection %warn "112:%subsubsection is deprecated" /##/
+#define %new %warn "117:%new is deprecated. Use %newobject"
+#define %text %insert("null")
+
+/* Code insertion directives such as %wrapper %{ ... %} */
+
+#define %begin %insert("begin")
+#define %runtime %insert("runtime")
+#define %header %insert("header")
+#define %wrapper %insert("wrapper")
+#define %init %insert("init")
+
+/* Class extension */
+
+#define %addmethods %warn "113:%addmethods is now %extend" %extend
+
+/* %ignore directive */
+
+#define %ignore %rename($ignore)
+#define %ignorewarn(x) %rename("$ignore:" x)
+
+/* Access control directives */
+
+#define %readonly %warn "114:%readonly is deprecated. Use %immutable; " %feature("immutable");
+#define %readwrite %warn "115:%readwrite is deprecated. Use %mutable; " %feature("immutable","");
+
+#define %immutable %feature("immutable")
+#define %noimmutable %feature("immutable","0")
+#define %clearimmutable %feature("immutable","")
+#define %mutable %clearimmutable
+
+/* Generation of default constructors/destructors (old form, don't use) */
+#define %nodefault %feature("nodefault","1")
+#define %default %feature("nodefault","0")
+#define %clearnodefault %feature("nodefault","")
+#define %makedefault %clearnodefault
+
+/* Disable the generation of implicit default constructor */
+#define %nodefaultctor %feature("nodefaultctor","1")
+#define %defaultctor %feature("nodefaultctor","0")
+#define %clearnodefaultctor %feature("nodefaultctor","")
+
+/* Disable the generation of implicit default destructor (dangerous) */
+#define %nodefaultdtor %feature("nodefaultdtor","1")
+#define %defaultdtor %feature("nodefaultdtor","0")
+#define %clearnodefaultdtor %feature("nodefaultdtor","")
+
+/* Enable the generation of copy constructor */
+#define %copyctor %feature("copyctor","1")
+#define %nocopyctor %feature("copyctor","0")
+#define %clearcopyctor %feature("copyctor","")
+
+/* Force the old nodefault behavior, ie disable both constructor and destructor */
+#define %oldnodefault %feature("oldnodefault","1")
+#define %nooldnodefault %feature("oldnodefault","0")
+#define %clearoldnodefault %feature("oldnodefault","")
+
+/* the %exception directive */
+#if defined(SWIGCSHARP) || defined(SWIGD)
+#define %exception %feature("except", canthrow=1)
+#else
+#define %exception %feature("except")
+#endif
+#define %noexception %feature("except","0")
+#define %clearexception %feature("except","")
+
+/* the %allowexception directive allows the %exception feature to
+ be applied to set/get variable methods */
+#define %allowexception %feature("allowexcept")
+#define %noallowexception %feature("allowexcept","0")
+#define %clearallowexception %feature("allowexcept","")
+
+/* the %exceptionvar directive, as %exception but it is only applied
+ to set/get variable methods. You don't need to use the
+ %allowexception directive when using %exceptionvar.
+*/
+#if defined(SWIGCSHARP) || defined(SWIGD)
+#define %exceptionvar %feature("exceptvar", canthrow=1)
+#else
+#define %exceptionvar %feature("exceptvar")
+#endif
+#define %noexceptionvar %feature("exceptvar","0")
+#define %clearexceptionvar %feature("exceptvar","")
+
+/* the %catches directive */
+#define %catches(tlist...) %feature("catches","("`tlist`")")
+#define %clearcatches %feature("catches","")
+
+/* the %exceptionclass directive */
+#define %exceptionclass %feature("exceptionclass")
+#define %noexceptionclass %feature("exceptionclass","0")
+#define %clearexceptionclass %feature("exceptionclass","")
+
+/* the %newobject directive */
+#define %newobject %feature("new")
+#define %nonewobject %feature("new","0")
+#define %clearnewobject %feature("new","")
+
+/* the %delobject directive */
+#define %delobject %feature("del")
+#define %nodelobject %feature("del","0")
+#define %cleardelobject %feature("del","")
+
+/* the %refobject/%unrefobject directives */
+#define %refobject %feature("ref")
+#define %norefobject %feature("ref","0")
+#define %clearrefobject %feature("ref","")
+
+#define %unrefobject %feature("unref")
+#define %nounrefobject %feature("unref","0")
+#define %clearunrefobject %feature("unref","")
+
+/* Directives for callback functions (experimental) */
+#define %callback(x) %feature("callback",`x`)
+#define %nocallback %feature("callback","0")
+#define %clearcallback %feature("callback","")
+
+/* the %nestedworkaround directive (deprecated) */
+#define %nestedworkaround %feature("nestedworkaround")
+#define %nonestedworkaround %feature("nestedworkaround","0")
+#define %clearnestedworkaround %feature("nestedworkaround","")
+
+/* the %flatnested directive */
+#define %flatnested %feature("flatnested")
+#define %noflatnested %feature("flatnested","0")
+#define %clearflatnested %feature("flatnested","")
+
+/* the %fastdispatch directive */
+#define %fastdispatch %feature("fastdispatch")
+#define %nofastdispatch %feature("fastdispatch","0")
+#define %clearfastdispatch %feature("fastdispatch","")
+
+/* directors directives */
+#define %director %feature("director")
+#define %nodirector %feature("director","0")
+#define %cleardirector %feature("director","")
+
+/* naturalvar directives */
+#define %naturalvar %feature("naturalvar")
+#define %nonaturalvar %feature("naturalvar","0")
+#define %clearnaturalvar %feature("naturalvar","")
+
+/* nspace directives */
+#define %nspace %feature("nspace")
+#define %nonspace %feature("nspace","0")
+#define %clearnspace %feature("nspace","")
+
+/* valuewrapper directives */
+#define %valuewrapper %feature("valuewrapper")
+#define %clearvaluewrapper %feature("valuewrapper","")
+#define %novaluewrapper %feature("novaluewrapper")
+#define %clearnovaluewrapper %feature("novaluewrapper","")
+
+/* Contract support - Experimental */
+#define %contract %feature("contract")
+#define %nocontract %feature("contract","0")
+#define %clearcontract %feature("contract","")
+
+/* Macro for setting a dynamic cast function */
+%define DYNAMIC_CAST(mangle,func)
+%init %{
+ mangle->dcast = (swig_dycast_func) func;
+%}
+%enddef
+
+/* aggregation support */
+/*
+ This macro performs constant aggregation. Basically the idea of
+ constant aggregation is that you can group a collection of constants
+ together. For example, suppose you have some code like this:
+
+ #define UP 1
+ #define DOWN 2
+ #define LEFT 3
+ #define RIGHT 4
+
+ Now, suppose you had a function like this:
+
+ int move(int direction)
+
+ In this case, you might want to restrict the direction argument to
+ one of the supplied constant names. To do this, you could write some
+ typemap code by hand. Alternatively, you can use the
+ %aggregate_check macro defined here to create a simple check
+ function for you. Here is an example:
+
+ %aggregate_check(int, check_direction, UP, DOWN, LEFT, RIGHT);
+
+ Now, using a typemap
+
+ %typemap(check) int direction {
+ if (!check_direction($1)) SWIG_exception(SWIG_ValueError,"Bad direction.");
+ }
+
+ or a contract (better)
+
+ %contract move(int x) {
+ require:
+ check_direction(x);
+ }
+
+*/
+
+%define %aggregate_check(TYPE, NAME, FIRST, ...)
+%wrapper %{
+static int NAME(TYPE x) {
+ static TYPE values[] = { FIRST, ##__VA_ARGS__ };
+ static int size = sizeof(values);
+ int i,j;
+ for (i = 0, j = 0; i < size; i+=sizeof(TYPE),j++) {
+ if (x == values[j]) return 1;
+ }
+ return 0;
+}
+%}
+%enddef
+
+
+/* -----------------------------------------------------------------------------
+ * %rename predicates
+ * ----------------------------------------------------------------------------- */
+/*
+ Predicates to be used with %rename, for example:
+
+ - to rename all the functions:
+
+ %rename("%(utitle)s", %$isfunction) "";
+
+ - to rename only the member methods:
+
+ %rename("m_%(utitle)s", %$isfunction, %$ismember) "";
+
+ - to rename only the global functions:
+
+ %rename("m_%(utitle)s", %$isfunction, %$not %$ismember) "";
+
+ or
+
+ %rename("g_%(utitle)s", %$isfunction, %$isglobal) "";
+
+ - to ignore the enumitems in a given class:
+
+ %rename("$ignore", %$isenumitem, %$classname="MyClass") "";
+
+ we use the prefix '%$' to avoid clashes with other swig
+ macros/directives.
+
+*/
+
+/* Note that when %$not is used with another macro, say %enum as follows: %$not %$enum, the result is "notmatch=enum" */
+%define %$not "not" %enddef
+
+%define %$isenum "match"="enum" %enddef
+%define %$isenumitem "match"="enumitem" %enddef
+%define %$isaccess "match"="access" %enddef
+%define %$isclass "match"="class","notmatch$template$templatetype"="class" %enddef
+%define %$isextend "match"="extend" %enddef
+%define %$isconstructor "match"="constructor" %enddef
+%define %$isdestructor "match"="destructor" %enddef
+%define %$isnamespace "match"="namespace" %enddef
+%define %$istemplate "match"="template" %enddef
+%define %$isconstant "match"="constant" %enddef /* %constant definition */
+%define %$isusing "match"="using" %enddef
+
+%define %$isunion "match$kind"="union" %enddef
+%define %$isfunction "match$kind"="function" %enddef
+%define %$isvariable "match$kind"="variable" %enddef
+%define %$isimmutable "match$feature:immutable"="1" %enddef
+%define %$hasconsttype "match$hasconsttype"="1" %enddef
+%define %$hasvalue "match$hasvalue"="1" %enddef
+%define %$isextension "match$isextension"="1" %enddef
+
+%define %$isstatic "match$storage"="static" %enddef
+%define %$isfriend "match$storage"="friend" %enddef
+%define %$istypedef "match$storage"="typedef" %enddef
+%define %$isvirtual "match$storage"="virtual" %enddef
+%define %$isexplicit "match$storage"="explicit" %enddef
+%define %$isextern "match$storage"="extern" %enddef
+
+%define %$ismember "match$ismember"="1" %enddef
+%define %$isglobal %$not %$ismember %enddef
+%define %$isextendmember "match$isextendmember"="1" %enddef
+%define %$innamespace "match$parentNode$nodeType"="namespace" %enddef
+
+%define %$ispublic "match$access"="public" %enddef
+%define %$isprotected "match$access"="protected" %enddef
+%define %$isprivate "match$access"="private" %enddef
+
+%define %$ismemberget "match$memberget"="1" %enddef
+%define %$ismemberset "match$memberset"="1" %enddef
+
+%define %$classname %$ismember,"match$parentNode$name" %enddef
+%define %$isnested "match$nested"="1" %enddef
+
+/* -----------------------------------------------------------------------------
+ * Common includes for warning labels, macros, fragments etc
+ * ----------------------------------------------------------------------------- */
+
+%include <swigwarnings.swg>
+%include <swigfragments.swg>
+
+/* -----------------------------------------------------------------------------
+ * Overloading support
+ * ----------------------------------------------------------------------------- */
+
+/*
+ * Function/method overloading support. This is done through typemaps,
+ * but also involves a precedence level.
+ */
+
+/* Macro for overload resolution */
+
+%define %typecheck(_x...) %typemap(typecheck, precedence=_x) %enddef
+
+/* Macros for precedence levels */
+
+%define SWIG_TYPECHECK_POINTER 0 %enddef
+%define SWIG_TYPECHECK_ITERATOR 5 %enddef
+%define SWIG_TYPECHECK_VOIDPTR 10 %enddef
+%define SWIG_TYPECHECK_BOOL 15 %enddef
+%define SWIG_TYPECHECK_UINT8 20 %enddef
+%define SWIG_TYPECHECK_INT8 25 %enddef
+%define SWIG_TYPECHECK_UINT16 30 %enddef
+%define SWIG_TYPECHECK_INT16 35 %enddef
+%define SWIG_TYPECHECK_UINT32 40 %enddef
+%define SWIG_TYPECHECK_INT32 45 %enddef
+%define SWIG_TYPECHECK_SIZE 47 %enddef
+%define SWIG_TYPECHECK_PTRDIFF 48 %enddef
+%define SWIG_TYPECHECK_UINT64 50 %enddef
+%define SWIG_TYPECHECK_INT64 55 %enddef
+%define SWIG_TYPECHECK_UINT128 60 %enddef
+%define SWIG_TYPECHECK_INT128 65 %enddef
+%define SWIG_TYPECHECK_INTEGER 70 %enddef
+%define SWIG_TYPECHECK_FLOAT 80 %enddef
+%define SWIG_TYPECHECK_DOUBLE 90 %enddef
+%define SWIG_TYPECHECK_CPLXFLT 95 %enddef
+%define SWIG_TYPECHECK_CPLXDBL 100 %enddef
+%define SWIG_TYPECHECK_COMPLEX 105 %enddef
+%define SWIG_TYPECHECK_UNICHAR 110 %enddef
+%define SWIG_TYPECHECK_STDUNISTRING 115 %enddef
+%define SWIG_TYPECHECK_UNISTRING 120 %enddef
+%define SWIG_TYPECHECK_CHAR 130 %enddef
+%define SWIG_TYPECHECK_STDSTRING 135 %enddef
+%define SWIG_TYPECHECK_STRING 140 %enddef
+%define SWIG_TYPECHECK_PAIR 150 %enddef
+%define SWIG_TYPECHECK_STDARRAY 155 %enddef
+%define SWIG_TYPECHECK_VECTOR 160 %enddef
+%define SWIG_TYPECHECK_DEQUE 170 %enddef
+%define SWIG_TYPECHECK_LIST 180 %enddef
+%define SWIG_TYPECHECK_SET 190 %enddef
+%define SWIG_TYPECHECK_MULTISET 200 %enddef
+%define SWIG_TYPECHECK_MAP 210 %enddef
+%define SWIG_TYPECHECK_MULTIMAP 220 %enddef
+%define SWIG_TYPECHECK_STACK 230 %enddef
+%define SWIG_TYPECHECK_QUEUE 240 %enddef
+
+%define SWIG_TYPECHECK_BOOL_ARRAY 1015 %enddef
+%define SWIG_TYPECHECK_INT8_ARRAY 1025 %enddef
+%define SWIG_TYPECHECK_INT16_ARRAY 1035 %enddef
+%define SWIG_TYPECHECK_INT32_ARRAY 1045 %enddef
+%define SWIG_TYPECHECK_INT64_ARRAY 1055 %enddef
+%define SWIG_TYPECHECK_INT128_ARRAY 1065 %enddef
+%define SWIG_TYPECHECK_FLOAT_ARRAY 1080 %enddef
+%define SWIG_TYPECHECK_DOUBLE_ARRAY 1090 %enddef
+%define SWIG_TYPECHECK_CHAR_ARRAY 1130 %enddef
+%define SWIG_TYPECHECK_STRING_ARRAY 1140 %enddef
+%define SWIG_TYPECHECK_OBJECT_ARRAY 1150 %enddef
+
+%define SWIG_TYPECHECK_BOOL_PTR 2015 %enddef
+%define SWIG_TYPECHECK_UINT8_PTR 2020 %enddef
+%define SWIG_TYPECHECK_INT8_PTR 2025 %enddef
+%define SWIG_TYPECHECK_UINT16_PTR 2030 %enddef
+%define SWIG_TYPECHECK_INT16_PTR 2035 %enddef
+%define SWIG_TYPECHECK_UINT32_PTR 2040 %enddef
+%define SWIG_TYPECHECK_INT32_PTR 2045 %enddef
+%define SWIG_TYPECHECK_UINT64_PTR 2050 %enddef
+%define SWIG_TYPECHECK_INT64_PTR 2055 %enddef
+%define SWIG_TYPECHECK_FLOAT_PTR 2080 %enddef
+%define SWIG_TYPECHECK_DOUBLE_PTR 2090 %enddef
+%define SWIG_TYPECHECK_CHAR_PTR 2130 %enddef
+
+%define SWIG_TYPECHECK_SWIGOBJECT 5000 %enddef
+
+
+/* -----------------------------------------------------------------------------
+ * Default handling of certain overloaded operators
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+%ignoreoperator(NEW) operator new;
+%ignoreoperator(DELETE) operator delete;
+%ignoreoperator(NEWARR) operator new[];
+%ignoreoperator(DELARR) operator delete[];
+
+/* add C++ operator aliases */
+%rename("operator &&") operator and; // `and' `&&'
+%rename("operator ||") operator or; // `or' `||'
+%rename("operator !") operator not; // `not' `!'
+%rename("operator &=") operator and_eq; // `and_eq' `&='
+%rename("operator &") operator bitand; // `bitand' `&'
+%rename("operator |") operator bitor; // `bitor' `|'
+%rename("operator ~") operator compl; // `compl' `~'
+%rename("operator !=") operator not_eq; // `not_eq' `!='
+%rename("operator |=") operator or_eq; // `or_eq' `|='
+%rename("operator ^") operator xor; // `xor' `^'
+%rename("operator ^=") operator xor_eq; // `xor_eq' `^='
+
+/* Smart pointer handling */
+
+%rename(__deref__) *::operator->;
+%rename(__ref__) *::operator*();
+%rename(__ref__) *::operator*() const;
+
+/* Define std namespace */
+namespace std {
+ /* Warn about std::initializer_list usage. The constructor/method where used should probably be ignored. See docs. */
+ template<typename T> class initializer_list {};
+ %typemap(in, warning=SWIGWARN_TYPEMAP_INITIALIZER_LIST_MSG) initializer_list<T> ""
+ %typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) initializer_list<T> ""
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Default char * and C array typemaps
+ * ----------------------------------------------------------------------------- */
+
+/* Set up the typemap for handling new return strings */
+
+#ifdef __cplusplus
+%typemap(newfree) char * "delete [] $1;"
+#else
+%typemap(newfree) char * "free($1);"
+#endif
+
+/* Default typemap for handling char * members */
+
+#ifdef __cplusplus
+%typemap(memberin,fragment="<string.h>") char * {
+ delete [] $1;
+ if ($input) {
+ $1 = ($1_type) (new char[strlen((const char *)$input)+1]);
+ strcpy((char *)$1, (const char *)$input);
+ } else {
+ $1 = 0;
+ }
+}
+%typemap(memberin,warning=SWIGWARN_TYPEMAP_CHARLEAK_MSG,fragment="<string.h>") const char * {
+ if ($input) {
+ $1 = ($1_type) (new char[strlen((const char *)$input)+1]);
+ strcpy((char *)$1, (const char *)$input);
+ } else {
+ $1 = 0;
+ }
+}
+%typemap(globalin,fragment="<string.h>") char * {
+ delete [] $1;
+ if ($input) {
+ $1 = ($1_type) (new char[strlen((const char *)$input)+1]);
+ strcpy((char *)$1, (const char *)$input);
+ } else {
+ $1 = 0;
+ }
+}
+%typemap(globalin,warning=SWIGWARN_TYPEMAP_CHARLEAK_MSG,fragment="<string.h>") const char * {
+ if ($input) {
+ $1 = ($1_type) (new char[strlen((const char *)$input)+1]);
+ strcpy((char *)$1, (const char *)$input);
+ } else {
+ $1 = 0;
+ }
+}
+#else
+%typemap(memberin,fragment="<string.h>") char * {
+ free($1);
+ if ($input) {
+ $1 = ($1_type) malloc(strlen((const char *)$input)+1);
+ strcpy((char *)$1, (const char *)$input);
+ } else {
+ $1 = 0;
+ }
+}
+%typemap(memberin,warning=SWIGWARN_TYPEMAP_CHARLEAK_MSG,fragment="<string.h>") const char * {
+ if ($input) {
+ $1 = ($1_type) malloc(strlen((const char *)$input)+1);
+ strcpy((char *)$1, (const char *)$input);
+ } else {
+ $1 = 0;
+ }
+}
+%typemap(globalin,fragment="<string.h>") char * {
+ free($1);
+ if ($input) {
+ $1 = ($1_type) malloc(strlen((const char *)$input)+1);
+ strcpy((char *)$1, (const char *)$input);
+ } else {
+ $1 = 0;
+ }
+}
+%typemap(globalin,warning=SWIGWARN_TYPEMAP_CHARLEAK_MSG,fragment="<string.h>") const char * {
+ if ($input) {
+ $1 = ($1_type) malloc(strlen((const char *)$input)+1);
+ strcpy((char *)$1, (const char *)$input);
+ } else {
+ $1 = 0;
+ }
+}
+
+#endif
+
+/* Character array handling */
+
+%typemap(memberin,fragment="<string.h>") char [ANY] {
+ if($input) {
+ strncpy((char*)$1, (const char *)$input, $1_dim0-1);
+ $1[$1_dim0-1] = 0;
+ } else {
+ $1[0] = 0;
+ }
+}
+
+%typemap(globalin,fragment="<string.h>") char [ANY] {
+ if($input) {
+ strncpy((char*)$1, (const char *)$input, $1_dim0-1);
+ $1[$1_dim0-1] = 0;
+ } else {
+ $1[0] = 0;
+ }
+}
+
+%typemap(memberin,fragment="<string.h>") char [] {
+ if ($input) strcpy((char *)$1, (const char *)$input);
+ else $1[0] = 0;
+}
+
+%typemap(globalin,fragment="<string.h>") char [] {
+ if ($input) strcpy((char *)$1, (const char *)$input);
+ else $1[0] = 0;
+}
+
+/* memberin/globalin typemap for arrays. */
+
+%typemap(memberin,fragment="<string.h>") SWIGTYPE [ANY] {
+ size_t ii;
+ $1_basetype *b = ($1_basetype *) $1;
+ for (ii = 0; ii < (size_t)$1_size; ii++) b[ii] = *(($1_basetype *) $input + ii);
+}
+
+%typemap(globalin,fragment="<string.h>") SWIGTYPE [ANY] {
+ size_t ii;
+ $1_basetype *b = ($1_basetype *) $1;
+ for (ii = 0; ii < (size_t)$1_size; ii++) b[ii] = *(($1_basetype *) $input + ii);
+}
+
+/* memberin/globalin typemap for double arrays. */
+
+%typemap(memberin,fragment="<string.h>") SWIGTYPE [ANY][ANY] {
+ $basetype (*inp)[$1_dim1] = ($basetype (*)[$1_dim1])($input);
+ $basetype (*dest)[$1_dim1] = ($basetype (*)[$1_dim1])($1);
+ size_t ii = 0;
+ for (; ii < $1_dim0; ++ii) {
+ $basetype *ip = inp[ii];
+ $basetype *dp = dest[ii];
+ size_t jj = 0;
+ for (; jj < $1_dim1; ++jj) dp[jj] = ip[jj];
+ }
+}
+
+%typemap(globalin,fragment="<string.h>") SWIGTYPE [ANY][ANY] {
+ $basetype (*inp)[$1_dim1] = ($basetype (*)[$1_dim1])($input);
+ $basetype (*dest)[$1_dim1] = ($basetype (*)[$1_dim1])($1);
+ size_t ii = 0;
+ for (; ii < $1_dim0; ++ii) {
+ $basetype *ip = inp[ii];
+ $basetype *dp = dest[ii];
+ size_t jj = 0;
+ for (; jj < $1_dim1; ++jj) dp[jj] = ip[jj];
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Runtime code
+ * ----------------------------------------------------------------------------- */
+
+
+%insert("runtime") "swiglabels.swg"
+
+
+/* The SwigValueWrapper class */
+
+/*
+ * This template wrapper is used to handle C++ objects that are passed or
+ * returned by value. This is necessary to handle objects that define
+ * no default-constructor (making it difficult for SWIG to properly declare
+ * local variables).
+ *
+ * The wrapper is used as follows. First consider a function like this:
+ *
+ * Vector cross_product(Vector a, Vector b)
+ *
+ * Now, if Vector is defined as a C++ class with no default constructor,
+ * code is generated as follows:
+ *
+ * Vector *wrap_cross_product(Vector *inarg1, Vector *inarg2) {
+ * SwigValueWrapper<Vector> arg1;
+ * SwigValueWrapper<Vector> arg2;
+ * SwigValueWrapper<Vector> result;
+ *
+ * arg1 = *inarg1;
+ * arg2 = *inarg2;
+ * ...
+ * result = cross_product(arg1,arg2);
+ * ...
+ * return new Vector(result);
+ * }
+ *
+ * In the wrappers, the template SwigValueWrapper simply provides a thin
+ * layer around a Vector *. However, it does this in a way that allows
+ * the object to be bound after the variable declaration (which is not possible
+ * with the bare object when it lacks a default constructor).
+ *
+ * An observant reader will notice that the code after the variable declarations
+ * is *identical* to the code used for classes that do define default constructors.
+ * Thus, this neat trick allows us to fix this special case without having to
+ * make massive changes to typemaps and other parts of the SWIG code generator.
+ *
+ * Note: this code is not included when SWIG runs in C-mode, when classes
+ * define default constructors, or when pointers and references are used.
+ * SWIG tries to avoid doing this except in very special circumstances.
+ *
+ * Note: This solution suffers from making a large number of copies
+ * of the underlying object. However, this is needed in the interest of
+ * safety and in order to cover all of the possible ways in which a value
+ * might be assigned. For example:
+ *
+ * arg1 = *inarg1; // Assignment from a pointer
+ * arg1 = Vector(1,2,3); // Assignment from a value
+ *
+ * SwigValueWrapper is a drop in replacement to modify normal value semantics by
+ * using the heap instead of the stack to copy/move the underlying object it is
+ * managing. Smart pointers also manage an underlying object on the heap, so
+ * SwigValueWrapper has characteristics of a smart pointer. The reset function
+ * is specific smart pointer functionality, but cannot be a non-static member as
+ * when SWIG modifies typemap code it assumes non-static member function calls
+ * are routed to the underlying object, changing for example $1.f() to (&x)->f().
+ * The reset function was added as an optimisation to avoid some copying/moving
+ * and to take ownership of an object already created on the heap.
+ *
+ * The class offers a strong guarantee of exception safety.
+ * With regards to the implementation, the private SwigSmartPointer nested class is
+ * a simple smart pointer providing exception safety, much like std::auto_ptr.
+ *
+ * This wrapping technique was suggested by William Fulton and is henceforth
+ * known as the "Fulton Transform" :-).
+ */
+
+#ifdef __cplusplus
+// Placed in the header section to ensure the language specific header files are
+// the first included headers and not <utility>
+%insert("header") %{
+#ifdef __cplusplus
+#include <utility>
+/* SwigValueWrapper is described in swig.swg */
+template<typename T> class SwigValueWrapper {
+ struct SwigSmartPointer {
+ T *ptr;
+ SwigSmartPointer(T *p) : ptr(p) { }
+ ~SwigSmartPointer() { delete ptr; }
+ SwigSmartPointer& operator=(SwigSmartPointer& rhs) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = rhs.ptr; rhs.ptr = 0; return *this; }
+ void reset(T *p) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = p; }
+ } pointer;
+ SwigValueWrapper& operator=(const SwigValueWrapper<T>& rhs);
+ SwigValueWrapper(const SwigValueWrapper<T>& rhs);
+public:
+ SwigValueWrapper() : pointer(0) { }
+ SwigValueWrapper& operator=(const T& t) { SwigSmartPointer tmp(new T(t)); pointer = tmp; return *this; }
+#if __cplusplus >=201103L
+ SwigValueWrapper& operator=(T&& t) { SwigSmartPointer tmp(new T(std::move(t))); pointer = tmp; return *this; }
+ operator T&&() const { return std::move(*pointer.ptr); }
+#else
+ operator T&() const { return *pointer.ptr; }
+#endif
+ T *operator&() const { return pointer.ptr; }
+ static void reset(SwigValueWrapper& t, T *p) { t.pointer.reset(p); }
+};
+
+/*
+ * SwigValueInit() is a generic initialisation solution as the following approach:
+ *
+ * T c_result = T();
+ *
+ * doesn't compile for all types for example:
+ *
+ * unsigned int c_result = unsigned int();
+ */
+template <typename T> T SwigValueInit() {
+ return T();
+}
+
+#if __cplusplus >=201103L
+# define SWIG_STD_MOVE(OBJ) std::move(OBJ)
+#else
+# define SWIG_STD_MOVE(OBJ) OBJ
+#endif
+
+#endif
+%}
+#endif
+
diff --git a/contrib/tools/swig/Lib/swigerrors.swg b/contrib/tools/swig/Lib/swigerrors.swg
new file mode 100644
index 0000000000..4d5a8e473d
--- /dev/null
+++ b/contrib/tools/swig/Lib/swigerrors.swg
@@ -0,0 +1,15 @@
+/* SWIG Errors applicable to all language modules, values are reserved from -1 to -99 */
+#define SWIG_UnknownError -1
+#define SWIG_IOError -2
+#define SWIG_RuntimeError -3
+#define SWIG_IndexError -4
+#define SWIG_TypeError -5
+#define SWIG_DivisionByZero -6
+#define SWIG_OverflowError -7
+#define SWIG_SyntaxError -8
+#define SWIG_ValueError -9
+#define SWIG_SystemError -10
+#define SWIG_AttributeError -11
+#define SWIG_MemoryError -12
+#define SWIG_NullReferenceError -13
+
diff --git a/contrib/tools/swig/Lib/swigfragments.swg b/contrib/tools/swig/Lib/swigfragments.swg
new file mode 100644
index 0000000000..28aa1180f9
--- /dev/null
+++ b/contrib/tools/swig/Lib/swigfragments.swg
@@ -0,0 +1,90 @@
+/* -----------------------------------------------------------------------------
+ * swigfragments.swg
+ *
+ * Common fragments
+ * ----------------------------------------------------------------------------- */
+
+/* -----------------------------------------------------------------------------
+ * Fragments for C header files
+ * ----------------------------------------------------------------------------- */
+
+%fragment("<float.h>", "header") %{
+#include <float.h>
+%}
+
+/* Default compiler options for gcc allow long_long but not LLONG_MAX.
+ * Define SWIG_NO_LLONG_MAX if this added limits support is not wanted. */
+%fragment("<limits.h>", "header") %{
+#include <limits.h>
+#if !defined(SWIG_NO_LLONG_MAX)
+# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__)
+# define LLONG_MAX __LONG_LONG_MAX__
+# define LLONG_MIN (-LLONG_MAX - 1LL)
+# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
+# endif
+#endif
+%}
+
+%fragment("<math.h>", "header") %{
+#include <math.h>
+%}
+
+%fragment("<string.h>", "header") %{
+#include <string.h>
+%}
+
+%fragment("<stddef.h>", "header") %{
+#include <stddef.h>
+%}
+
+%fragment("<stdio.h>", "header") %{
+#include <stdio.h>
+#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__BORLANDC__) || defined(_WATCOM)
+# ifndef snprintf
+# define snprintf _snprintf
+# endif
+#endif
+%}
+
+%fragment("<stdlib.h>", "header") %{
+#include <stdlib.h>
+#ifdef _MSC_VER
+# ifndef strtoull
+# define strtoull _strtoui64
+# endif
+# ifndef strtoll
+# define strtoll _strtoi64
+# endif
+#endif
+%}
+
+%fragment("<wchar.h>", "header") %{
+#include <wchar.h>
+#include <limits.h>
+#ifndef WCHAR_MIN
+# define WCHAR_MIN 0
+#endif
+#ifndef WCHAR_MAX
+# define WCHAR_MAX 65535
+#endif
+%}
+
+/* -----------------------------------------------------------------------------
+ * Fragments for C++ header files
+ * ----------------------------------------------------------------------------- */
+
+%fragment("<algorithm>", "header") %{
+#include <algorithm>
+%}
+
+%fragment("<stdexcept>", "header") %{
+#include <stdexcept>
+%}
+
+%fragment("<string>", "header") %{
+#include <string>
+%}
+
+%fragment("<memory>", "header") %{
+#include <memory>
+%}
diff --git a/contrib/tools/swig/Lib/swiginit.swg b/contrib/tools/swig/Lib/swiginit.swg
new file mode 100644
index 0000000000..e50b1b46dc
--- /dev/null
+++ b/contrib/tools/swig/Lib/swiginit.swg
@@ -0,0 +1,233 @@
+/* -----------------------------------------------------------------------------
+ * Type initialization:
+ * This problem is tough by the requirement that no dynamic
+ * memory is used. Also, since swig_type_info structures store pointers to
+ * swig_cast_info structures and swig_cast_info structures store pointers back
+ * to swig_type_info structures, we need some lookup code at initialization.
+ * The idea is that swig generates all the structures that are needed.
+ * The runtime then collects these partially filled structures.
+ * The SWIG_InitializeModule function takes these initial arrays out of
+ * swig_module, and does all the lookup, filling in the swig_module.types
+ * array with the correct data and linking the correct swig_cast_info
+ * structures together.
+ *
+ * The generated swig_type_info structures are assigned statically to an initial
+ * array. We just loop through that array, and handle each type individually.
+ * First we lookup if this type has been already loaded, and if so, use the
+ * loaded structure instead of the generated one. Then we have to fill in the
+ * cast linked list. The cast data is initially stored in something like a
+ * two-dimensional array. Each row corresponds to a type (there are the same
+ * number of rows as there are in the swig_type_initial array). Each entry in
+ * a column is one of the swig_cast_info structures for that type.
+ * The cast_initial array is actually an array of arrays, because each row has
+ * a variable number of columns. So to actually build the cast linked list,
+ * we find the array of casts associated with the type, and loop through it
+ * adding the casts to the list. The one last trick we need to do is making
+ * sure the type pointer in the swig_cast_info struct is correct.
+ *
+ * First off, we lookup the cast->type name to see if it is already loaded.
+ * There are three cases to handle:
+ * 1) If the cast->type has already been loaded AND the type we are adding
+ * casting info to has not been loaded (it is in this module), THEN we
+ * replace the cast->type pointer with the type pointer that has already
+ * been loaded.
+ * 2) If BOTH types (the one we are adding casting info to, and the
+ * cast->type) are loaded, THEN the cast info has already been loaded by
+ * the previous module so we just ignore it.
+ * 3) Finally, if cast->type has not already been loaded, then we add that
+ * swig_cast_info to the linked list (because the cast->type) pointer will
+ * be correct.
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* c-mode */
+#endif
+#endif
+
+#if 0
+#define SWIGRUNTIME_DEBUG
+#endif
+
+#ifndef SWIG_INIT_CLIENT_DATA_TYPE
+#define SWIG_INIT_CLIENT_DATA_TYPE void *
+#endif
+
+SWIGRUNTIME void
+SWIG_InitializeModule(SWIG_INIT_CLIENT_DATA_TYPE clientdata) {
+ size_t i;
+ swig_module_info *module_head, *iter;
+ int init;
+
+ /* check to see if the circular list has been setup, if not, set it up */
+ if (swig_module.next==0) {
+ /* Initialize the swig_module */
+ swig_module.type_initial = swig_type_initial;
+ swig_module.cast_initial = swig_cast_initial;
+ swig_module.next = &swig_module;
+ init = 1;
+ } else {
+ init = 0;
+ }
+
+ /* Try and load any already created modules */
+ module_head = SWIG_GetModule(clientdata);
+ if (!module_head) {
+ /* This is the first module loaded for this interpreter */
+ /* so set the swig module into the interpreter */
+ SWIG_SetModule(clientdata, &swig_module);
+ } else {
+ /* the interpreter has loaded a SWIG module, but has it loaded this one? */
+ iter=module_head;
+ do {
+ if (iter==&swig_module) {
+ /* Our module is already in the list, so there's nothing more to do. */
+ return;
+ }
+ iter=iter->next;
+ } while (iter!= module_head);
+
+ /* otherwise we must add our module into the list */
+ swig_module.next = module_head->next;
+ module_head->next = &swig_module;
+ }
+
+ /* When multiple interpreters are used, a module could have already been initialized in
+ a different interpreter, but not yet have a pointer in this interpreter.
+ In this case, we do not want to continue adding types... everything should be
+ set up already */
+ if (init == 0) return;
+
+ /* Now work on filling in swig_module.types */
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: size %lu\n", (unsigned long)swig_module.size);
+#endif
+ for (i = 0; i < swig_module.size; ++i) {
+ swig_type_info *type = 0;
+ swig_type_info *ret;
+ swig_cast_info *cast;
+
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: type %lu %s\n", (unsigned long)i, swig_module.type_initial[i]->name);
+#endif
+
+ /* if there is another module already loaded */
+ if (swig_module.next != &swig_module) {
+ type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name);
+ }
+ if (type) {
+ /* Overwrite clientdata field */
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: found type %s\n", type->name);
+#endif
+ if (swig_module.type_initial[i]->clientdata) {
+ type->clientdata = swig_module.type_initial[i]->clientdata;
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name);
+#endif
+ }
+ } else {
+ type = swig_module.type_initial[i];
+ }
+
+ /* Insert casting types */
+ cast = swig_module.cast_initial[i];
+ while (cast->type) {
+
+ /* Don't need to add information already in the list */
+ ret = 0;
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: look cast %s\n", cast->type->name);
+#endif
+ if (swig_module.next != &swig_module) {
+ ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name);
+#ifdef SWIGRUNTIME_DEBUG
+ if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name);
+#endif
+ }
+ if (ret) {
+ if (type == swig_module.type_initial[i]) {
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: skip old type %s\n", ret->name);
+#endif
+ cast->type = ret;
+ ret = 0;
+ } else {
+ /* Check for casting already in the list */
+ swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type);
+#ifdef SWIGRUNTIME_DEBUG
+ if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name);
+#endif
+ if (!ocast) ret = 0;
+ }
+ }
+
+ if (!ret) {
+#ifdef SWIGRUNTIME_DEBUG
+ printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name);
+#endif
+ if (type->cast) {
+ type->cast->prev = cast;
+ cast->next = type->cast;
+ }
+ type->cast = cast;
+ }
+ cast++;
+ }
+ /* Set entry in modules->types array equal to the type */
+ swig_module.types[i] = type;
+ }
+ swig_module.types[i] = 0;
+
+#ifdef SWIGRUNTIME_DEBUG
+ printf("**** SWIG_InitializeModule: Cast List ******\n");
+ for (i = 0; i < swig_module.size; ++i) {
+ int j = 0;
+ swig_cast_info *cast = swig_module.cast_initial[i];
+ printf("SWIG_InitializeModule: type %lu %s\n", (unsigned long)i, swig_module.type_initial[i]->name);
+ while (cast->type) {
+ printf("SWIG_InitializeModule: cast type %s\n", cast->type->name);
+ cast++;
+ ++j;
+ }
+ printf("---- Total casts: %d\n",j);
+ }
+ printf("**** SWIG_InitializeModule: Cast List ******\n");
+#endif
+}
+
+/* This function will propagate the clientdata field of type to
+* any new swig_type_info structures that have been added into the list
+* of equivalent types. It is like calling
+* SWIG_TypeClientData(type, clientdata) a second time.
+*/
+SWIGRUNTIME void
+SWIG_PropagateClientData(void) {
+ size_t i;
+ swig_cast_info *equiv;
+ static int init_run = 0;
+
+ if (init_run) return;
+ init_run = 1;
+
+ for (i = 0; i < swig_module.size; i++) {
+ if (swig_module.types[i]->clientdata) {
+ equiv = swig_module.types[i]->cast;
+ while (equiv) {
+ if (!equiv->converter) {
+ if (equiv->type && !equiv->type->clientdata)
+ SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata);
+ }
+ equiv = equiv->next;
+ }
+ }
+ }
+}
+
+#ifdef __cplusplus
+#if 0
+{ /* c-mode */
+#endif
+}
+#endif
diff --git a/contrib/tools/swig/Lib/swiglabels.swg b/contrib/tools/swig/Lib/swiglabels.swg
new file mode 100644
index 0000000000..b3855665e2
--- /dev/null
+++ b/contrib/tools/swig/Lib/swiglabels.swg
@@ -0,0 +1,123 @@
+/* -----------------------------------------------------------------------------
+ * This section contains generic SWIG labels for method/variable
+ * declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+# define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+# define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+# define SWIGINLINE inline
+# else
+# define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+# elif defined(__ICC)
+# define SWIGUNUSED __attribute__ ((__unused__))
+# else
+# define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+# pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+# define SWIGUNUSEDPARM(p)
+# else
+# define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if defined(__GNUC__)
+# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# ifndef GCC_HASCLASSVISIBILITY
+# define GCC_HASCLASSVISIBILITY
+# endif
+# endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# if defined(STATIC_LINKED)
+# define SWIGEXPORT
+# else
+# define SWIGEXPORT __declspec(dllexport)
+# endif
+# else
+# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+# define SWIGEXPORT __attribute__ ((visibility("default")))
+# else
+# define SWIGEXPORT
+# endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# define SWIGSTDCALL __stdcall
+# else
+# define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */
+#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES)
+# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
+#endif
+
+/* Intel's compiler complains if a variable which was never initialised is
+ * cast to void, which is a common idiom which we use to indicate that we
+ * are aware a variable isn't used. So we just silence that warning.
+ * See: https://github.com/swig/swig/issues/192 for more discussion.
+ */
+#ifdef __INTEL_COMPILER
+# pragma warning disable 592
+#endif
diff --git a/contrib/tools/swig/Lib/swigrun.swg b/contrib/tools/swig/Lib/swigrun.swg
new file mode 100644
index 0000000000..f632c4cb6d
--- /dev/null
+++ b/contrib/tools/swig/Lib/swigrun.swg
@@ -0,0 +1,581 @@
+/* -----------------------------------------------------------------------------
+ * swigrun.swg
+ *
+ * This file contains generic C API SWIG runtime support for pointer
+ * type checking.
+ * ----------------------------------------------------------------------------- */
+
+/* This should only be incremented when either the layout of swig_type_info changes,
+ or for whatever reason, the runtime changes incompatibly */
+#define SWIG_RUNTIME_VERSION "4"
+
+/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */
+#ifdef SWIG_TYPE_TABLE
+# define SWIG_QUOTE_STRING(x) #x
+# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x)
+# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE)
+#else
+# define SWIG_TYPE_TABLE_NAME
+#endif
+
+/*
+ You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for
+ creating a static or dynamic library from the SWIG runtime code.
+ In 99.9% of the cases, SWIG just needs to declare them as 'static'.
+
+ But only do this if strictly necessary, ie, if you have problems
+ with your compiler or suchlike.
+*/
+
+#ifndef SWIGRUNTIME
+# define SWIGRUNTIME SWIGINTERN
+#endif
+
+#ifndef SWIGRUNTIMEINLINE
+# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE
+#endif
+
+/* Generic buffer size */
+#ifndef SWIG_BUFFER_SIZE
+# define SWIG_BUFFER_SIZE 1024
+#endif
+
+/* Flags for pointer conversions */
+#define SWIG_POINTER_DISOWN 0x1
+#define SWIG_CAST_NEW_MEMORY 0x2
+#define SWIG_POINTER_NO_NULL 0x4
+#define SWIG_POINTER_CLEAR 0x8
+#define SWIG_POINTER_RELEASE (SWIG_POINTER_CLEAR | SWIG_POINTER_DISOWN)
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_OWN 0x1
+
+
+/*
+ Flags/methods for returning states.
+
+ The SWIG conversion methods, as ConvertPtr, return an integer
+ that tells if the conversion was successful or not. And if not,
+ an error code can be returned (see swigerrors.swg for the codes).
+
+ Use the following macros/flags to set or process the returning
+ states.
+
+ In old versions of SWIG, code such as the following was usually written:
+
+ if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) {
+ // success code
+ } else {
+ //fail code
+ }
+
+ Now you can be more explicit:
+
+ int res = SWIG_ConvertPtr(obj,vptr,ty.flags);
+ if (SWIG_IsOK(res)) {
+ // success code
+ } else {
+ // fail code
+ }
+
+ which is the same really, but now you can also do
+
+ Type *ptr;
+ int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags);
+ if (SWIG_IsOK(res)) {
+ // success code
+ if (SWIG_IsNewObj(res) {
+ ...
+ delete *ptr;
+ } else {
+ ...
+ }
+ } else {
+ // fail code
+ }
+
+ I.e., now SWIG_ConvertPtr can return new objects and you can
+ identify the case and take care of the deallocation. Of course that
+ also requires SWIG_ConvertPtr to return new result values, such as
+
+ int SWIG_ConvertPtr(obj, ptr,...) {
+ if (<obj is ok>) {
+ if (<need new object>) {
+ *ptr = <ptr to new allocated object>;
+ return SWIG_NEWOBJ;
+ } else {
+ *ptr = <ptr to old object>;
+ return SWIG_OLDOBJ;
+ }
+ } else {
+ return SWIG_BADOBJ;
+ }
+ }
+
+ Of course, returning the plain '0(success)/-1(fail)' still works, but you can be
+ more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the
+ SWIG errors code.
+
+ Finally, if the SWIG_CASTRANK_MODE is enabled, the result code
+ allows returning the 'cast rank', for example, if you have this
+
+ int food(double)
+ int fooi(int);
+
+ and you call
+
+ food(1) // cast rank '1' (1 -> 1.0)
+ fooi(1) // cast rank '0'
+
+ just use the SWIG_AddCast()/SWIG_CheckState()
+*/
+
+#define SWIG_OK (0)
+/* Runtime errors are < 0 */
+#define SWIG_ERROR (-1)
+/* Errors in range -1 to -99 are in swigerrors.swg (errors for all languages including those not using the runtime) */
+/* Errors in range -100 to -199 are language specific errors defined in *errors.swg */
+/* Errors < -200 are generic runtime specific errors */
+#define SWIG_ERROR_RELEASE_NOT_OWNED (-200)
+
+#define SWIG_IsOK(r) (r >= 0)
+#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError)
+
+/* The CastRankLimit says how many bits are used for the cast rank */
+#define SWIG_CASTRANKLIMIT (1 << 8)
+/* The NewMask denotes the object was created (using new/malloc) */
+#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1)
+/* The TmpMask is for in/out typemaps that use temporal objects */
+#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1)
+/* Simple returning values */
+#define SWIG_BADOBJ (SWIG_ERROR)
+#define SWIG_OLDOBJ (SWIG_OK)
+#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK)
+#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK)
+/* Check, add and del object mask methods */
+#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r)
+#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r)
+#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK))
+#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r)
+#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r)
+#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK))
+
+/* Cast-Rank Mode */
+#if defined(SWIG_CASTRANK_MODE)
+# ifndef SWIG_TypeRank
+# define SWIG_TypeRank unsigned long
+# endif
+# ifndef SWIG_MAXCASTRANK /* Default cast allowed */
+# define SWIG_MAXCASTRANK (2)
+# endif
+# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1)
+# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK)
+SWIGINTERNINLINE int SWIG_AddCast(int r) {
+ return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r;
+}
+SWIGINTERNINLINE int SWIG_CheckState(int r) {
+ return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0;
+}
+#else /* no cast-rank mode */
+# define SWIG_AddCast(r) (r)
+# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0)
+#endif
+
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *(*swig_converter_func)(void *, int *);
+typedef struct swig_type_info *(*swig_dycast_func)(void **);
+
+/* Structure to store information on one type */
+typedef struct swig_type_info {
+ const char *name; /* mangled name of this type */
+ const char *str; /* human readable name of this type */
+ swig_dycast_func dcast; /* dynamic cast function down a hierarchy */
+ struct swig_cast_info *cast; /* linked list of types that can cast into this type */
+ void *clientdata; /* language specific type data */
+ int owndata; /* flag if the structure owns the clientdata */
+} swig_type_info;
+
+/* Structure to store a type and conversion function used for casting */
+typedef struct swig_cast_info {
+ swig_type_info *type; /* pointer to type that is equivalent to this type */
+ swig_converter_func converter; /* function to cast the void pointers */
+ struct swig_cast_info *next; /* pointer to next cast in linked list */
+ struct swig_cast_info *prev; /* pointer to the previous cast */
+} swig_cast_info;
+
+/* Structure used to store module information
+ * Each module generates one structure like this, and the runtime collects
+ * all of these structures and stores them in a circularly linked list.*/
+typedef struct swig_module_info {
+ swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */
+ size_t size; /* Number of types in this module */
+ struct swig_module_info *next; /* Pointer to next element in circularly linked list */
+ swig_type_info **type_initial; /* Array of initially generated type structures */
+ swig_cast_info **cast_initial; /* Array of initially generated casting structures */
+ void *clientdata; /* Language specific module data */
+} swig_module_info;
+
+/*
+ Compare two type names skipping the space characters, therefore
+ "char*" == "char *" and "Class<int>" == "Class<int >", etc.
+
+ Return 0 when the two name types are equivalent, as in
+ strncmp, but skipping ' '.
+*/
+SWIGRUNTIME int
+SWIG_TypeNameComp(const char *f1, const char *l1,
+ const char *f2, const char *l2) {
+ for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) {
+ while ((*f1 == ' ') && (f1 != l1)) ++f1;
+ while ((*f2 == ' ') && (f2 != l2)) ++f2;
+ if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1;
+ }
+ return (int)((l1 - f1) - (l2 - f2));
+}
+
+/*
+ Check type equivalence in a name list like <name1>|<name2>|...
+ Return 0 if equal, -1 if nb < tb, 1 if nb > tb
+*/
+SWIGRUNTIME int
+SWIG_TypeCmp(const char *nb, const char *tb) {
+ int equiv = 1;
+ const char* te = tb + strlen(tb);
+ const char* ne = nb;
+ while (equiv != 0 && *ne) {
+ for (nb = ne; *ne; ++ne) {
+ if (*ne == '|') break;
+ }
+ equiv = SWIG_TypeNameComp(nb, ne, tb, te);
+ if (*ne) ++ne;
+ }
+ return equiv;
+}
+
+/*
+ Check type equivalence in a name list like <name1>|<name2>|...
+ Return 0 if not equal, 1 if equal
+*/
+SWIGRUNTIME int
+SWIG_TypeEquiv(const char *nb, const char *tb) {
+ return SWIG_TypeCmp(nb, tb) == 0 ? 1 : 0;
+}
+
+/*
+ Check the typename
+*/
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheck(const char *c, swig_type_info *ty) {
+ if (ty) {
+ swig_cast_info *iter = ty->cast;
+ while (iter) {
+ if (strcmp(iter->type->name, c) == 0) {
+ if (iter == ty->cast)
+ return iter;
+ /* Move iter to the top of the linked list */
+ iter->prev->next = iter->next;
+ if (iter->next)
+ iter->next->prev = iter->prev;
+ iter->next = ty->cast;
+ iter->prev = 0;
+ if (ty->cast) ty->cast->prev = iter;
+ ty->cast = iter;
+ return iter;
+ }
+ iter = iter->next;
+ }
+ }
+ return 0;
+}
+
+/*
+ Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison
+*/
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheckStruct(const swig_type_info *from, swig_type_info *ty) {
+ if (ty) {
+ swig_cast_info *iter = ty->cast;
+ while (iter) {
+ if (iter->type == from) {
+ if (iter == ty->cast)
+ return iter;
+ /* Move iter to the top of the linked list */
+ iter->prev->next = iter->next;
+ if (iter->next)
+ iter->next->prev = iter->prev;
+ iter->next = ty->cast;
+ iter->prev = 0;
+ if (ty->cast) ty->cast->prev = iter;
+ ty->cast = iter;
+ return iter;
+ }
+ iter = iter->next;
+ }
+ }
+ return 0;
+}
+
+/*
+ Cast a pointer up an inheritance hierarchy
+*/
+SWIGRUNTIMEINLINE void *
+SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) {
+ return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory);
+}
+
+/*
+ Dynamic pointer casting. Down an inheritance hierarchy
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) {
+ swig_type_info *lastty = ty;
+ if (!ty || !ty->dcast) return ty;
+ while (ty && (ty->dcast)) {
+ ty = (*ty->dcast)(ptr);
+ if (ty) lastty = ty;
+ }
+ return lastty;
+}
+
+/*
+ Return the name associated with this type
+*/
+SWIGRUNTIMEINLINE const char *
+SWIG_TypeName(const swig_type_info *ty) {
+ return ty->name;
+}
+
+/*
+ Return the pretty name associated with this type,
+ that is an unmangled type name in a form presentable to the user.
+*/
+SWIGRUNTIME const char *
+SWIG_TypePrettyName(const swig_type_info *type) {
+ /* The "str" field contains the equivalent pretty names of the
+ type, separated by vertical-bar characters. Choose the last
+ name. It should be the most specific; a fully resolved name
+ but not necessarily with default template parameters expanded. */
+ if (!type) return NULL;
+ if (type->str != NULL) {
+ const char *last_name = type->str;
+ const char *s;
+ for (s = type->str; *s; s++)
+ if (*s == '|') last_name = s+1;
+ return last_name;
+ }
+ else
+ return type->name;
+}
+
+/*
+ Set the clientdata field for a type
+*/
+SWIGRUNTIME void
+SWIG_TypeClientData(swig_type_info *ti, void *clientdata) {
+ swig_cast_info *cast = ti->cast;
+ /* if (ti->clientdata == clientdata) return; */
+ ti->clientdata = clientdata;
+
+ while (cast) {
+ if (!cast->converter) {
+ swig_type_info *tc = cast->type;
+ if (!tc->clientdata) {
+ SWIG_TypeClientData(tc, clientdata);
+ }
+ }
+ cast = cast->next;
+ }
+}
+SWIGRUNTIME void
+SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) {
+ SWIG_TypeClientData(ti, clientdata);
+ ti->owndata = 1;
+}
+
+/*
+ Search for a swig_type_info structure only by mangled name
+ Search is a O(log #types)
+
+ We start searching at module start, and finish searching when start == end.
+ Note: if start == end at the beginning of the function, we go all the way around
+ the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_MangledTypeQueryModule(swig_module_info *start,
+ swig_module_info *end,
+ const char *name) {
+ swig_module_info *iter = start;
+ do {
+ if (iter->size) {
+ size_t l = 0;
+ size_t r = iter->size - 1;
+ do {
+ /* since l+r >= 0, we can (>> 1) instead (/ 2) */
+ size_t i = (l + r) >> 1;
+ const char *iname = iter->types[i]->name;
+ if (iname) {
+ int compare = strcmp(name, iname);
+ if (compare == 0) {
+ return iter->types[i];
+ } else if (compare < 0) {
+ if (i) {
+ r = i - 1;
+ } else {
+ break;
+ }
+ } else if (compare > 0) {
+ l = i + 1;
+ }
+ } else {
+ break; /* should never happen */
+ }
+ } while (l <= r);
+ }
+ iter = iter->next;
+ } while (iter != end);
+ return 0;
+}
+
+/*
+ Search for a swig_type_info structure for either a mangled name or a human readable name.
+ It first searches the mangled names of the types, which is a O(log #types)
+ If a type is not found it then searches the human readable names, which is O(#types).
+
+ We start searching at module start, and finish searching when start == end.
+ Note: if start == end at the beginning of the function, we go all the way around
+ the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeQueryModule(swig_module_info *start,
+ swig_module_info *end,
+ const char *name) {
+ /* STEP 1: Search the name field using binary search */
+ swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name);
+ if (ret) {
+ return ret;
+ } else {
+ /* STEP 2: If the type hasn't been found, do a complete search
+ of the str field (the human readable name) */
+ swig_module_info *iter = start;
+ do {
+ size_t i = 0;
+ for (; i < iter->size; ++i) {
+ if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name)))
+ return iter->types[i];
+ }
+ iter = iter->next;
+ } while (iter != end);
+ }
+
+ /* neither found a match */
+ return 0;
+}
+
+/*
+ Pack binary data into a string
+*/
+SWIGRUNTIME char *
+SWIG_PackData(char *c, void *ptr, size_t sz) {
+ static const char hex[17] = "0123456789abcdef";
+ const unsigned char *u = (unsigned char *) ptr;
+ const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ unsigned char uu = *u;
+ *(c++) = hex[(uu & 0xf0) >> 4];
+ *(c++) = hex[uu & 0xf];
+ }
+ return c;
+}
+
+/*
+ Unpack binary data from a string
+*/
+SWIGRUNTIME const char *
+SWIG_UnpackData(const char *c, void *ptr, size_t sz) {
+ unsigned char *u = (unsigned char *) ptr;
+ const unsigned char *eu = u + sz;
+ for (; u != eu; ++u) {
+ char d = *(c++);
+ unsigned char uu;
+ if ((d >= '0') && (d <= '9'))
+ uu = (unsigned char)((d - '0') << 4);
+ else if ((d >= 'a') && (d <= 'f'))
+ uu = (unsigned char)((d - ('a'-10)) << 4);
+ else
+ return (char *) 0;
+ d = *(c++);
+ if ((d >= '0') && (d <= '9'))
+ uu |= (unsigned char)(d - '0');
+ else if ((d >= 'a') && (d <= 'f'))
+ uu |= (unsigned char)(d - ('a'-10));
+ else
+ return (char *) 0;
+ *u = uu;
+ }
+ return c;
+}
+
+/*
+ Pack 'void *' into a string buffer.
+*/
+SWIGRUNTIME char *
+SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) {
+ char *r = buff;
+ if ((2*sizeof(void *) + 2) > bsz) return 0;
+ *(r++) = '_';
+ r = SWIG_PackData(r,&ptr,sizeof(void *));
+ if (strlen(name) + 1 > (bsz - (r - buff))) return 0;
+ strcpy(r,name);
+ return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) {
+ if (*c != '_') {
+ if (strcmp(c,"NULL") == 0) {
+ *ptr = (void *) 0;
+ return name;
+ } else {
+ return 0;
+ }
+ }
+ return SWIG_UnpackData(++c,ptr,sizeof(void *));
+}
+
+SWIGRUNTIME char *
+SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) {
+ char *r = buff;
+ size_t lname = (name ? strlen(name) : 0);
+ if ((2*sz + 2 + lname) > bsz) return 0;
+ *(r++) = '_';
+ r = SWIG_PackData(r,ptr,sz);
+ if (lname) {
+ strncpy(r,name,lname+1);
+ } else {
+ *r = 0;
+ }
+ return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) {
+ if (*c != '_') {
+ if (strcmp(c,"NULL") == 0) {
+ memset(ptr,0,sz);
+ return name;
+ } else {
+ return 0;
+ }
+ }
+ return SWIG_UnpackData(++c,ptr,sz);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/contrib/tools/swig/Lib/swigwarn.swg b/contrib/tools/swig/Lib/swigwarn.swg
new file mode 100644
index 0000000000..6a069220ef
--- /dev/null
+++ b/contrib/tools/swig/Lib/swigwarn.swg
@@ -0,0 +1,300 @@
+/* SWIG warning codes - generated from swigwarn.h - do not edit */
+
+
+%define SWIGWARN_NONE 0 %enddef
+
+/* -- Deprecated features -- */
+
+%define SWIGWARN_DEPRECATED_EXTERN 101 %enddef
+%define SWIGWARN_DEPRECATED_VAL 102 %enddef
+%define SWIGWARN_DEPRECATED_OUT 103 %enddef
+%define SWIGWARN_DEPRECATED_DISABLEDOC 104 %enddef
+%define SWIGWARN_DEPRECATED_ENABLEDOC 105 %enddef
+%define SWIGWARN_DEPRECATED_DOCONLY 106 %enddef
+%define SWIGWARN_DEPRECATED_STYLE 107 %enddef
+%define SWIGWARN_DEPRECATED_LOCALSTYLE 108 %enddef
+%define SWIGWARN_DEPRECATED_TITLE 109 %enddef
+%define SWIGWARN_DEPRECATED_SECTION 110 %enddef
+%define SWIGWARN_DEPRECATED_SUBSECTION 111 %enddef
+%define SWIGWARN_DEPRECATED_SUBSUBSECTION 112 %enddef
+%define SWIGWARN_DEPRECATED_ADDMETHODS 113 %enddef
+%define SWIGWARN_DEPRECATED_READONLY 114 %enddef
+%define SWIGWARN_DEPRECATED_READWRITE 115 %enddef
+%define SWIGWARN_DEPRECATED_EXCEPT 116 %enddef
+%define SWIGWARN_DEPRECATED_NEW 117 %enddef
+%define SWIGWARN_DEPRECATED_EXCEPT_TM 118 %enddef
+%define SWIGWARN_DEPRECATED_IGNORE_TM 119 %enddef
+%define SWIGWARN_DEPRECATED_OPTC 120 %enddef
+%define SWIGWARN_DEPRECATED_NAME 121 %enddef
+%define SWIGWARN_DEPRECATED_NOEXTERN 122 %enddef
+%define SWIGWARN_DEPRECATED_NODEFAULT 123 %enddef
+/* Unused since 4.1.0: #define WARN_DEPRECATED_TYPEMAP_LANG 124 */
+%define SWIGWARN_DEPRECATED_INPUT_FILE 125 %enddef
+%define SWIGWARN_DEPRECATED_NESTED_WORKAROUND 126 %enddef
+
+/* -- Preprocessor -- */
+
+%define SWIGWARN_PP_MISSING_FILE 201 %enddef
+%define SWIGWARN_PP_EVALUATION 202 %enddef
+%define SWIGWARN_PP_INCLUDEALL_IMPORTALL 203 %enddef
+%define SWIGWARN_PP_CPP_WARNING 204 %enddef
+%define SWIGWARN_PP_CPP_ERROR 205 %enddef
+%define SWIGWARN_PP_UNEXPECTED_TOKENS 206 %enddef
+
+/* -- C/C++ Parser -- */
+
+%define SWIGWARN_PARSE_CLASS_KEYWORD 301 %enddef
+%define SWIGWARN_PARSE_REDEFINED 302 %enddef
+%define SWIGWARN_PARSE_EXTEND_UNDEF 303 %enddef
+%define SWIGWARN_PARSE_UNSUPPORTED_VALUE 304 %enddef
+%define SWIGWARN_PARSE_BAD_VALUE 305 %enddef
+%define SWIGWARN_PARSE_PRIVATE 306 %enddef
+%define SWIGWARN_PARSE_BAD_DEFAULT 307 %enddef
+%define SWIGWARN_PARSE_NAMESPACE_ALIAS 308 %enddef
+%define SWIGWARN_PARSE_PRIVATE_INHERIT 309 %enddef
+%define SWIGWARN_PARSE_TEMPLATE_REPEAT 310 %enddef
+%define SWIGWARN_PARSE_TEMPLATE_PARTIAL 311 %enddef
+%define SWIGWARN_PARSE_UNNAMED_NESTED_CLASS 312 %enddef
+%define SWIGWARN_PARSE_UNDEFINED_EXTERN 313 %enddef
+%define SWIGWARN_PARSE_KEYWORD 314 %enddef
+%define SWIGWARN_PARSE_USING_UNDEF 315 %enddef
+%define SWIGWARN_PARSE_MODULE_REPEAT 316 %enddef
+%define SWIGWARN_PARSE_TEMPLATE_SP_UNDEF 317 %enddef
+%define SWIGWARN_PARSE_TEMPLATE_AMBIG 318 %enddef
+%define SWIGWARN_PARSE_NO_ACCESS 319 %enddef
+%define SWIGWARN_PARSE_EXPLICIT_TEMPLATE 320 %enddef
+%define SWIGWARN_PARSE_BUILTIN_NAME 321 %enddef
+%define SWIGWARN_PARSE_REDUNDANT 322 %enddef
+%define SWIGWARN_PARSE_REC_INHERITANCE 323 %enddef
+%define SWIGWARN_PARSE_NESTED_TEMPLATE 324 %enddef
+%define SWIGWARN_PARSE_NAMED_NESTED_CLASS 325 %enddef
+%define SWIGWARN_PARSE_EXTEND_NAME 326 %enddef
+%define SWIGWARN_PARSE_EXTERN_TEMPLATE 327 %enddef
+
+%define SWIGWARN_CPP11_LAMBDA 340 %enddef
+%define SWIGWARN_CPP11_ALIAS_DECLARATION 341 %enddef /* redundant now */
+%define SWIGWARN_CPP11_ALIAS_TEMPLATE 342 %enddef /* redundant now */
+%define SWIGWARN_CPP11_VARIADIC_TEMPLATE 343 %enddef
+
+%define SWIGWARN_IGNORE_OPERATOR_NEW 350 %enddef /* new */
+%define SWIGWARN_IGNORE_OPERATOR_DELETE 351 %enddef /* delete */
+%define SWIGWARN_IGNORE_OPERATOR_PLUS 352 %enddef /* + */
+%define SWIGWARN_IGNORE_OPERATOR_MINUS 353 %enddef /* - */
+%define SWIGWARN_IGNORE_OPERATOR_MUL 354 %enddef /* * */
+%define SWIGWARN_IGNORE_OPERATOR_DIV 355 %enddef /* / */
+%define SWIGWARN_IGNORE_OPERATOR_MOD 356 %enddef /* % */
+%define SWIGWARN_IGNORE_OPERATOR_XOR 357 %enddef /* ^ */
+%define SWIGWARN_IGNORE_OPERATOR_AND 358 %enddef /* & */
+%define SWIGWARN_IGNORE_OPERATOR_OR 359 %enddef /* | */
+%define SWIGWARN_IGNORE_OPERATOR_NOT 360 %enddef /* ~ */
+%define SWIGWARN_IGNORE_OPERATOR_LNOT 361 %enddef /* ! */
+%define SWIGWARN_IGNORE_OPERATOR_EQ 362 %enddef /* = */
+%define SWIGWARN_IGNORE_OPERATOR_LT 363 %enddef /* < */
+%define SWIGWARN_IGNORE_OPERATOR_GT 364 %enddef /* > */
+%define SWIGWARN_IGNORE_OPERATOR_PLUSEQ 365 %enddef /* += */
+%define SWIGWARN_IGNORE_OPERATOR_MINUSEQ 366 %enddef /* -= */
+%define SWIGWARN_IGNORE_OPERATOR_MULEQ 367 %enddef /* *= */
+%define SWIGWARN_IGNORE_OPERATOR_DIVEQ 368 %enddef /* /= */
+%define SWIGWARN_IGNORE_OPERATOR_MODEQ 369 %enddef /* %= */
+%define SWIGWARN_IGNORE_OPERATOR_XOREQ 370 %enddef /* ^= */
+%define SWIGWARN_IGNORE_OPERATOR_ANDEQ 371 %enddef /* &= */
+%define SWIGWARN_IGNORE_OPERATOR_OREQ 372 %enddef /* |= */
+%define SWIGWARN_IGNORE_OPERATOR_LSHIFT 373 %enddef /* << */
+%define SWIGWARN_IGNORE_OPERATOR_RSHIFT 374 %enddef /* >> */
+%define SWIGWARN_IGNORE_OPERATOR_LSHIFTEQ 375 %enddef /* <<= */
+%define SWIGWARN_IGNORE_OPERATOR_RSHIFTEQ 376 %enddef /* >>= */
+%define SWIGWARN_IGNORE_OPERATOR_EQUALTO 377 %enddef /* == */
+%define SWIGWARN_IGNORE_OPERATOR_NOTEQUAL 378 %enddef /* != */
+%define SWIGWARN_IGNORE_OPERATOR_LTEQUAL 379 %enddef /* <= */
+%define SWIGWARN_IGNORE_OPERATOR_GTEQUAL 380 %enddef /* >= */
+%define SWIGWARN_IGNORE_OPERATOR_LAND 381 %enddef /* && */
+%define SWIGWARN_IGNORE_OPERATOR_LOR 382 %enddef /* || */
+%define SWIGWARN_IGNORE_OPERATOR_PLUSPLUS 383 %enddef /* ++ */
+%define SWIGWARN_IGNORE_OPERATOR_MINUSMINUS 384 %enddef /* -- */
+%define SWIGWARN_IGNORE_OPERATOR_COMMA 385 %enddef /* , */
+%define SWIGWARN_IGNORE_OPERATOR_ARROWSTAR 386 %enddef /* ->* */
+%define SWIGWARN_IGNORE_OPERATOR_ARROW 387 %enddef /* -> */
+%define SWIGWARN_IGNORE_OPERATOR_CALL 388 %enddef /* () */
+%define SWIGWARN_IGNORE_OPERATOR_INDEX 389 %enddef /* [] */
+%define SWIGWARN_IGNORE_OPERATOR_UPLUS 390 %enddef /* + */
+%define SWIGWARN_IGNORE_OPERATOR_UMINUS 391 %enddef /* - */
+%define SWIGWARN_IGNORE_OPERATOR_UMUL 392 %enddef /* * */
+%define SWIGWARN_IGNORE_OPERATOR_UAND 393 %enddef /* & */
+%define SWIGWARN_IGNORE_OPERATOR_NEWARR 394 %enddef /* new [] */
+%define SWIGWARN_IGNORE_OPERATOR_DELARR 395 %enddef /* delete [] */
+%define SWIGWARN_IGNORE_OPERATOR_REF 396 %enddef /* operator *() */
+%define SWIGWARN_IGNORE_OPERATOR_LTEQUALGT 397 %enddef /* <=> */
+
+/* please leave 350-399 free for WARN_IGNORE_OPERATOR_* */
+
+/* -- Type system and typemaps -- */
+
+%define SWIGWARN_TYPE_UNDEFINED_CLASS 401 %enddef
+%define SWIGWARN_TYPE_INCOMPLETE 402 %enddef
+%define SWIGWARN_TYPE_ABSTRACT 403 %enddef
+%define SWIGWARN_TYPE_REDEFINED 404 %enddef
+%define SWIGWARN_TYPE_RVALUE_REF_QUALIFIER_IGNORED 405 %enddef
+
+%define SWIGWARN_TYPEMAP_SOURCETARGET 450 %enddef /* No longer issued */
+%define SWIGWARN_TYPEMAP_CHARLEAK 451 %enddef
+%define SWIGWARN_TYPEMAP_SWIGTYPE 452 %enddef /* No longer issued */
+%define SWIGWARN_TYPEMAP_APPLY_UNDEF 453 %enddef
+%define SWIGWARN_TYPEMAP_SWIGTYPELEAK 454 %enddef
+%define SWIGWARN_TYPEMAP_WCHARLEAK 455 %enddef
+
+%define SWIGWARN_TYPEMAP_IN_UNDEF 460 %enddef
+%define SWIGWARN_TYPEMAP_OUT_UNDEF 461 %enddef
+%define SWIGWARN_TYPEMAP_VARIN_UNDEF 462 %enddef
+%define SWIGWARN_TYPEMAP_VAROUT_UNDEF 463 %enddef
+%define SWIGWARN_TYPEMAP_CONST_UNDEF 464 %enddef
+%define SWIGWARN_TYPEMAP_UNDEF 465 %enddef
+%define SWIGWARN_TYPEMAP_VAR_UNDEF 466 %enddef
+%define SWIGWARN_TYPEMAP_TYPECHECK 467 %enddef
+%define SWIGWARN_TYPEMAP_THROW 468 %enddef
+%define SWIGWARN_TYPEMAP_DIRECTORIN_UNDEF 469 %enddef
+%define SWIGWARN_TYPEMAP_THREAD_UNSAFE 470 %enddef /* mostly used in directorout typemaps */
+%define SWIGWARN_TYPEMAP_DIRECTOROUT_UNDEF 471 %enddef
+%define SWIGWARN_TYPEMAP_TYPECHECK_UNDEF 472 %enddef
+%define SWIGWARN_TYPEMAP_DIRECTOROUT_PTR 473 %enddef
+%define SWIGWARN_TYPEMAP_OUT_OPTIMAL_IGNORED 474 %enddef
+%define SWIGWARN_TYPEMAP_OUT_OPTIMAL_MULTIPLE 475 %enddef
+%define SWIGWARN_TYPEMAP_INITIALIZER_LIST 476 %enddef
+%define SWIGWARN_TYPEMAP_DIRECTORTHROWS_UNDEF 477 %enddef
+
+/* -- Fragments -- */
+%define SWIGWARN_FRAGMENT_NOT_FOUND 490 %enddef
+
+/* -- General code generation -- */
+
+%define SWIGWARN_LANG_OVERLOAD_DECL 501 %enddef
+%define SWIGWARN_LANG_OVERLOAD_CONSTRUCT 502 %enddef
+%define SWIGWARN_LANG_IDENTIFIER 503 %enddef
+%define SWIGWARN_LANG_RETURN_TYPE 504 %enddef
+%define SWIGWARN_LANG_VARARGS 505 %enddef
+%define SWIGWARN_LANG_VARARGS_KEYWORD 506 %enddef
+%define SWIGWARN_LANG_NATIVE_UNIMPL 507 %enddef
+%define SWIGWARN_LANG_DEREF_SHADOW 508 %enddef
+%define SWIGWARN_LANG_OVERLOAD_SHADOW 509 %enddef
+%define SWIGWARN_LANG_FRIEND_IGNORE 510 %enddef
+%define SWIGWARN_LANG_OVERLOAD_KEYWORD 511 %enddef
+%define SWIGWARN_LANG_OVERLOAD_CONST 512 %enddef
+%define SWIGWARN_LANG_CLASS_UNNAMED 513 %enddef
+%define SWIGWARN_LANG_DIRECTOR_VDESTRUCT 514 %enddef
+%define SWIGWARN_LANG_DISCARD_CONST 515 %enddef
+%define SWIGWARN_LANG_OVERLOAD_IGNORED 516 %enddef
+%define SWIGWARN_LANG_DIRECTOR_ABSTRACT 517 %enddef
+%define SWIGWARN_LANG_PORTABILITY_FILENAME 518 %enddef
+%define SWIGWARN_LANG_TEMPLATE_METHOD_IGNORE 519 %enddef
+%define SWIGWARN_LANG_SMARTPTR_MISSING 520 %enddef
+%define SWIGWARN_LANG_ILLEGAL_DESTRUCTOR 521 %enddef
+%define SWIGWARN_LANG_EXTEND_CONSTRUCTOR 522 %enddef
+%define SWIGWARN_LANG_EXTEND_DESTRUCTOR 523 %enddef
+%define SWIGWARN_LANG_EXPERIMENTAL 524 %enddef
+%define SWIGWARN_LANG_DIRECTOR_FINAL 525 %enddef
+%define SWIGWARN_LANG_USING_NAME_DIFFERENT 526 %enddef
+
+/* -- Doxygen comments -- */
+
+%define SWIGWARN_DOXYGEN_UNKNOWN_COMMAND 560 %enddef
+%define SWIGWARN_DOXYGEN_UNEXPECTED_END_OF_COMMENT 561 %enddef
+%define SWIGWARN_DOXYGEN_COMMAND_EXPECTED 562 %enddef
+%define SWIGWARN_DOXYGEN_HTML_ERROR 563 %enddef
+%define SWIGWARN_DOXYGEN_COMMAND_ERROR 564 %enddef
+%define SWIGWARN_DOXYGEN_UNKNOWN_CHARACTER 565 %enddef
+%define SWIGWARN_DOXYGEN_UNEXPECTED_ITERATOR_VALUE 566 %enddef
+
+/* -- Reserved (600-699) -- */
+
+/* -- Language module specific warnings (700 - 899) -- */
+
+
+%define SWIGWARN_D_TYPEMAP_CTYPE_UNDEF 700 %enddef
+%define SWIGWARN_D_TYPEMAP_IMTYPE_UNDEF 701 %enddef
+%define SWIGWARN_D_TYPEMAP_DTYPE_UNDEF 702 %enddef
+%define SWIGWARN_D_MULTIPLE_INHERITANCE 703 %enddef
+%define SWIGWARN_D_TYPEMAP_CLASSMOD_UNDEF 704 %enddef
+%define SWIGWARN_D_TYPEMAP_DBODY_UNDEF 705 %enddef
+%define SWIGWARN_D_TYPEMAP_DOUT_UNDEF 706 %enddef
+%define SWIGWARN_D_TYPEMAP_DIN_UNDEF 707 %enddef
+%define SWIGWARN_D_TYPEMAP_DDIRECTORIN_UNDEF 708 %enddef
+%define SWIGWARN_D_TYPEMAP_DCONSTRUCTOR_UNDEF 709 %enddef
+%define SWIGWARN_D_EXCODE_MISSING 710 %enddef
+%define SWIGWARN_D_CANTHROW_MISSING 711 %enddef
+%define SWIGWARN_D_NO_DIRECTORCONNECT_ATTR 712 %enddef
+%define SWIGWARN_D_NAME_COLLISION 713 %enddef
+
+/* please leave 700-719 free for D */
+
+%define SWIGWARN_SCILAB_TRUNCATED_NAME 720 %enddef
+
+/* please leave 720-739 free for Scilab */
+
+%define SWIGWARN_PYTHON_INDENT_MISMATCH 740 %enddef
+
+/* please leave 740-749 free for Python */
+
+%define SWIGWARN_R_MISSING_RTYPECHECK_TYPEMAP 750 %enddef
+
+/* please leave 750-759 free for R */
+
+%define SWIGWARN_RUBY_WRONG_NAME 801 %enddef
+%define SWIGWARN_RUBY_MULTIPLE_INHERITANCE 802 %enddef
+
+/* please leave 800-809 free for Ruby */
+
+%define SWIGWARN_JAVA_TYPEMAP_JNI_UNDEF 810 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_JTYPE_UNDEF 811 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_JSTYPE_UNDEF 812 %enddef
+%define SWIGWARN_JAVA_MULTIPLE_INHERITANCE 813 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_GETCPTR_UNDEF 814 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_CLASSMOD_UNDEF 815 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_JAVABODY_UNDEF 816 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_JAVAOUT_UNDEF 817 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_JAVAIN_UNDEF 818 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_JAVADIRECTORIN_UNDEF 819 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_JAVADIRECTOROUT_UNDEF 820 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_INTERFACECODE_UNDEF 821 %enddef
+%define SWIGWARN_JAVA_COVARIANT_RET 822 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_JAVACONSTRUCT_UNDEF 823 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_DIRECTORIN_NODESC 824 %enddef
+%define SWIGWARN_JAVA_NO_DIRECTORCONNECT_ATTR 825 %enddef
+%define SWIGWARN_JAVA_NSPACE_WITHOUT_PACKAGE 826 %enddef
+%define SWIGWARN_JAVA_TYPEMAP_INTERFACEMODIFIERS_UNDEF 827 %enddef
+
+/* please leave 810-829 free for Java */
+
+%define SWIGWARN_CSHARP_TYPEMAP_CTYPE_UNDEF 830 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CSTYPE_UNDEF 831 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF 832 %enddef
+%define SWIGWARN_CSHARP_MULTIPLE_INHERITANCE 833 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_GETCPTR_UNDEF 834 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CLASSMOD_UNDEF 835 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CSBODY_UNDEF 836 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CSOUT_UNDEF 837 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CSIN_UNDEF 838 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CSDIRECTORIN_UNDEF 839 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CSDIRECTOROUT_UNDEF 840 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_INTERFACECODE_UNDEF 841 %enddef
+%define SWIGWARN_CSHARP_COVARIANT_RET 842 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_CSCONSTRUCT_UNDEF 843 %enddef
+%define SWIGWARN_CSHARP_EXCODE 844 %enddef
+%define SWIGWARN_CSHARP_CANTHROW 845 %enddef
+%define SWIGWARN_CSHARP_NO_DIRECTORCONNECT_ATTR 846 %enddef
+%define SWIGWARN_CSHARP_TYPEMAP_INTERFACEMODIFIERS_UNDEF 847 %enddef
+
+/* please leave 830-849 free for C# */
+
+/* 850-860 were used by Modula 3 (removed in SWIG 4.1.0) - avoid reusing for now */
+
+%define SWIGWARN_PHP_MULTIPLE_INHERITANCE 870 %enddef
+%define SWIGWARN_PHP_UNKNOWN_PRAGMA 871 %enddef
+%define SWIGWARN_PHP_PUBLIC_BASE 872 %enddef
+
+/* please leave 870-889 free for PHP */
+
+%define SWIGWARN_GO_NAME_CONFLICT 890 %enddef
+
+/* please leave 890-899 free for Go */
+
+/* -- User defined warnings (900 - 999) -- */
+
diff --git a/contrib/tools/swig/Lib/swigwarnings.swg b/contrib/tools/swig/Lib/swigwarnings.swg
new file mode 100644
index 0000000000..63ae4c65a9
--- /dev/null
+++ b/contrib/tools/swig/Lib/swigwarnings.swg
@@ -0,0 +1,131 @@
+/*
+ Include the internal swig macro codes. These macros correspond to
+ the one found in Source/Include/swigwarn.h plus the 'SWIG' prefix.
+
+ For example, in the include file 'swigwarn.h' you will find
+
+ #define WARN_TYPEMAP_CHARLEAK ...
+
+ and in the 'swigwarn.swg' interface, you will see
+
+ %define SWIGWARN_TYPEMAP_CHARLEAK ...
+
+ This code can be used in warning filters as follows:
+
+ %warnfilter(SWIGWARN_TYPEMAP_CHARLEAK);
+
+ Warnings messages used in typemaps. Message names will be the same
+ as those in Lib/swigwarn.swg but with the suffix _MSG.
+
+ For example, for the code SWIGWARN_TYPEMAP_CHARLEAK, once you use
+
+ %typemapmsg(CHARLEAK,<msg>);
+
+ you use the message in your typemap as
+
+ %typemap(varin,warning=SWIGWARN_TYPEMAP_CHARLEAK_MSG) char *
+
+ while you suppress the warning using
+
+ %warnfilter(SWIGWARN_TYPEMAP_CHARLEAK);
+
+ as described above.
+*/
+
+/* -----------------------------------------------------------------------------
+ * SWIG warning codes
+ * ----------------------------------------------------------------------------- */
+
+%include <swigwarn.swg>
+
+/* -----------------------------------------------------------------------------
+ * Auxiliary macros
+ * ----------------------------------------------------------------------------- */
+
+/* Macro to define warning messages */
+#define %_warningmsg(Val, Msg...) `Val`":"Msg
+#define %warningmsg(Val, Msg...) %_warningmsg(Val, Msg)
+
+/* -----------------------------------------------------------------------------
+ * Typemap related warning messages
+ * ----------------------------------------------------------------------------- */
+
+%define SWIGWARN_TYPEMAP_CHARLEAK_MSG "451:Setting a const char * variable may leak memory." %enddef
+%define SWIGWARN_TYPEMAP_SWIGTYPELEAK_MSG "454:Setting a pointer/reference variable may leak memory." %enddef
+%define SWIGWARN_TYPEMAP_WCHARLEAK_MSG "455:Setting a const wchar_t * variable may leak memory." %enddef
+%define SWIGWARN_TYPEMAP_THREAD_UNSAFE_MSG "470:Thread/reentrant unsafe wrapping, consider returning by value instead." %enddef
+%define SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG "473:Returning a pointer or reference in a director method is not recommended." %enddef
+%define SWIGWARN_TYPEMAP_INITIALIZER_LIST_MSG "476:Initialization using std::initializer_list." %enddef
+
+/* -----------------------------------------------------------------------------
+ * Operator related warning messages
+ * ----------------------------------------------------------------------------- */
+
+%define SWIGWARN_IGNORE_OPERATOR_NEW_MSG "350:operator new ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_DELETE_MSG "351:operator delete ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_PLUS_MSG "352:operator+ ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_MINUS_MSG "353:operator- ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_MUL_MSG "354:operator* ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_DIV_MSG "355:operator/ ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_MOD_MSG "356:operator% ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_XOR_MSG "357:operator^ ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_AND_MSG "358:operator& ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_OR_MSG "359:operator| ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_NOT_MSG "360:operator~ ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_LNOT_MSG "361:operator! ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_EQ_MSG "362:operator= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_LT_MSG "363:operator< ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_GT_MSG "364:operator> ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_PLUSEQ_MSG "365:operator+= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_MINUSEQ_MSG "366:operator-= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_MULEQ_MSG "367:operator*= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_DIVEQ_MSG "368:operator/= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_MODEQ_MSG "369:operator%= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_XOREQ_MSG "370:operator^= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_ANDEQ_MSG "371:operator&= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_OREQ_MSG "372:operator|= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_LSHIFT_MSG "373:operator<< ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_RSHIFT_MSG "374:operator>> ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_LSHIFTEQ_MSG "375:operator<<= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_RSHIFTEQ_MSG "376:operator>>= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_EQUALTO_MSG "377:operator== ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_NOTEQUAL_MSG "378:operator!= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_LTEQUAL_MSG "379:operator<= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_GTEQUAL_MSG "380:operator>= ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_LAND_MSG "381:operator&& ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_LOR_MSG "382:operator|| ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_PLUSPLUS_MSG "383:operator++ ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_MINUSMINUS_MSG "384:operator-- ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_COMMA_MSG "385:operator-- ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_ARROWSTAR_MSG "386:operator->* ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_ARROW_MSG "387:operator-> ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_CALL_MSG "388:operator() ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_INDEX_MSG "389:operator[] ignored (consider using %%extend)" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_UPLUS_MSG "390:operator+ ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_UMINUS_MSG "391:operator- ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_UMUL_MSG "392:operator* ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_UAND_MSG "393:operator& ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_NEWARR_MSG "394:operator new[] ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_DELARR_MSG "395:operator delete[] ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_REF_MSG "396:operator*() ignored" %enddef
+%define SWIGWARN_IGNORE_OPERATOR_LTEQUALGT_MSG "397:operator<=> ignored" %enddef
+
+#define %ignoreoperator(Oper) %ignorewarn(SWIGWARN_IGNORE_OPERATOR_##Oper##_MSG)
+
+/* -----------------------------------------------------------------------------
+ * Macros for keyword and built-in names
+ * ----------------------------------------------------------------------------- */
+
+#define %keywordwarn(msg...) %namewarn(%warningmsg(SWIGWARN_PARSE_KEYWORD, msg))
+#define %builtinwarn(msg...) %namewarn(%warningmsg(SWIGWARN_PARSE_BUILTIN_NAME, msg), %$isfunction)
+
+
+/* -----------------------------------------------------------------------------
+ * Warning filter feature
+ * ----------------------------------------------------------------------------- */
+
+#define %_warnfilter(filter...) %feature("warnfilter",`filter`)
+#define %warnfilter(filter...) %_warnfilter(filter)
+
+
+
diff --git a/contrib/tools/swig/Lib/typemaps/README b/contrib/tools/swig/Lib/typemaps/README
new file mode 100644
index 0000000000..65134578d3
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/README
@@ -0,0 +1,54 @@
+Still in development, but if you are interested into looking around,
+start with
+
+
+ swigtypemaps.swg
+
+which is the head file. Also read the docs for %fragments in
+
+ fragments.swg
+
+and follow the definitions in one of the supported languages:
+
+ python, perl, ruby, tcl
+
+
+
+
+/* -----------------------------------------------------------------------------
+ * Internal typemap specializations
+ * ----------------------------------------------------------------------------- */
+
+
+carrays.swg Implement the carrays.i library
+cdata.swg Implement the cdata.i library
+cmalloc.swg Implement the cmalloc.i library
+cpointer.swg Implement the cpointer.i library
+cstring.swg Implement the cstring.i library typemaps for char *
+cwstring.swg Implement the cstring.i library typemaps for wchar_t *
+exception.swg Implement the exception.i library
+implicit.swg Allow the use of implicit C++ constructors
+
+string.swg Typemaps for char * string
+wstring.swg Typemaps for wchar_t * string
+std_string.swg Typemaps for std::string
+std_wstring.swg Typemaps for std::wstring
+swigtype.swg Typemaps for the SWIGTYPE type
+void.swg Typemaps for the 'void' type
+enumint.swg Typemaps for enums treated as 'int'
+swigobject.swg Typemaps for the SWIG_Object as in PyObject, Tcl_Obj, etc.
+misctypes.swg Typemaps for miscellaneos types (size_t, ptrdiff_t, etc)
+ptrtypes.swg Typemaps for types with a 'ptr' behavior
+valtypes.swg Typemaps for 'by value' types
+inoutlist.swg IN/OUTPUT/INOUT typemaps, where the OUTPUT values are returned in a list
+primtypes.swg Common macros to manage primitive types (short,int,double,etc)
+
+cstrings.swg Common macros to implemented the cstring/cwstring libraries
+std_strings.swg Common macros to implemented the std::string/std::wstring typemaps
+strings.swg Common macros and typemaps for string and wstring (char *, wchar_t *)
+
+swigmacros.swg Basic macros
+fragments.swg Macros for fragment manipulations
+
+
+typemaps.swg The old typemaps.i library, not needed anymore
diff --git a/contrib/tools/swig/Lib/typemaps/enumint.swg b/contrib/tools/swig/Lib/typemaps/enumint.swg
new file mode 100644
index 0000000000..d048bb6bf7
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/enumint.swg
@@ -0,0 +1,39 @@
+/* ------------------------------------------------------------
+ * Enums mapped as integer values
+ * ------------------------------------------------------------ */
+
+%apply int { enum SWIGTYPE };
+%apply const int& { const enum SWIGTYPE & };
+%apply const int& { const enum SWIGTYPE && };
+
+%typemap(in,fragment=SWIG_AsVal_frag(int),noblock=1) const enum SWIGTYPE & (int val, int ecode, $basetype temp) {
+ ecode = SWIG_AsVal(int)($input, &val);
+ if (!SWIG_IsOK(ecode)) {
+ %argument_fail(ecode, "$type", $symname, $argnum);
+ } else {
+ temp = %static_cast(val,$basetype);
+ $1 = &temp;
+ }
+}
+
+%typemap(in,fragment=SWIG_AsVal_frag(int),noblock=1) const enum SWIGTYPE && (int val, int ecode, $basetype temp) {
+ ecode = SWIG_AsVal(int)($input, &val);
+ if (!SWIG_IsOK(ecode)) {
+ %argument_fail(ecode, "$type", $symname, $argnum);
+ } else {
+ temp = %static_cast(val,$basetype);
+ $1 = &temp;
+ }
+}
+
+%typemap(varin,fragment=SWIG_AsVal_frag(int),noblock=1) enum SWIGTYPE {
+ if (sizeof(int) != sizeof($1)) {
+ %variable_fail(SWIG_AttributeError,"$type", "arch, read-only $name");
+ } else {
+ int ecode = SWIG_AsVal(int)($input, %reinterpret_cast(&$1,int*));
+ if (!SWIG_IsOK(ecode)) {
+ %variable_fail(ecode, "$type", "$name");
+ }
+ }
+}
+
diff --git a/contrib/tools/swig/Lib/typemaps/exception.swg b/contrib/tools/swig/Lib/typemaps/exception.swg
new file mode 100644
index 0000000000..aece8326f5
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/exception.swg
@@ -0,0 +1,87 @@
+/* -----------------------------------------------------------------------------
+ * exceptions.swg
+ *
+ * This SWIG library file provides language independent exception handling
+ * ----------------------------------------------------------------------------- */
+
+%include <typemaps/swigmacros.swg>
+
+
+/* macros for error manipulation */
+#define %nullref_fmt() "invalid null reference "
+#define %varfail_fmt(_type,_name) "in variable '"`_name`"' of type '"`_type`"'"
+#ifndef %argfail_fmt
+#define %argfail_fmt(_type,_name,_argn) "in method '" `_name` "', argument " `_argn`" of type '" `_type`"'"
+#endif
+#define %outfail_fmt(_type) "in output value of type '"_type"'"
+#ifndef %argnullref_fmt
+#define %argnullref_fmt(_type,_name,_argn) %nullref_fmt() %argfail_fmt(_type, _name, _argn)
+#endif
+#define %varnullref_fmt(_type,_name) %nullref_fmt() %varfail_fmt(_type, _name)
+#define %outnullref_fmt(_type) %nullref_fmt() %outfail_fmt(_type)
+#define %releasenotownedfail_fmt(_type,_name,_argn) "in method '" `_name` "', cannot release ownership as memory is not owned for argument " `_argn`" of type '" `_type`"'"
+
+/* setting an error */
+#define %error(code,msg...) SWIG_Error(code, msg)
+#define %type_error(msg...) SWIG_Error(SWIG_TypeError, msg)
+
+
+
+%insert("runtime") {
+
+%define_as(SWIG_exception_fail(code, msg), %block(%error(code, msg); SWIG_fail))
+
+%define_as(SWIG_contract_assert(expr, msg), do { if (!(expr)) { %error(SWIG_RuntimeError, msg); SWIG_fail; } } while (0))
+
+}
+
+#ifdef __cplusplus
+/*
+ You can use the SWIG_CATCH_STDEXCEPT macro with the %exception
+ directive as follows:
+
+ %exception {
+ try {
+ $action
+ }
+ catch (my_except& e) {
+ ...
+ }
+ SWIG_CATCH_STDEXCEPT // catch std::exception
+ catch (...) {
+ SWIG_exception_fail(SWIG_UnknownError, "Unknown exception");
+ }
+ }
+*/
+
+%fragment("<stdexcept>");
+
+%define SWIG_CATCH_STDEXCEPT
+ /* catching std::exception */
+ catch (std::invalid_argument& e) {
+ SWIG_exception_fail(SWIG_ValueError, e.what() );
+ } catch (std::domain_error& e) {
+ SWIG_exception_fail(SWIG_ValueError, e.what() );
+ } catch (std::overflow_error& e) {
+ SWIG_exception_fail(SWIG_OverflowError, e.what() );
+ } catch (std::out_of_range& e) {
+ SWIG_exception_fail(SWIG_IndexError, e.what() );
+ } catch (std::length_error& e) {
+ SWIG_exception_fail(SWIG_IndexError, e.what() );
+ } catch (std::runtime_error& e) {
+ SWIG_exception_fail(SWIG_RuntimeError, e.what() );
+ } catch (std::exception& e) {
+ SWIG_exception_fail(SWIG_SystemError, e.what() );
+ }
+%enddef
+%define SWIG_CATCH_UNKNOWN
+ catch (std::exception& e) {
+ SWIG_exception_fail(SWIG_SystemError, e.what() );
+ }
+ catch (...) {
+ SWIG_exception_fail(SWIG_UnknownError, "unknown exception");
+ }
+%enddef
+
+
+#endif /* __cplusplus */
diff --git a/contrib/tools/swig/Lib/typemaps/fragments.swg b/contrib/tools/swig/Lib/typemaps/fragments.swg
new file mode 100644
index 0000000000..e76a694eea
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/fragments.swg
@@ -0,0 +1,231 @@
+/*
+ Fragments
+ =========
+ See the "Typemap fragments" section in the documentation for understanding
+ fragments. Below is some info on how fragments and automatic type
+ specialization is used.
+
+ Macros that make the automatic generation of typemaps easier are provided.
+
+ Consider the following code:
+
+ %fragment(SWIG_From_frag(bool), "header") {
+ static PyObject*
+ SWIG_From_dec(bool)(bool value)
+ {
+ PyObject *obj = value ? Py_True : Py_False;
+ Py_INCREF(obj);
+ return obj;
+ }
+ }
+
+ %typemap(out, fragment=SWIG_From_frag(bool)) bool {
+ $result = SWIG_From(bool)($1));
+ }
+
+ Here the macros
+
+ SWIG_From_frag => fragment
+ SWIG_From_dec => declaration
+ SWIG_From => call
+
+ allow you to define/include a fragment, and declare and call the
+ 'from-bool' method as needed. In the simpler case, these macros
+ just return something like
+
+ SWIG_From_frag(bool) => "SWIG_From_bool"
+ SWIG_From_dec(bool) => SWIG_From_bool
+ SWIG_From(bool) => SWIG_From_bool
+
+ But they are specialized for the different languages requirements,
+ such as perl or tcl that requires passing the interpreter pointer,
+ and also they can manage C++ ugly types, for example:
+
+ SWIG_From_frag(std::complex<double>) => "SWIG_From_std_complex_Sl_double_Sg_"
+ SWIG_From_dec(std::complex<double>) => SWIG_From_std_complex_Sl_double_Sg_
+ SWIG_From(std::complex<double>) => SWIG_From_std_complex_Sl_double_Sg_
+
+
+ Hence, to declare methods to use with typemaps, always use the
+ SWIG_From* macros. In the same way, the SWIG_AsVal* and SWIG_AsPtr*
+ set of macros are provided.
+
+*/
+
+
+/* -----------------------------------------------------------------------------
+ * Define the basic macros to 'normalize' the type fragments
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_AS_DECL_ARGS
+#define SWIG_AS_DECL_ARGS
+#endif
+
+#ifndef SWIG_FROM_DECL_ARGS
+#define SWIG_FROM_DECL_ARGS
+#endif
+
+#ifndef SWIG_AS_CALL_ARGS
+#define SWIG_AS_CALL_ARGS
+#endif
+
+#ifndef SWIG_FROM_CALL_ARGS
+#define SWIG_FROM_CALL_ARGS
+#endif
+
+#define %fragment_name(Name, Type...) %string_name(Name) "_" {Type}
+
+#define SWIG_Traits_frag(Type...) %fragment_name(Traits, Type)
+#define SWIG_AsPtr_frag(Type...) %fragment_name(AsPtr, Type)
+#define SWIG_AsVal_frag(Type...) %fragment_name(AsVal, Type)
+#define SWIG_From_frag(Type...) %fragment_name(From, Type)
+
+#define SWIG_AsVal_name(Type...) %symbol_name(AsVal, Type)
+#define SWIG_AsPtr_name(Type...) %symbol_name(AsPtr, Type)
+#define SWIG_From_name(Type...) %symbol_name(From, Type)
+
+#define SWIG_AsVal_dec(Type...) SWIG_AsVal_name(Type) SWIG_AS_DECL_ARGS
+#define SWIG_AsPtr_dec(Type...) SWIG_AsPtr_name(Type) SWIG_AS_DECL_ARGS
+#define SWIG_From_dec(Type...) SWIG_From_name(Type) SWIG_FROM_DECL_ARGS
+
+#define SWIG_AsVal(Type...) SWIG_AsVal_name(Type) SWIG_AS_CALL_ARGS
+#define SWIG_AsPtr(Type...) SWIG_AsPtr_name(Type) SWIG_AS_CALL_ARGS
+#define SWIG_From(Type...) SWIG_From_name(Type) SWIG_FROM_CALL_ARGS
+
+/* ------------------------------------------------------------
+ * common fragments
+ * ------------------------------------------------------------ */
+
+%fragment("SWIG_isfinite","header",fragment="<math.h>,<float.h>") %{
+/* Getting isfinite working pre C99 across multiple platforms is non-trivial. Users can provide SWIG_isfinite on older platforms. */
+#ifndef SWIG_isfinite
+/* isfinite() is a macro for C99 */
+# if defined(isfinite)
+# define SWIG_isfinite(X) (isfinite(X))
+# elif defined(__cplusplus) && __cplusplus >= 201103L
+/* Use a template so that this works whether isfinite() is std::isfinite() or
+ * in the global namespace. The reality seems to vary between compiler
+ * versions.
+ *
+ * Make sure namespace std exists to avoid compiler warnings.
+ *
+ * extern "C++" is required as this fragment can end up inside an extern "C" { } block
+ */
+namespace std { }
+extern "C++" template<typename T>
+inline int SWIG_isfinite_func(T x) {
+ using namespace std;
+ return isfinite(x);
+}
+# define SWIG_isfinite(X) (SWIG_isfinite_func(X))
+# elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+# define SWIG_isfinite(X) (__builtin_isfinite(X))
+# elif defined(_MSC_VER)
+# define SWIG_isfinite(X) (_finite(X))
+# elif defined(__sun) && defined(__SVR4)
+# include <ieeefp.h>
+# define SWIG_isfinite(X) (finite(X))
+# endif
+#endif
+%}
+
+%fragment("SWIG_Float_Overflow_Check","header",fragment="<float.h>,SWIG_isfinite") %{
+/* Accept infinite as a valid float value unless we are unable to check if a value is finite */
+#ifdef SWIG_isfinite
+# define SWIG_Float_Overflow_Check(X) ((X < -FLT_MAX || X > FLT_MAX) && SWIG_isfinite(X))
+#else
+# define SWIG_Float_Overflow_Check(X) ((X < -FLT_MAX || X > FLT_MAX))
+#endif
+%}
+
+/* -----------------------------------------------------------------------------
+ * special macros for fragments
+ * ----------------------------------------------------------------------------- */
+
+/* Macros to derive numeric types */
+
+%define %numeric_type_from(Type, Base)
+%fragment(SWIG_From_frag(Type),"header",
+ fragment=SWIG_From_frag(Base)) {
+SWIGINTERNINLINE SWIG_Object
+SWIG_From_dec(Type)(Type value)
+{
+ return SWIG_From(Base)(value);
+}
+}
+%enddef
+
+%define %numeric_type_asval(Type, Base, Frag, OverflowCond)
+%fragment(SWIG_AsVal_frag(Type),"header",
+ fragment=Frag,
+ fragment=SWIG_AsVal_frag(Base)) {
+SWIGINTERN int
+SWIG_AsVal_dec(Type)(SWIG_Object obj, Type *val)
+{
+ Base v;
+ int res = SWIG_AsVal(Base)(obj, &v);
+ if (SWIG_IsOK(res)) {
+ if (OverflowCond) {
+ return SWIG_OverflowError;
+ } else {
+ if (val) *val = %numeric_cast(v, Type);
+ }
+ }
+ return res;
+}
+}
+%enddef
+
+#define %numeric_signed_type_asval(Type, Base, Frag, Min, Max) \
+%numeric_type_asval(Type, Base, Frag, (v < Min || v > Max))
+
+#define %numeric_unsigned_type_asval(Type, Base, Frag, Max) \
+%numeric_type_asval(Type, Base, Frag, (v > Max))
+
+
+/* Macro for 'signed long' derived types */
+
+%define %numeric_slong(Type, Frag, Min, Max)
+%numeric_type_from(Type, long)
+%numeric_signed_type_asval(Type, long, Frag , Min, Max)
+%enddef
+
+/* Macro for 'unsigned long' derived types */
+
+%define %numeric_ulong(Type, Frag, Max)
+%numeric_type_from(Type, unsigned long)
+%numeric_unsigned_type_asval(Type, unsigned long, Frag, Max)
+%enddef
+
+
+/* Macro for floating point derived types (original macro) */
+
+%define %numeric_double(Type, Frag, Min, Max)
+%numeric_type_from(Type, double)
+%numeric_signed_type_asval(Type, double, Frag , Min, Max)
+%enddef
+
+/* Macro for floating point derived types */
+
+%define %numeric_float(Type, Frag, OverflowCond)
+%numeric_type_from(Type, double)
+%numeric_type_asval(Type, double, Frag, OverflowCond)
+%enddef
+
+
+/* Macros for missing fragments */
+
+%define %ensure_fragment(Fragment)
+%fragment(`Fragment`,"header") {
+%#error "SWIG language implementation must provide the Fragment fragment"
+}
+%enddef
+
+%define %ensure_type_fragments(Type)
+%fragment(SWIG_From_frag(Type),"header") {
+%#error "SWIG language implementation must provide a SWIG_From_frag(Type) fragment"
+}
+%fragment(SWIG_AsVal_frag(Type),"header") {
+%#error "SWIG language implementation must provide a SWIG_AsVal_frag(Type) fragment"
+}
+%enddef
diff --git a/contrib/tools/swig/Lib/typemaps/inoutlist.swg b/contrib/tools/swig/Lib/typemaps/inoutlist.swg
new file mode 100644
index 0000000000..23fda85f3a
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/inoutlist.swg
@@ -0,0 +1,296 @@
+/* ------------------------------------------------------------
+ *
+ * Define the IN/OUTPUT typemaps assuming the output parameters are
+ * returned in a list, i.e., they are not directly modified.
+ *
+ * The user should provide the %append_output(result, obj) method,
+ * via a macro, which append a particular object to the result.
+ *
+ *
+ * In Tcl, for example, the file is used as:
+ *
+ * #define %append_output(obj) Tcl_ListObjAppendElement(interp,Tcl_GetObjResult(interp),obj);
+ * %include <typemaps/inoutlist.swg>
+ *
+ * while in Python it is used as:
+ *
+ * #define %append_output(obj) $result = SWIG_Python_AppendResult($result, obj)
+ * %include <typemaps/inoutlist.swg>
+ *
+ * where the method SWIG_Python_AppendResult is defined inside the
+ * %append_output fragment.
+ *
+ * If you forget to define %append_output, this file will generate
+ * an error.
+ *
+ * ------------------------------------------------------------ */
+
+
+//
+// Uncomment the following definition if you don't want the in/out
+// typemaps by default, ie, you prefer to use typemaps.i.
+//
+//#define SWIG_INOUT_NODEF
+
+//
+// Use the following definition to enable the INPUT parameters to
+// accept both 'by value' and 'pointer' objects.
+//
+#define SWIG_INPUT_ACCEPT_PTRS
+
+// ------------------------------------------------------------------------
+// Pointer handling
+//
+// These mappings provide support for input/output arguments and common
+// uses for C/C++ pointers.
+// ------------------------------------------------------------------------
+
+// INPUT typemaps.
+// These remap a C pointer to be an "INPUT" value which is passed by value
+// instead of reference.
+
+/*
+The following methods can be applied to turn a pointer into a simple
+"input" value. That is, instead of passing a pointer to an object,
+you would use a real value instead.
+
+To use these, suppose you had a C function like this :
+
+ double fadd(double *a, double *b) {
+ return *a+*b;
+ }
+
+You could wrap it with SWIG as follows :
+
+ double fadd(double *INPUT, double *INPUT);
+
+or you can use the %apply directive :
+
+ %apply double *INPUT { double *a, double *b };
+ double fadd(double *a, double *b);
+
+*/
+#if defined(SWIG_INPUT_ACCEPT_PTRS)
+#define %check_input_ptr(input,arg,desc,disown) (SWIG_IsOK((res = SWIG_ConvertPtr(input,%as_voidptrptr(arg),desc,disown))))
+#else
+#define %check_input_ptr(input,arg,desc,disown) (SWIG_IsOK((res = SWIG_ERROR)))
+#endif
+
+%define %_value_input_typemap(code, asval_meth, asval_frag, Type)
+ %typemap(in,noblock=1,fragment=asval_frag) Type *INPUT ($*ltype temp, int res = 0) {
+ if (!%check_input_ptr($input,&$1,$descriptor,$disown)) {
+ Type val;
+ int ecode = asval_meth($input, &val);
+ if (!SWIG_IsOK(ecode)) {
+ %argument_fail(ecode, "$*ltype",$symname, $argnum);
+ }
+ temp = %static_cast(val, $*ltype);
+ $1 = &temp;
+ res = SWIG_AddTmpMask(ecode);
+ }
+ }
+ %typemap(in,noblock=1,fragment=asval_frag) Type &INPUT($*ltype temp, int res = 0) {
+ if (!%check_input_ptr($input,&$1,$descriptor,$disown)) {
+ Type val;
+ int ecode = asval_meth($input, &val);
+ if (!SWIG_IsOK(ecode)) {
+ %argument_fail(ecode, "$*ltype",$symname, $argnum);
+ }
+ temp = %static_cast(val, $*ltype);
+ $1 = &temp;
+ res = SWIG_AddTmpMask(ecode);
+ }
+ }
+ %typemap(freearg,noblock=1,match="in") Type *INPUT, Type &INPUT {
+ if (SWIG_IsNewObj(res$argnum)) %delete($1);
+ }
+ %typemap(typecheck,noblock=1,precedence=code,fragment=asval_frag) Type *INPUT, Type &INPUT {
+ void *ptr = 0;
+ int res = asval_meth($input, 0);
+ $1 = SWIG_CheckState(res);
+ if (!$1) {
+ $1 = %check_input_ptr($input,&ptr,$1_descriptor,0);
+ }
+ }
+%enddef
+
+%define %_ptr_input_typemap(code,asptr_meth,asptr_frag,Type)
+ %typemap(in,noblock=1,fragment=asptr_frag) Type *INPUT(int res = 0) {
+ res = asptr_meth($input, &$1);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ res = SWIG_AddTmpMask(res);
+ }
+ %typemap(in,noblock=1,fragment=asptr_frag) Type &INPUT(int res = 0) {
+ res = asptr_meth($input, &$1);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ if (!$1) {
+ %argument_nullref("$type",$symname, $argnum);
+ }
+ res = SWIG_AddTmpMask(res);
+ }
+ %typemap(freearg,noblock=1,match="in") Type *INPUT, Type &INPUT {
+ if (SWIG_IsNewObj(res$argnum)) %delete($1);
+ }
+ %typemap(typecheck,noblock=1,precedence=code,fragment=asptr_frag) Type *INPUT, Type &INPUT {
+ int res = asptr_meth($input, (Type**)0);
+ $1 = SWIG_CheckState(res);
+ }
+%enddef
+
+// OUTPUT typemaps. These typemaps are used for parameters that
+// are output only. The output value is appended to the result as
+// a list element.
+
+/*
+The following methods can be applied to turn a pointer into an "output"
+value. When calling a function, no input value would be given for
+a parameter, but an output value would be returned. In the case of
+multiple output values, they are returned in the form of a list.
+
+
+For example, suppose you were trying to wrap the modf() function in the
+C math library which splits x into integral and fractional parts (and
+returns the integer part in one of its parameters):
+
+ double modf(double x, double *ip);
+
+You could wrap it with SWIG as follows :
+
+ double modf(double x, double *OUTPUT);
+
+or you can use the %apply directive :
+
+ %apply double *OUTPUT { double *ip };
+ double modf(double x, double *ip);
+
+The output of the function would be a list containing both output
+values.
+
+*/
+
+%define %_value_output_typemap(from_meth, from_frag, Type)
+ %typemap(in,numinputs=0,noblock=1)
+ Type *OUTPUT ($*1_ltype temp, int res = SWIG_TMPOBJ),
+ Type &OUTPUT ($*1_ltype temp, int res = SWIG_TMPOBJ) {
+ $1 = &temp;
+ }
+ %typemap(argout,noblock=1,fragment=from_frag) Type *OUTPUT, Type &OUTPUT {
+ if (SWIG_IsTmpObj(res$argnum)) {
+ %append_output(from_meth((*$1)));
+ } else {
+ int new_flags = SWIG_IsNewObj(res$argnum) ? (SWIG_POINTER_OWN | %newpointer_flags) : %newpointer_flags;
+ %append_output(SWIG_NewPointerObj((void*)($1), $1_descriptor, new_flags));
+ }
+ }
+%enddef
+
+
+// INOUT
+// Mappings for an argument that is both an input and output
+// parameter
+
+/*
+The following methods can be applied to make a function parameter both
+an input and output value. This combines the behavior of both the
+"INPUT" and "OUTPUT" methods described earlier. Output values are
+returned in the form of a list.
+
+For example, suppose you were trying to wrap the following function :
+
+ void neg(double *x) {
+ *x = -(*x);
+ }
+
+You could wrap it with SWIG as follows :
+
+ void neg(double *INOUT);
+
+or you can use the %apply directive :
+
+ %apply double *INOUT { double *x };
+ void neg(double *x);
+
+Unlike C, this mapping does not directly modify the input value.
+Rather, the modified input value shows up as the return value of the
+function. Thus, to apply this function to a variable you might do
+this :
+
+ x = neg(x)
+
+Note : previous versions of SWIG used the symbol 'BOTH' to mark
+input/output arguments. This is still supported, but will be slowly
+phased out in future releases.
+
+*/
+
+%define %_value_inout_typemap(Type)
+ %typemap(in) Type *INOUT = Type *INPUT;
+ %typemap(in) Type &INOUT = Type &INPUT;
+ %typemap(typecheck) Type *INOUT = Type *INPUT;
+ %typemap(typecheck) Type &INOUT = Type &INPUT;
+ %typemap(argout) Type *INOUT = Type *OUTPUT;
+ %typemap(argout) Type &INOUT = Type &OUTPUT;
+%enddef
+
+
+%define %_ptr_inout_typemap(Type)
+ %_value_inout_typemap(%arg(Type))
+ %typemap(typecheck) Type *INOUT = Type *INPUT;
+ %typemap(typecheck) Type &INOUT = Type &INPUT;
+ %typemap(freearg) Type *INOUT = Type *INPUT;
+ %typemap(freearg) Type &INOUT = Type &INPUT;
+%enddef
+
+#ifndef SWIG_INOUT_NODEF
+
+%define %value_input_typemap(code,asval_meth, asval_frag, Type...)
+ %_value_input_typemap(%arg(code),%arg(asval_meth),%arg(asval_frag),%arg(Type))
+%enddef
+
+%define %ptr_input_typemap(code,asval_meth,asval_frag,Type...)
+ %_ptr_input_typemap(%arg(code),%arg(asval_meth),%arg(asval_frag),%arg(Type))
+%enddef
+
+%define %value_output_typemap(from_meth,from_frag,Type...)
+ %_value_output_typemap(%arg(from_meth),%arg(from_frag),%arg(Type))
+%enddef
+
+#define %value_inout_typemap(Type...) %_value_inout_typemap(%arg(Type))
+#define %ptr_inout_typemap(Type...) %_ptr_inout_typemap(%arg(Type))
+
+#else /* You need to include typemaps.i */
+
+
+#define %value_output_typemap(Type...)
+#define %value_input_typemap(Type...)
+#define %value_inout_typemap(Type...)
+#define %ptr_input_typemap(Type...)
+#define %ptr_inout_typemap(Type...)
+
+#endif /* SWIG_INOUT_DEFAULT */
+
+/*----------------------------------------------------------------------
+ Front ends.
+
+ use the following macros to define your own IN/OUTPUT/INOUT typemaps
+
+ ------------------------------------------------------------------------*/
+%define %typemaps_inout(Code, AsValMeth, FromMeth, AsValFrag, FromFrag, Type...)
+ %_value_input_typemap(%arg(Code), %arg(AsValMeth),
+ %arg(AsValFrag), %arg(Type));
+ %_value_output_typemap(%arg(FromMeth), %arg(FromFrag), %arg(Type));
+ %_value_inout_typemap(%arg(Type));
+%enddef
+
+%define %typemaps_inoutn(Code,Type...)
+ %typemaps_inout(%arg(Code),
+ %arg(SWIG_AsVal(Type)),
+ %arg(SWIG_From(Type)),
+ %arg(SWIG_AsVal_frag(Type)),
+ %arg(SWIG_From_frag(Type)),
+ %arg(Type));
+%enddef
diff --git a/contrib/tools/swig/Lib/typemaps/misctypes.swg b/contrib/tools/swig/Lib/typemaps/misctypes.swg
new file mode 100644
index 0000000000..09c81d7433
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/misctypes.swg
@@ -0,0 +1,21 @@
+
+/* ------------------------------------------------------------
+ * --- ANSI/Posix C/C++ types ---
+ * ------------------------------------------------------------ */
+
+
+#ifdef __cplusplus
+
+%apply size_t { std::size_t };
+%apply const size_t& { const std::size_t& };
+
+%apply ptrdiff_t { std::ptrdiff_t };
+%apply const ptrdiff_t& { const std::ptrdiff_t& };
+
+#ifndef SWIG_INOUT_NODEF
+%apply size_t& { std::size_t& };
+%apply ptrdiff_t& { std::ptrdiff_t& };
+#endif
+
+#endif
+
diff --git a/contrib/tools/swig/Lib/typemaps/primtypes.swg b/contrib/tools/swig/Lib/typemaps/primtypes.swg
new file mode 100644
index 0000000000..dd80eb775e
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/primtypes.swg
@@ -0,0 +1,367 @@
+/* ------------------------------------------------------------
+ * Primitive type fragments and macros
+ * ------------------------------------------------------------ */
+
+/*
+ This file provide fragments and macros for the C/C++ primitive types.
+
+ The file defines default fragments for the following types:
+
+ bool
+ signed char
+ unsigned char
+ signed wchar_t // in C++
+ unsigned wchar_t // in C++
+ short
+ unsigned short
+ int
+ unsigned int
+ float
+ size_t
+ ptrdiff_t
+
+ which can always be redefined in the swig target language if needed.
+
+ The fragments for the following types, however, always need to be
+ defined in the target language:
+
+ long
+ unsigned long
+ long long
+ unsigned long long
+ double
+
+ If they are not provided, an #error directive will appear in the
+ wrapped code.
+
+ --------------------------------------------------------------------
+
+ This file provides the macro
+
+ %typemaps_primitive(CheckCode, Type)
+
+ which generates the typemaps for a primitive type with a given
+ checkcode. It is assumed that the primitive type is 'normalized' and
+ the corresponding SWIG_AsVal(Type) and SWIG_From(Type) methods are
+ provided via fragments.
+
+
+ The following auxiliary macros (explained with bash pseudo code) are
+ also defined:
+
+ %apply_ctypes(Macro)
+ for i in C Type
+ do
+ Macro($i)
+ done
+
+ %apply_cpptypes(Macro)
+ for i in C++ Type
+ do
+ Macro($i)
+ done
+
+ %apply_ctypes_2(Macro2)
+ for i in C Type
+ do
+ for j in C Type
+ do
+ Macro_2($i, $j)
+ done
+ done
+
+ %apply_cpptypes_2(Macro2)
+ for i in C++ Type
+ do
+ for j in C++ Type
+ do
+ Macro_2($i, $j)
+ done
+ done
+
+ %apply_checkctypes(Macro2)
+ for i in Check Type
+ do
+ Macro2(%checkcode($i), $i)
+ done
+
+*/
+
+
+/* ------------------------------------------------------------
+ * Primitive type fragments
+ * ------------------------------------------------------------ */
+/* boolean */
+
+%fragment(SWIG_From_frag(bool),"header",fragment=SWIG_From_frag(long)) {
+SWIGINTERN SWIG_Object
+SWIG_From_dec(bool)(bool value)
+{
+ return SWIG_From(long)(value ? 1 : 0);
+}
+}
+
+%fragment(SWIG_AsVal_frag(bool),"header",fragment=SWIG_AsVal_frag(long)) {
+SWIGINTERN int
+SWIG_AsVal_dec(bool)(SWIG_Object obj, bool *val)
+{
+ long v;
+ int res = SWIG_AsVal(long)(obj, val ? &v : 0);
+ if (SWIG_IsOK(res)) {
+ if (val) *val = v ? true : false;
+ return res;
+ }
+ return SWIG_TypeError;
+}
+}
+
+/* signed/unsigned char */
+
+%numeric_slong(signed char, "<limits.h>", SCHAR_MIN, SCHAR_MAX)
+%numeric_ulong(unsigned char, "<limits.h>", UCHAR_MAX)
+
+/* short/unsigned short */
+
+%numeric_slong(short, "<limits.h>", SHRT_MIN, SHRT_MAX)
+%numeric_ulong(unsigned short, "<limits.h>", USHRT_MAX)
+
+/* int/unsigned int */
+
+%numeric_slong(int, "<limits.h>", INT_MIN, INT_MAX)
+%numeric_ulong(unsigned int, "<limits.h>", UINT_MAX)
+
+/* signed/unsigned wchar_t */
+
+#ifdef __cplusplus
+%numeric_slong(signed wchar_t, "<wchar.h>", WCHAR_MIN, WCHAR_MAX)
+%numeric_ulong(unsigned wchar_t, "<wchar.h>", UWCHAR_MAX)
+#endif
+
+/* float */
+
+%numeric_float(float, "SWIG_Float_Overflow_Check", SWIG_Float_Overflow_Check(v))
+
+/* long/unsigned long */
+
+%ensure_type_fragments(long)
+%ensure_type_fragments(unsigned long)
+
+/* long long/unsigned long long */
+
+%fragment("SWIG_LongLongAvailable","header", fragment="<limits.h>") %{
+#if defined(LLONG_MAX) && !defined(SWIG_LONG_LONG_AVAILABLE)
+# define SWIG_LONG_LONG_AVAILABLE
+#endif
+%}
+
+%ensure_type_fragments(long long)
+%ensure_type_fragments(unsigned long long)
+
+/* double */
+
+%ensure_type_fragments(double)
+
+/* size_t */
+
+%fragment(SWIG_From_frag(size_t),"header",fragment=SWIG_From_frag(unsigned long),fragment=SWIG_From_frag(unsigned long long)) {
+SWIGINTERNINLINE SWIG_Object
+SWIG_From_dec(size_t)(size_t value)
+{
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+ if (sizeof(size_t) <= sizeof(unsigned long)) {
+%#endif
+ return SWIG_From(unsigned long)(%numeric_cast(value, unsigned long));
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+ } else {
+ /* assume sizeof(size_t) <= sizeof(unsigned long long) */
+ return SWIG_From(unsigned long long)(%numeric_cast(value, unsigned long long));
+ }
+%#endif
+}
+}
+
+%fragment(SWIG_AsVal_frag(size_t),"header",fragment=SWIG_AsVal_frag(unsigned long),fragment=SWIG_AsVal_frag(unsigned long long)) {
+SWIGINTERNINLINE int
+SWIG_AsVal_dec(size_t)(SWIG_Object obj, size_t *val)
+{
+ int res = SWIG_TypeError;
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+ if (sizeof(size_t) <= sizeof(unsigned long)) {
+%#endif
+ unsigned long v;
+ res = SWIG_AsVal(unsigned long)(obj, val ? &v : 0);
+ if (SWIG_IsOK(res) && val) *val = %numeric_cast(v, size_t);
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+ } else if (sizeof(size_t) <= sizeof(unsigned long long)) {
+ unsigned long long v;
+ res = SWIG_AsVal(unsigned long long)(obj, val ? &v : 0);
+ if (SWIG_IsOK(res) && val) *val = %numeric_cast(v, size_t);
+ }
+%#endif
+ return res;
+}
+}
+
+/* ptrdiff_t */
+
+%fragment(SWIG_From_frag(ptrdiff_t),"header",fragment=SWIG_From_frag(long),fragment=SWIG_From_frag(long long)) {
+SWIGINTERNINLINE SWIG_Object
+SWIG_From_dec(ptrdiff_t)(ptrdiff_t value)
+{
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+ if (sizeof(ptrdiff_t) <= sizeof(long)) {
+%#endif
+ return SWIG_From(long)(%numeric_cast(value, long));
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+ } else {
+ /* assume sizeof(ptrdiff_t) <= sizeof(long long) */
+ return SWIG_From(long long)(%numeric_cast(value, long long));
+ }
+%#endif
+}
+}
+
+%fragment(SWIG_AsVal_frag(ptrdiff_t),"header",fragment=SWIG_AsVal_frag(long),fragment=SWIG_AsVal_frag(long long)) {
+SWIGINTERNINLINE int
+SWIG_AsVal_dec(ptrdiff_t)(SWIG_Object obj, ptrdiff_t *val)
+{
+ int res = SWIG_TypeError;
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+ if (sizeof(ptrdiff_t) <= sizeof(long)) {
+%#endif
+ long v;
+ res = SWIG_AsVal(long)(obj, val ? &v : 0);
+ if (SWIG_IsOK(res) && val) *val = %numeric_cast(v, ptrdiff_t);
+%#ifdef SWIG_LONG_LONG_AVAILABLE
+ } else if (sizeof(ptrdiff_t) <= sizeof(long long)) {
+ long long v;
+ res = SWIG_AsVal(long long)(obj, val ? &v : 0);
+ if (SWIG_IsOK(res) && val) *val = %numeric_cast(v, ptrdiff_t);
+ }
+%#endif
+ return res;
+}
+}
+
+
+%fragment("SWIG_CanCastAsInteger","header",
+ fragment=SWIG_AsVal_frag(double),
+ fragment="<float.h>",
+ fragment="<math.h>") {
+SWIGINTERNINLINE int
+SWIG_CanCastAsInteger(double *d, double min, double max) {
+ double x = *d;
+ if ((min <= x && x <= max)) {
+ double fx = floor(x);
+ double cx = ceil(x);
+ double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */
+ if ((errno == EDOM) || (errno == ERANGE)) {
+ errno = 0;
+ } else {
+ double summ, reps, diff;
+ if (rd < x) {
+ diff = x - rd;
+ } else if (rd > x) {
+ diff = rd - x;
+ } else {
+ return 1;
+ }
+ summ = rd + x;
+ reps = diff/summ;
+ if (reps < 8*DBL_EPSILON) {
+ *d = rd;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+}
+
+/* ------------------------------------------------------------
+ * Generate the typemaps for primitive type
+ * ------------------------------------------------------------ */
+
+#define %typemaps_primitive(Code, Type) %typemaps_asvalfromn(%arg(Code), Type)
+
+/* ------------------------------------------------------------
+ * Primitive Type Macros
+ * ------------------------------------------------------------ */
+
+/* useful macros to derive typemap declarations from primitive types */
+
+%define _apply_macro(macro, arg2, arg1...)
+#if #arg1 != ""
+macro(%arg(arg1),arg2);
+#else
+macro(arg2);
+#endif
+%enddef
+
+/* Apply macro to the C-types */
+%define %apply_ctypes(Macro, Arg2...)
+_apply_macro(Macro, bool , Arg2);
+_apply_macro(Macro, signed char , Arg2);
+_apply_macro(Macro, unsigned char , Arg2);
+_apply_macro(Macro, short , Arg2);
+_apply_macro(Macro, unsigned short , Arg2);
+_apply_macro(Macro, int , Arg2);
+_apply_macro(Macro, unsigned int , Arg2);
+_apply_macro(Macro, long , Arg2);
+_apply_macro(Macro, unsigned long , Arg2);
+_apply_macro(Macro, long long , Arg2);
+_apply_macro(Macro, unsigned long long , Arg2);
+_apply_macro(Macro, float , Arg2);
+_apply_macro(Macro, double , Arg2);
+_apply_macro(Macro, char , Arg2);
+_apply_macro(Macro, wchar_t , Arg2);
+_apply_macro(Macro, size_t , Arg2);
+_apply_macro(Macro, ptrdiff_t , Arg2);
+%enddef
+
+/* apply the Macro2(Type1, Type2) to all C types */
+#define %apply_ctypes_2(Macro2) %apply_ctypes(%apply_ctypes, Macro2)
+
+
+/* apply the Macro(Type) to all C++ types */
+%define %apply_cpptypes(Macro, Arg2...)
+%apply_ctypes(Macro, Arg2)
+_apply_macro(Macro, std::size_t, Arg2);
+_apply_macro(Macro, std::ptrdiff_t, Arg2);
+_apply_macro(Macro, std::string, Arg2);
+_apply_macro(Macro, std::wstring, Arg2);
+_apply_macro(Macro, std::complex<float>, Arg2);
+_apply_macro(Macro, std::complex<double>, Arg2);
+%enddef
+
+/* apply the Macro2(Type1, Type2) to all C++ types */
+#define %apply_cpptypes_2(Macro2) %apply_cpptypes(%apply_cpptypes, Macro2)
+
+/* apply the Macro2(CheckCode,Type) to all Checked Types */
+%define %apply_checkctypes(Macro2)
+Macro2(%checkcode(BOOL), bool);
+Macro2(%checkcode(INT8), signed char);
+Macro2(%checkcode(UINT8), unsigned char);
+Macro2(%checkcode(INT16), short);
+Macro2(%checkcode(UINT16), unsigned short);
+Macro2(%checkcode(INT32), int);
+Macro2(%checkcode(UINT32), unsigned int);
+Macro2(%checkcode(INT64), long);
+Macro2(%checkcode(UINT64), unsigned long);
+Macro2(%checkcode(INT128), long long);
+Macro2(%checkcode(UINT128), unsigned long long);
+Macro2(%checkcode(FLOAT), float);
+Macro2(%checkcode(DOUBLE), double);
+Macro2(%checkcode(CHAR), char);
+Macro2(%checkcode(UNICHAR), wchar_t);
+Macro2(%checkcode(SIZE), size_t);
+Macro2(%checkcode(PTRDIFF), ptrdiff_t);
+%enddef
+
+
+/* ------------------------------------------------------------
+ * Generate the typemaps for all the primitive types with checkcode
+ * ------------------------------------------------------------ */
+
+%apply_checkctypes(%typemaps_primitive);
+
diff --git a/contrib/tools/swig/Lib/typemaps/ptrtypes.swg b/contrib/tools/swig/Lib/typemaps/ptrtypes.swg
new file mode 100644
index 0000000000..ca54fcdc26
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/ptrtypes.swg
@@ -0,0 +1,208 @@
+/* -----------------------------------------------------------------------------
+ * ptrtypes.swg
+ *
+ * Value typemaps (Type, const Type&) for "Ptr" types, such as swig
+ * wrapped classes, that define the AsPtr/From methods
+ *
+ * To apply them, just use one of the following macros:
+ *
+ * %typemaps_asptr(CheckCode, AsPtrMeth, AsPtrFrag, Type)
+ * %typemaps_asptrfrom(CheckCode, AsPtrMeth, FromMeth, AsPtrFrag, FromFrag, Type)
+ *
+ * or the simpler and normalize form:
+ *
+ * %typemaps_asptrfromn(CheckCode, Type)
+ *
+ * Also, you can use the individual typemap definitions:
+ *
+ * %ptr_in_typemap(asptr_meth,frag,Type)
+ * %ptr_varin_typemap(asptr_meth,frag,Type)
+ * %ptr_typecheck_typemap(check,asptr_meth,frag,Type)
+ * %ptr_directorout_typemap(asptr_meth,frag,Type)
+ * ----------------------------------------------------------------------------- */
+
+%include <typemaps/valtypes.swg>
+
+/* in */
+
+%define %ptr_in_typemap(asptr_meth,frag,Type...)
+ %typemap(in,fragment=frag) Type {
+ Type *ptr = (Type *)0;
+ int res = asptr_meth($input, &ptr);
+ if (!SWIG_IsOK(res) || !ptr) {
+ %argument_fail((ptr ? res : SWIG_TypeError), "$type", $symname, $argnum);
+ }
+ $1 = *ptr;
+ if (SWIG_IsNewObj(res)) %delete(ptr);
+ }
+ %typemap(freearg) Type ""
+ %typemap(in,fragment=frag) const Type & (int res = SWIG_OLDOBJ) {
+ Type *ptr = (Type *)0;
+ res = asptr_meth($input, &ptr);
+ if (!SWIG_IsOK(res)) { %argument_fail(res,"$type",$symname, $argnum); }
+ if (!ptr) { %argument_nullref("$type",$symname, $argnum); }
+ $1 = ptr;
+ }
+ %typemap(freearg,noblock=1) const Type & {
+ if (SWIG_IsNewObj(res$argnum)) %delete($1);
+ }
+%enddef
+
+/* varin */
+
+%define %ptr_varin_typemap(asptr_meth,frag,Type...)
+ %typemap(varin,fragment=frag) Type {
+ Type *ptr = (Type *)0;
+ int res = asptr_meth($input, &ptr);
+ if (!SWIG_IsOK(res) || !ptr) {
+ %variable_fail((ptr ? res : SWIG_TypeError), "$type", "$name");
+ }
+ $1 = *ptr;
+ if (SWIG_IsNewObj(res)) %delete(ptr);
+ }
+%enddef
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+/* directorout */
+
+%define %ptr_directorout_typemap(asptr_meth,frag,Type...)
+ %typemap(directorargout,noblock=1,fragment=frag) Type *DIRECTOROUT ($*ltype temp, int swig_ores) {
+ Type *swig_optr = 0;
+ swig_ores = $result ? asptr_meth($result, &swig_optr) : 0;
+ if (!SWIG_IsOK(swig_ores) || !swig_optr) {
+ %dirout_fail((swig_optr ? swig_ores : SWIG_TypeError),"$type");
+ }
+ temp = *swig_optr;
+ $1 = &temp;
+ if (SWIG_IsNewObj(swig_ores)) %delete(swig_optr);
+ }
+
+ %typemap(directorout,noblock=1,fragment=frag) Type {
+ Type *swig_optr = 0;
+ int swig_ores = asptr_meth($input, &swig_optr);
+ if (!SWIG_IsOK(swig_ores) || !swig_optr) {
+ %dirout_fail((swig_optr ? swig_ores : SWIG_TypeError),"$type");
+ }
+ $result = *swig_optr;
+ if (SWIG_IsNewObj(swig_ores)) %delete(swig_optr);
+ }
+
+ %typemap(directorout,noblock=1,fragment=frag,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) Type* {
+ Type *swig_optr = 0;
+ int swig_ores = asptr_meth($input, &swig_optr);
+ if (!SWIG_IsOK(swig_ores)) {
+ %dirout_fail(swig_ores,"$type");
+ }
+ $result = swig_optr;
+ if (SWIG_IsNewObj(swig_ores)) {
+ swig_acquire_ownership(swig_optr);
+ }
+ }
+ %typemap(directorfree,noblock=1) Type*
+ {
+ if (director) {
+ director->swig_release_ownership(%as_voidptr($input));
+ }
+ }
+
+ %typemap(directorout,noblock=1,fragment=frag,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) Type& {
+ Type *swig_optr = 0;
+ int swig_ores = asptr_meth($input, &swig_optr);
+ if (!SWIG_IsOK(swig_ores)) {
+ %dirout_fail(swig_ores,"$type");
+ } else {
+ if (!swig_optr) {
+ %dirout_nullref("$type");
+ }
+ }
+ $result = swig_optr;
+ if (SWIG_IsNewObj(swig_ores)) {
+ swig_acquire_ownership(swig_optr);
+ }
+ }
+ %typemap(directorfree,noblock=1) Type&
+ {
+ if (director) {
+ director->swig_release_ownership(%as_voidptr($input));
+ }
+ }
+
+
+ %typemap(directorout,fragment=frag) Type &DIRECTOROUT = Type
+
+%enddef
+
+#else
+
+#define %ptr_directorout_typemap(asptr_meth,frag,Type...)
+
+#endif /* SWIG_DIRECTOR_TYPEMAPS */
+
+/* typecheck */
+
+%define %ptr_typecheck_typemap(check,asptr_meth,frag,Type...)
+%typemap(typecheck,noblock=1,precedence=check,fragment=frag) Type * {
+ int res = asptr_meth($input, (Type**)(0));
+ $1 = SWIG_CheckState(res);
+}
+
+%typemap(typecheck,noblock=1,precedence=check,fragment=frag) Type, const Type& {
+ int res = asptr_meth($input, (Type**)(0));
+ $1 = SWIG_CheckState(res);
+}
+%enddef
+
+
+/*---------------------------------------------------------------------
+ * typemap definition for types with asptr method
+ *---------------------------------------------------------------------*/
+
+%define %typemaps_asptr(CheckCode, AsPtrMeth, AsPtrFrag, Type...)
+ %fragment(SWIG_AsVal_frag(Type),"header",fragment=SWIG_AsPtr_frag(Type)) {
+ SWIGINTERNINLINE int
+ SWIG_AsVal(Type)(SWIG_Object obj, Type *val)
+ {
+ Type *v = (Type *)0;
+ int res = SWIG_AsPtr(Type)(obj, &v);
+ if (!SWIG_IsOK(res)) return res;
+ if (v) {
+ if (val) *val = *v;
+ if (SWIG_IsNewObj(res)) {
+ %delete(v);
+ res = SWIG_DelNewMask(res);
+ }
+ return res;
+ }
+ return SWIG_ERROR;
+ }
+ }
+ %ptr_in_typemap(%arg(AsPtrMeth), %arg(AsPtrFrag), Type);
+ %ptr_varin_typemap(%arg(AsPtrMeth), %arg(AsPtrFrag), Type);
+ %ptr_directorout_typemap(%arg(AsPtrMeth), %arg(AsPtrFrag), Type);
+ %ptr_typecheck_typemap(%arg(CheckCode), %arg(AsPtrMeth),%arg(AsPtrFrag), Type);
+ %ptr_input_typemap(%arg(CheckCode),%arg(AsPtrMeth),%arg(AsPtrFrag),Type);
+%enddef
+
+/*---------------------------------------------------------------------
+ * typemap definition for types with asptr/from methods
+ *---------------------------------------------------------------------*/
+
+%define %typemaps_asptrfrom(CheckCode, AsPtrMeth, FromMeth, AsPtrFrag, FromFrag, Type...)
+ %typemaps_asptr(%arg(CheckCode), %arg(AsPtrMeth), %arg(AsPtrFrag), Type)
+ %typemaps_from(%arg(FromMeth), %arg(FromFrag), Type);
+ %value_output_typemap(%arg(FromMeth), %arg(FromFrag), Type);
+ %ptr_inout_typemap(Type);
+%enddef
+
+/*---------------------------------------------------------------------
+ * typemap definition for types with for 'normalized' asptr/from methods
+ *---------------------------------------------------------------------*/
+
+%define %typemaps_asptrfromn(CheckCode, Type...)
+%typemaps_asptrfrom(%arg(CheckCode),
+ %arg(SWIG_AsPtr(Type)),
+ %arg(SWIG_From(Type)),
+ %arg(SWIG_AsPtr_frag(Type)),
+ %arg(SWIG_From_frag(Type)),
+ Type);
+%enddef
diff --git a/contrib/tools/swig/Lib/typemaps/std_except.swg b/contrib/tools/swig/Lib/typemaps/std_except.swg
new file mode 100644
index 0000000000..75d066490f
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/std_except.swg
@@ -0,0 +1,37 @@
+%include <typemaps/exception.swg>
+
+/*
+ Mark all of std exception classes as "exception classes" via
+ the "exceptionclass" feature.
+
+ If needed, you can disable it by using %noexceptionclass.
+*/
+
+%define %std_exception_map(Exception, Code)
+ %exceptionclass Exception;
+#if !defined(SWIG_STD_EXCEPTIONS_AS_CLASSES)
+ %typemap(throws,noblock=1) Exception {
+ SWIG_exception_fail(Code, $1.what());
+ }
+ %ignore Exception;
+ struct Exception {
+ };
+#endif
+%enddef
+
+namespace std {
+ %std_exception_map(bad_cast, SWIG_TypeError);
+ %std_exception_map(bad_exception, SWIG_SystemError);
+ %std_exception_map(domain_error, SWIG_ValueError);
+ %std_exception_map(exception, SWIG_SystemError);
+ %std_exception_map(invalid_argument, SWIG_ValueError);
+ %std_exception_map(length_error, SWIG_IndexError);
+ %std_exception_map(logic_error, SWIG_RuntimeError);
+ %std_exception_map(out_of_range, SWIG_IndexError);
+ %std_exception_map(overflow_error, SWIG_OverflowError);
+ %std_exception_map(range_error, SWIG_OverflowError);
+ %std_exception_map(runtime_error, SWIG_RuntimeError);
+ %std_exception_map(underflow_error, SWIG_OverflowError);
+}
+
+%include <std/std_except.i>
diff --git a/contrib/tools/swig/Lib/typemaps/std_string.swg b/contrib/tools/swig/Lib/typemaps/std_string.swg
new file mode 100644
index 0000000000..5b57beab56
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/std_string.swg
@@ -0,0 +1,25 @@
+//
+// String
+//
+
+
+#ifndef SWIG_STD_BASIC_STRING
+#define SWIG_STD_STRING
+
+%include <typemaps/std_strings.swg>
+
+%fragment("<string>");
+
+namespace std
+{
+ %naturalvar string;
+ class string;
+}
+
+%typemaps_std_string(std::string, char, SWIG_AsCharPtrAndSize, SWIG_FromCharPtrAndSize, %checkcode(STDSTRING));
+
+#else
+
+%include <std/std_string.i>
+
+#endif
diff --git a/contrib/tools/swig/Lib/typemaps/std_strings.swg b/contrib/tools/swig/Lib/typemaps/std_strings.swg
new file mode 100644
index 0000000000..e9c23ba915
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/std_strings.swg
@@ -0,0 +1,78 @@
+
+/* defining the String asptr/from methods */
+
+%define %std_string_asptr(String, Char, SWIG_AsCharPtrAndSize, Frag)
+%fragment(SWIG_AsPtr_frag(String),"header",fragment=Frag) {
+SWIGINTERN int
+SWIG_AsPtr_dec(String)(SWIG_Object obj, String **val)
+{
+ Char* buf = 0 ; size_t size = 0; int alloc = SWIG_OLDOBJ;
+ if (SWIG_IsOK((SWIG_AsCharPtrAndSize(obj, &buf, &size, &alloc)))) {
+ if (buf) {
+ if (val) *val = new String(buf, size - 1);
+ if (alloc == SWIG_NEWOBJ) %delete_array(buf);
+ return SWIG_NEWOBJ;
+ } else {
+ if (val) *val = 0;
+ return SWIG_OLDOBJ;
+ }
+ } else {
+ static int init = 0;
+ static swig_type_info* descriptor = 0;
+ if (!init) {
+ descriptor = SWIG_TypeQuery(#String " *");
+ init = 1;
+ }
+ if (descriptor) {
+ String *vptr;
+ int res = SWIG_ConvertPtr(obj, (void**)&vptr, descriptor, 0);
+ if (SWIG_IsOK(res) && val) *val = vptr;
+ return res;
+ }
+ }
+ return SWIG_ERROR;
+}
+}
+%enddef
+
+%define %std_string_from(String, SWIG_FromCharPtrAndSize, Frag)
+%fragment(SWIG_From_frag(String),"header",fragment=Frag) {
+SWIGINTERNINLINE SWIG_Object
+SWIG_From_dec(String)(const String& s)
+{
+ return SWIG_FromCharPtrAndSize(s.data(), s.size());
+}
+}
+%enddef
+
+%define %std_string_asval(String)
+%fragment(SWIG_AsVal_frag(String),"header", fragment=SWIG_AsPtr_frag(String)) {
+SWIGINTERN int
+SWIG_AsVal_dec(String)(SWIG_Object obj, String *val)
+{
+ String* v = (String *) 0;
+ int res = SWIG_AsPtr(String)(obj, &v);
+ if (!SWIG_IsOK(res)) return res;
+ if (v) {
+ if (val) *val = *v;
+ if (SWIG_IsNewObj(res)) {
+ %delete(v);
+ res = SWIG_DelNewMask(res);
+ }
+ return res;
+ }
+ return SWIG_ERROR;
+}
+}
+%enddef
+
+
+%define %typemaps_std_string(String, Char, AsPtrMethod, FromMethod, CheckCode)
+
+%std_string_asptr(String, Char, AsPtrMethod, #AsPtrMethod)
+%std_string_asval(String)
+%std_string_from(String, FromMethod, #FromMethod)
+
+%typemaps_asptrfromn(%arg(CheckCode), String);
+
+%enddef
diff --git a/contrib/tools/swig/Lib/typemaps/string.swg b/contrib/tools/swig/Lib/typemaps/string.swg
new file mode 100644
index 0000000000..72f4aa5b56
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/string.swg
@@ -0,0 +1,36 @@
+%ensure_fragment(SWIG_AsCharPtrAndSize)
+%ensure_fragment(SWIG_FromCharPtrAndSize)
+
+%types(char *);
+
+%fragment("SWIG_pchar_descriptor","header") {
+SWIGINTERN swig_type_info*
+SWIG_pchar_descriptor(void)
+{
+ static int init = 0;
+ static swig_type_info* info = 0;
+ if (!init) {
+ info = SWIG_TypeQuery("_p_char");
+ init = 1;
+ }
+ return info;
+}
+}
+
+%fragment("SWIG_strnlen","header",fragment="SWIG_FromCharPtrAndSize") {
+SWIGINTERN size_t
+SWIG_strnlen(const char* s, size_t maxlen)
+{
+ const char *p;
+ for (p = s; maxlen-- && *p; p++)
+ ;
+ return p - s;
+}
+}
+
+%include <typemaps/strings.swg>
+%typemaps_string(%checkcode(STRING), %checkcode(CHAR),
+ SWIGWARN_TYPEMAP_CHARLEAK_MSG,
+ char, Char, SWIG_AsCharPtrAndSize, SWIG_FromCharPtrAndSize,
+ strlen, SWIG_strnlen,
+ "<limits.h>", CHAR_MIN, CHAR_MAX)
diff --git a/contrib/tools/swig/Lib/typemaps/strings.swg b/contrib/tools/swig/Lib/typemaps/strings.swg
new file mode 100644
index 0000000000..1237d98dfd
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/strings.swg
@@ -0,0 +1,658 @@
+//
+// Use the macro SWIG_PRESERVE_CARRAY_SIZE if you prefer to preserve
+// the size of char arrays, ie
+// ------------------------------------------
+// C Side => Language Side
+// ------------------------------------------
+// char name[5] = "hola" => 'hola\0'
+//
+// the default behaviour is
+//
+// char name[5] = "hola" => 'hola'
+//
+//
+//#define SWIG_PRESERVE_CARRAY_SIZE
+
+/* ------------------------------------------------------------
+ * String typemaps for type Char (char or wchar_t)
+ * ------------------------------------------------------------ */
+
+%define %_typemap_string(StringCode,
+ Char,
+ WarningLeakMsg,
+ SWIG_AsCharPtrAndSize,
+ SWIG_FromCharPtrAndSize,
+ SWIG_CharPtrLen,
+ SWIG_CharBufLen,
+ SWIG_AsCharPtr,
+ SWIG_FromCharPtr,
+ SWIG_AsCharArray,
+ SWIG_NewCopyCharArray,
+ SWIG_DeleteCharArray)
+
+/* in */
+
+%typemap(in,noblock=1,fragment=#SWIG_AsCharPtr)
+ Char * (int res, Char *buf = 0, int alloc = 0),
+ const Char * (int res, Char *buf = 0, int alloc = 0) {
+ res = SWIG_AsCharPtr($input, &buf, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ $1 = %reinterpret_cast(buf, $1_ltype);
+}
+%typemap(freearg,noblock=1,match="in") Char *, const Char * {
+ if (alloc$argnum == SWIG_NEWOBJ) SWIG_DeleteCharArray(buf$argnum);
+}
+
+%typemap(in,noblock=1,fragment=#SWIG_AsCharPtr) Char const*& (int res, Char *buf = 0, int alloc = 0) {
+ res = SWIG_AsCharPtr($input, &buf, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ $1 = &buf;
+}
+%typemap(freearg, noblock=1,match="in") Char const*& {
+ if (alloc$argnum == SWIG_NEWOBJ) SWIG_DeleteCharArray(buf$argnum);
+}
+
+/* out */
+
+%typemap(out,noblock=1,fragment=#SWIG_FromCharPtr) Char *, const Char * {
+ %set_output(SWIG_FromCharPtr((const Char *)$1));
+}
+
+
+%typemap(out,noblock=1,fragment=#SWIG_FromCharPtr) Char const*& {
+ %set_output(SWIG_FromCharPtr(*$1));
+}
+
+%typemap(newfree,noblock=1) Char * {
+ SWIG_DeleteCharArray($1);
+}
+
+/* varin */
+
+%typemap(varin,fragment=#SWIG_AsCharPtrAndSize) Char * {
+ Char *cptr = 0; size_t csize = 0; int alloc = SWIG_NEWOBJ;
+ int res = SWIG_AsCharPtrAndSize($input, &cptr, &csize, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res,"$type","$name");
+ }
+ SWIG_DeleteCharArray($1);
+ if (alloc == SWIG_NEWOBJ) {
+ $1 = cptr;
+ } else {
+ $1 = csize ? ($1_type)SWIG_NewCopyCharArray(cptr, csize, Char) : 0;
+ }
+}
+
+%typemap(varin,fragment=#SWIG_AsCharPtrAndSize,warning=WarningLeakMsg) const Char * {
+ Char *cptr = 0; size_t csize = 0; int alloc = SWIG_NEWOBJ;
+ int res = SWIG_AsCharPtrAndSize($input, &cptr, &csize, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ if (alloc == SWIG_NEWOBJ) {
+ $1 = cptr;
+ } else {
+ $1 = csize ? ($1_type)SWIG_NewCopyCharArray(cptr, csize, Char) : 0;
+ }
+}
+
+/* varout */
+
+%typemap(varout,noblock=1,fragment=#SWIG_FromCharPtr) Char *, const Char * {
+ %set_varoutput(SWIG_FromCharPtr($1));
+}
+
+/* memberin */
+
+%typemap(memberin,noblock=1) Char * {
+ SWIG_DeleteCharArray($1);
+ if ($input) {
+ size_t size = SWIG_CharPtrLen(%reinterpret_cast($input, const Char *)) + 1;
+ $1 = ($1_type)SWIG_NewCopyCharArray(%reinterpret_cast($input, const Char *), size, Char);
+ } else {
+ $1 = 0;
+ }
+}
+
+%typemap(memberin,noblock=1,warning=WarningLeakMsg) const Char * {
+ if ($input) {
+ size_t size = SWIG_CharPtrLen(%reinterpret_cast(%reinterpret_cast($input, const Char *), const Char *)) + 1;
+ $1 = ($1_type)SWIG_NewCopyCharArray($input, size, Char);
+ } else {
+ $1 = 0;
+ }
+}
+
+/* globalin */
+
+%typemap(globalin,noblock=1) Char * {
+ SWIG_DeleteCharArray($1);
+ if ($input) {
+ size_t size = SWIG_CharPtrLen(%reinterpret_cast(%reinterpret_cast($input, const Char *), const Char *)) + 1;
+ $1 = ($1_type)SWIG_NewCopyCharArray($input, size, Char);
+ } else {
+ $1 = 0;
+ }
+}
+
+%typemap(globalin,noblock=1,warning=WarningLeakMsg) const Char * {
+ if ($input) {
+ size_t size = SWIG_CharPtrLen($input) + 1;
+ $1 = ($1_type)SWIG_NewCopyCharArray($input, size, Char);
+ } else {
+ $1 = 0;
+ }
+}
+
+/* constant */
+
+%typemap(constcode,noblock=1,fragment=#SWIG_FromCharPtr)
+ Char *, Char const*, Char * const, Char const* const {
+ %set_constant("$symname", SWIG_FromCharPtr($value));
+}
+
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+/* directorin */
+
+%typemap(directorin,noblock=1,fragment=#SWIG_FromCharPtr)
+ Char *, Char const*, Char *const, Char const *const,
+ Char const *&, Char *const &, Char const *const & {
+ $input = SWIG_FromCharPtr((const Char *)$1);
+}
+
+
+/* directorout */
+
+%typemap(directorout,noblock=1,fragment=#SWIG_AsCharPtr,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) Char * (int res, Char *buf = 0, int alloc = SWIG_NEWOBJ) {
+ res = SWIG_AsCharPtr($input, &buf, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %dirout_fail(res, "$type");
+ }
+ if (alloc == SWIG_NEWOBJ) {
+ swig_acquire_ownership_array(buf);
+ }
+ $result = %reinterpret_cast(buf, $1_ltype);
+}
+%typemap(directorfree,noblock=1) Char *
+{
+ if (director) {
+ director->swig_release_ownership(%as_voidptr($input));
+ }
+}
+
+
+%typemap(directorout,noblock=1,fragment=#SWIG_AsCharPtr,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) Char *const& (int res, Char *buf = 0, int alloc = SWIG_NEWOBJ), Char const*const& (int res, Char *buf = 0, int alloc = SWIG_NEWOBJ) {
+ res = SWIG_AsCharPtr($input, &buf, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %dirout_fail(res, "$type");
+ }
+ static $*1_ltype tmp = buf;
+ $result = &tmp;
+ if (alloc == SWIG_NEWOBJ) {
+ swig_acquire_ownership_array(buf);
+ }
+}
+%typemap(directorfree,noblock=1)
+ Char * const&, Char const* const& {
+ if (director) {
+ director->swig_release_ownership(%as_voidptr(*$input));
+ }
+}
+
+#endif /* SWIG_DIRECTOR_TYPEMAPS */
+
+/* typecheck */
+
+%typemap(typecheck,noblock=1,precedence=StringCode,
+ fragment=#SWIG_AsCharPtr) Char *, const Char *, Char const*& {
+ int res = SWIG_AsCharPtr($input, 0, 0);
+ $1 = SWIG_CheckState(res);
+}
+
+
+/* throws */
+
+%typemap(throws,noblock=1,fragment=#SWIG_FromCharPtr) Char * {
+ %raise(SWIG_FromCharPtr($1), "$type", 0);
+}
+
+
+/* ------------------------------------------------------------
+ * Unknown size const Character array Char[ANY] handling
+ * ------------------------------------------------------------ */
+
+%apply Char * { Char [] };
+%apply const Char * { const Char [] };
+
+%typemap(varin,noblock=1,warning="462:Unable to set variable of type Char []") Char []
+{
+ %variable_fail(SWIG_AttributeError, "$type", "read-only $name");
+}
+
+
+/* ------------------------------------------------------------
+ * Fixed size Character array Char[ANY] handling
+ * ------------------------------------------------------------ */
+
+/* memberin and globalin typemaps */
+
+%typemap(memberin,noblock=1) Char [ANY]
+{
+ if ($input) memcpy($1,$input,$1_dim0*sizeof(Char));
+ else memset($1,0,$1_dim0*sizeof(Char));
+}
+
+%typemap(globalin,noblock=1) Char [ANY]
+{
+ if ($input) memcpy($1,$input,$1_dim0*sizeof(Char));
+ else memset($1,0,$1_dim0*sizeof(Char));
+}
+
+/* in */
+
+%typemap(in,noblock=1,fragment=#SWIG_AsCharArray)
+ Char [ANY] (Char temp[$1_dim0], int res),
+ const Char [ANY](Char temp[$1_dim0], int res)
+{
+ res = SWIG_AsCharArray($input, temp, $1_dim0);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ $1 = %reinterpret_cast(temp, $1_ltype);
+}
+%typemap(freearg) Char [ANY], const Char [ANY] ""
+
+%typemap(in,noblock=1,fragment=#SWIG_AsCharArray) const Char (&)[ANY] (Char temp[$1_dim0], int res)
+{
+ res = SWIG_AsCharArray($input, temp, $1_dim0);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ $1 = &temp;
+}
+%typemap(freearg) const Char (&)[ANY] ""
+
+%typemap(out,fragment=#SWIG_FromCharPtrAndSize,fragment=#SWIG_CharBufLen)
+ Char [ANY], const Char[ANY]
+{
+%#ifndef SWIG_PRESERVE_CARRAY_SIZE
+ size_t size = SWIG_CharBufLen($1, $1_dim0);
+%#else
+ size_t size = $1_dim0;
+%#endif
+ %set_output(SWIG_FromCharPtrAndSize($1, size));
+}
+
+/* varin */
+
+%typemap(varin,fragment=#SWIG_AsCharArray) Char [ANY]
+{
+ int res = SWIG_AsCharArray($input, $1, $1_dim0);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+}
+
+/* varout */
+
+%typemap(varout,fragment=#SWIG_CharBufLen)
+ Char [ANY], const Char [ANY] {
+%#ifndef SWIG_PRESERVE_CARRAY_SIZE
+ size_t size = SWIG_CharBufLen($1, $1_dim0);
+%#else
+ size_t size = $1_dim0;
+%#endif
+ %set_varoutput(SWIG_FromCharPtrAndSize($1, size));
+}
+
+/* constant */
+
+%typemap(constcode,fragment=#SWIG_CharBufLen)
+ Char [ANY], const Char [ANY]
+{
+%#ifndef SWIG_PRESERVE_CARRAY_SIZE
+ size_t size = SWIG_CharBufLen($1, $1_dim0);
+%#else
+ size_t size = $value_dim0;
+%#endif
+ %set_constant("$symname", SWIG_FromCharPtrAndSize($value,size));
+}
+
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+/* directorin */
+%typemap(directorin,fragment=#SWIG_CharBufLen)
+ Char [ANY], const Char [ANY]
+{
+%#ifndef SWIG_PRESERVE_CARRAY_SIZE
+ size_t size = SWIG_CharBufLen($1, $1_dim0);
+%#else
+ size_t size = $1_dim0;
+%#endif
+ $input = SWIG_FromCharPtrAndSize($1, size);
+}
+
+/* directorout */
+
+%typemap(directorout,noblock=1,fragment=#SWIG_AsCharArray)
+ Char [ANY] (Char temp[$result_dim0]),
+ const Char [ANY] (Char temp[$result_dim0], int res)
+{
+ res = SWIG_AsCharArray($input, temp, $result_dim0);
+ if (!SWIG_IsOK(res)) {
+ %dirout_fail(res, "$type");
+ }
+ $result = temp;
+}
+
+#endif /* SWIG_DIRECTOR_TYPEMAPS */
+
+/* typecheck */
+
+%typemap(typecheck,noblock=1,precedence=StringCode,
+ fragment=#SWIG_AsCharArray)
+ Char [ANY], const Char[ANY] {
+ int res = SWIG_AsCharArray($input, (Char *)0, $1_dim0);
+ $1 = SWIG_CheckState(res);
+}
+
+
+/* throws */
+
+%typemap(throws,fragment=#SWIG_CharBufLen)
+ Char [ANY], const Char[ANY]
+{
+%#ifndef SWIG_PRESERVE_CARRAY_SIZE
+ size_t size = SWIG_CharBufLen($1, $1_dim0);
+%#else
+ size_t size = $1_dim0;
+%#endif
+ %raise(SWIG_FromCharPtrAndSize($1, size), "$type", 0);
+}
+
+/* -------------------------------------------------------------------
+ * --- Really fix size Char arrays, including '\0'chars at the end ---
+ * ------------------------------------------------------------------- */
+
+%typemap(varout,noblock=1,fragment=#SWIG_FromCharPtrAndSize)
+ Char FIXSIZE[ANY], const Char FIXSIZE[ANY]
+{
+ %set_varoutput(SWIG_FromCharPtrAndSize($1, $1_dim0));
+}
+
+%typemap(out,noblock=1,fragment=#SWIG_FromCharPtrAndSize)
+ Char FIXSIZE[ANY], const Char FIXSIZE[ANY]
+{
+ %set_output(SWIG_FromCharPtrAndSize($1, $1_dim0));
+}
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+%typemap(directorin,noblock=1,fragment=#SWIG_FromCharPtrAndSize)
+ Char FIXSIZE[ANY], const Char FIXSIZE[ANY]
+{
+ $input = SWIG_FromCharPtrAndSize($1, $1_dim0);
+}
+
+#endif /* SWIG_DIRECTOR_TYPEMAPS */
+
+%typemap(throws,noblock=1,fragment=#SWIG_FromCharPtrAndSize)
+ Char FIXSIZE[ANY], const Char FIXSIZE[ANY] {
+ %raise(SWIG_FromCharPtrAndSize($1, $1_dim0), "$type", 0);
+}
+
+/* ------------------------------------------------------------
+ * --- String & length ---
+ * ------------------------------------------------------------ */
+
+/* Here len doesn't include the '0' terminator */
+%typemap(in,noblock=1,fragment=#SWIG_AsCharPtrAndSize)
+ (Char *STRING, size_t LENGTH) (int res, Char *buf = 0, size_t size = 0, int alloc = 0),
+ (const Char *STRING, size_t LENGTH) (int res, Char *buf = 0, size_t size = 0, int alloc = 0)
+{
+ res = SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ $1 = %reinterpret_cast(buf, $1_ltype);
+ $2 = %numeric_cast(size - 1, $2_ltype);
+}
+%typemap(freearg,noblock=1,match="in") (Char *STRING, size_t LENGTH) {
+ if (alloc$argnum == SWIG_NEWOBJ) SWIG_DeleteCharArray(buf$argnum);
+}
+/* old 'int' form */
+%typemap(in) (Char *STRING, int LENGTH) = (Char *STRING, size_t LENGTH);
+%typemap(freearg) (Char *STRING, int LENGTH) = (Char *STRING, size_t LENGTH);
+
+
+/* Here size includes the '0' terminator */
+%typemap(in,noblock=1,fragment=#SWIG_AsCharPtrAndSize)
+ (Char *STRING, size_t SIZE) (int res, Char *buf = 0, size_t size = 0, int alloc = 0),
+ (const Char *STRING, size_t SIZE) (int res, Char *buf = 0, size_t size = 0, int alloc = 0)
+{
+ res = SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ $1 = %reinterpret_cast(buf, $1_ltype);
+ $2 = %numeric_cast(size, $2_ltype);
+}
+%typemap(freearg,noblock=1,match="in") (Char *STRING, size_t SIZE) {
+ if (alloc$argnum == SWIG_NEWOBJ) SWIG_DeleteCharArray(buf$argnum);
+}
+/* old 'int' form */
+%typemap(in) (Char *STRING, int SIZE) = (Char *STRING, size_t SIZE);
+%typemap(freearg) (Char *STRING, int SIZE) = (Char *STRING, size_t SIZE);
+
+
+/* reverse order versions */
+
+/* Here len doesn't include the '0' terminator */
+%typemap(in,noblock=1,fragment=#SWIG_AsCharPtrAndSize)
+ (size_t LENGTH, Char *STRING) (int res, Char *buf = 0, size_t size = 0, int alloc = 0),
+ (size_t LENGTH, const Char *STRING) (int res, Char *buf = 0, size_t size = 0, int alloc = 0)
+{
+ res = SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+ $2 = %reinterpret_cast(buf, $2_ltype) ;
+ $1 = %numeric_cast(size - 1, $1_ltype) ;
+}
+%typemap(freearg, noblock=1, match="in") (size_t LENGTH, Char *STRING) {
+ if (alloc$argnum == SWIG_NEWOBJ) SWIG_DeleteCharArray(buf$argnum);
+}
+/* old 'int' form */
+%typemap(in) (int LENGTH, Char *STRING) = (size_t LENGTH, Char *STRING);
+%typemap(freearg) (int LENGTH, Char *STRING) = (size_t LENGTH, Char *STRING);
+
+/* Here size includes the '0' terminator */
+%typemap(in,noblock=1,fragment=#SWIG_AsCharPtrAndSize)
+ (size_t SIZE, Char *STRING) (int res, Char *buf = 0, size_t size = 0, int alloc = 0),
+ (size_t SIZE, const Char *STRING) (int res, Char *buf = 0, size_t size = 0, int alloc = 0)
+{
+ res = SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$type",$symname, $argnum);
+ }
+ $2 = %reinterpret_cast(buf, $2_ltype) ;
+ $1 = %numeric_cast(size, $1_ltype) ;
+}
+%typemap(freearg, noblock=1, match="in") (size_t SIZE, Char *STRING) {
+ if (alloc$argnum == SWIG_NEWOBJ) SWIG_DeleteCharArray(buf$argnum);
+}
+/* old 'int' form */
+%typemap(in) (int SIZE, Char *STRING) = (size_t SIZE, Char *STRING);
+%typemap(freearg) (int SIZE, Char *STRING) = (size_t SIZE, Char *STRING);
+
+
+%enddef
+
+
+/* ------------------------------------------------------------
+ * --- String fragment methods ---
+ * ------------------------------------------------------------ */
+
+#ifndef %_typemap2_string
+%define %_typemap2_string(StringCode, CharCode,
+ WarningLeakMsg,
+ Char, CharName,
+ SWIG_AsCharPtrAndSize,
+ SWIG_FromCharPtrAndSize,
+ SWIG_CharPtrLen,
+ SWIG_CharBufLen,
+ SWIG_NewCopyCharArray,
+ SWIG_DeleteCharArray,
+ FragLimits, CHAR_MIN, CHAR_MAX)
+
+%fragment("SWIG_From"#CharName"Ptr","header",fragment=#SWIG_FromCharPtrAndSize) {
+SWIGINTERNINLINE SWIG_Object
+SWIG_From##CharName##Ptr(const Char *cptr)
+{
+ return SWIG_FromCharPtrAndSize(cptr, (cptr ? SWIG_CharPtrLen(cptr) : 0));
+}
+}
+
+%fragment("SWIG_From"#CharName"Array","header",fragment=#SWIG_FromCharPtrAndSize) {
+SWIGINTERNINLINE SWIG_Object
+SWIG_From##CharName##Array(const Char *cptr, size_t size)
+{
+ return SWIG_FromCharPtrAndSize(cptr, size);
+}
+}
+
+%fragment("SWIG_As" #CharName "Ptr","header",fragment=#SWIG_AsCharPtrAndSize) {
+%define_as(SWIG_As##CharName##Ptr(obj, val, alloc), SWIG_AsCharPtrAndSize(obj, val, NULL, alloc))
+}
+
+%fragment("SWIG_As" #CharName "Array","header",fragment=#SWIG_AsCharPtrAndSize) {
+SWIGINTERN int
+SWIG_As##CharName##Array(SWIG_Object obj, Char *val, size_t size)
+{
+ Char* cptr = 0; size_t csize = 0; int alloc = SWIG_OLDOBJ;
+ int res = SWIG_AsCharPtrAndSize(obj, &cptr, &csize, &alloc);
+ if (SWIG_IsOK(res)) {
+ /* special case of single char conversion when we don't need space for NUL */
+ if (size == 1 && csize == 2 && cptr && !cptr[1]) --csize;
+ if (csize <= size) {
+ if (val) {
+ if (csize) memcpy(val, cptr, csize*sizeof(Char));
+ if (csize < size) memset(val + csize, 0, (size - csize)*sizeof(Char));
+ }
+ if (alloc == SWIG_NEWOBJ) {
+ SWIG_DeleteCharArray(cptr);
+ res = SWIG_DelNewMask(res);
+ }
+ return res;
+ }
+ if (alloc == SWIG_NEWOBJ) SWIG_DeleteCharArray(cptr);
+ }
+ return SWIG_TypeError;
+}
+}
+
+/* Char */
+
+%fragment(SWIG_From_frag(Char),"header",fragment=#SWIG_FromCharPtrAndSize) {
+SWIGINTERNINLINE SWIG_Object
+SWIG_From_dec(Char)(Char c)
+{
+ return SWIG_FromCharPtrAndSize(&c,1);
+}
+}
+
+%fragment(SWIG_AsVal_frag(Char),"header",
+ fragment="SWIG_As"#CharName"Array",
+ fragment=FragLimits,
+ fragment=SWIG_AsVal_frag(long)) {
+SWIGINTERN int
+SWIG_AsVal_dec(Char)(SWIG_Object obj, Char *val)
+{
+ int res = SWIG_As##CharName##Array(obj, val, 1);
+ if (!SWIG_IsOK(res)) {
+ long v;
+ res = SWIG_AddCast(SWIG_AsVal(long)(obj, &v));
+ if (SWIG_IsOK(res)) {
+ if ((CHAR_MIN <= v) && (v <= CHAR_MAX)) {
+ if (val) *val = %numeric_cast(v, Char);
+ } else {
+ res = SWIG_OverflowError;
+ }
+ }
+ }
+ return res;
+}
+}
+
+%_typemap_string(StringCode,
+ Char,
+ WarningLeakMsg,
+ SWIG_AsCharPtrAndSize,
+ SWIG_FromCharPtrAndSize,
+ SWIG_CharPtrLen,
+ SWIG_CharBufLen,
+ SWIG_As##CharName##Ptr,
+ SWIG_From##CharName##Ptr,
+ SWIG_As##CharName##Array,
+ SWIG_NewCopyCharArray,
+ SWIG_DeleteCharArray)
+
+%enddef
+#endif
+
+/* ------------------------------------------------------------
+ * String typemaps and fragments, with default allocators
+ * ------------------------------------------------------------ */
+
+%define %typemaps_string(StringCode, CharCode,
+ WarningLeakMsg,
+ Char, CharName,
+ SWIG_AsCharPtrAndSize,
+ SWIG_FromCharPtrAndSize,
+ SWIG_CharPtrLen,
+ SWIG_CharBufLen,
+ FragLimits, CHAR_MIN, CHAR_MAX)
+%_typemap2_string(StringCode, CharCode,
+ WarningLeakMsg,
+ Char, CharName,
+ SWIG_AsCharPtrAndSize,
+ SWIG_FromCharPtrAndSize,
+ SWIG_CharPtrLen,
+ SWIG_CharBufLen,
+ %new_copy_array,
+ %delete_array,
+ FragLimits, CHAR_MIN, CHAR_MAX)
+%enddef
+
+/* ------------------------------------------------------------
+ * String typemaps and fragments, with custom allocators
+ * ------------------------------------------------------------ */
+
+%define %typemaps_string_alloc(StringCode, CharCode,
+ WarningLeakMsg,
+ Char, CharName,
+ SWIG_AsCharPtrAndSize,
+ SWIG_FromCharPtrAndSize,
+ SWIG_CharPtrLen,
+ SWIG_CharBufLen,
+ SWIG_NewCopyCharArray,
+ SWIG_DeleteCharArray,
+ FragLimits, CHAR_MIN, CHAR_MAX)
+%_typemap2_string(StringCode, CharCode,
+ WarningLeakMsg,
+ Char, CharName,
+ SWIG_AsCharPtrAndSize,
+ SWIG_FromCharPtrAndSize,
+ SWIG_CharPtrLen,
+ SWIG_CharBufLen,
+ SWIG_NewCopyCharArray,
+ SWIG_DeleteCharArray,
+ FragLimits, CHAR_MIN, CHAR_MAX)
+%enddef
diff --git a/contrib/tools/swig/Lib/typemaps/swigmacros.swg b/contrib/tools/swig/Lib/typemaps/swigmacros.swg
new file mode 100644
index 0000000000..b772eb04b3
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/swigmacros.swg
@@ -0,0 +1,228 @@
+/* -----------------------------------------------------------------------------
+ * SWIG API. Portion only visible from SWIG
+ * ----------------------------------------------------------------------------- */
+/*
+ This file implements the internal macros of the 'SWIG API', which
+ are useful to implement all the SWIG target languages.
+
+ Basic preprocessor macros:
+ --------------------------
+
+ %arg(Arg) Safe argument wrap
+ %str(Arg) Stringify the argument
+ %begin_block Begin an execution block
+ %end_block End an execution block
+ %block(Block) Execute Block as an execution block
+ %define_as(Def, Val) Define 'Def' as 'Val', expanding Def and Val first
+ %ifcplusplus(V1, V2) if C++ Mode; then V1; else V2; fi
+
+
+ Casting Operations:
+ -------------------
+
+ SWIG provides the following casting macros, which implement the
+ corresponding C++ casting operations:
+
+ %const_cast(a, Type) const_cast<Type >(a)
+ %static_cast(a, Type) static_cast<Type >(a)
+ %reinterpret_cast(a, Type) reinterpret_cast<Type >(a)
+ %numeric_cast(a, Type) static_cast<Type >(a)
+ %as_voidptr(a) const_cast<void *>(static_cast<const void *>(a))
+ %as_voidptrptr(a) reinterpret_cast<void **>(a)
+
+ or their C unsafe versions. In C++ we use the safe version unless
+ SWIG_NO_CPLUSPLUS_CAST is defined
+
+
+ Memory allocation:
+ ------------------
+
+ These allocation/freeing macros are safe to use in C or C++ and
+ dispatch the proper new/delete/delete[] or free/malloc calls as
+ needed.
+
+ %new_instance(Type) Allocate a new instance of given Type
+ %new_copy(value,Type) Allocate and initialize a new instance with 'value'
+ %new_array(size,Type) Allocate a new array with given size and Type and zero initialize
+ %new_copy_array(cptr,size,Type) Allocate and initialize a new array from 'cptr'
+ %delete(cptr) Delete an instance
+ %delete_array(cptr) Delete an array
+
+
+ Auxiliary loop macros:
+ ----------------------
+
+ %formacro(Macro, Args...) or %formacro_1(Macro, Args...)
+ for i in Args
+ do
+ Macro($i)
+ done
+
+ %formacro_2(Macro2, Args...)
+ for i,j in Args
+ do
+ Macro2($i, $j)
+ done
+
+
+ Flags and conditional macros:
+ -----------------------------
+
+ %mark_flag(flag)
+ flag := True
+
+ %evalif(flag,expr)
+ if flag; then
+ expr
+ fi
+
+ %evalif_2(flag1 flag2,expr)
+ if flag1 and flag2; then
+ expr
+ fi
+
+
+*/
+/* -----------------------------------------------------------------------------
+ * Basic preprocessor macros
+ * ----------------------------------------------------------------------------- */
+
+#define %arg(Arg...) Arg
+#define %str(Arg) `Arg`
+#ifndef %begin_block
+# define %begin_block do {
+#endif
+#ifndef %end_block
+# define %end_block } while(0)
+#endif
+#define %block(Block...) %begin_block Block; %end_block
+
+/* define a new macro */
+%define %define_as(Def, Val...)%#define Def Val %enddef
+
+/* include C++ or else value */
+%define %ifcplusplus(cppval, nocppval)
+#ifdef __cplusplus
+cppval
+#else
+nocppval
+#endif
+%enddef
+
+/* -----------------------------------------------------------------------------
+ * Casting operators
+ * ----------------------------------------------------------------------------- */
+
+#if defined(__cplusplus) && !defined(SWIG_NO_CPLUSPLUS_CAST)
+# define %const_cast(a,Type...) const_cast< Type >(a)
+# define %static_cast(a,Type...) static_cast< Type >(a)
+# define %reinterpret_cast(a,Type...) reinterpret_cast< Type >(a)
+# define %numeric_cast(a,Type...) static_cast< Type >(a)
+#else /* C case */
+# define %const_cast(a,Type...) (Type)(a)
+# define %static_cast(a,Type...) (Type)(a)
+# define %reinterpret_cast(a,Type...) (Type)(a)
+# define %numeric_cast(a,Type...) (Type)(a)
+#endif /* __cplusplus */
+
+
+#define %as_voidptr(a) SWIG_as_voidptr(a)
+#define %as_voidptrptr(a) SWIG_as_voidptrptr(a)
+
+%insert("header") {
+%define_as(SWIG_as_voidptr(a), %const_cast(%static_cast(a,const void *), void *))
+%define_as(SWIG_as_voidptrptr(a), ((void)%as_voidptr(*a),%reinterpret_cast(a, void**)))
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Allocating/freeing elements
+ * ----------------------------------------------------------------------------- */
+
+#if defined(__cplusplus)
+# define %new_instance(Type...) (new Type())
+# define %new_copy(val,Type...) (new Type(%static_cast(val, const Type&)))
+# define %new_array(size,Type...) (new Type[size]())
+# define %new_copy_array(ptr,size,Type...) %reinterpret_cast(memcpy(new Type[size], ptr, sizeof(Type)*(size)), Type*)
+# define %delete(cptr) delete cptr
+# define %delete_array(cptr) delete[] cptr
+#else /* C case */
+# define %new_instance(Type...) (Type *)calloc(1,sizeof(Type))
+# define %new_copy(val,Type...) (Type *)memcpy(%new_instance(Type),&val,sizeof(Type))
+# define %new_array(size,Type...) (Type *)calloc(size, sizeof(Type))
+# define %new_copy_array(ptr,size,Type...) (Type *)memcpy(malloc((size)*sizeof(Type)), ptr, sizeof(Type)*(size))
+# define %delete(cptr) free((char*)cptr)
+# define %delete_array(cptr) free((char*)cptr)
+#endif /* __cplusplus */
+
+/* -----------------------------------------------------------------------------
+ * SWIG names and mangling
+ * ----------------------------------------------------------------------------- */
+
+#define %mangle(Type...) #@Type
+#define %descriptor(Type...) SWIGTYPE_ ## #@Type
+#define %string_name(Name) "SWIG_" %str(Name)
+#define %symbol_name(Name, Type...) SWIG_ ## Name ## _ #@Type
+#define %checkcode(Code) SWIG_TYPECHECK_ ## Code
+
+
+/* -----------------------------------------------------------------------------
+ * Auxiliary loop macros
+ * ----------------------------------------------------------------------------- */
+
+
+/* for loop for macro with one argument */
+%define %_formacro_1(macro, arg1,...)macro(arg1)
+#if #__VA_ARGS__ != "__fordone__"
+%_formacro_1(macro, __VA_ARGS__)
+#endif
+%enddef
+
+/* for loop for macro with one argument */
+%define %formacro_1(macro,...)%_formacro_1(macro,__VA_ARGS__,__fordone__)%enddef
+%define %formacro(macro,...)%_formacro_1(macro,__VA_ARGS__,__fordone__)%enddef
+
+/* for loop for macro with two arguments */
+%define %_formacro_2(macro, arg1, arg2, ...)macro(arg1, arg2)
+#if #__VA_ARGS__ != "__fordone__"
+%_formacro_2(macro, __VA_ARGS__)
+#endif
+%enddef
+
+/* for loop for macro with two arguments */
+%define %formacro_2(macro,...)%_formacro_2(macro, __VA_ARGS__, __fordone__)%enddef
+
+/* -----------------------------------------------------------------------------
+ * SWIG flags
+ * ----------------------------------------------------------------------------- */
+
+/*
+ mark a flag, ie, define a macro name but ignore it in
+ the interface.
+
+ the flag can be later used with %evalif
+*/
+
+%define %mark_flag(x) %define x 1 %enddef %enddef
+
+
+/*
+ %evalif and %evalif_2 are use to evaluate or process
+ an expression if the given predicate is 'true' (1).
+*/
+%define %_evalif(_x,_expr)
+#if _x == 1
+_expr
+#endif
+%enddef
+
+%define %_evalif_2(_x,_y,_expr)
+#if _x == 1 && _y == 1
+_expr
+#endif
+%enddef
+
+%define %evalif(_x,_expr...) %_evalif(%arg(_x),%arg(_expr)) %enddef
+
+%define %evalif_2(_x,_y,_expr...) %_evalif_2(%arg(_x),%arg(_y),%arg(_expr)) %enddef
+
diff --git a/contrib/tools/swig/Lib/typemaps/swigobject.swg b/contrib/tools/swig/Lib/typemaps/swigobject.swg
new file mode 100644
index 0000000000..26c6ba8edf
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/swigobject.swg
@@ -0,0 +1,37 @@
+/* ------------------------------------------------------------
+ * Language Object * - Just pass straight through unmodified
+ * ------------------------------------------------------------ */
+
+%typemap(in) SWIG_Object "$1 = $input;"
+
+%typemap(in,noblock=1) SWIG_Object const & ($*ltype temp)
+{
+ temp = %static_cast($input, $*ltype);
+ $1 = &temp;
+}
+
+%typemap(out,noblock=1) SWIG_Object {
+ %set_output($1);
+}
+
+%typemap(out,noblock=1) SWIG_Object const & {
+ %set_output(*$1);
+}
+
+%typecheck(SWIG_TYPECHECK_SWIGOBJECT) SWIG_Object "$1 = ($input != 0);";
+
+%typemap(throws,noblock=1) SWIG_Object {
+ %raise($1, "$type", 0);
+}
+
+%typemap(constcode,noblock=1) SWIG_Object {
+ %set_constant("$symname", $value);
+}
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+%typemap(directorin) SWIG_Object "$input = $1;"
+%typemap(directorout) SWIG_Object "$result = $input;"
+
+#endif /* SWIG_DIRECTOR_TYPEMAPS */
+
diff --git a/contrib/tools/swig/Lib/typemaps/swigtype.swg b/contrib/tools/swig/Lib/typemaps/swigtype.swg
new file mode 100644
index 0000000000..69f83794d2
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/swigtype.swg
@@ -0,0 +1,710 @@
+/* -----------------------------------------------------------------------------
+ * --- Input arguments ---
+ * ----------------------------------------------------------------------------- */
+/* Pointers and arrays */
+%typemap(in, noblock=1) SWIGTYPE *(void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp,$descriptor, $disown | %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$type", $symname, $argnum);
+ }
+ $1 = %reinterpret_cast(argp, $ltype);
+}
+%typemap(freearg) SWIGTYPE * ""
+
+%typemap(in, noblock=1) SWIGTYPE [] (void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp,$descriptor, $disown | %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$type", $symname, $argnum);
+ }
+ $1 = %reinterpret_cast(argp, $ltype);
+}
+%typemap(freearg) SWIGTYPE [] ""
+
+
+%typemap(in, noblock=1) SWIGTYPE *const& (void *argp = 0, int res = 0, $*1_ltype temp) {
+ res = SWIG_ConvertPtr($input, &argp, $*descriptor, $disown | %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$*ltype", $symname, $argnum);
+ }
+ temp = %reinterpret_cast(argp, $*ltype);
+ $1 = %reinterpret_cast(&temp, $1_ltype);
+}
+%typemap(freearg) SWIGTYPE *const& ""
+
+
+/* Reference */
+%typemap(in, noblock=1) SWIGTYPE & (void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp, $descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$type", $symname, $argnum);
+ }
+ if (!argp) { %argument_nullref("$type", $symname, $argnum); }
+ $1 = %reinterpret_cast(argp, $ltype);
+}
+%typemap(freearg) SWIGTYPE & ""
+
+#if defined(__cplusplus) && defined(%implicitconv_flag)
+%typemap(in,noblock=1,implicitconv=1) const SWIGTYPE & (void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp, $descriptor, %convertptr_flags | %implicitconv_flag);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$type", $symname, $argnum);
+ }
+ if (!argp) { %argument_nullref("$type", $symname, $argnum); }
+ $1 = %reinterpret_cast(argp, $ltype);
+}
+%typemap(freearg,noblock=1,match="in",implicitconv=1) const SWIGTYPE &
+{
+ if (SWIG_IsNewObj(res$argnum)) %delete($1);
+}
+#endif
+
+/* Rvalue reference */
+%typemap(in, noblock=1, fragment="<memory>") SWIGTYPE && (void *argp = 0, int res = 0, std::unique_ptr<$*1_ltype> rvrdeleter) {
+ res = SWIG_ConvertPtr($input, &argp, $descriptor, SWIG_POINTER_RELEASE | %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ if (res == SWIG_ERROR_RELEASE_NOT_OWNED) {
+ %releasenotowned_fail(res, "$type", $symname, $argnum);
+ } else {
+ %argument_fail(res, "$type", $symname, $argnum);
+ }
+ }
+ if (!argp) { %argument_nullref("$type", $symname, $argnum); }
+ $1 = %reinterpret_cast(argp, $ltype);
+ rvrdeleter.reset($1);
+}
+%typemap(freearg) SWIGTYPE && ""
+
+/* By value */
+#if defined(__cplusplus) && defined(%implicitconv_flag)
+%typemap(in,implicitconv=1) SWIGTYPE (void *argp, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp, $&descriptor, %convertptr_flags | %implicitconv_flag);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$type", $symname, $argnum);
+ }
+ if (!argp) {
+ %argument_nullref("$type", $symname, $argnum);
+ } else {
+ $&ltype temp = %reinterpret_cast(argp, $&ltype);
+ $1 = *temp;
+ if (SWIG_IsNewObj(res)) %delete(temp);
+ }
+}
+#else
+%typemap(in) SWIGTYPE (void *argp, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp, $&descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$type", $symname, $argnum);
+ }
+ if (!argp) {
+ %argument_nullref("$type", $symname, $argnum);
+ } else {
+ $1 = *(%reinterpret_cast(argp, $&ltype));
+ }
+}
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ * --- Output arguments ---
+ * ----------------------------------------------------------------------------- */
+
+/* Pointers, references */
+%typemap(out,noblock=1) SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE[] {
+ %set_output(SWIG_NewPointerObj(%as_voidptr($1), $descriptor, $owner | %newpointer_flags));
+}
+
+%typemap(out, noblock=1) SWIGTYPE *const& {
+ %set_output(SWIG_NewPointerObj(%as_voidptr(*$1), $*descriptor, $owner | %newpointer_flags));
+}
+
+/* Return by value */
+#ifdef __cplusplus
+%typemap(out, noblock=1) SWIGTYPE {
+ %set_output(SWIG_NewPointerObj((new $1_ltype($1)), $&descriptor, SWIG_POINTER_OWN | %newpointer_flags));
+}
+#else
+%typemap(out, noblock=1) SWIGTYPE {
+ %set_output(SWIG_NewPointerObj(%new_copy($1, $1_ltype), $&descriptor, SWIG_POINTER_OWN | %newpointer_flags));
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * --- Variable input ---
+ * ----------------------------------------------------------------------------- */
+
+/* memberin/globalin/varin, for fix arrays. */
+
+%typemap(memberin) SWIGTYPE [ANY] {
+ if ($input) {
+ size_t ii = 0;
+ for (; ii < (size_t)$1_dim0; ++ii) *($1_basetype *)&$1[ii] = *(($1_basetype *)$input + ii);
+ } else {
+ %variable_nullref("$type","$name");
+ }
+}
+
+%typemap(globalin) SWIGTYPE [ANY] {
+ if ($input) {
+ size_t ii = 0;
+ for (; ii < (size_t)$1_dim0; ++ii) *($1_basetype *)&$1[ii] = *(($1_basetype *)$input + ii);
+ } else {
+ %variable_nullref("$type","$name");
+ }
+}
+
+%typemap(varin) SWIGTYPE [ANY] {
+ $basetype *inp = 0;
+ int res = SWIG_ConvertPtr($input, %as_voidptrptr(&inp), $descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ } else if (inp) {
+ size_t ii = 0;
+ for (; ii < (size_t)$1_dim0; ++ii) *($1_basetype *)&$1[ii] = *(($1_basetype *)inp + ii);
+ } else {
+ %variable_nullref("$type", "$name");
+ }
+}
+
+
+/* memberin/globalin/varin, for fix double arrays. */
+
+%typemap(memberin) SWIGTYPE [ANY][ANY] {
+ if ($input) {
+ size_t ii = 0;
+ for (; ii < (size_t)$1_dim0; ++ii) {
+ if ($input[ii]) {
+ size_t jj = 0;
+ for (; jj < (size_t)$1_dim1; ++jj) $1[ii][jj] = $input[ii][jj];
+ } else {
+ %variable_nullref("$type","$name");
+ }
+ }
+ } else {
+ %variable_nullref("$type","$name");
+ }
+}
+
+%typemap(globalin) SWIGTYPE [ANY][ANY] {
+ if ($input) {
+ size_t ii = 0;
+ for (; ii < (size_t)$1_dim0; ++ii) {
+ if ($input[ii]) {
+ size_t jj = 0;
+ for (; jj < (size_t)$1_dim1; ++jj) $1[ii][jj] = $input[ii][jj];
+ } else {
+ %variable_nullref("$type","$name");
+ }
+ }
+ } else {
+ %variable_nullref("$type","$name");
+ }
+}
+
+%typemap(varin) SWIGTYPE [ANY][ANY] {
+ $basetype (*inp)[$1_dim1] = 0;
+ int res = SWIG_ConvertPtr($input, %as_voidptrptr(&inp), $descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ } else if (inp) {
+ size_t ii = 0;
+ for (; ii < (size_t)$1_dim0; ++ii) {
+ if (inp[ii]) {
+ size_t jj = 0;
+ for (; jj < (size_t)$1_dim1; ++jj) $1[ii][jj] = inp[ii][jj];
+ } else {
+ %variable_nullref("$type", "$name");
+ }
+ }
+ } else {
+ %variable_nullref("$type", "$name");
+ }
+}
+
+/* Pointers, references, and variable size arrays */
+
+%typemap(varin,warning=SWIGWARN_TYPEMAP_SWIGTYPELEAK_MSG) SWIGTYPE * {
+ void *argp = 0;
+ int res = SWIG_ConvertPtr($input, &argp, $descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ $1 = %reinterpret_cast(argp, $ltype);
+}
+
+%typemap(varin,noblock=1,warning="462:Unable to set dimensionless array variable") SWIGTYPE []
+{
+ %variable_fail(SWIG_AttributeError, "$type", "read-only $name");
+}
+
+%typemap(varin,warning=SWIGWARN_TYPEMAP_SWIGTYPELEAK_MSG) SWIGTYPE & {
+ void *argp = 0;
+ int res = SWIG_ConvertPtr($input, &argp, $descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ if (!argp) {
+ %variable_nullref("$type", "$name");
+ }
+ $1 = *(%reinterpret_cast(argp, $ltype));
+}
+
+%typemap(varin,warning=SWIGWARN_TYPEMAP_SWIGTYPELEAK_MSG) SWIGTYPE && {
+ void *argp = 0;
+ int res = SWIG_ConvertPtr($input, &argp, $descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ if (!argp) {
+ %variable_nullref("$type", "$name");
+ }
+ $1 = *(%reinterpret_cast(argp, $ltype));
+}
+
+#if defined(__cplusplus) && defined(%implicitconv_flag)
+%typemap(varin,implicitconv=1) SWIGTYPE {
+ void *argp = 0;
+ int res = SWIG_ConvertPtr($input, &argp, $&descriptor, %convertptr_flags | %implicitconv_flag);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ if (!argp) {
+ %variable_nullref("$type", "$name");
+ } else {
+ $&type temp;
+ temp = %reinterpret_cast(argp, $&type);
+ $1 = *temp;
+ if (SWIG_IsNewObj(res)) %delete(temp);
+ }
+}
+#else
+%typemap(varin) SWIGTYPE {
+ void *argp = 0;
+ int res = SWIG_ConvertPtr($input, &argp, $&descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ if (!argp) {
+ %variable_nullref("$type", "$name");
+ } else {
+ $1 = *(%reinterpret_cast(argp, $&type));
+ }
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * --- Variable output ---
+ * ----------------------------------------------------------------------------- */
+
+/* Pointers and arrays */
+%typemap(varout, noblock=1) SWIGTYPE * {
+ %set_varoutput(SWIG_NewPointerObj(%as_voidptr($1), $descriptor, %newpointer_flags));
+}
+
+%typemap(varout, noblock=1) SWIGTYPE [] {
+ %set_varoutput(SWIG_NewPointerObj(%as_voidptr($1), $descriptor, %newpointer_flags));
+}
+
+/* References */
+%typemap(varout, noblock=1) SWIGTYPE & {
+ %set_varoutput(SWIG_NewPointerObj(%as_voidptr(&$1), $descriptor, %newpointer_flags));
+}
+
+%typemap(varout, noblock=1) SWIGTYPE && {
+ %set_varoutput(SWIG_NewPointerObj(%as_voidptr(&$1), $descriptor, %newpointer_flags));
+}
+
+/* Value */
+%typemap(varout, noblock=1) SWIGTYPE {
+ %set_varoutput(SWIG_NewPointerObj(%as_voidptr(&$1), $&descriptor, %newpointer_flags));
+}
+
+/* ------------------------------------------------------------
+ * --- Typechecking rules ---
+ * ------------------------------------------------------------ */
+
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1) SWIGTYPE * {
+ void *vptr = 0;
+ int res = SWIG_ConvertPtr($input, &vptr, $descriptor, 0);
+ $1 = SWIG_CheckState(res);
+}
+
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1) SWIGTYPE *const& {
+ void *vptr = 0;
+ int res = SWIG_ConvertPtr($input, &vptr, $*descriptor, 0);
+ $1 = SWIG_CheckState(res);
+}
+
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1) SWIGTYPE & {
+ void *vptr = 0;
+ int res = SWIG_ConvertPtr($input, &vptr, $descriptor, SWIG_POINTER_NO_NULL);
+ $1 = SWIG_CheckState(res);
+}
+
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1) SWIGTYPE && {
+ void *vptr = 0;
+ int res = SWIG_ConvertPtr($input, &vptr, $descriptor, SWIG_POINTER_NO_NULL);
+ $1 = SWIG_CheckState(res);
+}
+
+#if defined(__cplusplus) && defined(%implicitconv_flag)
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1,implicitconv=1) const SWIGTYPE & {
+ int res = SWIG_ConvertPtr($input, 0, $descriptor, SWIG_POINTER_NO_NULL | %implicitconv_flag);
+ $1 = SWIG_CheckState(res);
+}
+
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1,implicitconv=1) const SWIGTYPE && {
+ int res = SWIG_ConvertPtr($input, 0, $descriptor, SWIG_POINTER_NO_NULL | %implicitconv_flag);
+ $1 = SWIG_CheckState(res);
+}
+
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1,implicitconv=1) SWIGTYPE {
+ int res = SWIG_ConvertPtr($input, 0, $&descriptor, SWIG_POINTER_NO_NULL | %implicitconv_flag);
+ $1 = SWIG_CheckState(res);
+}
+#else
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1) const SWIGTYPE & {
+ void *vptr = 0;
+ int res = SWIG_ConvertPtr($input, &vptr, $descriptor, SWIG_POINTER_NO_NULL);
+ $1 = SWIG_CheckState(res);
+}
+
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1) const SWIGTYPE && {
+ void *vptr = 0;
+ int res = SWIG_ConvertPtr($input, &vptr, $descriptor, SWIG_POINTER_NO_NULL);
+ $1 = SWIG_CheckState(res);
+}
+
+%typemap(typecheck,precedence=SWIG_TYPECHECK_POINTER,noblock=1) SWIGTYPE {
+ void *vptr = 0;
+ int res = SWIG_ConvertPtr($input, &vptr, $&descriptor, SWIG_POINTER_NO_NULL);
+ $1 = SWIG_CheckState(res);
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * --- Director typemaps --- *
+ * ----------------------------------------------------------------------------- */
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+/* directorin */
+
+%typemap(directorin,noblock=1) SWIGTYPE {
+ $input = SWIG_NewPointerObj((new $1_ltype(SWIG_STD_MOVE($1))), $&descriptor, SWIG_POINTER_OWN | %newpointer_flags);
+}
+
+%typemap(directorin,noblock=1) SWIGTYPE * {
+ $input = SWIG_NewPointerObj(%as_voidptr($1), $descriptor, %newpointer_flags);
+}
+
+%typemap(directorin,noblock=1) SWIGTYPE *const& {
+ $input = SWIG_NewPointerObj(%as_voidptr($1), $*descriptor, %newpointer_flags);
+}
+
+%typemap(directorin,noblock=1) SWIGTYPE & {
+ $input = SWIG_NewPointerObj(%as_voidptr(&$1), $descriptor, %newpointer_flags);
+}
+
+%typemap(directorin,noblock=1) SWIGTYPE && {
+ $input = SWIG_NewPointerObj(%as_voidptr(&$1_name), $descriptor, %newpointer_flags);
+}
+
+/* directorout */
+
+#if defined(__cplusplus) && defined(%implicitconv_flag)
+%typemap(directorout,noblock=1,implicitconv=1) SWIGTYPE (void * swig_argp, int swig_res = 0) {
+ swig_res = SWIG_ConvertPtr($input,&swig_argp,$&descriptor, %convertptr_flags | %implicitconv_flag);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res,"$type");
+ }
+ $result = *(%reinterpret_cast(swig_argp, $&ltype));
+ if (SWIG_IsNewObj(swig_res)) %delete(%reinterpret_cast(swig_argp, $&ltype));
+}
+#else
+%typemap(directorout,noblock=1) SWIGTYPE (void * swig_argp, int swig_res = 0) {
+ swig_res = SWIG_ConvertPtr($input,&swig_argp,$&descriptor, %convertptr_flags);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res,"$type");
+ }
+ $result = *(%reinterpret_cast(swig_argp, $&ltype));
+}
+#endif
+
+%typemap(directorout,noblock=1,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG)
+ SWIGTYPE *(void *swig_argp, int swig_res, swig_owntype own) {
+ swig_res = SWIG_ConvertPtrAndOwn($input, &swig_argp, $descriptor, %convertptr_flags | SWIG_POINTER_DISOWN, &own);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res,"$type");
+ }
+ $result = %reinterpret_cast(swig_argp, $ltype);
+ swig_acquire_ownership_obj(%as_voidptr($result), own /* & TODO: SWIG_POINTER_OWN */);
+}
+%typemap(directorfree,noblock=1,match="directorout") SWIGTYPE * {
+ if (director) {
+ SWIG_AcquirePtr($result, director->swig_release_ownership(%as_voidptr($input)));
+ }
+}
+
+%typemap(directorout,noblock=1,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG)
+ SWIGTYPE *const&(void *swig_argp, int swig_res, swig_owntype own) {
+ swig_res = SWIG_ConvertPtrAndOwn($input, &swig_argp, $*descriptor, %convertptr_flags | SWIG_POINTER_DISOWN, &own);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res,"$type");
+ }
+ $1_ltype swig_temp = new $*1_ltype(($*1_ltype)swig_argp);
+ swig_acquire_ownership(swig_temp);
+ $result = swig_temp;
+}
+%typemap(directorfree,noblock=1,match="directorout") SWIGTYPE *const& {
+ if (director) {
+ SWIG_AcquirePtr($result, director->swig_release_ownership(%as_voidptr(*$input)));
+ }
+}
+
+%typemap(directorout,noblock=1,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG)
+ SWIGTYPE &(void *swig_argp, int swig_res, swig_owntype own) {
+ swig_res = SWIG_ConvertPtrAndOwn($input, &swig_argp, $descriptor, %convertptr_flags | SWIG_POINTER_DISOWN, &own);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res,"$type");
+ }
+ if (!swig_argp) { %dirout_nullref("$type"); }
+ $result = %reinterpret_cast(swig_argp, $ltype);
+ swig_acquire_ownership_obj(%as_voidptr($result), own /* & TODO: SWIG_POINTER_OWN */);
+}
+%typemap(directorfree,noblock=1,match="directorout") SWIGTYPE & {
+ if (director) {
+ SWIG_AcquirePtr($result, director->swig_release_ownership(%as_voidptr($input)));
+ }
+}
+
+%typemap(directorout,noblock=1,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG)
+ SWIGTYPE &&(void *swig_argp, int swig_res, swig_owntype own) {
+ swig_res = SWIG_ConvertPtrAndOwn($input, &swig_argp, $descriptor, %convertptr_flags | SWIG_POINTER_DISOWN, &own);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res,"$type");
+ }
+ if (!swig_argp) { %dirout_nullref("$type"); }
+ $result = %reinterpret_cast(swig_argp, $ltype);
+ swig_acquire_ownership_obj(%as_voidptr($result), own /* & TODO: SWIG_POINTER_OWN */);
+}
+%typemap(directorfree,noblock=1,match="directorout") SWIGTYPE && {
+ if (director) {
+ SWIG_AcquirePtr($result, director->swig_release_ownership(%as_voidptr($input)));
+ }
+}
+
+#endif /* SWIG_DIRECTOR_TYPEMAPS */
+
+
+/* ------------------------------------------------------------
+ * --- Constants ---
+ * ------------------------------------------------------------ */
+
+%typemap(constcode,noblock=1) SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [] {
+ %set_constant("$symname", SWIG_NewPointerObj(%as_voidptr($value),$descriptor,%newpointer_flags));
+}
+
+%typemap(constcode,noblock=1) SWIGTYPE {
+ %set_constant("$symname", SWIG_NewPointerObj(%as_voidptr(&$value),$&descriptor,%newpointer_flags));
+}
+
+/* ------------------------------------------------------------
+ * --- Exception handling ---
+ * ------------------------------------------------------------ */
+
+%typemap(throws,noblock=1) SWIGTYPE {
+ %raise(SWIG_NewPointerObj(%new_copy($1, $1_ltype),$&descriptor,SWIG_POINTER_OWN), "$type", $&descriptor);
+}
+
+%typemap(throws,noblock=1) SWIGTYPE * {
+ %raise(SWIG_NewPointerObj(%as_voidptr($1),$descriptor,0), "$type", $descriptor);
+}
+
+%typemap(throws,noblock=1) SWIGTYPE [ANY] {
+ %raise(SWIG_NewPointerObj(%as_voidptr($1),$descriptor,0), "$type", $descriptor);
+}
+
+%typemap(throws,noblock=1) SWIGTYPE & {
+ %raise(SWIG_NewPointerObj(%as_voidptr(&$1),$descriptor,0), "$type", $descriptor);
+}
+
+%typemap(throws,noblock=1) SWIGTYPE && {
+ %raise(SWIG_NewPointerObj(%as_voidptr(&$1),$descriptor,0), "$type", $descriptor);
+}
+
+%typemap(throws,noblock=1) (...) {
+ SWIG_exception_fail(SWIG_RuntimeError,"unknown exception");
+}
+
+/* ------------------------------------------------------------
+ * --- CLASS::* typemaps ---
+ * ------------------------------------------------------------ */
+
+%typemap(in) SWIGTYPE (CLASS::*) {
+ int res = SWIG_ConvertMember($input, %as_voidptr(&$1), sizeof($1),$descriptor);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+}
+
+%typemap(out,noblock=1) SWIGTYPE (CLASS::*) {
+ %set_output(SWIG_NewMemberObj(%as_voidptr(&$1), sizeof($1), $descriptor));
+}
+
+%typemap(varin) SWIGTYPE (CLASS::*) {
+ int res = SWIG_ConvertMember($input,%as_voidptr(&$1), sizeof($1), $descriptor);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+}
+
+%typemap(varout,noblock=1) SWIGTYPE (CLASS::*) {
+ %set_varoutput(SWIG_NewMemberObj(%as_voidptr(&$1), sizeof($1), $descriptor));
+}
+
+%typemap(constcode,noblock=1) SWIGTYPE (CLASS::*) {
+ %set_constant("$symname", SWIG_NewMemberObj(%as_voidptr(&$value), sizeof($value), $descriptor));
+}
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+/* directorin */
+
+%typemap(directorin,noblock=1) SWIGTYPE (CLASS::*) {
+ $input = SWIG_NewMemberObj(%as_voidptr(&$1), sizeof($1), $descriptor);
+}
+
+/* directorout */
+
+%typemap(directorout) SWIGTYPE (CLASS::*) {
+ int swig_res = SWIG_ConvertMember($input,%as_voidptr(&$result), sizeof($result), $descriptor);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res,"$type");
+ }
+}
+#endif
+
+%apply SWIGTYPE (CLASS::*) { SWIGTYPE (CLASS::*const) }
+%apply SWIGTYPE & { SWIGTYPE (CLASS::*const&) }
+
+/* ------------------------------------------------------------
+ * --- function ptr typemaps ---
+ * ------------------------------------------------------------ */
+
+/*
+ ISO C++ doesn't allow direct casting of a function ptr to a object
+ ptr. So, maybe the ptr sizes are not the same, and we need to take
+ some providences.
+ */
+%typemap(in) SWIGTYPE ((*)(ANY)) {
+ int res = SWIG_ConvertFunctionPtr($input, (void**)(&$1), $descriptor);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type",$symname, $argnum);
+ }
+}
+
+%typecheck(SWIG_TYPECHECK_POINTER,noblock=1) SWIGTYPE ((*)(ANY)) {
+ void *ptr = 0;
+ int res = SWIG_ConvertFunctionPtr($input, &ptr, $descriptor);
+ $1 = SWIG_CheckState(res);
+}
+
+
+%typemap(out, noblock=1) SWIGTYPE ((*)(ANY)) {
+ %set_output(SWIG_NewFunctionPtrObj((void *)($1), $descriptor));
+}
+
+%typemap(varin) SWIGTYPE ((*)(ANY)) {
+ int res = SWIG_ConvertFunctionPtr($input, (void**)(&$1), $descriptor);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+}
+
+%typemap(varout,noblock=1) SWIGTYPE ((*)(ANY)) {
+ %set_varoutput(SWIG_NewFunctionPtrObj((void *)($1), $descriptor));
+}
+
+%typemap(constcode, noblock=1) SWIGTYPE ((*)(ANY)){
+ %set_constant("$symname", SWIG_NewFunctionPtrObj((void *)$value, $descriptor));
+}
+%typemap(constcode) SWIGTYPE ((* const)(ANY)) = SWIGTYPE ((*)(ANY));
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+/* directorin */
+
+%typemap(directorin,noblock=1) SWIGTYPE ((*)(ANY)) {
+ $input = SWIG_NewFunctionPtrObj((void*)($1), $descriptor);
+}
+
+/* directorout */
+
+%typemap(directorout) SWIGTYPE ((*)(ANY)) {
+ int swig_res = SWIG_ConvertFunctionPtr($input,(void**)(&$result),$descriptor);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res,"$type");
+ }
+}
+#endif
+
+%apply SWIGTYPE ((*)(ANY)) { SWIGTYPE ((* const)(ANY)) }
+
+%apply SWIGTYPE * { SWIGTYPE *const }
+
+/* ------------------------------------------------------------
+ * --- Special typemaps ---
+ * ------------------------------------------------------------ */
+
+/* DISOWN typemap */
+
+%typemap(in, noblock=1) SWIGTYPE *DISOWN (int res = 0) {
+ res = SWIG_ConvertPtr($input, %as_voidptrptr(&$1), $descriptor, SWIG_POINTER_DISOWN | %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res,"$type", $symname, $argnum);
+ }
+}
+
+%typemap(varin) SWIGTYPE *DISOWN {
+ void *temp = 0;
+ int res = SWIG_ConvertPtr($input, &temp, $descriptor, SWIG_POINTER_DISOWN | %convertptr_flags);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ $1 = ($ltype) temp;
+}
+
+/* DYNAMIC typemap */
+
+%typemap(out,noblock=1) SWIGTYPE *DYNAMIC, SWIGTYPE &DYNAMIC {
+ %set_output(SWIG_NewPointerObj(%as_voidptr($1), SWIG_TypeDynamicCast($descriptor, %as_voidptrptr(&$1)), $owner | %newpointer_flags));
+}
+
+/* INSTANCE typemap */
+
+#ifdef __cplusplus
+%typemap(out,noblock=1) SWIGTYPE INSTANCE {
+ %set_output(SWIG_NewInstanceObj((new $1_ltype($1)), $&1_descriptor, SWIG_POINTER_OWN | %newinstance_flags));
+}
+#else
+%typemap(out,noblock=1) SWIGTYPE INSTANCE {
+ %set_output(SWIG_NewInstanceObj(%new_copy($1, $1_ltype), $&1_descriptor, SWIG_POINTER_OWN | %newinstance_flags));
+}
+#endif
+
+%typemap(out,noblock=1) SWIGTYPE *INSTANCE, SWIGTYPE &INSTANCE, SWIGTYPE INSTANCE[] {
+ %set_output(SWIG_NewInstanceObj(%as_voidptr($1), $1_descriptor, $owner | %newinstance_flags));
+}
+
+%typemap(varout,noblock=1) SWIGTYPE *INSTANCE, SWIGTYPE INSTANCE[] {
+ %set_varoutput(SWIG_NewInstanceObj(%as_voidptr($1), $1_descriptor, %newinstance_flags));
+}
+
+%typemap(varout,noblock=1) SWIGTYPE &INSTANCE {
+ %set_varoutput(SWIG_NewInstanceObj(%as_voidptr($1), $1_descriptor, %newinstance_flags));
+}
+
+%typemap(varout,noblock=1) SWIGTYPE INSTANCE {
+ %set_varoutput(SWIG_NewInstanceObj(%as_voidptr(&$1), $&1_descriptor, %newinstance_flags));
+}
+
diff --git a/contrib/tools/swig/Lib/typemaps/swigtypemaps.swg b/contrib/tools/swig/Lib/typemaps/swigtypemaps.swg
new file mode 100644
index 0000000000..733e5acd0f
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/swigtypemaps.swg
@@ -0,0 +1,168 @@
+/* -----------------------------------------------------------------------------
+ * swigtypemaps.swg
+ *
+ * Unified Typemap Library frontend
+ * ----------------------------------------------------------------------------- */
+
+/*
+ This file provides the frontend to the Unified Typemap Library.
+
+ When using this library in a SWIG target language, you need to
+ define a minimum set of fragments, specialize a couple of macros,
+ and then include this file.
+
+ Typically you will create a 'mytypemaps.swg' file in each target
+ language, where you will have the following sections:
+
+ === mytypemaps.swg ===
+
+ // Fragment section
+ %include <typemaps/fragments.swg>
+ <include target language fragments>
+
+ // Unified typemap section
+ <specialized the typemap library macros>
+ %include <typemaps/swigtypemaps.swg>
+
+ // Local typemap section
+ <add/replace extra target language typemaps>
+
+ === mytypemaps.swg ===
+
+ While we add more docs, please take a look at the following cases
+ to see how you specialized the unified typemap library for a new
+ target language:
+
+ Lib/python/pytypemaps.swg
+ Lib/tcl/tcltypemaps.swg
+ Lib/ruby/rubytypemaps.swg
+ Lib/perl5/perltypemaps.swg
+
+*/
+
+#define SWIGUTL SWIGUTL
+
+/* -----------------------------------------------------------------------------
+ * Language specialization section.
+ *
+ * Tune these macros for each language as needed.
+ * ----------------------------------------------------------------------------- */
+
+/*
+ The SWIG target language object must be provided.
+ For example in python you define:
+
+ #define SWIG_Object PyObject *
+*/
+
+#if !defined(SWIG_Object)
+#error "SWIG_Object must be defined as the SWIG target language object"
+#endif
+
+/*==== flags for new/convert methods ====*/
+
+
+#ifndef %convertptr_flags
+%define %convertptr_flags 0 %enddef
+#endif
+
+#ifndef %newpointer_flags
+%define %newpointer_flags 0 %enddef
+#endif
+
+#ifndef %newinstance_flags
+%define %newinstance_flags 0 %enddef
+#endif
+
+/*==== set output ====*/
+
+#ifndef %set_output
+/* simple set output operation */
+#define %set_output(obj) $result = obj
+#endif
+
+/*==== set variable output ====*/
+
+#ifndef %set_varoutput
+/* simple set varoutput operation */
+#define %set_varoutput(obj) $result = obj
+#endif
+
+/*==== append output ====*/
+
+#ifndef %append_output
+#if defined(SWIG_AppendOutput)
+/* simple append operation */
+#define %append_output(obj) $result = SWIG_AppendOutput($result,obj)
+#else
+#error "Language must define SWIG_AppendOutput or %append_output"
+#endif
+#endif
+
+/*==== set constant ====*/
+
+#ifndef %set_constant
+#if defined(SWIG_SetConstant)
+/* simple set constant operation */
+#define %set_constant(name,value) SWIG_SetConstant(name,value)
+#else
+#error "Language must define SWIG_SetConstant or %set_constant"
+#endif
+#endif
+
+/*==== raise an exception ====*/
+
+#ifndef %raise
+#if defined(SWIG_Raise)
+/* simple raise operation */
+#define %raise(obj, type, desc) SWIG_Raise(obj, type, desc); SWIG_fail
+#else
+#error "Language must define SWIG_Raise or %raise"
+#endif
+#endif
+
+/*==== director output exception ====*/
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+#ifndef SWIG_DirOutFail
+#define SWIG_DirOutFail(code, msg) Swig::DirectorTypeMismatchException::raise(SWIG_ErrorType(code), msg)
+#endif
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ * Language independent definitions
+ * ----------------------------------------------------------------------------- */
+
+#define %error_block(Block...) %block(Block)
+#define %default_code(code) SWIG_ArgError(code)
+#define %argument_fail(code, type, name, argn) SWIG_exception_fail(%default_code(code), %argfail_fmt(type, name, argn))
+#define %argument_nullref(type, name, argn) SWIG_exception_fail(SWIG_ValueError, %argnullref_fmt(type, name, argn))
+#define %variable_fail(code, type, name) SWIG_exception_fail(%default_code(code), %varfail_fmt(type, name))
+#define %variable_nullref(type, name) SWIG_exception_fail(SWIG_ValueError, %varnullref_fmt(type, name))
+#define %releasenotowned_fail(code, type, name, argn) SWIG_exception_fail(%default_code(code), %releasenotownedfail_fmt(type, name, argn))
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+#define %dirout_fail(code, type) SWIG_DirOutFail(%default_code(code), %outfail_fmt(type))
+#define %dirout_nullref(type) SWIG_DirOutFail(SWIG_ValueError, %outnullref_fmt(type))
+#endif
+
+/* -----------------------------------------------------------------------------
+ * All the typemaps
+ * ----------------------------------------------------------------------------- */
+
+
+%include <typemaps/fragments.swg>
+%include <typemaps/exception.swg>
+%include <typemaps/swigtype.swg>
+%include <typemaps/void.swg>
+%include <typemaps/swigobject.swg>
+%include <typemaps/valtypes.swg>
+%include <typemaps/ptrtypes.swg>
+%include <typemaps/inoutlist.swg>
+%include <typemaps/primtypes.swg>
+%include <typemaps/string.swg>
+%include <typemaps/misctypes.swg>
+%include <typemaps/enumint.swg>
+
+
diff --git a/contrib/tools/swig/Lib/typemaps/typemaps.swg b/contrib/tools/swig/Lib/typemaps/typemaps.swg
new file mode 100644
index 0000000000..4629e8dfa0
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/typemaps.swg
@@ -0,0 +1,157 @@
+/* -----------------------------------------------------------------------------
+ * typemaps.swg
+ *
+ * Tcl Pointer handling
+ *
+ * These mappings provide support for input/output arguments and common
+ * uses for C/C++ pointers.
+ * ----------------------------------------------------------------------------- */
+
+// INPUT typemaps.
+// These remap a C pointer to be an "INPUT" value which is passed by value
+// instead of reference.
+
+/*
+The following methods can be applied to turn a pointer into a simple
+"input" value. That is, instead of passing a pointer to an object,
+you would use a real value instead.
+
+ int *INPUT
+ short *INPUT
+ long *INPUT
+ long long *INPUT
+ unsigned int *INPUT
+ unsigned short *INPUT
+ unsigned long *INPUT
+ unsigned long long *INPUT
+ unsigned char *INPUT
+ bool *INPUT
+ float *INPUT
+ double *INPUT
+
+To use these, suppose you had a C function like this :
+
+ double fadd(double *a, double *b) {
+ return *a+*b;
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ double fadd(double *INPUT, double *INPUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *INPUT { double *a, double *b };
+ double fadd(double *a, double *b);
+
+*/
+
+// OUTPUT typemaps. These typemaps are used for parameters that
+// are output only. The output value is appended to the result as
+// a list element.
+
+/*
+The following methods can be applied to turn a pointer into an "output"
+value. When calling a function, no input value would be given for
+a parameter, but an output value would be returned. In the case of
+multiple output values, they are returned in the form of a Tcl tuple.
+
+ int *OUTPUT
+ short *OUTPUT
+ long *OUTPUT
+ long long *OUTPUT
+ unsigned int *OUTPUT
+ unsigned short *OUTPUT
+ unsigned long *OUTPUT
+ unsigned long long *OUTPUT
+ unsigned char *OUTPUT
+ bool *OUTPUT
+ float *OUTPUT
+ double *OUTPUT
+
+For example, suppose you were trying to wrap the modf() function in the
+C math library which splits x into integral and fractional parts (and
+returns the integer part in one of its parameters).K:
+
+ double modf(double x, double *ip);
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ double modf(double x, double *OUTPUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *OUTPUT { double *ip };
+ double modf(double x, double *ip);
+
+The Tcl output of the function would be a tuple containing both
+output values.
+
+*/
+
+// INOUT
+// Mappings for an argument that is both an input and output
+// parameter
+
+/*
+The following methods can be applied to make a function parameter both
+an input and output value. This combines the behavior of both the
+"INPUT" and "OUTPUT" methods described earlier. Output values are
+returned in the form of a Tcl tuple.
+
+ int *INOUT
+ short *INOUT
+ long *INOUT
+ long long *INOUT
+ unsigned int *INOUT
+ unsigned short *INOUT
+ unsigned long *INOUT
+ unsigned long long *INOUT
+ unsigned char *INOUT
+ bool *INOUT
+ float *INOUT
+ double *INOUT
+
+For example, suppose you were trying to wrap the following function :
+
+ void neg(double *x) {
+ *x = -(*x);
+ }
+
+You could wrap it with SWIG as follows :
+
+ %include <typemaps.i>
+ void neg(double *INOUT);
+
+or you can use the %apply directive :
+
+ %include <typemaps.i>
+ %apply double *INOUT { double *x };
+ void neg(double *x);
+
+Unlike C, this mapping does not directly modify the input value (since
+this makes no sense in Tcl). Rather, the modified input value shows
+up as the return value of the function. Thus, to apply this function
+to a Tcl variable you might do this :
+
+ x = neg(x)
+
+Note : previous versions of SWIG used the symbol 'BOTH' to mark
+input/output arguments. This is still supported, but will be slowly
+phased out in future releases.
+
+*/
+
+
+#if defined(SWIG_INOUT_NODEF)
+
+%apply_checkctypes(%typemaps_inoutn)
+
+%apply size_t& { std::size_t& };
+%apply ptrdiff_t& { std::ptrdiff_t& };
+
+#endif
diff --git a/contrib/tools/swig/Lib/typemaps/valtypes.swg b/contrib/tools/swig/Lib/typemaps/valtypes.swg
new file mode 100644
index 0000000000..f2f34acfca
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/valtypes.swg
@@ -0,0 +1,215 @@
+/*---------------------------------------------------------------------
+ * Value typemaps (Type, const Type&) for value types, such as
+ * fundamental types (int, double), that define the AsVal/From
+ * methods.
+ *
+ * To apply them, just use one of the following macros:
+ *
+ * %typemaps_from(FromMeth, FromFrag, Type)
+ * %typemaps_asval(CheckCode, AsValMeth, AsValFrag, Type)
+ * %typemaps_asvalfrom(CheckCode, AsValMeth, FromMeth, AsValFrag, FromFrag, Type)
+ *
+ * or the simpler and normalize form:
+ *
+ * %typemaps_asvalfromn(CheckCode, Type)
+ *
+ * Also, you can use the individual typemap definitions:
+ *
+ * %value_in_typemap(asval_meth,frag,Type)
+ * %value_varin_typemap(asval_meth,frag,Type)
+ * %value_typecheck_typemap(checkcode,asval_meth,frag,Type)
+ * %value_directorout_typemap(asval_meth,frag,Type)
+ *
+ * %value_out_typemap(from_meth,frag,Type)
+ * %value_varout_typemap(from_meth,frag,Type)
+ * %value_constcode_typemap(from_meth,frag,Type)
+ * %value_directorin_typemap(from_meth,frag,Type)
+ * %value_throws_typemap(from_meth,frag,Type)
+ *
+ *---------------------------------------------------------------------*/
+
+/* in */
+
+%define %value_in_typemap(asval_meth,frag,Type...)
+ %typemap(in,noblock=1,fragment=frag) Type (Type val, int ecode = 0) {
+ ecode = asval_meth($input, &val);
+ if (!SWIG_IsOK(ecode)) {
+ %argument_fail(ecode, "$ltype", $symname, $argnum);
+ }
+ $1 = %static_cast(val,$ltype);
+ }
+ %typemap(freearg) Type ""
+ %typemap(in,noblock=1,fragment=frag) const Type & ($*ltype temp, Type val, int ecode = 0) {
+ ecode = asval_meth($input, &val);
+ if (!SWIG_IsOK(ecode)) {
+ %argument_fail(ecode, "$*ltype", $symname, $argnum);
+ }
+ temp = %static_cast(val, $*ltype);
+ $1 = &temp;
+ }
+ %typemap(freearg) const Type& ""
+%enddef
+
+/* out */
+
+%define %value_out_typemap(from_meth,frag,Type...)
+ %typemap(out,noblock=1,fragment=frag) Type, const Type {
+ %set_output(from_meth(%static_cast($1,Type)));
+ }
+ %typemap(out,noblock=1,fragment=frag) const Type& {
+ %set_output(from_meth(%static_cast(*$1,Type)));
+ }
+%enddef
+
+/* varin */
+
+%define %value_varin_typemap(asval_meth,frag,Type...)
+ %typemap(varin,fragment=frag) Type {
+ Type val;
+ int res = asval_meth($input, &val);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ $1 = %static_cast(val,$ltype);
+ }
+%enddef
+
+/* varout */
+
+%define %value_varout_typemap(from_meth,frag,Type...)
+ %typemap(varout,noblock=1,fragment=frag) Type, const Type& {
+ %set_varoutput(from_meth(%static_cast($1,Type)));
+ }
+%enddef
+
+/* constant installation code */
+
+%define %value_constcode_typemap(from_meth,frag,Type...)
+ %typemap(constcode,noblock=1,fragment=frag) Type {
+ %set_constant("$symname", from_meth(%static_cast($value,Type)));
+ }
+%enddef
+
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+/* directorin */
+
+%define %value_directorin_typemap(from_meth,frag,Type...)
+ %typemap(directorin,noblock=1,fragment=frag) Type *DIRECTORIN {
+ $input = from_meth(%static_cast(*$1,Type));
+ }
+ %typemap(directorin,noblock=1,fragment=frag) Type, const Type& {
+ $input = from_meth(%static_cast($1,Type));
+ }
+%enddef
+
+/* directorout */
+
+%define %value_directorout_typemap(asval_meth,frag,Type...)
+ %typemap(directorargout,noblock=1,fragment=frag) Type *DIRECTOROUT(Type swig_val, int swig_res) {
+ swig_res = asval_meth($result, &swig_val);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res, "$type");
+ }
+ *$1 = swig_val;
+ }
+ %typemap(directorout,noblock=1,fragment=frag) Type {
+ Type swig_val;
+ int swig_res = asval_meth($input, &swig_val);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res, "$type");
+ }
+ $result = %static_cast(swig_val,$type);
+ }
+ %typemap(directorout,noblock=1,fragment=frag,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) const Type& {
+ Type swig_val;
+ int swig_res = asval_meth($input, &swig_val);
+ if (!SWIG_IsOK(swig_res)) {
+ %dirout_fail(swig_res, "$type");
+ }
+ $basetype *temp = new $basetype(($basetype)swig_val);
+ swig_acquire_ownership(temp);
+ $result = temp;
+ }
+ %typemap(directorfree,noblock=1) const Type & {
+ if (director) {
+ director->swig_release_ownership(%as_voidptr($input));
+ }
+ }
+ %typemap(directorout,fragment=frag) Type &DIRECTOROUT = Type
+%enddef
+
+#else
+
+#define %value_directorin_typemap(from_meth,frag,Type...)
+#define %value_directorout_typemap(asval_meth,frag,Type...)
+
+#endif /* SWIG_DIRECTOR_TYPEMAPS */
+
+
+/* throws */
+
+%define %value_throws_typemap(from_meth,frag,Type...)
+ %typemap(throws,noblock=1,fragment=frag) Type {
+ %raise(from_meth(%static_cast($1,Type)), "$type", 0);
+ }
+%enddef
+
+/* typecheck */
+
+%define %value_typecheck_typemap(check,asval_meth,frag,Type...)
+ %typemap(typecheck,precedence=check,fragment=frag) Type, const Type& {
+ int res = asval_meth($input, NULL);
+ $1 = SWIG_CheckState(res);
+ }
+%enddef
+
+/*---------------------------------------------------------------------
+ * typemap definition for types with AsVal methods
+ *---------------------------------------------------------------------*/
+%define %typemaps_asval(CheckCode, AsValMeth, AsValFrag, Type...)
+ %value_in_typemap(%arg(AsValMeth), %arg(AsValFrag), Type);
+ %value_varin_typemap(%arg(AsValMeth), %arg(AsValFrag), Type);
+ %value_directorout_typemap(%arg(AsValMeth), %arg(AsValFrag), Type);
+ %value_typecheck_typemap(%arg(CheckCode), %arg(AsValMeth), %arg(AsValFrag), Type);
+ %value_input_typemap(%arg(CheckCode), %arg(AsValMeth), %arg(AsValFrag), Type);
+%enddef
+
+
+/*---------------------------------------------------------------------
+ * typemap definition for types with from method
+ *---------------------------------------------------------------------*/
+%define %typemaps_from(FromMeth, FromFrag, Type...)
+ %value_out_typemap(%arg(FromMeth), %arg(FromFrag), Type);
+ %value_varout_typemap(%arg(FromMeth), %arg(FromFrag), Type);
+ %value_constcode_typemap(%arg(FromMeth), %arg(FromFrag), Type);
+ %value_directorin_typemap(%arg(FromMeth), %arg(FromFrag), Type);
+ %value_throws_typemap(%arg(FromMeth), %arg(FromFrag), Type);
+ %value_output_typemap(%arg(FromMeth), %arg(FromFrag), Type);
+%enddef
+
+
+/*---------------------------------------------------------------------
+ * typemap definition for types with alval/from method
+ *---------------------------------------------------------------------*/
+
+%define %typemaps_asvalfrom(CheckCode, AsValMeth, FromMeth,
+ AsValFrag, FromFrag, Type...)
+ %typemaps_asval(%arg(CheckCode), %arg(AsValMeth), %arg(AsValFrag), Type);
+ %typemaps_from(%arg(FromMeth), %arg(FromFrag), Type);
+ %value_inout_typemap(Type);
+%enddef
+
+
+/*---------------------------------------------------------------------
+ * typemap definition for types with for 'normalized' asval/from methods
+ *---------------------------------------------------------------------*/
+%define %typemaps_asvalfromn(CheckCode, Type...)
+ %typemaps_asvalfrom(%arg(CheckCode),
+ SWIG_AsVal(Type),
+ SWIG_From(Type),
+ %arg(SWIG_AsVal_frag(Type)),
+ %arg(SWIG_From_frag(Type)),
+ Type);
+%enddef
diff --git a/contrib/tools/swig/Lib/typemaps/void.swg b/contrib/tools/swig/Lib/typemaps/void.swg
new file mode 100644
index 0000000000..795992bf48
--- /dev/null
+++ b/contrib/tools/swig/Lib/typemaps/void.swg
@@ -0,0 +1,84 @@
+/* ------------------------------------------------------------
+ * Void * - Accepts any kind of pointer
+ * ------------------------------------------------------------ */
+
+/* in */
+
+%typemap(in,noblock=1) void * (int res) {
+ res = SWIG_ConvertPtr($input,%as_voidptrptr(&$1), 0, $disown);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "$type", $symname, $argnum);
+ }
+}
+%typemap(freearg) void * ""
+
+%typemap(in,noblock=1) void * const& ($*ltype temp = 0, int res) {
+ res = SWIG_ConvertPtr($input, %as_voidptrptr(&temp), 0, $disown);
+ if (!SWIG_IsOK(res)) {
+ %argument_fail(res, "Stype", $symname, $argnum);
+ }
+ $1 = &temp;
+}
+%typemap(freearg) void * const& ""
+
+
+/* out */
+
+#if defined(VOID_Object)
+%typemap(out,noblock=1) void { $result = VOID_Object; }
+#else
+%typemap(out,noblock=1) void {}
+#endif
+
+/* varin */
+
+%typemap(varin) void * {
+ void *temp = 0;
+ int res = SWIG_ConvertPtr($input, &temp, 0, SWIG_POINTER_DISOWN);
+ if (!SWIG_IsOK(res)) {
+ %variable_fail(res, "$type", "$name");
+ }
+ $1 = ($1_ltype) temp;
+}
+
+/* typecheck */
+
+%typecheck(SWIG_TYPECHECK_VOIDPTR, noblock=1) void *
+{
+ void *ptr = 0;
+ int res = SWIG_ConvertPtr($input, &ptr, 0, 0);
+ $1 = SWIG_CheckState(res);
+}
+
+#if defined(SWIG_DIRECTOR_TYPEMAPS)
+
+/* directorin */
+
+%typemap(directorin,noblock=1) void *, void const*, void *const, void const *const,
+ void const *&, void *const &, void const *const & {
+ $input = SWIG_NewPointerObj(%as_voidptr($1), $descriptor, %newpointer_flags);
+}
+
+/* directorout */
+
+%typemap(directorout,noblock=1) void * (void *argp, int res) {
+ res = SWIG_ConvertPtr($input, &argp, 0, 0);
+ if (!SWIG_IsOK(res)) {
+ %dirout_fail(res,"$type");
+ }
+ $result = %reinterpret_cast(argp, $ltype);
+}
+
+%typemap(directorout,noblock=1,warning=SWIGWARN_TYPEMAP_DIRECTOROUT_PTR_MSG) void * const& (void *argp, int res) {
+ res = SWIG_ConvertPtr($input, &argp, 0, $disown);
+ if (!SWIG_IsOK(res)) {
+ %dirout_fail(res,"$type");
+ }
+ static $*ltype temp = %reinterpret_cast(argp, $*ltype);
+ $result = &temp;
+}
+
+
+
+#endif /* SWIG_DIRECTOR_TYPEMAPS */
+
diff --git a/contrib/tools/swig/Lib/wchar.i b/contrib/tools/swig/Lib/wchar.i
new file mode 100644
index 0000000000..14de346346
--- /dev/null
+++ b/contrib/tools/swig/Lib/wchar.i
@@ -0,0 +1,11 @@
+/* -----------------------------------------------------------------------------
+ * wchar.i
+ * ----------------------------------------------------------------------------- */
+
+/*
+ wchar_t not supported, unless otherwise specified in the target language.
+*/
+
+#if defined(SWIG_WCHAR)
+#undef SWIG_WCHAR
+#endif
diff --git a/contrib/tools/swig/README b/contrib/tools/swig/README
new file mode 100644
index 0000000000..e499e7605e
--- /dev/null
+++ b/contrib/tools/swig/README
@@ -0,0 +1,139 @@
+SWIG (Simplified Wrapper and Interface Generator)
+
+Tagline: SWIG is a compiler that integrates C and C++ with languages
+ including Perl, Python, Tcl, Ruby, PHP, Java, C#, D, Go, Lua,
+ Octave, R, Scheme (Guile, MzScheme/Racket), Scilab, Ocaml.
+ SWIG can also export its parse tree into XML.
+
+SWIG reads annotated C/C++ header files and creates wrapper code (glue
+code) in order to make the corresponding C/C++ libraries available to
+the listed languages, or to extend C/C++ programs with a scripting
+language.
+
+Up-to-date SWIG related information can be found at
+
+ https://www.swig.org
+
+A SWIG FAQ and other hints can be found on the SWIG Wiki:
+
+ https://github.com/swig/swig/wiki
+
+License
+=======
+Please see the LICENSE file for details of the SWIG license. For
+further insight into the license including the license of SWIG's
+output code, please visit
+
+ https://www.swig.org/legal.html
+
+Release Notes
+=============
+Please see the CHANGES.current file for a detailed list of bug fixes and
+new features for the current release. The CHANGES file contains bug fixes
+and new features for older versions. A summary of changes in each release
+can be found in the RELEASENOTES file.
+
+Documentation
+=============
+The Doc/Manual directory contains the most recent set of updated
+documentation for this release. The documentation is available in
+three different formats, each of which contains identical content.
+These format are, pdf (Doc/Manual/SWIGDocumentation.pdf), single
+page html (Doc/Manual/SWIGDocumentation.html) or multiple page html
+(other files in Doc/Manual). Please select your chosen format and
+copy/install to wherever takes your fancy.
+
+There is some technical developer documentation available in the
+Doc/Devel subdirectory. This is not necessarily up-to-date, but it
+has some information on SWIG internals.
+
+Documentation is also online at https://www.swig.org/doc.html.
+
+Backwards Compatibility
+=======================
+The developers strive their best to preserve backwards compatibility
+between releases, but this is not always possible as the overriding
+aim is to provide the best wrapping experience. Where backwards
+compatibility is known to be broken, it is clearly marked as an
+incompatibility in the CHANGES and CHANGES.current files.
+
+See the documentation for details of the SWIG_VERSION preprocessor
+symbol if you have backward compatibility issues and need to use more
+than one version of SWIG.
+
+Installation
+============
+Please read the Doc/Manual/Preface.html#Preface_installation for
+full installation instructions for Windows, Unix and Mac OS X
+using the release tarball/zip file. The INSTALL file has generic
+build and installation instructions for Unix users.
+Users wishing to build and install code from Github should
+visit https://swig.org/svn.html to obtain the more detailed
+instructions required for building code obtained from Github - extra
+steps are required compared to building from the release tarball.
+
+Testing
+=======
+The typical 'make -k check' can be performed on Unix operating systems.
+Please read Doc/Manual/Preface.html#Preface_testing for details.
+
+Examples
+========
+The Examples directory contains a variety of examples of using SWIG
+and it has some browsable documentation. Simply point your browser to
+the file "Example/index.html".
+
+The Examples directory also includes Visual C++ project 6 (.dsp) files for
+building some of the examples on Windows. Later versions of Visual Studio
+will convert these old style project files into a current solution file.
+
+Known Issues
+============
+There are minor known bugs, details of which are in the bug tracker, see
+https://www.swig.org/bugs.html.
+
+Troubleshooting
+===============
+In order to operate correctly, SWIG relies upon a set of library
+files. If after building SWIG, you get error messages like this,
+
+ $ swig foo.i
+ :1. Unable to find 'swig.swg'
+ :3. Unable to find 'tcl8.swg'
+
+it means that SWIG has either been incorrectly configured or
+installed. To fix this:
+
+ 1. Make sure you remembered to do a 'make install' and that
+ the installation actually worked. Make sure you have
+ write permission on the install directory.
+
+ 2. If that doesn't work, type 'swig -swiglib' to find out
+ where SWIG thinks its library is located.
+
+ 3. If the location is not where you expect, perhaps
+ you supplied a bad option to configure. Use
+ ./configure --prefix=pathname to set the SWIG install
+ location. Also, make sure you don't include a shell
+ escape character such as ~ when you specify the path.
+
+ 4. The SWIG library can be changed by setting the SWIG_LIB
+ environment variable. However, you really shouldn't
+ have to do this.
+
+If you are having other troubles, you might look at the SWIG Wiki at
+https://github.com/swig/swig/wiki.
+
+Participate!
+============
+Please report any errors and submit patches (if possible)! We only
+have access to a limited variety of hardware (Linux, Solaris, OS-X,
+and Windows). All contributions help.
+
+If you would like to join the SWIG development team or contribute a
+language module to the distribution, please contact the swig-devel
+mailing list, details at https://www.swig.org/mail.html.
+
+
+ -- The SWIG Maintainers
+
diff --git a/contrib/tools/swig/RELEASENOTES b/contrib/tools/swig/RELEASENOTES
new file mode 100644
index 0000000000..493f199549
--- /dev/null
+++ b/contrib/tools/swig/RELEASENOTES
@@ -0,0 +1,494 @@
+This file contains a brief overview of the changes made in each release.
+A detailed description of changes are available in the CHANGES.current
+and CHANGES files.
+
+Release Notes
+=============
+Detailed release notes are available with the release and are also
+published on the SWIG web site at https://swig.org/release.html.
+
+SWIG-4.1.1 summary:
+- Couple of stability fixes.
+- Stability fix in ccache-swig when calculating hashes of inputs.
+- Some template handling improvements.
+- R - minor fixes plus deprecation for rtypecheck typemaps being optional.
+
+SWIG-4.1.0 summary:
+- Add Javascript Node v12-v18 support, remove support prior to v6.
+- Octave 6.0 to 6.4 support added.
+- Add PHP 8 support.
+- PHP wrapping is now done entirely via PHP's C API - no more .php wrapper.
+- Perl 5.8.0 is now the oldest version SWIG supports.
+- Python 3.3 is now the oldest Python 3 version SWIG supports.
+- Python 3.9-3.11 support added.
+- Various memory leak fixes in Python generated code.
+- Scilab 5.5-6.1 support improved.
+- Many improvements for each and every target language.
+- Various preprocessor expression handling improvements.
+- Improved C99, C++11, C++14, C++17 support. Start adding C++20 standard.
+- Make SWIG much more move semantics friendly.
+- Add C++ std::unique_ptr support.
+- Few minor C++ template handling improvements.
+- Various C++ using declaration fixes.
+- Few fixes for handling Doxygen comments.
+- GitHub Actions is now used instead of Travis CI for continuous integration.
+- Add building SWIG using CMake as a secondary build system.
+- Update optional SWIG build dependency for regex support from PCRE to PCRE2.
+
+SWIG-4.0.2 summary:
+- A few fixes around doxygen comment handling.
+- Ruby 2.7 support added.
+- Various minor improvements to C#, D, Java, OCaml, Octave, Python,
+ R, Ruby.
+- Considerable performance improvement running SWIG on large
+ interface files.
+
+SWIG-4.0.1 summary:
+- SWIG now cleans up on error by removing all generated files.
+- Add Python 3.8 support.
+- Python Sphinx compatibility added for Doxygen comments.
+- Some minor regressions introduced in 4.0.0 were fixed.
+- Fix some C++17 compatibility problems in Python and Ruby generated
+ code.
+- Minor improvements/fixes for C#, Java, Javascript, Lua, MzScheme,
+ Ocaml, Octave and Python.
+
+SWIG-4.0.0 summary:
+- Support for Doxygen documentation comments which are parsed and
+ converted into JavaDoc or PyDoc comments.
+- STL wrappers improved for C#, Java and Ruby.
+- C++11 STL containers added for Java, Python and Ruby.
+- Improved support for parsing C++11 and C++14 code.
+- Various fixes for shared_ptr.
+- Various C preprocessor corner case fixes.
+- Corner case fixes for member function pointers.
+- Python module overhaul by simplifying the generated code and turning
+ most optimizations on by default.
+- %template improvements wrt scoping to align with C++ explicit
+ template instantiations.
+- Added support for a command-line options file (sometimes called a
+ response file).
+- Numerous enhancements and fixes for all supported target languages.
+- SWIG now classifies the status of target languages into either
+ 'Experimental' or 'Supported' to indicate the expected maturity
+ level.
+- Support for CFFI, Allegrocl, Chicken, CLISP, S-EXP, UFFI, Pike,
+ Modula3 has been removed.
+- Octave 4.4-5.1 support added.
+- PHP5 support removed, PHP7 is now the supported PHP version.
+- Minimum Python version required is now 2.7, 3.2-3.7 are the only
+ other versions supported.
+- Added support for Javascript NodeJS versions 2-10.
+- OCaml support is much improved and updated, minimum OCaml version
+ required is now 3.12.0.
+
+SWIG-3.0.12 summary:
+- Add support for Octave-4.2.
+- Enhance %extend to support template functions.
+- Language specific enhancements and fixes for C#, D, Guile, Java, PHP7.
+
+SWIG-3.0.11 summary:
+- PHP 7 support added.
+- C++11 alias templates and type aliasing support added.
+- Minor fixes and enhancements for C# Go Guile Java Javascript Octave
+ PHP Python R Ruby Scilab XML.
+
+SWIG-3.0.10 summary:
+- Regression fixes for smart pointers and importing Python modules.
+
+SWIG-3.0.9 summary:
+- Add support for Python's implicit namespace packages.
+- Fixes to support Go 1.6.
+- C++11 std::array support added for Java.
+- Improved C++ multiple inheritance support for Java/C# wrappers.
+- Various other minor fixes and improvements for C#, D, Go, Java,
+ Javascript, Lua, Python, R, Ruby, Scilab.
+
+SWIG-3.0.8 summary:
+- pdf documentation enhancements.
+- Various Python 3.5 issues fixed.
+- std::array support added for Ruby and Python.
+- shared_ptr support added for Ruby.
+- Minor improvements for CFFI, Go, Java, Perl, Python, Ruby.
+
+SWIG-3.0.7 summary:
+- Add support for Octave-4.0.0.
+- Remove potential Android security exploit in generated Java classes.
+- Minor new features and bug fixes.
+
+SWIG-3.0.6 summary:
+- Stability and regression fixes.
+- Fixed parsing of C++ corner cases.
+- Language improvements and bug fixes for C#, Go, Java, Lua, Python, R.
+
+SWIG-3.0.5 summary:
+- Added support for Scilab.
+- Important Python regression fix when wrapping C++ default arguments.
+- Minor improvements for C#, Go, Octave, PHP and Python.
+
+SWIG-3.0.4 summary:
+- Python regression fix when wrapping C++ default arguments.
+- Improved error messages.
+
+SWIG-3.0.3 summary:
+- Add support for C++11 strongly typed enumerations.
+- Numerous bug fixes and minor enhancements for C#, D, Go, Java,
+ Javascript, PHP, Perl and Python wrappers.
+
+SWIG-3.0.2 summary:
+- Bug fix during install and a couple of other minor changes.
+
+SWIG-3.0.1 summary:
+- Javascript module added. This supports JavascriptCore (Safari/Webkit),
+ v8 (Chromium) and node.js currently.
+- A few notable regressions introduced in 3.0.0 have been fixed - in
+ Lua, nested classes and parsing of operator <<.
+- The usual round of bug fixes and minor improvements for:
+ C#, GCJ, Go, Java, Lua, PHP and Python.
+
+SWIG-3.0.0 summary:
+- This is a major new release focusing primarily on C++ improvements.
+- C++11 support added. Please see documentation for details of supported
+ features: https://www.swig.org/Doc3.0/CPlusPlus11.html
+- Nested class support added. This has been taken full advantage of in
+ Java and C#. Other languages can use the nested classes, but require
+ further work for a more natural integration into the target language.
+ We urge folk knowledgeable in the other target languages to step
+ forward and help with this effort.
+- Lua: improved metatables and support for %nspace.
+- Go 1.3 support added.
+- Python import improvements including relative imports.
+- Python 3.3 support completed.
+- Perl director support added.
+- C# .NET 2 support is now the minimum. Generated using statements are
+ replaced by fully qualified names.
+- Bug fixes and improvements to the following languages:
+ C#, Go, Guile, Java, Lua, Perl, PHP, Python, Octave, R, Ruby, Tcl
+- Various other bug fixes and improvements affecting all languages.
+- Note that this release contains some backwards incompatible changes
+ in some languages.
+- Full detailed release notes are in the changes file.
+
+SWIG-2.0.12 summary:
+- This is a maintenance release backporting some fixes from the pending
+ 3.0.0 release.
+- Octave 3.8 support added.
+- C++11 support for new versions of erase/insert in the STL containers.
+- Compilation fixes on some systems for the generated Lua, PHP, Python
+ and R wrappers.
+
+SWIG-2.0.11 summary:
+- Minor bug fixes and enhancements mostly in Python, but also
+ C#, Lua, Ocaml, Octave, Perl, PHP, Python, R, Ruby, Tcl.
+
+SWIG-2.0.10 summary:
+- Ruby 1.9 support is now complete.
+- Add support for Guile 2.0 and Guile 1.6 support (GH interface) has
+ been dropped.
+- Various small language neutral improvements and fixes.
+- Various bug fixes and minor improvements specific to C#, CFFI, D,
+ Java, Octave, PHP, Python,
+- Minor bug fix in ccache-swig.
+- Development has moved to Github with Travis continuous integration
+ testing - patches using https://github.com/swig/swig are welcome.
+
+SWIG-2.0.9 summary:
+- Improved typemap matching.
+- Ruby 1.9 support is much improved.
+- Various bug fixes and minor improvements in C#, CFFI, Go, Java,
+ Modula3, Octave, Perl, Python, R, Ruby, Tcl and in ccache-swig.
+
+SWIG-2.0.8 summary:
+- Fix a couple of regressions introduced in 2.0.5 and 2.0.7.
+- Improved using declarations and using directives support.
+- Minor fixes/enhancements for C#, Java, Octave, Perl and Python.
+
+SWIG-2.0.7 summary:
+- Important regression fixes since 2.0.5 for typemaps in general and
+ in Python.
+- Fixes and enhancements for Go, Java, Octave and PHP.
+
+SWIG-2.0.6 summary:
+- Regression fix for Python STL wrappers on some systems.
+
+SWIG-2.0.5 summary:
+- Official Android support added including documentation and examples.
+- Improvements involving templates:
+ 1) Various fixes with templates and typedef types.
+ 2) Some template lookup problems fixed.
+ 3) Templated type fixes to use correct typemaps.
+- Autodoc documentation generation improvements.
+- Python STL container wrappers improvements including addition of
+ stepped slicing.
+- Approximately 70 fixes and minor enhancements for the following
+ target languages: AllegroCL, C#, D, Go, Java, Lua, Ocaml, Octave,
+ Perl, PHP, Python, R, Ruby, Tcl, Xml.
+
+SWIG-2.0.4 summary:
+- This is mainly a Python oriented release including support for Python
+ built-in types for superior performance with the new -builtin option.
+ The -builtin option is especially suitable for performance-critical
+ libraries and applications that call wrapped methods repeatedly.
+ See the python-specific chapter of the SWIG manual for more info.
+- Python 3.2 support has also been added and various Python bugs have
+ been fixed.
+- Octave 3.4 support has also been added.
+- There are also the usual minor generic improvements, as well as bug
+ fixes and enhancements for D, Guile, Lua, Octave, Perl and Tcl.
+
+SWIG-2.0.3 summary:
+- A bug fix release including a couple of fixes for regressions in the
+ 2.0 series.
+
+SWIG-2.0.2 summary:
+- Support for the D language has been added.
+- Various bug fixes and minor enhancements.
+- Bug fixes particular to the Clisp, C#, Go, MzScheme, Ocaml, PHP, R,
+ Ruby target languages.
+
+SWIG-2.0.1 summary:
+- Support for the Go language has been added.
+- New regular expression (regex) encoder for renaming symbols based on
+ the Perl Compatible Regular Expressions (PCRE) library.
+- Numerous fixes in reporting file and line numbers in error and warning
+ messages.
+- Various bug fixes and improvements in the C#, Lua, Perl, PHP, Ruby
+ and Python language modules.
+
+SWIG-2.0.0 summary:
+- License changes, see LICENSE file and https://www.swig.org/legal.html.
+- Much better nested class/struct support.
+- Much improved template partial specialization and explicit
+ specialization handling.
+- Namespace support improved with the 'nspace' feature where namespaces
+ can be automatically translated into Java packages or C# namespaces.
+- Improved typemap and symbol table debugging.
+- Numerous subtle typemap matching rule changes when using the default
+ (SWIGTYPE) type. These now work much like C++ class template partial
+ specialization matching.
+- Other small enhancements for typemaps. Typemap fragments are also now
+ official and documented.
+- Warning and error display refinements.
+- Wrapping of shared_ptr is improved and documented now.
+- Numerous C++ unary scope operator (::) fixes.
+- Better support for boolean expressions.
+- Various bug fixes and improvements in the Allegrocl, C#, Java, Lua,
+ Octave, PHP, Python, R, Ruby and XML modules.
+
+SWIG-1.3.40 summary:
+- SWIG now supports directors for PHP.
+- PHP support improved in general.
+- Octave 3.2 support added.
+- Various bug fixes/enhancements for Allegrocl, C#, Java, Octave, Perl,
+ Python, Ruby and Tcl.
+- Other generic fixes and minor new features.
+
+SWIG-1.3.39 summary:
+- Some new small feature enhancements.
+- Improved C# std::vector wrappers.
+- Bug fixes: mainly Python, but also Perl, MzScheme, CFFI, Allegrocl
+ and Ruby
+
+SWIG-1.3.38 summary:
+- Output directory regression fix and other minor bug fixes
+
+SWIG-1.3.37 summary:
+- Python 3 support added
+- SWIG now ships with a version of ccache that can be used with SWIG.
+ This enables the files generated by SWIG to be cached so that repeated
+ use of SWIG on unchanged input files speeds up builds quite considerably.
+- PHP 4 support removed and PHP support improved in general
+- Improved C# array support
+- Numerous Allegro CL improvements
+- Bug fixes/enhancements for Python, PHP, Java, C#, Chicken, Allegro CL,
+ CFFI, Ruby, Tcl, Perl, R, Lua.
+- Other minor generic bug fixes and enhancements
+
+SWIG-1.3.36 summary:
+- Enhancement to directors to wrap all protected members
+- Optimisation feature for objects returned by value
+- A few bugs fixes in the PHP, Java, Ruby, R, C#, Python, Lua and
+ Perl modules
+- Other minor generic bug fixes
+
+SWIG-1.3.35 summary:
+- Octave language module added
+- Bug fixes in Python, Lua, Java, C#, Perl modules
+- A few other generic bugs and runtime assertions fixed
+
+SWIG-1.3.34 summary:
+- shared_ptr support for Python
+- Support for latest R - version 2.6
+- Various minor improvements/bug fixes for R, Lua, Python, Java, C#
+- A few other generic bug fixes, mainly for templates and using statements
+
+SWIG-1.3.33 summary:
+- Fix regression for Perl where C++ wrappers would not compile
+- Fix regression parsing macros
+
+SWIG-1.3.32 summary:
+- shared_ptr support for Java and C#
+- Enhanced STL support for Ruby
+- Windows support for R
+- Fixed long-standing memory leak in PHP Module
+- Numerous fixes and minor enhancements for Allegrocl, C#, cffi, Chicken, Guile,
+ Java, Lua, Ocaml, Perl, PHP, Python, Ruby, Tcl.
+- Improved warning support
+
+SWIG-1.3.31 summary:
+- Python modern classes regression fix
+
+SWIG-1.3.30 summary:
+- Python-2.5 support
+- New language module: R
+- Director support added for C#
+- Numerous director fixes and improvements
+- Improved mingw/msys support
+- Better constants support in Guile and chicken modules
+- Support for generating PHP5 class wrappers
+- Important Java premature garbage collection fix
+- Minor improvements/fixes in cffi, php, allegrocl, perl, chicken, lua, ruby,
+ ocaml, python, java, c# and guile language modules
+- Many many other bug fixes
+
+SWIG-1.3.29 summary:
+- Numerous important bug fixes
+- Few minor new features
+- Some performance improvements in generated code for Python
+
+SWIG-1.3.28 summary:
+- More powerful renaming (%rename) capability.
+- More user friendly warning handling.
+- Add finer control for default constructors and destructors. We discourage
+ the use of the 'nodefault' option, which disables both constructors and
+ destructors, leading to possible memory leaks. Use instead 'nodefaultctor'
+ and/or 'nodefaultdtor'.
+- Automatic copy constructor wrapper generation via the 'copyctor' option/feature.
+- Better handling of Windows extensions and types.
+- Better runtime error reporting.
+- Add the %catches directive to catch and dispatch exceptions.
+- Add the %naturalvar directive for more 'natural' variable wrapping.
+- Better default handling of std::string variables using the %naturalvar directive.
+- Add the %allowexcept and %exceptionvar directives to handle exceptions when
+ accessing a variable.
+- Add the %delobject directive to mark methods that act like destructors.
+- Add the -fastdispatch option to enable smaller and faster overload dispatch
+ mechanism.
+- Template support for %rename, %feature and %typemap improved.
+- Add/doc more debug options, such as -dump_module, -debug_typemaps, etc.
+- Unified typemap library (UTL) potentially providing core typemaps for all
+ scripting languages based on the recently evolving Python typemaps.
+- New language module: Common Lisp with CFFI.
+- Python, Ruby, Perl and Tcl use the new UTL, many old reported and hidden
+ errors with typemaps are now fixed.
+- Initial Java support for languages using the UTL via GCJ, you can now use
+ Java libraries in your favorite script language using gcj + swig.
+- Tcl support for std::wstring.
+- PHP4 module update, many error fixes and actively maintained again.
+- Allegrocl support for C++, also enhanced C support.
+- Ruby support for bang methods.
+- Ruby support for user classes as native exceptions.
+- Perl improved dispatching in overloaded functions via the new cast and rank
+ mechanism.
+- Perl improved backward compatibility, 5.004 and later tested and working.
+- Python improved backward compatibility, 1.5.2 and later tested and working.
+- Python can use the same cast/rank mechanism via the -castmode option.
+- Python implicit conversion mechanism similar to C++, via the %implicitconv
+ directive (replaces and improves the implicit.i library).
+- Python threading support added.
+- Python STL support improved, iterators are supported and STL containers can
+ use now the native PyObject type.
+- Python many performance options and improvements, try the -O option to test
+ all of them. Python runtime benchmarks show up to 20 times better performance
+ compared to 1.3.27 and older versions.
+- Python support for 'multi-inheritance' on the python side.
+- Python simplified proxy classes, now swig doesn't need to generate the
+ additional 'ClassPtr' classes.
+- Python extended support for smart pointers.
+- Python better support for static member variables.
+- Python backward compatibility improved, many projects that used to work
+ only with swig-1.3.21 to swig-1.3.24 are working again with swig-1.3.28
+- Python test-suite is now 'valgrinded' before release, and swig also
+ reports memory leaks due to missing destructors.
+- Minor bug fixes and improvements to the Lua, Ruby, Java, C#, Python, Guile,
+ Chicken, Tcl and Perl modules.
+
+SWIG-1.3.27 summary:
+- Fix bug in anonymous typedef structures which was leading to strange behaviour
+
+SWIG-1.3.26 summary:
+- New language modules: Lua, CLISP and Common Lisp with UFFI.
+- Big overhaul to the PHP module.
+- Change to the way 'extern' is handled.
+- Minor bug fixes specific to C#, Java, Modula3, Ocaml, Allegro CL,
+ XML, Lisp s-expressions, Tcl, Ruby and Python modules.
+- Other minor improvements and bug fixes.
+
+SWIG-1.3.25 summary:
+- Improved runtime type system. Speed of module loading improved in
+ modules with lots of types. SWIG_RUNTIME_VERSION has been increased
+ from 1 to 2, but the API is exactly the same; only internal changes
+ were made.
+- The languages that use the runtime type system now support external
+ access to the runtime type system.
+- Various improvements with typemaps and template handling.
+- Fewer warnings in generated code.
+- Improved colour documentation.
+- Many C# module improvements (exception handling, prevention of early
+ garbage collection, C# attributes support added, more flexible type
+ marshalling/asymmetric types.)
+- Minor improvements and bug fixes specific to the C#, Java, TCL, Guile,
+ Chicken, MzScheme, Perl, Php, Python, Ruby and Ocaml modules).
+- Various other bug fixes and memory leak fixes.
+
+SWIG-1.3.24 summary:
+- Improved enum handling
+- More runtime library options
+- More bugs fixes for templates and template default arguments, directors
+ and other areas.
+- Better smart pointer support, including data members, static members
+ and %extend.
+
+SWIG-1.3.23 summary:
+- Improved support for callbacks
+- Python docstring support and better error handling
+- C++ default argument support for Java and C# added.
+- Improved c++ default argument support for the scripting languages plus
+ option to use original (compact) default arguments.
+- %feature and %ignore/%rename bug fixes and mods - they might need default
+ arguments specified to maintain compatible behaviour when using the new
+ default arguments wrapping.
+- Runtime library changes: Runtime code can now exist in more than one module
+ and so need not be compiled into just one module
+- Further improved support for templates and namespaces
+- Overloaded templated function support added
+- More powerful default typemaps (mixed default typemaps)
+- Some important %extend and director code bug fixes
+- Guile now defaults to using SCM API. The old interface can be obtained by
+ the -gh option.
+- Various minor improvements and bug fixes for C#, Chicken, Guile, Java,
+ MzScheme, Perl, Python and Ruby
+- Improved dependencies generation for constructing Makefiles.
+
+SWIG-1.3.22 summary:
+- Improved exception handling and translation of C errors or C++
+ exceptions into target language exceptions.
+- Improved enum support, mapping to built-in Java 1.5 enums and C#
+ enums or the typesafe enum pattern for these two languages.
+- Python - much better STL support and support for std::wstring,
+ wchar_t and FILE *.
+- Initial support for Modula3 and Allegro CL.
+- 64 bit TCL support.
+- Java and C#'s proxy classes are now nearly 100% generated from
+ typemaps and/or features for finer control on the generated code.
+- SWIG runtime library support deprecation.
+- Improved documentation. SWIG now additionally provides documentation
+ in the form of a single HTML page as well as a pdf document.
+- Enhanced C++ friend declaration support.
+- Better support for reference counted classes.
+- Various %fragment improvements.
+- RPM fixes.
+- Various minor improvements and bug fixes for C#, Chicken, Guile, Java,
+ MzScheme, Perl, Php, Python, Ruby and XML.
+
+
diff --git a/contrib/tools/swig/Source/CParse/cparse.h b/contrib/tools/swig/Source/CParse/cparse.h
new file mode 100644
index 0000000000..2b63f034d0
--- /dev/null
+++ b/contrib/tools/swig/Source/CParse/cparse.h
@@ -0,0 +1,85 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * cparse.h
+ *
+ * SWIG parser module.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_CPARSE_H
+#define SWIG_CPARSE_H
+
+#include "swig.h"
+#include "swigwarn.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* cscanner.c */
+ extern String *cparse_file;
+ extern int cparse_line;
+ extern int cparse_cplusplus;
+ extern int cparse_cplusplusout;
+ extern int cparse_start_line;
+ extern String *cparse_unknown_directive;
+ extern int scan_doxygen_comments;
+
+ extern void Swig_cparse_cplusplus(int);
+ extern void Swig_cparse_cplusplusout(int);
+ extern void scanner_file(File *);
+ extern void scanner_next_token(int);
+ extern void skip_balanced(int startchar, int endchar);
+ extern String *get_raw_text_balanced(int startchar, int endchar);
+ extern void skip_decl(void);
+ extern void scanner_check_typedef(void);
+ extern void scanner_ignore_typedef(void);
+ extern void scanner_last_id(int);
+ extern void scanner_clear_rename(void);
+ extern void scanner_set_location(String *file, int line);
+ extern void scanner_set_main_input_file(String *file);
+ extern String *scanner_get_main_input_file(void);
+ extern void Swig_cparse_follow_locators(int);
+ extern void start_inline(char *, int);
+ extern String *scanner_ccode;
+ extern int yylex(void);
+
+/* parser.y */
+ extern SwigType *Swig_cparse_type(String *);
+ extern Node *Swig_cparse(File *);
+ extern Hash *Swig_cparse_features(void);
+ extern void SWIG_cparse_set_compact_default_args(int defargs);
+ extern int SWIG_cparse_template_reduce(int treduce);
+
+/* util.c */
+ extern void Swig_cparse_replace_descriptor(String *s);
+ extern SwigType *Swig_cparse_smartptr(Node *n);
+ extern void cparse_normalize_void(Node *);
+ extern Parm *Swig_cparse_parm(String *s);
+ extern ParmList *Swig_cparse_parms(String *s, Node *file_line_node);
+ extern Node *Swig_cparse_new_node(const_String_or_char_ptr tag);
+
+/* templ.c */
+ extern int Swig_cparse_template_expand(Node *n, String *rname, ParmList *tparms, Symtab *tscope);
+ extern Node *Swig_cparse_template_locate(String *name, ParmList *tparms, Symtab *tscope);
+ extern void Swig_cparse_debug_templates(int);
+
+#ifdef __cplusplus
+}
+#endif
+#define SWIG_WARN_NODE_BEGIN(Node) \
+ { \
+ String *wrnfilter = Node ? Getattr(Node,"feature:warnfilter") : 0; \
+ if (wrnfilter) Swig_warnfilter(wrnfilter,1)
+#define SWIG_WARN_NODE_END(Node) \
+ if (wrnfilter) Swig_warnfilter(wrnfilter,0); \
+ }
+
+#define COMPOUND_EXPR_VAL(dtype) \
+ ((dtype).type == T_CHAR || (dtype).type == T_WCHAR ? (dtype).rawval : (dtype).val)
+#endif
diff --git a/contrib/tools/swig/Source/CParse/cscanner.c b/contrib/tools/swig/Source/CParse/cscanner.c
new file mode 100644
index 0000000000..2eb3f97749
--- /dev/null
+++ b/contrib/tools/swig/Source/CParse/cscanner.c
@@ -0,0 +1,1088 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * scanner.c
+ *
+ * SWIG tokenizer. This file is a wrapper around the generic C scanner
+ * found in Swig/scanner.c. Extra logic is added both to accommodate the
+ * bison-based grammar and certain peculiarities of C++ parsing (e.g.,
+ * operator overloading, typedef resolution, etc.). This code also splits
+ * C identifiers up into keywords and SWIG directives.
+ * ----------------------------------------------------------------------------- */
+
+#include "cparse.h"
+#include "parser.h"
+#include <string.h>
+#include <ctype.h>
+
+/* Scanner object */
+static Scanner *scan = 0;
+
+/* Global string containing C code. Used by the parser to grab code blocks */
+String *scanner_ccode = 0;
+
+/* The main file being parsed */
+static String *main_input_file = 0;
+
+/* Error reporting/location information */
+int cparse_line = 1;
+String *cparse_file = 0;
+int cparse_start_line = 0;
+
+/* C++ mode */
+int cparse_cplusplus = 0;
+
+/* Generate C++ compatible code when wrapping C code */
+int cparse_cplusplusout = 0;
+
+/* To allow better error reporting */
+String *cparse_unknown_directive = 0;
+
+/* Private vars */
+static int scan_init = 0;
+static int num_brace = 0;
+static int last_brace = 0;
+static int last_id = 0;
+static int rename_active = 0;
+
+/* Doxygen comments scanning */
+int scan_doxygen_comments = 0;
+
+int isStructuralDoxygen(String *s) {
+ static const char* const structuralTags[] = {
+ "addtogroup",
+ "callgraph",
+ "callergraph",
+ "category",
+ "def",
+ "defgroup",
+ "dir",
+ "example",
+ "file",
+ "headerfile",
+ "internal",
+ "mainpage",
+ "name",
+ "nosubgrouping",
+ "overload",
+ "package",
+ "page",
+ "protocol",
+ "relates",
+ "relatesalso",
+ "showinitializer",
+ "weakgroup",
+ };
+
+ unsigned n;
+ char *slashPointer = Strchr(s, '\\');
+ char *atPointer = Strchr(s,'@');
+ if (slashPointer == NULL && atPointer == NULL)
+ return 0;
+ else if(slashPointer == NULL)
+ slashPointer = atPointer;
+
+ slashPointer++; /* skip backslash or at sign */
+
+ for (n = 0; n < sizeof(structuralTags)/sizeof(structuralTags[0]); n++) {
+ const size_t len = strlen(structuralTags[n]);
+ if (strncmp(slashPointer, structuralTags[n], len) == 0) {
+ /* Take care to avoid false positives with prefixes of other tags. */
+ if (slashPointer[len] == '\0' || isspace((int)slashPointer[len]))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparse_cplusplus()
+ * ----------------------------------------------------------------------------- */
+
+void Swig_cparse_cplusplus(int v) {
+ cparse_cplusplus = v;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparse_cplusplusout()
+ * ----------------------------------------------------------------------------- */
+
+void Swig_cparse_cplusplusout(int v) {
+ cparse_cplusplusout = v;
+}
+
+/* ----------------------------------------------------------------------------
+ * scanner_init()
+ *
+ * Initialize buffers
+ * ------------------------------------------------------------------------- */
+
+void scanner_init(void) {
+ scan = NewScanner();
+ Scanner_idstart(scan,"%");
+ scan_init = 1;
+ scanner_ccode = NewStringEmpty();
+}
+
+/* ----------------------------------------------------------------------------
+ * scanner_file(DOHFile *f)
+ *
+ * Start reading from new file
+ * ------------------------------------------------------------------------- */
+void scanner_file(DOHFile * f) {
+ if (!scan_init) scanner_init();
+ Scanner_clear(scan);
+ Scanner_push(scan,f);
+}
+
+/* ----------------------------------------------------------------------------
+ * start_inline(char *text, int line)
+ *
+ * Take a chunk of text and recursively feed it back into the scanner. Used
+ * by the %inline directive.
+ * ------------------------------------------------------------------------- */
+
+void start_inline(char *text, int line) {
+ String *stext = NewString(text);
+
+ Seek(stext,0,SEEK_SET);
+ Setfile(stext,cparse_file);
+ Setline(stext,line);
+ Scanner_push(scan,stext);
+ Delete(stext);
+}
+
+/* -----------------------------------------------------------------------------
+ * skip_balanced()
+ *
+ * Skips a piece of code enclosed in begin/end symbols such as '{...}' or
+ * (...). Ignores symbols inside comments or strings.
+ * ----------------------------------------------------------------------------- */
+
+void skip_balanced(int startchar, int endchar) {
+ int start_line = Scanner_line(scan);
+ Clear(scanner_ccode);
+
+ if (Scanner_skip_balanced(scan,startchar,endchar) < 0) {
+ Swig_error(cparse_file, start_line, "Missing '%c'. Reached end of input.\n", endchar);
+ return;
+ }
+
+ cparse_line = Scanner_line(scan);
+ cparse_file = Scanner_file(scan);
+
+ Append(scanner_ccode, Scanner_text(scan));
+ if (endchar == '}')
+ num_brace--;
+ return;
+}
+
+/* -----------------------------------------------------------------------------
+ * get_raw_text_balanced()
+ *
+ * Returns raw text between 2 braces
+ * ----------------------------------------------------------------------------- */
+
+String *get_raw_text_balanced(int startchar, int endchar) {
+ return Scanner_get_raw_text_balanced(scan, startchar, endchar);
+}
+
+/* ----------------------------------------------------------------------------
+ * void skip_decl(void)
+ *
+ * This tries to skip over an entire declaration. For example
+ *
+ * friend ostream& operator<<(ostream&, const char *s);
+ *
+ * or
+ * friend ostream& operator<<(ostream&, const char *s) { }
+ *
+ * ------------------------------------------------------------------------- */
+
+void skip_decl(void) {
+ int tok;
+ int done = 0;
+ int start_line = Scanner_line(scan);
+
+ while (!done) {
+ tok = Scanner_token(scan);
+ if (tok == 0) {
+ if (!Swig_error_count()) {
+ Swig_error(cparse_file, start_line, "Missing semicolon (';'). Reached end of input.\n");
+ }
+ return;
+ }
+ if (tok == SWIG_TOKEN_LBRACE) {
+ if (Scanner_skip_balanced(scan,'{','}') < 0) {
+ Swig_error(cparse_file, start_line, "Missing closing brace ('}'). Reached end of input.\n");
+ }
+ break;
+ }
+ if (tok == SWIG_TOKEN_SEMI) {
+ done = 1;
+ }
+ }
+ cparse_file = Scanner_file(scan);
+ cparse_line = Scanner_line(scan);
+}
+
+/* ----------------------------------------------------------------------------
+ * int yylook()
+ *
+ * Lexical scanner.
+ * ------------------------------------------------------------------------- */
+
+static int yylook(void) {
+
+ int tok = 0;
+
+ while (1) {
+ if ((tok = Scanner_token(scan)) == 0)
+ return 0;
+ if (tok == SWIG_TOKEN_ERROR)
+ return 0;
+ cparse_start_line = Scanner_start_line(scan);
+ cparse_line = Scanner_line(scan);
+ cparse_file = Scanner_file(scan);
+
+ switch(tok) {
+ case SWIG_TOKEN_ID:
+ return ID;
+ case SWIG_TOKEN_LPAREN:
+ return LPAREN;
+ case SWIG_TOKEN_RPAREN:
+ return RPAREN;
+ case SWIG_TOKEN_SEMI:
+ return SEMI;
+ case SWIG_TOKEN_COMMA:
+ return COMMA;
+ case SWIG_TOKEN_STAR:
+ return STAR;
+ case SWIG_TOKEN_RBRACE:
+ num_brace--;
+ if (num_brace < 0) {
+ Swig_error(cparse_file, cparse_line, "Syntax error. Extraneous closing brace ('}')\n");
+ num_brace = 0;
+ } else {
+ return RBRACE;
+ }
+ break;
+ case SWIG_TOKEN_LBRACE:
+ last_brace = num_brace;
+ num_brace++;
+ return LBRACE;
+ case SWIG_TOKEN_EQUAL:
+ return EQUAL;
+ case SWIG_TOKEN_EQUALTO:
+ return EQUALTO;
+ case SWIG_TOKEN_PLUS:
+ return PLUS;
+ case SWIG_TOKEN_MINUS:
+ return MINUS;
+ case SWIG_TOKEN_SLASH:
+ return SLASH;
+ case SWIG_TOKEN_AND:
+ return AND;
+ case SWIG_TOKEN_LAND:
+ return LAND;
+ case SWIG_TOKEN_OR:
+ return OR;
+ case SWIG_TOKEN_LOR:
+ return LOR;
+ case SWIG_TOKEN_XOR:
+ return XOR;
+ case SWIG_TOKEN_NOT:
+ return NOT;
+ case SWIG_TOKEN_LNOT:
+ return LNOT;
+ case SWIG_TOKEN_NOTEQUAL:
+ return NOTEQUALTO;
+ case SWIG_TOKEN_LBRACKET:
+ return LBRACKET;
+ case SWIG_TOKEN_RBRACKET:
+ return RBRACKET;
+ case SWIG_TOKEN_QUESTION:
+ return QUESTIONMARK;
+ case SWIG_TOKEN_LESSTHAN:
+ return LESSTHAN;
+ case SWIG_TOKEN_LTEQUAL:
+ return LESSTHANOREQUALTO;
+ case SWIG_TOKEN_LSHIFT:
+ return LSHIFT;
+ case SWIG_TOKEN_GREATERTHAN:
+ return GREATERTHAN;
+ case SWIG_TOKEN_GTEQUAL:
+ return GREATERTHANOREQUALTO;
+ case SWIG_TOKEN_RSHIFT:
+ return RSHIFT;
+ case SWIG_TOKEN_ARROW:
+ return ARROW;
+ case SWIG_TOKEN_PERIOD:
+ return PERIOD;
+ case SWIG_TOKEN_MODULO:
+ return MODULO;
+ case SWIG_TOKEN_COLON:
+ return COLON;
+ case SWIG_TOKEN_DCOLONSTAR:
+ return DSTAR;
+ case SWIG_TOKEN_LTEQUALGT:
+ return LESSEQUALGREATER;
+
+ case SWIG_TOKEN_DCOLON:
+ {
+ int nexttok = Scanner_token(scan);
+ if (nexttok == SWIG_TOKEN_STAR) {
+ return DSTAR;
+ } else if (nexttok == SWIG_TOKEN_NOT) {
+ return DCNOT;
+ } else {
+ Scanner_pushtoken(scan,nexttok,Scanner_text(scan));
+ if (!last_id) {
+ scanner_next_token(DCOLON);
+ return NONID;
+ } else {
+ return DCOLON;
+ }
+ }
+ }
+ break;
+
+ case SWIG_TOKEN_ELLIPSIS:
+ return ELLIPSIS;
+
+ case SWIG_TOKEN_LLBRACKET:
+ do {
+ tok = Scanner_token(scan);
+ } while ((tok != SWIG_TOKEN_RRBRACKET) && (tok > 0));
+ if (tok <= 0) {
+ Swig_error(cparse_file, cparse_line, "Unbalanced double brackets, missing closing (']]'). Reached end of input.\n");
+ }
+ break;
+
+ case SWIG_TOKEN_RRBRACKET:
+ /* Turn an unmatched ]] back into two ] - e.g. `a[a[0]]` */
+ scanner_next_token(RBRACKET);
+ return RBRACKET;
+
+ /* Look for multi-character sequences */
+
+ case SWIG_TOKEN_RSTRING:
+ yylval.type = NewString(Scanner_text(scan));
+ return TYPE_RAW;
+
+ case SWIG_TOKEN_STRING:
+ yylval.id = Swig_copy_string(Char(Scanner_text(scan)));
+ return STRING;
+
+ case SWIG_TOKEN_WSTRING:
+ yylval.id = Swig_copy_string(Char(Scanner_text(scan)));
+ return WSTRING;
+
+ case SWIG_TOKEN_CHAR:
+ yylval.str = NewString(Scanner_text(scan));
+ if (Len(yylval.str) == 0) {
+ Swig_error(cparse_file, cparse_line, "Empty character constant\n");
+ }
+ return CHARCONST;
+
+ case SWIG_TOKEN_WCHAR:
+ yylval.str = NewString(Scanner_text(scan));
+ if (Len(yylval.str) == 0) {
+ Swig_error(cparse_file, cparse_line, "Empty character constant\n");
+ }
+ return WCHARCONST;
+
+ /* Numbers */
+
+ case SWIG_TOKEN_INT:
+ return NUM_INT;
+
+ case SWIG_TOKEN_UINT:
+ return NUM_UNSIGNED;
+
+ case SWIG_TOKEN_LONG:
+ return NUM_LONG;
+
+ case SWIG_TOKEN_ULONG:
+ return NUM_ULONG;
+
+ case SWIG_TOKEN_LONGLONG:
+ return NUM_LONGLONG;
+
+ case SWIG_TOKEN_ULONGLONG:
+ return NUM_ULONGLONG;
+
+ case SWIG_TOKEN_DOUBLE:
+ case SWIG_TOKEN_FLOAT:
+ return NUM_FLOAT;
+
+ case SWIG_TOKEN_BOOL:
+ return NUM_BOOL;
+
+ case SWIG_TOKEN_POUND:
+ Scanner_skip_line(scan);
+ yylval.id = Swig_copy_string(Char(Scanner_text(scan)));
+ return POUND;
+ break;
+
+ case SWIG_TOKEN_CODEBLOCK:
+ yylval.str = NewString(Scanner_text(scan));
+ return HBLOCK;
+
+ case SWIG_TOKEN_COMMENT:
+ {
+ typedef enum {
+ DOX_COMMENT_PRE = -1,
+ DOX_COMMENT_NONE,
+ DOX_COMMENT_POST
+ } comment_kind_t;
+ comment_kind_t existing_comment = DOX_COMMENT_NONE;
+
+ /* Concatenate or skip all consecutive comments at once. */
+ do {
+ String *cmt = Scanner_text(scan);
+ String *cmt_modified = 0;
+ char *loc = Char(cmt);
+ if ((strncmp(loc, "/*@SWIG", 7) == 0) && (loc[Len(cmt)-3] == '@')) {
+ Scanner_locator(scan, cmt);
+ }
+ if (scan_doxygen_comments) { /* else just skip this node, to avoid crashes in parser module*/
+
+ int slashStyle = 0; /* Flag for "///" style doxygen comments */
+ if (strncmp(loc, "///", 3) == 0) {
+ slashStyle = 1;
+ if (Len(cmt) == 3) {
+ /* Modify to make length=4 to ensure that the empty comment does
+ get processed to preserve the newlines in the original comments. */
+ cmt_modified = NewStringf("%s ", cmt);
+ cmt = cmt_modified;
+ loc = Char(cmt);
+ }
+ }
+
+ /* Check for all possible Doxygen comment start markers while ignoring
+ comments starting with a row of asterisks or slashes just as
+ Doxygen itself does. Also skip empty comment (slash-star-star-slash),
+ which causes a crash due to begin > end. */
+ if (Len(cmt) > 3 && loc[0] == '/' &&
+ ((loc[1] == '/' && ((loc[2] == '/' && loc[3] != '/') || loc[2] == '!')) ||
+ (loc[1] == '*' && ((loc[2] == '*' && loc[3] != '*' && loc[3] != '/') || loc[2] == '!')))) {
+ comment_kind_t this_comment = loc[3] == '<' ? DOX_COMMENT_POST : DOX_COMMENT_PRE;
+ if (existing_comment != DOX_COMMENT_NONE && this_comment != existing_comment) {
+ /* We can't concatenate together Doxygen pre- and post-comments. */
+ break;
+ }
+
+ if (this_comment == DOX_COMMENT_POST || !isStructuralDoxygen(loc)) {
+ String *str;
+
+ int begin = this_comment == DOX_COMMENT_POST ? 4 : 3;
+ int end = Len(cmt);
+ if (loc[end - 1] == '/' && loc[end - 2] == '*') {
+ end -= 2;
+ }
+
+ str = NewStringWithSize(loc + begin, end - begin);
+
+ if (existing_comment == DOX_COMMENT_NONE) {
+ yylval.str = str;
+ Setline(yylval.str, Scanner_start_line(scan));
+ Setfile(yylval.str, Scanner_file(scan));
+ } else {
+ if (slashStyle) {
+ /* Add a newline to the end of each doxygen "///" comment,
+ since they are processed individually, unlike the
+ slash-star style, which gets processed as a block with
+ newlines included. */
+ Append(yylval.str, "\n");
+ }
+ Append(yylval.str, str);
+ }
+
+ existing_comment = this_comment;
+ }
+ }
+ }
+ do {
+ tok = Scanner_token(scan);
+ } while (tok == SWIG_TOKEN_ENDLINE);
+ Delete(cmt_modified);
+ } while (tok == SWIG_TOKEN_COMMENT);
+
+ Scanner_pushtoken(scan, tok, Scanner_text(scan));
+
+ switch (existing_comment) {
+ case DOX_COMMENT_PRE:
+ return DOXYGENSTRING;
+ case DOX_COMMENT_NONE:
+ break;
+ case DOX_COMMENT_POST:
+ return DOXYGENPOSTSTRING;
+ }
+ }
+ break;
+ case SWIG_TOKEN_ENDLINE:
+ break;
+ case SWIG_TOKEN_BACKSLASH:
+ break;
+ default:
+ Swig_error(cparse_file, cparse_line, "Illegal token '%s'.\n", Scanner_text(scan));
+ return (ILLEGAL);
+ }
+ }
+}
+
+static int check_typedef = 0;
+
+void scanner_set_location(String *file, int line) {
+ Scanner_set_location(scan,file,line-1);
+}
+
+void scanner_check_typedef(void) {
+ check_typedef = 1;
+}
+
+void scanner_ignore_typedef(void) {
+ check_typedef = 0;
+}
+
+void scanner_last_id(int x) {
+ last_id = x;
+}
+
+void scanner_clear_rename(void) {
+ rename_active = 0;
+}
+
+/* Used to push a fictitious token into the scanner */
+static int next_token = 0;
+void scanner_next_token(int tok) {
+ next_token = tok;
+}
+
+void scanner_set_main_input_file(String *file) {
+ main_input_file = file;
+}
+
+String *scanner_get_main_input_file(void) {
+ return main_input_file;
+}
+
+/* ----------------------------------------------------------------------------
+ * int yylex()
+ *
+ * Gets the lexene and returns tokens.
+ * ------------------------------------------------------------------------- */
+
+int yylex(void) {
+
+ int l;
+ char *yytext;
+
+ if (!scan_init) {
+ scanner_init();
+ }
+
+ Delete(cparse_unknown_directive);
+ cparse_unknown_directive = NULL;
+
+ if (next_token) {
+ l = next_token;
+ next_token = 0;
+ return l;
+ }
+
+ l = yylook();
+
+ /* Swig_diagnostic(cparse_file, cparse_line, ":::%d: '%s'\n", l, Scanner_text(scan)); */
+
+ if (l == NONID) {
+ last_id = 1;
+ } else {
+ last_id = 0;
+ }
+
+ /* We got some sort of non-white space object. We set the start_line
+ variable unless it has already been set */
+
+ if (!cparse_start_line) {
+ cparse_start_line = cparse_line;
+ }
+
+ /* Copy the lexene */
+
+ switch (l) {
+
+ case NUM_INT:
+ case NUM_FLOAT:
+ case NUM_ULONG:
+ case NUM_LONG:
+ case NUM_UNSIGNED:
+ case NUM_LONGLONG:
+ case NUM_ULONGLONG:
+ case NUM_BOOL:
+ if (l == NUM_INT)
+ yylval.dtype.type = T_INT;
+ if (l == NUM_FLOAT)
+ yylval.dtype.type = T_DOUBLE;
+ if (l == NUM_ULONG)
+ yylval.dtype.type = T_ULONG;
+ if (l == NUM_LONG)
+ yylval.dtype.type = T_LONG;
+ if (l == NUM_UNSIGNED)
+ yylval.dtype.type = T_UINT;
+ if (l == NUM_LONGLONG)
+ yylval.dtype.type = T_LONGLONG;
+ if (l == NUM_ULONGLONG)
+ yylval.dtype.type = T_ULONGLONG;
+ if (l == NUM_BOOL)
+ yylval.dtype.type = T_BOOL;
+ yylval.dtype.val = NewString(Scanner_text(scan));
+ yylval.dtype.bitfield = 0;
+ yylval.dtype.throws = 0;
+ return (l);
+
+ case ID:
+ yytext = Char(Scanner_text(scan));
+ if (yytext[0] != '%') {
+ /* Look for keywords now */
+
+ if (strcmp(yytext, "int") == 0) {
+ yylval.type = NewSwigType(T_INT);
+ return (TYPE_INT);
+ }
+ if (strcmp(yytext, "double") == 0) {
+ yylval.type = NewSwigType(T_DOUBLE);
+ return (TYPE_DOUBLE);
+ }
+ if (strcmp(yytext, "void") == 0) {
+ yylval.type = NewSwigType(T_VOID);
+ return (TYPE_VOID);
+ }
+ if (strcmp(yytext, "char") == 0) {
+ yylval.type = NewSwigType(T_CHAR);
+ return (TYPE_CHAR);
+ }
+ if (strcmp(yytext, "wchar_t") == 0) {
+ yylval.type = NewSwigType(T_WCHAR);
+ return (TYPE_WCHAR);
+ }
+ if (strcmp(yytext, "short") == 0) {
+ yylval.type = NewSwigType(T_SHORT);
+ return (TYPE_SHORT);
+ }
+ if (strcmp(yytext, "long") == 0) {
+ yylval.type = NewSwigType(T_LONG);
+ return (TYPE_LONG);
+ }
+ if (strcmp(yytext, "float") == 0) {
+ yylval.type = NewSwigType(T_FLOAT);
+ return (TYPE_FLOAT);
+ }
+ if (strcmp(yytext, "signed") == 0) {
+ yylval.type = NewSwigType(T_INT);
+ return (TYPE_SIGNED);
+ }
+ if (strcmp(yytext, "unsigned") == 0) {
+ yylval.type = NewSwigType(T_UINT);
+ return (TYPE_UNSIGNED);
+ }
+ if (strcmp(yytext, "bool") == 0) {
+ yylval.type = NewSwigType(T_BOOL);
+ return (TYPE_BOOL);
+ }
+
+ /* Non ISO (Windows) C extensions */
+ if (strcmp(yytext, "__int8") == 0) {
+ yylval.type = NewString(yytext);
+ return (TYPE_NON_ISO_INT8);
+ }
+ if (strcmp(yytext, "__int16") == 0) {
+ yylval.type = NewString(yytext);
+ return (TYPE_NON_ISO_INT16);
+ }
+ if (strcmp(yytext, "__int32") == 0) {
+ yylval.type = NewString(yytext);
+ return (TYPE_NON_ISO_INT32);
+ }
+ if (strcmp(yytext, "__int64") == 0) {
+ yylval.type = NewString(yytext);
+ return (TYPE_NON_ISO_INT64);
+ }
+
+ /* C++ keywords */
+ if (cparse_cplusplus) {
+ if (strcmp(yytext, "and") == 0)
+ return (LAND);
+ if (strcmp(yytext, "or") == 0)
+ return (LOR);
+ if (strcmp(yytext, "not") == 0)
+ return (LNOT);
+ if (strcmp(yytext, "class") == 0)
+ return (CLASS);
+ if (strcmp(yytext, "private") == 0)
+ return (PRIVATE);
+ if (strcmp(yytext, "public") == 0)
+ return (PUBLIC);
+ if (strcmp(yytext, "protected") == 0)
+ return (PROTECTED);
+ if (strcmp(yytext, "friend") == 0)
+ return (FRIEND);
+ if (strcmp(yytext, "constexpr") == 0)
+ return (CONSTEXPR);
+ if (strcmp(yytext, "thread_local") == 0)
+ return (THREAD_LOCAL);
+ if (strcmp(yytext, "decltype") == 0)
+ return (DECLTYPE);
+ if (strcmp(yytext, "virtual") == 0)
+ return (VIRTUAL);
+ if (strcmp(yytext, "static_assert") == 0)
+ return (STATIC_ASSERT);
+ if (strcmp(yytext, "operator") == 0) {
+ int nexttok;
+ String *s = NewString("operator ");
+
+ /* If we have an operator, we have to collect the operator symbol and attach it to
+ the operator identifier. To do this, we need to scan ahead by several tokens.
+ Cases include:
+
+ (1) If the next token is an operator as determined by Scanner_isoperator(),
+ it means that the operator applies to one of the standard C++ mathematical,
+ assignment, or logical operator symbols (e.g., '+','<=','==','&', etc.)
+ In this case, we merely append the symbol text to the operator string above.
+
+ (2) If the next token is (, we look for ). This is operator ().
+ (3) If the next token is [, we look for ]. This is operator [].
+ (4) If the next token is an identifier. The operator is possibly a conversion operator.
+ (a) Must check for special case new[] and delete[]
+
+ Error handling is somewhat tricky here. We'll try to back out gracefully if we can.
+
+ */
+
+ do {
+ nexttok = Scanner_token(scan);
+ } while (nexttok == SWIG_TOKEN_ENDLINE || nexttok == SWIG_TOKEN_COMMENT);
+
+ if (Scanner_isoperator(nexttok)) {
+ /* One of the standard C/C++ symbolic operators */
+ Append(s,Scanner_text(scan));
+ yylval.str = s;
+ return OPERATOR;
+ } else if (nexttok == SWIG_TOKEN_LPAREN) {
+ /* Function call operator. The next token MUST be a RPAREN */
+ nexttok = Scanner_token(scan);
+ if (nexttok != SWIG_TOKEN_RPAREN) {
+ Swig_error(Scanner_file(scan),Scanner_line(scan),"Syntax error. Bad operator name.\n");
+ } else {
+ Append(s,"()");
+ yylval.str = s;
+ return OPERATOR;
+ }
+ } else if (nexttok == SWIG_TOKEN_LBRACKET) {
+ /* Array access operator. The next token MUST be a RBRACKET */
+ nexttok = Scanner_token(scan);
+ if (nexttok != SWIG_TOKEN_RBRACKET) {
+ Swig_error(Scanner_file(scan),Scanner_line(scan),"Syntax error. Bad operator name.\n");
+ } else {
+ Append(s,"[]");
+ yylval.str = s;
+ return OPERATOR;
+ }
+ } else if (nexttok == SWIG_TOKEN_STRING) {
+ /* Operator "" or user-defined string literal ""_suffix */
+ Append(s,"\"\"");
+ yylval.str = s;
+ return OPERATOR;
+ } else if (nexttok == SWIG_TOKEN_ID) {
+ /* We have an identifier. This could be any number of things. It could be a named version of
+ an operator (e.g., 'and_eq') or it could be a conversion operator. To deal with this, we're
+ going to read tokens until we encounter a ( or ;. Some care is needed for formatting. */
+ int needspace = 1;
+ int termtoken = 0;
+ const char *termvalue = 0;
+
+ Append(s,Scanner_text(scan));
+ while (1) {
+
+ nexttok = Scanner_token(scan);
+ if (nexttok <= 0) {
+ Swig_error(Scanner_file(scan),Scanner_line(scan),"Syntax error. Bad operator name.\n");
+ }
+ if (nexttok == SWIG_TOKEN_LPAREN) {
+ termtoken = SWIG_TOKEN_LPAREN;
+ termvalue = "(";
+ break;
+ } else if (nexttok == SWIG_TOKEN_CODEBLOCK) {
+ termtoken = SWIG_TOKEN_CODEBLOCK;
+ termvalue = Char(Scanner_text(scan));
+ break;
+ } else if (nexttok == SWIG_TOKEN_LBRACE) {
+ termtoken = SWIG_TOKEN_LBRACE;
+ termvalue = "{";
+ break;
+ } else if (nexttok == SWIG_TOKEN_SEMI) {
+ termtoken = SWIG_TOKEN_SEMI;
+ termvalue = ";";
+ break;
+ } else if (nexttok == SWIG_TOKEN_STRING) {
+ termtoken = SWIG_TOKEN_STRING;
+ termvalue = Swig_copy_string(Char(Scanner_text(scan)));
+ break;
+ } else if (nexttok == SWIG_TOKEN_ID) {
+ if (needspace) {
+ Append(s," ");
+ }
+ Append(s,Scanner_text(scan));
+ } else if (nexttok == SWIG_TOKEN_ENDLINE) {
+ } else if (nexttok == SWIG_TOKEN_COMMENT) {
+ } else {
+ Append(s,Scanner_text(scan));
+ needspace = 0;
+ }
+ }
+ yylval.str = s;
+ if (!rename_active) {
+ String *cs;
+ char *t = Char(s) + 9;
+ if (!((strcmp(t, "new") == 0)
+ || (strcmp(t, "delete") == 0)
+ || (strcmp(t, "new[]") == 0)
+ || (strcmp(t, "delete[]") == 0)
+ || (strcmp(t, "and") == 0)
+ || (strcmp(t, "and_eq") == 0)
+ || (strcmp(t, "bitand") == 0)
+ || (strcmp(t, "bitor") == 0)
+ || (strcmp(t, "compl") == 0)
+ || (strcmp(t, "not") == 0)
+ || (strcmp(t, "not_eq") == 0)
+ || (strcmp(t, "or") == 0)
+ || (strcmp(t, "or_eq") == 0)
+ || (strcmp(t, "xor") == 0)
+ || (strcmp(t, "xor_eq") == 0)
+ )) {
+ /* retract(strlen(t)); */
+
+ /* The operator is a conversion operator. In order to deal with this, we need to feed the
+ type information back into the parser. For now this is a hack. Needs to be cleaned up later. */
+ cs = NewString(t);
+ if (termtoken) Append(cs,termvalue);
+ Seek(cs,0,SEEK_SET);
+ Setline(cs,cparse_line);
+ Setfile(cs,cparse_file);
+ Scanner_push(scan,cs);
+ Delete(cs);
+ return CONVERSIONOPERATOR;
+ }
+ }
+ if (termtoken)
+ Scanner_pushtoken(scan, termtoken, termvalue);
+ return (OPERATOR);
+ }
+ }
+ if (strcmp(yytext, "throw") == 0)
+ return (THROW);
+ if (strcmp(yytext, "noexcept") == 0)
+ return (NOEXCEPT);
+ if (strcmp(yytext, "try") == 0)
+ return (yylex());
+ if (strcmp(yytext, "catch") == 0)
+ return (CATCH);
+ if (strcmp(yytext, "inline") == 0)
+ return (yylex());
+ if (strcmp(yytext, "mutable") == 0)
+ return (yylex());
+ if (strcmp(yytext, "explicit") == 0)
+ return (EXPLICIT);
+ if (strcmp(yytext, "auto") == 0)
+ return (AUTO);
+ if (strcmp(yytext, "export") == 0)
+ return (yylex());
+ if (strcmp(yytext, "typename") == 0)
+ return (TYPENAME);
+ if (strcmp(yytext, "template") == 0) {
+ yylval.intvalue = cparse_line;
+ return (TEMPLATE);
+ }
+ if (strcmp(yytext, "delete") == 0)
+ return (DELETE_KW);
+ if (strcmp(yytext, "default") == 0)
+ return (DEFAULT);
+ if (strcmp(yytext, "using") == 0)
+ return (USING);
+ if (strcmp(yytext, "namespace") == 0)
+ return (NAMESPACE);
+ if (strcmp(yytext, "override") == 0) {
+ last_id = 1;
+ return (OVERRIDE);
+ }
+ if (strcmp(yytext, "final") == 0) {
+ last_id = 1;
+ return (FINAL);
+ }
+ } else {
+ if (strcmp(yytext, "class") == 0) {
+ Swig_warning(WARN_PARSE_CLASS_KEYWORD, cparse_file, cparse_line, "class keyword used, but not in C++ mode.\n");
+ }
+ if (strcmp(yytext, "_Complex") == 0) {
+ yylval.type = NewSwigType(T_COMPLEX);
+ return (TYPE_COMPLEX);
+ }
+ if (strcmp(yytext, "restrict") == 0)
+ return (yylex());
+ }
+
+ /* Misc keywords */
+
+ if (strcmp(yytext, "extern") == 0)
+ return (EXTERN);
+ if (strcmp(yytext, "const") == 0)
+ return (CONST_QUAL);
+ if (strcmp(yytext, "static") == 0)
+ return (STATIC);
+ if (strcmp(yytext, "struct") == 0)
+ return (STRUCT);
+ if (strcmp(yytext, "union") == 0)
+ return (UNION);
+ if (strcmp(yytext, "enum") == 0)
+ return (ENUM);
+ if (strcmp(yytext, "sizeof") == 0)
+ return (SIZEOF);
+
+ if (strcmp(yytext, "typedef") == 0) {
+ yylval.intvalue = 0;
+ return (TYPEDEF);
+ }
+
+ /* Ignored keywords */
+
+ if (strcmp(yytext, "volatile") == 0)
+ return (VOLATILE);
+ if (strcmp(yytext, "register") == 0)
+ return (REGISTER);
+ if (strcmp(yytext, "inline") == 0)
+ return (yylex());
+
+ } else {
+ /* SWIG directives */
+ String *stext = 0;
+ if (strcmp(yytext, "%module") == 0)
+ return (MODULE);
+ if (strcmp(yytext, "%insert") == 0)
+ return (INSERT);
+ if (strcmp(yytext, "%name") == 0)
+ return (NAME);
+ if (strcmp(yytext, "%rename") == 0) {
+ rename_active = 1;
+ return (RENAME);
+ }
+ if (strcmp(yytext, "%namewarn") == 0) {
+ rename_active = 1;
+ return (NAMEWARN);
+ }
+ if (strcmp(yytext, "%includefile") == 0)
+ return (INCLUDE);
+ if (strcmp(yytext, "%beginfile") == 0)
+ return (BEGINFILE);
+ if (strcmp(yytext, "%endoffile") == 0)
+ return (ENDOFFILE);
+ if (strcmp(yytext, "%val") == 0) {
+ Swig_warning(WARN_DEPRECATED_VAL, cparse_file, cparse_line, "%%val directive deprecated (ignored).\n");
+ return (yylex());
+ }
+ if (strcmp(yytext, "%out") == 0) {
+ Swig_warning(WARN_DEPRECATED_OUT, cparse_file, cparse_line, "%%out directive deprecated (ignored).\n");
+ return (yylex());
+ }
+ if (strcmp(yytext, "%constant") == 0)
+ return (CONSTANT);
+ if (strcmp(yytext, "%typedef") == 0) {
+ yylval.intvalue = 1;
+ return (TYPEDEF);
+ }
+ if (strcmp(yytext, "%native") == 0)
+ return (NATIVE);
+ if (strcmp(yytext, "%pragma") == 0)
+ return (PRAGMA);
+ if (strcmp(yytext, "%extend") == 0)
+ return (EXTEND);
+ if (strcmp(yytext, "%fragment") == 0)
+ return (FRAGMENT);
+ if (strcmp(yytext, "%inline") == 0)
+ return (INLINE);
+ if (strcmp(yytext, "%typemap") == 0)
+ return (TYPEMAP);
+ if (strcmp(yytext, "%feature") == 0) {
+ /* The rename_active indicates we don't need the information of the
+ * following function's return type. This applied for %rename, so do
+ * %feature.
+ */
+ rename_active = 1;
+ return (FEATURE);
+ }
+ if (strcmp(yytext, "%except") == 0)
+ return (EXCEPT);
+ if (strcmp(yytext, "%importfile") == 0)
+ return (IMPORT);
+ if (strcmp(yytext, "%echo") == 0)
+ return (ECHO);
+ if (strcmp(yytext, "%apply") == 0)
+ return (APPLY);
+ if (strcmp(yytext, "%clear") == 0)
+ return (CLEAR);
+ if (strcmp(yytext, "%types") == 0)
+ return (TYPES);
+ if (strcmp(yytext, "%parms") == 0)
+ return (PARMS);
+ if (strcmp(yytext, "%varargs") == 0)
+ return (VARARGS);
+ if (strcmp(yytext, "%template") == 0) {
+ return (SWIGTEMPLATE);
+ }
+ if (strcmp(yytext, "%warn") == 0)
+ return (WARN);
+
+ /* Note down the apparently unknown directive for error reporting - if
+ * we end up reporting a generic syntax error we'll instead report an
+ * error for his as an unknown directive. Then we treat it as MODULO
+ * (`%`) followed by an identifier and if that parses OK then
+ * `cparse_unknown_directive` doesn't get used.
+ *
+ * This allows `a%b` to be handled in expressions without a space after
+ * the operator.
+ */
+ cparse_unknown_directive = NewString(yytext);
+ stext = NewString(yytext + 1);
+ Seek(stext,0,SEEK_SET);
+ Setfile(stext,cparse_file);
+ Setline(stext,cparse_line);
+ Scanner_push(scan,stext);
+ Delete(stext);
+ return (MODULO);
+ }
+ /* Have an unknown identifier, as a last step, we'll do a typedef lookup on it. */
+
+ /* Need to fix this */
+ if (check_typedef) {
+ if (SwigType_istypedef(yytext)) {
+ yylval.type = NewString(yytext);
+ return (TYPE_TYPEDEF);
+ }
+ }
+ yylval.id = Swig_copy_string(yytext);
+ last_id = 1;
+ return (ID);
+ case POUND:
+ return yylex();
+ case SWIG_TOKEN_COMMENT:
+ return yylex();
+ default:
+ return (l);
+ }
+}
diff --git a/contrib/tools/swig/Source/CParse/parser.y b/contrib/tools/swig/Source/CParse/parser.y
new file mode 100644
index 0000000000..97b467b5c4
--- /dev/null
+++ b/contrib/tools/swig/Source/CParse/parser.y
@@ -0,0 +1,7499 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * parser.y
+ *
+ * YACC parser for SWIG. The grammar is a somewhat broken subset of C/C++.
+ * This file is a bit of a mess and probably needs to be rewritten at
+ * some point. Beware.
+ * ----------------------------------------------------------------------------- */
+
+/* There are a small number of known shift-reduce conflicts in this file, fail
+ compilation if any more are introduced.
+
+ Please don't increase the number of the conflicts if at all possible. And if
+ you really have no choice but to do it, make sure you clearly document each
+ new conflict in this file.
+ */
+%expect 7
+
+%{
+#define yylex yylex
+
+/* doh.h uses #pragma GCC poison with GCC to prevent direct calls to certain
+ * standard C library functions being introduced, but those cause errors due
+ * to checks like `#if defined YYMALLOC || defined malloc` in the bison
+ * template code. We can't easily arrange to include headers after that
+ * template code, so instead we disable the problematic poisoning for this
+ * file.
+ */
+#define DOH_NO_POISON_MALLOC_FREE
+
+#include "swig.h"
+#include "cparse.h"
+#include "preprocessor.h"
+#include <ctype.h>
+
+/* We do this for portability */
+#undef alloca
+#define alloca Malloc
+
+#define YYMALLOC Malloc
+#define YYFREE Free
+
+/* -----------------------------------------------------------------------------
+ * Externals
+ * ----------------------------------------------------------------------------- */
+
+int yyparse(void);
+
+/* NEW Variables */
+
+static Node *top = 0; /* Top of the generated parse tree */
+static int unnamed = 0; /* Unnamed datatype counter */
+static Hash *classes = 0; /* Hash table of classes */
+static Hash *classes_typedefs = 0; /* Hash table of typedef classes: typedef struct X {...} Y; */
+static Symtab *prev_symtab = 0;
+static Node *current_class = 0;
+String *ModuleName = 0;
+static Node *module_node = 0;
+static String *Classprefix = 0;
+static String *Namespaceprefix = 0;
+static int inclass = 0;
+static Node *currentOuterClass = 0; /* for nested classes */
+static const char *last_cpptype = 0;
+static int inherit_list = 0;
+static Parm *template_parameters = 0;
+static int parsing_template_declaration = 0;
+static int extendmode = 0;
+static int compact_default_args = 0;
+static int template_reduce = 0;
+static int cparse_externc = 0;
+int ignore_nested_classes = 0;
+int kwargs_supported = 0;
+/* -----------------------------------------------------------------------------
+ * Doxygen Comment Globals
+ * ----------------------------------------------------------------------------- */
+static String *currentDeclComment = NULL; /* Comment of C/C++ declaration. */
+static Node *previousNode = NULL; /* Pointer to the previous node (for post comments) */
+static Node *currentNode = NULL; /* Pointer to the current node (for post comments) */
+
+/* -----------------------------------------------------------------------------
+ * Assist Functions
+ * ----------------------------------------------------------------------------- */
+
+
+
+/* Called by the parser (yyparse) when an error is found.*/
+static void yyerror (const char *e) {
+ (void)e;
+}
+
+static Node *new_node(const_String_or_char_ptr tag) {
+ Node *n = Swig_cparse_new_node(tag);
+ /* Remember the previous node in case it will need a post-comment */
+ previousNode = currentNode;
+ currentNode = n;
+ return n;
+}
+
+/* Copies a node. Does not copy tree links or symbol table data (except for
+ sym:name) */
+
+static Node *copy_node(Node *n) {
+ Node *nn;
+ Iterator k;
+ nn = NewHash();
+ Setfile(nn,Getfile(n));
+ Setline(nn,Getline(n));
+ for (k = First(n); k.key; k = Next(k)) {
+ String *ci;
+ String *key = k.key;
+ char *ckey = Char(key);
+ if ((strcmp(ckey,"nextSibling") == 0) ||
+ (strcmp(ckey,"previousSibling") == 0) ||
+ (strcmp(ckey,"parentNode") == 0) ||
+ (strcmp(ckey,"lastChild") == 0)) {
+ continue;
+ }
+ if (Strncmp(key,"csym:",5) == 0) continue;
+ /* We do copy sym:name. For templates */
+ if ((strcmp(ckey,"sym:name") == 0) ||
+ (strcmp(ckey,"sym:weak") == 0) ||
+ (strcmp(ckey,"sym:typename") == 0)) {
+ String *ci = Copy(k.item);
+ Setattr(nn,key, ci);
+ Delete(ci);
+ continue;
+ }
+ if (strcmp(ckey,"sym:symtab") == 0) {
+ Setattr(nn,"sym:needs_symtab", "1");
+ }
+ /* We don't copy any other symbol table attributes */
+ if (strncmp(ckey,"sym:",4) == 0) {
+ continue;
+ }
+ /* If children. We copy them recursively using this function */
+ if (strcmp(ckey,"firstChild") == 0) {
+ /* Copy children */
+ Node *cn = k.item;
+ while (cn) {
+ Node *copy = copy_node(cn);
+ appendChild(nn,copy);
+ Delete(copy);
+ cn = nextSibling(cn);
+ }
+ continue;
+ }
+ /* We don't copy the symbol table. But we drop an attribute
+ requires_symtab so that functions know it needs to be built */
+
+ if (strcmp(ckey,"symtab") == 0) {
+ /* Node defined a symbol table. */
+ Setattr(nn,"requires_symtab","1");
+ continue;
+ }
+ /* Can't copy nodes */
+ if (strcmp(ckey,"node") == 0) {
+ continue;
+ }
+ if ((strcmp(ckey,"parms") == 0) || (strcmp(ckey,"pattern") == 0) || (strcmp(ckey,"throws") == 0)
+ || (strcmp(ckey,"kwargs") == 0)) {
+ ParmList *pl = CopyParmList(k.item);
+ Setattr(nn,key,pl);
+ Delete(pl);
+ continue;
+ }
+ if (strcmp(ckey,"nested:outer") == 0) { /* don't copy outer classes links, they will be updated later */
+ Setattr(nn, key, k.item);
+ continue;
+ }
+ /* defaultargs will be patched back in later in update_defaultargs() */
+ if (strcmp(ckey,"defaultargs") == 0) {
+ Setattr(nn, "needs_defaultargs", "1");
+ continue;
+ }
+ /* same for abstracts, which contains pointers to the source node children, and so will need to be patch too */
+ if (strcmp(ckey,"abstracts") == 0) {
+ SetFlag(nn, "needs_abstracts");
+ continue;
+ }
+ /* Looks okay. Just copy the data using Copy */
+ ci = Copy(k.item);
+ Setattr(nn, key, ci);
+ Delete(ci);
+ }
+ return nn;
+}
+
+static void set_comment(Node *n, String *comment) {
+ String *name;
+ Parm *p;
+ if (!n || !comment)
+ return;
+
+ if (Getattr(n, "doxygen"))
+ Append(Getattr(n, "doxygen"), comment);
+ else {
+ Setattr(n, "doxygen", comment);
+ /* This is the first comment, populate it with @params, if any */
+ p = Getattr(n, "parms");
+ while (p) {
+ if (Getattr(p, "doxygen"))
+ Printv(comment, "\n@param ", Getattr(p, "name"), Getattr(p, "doxygen"), NIL);
+ p=nextSibling(p);
+ }
+ }
+
+ /* Append same comment to every generated overload */
+ name = Getattr(n, "name");
+ if (!name)
+ return;
+ n = nextSibling(n);
+ while (n && Getattr(n, "name") && Strcmp(Getattr(n, "name"), name) == 0) {
+ Setattr(n, "doxygen", comment);
+ n = nextSibling(n);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Variables
+ * ----------------------------------------------------------------------------- */
+
+static char *typemap_lang = 0; /* Current language setting */
+
+static int cplus_mode = 0;
+
+/* C++ modes */
+
+#define CPLUS_PUBLIC 1
+#define CPLUS_PRIVATE 2
+#define CPLUS_PROTECTED 3
+
+/* include types */
+static int import_mode = 0;
+
+void SWIG_typemap_lang(const char *tm_lang) {
+ typemap_lang = Swig_copy_string(tm_lang);
+}
+
+void SWIG_cparse_set_compact_default_args(int defargs) {
+ compact_default_args = defargs;
+}
+
+int SWIG_cparse_template_reduce(int treduce) {
+ template_reduce = treduce;
+ return treduce;
+}
+
+/* -----------------------------------------------------------------------------
+ * Assist functions
+ * ----------------------------------------------------------------------------- */
+
+static int promote_type(int t) {
+ if (t <= T_UCHAR || t == T_CHAR || t == T_WCHAR) return T_INT;
+ return t;
+}
+
+/* Perform type-promotion for binary operators */
+static int promote(int t1, int t2) {
+ t1 = promote_type(t1);
+ t2 = promote_type(t2);
+ return t1 > t2 ? t1 : t2;
+}
+
+static String *yyrename = 0;
+
+/* Forward renaming operator */
+
+static String *resolve_create_node_scope(String *cname, int is_class_definition);
+
+
+Hash *Swig_cparse_features(void) {
+ static Hash *features_hash = 0;
+ if (!features_hash) features_hash = NewHash();
+ return features_hash;
+}
+
+/* Fully qualify any template parameters */
+static String *feature_identifier_fix(String *s) {
+ String *tp = SwigType_istemplate_templateprefix(s);
+ if (tp) {
+ String *ts, *ta, *tq;
+ ts = SwigType_templatesuffix(s);
+ ta = SwigType_templateargs(s);
+ tq = Swig_symbol_type_qualify(ta,0);
+ Append(tp,tq);
+ Append(tp,ts);
+ Delete(ts);
+ Delete(ta);
+ Delete(tq);
+ return tp;
+ } else {
+ return NewString(s);
+ }
+}
+
+static void set_access_mode(Node *n) {
+ if (cplus_mode == CPLUS_PUBLIC)
+ Setattr(n, "access", "public");
+ else if (cplus_mode == CPLUS_PROTECTED)
+ Setattr(n, "access", "protected");
+ else
+ Setattr(n, "access", "private");
+}
+
+static void restore_access_mode(Node *n) {
+ String *mode = Getattr(n, "access");
+ if (Strcmp(mode, "private") == 0)
+ cplus_mode = CPLUS_PRIVATE;
+ else if (Strcmp(mode, "protected") == 0)
+ cplus_mode = CPLUS_PROTECTED;
+ else
+ cplus_mode = CPLUS_PUBLIC;
+}
+
+/* Generate the symbol table name for an object */
+/* This is a bit of a mess. Need to clean up */
+static String *add_oldname = 0;
+
+
+
+static String *make_name(Node *n, String *name,SwigType *decl) {
+ String *made_name = 0;
+ int destructor = name && (*(Char(name)) == '~');
+
+ if (yyrename) {
+ String *s = NewString(yyrename);
+ Delete(yyrename);
+ yyrename = 0;
+ if (destructor && (*(Char(s)) != '~')) {
+ Insert(s,0,"~");
+ }
+ return s;
+ }
+
+ if (!name) return 0;
+
+ if (parsing_template_declaration)
+ SetFlag(n, "parsing_template_declaration");
+ made_name = Swig_name_make(n, Namespaceprefix, name, decl, add_oldname);
+ Delattr(n, "parsing_template_declaration");
+
+ return made_name;
+}
+
+/* Generate an unnamed identifier */
+static String *make_unnamed(void) {
+ unnamed++;
+ return NewStringf("$unnamed%d$",unnamed);
+}
+
+/* Return if the node is a friend declaration */
+static int is_friend(Node *n) {
+ return Cmp(Getattr(n,"storage"),"friend") == 0;
+}
+
+static int is_operator(String *name) {
+ return Strncmp(name,"operator ", 9) == 0;
+}
+
+
+/* Add declaration list to symbol table */
+static int add_only_one = 0;
+
+static void add_symbols(Node *n) {
+ String *decl;
+ String *wrn = 0;
+
+ if (inclass && n) {
+ cparse_normalize_void(n);
+ }
+ while (n) {
+ String *symname = 0;
+ /* for friends, we need to pop the scope once */
+ String *old_prefix = 0;
+ Symtab *old_scope = 0;
+ int isfriend = inclass && is_friend(n);
+ int iscdecl = Cmp(nodeType(n),"cdecl") == 0;
+ int only_csymbol = 0;
+
+ if (inclass) {
+ String *name = Getattr(n, "name");
+ if (isfriend) {
+ /* for friends, we need to add the scopename if needed */
+ String *prefix = name ? Swig_scopename_prefix(name) : 0;
+ old_prefix = Namespaceprefix;
+ old_scope = Swig_symbol_popscope();
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ if (!prefix) {
+ if (name && !is_operator(name) && Namespaceprefix) {
+ String *nname = NewStringf("%s::%s", Namespaceprefix, name);
+ Setattr(n,"name",nname);
+ Delete(nname);
+ }
+ } else {
+ Symtab *st = Swig_symbol_getscope(prefix);
+ String *ns = st ? Getattr(st,"name") : prefix;
+ String *base = Swig_scopename_last(name);
+ String *nname = NewStringf("%s::%s", ns, base);
+ Setattr(n,"name",nname);
+ Delete(nname);
+ Delete(base);
+ Delete(prefix);
+ }
+ Namespaceprefix = 0;
+ } else {
+ /* for member functions, we need to remove the redundant
+ class scope if provided, as in
+
+ struct Foo {
+ int Foo::method(int a);
+ };
+
+ */
+ String *prefix = name ? Swig_scopename_prefix(name) : 0;
+ if (prefix) {
+ if (Classprefix && (Equal(prefix,Classprefix))) {
+ String *base = Swig_scopename_last(name);
+ Setattr(n,"name",base);
+ Delete(base);
+ }
+ Delete(prefix);
+ }
+ }
+ }
+
+ if (!isfriend && (inclass || extendmode)) {
+ Setattr(n,"ismember","1");
+ }
+
+ if (extendmode) {
+ if (!Getattr(n, "template"))
+ SetFlag(n,"isextendmember");
+ }
+
+ if (!isfriend && inclass) {
+ if ((cplus_mode != CPLUS_PUBLIC)) {
+ only_csymbol = 1;
+ if (cplus_mode == CPLUS_PROTECTED) {
+ Setattr(n,"access", "protected");
+ only_csymbol = !Swig_need_protected(n);
+ } else {
+ Setattr(n,"access", "private");
+ /* private are needed only when they are pure virtuals - why? */
+ if ((Cmp(Getattr(n,"storage"),"virtual") == 0) && (Cmp(Getattr(n,"value"),"0") == 0)) {
+ only_csymbol = 0;
+ }
+ if (Cmp(nodeType(n),"destructor") == 0) {
+ /* Needed for "unref" feature */
+ only_csymbol = 0;
+ }
+ }
+ } else {
+ Setattr(n,"access", "public");
+ }
+ }
+ if (Getattr(n,"sym:name")) {
+ n = nextSibling(n);
+ continue;
+ }
+ decl = Getattr(n,"decl");
+ if (!SwigType_isfunction(decl)) {
+ String *name = Getattr(n,"name");
+ String *makename = Getattr(n,"parser:makename");
+ if (iscdecl) {
+ String *storage = Getattr(n, "storage");
+ if (Cmp(storage,"typedef") == 0) {
+ Setattr(n,"kind","typedef");
+ } else {
+ SwigType *type = Getattr(n,"type");
+ String *value = Getattr(n,"value");
+ Setattr(n,"kind","variable");
+ if (value && Len(value)) {
+ Setattr(n,"hasvalue","1");
+ }
+ if (type) {
+ SwigType *ty;
+ SwigType *tmp = 0;
+ if (decl) {
+ ty = tmp = Copy(type);
+ SwigType_push(ty,decl);
+ } else {
+ ty = type;
+ }
+ if (!SwigType_ismutable(ty) || (storage && Strstr(storage, "constexpr"))) {
+ SetFlag(n,"hasconsttype");
+ SetFlag(n,"feature:immutable");
+ }
+ if (tmp) Delete(tmp);
+ }
+ if (!type) {
+ Printf(stderr,"notype name %s\n", name);
+ }
+ }
+ }
+ Swig_features_get(Swig_cparse_features(), Namespaceprefix, name, 0, n);
+ if (makename) {
+ symname = make_name(n, makename,0);
+ Delattr(n,"parser:makename"); /* temporary information, don't leave it hanging around */
+ } else {
+ makename = name;
+ symname = make_name(n, makename,0);
+ }
+
+ if (!symname) {
+ symname = Copy(Getattr(n,"unnamed"));
+ }
+ if (symname) {
+ if (parsing_template_declaration)
+ SetFlag(n, "parsing_template_declaration");
+ wrn = Swig_name_warning(n, Namespaceprefix, symname,0);
+ Delattr(n, "parsing_template_declaration");
+ }
+ } else {
+ String *name = Getattr(n,"name");
+ SwigType *fdecl = Copy(decl);
+ SwigType *fun = SwigType_pop_function(fdecl);
+ if (iscdecl) {
+ Setattr(n,"kind","function");
+ }
+
+ Swig_features_get(Swig_cparse_features(),Namespaceprefix,name,fun,n);
+
+ symname = make_name(n, name,fun);
+ if (parsing_template_declaration)
+ SetFlag(n, "parsing_template_declaration");
+ wrn = Swig_name_warning(n, Namespaceprefix,symname,fun);
+ Delattr(n, "parsing_template_declaration");
+
+ Delete(fdecl);
+ Delete(fun);
+
+ }
+ if (!symname) {
+ n = nextSibling(n);
+ continue;
+ }
+ if (cparse_cplusplus) {
+ String *value = Getattr(n, "value");
+ if (value && Strcmp(value, "delete") == 0) {
+ /* C++11 deleted definition / deleted function */
+ SetFlag(n,"deleted");
+ SetFlag(n,"feature:ignore");
+ }
+ if (SwigType_isrvalue_reference(Getattr(n, "refqualifier"))) {
+ /* Ignore rvalue ref-qualifiers by default
+ * Use Getattr instead of GetFlag to handle explicit ignore and explicit not ignore */
+ if (!(Getattr(n, "feature:ignore") || Strncmp(symname, "$ignore", 7) == 0)) {
+ SWIG_WARN_NODE_BEGIN(n);
+ Swig_warning(WARN_TYPE_RVALUE_REF_QUALIFIER_IGNORED, Getfile(n), Getline(n),
+ "Method with rvalue ref-qualifier %s ignored.\n", Swig_name_decl(n));
+ SWIG_WARN_NODE_END(n);
+ SetFlag(n, "feature:ignore");
+ }
+ }
+ }
+ if (only_csymbol || GetFlag(n, "feature:ignore") || Strncmp(symname, "$ignore", 7) == 0) {
+ /* Only add to C symbol table and continue */
+ Swig_symbol_add(0, n);
+ if (!only_csymbol && !GetFlag(n, "feature:ignore")) {
+ /* Print the warning attached to $ignore name, if any */
+ char *c = Char(symname) + 7;
+ if (strlen(c)) {
+ SWIG_WARN_NODE_BEGIN(n);
+ Swig_warning(0,Getfile(n), Getline(n), "%s\n",c+1);
+ SWIG_WARN_NODE_END(n);
+ }
+ /* If the symbol was ignored via "rename" and is visible, set also feature:ignore*/
+ SetFlag(n, "feature:ignore");
+ }
+ if (!GetFlag(n, "feature:ignore") && Strcmp(symname,"$ignore") == 0) {
+ /* Add feature:ignore if the symbol was explicitly ignored, regardless of visibility */
+ SetFlag(n, "feature:ignore");
+ }
+ } else {
+ Node *c;
+ if ((wrn) && (Len(wrn))) {
+ String *metaname = symname;
+ if (!Getmeta(metaname,"already_warned")) {
+ SWIG_WARN_NODE_BEGIN(n);
+ Swig_warning(0,Getfile(n),Getline(n), "%s\n", wrn);
+ SWIG_WARN_NODE_END(n);
+ Setmeta(metaname,"already_warned","1");
+ }
+ }
+ c = Swig_symbol_add(symname,n);
+
+ if (c != n) {
+ /* symbol conflict attempting to add in the new symbol */
+ if (Getattr(n,"sym:weak")) {
+ Setattr(n,"sym:name",symname);
+ } else {
+ String *e = NewStringEmpty();
+ String *en = NewStringEmpty();
+ String *ec = NewStringEmpty();
+ int redefined = Swig_need_redefined_warn(n,c,inclass);
+ if (redefined) {
+ Printf(en,"Identifier '%s' redefined (ignored)",symname);
+ Printf(ec,"previous definition of '%s'",symname);
+ } else {
+ Printf(en,"Redundant redeclaration of '%s'",symname);
+ Printf(ec,"previous declaration of '%s'",symname);
+ }
+ if (Cmp(symname,Getattr(n,"name"))) {
+ Printf(en," (Renamed from '%s')", SwigType_namestr(Getattr(n,"name")));
+ }
+ Printf(en,",");
+ if (Cmp(symname,Getattr(c,"name"))) {
+ Printf(ec," (Renamed from '%s')", SwigType_namestr(Getattr(c,"name")));
+ }
+ Printf(ec,".");
+ SWIG_WARN_NODE_BEGIN(n);
+ if (redefined) {
+ Swig_warning(WARN_PARSE_REDEFINED,Getfile(n),Getline(n),"%s\n",en);
+ Swig_warning(WARN_PARSE_REDEFINED,Getfile(c),Getline(c),"%s\n",ec);
+ } else if (!is_friend(n) && !is_friend(c)) {
+ Swig_warning(WARN_PARSE_REDUNDANT,Getfile(n),Getline(n),"%s\n",en);
+ Swig_warning(WARN_PARSE_REDUNDANT,Getfile(c),Getline(c),"%s\n",ec);
+ }
+ SWIG_WARN_NODE_END(n);
+ Printf(e,"%s:%d:%s\n%s:%d:%s\n",Getfile(n),Getline(n),en,
+ Getfile(c),Getline(c),ec);
+ Setattr(n,"error",e);
+ Delete(e);
+ Delete(en);
+ Delete(ec);
+ }
+ }
+ }
+ /* restore the class scope if needed */
+ if (isfriend) {
+ Swig_symbol_setscope(old_scope);
+ if (old_prefix) {
+ Delete(Namespaceprefix);
+ Namespaceprefix = old_prefix;
+ }
+ }
+ Delete(symname);
+
+ if (add_only_one) return;
+ n = nextSibling(n);
+ }
+}
+
+
+/* add symbols a parse tree node copy */
+
+static void add_symbols_copy(Node *n) {
+ String *name;
+ int emode = 0;
+ while (n) {
+ char *cnodeType = Char(nodeType(n));
+
+ if (strcmp(cnodeType,"access") == 0) {
+ String *kind = Getattr(n,"kind");
+ if (Strcmp(kind,"public") == 0) {
+ cplus_mode = CPLUS_PUBLIC;
+ } else if (Strcmp(kind,"private") == 0) {
+ cplus_mode = CPLUS_PRIVATE;
+ } else if (Strcmp(kind,"protected") == 0) {
+ cplus_mode = CPLUS_PROTECTED;
+ }
+ n = nextSibling(n);
+ continue;
+ }
+
+ add_oldname = Getattr(n,"sym:name");
+ if ((add_oldname) || (Getattr(n,"sym:needs_symtab"))) {
+ int old_inclass = -1;
+ Node *old_current_class = 0;
+ if (add_oldname) {
+ DohIncref(add_oldname);
+ /* Disable this, it prevents %rename to work with templates */
+ /* If already renamed, we used that name */
+ /*
+ if (Strcmp(add_oldname, Getattr(n,"name")) != 0) {
+ Delete(yyrename);
+ yyrename = Copy(add_oldname);
+ }
+ */
+ }
+ Delattr(n,"sym:needs_symtab");
+ Delattr(n,"sym:name");
+
+ add_only_one = 1;
+ add_symbols(n);
+
+ if (Getattr(n,"partialargs")) {
+ Swig_symbol_cadd(Getattr(n,"partialargs"),n);
+ }
+ add_only_one = 0;
+ name = Getattr(n,"name");
+ if (Getattr(n,"requires_symtab")) {
+ Swig_symbol_newscope();
+ Swig_symbol_setscopename(name);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ }
+ if (strcmp(cnodeType,"class") == 0) {
+ old_inclass = inclass;
+ inclass = 1;
+ old_current_class = current_class;
+ current_class = n;
+ if (Strcmp(Getattr(n,"kind"),"class") == 0) {
+ cplus_mode = CPLUS_PRIVATE;
+ } else {
+ cplus_mode = CPLUS_PUBLIC;
+ }
+ }
+ if (strcmp(cnodeType,"extend") == 0) {
+ emode = cplus_mode;
+ cplus_mode = CPLUS_PUBLIC;
+ }
+ add_symbols_copy(firstChild(n));
+ if (strcmp(cnodeType,"extend") == 0) {
+ cplus_mode = emode;
+ }
+ if (Getattr(n,"requires_symtab")) {
+ Setattr(n,"symtab", Swig_symbol_popscope());
+ Delattr(n,"requires_symtab");
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ }
+ if (add_oldname) {
+ Delete(add_oldname);
+ add_oldname = 0;
+ }
+ if (strcmp(cnodeType,"class") == 0) {
+ inclass = old_inclass;
+ current_class = old_current_class;
+ }
+ } else {
+ if (strcmp(cnodeType,"extend") == 0) {
+ emode = cplus_mode;
+ cplus_mode = CPLUS_PUBLIC;
+ }
+ add_symbols_copy(firstChild(n));
+ if (strcmp(cnodeType,"extend") == 0) {
+ cplus_mode = emode;
+ }
+ }
+ n = nextSibling(n);
+ }
+}
+
+/* Add in the "defaultargs" attribute for functions in instantiated templates.
+ * n should be any instantiated template (class or start of linked list of functions). */
+static void update_defaultargs(Node *n) {
+ if (n) {
+ Node *firstdefaultargs = n;
+ update_defaultargs(firstChild(n));
+ n = nextSibling(n);
+ /* recursively loop through nodes of all types, but all we really need are the overloaded functions */
+ while (n) {
+ update_defaultargs(firstChild(n));
+ if (!Getattr(n, "defaultargs")) {
+ if (Getattr(n, "needs_defaultargs")) {
+ Setattr(n, "defaultargs", firstdefaultargs);
+ Delattr(n, "needs_defaultargs");
+ } else {
+ firstdefaultargs = n;
+ }
+ } else {
+ /* Functions added in with %extend (for specialized template classes) will already have default args patched up */
+ assert(Getattr(n, "defaultargs") == firstdefaultargs);
+ }
+ n = nextSibling(n);
+ }
+ }
+}
+
+/* Check a set of declarations to see if any are pure-abstract */
+
+static List *pure_abstracts(Node *n) {
+ List *abstracts = 0;
+ while (n) {
+ if (Cmp(nodeType(n),"cdecl") == 0) {
+ String *decl = Getattr(n,"decl");
+ if (SwigType_isfunction(decl)) {
+ String *init = Getattr(n,"value");
+ if (Cmp(init,"0") == 0) {
+ if (!abstracts) {
+ abstracts = NewList();
+ }
+ Append(abstracts,n);
+ SetFlag(n,"abstract");
+ }
+ }
+ } else if (Cmp(nodeType(n),"destructor") == 0) {
+ if (Cmp(Getattr(n,"value"),"0") == 0) {
+ if (!abstracts) {
+ abstracts = NewList();
+ }
+ Append(abstracts,n);
+ SetFlag(n,"abstract");
+ }
+ }
+ n = nextSibling(n);
+ }
+ return abstracts;
+}
+
+/* Recompute the "abstracts" attribute for the classes in instantiated templates, similarly to update_defaultargs() above. */
+static void update_abstracts(Node *n) {
+ for (; n; n = nextSibling(n)) {
+ Node* const child = firstChild(n);
+ if (!child)
+ continue;
+
+ update_abstracts(child);
+
+ if (Getattr(n, "needs_abstracts")) {
+ Setattr(n, "abstracts", pure_abstracts(child));
+ Delattr(n, "needs_abstracts");
+ }
+ }
+}
+
+/* Make a classname */
+
+static String *make_class_name(String *name) {
+ String *nname = 0;
+ String *prefix;
+ if (Namespaceprefix) {
+ nname= NewStringf("%s::%s", Namespaceprefix, name);
+ } else {
+ nname = NewString(name);
+ }
+ prefix = SwigType_istemplate_templateprefix(nname);
+ if (prefix) {
+ String *args, *qargs;
+ args = SwigType_templateargs(nname);
+ qargs = Swig_symbol_type_qualify(args,0);
+ Append(prefix,qargs);
+ Delete(nname);
+ Delete(args);
+ Delete(qargs);
+ nname = prefix;
+ }
+ return nname;
+}
+
+/* Use typedef name as class name */
+
+static void add_typedef_name(Node *n, Node *declnode, String *oldName, Symtab *cscope, String *scpname) {
+ String *class_rename = 0;
+ SwigType *decl = Getattr(declnode, "decl");
+ if (!decl || !Len(decl)) {
+ String *cname;
+ String *tdscopename;
+ String *class_scope = Swig_symbol_qualifiedscopename(cscope);
+ String *name = Getattr(declnode, "name");
+ cname = Copy(name);
+ Setattr(n, "tdname", cname);
+ tdscopename = class_scope ? NewStringf("%s::%s", class_scope, name) : Copy(name);
+ class_rename = Getattr(n, "class_rename");
+ if (class_rename && (Strcmp(class_rename, oldName) == 0))
+ Setattr(n, "class_rename", NewString(name));
+ if (!classes_typedefs) classes_typedefs = NewHash();
+ if (!Equal(scpname, tdscopename) && !Getattr(classes_typedefs, tdscopename)) {
+ Setattr(classes_typedefs, tdscopename, n);
+ }
+ Setattr(n, "decl", decl);
+ Delete(class_scope);
+ Delete(cname);
+ Delete(tdscopename);
+ }
+}
+
+/* If the class name is qualified. We need to create or lookup namespace entries */
+
+static Symtab *set_scope_to_global(void) {
+ Symtab *symtab = Swig_symbol_global_scope();
+ Swig_symbol_setscope(symtab);
+ return symtab;
+}
+
+/* Remove the block braces, { and }, if the 'noblock' attribute is set.
+ * Node *kw can be either a Hash or Parmlist. */
+static String *remove_block(Node *kw, const String *inputcode) {
+ String *modified_code = 0;
+ while (kw) {
+ String *name = Getattr(kw,"name");
+ if (name && (Cmp(name,"noblock") == 0)) {
+ char *cstr = Char(inputcode);
+ int len = Len(inputcode);
+ if (len && cstr[0] == '{') {
+ --len; ++cstr;
+ if (len && cstr[len - 1] == '}') { --len; }
+ /* we now remove the extra spaces */
+ while (len && isspace((int)cstr[0])) { --len; ++cstr; }
+ while (len && isspace((int)cstr[len - 1])) { --len; }
+ modified_code = NewStringWithSize(cstr, len);
+ break;
+ }
+ }
+ kw = nextSibling(kw);
+ }
+ return modified_code;
+}
+
+/*
+#define RESOLVE_DEBUG 1
+*/
+static Node *nscope = 0;
+static Node *nscope_inner = 0;
+
+/* Remove the scope prefix from cname and return the base name without the prefix.
+ * The scopes required for the symbol name are resolved and/or created, if required.
+ * For example AA::BB::CC as input returns CC and creates the namespace AA then inner
+ * namespace BB in the current scope. */
+static String *resolve_create_node_scope(String *cname, int is_class_definition) {
+ Symtab *gscope = 0;
+ Node *cname_node = 0;
+ String *last = Swig_scopename_last(cname);
+ nscope = 0;
+ nscope_inner = 0;
+
+ if (Strncmp(cname,"::" ,2) != 0) {
+ if (is_class_definition) {
+ /* Only lookup symbols which are in scope via a using declaration but not via a using directive.
+ For example find y via 'using x::y' but not y via a 'using namespace x'. */
+ cname_node = Swig_symbol_clookup_no_inherit(cname, 0);
+ if (!cname_node) {
+ Node *full_lookup_node = Swig_symbol_clookup(cname, 0);
+ if (full_lookup_node) {
+ /* This finds a symbol brought into scope via both a using directive and a using declaration. */
+ Node *last_node = Swig_symbol_clookup_no_inherit(last, 0);
+ if (last_node == full_lookup_node)
+ cname_node = last_node;
+ }
+ }
+ } else {
+ /* For %template, the template needs to be in scope via any means. */
+ cname_node = Swig_symbol_clookup(cname, 0);
+ }
+ }
+#if RESOLVE_DEBUG
+ if (!cname_node)
+ Printf(stdout, "symbol does not yet exist (%d): [%s]\n", is_class_definition, cname);
+ else
+ Printf(stdout, "symbol does exist (%d): [%s]\n", is_class_definition, cname);
+#endif
+
+ if (cname_node) {
+ /* The symbol has been defined already or is in another scope.
+ If it is a weak symbol, it needs replacing and if it was brought into the current scope,
+ the scope needs adjusting appropriately for the new symbol.
+ Similarly for defined templates. */
+ Symtab *symtab = Getattr(cname_node, "sym:symtab");
+ Node *sym_weak = Getattr(cname_node, "sym:weak");
+ if ((symtab && sym_weak) || Equal(nodeType(cname_node), "template")) {
+ /* Check if the scope is the current scope */
+ String *current_scopename = Swig_symbol_qualifiedscopename(0);
+ String *found_scopename = Swig_symbol_qualifiedscopename(symtab);
+ if (!current_scopename)
+ current_scopename = NewString("");
+ if (!found_scopename)
+ found_scopename = NewString("");
+
+ {
+ int fail = 1;
+ List *current_scopes = Swig_scopename_tolist(current_scopename);
+ List *found_scopes = Swig_scopename_tolist(found_scopename);
+ Iterator cit = First(current_scopes);
+ Iterator fit = First(found_scopes);
+#if RESOLVE_DEBUG
+Printf(stdout, "comparing current: [%s] found: [%s]\n", current_scopename, found_scopename);
+#endif
+ for (; fit.item && cit.item; fit = Next(fit), cit = Next(cit)) {
+ String *current = cit.item;
+ String *found = fit.item;
+#if RESOLVE_DEBUG
+ Printf(stdout, " looping %s %s\n", current, found);
+#endif
+ if (Strcmp(current, found) != 0)
+ break;
+ }
+
+ if (!cit.item) {
+ String *subscope = NewString("");
+ for (; fit.item; fit = Next(fit)) {
+ if (Len(subscope) > 0)
+ Append(subscope, "::");
+ Append(subscope, fit.item);
+ }
+ if (Len(subscope) > 0)
+ cname = NewStringf("%s::%s", subscope, last);
+ else
+ cname = Copy(last);
+#if RESOLVE_DEBUG
+ Printf(stdout, "subscope to create: [%s] cname: [%s]\n", subscope, cname);
+#endif
+ fail = 0;
+ Delete(subscope);
+ } else {
+ if (is_class_definition) {
+ if (!fit.item) {
+ /* It is valid to define a new class with the same name as one forward declared in a parent scope */
+ fail = 0;
+ } else if (Swig_scopename_check(cname)) {
+ /* Classes defined with scope qualifiers must have a matching forward declaration in matching scope */
+ fail = 1;
+ } else {
+ /* This may let through some invalid cases */
+ fail = 0;
+ }
+#if RESOLVE_DEBUG
+ Printf(stdout, "scope for class definition, fail: %d\n", fail);
+#endif
+ } else {
+#if RESOLVE_DEBUG
+ Printf(stdout, "no matching base scope for template\n");
+#endif
+ fail = 1;
+ }
+ }
+
+ Delete(found_scopes);
+ Delete(current_scopes);
+
+ if (fail) {
+ String *cname_resolved = NewStringf("%s::%s", found_scopename, last);
+ Swig_error(cparse_file, cparse_line, "'%s' resolves to '%s' and was incorrectly instantiated in scope '%s' instead of within scope '%s'.\n", cname, cname_resolved, current_scopename, found_scopename);
+ cname = Copy(last);
+ Delete(cname_resolved);
+ }
+ }
+
+ Delete(current_scopename);
+ Delete(found_scopename);
+ }
+ } else if (!is_class_definition) {
+ /* A template instantiation requires a template to be found in scope... fail here too?
+ Swig_error(cparse_file, cparse_line, "No template found to instantiate '%s' with %%template.\n", cname);
+ */
+ }
+
+ if (Swig_scopename_check(cname)) {
+ Node *ns;
+ String *prefix = Swig_scopename_prefix(cname);
+ if (prefix && (Strncmp(prefix,"::",2) == 0)) {
+/* I don't think we can use :: global scope to declare classes and hence neither %template. - consider reporting error instead - wsfulton. */
+ /* Use the global scope */
+ String *nprefix = NewString(Char(prefix)+2);
+ Delete(prefix);
+ prefix= nprefix;
+ gscope = set_scope_to_global();
+ }
+ if (Len(prefix) == 0) {
+ String *base = Copy(last);
+ /* Use the global scope, but we need to add a 'global' namespace. */
+ if (!gscope) gscope = set_scope_to_global();
+ /* note that this namespace is not the "unnamed" one,
+ and we don't use Setattr(nscope,"name", ""),
+ because the unnamed namespace is private */
+ nscope = new_node("namespace");
+ Setattr(nscope,"symtab", gscope);;
+ nscope_inner = nscope;
+ Delete(last);
+ return base;
+ }
+ /* Try to locate the scope */
+ ns = Swig_symbol_clookup(prefix,0);
+ if (!ns) {
+ Swig_error(cparse_file,cparse_line,"Undefined scope '%s'\n", prefix);
+ } else {
+ Symtab *nstab = Getattr(ns,"symtab");
+ if (!nstab) {
+ Swig_error(cparse_file,cparse_line, "'%s' is not defined as a valid scope.\n", prefix);
+ ns = 0;
+ } else {
+ /* Check if the node scope is the current scope */
+ String *tname = Swig_symbol_qualifiedscopename(0);
+ String *nname = Swig_symbol_qualifiedscopename(nstab);
+ if (tname && (Strcmp(tname,nname) == 0)) {
+ ns = 0;
+ cname = Copy(last);
+ }
+ Delete(tname);
+ Delete(nname);
+ }
+ if (ns) {
+ /* we will try to create a new node using the namespaces we
+ can find in the scope name */
+ List *scopes = Swig_scopename_tolist(prefix);
+ String *sname;
+ Iterator si;
+
+ for (si = First(scopes); si.item; si = Next(si)) {
+ Node *ns1,*ns2;
+ sname = si.item;
+ ns1 = Swig_symbol_clookup(sname,0);
+ assert(ns1);
+ if (Strcmp(nodeType(ns1),"namespace") == 0) {
+ if (Getattr(ns1,"alias")) {
+ ns1 = Getattr(ns1,"namespace");
+ }
+ } else {
+ /* now this last part is a class */
+ si = Next(si);
+ /* or a nested class tree, which is unrolled here */
+ for (; si.item; si = Next(si)) {
+ if (si.item) {
+ Printf(sname,"::%s",si.item);
+ }
+ }
+ /* we get the 'inner' class */
+ nscope_inner = Swig_symbol_clookup(sname,0);
+ /* set the scope to the inner class */
+ Swig_symbol_setscope(Getattr(nscope_inner,"symtab"));
+ /* save the last namespace prefix */
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ /* and return the node name, including the inner class prefix */
+ break;
+ }
+ /* here we just populate the namespace tree as usual */
+ ns2 = new_node("namespace");
+ Setattr(ns2,"name",sname);
+ Setattr(ns2,"symtab", Getattr(ns1,"symtab"));
+ add_symbols(ns2);
+ Swig_symbol_setscope(Getattr(ns1,"symtab"));
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ if (nscope_inner) {
+ if (Getattr(nscope_inner,"symtab") != Getattr(ns2,"symtab")) {
+ appendChild(nscope_inner,ns2);
+ Delete(ns2);
+ }
+ }
+ nscope_inner = ns2;
+ if (!nscope) nscope = ns2;
+ }
+ cname = Copy(last);
+ Delete(scopes);
+ }
+ }
+ Delete(prefix);
+ }
+ Delete(last);
+
+ return cname;
+}
+
+/* look for simple typedef name in typedef list */
+static String *try_to_find_a_name_for_unnamed_structure(const char *storage, Node *decls) {
+ String *name = 0;
+ Node *n = decls;
+ if (storage && (strcmp(storage, "typedef") == 0)) {
+ for (; n; n = nextSibling(n)) {
+ if (!Len(Getattr(n, "decl"))) {
+ name = Copy(Getattr(n, "name"));
+ break;
+ }
+ }
+ }
+ return name;
+}
+
+/* traverse copied tree segment, and update outer class links*/
+static void update_nested_classes(Node *n)
+{
+ Node *c = firstChild(n);
+ while (c) {
+ if (Getattr(c, "nested:outer"))
+ Setattr(c, "nested:outer", n);
+ update_nested_classes(c);
+ c = nextSibling(c);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * nested_forward_declaration()
+ *
+ * Nested struct handling for C++ code if the nested classes are disabled.
+ * Create the nested class/struct/union as a forward declaration.
+ * ----------------------------------------------------------------------------- */
+
+static Node *nested_forward_declaration(const char *storage, const char *kind, String *sname, String *name, Node *cpp_opt_declarators) {
+ Node *nn = 0;
+
+ if (sname) {
+ /* Add forward declaration of the nested type */
+ Node *n = new_node("classforward");
+ Setattr(n, "kind", kind);
+ Setattr(n, "name", sname);
+ Setattr(n, "storage", storage);
+ Setattr(n, "sym:weak", "1");
+ add_symbols(n);
+ nn = n;
+ }
+
+ /* Add any variable instances. Also add in any further typedefs of the nested type.
+ Note that anonymous typedefs (eg typedef struct {...} a, b;) are treated as class forward declarations */
+ if (cpp_opt_declarators) {
+ int storage_typedef = (storage && (strcmp(storage, "typedef") == 0));
+ int variable_of_anonymous_type = !sname && !storage_typedef;
+ if (!variable_of_anonymous_type) {
+ int anonymous_typedef = !sname && (storage && (strcmp(storage, "typedef") == 0));
+ Node *n = cpp_opt_declarators;
+ SwigType *type = name;
+ while (n) {
+ Setattr(n, "type", type);
+ Setattr(n, "storage", storage);
+ if (anonymous_typedef) {
+ Setattr(n, "nodeType", "classforward");
+ Setattr(n, "sym:weak", "1");
+ }
+ n = nextSibling(n);
+ }
+ add_symbols(cpp_opt_declarators);
+
+ if (nn) {
+ set_nextSibling(nn, cpp_opt_declarators);
+ } else {
+ nn = cpp_opt_declarators;
+ }
+ }
+ }
+
+ if (!currentOuterClass || !GetFlag(currentOuterClass, "nested")) {
+ if (nn && Equal(nodeType(nn), "classforward")) {
+ Node *n = nn;
+ if (!GetFlag(n, "feature:ignore")) {
+ SWIG_WARN_NODE_BEGIN(n);
+ Swig_warning(WARN_PARSE_NAMED_NESTED_CLASS, cparse_file, cparse_line,"Nested %s not currently supported (%s ignored)\n", kind, sname ? sname : name);
+ SWIG_WARN_NODE_END(n);
+ }
+ } else {
+ Swig_warning(WARN_PARSE_UNNAMED_NESTED_CLASS, cparse_file, cparse_line, "Nested %s not currently supported (ignored).\n", kind);
+ }
+ }
+
+ return nn;
+}
+
+
+Node *Swig_cparse(File *f) {
+ scanner_file(f);
+ top = 0;
+ yyparse();
+ return top;
+}
+
+static void single_new_feature(const char *featurename, String *val, Hash *featureattribs, char *declaratorid, SwigType *type, ParmList *declaratorparms, String *qualifier) {
+ String *fname;
+ String *name;
+ String *fixname;
+ SwigType *t = Copy(type);
+
+ /* Printf(stdout, "single_new_feature: [%s] [%s] [%s] [%s] [%s] [%s]\n", featurename, val, declaratorid, t, ParmList_str_defaultargs(declaratorparms), qualifier); */
+
+ /* Warn about deprecated features */
+ if (strcmp(featurename, "nestedworkaround") == 0)
+ Swig_warning(WARN_DEPRECATED_NESTED_WORKAROUND, cparse_file, cparse_line, "The 'nestedworkaround' feature is deprecated.\n");
+
+ fname = NewStringf("feature:%s",featurename);
+ if (declaratorid) {
+ fixname = feature_identifier_fix(declaratorid);
+ } else {
+ fixname = NewStringEmpty();
+ }
+ if (Namespaceprefix) {
+ name = NewStringf("%s::%s",Namespaceprefix, fixname);
+ } else {
+ name = fixname;
+ }
+
+ if (declaratorparms) Setmeta(val,"parms",declaratorparms);
+ if (!Len(t)) t = 0;
+ if (t) {
+ if (qualifier) SwigType_push(t,qualifier);
+ if (SwigType_isfunction(t)) {
+ SwigType *decl = SwigType_pop_function(t);
+ if (SwigType_ispointer(t)) {
+ String *nname = NewStringf("*%s",name);
+ Swig_feature_set(Swig_cparse_features(), nname, decl, fname, val, featureattribs);
+ Delete(nname);
+ } else {
+ Swig_feature_set(Swig_cparse_features(), name, decl, fname, val, featureattribs);
+ }
+ Delete(decl);
+ } else if (SwigType_ispointer(t)) {
+ String *nname = NewStringf("*%s",name);
+ Swig_feature_set(Swig_cparse_features(),nname,0,fname,val, featureattribs);
+ Delete(nname);
+ }
+ } else {
+ /* Global feature, that is, feature not associated with any particular symbol */
+ Swig_feature_set(Swig_cparse_features(),name,0,fname,val, featureattribs);
+ }
+ Delete(fname);
+ Delete(name);
+}
+
+/* Add a new feature to the Hash. Additional features are added if the feature has a parameter list (declaratorparms)
+ * and one or more of the parameters have a default argument. An extra feature is added for each defaulted parameter,
+ * simulating the equivalent overloaded method. */
+static void new_feature(const char *featurename, String *val, Hash *featureattribs, char *declaratorid, SwigType *type, ParmList *declaratorparms, String *qualifier) {
+
+ ParmList *declparms = declaratorparms;
+
+ /* remove the { and } braces if the noblock attribute is set */
+ String *newval = remove_block(featureattribs, val);
+ val = newval ? newval : val;
+
+ /* Add the feature */
+ single_new_feature(featurename, val, featureattribs, declaratorid, type, declaratorparms, qualifier);
+
+ /* Add extra features if there are default parameters in the parameter list */
+ if (type) {
+ while (declparms) {
+ if (ParmList_has_defaultargs(declparms)) {
+
+ /* Create a parameter list for the new feature by copying all
+ but the last (defaulted) parameter */
+ ParmList* newparms = CopyParmListMax(declparms, ParmList_len(declparms)-1);
+
+ /* Create new declaration - with the last parameter removed */
+ SwigType *newtype = Copy(type);
+ Delete(SwigType_pop_function(newtype)); /* remove the old parameter list from newtype */
+ SwigType_add_function(newtype,newparms);
+
+ single_new_feature(featurename, Copy(val), featureattribs, declaratorid, newtype, newparms, qualifier);
+ declparms = newparms;
+ } else {
+ declparms = 0;
+ }
+ }
+ }
+}
+
+/* check if a function declaration is a plain C object */
+static int is_cfunction(Node *n) {
+ if (!cparse_cplusplus || cparse_externc)
+ return 1;
+ if (Swig_storage_isexternc(n)) {
+ return 1;
+ }
+ return 0;
+}
+
+/* If the Node is a function with parameters, check to see if any of the parameters
+ * have default arguments. If so create a new function for each defaulted argument.
+ * The additional functions form a linked list of nodes with the head being the original Node n. */
+static void default_arguments(Node *n) {
+ Node *function = n;
+
+ if (function) {
+ ParmList *varargs = Getattr(function,"feature:varargs");
+ if (varargs) {
+ /* Handles the %varargs directive by looking for "feature:varargs" and
+ * substituting ... with an alternative set of arguments. */
+ Parm *p = Getattr(function,"parms");
+ Parm *pp = 0;
+ while (p) {
+ SwigType *t = Getattr(p,"type");
+ if (Strcmp(t,"v(...)") == 0) {
+ if (pp) {
+ ParmList *cv = Copy(varargs);
+ set_nextSibling(pp,cv);
+ Delete(cv);
+ } else {
+ ParmList *cv = Copy(varargs);
+ Setattr(function,"parms", cv);
+ Delete(cv);
+ }
+ break;
+ }
+ pp = p;
+ p = nextSibling(p);
+ }
+ }
+
+ /* Do not add in functions if kwargs is being used or if user wants old default argument wrapping
+ (one wrapped method per function irrespective of number of default arguments) */
+ if (compact_default_args
+ || is_cfunction(function)
+ || GetFlag(function,"feature:compactdefaultargs")
+ || (GetFlag(function,"feature:kwargs") && kwargs_supported)) {
+ ParmList *p = Getattr(function,"parms");
+ if (p)
+ Setattr(p,"compactdefargs", "1"); /* mark parameters for special handling */
+ function = 0; /* don't add in extra methods */
+ }
+ }
+
+ while (function) {
+ ParmList *parms = Getattr(function,"parms");
+ if (ParmList_has_defaultargs(parms)) {
+
+ /* Create a parameter list for the new function by copying all
+ but the last (defaulted) parameter */
+ ParmList* newparms = CopyParmListMax(parms,ParmList_len(parms)-1);
+
+ /* Create new function and add to symbol table */
+ {
+ SwigType *ntype = Copy(nodeType(function));
+ char *cntype = Char(ntype);
+ Node *new_function = new_node(ntype);
+ SwigType *decl = Copy(Getattr(function,"decl"));
+ int constqualifier = SwigType_isconst(decl);
+ String *ccode = Copy(Getattr(function,"code"));
+ String *cstorage = Copy(Getattr(function,"storage"));
+ String *cvalue = Copy(Getattr(function,"value"));
+ SwigType *ctype = Copy(Getattr(function,"type"));
+ String *cthrow = Copy(Getattr(function,"throw"));
+
+ Delete(SwigType_pop_function(decl)); /* remove the old parameter list from decl */
+ SwigType_add_function(decl,newparms);
+ if (constqualifier)
+ SwigType_add_qualifier(decl,"const");
+
+ Setattr(new_function,"name", Getattr(function,"name"));
+ Setattr(new_function,"code", ccode);
+ Setattr(new_function,"decl", decl);
+ Setattr(new_function,"parms", newparms);
+ Setattr(new_function,"storage", cstorage);
+ Setattr(new_function,"value", cvalue);
+ Setattr(new_function,"type", ctype);
+ Setattr(new_function,"throw", cthrow);
+
+ Delete(ccode);
+ Delete(cstorage);
+ Delete(cvalue);
+ Delete(ctype);
+ Delete(cthrow);
+ Delete(decl);
+
+ {
+ Node *throws = Getattr(function,"throws");
+ ParmList *pl = CopyParmList(throws);
+ if (throws) Setattr(new_function,"throws",pl);
+ Delete(pl);
+ }
+
+ /* copy specific attributes for global (or in a namespace) template functions - these are not templated class methods */
+ if (strcmp(cntype,"template") == 0) {
+ Node *templatetype = Getattr(function,"templatetype");
+ Node *symtypename = Getattr(function,"sym:typename");
+ Parm *templateparms = Getattr(function,"templateparms");
+ if (templatetype) {
+ Node *tmp = Copy(templatetype);
+ Setattr(new_function,"templatetype",tmp);
+ Delete(tmp);
+ }
+ if (symtypename) {
+ Node *tmp = Copy(symtypename);
+ Setattr(new_function,"sym:typename",tmp);
+ Delete(tmp);
+ }
+ if (templateparms) {
+ Parm *tmp = CopyParmList(templateparms);
+ Setattr(new_function,"templateparms",tmp);
+ Delete(tmp);
+ }
+ } else if (strcmp(cntype,"constructor") == 0) {
+ /* only copied for constructors as this is not a user defined feature - it is hard coded in the parser */
+ if (GetFlag(function,"feature:new")) SetFlag(new_function,"feature:new");
+ }
+
+ add_symbols(new_function);
+ /* mark added functions as ones with overloaded parameters and point to the parsed method */
+ Setattr(new_function,"defaultargs", n);
+
+ /* Point to the new function, extending the linked list */
+ set_nextSibling(function, new_function);
+ Delete(new_function);
+ function = new_function;
+
+ Delete(ntype);
+ }
+ } else {
+ function = 0;
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * mark_nodes_as_extend()
+ *
+ * Used by the %extend to mark subtypes with "feature:extend".
+ * template instances declared within %extend are skipped
+ * ----------------------------------------------------------------------------- */
+
+static void mark_nodes_as_extend(Node *n) {
+ for (; n; n = nextSibling(n)) {
+ if (Getattr(n, "template") && Strcmp(nodeType(n), "class") == 0)
+ continue;
+ /* Fix me: extend is not a feature. Replace with isextendmember? */
+ Setattr(n, "feature:extend", "1");
+ mark_nodes_as_extend(firstChild(n));
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * add_qualifier_to_declarator()
+ *
+ * Normally the qualifier is pushed on to the front of the type.
+ * Adding a qualifier to a pointer to member function is a special case.
+ * For example : typedef double (Cls::*pmf)(void) const;
+ * The qualifier is : q(const).
+ * The declarator is : m(Cls).f(void).
+ * We need : m(Cls).q(const).f(void).
+ * ----------------------------------------------------------------------------- */
+
+static String *add_qualifier_to_declarator(SwigType *type, SwigType *qualifier) {
+ int is_pointer_to_member_function = 0;
+ String *decl = Copy(type);
+ String *poppedtype = NewString("");
+ assert(qualifier);
+
+ while (decl) {
+ if (SwigType_ismemberpointer(decl)) {
+ String *memberptr = SwigType_pop(decl);
+ if (SwigType_isfunction(decl)) {
+ is_pointer_to_member_function = 1;
+ SwigType_push(decl, qualifier);
+ SwigType_push(decl, memberptr);
+ Insert(decl, 0, poppedtype);
+ Delete(memberptr);
+ break;
+ } else {
+ Append(poppedtype, memberptr);
+ }
+ Delete(memberptr);
+ } else {
+ String *popped = SwigType_pop(decl);
+ if (!popped)
+ break;
+ Append(poppedtype, popped);
+ Delete(popped);
+ }
+ }
+
+ if (!is_pointer_to_member_function) {
+ Delete(decl);
+ decl = Copy(type);
+ SwigType_push(decl, qualifier);
+ }
+
+ Delete(poppedtype);
+ return decl;
+}
+
+%}
+
+%union {
+ const char *id;
+ List *bases;
+ struct Define {
+ String *val;
+ String *rawval;
+ int type;
+ String *qualifier;
+ String *refqualifier;
+ String *bitfield;
+ Parm *throws;
+ String *throwf;
+ String *nexcept;
+ String *final;
+ } dtype;
+ struct {
+ const char *type;
+ String *filename;
+ int line;
+ } loc;
+ struct {
+ char *id;
+ SwigType *type;
+ String *defarg;
+ ParmList *parms;
+ short have_parms;
+ ParmList *throws;
+ String *throwf;
+ String *nexcept;
+ String *final;
+ } decl;
+ Parm *tparms;
+ struct {
+ String *method;
+ Hash *kwargs;
+ } tmap;
+ struct {
+ String *type;
+ String *us;
+ } ptype;
+ SwigType *type;
+ String *str;
+ Parm *p;
+ ParmList *pl;
+ int intvalue;
+ Node *node;
+};
+
+// Define special token END for end of input.
+%token END 0
+
+%token <id> ID
+%token <str> HBLOCK
+%token <id> POUND
+%token <id> STRING WSTRING
+%token <loc> INCLUDE IMPORT INSERT
+%token <str> CHARCONST WCHARCONST
+%token <dtype> NUM_INT NUM_FLOAT NUM_UNSIGNED NUM_LONG NUM_ULONG NUM_LONGLONG NUM_ULONGLONG NUM_BOOL
+%token <intvalue> TYPEDEF
+%token <type> TYPE_INT TYPE_UNSIGNED TYPE_SHORT TYPE_LONG TYPE_FLOAT TYPE_DOUBLE TYPE_CHAR TYPE_WCHAR TYPE_VOID TYPE_SIGNED TYPE_BOOL TYPE_COMPLEX TYPE_TYPEDEF TYPE_RAW TYPE_NON_ISO_INT8 TYPE_NON_ISO_INT16 TYPE_NON_ISO_INT32 TYPE_NON_ISO_INT64
+%token LPAREN RPAREN COMMA SEMI EXTERN INIT LBRACE RBRACE PERIOD ELLIPSIS
+%token CONST_QUAL VOLATILE REGISTER STRUCT UNION EQUAL SIZEOF MODULE LBRACKET RBRACKET
+%token BEGINFILE ENDOFFILE
+%token ILLEGAL CONSTANT
+%token NAME RENAME NAMEWARN EXTEND PRAGMA FEATURE VARARGS
+%token ENUM
+%token CLASS TYPENAME PRIVATE PUBLIC PROTECTED COLON STATIC VIRTUAL FRIEND THROW CATCH EXPLICIT
+%token STATIC_ASSERT CONSTEXPR THREAD_LOCAL DECLTYPE AUTO NOEXCEPT /* C++11 keywords */
+%token OVERRIDE FINAL /* C++11 identifiers with special meaning */
+%token USING
+%token <node> NAMESPACE
+%token NATIVE INLINE
+%token TYPEMAP EXCEPT ECHO APPLY CLEAR SWIGTEMPLATE FRAGMENT
+%token WARN
+%token LESSTHAN GREATERTHAN DELETE_KW DEFAULT
+%token LESSTHANOREQUALTO GREATERTHANOREQUALTO EQUALTO NOTEQUALTO LESSEQUALGREATER
+%token ARROW
+%token QUESTIONMARK
+%token TYPES PARMS
+%token NONID DSTAR DCNOT
+%token <intvalue> TEMPLATE
+%token <str> OPERATOR
+%token <str> CONVERSIONOPERATOR
+%token PARSETYPE PARSEPARM PARSEPARMS
+
+%token <str> DOXYGENSTRING
+%token <str> DOXYGENPOSTSTRING
+
+%left CAST
+%left QUESTIONMARK
+%left LOR
+%left LAND
+%left OR
+%left XOR
+%left AND
+%left EQUALTO NOTEQUALTO
+%left GREATERTHAN LESSTHAN GREATERTHANOREQUALTO LESSTHANOREQUALTO
+%left LESSEQUALGREATER
+%left LSHIFT RSHIFT
+%left PLUS MINUS
+%left STAR SLASH MODULO
+%left UMINUS NOT LNOT
+%left DCOLON
+
+%type <node> program interface declaration swig_directive ;
+
+/* SWIG directives */
+%type <node> extend_directive apply_directive clear_directive constant_directive ;
+%type <node> echo_directive except_directive fragment_directive include_directive inline_directive ;
+%type <node> insert_directive module_directive name_directive native_directive ;
+%type <node> pragma_directive rename_directive feature_directive varargs_directive typemap_directive ;
+%type <node> types_directive template_directive warn_directive ;
+
+/* C declarations */
+%type <node> c_declaration c_decl c_decl_tail c_enum_key c_enum_inherit c_enum_decl c_enum_forward_decl c_constructor_decl;
+%type <node> enumlist enumlist_item edecl_with_dox edecl;
+
+/* C++ declarations */
+%type <node> cpp_declaration cpp_class_decl cpp_forward_class_decl cpp_template_decl cpp_alternate_rettype;
+%type <node> cpp_members cpp_member cpp_member_no_dox;
+%type <node> cpp_constructor_decl cpp_destructor_decl cpp_protection_decl cpp_conversion_operator cpp_static_assert;
+%type <node> cpp_swig_directive cpp_template_possible cpp_opt_declarators ;
+%type <node> cpp_using_decl cpp_namespace_decl cpp_catch_decl cpp_lambda_decl;
+%type <node> kwargs options;
+
+/* Misc */
+%type <id> identifier;
+%type <dtype> initializer cpp_const exception_specification cv_ref_qualifier qualifiers_exception_specification;
+%type <id> storage_class extern_string;
+%type <pl> parms ptail rawparms varargs_parms ;
+%type <pl> templateparameters templateparameterstail;
+%type <p> parm_no_dox parm valparm rawvalparms valparms valptail ;
+%type <p> typemap_parm tm_list tm_tail ;
+%type <p> templateparameter ;
+%type <id> templcpptype cpptype classkey classkeyopt access_specifier;
+%type <node> base_specifier;
+%type <str> variadic;
+%type <type> type rawtype type_right anon_bitfield_type decltype ;
+%type <bases> base_list inherit raw_inherit;
+%type <dtype> definetype def_args etype default_delete deleted_definition explicit_default;
+%type <dtype> expr exprnum exprsimple exprcompound valexpr exprmem callparms callptail;
+%type <id> ename ;
+%type <id> less_valparms_greater;
+%type <str> type_qualifier;
+%type <str> ref_qualifier;
+%type <id> type_qualifier_raw;
+%type <id> idstring idstringopt;
+%type <id> pragma_lang;
+%type <str> pragma_arg;
+%type <loc> includetype;
+%type <type> pointer primitive_type;
+%type <decl> declarator direct_declarator notso_direct_declarator parameter_declarator plain_declarator;
+%type <decl> abstract_declarator direct_abstract_declarator ctor_end;
+%type <tmap> typemap_type;
+%type <str> idcolon idcolontail idcolonnt idcolontailnt idtemplate idtemplatetemplate stringbrace stringbracesemi;
+%type <str> string stringnum wstring;
+%type <tparms> template_parms;
+%type <dtype> cpp_end cpp_vend;
+%type <intvalue> rename_namewarn;
+%type <ptype> type_specifier primitive_type_list ;
+%type <node> fname stringtype;
+%type <node> featattr;
+%type <node> lambda_introducer lambda_body lambda_template;
+%type <pl> lambda_tail;
+%type <str> virt_specifier_seq virt_specifier_seq_opt;
+%type <str> class_virt_specifier_opt;
+
+%%
+
+/* ======================================================================
+ * High-level Interface file
+ *
+ * An interface is just a sequence of declarations which may be SWIG directives
+ * or normal C declarations.
+ * ====================================================================== */
+
+program : interface {
+ if (!classes) classes = NewHash();
+ Setattr($1,"classes",classes);
+ Setattr($1,"name",ModuleName);
+
+ if ((!module_node) && ModuleName) {
+ module_node = new_node("module");
+ Setattr(module_node,"name",ModuleName);
+ }
+ Setattr($1,"module",module_node);
+ top = $1;
+ }
+ | PARSETYPE parm SEMI {
+ top = Copy(Getattr($2,"type"));
+ Delete($2);
+ }
+ | PARSETYPE error {
+ top = 0;
+ }
+ | PARSEPARM parm SEMI {
+ top = $2;
+ }
+ | PARSEPARM error {
+ top = 0;
+ }
+ | PARSEPARMS LPAREN parms RPAREN SEMI {
+ top = $3;
+ }
+ | PARSEPARMS error SEMI {
+ top = 0;
+ }
+ ;
+
+interface : interface declaration {
+ /* add declaration to end of linked list (the declaration isn't always a single declaration, sometimes it is a linked list itself) */
+ if (currentDeclComment != NULL) {
+ set_comment($2, currentDeclComment);
+ currentDeclComment = NULL;
+ }
+ appendChild($1,$2);
+ $$ = $1;
+ }
+ | interface DOXYGENSTRING {
+ currentDeclComment = $2;
+ $$ = $1;
+ }
+ | interface DOXYGENPOSTSTRING {
+ Node *node = lastChild($1);
+ if (node) {
+ set_comment(node, $2);
+ }
+ $$ = $1;
+ }
+ | empty {
+ $$ = new_node("top");
+ }
+ ;
+
+declaration : swig_directive { $$ = $1; }
+ | c_declaration { $$ = $1; }
+ | cpp_declaration { $$ = $1; }
+ | SEMI { $$ = 0; }
+ | error {
+ $$ = 0;
+ if (cparse_unknown_directive) {
+ Swig_error(cparse_file, cparse_line, "Unknown directive '%s'.\n", cparse_unknown_directive);
+ } else {
+ Swig_error(cparse_file, cparse_line, "Syntax error in input(1).\n");
+ }
+ Exit(EXIT_FAILURE);
+ }
+/* Out of class constructor/destructor declarations */
+ | c_constructor_decl {
+ if ($$) {
+ add_symbols($$);
+ }
+ $$ = $1;
+ }
+
+/* Out of class conversion operator. For example:
+ inline A::operator char *() const { ... }.
+
+ This is nearly impossible to parse normally. We just let the
+ first part generate a syntax error and then resynchronize on the
+ CONVERSIONOPERATOR token---discarding the rest of the definition. Ugh.
+
+ */
+
+ | error CONVERSIONOPERATOR {
+ $$ = 0;
+ skip_decl();
+ }
+ ;
+
+/* ======================================================================
+ * SWIG DIRECTIVES
+ * ====================================================================== */
+
+swig_directive : extend_directive { $$ = $1; }
+ | apply_directive { $$ = $1; }
+ | clear_directive { $$ = $1; }
+ | constant_directive { $$ = $1; }
+ | echo_directive { $$ = $1; }
+ | except_directive { $$ = $1; }
+ | fragment_directive { $$ = $1; }
+ | include_directive { $$ = $1; }
+ | inline_directive { $$ = $1; }
+ | insert_directive { $$ = $1; }
+ | module_directive { $$ = $1; }
+ | name_directive { $$ = $1; }
+ | native_directive { $$ = $1; }
+ | pragma_directive { $$ = $1; }
+ | rename_directive { $$ = $1; }
+ | feature_directive { $$ = $1; }
+ | varargs_directive { $$ = $1; }
+ | typemap_directive { $$ = $1; }
+ | types_directive { $$ = $1; }
+ | template_directive { $$ = $1; }
+ | warn_directive { $$ = $1; }
+ ;
+
+/* ------------------------------------------------------------
+ %extend classname { ... }
+ ------------------------------------------------------------ */
+
+extend_directive : EXTEND options classkeyopt idcolon LBRACE {
+ Node *cls;
+ String *clsname;
+ extendmode = 1;
+ cplus_mode = CPLUS_PUBLIC;
+ if (!classes) classes = NewHash();
+ if (!classes_typedefs) classes_typedefs = NewHash();
+ clsname = make_class_name($4);
+ cls = Getattr(classes,clsname);
+ if (!cls) {
+ cls = Getattr(classes_typedefs, clsname);
+ if (!cls) {
+ /* No previous definition. Create a new scope */
+ Node *am = Getattr(Swig_extend_hash(),clsname);
+ if (!am) {
+ Swig_symbol_newscope();
+ Swig_symbol_setscopename($4);
+ prev_symtab = 0;
+ } else {
+ prev_symtab = Swig_symbol_setscope(Getattr(am,"symtab"));
+ }
+ current_class = 0;
+ } else {
+ /* Previous typedef class definition. Use its symbol table.
+ Deprecated, just the real name should be used.
+ Note that %extend before the class typedef never worked, only %extend after the class typedef. */
+ prev_symtab = Swig_symbol_setscope(Getattr(cls, "symtab"));
+ current_class = cls;
+ SWIG_WARN_NODE_BEGIN(cls);
+ Swig_warning(WARN_PARSE_EXTEND_NAME, cparse_file, cparse_line, "Deprecated %%extend name used - the %s name '%s' should be used instead of the typedef name '%s'.\n", Getattr(cls, "kind"), SwigType_namestr(Getattr(cls, "name")), $4);
+ SWIG_WARN_NODE_END(cls);
+ }
+ } else {
+ /* Previous class definition. Use its symbol table */
+ prev_symtab = Swig_symbol_setscope(Getattr(cls,"symtab"));
+ current_class = cls;
+ }
+ Classprefix = NewString($4);
+ Namespaceprefix= Swig_symbol_qualifiedscopename(0);
+ Delete(clsname);
+ } cpp_members RBRACE {
+ String *clsname;
+ extendmode = 0;
+ $$ = new_node("extend");
+ Setattr($$,"symtab",Swig_symbol_popscope());
+ if (prev_symtab) {
+ Swig_symbol_setscope(prev_symtab);
+ }
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ clsname = make_class_name($4);
+ Setattr($$,"name",clsname);
+
+ mark_nodes_as_extend($7);
+ if (current_class) {
+ /* We add the extension to the previously defined class */
+ appendChild($$, $7);
+ appendChild(current_class,$$);
+ } else {
+ /* We store the extensions in the extensions hash */
+ Node *am = Getattr(Swig_extend_hash(),clsname);
+ if (am) {
+ /* Append the members to the previous extend methods */
+ appendChild(am, $7);
+ } else {
+ appendChild($$, $7);
+ Setattr(Swig_extend_hash(),clsname,$$);
+ }
+ }
+ current_class = 0;
+ Delete(Classprefix);
+ Delete(clsname);
+ Classprefix = 0;
+ prev_symtab = 0;
+ $$ = 0;
+
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %apply
+ ------------------------------------------------------------ */
+
+apply_directive : APPLY typemap_parm LBRACE tm_list RBRACE {
+ $$ = new_node("apply");
+ Setattr($$,"pattern",Getattr($2,"pattern"));
+ appendChild($$,$4);
+ };
+
+/* ------------------------------------------------------------
+ %clear
+ ------------------------------------------------------------ */
+
+clear_directive : CLEAR tm_list SEMI {
+ $$ = new_node("clear");
+ appendChild($$,$2);
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %constant name = value;
+ %constant type name = value;
+
+ Note: Source/Preprocessor/cpp.c injects `%constant X = Y;` for
+ each `#define X Y` so that's handled here too.
+ ------------------------------------------------------------ */
+
+constant_directive : CONSTANT identifier EQUAL definetype SEMI {
+ if (($4.type != T_ERROR) && ($4.type != T_SYMBOL)) {
+ SwigType *type = NewSwigType($4.type);
+ $$ = new_node("constant");
+ Setattr($$,"name",$2);
+ Setattr($$,"type",type);
+ Setattr($$,"value",$4.val);
+ if ($4.rawval) Setattr($$,"rawval", $4.rawval);
+ Setattr($$,"storage","%constant");
+ SetFlag($$,"feature:immutable");
+ add_symbols($$);
+ Delete(type);
+ } else {
+ if ($4.type == T_ERROR) {
+ Swig_warning(WARN_PARSE_UNSUPPORTED_VALUE,cparse_file,cparse_line,"Unsupported constant value (ignored)\n");
+ }
+ $$ = 0;
+ }
+
+ }
+ | CONSTANT type declarator def_args SEMI {
+ if (($4.type != T_ERROR) && ($4.type != T_SYMBOL)) {
+ SwigType_push($2,$3.type);
+ /* Sneaky callback function trick */
+ if (SwigType_isfunction($2)) {
+ SwigType_add_pointer($2);
+ }
+ $$ = new_node("constant");
+ Setattr($$,"name",$3.id);
+ Setattr($$,"type",$2);
+ Setattr($$,"value",$4.val);
+ if ($4.rawval) Setattr($$,"rawval", $4.rawval);
+ Setattr($$,"storage","%constant");
+ SetFlag($$,"feature:immutable");
+ add_symbols($$);
+ } else {
+ if ($4.type == T_ERROR) {
+ Swig_warning(WARN_PARSE_UNSUPPORTED_VALUE,cparse_file,cparse_line, "Unsupported constant value\n");
+ }
+ $$ = 0;
+ }
+ }
+ /* Member function pointers with qualifiers. eg.
+ %constant short (Funcs::*pmf)(bool) const = &Funcs::F; */
+ | CONSTANT type direct_declarator LPAREN parms RPAREN cv_ref_qualifier def_args SEMI {
+ if (($8.type != T_ERROR) && ($8.type != T_SYMBOL)) {
+ SwigType_add_function($2, $5);
+ SwigType_push($2, $7.qualifier);
+ SwigType_push($2, $3.type);
+ /* Sneaky callback function trick */
+ if (SwigType_isfunction($2)) {
+ SwigType_add_pointer($2);
+ }
+ $$ = new_node("constant");
+ Setattr($$, "name", $3.id);
+ Setattr($$, "type", $2);
+ Setattr($$, "value", $8.val);
+ if ($8.rawval) Setattr($$, "rawval", $8.rawval);
+ Setattr($$, "storage", "%constant");
+ SetFlag($$, "feature:immutable");
+ add_symbols($$);
+ } else {
+ if ($8.type == T_ERROR) {
+ Swig_warning(WARN_PARSE_UNSUPPORTED_VALUE,cparse_file,cparse_line, "Unsupported constant value\n");
+ }
+ $$ = 0;
+ }
+ }
+ | CONSTANT error SEMI {
+ Swig_warning(WARN_PARSE_BAD_VALUE,cparse_file,cparse_line,"Bad constant value (ignored).\n");
+ $$ = 0;
+ }
+ | CONSTANT error END {
+ Swig_error(cparse_file,cparse_line,"Missing semicolon (';') after %%constant.\n");
+ Exit(EXIT_FAILURE);
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %echo "text"
+ %echo %{ ... %}
+ ------------------------------------------------------------ */
+
+echo_directive : ECHO HBLOCK {
+ char temp[64];
+ Replace($2,"$file",cparse_file, DOH_REPLACE_ANY);
+ sprintf(temp,"%d", cparse_line);
+ Replace($2,"$line",temp,DOH_REPLACE_ANY);
+ Printf(stderr,"%s\n", $2);
+ Delete($2);
+ $$ = 0;
+ }
+ | ECHO string {
+ char temp[64];
+ String *s = $2;
+ Replace(s,"$file",cparse_file, DOH_REPLACE_ANY);
+ sprintf(temp,"%d", cparse_line);
+ Replace(s,"$line",temp,DOH_REPLACE_ANY);
+ Printf(stderr,"%s\n", s);
+ Delete(s);
+ $$ = 0;
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %except(lang) { ... }
+ %except { ... }
+ %except(lang);
+ %except;
+ ------------------------------------------------------------ */
+
+except_directive : EXCEPT LPAREN identifier RPAREN LBRACE {
+ skip_balanced('{','}');
+ $$ = 0;
+ Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n");
+ }
+
+ | EXCEPT LBRACE {
+ skip_balanced('{','}');
+ $$ = 0;
+ Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n");
+ }
+
+ | EXCEPT LPAREN identifier RPAREN SEMI {
+ $$ = 0;
+ Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n");
+ }
+
+ | EXCEPT SEMI {
+ $$ = 0;
+ Swig_warning(WARN_DEPRECATED_EXCEPT,cparse_file, cparse_line, "%%except is deprecated. Use %%exception instead.\n");
+ }
+ ;
+
+/* fragment keyword arguments */
+stringtype : string LBRACE parm RBRACE {
+ $$ = NewHash();
+ Setattr($$,"value",$1);
+ Setattr($$,"type",Getattr($3,"type"));
+ }
+ ;
+
+fname : string {
+ $$ = NewHash();
+ Setattr($$,"value",$1);
+ }
+ | stringtype {
+ $$ = $1;
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %fragment(name, section) %{ ... %}
+ %fragment("name" {type}, "section") %{ ... %}
+ %fragment("name", "section", fragment="fragment1", fragment="fragment2") %{ ... %}
+ Also as above but using { ... }
+ %fragment("name");
+ ------------------------------------------------------------ */
+
+fragment_directive: FRAGMENT LPAREN fname COMMA kwargs RPAREN HBLOCK {
+ Hash *p = $5;
+ $$ = new_node("fragment");
+ Setattr($$,"value",Getattr($3,"value"));
+ Setattr($$,"type",Getattr($3,"type"));
+ Setattr($$,"section",Getattr(p,"name"));
+ Setattr($$,"kwargs",nextSibling(p));
+ Setattr($$,"code",$7);
+ }
+ | FRAGMENT LPAREN fname COMMA kwargs RPAREN LBRACE {
+ Hash *p = $5;
+ String *code;
+ skip_balanced('{','}');
+ $$ = new_node("fragment");
+ Setattr($$,"value",Getattr($3,"value"));
+ Setattr($$,"type",Getattr($3,"type"));
+ Setattr($$,"section",Getattr(p,"name"));
+ Setattr($$,"kwargs",nextSibling(p));
+ Delitem(scanner_ccode,0);
+ Delitem(scanner_ccode,DOH_END);
+ code = Copy(scanner_ccode);
+ Setattr($$,"code",code);
+ Delete(code);
+ }
+ | FRAGMENT LPAREN fname RPAREN SEMI {
+ $$ = new_node("fragment");
+ Setattr($$,"value",Getattr($3,"value"));
+ Setattr($$,"type",Getattr($3,"type"));
+ Setattr($$,"emitonly","1");
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %includefile(option1="xyz", ...) "filename" [ declarations ]
+ %importfile(option1="xyz", ...) "filename" [ declarations ]
+ ------------------------------------------------------------ */
+
+include_directive: includetype options string BEGINFILE {
+ $1.filename = Copy(cparse_file);
+ $1.line = cparse_line;
+ scanner_set_location($3,1);
+ if ($2) {
+ String *maininput = Getattr($2, "maininput");
+ if (maininput)
+ scanner_set_main_input_file(NewString(maininput));
+ }
+ } interface ENDOFFILE {
+ String *mname = 0;
+ $$ = $6;
+ scanner_set_location($1.filename,$1.line+1);
+ if (strcmp($1.type,"include") == 0) set_nodeType($$,"include");
+ if (strcmp($1.type,"import") == 0) {
+ mname = $2 ? Getattr($2,"module") : 0;
+ set_nodeType($$,"import");
+ if (import_mode) --import_mode;
+ }
+
+ Setattr($$,"name",$3);
+ /* Search for the module (if any) */
+ {
+ Node *n = firstChild($$);
+ while (n) {
+ if (Strcmp(nodeType(n),"module") == 0) {
+ if (mname) {
+ Setattr(n,"name", mname);
+ mname = 0;
+ }
+ Setattr($$,"module",Getattr(n,"name"));
+ break;
+ }
+ n = nextSibling(n);
+ }
+ if (mname) {
+ /* There is no module node in the import
+ node, ie, you imported a .h file
+ directly. We are forced then to create
+ a new import node with a module node.
+ */
+ Node *nint = new_node("import");
+ Node *mnode = new_node("module");
+ Setattr(mnode,"name", mname);
+ Setattr(mnode,"options",$2);
+ appendChild(nint,mnode);
+ Delete(mnode);
+ appendChild(nint,firstChild($$));
+ $$ = nint;
+ Setattr($$,"module",mname);
+ }
+ }
+ Setattr($$,"options",$2);
+ }
+ ;
+
+includetype : INCLUDE { $$.type = "include"; }
+ | IMPORT { $$.type = "import"; ++import_mode;}
+ ;
+
+/* ------------------------------------------------------------
+ %inline %{ ... %}
+ ------------------------------------------------------------ */
+
+inline_directive : INLINE HBLOCK {
+ String *cpps;
+ if (Namespaceprefix) {
+ Swig_error(cparse_file, cparse_start_line, "%%inline directive inside a namespace is disallowed.\n");
+ $$ = 0;
+ } else {
+ $$ = new_node("insert");
+ Setattr($$,"code",$2);
+ /* Need to run through the preprocessor */
+ Seek($2,0,SEEK_SET);
+ Setline($2,cparse_start_line);
+ Setfile($2,cparse_file);
+ cpps = Preprocessor_parse($2);
+ start_inline(Char(cpps), cparse_start_line);
+ Delete($2);
+ Delete(cpps);
+ }
+
+ }
+ | INLINE LBRACE {
+ String *cpps;
+ int start_line = cparse_line;
+ skip_balanced('{','}');
+ if (Namespaceprefix) {
+ Swig_error(cparse_file, cparse_start_line, "%%inline directive inside a namespace is disallowed.\n");
+
+ $$ = 0;
+ } else {
+ String *code;
+ $$ = new_node("insert");
+ Delitem(scanner_ccode,0);
+ Delitem(scanner_ccode,DOH_END);
+ code = Copy(scanner_ccode);
+ Setattr($$,"code", code);
+ Delete(code);
+ cpps=Copy(scanner_ccode);
+ start_inline(Char(cpps), start_line);
+ Delete(cpps);
+ }
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %{ ... %}
+ %insert(section) "filename"
+ %insert("section") "filename"
+ %insert(section) %{ ... %}
+ %insert("section") %{ ... %}
+ ------------------------------------------------------------ */
+
+insert_directive : HBLOCK {
+ $$ = new_node("insert");
+ Setattr($$,"code",$1);
+ }
+ | INSERT LPAREN idstring RPAREN string {
+ String *code = NewStringEmpty();
+ $$ = new_node("insert");
+ Setattr($$,"section",$3);
+ Setattr($$,"code",code);
+ if (Swig_insert_file($5,code) < 0) {
+ Swig_error(cparse_file, cparse_line, "Couldn't find '%s'.\n", $5);
+ $$ = 0;
+ }
+ }
+ | INSERT LPAREN idstring RPAREN HBLOCK {
+ $$ = new_node("insert");
+ Setattr($$,"section",$3);
+ Setattr($$,"code",$5);
+ }
+ | INSERT LPAREN idstring RPAREN LBRACE {
+ String *code;
+ skip_balanced('{','}');
+ $$ = new_node("insert");
+ Setattr($$,"section",$3);
+ Delitem(scanner_ccode,0);
+ Delitem(scanner_ccode,DOH_END);
+ code = Copy(scanner_ccode);
+ Setattr($$,"code", code);
+ Delete(code);
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %module modname
+ %module "modname"
+ ------------------------------------------------------------ */
+
+module_directive: MODULE options idstring {
+ $$ = new_node("module");
+ if ($2) {
+ Setattr($$,"options",$2);
+ if (Getattr($2,"directors")) {
+ Wrapper_director_mode_set(1);
+ if (!cparse_cplusplus) {
+ Swig_error(cparse_file, cparse_line, "Directors are not supported for C code and require the -c++ option\n");
+ }
+ }
+ if (Getattr($2,"dirprot")) {
+ Wrapper_director_protected_mode_set(1);
+ }
+ if (Getattr($2,"allprotected")) {
+ Wrapper_all_protected_mode_set(1);
+ }
+ if (Getattr($2,"templatereduce")) {
+ template_reduce = 1;
+ }
+ if (Getattr($2,"notemplatereduce")) {
+ template_reduce = 0;
+ }
+ }
+ if (!ModuleName) ModuleName = NewString($3);
+ if (!import_mode) {
+ /* first module included, we apply global
+ ModuleName, which can be modify by -module */
+ String *mname = Copy(ModuleName);
+ Setattr($$,"name",mname);
+ Delete(mname);
+ } else {
+ /* import mode, we just pass the idstring */
+ Setattr($$,"name",$3);
+ }
+ if (!module_node) module_node = $$;
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %name(newname) declaration
+ %name("newname") declaration
+ ------------------------------------------------------------ */
+
+name_directive : NAME LPAREN idstring RPAREN {
+ Swig_warning(WARN_DEPRECATED_NAME,cparse_file,cparse_line, "%%name is deprecated. Use %%rename instead.\n");
+ Delete(yyrename);
+ yyrename = NewString($3);
+ $$ = 0;
+ }
+ | NAME LPAREN RPAREN {
+ Swig_warning(WARN_DEPRECATED_NAME,cparse_file,cparse_line, "%%name is deprecated. Use %%rename instead.\n");
+ $$ = 0;
+ Swig_error(cparse_file,cparse_line,"Missing argument to %%name directive.\n");
+ }
+ ;
+
+
+/* ------------------------------------------------------------
+ %native(scriptname) name;
+ %native(scriptname) type name (parms);
+ ------------------------------------------------------------ */
+
+native_directive : NATIVE LPAREN identifier RPAREN storage_class identifier SEMI {
+ $$ = new_node("native");
+ Setattr($$,"name",$3);
+ Setattr($$,"wrap:name",$6);
+ add_symbols($$);
+ }
+ | NATIVE LPAREN identifier RPAREN storage_class type declarator SEMI {
+ if (!SwigType_isfunction($7.type)) {
+ Swig_error(cparse_file,cparse_line,"%%native declaration '%s' is not a function.\n", $7.id);
+ $$ = 0;
+ } else {
+ Delete(SwigType_pop_function($7.type));
+ /* Need check for function here */
+ SwigType_push($6,$7.type);
+ $$ = new_node("native");
+ Setattr($$,"name",$3);
+ Setattr($$,"wrap:name",$7.id);
+ Setattr($$,"type",$6);
+ Setattr($$,"parms",$7.parms);
+ Setattr($$,"decl",$7.type);
+ }
+ add_symbols($$);
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %pragma(lang) name=value
+ %pragma(lang) name
+ %pragma name = value
+ %pragma name
+ ------------------------------------------------------------ */
+
+pragma_directive : PRAGMA pragma_lang identifier EQUAL pragma_arg {
+ $$ = new_node("pragma");
+ Setattr($$,"lang",$2);
+ Setattr($$,"name",$3);
+ Setattr($$,"value",$5);
+ }
+ | PRAGMA pragma_lang identifier {
+ $$ = new_node("pragma");
+ Setattr($$,"lang",$2);
+ Setattr($$,"name",$3);
+ }
+ ;
+
+pragma_arg : string { $$ = $1; }
+ | HBLOCK { $$ = $1; }
+ ;
+
+pragma_lang : LPAREN identifier RPAREN { $$ = $2; }
+ | empty { $$ = (char *) "swig"; }
+ ;
+
+/* ------------------------------------------------------------
+ %rename(newname) identifier;
+ ------------------------------------------------------------ */
+
+rename_directive : rename_namewarn declarator idstring SEMI {
+ SwigType *t = $2.type;
+ Hash *kws = NewHash();
+ String *fixname;
+ fixname = feature_identifier_fix($2.id);
+ Setattr(kws,"name",$3);
+ if (!Len(t)) t = 0;
+ /* Special declarator check */
+ if (t) {
+ if (SwigType_isfunction(t)) {
+ SwigType *decl = SwigType_pop_function(t);
+ if (SwigType_ispointer(t)) {
+ String *nname = NewStringf("*%s",fixname);
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix, nname,decl,kws,$2.parms);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,nname,decl,kws);
+ }
+ Delete(nname);
+ } else {
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix,(fixname),decl,kws,$2.parms);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,(fixname),decl,kws);
+ }
+ }
+ Delete(decl);
+ } else if (SwigType_ispointer(t)) {
+ String *nname = NewStringf("*%s",fixname);
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix,(nname),0,kws,$2.parms);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,(nname),0,kws);
+ }
+ Delete(nname);
+ }
+ } else {
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix,(fixname),0,kws,$2.parms);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,(fixname),0,kws);
+ }
+ }
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ | rename_namewarn LPAREN kwargs RPAREN declarator cpp_const SEMI {
+ String *fixname;
+ Hash *kws = $3;
+ SwigType *t = $5.type;
+ fixname = feature_identifier_fix($5.id);
+ if (!Len(t)) t = 0;
+ /* Special declarator check */
+ if (t) {
+ if ($6.qualifier) SwigType_push(t,$6.qualifier);
+ if (SwigType_isfunction(t)) {
+ SwigType *decl = SwigType_pop_function(t);
+ if (SwigType_ispointer(t)) {
+ String *nname = NewStringf("*%s",fixname);
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix, nname,decl,kws,$5.parms);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,nname,decl,kws);
+ }
+ Delete(nname);
+ } else {
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix,(fixname),decl,kws,$5.parms);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,(fixname),decl,kws);
+ }
+ }
+ Delete(decl);
+ } else if (SwigType_ispointer(t)) {
+ String *nname = NewStringf("*%s",fixname);
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix,(nname),0,kws,$5.parms);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,(nname),0,kws);
+ }
+ Delete(nname);
+ }
+ } else {
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix,(fixname),0,kws,$5.parms);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,(fixname),0,kws);
+ }
+ }
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ | rename_namewarn LPAREN kwargs RPAREN string SEMI {
+ if ($1) {
+ Swig_name_rename_add(Namespaceprefix,$5,0,$3,0);
+ } else {
+ Swig_name_namewarn_add(Namespaceprefix,$5,0,$3);
+ }
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ ;
+
+rename_namewarn : RENAME {
+ $$ = 1;
+ }
+ | NAMEWARN {
+ $$ = 0;
+ };
+
+
+/* ------------------------------------------------------------
+ Feature targeting a symbol name (non-global feature):
+
+ %feature(featurename) name "val";
+ %feature(featurename, val) name;
+
+ where "val" could instead be the other bracket types, that is,
+ { val } or %{ val %} or indeed omitted whereupon it defaults to "1".
+ Or, the global feature which does not target a symbol name:
+
+ %feature(featurename) "val";
+ %feature(featurename, val);
+
+ An empty val (empty string) clears the feature.
+ Any number of feature attributes can optionally be added, for example
+ a non-global feature with 2 attributes:
+
+ %feature(featurename, attrib1="attribval1", attrib2="attribval2") name "val";
+ %feature(featurename, val, attrib1="attribval1", attrib2="attribval2") name;
+ ------------------------------------------------------------ */
+
+ /* Non-global feature */
+feature_directive : FEATURE LPAREN idstring RPAREN declarator cpp_const stringbracesemi {
+ String *val = $7 ? NewString($7) : NewString("1");
+ new_feature($3, val, 0, $5.id, $5.type, $5.parms, $6.qualifier);
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ | FEATURE LPAREN idstring COMMA stringnum RPAREN declarator cpp_const SEMI {
+ String *val = Len($5) ? $5 : 0;
+ new_feature($3, val, 0, $7.id, $7.type, $7.parms, $8.qualifier);
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ | FEATURE LPAREN idstring featattr RPAREN declarator cpp_const stringbracesemi {
+ String *val = $8 ? NewString($8) : NewString("1");
+ new_feature($3, val, $4, $6.id, $6.type, $6.parms, $7.qualifier);
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ | FEATURE LPAREN idstring COMMA stringnum featattr RPAREN declarator cpp_const SEMI {
+ String *val = Len($5) ? $5 : 0;
+ new_feature($3, val, $6, $8.id, $8.type, $8.parms, $9.qualifier);
+ $$ = 0;
+ scanner_clear_rename();
+ }
+
+ /* Global feature */
+ | FEATURE LPAREN idstring RPAREN stringbracesemi {
+ String *val = $5 ? NewString($5) : NewString("1");
+ new_feature($3, val, 0, 0, 0, 0, 0);
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ | FEATURE LPAREN idstring COMMA stringnum RPAREN SEMI {
+ String *val = Len($5) ? $5 : 0;
+ new_feature($3, val, 0, 0, 0, 0, 0);
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ | FEATURE LPAREN idstring featattr RPAREN stringbracesemi {
+ String *val = $6 ? NewString($6) : NewString("1");
+ new_feature($3, val, $4, 0, 0, 0, 0);
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ | FEATURE LPAREN idstring COMMA stringnum featattr RPAREN SEMI {
+ String *val = Len($5) ? $5 : 0;
+ new_feature($3, val, $6, 0, 0, 0, 0);
+ $$ = 0;
+ scanner_clear_rename();
+ }
+ ;
+
+stringbracesemi : stringbrace { $$ = $1; }
+ | SEMI { $$ = 0; }
+ | PARMS LPAREN parms RPAREN SEMI { $$ = $3; }
+ ;
+
+featattr : COMMA idstring EQUAL stringnum {
+ $$ = NewHash();
+ Setattr($$,"name",$2);
+ Setattr($$,"value",$4);
+ }
+ | COMMA idstring EQUAL stringnum featattr {
+ $$ = NewHash();
+ Setattr($$,"name",$2);
+ Setattr($$,"value",$4);
+ set_nextSibling($$,$5);
+ }
+ ;
+
+/* %varargs() directive. */
+
+varargs_directive : VARARGS LPAREN varargs_parms RPAREN declarator cpp_const SEMI {
+ Parm *val;
+ String *name;
+ SwigType *t;
+ if (Namespaceprefix) name = NewStringf("%s::%s", Namespaceprefix, $5.id);
+ else name = NewString($5.id);
+ val = $3;
+ if ($5.parms) {
+ Setmeta(val,"parms",$5.parms);
+ }
+ t = $5.type;
+ if (!Len(t)) t = 0;
+ if (t) {
+ if ($6.qualifier) SwigType_push(t,$6.qualifier);
+ if (SwigType_isfunction(t)) {
+ SwigType *decl = SwigType_pop_function(t);
+ if (SwigType_ispointer(t)) {
+ String *nname = NewStringf("*%s",name);
+ Swig_feature_set(Swig_cparse_features(), nname, decl, "feature:varargs", val, 0);
+ Delete(nname);
+ } else {
+ Swig_feature_set(Swig_cparse_features(), name, decl, "feature:varargs", val, 0);
+ }
+ Delete(decl);
+ } else if (SwigType_ispointer(t)) {
+ String *nname = NewStringf("*%s",name);
+ Swig_feature_set(Swig_cparse_features(),nname,0,"feature:varargs",val, 0);
+ Delete(nname);
+ }
+ } else {
+ Swig_feature_set(Swig_cparse_features(),name,0,"feature:varargs",val, 0);
+ }
+ Delete(name);
+ $$ = 0;
+ };
+
+varargs_parms : parms { $$ = $1; }
+ | NUM_INT COMMA parm {
+ int i;
+ int n;
+ Parm *p;
+ n = atoi(Char($1.val));
+ if (n <= 0) {
+ Swig_error(cparse_file, cparse_line,"Argument count in %%varargs must be positive.\n");
+ $$ = 0;
+ } else {
+ String *name = Getattr($3, "name");
+ $$ = Copy($3);
+ if (name)
+ Setattr($$, "name", NewStringf("%s%d", name, n));
+ for (i = 1; i < n; i++) {
+ p = Copy($3);
+ name = Getattr(p, "name");
+ if (name)
+ Setattr(p, "name", NewStringf("%s%d", name, n-i));
+ set_nextSibling(p,$$);
+ Delete($$);
+ $$ = p;
+ }
+ }
+ }
+ ;
+
+
+/* ------------------------------------------------------------
+ %typemap(method) type { ... }
+ %typemap(method) type "..."
+ %typemap(method) type; - typemap deletion
+ %typemap(method) type1,type2,... = type; - typemap copy
+ %typemap type1,type2,... = type; - typemap copy
+ ------------------------------------------------------------ */
+
+typemap_directive : TYPEMAP LPAREN typemap_type RPAREN tm_list stringbrace {
+ $$ = 0;
+ if ($3.method) {
+ String *code = 0;
+ $$ = new_node("typemap");
+ Setattr($$,"method",$3.method);
+ if ($3.kwargs) {
+ ParmList *kw = $3.kwargs;
+ code = remove_block(kw, $6);
+ Setattr($$,"kwargs", $3.kwargs);
+ }
+ code = code ? code : NewString($6);
+ Setattr($$,"code", code);
+ Delete(code);
+ appendChild($$,$5);
+ }
+ }
+ | TYPEMAP LPAREN typemap_type RPAREN tm_list SEMI {
+ $$ = 0;
+ if ($3.method) {
+ $$ = new_node("typemap");
+ Setattr($$,"method",$3.method);
+ appendChild($$,$5);
+ }
+ }
+ | TYPEMAP LPAREN typemap_type RPAREN tm_list EQUAL typemap_parm SEMI {
+ $$ = 0;
+ if ($3.method) {
+ $$ = new_node("typemapcopy");
+ Setattr($$,"method",$3.method);
+ Setattr($$,"pattern", Getattr($7,"pattern"));
+ appendChild($$,$5);
+ }
+ }
+ ;
+
+/* typemap method type (lang,method) or (method) */
+
+typemap_type : kwargs {
+ String *name = Getattr($1, "name");
+ Hash *p = nextSibling($1);
+ $$.method = name;
+ $$.kwargs = p;
+ if (Getattr($1, "value")) {
+ Swig_error(cparse_file, cparse_line,
+ "%%typemap method shouldn't have a value specified.\n");
+ }
+ while (p) {
+ if (!Getattr(p, "value")) {
+ Swig_error(cparse_file, cparse_line,
+ "%%typemap attribute '%s' is missing its value. If this is specifying the target language, that's no longer supported: use #ifdef SWIG<LANG> instead.\n",
+ Getattr(p, "name"));
+ /* Set to empty value to avoid segfaults later. */
+ Setattr(p, "value", NewStringEmpty());
+ }
+ p = nextSibling(p);
+ }
+ }
+ ;
+
+tm_list : typemap_parm tm_tail {
+ $$ = $1;
+ set_nextSibling($$,$2);
+ }
+ ;
+
+tm_tail : COMMA typemap_parm tm_tail {
+ $$ = $2;
+ set_nextSibling($$,$3);
+ }
+ | empty { $$ = 0;}
+ ;
+
+typemap_parm : type plain_declarator {
+ Parm *parm;
+ SwigType_push($1,$2.type);
+ $$ = new_node("typemapitem");
+ parm = NewParmWithoutFileLineInfo($1,$2.id);
+ Setattr($$,"pattern",parm);
+ Setattr($$,"parms", $2.parms);
+ Delete(parm);
+ /* $$ = NewParmWithoutFileLineInfo($1,$2.id);
+ Setattr($$,"parms",$2.parms); */
+ }
+ | LPAREN parms RPAREN {
+ $$ = new_node("typemapitem");
+ Setattr($$,"pattern",$2);
+ /* Setattr($$,"multitype",$2); */
+ }
+ | LPAREN parms RPAREN LPAREN parms RPAREN {
+ $$ = new_node("typemapitem");
+ Setattr($$,"pattern", $2);
+ /* Setattr($$,"multitype",$2); */
+ Setattr($$,"parms",$5);
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %types(parmlist);
+ %types(parmlist) %{ ... %}
+ ------------------------------------------------------------ */
+
+types_directive : TYPES LPAREN parms RPAREN stringbracesemi {
+ $$ = new_node("types");
+ Setattr($$,"parms",$3);
+ if ($5)
+ Setattr($$,"convcode",NewString($5));
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %template(name) tname<args>;
+ ------------------------------------------------------------ */
+
+template_directive: SWIGTEMPLATE LPAREN idstringopt RPAREN idcolonnt LESSTHAN valparms GREATERTHAN SEMI {
+ Parm *p, *tp;
+ Node *n;
+ Node *outer_class = currentOuterClass;
+ Symtab *tscope = 0;
+ int specialized = 0;
+ int variadic = 0;
+
+ $$ = 0;
+
+ tscope = Swig_symbol_current(); /* Get the current scope */
+
+ /* If the class name is qualified, we need to create or lookup namespace entries */
+ $5 = resolve_create_node_scope($5, 0);
+
+ if (nscope_inner && Strcmp(nodeType(nscope_inner), "class") == 0) {
+ outer_class = nscope_inner;
+ }
+
+ /*
+ We use the new namespace entry 'nscope' only to
+ emit the template node. The template parameters are
+ resolved in the current 'tscope'.
+
+ This is closer to the C++ (typedef) behavior.
+ */
+ n = Swig_cparse_template_locate($5,$7,tscope);
+
+ /* Patch the argument types to respect namespaces */
+ p = $7;
+ while (p) {
+ SwigType *value = Getattr(p,"value");
+ if (!value) {
+ SwigType *ty = Getattr(p,"type");
+ if (ty) {
+ SwigType *rty = 0;
+ int reduce = template_reduce;
+ if (reduce || !SwigType_ispointer(ty)) {
+ rty = Swig_symbol_typedef_reduce(ty,tscope);
+ if (!reduce) reduce = SwigType_ispointer(rty);
+ }
+ ty = reduce ? Swig_symbol_type_qualify(rty,tscope) : Swig_symbol_type_qualify(ty,tscope);
+ Setattr(p,"type",ty);
+ Delete(ty);
+ Delete(rty);
+ }
+ } else {
+ value = Swig_symbol_type_qualify(value,tscope);
+ Setattr(p,"value",value);
+ Delete(value);
+ }
+
+ p = nextSibling(p);
+ }
+
+ /* Look for the template */
+ {
+ Node *nn = n;
+ Node *linklistend = 0;
+ Node *linkliststart = 0;
+ while (nn) {
+ Node *templnode = 0;
+ if (Strcmp(nodeType(nn),"template") == 0) {
+ int nnisclass = (Strcmp(Getattr(nn,"templatetype"),"class") == 0); /* if not a templated class it is a templated function */
+ Parm *tparms = Getattr(nn,"templateparms");
+ if (!tparms) {
+ specialized = 1;
+ } else if (Getattr(tparms,"variadic") && strncmp(Char(Getattr(tparms,"variadic")), "1", 1)==0) {
+ variadic = 1;
+ }
+ if (nnisclass && !variadic && !specialized && (ParmList_len($7) > ParmList_len(tparms))) {
+ Swig_error(cparse_file, cparse_line, "Too many template parameters. Maximum of %d.\n", ParmList_len(tparms));
+ } else if (nnisclass && !specialized && ((ParmList_len($7) < (ParmList_numrequired(tparms) - (variadic?1:0))))) { /* Variadic parameter is optional */
+ Swig_error(cparse_file, cparse_line, "Not enough template parameters specified. %d required.\n", (ParmList_numrequired(tparms)-(variadic?1:0)) );
+ } else if (!nnisclass && ((ParmList_len($7) != ParmList_len(tparms)))) {
+ /* must be an overloaded templated method - ignore it as it is overloaded with a different number of template parameters */
+ nn = Getattr(nn,"sym:nextSibling"); /* repeat for overloaded templated functions */
+ continue;
+ } else {
+ String *tname = Copy($5);
+ int def_supplied = 0;
+ /* Expand the template */
+ Node *templ = Swig_symbol_clookup($5,0);
+ Parm *targs = templ ? Getattr(templ,"templateparms") : 0;
+
+ ParmList *temparms;
+ if (specialized) temparms = CopyParmList($7);
+ else temparms = CopyParmList(tparms);
+
+ /* Create typedef's and arguments */
+ p = $7;
+ tp = temparms;
+ if (!p && ParmList_len(p) != ParmList_len(temparms)) {
+ /* we have no template parameters supplied in %template for a template that has default args*/
+ p = tp;
+ def_supplied = 1;
+ }
+
+ while (p) {
+ String *value = Getattr(p,"value");
+ if (def_supplied) {
+ Setattr(p,"default","1");
+ }
+ if (value) {
+ Setattr(tp,"value",value);
+ } else {
+ SwigType *ty = Getattr(p,"type");
+ if (ty) {
+ Setattr(tp,"type",ty);
+ }
+ Delattr(tp,"value");
+ }
+ /* fix default arg values */
+ if (targs) {
+ Parm *pi = temparms;
+ Parm *ti = targs;
+ String *tv = Getattr(tp,"value");
+ if (!tv) tv = Getattr(tp,"type");
+ while(pi != tp && ti && pi) {
+ String *name = Getattr(ti,"name");
+ String *value = Getattr(pi,"value");
+ if (!value) value = Getattr(pi,"type");
+ Replaceid(tv, name, value);
+ pi = nextSibling(pi);
+ ti = nextSibling(ti);
+ }
+ }
+ p = nextSibling(p);
+ tp = nextSibling(tp);
+ if (!p && tp) {
+ p = tp;
+ def_supplied = 1;
+ } else if (p && !tp) { /* Variadic template - tp < p */
+ SWIG_WARN_NODE_BEGIN(nn);
+ Swig_warning(WARN_CPP11_VARIADIC_TEMPLATE,cparse_file, cparse_line,"Only the first variadic template argument is currently supported.\n");
+ SWIG_WARN_NODE_END(nn);
+ break;
+ }
+ }
+
+ templnode = copy_node(nn);
+ update_nested_classes(templnode); /* update classes nested within template */
+ /* We need to set the node name based on name used to instantiate */
+ Setattr(templnode,"name",tname);
+ Delete(tname);
+ if (!specialized) {
+ Delattr(templnode,"sym:typename");
+ } else {
+ Setattr(templnode,"sym:typename","1");
+ }
+ /* for now, nested %template is allowed only in the same scope as the template declaration */
+ if ($3 && !(nnisclass && ((outer_class && (outer_class != Getattr(nn, "nested:outer")))
+ ||(extendmode && current_class && (current_class != Getattr(nn, "nested:outer")))))) {
+ /*
+ Comment this out for 1.3.28. We need to
+ re-enable it later but first we need to
+ move %ignore from using %rename to use
+ %feature(ignore).
+
+ String *symname = Swig_name_make(templnode,0,$3,0,0);
+ */
+ String *symname = NewString($3);
+ Swig_cparse_template_expand(templnode,symname,temparms,tscope);
+ Setattr(templnode,"sym:name",symname);
+ } else {
+ static int cnt = 0;
+ String *nname = NewStringf("__dummy_%d__", cnt++);
+ Swig_cparse_template_expand(templnode,nname,temparms,tscope);
+ Setattr(templnode,"sym:name",nname);
+ SetFlag(templnode,"hidden");
+ Delete(nname);
+ Setattr(templnode,"feature:onlychildren", "typemap,typemapitem,typemapcopy,typedef,types,fragment,apply");
+ if ($3) {
+ Swig_warning(WARN_PARSE_NESTED_TEMPLATE, cparse_file, cparse_line, "Named nested template instantiations not supported. Processing as if no name was given to %%template().\n");
+ }
+ }
+ Delattr(templnode,"templatetype");
+ Setattr(templnode,"template",nn);
+ Setfile(templnode,cparse_file);
+ Setline(templnode,cparse_line);
+ Delete(temparms);
+ if (outer_class && nnisclass) {
+ SetFlag(templnode, "nested");
+ Setattr(templnode, "nested:outer", outer_class);
+ }
+ add_symbols_copy(templnode);
+
+ if (Strcmp(nodeType(templnode),"class") == 0) {
+
+ /* Identify pure abstract methods */
+ Setattr(templnode,"abstracts", pure_abstracts(firstChild(templnode)));
+
+ /* Set up inheritance in symbol table */
+ {
+ Symtab *csyms;
+ List *baselist = Getattr(templnode,"baselist");
+ csyms = Swig_symbol_current();
+ Swig_symbol_setscope(Getattr(templnode,"symtab"));
+ if (baselist) {
+ List *bases = Swig_make_inherit_list(Getattr(templnode,"name"),baselist, Namespaceprefix);
+ if (bases) {
+ Iterator s;
+ for (s = First(bases); s.item; s = Next(s)) {
+ Symtab *st = Getattr(s.item,"symtab");
+ if (st) {
+ Setfile(st,Getfile(s.item));
+ Setline(st,Getline(s.item));
+ Swig_symbol_inherit(st);
+ }
+ }
+ Delete(bases);
+ }
+ }
+ Swig_symbol_setscope(csyms);
+ }
+
+ /* Merge in %extend methods for this class.
+ This only merges methods within %extend for a template specialized class such as
+ template<typename T> class K {}; %extend K<int> { ... }
+ The copy_node() call above has already added in the generic %extend methods such as
+ template<typename T> class K {}; %extend K { ... } */
+
+ /* !!! This may be broken. We may have to add the
+ %extend methods at the beginning of the class */
+ {
+ String *stmp = 0;
+ String *clsname;
+ Node *am;
+ if (Namespaceprefix) {
+ clsname = stmp = NewStringf("%s::%s", Namespaceprefix, Getattr(templnode,"name"));
+ } else {
+ clsname = Getattr(templnode,"name");
+ }
+ am = Getattr(Swig_extend_hash(),clsname);
+ if (am) {
+ Symtab *st = Swig_symbol_current();
+ Swig_symbol_setscope(Getattr(templnode,"symtab"));
+ /* Printf(stdout,"%s: %s %p %p\n", Getattr(templnode,"name"), clsname, Swig_symbol_current(), Getattr(templnode,"symtab")); */
+ Swig_extend_merge(templnode,am);
+ Swig_symbol_setscope(st);
+ Swig_extend_append_previous(templnode,am);
+ Delattr(Swig_extend_hash(),clsname);
+ }
+ if (stmp) Delete(stmp);
+ }
+
+ /* Add to classes hash */
+ if (!classes)
+ classes = NewHash();
+
+ if (Namespaceprefix) {
+ String *temp = NewStringf("%s::%s", Namespaceprefix, Getattr(templnode,"name"));
+ Setattr(classes,temp,templnode);
+ Delete(temp);
+ } else {
+ String *qs = Swig_symbol_qualifiedscopename(templnode);
+ Setattr(classes, qs,templnode);
+ Delete(qs);
+ }
+ }
+ }
+
+ /* all the overloaded templated functions are added into a linked list */
+ if (!linkliststart)
+ linkliststart = templnode;
+ if (nscope_inner) {
+ /* non-global namespace */
+ if (templnode) {
+ appendChild(nscope_inner,templnode);
+ Delete(templnode);
+ if (nscope) $$ = nscope;
+ }
+ } else {
+ /* global namespace */
+ if (!linklistend) {
+ $$ = templnode;
+ } else {
+ set_nextSibling(linklistend,templnode);
+ Delete(templnode);
+ }
+ linklistend = templnode;
+ }
+ }
+ nn = Getattr(nn,"sym:nextSibling"); /* repeat for overloaded templated functions. If a templated class there will never be a sibling. */
+ }
+ update_defaultargs(linkliststart);
+ update_abstracts(linkliststart);
+ }
+ Swig_symbol_setscope(tscope);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ }
+ ;
+
+/* ------------------------------------------------------------
+ %warn "text"
+ %warn(no)
+ ------------------------------------------------------------ */
+
+warn_directive : WARN string {
+ Swig_warning(0,cparse_file, cparse_line,"%s\n", $2);
+ $$ = 0;
+ }
+ ;
+
+/* ======================================================================
+ * C Parsing
+ * ====================================================================== */
+
+c_declaration : c_decl {
+ $$ = $1;
+ if ($$) {
+ add_symbols($$);
+ default_arguments($$);
+ }
+ }
+ | c_enum_decl { $$ = $1; }
+ | c_enum_forward_decl { $$ = $1; }
+
+/* An extern C type declaration, disable cparse_cplusplus if needed. */
+
+ | EXTERN string LBRACE {
+ if (Strcmp($2,"C") == 0) {
+ cparse_externc = 1;
+ }
+ } interface RBRACE {
+ cparse_externc = 0;
+ if (Strcmp($2,"C") == 0) {
+ Node *n = firstChild($5);
+ $$ = new_node("extern");
+ Setattr($$,"name",$2);
+ appendChild($$,n);
+ while (n) {
+ String *s = Getattr(n, "storage");
+ if (s) {
+ if (Strstr(s, "thread_local")) {
+ Insert(s,0,"externc ");
+ } else if (!Equal(s, "typedef")) {
+ Setattr(n,"storage","externc");
+ }
+ } else {
+ Setattr(n,"storage","externc");
+ }
+ n = nextSibling(n);
+ }
+ } else {
+ if (!Equal($2,"C++")) {
+ Swig_warning(WARN_PARSE_UNDEFINED_EXTERN,cparse_file, cparse_line,"Unrecognized extern type \"%s\".\n", $2);
+ }
+ $$ = new_node("extern");
+ Setattr($$,"name",$2);
+ appendChild($$,firstChild($5));
+ }
+ }
+ | cpp_lambda_decl {
+ $$ = $1;
+ SWIG_WARN_NODE_BEGIN($$);
+ Swig_warning(WARN_CPP11_LAMBDA, cparse_file, cparse_line, "Lambda expressions and closures are not fully supported yet.\n");
+ SWIG_WARN_NODE_END($$);
+ }
+ | USING idcolon EQUAL type plain_declarator SEMI {
+ /* Convert using statement to a typedef statement */
+ $$ = new_node("cdecl");
+ Setattr($$,"type",$4);
+ Setattr($$,"storage","typedef");
+ Setattr($$,"name",$2);
+ Setattr($$,"decl",$5.type);
+ SetFlag($$,"typealias");
+ add_symbols($$);
+ }
+ | TEMPLATE LESSTHAN template_parms GREATERTHAN USING idcolon EQUAL type plain_declarator SEMI {
+ /* Convert alias template to a "template" typedef statement */
+ $$ = new_node("template");
+ Setattr($$,"type",$8);
+ Setattr($$,"storage","typedef");
+ Setattr($$,"name",$6);
+ Setattr($$,"decl",$9.type);
+ Setattr($$,"templateparms",$3);
+ Setattr($$,"templatetype","cdecl");
+ SetFlag($$,"aliastemplate");
+ add_symbols($$);
+ }
+ | cpp_static_assert {
+ $$ = $1;
+ }
+ ;
+
+/* ------------------------------------------------------------
+ A C global declaration of some kind (may be variable, function, typedef, etc.)
+ ------------------------------------------------------------ */
+
+c_decl : storage_class type declarator cpp_const initializer c_decl_tail {
+ String *decl = $3.type;
+ $$ = new_node("cdecl");
+ if ($4.qualifier)
+ decl = add_qualifier_to_declarator($3.type, $4.qualifier);
+ Setattr($$,"refqualifier",$4.refqualifier);
+ Setattr($$,"type",$2);
+ Setattr($$,"storage",$1);
+ Setattr($$,"name",$3.id);
+ Setattr($$,"decl",decl);
+ Setattr($$,"parms",$3.parms);
+ Setattr($$,"value",$5.val);
+ Setattr($$,"throws",$4.throws);
+ Setattr($$,"throw",$4.throwf);
+ Setattr($$,"noexcept",$4.nexcept);
+ Setattr($$,"final",$4.final);
+ if ($5.val && $5.type) {
+ /* store initializer type as it might be different to the declared type */
+ SwigType *valuetype = NewSwigType($5.type);
+ if (Len(valuetype) > 0)
+ Setattr($$,"valuetype",valuetype);
+ else
+ Delete(valuetype);
+ }
+ if (!$6) {
+ if (Len(scanner_ccode)) {
+ String *code = Copy(scanner_ccode);
+ Setattr($$,"code",code);
+ Delete(code);
+ }
+ } else {
+ Node *n = $6;
+ /* Inherit attributes */
+ while (n) {
+ String *type = Copy($2);
+ Setattr(n,"type",type);
+ Setattr(n,"storage",$1);
+ n = nextSibling(n);
+ Delete(type);
+ }
+ }
+ if ($5.bitfield) {
+ Setattr($$,"bitfield", $5.bitfield);
+ }
+
+ if ($3.id) {
+ /* Look for "::" declarations (ignored) */
+ if (Strstr($3.id, "::")) {
+ /* This is a special case. If the scope name of the declaration exactly
+ matches that of the declaration, then we will allow it. Otherwise, delete. */
+ String *p = Swig_scopename_prefix($3.id);
+ if (p) {
+ if ((Namespaceprefix && Strcmp(p, Namespaceprefix) == 0) ||
+ (Classprefix && Strcmp(p, Classprefix) == 0)) {
+ String *lstr = Swig_scopename_last($3.id);
+ Setattr($$, "name", lstr);
+ Delete(lstr);
+ set_nextSibling($$, $6);
+ } else {
+ Delete($$);
+ $$ = $6;
+ }
+ Delete(p);
+ } else {
+ Delete($$);
+ $$ = $6;
+ }
+ } else {
+ set_nextSibling($$, $6);
+ }
+ } else {
+ Swig_error(cparse_file, cparse_line, "Missing symbol name for global declaration\n");
+ $$ = 0;
+ }
+
+ if ($4.qualifier && $1 && Strstr($1, "static"))
+ Swig_error(cparse_file, cparse_line, "Static function %s cannot have a qualifier.\n", Swig_name_decl($$));
+ }
+ /* Alternate function syntax introduced in C++11:
+ auto funcName(int x, int y) -> int; */
+ | storage_class AUTO declarator cpp_const ARROW cpp_alternate_rettype virt_specifier_seq_opt initializer c_decl_tail {
+ $$ = new_node("cdecl");
+ if ($4.qualifier) SwigType_push($3.type, $4.qualifier);
+ Setattr($$,"refqualifier",$4.refqualifier);
+ Setattr($$,"type",$6);
+ Setattr($$,"storage",$1);
+ Setattr($$,"name",$3.id);
+ Setattr($$,"decl",$3.type);
+ Setattr($$,"parms",$3.parms);
+ Setattr($$,"value",$4.val);
+ Setattr($$,"throws",$4.throws);
+ Setattr($$,"throw",$4.throwf);
+ Setattr($$,"noexcept",$4.nexcept);
+ Setattr($$,"final",$4.final);
+ if (!$9) {
+ if (Len(scanner_ccode)) {
+ String *code = Copy(scanner_ccode);
+ Setattr($$,"code",code);
+ Delete(code);
+ }
+ } else {
+ Node *n = $9;
+ while (n) {
+ String *type = Copy($6);
+ Setattr(n,"type",type);
+ Setattr(n,"storage",$1);
+ n = nextSibling(n);
+ Delete(type);
+ }
+ }
+ if ($4.bitfield) {
+ Setattr($$,"bitfield", $4.bitfield);
+ }
+
+ if (Strstr($3.id,"::")) {
+ String *p = Swig_scopename_prefix($3.id);
+ if (p) {
+ if ((Namespaceprefix && Strcmp(p, Namespaceprefix) == 0) ||
+ (Classprefix && Strcmp(p, Classprefix) == 0)) {
+ String *lstr = Swig_scopename_last($3.id);
+ Setattr($$,"name",lstr);
+ Delete(lstr);
+ set_nextSibling($$, $9);
+ } else {
+ Delete($$);
+ $$ = $9;
+ }
+ Delete(p);
+ } else {
+ Delete($$);
+ $$ = $9;
+ }
+ } else {
+ set_nextSibling($$, $9);
+ }
+
+ if ($4.qualifier && $1 && Strstr($1, "static"))
+ Swig_error(cparse_file, cparse_line, "Static function %s cannot have a qualifier.\n", Swig_name_decl($$));
+ }
+ ;
+
+/* Allow lists of variables and functions to be built up */
+
+c_decl_tail : SEMI {
+ $$ = 0;
+ Clear(scanner_ccode);
+ }
+ | COMMA declarator cpp_const initializer c_decl_tail {
+ $$ = new_node("cdecl");
+ if ($3.qualifier) SwigType_push($2.type,$3.qualifier);
+ Setattr($$,"refqualifier",$3.refqualifier);
+ Setattr($$,"name",$2.id);
+ Setattr($$,"decl",$2.type);
+ Setattr($$,"parms",$2.parms);
+ Setattr($$,"value",$4.val);
+ Setattr($$,"throws",$3.throws);
+ Setattr($$,"throw",$3.throwf);
+ Setattr($$,"noexcept",$3.nexcept);
+ Setattr($$,"final",$3.final);
+ if ($4.bitfield) {
+ Setattr($$,"bitfield", $4.bitfield);
+ }
+ if (!$5) {
+ if (Len(scanner_ccode)) {
+ String *code = Copy(scanner_ccode);
+ Setattr($$,"code",code);
+ Delete(code);
+ }
+ } else {
+ set_nextSibling($$, $5);
+ }
+ }
+ | LBRACE {
+ skip_balanced('{','}');
+ $$ = 0;
+ }
+ | error {
+ $$ = 0;
+ if (yychar == RPAREN) {
+ Swig_error(cparse_file, cparse_line, "Unexpected closing parenthesis (')').\n");
+ } else {
+ Swig_error(cparse_file, cparse_line, "Syntax error - possibly a missing semicolon (';').\n");
+ }
+ Exit(EXIT_FAILURE);
+ }
+ ;
+
+initializer : def_args {
+ $$ = $1;
+ }
+ ;
+
+cpp_alternate_rettype : primitive_type { $$ = $1; }
+ | TYPE_BOOL { $$ = $1; }
+ | TYPE_VOID { $$ = $1; }
+/*
+ | TYPE_TYPEDEF template_decl { $$ = NewStringf("%s%s",$1,$2); }
+*/
+ | TYPE_RAW { $$ = $1; }
+ | idcolon { $$ = $1; }
+ | idcolon AND {
+ $$ = $1;
+ SwigType_add_reference($$);
+ }
+ | decltype { $$ = $1; }
+ ;
+
+/* ------------------------------------------------------------
+ Lambda functions and expressions, such as:
+ auto myFunc = [] { return something; };
+ auto myFunc = [](int x, int y) { return x+y; };
+ auto myFunc = [](int x, int y) -> int { return x+y; };
+ auto myFunc = [](int x, int y) throw() -> int { return x+y; };
+ auto six = [](int x, int y) { return x+y; }(4, 2);
+ ------------------------------------------------------------ */
+cpp_lambda_decl : storage_class AUTO idcolon EQUAL lambda_introducer lambda_template LPAREN parms RPAREN cpp_const lambda_body lambda_tail {
+ $$ = new_node("lambda");
+ Setattr($$,"name",$3);
+ add_symbols($$);
+ }
+ | storage_class AUTO idcolon EQUAL lambda_introducer lambda_template LPAREN parms RPAREN cpp_const ARROW type lambda_body lambda_tail {
+ $$ = new_node("lambda");
+ Setattr($$,"name",$3);
+ add_symbols($$);
+ }
+ | storage_class AUTO idcolon EQUAL lambda_introducer lambda_template lambda_body lambda_tail {
+ $$ = new_node("lambda");
+ Setattr($$,"name",$3);
+ add_symbols($$);
+ }
+ ;
+
+lambda_introducer : LBRACKET {
+ skip_balanced('[',']');
+ $$ = 0;
+ }
+ ;
+
+lambda_template : LESSTHAN {
+ skip_balanced('<','>');
+ $$ = 0;
+ }
+ | empty { $$ = 0; }
+ ;
+
+lambda_body : LBRACE {
+ skip_balanced('{','}');
+ $$ = 0;
+ }
+
+lambda_tail : SEMI {
+ $$ = 0;
+ }
+ | LPAREN {
+ skip_balanced('(',')');
+ } SEMI {
+ $$ = 0;
+ }
+ ;
+
+/* ------------------------------------------------------------
+ enum
+ or
+ enum class
+ ------------------------------------------------------------ */
+
+c_enum_key : ENUM {
+ $$ = (char *)"enum";
+ }
+ | ENUM CLASS {
+ $$ = (char *)"enum class";
+ }
+ | ENUM STRUCT {
+ $$ = (char *)"enum struct";
+ }
+ ;
+
+/* ------------------------------------------------------------
+ base enum type (eg. unsigned short)
+ ------------------------------------------------------------ */
+
+c_enum_inherit : COLON type_right {
+ $$ = $2;
+ }
+ | empty { $$ = 0; }
+ ;
+/* ------------------------------------------------------------
+ enum [class] Name;
+ enum [class] Name [: base_type];
+ ------------------------------------------------------------ */
+
+c_enum_forward_decl : storage_class c_enum_key ename c_enum_inherit SEMI {
+ SwigType *ty = 0;
+ int scopedenum = $3 && !Equal($2, "enum");
+ $$ = new_node("enumforward");
+ ty = NewStringf("enum %s", $3);
+ Setattr($$,"enumkey",$2);
+ if (scopedenum)
+ SetFlag($$, "scopedenum");
+ Setattr($$,"name",$3);
+ Setattr($$,"inherit",$4);
+ Setattr($$,"type",ty);
+ Setattr($$,"sym:weak", "1");
+ add_symbols($$);
+ }
+ ;
+
+/* ------------------------------------------------------------
+ enum [class] Name [: base_type] { ... };
+ or
+ enum [class] Name [: base_type] { ... } MyEnum [= ...];
+ * ------------------------------------------------------------ */
+
+c_enum_decl : storage_class c_enum_key ename c_enum_inherit LBRACE enumlist RBRACE SEMI {
+ SwigType *ty = 0;
+ int scopedenum = $3 && !Equal($2, "enum");
+ $$ = new_node("enum");
+ ty = NewStringf("enum %s", $3);
+ Setattr($$,"enumkey",$2);
+ if (scopedenum)
+ SetFlag($$, "scopedenum");
+ Setattr($$,"name",$3);
+ Setattr($$,"inherit",$4);
+ Setattr($$,"type",ty);
+ appendChild($$,$6);
+ add_symbols($$); /* Add to tag space */
+
+ if (scopedenum) {
+ Swig_symbol_newscope();
+ Swig_symbol_setscopename($3);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ }
+
+ add_symbols($6); /* Add enum values to appropriate enum or enum class scope */
+
+ if (scopedenum) {
+ Setattr($$,"symtab", Swig_symbol_popscope());
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ }
+ }
+ | storage_class c_enum_key ename c_enum_inherit LBRACE enumlist RBRACE declarator cpp_const initializer c_decl_tail {
+ Node *n;
+ SwigType *ty = 0;
+ String *unnamed = 0;
+ int unnamedinstance = 0;
+ int scopedenum = $3 && !Equal($2, "enum");
+
+ $$ = new_node("enum");
+ Setattr($$,"enumkey",$2);
+ if (scopedenum)
+ SetFlag($$, "scopedenum");
+ Setattr($$,"inherit",$4);
+ if ($3) {
+ Setattr($$,"name",$3);
+ ty = NewStringf("enum %s", $3);
+ } else if ($8.id) {
+ unnamed = make_unnamed();
+ ty = NewStringf("enum %s", unnamed);
+ Setattr($$,"unnamed",unnamed);
+ /* name is not set for unnamed enum instances, e.g. enum { foo } Instance; */
+ if ($1 && Cmp($1,"typedef") == 0) {
+ Setattr($$,"name",$8.id);
+ } else {
+ unnamedinstance = 1;
+ }
+ Setattr($$,"storage",$1);
+ }
+ if ($8.id && Cmp($1,"typedef") == 0) {
+ Setattr($$,"tdname",$8.id);
+ Setattr($$,"allows_typedef","1");
+ }
+ appendChild($$,$6);
+ n = new_node("cdecl");
+ Setattr(n,"type",ty);
+ Setattr(n,"name",$8.id);
+ Setattr(n,"storage",$1);
+ Setattr(n,"decl",$8.type);
+ Setattr(n,"parms",$8.parms);
+ Setattr(n,"unnamed",unnamed);
+
+ if (unnamedinstance) {
+ SwigType *cty = NewString("enum ");
+ Setattr($$,"type",cty);
+ SetFlag($$,"unnamedinstance");
+ SetFlag(n,"unnamedinstance");
+ Delete(cty);
+ }
+ if ($11) {
+ Node *p = $11;
+ set_nextSibling(n,p);
+ while (p) {
+ SwigType *cty = Copy(ty);
+ Setattr(p,"type",cty);
+ Setattr(p,"unnamed",unnamed);
+ Setattr(p,"storage",$1);
+ Delete(cty);
+ p = nextSibling(p);
+ }
+ } else {
+ if (Len(scanner_ccode)) {
+ String *code = Copy(scanner_ccode);
+ Setattr(n,"code",code);
+ Delete(code);
+ }
+ }
+
+ /* Ensure that typedef enum ABC {foo} XYZ; uses XYZ for sym:name, like structs.
+ * Note that class_rename/yyrename are bit of a mess so used this simple approach to change the name. */
+ if ($8.id && $3 && Cmp($1,"typedef") == 0) {
+ String *name = NewString($8.id);
+ Setattr($$, "parser:makename", name);
+ Delete(name);
+ }
+
+ add_symbols($$); /* Add enum to tag space */
+ set_nextSibling($$,n);
+ Delete(n);
+
+ if (scopedenum) {
+ Swig_symbol_newscope();
+ Swig_symbol_setscopename($3);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ }
+
+ add_symbols($6); /* Add enum values to appropriate enum or enum class scope */
+
+ if (scopedenum) {
+ Setattr($$,"symtab", Swig_symbol_popscope());
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ }
+
+ add_symbols(n);
+ Delete(unnamed);
+ }
+ ;
+
+c_constructor_decl : storage_class type LPAREN parms RPAREN ctor_end {
+ /* This is a sick hack. If the ctor_end has parameters,
+ and the parms parameter only has 1 parameter, this
+ could be a declaration of the form:
+
+ type (id)(parms)
+
+ Otherwise it's an error. */
+ int err = 0;
+ $$ = 0;
+
+ if ((ParmList_len($4) == 1) && (!Swig_scopename_check($2))) {
+ SwigType *ty = Getattr($4,"type");
+ String *name = Getattr($4,"name");
+ err = 1;
+ if (!name) {
+ $$ = new_node("cdecl");
+ Setattr($$,"type",$2);
+ Setattr($$,"storage",$1);
+ Setattr($$,"name",ty);
+
+ if ($6.have_parms) {
+ SwigType *decl = NewStringEmpty();
+ SwigType_add_function(decl,$6.parms);
+ Setattr($$,"decl",decl);
+ Setattr($$,"parms",$6.parms);
+ if (Len(scanner_ccode)) {
+ String *code = Copy(scanner_ccode);
+ Setattr($$,"code",code);
+ Delete(code);
+ }
+ }
+ if ($6.defarg) {
+ Setattr($$,"value",$6.defarg);
+ }
+ Setattr($$,"throws",$6.throws);
+ Setattr($$,"throw",$6.throwf);
+ Setattr($$,"noexcept",$6.nexcept);
+ Setattr($$,"final",$6.final);
+ err = 0;
+ }
+ }
+ if (err) {
+ Swig_error(cparse_file,cparse_line,"Syntax error in input(2).\n");
+ Exit(EXIT_FAILURE);
+ }
+ }
+ ;
+
+/* ======================================================================
+ * C++ Support
+ * ====================================================================== */
+
+cpp_declaration : cpp_class_decl { $$ = $1; }
+ | cpp_forward_class_decl { $$ = $1; }
+ | cpp_template_decl { $$ = $1; }
+ | cpp_using_decl { $$ = $1; }
+ | cpp_namespace_decl { $$ = $1; }
+ | cpp_catch_decl { $$ = 0; }
+ ;
+
+
+/* A simple class/struct/union definition */
+
+/* Note that class_virt_specifier_opt for supporting final classes introduces one shift-reduce conflict
+ with C style variable declarations, such as: struct X final; */
+
+cpp_class_decl: storage_class cpptype idcolon class_virt_specifier_opt inherit LBRACE {
+ String *prefix;
+ List *bases = 0;
+ Node *scope = 0;
+ String *code;
+ $<node>$ = new_node("class");
+ Setline($<node>$,cparse_start_line);
+ Setattr($<node>$,"kind",$2);
+ if ($5) {
+ Setattr($<node>$,"baselist", Getattr($5,"public"));
+ Setattr($<node>$,"protectedbaselist", Getattr($5,"protected"));
+ Setattr($<node>$,"privatebaselist", Getattr($5,"private"));
+ }
+ Setattr($<node>$,"allows_typedef","1");
+
+ /* preserve the current scope */
+ Setattr($<node>$,"prev_symtab",Swig_symbol_current());
+
+ /* If the class name is qualified. We need to create or lookup namespace/scope entries */
+ scope = resolve_create_node_scope($3, 1);
+ /* save nscope_inner to the class - it may be overwritten in nested classes*/
+ Setattr($<node>$, "nested:innerscope", nscope_inner);
+ Setattr($<node>$, "nested:nscope", nscope);
+ Setfile(scope,cparse_file);
+ Setline(scope,cparse_line);
+ $3 = scope;
+ Setattr($<node>$,"name",$3);
+
+ if (currentOuterClass) {
+ SetFlag($<node>$, "nested");
+ Setattr($<node>$, "nested:outer", currentOuterClass);
+ set_access_mode($<node>$);
+ }
+ Swig_features_get(Swig_cparse_features(), Namespaceprefix, Getattr($<node>$, "name"), 0, $<node>$);
+ /* save yyrename to the class attribute, to be used later in add_symbols()*/
+ Setattr($<node>$, "class_rename", make_name($<node>$, $3, 0));
+ Setattr($<node>$, "Classprefix", $3);
+ Classprefix = NewString($3);
+ /* Deal with inheritance */
+ if ($5)
+ bases = Swig_make_inherit_list($3,Getattr($5,"public"),Namespaceprefix);
+ prefix = SwigType_istemplate_templateprefix($3);
+ if (prefix) {
+ String *fbase, *tbase;
+ if (Namespaceprefix) {
+ fbase = NewStringf("%s::%s", Namespaceprefix,$3);
+ tbase = NewStringf("%s::%s", Namespaceprefix, prefix);
+ } else {
+ fbase = Copy($3);
+ tbase = Copy(prefix);
+ }
+ Swig_name_inherit(tbase,fbase);
+ Delete(fbase);
+ Delete(tbase);
+ }
+ if (strcmp($2,"class") == 0) {
+ cplus_mode = CPLUS_PRIVATE;
+ } else {
+ cplus_mode = CPLUS_PUBLIC;
+ }
+ if (!cparse_cplusplus) {
+ set_scope_to_global();
+ }
+ Swig_symbol_newscope();
+ Swig_symbol_setscopename($3);
+ Swig_inherit_base_symbols(bases);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ cparse_start_line = cparse_line;
+
+ /* If there are active template parameters, we need to make sure they are
+ placed in the class symbol table so we can catch shadows */
+
+ if (template_parameters) {
+ Parm *tp = template_parameters;
+ while(tp) {
+ String *tpname = Copy(Getattr(tp,"name"));
+ Node *tn = new_node("templateparm");
+ Setattr(tn,"name",tpname);
+ Swig_symbol_cadd(tpname,tn);
+ tp = nextSibling(tp);
+ Delete(tpname);
+ }
+ }
+ Delete(prefix);
+ inclass = 1;
+ currentOuterClass = $<node>$;
+ if (cparse_cplusplusout) {
+ /* save the structure declaration to declare it in global scope for C++ to see */
+ code = get_raw_text_balanced('{', '}');
+ Setattr($<node>$, "code", code);
+ Delete(code);
+ }
+ } cpp_members RBRACE cpp_opt_declarators {
+ Node *p;
+ SwigType *ty;
+ Symtab *cscope;
+ Node *am = 0;
+ String *scpname = 0;
+ (void) $<node>6;
+ $$ = currentOuterClass;
+ currentOuterClass = Getattr($$, "nested:outer");
+ nscope_inner = Getattr($<node>$, "nested:innerscope");
+ nscope = Getattr($<node>$, "nested:nscope");
+ Delattr($<node>$, "nested:innerscope");
+ Delattr($<node>$, "nested:nscope");
+ if (nscope_inner && Strcmp(nodeType(nscope_inner), "class") == 0) { /* actual parent class for this class */
+ Node* forward_declaration = Swig_symbol_clookup_no_inherit(Getattr($<node>$,"name"), Getattr(nscope_inner, "symtab"));
+ if (forward_declaration) {
+ Setattr($<node>$, "access", Getattr(forward_declaration, "access"));
+ }
+ Setattr($<node>$, "nested:outer", nscope_inner);
+ SetFlag($<node>$, "nested");
+ }
+ if (!currentOuterClass)
+ inclass = 0;
+ cscope = Getattr($$, "prev_symtab");
+ Delattr($$, "prev_symtab");
+
+ /* Check for pure-abstract class */
+ Setattr($$,"abstracts", pure_abstracts($8));
+
+ /* This bit of code merges in a previously defined %extend directive (if any) */
+ {
+ String *clsname = Swig_symbol_qualifiedscopename(0);
+ am = Getattr(Swig_extend_hash(), clsname);
+ if (am) {
+ Swig_extend_merge($$, am);
+ Delattr(Swig_extend_hash(), clsname);
+ }
+ Delete(clsname);
+ }
+ if (!classes) classes = NewHash();
+ scpname = Swig_symbol_qualifiedscopename(0);
+ Setattr(classes, scpname, $$);
+
+ appendChild($$, $8);
+
+ if (am)
+ Swig_extend_append_previous($$, am);
+
+ p = $10;
+ if (p && !nscope_inner) {
+ if (!cparse_cplusplus && currentOuterClass)
+ appendChild(currentOuterClass, p);
+ else
+ appendSibling($$, p);
+ }
+
+ if (nscope_inner) {
+ ty = NewString(scpname); /* if the class is declared out of scope, let the declarator use fully qualified type*/
+ } else if (cparse_cplusplus && !cparse_externc) {
+ ty = NewString($3);
+ } else {
+ ty = NewStringf("%s %s", $2, $3);
+ }
+ while (p) {
+ Setattr(p, "storage", $1);
+ Setattr(p, "type" ,ty);
+ if (!cparse_cplusplus && currentOuterClass && (!Getattr(currentOuterClass, "name"))) {
+ SetFlag(p, "hasconsttype");
+ SetFlag(p, "feature:immutable");
+ }
+ p = nextSibling(p);
+ }
+ if ($10 && Cmp($1,"typedef") == 0)
+ add_typedef_name($$, $10, $3, cscope, scpname);
+ Delete(scpname);
+
+ if (cplus_mode != CPLUS_PUBLIC) {
+ /* we 'open' the class at the end, to allow %template
+ to add new members */
+ Node *pa = new_node("access");
+ Setattr(pa, "kind", "public");
+ cplus_mode = CPLUS_PUBLIC;
+ appendChild($$, pa);
+ Delete(pa);
+ }
+ if (currentOuterClass)
+ restore_access_mode($$);
+ Setattr($$, "symtab", Swig_symbol_popscope());
+ Classprefix = Getattr($<node>$, "Classprefix");
+ Delattr($<node>$, "Classprefix");
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ if (cplus_mode == CPLUS_PRIVATE) {
+ $$ = 0; /* skip private nested classes */
+ } else if (cparse_cplusplus && currentOuterClass && ignore_nested_classes && !GetFlag($$, "feature:flatnested")) {
+ $$ = nested_forward_declaration($1, $2, $3, Copy($3), $10);
+ } else if (nscope_inner) {
+ /* this is tricky */
+ /* we add the declaration in the original namespace */
+ if (Strcmp(nodeType(nscope_inner), "class") == 0 && cparse_cplusplus && ignore_nested_classes && !GetFlag($$, "feature:flatnested"))
+ $$ = nested_forward_declaration($1, $2, $3, Copy($3), $10);
+ appendChild(nscope_inner, $$);
+ Swig_symbol_setscope(Getattr(nscope_inner, "symtab"));
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ yyrename = Copy(Getattr($<node>$, "class_rename"));
+ add_symbols($$);
+ Delattr($$, "class_rename");
+ /* but the variable definition in the current scope */
+ Swig_symbol_setscope(cscope);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ add_symbols($10);
+ if (nscope) {
+ $$ = nscope; /* here we return recreated namespace tower instead of the class itself */
+ if ($10) {
+ appendSibling($$, $10);
+ }
+ } else if (!SwigType_istemplate(ty) && template_parameters == 0) { /* for template we need the class itself */
+ $$ = $10;
+ }
+ } else {
+ Delete(yyrename);
+ yyrename = 0;
+ if (!cparse_cplusplus && currentOuterClass) { /* nested C structs go into global scope*/
+ Node *outer = currentOuterClass;
+ while (Getattr(outer, "nested:outer"))
+ outer = Getattr(outer, "nested:outer");
+ appendSibling(outer, $$);
+ Swig_symbol_setscope(cscope); /* declaration goes in the parent scope */
+ add_symbols($10);
+ set_scope_to_global();
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ yyrename = Copy(Getattr($<node>$, "class_rename"));
+ add_symbols($$);
+ if (!cparse_cplusplusout)
+ Delattr($$, "nested:outer");
+ Delattr($$, "class_rename");
+ $$ = 0;
+ } else {
+ yyrename = Copy(Getattr($<node>$, "class_rename"));
+ add_symbols($$);
+ add_symbols($10);
+ Delattr($$, "class_rename");
+ }
+ }
+ Delete(ty);
+ Swig_symbol_setscope(cscope);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ Classprefix = currentOuterClass ? Getattr(currentOuterClass, "Classprefix") : 0;
+ }
+
+/* An unnamed struct, possibly with a typedef */
+
+ | storage_class cpptype inherit LBRACE {
+ String *unnamed;
+ String *code;
+ unnamed = make_unnamed();
+ $<node>$ = new_node("class");
+ Setline($<node>$,cparse_start_line);
+ Setattr($<node>$,"kind",$2);
+ if ($3) {
+ Setattr($<node>$,"baselist", Getattr($3,"public"));
+ Setattr($<node>$,"protectedbaselist", Getattr($3,"protected"));
+ Setattr($<node>$,"privatebaselist", Getattr($3,"private"));
+ }
+ Setattr($<node>$,"storage",$1);
+ Setattr($<node>$,"unnamed",unnamed);
+ Setattr($<node>$,"allows_typedef","1");
+ if (currentOuterClass) {
+ SetFlag($<node>$, "nested");
+ Setattr($<node>$, "nested:outer", currentOuterClass);
+ set_access_mode($<node>$);
+ }
+ Swig_features_get(Swig_cparse_features(), Namespaceprefix, 0, 0, $<node>$);
+ /* save yyrename to the class attribute, to be used later in add_symbols()*/
+ Setattr($<node>$, "class_rename", make_name($<node>$,0,0));
+ if (strcmp($2,"class") == 0) {
+ cplus_mode = CPLUS_PRIVATE;
+ } else {
+ cplus_mode = CPLUS_PUBLIC;
+ }
+ Swig_symbol_newscope();
+ cparse_start_line = cparse_line;
+ currentOuterClass = $<node>$;
+ inclass = 1;
+ Classprefix = 0;
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ /* save the structure declaration to make a typedef for it later*/
+ code = get_raw_text_balanced('{', '}');
+ Setattr($<node>$, "code", code);
+ Delete(code);
+ } cpp_members RBRACE cpp_opt_declarators {
+ String *unnamed;
+ List *bases = 0;
+ String *name = 0;
+ Node *n;
+ Classprefix = 0;
+ (void)$<node>5;
+ $$ = currentOuterClass;
+ currentOuterClass = Getattr($$, "nested:outer");
+ if (!currentOuterClass)
+ inclass = 0;
+ else
+ restore_access_mode($$);
+ unnamed = Getattr($$,"unnamed");
+ /* Check for pure-abstract class */
+ Setattr($$,"abstracts", pure_abstracts($6));
+ n = $8;
+ if (cparse_cplusplus && currentOuterClass && ignore_nested_classes && !GetFlag($$, "feature:flatnested")) {
+ String *name = n ? Copy(Getattr(n, "name")) : 0;
+ $$ = nested_forward_declaration($1, $2, 0, name, n);
+ Swig_symbol_popscope();
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ } else if (n) {
+ appendSibling($$,n);
+ /* If a proper typedef name was given, we'll use it to set the scope name */
+ name = try_to_find_a_name_for_unnamed_structure($1, n);
+ if (name) {
+ String *scpname = 0;
+ SwigType *ty;
+ Setattr($$,"tdname",name);
+ Setattr($$,"name",name);
+ Swig_symbol_setscopename(name);
+ if ($3)
+ bases = Swig_make_inherit_list(name,Getattr($3,"public"),Namespaceprefix);
+ Swig_inherit_base_symbols(bases);
+
+ /* If a proper name was given, we use that as the typedef, not unnamed */
+ Clear(unnamed);
+ Append(unnamed, name);
+ if (cparse_cplusplus && !cparse_externc) {
+ ty = NewString(name);
+ } else {
+ ty = NewStringf("%s %s", $2,name);
+ }
+ while (n) {
+ Setattr(n,"storage",$1);
+ Setattr(n, "type", ty);
+ if (!cparse_cplusplus && currentOuterClass && (!Getattr(currentOuterClass, "name"))) {
+ SetFlag(n,"hasconsttype");
+ SetFlag(n,"feature:immutable");
+ }
+ n = nextSibling(n);
+ }
+ n = $8;
+
+ /* Check for previous extensions */
+ {
+ String *clsname = Swig_symbol_qualifiedscopename(0);
+ Node *am = Getattr(Swig_extend_hash(),clsname);
+ if (am) {
+ /* Merge the extension into the symbol table */
+ Swig_extend_merge($$,am);
+ Swig_extend_append_previous($$,am);
+ Delattr(Swig_extend_hash(),clsname);
+ }
+ Delete(clsname);
+ }
+ if (!classes) classes = NewHash();
+ scpname = Swig_symbol_qualifiedscopename(0);
+ Setattr(classes,scpname,$$);
+ Delete(scpname);
+ } else { /* no suitable name was found for a struct */
+ Setattr($$, "nested:unnamed", Getattr(n, "name")); /* save the name of the first declarator for later use in name generation*/
+ while (n) { /* attach unnamed struct to the declarators, so that they would receive proper type later*/
+ Setattr(n, "nested:unnamedtype", $$);
+ Setattr(n, "storage", $1);
+ n = nextSibling(n);
+ }
+ n = $8;
+ Swig_symbol_setscopename("<unnamed>");
+ }
+ appendChild($$,$6);
+ /* Pop the scope */
+ Setattr($$,"symtab",Swig_symbol_popscope());
+ if (name) {
+ Delete(yyrename);
+ yyrename = Copy(Getattr($<node>$, "class_rename"));
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ add_symbols($$);
+ add_symbols(n);
+ Delattr($$, "class_rename");
+ }else if (cparse_cplusplus)
+ $$ = 0; /* ignore unnamed structs for C++ */
+ Delete(unnamed);
+ } else { /* unnamed struct w/o declarator*/
+ Swig_symbol_popscope();
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ add_symbols($6);
+ Delete($$);
+ $$ = $6; /* pass member list to outer class/namespace (instead of self)*/
+ }
+ Classprefix = currentOuterClass ? Getattr(currentOuterClass, "Classprefix") : 0;
+ }
+ ;
+
+cpp_opt_declarators : SEMI { $$ = 0; }
+ | declarator cpp_const initializer c_decl_tail {
+ $$ = new_node("cdecl");
+ Setattr($$,"name",$1.id);
+ Setattr($$,"decl",$1.type);
+ Setattr($$,"parms",$1.parms);
+ set_nextSibling($$, $4);
+ }
+ ;
+/* ------------------------------------------------------------
+ class Name;
+ ------------------------------------------------------------ */
+
+cpp_forward_class_decl : storage_class cpptype idcolon SEMI {
+ if ($1 && (Strcmp($1,"friend") == 0)) {
+ /* Ignore */
+ $$ = 0;
+ } else {
+ $$ = new_node("classforward");
+ Setattr($$,"kind",$2);
+ Setattr($$,"name",$3);
+ Setattr($$,"sym:weak", "1");
+ add_symbols($$);
+ }
+ }
+ ;
+
+/* ------------------------------------------------------------
+ template<...> decl
+ ------------------------------------------------------------ */
+
+cpp_template_decl : TEMPLATE LESSTHAN template_parms GREATERTHAN {
+ if (currentOuterClass)
+ Setattr(currentOuterClass, "template_parameters", template_parameters);
+ template_parameters = $3;
+ parsing_template_declaration = 1;
+ } cpp_template_possible {
+ String *tname = 0;
+ int error = 0;
+
+ /* check if we get a namespace node with a class declaration, and retrieve the class */
+ Symtab *cscope = Swig_symbol_current();
+ Symtab *sti = 0;
+ Node *ntop = $6;
+ Node *ni = ntop;
+ SwigType *ntype = ni ? nodeType(ni) : 0;
+ while (ni && Strcmp(ntype,"namespace") == 0) {
+ sti = Getattr(ni,"symtab");
+ ni = firstChild(ni);
+ ntype = nodeType(ni);
+ }
+ if (sti) {
+ Swig_symbol_setscope(sti);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ $6 = ni;
+ }
+
+ $$ = $6;
+ if ($$) tname = Getattr($$,"name");
+
+ /* Check if the class is a template specialization */
+ if (($$) && (Strchr(tname,'<')) && (!is_operator(tname))) {
+ /* If a specialization. Check if defined. */
+ Node *tempn = 0;
+ {
+ String *tbase = SwigType_templateprefix(tname);
+ tempn = Swig_symbol_clookup_local(tbase,0);
+ if (!tempn || (Strcmp(nodeType(tempn),"template") != 0)) {
+ SWIG_WARN_NODE_BEGIN(tempn);
+ Swig_warning(WARN_PARSE_TEMPLATE_SP_UNDEF, Getfile($$),Getline($$),"Specialization of non-template '%s'.\n", tbase);
+ SWIG_WARN_NODE_END(tempn);
+ tempn = 0;
+ error = 1;
+ }
+ Delete(tbase);
+ }
+ Setattr($$,"specialization","1");
+ Setattr($$,"templatetype",nodeType($$));
+ set_nodeType($$,"template");
+ /* Template partial specialization */
+ if (tempn && ($3) && ($6)) {
+ List *tlist;
+ String *targs = SwigType_templateargs(tname);
+ tlist = SwigType_parmlist(targs);
+ /* Printf(stdout,"targs = '%s' %s\n", targs, tlist); */
+ if (!Getattr($$,"sym:weak")) {
+ Setattr($$,"sym:typename","1");
+ }
+
+ if (Len(tlist) != ParmList_len(Getattr(tempn,"templateparms"))) {
+ Swig_error(Getfile($$),Getline($$),"Inconsistent argument count in template partial specialization. %d %d\n", Len(tlist), ParmList_len(Getattr(tempn,"templateparms")));
+
+ } else {
+
+ /* This code builds the argument list for the partial template
+ specialization. This is a little hairy, but the idea is as
+ follows:
+
+ $3 contains a list of arguments supplied for the template.
+ For example template<class T>.
+
+ tlist is a list of the specialization arguments--which may be
+ different. For example class<int,T>.
+
+ tp is a copy of the arguments in the original template definition.
+
+ The patching algorithm walks through the list of supplied
+ arguments ($3), finds the position in the specialization arguments
+ (tlist), and then patches the name in the argument list of the
+ original template.
+ */
+
+ {
+ String *pn;
+ Parm *p, *p1;
+ int i, nargs;
+ Parm *tp = CopyParmList(Getattr(tempn,"templateparms"));
+ nargs = Len(tlist);
+ p = $3;
+ while (p) {
+ for (i = 0; i < nargs; i++){
+ pn = Getattr(p,"name");
+ if (Strcmp(pn,SwigType_base(Getitem(tlist,i))) == 0) {
+ int j;
+ Parm *p1 = tp;
+ for (j = 0; j < i; j++) {
+ p1 = nextSibling(p1);
+ }
+ Setattr(p1,"name",pn);
+ Setattr(p1,"partialarg","1");
+ }
+ }
+ p = nextSibling(p);
+ }
+ p1 = tp;
+ i = 0;
+ while (p1) {
+ if (!Getattr(p1,"partialarg")) {
+ Delattr(p1,"name");
+ Setattr(p1,"type", Getitem(tlist,i));
+ }
+ i++;
+ p1 = nextSibling(p1);
+ }
+ Setattr($$,"templateparms",tp);
+ Delete(tp);
+ }
+ #if 0
+ /* Patch the parameter list */
+ if (tempn) {
+ Parm *p,*p1;
+ ParmList *tp = CopyParmList(Getattr(tempn,"templateparms"));
+ p = $3;
+ p1 = tp;
+ while (p && p1) {
+ String *pn = Getattr(p,"name");
+ Printf(stdout,"pn = '%s'\n", pn);
+ if (pn) Setattr(p1,"name",pn);
+ else Delattr(p1,"name");
+ pn = Getattr(p,"type");
+ if (pn) Setattr(p1,"type",pn);
+ p = nextSibling(p);
+ p1 = nextSibling(p1);
+ }
+ Setattr($$,"templateparms",tp);
+ Delete(tp);
+ } else {
+ Setattr($$,"templateparms",$3);
+ }
+ #endif
+ Delattr($$,"specialization");
+ Setattr($$,"partialspecialization","1");
+ /* Create a specialized name for matching */
+ {
+ Parm *p = $3;
+ String *fname = NewString(Getattr($$,"name"));
+ String *ffname = 0;
+ ParmList *partialparms = 0;
+
+ char tmp[32];
+ int i, ilen;
+ while (p) {
+ String *n = Getattr(p,"name");
+ if (!n) {
+ p = nextSibling(p);
+ continue;
+ }
+ ilen = Len(tlist);
+ for (i = 0; i < ilen; i++) {
+ if (Strstr(Getitem(tlist,i),n)) {
+ sprintf(tmp,"$%d",i+1);
+ Replaceid(fname,n,tmp);
+ }
+ }
+ p = nextSibling(p);
+ }
+ /* Patch argument names with typedef */
+ {
+ Iterator tt;
+ Parm *parm_current = 0;
+ List *tparms = SwigType_parmlist(fname);
+ ffname = SwigType_templateprefix(fname);
+ Append(ffname,"<(");
+ for (tt = First(tparms); tt.item; ) {
+ SwigType *rtt = Swig_symbol_typedef_reduce(tt.item,0);
+ SwigType *ttr = Swig_symbol_type_qualify(rtt,0);
+
+ Parm *newp = NewParmWithoutFileLineInfo(ttr, 0);
+ if (partialparms)
+ set_nextSibling(parm_current, newp);
+ else
+ partialparms = newp;
+ parm_current = newp;
+
+ Append(ffname,ttr);
+ tt = Next(tt);
+ if (tt.item) Putc(',',ffname);
+ Delete(rtt);
+ Delete(ttr);
+ }
+ Delete(tparms);
+ Append(ffname,")>");
+ }
+ {
+ Node *new_partial = NewHash();
+ String *partials = Getattr(tempn,"partials");
+ if (!partials) {
+ partials = NewList();
+ Setattr(tempn,"partials",partials);
+ Delete(partials);
+ }
+ /* Printf(stdout,"partial: fname = '%s', '%s'\n", fname, Swig_symbol_typedef_reduce(fname,0)); */
+ Setattr(new_partial, "partialparms", partialparms);
+ Setattr(new_partial, "templcsymname", ffname);
+ Append(partials, new_partial);
+ }
+ Setattr($$,"partialargs",ffname);
+ Swig_symbol_cadd(ffname,$$);
+ }
+ }
+ Delete(tlist);
+ Delete(targs);
+ } else {
+ /* An explicit template specialization */
+ /* add default args from primary (unspecialized) template */
+ String *ty = Swig_symbol_template_deftype(tname,0);
+ String *fname = Swig_symbol_type_qualify(ty,0);
+ Swig_symbol_cadd(fname,$$);
+ Delete(ty);
+ Delete(fname);
+ }
+ } else if ($$) {
+ Setattr($$,"templatetype",nodeType($6));
+ set_nodeType($$,"template");
+ Setattr($$,"templateparms", $3);
+ if (!Getattr($$,"sym:weak")) {
+ Setattr($$,"sym:typename","1");
+ }
+ add_symbols($$);
+ default_arguments($$);
+ /* We also place a fully parameterized version in the symbol table */
+ {
+ Parm *p;
+ String *fname = NewStringf("%s<(", Getattr($$,"name"));
+ p = $3;
+ while (p) {
+ String *n = Getattr(p,"name");
+ if (!n) n = Getattr(p,"type");
+ Append(fname,n);
+ p = nextSibling(p);
+ if (p) Putc(',',fname);
+ }
+ Append(fname,")>");
+ Swig_symbol_cadd(fname,$$);
+ }
+ }
+ $$ = ntop;
+ Swig_symbol_setscope(cscope);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ if (error || (nscope_inner && Strcmp(nodeType(nscope_inner), "class") == 0)) {
+ $$ = 0;
+ }
+ if (currentOuterClass)
+ template_parameters = Getattr(currentOuterClass, "template_parameters");
+ else
+ template_parameters = 0;
+ parsing_template_declaration = 0;
+ }
+
+ /* Class template explicit instantiation definition */
+ | TEMPLATE cpptype idcolon {
+ Swig_warning(WARN_PARSE_EXPLICIT_TEMPLATE, cparse_file, cparse_line, "Explicit template instantiation ignored.\n");
+ $$ = 0;
+ }
+
+ /* Function template explicit instantiation definition */
+ | TEMPLATE cpp_alternate_rettype idcolon LPAREN parms RPAREN {
+ Swig_warning(WARN_PARSE_EXPLICIT_TEMPLATE, cparse_file, cparse_line, "Explicit template instantiation ignored.\n");
+ $$ = 0;
+ }
+
+ /* Class template explicit instantiation declaration (extern template) */
+ | EXTERN TEMPLATE cpptype idcolon {
+ Swig_warning(WARN_PARSE_EXTERN_TEMPLATE, cparse_file, cparse_line, "Extern template ignored.\n");
+ $$ = 0;
+ }
+
+ /* Function template explicit instantiation declaration (extern template) */
+ | EXTERN TEMPLATE cpp_alternate_rettype idcolon LPAREN parms RPAREN {
+ Swig_warning(WARN_PARSE_EXTERN_TEMPLATE, cparse_file, cparse_line, "Extern template ignored.\n");
+ $$ = 0;
+ }
+ ;
+
+cpp_template_possible: c_decl {
+ $$ = $1;
+ }
+ | cpp_class_decl {
+ $$ = $1;
+ }
+ | cpp_constructor_decl {
+ $$ = $1;
+ }
+ | cpp_template_decl {
+ $$ = 0;
+ }
+ | cpp_forward_class_decl {
+ $$ = $1;
+ }
+ | cpp_conversion_operator {
+ $$ = $1;
+ }
+ ;
+
+template_parms : templateparameters {
+ /* Rip out the parameter names */
+ Parm *p = $1;
+ $$ = $1;
+
+ while (p) {
+ String *name = Getattr(p,"name");
+ if (!name) {
+ /* Hmmm. Maybe it's a 'class T' parameter */
+ char *type = Char(Getattr(p,"type"));
+ /* Template template parameter */
+ if (strncmp(type,"template<class> ",16) == 0) {
+ type += 16;
+ }
+ if ((strncmp(type,"class ",6) == 0) || (strncmp(type,"typename ", 9) == 0)) {
+ char *t = strchr(type,' ');
+ Setattr(p,"name", t+1);
+ } else
+ /* Variadic template args */
+ if ((strncmp(type,"class... ",9) == 0) || (strncmp(type,"typename... ", 12) == 0)) {
+ char *t = strchr(type,' ');
+ Setattr(p,"name", t+1);
+ Setattr(p,"variadic", "1");
+ } else {
+ /*
+ Swig_error(cparse_file, cparse_line, "Missing template parameter name\n");
+ $$.rparms = 0;
+ $$.parms = 0;
+ break; */
+ }
+ }
+ p = nextSibling(p);
+ }
+ }
+ ;
+
+templateparameters : templateparameter templateparameterstail {
+ set_nextSibling($1,$2);
+ $$ = $1;
+ }
+ | empty { $$ = 0; }
+ ;
+
+templateparameter : templcpptype def_args {
+ $$ = NewParmWithoutFileLineInfo(NewString($1), 0);
+ Setattr($$, "value", $2.rawval ? $2.rawval : $2.val);
+ }
+ | parm {
+ $$ = $1;
+ }
+ ;
+
+templateparameterstail : COMMA templateparameter templateparameterstail {
+ set_nextSibling($2,$3);
+ $$ = $2;
+ }
+ | empty { $$ = 0; }
+ ;
+
+/* Namespace support */
+
+cpp_using_decl : USING idcolon SEMI {
+ String *uname = Swig_symbol_type_qualify($2,0);
+ String *name = Swig_scopename_last($2);
+ $$ = new_node("using");
+ Setattr($$,"uname",uname);
+ Setattr($$,"name", name);
+ Delete(uname);
+ Delete(name);
+ add_symbols($$);
+ }
+ | USING NAMESPACE idcolon SEMI {
+ Node *n = Swig_symbol_clookup($3,0);
+ if (!n) {
+ Swig_error(cparse_file, cparse_line, "Nothing known about namespace '%s'\n", $3);
+ $$ = 0;
+ } else {
+
+ while (Strcmp(nodeType(n),"using") == 0) {
+ n = Getattr(n,"node");
+ }
+ if (n) {
+ if (Strcmp(nodeType(n),"namespace") == 0) {
+ Symtab *current = Swig_symbol_current();
+ Symtab *symtab = Getattr(n,"symtab");
+ $$ = new_node("using");
+ Setattr($$,"node",n);
+ Setattr($$,"namespace", $3);
+ if (current != symtab) {
+ Swig_symbol_inherit(symtab);
+ }
+ } else {
+ Swig_error(cparse_file, cparse_line, "'%s' is not a namespace.\n", $3);
+ $$ = 0;
+ }
+ } else {
+ $$ = 0;
+ }
+ }
+ }
+ ;
+
+cpp_namespace_decl : NAMESPACE idcolon LBRACE {
+ Hash *h;
+ Node *parent_ns = 0;
+ List *scopes = Swig_scopename_tolist($2);
+ int ilen = Len(scopes);
+ int i;
+
+/*
+Printf(stdout, "==== Namespace %s creation...\n", $2);
+*/
+ $<node>$ = 0;
+ for (i = 0; i < ilen; i++) {
+ Node *ns = new_node("namespace");
+ Symtab *current_symtab = Swig_symbol_current();
+ String *scopename = Getitem(scopes, i);
+ Setattr(ns, "name", scopename);
+ $<node>$ = ns;
+ if (parent_ns)
+ appendChild(parent_ns, ns);
+ parent_ns = ns;
+ h = Swig_symbol_clookup(scopename, 0);
+ if (h && (current_symtab == Getattr(h, "sym:symtab")) && (Strcmp(nodeType(h), "namespace") == 0)) {
+/*
+Printf(stdout, " Scope %s [found C++17 style]\n", scopename);
+*/
+ if (Getattr(h, "alias")) {
+ h = Getattr(h, "namespace");
+ Swig_warning(WARN_PARSE_NAMESPACE_ALIAS, cparse_file, cparse_line, "Namespace alias '%s' not allowed here. Assuming '%s'\n",
+ scopename, Getattr(h, "name"));
+ scopename = Getattr(h, "name");
+ }
+ Swig_symbol_setscope(Getattr(h, "symtab"));
+ } else {
+/*
+Printf(stdout, " Scope %s [creating single scope C++17 style]\n", scopename);
+*/
+ h = Swig_symbol_newscope();
+ Swig_symbol_setscopename(scopename);
+ }
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ }
+ Delete(scopes);
+ } interface RBRACE {
+ Node *n = $<node>4;
+ Node *top_ns = 0;
+ do {
+ Setattr(n, "symtab", Swig_symbol_popscope());
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ add_symbols(n);
+ top_ns = n;
+ n = parentNode(n);
+ } while(n);
+ appendChild($<node>4, firstChild($5));
+ Delete($5);
+ $$ = top_ns;
+ }
+ | NAMESPACE LBRACE {
+ Hash *h;
+ $1 = Swig_symbol_current();
+ h = Swig_symbol_clookup(" ",0);
+ if (h && (Strcmp(nodeType(h),"namespace") == 0)) {
+ Swig_symbol_setscope(Getattr(h,"symtab"));
+ } else {
+ Swig_symbol_newscope();
+ /* we don't use "__unnamed__", but a long 'empty' name */
+ Swig_symbol_setscopename(" ");
+ }
+ Namespaceprefix = 0;
+ } interface RBRACE {
+ $$ = $4;
+ set_nodeType($$,"namespace");
+ Setattr($$,"unnamed","1");
+ Setattr($$,"symtab", Swig_symbol_popscope());
+ Swig_symbol_setscope($1);
+ Delete(Namespaceprefix);
+ Namespaceprefix = Swig_symbol_qualifiedscopename(0);
+ add_symbols($$);
+ }
+ | NAMESPACE identifier EQUAL idcolon SEMI {
+ /* Namespace alias */
+ Node *n;
+ $$ = new_node("namespace");
+ Setattr($$,"name",$2);
+ Setattr($$,"alias",$4);
+ n = Swig_symbol_clookup($4,0);
+ if (!n) {
+ Swig_error(cparse_file, cparse_line, "Unknown namespace '%s'\n", $4);
+ $$ = 0;
+ } else {
+ if (Strcmp(nodeType(n),"namespace") != 0) {
+ Swig_error(cparse_file, cparse_line, "'%s' is not a namespace\n",$4);
+ $$ = 0;
+ } else {
+ while (Getattr(n,"alias")) {
+ n = Getattr(n,"namespace");
+ }
+ Setattr($$,"namespace",n);
+ add_symbols($$);
+ /* Set up a scope alias */
+ Swig_symbol_alias($2,Getattr(n,"symtab"));
+ }
+ }
+ }
+ ;
+
+cpp_members : cpp_member cpp_members {
+ $$ = $1;
+ /* Insert cpp_member (including any siblings) to the front of the cpp_members linked list */
+ if ($$) {
+ Node *p = $$;
+ Node *pp =0;
+ while (p) {
+ pp = p;
+ p = nextSibling(p);
+ }
+ set_nextSibling(pp,$2);
+ if ($2)
+ set_previousSibling($2, pp);
+ } else {
+ $$ = $2;
+ }
+ }
+ | EXTEND LBRACE {
+ extendmode = 1;
+ if (cplus_mode != CPLUS_PUBLIC) {
+ Swig_error(cparse_file,cparse_line,"%%extend can only be used in a public section\n");
+ }
+ } cpp_members RBRACE {
+ extendmode = 0;
+ } cpp_members {
+ $$ = new_node("extend");
+ mark_nodes_as_extend($4);
+ appendChild($$,$4);
+ set_nextSibling($$,$7);
+ }
+ | include_directive { $$ = $1; }
+ | empty { $$ = 0;}
+ | error {
+ Swig_error(cparse_file,cparse_line,"Syntax error in input(3).\n");
+ Exit(EXIT_FAILURE);
+ } cpp_members {
+ $$ = $3;
+ }
+ ;
+
+/* ======================================================================
+ * C++ Class members
+ * ====================================================================== */
+
+/* A class member. May be data or a function. Static or virtual as well */
+
+cpp_member_no_dox : c_declaration { $$ = $1; }
+ | cpp_constructor_decl {
+ $$ = $1;
+ if (extendmode && current_class) {
+ String *symname;
+ symname= make_name($$,Getattr($$,"name"), Getattr($$,"decl"));
+ if (Strcmp(symname,Getattr($$,"name")) == 0) {
+ /* No renaming operation. Set name to class name */
+ Delete(yyrename);
+ yyrename = NewString(Getattr(current_class,"sym:name"));
+ } else {
+ Delete(yyrename);
+ yyrename = symname;
+ }
+ }
+ add_symbols($$);
+ default_arguments($$);
+ }
+ | cpp_destructor_decl { $$ = $1; }
+ | cpp_protection_decl { $$ = $1; }
+ | cpp_swig_directive { $$ = $1; }
+ | cpp_conversion_operator { $$ = $1; }
+ | cpp_forward_class_decl { $$ = $1; }
+ | cpp_class_decl { $$ = $1; }
+ | storage_class idcolon SEMI { $$ = 0; }
+ | cpp_using_decl { $$ = $1; }
+ | cpp_template_decl { $$ = $1; }
+ | cpp_catch_decl { $$ = 0; }
+ | template_directive { $$ = $1; }
+ | warn_directive { $$ = $1; }
+ | anonymous_bitfield { $$ = 0; }
+ | fragment_directive {$$ = $1; }
+ | types_directive {$$ = $1; }
+ | SEMI { $$ = 0; }
+
+cpp_member : cpp_member_no_dox {
+ $$ = $1;
+ }
+ | DOXYGENSTRING cpp_member_no_dox {
+ $$ = $2;
+ set_comment($2, $1);
+ }
+ | cpp_member_no_dox DOXYGENPOSTSTRING {
+ $$ = $1;
+ set_comment($1, $2);
+ }
+ ;
+
+/* Possibly a constructor */
+/* Note: the use of 'type' is here to resolve a shift-reduce conflict. For example:
+ typedef Foo ();
+ typedef Foo (*ptr)();
+*/
+
+cpp_constructor_decl : storage_class type LPAREN parms RPAREN ctor_end {
+ if (inclass || extendmode) {
+ SwigType *decl = NewStringEmpty();
+ $$ = new_node("constructor");
+ Setattr($$,"storage",$1);
+ Setattr($$,"name",$2);
+ Setattr($$,"parms",$4);
+ SwigType_add_function(decl,$4);
+ Setattr($$,"decl",decl);
+ Setattr($$,"throws",$6.throws);
+ Setattr($$,"throw",$6.throwf);
+ Setattr($$,"noexcept",$6.nexcept);
+ Setattr($$,"final",$6.final);
+ if (Len(scanner_ccode)) {
+ String *code = Copy(scanner_ccode);
+ Setattr($$,"code",code);
+ Delete(code);
+ }
+ SetFlag($$,"feature:new");
+ if ($6.defarg)
+ Setattr($$,"value",$6.defarg);
+ } else {
+ $$ = 0;
+ }
+ }
+ ;
+
+/* A destructor (hopefully) */
+
+cpp_destructor_decl : NOT idtemplate LPAREN parms RPAREN cpp_end {
+ String *name = NewStringf("%s",$2);
+ if (*(Char(name)) != '~') Insert(name,0,"~");
+ $$ = new_node("destructor");
+ Setattr($$,"name",name);
+ Delete(name);
+ if (Len(scanner_ccode)) {
+ String *code = Copy(scanner_ccode);
+ Setattr($$,"code",code);
+ Delete(code);
+ }
+ {
+ String *decl = NewStringEmpty();
+ SwigType_add_function(decl,$4);
+ Setattr($$,"decl",decl);
+ Delete(decl);
+ }
+ Setattr($$,"throws",$6.throws);
+ Setattr($$,"throw",$6.throwf);
+ Setattr($$,"noexcept",$6.nexcept);
+ Setattr($$,"final",$6.final);
+ if ($6.val)
+ Setattr($$,"value",$6.val);
+ if ($6.qualifier)
+ Swig_error(cparse_file, cparse_line, "Destructor %s %s cannot have a qualifier.\n", Swig_name_decl($$), SwigType_str($6.qualifier, 0));
+ add_symbols($$);
+ }
+
+/* A virtual destructor */
+
+ | VIRTUAL NOT idtemplate LPAREN parms RPAREN cpp_vend {
+ String *name;
+ $$ = new_node("destructor");
+ Setattr($$,"storage","virtual");
+ name = NewStringf("%s",$3);
+ if (*(Char(name)) != '~') Insert(name,0,"~");
+ Setattr($$,"name",name);
+ Delete(name);
+ Setattr($$,"throws",$7.throws);
+ Setattr($$,"throw",$7.throwf);
+ Setattr($$,"noexcept",$7.nexcept);
+ Setattr($$,"final",$7.final);
+ if ($7.val)
+ Setattr($$,"value",$7.val);
+ if (Len(scanner_ccode)) {
+ String *code = Copy(scanner_ccode);
+ Setattr($$,"code",code);
+ Delete(code);
+ }
+ {
+ String *decl = NewStringEmpty();
+ SwigType_add_function(decl,$5);
+ Setattr($$,"decl",decl);
+ Delete(decl);
+ }
+ if ($7.qualifier)
+ Swig_error(cparse_file, cparse_line, "Destructor %s %s cannot have a qualifier.\n", Swig_name_decl($$), SwigType_str($7.qualifier, 0));
+ add_symbols($$);
+ }
+ ;
+
+
+/* C++ type conversion operator */
+cpp_conversion_operator : storage_class CONVERSIONOPERATOR type pointer LPAREN parms RPAREN cpp_vend {
+ $$ = new_node("cdecl");
+ Setattr($$,"type",$3);
+ Setattr($$,"name",$2);
+ Setattr($$,"storage",$1);
+
+ SwigType_add_function($4,$6);
+ if ($8.qualifier) {
+ SwigType_push($4,$8.qualifier);
+ }
+ if ($8.val) {
+ Setattr($$,"value",$8.val);
+ }
+ Setattr($$,"refqualifier",$8.refqualifier);
+ Setattr($$,"decl",$4);
+ Setattr($$,"parms",$6);
+ Setattr($$,"conversion_operator","1");
+ add_symbols($$);
+ }
+ | storage_class CONVERSIONOPERATOR type AND LPAREN parms RPAREN cpp_vend {
+ SwigType *decl;
+ $$ = new_node("cdecl");
+ Setattr($$,"type",$3);
+ Setattr($$,"name",$2);
+ Setattr($$,"storage",$1);
+ decl = NewStringEmpty();
+ SwigType_add_reference(decl);
+ SwigType_add_function(decl,$6);
+ if ($8.qualifier) {
+ SwigType_push(decl,$8.qualifier);
+ }
+ if ($8.val) {
+ Setattr($$,"value",$8.val);
+ }
+ Setattr($$,"refqualifier",$8.refqualifier);
+ Setattr($$,"decl",decl);
+ Setattr($$,"parms",$6);
+ Setattr($$,"conversion_operator","1");
+ add_symbols($$);
+ }
+ | storage_class CONVERSIONOPERATOR type LAND LPAREN parms RPAREN cpp_vend {
+ SwigType *decl;
+ $$ = new_node("cdecl");
+ Setattr($$,"type",$3);
+ Setattr($$,"name",$2);
+ Setattr($$,"storage",$1);
+ decl = NewStringEmpty();
+ SwigType_add_rvalue_reference(decl);
+ SwigType_add_function(decl,$6);
+ if ($8.qualifier) {
+ SwigType_push(decl,$8.qualifier);
+ }
+ if ($8.val) {
+ Setattr($$,"value",$8.val);
+ }
+ Setattr($$,"refqualifier",$8.refqualifier);
+ Setattr($$,"decl",decl);
+ Setattr($$,"parms",$6);
+ Setattr($$,"conversion_operator","1");
+ add_symbols($$);
+ }
+
+ | storage_class CONVERSIONOPERATOR type pointer AND LPAREN parms RPAREN cpp_vend {
+ SwigType *decl;
+ $$ = new_node("cdecl");
+ Setattr($$,"type",$3);
+ Setattr($$,"name",$2);
+ Setattr($$,"storage",$1);
+ decl = NewStringEmpty();
+ SwigType_add_pointer(decl);
+ SwigType_add_reference(decl);
+ SwigType_add_function(decl,$7);
+ if ($9.qualifier) {
+ SwigType_push(decl,$9.qualifier);
+ }
+ if ($9.val) {
+ Setattr($$,"value",$9.val);
+ }
+ Setattr($$,"refqualifier",$9.refqualifier);
+ Setattr($$,"decl",decl);
+ Setattr($$,"parms",$7);
+ Setattr($$,"conversion_operator","1");
+ add_symbols($$);
+ }
+
+ | storage_class CONVERSIONOPERATOR type LPAREN parms RPAREN cpp_vend {
+ String *t = NewStringEmpty();
+ $$ = new_node("cdecl");
+ Setattr($$,"type",$3);
+ Setattr($$,"name",$2);
+ Setattr($$,"storage",$1);
+ SwigType_add_function(t,$5);
+ if ($7.qualifier) {
+ SwigType_push(t,$7.qualifier);
+ }
+ if ($7.val) {
+ Setattr($$,"value",$7.val);
+ }
+ Setattr($$,"refqualifier",$7.refqualifier);
+ Setattr($$,"decl",t);
+ Setattr($$,"parms",$5);
+ Setattr($$,"conversion_operator","1");
+ add_symbols($$);
+ }
+ ;
+
+/* isolated catch clause. */
+
+cpp_catch_decl : CATCH LPAREN parms RPAREN LBRACE {
+ skip_balanced('{','}');
+ $$ = 0;
+ }
+ ;
+
+/* static_assert(bool, const char*); (C++11)
+ * static_assert(bool); (C++17) */
+cpp_static_assert : STATIC_ASSERT LPAREN {
+ skip_balanced('(',')');
+ $$ = 0;
+ }
+ ;
+
+/* public: */
+cpp_protection_decl : PUBLIC COLON {
+ $$ = new_node("access");
+ Setattr($$,"kind","public");
+ cplus_mode = CPLUS_PUBLIC;
+ }
+
+/* private: */
+ | PRIVATE COLON {
+ $$ = new_node("access");
+ Setattr($$,"kind","private");
+ cplus_mode = CPLUS_PRIVATE;
+ }
+
+/* protected: */
+
+ | PROTECTED COLON {
+ $$ = new_node("access");
+ Setattr($$,"kind","protected");
+ cplus_mode = CPLUS_PROTECTED;
+ }
+ ;
+/* These directives can be included inside a class definition */
+
+cpp_swig_directive: pragma_directive { $$ = $1; }
+
+/* A constant (includes #defines) inside a class */
+ | constant_directive { $$ = $1; }
+
+/* This is the new style rename */
+
+ | name_directive { $$ = $1; }
+
+/* rename directive */
+ | rename_directive { $$ = $1; }
+ | feature_directive { $$ = $1; }
+ | varargs_directive { $$ = $1; }
+ | insert_directive { $$ = $1; }
+ | typemap_directive { $$ = $1; }
+ | apply_directive { $$ = $1; }
+ | clear_directive { $$ = $1; }
+ | echo_directive { $$ = $1; }
+ ;
+
+cpp_end : cpp_const SEMI {
+ Clear(scanner_ccode);
+ $$.val = 0;
+ $$.qualifier = $1.qualifier;
+ $$.refqualifier = $1.refqualifier;
+ $$.bitfield = 0;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ }
+ | cpp_const EQUAL default_delete SEMI {
+ Clear(scanner_ccode);
+ $$.val = $3.val;
+ $$.qualifier = $1.qualifier;
+ $$.refqualifier = $1.refqualifier;
+ $$.bitfield = 0;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ }
+ | cpp_const LBRACE {
+ skip_balanced('{','}');
+ $$.val = 0;
+ $$.qualifier = $1.qualifier;
+ $$.refqualifier = $1.refqualifier;
+ $$.bitfield = 0;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ }
+ ;
+
+cpp_vend : cpp_const SEMI {
+ Clear(scanner_ccode);
+ $$.val = 0;
+ $$.qualifier = $1.qualifier;
+ $$.refqualifier = $1.refqualifier;
+ $$.bitfield = 0;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ }
+ | cpp_const EQUAL definetype SEMI {
+ Clear(scanner_ccode);
+ $$.val = $3.val;
+ $$.qualifier = $1.qualifier;
+ $$.refqualifier = $1.refqualifier;
+ $$.bitfield = 0;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ }
+ | cpp_const LBRACE {
+ skip_balanced('{','}');
+ $$.val = 0;
+ $$.qualifier = $1.qualifier;
+ $$.refqualifier = $1.refqualifier;
+ $$.bitfield = 0;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ }
+ ;
+
+
+anonymous_bitfield : storage_class anon_bitfield_type COLON expr SEMI { };
+
+/* Equals type_right without the ENUM keyword and cpptype (templates etc.): */
+anon_bitfield_type : primitive_type { $$ = $1;
+ /* Printf(stdout,"primitive = '%s'\n", $$);*/
+ }
+ | TYPE_BOOL { $$ = $1; }
+ | TYPE_VOID { $$ = $1; }
+/*
+ | TYPE_TYPEDEF template_decl { $$ = NewStringf("%s%s",$1,$2); }
+*/
+ | TYPE_RAW { $$ = $1; }
+
+ | idcolon {
+ $$ = $1;
+ }
+ ;
+
+/* ======================================================================
+ * PRIMITIVES
+ * ====================================================================== */
+extern_string : EXTERN string {
+ if (Strcmp($2,"C") == 0) {
+ $$ = "externc";
+ } else if (Strcmp($2,"C++") == 0) {
+ $$ = "extern";
+ } else {
+ Swig_warning(WARN_PARSE_UNDEFINED_EXTERN,cparse_file, cparse_line,"Unrecognized extern type \"%s\".\n", $2);
+ $$ = 0;
+ }
+ }
+ ;
+
+storage_class : EXTERN { $$ = "extern"; }
+ | extern_string { $$ = $1; }
+ | extern_string THREAD_LOCAL {
+ if (Equal($1, "extern")) {
+ $$ = "extern thread_local";
+ } else {
+ $$ = "externc thread_local";
+ }
+ }
+ | extern_string TYPEDEF { $$ = "typedef"; }
+ | STATIC { $$ = "static"; }
+ | TYPEDEF { $$ = "typedef"; }
+ | VIRTUAL { $$ = "virtual"; }
+ | FRIEND { $$ = "friend"; }
+ | EXPLICIT { $$ = "explicit"; }
+ | CONSTEXPR { $$ = "constexpr"; }
+ | EXPLICIT CONSTEXPR { $$ = "explicit constexpr"; }
+ | CONSTEXPR EXPLICIT { $$ = "explicit constexpr"; }
+ | STATIC CONSTEXPR { $$ = "static constexpr"; }
+ | CONSTEXPR STATIC { $$ = "static constexpr"; }
+ | THREAD_LOCAL { $$ = "thread_local"; }
+ | THREAD_LOCAL STATIC { $$ = "static thread_local"; }
+ | STATIC THREAD_LOCAL { $$ = "static thread_local"; }
+ | EXTERN THREAD_LOCAL { $$ = "extern thread_local"; }
+ | THREAD_LOCAL EXTERN { $$ = "extern thread_local"; }
+ | empty { $$ = 0; }
+ ;
+
+/* ------------------------------------------------------------------------------
+ Function parameter lists
+ ------------------------------------------------------------------------------ */
+
+parms : rawparms {
+ Parm *p;
+ $$ = $1;
+ p = $1;
+ while (p) {
+ Replace(Getattr(p,"type"),"typename ", "", DOH_REPLACE_ANY);
+ p = nextSibling(p);
+ }
+ }
+ ;
+
+rawparms : parm ptail {
+ set_nextSibling($1,$2);
+ $$ = $1;
+ }
+ | empty {
+ $$ = 0;
+ previousNode = currentNode;
+ currentNode=0;
+ }
+ ;
+
+ptail : COMMA parm ptail {
+ set_nextSibling($2,$3);
+ $$ = $2;
+ }
+ | COMMA DOXYGENPOSTSTRING parm ptail {
+ set_comment(previousNode, $2);
+ set_nextSibling($3, $4);
+ $$ = $3;
+ }
+ | empty { $$ = 0; }
+ ;
+
+
+parm_no_dox : rawtype parameter_declarator {
+ SwigType_push($1,$2.type);
+ $$ = NewParmWithoutFileLineInfo($1,$2.id);
+ previousNode = currentNode;
+ currentNode = $$;
+ Setfile($$,cparse_file);
+ Setline($$,cparse_line);
+ if ($2.defarg) {
+ Setattr($$,"value",$2.defarg);
+ }
+ }
+
+ | TEMPLATE LESSTHAN cpptype GREATERTHAN cpptype idcolon def_args {
+ $$ = NewParmWithoutFileLineInfo(NewStringf("template<class> %s %s", $5,$6), 0);
+ previousNode = currentNode;
+ currentNode = $$;
+ Setfile($$,cparse_file);
+ Setline($$,cparse_line);
+ if ($7.val) {
+ Setattr($$,"value",$7.val);
+ }
+ }
+ | ELLIPSIS {
+ SwigType *t = NewString("v(...)");
+ $$ = NewParmWithoutFileLineInfo(t, 0);
+ previousNode = currentNode;
+ currentNode = $$;
+ Setfile($$,cparse_file);
+ Setline($$,cparse_line);
+ }
+ ;
+
+parm : parm_no_dox {
+ $$ = $1;
+ }
+ | DOXYGENSTRING parm_no_dox {
+ $$ = $2;
+ set_comment($2, $1);
+ }
+ | parm_no_dox DOXYGENPOSTSTRING {
+ $$ = $1;
+ set_comment($1, $2);
+ }
+ ;
+
+valparms : rawvalparms {
+ Parm *p;
+ $$ = $1;
+ p = $1;
+ while (p) {
+ if (Getattr(p,"type")) {
+ Replace(Getattr(p,"type"),"typename ", "", DOH_REPLACE_ANY);
+ }
+ p = nextSibling(p);
+ }
+ }
+ ;
+
+rawvalparms : valparm valptail {
+ set_nextSibling($1,$2);
+ $$ = $1;
+ }
+ | empty { $$ = 0; }
+ ;
+
+valptail : COMMA valparm valptail {
+ set_nextSibling($2,$3);
+ $$ = $2;
+ }
+ | empty { $$ = 0; }
+ ;
+
+
+valparm : parm {
+ $$ = $1;
+ {
+ /* We need to make a possible adjustment for integer parameters. */
+ SwigType *type;
+ Node *n = 0;
+
+ while (!n) {
+ type = Getattr($1,"type");
+ n = Swig_symbol_clookup(type,0); /* See if we can find a node that matches the typename */
+ if ((n) && (Strcmp(nodeType(n),"cdecl") == 0)) {
+ SwigType *decl = Getattr(n,"decl");
+ if (!SwigType_isfunction(decl)) {
+ String *value = Getattr(n,"value");
+ if (value) {
+ String *v = Copy(value);
+ Setattr($1,"type",v);
+ Delete(v);
+ n = 0;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ }
+ | valexpr {
+ $$ = NewParmWithoutFileLineInfo(0,0);
+ Setfile($$,cparse_file);
+ Setline($$,cparse_line);
+ Setattr($$,"value",$1.val);
+ }
+ ;
+
+callparms : valexpr callptail {
+ $$ = $1;
+ Printf($$.val, "%s", $2.val);
+ }
+ | empty { $$.val = NewStringEmpty(); }
+ ;
+
+callptail : COMMA valexpr callptail {
+ $$.val = NewStringf(",%s%s", $2.val, $3.val);
+ $$.type = 0;
+ }
+ | empty { $$.val = NewStringEmpty(); }
+ ;
+
+def_args : EQUAL definetype {
+ $$ = $2;
+ if ($2.type == T_ERROR) {
+ Swig_warning(WARN_PARSE_BAD_DEFAULT,cparse_file, cparse_line, "Can't set default argument (ignored)\n");
+ $$.val = 0;
+ $$.rawval = 0;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ }
+ | EQUAL definetype LBRACKET expr RBRACKET {
+ $$ = $2;
+ if ($2.type == T_ERROR) {
+ Swig_warning(WARN_PARSE_BAD_DEFAULT,cparse_file, cparse_line, "Can't set default argument (ignored)\n");
+ $$ = $2;
+ $$.val = 0;
+ $$.rawval = 0;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ } else {
+ $$.val = NewStringf("%s[%s]",$2.val,$4.val);
+ }
+ }
+ | EQUAL LBRACE {
+ skip_balanced('{','}');
+ $$.val = NewString(scanner_ccode);
+ $$.rawval = 0;
+ $$.type = T_INT;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ | COLON expr {
+ $$.val = 0;
+ $$.rawval = 0;
+ $$.type = 0;
+ $$.bitfield = $2.val;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ | empty {
+ $$.val = 0;
+ $$.rawval = 0;
+ $$.type = T_INT;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ ;
+
+parameter_declarator : declarator def_args {
+ $$ = $1;
+ $$.defarg = $2.rawval ? $2.rawval : $2.val;
+ }
+ | abstract_declarator def_args {
+ $$ = $1;
+ $$.defarg = $2.rawval ? $2.rawval : $2.val;
+ }
+ | def_args {
+ $$.type = 0;
+ $$.id = 0;
+ $$.defarg = $1.rawval ? $1.rawval : $1.val;
+ }
+ /* Member function pointers with qualifiers. eg.
+ int f(short (Funcs::*parm)(bool) const); */
+ | direct_declarator LPAREN parms RPAREN cv_ref_qualifier {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_function(t,$3);
+ if ($5.qualifier)
+ SwigType_push(t, $5.qualifier);
+ if (!$$.have_parms) {
+ $$.parms = $3;
+ $$.have_parms = 1;
+ }
+ if (!$$.type) {
+ $$.type = t;
+ } else {
+ SwigType_push(t, $$.type);
+ Delete($$.type);
+ $$.type = t;
+ }
+ $$.defarg = 0;
+ }
+ ;
+
+plain_declarator : declarator {
+ $$ = $1;
+ if (SwigType_isfunction($1.type)) {
+ Delete(SwigType_pop_function($1.type));
+ } else if (SwigType_isarray($1.type)) {
+ SwigType *ta = SwigType_pop_arrays($1.type);
+ if (SwigType_isfunction($1.type)) {
+ Delete(SwigType_pop_function($1.type));
+ } else {
+ $$.parms = 0;
+ }
+ SwigType_push($1.type,ta);
+ Delete(ta);
+ } else {
+ $$.parms = 0;
+ }
+ }
+ | abstract_declarator {
+ $$ = $1;
+ if (SwigType_isfunction($1.type)) {
+ Delete(SwigType_pop_function($1.type));
+ } else if (SwigType_isarray($1.type)) {
+ SwigType *ta = SwigType_pop_arrays($1.type);
+ if (SwigType_isfunction($1.type)) {
+ Delete(SwigType_pop_function($1.type));
+ } else {
+ $$.parms = 0;
+ }
+ SwigType_push($1.type,ta);
+ Delete(ta);
+ } else {
+ $$.parms = 0;
+ }
+ }
+ /* Member function pointers with qualifiers. eg.
+ int f(short (Funcs::*parm)(bool) const) */
+ | direct_declarator LPAREN parms RPAREN cv_ref_qualifier {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_function(t, $3);
+ if ($5.qualifier)
+ SwigType_push(t, $5.qualifier);
+ if (!$$.have_parms) {
+ $$.parms = $3;
+ $$.have_parms = 1;
+ }
+ if (!$$.type) {
+ $$.type = t;
+ } else {
+ SwigType_push(t, $$.type);
+ Delete($$.type);
+ $$.type = t;
+ }
+ }
+ | empty {
+ $$.type = 0;
+ $$.id = 0;
+ $$.parms = 0;
+ }
+ ;
+
+declarator : pointer notso_direct_declarator {
+ $$ = $2;
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | pointer AND notso_direct_declarator {
+ $$ = $3;
+ SwigType_add_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | pointer LAND notso_direct_declarator {
+ $$ = $3;
+ SwigType_add_rvalue_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | direct_declarator {
+ $$ = $1;
+ if (!$$.type) $$.type = NewStringEmpty();
+ }
+ | AND notso_direct_declarator {
+ $$ = $2;
+ $$.type = NewStringEmpty();
+ SwigType_add_reference($$.type);
+ if ($2.type) {
+ SwigType_push($$.type,$2.type);
+ Delete($2.type);
+ }
+ }
+ | LAND notso_direct_declarator {
+ /* Introduced in C++11, move operator && */
+ /* Adds one S/R conflict */
+ $$ = $2;
+ $$.type = NewStringEmpty();
+ SwigType_add_rvalue_reference($$.type);
+ if ($2.type) {
+ SwigType_push($$.type,$2.type);
+ Delete($2.type);
+ }
+ }
+ | idcolon DSTAR notso_direct_declarator {
+ SwigType *t = NewStringEmpty();
+
+ $$ = $3;
+ SwigType_add_memberpointer(t,$1);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | pointer idcolon DSTAR notso_direct_declarator {
+ SwigType *t = NewStringEmpty();
+ $$ = $4;
+ SwigType_add_memberpointer(t,$2);
+ SwigType_push($1,t);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ Delete(t);
+ }
+ | pointer idcolon DSTAR AND notso_direct_declarator {
+ $$ = $5;
+ SwigType_add_memberpointer($1,$2);
+ SwigType_add_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | idcolon DSTAR AND notso_direct_declarator {
+ SwigType *t = NewStringEmpty();
+ $$ = $4;
+ SwigType_add_memberpointer(t,$1);
+ SwigType_add_reference(t);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+
+ /* Variadic versions eg. MyClasses&... myIds */
+
+ | pointer ELLIPSIS notso_direct_declarator {
+ $$ = $3;
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | pointer AND ELLIPSIS notso_direct_declarator {
+ $$ = $4;
+ SwigType_add_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | pointer LAND ELLIPSIS notso_direct_declarator {
+ $$ = $4;
+ SwigType_add_rvalue_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | ELLIPSIS direct_declarator {
+ $$ = $2;
+ if (!$$.type) $$.type = NewStringEmpty();
+ }
+ | AND ELLIPSIS notso_direct_declarator {
+ $$ = $3;
+ $$.type = NewStringEmpty();
+ SwigType_add_reference($$.type);
+ if ($3.type) {
+ SwigType_push($$.type,$3.type);
+ Delete($3.type);
+ }
+ }
+ | LAND ELLIPSIS notso_direct_declarator {
+ /* Introduced in C++11, move operator && */
+ /* Adds one S/R conflict */
+ $$ = $3;
+ $$.type = NewStringEmpty();
+ SwigType_add_rvalue_reference($$.type);
+ if ($3.type) {
+ SwigType_push($$.type,$3.type);
+ Delete($3.type);
+ }
+ }
+ | idcolon DSTAR ELLIPSIS notso_direct_declarator {
+ SwigType *t = NewStringEmpty();
+
+ $$ = $4;
+ SwigType_add_memberpointer(t,$1);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | pointer idcolon DSTAR ELLIPSIS notso_direct_declarator {
+ SwigType *t = NewStringEmpty();
+ $$ = $5;
+ SwigType_add_memberpointer(t,$2);
+ SwigType_push($1,t);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ Delete(t);
+ }
+ | pointer idcolon DSTAR AND ELLIPSIS notso_direct_declarator {
+ $$ = $6;
+ SwigType_add_memberpointer($1,$2);
+ SwigType_add_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | pointer idcolon DSTAR LAND ELLIPSIS notso_direct_declarator {
+ $$ = $6;
+ SwigType_add_memberpointer($1,$2);
+ SwigType_add_rvalue_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | idcolon DSTAR AND ELLIPSIS notso_direct_declarator {
+ SwigType *t = NewStringEmpty();
+ $$ = $5;
+ SwigType_add_memberpointer(t,$1);
+ SwigType_add_reference(t);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | idcolon DSTAR LAND ELLIPSIS notso_direct_declarator {
+ SwigType *t = NewStringEmpty();
+ $$ = $5;
+ SwigType_add_memberpointer(t,$1);
+ SwigType_add_rvalue_reference(t);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ ;
+
+notso_direct_declarator : idcolon {
+ /* Note: This is non-standard C. Template declarator is allowed to follow an identifier */
+ $$.id = Char($1);
+ $$.type = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+ | NOT idcolon {
+ $$.id = Char(NewStringf("~%s",$2));
+ $$.type = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+
+/* This generates a shift-reduce conflict with constructors */
+ | LPAREN idcolon RPAREN {
+ $$.id = Char($2);
+ $$.type = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+
+/*
+ | LPAREN AND idcolon RPAREN {
+ $$.id = Char($3);
+ $$.type = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+*/
+/* Technically, this should be LPAREN declarator RPAREN, but we get reduce/reduce conflicts */
+ | LPAREN pointer notso_direct_declarator RPAREN {
+ $$ = $3;
+ if ($$.type) {
+ SwigType_push($2,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $2;
+ }
+ | LPAREN idcolon DSTAR notso_direct_declarator RPAREN {
+ SwigType *t;
+ $$ = $4;
+ t = NewStringEmpty();
+ SwigType_add_memberpointer(t,$2);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | notso_direct_declarator LBRACKET RBRACKET {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_array(t,"");
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | notso_direct_declarator LBRACKET expr RBRACKET {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_array(t,$3.val);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | notso_direct_declarator LPAREN parms RPAREN {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_function(t,$3);
+ if (!$$.have_parms) {
+ $$.parms = $3;
+ $$.have_parms = 1;
+ }
+ if (!$$.type) {
+ $$.type = t;
+ } else {
+ SwigType_push(t, $$.type);
+ Delete($$.type);
+ $$.type = t;
+ }
+ }
+ ;
+
+direct_declarator : idcolon {
+ /* Note: This is non-standard C. Template declarator is allowed to follow an identifier */
+ $$.id = Char($1);
+ $$.type = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+
+ | NOT idcolon {
+ $$.id = Char(NewStringf("~%s",$2));
+ $$.type = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+
+/* This generate a shift-reduce conflict with constructors */
+/*
+ | LPAREN idcolon RPAREN {
+ $$.id = Char($2);
+ $$.type = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+*/
+/* Technically, this should be LPAREN declarator RPAREN, but we get reduce/reduce conflicts */
+ | LPAREN pointer direct_declarator RPAREN {
+ $$ = $3;
+ if ($$.type) {
+ SwigType_push($2,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $2;
+ }
+ | LPAREN AND direct_declarator RPAREN {
+ $$ = $3;
+ if (!$$.type) {
+ $$.type = NewStringEmpty();
+ }
+ SwigType_add_reference($$.type);
+ }
+ | LPAREN LAND direct_declarator RPAREN {
+ $$ = $3;
+ if (!$$.type) {
+ $$.type = NewStringEmpty();
+ }
+ SwigType_add_rvalue_reference($$.type);
+ }
+ | LPAREN idcolon DSTAR declarator RPAREN {
+ SwigType *t;
+ $$ = $4;
+ t = NewStringEmpty();
+ SwigType_add_memberpointer(t,$2);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | LPAREN idcolon DSTAR type_qualifier declarator RPAREN {
+ SwigType *t;
+ $$ = $5;
+ t = NewStringEmpty();
+ SwigType_add_memberpointer(t, $2);
+ SwigType_push(t, $4);
+ if ($$.type) {
+ SwigType_push(t, $$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | LPAREN idcolon DSTAR abstract_declarator RPAREN {
+ SwigType *t;
+ $$ = $4;
+ t = NewStringEmpty();
+ SwigType_add_memberpointer(t, $2);
+ if ($$.type) {
+ SwigType_push(t, $$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | LPAREN idcolon DSTAR type_qualifier abstract_declarator RPAREN {
+ SwigType *t;
+ $$ = $5;
+ t = NewStringEmpty();
+ SwigType_add_memberpointer(t, $2);
+ SwigType_push(t, $4);
+ if ($$.type) {
+ SwigType_push(t, $$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | direct_declarator LBRACKET RBRACKET {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_array(t,"");
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | direct_declarator LBRACKET expr RBRACKET {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_array(t,$3.val);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | direct_declarator LPAREN parms RPAREN {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_function(t,$3);
+ if (!$$.have_parms) {
+ $$.parms = $3;
+ $$.have_parms = 1;
+ }
+ if (!$$.type) {
+ $$.type = t;
+ } else {
+ SwigType_push(t, $$.type);
+ Delete($$.type);
+ $$.type = t;
+ }
+ }
+ /* User-defined string literals. eg.
+ int operator"" _mySuffix(const char* val, int length) {...} */
+ /* This produces one S/R conflict. */
+ | OPERATOR ID LPAREN parms RPAREN {
+ SwigType *t;
+ Append($1, " "); /* intervening space is mandatory */
+ Append($1, Char($2));
+ $$.id = Char($1);
+ t = NewStringEmpty();
+ SwigType_add_function(t,$4);
+ if (!$$.have_parms) {
+ $$.parms = $4;
+ $$.have_parms = 1;
+ }
+ if (!$$.type) {
+ $$.type = t;
+ } else {
+ SwigType_push(t, $$.type);
+ Delete($$.type);
+ $$.type = t;
+ }
+ }
+ ;
+
+abstract_declarator : pointer {
+ $$.type = $1;
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+ | pointer direct_abstract_declarator {
+ $$ = $2;
+ SwigType_push($1,$2.type);
+ $$.type = $1;
+ Delete($2.type);
+ }
+ | pointer AND {
+ $$.type = $1;
+ SwigType_add_reference($$.type);
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+ | pointer LAND {
+ $$.type = $1;
+ SwigType_add_rvalue_reference($$.type);
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+ | pointer AND direct_abstract_declarator {
+ $$ = $3;
+ SwigType_add_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | pointer LAND direct_abstract_declarator {
+ $$ = $3;
+ SwigType_add_rvalue_reference($1);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ | direct_abstract_declarator {
+ $$ = $1;
+ }
+ | AND direct_abstract_declarator {
+ $$ = $2;
+ $$.type = NewStringEmpty();
+ SwigType_add_reference($$.type);
+ if ($2.type) {
+ SwigType_push($$.type,$2.type);
+ Delete($2.type);
+ }
+ }
+ | LAND direct_abstract_declarator {
+ $$ = $2;
+ $$.type = NewStringEmpty();
+ SwigType_add_rvalue_reference($$.type);
+ if ($2.type) {
+ SwigType_push($$.type,$2.type);
+ Delete($2.type);
+ }
+ }
+ | AND {
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ $$.type = NewStringEmpty();
+ SwigType_add_reference($$.type);
+ }
+ | LAND {
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ $$.type = NewStringEmpty();
+ SwigType_add_rvalue_reference($$.type);
+ }
+ | idcolon DSTAR {
+ $$.type = NewStringEmpty();
+ SwigType_add_memberpointer($$.type,$1);
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+ | idcolon DSTAR type_qualifier {
+ $$.type = NewStringEmpty();
+ SwigType_add_memberpointer($$.type, $1);
+ SwigType_push($$.type, $3);
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ }
+ | pointer idcolon DSTAR {
+ SwigType *t = NewStringEmpty();
+ $$.type = $1;
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ SwigType_add_memberpointer(t,$2);
+ SwigType_push($$.type,t);
+ Delete(t);
+ }
+ | pointer idcolon DSTAR direct_abstract_declarator {
+ $$ = $4;
+ SwigType_add_memberpointer($1,$2);
+ if ($$.type) {
+ SwigType_push($1,$$.type);
+ Delete($$.type);
+ }
+ $$.type = $1;
+ }
+ ;
+
+direct_abstract_declarator : direct_abstract_declarator LBRACKET RBRACKET {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_array(t,"");
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | direct_abstract_declarator LBRACKET expr RBRACKET {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_array(t,$3.val);
+ if ($$.type) {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ }
+ $$.type = t;
+ }
+ | LBRACKET RBRACKET {
+ $$.type = NewStringEmpty();
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ SwigType_add_array($$.type,"");
+ }
+ | LBRACKET expr RBRACKET {
+ $$.type = NewStringEmpty();
+ $$.id = 0;
+ $$.parms = 0;
+ $$.have_parms = 0;
+ SwigType_add_array($$.type,$2.val);
+ }
+ | LPAREN abstract_declarator RPAREN {
+ $$ = $2;
+ }
+ | direct_abstract_declarator LPAREN parms RPAREN {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_function(t,$3);
+ if (!$$.type) {
+ $$.type = t;
+ } else {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ $$.type = t;
+ }
+ if (!$$.have_parms) {
+ $$.parms = $3;
+ $$.have_parms = 1;
+ }
+ }
+ | direct_abstract_declarator LPAREN parms RPAREN cv_ref_qualifier {
+ SwigType *t;
+ $$ = $1;
+ t = NewStringEmpty();
+ SwigType_add_function(t,$3);
+ SwigType_push(t, $5.qualifier);
+ if (!$$.type) {
+ $$.type = t;
+ } else {
+ SwigType_push(t,$$.type);
+ Delete($$.type);
+ $$.type = t;
+ }
+ if (!$$.have_parms) {
+ $$.parms = $3;
+ $$.have_parms = 1;
+ }
+ }
+ | LPAREN parms RPAREN {
+ $$.type = NewStringEmpty();
+ SwigType_add_function($$.type,$2);
+ $$.parms = $2;
+ $$.have_parms = 1;
+ $$.id = 0;
+ }
+ ;
+
+
+pointer : STAR type_qualifier pointer {
+ $$ = NewStringEmpty();
+ SwigType_add_pointer($$);
+ SwigType_push($$,$2);
+ SwigType_push($$,$3);
+ Delete($3);
+ }
+ | STAR pointer {
+ $$ = NewStringEmpty();
+ SwigType_add_pointer($$);
+ SwigType_push($$,$2);
+ Delete($2);
+ }
+ | STAR type_qualifier {
+ $$ = NewStringEmpty();
+ SwigType_add_pointer($$);
+ SwigType_push($$,$2);
+ }
+ | STAR {
+ $$ = NewStringEmpty();
+ SwigType_add_pointer($$);
+ }
+ ;
+
+/* cv-qualifier plus C++11 ref-qualifier for non-static member functions */
+cv_ref_qualifier : type_qualifier {
+ $$.qualifier = $1;
+ $$.refqualifier = 0;
+ }
+ | type_qualifier ref_qualifier {
+ $$.qualifier = $1;
+ $$.refqualifier = $2;
+ SwigType_push($$.qualifier, $2);
+ }
+ | ref_qualifier {
+ $$.qualifier = NewStringEmpty();
+ $$.refqualifier = $1;
+ SwigType_push($$.qualifier, $1);
+ }
+ ;
+
+ref_qualifier : AND {
+ $$ = NewStringEmpty();
+ SwigType_add_reference($$);
+ }
+ | LAND {
+ $$ = NewStringEmpty();
+ SwigType_add_rvalue_reference($$);
+ }
+ ;
+
+type_qualifier : type_qualifier_raw {
+ $$ = NewStringEmpty();
+ if ($1) SwigType_add_qualifier($$,$1);
+ }
+ | type_qualifier_raw type_qualifier {
+ $$ = $2;
+ if ($1) SwigType_add_qualifier($$,$1);
+ }
+ ;
+
+type_qualifier_raw : CONST_QUAL { $$ = "const"; }
+ | VOLATILE { $$ = "volatile"; }
+ | REGISTER { $$ = 0; }
+ ;
+
+/* Data type must be a built in type or an identifier for user-defined types
+ This type can be preceded by a modifier. */
+
+type : rawtype {
+ $$ = $1;
+ Replace($$,"typename ","", DOH_REPLACE_ANY);
+ }
+ ;
+
+rawtype : type_qualifier type_right {
+ $$ = $2;
+ SwigType_push($$,$1);
+ }
+ | type_right { $$ = $1; }
+ | type_right type_qualifier {
+ $$ = $1;
+ SwigType_push($$,$2);
+ }
+ | type_qualifier type_right type_qualifier {
+ $$ = $2;
+ SwigType_push($$,$3);
+ SwigType_push($$,$1);
+ }
+ ;
+
+type_right : primitive_type { $$ = $1;
+ /* Printf(stdout,"primitive = '%s'\n", $$);*/
+ }
+ | TYPE_BOOL { $$ = $1; }
+ | TYPE_VOID { $$ = $1; }
+/*
+ | TYPE_TYPEDEF template_decl { $$ = NewStringf("%s%s",$1,$2); }
+*/
+ | c_enum_key idcolon { $$ = NewStringf("enum %s", $2); }
+ | TYPE_RAW { $$ = $1; }
+
+ | idcolon {
+ $$ = $1;
+ }
+ | cpptype idcolon {
+ $$ = NewStringf("%s %s", $1, $2);
+ }
+ | decltype {
+ $$ = $1;
+ }
+ ;
+
+decltype : DECLTYPE LPAREN idcolon RPAREN {
+ Node *n = Swig_symbol_clookup($3,0);
+ if (!n) {
+ Swig_error(cparse_file, cparse_line, "Identifier %s not defined.\n", $3);
+ $$ = $3;
+ } else {
+ $$ = Getattr(n, "type");
+ }
+ }
+ ;
+
+primitive_type : primitive_type_list {
+ if (!$1.type) $1.type = NewString("int");
+ if ($1.us) {
+ $$ = NewStringf("%s %s", $1.us, $1.type);
+ Delete($1.us);
+ Delete($1.type);
+ } else {
+ $$ = $1.type;
+ }
+ if (Cmp($$,"signed int") == 0) {
+ Delete($$);
+ $$ = NewString("int");
+ } else if (Cmp($$,"signed long") == 0) {
+ Delete($$);
+ $$ = NewString("long");
+ } else if (Cmp($$,"signed short") == 0) {
+ Delete($$);
+ $$ = NewString("short");
+ } else if (Cmp($$,"signed long long") == 0) {
+ Delete($$);
+ $$ = NewString("long long");
+ }
+ }
+ ;
+
+primitive_type_list : type_specifier {
+ $$ = $1;
+ }
+ | type_specifier primitive_type_list {
+ if ($1.us && $2.us) {
+ Swig_error(cparse_file, cparse_line, "Extra %s specifier.\n", $2.us);
+ }
+ $$ = $2;
+ if ($1.us) $$.us = $1.us;
+ if ($1.type) {
+ if (!$2.type) $$.type = $1.type;
+ else {
+ int err = 0;
+ if ((Cmp($1.type,"long") == 0)) {
+ if ((Cmp($2.type,"long") == 0) || (Strncmp($2.type,"double",6) == 0)) {
+ $$.type = NewStringf("long %s", $2.type);
+ } else if (Cmp($2.type,"int") == 0) {
+ $$.type = $1.type;
+ } else {
+ err = 1;
+ }
+ } else if ((Cmp($1.type,"short")) == 0) {
+ if (Cmp($2.type,"int") == 0) {
+ $$.type = $1.type;
+ } else {
+ err = 1;
+ }
+ } else if (Cmp($1.type,"int") == 0) {
+ $$.type = $2.type;
+ } else if (Cmp($1.type,"double") == 0) {
+ if (Cmp($2.type,"long") == 0) {
+ $$.type = NewString("long double");
+ } else if (Cmp($2.type,"_Complex") == 0) {
+ $$.type = NewString("double _Complex");
+ } else {
+ err = 1;
+ }
+ } else if (Cmp($1.type,"float") == 0) {
+ if (Cmp($2.type,"_Complex") == 0) {
+ $$.type = NewString("float _Complex");
+ } else {
+ err = 1;
+ }
+ } else if (Cmp($1.type,"_Complex") == 0) {
+ $$.type = NewStringf("%s _Complex", $2.type);
+ } else {
+ err = 1;
+ }
+ if (err) {
+ Swig_error(cparse_file, cparse_line, "Extra %s specifier.\n", $1.type);
+ }
+ }
+ }
+ }
+ ;
+
+
+type_specifier : TYPE_INT {
+ $$.type = NewString("int");
+ $$.us = 0;
+ }
+ | TYPE_SHORT {
+ $$.type = NewString("short");
+ $$.us = 0;
+ }
+ | TYPE_LONG {
+ $$.type = NewString("long");
+ $$.us = 0;
+ }
+ | TYPE_CHAR {
+ $$.type = NewString("char");
+ $$.us = 0;
+ }
+ | TYPE_WCHAR {
+ $$.type = NewString("wchar_t");
+ $$.us = 0;
+ }
+ | TYPE_FLOAT {
+ $$.type = NewString("float");
+ $$.us = 0;
+ }
+ | TYPE_DOUBLE {
+ $$.type = NewString("double");
+ $$.us = 0;
+ }
+ | TYPE_SIGNED {
+ $$.us = NewString("signed");
+ $$.type = 0;
+ }
+ | TYPE_UNSIGNED {
+ $$.us = NewString("unsigned");
+ $$.type = 0;
+ }
+ | TYPE_COMPLEX {
+ $$.type = NewString("_Complex");
+ $$.us = 0;
+ }
+ | TYPE_NON_ISO_INT8 {
+ $$.type = NewString("__int8");
+ $$.us = 0;
+ }
+ | TYPE_NON_ISO_INT16 {
+ $$.type = NewString("__int16");
+ $$.us = 0;
+ }
+ | TYPE_NON_ISO_INT32 {
+ $$.type = NewString("__int32");
+ $$.us = 0;
+ }
+ | TYPE_NON_ISO_INT64 {
+ $$.type = NewString("__int64");
+ $$.us = 0;
+ }
+ ;
+
+definetype : { /* scanner_check_typedef(); */ } expr {
+ $$ = $2;
+ if ($$.type == T_STRING) {
+ $$.rawval = NewStringf("\"%(escape)s\"",$$.val);
+ } else if ($$.type != T_CHAR && $$.type != T_WSTRING && $$.type != T_WCHAR) {
+ $$.rawval = NewStringf("%s", $$.val);
+ }
+ $$.qualifier = 0;
+ $$.refqualifier = 0;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ scanner_ignore_typedef();
+ }
+ | default_delete {
+ $$ = $1;
+ }
+ ;
+
+default_delete : deleted_definition {
+ $$ = $1;
+ }
+ | explicit_default {
+ $$ = $1;
+ }
+ ;
+
+/* For C++ deleted definition '= delete' */
+deleted_definition : DELETE_KW {
+ $$.val = NewString("delete");
+ $$.rawval = 0;
+ $$.type = T_STRING;
+ $$.qualifier = 0;
+ $$.refqualifier = 0;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ ;
+
+/* For C++ explicitly defaulted functions '= default' */
+explicit_default : DEFAULT {
+ $$.val = NewString("default");
+ $$.rawval = 0;
+ $$.type = T_STRING;
+ $$.qualifier = 0;
+ $$.refqualifier = 0;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ ;
+
+/* Some stuff for handling enums */
+
+ename : identifier { $$ = $1; }
+ | empty { $$ = (char *) 0;}
+ ;
+
+constant_directives : constant_directive
+ | constant_directive constant_directives
+ ;
+
+optional_ignored_defines
+ : constant_directives
+ | empty
+ ;
+
+/* Enum lists - any #define macros (constant directives) within the enum list are ignored. Trailing commas accepted. */
+
+/*
+ Note that "_last" attribute is not supposed to be set on the last enum element, as might be expected from its name, but on the _first_ one, and _only_ on it,
+ so we propagate it back to the first item while parsing and reset it on all the subsequent ones.
+ */
+
+enumlist : enumlist_item {
+ Setattr($1,"_last",$1);
+ $$ = $1;
+ }
+ | enumlist_item DOXYGENPOSTSTRING {
+ Setattr($1,"_last",$1);
+ set_comment($1, $2);
+ $$ = $1;
+ }
+ | enumlist_item COMMA enumlist {
+ if ($3) {
+ set_nextSibling($1, $3);
+ Setattr($1,"_last",Getattr($3,"_last"));
+ Setattr($3,"_last",NULL);
+ } else {
+ Setattr($1,"_last",$1);
+ }
+ $$ = $1;
+ }
+ | enumlist_item COMMA DOXYGENPOSTSTRING enumlist {
+ if ($4) {
+ set_nextSibling($1, $4);
+ Setattr($1,"_last",Getattr($4,"_last"));
+ Setattr($4,"_last",NULL);
+ } else {
+ Setattr($1,"_last",$1);
+ }
+ set_comment($1, $3);
+ $$ = $1;
+ }
+ | optional_ignored_defines {
+ $$ = 0;
+ }
+ ;
+
+enumlist_item : optional_ignored_defines edecl_with_dox optional_ignored_defines {
+ $$ = $2;
+ }
+ ;
+
+edecl_with_dox : edecl {
+ $$ = $1;
+ }
+ | DOXYGENSTRING edecl {
+ $$ = $2;
+ set_comment($2, $1);
+ }
+ ;
+
+edecl : identifier {
+ SwigType *type = NewSwigType(T_INT);
+ $$ = new_node("enumitem");
+ Setattr($$,"name",$1);
+ Setattr($$,"type",type);
+ SetFlag($$,"feature:immutable");
+ Delete(type);
+ }
+ | identifier EQUAL etype {
+ SwigType *type = NewSwigType($3.type == T_BOOL ? T_BOOL : ($3.type == T_CHAR ? T_CHAR : T_INT));
+ $$ = new_node("enumitem");
+ Setattr($$,"name",$1);
+ Setattr($$,"type",type);
+ SetFlag($$,"feature:immutable");
+ Setattr($$,"enumvalue", $3.val);
+ Setattr($$,"value",$1);
+ Delete(type);
+ }
+ ;
+
+etype : expr {
+ $$ = $1;
+ if (($$.type != T_INT) && ($$.type != T_UINT) &&
+ ($$.type != T_LONG) && ($$.type != T_ULONG) &&
+ ($$.type != T_LONGLONG) && ($$.type != T_ULONGLONG) &&
+ ($$.type != T_SHORT) && ($$.type != T_USHORT) &&
+ ($$.type != T_SCHAR) && ($$.type != T_UCHAR) &&
+ ($$.type != T_CHAR) && ($$.type != T_BOOL)) {
+ Swig_error(cparse_file,cparse_line,"Type error. Expecting an integral type\n");
+ }
+ }
+ ;
+
+/* Arithmetic expressions. Used for constants, C++ templates, and other cool stuff. */
+
+expr : valexpr { $$ = $1; }
+ | type {
+ Node *n;
+ $$.val = $1;
+ $$.type = T_INT;
+ /* Check if value is in scope */
+ n = Swig_symbol_clookup($1,0);
+ if (n) {
+ /* A band-aid for enum values used in expressions. */
+ if (Strcmp(nodeType(n),"enumitem") == 0) {
+ String *q = Swig_symbol_qualified(n);
+ if (q) {
+ $$.val = NewStringf("%s::%s", q, Getattr(n,"name"));
+ Delete(q);
+ }
+ }
+ }
+ }
+ ;
+
+/* simple member access expressions */
+exprmem : ID ARROW ID {
+ $$.val = NewStringf("%s->%s", $1, $3);
+ $$.type = 0;
+ }
+ | ID ARROW ID LPAREN callparms RPAREN {
+ $$.val = NewStringf("%s->%s(%s)", $1, $3, $5.val);
+ $$.type = 0;
+ }
+ | exprmem ARROW ID {
+ $$ = $1;
+ Printf($$.val, "->%s", $3);
+ }
+ | exprmem ARROW ID LPAREN callparms RPAREN {
+ $$ = $1;
+ Printf($$.val, "->%s(%s)", $3, $5.val);
+ }
+ | ID PERIOD ID {
+ $$.val = NewStringf("%s.%s", $1, $3);
+ $$.type = 0;
+ }
+ | ID PERIOD ID LPAREN callparms RPAREN {
+ $$.val = NewStringf("%s.%s(%s)", $1, $3, $5.val);
+ $$.type = 0;
+ }
+ | exprmem PERIOD ID {
+ $$ = $1;
+ Printf($$.val, ".%s", $3);
+ }
+ | exprmem PERIOD ID LPAREN callparms RPAREN {
+ $$ = $1;
+ Printf($$.val, ".%s(%s)", $3, $5.val);
+ }
+ ;
+
+/* Non-compound expression */
+exprsimple : exprnum {
+ $$ = $1;
+ }
+ | exprmem {
+ $$ = $1;
+ }
+ | string {
+ $$.val = $1;
+ $$.type = T_STRING;
+ }
+ | SIZEOF LPAREN type parameter_declarator RPAREN {
+ SwigType_push($3,$4.type);
+ $$.val = NewStringf("sizeof(%s)",SwigType_str($3,0));
+ $$.type = T_ULONG;
+ }
+ | SIZEOF ELLIPSIS LPAREN type parameter_declarator RPAREN {
+ SwigType_push($4,$5.type);
+ $$.val = NewStringf("sizeof...(%s)",SwigType_str($4,0));
+ $$.type = T_ULONG;
+ }
+ /* We don't support all valid expressions here currently - e.g.
+ * sizeof(<unaryop> x) doesn't work - but those are unlikely to
+ * be seen in real code.
+ *
+ * Note: sizeof(x) is not handled here, but instead by the rule
+ * for sizeof(<type>) because it matches that syntactically.
+ */
+ | SIZEOF LPAREN exprsimple RPAREN {
+ $$.val = NewStringf("sizeof(%s)", $3.val);
+ $$.type = T_ULONG;
+ }
+ /* `sizeof expr` without parentheses is valid for an expression,
+ * but not for a type. This doesn't support `sizeof x` in
+ * addition to the case not supported above.
+ */
+ | SIZEOF exprsimple {
+ $$.val = NewStringf("sizeof(%s)", $2.val);
+ $$.type = T_ULONG;
+ }
+ | wstring {
+ $$.val = $1;
+ $$.rawval = NewStringf("L\"%s\"", $$.val);
+ $$.type = T_WSTRING;
+ }
+ | CHARCONST {
+ $$.val = NewString($1);
+ if (Len($$.val)) {
+ $$.rawval = NewStringf("'%(escape)s'", $$.val);
+ } else {
+ $$.rawval = NewString("'\\0'");
+ }
+ $$.type = T_CHAR;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ | WCHARCONST {
+ $$.val = NewString($1);
+ if (Len($$.val)) {
+ $$.rawval = NewStringf("L\'%s\'", $$.val);
+ } else {
+ $$.rawval = NewString("L'\\0'");
+ }
+ $$.type = T_WCHAR;
+ $$.bitfield = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+
+ ;
+
+valexpr : exprsimple { $$ = $1; }
+ | exprcompound { $$ = $1; }
+
+/* grouping */
+ | LPAREN expr RPAREN %prec CAST {
+ $$.val = NewStringf("(%s)",$2.val);
+ if ($2.rawval) {
+ $$.rawval = NewStringf("(%s)",$2.rawval);
+ }
+ $$.type = $2.type;
+ }
+
+/* A few common casting operations */
+
+ | LPAREN expr RPAREN expr %prec CAST {
+ $$ = $4;
+ if ($4.type != T_STRING) {
+ switch ($2.type) {
+ case T_FLOAT:
+ case T_DOUBLE:
+ case T_LONGDOUBLE:
+ case T_FLTCPLX:
+ case T_DBLCPLX:
+ $$.val = NewStringf("(%s)%s", $2.val, $4.val); /* SwigType_str and decimal points don't mix! */
+ break;
+ default:
+ $$.val = NewStringf("(%s) %s", SwigType_str($2.val,0), $4.val);
+ break;
+ }
+ }
+ $$.type = promote($2.type, $4.type);
+ }
+ | LPAREN expr pointer RPAREN expr %prec CAST {
+ $$ = $5;
+ if ($5.type != T_STRING) {
+ SwigType_push($2.val,$3);
+ $$.val = NewStringf("(%s) %s", SwigType_str($2.val,0), $5.val);
+ }
+ }
+ | LPAREN expr AND RPAREN expr %prec CAST {
+ $$ = $5;
+ if ($5.type != T_STRING) {
+ SwigType_add_reference($2.val);
+ $$.val = NewStringf("(%s) %s", SwigType_str($2.val,0), $5.val);
+ }
+ }
+ | LPAREN expr LAND RPAREN expr %prec CAST {
+ $$ = $5;
+ if ($5.type != T_STRING) {
+ SwigType_add_rvalue_reference($2.val);
+ $$.val = NewStringf("(%s) %s", SwigType_str($2.val,0), $5.val);
+ }
+ }
+ | LPAREN expr pointer AND RPAREN expr %prec CAST {
+ $$ = $6;
+ if ($6.type != T_STRING) {
+ SwigType_push($2.val,$3);
+ SwigType_add_reference($2.val);
+ $$.val = NewStringf("(%s) %s", SwigType_str($2.val,0), $6.val);
+ }
+ }
+ | LPAREN expr pointer LAND RPAREN expr %prec CAST {
+ $$ = $6;
+ if ($6.type != T_STRING) {
+ SwigType_push($2.val,$3);
+ SwigType_add_rvalue_reference($2.val);
+ $$.val = NewStringf("(%s) %s", SwigType_str($2.val,0), $6.val);
+ }
+ }
+ | AND expr {
+ $$ = $2;
+ $$.val = NewStringf("&%s",$2.val);
+ }
+ | STAR expr {
+ $$ = $2;
+ $$.val = NewStringf("*%s",$2.val);
+ }
+ ;
+
+exprnum : NUM_INT { $$ = $1; }
+ | NUM_FLOAT { $$ = $1; }
+ | NUM_UNSIGNED { $$ = $1; }
+ | NUM_LONG { $$ = $1; }
+ | NUM_ULONG { $$ = $1; }
+ | NUM_LONGLONG { $$ = $1; }
+ | NUM_ULONGLONG { $$ = $1; }
+ | NUM_BOOL { $$ = $1; }
+ ;
+
+exprcompound : expr PLUS expr {
+ $$.val = NewStringf("%s+%s", COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote($1.type,$3.type);
+ }
+ | expr MINUS expr {
+ $$.val = NewStringf("%s-%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote($1.type,$3.type);
+ }
+ | expr STAR expr {
+ $$.val = NewStringf("%s*%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote($1.type,$3.type);
+ }
+ | expr SLASH expr {
+ $$.val = NewStringf("%s/%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote($1.type,$3.type);
+ }
+ | expr MODULO expr {
+ $$.val = NewStringf("%s%%%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote($1.type,$3.type);
+ }
+ | expr AND expr {
+ $$.val = NewStringf("%s&%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote($1.type,$3.type);
+ }
+ | expr OR expr {
+ $$.val = NewStringf("%s|%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote($1.type,$3.type);
+ }
+ | expr XOR expr {
+ $$.val = NewStringf("%s^%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote($1.type,$3.type);
+ }
+ | expr LSHIFT expr {
+ $$.val = NewStringf("%s << %s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote_type($1.type);
+ }
+ | expr RSHIFT expr {
+ $$.val = NewStringf("%s >> %s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = promote_type($1.type);
+ }
+ | expr LAND expr {
+ $$.val = NewStringf("%s&&%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr LOR expr {
+ $$.val = NewStringf("%s||%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr EQUALTO expr {
+ $$.val = NewStringf("%s==%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr NOTEQUALTO expr {
+ $$.val = NewStringf("%s!=%s",COMPOUND_EXPR_VAL($1),COMPOUND_EXPR_VAL($3));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ /* Trying to parse `>` in the general case results in conflicts
+ * in the parser, but all user-reported cases are actually inside
+ * parentheses and we can handle that case.
+ */
+ | LPAREN expr GREATERTHAN expr RPAREN {
+ $$.val = NewStringf("%s > %s", COMPOUND_EXPR_VAL($2), COMPOUND_EXPR_VAL($4));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+
+ /* Similarly for `<` except trying to handle exprcompound on the
+ * left side gives a shift/reduce conflict, so also restrict
+ * handling to non-compound subexpressions there. Again this
+ * covers all user-reported cases.
+ */
+ | LPAREN exprsimple LESSTHAN expr RPAREN {
+ $$.val = NewStringf("%s < %s", COMPOUND_EXPR_VAL($2), COMPOUND_EXPR_VAL($4));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr GREATERTHANOREQUALTO expr {
+ $$.val = NewStringf("%s >= %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr LESSTHANOREQUALTO expr {
+ $$.val = NewStringf("%s <= %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3));
+ $$.type = cparse_cplusplus ? T_BOOL : T_INT;
+ }
+ | expr LESSEQUALGREATER expr {
+ $$.val = NewStringf("%s <=> %s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3));
+ /* Really `<=>` returns one of `std::strong_ordering`,
+ * `std::partial_ordering` or `std::weak_ordering`, but we
+ * fake it by treating the return value as `int`. The main
+ * thing to do with the return value in this context is to
+ * compare it with 0, for which `int` does the job. */
+ $$.type = T_INT;
+ }
+ | expr QUESTIONMARK expr COLON expr %prec QUESTIONMARK {
+ $$.val = NewStringf("%s?%s:%s", COMPOUND_EXPR_VAL($1), COMPOUND_EXPR_VAL($3), COMPOUND_EXPR_VAL($5));
+ /* This may not be exactly right, but is probably good enough
+ * for the purposes of parsing constant expressions. */
+ $$.type = promote($3.type, $5.type);
+ }
+ | MINUS expr %prec UMINUS {
+ $$.val = NewStringf("-%s",$2.val);
+ $$.type = $2.type;
+ }
+ | PLUS expr %prec UMINUS {
+ $$.val = NewStringf("+%s",$2.val);
+ $$.type = $2.type;
+ }
+ | NOT expr {
+ $$.val = NewStringf("~%s",$2.val);
+ $$.type = $2.type;
+ }
+ | LNOT expr {
+ $$.val = NewStringf("!%s",COMPOUND_EXPR_VAL($2));
+ $$.type = T_INT;
+ }
+ | type LPAREN {
+ String *qty;
+ skip_balanced('(',')');
+ qty = Swig_symbol_type_qualify($1,0);
+ if (SwigType_istemplate(qty)) {
+ String *nstr = SwigType_namestr(qty);
+ Delete(qty);
+ qty = nstr;
+ }
+ $$.val = NewStringf("%s%s",qty,scanner_ccode);
+ Clear(scanner_ccode);
+ $$.type = T_INT;
+ Delete(qty);
+ }
+ ;
+
+variadic : ELLIPSIS {
+ $$ = NewString("...");
+ }
+ | empty {
+ $$ = 0;
+ }
+ ;
+
+inherit : raw_inherit {
+ $$ = $1;
+ }
+ ;
+
+raw_inherit : COLON { inherit_list = 1; } base_list { $$ = $3; inherit_list = 0; }
+ | empty { $$ = 0; }
+ ;
+
+base_list : base_specifier {
+ Hash *list = NewHash();
+ Node *base = $1;
+ Node *name = Getattr(base,"name");
+ List *lpublic = NewList();
+ List *lprotected = NewList();
+ List *lprivate = NewList();
+ Setattr(list,"public",lpublic);
+ Setattr(list,"protected",lprotected);
+ Setattr(list,"private",lprivate);
+ Delete(lpublic);
+ Delete(lprotected);
+ Delete(lprivate);
+ Append(Getattr(list,Getattr(base,"access")),name);
+ $$ = list;
+ }
+
+ | base_list COMMA base_specifier {
+ Hash *list = $1;
+ Node *base = $3;
+ Node *name = Getattr(base,"name");
+ Append(Getattr(list,Getattr(base,"access")),name);
+ $$ = list;
+ }
+ ;
+
+base_specifier : opt_virtual {
+ $<intvalue>$ = cparse_line;
+ } idcolon variadic {
+ $$ = NewHash();
+ Setfile($$,cparse_file);
+ Setline($$,$<intvalue>2);
+ Setattr($$,"name",$3);
+ Setfile($3,cparse_file);
+ Setline($3,$<intvalue>2);
+ if (last_cpptype && (Strcmp(last_cpptype,"struct") != 0)) {
+ Setattr($$,"access","private");
+ Swig_warning(WARN_PARSE_NO_ACCESS, Getfile($$), Getline($$), "No access specifier given for base class '%s' (ignored).\n", SwigType_namestr($3));
+ } else {
+ Setattr($$,"access","public");
+ }
+ if ($4)
+ SetFlag($$, "variadic");
+ }
+ | opt_virtual access_specifier {
+ $<intvalue>$ = cparse_line;
+ } opt_virtual idcolon variadic {
+ $$ = NewHash();
+ Setfile($$,cparse_file);
+ Setline($$,$<intvalue>3);
+ Setattr($$,"name",$5);
+ Setfile($5,cparse_file);
+ Setline($5,$<intvalue>3);
+ Setattr($$,"access",$2);
+ if (Strcmp($2,"public") != 0) {
+ Swig_warning(WARN_PARSE_PRIVATE_INHERIT, Getfile($$), Getline($$), "%s inheritance from base '%s' (ignored).\n", $2, SwigType_namestr($5));
+ }
+ if ($6)
+ SetFlag($$, "variadic");
+ }
+ ;
+
+access_specifier : PUBLIC { $$ = (char*)"public"; }
+ | PRIVATE { $$ = (char*)"private"; }
+ | PROTECTED { $$ = (char*)"protected"; }
+ ;
+
+templcpptype : CLASS {
+ $$ = (char*)"class";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ | TYPENAME {
+ $$ = (char *)"typename";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ | CLASS ELLIPSIS {
+ $$ = (char *)"class...";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ | TYPENAME ELLIPSIS {
+ $$ = (char *)"typename...";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ ;
+
+cpptype : templcpptype {
+ $$ = $1;
+ }
+ | STRUCT {
+ $$ = (char*)"struct";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ | UNION {
+ $$ = (char*)"union";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ ;
+
+classkey : CLASS {
+ $$ = (char*)"class";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ | STRUCT {
+ $$ = (char*)"struct";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ | UNION {
+ $$ = (char*)"union";
+ if (!inherit_list) last_cpptype = $$;
+ }
+ ;
+
+classkeyopt : classkey {
+ $$ = $1;
+ }
+ | empty {
+ $$ = 0;
+ }
+ ;
+
+opt_virtual : VIRTUAL
+ | empty
+ ;
+
+virt_specifier_seq : OVERRIDE {
+ $$ = 0;
+ }
+ | FINAL {
+ $$ = NewString("1");
+ }
+ | FINAL OVERRIDE {
+ $$ = NewString("1");
+ }
+ | OVERRIDE FINAL {
+ $$ = NewString("1");
+ }
+ ;
+
+virt_specifier_seq_opt : virt_specifier_seq {
+ $$ = $1;
+ }
+ | empty {
+ $$ = 0;
+ }
+ ;
+
+class_virt_specifier_opt : FINAL {
+ $$ = NewString("1");
+ }
+ | empty {
+ $$ = 0;
+ }
+ ;
+
+exception_specification : THROW LPAREN parms RPAREN {
+ $$.throws = $3;
+ $$.throwf = NewString("1");
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ | NOEXCEPT {
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = NewString("true");
+ $$.final = 0;
+ }
+ | virt_specifier_seq {
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = $1;
+ }
+ | THROW LPAREN parms RPAREN virt_specifier_seq {
+ $$.throws = $3;
+ $$.throwf = NewString("1");
+ $$.nexcept = 0;
+ $$.final = $5;
+ }
+ | NOEXCEPT virt_specifier_seq {
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = NewString("true");
+ $$.final = $2;
+ }
+ | NOEXCEPT LPAREN expr RPAREN {
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = $3.val;
+ $$.final = 0;
+ }
+ ;
+
+qualifiers_exception_specification : cv_ref_qualifier {
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ $$.qualifier = $1.qualifier;
+ $$.refqualifier = $1.refqualifier;
+ }
+ | exception_specification {
+ $$ = $1;
+ $$.qualifier = 0;
+ $$.refqualifier = 0;
+ }
+ | cv_ref_qualifier exception_specification {
+ $$ = $2;
+ $$.qualifier = $1.qualifier;
+ $$.refqualifier = $1.refqualifier;
+ }
+ ;
+
+cpp_const : qualifiers_exception_specification {
+ $$ = $1;
+ }
+ | empty {
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ $$.qualifier = 0;
+ $$.refqualifier = 0;
+ }
+ ;
+
+ctor_end : cpp_const ctor_initializer SEMI {
+ Clear(scanner_ccode);
+ $$.have_parms = 0;
+ $$.defarg = 0;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ if ($1.qualifier)
+ Swig_error(cparse_file, cparse_line, "Constructor cannot have a qualifier.\n");
+ }
+ | cpp_const ctor_initializer LBRACE {
+ skip_balanced('{','}');
+ $$.have_parms = 0;
+ $$.defarg = 0;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ if ($1.qualifier)
+ Swig_error(cparse_file, cparse_line, "Constructor cannot have a qualifier.\n");
+ }
+ | LPAREN parms RPAREN SEMI {
+ Clear(scanner_ccode);
+ $$.parms = $2;
+ $$.have_parms = 1;
+ $$.defarg = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ | LPAREN parms RPAREN LBRACE {
+ skip_balanced('{','}');
+ $$.parms = $2;
+ $$.have_parms = 1;
+ $$.defarg = 0;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ | EQUAL definetype SEMI {
+ $$.have_parms = 0;
+ $$.defarg = $2.val;
+ $$.throws = 0;
+ $$.throwf = 0;
+ $$.nexcept = 0;
+ $$.final = 0;
+ }
+ | exception_specification EQUAL default_delete SEMI {
+ $$.have_parms = 0;
+ $$.defarg = $3.val;
+ $$.throws = $1.throws;
+ $$.throwf = $1.throwf;
+ $$.nexcept = $1.nexcept;
+ $$.final = $1.final;
+ if ($1.qualifier)
+ Swig_error(cparse_file, cparse_line, "Constructor cannot have a qualifier.\n");
+ }
+ ;
+
+ctor_initializer : COLON mem_initializer_list
+ | empty
+ ;
+
+mem_initializer_list : mem_initializer
+ | mem_initializer_list COMMA mem_initializer
+ | mem_initializer ELLIPSIS
+ | mem_initializer_list COMMA mem_initializer ELLIPSIS
+ ;
+
+mem_initializer : idcolon LPAREN {
+ skip_balanced('(',')');
+ Clear(scanner_ccode);
+ }
+ /* Uniform initialization in C++11.
+ Example:
+ struct MyStruct {
+ MyStruct(int x, double y) : x_{x}, y_{y} {}
+ int x_;
+ double y_;
+ };
+ */
+ | idcolon LBRACE {
+ skip_balanced('{','}');
+ Clear(scanner_ccode);
+ }
+ ;
+
+less_valparms_greater : LESSTHAN valparms GREATERTHAN {
+ String *s = NewStringEmpty();
+ SwigType_add_template(s,$2);
+ $$ = Char(s);
+ scanner_last_id(1);
+ }
+ ;
+
+/* Identifiers including the C++11 identifiers with special meaning */
+identifier : ID { $$ = $1; }
+ | OVERRIDE { $$ = Swig_copy_string("override"); }
+ | FINAL { $$ = Swig_copy_string("final"); }
+ ;
+
+idstring : identifier { $$ = $1; }
+ | default_delete { $$ = Char($1.val); }
+ | string { $$ = Char($1); }
+ ;
+
+idstringopt : idstring { $$ = $1; }
+ | empty { $$ = 0; }
+ ;
+
+idcolon : idtemplate idcolontail {
+ $$ = 0;
+ if (!$$) $$ = NewStringf("%s%s", $1,$2);
+ Delete($2);
+ }
+ | NONID DCOLON idtemplatetemplate idcolontail {
+ $$ = NewStringf("::%s%s",$3,$4);
+ Delete($4);
+ }
+ | idtemplate {
+ $$ = NewString($1);
+ }
+ | NONID DCOLON idtemplatetemplate {
+ $$ = NewStringf("::%s",$3);
+ }
+ | OPERATOR {
+ $$ = NewStringf("%s", $1);
+ }
+ | OPERATOR less_valparms_greater {
+ $$ = NewStringf("%s%s", $1, $2);
+ }
+ | NONID DCOLON OPERATOR {
+ $$ = NewStringf("::%s",$3);
+ }
+ ;
+
+idcolontail : DCOLON idtemplatetemplate idcolontail {
+ $$ = NewStringf("::%s%s",$2,$3);
+ Delete($3);
+ }
+ | DCOLON idtemplatetemplate {
+ $$ = NewStringf("::%s",$2);
+ }
+ | DCOLON OPERATOR {
+ $$ = NewStringf("::%s",$2);
+ }
+/* | DCOLON CONVERSIONOPERATOR {
+ $$ = NewString($2);
+ } */
+
+ | DCNOT idtemplate {
+ $$ = NewStringf("::~%s",$2);
+ }
+ ;
+
+
+idtemplate : identifier {
+ $$ = NewStringf("%s", $1);
+ }
+ | identifier less_valparms_greater {
+ $$ = NewStringf("%s%s", $1, $2);
+ }
+ ;
+
+idtemplatetemplate : idtemplate {
+ $$ = $1;
+ }
+ | TEMPLATE identifier less_valparms_greater {
+ $$ = NewStringf("%s%s", $2, $3);
+ }
+ ;
+
+/* Identifier, but no templates */
+idcolonnt : identifier idcolontailnt {
+ $$ = 0;
+ if (!$$) $$ = NewStringf("%s%s", $1,$2);
+ Delete($2);
+ }
+ | NONID DCOLON identifier idcolontailnt {
+ $$ = NewStringf("::%s%s",$3,$4);
+ Delete($4);
+ }
+ | identifier {
+ $$ = NewString($1);
+ }
+ | NONID DCOLON identifier {
+ $$ = NewStringf("::%s",$3);
+ }
+ | OPERATOR {
+ $$ = NewString($1);
+ }
+ | NONID DCOLON OPERATOR {
+ $$ = NewStringf("::%s",$3);
+ }
+ ;
+
+idcolontailnt : DCOLON identifier idcolontailnt {
+ $$ = NewStringf("::%s%s",$2,$3);
+ Delete($3);
+ }
+ | DCOLON identifier {
+ $$ = NewStringf("::%s",$2);
+ }
+ | DCOLON OPERATOR {
+ $$ = NewStringf("::%s",$2);
+ }
+ | DCNOT identifier {
+ $$ = NewStringf("::~%s",$2);
+ }
+ ;
+
+/* Concatenated strings */
+string : string STRING {
+ $$ = NewStringf("%s%s", $1, $2);
+ }
+ | STRING { $$ = NewString($1);}
+ ;
+/* Concatenated wide strings: L"str1" L"str2" */
+wstring : wstring WSTRING {
+ $$ = NewStringf("%s%s", $1, $2);
+ }
+/* Concatenated wide string and normal string literal: L"str1" "str2" */
+/*not all the compilers support this concatenation mode, so perhaps better to postpone it*/
+ /*| wstring STRING { here $2 comes unescaped, we have to escape it back first via NewStringf("%(escape)s)"
+ $$ = NewStringf("%s%s", $1, $2);
+ }*/
+ | WSTRING { $$ = NewString($1);}
+ ;
+
+stringbrace : string {
+ $$ = $1;
+ }
+ | LBRACE {
+ skip_balanced('{','}');
+ $$ = NewString(scanner_ccode);
+ }
+ | HBLOCK {
+ $$ = $1;
+ }
+ ;
+
+options : LPAREN kwargs RPAREN {
+ Hash *n;
+ $$ = NewHash();
+ n = $2;
+ while(n) {
+ String *name, *value;
+ name = Getattr(n,"name");
+ value = Getattr(n,"value");
+ if (!value) value = (String *) "1";
+ Setattr($$,name, value);
+ n = nextSibling(n);
+ }
+ }
+ | empty { $$ = 0; };
+
+
+/* Keyword arguments */
+kwargs : idstring EQUAL stringnum {
+ $$ = NewHash();
+ Setattr($$,"name",$1);
+ Setattr($$,"value",$3);
+ }
+ | idstring EQUAL stringnum COMMA kwargs {
+ $$ = NewHash();
+ Setattr($$,"name",$1);
+ Setattr($$,"value",$3);
+ set_nextSibling($$,$5);
+ }
+ | idstring {
+ $$ = NewHash();
+ Setattr($$,"name",$1);
+ }
+ | idstring COMMA kwargs {
+ $$ = NewHash();
+ Setattr($$,"name",$1);
+ set_nextSibling($$,$3);
+ }
+ | idstring EQUAL stringtype {
+ $$ = $3;
+ Setattr($$,"name",$1);
+ }
+ | idstring EQUAL stringtype COMMA kwargs {
+ $$ = $3;
+ Setattr($$,"name",$1);
+ set_nextSibling($$,$5);
+ }
+ ;
+
+stringnum : string {
+ $$ = $1;
+ }
+ | exprnum {
+ $$ = Char($1.val);
+ }
+ ;
+
+empty : ;
+
+%%
+
+SwigType *Swig_cparse_type(String *s) {
+ String *ns;
+ ns = NewStringf("%s;",s);
+ Seek(ns,0,SEEK_SET);
+ scanner_file(ns);
+ top = 0;
+ scanner_next_token(PARSETYPE);
+ yyparse();
+ /* Printf(stdout,"typeparse: '%s' ---> '%s'\n", s, top); */
+ return top;
+}
+
+
+Parm *Swig_cparse_parm(String *s) {
+ String *ns;
+ ns = NewStringf("%s;",s);
+ Seek(ns,0,SEEK_SET);
+ scanner_file(ns);
+ top = 0;
+ scanner_next_token(PARSEPARM);
+ yyparse();
+ /* Printf(stdout,"typeparse: '%s' ---> '%s'\n", s, top); */
+ Delete(ns);
+ return top;
+}
+
+
+ParmList *Swig_cparse_parms(String *s, Node *file_line_node) {
+ String *ns;
+ char *cs = Char(s);
+ if (cs && cs[0] != '(') {
+ ns = NewStringf("(%s);",s);
+ } else {
+ ns = NewStringf("%s;",s);
+ }
+ Setfile(ns, Getfile(file_line_node));
+ Setline(ns, Getline(file_line_node));
+ Seek(ns,0,SEEK_SET);
+ scanner_file(ns);
+ top = 0;
+ scanner_next_token(PARSEPARMS);
+ yyparse();
+ /* Printf(stdout,"typeparse: '%s' ---> '%s'\n", s, top); */
+ return top;
+}
+
diff --git a/contrib/tools/swig/Source/CParse/templ.c b/contrib/tools/swig/Source/CParse/templ.c
new file mode 100644
index 0000000000..0dec215869
--- /dev/null
+++ b/contrib/tools/swig/Source/CParse/templ.c
@@ -0,0 +1,976 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * templ.c
+ *
+ * Expands a template into a specialized version.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+
+static int template_debug = 0;
+
+
+const char *baselists[3];
+
+void SwigType_template_init(void) {
+ baselists[0] = "baselist";
+ baselists[1] = "protectedbaselist";
+ baselists[2] = "privatebaselist";
+}
+
+
+static void add_parms(ParmList *p, List *patchlist, List *typelist, int is_pattern) {
+ while (p) {
+ SwigType *ty = Getattr(p, "type");
+ SwigType *val = Getattr(p, "value");
+ Append(typelist, ty);
+ Append(typelist, val);
+ if (is_pattern) {
+ /* Typemap patterns are not simple parameter lists.
+ * Output style ("out", "ret" etc) typemap names can be
+ * qualified names and so may need template expansion */
+ SwigType *name = Getattr(p, "name");
+ Append(typelist, name);
+ }
+ Append(patchlist, val);
+ p = nextSibling(p);
+ }
+}
+
+void Swig_cparse_debug_templates(int x) {
+ template_debug = x;
+}
+
+/* -----------------------------------------------------------------------------
+ * cparse_template_expand()
+ *
+ * Expands a template node into a specialized version. This is done by
+ * patching typenames and other aspects of the node according to a list of
+ * template parameters
+ * ----------------------------------------------------------------------------- */
+
+static void cparse_template_expand(Node *templnode, Node *n, String *tname, String *rname, String *templateargs, List *patchlist, List *typelist, List *cpatchlist) {
+ static int expanded = 0;
+ String *nodeType;
+ if (!n)
+ return;
+ nodeType = nodeType(n);
+ if (Getattr(n, "error"))
+ return;
+
+ if (Equal(nodeType, "template")) {
+ /* Change the node type back to normal */
+ if (!expanded) {
+ expanded = 1;
+ set_nodeType(n, Getattr(n, "templatetype"));
+ cparse_template_expand(templnode, n, tname, rname, templateargs, patchlist, typelist, cpatchlist);
+ expanded = 0;
+ return;
+ } else {
+ /* Called when template appears inside another template */
+ /* Member templates */
+
+ set_nodeType(n, Getattr(n, "templatetype"));
+ cparse_template_expand(templnode, n, tname, rname, templateargs, patchlist, typelist, cpatchlist);
+ set_nodeType(n, "template");
+ return;
+ }
+ } else if (Equal(nodeType, "cdecl")) {
+ /* A simple C declaration */
+ SwigType *t, *v, *d;
+ String *code;
+ t = Getattr(n, "type");
+ v = Getattr(n, "value");
+ d = Getattr(n, "decl");
+
+ code = Getattr(n, "code");
+
+ Append(typelist, t);
+ Append(typelist, d);
+ Append(patchlist, v);
+ Append(cpatchlist, code);
+
+ if (Getattr(n, "conversion_operator")) {
+ Append(cpatchlist, Getattr(n, "name"));
+ if (Getattr(n, "sym:name")) {
+ Append(cpatchlist, Getattr(n, "sym:name"));
+ }
+ }
+ if (checkAttribute(n, "storage", "friend")) {
+ String *symname = Getattr(n, "sym:name");
+ if (symname) {
+ String *stripped_name = SwigType_templateprefix(symname);
+ Setattr(n, "sym:name", stripped_name);
+ Delete(stripped_name);
+ }
+ Append(typelist, Getattr(n, "name"));
+ }
+
+ add_parms(Getattr(n, "parms"), cpatchlist, typelist, 0);
+ add_parms(Getattr(n, "throws"), cpatchlist, typelist, 0);
+
+ } else if (Equal(nodeType, "class")) {
+ /* Patch base classes */
+ {
+ int b = 0;
+ for (b = 0; b < 3; ++b) {
+ List *bases = Getattr(n, baselists[b]);
+ if (bases) {
+ int i;
+ int ilen = Len(bases);
+ for (i = 0; i < ilen; i++) {
+ String *name = Copy(Getitem(bases, i));
+ Setitem(bases, i, name);
+ Append(typelist, name);
+ }
+ }
+ }
+ }
+ /* Patch children */
+ {
+ Node *cn = firstChild(n);
+ while (cn) {
+ cparse_template_expand(templnode, cn, tname, rname, templateargs, patchlist, typelist, cpatchlist);
+ cn = nextSibling(cn);
+ }
+ }
+ } else if (Equal(nodeType, "constructor")) {
+ String *name = Getattr(n, "name");
+ if (!(Getattr(n, "templatetype"))) {
+ String *symname;
+ String *stripped_name = SwigType_templateprefix(name);
+ if (Strstr(tname, stripped_name)) {
+ Replaceid(name, stripped_name, tname);
+ }
+ Delete(stripped_name);
+ symname = Getattr(n, "sym:name");
+ if (symname) {
+ stripped_name = SwigType_templateprefix(symname);
+ if (Strstr(tname, stripped_name)) {
+ Replaceid(symname, stripped_name, tname);
+ }
+ Delete(stripped_name);
+ }
+ if (strchr(Char(name), '<')) {
+ Append(patchlist, Getattr(n, "name"));
+ } else {
+ Append(name, templateargs);
+ }
+ name = Getattr(n, "sym:name");
+ if (name) {
+ if (strchr(Char(name), '<')) {
+ Clear(name);
+ Append(name, rname);
+ } else {
+ String *tmp = Copy(name);
+ Replace(tmp, tname, rname, DOH_REPLACE_ANY);
+ Clear(name);
+ Append(name, tmp);
+ Delete(tmp);
+ }
+ }
+ /* Setattr(n,"sym:name",name); */
+ }
+ Append(cpatchlist, Getattr(n, "code"));
+ Append(typelist, Getattr(n, "decl"));
+ add_parms(Getattr(n, "parms"), cpatchlist, typelist, 0);
+ add_parms(Getattr(n, "throws"), cpatchlist, typelist, 0);
+ } else if (Equal(nodeType, "destructor")) {
+ /* We only need to patch the dtor of the template itself, not the destructors of any nested classes, so check that the parent of this node is the root
+ * template node, with the special exception for %extend which adds its methods under an intermediate node. */
+ Node* parent = parentNode(n);
+ if (parent == templnode || (parentNode(parent) == templnode && Equal(nodeType(parent), "extend"))) {
+ String *name = Getattr(n, "name");
+ if (name) {
+ if (strchr(Char(name), '<'))
+ Append(patchlist, Getattr(n, "name"));
+ else
+ Append(name, templateargs);
+ }
+ name = Getattr(n, "sym:name");
+ if (name) {
+ if (strchr(Char(name), '<')) {
+ String *sn = Copy(tname);
+ Setattr(n, "sym:name", sn);
+ Delete(sn);
+ } else {
+ Replace(name, tname, rname, DOH_REPLACE_ANY);
+ }
+ }
+ /* Setattr(n,"sym:name",name); */
+ Append(cpatchlist, Getattr(n, "code"));
+ }
+ } else if (Equal(nodeType, "using")) {
+ String *uname = Getattr(n, "uname");
+ if (uname && strchr(Char(uname), '<')) {
+ Append(patchlist, uname);
+ }
+ if (Getattr(n, "namespace")) {
+ /* Namespace link. This is nasty. Is other namespace defined? */
+
+ }
+ } else {
+ /* Look for obvious parameters */
+ Node *cn;
+ Append(cpatchlist, Getattr(n, "code"));
+ Append(typelist, Getattr(n, "type"));
+ Append(typelist, Getattr(n, "decl"));
+ add_parms(Getattr(n, "parms"), cpatchlist, typelist, 0);
+ add_parms(Getattr(n, "kwargs"), cpatchlist, typelist, 0);
+ add_parms(Getattr(n, "pattern"), cpatchlist, typelist, 1);
+ add_parms(Getattr(n, "throws"), cpatchlist, typelist, 0);
+ cn = firstChild(n);
+ while (cn) {
+ cparse_template_expand(templnode, cn, tname, rname, templateargs, patchlist, typelist, cpatchlist);
+ cn = nextSibling(cn);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * cparse_fix_function_decl()
+ *
+ * Move the prefix of the "type" attribute (excluding any trailing qualifier)
+ * to the end of the "decl" attribute.
+ * Examples:
+ * decl="f().", type="p.q(const).char" => decl="f().p.", type="q(const).char"
+ * decl="f().p.", type="p.SomeClass" => decl="f().p.p.", type="SomeClass"
+ * decl="f().", type="r.q(const).p.int" => decl="f().r.q(const).p.", type="int"
+ * ----------------------------------------------------------------------------- */
+
+static void cparse_fix_function_decl(String *name, SwigType *decl, SwigType *type) {
+ String *prefix;
+ int prefixLen;
+ SwigType *last;
+
+ /* The type's prefix is what potentially has to be moved to the end of 'decl' */
+ prefix = SwigType_prefix(type);
+
+ /* First some parts (qualifier and array) have to be removed from prefix
+ in order to remain in the 'type' attribute. */
+ last = SwigType_last(prefix);
+ while (last) {
+ if (SwigType_isqualifier(last) || SwigType_isarray(last)) {
+ /* Keep this part in the 'type' */
+ Delslice(prefix, Len(prefix) - Len(last), DOH_END);
+ Delete(last);
+ last = SwigType_last(prefix);
+ } else {
+ /* Done with processing prefix */
+ Delete(last);
+ last = 0;
+ }
+ }
+
+ /* Transfer prefix from the 'type' to the 'decl' attribute */
+ prefixLen = Len(prefix);
+ if (prefixLen > 0) {
+ Append(decl, prefix);
+ Delslice(type, 0, prefixLen);
+ if (template_debug) {
+ Printf(stdout, " change function '%s' to type='%s', decl='%s'\n", name, type, decl);
+ }
+ }
+
+ Delete(prefix);
+}
+
+/* -----------------------------------------------------------------------------
+ * cparse_postprocess_expanded_template()
+ *
+ * This function postprocesses the given node after template expansion.
+ * Currently the only task to perform is fixing function decl and type attributes.
+ * ----------------------------------------------------------------------------- */
+
+static void cparse_postprocess_expanded_template(Node *n) {
+ String *nodeType;
+ if (!n)
+ return;
+ nodeType = nodeType(n);
+ if (Getattr(n, "error"))
+ return;
+
+ if (Equal(nodeType, "cdecl")) {
+ /* A simple C declaration */
+ SwigType *d = Getattr(n, "decl");
+ if (d && SwigType_isfunction(d)) {
+ /* A function node */
+ SwigType *t = Getattr(n, "type");
+ if (t) {
+ String *name = Getattr(n, "name");
+ cparse_fix_function_decl(name, d, t);
+ }
+ }
+ } else {
+ /* Look for any children */
+ Node *cn = firstChild(n);
+ while (cn) {
+ cparse_postprocess_expanded_template(cn);
+ cn = nextSibling(cn);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * partial_arg()
+ * ----------------------------------------------------------------------------- */
+
+static
+String *partial_arg(String *s, String *p) {
+ char *c;
+ char *cp = Char(p);
+ String *prefix;
+ String *newarg;
+
+ /* Find the prefix on the partial argument */
+
+ c = strchr(cp, '$');
+ if (!c) {
+ return Copy(s);
+ }
+ prefix = NewStringWithSize(cp, (int)(c - cp));
+ newarg = Copy(s);
+ Replace(newarg, prefix, "", DOH_REPLACE_FIRST);
+ Delete(prefix);
+ return newarg;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparse_template_expand()
+ * ----------------------------------------------------------------------------- */
+
+int Swig_cparse_template_expand(Node *n, String *rname, ParmList *tparms, Symtab *tscope) {
+ List *patchlist, *cpatchlist, *typelist;
+ String *templateargs;
+ String *tname;
+ String *iname;
+ String *tbase;
+ patchlist = NewList();
+ cpatchlist = NewList();
+ typelist = NewList();
+
+ {
+ String *tmp = NewStringEmpty();
+ if (tparms) {
+ SwigType_add_template(tmp, tparms);
+ }
+ templateargs = Copy(tmp);
+ Delete(tmp);
+ }
+
+ tname = Copy(Getattr(n, "name"));
+ tbase = Swig_scopename_last(tname);
+
+ /* Look for partial specialization matching */
+ if (Getattr(n, "partialargs")) {
+ Parm *p, *tp;
+ ParmList *ptargs = SwigType_function_parms(Getattr(n, "partialargs"), n);
+ p = ptargs;
+ tp = tparms;
+ while (p && tp) {
+ SwigType *ptype;
+ SwigType *tptype;
+ SwigType *partial_type;
+ ptype = Getattr(p, "type");
+ tptype = Getattr(tp, "type");
+ if (ptype && tptype) {
+ partial_type = partial_arg(tptype, ptype);
+ /* Printf(stdout,"partial '%s' '%s' ---> '%s'\n", tptype, ptype, partial_type); */
+ Setattr(tp, "type", partial_type);
+ Delete(partial_type);
+ }
+ p = nextSibling(p);
+ tp = nextSibling(tp);
+ }
+ assert(ParmList_len(ptargs) == ParmList_len(tparms));
+ Delete(ptargs);
+ }
+
+ /*
+ Parm *p = tparms;
+ while (p) {
+ Printf(stdout, "tparm: '%s' '%s' '%s'\n", Getattr(p, "name"), Getattr(p, "type"), Getattr(p, "value"));
+ p = nextSibling(p);
+ }
+ */
+
+ /* Printf(stdout,"targs = '%s'\n", templateargs);
+ Printf(stdout,"rname = '%s'\n", rname);
+ Printf(stdout,"tname = '%s'\n", tname); */
+ cparse_template_expand(n, n, tname, rname, templateargs, patchlist, typelist, cpatchlist);
+
+ /* Set the name */
+ {
+ String *name = Getattr(n, "name");
+ if (name) {
+ Append(name, templateargs);
+ }
+ iname = name;
+ }
+
+ /* Patch all of the types */
+ {
+ Parm *tp = Getattr(n, "templateparms");
+ Parm *p = tparms;
+ /* Printf(stdout,"%s\n", ParmList_str_defaultargs(tp)); */
+
+ if (tp) {
+ Symtab *tsdecl = Getattr(n, "sym:symtab");
+ String *tsname = Getattr(n, "sym:name");
+ while (p && tp) {
+ String *name, *value, *valuestr, *tmp, *tmpr;
+ int sz, i;
+ String *dvalue = 0;
+ String *qvalue = 0;
+
+ name = Getattr(tp, "name");
+ value = Getattr(p, "value");
+
+ if (name) {
+ if (!value)
+ value = Getattr(p, "type");
+ qvalue = Swig_symbol_typedef_reduce(value, tsdecl);
+ dvalue = Swig_symbol_type_qualify(qvalue, tsdecl);
+ if (SwigType_istemplate(dvalue)) {
+ String *ty = Swig_symbol_template_deftype(dvalue, tscope);
+ Delete(dvalue);
+ dvalue = ty;
+ }
+
+ assert(dvalue);
+ valuestr = SwigType_str(dvalue, 0);
+ /* Need to patch default arguments */
+ {
+ Parm *rp = nextSibling(p);
+ while (rp) {
+ String *rvalue = Getattr(rp, "value");
+ if (rvalue) {
+ Replace(rvalue, name, dvalue, DOH_REPLACE_ID);
+ }
+ rp = nextSibling(rp);
+ }
+ }
+ sz = Len(patchlist);
+ for (i = 0; i < sz; i++) {
+ String *s = Getitem(patchlist, i);
+ Replace(s, name, dvalue, DOH_REPLACE_ID);
+ }
+ sz = Len(typelist);
+ for (i = 0; i < sz; i++) {
+ String *s = Getitem(typelist, i);
+ /*
+ The approach of 'trivially' replacing template arguments is kind of fragile.
+ In particular if types with similar name in different namespaces appear.
+ We will not replace template args if a type/class exists with the same
+ name which is not a template.
+ */
+ Node * tynode = Swig_symbol_clookup(s, 0);
+ String *tyname = tynode ? Getattr(tynode, "sym:name") : 0;
+ if (!tyname || !tsname || !Equal(tyname, tsname) || Getattr(tynode, "templatetype")) {
+ SwigType_typename_replace(s, name, dvalue);
+ SwigType_typename_replace(s, tbase, iname);
+ }
+ }
+
+ tmp = NewStringf("#%s", name);
+ tmpr = NewStringf("\"%s\"", valuestr);
+
+ sz = Len(cpatchlist);
+ for (i = 0; i < sz; i++) {
+ String *s = Getitem(cpatchlist, i);
+ Replace(s, tmp, tmpr, DOH_REPLACE_ID);
+ Replace(s, name, valuestr, DOH_REPLACE_ID);
+ }
+ Delete(tmp);
+ Delete(tmpr);
+ Delete(valuestr);
+ Delete(dvalue);
+ Delete(qvalue);
+ }
+ p = nextSibling(p);
+ tp = nextSibling(tp);
+ if (!p)
+ p = tp;
+ }
+ } else {
+ /* No template parameters at all. This could be a specialization */
+ int i, sz;
+ sz = Len(typelist);
+ for (i = 0; i < sz; i++) {
+ String *s = Getitem(typelist, i);
+ SwigType_typename_replace(s, tbase, iname);
+ }
+ }
+ }
+ cparse_postprocess_expanded_template(n);
+
+ /* Patch bases */
+ {
+ List *bases = Getattr(n, "baselist");
+ if (bases) {
+ Iterator b;
+ for (b = First(bases); b.item; b = Next(b)) {
+ String *qn = Swig_symbol_type_qualify(b.item, tscope);
+ Clear(b.item);
+ Append(b.item, qn);
+ Delete(qn);
+ }
+ }
+ }
+ Delete(patchlist);
+ Delete(cpatchlist);
+ Delete(typelist);
+ Delete(tbase);
+ Delete(tname);
+ Delete(templateargs);
+
+ /* set_nodeType(n,"template"); */
+ return 0;
+}
+
+typedef enum { ExactNoMatch = -2, PartiallySpecializedNoMatch = -1, PartiallySpecializedMatch = 1, ExactMatch = 2 } EMatch;
+
+/* -----------------------------------------------------------------------------
+ * does_parm_match()
+ *
+ * Template argument deduction - check if a template type matches a partially specialized
+ * template parameter type. Typedef reduce 'partial_parm_type' to see if it matches 'type'.
+ *
+ * type - template parameter type to match against
+ * partial_parm_type - partially specialized template type - a possible match
+ * partial_parm_type_base - base type of partial_parm_type
+ * tscope - template scope
+ * specialization_priority - (output) contains a value indicating how good the match is
+ * (higher is better) only set if return is set to PartiallySpecializedMatch or ExactMatch.
+ * ----------------------------------------------------------------------------- */
+
+static EMatch does_parm_match(SwigType *type, SwigType *partial_parm_type, const char *partial_parm_type_base, Symtab *tscope, int *specialization_priority) {
+ static const int EXACT_MATCH_PRIORITY = 99999; /* a number bigger than the length of any conceivable type */
+ int matches;
+ int substitutions;
+ EMatch match;
+ SwigType *ty = Swig_symbol_typedef_reduce(type, tscope);
+ String *base = SwigType_base(ty);
+ SwigType *t = Copy(partial_parm_type);
+ substitutions = Replaceid(t, partial_parm_type_base, base); /* eg: Replaceid("p.$1", "$1", "int") returns t="p.int" */
+ matches = Equal(ty, t);
+ *specialization_priority = -1;
+ if (substitutions == 1) {
+ /* we have a non-explicit specialized parameter (in partial_parm_type) because a substitution for $1, $2... etc has taken place */
+ SwigType *tt = Copy(partial_parm_type);
+ int len;
+ /*
+ check for match to partial specialization type, for example, all of the following could match the type in the %template:
+ template <typename T> struct XX {};
+ template <typename T> struct XX<T &> {}; // r.$1
+ template <typename T> struct XX<T const&> {}; // r.q(const).$1
+ template <typename T> struct XX<T *const&> {}; // r.q(const).p.$1
+ %template(XXX) XX<int *const&>; // r.q(const).p.int
+
+ where type="r.q(const).p.int" will match either of tt="r.", tt="r.q(const)" tt="r.q(const).p"
+ */
+ Replaceid(tt, partial_parm_type_base, ""); /* remove the $1, $2 etc, eg tt="p.$1" => "p." */
+ len = Len(tt);
+ if (Strncmp(tt, ty, len) == 0) {
+ match = PartiallySpecializedMatch;
+ *specialization_priority = len;
+ } else {
+ match = PartiallySpecializedNoMatch;
+ }
+ Delete(tt);
+ } else {
+ match = matches ? ExactMatch : ExactNoMatch;
+ if (matches)
+ *specialization_priority = EXACT_MATCH_PRIORITY; /* exact matches always take precedence */
+ }
+ /*
+ Printf(stdout, " does_parm_match %2d %5d [%s] [%s]\n", match, *specialization_priority, type, partial_parm_type);
+ */
+ Delete(t);
+ Delete(base);
+ Delete(ty);
+ return match;
+}
+
+/* -----------------------------------------------------------------------------
+ * template_locate()
+ *
+ * Search for a template that matches name with given parameters.
+ * ----------------------------------------------------------------------------- */
+
+static Node *template_locate(String *name, Parm *tparms, Symtab *tscope) {
+ Node *n = 0;
+ String *tname = 0;
+ Node *templ;
+ Symtab *primary_scope = 0;
+ List *possiblepartials = 0;
+ Parm *p;
+ Parm *parms = 0;
+ Parm *targs;
+ ParmList *expandedparms;
+ int *priorities_matrix = 0;
+ int max_possible_partials = 0;
+ int posslen = 0;
+
+ /* Search for primary (unspecialized) template */
+ templ = Swig_symbol_clookup(name, 0);
+
+ if (template_debug) {
+ tname = Copy(name);
+ SwigType_add_template(tname, tparms);
+ Printf(stdout, "\n");
+ Swig_diagnostic(cparse_file, cparse_line, "template_debug: Searching for match to: '%s'\n", tname);
+ Delete(tname);
+ tname = 0;
+ }
+
+ if (templ) {
+ tname = Copy(name);
+ parms = CopyParmList(tparms);
+
+ /* All template specializations must be in the primary template's scope, store the symbol table for this scope for specialization lookups */
+ primary_scope = Getattr(templ, "sym:symtab");
+
+ /* Add default values from primary template */
+ targs = Getattr(templ, "templateparms");
+ expandedparms = Swig_symbol_template_defargs(parms, targs, tscope, primary_scope);
+
+ /* reduce the typedef */
+ p = expandedparms;
+ while (p) {
+ SwigType *ty = Getattr(p, "type");
+ if (ty) {
+ SwigType *nt = Swig_symbol_type_qualify(ty, tscope);
+ Setattr(p, "type", nt);
+ Delete(nt);
+ }
+ p = nextSibling(p);
+ }
+ SwigType_add_template(tname, expandedparms);
+
+ /* Search for an explicit (exact) specialization. Example: template<> class name<int> { ... } */
+ {
+ if (template_debug) {
+ Printf(stdout, " searching for : '%s' (explicit specialization)\n", tname);
+ }
+ n = Swig_symbol_clookup_local(tname, primary_scope);
+ if (!n) {
+ SwigType *rname = Swig_symbol_typedef_reduce(tname, tscope);
+ if (!Equal(rname, tname)) {
+ if (template_debug) {
+ Printf(stdout, " searching for : '%s' (explicit specialization with typedef reduction)\n", rname);
+ }
+ n = Swig_symbol_clookup_local(rname, primary_scope);
+ }
+ Delete(rname);
+ }
+ if (n) {
+ Node *tn;
+ String *nodeType = nodeType(n);
+ if (Equal(nodeType, "template")) {
+ if (template_debug) {
+ Printf(stdout, " explicit specialization found: '%s'\n", Getattr(n, "name"));
+ }
+ goto success;
+ }
+ tn = Getattr(n, "template");
+ if (tn) {
+ if (template_debug) {
+ Printf(stdout, " previous instantiation found: '%s'\n", Getattr(n, "name"));
+ }
+ n = tn;
+ goto success; /* Previously wrapped by a template instantiation */
+ }
+ Swig_error(cparse_file, cparse_line, "'%s' is not defined as a template. (%s)\n", name, nodeType(n));
+ Delete(tname);
+ Delete(parms);
+ return 0; /* Found a match, but it's not a template of any kind. */
+ }
+ }
+
+ /* Search for partial specializations.
+ * Example: template<typename T> class name<T *> { ... }
+
+ * There are 3 types of template arguments:
+ * (1) Template type arguments
+ * (2) Template non type arguments
+ * (3) Template template arguments
+ * only (1) is really supported for partial specializations
+ */
+
+ /* Rank each template parameter against the desired template parameters then build a matrix of best matches */
+ possiblepartials = NewList();
+ {
+ char tmp[32];
+ List *partials;
+
+ partials = Getattr(templ, "partials"); /* note that these partial specializations do not include explicit specializations */
+ if (partials) {
+ Iterator pi;
+ int parms_len = ParmList_len(parms);
+ int *priorities_row;
+ max_possible_partials = Len(partials);
+ priorities_matrix = (int *)Malloc(sizeof(int) * max_possible_partials * parms_len); /* slightly wasteful allocation for max possible matches */
+ priorities_row = priorities_matrix;
+ for (pi = First(partials); pi.item; pi = Next(pi)) {
+ Parm *p = parms;
+ int all_parameters_match = 1;
+ int i = 1;
+ Parm *partialparms = Getattr(pi.item, "partialparms");
+ Parm *pp = partialparms;
+ String *templcsymname = Getattr(pi.item, "templcsymname");
+ if (template_debug) {
+ Printf(stdout, " checking match: '%s' (partial specialization)\n", templcsymname);
+ }
+ if (ParmList_len(partialparms) == parms_len) {
+ while (p && pp) {
+ SwigType *t;
+ sprintf(tmp, "$%d", i);
+ t = Getattr(p, "type");
+ if (!t)
+ t = Getattr(p, "value");
+ if (t) {
+ EMatch match = does_parm_match(t, Getattr(pp, "type"), tmp, tscope, priorities_row + i - 1);
+ if (match < (int)PartiallySpecializedMatch) {
+ all_parameters_match = 0;
+ break;
+ }
+ }
+ i++;
+ p = nextSibling(p);
+ pp = nextSibling(pp);
+ }
+ if (all_parameters_match) {
+ Append(possiblepartials, pi.item);
+ priorities_row += parms_len;
+ }
+ }
+ }
+ }
+ }
+
+ posslen = Len(possiblepartials);
+ if (template_debug) {
+ int i;
+ if (posslen == 0)
+ Printf(stdout, " matched partials: NONE\n");
+ else if (posslen == 1)
+ Printf(stdout, " chosen partial: '%s'\n", Getattr(Getitem(possiblepartials, 0), "templcsymname"));
+ else {
+ Printf(stdout, " possibly matched partials:\n");
+ for (i = 0; i < posslen; i++) {
+ Printf(stdout, " '%s'\n", Getattr(Getitem(possiblepartials, i), "templcsymname"));
+ }
+ }
+ }
+
+ if (posslen > 1) {
+ /* Now go through all the possibly matched partial specialization templates and look for a non-ambiguous match.
+ * Exact matches rank the highest and deduced parameters are ranked by how specialized they are, eg looking for
+ * a match to const int *, the following rank (highest to lowest):
+ * const int * (exact match)
+ * const T *
+ * T *
+ * T
+ *
+ * An ambiguous example when attempting to match as either specialization could match: %template() X<int *, double *>;
+ * template<typename T1, typename T2> X class {}; // primary template
+ * template<typename T1> X<T1, double *> class {}; // specialization (1)
+ * template<typename T2> X<int *, T2> class {}; // specialization (2)
+ */
+ if (template_debug) {
+ int row, col;
+ int parms_len = ParmList_len(parms);
+ Printf(stdout, " parameter priorities matrix (%d parms):\n", parms_len);
+ for (row = 0; row < posslen; row++) {
+ int *priorities_row = priorities_matrix + row*parms_len;
+ Printf(stdout, " ");
+ for (col = 0; col < parms_len; col++) {
+ Printf(stdout, "%5d ", priorities_row[col]);
+ }
+ Printf(stdout, "\n");
+ }
+ }
+ {
+ int row, col;
+ int parms_len = ParmList_len(parms);
+ /* Printf(stdout, " parameter priorities inverse matrix (%d parms):\n", parms_len); */
+ for (col = 0; col < parms_len; col++) {
+ int *priorities_col = priorities_matrix + col;
+ int maxpriority = -1;
+ /*
+ Printf(stdout, "max_possible_partials: %d col:%d\n", max_possible_partials, col);
+ Printf(stdout, " ");
+ */
+ /* determine the highest rank for this nth parameter */
+ for (row = 0; row < posslen; row++) {
+ int *element_ptr = priorities_col + row*parms_len;
+ int priority = *element_ptr;
+ if (priority > maxpriority)
+ maxpriority = priority;
+ /* Printf(stdout, "%5d ", priority); */
+ }
+ /* Printf(stdout, "\n"); */
+ /* flag all the parameters which equal the highest rank */
+ for (row = 0; row < posslen; row++) {
+ int *element_ptr = priorities_col + row*parms_len;
+ int priority = *element_ptr;
+ *element_ptr = (priority >= maxpriority) ? 1 : 0;
+ }
+ }
+ }
+ {
+ int row, col;
+ int parms_len = ParmList_len(parms);
+ Iterator pi = First(possiblepartials);
+ Node *chosenpartials = NewList();
+ if (template_debug)
+ Printf(stdout, " priority flags matrix:\n");
+ for (row = 0; row < posslen; row++) {
+ int *priorities_row = priorities_matrix + row*parms_len;
+ int highest_count = 0; /* count of highest priority parameters */
+ for (col = 0; col < parms_len; col++) {
+ highest_count += priorities_row[col];
+ }
+ if (template_debug) {
+ Printf(stdout, " ");
+ for (col = 0; col < parms_len; col++) {
+ Printf(stdout, "%5d ", priorities_row[col]);
+ }
+ Printf(stdout, "\n");
+ }
+ if (highest_count == parms_len) {
+ Append(chosenpartials, pi.item);
+ }
+ pi = Next(pi);
+ }
+ if (Len(chosenpartials) > 0) {
+ /* one or more best match found */
+ Delete(possiblepartials);
+ possiblepartials = chosenpartials;
+ posslen = Len(possiblepartials);
+ } else {
+ /* no best match found */
+ Delete(chosenpartials);
+ }
+ }
+ }
+
+ if (posslen > 0) {
+ String *s = Getattr(Getitem(possiblepartials, 0), "templcsymname");
+ n = Swig_symbol_clookup_local(s, primary_scope);
+ if (posslen > 1) {
+ int i;
+ if (n) {
+ Swig_warning(WARN_PARSE_TEMPLATE_AMBIG, cparse_file, cparse_line, "Instantiation of template '%s' is ambiguous,\n", SwigType_namestr(tname));
+ Swig_warning(WARN_PARSE_TEMPLATE_AMBIG, Getfile(n), Getline(n), " instantiation '%s' used,\n", SwigType_namestr(Getattr(n, "name")));
+ }
+ for (i = 1; i < posslen; i++) {
+ String *templcsymname = Getattr(Getitem(possiblepartials, i), "templcsymname");
+ Node *ignored_node = Swig_symbol_clookup_local(templcsymname, primary_scope);
+ assert(ignored_node);
+ Swig_warning(WARN_PARSE_TEMPLATE_AMBIG, Getfile(ignored_node), Getline(ignored_node), " instantiation '%s' ignored.\n", SwigType_namestr(Getattr(ignored_node, "name")));
+ }
+ }
+ }
+
+ if (!n) {
+ if (template_debug) {
+ Printf(stdout, " chosen primary template: '%s'\n", Getattr(templ, "name"));
+ }
+ n = templ;
+ }
+ } else {
+ if (template_debug) {
+ Printf(stdout, " primary template not found\n");
+ }
+ /* Give up if primary (unspecialized) template not found as specializations will only exist if there is a primary template */
+ n = 0;
+ }
+
+ if (!n) {
+ Swig_error(cparse_file, cparse_line, "Template '%s' undefined.\n", name);
+ } else if (n) {
+ String *nodeType = nodeType(n);
+ if (!Equal(nodeType, "template")) {
+ Swig_error(cparse_file, cparse_line, "'%s' is not defined as a template. (%s)\n", name, nodeType);
+ n = 0;
+ }
+ }
+success:
+ Delete(tname);
+ Delete(possiblepartials);
+ if ((template_debug) && (n)) {
+ /*
+ Printf(stdout, "Node: %p\n", n);
+ Swig_print_node(n);
+ */
+ Printf(stdout, " chosen template:'%s'\n", Getattr(n, "name"));
+ }
+ Delete(parms);
+ Free(priorities_matrix);
+ return n;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparse_template_locate()
+ *
+ * Search for a template that matches name with given parameters.
+ * For templated classes finds the specialized template should there be one.
+ * For templated functions finds the unspecialized template even if a specialized
+ * template exists.
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_cparse_template_locate(String *name, Parm *tparms, Symtab *tscope) {
+ Node *n = template_locate(name, tparms, tscope); /* this function does what we want for templated classes */
+
+ if (n) {
+ String *nodeType = nodeType(n);
+ int isclass = 0;
+ assert(Equal(nodeType, "template"));
+ (void)nodeType;
+ isclass = (Equal(Getattr(n, "templatetype"), "class"));
+ if (!isclass) {
+ /* If not a templated class we must have a templated function.
+ The template found is not necessarily the one we want when dealing with templated
+ functions. We don't want any specialized templated functions as they won't have
+ the default parameters. Let's look for the unspecialized template. Also make sure
+ the number of template parameters is correct as it is possible to overload a
+ templated function with different numbers of template parameters. */
+
+ if (template_debug) {
+ Printf(stdout, " Not a templated class, seeking most appropriate templated function\n");
+ }
+
+ n = Swig_symbol_clookup_local(name, 0);
+ while (n) {
+ Parm *tparmsfound = Getattr(n, "templateparms");
+ if (ParmList_len(tparms) == ParmList_len(tparmsfound)) {
+ /* successful match */
+ break;
+ }
+ /* repeat until we find a match with correct number of templated parameters */
+ n = Getattr(n, "sym:nextSibling");
+ }
+
+ if (!n) {
+ Swig_error(cparse_file, cparse_line, "Template '%s' undefined.\n", name);
+ }
+
+ if ((template_debug) && (n)) {
+ Printf(stdout, "Templated function found: %p\n", n);
+ Swig_print_node(n);
+ }
+ }
+ }
+
+ return n;
+}
diff --git a/contrib/tools/swig/Source/CParse/util.c b/contrib/tools/swig/Source/CParse/util.c
new file mode 100644
index 0000000000..00863c0357
--- /dev/null
+++ b/contrib/tools/swig/Source/CParse/util.c
@@ -0,0 +1,126 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * util.c
+ *
+ * Parsing utilities.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparse_replace_descriptor()
+ *
+ * Replaces type descriptor string $descriptor() with the SWIG type descriptor
+ * string.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_cparse_replace_descriptor(String *s) {
+ char tmp[512];
+ String *arg = 0;
+ SwigType *t;
+ char *c = 0;
+
+ while ((c = strstr(Char(s), "$descriptor("))) {
+ char *d = tmp;
+ int level = 0;
+ while (*c) {
+ if (*c == '(')
+ level++;
+ if (*c == ')') {
+ level--;
+ if (level == 0) {
+ break;
+ }
+ }
+ *d = *c;
+ d++;
+ c++;
+ }
+ *d = 0;
+ arg = NewString(tmp + 12);
+ t = Swig_cparse_type(arg);
+ Delete(arg);
+ arg = 0;
+
+ if (t) {
+ String *mangle;
+ String *descriptor;
+
+ mangle = SwigType_manglestr(t);
+ descriptor = NewStringf("SWIGTYPE%s", mangle);
+ SwigType_remember(t);
+ *d = ')';
+ d++;
+ *d = 0;
+ Replace(s, tmp, descriptor, DOH_REPLACE_ANY);
+ Delete(mangle);
+ Delete(descriptor);
+ Delete(t);
+ } else {
+ Swig_error(Getfile(s), Getline(s), "Bad $descriptor() macro.\n");
+ break;
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparse_smartptr()
+ *
+ * Parse the type in smartptr feature and convert into a SwigType.
+ * Error out if the parsing fails as this is like a parser syntax error.
+ * ----------------------------------------------------------------------------- */
+
+SwigType *Swig_cparse_smartptr(Node *n) {
+ SwigType *smart = 0;
+ String *smartptr = Getattr(n, "feature:smartptr");
+ if (smartptr) {
+ SwigType *cpt = Swig_cparse_type(smartptr);
+ if (cpt) {
+ smart = SwigType_typedef_resolve_all(cpt);
+ Delete(cpt);
+ } else {
+ Swig_error(Getfile(n), Getline(n), "Invalid type (%s) in 'smartptr' feature for class %s.\n", smartptr, SwigType_namestr(Getattr(n, "name")));
+ }
+ }
+ return smart;
+}
+
+/* -----------------------------------------------------------------------------
+ * cparse_normalize_void()
+ *
+ * This function is used to replace arguments of the form (void) with empty
+ * arguments in C++
+ * ----------------------------------------------------------------------------- */
+
+void cparse_normalize_void(Node *n) {
+ String *decl = Getattr(n, "decl");
+ Parm *parms = Getattr(n, "parms");
+
+ if (SwigType_isfunction(decl)) {
+ if ((ParmList_len(parms) == 1) && (SwigType_type(Getattr(parms, "type")) == T_VOID)) {
+ Replaceall(decl, "f(void).", "f().");
+ Delattr(n, "parms");
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparse_new_node()
+ *
+ * Create an empty parse node, setting file and line number information
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_cparse_new_node(const_String_or_char_ptr tag) {
+ Node *n = NewHash();
+ set_nodeType(n,tag);
+ Setfile(n,cparse_file);
+ Setline(n,cparse_line);
+ return n;
+}
diff --git a/contrib/tools/swig/Source/DOH/README b/contrib/tools/swig/Source/DOH/README
new file mode 100644
index 0000000000..be90f25b46
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/README
@@ -0,0 +1,124 @@
+DOH (Dave's Object Hack)
+
+Overview:
+---------
+DOH is a small C library that provides a number of simple yet powerful
+data structures. The data structures are built around a dynamic typing
+model in which any given object is allowed to support one or more
+classes of operations. Furthermore, a simple garbage collection
+scheme and a variety of interesting library methods are available.
+All and all, the operation of DOH makes massive abuse of the C type
+system and would probably make the language purists scream and
+performance addicts run away in horror. However, I really don't
+care--so there! However, for the rest of us, DOH is actually kind of
+fun to use. This is only a short description of the methods and is no
+way meant to be exhaustive.
+
+Common Operations (for all types)
+---------------------------------
+Delete(obj) Decrease the reference count and destroy if zero
+Copy(obj) Make a copy of an object.
+Clear(obj) Clear an object.
+Setscope(obj) Set scope of an object (guru's only)
+Str(obj) Create a string representation of obj.
+Data(obj) Return pointer to raw data in an object
+Char(obj) Convert to a char *
+Len(obj) Length of an object
+Hash(obj) Hash value (used for mapping)
+Cmp(obj1,obj2) Compare two objects.
+Name(obj) Return the object name
+First(obj) Return first object (iterator)
+Next(obj) Return next object
+Dump(obj,out) Serialize on out
+Load(in) Unserialize from in
+First(obj) Iterator
+Next(iter) Next iterator
+
+Mapping Operations (for hash table behavior)
+--------------------------------------------
+Getattr(hash,key) Get an attribute
+Setattr(hash,key,value) Set an attribute
+Delattr(hash,key) Delete an attribute
+First(hash) Get first object (iterator)
+Next(hash) Get next object
+GetInt(hash,key) Get attribute as an 'int'
+SetInt(hash,key,ivalue) Set attribute as an 'int'
+GetDouble(hash,key) Get attribute as a 'double'
+SetDouble(hash,key,dvalue) Set Attribute as a 'double'
+GetChar(hash,key) Get attribute as a 'char *'
+
+Sequence Operations
+-------------------
+Getitem(list,index) Get an item
+Setitem(list,index,val) Set an item
+Delitem(list,index) Delete an item
+Insert(list,index,val) Insert an item
+Append(list,val) Append to end
+Push(list,val) Insert at beginning
+
+File Operations
+---------------
+Read(obj,buffer,len) Read data
+Write(obj,buffer,len) Write data
+Getc(obj) Get a character
+Putc(ch,obj) Put a character
+Ungetc(ch,obj) Put character back on input stream
+Seek(obj,offset,whence) Seek
+Tell(obj) Return file pointer
+Delete(obj) Decrease the reference count, close file if zero
+
+String Operations
+-----------------
+Replace(obj, orig, rep, flags) Replace occurrences of orig with rep.
+Chop(obj) Remove trailing whitespace
+
+flags is one of the following:
+ DOH_REPLACE_ID
+ DOH_REPLACE_ID_BEGIN
+ DOH_REPLACE_ID_END
+ DOH_REPLACE_NUMBER_END
+
+and can be combined with one or more of the following:
+ DOH_REPLACE_ANY
+ DOH_REPLACE_NOQUOTE
+ DOH_REPLACE_NOCOMMENT
+ DOH_REPLACE_FIRST
+
+Callable Operations
+-------------------
+Call(obj, args) Perform a function call with arguments args.
+
+Miscellaneous library functions
+-------------------------------
+NewScope() Create a new scope
+DelScope(s) Delete scope s
+Readline(in) Read a line of input from in
+Printf(out,fmt,...) Formatted output
+DohEncoding(name, fn) Register a format encoding for Printf
+
+Currently Available datatypes
+------------------------------
+NewString(char *initial) Strings
+NewHash() Hash
+NewList() List
+NewVoid(void *ptr, void (*del)(void *)) Void
+NewFile(char *filename, char *mode, List *newfiles) File
+NewCallable(DOH *(*func)(DOH *, DOH *)) Callable object
+
+
+Odds and ends:
+
+ 1. All objects are of type 'DOH *'
+ 2. When in doubt, see rule (1)
+ 3. In certain cases, DOH performs implicit conversions
+ of 'char *' to an appropriate DOH string representation.
+ For operations involving files, DOH works with many
+ kinds of objects including FILE *, DOH File objects,
+ and DOH strings. Don't even ask how this works.
+
+ 4. More complete documentation is forthcoming.
+
+
+
+
+
diff --git a/contrib/tools/swig/Source/DOH/base.c b/contrib/tools/swig/Source/DOH/base.c
new file mode 100644
index 0000000000..8731a5f118
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/base.c
@@ -0,0 +1,937 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * base.c
+ *
+ * This file contains the function entry points for dispatching methods on
+ * DOH objects. A number of small utility functions are also included.
+ * ----------------------------------------------------------------------------- */
+
+#include "dohint.h"
+
+/* -----------------------------------------------------------------------------
+ * DohDelete()
+ * ----------------------------------------------------------------------------- */
+
+void DohDelete(DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+
+ if (!obj)
+ return;
+ if (!DohCheck(b)) {
+ fputs("Fatal internal error: Attempt to delete a non-DOH object.\n", stderr);
+ Exit(EXIT_FAILURE);
+ }
+ if (b->flag_intern)
+ return;
+ assert(b->refcount > 0);
+ b->refcount--;
+ if (b->refcount <= 0) {
+ objinfo = b->type;
+ if (objinfo->doh_del) {
+ (objinfo->doh_del) (b);
+ } else {
+ if (b->data)
+ DohFree(b->data);
+ }
+ DohObjFree(b);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * DohCopy()
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohCopy(const DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+
+ if (!obj)
+ return 0;
+ if (!DohCheck(b)) {
+ fputs("Fatal internal error: Attempt to copy a non-DOH object.\n", stderr);
+ Exit(EXIT_FAILURE);
+ }
+ objinfo = b->type;
+ if (objinfo->doh_copy) {
+ DohBase *bc = (DohBase *) (objinfo->doh_copy) (b);
+ if ((bc) && b->meta) {
+ bc->meta = Copy(b->meta);
+ }
+ return (DOH *) bc;
+ }
+ return 0;
+}
+
+void DohIncref(DOH *obj) {
+ Incref(obj);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohClear()
+ * ----------------------------------------------------------------------------- */
+
+void DohClear(DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_clear)
+ (objinfo->doh_clear) (b);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohStr()
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohStr(const DOH *obj) {
+ char buffer[512];
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (DohCheck(b)) {
+ objinfo = b->type;
+ if (objinfo->doh_str) {
+ return (objinfo->doh_str) (b);
+ }
+ sprintf(buffer, "<Object '%s' at %p>", objinfo->objname, (void *) b);
+ return NewString(buffer);
+ } else {
+ return NewString(obj);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * DohDump()
+ * ----------------------------------------------------------------------------- */
+
+int DohDump(const DOH *obj, DOH *out) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_dump) {
+ return (objinfo->doh_dump) (b, out);
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohLen() - Defaults to strlen() if not a DOH object
+ * ----------------------------------------------------------------------------- */
+int DohLen(const DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (!b)
+ return 0;
+ if (DohCheck(b)) {
+ objinfo = b->type;
+ if (objinfo->doh_len) {
+ return (objinfo->doh_len) (b);
+ }
+ return 0;
+ } else {
+ return (int)strlen((char *) obj);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * DohHashVal()
+ * ----------------------------------------------------------------------------- */
+
+int DohHashval(const DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ /* obj is already checked and/or converted into DohBase* */
+ /* if (DohCheck(b)) */
+ {
+ objinfo = b->type;
+ if (objinfo->doh_hashval) {
+ return (objinfo->doh_hashval) (b);
+ }
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohData()
+ * ----------------------------------------------------------------------------- */
+
+void *DohData(const DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (DohCheck(obj)) {
+ objinfo = b->type;
+ if (objinfo->doh_data) {
+ return (objinfo->doh_data) (b);
+ }
+ return 0;
+ }
+ return (void *) obj;
+}
+
+/* -----------------------------------------------------------------------------
+ * RawData()
+ * ----------------------------------------------------------------------------- */
+
+static void *RawData(DohBase *b) {
+ DohObjInfo *objinfo = b->type;
+ return (objinfo->doh_data) ? (objinfo->doh_data) (b) : 0;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * DohCmp()
+ * ----------------------------------------------------------------------------- */
+
+int DohCmp(const DOH *obj1, const DOH *obj2) {
+ DohBase *b1, *b2;
+ DohObjInfo *b1info, *b2info;
+ int c1, c2;
+ b1 = (DohBase *) obj1;
+ b2 = (DohBase *) obj2;
+ c1 = DohCheck(b1);
+ c2 = DohCheck(b2);
+ /* most of the times, obj2 is a plain c string */
+ if (!c1 || !c2) {
+ if ((b1 == 0) && (b2 == 0))
+ return 0;
+ if (b1 && !b2)
+ return 1;
+ if (!b1 && b2)
+ return -1;
+ return strcmp((char *) (c1 ? RawData(b1) : (void *) obj1), (char *) (c2 ? RawData(b2) : (void *) obj2));
+ }
+ b1info = b1->type;
+ b2info = b2->type;
+ if ((b1info == b2info) && (b1info->doh_cmp))
+ return (b1info->doh_cmp) (b1, b2);
+ return 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohEqual()
+ * ----------------------------------------------------------------------------- */
+
+int DohEqual(const DOH *obj1, const DOH *obj2) {
+ DohBase *b1 = (DohBase *) obj1;
+ DohBase *b2 = (DohBase *) obj2;
+ if (!b1) {
+ return !b2;
+ } else if (!b2) {
+ return 0;
+ } else {
+ DohObjInfo *b1info = 0;
+ DohObjInfo *b2info = 0;
+ if (DohCheck(b1)) {
+ b1info = b1->type;
+ if (DohCheck(b2)) {
+ b2info = b2->type;
+ } else {
+ int len = (b1info->doh_len) (b1);
+ char *cobj = (char *) obj2;
+ return len == (int) strlen(cobj) ? (memcmp(RawData(b1), cobj, len) == 0) : 0;
+ }
+ } else if (DohCheck(b2)) {
+ int len = (b2->type->doh_len) (b2);
+ char *cobj = (char *) obj1;
+ return len == (int) strlen(cobj) ? (memcmp(RawData(b2), cobj, len) == 0) : 0;
+ } else {
+ return strcmp((char *) obj1, (char *) obj2) == 0;
+ }
+
+ if (!b1info) {
+ return obj1 == obj2;
+ } else if (b1info == b2info) {
+ return b1info->doh_equal ? (b1info->doh_equal) (b1, b2) : (b1info->doh_cmp ? (b1info->doh_cmp) (b1, b2) == 0 : (b1 == b2));
+ } else {
+ return 0;
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * DohFirst()
+ * ----------------------------------------------------------------------------- */
+
+DohIterator DohFirst(DOH *obj) {
+ DohIterator iter;
+ DohBase *b;
+ DohObjInfo *binfo;
+
+ b = (DohBase *) obj;
+ if (DohCheck(b)) {
+ binfo = b->type;
+ if (binfo->doh_first) {
+ return (binfo->doh_first) (b);
+ }
+ }
+ iter.object = 0;
+ iter.item = 0;
+ iter.key = 0;
+ iter._current = 0;
+ iter._index = 0;
+ return iter;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohNext()
+ * ----------------------------------------------------------------------------- */
+
+DohIterator DohNext(DohIterator iter) {
+ DohIterator niter;
+
+ if (iter.object) {
+ DohBase *b;
+ DohObjInfo *binfo;
+
+ b = (DohBase *) iter.object;
+ binfo = b->type;
+ if (binfo->doh_next) {
+ return (binfo->doh_next) (iter);
+ }
+ }
+ niter = iter;
+ return niter;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohIsMapping()
+ * ----------------------------------------------------------------------------- */
+int DohIsMapping(const DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (!DohCheck(b))
+ return 0;
+ objinfo = b->type;
+ if (objinfo->doh_hash)
+ return 1;
+ else
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetattr()
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohGetattr(DOH *obj, const DOH *name) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_hash && objinfo->doh_hash->doh_getattr) {
+ DOH *r = (objinfo->doh_hash->doh_getattr) (b, (DOH *) name);
+ return (r == DohNone) ? 0 : r;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetattr()
+ * ----------------------------------------------------------------------------- */
+
+int DohSetattr(DOH *obj, const DOH *name, const DOH *value) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_hash && objinfo->doh_hash->doh_setattr) {
+ return (objinfo->doh_hash->doh_setattr) (b, (DOH *) name, (DOH *) value);
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohDelattr()
+ * ----------------------------------------------------------------------------- */
+
+int DohDelattr(DOH *obj, const DOH *name) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_hash && objinfo->doh_hash->doh_delattr) {
+ return (objinfo->doh_hash->doh_delattr) (b, (DOH *) name);
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohCheckattr()
+ * ----------------------------------------------------------------------------- */
+
+int DohCheckattr(DOH *obj, const DOH *name, const DOH *value) {
+ DOH *attr = Getattr(obj,name);
+ if (!attr) return 0;
+ return DohEqual(attr,value);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohKeys()
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohKeys(DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo && objinfo->doh_hash->doh_keys) {
+ return (objinfo->doh_hash->doh_keys) (b);
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSortedKeys()
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohSortedKeys(DOH *obj, int (*cmp) (const DOH *, const DOH *)) {
+ DOHList *keys = DohKeys(obj);
+ if (keys) {
+ DohSortList(keys, cmp);
+ }
+ return keys;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetInt()
+ * ----------------------------------------------------------------------------- */
+
+int DohGetInt(DOH *obj, const DOH *name) {
+ DOH *val;
+ val = Getattr(obj, (DOH *) name);
+ if (!val)
+ return 0;
+ if (DohIsString(val)) {
+ return atoi((char *) Data(val));
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetDouble()
+ * ----------------------------------------------------------------------------- */
+
+double DohGetDouble(DOH *obj, const DOH *name) {
+ DOH *val;
+ val = Getattr(obj, (DOH *) name);
+ if (!val)
+ return 0;
+ if (DohIsString(val)) {
+ return atof((char *) Data(val));
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetChar()
+ * ----------------------------------------------------------------------------- */
+
+char *DohGetChar(DOH *obj, const DOH *name) {
+ DOH *val;
+ val = Getattr(obj, (DOH *) name);
+ if (!val)
+ return 0;
+ if (DohIsString(val)) {
+ return (char *) Data(val);
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetFlagAttr() / DohGetFlag()
+ * A flag is unset if the attribute (name) does not exist on the node (obj),
+ * or it is set to "0". If the attribute is set to any other value,
+ * the flag is set.
+ *
+ * DohGetFlag() returns if the flag is set or not
+ * DohGetFlagAttr() returns the flag value if is set, NULL otherwise
+ * ----------------------------------------------------------------------------- */
+
+
+DOH *DohGetFlagAttr(DOH *obj, const DOH *name) {
+ DOH *val = Getattr(obj, (DOH *) name);
+ if (!val) {
+ return NULL;
+ } else {
+ const char *cval = Char(val);
+ if (!cval)
+ return val;
+ return (strcmp(cval, "0") != 0) ? val : NULL;
+ }
+}
+
+int DohGetFlag(DOH *obj, const DOH *name) {
+ return DohGetFlagAttr(obj, name) ? 1 : 0;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * DohGetVoid()
+ * ----------------------------------------------------------------------------- */
+
+void *DohGetVoid(DOH *obj, const DOH *name) {
+ DOH *val;
+ val = Getattr(obj, (DOH *) name);
+ if (!val)
+ return 0;
+ return (void *) Data(val);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetInt()
+ * ----------------------------------------------------------------------------- */
+
+void DohSetInt(DOH *obj, const DOH *name, int value) {
+ DOH *temp;
+ temp = NewStringEmpty();
+ Printf(temp, "%d", value);
+ Setattr(obj, (DOH *) name, temp);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetDouble()
+ * ----------------------------------------------------------------------------- */
+
+void DohSetDouble(DOH *obj, const DOH *name, double value) {
+ DOH *temp;
+ temp = NewStringEmpty();
+ Printf(temp, "%0.17f", value);
+ Setattr(obj, (DOH *) name, temp);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetChar()
+ * ----------------------------------------------------------------------------- */
+
+void DohSetChar(DOH *obj, const DOH *name, char *value) {
+ Setattr(obj, (DOH *) name, NewString(value));
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetFlag()
+ * ----------------------------------------------------------------------------- */
+
+void DohSetFlagAttr(DOH *obj, const DOH *name, const DOH *attr) {
+ Setattr(obj, (DOH *) name, attr ? attr : NewString("0"));
+}
+
+void DohSetFlag(DOH *obj, const DOH *name) {
+ Setattr(obj, (DOH *) name, NewString("1"));
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetVoid()
+ * ----------------------------------------------------------------------------- */
+
+void DohSetVoid(DOH *obj, const DOH *name, void *value) {
+ Setattr(obj, (DOH *) name, NewVoid(value, 0));
+}
+
+/* -----------------------------------------------------------------------------
+ * DohIsSequence()
+ * ----------------------------------------------------------------------------- */
+
+int DohIsSequence(const DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (!DohCheck(b))
+ return 0;
+ objinfo = b->type;
+ if (objinfo->doh_list)
+ return 1;
+ else
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetitem()
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohGetitem(DOH *obj, int index) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_list && objinfo->doh_list->doh_getitem) {
+ return (objinfo->doh_list->doh_getitem) (b, index);
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetitem()
+ * ----------------------------------------------------------------------------- */
+
+int DohSetitem(DOH *obj, int index, const DOH *value) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_list && objinfo->doh_list->doh_setitem) {
+ return (objinfo->doh_list->doh_setitem) (b, index, (DOH *) value);
+ }
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohDelitem()
+ * ----------------------------------------------------------------------------- */
+
+int DohDelitem(DOH *obj, int index) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_list && objinfo->doh_list->doh_delitem) {
+ return (objinfo->doh_list->doh_delitem) (b, index);
+ }
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohInsertitem()
+ * ----------------------------------------------------------------------------- */
+
+int DohInsertitem(DOH *obj, int index, const DOH *value) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_list && objinfo->doh_list->doh_insitem) {
+ return (objinfo->doh_list->doh_insitem) (b, index, (DOH *) value);
+ }
+ return -1;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * DohDelslice()
+ * ----------------------------------------------------------------------------- */
+
+int DohDelslice(DOH *obj, int sindex, int eindex) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo = b->type;
+ if (objinfo->doh_list && objinfo->doh_list->doh_delslice) {
+ return (objinfo->doh_list->doh_delslice) (b, sindex, eindex);
+ }
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohIsFile()
+ * ----------------------------------------------------------------------------- */
+
+int DohIsFile(const DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (!DohCheck(b))
+ return 0;
+ objinfo = b->type;
+ if (objinfo->doh_file)
+ return 1;
+ else
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohRead()
+ * ----------------------------------------------------------------------------- */
+
+int DohRead(DOH *obj, void *buffer, int length) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (DohCheck(obj)) {
+ objinfo = b->type;
+ if ((objinfo->doh_file) && (objinfo->doh_file->doh_read)) {
+ return (objinfo->doh_file->doh_read) (b, buffer, length);
+ }
+ return -1;
+ }
+ /* Hmmm. Not a file. Maybe it's a real FILE */
+ return (int)fread(buffer, 1, length, (FILE *) b);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohWrite()
+ * ----------------------------------------------------------------------------- */
+
+int DohWrite(DOH *obj, const void *buffer, int length) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (DohCheck(obj)) {
+ objinfo = b->type;
+ if ((objinfo->doh_file) && (objinfo->doh_file->doh_write)) {
+ return (objinfo->doh_file->doh_write) (b, buffer, length);
+ }
+ return -1;
+ }
+ /* Hmmm. Not a file. Maybe it's a real FILE */
+ return (int)fwrite(buffer, 1, length, (FILE *) b);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSeek()
+ * ----------------------------------------------------------------------------- */
+
+int DohSeek(DOH *obj, long offset, int whence) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (DohCheck(obj)) {
+ objinfo = b->type;
+ if ((objinfo->doh_file) && (objinfo->doh_file->doh_seek)) {
+ return (objinfo->doh_file->doh_seek) (b, offset, whence);
+ }
+ return -1;
+ }
+ return fseek((FILE *) b, offset, whence);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohTell()
+ * ----------------------------------------------------------------------------- */
+
+long DohTell(DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (DohCheck(obj)) {
+ objinfo = b->type;
+ if ((objinfo->doh_file) && (objinfo->doh_file->doh_tell)) {
+ return (objinfo->doh_file->doh_tell) (b);
+ }
+ return -1;
+ }
+ return ftell((FILE *) b);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetc()
+ * ----------------------------------------------------------------------------- */
+
+int DohGetc(DOH *obj) {
+ static DOH *lastdoh = 0;
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (obj == lastdoh) {
+ objinfo = b->type;
+ return (objinfo->doh_file->doh_getc) (b);
+ }
+ if (DohCheck(obj)) {
+ objinfo = b->type;
+ if (objinfo->doh_file->doh_getc) {
+ lastdoh = obj;
+ return (objinfo->doh_file->doh_getc) (b);
+ }
+ return EOF;
+ }
+ return fgetc((FILE *) b);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohPutc()
+ * ----------------------------------------------------------------------------- */
+
+int DohPutc(int ch, DOH *obj) {
+ static DOH *lastdoh = 0;
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+
+ if (obj == lastdoh) {
+ objinfo = b->type;
+ return (objinfo->doh_file->doh_putc) (b, ch);
+ }
+ if (DohCheck(obj)) {
+ objinfo = b->type;
+ if (objinfo->doh_file->doh_putc) {
+ lastdoh = obj;
+ return (objinfo->doh_file->doh_putc) (b, ch);
+ }
+ return EOF;
+ }
+ return fputc(ch, (FILE *) b);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohUngetc()
+ * ----------------------------------------------------------------------------- */
+
+int DohUngetc(int ch, DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (DohCheck(obj)) {
+ objinfo = b->type;
+ if (objinfo->doh_file->doh_ungetc) {
+ return (objinfo->doh_file->doh_ungetc) (b, ch);
+ }
+ return EOF;
+ }
+ return ungetc(ch, (FILE *) b);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohIsString()
+ * ----------------------------------------------------------------------------- */
+
+int DohIsString(const DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ DohObjInfo *objinfo;
+ if (!DohCheck(b))
+ return 0;
+ objinfo = b->type;
+ if (objinfo->doh_string)
+ return 1;
+ else
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohReplace()
+ * ----------------------------------------------------------------------------- */
+
+int DohReplace(DOH *src, const DOH *token, const DOH *rep, int flags) {
+ DohBase *b = (DohBase *) src;
+ DohObjInfo *objinfo;
+ if (!token)
+ return 0;
+ if (!rep)
+ rep = "";
+ if (DohIsString(src)) {
+ objinfo = b->type;
+ if (objinfo->doh_string->doh_replace) {
+ return (objinfo->doh_string->doh_replace) (b, (DOH *) token, (DOH *) rep, flags);
+ }
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohChop()
+ * ----------------------------------------------------------------------------- */
+
+void DohChop(DOH *src) {
+ DohBase *b = (DohBase *) src;
+ DohObjInfo *objinfo;
+ if (DohIsString(src)) {
+ objinfo = b->type;
+ if (objinfo->doh_string->doh_chop) {
+ (objinfo->doh_string->doh_chop) (b);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetFile()
+ * ----------------------------------------------------------------------------- */
+void DohSetfile(DOH *ho, DOH *file) {
+ DohBase *h = (DohBase *) ho;
+ DohObjInfo *objinfo;
+ if (!h)
+ return;
+ objinfo = h->type;
+ if (objinfo->doh_setfile)
+ (objinfo->doh_setfile) (h, file);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetFile()
+ * ----------------------------------------------------------------------------- */
+DOH *DohGetfile(const DOH *ho) {
+ DohBase *h = (DohBase *) ho;
+ DohObjInfo *objinfo;
+ if (!h)
+ return 0;
+ objinfo = h->type;
+ if (objinfo->doh_getfile)
+ return (objinfo->doh_getfile) (h);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetLine()
+ * ----------------------------------------------------------------------------- */
+void DohSetline(DOH *ho, int l) {
+ DohBase *h = (DohBase *) ho;
+ DohObjInfo *objinfo;
+ if (!h)
+ return;
+ objinfo = h->type;
+ if (objinfo->doh_setline)
+ (objinfo->doh_setline) (h, l);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetLine()
+ * ----------------------------------------------------------------------------- */
+int DohGetline(const DOH *ho) {
+ DohBase *h = (DohBase *) ho;
+ DohObjInfo *objinfo;
+ if (!h)
+ return 0;
+ objinfo = h->type;
+ if (objinfo->doh_getline)
+ return (objinfo->doh_getline) (h);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetmeta()
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohGetmeta(DOH *ho, const DOH *name) {
+ DohBase *h = (DohBase *) ho;
+ if (!DohCheck(ho))
+ return 0;
+ if (!h->meta)
+ return 0;
+ return DohGetattr(h->meta, name);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetmeta()
+ * ----------------------------------------------------------------------------- */
+
+int DohSetmeta(DOH *ho, const DOH *name, const DOH *value) {
+ DohBase *h = (DohBase *) ho;
+ if (!DohCheck(ho))
+ return 0;
+ if (!h->meta)
+ h->meta = NewHash();
+ return DohSetattr(h->meta, name, value);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohDelmeta()
+ * ----------------------------------------------------------------------------- */
+
+int DohDelmeta(DOH *ho, const DOH *name) {
+ DohBase *h = (DohBase *) ho;
+ if (!DohCheck(ho))
+ return 0;
+ if (!h->meta)
+ return 0;
+ return DohDelattr(h->meta, name);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetmark()
+ * ----------------------------------------------------------------------------- */
+
+void DohSetmark(DOH *ho, int x) {
+ DohBase *h = (DohBase *) ho;
+ h->flag_usermark = x;
+}
+
+int DohGetmark(DOH *ho) {
+ DohBase *h = (DohBase *) ho;
+ return h->flag_usermark;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohCall()
+ *
+ * Invokes a function via DOH. A Function is represented by a hash table with
+ * the following attributes:
+ *
+ * "builtin" - Pointer to built-in function (if any)
+ *
+ * (Additional attributes may be added later)
+ *
+ * Returns a DOH object with result on success. Returns NULL on error
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohCall(DOH *func, DOH *args) {
+ DOH *result;
+ DohFuncPtr_t builtin;
+
+ builtin.p = GetVoid(func, "builtin");
+
+ if (!builtin.p)
+ return 0;
+ result = (*builtin.func) (args);
+ return result;
+}
diff --git a/contrib/tools/swig/Source/DOH/doh.h b/contrib/tools/swig/Source/DOH/doh.h
new file mode 100644
index 0000000000..45a1f7fc89
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/doh.h
@@ -0,0 +1,507 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doh.h
+ *
+ * This file describes of the externally visible functions in DOH.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_DOH_H
+#define SWIG_DOH_H
+
+#include "swigconfig.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Set the namespace prefix for DOH API functions. This can be used to control
+ visibility of the functions in libraries */
+
+/* Set this macro if you want to change DOH linkage. You would do this if you
+ wanted to hide DOH in a library using a different set of names. Note: simply
+ change "Doh" to a new name. */
+
+/*
+#define DOH_NAMESPACE(x) Doh ## x
+*/
+
+#ifdef DOH_NAMESPACE
+
+/* Namespace control. These macros define all of the public API names in DOH */
+
+#define DohCheck DOH_NAMESPACE(Check)
+#define DohIntern DOH_NAMESPACE(Intern)
+#define DohDelete DOH_NAMESPACE(Delete)
+#define DohCopy DOH_NAMESPACE(Copy)
+#define DohClear DOH_NAMESPACE(Clear)
+#define DohStr DOH_NAMESPACE(Str)
+#define DohData DOH_NAMESPACE(Data)
+#define DohDump DOH_NAMESPACE(Dump)
+#define DohLen DOH_NAMESPACE(Len)
+#define DohHashval DOH_NAMESPACE(Hashval)
+#define DohCmp DOH_NAMESPACE(Cmp)
+#define DohEqual DOH_NAMESPACE(Equal)
+#define DohIncref DOH_NAMESPACE(Incref)
+#define DohCheckattr DOH_NAMESPACE(Checkattr)
+#define DohSetattr DOH_NAMESPACE(Setattr)
+#define DohDelattr DOH_NAMESPACE(Delattr)
+#define DohKeys DOH_NAMESPACE(Keys)
+#define DohSortedKeys DOH_NAMESPACE(SortedKeys)
+#define DohGetInt DOH_NAMESPACE(GetInt)
+#define DohGetDouble DOH_NAMESPACE(GetDouble)
+#define DohGetChar DOH_NAMESPACE(GetChar)
+#define DohSetChar DOH_NAMESPACE(SetChar)
+#define DohSetInt DOH_NAMESPACE(SetInt)
+#define DohSetDouble DOH_NAMESPACE(SetDouble)
+#define DohSetVoid DOH_NAMESPACE(SetVoid)
+#define DohGetVoid DOH_NAMESPACE(GetVoid)
+#define DohGetitem DOH_NAMESPACE(Getitem)
+#define DohSetitem DOH_NAMESPACE(Setitem)
+#define DohDelitem DOH_NAMESPACE(Delitem)
+#define DohInsertitem DOH_NAMESPACE(Insertitem)
+#define DohDelslice DOH_NAMESPACE(Delslice)
+#define DohWrite DOH_NAMESPACE(Write)
+#define DohRead DOH_NAMESPACE(Read)
+#define DohSeek DOH_NAMESPACE(Seek)
+#define DohTell DOH_NAMESPACE(Tell)
+#define DohGetc DOH_NAMESPACE(Getc)
+#define DohPutc DOH_NAMESPACE(Putc)
+#define DohUngetc DOH_NAMESPACE(Ungetc)
+#define DohGetline DOH_NAMESPACE(Getline)
+#define DohSetline DOH_NAMESPACE(Setline)
+#define DohGetfile DOH_NAMESPACE(Getfile)
+#define DohSetfile DOH_NAMESPACE(Setfile)
+#define DohReplace DOH_NAMESPACE(Replace)
+#define DohChop DOH_NAMESPACE(Chop)
+#define DohGetmeta DOH_NAMESPACE(Getmeta)
+#define DohSetmeta DOH_NAMESPACE(Setmeta)
+#define DohDelmeta DOH_NAMESPACE(Delmeta)
+#define DohEncoding DOH_NAMESPACE(Encoding)
+#define DohPrintf DOH_NAMESPACE(Printf)
+#define DohvPrintf DOH_NAMESPACE(vPrintf)
+#define DohPrintv DOH_NAMESPACE(Printv)
+#define DohReadline DOH_NAMESPACE(Readline)
+#define DohIsMapping DOH_NAMESPACE(IsMapping)
+#define DohIsSequence DOH_NAMESPACE(IsSequence)
+#define DohIsString DOH_NAMESPACE(IsString)
+#define DohIsFile DOH_NAMESPACE(IsFile)
+#define DohNewString DOH_NAMESPACE(NewString)
+#define DohNewStringEmpty DOH_NAMESPACE(NewStringEmpty)
+#define DohNewStringWithSize DOH_NAMESPACE(NewStringWithSize)
+#define DohNewStringf DOH_NAMESPACE(NewStringf)
+#define DohStrcmp DOH_NAMESPACE(Strcmp)
+#define DohStrncmp DOH_NAMESPACE(Strncmp)
+#define DohStrstr DOH_NAMESPACE(Strstr)
+#define DohStrchr DOH_NAMESPACE(Strchr)
+#define DohNewFile DOH_NAMESPACE(NewFile)
+#define DohNewFileFromFile DOH_NAMESPACE(NewFileFromFile)
+#define DohNewFileFromFd DOH_NAMESPACE(NewFileFromFd)
+#define DohFileErrorDisplay DOH_NAMESPACE(FileErrorDisplay)
+#define DohCopyto DOH_NAMESPACE(Copyto)
+#define DohNewList DOH_NAMESPACE(NewList)
+#define DohNewHash DOH_NAMESPACE(NewHash)
+#define DohNewVoid DOH_NAMESPACE(NewVoid)
+#define DohSplit DOH_NAMESPACE(Split)
+#define DohSplitLines DOH_NAMESPACE(SplitLines)
+#define DohNone DOH_NAMESPACE(None)
+#define DohCall DOH_NAMESPACE(Call)
+#define DohObjMalloc DOH_NAMESPACE(ObjMalloc)
+#define DohObjFree DOH_NAMESPACE(ObjFree)
+#define DohMemoryDebug DOH_NAMESPACE(MemoryDebug)
+#define DohStringType DOH_NAMESPACE(StringType)
+#define DohListType DOH_NAMESPACE(ListType)
+#define DohHashType DOH_NAMESPACE(HashType)
+#define DohFileType DOH_NAMESPACE(FileType)
+#define DohVoidType DOH_NAMESPACE(VoidType)
+#define DohIterator DOH_NAMESPACE(Iterator)
+#define DohFirst DOH_NAMESPACE(First)
+#define DohNext DOH_NAMESPACE(Next)
+#define DohMalloc DOH_NAMESPACE(Malloc)
+#define DohRealloc DOH_NAMESPACE(Realloc)
+#define DohCalloc DOH_NAMESPACE(Calloc)
+#define DohFree DOH_NAMESPACE(Free)
+#define DohSetExitHandler DOH_NAMESPACE(SetExitHandler)
+#define DohExit DOH_NAMESPACE(Exit)
+#endif
+
+#define DOH_MAJOR_VERSION 0
+#define DOH_MINOR_VERSION 1
+
+typedef void DOH;
+
+/*
+ * With dynamic typing, all DOH objects are technically of type 'void *'.
+ * However, to clarify the reading of source code, the following symbolic
+ * names are used.
+ */
+
+#define DOHString DOH
+#define DOHList DOH
+#define DOHHash DOH
+#define DOHFile DOH
+#define DOHVoid DOH
+#define DOHString_or_char DOH
+#define DOHObj_or_char DOH
+
+typedef const DOHString_or_char * const_String_or_char_ptr;
+typedef const DOHString_or_char * DOHconst_String_or_char_ptr;
+
+#define DOH_BEGIN -1
+#define DOH_END -2
+#define DOH_CUR -3
+#define DOH_CURRENT -3
+
+/* Iterator objects */
+
+typedef struct {
+ void *key; /* Current key (if any) */
+ void *item; /* Current item */
+ void *object; /* Object being iterated over */
+ void *_current; /* Internal use */
+ int _index; /* Internal use */
+} DohIterator;
+
+/* Memory management */
+
+/* Wrappers around malloc(), realloc() and calloc() which never return NULL. */
+extern void *DohMalloc(size_t size);
+extern void *DohRealloc(void *ptr, size_t size);
+extern void *DohCalloc(size_t n, size_t size);
+
+#ifndef DohFree
+#define DohFree free
+#endif
+
+extern int DohCheck(const DOH *ptr); /* Check if a DOH object */
+extern void DohIntern(DOH *); /* Intern an object */
+
+/* Basic object methods. Common to most objects */
+
+extern void DohDelete(DOH *obj); /* Delete an object */
+extern DOH *DohCopy(const DOH *obj);
+extern void DohClear(DOH *obj);
+extern DOHString *DohStr(const DOH *obj);
+extern void *DohData(const DOH *obj);
+extern int DohDump(const DOH *obj, DOHFile * out);
+extern int DohLen(const DOH *obj);
+extern int DohHashval(const DOH *obj);
+extern int DohCmp(const DOH *obj1, const DOH *obj2);
+extern int DohEqual(const DOH *obj1, const DOH *obj2);
+extern void DohIncref(DOH *obj);
+
+/* Mapping methods */
+
+extern DOH *DohGetattr(DOH *obj, const DOHString_or_char *name);
+extern int DohSetattr(DOH *obj, const DOHString_or_char *name, const DOHObj_or_char * value);
+extern int DohDelattr(DOH *obj, const DOHString_or_char *name);
+extern int DohCheckattr(DOH *obj, const DOHString_or_char *name, const DOHString_or_char *value);
+extern DOH *DohKeys(DOH *obj);
+extern DOH *DohSortedKeys(DOH *obj, int (*cmp) (const DOH *, const DOH *));
+extern int DohGetInt(DOH *obj, const DOHString_or_char *name);
+extern void DohSetInt(DOH *obj, const DOHString_or_char *name, int);
+extern double DohGetDouble(DOH *obj, const DOHString_or_char *name);
+extern void DohSetDouble(DOH *obj, const DOHString_or_char *name, double);
+extern char *DohGetChar(DOH *obj, const DOHString_or_char *name);
+extern void DohSetChar(DOH *obj, const DOH *name, char *value);
+extern void *DohGetFlagAttr(DOH *obj, const DOHString_or_char *name);
+extern int DohGetFlag(DOH *obj, const DOHString_or_char *name);
+extern void DohSetFlagAttr(DOH *obj, const DOHString_or_char *name, const DOHString_or_char *attr);
+extern void DohSetFlag(DOH *obj, const DOHString_or_char *name);
+extern void *DohGetVoid(DOH *obj, const DOHString_or_char *name);
+extern void DohSetVoid(DOH *obj, const DOHString_or_char *name, void *value);
+
+/* Sequence methods */
+
+extern DOH *DohGetitem(DOH *obj, int index);
+extern int DohSetitem(DOH *obj, int index, const DOHObj_or_char * value);
+extern int DohDelitem(DOH *obj, int index);
+extern int DohInsertitem(DOH *obj, int index, const DOHObj_or_char * value);
+extern int DohDelslice(DOH *obj, int sindex, int eindex);
+
+/* File methods */
+
+extern int DohWrite(DOHFile * obj, const void *buffer, int length);
+extern int DohRead(DOHFile * obj, void *buffer, int length);
+extern int DohSeek(DOHFile * obj, long offset, int whence);
+extern long DohTell(DOHFile * obj);
+extern int DohGetc(DOHFile * obj);
+extern int DohPutc(int ch, DOHFile * obj);
+extern int DohUngetc(int ch, DOHFile * obj);
+
+
+
+/* Iterators */
+extern DohIterator DohFirst(DOH *obj);
+extern DohIterator DohNext(DohIterator x);
+
+/* Positional */
+
+extern int DohGetline(const DOH *obj);
+extern void DohSetline(DOH *obj, int line);
+extern DOH *DohGetfile(const DOH *obj);
+extern void DohSetfile(DOH *obj, DOH *file);
+
+ /* String Methods */
+
+extern int DohReplace(DOHString * src, const DOHString_or_char *token, const DOHString_or_char *rep, int flags);
+extern void DohChop(DOHString * src);
+
+/* Meta-variables */
+extern DOH *DohGetmeta(DOH *, const DOH *);
+extern int DohSetmeta(DOH *, const DOH *, const DOH *value);
+extern int DohDelmeta(DOH *, const DOH *);
+
+ /* Utility functions */
+
+extern void DohEncoding(const char *name, DOH *(*fn) (DOH *s));
+extern int DohPrintf(DOHFile * obj, const char *format, ...);
+extern int DohvPrintf(DOHFile * obj, const char *format, va_list ap);
+extern int DohPrintv(DOHFile * obj, ...);
+extern DOH *DohReadline(DOHFile * in);
+
+ /* Miscellaneous */
+
+extern int DohIsMapping(const DOH *obj);
+extern int DohIsSequence(const DOH *obj);
+extern int DohIsString(const DOH *obj);
+extern int DohIsFile(const DOH *obj);
+
+extern void DohSetMaxHashExpand(int count);
+extern int DohGetMaxHashExpand(void);
+extern void DohSetmark(DOH *obj, int x);
+extern int DohGetmark(DOH *obj);
+
+/* Set the function for DohExit() to call instead of exit().
+ *
+ * The registered function can perform clean up, etc. It should simply
+ * return when done and then exit() will get called. Bear in mind that
+ * the handler function can be called after malloc() has failed, so it's
+ * a good idea for it to avoid allocating additional memory.
+ *
+ * The registered handler function is unregistered by DohExit() before calling
+ * it to avoid the potential for infinite loops.
+ *
+ * Note: This is sort of like C's atexit(), only for DohExit(). However
+ * only one function can be registered (setting a new function overrides the
+ * previous one) and the registered function is passed the exit status so can
+ * vary its actions based on that.
+ */
+extern void DohSetExitHandler(void (*new_handler)(int));
+extern void DohExit(int status);
+
+/* -----------------------------------------------------------------------------
+ * Strings.
+ * ----------------------------------------------------------------------------- */
+
+extern DOHString *DohNewStringEmpty(void);
+extern DOHString *DohNewString(const DOHString_or_char *c);
+extern DOHString *DohNewStringWithSize(const DOHString_or_char *c, int len);
+extern DOHString *DohNewStringf(const DOHString_or_char *fmt, ...);
+
+extern int DohStrcmp(const DOHString_or_char *s1, const DOHString_or_char *s2);
+extern int DohStrncmp(const DOHString_or_char *s1, const DOHString_or_char *s2, int n);
+extern char *DohStrstr(const DOHString_or_char *s1, const DOHString_or_char *s2);
+extern char *DohStrchr(const DOHString_or_char *s1, int ch);
+
+/* String replacement flags */
+
+#define DOH_REPLACE_ANY 0x01
+#define DOH_REPLACE_NOQUOTE 0x02
+#define DOH_REPLACE_NOCOMMENT 0x04
+#define DOH_REPLACE_ID 0x08
+#define DOH_REPLACE_FIRST 0x10
+#define DOH_REPLACE_ID_BEGIN 0x20
+#define DOH_REPLACE_ID_END 0x40
+#define DOH_REPLACE_NUMBER_END 0x80
+
+#define Replaceall(s,t,r) DohReplace(s,t,r,DOH_REPLACE_ANY)
+#define Replaceid(s,t,r) DohReplace(s,t,r,DOH_REPLACE_ID)
+
+/* -----------------------------------------------------------------------------
+ * Files
+ * ----------------------------------------------------------------------------- */
+
+extern DOHFile *DohNewFile(DOHString *filename, const char *mode, DOHList *outfiles);
+extern DOHFile *DohNewFileFromFile(FILE *f);
+extern DOHFile *DohNewFileFromFd(int fd);
+extern void DohFileErrorDisplay(DOHString * filename);
+extern int DohCopyto(DOHFile * input, DOHFile * output);
+extern void DohCloseAllOpenFiles(void);
+
+
+/* -----------------------------------------------------------------------------
+ * List
+ * ----------------------------------------------------------------------------- */
+
+extern DOHList *DohNewList(void);
+extern void DohSortList(DOH *lo, int (*cmp) (const DOH *, const DOH *));
+
+/* -----------------------------------------------------------------------------
+ * Hash
+ * ----------------------------------------------------------------------------- */
+
+extern DOHHash *DohNewHash(void);
+
+/* -----------------------------------------------------------------------------
+ * Void
+ * ----------------------------------------------------------------------------- */
+
+extern DOHVoid *DohNewVoid(void *ptr, void (*del) (void *));
+extern DOHList *DohSplit(DOHFile * input, char ch, int nsplits);
+extern DOHList *DohSplitLines(DOHFile * input);
+extern DOH *DohNone;
+
+/* Helper union for converting between function and object pointers. */
+typedef union DohFuncPtr {
+ void* p;
+ DOH *(*func)(DOH *);
+} DohFuncPtr_t;
+
+extern void DohMemoryDebug(void);
+
+#ifndef DOH_LONG_NAMES
+/* Macros to invoke the above functions. Includes the location of
+ the caller to simplify debugging if something goes wrong */
+
+#define Delete DohDelete
+#define Copy DohCopy
+#define Clear DohClear
+#define Str DohStr
+#define Dump DohDump
+#define Getattr DohGetattr
+#define Setattr DohSetattr
+#define Delattr DohDelattr
+#define Checkattr DohCheckattr
+#define Hashval DohHashval
+#define Getitem DohGetitem
+#define Setitem DohSetitem
+#define Delitem DohDelitem
+#define Insert DohInsertitem
+#define Delslice DohDelslice
+#define Append(s,x) DohInsertitem(s,DOH_END,x)
+#define Push(s,x) DohInsertitem(s,DOH_BEGIN,x)
+#define Len DohLen
+#define Data DohData
+#define Char(X) ((char *) Data(X))
+#define Cmp DohCmp
+#define Equal DohEqual
+#define Setline DohSetline
+#define Getline DohGetline
+#define Setfile DohSetfile
+#define Getfile DohGetfile
+#define Write DohWrite
+#define Read DohRead
+#define Seek DohSeek
+#define Tell DohTell
+#define Printf DohPrintf
+#define Printv DohPrintv
+#define Getc DohGetc
+#define Putc DohPutc
+#define Ungetc DohUngetc
+
+/* #define StringPutc DohStringPutc */
+/* #define StringGetc DohStringGetc */
+/* #define StringUngetc DohStringUngetc */
+/* #define StringAppend Append */
+/* #define StringLen DohStringLen */
+/* #define StringChar DohStringChar */
+/* #define StringEqual DohStringEqual */
+
+#define vPrintf DohvPrintf
+#define GetInt DohGetInt
+#define GetDouble DohGetDouble
+#define GetChar DohGetChar
+#define GetVoid DohGetVoid
+#define GetFlagAttr DohGetFlagAttr
+#define GetFlag DohGetFlag
+#define SetInt DohSetInt
+#define SetDouble DohSetDouble
+#define SetChar DohSetattr
+#define SetVoid DohSetVoid
+#define SetFlagAttr DohSetFlagAttr
+#define SetFlag DohSetFlag
+#define UnsetFlag(o,n) DohSetFlagAttr(o,n,NULL)
+#define ClearFlag(o,n) DohSetFlagAttr(o,n,"")
+#define Readline DohReadline
+#define Replace DohReplace
+#define Chop DohChop
+#define Getmeta DohGetmeta
+#define Setmeta DohSetmeta
+#define Delmeta DohDelmeta
+#define NewString DohNewString
+#define NewStringEmpty DohNewStringEmpty
+#define NewStringWithSize DohNewStringWithSize
+#define NewStringf DohNewStringf
+#define NewHash DohNewHash
+#define NewList DohNewList
+#define NewFile DohNewFile
+#define NewFileFromFile DohNewFileFromFile
+#define NewFileFromFd DohNewFileFromFd
+#define FileErrorDisplay DohFileErrorDisplay
+#define NewVoid DohNewVoid
+#define Keys DohKeys
+#define SortedKeys DohSortedKeys
+#define Strcmp DohStrcmp
+#define Strncmp DohStrncmp
+#define Strstr DohStrstr
+#define Strchr DohStrchr
+#define Copyto DohCopyto
+#define CloseAllOpenFiles DohCloseAllOpenFiles
+#define Split DohSplit
+#define SplitLines DohSplitLines
+#define Setmark DohSetmark
+#define Getmark DohGetmark
+#define SetMaxHashExpand DohSetMaxHashExpand
+#define GetMaxHashExpand DohGetMaxHashExpand
+#define None DohNone
+#define Call DohCall
+#define First DohFirst
+#define Next DohNext
+#define Iterator DohIterator
+#define SortList DohSortList
+#define Malloc DohMalloc
+#define Realloc DohRealloc
+#define Calloc DohCalloc
+#define Free DohFree
+#define SetExitHandler DohSetExitHandler
+#define Exit DohExit
+#endif
+
+#ifdef NIL
+#undef NIL
+#endif
+
+#define NIL (char *) NULL
+
+/* Defines to allow use of poisoned identifiers.
+ *
+ * For DOH-internal use only!
+ */
+#define doh_internal_calloc calloc
+#define doh_internal_exit exit
+/* doh_internal_free not needed as Free() is a macro defined above. */
+#define doh_internal_malloc malloc
+#define doh_internal_realloc realloc
+
+#if defined __GNUC__ && defined DOH_POISON
+/* Use Malloc(), Realloc(), Calloc(), and Free() instead (which will exit with
+ * an error rather than return NULL).
+ */
+# ifndef DOH_NO_POISON_MALLOC_FREE
+/* This works around bison's template checking if malloc and free are defined,
+ * which triggers GCC's poison checks.
+ */
+# pragma GCC poison malloc free
+# endif
+# pragma GCC poison realloc calloc
+/* Use Exit() instead (which will remove output files on error). */
+# pragma GCC poison abort exit
+#endif
+
+#endif /* SWIG_DOH_H */
diff --git a/contrib/tools/swig/Source/DOH/dohint.h b/contrib/tools/swig/Source/DOH/dohint.h
new file mode 100644
index 0000000000..b637debd26
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/dohint.h
@@ -0,0 +1,131 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * dohint.h
+ *
+ * This file describes internally managed objects.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_DOHINT_H
+#define SWIG_DOHINT_H
+
+#include "doh.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+/* Hash objects */
+typedef struct {
+ DOH *(*doh_getattr) (DOH *obj, DOH *name); /* Get attribute */
+ int (*doh_setattr) (DOH *obj, DOH *name, DOH *value); /* Set attribute */
+ int (*doh_delattr) (DOH *obj, DOH *name); /* Del attribute */
+ DOH *(*doh_keys) (DOH *obj); /* All keys as a list */
+} DohHashMethods;
+
+/* List objects */
+typedef struct {
+ DOH *(*doh_getitem) (DOH *obj, int index); /* Get item */
+ int (*doh_setitem) (DOH *obj, int index, DOH *value); /* Set item */
+ int (*doh_delitem) (DOH *obj, int index); /* Delete item */
+ int (*doh_insitem) (DOH *obj, int index, DOH *value); /* Insert item */
+ int (*doh_delslice) (DOH *obj, int sindex, int eindex); /* Delete slice */
+} DohListMethods;
+
+/* File methods */
+typedef struct {
+ int (*doh_read) (DOH *obj, void *buffer, int nbytes); /* Read bytes */
+ int (*doh_write) (DOH *obj, const void *buffer, int nbytes); /* Write bytes */
+ int (*doh_putc) (DOH *obj, int ch); /* Put character */
+ int (*doh_getc) (DOH *obj); /* Get character */
+ int (*doh_ungetc) (DOH *obj, int ch); /* Unget character */
+ int (*doh_seek) (DOH *obj, long offset, int whence); /* Seek */
+ long (*doh_tell) (DOH *obj); /* Tell */
+} DohFileMethods;
+
+/* String methods */
+typedef struct {
+ int (*doh_replace) (DOH *obj, const DOHString_or_char *old, const DOHString_or_char *rep, int flags);
+ void (*doh_chop) (DOH *obj);
+} DohStringMethods;
+
+/* -----------------------------------------------------------------------------
+ * DohObjInfo
+ * ----------------------------------------------------------------------------- */
+
+typedef struct DohObjInfo {
+ const char *objname; /* Object name */
+
+ /* Basic object methods */
+ void (*doh_del) (DOH *obj); /* Delete object */
+ DOH *(*doh_copy) (DOH *obj); /* Copy and object */
+ void (*doh_clear) (DOH *obj); /* Clear an object */
+
+ /* I/O methods */
+ DOH *(*doh_str) (DOH *obj); /* Make a full string */
+ void *(*doh_data) (DOH *obj); /* Return raw data */
+ int (*doh_dump) (DOH *obj, DOH *out); /* Serialize on out */
+
+ /* Length and hash values */
+ int (*doh_len) (DOH *obj);
+ int (*doh_hashval) (DOH *obj);
+
+ /* Compare */
+ int (*doh_cmp) (DOH *obj1, DOH *obj2);
+
+ /* Equal */
+ int (*doh_equal) (DOH *obj1, DOH *obj2);
+
+ /* Iterators */
+ DohIterator (*doh_first) (DOH *obj);
+ DohIterator (*doh_next) (DohIterator);
+
+ /* Positional */
+ void (*doh_setfile) (DOH *obj, DOHString_or_char *file);
+ DOH *(*doh_getfile) (DOH *obj);
+ void (*doh_setline) (DOH *obj, int line);
+ int (*doh_getline) (DOH *obj);
+
+ DohHashMethods *doh_hash; /* Hash methods */
+ DohListMethods *doh_list; /* List methods */
+ DohFileMethods *doh_file; /* File methods */
+ DohStringMethods *doh_string; /* String methods */
+ void *doh_reserved; /* Reserved */
+ void *clientdata; /* User data */
+} DohObjInfo;
+
+typedef struct {
+ void *data; /* Data pointer */
+ DohObjInfo *type;
+ void *meta; /* Meta data */
+ unsigned int flag_intern:1; /* Interned object */
+ unsigned int flag_marked:1; /* Mark flag. Used to avoid recursive loops in places */
+ unsigned int flag_user:1; /* User flag */
+ unsigned int flag_usermark:1; /* User marked */
+ unsigned int refcount:28; /* Reference count (max 16 million) */
+} DohBase;
+
+/* Macros for decrefing and increfing (safe for null objects). */
+
+#define Decref(a) if (a) ((DohBase *) a)->refcount--
+#define Incref(a) if (a) ((DohBase *) a)->refcount++
+#define Refcount(a) ((DohBase *) a)->refcount
+
+/* Macros for manipulating objects in a safe manner */
+#define ObjData(a) ((DohBase *)a)->data
+#define ObjSetMark(a,x) ((DohBase *)a)->flag_marked = x
+#define ObjGetMark(a) ((DohBase *)a)->flag_marked
+#define ObjType(a) ((DohBase *)a)->type
+
+extern DOH *DohObjMalloc(DohObjInfo *type, void *data); /* Allocate a DOH object */
+extern void DohObjFree(DOH *ptr); /* Free a DOH object */
+
+#endif /* SWIG_DOHINT_H */
diff --git a/contrib/tools/swig/Source/DOH/file.c b/contrib/tools/swig/Source/DOH/file.c
new file mode 100644
index 0000000000..4bcf5d5e1d
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/file.c
@@ -0,0 +1,342 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * file.c
+ *
+ * This file implements a file-like object that can be built around an
+ * ordinary FILE * or integer file descriptor.
+ * ----------------------------------------------------------------------------- */
+
+#include "dohint.h"
+
+#ifdef DOH_INTFILE
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+typedef struct {
+ FILE *filep;
+ int fd;
+ int closeondel;
+} DohFile;
+
+/* -----------------------------------------------------------------------------
+ * open_files_list_instance
+ * open_files_list_add
+ * open_files_list_remove
+ *
+ * Singleton list containing all the files that have been opened by DohNewFile.
+ * Open file pointers are held in the list as strings so as to not affect the
+ * reference count of the underlying DOH objects.
+ * ----------------------------------------------------------------------------- */
+
+static DOHList *open_files_list_instance(void) {
+ static DOHList *all_open_files = 0;
+ if (!all_open_files)
+ all_open_files = DohNewList();
+ return all_open_files;
+}
+
+static void open_files_list_add(DohFile *f) {
+ DOHList *all_open_files = open_files_list_instance();
+ DOHString *sf = NewStringf("%p", f);
+ Append(all_open_files, sf);
+ Delete(sf);
+}
+
+static void open_files_list_remove(DohFile *f) {
+ int i;
+ int removed = 0;
+ DOHList *all_open_files = open_files_list_instance();
+ DOHString *sf = NewStringf("%p", f);
+ for (i = 0; i < DohLen(all_open_files); i++) {
+ DOHString *sf_i = Getitem(all_open_files, i);
+ if (Strcmp(sf, sf_i) == 0) {
+ DohDelitem(all_open_files, i);
+ removed = 1;
+ break;
+ }
+ }
+ Delete(sf);
+ assert(removed);
+ (void)removed;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohCloseAllOpenFiles()
+ *
+ * Close all opened files, to be called on program termination
+ * ----------------------------------------------------------------------------- */
+
+void DohCloseAllOpenFiles(void) {
+ int i;
+ DOHList *all_open_files = open_files_list_instance();
+ for (i = 0; i < DohLen(all_open_files); i++) {
+ DohFile *f = 0;
+ DOHString *sf = Getitem(all_open_files, i);
+ int check = sscanf(Char(sf), "%p", (void **)&f);
+ assert(check == 1);
+ (void)check;
+ if (f->closeondel) {
+ if (f->filep) {
+ check = fclose(f->filep);
+ assert(check == 0);
+ }
+ f->closeondel = 0;
+ f->filep = 0;
+ }
+ }
+ DohClear(all_open_files);
+}
+
+/* -----------------------------------------------------------------------------
+ * DelFile()
+ * ----------------------------------------------------------------------------- */
+
+static void DelFile(DOH *fo) {
+ DohFile *f = (DohFile *) ObjData(fo);
+ if (f->closeondel) {
+ if (f->filep) {
+ fclose(f->filep);
+ }
+#ifdef DOH_INTFILE
+ if (f->fd) {
+ close(f->fd);
+ }
+#endif
+ open_files_list_remove(f);
+ }
+ DohFree(f);
+}
+
+/* -----------------------------------------------------------------------------
+ * File_read()
+ * ----------------------------------------------------------------------------- */
+
+static int File_read(DOH *fo, void *buffer, int len) {
+ DohFile *f = (DohFile *) ObjData(fo);
+
+ if (f->filep) {
+ return (int)fread(buffer, 1, len, f->filep);
+ } else if (f->fd) {
+#ifdef DOH_INTFILE
+ return read(f->fd, buffer, len);
+#endif
+ }
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * File_write()
+ * ----------------------------------------------------------------------------- */
+
+static int File_write(DOH *fo, const void *buffer, int len) {
+ DohFile *f = (DohFile *) ObjData(fo);
+ if (f->filep) {
+ int ret = (int) fwrite(buffer, 1, len, f->filep);
+ int err = (ret != len) ? ferror(f->filep) : 0;
+ return err ? -1 : ret;
+ } else if (f->fd) {
+#ifdef DOH_INTFILE
+ return write(f->fd, buffer, len);
+#endif
+ }
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * File_seek()
+ * ----------------------------------------------------------------------------- */
+
+static int File_seek(DOH *fo, long offset, int whence) {
+ DohFile *f = (DohFile *) ObjData(fo);
+ if (f->filep) {
+ return fseek(f->filep, offset, whence);
+ } else if (f->fd) {
+#ifdef DOH_INTFILE
+ return lseek(f->fd, offset, whence);
+#endif
+ }
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * File_tell()
+ * ----------------------------------------------------------------------------- */
+
+static long File_tell(DOH *fo) {
+ DohFile *f = (DohFile *) ObjData(fo);
+ if (f->filep) {
+ return ftell(f->filep);
+ } else if (f->fd) {
+#ifdef DOH_INTFILE
+ return lseek(f->fd, 0, SEEK_CUR);
+#endif
+ }
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * File_putc()
+ * ----------------------------------------------------------------------------- */
+
+static int File_putc(DOH *fo, int ch) {
+ DohFile *f = (DohFile *) ObjData(fo);
+ if (f->filep) {
+ return fputc(ch, f->filep);
+ } else if (f->fd) {
+#ifdef DOH_INTFILE
+ char c;
+ c = (char) ch;
+ return write(f->fd, &c, 1);
+#endif
+ }
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * File_getc()
+ * ----------------------------------------------------------------------------- */
+
+static int File_getc(DOH *fo) {
+ DohFile *f = (DohFile *) ObjData(fo);
+ if (f->filep) {
+ return fgetc(f->filep);
+ } else if (f->fd) {
+#ifdef DOH_INTFILE
+ unsigned char c;
+ if (read(f->fd, &c, 1) < 0)
+ return EOF;
+ return c;
+#endif
+ }
+ return EOF;
+}
+
+/* -----------------------------------------------------------------------------
+ * File_ungetc()
+ *
+ * Put a character back onto the input
+ * ----------------------------------------------------------------------------- */
+
+static int File_ungetc(DOH *fo, int ch) {
+ DohFile *f = (DohFile *) ObjData(fo);
+ if (f->filep) {
+ return ungetc(ch, f->filep);
+ } else if (f->fd) {
+#ifdef DOH_INTFILE
+ /* Not implemented yet */
+#endif
+ }
+ return -1;
+}
+
+static DohFileMethods FileFileMethods = {
+ File_read,
+ File_write,
+ File_putc,
+ File_getc,
+ File_ungetc,
+ File_seek,
+ File_tell,
+};
+
+static DohObjInfo DohFileType = {
+ "DohFile", /* objname */
+ DelFile, /* doh_del */
+ 0, /* doh_copy */
+ 0, /* doh_clear */
+ 0, /* doh_str */
+ 0, /* doh_data */
+ 0, /* doh_dump */
+ 0, /* doh_len */
+ 0, /* doh_hash */
+ 0, /* doh_cmp */
+ 0, /* doh_equal */
+ 0, /* doh_first */
+ 0, /* doh_next */
+ 0, /* doh_setfile */
+ 0, /* doh_getfile */
+ 0, /* doh_setline */
+ 0, /* doh_getline */
+ 0, /* doh_mapping */
+ 0, /* doh_sequence */
+ &FileFileMethods, /* doh_file */
+ 0, /* doh_string */
+ 0, /* doh_callable */
+ 0, /* doh_position */
+};
+
+/* -----------------------------------------------------------------------------
+ * NewFile()
+ *
+ * Create a new file from a given filename and mode.
+ * If newfiles is non-zero, the filename is added to the list of new files.
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohNewFile(DOHString *filename, const char *mode, DOHList *newfiles) {
+ DohFile *f;
+ DOH *obj;
+ FILE *file;
+ char *filen;
+
+ filen = Char(filename);
+ file = fopen(filen, mode);
+ if (!file)
+ return 0;
+
+ f = (DohFile *) DohMalloc(sizeof(DohFile));
+ if (newfiles)
+ Append(newfiles, filename);
+ f->filep = file;
+ f->fd = 0;
+ f->closeondel = 1;
+ obj = DohObjMalloc(&DohFileType, f);
+ open_files_list_add(f);
+ return obj;
+}
+
+/* -----------------------------------------------------------------------------
+ * NewFileFromFile()
+ *
+ * Create a file object from an already open FILE *.
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohNewFileFromFile(FILE *file) {
+ DohFile *f;
+ f = (DohFile *) DohMalloc(sizeof(DohFile));
+ f->filep = file;
+ f->fd = 0;
+ f->closeondel = 0;
+ return DohObjMalloc(&DohFileType, f);
+}
+
+/* -----------------------------------------------------------------------------
+ * NewFileFromFd()
+ *
+ * Create a file object from an already open FILE *.
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohNewFileFromFd(int fd) {
+ DohFile *f;
+ f = (DohFile *) DohMalloc(sizeof(DohFile));
+ f->filep = 0;
+ f->fd = fd;
+ f->closeondel = 0;
+ return DohObjMalloc(&DohFileType, f);
+}
+
+/* -----------------------------------------------------------------------------
+ * FileErrorDisplay()
+ *
+ * Display cause of one of the NewFile functions failing.
+ * ----------------------------------------------------------------------------- */
+
+void DohFileErrorDisplay(DOHString * filename) {
+ Printf(stderr, "Unable to open file %s: %s\n", filename, strerror(errno));
+}
diff --git a/contrib/tools/swig/Source/DOH/fio.c b/contrib/tools/swig/Source/DOH/fio.c
new file mode 100644
index 0000000000..972fb23df9
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/fio.c
@@ -0,0 +1,599 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * fio.c
+ *
+ * This file implements a number of standard I/O operations included
+ * formatted output, readline, and splitting.
+ * ----------------------------------------------------------------------------- */
+
+#include "dohint.h"
+
+#define OBUFLEN 512
+
+static DOH *encodings = 0; /* Encoding hash */
+
+/* -----------------------------------------------------------------------------
+ * Writen()
+ *
+ * Writes N characters of output and retries until all characters are
+ * written. This is useful should a write operation encounter a spurious signal.
+ * ----------------------------------------------------------------------------- */
+
+static int Writen(DOH *out, void *buffer, int len) {
+ int nw = len, ret;
+ char *cb = (char *) buffer;
+ while (nw) {
+ ret = Write(out, cb, nw);
+ if (ret < 0)
+ return -1;
+ nw = nw - ret;
+ cb += ret;
+ }
+ return len;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohEncoding()
+ *
+ * Registers a new printf encoding method. An encoder function should accept
+ * two file-like objects and operate as a filter.
+ * ----------------------------------------------------------------------------- */
+
+void DohEncoding(const char *name, DOH *(*fn) (DOH *s)) {
+ DohFuncPtr_t fp;
+
+ if (!encodings)
+ encodings = NewHash();
+
+ fp.func = fn;
+ Setattr(encodings, (void *) name, NewVoid(fp.p, 0));
+}
+
+/* internal function for processing an encoding */
+static DOH *encode(char *name, DOH *s) {
+ DOH *handle, *ns;
+ DohFuncPtr_t fp;
+ long pos;
+ char *cfmt = strchr(name, ':');
+ DOH *tmp = 0;
+ if (cfmt) {
+ tmp = NewString(cfmt + 1);
+ Append(tmp, s);
+ Setfile(tmp, Getfile((DOH *) s));
+ Setline(tmp, Getline((DOH *) s));
+ *cfmt = '\0';
+ }
+ if (!encodings || !(handle = Getattr(encodings, name))) {
+ return Copy(s);
+ }
+ if (tmp)
+ s = tmp;
+ pos = Tell(s);
+ Seek(s, 0, SEEK_SET);
+
+ fp.p = Data(handle);
+ ns = (*fp.func) (s);
+ assert(pos != -1);
+ (void)Seek(s, pos, SEEK_SET);
+ if (tmp)
+ Delete(tmp);
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohvPrintf()
+ *
+ * DOH implementation of printf. Output can be directed to any file-like object
+ * including bare FILE * objects. The same formatting codes as printf are
+ * recognized with two extensions:
+ *
+ * %s - Prints a "char *" or the string representation of any
+ * DOH object. This will implicitly result in a call to
+ * Str(obj).
+ *
+ * %(encoder)* - Filters the output through an encoding function registered
+ * with DohEncoder().
+ *
+ * Note: This function is not particularly memory efficient with large strings.
+ * It's better to use Dump() or some other method instead.
+ * ----------------------------------------------------------------------------- */
+
+int DohvPrintf(DOH *so, const char *format, va_list ap) {
+ static const char *fmt_codes = "dioxXucsSfeEgGpn";
+ int state = 0;
+ const char *p = format;
+ char newformat[256];
+ char obuffer[OBUFLEN];
+ char *fmt = 0;
+ char temp[64];
+ int widthval = 0;
+ int precval = 0;
+ int maxwidth;
+ char *w = 0;
+ int ivalue;
+ double dvalue;
+ void *pvalue;
+ char *stemp;
+ int nbytes = 0;
+ char encoder[128], *ec = 0;
+ int plevel = 0;
+
+ memset(newformat, 0, sizeof(newformat));
+
+ while (*p) {
+ switch (state) {
+ case 0: /* Ordinary text */
+ if (*p != '%') {
+ Putc(*p, so);
+ nbytes++;
+ } else {
+ fmt = newformat;
+ widthval = 0;
+ precval = 0;
+ *(fmt++) = *p;
+ encoder[0] = 0;
+ state = 10;
+ }
+ break;
+ case 10: /* Look for a width and precision */
+ if (isdigit((int) *p) && (*p != '0')) {
+ w = temp;
+ *(w++) = *p;
+ *(fmt++) = *p;
+ state = 20;
+ } else if (strchr(fmt_codes, *p)) {
+ /* Got one of the formatting codes */
+ p--;
+ state = 100;
+ } else if (*p == '*') {
+ /* Width field is specified in the format list */
+ widthval = va_arg(ap, int);
+ sprintf(temp, "%d", widthval);
+ for (w = temp; *w; w++) {
+ *(fmt++) = *w;
+ }
+ state = 30;
+ } else if (*p == '%') {
+ Putc(*p, so);
+ fmt = newformat;
+ nbytes++;
+ state = 0;
+ } else if (*p == '(') {
+ ++plevel;
+ ec = encoder;
+ state = 60;
+ } else {
+ *(fmt++) = *p;
+ }
+ break;
+
+ case 20: /* Hmmm. At the start of a width field */
+ if (isdigit((int) *p)) {
+ *(w++) = *p;
+ *(fmt++) = *p;
+ } else if (strchr(fmt_codes, *p)) {
+ /* Got one of the formatting codes */
+ /* Figure out width */
+ *w = 0;
+ widthval = atoi(temp);
+ p--;
+ state = 100;
+ } else if (*p == '.') {
+ *w = 0;
+ widthval = atoi(temp);
+ w = temp;
+ *(fmt++) = *p;
+ state = 40;
+ } else {
+ /* ??? */
+ *w = 0;
+ widthval = atoi(temp);
+ state = 50;
+ }
+ break;
+
+ case 30: /* Parsed a width from an argument. Look for a . */
+ if (*p == '.') {
+ w = temp;
+ *(fmt++) = *p;
+ state = 40;
+ } else if (strchr(fmt_codes, *p)) {
+ /* Got one of the formatting codes */
+ /* Figure out width */
+ p--;
+ state = 100;
+ } else {
+ /* hmmm. Something else. */
+ state = 50;
+ }
+ break;
+
+ case 40:
+ /* Start of precision expected */
+ if (isdigit((int) *p) && (*p != '0')) {
+ *(fmt++) = *p;
+ *(w++) = *p;
+ state = 41;
+ } else if (*p == '*') {
+ /* Precision field is specified in the format list */
+ precval = va_arg(ap, int);
+ sprintf(temp, "%d", precval);
+ for (w = temp; *w; w++) {
+ *(fmt++) = *w;
+ }
+ state = 50;
+ } else if (strchr(fmt_codes, *p)) {
+ p--;
+ state = 100;
+ } else {
+ *(fmt++) = *p;
+ state = 50;
+ }
+ break;
+ case 41:
+ if (isdigit((int) *p)) {
+ *(fmt++) = *p;
+ *(w++) = *p;
+ } else if (strchr(fmt_codes, *p)) {
+ /* Got one of the formatting codes */
+ /* Figure out width */
+ *w = 0;
+ precval = atoi(temp);
+ p--;
+ state = 100;
+ } else {
+ *w = 0;
+ precval = atoi(temp);
+ *(fmt++) = *p;
+ state = 50;
+ }
+ break;
+ /* Hang out, wait for format specifier */
+ case 50:
+ if (strchr(fmt_codes, *p)) {
+ p--;
+ state = 100;
+ } else {
+ *(fmt++) = *p;
+ }
+ break;
+
+ /* Got an encoding header */
+ case 60:
+ if (*p == '(') {
+ ++plevel;
+ *ec = *p;
+ ec++;
+ } else if (*p == ')') {
+ --plevel;
+ if (plevel <= 0) {
+ *ec = 0;
+ state = 10;
+ } else {
+ *ec = *p;
+ ec++;
+ }
+ } else {
+ *ec = *p;
+ ec++;
+ }
+ break;
+ case 100:
+ /* Got a formatting code */
+ if (widthval < precval)
+ maxwidth = precval;
+ else
+ maxwidth = widthval;
+ if ((*p == 's') || (*p == 'S')) { /* Null-Terminated string */
+ DOH *doh;
+ DOH *Sval;
+ DOH *enc = 0;
+ doh = va_arg(ap, DOH *);
+ if (DohCheck(doh)) {
+ /* Is a DOH object. */
+ if (DohIsString(doh)) {
+ Sval = doh;
+ } else {
+ Sval = Str(doh);
+ }
+ if (strlen(encoder)) {
+ enc = encode(encoder, Sval);
+ maxwidth = maxwidth + (int)strlen(newformat) + Len(enc);
+ } else {
+ maxwidth = maxwidth + (int)strlen(newformat) + Len(Sval);
+ }
+ *(fmt++) = 's';
+ *fmt = 0;
+ if ((maxwidth + 1) < OBUFLEN) {
+ stemp = obuffer;
+ } else {
+ stemp = (char *) DohMalloc(maxwidth + 1);
+ }
+ if (enc) {
+ nbytes += sprintf(stemp, newformat, Data(enc));
+ } else {
+ nbytes += sprintf(stemp, newformat, Data(Sval));
+ }
+ if (Writen(so, stemp, (int)strlen(stemp)) < 0)
+ return -1;
+ if ((DOH *) Sval != doh) {
+ Delete(Sval);
+ }
+ if (enc)
+ Delete(enc);
+ if (*p == 'S') {
+ Delete(doh);
+ }
+ if (stemp != obuffer) {
+ DohFree(stemp);
+ }
+ } else {
+ if (!doh)
+ doh = (char *) "";
+
+ if (strlen(encoder)) {
+ DOH *s = NewString(doh);
+ Seek(s, 0, SEEK_SET);
+ enc = encode(encoder, s);
+ Delete(s);
+ doh = Char(enc);
+ } else {
+ enc = 0;
+ }
+ maxwidth = maxwidth + (int)strlen(newformat) + (int)strlen((char *) doh);
+ *(fmt++) = 's';
+ *fmt = 0;
+ if ((maxwidth + 1) < OBUFLEN) {
+ stemp = obuffer;
+ } else {
+ stemp = (char *) DohMalloc(maxwidth + 1);
+ }
+ nbytes += sprintf(stemp, newformat, doh);
+ if (Writen(so, stemp, (int)strlen(stemp)) < 0)
+ return -1;
+ if (stemp != obuffer) {
+ DohFree(stemp);
+ }
+ if (enc)
+ Delete(enc);
+ }
+ } else {
+ *(fmt++) = *p;
+ *fmt = 0;
+ maxwidth = maxwidth + (int)strlen(newformat) + 64;
+
+ /* Only allocate a buffer if it is too big to fit. Shouldn't have to do
+ this very often */
+
+ if (maxwidth < OBUFLEN)
+ stemp = obuffer;
+ else
+ stemp = (char *) DohMalloc(maxwidth + 1);
+ switch (*p) {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'c':
+ ivalue = va_arg(ap, int);
+ nbytes += sprintf(stemp, newformat, ivalue);
+ break;
+ case 'f':
+ case 'g':
+ case 'e':
+ case 'E':
+ case 'G':
+ dvalue = va_arg(ap, double);
+ nbytes += sprintf(stemp, newformat, dvalue);
+ break;
+ case 'p':
+ pvalue = va_arg(ap, void *);
+ nbytes += sprintf(stemp, newformat, pvalue);
+ break;
+ default:
+ break;
+ }
+ if (Writen(so, stemp, (int)strlen(stemp)) < 0)
+ return -1;
+ if (stemp != obuffer)
+ DohFree(stemp);
+ }
+ state = 0;
+ break;
+ }
+ p++;
+ }
+ if (state) {
+ int r;
+ *fmt = 0;
+ r = Writen(so, fmt, (int)strlen(fmt));
+ if (r < 0)
+ return -1;
+ nbytes += r;
+ }
+ return nbytes;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohPrintf()
+ *
+ * Variable length argument entry point to Printf
+ * ----------------------------------------------------------------------------- */
+
+int DohPrintf(DOH *obj, const char *format, ...) {
+ va_list ap;
+ int ret;
+ va_start(ap, format);
+ ret = DohvPrintf(obj, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohPrintv()
+ *
+ * Print a null-terminated variable length list of DOH objects
+ * ----------------------------------------------------------------------------- */
+
+int DohPrintv(DOHFile * f, ...) {
+ va_list ap;
+ int ret = 0;
+ DOH *obj;
+ va_start(ap, f);
+ while (1) {
+ obj = va_arg(ap, void *);
+ if ((!obj) || (obj == DohNone))
+ break;
+ if (DohCheck(obj)) {
+ ret += DohDump(obj, f);
+ } else {
+ ret += DohWrite(f, obj, (int)strlen((char *) obj));
+ }
+ }
+ va_end(ap);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohCopyto()
+ *
+ * Copies all of the input from an input stream to an output stream. Returns the
+ * number of bytes copied.
+ * ----------------------------------------------------------------------------- */
+
+int DohCopyto(DOH *in, DOH *out) {
+ int nbytes = 0, ret;
+ int nwrite = 0, wret;
+ char *cw;
+ char buffer[16384];
+
+ if ((!in) || (!out))
+ return 0;
+ while (1) {
+ ret = Read(in, buffer, 16384);
+ if (ret > 0) {
+ nwrite = ret;
+ cw = buffer;
+ while (nwrite) {
+ wret = Write(out, cw, nwrite);
+ if (wret < 0) {
+ nbytes = -1;
+ break;
+ }
+ nwrite = nwrite - wret;
+ cw += wret;
+ }
+ nbytes += ret;
+ } else {
+ break;
+ }
+ }
+ return nbytes;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * DohSplit()
+ *
+ * Split an input stream into a list of strings delimited by the specified
+ * character. Optionally accepts a maximum number of splits to perform.
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohSplit(DOH *in, char ch, int nsplits) {
+ DOH *list;
+ DOH *str;
+ int c;
+
+ list = NewList();
+
+ if (DohIsString(in)) {
+ Seek(in, 0, SEEK_SET);
+ }
+
+ while (1) {
+ str = NewStringEmpty();
+ do {
+ c = Getc(in);
+ } while ((c != EOF) && (c == ch));
+ if (c != EOF) {
+ Putc(c, str);
+ while (1) {
+ c = Getc(in);
+ if ((c == EOF) || ((c == ch) && (nsplits != 0)))
+ break;
+ Putc(c, str);
+ }
+ nsplits--;
+ }
+ Append(list, str);
+ Delete(str);
+ if (c == EOF)
+ break;
+ }
+ return list;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSplitLines()
+ *
+ * Split an input stream into a list of strings delimited by newline characters.
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohSplitLines(DOH *in) {
+ DOH *list;
+ DOH *str;
+ int c = 0;
+
+ list = NewList();
+
+ if (DohIsString(in)) {
+ Seek(in, 0, SEEK_SET);
+ }
+
+ while (c != EOF) {
+ str = NewStringEmpty();
+ while ((c = Getc(in)) != '\n' && c != EOF) {
+ Putc(c, str);
+ }
+ Append(list, str);
+ Delete(str);
+ }
+ return list;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * DohReadline()
+ *
+ * Read a single input line and return it as a string.
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohReadline(DOH *in) {
+ char c;
+ int n = 0;
+ DOH *s = NewStringEmpty();
+ while (1) {
+ if (Read(in, &c, 1) < 0) {
+ if (n == 0) {
+ Delete(s);
+ s = 0;
+ }
+ break;
+ }
+ if (c == '\n')
+ break;
+ if (c == '\r')
+ continue;
+ Putc(c, s);
+ n++;
+ }
+ return s;
+}
diff --git a/contrib/tools/swig/Source/DOH/hash.c b/contrib/tools/swig/Source/DOH/hash.c
new file mode 100644
index 0000000000..b9d501e3c4
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/hash.c
@@ -0,0 +1,583 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * hash.c
+ *
+ * Implements a simple hash table object.
+ * ----------------------------------------------------------------------------- */
+
+#include "dohint.h"
+
+extern DohObjInfo DohHashType;
+
+/* Hash node */
+typedef struct HashNode {
+ DOH *key;
+ DOH *object;
+ struct HashNode *next;
+} HashNode;
+
+/* Hash object */
+typedef struct Hash {
+ DOH *file;
+ int line;
+ HashNode **hashtable;
+ int hashsize;
+ int nitems;
+} Hash;
+
+/* Key interning structure */
+typedef struct KeyValue {
+ char *cstr;
+ DOH *sstr;
+ struct KeyValue *left;
+ struct KeyValue *right;
+} KeyValue;
+
+static KeyValue *root = 0;
+static int max_expand = 1;
+
+/* Find or create a key in the interned key table */
+static DOH *find_key(DOH *doh_c) {
+ char *c = (char *) doh_c;
+ KeyValue *r, *s;
+ int d = 0;
+ /* OK, sure, we use a binary tree for maintaining interned
+ symbols. Then we use their hash values for accessing secondary
+ hash tables. */
+ r = root;
+ s = 0;
+ while (r) {
+ s = r;
+ d = strcmp(r->cstr, c);
+ if (d == 0)
+ return r->sstr;
+ if (d < 0)
+ r = r->left;
+ else
+ r = r->right;
+ }
+ /* fprintf(stderr,"Interning '%s'\n", c); */
+ r = (KeyValue *) DohMalloc(sizeof(KeyValue));
+ r->cstr = (char *) DohMalloc(strlen(c) + 1);
+ strcpy(r->cstr, c);
+ r->sstr = NewString(c);
+ DohIntern(r->sstr);
+ r->left = 0;
+ r->right = 0;
+ if (!s) {
+ root = r;
+ } else {
+ if (d < 0)
+ s->left = r;
+ else
+ s->right = r;
+ }
+ return r->sstr;
+}
+
+#define HASH_INIT_SIZE 7
+
+/* Create a new hash node */
+static HashNode *NewNode(DOH *k, void *obj) {
+ HashNode *hn = (HashNode *) DohMalloc(sizeof(HashNode));
+ hn->key = k;
+ Incref(hn->key);
+ hn->object = obj;
+ Incref(obj);
+ hn->next = 0;
+ return hn;
+}
+
+/* Delete a hash node */
+static void DelNode(HashNode *hn) {
+ Delete(hn->key);
+ Delete(hn->object);
+ DohFree(hn);
+}
+
+/* -----------------------------------------------------------------------------
+ * DelHash()
+ *
+ * Delete a hash table.
+ * ----------------------------------------------------------------------------- */
+
+static void DelHash(DOH *ho) {
+ Hash *h = (Hash *) ObjData(ho);
+ HashNode *n, *next;
+ int i;
+
+ for (i = 0; i < h->hashsize; i++) {
+ n = h->hashtable[i];
+ while (n) {
+ next = n->next;
+ DelNode(n);
+ n = next;
+ }
+ }
+ DohFree(h->hashtable);
+ h->hashtable = 0;
+ h->hashsize = 0;
+ DohFree(h);
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash_clear()
+ *
+ * Clear all of the entries in the hash table.
+ * ----------------------------------------------------------------------------- */
+
+static void Hash_clear(DOH *ho) {
+ Hash *h = (Hash *) ObjData(ho);
+ HashNode *n, *next;
+ int i;
+
+ for (i = 0; i < h->hashsize; i++) {
+ n = h->hashtable[i];
+ while (n) {
+ next = n->next;
+ DelNode(n);
+ n = next;
+ }
+ h->hashtable[i] = 0;
+ }
+ h->nitems = 0;
+}
+
+/* resize the hash table */
+static void resize(Hash *h) {
+ HashNode *n, *next, **table;
+ int oldsize, newsize;
+ int i, p, hv;
+
+ if (h->nitems < 2 * h->hashsize)
+ return;
+
+ /* Too big. We have to rescale everything now */
+ oldsize = h->hashsize;
+
+ /* Calculate a new size */
+ newsize = 2 * oldsize + 1;
+ p = 3;
+ while (p < (newsize >> 1)) {
+ if (((newsize / p) * p) == newsize) {
+ newsize += 2;
+ p = 3;
+ continue;
+ }
+ p = p + 2;
+ }
+
+ table = (HashNode **) DohCalloc(newsize, sizeof(HashNode *));
+
+ /* Walk down the old set of nodes and re-place */
+ h->hashsize = newsize;
+ for (i = 0; i < oldsize; i++) {
+ n = h->hashtable[i];
+ while (n) {
+ hv = Hashval(n->key) % newsize;
+ next = n->next;
+ n->next = table[hv];
+ table[hv] = n;
+ n = next;
+ }
+ }
+ DohFree(h->hashtable);
+ h->hashtable = table;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash_setattr()
+ *
+ * Set an attribute in the hash table. Deletes the existing entry if it already
+ * exists.
+ * ----------------------------------------------------------------------------- */
+
+static int Hash_setattr(DOH *ho, DOH *k, DOH *obj) {
+ int hv;
+ HashNode *n, *prev;
+ Hash *h = (Hash *) ObjData(ho);
+
+ if (!obj) {
+ return DohDelattr(ho, k);
+ }
+ if (!DohCheck(k))
+ k = find_key(k);
+ if (!DohCheck(obj)) {
+ obj = NewString((char *) obj);
+ Decref(obj);
+ }
+ hv = (Hashval(k)) % h->hashsize;
+ n = h->hashtable[hv];
+ prev = 0;
+ while (n) {
+ if (Cmp(n->key, k) == 0) {
+ /* Node already exists. Just replace its contents */
+ if (n->object == obj) {
+ /* Whoa. Same object. Do nothing */
+ return 1;
+ }
+ Delete(n->object);
+ n->object = obj;
+ Incref(obj);
+ return 1; /* Return 1 to indicate a replacement */
+ } else {
+ prev = n;
+ n = n->next;
+ }
+ }
+ /* Add this to the table */
+ n = NewNode(k, obj);
+ if (prev)
+ prev->next = n;
+ else
+ h->hashtable[hv] = n;
+ h->nitems++;
+ resize(h);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash_getattr()
+ *
+ * Get an attribute from the hash table. Returns 0 if it doesn't exist.
+ * ----------------------------------------------------------------------------- */
+typedef int (*binop) (DOH *obj1, DOH *obj2);
+
+
+static DOH *Hash_getattr(DOH *h, DOH *k) {
+ DOH *obj = 0;
+ Hash *ho = (Hash *) ObjData(h);
+ DOH *ko = DohCheck(k) ? k : find_key(k);
+ int hv = Hashval(ko) % ho->hashsize;
+ DohObjInfo *k_type = ((DohBase*)ko)->type;
+ HashNode *n = ho->hashtable[hv];
+ if (k_type->doh_equal) {
+ binop equal = k_type->doh_equal;
+ while (n) {
+ DohBase *nk = (DohBase *)n->key;
+ if ((k_type == nk->type) && equal(ko, nk)) obj = n->object;
+ n = n->next;
+ }
+ } else {
+ binop cmp = k_type->doh_cmp;
+ while (n) {
+ DohBase *nk = (DohBase *)n->key;
+ if ((k_type == nk->type) && (cmp(ko, nk) == 0)) obj = n->object;
+ n = n->next;
+ }
+ }
+ return obj;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash_delattr()
+ *
+ * Delete an object from the hash table.
+ * ----------------------------------------------------------------------------- */
+
+static int Hash_delattr(DOH *ho, DOH *k) {
+ HashNode *n, *prev;
+ int hv;
+ Hash *h = (Hash *) ObjData(ho);
+
+ if (!DohCheck(k))
+ k = find_key(k);
+ hv = Hashval(k) % h->hashsize;
+ n = h->hashtable[hv];
+ prev = 0;
+ while (n) {
+ if (Cmp(n->key, k) == 0) {
+ /* Found it, kill it */
+
+ if (prev) {
+ prev->next = n->next;
+ } else {
+ h->hashtable[hv] = n->next;
+ }
+ DelNode(n);
+ h->nitems--;
+ return 1;
+ }
+ prev = n;
+ n = n->next;
+ }
+ return 0;
+}
+
+static DohIterator Hash_firstiter(DOH *ho) {
+ DohIterator iter;
+ Hash *h = (Hash *) ObjData(ho);
+ iter.object = ho;
+ iter._current = 0;
+ iter.item = 0;
+ iter.key = 0;
+ iter._index = 0; /* Index in hash table */
+ while ((iter._index < h->hashsize) && !h->hashtable[iter._index])
+ iter._index++;
+
+ if (iter._index >= h->hashsize) {
+ return iter;
+ }
+ iter._current = h->hashtable[iter._index];
+ iter.item = ((HashNode *) iter._current)->object;
+ iter.key = ((HashNode *) iter._current)->key;
+
+ /* Actually save the next slot in the hash. This makes it possible to
+ delete the item being iterated over without trashing the universe */
+ iter._current = ((HashNode *) iter._current)->next;
+ return iter;
+}
+
+static DohIterator Hash_nextiter(DohIterator iter) {
+ Hash *h = (Hash *) ObjData(iter.object);
+ if (!iter._current) {
+ iter._index++;
+ while ((iter._index < h->hashsize) && !h->hashtable[iter._index]) {
+ iter._index++;
+ }
+ if (iter._index >= h->hashsize) {
+ iter.item = 0;
+ iter.key = 0;
+ iter._current = 0;
+ return iter;
+ }
+ iter._current = h->hashtable[iter._index];
+ }
+ iter.key = ((HashNode *) iter._current)->key;
+ iter.item = ((HashNode *) iter._current)->object;
+
+ /* Store the next node to iterator on */
+ iter._current = ((HashNode *) iter._current)->next;
+ return iter;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash_keys()
+ *
+ * Return a list of keys
+ * ----------------------------------------------------------------------------- */
+
+static DOH *Hash_keys(DOH *so) {
+ DOH *keys;
+ Iterator i;
+
+ keys = NewList();
+ for (i = First(so); i.key; i = Next(i)) {
+ Append(keys, i.key);
+ }
+ return keys;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohSetMaxHashExpand()
+ *
+ * Controls how many Hash objects are displayed in full in Hash_str
+ * ----------------------------------------------------------------------------- */
+
+void DohSetMaxHashExpand(int count) {
+ max_expand = count;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohGetMaxHashExpand()
+ *
+ * Returns how many Hash objects are displayed in full in Hash_str
+ * ----------------------------------------------------------------------------- */
+
+int DohGetMaxHashExpand(void) {
+ return max_expand;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash_str()
+ *
+ * Create a string representation of a hash table (mainly for debugging).
+ * ----------------------------------------------------------------------------- */
+
+static DOH *Hash_str(DOH *ho) {
+ int i, j;
+ HashNode *n;
+ DOH *s;
+ static int expanded = 0;
+ static const char *tab = " ";
+ Hash *h = (Hash *) ObjData(ho);
+
+ s = NewStringEmpty();
+ if (ObjGetMark(ho)) {
+ Printf(s, "Hash(%p)", ho);
+ return s;
+ }
+ if (expanded >= max_expand) {
+ /* replace each hash attribute with a '.' */
+ Printf(s, "Hash(%p) {", ho);
+ for (i = 0; i < h->hashsize; i++) {
+ n = h->hashtable[i];
+ while (n) {
+ Putc('.', s);
+ n = n->next;
+ }
+ }
+ Putc('}', s);
+ return s;
+ }
+ ObjSetMark(ho, 1);
+ Printf(s, "Hash(%p) {\n", ho);
+ for (i = 0; i < h->hashsize; i++) {
+ n = h->hashtable[i];
+ while (n) {
+ for (j = 0; j < expanded + 1; j++)
+ Printf(s, tab);
+ expanded += 1;
+ Printf(s, "'%s' : %s, \n", n->key, n->object);
+ expanded -= 1;
+ n = n->next;
+ }
+ }
+ for (j = 0; j < expanded; j++)
+ Printf(s, tab);
+ Printf(s, "}");
+ ObjSetMark(ho, 0);
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash_len()
+ *
+ * Return number of entries in the hash table.
+ * ----------------------------------------------------------------------------- */
+
+static int Hash_len(DOH *ho) {
+ Hash *h = (Hash *) ObjData(ho);
+ return h->nitems;
+}
+
+/* -----------------------------------------------------------------------------
+ * CopyHash()
+ *
+ * Make a copy of a hash table. Note: this is a shallow copy.
+ * ----------------------------------------------------------------------------- */
+
+static DOH *CopyHash(DOH *ho) {
+ Hash *h, *nh;
+ HashNode *n;
+ DOH *nho;
+
+ int i;
+ h = (Hash *) ObjData(ho);
+ nh = (Hash *) DohMalloc(sizeof(Hash));
+ nh->hashsize = h->hashsize;
+ nh->hashtable = (HashNode **) DohMalloc(nh->hashsize * sizeof(HashNode *));
+ for (i = 0; i < nh->hashsize; i++) {
+ nh->hashtable[i] = 0;
+ }
+ nh->nitems = 0;
+ nh->line = h->line;
+ nh->file = h->file;
+ if (nh->file)
+ Incref(nh->file);
+
+ nho = DohObjMalloc(&DohHashType, nh);
+ for (i = 0; i < h->hashsize; i++) {
+ n = h->hashtable[i];
+ while (n) {
+ Hash_setattr(nho, n->key, n->object);
+ n = n->next;
+ }
+ }
+ return nho;
+}
+
+
+
+static void Hash_setfile(DOH *ho, DOH *file) {
+ DOH *fo;
+ Hash *h = (Hash *) ObjData(ho);
+
+ if (!DohCheck(file)) {
+ fo = NewString(file);
+ Decref(fo);
+ } else
+ fo = file;
+ Incref(fo);
+ Delete(h->file);
+ h->file = fo;
+}
+
+static DOH *Hash_getfile(DOH *ho) {
+ Hash *h = (Hash *) ObjData(ho);
+ return h->file;
+}
+
+static void Hash_setline(DOH *ho, int line) {
+ Hash *h = (Hash *) ObjData(ho);
+ h->line = line;
+}
+
+static int Hash_getline(DOH *ho) {
+ Hash *h = (Hash *) ObjData(ho);
+ return h->line;
+}
+
+/* -----------------------------------------------------------------------------
+ * type information
+ * ----------------------------------------------------------------------------- */
+
+static DohHashMethods HashHashMethods = {
+ Hash_getattr,
+ Hash_setattr,
+ Hash_delattr,
+ Hash_keys,
+};
+
+DohObjInfo DohHashType = {
+ "Hash", /* objname */
+ DelHash, /* doh_del */
+ CopyHash, /* doh_copy */
+ Hash_clear, /* doh_clear */
+ Hash_str, /* doh_str */
+ 0, /* doh_data */
+ 0, /* doh_dump */
+ Hash_len, /* doh_len */
+ 0, /* doh_hash */
+ 0, /* doh_cmp */
+ 0, /* doh_equal */
+ Hash_firstiter, /* doh_first */
+ Hash_nextiter, /* doh_next */
+ Hash_setfile, /* doh_setfile */
+ Hash_getfile, /* doh_getfile */
+ Hash_setline, /* doh_setline */
+ Hash_getline, /* doh_getline */
+ &HashHashMethods, /* doh_mapping */
+ 0, /* doh_sequence */
+ 0, /* doh_file */
+ 0, /* doh_string */
+ 0, /* doh_positional */
+ 0,
+};
+
+/* -----------------------------------------------------------------------------
+ * NewHash()
+ *
+ * Create a new hash table.
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohNewHash(void) {
+ Hash *h;
+ int i;
+ h = (Hash *) DohMalloc(sizeof(Hash));
+ h->hashsize = HASH_INIT_SIZE;
+ h->hashtable = (HashNode **) DohMalloc(h->hashsize * sizeof(HashNode *));
+ for (i = 0; i < h->hashsize; i++) {
+ h->hashtable[i] = 0;
+ }
+ h->nitems = 0;
+ h->file = 0;
+ h->line = 0;
+ return DohObjMalloc(&DohHashType, h);
+}
diff --git a/contrib/tools/swig/Source/DOH/list.c b/contrib/tools/swig/Source/DOH/list.c
new file mode 100644
index 0000000000..cc619022d8
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/list.c
@@ -0,0 +1,371 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * list.c
+ *
+ * Implements a simple list object.
+ * ----------------------------------------------------------------------------- */
+
+#include "dohint.h"
+
+typedef struct List {
+ int maxitems; /* Max size */
+ int nitems; /* Num items */
+ DOH *file;
+ int line;
+ DOH **items;
+} List;
+
+extern DohObjInfo DohListType;
+
+/* Doubles amount of memory in a list */
+static
+void more(List *l) {
+ l->items = (void **) DohRealloc(l->items, l->maxitems * 2 * sizeof(void *));
+ l->maxitems *= 2;
+}
+
+/* -----------------------------------------------------------------------------
+ * CopyList()
+ *
+ * Make a shallow copy of a list.
+ * ----------------------------------------------------------------------------- */
+static DOH *CopyList(DOH *lo) {
+ List *l, *nl;
+ int i;
+ l = (List *) ObjData(lo);
+ nl = (List *) DohMalloc(sizeof(List));
+ nl->nitems = l->nitems;
+ nl->maxitems = l->maxitems;
+ nl->items = (void **) DohMalloc(l->maxitems * sizeof(void *));
+ for (i = 0; i < l->nitems; i++) {
+ nl->items[i] = l->items[i];
+ Incref(nl->items[i]);
+ }
+ nl->file = l->file;
+ if (nl->file)
+ Incref(nl->file);
+ nl->line = l->line;
+ return DohObjMalloc(&DohListType, nl);
+}
+
+/* -----------------------------------------------------------------------------
+ * DelList()
+ *
+ * Delete a list.
+ * ----------------------------------------------------------------------------- */
+
+static void DelList(DOH *lo) {
+ List *l = (List *) ObjData(lo);
+ int i;
+ for (i = 0; i < l->nitems; i++)
+ Delete(l->items[i]);
+ DohFree(l->items);
+ DohFree(l);
+}
+
+/* -----------------------------------------------------------------------------
+ * List_clear()
+ *
+ * Remove all of the list entries, but keep the list object intact.
+ * ----------------------------------------------------------------------------- */
+
+static void List_clear(DOH *lo) {
+ List *l = (List *) ObjData(lo);
+ int i;
+ for (i = 0; i < l->nitems; i++) {
+ Delete(l->items[i]);
+ }
+ l->nitems = 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * List_insert()
+ *
+ * Insert an item into the list. If the item is not a DOH object, it is assumed
+ * to be a 'char *' and is used to construct an equivalent string object.
+ * ----------------------------------------------------------------------------- */
+
+static int List_insert(DOH *lo, int pos, DOH *item) {
+ List *l = (List *) ObjData(lo);
+ int i;
+
+ if (!item)
+ return -1;
+ if (!DohCheck(item)) {
+ item = NewString(item);
+ Decref(item);
+ }
+ if (pos == DOH_END)
+ pos = l->nitems;
+ if (pos < 0)
+ pos = 0;
+ if (pos > l->nitems)
+ pos = l->nitems;
+ if (l->nitems == l->maxitems)
+ more(l);
+ for (i = l->nitems; i > pos; i--) {
+ l->items[i] = l->items[i - 1];
+ }
+ l->items[pos] = item;
+ Incref(item);
+ l->nitems++;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * List_remove()
+ *
+ * Remove an item from a list.
+ * ----------------------------------------------------------------------------- */
+
+static int List_remove(DOH *lo, int pos) {
+ List *l = (List *) ObjData(lo);
+ int i;
+ if (pos == DOH_END)
+ pos = l->nitems - 1;
+ if (pos == DOH_BEGIN)
+ pos = 0;
+ assert(!((pos < 0) || (pos >= l->nitems)));
+ Delete(l->items[pos]);
+ for (i = pos; i < l->nitems - 1; i++) {
+ l->items[i] = l->items[i + 1];
+ }
+ l->nitems--;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * List_len()
+ *
+ * Return the number of elements in the list
+ * ----------------------------------------------------------------------------- */
+
+static int List_len(DOH *lo) {
+ List *l = (List *) ObjData(lo);
+ return l->nitems;
+}
+
+/* -----------------------------------------------------------------------------
+ * List_get()
+ *
+ * Get the nth item from the list.
+ * ----------------------------------------------------------------------------- */
+
+static DOH *List_get(DOH *lo, int n) {
+ List *l = (List *) ObjData(lo);
+ if (n == DOH_END)
+ n = l->nitems - 1;
+ if (n == DOH_BEGIN)
+ n = 0;
+ assert(!((n < 0) || (n >= l->nitems)));
+ return l->items[n];
+}
+
+/* -----------------------------------------------------------------------------
+ * List_set()
+ *
+ * Set the nth item in the list replacing any previous item.
+ * ----------------------------------------------------------------------------- */
+
+static int List_set(DOH *lo, int n, DOH *val) {
+ List *l = (List *) ObjData(lo);
+ if (!val)
+ return -1;
+ assert(!((n < 0) || (n >= l->nitems)));
+ if (!DohCheck(val)) {
+ val = NewString(val);
+ Decref(val);
+ }
+ Delete(l->items[n]);
+ l->items[n] = val;
+ Incref(val);
+ Delete(val);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * List_first()
+ *
+ * Return the first item in the list.
+ * ----------------------------------------------------------------------------- */
+
+static DohIterator List_first(DOH *lo) {
+ DohIterator iter;
+ List *l = (List *) ObjData(lo);
+ iter.object = lo;
+ iter._index = 0;
+ iter._current = 0;
+ iter.key = 0;
+ if (l->nitems > 0) {
+ iter.item = l->items[0];
+ } else {
+ iter.item = 0;
+ }
+ return iter;
+}
+
+/* -----------------------------------------------------------------------------
+ * List_next()
+ *
+ * Return the next item in the list.
+ * ----------------------------------------------------------------------------- */
+
+static DohIterator List_next(DohIterator iter) {
+ List *l = (List *) ObjData(iter.object);
+ iter._index = iter._index + 1;
+ if (iter._index >= l->nitems) {
+ iter.item = 0;
+ iter.key = 0;
+ } else {
+ iter.item = l->items[iter._index];
+ }
+ return iter;
+}
+
+/* -----------------------------------------------------------------------------
+ * List_str()
+ *
+ * Create a string representation of the list.
+ * ----------------------------------------------------------------------------- */
+static DOH *List_str(DOH *lo) {
+ DOH *s;
+ int i;
+ List *l = (List *) ObjData(lo);
+ s = NewStringEmpty();
+ if (ObjGetMark(lo)) {
+ Printf(s, "List(%p)", lo);
+ return s;
+ }
+ ObjSetMark(lo, 1);
+ Printf(s, "List[ ");
+ for (i = 0; i < l->nitems; i++) {
+ Printf(s, "%s", l->items[i]);
+ if ((i + 1) < l->nitems)
+ Printf(s, ", ");
+ }
+ Printf(s, " ]");
+ ObjSetMark(lo, 0);
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * List_dump()
+ *
+ * Dump the items to an output stream.
+ * ----------------------------------------------------------------------------- */
+
+static int List_dump(DOH *lo, DOH *out) {
+ int nsent = 0;
+ int i, ret;
+ List *l = (List *) ObjData(lo);
+ for (i = 0; i < l->nitems; i++) {
+ ret = Dump(l->items[i], out);
+ if (ret < 0)
+ return -1;
+ nsent += ret;
+ }
+ return nsent;
+}
+
+static void List_setfile(DOH *lo, DOH *file) {
+ DOH *fo;
+ List *l = (List *) ObjData(lo);
+
+ if (!DohCheck(file)) {
+ fo = NewString(file);
+ Decref(fo);
+ } else
+ fo = file;
+ Incref(fo);
+ Delete(l->file);
+ l->file = fo;
+}
+
+static DOH *List_getfile(DOH *lo) {
+ List *l = (List *) ObjData(lo);
+ return l->file;
+}
+
+static void List_setline(DOH *lo, int line) {
+ List *l = (List *) ObjData(lo);
+ l->line = line;
+}
+
+static int List_getline(DOH *lo) {
+ List *l = (List *) ObjData(lo);
+ return l->line;
+}
+
+static DohListMethods ListListMethods = {
+ List_get,
+ List_set,
+ List_remove,
+ List_insert,
+ 0, /* delslice */
+};
+
+DohObjInfo DohListType = {
+ "List", /* objname */
+ DelList, /* doh_del */
+ CopyList, /* doh_copy */
+ List_clear, /* doh_clear */
+ List_str, /* doh_str */
+ 0, /* doh_data */
+ List_dump, /* doh_dump */
+ List_len, /* doh_len */
+ 0, /* doh_hash */
+ 0, /* doh_cmp */
+ 0, /* doh_equal */
+ List_first, /* doh_first */
+ List_next, /* doh_next */
+ List_setfile, /* doh_setfile */
+ List_getfile, /* doh_getfile */
+ List_setline, /* doh_setline */
+ List_getline, /* doh_getline */
+ 0, /* doh_mapping */
+ &ListListMethods, /* doh_sequence */
+ 0, /* doh_file */
+ 0, /* doh_string */
+ 0, /* doh_callable */
+ 0, /* doh_position */
+};
+
+/* -----------------------------------------------------------------------------
+ * NewList()
+ *
+ * Create a new list.
+ * ----------------------------------------------------------------------------- */
+
+#define MAXLISTITEMS 8
+
+DOH *DohNewList(void) {
+ List *l = (List *) DohMalloc(sizeof(List));
+ l->nitems = 0;
+ l->maxitems = MAXLISTITEMS;
+ l->items = (void **) DohCalloc(l->maxitems, sizeof(void *));
+ l->file = 0;
+ l->line = 0;
+ return DohObjMalloc(&DohListType, l);
+}
+
+static int (*List_sort_compare_func) (const DOH *, const DOH *);
+static int List_qsort_compare(const void *a, const void *b) {
+ return List_sort_compare_func(*((DOH **) a), *((DOH **) b));
+}
+
+/* Sort a list */
+void DohSortList(DOH *lo, int (*cmp) (const DOH *, const DOH *)) {
+ List *l = (List *) ObjData(lo);
+ if (cmp) {
+ List_sort_compare_func = cmp;
+ } else {
+ List_sort_compare_func = DohCmp;
+ }
+ qsort(l->items, l->nitems, sizeof(DOH *), List_qsort_compare);
+}
diff --git a/contrib/tools/swig/Source/DOH/memory.c b/contrib/tools/swig/Source/DOH/memory.c
new file mode 100644
index 0000000000..f8081d3aee
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/memory.c
@@ -0,0 +1,292 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * memory.c
+ *
+ * This file implements all of DOH's memory management including allocation
+ * of objects and checking of objects.
+ * ----------------------------------------------------------------------------- */
+
+#include "dohint.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef DOH_POOL_SIZE
+#define DOH_POOL_SIZE 4194304
+#endif
+
+/* Checks stale DOH object use - will use a lot more memory as pool memory is not re-used. */
+/*
+#define DOH_DEBUG_MEMORY_POOLS
+*/
+
+static int PoolSize = DOH_POOL_SIZE;
+
+DOH *DohNone = 0; /* The DOH None object */
+
+typedef struct pool {
+ DohBase *ptr; /* Start of pool */
+ int len; /* Length of pool */
+ int blen; /* Byte length of pool */
+ int current; /* Current position for next allocation */
+ char *pbeg; /* Beg of pool */
+ char *pend; /* End of pool */
+ struct pool *next; /* Next pool */
+} Pool;
+
+static DohBase *FreeList = 0; /* List of free objects */
+static Pool *Pools = 0;
+static int pools_initialized = 0;
+
+/* ----------------------------------------------------------------------
+ * CreatePool() - Create a new memory pool
+ * ---------------------------------------------------------------------- */
+
+static void CreatePool(void) {
+ Pool *p = 0;
+ p = (Pool *) DohMalloc(sizeof(Pool));
+ p->ptr = (DohBase *) DohCalloc(PoolSize, sizeof(DohBase));
+ p->len = PoolSize;
+ p->blen = PoolSize * sizeof(DohBase);
+ p->current = 0;
+ p->pbeg = ((char *) p->ptr);
+ p->pend = p->pbeg + p->blen;
+ p->next = Pools;
+ Pools = p;
+}
+
+/* ----------------------------------------------------------------------
+ * InitPools() - Initialize the memory allocator
+ * ---------------------------------------------------------------------- */
+
+static void InitPools(void) {
+ if (pools_initialized)
+ return;
+ CreatePool(); /* Create initial pool */
+ pools_initialized = 1;
+ DohNone = NewVoid(0, 0); /* Create the None object */
+ DohIntern(DohNone);
+}
+
+/* ----------------------------------------------------------------------
+ * DohCheck()
+ *
+ * Returns 1 if an arbitrary pointer is a DOH object.
+ * ---------------------------------------------------------------------- */
+
+int DohCheck(const DOH *ptr) {
+ Pool *p = Pools;
+ char *cptr = (char *) ptr;
+ while (p) {
+ if ((cptr >= p->pbeg) && (cptr < p->pend)) {
+#ifdef DOH_DEBUG_MEMORY_POOLS
+ DohBase *b = (DohBase *) ptr;
+ int DOH_object_already_deleted = b->type == 0;
+ assert(!DOH_object_already_deleted);
+#endif
+ return 1;
+ }
+ /*
+ pptr = (char *) p->ptr;
+ if ((cptr >= pptr) && (cptr < (pptr+(p->current*sizeof(DohBase))))) return 1; */
+ p = p->next;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * DohIntern()
+ * ----------------------------------------------------------------------------- */
+
+void DohIntern(DOH *obj) {
+ DohBase *b = (DohBase *) obj;
+ b->flag_intern = 1;
+}
+
+/* ----------------------------------------------------------------------
+ * DohObjMalloc()
+ *
+ * Allocate memory for a new object.
+ * ---------------------------------------------------------------------- */
+
+DOH *DohObjMalloc(DohObjInfo *type, void *data) {
+ DohBase *obj;
+ if (!pools_initialized)
+ InitPools();
+#ifndef DOH_DEBUG_MEMORY_POOLS
+ if (FreeList) {
+ obj = FreeList;
+ FreeList = (DohBase *) obj->data;
+ } else {
+#endif
+ while (Pools->current == Pools->len) {
+ CreatePool();
+ }
+ obj = Pools->ptr + Pools->current;
+ ++Pools->current;
+#ifndef DOH_DEBUG_MEMORY_POOLS
+ }
+#endif
+ obj->type = type;
+ obj->data = data;
+ obj->meta = 0;
+ obj->refcount = 1;
+ obj->flag_intern = 0;
+ obj->flag_marked = 0;
+ obj->flag_user = 0;
+ obj->flag_usermark = 0;
+ return (DOH *) obj;
+}
+
+/* ----------------------------------------------------------------------
+ * DohObjFree() - Free a DOH object
+ * ---------------------------------------------------------------------- */
+
+void DohObjFree(DOH *ptr) {
+ DohBase *b, *meta;
+ b = (DohBase *) ptr;
+ if (b->flag_intern)
+ return;
+ meta = (DohBase *) b->meta;
+ b->data = (void *) FreeList;
+ b->meta = 0;
+ b->type = 0;
+ b->refcount = 0;
+ FreeList = b;
+ if (meta) {
+ Delete(meta);
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * DohMemoryDebug()
+ *
+ * Display memory usage statistics
+ * ---------------------------------------------------------------------- */
+
+void DohMemoryDebug(void) {
+ extern DohObjInfo DohStringType;
+ extern DohObjInfo DohListType;
+ extern DohObjInfo DohHashType;
+
+ Pool *p;
+ int totsize = 0;
+ int totused = 0;
+ int totfree = 0;
+
+ int numstring = 0;
+ int numlist = 0;
+ int numhash = 0;
+
+ printf("Memory statistics:\n\n");
+ printf("Pools:\n");
+
+ p = Pools;
+ while (p) {
+ /* Calculate number of used, free items */
+ int i;
+ int nused = 0, nfree = 0;
+ for (i = 0; i < p->len; i++) {
+ if (p->ptr[i].refcount <= 0)
+ nfree++;
+ else {
+ nused++;
+ if (p->ptr[i].type == &DohStringType)
+ numstring++;
+ else if (p->ptr[i].type == &DohListType)
+ numlist++;
+ else if (p->ptr[i].type == &DohHashType)
+ numhash++;
+ }
+ }
+ printf(" Pool %8p: size = %10d. used = %10d. free = %10d\n", (void *) p, p->len, nused, nfree);
+ totsize += p->len;
+ totused += nused;
+ totfree += nfree;
+ p = p->next;
+ }
+ printf("\n Total: size = %10d, used = %10d, free = %10d\n", totsize, totused, totfree);
+
+ printf("\nObject types\n");
+ printf(" Strings : %d\n", numstring);
+ printf(" Lists : %d\n", numlist);
+ printf(" Hashes : %d\n", numhash);
+
+#if 0
+ p = Pools;
+ while (p) {
+ int i;
+ for (i = 0; i < p->len; i++) {
+ if (p->ptr[i].refcount > 0) {
+ if (p->ptr[i].type == &DohStringType) {
+ Printf(stdout, "%s\n", p->ptr + i);
+ }
+ }
+ }
+ p = p->next;
+ }
+#endif
+
+}
+
+/* Function to call instead of exit(). */
+static void (*doh_exit_handler)(int) = NULL;
+
+void DohSetExitHandler(void (*new_handler)(int)) {
+ doh_exit_handler = new_handler;
+}
+
+void DohExit(int status) {
+ if (doh_exit_handler) {
+ void (*handler)(int) = doh_exit_handler;
+ /* Unset the handler to avoid infinite loops if it tries to do something
+ * which calls DohExit() (e.g. calling Malloc() and that failing).
+ */
+ doh_exit_handler = NULL;
+ handler(status);
+ }
+ doh_internal_exit(status);
+}
+
+static void allocation_failed(size_t n, size_t size) {
+ /* Report and exit as directly as possible to try to avoid further issues due
+ * to lack of memory. */
+ if (n == 1) {
+#if defined __STDC_VERSION__ && __STDC_VERSION__-0 >= 19901L
+ fprintf(stderr, "Failed to allocate %zu bytes\n", size);
+#else
+ fprintf(stderr, "Failed to allocate %lu bytes\n", (unsigned long)size);
+#endif
+ } else {
+#if defined __STDC_VERSION__ && __STDC_VERSION__-0 >= 19901L
+ fprintf(stderr, "Failed to allocate %zu*%zu bytes\n", n, size);
+#else
+ fprintf(stderr, "Failed to allocate %lu*%lu bytes\n", (unsigned long)n, (unsigned long)size);
+#endif
+ }
+ DohExit(EXIT_FAILURE);
+}
+
+void *DohMalloc(size_t size) {
+ void *p = doh_internal_malloc(size);
+ if (!p) allocation_failed(1, size);
+ return p;
+}
+
+void *DohRealloc(void *ptr, size_t size) {
+ void *p = doh_internal_realloc(ptr, size);
+ if (!p) allocation_failed(1, size);
+ return p;
+}
+
+void *DohCalloc(size_t n, size_t size) {
+ void *p = doh_internal_calloc(n, size);
+ if (!p) allocation_failed(n, size);
+ return p;
+}
diff --git a/contrib/tools/swig/Source/DOH/string.c b/contrib/tools/swig/Source/DOH/string.c
new file mode 100644
index 0000000000..c86cab0be5
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/string.c
@@ -0,0 +1,1286 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * string.c
+ *
+ * Implements a string object that supports both sequence operations and
+ * file semantics.
+ * ----------------------------------------------------------------------------- */
+
+#include "dohint.h"
+
+extern DohObjInfo DohStringType;
+
+typedef struct String {
+ DOH *file;
+ int line;
+ int maxsize; /* Max size allocated */
+ int len; /* Current length */
+ int hashkey; /* Hash key value */
+ int sp; /* Current position */
+ char *str; /* String data */
+} String;
+
+/* -----------------------------------------------------------------------------
+ * String_data() - Return as a 'void *'
+ * ----------------------------------------------------------------------------- */
+
+static void *String_data(DOH *so) {
+ String *s = (String *) ObjData(so);
+ s->str[s->len] = 0;
+ return (void *) s->str;
+}
+
+/* static char *String_char(DOH *so) {
+ return (char *) String_data(so);
+}
+*/
+
+/* -----------------------------------------------------------------------------
+ * String_dump() - Serialize a string onto out
+ * ----------------------------------------------------------------------------- */
+
+static int String_dump(DOH *so, DOH *out) {
+ int nsent;
+ int ret;
+ String *s = (String *) ObjData(so);
+ nsent = 0;
+ while (nsent < s->len) {
+ ret = Write(out, s->str + nsent, (s->len - nsent));
+ if (ret < 0)
+ return ret;
+ nsent += ret;
+ }
+ return nsent;
+}
+
+/* -----------------------------------------------------------------------------
+ * CopyString() - Copy a string
+ * ----------------------------------------------------------------------------- */
+
+static DOH *CopyString(DOH *so) {
+ String *str;
+ String *s = (String *) ObjData(so);
+ str = (String *) DohMalloc(sizeof(String));
+ str->hashkey = s->hashkey;
+ str->sp = s->sp;
+ str->line = s->line;
+ str->file = s->file;
+ if (str->file)
+ Incref(str->file);
+ str->str = (char *) DohMalloc(s->len + 1);
+ memcpy(str->str, s->str, s->len);
+ str->maxsize = s->len;
+ str->len = s->len;
+ str->str[str->len] = 0;
+
+ return DohObjMalloc(&DohStringType, str);
+}
+
+/* -----------------------------------------------------------------------------
+ * DelString() - Delete a string
+ * ----------------------------------------------------------------------------- */
+
+static void DelString(DOH *so) {
+ String *s = (String *) ObjData(so);
+ DohFree(s->str);
+ DohFree(s);
+}
+
+/* -----------------------------------------------------------------------------
+ * DohString_len() - Length of a string
+ * ----------------------------------------------------------------------------- */
+
+static int String_len(DOH *so) {
+ String *s = (String *) ObjData(so);
+ return s->len;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * String_cmp() - Compare two strings
+ * ----------------------------------------------------------------------------- */
+
+static int String_cmp(DOH *so1, DOH *so2) {
+ String *s1, *s2;
+ char *c1, *c2;
+ int maxlen, i;
+ s1 = (String *) ObjData(so1);
+ s2 = (String *) ObjData(so2);
+ maxlen = s1->len;
+ if (s2->len < maxlen)
+ maxlen = s2->len;
+ c1 = s1->str;
+ c2 = s2->str;
+ for (i = maxlen; i; --i, c1++, c2++) {
+ if (*c1 != *c2)
+ break;
+ }
+ if (i != 0) {
+ if (*c1 < *c2)
+ return -1;
+ else
+ return 1;
+ }
+ if (s1->len == s2->len)
+ return 0;
+ if (s1->len > s2->len)
+ return 1;
+ return -1;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_equal() - Say if two string are equal
+ * ----------------------------------------------------------------------------- */
+
+static int String_equal(DOH *so1, DOH *so2) {
+ String *s1 = (String *) ObjData(so1);
+ String *s2 = (String *) ObjData(so2);
+ int len = s1->len;
+ if (len != s2->len) {
+ return 0;
+ } else {
+ char *c1 = s1->str;
+ char *c2 = s2->str;
+#if 0
+ int mlen = len >> 2;
+ int i = mlen;
+ for (; i; --i) {
+ if (*(c1++) != *(c2++))
+ return 0;
+ if (*(c1++) != *(c2++))
+ return 0;
+ if (*(c1++) != *(c2++))
+ return 0;
+ if (*(c1++) != *(c2++))
+ return 0;
+ }
+ for (i = len - (mlen << 2); i; --i) {
+ if (*(c1++) != *(c2++))
+ return 0;
+ }
+ return 1;
+#else
+ return memcmp(c1, c2, len) == 0;
+#endif
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * String_hash() - Compute string hash value
+ * ----------------------------------------------------------------------------- */
+
+static int String_hash(DOH *so) {
+ String *s = (String *) ObjData(so);
+ if (s->hashkey >= 0) {
+ return s->hashkey;
+ } else {
+ /* We use the djb2 hash function: https://theartincode.stanis.me/008-djb2/
+ *
+ * One difference is we use initial seed 0. It seems the usual seed value
+ * is intended to help spread out hash values, which is beneficial if
+ * linear probing is used but DOH Hash uses a chain of buckets instead, and
+ * grouped hash values are probably more cache friendly. In tests using
+ * 0 seems slightly faster anyway.
+ */
+ const char *c = s->str;
+ unsigned int len = s->len > 50 ? 50 : s->len;
+ unsigned int h = 0;
+ unsigned int mlen = len >> 2;
+ unsigned int i = mlen;
+ for (; i; --i) {
+ h = h + (h << 5) + *(c++);
+ h = h + (h << 5) + *(c++);
+ h = h + (h << 5) + *(c++);
+ h = h + (h << 5) + *(c++);
+ }
+ for (i = len - (mlen << 2); i; --i) {
+ h = h + (h << 5) + *(c++);
+ }
+ h &= 0x7fffffff;
+ s->hashkey = (int)h;
+ return h;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * DohString_append() - Append to s
+ * ----------------------------------------------------------------------------- */
+
+static void DohString_append(DOH *so, const DOHString_or_char *str) {
+ int oldlen, newlen, newmaxsize, l, sp;
+ char *tc;
+ String *s = (String *) ObjData(so);
+ char *newstr = 0;
+
+ if (DohCheck(str)) {
+ String *ss = (String *) ObjData(str);
+ newstr = (char *) String_data((DOH *) str);
+ l = ss->len;
+ } else {
+ newstr = (char *) (str);
+ l = (int) strlen(newstr);
+ }
+ if (!newstr)
+ return;
+ s->hashkey = -1;
+
+ oldlen = s->len;
+ newlen = oldlen + l + 1;
+ if (newlen >= s->maxsize - 1) {
+ newmaxsize = 2 * s->maxsize;
+ if (newlen >= newmaxsize - 1)
+ newmaxsize = newlen + 1;
+ s->str = (char *) DohRealloc(s->str, newmaxsize);
+ s->maxsize = newmaxsize;
+ }
+ tc = s->str;
+ memcpy(tc + oldlen, newstr, l + 1);
+ sp = s->sp;
+ if (sp >= oldlen) {
+ int i = oldlen + l - sp;
+ tc += sp;
+ for (; i; --i) {
+ if (*(tc++) == '\n')
+ s->line++;
+ }
+ s->sp = oldlen + l;
+ }
+ s->len += l;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * String_clear() - Clear a string
+ * ----------------------------------------------------------------------------- */
+
+static void String_clear(DOH *so) {
+ String *s = (String *) ObjData(so);
+ s->hashkey = -1;
+ s->len = 0;
+ *(s->str) = 0;
+ s->sp = 0;
+ s->line = 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_insert() - Insert a string
+ * ----------------------------------------------------------------------------- */
+
+static int String_insert(DOH *so, int pos, DOH *str) {
+ String *s;
+ int len;
+ char *data;
+
+ if (pos == DOH_END) {
+ DohString_append(so, str);
+ return 0;
+ }
+
+
+ s = (String *) ObjData(so);
+ s->hashkey = -1;
+ if (DohCheck(str)) {
+ String *ss = (String *) ObjData(str);
+ data = (char *) String_data(str);
+ len = ss->len;
+ } else {
+ data = (char *) (str);
+ len = (int) strlen(data);
+ }
+
+ if (pos < 0)
+ pos = 0;
+ else if (pos > s->len)
+ pos = s->len;
+
+ /* See if there is room to insert the new data */
+ while (s->maxsize <= s->len + len) {
+ int newsize = 2 * s->maxsize;
+ s->str = (char *) DohRealloc(s->str, newsize);
+ s->maxsize = newsize;
+ }
+ memmove(s->str + pos + len, s->str + pos, (s->len - pos));
+ memcpy(s->str + pos, data, len);
+ if (s->sp >= pos) {
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] == '\n')
+ s->line++;
+ }
+ s->sp += len;
+ }
+ s->len += len;
+ s->str[s->len] = 0;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_delitem() - Delete a character
+ * ----------------------------------------------------------------------------- */
+
+static int String_delitem(DOH *so, int pos) {
+ String *s = (String *) ObjData(so);
+ s->hashkey = -1;
+ if (pos == DOH_END)
+ pos = s->len - 1;
+ if (pos == DOH_BEGIN)
+ pos = 0;
+ if (s->len == 0)
+ return 0;
+
+ if (s->sp > pos) {
+ s->sp--;
+ assert(s->sp >= 0);
+ if (s->str[pos] == '\n')
+ s->line--;
+ }
+ memmove(s->str + pos, s->str + pos + 1, ((s->len - 1) - pos));
+ s->len--;
+ s->str[s->len] = 0;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_delslice() - Delete a range
+ * ----------------------------------------------------------------------------- */
+
+static int String_delslice(DOH *so, int sindex, int eindex) {
+ String *s = (String *) ObjData(so);
+ int size;
+ if (s->len == 0)
+ return 0;
+ s->hashkey = -1;
+ if (eindex == DOH_END)
+ eindex = s->len;
+ if (sindex == DOH_BEGIN)
+ sindex = 0;
+
+ size = eindex - sindex;
+ if (s->sp > sindex) {
+ /* Adjust the file pointer and line count */
+ int i, end;
+ if (s->sp > eindex) {
+ end = eindex;
+ s->sp -= size;
+ } else {
+ end = s->sp;
+ s->sp = sindex;
+ }
+ for (i = sindex; i < end; i++) {
+ if (s->str[i] == '\n')
+ s->line--;
+ }
+ assert(s->sp >= 0);
+ }
+ memmove(s->str + sindex, s->str + eindex, s->len - eindex);
+ s->len -= size;
+ s->str[s->len] = 0;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_str() - Returns a string (used by printing commands)
+ * ----------------------------------------------------------------------------- */
+
+static DOH *String_str(DOH *so) {
+ String *s = (String *) ObjData(so);
+ s->str[s->len] = 0;
+ return NewString(s->str);
+}
+
+/* -----------------------------------------------------------------------------
+ * String_read() - Read data from a string
+ * ----------------------------------------------------------------------------- */
+
+static int String_read(DOH *so, void *buffer, int len) {
+ int reallen, retlen;
+ char *cb;
+ String *s = (String *) ObjData(so);
+ if ((s->sp + len) > s->len)
+ reallen = (s->len - s->sp);
+ else
+ reallen = len;
+
+ cb = (char *) buffer;
+ retlen = reallen;
+
+ if (reallen > 0) {
+ memmove(cb, s->str + s->sp, reallen);
+ s->sp += reallen;
+ }
+ return retlen;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_write() - Write data to a string
+ * ----------------------------------------------------------------------------- */
+static int String_write(DOH *so, const void *buffer, int len) {
+ int newlen;
+ String *s = (String *) ObjData(so);
+ s->hashkey = -1;
+ if (s->sp > s->len)
+ s->sp = s->len;
+ newlen = s->sp + len + 1;
+ if (newlen > s->maxsize) {
+ s->str = (char *) DohRealloc(s->str, newlen);
+ s->maxsize = newlen;
+ s->len = s->sp + len;
+ }
+ if ((s->sp + len) > s->len)
+ s->len = s->sp + len;
+ memmove(s->str + s->sp, buffer, len);
+ s->sp += len;
+ s->str[s->len] = 0;
+ return len;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_seek() - Seek to a new position
+ * ----------------------------------------------------------------------------- */
+
+static int String_seek(DOH *so, long offset, int whence) {
+ int pos, nsp, inc;
+ String *s = (String *) ObjData(so);
+ if (whence == SEEK_SET)
+ pos = 0;
+ else if (whence == SEEK_CUR)
+ pos = s->sp;
+ else if (whence == SEEK_END) {
+ pos = s->len;
+ offset = -offset;
+ } else
+ pos = s->sp;
+
+ nsp = pos + offset;
+ if (nsp < 0)
+ nsp = 0;
+ if (s->len > 0 && nsp > s->len)
+ nsp = s->len;
+
+ inc = (nsp > s->sp) ? 1 : -1;
+
+ {
+#if 0
+ int sp = s->sp;
+ char *tc = s->str;
+ int len = s->len;
+ while (sp != nsp) {
+ int prev = sp + inc;
+ if (prev >= 0 && prev <= len && tc[prev] == '\n')
+ s->line += inc;
+ sp += inc;
+ }
+#else
+ int sp = s->sp;
+ char *tc = s->str;
+ if (inc > 0) {
+ while (sp != nsp) {
+ if (tc[++sp] == '\n')
+ ++s->line;
+ }
+ } else {
+ while (sp != nsp) {
+ if (tc[--sp] == '\n')
+ --s->line;
+ }
+ }
+#endif
+ s->sp = sp;
+ }
+ assert(s->sp >= 0);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_tell() - Return current position
+ * ----------------------------------------------------------------------------- */
+
+static long String_tell(DOH *so) {
+ String *s = (String *) ObjData(so);
+ return (long) (s->sp);
+}
+
+/* -----------------------------------------------------------------------------
+ * String_putc()
+ * ----------------------------------------------------------------------------- */
+
+static int String_putc(DOH *so, int ch) {
+ String *s = (String *) ObjData(so);
+ int len = s->len;
+ int sp = s->sp;
+ s->hashkey = -1;
+ if (sp >= len) {
+ int maxsize = s->maxsize;
+ char *tc = s->str;
+ if (len > (maxsize - 2)) {
+ maxsize *= 2;
+ tc = (char *) DohRealloc(tc, maxsize);
+ s->maxsize = (int) maxsize;
+ s->str = tc;
+ }
+ tc += sp;
+ *tc = (char) ch;
+ *(++tc) = 0;
+ s->len = s->sp = sp + 1;
+ } else {
+ s->str[s->sp++] = (char) ch;
+ }
+ if (ch == '\n')
+ s->line++;
+ return ch;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_getc()
+ * ----------------------------------------------------------------------------- */
+
+static int String_getc(DOH *so) {
+ int c;
+ String *s = (String *) ObjData(so);
+ if (s->sp >= s->len)
+ c = EOF;
+ else
+ c = (int)(unsigned char) s->str[s->sp++];
+ if (c == '\n')
+ s->line++;
+ return c;
+}
+
+/* -----------------------------------------------------------------------------
+ * String_ungetc()
+ * ----------------------------------------------------------------------------- */
+
+static int String_ungetc(DOH *so, int ch) {
+ String *s = (String *) ObjData(so);
+ if (ch == EOF)
+ return ch;
+ if (s->sp <= 0)
+ return EOF;
+ s->sp--;
+ if (ch == '\n')
+ s->line--;
+ return ch;
+}
+
+static char *end_quote(char *s) {
+ char *qs;
+ char qc;
+ char *q;
+ char *nl;
+ qc = *s;
+ qs = s;
+ while (1) {
+ q = strpbrk(s + 1, "\"\'");
+ nl = strchr(s + 1, '\n');
+ if (nl && (nl < q)) {
+ /* A new line appears before the end of the string */
+ if (*(nl - 1) == '\\') {
+ s = nl + 1;
+ continue;
+ }
+ /* String was terminated by a newline. Wing it */
+ return qs;
+ }
+ if (!q && nl) {
+ return qs;
+ }
+ if (!q)
+ return 0;
+ if ((*q == qc) && (*(q - 1) != '\\'))
+ return q;
+ s = q;
+ }
+}
+
+static char *end_comment(char *s) {
+ char *substring = strstr(s, "*/");
+ if (substring)
+ ++substring;
+ return substring;
+}
+
+static char *match_simple(char *base, char *s, char *token, int tokenlen) {
+ (void) base;
+ (void) tokenlen;
+ return strstr(s, token);
+}
+
+static char *match_identifier(char *base, char *s, char *token, int tokenlen) {
+ while (s) {
+ s = strstr(s, token);
+ if (!s)
+ return 0;
+ if ((s > base) && (isalnum((int) *(s - 1)) || (*(s - 1) == '_'))) {
+ /* We could advance by tokenlen if strstr(s, token) matches can't overlap. */
+ ++s;
+ continue;
+ }
+ if (isalnum((int) *(s + tokenlen)) || (*(s + tokenlen) == '_')) {
+ /* We could advance by tokenlen if strstr(s, token) matches can't overlap. */
+ ++s;
+ continue;
+ }
+ return s;
+ }
+ return 0;
+}
+
+
+static char *match_identifier_begin(char *base, char *s, char *token, int tokenlen) {
+ (void)tokenlen;
+ while (s) {
+ s = strstr(s, token);
+ if (!s)
+ return 0;
+ if ((s > base) && (isalnum((int) *(s - 1)) || (*(s - 1) == '_'))) {
+ /* We could advance by tokenlen if strstr(s, token) matches can't overlap. */
+ ++s;
+ continue;
+ }
+ return s;
+ }
+ return 0;
+}
+
+static char *match_identifier_end(char *base, char *s, char *token, int tokenlen) {
+ (void) base;
+ while (s) {
+ s = strstr(s, token);
+ if (!s)
+ return 0;
+ if (isalnum((int) *(s + tokenlen)) || (*(s + tokenlen) == '_')) {
+ /* We could advance by tokenlen if strstr(s, token) matches can't overlap. */
+ ++s;
+ continue;
+ }
+ return s;
+ }
+ return 0;
+}
+
+static char *match_number_end(char *base, char *s, char *token, int tokenlen) {
+ (void) base;
+ while (s) {
+ s = strstr(s, token);
+ if (!s)
+ return 0;
+ if (isdigit((int) *(s + tokenlen))) {
+ /* We could advance by tokenlen if strstr(s, token) matches can't overlap. */
+ ++s;
+ continue;
+ }
+ return s;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * replace_simple()
+ *
+ * Replaces count non-overlapping occurrences of token with rep in a string.
+ * ----------------------------------------------------------------------------- */
+
+static int replace_simple(String *str, char *token, char *rep, int flags, int count, char *(*match) (char *, char *, char *, int)) {
+ int tokenlen; /* Length of the token */
+ int replen; /* Length of the replacement */
+ int delta, expand = 0;
+ int ic;
+ int rcount = 0;
+ int noquote = 0;
+ int nocomment = 0;
+ char *c, *s, *t, *first;
+ char *q, *q2;
+ char *base;
+ int i;
+
+ /* Figure out if anything gets replaced */
+ if (!strlen(token))
+ return 0;
+
+ base = str->str;
+ tokenlen = (int)strlen(token);
+ s = (*match) (base, base, token, tokenlen);
+
+ if (!s)
+ return 0; /* No matches. Who cares */
+
+ str->hashkey = -1;
+
+ if (flags & DOH_REPLACE_NOQUOTE)
+ noquote = 1;
+
+ if (flags & DOH_REPLACE_NOCOMMENT)
+ nocomment = 1;
+
+ assert(!(noquote && nocomment)); /* quote and comment combination not implemented */
+
+ /* If we are not replacing inside quotes, we need to do a little extra work */
+ if (noquote) {
+ q = strpbrk(base, "\"\'");
+ if (!q) {
+ noquote = 0; /* Well, no quotes to worry about. Oh well */
+ } else {
+ while (q && (q < s)) {
+ /* First match was found inside a quote. Try to find another match */
+ q2 = end_quote(q);
+ if (!q2) {
+ return 0;
+ }
+ if (q2 > s) {
+ /* Find next match */
+ s = (*match) (base, q2 + 1, token, tokenlen);
+ }
+ if (!s)
+ return 0; /* Oh well, no matches */
+ q = strpbrk(q2 + 1, "\"\'");
+ if (!q)
+ noquote = 0; /* No more quotes */
+ }
+ }
+ }
+
+ /* If we are not replacing inside comments, we need to do a little extra work */
+ if (nocomment) {
+ q = strstr(base, "/*");
+ if (!q) {
+ nocomment = 0; /* Well, no comments to worry about. Oh well */
+ } else {
+ while (q && (q < s)) {
+ /* First match was found inside a comment. Try to find another match */
+ q2 = end_comment(q);
+ if (!q2) {
+ return 0;
+ }
+ if (q2 > s) {
+ /* Find next match */
+ s = (*match) (base, q2 + 1, token, tokenlen);
+ }
+ if (!s)
+ return 0; /* Oh well, no matches */
+ q = strstr(q2 + 1, "/*");
+ if (!q)
+ nocomment = 0; /* No more comments */
+ }
+ }
+ }
+
+ first = s;
+ replen = (int)strlen(rep);
+
+ delta = (replen - tokenlen);
+
+ if (delta <= 0) {
+ /* String is either shrinking or staying the same size */
+ /* In this case, we do the replacement in place without memory reallocation */
+ ic = count;
+ t = s; /* Target of memory copies */
+ while (ic && s) {
+ if (replen) {
+ memcpy(t, rep, replen);
+ t += replen;
+ }
+ rcount++;
+ expand += delta;
+ /* Find the next location */
+ s += tokenlen;
+ if (ic == 1)
+ break;
+ c = (*match) (base, s, token, tokenlen);
+
+ if (noquote) {
+ q = strpbrk(s, "\"\'");
+ if (!q) {
+ noquote = 0;
+ } else {
+ while (q && (q < c)) {
+ /* First match was found inside a quote. Try to find another match */
+ q2 = end_quote(q);
+ if (!q2) {
+ c = 0;
+ break;
+ }
+ if (q2 > c)
+ c = (*match) (base, q2 + 1, token, tokenlen);
+ if (!c)
+ break;
+ q = strpbrk(q2 + 1, "\"\'");
+ if (!q)
+ noquote = 0; /* No more quotes */
+ }
+ }
+ }
+ if (nocomment) {
+ q = strstr(s, "/*");
+ if (!q) {
+ nocomment = 0;
+ } else {
+ while (q && (q < c)) {
+ /* First match was found inside a comment. Try to find another match */
+ q2 = end_comment(q);
+ if (!q2) {
+ c = 0;
+ break;
+ }
+ if (q2 > c)
+ c = (*match) (base, q2 + 1, token, tokenlen);
+ if (!c)
+ break;
+ q = strstr(q2 + 1, "/*");
+ if (!q)
+ nocomment = 0; /* No more comments */
+ }
+ }
+ }
+ if (delta) {
+ if (c) {
+ memmove(t, s, c - s);
+ t += (c - s);
+ } else {
+ memmove(t, s, (str->str + str->len) - s + 1);
+ }
+ } else {
+ if (c) {
+ t += (c - s);
+ }
+ }
+ s = c;
+ ic--;
+ }
+ if (s && delta) {
+ memmove(t, s, (str->str + str->len) - s + 1);
+ }
+ str->len += expand;
+ str->str[str->len] = 0;
+ if (str->sp >= str->len)
+ str->sp += expand; /* Fix the end of file pointer */
+ return rcount;
+ }
+ /* The string is expanding as a result of the replacement */
+ /* Figure out how much expansion is going to occur and allocate a new string */
+ {
+ char *ns;
+ int newsize;
+
+ rcount++;
+ ic = count - 1;
+ s += tokenlen;
+ while (ic && (c = (*match) (base, s, token, tokenlen))) {
+ if (noquote) {
+ q = strpbrk(s, "\"\'");
+ if (!q) {
+ break;
+ } else {
+ while (q && (q < c)) {
+ /* First match was found inside a quote. Try to find another match */
+ q2 = end_quote(q);
+ if (!q2) {
+ c = 0;
+ break;
+ }
+ if (q2 > c) {
+ c = (*match) (base, q2 + 1, token, tokenlen);
+ if (!c)
+ break;
+ }
+ q = strpbrk(q2 + 1, "\"\'");
+ if (!q)
+ noquote = 0;
+ }
+ }
+ }
+ if (nocomment) {
+ q = strstr(s, "/*");
+ if (!q) {
+ break;
+ } else {
+ while (q && (q < c)) {
+ /* First match was found inside a comment. Try to find another match */
+ q2 = end_comment(q);
+ if (!q2) {
+ c = 0;
+ break;
+ }
+ if (q2 > c) {
+ c = (*match) (base, q2 + 1, token, tokenlen);
+ if (!c)
+ break;
+ }
+ q = strstr(q2 + 1, "/*");
+ if (!q)
+ nocomment = 0;
+ }
+ }
+ }
+ if (c) {
+ rcount++;
+ ic--;
+ s = c + tokenlen;
+ } else {
+ break;
+ }
+ }
+
+ expand = delta * rcount; /* Total amount of expansion for the replacement */
+ newsize = str->maxsize;
+ while ((str->len + expand) >= newsize)
+ newsize *= 2;
+
+ ns = (char *) DohMalloc(newsize);
+ t = ns;
+ s = first;
+
+ /* Copy the first part of the string */
+ if (first > str->str) {
+ memcpy(t, str->str, (first - str->str));
+ t += (first - str->str);
+ }
+ for (i = 0; i < rcount; i++) {
+ memcpy(t, rep, replen);
+ t += replen;
+ s += tokenlen;
+ c = (*match) (base, s, token, tokenlen);
+ if (noquote) {
+ q = strpbrk(s, "\"\'");
+ if (!q) {
+ noquote = 0;
+ } else {
+ while (q && (q < c)) {
+ /* First match was found inside a quote. Try to find another match */
+ q2 = end_quote(q);
+ if (!q2) {
+ c = 0;
+ break;
+ }
+ if (q2 > c) {
+ c = (*match) (base, q2 + 1, token, tokenlen);
+ if (!c)
+ break;
+ }
+ q = strpbrk(q2 + 1, "\"\'");
+ if (!q)
+ noquote = 0; /* No more quotes */
+ }
+ }
+ }
+ if (nocomment) {
+ q = strstr(s, "/*");
+ if (!q) {
+ nocomment = 0;
+ } else {
+ while (q && (q < c)) {
+ /* First match was found inside a comment. Try to find another match */
+ q2 = end_comment(q);
+ if (!q2) {
+ c = 0;
+ break;
+ }
+ if (q2 > c) {
+ c = (*match) (base, q2 + 1, token, tokenlen);
+ if (!c)
+ break;
+ }
+ q = strstr(q2 + 1, "/*");
+ if (!q)
+ nocomment = 0; /* No more comments */
+ }
+ }
+ }
+ if (i < (rcount - 1)) {
+ memcpy(t, s, c - s);
+ t += (c - s);
+ } else {
+ memcpy(t, s, (str->str + str->len) - s + 1);
+ }
+ s = c;
+ }
+ c = str->str;
+ str->str = ns;
+ if (str->sp >= str->len)
+ str->sp += expand;
+ str->len += expand;
+ str->str[str->len] = 0;
+ str->maxsize = newsize;
+ DohFree(c);
+ return rcount;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * String_replace()
+ * ----------------------------------------------------------------------------- */
+
+static int String_replace(DOH *stro, const DOHString_or_char *token, const DOHString_or_char *rep, int flags) {
+ int count = -1;
+ String *str = (String *) ObjData(stro);
+
+ if (flags & DOH_REPLACE_FIRST)
+ count = 1;
+
+ if (flags & DOH_REPLACE_ID_END) {
+ return replace_simple(str, Char(token), Char(rep), flags, count, match_identifier_end);
+ } else if (flags & DOH_REPLACE_ID_BEGIN) {
+ return replace_simple(str, Char(token), Char(rep), flags, count, match_identifier_begin);
+ } else if (flags & DOH_REPLACE_ID) {
+ return replace_simple(str, Char(token), Char(rep), flags, count, match_identifier);
+ } else if (flags & DOH_REPLACE_NUMBER_END) {
+ return replace_simple(str, Char(token), Char(rep), flags, count, match_number_end);
+ } else {
+ return replace_simple(str, Char(token), Char(rep), flags, count, match_simple);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * String_chop()
+ * ----------------------------------------------------------------------------- */
+
+static void String_chop(DOH *so) {
+ char *c;
+ String *str = (String *) ObjData(so);
+ /* Replace trailing whitespace */
+ c = str->str + str->len - 1;
+ while ((str->len > 0) && (isspace((int) *c))) {
+ if (str->sp >= str->len) {
+ str->sp--;
+ if (*c == '\n')
+ str->line--;
+ }
+ str->len--;
+ c--;
+ }
+ str->str[str->len] = 0;
+ assert(str->sp >= 0);
+ str->hashkey = -1;
+}
+
+static void String_setfile(DOH *so, DOH *file) {
+ DOH *fo;
+ String *str = (String *) ObjData(so);
+
+ if (!DohCheck(file)) {
+ fo = NewString(file);
+ Decref(fo);
+ } else
+ fo = file;
+ Incref(fo);
+ Delete(str->file);
+ str->file = fo;
+}
+
+static DOH *String_getfile(DOH *so) {
+ String *str = (String *) ObjData(so);
+ return str->file;
+}
+
+static void String_setline(DOH *so, int line) {
+ String *str = (String *) ObjData(so);
+ str->line = line;
+}
+
+static int String_getline(DOH *so) {
+ String *str = (String *) ObjData(so);
+ return str->line;
+}
+
+static DohListMethods StringListMethods = {
+ 0, /* doh_getitem */
+ 0, /* doh_setitem */
+ String_delitem, /* doh_delitem */
+ String_insert, /* doh_insitem */
+ String_delslice, /* doh_delslice */
+};
+
+static DohFileMethods StringFileMethods = {
+ String_read,
+ String_write,
+ String_putc,
+ String_getc,
+ String_ungetc,
+ String_seek,
+ String_tell,
+};
+
+static DohStringMethods StringStringMethods = {
+ String_replace,
+ String_chop,
+};
+
+DohObjInfo DohStringType = {
+ "String", /* objname */
+ DelString, /* doh_del */
+ CopyString, /* doh_copy */
+ String_clear, /* doh_clear */
+ String_str, /* doh_str */
+ String_data, /* doh_data */
+ String_dump, /* doh_dump */
+ String_len, /* doh_len */
+ String_hash, /* doh_hash */
+ String_cmp, /* doh_cmp */
+ String_equal, /* doh_equal */
+ 0, /* doh_first */
+ 0, /* doh_next */
+ String_setfile, /* doh_setfile */
+ String_getfile, /* doh_getfile */
+ String_setline, /* doh_setline */
+ String_getline, /* doh_getline */
+ 0, /* doh_mapping */
+ &StringListMethods, /* doh_sequence */
+ &StringFileMethods, /* doh_file */
+ &StringStringMethods, /* doh_string */
+ 0, /* doh_position */
+ 0
+};
+
+
+#define INIT_MAXSIZE 16
+
+/* -----------------------------------------------------------------------------
+ * NewString() - Create a new string
+ * ----------------------------------------------------------------------------- */
+
+DOHString *DohNewString(const DOHString_or_char *so) {
+ int l = 0, max;
+ String *str;
+ char *s;
+ int hashkey = -1;
+ if (DohCheck(so)) {
+ str = (String *) ObjData(so);
+ s = (char *) String_data((String *) so);
+ l = s ? str->len : 0;
+ hashkey = str->hashkey;
+ } else {
+ s = (char *) so;
+ l = s ? (int) strlen(s) : 0;
+ }
+
+ str = (String *) DohMalloc(sizeof(String));
+ str->hashkey = hashkey;
+ str->sp = 0;
+ str->line = 1;
+ str->file = 0;
+ max = INIT_MAXSIZE;
+ if (s) {
+ if ((l + 1) > max)
+ max = l + 1;
+ }
+ str->str = (char *) DohMalloc(max);
+ str->maxsize = max;
+ if (s) {
+ strcpy(str->str, s);
+ str->len = l;
+ str->sp = l;
+ } else {
+ str->str[0] = 0;
+ str->len = 0;
+ }
+ return DohObjMalloc(&DohStringType, str);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * NewStringEmpty() - Create a new string
+ * ----------------------------------------------------------------------------- */
+
+DOHString *DohNewStringEmpty(void) {
+ int max = INIT_MAXSIZE;
+ String *str = (String *) DohMalloc(sizeof(String));
+ str->hashkey = 0;
+ str->sp = 0;
+ str->line = 1;
+ str->file = 0;
+ str->str = (char *) DohMalloc(max);
+ str->maxsize = max;
+ str->str[0] = 0;
+ str->len = 0;
+ return DohObjMalloc(&DohStringType, str);
+}
+
+/* -----------------------------------------------------------------------------
+ * NewStringWithSize() - Create a new string
+ * ----------------------------------------------------------------------------- */
+
+DOHString *DohNewStringWithSize(const DOHString_or_char *so, int len) {
+ int l = 0, max;
+ String *str;
+ char *s;
+ if (DohCheck(so)) {
+ s = (char *) String_data((String *) so);
+ } else {
+ s = (char *) so;
+ }
+
+ str = (String *) DohMalloc(sizeof(String));
+ str->hashkey = -1;
+ str->sp = 0;
+ str->line = 1;
+ str->file = 0;
+ max = INIT_MAXSIZE;
+ if (s) {
+ l = (int) len;
+ if ((l + 1) > max)
+ max = l + 1;
+ }
+ str->str = (char *) DohMalloc(max);
+ str->maxsize = max;
+ if (s) {
+ strncpy(str->str, s, len);
+ str->str[l] = 0;
+ str->len = l;
+ str->sp = l;
+ } else {
+ str->str[0] = 0;
+ str->len = 0;
+ }
+ return DohObjMalloc(&DohStringType, str);
+}
+
+/* -----------------------------------------------------------------------------
+ * NewStringf()
+ *
+ * Create a new string from a list of objects.
+ * ----------------------------------------------------------------------------- */
+
+DOHString *DohNewStringf(const DOHString_or_char *fmt, ...) {
+ va_list ap;
+ DOH *r;
+ va_start(ap, fmt);
+ r = NewStringEmpty();
+ DohvPrintf(r, Char(fmt), ap);
+ va_end(ap);
+ return (DOHString *) r;
+}
+
+/* -----------------------------------------------------------------------------
+ * Strcmp()
+ * Strncmp()
+ * Strstr()
+ * Strchr()
+ *
+ * Some utility functions.
+ * ----------------------------------------------------------------------------- */
+
+int DohStrcmp(const DOHString_or_char *s1, const DOHString_or_char *s2) {
+ const char *c1 = Char(s1);
+ const char *c2 = Char(s2);
+ return strcmp(c1, c2);
+}
+
+int DohStrncmp(const DOHString_or_char *s1, const DOHString_or_char *s2, int n) {
+ return strncmp(Char(s1), Char(s2), n);
+}
+
+char *DohStrstr(const DOHString_or_char *s1, const DOHString_or_char *s2) {
+ char *p1 = Char(s1);
+ char *p2 = Char(s2);
+ return p1 == 0 || p2 == 0 || *p2 == '\0' ? p1 : strstr(p1, p2);
+}
+
+char *DohStrchr(const DOHString_or_char *s1, int ch) {
+ return strchr(Char(s1), ch);
+}
diff --git a/contrib/tools/swig/Source/DOH/void.c b/contrib/tools/swig/Source/DOH/void.c
new file mode 100644
index 0000000000..bbecca21b4
--- /dev/null
+++ b/contrib/tools/swig/Source/DOH/void.c
@@ -0,0 +1,96 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * void.c
+ *
+ * Implements a "void" object that is really just a DOH container around
+ * an arbitrary C object represented as a void *.
+ * ----------------------------------------------------------------------------- */
+
+#include "dohint.h"
+
+typedef struct {
+ void *ptr;
+ void (*del) (void *);
+} VoidObj;
+
+/* -----------------------------------------------------------------------------
+ * Void_delete()
+ *
+ * Delete a void object. Invokes the destructor supplied at the time of creation.
+ * ----------------------------------------------------------------------------- */
+
+static void Void_delete(DOH *vo) {
+ VoidObj *v = (VoidObj *) ObjData(vo);
+ if (v->del)
+ (*v->del) (v->ptr);
+ DohFree(v);
+}
+
+/* -----------------------------------------------------------------------------
+ * Void_copy()
+ *
+ * Copies a void object. This is only a shallow copy. The object destruction
+ * function is not copied in order to avoid potential double-free problems.
+ * ----------------------------------------------------------------------------- */
+
+static DOH *Void_copy(DOH *vo) {
+ VoidObj *v = (VoidObj *) ObjData(vo);
+ return NewVoid(v->ptr, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Void_data()
+ *
+ * Returns the void * stored in the object.
+ * ----------------------------------------------------------------------------- */
+
+static void *Void_data(DOH *vo) {
+ VoidObj *v = (VoidObj *) ObjData(vo);
+ return v->ptr;
+}
+
+static DohObjInfo DohVoidType = {
+ "VoidObj", /* objname */
+ Void_delete, /* doh_del */
+ Void_copy, /* doh_copy */
+ 0, /* doh_clear */
+ 0, /* doh_str */
+ Void_data, /* doh_data */
+ 0, /* doh_dump */
+ 0, /* doh_len */
+ 0, /* doh_hash */
+ 0, /* doh_cmp */
+ 0, /* doh_equal */
+ 0, /* doh_first */
+ 0, /* doh_next */
+ 0, /* doh_setfile */
+ 0, /* doh_getfile */
+ 0, /* doh_setline */
+ 0, /* doh_getline */
+ 0, /* doh_mapping */
+ 0, /* doh_sequence */
+ 0, /* doh_file */
+ 0, /* doh_string */
+ 0, /* doh_reserved */
+ 0, /* clientdata */
+};
+
+/* -----------------------------------------------------------------------------
+ * NewVoid()
+ *
+ * Creates a new Void object given a void * and an optional destructor function.
+ * ----------------------------------------------------------------------------- */
+
+DOH *DohNewVoid(void *obj, void (*del) (void *)) {
+ VoidObj *v;
+ v = (VoidObj *) DohMalloc(sizeof(VoidObj));
+ v->ptr = obj;
+ v->del = del;
+ return DohObjMalloc(&DohVoidType, v);
+}
diff --git a/contrib/tools/swig/Source/Doxygen/doxycommands.h b/contrib/tools/swig/Source/Doxygen/doxycommands.h
new file mode 100644
index 0000000000..782b6ab944
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxycommands.h
@@ -0,0 +1,174 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxycommands.h
+ *
+ * Part of the Doxygen comment translation module of SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef DOXYGENCOMMANDS_H
+#define DOXYGENCOMMANDS_H
+
+// doxy commands are not processed inside this block
+const char *CMD_HTML_ONLY = "htmlonly";
+// doxy commands are not processed inside this block
+const char *CMD_VERBATIM = "verbatim";
+const char *CMD_CODE = "code";
+const char *CMD_LATEX_1 = "f$";
+const char *CMD_LATEX_2 = "f{";
+const char *CMD_LATEX_3 = "f[";
+const char *CMD_END_HTML_ONLY = "endhtmlonly";
+const char *CMD_END_VERBATIM = "endverbatim";
+const char *CMD_END_CODE = "endcode";
+const char *CMD_END_LATEX_1 = "f$";
+const char *CMD_END_LATEX_2 = "f}";
+const char *CMD_END_LATEX_3 = "f]";
+
+const char *sectionIndicators[] = {
+ "attention", "author", "authors", "brief", "bug", "cond", "date",
+ "deprecated", "details", "else", "elseif", "endcond", "endif",
+ "exception", "if", "ifnot", "invariant", "note", "par", "param",
+ "tparam", "post", "pre", "remarks", "remark", "result", "return",
+ "returns", "retval", "sa", "see", "since", "test", "throw", "throws",
+ "todo", "version", "warning", "xrefitem"
+};
+
+const int sectionIndicatorsSize = sizeof(sectionIndicators) / sizeof(*sectionIndicators);
+
+/* All of the doxygen commands divided up by how they are parsed */
+const char *simpleCommands[] = {
+ // the first line are escaped chars, except \~, which is a language ID command.
+ "n", "$", "@", "\\", "&", "~", "<", ">", "#", "%", "\"", ".", "::",
+ // Member groups, which we currently ignore.
+ "{", "}",
+ "endcond",
+ "callgraph", "callergraph", "showinitializer", "hideinitializer", "internal",
+ "nosubgrouping", "public", "publicsection", "private", "privatesection",
+ "protected", "protectedsection", "tableofcontents"
+};
+
+const int simpleCommandsSize = sizeof(simpleCommands) / sizeof(*simpleCommands);
+
+const char *commandWords[] = {
+ "a", "b", "c", "e", "em", "p", "def", "enum", "package", "relates",
+ "namespace", "relatesalso", "anchor", "dontinclude", "include",
+ "includelineno", "copydoc", "copybrief", "copydetails", "verbinclude",
+ "htmlinclude", "extends", "implements", "memberof", "related", "relatedalso",
+ "cite"
+};
+
+const int commandWordsSize = sizeof(commandWords) / sizeof(*commandWords);
+
+const char *commandLines[] = {
+ "addindex", "fn", "name", "line", "var", "skipline", "typedef", "skip",
+ "until", "property"
+};
+
+const int commandLinesSize = sizeof(commandLines) / sizeof(*commandLines);
+
+const char *commandParagraph[] = {
+ "partofdescription", "result", "return", "returns", "remarks", "remark",
+ "since", "test", "sa", "see", "pre", "post", "details", "invariant",
+ "deprecated", "date", "note", "warning", "version", "todo", "bug",
+ "attention", "brief", "author", "authors", "copyright", "short"
+};
+
+const int commandParagraphSize = sizeof(commandParagraph) / sizeof(*commandParagraph);
+
+const char *commandEndCommands[] = {
+ CMD_HTML_ONLY, "latexonly", "manonly", "xmlonly", "link", "rtfonly"
+};
+
+const int commandEndCommandsSize = sizeof(commandEndCommands) / sizeof(*commandEndCommands);
+
+const char *commandWordParagraphs[] = {
+ "param", "tparam", "throw", "throws", "retval", "exception", "example"
+};
+
+const int commandWordParagraphsSize = sizeof(commandWordParagraphs) / sizeof(*commandWordParagraphs);
+
+const char *commandWordLines[] = {
+ "page", "subsection", "subsubsection", "section", "paragraph", "defgroup",
+ "snippet", "mainpage"
+};
+
+const int commandWordLinesSize = sizeof(commandWordLines) / sizeof(*commandWordLines);
+
+const char *commandWordOWordOWords[] = {
+ "category", "class", "protocol", "interface", "struct", "union"
+};
+
+const int commandWordOWordOWordsSize = sizeof(commandWordOWordOWords) / sizeof(*commandWordOWordOWords);
+
+const char *commandOWords[] = {
+ "dir", "file", "cond"
+};
+
+const int commandOWordsSize = sizeof(commandOWords) / sizeof(*commandOWords);
+
+const char *commandErrorThrowings[] = {
+ "annotatedclassstd::list", "classhierarchy", "define", "functionindex", "header",
+ "headerfilestd::list", "inherit", "l", "postheader", "endcode", "enddot", "endmsc", "endhtmlonly",
+ "endlatexonly", "endmanonly", "endlink", "endverbatim", "endxmlonly", "f]", "f}", "endif", "else",
+ "endrtfonly"
+};
+
+const int commandErrorThrowingsSize = sizeof(commandErrorThrowings) / sizeof(*commandErrorThrowings);
+
+const char *commandUniques[] = {
+ "xrefitem", "arg", "ingroup", "par", "headerfile", "overload", "weakgroup", "ref", "subpage", "dotfile", "image", "addtogroup", "li",
+ "if", "ifnot", "elseif", "else", "mscfile", "code", CMD_VERBATIM, "f{", "f[", "f$", "dot", "msc"
+};
+
+const int commandUniquesSize = sizeof(commandUniques) / sizeof(*commandUniques);
+
+// These HTML commands are transformed when producing output in other formats.
+// Other commands are left intact, but '<' and '> are replaced with entities in HTML
+// output. So <varName> appears as &lt;varName&gt; in HTML output. The same
+// behavior must be repeated by SWIG. See Doxygen doc for the list of commands.
+// '<' is prepended to distinguish HTML tags from Doxygen commands.
+const char *commandHtml[] = {
+ "<a", "<b", "<blockquote", "<body", "<br", "<center", "<caption", "<code", "<dd", "<dfn",
+ "<div", "<dl", "<dt", "<em", "<form", "<hr", "<h1", "<h2", "<h3", "<i", "<input", "<img",
+ "<li", "<meta", "<multicol", "<ol", "<p", "<pre", "<small", "<span", "<strong",
+ "<sub", "<sup", "<table", "<td", "<th", "<tr", "<tt", "<kbd", "<ul", "<var"
+};
+
+const int commandHtmlSize = sizeof(commandHtml) / sizeof(*commandHtml);
+
+// Only entities which are translatable to plain text are used here. Others
+// are copied unchanged to output.
+const char *commandHtmlEntities[] = {
+ "&copy", // (C)
+ "&trade", // (TM)
+ "&reg", // (R)
+ "&lt", // less-than symbol
+ "&gt", // greater-than symbol
+ "&amp", // ampersand
+ "&apos", // single quotation mark (straight)
+ "&quot", // double quotation mark (straight)
+ "&lsquo", // left single quotation mark
+ "&rsquo", // right single quotation mark
+ "&ldquo", // left double quotation mark
+ "&rdquo", // right double quotation mark
+ "&ndash", // n-dash (for numeric ranges, e.g. 2–8)
+ "&mdash", // --
+ "&nbsp", //
+ "&times", // x
+ "&minus", // -
+ "&sdot", // .
+ "&sim", // ~
+ "&le", // <=
+ "&ge", // >=
+ "&larr", // <--
+ "&rarr" // -->
+};
+
+const int commandHtmlEntitiesSize = sizeof(commandHtmlEntities) / sizeof(*commandHtmlEntities);
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/doxyentity.cxx b/contrib/tools/swig/Source/Doxygen/doxyentity.cxx
new file mode 100644
index 0000000000..6fe97dd362
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxyentity.cxx
@@ -0,0 +1,69 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxyentity.cxx
+ *
+ * Part of the Doxygen comment translation module of SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "doxyentity.h"
+#include <iostream>
+
+using std::cout;
+
+DoxygenEntity::DoxygenEntity(const std::string &typeEnt):typeOfEntity(typeEnt), isLeaf(true) {
+}
+
+
+/* Basic node for commands that have
+ * only 1 item after them
+ * example: \b word
+ * OR holding a std::string
+ */
+DoxygenEntity::DoxygenEntity(const std::string &typeEnt, const std::string &param1) : typeOfEntity(typeEnt), data(param1), isLeaf(true) {
+}
+
+
+/* Nonterminal node
+ * contains
+ */
+DoxygenEntity::DoxygenEntity(const std::string &typeEnt, const DoxygenEntityList &entList) : typeOfEntity(typeEnt), isLeaf(false), entityList(entList) {
+}
+
+
+void DoxygenEntity::printEntity(int level) const {
+
+ int thisLevel = level;
+
+ if (isLeaf) {
+ for (int i = 0; i < thisLevel; i++) {
+ cout << "\t";
+ }
+
+ cout << "Node Leaf Command: '" << typeOfEntity << "', ";
+
+ if (!data.empty()) {
+ cout << "Node Data: '" << data << "'";
+ }
+ cout << std::endl;
+
+ } else {
+
+ for (int i = 0; i < thisLevel; i++) {
+ cout << "\t";
+ }
+
+ cout << "Node Command: '" << typeOfEntity << "'" << std::endl;
+
+ thisLevel++;
+
+ for (DoxygenEntityListCIt p = entityList.begin(); p != entityList.end(); p++) {
+ p->printEntity(thisLevel);
+ }
+ }
+}
diff --git a/contrib/tools/swig/Source/Doxygen/doxyentity.h b/contrib/tools/swig/Source/Doxygen/doxyentity.h
new file mode 100644
index 0000000000..e475141a3a
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxyentity.h
@@ -0,0 +1,45 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxyentity.h
+ *
+ * Part of the Doxygen comment translation module of SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_DOXYENTITY_H
+#define SWIG_DOXYENTITY_H
+
+#include <string>
+#include <list>
+
+
+class DoxygenEntity;
+
+typedef std::list<DoxygenEntity> DoxygenEntityList;
+typedef DoxygenEntityList::iterator DoxygenEntityListIt;
+typedef DoxygenEntityList::const_iterator DoxygenEntityListCIt;
+
+
+/*
+ * Structure to represent a doxygen comment entry
+ */
+class DoxygenEntity {
+public:
+ std::string typeOfEntity;
+ std::string data;
+ bool isLeaf;
+ DoxygenEntityList entityList;
+
+ DoxygenEntity(const std::string &typeEnt);
+ DoxygenEntity(const std::string &typeEnt, const std::string &param1);
+ DoxygenEntity(const std::string &typeEnt, const DoxygenEntityList &entList);
+
+ void printEntity(int level) const;
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/doxyparser.cxx b/contrib/tools/swig/Source/Doxygen/doxyparser.cxx
new file mode 100644
index 0000000000..0c445f1a04
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxyparser.cxx
@@ -0,0 +1,1494 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxyparser.cxx
+ * ----------------------------------------------------------------------------- */
+
+#include "doxyparser.h"
+#include "doxycommands.h"
+#include "swig.h"
+#include "swigwarn.h"
+
+#include <iostream>
+#include <algorithm>
+#include <vector>
+
+using std::string;
+using std::cout;
+using std::endl;
+
+// This constant defines the (only) characters valid inside a Doxygen "word".
+// It includes some unusual ones because of the commands such as \f[, \f{, \f],
+// \f} and \f$.
+static const char *DOXYGEN_WORD_CHARS = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "$[]{}";
+
+// Define static class members
+DoxygenParser::DoxyCommandsMap DoxygenParser::doxygenCommands;
+std::set<std::string> DoxygenParser::doxygenSectionIndicators;
+
+const int TOKENSPERLINE = 8; //change this to change the printing behaviour of the token list
+const std::string END_HTML_TAG_MARK("/");
+
+std::string getBaseCommand(const std::string &cmd) {
+ if (cmd.substr(0,5) == "param")
+ return "param";
+ else if (cmd.substr(0,4) == "code")
+ return "code";
+ else
+ return cmd;
+}
+
+// Find the first position beyond the word command. Extra logic is
+// used to avoid putting the characters "," and "." in
+// DOXYGEN_WORD_CHARS.
+static size_t getEndOfWordCommand(const std::string &line, size_t pos) {
+ size_t endOfWordPos = line.find_first_not_of(DOXYGEN_WORD_CHARS, pos);
+ if (line.substr(pos, 6) == "param[")
+ // include ",", which can appear in param[in,out]
+ endOfWordPos = line.find_first_not_of(string(DOXYGEN_WORD_CHARS)+ ",", pos);
+ else if (line.substr(pos, 5) == "code{")
+ // include ".", which can appear in e.g. code{.py}
+ endOfWordPos = line.find_first_not_of(string(DOXYGEN_WORD_CHARS)+ ".", pos);
+ return endOfWordPos;
+}
+
+
+DoxygenParser::DoxygenParser(bool noisy) : noisy(noisy) {
+ fillTables();
+}
+
+DoxygenParser::~DoxygenParser() {
+}
+
+void DoxygenParser::fillTables() {
+ // run it only once
+ if (doxygenCommands.size())
+ return;
+
+ // fill in tables with data from doxycommands.h
+ for (int i = 0; i < simpleCommandsSize; i++)
+ doxygenCommands[simpleCommands[i]] = SIMPLECOMMAND;
+
+ for (int i = 0; i < commandWordsSize; i++)
+ doxygenCommands[commandWords[i]] = COMMANDWORD;
+
+ for (int i = 0; i < commandLinesSize; i++)
+ doxygenCommands[commandLines[i]] = COMMANDLINE;
+
+ for (int i = 0; i < commandParagraphSize; i++)
+ doxygenCommands[commandParagraph[i]] = COMMANDPARAGRAPH;
+
+ for (int i = 0; i < commandEndCommandsSize; i++)
+ doxygenCommands[commandEndCommands[i]] = COMMANDENDCOMMAND;
+
+ for (int i = 0; i < commandWordParagraphsSize; i++)
+ doxygenCommands[commandWordParagraphs[i]] = COMMANDWORDPARAGRAPH;
+
+ for (int i = 0; i < commandWordLinesSize; i++)
+ doxygenCommands[commandWordLines[i]] = COMMANDWORDLINE;
+
+ for (int i = 0; i < commandWordOWordOWordsSize; i++)
+ doxygenCommands[commandWordOWordOWords[i]] = COMMANDWORDOWORDWORD;
+
+ for (int i = 0; i < commandOWordsSize; i++)
+ doxygenCommands[commandOWords[i]] = COMMANDOWORD;
+
+ for (int i = 0; i < commandErrorThrowingsSize; i++)
+ doxygenCommands[commandErrorThrowings[i]] = COMMANDERRORTHROW;
+
+ for (int i = 0; i < commandUniquesSize; i++)
+ doxygenCommands[commandUniques[i]] = COMMANDUNIQUE;
+
+ for (int i = 0; i < commandHtmlSize; i++)
+ doxygenCommands[commandHtml[i]] = COMMAND_HTML;
+
+ for (int i = 0; i < commandHtmlEntitiesSize; i++)
+ doxygenCommands[commandHtmlEntities[i]] = COMMAND_HTML_ENTITY;
+
+ // fill section indicators command set
+ for (int i = 0; i < sectionIndicatorsSize; i++)
+ doxygenSectionIndicators.insert(sectionIndicators[i]);
+}
+
+std::string DoxygenParser::stringToLower(const std::string &stringToConvert) {
+
+ string result(stringToConvert.size(), ' ');
+
+ for (size_t i = 0; i < result.size(); i++) {
+ result[i] = tolower(stringToConvert[i]);
+ }
+
+ return result;
+}
+
+bool DoxygenParser::isSectionIndicator(const std::string &smallString) {
+
+ std::set<std::string>::iterator it = doxygenSectionIndicators.find(stringToLower(smallString));
+
+ return it != doxygenSectionIndicators.end();
+}
+
+void DoxygenParser::printTree(const DoxygenEntityList &rootList) {
+ DoxygenEntityList::const_iterator p = rootList.begin();
+ while (p != rootList.end()) {
+ (*p).printEntity(0);
+ p++;
+ }
+}
+
+DoxygenParser::DoxyCommandEnum DoxygenParser::commandBelongs(const std::string &theCommand) {
+ DoxyCommandsMapIt it = doxygenCommands.find(stringToLower(getBaseCommand(theCommand)));
+
+ if (it != doxygenCommands.end()) {
+ return it->second;
+ }
+ // Check if this command is defined as an alias.
+ if (Getattr(m_node, ("feature:doxygen:alias:" + theCommand).c_str())) {
+ return COMMAND_ALIAS;
+ }
+ // Check if this command should be ignored.
+ if (String *const ignore = getIgnoreFeature(theCommand)) {
+ // Check that no value is specified for this feature ("1" is the implicit
+ // one given to it by SWIG itself), we may use the value in the future, but
+ // for now we only use the attributes.
+ if (Strcmp(ignore, "1") != 0) {
+ Swig_warning(WARN_PP_UNEXPECTED_TOKENS, m_fileName.c_str(), m_fileLineNo,
+ "Feature \"doxygen:ignore\" value ignored for Doxygen command \"%s\".\n", theCommand.c_str());
+ }
+ // Also ensure that the matching end command, if any, will be recognized.
+ const string endCommand = getIgnoreFeatureEndCommand(theCommand);
+ if (!endCommand.empty()) {
+ Setattr(m_node, ("feature:doxygen:ignore:" + endCommand).c_str(), NewString("1"));
+ }
+
+ return COMMAND_IGNORE;
+ }
+
+ return NONE;
+}
+
+std::string DoxygenParser::trim(const std::string &text) {
+ size_t start = text.find_first_not_of(" \t");
+ size_t end = text.find_last_not_of(" \t");
+
+ if (start == string::npos || start > end) {
+ return "";
+ }
+ return text.substr(start, end - start + 1);
+}
+
+bool DoxygenParser::isEndOfLine() {
+ if (m_tokenListIt == m_tokenList.end()) {
+ return false;
+ }
+ Token nextToken = *m_tokenListIt;
+ return nextToken.m_tokenType == END_LINE;
+}
+
+void DoxygenParser::skipWhitespaceTokens() {
+ if (m_tokenListIt == m_tokenList.end()) {
+ return;
+ }
+
+ while (m_tokenListIt != m_tokenList.end()
+ && (m_tokenListIt->m_tokenType == END_LINE || trim(m_tokenListIt->m_tokenString).empty())) {
+
+ m_tokenListIt++;
+ }
+}
+
+std::string DoxygenParser::getNextToken() {
+
+ if (m_tokenListIt == m_tokenList.end()) {
+ return "";
+ }
+
+ if (m_tokenListIt->m_tokenType == PLAINSTRING) {
+ return (m_tokenListIt++)->m_tokenString;
+ }
+
+ return "";
+}
+
+std::string DoxygenParser::getNextWord() {
+
+ /* if (m_tokenListIt == m_tokenList.end()) {
+ return "";
+ }
+ */
+ while (m_tokenListIt != m_tokenList.end()
+ && (m_tokenListIt->m_tokenType == PLAINSTRING)) {
+ // handle quoted strings as words
+ string token = m_tokenListIt->m_tokenString;
+ if (token == "\"") {
+
+ string word = m_tokenListIt->m_tokenString;
+ m_tokenListIt++;
+ while (true) {
+ string nextWord = getNextToken();
+ if (nextWord.empty()) { // maybe report unterminated string error
+ return word;
+ }
+ word += nextWord;
+ if (nextWord == "\"") {
+ return word;
+ }
+ }
+ }
+
+ string tokenStr = trim(m_tokenListIt->m_tokenString);
+ m_tokenListIt++;
+ if (!tokenStr.empty()) {
+ return tokenStr;
+ }
+ }
+
+ return "";
+}
+
+DoxygenParser::TokenListCIt DoxygenParser::getOneLine(const TokenList &tokList) {
+
+ TokenListCIt endOfLineIt = m_tokenListIt;
+
+ while (endOfLineIt != tokList.end()) {
+ if (endOfLineIt->m_tokenType == END_LINE) {
+ return endOfLineIt;
+ }
+ endOfLineIt++;
+ }
+
+ return tokList.end();
+}
+
+std::string DoxygenParser::getStringTilCommand(const TokenList &tokList) {
+
+ if (m_tokenListIt == tokList.end()) {
+ return "";
+ }
+
+ string description;
+
+ while (m_tokenListIt->m_tokenType == PLAINSTRING) {
+ const Token &currentToken = *m_tokenListIt++;
+ if (currentToken.m_tokenType == PLAINSTRING) {
+ description = description + currentToken.m_tokenString; // + " ";
+ }
+ }
+ return description;
+}
+
+std::string DoxygenParser::getStringTilEndCommand(const std::string &theCommand, const TokenList &tokList) {
+
+ if (m_tokenListIt == tokList.end()) {
+ return "";
+ }
+
+ string description;
+ while (m_tokenListIt != tokList.end()) {
+
+ if (m_tokenListIt->m_tokenType == PLAINSTRING) {
+ description += m_tokenListIt->m_tokenString;
+ } else if (m_tokenListIt->m_tokenType == END_LINE) {
+ description += "\n";
+ } else if (m_tokenListIt->m_tokenString == theCommand) {
+ m_tokenListIt++;
+ return description;
+ }
+
+ m_tokenListIt++;
+ }
+
+ printListError(WARN_DOXYGEN_COMMAND_EXPECTED, "Expected Doxygen command: " + theCommand + ".");
+
+ return description;
+}
+
+DoxygenParser::TokenListCIt DoxygenParser::getEndOfParagraph(const TokenList &tokList) {
+
+ TokenListCIt endOfParagraph = m_tokenListIt;
+
+ while (endOfParagraph != tokList.end()) {
+ // If \code or \verbatim is encountered within a paragraph, then
+ // go all the way to the end of that command, since the content
+ // could contain empty lines that would appear to be paragraph
+ // ends:
+ if (endOfParagraph->m_tokenType == COMMAND &&
+ (endOfParagraph->m_tokenString == "code" ||
+ endOfParagraph->m_tokenString == "verbatim")) {
+ const string theCommand = endOfParagraph->m_tokenString;
+ endOfParagraph = getEndCommand("end" + theCommand, tokList);
+ endOfParagraph++; // Move after the end command
+ return endOfParagraph;
+ }
+ if (endOfParagraph->m_tokenType == END_LINE) {
+ endOfParagraph++;
+ if (endOfParagraph != tokList.end()
+ && endOfParagraph->m_tokenType == END_LINE) {
+ endOfParagraph++;
+ //cout << "ENCOUNTERED END OF PARA" << endl;
+ return endOfParagraph;
+ }
+
+ } else if (endOfParagraph->m_tokenType == COMMAND) {
+
+ if (isSectionIndicator(getBaseCommand(endOfParagraph->m_tokenString))) {
+ return endOfParagraph;
+ } else {
+ endOfParagraph++;
+ }
+
+ } else if (endOfParagraph->m_tokenType == PLAINSTRING) {
+ endOfParagraph++;
+ } else {
+ return tokList.end();
+ }
+ }
+
+ return tokList.end();
+}
+
+DoxygenParser::TokenListCIt DoxygenParser::getEndOfSection(const std::string &theCommand, const TokenList &tokList) {
+
+ TokenListCIt endOfParagraph = m_tokenListIt;
+
+ while (endOfParagraph != tokList.end()) {
+ if (endOfParagraph->m_tokenType == COMMAND) {
+ if (theCommand == endOfParagraph->m_tokenString)
+ return endOfParagraph;
+ else
+ endOfParagraph++;
+ } else if (endOfParagraph->m_tokenType == PLAINSTRING) {
+ endOfParagraph++;
+ } else if (endOfParagraph->m_tokenType == END_LINE) {
+ endOfParagraph++;
+ if (endOfParagraph->m_tokenType == END_LINE) {
+ endOfParagraph++;
+ return endOfParagraph;
+ }
+ }
+ }
+ return tokList.end();
+}
+
+DoxygenParser::TokenListCIt DoxygenParser::getEndCommand(const std::string &theCommand, const TokenList &tokList) {
+
+ TokenListCIt endOfCommand = m_tokenListIt;
+
+ while (endOfCommand != tokList.end()) {
+ endOfCommand++;
+ if ((*endOfCommand).m_tokenType == COMMAND) {
+ if (theCommand == (*endOfCommand).m_tokenString) {
+ return endOfCommand;
+ }
+ }
+ }
+ //End command not found
+ return tokList.end();
+}
+
+void DoxygenParser::skipEndOfLine() {
+ if (m_tokenListIt != m_tokenList.end()
+ && m_tokenListIt->m_tokenType == END_LINE) {
+ m_tokenListIt++;
+ }
+}
+
+void DoxygenParser::addSimpleCommand(const std::string &theCommand, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ doxyList.push_back(DoxygenEntity(theCommand));
+}
+
+void DoxygenParser::addCommandWord(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ if (isEndOfLine()) {
+ // handles cases when command is at the end of line (for example "\c\nreally"
+ skipWhitespaceTokens();
+ doxyList.push_back(DoxygenEntity("plainstd::endl"));
+ }
+ std::string name = getNextWord();
+ if (!name.empty()) {
+ DoxygenEntityList aNewList;
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ } else {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ }
+}
+
+void DoxygenParser::addCommandLine(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ TokenListCIt endOfLine = getOneLine(tokList);
+ DoxygenEntityList aNewList = parse(endOfLine, tokList);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ skipEndOfLine();
+}
+
+void DoxygenParser::addCommandParagraph(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ TokenListCIt endOfParagraph = getEndOfParagraph(tokList);
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfParagraph, tokList);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandEndCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ TokenListCIt endCommand = getEndCommand("end" + theCommand, tokList);
+ if (endCommand == tokList.end()) {
+ printListError(WARN_DOXYGEN_COMMAND_EXPECTED, "Expected Doxygen command: end" + theCommand + ".");
+ return;
+ }
+ DoxygenEntityList aNewList;
+ aNewList = parse(endCommand, tokList);
+ m_tokenListIt++;
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandWordParagraph(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string name = getNextWord();
+
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+ TokenListCIt endOfParagraph = getEndOfParagraph(tokList);
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfParagraph, tokList);
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandWordLine(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+
+ TokenListCIt endOfLine = getOneLine(tokList);
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfLine, tokList);
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ //else cout << "No line followed " << theCommand << " command. Not added" << endl;
+}
+
+void DoxygenParser::addCommandWordOWordOWord(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+ std::string headerfile = getNextWord();
+ std::string headername = getNextWord();
+ DoxygenEntityList aNewList;
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ if (!headerfile.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", headerfile));
+ if (!headername.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", headername));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandOWord(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string name = getNextWord();
+ DoxygenEntityList aNewList;
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandErrorThrow(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &) {
+
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": Unexpectedly encountered this command.");
+ m_tokenListIt = getOneLine(tokList);
+}
+
+void DoxygenParser::addCommandHtml(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string htmlTagArgs = getNextToken();
+ doxyList.push_back(DoxygenEntity(theCommand, htmlTagArgs));
+}
+
+void DoxygenParser::addCommandHtmlEntity(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ DoxygenEntityList aNewList;
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandUnique(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+
+ static std::map<std::string, std::string> endCommands;
+ DoxygenEntityList aNewList;
+ if (theCommand == "arg" || theCommand == "li") {
+ TokenListCIt endOfSection = getEndOfSection(theCommand, tokList);
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfSection, tokList);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \xrefitem <key> "(heading)" "(std::list title)" {text}
+ else if (theCommand == "xrefitem") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string key = getNextWord();
+ if (key.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No key followed the command. Command ignored.");
+ return;
+ }
+ std::string heading = getNextWord();
+ if (key.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No heading followed the command. Command ignored.");
+ return;
+ }
+ std::string title = getNextWord();
+ if (title.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No title followed the command. Command ignored.");
+ return;
+ }
+ TokenListCIt endOfParagraph = getEndOfParagraph(tokList);
+ aNewList = parse(endOfParagraph, tokList);
+ aNewList.push_front(DoxygenEntity("plainstd::string", title));
+ aNewList.push_front(DoxygenEntity("plainstd::string", heading));
+ aNewList.push_front(DoxygenEntity("plainstd::string", key));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \ingroup (<groupname> [<groupname> <groupname>])
+ else if (theCommand == "ingroup") {
+ std::string name = getNextWord();
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ name = getNextWord();
+ if (!name.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ name = getNextWord();
+ if (!name.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \par [(paragraph title)] { paragraph }
+ else if (theCommand == "par") {
+ TokenListCIt endOfLine = getOneLine(tokList);
+ aNewList = parse(endOfLine, tokList);
+ DoxygenEntityList aNewList2;
+ TokenListCIt endOfParagraph = getEndOfParagraph(tokList);
+ aNewList2 = parse(endOfParagraph, tokList);
+ aNewList.splice(aNewList.end(), aNewList2);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \headerfile <header-file> [<header-name>]
+ else if (theCommand == "headerfile") {
+ DoxygenEntityList aNewList;
+ std::string name = getNextWord();
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ name = getNextWord();
+ if (!name.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \overload [(function declaration)]
+ else if (theCommand == "overload") {
+ TokenListCIt endOfLine = getOneLine(tokList);
+ if (endOfLine != m_tokenListIt) {
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfLine, tokList);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ } else
+ doxyList.push_back(DoxygenEntity(theCommand));
+ }
+ // \weakgroup <name> [(title)]
+ else if (theCommand == "weakgroup") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+ DoxygenEntityList aNewList;
+ TokenListCIt endOfLine = getOneLine(tokList);
+ if (endOfLine != m_tokenListIt) {
+ aNewList = parse(endOfLine, tokList);
+ }
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \ref <name> ["(text)"]
+ else if (theCommand == "ref") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No key followed the command. Command ignored.");
+ return;
+ }
+ DoxygenEntityList aNewList;
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ // TokenListCIt endOfLine = getOneLine(tokList);
+ // if (endOfLine != m_tokenListIt) {
+ // aNewList = parse(endOfLine, tokList);
+ //}
+ TokenListCIt tmpIt = m_tokenListIt;
+ std::string refTitle = getNextWord();
+ // If title is following the ref tag, it must be quoted. Otherwise
+ // doxy puts link on ref id.
+ if (refTitle.size() > 1 && refTitle[0] == '"') {
+ // remove quotes
+ refTitle = refTitle.substr(1, refTitle.size() - 2);
+ aNewList.push_back(DoxygenEntity("plainstd::string", refTitle));
+ } else {
+ // no quoted string is following, so we have to restore iterator
+ m_tokenListIt = tmpIt;
+ }
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \subpage <name> ["(text)"]
+ else if (theCommand == "subpage") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No name followed the command. Command ignored.");
+ return;
+ }
+ std::string text = getNextWord();
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ if (!text.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", text));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \code ... \endcode
+ // \verbatim ... \endverbatim
+ // \dot dotcode \enddot
+ // \msc msccode \endmsc
+ // \f[ ... \f]
+ // \f{ ... \f}
+ // \f{env}{ ... \f}
+ // \f$ ... \f$
+ else if (getBaseCommand(theCommand) == "code" || theCommand == "verbatim"
+ || theCommand == "dot" || theCommand == "msc" || theCommand == "f[" || theCommand == "f{" || theCommand == "f$") {
+ if (!endCommands.size()) {
+ // fill in static table of end commands
+ endCommands["f["] = "f]";
+ endCommands["f{"] = "f}";
+ endCommands["f$"] = "f$";
+ }
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string endCommand;
+ std::map<std::string, std::string>::iterator it;
+ it = endCommands.find(theCommand);
+ if (it != endCommands.end())
+ endCommand = it->second;
+ else
+ endCommand = "end" + getBaseCommand(theCommand);
+
+ std::string content = getStringTilEndCommand(endCommand, tokList);
+ aNewList.push_back(DoxygenEntity("plainstd::string", content));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \dotfile <file> ["caption"]
+ // \mscfile <file> ["caption"]
+ else if (theCommand == "dotfile" || theCommand == "mscfile") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string file = getNextWord();
+ if (file.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No file followed the command. Command ignored.");
+ return;
+ }
+ std::string caption = getNextWord();
+ aNewList.push_back(DoxygenEntity("plainstd::string", file));
+ if (!caption.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", caption));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \image <format> <file> ["caption"] [<sizeindication>=<size>]
+ else if (theCommand == "image") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string format = getNextWord();
+ if (format.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No format followed the command. Command ignored.");
+ return;
+ }
+ std::string file = getNextWord();
+ if (file.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No name followed the command. Command ignored.");
+ return;
+ }
+ std::string caption = getNextWord();
+ std::string size = getNextWord();
+
+ DoxygenEntityList aNewList;
+ aNewList.push_back(DoxygenEntity("plainstd::string", format));
+ aNewList.push_back(DoxygenEntity("plainstd::string", file));
+ if (!caption.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", caption));
+ if (!size.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", size));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \addtogroup <name> [(title)]
+ else if (theCommand == "addtogroup") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": There should be at least one word following the command. Command ignored.");
+ return;
+ }
+ DoxygenEntityList aNewList;
+ TokenListCIt endOfLine = getOneLine(tokList);
+ if (endOfLine != m_tokenListIt) {
+ aNewList = parse(endOfLine, tokList);
+ }
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ skipEndOfLine();
+ }
+ // \if <cond> [\else ...] [\elseif <cond> ...] \endif
+ else if (theCommand == "if" || theCommand == "ifnot" || theCommand == "else" || theCommand == "elseif") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string cond;
+ bool skipEndif = false; // if true then we skip endif after parsing block of code
+ bool needsCond = (theCommand == "if" || theCommand == "ifnot" || theCommand == "elseif");
+ if (needsCond) {
+ cond = getNextWord();
+ if (cond.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+ }
+
+ int nestedCounter = 1;
+ TokenListCIt endCommand = tokList.end();
+
+ // go through the commands and find closing endif or else or elseif
+ for (TokenListCIt it = m_tokenListIt; it != tokList.end(); it++) {
+ if (it->m_tokenType == COMMAND) {
+ if (it->m_tokenString == "if" || it->m_tokenString == "ifnot")
+ nestedCounter++;
+ else if (it->m_tokenString == "endif")
+ nestedCounter--;
+ if (nestedCounter == 1 && (it->m_tokenString == "else" || it->m_tokenString == "elseif")) { // else found
+ endCommand = it;
+ break;
+ }
+ if (nestedCounter == 0) { // endif found
+ endCommand = it;
+ skipEndif = true;
+ break;
+ }
+ }
+ }
+
+ if (endCommand == tokList.end()) {
+ printListError(WARN_DOXYGEN_COMMAND_EXPECTED, "Expected Doxygen command: endif.");
+ return;
+ }
+
+ DoxygenEntityList aNewList;
+ aNewList = parse(endCommand, tokList);
+ if (skipEndif)
+ m_tokenListIt++;
+ if (needsCond)
+ aNewList.push_front(DoxygenEntity("plainstd::string", cond));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+}
+
+void DoxygenParser::aliasCommand(const std::string &theCommand, const TokenList &/* tokList */ , DoxygenEntityList &doxyList) {
+ String *const alias = Getattr(m_node, ("feature:doxygen:alias:" + theCommand).c_str());
+ if (!alias)
+ return;
+
+ doxyList.push_back(DoxygenEntity("plainstd::string", Char(alias)));
+}
+
+String *DoxygenParser::getIgnoreFeature(const std::string &theCommand, const char *argument) const {
+ string feature_name = "feature:doxygen:ignore:" + theCommand;
+ if (argument) {
+ feature_name += ':';
+ feature_name += argument;
+ }
+
+ return Getattr(m_node, feature_name.c_str());
+}
+
+string DoxygenParser::getIgnoreFeatureEndCommand(const std::string &theCommand) const {
+ // We may be dealing either with a simple command or with the starting command
+ // of a block, as indicated by the value of "range" starting with "end".
+ string endCommand;
+ if (String *const range = getIgnoreFeature(theCommand, "range")) {
+ const char *const p = Char(range);
+ if (strncmp(p, "end", 3) == 0) {
+ if (p[3] == ':') {
+ // Normally the end command name follows after the colon.
+ endCommand = p + 4;
+ } else if (p[3] == '\0') {
+ // But it may be omitted in which case the default Doxygen convention of
+ // using "something"/"endsomething" is used.
+ endCommand = "end" + theCommand;
+ }
+ }
+ }
+
+ return endCommand;
+}
+
+void DoxygenParser::ignoreCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ const string endCommand = getIgnoreFeatureEndCommand(theCommand);
+ if (!endCommand.empty()) {
+ TokenListCIt itEnd = getEndCommand(endCommand, tokList);
+ if (itEnd == tokList.end()) {
+ printListError(WARN_DOXYGEN_COMMAND_EXPECTED, "Expected Doxygen command: " + endCommand + ".");
+ return;
+ }
+ // If we ignore the command, also ignore any whitespace preceding it as we
+ // want to avoid having lines consisting of whitespace only or trailing
+ // whitespace in general (at least Python, with its pep8 tool, really
+ // doesn't like it).
+ if (!doxyList.empty()) {
+ DoxygenEntityList::iterator i = doxyList.end();
+ --i;
+ if (i->typeOfEntity == "plainstd::string" && i->data.find_first_not_of(" \t") == std::string::npos) {
+ doxyList.erase(i);
+ }
+ }
+ // Determine what to do with the part of the comment between the start and
+ // end commands: by default, we simply throw it away, but "contents"
+ // attribute may be used to change this.
+ if (String *const contents = getIgnoreFeature(theCommand, "contents")) {
+ // Currently only "parse" is supported but we may need to add "copy" to
+ // handle custom tags which contain text that is supposed to be copied
+ // verbatim in the future.
+ if (Strcmp(contents, "parse") == 0) {
+ DoxygenEntityList aNewList = parse(itEnd, tokList);
+ doxyList.splice(doxyList.end(), aNewList);
+ } else {
+ Swig_error(m_fileName.c_str(), m_fileLineNo, "Invalid \"doxygen:ignore\" feature \"contents\" attribute \"%s\".\n", Char(contents));
+ return;
+ }
+ }
+
+ m_tokenListIt = itEnd;
+ m_tokenListIt++;
+ } else if (String *const range = getIgnoreFeature(theCommand, "range")) {
+ // Currently we only support "line" but, in principle, we should also
+ // support "word" and "paragraph" for consistency with the built-in Doxygen
+ // commands which can have either of these three ranges (which are indicated
+ // using <word-arg>, (line-arg) and {para-arg} respectively in Doxygen
+ // documentation).
+ if (Strcmp(range, "line") == 0) {
+ // Consume everything until the end of line.
+ m_tokenListIt = getOneLine(tokList);
+ skipEndOfLine();
+ } else {
+ Swig_error(m_fileName.c_str(), m_fileLineNo, "Invalid \"doxygen:ignore\" feature \"range\" attribute \"%s\".\n", Char(range));
+ return;
+ }
+ }
+}
+
+void DoxygenParser::addCommand(const std::string &commandString, const TokenList &tokList, DoxygenEntityList &doxyList) {
+
+ string theCommand = stringToLower(commandString);
+
+ if (theCommand == "plainstd::string") {
+ string nextPhrase = getStringTilCommand(tokList);
+ if (noisy)
+ cout << "Parsing plain std::string :" << nextPhrase << endl;
+ doxyList.push_back(DoxygenEntity("plainstd::string", nextPhrase));
+ return;
+ }
+
+ switch (commandBelongs(commandString)) {
+ case SIMPLECOMMAND:
+ addSimpleCommand(theCommand, doxyList);
+ break;
+ case COMMANDWORD:
+ addCommandWord(theCommand, tokList, doxyList);
+ break;
+ case COMMANDLINE:
+ addCommandLine(theCommand, tokList, doxyList);
+ break;
+ case COMMANDPARAGRAPH:
+ addCommandParagraph(theCommand, tokList, doxyList);
+ break;
+ case COMMANDENDCOMMAND:
+ addCommandEndCommand(theCommand, tokList, doxyList);
+ break;
+ case COMMANDWORDPARAGRAPH:
+ addCommandWordParagraph(theCommand, tokList, doxyList);
+ break;
+ case COMMANDWORDLINE:
+ addCommandWordLine(theCommand, tokList, doxyList);
+ break;
+ case COMMANDWORDOWORDWORD:
+ addCommandWordOWordOWord(theCommand, tokList, doxyList);
+ break;
+ case COMMANDOWORD:
+ addCommandOWord(theCommand, tokList, doxyList);
+ break;
+ case COMMANDERRORTHROW:
+ addCommandErrorThrow(theCommand, tokList, doxyList);
+ break;
+ case COMMANDUNIQUE:
+ addCommandUnique(theCommand, tokList, doxyList);
+ break;
+ case COMMAND_HTML:
+ addCommandHtml(theCommand, tokList, doxyList);
+ break;
+ case COMMAND_HTML_ENTITY:
+ addCommandHtmlEntity(theCommand, tokList, doxyList);
+ break;
+ case COMMAND_ALIAS:
+ aliasCommand(commandString, tokList, doxyList);
+ break;
+ case COMMAND_IGNORE:
+ ignoreCommand(commandString, tokList, doxyList);
+ break;
+ case NONE:
+ case END_LINE:
+ case PARAGRAPH_END:
+ case PLAINSTRING:
+ case COMMAND:
+ // TODO: Ensure that these values either are correctly ignored here or can't happen.
+ break;
+ }
+}
+
+/**
+ * This method converts TokenList to DoxygenEntryList.
+ */
+DoxygenEntityList DoxygenParser::parse(TokenListCIt endParsingIndex, const TokenList &tokList, bool root) {
+ // if we are root, than any strings should be added as 'partofdescription', else as 'plainstd::string'
+ std::string currPlainstringCommandType = root ? "partofdescription" : "plainstd::string";
+ DoxygenEntityList aNewList;
+
+ // Less than check (instead of not equal) is a safeguard in case the
+ // iterator is incremented past the end
+ while (m_tokenListIt < endParsingIndex) {
+
+ Token currToken = *m_tokenListIt;
+
+ if (noisy)
+ cout << "Parsing for phrase starting in:" << currToken.toString() << endl;
+
+ if (currToken.m_tokenType == END_LINE) {
+ aNewList.push_back(DoxygenEntity("plainstd::endl"));
+ m_tokenListIt++;
+ } else if (currToken.m_tokenType == COMMAND) {
+ m_tokenListIt++;
+ addCommand(currToken.m_tokenString, tokList, aNewList);
+ } else if (currToken.m_tokenType == PLAINSTRING) {
+ addCommand(currPlainstringCommandType, tokList, aNewList);
+ }
+
+ // If addCommand above misbehaves, it can move the iterator past endParsingIndex
+ if (m_tokenListIt > endParsingIndex)
+ printListError(WARN_DOXYGEN_UNEXPECTED_ITERATOR_VALUE, "Unexpected iterator value in DoxygenParser::parse");
+
+ if (endParsingIndex != tokList.end() && m_tokenListIt == tokList.end()) {
+ // this could happen if we can't reach the original endParsingIndex
+ printListError(WARN_DOXYGEN_UNEXPECTED_END_OF_COMMENT, "Unexpected end of Doxygen comment encountered.");
+ break;
+ }
+ }
+ return aNewList;
+}
+
+DoxygenEntityList DoxygenParser::createTree(Node *node, String *documentation) {
+ m_node = node;
+
+ tokenizeDoxygenComment(Char(documentation), Char(Getfile(documentation)), Getline(documentation));
+
+ if (noisy) {
+ cout << "---TOKEN LIST---" << endl;
+ printList();
+ }
+
+ DoxygenEntityList rootList = parse(m_tokenList.end(), m_tokenList, true);
+
+ if (noisy) {
+ cout << "PARSED LIST" << endl;
+ printTree(rootList);
+ }
+ return rootList;
+}
+
+/*
+ * Splits 'text' on 'separator' chars. Separator chars are not part of the
+ * strings.
+ */
+DoxygenParser::StringVector DoxygenParser::split(const std::string &text, char separator) {
+ StringVector lines;
+ size_t prevPos = 0, pos = 0;
+
+ while (pos < string::npos) {
+ pos = text.find(separator, prevPos);
+ lines.push_back(text.substr(prevPos, pos - prevPos));
+ prevPos = pos + 1;
+ }
+
+ return lines;
+}
+
+/*
+ * Returns true, if 'c' is one of doxygen comment block start
+ * characters: *, /, or !
+ */
+bool DoxygenParser::isStartOfDoxyCommentChar(char c) {
+ return (strchr("*/!", c) != NULL);
+}
+
+/*
+ * Adds token with Doxygen command to token list, but only if command is one of
+ * Doxygen commands. In that case true is returned. If the command is not
+ * recognized as a doxygen command, it is ignored and false is returned.
+ */
+bool DoxygenParser::addDoxyCommand(DoxygenParser::TokenList &tokList, const std::string &cmd) {
+ if (commandBelongs(cmd) != NONE) {
+ tokList.push_back(Token(COMMAND, cmd));
+ return true;
+ } else {
+ if (cmd.empty()) {
+ // This actually indicates a bug in the code in this file, as this
+ // function shouldn't be called at all in this case.
+ printListError(WARN_DOXYGEN_UNKNOWN_COMMAND, "Unexpected empty Doxygen command.");
+ return false;
+ }
+
+ // This function is called for the special Doxygen commands, but also for
+ // HTML commands (or anything that looks like them, actually) and entities.
+ // We don't recognize all of those, so just ignore them and pass them
+ // through, but warn about unknown Doxygen commands as ignoring them will
+ // often result in wrong output being generated.
+ const char ch = *cmd.begin();
+ if (ch != '<' && ch != '&') {
+ // Before calling printListError() we must ensure that m_tokenListIt used
+ // by it is valid.
+ const TokenListCIt itSave = m_tokenListIt;
+ m_tokenListIt = m_tokenList.end();
+
+ printListError(WARN_DOXYGEN_UNKNOWN_COMMAND, "Unknown Doxygen command: " + cmd + ".");
+
+ m_tokenListIt = itSave;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * This method copies comment text to output as it is - no processing is
+ * done, Doxygen commands are ignored. It is used for commands \verbatim,
+ * \htmlonly, \f$, \f[, and \f{.
+ */
+size_t DoxygenParser::processVerbatimText(size_t pos, const std::string &line) {
+ if (line[pos] == '\\' || line[pos] == '@') { // check for end commands
+
+ pos++;
+ size_t endOfWordPos = line.find_first_not_of(DOXYGEN_WORD_CHARS, pos);
+ string cmd = line.substr(pos, endOfWordPos - pos);
+
+ if (cmd == CMD_END_HTML_ONLY || cmd == CMD_END_VERBATIM || cmd == CMD_END_LATEX_1 || cmd == CMD_END_LATEX_2 || cmd == CMD_END_LATEX_3 || cmd == CMD_END_CODE) {
+
+ m_isVerbatimText = false;
+ addDoxyCommand(m_tokenList, cmd);
+
+ } else {
+
+ m_tokenList.push_back(Token(PLAINSTRING,
+ // include '\' or '@'
+ line.substr(pos - 1, endOfWordPos - pos + 1)));
+ }
+
+ pos = endOfWordPos;
+
+ } else {
+
+ // whitespaces are stored as plain strings
+ size_t startOfPossibleEndCmd = line.find_first_of("\\@", pos);
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, startOfPossibleEndCmd - pos)));
+ pos = startOfPossibleEndCmd;
+ }
+
+ return pos;
+}
+
+/*
+ * Processes doxy commands for escaped characters: \$ \@ \\ \& \~ \< \> \# \% \" \. \::
+ * Handling this separately supports documentation text like \@someText.
+ */
+bool DoxygenParser::processEscapedChars(size_t &pos, const std::string &line) {
+ if ((pos + 1) < line.size()) {
+
+ // \ and @ with trailing whitespace or quoted get to output as plain string
+ string whitespaces = " '\t\n";
+ if (whitespaces.find(line[pos + 1]) != string::npos) {
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, 1)));
+ pos++;
+ return true;
+ }
+ // these chars can be escaped for doxygen
+ string escapedChars = "$@\\&~<>#%\".";
+ if (escapedChars.find(line[pos + 1]) != string::npos) {
+
+ addDoxyCommand(m_tokenList, line.substr(pos + 1, 1));
+ pos += 2;
+ return true;
+
+ } else if ((pos + 2) < line.size() && line[pos + 1] == ':' && line[pos + 2] == ':') {
+
+ // add command \:: - handling this separately supports documentation
+ // text like \::someText
+ addDoxyCommand(m_tokenList, line.substr(pos + 1, 2));
+ pos += 3;
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Processes word doxygen commands, like \arg, \c, \b, \return, ...
+ */
+void DoxygenParser::processWordCommands(size_t &pos, const std::string &line) {
+ pos++;
+ size_t endOfWordPos = getEndOfWordCommand(line, pos);
+
+ string cmd = line.substr(pos, endOfWordPos - pos);
+ if (cmd.empty()) {
+ // This was a bare backslash, just ignore it.
+ return;
+ }
+
+ addDoxyCommand(m_tokenList, cmd);
+
+ // A flag for whether we want to skip leading spaces after the command
+ bool skipLeadingSpace = true;
+
+ if (cmd == CMD_HTML_ONLY || cmd == CMD_VERBATIM || cmd == CMD_LATEX_1 || cmd == CMD_LATEX_2 || cmd == CMD_LATEX_3 || getBaseCommand(cmd) == CMD_CODE) {
+
+ m_isVerbatimText = true;
+
+ // Skipping leading space is necessary with inline \code command,
+ // and it won't hurt anything for block \code (TODO: are the other
+ // commands also compatible with skip leading space? If so, just
+ // do it every time.)
+ if (getBaseCommand(cmd) == CMD_CODE) skipLeadingSpace = true;
+ else skipLeadingSpace = false;
+ } else if (cmd.substr(0,3) == "end") {
+ // If processing an "end" command such as "endlink", don't skip
+ // the space before the next string
+ skipLeadingSpace = false;
+ }
+
+ if (skipLeadingSpace) {
+ // skip any possible spaces after command, because some commands have parameters,
+ // and spaces between command and parameter must be ignored.
+ if (endOfWordPos != string::npos) {
+ endOfWordPos = line.find_first_not_of(" \t", endOfWordPos);
+ }
+ }
+
+ pos = endOfWordPos;
+}
+
+void DoxygenParser::processHtmlTags(size_t &pos, const std::string &line) {
+ bool isEndHtmlTag = false;
+ pos++;
+ if (line.size() > pos && line[pos] == '/') {
+ isEndHtmlTag = true;
+ pos++;
+ }
+
+ size_t endHtmlPos = line.find_first_of("\t >", pos);
+
+ string cmd = line.substr(pos, endHtmlPos - pos);
+ pos = endHtmlPos;
+
+ // prepend '<' to distinguish HTML tags from doxygen commands
+ if (!cmd.empty() && addDoxyCommand(m_tokenList, '<' + cmd)) {
+ // it is a valid HTML command
+ if (pos == string::npos) {
+ pos = line.size();
+ }
+ if (line[pos] != '>') {
+ // it should be HTML tag with args,
+ // for example <A ...>, <IMG ...>, ...
+ if (isEndHtmlTag) {
+ m_tokenListIt = m_tokenList.end();
+ printListError(WARN_DOXYGEN_HTML_ERROR, "Doxygen HTML error for tag " + cmd + ": Illegal end HTML tag without greater-than ('>') found.");
+ }
+
+ endHtmlPos = line.find(">", pos);
+ if (endHtmlPos == string::npos) {
+ m_tokenListIt = m_tokenList.end();
+ printListError(WARN_DOXYGEN_HTML_ERROR, "Doxygen HTML error for tag " + cmd + ": HTML tag without greater-than ('>') found.");
+ }
+ // add args of HTML command, like link URL, image URL, ...
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, endHtmlPos - pos)));
+ pos = endHtmlPos;
+ } else {
+ if (isEndHtmlTag) {
+ m_tokenList.push_back(Token(PLAINSTRING, END_HTML_TAG_MARK));
+ } else {
+ // it is a simple tag, so push empty string
+ m_tokenList.push_back(Token(PLAINSTRING, ""));
+ }
+ }
+
+ if (pos < line.size()) {
+ pos++; // skip '>'
+ }
+ } else {
+ // the command is not HTML supported by Doxygen, < and > will be
+ // replaced by HTML entities &lt; and &gt; respectively,
+ addDoxyCommand(m_tokenList, "&lt");
+ m_tokenList.push_back(Token(PLAINSTRING, cmd));
+ }
+}
+
+void DoxygenParser::processHtmlEntities(size_t &pos, const std::string &line) {
+ size_t endOfWordPos = line.find_first_not_of("abcdefghijklmnopqrstuvwxyz", pos + 1);
+
+ if (endOfWordPos != string::npos) {
+
+ if (line[endOfWordPos] == ';' && (endOfWordPos - pos) > 1) {
+ // if entity is not recognized by Doxygen (not in the list of
+ // commands) nothing is added (here and in Doxygen).
+ addDoxyCommand(m_tokenList, line.substr(pos, endOfWordPos - pos));
+ endOfWordPos++; // skip ';'
+ } else {
+ // it is not an entity - add entity for ampersand and the rest of string
+ addDoxyCommand(m_tokenList, "&amp");
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos + 1, endOfWordPos - pos - 1)));
+ }
+ }
+ pos = endOfWordPos;
+}
+
+/*
+ * This method processes normal comment, which has to be tokenized.
+ */
+size_t DoxygenParser::processNormalComment(size_t pos, const std::string &line) {
+ switch (line[pos]) {
+ case '\\':
+ case '@':
+ if (processEscapedChars(pos, line)) {
+ break;
+ }
+ // handle word commands \arg, \c, \return, ... and \f[, \f$, ... commands
+ processWordCommands(pos, line);
+ break;
+
+ case ' ': // whitespace
+ case '\t':
+ {
+ // whitespaces are stored as plain strings
+ size_t startOfNextWordPos = line.find_first_not_of(" \t", pos + 1);
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, startOfNextWordPos - pos)));
+ pos = startOfNextWordPos;
+ }
+ break;
+
+ case '<':
+ processHtmlTags(pos, line);
+ break;
+ case '>': // this char is detected here only when it is not part of HTML tag
+ addDoxyCommand(m_tokenList, "&gt");
+ pos++;
+ break;
+ case '&':
+ processHtmlEntities(pos, line);
+ break;
+ case '"':
+ m_isInQuotedString = true;
+ m_tokenList.push_back(Token(PLAINSTRING, "\""));
+ pos++;
+ break;
+ default:
+ m_tokenListIt = m_tokenList.end();
+ printListError(WARN_DOXYGEN_UNKNOWN_CHARACTER, std::string("Unknown special character in Doxygen comment: ") + line[pos] + ".");
+ }
+
+ return pos;
+}
+
+/*
+ * This is the main method, which tokenizes Doxygen comment to words and
+ * doxygen commands.
+ */
+void DoxygenParser::tokenizeDoxygenComment(const std::string &doxygenComment, const std::string &fileName, int fileLine) {
+ m_isVerbatimText = false;
+ m_isInQuotedString = false;
+ m_tokenList.clear();
+ m_fileLineNo = fileLine;
+ m_fileName = fileName;
+
+ StringVector lines = split(doxygenComment, '\n');
+
+ // remove trailing spaces, because they cause additional new line at the end
+ // comment, which is wrong, because these spaces are space preceding
+ // end of comment : ' */'
+ if (!doxygenComment.empty() && doxygenComment[doxygenComment.size() - 1] == ' ') {
+
+ string lastLine = lines[lines.size() - 1];
+
+ if (trim(lastLine).empty()) {
+ lines.pop_back(); // remove trailing empty line
+ }
+ }
+
+ for (StringVectorCIt it = lines.begin(); it != lines.end(); it++) {
+ const string &line = *it;
+ size_t pos = line.find_first_not_of(" \t");
+
+ if (pos == string::npos) {
+ m_tokenList.push_back(Token(END_LINE, "\n"));
+ continue;
+ }
+ // skip sequences of '*', '/', and '!' of any length
+ bool isStartOfCommentLineCharFound = false;
+ while (pos < line.size() && isStartOfDoxyCommentChar(line[pos])) {
+ pos++;
+ isStartOfCommentLineCharFound = true;
+ }
+
+ if (pos == line.size()) {
+ m_tokenList.push_back(Token(END_LINE, "\n"));
+ continue;
+ }
+ // if 'isStartOfCommentLineCharFound' then preserve leading spaces, so
+ // ' * comment' gets translated to ' * comment', not ' * comment'
+ // This is important to keep formatting for comments translated to Python.
+ if (isStartOfCommentLineCharFound && line[pos] == ' ') {
+ pos++; // points to char after ' * '
+ if (pos == line.size()) {
+ m_tokenList.push_back(Token(END_LINE, "\n"));
+ continue;
+ }
+ }
+ // line[pos] may be ' \t' or start of word, it there was no '*', '/' or '!'
+ // at beginning of the line. Make sure it points to start of the first word
+ // in the line.
+ if (isStartOfCommentLineCharFound) {
+ size_t firstWordPos = line.find_first_not_of(" \t", pos);
+ if (firstWordPos == string::npos) {
+ m_tokenList.push_back(Token(END_LINE, "\n"));
+ continue;
+ }
+
+ if (firstWordPos > pos) {
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, firstWordPos - pos)));
+ pos = firstWordPos;
+ }
+ } else {
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(0, pos)));
+ }
+
+ while (pos != string::npos) {
+ // find the end of the word
+ size_t doxyCmdOrHtmlTagPos = line.find_first_of("\\@<>&\" \t", pos);
+ if (doxyCmdOrHtmlTagPos != pos) {
+ // plain text found
+ // if the last char is punctuation, make it a separate word, otherwise
+ // it may be included with word also when not appropriate, for example:
+ // colors are \b red, green, and blue --> colors are <b>red,</b> green, and blue
+ // instead of (comma not bold):
+ // colors are \b red, green, and blue --> colors are <b>red</b>, green, and blue
+ // In Python it looks even worse:
+ // colors are \b red, green, and blue --> colors are 'red,' green, and blue
+ string text = line.substr(pos, doxyCmdOrHtmlTagPos - pos);
+ string punctuations(".,:");
+ size_t textSize = text.size();
+
+ if (!text.empty()
+ && punctuations.find(text[text.size() - 1]) != string::npos &&
+ // but do not break ellipsis (...)
+ !(textSize > 1 && text[textSize - 2] == '.')) {
+ m_tokenList.push_back(Token(PLAINSTRING, text.substr(0, text.size() - 1)));
+ m_tokenList.push_back(Token(PLAINSTRING, text.substr(text.size() - 1)));
+ } else {
+ m_tokenList.push_back(Token(PLAINSTRING, text));
+ }
+ }
+
+ pos = doxyCmdOrHtmlTagPos;
+ if (pos != string::npos) {
+ if (m_isVerbatimText) {
+ pos = processVerbatimText(pos, line);
+
+ } else if (m_isInQuotedString) {
+
+ if (line[pos] == '"') {
+ m_isInQuotedString = false;
+ }
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, 1)));
+ pos++;
+
+ } else {
+ pos = processNormalComment(pos, line);
+ }
+ }
+ }
+ m_tokenList.push_back(Token(END_LINE, "\n")); // add when pos == npos - end of line
+ }
+
+ m_tokenListIt = m_tokenList.begin();
+}
+
+void DoxygenParser::printList() {
+
+ int tokNo = 0;
+ for (TokenListCIt it = m_tokenList.begin(); it != m_tokenList.end(); it++, tokNo++) {
+
+ cout << it->toString() << " ";
+
+ if ((tokNo % TOKENSPERLINE) == 0) {
+ cout << endl;
+ }
+ }
+}
+
+void DoxygenParser::printListError(int warningType, const std::string &message) {
+ int curLine = m_fileLineNo;
+ for (TokenListCIt it = m_tokenList.begin(); it != m_tokenListIt; it++) {
+ if (it->m_tokenType == END_LINE) {
+ curLine++;
+ }
+ }
+
+ Swig_warning(warningType, m_fileName.c_str(), curLine, "%s\n", message.c_str());
+}
diff --git a/contrib/tools/swig/Source/Doxygen/doxyparser.h b/contrib/tools/swig/Source/Doxygen/doxyparser.h
new file mode 100644
index 0000000000..a60446517b
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxyparser.h
@@ -0,0 +1,377 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxyparser.h
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_DOXYPARSER_H
+#define SWIG_DOXYPARSER_H
+#include <string>
+#include <list>
+#include <map>
+#include <vector>
+#include <set>
+
+#include "swig.h"
+
+#include "doxyentity.h"
+
+// Utility function to return the base part of a command that may
+// include options, e.g. param[in] -> param
+std::string getBaseCommand(const std::string &cmd);
+
+
+class DoxygenParser {
+private:
+
+ enum DoxyCommandEnum {
+ NONE = -1,
+ SIMPLECOMMAND,
+ COMMANDWORD,
+ COMMANDLINE,
+ COMMANDPARAGRAPH,
+ COMMANDENDCOMMAND,
+ COMMANDWORDPARAGRAPH,
+ COMMANDWORDLINE,
+ COMMANDWORDOWORDWORD,
+ COMMANDOWORD,
+ COMMANDERRORTHROW,
+ COMMANDUNIQUE,
+ COMMAND_HTML,
+ COMMAND_HTML_ENTITY,
+ COMMAND_ALIAS,
+ COMMAND_IGNORE,
+ END_LINE,
+ PARAGRAPH_END,
+ PLAINSTRING,
+ COMMAND
+ };
+
+
+ /** This class contains parts of Doxygen comment as a token. */
+ class Token {
+ public:
+ DoxyCommandEnum m_tokenType;
+ std::string m_tokenString; /* the data , such as param for @param */
+
+ Token(DoxyCommandEnum tType, std::string tString) : m_tokenType(tType), m_tokenString(tString) {
+ }
+
+ std::string toString() const {
+ switch (m_tokenType) {
+ case END_LINE:
+ return "{END OF LINE}";
+ case PARAGRAPH_END:
+ return "{END OF PARAGRAPH}";
+ case PLAINSTRING:
+ return "{PLAINSTRING :" + m_tokenString + "}";
+ case COMMAND:
+ return "{COMMAND : " + m_tokenString + "}";
+ default:
+ return "";
+ }
+ }
+ };
+
+
+ typedef std::vector<Token> TokenList;
+ typedef TokenList::const_iterator TokenListCIt;
+ typedef TokenList::iterator TokenListIt;
+
+ TokenList m_tokenList;
+ TokenListCIt m_tokenListIt;
+
+ typedef std::map<std::string, DoxyCommandEnum> DoxyCommandsMap;
+ typedef DoxyCommandsMap::iterator DoxyCommandsMapIt;
+
+ /*
+ * Map of Doxygen commands to determine if a string is a
+ * command and how it needs to be parsed
+ */
+ static DoxyCommandsMap doxygenCommands;
+ static std::set<std::string> doxygenSectionIndicators;
+
+ bool m_isVerbatimText; // used to handle \htmlonly and \verbatim commands
+ bool m_isInQuotedString;
+
+ Node *m_node;
+ std::string m_fileName;
+ int m_fileLineNo;
+
+ /*
+ * Return the end command for a command appearing in "ignore" feature or empty
+ * string if this is a simple command and not a block one.
+ */
+ std::string getIgnoreFeatureEndCommand(const std::string &theCommand) const;
+
+ /*
+ * Helper for getting the value of doxygen:ignore feature or its argument.
+ */
+ String *getIgnoreFeature(const std::string &theCommand, const char *argument = NULL) const;
+
+ /*
+ * Whether to print lots of debug info during parsing
+ */
+ bool noisy;
+
+ /*
+ *Changes a std::string to all lower case
+ */
+ std::string stringToLower(const std::string &stringToConvert);
+
+ /*
+ * isSectionIndicator returns a boolean if the command is a section indicator
+ * This is a helper method for finding the end of a paragraph
+ * by Doxygen's terms
+ */
+ bool isSectionIndicator(const std::string &smallString);
+ /*
+ * Determines how a command should be handled (what group it belongs to
+ * for parsing rules
+ */
+ DoxyCommandEnum commandBelongs(const std::string &theCommand);
+
+ /*
+ *prints the parse tree
+ */
+ void printTree(const std::list<DoxygenEntity> &rootList);
+
+ /**
+ * Returns true if the next token is end of line token. This is important
+ * when single word commands like \c are at the end of line.
+ */
+ bool isEndOfLine();
+
+ /**
+ * Skips spaces, tabs, and end of line tokens.
+ */
+ void skipWhitespaceTokens();
+
+ /**
+ * Removes all spaces and tabs from beginning end end of string.
+ */
+ std::string trim(const std::string &text);
+
+ /*
+ * Returns string of the next token if the next token is PLAINSTRING. Returns
+ * empty string otherwise.
+ */
+ std::string getNextToken();
+
+ /*
+ * Returns the next word ON THE CURRENT LINE ONLY
+ * if a new line is encountered, returns a blank std::string.
+ * Updates the iterator if successful.
+ */
+ std::string getNextWord();
+
+ /*
+ * Returns the next word, which is not necessarily on the same line.
+ * Updates the iterator if successful.
+ */
+ std::string getNextWordInComment();
+
+ /*
+ * Returns the location of the end of the line as
+ * an iterator.
+ */
+ TokenListCIt getOneLine(const TokenList &tokList);
+
+ /*
+ * Returns a properly formatted std::string
+ * up til ANY command or end of line is encountered.
+ */
+ std::string getStringTilCommand(const TokenList &tokList);
+
+ /*
+ * Returns a properly formatted std::string
+ * up til the command specified is encountered
+ */
+ //TODO check that this behaves properly for formulas
+ std::string getStringTilEndCommand(const std::string &theCommand, const TokenList &tokList);
+
+ /*
+ * Returns the end of a Paragraph as an iterator-
+ * Paragraph is defined in Doxygen to be a paragraph of text
+ * separated by either a structural command or a blank line
+ */
+ TokenListCIt getEndOfParagraph(const TokenList &tokList);
+
+ /*
+ * Returns the end of a section, defined as the first blank line OR first
+ * encounter of the same command. Example of this behaviour is \arg.
+ * If no end is encountered, returns the last token of the std::list.
+ */
+ TokenListCIt getEndOfSection(const std::string &theCommand, const TokenList &tokList);
+
+ /*
+ * This method is for returning the end of a specific form of doxygen command
+ * that begins with a \command and ends in \endcommand
+ * such as \code and \endcode. The proper usage is
+ * progressTilEndCommand("endcode", tokenList);
+ * If the end is never encountered, it returns the end of the std::list.
+ */
+ TokenListCIt getEndCommand(const std::string &theCommand, const TokenList &tokList);
+ /*
+ * A special method for commands such as \arg that end at the end of a
+ * paragraph OR when another \arg is encountered
+ //TODO getTilAnyCommand
+ TokenListCIt getTilAnyCommand(const std::string &theCommand, const TokenList &tokList);
+ */
+
+ /**
+ * This methods skips end of line token, if it is the next token to be
+ * processed. It is called with comment commands which have args till the
+ * end of line, such as 'addtogroup' or 'addindex'.
+ * It is up to translator to specific language to decide whether
+ * to insert eol or not. For example, if a command is ignored in target
+ * language, new lines may make formatting ugly (Python).
+ */
+ void skipEndOfLine();
+
+ /*
+ * Method for Adding a Simple Command
+ * Format: @command
+ * Plain commands, such as newline etc, they contain no other data
+ * \n \\ \@ \& \$ \# \< \> \% \{ \}
+ */
+ void addSimpleCommand(const std::string &theCommand, DoxygenEntityList &doxyList);
+ /*
+ * CommandWord
+ * Format: @command <word>
+ * Commands with a single WORD after then such as @b
+ * "a", "b", "c", "e", "em", "p", "def", "enum", "example", "package",
+ * "relates", "namespace", "relatesalso","anchor", "dontinclude", "include",
+ * "includelineno"
+ */
+ void addCommandWord(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * CommandLine
+ * Format: @command (line)
+ * Commands with a single LINE after then such as @var
+ * "addindex", "fn", "name", "line", "var", "skipline", "typedef", "skip",
+ * "until", "property"
+ */
+ void addCommandLine(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * CommandParagraph
+ * Format: @command {paragraph}
+ * Commands with a single paragraph after then such as @return
+ * "return", "remarks", "since", "test", "sa", "see", "pre", "post",
+ * "details", "invariant", "deprecated", "date", "note", "warning",
+ * "version", "todo", "bug", "attention", "brief", "arg", "author"
+ */
+ void addCommandParagraph(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * Command EndCommand
+ * Format: @command and ends at @endcommand
+ * Commands that take in a block of text such as @code:
+ * "code", "dot", "msc", "f$", "f[", "f{environment}{", "htmlonly",
+ * "latexonly", "manonly", "verbatim", "xmlonly", "cond", "if", "ifnot",
+ * "link"
+ * Returns 1 if success, 0 if the endcommand is never encountered.
+ */
+ void addCommandEndCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * CommandWordParagraph
+ * Format: @command <word> {paragraph}
+ * Commands such as param
+ * "param", "tparam", "throw", "throws", "retval", "exception"
+ */
+ void addCommandWordParagraph(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * CommandWordLine
+ * Format: @command <word> (line)
+ * Commands such as param
+ * "page", "subsection", "subsubsection", "section", "paragraph", "defgroup"
+ */
+ void addCommandWordLine(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * Command Word Optional Word Optional Word
+ * Format: @command <word> [<header-file>] [<header-name>]
+ * Commands such as class
+ * "category", "class", "protocol", "interface", "struct", "union"
+ */
+ void addCommandWordOWordOWord(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * Command Optional Word
+ * Format: @command [<word>]
+ * Commands such as dir
+ * "dir", "file", "cond"
+ */
+ void addCommandOWord(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ * Commands that should not be encountered (such as PHP only)
+ * goes til the end of line then returns
+ */
+ void addCommandErrorThrow(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ void addCommandHtml(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ void addCommandHtmlEntity(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ *Adds the unique commands- different process for each unique command
+ */
+ void addCommandUnique(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ * Replace the given command with its predefined alias expansion.
+ */
+ void aliasCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ * Simply ignore the given command, possibly with the word following it or
+ * until the matching end command.
+ */
+ void ignoreCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ * The actual "meat" of the doxygen parser. Calls the correct addCommand...()
+ * function.
+ */
+ void addCommand(const std::string &commandString, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ DoxygenEntityList parse(TokenListCIt endParsingIndex, const TokenList &tokList, bool root = false);
+
+ /*
+ * Fill static doxygenCommands and sectionIndicators containers
+ */
+ void fillTables();
+
+ /** Processes comment when \htmlonly and \verbatim commands are encountered. */
+ size_t processVerbatimText(size_t pos, const std::string &line);
+
+ bool processEscapedChars(size_t &pos, const std::string &line);
+ void processWordCommands(size_t &pos, const std::string &line);
+ void processHtmlTags(size_t &pos, const std::string &line);
+ void processHtmlEntities(size_t &pos, const std::string &line);
+
+
+ /** Processes comment outside \htmlonly and \verbatim commands. */
+ size_t processNormalComment(size_t pos, const std::string &line);
+
+ void tokenizeDoxygenComment(const std::string &doxygenComment, const std::string &fileName, int fileLine);
+ void printList();
+ void printListError(int warningType, const std::string &message);
+
+ typedef std::vector<std::string> StringVector;
+ typedef StringVector::const_iterator StringVectorCIt;
+
+ StringVector split(const std::string &text, char separator);
+ bool isStartOfDoxyCommentChar(char c);
+ bool addDoxyCommand(DoxygenParser::TokenList &tokList, const std::string &cmd);
+
+public:
+ DoxygenParser(bool noisy = false);
+ virtual ~DoxygenParser();
+ DoxygenEntityList createTree(Node *node, String *documentation);
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/doxytranslator.cxx b/contrib/tools/swig/Source/Doxygen/doxytranslator.cxx
new file mode 100644
index 0000000000..a638717eca
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxytranslator.cxx
@@ -0,0 +1,69 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxytranslator.cxx
+ *
+ * Module to return documentation for nodes formatted for various documentation
+ * systems.
+ * ----------------------------------------------------------------------------- */
+
+#include "doxytranslator.h"
+
+DoxygenTranslator::DoxygenTranslator(int flags) : m_flags(flags), parser((flags &debug_parser) != 0) {
+}
+
+
+DoxygenTranslator::~DoxygenTranslator() {
+}
+
+
+bool DoxygenTranslator::hasDocumentation(Node *node) {
+ return getDoxygenComment(node) != NULL;
+}
+
+
+String *DoxygenTranslator::getDoxygenComment(Node *node) {
+ return Getattr(node, "doxygen");
+}
+
+/**
+ * Indent all lines in the comment by given indentation string
+ */
+void DoxygenTranslator::extraIndentation(String *comment, const_String_or_char_ptr indentationString) {
+ if (indentationString || Len(indentationString) > 0) {
+ int len = Len(comment);
+ bool trailing_newline = len > 0 && *(Char(comment) + len - 1) == '\n';
+ Insert(comment, 0, indentationString);
+ String *replace = NewStringf("\n%s", indentationString);
+ Replaceall(comment, "\n", replace);
+ if (trailing_newline) {
+ len = Len(comment);
+ Delslice(comment, len - 2, len); // Remove added trailing spaces on last line
+ }
+ Delete(replace);
+ }
+}
+
+String *DoxygenTranslator::getDocumentation(Node *node, const_String_or_char_ptr indentationString) {
+
+ if (!hasDocumentation(node)) {
+ return NewString("");
+ }
+
+ String *documentation = makeDocumentation(node);
+ extraIndentation(documentation, indentationString);
+ return documentation;
+}
+
+
+void DoxygenTranslator::printTree(const DoxygenEntityList &entityList) {
+
+ for (DoxygenEntityListCIt p = entityList.begin(); p != entityList.end(); p++) {
+ p->printEntity(0);
+ }
+}
diff --git a/contrib/tools/swig/Source/Doxygen/doxytranslator.h b/contrib/tools/swig/Source/Doxygen/doxytranslator.h
new file mode 100644
index 0000000000..f0d3b1b060
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxytranslator.h
@@ -0,0 +1,90 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxytranslator.h
+ *
+ * Module to return documentation for nodes formatted for various documentation
+ * systems.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_DOXYTRANSLATOR_H
+#define SWIG_DOXYTRANSLATOR_H
+
+#include "swig.h"
+#include "doxyentity.h"
+#include "doxyparser.h"
+#include <list>
+#include <string>
+
+
+/*
+ * This is a base class for translator classes. It defines the basic interface
+ * for translators, which convert Doxygen comments into alternative formats for
+ * target languages.
+ */
+class DoxygenTranslator {
+public:
+ /*
+ * Bit flags for the translator ctor.
+ *
+ * Derived classes may define additional flags.
+ */
+ enum {
+ // Use DoxygenParser in "noisy" mode.
+ debug_parser = 1,
+
+ // Output results of translating Doxygen comments.
+ debug_translator = 2
+ };
+
+ /*
+ * Constructor
+ */
+ DoxygenTranslator(int flags = 0);
+
+ /*
+ * Virtual destructor.
+ */
+ virtual ~DoxygenTranslator();
+
+ /*
+ * Return the documentation for a given node formatted for the correct
+ * documentation system.
+ */
+ String *getDocumentation(Node *node, const_String_or_char_ptr indentationString);
+
+ /*
+ * Returns true if the specified node has comment attached.
+ */
+ bool hasDocumentation(Node *node);
+
+ /*
+ * Get original comment string in Doxygen-format.
+ */
+ String *getDoxygenComment(Node *node);
+
+protected:
+ // The flags passed to the ctor.
+ const int m_flags;
+
+ DoxygenParser parser;
+
+ /*
+ * Returns the documentation formatted for a target language.
+ */
+ virtual String *makeDocumentation(Node *node) = 0;
+
+ /*
+ * Prints the details of a parsed entity list to stdout (for debugging).
+ */
+ void printTree(const DoxygenEntityList &entityList);
+
+ void extraIndentation(String *comment, const_String_or_char_ptr indentationString);
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/javadoc.cxx b/contrib/tools/swig/Source/Doxygen/javadoc.cxx
new file mode 100644
index 0000000000..a1406b2303
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/javadoc.cxx
@@ -0,0 +1,849 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * javadoc.cxx
+ * ----------------------------------------------------------------------------- */
+
+#include "javadoc.h"
+#include "doxyparser.h"
+#include <iostream>
+#include <vector>
+#include <list>
+#include "swigmod.h"
+#define APPROX_LINE_LENGTH 64 // characters per line allowed
+#define TAB_SIZE 8 // current tab size in spaces
+//TODO {@link} {@linkplain} {@docRoot}, and other useful doxy commands that are not a javadoc tag
+
+// define static tables, they are filled in JavaDocConverter's constructor
+std::map<std::string, std::pair<JavaDocConverter::tagHandler, std::string> > JavaDocConverter::tagHandlers;
+
+using std::string;
+using std::list;
+using std::vector;
+
+void JavaDocConverter::fillStaticTables() {
+ if (tagHandlers.size()) // fill only once
+ return;
+
+ /*
+ * Some translation rules:
+ *
+ * @ and \ must be escaped for both Java and Python to appear on output: \@, \\,
+ * while Doxygen produces output in both cases.
+ * Rule: @ and \ with space on the right should get to output.
+ *
+ * :: remains intact, even in class::method(). But you can use class#method also
+ * in C++ comment and it is properly translated to C++ output (changed by doxygen to ::)
+ * and Java output (remains #).
+ * Rule: SWIG type system can't be used to convert C::m to C#m, because in Java it is C.m
+ * Use string replacement :: --> # in tag see and links.
+ *
+ * HTML tags must be translated - remain in Java, to markdown in Python
+ *
+ * Unknown HTML tags, for example <x> is translated to &lt;x&gt; by doxygen, while
+ * Java src is <x> and therefore invisible on output - browser ignores unknown command.
+ * This is handy in syntax descriptions, for example: more <fileName>.
+ *
+ * Standalone < and > need not be translated, they are rendered properly in
+ * all three outputs.
+ *
+ * ., %, and " need not to be translated
+ *
+ * entities must be translated - remain in Java, something meaningful in Python (&lt, ...)
+ *
+ * - Python
+ * - add comments also to auto-generated methods like equals(), delete() in Java,
+ * and methods for std::vector(), ...
+ * Commenting methods of std types is simple - add comment to std_*.i file.
+ */
+
+ // these commands insert HTML tags
+ tagHandlers["a"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
+ tagHandlers["arg"] = make_pair(&JavaDocConverter::handleTagHtml, "li");
+ tagHandlers["b"] = make_pair(&JavaDocConverter::handleTagHtml, "b");
+ tagHandlers["c"] = make_pair(&JavaDocConverter::handleTagHtml, "code");
+ tagHandlers["cite"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
+ tagHandlers["e"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
+ tagHandlers["em"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
+ tagHandlers["li"] = make_pair(&JavaDocConverter::handleTagHtml, "li");
+ tagHandlers["p"] = make_pair(&JavaDocConverter::handleTagHtml, "code");
+ // these commands insert just a single char, some of them need to be escaped
+ tagHandlers["$"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["@"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["\\"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["<"] = make_pair(&JavaDocConverter::handleTagChar, "&lt;");
+ tagHandlers[">"] = make_pair(&JavaDocConverter::handleTagChar, "&gt;");
+ tagHandlers["&"] = make_pair(&JavaDocConverter::handleTagChar, "&amp;");
+ tagHandlers["#"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["%"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["~"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["\""] = make_pair(&JavaDocConverter::handleTagChar, "&quot;");
+ tagHandlers["."] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["::"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ // these commands are stripped out
+ tagHandlers["attention"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["anchor"] = make_pair(&JavaDocConverter::handleTagAnchor, "");
+ tagHandlers["brief"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["bug"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["date"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["details"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ // this command is inserts text accumulated after cmd htmlonly -
+ // see DoxygenParser - CMD_HTML_ONLY.
+ tagHandlers["htmlonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["invariant"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["latexonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["manonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["partofdescription"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["rtfonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["short"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["xmlonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ // these commands are kept as-is, they are supported by JavaDoc
+ tagHandlers["author"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["authors"] = make_pair(&JavaDocConverter::handleTagSame, "author");
+ tagHandlers["deprecated"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["exception"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["package"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["param"] = make_pair(&JavaDocConverter::handleTagParam, "");
+ tagHandlers["tparam"] = make_pair(&JavaDocConverter::handleTagParam, "");
+ tagHandlers["ref"] = make_pair(&JavaDocConverter::handleTagRef, "");
+ tagHandlers["result"] = make_pair(&JavaDocConverter::handleTagSame, "return");
+ tagHandlers["return"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["returns"] = make_pair(&JavaDocConverter::handleTagSame, "return");
+ //tagHandlers["see"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ //tagHandlers["sa"] = make_pair(&JavaDocConverter::handleTagSame, "see");
+ tagHandlers["since"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["throws"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["throw"] = make_pair(&JavaDocConverter::handleTagSame, "throws");
+ tagHandlers["version"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ // these commands have special handlers
+ tagHandlers["code"] = make_pair(&JavaDocConverter::handleTagExtended, "code");
+ tagHandlers["cond"] = make_pair(&JavaDocConverter::handleTagMessage, "Conditional comment: ");
+ tagHandlers["copyright"] = make_pair(&JavaDocConverter::handleTagMessage, "Copyright: ");
+ tagHandlers["else"] = make_pair(&JavaDocConverter::handleTagIf, "Else: ");
+ tagHandlers["elseif"] = make_pair(&JavaDocConverter::handleTagIf, "Else if: ");
+ tagHandlers["endcond"] = make_pair(&JavaDocConverter::handleTagMessage, "End of conditional comment.");
+ // space in second arg prevents Javadoc to treat '@ example' as command. File name of
+ // example is still informative to user.
+ tagHandlers["example"] = make_pair(&JavaDocConverter::handleTagSame, " example");
+ tagHandlers["if"] = make_pair(&JavaDocConverter::handleTagIf, "If: ");
+ tagHandlers["ifnot"] = make_pair(&JavaDocConverter::handleTagIf, "If not: ");
+ tagHandlers["image"] = make_pair(&JavaDocConverter::handleTagImage, "");
+ tagHandlers["link"] = make_pair(&JavaDocConverter::handleTagLink, "");
+ tagHandlers["see"] = make_pair(&JavaDocConverter::handleTagSee, "");
+ tagHandlers["sa"] = make_pair(&JavaDocConverter::handleTagSee, "");
+ tagHandlers["note"] = make_pair(&JavaDocConverter::handleTagMessage, "Note: ");
+ tagHandlers["overload"] = make_pair(&JavaDocConverter::handleTagMessage,
+ "This is an overloaded member function, provided for"
+ " convenience. It differs from the above function only in what" " argument(s) it accepts.");
+ tagHandlers["par"] = make_pair(&JavaDocConverter::handleTagPar, "");
+ tagHandlers["remark"] = make_pair(&JavaDocConverter::handleTagMessage, "Remarks: ");
+ tagHandlers["remarks"] = make_pair(&JavaDocConverter::handleTagMessage, "Remarks: ");
+ tagHandlers["todo"] = make_pair(&JavaDocConverter::handleTagMessage, "TODO: ");
+ tagHandlers["verbatim"] = make_pair(&JavaDocConverter::handleTagExtended, "literal");
+
+ // \f commands output literal Latex formula, which is still better than nothing.
+ tagHandlers["f$"] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
+ tagHandlers["f["] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
+ tagHandlers["f{"] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
+
+ tagHandlers["warning"] = make_pair(&JavaDocConverter::handleTagMessage, "Warning: ");
+ // this command just prints its contents
+ // (it is internal command of swig's parser, contains plain text)
+ tagHandlers["plainstd::string"] = make_pair(&JavaDocConverter::handlePlainString, "");
+ tagHandlers["plainstd::endl"] = make_pair(&JavaDocConverter::handleNewLine, "");
+ tagHandlers["n"] = make_pair(&JavaDocConverter::handleNewLine, "");
+
+ // HTML tags
+ tagHandlers["<a"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<a");
+ tagHandlers["<b"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<b");
+ tagHandlers["<blockquote"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<blockquote");
+ tagHandlers["<body"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<body");
+ tagHandlers["<br"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<br");
+ tagHandlers["<center"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<center");
+ tagHandlers["<caption"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<caption");
+ tagHandlers["<code"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<code");
+ tagHandlers["<dd"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dd");
+ tagHandlers["<dfn"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dfn");
+ tagHandlers["<div"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<div");
+ tagHandlers["<dl"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dl");
+ tagHandlers["<dt"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dt");
+ tagHandlers["<em"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<em");
+ tagHandlers["<form"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<form");
+ tagHandlers["<hr"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<hr");
+ tagHandlers["<h1"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h1");
+ tagHandlers["<h2"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h2");
+ tagHandlers["<h3"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h3");
+ tagHandlers["<i"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<i");
+ tagHandlers["<input"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<input");
+ tagHandlers["<img"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<img");
+ tagHandlers["<li"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<li");
+ tagHandlers["<meta"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<meta");
+ tagHandlers["<multicol"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<multicol");
+ tagHandlers["<ol"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<ol");
+ tagHandlers["<p"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<p");
+ tagHandlers["<pre"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<pre");
+ tagHandlers["<small"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<small");
+ tagHandlers["<span"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<span");
+ tagHandlers["<strong"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<strong");
+ tagHandlers["<sub"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<sub");
+ tagHandlers["<sup"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<sup");
+ tagHandlers["<table"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<table");
+ tagHandlers["<td"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<td");
+ tagHandlers["<th"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<th");
+ tagHandlers["<tr"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<tr");
+ tagHandlers["<tt"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<tt");
+ tagHandlers["<kbd"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<kbd");
+ tagHandlers["<ul"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<ul");
+ tagHandlers["<var"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<var");
+
+ // HTML entities
+ tagHandlers["&copy"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&copy");
+ tagHandlers["&trade"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&trade");
+ tagHandlers["&reg"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&reg");
+ tagHandlers["&lt"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&lt");
+ tagHandlers["&gt"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&gt");
+ tagHandlers["&amp"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&amp");
+ tagHandlers["&apos"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&apos");
+ tagHandlers["&quot"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&quot");
+ tagHandlers["&lsquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&lsquo");
+ tagHandlers["&rsquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rsquo");
+ tagHandlers["&ldquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ldquo");
+ tagHandlers["&rdquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rdquo");
+ tagHandlers["&ndash"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ndash");
+ tagHandlers["&mdash"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&mdash");
+ tagHandlers["&nbsp"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&nbsp");
+ tagHandlers["&times"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&times");
+ tagHandlers["&minus"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&minus");
+ tagHandlers["&sdot"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&sdot");
+ tagHandlers["&sim"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&sim");
+ tagHandlers["&le"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&le");
+ tagHandlers["&ge"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ge");
+ tagHandlers["&larr"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&larr");
+ tagHandlers["&rarr"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rarr");
+}
+
+JavaDocConverter::JavaDocConverter(int flags) :
+ DoxygenTranslator(flags) {
+ fillStaticTables();
+}
+
+/**
+ * Formats comment lines by inserting '\n *' at to long lines and tabs for
+ * indent. Currently it is disabled, which means original comment format is
+ * preserved. Experience shows, that this is usually better than breaking
+ * lines automatically, especially because original line endings are not removed,
+ * which results in short lines. To be useful, this function should have much
+ * better algorithm.
+ */
+std::string JavaDocConverter::formatCommand(std::string unformattedLine, int indent) {
+ std::string formattedLines;
+ return unformattedLine; // currently disabled
+ std::string::size_type lastPosition = 0;
+ std::string::size_type i = 0;
+ int isFirstLine = 1;
+ while (i != std::string::npos && i < unformattedLine.length()) {
+ lastPosition = i;
+ if (isFirstLine) {
+ i += APPROX_LINE_LENGTH;
+ } else {
+ i += APPROX_LINE_LENGTH - indent * TAB_SIZE;
+ }
+
+ i = unformattedLine.find(" ", i);
+
+ if (i > 0 && i + 1 < unformattedLine.length()) {
+ if (!isFirstLine)
+ for (int j = 0; j < indent; j++) {
+ formattedLines.append("\t");
+ } else {
+ isFirstLine = 0;
+ }
+ formattedLines.append(unformattedLine.substr(lastPosition, i - lastPosition + 1));
+ formattedLines.append("\n *");
+
+ }
+ }
+ if (lastPosition < unformattedLine.length()) {
+ if (!isFirstLine) {
+ for (int j = 0; j < indent; j++) {
+ formattedLines.append("\t");
+ }
+ }
+ formattedLines.append(unformattedLine.substr(lastPosition, unformattedLine.length() - lastPosition));
+ }
+
+ return formattedLines;
+}
+
+/**
+ * Returns true, if the given parameter exists in the current node
+ * (for example param is a name of function parameter). If feature
+ * 'doxygen:nostripparams' is set, then this method always returns
+ * true - parameters are copied to output regardless of presence in
+ * function params list.
+ */
+bool JavaDocConverter::paramExists(std::string param) {
+
+ if (GetFlag(currentNode, "feature:doxygen:nostripparams")) {
+ return true;
+ }
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+
+ for (Parm *p = plist; p;) {
+
+ if (Getattr(p, "name") && Char(Getattr(p, "name")) == param) {
+ return true;
+ }
+ /* doesn't seem to work always: in some cases (especially for 'self' parameters)
+ * tmap:in is present, but tmap:in:next is not and so this code skips all the parameters
+ */
+ //p = Getattr(p, "tmap:in") ? Getattr(p, "tmap:in:next") : nextSibling(p);
+ p = nextSibling(p);
+ }
+
+ Delete(plist);
+
+ return false;
+}
+
+std::string JavaDocConverter::translateSubtree(DoxygenEntity &doxygenEntity) {
+ std::string translatedComment;
+
+ if (doxygenEntity.isLeaf) {
+ return translatedComment;
+ }
+
+ for (DoxygenEntityListIt p = doxygenEntity.entityList.begin(); p != doxygenEntity.entityList.end(); p++) {
+
+ translateEntity(*p, translatedComment);
+ translateSubtree(*p);
+ }
+
+ return translatedComment;
+}
+
+/**
+ * Checks if a handler for the given tag exists, and calls it.
+ */
+void JavaDocConverter::translateEntity(DoxygenEntity &tag, std::string &translatedComment) {
+
+ std::map<std::string, std::pair<tagHandler, std::string> >::iterator it;
+ it = tagHandlers.find(getBaseCommand(tag.typeOfEntity));
+
+ if (it != tagHandlers.end()) {
+ (this->*(it->second.first))(tag, translatedComment, it->second.second);
+ } else {
+ // do NOT print warning, since there are many tags, which are not
+ // translatable - many warnings hide important ones
+ // addError(WARN_DOXYGEN_COMMAND_ERROR, "Unknown doxygen or HTML tag: " + tag.typeOfEntity);
+ }
+}
+
+
+void JavaDocConverter::handleTagAnchor(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ translatedComment += "<a id=\"" + translateSubtree(tag) + "\"></a>";
+}
+
+
+void JavaDocConverter::handleTagHtml(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ if (tag.entityList.size()) { // do not include empty tags
+ std::string tagData = translateSubtree(tag);
+ // wrap the thing, ignoring whitespace
+ size_t wsPos = tagData.find_last_not_of("\n\t ");
+ if (wsPos != std::string::npos)
+ translatedComment += "<" + arg + ">" + tagData.substr(0, wsPos + 1) + "</" + arg + ">" + tagData.substr(wsPos + 1);
+ else
+ translatedComment += "<" + arg + ">" + translateSubtree(tag) + "</" + arg + "> ";
+ }
+}
+
+void JavaDocConverter::handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</ul>
+ translatedComment += "</" + arg.substr(1) + ">";
+ } else {
+ translatedComment += arg + htmlTagArgs + ">";
+ }
+}
+
+void JavaDocConverter::handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, std::string &arg) {
+ // html entities can be preserved for Java
+ translatedComment += arg + ';';
+}
+
+void JavaDocConverter::handleNewLine(DoxygenEntity &, std::string &translatedComment, std::string &) {
+ // <br> tag is added, because otherwise to much text is joined
+ // into same paragraph by javadoc. For example, doxy list:
+ // - item one
+ // - item two
+ // becomes one paragraph with surrounding text without newlines.
+ // This way we get to many empty lines in javadoc output, but this
+ // is still better than joined lines. Possibility for improvements
+ // exists.
+ translatedComment += "<br>\n * ";
+}
+
+void JavaDocConverter::handleTagChar(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ // escape it if we need to, else just print
+ if (arg.size())
+ translatedComment += arg;
+ else
+ translatedComment += tag.typeOfEntity;
+}
+
+// handles tags which are the same in Doxygen and Javadoc.
+void JavaDocConverter::handleTagSame(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ if (arg.size())
+ tag.typeOfEntity = arg;
+ translatedComment += formatCommand(std::string("@" + tag.typeOfEntity + " " + translateSubtree(tag)), 2);
+}
+
+void JavaDocConverter::handleParagraph(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ translatedComment += formatCommand(translateSubtree(tag), 0);
+}
+
+void JavaDocConverter::handlePlainString(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ translatedComment += tag.data;
+ // if (tag.data.size() && tag.data[tag.data.size()-1] != ' ')
+ // translatedComment += " ";
+}
+
+void JavaDocConverter::handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ translatedComment += arg + " ";
+ for (DoxygenEntityListCIt it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
+ translatedComment += it->data;
+ }
+}
+
+void JavaDocConverter::handleTagExtended(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ std::string dummy;
+ translatedComment += "{@" + arg + " ";
+ handleParagraph(tag, translatedComment, dummy);
+ translatedComment += "}";
+}
+
+void JavaDocConverter::handleTagIf(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ std::string dummy;
+ translatedComment += arg;
+ if (tag.entityList.size()) {
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ translatedComment += " {" + translateSubtree(tag) + "}";
+ }
+}
+
+void JavaDocConverter::handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ std::string dummy;
+ translatedComment += formatCommand(arg, 0);
+ handleParagraph(tag, translatedComment, dummy);
+}
+
+void JavaDocConverter::handleTagImage(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ if (tag.entityList.size() < 2)
+ return;
+
+ std::string file;
+ std::string title;
+
+ std::list<DoxygenEntity>::iterator it = tag.entityList.begin();
+ if (it->data != "html")
+ return;
+
+ it++;
+ file = it->data;
+
+ it++;
+ if (it != tag.entityList.end())
+ title = it->data;
+
+ translatedComment += "<img src=";
+ if (file.size() >= 2 && file[0] == '"' && file[file.size() - 1] == '"')
+ translatedComment += file;
+ else
+ translatedComment += "\"" + file + "\"";
+ if (title.size())
+ translatedComment += " alt=" + title;
+
+ // the size indication is supported for Latex only in Doxygen, see manual
+
+ translatedComment += "/>";
+}
+
+void JavaDocConverter::handleTagPar(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+ translatedComment += "<p";
+ if (tag.entityList.size()) {
+ translatedComment += " alt=\"" + tag.entityList.begin()->data + "\"";
+ translatedComment += ">";
+ tag.entityList.pop_front();
+ handleParagraph(tag, translatedComment, dummy);
+ }
+ translatedComment += "</p>";
+}
+
+
+void JavaDocConverter::handleTagParam(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+
+ if (!tag.entityList.size())
+ return;
+ if (!paramExists(tag.entityList.begin()->data))
+ return;
+
+ translatedComment += "@param ";
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ handleParagraph(tag, translatedComment, dummy);
+}
+
+
+void JavaDocConverter::handleTagRef(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+ if (!tag.entityList.size())
+ return;
+
+ // we translate to link, although \page is not supported in Java, but
+ // reader at least knows what to look at. Also for \anchor tag on the same
+ // page this link works.
+ string anchor = tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ string anchorText = anchor;
+ if (!tag.entityList.empty()) {
+ anchorText = tag.entityList.begin()->data;
+ }
+ translatedComment += "<a href=\"#" + anchor + "\">" + anchorText + "</a>";
+}
+
+
+string JavaDocConverter::convertLink(string linkObject) {
+ if (GetFlag(currentNode, "feature:doxygen:nolinktranslate"))
+ return linkObject;
+ // find the params in function in linkObject (if any)
+ size_t lbracePos = linkObject.find('(', 0);
+ size_t rbracePos = linkObject.find(')', 0);
+ if (lbracePos == string::npos || rbracePos == string::npos || lbracePos >= rbracePos)
+ return "";
+
+ string paramsStr = linkObject.substr(lbracePos + 1,
+ rbracePos - lbracePos - 1);
+ // strip the params, to fill them later
+ string additionalObject = linkObject.substr(rbracePos + 1, string::npos);
+ linkObject = linkObject.substr(0, lbracePos);
+
+ // find all the params
+ vector<string> params;
+ size_t lastPos = 0, commaPos = 0;
+ while (true) {
+ commaPos = paramsStr.find(',', lastPos);
+ if (commaPos == string::npos)
+ commaPos = paramsStr.size();
+ string param = paramsStr.substr(lastPos, commaPos - lastPos);
+ // if any param type is empty, we are failed
+ if (!param.size())
+ return "";
+ params.push_back(param);
+ lastPos = commaPos + 1;
+ if (lastPos >= paramsStr.size())
+ break;
+ }
+
+ linkObject += "(";
+ for (size_t i = 0; i < params.size(); i++) {
+ // translate c/c++ type string to swig's type
+ // i e 'int **arr[100][10]' -> 'a(100).a(10).p.p.int'
+ // also converting arrays to pointers
+ string paramStr = params[i];
+ String *swigType = NewString("");
+
+ // handle const qualifier
+ if (paramStr.find("const") != string::npos)
+ SwigType_add_qualifier(swigType, "const");
+
+ // handle pointers, references and arrays
+ for (int j = (int)params[i].size() - 1; j >= 0; j--) {
+ // skip all the [...] blocks, write 'p.' for every of it
+ if (paramStr[j] == ']') {
+ while (j >= 0 && paramStr[j] != '[')
+ j--;
+ // no closing brace
+ if (j < 0)
+ return "";
+ SwigType_add_pointer(swigType);
+ continue;
+ } else if (paramStr[j] == '*')
+ SwigType_add_pointer(swigType);
+ else if (paramStr[j] == '&')
+ SwigType_add_reference(swigType);
+ else if (isalnum(paramStr[j])) {
+ size_t typeNameStart = paramStr.find_last_of(' ', j + 1);
+ if (typeNameStart == string::npos)
+ typeNameStart = 0;
+ else
+ typeNameStart++;
+ Append(swigType, paramStr.substr(typeNameStart, j - typeNameStart + 1).c_str());
+ break;
+ }
+ }
+
+ // make dummy param list, to lookup typemaps for it
+ Parm *dummyParam = NewParm(swigType, "", 0);
+ Swig_typemap_attach_parms("jstype", dummyParam, NULL);
+ Language::instance()->replaceSpecialVariables(0, Getattr(dummyParam, "tmap:jstype"), dummyParam);
+
+ //Swig_print(dummyParam, 1);
+ linkObject += Char(Getattr(dummyParam, "tmap:jstype"));
+ if (i != params.size() - 1)
+ linkObject += ",";
+
+ Delete(dummyParam);
+ Delete(swigType);
+ }
+ linkObject += ")";
+
+ return linkObject + additionalObject;
+}
+
+void JavaDocConverter::handleTagLink(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+ if (!tag.entityList.size())
+ return;
+
+ string linkObject = convertLink(tag.entityList.begin()->data);
+ if (!linkObject.size())
+ linkObject = tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+
+ translatedComment += "{@link ";
+ translatedComment += linkObject + " ";
+ handleParagraph(tag, translatedComment, dummy);
+ translatedComment += "}";
+}
+
+void JavaDocConverter::handleTagSee(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+ if (!tag.entityList.size())
+ return;
+
+ // tag.entity list contains contents of the @see paragraph. It should contain
+ // one link (references) to method with or without parameters. Doxygen supports
+ // arbitrary text and types mixed, but this feature is not supported here.
+ // :: or # may be used as a separator between class name and method name.
+ list<DoxygenEntity>::iterator it;
+ string methodRef;
+ for (it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
+ if (it->typeOfEntity == "plainstd::endl") {
+ // handleNewLine(*it, translatedComment, dummy);
+ continue;
+ }
+ // restore entities which may be used in C++ type declaration
+ if (it->typeOfEntity == "&amp") {
+ methodRef += '&';
+ } else if (it->typeOfEntity == "&lt") {
+ methodRef += '<';
+ } else if (it->typeOfEntity == "&gt") {
+ methodRef += '>';
+ } else {
+ methodRef += it->data;
+ }
+ }
+
+ // replace :: with #, but only if it appears before left brace
+ size_t lbrace = methodRef.find('(');
+ size_t dblColon = methodRef.find("::");
+ if (dblColon < lbrace) {
+ methodRef = methodRef.substr(0, dblColon) + '#' + methodRef.substr(dblColon + 2);
+ }
+
+ translatedComment += "@see ";
+ string linkObject = convertLink(methodRef);
+ if (!linkObject.size()) {
+ linkObject = methodRef;
+ }
+ translatedComment += linkObject;
+}
+
+/* This function moves all line endings at the end of child entities
+ * out of the child entities to the parent.
+ * For example, entity tree:
+
+ -root
+ |-param
+ |-paramText
+ |-endline
+
+ should be turned to
+
+ -root
+ |-param
+ |-paramText
+ |-endline
+ *
+ */
+int JavaDocConverter::shiftEndlinesUpTree(DoxygenEntity &root, int level) {
+ DoxygenEntityListIt it = root.entityList.begin();
+ while (it != root.entityList.end()) {
+ // remove line endings
+ int ret = shiftEndlinesUpTree(*it, level + 1);
+ // insert them after this element
+ it++;
+ for (int i = 0; i < ret; i++) {
+ root.entityList.insert(it, DoxygenEntity("plainstd::endl"));
+ }
+ }
+
+ // continue only if we are not root
+ if (!level) {
+ return 0;
+ }
+
+ int removedCount = 0;
+ while (!root.entityList.empty()
+ && root.entityList.rbegin()->typeOfEntity == "plainstd::endl") {
+ root.entityList.pop_back();
+ removedCount++;
+ }
+ return removedCount;
+}
+
+/**
+ * This makes sure that all comment lines contain '*'. It is not mandatory in doxygen,
+ * but highly recommended for Javadoc. '*' in empty lines are indented according
+ * to indentation of the first line. Indentation of non-empty lines is not
+ * changed - garbage in garbage out.
+ */
+std::string JavaDocConverter::indentAndInsertAsterisks(const string &doc) {
+
+ size_t idx = doc.find('\n');
+ size_t indent = 0;
+ bool singleLineComment = idx == string::npos;
+ // Detect indentation.
+ // The first line in comment is the one after '/**', which may be
+ // spaces and '\n' or the text. In any case it is not suitable to detect
+ // indentation, so we have to skip the first '\n'.
+ // However, if there is just one line, then use that line to detect indentation.
+ if (idx != string::npos) {
+ size_t nonspaceIdx = doc.find_first_not_of(" \t", idx + 1);
+ if (nonspaceIdx != string::npos) {
+ indent = nonspaceIdx - idx;
+ }
+ }
+
+ if (indent == 0) {
+ // we can't indent the first line less than 0
+ indent = 1;
+ }
+ // Create the first line of Javadoc comment.
+ string indentStr(indent - 1, ' ');
+ string translatedStr = indentStr + "/**";
+ if (indent > 1) {
+ // remove the first space, so that '*' will be aligned
+ translatedStr = translatedStr.substr(1);
+ }
+
+ translatedStr += doc;
+
+ // insert '*' before each comment line, if it does not have it
+ idx = translatedStr.find('\n');
+
+ while (idx != string::npos) {
+
+ size_t nonspaceIdx = translatedStr.find_first_not_of(" \t", idx + 1);
+ if (nonspaceIdx != string::npos && translatedStr[nonspaceIdx] != '*') {
+ // line without '*' found - is it empty?
+ if (translatedStr[nonspaceIdx] != '\n') {
+ // add '* ' to each line without it
+ translatedStr = translatedStr.substr(0, nonspaceIdx) + "* " + translatedStr.substr(nonspaceIdx);
+ //printf(translatedStr.c_str());
+ } else {
+ // we found empty line, replace it with indented '*'
+ translatedStr = translatedStr.substr(0, idx + 1) + indentStr + "* " + translatedStr.substr(nonspaceIdx);
+ }
+ }
+ idx = translatedStr.find('\n', nonspaceIdx);
+ }
+
+ // Add the last comment line properly indented
+ size_t nonspaceEndIdx = translatedStr.find_last_not_of(" \t");
+ if (nonspaceEndIdx != string::npos) {
+ if (translatedStr[nonspaceEndIdx] != '\n') {
+ if (!singleLineComment)
+ translatedStr += '\n';
+ } else {
+ // remove trailing spaces
+ translatedStr = translatedStr.substr(0, nonspaceEndIdx + 1);
+ }
+ }
+ translatedStr += indentStr + "*/\n";
+
+ return translatedStr;
+}
+
+String *JavaDocConverter::makeDocumentation(Node *node) {
+
+ String *documentation = getDoxygenComment(node);
+
+ if (documentation == NULL) {
+ return NewString("");
+ }
+
+ if (GetFlag(node, "feature:doxygen:notranslate")) {
+
+ string doc = Char(documentation);
+
+ string translatedStr = indentAndInsertAsterisks(doc);
+
+ return NewString(translatedStr.c_str());
+ }
+
+ DoxygenEntityList entityList = parser.createTree(node, documentation);
+
+ // entityList.sort(CompareDoxygenEntities()); sorting currently not used,
+
+ if (m_flags & debug_translator) {
+ std::cout << "---RESORTED LIST---" << std::endl;
+ printTree(entityList);
+ }
+ // store the current node
+ // (currently just to handle params)
+ currentNode = node;
+
+ std::string javaDocString = "/**\n * ";
+
+ DoxygenEntity root("root", entityList);
+
+ shiftEndlinesUpTree(root);
+
+ // strip line endings at the beginning
+ while (!root.entityList.empty()
+ && root.entityList.begin()->typeOfEntity == "plainstd::endl") {
+ root.entityList.pop_front();
+ }
+
+ // and at the end
+ while (!root.entityList.empty()
+ && root.entityList.rbegin()->typeOfEntity == "plainstd::endl") {
+ root.entityList.pop_back();
+ }
+
+ javaDocString += translateSubtree(root);
+
+ javaDocString += "\n */\n";
+
+ if (m_flags & debug_translator) {
+ std::cout << "\n---RESULT IN JAVADOC---" << std::endl;
+ std::cout << javaDocString;
+ }
+
+ return NewString(javaDocString.c_str());
+}
+
+void JavaDocConverter::addError(int warningType, const std::string &message) {
+ Swig_warning(warningType, "", 0, "Doxygen parser warning: %s. \n", message.c_str());
+}
diff --git a/contrib/tools/swig/Source/Doxygen/javadoc.h b/contrib/tools/swig/Source/Doxygen/javadoc.h
new file mode 100644
index 0000000000..d2c956c9db
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/javadoc.h
@@ -0,0 +1,169 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * javadoc.h
+ *
+ * Module to return documentation for nodes formatted for JavaDoc
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_JAVADOC_H
+#define SWIG_JAVADOC_H
+
+#include "doxytranslator.h"
+#include <map>
+
+/*
+ * A class to translate doxygen comments into JavaDoc style comments.
+ */
+class JavaDocConverter : public DoxygenTranslator {
+public:
+ JavaDocConverter(int flags = 0);
+ String *makeDocumentation(Node *node);
+
+protected:
+ /*
+ * Used to properly format JavaDoc-style command
+ */
+ std::string formatCommand(std::string unformattedLine, int indent);
+
+ /*
+ * Translate every entity in a tree.
+ */
+ std::string translateSubtree(DoxygenEntity &doxygenEntity);
+
+ /*
+ * Translate one entity with the appropriate handler, according
+ * to the tagHandlers
+ */
+ void translateEntity(DoxygenEntity &tag, std::string &translatedComment);
+
+ /*
+ * Fix all endlines location, etc
+ */
+ int shiftEndlinesUpTree(DoxygenEntity &root, int level = 0);
+
+ /*
+ * Convert params in link-objects and references
+ */
+ std::string convertLink(std::string linkObject);
+
+ /*
+ * Typedef for the function that handles one tag
+ * arg - some string argument to easily pass it through lookup table
+ */
+ typedef void (JavaDocConverter::*tagHandler) (DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /**
+ * Copies verbatim args of the tag to output, used for commands like \f$, ...
+ */
+ void handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /** Creates anchor link. */
+ void handleTagAnchor(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Wrap the command data with the html tag
+ * arg - html tag, with no braces
+ */
+ void handleTagHtml(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /* Handles HTML tags recognized by Doxygen, like <A ...>, <ul>, <table>, ... */
+ void handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /* Handles HTML entities recognized by Doxygen, like &lt;, &copy;, ... */
+ void handleHtmlEntity(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Just prints new line
+ */
+ void handleNewLine(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print the name of tag to the output, used for escape-commands
+ * arg - html-escaped variant, if not provided the command data is used
+ */
+ void handleTagChar(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Do not translate and print as-is
+ * arg - the new tag name, if it needs to be renamed
+ */
+ void handleTagSame(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print only the content and strip original tag
+ */
+ void handleParagraph(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print only data part of code
+ */
+ void handlePlainString(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print extended Javadoc command, like {@code ...} or {@literal ...}
+ * arg - command name
+ */
+ void handleTagExtended(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print the if-elseif-else-endif section
+ */
+ void handleTagIf(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Prints the specified message, than the contents of the tag
+ * arg - message
+ */
+ void handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Insert <img src=... /> tag if the 'format' field is specified as 'html'
+ */
+ void handleTagImage(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Insert <p alt='title'>...</p>
+ */
+ void handleTagPar(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Insert \@param command, if it is really a function param
+ */
+ void handleTagParam(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Writes link for \ref tag.
+ */
+ void handleTagRef(DoxygenEntity &tag, std::string &translatedComment, std::string &);
+
+ /*
+ * Insert {@link...} command, and handle all the <link-object>s correctly
+ * (like converting types of params, etc)
+ */
+ void handleTagLink(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Insert @see command, and handle all the <link-object>s correctly
+ * (like converting types of params, etc)
+ */
+ void handleTagSee(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+private:
+ Node *currentNode;
+ // this contains the handler pointer and one string argument
+ static std::map<std::string, std::pair<tagHandler, std::string> > tagHandlers;
+ void fillStaticTables();
+
+ bool paramExists(std::string param);
+ std::string indentAndInsertAsterisks(const std::string &doc);
+
+ void addError(int warningType, const std::string &message);
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/pydoc.cxx b/contrib/tools/swig/Source/Doxygen/pydoc.cxx
new file mode 100644
index 0000000000..28f6051a72
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/pydoc.cxx
@@ -0,0 +1,993 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * pydoc.cxx
+ *
+ * Module to return documentation for nodes formatted for PyDoc
+ * ----------------------------------------------------------------------------- */
+
+#include "pydoc.h"
+#include "doxyparser.h"
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+
+#include "swigmod.h"
+
+// define static tables, they are filled in PyDocConverter's constructor
+PyDocConverter::TagHandlersMap PyDocConverter::tagHandlers;
+std::map<std::string, std::string> PyDocConverter::sectionTitles;
+
+using std::string;
+
+// Helper class increasing the provided indent string in its ctor and decreasing
+// it in its dtor.
+class IndentGuard {
+public:
+ // One indent level.
+ static const char *Level() {
+ return " ";
+ }
+ // Default ctor doesn't do anything and prevents the dtor from doing anything// too and should only be used when the guard needs to be initialized// conditionally as Init() can then be called after checking some condition.// Otherwise, prefer to use the non default ctor below.
+ IndentGuard() {
+ m_initialized = false;
+ }
+
+ // Ctor takes the output to determine the current indent and to remove the
+ // extra indent added to it in the dtor and the variable containing the indent
+ // to use, which must be used after every new line by the code actually
+ // updating the output.
+ IndentGuard(string &output, string &indent) {
+ Init(output, indent);
+ }
+
+ // Really initializes the object created using the default ctor.
+ void Init(string &output, string &indent) {
+ m_output = &output;
+ m_indent = &indent;
+
+ const string::size_type lastNonSpace = m_output->find_last_not_of(' ');
+ if (lastNonSpace == string::npos) {
+ m_firstLineIndent = m_output->length();
+ } else if ((*m_output)[lastNonSpace] == '\n') {
+ m_firstLineIndent = m_output->length() - (lastNonSpace + 1);
+ } else {
+ m_firstLineIndent = 0;
+ }
+
+ // Notice that the indent doesn't include the first line indent because it's
+ // implicit, i.e. it is present in the input and so is copied into the
+ // output anyhow.
+ *m_indent = Level();
+
+ m_initialized = true;
+ }
+
+ // Get the indent for the first line of the paragraph, which is smaller than
+ // the indent for the subsequent lines.
+ string getFirstLineIndent() const {
+ return string(m_firstLineIndent, ' ');
+ }
+
+ ~IndentGuard() {
+ if (!m_initialized)
+ return;
+
+ m_indent->clear();
+
+ // Get rid of possible remaining extra indent, e.g. if there were any trailing
+ // new lines: we shouldn't add the extra indent level to whatever follows
+ // this paragraph.
+ static const size_t lenIndentLevel = strlen(Level());
+ if (m_output->length() > lenIndentLevel) {
+ const size_t start = m_output->length() - lenIndentLevel;
+ if (m_output->compare(start, string::npos, Level()) == 0)
+ m_output->erase(start);
+ }
+ }
+
+private:
+ string *m_output;
+ string *m_indent;
+ string::size_type m_firstLineIndent;
+ bool m_initialized;
+
+ IndentGuard(const IndentGuard &);
+ IndentGuard &operator=(const IndentGuard &);
+};
+
+// Return the indent of the given multiline string, i.e. the maximal number of
+// spaces present in the beginning of all its non-empty lines.
+static size_t determineIndent(const string &s) {
+ size_t minIndent = static_cast<size_t>(-1);
+
+ for (size_t lineStart = 0; lineStart < s.length();) {
+ const size_t lineEnd = s.find('\n', lineStart);
+ const size_t firstNonSpace = s.find_first_not_of(' ', lineStart);
+
+ // If inequality doesn't hold, it means that this line contains only spaces
+ // (notice that this works whether lineEnd is valid or string::npos), in
+ // which case it doesn't matter when determining the indent.
+ if (firstNonSpace < lineEnd) {
+ // Here we can be sure firstNonSpace != string::npos.
+ const size_t lineIndent = firstNonSpace - lineStart;
+ if (lineIndent < minIndent)
+ minIndent = lineIndent;
+ }
+
+ if (lineEnd == string::npos)
+ break;
+
+ lineStart = lineEnd + 1;
+ }
+
+ return minIndent;
+}
+
+static void trimWhitespace(string &s) {
+ const string::size_type lastNonSpace = s.find_last_not_of(' ');
+ if (lastNonSpace == string::npos)
+ s.clear();
+ else
+ s.erase(lastNonSpace + 1);
+}
+
+// Erase the first character in the string if it is a newline
+static void eraseLeadingNewLine(string &s) {
+ if (!s.empty() && s[0] == '\n')
+ s.erase(s.begin());
+}
+
+// Erase the last character in the string if it is a newline
+static void eraseTrailingNewLine(string &s) {
+ if (!s.empty() && s[s.size() - 1] == '\n')
+ s.erase(s.size() - 1);
+}
+
+// Check the generated docstring line by line and make sure that any
+// code and verbatim blocks have an empty line preceding them, which
+// is necessary for Sphinx. Additionally, this strips any empty lines
+// appearing at the beginning of the docstring.
+static string padCodeAndVerbatimBlocks(const string &docString) {
+ std::string result;
+
+ std::istringstream iss(docString);
+
+ // Initialize to false because there is no previous line yet
+ bool lastLineWasNonBlank = false;
+
+ for (string line; std::getline(iss, line); result += line) {
+ if (!result.empty()) {
+ // Terminate the previous line
+ result += '\n';
+ }
+
+ const size_t pos = line.find_first_not_of(" \t");
+ if (pos == string::npos) {
+ lastLineWasNonBlank = false;
+ } else {
+ if (lastLineWasNonBlank &&
+ (line.compare(pos, 13, ".. code-block") == 0 ||
+ line.compare(pos, 7, ".. math") == 0 ||
+ line.compare(pos, 3, ">>>") == 0)) {
+ // Must separate code or math blocks from the previous line
+ result += '\n';
+ }
+ lastLineWasNonBlank = true;
+ }
+ }
+ return result;
+}
+
+// Helper function to extract the option value from a command,
+// e.g. param[in] -> in
+static std::string getCommandOption(const std::string &command, char openChar, char closeChar) {
+ string option;
+
+ size_t opt_begin, opt_end;
+ opt_begin = command.find(openChar);
+ opt_end = command.find(closeChar);
+ if (opt_begin != string::npos && opt_end != string::npos)
+ option = command.substr(opt_begin+1, opt_end-opt_begin-1);
+
+ return option;
+}
+
+
+/* static */
+PyDocConverter::TagHandlersMap::mapped_type PyDocConverter::make_handler(tagHandler handler) {
+ return make_pair(handler, std::string());
+}
+
+/* static */
+PyDocConverter::TagHandlersMap::mapped_type PyDocConverter::make_handler(tagHandler handler, const char *arg) {
+ return make_pair(handler, arg);
+}
+
+void PyDocConverter::fillStaticTables() {
+ if (tagHandlers.size()) // fill only once
+ return;
+
+ // table of section titles, they are printed only once
+ // for each group of specified doxygen commands
+ sectionTitles["author"] = "Author: ";
+ sectionTitles["authors"] = "Authors: ";
+ sectionTitles["copyright"] = "Copyright: ";
+ sectionTitles["deprecated"] = "Deprecated: ";
+ sectionTitles["example"] = "Example: ";
+ sectionTitles["note"] = "Notes: ";
+ sectionTitles["remark"] = "Remarks: ";
+ sectionTitles["remarks"] = "Remarks: ";
+ sectionTitles["warning"] = "Warning: ";
+// sectionTitles["sa"] = "See also: ";
+// sectionTitles["see"] = "See also: ";
+ sectionTitles["since"] = "Since: ";
+ sectionTitles["todo"] = "TODO: ";
+ sectionTitles["version"] = "Version: ";
+
+ tagHandlers["a"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ tagHandlers["b"] = make_handler(&PyDocConverter::handleTagWrap, "**");
+ // \c command is translated as single quotes around next word
+ tagHandlers["c"] = make_handler(&PyDocConverter::handleTagWrap, "``");
+ tagHandlers["cite"] = make_handler(&PyDocConverter::handleTagWrap, "'");
+ tagHandlers["e"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ // these commands insert just a single char, some of them need to be escaped
+ tagHandlers["$"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["@"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["\\"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["<"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers[">"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["&"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["#"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["%"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["~"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["\""] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["."] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["::"] = make_handler(&PyDocConverter::handleTagChar);
+ // these commands are stripped out, and only their content is printed
+ tagHandlers["attention"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["author"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["authors"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["brief"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["bug"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["code"] = make_handler(&PyDocConverter::handleCode);
+ tagHandlers["copyright"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["date"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["deprecated"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["details"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["em"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ tagHandlers["example"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["exception"] = tagHandlers["throw"] = tagHandlers["throws"] = make_handler(&PyDocConverter::handleTagException);
+ tagHandlers["htmlonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["invariant"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["latexonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["link"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["manonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["note"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["p"] = make_handler(&PyDocConverter::handleTagWrap, "``");
+ tagHandlers["partofdescription"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["rtfonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["remark"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["remarks"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["sa"] = make_handler(&PyDocConverter::handleTagMessage, "See also: ");
+ tagHandlers["see"] = make_handler(&PyDocConverter::handleTagMessage, "See also: ");
+ tagHandlers["since"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["short"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["todo"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["version"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["verbatim"] = make_handler(&PyDocConverter::handleVerbatimBlock);
+ tagHandlers["warning"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["xmlonly"] = make_handler(&PyDocConverter::handleParagraph);
+ // these commands have special handlers
+ tagHandlers["arg"] = make_handler(&PyDocConverter::handleTagMessage, "* ");
+ tagHandlers["cond"] = make_handler(&PyDocConverter::handleTagMessage, "Conditional comment: ");
+ tagHandlers["else"] = make_handler(&PyDocConverter::handleTagIf, "Else: ");
+ tagHandlers["elseif"] = make_handler(&PyDocConverter::handleTagIf, "Else if: ");
+ tagHandlers["endcond"] = make_handler(&PyDocConverter::handleTagMessage, "End of conditional comment.");
+ tagHandlers["if"] = make_handler(&PyDocConverter::handleTagIf, "If: ");
+ tagHandlers["ifnot"] = make_handler(&PyDocConverter::handleTagIf, "If not: ");
+ tagHandlers["image"] = make_handler(&PyDocConverter::handleTagImage);
+ tagHandlers["li"] = make_handler(&PyDocConverter::handleTagMessage, "* ");
+ tagHandlers["overload"] = make_handler(&PyDocConverter::handleTagMessage,
+ "This is an overloaded member function, provided for"
+ " convenience.\nIt differs from the above function only in what" " argument(s) it accepts.");
+ tagHandlers["par"] = make_handler(&PyDocConverter::handleTagPar);
+ tagHandlers["param"] = tagHandlers["tparam"] = make_handler(&PyDocConverter::handleTagParam);
+ tagHandlers["ref"] = make_handler(&PyDocConverter::handleTagRef);
+ tagHandlers["result"] = tagHandlers["return"] = tagHandlers["returns"] = make_handler(&PyDocConverter::handleTagReturn);
+
+ // this command just prints its contents
+ // (it is internal command of swig's parser, contains plain text)
+ tagHandlers["plainstd::string"] = make_handler(&PyDocConverter::handlePlainString);
+ tagHandlers["plainstd::endl"] = make_handler(&PyDocConverter::handleNewLine);
+ tagHandlers["n"] = make_handler(&PyDocConverter::handleNewLine);
+
+ // \f commands output literal Latex formula, which is still better than nothing.
+ tagHandlers["f$"] = tagHandlers["f["] = tagHandlers["f{"] = make_handler(&PyDocConverter::handleMath);
+
+ // HTML tags
+ tagHandlers["<a"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_A);
+ tagHandlers["<b"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+ tagHandlers["<blockquote"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_A, "Quote: ");
+ tagHandlers["<body"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<br"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "\n");
+
+ // there is no formatting for this tag as it was deprecated in HTML 4.01 and
+ // not used in HTML 5
+ tagHandlers["<center"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<caption"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<code"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "``");
+
+ tagHandlers["<dl"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<dd"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+ tagHandlers["<dt"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+
+ tagHandlers["<dfn"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<div"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<em"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+ tagHandlers["<form"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<hr"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "--------------------------------------------------------------------\n");
+ tagHandlers["<h1"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "# ");
+ tagHandlers["<h2"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "## ");
+ tagHandlers["<h3"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "### ");
+ tagHandlers["<i"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "*");
+ tagHandlers["<input"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<img"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "Image:");
+ tagHandlers["<li"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "* ");
+ tagHandlers["<meta"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<multicol"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<ol"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<p"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "\n");
+ tagHandlers["<pre"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<small"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<span"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "'");
+ tagHandlers["<strong"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+
+ // make a space between text and super/sub script.
+ tagHandlers["<sub"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+ tagHandlers["<sup"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+
+ tagHandlers["<table"] = make_handler(&PyDocConverter::handleDoxyHtmlTagNoParam);
+ tagHandlers["<td"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_td);
+ tagHandlers["<th"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_th);
+ tagHandlers["<tr"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_tr);
+ tagHandlers["<tt"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<kbd"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<ul"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<var"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "*");
+
+ // HTML entities
+ tagHandlers["&copy"] = make_handler(&PyDocConverter::handleHtmlEntity, "(C)");
+ tagHandlers["&trade"] = make_handler(&PyDocConverter::handleHtmlEntity, " TM");
+ tagHandlers["&reg"] = make_handler(&PyDocConverter::handleHtmlEntity, "(R)");
+ tagHandlers["&lt"] = make_handler(&PyDocConverter::handleHtmlEntity, "<");
+ tagHandlers["&gt"] = make_handler(&PyDocConverter::handleHtmlEntity, ">");
+ tagHandlers["&amp"] = make_handler(&PyDocConverter::handleHtmlEntity, "&");
+ tagHandlers["&apos"] = make_handler(&PyDocConverter::handleHtmlEntity, "'");
+ tagHandlers["&quot"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&lsquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "`");
+ tagHandlers["&rsquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "'");
+ tagHandlers["&ldquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&rdquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&ndash"] = make_handler(&PyDocConverter::handleHtmlEntity, "-");
+ tagHandlers["&mdash"] = make_handler(&PyDocConverter::handleHtmlEntity, "--");
+ tagHandlers["&nbsp"] = make_handler(&PyDocConverter::handleHtmlEntity, " ");
+ tagHandlers["&times"] = make_handler(&PyDocConverter::handleHtmlEntity, "x");
+ tagHandlers["&minus"] = make_handler(&PyDocConverter::handleHtmlEntity, "-");
+ tagHandlers["&sdot"] = make_handler(&PyDocConverter::handleHtmlEntity, ".");
+ tagHandlers["&sim"] = make_handler(&PyDocConverter::handleHtmlEntity, "~");
+ tagHandlers["&le"] = make_handler(&PyDocConverter::handleHtmlEntity, "<=");
+ tagHandlers["&ge"] = make_handler(&PyDocConverter::handleHtmlEntity, ">=");
+ tagHandlers["&larr"] = make_handler(&PyDocConverter::handleHtmlEntity, "<--");
+ tagHandlers["&rarr"] = make_handler(&PyDocConverter::handleHtmlEntity, "-->");
+}
+
+PyDocConverter::PyDocConverter(int flags):
+DoxygenTranslator(flags), m_tableLineLen(0), m_prevRowIsTH(false) {
+ fillStaticTables();
+}
+
+// Return the type as it should appear in the output documentation.
+static std::string getPyDocType(Node *n, const_String_or_char_ptr lname = "") {
+ std::string type;
+
+ String *s = Swig_typemap_lookup("doctype", n, lname, 0);
+ if (!s) {
+ if (String *t = Getattr(n, "type"))
+ s = SwigType_str(t, "");
+ }
+
+ if (!s)
+ return type;
+
+ if (Language::classLookup(s)) {
+ // In Python C++ namespaces are flattened, so remove all but last component
+ // of the name.
+ String *const last = Swig_scopename_last(s);
+
+ // We are not actually sure whether it's a documented class or not, but
+ // there doesn't seem to be any harm in making it a reference if it isn't,
+ // while there is a lot of benefit in having a hyperlink if it is.
+ type = ":py:class:`";
+ type += Char(last);
+ type += "`";
+
+ Delete(last);
+ } else {
+ type = Char(s);
+ }
+
+ Delete(s);
+
+ return type;
+}
+
+std::string PyDocConverter::getParamType(std::string param) {
+ std::string type;
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+ for (Parm *p = plist; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+ if (pname && Char(pname) == param) {
+ type = getPyDocType(p, pname);
+ break;
+ }
+ }
+ Delete(plist);
+ return type;
+}
+
+std::string PyDocConverter::getParamValue(std::string param) {
+ std::string value;
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+ for (Parm *p = plist; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+ if (pname && Char(pname) == param) {
+ String *pval = Getattr(p, "value");
+ if (pval)
+ value = Char(pval);
+ break;
+ }
+ }
+ Delete(plist);
+ return value;
+}
+
+std::string PyDocConverter::translateSubtree(DoxygenEntity &doxygenEntity) {
+ std::string translatedComment;
+
+ if (doxygenEntity.isLeaf)
+ return translatedComment;
+
+ std::string currentSection;
+ std::list<DoxygenEntity>::iterator p = doxygenEntity.entityList.begin();
+ while (p != doxygenEntity.entityList.end()) {
+ std::map<std::string, std::string>::iterator it;
+ it = sectionTitles.find(p->typeOfEntity);
+ if (it != sectionTitles.end()) {
+ if (it->second != currentSection) {
+ currentSection = it->second;
+ translatedComment += currentSection;
+ }
+ }
+ translateEntity(*p, translatedComment);
+ translateSubtree(*p);
+ p++;
+ }
+
+ return translatedComment;
+}
+
+void PyDocConverter::translateEntity(DoxygenEntity &doxyEntity, std::string &translatedComment) {
+ // check if we have needed handler and call it
+ std::map<std::string, std::pair<tagHandler, std::string> >::iterator it;
+ it = tagHandlers.find(getBaseCommand(doxyEntity.typeOfEntity));
+ if (it != tagHandlers.end())
+ (this->*(it->second.first)) (doxyEntity, translatedComment, it->second.second);
+}
+
+void PyDocConverter::handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += translateSubtree(tag);
+}
+
+void PyDocConverter::handleVerbatimBlock(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ string verb = translateSubtree(tag);
+
+ eraseLeadingNewLine(verb);
+
+ // Remove the last newline to prevent doubling the newline already present after \endverbatim
+ trimWhitespace(verb); // Needed to catch trailing newline below
+ eraseTrailingNewLine(verb);
+ translatedComment += verb;
+}
+
+void PyDocConverter::handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ IndentGuard indent;
+
+ // Only \f$ is translated to inline formulae, \f[ and \f{ are for the block ones.
+ const bool inlineFormula = tag.typeOfEntity == "f$";
+
+ string formulaNL;
+
+ if (inlineFormula) {
+ translatedComment += ":math:`";
+ } else {
+ indent.Init(translatedComment, m_indent);
+
+ trimWhitespace(translatedComment);
+
+ const string formulaIndent = indent.getFirstLineIndent();
+ translatedComment += formulaIndent;
+ translatedComment += ".. math::\n";
+
+ formulaNL = '\n';
+ formulaNL += formulaIndent;
+ formulaNL += m_indent;
+ translatedComment += formulaNL;
+ }
+
+ std::string formula;
+ handleTagVerbatim(tag, formula, arg);
+
+ // It is important to ensure that we have no spaces around the inline math
+ // contents, so strip them.
+ const size_t start = formula.find_first_not_of(" \t\n");
+ const size_t end = formula.find_last_not_of(" \t\n");
+ if (start != std::string::npos) {
+ for (size_t n = start; n <= end; n++) {
+ if (formula[n] == '\n') {
+ // New lines must be suppressed in inline maths and indented in the block ones.
+ if (!inlineFormula)
+ translatedComment += formulaNL;
+ } else {
+ // Just copy everything else.
+ translatedComment += formula[n];
+ }
+ }
+ }
+
+ if (inlineFormula) {
+ translatedComment += "`";
+ }
+}
+
+void PyDocConverter::handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ trimWhitespace(translatedComment);
+
+ // Check for an option given to the code command (e.g. code{.py}),
+ // and try to set the code-block language accordingly.
+ string option = getCommandOption(tag.typeOfEntity, '{', '}');
+ // Set up the language option to the code-block command, which can
+ // be any language supported by pygments:
+ string codeLanguage;
+ if (option == ".py")
+ // Other possibilities here are "default" or "python3". In Sphinx
+ // 2.1.2, basic syntax doesn't render quite the same in these as
+ // with "python", which for basic keywords seems to provide
+ // slightly richer formatting. Another option would be to leave
+ // the language empty, but testing with Sphinx 1.8.5 has produced
+ // an error "1 argument required".
+ codeLanguage = "python";
+ else if (option == ".java")
+ codeLanguage = "java";
+ else if (option == ".c")
+ codeLanguage = "c";
+ else
+ // If there is not a match, or if no option was given, go out on a
+ // limb and assume that the examples in the C or C++ sources use
+ // C++.
+ codeLanguage = "c++";
+
+ std::string code;
+ handleTagVerbatim(tag, code, arg);
+
+ // Try and remove leading newline, which is present for block \code
+ // command:
+ eraseLeadingNewLine(code);
+
+ // Check for python doctest blocks, and treat them specially:
+ bool isDocTestBlock = false;
+ size_t startPos;
+ // ">>>" would normally appear at the beginning, but doxygen comment
+ // style may have space in front, so skip leading whitespace
+ if ((startPos=code.find_first_not_of(" \t")) != string::npos && code.substr(startPos,3) == ">>>")
+ isDocTestBlock = true;
+
+ string codeIndent;
+ if (! isDocTestBlock) {
+ // Use the current indent for the code-block line itself.
+ translatedComment += indent.getFirstLineIndent();
+ translatedComment += ".. code-block:: " + codeLanguage + "\n\n";
+
+ // Specify the level of extra indentation that will be used for
+ // subsequent lines within the code block. Note that the correct
+ // "starting indentation" is already present in the input, so we
+ // only need to add the desired code block indentation.
+ codeIndent = m_indent;
+ }
+
+ translatedComment += codeIndent;
+ for (size_t n = 0; n < code.length(); n++) {
+ if (code[n] == '\n') {
+ // Don't leave trailing white space, this results in PEP8 validation
+ // errors in Python code (which are performed by our own unit tests).
+ trimWhitespace(translatedComment);
+ translatedComment += '\n';
+
+ // Ensure that we indent all the lines by the code indent.
+ translatedComment += codeIndent;
+ } else {
+ // Just copy everything else.
+ translatedComment += code[n];
+ }
+ }
+
+ trimWhitespace(translatedComment);
+
+ // For block commands, the translator adds the newline after
+ // \endcode, so try and compensate by removing the last newline from
+ // the code text:
+ eraseTrailingNewLine(translatedComment);
+}
+
+void PyDocConverter::handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += tag.data;
+}
+
+void PyDocConverter::handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ for (DoxygenEntityListCIt it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
+ translatedComment += it->data;
+ }
+}
+
+void PyDocConverter::handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ handleParagraph(tag, translatedComment);
+}
+
+void PyDocConverter::handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += tag.typeOfEntity;
+}
+
+void PyDocConverter::handleTagIf(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ if (tag.entityList.size()) {
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ translatedComment += " {" + translateSubtree(tag) + "}";
+ }
+}
+
+void PyDocConverter::handleTagPar(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += "Title: ";
+ if (tag.entityList.size())
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ handleParagraph(tag, translatedComment);
+}
+
+void PyDocConverter::handleTagImage(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ if (tag.entityList.size() < 2)
+ return;
+ tag.entityList.pop_front();
+ translatedComment += "Image: ";
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ if (tag.entityList.size())
+ translatedComment += "(" + tag.entityList.begin()->data + ")";
+}
+
+void PyDocConverter::handleTagParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ if (tag.entityList.size() < 2)
+ return;
+
+ IndentGuard indent(translatedComment, m_indent);
+
+ DoxygenEntity paramNameEntity = *tag.entityList.begin();
+ tag.entityList.pop_front();
+
+ const std::string &paramName = paramNameEntity.data;
+
+ const std::string paramType = getParamType(paramName);
+ const std::string paramValue = getParamValue(paramName);
+
+ // Get command option, e.g. "in", "out", or "in,out"
+ string commandOpt = getCommandOption(tag.typeOfEntity, '[', ']');
+ if (commandOpt == "in,out") commandOpt = "in/out";
+
+ // If provided, append the parameter direction to the type
+ // information via a suffix:
+ std::string suffix;
+ if (commandOpt.size() > 0)
+ suffix = ", " + commandOpt;
+
+ // If the parameter has a default value, flag it as optional in the
+ // generated type definition. Particularly helpful when the python
+ // call is generated with *args, **kwargs.
+ if (paramValue.size() > 0)
+ suffix += ", optional";
+
+ if (!paramType.empty()) {
+ translatedComment += ":type " + paramName + ": " + paramType + suffix + "\n";
+ translatedComment += indent.getFirstLineIndent();
+ }
+
+ translatedComment += ":param " + paramName + ":";
+
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ const std::string pytype = getPyDocType(currentNode);
+ if (!pytype.empty()) {
+ translatedComment += ":rtype: ";
+ translatedComment += pytype;
+ translatedComment += "\n";
+ translatedComment += indent.getFirstLineIndent();
+ }
+
+ translatedComment += ":return: ";
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ translatedComment += ":raises: ";
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::handleTagRef(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ if (!tag.entityList.size())
+ return;
+
+ string anchor = tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ string anchorText = anchor;
+ if (!tag.entityList.empty()) {
+ anchorText = tag.entityList.begin()->data;
+ }
+ translatedComment += "'" + anchorText + "'";
+}
+
+
+void PyDocConverter::handleTagWrap(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ if (tag.entityList.size()) { // do not include empty tags
+ std::string tagData = translateSubtree(tag);
+ // wrap the thing, ignoring whitespace
+ size_t wsPos = tagData.find_last_not_of("\n\t ");
+ if (wsPos != std::string::npos && wsPos != tagData.size() - 1)
+ translatedComment += arg + tagData.substr(0, wsPos + 1) + arg + tagData.substr(wsPos + 1);
+ else
+ translatedComment += arg + tagData + arg;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</ul>
+ // translatedComment += "</" + arg.substr(1) + ">";
+ } else {
+ translatedComment += arg + htmlTagArgs;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTagNoParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</ul>
+ } else {
+ translatedComment += arg;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag_A(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, "</a>
+ translatedComment += " (" + m_url + ')';
+ m_url.clear();
+ } else {
+ m_url.clear();
+ size_t pos = htmlTagArgs.find('=');
+ if (pos != string::npos) {
+ m_url = htmlTagArgs.substr(pos + 1);
+ }
+ translatedComment += arg;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag2(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</em>
+ translatedComment += arg;
+ } else {
+ translatedComment += arg;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag_tr(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ size_t nlPos = translatedComment.rfind('\n');
+ if (htmlTagArgs == "/") {
+ // end tag, </tr> appends vertical table line '|'
+ translatedComment += '|';
+ if (nlPos != string::npos) {
+ size_t startOfTableLinePos = translatedComment.find_first_not_of(" \t", nlPos + 1);
+ if (startOfTableLinePos != string::npos) {
+ m_tableLineLen = translatedComment.size() - startOfTableLinePos;
+ }
+ }
+ } else {
+ if (m_prevRowIsTH) {
+ // if previous row contained <th> tag, add horizontal separator
+ // but first get leading spaces, because they'll be needed for the next row
+ size_t numLeadingSpaces = translatedComment.size() - nlPos - 1;
+
+ translatedComment += string(m_tableLineLen, '-') + '\n';
+
+ if (nlPos != string::npos) {
+ translatedComment += string(numLeadingSpaces, ' ');
+ }
+ m_prevRowIsTH = false;
+ }
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag_th(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end tag, </th> is ignored
+ } else {
+ translatedComment += '|';
+ m_prevRowIsTH = true;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag_td(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end tag, </td> is ignored
+ } else {
+ translatedComment += '|';
+ }
+}
+
+void PyDocConverter::handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg) {
+ // html entities
+ translatedComment += arg;
+}
+
+void PyDocConverter::handleNewLine(DoxygenEntity &, std::string &translatedComment, const std::string &) {
+ trimWhitespace(translatedComment);
+
+ translatedComment += "\n";
+ if (!m_indent.empty())
+ translatedComment += m_indent;
+}
+
+String *PyDocConverter::makeDocumentation(Node *n) {
+ String *documentation;
+ std::string pyDocString;
+
+ // store the node, we may need it later
+ currentNode = n;
+
+ // for overloaded functions we must concat documentation for underlying overloads
+ if (Getattr(n, "sym:overloaded")) {
+ // rewind to the first overload
+ while (Getattr(n, "sym:previousSibling"))
+ n = Getattr(n, "sym:previousSibling");
+
+ std::vector<std::string> allDocumentation;
+
+ // minimal indent of any documentation comments, not initialized yet
+ size_t minIndent = static_cast<size_t>(-1);
+
+ // for each real method (not a generated overload) append the documentation
+ string oneDoc;
+ while (n) {
+ documentation = getDoxygenComment(n);
+ if (!Swig_is_generated_overload(n) && documentation) {
+ currentNode = n;
+ if (GetFlag(n, "feature:doxygen:notranslate")) {
+ String *comment = NewString("");
+ Append(comment, documentation);
+ Replaceall(comment, "\n *", "\n");
+ oneDoc = Char(comment);
+ Delete(comment);
+ } else {
+ std::list<DoxygenEntity> entityList = parser.createTree(n, documentation);
+ DoxygenEntity root("root", entityList);
+
+ oneDoc = translateSubtree(root);
+ }
+
+ // find the minimal indent of this documentation comment, we need to
+ // ensure that the entire comment is indented by it to avoid the leading
+ // parts of the other lines being simply discarded later
+ const size_t oneIndent = determineIndent(oneDoc);
+ if (oneIndent < minIndent)
+ minIndent = oneIndent;
+
+ allDocumentation.push_back(oneDoc);
+ }
+ n = Getattr(n, "sym:nextSibling");
+ }
+
+ // construct final documentation string
+ if (allDocumentation.size() > 1) {
+ string indentStr;
+ if (minIndent != static_cast<size_t>(-1))
+ indentStr.assign(minIndent, ' ');
+
+ std::ostringstream concatDocString;
+ for (size_t realOverloadCount = 0; realOverloadCount < allDocumentation.size(); realOverloadCount++) {
+ if (realOverloadCount != 0) {
+ // separate it from the preceding one.
+ concatDocString << "\n" << indentStr << "|\n\n";
+ }
+
+ oneDoc = allDocumentation[realOverloadCount];
+ trimWhitespace(oneDoc);
+ concatDocString << indentStr << "*Overload " << (realOverloadCount + 1) << ":*\n" << oneDoc;
+ }
+ pyDocString = concatDocString.str();
+ } else if (allDocumentation.size() == 1) {
+ pyDocString = *(allDocumentation.begin());
+ }
+ }
+ // for other nodes just process as normal
+ else {
+ documentation = getDoxygenComment(n);
+ if (documentation != NULL) {
+ if (GetFlag(n, "feature:doxygen:notranslate")) {
+ String *comment = NewString("");
+ Append(comment, documentation);
+ Replaceall(comment, "\n *", "\n");
+ pyDocString = Char(comment);
+ Delete(comment);
+ } else {
+ std::list<DoxygenEntity> entityList = parser.createTree(n, documentation);
+ DoxygenEntity root("root", entityList);
+ pyDocString = translateSubtree(root);
+ }
+ }
+ }
+
+ // if we got something log the result
+ if (!pyDocString.empty()) {
+
+ // remove the last '\n' since additional one is added during writing to file
+ eraseTrailingNewLine(pyDocString);
+
+ // ensure that a blank line occurs before code or math blocks
+ pyDocString = padCodeAndVerbatimBlocks(pyDocString);
+
+ if (m_flags & debug_translator) {
+ std::cout << "\n---RESULT IN PYDOC---" << std::endl;
+ std::cout << pyDocString;
+ std::cout << std::endl;
+ }
+ }
+
+ return NewString(pyDocString.c_str());
+}
+
diff --git a/contrib/tools/swig/Source/Doxygen/pydoc.h b/contrib/tools/swig/Source/Doxygen/pydoc.h
new file mode 100644
index 0000000000..880ee3069c
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/pydoc.h
@@ -0,0 +1,208 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * pydoc.h
+ *
+ * Module to return documentation for nodes formatted for PyDoc
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_PYDOC_H
+#define SWIG_PYDOC_H
+
+#include <list>
+#include <string>
+#include "swig.h"
+#include "doxyentity.h"
+#include "doxytranslator.h"
+
+#define DOC_STRING_LENGTH 64 // characters per line allowed
+#define DOC_PARAM_STRING_LENGTH 30 // characters reserved for param name / type
+
+class PyDocConverter : public DoxygenTranslator {
+public:
+ PyDocConverter(int flags = 0);
+ String *makeDocumentation(Node *node);
+
+protected:
+
+ size_t m_tableLineLen;
+ bool m_prevRowIsTH;
+ std::string m_url;
+
+ /*
+ * Translate every entity in a tree, also manages sections
+ * display. Prints title for every group of tags that have
+ * a section title associated with them
+ */
+ std::string translateSubtree(DoxygenEntity &doxygenEntity);
+
+ /*
+ * Translate one entity with the appropriate handler, according
+ * to the tagHandlers
+ */
+ void translateEntity(DoxygenEntity &doxyEntity, std::string &translatedComment);
+
+ /*
+ * Typedef for the function that handles one tag
+ * arg - some string argument to easily pass it through lookup table
+ */
+ typedef void (PyDocConverter::*tagHandler) (DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Wrap the command data with the some string
+ * arg - string to wrap with, like '_' or '*'
+ */
+ void handleTagWrap(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Just prints new line
+ */
+ void handleNewLine(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print the name of tag to the output, used for escape-commands
+ */
+ void handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format the contents of the \exception tag or its synonyms.
+ */
+ void handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print only the content and strip original tag
+ */
+ void handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+ /*
+ * Handle Doxygen verbatim tag
+ */
+ void handleVerbatimBlock(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+ /*
+ * Handle one of the Doxygen formula-related tags.
+ */
+ void handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Handle a code snippet.
+ */
+ void handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print only data part of code
+ */
+ void handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /**
+ * Copies verbatim args of the tag to output, used for commands like \f$, ...
+ */
+ void handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print the if-elseif-else-endif section
+ */
+ void handleTagIf(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Prints the specified message, than the contents of the tag
+ * arg - message
+ */
+ void handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Insert 'Title: ...'
+ */
+ void handleTagPar(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Insert 'Image: ...'
+ */
+ void handleTagImage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format nice param description with type information
+ */
+ void handleTagParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format the contents of the \return tag or its synonyms.
+ */
+ void handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Writes text for \ref tag.
+ */
+ void handleTagRef(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML tags recognized by Doxygen, like <A ...>, <ul>, <table>, ... */
+
+ void handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /** Does not output params of HTML tag, for example in <table border='1'>
+ * 'border=1' is not written to output.
+ */
+ void handleDoxyHtmlTagNoParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /** Translates tag <a href = "url">text</a> to: text ("url"). */
+ void handleDoxyHtmlTag_A(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Handles HTML tags, which are translated to markdown-like syntax, for example
+ * <i>text</i> --> _text_. Appends arg for start HTML tag and end HTML tag.
+ */
+ void handleDoxyHtmlTag2(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <tr> */
+ void handleDoxyHtmlTag_tr(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <th> */
+ void handleDoxyHtmlTag_th(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <td> */
+ void handleDoxyHtmlTag_td(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML entities recognized by Doxygen, like &lt;, &copy;, ... */
+ void handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg);
+
+
+ /*
+ * Simple helper function that calculates correct parameter type
+ * of the node stored in 'currentNode'
+ * If param with specified name is not found, empty string is returned
+ */
+ std::string getParamType(std::string name);
+
+ /*
+ * Simple helper function to retrieve the parameter value
+ */
+ std::string getParamValue(std::string name);
+
+private:
+ // temporary thing, should be refactored somehow
+ Node *currentNode;
+
+ // Extra indent for the current paragraph, must be output after each new line.
+ std::string m_indent;
+
+
+ // this contains the handler pointer and one string argument
+ typedef std::map<std::string, std::pair<tagHandler, std::string> >TagHandlersMap;
+ static TagHandlersMap tagHandlers;
+
+ // this contains the sections titles, like 'Arguments:' or 'Notes:', that are printed only once
+ static std::map<std::string, std::string> sectionTitles;
+
+ // Helper functions for fillStaticTables(): make a new tag handler object.
+ TagHandlersMap::mapped_type make_handler(tagHandler handler);
+ TagHandlersMap::mapped_type make_handler(tagHandler handler, const char *arg);
+
+ void fillStaticTables();
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Include/swigconfig.h b/contrib/tools/swig/Source/Include/swigconfig.h
new file mode 100644
index 0000000000..b6287a10b8
--- /dev/null
+++ b/contrib/tools/swig/Source/Include/swigconfig.h
@@ -0,0 +1,105 @@
+/* Source/Include/swigconfig.h. Generated from swigconfig.h.in by configure. */
+/* Source/Include/swigconfig.h.in. Generated from configure.ac by autoheader. */
+
+/* define if the Boost library is available */
+#define HAVE_BOOST /**/
+
+/* define if the compiler supports basic C++11 syntax */
+/* #undef HAVE_CXX11 */
+
+/* define if the compiler supports basic C++14 syntax */
+/* #undef HAVE_CXX14 */
+
+/* define if the compiler supports basic C++17 syntax */
+/* #undef HAVE_CXX17 */
+
+/* define if the compiler supports basic C++20 syntax */
+#define HAVE_CXX20 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #undef HAVE_INTTYPES_H */
+
+/* Define to 1 if you have the `dl' library (-ldl). */
+#define HAVE_LIBDL 1
+
+/* Define to 1 if you have the `dld' library (-ldld). */
+/* #undef HAVE_LIBDLD */
+
+/* Define if you have PCRE2 library */
+#define HAVE_PCRE 1
+
+/* Define if popen is available */
+#define HAVE_POPEN 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #undef HAVE_STDINT_H */
+
+/* Define to 1 if you have the <stdio.h> header file. */
+/* #undef HAVE_STDIO_H */
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+/* #undef HAVE_STDLIB_H */
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #undef HAVE_STRINGS_H */
+
+/* Define to 1 if you have the <string.h> header file. */
+/* #undef HAVE_STRING_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+/* #undef HAVE_SYS_STAT_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+/* #undef HAVE_SYS_TYPES_H */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #undef HAVE_UNISTD_H */
+
+/* Name of package */
+#define PACKAGE "swig"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "https://www.swig.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "swig"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "swig 4.1.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "swig"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.1.1"
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+/* #undef STDC_HEADERS */
+
+/* Compiler that built SWIG */
+#define SWIG_CXX "g++"
+
+/* Directory for SWIG system-independent libraries */
+#define SWIG_LIB "/var/empty/swig-4.1.1/share/swig/4.1.1"
+
+/* Directory for SWIG system-independent libraries (Unix install on native
+ Windows) */
+#define SWIG_LIB_WIN_UNIX ""
+
+/* Platform that SWIG is built for */
+#define SWIG_PLATFORM "x86_64-pc-linux-gnu"
+
+/* Version number of package */
+#define VERSION "4.1.1"
+
+
+/* Deal with attempt by Microsoft to deprecate C standard runtime functions */
+#if defined(_MSC_VER)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
diff --git a/contrib/tools/swig/Source/Include/swigwarn.h b/contrib/tools/swig/Source/Include/swigwarn.h
new file mode 100644
index 0000000000..f38607f925
--- /dev/null
+++ b/contrib/tools/swig/Source/Include/swigwarn.h
@@ -0,0 +1,327 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigwarn.h
+ *
+ * SWIG warning message numbers
+ * This file serves as the main registry of warning message numbers. Some of these
+ * numbers are used internally in the C/C++ source code of SWIG. However, some
+ * of the numbers are used in SWIG configuration files (swig.swg and others).
+ *
+ * The numbers are roughly organized into a few different classes by functionality.
+ *
+ * Even though symbolic constants are used in the SWIG source, this is
+ * not always the case in SWIG interface files. Do not change the
+ * numbers in this file.
+ *
+ * This file is used as the input for generating Lib/swigwarn.swg.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_SWIGWARN_H
+#define SWIG_SWIGWARN_H
+
+#define WARN_NONE 0
+
+/* -- Deprecated features -- */
+
+#define WARN_DEPRECATED_EXTERN 101
+#define WARN_DEPRECATED_VAL 102
+#define WARN_DEPRECATED_OUT 103
+#define WARN_DEPRECATED_DISABLEDOC 104
+#define WARN_DEPRECATED_ENABLEDOC 105
+#define WARN_DEPRECATED_DOCONLY 106
+#define WARN_DEPRECATED_STYLE 107
+#define WARN_DEPRECATED_LOCALSTYLE 108
+#define WARN_DEPRECATED_TITLE 109
+#define WARN_DEPRECATED_SECTION 110
+#define WARN_DEPRECATED_SUBSECTION 111
+#define WARN_DEPRECATED_SUBSUBSECTION 112
+#define WARN_DEPRECATED_ADDMETHODS 113
+#define WARN_DEPRECATED_READONLY 114
+#define WARN_DEPRECATED_READWRITE 115
+#define WARN_DEPRECATED_EXCEPT 116
+#define WARN_DEPRECATED_NEW 117
+#define WARN_DEPRECATED_EXCEPT_TM 118
+#define WARN_DEPRECATED_IGNORE_TM 119
+#define WARN_DEPRECATED_OPTC 120
+#define WARN_DEPRECATED_NAME 121
+#define WARN_DEPRECATED_NOEXTERN 122
+#define WARN_DEPRECATED_NODEFAULT 123
+/* Unused since 4.1.0: #define WARN_DEPRECATED_TYPEMAP_LANG 124 */
+#define WARN_DEPRECATED_INPUT_FILE 125
+#define WARN_DEPRECATED_NESTED_WORKAROUND 126
+
+/* -- Preprocessor -- */
+
+#define WARN_PP_MISSING_FILE 201
+#define WARN_PP_EVALUATION 202
+#define WARN_PP_INCLUDEALL_IMPORTALL 203
+#define WARN_PP_CPP_WARNING 204
+#define WARN_PP_CPP_ERROR 205
+#define WARN_PP_UNEXPECTED_TOKENS 206
+
+/* -- C/C++ Parser -- */
+
+#define WARN_PARSE_CLASS_KEYWORD 301
+#define WARN_PARSE_REDEFINED 302
+#define WARN_PARSE_EXTEND_UNDEF 303
+#define WARN_PARSE_UNSUPPORTED_VALUE 304
+#define WARN_PARSE_BAD_VALUE 305
+#define WARN_PARSE_PRIVATE 306
+#define WARN_PARSE_BAD_DEFAULT 307
+#define WARN_PARSE_NAMESPACE_ALIAS 308
+#define WARN_PARSE_PRIVATE_INHERIT 309
+#define WARN_PARSE_TEMPLATE_REPEAT 310
+#define WARN_PARSE_TEMPLATE_PARTIAL 311
+#define WARN_PARSE_UNNAMED_NESTED_CLASS 312
+#define WARN_PARSE_UNDEFINED_EXTERN 313
+#define WARN_PARSE_KEYWORD 314
+#define WARN_PARSE_USING_UNDEF 315
+#define WARN_PARSE_MODULE_REPEAT 316
+#define WARN_PARSE_TEMPLATE_SP_UNDEF 317
+#define WARN_PARSE_TEMPLATE_AMBIG 318
+#define WARN_PARSE_NO_ACCESS 319
+#define WARN_PARSE_EXPLICIT_TEMPLATE 320
+#define WARN_PARSE_BUILTIN_NAME 321
+#define WARN_PARSE_REDUNDANT 322
+#define WARN_PARSE_REC_INHERITANCE 323
+#define WARN_PARSE_NESTED_TEMPLATE 324
+#define WARN_PARSE_NAMED_NESTED_CLASS 325
+#define WARN_PARSE_EXTEND_NAME 326
+#define WARN_PARSE_EXTERN_TEMPLATE 327
+
+#define WARN_CPP11_LAMBDA 340
+#define WARN_CPP11_ALIAS_DECLARATION 341 /* redundant now */
+#define WARN_CPP11_ALIAS_TEMPLATE 342 /* redundant now */
+#define WARN_CPP11_VARIADIC_TEMPLATE 343
+
+#define WARN_IGNORE_OPERATOR_NEW 350 /* new */
+#define WARN_IGNORE_OPERATOR_DELETE 351 /* delete */
+#define WARN_IGNORE_OPERATOR_PLUS 352 /* + */
+#define WARN_IGNORE_OPERATOR_MINUS 353 /* - */
+#define WARN_IGNORE_OPERATOR_MUL 354 /* * */
+#define WARN_IGNORE_OPERATOR_DIV 355 /* / */
+#define WARN_IGNORE_OPERATOR_MOD 356 /* % */
+#define WARN_IGNORE_OPERATOR_XOR 357 /* ^ */
+#define WARN_IGNORE_OPERATOR_AND 358 /* & */
+#define WARN_IGNORE_OPERATOR_OR 359 /* | */
+#define WARN_IGNORE_OPERATOR_NOT 360 /* ~ */
+#define WARN_IGNORE_OPERATOR_LNOT 361 /* ! */
+#define WARN_IGNORE_OPERATOR_EQ 362 /* = */
+#define WARN_IGNORE_OPERATOR_LT 363 /* < */
+#define WARN_IGNORE_OPERATOR_GT 364 /* > */
+#define WARN_IGNORE_OPERATOR_PLUSEQ 365 /* += */
+#define WARN_IGNORE_OPERATOR_MINUSEQ 366 /* -= */
+#define WARN_IGNORE_OPERATOR_MULEQ 367 /* *= */
+#define WARN_IGNORE_OPERATOR_DIVEQ 368 /* /= */
+#define WARN_IGNORE_OPERATOR_MODEQ 369 /* %= */
+#define WARN_IGNORE_OPERATOR_XOREQ 370 /* ^= */
+#define WARN_IGNORE_OPERATOR_ANDEQ 371 /* &= */
+#define WARN_IGNORE_OPERATOR_OREQ 372 /* |= */
+#define WARN_IGNORE_OPERATOR_LSHIFT 373 /* << */
+#define WARN_IGNORE_OPERATOR_RSHIFT 374 /* >> */
+#define WARN_IGNORE_OPERATOR_LSHIFTEQ 375 /* <<= */
+#define WARN_IGNORE_OPERATOR_RSHIFTEQ 376 /* >>= */
+#define WARN_IGNORE_OPERATOR_EQUALTO 377 /* == */
+#define WARN_IGNORE_OPERATOR_NOTEQUAL 378 /* != */
+#define WARN_IGNORE_OPERATOR_LTEQUAL 379 /* <= */
+#define WARN_IGNORE_OPERATOR_GTEQUAL 380 /* >= */
+#define WARN_IGNORE_OPERATOR_LAND 381 /* && */
+#define WARN_IGNORE_OPERATOR_LOR 382 /* || */
+#define WARN_IGNORE_OPERATOR_PLUSPLUS 383 /* ++ */
+#define WARN_IGNORE_OPERATOR_MINUSMINUS 384 /* -- */
+#define WARN_IGNORE_OPERATOR_COMMA 385 /* , */
+#define WARN_IGNORE_OPERATOR_ARROWSTAR 386 /* ->* */
+#define WARN_IGNORE_OPERATOR_ARROW 387 /* -> */
+#define WARN_IGNORE_OPERATOR_CALL 388 /* () */
+#define WARN_IGNORE_OPERATOR_INDEX 389 /* [] */
+#define WARN_IGNORE_OPERATOR_UPLUS 390 /* + */
+#define WARN_IGNORE_OPERATOR_UMINUS 391 /* - */
+#define WARN_IGNORE_OPERATOR_UMUL 392 /* * */
+#define WARN_IGNORE_OPERATOR_UAND 393 /* & */
+#define WARN_IGNORE_OPERATOR_NEWARR 394 /* new [] */
+#define WARN_IGNORE_OPERATOR_DELARR 395 /* delete [] */
+#define WARN_IGNORE_OPERATOR_REF 396 /* operator *() */
+#define WARN_IGNORE_OPERATOR_LTEQUALGT 397 /* <=> */
+
+/* please leave 350-399 free for WARN_IGNORE_OPERATOR_* */
+
+/* -- Type system and typemaps -- */
+
+#define WARN_TYPE_UNDEFINED_CLASS 401
+#define WARN_TYPE_INCOMPLETE 402
+#define WARN_TYPE_ABSTRACT 403
+#define WARN_TYPE_REDEFINED 404
+#define WARN_TYPE_RVALUE_REF_QUALIFIER_IGNORED 405
+
+#define WARN_TYPEMAP_SOURCETARGET 450 /* No longer issued */
+#define WARN_TYPEMAP_CHARLEAK 451
+#define WARN_TYPEMAP_SWIGTYPE 452 /* No longer issued */
+#define WARN_TYPEMAP_APPLY_UNDEF 453
+#define WARN_TYPEMAP_SWIGTYPELEAK 454
+#define WARN_TYPEMAP_WCHARLEAK 455
+
+#define WARN_TYPEMAP_IN_UNDEF 460
+#define WARN_TYPEMAP_OUT_UNDEF 461
+#define WARN_TYPEMAP_VARIN_UNDEF 462
+#define WARN_TYPEMAP_VAROUT_UNDEF 463
+#define WARN_TYPEMAP_CONST_UNDEF 464
+#define WARN_TYPEMAP_UNDEF 465
+#define WARN_TYPEMAP_VAR_UNDEF 466
+#define WARN_TYPEMAP_TYPECHECK 467
+#define WARN_TYPEMAP_THROW 468
+#define WARN_TYPEMAP_DIRECTORIN_UNDEF 469
+#define WARN_TYPEMAP_THREAD_UNSAFE 470 /* mostly used in directorout typemaps */
+#define WARN_TYPEMAP_DIRECTOROUT_UNDEF 471
+#define WARN_TYPEMAP_TYPECHECK_UNDEF 472
+#define WARN_TYPEMAP_DIRECTOROUT_PTR 473
+#define WARN_TYPEMAP_OUT_OPTIMAL_IGNORED 474
+#define WARN_TYPEMAP_OUT_OPTIMAL_MULTIPLE 475
+#define WARN_TYPEMAP_INITIALIZER_LIST 476
+#define WARN_TYPEMAP_DIRECTORTHROWS_UNDEF 477
+
+/* -- Fragments -- */
+#define WARN_FRAGMENT_NOT_FOUND 490
+
+/* -- General code generation -- */
+
+#define WARN_LANG_OVERLOAD_DECL 501
+#define WARN_LANG_OVERLOAD_CONSTRUCT 502
+#define WARN_LANG_IDENTIFIER 503
+#define WARN_LANG_RETURN_TYPE 504
+#define WARN_LANG_VARARGS 505
+#define WARN_LANG_VARARGS_KEYWORD 506
+#define WARN_LANG_NATIVE_UNIMPL 507
+#define WARN_LANG_DEREF_SHADOW 508
+#define WARN_LANG_OVERLOAD_SHADOW 509
+#define WARN_LANG_FRIEND_IGNORE 510
+#define WARN_LANG_OVERLOAD_KEYWORD 511
+#define WARN_LANG_OVERLOAD_CONST 512
+#define WARN_LANG_CLASS_UNNAMED 513
+#define WARN_LANG_DIRECTOR_VDESTRUCT 514
+#define WARN_LANG_DISCARD_CONST 515
+#define WARN_LANG_OVERLOAD_IGNORED 516
+#define WARN_LANG_DIRECTOR_ABSTRACT 517
+#define WARN_LANG_PORTABILITY_FILENAME 518
+#define WARN_LANG_TEMPLATE_METHOD_IGNORE 519
+#define WARN_LANG_SMARTPTR_MISSING 520
+#define WARN_LANG_ILLEGAL_DESTRUCTOR 521
+#define WARN_LANG_EXTEND_CONSTRUCTOR 522
+#define WARN_LANG_EXTEND_DESTRUCTOR 523
+#define WARN_LANG_EXPERIMENTAL 524
+#define WARN_LANG_DIRECTOR_FINAL 525
+#define WARN_LANG_USING_NAME_DIFFERENT 526
+
+/* -- Doxygen comments -- */
+
+#define WARN_DOXYGEN_UNKNOWN_COMMAND 560
+#define WARN_DOXYGEN_UNEXPECTED_END_OF_COMMENT 561
+#define WARN_DOXYGEN_COMMAND_EXPECTED 562
+#define WARN_DOXYGEN_HTML_ERROR 563
+#define WARN_DOXYGEN_COMMAND_ERROR 564
+#define WARN_DOXYGEN_UNKNOWN_CHARACTER 565
+#define WARN_DOXYGEN_UNEXPECTED_ITERATOR_VALUE 566
+
+/* -- Reserved (600-699) -- */
+
+/* -- Language module specific warnings (700 - 899) -- */
+
+/* Feel free to claim any number in this space that's not currently being used. Just make sure you
+ add an entry here */
+
+#define WARN_D_TYPEMAP_CTYPE_UNDEF 700
+#define WARN_D_TYPEMAP_IMTYPE_UNDEF 701
+#define WARN_D_TYPEMAP_DTYPE_UNDEF 702
+#define WARN_D_MULTIPLE_INHERITANCE 703
+#define WARN_D_TYPEMAP_CLASSMOD_UNDEF 704
+#define WARN_D_TYPEMAP_DBODY_UNDEF 705
+#define WARN_D_TYPEMAP_DOUT_UNDEF 706
+#define WARN_D_TYPEMAP_DIN_UNDEF 707
+#define WARN_D_TYPEMAP_DDIRECTORIN_UNDEF 708
+#define WARN_D_TYPEMAP_DCONSTRUCTOR_UNDEF 709
+#define WARN_D_EXCODE_MISSING 710
+#define WARN_D_CANTHROW_MISSING 711
+#define WARN_D_NO_DIRECTORCONNECT_ATTR 712
+#define WARN_D_NAME_COLLISION 713
+
+/* please leave 700-719 free for D */
+
+#define WARN_SCILAB_TRUNCATED_NAME 720
+
+/* please leave 720-739 free for Scilab */
+
+#define WARN_PYTHON_INDENT_MISMATCH 740
+
+/* please leave 740-749 free for Python */
+
+#define WARN_R_MISSING_RTYPECHECK_TYPEMAP 750
+
+/* please leave 750-759 free for R */
+
+#define WARN_RUBY_WRONG_NAME 801
+#define WARN_RUBY_MULTIPLE_INHERITANCE 802
+
+/* please leave 800-809 free for Ruby */
+
+#define WARN_JAVA_TYPEMAP_JNI_UNDEF 810
+#define WARN_JAVA_TYPEMAP_JTYPE_UNDEF 811
+#define WARN_JAVA_TYPEMAP_JSTYPE_UNDEF 812
+#define WARN_JAVA_MULTIPLE_INHERITANCE 813
+#define WARN_JAVA_TYPEMAP_GETCPTR_UNDEF 814
+#define WARN_JAVA_TYPEMAP_CLASSMOD_UNDEF 815
+#define WARN_JAVA_TYPEMAP_JAVABODY_UNDEF 816
+#define WARN_JAVA_TYPEMAP_JAVAOUT_UNDEF 817
+#define WARN_JAVA_TYPEMAP_JAVAIN_UNDEF 818
+#define WARN_JAVA_TYPEMAP_JAVADIRECTORIN_UNDEF 819
+#define WARN_JAVA_TYPEMAP_JAVADIRECTOROUT_UNDEF 820
+#define WARN_JAVA_TYPEMAP_INTERFACECODE_UNDEF 821
+#define WARN_JAVA_COVARIANT_RET 822
+#define WARN_JAVA_TYPEMAP_JAVACONSTRUCT_UNDEF 823
+#define WARN_JAVA_TYPEMAP_DIRECTORIN_NODESC 824
+#define WARN_JAVA_NO_DIRECTORCONNECT_ATTR 825
+#define WARN_JAVA_NSPACE_WITHOUT_PACKAGE 826
+#define WARN_JAVA_TYPEMAP_INTERFACEMODIFIERS_UNDEF 827
+
+/* please leave 810-829 free for Java */
+
+#define WARN_CSHARP_TYPEMAP_CTYPE_UNDEF 830
+#define WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF 831
+#define WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF 832
+#define WARN_CSHARP_MULTIPLE_INHERITANCE 833
+#define WARN_CSHARP_TYPEMAP_GETCPTR_UNDEF 834
+#define WARN_CSHARP_TYPEMAP_CLASSMOD_UNDEF 835
+#define WARN_CSHARP_TYPEMAP_CSBODY_UNDEF 836
+#define WARN_CSHARP_TYPEMAP_CSOUT_UNDEF 837
+#define WARN_CSHARP_TYPEMAP_CSIN_UNDEF 838
+#define WARN_CSHARP_TYPEMAP_CSDIRECTORIN_UNDEF 839
+#define WARN_CSHARP_TYPEMAP_CSDIRECTOROUT_UNDEF 840
+#define WARN_CSHARP_TYPEMAP_INTERFACECODE_UNDEF 841
+#define WARN_CSHARP_COVARIANT_RET 842
+#define WARN_CSHARP_TYPEMAP_CSCONSTRUCT_UNDEF 843
+#define WARN_CSHARP_EXCODE 844
+#define WARN_CSHARP_CANTHROW 845
+#define WARN_CSHARP_NO_DIRECTORCONNECT_ATTR 846
+#define WARN_CSHARP_TYPEMAP_INTERFACEMODIFIERS_UNDEF 847
+
+/* please leave 830-849 free for C# */
+
+/* 850-860 were used by Modula 3 (removed in SWIG 4.1.0) - avoid reusing for now */
+
+#define WARN_PHP_MULTIPLE_INHERITANCE 870
+#define WARN_PHP_UNKNOWN_PRAGMA 871
+#define WARN_PHP_PUBLIC_BASE 872
+
+/* please leave 870-889 free for PHP */
+
+#define WARN_GO_NAME_CONFLICT 890
+
+/* please leave 890-899 free for Go */
+
+/* -- User defined warnings (900 - 999) -- */
+
+#endif
diff --git a/contrib/tools/swig/Source/Modules/README b/contrib/tools/swig/Source/Modules/README
new file mode 100644
index 0000000000..058779d227
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/README
@@ -0,0 +1,9 @@
+06/25/2002
+
+This directory contains all of the SWIG language modules. Many of these
+modules contain code that dates back to SWIG1.0. The module API has changed
+a lot in the development releases so this is fairly messy. We're working on
+cleaning it up, but you'll have to bear with us until it's done.
+
+-- Dave
+
diff --git a/contrib/tools/swig/Source/Modules/allocate.cxx b/contrib/tools/swig/Source/Modules/allocate.cxx
new file mode 100644
index 0000000000..0e1262f839
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/allocate.cxx
@@ -0,0 +1,971 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * allocate.cxx
+ *
+ * This module tries to figure out which classes and structures support
+ * default constructors and destructors in C++. There are several rules that
+ * define this behavior including pure abstract methods, private sections,
+ * and non-default constructors in base classes. See the ARM or
+ * Doc/Manual/SWIGPlus.html for details.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+static int virtual_elimination_mode = 0; /* set to 0 on default */
+
+/* Set virtual_elimination_mode */
+void Wrapper_virtual_elimination_mode_set(int flag) {
+ virtual_elimination_mode = flag;
+}
+
+/* Helper function to assist with abstract class checking.
+ This is a major hack. Sorry. */
+
+extern "C" {
+ static String *search_decl = 0; /* Declarator being searched */
+ static int check_implemented(Node *n) {
+ String *decl;
+ if (!n)
+ return 0;
+ while (n) {
+ if (Strcmp(nodeType(n), "cdecl") == 0) {
+ decl = Getattr(n, "decl");
+ if (SwigType_isfunction(decl)) {
+ SwigType *decl1 = SwigType_typedef_resolve_all(decl);
+ SwigType *decl2 = SwigType_pop_function(decl1);
+ if (Strcmp(decl2, search_decl) == 0) {
+ if (!GetFlag(n, "abstract")) {
+ Delete(decl1);
+ Delete(decl2);
+ return 1;
+ }
+ }
+ Delete(decl1);
+ Delete(decl2);
+ }
+ }
+ n = Getattr(n, "csym:nextSibling");
+ }
+ return 0;
+ }
+}
+
+class Allocate:public Dispatcher {
+ Node *inclass;
+ int extendmode;
+
+ /* Checks if a function, n, is the same as any in the base class, ie if the method is polymorphic.
+ * Also checks for methods which will be hidden (ie a base has an identical non-virtual method).
+ * Both methods must have public access for a match to occur. */
+ int function_is_defined_in_bases(Node *n, Node *bases) {
+
+ if (!bases)
+ return 0;
+
+ String *this_decl = Getattr(n, "decl");
+ if (!this_decl)
+ return 0;
+
+ String *name = Getattr(n, "name");
+ String *this_type = Getattr(n, "type");
+ String *resolved_decl = SwigType_typedef_resolve_all(this_decl);
+
+ // Search all base classes for methods with same signature
+ for (int i = 0; i < Len(bases); i++) {
+ Node *b = Getitem(bases, i);
+ Node *base = firstChild(b);
+ while (base) {
+ if (Strcmp(nodeType(base), "extend") == 0) {
+ // Loop through all the %extend methods
+ Node *extend = firstChild(base);
+ while (extend) {
+ if (function_is_defined_in_bases_seek(n, b, extend, this_decl, name, this_type, resolved_decl)) {
+ Delete(resolved_decl);
+ return 1;
+ }
+ extend = nextSibling(extend);
+ }
+ } else if (Strcmp(nodeType(base), "using") == 0) {
+ // Loop through all the using declaration methods
+ Node *usingdecl = firstChild(base);
+ while (usingdecl) {
+ if (function_is_defined_in_bases_seek(n, b, usingdecl, this_decl, name, this_type, resolved_decl)) {
+ Delete(resolved_decl);
+ return 1;
+ }
+ usingdecl = nextSibling(usingdecl);
+ }
+ } else {
+ // normal methods
+ if (function_is_defined_in_bases_seek(n, b, base, this_decl, name, this_type, resolved_decl)) {
+ Delete(resolved_decl);
+ return 1;
+ }
+ }
+ base = nextSibling(base);
+ }
+ }
+ Delete(resolved_decl);
+ resolved_decl = 0;
+ for (int j = 0; j < Len(bases); j++) {
+ Node *b = Getitem(bases, j);
+ if (function_is_defined_in_bases(n, Getattr(b, "allbases")))
+ return 1;
+ }
+ return 0;
+ }
+
+ /* Helper function for function_is_defined_in_bases */
+ int function_is_defined_in_bases_seek(Node *n, Node *b, Node *base, String *this_decl, String *name, String *this_type, String *resolved_decl) {
+
+ String *base_decl = Getattr(base, "decl");
+ SwigType *base_type = Getattr(base, "type");
+ if (base_decl && base_type) {
+ if (checkAttribute(base, "name", name) && !GetFlag(b, "feature:ignore") /* whole class is ignored */ ) {
+ if (SwigType_isfunction(resolved_decl) && SwigType_isfunction(base_decl)) {
+ // We have found a method that has the same name as one in a base class
+ bool covariant_returntype = false;
+ bool returntype_match = Strcmp(base_type, this_type) == 0 ? true : false;
+ bool decl_match = Strcmp(base_decl, this_decl) == 0 ? true : false;
+ if (returntype_match && decl_match) {
+ // Exact match - we have found a method with identical signature
+ // No typedef resolution was done, but skipping it speeds things up slightly
+ } else {
+ // Either we have:
+ // 1) matching methods but are one of them uses a different typedef (return type or parameter) to the one in base class' method
+ // 2) matching polymorphic methods with covariant return type
+ // 3) a non-matching method (ie an overloaded method of some sort)
+ // 4) a matching method which is not polymorphic, ie it hides the base class' method
+
+ // Check if fully resolved return types match (including
+ // covariant return types)
+ if (!returntype_match) {
+ String *this_returntype = function_return_type(n);
+ String *base_returntype = function_return_type(base);
+ returntype_match = Strcmp(this_returntype, base_returntype) == 0 ? true : false;
+ if (!returntype_match) {
+ covariant_returntype = SwigType_issubtype(this_returntype, base_returntype) ? true : false;
+ returntype_match = covariant_returntype;
+ }
+ Delete(this_returntype);
+ Delete(base_returntype);
+ }
+ // The return types must match at this point, for the whole method to match
+ if (returntype_match && !decl_match) {
+ // Now need to check the parameter list
+ // First do an inexpensive parameter count
+ ParmList *this_parms = Getattr(n, "parms");
+ ParmList *base_parms = Getattr(base, "parms");
+ if (ParmList_len(this_parms) == ParmList_len(base_parms)) {
+ // Number of parameters are the same, now check that all the parameters match
+ SwigType *base_fn = NewString("");
+ SwigType *this_fn = NewString("");
+ SwigType_add_function(base_fn, base_parms);
+ SwigType_add_function(this_fn, this_parms);
+ base_fn = SwigType_typedef_resolve_all(base_fn);
+ this_fn = SwigType_typedef_resolve_all(this_fn);
+ if (Strcmp(base_fn, this_fn) == 0) {
+ // Finally check that the qualifiers match
+ int base_qualifier = SwigType_isqualifier(resolved_decl);
+ int this_qualifier = SwigType_isqualifier(base_decl);
+ if (base_qualifier == this_qualifier) {
+ decl_match = true;
+ }
+ }
+ Delete(base_fn);
+ Delete(this_fn);
+ }
+ }
+ }
+ //Printf(stderr,"look %s %s %d %d\n",base_decl, this_decl, returntype_match, decl_match);
+
+ if (decl_match && returntype_match) {
+ // Found an identical method in the base class
+ bool this_wrapping_protected_members = is_member_director(n) ? true : false; // This should really check for dirprot rather than just being a director method
+ bool base_wrapping_protected_members = is_member_director(base) ? true : false; // This should really check for dirprot rather than just being a director method
+ bool both_have_public_access = is_public(n) && is_public(base);
+ bool both_have_protected_access = (is_protected(n) && this_wrapping_protected_members) && (is_protected(base) && base_wrapping_protected_members);
+ bool both_have_private_access = is_private(n) && is_private(base);
+ if (checkAttribute(base, "storage", "virtual")) {
+ // Found a polymorphic method.
+ // Mark the polymorphic method, in case the virtual keyword was not used.
+ Setattr(n, "storage", "virtual");
+ if (!GetFlag(b, "feature:interface")) { // interface implementation neither hides nor overrides
+ if (both_have_public_access || both_have_protected_access) {
+ if (!is_non_public_base(inclass, b))
+ Setattr(n, "override", base); // Note C# definition of override, ie access must be the same
+ }
+ else if (!both_have_private_access) {
+ // Different access
+ if (this_wrapping_protected_members || base_wrapping_protected_members)
+ if (!is_non_public_base(inclass, b))
+ Setattr(n, "hides", base); // Note C# definition of hiding, ie hidden if access is different
+ }
+ }
+ // Try and find the most base's covariant return type
+ SwigType *most_base_covariant_type = Getattr(base, "covariant");
+ if (!most_base_covariant_type && covariant_returntype)
+ most_base_covariant_type = function_return_type(base, false);
+
+ if (!most_base_covariant_type) {
+ // Eliminate the derived virtual method.
+ if (virtual_elimination_mode && !is_member_director(n))
+ if (both_have_public_access)
+ if (!is_non_public_base(inclass, b))
+ if (!Swig_symbol_isoverloaded(n)) {
+ // Don't eliminate if an overloaded method as this hides the method
+ // in the scripting languages: the dispatch function will hide the base method if ignored.
+ SetFlag(n, "feature:ignore");
+ }
+ } else {
+ // Some languages need to know about covariant return types
+ Setattr(n, "covariant", most_base_covariant_type);
+ }
+
+ } else {
+ // Found an identical method in the base class, but it is not polymorphic.
+ if (both_have_public_access || both_have_protected_access)
+ if (!is_non_public_base(inclass, b))
+ Setattr(n, "hides", base);
+ }
+ if (both_have_public_access || both_have_protected_access)
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ /* Determines whether the base class, b, is in the list of private
+ * or protected base classes for class n. */
+ bool is_non_public_base(Node *n, Node *b) {
+ bool non_public_base = false;
+ Node *bases = Getattr(n, "privatebases");
+ if (bases) {
+ for (int i = 0; i < Len(bases); i++) {
+ Node *base = Getitem(bases, i);
+ if (base == b)
+ non_public_base = true;
+ }
+ }
+ bases = Getattr(n, "protectedbases");
+ if (bases) {
+ for (int i = 0; i < Len(bases); i++) {
+ Node *base = Getitem(bases, i);
+ if (base == b)
+ non_public_base = true;
+ }
+ }
+ return non_public_base;
+ }
+
+ /* Returns the return type for a function. The node n should be a function.
+ If resolve is true the fully returned type is fully resolved.
+ Caller is responsible for deleting returned string. */
+ String *function_return_type(Node *n, bool resolve = true) {
+ String *decl = Getattr(n, "decl");
+ SwigType *type = Getattr(n, "type");
+ String *ty = NewString(type);
+ SwigType_push(ty, decl);
+ if (SwigType_isqualifier(ty))
+ Delete(SwigType_pop(ty));
+ Delete(SwigType_pop_function(ty));
+ if (resolve) {
+ String *unresolved = ty;
+ ty = SwigType_typedef_resolve_all(unresolved);
+ Delete(unresolved);
+ }
+ return ty;
+ }
+
+ /* Checks if a class member is the same as inherited from the class bases */
+ int class_member_is_defined_in_bases(Node *member, Node *classnode) {
+ Node *bases; /* bases is the closest ancestors of classnode */
+ int defined = 0;
+
+ bases = Getattr(classnode, "allbases");
+ if (!bases)
+ return 0;
+
+ {
+ int old_mode = virtual_elimination_mode;
+ if (is_member_director(classnode, member))
+ virtual_elimination_mode = 0;
+
+ if (function_is_defined_in_bases(member, bases)) {
+ defined = 1;
+ }
+
+ virtual_elimination_mode = old_mode;
+ }
+
+ if (defined)
+ return 1;
+ else
+ return 0;
+ }
+
+ /* Checks to see if a class is abstract through inheritance,
+ and saves the first node that seems to be abstract.
+ */
+ int is_abstract_inherit(Node *n, Node *base = 0, int first = 0) {
+ if (!first && (base == n))
+ return 0;
+ if (!base) {
+ /* Root node */
+ Symtab *stab = Getattr(n, "symtab"); /* Get symbol table for node */
+ Symtab *oldtab = Swig_symbol_setscope(stab);
+ int ret = is_abstract_inherit(n, n, 1);
+ Swig_symbol_setscope(oldtab);
+ return ret;
+ }
+ List *abstracts = Getattr(base, "abstracts");
+ if (abstracts) {
+ int dabstract = 0;
+ int len = Len(abstracts);
+ for (int i = 0; i < len; i++) {
+ Node *nn = Getitem(abstracts, i);
+ String *name = Getattr(nn, "name");
+ if (!name)
+ continue;
+ if (Strchr(name, '~'))
+ continue; /* Don't care about destructors */
+ String *base_decl = Getattr(nn, "decl");
+ if (base_decl)
+ base_decl = SwigType_typedef_resolve_all(base_decl);
+ if (SwigType_isfunction(base_decl))
+ search_decl = SwigType_pop_function(base_decl);
+ Node *dn = Swig_symbol_clookup_local_check(name, 0, check_implemented);
+ Delete(search_decl);
+ Delete(base_decl);
+
+ if (!dn) {
+ List *nabstracts = Getattr(n, "abstracts");
+ if (!nabstracts) {
+ nabstracts = NewList();
+ Setattr(n, "abstracts", nabstracts);
+ Delete(nabstracts);
+ }
+ Append(nabstracts, nn);
+ if (!Getattr(n, "abstracts:firstnode")) {
+ Setattr(n, "abstracts:firstnode", nn);
+ }
+ dabstract = base != n;
+ }
+ }
+ if (dabstract)
+ return 1;
+ }
+ List *bases = Getattr(base, "allbases");
+ if (!bases)
+ return 0;
+ for (int i = 0; i < Len(bases); i++) {
+ if (is_abstract_inherit(n, Getitem(bases, i))) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+
+ /* Grab methods used by smart pointers */
+
+ List *smart_pointer_methods(Node *cls, List *methods, int isconst, String *classname = 0) {
+ if (!methods) {
+ methods = NewList();
+ }
+
+ Node *c = firstChild(cls);
+
+ while (c) {
+ if (Getattr(c, "error") || GetFlag(c, "feature:ignore")) {
+ c = nextSibling(c);
+ continue;
+ }
+ if (!isconst && (Strcmp(nodeType(c), "extend") == 0)) {
+ methods = smart_pointer_methods(c, methods, isconst, Getattr(cls, "name"));
+ } else if (Strcmp(nodeType(c), "cdecl") == 0) {
+ if (!GetFlag(c, "feature:ignore")) {
+ String *storage = Getattr(c, "storage");
+ if (!((Cmp(storage, "typedef") == 0))
+ && !((Cmp(storage, "friend") == 0))) {
+ String *name = Getattr(c, "name");
+ String *symname = Getattr(c, "sym:name");
+ Node *e = Swig_symbol_clookup_local(name, 0);
+ if (e && is_public(e) && !GetFlag(e, "feature:ignore") && (Cmp(symname, Getattr(e, "sym:name")) == 0)) {
+ Swig_warning(WARN_LANG_DEREF_SHADOW, Getfile(e), Getline(e), "Declaration of '%s' shadows declaration accessible via operator->(),\n", name);
+ Swig_warning(WARN_LANG_DEREF_SHADOW, Getfile(c), Getline(c), "previous declaration of '%s'.\n", name);
+ } else {
+ /* Make sure node with same name doesn't already exist */
+ int k;
+ int match = 0;
+ for (k = 0; k < Len(methods); k++) {
+ e = Getitem(methods, k);
+ if (Cmp(symname, Getattr(e, "sym:name")) == 0) {
+ match = 1;
+ break;
+ }
+ if (!Getattr(e, "sym:name") && (Cmp(name, Getattr(e, "name")) == 0)) {
+ match = 1;
+ break;
+ }
+ }
+ if (!match) {
+ Node *cc = c;
+ while (cc) {
+ Node *cp = cc;
+ if (classname) {
+ Setattr(cp, "extendsmartclassname", classname);
+ }
+ Setattr(cp, "allocate:smartpointeraccess", "1");
+ /* If constant, we have to be careful */
+ if (isconst) {
+ SwigType *decl = Getattr(cp, "decl");
+ if (decl) {
+ if (SwigType_isfunction(decl)) { /* If method, we only add if it's a const method */
+ if (SwigType_isconst(decl)) {
+ Append(methods, cp);
+ }
+ } else {
+ Append(methods, cp);
+ }
+ } else {
+ Append(methods, cp);
+ }
+ } else {
+ Append(methods, cp);
+ }
+ cc = Getattr(cc, "sym:nextSibling");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ c = nextSibling(c);
+ }
+ /* Look for methods in base classes */
+ {
+ Node *bases = Getattr(cls, "bases");
+ int k;
+ for (k = 0; k < Len(bases); k++) {
+ smart_pointer_methods(Getitem(bases, k), methods, isconst);
+ }
+ }
+ /* Remove protected/private members */
+ {
+ for (int i = 0; i < Len(methods);) {
+ Node *n = Getitem(methods, i);
+ if (!is_public(n)) {
+ Delitem(methods, i);
+ continue;
+ }
+ i++;
+ }
+ }
+ return methods;
+ }
+
+ void mark_exception_classes(ParmList *p) {
+ while (p) {
+ SwigType *ty = Getattr(p, "type");
+ SwigType *t = SwigType_typedef_resolve_all(ty);
+ if (SwigType_isreference(t) || SwigType_ispointer(t) || SwigType_isarray(t)) {
+ Delete(SwigType_pop(t));
+ }
+ Node *c = Swig_symbol_clookup(t, 0);
+ if (c) {
+ if (!GetFlag(c, "feature:exceptionclass")) {
+ SetFlag(c, "feature:exceptionclass");
+ }
+ }
+ p = nextSibling(p);
+ Delete(t);
+ }
+ }
+
+
+ void process_exceptions(Node *n) {
+ ParmList *catchlist = 0;
+ /*
+ the "catchlist" attribute is used to emit the block
+
+ try {$action;}
+ catch <list of catches>;
+
+ in emit.cxx
+
+ and is either constructed from the "feature:catches" feature
+ or copied from the node "throws" list.
+ */
+ String *scatchlist = Getattr(n, "feature:catches");
+ if (scatchlist) {
+ catchlist = Swig_cparse_parms(scatchlist, n);
+ if (catchlist) {
+ Setattr(n, "catchlist", catchlist);
+ mark_exception_classes(catchlist);
+ Delete(catchlist);
+ }
+ }
+ ParmList *throws = Getattr(n, "throws");
+ if (throws) {
+ /* if there is no explicit catchlist, we catch everything in the throws list */
+ if (!catchlist) {
+ Setattr(n, "catchlist", throws);
+ }
+ mark_exception_classes(throws);
+ }
+ }
+
+public:
+Allocate():
+ inclass(NULL), extendmode(0) {
+ }
+
+ virtual int top(Node *n) {
+ cplus_mode = PUBLIC;
+ inclass = 0;
+ extendmode = 0;
+ emit_children(n);
+ return SWIG_OK;
+ }
+
+ virtual int importDirective(Node *n) {
+ return emit_children(n);
+ }
+ virtual int includeDirective(Node *n) {
+ return emit_children(n);
+ }
+ virtual int externDeclaration(Node *n) {
+ return emit_children(n);
+ }
+ virtual int namespaceDeclaration(Node *n) {
+ return emit_children(n);
+ }
+ virtual int extendDirective(Node *n) {
+ extendmode = 1;
+ emit_children(n);
+ extendmode = 0;
+ return SWIG_OK;
+ }
+
+ virtual int classDeclaration(Node *n) {
+ Symtab *symtab = Swig_symbol_current();
+ Swig_symbol_setscope(Getattr(n, "symtab"));
+ save_value<Node*> oldInclass(inclass);
+ save_value<AccessMode> oldAcessMode(cplus_mode);
+ save_value<int> oldExtendMode(extendmode);
+ if (Getattr(n, "template"))
+ extendmode = 0;
+ if (!CPlusPlus) {
+ /* Always have default constructors/destructors in C */
+ Setattr(n, "allocate:default_constructor", "1");
+ Setattr(n, "allocate:default_destructor", "1");
+ }
+
+ if (Getattr(n, "allocate:visit"))
+ return SWIG_OK;
+ Setattr(n, "allocate:visit", "1");
+
+ /* Always visit base classes first */
+ {
+ List *bases = Getattr(n, "bases");
+ if (bases) {
+ for (int i = 0; i < Len(bases); i++) {
+ Node *b = Getitem(bases, i);
+ classDeclaration(b);
+ }
+ }
+ }
+ inclass = n;
+ String *kind = Getattr(n, "kind");
+ if (Strcmp(kind, "class") == 0) {
+ cplus_mode = PRIVATE;
+ } else {
+ cplus_mode = PUBLIC;
+ }
+
+ emit_children(n);
+
+ /* Check if the class is abstract via inheritance. This might occur if a class didn't have
+ any pure virtual methods of its own, but it didn't implement all of the pure methods in
+ a base class */
+ if (!Getattr(n, "abstracts") && is_abstract_inherit(n)) {
+ if (((Getattr(n, "allocate:public_constructor") || (!GetFlag(n, "feature:nodefault") && !Getattr(n, "allocate:has_constructor"))))) {
+ if (!GetFlag(n, "feature:notabstract")) {
+ Node *na = Getattr(n, "abstracts:firstnode");
+ if (na) {
+ Swig_warning(WARN_TYPE_ABSTRACT, Getfile(n), Getline(n),
+ "Class '%s' might be abstract, " "no constructors generated,\n", SwigType_namestr(Getattr(n, "name")));
+ Swig_warning(WARN_TYPE_ABSTRACT, Getfile(na), Getline(na), "Method %s might not be implemented.\n", Swig_name_decl(na));
+ if (!Getattr(n, "abstracts")) {
+ List *abstracts = NewList();
+ Append(abstracts, na);
+ Setattr(n, "abstracts", abstracts);
+ Delete(abstracts);
+ }
+ }
+ }
+ }
+ }
+
+ if (!Getattr(n, "allocate:has_constructor")) {
+ /* No constructor is defined. We need to check a few things */
+ /* If class is abstract. No default constructor. Sorry */
+ if (Getattr(n, "abstracts")) {
+ Delattr(n, "allocate:default_constructor");
+ }
+ if (!Getattr(n, "allocate:default_constructor")) {
+ /* Check base classes */
+ List *bases = Getattr(n, "allbases");
+ int allows_default = 1;
+
+ for (int i = 0; i < Len(bases); i++) {
+ Node *n = Getitem(bases, i);
+ /* If base class does not allow default constructor, we don't allow it either */
+ if (!Getattr(n, "allocate:default_constructor") && (!Getattr(n, "allocate:default_base_constructor"))) {
+ allows_default = 0;
+ }
+ }
+ if (allows_default) {
+ Setattr(n, "allocate:default_constructor", "1");
+ }
+ }
+ }
+ if (!Getattr(n, "allocate:has_copy_constructor")) {
+ if (Getattr(n, "abstracts")) {
+ Delattr(n, "allocate:copy_constructor");
+ }
+ if (!Getattr(n, "allocate:copy_constructor")) {
+ /* Check base classes */
+ List *bases = Getattr(n, "allbases");
+ int allows_copy = 1;
+
+ for (int i = 0; i < Len(bases); i++) {
+ Node *n = Getitem(bases, i);
+ /* If base class does not allow copy constructor, we don't allow it either */
+ if (!Getattr(n, "allocate:copy_constructor") && (!Getattr(n, "allocate:copy_base_constructor"))) {
+ allows_copy = 0;
+ }
+ }
+ if (allows_copy) {
+ Setattr(n, "allocate:copy_constructor", "1");
+ }
+ }
+ }
+
+ if (!Getattr(n, "allocate:has_destructor")) {
+ /* No destructor was defined */
+ List *bases = Getattr(n, "allbases");
+ int allows_destruct = 1;
+
+ for (int i = 0; i < Len(bases); i++) {
+ Node *n = Getitem(bases, i);
+ /* If base class does not allow default destructor, we don't allow it either */
+ if (!Getattr(n, "allocate:default_destructor") && (!Getattr(n, "allocate:default_base_destructor"))) {
+ allows_destruct = 0;
+ }
+ }
+ if (allows_destruct) {
+ Setattr(n, "allocate:default_destructor", "1");
+ }
+ }
+
+ if (!Getattr(n, "allocate:has_assign")) {
+ /* No assignment operator was defined */
+ List *bases = Getattr(n, "allbases");
+ int allows_assign = 1;
+
+ for (int i = 0; i < Len(bases); i++) {
+ Node *n = Getitem(bases, i);
+ /* If base class does not allow assignment, we don't allow it either */
+ if (Getattr(n, "allocate:has_assign")) {
+ allows_assign = !Getattr(n, "allocate:noassign");
+ }
+ }
+ if (!allows_assign) {
+ Setattr(n, "allocate:noassign", "1");
+ }
+ }
+
+ if (!Getattr(n, "allocate:has_new")) {
+ /* No new operator was defined */
+ List *bases = Getattr(n, "allbases");
+ int allows_new = 1;
+
+ for (int i = 0; i < Len(bases); i++) {
+ Node *n = Getitem(bases, i);
+ /* If base class does not allow new operator, we don't allow it either */
+ if (Getattr(n, "allocate:has_new")) {
+ allows_new = !Getattr(n, "allocate:nonew");
+ }
+ }
+ if (!allows_new) {
+ Setattr(n, "allocate:nonew", "1");
+ }
+ }
+
+ /* Check if base classes allow smart pointers, but might be hidden */
+ if (!Getattr(n, "allocate:smartpointer")) {
+ Node *sp = Swig_symbol_clookup("operator ->", 0);
+ if (sp) {
+ /* Look for parent */
+ Node *p = parentNode(sp);
+ if (Strcmp(nodeType(p), "extend") == 0) {
+ p = parentNode(p);
+ }
+ if (Strcmp(nodeType(p), "class") == 0) {
+ if (GetFlag(p, "feature:ignore")) {
+ Setattr(n, "allocate:smartpointer", Getattr(p, "allocate:smartpointer"));
+ }
+ }
+ }
+ }
+
+ Swig_interface_propagate_methods(n);
+
+ /* Only care about default behavior. Remove temporary values */
+ Setattr(n, "allocate:visit", "1");
+ Swig_symbol_setscope(symtab);
+ return SWIG_OK;
+ }
+
+ virtual int accessDeclaration(Node *n) {
+ String *kind = Getattr(n, "kind");
+ if (Cmp(kind, "public") == 0) {
+ cplus_mode = PUBLIC;
+ } else if (Cmp(kind, "private") == 0) {
+ cplus_mode = PRIVATE;
+ } else if (Cmp(kind, "protected") == 0) {
+ cplus_mode = PROTECTED;
+ }
+ return SWIG_OK;
+ }
+
+ virtual int usingDeclaration(Node *n) {
+
+ Node *c = 0;
+ for (c = firstChild(n); c; c = nextSibling(c)) {
+ if (Strcmp(nodeType(c), "cdecl") == 0) {
+ process_exceptions(c);
+
+ if (inclass)
+ class_member_is_defined_in_bases(c, inclass);
+ }
+ }
+
+ return SWIG_OK;
+ }
+
+ virtual int cDeclaration(Node *n) {
+
+ process_exceptions(n);
+
+ if (inclass) {
+ /* check whether the member node n is defined in class node in class's bases */
+ class_member_is_defined_in_bases(n, inclass);
+
+ /* Check to see if this is a static member or not. If so, we add an attribute
+ cplus:staticbase that saves the current class */
+
+ if (Swig_storage_isstatic(n)) {
+ Setattr(n, "cplus:staticbase", inclass);
+ } else if (Cmp(Getattr(n, "kind"), "variable") == 0) {
+ /* Check member variable to determine whether assignment is valid */
+ if (SwigType_isreference(Getattr(n, "type"))) {
+ /* Can't assign a class with reference member data */
+ Setattr(inclass, "allocate:noassign", "1");
+ }
+ }
+
+ String *name = Getattr(n, "name");
+ if (cplus_mode != PUBLIC) {
+ if (Strcmp(name, "operator =") == 0) {
+ /* Look for a private assignment operator */
+ if (!GetFlag(n, "deleted"))
+ Setattr(inclass, "allocate:has_assign", "1");
+ Setattr(inclass, "allocate:noassign", "1");
+ } else if (Strcmp(name, "operator new") == 0) {
+ /* Look for a private new operator */
+ if (!GetFlag(n, "deleted"))
+ Setattr(inclass, "allocate:has_new", "1");
+ Setattr(inclass, "allocate:nonew", "1");
+ }
+ } else {
+ if (Strcmp(name, "operator =") == 0) {
+ if (!GetFlag(n, "deleted"))
+ Setattr(inclass, "allocate:has_assign", "1");
+ else
+ Setattr(inclass, "allocate:noassign", "1");
+ } else if (Strcmp(name, "operator new") == 0) {
+ if (!GetFlag(n, "deleted"))
+ Setattr(inclass, "allocate:has_new", "1");
+ else
+ Setattr(inclass, "allocate:nonew", "1");
+ }
+ /* Look for smart pointer operator */
+ if ((Strcmp(name, "operator ->") == 0) && (!GetFlag(n, "feature:ignore"))) {
+ /* Look for version with no parameters */
+ Node *sn = n;
+ while (sn) {
+ if (!Getattr(sn, "parms")) {
+ SwigType *type = SwigType_typedef_resolve_all(Getattr(sn, "type"));
+ SwigType_push(type, Getattr(sn, "decl"));
+ Delete(SwigType_pop_function(type));
+ SwigType *base = SwigType_base(type);
+ Node *sc = Swig_symbol_clookup(base, 0);
+ if ((sc) && (Strcmp(nodeType(sc), "class") == 0)) {
+ if (SwigType_check_decl(type, "p.")) {
+ /* Need to check if type is a const pointer */
+ int isconst = 0;
+ Delete(SwigType_pop(type));
+ if (SwigType_isconst(type)) {
+ isconst = !Getattr(inclass, "allocate:smartpointermutable");
+ Setattr(inclass, "allocate:smartpointerconst", "1");
+ }
+ else {
+ Setattr(inclass, "allocate:smartpointermutable", "1");
+ }
+ List *methods = smart_pointer_methods(sc, 0, isconst);
+ Setattr(inclass, "allocate:smartpointer", methods);
+ Setattr(inclass, "allocate:smartpointerpointeeclassname", Getattr(sc, "name"));
+ } else {
+ /* Hmmm. The return value is not a pointer. If the type is a value
+ or reference. We're going to chase it to see if another operator->()
+ can be found */
+ if ((SwigType_check_decl(type, "")) || (SwigType_check_decl(type, "r."))) {
+ Node *nn = Swig_symbol_clookup("operator ->", Getattr(sc, "symtab"));
+ if (nn) {
+ Delete(base);
+ Delete(type);
+ sn = nn;
+ continue;
+ }
+ }
+ }
+ }
+ Delete(base);
+ Delete(type);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return SWIG_OK;
+ }
+
+ virtual int constructorDeclaration(Node *n) {
+ if (!inclass)
+ return SWIG_OK;
+ Parm *parms = Getattr(n, "parms");
+
+ process_exceptions(n);
+ if (!extendmode) {
+ if (!ParmList_numrequired(parms)) {
+ /* Class does define a default constructor */
+ /* However, we had better see where it is defined */
+ if (cplus_mode == PUBLIC) {
+ Setattr(inclass, "allocate:default_constructor", "1");
+ } else if (cplus_mode == PROTECTED) {
+ Setattr(inclass, "allocate:default_base_constructor", "1");
+ }
+ }
+ /* Class defines some kind of constructor. May or may not be public */
+ Setattr(inclass, "allocate:has_constructor", "1");
+ if (cplus_mode == PUBLIC) {
+ Setattr(inclass, "allocate:public_constructor", "1");
+ }
+ } else {
+ Setattr(inclass, "allocate:has_constructor", "1");
+ Setattr(inclass, "allocate:public_constructor", "1");
+ }
+
+
+ /* See if this is a copy constructor */
+ if (parms && (ParmList_numrequired(parms) == 1)) {
+ /* Look for a few cases. X(const X &), X(X &), X(X *) */
+ int copy_constructor = 0;
+ SwigType *type = Getattr(inclass, "name");
+ String *tn = NewStringf("r.q(const).%s", type);
+ String *cc = SwigType_typedef_resolve_all(tn);
+ SwigType *rt = SwigType_typedef_resolve_all(Getattr(parms, "type"));
+ if (SwigType_istemplate(type)) {
+ String *tmp = Swig_symbol_template_deftype(cc, 0);
+ Delete(cc);
+ cc = tmp;
+ tmp = Swig_symbol_template_deftype(rt, 0);
+ Delete(rt);
+ rt = tmp;
+ }
+ if (Strcmp(cc, rt) == 0) {
+ copy_constructor = 1;
+ } else {
+ Delete(cc);
+ cc = NewStringf("r.%s", Getattr(inclass, "name"));
+ if (Strcmp(cc, Getattr(parms, "type")) == 0) {
+ copy_constructor = 1;
+ } else {
+ Delete(cc);
+ cc = NewStringf("p.%s", Getattr(inclass, "name"));
+ String *ty = SwigType_strip_qualifiers(Getattr(parms, "type"));
+ if (Strcmp(cc, ty) == 0) {
+ copy_constructor = 1;
+ }
+ Delete(ty);
+ }
+ }
+ Delete(cc);
+ Delete(rt);
+ Delete(tn);
+
+ if (copy_constructor) {
+ Setattr(n, "copy_constructor", "1");
+ Setattr(inclass, "allocate:has_copy_constructor", "1");
+ if (cplus_mode == PUBLIC) {
+ Setattr(inclass, "allocate:copy_constructor", "1");
+ } else if (cplus_mode == PROTECTED) {
+ Setattr(inclass, "allocate:copy_base_constructor", "1");
+ }
+ }
+ }
+ return SWIG_OK;
+ }
+
+ virtual int destructorDeclaration(Node *n) {
+ (void) n;
+ if (!inclass)
+ return SWIG_OK;
+ if (!extendmode) {
+ Setattr(inclass, "allocate:has_destructor", "1");
+ if (cplus_mode == PUBLIC) {
+ Setattr(inclass, "allocate:default_destructor", "1");
+ } else if (cplus_mode == PROTECTED) {
+ Setattr(inclass, "allocate:default_base_destructor", "1");
+ } else if (cplus_mode == PRIVATE) {
+ Setattr(inclass, "allocate:private_destructor", "1");
+ }
+ } else {
+ Setattr(inclass, "allocate:has_destructor", "1");
+ Setattr(inclass, "allocate:default_destructor", "1");
+ }
+ return SWIG_OK;
+ }
+};
+
+void Swig_default_allocators(Node *n) {
+ if (!n)
+ return;
+ Allocate *a = new Allocate;
+ a->top(n);
+ delete a;
+}
diff --git a/contrib/tools/swig/Source/Modules/contract.cxx b/contrib/tools/swig/Source/Modules/contract.cxx
new file mode 100644
index 0000000000..dfc85ebe76
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/contract.cxx
@@ -0,0 +1,358 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * contract.cxx
+ *
+ * Support for Wrap by Contract in SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+
+/* Contract structure. This holds rules about the different kinds of contract sections
+ and their combination rules */
+
+struct contract {
+ const char *section;
+ const char *combiner;
+};
+/* Contract rules. This table defines what contract sections are recognized as well as
+ how contracts are to combined via inheritance */
+
+static contract Rules[] = {
+ {"require:", "&&"},
+ {"ensure:", "||"},
+ {NULL, NULL}
+};
+
+/* ----------------------------------------------------------------------------
+ * class Contracts:
+ *
+ * This class defines the functions that need to be used in
+ * "wrap by contract" module.
+ * ------------------------------------------------------------------------- */
+
+class Contracts:public Dispatcher {
+ String *make_expression(String *s, Node *n);
+ void substitute_parms(String *s, ParmList *p, int method);
+public:
+ Hash *ContractSplit(Node *n);
+ int emit_contract(Node *n, int method);
+ int cDeclaration(Node *n);
+ int constructorDeclaration(Node *n);
+ int externDeclaration(Node *n);
+ int extendDirective(Node *n);
+ int importDirective(Node *n);
+ int includeDirective(Node *n);
+ int namespaceDeclaration(Node *n);
+ int classDeclaration(Node *n);
+ virtual int top(Node *n);
+};
+
+static int Contract_Mode = 0; /* contract option */
+static int InClass = 0; /* Parsing C++ or not */
+static int InConstructor = 0;
+static Node *CurrentClass = 0;
+
+/* Set the contract mode, default is 0 (not open) */
+/* Normally set in main.cxx, when get the "-contracts" option */
+void Swig_contract_mode_set(int flag) {
+ Contract_Mode = flag;
+}
+
+/* Get the contract mode */
+int Swig_contract_mode_get() {
+ return Contract_Mode;
+}
+
+/* Apply contracts */
+void Swig_contracts(Node *n) {
+
+ Contracts *a = new Contracts;
+ a->top(n);
+ delete a;
+}
+
+/* Split the whole contract into preassertion, postassertion and others */
+Hash *Contracts::ContractSplit(Node *n) {
+
+ String *contract = Getattr(n, "feature:contract");
+ Hash *result;
+ if (!contract)
+ return NULL;
+
+ result = NewHash();
+ String *current_section = NewString("");
+ const char *current_section_name = Rules[0].section;
+ List *l = SplitLines(contract);
+
+ Iterator i;
+ for (i = First(l); i.item; i = Next(i)) {
+ int found = 0;
+ if (Strchr(i.item, '{'))
+ continue;
+ if (Strchr(i.item, '}'))
+ continue;
+ for (int j = 0; Rules[j].section; j++) {
+ if (Strstr(i.item, Rules[j].section)) {
+ if (Len(current_section)) {
+ Setattr(result, current_section_name, current_section);
+ current_section = Getattr(result, Rules[j].section);
+ if (!current_section)
+ current_section = NewString("");
+ }
+ current_section_name = Rules[j].section;
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ Append(current_section, i.item);
+ }
+ if (Len(current_section))
+ Setattr(result, current_section_name, current_section);
+ return result;
+}
+
+/* This function looks in base classes and collects contracts found */
+void inherit_contracts(Node *c, Node *n, Hash *contracts, Hash *messages) {
+
+ Node *b, *temp;
+ String *name, *type, *local_decl, *base_decl;
+ List *bases;
+ int found = 0;
+
+ bases = Getattr(c, "bases");
+ if (!bases)
+ return;
+
+ name = Getattr(n, "name");
+ type = Getattr(n, "type");
+ local_decl = Getattr(n, "decl");
+ if (local_decl) {
+ local_decl = SwigType_typedef_resolve_all(local_decl);
+ } else {
+ return;
+ }
+ /* Width first search */
+ for (int i = 0; i < Len(bases); i++) {
+ b = Getitem(bases, i);
+ temp = firstChild(b);
+ while (temp) {
+ base_decl = Getattr(temp, "decl");
+ if (base_decl) {
+ base_decl = SwigType_typedef_resolve_all(base_decl);
+ if ((checkAttribute(temp, "storage", "virtual")) &&
+ (checkAttribute(temp, "name", name)) && (checkAttribute(temp, "type", type)) && (!Strcmp(local_decl, base_decl))) {
+ /* Yes, match found. */
+ Hash *icontracts = Getattr(temp, "contract:rules");
+ Hash *imessages = Getattr(temp, "contract:messages");
+ found = 1;
+ if (icontracts && imessages) {
+ /* Add inherited contracts and messages to the contract rules above */
+ int j = 0;
+ for (j = 0; Rules[j].section; j++) {
+ String *t = Getattr(contracts, Rules[j].section);
+ String *s = Getattr(icontracts, Rules[j].section);
+ if (s) {
+ if (t) {
+ Insert(t, 0, "(");
+ Printf(t, ") %s (%s)", Rules[j].combiner, s);
+ String *m = Getattr(messages, Rules[j].section);
+ Printf(m, " %s [%s from %s]", Rules[j].combiner, Getattr(imessages, Rules[j].section), Getattr(b, "name"));
+ } else {
+ Setattr(contracts, Rules[j].section, NewString(s));
+ Setattr(messages, Rules[j].section, NewStringf("[%s from %s]", Getattr(imessages, Rules[j].section), Getattr(b, "name")));
+ }
+ }
+ }
+ }
+ }
+ Delete(base_decl);
+ }
+ temp = nextSibling(temp);
+ }
+ }
+ Delete(local_decl);
+ if (!found) {
+ for (int j = 0; j < Len(bases); j++) {
+ b = Getitem(bases, j);
+ inherit_contracts(b, n, contracts, messages);
+ }
+ }
+}
+
+/* This function cleans up the assertion string by removing some extraneous characters.
+ Splitting the assertion into pieces */
+
+String *Contracts::make_expression(String *s, Node *n) {
+ String *str_assert, *expr = 0;
+ List *list_assert;
+
+ str_assert = NewString(s);
+ /* Omit all useless characters and split by ; */
+ Replaceall(str_assert, "\n", "");
+ Replaceall(str_assert, "{", "");
+ Replaceall(str_assert, "}", "");
+ Replace(str_assert, " ", "", DOH_REPLACE_ANY | DOH_REPLACE_NOQUOTE);
+ Replace(str_assert, "\t", "", DOH_REPLACE_ANY | DOH_REPLACE_NOQUOTE);
+
+ list_assert = Split(str_assert, ';', -1);
+ Delete(str_assert);
+
+ /* build up new assertion */
+ str_assert = NewString("");
+ Iterator ei;
+
+ for (ei = First(list_assert); ei.item; ei = Next(ei)) {
+ expr = ei.item;
+ if (Len(expr)) {
+ Replaceid(expr, Getattr(n, "name"), Swig_cresult_name());
+ if (Len(str_assert))
+ Append(str_assert, "&&");
+ Printf(str_assert, "(%s)", expr);
+ }
+ }
+ Delete(list_assert);
+ return str_assert;
+}
+
+/* This function substitutes parameter names for argument names in the
+ contract specification. Note: it is assumed that the wrapper code
+ uses arg1 for self and arg2..argn for arguments. */
+
+void Contracts::substitute_parms(String *s, ParmList *p, int method) {
+ int argnum = 1;
+ char argname[32];
+
+ if (method) {
+ Replaceid(s, "$self", "arg1");
+ argnum++;
+ }
+ while (p) {
+ sprintf(argname, "arg%d", argnum);
+ String *name = Getattr(p, "name");
+ if (name) {
+ Replaceid(s, name, argname);
+ }
+ argnum++;
+ p = nextSibling(p);
+ }
+}
+
+int Contracts::emit_contract(Node *n, int method) {
+ Hash *contracts;
+ Hash *messages;
+ String *c;
+
+ ParmList *cparms;
+
+ if (!Getattr(n, "feature:contract"))
+ return SWIG_ERROR;
+
+ /* Get contract parameters */
+ cparms = Getmeta(Getattr(n, "feature:contract"), "parms");
+
+ /* Split contract into preassert & postassert */
+ contracts = ContractSplit(n);
+ if (!contracts)
+ return SWIG_ERROR;
+
+ /* This messages hash is used to hold the error messages that will be displayed on
+ failed contract. */
+
+ messages = NewHash();
+
+ /* Take the different contract expressions and clean them up a bit */
+ Iterator i;
+ for (i = First(contracts); i.item; i = Next(i)) {
+ String *e = make_expression(i.item, n);
+ substitute_parms(e, cparms, method);
+ Setattr(contracts, i.key, e);
+
+ /* Make a string containing error messages */
+ Setattr(messages, i.key, NewString(e));
+ }
+
+ /* If we're in a class. We need to inherit other assertions. */
+ if (InClass) {
+ inherit_contracts(CurrentClass, n, contracts, messages);
+ }
+
+ /* Save information */
+ Setattr(n, "contract:rules", contracts);
+ Setattr(n, "contract:messages", messages);
+
+ /* Okay. Generate the contract runtime code. */
+
+ if ((c = Getattr(contracts, "require:"))) {
+ Setattr(n, "contract:preassert", NewStringf("SWIG_contract_assert(%s, \"Contract violation: require: %s\");\n", c, Getattr(messages, "require:")));
+ }
+ if ((c = Getattr(contracts, "ensure:"))) {
+ Setattr(n, "contract:postassert", NewStringf("SWIG_contract_assert(%s, \"Contract violation: ensure: %s\");\n", c, Getattr(messages, "ensure:")));
+ }
+ return SWIG_OK;
+}
+
+int Contracts::cDeclaration(Node *n) {
+ int ret = SWIG_OK;
+ String *decl = Getattr(n, "decl");
+
+ /* Not a function. Don't even bother with it (for now) */
+ if (!SwigType_isfunction(decl))
+ return SWIG_OK;
+
+ if (Getattr(n, "feature:contract"))
+ ret = emit_contract(n, InClass && !Swig_storage_isstatic(n));
+ return ret;
+}
+
+int Contracts::constructorDeclaration(Node *n) {
+ int ret = SWIG_OK;
+ InConstructor = 1;
+ if (Getattr(n, "feature:contract"))
+ ret = emit_contract(n, 0);
+ InConstructor = 0;
+ return ret;
+}
+
+int Contracts::externDeclaration(Node *n) {
+ return emit_children(n);
+}
+
+int Contracts::extendDirective(Node *n) {
+ return emit_children(n);
+}
+
+int Contracts::importDirective(Node *n) {
+ return emit_children(n);
+}
+
+int Contracts::includeDirective(Node *n) {
+ return emit_children(n);
+}
+
+int Contracts::namespaceDeclaration(Node *n) {
+ return emit_children(n);
+}
+
+int Contracts::classDeclaration(Node *n) {
+ int ret = SWIG_OK;
+ int oldInClass = InClass;
+ Node *oldClass = CurrentClass;
+ InClass = 1;
+ CurrentClass = n;
+ emit_children(n);
+ InClass = oldInClass;
+ CurrentClass = oldClass;
+ return ret;
+}
+
+int Contracts::top(Node *n) {
+ emit_children(n);
+ return SWIG_OK;
+}
diff --git a/contrib/tools/swig/Source/Modules/csharp.cxx b/contrib/tools/swig/Source/Modules/csharp.cxx
new file mode 100644
index 0000000000..240a002b40
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/csharp.cxx
@@ -0,0 +1,4616 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * csharp.cxx
+ *
+ * C# language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+#include <limits.h> // for INT_MAX
+#include <ctype.h>
+
+/* Hash type used for upcalls from C/C++ */
+typedef DOH UpcallData;
+
+class CSHARP:public Language {
+ static const char *usage;
+ const String *empty_string;
+ const String *public_string;
+ const String *protected_string;
+
+ Hash *swig_types_hash;
+ File *f_begin;
+ File *f_runtime;
+ File *f_runtime_h;
+ File *f_header;
+ File *f_wrappers;
+ File *f_init;
+ File *f_directors;
+ File *f_directors_h;
+ File *f_single_out;
+ List *filenames_list;
+
+ bool proxy_flag; // Flag for generating proxy classes
+ bool native_function_flag; // Flag for when wrapping a native function
+ bool enum_constant_flag; // Flag for when wrapping an enum or constant
+ bool static_flag; // Flag for when wrapping a static functions or member variables
+ bool variable_wrapper_flag; // Flag for when wrapping a nonstatic member variable
+ bool wrapping_member_flag; // Flag for when wrapping a member variable/enum/const
+ bool global_variable_flag; // Flag for when wrapping a global variable
+ bool old_variable_names; // Flag for old style variable names in the intermediary class
+ bool generate_property_declaration_flag; // Flag for generating properties
+
+ String *imclass_name; // intermediary class name
+ String *module_class_name; // module class name
+ String *imclass_class_code; // intermediary class code
+ String *proxy_class_def;
+ String *proxy_class_code;
+ String *interface_class_code; // if %feature("interface") was declared for a class, here goes the interface declaration
+ String *module_class_code;
+ String *proxy_class_name; // proxy class name
+ String *full_imclass_name; // fully qualified intermediary class name when using nspace feature, otherwise same as imclass_name
+ String *variable_name; //Name of a variable being wrapped
+ String *proxy_class_constants_code;
+ String *module_class_constants_code;
+ String *enum_code;
+ String *dllimport; // DllImport attribute name
+ String *namespce; // Optional namespace name
+ String *imclass_imports; //intermediary class imports from %pragma
+ String *module_imports; //module imports from %pragma
+ String *imclass_baseclass; //inheritance for intermediary class class from %pragma
+ String *module_baseclass; //inheritance for module class from %pragma
+ String *imclass_interfaces; //interfaces for intermediary class class from %pragma
+ String *module_interfaces; //interfaces for module class from %pragma
+ String *imclass_class_modifiers; //class modifiers for intermediary class overridden by %pragma
+ String *module_class_modifiers; //class modifiers for module class overridden by %pragma
+ String *upcasts_code; //C++ casts for inheritance hierarchies C++ code
+ String *imclass_cppcasts_code; //C++ casts up inheritance hierarchies intermediary class code
+ String *director_callback_typedefs; // Director function pointer typedefs for callbacks
+ String *director_callbacks; // Director callback function pointer member variables
+ String *director_delegate_callback; // Director callback method that delegates are set to call
+ String *director_delegate_definitions; // Director delegates definitions in proxy class
+ String *director_delegate_instances; // Director delegates member variables in proxy class
+ String *director_method_types; // Director method types
+ String *director_connect_parms; // Director delegates parameter list for director connect call
+ String *destructor_call; //C++ destructor call if any
+ String *output_file; // File name for single file mode. If set all generated code will be written to this file
+
+ // Director method stuff:
+ List *dmethods_seq;
+ Hash *dmethods_table;
+ int n_dmethods;
+ int n_directors;
+ int first_class_dmethod;
+ int curr_class_dmethod;
+ int nesting_depth;
+
+ enum EnumFeature { SimpleEnum, TypeunsafeEnum, TypesafeEnum, ProperEnum };
+
+public:
+
+ /* -----------------------------------------------------------------------------
+ * CSHARP()
+ * ----------------------------------------------------------------------------- */
+
+ CSHARP():empty_string(NewString("")),
+ public_string(NewString("public")),
+ protected_string(NewString("protected")),
+ swig_types_hash(NULL),
+ f_begin(NULL),
+ f_runtime(NULL),
+ f_runtime_h(NULL),
+ f_header(NULL),
+ f_wrappers(NULL),
+ f_init(NULL),
+ f_directors(NULL),
+ f_directors_h(NULL),
+ f_single_out(NULL),
+ filenames_list(NULL),
+ proxy_flag(true),
+ native_function_flag(false),
+ enum_constant_flag(false),
+ static_flag(false),
+ variable_wrapper_flag(false),
+ wrapping_member_flag(false),
+ global_variable_flag(false),
+ old_variable_names(false),
+ generate_property_declaration_flag(false),
+ imclass_name(NULL),
+ module_class_name(NULL),
+ imclass_class_code(NULL),
+ proxy_class_def(NULL),
+ proxy_class_code(NULL),
+ interface_class_code(NULL),
+ module_class_code(NULL),
+ proxy_class_name(NULL),
+ full_imclass_name(NULL),
+ variable_name(NULL),
+ proxy_class_constants_code(NULL),
+ module_class_constants_code(NULL),
+ enum_code(NULL),
+ dllimport(NULL),
+ namespce(NULL),
+ imclass_imports(NULL),
+ module_imports(NULL),
+ imclass_baseclass(NULL),
+ module_baseclass(NULL),
+ imclass_interfaces(NULL),
+ module_interfaces(NULL),
+ imclass_class_modifiers(NULL),
+ module_class_modifiers(NULL),
+ upcasts_code(NULL),
+ imclass_cppcasts_code(NULL),
+ director_callback_typedefs(NULL),
+ director_callbacks(NULL),
+ director_delegate_callback(NULL),
+ director_delegate_definitions(NULL),
+ director_delegate_instances(NULL),
+ director_method_types(NULL),
+ director_connect_parms(NULL),
+ destructor_call(NULL),
+ output_file(NULL),
+ dmethods_seq(NULL),
+ dmethods_table(NULL),
+ n_dmethods(0),
+ n_directors(0),
+ first_class_dmethod(0),
+ curr_class_dmethod(0),
+ nesting_depth(0){
+ /* for now, multiple inheritance in directors is disabled, this
+ should be easy to implement though */
+ director_multiple_inheritance = 0;
+ director_language = 1;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getProxyName()
+ *
+ * Test to see if a type corresponds to something wrapped with a proxy class.
+ * Return NULL if not otherwise the proxy class name, fully qualified with
+ * a namespace if the nspace feature is used.
+ * ----------------------------------------------------------------------------- */
+
+ String *getProxyName(SwigType *t) {
+ String *proxyname = NULL;
+ if (proxy_flag) {
+ Node *n = classLookup(t);
+ if (n) {
+ proxyname = Getattr(n, "proxyname");
+ if (!proxyname) {
+ String *nspace = Getattr(n, "sym:nspace");
+ String *symname = Copy(Getattr(n, "sym:name"));
+ if (symname && !GetFlag(n, "feature:flatnested")) {
+ for (Node *outer_class = Getattr(n, "nested:outer"); outer_class; outer_class = Getattr(outer_class, "nested:outer")) {
+ if (String* name = Getattr(outer_class, "sym:name")) {
+ Push(symname, ".");
+ Push(symname, name);
+ }
+ else
+ return NULL;
+ }
+ }
+ if (nspace) {
+ if (namespce)
+ proxyname = NewStringf("%s.%s.%s", namespce, nspace, symname);
+ else
+ proxyname = NewStringf("%s.%s", nspace, symname);
+ } else {
+ proxyname = Copy(symname);
+ }
+ Setattr(n, "proxyname", proxyname);
+ Delete(proxyname);
+ Delete(symname);
+ }
+ }
+ }
+ return proxyname;
+ }
+
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+
+ SWIG_library_directory("csharp");
+
+ // Look for certain command line options
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-dllimport") == 0) {
+ if (argv[i + 1]) {
+ dllimport = NewString("");
+ Printf(dllimport, argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-namespace") == 0) {
+ if (argv[i + 1]) {
+ namespce = NewString("");
+ Printf(namespce, argv[i + 1]);
+ if (Len(namespce) == 0) {
+ Delete(namespce);
+ namespce = 0;
+ }
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if ((strcmp(argv[i], "-noproxy") == 0)) {
+ Swig_mark_arg(i);
+ proxy_flag = false;
+ } else if (strcmp(argv[i], "-oldvarnames") == 0) {
+ Swig_mark_arg(i);
+ old_variable_names = true;
+ } else if (strcmp(argv[i], "-outfile") == 0) {
+ if (argv[i + 1]) {
+ output_file = NewString("");
+ Printf(output_file, argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-help") == 0) {
+ Printf(stdout, "%s\n", usage);
+ }
+ }
+ }
+
+ // Add a symbol to the parser for conditional compilation
+ Preprocessor_define("SWIGCSHARP 1", 0);
+
+ // Add typemap definitions
+ SWIG_typemap_lang("csharp");
+ SWIG_config_file("csharp.swg");
+
+ allow_overloading();
+ Swig_interface_feature_enable();
+ }
+
+ /* ---------------------------------------------------------------------
+ * top()
+ * --------------------------------------------------------------------- */
+
+ virtual int top(Node *n) {
+
+ // Get any options set in the module directive
+ Node *optionsnode = Getattr(Getattr(n, "module"), "options");
+
+ if (optionsnode) {
+ if (Getattr(optionsnode, "imclassname"))
+ imclass_name = Copy(Getattr(optionsnode, "imclassname"));
+ /* check if directors are enabled for this module. note: this
+ * is a "master" switch, without which no director code will be
+ * emitted. %feature("director") statements are also required
+ * to enable directors for individual classes or methods.
+ *
+ * use %module(directors="1") modulename at the start of the
+ * interface file to enable director generation.
+ */
+ if (Getattr(optionsnode, "directors")) {
+ allow_directors();
+ }
+ if (Getattr(optionsnode, "dirprot")) {
+ allow_dirprot();
+ }
+ allow_allprotected(GetFlag(optionsnode, "allprotected"));
+ }
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+ String *outfile_h = Getattr(n, "outfile_h");
+
+ if (!outfile) {
+ Printf(stderr, "Unable to determine outfile\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+
+ if (directorsEnabled()) {
+ if (!outfile_h) {
+ Printf(stderr, "Unable to determine outfile_h\n");
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
+ if (!f_runtime_h) {
+ FileErrorDisplay(outfile_h);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+ f_directors_h = NewString("");
+ f_directors = NewString("");
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+
+ swig_types_hash = NewHash();
+ filenames_list = NewList();
+
+ // Make the intermediary class and module class names. The intermediary class name can be set in the module directive.
+ if (!imclass_name) {
+ imclass_name = NewStringf("%sPINVOKE", Getattr(n, "name"));
+ module_class_name = Copy(Getattr(n, "name"));
+ } else {
+ // Rename the module name if it is the same as intermediary class name - a backwards compatibility solution
+ if (Cmp(imclass_name, Getattr(n, "name")) == 0)
+ module_class_name = NewStringf("%sModule", Getattr(n, "name"));
+ else
+ module_class_name = Copy(Getattr(n, "name"));
+ }
+
+ // module class and intermediary classes are always created
+ if (!addSymbol(imclass_name, n))
+ return SWIG_ERROR;
+ if (!addSymbol(module_class_name, n))
+ return SWIG_ERROR;
+
+ imclass_class_code = NewString("");
+ proxy_class_def = NewString("");
+ proxy_class_code = NewString("");
+ module_class_constants_code = NewString("");
+ imclass_baseclass = NewString("");
+ imclass_interfaces = NewString("");
+ imclass_class_modifiers = NewString("");
+ module_class_code = NewString("");
+ module_baseclass = NewString("");
+ module_interfaces = NewString("");
+ module_imports = NewString("");
+ module_class_modifiers = NewString("");
+ imclass_imports = NewString("");
+ imclass_cppcasts_code = NewString("");
+ director_connect_parms = NewString("");
+ upcasts_code = NewString("");
+ dmethods_seq = NewList();
+ dmethods_table = NewHash();
+ n_dmethods = 0;
+ n_directors = 0;
+ if (!dllimport)
+ dllimport = Copy(module_class_name);
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "CSHARP");
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+
+ /* Emit initial director header and director code: */
+ Swig_banner(f_directors_h);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module_class_name);
+ Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module_class_name);
+
+ Printf(f_directors, "\n\n");
+ Printf(f_directors, "/* ---------------------------------------------------\n");
+ Printf(f_directors, " * C++ director class methods\n");
+ Printf(f_directors, " * --------------------------------------------------- */\n\n");
+ if (outfile_h) {
+ String *filename = Swig_file_filename(outfile_h);
+ Printf(f_directors, "#include \"%s\"\n\n", filename);
+ Delete(filename);
+ }
+ }
+
+ Printf(f_runtime, "\n");
+ if (namespce) {
+ String *wrapper_name = NewStringf("");
+ Printf(wrapper_name, "CSharp_%s_%%f", namespce);
+ Swig_name_register("wrapper", wrapper_name);
+ Delete(wrapper_name);
+ }
+ else {
+ Swig_name_register("wrapper", "CSharp_%f");
+ }
+
+ if (old_variable_names) {
+ Swig_name_register("set", "set_%n%v");
+ Swig_name_register("get", "get_%n%v");
+ }
+
+ Printf(f_wrappers, "\n#ifdef __cplusplus\n");
+ Printf(f_wrappers, "extern \"C\" {\n");
+ Printf(f_wrappers, "#endif\n\n");
+
+ /* Emit code */
+ Language::top(n);
+
+ if (directorsEnabled()) {
+ // Insert director runtime into the f_runtime file (make it occur before %header section)
+ Swig_insert_file("director_common.swg", f_runtime);
+ Swig_insert_file("director.swg", f_runtime);
+ }
+ // Generate the intermediary class
+ {
+ File *f_im = getOutputFile(SWIG_output_directory(), imclass_name);
+
+ addOpenNamespace(0, f_im);
+
+ if (imclass_imports)
+ Printf(f_im, "%s\n", imclass_imports);
+
+ if (Len(imclass_class_modifiers) > 0)
+ Printf(f_im, "%s ", imclass_class_modifiers);
+ Printf(f_im, "%s ", imclass_name);
+
+ if (imclass_baseclass && *Char(imclass_baseclass))
+ Printf(f_im, ": %s ", imclass_baseclass);
+ if (Len(imclass_interfaces) > 0)
+ Printv(f_im, "implements ", imclass_interfaces, " ", NIL);
+ Printf(f_im, "{\n");
+
+ // Add the intermediary class methods
+ Replaceall(imclass_class_code, "$module", module_class_name);
+ Replaceall(imclass_class_code, "$imclassname", imclass_name);
+ Replaceall(imclass_class_code, "$dllimport", dllimport);
+ Printv(f_im, imclass_class_code, NIL);
+ Printv(f_im, imclass_cppcasts_code, NIL);
+
+ // Finish off the class
+ Printf(f_im, "}\n");
+ addCloseNamespace(0, f_im);
+
+ if (f_im != f_single_out)
+ Delete(f_im);
+ f_im = NULL;
+ }
+
+ // Generate the C# module class
+ {
+ File *f_module = getOutputFile(SWIG_output_directory(), module_class_name);
+
+ addOpenNamespace(0, f_module);
+
+ if (module_imports)
+ Printf(f_module, "%s\n", module_imports);
+
+ if (Len(module_class_modifiers) > 0)
+ Printf(f_module, "%s ", module_class_modifiers);
+ Printf(f_module, "%s ", module_class_name);
+
+ if (module_baseclass && *Char(module_baseclass))
+ Printf(f_module, ": %s ", module_baseclass);
+ if (Len(module_interfaces) > 0)
+ Printv(f_module, "implements ", module_interfaces, " ", NIL);
+ Printf(f_module, "{\n");
+
+ Replaceall(module_class_code, "$module", module_class_name);
+ Replaceall(module_class_constants_code, "$module", module_class_name);
+
+ Replaceall(module_class_code, "$imclassname", imclass_name);
+ Replaceall(module_class_constants_code, "$imclassname", imclass_name);
+
+ Replaceall(module_class_code, "$dllimport", dllimport);
+ Replaceall(module_class_constants_code, "$dllimport", dllimport);
+
+ // Add the wrapper methods
+ Printv(f_module, module_class_code, NIL);
+
+ // Write out all the global constants
+ Printv(f_module, module_class_constants_code, NIL);
+
+ // Finish off the class
+ Printf(f_module, "}\n");
+ addCloseNamespace(0, f_module);
+
+ if (f_module != f_single_out)
+ Delete(f_module);
+ f_module = NULL;
+ }
+
+ if (upcasts_code)
+ Printv(f_wrappers, upcasts_code, NIL);
+
+ Printf(f_wrappers, "#ifdef __cplusplus\n");
+ Printf(f_wrappers, "}\n");
+ Printf(f_wrappers, "#endif\n");
+
+ // Output a C# type wrapper class for each SWIG type
+ for (Iterator swig_type = First(swig_types_hash); swig_type.key; swig_type = Next(swig_type)) {
+ emitTypeWrapperClass(swig_type.key, swig_type.item);
+ }
+
+ // Check for overwriting file problems on filesystems that are case insensitive
+ Iterator it1;
+ Iterator it2;
+ for (it1 = First(filenames_list); it1.item; it1 = Next(it1)) {
+ String *item1_lower = Swig_string_lower(it1.item);
+ for (it2 = Next(it1); it2.item; it2 = Next(it2)) {
+ String *item2_lower = Swig_string_lower(it2.item);
+ if (it1.item && it2.item) {
+ if (Strcmp(item1_lower, item2_lower) == 0) {
+ Swig_warning(WARN_LANG_PORTABILITY_FILENAME, input_file, line_number,
+ "Portability warning: File %s will be overwritten by %s on case insensitive filesystems such as "
+ "Windows' FAT32 and NTFS unless the class/module name is renamed\n", it1.item, it2.item);
+ }
+ }
+ Delete(item2_lower);
+ }
+ Delete(item1_lower);
+ }
+
+ Delete(swig_types_hash);
+ swig_types_hash = NULL;
+ Delete(filenames_list);
+ filenames_list = NULL;
+ Delete(imclass_name);
+ imclass_name = NULL;
+ Delete(imclass_class_code);
+ imclass_class_code = NULL;
+ Delete(proxy_class_def);
+ proxy_class_def = NULL;
+ Delete(proxy_class_code);
+ proxy_class_code = NULL;
+ Delete(module_class_constants_code);
+ module_class_constants_code = NULL;
+ Delete(imclass_baseclass);
+ imclass_baseclass = NULL;
+ Delete(imclass_interfaces);
+ imclass_interfaces = NULL;
+ Delete(imclass_class_modifiers);
+ imclass_class_modifiers = NULL;
+ Delete(module_class_name);
+ module_class_name = NULL;
+ Delete(module_class_code);
+ module_class_code = NULL;
+ Delete(module_baseclass);
+ module_baseclass = NULL;
+ Delete(module_interfaces);
+ module_interfaces = NULL;
+ Delete(module_imports);
+ module_imports = NULL;
+ Delete(module_class_modifiers);
+ module_class_modifiers = NULL;
+ Delete(imclass_imports);
+ imclass_imports = NULL;
+ Delete(imclass_cppcasts_code);
+ imclass_cppcasts_code = NULL;
+ Delete(upcasts_code);
+ upcasts_code = NULL;
+ Delete(dmethods_seq);
+ dmethods_seq = NULL;
+ Delete(dmethods_table);
+ dmethods_table = NULL;
+ Delete(namespce);
+ namespce = NULL;
+ n_dmethods = 0;
+
+ /* Close all of the files */
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+
+ if (directorsEnabled()) {
+ Dump(f_directors, f_begin);
+ Dump(f_directors_h, f_runtime_h);
+
+ Printf(f_runtime_h, "\n");
+ Printf(f_runtime_h, "#endif\n");
+
+ Delete(f_runtime_h);
+ f_runtime_h = NULL;
+ Delete(f_directors);
+ f_directors = NULL;
+ Delete(f_directors_h);
+ f_directors_h = NULL;
+ }
+
+ if (f_single_out) {
+ Dump(f_single_out, f_begin);
+ Delete(f_single_out);
+ f_single_out = NULL;
+ }
+
+ Dump(f_wrappers, f_begin);
+ Wrapper_pretty_print(f_init, f_begin);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_runtime);
+ Delete(f_begin);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * emitBanner()
+ * ----------------------------------------------------------------------------- */
+
+ void emitBanner(File *f) {
+ Printf(f, "//------------------------------------------------------------------------------\n");
+ Printf(f, "// <auto-generated />\n");
+ Printf(f, "//\n");
+ Swig_banner_target_lang(f, "//");
+ Printf(f, "//------------------------------------------------------------------------------\n\n");
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getOutputFile()
+ *
+ * Prepares a File object by creating the file in the file system and
+ * writing the banner for auto-generated files to it (emitBanner).
+ * If '-outfile' is provided (single file mode) the supplied parameters will
+ * be ignored and the returned file will always be:
+ * <outdir>/<outfile>
+ * Otherwise the file will be:
+ * <dir>/<name>.cs
+ * ----------------------------------------------------------------------------- */
+
+ File *getOutputFile(const String *dir, const String *name) {
+ if (output_file) {
+ if (!f_single_out) {
+ String *filen = NewStringf("%s%s", SWIG_output_directory(), output_file);
+ f_single_out = NewFile(filen, "w", SWIG_output_files());
+ if (!f_single_out) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ emitBanner(f_single_out);
+ }
+ return f_single_out;
+ } else {
+ String *filen = NewStringf("%s%s.cs", dir, name);
+ File *f = NewFile(filen, "w", SWIG_output_files());
+ if (!f) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ emitBanner(f);
+ return f;
+ }
+ }
+
+ /*-----------------------------------------------------------------------
+ * Add new director upcall signature
+ *----------------------------------------------------------------------*/
+
+ UpcallData *addUpcallMethod(String *imclass_method, String *class_method, String *decl, String *overloaded_name) {
+ String *key = NewStringf("%s|%s", imclass_method, decl);
+
+ ++curr_class_dmethod;
+
+ String *class_methodidx = NewStringf("%d", n_dmethods - first_class_dmethod);
+ n_dmethods++;
+
+ Hash *new_udata = NewHash();
+ Append(dmethods_seq, new_udata);
+ Setattr(dmethods_table, key, new_udata);
+
+ Setattr(new_udata, "method", Copy(class_method));
+ Setattr(new_udata, "class_methodidx", class_methodidx);
+ Setattr(new_udata, "decl", Copy(decl));
+ Setattr(new_udata, "overname", Copy(overloaded_name));
+
+ Delete(key);
+ return new_udata;
+ }
+
+ /*-----------------------------------------------------------------------
+ * Get director upcall signature
+ *----------------------------------------------------------------------*/
+
+ /*
+ UpcallData * getUpcallMethodData(String *director_class, String *decl) {
+ String *key = NewStringf("%s|%s", director_class, decl);
+ UpcallData *udata = Getattr(dmethods_table, key);
+
+ Delete(key);
+ return udata;
+ }
+ */
+
+ /* ----------------------------------------------------------------------
+ * nativeWrapper()
+ * ---------------------------------------------------------------------- */
+
+ virtual int nativeWrapper(Node *n) {
+ String *wrapname = Getattr(n, "wrap:name");
+
+ if (!addSymbol(wrapname, n, imclass_name))
+ return SWIG_ERROR;
+
+ if (Getattr(n, "type")) {
+ Swig_save("nativeWrapper", n, "name", NIL);
+ Setattr(n, "name", wrapname);
+ native_function_flag = true;
+ functionWrapper(n);
+ Swig_restore(n);
+ native_function_flag = false;
+ } else {
+ Swig_error(input_file, line_number, "No return type for %%native method %s.\n", Getattr(n, "wrap:name"));
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * functionWrapper()
+ * ---------------------------------------------------------------------- */
+
+ virtual int functionWrapper(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ Parm *p;
+ int i;
+ String *c_return_type = NewString("");
+ String *im_return_type = NewString("");
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+ String *body = NewString("");
+ String *im_outattributes = 0;
+ int num_arguments = 0;
+ bool is_void_return;
+ String *overloaded_name = getOverloadedName(n);
+
+ if (!Getattr(n, "sym:overloaded")) {
+ if (!addSymbol(symname, n, imclass_name))
+ return SWIG_ERROR;
+ }
+
+ /*
+ The rest of this function deals with generating the intermediary class wrapper function (that wraps
+ a c/c++ function) and generating the PInvoke c code. Each C# wrapper function has a
+ matching PInvoke c function call.
+ */
+
+ // A new wrapper function object
+ Wrapper *f = NewWrapper();
+
+ // Make a wrapper name for this function
+ String *wname = Swig_name_wrapper(overloaded_name);
+
+ /* Attach the non-standard typemaps to the parameter list. */
+ Swig_typemap_attach_parms("ctype", l, f);
+ Swig_typemap_attach_parms("imtype", l, f);
+
+ /* Get return types */
+ if ((tm = Swig_typemap_lookup("ctype", n, "", 0))) {
+ String *ctypeout = Getattr(n, "tmap:ctype:out"); // the type in the ctype typemap's out attribute overrides the type in the typemap
+ if (ctypeout)
+ tm = ctypeout;
+ Printf(c_return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CTYPE_UNDEF, input_file, line_number, "No ctype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if ((tm = Swig_typemap_lookup("imtype", n, "", 0))) {
+ String *imtypeout = Getattr(n, "tmap:imtype:out"); // the type in the imtype typemap's out attribute overrides the type in the typemap
+ if (imtypeout)
+ tm = imtypeout;
+ Printf(im_return_type, "%s", tm);
+ im_outattributes = Getattr(n, "tmap:imtype:outattributes");
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ is_void_return = (Cmp(c_return_type, "void") == 0);
+ if (!is_void_return)
+ Wrapper_add_localv(f, "jresult", c_return_type, "jresult", NIL);
+
+ Printv(f->def, " SWIGEXPORT ", c_return_type, " SWIGSTDCALL ", wname, "(", NIL);
+
+ // Emit all of the local variables for holding arguments.
+ emit_parameter_variables(l, f);
+
+ /* Attach the standard typemaps */
+ emit_attach_parmmaps(l, f);
+
+ // Parameter overloading
+ Setattr(n, "wrap:parms", l);
+ Setattr(n, "wrap:name", wname);
+
+ // Wrappers not wanted for some methods where the parameters cannot be overloaded in C#
+ if (Getattr(n, "sym:overloaded")) {
+ // Emit warnings for the few cases that can't be overloaded in C# and give up on generating wrapper
+ Swig_overload_check(n);
+ if (Getattr(n, "overload:ignore")) {
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+ }
+
+ Printv(imclass_class_code, "\n [global::System.Runtime.InteropServices.DllImport(\"", dllimport, "\", EntryPoint=\"", wname, "\")]\n", NIL);
+
+ if (im_outattributes)
+ Printf(imclass_class_code, " %s\n", im_outattributes);
+
+ Printf(imclass_class_code, " public static extern %s %s(", im_return_type, overloaded_name);
+
+
+ /* Get number of required and total arguments */
+ num_arguments = emit_num_arguments(l);
+ int gencomma = 0;
+
+ // Now walk the function parameter list and generate code to get arguments
+ for (i = 0, p = l; i < num_arguments; i++) {
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+ String *im_param_type = NewString("");
+ String *c_param_type = NewString("");
+ String *arg = NewString("");
+
+ Printf(arg, "j%s", ln);
+
+ /* Get the ctype types of the parameter */
+ if ((tm = Getattr(p, "tmap:ctype"))) {
+ Printv(c_param_type, tm, NIL);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CTYPE_UNDEF, input_file, line_number, "No ctype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Get the intermediary class parameter types of the parameter */
+ if ((tm = Getattr(p, "tmap:imtype"))) {
+ const String *inattributes = Getattr(p, "tmap:imtype:inattributes");
+ Printf(im_param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to intermediary class method */
+ if (gencomma)
+ Printf(imclass_class_code, ", ");
+ Printf(imclass_class_code, "%s %s", im_param_type, arg);
+
+ // Add parameter to C function
+ Printv(f->def, gencomma ? ", " : "", c_param_type, " ", arg, NIL);
+
+ gencomma = 1;
+
+ // Get typemap for this argument
+ if ((tm = Getattr(p, "tmap:in"))) {
+ canThrow(n, "in", p);
+ Replaceall(tm, "$arg", arg); /* deprecated? */
+ Replaceall(tm, "$input", arg);
+ Setattr(p, "emit:input", arg);
+ Printf(f->code, "%s\n", tm);
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ p = nextSibling(p);
+ }
+ Delete(im_param_type);
+ Delete(c_param_type);
+ Delete(arg);
+ }
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ canThrow(n, "check", p);
+ Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ canThrow(n, "freearg", p);
+ Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert argument output code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ canThrow(n, "argout", p);
+ Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
+ Replaceall(tm, "$result", "jresult");
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Look for usage of throws typemap and the canthrow flag
+ ParmList *throw_parm_list = NULL;
+ if ((throw_parm_list = Getattr(n, "catchlist"))) {
+ Swig_typemap_attach_parms("throws", throw_parm_list, f);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ canThrow(n, "throws", p);
+ }
+ }
+ }
+
+ String *null_attribute = 0;
+ // Now write code to make the function call
+ if (!native_function_flag) {
+
+ Swig_director_emit_dynamic_cast(n, f);
+ String *actioncode = emit_action(n);
+
+ /* Return value if necessary */
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ canThrow(n, "out", n);
+ Replaceall(tm, "$result", "jresult");
+
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "1");
+ else
+ Replaceall(tm, "$owner", "0");
+
+ Printf(f->code, "%s", tm);
+ null_attribute = Getattr(n, "tmap:out:null");
+ if (Len(tm))
+ Printf(f->code, "\n");
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(t, 0), Getattr(n, "name"));
+ }
+ emit_return_variable(n, t, f);
+ }
+
+ /* Output argument output code */
+ Printv(f->code, outarg, NIL);
+
+ /* Output cleanup code */
+ Printv(f->code, cleanup, NIL);
+
+ /* Look to see if there is any newfree cleanup code */
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ canThrow(n, "newfree", n);
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if (!native_function_flag) {
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ canThrow(n, "ret", n);
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ /* Finish C function and intermediary class function definitions */
+ Printf(imclass_class_code, ")");
+ Printf(imclass_class_code, ";\n");
+
+ Printf(f->def, ") {");
+
+ if (!is_void_return)
+ Printv(f->code, " return jresult;\n", NIL);
+ Printf(f->code, "}\n");
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", symname);
+
+ /* Contract macro modification */
+ if (Replaceall(f->code, "SWIG_contract_assert(", "SWIG_contract_assert($null, ") > 0) {
+ Setattr(n, "csharp:canthrow", "1");
+ }
+
+ if (!null_attribute)
+ Replaceall(f->code, "$null", "0");
+ else
+ Replaceall(f->code, "$null", null_attribute);
+
+ /* Dump the function out */
+ if (!native_function_flag) {
+ Wrapper_print(f, f_wrappers);
+
+ // Handle %csexception which sets the canthrow attribute
+ if (Getattr(n, "feature:except:canthrow"))
+ Setattr(n, "csharp:canthrow", "1");
+
+ // A very simple check (it is not foolproof) to help typemap/feature writers for
+ // throwing C# exceptions from unmanaged code. It checks for the common methods which
+ // set a pending C# exception... the 'canthrow' typemap/feature attribute must be set
+ // so that code which checks for pending exceptions is added in the C# proxy method.
+ if (!Getattr(n, "csharp:canthrow")) {
+ if (Strstr(f->code, "SWIG_exception")) {
+ Swig_warning(WARN_CSHARP_CANTHROW, input_file, line_number,
+ "Unmanaged code contains a call to SWIG_exception and C# code does not handle pending exceptions via the canthrow attribute.\n");
+ } else if (Strstr(f->code, "SWIG_CSharpSetPendingException")) {
+ Swig_warning(WARN_CSHARP_CANTHROW, input_file, line_number,
+ "Unmanaged code contains a call to a SWIG_CSharpSetPendingException method and C# code does not handle pending exceptions via the canthrow attribute.\n");
+ }
+ }
+ }
+
+ if (!(proxy_flag && is_wrapping_class()) && !enum_constant_flag) {
+ moduleClassFunctionHandler(n);
+ }
+
+ /*
+ * Generate the proxy class properties for public member variables.
+ * Not for enums and constants.
+ */
+ if (proxy_flag && wrapping_member_flag && !enum_constant_flag) {
+ // Capitalize the first letter in the variable in the getter/setter function name
+ bool getter_flag = Cmp(symname, Swig_name_set(getNSpace(), Swig_name_member(0, getClassPrefix(), variable_name))) != 0;
+
+ String *getter_setter_name = NewString("");
+ if (!getter_flag)
+ Printf(getter_setter_name, "set");
+ else
+ Printf(getter_setter_name, "get");
+ Putc(toupper((int) *Char(variable_name)), getter_setter_name);
+ Printf(getter_setter_name, "%s", Char(variable_name) + 1);
+
+ Setattr(n, "proxyfuncname", getter_setter_name);
+ Setattr(n, "imfuncname", symname);
+
+ proxyClassFunctionHandler(n);
+ Delete(getter_setter_name);
+ }
+
+ Delete(c_return_type);
+ Delete(im_return_type);
+ Delete(cleanup);
+ Delete(outarg);
+ Delete(body);
+ Delete(overloaded_name);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * variableWrapper()
+ * ----------------------------------------------------------------------- */
+
+ virtual int variableWrapper(Node *n) {
+ Language::variableWrapper(n);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * globalvariableHandler()
+ * ------------------------------------------------------------------------ */
+
+ virtual int globalvariableHandler(Node *n) {
+
+ generate_property_declaration_flag = true;
+ variable_name = Getattr(n, "sym:name");
+ global_variable_flag = true;
+ int ret = Language::globalvariableHandler(n);
+ global_variable_flag = false;
+ generate_property_declaration_flag = false;
+
+ if (proxy_flag) {
+ Printf(module_class_code, "\n }\n\n");
+ }
+
+ return ret;
+ }
+
+ String *getCurrentScopeName(String *nspace) {
+ String *scope = 0;
+ if (nspace || getCurrentClass()) {
+ scope = NewString("");
+ if (nspace)
+ Printf(scope, "%s", nspace);
+ if (Node *cls = getCurrentClass()) {
+ if (Node *outer = Getattr(cls, "nested:outer")) {
+ String *outerClassesPrefix = Copy(Getattr(outer, "sym:name"));
+ for (outer = Getattr(outer, "nested:outer"); outer != 0; outer = Getattr(outer, "nested:outer")) {
+ Push(outerClassesPrefix, ".");
+ Push(outerClassesPrefix, Getattr(outer, "sym:name"));
+ }
+ Printv(scope, nspace ? "." : "", outerClassesPrefix, ".", proxy_class_name, NIL);
+ Delete(outerClassesPrefix);
+ } else
+ Printv(scope, nspace ? "." : "", proxy_class_name, NIL);
+ }
+ }
+ return scope;
+ }
+
+ /* ----------------------------------------------------------------------
+ * enumDeclaration()
+ *
+ * C/C++ enums can be mapped in one of 4 ways, depending on the cs:enum feature specified:
+ * 1) Simple enums - simple constant within the proxy class or module class
+ * 2) Typeunsafe enums - simple constant in a C# class (class named after the c++ enum name)
+ * 3) Typesafe enum - typesafe enum pattern (class named after the c++ enum name)
+ * 4) Proper enums - proper C# enum
+ * Anonymous enums always default to 1)
+ * ---------------------------------------------------------------------- */
+
+ virtual int enumDeclaration(Node *n) {
+
+ if (!ImportMode) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ String *nspace = Getattr(n, "sym:nspace"); // NSpace/getNSpace() only works during Language::enumDeclaration call
+ if (proxy_flag && !is_wrapping_class()) {
+ // Global enums / enums in a namespace
+ assert(!full_imclass_name);
+
+ if (!nspace) {
+ full_imclass_name = NewStringf("%s", imclass_name);
+ } else {
+ if (namespce) {
+ full_imclass_name = NewStringf("%s.%s", namespce, imclass_name);
+ } else {
+ full_imclass_name = NewStringf("%s", imclass_name);
+ }
+ }
+ }
+
+ enum_code = NewString("");
+ String *symname = Getattr(n, "sym:name");
+ String *constants_code = (proxy_flag && is_wrapping_class())? proxy_class_constants_code : module_class_constants_code;
+ EnumFeature enum_feature = decodeEnumFeature(n);
+ String *typemap_lookup_type = Getattr(n, "name");
+
+ if ((enum_feature != SimpleEnum) && symname && typemap_lookup_type) {
+ // Wrap (non-anonymous) C/C++ enum within a typesafe, typeunsafe or proper C# enum
+
+ String *scope = getCurrentScopeName(nspace);
+ if (!addSymbol(symname, n, scope))
+ return SWIG_ERROR;
+
+ // Pure C# baseclass and interfaces
+ const String *pure_baseclass = typemapLookup(n, "csbase", typemap_lookup_type, WARN_NONE);
+ const String *pure_interfaces = typemapLookup(n, "csinterfaces", typemap_lookup_type, WARN_NONE);
+
+ // Class attributes
+ const String *csattributes = typemapLookup(n, "csattributes", typemap_lookup_type, WARN_NONE);
+ if (csattributes && *Char(csattributes))
+ Printf(enum_code, "%s\n", csattributes);
+
+ // Emit the enum
+ Printv(enum_code, typemapLookup(n, "csclassmodifiers", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CLASSMOD_UNDEF), // Class modifiers (enum modifiers really)
+ " ", symname, (*Char(pure_baseclass) || *Char(pure_interfaces)) ? " : " : "", pure_baseclass, ((*Char(pure_baseclass)) && *Char(pure_interfaces)) ? // Interfaces
+ ", " : "", pure_interfaces, " {\n", NIL);
+ Delete(scope);
+ } else {
+ // Wrap C++ enum with integers - just indicate start of enum with a comment, no comment for anonymous enums of any sort
+ if (symname && !Getattr(n, "unnamedinstance"))
+ Printf(constants_code, " // %s \n", symname);
+ }
+
+ // Emit each enum item
+ Language::enumDeclaration(n);
+
+ if ((enum_feature != SimpleEnum) && symname && typemap_lookup_type) {
+ // Wrap (non-anonymous) C/C++ enum within a typesafe, typeunsafe or proper C# enum
+ // Finish the enum declaration
+ // Typemaps are used to generate the enum definition in a similar manner to proxy classes.
+ Printv(enum_code, (enum_feature == ProperEnum) ? "\n" : typemapLookup(n, "csbody", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSBODY_UNDEF), // main body of class
+ typemapLookup(n, "cscode", typemap_lookup_type, WARN_NONE), // extra C# code
+ "}", NIL);
+
+ Replaceall(enum_code, "$csclassname", symname);
+
+ // Substitute $enumvalues - intended usage is for typesafe enums
+ if (Getattr(n, "enumvalues"))
+ Replaceall(enum_code, "$enumvalues", Getattr(n, "enumvalues"));
+ else
+ Replaceall(enum_code, "$enumvalues", "");
+
+ if (proxy_flag && is_wrapping_class()) {
+ // Enums defined within the C++ class are defined within the proxy class
+
+ // Add extra indentation
+ Replaceall(enum_code, "\n", "\n ");
+ Replaceall(enum_code, " \n", "\n");
+
+ Printv(proxy_class_constants_code, " ", enum_code, "\n\n", NIL);
+ } else {
+ // Global enums are defined in their own file
+ String *output_directory = outputDirectory(nspace);
+ File *f_enum = getOutputFile(output_directory, symname);
+
+ addOpenNamespace(nspace, f_enum);
+
+ Printv(f_enum, typemapLookup(n, "csimports", typemap_lookup_type, WARN_NONE), // Import statements
+ "\n", enum_code, "\n", NIL);
+
+ addCloseNamespace(nspace, f_enum);
+ if (f_enum != f_single_out)
+ Delete(f_enum);
+ f_enum = NULL;
+ Delete(output_directory);
+ }
+ } else {
+ // Wrap C++ enum with simple constant
+ Printf(enum_code, "\n");
+ if (proxy_flag && is_wrapping_class())
+ Printv(proxy_class_constants_code, enum_code, NIL);
+ else
+ Printv(module_class_constants_code, enum_code, NIL);
+ }
+
+ Delete(enum_code);
+ enum_code = NULL;
+
+ if (proxy_flag && !is_wrapping_class()) {
+ Delete(full_imclass_name);
+ full_imclass_name = 0;
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * enumvalueDeclaration()
+ * ---------------------------------------------------------------------- */
+
+ virtual int enumvalueDeclaration(Node *n) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ Swig_require("enumvalueDeclaration", n, "*name", "?value", NIL);
+ String *symname = Getattr(n, "sym:name");
+ String *value = Getattr(n, "value");
+ String *name = Getattr(n, "name");
+ Node *parent = parentNode(n);
+ int unnamedinstance = GetFlag(parent, "unnamedinstance");
+ String *parent_name = Getattr(parent, "name");
+ String *nspace = getNSpace();
+ String *newsymname = 0;
+ String *tmpValue;
+
+ // Strange hack from parent method
+ if (value)
+ tmpValue = NewString(value);
+ else
+ tmpValue = NewString(name);
+ // Note that this is used in enumValue() amongst other places
+ Setattr(n, "value", tmpValue);
+
+ // Deal with enum values that are not int
+ int swigtype = SwigType_type(Getattr(n, "type"));
+ if (swigtype == T_BOOL) {
+ const char *val = Equal(Getattr(n, "enumvalue"), "true") ? "1" : "0";
+ Setattr(n, "enumvalue", val);
+ } else if (swigtype == T_CHAR) {
+ String *val = NewStringf("'%(hexescape)s'", Getattr(n, "enumvalue"));
+ Setattr(n, "enumvalue", val);
+ Delete(val);
+ }
+
+ {
+ EnumFeature enum_feature = decodeEnumFeature(parent);
+
+ if ((enum_feature == SimpleEnum) && GetFlag(parent, "scopedenum")) {
+ newsymname = Swig_name_member(0, Getattr(parent, "sym:name"), symname);
+ symname = newsymname;
+ }
+
+ // Add to language symbol table
+ String *scope = 0;
+ if (unnamedinstance || !parent_name || enum_feature == SimpleEnum) {
+ String *enumClassPrefix = getEnumClassPrefix();
+ if (enumClassPrefix) {
+ scope = NewString("");
+ if (nspace)
+ Printf(scope, "%s.", nspace);
+ Printf(scope, "%s", enumClassPrefix);
+ } else {
+ scope = Copy(module_class_name);
+ }
+ } else {
+ scope = getCurrentScopeName(nspace);
+ if (!scope)
+ scope = Copy(Getattr(parent, "sym:name"));
+ else
+ Printf(scope, ".%s", Getattr(parent, "sym:name"));
+ }
+ if (!addSymbol(symname, n, scope))
+ return SWIG_ERROR;
+
+ const String *csattributes = Getattr(n, "feature:cs:attributes");
+
+ if ((enum_feature == ProperEnum) && parent_name && !unnamedinstance) {
+ // Wrap (non-anonymous) C/C++ enum with a proper C# enum
+ // Emit the enum item.
+ if (!GetFlag(n, "firstenumitem"))
+ Printf(enum_code, ",\n");
+
+ if (csattributes)
+ Printf(enum_code, " %s\n", csattributes);
+
+ Printf(enum_code, " %s", symname);
+
+ // Check for the %csconstvalue feature
+ String *value = Getattr(n, "feature:cs:constvalue");
+
+ // Note that the enum value must be a true constant and cannot be set from a PINVOKE call, thus no support for %csconst(0)
+ value = value ? value : Getattr(n, "enumvalue");
+ if (value) {
+ Printf(enum_code, " = %s", value);
+ }
+ } else {
+ // Wrap C/C++ enums with constant integers or use the typesafe enum pattern
+ SwigType *typemap_lookup_type = parent_name ? parent_name : NewString("enum ");
+ Setattr(n, "type", typemap_lookup_type);
+ const String *tm = typemapLookup(n, "cstype", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF);
+
+ String *return_type = Copy(tm);
+ substituteClassname(typemap_lookup_type, return_type);
+ const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+
+ if (csattributes)
+ Printf(enum_code, " %s\n", csattributes);
+
+ if ((enum_feature == TypesafeEnum) && parent_name && !unnamedinstance) {
+ // Wrap (non-anonymous) enum using the typesafe enum pattern
+ if (Getattr(n, "enumvalue")) {
+ String *value = enumValue(n);
+ Printf(enum_code, " %s static readonly %s %s = new %s(\"%s\", %s);\n", methodmods, return_type, symname, return_type, symname, value);
+ Delete(value);
+ } else {
+ Printf(enum_code, " %s static readonly %s %s = new %s(\"%s\");\n", methodmods, return_type, symname, return_type, symname);
+ }
+ } else {
+ // Simple integer constants
+ // Note these are always generated for anonymous enums, no matter what enum_feature is specified
+ // Code generated is the same for SimpleEnum and TypeunsafeEnum -> the class it is generated into is determined later
+
+ // The %csconst feature determines how the constant value is obtained
+ int const_feature_flag = GetFlag(n, "feature:cs:const");
+
+ const char *const_readonly = const_feature_flag ? "const" : "static readonly";
+ String *value = enumValue(n);
+ Printf(enum_code, " %s %s %s %s = %s;\n", methodmods, const_readonly, return_type, symname, value);
+ Delete(value);
+ }
+ Delete(return_type);
+ }
+
+ // Add the enum value to the comma separated list being constructed in the enum declaration.
+ String *enumvalues = Getattr(parent, "enumvalues");
+ if (!enumvalues)
+ Setattr(parent, "enumvalues", Copy(symname));
+ else
+ Printv(enumvalues, ", ", symname, NIL);
+ Delete(scope);
+ }
+
+ Delete(newsymname);
+ Delete(tmpValue);
+ Swig_restore(n);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * constantWrapper()
+ * Used for wrapping constants - #define or %constant.
+ * Also for inline initialised const static primitive type member variables (short, int, double, enums etc).
+ * C# static const variables are generated for these.
+ * If the %csconst(1) feature is used then the C constant value is used to initialise the C# const variable.
+ * If not, a PINVOKE method is generated to get the C constant value for initialisation of the C# const variable.
+ * However, if the %csconstvalue feature is used, it overrides all other ways to generate the initialisation.
+ * Also note that this method might be called for wrapping enum items (when the enum is using %csconst(0)).
+ * ------------------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+ SwigType *valuetype = Getattr(n, "valuetype");
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ String *return_type = NewString("");
+ String *constants_code = NewString("");
+ Swig_save("constantWrapper", n, "value", NIL);
+ Swig_save("constantWrapper", n, "tmap:ctype:out", "tmap:imtype:out", "tmap:cstype:out", "tmap:out:null", "tmap:imtype:outattributes", "tmap:cstype:outattributes", NIL);
+
+ bool is_enum_item = (Cmp(nodeType(n), "enumitem") == 0);
+
+ const String *itemname = (proxy_flag && wrapping_member_flag) ? variable_name : symname;
+ if (!is_enum_item) {
+ String *scope = 0;
+ if (proxy_class_name) {
+ String *nspace = getNSpace();
+ scope = NewString("");
+ if (nspace)
+ Printf(scope, "%s.", nspace);
+ Printf(scope, "%s", proxy_class_name);
+ } else {
+ scope = Copy(module_class_name);
+ }
+ if (!addSymbol(itemname, n, scope))
+ return SWIG_ERROR;
+ Delete(scope);
+ }
+
+ // The %csconst feature determines how the constant value is obtained
+ int const_feature_flag = GetFlag(n, "feature:cs:const");
+
+ /* Adjust the enum type for the Swig_typemap_lookup.
+ * We want the same jstype typemap for all the enum items so we use the enum type (parent node). */
+ if (is_enum_item) {
+ t = Getattr(parentNode(n), "enumtype");
+ Setattr(n, "type", t);
+ }
+
+ /* Attach the non-standard typemaps to the parameter list. */
+ Swig_typemap_attach_parms("cstype", l, NULL);
+
+ /* Get C# return types */
+ bool classname_substituted_flag = false;
+
+ if ((tm = Swig_typemap_lookup("cstype", n, "", 0))) {
+ String *cstypeout = Getattr(n, "tmap:cstype:out"); // the type in the cstype typemap's out attribute overrides the type in the typemap
+ if (cstypeout)
+ tm = cstypeout;
+ classname_substituted_flag = substituteClassname(t, tm);
+ Printf(return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ // Default (octal) escaping is no good - change to hex escaped value
+ String *hexescaped_value = Getattr(n, "rawvalue") ? NewStringf("%(hexescape)s", Getattr(n, "rawvalue")) : 0;
+ // Add the stripped quotes back in
+ String *new_value = NewString("");
+ if (SwigType_type(t) == T_STRING) {
+ Printf(new_value, "\"%s\"", hexescaped_value ? hexescaped_value : Copy(Getattr(n, "value")));
+ Setattr(n, "value", new_value);
+ } else if (SwigType_type(t) == T_CHAR) {
+ Printf(new_value, "\'%s\'", hexescaped_value ? hexescaped_value : Copy(Getattr(n, "value")));
+ Setattr(n, "value", new_value);
+ }
+
+ const String *outattributes = Getattr(n, "tmap:cstype:outattributes");
+ if (outattributes)
+ Printf(constants_code, " %s\n", outattributes);
+
+ const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+
+ Printf(constants_code, " %s %s %s %s = ", methodmods, (const_feature_flag ? "const" : "static readonly"), return_type, itemname);
+
+ // Check for the %csconstvalue feature
+ String *value = Getattr(n, "feature:cs:constvalue");
+
+ if (value) {
+ Printf(constants_code, "%s;\n", value);
+ } else if (!const_feature_flag) {
+ // Default enum and constant handling will work with any type of C constant and initialises the C# variable from C through a PINVOKE call.
+
+ if (classname_substituted_flag) {
+ if (SwigType_isenum(t)) {
+ // This handles wrapping of inline initialised const enum static member variables (not when wrapping enum items - ignored later on)
+ Printf(constants_code, "(%s)%s.%s();\n", return_type, full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
+ } else {
+ // This handles function pointers using the %constant directive
+ Printf(constants_code, "new %s(%s.%s(), false);\n", return_type, full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
+ }
+ } else {
+ Printf(constants_code, "%s.%s();\n", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
+ }
+
+ // Each constant and enum value is wrapped with a separate PInvoke function call
+ SetFlag(n, "feature:immutable");
+ enum_constant_flag = true;
+ variableWrapper(n);
+ enum_constant_flag = false;
+ } else {
+ // Alternative constant handling will use the C syntax to make a true C# constant and hope that it compiles as C# code
+ if (Getattr(n, "wrappedasconstant")) {
+ if (SwigType_type(t) == T_CHAR) {
+ if (SwigType_type(valuetype) == T_CHAR)
+ Printf(constants_code, "\'%(hexescape)s\';\n", Getattr(n, "staticmembervariableHandler:value"));
+ else
+ Printf(constants_code, "(char)%s;\n", Getattr(n, "staticmembervariableHandler:value"));
+ } else {
+ Printf(constants_code, "%s;\n", Getattr(n, "staticmembervariableHandler:value"));
+ }
+ } else {
+ Printf(constants_code, "%s;\n", Getattr(n, "value"));
+ }
+ }
+
+ // Emit the generated code to appropriate place
+ // Enums only emit the intermediate and PINVOKE methods, so no proxy or module class wrapper methods needed
+ if (!is_enum_item) {
+ if (proxy_flag && wrapping_member_flag)
+ Printv(proxy_class_constants_code, constants_code, NIL);
+ else
+ Printv(module_class_constants_code, constants_code, NIL);
+ }
+ // Cleanup
+ Swig_restore(n);
+ Delete(new_value);
+ Delete(return_type);
+ Delete(constants_code);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * insertDirective()
+ * ----------------------------------------------------------------------------- */
+
+ virtual int insertDirective(Node *n) {
+ int ret = SWIG_OK;
+ String *code = Getattr(n, "code");
+ String *section = Getattr(n, "section");
+ Replaceall(code, "$module", module_class_name);
+ Replaceall(code, "$imclassname", imclass_name);
+ Replaceall(code, "$dllimport", dllimport);
+
+ if (!ImportMode && (Cmp(section, "proxycode") == 0)) {
+ if (proxy_class_code) {
+ Swig_typemap_replace_embedded_typemap(code, n);
+ int offset = Len(code) > 0 && *Char(code) == '\n' ? 1 : 0;
+ Printv(proxy_class_code, Char(code) + offset, "\n", NIL);
+ }
+ } else {
+ ret = Language::insertDirective(n);
+ }
+ return ret;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * pragmaDirective()
+ *
+ * Valid Pragmas:
+ * imclassbase - base (extends) for the intermediary class
+ * imclassclassmodifiers - class modifiers for the intermediary class
+ * imclasscode - text (C# code) is copied verbatim to the intermediary class
+ * imclassimports - import statements for the intermediary class
+ * imclassinterfaces - interface (implements) for the intermediary class
+ *
+ * modulebase - base (extends) for the module class
+ * moduleclassmodifiers - class modifiers for the module class
+ * modulecode - text (C# code) is copied verbatim to the module class
+ * moduleimports - import statements for the module class
+ * moduleinterfaces - interface (implements) for the module class
+ *
+ * ----------------------------------------------------------------------------- */
+
+ virtual int pragmaDirective(Node *n) {
+ if (!ImportMode) {
+ String *lang = Getattr(n, "lang");
+ String *code = Getattr(n, "name");
+ String *value = Getattr(n, "value");
+
+ if (Strcmp(lang, "csharp") == 0) {
+
+ String *strvalue = NewString(value);
+ Replaceall(strvalue, "\\\"", "\"");
+
+ if (Strcmp(code, "imclassbase") == 0) {
+ Delete(imclass_baseclass);
+ imclass_baseclass = Copy(strvalue);
+ } else if (Strcmp(code, "imclassclassmodifiers") == 0) {
+ Delete(imclass_class_modifiers);
+ imclass_class_modifiers = Copy(strvalue);
+ } else if (Strcmp(code, "imclasscode") == 0) {
+ Printf(imclass_class_code, "%s\n", strvalue);
+ } else if (Strcmp(code, "imclassimports") == 0) {
+ Delete(imclass_imports);
+ imclass_imports = Copy(strvalue);
+ } else if (Strcmp(code, "imclassinterfaces") == 0) {
+ Delete(imclass_interfaces);
+ imclass_interfaces = Copy(strvalue);
+ } else if (Strcmp(code, "modulebase") == 0) {
+ Delete(module_baseclass);
+ module_baseclass = Copy(strvalue);
+ } else if (Strcmp(code, "moduleclassmodifiers") == 0) {
+ Delete(module_class_modifiers);
+ module_class_modifiers = Copy(strvalue);
+ } else if (Strcmp(code, "modulecode") == 0) {
+ Printf(module_class_code, "%s\n", strvalue);
+ } else if (Strcmp(code, "moduleimports") == 0) {
+ Delete(module_imports);
+ module_imports = Copy(strvalue);
+ } else if (Strcmp(code, "moduleinterfaces") == 0) {
+ Delete(module_interfaces);
+ module_interfaces = Copy(strvalue);
+ } else {
+ Swig_error(input_file, line_number, "Unrecognized pragma.\n");
+ }
+ Delete(strvalue);
+ }
+ }
+ return Language::pragmaDirective(n);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getQualifiedInterfaceName()
+ * ----------------------------------------------------------------------------- */
+
+ String *getQualifiedInterfaceName(Node *n) {
+ String *ret = Getattr(n, "interface:qname");
+ if (!ret) {
+ String *nspace = Getattr(n, "sym:nspace");
+ String *interface_name = Getattr(n, "interface:name");
+ if (nspace) {
+ if (namespce)
+ ret = NewStringf("%s.%s.%s", namespce, nspace, interface_name);
+ else
+ ret = NewStringf("%s.%s", nspace, interface_name);
+ } else {
+ ret = Copy(interface_name);
+ }
+ Setattr(n, "interface:qname", ret);
+ }
+ return ret;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getInterfaceName()
+ * ----------------------------------------------------------------------------- */
+
+ String *getInterfaceName(SwigType *t, bool qualified) {
+ String *interface_name = NULL;
+ if (proxy_flag) {
+ Node *n = classLookup(t);
+ if (n && Getattr(n, "interface:name"))
+ interface_name = qualified ? getQualifiedInterfaceName(n) : Getattr(n, "interface:name");
+ }
+ return interface_name;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * addInterfaceNameAndUpcasts()
+ * ----------------------------------------------------------------------------- */
+
+ void addInterfaceNameAndUpcasts(SwigType *smart, String *interface_list, String *interface_upcasts, List *base_list, SwigType *c_classname) {
+ for (Iterator it = First(base_list); it.item; it = Next(it)) {
+ Node *base = it.item;
+ SwigType *c_baseclassname = Getattr(base, "name");
+ String *interface_name = Getattr(base, "interface:name");
+ if (Len(interface_list))
+ Append(interface_list, ", ");
+ Append(interface_list, interface_name);
+
+ Node *attributes = NewHash();
+ String *interface_code = Copy(typemapLookup(base, "csinterfacecode", Getattr(base, "classtypeobj"), WARN_CSHARP_TYPEMAP_INTERFACECODE_UNDEF, attributes));
+ String *cptr_method_name = 0;
+ if (interface_code) {
+ Replaceall(interface_code, "$interfacename", interface_name);
+ Printv(interface_upcasts, interface_code, NIL);
+ cptr_method_name = Copy(Getattr(attributes, "tmap:csinterfacecode:cptrmethod"));
+ }
+ if (!cptr_method_name)
+ cptr_method_name = NewStringf("%s_GetInterfaceCPtr", interface_name);
+ Replaceall(cptr_method_name, ".", "_");
+ Replaceall(cptr_method_name, "$interfacename", interface_name);
+
+ String *upcast_method_name = Swig_name_member(getNSpace(), getClassPrefix(), cptr_method_name);
+ upcastsCode(smart, upcast_method_name, c_classname, c_baseclassname);
+
+ Delete(upcast_method_name);
+ Delete(cptr_method_name);
+ Delete(interface_code);
+ }
+ }
+
+ /* -----------------------------------------------------------------------------
+ * upcastsCode()
+ *
+ * Add code for C++ casting to base class
+ * ----------------------------------------------------------------------------- */
+
+ void upcastsCode(SwigType *smart, String *upcast_method_name, SwigType *c_classname, SwigType *c_baseclassname) {
+ String *wname = Swig_name_wrapper(upcast_method_name);
+
+ Printv(imclass_cppcasts_code, "\n [global::System.Runtime.InteropServices.DllImport(\"", dllimport, "\", EntryPoint=\"", wname, "\")]\n", NIL);
+ Printf(imclass_cppcasts_code, " public static extern global::System.IntPtr %s(global::System.IntPtr jarg1);\n", upcast_method_name);
+
+ Replaceall(imclass_cppcasts_code, "$csclassname", proxy_class_name);
+
+ String *classname = SwigType_namestr(c_classname);
+ String *baseclassname = SwigType_namestr(c_baseclassname);
+ if (smart) {
+ String *smartnamestr = SwigType_namestr(smart);
+ String *bsmartnamestr = SwigType_namestr(smart);
+
+ // TODO: SwigType_typedef_resolve_all on a String instead of SwigType is incorrect for templates
+ SwigType *rclassname = SwigType_typedef_resolve_all(classname);
+ SwigType *rbaseclassname = SwigType_typedef_resolve_all(baseclassname);
+ Replaceall(bsmartnamestr, rclassname, rbaseclassname);
+
+ Printv(upcasts_code,
+ "SWIGEXPORT ", bsmartnamestr, " * SWIGSTDCALL ", wname, "(", smartnamestr, " *jarg1) {\n",
+ " return jarg1 ? new ", bsmartnamestr, "(*jarg1) : 0;\n"
+ "}\n", "\n", NIL);
+
+ Delete(rbaseclassname);
+ Delete(rclassname);
+ Delete(bsmartnamestr);
+ Delete(smartnamestr);
+ } else {
+ Printv(upcasts_code,
+ "SWIGEXPORT ", baseclassname, " * SWIGSTDCALL ", wname, "(", classname, " *jarg1) {\n",
+ " return (", baseclassname, " *)jarg1;\n"
+ "}\n", "\n", NIL);
+ }
+
+ Delete(baseclassname);
+ Delete(classname);
+ Delete(wname);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * emitProxyClassDefAndCPPCasts()
+ * ----------------------------------------------------------------------------- */
+
+ void emitProxyClassDefAndCPPCasts(Node *n) {
+ SwigType *c_classname = Getattr(n, "name");
+ SwigType *c_baseclassname = NULL;
+ String *baseclass = NULL;
+ String *interface_list = NewStringEmpty();
+ String *interface_upcasts = NewStringEmpty();
+ SwigType *typemap_lookup_type = Getattr(n, "classtypeobj");
+ bool feature_director = Swig_directorclass(n) ? true : false;
+ bool has_outerclass = Getattr(n, "nested:outer") != 0 && !GetFlag(n, "feature:flatnested");
+ SwigType *smart = Swig_cparse_smartptr(n);
+
+ // Inheritance from pure C# classes
+ Node *attributes = NewHash();
+ const String *pure_baseclass = typemapLookup(n, "csbase", typemap_lookup_type, WARN_NONE, attributes);
+ bool purebase_replace = GetFlag(attributes, "tmap:csbase:replace") ? true : false;
+ bool purebase_notderived = GetFlag(attributes, "tmap:csbase:notderived") ? true : false;
+ Delete(attributes);
+
+ // C++ inheritance
+ if (!purebase_replace) {
+ List *baselist = Getattr(n, "bases");
+ if (baselist) {
+ Iterator base = First(baselist);
+ while (base.item) {
+ if (!(GetFlag(base.item, "feature:ignore") || GetFlag(base.item, "feature:interface"))) {
+ SwigType *baseclassname = Getattr(base.item, "name");
+ if (!c_baseclassname) {
+ String *name = getProxyName(baseclassname);
+ if (name) {
+ c_baseclassname = baseclassname;
+ baseclass = name;
+ }
+ } else {
+ /* Warn about multiple inheritance for additional base class(es) */
+ String *proxyclassname = Getattr(n, "classtypeobj");
+ Swig_warning(WARN_CSHARP_MULTIPLE_INHERITANCE, Getfile(n), Getline(n),
+ "Warning for %s, base %s ignored. Multiple inheritance is not supported in C#.\n", SwigType_namestr(proxyclassname), SwigType_namestr(baseclassname));
+ }
+ }
+ base = Next(base);
+ }
+ }
+ }
+ List *interface_bases = Getattr(n, "interface:bases");
+ if (interface_bases)
+ addInterfaceNameAndUpcasts(smart, interface_list, interface_upcasts, interface_bases, c_classname);
+
+ bool derived = baseclass != 0;
+ if (derived && purebase_notderived)
+ pure_baseclass = empty_string;
+ const String *wanted_base = baseclass ? baseclass : pure_baseclass;
+
+ if (purebase_replace) {
+ wanted_base = pure_baseclass;
+ derived = false;
+ baseclass = NULL;
+ if (purebase_notderived)
+ Swig_error(Getfile(n), Getline(n), "The csbase typemap for proxy %s must contain just one of the 'replace' or 'notderived' attributes.\n", typemap_lookup_type);
+ } else if (Len(pure_baseclass) > 0 && Len(baseclass) > 0) {
+ Swig_warning(WARN_CSHARP_MULTIPLE_INHERITANCE, Getfile(n), Getline(n),
+ "Warning for %s, base %s ignored. Multiple inheritance is not supported in C#. "
+ "Perhaps you need one of the 'replace' or 'notderived' attributes in the csbase typemap?\n", typemap_lookup_type, pure_baseclass);
+ }
+
+ // Pure C# interfaces
+ const String *pure_interfaces = typemapLookup(n, derived ? "csinterfaces_derived" : "csinterfaces", typemap_lookup_type, WARN_NONE);
+ if (*Char(interface_list) && *Char(pure_interfaces))
+ Append(interface_list, ", ");
+ Append(interface_list, pure_interfaces);
+ // Start writing the proxy class
+ if (!has_outerclass)
+ Printv(proxy_class_def, typemapLookup(n, "csimports", typemap_lookup_type, WARN_NONE), // Import statements
+ "\n", NIL);
+
+ // Class attributes
+ const String *csattributes = typemapLookup(n, "csattributes", typemap_lookup_type, WARN_NONE);
+ if (csattributes && *Char(csattributes))
+ Printf(proxy_class_def, "%s\n", csattributes);
+
+ Printv(proxy_class_def, typemapLookup(n, "csclassmodifiers", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CLASSMOD_UNDEF), // Class modifiers
+ " $csclassname", // Class name and base class
+ (*Char(wanted_base) || *Char(interface_list)) ? " : " : "", wanted_base, (*Char(wanted_base) && *Char(interface_list)) ? // Interfaces
+ ", " : "", interface_list, " {", derived ? typemapLookup(n, "csbody_derived", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSBODY_UNDEF) : // main body of class
+ typemapLookup(n, "csbody", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSBODY_UNDEF), // main body of class
+ NIL);
+
+ // C++ destructor is wrapped by the Finalize and Dispose methods
+
+ const char *tmap_method = derived ? "csdestruct_derived" : "csdestruct";
+ const String *tm = typemapExists(n, tmap_method, typemap_lookup_type);
+ if (tm) {
+ Swig_error(Getfile(tm), Getline(tm),
+ "A deprecated %s typemap was found for %s, please remove it and replace all csdestruct, csdestruct_derived and csfinalize typemaps by the csdispose, csdispose_derived, csdisposing and csdisposing_derived typemaps.\n",
+ tmap_method, proxy_class_name);
+ }
+ tmap_method = "csfinalize";
+ tm = typemapExists(n, tmap_method, typemap_lookup_type);
+ if (tm) {
+ Swig_error(Getfile(tm), Getline(tm),
+ "A deprecated %s typemap was found for %s, please remove it and replace all csdestruct, csdestruct_derived and csfinalize typemaps by the csdispose, csdispose_derived, csdisposing and csdisposing_derived typemaps.\n",
+ tmap_method, proxy_class_name);
+ }
+
+ tmap_method = derived ? "csdisposing_derived" : "csdisposing";
+ String *destruct = NewString("");
+ attributes = NewHash();
+ const String *destruct_methodname = NULL;
+ const String *destruct_methodmodifiers = NULL;
+ const String *destruct_parameters = NULL;
+ if (derived) {
+ tm = typemapLookup(n, "csdisposing_derived", typemap_lookup_type, WARN_NONE, attributes);
+ destruct_methodname = Getattr(attributes, "tmap:csdisposing_derived:methodname");
+ destruct_methodmodifiers = Getattr(attributes, "tmap:csdisposing_derived:methodmodifiers");
+ destruct_parameters = Getattr(attributes, "tmap:csdisposing_derived:parameters");
+ } else {
+ tm = typemapLookup(n, "csdisposing", typemap_lookup_type, WARN_NONE, attributes);
+ destruct_methodname = Getattr(attributes, "tmap:csdisposing:methodname");
+ destruct_methodmodifiers = Getattr(attributes, "tmap:csdisposing:methodmodifiers");
+ destruct_parameters = Getattr(attributes, "tmap:csdisposing:parameters");
+ }
+ if (tm && *Char(tm)) {
+ if (!destruct_methodname) {
+ Swig_error(Getfile(n), Getline(n), "No methodname attribute defined in %s typemap for %s\n", tmap_method, proxy_class_name);
+ }
+ if (!destruct_methodmodifiers) {
+ Swig_error(Getfile(n), Getline(n),
+ "No methodmodifiers attribute defined in %s typemap for %s.\n", tmap_method, proxy_class_name);
+ }
+ if (!destruct_parameters)
+ destruct_parameters = empty_string;
+ }
+ // Emit the Finalize and Dispose methods
+ if (tm) {
+ // Finalize and Dispose methods
+ Printv(proxy_class_def, typemapLookup(n, derived ? "csdispose_derived" : "csdispose", typemap_lookup_type, WARN_NONE), NIL);
+ // Dispose(bool disposing) method
+ Printv(destruct, tm, NIL);
+ if (*Char(destructor_call))
+ Replaceall(destruct, "$imcall", destructor_call);
+ else
+ Replaceall(destruct, "$imcall", "throw new global::System.MethodAccessException(\"C++ destructor does not have public access\")");
+ if (*Char(destruct)) {
+ Printv(proxy_class_def, "\n ", NIL);
+ const String *methodmods = Getattr(n, "destructmethodmodifiers");
+ if (methodmods)
+ Printv(proxy_class_def, methodmods, NIL);
+ else
+ Printv(proxy_class_def, destruct_methodmodifiers, " ", derived ? "override" : "virtual", NIL);
+ Printv(proxy_class_def, " void ", destruct_methodname, "(", destruct_parameters, ") ", destruct, "\n", NIL);
+ }
+ }
+ if (*Char(interface_upcasts))
+ Printv(proxy_class_def, interface_upcasts, NIL);
+
+ if (feature_director) {
+ // Generate director connect method
+ // put this in classDirectorEnd ???
+ Printf(proxy_class_code, " private void SwigDirectorConnect() {\n");
+
+ int i;
+ for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *method = Getattr(udata, "method");
+ String *methid = Getattr(udata, "class_methodidx");
+ String *overname = Getattr(udata, "overname");
+ Printf(proxy_class_code, " if (SwigDerivedClassHasMethod(\"%s\", swigMethodTypes%s))\n", method, methid);
+ Printf(proxy_class_code, " swigDelegate%s = new SwigDelegate%s_%s(SwigDirectorMethod%s);\n", methid, proxy_class_name, methid, overname);
+ }
+ String *director_connect_method_name = Swig_name_member(getNSpace(), getClassPrefix(), "director_connect");
+ Printf(proxy_class_code, " %s.%s(swigCPtr", imclass_name, director_connect_method_name);
+ for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *methid = Getattr(udata, "class_methodidx");
+ Printf(proxy_class_code, ", swigDelegate%s", methid);
+ }
+ Printf(proxy_class_code, ");\n");
+ Printf(proxy_class_code, " }\n");
+
+ if (first_class_dmethod < curr_class_dmethod) {
+ // Only emit if there is at least one director method
+ Printf(proxy_class_code, "\n");
+ Printf(proxy_class_code, " private bool SwigDerivedClassHasMethod(string methodName, global::System.Type[] methodTypes) {\n");
+ Printf(proxy_class_code, " global::System.Reflection.MethodInfo[] methodInfos = this.GetType().GetMethods(\n");
+ Printf(proxy_class_code, " global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance);\n");
+ Printf(proxy_class_code, " foreach (global::System.Reflection.MethodInfo methodInfo in methodInfos) {\n");
+ Printf(proxy_class_code, " if (methodInfo.DeclaringType == null)\n");
+ Printf(proxy_class_code, " continue;\n\n");
+ Printf(proxy_class_code, " if (methodInfo.Name != methodName)\n");
+ Printf(proxy_class_code, " continue;\n\n");
+ Printf(proxy_class_code, " var parameters = methodInfo.GetParameters();\n");
+ Printf(proxy_class_code, " if (parameters.Length != methodTypes.Length)\n");
+ Printf(proxy_class_code, " continue;\n\n");
+ Printf(proxy_class_code, " bool parametersMatch = true;\n");
+ Printf(proxy_class_code, " for (var i = 0; i < parameters.Length; i++) {\n");
+ Printf(proxy_class_code, " if (parameters[i].ParameterType != methodTypes[i]) {\n");
+ Printf(proxy_class_code, " parametersMatch = false;\n");
+ Printf(proxy_class_code, " break;\n");
+ Printf(proxy_class_code, " }\n");
+ Printf(proxy_class_code, " }\n\n");
+ Printf(proxy_class_code, " if (!parametersMatch)\n");
+ Printf(proxy_class_code, " continue;\n\n");
+ Printf(proxy_class_code, " if (methodInfo.IsVirtual && (methodInfo.DeclaringType.IsSubclassOf(typeof(%s))) &&\n", proxy_class_name);
+ Printf(proxy_class_code, " methodInfo.DeclaringType != methodInfo.GetBaseDefinition().DeclaringType) {\n");
+ Printf(proxy_class_code, " return true;\n");
+ Printf(proxy_class_code, " }\n");
+ Printf(proxy_class_code, " }\n\n");
+ Printf(proxy_class_code, " return false;\n");
+
+ /* Could add this code to cover corner case where the GetMethod() returns a method which allows type
+ * promotion, eg it will return foo(double), if looking for foo(int).
+ if (hasDerivedMethod) {
+ hasDerivedMethod = false;
+ if (methodInfo != null)
+ {
+ hasDerivedMethod = true;
+ ParameterInfo[] parameterArray1 = methodInfo.GetParameters();
+ for (int i=0; i<methodTypes.Length; i++)
+ {
+ if (parameterArray1[0].ParameterType != methodTypes[0])
+ {
+ hasDerivedMethod = false;
+ break;
+ }
+ }
+ }
+ }
+ */
+ //Printf(proxy_class_code, " return hasDerivedMethod;\n");
+ Printf(proxy_class_code, " }\n");
+ }
+
+ if (Len(director_delegate_callback) > 0)
+ Printv(proxy_class_code, director_delegate_callback, NIL);
+ if (Len(director_delegate_definitions) > 0)
+ Printv(proxy_class_code, "\n", director_delegate_definitions, NIL);
+ if (Len(director_delegate_instances) > 0)
+ Printv(proxy_class_code, "\n", director_delegate_instances, NIL);
+ if (Len(director_method_types) > 0)
+ Printv(proxy_class_code, "\n", director_method_types, NIL);
+
+ Delete(director_callback_typedefs);
+ director_callback_typedefs = NULL;
+ Delete(director_callbacks);
+ director_callbacks = NULL;
+ Delete(director_delegate_callback);
+ director_delegate_callback = NULL;
+ Delete(director_delegate_definitions);
+ director_delegate_definitions = NULL;
+ Delete(director_delegate_instances);
+ director_delegate_instances = NULL;
+ Delete(director_method_types);
+ director_method_types = NULL;
+ Delete(director_connect_parms);
+ director_connect_parms = NULL;
+ Delete(director_connect_method_name);
+ }
+
+ Delete(interface_upcasts);
+ Delete(interface_list);
+ Delete(attributes);
+ Delete(destruct);
+
+ // Emit extra user code
+ Printv(proxy_class_def, typemapLookup(n, "cscode", typemap_lookup_type, WARN_NONE), // extra C# code
+ "\n", NIL);
+
+ if (derived) {
+ String *upcast_method_name = Swig_name_member(getNSpace(), getClassPrefix(), smart != 0 ? "SWIGSmartPtrUpcast" : "SWIGUpcast");
+ upcastsCode(smart, upcast_method_name, c_classname, c_baseclassname);
+ Delete(upcast_method_name);
+ }
+
+ Delete(smart);
+ }
+
+ /* ----------------------------------------------------------------------
+ * emitInterfaceDeclaration()
+ * ---------------------------------------------------------------------- */
+
+ void emitInterfaceDeclaration(Node *n, String *interface_name, File *f_interface) {
+ Printv(f_interface, typemapLookup(n, "csimports", Getattr(n, "classtypeobj"), WARN_NONE), "\n", NIL);
+ Printv(f_interface, typemapLookup(n, "csinterfacemodifiers", Getattr(n, "classtypeobj"), WARN_CSHARP_TYPEMAP_INTERFACEMODIFIERS_UNDEF), NIL);
+ Printf(f_interface, " %s", interface_name);
+ if (List *baselist = Getattr(n, "bases")) {
+ String *bases = 0;
+ for (Iterator base = First(baselist); base.item; base = Next(base)) {
+ if (GetFlag(base.item, "feature:ignore") || !GetFlag(base.item, "feature:interface"))
+ continue; // TODO: warn about skipped non-interface bases
+ String *base_iname = Getattr(base.item, "interface:name");
+ if (!bases)
+ bases = NewStringf(" : %s", base_iname);
+ else {
+ Append(bases, ", ");
+ Append(bases, base_iname);
+ }
+ }
+ if (bases) {
+ Printv(f_interface, bases, NIL);
+ Delete(bases);
+ }
+ }
+ Printf(f_interface, " {\n");
+
+ Node *attributes = NewHash();
+ String *interface_code = Copy(typemapLookup(n, "csinterfacecode", Getattr(n, "classtypeobj"), WARN_CSHARP_TYPEMAP_INTERFACECODE_UNDEF, attributes));
+ if (interface_code) {
+ String *interface_declaration = Copy(Getattr(attributes, "tmap:csinterfacecode:declaration"));
+ if (interface_declaration) {
+ Replaceall(interface_declaration, "$interfacename", interface_name);
+ Printv(f_interface, interface_declaration, NIL);
+ Delete(interface_declaration);
+ }
+ Delete(interface_code);
+ }
+ }
+
+ /* ----------------------------------------------------------------------
+ * classHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int classHandler(Node *n) {
+ String *nspace = getNSpace();
+ File *f_proxy = NULL;
+ File *f_interface = NULL;
+ // save class local variables
+ String *old_proxy_class_name = proxy_class_name;
+ String *old_full_imclass_name = full_imclass_name;
+ String *old_destructor_call = destructor_call;
+ String *old_proxy_class_constants_code = proxy_class_constants_code;
+ String *old_proxy_class_def = proxy_class_def;
+ String *old_proxy_class_code = proxy_class_code;
+ bool has_outerclass = Getattr(n, "nested:outer") && !GetFlag(n, "feature:flatnested");
+ String *old_interface_class_code = interface_class_code;
+ interface_class_code = 0;
+
+ if (proxy_flag) {
+ proxy_class_name = NewString(Getattr(n, "sym:name"));
+ String *interface_name = GetFlag(n, "feature:interface") ? Getattr(n, "interface:name") : 0;
+ if (Node *outer = Getattr(n, "nested:outer")) {
+ String *outerClassesPrefix = Copy(Getattr(outer, "sym:name"));
+ for (outer = Getattr(outer, "nested:outer"); outer != 0; outer = Getattr(outer, "nested:outer")) {
+ Push(outerClassesPrefix, ".");
+ Push(outerClassesPrefix, Getattr(outer, "sym:name"));
+ }
+ String *fnspace = nspace ? NewStringf("%s.%s", nspace, outerClassesPrefix) : outerClassesPrefix;
+ if (!addSymbol(proxy_class_name, n, fnspace))
+ return SWIG_ERROR;
+ if (interface_name && !addInterfaceSymbol(interface_name, n, fnspace))
+ return SWIG_ERROR;
+ if (nspace)
+ Delete(fnspace);
+ Delete(outerClassesPrefix);
+ } else {
+ if (!addSymbol(proxy_class_name, n, nspace))
+ return SWIG_ERROR;
+ if (interface_name && !addInterfaceSymbol(interface_name, n, nspace))
+ return SWIG_ERROR;
+ }
+
+ if (!nspace) {
+ full_imclass_name = NewStringf("%s", imclass_name);
+ if (Cmp(proxy_class_name, imclass_name) == 0) {
+ Printf(stderr, "Class name cannot be equal to intermediary class name: %s\n", proxy_class_name);
+ Exit(EXIT_FAILURE);
+ }
+
+ if (Cmp(proxy_class_name, module_class_name) == 0) {
+ Printf(stderr, "Class name cannot be equal to module class name: %s\n", proxy_class_name);
+ Exit(EXIT_FAILURE);
+ }
+ } else {
+ if (namespce) {
+ full_imclass_name = NewStringf("%s.%s", namespce, imclass_name);
+ } else {
+ full_imclass_name = NewStringf("%s", imclass_name);
+ }
+ }
+
+ if (!has_outerclass) {
+ String *output_directory = outputDirectory(nspace);
+ f_proxy = getOutputFile(output_directory, proxy_class_name);
+
+ addOpenNamespace(nspace, f_proxy);
+ Delete(output_directory);
+ }
+ else
+ ++nesting_depth;
+
+ proxy_class_def = NewString("");
+ proxy_class_code = NewString("");
+ destructor_call = NewString("");
+ proxy_class_constants_code = NewString("");
+
+ if (GetFlag(n, "feature:interface")) {
+ interface_class_code = NewString("");
+ String *output_directory = outputDirectory(nspace);
+ f_interface = getOutputFile(output_directory, interface_name);
+ addOpenNamespace(nspace, f_interface);
+ emitInterfaceDeclaration(n, interface_name, interface_class_code);
+ Delete(output_directory);
+ }
+ }
+
+ Language::classHandler(n);
+
+ if (proxy_flag) {
+
+ emitProxyClassDefAndCPPCasts(n);
+
+ String *csclazzname = Swig_name_member(getNSpace(), getClassPrefix(), ""); // mangled full proxy class name
+
+ Replaceall(proxy_class_def, "$csclassname", proxy_class_name);
+ Replaceall(proxy_class_code, "$csclassname", proxy_class_name);
+ Replaceall(proxy_class_constants_code, "$csclassname", proxy_class_name);
+ Replaceall(interface_class_code, "$csclassname", proxy_class_name);
+
+ Replaceall(proxy_class_def, "$csclazzname", csclazzname);
+ Replaceall(proxy_class_code, "$csclazzname", csclazzname);
+ Replaceall(proxy_class_constants_code, "$csclazzname", csclazzname);
+ Replaceall(interface_class_code, "$csclazzname", csclazzname);
+
+ Replaceall(proxy_class_def, "$module", module_class_name);
+ Replaceall(proxy_class_code, "$module", module_class_name);
+ Replaceall(proxy_class_constants_code, "$module", module_class_name);
+ Replaceall(interface_class_code, "$module", module_class_name);
+
+ Replaceall(proxy_class_def, "$imclassname", full_imclass_name);
+ Replaceall(proxy_class_code, "$imclassname", full_imclass_name);
+ Replaceall(proxy_class_constants_code, "$imclassname", full_imclass_name);
+ Replaceall(interface_class_code, "$imclassname", full_imclass_name);
+
+ Replaceall(proxy_class_def, "$dllimport", dllimport);
+ Replaceall(proxy_class_code, "$dllimport", dllimport);
+ Replaceall(proxy_class_constants_code, "$dllimport", dllimport);
+ Replaceall(interface_class_code, "$dllimport", dllimport);
+
+ if (!has_outerclass)
+ Printv(f_proxy, proxy_class_def, proxy_class_code, NIL);
+ else {
+ Swig_offset_string(proxy_class_def, nesting_depth);
+ Append(old_proxy_class_code, proxy_class_def);
+ Swig_offset_string(proxy_class_code, nesting_depth);
+ Append(old_proxy_class_code, proxy_class_code);
+ }
+
+ // Write out all the constants
+ if (Len(proxy_class_constants_code) != 0) {
+ if (!has_outerclass)
+ Printv(f_proxy, proxy_class_constants_code, NIL);
+ else {
+ Swig_offset_string(proxy_class_constants_code, nesting_depth);
+ Append(old_proxy_class_code, proxy_class_constants_code);
+ }
+ }
+ if (!has_outerclass) {
+ Printf(f_proxy, "}\n");
+ addCloseNamespace(nspace, f_proxy);
+ if (f_proxy != f_single_out)
+ Delete(f_proxy);
+ f_proxy = NULL;
+ } else {
+ for (int i = 0; i < nesting_depth; ++i)
+ Append(old_proxy_class_code, " ");
+ Append(old_proxy_class_code, "}\n\n");
+ --nesting_depth;
+ }
+
+ if (f_interface) {
+ Printv(f_interface, interface_class_code, "}\n", NIL);
+ addCloseNamespace(nspace, f_interface);
+ if (f_interface != f_single_out)
+ Delete(f_interface);
+ f_interface = 0;
+ }
+
+ emitDirectorExtraMethods(n);
+
+ Delete(interface_class_code);
+ interface_class_code = old_interface_class_code;
+ Delete(csclazzname);
+ Delete(proxy_class_name);
+ proxy_class_name = old_proxy_class_name;
+ Delete(full_imclass_name);
+ full_imclass_name = old_full_imclass_name;
+ Delete(destructor_call);
+ destructor_call = old_destructor_call;
+ Delete(proxy_class_constants_code);
+ proxy_class_constants_code = old_proxy_class_constants_code;
+ Delete(proxy_class_def);
+ proxy_class_def = old_proxy_class_def;
+ Delete(proxy_class_code);
+ proxy_class_code = old_proxy_class_code;
+ }
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * memberfunctionHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int memberfunctionHandler(Node *n) {
+ Language::memberfunctionHandler(n);
+
+ if (proxy_flag) {
+ String *overloaded_name = getOverloadedName(n);
+ String *intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), overloaded_name);
+ Setattr(n, "proxyfuncname", Getattr(n, "sym:name"));
+ Setattr(n, "imfuncname", intermediary_function_name);
+ proxyClassFunctionHandler(n);
+ Delete(overloaded_name);
+ }
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+
+ static_flag = true;
+ Language::staticmemberfunctionHandler(n);
+
+ if (proxy_flag) {
+ String *overloaded_name = getOverloadedName(n);
+ String *intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), overloaded_name);
+ Setattr(n, "proxyfuncname", Getattr(n, "sym:name"));
+ Setattr(n, "imfuncname", intermediary_function_name);
+ proxyClassFunctionHandler(n);
+ Delete(overloaded_name);
+ }
+ static_flag = false;
+
+ return SWIG_OK;
+ }
+
+
+ /* -----------------------------------------------------------------------------
+ * proxyClassFunctionHandler()
+ *
+ * Function called for creating a C# wrapper function around a c++ function in the
+ * proxy class. Used for both static and non-static C++ class functions.
+ * C++ class static functions map to C# static functions.
+ * Two extra attributes in the Node must be available. These are "proxyfuncname" -
+ * the name of the C# class proxy function, which in turn will call "imfuncname" -
+ * the intermediary (PInvoke) function name in the intermediary class.
+ * ----------------------------------------------------------------------------- */
+
+ void proxyClassFunctionHandler(Node *n) {
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *intermediary_function_name = Getattr(n, "imfuncname");
+ String *proxy_function_name = Getattr(n, "proxyfuncname");
+ String *tm;
+ Parm *p;
+ Parm *last_parm = 0;
+ int i;
+ String *imcall = NewString("");
+ String *return_type = NewString("");
+ String *function_code = NewString("");
+ bool setter_flag = false;
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *terminator_code = NewString("");
+ bool is_interface = GetFlag(parentNode(n), "feature:interface") && !checkAttribute(n, "kind", "variable")
+ && !static_flag && Getattr(n, "interface:owner") == 0;
+
+ if (!proxy_flag)
+ return;
+
+ // Wrappers not wanted for some methods where the parameters cannot be overloaded in C#
+ if (Getattr(n, "overload:ignore"))
+ return;
+
+ // Don't generate proxy method for additional explicitcall method used in directors
+ if (GetFlag(n, "explicitcall"))
+ return;
+
+ if (l) {
+ if (SwigType_type(Getattr(l, "type")) == T_VOID) {
+ l = nextSibling(l);
+ }
+ }
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("in", l, NULL);
+ Swig_typemap_attach_parms("cstype", l, NULL);
+ Swig_typemap_attach_parms("csin", l, NULL);
+
+ /* Get return types */
+ if ((tm = Swig_typemap_lookup("cstype", n, "", 0))) {
+ // Note that in the case of polymorphic (covariant) return types, the method's return type is changed to be the base of the C++ return type
+ SwigType *covariant = Getattr(n, "covariant");
+ String *cstypeout = Getattr(n, "tmap:cstype:out"); // the type in the cstype typemap's out attribute overrides the type in the typemap
+ if (cstypeout)
+ tm = cstypeout;
+ substituteClassname(covariant ? covariant : t, tm);
+ Printf(return_type, "%s", tm);
+ if (covariant)
+ Swig_warning(WARN_CSHARP_COVARIANT_RET, input_file, line_number,
+ "Covariant return types not supported in C#. Proxy method will return %s.\n", SwigType_str(covariant, 0));
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if (wrapping_member_flag && !enum_constant_flag) {
+ // Properties
+ setter_flag = (Cmp(Getattr(n, "sym:name"), Swig_name_set(getNSpace(), Swig_name_member(0, getClassPrefix(), variable_name))) == 0);
+ if (setter_flag)
+ Swig_typemap_attach_parms("csvarin", l, NULL);
+ }
+
+ /* Start generating the proxy function */
+ const String *outattributes = Getattr(n, "tmap:cstype:outattributes");
+ if (outattributes)
+ Printf(function_code, " %s\n", outattributes);
+ const String *csattributes = Getattr(n, "feature:cs:attributes");
+ if (csattributes)
+ Printf(function_code, " %s\n", csattributes);
+ const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
+ if (methodmods) {
+ if (is_smart_pointer()) {
+ // Smart pointer classes do not mirror the inheritance hierarchy of the underlying pointer type, so no virtual/override/new required.
+ String *mmods = Copy(methodmods);
+ Replaceall(mmods, "override", "");
+ Replaceall(mmods, "virtual", "");
+ Replaceall(mmods, "new", "");
+ Chop(mmods); // remove trailing whitespace
+ Printf(function_code, " %s ", mmods);
+ Delete(mmods);
+ } else {
+ Printf(function_code, " %s ", methodmods);
+ }
+ } else {
+ methodmods = (is_public(n) ? public_string : protected_string);
+ Printf(function_code, " %s ", methodmods);
+ if (!is_smart_pointer()) {
+ // Smart pointer classes do not mirror the inheritance hierarchy of the underlying pointer type, so no virtual/override/new required.
+ if (Getattr(n, "override"))
+ Printf(function_code, "override ");
+ else if (checkAttribute(n, "storage", "virtual"))
+ Printf(function_code, "virtual ");
+ if (Getattr(n, "hides"))
+ Printf(function_code, "new ");
+ }
+ }
+ if (static_flag)
+ Printf(function_code, "static ");
+ Printf(function_code, "%s %s(", return_type, proxy_function_name);
+ if (is_interface)
+ Printf(interface_class_code, " %s %s(", return_type, proxy_function_name);
+
+
+ Printv(imcall, full_imclass_name, ".$imfuncname(", NIL);
+ if (!static_flag)
+ Printf(imcall, "swigCPtr");
+
+ emit_mark_varargs(l);
+
+ int gencomma = !static_flag;
+
+ /* Output each parameter */
+ for (i = 0, p = l; p; i++) {
+
+ /* Ignored varargs */
+ if (checkAttribute(p, "varargs:ignore", "1")) {
+ p = nextSibling(p);
+ continue;
+ }
+
+ /* Ignored parameters */
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ /* Ignore the 'this' argument for variable wrappers */
+ if (!(variable_wrapper_flag && i == 0)) {
+ SwigType *pt = Getattr(p, "type");
+ String *param_type = NewString("");
+ if (setter_flag)
+ last_parm = p;
+
+ /* Get the C# parameter type */
+ if ((tm = Getattr(p, "tmap:cstype"))) {
+ substituteClassname(pt, tm);
+ const String *inattributes = Getattr(p, "tmap:cstype:inattributes");
+ Printf(param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma)
+ Printf(imcall, ", ");
+
+ String *arg = makeParameterName(n, p, i, setter_flag);
+
+ // Use typemaps to transform type used in C# wrapper function (in proxy class) to type used in PInvoke function (in intermediary class)
+ if ((tm = Getattr(p, "tmap:csin"))) {
+ substituteClassname(pt, tm);
+ Replaceall(tm, "$csinput", arg);
+ String *pre = Getattr(p, "tmap:csin:pre");
+ if (pre) {
+ substituteClassname(pt, pre);
+ Replaceall(pre, "$csinput", arg);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:csin:post");
+ if (post) {
+ substituteClassname(pt, post);
+ Replaceall(post, "$csinput", arg);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ String *terminator = Getattr(p, "tmap:csin:terminator");
+ if (terminator) {
+ substituteClassname(pt, terminator);
+ Replaceall(terminator, "$csinput", arg);
+ if (Len(terminator_code) > 0)
+ Insert(terminator_code, 0, "\n");
+ Insert(terminator_code, 0, terminator);
+ }
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to proxy function */
+ if (gencomma >= 2) {
+ Printf(function_code, ", ");
+ if (is_interface)
+ Printf(interface_class_code, ", ");
+ }
+ gencomma = 2;
+ Printf(function_code, "%s %s", param_type, arg);
+ if (is_interface)
+ Printf(interface_class_code, "%s %s", param_type, arg);
+
+ Delete(arg);
+ Delete(param_type);
+ }
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ Printf(imcall, ")");
+ Printf(function_code, ")");
+ if (is_interface)
+ Printf(interface_class_code, ");\n");
+
+ // Transform return type used in PInvoke function (in intermediary class) to type used in C# wrapper function (in proxy class)
+ if ((tm = Swig_typemap_lookup("csout", n, "", 0))) {
+ excodeSubstitute(n, tm, "csout", n);
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ bool is_terminator_code = Len(terminator_code) > 0;
+ if (is_pre_code || is_post_code || is_terminator_code) {
+ Replaceall(tm, "\n ", "\n "); // add extra indentation to code in typemap
+ if (is_post_code) {
+ Insert(tm, 0, "\n try ");
+ Printv(tm, " finally {\n", post_code, "\n }", NIL);
+ } else {
+ Insert(tm, 0, "\n ");
+ }
+ if (is_pre_code) {
+ Insert(tm, 0, pre_code);
+ Insert(tm, 0, "\n");
+ }
+ if (is_terminator_code) {
+ Printv(tm, "\n", terminator_code, NIL);
+ }
+ Insert(tm, 0, "{");
+ Printf(tm, "\n }");
+ }
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "true");
+ else
+ Replaceall(tm, "$owner", "false");
+ substituteClassname(t, tm);
+
+ // For director methods: generate code to selectively make a normal polymorphic call or
+ // an explicit method call - needed to prevent infinite recursion calls in director methods.
+ Node *explicit_n = Getattr(n, "explicitcallnode");
+ if (explicit_n) {
+ String *ex_overloaded_name = getOverloadedName(explicit_n);
+ String *ex_intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), ex_overloaded_name);
+
+ String *ex_imcall = Copy(imcall);
+ Replaceall(ex_imcall, "$imfuncname", ex_intermediary_function_name);
+ Replaceall(imcall, "$imfuncname", intermediary_function_name);
+ String *excode = NewString("");
+ Node *directorNode = Getattr(n, "directorNode");
+ UpcallData *udata = directorNode ? Getattr(directorNode, "upcalldata") : 0;
+ if (udata) {
+ String *methid = Getattr(udata, "class_methodidx");
+
+ if (!Cmp(return_type, "void"))
+ Printf(excode, "if (SwigDerivedClassHasMethod(\"%s\", swigMethodTypes%s)) %s; else %s", proxy_function_name, methid, ex_imcall, imcall);
+ else
+ Printf(excode, "(SwigDerivedClassHasMethod(\"%s\", swigMethodTypes%s) ? %s : %s)", proxy_function_name, methid, ex_imcall, imcall);
+
+ Clear(imcall);
+ Printv(imcall, excode, NIL);
+ } else {
+ // probably an ignored method or nodirector
+ }
+ Delete(excode);
+ Delete(ex_overloaded_name);
+ } else {
+ Replaceall(imcall, "$imfuncname", intermediary_function_name);
+ }
+ Replaceall(tm, "$imfuncname", intermediary_function_name);
+ Replaceall(tm, "$imcall", imcall);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csout typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if (wrapping_member_flag && !enum_constant_flag) {
+ // Properties
+ if (generate_property_declaration_flag) { // Ensure the declaration is generated just once should the property contain both a set and get
+ // Get the C# variable type - obtained differently depending on whether a setter is required.
+ String *variable_type = return_type;
+ if (setter_flag) {
+ assert(last_parm); // (last parameter is the only parameter for properties)
+ /* Get variable type - ensure the variable name is fully resolved during typemap lookup via the symbol table set in NewParmNode */
+ SwigType *cvariable_type = Getattr(last_parm, "type");
+ Parm *variable_parm = NewParmNode(cvariable_type, n);
+ if ((tm = Swig_typemap_lookup("cstype", variable_parm, "", 0))) {
+ String *cstypeout = Getattr(variable_parm, "tmap:cstype:out"); // the type in the cstype typemap's out attribute overrides the type in the typemap
+ if (cstypeout)
+ tm = cstypeout;
+ substituteClassname(cvariable_type, tm);
+ variable_type = tm;
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(cvariable_type, 0));
+ }
+ }
+ const String *csattributes = Getattr(n, "feature:cs:attributes");
+ if (csattributes)
+ Printf(proxy_class_code, " %s\n", csattributes);
+ const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
+ if (!methodmods)
+ methodmods = (is_public(n) ? public_string : protected_string);
+ Printf(proxy_class_code, " %s %s%s %s {", methodmods, static_flag ? "static " : "", variable_type, variable_name);
+ }
+ generate_property_declaration_flag = false;
+
+ if (setter_flag) {
+ // Setter method
+ assert(last_parm); // (last parameter is the only parameter for properties)
+ SwigType *cvariable_type = Getattr(last_parm, "type");
+ Parm *variable_parm = NewParmNode(cvariable_type, n);
+ if ((tm = Swig_typemap_lookup("csvarin", variable_parm, "", 0))) {
+ substituteClassname(cvariable_type, tm);
+ Replaceall(tm, "$csinput", "value");
+ Replaceall(tm, "$imfuncname", intermediary_function_name);
+ Replaceall(tm, "$imcall", imcall);
+ excodeSubstitute(n, tm, "csvarin", variable_parm);
+ Printf(proxy_class_code, "%s", tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarin typemap defined for %s\n", SwigType_str(cvariable_type, 0));
+ }
+ } else {
+ // Getter method
+ if ((tm = Swig_typemap_lookup("csvarout", n, "", 0))) {
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "true");
+ else
+ Replaceall(tm, "$owner", "false");
+ substituteClassname(t, tm);
+ Replaceall(tm, "$imfuncname", intermediary_function_name);
+ Replaceall(tm, "$imcall", imcall);
+ excodeSubstitute(n, tm, "csvarout", n);
+ Printf(proxy_class_code, "%s", tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarout typemap defined for %s\n", SwigType_str(t, 0));
+ }
+ }
+ } else {
+ // Normal function call
+ Printf(function_code, " %s\n\n", tm ? (const String *) tm : empty_string);
+ Printv(proxy_class_code, function_code, NIL);
+ }
+
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(terminator_code);
+ Delete(function_code);
+ Delete(return_type);
+ Delete(imcall);
+ }
+
+ /* ----------------------------------------------------------------------
+ * constructorHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int constructorHandler(Node *n) {
+
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ Parm *p;
+ int i;
+ String *function_code = NewString("");
+ String *helper_code = NewString(""); // Holds code for the constructor helper method generated only when the csin typemap has code in the pre or post attributes
+ String *helper_args = NewString("");
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *terminator_code = NewString("");
+ String *im_return_type = NewString("");
+ bool feature_director = (parentNode(n) && Swig_directorclass(n));
+
+ Language::constructorHandler(n);
+
+ // Wrappers not wanted for some methods where the parameters cannot be overloaded in C#
+ if (Getattr(n, "overload:ignore"))
+ return SWIG_OK;
+
+ if (proxy_flag) {
+ String *overloaded_name = getOverloadedName(n);
+ String *mangled_overname = Swig_name_construct(getNSpace(), overloaded_name);
+ String *imcall = NewString("");
+
+ const String *csattributes = Getattr(n, "feature:cs:attributes");
+ if (csattributes) {
+ Printf(function_code, " %s\n", csattributes);
+ Printf(helper_code, " %s\n", csattributes);
+ }
+ const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+
+ tm = Getattr(n, "tmap:imtype"); // typemaps were attached earlier to the node
+ String *imtypeout = Getattr(n, "tmap:imtype:out"); // the type in the imtype typemap's out attribute overrides the type in the typemap
+ if (imtypeout)
+ tm = imtypeout;
+ Printf(im_return_type, "%s", tm);
+
+ Printf(function_code, " %s %s(", methodmods, proxy_class_name);
+ Printf(helper_code, " static private %s SwigConstruct%s(", im_return_type, proxy_class_name);
+
+ Printv(imcall, full_imclass_name, ".", mangled_overname, "(", NIL);
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("in", l, NULL);
+ Swig_typemap_attach_parms("cstype", l, NULL);
+ Swig_typemap_attach_parms("csin", l, NULL);
+
+ emit_mark_varargs(l);
+
+ int gencomma = 0;
+
+ /* Output each parameter */
+ for (i = 0, p = l; p; i++) {
+
+ /* Ignored varargs */
+ if (checkAttribute(p, "varargs:ignore", "1")) {
+ p = nextSibling(p);
+ continue;
+ }
+
+ /* Ignored parameters */
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *param_type = NewString("");
+
+ /* Get the C# parameter type */
+ if ((tm = Getattr(p, "tmap:cstype"))) {
+ substituteClassname(pt, tm);
+ const String *inattributes = Getattr(p, "tmap:cstype:inattributes");
+ Printf(param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma)
+ Printf(imcall, ", ");
+
+ String *arg = makeParameterName(n, p, i, false);
+ String *cshin = 0;
+
+ // Use typemaps to transform type used in C# wrapper function (in proxy class) to type used in PInvoke function (in intermediary class)
+ if ((tm = Getattr(p, "tmap:csin"))) {
+ substituteClassname(pt, tm);
+ Replaceall(tm, "$csinput", arg);
+ String *pre = Getattr(p, "tmap:csin:pre");
+ if (pre) {
+ substituteClassname(pt, pre);
+ Replaceall(pre, "$csinput", arg);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:csin:post");
+ if (post) {
+ substituteClassname(pt, post);
+ Replaceall(post, "$csinput", arg);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ String *terminator = Getattr(p, "tmap:csin:terminator");
+ if (terminator) {
+ substituteClassname(pt, terminator);
+ Replaceall(terminator, "$csinput", arg);
+ if (Len(terminator_code) > 0)
+ Insert(terminator_code, 0, "\n");
+ Insert(terminator_code, 0, terminator);
+ }
+ cshin = Getattr(p, "tmap:csin:cshin");
+ if (cshin)
+ Replaceall(cshin, "$csinput", arg);
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to proxy function */
+ if (gencomma) {
+ Printf(function_code, ", ");
+ Printf(helper_code, ", ");
+ Printf(helper_args, ", ");
+ }
+ Printf(function_code, "%s %s", param_type, arg);
+ Printf(helper_code, "%s %s", param_type, arg);
+ Printf(helper_args, "%s", cshin ? cshin : arg);
+ ++gencomma;
+
+ Delete(cshin);
+ Delete(arg);
+ Delete(param_type);
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ Printf(imcall, ")");
+
+ Printf(function_code, ")");
+ Printf(helper_code, ")");
+
+ /* Insert the csconstruct typemap, doing the replacement for $directorconnect, as needed */
+ Hash *attributes = NewHash();
+ String *typemap_lookup_type = Getattr(getCurrentClass(), "classtypeobj");
+ String *construct_tm = Copy(typemapLookup(n, "csconstruct", typemap_lookup_type,
+ WARN_CSHARP_TYPEMAP_CSCONSTRUCT_UNDEF, attributes));
+ if (construct_tm) {
+ if (!feature_director) {
+ Replaceall(construct_tm, "$directorconnect", "");
+ } else {
+ String *connect_attr = Getattr(attributes, "tmap:csconstruct:directorconnect");
+
+ if (connect_attr) {
+ Replaceall(construct_tm, "$directorconnect", connect_attr);
+ } else {
+ Swig_warning(WARN_CSHARP_NO_DIRECTORCONNECT_ATTR, input_file, line_number, "\"directorconnect\" attribute missing in %s \"csconstruct\" typemap.\n",
+ Getattr(n, "name"));
+ Replaceall(construct_tm, "$directorconnect", "");
+ }
+ }
+
+ Printv(function_code, " ", construct_tm, NIL);
+ }
+
+ excodeSubstitute(n, function_code, "csconstruct", attributes);
+
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ bool is_terminator_code = Len(terminator_code) > 0;
+ if (is_pre_code || is_post_code || is_terminator_code) {
+ Printf(helper_code, " {\n");
+ if (is_pre_code) {
+ Printv(helper_code, pre_code, "\n", NIL);
+ }
+ if (is_post_code) {
+ Printf(helper_code, " try {\n");
+ Printv(helper_code, " return ", imcall, ";\n", NIL);
+ Printv(helper_code, " } finally {\n", post_code, "\n }", NIL);
+ } else {
+ Printv(helper_code, " return ", imcall, ";", NIL);
+ }
+ if (is_terminator_code) {
+ Printv(helper_code, "\n", terminator_code, NIL);
+ }
+ Printf(helper_code, "\n }\n");
+ String *helper_name = NewStringf("%s.SwigConstruct%s(%s)", proxy_class_name, proxy_class_name, helper_args);
+ String *im_outattributes = Getattr(n, "tmap:imtype:outattributes");
+ if (im_outattributes)
+ Printf(proxy_class_code, " %s\n", im_outattributes);
+ Printv(proxy_class_code, helper_code, "\n", NIL);
+ Replaceall(function_code, "$imcall", helper_name);
+ Delete(helper_name);
+ } else {
+ Replaceall(function_code, "$imcall", imcall);
+ }
+
+ Printv(proxy_class_code, function_code, "\n", NIL);
+
+ Delete(helper_args);
+ Delete(im_return_type);
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(terminator_code);
+ Delete(construct_tm);
+ Delete(attributes);
+ Delete(overloaded_name);
+ Delete(imcall);
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * destructorHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int destructorHandler(Node *n) {
+ Language::destructorHandler(n);
+ String *symname = Getattr(n, "sym:name");
+
+ if (proxy_flag) {
+ Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL);
+ const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
+ if (methodmods)
+ Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods);
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * membervariableHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int membervariableHandler(Node *n) {
+
+ generate_property_declaration_flag = true;
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ variable_wrapper_flag = true;
+ Language::membervariableHandler(n);
+ wrapping_member_flag = false;
+ variable_wrapper_flag = false;
+ generate_property_declaration_flag = false;
+
+ Printf(proxy_class_code, "\n }\n\n");
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * staticmembervariableHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int staticmembervariableHandler(Node *n) {
+
+ bool static_const_member_flag = (Getattr(n, "value") == 0);
+
+ generate_property_declaration_flag = true;
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ static_flag = true;
+ Language::staticmembervariableHandler(n);
+ wrapping_member_flag = false;
+ static_flag = false;
+ generate_property_declaration_flag = false;
+
+ if (static_const_member_flag)
+ Printf(proxy_class_code, "\n }\n\n");
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * memberconstantHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int memberconstantHandler(Node *n) {
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ Language::memberconstantHandler(n);
+ wrapping_member_flag = false;
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getOverloadedName()
+ * ----------------------------------------------------------------------------- */
+
+ String *getOverloadedName(Node *n) {
+
+ /* A C# HandleRef is used for all classes in the SWIG intermediary class.
+ * The intermediary class methods are thus mangled when overloaded to give
+ * a unique name. */
+ String *overloaded_name = NewStringf("%s", Getattr(n, "sym:name"));
+
+ if (Getattr(n, "sym:overloaded")) {
+ Printv(overloaded_name, Getattr(n, "sym:overname"), NIL);
+ }
+
+ return overloaded_name;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * moduleClassFunctionHandler()
+ * ----------------------------------------------------------------------------- */
+
+ void moduleClassFunctionHandler(Node *n) {
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ Parm *p;
+ Parm *last_parm = 0;
+ int i;
+ String *imcall = NewString("");
+ String *return_type = NewString("");
+ String *function_code = NewString("");
+ int num_arguments = 0;
+ String *overloaded_name = getOverloadedName(n);
+ String *func_name = NULL;
+ bool setter_flag = false;
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *terminator_code = NewString("");
+
+ if (l) {
+ if (SwigType_type(Getattr(l, "type")) == T_VOID) {
+ l = nextSibling(l);
+ }
+ }
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("cstype", l, NULL);
+ Swig_typemap_attach_parms("csin", l, NULL);
+
+ /* Get return types */
+ if ((tm = Swig_typemap_lookup("cstype", n, "", 0))) {
+ String *cstypeout = Getattr(n, "tmap:cstype:out"); // the type in the cstype typemap's out attribute overrides the type in the typemap
+ if (cstypeout)
+ tm = cstypeout;
+ substituteClassname(t, tm);
+ Printf(return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ /* Change function name for global variables */
+ if (proxy_flag && global_variable_flag) {
+ // Capitalize the first letter in the variable to create the getter/setter function name
+ func_name = NewString("");
+ setter_flag = (Cmp(Getattr(n, "sym:name"), Swig_name_set(getNSpace(), variable_name)) == 0);
+ if (setter_flag)
+ Printf(func_name, "set");
+ else
+ Printf(func_name, "get");
+ Putc(toupper((int) *Char(variable_name)), func_name);
+ Printf(func_name, "%s", Char(variable_name) + 1);
+ if (setter_flag)
+ Swig_typemap_attach_parms("csvarin", l, NULL);
+ } else {
+ func_name = Copy(Getattr(n, "sym:name"));
+ }
+
+ /* Start generating the function */
+ const String *outattributes = Getattr(n, "tmap:cstype:outattributes");
+ if (outattributes)
+ Printf(function_code, " %s\n", outattributes);
+ const String *csattributes = Getattr(n, "feature:cs:attributes");
+ if (csattributes)
+ Printf(function_code, " %s\n", csattributes);
+ const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+ Printf(function_code, " %s static %s %s(", methodmods, return_type, func_name);
+ Printv(imcall, imclass_name, ".", overloaded_name, "(", NIL);
+
+ /* Get number of required and total arguments */
+ num_arguments = emit_num_arguments(l);
+
+ bool global_or_member_variable = global_variable_flag || (wrapping_member_flag && !enum_constant_flag);
+ int gencomma = 0;
+
+ /* Output each parameter */
+ for (i = 0, p = l; i < num_arguments; i++) {
+
+ /* Ignored parameters */
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *param_type = NewString("");
+ last_parm = p;
+
+ /* Get the C# parameter type */
+ if ((tm = Getattr(p, "tmap:cstype"))) {
+ substituteClassname(pt, tm);
+ const String *inattributes = Getattr(p, "tmap:cstype:inattributes");
+ Printf(param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma)
+ Printf(imcall, ", ");
+
+ String *arg = makeParameterName(n, p, i, global_or_member_variable);
+
+ // Use typemaps to transform type used in C# wrapper function (in proxy class) to type used in PInvoke function (in intermediary class)
+ if ((tm = Getattr(p, "tmap:csin"))) {
+ substituteClassname(pt, tm);
+ Replaceall(tm, "$csinput", arg);
+ String *pre = Getattr(p, "tmap:csin:pre");
+ if (pre) {
+ substituteClassname(pt, pre);
+ Replaceall(pre, "$csinput", arg);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:csin:post");
+ if (post) {
+ substituteClassname(pt, post);
+ Replaceall(post, "$csinput", arg);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ String *terminator = Getattr(p, "tmap:csin:terminator");
+ if (terminator) {
+ substituteClassname(pt, terminator);
+ Replaceall(terminator, "$csinput", arg);
+ if (Len(terminator_code) > 0)
+ Insert(terminator_code, 0, "\n");
+ Insert(terminator_code, 0, terminator);
+ }
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to module class function */
+ if (gencomma >= 2)
+ Printf(function_code, ", ");
+ gencomma = 2;
+ Printf(function_code, "%s %s", param_type, arg);
+
+ p = Getattr(p, "tmap:in:next");
+ Delete(arg);
+ Delete(param_type);
+ }
+
+ Printf(imcall, ")");
+ Printf(function_code, ")");
+
+ // Transform return type used in PInvoke function (in intermediary class) to type used in C# wrapper function (in module class)
+ if ((tm = Swig_typemap_lookup("csout", n, "", 0))) {
+ excodeSubstitute(n, tm, "csout", n);
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ bool is_terminator_code = Len(terminator_code) > 0;
+ if (is_pre_code || is_post_code || is_terminator_code) {
+ Replaceall(tm, "\n ", "\n "); // add extra indentation to code in typemap
+ if (is_post_code) {
+ Insert(tm, 0, "\n try ");
+ Printv(tm, " finally {\n", post_code, "\n }", NIL);
+ } else {
+ Insert(tm, 0, "\n ");
+ }
+ if (is_pre_code) {
+ Insert(tm, 0, pre_code);
+ Insert(tm, 0, "\n");
+ }
+ if (is_terminator_code) {
+ Printv(tm, "\n", terminator_code, NIL);
+ }
+ Insert(tm, 0, "{");
+ Printf(tm, "\n }");
+ }
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "true");
+ else
+ Replaceall(tm, "$owner", "false");
+ substituteClassname(t, tm);
+ Replaceall(tm, "$imfuncname", overloaded_name);
+ Replaceall(tm, "$imcall", imcall);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csout typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if (proxy_flag && global_variable_flag) {
+ // Properties
+ if (generate_property_declaration_flag) { // Ensure the declaration is generated just once should the property contain both a set and get
+ // Get the C# variable type - obtained differently depending on whether a setter is required.
+ String *variable_type = return_type;
+ if (setter_flag) {
+ p = last_parm; // (last parameter is the only parameter for properties)
+ SwigType *pt = Getattr(p, "type");
+ if ((tm = Getattr(p, "tmap:cstype"))) {
+ substituteClassname(pt, tm);
+ String *cstypeout = Getattr(p, "tmap:cstype:out"); // the type in the cstype typemap's out attribute overrides the type in the typemap
+ variable_type = cstypeout ? cstypeout : tm;
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarin typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+ }
+ const String *csattributes = Getattr(n, "feature:cs:attributes");
+ if (csattributes)
+ Printf(module_class_code, " %s\n", csattributes);
+ const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
+ if (!methodmods)
+ methodmods = (is_public(n) ? public_string : protected_string);
+ Printf(module_class_code, " %s static %s %s {", methodmods, variable_type, variable_name);
+ }
+ generate_property_declaration_flag = false;
+
+ if (setter_flag) {
+ // Setter method
+ p = last_parm; // (last parameter is the only parameter for properties)
+ SwigType *pt = Getattr(p, "type");
+ if ((tm = Getattr(p, "tmap:csvarin"))) {
+ substituteClassname(pt, tm);
+ Replaceall(tm, "$csinput", "value");
+ Replaceall(tm, "$imfuncname", overloaded_name);
+ Replaceall(tm, "$imcall", imcall);
+ excodeSubstitute(n, tm, "csvarin", p);
+ Printf(module_class_code, "%s", tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarin typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+ } else {
+ // Getter method
+ if ((tm = Swig_typemap_lookup("csvarout", n, "", 0))) {
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "true");
+ else
+ Replaceall(tm, "$owner", "false");
+ substituteClassname(t, tm);
+ Replaceall(tm, "$imfuncname", overloaded_name);
+ Replaceall(tm, "$imcall", imcall);
+ excodeSubstitute(n, tm, "csvarout", n);
+ Printf(module_class_code, "%s", tm);
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarout typemap defined for %s\n", SwigType_str(t, 0));
+ }
+ }
+ } else {
+ // Normal function call
+ Printf(function_code, " %s\n\n", tm ? (const String *) tm : empty_string);
+ Printv(module_class_code, function_code, NIL);
+ }
+
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(terminator_code);
+ Delete(function_code);
+ Delete(return_type);
+ Delete(imcall);
+ Delete(func_name);
+ }
+
+ /*----------------------------------------------------------------------
+ * replaceSpecialVariables()
+ *--------------------------------------------------------------------*/
+
+ virtual void replaceSpecialVariables(String *method, String *tm, Parm *parm) {
+ (void)method;
+ SwigType *type = Getattr(parm, "type");
+ substituteClassname(type, tm);
+ }
+
+ /*----------------------------------------------------------------------
+ * decodeEnumFeature()
+ * Decode the possible enum features, which are one of:
+ * %csenum(simple)
+ * %csenum(typeunsafe) - default
+ * %csenum(typesafe)
+ * %csenum(proper)
+ *--------------------------------------------------------------------*/
+
+ EnumFeature decodeEnumFeature(Node *n) {
+ EnumFeature enum_feature = TypeunsafeEnum;
+ String *feature = Getattr(n, "feature:cs:enum");
+ if (feature) {
+ if (Cmp(feature, "simple") == 0)
+ enum_feature = SimpleEnum;
+ else if (Cmp(feature, "typesafe") == 0)
+ enum_feature = TypesafeEnum;
+ else if (Cmp(feature, "proper") == 0)
+ enum_feature = ProperEnum;
+ }
+ return enum_feature;
+ }
+
+ /* -----------------------------------------------------------------------
+ * enumValue()
+ * This method will return a string with an enum value to use in C# generated
+ * code. If the %csconst feature is not used, the string will contain the intermediary
+ * class call to obtain the enum value. The intermediary class and PINVOKE methods to obtain
+ * the enum value will be generated. Otherwise the C/C++ enum value will be used if there
+ * is one and hopefully it will compile as C# code - e.g. 20 as in: enum E{e=20};
+ * The %csconstvalue feature overrides all other ways to generate the constant value.
+ * The caller must delete memory allocated for the returned string.
+ * ------------------------------------------------------------------------ */
+
+ String *enumValue(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+
+ // Check for the %csconstvalue feature
+ String *value = Getattr(n, "feature:cs:constvalue");
+
+ if (!value) {
+ // The %csconst feature determines how the constant value is obtained
+ int const_feature_flag = GetFlag(n, "feature:cs:const");
+
+ if (const_feature_flag) {
+ // Use the C syntax to make a true C# constant and hope that it compiles as C# code
+ value = Getattr(n, "enumvalue") ? Copy(Getattr(n, "enumvalue")) : Copy(Getattr(n, "enumvalueex"));
+ } else {
+ String *newsymname = 0;
+ if (!getCurrentClass() || !proxy_flag) {
+ String *enumClassPrefix = getEnumClassPrefix();
+ if (enumClassPrefix) {
+ // A global scoped enum
+ newsymname = Swig_name_member(0, enumClassPrefix, symname);
+ symname = newsymname;
+ }
+ }
+
+ // Get the enumvalue from a PINVOKE call
+ if (!getCurrentClass() || !cparse_cplusplus || !proxy_flag) {
+ // Strange hack to change the name
+ Setattr(n, "name", Getattr(n, "value")); /* for wrapping of enums in a namespace when emit_action is used */
+ constantWrapper(n);
+ value = NewStringf("%s.%s()", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
+ } else {
+ memberconstantHandler(n);
+ value = NewStringf("%s.%s()", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), Swig_name_member(0, getEnumClassPrefix(), symname)));
+ }
+ }
+ }
+ return value;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getEnumName()
+ * ----------------------------------------------------------------------------- */
+
+ String *getEnumName(SwigType *t) {
+ Node *enumname = NULL;
+ Node *n = enumLookup(t);
+ if (n) {
+ enumname = Getattr(n, "enumname");
+ if (!enumname) {
+ String *symname = Getattr(n, "sym:name");
+ if (symname) {
+ // Add in class scope when referencing enum if not a global enum
+ String *scopename_prefix = Swig_scopename_prefix(Getattr(n, "name"));
+ String *proxyname = 0;
+ if (scopename_prefix) {
+ proxyname = getProxyName(scopename_prefix);
+ }
+ if (proxyname) {
+ enumname = NewStringf("%s.%s", proxyname, symname);
+ } else {
+ // global enum or enum in a namespace
+ String *nspace = Getattr(n, "sym:nspace");
+ if (nspace) {
+ if (namespce)
+ enumname = NewStringf("%s.%s.%s", namespce, nspace, symname);
+ else
+ enumname = NewStringf("%s.%s", nspace, symname);
+ } else {
+ enumname = Copy(symname);
+ }
+ }
+ Setattr(n, "enumname", enumname);
+ Delete(enumname);
+ Delete(scopename_prefix);
+ }
+ }
+ }
+
+ return enumname;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * substituteClassname()
+ *
+ * Substitute the special variable $csclassname with the proxy class name for classes/structs/unions
+ * that SWIG knows about. Also substitutes enums with enum name.
+ * Otherwise use the $descriptor name for the C# class name. Note that the $&csclassname substitution
+ * is the same as a $&descriptor substitution, ie one pointer added to descriptor name.
+ * Inputs:
+ * pt - parameter type
+ * tm - typemap contents that might contain the special variable to be replaced
+ * Outputs:
+ * tm - typemap contents complete with the special variable substitution
+ * Return:
+ * substitution_performed - flag indicating if a substitution was performed
+ * ----------------------------------------------------------------------------- */
+
+ bool substituteClassname(SwigType *pt, String *tm) {
+ bool substitution_performed = false;
+ SwigType *type = Copy(SwigType_typedef_resolve_all(pt));
+ SwigType *strippedtype = SwigType_strip_qualifiers(type);
+
+ if (Strstr(tm, "$csclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ substituteClassnameSpecialVariable(classnametype, tm, "$csclassname");
+ substitution_performed = true;
+ Delete(classnametype);
+ }
+ if (Strstr(tm, "$*csclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ Delete(SwigType_pop(classnametype));
+ if (Len(classnametype) > 0) {
+ substituteClassnameSpecialVariable(classnametype, tm, "$*csclassname");
+ substitution_performed = true;
+ }
+ Delete(classnametype);
+ }
+ if (Strstr(tm, "$&csclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ SwigType_add_pointer(classnametype);
+ substituteClassnameSpecialVariable(classnametype, tm, "$&csclassname");
+ substitution_performed = true;
+ Delete(classnametype);
+ }
+ if (Strstr(tm, "$csinterfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$csinterfacename", true);
+ substitution_performed = true;
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$*csinterfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ Delete(SwigType_pop(interfacenametype));
+ if (Len(interfacenametype) > 0) {
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$*csinterfacename", true);
+ substitution_performed = true;
+ }
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$&csinterfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ SwigType_add_pointer(interfacenametype);
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$&csinterfacename", true);
+ substitution_performed = true;
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$interfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$interfacename", false);
+ substitution_performed = true;
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$*interfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ Delete(SwigType_pop(interfacenametype));
+ if (Len(interfacenametype) > 0) {
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$*interfacename", false);
+ substitution_performed = true;
+ }
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$&interfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ SwigType_add_pointer(interfacenametype);
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$&interfacename", false);
+ substitution_performed = true;
+ Delete(interfacenametype);
+ }
+
+ Delete(strippedtype);
+ Delete(type);
+
+ return substitution_performed;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * substituteClassnameSpecialVariable()
+ * ----------------------------------------------------------------------------- */
+
+ void substituteClassnameSpecialVariable(SwigType *classnametype, String *tm, const char *classnamespecialvariable) {
+ String *replacementname;
+ if (SwigType_isenum(classnametype)) {
+ String *enumname = getEnumName(classnametype);
+ if (enumname) {
+ replacementname = Copy(enumname);
+ } else {
+ bool anonymous_enum = (Cmp(classnametype, "enum ") == 0);
+ if (anonymous_enum) {
+ replacementname = NewString("int");
+ } else {
+ // An unknown enum - one that has not been parsed (neither a C enum forward reference nor a definition) or an ignored enum
+ replacementname = NewStringf("SWIGTYPE%s", SwigType_manglestr(classnametype));
+ Replace(replacementname, "enum ", "", DOH_REPLACE_ANY);
+ Setattr(swig_types_hash, replacementname, classnametype);
+ }
+ }
+ } else {
+ String *classname = getProxyName(classnametype); // getProxyName() works for pointers to classes too
+ if (classname) {
+ replacementname = Copy(classname);
+ } else {
+ // use $descriptor if SWIG does not know anything about this type. Note that any typedefs are resolved.
+ replacementname = NewStringf("SWIGTYPE%s", SwigType_manglestr(classnametype));
+
+ // Add to hash table so that the type wrapper classes can be created later
+ Setattr(swig_types_hash, replacementname, classnametype);
+ }
+ }
+ Replaceall(tm, classnamespecialvariable, replacementname);
+
+ Delete(replacementname);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * substituteInterfacenameSpecialVariable()
+ * ----------------------------------------------------------------------------- */
+
+ void substituteInterfacenameSpecialVariable(SwigType *interfacenametype, String *tm, const char *interfacenamespecialvariable, bool qualified) {
+
+ String *interfacename = getInterfaceName(interfacenametype, qualified);
+ if (interfacename) {
+ String *replacementname = Copy(interfacename);
+ Replaceall(tm, interfacenamespecialvariable, replacementname);
+ Delete(replacementname);
+ }
+ }
+
+ /* -----------------------------------------------------------------------------
+ * emitTypeWrapperClass()
+ * ----------------------------------------------------------------------------- */
+
+ void emitTypeWrapperClass(String *classname, SwigType *type) {
+ Node *n = NewHash();
+ Setfile(n, input_file);
+ Setline(n, line_number);
+
+ String *swigtype = NewString("");
+ File *f_swigtype = getOutputFile(SWIG_output_directory(), classname);
+
+ addOpenNamespace(0, f_swigtype);
+
+ // Pure C# baseclass and interfaces
+ const String *pure_baseclass = typemapLookup(n, "csbase", type, WARN_NONE);
+ const String *pure_interfaces = typemapLookup(n, "csinterfaces", type, WARN_NONE);
+
+ // Emit the class
+ Printv(swigtype, typemapLookup(n, "csimports", type, WARN_NONE), // Import statements
+ "\n", NIL);
+
+ // Class attributes
+ const String *csattributes = typemapLookup(n, "csattributes", type, WARN_NONE);
+ if (csattributes && *Char(csattributes))
+ Printf(swigtype, "%s\n", csattributes);
+
+ Printv(swigtype, typemapLookup(n, "csclassmodifiers", type, WARN_CSHARP_TYPEMAP_CLASSMOD_UNDEF), // Class modifiers
+ " $csclassname", // Class name and base class
+ (*Char(pure_baseclass) || *Char(pure_interfaces)) ? " : " : "", pure_baseclass, ((*Char(pure_baseclass)) && *Char(pure_interfaces)) ? // Interfaces
+ ", " : "", pure_interfaces, " {", typemapLookup(n, "csbody", type, WARN_CSHARP_TYPEMAP_CSBODY_UNDEF), // main body of class
+ typemapLookup(n, "cscode", type, WARN_NONE), // extra C# code
+ "}\n", NIL);
+
+ Replaceall(swigtype, "$csclassname", classname);
+ Replaceall(swigtype, "$module", module_class_name);
+ Replaceall(swigtype, "$imclassname", imclass_name);
+ Replaceall(swigtype, "$dllimport", dllimport);
+
+ // For unknown enums
+ Replaceall(swigtype, "$enumvalues", "");
+
+ Printv(f_swigtype, swigtype, NIL);
+
+ addCloseNamespace(0, f_swigtype);
+ if (f_swigtype != f_single_out)
+ Delete(f_swigtype);
+ f_swigtype = NULL;
+ Delete(swigtype);
+ Delete(n);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * typemapLookup()
+ * n - for input only and must contain info for Getfile(n) and Getline(n) to work
+ * tmap_method - typemap method name
+ * type - typemap type to lookup
+ * warning - warning number to issue if no typemaps found
+ * typemap_attributes - the typemap attributes are attached to this node and will
+ * also be used for temporary storage if non null
+ * return is never NULL, unlike Swig_typemap_lookup()
+ * ----------------------------------------------------------------------------- */
+
+ const String *typemapLookup(Node *n, const_String_or_char_ptr tmap_method, SwigType *type, int warning, Node *typemap_attributes = 0) {
+ Node *node = !typemap_attributes ? NewHash() : typemap_attributes;
+ Setattr(node, "type", type);
+ Setfile(node, Getfile(n));
+ Setline(node, Getline(n));
+ const String *tm = Swig_typemap_lookup(tmap_method, node, "", 0);
+ if (!tm) {
+ tm = empty_string;
+ if (warning != WARN_NONE)
+ Swig_warning(warning, Getfile(n), Getline(n), "No %s typemap defined for %s\n", tmap_method, SwigType_str(type, 0));
+ }
+ if (!typemap_attributes)
+ Delete(node);
+ return tm;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * typemapExists()
+ * n - for input only and must contain info for Getfile(n) and Getline(n) to work
+ * tmap_method - typemap method name
+ * type - typemap type to lookup
+ * returns found typemap or NULL if not found
+ * ----------------------------------------------------------------------------- */
+
+ const String *typemapExists(Node *n, const_String_or_char_ptr tmap_method, SwigType *type) {
+ Node *node = NewHash();
+ Setattr(node, "type", type);
+ Setfile(node, Getfile(n));
+ Setline(node, Getline(n));
+ const String *tm = Swig_typemap_lookup(tmap_method, node, "", 0);
+ Delete(node);
+ return tm;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * canThrow()
+ * Determine whether the code in the typemap can throw a C# exception.
+ * If so, note it for later when excodeSubstitute() is called.
+ * ----------------------------------------------------------------------------- */
+
+ void canThrow(Node *n, const String *typemap, Node *parameter) {
+ String *canthrow_attribute = NewStringf("tmap:%s:canthrow", typemap);
+ String *canthrow = Getattr(parameter, canthrow_attribute);
+ if (canthrow)
+ Setattr(n, "csharp:canthrow", "1");
+ Delete(canthrow_attribute);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * excodeSubstitute()
+ * If a method can throw a C# exception, additional exception code is added to
+ * check for the pending exception so that it can then throw the exception. The
+ * $excode special variable is replaced by the exception code in the excode
+ * typemap attribute.
+ * ----------------------------------------------------------------------------- */
+
+ void excodeSubstitute(Node *n, String *code, const String *typemap, Node *parameter) {
+ String *excode_attribute = NewStringf("tmap:%s:excode", typemap);
+ String *excode = Getattr(parameter, excode_attribute);
+ if (Getattr(n, "csharp:canthrow")) {
+ int count = Replaceall(code, "$excode", excode);
+ if (count < 1 || !excode) {
+ Swig_warning(WARN_CSHARP_EXCODE, input_file, line_number,
+ "C# exception may not be thrown - no $excode or excode attribute in '%s' typemap.\n", typemap);
+ }
+ } else {
+ Replaceall(code, "$excode", empty_string);
+ }
+ Delete(excode_attribute);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * addOpenNamespace()
+ * ----------------------------------------------------------------------------- */
+
+ void addOpenNamespace(const String *nspace, File *file) {
+ if (namespce || nspace) {
+ Printf(file, "namespace ");
+ if (namespce)
+ Printv(file, namespce, nspace ? "." : "", NIL);
+ if (nspace)
+ Printv(file, nspace, NIL);
+ Printf(file, " {\n");
+ }
+ }
+
+ /* -----------------------------------------------------------------------------
+ * addCloseNamespace()
+ * ----------------------------------------------------------------------------- */
+
+ void addCloseNamespace(const String *nspace, File *file) {
+ if (namespce || nspace)
+ Printf(file, "\n}\n");
+ }
+
+ /* -----------------------------------------------------------------------------
+ * outputDirectory()
+ *
+ * Return the directory to use for generating C# classes/enums and create the
+ * subdirectory (does not create if language specific outdir does not exist).
+ * ----------------------------------------------------------------------------- */
+
+ String *outputDirectory(String *nspace) {
+ String *output_directory = Copy(SWIG_output_directory());
+ if (nspace) {
+ String *nspace_subdirectory = Copy(nspace);
+ Replaceall(nspace_subdirectory, ".", SWIG_FILE_DELIMITER);
+ String *newdir_error = Swig_new_subdirectory(output_directory, nspace_subdirectory);
+ if (newdir_error) {
+ Printf(stderr, "%s\n", newdir_error);
+ Delete(newdir_error);
+ Exit(EXIT_FAILURE);
+ }
+ Printv(output_directory, nspace_subdirectory, SWIG_FILE_DELIMITER, 0);
+ Delete(nspace_subdirectory);
+ }
+ return output_directory;
+ }
+
+ /*----------------------------------------------------------------------
+ * Start of director methods
+ *--------------------------------------------------------------------*/
+
+#if 0
+ /*----------------------------------------------------------------------
+ * emitDirectorUpcalls()
+ *--------------------------------------------------------------------*/
+
+ void emitDirectorUpcalls() {
+ if (n_dmethods) {
+ Wrapper *w = NewWrapper();
+ String *dmethod_data = NewString("");
+ int n_methods = 0;
+ Iterator udata_iter;
+
+ udata_iter = First(dmethods_seq);
+ while (udata_iter.item) {
+ UpcallData *udata = udata_iter.item;
+ Printf(dmethod_data, " { \"%s\", \"%s\" }", Getattr(udata, "imclass_method"), Getattr(udata, "imclass_fdesc"));
+ ++n_methods;
+
+ udata_iter = Next(udata_iter);
+
+ if (udata_iter.item)
+ Putc(',', dmethod_data);
+ Putc('\n', dmethod_data);
+ }
+
+
+ Wrapper_print(w, f_wrappers);
+ Delete(dmethod_data);
+ Delete(swig_module_init);
+ DelWrapper(w);
+ }
+ }
+#endif
+
+ /*----------------------------------------------------------------------
+ * emitDirectorExtraMethods()
+ *
+ * This is where the director connect method is generated.
+ *--------------------------------------------------------------------*/
+ void emitDirectorExtraMethods(Node *n) {
+ if (!Swig_directorclass(n))
+ return;
+
+ // Output the director connect method:
+ String *norm_name = SwigType_namestr(Getattr(n, "name"));
+ String *dirclassname = directorClassName(n);
+ String *swig_director_connect = Swig_name_member(getNSpace(), getClassPrefix(), "director_connect");
+ String *wname = Swig_name_wrapper(swig_director_connect);
+ String *sym_name = Getattr(n, "sym:name");
+ String *qualified_classname = Copy(sym_name);
+ String *nspace = getNSpace();
+ String *dirClassName = directorClassName(n);
+ String *smartptr = Getattr(n, "feature:smartptr");
+ if (!GetFlag(n, "feature:flatnested")) {
+ for (Node *outer_class = Getattr(n, "nested:outer"); outer_class; outer_class = Getattr(outer_class, "nested:outer")) {
+
+ Push(qualified_classname, ".");
+ Push(qualified_classname, Getattr(outer_class, "sym:name"));
+ }
+ }
+ if (nspace)
+ Insert(qualified_classname, 0, NewStringf("%s.", nspace));
+
+ Printv(imclass_class_code, "\n [global::System.Runtime.InteropServices.DllImport(\"", dllimport, "\", EntryPoint=\"", wname, "\")]\n", NIL);
+ Printf(imclass_class_code, " public static extern void %s(global::System.Runtime.InteropServices.HandleRef jarg1", swig_director_connect);
+
+ Wrapper *code_wrap = NewWrapper();
+ Printf(code_wrap->def, "SWIGEXPORT void SWIGSTDCALL %s(void *objarg", wname);
+
+ if (smartptr) {
+ Printf(code_wrap->code, " %s *obj = (%s *)objarg;\n", smartptr, smartptr);
+ Printf(code_wrap->code, " // Keep a local instance of the smart pointer around while we are using the raw pointer\n");
+ Printf(code_wrap->code, " // Avoids using smart pointer specific API.\n");
+ Printf(code_wrap->code, " %s *director = static_cast<%s *>(obj->operator->());\n", dirClassName, dirClassName);
+ } else {
+ Printf(code_wrap->code, " %s *obj = (%s *)objarg;\n", norm_name, norm_name);
+ Printf(code_wrap->code, " %s *director = static_cast<%s *>(obj);\n", dirClassName, dirClassName);
+ }
+
+ Printf(code_wrap->code, " director->swig_connect_director(");
+
+ for (int i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *methid = Getattr(udata, "class_methodidx");
+
+ Printf(code_wrap->def, ", ");
+ if (i != first_class_dmethod)
+ Printf(code_wrap->code, ", ");
+ Printf(code_wrap->def, "%s::SWIG_Callback%s_t callback%s", dirclassname, methid, methid);
+ Printf(code_wrap->code, "callback%s", methid);
+ Printf(imclass_class_code, ", %s.SwigDelegate%s_%s delegate%s", qualified_classname, sym_name, methid, methid);
+ }
+
+ Printf(code_wrap->def, ") {\n");
+ Printf(code_wrap->code, ");\n");
+ Printf(imclass_class_code, ");\n");
+ Printf(code_wrap->code, "}\n");
+
+ Wrapper_print(code_wrap, f_wrappers);
+ DelWrapper(code_wrap);
+
+ Delete(wname);
+ Delete(swig_director_connect);
+ Delete(qualified_classname);
+ Delete(dirclassname);
+ }
+
+ /* ---------------------------------------------------------------
+ * classDirectorMethod()
+ *
+ * Emit a virtual director method to pass a method call on to the
+ * underlying C# object.
+ *
+ * --------------------------------------------------------------- */
+
+ int classDirectorMethod(Node *n, Node *parent, String *super) {
+ String *classname = Getattr(parent, "sym:name");
+ String *c_classname = Getattr(parent, "name");
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *returntype = Getattr(n, "type");
+ String *overloaded_name = getOverloadedName(n);
+ String *storage = Getattr(n, "storage");
+ String *value = Getattr(n, "value");
+ String *decl = Getattr(n, "decl");
+ String *declaration = NewString("");
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *terminator_code = NewString("");
+ String *tm;
+ Parm *p;
+ int i;
+ Wrapper *w = NewWrapper();
+ ParmList *l = Getattr(n, "parms");
+ bool is_void = !(Cmp(returntype, "void"));
+ String *qualified_return = 0;
+ bool pure_virtual = (!(Cmp(storage, "virtual")) && !(Cmp(value, "0")));
+ int status = SWIG_OK;
+ bool output_director = true;
+ String *dirclassname = directorClassName(parent);
+ String *qualified_name = NewStringf("%s::%s", dirclassname, name);
+ SwigType *c_ret_type = NULL;
+ String *jupcall_args = NewString("");
+ String *imclass_dmethod;
+ String *callback_typedef_parms = NewString("");
+ String *delegate_parms = NewString("");
+ String *proxy_method_types = NewString("");
+ String *callback_def = NewString("");
+ String *callback_code = NewString("");
+ String *imcall_args = NewString("");
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+
+ // Kludge Alert: functionWrapper sets sym:overload properly, but it
+ // isn't at this point, so we have to manufacture it ourselves. At least
+ // we're consistent with the sym:overload name in functionWrapper. (?? when
+ // does the overloaded method name get set?)
+
+ imclass_dmethod = NewStringf("SwigDirector_%s", Swig_name_member(getNSpace(), getClassPrefix(), overloaded_name));
+
+ qualified_return = SwigType_rcaststr(returntype, "c_result");
+
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ String *base_typename = SwigType_base(returntype);
+ String *resolved_typename = SwigType_typedef_resolve_all(base_typename);
+ Symtab *symtab = Getattr(n, "sym:symtab");
+ Node *typenode = Swig_symbol_clookup(resolved_typename, symtab);
+
+ if (SwigType_ispointer(returntype) || (typenode && Getattr(typenode, "abstracts"))) {
+ /* initialize pointers to something sane. Same for abstract
+ classes when a reference is returned. */
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ } else {
+ /* If returning a reference, initialize the pointer to a sane
+ default - if a C# exception occurs, then the pointer returns
+ something other than a NULL-initialized reference. */
+ SwigType *noref_type = SwigType_del_reference(Copy(returntype));
+ String *noref_ltype = SwigType_lstr(noref_type, 0);
+ String *return_ltype = SwigType_lstr(returntype, 0);
+
+ Wrapper_add_localv(w, "result_default", "static", noref_ltype, "result_default", NIL);
+ Wrapper_add_localv(w, "c_result", return_ltype, "c_result", NIL);
+ Printf(w->code, "result_default = SwigValueInit< %s >();\n", noref_ltype);
+ Printf(w->code, "c_result = &result_default;\n");
+ Delete(return_ltype);
+ Delete(noref_ltype);
+ Delete(noref_type);
+ }
+
+ Delete(base_typename);
+ Delete(resolved_typename);
+ }
+ } else {
+ SwigType *vt;
+
+ vt = cplus_value_type(returntype);
+ if (!vt) {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), NIL);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(vt, "c_result"), NIL);
+ Delete(vt);
+ }
+ }
+ }
+
+ /* Create the intermediate class wrapper */
+ tm = Swig_typemap_lookup("imtype", n, "", 0);
+ if (tm) {
+ String *imtypeout = Getattr(n, "tmap:imtype:out"); // the type in the imtype typemap's out attribute overrides the type in the typemap
+ if (imtypeout)
+ tm = imtypeout;
+ const String *im_directoroutattributes = Getattr(n, "tmap:imtype:directoroutattributes");
+ if (im_directoroutattributes) {
+ Printf(callback_def, " %s\n", im_directoroutattributes);
+ if (!ignored_method)
+ Printf(director_delegate_definitions, " %s\n", im_directoroutattributes);
+ }
+
+ Printf(callback_def, " private %s SwigDirectorMethod%s(", tm, overloaded_name);
+ if (!ignored_method) {
+ const String *csdirectordelegatemodifiers = Getattr(n, "feature:csdirectordelegatemodifiers");
+ String *modifiers = (csdirectordelegatemodifiers ? NewStringf("%s%s", csdirectordelegatemodifiers, Len(csdirectordelegatemodifiers) > 0 ? " " : "") : NewStringf("public "));
+ Printf(director_delegate_definitions, " %sdelegate %s", modifiers, tm);
+ Delete(modifiers);
+ }
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s\n", SwigType_str(returntype, 0));
+ }
+
+ if ((c_ret_type = Swig_typemap_lookup("ctype", n, "", 0))) {
+ if (!is_void && !ignored_method) {
+ String *jretval_decl = NewStringf("%s jresult", c_ret_type);
+ Wrapper_add_localv(w, "jresult", jretval_decl, "= 0", NIL);
+ Delete(jretval_decl);
+ }
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CTYPE_UNDEF, input_file, line_number, "No ctype typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ Swig_director_parms_fixup(l);
+
+ /* Attach the standard typemaps */
+ Swig_typemap_attach_parms("out", l, 0);
+ Swig_typemap_attach_parms("ctype", l, 0);
+ Swig_typemap_attach_parms("imtype", l, 0);
+ Swig_typemap_attach_parms("cstype", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("csdirectorin", l, 0);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ /* Preamble code */
+ if (!ignored_method)
+ Printf(w->code, "if (!swig_callback%s) {\n", overloaded_name);
+
+ if (!pure_virtual) {
+ String *super_call = Swig_method_call(super, l);
+ if (is_void) {
+ Printf(w->code, "%s;\n", super_call);
+ if (!ignored_method)
+ Printf(w->code, "return;\n");
+ } else {
+ Printf(w->code, "return %s;\n", super_call);
+ }
+ Delete(super_call);
+ } else {
+ Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"%s::%s\");\n", SwigType_namestr(c_classname), SwigType_namestr(name));
+ if (!is_void)
+ Printf(w->code, "return %s;", qualified_return);
+ else if (!ignored_method)
+ Printf(w->code, "return;\n");
+ }
+
+ if (!ignored_method)
+ Printf(w->code, "} else {\n");
+
+ /* Go through argument list, convert from native to C# */
+ for (i = 0, p = l; p; ++i) {
+ /* Is this superfluous? */
+ while (checkAttribute(p, "tmap:directorin:numinputs", "0")) {
+ p = Getattr(p, "tmap:directorin:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = makeParameterName(n, p, i, false);
+ String *c_param_type = NULL;
+ String *c_decl = NewString("");
+ String *arg = NewString("");
+
+ Printf(arg, "j%s", ln);
+
+ /* And add to the upcall args */
+ if (i > 0)
+ Printf(jupcall_args, ", ");
+ Printf(jupcall_args, "%s", arg);
+
+ /* Get parameter's intermediary C type */
+ if ((c_param_type = Getattr(p, "tmap:ctype"))) {
+ String *ctypeout = Getattr(p, "tmap:ctype:out"); // the type in the ctype typemap's out attribute overrides the type in the typemap
+ if (ctypeout)
+ c_param_type = ctypeout;
+
+ /* Add to local variables */
+ Printf(c_decl, "%s %s", c_param_type, arg);
+ if (!ignored_method)
+ Wrapper_add_localv(w, arg, c_decl, (!(SwigType_ispointer(pt) || SwigType_isreference(pt)) ? "" : "= 0"), NIL);
+
+ /* Add input marshalling code */
+ if ((tm = Getattr(p, "tmap:directorin"))) {
+
+ Setattr(p, "emit:directorinput", arg);
+ Replaceall(tm, "$input", arg);
+ Replaceall(tm, "$owner", "0");
+
+ if (Len(tm))
+ if (!ignored_method)
+ Printf(w->code, "%s\n", tm);
+
+ /* Add C type to callback typedef */
+ if (i > 0)
+ Printf(callback_typedef_parms, ", ");
+ Printf(callback_typedef_parms, "%s", c_param_type);
+
+ /* Add parameter to the intermediate class code if generating the
+ * intermediate's upcall code */
+ if ((tm = Getattr(p, "tmap:imtype"))) {
+
+ String *imtypeout = Getattr(p, "tmap:imtype:out"); // the type in the imtype typemap's out attribute overrides the type in the typemap
+ if (imtypeout)
+ tm = imtypeout;
+ const String *im_directorinattributes = Getattr(p, "tmap:imtype:directorinattributes");
+
+ String *din = Copy(Getattr(p, "tmap:csdirectorin"));
+
+ if (din) {
+ Replaceall(din, "$module", module_class_name);
+ Replaceall(din, "$imclassname", imclass_name);
+ substituteClassname(pt, din);
+ Replaceall(din, "$iminput", ln);
+
+ // pre and post attribute support
+ String *pre = Getattr(p, "tmap:csdirectorin:pre");
+ if (pre) {
+ substituteClassname(pt, pre);
+ Replaceall(pre, "$iminput", ln);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:csdirectorin:post");
+ if (post) {
+ substituteClassname(pt, post);
+ Replaceall(post, "$iminput", ln);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ String *terminator = Getattr(p, "tmap:csdirectorin:terminator");
+ if (terminator) {
+ substituteClassname(pt, terminator);
+ Replaceall(terminator, "$iminput", ln);
+ if (Len(terminator_code) > 0)
+ Insert(terminator_code, 0, "\n");
+ Insert(terminator_code, 0, terminator);
+ }
+
+ if (i > 0) {
+ Printf(delegate_parms, ", ");
+ Printf(proxy_method_types, ", ");
+ Printf(imcall_args, ", ");
+ }
+ Printf(delegate_parms, "%s%s %s", im_directorinattributes ? im_directorinattributes : empty_string, tm, ln);
+
+ if (Cmp(din, ln)) {
+ Printv(imcall_args, din, NIL);
+ } else
+ Printv(imcall_args, ln, NIL);
+
+ /* Get the C# parameter type */
+ if ((tm = Getattr(p, "tmap:cstype"))) {
+ substituteClassname(pt, tm);
+ int flags = DOH_REPLACE_FIRST | DOH_REPLACE_ID_BEGIN | DOH_REPLACE_NOCOMMENT;
+ if (Replace(tm, "ref ", "", flags) || Replace(tm, "ref\t", "", flags)) {
+ Printf(proxy_method_types, "typeof(%s).MakeByRefType()", tm);
+ } else if (Replace(tm, "out ", "", flags) || Replace(tm, "out\t", "", flags)) {
+ Printf(proxy_method_types, "typeof(%s).MakeByRefType()", tm);
+ } else {
+ Printf(proxy_method_types, "typeof(%s)", tm);
+ }
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSDIRECTORIN_UNDEF, input_file, line_number, "No csdirectorin typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ p = Getattr(p, "tmap:directorin:next");
+
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CSDIRECTORIN_UNDEF, input_file, line_number,
+ "No or improper directorin typemap defined for argument %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ p = nextSibling(p);
+ output_director = false;
+ }
+ } else {
+ Swig_warning(WARN_CSHARP_TYPEMAP_CTYPE_UNDEF, input_file, line_number, "No ctype typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ p = nextSibling(p);
+ }
+
+ Delete(ln);
+ Delete(arg);
+ Delete(c_decl);
+ }
+
+ /* header declaration, start wrapper definition */
+ String *target;
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Add any exception specifications to the methods in the director class
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ ParmList *throw_parm_list = NULL;
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list)
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+ Printf(w->def, "%s", SwigType_str(Getattr(p, "type"), 0));
+ Printf(declaration, "%s", SwigType_str(Getattr(p, "type"), 0));
+ }
+ }
+
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+
+ /* Finish off the inherited upcall's definition */
+
+ Printf(callback_def, "%s)", delegate_parms);
+ Printf(callback_def, " {\n");
+
+ /* Emit the intermediate class's upcall to the actual class */
+
+ String *upcall = NewStringf("%s(%s)", symname, imcall_args);
+
+ if ((tm = Swig_typemap_lookup("csdirectorout", n, "", 0))) {
+ substituteClassname(returntype, tm);
+ Replaceall(tm, "$cscall", upcall);
+ if (!is_void)
+ Insert(tm, 0, "return ");
+ Replaceall(tm, "\n ", "\n "); // add extra indentation to code in typemap
+
+ // pre and post attribute support
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ bool is_terminator_code = Len(terminator_code) > 0;
+ if (is_pre_code && is_post_code)
+ Printf(callback_code, "%s\n try {\n %s;\n } finally {\n%s\n }\n", pre_code, tm, post_code);
+ else if (is_pre_code)
+ Printf(callback_code, "%s\n %s;\n", pre_code, tm);
+ else if (is_post_code)
+ Printf(callback_code, " try {\n %s;\n } finally {\n%s\n }\n", tm, post_code);
+ else
+ Printf(callback_code, " %s;\n", tm);
+ if (is_terminator_code)
+ Printv(callback_code, "\n", terminator_code, NIL);
+ }
+
+ Printf(callback_code, " }\n");
+ Delete(upcall);
+
+ if (!ignored_method) {
+ if (!is_void)
+ Printf(w->code, "jresult = (%s) ", c_ret_type);
+
+ Printf(w->code, "swig_callback%s(%s);\n", overloaded_name, jupcall_args);
+
+ if (!is_void) {
+ String *jresult_str = NewString("jresult");
+ String *result_str = NewString("c_result");
+
+ /* Copy jresult into c_result... */
+ if ((tm = Swig_typemap_lookup("directorout", n, result_str, w))) {
+ Replaceall(tm, "$input", jresult_str);
+ Replaceall(tm, "$result", result_str);
+ Printf(w->code, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s used in %s::%s (skipping director method)\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ Delete(jresult_str);
+ Delete(result_str);
+ }
+
+ /* Marshal outputs */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout"))) {
+ canThrow(n, "directorargout", p);
+ Replaceall(tm, "$result", "jresult");
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Terminate wrapper code */
+ Printf(w->code, "}\n");
+ if (!is_void)
+ Printf(w->code, "return %s;", qualified_return);
+ }
+
+ Printf(w->code, "}");
+
+ // We expose virtual protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewString("");
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+
+ /* emit the director method */
+ if (status == SWIG_OK && output_director) {
+ if (!is_void) {
+ Replaceall(w->code, "$null", qualified_return);
+ } else {
+ Replaceall(w->code, "$null", "");
+ }
+ if (!ignored_method)
+ Printv(director_delegate_callback, "\n", callback_def, callback_code, NIL);
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+
+ if (!ignored_method) {
+ /* Emit the actual upcall through */
+ UpcallData *udata = addUpcallMethod(imclass_dmethod, symname, decl, overloaded_name);
+ String *methid = Getattr(udata, "class_methodidx");
+ Setattr(n, "upcalldata", udata);
+ /*
+ Printf(stdout, "setting upcalldata, nodeType: %s %s::%s %p\n", nodeType(n), classname, Getattr(n, "name"), n);
+ */
+
+ Printf(director_callback_typedefs, " typedef %s (SWIGSTDCALL* SWIG_Callback%s_t)(", c_ret_type, methid);
+ Printf(director_callback_typedefs, "%s);\n", callback_typedef_parms);
+ Printf(director_callbacks, " SWIG_Callback%s_t swig_callback%s;\n", methid, overloaded_name);
+
+ Printf(director_delegate_definitions, " SwigDelegate%s_%s(%s);\n", classname, methid, delegate_parms);
+ Printf(director_delegate_instances, " private SwigDelegate%s_%s swigDelegate%s;\n", classname, methid, methid);
+ Printf(director_method_types, " private static global::System.Type[] swigMethodTypes%s = new global::System.Type[] { %s };\n", methid, proxy_method_types);
+ Printf(director_connect_parms, "SwigDirector%s%s delegate%s", classname, methid, methid);
+ }
+
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(terminator_code);
+ Delete(qualified_return);
+ Delete(declaration);
+ Delete(callback_typedef_parms);
+ Delete(delegate_parms);
+ Delete(proxy_method_types);
+ Delete(callback_def);
+ Delete(callback_code);
+ Delete(dirclassname);
+ DelWrapper(w);
+
+ return status;
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorConstructor()
+ * ------------------------------------------------------------ */
+
+ int classDirectorConstructor(Node *n) {
+ Node *parent = parentNode(n);
+ String *decl = Getattr(n, "decl");
+ String *supername = Swig_class_name(parent);
+ String *dirclassname = directorClassName(parent);
+ String *sub = NewString("");
+ Parm *p;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms;
+ int argidx = 0;
+
+ /* Assign arguments to superclass's parameters, if not already done */
+ for (p = superparms; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+
+ if (!pname) {
+ pname = NewStringf("arg%d", argidx++);
+ Setattr(p, "name", pname);
+ }
+ }
+
+ // TODO: Is this copy needed?
+ parms = CopyParmList(superparms);
+
+ if (!Getattr(n, "defaultargs")) {
+ /* constructor */
+ {
+ String *basetype = Getattr(parent, "classtype");
+ String *target = Swig_method_decl(0, decl, dirclassname, parms, 0);
+ String *call = Swig_csuperclass_call(0, basetype, superparms);
+
+ Printf(f_directors, "%s::%s : %s, %s {\n", dirclassname, target, call, Getattr(parent, "director:ctor"));
+ Printf(f_directors, " swig_init_callbacks();\n");
+ Printf(f_directors, "}\n\n");
+
+ Delete(target);
+ Delete(call);
+ }
+
+ /* constructor header */
+ {
+ String *target = Swig_method_decl(0, decl, dirclassname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+
+ Delete(sub);
+ Delete(supername);
+ Delete(parms);
+ Delete(dirclassname);
+ return Language::classDirectorConstructor(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorDefaultConstructor()
+ * ------------------------------------------------------------ */
+
+ int classDirectorDefaultConstructor(Node *n) {
+ String *dirclassname = directorClassName(n);
+ String *classtype = SwigType_namestr(Getattr(n, "name"));
+ Wrapper *w = NewWrapper();
+
+ Printf(w->def, "%s::%s() : %s {", dirclassname, dirclassname, Getattr(n, "director:ctor"));
+ Printf(w->code, "}\n");
+ Wrapper_print(w, f_directors);
+
+ Printf(f_directors_h, " %s();\n", dirclassname);
+ DelWrapper(w);
+ Delete(classtype);
+ Delete(dirclassname);
+ return Language::classDirectorDefaultConstructor(n);
+ }
+
+
+ /* ------------------------------------------------------------
+ * classDirectorInit()
+ * ------------------------------------------------------------ */
+
+ int classDirectorInit(Node *n) {
+ Delete(none_comparison);
+ none_comparison = NewString(""); // not used
+
+ Delete(director_ctor_code);
+ director_ctor_code = NewString("$director_new");
+
+ directorDeclaration(n);
+
+ Printf(f_directors_h, "%s {\n", Getattr(n, "director:decl"));
+ Printf(f_directors_h, "\npublic:\n");
+
+ /* Keep track of the director methods for this class */
+ first_class_dmethod = curr_class_dmethod = n_dmethods;
+
+ director_callback_typedefs = NewString("");
+ director_callbacks = NewString("");
+ director_delegate_callback = NewString("");
+ director_delegate_definitions = NewString("");
+ director_delegate_instances = NewString("");
+ director_method_types = NewString("");
+ director_connect_parms = NewString("");
+
+ return Language::classDirectorInit(n);
+ }
+
+ int classDeclaration(Node *n) {
+ String *old_director_callback_typedefs = director_callback_typedefs;
+ String *old_director_callbacks = director_callbacks;
+ String *old_director_delegate_callback = director_delegate_callback;
+ String *old_director_delegate_definitions = director_delegate_definitions;
+ String *old_director_delegate_instances = director_delegate_instances;
+ String *old_director_method_types = director_method_types;
+ String *old_director_connect_parms = director_connect_parms;
+
+ int ret = Language::classDeclaration(n);
+
+ // these variables are deleted in emitProxyClassDefAndCPPCasts, hence no Delete here
+ director_callback_typedefs = old_director_callback_typedefs;
+ director_callbacks = old_director_callbacks;
+ director_delegate_callback = old_director_delegate_callback;
+ director_delegate_definitions = old_director_delegate_definitions;
+ director_delegate_instances = old_director_delegate_instances;
+ director_method_types = old_director_method_types;
+ director_connect_parms = old_director_connect_parms;
+
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * classDirectorDestructor()
+ * ---------------------------------------------------------------------- */
+
+ int classDirectorDestructor(Node *n) {
+ Node *current_class = getCurrentClass();
+ String *dirclassname = directorClassName(current_class);
+ Wrapper *w = NewWrapper();
+
+ if (Getattr(n, "noexcept")) {
+ Printf(f_directors_h, " virtual ~%s() noexcept;\n", dirclassname);
+ Printf(w->def, "%s::~%s() noexcept {\n", dirclassname, dirclassname);
+ } else if (Getattr(n, "throw")) {
+ Printf(f_directors_h, " virtual ~%s() throw();\n", dirclassname);
+ Printf(w->def, "%s::~%s() throw() {\n", dirclassname, dirclassname);
+ } else {
+ Printf(f_directors_h, " virtual ~%s();\n", dirclassname);
+ Printf(w->def, "%s::~%s() {\n", dirclassname, dirclassname);
+ }
+
+ Printv(w->code, "}\n", NIL);
+
+ Wrapper_print(w, f_directors);
+
+ DelWrapper(w);
+ Delete(dirclassname);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorEnd()
+ * ------------------------------------------------------------ */
+
+ int classDirectorEnd(Node *n) {
+ int i;
+ String *dirclassname = directorClassName(n);
+
+ Wrapper *w = NewWrapper();
+
+ if (Len(director_callback_typedefs) > 0) {
+ Printf(f_directors_h, "\n%s", director_callback_typedefs);
+ }
+
+ Printf(f_directors_h, " void swig_connect_director(");
+
+ Printf(w->def, "void %s::swig_connect_director(", dirclassname);
+
+ for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *methid = Getattr(udata, "class_methodidx");
+ String *overname = Getattr(udata, "overname");
+
+ Printf(f_directors_h, "SWIG_Callback%s_t callback%s", methid, overname);
+ Printf(w->def, "SWIG_Callback%s_t callback%s", methid, overname);
+ Printf(w->code, "swig_callback%s = callback%s;\n", overname, overname);
+ if (i != curr_class_dmethod - 1) {
+ Printf(f_directors_h, ", ");
+ Printf(w->def, ", ");
+ }
+ }
+
+ Printf(f_directors_h, ");\n");
+ Printf(w->def, ") {");
+
+
+ if (Len(director_callbacks) > 0) {
+ Printf(f_directors_h, "\nprivate:\n%s", director_callbacks);
+ }
+ Printf(f_directors_h, " void swig_init_callbacks();\n");
+ Printf(f_directors_h, "};\n\n");
+ Printf(w->code, "}\n\n");
+
+ Printf(w->code, "void %s::swig_init_callbacks() {\n", dirclassname);
+ for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *overname = Getattr(udata, "overname");
+ Printf(w->code, "swig_callback%s = 0;\n", overname);
+ }
+ Printf(w->code, "}");
+
+ Wrapper_print(w, f_directors);
+
+ DelWrapper(w);
+ Delete(dirclassname);
+
+ return Language::classDirectorEnd(n);
+ }
+
+ /* --------------------------------------------------------------------
+ * classDirectorDisown()
+ * ------------------------------------------------------------------*/
+ virtual int classDirectorDisown(Node *n) {
+ (void) n;
+ return SWIG_OK;
+ }
+
+ /*----------------------------------------------------------------------
+ * extraDirectorProtectedCPPMethodsRequired()
+ *--------------------------------------------------------------------*/
+
+ bool extraDirectorProtectedCPPMethodsRequired() const {
+ return false;
+ }
+
+ /*----------------------------------------------------------------------
+ * directorDeclaration()
+ *
+ * Generate the director class's declaration
+ * e.g. "class SwigDirector_myclass : public myclass, public Swig::Director {"
+ *--------------------------------------------------------------------*/
+
+ void directorDeclaration(Node *n) {
+
+ String *base = Getattr(n, "classtype");
+ String *class_ctor = NewString("Swig::Director()");
+
+ String *dirclassname = directorClassName(n);
+ String *declaration = Swig_class_declaration(n, dirclassname);
+
+ Printf(declaration, " : public %s, public Swig::Director", base);
+
+ // Stash stuff for later.
+ Setattr(n, "director:decl", declaration);
+ Setattr(n, "director:ctor", class_ctor);
+
+ Delete(dirclassname);
+ }
+
+ /*----------------------------------------------------------------------
+ * nestedClassesSupport()
+ *--------------------------------------------------------------------*/
+
+ NestedClassSupport nestedClassesSupport() const {
+ return NCS_Full;
+ }
+}; /* class CSHARP */
+
+/* -----------------------------------------------------------------------------
+ * swig_csharp() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_csharp() {
+ return new CSHARP();
+}
+extern "C" Language *swig_csharp(void) {
+ return new_swig_csharp();
+}
+
+/* -----------------------------------------------------------------------------
+ * Static member variables
+ * ----------------------------------------------------------------------------- */
+
+const char *CSHARP::usage = "\
+C# Options (available with -csharp)\n\
+ -dllimport <dl> - Override DllImport attribute name to <dl>\n\
+ -namespace <nm> - Generate wrappers into C# namespace <nm>\n\
+ -noproxy - Generate the low-level functional interface instead\n\
+ of proxy classes\n\
+ -oldvarnames - Old intermediary method names for variable wrappers\n\
+ -outfile <file> - Write all C# into a single <file> located in the output directory\n\
+\n";
diff --git a/contrib/tools/swig/Source/Modules/d.cxx b/contrib/tools/swig/Source/Modules/d.cxx
new file mode 100644
index 0000000000..31f300f2ea
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/d.cxx
@@ -0,0 +1,4649 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * d.cxx
+ *
+ * D language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+#include <ctype.h>
+
+// Hash type used for storing information about director callbacks for a class.
+typedef DOH UpcallData;
+
+class D : public Language {
+ static const char *usage;
+ const String *empty_string;
+ const String *public_string;
+ const String *protected_string;
+
+ /*
+ * Files and file sections containing C/C++ code.
+ */
+ File *f_begin;
+ File *f_runtime;
+ File *f_runtime_h;
+ File *f_header;
+ File *f_wrappers;
+ File *f_init;
+ File *f_directors;
+ File *f_directors_h;
+ List *filenames_list;
+
+ /*
+ * Command line-set modes of operation.
+ */
+ // Whether a single proxy D module is generated or classes and enums are
+ // written to their own files.
+ bool split_proxy_dmodule;
+
+ // The major D version targeted (currently 1 or 2).
+ unsigned short d_version;
+
+ /*
+ * State variables which indicate what is being wrapped at the moment.
+ * This is probably not the most elegant way of handling state, but it has
+ * proven to work in the C# and Java modules.
+ */
+ // Indicates if wrapping a native function.
+ bool native_function_flag;
+
+ // Indicates if wrapping a static functions or member variables
+ bool static_flag;
+
+ // Indicates if wrapping a nonstatic member variable
+ bool variable_wrapper_flag;
+
+ // Indicates if wrapping a member variable/enum/const.
+ bool wrapping_member_flag;
+
+ // Indicates if wrapping a global variable.
+ bool global_variable_flag;
+
+ // Name of a variable being wrapped.
+ String *variable_name;
+
+ /*
+ * Variables temporarily holding the generated C++ code.
+ */
+ // C++ code for the generated wrapper functions for casts up the C++
+ // for inheritance hierarchies.
+ String *upcasts_code;
+
+ // Function pointer typedefs for handling director callbacks on the C++ side.
+ String *director_callback_typedefs;
+
+ // Variables for storing the function pointers to the director callbacks on
+ // the C++ side.
+ String *director_callback_pointers;
+
+ /*
+ * Names of generated D entities.
+ */
+ // The name of the D module containing the interface to the C wrapper.
+ String *im_dmodule_name;
+
+ // The fully qualified name of the wrap D module (package name included).
+ String *im_dmodule_fq_name;
+
+ // The name of the proxy module which exposes the (SWIG) module contents as a
+ // D module.
+ String *proxy_dmodule_name;
+
+ // The fully qualified name of the proxy D module.
+ String *proxy_dmodule_fq_name;
+
+ // Optional: Package the D modules are placed in (set via the -package
+ // command line option).
+ String *package;
+
+ // The directory the generated D module files are written to. Is constructed
+ // from the package path if a target package is set, points to the general
+ // output directory otherwise.
+ String *dmodule_directory;
+
+ // The name of the library which contains the C wrapper (used when generating
+ // the dynamic library loader). Can be overridden via the -wrapperlibrary
+ // command line flag.
+ String *wrap_library_name;
+
+ /*
+ * Variables temporarily holding the generated D code.
+ */
+ // Import statements written to the intermediary D module header set via
+ // %pragma(d) imdmoduleimports.
+ String *im_dmodule_imports;
+
+ // The code for the intermediary D module body.
+ String *im_dmodule_code;
+
+ // Import statements for all proxy modules (the main proxy module and, if in
+ // split proxy module mode, the proxy class modules) from
+ // %pragma(d) globalproxyimports.
+ String *global_proxy_imports;
+
+ // The D code for the main proxy modules. nspace_proxy_dmodules is a hash from
+ // the namespace name as key to an {"imports", "code"}. If the nspace feature
+ // is not active, only proxy_dmodule_imports and proxy_dmodule_code are used,
+ // which contain the code for the root proxy module.
+ //
+ // These variables should not be accessed directly but rather via the
+ // proxy{Imports, Code}Buffer)() helper functions which return the right
+ // buffer for a given namespace. If not in split proxy mode, they contain the
+ // whole proxy code.
+ String *proxy_dmodule_imports;
+ String *proxy_dmodule_code;
+ Hash *nspace_proxy_dmodules;
+
+ // The D code generated for the currently processed enum.
+ String *proxy_enum_code;
+
+ /*
+ * D data for the current proxy class.
+ *
+ * These strings are mainly used to temporarily accumulate code from the
+ * various member handling functions while a single class is processed and are
+ * no longer relevant once that class has been finished, i.e. after
+ * classHandler() has returned.
+ */
+ // The unqualified name of the current proxy class.
+ String *proxy_class_name;
+
+ // The name of the current proxy class, qualified with the name of the
+ // namespace it is in, if any.
+ String *proxy_class_qname;
+
+ // The import directives for the current proxy class. They are written to the
+ // same D module the proxy class is written to.
+ String *proxy_class_imports;
+
+ // Code for enumerations nested in the current proxy class. Is emitted earlier
+ // than the rest of the body to work around forward referencing-issues.
+ String *proxy_class_enums_code;
+
+ // The generated D code making up the body of the current proxy class.
+ String *proxy_class_body_code;
+
+ // D code which is emitted right after the proxy class.
+ String *proxy_class_epilogue_code;
+
+ // The full code for the current proxy class, including the epilogue.
+ String* proxy_class_code;
+
+ // Contains a D call to the function wrapping C++ the destructor of the
+ // current class (if there is a public C++ destructor).
+ String *destructor_call;
+
+ // D code for the director callbacks generated for the current class.
+ String *director_dcallbacks_code;
+
+ /*
+ * Code for dynamically loading the wrapper library on the D side.
+ */
+ // D code which is inserted into the im D module if dynamic linking is used.
+ String *wrapper_loader_code;
+
+ // The D code to bind a function pointer to a library symbol.
+ String *wrapper_loader_bind_command;
+
+ // The cumulated binding commands binding all the functions declared in the
+ // intermediary D module to the C/C++ library symbols.
+ String *wrapper_loader_bind_code;
+
+ /*
+ * Director data.
+ */
+ List *dmethods_seq;
+ Hash *dmethods_table;
+ int n_dmethods;
+ int first_class_dmethod;
+ int curr_class_dmethod;
+
+ /*
+ * SWIG types data.
+ */
+ // Collects information about encountered types SWIG does not know about (e.g.
+ // incomplete types). This is used later to generate type wrapper proxy
+ // classes for the unknown types.
+ Hash *unknown_types;
+
+
+public:
+ /* ---------------------------------------------------------------------------
+ * D::D()
+ * --------------------------------------------------------------------------- */
+ D():empty_string(NewString("")),
+ public_string(NewString("public")),
+ protected_string(NewString("protected")),
+ f_begin(NULL),
+ f_runtime(NULL),
+ f_runtime_h(NULL),
+ f_header(NULL),
+ f_wrappers(NULL),
+ f_init(NULL),
+ f_directors(NULL),
+ f_directors_h(NULL),
+ filenames_list(NULL),
+ split_proxy_dmodule(false),
+ d_version(1),
+ native_function_flag(false),
+ static_flag(false),
+ variable_wrapper_flag(false),
+ wrapping_member_flag(false),
+ global_variable_flag(false),
+ variable_name(NULL),
+ upcasts_code(NULL),
+ director_callback_typedefs(NULL),
+ director_callback_pointers(NULL),
+ im_dmodule_name(NULL),
+ im_dmodule_fq_name(NULL),
+ proxy_dmodule_name(NULL),
+ proxy_dmodule_fq_name(NULL),
+ package(NULL),
+ dmodule_directory(NULL),
+ wrap_library_name(NULL),
+ im_dmodule_imports(NULL),
+ im_dmodule_code(NULL),
+ global_proxy_imports(NULL),
+ proxy_dmodule_imports(NULL),
+ proxy_dmodule_code(NULL),
+ nspace_proxy_dmodules(NULL),
+ proxy_enum_code(NULL),
+ proxy_class_name(NULL),
+ proxy_class_qname(NULL),
+ proxy_class_imports(NULL),
+ proxy_class_enums_code(NULL),
+ proxy_class_body_code(NULL),
+ proxy_class_epilogue_code(NULL),
+ proxy_class_code(NULL),
+ destructor_call(NULL),
+ director_dcallbacks_code(NULL),
+ wrapper_loader_code(NULL),
+ wrapper_loader_bind_command(NULL),
+ wrapper_loader_bind_code(NULL),
+ dmethods_seq(NULL),
+ dmethods_table(NULL),
+ n_dmethods(0),
+ first_class_dmethod(0),
+ curr_class_dmethod(0),
+ unknown_types(NULL) {
+
+ // For now, multiple inheritance with directors is not possible. It should be
+ // easy to implement though.
+ director_multiple_inheritance = 0;
+ director_language = 1;
+
+ // Not used:
+ Delete(none_comparison);
+ none_comparison = NewString("");
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::main()
+ * --------------------------------------------------------------------------- */
+ virtual void main(int argc, char *argv[]) {
+ SWIG_library_directory("d");
+
+ // Look for certain command line options
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if ((strcmp(argv[i], "-d2") == 0)) {
+ Swig_mark_arg(i);
+ d_version = 2;
+ } else if (strcmp(argv[i], "-wrapperlibrary") == 0) {
+ if (argv[i + 1]) {
+ wrap_library_name = NewString("");
+ Printf(wrap_library_name, argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-package") == 0) {
+ if (argv[i + 1]) {
+ package = NewString("");
+ Printf(package, argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if ((strcmp(argv[i], "-splitproxy") == 0)) {
+ Swig_mark_arg(i);
+ split_proxy_dmodule = true;
+ } else if (strcmp(argv[i], "-help") == 0) {
+ Printf(stdout, "%s\n", usage);
+ }
+ }
+ }
+
+ // Add a symbol to the parser for conditional compilation
+ Preprocessor_define("SWIGD 1", 0);
+
+ // Also make the target D version available as preprocessor symbol for
+ // use in our library files.
+ String *version_define = NewStringf("SWIG_D_VERSION %u", d_version);
+ Preprocessor_define(version_define, 0);
+ Delete(version_define);
+
+ // Add typemap definitions
+ SWIG_typemap_lang("d");
+ SWIG_config_file("d.swg");
+
+ allow_overloading();
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::top()
+ * --------------------------------------------------------------------------- */
+ virtual int top(Node *n) {
+ // Get any options set in the module directive
+ Node *optionsnode = Getattr(Getattr(n, "module"), "options");
+
+ if (optionsnode) {
+ if (Getattr(optionsnode, "imdmodulename")) {
+ im_dmodule_name = Copy(Getattr(optionsnode, "imdmodulename"));
+ }
+
+ if (Getattr(optionsnode, "directors")) {
+ // Check if directors are enabled for this module. Note: This is a
+ // "master switch", if it is not set, not director code will be emitted
+ // at all. %feature("director") statements are also required to enable
+ // directors for individual classes or methods.
+ //
+ // Use the »directors« attributte of the %module directive to enable
+ // director generation (e.g. »%module(directors="1") modulename«).
+ allow_directors();
+ }
+
+ if (Getattr(optionsnode, "dirprot")) {
+ allow_dirprot();
+ }
+
+ allow_allprotected(GetFlag(optionsnode, "allprotected"));
+ }
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+ String *outfile_h = Getattr(n, "outfile_h");
+
+ if (!outfile) {
+ Printf(stderr, "Unable to determine outfile\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+
+ if (directorsEnabled()) {
+ if (!outfile_h) {
+ Printf(stderr, "Unable to determine outfile_h\n");
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
+ if (!f_runtime_h) {
+ FileErrorDisplay(outfile_h);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+ f_directors_h = NewString("");
+ f_directors = NewString("");
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+
+ unknown_types = NewHash();
+ filenames_list = NewList();
+
+ // Make the package name and the resulting module output path.
+ if (package) {
+ // Append a dot so we can prepend the package variable directly to the
+ // module names in the rest of the code.
+ Printv(package, ".", NIL);
+ } else {
+ // Write the generated D modules to the »root« package by default.
+ package = NewString("");
+ }
+
+ dmodule_directory = Copy(SWIG_output_directory());
+ if (Len(package) > 0) {
+ String *package_directory = Copy(package);
+ Replaceall(package_directory, ".", SWIG_FILE_DELIMITER);
+ Printv(dmodule_directory, package_directory, NIL);
+ Delete(package_directory);
+ }
+
+ // Make the wrap and proxy D module names.
+ // The wrap module name can be set in the module directive.
+ if (!im_dmodule_name) {
+ im_dmodule_name = NewStringf("%s_im", Getattr(n, "name"));
+ }
+ im_dmodule_fq_name = NewStringf("%s%s", package, im_dmodule_name);
+ proxy_dmodule_name = Copy(Getattr(n, "name"));
+ proxy_dmodule_fq_name = NewStringf("%s%s", package, proxy_dmodule_name);
+
+ im_dmodule_code = NewString("");
+ proxy_class_imports = NewString("");
+ proxy_class_enums_code = NewString("");
+ proxy_class_body_code = NewString("");
+ proxy_class_epilogue_code = NewString("");
+ proxy_class_code = NewString("");
+ destructor_call = NewString("");
+ proxy_dmodule_code = NewString("");
+ proxy_dmodule_imports = NewString("");
+ nspace_proxy_dmodules = NewHash();
+ im_dmodule_imports = NewString("");
+ upcasts_code = NewString("");
+ global_proxy_imports = NewString("");
+ wrapper_loader_code = NewString("");
+ wrapper_loader_bind_command = NewString("");
+ wrapper_loader_bind_code = NewString("");
+ dmethods_seq = NewList();
+ dmethods_table = NewHash();
+ n_dmethods = 0;
+
+ // By default, expect the dynamically loaded wrapper library to be named
+ // [lib]<module>_wrap[.so/.dll].
+ if (!wrap_library_name)
+ wrap_library_name = NewStringf("%s_wrap", Getattr(n, "name"));
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "D");
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+
+ /* Emit initial director header and director code: */
+ Swig_banner(f_directors_h);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", proxy_dmodule_name);
+ Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", proxy_dmodule_name);
+
+ Printf(f_directors, "\n\n");
+ Printf(f_directors, "/* ---------------------------------------------------\n");
+ Printf(f_directors, " * C++ director class methods\n");
+ Printf(f_directors, " * --------------------------------------------------- */\n\n");
+ if (outfile_h) {
+ String *filename = Swig_file_filename(outfile_h);
+ Printf(f_directors, "#include \"%s\"\n\n", filename);
+ Delete(filename);
+ }
+ }
+
+ Printf(f_runtime, "\n");
+
+ Swig_name_register("wrapper", "D_%f");
+
+ Printf(f_wrappers, "\n#ifdef __cplusplus\n");
+ Printf(f_wrappers, "extern \"C\" {\n");
+ Printf(f_wrappers, "#endif\n\n");
+
+ // Emit all the wrapper code.
+ Language::top(n);
+
+ if (directorsEnabled()) {
+ // Insert director runtime into the f_runtime file (before %header section).
+ Swig_insert_file("director_common.swg", f_runtime);
+ Swig_insert_file("director.swg", f_runtime);
+ }
+
+ // Generate the wrap D module.
+ // TODO: Add support for »static« linking.
+ {
+ String *filen = NewStringf("%s%s.d", dmodule_directory, im_dmodule_name);
+ File *im_d_file = NewFile(filen, "w", SWIG_output_files());
+ if (!im_d_file) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ // Start writing out the intermediary class file.
+ emitBanner(im_d_file);
+
+ Printf(im_d_file, "module %s;\n", im_dmodule_fq_name);
+
+ Printv(im_d_file, im_dmodule_imports, "\n", NIL);
+
+ Replaceall(wrapper_loader_code, "$wraplibrary", wrap_library_name);
+ Replaceall(wrapper_loader_code, "$wrapperloaderbindcode", wrapper_loader_bind_code);
+ Replaceall(wrapper_loader_code, "$module", proxy_dmodule_name);
+ Printf(im_d_file, "%s\n", wrapper_loader_code);
+
+ // Add the wrapper function declarations.
+ replaceModuleVariables(im_dmodule_code);
+ Printv(im_d_file, im_dmodule_code, NIL);
+
+ Delete(im_d_file);
+ }
+
+ // Generate the main D proxy module.
+ {
+ String *filen = NewStringf("%s%s.d", dmodule_directory, proxy_dmodule_name);
+ File *proxy_d_file = NewFile(filen, "w", SWIG_output_files());
+ if (!proxy_d_file) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ emitBanner(proxy_d_file);
+
+ Printf(proxy_d_file, "module %s;\n", proxy_dmodule_fq_name);
+ Printf(proxy_d_file, "\nstatic import %s;\n", im_dmodule_fq_name);
+ Printv(proxy_d_file, global_proxy_imports, NIL);
+ Printv(proxy_d_file, proxy_dmodule_imports, NIL);
+ Printv(proxy_d_file, "\n", NIL);
+
+ // Write a D type wrapper class for each SWIG type to the proxy module code.
+ for (Iterator swig_type = First(unknown_types); swig_type.key; swig_type = Next(swig_type)) {
+ writeTypeWrapperClass(swig_type.key, swig_type.item);
+ }
+
+ // Add the proxy functions (and classes, if they are not written to a separate file).
+ replaceModuleVariables(proxy_dmodule_code);
+ Printv(proxy_d_file, proxy_dmodule_code, NIL);
+
+ Delete(proxy_d_file);
+ }
+
+ // Generate the additional proxy modules for nspace support.
+ for (Iterator it = First(nspace_proxy_dmodules); it.key; it = Next(it)) {
+ String *module_name = createLastNamespaceName(it.key);
+
+ String *filename = NewStringf("%s%s.d", outputDirectory(it.key), module_name);
+ File *file = NewFile(filename, "w", SWIG_output_files());
+ if (!file) {
+ FileErrorDisplay(filename);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(filename);
+
+ emitBanner(file);
+
+ Printf(file, "module %s%s.%s;\n", package, it.key, module_name);
+ Printf(file, "\nstatic import %s;\n", im_dmodule_fq_name);
+ Printv(file, global_proxy_imports, NIL);
+ Printv(file, Getattr(it.item, "imports"), NIL);
+ Printv(file, "\n", NIL);
+
+ String *code = Getattr(it.item, "code");
+ replaceModuleVariables(code);
+ Printv(file, code, NIL);
+
+ Delete(file);
+ Delete(module_name);
+ }
+
+ if (upcasts_code)
+ Printv(f_wrappers, upcasts_code, NIL);
+
+ Printf(f_wrappers, "#ifdef __cplusplus\n");
+ Printf(f_wrappers, "}\n");
+ Printf(f_wrappers, "#endif\n");
+
+ // Check for overwriting file problems on filesystems that are case insensitive
+ Iterator it1;
+ Iterator it2;
+ for (it1 = First(filenames_list); it1.item; it1 = Next(it1)) {
+ String *item1_lower = Swig_string_lower(it1.item);
+ for (it2 = Next(it1); it2.item; it2 = Next(it2)) {
+ String *item2_lower = Swig_string_lower(it2.item);
+ if (it1.item && it2.item) {
+ if (Strcmp(item1_lower, item2_lower) == 0) {
+ Swig_warning(WARN_LANG_PORTABILITY_FILENAME, input_file, line_number,
+ "Portability warning: File %s will be overwritten by %s on case insensitive filesystems such as "
+ "Windows' FAT32 and NTFS unless the class/module name is renamed\n", it1.item, it2.item);
+ }
+ }
+ Delete(item2_lower);
+ }
+ Delete(item1_lower);
+ }
+
+ Delete(unknown_types);
+ unknown_types = NULL;
+ Delete(filenames_list);
+ filenames_list = NULL;
+ Delete(im_dmodule_name);
+ im_dmodule_name = NULL;
+ Delete(im_dmodule_fq_name);
+ im_dmodule_fq_name = NULL;
+ Delete(im_dmodule_code);
+ im_dmodule_code = NULL;
+ Delete(proxy_class_imports);
+ proxy_class_imports = NULL;
+ Delete(proxy_class_enums_code);
+ proxy_class_enums_code = NULL;
+ Delete(proxy_class_body_code);
+ proxy_class_body_code = NULL;
+ Delete(proxy_class_epilogue_code);
+ proxy_class_epilogue_code = NULL;
+ Delete(proxy_class_code);
+ proxy_class_code = NULL;
+ Delete(destructor_call);
+ destructor_call = NULL;
+ Delete(proxy_dmodule_name);
+ proxy_dmodule_name = NULL;
+ Delete(proxy_dmodule_fq_name);
+ proxy_dmodule_fq_name = NULL;
+ Delete(proxy_dmodule_code);
+ proxy_dmodule_code = NULL;
+ Delete(proxy_dmodule_imports);
+ proxy_dmodule_imports = NULL;
+ Delete(nspace_proxy_dmodules);
+ nspace_proxy_dmodules = NULL;
+ Delete(im_dmodule_imports);
+ im_dmodule_imports = NULL;
+ Delete(upcasts_code);
+ upcasts_code = NULL;
+ Delete(global_proxy_imports);
+ global_proxy_imports = NULL;
+ Delete(wrapper_loader_code);
+ wrapper_loader_code = NULL;
+ Delete(wrapper_loader_bind_code);
+ wrapper_loader_bind_code = NULL;
+ Delete(wrapper_loader_bind_command);
+ wrapper_loader_bind_command = NULL;
+ Delete(dmethods_seq);
+ dmethods_seq = NULL;
+ Delete(dmethods_table);
+ dmethods_table = NULL;
+ Delete(package);
+ package = NULL;
+ Delete(dmodule_directory);
+ dmodule_directory = NULL;
+ n_dmethods = 0;
+
+ // Merge all the generated C/C++ code and close the output files.
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+
+ if (directorsEnabled()) {
+ Dump(f_directors, f_begin);
+ Dump(f_directors_h, f_runtime_h);
+
+ Printf(f_runtime_h, "\n");
+ Printf(f_runtime_h, "#endif\n");
+
+ Delete(f_runtime_h);
+ f_runtime_h = NULL;
+ Delete(f_directors);
+ f_directors = NULL;
+ Delete(f_directors_h);
+ f_directors_h = NULL;
+ }
+
+ Dump(f_wrappers, f_begin);
+ Wrapper_pretty_print(f_init, f_begin);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_runtime);
+ Delete(f_begin);
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::insertDirective()
+ * --------------------------------------------------------------------------- */
+ virtual int insertDirective(Node *n) {
+ int ret = SWIG_OK;
+ String *code = Getattr(n, "code");
+ String *section = Getattr(n, "section");
+ replaceModuleVariables(code);
+
+ if (!ImportMode && (Cmp(section, "proxycode") == 0)) {
+ if (proxy_class_body_code) {
+ Swig_typemap_replace_embedded_typemap(code, n);
+ Printv(proxy_class_body_code, code, NIL);
+ }
+ } else {
+ ret = Language::insertDirective(n);
+ }
+ return ret;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::pragmaDirective()
+ *
+ * Valid Pragmas:
+ * imdmodulecode - text (D code) is copied verbatim to the wrap module
+ * imdmoduleimports - import statements for the im D module
+ *
+ * proxydmodulecode - text (D code) is copied verbatim to the proxy module
+ * (the main proxy module if in split proxy mode).
+ * globalproxyimports - import statements inserted into _all_ proxy modules.
+ *
+ * wrapperloadercode - D code for loading the wrapper library (is copied to
+ * the im D module).
+ * wrapperloaderbindcommand - D code for binding a symbol from the wrapper
+ * library to the declaration in the im D module.
+ * --------------------------------------------------------------------------- */
+ virtual int pragmaDirective(Node *n) {
+ if (!ImportMode) {
+ String *lang = Getattr(n, "lang");
+ String *code = Getattr(n, "name");
+ String *value = Getattr(n, "value");
+
+ if (Strcmp(lang, "d") == 0) {
+ String *strvalue = NewString(value);
+ Replaceall(strvalue, "\\\"", "\"");
+
+ if (Strcmp(code, "imdmodulecode") == 0) {
+ Printf(im_dmodule_code, "%s\n", strvalue);
+ } else if (Strcmp(code, "imdmoduleimports") == 0) {
+ replaceImportTypeMacros(strvalue);
+ Chop(strvalue);
+ Printf(im_dmodule_imports, "%s\n", strvalue);
+ } else if (Strcmp(code, "proxydmodulecode") == 0) {
+ Printf(proxyCodeBuffer(0), "%s\n", strvalue);
+ } else if (Strcmp(code, "globalproxyimports") == 0) {
+ replaceImportTypeMacros(strvalue);
+ Chop(strvalue);
+ Printf(global_proxy_imports, "%s\n", strvalue);
+ } else if (Strcmp(code, "wrapperloadercode") == 0) {
+ Delete(wrapper_loader_code);
+ wrapper_loader_code = Copy(strvalue);
+ } else if (Strcmp(code, "wrapperloaderbindcommand") == 0) {
+ Delete(wrapper_loader_bind_command);
+ wrapper_loader_bind_command = Copy(strvalue);
+ } else {
+ Swig_error(input_file, line_number, "Unrecognized pragma.\n");
+ }
+ Delete(strvalue);
+ }
+ }
+ return Language::pragmaDirective(n);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::enumDeclaration()
+ *
+ * Wraps C/C++ enums as D enums.
+ * --------------------------------------------------------------------------- */
+ virtual int enumDeclaration(Node *n) {
+ if (ImportMode)
+ return SWIG_OK;
+
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ proxy_enum_code = NewString("");
+ String *symname = Getattr(n, "sym:name");
+ String *typemap_lookup_type = Getattr(n, "name");
+
+ // Emit the enum declaration.
+ if (typemap_lookup_type) {
+ const String *enummodifiers = lookupCodeTypemap(n, "dclassmodifiers", typemap_lookup_type, WARN_D_TYPEMAP_CLASSMOD_UNDEF);
+ Printv(proxy_enum_code, "\n", enummodifiers, " ", symname, " {\n", NIL);
+ } else {
+ // Handle anonymous enums.
+ Printv(proxy_enum_code, "\nenum {\n", NIL);
+ }
+
+ // Emit each enum item.
+ Language::enumDeclaration(n);
+
+ if (GetFlag(n, "nonempty")) {
+ // Finish the enum.
+ if (typemap_lookup_type) {
+ Printv(proxy_enum_code,
+ lookupCodeTypemap(n, "dcode", typemap_lookup_type, WARN_NONE), // Extra D code
+ "\n}\n", NIL);
+ } else {
+ // Handle anonymous enums.
+ Printv(proxy_enum_code, "\n}\n", NIL);
+ }
+ Replaceall(proxy_enum_code, "$dclassname", symname);
+ } else {
+ // D enum declarations must have at least one member to be legal, so emit
+ // an alias to int instead (their ctype/imtype is always int).
+ Delete(proxy_enum_code);
+ proxy_enum_code = NewStringf("\nalias int %s;\n", symname);
+ }
+
+ const String* imports =
+ lookupCodeTypemap(n, "dimports", typemap_lookup_type, WARN_NONE);
+ String* imports_trimmed;
+ if (Len(imports) > 0) {
+ imports_trimmed = Copy(imports);
+ Chop(imports_trimmed);
+ replaceImportTypeMacros(imports_trimmed);
+ Printv(imports_trimmed, "\n", NIL);
+ } else {
+ imports_trimmed = NewString("");
+ }
+
+ if (is_wrapping_class()) {
+ // Enums defined within the C++ class are written into the proxy
+ // class.
+ Printv(proxy_class_imports, imports_trimmed, NIL);
+ Printv(proxy_class_enums_code, proxy_enum_code, NIL);
+ } else {
+ // Write non-anonymous enums to their own file if in split proxy module
+ // mode.
+ if (split_proxy_dmodule && typemap_lookup_type) {
+ assertClassNameValidity(proxy_class_name);
+
+ String *nspace = Getattr(n, "sym:nspace");
+ String *output_directory = outputDirectory(nspace);
+ String *filename = NewStringf("%s%s.d", output_directory, symname);
+ Delete(output_directory);
+
+ File *class_file = NewFile(filename, "w", SWIG_output_files());
+ if (!class_file) {
+ FileErrorDisplay(filename);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filename));
+ Delete(filename);
+
+ emitBanner(class_file);
+ if (nspace) {
+ Printf(class_file, "module %s%s.%s;\n", package, nspace, symname);
+ } else {
+ Printf(class_file, "module %s%s;\n", package, symname);
+ }
+ Printv(class_file, imports_trimmed, NIL);
+
+ Printv(class_file, proxy_enum_code, NIL);
+
+ Delete(class_file);
+ } else {
+ String *nspace = Getattr(n, "sym:nspace");
+ Printv(proxyImportsBuffer(nspace), imports, NIL);
+ Printv(proxyCodeBuffer(nspace), proxy_enum_code, NIL);
+ }
+ }
+
+ Delete(imports_trimmed);
+
+ Delete(proxy_enum_code);
+ proxy_enum_code = NULL;
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::enumvalueDeclaration()
+ * --------------------------------------------------------------------------- */
+ virtual int enumvalueDeclaration(Node *n) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ Swig_require("enumvalueDeclaration", n, "*name", "?value", NIL);
+ String *value = Getattr(n, "value");
+ String *name = Getattr(n, "name");
+ Node *parent = parentNode(n);
+ String *tmpValue;
+
+ // Strange hack from parent method.
+ // RESEARCH: What is this doing?
+ if (value)
+ tmpValue = NewString(value);
+ else
+ tmpValue = NewString(name);
+ // Note that this is used in enumValue() amongst other places
+ Setattr(n, "value", tmpValue);
+
+ // Deal with enum values that are not int
+ int swigtype = SwigType_type(Getattr(n, "type"));
+ if (swigtype == T_BOOL) {
+ const char *val = Equal(Getattr(n, "enumvalue"), "true") ? "1" : "0";
+ Setattr(n, "enumvalue", val);
+ } else if (swigtype == T_CHAR) {
+ String *val = NewStringf("'%(escape)s'", Getattr(n, "enumvalue"));
+ Setattr(n, "enumvalue", val);
+ Delete(val);
+ }
+
+ // Emit the enum item.
+ {
+ if (!GetFlag(n, "firstenumitem"))
+ Printf(proxy_enum_code, ",\n");
+
+ Printf(proxy_enum_code, " %s", Getattr(n, "sym:name"));
+
+ // Check for the %dconstvalue feature
+ String *value = Getattr(n, "feature:d:constvalue");
+
+ // Note that in D, enum values must be compile-time constants. Thus,
+ // %dmanifestconst(0) (getting the enum values at runtime) is not supported.
+ value = value ? value : Getattr(n, "enumvalue");
+ if (value) {
+ Printf(proxy_enum_code, " = %s", value);
+ }
+
+ // Keep track that the currently processed enum has at least one value.
+ SetFlag(parent, "nonempty");
+ }
+
+ Delete(tmpValue);
+ Swig_restore(n);
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::memberfunctionHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int memberfunctionHandler(Node *n) {
+ Language::memberfunctionHandler(n);
+
+ String *overloaded_name = getOverloadedName(n);
+ String *intermediary_function_name =
+ Swig_name_member(getNSpace(), proxy_class_name, overloaded_name);
+ Setattr(n, "imfuncname", intermediary_function_name);
+
+ String *proxy_func_name = Getattr(n, "sym:name");
+ Setattr(n, "proxyfuncname", proxy_func_name);
+ if (split_proxy_dmodule &&
+ Len(Getattr(n, "parms")) == 0 &&
+ Strncmp(proxy_func_name, package, Len(proxy_func_name)) == 0) {
+ // If we are in split proxy mode and the function is named like the
+ // target package, the D compiler is unable to resolve the ambiguity
+ // between the package name and an argument-less function call.
+ // TODO: This might occur with nspace as well, augment the check.
+ Swig_warning(WARN_D_NAME_COLLISION, input_file, line_number,
+ "%s::%s might collide with the package name, consider using %%rename to resolve the ambiguity.\n",
+ proxy_class_name, proxy_func_name);
+ }
+
+ writeProxyClassFunction(n);
+
+ Delete(overloaded_name);
+
+ // For each function, look if we have to alias in the parent class function
+ // for the overload resolution process to work as expected from C++
+ // (http://www.digitalmars.com/d/2.0/function.html#function-inheritance).
+ // For multiple overloads, only emit the alias directive once (for the
+ // last method, »sym:nextSibling« is null then).
+ // Smart pointer classes do not mirror the inheritance hierarchy of the
+ // underlying types, so aliasing the base class methods in is not required
+ // for them.
+ // DMD BUG: We have to emit the alias after the last function because
+ // taking a delegate in the overload checking code fails otherwise
+ // (http://d.puremagic.com/issues/show_bug.cgi?id=4860).
+ if (!Getattr(n, "sym:nextSibling") && !is_smart_pointer() &&
+ !areAllOverloadsOverridden(n)) {
+ String *name = Getattr(n, "sym:name");
+ Printf(proxy_class_body_code, "\nalias $dbaseclass.%s %s;\n", name, name);
+ }
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::staticmemberfunctionHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int staticmemberfunctionHandler(Node *n) {
+ static_flag = true;
+
+ Language::staticmemberfunctionHandler(n);
+
+ String *overloaded_name = getOverloadedName(n);
+ String *intermediary_function_name =
+ Swig_name_member(getNSpace(), proxy_class_name, overloaded_name);
+ Setattr(n, "proxyfuncname", Getattr(n, "sym:name"));
+ Setattr(n, "imfuncname", intermediary_function_name);
+ writeProxyClassFunction(n);
+ Delete(overloaded_name);
+
+ static_flag = false;
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::globalvariableHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int globalvariableHandler(Node *n) {
+ variable_name = Getattr(n, "sym:name");
+ global_variable_flag = true;
+ int ret = Language::globalvariableHandler(n);
+ global_variable_flag = false;
+
+ return ret;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::membervariableHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int membervariableHandler(Node *n) {
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ variable_wrapper_flag = true;
+ Language::membervariableHandler(n);
+ wrapping_member_flag = false;
+ variable_wrapper_flag = false;
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::staticmembervariableHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int staticmembervariableHandler(Node *n) {
+ if (GetFlag(n, "feature:d:manifestconst") != 1) {
+ Delattr(n, "value");
+ }
+
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ static_flag = true;
+ Language::staticmembervariableHandler(n);
+ wrapping_member_flag = false;
+ static_flag = false;
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::memberconstantHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int memberconstantHandler(Node *n) {
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ Language::memberconstantHandler(n);
+ wrapping_member_flag = false;
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::constructorHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int constructorHandler(Node *n) {
+ Language::constructorHandler(n);
+
+ // Wrappers not wanted for some methods where the parameters cannot be overloadedprocess in D.
+ if (Getattr(n, "overload:ignore")) {
+ return SWIG_OK;
+ }
+
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ String *proxy_constructor_code = NewString("");
+ int i;
+
+ // Holds code for the constructor helper method generated only when the din
+ // typemap has code in the pre or post attributes.
+ String *helper_code = NewString("");
+ String *helper_args = NewString("");
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *terminator_code = NewString("");
+ NewString("");
+
+ String *overloaded_name = getOverloadedName(n);
+ String *mangled_overname = Swig_name_construct(getNSpace(), overloaded_name);
+ String *imcall = NewString("");
+
+ const String *methodmods = Getattr(n, "feature:d:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+
+ // Typemaps were attached earlier to the node, get the return type of the
+ // call to the C++ constructor wrapper.
+ const String *wrapper_return_type = lookupDTypemap(n, "imtype", true);
+
+ String *imtypeout = Getattr(n, "tmap:imtype:out");
+ if (imtypeout) {
+ // The type in the imtype typemap's out attribute overrides the type in
+ // the typemap itself.
+ wrapper_return_type = imtypeout;
+ }
+
+ Printf(proxy_constructor_code, "\n%s this(", methodmods);
+ Printf(helper_code, "static private %s SwigConstruct%s(",
+ wrapper_return_type, proxy_class_name);
+
+ Printv(imcall, im_dmodule_fq_name, ".", mangled_overname, "(", NIL);
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("in", l, NULL);
+ Swig_typemap_attach_parms("dtype", l, NULL);
+ Swig_typemap_attach_parms("din", l, NULL);
+
+ emit_mark_varargs(l);
+
+ int gencomma = 0;
+
+ /* Output each parameter */
+ Parm *p = l;
+ for (i = 0; p; i++) {
+ if (checkAttribute(p, "varargs:ignore", "1")) {
+ // Skip ignored varargs.
+ p = nextSibling(p);
+ continue;
+ }
+
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ // Skip ignored parameters.
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *param_type = NewString("");
+
+ // Get the D parameter type.
+ if ((tm = lookupDTypemap(p, "dtype", true))) {
+ const String *inattributes = Getattr(p, "tmap:dtype:inattributes");
+ Printf(param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DTYPE_UNDEF, input_file, line_number,
+ "No dtype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma)
+ Printf(imcall, ", ");
+
+ String *arg = makeParameterName(n, p, i, false);
+ String *parmtype = 0;
+
+ // Get the D code to convert the parameter value to the type used in the
+ // intermediary D module.
+ if ((tm = lookupDTypemap(p, "din"))) {
+ Replaceall(tm, "$dinput", arg);
+ String *pre = Getattr(p, "tmap:din:pre");
+ if (pre) {
+ replaceClassname(pre, pt);
+ Replaceall(pre, "$dinput", arg);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:din:post");
+ if (post) {
+ replaceClassname(post, pt);
+ Replaceall(post, "$dinput", arg);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ String *terminator = Getattr(p, "tmap:din:terminator");
+ if (terminator) {
+ replaceClassname(terminator, pt);
+ Replaceall(terminator, "$dinput", arg);
+ if (Len(terminator_code) > 0)
+ Insert(terminator_code, 0, "\n");
+ Insert(terminator_code, 0, terminator);
+ }
+ parmtype = Getattr(p, "tmap:din:parmtype");
+ if (parmtype)
+ Replaceall(parmtype, "$dinput", arg);
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DIN_UNDEF, input_file, line_number,
+ "No din typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to proxy function */
+ if (gencomma) {
+ Printf(proxy_constructor_code, ", ");
+ Printf(helper_code, ", ");
+ Printf(helper_args, ", ");
+ }
+ Printf(proxy_constructor_code, "%s %s", param_type, arg);
+ Printf(helper_code, "%s %s", param_type, arg);
+ Printf(helper_args, "%s", parmtype ? parmtype : arg);
+ ++gencomma;
+
+ Delete(parmtype);
+ Delete(arg);
+ Delete(param_type);
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ Printf(imcall, ")");
+
+ Printf(proxy_constructor_code, ")");
+ Printf(helper_code, ")");
+
+ // Insert the dconstructor typemap (replacing $directorconnect as needed).
+ Hash *attributes = NewHash();
+ String *typemap_lookup_type = Getattr(getCurrentClass(), "classtypeobj");
+ String *construct_tm = Copy(lookupCodeTypemap(n, "dconstructor",
+ typemap_lookup_type, WARN_D_TYPEMAP_DCONSTRUCTOR_UNDEF, attributes));
+ if (construct_tm) {
+ const bool use_director = (parentNode(n) && Swig_directorclass(n));
+ if (!use_director) {
+ Replaceall(construct_tm, "$directorconnect", "");
+ } else {
+ String *connect_attr = Getattr(attributes, "tmap:dconstructor:directorconnect");
+
+ if (connect_attr) {
+ Replaceall(construct_tm, "$directorconnect", connect_attr);
+ } else {
+ Swig_warning(WARN_D_NO_DIRECTORCONNECT_ATTR, input_file, line_number,
+ "\"directorconnect\" attribute missing in %s \"dconstructor\" typemap.\n",
+ Getattr(n, "name"));
+ Replaceall(construct_tm, "$directorconnect", "");
+ }
+ }
+
+ Printv(proxy_constructor_code, " ", construct_tm, NIL);
+ }
+
+ replaceExcode(n, proxy_constructor_code, "dconstructor", attributes);
+
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ bool is_terminator_code = Len(terminator_code) > 0;
+ if (is_pre_code || is_post_code || is_terminator_code) {
+ Printf(helper_code, " {\n");
+ if (is_pre_code) {
+ Printv(helper_code, pre_code, "\n", NIL);
+ }
+ if (is_post_code) {
+ Printf(helper_code, " try {\n");
+ Printv(helper_code, " return ", imcall, ";\n", NIL);
+ Printv(helper_code, " } finally {\n", post_code, "\n }", NIL);
+ } else {
+ Printv(helper_code, " return ", imcall, ";", NIL);
+ }
+ if (is_terminator_code) {
+ Printv(helper_code, "\n", terminator_code, NIL);
+ }
+ Printf(helper_code, "\n}\n");
+ String *helper_name = NewStringf("%s.SwigConstruct%s(%s)",
+ proxy_class_name, proxy_class_name, helper_args);
+ Replaceall(proxy_constructor_code, "$imcall", helper_name);
+ Delete(helper_name);
+ } else {
+ Replaceall(proxy_constructor_code, "$imcall", imcall);
+ }
+
+ Printv(proxy_class_body_code, proxy_constructor_code, "\n", NIL);
+
+ Delete(helper_args);
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(terminator_code);
+ Delete(construct_tm);
+ Delete(attributes);
+ Delete(overloaded_name);
+ Delete(imcall);
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::destructorHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int destructorHandler(Node *n) {
+ Language::destructorHandler(n);
+ String *symname = Getattr(n, "sym:name");
+
+ Printv(destructor_call, im_dmodule_fq_name, ".", Swig_name_destroy(getNSpace(),symname), "(cast(void*)swigCPtr)", NIL);
+ const String *methodmods = Getattr(n, "feature:d:methodmodifiers");
+ if (methodmods)
+ Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods);
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::classHandler()
+ * --------------------------------------------------------------------------- */
+ virtual int classHandler(Node *n) {
+ String *nspace = getNSpace();
+ File *class_file = NULL;
+
+ proxy_class_name = Copy(Getattr(n, "sym:name"));
+ if (nspace) {
+ proxy_class_qname = NewStringf("%s.%s", nspace, proxy_class_name);
+ } else {
+ proxy_class_qname = Copy(proxy_class_name);
+ }
+
+ if (!addSymbol(proxy_class_name, n, nspace)) {
+ return SWIG_ERROR;
+ }
+
+ assertClassNameValidity(proxy_class_name);
+
+ if (split_proxy_dmodule) {
+ String *output_directory = outputDirectory(nspace);
+ String *filename = NewStringf("%s%s.d", output_directory, proxy_class_name);
+ class_file = NewFile(filename, "w", SWIG_output_files());
+ Delete(output_directory);
+ if (!class_file) {
+ FileErrorDisplay(filename);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filename));
+ Delete(filename);
+
+ emitBanner(class_file);
+ if (nspace) {
+ Printf(class_file, "module %s%s.%s;\n", package, nspace, proxy_class_name);
+ } else {
+ Printf(class_file, "module %s%s;\n", package, proxy_class_name);
+ }
+ Printf(class_file, "\nstatic import %s;\n", im_dmodule_fq_name);
+ }
+
+ Clear(proxy_class_imports);
+ Clear(proxy_class_enums_code);
+ Clear(proxy_class_body_code);
+ Clear(proxy_class_epilogue_code);
+ Clear(proxy_class_code);
+ Clear(destructor_call);
+
+
+ // Traverse the tree for this class, using the *Handler()s to generate code
+ // to the proxy_class_* variables.
+ Language::classHandler(n);
+
+
+ writeProxyClassAndUpcasts(n);
+ writeDirectorConnectWrapper(n);
+
+ Replaceall(proxy_class_code, "$dclassname", proxy_class_name);
+
+ String *dclazzname = Swig_name_member(getNSpace(), proxy_class_name, "");
+ Replaceall(proxy_class_code, "$dclazzname", dclazzname);
+ Delete(dclazzname);
+
+ if (split_proxy_dmodule) {
+ Printv(class_file, global_proxy_imports, NIL);
+ Printv(class_file, proxy_class_imports, NIL);
+
+ replaceModuleVariables(proxy_class_code);
+ Printv(class_file, proxy_class_code, NIL);
+
+ Delete(class_file);
+ } else {
+ Printv(proxyImportsBuffer(getNSpace()), proxy_class_imports, NIL);
+ Printv(proxyCodeBuffer(getNSpace()), proxy_class_code, NIL);
+ }
+
+ Delete(proxy_class_qname);
+ proxy_class_qname = NULL;
+ Delete(proxy_class_name);
+ proxy_class_name = NULL;
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::constantWrapper()
+ *
+ * Used for wrapping constants declared by #define or %constant and also for
+ * (primitive) static member constants initialised inline.
+ *
+ * If the %dmanifestconst feature is used, the C/C++ constant value is used to
+ * initialize a D »const«. If not, a »getter« method is generated which
+ * retrieves the value via a call to the C wrapper. However, if there is a
+ * %dconstvalue specified, it overrides all other settings.
+ * --------------------------------------------------------------------------- */
+ virtual int constantWrapper(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ if (!addSymbol(symname, n))
+ return SWIG_ERROR;
+
+ // The %dmanifestconst feature determines if a D manifest constant
+ // (const/enum) or a getter function is created.
+ if (GetFlag(n, "feature:d:manifestconst") != 1) {
+ // Default constant handling will work with any type of C constant. It
+ // generates a getter function (which is the same as a read only property
+ // in D) which retrieves the value via by calling the C wrapper.
+ // Note that this is only called for global constants, static member
+ // constants are already handled in staticmemberfunctionHandler().
+
+ Swig_save("constantWrapper", n, "value", NIL);
+ Swig_save("constantWrapper", n, "tmap:ctype:out", "tmap:imtype:out", "tmap:dtype:out", "tmap:out:null", "tmap:imtype:outattributes", "tmap:dtype:outattributes", NIL);
+
+ // Add the stripped quotes back in.
+ String *old_value = Getattr(n, "value");
+ SwigType *t = Getattr(n, "type");
+ if (SwigType_type(t) == T_STRING) {
+ Setattr(n, "value", NewStringf("\"%s\"", old_value));
+ Delete(old_value);
+ } else if (SwigType_type(t) == T_CHAR) {
+ Setattr(n, "value", NewStringf("\'%s\'", old_value));
+ Delete(old_value);
+ }
+
+ SetFlag(n, "feature:immutable");
+ int result = globalvariableHandler(n);
+
+ Swig_restore(n);
+ return result;
+ }
+
+ String *constants_code = NewString("");
+ SwigType *t = Getattr(n, "type");
+ SwigType *valuetype = Getattr(n, "valuetype");
+ ParmList *l = Getattr(n, "parms");
+
+ // Attach the non-standard typemaps to the parameter list.
+ Swig_typemap_attach_parms("dtype", l, NULL);
+
+ // Get D return type.
+ String *return_type = NewString("");
+ String *tm;
+ if ((tm = lookupDTypemap(n, "dtype"))) {
+ String *dtypeout = Getattr(n, "tmap:dtype:out");
+ if (dtypeout) {
+ // The type in the out attribute of the typemap overrides the type
+ // in the dtype typemap.
+ tm = dtypeout;
+ replaceClassname(tm, t);
+ }
+ Printf(return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DTYPE_UNDEF, input_file, line_number,
+ "No dtype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ const String *itemname = wrapping_member_flag ? variable_name : symname;
+
+ String *attributes = Getattr(n, "feature:d:methodmodifiers");
+ if (attributes) {
+ attributes = Copy(attributes);
+ } else {
+ attributes = Copy(is_public(n) ? public_string : protected_string);
+ }
+
+ if (d_version == 1) {
+ if (static_flag) {
+ Printv(attributes, " static", NIL);
+ }
+ Printf(constants_code, "\n%s const %s %s = ", attributes, return_type, itemname);
+ } else {
+ Printf(constants_code, "\n%s enum %s %s = ", attributes, return_type, itemname);
+ }
+ Delete(attributes);
+
+ // Retrieve the override value set via %dconstvalue, if any.
+ String *override_value = Getattr(n, "feature:d:constvalue");
+ if (override_value) {
+ Printf(constants_code, "%s;\n", override_value);
+ } else {
+ // Just take the value from the C definition and hope it compiles in D.
+ if (Getattr(n, "wrappedasconstant")) {
+ if (SwigType_type(valuetype) == T_CHAR)
+ Printf(constants_code, "\'%(escape)s\';\n", Getattr(n, "staticmembervariableHandler:value"));
+ else
+ Printf(constants_code, "%s;\n", Getattr(n, "staticmembervariableHandler:value"));
+ } else {
+ // Add the stripped quotes back in.
+ String* value = Getattr(n, "value");
+ if (SwigType_type(t) == T_STRING) {
+ Printf(constants_code, "\"%s\";\n", value);
+ } else if (SwigType_type(t) == T_CHAR) {
+ Printf(constants_code, "\'%s\';\n", value);
+ } else {
+ Printf(constants_code, "%s;\n", value);
+ }
+ }
+ }
+
+ // Emit the generated code to appropriate place.
+ if (wrapping_member_flag) {
+ Printv(proxy_class_body_code, constants_code, NIL);
+ } else {
+ Printv(proxyCodeBuffer(getNSpace()), constants_code, NIL);
+ }
+
+ // Cleanup.
+ Delete(return_type);
+ Delete(constants_code);
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::functionWrapper()
+ *
+ * Generates the C wrapper code for a function and the corresponding
+ * declaration in the wrap D module.
+ * --------------------------------------------------------------------------- */
+ virtual int functionWrapper(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ Parm *p;
+ int i;
+ String *c_return_type = NewString("");
+ String *im_return_type = NewString("");
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+ String *body = NewString("");
+ int num_arguments = 0;
+ bool is_void_return;
+ String *overloaded_name = getOverloadedName(n);
+
+ if (!Getattr(n, "sym:overloaded")) {
+ if (!addSymbol(Getattr(n, "sym:name"), n))
+ return SWIG_ERROR;
+ }
+
+ // A new wrapper function object
+ Wrapper *f = NewWrapper();
+
+ // Make a wrapper name for this function
+ String *wname = Swig_name_wrapper(overloaded_name);
+
+ /* Attach the non-standard typemaps to the parameter list. */
+ Swig_typemap_attach_parms("ctype", l, f);
+ Swig_typemap_attach_parms("imtype", l, f);
+
+ /* Get return types */
+ if ((tm = lookupDTypemap(n, "ctype"))) {
+ String *ctypeout = Getattr(n, "tmap:ctype:out");
+ if (ctypeout) {
+ // The type in the ctype typemap's out attribute overrides the type in
+ // the typemap itself.
+ tm = ctypeout;
+ }
+ Printf(c_return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_CTYPE_UNDEF, input_file, line_number,
+ "No ctype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if ((tm = lookupDTypemap(n, "imtype"))) {
+ String *imtypeout = Getattr(n, "tmap:imtype:out");
+ if (imtypeout) {
+ // The type in the imtype typemap's out attribute overrides the type in
+ // the typemap itself.
+ tm = imtypeout;
+ }
+ Printf(im_return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_IMTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ is_void_return = (Cmp(c_return_type, "void") == 0);
+ if (!is_void_return)
+ Wrapper_add_localv(f, "jresult", c_return_type, "jresult", NIL);
+
+ Printv(f->def, " SWIGEXPORT ", c_return_type, " ", wname, "(", NIL);
+
+ // Emit all of the local variables for holding arguments.
+ emit_parameter_variables(l, f);
+
+ /* Attach the standard typemaps */
+ emit_attach_parmmaps(l, f);
+
+ // Parameter overloading
+ Setattr(n, "wrap:parms", l);
+ Setattr(n, "wrap:name", wname);
+
+ // Wrappers not wanted for some methods where the parameters cannot be overloaded in D
+ if (Getattr(n, "sym:overloaded")) {
+ // Emit warnings for the few cases that can't be overloaded in D and give up on generating wrapper
+ Swig_overload_check(n);
+ if (Getattr(n, "overload:ignore")) {
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+ }
+
+ // Collect the parameter list for the intermediary D module declaration of
+ // the generated wrapper function.
+ String *im_dmodule_parameters = NewString("(");
+
+ /* Get number of required and total arguments */
+ num_arguments = emit_num_arguments(l);
+ int gencomma = 0;
+
+ // Now walk the function parameter list and generate code to get arguments
+ for (i = 0, p = l; i < num_arguments; i++) {
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+ String *im_param_type = NewString("");
+ String *c_param_type = NewString("");
+ String *arg = NewString("");
+
+ Printf(arg, "j%s", ln);
+
+ /* Get the ctype types of the parameter */
+ if ((tm = lookupDTypemap(p, "ctype", true))) {
+ Printv(c_param_type, tm, NIL);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_CTYPE_UNDEF, input_file, line_number, "No ctype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Get the intermediary class parameter types of the parameter */
+ if ((tm = lookupDTypemap(p, "imtype", true))) {
+ const String *inattributes = Getattr(p, "tmap:imtype:inattributes");
+ Printf(im_param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_IMTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to intermediary class method */
+ if (gencomma)
+ Printf(im_dmodule_parameters, ", ");
+ Printf(im_dmodule_parameters, "%s %s", im_param_type, arg);
+
+ // Add parameter to C function
+ Printv(f->def, gencomma ? ", " : "", c_param_type, " ", arg, NIL);
+
+ gencomma = 1;
+
+ // Get typemap for this argument
+ if ((tm = Getattr(p, "tmap:in"))) {
+ canThrow(n, "in", p);
+ Replaceall(tm, "$input", arg);
+ Setattr(p, "emit:input", arg);
+ Printf(f->code, "%s\n", tm);
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ p = nextSibling(p);
+ }
+ Delete(im_param_type);
+ Delete(c_param_type);
+ Delete(arg);
+ }
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ canThrow(n, "check", p);
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ canThrow(n, "freearg", p);
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert argument output code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ canThrow(n, "argout", p);
+ Replaceall(tm, "$result", "jresult");
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Look for usage of throws typemap and the canthrow flag
+ ParmList *throw_parm_list = NULL;
+ if ((throw_parm_list = Getattr(n, "catchlist"))) {
+ Swig_typemap_attach_parms("throws", throw_parm_list, f);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ canThrow(n, "throws", p);
+ }
+ }
+ }
+
+ String *null_attribute = 0;
+ // Now write code to make the function call
+ if (!native_function_flag) {
+
+ Swig_director_emit_dynamic_cast(n, f);
+ String *actioncode = emit_action(n);
+
+ /* Return value if necessary */
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ canThrow(n, "out", n);
+ Replaceall(tm, "$result", "jresult");
+
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "1");
+ else
+ Replaceall(tm, "$owner", "0");
+
+ Printf(f->code, "%s", tm);
+ null_attribute = Getattr(n, "tmap:out:null");
+ if (Len(tm))
+ Printf(f->code, "\n");
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(t, 0), Getattr(n, "name"));
+ }
+ emit_return_variable(n, t, f);
+ }
+
+ /* Output argument output code */
+ Printv(f->code, outarg, NIL);
+
+ /* Output cleanup code */
+ Printv(f->code, cleanup, NIL);
+
+ /* Look to see if there is any newfree cleanup code */
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ canThrow(n, "newfree", n);
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if (!native_function_flag) {
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ canThrow(n, "ret", n);
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ // Complete D im parameter list and emit the declaration/binding code.
+ Printv(im_dmodule_parameters, ")", NIL);
+ writeImDModuleFunction(overloaded_name, im_return_type,
+ im_dmodule_parameters, wname);
+ Delete(im_dmodule_parameters);
+
+ // Finish C function header.
+ Printf(f->def, ") {");
+
+ if (!is_void_return)
+ Printv(f->code, " return jresult;\n", NIL);
+ Printf(f->code, "}\n");
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", symname);
+
+ /* Contract macro modification */
+ if (Replaceall(f->code, "SWIG_contract_assert(", "SWIG_contract_assert($null, ") > 0) {
+ Setattr(n, "d:canthrow", "1");
+ }
+
+ if (!null_attribute)
+ Replaceall(f->code, "$null", "0");
+ else
+ Replaceall(f->code, "$null", null_attribute);
+
+ /* Dump the function out */
+ if (!native_function_flag) {
+ Wrapper_print(f, f_wrappers);
+
+ // Handle %exception which sets the canthrow attribute.
+ if (Getattr(n, "feature:except:canthrow")) {
+ Setattr(n, "d:canthrow", "1");
+ }
+
+ // A very simple check (it is not foolproof) to assist typemap writers
+ // with setting the correct features when the want to throw D exceptions
+ // from C++ code. It checks for the common methods which set
+ // a pending D exception and issues a warning if one of them has been found
+ // in the typemap, but the »canthrow« attribute/feature is not set.
+ if (!Getattr(n, "d:canthrow")) {
+ if (Strstr(f->code, "SWIG_exception")) {
+ Swig_warning(WARN_D_CANTHROW_MISSING, input_file, line_number,
+ "C code contains a call to SWIG_exception and D code does not handle pending exceptions via the canthrow attribute.\n");
+ } else if (Strstr(f->code, "SWIG_DSetPendingException")) {
+ Swig_warning(WARN_D_CANTHROW_MISSING, input_file, line_number,
+ "C code contains a call to a SWIG_DSetPendingException method and D code does not handle pending exceptions via the canthrow attribute.\n");
+ }
+ }
+ }
+
+ // If we are not processing an enum or constant, and we were not generating
+ // a wrapper function which will be accessed via a proxy class, write a
+ // function to the proxy D module.
+ if (!is_wrapping_class()) {
+ writeProxyDModuleFunction(n);
+ }
+
+ // If we are processing a public member variable, write the property-style
+ // member function to the proxy class.
+ if (wrapping_member_flag) {
+ Setattr(n, "proxyfuncname", variable_name);
+ Setattr(n, "imfuncname", symname);
+
+ writeProxyClassFunction(n);
+ }
+
+ Delete(c_return_type);
+ Delete(im_return_type);
+ Delete(cleanup);
+ Delete(outarg);
+ Delete(body);
+ Delete(overloaded_name);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::nativeWrapper()
+ * --------------------------------------------------------------------------- */
+ virtual int nativeWrapper(Node *n) {
+ String *wrapname = Getattr(n, "wrap:name");
+
+ if (!addSymbol(wrapname, n))
+ return SWIG_ERROR;
+
+ if (Getattr(n, "type")) {
+ Swig_save("nativeWrapper", n, "name", NIL);
+ Setattr(n, "name", wrapname);
+ native_function_flag = true;
+ functionWrapper(n);
+ Swig_restore(n);
+ native_function_flag = false;
+ } else {
+ Swig_error(input_file, line_number, "No return type for %%native method %s.\n", Getattr(n, "wrap:name"));
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::classDirector()
+ * --------------------------------------------------------------------------- */
+ virtual int classDirector(Node *n) {
+ String *nspace = Getattr(n, "sym:nspace");
+ proxy_class_name = NewString(Getattr(n, "sym:name"));
+ if (nspace) {
+ proxy_class_qname = NewStringf("%s.%s", nspace, proxy_class_name);
+ } else {
+ proxy_class_qname = Copy(proxy_class_name);
+ }
+
+ int success = Language::classDirector(n);
+
+ Delete(proxy_class_qname);
+ proxy_class_qname = NULL;
+ Delete(proxy_class_name);
+ proxy_class_name = NULL;
+
+ return success;
+ }
+
+
+ /* ---------------------------------------------------------------------------
+ * D::classDirectorInit()
+ * --------------------------------------------------------------------------- */
+ virtual int classDirectorInit(Node *n) {
+ Delete(director_ctor_code);
+ director_ctor_code = NewString("$director_new");
+
+ // Write C++ director class declaration, for example:
+ // class SwigDirector_myclass : public myclass, public Swig::Director {
+ String *classname = Swig_class_name(n);
+ String *directorname = directorClassName(n);
+ String *declaration = Swig_class_declaration(n, directorname);
+ const String *base = Getattr(n, "classtype");
+
+ Printf(f_directors_h,
+ "%s : public %s, public Swig::Director {\n", declaration, base);
+ Printf(f_directors_h, "\npublic:\n");
+
+ Delete(declaration);
+ Delete(directorname);
+ Delete(classname);
+
+ // Stash for later.
+ Setattr(n, "director:ctor", NewString("Swig::Director()"));
+
+ // Keep track of the director methods for this class.
+ first_class_dmethod = curr_class_dmethod = n_dmethods;
+
+ director_callback_typedefs = NewString("");
+ director_callback_pointers = NewString("");
+ director_dcallbacks_code = NewString("");
+
+ return Language::classDirectorInit(n);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::classDirectorMethod()
+ *
+ * Emit a virtual director method to pass a method call on to the
+ * underlying D object.
+ * --------------------------------------------------------------------------- */
+ virtual int classDirectorMethod(Node *n, Node *parent, String *super) {
+ String *classname = Getattr(parent, "sym:name");
+ String *c_classname = Getattr(parent, "name");
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *returntype = Getattr(n, "type");
+ String *overloaded_name = getOverloadedName(n);
+ String *storage = Getattr(n, "storage");
+ String *value = Getattr(n, "value");
+ String *decl = Getattr(n, "decl");
+ String *declaration = NewString("");
+ String *tm;
+ Parm *p;
+ int i;
+ Wrapper *w = NewWrapper();
+ ParmList *l = Getattr(n, "parms");
+ bool is_void = !(Cmp(returntype, "void"));
+ String *qualified_return = 0;
+ bool pure_virtual = (!(Cmp(storage, "virtual")) && !(Cmp(value, "0")));
+ int status = SWIG_OK;
+ bool output_director = true;
+ String *dirclassname = directorClassName(parent);
+ String *qualified_name = NewStringf("%s::%s", dirclassname, name);
+ SwigType *c_ret_type = NULL;
+ String *dcallback_call_args = NewString("");
+ String *imclass_dmethod;
+ String *callback_typedef_parms = NewString("");
+ String *delegate_parms = NewString("");
+ String *proxy_method_param_list = NewString("");
+ String *proxy_callback_return_type = NewString("");
+ String *callback_def = NewString("");
+ String *callback_code = NewString("");
+ String *imcall_args = NewString("");
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+
+ // Kludge Alert: functionWrapper sets sym:overload properly, but it
+ // isn't at this point, so we have to manufacture it ourselves. At least
+ // we're consistent with the sym:overload name in functionWrapper. (?? when
+ // does the overloaded method name get set?)
+
+ imclass_dmethod = NewStringf("SwigDirector_%s", Swig_name_member(getNSpace(), classname, overloaded_name));
+
+ qualified_return = SwigType_rcaststr(returntype, "c_result");
+
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ String *base_typename = SwigType_base(returntype);
+ String *resolved_typename = SwigType_typedef_resolve_all(base_typename);
+ Symtab *symtab = Getattr(n, "sym:symtab");
+ Node *typenode = Swig_symbol_clookup(resolved_typename, symtab);
+
+ if (SwigType_ispointer(returntype) || (typenode && Getattr(typenode, "abstracts"))) {
+ /* initialize pointers to something sane. Same for abstract
+ classes when a reference is returned. */
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ } else {
+ /* If returning a reference, initialize the pointer to a sane
+ default - if a D exception occurs, then the pointer returns
+ something other than a NULL-initialized reference. */
+ SwigType *noref_type = SwigType_del_reference(Copy(returntype));
+ String *noref_ltype = SwigType_lstr(noref_type, 0);
+ String *return_ltype = SwigType_lstr(returntype, 0);
+
+ Wrapper_add_localv(w, "result_default", "static", noref_ltype, "result_default", NIL);
+ Wrapper_add_localv(w, "c_result", return_ltype, "c_result", NIL);
+ Printf(w->code, "result_default = SwigValueInit< %s >();\n", noref_ltype);
+ Printf(w->code, "c_result = &result_default;\n");
+ Delete(return_ltype);
+ Delete(noref_ltype);
+ Delete(noref_type);
+ }
+
+ Delete(base_typename);
+ Delete(resolved_typename);
+ }
+ } else {
+ SwigType *vt;
+
+ vt = cplus_value_type(returntype);
+ if (!vt) {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), NIL);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(vt, "c_result"), NIL);
+ Delete(vt);
+ }
+ }
+ }
+
+ /* Create the intermediate class wrapper */
+ tm = lookupDTypemap(n, "imtype");
+ if (tm) {
+ String *imtypeout = Getattr(n, "tmap:imtype:out");
+ if (imtypeout) {
+ // The type in the imtype typemap's out attribute overrides the type
+ // in the typemap.
+ tm = imtypeout;
+ }
+ Printf(callback_def, "\nprivate extern(C) %s swigDirectorCallback_%s_%s(void* dObject", tm, classname, overloaded_name);
+ Printv(proxy_callback_return_type, tm, NIL);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_IMTYPE_UNDEF, input_file, line_number,
+ "No imtype typemap defined for %s\n", SwigType_str(returntype, 0));
+ }
+
+ if ((c_ret_type = Swig_typemap_lookup("ctype", n, "", 0))) {
+ if (!is_void && !ignored_method) {
+ String *jretval_decl = NewStringf("%s jresult", c_ret_type);
+ Wrapper_add_localv(w, "jresult", jretval_decl, "= 0", NIL);
+ Delete(jretval_decl);
+ }
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_CTYPE_UNDEF, input_file, line_number,
+ "No ctype typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ Swig_director_parms_fixup(l);
+
+ // Attach the standard typemaps.
+ Swig_typemap_attach_parms("out", l, 0);
+ Swig_typemap_attach_parms("ctype", l, 0);
+ Swig_typemap_attach_parms("imtype", l, 0);
+ Swig_typemap_attach_parms("dtype", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("ddirectorin", l, 0);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ // Preamble code.
+ if (!ignored_method)
+ Printf(w->code, "if (!swig_callback_%s) {\n", overloaded_name);
+
+ if (!pure_virtual) {
+ String *super_call = Swig_method_call(super, l);
+ if (is_void) {
+ Printf(w->code, "%s;\n", super_call);
+ if (!ignored_method)
+ Printf(w->code, "return;\n");
+ } else {
+ Printf(w->code, "return %s;\n", super_call);
+ }
+ Delete(super_call);
+ } else {
+ Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"%s::%s\");\n", SwigType_namestr(c_classname), SwigType_namestr(name));
+ if (!is_void)
+ Printf(w->code, "return %s;", qualified_return);
+ else if (!ignored_method)
+ Printf(w->code, "return;\n");
+ }
+
+ if (!ignored_method)
+ Printf(w->code, "} else {\n");
+
+ // Go through argument list.
+ for (i = 0, p = l; p; ++i) {
+ /* Is this superfluous? */
+ while (checkAttribute(p, "tmap:directorin:numinputs", "0")) {
+ p = Getattr(p, "tmap:directorin:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = makeParameterName(n, p, i, false);
+ String *c_param_type = NULL;
+ String *c_decl = NewString("");
+ String *arg = NewString("");
+
+ Printf(arg, "j%s", ln);
+
+ // Add each parameter to the D callback invocation arguments.
+ Printf(dcallback_call_args, ", %s", arg);
+
+ /* Get parameter's intermediary C type */
+ if ((c_param_type = lookupDTypemap(p, "ctype", true))) {
+ String *ctypeout = Getattr(p, "tmap:ctype:out");
+ if (ctypeout) {
+ // The type in the ctype typemap's out attribute overrides the type
+ // in the typemap itself.
+ c_param_type = ctypeout;
+ }
+
+ /* Add to local variables */
+ Printf(c_decl, "%s %s", c_param_type, arg);
+ if (!ignored_method)
+ Wrapper_add_localv(w, arg, c_decl, (!(SwigType_ispointer(pt) || SwigType_isreference(pt)) ? "" : "= 0"), NIL);
+
+ /* Add input marshalling code */
+ if ((tm = Getattr(p, "tmap:directorin"))) {
+
+ Setattr(p, "emit:directorinput", arg);
+ Replaceall(tm, "$input", arg);
+ Replaceall(tm, "$owner", "0");
+
+ if (Len(tm))
+ if (!ignored_method)
+ Printf(w->code, "%s\n", tm);
+
+ // Add parameter type to the C typedef for the D callback function.
+ Printf(callback_typedef_parms, ", %s", c_param_type);
+
+ /* Add parameter to the intermediate class code if generating the
+ * intermediate's upcall code */
+ if ((tm = lookupDTypemap(p, "imtype", true))) {
+ String *imtypeout = Getattr(p, "tmap:imtype:out");
+ if (imtypeout) {
+ // The type in the imtype typemap's out attribute overrides the
+ // type in the typemap itself.
+ tm = imtypeout;
+ }
+ const String *im_directorinattributes = Getattr(p, "tmap:imtype:directorinattributes");
+
+ // TODO: Is this copy really needed?
+ String *din = Copy(lookupDTypemap(p, "ddirectorin", true));
+
+ if (din) {
+ Replaceall(din, "$winput", ln);
+
+ Printf(delegate_parms, ", ");
+ if (i > 0) {
+ Printf(proxy_method_param_list, ", ");
+ Printf(imcall_args, ", ");
+ }
+ Printf(delegate_parms, "%s%s %s", im_directorinattributes ? im_directorinattributes : empty_string, tm, ln);
+
+ if (Cmp(din, ln)) {
+ Printv(imcall_args, din, NIL);
+ } else {
+ Printv(imcall_args, ln, NIL);
+ }
+
+ Delete(din);
+
+ // Get the parameter type in the proxy D class (used later when
+ // generating the overload checking code for the directorConnect
+ // function).
+ if ((tm = lookupDTypemap(p, "dtype", true))) {
+ Printf(proxy_method_param_list, "%s", tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DTYPE_UNDEF, input_file, line_number,
+ "No dtype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DDIRECTORIN_UNDEF, input_file, line_number,
+ "No ddirectorin typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_IMTYPE_UNDEF, input_file, line_number,
+ "No imtype typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ p = Getattr(p, "tmap:directorin:next");
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DDIRECTORIN_UNDEF, input_file, line_number,
+ "No or improper directorin typemap defined for argument %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ p = nextSibling(p);
+ output_director = false;
+ }
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_CTYPE_UNDEF, input_file, line_number,
+ "No ctype typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ p = nextSibling(p);
+ }
+
+ Delete(arg);
+ Delete(c_decl);
+ Delete(c_param_type);
+ Delete(ln);
+ }
+
+ /* header declaration, start wrapper definition */
+ String *target;
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Add any exception specifications to the methods in the director class
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ ParmList *throw_parm_list = NULL;
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list)
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+ Printf(w->def, "%s", SwigType_str(Getattr(p, "type"), 0));
+ Printf(declaration, "%s", SwigType_str(Getattr(p, "type"), 0));
+ }
+ }
+
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+
+ // Finish the callback function declaraction.
+ Printf(callback_def, "%s)", delegate_parms);
+ Printf(callback_def, " {\n");
+
+ /* Emit the intermediate class's upcall to the actual class */
+
+ String *upcall = NewStringf("(cast(%s)dObject).%s(%s)", classname, symname, imcall_args);
+
+ if (!is_void) {
+ if ((tm = lookupDTypemap(n, "ddirectorout"))) {
+ Replaceall(tm, "$dcall", upcall);
+ Printf(callback_code, " return %s;\n", tm);
+ }
+ } else {
+ Printf(callback_code, " %s;\n", upcall);
+ }
+
+ Printf(callback_code, "}\n");
+ Delete(upcall);
+
+ if (!ignored_method) {
+ if (!is_void)
+ Printf(w->code, "jresult = (%s) ", c_ret_type);
+
+ Printf(w->code, "swig_callback_%s(d_object%s);\n", overloaded_name, dcallback_call_args);
+
+ if (!is_void) {
+ String *jresult_str = NewString("jresult");
+ String *result_str = NewString("c_result");
+
+ /* Copy jresult into c_result... */
+ if ((tm = Swig_typemap_lookup("directorout", n, result_str, w))) {
+ Replaceall(tm, "$input", jresult_str);
+ Replaceall(tm, "$result", result_str);
+ Printf(w->code, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s used in %s::%s (skipping director method)\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ Delete(jresult_str);
+ Delete(result_str);
+ }
+
+ /* Marshal outputs */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout"))) {
+ canThrow(n, "directorargout", p);
+ Replaceall(tm, "$result", "jresult");
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Terminate wrapper code */
+ Printf(w->code, "}\n");
+ if (!is_void)
+ Printf(w->code, "return %s;", qualified_return);
+ }
+
+ Printf(w->code, "}");
+
+ // We expose virtual protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewString("");
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+
+ /* emit the director method */
+ if (status == SWIG_OK && output_director) {
+ if (!is_void) {
+ Replaceall(w->code, "$null", qualified_return);
+ } else {
+ Replaceall(w->code, "$null", "");
+ }
+ if (!ignored_method)
+ Printv(director_dcallbacks_code, callback_def, callback_code, NIL);
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+
+ if (!ignored_method) {
+ // Register the upcall method so that the callback registering code can
+ // be written later.
+
+ // We cannot directly use n here because its »type« attribute does not
+ // the full return type any longer after Language::functionHandler has
+ // returned.
+ String *dp_return_type = lookupDTypemap(n, "dtype");
+ if (dp_return_type) {
+ String *dtypeout = Getattr(n, "tmap:dtype:out");
+ if (dtypeout) {
+ // The type in the dtype typemap's out attribute overrides the type
+ // in the typemap itself.
+ dp_return_type = dtypeout;
+ replaceClassname(dp_return_type, returntype);
+ }
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DTYPE_UNDEF, input_file, line_number,
+ "No dtype typemap defined for %s\n", SwigType_str(returntype, 0));
+ dp_return_type = NewString("");
+ }
+
+ UpcallData *udata = addUpcallMethod(imclass_dmethod, symname, decl, overloaded_name, dp_return_type, proxy_method_param_list);
+ Delete(dp_return_type);
+
+ // Write the global callback function pointer on the C code.
+ String *methid = Getattr(udata, "class_methodidx");
+
+ Printf(director_callback_typedefs, " typedef %s (* SWIG_Callback%s_t)", c_ret_type, methid);
+ Printf(director_callback_typedefs, "(void *dobj%s);\n", callback_typedef_parms);
+ Printf(director_callback_pointers, " SWIG_Callback%s_t swig_callback_%s;\n", methid, overloaded_name);
+
+ // Write the type alias for the callback to the intermediary D module.
+ String *proxy_callback_type = NewString("");
+ String *dirClassName = directorClassName(parent);
+ Printf(proxy_callback_type, "%s_Callback%s", dirClassName, methid);
+ Printf(im_dmodule_code, "alias extern(C) %s function(void*%s) %s;\n", proxy_callback_return_type, delegate_parms, proxy_callback_type);
+ Delete(proxy_callback_type);
+ Delete(dirClassName);
+ }
+
+ Delete(qualified_return);
+ Delete(c_ret_type);
+ Delete(declaration);
+ Delete(callback_typedef_parms);
+ Delete(delegate_parms);
+ Delete(proxy_method_param_list);
+ Delete(callback_def);
+ Delete(callback_code);
+ DelWrapper(w);
+
+ return status;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::classDirectorConstructor()
+ * --------------------------------------------------------------------------- */
+ virtual int classDirectorConstructor(Node *n) {
+ Node *parent = parentNode(n);
+ String *decl = Getattr(n, "decl");;
+ String *supername = Swig_class_name(parent);
+ String *dirclassname = directorClassName(parent);
+ String *sub = NewString("");
+ Parm *p;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms;
+ int argidx = 0;
+
+ /* Assign arguments to superclass's parameters, if not already done */
+ for (p = superparms; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+
+ if (!pname) {
+ pname = NewStringf("arg%d", argidx++);
+ Setattr(p, "name", pname);
+ }
+ }
+
+ // TODO: Is this copy needed?
+ parms = CopyParmList(superparms);
+
+ if (!Getattr(n, "defaultargs")) {
+ /* constructor */
+ {
+ String *basetype = Getattr(parent, "classtype");
+ String *target = Swig_method_decl(0, decl, dirclassname, parms, 0);
+ String *call = Swig_csuperclass_call(0, basetype, superparms);
+ String *classtype = SwigType_namestr(Getattr(n, "name"));
+
+ Printf(f_directors, "%s::%s : %s, %s {\n", dirclassname, target, call, Getattr(parent, "director:ctor"));
+ Printf(f_directors, " swig_init_callbacks();\n");
+ Printf(f_directors, "}\n\n");
+
+ Delete(classtype);
+ Delete(target);
+ Delete(call);
+ }
+
+ /* constructor header */
+ {
+ String *target = Swig_method_decl(0, decl, dirclassname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+
+ Delete(sub);
+ Delete(supername);
+ Delete(parms);
+ Delete(dirclassname);
+ return Language::classDirectorConstructor(n);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::classDirectorDefaultConstructor()
+ * --------------------------------------------------------------------------- */
+ virtual int classDirectorDefaultConstructor(Node *n) {
+ String *dirclassname = directorClassName(n);
+ String *classtype = SwigType_namestr(Getattr(n, "name"));
+ Wrapper *w = NewWrapper();
+
+ Printf(w->def, "%s::%s() : %s {", dirclassname, dirclassname, Getattr(n, "director:ctor"));
+ Printf(w->code, "}\n");
+ Wrapper_print(w, f_directors);
+
+ Printf(f_directors_h, " %s();\n", dirclassname);
+ DelWrapper(w);
+ Delete(classtype);
+ Delete(dirclassname);
+ return Language::classDirectorDefaultConstructor(n);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::classDirectorDestructor()
+ * --------------------------------------------------------------------------- */
+ virtual int classDirectorDestructor(Node *n) {
+ Node *current_class = getCurrentClass();
+ String *dirclassname = directorClassName(current_class);
+ Wrapper *w = NewWrapper();
+
+ if (Getattr(n, "noexcept")) {
+ Printf(f_directors_h, " virtual ~%s() noexcept;\n", dirclassname);
+ Printf(w->def, "%s::~%s() noexcept {\n", dirclassname, dirclassname);
+ } else if (Getattr(n, "throw")) {
+ Printf(f_directors_h, " virtual ~%s() throw();\n", dirclassname);
+ Printf(w->def, "%s::~%s() throw() {\n", dirclassname, dirclassname);
+ } else {
+ Printf(f_directors_h, " virtual ~%s();\n", dirclassname);
+ Printf(w->def, "%s::~%s() {\n", dirclassname, dirclassname);
+ }
+
+ Printv(w->code, "}\n", NIL);
+
+ Wrapper_print(w, f_directors);
+
+ DelWrapper(w);
+ Delete(dirclassname);
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::classDirectorEnd()
+ * --------------------------------------------------------------------------- */
+ virtual int classDirectorEnd(Node *n) {
+ int i;
+ String *director_classname = directorClassName(n);
+
+ Wrapper *w = NewWrapper();
+
+ if (Len(director_callback_typedefs) > 0) {
+ Printf(f_directors_h, "\n%s", director_callback_typedefs);
+ }
+
+ Printf(f_directors_h, " void swig_connect_director(void* dobj");
+
+ Printf(w->def, "void %s::swig_connect_director(void* dobj", director_classname);
+ Printf(w->code, "d_object = dobj;");
+
+ for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *methid = Getattr(udata, "class_methodidx");
+ String *overname = Getattr(udata, "overname");
+
+ Printf(f_directors_h, ", SWIG_Callback%s_t callback%s", methid, overname);
+ Printf(w->def, ", SWIG_Callback%s_t callback_%s", methid, overname);
+ Printf(w->code, "swig_callback_%s = callback_%s;\n", overname, overname);
+ }
+
+ Printf(f_directors_h, ");\n");
+ Printf(w->def, ") {");
+
+ Printf(f_directors_h, "\nprivate:\n");
+ Printf(f_directors_h, " void swig_init_callbacks();\n");
+ Printf(f_directors_h, " void *d_object;\n");
+ if (Len(director_callback_pointers) > 0) {
+ Printf(f_directors_h, "%s", director_callback_pointers);
+ }
+ Printf(f_directors_h, "};\n\n");
+ Printf(w->code, "}\n\n");
+
+ Printf(w->code, "void %s::swig_init_callbacks() {\n", director_classname);
+ for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *overname = Getattr(udata, "overname");
+ Printf(w->code, "swig_callback_%s = 0;\n", overname);
+ }
+ Printf(w->code, "}");
+
+ Wrapper_print(w, f_directors);
+
+ DelWrapper(w);
+
+ return Language::classDirectorEnd(n);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::classDirectorDisown()
+ * --------------------------------------------------------------------------- */
+ virtual int classDirectorDisown(Node *n) {
+ (void) n;
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::replaceSpecialVariables()
+ * --------------------------------------------------------------------------- */
+ virtual void replaceSpecialVariables(String *method, String *tm, Parm *parm) {
+ (void)method;
+ SwigType *type = Getattr(parm, "type");
+
+ // Just assume that this goes to the proxy class, we cannot know.
+ replaceClassname(tm, type);
+ }
+
+protected:
+ /* ---------------------------------------------------------------------------
+ * D::extraDirectorProtectedCPPMethodsRequired()
+ * --------------------------------------------------------------------------- */
+ virtual bool extraDirectorProtectedCPPMethodsRequired() const {
+ return false;
+ }
+
+private:
+ /* ---------------------------------------------------------------------------
+ * D::writeImDModuleFunction()
+ *
+ * Writes a function declaration for the given (C) wrapper function to the
+ * intermediary D module.
+ *
+ * d_name - The name the function in the intermediary D module will get.
+ * return type - The return type of the function in the C wrapper.
+ * parameters - The parameter list of the C wrapper function.
+ * wrapper_function_name - The name of the exported function in the C wrapper
+ * (usually d_name prefixed by »D_«).
+ * --------------------------------------------------------------------------- */
+ void writeImDModuleFunction(const_String_or_char_ptr d_name,
+ const_String_or_char_ptr return_type, const_String_or_char_ptr parameters,
+ const_String_or_char_ptr wrapper_function_name) {
+
+ // TODO: Add support for static linking here.
+ Printf(im_dmodule_code, "SwigExternC!(%s function%s) %s;\n", return_type,
+ parameters, d_name);
+ Printv(wrapper_loader_bind_code, wrapper_loader_bind_command, NIL);
+ Replaceall(wrapper_loader_bind_code, "$function", d_name);
+ Replaceall(wrapper_loader_bind_code, "$symbol", wrapper_function_name);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::writeProxyClassFunction()
+ *
+ * Creates a D proxy function for a C++ function in the wrapped class. Used
+ * for both static and non-static C++ class functions.
+ *
+ * The Node must contain two extra attributes.
+ * - "proxyfuncname": The name of the D proxy function.
+ * - "imfuncname": The corresponding function in the intermediary D module.
+ * --------------------------------------------------------------------------- */
+ void writeProxyClassFunction(Node *n) {
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *intermediary_function_name = Getattr(n, "imfuncname");
+ String *proxy_function_name = Getattr(n, "proxyfuncname");
+ String *tm;
+ Parm *p;
+ int i;
+ String *imcall = NewString("");
+ String *return_type = NewString("");
+ String *function_code = NewString("");
+ bool setter_flag = false;
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *terminator_code = NewString("");
+
+ // Wrappers not wanted for some methods where the parameters cannot be
+ // overloaded in D.
+ if (Getattr(n, "overload:ignore"))
+ return;
+
+ // Don't generate proxy method for additional explicitcall method used in
+ // directors.
+ if (GetFlag(n, "explicitcall"))
+ return;
+
+ // RESEARCH: What is this good for?
+ if (l) {
+ if (SwigType_type(Getattr(l, "type")) == T_VOID) {
+ l = nextSibling(l);
+ }
+ }
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("in", l, NULL);
+ Swig_typemap_attach_parms("dtype", l, NULL);
+ Swig_typemap_attach_parms("din", l, NULL);
+
+ // Get return types.
+ if ((tm = lookupDTypemap(n, "dtype"))) {
+ String *dtypeout = Getattr(n, "tmap:dtype:out");
+ if (dtypeout) {
+ // The type in the dtype typemap's out attribute overrides the type in
+ // the typemap.
+ tm = dtypeout;
+ replaceClassname(tm, t);
+ }
+ Printf(return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DTYPE_UNDEF, input_file, line_number,
+ "No dtype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if (wrapping_member_flag) {
+ // Check if this is a setter method for a public member.
+ const String *setter_name = Swig_name_set(getNSpace(),
+ Swig_name_member(0, proxy_class_name, variable_name));
+
+ if (Cmp(Getattr(n, "sym:name"), setter_name) == 0) {
+ setter_flag = true;
+ }
+ }
+
+ // Write function modifiers.
+ {
+ String *modifiers;
+
+ const String *mods_override = Getattr(n, "feature:d:methodmodifiers");
+ if (mods_override) {
+ modifiers = Copy(mods_override);
+ } else {
+ modifiers = Copy(is_public(n) ? public_string : protected_string);
+
+ if (Getattr(n, "override")) {
+ Printf(modifiers, " override");
+ }
+ }
+
+ if (is_smart_pointer()) {
+ // Smart pointer classes do not mirror the inheritance hierarchy of the
+ // underlying pointer type, so no override required.
+ Replaceall(modifiers, "override", "");
+ }
+
+ Chop(modifiers);
+
+ if (static_flag) {
+ Printf(modifiers, " static");
+ }
+
+ Printf(function_code, "%s ", modifiers);
+ Delete(modifiers);
+ }
+
+ // Complete the function declaration up to the parameter list.
+ Printf(function_code, "%s %s(", return_type, proxy_function_name);
+
+ // Write the wrapper function call up to the parameter list.
+ Printv(imcall, im_dmodule_fq_name, ".$imfuncname(", NIL);
+ if (!static_flag) {
+ Printf(imcall, "cast(void*)swigCPtr");
+ }
+
+ String *proxy_param_types = NewString("");
+
+ // Write the parameter list for the proxy function declaration and the
+ // wrapper function call.
+ emit_mark_varargs(l);
+ int gencomma = !static_flag;
+ for (i = 0, p = l; p; i++) {
+ // Ignored varargs.
+ if (checkAttribute(p, "varargs:ignore", "1")) {
+ p = nextSibling(p);
+ continue;
+ }
+
+ // Ignored parameters.
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ // Ignore the 'this' argument for variable wrappers.
+ if (!(variable_wrapper_flag && i == 0)) {
+ String *param_name = makeParameterName(n, p, i, setter_flag);
+ SwigType *pt = Getattr(p, "type");
+
+ // Write the wrapper function call argument.
+ {
+ if (gencomma) {
+ Printf(imcall, ", ");
+ }
+
+ if ((tm = lookupDTypemap(p, "din", true))) {
+ Replaceall(tm, "$dinput", param_name);
+ String *pre = Getattr(p, "tmap:din:pre");
+ if (pre) {
+ replaceClassname(pre, pt);
+ Replaceall(pre, "$dinput", param_name);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:din:post");
+ if (post) {
+ replaceClassname(post, pt);
+ Replaceall(post, "$dinput", param_name);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ String *terminator = Getattr(p, "tmap:din:terminator");
+ if (terminator) {
+ replaceClassname(terminator, pt);
+ Replaceall(terminator, "$dinput", param_name);
+ if (Len(terminator_code) > 0)
+ Insert(terminator_code, 0, "\n");
+ Insert(terminator_code, 0, terminator);
+ }
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DIN_UNDEF, input_file, line_number,
+ "No din typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+ }
+
+ // Write the D proxy function parameter.
+ {
+ String *proxy_type = NewString("");
+
+ if ((tm = lookupDTypemap(p, "dtype"))) {
+ const String *inattributes = Getattr(p, "tmap:dtype:inattributes");
+ Printf(proxy_type, "%s%s", inattributes ? inattributes : empty_string, tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DTYPE_UNDEF, input_file, line_number,
+ "No dtype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma >= 2) {
+ Printf(function_code, ", ");
+ Printf(proxy_param_types, ", ");
+ }
+ gencomma = 2;
+ Printf(function_code, "%s %s", proxy_type, param_name);
+ Append(proxy_param_types, proxy_type);
+
+ Delete(proxy_type);
+ }
+
+ Delete(param_name);
+ }
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ Printf(imcall, ")");
+ Printf(function_code, ") ");
+
+ if (d_version > 1 && wrapping_member_flag) {
+ Printf(function_code, "@property ");
+ }
+
+ if (wrapMemberFunctionAsDConst(n)) {
+ Printf(function_code, "const ");
+ }
+
+ // Lookup the code used to convert the wrapper return value to the proxy
+ // function return type.
+ if ((tm = lookupDTypemap(n, "dout"))) {
+ replaceExcode(n, tm, "dout", n);
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ bool is_terminator_code = Len(terminator_code) > 0;
+ if (is_pre_code || is_post_code || is_terminator_code) {
+ if (is_post_code) {
+ Insert(tm, 0, "\n try ");
+ Printv(tm, " finally {\n", post_code, "\n }", NIL);
+ } else {
+ Insert(tm, 0, "\n ");
+ }
+ if (is_pre_code) {
+ Insert(tm, 0, pre_code);
+ Insert(tm, 0, "\n");
+ }
+ if (is_terminator_code) {
+ Printv(tm, "\n", terminator_code, NIL);
+ }
+ Insert(tm, 0, "{");
+ Printv(tm, "}", NIL);
+ }
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "true");
+ else
+ Replaceall(tm, "$owner", "false");
+ replaceClassname(tm, t);
+
+ // For director methods: generate code to selectively make a normal
+ // polymorphic call or an explicit method call. Needed to prevent infinite
+ // recursion when calling director methods.
+ Node *explicit_n = Getattr(n, "explicitcallnode");
+ if (explicit_n && Swig_directorclass(getCurrentClass())) {
+ String *ex_overloaded_name = getOverloadedName(explicit_n);
+ String *ex_intermediary_function_name = Swig_name_member(getNSpace(), proxy_class_name, ex_overloaded_name);
+
+ String *ex_imcall = Copy(imcall);
+ Replaceall(ex_imcall, "$imfuncname", ex_intermediary_function_name);
+ Replaceall(imcall, "$imfuncname", intermediary_function_name);
+
+ String *excode = NewString("");
+ if (!Cmp(return_type, "void"))
+ Printf(excode, "if (swigIsMethodOverridden!(%s delegate(%s), %s function(%s), %s)()) %s; else %s",
+ return_type, proxy_param_types, return_type, proxy_param_types, proxy_function_name, ex_imcall, imcall);
+ else
+ Printf(excode, "((swigIsMethodOverridden!(%s delegate(%s), %s function(%s), %s)()) ? %s : %s)",
+ return_type, proxy_param_types, return_type, proxy_param_types, proxy_function_name, ex_imcall, imcall);
+
+ Clear(imcall);
+ Printv(imcall, excode, NIL);
+ Delete(ex_overloaded_name);
+ Delete(excode);
+ } else {
+ Replaceall(imcall, "$imfuncname", intermediary_function_name);
+ }
+ Replaceall(tm, "$imfuncname", intermediary_function_name);
+ Replaceall(tm, "$imcall", imcall);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DOUT_UNDEF, input_file, line_number,
+ "No dout typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ Delete(proxy_param_types);
+
+ // The whole function body is now in stored tm (if there was a matching type
+ // map, of course), so simply append it to the code buffer. The braces are
+ // included in the typemap.
+ Printv(function_code, tm, NIL);
+
+ // Write function code buffer to the class code.
+ Printv(proxy_class_body_code, "\n", function_code, "\n", NIL);
+
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(terminator_code);
+ Delete(function_code);
+ Delete(return_type);
+ Delete(imcall);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::writeProxyDModuleFunction()
+ * --------------------------------------------------------------------------- */
+ void writeProxyDModuleFunction(Node *n) {
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ Parm *p;
+ int i;
+ String *imcall = NewString("");
+ String *return_type = NewString("");
+ String *function_code = NewString("");
+ int num_arguments = 0;
+ String *overloaded_name = getOverloadedName(n);
+ String *func_name = NULL;
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *terminator_code = NewString("");
+
+ // RESEARCH: What is this good for?
+ if (l) {
+ if (SwigType_type(Getattr(l, "type")) == T_VOID) {
+ l = nextSibling(l);
+ }
+ }
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("dtype", l, NULL);
+ Swig_typemap_attach_parms("din", l, NULL);
+
+ /* Get return types */
+ if ((tm = lookupDTypemap(n, "dtype"))) {
+ String *dtypeout = Getattr(n, "tmap:dtype:out");
+ if (dtypeout) {
+ // The type in the dtype typemap's out attribute overrides the type in
+ // the typemap.
+ tm = dtypeout;
+ replaceClassname(tm, t);
+ }
+ Printf(return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DTYPE_UNDEF, input_file, line_number,
+ "No dtype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ /* Change function name for global variables */
+ if (global_variable_flag) {
+ // RESEARCH: Is the Copy() needed here?
+ func_name = Copy(variable_name);
+ } else {
+ func_name = Copy(Getattr(n, "sym:name"));
+ }
+
+ /* Start generating the function */
+ const String *outattributes = Getattr(n, "tmap:dtype:outattributes");
+ if (outattributes)
+ Printf(function_code, " %s\n", outattributes);
+
+ const String *methodmods = Getattr(n, "feature:d:methodmodifiers");
+ // TODO: Check if is_public(n) could possibly make any sense here
+ // (private global functions would be useless anyway?).
+ methodmods = methodmods ? methodmods : empty_string;
+
+ Printf(function_code, "\n%s%s %s(", methodmods, return_type, func_name);
+ Printv(imcall, im_dmodule_fq_name, ".", overloaded_name, "(", NIL);
+
+ /* Get number of required and total arguments */
+ num_arguments = emit_num_arguments(l);
+
+ int gencomma = 0;
+
+ /* Output each parameter */
+ for (i = 0, p = l; i < num_arguments; i++) {
+
+ /* Ignored parameters */
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *param_type = NewString("");
+
+ // Get the D parameter type.
+ if ((tm = lookupDTypemap(p, "dtype", true))) {
+ const String *inattributes = Getattr(p, "tmap:dtype:inattributes");
+ Printf(param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DTYPE_UNDEF, input_file, line_number,
+ "No dtype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma)
+ Printf(imcall, ", ");
+
+ const bool generating_setter = global_variable_flag || wrapping_member_flag;
+ String *arg = makeParameterName(n, p, i, generating_setter);
+
+ // Get the D code to convert the parameter value to the type used in the
+ // wrapper D module.
+ if ((tm = lookupDTypemap(p, "din", true))) {
+ Replaceall(tm, "$dinput", arg);
+ String *pre = Getattr(p, "tmap:din:pre");
+ if (pre) {
+ replaceClassname(pre, pt);
+ Replaceall(pre, "$dinput", arg);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:din:post");
+ if (post) {
+ replaceClassname(post, pt);
+ Replaceall(post, "$dinput", arg);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ String *terminator = Getattr(p, "tmap:din:terminator");
+ if (terminator) {
+ replaceClassname(terminator, pt);
+ Replaceall(terminator, "$dinput", arg);
+ if (Len(terminator_code) > 0)
+ Insert(terminator_code, 0, "\n");
+ Insert(terminator_code, 0, terminator);
+ }
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DIN_UNDEF, input_file, line_number,
+ "No din typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to module class function */
+ if (gencomma >= 2)
+ Printf(function_code, ", ");
+ gencomma = 2;
+ Printf(function_code, "%s %s", param_type, arg);
+
+ p = Getattr(p, "tmap:in:next");
+ Delete(arg);
+ Delete(param_type);
+ }
+
+ Printf(imcall, ")");
+ Printf(function_code, ") ");
+
+ if (global_variable_flag && (d_version > 1)) {
+ Printf(function_code, "@property ");
+ }
+
+ // Lookup the code used to convert the wrapper return value to the proxy
+ // function return type.
+ if ((tm = lookupDTypemap(n, "dout"))) {
+ replaceExcode(n, tm, "dout", n);
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ bool is_terminator_code = Len(terminator_code) > 0;
+ if (is_pre_code || is_post_code || is_terminator_code) {
+ if (is_post_code) {
+ Insert(tm, 0, "\n try ");
+ Printv(tm, " finally {\n", post_code, "\n }", NIL);
+ } else {
+ Insert(tm, 0, "\n ");
+ }
+ if (is_pre_code) {
+ Insert(tm, 0, pre_code);
+ Insert(tm, 0, "\n");
+ }
+ if (is_terminator_code) {
+ Printv(tm, "\n", terminator_code, NIL);
+ }
+ Insert(tm, 0, " {");
+ Printf(tm, "\n}");
+ }
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "true");
+ else
+ Replaceall(tm, "$owner", "false");
+ replaceClassname(tm, t);
+ Replaceall(tm, "$imfuncname", overloaded_name);
+ Replaceall(tm, "$imcall", imcall);
+ } else {
+ Swig_warning(WARN_D_TYPEMAP_DOUT_UNDEF, input_file, line_number,
+ "No dout typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ // The whole function code is now stored in tm (if there was a matching
+ // type map, of course), so simply append it to the code buffer.
+ Printf(function_code, "%s\n", tm ? (const String *) tm : empty_string);
+ Printv(proxyCodeBuffer(getNSpace()), function_code, NIL);
+
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(terminator_code);
+ Delete(function_code);
+ Delete(return_type);
+ Delete(imcall);
+ Delete(func_name);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::writeProxyClassAndUpcasts()
+ *
+ * Collects all the code fragments generated by the handler function while
+ * traversing the tree from the proxy_class_* variables and writes the
+ * class definition (including any epilogue code) to proxy_class_code.
+ *
+ * Also writes the upcast function to the wrapper layer when processing a
+ * derived class.
+ *
+ * Inputs:
+ * n – The class node currently processed.
+ * --------------------------------------------------------------------------- */
+ void writeProxyClassAndUpcasts(Node *n) {
+ SwigType *typemap_lookup_type = Getattr(n, "classtypeobj");
+
+ /*
+ * Handle inheriting from D and C++ classes.
+ */
+
+ String *c_classname = Getattr(n, "name");
+ String *c_baseclassname = NULL;
+ Node *basenode = NULL;
+ String *baseclass = NULL;
+
+ // Inheritance from pure D classes.
+ Node *attributes = NewHash();
+ const String *pure_baseclass =
+ lookupCodeTypemap(n, "dbase", typemap_lookup_type, WARN_NONE, attributes);
+ bool purebase_replace = GetFlag(attributes, "tmap:dbase:replace") ? true : false;
+ bool purebase_notderived = GetFlag(attributes, "tmap:dbase:notderived") ? true : false;
+ Delete(attributes);
+
+ // C++ inheritance.
+ if (!purebase_replace) {
+ List *baselist = Getattr(n, "bases");
+ if (baselist) {
+ Iterator base = First(baselist);
+ while (base.item) {
+ if (!GetFlag(base.item, "feature:ignore")) {
+ SwigType *baseclassname = Getattr(base.item, "name");
+ if (!c_baseclassname) {
+ basenode = base.item;
+ String *name = createProxyName(baseclassname);
+ if (name) {
+ c_baseclassname = baseclassname;
+ baseclass = name;
+ }
+ } else {
+ /* Warn about multiple inheritance for additional base class(es) */
+ String *proxyclassname = Getattr(n, "classtypeobj");
+ Swig_warning(WARN_D_MULTIPLE_INHERITANCE, Getfile(n), Getline(n),
+ "Base %s of class %s ignored: multiple inheritance is not supported in D.\n", SwigType_namestr(baseclassname), SwigType_namestr(proxyclassname));
+ }
+ }
+ base = Next(base);
+ }
+ }
+ }
+
+ bool derived = baseclass != NULL;
+
+ if (derived && purebase_notderived) {
+ pure_baseclass = empty_string;
+ }
+ const String *wanted_base = baseclass ? baseclass : pure_baseclass;
+
+ if (purebase_replace) {
+ wanted_base = pure_baseclass;
+ derived = false;
+ basenode = NULL;
+ baseclass = NULL;
+ if (purebase_notderived) {
+ Swig_error(Getfile(n), Getline(n),
+ "The dbase typemap for proxy %s must contain just one of the 'replace' or 'notderived' attributes.\n",
+ typemap_lookup_type);
+ }
+ } else if (baseclass && Len(pure_baseclass) > 0) {
+ Swig_warning(WARN_D_MULTIPLE_INHERITANCE, Getfile(n), Getline(n),
+ "Warning for %s, base class %s ignored. Multiple inheritance is not supported in D. "
+ "Perhaps you need one of the 'replace' or 'notderived' attributes in the dbase typemap?\n", typemap_lookup_type, pure_baseclass);
+ }
+
+ // Add code to do C++ casting to base class (only for classes in an inheritance hierarchy)
+ if (derived) {
+ writeClassUpcast(n, proxy_class_name, c_classname, c_baseclassname);
+ }
+
+ /*
+ * Write needed imports.
+ */
+ // If this class is derived from a C++ class, we need to have the D class
+ // generated for it in scope.
+ if (derived) {
+ requireDType(Getattr(basenode, "sym:nspace"), Getattr(basenode, "sym:name"));
+ }
+
+ // Write any custom import statements to the proxy module header.
+ const String *imports = lookupCodeTypemap(n, "dimports", typemap_lookup_type, WARN_NONE);
+ if (Len(imports) > 0) {
+ String* imports_trimmed = Copy(imports);
+ Chop(imports_trimmed);
+ replaceImportTypeMacros(imports_trimmed);
+ Printv(proxy_class_imports, imports_trimmed, "\n", NIL);
+ Delete(imports_trimmed);
+ }
+
+ /*
+ * Write the proxy class header.
+ */
+ // Class modifiers.
+ const String *modifiers =
+ lookupCodeTypemap(n, "dclassmodifiers", typemap_lookup_type, WARN_D_TYPEMAP_CLASSMOD_UNDEF);
+
+ // User-defined interfaces.
+ const String *interfaces =
+ lookupCodeTypemap(n, derived ? "dinterfaces_derived" : "dinterfaces", typemap_lookup_type, WARN_NONE);
+
+ Printv(proxy_class_code,
+ "\n",
+ modifiers,
+ " $dclassname",
+ (*Char(wanted_base) || *Char(interfaces)) ? " : " : "", wanted_base,
+ (*Char(wanted_base) && *Char(interfaces)) ? ", " : "", interfaces, " {",
+ NIL);
+
+ /*
+ * Write the proxy class body.
+ */
+ String* body = NewString("");
+
+ // Default class body.
+ const String *dbody;
+ if (derived) {
+ dbody = lookupCodeTypemap(n, "dbody_derived", typemap_lookup_type, WARN_D_TYPEMAP_DBODY_UNDEF);
+ } else {
+ dbody = lookupCodeTypemap(n, "dbody", typemap_lookup_type, WARN_D_TYPEMAP_DBODY_UNDEF);
+ }
+
+ Printv(body, dbody, NIL);
+
+ // Destructor and dispose().
+ // If the C++ destructor is accessible (public), it is wrapped by the
+ // dispose() method which is also called by the emitted D constructor. If it
+ // is not accessible, no D destructor is written and the generated dispose()
+ // method throws an exception.
+ // This enables C++ classes with protected or private destructors to be used
+ // in D as it would be used in C++ (GC finalization is a no-op then because
+ // of the empty D destructor) while preventing usage in »scope« variables.
+ // The method name for the dispose() method is specified in a typemap
+ // attribute called »methodname«.
+ const String *tm = NULL;
+
+ const String *dispose_methodname;
+ const String *dispose_methodmodifiers;
+ const String *dispose_parameters;
+ attributes = NewHash();
+ if (derived) {
+ tm = lookupCodeTypemap(n, "ddispose_derived", typemap_lookup_type, WARN_NONE, attributes);
+ dispose_methodname = Getattr(attributes, "tmap:ddispose_derived:methodname");
+ dispose_methodmodifiers = Getattr(attributes, "tmap:ddispose_derived:methodmodifiers");
+ dispose_parameters = Getattr(attributes, "tmap:ddispose_derived:parameters");
+ } else {
+ tm = lookupCodeTypemap(n, "ddispose", typemap_lookup_type, WARN_NONE, attributes);
+ dispose_methodname = Getattr(attributes, "tmap:ddispose:methodname");
+ dispose_methodmodifiers = Getattr(attributes, "tmap:ddispose:methodmodifiers");
+ dispose_parameters = Getattr(attributes, "tmap:ddispose:parameters");
+ }
+
+ if (tm && *Char(tm)) {
+ if (!dispose_methodname) {
+ Swig_error(Getfile(n), Getline(n),
+ "No methodname attribute defined in the ddispose%s typemap for %s\n",
+ (derived ? "_derived" : ""), proxy_class_name);
+ }
+ if (!dispose_methodmodifiers) {
+ Swig_error(Getfile(n), Getline(n),
+ "No methodmodifiers attribute defined in ddispose%s typemap for %s.\n",
+ (derived ? "_derived" : ""), proxy_class_name);
+ }
+ if (!dispose_parameters)
+ dispose_parameters = empty_string;
+ }
+
+ if (tm) {
+ // Write the destructor if the C++ one is accessible.
+ if (*Char(destructor_call)) {
+ Printv(body,
+ lookupCodeTypemap(n, "ddestructor", typemap_lookup_type, WARN_NONE), NIL);
+ }
+
+ // Write the dispose() method.
+ String *dispose_code = NewString("");
+ Printv(dispose_code, tm, NIL);
+
+ if (*Char(destructor_call)) {
+ Replaceall(dispose_code, "$imcall", destructor_call);
+ } else {
+ Replaceall(dispose_code, "$imcall", "throw new object.Exception(\"C++ destructor does not have public access\")");
+ }
+
+ if (*Char(dispose_code)) {
+ Printv(body, "\n", NIL);
+ const String *methodmods = Getattr(n, "destructmethodmodifiers");
+ if (methodmods)
+ Printv(body, methodmods, NIL);
+ else
+ Printv(body, dispose_methodmodifiers, (derived ? " override" : ""), NIL);
+ Printv(body, " void ", dispose_methodname, "(", dispose_parameters, ") ", dispose_code, "\n", NIL);
+ }
+ }
+
+ if (Swig_directorclass(n)) {
+ // If directors are enabled for the current class, generate the
+ // director connect helper function which is called from the constructor
+ // and write it to the class body.
+ writeDirectorConnectProxy(n);
+ }
+
+ // Write all constants and enumerations first to prevent forward reference
+ // errors.
+ Printv(body, proxy_class_enums_code, NIL);
+
+ // Write the code generated in other methods to the class body.
+ Printv(body, proxy_class_body_code, NIL);
+
+ // Append extra user D code to the class body.
+ Printv(body,
+ lookupCodeTypemap(n, "dcode", typemap_lookup_type, WARN_NONE), "\n", NIL);
+
+ // Write the class body and the curly bracket closing the class definition
+ // to the proxy module.
+ indentCode(body);
+ Replaceall(body, "$dbaseclass", baseclass);
+
+ Printv(proxy_class_code, body, "\n}\n", NIL);
+ Delete(body);
+
+ // Write the epilogue code if there is any.
+ Printv(proxy_class_code, proxy_class_epilogue_code, NIL);
+ }
+
+
+ /* ---------------------------------------------------------------------------
+ * D::writeClassUpcast()
+ * --------------------------------------------------------------------------- */
+ void writeClassUpcast(Node *n, const String* d_class_name, SwigType* c_classname, SwigType* c_baseclassname) {
+
+ SwigType *smart = Swig_cparse_smartptr(n);
+ String *upcast_name = Swig_name_member(getNSpace(), d_class_name, (smart != 0 ? "SmartPtrUpcast" : "Upcast"));
+ String *upcast_wrapper_name = Swig_name_wrapper(upcast_name);
+
+ writeImDModuleFunction(upcast_name, "void*", "(void* objectRef)",
+ upcast_wrapper_name);
+
+ String *classname = SwigType_namestr(c_classname);
+ String *baseclassname = SwigType_namestr(c_baseclassname);
+ if (smart) {
+ String *smartnamestr = SwigType_namestr(smart);
+ String *bsmartnamestr = SwigType_namestr(smart);
+
+ // TODO: SwigType_typedef_resolve_all on a String instead of SwigType is incorrect for templates
+ SwigType *rclassname = SwigType_typedef_resolve_all(classname);
+ SwigType *rbaseclassname = SwigType_typedef_resolve_all(baseclassname);
+ Replaceall(bsmartnamestr, rclassname, rbaseclassname);
+
+ Printv(upcasts_code,
+ "SWIGEXPORT ", bsmartnamestr, " * ", upcast_wrapper_name,
+ "(", smartnamestr, " *objectRef) {\n",
+ " return objectRef ? new ", bsmartnamestr, "(*objectRef) : 0;\n"
+ "}\n",
+ "\n", NIL);
+
+ Delete(rbaseclassname);
+ Delete(rclassname);
+ Delete(bsmartnamestr);
+ Delete(smartnamestr);
+ } else {
+ Printv(upcasts_code,
+ "SWIGEXPORT ", baseclassname, " * ", upcast_wrapper_name,
+ "(", classname, " *objectRef) {\n",
+ " return (", baseclassname, " *)objectRef;\n"
+ "}\n",
+ "\n", NIL);
+ }
+
+ Replaceall(upcasts_code, "$cclass", classname);
+ Replaceall(upcasts_code, "$cbaseclass", baseclassname);
+
+ Delete(baseclassname);
+ Delete(classname);
+ Delete(upcast_name);
+ Delete(upcast_wrapper_name);
+ Delete(smart);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::writeTypeWrapperClass()
+ * --------------------------------------------------------------------------- */
+ void writeTypeWrapperClass(String *classname, SwigType *type) {
+ Node *n = NewHash();
+ Setfile(n, input_file);
+ Setline(n, line_number);
+
+ assertClassNameValidity(classname);
+
+ String* imports_target;
+ String* code_target;
+ File *class_file = NULL;
+ if (split_proxy_dmodule) {
+ String *filename = NewStringf("%s%s.d", dmodule_directory, classname);
+ class_file = NewFile(filename, "w", SWIG_output_files());
+ if (!class_file) {
+ FileErrorDisplay(filename);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filename));
+ Delete(filename);
+
+ emitBanner(class_file);
+ Printf(class_file, "module %s%s;\n", package, classname);
+ Printf(class_file, "\nstatic import %s;\n", im_dmodule_fq_name);
+
+ imports_target = NewString("");
+ code_target = NewString("");
+ } else {
+ imports_target = proxyImportsBuffer(0);
+ code_target = proxyCodeBuffer(0);
+ }
+
+ // Import statements.
+ const String *imports = lookupCodeTypemap(n, "dimports", type, WARN_NONE);
+ if (Len(imports) > 0) {
+ String *imports_trimmed = Copy(imports);
+ Chop(imports_trimmed);
+ replaceImportTypeMacros(imports_trimmed);
+ Printv(imports_target, imports_trimmed, "\n", NIL);
+ Delete(imports_trimmed);
+ }
+
+ // Pure D baseclass and interfaces (no C++ inheritance possible.
+ const String *pure_baseclass = lookupCodeTypemap(n, "dbase", type, WARN_NONE);
+ const String *pure_interfaces = lookupCodeTypemap(n, "dinterfaces", type, WARN_NONE);
+
+ // Emit the class.
+ Printv(code_target,
+ "\n",
+ lookupCodeTypemap(n, "dclassmodifiers", type, WARN_D_TYPEMAP_CLASSMOD_UNDEF),
+ " $dclassname",
+ (*Char(pure_baseclass) || *Char(pure_interfaces)) ? " : " : "", pure_baseclass,
+ ((*Char(pure_baseclass)) && *Char(pure_interfaces)) ? ", " : "", pure_interfaces,
+ " {", NIL);
+
+ String* body = NewString("");
+ Printv(body, lookupCodeTypemap(n, "dbody", type, WARN_D_TYPEMAP_DBODY_UNDEF),
+ lookupCodeTypemap(n, "dcode", type, WARN_NONE), NIL);
+ indentCode(body);
+ Printv(code_target, body, "\n}\n", NIL);
+ Delete(body);
+
+ Replaceall(code_target, "$dclassname", classname);
+
+ if (split_proxy_dmodule) {
+ Printv(class_file, imports_target, NIL);
+ Delete(imports_target);
+
+ replaceModuleVariables(code_target);
+ Printv(class_file, code_target, NIL);
+ Delete(code_target);
+
+ Delete(class_file);
+ }
+
+ Delete(n);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::writeDirectorConnectProxy(Node *classNode)
+ *
+ * Writes the helper method which registers the director callbacks by calling
+ * the director connect function from the D side to the proxy class.
+ * --------------------------------------------------------------------------- */
+ void writeDirectorConnectProxy(Node* classNode) {
+ String *dirClassName = directorClassName(classNode);
+ String *connect_name = Swig_name_member(getNSpace(),
+ proxy_class_name, "director_connect");
+ Printf(proxy_class_body_code, "\nprivate void swigDirectorConnect() {\n");
+
+ int i;
+ for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *method = Getattr(udata, "method");
+ String *overloaded_name = Getattr(udata, "overname");
+ String *return_type = Getattr(udata, "return_type");
+ String *param_list = Getattr(udata, "param_list");
+ String *methid = Getattr(udata, "class_methodidx");
+ Printf(proxy_class_body_code, " %s.%s_Callback%s callback%s;\n", im_dmodule_fq_name, dirClassName, methid, methid);
+ Printf(proxy_class_body_code, " if (swigIsMethodOverridden!(%s delegate(%s), %s function(%s), %s)()) {\n", return_type, param_list, return_type, param_list, method);
+ Printf(proxy_class_body_code, " callback%s = &swigDirectorCallback_%s_%s;\n", methid, proxy_class_name, overloaded_name);
+ Printf(proxy_class_body_code, " }\n\n");
+ }
+ Printf(proxy_class_body_code, " %s.%s(cast(void*)swigCPtr, cast(void*)this", im_dmodule_fq_name, connect_name);
+ for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *methid = Getattr(udata, "class_methodidx");
+ Printf(proxy_class_body_code, ", callback%s", methid);
+ }
+ Printf(proxy_class_body_code, ");\n");
+ Printf(proxy_class_body_code, "}\n");
+
+ // Helper function to determine if a method has been overridden in a
+ // subclass of the wrapped class. If not, we just pass null to the
+ // director_connect_function since the method from the C++ class should
+ // be called as usual (see above).
+ // Only emit it if the proxy class has at least one method.
+ if (first_class_dmethod < curr_class_dmethod) {
+ Printf(proxy_class_body_code, "\n");
+ Printf(proxy_class_body_code, "private bool swigIsMethodOverridden(DelegateType, FunctionType, alias fn)() %s{\n", (d_version > 1) ? "const " : "");
+ Printf(proxy_class_body_code, " DelegateType dg = &fn;\n");
+ Printf(proxy_class_body_code, " return dg.funcptr != SwigNonVirtualAddressOf!(FunctionType, fn);\n");
+ Printf(proxy_class_body_code, "}\n");
+ Printf(proxy_class_body_code, "\n");
+ Printf(proxy_class_body_code, "private static Function SwigNonVirtualAddressOf(Function, alias fn)() {\n");
+ Printf(proxy_class_body_code, " return cast(Function) &fn;\n");
+ Printf(proxy_class_body_code, "}\n");
+ }
+
+ if (Len(director_dcallbacks_code) > 0) {
+ Printv(proxy_class_epilogue_code, director_dcallbacks_code, NIL);
+ }
+
+ Delete(director_callback_typedefs);
+ director_callback_typedefs = NULL;
+ Delete(director_callback_pointers);
+ director_callback_pointers = NULL;
+ Delete(director_dcallbacks_code);
+ director_dcallbacks_code = NULL;
+ Delete(dirClassName);
+ Delete(connect_name);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::writeDirectorConnectWrapper()
+ *
+ * Writes the director connect function and the corresponding declaration to
+ * the C++ wrapper respectively the D wrapper.
+ * --------------------------------------------------------------------------- */
+ void writeDirectorConnectWrapper(Node *n) {
+ if (!Swig_directorclass(n))
+ return;
+
+ // Output the director connect method.
+ String *norm_name = SwigType_namestr(Getattr(n, "name"));
+ String *connect_name = Swig_name_member(getNSpace(),
+ proxy_class_name, "director_connect");
+ String *dirClassName = directorClassName(n);
+ Wrapper *code_wrap;
+
+ Printv(wrapper_loader_bind_code, wrapper_loader_bind_command, NIL);
+ Replaceall(wrapper_loader_bind_code, "$function", connect_name);
+ Replaceall(wrapper_loader_bind_code, "$symbol", Swig_name_wrapper(connect_name));
+
+ Printf(im_dmodule_code, "extern(C) void function(void* cObject, void* dObject");
+
+ code_wrap = NewWrapper();
+ Printf(code_wrap->def, "SWIGEXPORT void D_%s(void *objarg, void *dobj", connect_name);
+
+ Printf(code_wrap->code, " %s *obj = (%s *)objarg;\n", norm_name, norm_name);
+ Printf(code_wrap->code, " %s *director = static_cast<%s *>(obj);\n", dirClassName, dirClassName);
+
+ Printf(code_wrap->code, " director->swig_connect_director(dobj");
+
+ for (int i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+ String *methid = Getattr(udata, "class_methodidx");
+
+ Printf(code_wrap->def, ", %s::SWIG_Callback%s_t callback%s", dirClassName, methid, methid);
+ Printf(code_wrap->code, ", callback%s", methid);
+ Printf(im_dmodule_code, ", %s_Callback%s callback%s", dirClassName, methid, methid);
+ }
+
+ Printf(code_wrap->def, ") {\n");
+ Printf(code_wrap->code, ");\n");
+ Printf(im_dmodule_code, ") %s;\n", connect_name);
+ Printf(code_wrap->code, "}\n");
+
+ Wrapper_print(code_wrap, f_wrappers);
+ DelWrapper(code_wrap);
+
+ Delete(connect_name);
+ Delete(dirClassName);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::requireDType()
+ *
+ * If the given type is not already in scope in the current module, adds an
+ * import statement for it. The name is considered relative to the global root
+ * package if one is set.
+ *
+ * This is only used for dependencies created in generated code, user-
+ * (i.e. typemap-) specified import statements are handled separately.
+ * --------------------------------------------------------------------------- */
+ void requireDType(const String *nspace, const String *symname) {
+ String *dmodule = createModuleName(nspace, symname);
+
+ if (!inProxyModule(dmodule)) {
+ String *import = createImportStatement(dmodule);
+ Append(import, "\n");
+ if (is_wrapping_class()) {
+ addImportStatement(proxy_class_imports, import);
+ } else {
+ addImportStatement(proxyImportsBuffer(getNSpace()), import);
+ }
+ Delete(import);
+ }
+ Delete(dmodule);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::addImportStatement()
+ *
+ * Adds the given import statement to the given list of import statements if
+ * there is no statement importing that module present yet.
+ * --------------------------------------------------------------------------- */
+ void addImportStatement(String *target, const String *import) const {
+ char *position = Strstr(target, import);
+ if (position) {
+ // If the import statement has been found in the target string, we have to
+ // check if the previous import was static, which would lead to problems
+ // if this import is not.
+ // Thus, we check if the seven characters in front of the occurrence are
+ // »static «. If the import string passed is also static, the checks fail
+ // even if the found statement is also static because the last seven
+ // characters would be part of the previous import statement then.
+
+ if (position - Char(target) < 7) {
+ return;
+ }
+ if (strncmp(position - 7, "static ", 7)) {
+ return;
+ }
+ }
+
+ Printv(target, import, NIL);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::createImportStatement()
+ *
+ * Creates a string containing an import statement for the given module.
+ * --------------------------------------------------------------------------- */
+ String *createImportStatement(const String *dmodule_name,
+ bool static_import = true) const {
+
+ if (static_import) {
+ return NewStringf("static import %s%s;", package, dmodule_name);
+ } else {
+ return NewStringf("import %s%s;", package, dmodule_name);
+ }
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::inProxyModule()
+ *
+ * Determines if the specified proxy type is declared in the currently
+ * processed proxy D module.
+ *
+ * This function is used to determine if fully qualified type names have to
+ * be used (package, module and type name). If the split proxy mode is not
+ * used, this solely depends on whether the type is in the current namespace.
+ * --------------------------------------------------------------------------- */
+ bool inProxyModule(const String *type_name) const {
+ if (!split_proxy_dmodule) {
+ String *nspace = createOuterNamespaceNames(type_name);
+
+ // Check if strings are either both null (no namespace) or are both
+ // non-null and have the same contents. Cannot use Strcmp for this
+ // directly because of its strange way of handling the case where only
+ // one argument is 0 ("<").
+ bool result = !nspace && !getNSpace();
+ if (nspace && getNSpace())
+ result = (Strcmp(nspace, getNSpace()) == 0);
+
+ Delete(nspace);
+ return result;
+ }
+
+ if (!is_wrapping_class()) {
+ return false;
+ }
+
+ return (Strcmp(proxy_class_qname, type_name) == 0);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::addUpcallMethod()
+ *
+ * Adds new director upcall signature.
+ * --------------------------------------------------------------------------- */
+ UpcallData *addUpcallMethod(String *imclass_method, String *class_method,
+ String *decl, String *overloaded_name, String *return_type, String *param_list) {
+
+ String *key = NewStringf("%s|%s", imclass_method, decl);
+
+ ++curr_class_dmethod;
+
+ String *class_methodidx = NewStringf("%d", n_dmethods - first_class_dmethod);
+ n_dmethods++;
+
+ Hash *new_udata = NewHash();
+ Append(dmethods_seq, new_udata);
+ Setattr(dmethods_table, key, new_udata);
+
+ Setattr(new_udata, "method", Copy(class_method));
+ Setattr(new_udata, "class_methodidx", class_methodidx);
+ Setattr(new_udata, "decl", Copy(decl));
+ Setattr(new_udata, "overname", Copy(overloaded_name));
+ Setattr(new_udata, "return_type", Copy(return_type));
+ Setattr(new_udata, "param_list", Copy(param_list));
+
+ Delete(key);
+ return new_udata;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::assertClassNameValidity()
+ * --------------------------------------------------------------------------- */
+ void assertClassNameValidity(const String* class_name) const {
+ // TODO: With nspace support, there could arise problems also when not in
+ // split proxy mode, warnings for these should be added.
+ if (split_proxy_dmodule) {
+ if (Cmp(class_name, im_dmodule_name) == 0) {
+ Swig_error(input_file, line_number,
+ "Class name cannot be equal to intermediary D module name: %s\n",
+ class_name);
+ Exit(EXIT_FAILURE);
+ }
+
+ String *nspace = getNSpace();
+ if (nspace) {
+ // Check the root package/outermost namespace (a class A in module
+ // A.B leads to problems if another module A.C is also imported)
+ if (Len(package) > 0) {
+ String *dotless_package = NewStringWithSize(package, Len(package) - 1);
+ if (Cmp(class_name, dotless_package) == 0) {
+ Swig_error(input_file, line_number,
+ "Class name cannot be the same as the root package it is in: %s\n",
+ class_name);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(dotless_package);
+ } else {
+ String *outer = createFirstNamespaceName(nspace);
+ if (Cmp(class_name, outer) == 0) {
+ Swig_error(input_file, line_number,
+ "Class name cannot be the same as the outermost namespace it is in: %s\n",
+ class_name);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(outer);
+ }
+
+ // … and the innermost one (because of the conflict with the main proxy
+ // module named like the namespace).
+ String *inner = createLastNamespaceName(nspace);
+ if (Cmp(class_name, inner) == 0) {
+ Swig_error(input_file, line_number,
+ "Class name cannot be the same as the innermost namespace it is in: %s\n",
+ class_name);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(inner);
+ } else {
+ if (Cmp(class_name, proxy_dmodule_name) == 0) {
+ Swig_error(input_file, line_number,
+ "Class name cannot be equal to proxy D module name: %s\n",
+ class_name);
+ Exit(EXIT_FAILURE);
+ }
+ }
+ }
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::getPrimitiveDptype()
+ *
+ * Returns the D proxy type for the passed type if it is a primitive type in
+ * both C and D.
+ * --------------------------------------------------------------------------- */
+ String *getPrimitiveDptype(Node *node, SwigType *type) {
+ SwigType *stripped_type = SwigType_typedef_resolve_all(type);
+
+ // A reference can only be the »outermost element« of a type.
+ bool mutable_ref = false;
+ if (SwigType_isreference(stripped_type)) {
+ SwigType_del_reference(stripped_type);
+
+ if (SwigType_isconst(stripped_type)) {
+ SwigType_del_qualifier(stripped_type);
+ } else {
+ mutable_ref = true;
+ }
+ }
+
+ // Strip all the pointers from the type.
+ int indirection_count = 0;
+ while (SwigType_ispointer(stripped_type)) {
+ ++indirection_count;
+ SwigType_del_pointer(stripped_type);
+ }
+
+ // Now that we got rid of the pointers, see if we are dealing with a
+ // primitive type.
+ String *dtype = 0;
+ if (SwigType_isfunction(stripped_type) && indirection_count > 0) {
+ // type was a function pointer, split it up.
+ SwigType_add_pointer(stripped_type);
+ --indirection_count;
+
+ SwigType *return_type = Copy(stripped_type);
+ SwigType *params_type = SwigType_functionpointer_decompose(return_type);
+ String *return_dtype = getPrimitiveDptype(node, return_type);
+ Delete(return_type);
+ if (!return_dtype) {
+ return 0;
+ }
+
+ List *parms = SwigType_parmlist(params_type);
+ List *param_dtypes = NewList();
+ for (Iterator it = First(parms); it.item; it = Next(it)) {
+ String *current_dtype = getPrimitiveDptype(node, it.item);
+ if (Cmp(current_dtype, "void") == 0) {
+ // void somefunc(void) is legal syntax in C, but not in D, so simply
+ // skip the void parameter.
+ Delete(current_dtype);
+ continue;
+ }
+ if (!current_dtype) {
+ Delete(return_dtype);
+ Delete(param_dtypes);
+ return 0;
+ }
+ Append(param_dtypes, current_dtype);
+ }
+
+ String *param_list = NewString("");
+ {
+ bool gen_comma = false;
+ for (Iterator it = First(param_dtypes); it.item; it = Next(it)) {
+ if (gen_comma) {
+ Append(param_list, ", ");
+ }
+ Append(param_list, it.item);
+ Delete(it.item);
+ gen_comma = true;
+ }
+ }
+
+ dtype = NewStringf("%s.SwigExternC!(%s function(%s))", im_dmodule_fq_name,
+ return_dtype, param_list);
+ Delete(param_list);
+ Delete(param_dtypes);
+ Delete(return_dtype);
+ } else {
+ Hash *attributes = NewHash();
+ const String *tm =
+ lookupCodeTypemap(node, "dtype", stripped_type, WARN_NONE, attributes);
+ if(!GetFlag(attributes, "tmap:dtype:cprimitive")) {
+ dtype = 0;
+ } else {
+ dtype = Copy(tm);
+
+ // We need to call replaceClassname here with the stripped type to avoid
+ // $dclassname in the enum typemaps being replaced later with the full
+ // type.
+ replaceClassname(dtype, stripped_type);
+ }
+ Delete(attributes);
+ }
+ Delete(stripped_type);
+
+ if (!dtype) {
+ // The type passed is no primitive type.
+ return 0;
+ }
+
+ // The type is ultimately a primitive type, now append the right number of
+ // indirection levels (pointers).
+ for (int i = 0; i < indirection_count; ++i) {
+ Append(dtype, "*");
+ }
+
+ // Add a level of indirection for a mutable reference since it is wrapped
+ // as a pointer.
+ if (mutable_ref) {
+ Append(dtype, "*");
+ }
+
+ return dtype;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::lookupCodeTypemap()
+ *
+ * Looks up a D code fragment for generating the wrapper class for the given
+ * type.
+ *
+ * n - for input only and must contain info for Getfile(n) and Getline(n) to work
+ * tmap_method - typemap method name
+ * type - typemap type to lookup
+ * warning - warning number to issue if no typemaps found
+ * typemap_attributes - the typemap attributes are attached to this node and will
+ * also be used for temporary storage if non null
+ * return is never NULL, unlike Swig_typemap_lookup()
+ * --------------------------------------------------------------------------- */
+ const String *lookupCodeTypemap(Node *n, const_String_or_char_ptr tmap_method,
+ SwigType *type, int warning, Node *typemap_attributes = 0) const {
+
+ Node *node = !typemap_attributes ? NewHash() : typemap_attributes;
+ Setattr(node, "type", type);
+ Setfile(node, Getfile(n));
+ Setline(node, Getline(n));
+ const String *tm = Swig_typemap_lookup(tmap_method, node, "", 0);
+ if (!tm) {
+ tm = empty_string;
+ if (warning != WARN_NONE) {
+ Swig_warning(warning, Getfile(n), Getline(n),
+ "No %s typemap defined for %s\n", tmap_method, SwigType_str(type, 0));
+ }
+ }
+ if (!typemap_attributes) {
+ Delete(node);
+ }
+
+ return tm;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::lookupDTypemap()
+ *
+ * Looks up a D typemap for the given node, replacing D-specific special
+ * variables as needed.
+ *
+ * The method parameter specifies the typemap method to use. If attached is
+ * true, the value is just fetched from the tmap:<method> node attribute,
+ * Swig_typemap_lookup is used otherwise.
+ * --------------------------------------------------------------------------- */
+ String *lookupDTypemap(Node *n, const_String_or_char_ptr method, bool attached = false) {
+ String *result = 0;
+
+ if (attached) {
+ String *attr_name = NewStringf("tmap:%s", method);
+ result = Copy(Getattr(n, attr_name));
+ Delete(attr_name);
+ } else {
+ // FIXME: As a workaround for a bug so far only surfacing in the
+ // smart_pointer_const_overload test case, remove the nativepointer
+ // typemap attribute since it seems to be already there from a dout
+ // typemap of a different type in that test.
+ String *np_key = NewStringf("tmap:%s:nativepointer", method);
+ Delattr(n, np_key);
+ Delete(np_key);
+
+ result = Swig_typemap_lookup(method, n, "", 0);
+ }
+
+ if (!result) {
+ return 0;
+ }
+
+ // Check if the passed node actually has type information attached. This
+ // is not the case e.g. in constructorWrapper.
+ SwigType *type = Getattr(n, "type");
+ if (type) {
+ String *np_key = NewStringf("tmap:%s:nativepointer", method);
+ String *np_value = Getattr(n, np_key);
+ Delete(np_key);
+ String *dtype;
+ if (np_value && (dtype = getPrimitiveDptype(n, type))) {
+ // If the typemap in question has a »nativepointer« attribute and we
+ // are dealing with a primitive type, use it instead.
+ result = Copy(np_value);
+ Replaceall(result, "$dtype", dtype);
+ }
+
+ replaceClassname(result, type);
+ }
+
+ return result;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::replaceClassname()
+ *
+ * Replaces the special variable $dclassname with the proxy class name for
+ * classes/structs/unions SWIG knows about. Also substitutes the enumeration
+ * name for non-anonymous enums. Otherwise, $classname is replaced with a
+ * $descriptor(type)-like name.
+ *
+ * $*dclassname and $&classname work like with descriptors (see manual section
+ * 10.4.3), they remove a prointer from respectively add a pointer to the type.
+ *
+ * Inputs:
+ * tm - String to perform the substitution at (will usually come from a
+ * typemap.
+ * pt - The type to substitute for the variables.
+ * Outputs:
+ * tm - String with the variables substituted.
+ * Return:
+ * substitution_performed - flag indicating if a substitution was performed
+ * --------------------------------------------------------------------------- */
+ bool replaceClassname(String *tm, SwigType *pt) {
+ bool substitution_performed = false;
+ SwigType *type = Copy(SwigType_typedef_resolve_all(pt));
+ SwigType *strippedtype = SwigType_strip_qualifiers(type);
+
+ if (Strstr(tm, "$dclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ replaceClassnameVariable(tm, "$dclassname", classnametype);
+ substitution_performed = true;
+ Delete(classnametype);
+ }
+ if (Strstr(tm, "$*dclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ Delete(SwigType_pop(classnametype));
+ replaceClassnameVariable(tm, "$*dclassname", classnametype);
+ substitution_performed = true;
+ Delete(classnametype);
+ }
+ if (Strstr(tm, "$&dclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ SwigType_add_pointer(classnametype);
+ replaceClassnameVariable(tm, "$&dclassname", classnametype);
+ substitution_performed = true;
+ Delete(classnametype);
+ }
+
+ Delete(strippedtype);
+ Delete(type);
+
+ return substitution_performed;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::replaceClassnameVariable()
+ *
+ * See D::replaceClassname().
+ * --------------------------------------------------------------------------- */
+ void replaceClassnameVariable(String *target, const char *variable, SwigType *type) {
+ // TODO: Fix const-correctness of methods called in here and make type const.
+
+ // We make use of the fact that this function is called at least once for
+ // every type encountered which is written to a separate file, which allows
+ // us to handle imports here.
+ // When working in split proxy module mode, each generated proxy class/enum
+ // is written to a separate module. This requires us to add a corresponding
+ // import when a type is used in another generated module. If we are not
+ // working in split proxy module mode, this is not relevant and the
+ // generated module name is discarded.
+ String *type_name;
+
+ if (SwigType_isenum(type)) {
+ // RESEARCH: Make sure that we really cannot get here for anonymous enums.
+ Node *n = enumLookup(type);
+ if (n) {
+ String *enum_name = Getattr(n, "sym:name");
+
+ Node *p = parentNode(n);
+ if (p && !Strcmp(nodeType(p), "class")) {
+ // This is a nested enum.
+ String *parent_name = Getattr(p, "sym:name");
+ String *nspace = Getattr(p, "sym:nspace");
+
+ // An enum nested in a class is not written to a separate module (this
+ // would not even be possible in D), so just import the parent.
+ requireDType(nspace, parent_name);
+
+ String *module = createModuleName(nspace, parent_name);
+ if (inProxyModule(module)) {
+ type_name = NewStringf("%s.%s", parent_name, enum_name);
+ } else {
+ type_name = NewStringf("%s%s.%s.%s", package, module, parent_name, enum_name);
+ }
+ } else {
+ // A non-nested enum is written to a separate module, import it.
+ String *nspace = Getattr(n, "sym:nspace");
+ requireDType(nspace, enum_name);
+
+ String *module = createModuleName(nspace, enum_name);
+ if (inProxyModule(module)) {
+ type_name = Copy(enum_name);
+ } else {
+ type_name = NewStringf("%s%s.%s", package, module, enum_name);
+ }
+ }
+ } else {
+ type_name = NewStringf("int");
+ }
+ } else {
+ Node *n = classLookup(type);
+ if (n) {
+ String *class_name = Getattr(n, "sym:name");
+ String *nspace = Getattr(n, "sym:nspace");
+ requireDType(nspace, class_name);
+
+ String *module = createModuleName(nspace, class_name);
+ if (inProxyModule(module)) {
+ type_name = Copy(class_name);
+ } else {
+ type_name = NewStringf("%s%s.%s", package, module, class_name);
+ }
+ Delete(module);
+ } else {
+ // SWIG does not know anything about the type (after resolving typedefs).
+ // Just mangle the type name string like $descriptor(type) would do.
+ String *descriptor = NewStringf("SWIGTYPE%s", SwigType_manglestr(type));
+ requireDType(NULL, descriptor);
+
+ String *module = createModuleName(NULL, descriptor);
+ if (inProxyModule(module)) {
+ type_name = Copy(descriptor);
+ } else {
+ type_name = NewStringf("%s%s.%s", package, module, descriptor);
+ }
+ Delete(module);
+
+ // Add to hash table so that a type wrapper class can be created later.
+ Setattr(unknown_types, descriptor, type);
+
+ Delete(descriptor);
+ }
+ }
+
+ Replaceall(target, variable, type_name);
+ Delete(type_name);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::createModuleName()
+ *
+ * Returns a string holding the name of the module to import to bring the
+ * given type in scope.
+ * --------------------------------------------------------------------------- */
+ String *createModuleName(const String *nspace, const String *type_name) const {
+ String *module;
+ if (nspace) {
+ module = NewStringf("%s.", nspace);
+ if (split_proxy_dmodule) {
+ Printv(module, type_name, NIL);
+ } else {
+ String *inner = createLastNamespaceName(nspace);
+ Printv(module, inner, NIL);
+ Delete(inner);
+ }
+ } else {
+ if (split_proxy_dmodule) {
+ module = Copy(type_name);
+ } else {
+ module = Copy(proxy_dmodule_name);
+ }
+ }
+ return module;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::replaceModuleVariables()
+ *
+ * Replaces the $imdmodule and $module variables with their values in the
+ * target string.
+ * --------------------------------------------------------------------------- */
+ void replaceModuleVariables(String *target) const {
+ Replaceall(target, "$imdmodule", im_dmodule_fq_name);
+ Replaceall(target, "$module", proxy_dmodule_name);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::replaceExcode()
+ *
+ * If a C++ method can throw a exception, additional code is added to the
+ * proxy method to check if an exception is pending so that it can be
+ * rethrown on the D side.
+ *
+ * This method replaces the $excode variable with the exception handling code
+ * in the excode typemap attribute if it »canthrow« an exception.
+ * --------------------------------------------------------------------------- */
+ void replaceExcode(Node *n, String *code, const String *typemap, Node *parameter) const {
+ String *excode_attribute = NewStringf("tmap:%s:excode", typemap);
+ String *excode = Getattr(parameter, excode_attribute);
+ if (Getattr(n, "d:canthrow")) {
+ int count = Replaceall(code, "$excode", excode);
+ if (count < 1 || !excode) {
+ Swig_warning(WARN_D_EXCODE_MISSING, input_file, line_number,
+ "D exception may not be thrown – no $excode or excode attribute in '%s' typemap.\n",
+ typemap);
+ }
+ } else {
+ Replaceall(code, "$excode", "");
+ }
+ Delete(excode_attribute);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::replaceImportTypeMacros()
+ *
+ * Replaces the $importtype(SomeDClass) macro with an import statement if it
+ * is required to get SomeDClass in scope for the currently generated proxy
+ * D module.
+ * --------------------------------------------------------------------------- */
+ void replaceImportTypeMacros(String *target) const {
+ // Code from replace_embedded_typemap.
+ char *start = 0;
+ while ((start = Strstr(target, "$importtype("))) {
+ char *end = 0;
+ char *param_start = 0;
+ char *param_end = 0;
+ int level = 0;
+ char *c = start;
+ while (*c) {
+ if (*c == '(') {
+ if (level == 0) {
+ param_start = c + 1;
+ }
+ level++;
+ }
+ if (*c == ')') {
+ level--;
+ if (level == 0) {
+ param_end = c;
+ end = c + 1;
+ break;
+ }
+ }
+ c++;
+ }
+
+ if (end) {
+ String *current_macro = NewStringWithSize(start, (int)(end - start));
+ String *current_param = NewStringWithSize(param_start, (int)(param_end - param_start));
+
+
+ if (inProxyModule(current_param)) {
+ Replace(target, current_macro, "", DOH_REPLACE_ANY);
+ } else {
+ String *import = createImportStatement(current_param, false);
+ Replace(target, current_macro, import, DOH_REPLACE_ANY);
+ Delete(import);
+ }
+
+ Delete(current_param);
+ Delete(current_macro);
+ } else {
+ String *current_macro = NewStringWithSize(start, (int)(c - start));
+ Swig_error(Getfile(target), Getline(target), "Syntax error in: %s\n", current_macro);
+ Replace(target, current_macro, "<error in $importtype macro>", DOH_REPLACE_ANY);
+ Delete(current_macro);
+ }
+ }
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::getOverloadedName()
+ * --------------------------------------------------------------------------- */
+ String *getOverloadedName(Node *n) const {
+ // A void* parameter is used for all wrapped classes in the wrapper code.
+ // Thus, the wrapper function names for overloaded functions are postfixed
+ // with a counter string to make them unique.
+ String *overloaded_name = Copy(Getattr(n, "sym:name"));
+
+ if (Getattr(n, "sym:overloaded")) {
+ Append(overloaded_name, Getattr(n, "sym:overname"));
+ }
+
+ return overloaded_name;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::createProxyName()
+ *
+ * Returns the D class name if a type corresponds to something wrapped with a
+ * proxy class, NULL otherwise.
+ * --------------------------------------------------------------------------- */
+ String *createProxyName(SwigType *t) {
+ String *proxyname = NULL;
+ Node *n = classLookup(t);
+ if (n) {
+ String *nspace = Getattr(n, "sym:nspace");
+ String *symname = Getattr(n, "sym:name");
+
+ String *module = createModuleName(nspace, symname);
+ if (inProxyModule(module)) {
+ proxyname = Copy(symname);
+ } else {
+ proxyname = NewStringf("%s%s.%s", package, module, symname);
+ }
+ }
+ return proxyname;
+ }
+
+ String *makeParameterName(Node *n, Parm *p, int arg_num, bool setter) const {
+ String *arg = Language::makeParameterName(n, p, arg_num, setter);
+
+ if (split_proxy_dmodule && Strncmp(arg, package, Len(arg)) == 0) {
+ // If we are in split proxy mode and the argument is named like the target
+ // package, we append an underscore to its name to avoid clashes.
+ Append(arg, "_");
+ }
+
+ return arg;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::canThrow()
+ *
+ * Determines whether the code in the typemap can throw a D exception.
+ * If so, note it for later when excodeSubstitute() is called.
+ * --------------------------------------------------------------------------- */
+ void canThrow(Node *n, const String *typemap, Node *parameter) const {
+ String *canthrow_attribute = NewStringf("tmap:%s:canthrow", typemap);
+ String *canthrow = Getattr(parameter, canthrow_attribute);
+ if (canthrow)
+ Setattr(n, "d:canthrow", "1");
+ Delete(canthrow_attribute);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::wrapMemberFunctionAsDConst()
+ *
+ * Determines whether the member function represented by the passed node is
+ * wrapped as D »const« or not.
+ * --------------------------------------------------------------------------- */
+ bool wrapMemberFunctionAsDConst(Node *n) const {
+ if (d_version == 1) return false;
+ if (static_flag) return false; // Never emit »const« for static member functions.
+ return GetFlag(n, "memberget") || SwigType_isconst(Getattr(n, "decl"));
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::areAllOverloadsOverridden()
+ *
+ * Determines whether the class the passed function node belongs to overrides
+ * all the overlaods for the passed function node defined somewhere up the
+ * inheritance hierarchy.
+ * --------------------------------------------------------------------------- */
+ bool areAllOverloadsOverridden(Node *n) const {
+ List *base_list = Getattr(parentNode(n), "bases");
+ if (!base_list) {
+ // If the class which contains n is not derived from any other class,
+ // there cannot be any not-overridden overloads.
+ return true;
+ }
+
+ // In case of multiple base classes, skip to the one which has not been
+ // ignored.
+ // RESEARCH: Also emit a warning in case of multiple inheritance here?
+ Iterator it = First(base_list);
+ while (it.item && GetFlag(it.item, "feature:ignore")) {
+ it = Next(it);
+ }
+ Node *base_class = it.item;
+
+ if (!base_class) {
+ // If all base classes have been ignored, there cannot be one either.
+ return true;
+ }
+
+ // We try to find at least a single overload which exists in the base class
+ // so we can progress up the inheritance hierarchy even if there have been
+ // new overloads introduced after the topmost class.
+ Node *base_function = NULL;
+ String *symname = Getattr(n, "sym:name");
+ if (symname) {
+ for (Node *tmp = firstChild(base_class); tmp; tmp = nextSibling(tmp)) {
+ String *child_symname = Getattr(tmp, "sym:name");
+ if (child_symname && (Strcmp(child_symname, symname) == 0)) {
+ base_function = tmp;
+ break;
+ }
+ }
+ }
+
+ if (!base_function) {
+ // If there is no overload which also exists in the super class, there
+ // cannot be any base class overloads not overridden.
+ return true;
+ }
+
+ size_t base_overload_count = 0;
+ for (Node *tmp = firstSibling(base_function); tmp; tmp = Getattr(tmp, "sym:nextSibling")) {
+ if (is_protected(base_function) &&
+ !(Swig_director_mode() && Swig_director_protected_mode() && Swig_all_protected_mode())) {
+ // If the base class function is »protected« and were are not in
+ // director mode, it is not emitted to the base class and thus we do
+ // not count it. Otherwise, we would run into issues if the visibility
+ // of some functions was changed from protected to public in a child
+ // class with the using directive.
+ continue;
+ }
+ ++base_overload_count;
+ }
+
+ return ((base_overload_count <= overridingOverloadCount(n)) &&
+ areAllOverloadsOverridden(base_function));
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::overridingOverloadCount()
+ *
+ * Given a member function node, this function counts how many of the
+ * overloads of the function (including itself) override a function in the
+ * base class.
+ * --------------------------------------------------------------------------- */
+ size_t overridingOverloadCount(Node *n) const {
+ size_t result = 0;
+
+ Node *tmp = firstSibling(n);
+ do {
+ // KLUDGE: We also have to count the function if the access attribute is
+ // not present, since this means that it has been promoted into another
+ // protection level in the base class with the C++ »using« directive, and
+ // is thus taken into account when counting the base class overloads, even
+ // if it is not marked as »override« by the SWIG parser.
+ if (Getattr(n, "override") || !Getattr(n, "access")) {
+ ++result;
+ }
+ } while((tmp = Getattr(tmp, "sym:nextSibling")));
+
+ return result;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::firstSibling()
+ *
+ * Returns the first sibling of the passed node.
+ * --------------------------------------------------------------------------- */
+ Node *firstSibling(Node *n) const {
+ Node *result = n;
+ while (Node *tmp = Getattr(result, "sym:previousSibling")) {
+ result = tmp;
+ }
+ return result;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::indentCode()
+ *
+ * Helper function to indent a code (string) by one level.
+ * --------------------------------------------------------------------------- */
+ void indentCode(String* code) const {
+ Replaceall(code, "\n", "\n ");
+ Replaceall(code, " \n", "\n");
+ Chop(code);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::emitBanner()
+ * --------------------------------------------------------------------------- */
+ void emitBanner(File *f) const {
+ Printf(f, "/* ----------------------------------------------------------------------------\n");
+ Swig_banner_target_lang(f, " *");
+ Printf(f, " * ----------------------------------------------------------------------------- */\n\n");
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::outputDirectory()
+ *
+ * Returns the directory to write the D modules for the given namespace to and
+ * and creates the subdirectory if it doesn't exist.
+ * --------------------------------------------------------------------------- */
+ String *outputDirectory(String *nspace) {
+ String *output_directory = Copy(dmodule_directory);
+ if (nspace) {
+ String *nspace_subdirectory = Copy(nspace);
+ Replaceall(nspace_subdirectory, ".", SWIG_FILE_DELIMITER);
+ String *newdir_error = Swig_new_subdirectory(output_directory, nspace_subdirectory);
+ if (newdir_error) {
+ Printf(stderr, "%s\n", newdir_error);
+ Delete(newdir_error);
+ Exit(EXIT_FAILURE);
+ }
+ Printv(output_directory, nspace_subdirectory, SWIG_FILE_DELIMITER, 0);
+ Delete(nspace_subdirectory);
+ }
+ return output_directory;
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::proxyCodeBuffer()
+ *
+ * Returns the buffer to write proxy code for the given namespace to.
+ * --------------------------------------------------------------------------- */
+ String *proxyCodeBuffer(String *nspace) {
+ if (!nspace) {
+ return proxy_dmodule_code;
+ }
+
+ Hash *hash = Getattr(nspace_proxy_dmodules, nspace);
+ if (!hash) {
+ hash = NewHash();
+ Setattr(hash, "code", NewString(""));
+ Setattr(hash, "imports", NewString(""));
+ Setattr(nspace_proxy_dmodules, nspace, hash);
+ }
+ return Getattr(hash, "code");
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::proxyCodeBuffer()
+ *
+ * Returns the buffer to write imports for the proxy code for the given
+ * namespace to.
+ * --------------------------------------------------------------------------- */
+ String *proxyImportsBuffer(String *nspace) {
+ if (!nspace) {
+ return proxy_dmodule_imports;
+ }
+
+ Hash *hash = Getattr(nspace_proxy_dmodules, nspace);
+ if (!hash) {
+ hash = NewHash();
+ Setattr(hash, "code", NewString(""));
+ Setattr(hash, "imports", NewString(""));
+ Setattr(nspace_proxy_dmodules, nspace, hash);
+ }
+ return Getattr(hash, "imports");
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::createFirstNamespaceName()
+ *
+ * Returns a new string containing the name of the outermost namespace, e.g.
+ * »A« for the argument »A.B.C«.
+ * --------------------------------------------------------------------------- */
+ String *createFirstNamespaceName(const String *nspace) const {
+ char *tmp = Char(nspace);
+ char *c = tmp;
+ char *co = 0;
+ if (!strstr(c, "."))
+ return 0;
+
+ co = c + Len(nspace);
+
+ while (*c && (c != co)) {
+ if (*c == '.') {
+ break;
+ }
+ c++;
+ }
+ if (!*c || (c == tmp)) {
+ return NULL;
+ }
+ return NewStringWithSize(tmp, (int)(c - tmp));
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::createLastNamespaceName()
+ *
+ * Returns a new string containing the name of the innermost namespace, e.g.
+ * »C« for the argument »A.B.C«.
+ * --------------------------------------------------------------------------- */
+ String *createLastNamespaceName(const String *nspace) const {
+ if (!nspace) return NULL;
+ char *c = Char(nspace);
+ char *cc = c;
+ if (!strstr(c, "."))
+ return NewString(nspace);
+
+ while (*c) {
+ if (*c == '.') {
+ cc = c;
+ }
+ ++c;
+ }
+ return NewString(cc + 1);
+ }
+
+ /* ---------------------------------------------------------------------------
+ * D::createOuterNamespaceNames()
+ *
+ * Returns a new string containing the name of the outer namespace, e.g.
+ * »A.B« for the argument »A.B.C«.
+ * --------------------------------------------------------------------------- */
+ String *createOuterNamespaceNames(const String *nspace) const {
+ if (!nspace) return NULL;
+ char *tmp = Char(nspace);
+ char *c = tmp;
+ char *cc = c;
+ if (!strstr(c, "."))
+ return NULL;
+
+ while (*c) {
+ if (*c == '.') {
+ cc = c;
+ }
+ ++c;
+ }
+ if (cc == tmp) {
+ return NULL;
+ }
+ return NewStringWithSize(tmp, (int)(cc - tmp));
+ }
+};
+
+static Language *new_swig_d() {
+ return new D();
+}
+
+/* -----------------------------------------------------------------------------
+ * swig_d() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+extern "C" Language *swig_d(void) {
+ return new_swig_d();
+}
+
+/* -----------------------------------------------------------------------------
+ * Usage information displayed at the command line.
+ * ----------------------------------------------------------------------------- */
+const char *D::usage = "\
+D Options (available with -d)\n\
+ -d2 - Generate code for D2/Phobos (default: D1/Tango)\n\
+ -package <pkg> - Write generated D modules into package <pkg>\n\
+ -splitproxy - Write each D type to a dedicated file instead of\n\
+ generating a single proxy D module.\n\
+ -wrapperlibrary <wl> - Set the name of the wrapper library to <wl>\n\
+\n";
diff --git a/contrib/tools/swig/Source/Modules/directors.cxx b/contrib/tools/swig/Source/Modules/directors.cxx
new file mode 100644
index 0000000000..14e2e84661
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/directors.cxx
@@ -0,0 +1,270 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * directors.cxx
+ *
+ * Director support functions.
+ * Not all of these may be necessary, and some may duplicate existing functionality
+ * in SWIG. --MR
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+
+/* -----------------------------------------------------------------------------
+ * Swig_csuperclass_call()
+ *
+ * Generates a fully qualified method call, including the full parameter list.
+ * e.g. "base::method(i, j)"
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_csuperclass_call(String *base, String *method, ParmList *l) {
+ String *call = NewString("");
+ int arg_idx = 0;
+ Parm *p;
+ if (base) {
+ Printf(call, "%s::", base);
+ }
+ Printf(call, "%s(", method);
+ for (p = l; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+ if (!pname && Cmp(Getattr(p, "type"), "void")) {
+ pname = NewString("");
+ Printf(pname, "arg%d", arg_idx++);
+ }
+ if (p != l)
+ Printf(call, ", ");
+ Printv(call, pname, NIL);
+ }
+ Printf(call, ")");
+ return call;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_class_declaration()
+ *
+ * Generate the start of a class/struct declaration.
+ * e.g. "class myclass"
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_class_declaration(Node *n, String *name) {
+ if (!name) {
+ name = Getattr(n, "sym:name");
+ }
+ String *result = NewString("");
+ String *kind = Getattr(n, "kind");
+ Printf(result, "%s %s", kind, name);
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_class_name()
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_class_name(Node *n) {
+ String *name;
+ name = Copy(Getattr(n, "sym:name"));
+ return name;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_director_declaration()
+ *
+ * Generate the full director class declaration, complete with base classes.
+ * e.g. "class SwigDirector_myclass : public myclass, public Swig::Director {"
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_director_declaration(Node *n) {
+ String *classname = Swig_class_name(n);
+ String *directorname = Language::instance()->directorClassName(n);
+ String *base = Getattr(n, "classtype");
+ String *declaration = Swig_class_declaration(n, directorname);
+
+ Printf(declaration, " : public %s, public Swig::Director {\n", base);
+ Delete(classname);
+ Delete(directorname);
+ return declaration;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_method_call()
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_method_call(const_String_or_char_ptr name, ParmList *parms) {
+ String *func;
+ int comma = 0;
+ Parm *p = parms;
+ SwigType *pt;
+ String *nname;
+
+ func = NewString("");
+ nname = SwigType_namestr(name);
+ Printf(func, "%s(", nname);
+ while (p) {
+ String *pname;
+ pt = Getattr(p, "type");
+ if ((SwigType_type(pt) != T_VOID)) {
+ if (comma)
+ Printf(func, ",");
+ pname = Getattr(p, "name");
+ Printf(func, "%s", pname);
+ comma = 1;
+ }
+ p = nextSibling(p);
+ }
+ Printf(func, ")");
+ return func;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_method_decl()
+ *
+ * Return a stringified version of a C/C++ declaration.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_method_decl(SwigType *return_base_type, SwigType *decl, const_String_or_char_ptr id, List *args, int default_args) {
+ String *result = NewString("");
+ bool conversion_operator = Strstr(id, "operator ") != 0 && !return_base_type;
+
+ Parm *parm = args;
+ int arg_idx = 0;
+ while (parm) {
+ String *type = Getattr(parm, "type");
+ String *name = Getattr(parm, "name");
+ if (!name && Cmp(type, "void")) {
+ name = NewString("");
+ Printf(name, "arg%d", arg_idx++);
+ Setattr(parm, "name", name);
+ }
+ parm = nextSibling(parm);
+ }
+
+ String *rettype = Copy(decl);
+ String *quals = SwigType_pop_function_qualifiers(rettype);
+ String *qualifiers = 0;
+ if (quals)
+ qualifiers = SwigType_str(quals, 0);
+
+ String *popped_decl = SwigType_pop_function(rettype);
+ if (return_base_type)
+ Append(rettype, return_base_type);
+
+ if (!conversion_operator) {
+ SwigType *rettype_stripped = SwigType_strip_qualifiers(rettype);
+ String *rtype = SwigType_str(rettype, 0);
+ Append(result, rtype);
+ if ((SwigType_issimple(rettype_stripped) && return_base_type) || SwigType_isqualifier(rettype))
+ Append(result, " ");
+ Delete(rtype);
+ Delete(rettype_stripped);
+ }
+
+ if (id)
+ Append(result, id);
+
+ String *args_string = default_args ? ParmList_str_defaultargs(args) : ParmList_str(args);
+ Printv(result, "(", args_string, ")", NIL);
+
+ if (qualifiers)
+ Printv(result, " ", qualifiers, NIL);
+
+ Delete(args_string);
+ Delete(popped_decl);
+ Delete(qualifiers);
+ Delete(quals);
+ Delete(rettype);
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_director_emit_dynamic_cast()
+ *
+ * In order to call protected virtual director methods from the target language, we need
+ * to add an extra dynamic_cast to call the public C++ wrapper in the director class.
+ * Also for non-static protected members when the allprotected option is on.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_director_emit_dynamic_cast(Node *n, Wrapper *f) {
+ // TODO: why is the storage element removed in staticmemberfunctionHandler ??
+ if ((!is_public(n) && (is_member_director(n) || GetFlag(n, "explicitcall"))) ||
+ (is_non_virtual_protected_access(n) && !(Swig_storage_isstatic_custom(n, "staticmemberfunctionHandler:storage") ||
+ Swig_storage_isstatic(n))
+ && !Equal(nodeType(n), "constructor"))) {
+ Node *parent = Getattr(n, "parentNode");
+ String *dirname;
+ String *dirdecl;
+ dirname = Language::instance()->directorClassName(parent);
+ dirdecl = NewStringf("%s *darg = 0", dirname);
+ Wrapper_add_local(f, "darg", dirdecl);
+ Printf(f->code, "darg = dynamic_cast<%s *>(arg1);\n", dirname);
+ Delete(dirname);
+ Delete(dirdecl);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_director_parms_fixup()
+ *
+ * For each parameter in the C++ member function, copy the parameter name
+ * to its "lname"; this ensures that Swig_typemap_attach_parms() will do
+ * the right thing when it sees strings like "$1" in "directorin" typemaps.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_director_parms_fixup(ParmList *parms) {
+ Parm *p;
+ int i;
+ for (i = 0, p = parms; p; p = nextSibling(p), ++i) {
+ String *arg = Getattr(p, "name");
+ String *lname = 0;
+
+ if (!arg && !Equal(Getattr(p, "type"), "void")) {
+ lname = NewStringf("arg%d", i);
+ Setattr(p, "name", lname);
+ } else
+ lname = Copy(arg);
+
+ Setattr(p, "lname", lname);
+ Delete(lname);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_director_can_unwrap()
+ *
+ * Determine whether a function's return type can be returned as an existing
+ * target language object instead of creating a new target language object.
+ * Must be a director class and only for return by pointer or reference only
+ * (not by value or by pointer to pointer etc).
+ * ----------------------------------------------------------------------------- */
+
+bool Swig_director_can_unwrap(Node *n) {
+
+ // FIXME: this will not try to unwrap directors returned as non-director
+ // base class pointers!
+
+ bool unwrap = false;
+
+ String *type = Getattr(n, "type");
+ SwigType *t = SwigType_typedef_resolve_all(type);
+ SwigType *t1 = SwigType_strip_qualifiers(t);
+ SwigType *prefix = SwigType_prefix(t1);
+
+ if (Strcmp(prefix, "p.") == 0 || Strcmp(prefix, "r.") == 0) {
+ Node *parent = Swig_methodclass(n);
+ Node *module = Getattr(parent, "module");
+ Node *target = Swig_directormap(module, t1);
+ if (target)
+ unwrap = true;
+ }
+
+ Delete(prefix);
+ Delete(t1);
+ Delete(t);
+
+ return unwrap;
+}
diff --git a/contrib/tools/swig/Source/Modules/emit.cxx b/contrib/tools/swig/Source/Modules/emit.cxx
new file mode 100644
index 0000000000..74adc54009
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/emit.cxx
@@ -0,0 +1,556 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * emit.cxx
+ *
+ * Useful functions for emitting various pieces of code.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+/* -----------------------------------------------------------------------------
+ * emit_return_variable()
+ *
+ * Emits a variable declaration for a function return value.
+ * The variable name is always called result.
+ * n => Node of the method being wrapped
+ * rt => the return type
+ * f => the wrapper to generate code into
+ * ----------------------------------------------------------------------------- */
+
+void emit_return_variable(Node *n, SwigType *rt, Wrapper *f) {
+
+ if (!GetFlag(n, "tmap:out:optimal")) {
+ if (rt && (SwigType_type(rt) != T_VOID)) {
+ SwigType *vt = cplus_value_type(rt);
+ SwigType *tt = vt ? vt : rt;
+ SwigType *lt = SwigType_ltype(tt);
+ String *lstr = SwigType_str(lt, Swig_cresult_name());
+ if (SwigType_ispointer(lt)) {
+ Wrapper_add_localv(f, Swig_cresult_name(), lstr, "= 0", NULL);
+ } else {
+ Wrapper_add_local(f, Swig_cresult_name(), lstr);
+ }
+ if (vt) {
+ Delete(vt);
+ }
+ Delete(lt);
+ Delete(lstr);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * emit_parameter_variables()
+ *
+ * Emits a list of variable declarations for function parameters.
+ * The variable names are always called arg1, arg2, etc...
+ * l => the parameter list
+ * f => the wrapper to generate code into
+ * ----------------------------------------------------------------------------- */
+
+void emit_parameter_variables(ParmList *l, Wrapper *f) {
+
+ Parm *p;
+ String *tm;
+
+ /* Emit function arguments */
+ Swig_cargs(f, l);
+
+ /* Attach typemaps to parameters */
+ /* Swig_typemap_attach_parms("ignore",l,f); */
+
+ Swig_typemap_attach_parms("default", l, f);
+ Swig_typemap_attach_parms("arginit", l, f);
+
+ /* Apply the arginit and default */
+ p = l;
+ while (p) {
+ tm = Getattr(p, "tmap:arginit");
+ if (tm) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:arginit:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Apply the default typemap */
+ p = l;
+ while (p) {
+ tm = Getattr(p, "tmap:default");
+ if (tm) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:default:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * emit_attach_parmmaps()
+ *
+ * Attach the standard parameter related typemaps.
+ * ----------------------------------------------------------------------------- */
+
+void emit_attach_parmmaps(ParmList *l, Wrapper *f) {
+ Swig_typemap_attach_parms("in", l, f);
+ Swig_typemap_attach_parms("typecheck", l, 0);
+ Swig_typemap_attach_parms("argout", l, f);
+ Swig_typemap_attach_parms("check", l, f);
+ Swig_typemap_attach_parms("freearg", l, f);
+
+ {
+ /* This is compatibility code to deal with the deprecated "ignore" typemap */
+ Parm *p = l;
+ Parm *np;
+ while (p) {
+ String *tm = Getattr(p, "tmap:in");
+ if (tm && checkAttribute(p, "tmap:in:numinputs", "0")) {
+ Printv(f->code, tm, "\n", NIL);
+ np = Getattr(p, "tmap:in:next");
+ while (p && (p != np)) {
+ /* Setattr(p,"ignore","1"); Deprecate */
+ p = nextSibling(p);
+ }
+ } else if (tm) {
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ }
+
+ /* Perform a sanity check on "in" and "freearg" typemaps. These
+ must exactly match to avoid chaos. If a mismatch occurs, we
+ nuke the freearg typemap */
+ {
+ Parm *p = l;
+ Parm *npin, *npfreearg;
+ while (p) {
+ npin = Getattr(p, "tmap:in:next");
+
+ /*
+ if (Getattr(p,"tmap:ignore")) {
+ npin = Getattr(p,"tmap:ignore:next");
+ } else if (Getattr(p,"tmap:in")) {
+ npin = Getattr(p,"tmap:in:next");
+ }
+ */
+
+ if (Getattr(p, "tmap:freearg")) {
+ npfreearg = Getattr(p, "tmap:freearg:next");
+ if (npin != npfreearg) {
+ while (p != npin) {
+ Delattr(p, "tmap:freearg");
+ Delattr(p, "tmap:freearg:next");
+ p = nextSibling(p);
+ }
+ }
+ }
+ p = npin;
+ }
+ }
+
+ /* Check for variable length arguments with no input typemap.
+ If no input is defined, we set this to ignore and print a
+ message.
+ */
+ {
+ Parm *p = l;
+ Parm *lp = 0;
+ while (p) {
+ if (!checkAttribute(p, "tmap:in:numinputs", "0")) {
+ lp = p;
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+ if (SwigType_isvarargs(Getattr(p, "type"))) {
+ Swig_warning(WARN_LANG_VARARGS, input_file, line_number, "Variable length arguments discarded.\n");
+ Setattr(p, "tmap:in", "");
+ }
+ lp = 0;
+ p = nextSibling(p);
+ }
+
+ /* Check if last input argument is variable length argument */
+ if (lp) {
+ p = lp;
+ while (p) {
+ if (SwigType_isvarargs(Getattr(p, "type"))) {
+ // Mark the head of the ParmList that it has varargs
+ Setattr(l, "emit:varargs", lp);
+//Printf(stdout, "setting emit:varargs %s ... %s +++ %s\n", Getattr(l, "emit:varargs"), Getattr(l, "type"), Getattr(p, "type"));
+ break;
+ }
+ p = nextSibling(p);
+ }
+ }
+ }
+
+ /*
+ * An equivalent type can be used in the typecheck typemap for SWIG to detect the overloading of equivalent
+ * target language types. This is primarily for the smartptr feature, where a pointer and a smart pointer
+ * are seen as equivalent types in the target language.
+ */
+ {
+ Parm *p = l;
+ while (p) {
+ String *tm = Getattr(p, "tmap:typecheck");
+ if (tm) {
+ String *equivalent = Getattr(p, "tmap:typecheck:equivalent");
+ if (equivalent) {
+ String *precedence = Getattr(p, "tmap:typecheck:precedence");
+ if (precedence && Strcmp(precedence, "0") != 0)
+ Swig_error(Getfile(tm), Getline(tm), "The 'typecheck' typemap for %s contains an 'equivalent' attribute for a 'precedence' that is not set to SWIG_TYPECHECK_POINTER or 0.\n", SwigType_str(Getattr(p, "type"), 0));
+ SwigType *cpt = Swig_cparse_type(equivalent);
+ if (cpt) {
+ Setattr(p, "equivtype", cpt);
+ Delete(cpt);
+ } else {
+ Swig_error(Getfile(tm), Getline(tm), "Invalid type (%s) in 'equivalent' attribute in 'typecheck' typemap for type %s.\n", equivalent, SwigType_str(Getattr(p, "type"), 0));
+ }
+ }
+ p = Getattr(p, "tmap:typecheck:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * emit_num_arguments()
+ *
+ * Calculate the total number of arguments. This function is safe for use
+ * with multi-argument typemaps which may change the number of arguments in
+ * strange ways.
+ * ----------------------------------------------------------------------------- */
+
+int emit_num_arguments(ParmList *parms) {
+ Parm *p = parms;
+ int nargs = 0;
+
+ while (p) {
+ if (Getattr(p, "tmap:in")) {
+ nargs += GetInt(p, "tmap:in:numinputs");
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* DB 04/02/2003: Not sure this is necessary with tmap:in:numinputs */
+ /*
+ if (parms && (p = Getattr(parms,"emit:varargs"))) {
+ if (!nextSibling(p)) {
+ nargs--;
+ }
+ }
+ */
+ return nargs;
+}
+
+/* -----------------------------------------------------------------------------
+ * emit_num_required()
+ *
+ * Computes the number of required arguments. This function is safe for
+ * use with multi-argument typemaps and knows how to skip over everything
+ * properly. Note that parameters with default values are counted unless
+ * the compact default args option is on.
+ * ----------------------------------------------------------------------------- */
+
+int emit_num_required(ParmList *parms) {
+ Parm *p = parms;
+ int nargs = 0;
+ Parm *first_default_arg = 0;
+ int compactdefargs = ParmList_is_compactdefargs(p);
+
+ while (p) {
+ if (Getattr(p, "tmap:in") && checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ if (Getattr(p, "tmap:default"))
+ break;
+ if (Getattr(p, "value")) {
+ if (!first_default_arg)
+ first_default_arg = p;
+ if (compactdefargs)
+ break;
+ }
+ nargs += GetInt(p, "tmap:in:numinputs");
+ if (Getattr(p, "tmap:in")) {
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ }
+
+ /* Print error message for non-default arguments following default arguments */
+ /* The error message is printed more than once with most language modules, this ought to be fixed */
+ if (first_default_arg) {
+ p = first_default_arg;
+ while (p) {
+ if (Getattr(p, "tmap:in") && checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ if (!Getattr(p, "value") && (!Getattr(p, "tmap:default"))) {
+ Swig_error(Getfile(p), Getline(p), "Non-optional argument '%s' follows an optional argument.\n", Getattr(p, "name"));
+ }
+ if (Getattr(p, "tmap:in")) {
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ }
+ }
+
+ /* DB 04/02/2003: Not sure this is necessary with tmap:in:numinputs */
+ /*
+ if (parms && (p = Getattr(parms,"emit:varargs"))) {
+ if (!nextSibling(p)) {
+ nargs--;
+ }
+ }
+ */
+ return nargs;
+}
+
+/* -----------------------------------------------------------------------------
+ * emit_isvarargs()
+ *
+ * Checks if a ParmList is a parameter list containing varargs.
+ * This function requires emit_attach_parmmaps to have been called beforehand.
+ * ----------------------------------------------------------------------------- */
+
+int emit_isvarargs(ParmList *p) {
+ if (!p)
+ return 0;
+ if (Getattr(p, "emit:varargs"))
+ return 1;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * emit_isvarargs_function()
+ *
+ * Checks for varargs in a function/constructor (can be overloaded)
+ * ----------------------------------------------------------------------------- */
+
+bool emit_isvarargs_function(Node *n) {
+ bool has_varargs = false;
+ Node *over = Getattr(n, "sym:overloaded");
+ if (over) {
+ for (Node *sibling = over; sibling; sibling = Getattr(sibling, "sym:nextSibling")) {
+ if (ParmList_has_varargs(Getattr(sibling, "parms"))) {
+ has_varargs = true;
+ break;
+ }
+ }
+ } else {
+ has_varargs = ParmList_has_varargs(Getattr(n, "parms")) ? true : false;
+ }
+ return has_varargs;
+}
+
+/* -----------------------------------------------------------------------------
+ * void emit_mark_vararg_parms()
+ *
+ * Marks the vararg parameters which are to be ignored.
+ * Vararg parameters are marked as ignored if there is no 'in' varargs (...)
+ * typemap.
+ * ----------------------------------------------------------------------------- */
+
+void emit_mark_varargs(ParmList *l) {
+ Parm *p = l;
+ while (p) {
+ if (SwigType_isvarargs(Getattr(p, "type")))
+ if (!Getattr(p, "tmap:in"))
+ Setattr(p, "varargs:ignore", "1");
+ p = nextSibling(p);
+ }
+}
+
+#if 0
+/* replace_contract_args. This function replaces argument names in contract
+ specifications. Used in conjunction with the %contract directive. */
+
+static void replace_contract_args(Parm *cp, Parm *rp, String *s) {
+ while (cp && rp) {
+ String *n = Getattr(cp, "name");
+ if (n) {
+ Replace(s, n, Getattr(rp, "lname"), DOH_REPLACE_ID);
+ }
+ cp = nextSibling(cp);
+ rp = nextSibling(rp);
+ }
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * int emit_action_code()
+ *
+ * Emits action code for a wrapper. Adds in exception handling code (%exception).
+ * eaction -> the action code to emit
+ * wrappercode -> the emitted code (output)
+ * ----------------------------------------------------------------------------- */
+int emit_action_code(Node *n, String *wrappercode, String *eaction) {
+ assert(Getattr(n, "wrap:name"));
+
+ /* Look for except feature (%exception) */
+ String *tm = GetFlagAttr(n, "feature:except");
+ if (tm)
+ tm = Copy(tm);
+ if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
+ if (Strstr(tm, "$")) {
+ Swig_replace_special_variables(n, parentNode(n), tm);
+ Replaceall(tm, "$function", eaction); // deprecated
+ Replaceall(tm, "$action", eaction);
+ }
+ Printv(wrappercode, tm, "\n", NIL);
+ Delete(tm);
+ return 1;
+ } else {
+ Printv(wrappercode, eaction, "\n", NIL);
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * int emit_action()
+ *
+ * Emits the call to the wrapped function.
+ * Adds in exception specification exception handling and %exception code.
+ * ----------------------------------------------------------------------------- */
+String *emit_action(Node *n) {
+ String *actioncode = NewStringEmpty();
+ String *tm;
+ String *action;
+ String *wrap;
+ ParmList *catchlist = Getattr(n, "catchlist");
+
+ /* Look for fragments */
+ {
+ String *fragment = Getattr(n, "feature:fragment");
+ if (fragment) {
+ char *c, *tok;
+ String *t = Copy(fragment);
+ c = Char(t);
+ tok = strtok(c, ",");
+ while (tok) {
+ String *fname = NewString(tok);
+ Setfile(fname, Getfile(n));
+ Setline(fname, Getline(n));
+ Swig_fragment_emit(fname);
+ Delete(fname);
+ tok = strtok(NULL, ",");
+ }
+ Delete(t);
+ }
+ }
+
+ /* Emit wrapper code (if any) */
+ wrap = Getattr(n, "wrap:code");
+ if (wrap && Swig_filebyname("header") != Getattr(n, "wrap:code:done")) {
+ File *f_code = Swig_filebyname("header");
+ if (f_code) {
+ Printv(f_code, wrap, NIL);
+ }
+ Setattr(n, "wrap:code:done", f_code);
+ }
+
+ action = Getattr(n, "feature:action");
+ if (!action)
+ action = Getattr(n, "wrap:action");
+ assert(action != 0);
+
+ /* Emit contract code (if any) */
+ if (Swig_contract_mode_get()) {
+ /* Preassertion */
+ tm = Getattr(n, "contract:preassert");
+ if (Len(tm)) {
+ Printv(actioncode, tm, "\n", NIL);
+ }
+ }
+ /* Exception handling code */
+
+ /* saves action -> eaction for postcatching exception */
+ String *eaction = NewString("");
+
+ /* If we are in C++ mode and there is an exception specification. We're going to
+ enclose the block in a try block */
+ if (catchlist) {
+ Printf(eaction, "try {\n");
+ }
+
+ String *preaction = Getattr(n, "wrap:preaction");
+ if (preaction)
+ Printv(eaction, preaction, NIL);
+
+ Printv(eaction, action, NIL);
+
+ String *postaction = Getattr(n, "wrap:postaction");
+ if (postaction)
+ Printv(eaction, postaction, NIL);
+
+ if (catchlist) {
+ int unknown_catch = 0;
+ int has_varargs = 0;
+ Printf(eaction, "}");
+ for (Parm *ep = catchlist; ep; ep = nextSibling(ep)) {
+ String *em = Swig_typemap_lookup("throws", ep, "_e", 0);
+ if (em) {
+ SwigType *et = Getattr(ep, "type");
+ SwigType *etr = SwigType_typedef_resolve_all(et);
+ if (SwigType_isreference(etr) || SwigType_ispointer(etr) || SwigType_isarray(etr)) {
+ Printf(eaction, " catch(%s) {", SwigType_str(et, "_e"));
+ } else if (SwigType_isvarargs(etr)) {
+ Printf(eaction, " catch(...) {");
+ has_varargs = 1;
+ } else {
+ Printf(eaction, " catch(%s) {", SwigType_str(et, "&_e"));
+ }
+ Printv(eaction, em, "\n", NIL);
+ Printf(eaction, "}");
+ } else {
+ Swig_warning(WARN_TYPEMAP_THROW, Getfile(n), Getline(n), "No 'throws' typemap defined for exception type '%s'\n", SwigType_str(Getattr(ep, "type"), 0));
+ unknown_catch = 1;
+ }
+ }
+ if (unknown_catch && !has_varargs) {
+ Printf(eaction, " catch(...) {\nthrow;\n}");
+ }
+ }
+
+ /* Look for except typemap (Deprecated) */
+ tm = Swig_typemap_lookup("except", n, Swig_cresult_name(), 0);
+ if (tm) {
+ Setattr(n, "feature:except", tm);
+ tm = 0;
+ }
+
+ /* emit the except feature code */
+ emit_action_code(n, actioncode, eaction);
+
+ Delete(eaction);
+
+ /* Emit contract code (if any) */
+ if (Swig_contract_mode_get()) {
+ /* Postassertion */
+ tm = Getattr(n, "contract:postassert");
+ if (Len(tm)) {
+ Printv(actioncode, tm, "\n", NIL);
+ }
+ }
+
+ return actioncode;
+}
diff --git a/contrib/tools/swig/Source/Modules/go.cxx b/contrib/tools/swig/Source/Modules/go.cxx
new file mode 100644
index 0000000000..bf63bec639
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/go.cxx
@@ -0,0 +1,5708 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * go.cxx
+ *
+ * Go language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+#include <ctype.h>
+
+/* ----------------------------------------------------------------------
+ * siphash()
+ *
+ * 64-bit SipHash-2-4 to generate unique id for each module
+ * ---------------------------------------------------------------------- */
+
+// An unsigned 64-bit integer that works on a 32-bit host.
+typedef struct {
+ // Assume unsigned long is at least 32 bits.
+ unsigned long hi;
+ unsigned long lo;
+} swig_uint64;
+
+// Rotate v left by bits, which must be <= 32.
+static inline void _rotl(swig_uint64 *v, int bits) {
+ assert(bits <= 32);
+ unsigned long tmp = v->hi;
+ if (bits == 32) {
+ v->hi = v->lo;
+ v->lo = tmp;
+ } else {
+ v->hi = (tmp << bits) | ((0xfffffffful & v->lo) >> (32 - bits));
+ v->lo = (v->lo << bits) | ((0xfffffffful & tmp) >> (32 - bits));
+ }
+}
+
+// dst ^= src
+static inline void _xor(swig_uint64 *dst, swig_uint64 *src) {
+ dst->lo ^= src->lo;
+ dst->hi ^= src->hi;
+}
+
+// dst += src
+static inline void _add(swig_uint64 *dst, swig_uint64 *src) {
+ dst->lo += src->lo;
+ dst->hi += src->hi + ((dst->lo & 0xfffffffful) < (src->lo&0xfffffffful) ? 1 : 0);
+}
+#define SIPROUND \
+ do { \
+ _add(&v0, &v1); _rotl(&v1, 13); _xor(&v1, &v0); _rotl(&v0, 32); \
+ _add(&v2, &v3); _rotl(&v3, 16); _xor(&v3, &v2); \
+ _add(&v0, &v3); _rotl(&v3, 21); _xor(&v3, &v0); \
+ _add(&v2, &v1); _rotl(&v1, 17); _xor(&v1, &v2); _rotl(&v2, 32); \
+ } while(0)
+
+// Set out to the hash of inc/inlen.
+static void siphash(swig_uint64 *out, const char *inc, unsigned long inlen) {
+ /* "somepseudorandomlygeneratedbytes" */
+ swig_uint64 v0 = {0x736f6d65UL, 0x70736575UL};
+ swig_uint64 v1 = {0x646f7261UL, 0x6e646f6dUL};
+ swig_uint64 v2 = {0x6c796765UL, 0x6e657261UL};
+ swig_uint64 v3 = {0x74656462UL, 0x79746573UL};
+ swig_uint64 b;
+ /* hard-coded k. */
+ swig_uint64 k0 = {0x07060504UL, 0x03020100UL};
+ swig_uint64 k1 = {0x0F0E0D0CUL, 0x0B0A0908UL};
+ int i;
+ const int cROUNDS = 2, dROUNDS = 4;
+ const unsigned char *in = (const unsigned char *)inc;
+ const unsigned char *end = in + inlen - (inlen % 8);
+ int left = inlen & 7;
+ _xor(&v3, &k1); _xor(&v2, &k0); _xor(&v1, &k1); _xor(&v0, &k0);
+ for (; in != end; in += 8) {
+ b.hi = 0; b.lo = 0;
+ for (i = 0; i < 4; i++) {
+ b.lo |= ((unsigned long)in[i]) << (8*i);
+ }
+ for (i = 0; i < 4; i++) {
+ b.hi |= ((unsigned long)in[i+4]) << (8*i);
+ }
+ _xor(&v3, &b);
+ for (i = 0; i < cROUNDS; i++) {
+ SIPROUND;
+ }
+ _xor(&v0, &b);
+ }
+ b.hi = (inlen & 0xff)<<24; b.lo = 0;
+ for (; left; left--) {
+ if (left > 4) {
+ b.hi |= ((unsigned long)in[left-1]) << (8*left-8-32);
+ } else {
+ b.lo |= ((unsigned long)in[left-1]) << (8*left-8);
+ }
+ }
+ _xor(&v3, &b);
+ for(i=0; i<cROUNDS; i++) {
+ SIPROUND;
+ }
+ _xor(&v0, &b); v2.lo ^= 0xff;
+ for(i=0; i<dROUNDS; i++) {
+ SIPROUND;
+ }
+ out->lo = 0; out->hi = 0;
+ _xor(out, &v0); _xor(out, &v1); _xor(out, &v2); _xor(out, &v3);
+}
+#undef SIPROUND
+
+class GO:public Language {
+ static const char *const usage;
+
+ // Go package name.
+ String *package;
+ // SWIG module name.
+ String *module;
+ // Flag for generating gccgo output.
+ bool gccgo_flag;
+ // Prefix to use with gccgo.
+ String *go_prefix;
+ // -fgo-prefix option.
+ String *prefix_option;
+ // -fgo-pkgpath option.
+ String *pkgpath_option;
+ // Prefix for translating %import directive to import statements.
+ String *import_prefix;
+ // Whether to use a shared library.
+ bool use_shlib;
+ // Name of shared library to import.
+ String *soname;
+ // Size in bits of the Go type "int". 0 if not specified.
+ int intgo_type_size;
+
+ /* Output files */
+ File *f_c_begin;
+ File *f_go_begin;
+
+ /* Output fragments */
+ File *f_c_runtime;
+ File *f_c_header;
+ File *f_c_wrappers;
+ File *f_c_init;
+ File *f_c_directors;
+ File *f_c_directors_h;
+ File *f_go_imports;
+ File *f_go_runtime;
+ File *f_go_header;
+ File *f_go_wrappers;
+ File *f_go_directors;
+ File *f_cgo_comment;
+ File *f_cgo_comment_typedefs;
+
+ // True if we imported a module.
+ bool saw_import;
+ // If not NULL, name of import package being processed.
+ String *imported_package;
+ // Build interface methods while handling a class. This is only
+ // non-NULL when we are handling methods.
+ String *interfaces;
+ // The class node while handling a class. This is only non-NULL
+ // when we are handling methods.
+ Node *class_node;
+ // The class name while handling a class. This is only non-NULL
+ // when we are handling methods. This is the name of the class as
+ // SWIG sees it.
+ String *class_name;
+ // The receiver name while handling a class. This is only non-NULL
+ // when we are handling methods. This is the name of the class
+ // as run through goCPointerType.
+ String *class_receiver;
+ // A hash table of method names that we have seen when processing a
+ // class. This lets us detect base class methods that we don't want
+ // to use.
+ Hash *class_methods;
+ // True when we are generating the wrapper functions for a variable.
+ bool making_variable_wrappers;
+ // True when working with a static member function.
+ bool is_static_member_function;
+ // A hash table of enum types that we have seen but which may not have
+ // been defined. The index is a SwigType.
+ Hash *undefined_enum_types;
+ // A hash table of types that we have seen but which may not have
+ // been defined. The index is a SwigType.
+ Hash *undefined_types;
+ // A hash table of classes which were defined. The index is a Go
+ // type name.
+ Hash *defined_types;
+ // A hash table of all the go_imports already imported. The index is a full
+ // import name e.g. '"runtime"' or '_ "runtime/cgo"' or 'sc "syscall"'.
+ Hash *go_imports;
+ // A unique ID used to make public symbols unique.
+ String *unique_id;
+
+public:
+ GO():package(NULL),
+ module(NULL),
+ gccgo_flag(false),
+ go_prefix(NULL),
+ prefix_option(NULL),
+ pkgpath_option(NULL),
+ import_prefix(NULL),
+ use_shlib(false),
+ soname(NULL),
+ intgo_type_size(0),
+ f_c_begin(NULL),
+ f_go_begin(NULL),
+ f_c_runtime(NULL),
+ f_c_header(NULL),
+ f_c_wrappers(NULL),
+ f_c_init(NULL),
+ f_c_directors(NULL),
+ f_c_directors_h(NULL),
+ f_go_imports(NULL),
+ f_go_runtime(NULL),
+ f_go_header(NULL),
+ f_go_wrappers(NULL),
+ f_go_directors(NULL),
+ f_cgo_comment(NULL),
+ f_cgo_comment_typedefs(NULL),
+ saw_import(false),
+ imported_package(NULL),
+ interfaces(NULL),
+ class_node(NULL),
+ class_name(NULL),
+ class_receiver(NULL),
+ class_methods(NULL),
+ making_variable_wrappers(false),
+ is_static_member_function(false),
+ undefined_enum_types(NULL),
+ undefined_types(NULL),
+ defined_types(NULL),
+ go_imports(NULL),
+ unique_id(NULL) {
+ director_multiple_inheritance = 1;
+ director_language = 1;
+ director_prot_ctor_code = NewString("_swig_gopanic(\"accessing abstract class or protected constructor\");");
+ }
+
+private:
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+ virtual void main(int argc, char *argv[]) {
+
+ SWIG_library_directory("go");
+ bool saw_nocgo_flag = false;
+
+ // Process command line options.
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-package") == 0) {
+ if (argv[i + 1]) {
+ package = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-cgo") == 0) {
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-no-cgo") == 0) {
+ Swig_mark_arg(i);
+ saw_nocgo_flag = true;
+ } else if (strcmp(argv[i], "-gccgo") == 0) {
+ Swig_mark_arg(i);
+ gccgo_flag = true;
+ } else if (strcmp(argv[i], "-go-prefix") == 0) {
+ if (argv[i + 1]) {
+ prefix_option = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-go-pkgpath") == 0) {
+ if (argv[i + 1]) {
+ pkgpath_option = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-import-prefix") == 0) {
+ if (argv[i + 1]) {
+ import_prefix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-use-shlib") == 0) {
+ Swig_mark_arg(i);
+ use_shlib = true;
+ } else if (strcmp(argv[i], "-soname") == 0) {
+ if (argv[i + 1]) {
+ soname = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-longsize") == 0) {
+ // Ignore for backward compatibility.
+ if (argv[i + 1]) {
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ ++i;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-intgosize") == 0) {
+ if (argv[i + 1]) {
+ intgo_type_size = atoi(argv[i + 1]);
+ if (intgo_type_size != 32 && intgo_type_size != 64) {
+ Printf(stderr, "-intgosize not 32 or 64\n");
+ Swig_arg_error();
+ }
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ ++i;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-help") == 0) {
+ Printf(stdout, "%s\n", usage);
+ }
+ }
+ }
+
+ if (saw_nocgo_flag) {
+ Printf(stderr, "SWIG -go: -no-cgo option is no longer supported\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ if (gccgo_flag && !pkgpath_option && !prefix_option) {
+ prefix_option = NewString("go");
+ }
+
+ // Add preprocessor symbol to parser.
+ Preprocessor_define("SWIGGO 1", 0);
+
+ if (gccgo_flag) {
+ Preprocessor_define("SWIGGO_GCCGO 1", 0);
+ }
+
+ if (intgo_type_size == 32) {
+ Preprocessor_define("SWIGGO_INTGO_SIZE 32", 0);
+ } else if (intgo_type_size == 64) {
+ Preprocessor_define("SWIGGO_INTGO_SIZE 64", 0);
+ } else {
+ Preprocessor_define("SWIGGO_INTGO_SIZE 0", 0);
+ }
+
+ // Add typemap definitions.
+ SWIG_typemap_lang("go");
+ SWIG_config_file("go.swg");
+
+ allow_overloading();
+ }
+
+ /* ---------------------------------------------------------------------
+ * top()
+ *
+ * For gc, we are going to create the following files:
+ *
+ * 1) A .c or .cxx file compiled with gcc. This file will contain
+ * function wrappers. Each wrapper will take a pointer to a
+ * struct holding the arguments, unpack them, and call the real
+ * function.
+ *
+ * 2) A .go file which defines the Go form of all types, and which
+ * defines Go function wrappers. Each wrapper will call the C
+ * function wrapper in the second file.
+ *
+ * 3) A .c file compiled with 6c/8c. This file will define
+ * Go-callable C function wrappers. Each wrapper will use
+ * cgocall to call the function wrappers in the first file.
+ *
+ * When generating code for gccgo, we don't need the third file, and
+ * the function wrappers in the first file have a different form.
+ *
+ * --------------------------------------------------------------------- */
+
+ virtual int top(Node *n) {
+ Node *optionsnode = Getattr(Getattr(n, "module"), "options");
+ if (optionsnode) {
+ if (Getattr(optionsnode, "directors")) {
+ allow_directors();
+ }
+ if (Getattr(optionsnode, "dirprot")) {
+ allow_dirprot();
+ }
+ allow_allprotected(GetFlag(optionsnode, "allprotected"));
+ }
+
+ module = Getattr(n, "name");
+ if (!package) {
+ package = Copy(module);
+ }
+ if (!soname && use_shlib) {
+ soname = Copy(package);
+ Append(soname, ".so");
+ }
+
+ if (gccgo_flag) {
+ String *pref;
+ if (pkgpath_option) {
+ pref = pkgpath_option;
+ } else {
+ pref = prefix_option;
+ }
+ go_prefix = NewString("");
+ for (char *p = Char(pref); *p != '\0'; p++) {
+ if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '.' || *p == '$') {
+ Putc(*p, go_prefix);
+ } else {
+ Putc('_', go_prefix);
+ }
+ }
+ if (!pkgpath_option) {
+ Append(go_prefix, ".");
+ Append(go_prefix, getModuleName(package));
+ }
+ }
+
+ // Get filenames.
+
+ String *swig_filename = Getattr(n, "infile");
+ String *c_filename = Getattr(n, "outfile");
+ String *c_filename_h = Getattr(n, "outfile_h");
+
+ String *go_filename = NewString("");
+ Printf(go_filename, "%s%s.go", SWIG_output_directory(), module);
+
+ // Generate a unique ID based on a hash of the SWIG input.
+ swig_uint64 hash = {0, 0};
+ FILE *swig_input = Swig_open(swig_filename);
+ if (swig_input == NULL) {
+ FileErrorDisplay(swig_filename);
+ Exit(EXIT_FAILURE);
+ }
+ String *swig_input_content = Swig_read_file(swig_input);
+ siphash(&hash, Char(swig_input_content), Len(swig_input_content));
+ Delete(swig_input_content);
+ fclose(swig_input);
+ unique_id = NewString("");
+ Printf(unique_id, "_%s_%08x%08x", getModuleName(package), hash.hi, hash.lo);
+
+ // Open files.
+
+ f_c_begin = NewFile(c_filename, "w", SWIG_output_files());
+ if (!f_c_begin) {
+ FileErrorDisplay(c_filename);
+ Exit(EXIT_FAILURE);
+ }
+
+ if (directorsEnabled()) {
+ if (!c_filename_h) {
+ Printf(stderr, "Unable to determine outfile_h\n");
+ Exit(EXIT_FAILURE);
+ }
+ f_c_directors_h = NewFile(c_filename_h, "w", SWIG_output_files());
+ if (!f_c_directors_h) {
+ FileErrorDisplay(c_filename_h);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ f_go_begin = NewFile(go_filename, "w", SWIG_output_files());
+ if (!f_go_begin) {
+ FileErrorDisplay(go_filename);
+ Exit(EXIT_FAILURE);
+ }
+
+ f_c_runtime = NewString("");
+ f_c_header = NewString("");
+ f_c_wrappers = NewString("");
+ f_c_init = NewString("");
+ f_c_directors = NewString("");
+ f_go_imports = NewString("");
+ f_go_runtime = NewString("");
+ f_go_header = NewString("");
+ f_go_wrappers = NewString("");
+ f_go_directors = NewString("");
+ f_cgo_comment = NewString("");
+ f_cgo_comment_typedefs = NewString("");
+
+ Swig_register_filebyname("begin", f_c_begin);
+ Swig_register_filebyname("runtime", f_c_runtime);
+ Swig_register_filebyname("header", f_c_header);
+ Swig_register_filebyname("wrapper", f_c_wrappers);
+ Swig_register_filebyname("init", f_c_init);
+ Swig_register_filebyname("director", f_c_directors);
+ Swig_register_filebyname("director_h", f_c_directors_h);
+ Swig_register_filebyname("go_begin", f_go_begin);
+ Swig_register_filebyname("go_imports", f_go_imports);
+ Swig_register_filebyname("go_runtime", f_go_runtime);
+ Swig_register_filebyname("go_header", f_go_header);
+ Swig_register_filebyname("go_wrapper", f_go_wrappers);
+ Swig_register_filebyname("go_director", f_go_directors);
+ Swig_register_filebyname("cgo_comment", f_cgo_comment);
+ Swig_register_filebyname("cgo_comment_typedefs", f_cgo_comment_typedefs);
+
+ Swig_banner(f_c_begin);
+
+ Swig_obligatory_macros(f_c_runtime, "GO");
+
+ if (CPlusPlus) {
+ Printf(f_c_begin, "\n// source: %s\n\n", swig_filename);
+ } else {
+ Printf(f_c_begin, "\n/* source: %s */\n\n", swig_filename);
+ }
+
+ Printf(f_c_runtime, "#define SWIGMODULE %s\n", module);
+
+ if (gccgo_flag) {
+ Printf(f_c_runtime, "#define SWIGGO_PREFIX %s\n", go_prefix);
+ }
+
+ if (directorsEnabled()) {
+ Printf(f_c_runtime, "#define SWIG_DIRECTORS\n");
+
+ Swig_banner(f_c_directors_h);
+ Printf(f_c_directors_h, "\n// source: %s\n\n", swig_filename);
+
+ Printf(f_c_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module);
+ Printf(f_c_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module);
+ Printf(f_c_directors_h, "class Swig_memory;\n\n");
+
+ Printf(f_c_directors, "\n// C++ director class methods.\n");
+ String *filename = Swig_file_filename(c_filename_h);
+ Printf(f_c_directors, "#include \"%s\"\n\n", filename);
+ Delete(filename);
+ }
+
+ Swig_banner(f_go_begin);
+ Printf(f_go_begin, "\n// source: %s\n", swig_filename);
+
+ Printv(f_cgo_comment_typedefs, "/*\n", NULL);
+
+ // The cgo program defines the intgo type after our function
+ // definitions, but we want those definitions to be able to use
+ // intgo also.
+ Printv(f_cgo_comment_typedefs, "#define intgo swig_intgo\n", NULL);
+ Printv(f_cgo_comment_typedefs, "typedef void *swig_voidp;\n", NULL);
+
+ // Output module initialization code.
+
+ Printf(f_go_begin, "\npackage %s\n\n", getModuleName(package));
+
+ // All the C++ wrappers should be extern "C".
+
+ Printv(f_c_wrappers, "#ifdef __cplusplus\n", "extern \"C\" {\n", "#endif\n\n", NULL);
+
+ // Set up the hash table for types not defined by SWIG.
+
+ undefined_enum_types = NewHash();
+ undefined_types = NewHash();
+ defined_types = NewHash();
+ go_imports = NewHash();
+
+ // Emit code.
+
+ Language::top(n);
+
+ if (directorsEnabled()) {
+ // Insert director runtime into the f_runtime file (make it occur before %header section)
+ Swig_insert_file("director_common.swg", f_c_runtime);
+ Swig_insert_file("director.swg", f_c_runtime);
+ }
+
+ Delete(go_imports);
+
+ // Write out definitions for the types not defined by SWIG.
+
+ if (Len(undefined_enum_types) > 0)
+ Printv(f_go_wrappers, "\n", NULL);
+ for (Iterator p = First(undefined_enum_types); p.key; p = Next(p)) {
+ String *name = p.item;
+ Printv(f_go_wrappers, "type ", name, " int\n", NULL);
+ }
+
+ Printv(f_go_wrappers, "\n", NULL);
+ for (Iterator p = First(undefined_types); p.key; p = Next(p)) {
+ String *ty = goType(NULL, p.key);
+ if (!Getattr(defined_types, ty)) {
+ String *cp = goCPointerType(p.key, false);
+ if (!Getattr(defined_types, cp)) {
+ Printv(f_go_wrappers, "type ", cp, " uintptr\n", NULL);
+ Printv(f_go_wrappers, "type ", ty, " interface {\n", NULL);
+ Printv(f_go_wrappers, "\tSwigcptr() uintptr;\n", NULL);
+ Printv(f_go_wrappers, "}\n", NULL);
+ Printv(f_go_wrappers, "func (p ", cp, ") Swigcptr() uintptr {\n", NULL);
+ Printv(f_go_wrappers, "\treturn uintptr(p)\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+ }
+ Delete(cp);
+ }
+ Delete(ty);
+ }
+ Delete(undefined_enum_types);
+ Delete(undefined_types);
+ Delete(defined_types);
+
+ /* Write and cleanup */
+
+ Dump(f_c_header, f_c_runtime);
+
+ if (directorsEnabled()) {
+ Printf(f_c_directors_h, "#endif\n");
+ Delete(f_c_directors_h);
+ f_c_directors_h = NULL;
+
+ Dump(f_c_directors, f_c_runtime);
+ Delete(f_c_directors);
+ f_c_directors = NULL;
+ }
+
+ // End the extern "C".
+ Printv(f_c_wrappers, "#ifdef __cplusplus\n", "}\n", "#endif\n\n", NULL);
+
+ // End the cgo comment.
+ Printv(f_cgo_comment, "#undef intgo\n", NULL);
+ Printv(f_cgo_comment, "*/\n", NULL);
+ Printv(f_cgo_comment, "import \"C\"\n", NULL);
+ Printv(f_cgo_comment, "\n", NULL);
+
+ bool need_panic = false;
+ if (Strstr(f_c_runtime, "SWIG_contract_assert(") != 0 || Strstr(f_c_wrappers, "SWIG_contract_assert(") != 0) {
+ Printv(f_c_begin, "\n#define SWIG_contract_assert(expr, msg) if (!(expr)) { _swig_gopanic(msg); } else\n\n", NULL);
+ need_panic = true;
+ }
+
+ if (!gccgo_flag && (need_panic || Strstr(f_c_runtime, "_swig_gopanic") != 0 || Strstr(f_c_wrappers, "_swig_gopanic") != 0)) {
+ Printv(f_go_header, "//export cgo_panic_", unique_id, "\n", NULL);
+ Printv(f_go_header, "func cgo_panic_", unique_id, "(p *byte) {\n", NULL);
+ Printv(f_go_header, "\ts := (*[1024]byte)(unsafe.Pointer(p))[:]\n", NULL);
+ Printv(f_go_header, "\tfor i, b := range s {\n", NULL);
+ Printv(f_go_header, "\t\tif b == 0 {\n", NULL);
+ Printv(f_go_header, "\t\t\tpanic(string(s[:i]))\n", NULL);
+ Printv(f_go_header, "\t\t}\n", NULL);
+ Printv(f_go_header, "\t}\n", NULL);
+ Printv(f_go_header, "\tpanic(string(s))\n", NULL);
+ Printv(f_go_header, "}\n\n", NULL);
+
+ Printv(f_c_begin, "\nextern\n", NULL);
+ Printv(f_c_begin, "#ifdef __cplusplus\n", NULL);
+ Printv(f_c_begin, " \"C\"\n", NULL);
+ Printv(f_c_begin, "#endif\n", NULL);
+ Printv(f_c_begin, " void cgo_panic_", unique_id, "(const char*);\n", NULL);
+ Printv(f_c_begin, "static void _swig_gopanic(const char *p) {\n", NULL);
+ Printv(f_c_begin, " cgo_panic_", unique_id, "(p);\n", NULL);
+ Printv(f_c_begin, "}\n\n", NULL);
+ }
+
+ Dump(f_c_runtime, f_c_begin);
+ Dump(f_c_wrappers, f_c_begin);
+ Dump(f_c_init, f_c_begin);
+ Dump(f_cgo_comment_typedefs, f_go_begin);
+ Dump(f_cgo_comment, f_go_begin);
+ Dump(f_go_imports, f_go_begin);
+ Dump(f_go_header, f_go_begin);
+ Dump(f_go_runtime, f_go_begin);
+ Dump(f_go_wrappers, f_go_begin);
+ if (directorsEnabled()) {
+ Dump(f_go_directors, f_go_begin);
+ }
+ Delete(f_c_runtime);
+ Delete(f_c_header);
+ Delete(f_c_wrappers);
+ Delete(f_c_init);
+ Delete(f_go_imports);
+ Delete(f_go_runtime);
+ Delete(f_go_header);
+ Delete(f_go_wrappers);
+ Delete(f_go_directors);
+ Delete(f_cgo_comment);
+ Delete(f_cgo_comment_typedefs);
+ Delete(f_c_begin);
+ Delete(f_go_begin);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * importDirective()
+ *
+ * Handle a SWIG import statement by generating a Go import
+ * statement.
+ * ------------------------------------------------------------ */
+
+ virtual int importDirective(Node *n) {
+ String *hold_import = imported_package;
+ String *modname = Getattr(n, "module");
+ if (modname) {
+ if (!Getattr(go_imports, modname)) {
+ Setattr(go_imports, modname, modname);
+ Printv(f_go_imports, "import \"", NULL);
+ if (import_prefix) {
+ Printv(f_go_imports, import_prefix, "/", NULL);
+ }
+ Printv(f_go_imports, modname, "\"\n", NULL);
+ }
+ imported_package = modname;
+ saw_import = true;
+ }
+ int r = Language::importDirective(n);
+ imported_package = hold_import;
+ return r;
+ }
+
+ /* ----------------------------------------------------------------------
+ * Language::insertDirective()
+ *
+ * If the section is go_imports, store them for later.
+ * ---------------------------------------------------------------------- */
+ virtual int insertDirective(Node *n) {
+ char *section = Char(Getattr(n, "section"));
+ if ((ImportMode && !Getattr(n, "generated")) ||
+ !section || (strcmp(section, "go_imports") != 0)) {
+ return Language::insertDirective(n);
+ }
+
+ char *code = Char(Getattr(n, "code"));
+ char *pch = strtok(code, ",");
+ while (pch != NULL) {
+ // Do not import same thing more than once.
+ if (!Getattr(go_imports, pch)) {
+ Setattr(go_imports, pch, pch);
+ Printv(f_go_imports, "import ", pch, "\n", NULL);
+ }
+ pch = strtok(NULL, ",");
+ }
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * functionWrapper()
+ *
+ * Implement a function.
+ * ---------------------------------------------------------------------- */
+
+ virtual int functionWrapper(Node *n) {
+ if (GetFlag(n, "feature:ignore")) {
+ return SWIG_OK;
+ }
+
+ // We don't need explicit calls.
+ if (GetFlag(n, "explicitcall")) {
+ return SWIG_OK;
+ }
+
+ // Don't emit constructors for abstract director classes. They
+ // will never succeed anyhow.
+ if (Swig_methodclass(n) && Swig_directorclass(n)
+ && Strcmp(Char(Getattr(n, "wrap:action")), director_prot_ctor_code) == 0) {
+ return SWIG_OK;
+ }
+
+ String *name = Getattr(n, "sym:name");
+ String *nodetype = Getattr(n, "nodeType");
+ bool is_static = is_static_member_function || isStatic(n);
+ bool is_friend = isFriend(n);
+ bool is_ctor_dtor = false;
+
+ SwigType *result = Getattr(n, "type");
+
+ // For some reason SWIG changs the "type" value during the call to
+ // functionWrapper. We need to remember the type for possible
+ // overload processing.
+ Setattr(n, "go:type", Copy(result));
+
+ String *go_name;
+
+ String *r1 = NULL;
+ if (making_variable_wrappers) {
+ // Change the name of the variable setter and getter functions
+ // to be more Go like.
+
+ bool is_set = Strcmp(Char(name) + Len(name) - 4, "_set") == 0;
+ assert(is_set || Strcmp(Char(name) + Len(name) - 4, "_get") == 0);
+
+ // Start with Set or Get.
+ go_name = NewString(is_set ? "Set" : "Get");
+
+ // If this is a static variable, put in the class name,
+ // capitalized.
+ if (is_static && class_name) {
+ String *ccn = exportedName(class_name);
+ Append(go_name, ccn);
+ Delete(ccn);
+ }
+
+ // Add the rest of the name, capitalized, dropping the _set or
+ // _get.
+ String *c1 = removeClassname(name);
+ String *c2 = exportedName(c1);
+ char *p = Char(c2);
+ int len = Len(p);
+ for (int i = 0; i < len - 4; ++i) {
+ Putc(p[i], go_name);
+ }
+ Delete(c2);
+ Delete(c1);
+
+ if (!checkIgnoredParameters(n, go_name)) {
+ Delete(go_name);
+ return SWIG_NOWRAP;
+ }
+ } else if (Cmp(nodetype, "constructor") == 0) {
+ is_ctor_dtor = true;
+
+ // Change the name of a constructor to be more Go like. Change
+ // new_ to New, and capitalize the class name.
+ assert(Strncmp(name, "new_", 4) == 0);
+ String *c1 = NewString(Char(name) + 4);
+ String *c2 = exportedName(c1);
+ go_name = NewString("New");
+ Append(go_name, c2);
+ Delete(c2);
+ Delete(c1);
+
+ if (Swig_methodclass(n) && Swig_directorclass(n)) {
+ // The core SWIG code skips the first parameter when
+ // generating the $nondirector_new string. Recreate the
+ // action in this case. But don't it if we are using the
+ // special code for an abstract class.
+ String *call = Swig_cppconstructor_call(getClassType(),
+ Getattr(n, "parms"));
+ SwigType *type = Copy(getClassType());
+ SwigType_add_pointer(type);
+ String *cres = Swig_cresult(type, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ }
+ } else if (Cmp(nodetype, "destructor") == 0) {
+ // No need to emit protected destructors.
+ if (!is_public(n)) {
+ return SWIG_OK;
+ }
+
+ is_ctor_dtor = true;
+
+ // Change the name of a destructor to be more Go like. Change
+ // delete_ to Delete and capitalize the class name.
+ assert(Strncmp(name, "delete_", 7) == 0);
+ String *c1 = NewString(Char(name) + 7);
+ String *c2 = exportedName(c1);
+ go_name = NewString("Delete");
+ Append(go_name, c2);
+ Delete(c2);
+ Delete(c1);
+
+ result = NewString("void");
+ r1 = result;
+ } else {
+ if (!checkFunctionVisibility(n, NULL)) {
+ return SWIG_OK;
+ }
+
+ go_name = buildGoName(name, is_static, is_friend);
+
+ if (!checkIgnoredParameters(n, go_name)) {
+ Delete(go_name);
+ return SWIG_NOWRAP;
+ }
+ }
+
+ String *overname = NULL;
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ } else {
+ String *scope;
+ if (!class_name || is_static || is_ctor_dtor) {
+ scope = NULL;
+ } else {
+ scope = NewString("swiggoscope.");
+ Append(scope, class_name);
+ }
+ if (!checkNameConflict(go_name, n, scope)) {
+ Delete(go_name);
+ return SWIG_NOWRAP;
+ }
+ }
+
+ String *wname = Swig_name_wrapper(name);
+ if (overname) {
+ Append(wname, overname);
+ }
+ Append(wname, unique_id);
+ Setattr(n, "wrap:name", wname);
+
+ ParmList *parms = Getattr(n, "parms");
+ Setattr(n, "wrap:parms", parms);
+
+ int r = makeWrappers(n, go_name, overname, wname, NULL, parms, result, is_static);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ if (Getattr(n, "sym:overloaded") && !Getattr(n, "sym:nextSibling")) {
+ String *scope ;
+ if (!class_name || is_static || is_ctor_dtor) {
+ scope = NULL;
+ } else {
+ scope = NewString("swiggoscope.");
+ Append(scope, class_name);
+ }
+ if (!checkNameConflict(go_name, n, scope)) {
+ Delete(go_name);
+ return SWIG_NOWRAP;
+ }
+
+ String *receiver = class_receiver;
+ if (is_static || is_ctor_dtor) {
+ receiver = NULL;
+ }
+ r = makeDispatchFunction(n, go_name, receiver, is_static, NULL, false);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+
+ Delete(wname);
+ Delete(go_name);
+ Delete(r1);
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ *
+ * For some reason the language code removes the "storage" attribute
+ * for a static function before calling functionWrapper, which means
+ * that we have no way of knowing whether a function is static or
+ * not. That makes no sense in the Go context. Here we note that a
+ * function is static.
+ * ---------------------------------------------------------------------- */
+
+ int staticmemberfunctionHandler(Node *n) {
+ assert(!is_static_member_function);
+ is_static_member_function = true;
+ int r = Language::staticmemberfunctionHandler(n);
+ is_static_member_function = false;
+ return r;
+ }
+
+ /* ----------------------------------------------------------------------
+ * makeWrappers()
+ *
+ * Write out the various function wrappers.
+ * n: The function we are emitting.
+ * go_name: The name of the function in Go.
+ * overname: The overload string for overloaded function.
+ * wname: The SWIG wrapped name--the name of the C function.
+ * base: A list of the names of base classes, in the case where this
+ * is a virtual method not defined in the current class.
+ * parms: The parameters.
+ * result: The result type.
+ * is_static: Whether this is a static method or member.
+ * ---------------------------------------------------------------------- */
+
+ int makeWrappers(Node *n, String *go_name, String *overname, String *wname, List *base, ParmList *parms, SwigType *result, bool is_static) {
+
+ assert(result);
+
+ int ret = SWIG_OK;
+
+ int r = makeCgoWrappers(n, go_name, overname, wname, base, parms, result, is_static);
+ if (r != SWIG_OK) {
+ ret = r;
+ }
+
+ if (class_methods) {
+ Setattr(class_methods, Getattr(n, "name"), NewString(""));
+ }
+
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * struct cgoWrapperInfo
+ *
+ * Information needed by the CGO wrapper functions.
+ * ---------------------------------------------------------------------- */
+
+ struct cgoWrapperInfo {
+ // The function we are generating code for.
+ Node *n;
+ // The name of the Go function.
+ String *go_name;
+ // The overload string for an overloaded function.
+ String *overname;
+ // The name of the C wrapper function.
+ String *wname;
+ // The base classes.
+ List *base;
+ // The parameters.
+ ParmList *parms;
+ // The result type.
+ SwigType *result;
+ // Whether this is a static function, not a class method.
+ bool is_static;
+ // The Go receiver type.
+ String *receiver;
+ // Whether this is a class constructor.
+ bool is_constructor;
+ // Whether this is a class destructor.
+ bool is_destructor;
+ };
+
+ /* ----------------------------------------------------------------------
+ * makeCgoWrappers()
+ *
+ * Write out the wrappers for a function when producing cgo input
+ * files.
+ * ---------------------------------------------------------------------- */
+
+ int makeCgoWrappers(Node *n, String *go_name, String *overname, String *wname, List *base, ParmList *parms, SwigType *result, bool is_static) {
+ Swig_save("makeCgoWrappers", n, "emit:cgotype", "emit:cgotypestruct", NULL);
+
+ cgoWrapperInfo info;
+
+ info.n = n;
+ info.go_name = go_name;
+ info.overname = overname;
+ info.wname = wname;
+ info.base = base;
+ info.parms = parms;
+ info.result = result;
+ info.is_static = is_static;
+
+ info.receiver = class_receiver;
+ if (is_static) {
+ info.receiver = NULL;
+ }
+
+ String *nodetype = Getattr(n, "nodeType");
+ info.is_constructor = Cmp(nodetype, "constructor") == 0;
+ info.is_destructor = Cmp(nodetype, "destructor") == 0;
+ if (info.is_constructor || info.is_destructor) {
+ assert(class_receiver);
+ assert(!base);
+ info.receiver = NULL;
+ }
+
+ int ret = SWIG_OK;
+
+ int r = cgoGoWrapper(&info);
+ if (r != SWIG_OK) {
+ ret = r;
+ }
+
+ r = cgoCommentWrapper(&info);
+ if (r != SWIG_OK) {
+ ret = r;
+ }
+
+ r = cgoGccWrapper(&info);
+ if (r != SWIG_OK) {
+ ret = r;
+ }
+
+ Swig_restore(n);
+
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * cgoGoWrapper()
+ *
+ * Write out Go code to call a cgo function. This code will go into
+ * the generated Go output file.
+ * ---------------------------------------------------------------------- */
+ int cgoGoWrapper(const cgoWrapperInfo *info) {
+
+ Wrapper *dummy = initGoTypemaps(info->parms);
+
+ bool add_to_interface = interfaces && !info->is_constructor && !info->is_destructor && !info->is_static && !info->overname && checkFunctionVisibility(info->n, NULL);
+
+ Printv(f_go_wrappers, "func ", NULL);
+
+ Parm *p = info->parms;
+ int pi = 0;
+
+ // Add the receiver first if this is a method.
+ if (info->receiver) {
+ Printv(f_go_wrappers, "(", NULL);
+ if (info->base && info->receiver) {
+ Printv(f_go_wrappers, "_swig_base", NULL);
+ } else {
+ Printv(f_go_wrappers, Getattr(p, "lname"), NULL);
+ p = nextParm(p);
+ ++pi;
+ }
+ Printv(f_go_wrappers, " ", info->receiver, ") ", NULL);
+ }
+
+ Printv(f_go_wrappers, info->go_name, NULL);
+ if (info->overname) {
+ Printv(f_go_wrappers, info->overname, NULL);
+ }
+ Printv(f_go_wrappers, "(", NULL);
+
+ // If we are doing methods, add this method to the interface.
+ if (add_to_interface) {
+ Printv(interfaces, "\t", info->go_name, "(", NULL);
+ }
+
+ // Write out the parameters to both the function definition and
+ // the interface.
+
+ String *parm_print = NewString("");
+
+ int parm_count = emit_num_arguments(info->parms);
+ int required_count = emit_num_required(info->parms);
+ int args = 0;
+
+ for (; pi < parm_count; ++pi) {
+ p = getParm(p);
+ if (pi == 0 && info->is_destructor) {
+ String *cl = exportedName(class_name);
+ Printv(parm_print, Getattr(p, "lname"), " ", cl, NULL);
+ Delete(cl);
+ ++args;
+ } else {
+ if (args > 0) {
+ Printv(parm_print, ", ", NULL);
+ }
+ ++args;
+ if (pi >= required_count) {
+ Printv(parm_print, "_swig_args ...interface{}", NULL);
+ break;
+ }
+ Printv(parm_print, Getattr(p, "lname"), " ", NULL);
+ String *tm = goType(p, Getattr(p, "type"));
+ Printv(parm_print, tm, NULL);
+ Delete(tm);
+ }
+ p = nextParm(p);
+ }
+
+ Printv(parm_print, ")", NULL);
+
+ // Write out the result type.
+ if (info->is_constructor) {
+ String *cl = exportedName(class_name);
+ Printv(parm_print, " (_swig_ret ", cl, ")", NULL);
+ Delete(cl);
+ } else {
+ if (SwigType_type(info->result) != T_VOID) {
+ String *tm = goType(info->n, info->result);
+ Printv(parm_print, " (_swig_ret ", tm, ")", NULL);
+ Delete(tm);
+ }
+ }
+
+ Printv(f_go_wrappers, parm_print, NULL);
+ if (add_to_interface) {
+ Printv(interfaces, parm_print, "\n", NULL);
+ }
+
+ // Write out the function body.
+
+ Printv(f_go_wrappers, " {\n", NULL);
+
+ if (parm_count > required_count) {
+ Parm *p = info->parms;
+ int i;
+ for (i = 0; i < required_count; ++i) {
+ p = getParm(p);
+ p = nextParm(p);
+ }
+ for (; i < parm_count; ++i) {
+ p = getParm(p);
+ String *tm = goType(p, Getattr(p, "type"));
+ Printv(f_go_wrappers, "\tvar ", Getattr(p, "lname"), " ", tm, "\n", NULL);
+ Printf(f_go_wrappers, "\tif len(_swig_args) > %d {\n", i - required_count);
+ Printf(f_go_wrappers, "\t\t%s = _swig_args[%d].(%s)\n", Getattr(p, "lname"), i - required_count, tm);
+ Printv(f_go_wrappers, "\t}\n", NULL);
+ Delete(tm);
+ p = nextParm(p);
+ }
+ }
+
+ String *call = NewString("\t");
+
+ String *ret_type = NULL;
+ bool memcpy_ret = false;
+ String *wt = NULL;
+ if (SwigType_type(info->result) != T_VOID) {
+ if (info->is_constructor) {
+ ret_type = exportedName(class_name);
+ } else {
+ ret_type = goImType(info->n, info->result);
+ }
+ Printv(f_go_wrappers, "\tvar swig_r ", ret_type, "\n", NULL);
+
+ bool c_struct_type;
+ Delete(cgoTypeForGoValue(info->n, info->result, &c_struct_type));
+ if (c_struct_type) {
+ memcpy_ret = true;
+ }
+
+ if (memcpy_ret) {
+ Printv(call, "swig_r_p := ", NULL);
+ } else {
+ Printv(call, "swig_r = (", ret_type, ")(", NULL);
+ }
+
+ if (info->is_constructor || goTypeIsInterface(info->n, info->result)) {
+ if (info->is_constructor) {
+ wt = Copy(class_receiver);
+ } else {
+ wt = goWrapperType(info->n, info->result, true);
+ }
+ Printv(call, wt, "(", NULL);
+ }
+ }
+
+ Printv(call, "C.", info->wname, "(", NULL);
+
+ args = 0;
+
+ if (parm_count > required_count) {
+ Printv(call, "C.swig_intgo(len(_swig_args))", NULL);
+ ++args;
+ }
+
+ if (info->base && info->receiver) {
+ if (args > 0) {
+ Printv(call, ", ", NULL);
+ }
+ ++args;
+ Printv(call, "C.uintptr_t(_swig_base)", NULL);
+ }
+
+ p = info->parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ if (args > 0) {
+ Printv(call, ", ", NULL);
+ }
+ ++args;
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+
+ String *ivar = NewStringf("_swig_i_%d", i);
+
+ String *goin = goGetattr(p, "tmap:goin");
+ if (goin == NULL) {
+ Printv(f_go_wrappers, "\t", ivar, " := ", NULL);
+ bool need_close = false;
+ if ((i == 0 && info->is_destructor) || ((i > 0 || !info->receiver || info->base || info->is_constructor) && goTypeIsInterface(p, pt))) {
+ Printv(f_go_wrappers, "getSwigcptr(", NULL);
+ need_close = true;
+ }
+ Printv(f_go_wrappers, ln, NULL);
+ if (need_close) {
+ Printv(f_go_wrappers, ")", NULL);
+ }
+ Printv(f_go_wrappers, "\n", NULL);
+ Setattr(p, "emit:goinput", ln);
+ } else {
+ String *itm = goImType(p, pt);
+ Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
+ goin = Copy(goin);
+ Replaceall(goin, "$input", ln);
+ Replaceall(goin, "$result", ivar);
+ Printv(f_go_wrappers, goin, "\n", NULL);
+ Delete(goin);
+ Setattr(p, "emit:goinput", ivar);
+ }
+
+ bool c_struct_type;
+ String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
+ if (c_struct_type) {
+ Printv(call, "*(*C.", ct, ")(unsafe.Pointer(&", ivar, "))", NULL);
+ } else {
+ Printv(call, "C.", ct, "(", ivar, ")", NULL);
+ }
+ Delete(ct);
+
+ p = nextParm(p);
+ }
+
+ Printv(f_go_wrappers, call, ")", NULL);
+ Delete(call);
+
+ if (wt) {
+ // Close the type conversion to the wrapper type.
+ Printv(f_go_wrappers, ")", NULL);
+ }
+ if (SwigType_type(info->result) != T_VOID && !memcpy_ret) {
+ // Close the type conversion of the return value.
+ Printv(f_go_wrappers, ")", NULL);
+ }
+
+ Printv(f_go_wrappers, "\n", NULL);
+
+ if (memcpy_ret) {
+ Printv(f_go_wrappers, "\tswig_r = *(*", ret_type, ")(unsafe.Pointer(&swig_r_p))\n", NULL);
+ }
+ if (ret_type) {
+ Delete(ret_type);
+ }
+
+ goargout(info->parms);
+
+ if (SwigType_type(info->result) != T_VOID) {
+
+ Swig_save("cgoGoWrapper", info->n, "type", "tmap:goout", NULL);
+ Setattr(info->n, "type", info->result);
+
+ String *goout = goTypemapLookup("goout", info->n, "swig_r");
+ if (goout == NULL) {
+ Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
+ } else {
+ String *tm = goType(info->n, info->result);
+ Printv(f_go_wrappers, "\tvar swig_r_1 ", tm, "\n", NULL);
+ goout = Copy(goout);
+ Replaceall(goout, "$input", "swig_r");
+ Replaceall(goout, "$result", "swig_r_1");
+ Printv(f_go_wrappers, goout, "\n", NULL);
+ Printv(f_go_wrappers, "\treturn swig_r_1\n", NULL);
+ }
+
+ Swig_restore(info->n);
+ }
+
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ DelWrapper(dummy);
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * cgoCommentWrapper()
+ *
+ * Write out a cgo function to call a C/C++ function. This code
+ * will go into the cgo comment in the generated Go output file.
+ * ---------------------------------------------------------------------- */
+ int cgoCommentWrapper(const cgoWrapperInfo *info) {
+ String *ret_type;
+ if (SwigType_type(info->result) == T_VOID) {
+ ret_type = NewString("void");
+ } else {
+ bool c_struct_type;
+ ret_type = cgoTypeForGoValue(info->n, info->result, &c_struct_type);
+ }
+
+ Printv(f_cgo_comment, "extern ", ret_type, " ", info->wname, "(", NULL);
+
+ Delete(ret_type);
+
+ int parm_count = emit_num_arguments(info->parms);
+ int required_count = emit_num_required(info->parms);
+ int args = 0;
+
+ if (parm_count > required_count) {
+ Printv(f_cgo_comment, "intgo _swig_args", NULL);
+ ++args;
+ }
+
+ if (info->base && info->receiver) {
+ if (args > 0) {
+ Printv(f_cgo_comment, ", ", NULL);
+ }
+ ++args;
+ Printv(f_cgo_comment, "uintptr_t _swig_base", NULL);
+ }
+
+ Parm *p = info->parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ if (args > 0) {
+ Printv(f_cgo_comment, ", ", NULL);
+ }
+ ++args;
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+
+ bool c_struct_type;
+ String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
+ Printv(f_cgo_comment, ct, " ", ln, NULL);
+ Delete(ct);
+
+ p = nextParm(p);
+ }
+
+ if (args == 0) {
+ Printv(f_cgo_comment, "void", NULL);
+ }
+
+ Printv(f_cgo_comment, ");\n", NULL);
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * cgoGccWrapper()
+ *
+ * Write out code to the C/C++ wrapper file. This code will be
+ * called by the code generated by cgoCommentWrapper.
+ * ---------------------------------------------------------------------- */
+ int cgoGccWrapper(const cgoWrapperInfo *info) {
+ Wrapper *f = NewWrapper();
+
+ Swig_save("cgoGccWrapper", info->n, "parms", NULL);
+
+ ParmList *parms = info->parms;
+
+ Parm *base_parm = NULL;
+ if (info->base && !isStatic(info->n)) {
+ SwigType *base_type = Copy(getClassType());
+ SwigType_add_pointer(base_type);
+ base_parm = NewParm(base_type, NewString("arg1"), info->n);
+ set_nextSibling(base_parm, parms);
+ parms = base_parm;
+ }
+
+ emit_parameter_variables(parms, f);
+ emit_attach_parmmaps(parms, f);
+ int parm_count = emit_num_arguments(parms);
+ int required_count = emit_num_required(parms);
+
+ emit_return_variable(info->n, info->result, f);
+
+ // Start the function definition.
+
+ String *fnname = NewString("");
+ Printv(fnname, info->wname, "(", NULL);
+
+ int args = 0;
+
+ if (parm_count > required_count) {
+ Printv(fnname, "intgo _swig_optargc", NULL);
+ ++args;
+ }
+
+ Parm *p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ if (args > 0) {
+ Printv(fnname, ", ", NULL);
+ }
+ ++args;
+
+ p = getParm(p);
+
+ SwigType *pt = Copy(Getattr(p, "type"));
+ if (SwigType_isarray(pt) && Getattr(p, "tmap:gotype") == NULL) {
+ SwigType_del_array(pt);
+ SwigType_add_pointer(pt);
+ }
+ String *pn = NewStringf("_swig_go_%d", i);
+ String *ct = gcCTypeForGoValue(p, pt, pn);
+ Printv(fnname, ct, NULL);
+ Delete(ct);
+ Delete(pn);
+ Delete(pt);
+
+ p = nextParm(p);
+ }
+
+ Printv(fnname, ")", NULL);
+
+ if (SwigType_type(info->result) == T_VOID) {
+ Printv(f->def, "void ", fnname, NULL);
+ } else {
+ String *ct = gcCTypeForGoValue(info->n, info->result, fnname);
+ Printv(f->def, ct, NULL);
+ Delete(ct);
+
+ String *ln = NewString("_swig_go_result");
+ ct = gcCTypeForGoValue(info->n, info->result, ln);
+ Wrapper_add_local(f, "_swig_go_result", ct);
+ Delete(ct);
+ Delete(ln);
+ }
+
+ Delete(fnname);
+
+ Printv(f->def, " {\n", NULL);
+
+ // Apply the in typemaps.
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ String *tm = Getattr(p, "tmap:in");
+ if (!tm) {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "unable to use type %s as a function argument\n", SwigType_str(Getattr(p, "type"), 0));
+ } else {
+ tm = Copy(tm);
+ String *pn = NewStringf("_swig_go_%d", i);
+ Replaceall(tm, "$input", pn);
+ if (i < required_count) {
+ Printv(f->code, "\t", tm, "\n", NULL);
+ } else {
+ Printf(f->code, "\tif (_swig_optargc > %d) {\n", i - required_count);
+ Printv(f->code, "\t\t", tm, "\n", NULL);
+ Printv(f->code, "\t}\n", NULL);
+ }
+ Delete(tm);
+ Setattr(p, "emit:input", pn);
+ }
+ p = nextParm(p);
+ }
+
+ Printv(f->code, "\n", NULL);
+
+ // Do the real work of the function.
+
+ checkConstraints(parms, f);
+
+ emitGoAction(info->n, info->base, parms, info->result, f);
+
+ argout(parms, f);
+
+ cleanupFunction(info->n, f, parms);
+
+ if (SwigType_type(info->result) != T_VOID) {
+ Printv(f->code, "\treturn _swig_go_result;\n", NULL);
+ }
+
+ Printv(f->code, "}\n", NULL);
+
+ Wrapper_print(f, f_c_wrappers);
+
+ Swig_restore(info->n);
+
+ DelWrapper(f);
+ if (base_parm) {
+ Delete(base_parm);
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * initGoTypemaps()
+ *
+ * Initialize the typenames for a Go wrapper, returning a dummy
+ * Wrapper*. Also set consistent names for the parameters.
+ * ---------------------------------------------------------------------- */
+
+ Wrapper* initGoTypemaps(ParmList *parms) {
+ Wrapper *dummy = NewWrapper();
+ emit_attach_parmmaps(parms, dummy);
+
+ Parm *p = parms;
+ int parm_count = emit_num_arguments(parms);
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ Swig_cparm_name(p, i);
+ p = nextParm(p);
+ }
+
+ Swig_typemap_attach_parms("default", parms, dummy);
+ Swig_typemap_attach_parms("gotype", parms, dummy);
+ Swig_typemap_attach_parms("goin", parms, dummy);
+ Swig_typemap_attach_parms("goargout", parms, dummy);
+ Swig_typemap_attach_parms("imtype", parms, dummy);
+
+ return dummy;
+ }
+
+ /* -----------------------------------------------------------------------
+ * checkConstraints()
+ *
+ * Check parameter constraints if any. This is used for the C/C++
+ * function. This assumes that each parameter has an "emit:input"
+ * property with the name to use to refer to that parameter.
+ * ----------------------------------------------------------------------- */
+
+ void checkConstraints(ParmList *parms, Wrapper *f) {
+ Parm *p = parms;
+ while (p) {
+ String *tm = Getattr(p, "tmap:check");
+ if (!tm) {
+ p = nextSibling(p);
+ } else {
+ tm = Copy(tm);
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(f->code, tm, "\n\n", NULL);
+ Delete(tm);
+ p = Getattr(p, "tmap:check:next");
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------------
+ * emitGoAction()
+ *
+ * Emit the action of the function. This is used for the C/C++ function.
+ * ----------------------------------------------------------------------- */
+
+ void emitGoAction(Node *n, List *base, ParmList *parms, SwigType *result, Wrapper *f) {
+ String *actioncode;
+ if (!base || isStatic(n)) {
+ Swig_director_emit_dynamic_cast(n, f);
+ actioncode = emit_action(n);
+ } else {
+ // Call the base class method.
+ actioncode = NewString("");
+
+ String *current = NewString("");
+ Printv(current, Getattr(parms, "lname"), NULL);
+
+ int vc = 0;
+ for (Iterator bi = First(base); bi.item; bi = Next(bi)) {
+ Printf(actioncode, " %s *swig_b%d = (%s *)%s;\n", bi.item, vc, bi.item, current);
+ Delete(current);
+ current = NewString("");
+ Printf(current, "swig_b%d", vc);
+ ++vc;
+ }
+
+ String *code = Copy(Getattr(n, "wrap:action"));
+ Replace(code, Getattr(parms, "lname"), current, DOH_REPLACE_ANY | DOH_REPLACE_ID);
+ Delete(current);
+ Printv(actioncode, code, "\n", NULL);
+ }
+
+ Swig_save("emitGoAction", n, "type", "tmap:out", NULL);
+
+ Setattr(n, "type", result);
+
+ String *tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode);
+ if (!tm) {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s\n", SwigType_str(result, 0));
+ } else {
+ Replaceall(tm, "$result", "_swig_go_result");
+ if (GetFlag(n, "feature:new")) {
+ Replaceall(tm, "$owner", "1");
+ } else {
+ Replaceall(tm, "$owner", "0");
+ }
+ Printv(f->code, tm, "\n", NULL);
+ Delete(tm);
+ }
+
+ Swig_restore(n);
+ }
+
+ /* -----------------------------------------------------------------------
+ * argout()
+ *
+ * Handle argument output code if any. This is used for the C/C++
+ * function. This assumes that each parameter has an "emit:input"
+ * property with the name to use to refer to that parameter.
+ * ----------------------------------------------------------------------- */
+
+ void argout(ParmList *parms, Wrapper *f) {
+ Parm *p = parms;
+ while (p) {
+ String *tm = Getattr(p, "tmap:argout");
+ if (!tm) {
+ p = nextSibling(p);
+ } else {
+ tm = Copy(tm);
+ Replaceall(tm, "$result", Swig_cresult_name());
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(f->code, tm, "\n", NULL);
+ Delete(tm);
+ p = Getattr(p, "tmap:argout:next");
+ }
+ }
+ }
+
+ /* -----------------------------------------------------------------------
+ * goargout()
+ *
+ * Handle Go argument output code if any. This is used for the Go
+ * function. This assumes that each parameter has an "emit:goinput"
+ * property with the name to use to refer to that parameter.
+ * ----------------------------------------------------------------------- */
+
+ void goargout(ParmList *parms) {
+ Parm *p = parms;
+ while (p) {
+ String *tm = Getattr(p, "tmap:goargout");
+ if (!tm) {
+ p = nextSibling(p);
+ } else {
+ tm = Copy(tm);
+ Replaceall(tm, "$result", "swig_r");
+ Replaceall(tm, "$input", Getattr(p, "emit:goinput"));
+ Printv(f_go_wrappers, tm, "\n", NULL);
+ Delete(tm);
+ p = Getattr(p, "tmap:goargout:next");
+ }
+ }
+
+ // If we need to memcpy a parameter to pass it to the C code, the
+ // compiler may think that the parameter is not live during the
+ // function call. If the garbage collector runs while the C/C++
+ // function is running, the parameter may be freed. Force the
+ // compiler to see the parameter as live across the C/C++ function.
+ int parm_count = emit_num_arguments(parms);
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ bool c_struct_type;
+ Delete(cgoTypeForGoValue(p, Getattr(p, "type"), &c_struct_type));
+ if (c_struct_type) {
+ Printv(f_go_wrappers, "\tif Swig_escape_always_false {\n", NULL);
+ Printv(f_go_wrappers, "\t\tSwig_escape_val = ", Getattr(p, "emit:goinput"), "\n", NULL);
+ Printv(f_go_wrappers, "\t}\n", NULL);
+ }
+ p = nextParm(p);
+ }
+ }
+
+ /* -----------------------------------------------------------------------
+ * freearg()
+ *
+ * Handle argument cleanup code if any. This is used for the C/C++
+ * function. This assumes that each parameter has an "emit:input"
+ * property with the name to use to refer to that parameter.
+ * ----------------------------------------------------------------------- */
+
+ String *freearg(ParmList *parms) {
+ String *ret = NewString("");
+ Parm *p = parms;
+ while (p) {
+ String *tm = Getattr(p, "tmap:freearg");
+ if (!tm) {
+ p = nextSibling(p);
+ } else {
+ tm = Copy(tm);
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(ret, tm, "\n", NULL);
+ Delete(tm);
+ p = Getattr(p, "tmap:freearg:next");
+ }
+ }
+ return ret;
+ }
+
+ /* -----------------------------------------------------------------------
+ * cleanupFunction()
+ *
+ * Final function cleanup code.
+ * ----------------------------------------------------------------------- */
+
+ void cleanupFunction(Node *n, Wrapper *f, ParmList *parms) {
+ String *cleanup = freearg(parms);
+ Printv(f->code, cleanup, NULL);
+
+ if (GetFlag(n, "feature:new")) {
+ String *tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0);
+ if (tm) {
+ Printv(f->code, tm, "\n", NULL);
+ Delete(tm);
+ }
+ }
+
+ Replaceall(f->code, "$cleanup", cleanup);
+ Delete(cleanup);
+
+ /* See if there is any return cleanup code */
+ String *tm;
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+
+ Replaceall(f->code, "$symname", Getattr(n, "sym:name"));
+ }
+
+ /* -----------------------------------------------------------------------
+ * variableHandler()
+ *
+ * This exists just to set the making_variable_wrappers flag.
+ * ----------------------------------------------------------------------- */
+
+ virtual int variableHandler(Node *n) {
+ assert(!making_variable_wrappers);
+ making_variable_wrappers = true;
+ int r = Language::variableHandler(n);
+ making_variable_wrappers = false;
+ return r;
+ }
+
+ /* -----------------------------------------------------------------------
+ * constantWrapper()
+ *
+ * Product a const declaration.
+ * ------------------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ SwigType *type = Getattr(n, "type");
+
+ if (!SwigType_issimple(type) && SwigType_type(type) != T_STRING) {
+ return goComplexConstant(n, type);
+ }
+
+ if (Swig_storage_isstatic(n)) {
+ return goComplexConstant(n, type);
+ }
+
+ String *go_name = buildGoName(Getattr(n, "sym:name"), false, false);
+
+ String *tm = goType(n, type);
+ String *value = Getattr(n, "value");
+
+ String *copy = NULL;
+ if (SwigType_type(type) == T_BOOL) {
+ if (Cmp(value, "true") != 0 && Cmp(value, "false") != 0) {
+ return goComplexConstant(n, type);
+ }
+ } else if (SwigType_type(type) == T_STRING || SwigType_type(type) == T_CHAR) {
+ // Backslash sequences are somewhat different in Go and C/C++.
+ if (Strchr(value, '\\') != 0) {
+ return goComplexConstant(n, type);
+ }
+ } else {
+ // Accept a 0x prefix, and strip combinations of u and l
+ // suffixes. Otherwise accept digits, decimal point, and
+ // exponentiation. Treat anything else as too complicated to
+ // handle as a Go constant.
+ char *p = Char(value);
+ int len = (int)strlen(p);
+ bool need_copy = false;
+ while (len > 0) {
+ char c = p[len - 1];
+ if (c != 'l' && c != 'L' && c != 'u' && c != 'U') {
+ break;
+ }
+ --len;
+ need_copy = true;
+ }
+ bool is_hex = false;
+ int i = 0;
+ if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ i = 2;
+ is_hex = true;
+ }
+ for (; i < len; ++i) {
+ switch (p[i]) {
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'f': case 'A': case 'B': case 'C': case 'D': case 'F':
+ if (!is_hex) {
+ return goComplexConstant(n, type);
+ }
+ break;
+ case '.': case 'e': case 'E': case '+': case '-':
+ break;
+ default:
+ return goComplexConstant(n, type);
+ }
+ }
+ if (need_copy) {
+ copy = Copy(value);
+ Replaceall(copy, p + len, "");
+ value = copy;
+ }
+ }
+
+ if (!checkNameConflict(go_name, n, NULL)) {
+ Delete(tm);
+ Delete(go_name);
+ Delete(copy);
+ return SWIG_NOWRAP;
+ }
+
+ Printv(f_go_wrappers, "const ", go_name, " ", tm, " = ", NULL);
+ if (SwigType_type(type) == T_STRING) {
+ Printv(f_go_wrappers, "\"", value, "\"", NULL);
+ } else if (SwigType_type(type) == T_CHAR) {
+ Printv(f_go_wrappers, "'", value, "'", NULL);
+ } else {
+ Printv(f_go_wrappers, value, NULL);
+ }
+
+ Printv(f_go_wrappers, "\n", NULL);
+
+ Delete(tm);
+ Delete(go_name);
+ Delete(copy);
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * enumDeclaration()
+ *
+ * A C++ enum type turns into a Named go int type.
+ * ---------------------------------------------------------------------- */
+
+ virtual int enumDeclaration(Node *n) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ String *name = goEnumName(n);
+ if (Strcmp(name, "int") != 0) {
+ if (!ImportMode || !imported_package) {
+ if (!checkNameConflict(name, n, NULL)) {
+ Delete(name);
+ return SWIG_NOWRAP;
+ }
+ Printv(f_go_wrappers, "type ", name, " int\n", NULL);
+ } else {
+ String *nw = NewString("");
+ Printv(nw, getModuleName(imported_package), ".", name, NULL);
+ Setattr(n, "go:enumname", nw);
+ }
+ }
+ Delete(name);
+
+ return Language::enumDeclaration(n);
+ }
+
+ /* -----------------------------------------------------------------------
+ * enumvalueDeclaration()
+ *
+ * Declare a single value of an enum type. We fetch the value by
+ * calling a C/C++ function.
+ * ------------------------------------------------------------------------ */
+
+ virtual int enumvalueDeclaration(Node *n) {
+ if (!is_public(n)) {
+ return SWIG_OK;
+ }
+
+ Swig_require("enumvalueDeclaration", n, "*sym:name", NIL);
+ Node *parent = parentNode(n);
+
+ if (Getattr(parent, "unnamed")) {
+ Setattr(n, "type", NewString("int"));
+ } else {
+ Setattr(n, "type", Getattr(parent, "enumtype"));
+ }
+
+ if (GetFlag(parent, "scopedenum")) {
+ String *symname = Getattr(n, "sym:name");
+ symname = Swig_name_member(0, Getattr(parent, "sym:name"), symname);
+ Setattr(n, "sym:name", symname);
+ Delete(symname);
+ }
+
+ int ret = goComplexConstant(n, Getattr(n, "type"));
+ Swig_restore(n);
+ return ret;
+ }
+
+ /* -----------------------------------------------------------------------
+ * goComplexConstant()
+ *
+ * Handle a const declaration for something which is not a Go constant.
+ * ------------------------------------------------------------------------ */
+
+ int goComplexConstant(Node *n, SwigType *type) {
+ String *symname = Getattr(n, "sym:name");
+ if (!symname) {
+ symname = Getattr(n, "name");
+ }
+
+ String *varname = buildGoName(symname, true, false);
+
+ if (!checkNameConflict(varname, n, NULL)) {
+ Delete(varname);
+ return SWIG_NOWRAP;
+ }
+
+ String *rawval = Getattr(n, "rawval");
+ if (rawval && Len(rawval)) {
+ // Based on Swig_VargetToFunction
+ String *nname = NewStringf("(%s)", rawval);
+ String *call;
+ if (SwigType_isclass(type)) {
+ call = NewStringf("%s", nname);
+ } else {
+ call = SwigType_lcaststr(type, nname);
+ }
+ String *cres = Swig_cresult(type, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(nname);
+ Delete(call);
+ Delete(cres);
+ } else {
+ String *get = NewString("");
+ Printv(get, Swig_cresult_name(), " = ", NULL);
+
+ char quote;
+ if (Getattr(n, "wrappedasconstant")) {
+ quote = '\0';
+ } else if (SwigType_type(type) == T_CHAR) {
+ quote = '\'';
+ } else if (SwigType_type(type) == T_STRING) {
+ Printv(get, "(char *)", NULL);
+ quote = '"';
+ } else {
+ quote = '\0';
+ }
+
+ if (quote != '\0') {
+ Printf(get, "%c", quote);
+ }
+
+ Printv(get, Getattr(n, "value"), NULL);
+
+ if (quote != '\0') {
+ Printf(get, "%c", quote);
+ }
+
+ Printv(get, ";\n", NULL);
+
+ Setattr(n, "wrap:action", get);
+ Delete(get);
+ }
+
+ String *sname = Copy(symname);
+ if (class_name) {
+ Append(sname, "_");
+ Append(sname, class_name);
+ }
+
+ String *go_name = NewString("_swig_get");
+ if (class_name) {
+ Append(go_name, class_name);
+ Append(go_name, "_");
+ }
+ Append(go_name, sname);
+
+ String *wname = Swig_name_wrapper(sname);
+ Append(wname, unique_id);
+ Setattr(n, "wrap:name", wname);
+
+ int r = makeWrappers(n, go_name, NULL, wname, NULL, NULL, type, true);
+
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ String *t = goType(n, type);
+ Printv(f_go_wrappers, "var ", varname, " ", t, " = ", go_name, "()\n", NULL);
+
+ Delete(varname);
+ Delete(t);
+ Delete(go_name);
+ Delete(sname);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classHandler()
+ *
+ * For a C++ class, in Go we generate both a struct and an
+ * interface. The interface will declare all the class public
+ * methods. We will define all the methods on the struct, so that
+ * the struct meets the interface. We then expect users of the
+ * class to use the interface.
+ * ------------------------------------------------------------ */
+
+ virtual int classHandler(Node *n) {
+ class_node = n;
+
+ List *baselist = Getattr(n, "bases");
+ bool has_base_classes = baselist && Len(baselist) > 0;
+
+ String *name = Getattr(n, "sym:name");
+
+ String *go_name = exportedName(name);
+
+ if (!checkNameConflict(go_name, n, NULL)) {
+ Delete(go_name);
+ SetFlag(n, "go:conflict");
+ return SWIG_NOWRAP;
+ }
+
+ String *go_type_name = goCPointerType(Getattr(n, "classtypeobj"), true);
+
+ class_name = name;
+ class_receiver = go_type_name;
+ class_methods = NewHash();
+
+ int isdir = GetFlag(n, "feature:director");
+ int isnodir = GetFlag(n, "feature:nodirector");
+ bool is_director = isdir && !isnodir;
+
+ Printv(f_go_wrappers, "type ", go_type_name, " uintptr\n\n", NULL);
+
+ // A method to return the pointer to the C++ class. This is used
+ // by generated code to convert between the interface and the C++
+ // value.
+ Printv(f_go_wrappers, "func (p ", go_type_name, ") Swigcptr() uintptr {\n", NULL);
+ Printv(f_go_wrappers, "\treturn (uintptr)(p)\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ // A method used as a marker for the class, to avoid invalid
+ // interface conversions when using multiple inheritance.
+ Printv(f_go_wrappers, "func (p ", go_type_name, ") SwigIs", go_name, "() {\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ if (is_director) {
+ // Return the interface passed to the NewDirector function.
+ Printv(f_go_wrappers, "func (p ", go_type_name, ") DirectorInterface() interface{} {\n", NULL);
+ Printv(f_go_wrappers, "\treturn nil\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+ }
+
+ // We have seen a definition for this type.
+ Setattr(defined_types, go_name, go_name);
+ Setattr(defined_types, go_type_name, go_type_name);
+
+ interfaces = NewString("");
+
+ int r = Language::classHandler(n);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ if (has_base_classes) {
+ // For each method defined in a base class but not defined in
+ // this class, we need to define the method in this class. We
+ // can't use anonymous field inheritance because it works
+ // differently in Go and in C++.
+
+ Hash *local = NewHash();
+ for (Node *ni = Getattr(n, "firstChild"); ni; ni = nextSibling(ni)) {
+
+ if (!is_public(ni)) {
+ continue;
+ }
+
+ String *type = Getattr(ni, "nodeType");
+ if (Cmp(type, "constructor") == 0 || Cmp(type, "destructor") == 0) {
+ continue;
+ }
+
+ String *cname = Getattr(ni, "sym:name");
+ if (!cname) {
+ cname = Getattr(ni, "name");
+ }
+ if (cname) {
+ Setattr(local, cname, NewString(""));
+ }
+ }
+
+ for (Iterator b = First(baselist); b.item; b = Next(b)) {
+ List *bases = NewList();
+ Append(bases, Getattr(b.item, "classtype"));
+ int r = addBase(n, b.item, bases, local);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ Delete(bases);
+ }
+
+ Delete(local);
+
+ Hash *parents = NewHash();
+ addFirstBaseInterface(n, parents, baselist);
+ int r = addExtraBaseInterfaces(n, parents, baselist);
+ Delete(parents);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+
+ Printv(f_go_wrappers, "type ", go_name, " interface {\n", NULL);
+ Printv(f_go_wrappers, "\tSwigcptr() uintptr\n", NULL);
+ Printv(f_go_wrappers, "\tSwigIs", go_name, "()\n", NULL);
+
+ if (is_director) {
+ Printv(f_go_wrappers, "\tDirectorInterface() interface{}\n", NULL);
+ }
+
+ Append(f_go_wrappers, interfaces);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+ Delete(interfaces);
+
+ interfaces = NULL;
+ class_name = NULL;
+ class_receiver = NULL;
+ class_node = NULL;
+ Delete(class_methods);
+ class_methods = NULL;
+
+ Delete(go_type_name);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * addBase()
+ *
+ * Implement methods and members defined in a parent class for a
+ * child class.
+ * ------------------------------------------------------------ */
+
+ int addBase(Node *n, Node *base, List *bases, Hash *local) {
+ if (GetFlag(base, "feature:ignore")) {
+ return SWIG_OK;
+ }
+
+ for (Node *ni = Getattr(base, "firstChild"); ni; ni = nextSibling(ni)) {
+ int r = goBaseEntry(n, bases, local, ni);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+
+ List *baselist = Getattr(base, "bases");
+ if (baselist && Len(baselist) > 0) {
+ for (Iterator b = First(baselist); b.item; b = Next(b)) {
+ List *nb = Copy(bases);
+ Append(nb, Getattr(b.item, "classtype"));
+ int r = addBase(n, b.item, nb, local);
+ Delete(nb);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * goBaseEntry()
+ *
+ * Implement one entry defined in a parent class for a child class.
+ * n is the child class.
+ * ------------------------------------------------------------ */
+
+ int goBaseEntry(Node* n, List* bases, Hash *local, Node* entry) {
+ if (GetFlag(entry, "feature:ignore")) {
+ return SWIG_OK;
+ }
+
+ if (!is_public(entry)) {
+ return SWIG_OK;
+ }
+
+ String *type = Getattr(entry, "nodeType");
+ if (Strcmp(type, "constructor") == 0 || Strcmp(type, "destructor") == 0 || Strcmp(type, "enum") == 0 || Strcmp(type, "using") == 0 || Strcmp(type, "classforward") == 0 || Strcmp(type, "template") == 0) {
+ return SWIG_OK;
+ }
+
+ if (Strcmp(type, "extend") == 0) {
+ for (Node* extend = firstChild(entry); extend; extend = nextSibling(extend)) {
+ if (isStatic(extend)) {
+ // If we don't do this, the extend_default test case fails.
+ continue;
+ }
+
+ int r = goBaseEntry(n, bases, local, extend);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+ return SWIG_OK;
+ }
+
+ String *storage = Getattr(entry, "storage");
+ if (storage && (Strcmp(storage, "typedef") == 0 || Strcmp(storage, "friend") == 0)) {
+ return SWIG_OK;
+ }
+
+ String *mname = Getattr(entry, "sym:name");
+ if (!mname) {
+ return SWIG_OK;
+ }
+
+ String *lname = Getattr(entry, "name");
+ if (Getattr(class_methods, lname)) {
+ return SWIG_OK;
+ }
+ if (Getattr(local, lname)) {
+ return SWIG_OK;
+ }
+ Setattr(local, lname, NewString(""));
+
+ String *ty = NewString(Getattr(entry, "type"));
+ SwigType_push(ty, Getattr(entry, "decl"));
+ String *fullty = SwigType_typedef_resolve_all(ty);
+ bool is_function = SwigType_isfunction(fullty) ? true : false;
+ Delete(ty);
+ Delete(fullty);
+
+ if (is_function) {
+ int r = goBaseMethod(n, bases, entry);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ if (Getattr(entry, "sym:overloaded")) {
+ for (Node *on = Getattr(entry, "sym:nextSibling"); on; on = Getattr(on, "sym:nextSibling")) {
+ r = goBaseMethod(n, bases, on);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+
+ String *receiver = class_receiver;
+ bool is_static = isStatic(entry);
+ if (is_static) {
+ receiver = NULL;
+ }
+ String *go_name = buildGoName(Getattr(entry, "sym:name"), is_static, false);
+ r = makeDispatchFunction(entry, go_name, receiver, is_static, NULL, false);
+ Delete(go_name);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+ } else {
+ int r = goBaseVariable(n, bases, entry);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * goBaseMethod()
+ *
+ * Implement a method defined in a parent class for a child class.
+ * ------------------------------------------------------------ */
+
+ int goBaseMethod(Node *method_class, List *bases, Node *method) {
+ String *symname = Getattr(method, "sym:name");
+ if (!validIdentifier(symname)) {
+ return SWIG_OK;
+ }
+
+ String *name = NewString("");
+ Printv(name, Getattr(method_class, "sym:name"), "_", symname, NULL);
+
+ bool is_static = isStatic(method);
+
+ String *go_name = buildGoName(name, is_static, false);
+
+ String *overname = NULL;
+ if (Getattr(method, "sym:overloaded")) {
+ overname = Getattr(method, "sym:overname");
+ }
+ String *wname = Swig_name_wrapper(name);
+ if (overname) {
+ Append(wname, overname);
+ }
+ Append(wname, unique_id);
+
+ String *result = NewString(Getattr(method, "type"));
+ SwigType_push(result, Getattr(method, "decl"));
+ if (SwigType_isqualifier(result)) {
+ Delete(SwigType_pop(result));
+ }
+ Delete(SwigType_pop_function(result));
+
+ // If the base method is imported, wrap:action may not be set.
+ Swig_save("goBaseMethod", method, "wrap:name", "wrap:action", "parms", NULL);
+ Setattr(method, "wrap:name", wname);
+ if (!Getattr(method, "wrap:action")) {
+ if (!is_static) {
+ Swig_MethodToFunction(method, getNSpace(), getClassType(), (Getattr(method, "template") ? SmartPointer : Extend | SmartPointer), NULL, false);
+ // Remove any self parameter that was just added.
+ ParmList *parms = Getattr(method, "parms");
+ if (parms && Getattr(parms, "self")) {
+ parms = CopyParmList(nextSibling(parms));
+ Setattr(method, "parms", parms);
+ }
+ } else {
+ String *call = Swig_cfunction_call(Getattr(method, "name"), Getattr(method, "parms"));
+ Setattr(method, "wrap:action", Swig_cresult(Getattr(method, "type"), Swig_cresult_name(), call));
+ }
+ }
+
+ // A method added by %extend in a base class may have void parms.
+ ParmList* parms = Getattr(method, "parms");
+ if (parms != NULL && SwigType_type(Getattr(parms, "type")) == T_VOID) {
+ parms = NULL;
+ }
+
+ int r = makeWrappers(method, go_name, overname, wname, bases, parms, result, is_static);
+
+ Swig_restore(method);
+
+ Delete(result);
+ Delete(go_name);
+ Delete(name);
+
+ return r;
+ }
+
+ /* ------------------------------------------------------------
+ * goBaseVariable()
+ *
+ * Add accessors for a member variable defined in a parent class for
+ * a child class.
+ * ------------------------------------------------------------ */
+
+ int goBaseVariable(Node *var_class, List *bases, Node *var) {
+ if (isStatic(var)) {
+ return SWIG_OK;
+ }
+
+ String *var_name = buildGoName(Getattr(var, "sym:name"), false, false);
+
+ Swig_save("goBaseVariable", var, "type", "wrap:action", NULL);
+
+ // For a pointer type we apparently have to wrap in the decl.
+ SwigType *var_type = NewString(Getattr(var, "type"));
+ SwigType_push(var_type, Getattr(var, "decl"));
+ Setattr(var, "type", var_type);
+
+ SwigType *vt = Copy(var_type);
+
+ int flags = Extend | SmartPointer | use_naturalvar_mode(var);
+ if (isNonVirtualProtectedAccess(var)) {
+ flags |= CWRAP_ALL_PROTECTED_ACCESS;
+ }
+
+ // Copied from Swig_wrapped_member_var_type.
+ if (SwigType_isclass(vt)) {
+ if (flags & CWRAP_NATURAL_VAR) {
+ if (CPlusPlus) {
+ if (!SwigType_isconst(vt)) {
+ SwigType_add_qualifier(vt, "const");
+ }
+ SwigType_add_reference(vt);
+ }
+ } else {
+ SwigType_add_pointer(vt);
+ }
+ }
+
+ String *mname = Swig_name_member(getNSpace(), Getattr(var_class, "sym:name"), var_name);
+
+ if (is_assignable(var)) {
+ for (Iterator ki = First(var); ki.key; ki = Next(ki)) {
+ if (Strncmp(ki.key, "tmap:", 5) == 0) {
+ Delattr(var, ki.key);
+ }
+ }
+ Swig_save("goBaseVariableSet", var, "name", "sym:name", "type", NULL);
+
+ String *mname_set = NewString("Set");
+ Append(mname_set, mname);
+
+ String *go_name = NewString("Set");
+ Append(go_name, var_name);
+
+ Swig_MembersetToFunction(var, class_name, flags);
+
+ String *wname = Swig_name_wrapper(mname_set);
+ Append(wname, unique_id);
+ ParmList *parms = NewParm(vt, var_name, var);
+ String *result = NewString("void");
+ int r = makeWrappers(var, go_name, NULL, wname, bases, parms, result, false);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ Delete(wname);
+ Delete(parms);
+ Delete(result);
+ Delete(go_name);
+ Delete(mname_set);
+
+ Swig_restore(var);
+ for (Iterator ki = First(var); ki.key; ki = Next(ki)) {
+ if (Strncmp(ki.key, "tmap:", 5) == 0) {
+ Delattr(var, ki.key);
+ }
+ }
+ }
+
+ Swig_MembergetToFunction(var, class_name, flags);
+
+ String *mname_get = NewString("Get");
+ Append(mname_get, mname);
+
+ String *go_name = NewString("Get");
+ Append(go_name, var_name);
+
+ String *wname = Swig_name_wrapper(mname_get);
+ Append(wname, unique_id);
+
+ int r = makeWrappers(var, go_name, NULL, wname, bases, NULL, vt, false);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ Delete(wname);
+ Delete(mname_get);
+ Delete(go_name);
+ Delete(mname);
+ Delete(var_name);
+ Delete(var_type);
+ Delete(vt);
+
+ Swig_restore(var);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * addFirstBaseInterface()
+ *
+ * When a C++ class uses multiple inheritance, we can use the C++
+ * pointer for the first base class but not for any subsequent base
+ * classes. However, the Go interface will match the interface for
+ * all the base classes. To avoid accidentally treating a class as
+ * a pointer to a base class other than the first one, we use an
+ * isClassname method. This function adds those methods as
+ * required.
+ *
+ * For convenience when using multiple inheritance, we also add
+ * functions to retrieve the base class pointers.
+ * ------------------------------------------------------------ */
+
+ void addFirstBaseInterface(Node *n, Hash *parents, List *bases) {
+ if (!bases || Len(bases) == 0) {
+ return;
+ }
+ Iterator b = First(bases);
+ if (!GetFlag(b.item, "feature:ignore")) {
+ String *go_name = buildGoName(Getattr(n, "sym:name"), false, false);
+ String *go_type_name = goCPointerType(Getattr(n, "classtypeobj"), true);
+ String *go_base_name = exportedName(Getattr(b.item, "sym:name"));
+ String *go_base_type = goType(n, Getattr(b.item, "classtypeobj"));
+ String *go_base_type_name = goCPointerType(Getattr(b.item, "classtypeobj"), true);
+
+ Printv(f_go_wrappers, "func (p ", go_type_name, ") SwigIs", go_base_name, "() {\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Printv(interfaces, "\tSwigIs", go_base_name, "()\n", NULL);
+
+ Printv(f_go_wrappers, "func (p ", go_type_name, ") SwigGet", go_base_name, "() ", go_base_type, " {\n", NULL);
+ Printv(f_go_wrappers, "\treturn ", go_base_type_name, "(getSwigcptr(p))\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Printv(interfaces, "\tSwigGet", go_base_name, "() ", go_base_type, "\n", NULL);
+
+ Setattr(parents, go_base_name, NewString(""));
+
+ Delete(go_name);
+ Delete(go_type_name);
+ Delete(go_base_type);
+ Delete(go_base_type_name);
+ }
+
+ addFirstBaseInterface(n, parents, Getattr(b.item, "bases"));
+ }
+
+ /* ------------------------------------------------------------
+ * addExtraBaseInterfaces()
+ *
+ * Add functions to retrieve the base class pointers for all base
+ * classes other than the first.
+ * ------------------------------------------------------------ */
+
+ int addExtraBaseInterfaces(Node *n, Hash *parents, List *bases) {
+ Iterator b = First(bases);
+
+ Node *fb = b.item;
+
+ for (b = Next(b); b.item; b = Next(b)) {
+ if (GetFlag(b.item, "feature:ignore")) {
+ continue;
+ }
+
+ String *go_base_name = exportedName(Getattr(b.item, "sym:name"));
+
+ Swig_save("addExtraBaseInterface", n, "wrap:action", "wrap:name", "wrap:parms", NULL);
+
+ SwigType *type = Copy(Getattr(n, "classtypeobj"));
+ SwigType_add_pointer(type);
+ Parm *parm = NewParm(type, "self", n);
+ Setattr(n, "wrap:parms", parm);
+
+ String *pn = Swig_cparm_name(parm, 0);
+ String *action = NewString("");
+ Printv(action, Swig_cresult_name(), " = (", Getattr(b.item, "classtype"), "*)", pn, ";", NULL);
+ Delete(pn);
+
+ Setattr(n, "wrap:action", action);
+
+ String *name = Copy(class_name);
+ Append(name, "_SwigGet");
+ Append(name, go_base_name);
+
+ String *go_name = NewString("SwigGet");
+ String *c1 = exportedName(go_base_name);
+ Append(go_name, c1);
+ Delete(c1);
+
+ String *wname = Swig_name_wrapper(name);
+ Append(wname, unique_id);
+ Setattr(n, "wrap:name", wname);
+
+ SwigType *result = Copy(Getattr(b.item, "classtypeobj"));
+ SwigType_add_pointer(result);
+
+ int r = makeWrappers(n, go_name, NULL, wname, NULL, parm, result, false);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ Swig_restore(n);
+
+ Setattr(parents, go_base_name, NewString(""));
+
+ Delete(go_name);
+ Delete(type);
+ Delete(parm);
+ Delete(action);
+ Delete(result);
+
+ String *ns = NewString("");
+ addParentExtraBaseInterfaces(n, parents, b.item, false, ns);
+ Delete(ns);
+ }
+
+ if (!GetFlag(fb, "feature:ignore")) {
+ String *ns = NewString("");
+ addParentExtraBaseInterfaces(n, parents, fb, true, ns);
+ Delete(ns);
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * addParentExtraBaseInterfaces()
+ *
+ * Add functions to retrieve the base class pointers for all base
+ * classes of parents other than the first base class at each level.
+ * ------------------------------------------------------------ */
+
+ void addParentExtraBaseInterfaces(Node *n, Hash *parents, Node *base, bool is_base_first, String *sofar) {
+ List *baselist = Getattr(base, "bases");
+ if (!baselist || Len(baselist) == 0) {
+ return;
+ }
+
+ String *go_this_base_name = exportedName(Getattr(base, "sym:name"));
+
+ String *sf = NewString("");
+ Printv(sf, sofar, ".SwigGet", go_this_base_name, "()", NULL);
+
+ Iterator b = First(baselist);
+
+ if (is_base_first) {
+ if (!b.item) {
+ return;
+ }
+ if (!GetFlag(b.item, "feature:ignore")) {
+ addParentExtraBaseInterfaces(n, parents, b.item, true, sf);
+ }
+
+ b = Next(b);
+ }
+
+ String *go_name = buildGoName(Getattr(n, "sym:name"), false, false);
+ String *go_type_name = goCPointerType(Getattr(n, "classtypeobj"), true);
+
+ for (; b.item; b = Next(b)) {
+ if (GetFlag(b.item, "feature:ignore")) {
+ continue;
+ }
+
+ String *go_base_name = exportedName(Getattr(b.item, "sym:name"));
+
+ if (!Getattr(parents, go_base_name)) {
+ Printv(f_go_wrappers, "func (p ", go_type_name, ") SwigGet", go_base_name, "() ", go_base_name, " {\n", NULL);
+ Printv(f_go_wrappers, "\treturn p", sf, ".SwigGet", go_base_name, "()\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Printv(interfaces, "\tSwigGet", go_base_name, "() ", go_base_name, "\n", NULL);
+
+ addParentExtraBaseInterfaces(n, parents, b.item, false, sf);
+
+ Setattr(parents, go_base_name, NewString(""));
+ }
+ }
+
+ Delete(go_name);
+ Delete(go_type_name);
+ Delete(go_this_base_name);
+ Delete(sf);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorInit
+ *
+ * Add support for a director class.
+ *
+ * Virtual inheritance is different in Go and C++. We implement
+ * director classes by defining a new function in Go,
+ * NewDirectorClassname, which takes a empty interface value and
+ * creates an instance of a new child class. The new child class
+ * refers all methods back to Go. The Go code checks whether the
+ * value passed to NewDirectorClassname implements that method; if
+ * it does, it calls it, otherwise it calls back into C++.
+ * ------------------------------------------------------------ */
+
+ int classDirectorInit(Node *n) {
+ // Because we use a different function to handle inheritance in
+ // Go, ordinary creations of the object should not create a
+ // director object.
+ Delete(director_ctor_code);
+ director_ctor_code = NewString("$nondirector_new");
+
+ class_node = n;
+
+ String *name = Getattr(n, "sym:name");
+
+ assert(!class_name);
+ class_name = name;
+
+ String *go_name = exportedName(name);
+
+ String *go_type_name = goCPointerType(Getattr(n, "classtypeobj"), true);
+
+ assert(!class_receiver);
+ class_receiver = go_type_name;
+
+ String *director_struct_name = NewString("_swig_Director");
+ Append(director_struct_name, go_name);
+
+ String *cxx_director_name = NewString("SwigDirector_");
+ Append(cxx_director_name, name);
+
+ // The Go type of the director class.
+ Printv(f_go_wrappers, "type ", director_struct_name, " struct {\n", NULL);
+ Printv(f_go_wrappers, "\t", go_type_name, "\n", NULL);
+ Printv(f_go_wrappers, "\tv interface{}\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Printv(f_go_wrappers, "func (p *", director_struct_name, ") Swigcptr() uintptr {\n", NULL);
+ Printv(f_go_wrappers, "\treturn getSwigcptr(p.", go_type_name, ")\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Printv(f_go_wrappers, "func (p *", director_struct_name, ") SwigIs", go_name, "() {\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Printv(f_go_wrappers, "func (p *", director_struct_name, ") DirectorInterface() interface{} {\n", NULL);
+ Printv(f_go_wrappers, "\treturn p.v\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ // Start defining the director class.
+ Printv(f_c_directors_h, "class ", cxx_director_name, " : public ", Getattr(n, "classtype"), "\n", NULL);
+ Printv(f_c_directors_h, "{\n", NULL);
+ Printv(f_c_directors_h, " public:\n", NULL);
+
+ Delete(director_struct_name);
+ Delete(cxx_director_name);
+
+ class_methods = NewHash();
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorConstructor
+ *
+ * Emit a constructor for a director class.
+ * ------------------------------------------------------------ */
+
+ int classDirectorConstructor(Node *n) {
+ bool is_ignored = GetFlag(n, "feature:ignore") ? true : false;
+
+ String *name = Getattr(n, "sym:name");
+ if (!name) {
+ assert(is_ignored);
+ name = Getattr(n, "name");
+ }
+
+ String *overname = NULL;
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ }
+
+ String *go_name = exportedName(name);
+
+ ParmList *parms = Getattr(n, "parms");
+ Setattr(n, "wrap:parms", parms);
+
+ String *cn = exportedName(Getattr(parentNode(n), "sym:name"));
+
+ String *go_type_name = goCPointerType(Getattr(parentNode(n), "classtypeobj"), true);
+
+ String *director_struct_name = NewString("_swig_Director");
+ Append(director_struct_name, cn);
+
+ String *fn_name = NewString("_swig_NewDirector");
+ Append(fn_name, cn);
+ Append(fn_name, go_name);
+
+ if (!overname && !is_ignored) {
+ if (!checkNameConflict(fn_name, n, NULL)) {
+ return SWIG_NOWRAP;
+ }
+ }
+
+ String *fn_with_over_name = Copy(fn_name);
+ if (overname) {
+ Append(fn_with_over_name, overname);
+ }
+
+ String *wname = Swig_name_wrapper(fn_name);
+
+ if (overname) {
+ Append(wname, overname);
+ }
+ Append(wname, unique_id);
+ Setattr(n, "wrap:name", wname);
+
+ bool is_static = isStatic(n);
+
+ Wrapper *dummy = NewWrapper();
+ emit_attach_parmmaps(parms, dummy);
+ DelWrapper(dummy);
+
+ Swig_typemap_attach_parms("gotype", parms, NULL);
+ Swig_typemap_attach_parms("goin", parms, NULL);
+ Swig_typemap_attach_parms("goargout", parms, NULL);
+ Swig_typemap_attach_parms("imtype", parms, NULL);
+ int parm_count = emit_num_arguments(parms);
+
+ String *func_name = NewString("NewDirector");
+ Append(func_name, go_name);
+
+ String *func_with_over_name = Copy(func_name);
+ if (overname) {
+ Append(func_with_over_name, overname);
+ }
+
+ SwigType *first_type = NewString("int");
+ Parm *first_parm = NewParm(first_type, "swig_p", n);
+ set_nextSibling(first_parm, parms);
+ Setattr(first_parm, "lname", "p");
+
+ Parm *p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ Swig_cparm_name(p, i);
+ p = nextParm(p);
+ }
+
+ if (!is_ignored) {
+ Printv(f_cgo_comment, "extern uintptr_t ", wname, "(int", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ bool c_struct_type;
+ String *ct = cgoTypeForGoValue(p, Getattr(p, "type"), &c_struct_type);
+ Printv(f_cgo_comment, ", ", ct, " ", Getattr(p, "lname"), NULL);
+ p = nextParm(p);
+ }
+ Printv(f_cgo_comment, ");\n", NULL);
+
+ // Write out the Go function that calls the wrapper.
+
+ Printv(f_go_wrappers, "func ", func_with_over_name, "(v interface{}", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ Printv(f_go_wrappers, ", ", Getattr(p, "lname"), " ", NULL);
+ String *tm = goType(p, Getattr(p, "type"));
+ Printv(f_go_wrappers, tm, NULL);
+ Delete(tm);
+ p = nextParm(p);
+ }
+
+ Printv(f_go_wrappers, ") ", cn, " {\n", NULL);
+
+ Printv(f_go_wrappers, "\tp := &", director_struct_name, "{0, v}\n", NULL);
+
+ String *call = NewString("");
+
+ Printv(call, "\tp.", class_receiver, " = ", NULL);
+ Printv(call, go_type_name, "(C.", wname, "(C.int(swigDirectorAdd(p))", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ Printv(call, ", ", NULL);
+
+ p = getParm(p);
+ String *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+
+ String *ivar = NewStringf("_swig_i_%d", i);
+
+ String *goin = goGetattr(p, "tmap:goin");
+ if (goin == NULL) {
+ Printv(f_go_wrappers, "\t", ivar, " := ", NULL);
+ bool need_close = false;
+ if (goTypeIsInterface(p, pt)) {
+ Printv(f_go_wrappers, "getSwigcptr(", NULL);
+ need_close = true;
+ }
+ Printv(f_go_wrappers, ln, NULL);
+ if (need_close) {
+ Printv(f_go_wrappers, ")", NULL);
+ }
+ Printv(f_go_wrappers, "\n", NULL);
+ } else {
+ String *itm = goImType(p, pt);
+ Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
+ goin = Copy(goin);
+ Replaceall(goin, "$input", ln);
+ Replaceall(goin, "$result", ivar);
+ Printv(f_go_wrappers, goin, "\n", NULL);
+ Delete(goin);
+ }
+
+ Setattr(p, "emit:goinput", ivar);
+
+ bool c_struct_type;
+ String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
+ if (c_struct_type) {
+ Printv(call, "*(*C.", ct, ")(unsafe.Pointer(&", ivar, "))", NULL);
+ } else {
+ Printv(call, "C.", ct, "(", ivar, ")", NULL);
+ }
+ Delete(ct);
+
+ p = nextParm(p);
+ }
+
+ Printv(call, "))", NULL);
+
+ Printv(f_go_wrappers, call, "\n", NULL);
+
+ goargout(parms);
+
+ Printv(f_go_wrappers, "\treturn p\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ SwigType *result = Copy(Getattr(parentNode(n), "classtypeobj"));
+ SwigType_add_pointer(result);
+
+ Swig_save("classDirectorConstructor", n, "wrap:name", "wrap:action", NULL);
+
+ String *dwname = Swig_name_wrapper(name);
+ Append(dwname, unique_id);
+ Setattr(n, "wrap:name", dwname);
+
+ String *action = NewString("");
+ Printv(action, Swig_cresult_name(), " = new SwigDirector_", class_name, "(", NULL);
+ String *pname = Swig_cparm_name(NULL, 0);
+ Printv(action, pname, NULL);
+ Delete(pname);
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ String *pname = Swig_cparm_name(NULL, i + 1);
+ Printv(action, ", ", NULL);
+ if (SwigType_isreference(Getattr(p, "type"))) {
+ Printv(action, "*", NULL);
+ }
+ Printv(action, pname, NULL);
+ Delete(pname);
+ p = nextParm(p);
+ }
+ Printv(action, ");", NULL);
+ Setattr(n, "wrap:action", action);
+
+ cgoWrapperInfo info;
+
+ info.n = n;
+ info.go_name = func_name;
+ info.overname = overname;
+ info.wname = wname;
+ info.base = NULL;
+ info.parms = first_parm;
+ info.result = result;
+ info.is_static = false;
+ info.receiver = NULL;
+ info.is_constructor = true;
+ info.is_destructor = false;
+
+ int r = cgoGccWrapper(&info);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ Swig_restore(n);
+
+ Delete(result);
+ }
+
+ String *cxx_director_name = NewString("SwigDirector_");
+ Append(cxx_director_name, class_name);
+
+ String *decl = Swig_method_decl(NULL, Getattr(n, "decl"), cxx_director_name, first_parm, 0);
+ Printv(f_c_directors_h, " ", decl, ";\n", NULL);
+ Delete(decl);
+
+ decl = Swig_method_decl(NULL, Getattr(n, "decl"), cxx_director_name, first_parm, 0);
+ Printv(f_c_directors, cxx_director_name, "::", decl, "\n", NULL);
+ Delete(decl);
+
+ Printv(f_c_directors, " : ", Getattr(parentNode(n), "classtype"), "(", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ if (i > 0) {
+ Printv(f_c_directors, ", ", NULL);
+ }
+ String *pn = Getattr(p, "name");
+ assert(pn);
+ Printv(f_c_directors, pn, NULL);
+ p = nextParm(p);
+ }
+ Printv(f_c_directors, "),\n", NULL);
+ Printv(f_c_directors, " go_val(swig_p), swig_mem(0)\n", NULL);
+ Printv(f_c_directors, "{ }\n\n", NULL);
+
+ if (Getattr(n, "sym:overloaded") && !Getattr(n, "sym:nextSibling")) {
+ int r = makeDispatchFunction(n, func_name, cn, is_static, Getattr(parentNode(n), "classtypeobj"), false);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ }
+
+ Delete(cxx_director_name);
+ Delete(go_name);
+ Delete(cn);
+ Delete(go_type_name);
+ Delete(director_struct_name);
+ Delete(fn_name);
+ Delete(fn_with_over_name);
+ Delete(func_name);
+ Delete(func_with_over_name);
+ Delete(wname);
+ Delete(first_type);
+ Delete(first_parm);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorDestructor
+ *
+ * Emit a destructor for a director class.
+ * ------------------------------------------------------------ */
+
+ int classDirectorDestructor(Node *n) {
+ if (!is_public(n)) {
+ return SWIG_OK;
+ }
+
+ bool is_ignored = GetFlag(n, "feature:ignore") ? true : false;
+
+ if (!is_ignored) {
+ String *fnname = NewString("DeleteDirector");
+ String *c1 = exportedName(class_name);
+ Append(fnname, c1);
+ Delete(c1);
+
+ String *wname = Swig_name_wrapper(fnname);
+ Append(wname, unique_id);
+
+ Setattr(n, "wrap:name", fnname);
+
+ Swig_DestructorToFunction(n, getNSpace(), getClassType(), CPlusPlus, Extend);
+
+ ParmList *parms = Getattr(n, "parms");
+ Setattr(n, "wrap:parms", parms);
+
+ String *result = NewString("void");
+ int r = makeWrappers(n, fnname, NULL, wname, NULL, parms, result, isStatic(n));
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ Delete(result);
+ Delete(fnname);
+ Delete(wname);
+ }
+
+ // Generate the destructor for the C++ director class. Since the
+ // Go code is keeping a pointer to the C++ object, we need to call
+ // back to the Go code to let it know that the C++ object is gone.
+
+ String *go_name = NewString("Swiggo_DeleteDirector_");
+ Append(go_name, class_name);
+
+ String *cn = exportedName(class_name);
+
+ String *director_struct_name = NewString("_swig_Director");
+ Append(director_struct_name, cn);
+
+ Printv(f_c_directors_h, " virtual ~SwigDirector_", class_name, "()", NULL);
+
+ String *throws = buildThrow(n);
+ if (throws) {
+ Printv(f_c_directors_h, " ", throws, NULL);
+ }
+
+ Printv(f_c_directors_h, ";\n", NULL);
+
+ String *director_sig = NewString("");
+
+ Printv(director_sig, "SwigDirector_", class_name, "::~SwigDirector_", class_name, "()", NULL);
+
+ if (throws) {
+ Printv(director_sig, " ", throws, NULL);
+ Delete(throws);
+ }
+
+ Printv(director_sig, "\n", NULL);
+ Printv(director_sig, "{\n", NULL);
+
+ if (is_ignored) {
+ Printv(f_c_directors, director_sig, NULL);
+ } else {
+ makeDirectorDestructorWrapper(go_name, director_struct_name, director_sig);
+ }
+
+ Printv(f_c_directors, " delete swig_mem;\n", NULL);
+
+ Printv(f_c_directors, "}\n\n", NULL);
+
+ Delete(director_sig);
+ Delete(go_name);
+ Delete(cn);
+ Delete(director_struct_name);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * makeDirectorDestructorWrapper
+ *
+ * Emit the function wrapper for the destructor of a director class.
+ * ------------------------------------------------------------ */
+
+ void makeDirectorDestructorWrapper(String *go_name, String *director_struct_name, String *director_sig) {
+ String *wname = Copy(go_name);
+ Append(wname, unique_id);
+
+ Printv(f_go_wrappers, "//export ", wname, "\n", NULL);
+ Printv(f_go_wrappers, "func ", wname, "(c int) {\n", NULL);
+ Printv(f_go_wrappers, "\tswigDirectorLookup(c).(*", director_struct_name, ").", class_receiver, " = 0\n", NULL);
+ Printv(f_go_wrappers, "\tswigDirectorDelete(c)\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Printv(f_c_directors, "extern \"C\" void ", wname, "(intgo);\n", NULL);
+ Printv(f_c_directors, director_sig, NULL);
+ Printv(f_c_directors, " ", wname, "(go_val);\n", NULL);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorMethod
+ *
+ * Emit a method for a director class, plus its overloads.
+ * ------------------------------------------------------------ */
+
+ int classDirectorMethod(Node *n, Node *parent, String *super) {
+ bool is_ignored = GetFlag(n, "feature:ignore") ? true : false;
+
+ // We don't need explicit calls.
+ if (GetFlag(n, "explicitcall")) {
+ return SWIG_OK;
+ }
+
+ String *name = Getattr(n, "sym:name");
+ if (!name) {
+ assert(is_ignored);
+ (void)is_ignored;
+ name = Getattr(n, "name");
+ }
+
+ bool overloaded = Getattr(n, "sym:overloaded") && !Getattr(n, "explicitcallnode");
+ if (!overloaded) {
+ int r = oneClassDirectorMethod(n, parent, super);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ } else {
+ // Handle overloaded methods here, because otherwise we will
+ // reject them in the class_methods hash table. We need to use
+ // class_methods so that we correctly handle cases where a
+ // function in one class hides a function of the same name in a
+ // parent class.
+ if (!Getattr(class_methods, name)) {
+ for (Node *on = Getattr(n, "sym:overloaded"); on; on = Getattr(on, "sym:nextSibling")) {
+ // Swig_overload_rank expects wrap:name and wrap:parms to be
+ // set.
+ String *wn = Swig_name_wrapper(Getattr(on, "sym:name"));
+ Append(wn, Getattr(on, "sym:overname"));
+ Append(wn, unique_id);
+ Setattr(on, "wrap:name", wn);
+ Delete(wn);
+ Setattr(on, "wrap:parms", Getattr(on, "parms"));
+ }
+ }
+
+ int r = oneClassDirectorMethod(n, parent, super);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ if (!Getattr(n, "sym:nextSibling"))
+ {
+ // Last overloaded function
+ Node *on = Getattr(n, "sym:overloaded");
+ bool is_static = isStatic(on);
+
+ String *cn = exportedName(Getattr(parent, "sym:name"));
+ String *go_name = buildGoName(name, is_static, false);
+
+ String *director_struct_name = NewString("_swig_Director");
+ Append(director_struct_name, cn);
+
+ int r = makeDispatchFunction(on, go_name, director_struct_name, is_static, director_struct_name, false);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ if (!GetFlag(n, "abstract")) {
+ String *go_upcall = NewString("Director");
+ Append(go_upcall, cn);
+ Append(go_upcall, go_name);
+ r = makeDispatchFunction(on, go_upcall, director_struct_name, is_static, director_struct_name, true);
+ if (r != SWIG_OK) {
+ return r;
+ }
+ Delete(go_upcall);
+ }
+
+ Delete(director_struct_name);
+ Delete(go_name);
+ Delete(cn);
+ }
+ }
+ Setattr(class_methods, name, NewString(""));
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * oneClassDirectorMethod
+ *
+ * Emit a method for a director class.
+ * ------------------------------------------------------------ */
+
+ int oneClassDirectorMethod(Node *n, Node *parent, String *super) {
+ String *symname = Getattr(n, "sym:name");
+ if (!checkFunctionVisibility(n, parent)) {
+ return SWIG_OK;
+ }
+
+ bool is_ignored = GetFlag(n, "feature:ignore") ? true : false;
+ bool is_pure_virtual = (Cmp(Getattr(n, "storage"), "virtual") == 0 && Cmp(Getattr(n, "value"), "0") == 0);
+
+ String *name = Getattr(n, "sym:name");
+ if (!name) {
+ assert(is_ignored);
+ name = Getattr(n, "name");
+ }
+
+ String *overname = NULL;
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ }
+
+ String *cn = exportedName(Getattr(parent, "sym:name"));
+
+ String *go_type_name = goCPointerType(Getattr(parent, "classtypeobj"), true);
+
+ String *director_struct_name = NewString("_swig_Director");
+ Append(director_struct_name, cn);
+
+ bool is_static = isStatic(n);
+
+ String *go_name = buildGoName(name, is_static, false);
+
+ ParmList *parms = Getattr(n, "parms");
+ Setattr(n, "wrap:parms", parms);
+
+ Wrapper *dummy = NewWrapper();
+ emit_attach_parmmaps(parms, dummy);
+
+ Swig_typemap_attach_parms("gotype", parms, NULL);
+ Swig_typemap_attach_parms("imtype", parms, NULL);
+ int parm_count = emit_num_arguments(parms);
+
+ SwigType *result = Getattr(n, "type");
+
+ // Save the type for overload processing.
+ Setattr(n, "go:type", result);
+
+ String *interface_name = NewString("_swig_DirectorInterface");
+ Append(interface_name, cn);
+ Append(interface_name, go_name);
+ if (overname) {
+ Append(interface_name, overname);
+ }
+
+ String *callback_name = Copy(director_struct_name);
+ Append(callback_name, "_callback_");
+ Append(callback_name, name);
+ Replace(callback_name, "_swig", "Swig", DOH_REPLACE_FIRST);
+ if (overname) {
+ Append(callback_name, overname);
+ }
+ Append(callback_name, unique_id);
+
+ String *upcall_name = Copy(director_struct_name);
+ Append(upcall_name, "_upcall_");
+ Append(upcall_name, go_name);
+
+ String *upcall_wname = Swig_name_wrapper(upcall_name);
+ if (overname) {
+ Append(upcall_wname, overname);
+ }
+ Append(upcall_wname, unique_id);
+
+ String *upcall_gc_name = buildGoWrapperName(upcall_name, overname);
+
+ String *go_with_over_name = Copy(go_name);
+ if (overname) {
+ Append(go_with_over_name, overname);
+ }
+
+ Parm *p = 0;
+ Wrapper *w = NewWrapper();
+
+ Swig_director_parms_fixup(parms);
+
+ Swig_typemap_attach_parms("directorin", parms, w);
+ Swig_typemap_attach_parms("directorargout", parms, w);
+ Swig_typemap_attach_parms("godirectorin", parms, w);
+ Swig_typemap_attach_parms("goin", parms, dummy);
+ Swig_typemap_attach_parms("goargout", parms, dummy);
+
+ DelWrapper(dummy);
+
+ if (!is_ignored) {
+ // We use an interface to see if this method is defined in Go.
+ Printv(f_go_wrappers, "type ", interface_name, " interface {\n", NULL);
+ Printv(f_go_wrappers, "\t", go_with_over_name, "(", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ if (i > 0) {
+ Printv(f_go_wrappers, ", ", NULL);
+ }
+ String *tm = goType(p, Getattr(p, "type"));
+ Printv(f_go_wrappers, tm, NULL);
+ Delete(tm);
+ p = nextParm(p);
+ }
+
+ Printv(f_go_wrappers, ")", NULL);
+
+ if (SwigType_type(result) != T_VOID) {
+ String *tm = goType(n, result);
+ Printv(f_go_wrappers, " ", tm, NULL);
+ Delete(tm);
+ }
+
+ Printv(f_go_wrappers, "\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ if (!GetFlag(n, "abstract")) {
+ Printv(f_cgo_comment, "extern ", NULL);
+
+ if (SwigType_type(result) == T_VOID) {
+ Printv(f_cgo_comment, "void", NULL);
+ } else {
+ bool c_struct_type;
+ String *ret_type = cgoTypeForGoValue(n, result, &c_struct_type);
+ Printv(f_cgo_comment, ret_type, NULL);
+ Delete(ret_type);
+ }
+
+ Printv(f_cgo_comment, " ", upcall_wname, "(uintptr_t", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ bool c_struct_type;
+ String *ct = cgoTypeForGoValue(p, Getattr(p, "type"), &c_struct_type);
+ Printv(f_cgo_comment, ", ", ct, " ", Getattr(p, "lname"), NULL);
+ p = nextParm(p);
+ }
+ Printv(f_cgo_comment, ");\n", NULL);
+ }
+
+ // Define the method on the director class in Go.
+
+ Printv(f_go_wrappers, "func (swig_p *", director_struct_name, ") ", go_with_over_name, "(", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ if (i > 0) {
+ Printv(f_go_wrappers, ", ", NULL);
+ }
+ Printv(f_go_wrappers, Getattr(p, "lname"), " ", NULL);
+ String *tm = goType(p, Getattr(p, "type"));
+ Printv(f_go_wrappers, tm, NULL);
+ Delete(tm);
+ p = nextParm(p);
+ }
+
+ Printv(f_go_wrappers, ")", NULL);
+
+ if (SwigType_type(result) != T_VOID) {
+ String *tm = goType(n, result);
+ Printv(f_go_wrappers, " ", tm, NULL);
+ Delete(tm);
+ }
+
+ Printv(f_go_wrappers, " {\n", NULL);
+
+ Printv(f_go_wrappers, "\tif swig_g, swig_ok := swig_p.v.(", interface_name, "); swig_ok {\n", NULL);
+ Printv(f_go_wrappers, "\t\t", NULL);
+ if (SwigType_type(result) != T_VOID) {
+ Printv(f_go_wrappers, "return ", NULL);
+ }
+ Printv(f_go_wrappers, "swig_g.", go_with_over_name, "(", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ if (i > 0) {
+ Printv(f_go_wrappers, ", ", NULL);
+ }
+ Printv(f_go_wrappers, Getattr(p, "lname"), NULL);
+ p = nextParm(p);
+ }
+
+ Printv(f_go_wrappers, ")\n", NULL);
+ if (SwigType_type(result) == T_VOID) {
+ Printv(f_go_wrappers, "\t\treturn\n", NULL);
+ }
+ Printv(f_go_wrappers, "\t}\n", NULL);
+
+ if (GetFlag(n, "abstract")) {
+ Printv(f_go_wrappers, "\tpanic(\"call to pure virtual method\")\n", NULL);
+ } else {
+ String *ret_type = NULL;
+ bool memcpy_ret = false;
+ String *wt = NULL;
+ String *goout = NULL;
+ if (SwigType_type(result) != T_VOID) {
+ ret_type = goImType(n, result);
+ Printv(f_go_wrappers, "\tvar swig_r ", ret_type, "\n", NULL);
+ goout = goTypemapLookup("goout", n, "swig_r");
+
+ bool c_struct_type;
+ Delete(cgoTypeForGoValue(n, result, &c_struct_type));
+ if (c_struct_type) {
+ memcpy_ret = true;
+ }
+ }
+
+ String *call = NewString("");
+
+ Printv(call, "\t", NULL);
+ if (SwigType_type(result) != T_VOID) {
+ if (memcpy_ret) {
+ Printv(call, "swig_r_p := ", NULL);
+ } else {
+ Printv(call, "swig_r = (", ret_type, ")(", NULL);
+ }
+ if (goTypeIsInterface(n, result)) {
+ wt = goWrapperType(n, result, true);
+ Printv(call, "(", wt, ")(", NULL);
+ }
+ }
+
+ Printv(call, "C.", upcall_wname, "(C.uintptr_t(swig_p.",
+ go_type_name, ")", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ Printv(call, ", ", NULL);
+ p = getParm(p);
+ SwigType *pt = Getattr(p, "type");
+
+ String *ln = Getattr(p, "lname");
+
+ String *ivar = NewStringf("_swig_i_%d", i);
+
+ // This is an ordinary call from Go to C++, so adjust using
+ // the goin typemap.
+ String *goin = goGetattr(p, "tmap:goin");
+ if (goin == NULL) {
+ Printv(f_go_wrappers, "\t", ivar, " := ", NULL);
+ bool need_close = false;
+ if (goTypeIsInterface(p, pt)) {
+ Printv(f_go_wrappers, "getSwigcptr(", NULL);
+ need_close = true;
+ }
+ Printv(f_go_wrappers, ln, NULL);
+ if (need_close) {
+ Printv(f_go_wrappers, ")", NULL);
+ }
+ Printv(f_go_wrappers, "\n", NULL);
+ } else {
+ String *itm = goImType(p, pt);
+ Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
+ goin = Copy(goin);
+ Replaceall(goin, "$input", ln);
+ Replaceall(goin, "$result", ivar);
+ Printv(f_go_wrappers, goin, "\n", NULL);
+ Delete(goin);
+ }
+
+ Setattr(p, "emit:goinput", ivar);
+
+ bool c_struct_type;
+ String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
+ if (c_struct_type) {
+ Printv(call, "*(*C.", ct, ")(unsafe.Pointer(&", ivar, "))", NULL);
+ } else {
+ Printv(call, "C.", ct, "(", ivar, ")", NULL);
+ }
+
+ p = nextParm(p);
+ }
+
+ Printv(call, ")", NULL);
+
+ if (wt) {
+ // Close the type conversion to the wrapper type.
+ Printv(call, ")", NULL);
+ }
+ if (SwigType_type(result) != T_VOID && !memcpy_ret) {
+ // Close the type conversion of the return value.
+ Printv(call, ")", NULL);
+ }
+
+ Printv(call, "\n", NULL);
+
+ Printv(f_go_wrappers, call, NULL);
+ Delete(call);
+
+ if (memcpy_ret) {
+ Printv(f_go_wrappers, "\tswig_r = *(*", ret_type, ")(unsafe.Pointer(&swig_r_p))\n", NULL);
+ }
+
+ goargout(parms);
+
+ if (SwigType_type(result) != T_VOID) {
+ if (goout == NULL) {
+ Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
+ } else {
+ String *tm = goType(n, result);
+ Printv(f_go_wrappers, "\tvar swig_r_1 ", tm, "\n", NULL);
+ Replaceall(goout, "$input", "swig_r");
+ Replaceall(goout, "$result", "swig_r_1");
+ Printv(f_go_wrappers, goout, "\n", NULL);
+ Printv(f_go_wrappers, "\treturn swig_r_1\n", NULL);
+ }
+ }
+
+ if (ret_type) {
+ Delete(ret_type);
+ }
+ if (wt) {
+ Delete(wt);
+ }
+ }
+
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ if (!GetFlag(n, "abstract")) {
+ // Define a function that uses the Go director type that other
+ // methods in the Go type can call to get parent methods.
+
+ Printv(f_go_wrappers, "func Director", cn, go_with_over_name, "(swig_p ", cn, NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ Printv(f_go_wrappers, ", ", Getattr(p, "lname"), " ", NULL);
+ String *tm = goType(p, Getattr(p, "type"));
+ Printv(f_go_wrappers, tm, NULL);
+ Delete(tm);
+ p = nextParm(p);
+ }
+
+ Printv(f_go_wrappers, ")", NULL);
+
+ if (SwigType_type(result) != T_VOID) {
+ String *tm = goType(n, result);
+ Printv(f_go_wrappers, " ", tm, NULL);
+ Delete(tm);
+ }
+
+ Printv(f_go_wrappers, " {\n", NULL);
+
+ String *ret_type = NULL;
+ bool memcpy_ret = false;
+ String *wt = NULL;
+ String *goout = NULL;
+ if (SwigType_type(result) != T_VOID) {
+ ret_type = goImType(n, result);
+ Printv(f_go_wrappers, "\tvar swig_r ", ret_type, "\n", NULL);
+ goout = goTypemapLookup("goout", n, "swig_r");
+
+ bool c_struct_type;
+ Delete(cgoTypeForGoValue(n, result, &c_struct_type));
+ if (c_struct_type) {
+ memcpy_ret = true;
+ }
+ }
+
+ String *call = NewString("");
+
+ Printv(call, "\t", NULL);
+ if (SwigType_type(result) != T_VOID) {
+ if (memcpy_ret) {
+ Printv(call, "swig_r_p := ", NULL);
+ } else {
+ Printv(call, "swig_r = (", ret_type, ")(", NULL);
+ }
+ if (goTypeIsInterface(n, result)) {
+ wt = goWrapperType(n, result, true);
+ Printv(call, "(", wt, ")(", NULL);
+ }
+ }
+
+ Printv(call, "C.", upcall_wname, "(C.uintptr_t(swig_p.(*",
+ director_struct_name, ").", go_type_name, ")", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ Printv(call, ", ", NULL);
+ p = getParm(p);
+ SwigType *pt = Getattr(p, "type");
+
+ String *ivar = NewStringf("_swig_i_%d", i);
+
+ String *ln = Copy(Getattr(p, "lname"));
+
+ String *goin = goGetattr(p, "tmap:goin");
+ if (goin == NULL) {
+ Printv(f_go_wrappers, "\t", ivar, " := ", NULL);
+ bool need_close = false;
+ if (goTypeIsInterface(p, pt)) {
+ Printv(f_go_wrappers, "getSwigcptr(", NULL);
+ need_close = true;
+ }
+ Printv(f_go_wrappers, ln, NULL);
+ if (need_close) {
+ Printv(f_go_wrappers, ")", NULL);
+ }
+ Printv(f_go_wrappers, "\n", NULL);
+ } else {
+ String *itm = goImType(p, pt);
+ Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
+ goin = Copy(goin);
+ Replaceall(goin, "$input", ln);
+ Replaceall(goin, "$result", ivar);
+ Printv(f_go_wrappers, goin, "\n", NULL);
+ Delete(goin);
+ }
+
+ Setattr(p, "emit:goinput", ivar);
+
+ bool c_struct_type;
+ String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
+ if (c_struct_type) {
+ Printv(call, "*(*C.", ct, ")(unsafe.Pointer(&", ivar, "))", NULL);
+ } else {
+ Printv(call, "C.", ct, "(", ivar, ")", NULL);
+ }
+
+ Delete(ln);
+
+ p = nextParm(p);
+ }
+
+ Printv(call, ")", NULL);
+
+ if (wt) {
+ // Close the type conversion to the wrapper type.
+ Printv(call, ")", NULL);
+ }
+ if (SwigType_type(result) != T_VOID && !memcpy_ret) {
+ // Close the type conversion of the return value.
+ Printv(call, ")", NULL);
+ }
+
+ Printv(call, "\n", NULL);
+
+ Printv(f_go_wrappers, call, NULL);
+ Delete(call);
+
+ if (memcpy_ret) {
+ Printv(f_go_wrappers, "\tswig_r = *(*", ret_type, ")(unsafe.Pointer(&swig_r_p))\n", NULL);
+ }
+
+ goargout(parms);
+
+ if (SwigType_type(result) != T_VOID) {
+ if (goout == NULL) {
+ Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
+ } else {
+ String *tm = goType(n, result);
+ Printv(f_go_wrappers, "\tvar swig_r_1 ", tm, "\n", NULL);
+ Replaceall(goout, "$input", "swig_r");
+ Replaceall(goout, "$result", "swig_r_1");
+ Printv(f_go_wrappers, goout, "\n", NULL);
+ Printv(f_go_wrappers, "\treturn swig_r_1\n", NULL);
+ }
+ }
+
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ if (ret_type) {
+ Delete(ret_type);
+ }
+ if (wt) {
+ Delete(wt);
+ }
+
+ // Define a method in the C++ director class that the C++
+ // upcall function can call. This permits an upcall to a
+ // protected method.
+
+ String *upcall_method_name = NewString("_swig_upcall_");
+ Append(upcall_method_name, name);
+ if (overname) {
+ Append(upcall_method_name, overname);
+ }
+ SwigType *rtype = Getattr(n, "classDirectorMethods:type");
+ String *upcall_decl = Swig_method_decl(rtype, Getattr(n, "decl"), upcall_method_name, parms, 0);
+ Printv(f_c_directors_h, " ", upcall_decl, " {\n", NULL);
+ Delete(upcall_decl);
+
+ Printv(f_c_directors_h, " ", NULL);
+ if (SwigType_type(result) != T_VOID) {
+ Printv(f_c_directors_h, "return ", NULL);
+ }
+
+ String *super_call = Swig_method_call(super, parms);
+ Printv(f_c_directors_h, super_call, ";\n", NULL);
+ Delete(super_call);
+
+ Printv(f_c_directors_h, " }\n", NULL);
+
+ // Define the C++ function that the Go function calls.
+
+ SwigType *first_type = NULL;
+ Parm *first_parm = parms;
+ if (!is_static) {
+ first_type = NewString("SwigDirector_");
+ Append(first_type, class_name);
+ SwigType_add_pointer(first_type);
+ first_parm = NewParm(first_type, "p", n);
+ set_nextSibling(first_parm, parms);
+ }
+
+ Swig_save("classDirectorMethod", n, "wrap:name", "wrap:action", NULL);
+
+ Setattr(n, "wrap:name", upcall_wname);
+
+ String *action = NewString("");
+ if (SwigType_type(result) != T_VOID) {
+ Printv(action, Swig_cresult_name(), " = (", SwigType_lstr(result, 0), ")", NULL);
+ if (SwigType_isreference(result)) {
+ Printv(action, "&", NULL);
+ }
+ }
+ Printv(action, Swig_cparm_name(NULL, 0), "->", upcall_method_name, "(", NULL);
+
+ p = parms;
+ int i = 0;
+ while (p != NULL) {
+ if (SwigType_type(Getattr(p, "type")) != T_VOID) {
+ String *pname = Swig_cparm_name(NULL, i + 1);
+ if (i > 0) {
+ Printv(action, ", ", NULL);
+ }
+
+ // A parameter whose type is a reference is converted into a
+ // pointer type by gcCTypeForGoValue. We are calling a
+ // function which expects a reference so we need to convert
+ // back.
+ if (SwigType_isreference(Getattr(p, "type"))) {
+ Printv(action, "*", NULL);
+ }
+
+ Printv(action, pname, NULL);
+ Delete(pname);
+ i++;
+ }
+ p = nextSibling(p);
+ }
+ Printv(action, ");", NULL);
+ Setattr(n, "wrap:action", action);
+
+ cgoWrapperInfo info;
+
+ info.n = n;
+ info.go_name = go_name;
+ info.overname = overname;
+ info.wname = upcall_wname;
+ info.base = NULL;
+ info.parms = first_parm;
+ info.result = result;
+ info.is_static = is_static;
+ info.receiver = NULL;
+ info.is_constructor = false;
+ info.is_destructor = false;
+
+ int r = cgoGccWrapper(&info);
+ if (r != SWIG_OK) {
+ return r;
+ }
+
+ Delete(first_type);
+ if (first_parm != parms) {
+ Delete(first_parm);
+ }
+
+ Swig_restore(n);
+ Delete(upcall_method_name);
+ }
+
+ // The Go function which invokes the method. This is called by
+ // the C++ method on the director class.
+
+ Printv(f_go_wrappers, "//export ", callback_name, "\n",
+ "func ", callback_name, "(swig_c int", NULL);
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ String *tm = goWrapperType(p, Getattr(p, "type"), false);
+ Printv(f_go_wrappers, ", ", Getattr(p, "lname"), " ", tm, NULL);
+ Delete(tm);
+ p = nextParm(p);
+ }
+
+ Printv(f_go_wrappers, ") ", NULL);
+ String *result_wrapper = NULL;
+ if (SwigType_type(result) != T_VOID) {
+ result_wrapper = goWrapperType(n, result, true);
+ Printv(f_go_wrappers, "(swig_result ", result_wrapper, ") ", NULL);
+ }
+ Printv(f_go_wrappers, "{\n", NULL);
+
+ if (is_ignored) {
+ Printv(f_go_wrappers, "\treturn\n", NULL);
+ } else {
+ bool result_is_interface = false;
+ String *goout = NULL;
+ if (SwigType_type(result) != T_VOID) {
+ result_is_interface = goTypeIsInterface(NULL, result);
+ Printv(f_go_wrappers, "\tvar swig_r ", NULL);
+ if (!result_is_interface) {
+ Printv(f_go_wrappers, goType(n, result), NULL);
+ } else {
+ Printv(f_go_wrappers, result_wrapper, NULL);
+ }
+ Printv(f_go_wrappers, "\n", NULL);
+ goout = goTypemapLookup("godirectorout", n, "swig_r");
+ }
+
+ String *call = NewString("");
+ Printv(call, "\t", NULL);
+
+ if (SwigType_type(result) != T_VOID) {
+ Printv(call, "swig_r = ", NULL);
+ if (result_is_interface) {
+ Printv(call, result_wrapper, "(getSwigcptr(", NULL);
+ }
+ }
+ Printv(call, "swig_p.", go_with_over_name, "(", NULL);
+
+ String *goincode = NewString("");
+
+ p = parms;
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ if (i > 0) {
+ Printv(call, ", ", NULL);
+ }
+ SwigType *pt = Getattr(p, "type");
+
+ String *ln = NewString("");
+
+ // If the Go representation is an interface type class, then
+ // we are receiving a uintptr, and must convert to the
+ // interface.
+ bool is_interface = goTypeIsInterface(p, pt);
+ if (is_interface) {
+ // Passing is_result as true to goWrapperType gives us the
+ // name of the Go type we need to convert to an interface.
+ String *wt = goWrapperType(p, pt, true);
+ Printv(ln, wt, "(", NULL);
+ Delete(wt);
+ }
+
+ Printv(ln, Getattr(p, "lname"), NULL);
+
+ if (is_interface) {
+ Printv(ln, ")", NULL);
+ }
+
+ String *goin = goGetattr(p, "tmap:godirectorin");
+ if (goin == NULL) {
+ Printv(call, ln, NULL);
+ } else {
+ String *ivar = NewString("");
+ Printf(ivar, "_swig_i_%d", i);
+ String *itm = goType(p, pt);
+ Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
+ goin = Copy(goin);
+ Replaceall(goin, "$input", ln);
+ Replaceall(goin, "$result", ivar);
+ Printv(goincode, goin, "\n", NULL);
+ Delete(goin);
+ Printv(call, ivar, NULL);
+ Delete(ivar);
+ }
+
+ Delete(ln);
+
+ p = nextParm(p);
+ }
+
+ Printv(call, ")", NULL);
+
+ if (result_is_interface) {
+ Printv(call, "))", NULL);
+ }
+ Printv(call, "\n", NULL);
+
+ Printv(f_go_wrappers, "\tswig_p := swigDirectorLookup(swig_c).(*", director_struct_name, ")\n", NULL);
+ Printv(f_go_wrappers, goincode, NULL);
+ Printv(f_go_wrappers, call, NULL);
+ Delete(call);
+
+ if (SwigType_type(result) != T_VOID) {
+ if (goout == NULL) {
+ Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
+ } else {
+ String *tm = goImType(n, result);
+ Printv(f_go_wrappers, "\tvar swig_r_1 ", tm, "\n", NULL);
+ Replaceall(goout, "$input", "swig_r");
+ Replaceall(goout, "$result", "swig_r_1");
+ Printv(f_go_wrappers, goout, "\n", NULL);
+ Printv(f_go_wrappers, "\treturn swig_r_1\n", NULL);
+ }
+ }
+ }
+
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Delete(result_wrapper);
+
+ Delete(upcall_wname);
+ Delete(upcall_gc_name);
+ Delete(go_with_over_name);
+ }
+
+ if (!is_ignored || is_pure_virtual) {
+ // Declare the method for the director class.
+
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ String *decl = Swig_method_decl(rtype, Getattr(n, "decl"), Getattr(n, "name"), parms, 0);
+ Printv(f_c_directors_h, " virtual ", decl, NULL);
+ Delete(decl);
+
+ String *qname = NewString("");
+ Printv(qname, "SwigDirector_", class_name, "::", Getattr(n, "name"), NULL);
+ decl = Swig_method_decl(rtype, Getattr(n, "decl"), qname, parms, 0);
+ Printv(w->def, decl, NULL);
+ Delete(decl);
+ Delete(qname);
+
+ String *throws = buildThrow(n);
+ if (throws) {
+ Printv(f_c_directors_h, " ", throws, NULL);
+ Printv(w->def, " ", throws, NULL);
+ Delete(throws);
+ }
+
+ Printv(f_c_directors_h, ";\n", NULL);
+
+ Printv(w->def, " {\n", NULL);
+
+ if (SwigType_type(result) != T_VOID) {
+ if (!SwigType_isclass(result)) {
+ if (!(SwigType_ispointer(result) || SwigType_isreference(result))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(result, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(result, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(result, "c_result"), "= 0", NIL);
+ }
+ } else {
+ String *cres = SwigType_lstr(result, "c_result");
+ Printf(w->code, "%s;\n", cres);
+ Delete(cres);
+ }
+ }
+
+ if (!is_ignored) {
+ makeDirectorMethodWrapper(n, w, callback_name);
+ } else {
+ assert(is_pure_virtual);
+ Printv(w->code, " _swig_gopanic(\"call to pure virtual function ", Getattr(parent, "sym:name"), name, "\");\n", NULL);
+ if (SwigType_type(result) != T_VOID) {
+ String *retstr = SwigType_rcaststr(result, "c_result");
+ Printv(w->code, " return ", retstr, ";\n", NULL);
+ Delete(retstr);
+ }
+ }
+
+ Printv(w->code, "}", NULL);
+
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_c_directors);
+ }
+
+ Delete(cn);
+ Delete(go_type_name);
+ Delete(director_struct_name);
+ Delete(interface_name);
+ Delete(callback_name);
+ Delete(upcall_name);
+ Delete(go_name);
+ DelWrapper(w);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * makeDirectorMethodWrapper
+ *
+ * Emit the function wrapper for a director method.
+ * ------------------------------------------------------------ */
+ void makeDirectorMethodWrapper(Node *n, Wrapper *w, String *callback_name) {
+ ParmList *parms = Getattr(n, "wrap:parms");
+ SwigType *result = Getattr(n, "type");
+
+ Printv(f_c_directors, "extern \"C\" ", NULL);
+
+ String *fnname = Copy(callback_name);
+ Append(fnname, "(int");
+
+ Parm *p = parms;
+ while (p) {
+ while (checkAttribute(p, "tmap:directorin:numinputs", "0")) {
+ p = Getattr(p, "tmap:directorin:next");
+ }
+ String *cg = gcCTypeForGoValue(p, Getattr(p, "type"), Getattr(p, "lname"));
+ Printv(fnname, ", ", cg, NULL);
+ Delete(cg);
+ p = Getattr(p, "tmap:directorin:next");
+ }
+
+ Printv(fnname, ")", NULL);
+
+ if (SwigType_type(result) == T_VOID) {
+ Printv(f_c_directors, "void ", fnname, NULL);
+ } else {
+ String *tm = gcCTypeForGoValue(n, result, fnname);
+ Printv(f_c_directors, tm, NULL);
+ Delete(tm);
+ }
+
+ Delete(fnname);
+
+ Printv(f_c_directors, ";\n", NULL);
+
+ if (SwigType_type(result) != T_VOID) {
+ String *r = NewString(Swig_cresult_name());
+ String *tm = gcCTypeForGoValue(n, result, r);
+ Wrapper_add_local(w, r, tm);
+ Delete(tm);
+ Delete(r);
+ }
+
+ String *args = NewString("");
+
+ p = parms;
+ while (p) {
+ while (checkAttribute(p, "tmap:directorin:numinputs", "0")) {
+ p = Getattr(p, "tmap:directorin:next");
+ }
+
+ String *pn = NewString("swig_");
+ Append(pn, Getattr(p, "lname"));
+ Setattr(p, "emit:directorinput", pn);
+
+ String *tm = gcCTypeForGoValue(p, Getattr(p, "type"), pn);
+ Wrapper_add_local(w, pn, tm);
+ Delete(tm);
+
+ tm = Getattr(p, "tmap:directorin");
+ if (!tm) {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file,
+ line_number, "Unable to use type %s as director method argument\n", SwigType_str(Getattr(p, "type"), 0));
+ } else {
+ tm = Copy(tm);
+ Replaceall(tm, "$input", pn);
+ Replaceall(tm, "$owner", 0);
+ Printv(w->code, " ", tm, "\n", NULL);
+ Delete(tm);
+
+ Printv(args, ", ", pn, NULL);
+ }
+
+ p = Getattr(p, "tmap:directorin:next");
+ }
+
+ Printv(w->code, " ", NULL);
+ if (SwigType_type(result) != T_VOID) {
+ Printv(w->code, Swig_cresult_name(), " = ", NULL);
+ }
+ Printv(w->code, callback_name, "(go_val", args, ");\n", NULL);
+
+ /* Marshal outputs */
+ for (p = parms; p; ) {
+ String *tm;
+ if ((tm = Getattr(p, "tmap:directorargout"))) {
+ tm = Copy(tm);
+ Replaceall(tm, "$result", "jresult");
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NULL);
+ Delete(tm);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ if (SwigType_type(result) != T_VOID) {
+ String *result_str = NewString("c_result");
+ String *tm = Swig_typemap_lookup("directorout", n, result_str, NULL);
+ if (!tm) {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use type %s as director method result\n", SwigType_str(result, 0));
+ } else {
+ tm = Copy(tm);
+ Replaceall(tm, "$input", Swig_cresult_name());
+ Replaceall(tm, "$result", "c_result");
+ Printv(w->code, " ", tm, "\n", NULL);
+ String *retstr = SwigType_rcaststr(result, "c_result");
+ Printv(w->code, " return ", retstr, ";\n", NULL);
+ Delete(retstr);
+ Delete(tm);
+ }
+ Delete(result_str);
+ }
+ }
+
+
+ /* ------------------------------------------------------------
+ * classDirectorEnd
+ *
+ * Complete support for a director class.
+ * ------------------------------------------------------------ */
+
+ int classDirectorEnd(Node *n) {
+ (void) n;
+
+ Printv(f_c_directors_h, " private:\n", NULL);
+ Printv(f_c_directors_h, " intgo go_val;\n", NULL);
+ Printv(f_c_directors_h, " Swig_memory *swig_mem;\n", NULL);
+ Printv(f_c_directors_h, "};\n\n", NULL);
+
+ class_name = NULL;
+ class_node = NULL;
+
+ Delete(class_receiver);
+ class_receiver = NULL;
+
+ Delete(class_methods);
+ class_methods = NULL;
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorDisown
+ *
+ * I think Go does not require a disown method.
+ * ------------------------------------------------------------ */
+
+ int classDirectorDisown(Node *n) {
+ (void) n;
+ return SWIG_OK;
+ }
+
+ /*----------------------------------------------------------------------
+ * buildThrow()
+ *
+ * Build and return a throw clause if needed.
+ *--------------------------------------------------------------------*/
+
+ String *buildThrow(Node *n) {
+ if (Getattr(n, "noexcept"))
+ return NewString("noexcept");
+ ParmList *throw_parm_list = Getattr(n, "throws");
+ if (!throw_parm_list && !Getattr(n, "throw"))
+ return NULL;
+ String *ret = NewString("throw(");
+ if (throw_parm_list) {
+ Swig_typemap_attach_parms("throws", throw_parm_list, NULL);
+ }
+ bool first = true;
+ for (Parm *p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (first) {
+ first = false;
+ } else {
+ Printv(ret, ", ", NULL);
+ }
+ String *s = SwigType_str(Getattr(p, "type"), 0);
+ Printv(ret, s, NULL);
+ Delete(s);
+ }
+ }
+ Printv(ret, ")", NULL);
+ return ret;
+ }
+
+ /*----------------------------------------------------------------------
+ * extraDirectorProtectedCPPMethodsRequired()
+ *
+ * We don't need to check upcall when calling methods.
+ *--------------------------------------------------------------------*/
+
+ bool extraDirectorProtectedCPPMethodsRequired() const {
+ return false;
+ }
+
+ /*----------------------------------------------------------------------
+ * makeDispatchFunction
+ *
+ * Make a dispatch function for an overloaded C++ function. The
+ * receiver parameter is the receiver for a method, unless is_upcall
+ * is true. If is_upcall is true, then the receiver parameter is
+ * the type of the first argument to the function.
+ *--------------------------------------------------------------------*/
+
+ int makeDispatchFunction(Node *n, String *go_name, String *receiver, bool is_static, SwigType *director_struct, bool is_upcall) {
+ bool is_director = director_struct ? true : false;
+
+ String *nodetype = Getattr(n, "nodeType");
+ bool is_constructor = Cmp(nodetype, "constructor") == 0;
+ bool is_destructor = Cmp(nodetype, "destructor") == 0;
+
+ bool can_use_receiver = (!is_constructor && !is_destructor && !is_upcall);
+
+ bool use_receiver = (!is_static && can_use_receiver);
+
+ bool add_to_interface = (interfaces && !is_constructor && !is_destructor && !is_static && !is_upcall);
+
+ List *dispatch = Swig_overload_rank(n, false);
+ int nfunc = Len(dispatch);
+
+ SwigType *all_result;
+ bool mismatch;
+ if (is_constructor) {
+ assert(!is_upcall);
+ if (!is_director) {
+ all_result = Copy(Getattr(class_node, "classtypeobj"));
+ } else {
+ all_result = Copy(director_struct);
+ }
+ mismatch = false;
+ } else {
+ all_result = NULL;
+ mismatch = false;
+ bool any_void = false;
+ for (int i = 0; i < nfunc; ++i) {
+ Node *nn = Getitem(dispatch, i);
+ Node *ni = Getattr(nn, "directorNode") ? Getattr(nn, "directorNode") : nn;
+ SwigType *result = Getattr(ni, "go:type");
+ assert(result);
+
+ if (SwigType_type(result) == T_VOID) {
+ if (all_result) {
+ mismatch = true;
+ }
+ any_void = true;
+ } else {
+ if (any_void) {
+ mismatch = true;
+ } else if (!all_result) {
+ all_result = Copy(result);
+ } else if (Cmp(result, all_result) != 0) {
+ mismatch = true;
+ }
+ }
+ }
+ if (mismatch) {
+ Delete(all_result);
+ all_result = NULL;
+ } else if (all_result) {
+ ;
+ } else {
+ all_result = NewString("void");
+ }
+ }
+
+ Printv(f_go_wrappers, "func ", NULL);
+
+ if (receiver && use_receiver) {
+ Printv(f_go_wrappers, "(p ", receiver, ") ", NULL);
+ }
+
+ Printv(f_go_wrappers, go_name, "(", NULL);
+ if (is_director && is_constructor) {
+ Printv(f_go_wrappers, "abi interface{}, ", NULL);
+ assert(!add_to_interface);
+ }
+ if (is_upcall) {
+ Printv(f_go_wrappers, "p *", receiver, ", ", NULL);
+ assert(!add_to_interface);
+ }
+ Printv(f_go_wrappers, "a ...interface{})", NULL);
+
+ if (add_to_interface) {
+ Printv(interfaces, "\t", go_name, "(a ...interface{})", NULL);
+ }
+
+ if (mismatch) {
+ Printv(f_go_wrappers, " interface{}", NULL);
+ if (add_to_interface) {
+ Printv(interfaces, " interface{}", NULL);
+ }
+ } else if (all_result && SwigType_type(all_result) != T_VOID) {
+ if (is_director && is_constructor) {
+ Printv(f_go_wrappers, " ", receiver, NULL);
+ if (add_to_interface) {
+ Printv(interfaces, " ", receiver, NULL);
+ }
+ } else {
+ String *tm = goType(n, all_result);
+ Printv(f_go_wrappers, " ", tm, NULL);
+ if (add_to_interface) {
+ Printv(interfaces, " ", tm, NULL);
+ }
+ Delete(tm);
+ }
+ }
+ Printv(f_go_wrappers, " {\n", NULL);
+ if (add_to_interface) {
+ Printv(interfaces, "\n", NULL);
+ }
+
+ Printv(f_go_wrappers, "\targc := len(a)\n", NULL);
+
+ for (int i = 0; i < nfunc; ++i) {
+ int fn = 0;
+ Node *nn = Getitem(dispatch, i);
+ Node *ni = Getattr(nn, "directorNode") ? Getattr(nn, "directorNode") : nn;
+ Parm *pi = Getattr(ni, "wrap:parms");
+
+ // If we are using a receiver, we want to ignore a leading self
+ // parameter. Because of the way this is called, there may or
+ // may not be a self parameter at this point.
+ if (use_receiver && pi && Getattr(pi, "self")) {
+ pi = getParm(pi);
+ if (pi) {
+ pi = nextParm(pi);
+ }
+ }
+
+ int num_required = emit_num_required(pi);
+ int num_arguments = emit_num_arguments(pi);
+ bool varargs = emit_isvarargs(pi) ? true : false;
+
+ if (varargs) {
+ Printf(f_go_wrappers, "\tif argc >= %d {\n", num_required);
+ } else {
+ if (num_required == num_arguments) {
+ Printf(f_go_wrappers, "\tif argc == %d {\n", num_required);
+ } else {
+ Printf(f_go_wrappers, "\tif argc >= %d && argc <= %d {\n", num_required, num_arguments);
+ }
+ }
+
+ // Build list of collisions with the same number of arguments.
+ List *coll = NewList();
+ for (int k = i + 1; k < nfunc; ++k) {
+ Node *nnk = Getitem(dispatch, k);
+ Node *nk = Getattr(nnk, "directorNode") ? Getattr(nnk, "directorNode") : nnk;
+ Parm *pk = Getattr(nk, "wrap:parms");
+ if (use_receiver && pk && Getattr(pk, "self")) {
+ pk = getParm(pk);
+ if (pk) {
+ pk = nextParm(pk);
+ }
+ }
+ int nrk = emit_num_required(pk);
+ int nak = emit_num_arguments(pk);
+ if ((nrk >= num_required && nrk <= num_arguments)
+ || (nak >= num_required && nak <= num_arguments)
+ || (nrk <= num_required && nak >= num_arguments)
+ || (varargs && nrk >= num_required)) {
+ Append(coll, nk);
+ }
+ }
+
+ int num_braces = 0;
+ if (Len(coll) > 0 && num_arguments > 0) {
+ int j = 0;
+ Parm *pj = pi;
+ while (pj) {
+ pj = getParm(pj);
+ if (!pj) {
+ break;
+ }
+
+ // If all the overloads have the same type in this position,
+ // we can omit the check.
+ SwigType *tm = goOverloadType(pj, Getattr(pj, "type"));
+ bool emitcheck = false;
+ for (int k = 0; k < Len(coll) && !emitcheck; ++k) {
+ Node *nk = Getitem(coll, k);
+ Parm *pk = Getattr(nk, "wrap:parms");
+ if (use_receiver && pk && Getattr(pk, "self")) {
+ pk = getParm(pk);
+ if (pk) {
+ pk = nextParm(pk);
+ }
+ }
+ int nak = emit_num_arguments(pk);
+ if (nak <= j)
+ continue;
+ int l = 0;
+ Parm *pl = pk;
+ while (pl && l <= j) {
+ pl = getParm(pl);
+ if (!pl) {
+ break;
+ }
+ if (l == j) {
+ SwigType *tml = goOverloadType(pl, Getattr(pl, "type"));
+ if (Cmp(tm, tml) != 0) {
+ emitcheck = true;
+ }
+ Delete(tml);
+ }
+ pl = nextParm(pl);
+ ++l;
+ }
+ }
+
+ if (emitcheck) {
+ if (j >= num_required) {
+ Printf(f_go_wrappers, "\t\tif argc > %d {\n", j);
+ ++num_braces;
+ }
+
+ fn = i + 1;
+ Printf(f_go_wrappers, "\t\tif _, ok := a[%d].(%s); !ok {\n", j, tm);
+ Printf(f_go_wrappers, "\t\t\tgoto check_%d\n", fn);
+ Printv(f_go_wrappers, "\t\t}\n", NULL);
+ }
+
+ Delete(tm);
+
+ pj = nextParm(pj);
+
+ ++j;
+ }
+ }
+
+ for (; num_braces > 0; --num_braces) {
+ Printv(f_go_wrappers, "\t\t}\n", NULL);
+ }
+
+ // We may need to generate multiple calls if there are variable
+ // argument lists involved. Build the start of the call.
+
+ String *start = NewString("");
+
+ SwigType *result = Getattr(ni, "go:type");
+
+ if (is_constructor) {
+ result = all_result;
+ } else if (is_destructor) {
+ result = NULL;
+ }
+
+ if (result && SwigType_type(result) != T_VOID && (!all_result || SwigType_type(all_result) != T_VOID)) {
+ Printv(start, "return ", NULL);
+ }
+
+ bool advance_parm = false;
+
+ if (receiver && use_receiver) {
+ Printv(start, "p.", go_name, NULL);
+ } else if (can_use_receiver && !isStatic(ni) && pi && Getattr(pi, "self")) {
+ // This is an overload of a static function and a non-static
+ // function.
+ assert(num_required > 0);
+ SwigType *tm = goWrapperType(pi, Getattr(pi, "type"), true);
+ String *nm = buildGoName(Getattr(ni, "sym:name"), false, isFriend(ni));
+ Printv(start, "a[0].(", tm, ").", nm, NULL);
+ Delete(nm);
+ Delete(tm);
+ advance_parm = true;
+ } else {
+ Printv(start, go_name, NULL);
+ }
+
+ Printv(start, Getattr(ni, "sym:overname"), "(", NULL);
+
+ bool need_comma = false;
+
+ if (is_director && is_constructor) {
+ Printv(start, "abi", NULL);
+ need_comma = true;
+ }
+ if (is_upcall) {
+ Printv(start, "p", NULL);
+ need_comma = true;
+ }
+ Parm *p = pi;
+ int pn = 0;
+ if (advance_parm) {
+ p = getParm(p);
+ if (p) {
+ p = nextParm(p);
+ }
+ ++pn;
+ }
+ while (pn < num_required) {
+ p = getParm(p);
+
+ if (need_comma) {
+ Printv(start, ", ", NULL);
+ }
+
+ SwigType *tm = goType(p, Getattr(p, "type"));
+ Printf(start, "a[%d].(%s)", pn, tm);
+ Delete(tm);
+
+ need_comma = true;
+ ++pn;
+ p = nextParm(p);
+ }
+
+ String *end = NULL;
+ if (!result || SwigType_type(result) == T_VOID || (all_result && SwigType_type(all_result) == T_VOID)) {
+ end = NewString("");
+ Printv(end, "return", NULL);
+ if (!all_result || SwigType_type(all_result) != T_VOID) {
+ Printv(end, " 0", NULL);
+ }
+ }
+
+ if (num_required == num_arguments) {
+ Printv(f_go_wrappers, "\t\t", start, ")\n", NULL);
+ if (end) {
+ Printv(f_go_wrappers, "\t\t", end, "\n", NULL);
+ }
+ } else {
+ Printv(f_go_wrappers, "\t\tswitch argc {\n", NULL);
+ for (int j = num_required; j <= num_arguments; ++j) {
+ Printf(f_go_wrappers, "\t\tcase %d:\n", j);
+ Printv(f_go_wrappers, "\t\t\t", start, NULL);
+ bool nc = need_comma;
+ for (int k = num_required; k < j; ++k) {
+ if (nc) {
+ Printv(f_go_wrappers, ", ", NULL);
+ }
+ Printf(f_go_wrappers, "a[%d]", k);
+ nc = true;
+ }
+ Printv(f_go_wrappers, ")\n", NULL);
+ if (end) {
+ Printv(f_go_wrappers, "\t\t\t", end, "\n", NULL);
+ }
+ }
+ Printv(f_go_wrappers, "\t\t}\n", NULL);
+ }
+
+ Printv(f_go_wrappers, "\t}\n", NULL);
+
+ if (fn != 0) {
+ Printf(f_go_wrappers, "check_%d:\n", fn);
+ }
+
+ Delete(coll);
+ }
+
+ Printv(f_go_wrappers, "\tpanic(\"No match for overloaded function call\")\n", NULL);
+ Printv(f_go_wrappers, "}\n\n", NULL);
+
+ Delete(all_result);
+ Delete(dispatch);
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * checkFunctionVisibility()
+ *
+ * Return true if we should write out a function based on its
+ * visibility, false otherwise.
+ * ---------------------------------------------------------------------- */
+
+ bool checkFunctionVisibility(Node *n, Node *parent) {
+ // Write out a public function.
+ if (is_public(n))
+ return true;
+ // Don't write out a private function.
+ if (is_private(n))
+ return false;
+ // Write a protected function for a director class in
+ // dirprot_mode.
+ if (parent == NULL) {
+ return false;
+ }
+ if (dirprot_mode() && Swig_directorclass(parent))
+ return true;
+ // Otherwise don't write out a protected function.
+ return false;
+ }
+
+
+ /* ----------------------------------------------------------------------
+ * exportedName()
+ *
+ * Given a C/C++ name, return a name in Go which will be exported.
+ * If the first character is an upper case letter, this returns a
+ * copy of its argument. If the first character is a lower case
+ * letter, this forces it to upper case. Otherwise, this prepends
+ * 'X'.
+ * ---------------------------------------------------------------------- */
+
+ String *exportedName(String *name) {
+ String *copy = Copy(name);
+ char c = *Char(copy);
+ if (islower(c)) {
+ char l[2];
+ char u[2];
+ l[0] = c;
+ l[1] = '\0';
+ u[0] = toupper(c);
+ u[1] = '\0';
+ Replace(copy, l, u, DOH_REPLACE_FIRST);
+ } else if (!isalpha(c)) {
+ char l[2];
+ char u[3];
+ l[0] = c;
+ l[1] = '\0';
+ u[0] = 'X';
+ u[1] = c;
+ u[2] = '\0';
+ Replace(copy, l, u, DOH_REPLACE_FIRST);
+ }
+ String *ret = Swig_name_mangle(copy);
+ Delete(copy);
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * removeClassname()
+ *
+ * If the name starts with the current class name, followed by an
+ * underscore, remove it. If there is no current class name, this
+ * simply returns a copy of the name. This undoes Swig's way of
+ * recording the class name in a member name.
+ * ---------------------------------------------------------------------- */
+
+ String *removeClassname(String *name) {
+ String *copy = Copy(name);
+ if (class_name) {
+ char *p = Char(name);
+ if (Strncmp(name, class_name, Len(class_name)) == 0 && p[Len(class_name)] == '_') {
+ Replace(copy, class_name, "", DOH_REPLACE_FIRST);
+ Replace(copy, "_", "", DOH_REPLACE_FIRST);
+ }
+ }
+ return copy;
+ }
+
+ /* ----------------------------------------------------------------------
+ * buildGoName()
+ *
+ * Build the name to use for an ordinary function, variable, or
+ * whatever in Go. The name argument is something like the sym:name
+ * attribute of the node. If is_static is false, this could be a
+ * method, and the returned name will be the name of the
+ * method--i.e., it will not include the class name.
+ * ---------------------------------------------------------------------- */
+
+ String *buildGoName(String *name, bool is_static, bool is_friend) {
+ String *nw = NewString("");
+ if (is_static && !is_friend && class_name) {
+ String *c1 = exportedName(class_name);
+ Append(nw, c1);
+ Delete(c1);
+ }
+ String *c2 = removeClassname(name);
+ String *c3 = exportedName(c2);
+ Append(nw, c3);
+ Delete(c2);
+ Delete(c3);
+ String *ret = Swig_name_mangle(nw);
+ Delete(nw);
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * buildGoWrapperName()
+ *
+ * Build the name to use for a Go wrapper function. This is a
+ * function called by the real Go function in order to convert C++
+ * classes from interfaces to pointers, and other such conversions
+ * between the Go type and the C++ type.
+ * ---------------------------------------------------------------------- */
+
+ String *buildGoWrapperName(String *name, String *overname) {
+ String *s1 = NewString("_swig_wrap_");
+ Append(s1, name);
+ String *s2 = Swig_name_mangle(s1);
+ Delete(s1);
+ if (overname) {
+ Append(s2, overname);
+ }
+ return s2;
+ }
+
+ /* ----------------------------------------------------------------------
+ * checkNameConflict()
+ *
+ * Check for a name conflict on the name we are going to use in Go.
+ * These conflicts are likely because of the enforced
+ * capitalization. When we find one, issue a warning and return
+ * false. If the name is OK, return true.
+ * ---------------------------------------------------------------------- */
+
+ bool checkNameConflict(String* name, Node* n, const_String_or_char_ptr scope) {
+ Node *lk = symbolLookup(name, scope);
+ if (lk) {
+ String *n1 = Getattr(n, "sym:name");
+ if (!n1) {
+ n1 = Getattr(n, "name");
+ }
+ String *n2 = Getattr(lk, "sym:name");
+ if (!n2) {
+ n2 = Getattr(lk, "name");
+ }
+ Swig_warning(WARN_GO_NAME_CONFLICT, input_file, line_number,
+ "Ignoring '%s' due to Go name ('%s') conflict with '%s'\n",
+ n1, name, n2);
+ return false;
+ }
+ bool r = addSymbol(name, n, scope) ? true : false;
+ assert(r);
+ (void)r;
+ return true;
+ }
+
+ /* ----------------------------------------------------------------------
+ * checkIgnoredParameters()
+ *
+ * If any of the parameters of this function, or the return type,
+ * are ignored due to a name conflict, give a warning and return
+ * false.
+ * ---------------------------------------------------------------------- */
+
+ bool checkIgnoredParameters(Node *n, String *go_name) {
+ ParmList *parms = Getattr(n, "parms");
+ if (parms) {
+ Wrapper *dummy = NewWrapper();
+ emit_attach_parmmaps(parms, dummy);
+ int parm_count = emit_num_arguments(parms);
+ Parm *p = parms;
+
+ for (int i = 0; i < parm_count; ++i) {
+ p = getParm(p);
+ if (!checkIgnoredType(n, go_name, Getattr(p, "type"))) {
+ DelWrapper(dummy);
+ return false;
+ }
+ p = nextParm(p);
+ }
+
+ DelWrapper(dummy);
+ }
+
+ if (!checkIgnoredType(n, go_name, Getattr(n, "type"))) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /* ----------------------------------------------------------------------
+ * checkIgnoredType()
+ *
+ * If this type is being ignored due to a name conflict, give a
+ * warning and return false.
+ * ---------------------------------------------------------------------- */
+
+ bool checkIgnoredType(Node *n, String *go_name, SwigType *type) {
+ if (hasGoTypemap(n, type)) {
+ return true;
+ }
+
+ SwigType *t = SwigType_typedef_resolve_all(type);
+
+ bool ret = true;
+ bool is_conflict = false;
+ Node *e = Language::enumLookup(t);
+ if (e) {
+ if (GetFlag(e, "go:conflict")) {
+ is_conflict = true;
+ }
+ } else if (SwigType_issimple(t)) {
+ Node *cn = classLookup(t);
+ if (cn) {
+ if (GetFlag(cn, "go:conflict")) {
+ is_conflict = true;
+ }
+ }
+ } else if (SwigType_ispointer(t) || SwigType_isarray(t) || SwigType_isqualifier(t) || SwigType_isreference(t)) {
+ SwigType *r = Copy(t);
+ if (SwigType_ispointer(r)) {
+ SwigType_del_pointer(r);
+ } else if (SwigType_isarray(r)) {
+ SwigType_del_array(r);
+ } else if (SwigType_isqualifier(r)) {
+ SwigType_del_qualifier(r);
+ } else {
+ SwigType_del_reference(r);
+ }
+
+ if (!checkIgnoredType(n, go_name, r)) {
+ ret = false;
+ }
+
+ Delete(r);
+ }
+
+ if (is_conflict) {
+ String *s = SwigType_str(t, NULL);
+ Swig_warning(WARN_GO_NAME_CONFLICT, input_file, line_number,
+ "Ignoring '%s' (Go name '%s') due to Go name conflict for parameter or result type '%s'\n",
+ Getattr(n, "name"), go_name, s);
+ Delete(s);
+ ret = false;
+ }
+
+ Delete(t);
+
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * goType()
+ *
+ * Given a SWIG type, return a string for the type in Go.
+ * ---------------------------------------------------------------------- */
+
+ String *goType(Node *n, SwigType *type) {
+ return goTypeWithInfo(n, type, false, NULL);
+ }
+
+ /* ----------------------------------------------------------------------
+ * goImType()
+ *
+ * Given a SWIG type, return a string for the intermediate Go type
+ * to pass to C/C++. This is like goType except that it looks for
+ * an imtype typemap entry first.
+ * ---------------------------------------------------------------------- */
+
+ String *goImType(Node *n, SwigType *type) {
+ return goTypeWithInfo(n, type, true, NULL);
+ }
+
+ /* ----------------------------------------------------------------------
+ * goTypeWithInfo()
+ *
+ * Like goType, but return some more information.
+ *
+ * If use_imtype is true, this look for a imtype typemap entry.
+ *
+ * If the p_is_interface parameter is not NULL, this sets
+ * *p_is_interface to indicate whether this type is going to be
+ * represented by a Go interface type. These are cases where the Go
+ * code needs to make some adjustments when passing values back and
+ * forth with C/C++.
+ * ---------------------------------------------------------------------- */
+
+ String *goTypeWithInfo(Node *n, SwigType *type, bool use_imtype, bool *p_is_interface) {
+ if (p_is_interface) {
+ *p_is_interface = false;
+ }
+
+ String *ret = NULL;
+ if (use_imtype) {
+ if (n && Cmp(type, Getattr(n, "type")) == 0) {
+ if (Strcmp(Getattr(n, "nodeType"), "parm") == 0) {
+ ret = Getattr(n, "tmap:imtype");
+ }
+ if (!ret) {
+ ret = Swig_typemap_lookup("imtype", n, "", NULL);
+ }
+ } else {
+ Parm *p = NewParm(type, "goImType", n);
+ ret = Swig_typemap_lookup("imtype", p, "", NULL);
+ Delete(p);
+ }
+ }
+ if (!ret) {
+ if (n && Cmp(type, Getattr(n, "type")) == 0) {
+ if (Strcmp(Getattr(n, "nodeType"), "parm") == 0) {
+ ret = Getattr(n, "tmap:gotype");
+ }
+ if (!ret) {
+ ret = Swig_typemap_lookup("gotype", n, "", NULL);
+ }
+ } else {
+ Parm *p = NewParm(type, "goType", n);
+ ret = Swig_typemap_lookup("gotype", p, "", NULL);
+ Delete(p);
+ }
+ }
+
+ if (ret && Strstr(ret, "$gotypename") != 0) {
+ ret = NULL;
+ }
+
+ if (ret) {
+ return Copy(ret);
+ }
+
+ SwigType *t = SwigType_typedef_resolve_all(type);
+
+ if (SwigType_isenum(t)) {
+ Node *e = Language::enumLookup(t);
+ if (e) {
+ ret = goEnumName(e);
+ } else if (Strcmp(t, "enum ") == 0) {
+ ret = NewString("int");
+ } else {
+ // An unknown enum - one that has not been parsed (neither a C enum forward reference nor a definition) or an ignored enum
+ String *tt = Copy(t);
+ Replace(tt, "enum ", "", DOH_REPLACE_ANY);
+ ret = exportedName(tt);
+ Setattr(undefined_enum_types, t, ret);
+ Delete(tt);
+ }
+ } else if (SwigType_isfunctionpointer(t) || SwigType_isfunction(t)) {
+ ret = NewString("_swig_fnptr");
+ } else if (SwigType_ismemberpointer(t)) {
+ ret = NewString("_swig_memberptr");
+ } else if (SwigType_issimple(t)) {
+ Node *cn = classLookup(t);
+ if (cn) {
+ ret = Getattr(cn, "sym:name");
+ if (!ret) {
+ ret = Getattr(cn, "name");
+ }
+ ret = exportedName(ret);
+
+ Node *cnmod = Getattr(cn, "module");
+ if (!cnmod || Strcmp(Getattr(cnmod, "name"), module) == 0) {
+ Setattr(undefined_types, t, t);
+ } else {
+ String *nw = NewString("");
+ Printv(nw, getModuleName(Getattr(cnmod, "name")), ".", ret, NULL);
+ Delete(ret);
+ ret = nw;
+ }
+ } else {
+ // SWIG does not know about this type.
+ ret = exportedName(t);
+ Setattr(undefined_types, t, t);
+ }
+ if (p_is_interface) {
+ *p_is_interface = true;
+ }
+ } else if (SwigType_ispointer(t) || SwigType_isarray(t)) {
+ SwigType *r = Copy(t);
+ if (SwigType_ispointer(r)) {
+ SwigType_del_pointer(r);
+ } else {
+ SwigType_del_array(r);
+ }
+
+ if (SwigType_type(r) == T_VOID) {
+ ret = NewString("uintptr");
+ } else {
+ bool is_interface;
+ String *base = goTypeWithInfo(n, r, false, &is_interface);
+
+ // At the Go level, an unknown or class type is handled as an
+ // interface wrapping a pointer. This means that if a
+ // function returns the C type X, we will be wrapping the C
+ // type X*. In Go we will call that type X. That means that
+ // if a C function expects X*, we can pass the Go type X. And
+ // that means that when we see the C type X*, we should use
+ // the Go type X.
+
+ // The is_interface variable tells us this. However, it will
+ // be true both for the case of X and for the case of X*. If
+ // r is a pointer here, then we are looking at X**. There is
+ // really no good way for us to handle that.
+ bool is_pointer_to_pointer = false;
+ if (is_interface) {
+ SwigType *c = Copy(r);
+ if (SwigType_isqualifier(c)) {
+ SwigType_del_qualifier(c);
+ if (SwigType_ispointer(c) || SwigType_isarray(c)) {
+ is_pointer_to_pointer = true;
+ }
+ }
+ Delete(c);
+ }
+
+ if (is_interface) {
+ if (!is_pointer_to_pointer) {
+ ret = base;
+ if (p_is_interface) {
+ *p_is_interface = true;
+ }
+ } else {
+ ret = NewString("uintptr");
+ }
+ } else {
+ ret = NewString("*");
+ Append(ret, base);
+ Delete(base);
+ }
+ }
+
+ Delete(r);
+ } else if (SwigType_isreference(t)) {
+ SwigType *r = Copy(t);
+ SwigType_del_reference(r);
+
+ // If this is a const reference, and we are looking at a pointer
+ // to it, then we just use the pointer we already have.
+ bool add_pointer = true;
+ if (SwigType_isqualifier(r)) {
+ String *q = SwigType_parm(r);
+ if (Strcmp(q, "const") == 0) {
+ SwigType *c = Copy(r);
+ SwigType_del_qualifier(c);
+ if (SwigType_ispointer(c)) {
+ add_pointer = false;
+ }
+ Delete(c);
+ }
+ }
+ if (add_pointer) {
+ SwigType_add_pointer(r);
+ }
+ ret = goTypeWithInfo(n, r, false, p_is_interface);
+ Delete(r);
+ } else if (SwigType_isqualifier(t)) {
+ SwigType *r = Copy(t);
+ SwigType_del_qualifier(r);
+ ret = goTypeWithInfo(n, r, false, p_is_interface);
+ Delete(r);
+ } else if (SwigType_isvarargs(t)) {
+ ret = NewString("[]interface{}");
+ }
+
+ Delete(t);
+
+ if (!ret) {
+ Swig_warning(WARN_LANG_NATIVE_UNIMPL, input_file, line_number, "No Go typemap defined for %s\n", SwigType_str(type, 0));
+ ret = NewString("uintptr");
+ }
+
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * cgoTypeForGoValue()
+ *
+ * Given a SWIG type, return a string for the C type to use for the
+ * cgo wrapper code. This always returns a simple identifier, since
+ * it is used in Go code as C.name.
+ *
+ * This sets *c_struct_type if the C type uses a struct where the Go
+ * type uses a simple type. This is true for strings and slices.
+ * When this is true the Go code has to jump through unsafe hoops to
+ * pass the type checker.
+ * ---------------------------------------------------------------------- */
+
+ String *cgoTypeForGoValue(Node *n, SwigType *type, bool *c_struct_type) {
+ *c_struct_type = false;
+
+ bool is_interface;
+ String *go_type = goTypeWithInfo(n, type, true, &is_interface);
+ if (is_interface) {
+ Delete(go_type);
+ return NewString("uintptr_t");
+ }
+ if (Strcmp(go_type, "uintptr") == 0) {
+ Delete(go_type);
+ return NewString("uintptr_t");
+ }
+ if (((char*)Char(go_type))[0] == '*') {
+ // Treat all pointers as void*. There is no meaningful type
+ // checking going on here anyhow, and that lets us avoid
+ // worrying about defining the base type of the pointer.
+ Delete(go_type);
+ return NewString("swig_voidp");
+ }
+
+ // Check for some Go types that are really pointers under the covers.
+ bool is_hidden_pointer = Strncmp(go_type, "func(", 5) == 0 || Strncmp(go_type, "map[", 4) == 0 || Strncmp(go_type, "chan ", 5) == 0;
+
+ Delete(go_type);
+
+ String *ct = Getattr(n, "emit:cgotype");
+ if (ct) {
+ *c_struct_type = Getattr(n, "emit:cgotypestruct") ? true : false;
+ return Copy(ct);
+ }
+
+ String *t = Copy(type);
+ if (SwigType_isarray(t) && Getattr(n, "tmap:gotype") == NULL) {
+ SwigType_del_array(t);
+ SwigType_add_pointer(t);
+ }
+
+ bool add_typedef = true;
+
+ static int count;
+ ++count;
+ ct = NewStringf("swig_type_%d", count);
+
+ String *gct = gcCTypeForGoValue(n, t, ct);
+ Delete(t);
+
+ if (Strncmp(gct, "_gostring_", 10) == 0 || Strncmp(gct, "_goslice_", 9) == 0) {
+ *c_struct_type = true;
+ Setattr(n, "emit:cgotypestruct", type);
+ } else {
+ char *p = Strstr(gct, ct);
+ if (p != NULL && p > (char*)Char(gct) && p[-1] == '*' && p[Len(ct)] == '\0') {
+ // Treat all pointers as void*. See above.
+ Delete(ct);
+ --count;
+ ct = NewString("swig_voidp");
+ add_typedef = false;
+ if (is_hidden_pointer) {
+ // A Go type that is really a pointer, like func, map, chan,
+ // is being represented in C by a pointer. This is fine,
+ // but we have to memcpy the type rather than simply
+ // converting it.
+ *c_struct_type = true;
+ Setattr(n, "emit:cgotypestruct", type);
+ }
+ }
+
+ if (Strncmp(gct, "bool ", 5) == 0) {
+ // Change the C++ type bool to the C type _Bool.
+ Replace(gct, "bool", "_Bool", DOH_REPLACE_FIRST);
+ }
+ if (Strncmp(gct, "intgo ", 6) == 0) {
+ // We #define intgo to swig_intgo for the cgo comment.
+ Replace(gct, "intgo", "swig_intgo", DOH_REPLACE_FIRST);
+ }
+ p = Strstr(gct, ct);
+ if (p != NULL && p > (char*)Char(gct) && p[-1] == ' ' && p[Len(ct)] == '\0') {
+ String *q = NewStringWithSize(gct, Len(gct) - Len(ct) - 1);
+ if (validIdentifier(q)) {
+ // This is a simple type name, and we can use it directly.
+ Delete(ct);
+ --count;
+ ct = q;
+ add_typedef = false;
+ }
+ }
+ }
+ if (add_typedef) {
+ Printv(f_cgo_comment_typedefs, "typedef ", gct, ";\n", NULL);
+ }
+
+ Setattr(n, "emit:cgotype", ct);
+
+ Delete(gct);
+
+ return Copy(ct);
+ }
+
+ /* ----------------------------------------------------------------------
+ * goWrapperType()
+ *
+ * Given a type, return a string for the type to use for the wrapped
+ * Go function. This function exists because for a C++ class we
+ * need to convert interface and reference types.
+ * ---------------------------------------------------------------------- */
+
+ String *goWrapperType(Node *n, SwigType *type, bool is_result) {
+ bool is_interface;
+ String *ret = goTypeWithInfo(n, type, true, &is_interface);
+
+ // If this is an interface, we want to pass the real type.
+ if (is_interface) {
+ Delete(ret);
+ if (!is_result) {
+ ret = NewString("uintptr");
+ } else {
+ SwigType *ty = SwigType_typedef_resolve_all(type);
+ while (true) {
+ if (SwigType_ispointer(ty)) {
+ SwigType_del_pointer(ty);
+ } else if (SwigType_isarray(ty)) {
+ SwigType_del_array(ty);
+ } else if (SwigType_isreference(ty)) {
+ SwigType_del_reference(ty);
+ } else if (SwigType_isqualifier(ty)) {
+ SwigType_del_qualifier(ty);
+ } else {
+ break;
+ }
+ }
+ assert(SwigType_issimple(ty));
+ String *p = goCPointerType(ty, true);
+ Delete(ty);
+ ret = p;
+ }
+ }
+
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * goOverloadType()
+ *
+ * Given a type, return the Go type to use when dispatching of
+ * overloaded functions. This is normally just the usual Go type.
+ * However, for a C++ class, the usual Go type is an interface type.
+ * And if that interface type represents a C++ type that SWIG does
+ * not know about, then the interface type generated for any C++
+ * class will match that interface. So for that case, we match on
+ * the underlying integer type.
+ *
+ * It has to work this way so that we can handle a derived type of a
+ * %ignore'd type. It's unlikely that anybody will have a value of
+ * an undefined type, but we support it because it worked in the
+ * past.
+ * ---------------------------------------------------------------------- */
+
+ String *goOverloadType(Node *n, SwigType *type) {
+ SwigType *ty = SwigType_typedef_resolve_all(type);
+ while (true) {
+ if (SwigType_ispointer(ty)) {
+ SwigType_del_pointer(ty);
+ } else if (SwigType_isarray(ty)) {
+ SwigType_del_array(ty);
+ } else if (SwigType_isreference(ty)) {
+ SwigType_del_reference(ty);
+ } else if (SwigType_isqualifier(ty)) {
+ SwigType_del_qualifier(ty);
+ } else {
+ break;
+ }
+ }
+
+ String* go_type = goType(n, ty);
+
+ if (Getattr(undefined_types, ty) && !Getattr(defined_types, go_type)) {
+ Delete(go_type);
+ return goWrapperType(n, type, true);
+ }
+
+ Delete(go_type);
+ return goType(n, type);
+ }
+
+ /* ----------------------------------------------------------------------
+ * goCPointerType()
+ *
+ * Return the name of the Go type to use for the C pointer value.
+ * The regular C type is the name of an interface type which wraps a
+ * pointer whose name is returned by this function.
+ * ---------------------------------------------------------------------- */
+
+ String *goCPointerType(SwigType *type, bool add_to_hash) {
+ SwigType *ty = SwigType_typedef_resolve_all(type);
+ Node *cn = classLookup(ty);
+ String *ex;
+ String *ret;
+ if (!cn) {
+ if (add_to_hash) {
+ Setattr(undefined_types, ty, ty);
+ }
+ ret = NewString("Swigcptr");
+ ex = exportedName(ty);
+ Append(ret, ex);
+ } else {
+ String *cname = Getattr(cn, "sym:name");
+ if (!cname) {
+ cname = Getattr(cn, "name");
+ }
+ ex = exportedName(cname);
+ Node *cnmod = Getattr(cn, "module");
+ if (!cnmod || Strcmp(Getattr(cnmod, "name"), module) == 0) {
+ if (add_to_hash) {
+ Setattr(undefined_types, ty, ty);
+ }
+ ret = NewString("Swigcptr");
+ Append(ret, ex);
+ } else {
+ ret = NewString("");
+ Printv(ret, getModuleName(Getattr(cnmod, "name")), ".Swigcptr", ex, NULL);
+ }
+ }
+ Delete(ty);
+ Delete(ex);
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * gcCTypeForGoValue()
+ *
+ * Given a type, return the C/C++ type which will be used to catch
+ * the value in Go. This is the gc version.
+ * ---------------------------------------------------------------------- */
+
+ String *gcCTypeForGoValue(Node *n, SwigType *type, String *name) {
+ bool is_interface;
+ String *gt = goTypeWithInfo(n, type, true, &is_interface);
+
+ String *tail = NewString("");
+ SwigType *t = SwigType_typedef_resolve_all(type);
+ bool is_const_ref = false;
+ if (SwigType_isreference(t)) {
+ SwigType* tt = Copy(t);
+ SwigType_del_reference(tt);
+ if (SwigType_isqualifier(tt)) {
+ String* q = SwigType_parm(tt);
+ if (Strcmp(q, "const") == 0) {
+ is_const_ref = true;
+ }
+ }
+ Delete(tt);
+ }
+ if (!is_const_ref) {
+ while (Strncmp(gt, "*", 1) == 0) {
+ Replace(gt, "*", "", DOH_REPLACE_FIRST);
+ Printv(tail, "*", NULL);
+ }
+ }
+ Delete(t);
+
+ bool is_string = Strcmp(gt, "string") == 0;
+ bool is_slice = Strncmp(gt, "[]", 2) == 0;
+ bool is_function = Strcmp(gt, "_swig_fnptr") == 0;
+ bool is_member = Strcmp(gt, "_swig_memberptr") == 0;
+ bool is_complex64 = Strcmp(gt, "complex64") == 0;
+ bool is_complex128 = Strcmp(gt, "complex128") == 0;
+ bool is_bool = false;
+ bool is_int8 = false;
+ bool is_int16 = false;
+ bool is_int = Strcmp(gt, "int") == 0 || Strcmp(gt, "uint") == 0;
+ bool is_int32 = false;
+ bool is_int64 = false;
+ bool is_float32 = false;
+ bool is_float64 = false;
+
+ bool has_typemap = (n != NULL && Getattr(n, "tmap:gotype") != NULL) || hasGoTypemap(n, type);
+ if (has_typemap) {
+ is_bool = Strcmp(gt, "bool") == 0;
+ is_int8 = Strcmp(gt, "int8") == 0 || Strcmp(gt, "uint8") == 0 || Strcmp(gt, "byte") == 0;
+ is_int16 = Strcmp(gt, "int16") == 0 || Strcmp(gt, "uint16") == 0;
+ is_int32 = Strcmp(gt, "int32") == 0 || Strcmp(gt, "uint32") == 0;
+ is_int64 = Strcmp(gt, "int64") == 0 || Strcmp(gt, "uint64") == 0;
+ is_float32 = Strcmp(gt, "float32") == 0;
+ is_float64 = Strcmp(gt, "float64") == 0;
+ }
+ Delete(gt);
+
+ String *ret;
+ if (is_string) {
+ // Note that we don't turn a reference to a string into a
+ // pointer to a string. Strings are immutable anyhow.
+ ret = NewString("");
+ Printv(ret, "_gostring_", tail, " ", name, NULL);
+ Delete(tail);
+ return ret;
+ } else if (is_slice) {
+ // Slices are always passed as a _goslice_, whether or not references
+ // are involved.
+ ret = NewString("");
+ Printv(ret, "_goslice_", tail, " ", name, NULL);
+ Delete(tail);
+ return ret;
+ } else if (is_function || is_member) {
+ ret = NewString("");
+ Printv(ret, "void*", tail, " ", name, NULL);
+ Delete(tail);
+ return ret;
+ } else if (is_complex64) {
+ ret = NewString("_Complex float ");
+ } else if (is_complex128) {
+ ret = NewString("_Complex double ");
+ } else if (is_interface) {
+ SwigType *t = SwigType_typedef_resolve_all(type);
+ if (SwigType_ispointer(t)) {
+ SwigType_del_pointer(t);
+ }
+ if (SwigType_isreference(t)) {
+ SwigType_del_reference(t);
+ }
+ SwigType_add_pointer(t);
+ ret = SwigType_lstr(t, name);
+ Delete(t);
+ Delete(tail);
+ return ret;
+ } else {
+ SwigType *t = SwigType_typedef_resolve_all(type);
+ if (!has_typemap && SwigType_isreference(t)) {
+ // A const reference to a known type, or to a pointer, is not
+ // mapped to a pointer.
+ SwigType_del_reference(t);
+ if (SwigType_isqualifier(t)) {
+ String *q = SwigType_parm(t);
+ if (Strcmp(q, "const") == 0) {
+ SwigType_del_qualifier(t);
+ if (hasGoTypemap(n, t) || SwigType_ispointer(t)) {
+ if (is_int) {
+ ret = NewString("intgo ");
+ Append(ret, name);
+ } else if (is_int64) {
+ ret = NewString("long long ");
+ Append(ret, name);
+ } else {
+ ret = SwigType_lstr(t, name);
+ }
+ Delete(q);
+ Delete(t);
+ Delete(tail);
+ return ret;
+ }
+ }
+ Delete(q);
+ }
+ }
+
+ if (Language::enumLookup(t) != NULL) {
+ is_int = true;
+ } else {
+ SwigType *tstripped = SwigType_strip_qualifiers(t);
+ if (SwigType_isenum(tstripped))
+ is_int = true;
+ Delete(tstripped);
+ }
+
+ Delete(t);
+ if (is_bool) {
+ ret = NewString("bool ");
+ } else if (is_int8) {
+ ret = NewString("char ");
+ } else if (is_int16) {
+ ret = NewString("short ");
+ } else if (is_int) {
+ ret = NewString("intgo ");
+ } else if (is_int32) {
+ ret = NewString("int ");
+ } else if (is_int64) {
+ ret = NewString("long long ");
+ } else if (is_float32) {
+ ret = NewString("float ");
+ } else if (is_float64) {
+ ret = NewString("double ");
+ } else {
+ Delete(tail);
+ return SwigType_lstr(type, name);
+ }
+ }
+
+ Append(ret, tail);
+ if (!has_typemap && SwigType_isreference(type)) {
+ Append(ret, "* ");
+ }
+ Append(ret, name);
+ Delete(tail);
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * goTypeIsInterface
+ *
+ * Return whether this C++ type is represented as an interface type
+ * in Go. These types require adjustments in the Go code when
+ * passing them back and forth between Go and C++.
+ * ---------------------------------------------------------------------- */
+
+ bool goTypeIsInterface(Node *n, SwigType *type) {
+ bool is_interface;
+ Delete(goTypeWithInfo(n, type, false, &is_interface));
+ return is_interface;
+ }
+
+ /* ----------------------------------------------------------------------
+ * hasGoTypemap
+ *
+ * Return whether a type has a "gotype" typemap entry.
+ * ---------------------------------------------------------------------- */
+
+ bool hasGoTypemap(Node *n, SwigType *type) {
+ Parm *p = NewParm(type, "test", n);
+ SwigType *tm = Swig_typemap_lookup("gotype", p, "", NULL);
+ Delete(p);
+ if (tm && Strstr(tm, "$gotypename") == 0) {
+ Delete(tm);
+ return true;
+ }
+ Delete(tm);
+ return false;
+ }
+
+ /* ----------------------------------------------------------------------
+ * goEnumName()
+ *
+ * Given an enum node, return a string to use for the enum type in Go.
+ * ---------------------------------------------------------------------- */
+
+ String *goEnumName(Node *n) {
+ String *ret = Getattr(n, "go:enumname");
+ if (ret) {
+ return Copy(ret);
+ }
+
+ if (Equal(Getattr(n, "type"), "enum ")) {
+ return NewString("int");
+ }
+
+ String *type = Getattr(n, "enumtype");
+ assert(type);
+ char *p = Char(type);
+ int len = Len(type);
+ String *s = NewString("");
+ bool capitalize = true;
+ for (int i = 0; i < len; ++i, ++p) {
+ if (*p == ':') {
+ ++i;
+ ++p;
+ assert(*p == ':');
+ capitalize = true;
+ } else if (capitalize) {
+ Putc(toupper(*p), s);
+ capitalize = false;
+ } else {
+ Putc(*p, s);
+ }
+ }
+
+ ret = Swig_name_mangle(s);
+ Delete(s);
+ return ret;
+ }
+
+
+ /* ----------------------------------------------------------------------
+ * getParm()
+ *
+ * Get the real parameter to use.
+ * ---------------------------------------------------------------------- */
+
+ Parm *getParm(Parm *p) {
+ while (p && checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+ return p;
+ }
+
+ /* ----------------------------------------------------------------------
+ * nextParm()
+ *
+ * Return the next parameter.
+ * ---------------------------------------------------------------------- */
+
+ Parm *nextParm(Parm *p) {
+ if (!p) {
+ return NULL;
+ } else if (Getattr(p, "tmap:in")) {
+ return Getattr(p, "tmap:in:next");
+ } else {
+ return nextSibling(p);
+ }
+ }
+
+ /* ----------------------------------------------------------------------
+ * isStatic
+ *
+ * Return whether a node should be considered as static rather than
+ * as a member.
+ * ---------------------------------------------------------------------- */
+
+ bool isStatic(Node *n) {
+ String *storage = Getattr(n, "storage");
+ return (storage && (Swig_storage_isstatic(n) || Strcmp(storage, "friend") == 0) && (!SmartPointer || !Getattr(n, "allocate:smartpointeraccess")));
+ }
+
+ /* ----------------------------------------------------------------------
+ * isFriend
+ *
+ * Return whether a node is a friend.
+ * ---------------------------------------------------------------------- */
+
+ bool isFriend(Node *n) {
+ String *storage = Getattr(n, "storage");
+ return storage && Strcmp(storage, "friend") == 0;
+ }
+
+ /* ----------------------------------------------------------------------
+ * goGetattr
+ *
+ * Fetch an attribute from a node but return NULL if it is the empty string.
+ * ---------------------------------------------------------------------- */
+ Node *goGetattr(Node *n, const char *name) {
+ Node *ret = Getattr(n, name);
+ if (ret != NULL && Len(ret) == 0) {
+ ret = NULL;
+ }
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * goTypemapLookup
+ *
+ * Look up a typemap but return NULL if it is the empty string.
+ * ---------------------------------------------------------------------- */
+ String *goTypemapLookup(const char *name, Node *node, const char *lname) {
+ String *ret = Swig_typemap_lookup(name, node, lname, NULL);
+ if (ret != NULL && Len(ret) == 0) {
+ ret = NULL;
+ }
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------
+ * getModuleName
+ *
+ * Return the name of a module. This is different from module path:
+ * "some/path/to/module" -> "module".
+ * ---------------------------------------------------------------------- */
+
+ String *getModuleName(String *module_path) {
+ char *suffix = strrchr(Char(module_path), '/');
+ if (suffix == NULL) {
+ return module_path;
+ }
+ return Str(suffix + 1);
+ }
+
+}; /* class GO */
+
+/* -----------------------------------------------------------------------------
+ * swig_go() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_go() {
+ return new GO();
+}
+extern "C" Language *swig_go(void) {
+ return new_swig_go();
+}
+
+/* -----------------------------------------------------------------------------
+ * Static member variables
+ * ----------------------------------------------------------------------------- */
+
+// Usage message.
+const char * const GO::usage = "\
+Go Options (available with -go)\n\
+ -cgo - Generate cgo input files\n\
+ -no-cgo - Do not generate cgo input files\n\
+ -gccgo - Generate code for gccgo rather than gc\n\
+ -go-pkgpath <p> - Like gccgo -fgo-pkgpath option\n\
+ -go-prefix <p> - Like gccgo -fgo-prefix option\n\
+ -import-prefix <p> - Prefix to add to %import directives\n\
+ -intgosize <s> - Set size of Go int type--32 or 64 bits\n\
+ -package <name> - Set name of the Go package to <name>\n\
+ -use-shlib - Force use of a shared library\n\
+ -soname <name> - Set shared library holding C/C++ code to <name>\n\
+\n";
diff --git a/contrib/tools/swig/Source/Modules/guile.cxx b/contrib/tools/swig/Source/Modules/guile.cxx
new file mode 100644
index 0000000000..605e031975
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/guile.cxx
@@ -0,0 +1,1662 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * guile.cxx
+ *
+ * Guile language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include <ctype.h>
+
+// Note string broken in half for compilers that can't handle long strings
+static const char *usage = "\
+Guile Options (available with -guile)\n\
+ -emitsetters - Emit procedures-with-setters for variables\n\
+ and structure slots.\n\
+ -emitslotaccessors - Emit accessor methods for all GOOPS slots\n" "\
+ -exportprimitive - Add the (export ...) code from scmstub into the\n\
+ GOOPS file.\n\
+ -goopsprefix <prefix> - Prepend <prefix> to all goops identifiers\n\
+ -Linkage <lstyle> - Use linkage protocol <lstyle> (default `simple')\n\
+ Use `module' for native Guile module linking\n\
+ (requires Guile >= 1.5.0). Use `passive' for\n\
+ passive linking (no C-level module-handling code),\n\
+ `ltdlmod' for Guile's old dynamic module\n\
+ convention (Guile <= 1.4), or `hobbit' for hobbit\n\
+ modules.\n\
+ -onlysetters - Don't emit traditional getter and setter\n\
+ procedures for structure slots,\n\
+ only emit procedures-with-setters.\n\
+ -package <name> - Set the path of the module to <name>\n\
+ (default NULL)\n\
+ -prefix <name> - Use <name> as prefix [default \"gswig_\"]\n\
+ -procdoc <file> - Output procedure documentation to <file>\n\
+ -procdocformat <format> - Output procedure documentation in <format>;\n\
+ one of `guile-1.4', `plain', `texinfo'\n\
+ -proxy - Export GOOPS class definitions\n\
+ -primsuffix <suffix> - Name appended to primitive module when exporting\n\
+ GOOPS classes. (default = \"primitive\")\n\
+ -scmstub - Output Scheme file with module declaration and\n\
+ exports; only with `passive' and `simple' linkage\n\
+ -useclassprefix - Prepend the class name to all goops identifiers\n\
+\n";
+
+static File *f_begin = 0;
+static File *f_runtime = 0;
+static File *f_header = 0;
+static File *f_wrappers = 0;
+static File *f_init = 0;
+
+
+static String *prefix = NewString("gswig_");
+static char *module = 0;
+static String *package = 0;
+static enum {
+ GUILE_LSTYLE_SIMPLE, // call `SWIG_init()'
+ GUILE_LSTYLE_PASSIVE, // passive linking (no module code)
+ GUILE_LSTYLE_MODULE, // native guile module linking (Guile >= 1.4.1)
+ GUILE_LSTYLE_LTDLMOD_1_4, // old (Guile <= 1.4) dynamic module convention
+ GUILE_LSTYLE_HOBBIT // use (hobbit4d link)
+} linkage = GUILE_LSTYLE_SIMPLE;
+
+static File *procdoc = 0;
+static bool scmstub = false;
+static String *scmtext;
+static bool goops = false;
+static String *goopstext;
+static String *goopscode;
+static String *goopsexport;
+
+static enum {
+ GUILE_1_4,
+ PLAIN,
+ TEXINFO
+} docformat = GUILE_1_4;
+
+static int emit_setters = 0;
+static int only_setters = 0;
+static int emit_slot_accessors = 0;
+static int struct_member = 0;
+
+static String *beforereturn = 0;
+static String *return_nothing_doc = 0;
+static String *return_one_doc = 0;
+static String *return_multi_doc = 0;
+
+static String *exported_symbols = 0;
+
+static int exporting_destructor = 0;
+static String *swigtype_ptr = 0;
+
+/* GOOPS stuff */
+static String *primsuffix = 0;
+static String *class_name = 0;
+static String *short_class_name = 0;
+static String *goops_class_methods;
+static int in_class = 0;
+static int have_constructor = 0;
+static int useclassprefix = 0; // -useclassprefix argument
+static String *goopsprefix = 0; // -goopsprefix argument
+static int primRenamer = 0; // if (use-modules ((...) :renamer ...) is exported to GOOPS file
+static int exportprimitive = 0; // -exportprimitive argument
+static String *memberfunction_name = 0;
+
+extern "C" {
+ static int has_classname(Node *class_node) {
+ return Getattr(class_node, "guile:goopsclassname") ? 1 : 0;
+ }
+}
+
+class GUILE:public Language {
+public:
+
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+ int i;
+
+ SWIG_library_directory("guile");
+ SWIG_typemap_lang("guile");
+
+ // Look for certain command line options
+ for (i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage, stdout);
+ Exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "-prefix") == 0) {
+ if (argv[i + 1]) {
+ prefix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-package") == 0) {
+ if (argv[i + 1]) {
+ package = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-Linkage") == 0 || strcmp(argv[i], "-linkage") == 0) {
+ if (argv[i + 1]) {
+ if (0 == strcmp(argv[i + 1], "ltdlmod"))
+ linkage = GUILE_LSTYLE_LTDLMOD_1_4;
+ else if (0 == strcmp(argv[i + 1], "hobbit"))
+ linkage = GUILE_LSTYLE_HOBBIT;
+ else if (0 == strcmp(argv[i + 1], "simple"))
+ linkage = GUILE_LSTYLE_SIMPLE;
+ else if (0 == strcmp(argv[i + 1], "passive"))
+ linkage = GUILE_LSTYLE_PASSIVE;
+ else if (0 == strcmp(argv[i + 1], "module"))
+ linkage = GUILE_LSTYLE_MODULE;
+ else
+ Swig_arg_error();
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-procdoc") == 0) {
+ if (argv[i + 1]) {
+ procdoc = NewFile(argv[i + 1], "w", SWIG_output_files());
+ if (!procdoc) {
+ FileErrorDisplay(argv[i + 1]);
+ Exit(EXIT_FAILURE);
+ }
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-procdocformat") == 0) {
+ if (strcmp(argv[i + 1], "guile-1.4") == 0)
+ docformat = GUILE_1_4;
+ else if (strcmp(argv[i + 1], "plain") == 0)
+ docformat = PLAIN;
+ else if (strcmp(argv[i + 1], "texinfo") == 0)
+ docformat = TEXINFO;
+ else
+ Swig_arg_error();
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else if (strcmp(argv[i], "-emit-setters") == 0 || strcmp(argv[i], "-emitsetters") == 0) {
+ emit_setters = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-only-setters") == 0 || strcmp(argv[i], "-onlysetters") == 0) {
+ emit_setters = 1;
+ only_setters = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-emit-slot-accessors") == 0 || strcmp(argv[i], "-emitslotaccessors") == 0) {
+ emit_slot_accessors = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-scmstub") == 0) {
+ scmstub = true;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-shadow") == 0) || ((strcmp(argv[i], "-proxy") == 0))) {
+ goops = true;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-gh") == 0) {
+ Printf(stderr, "Deprecated command line option: -gh. Wrappers are always generated for the SCM interface. See documentation for more information regarding the deprecated GH interface.\n");
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-scm") == 0) {
+ Printf(stderr, "Deprecated command line option: -scm. Wrappers are always generated for the SCM interface. See documentation for more information regarding the deprecated GH interface.\n");
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-primsuffix") == 0) {
+ if (argv[i + 1]) {
+ primsuffix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-goopsprefix") == 0) {
+ if (argv[i + 1]) {
+ goopsprefix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-useclassprefix") == 0) {
+ useclassprefix = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-exportprimitive") == 0) {
+ exportprimitive = 1;
+ // should use Swig_warning() here?
+ Swig_mark_arg(i);
+ }
+ }
+ }
+
+ // set default value for primsuffix
+ if (!primsuffix)
+ primsuffix = NewString("primitive");
+
+ //goops support can only be enabled if passive or module linkage is used
+ if (goops) {
+ if (linkage != GUILE_LSTYLE_PASSIVE && linkage != GUILE_LSTYLE_MODULE) {
+ Printf(stderr, "guile: GOOPS support requires passive or module linkage\n");
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ if (goops) {
+ // -proxy implies -emit-setters
+ emit_setters = 1;
+ }
+
+ if ((linkage == GUILE_LSTYLE_PASSIVE && scmstub) || linkage == GUILE_LSTYLE_MODULE)
+ primRenamer = 1;
+
+ if (exportprimitive && primRenamer) {
+ // should use Swig_warning() ?
+ Printf(stderr, "guile: Warning: -exportprimitive only makes sense with passive linkage without a scmstub.\n");
+ }
+
+ // Make sure `prefix' ends in an underscore
+ if (prefix) {
+ const char *px = Char(prefix);
+ if (px[Len(prefix) - 1] != '_')
+ Printf(prefix, "_");
+ }
+
+ /* Add a symbol for this module */
+ Preprocessor_define("SWIGGUILE 1", 0);
+ /* Read in default typemaps */
+ SWIG_config_file("guile_scm.swg");
+ allow_overloading();
+
+ }
+
+ /* ------------------------------------------------------------
+ * top()
+ * ------------------------------------------------------------ */
+
+ virtual int top(Node *n) {
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+
+ scmtext = NewString("");
+ Swig_register_filebyname("scheme", scmtext);
+ exported_symbols = NewString("");
+ goopstext = NewString("");
+ Swig_register_filebyname("goops", goopstext);
+ goopscode = NewString("");
+ goopsexport = NewString("");
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "GUILE");
+
+ /* Write out directives and declarations */
+
+ module = Swig_copy_string(Char(Getattr(n, "name")));
+
+ switch (linkage) {
+ case GUILE_LSTYLE_SIMPLE:
+ /* Simple linkage; we have to export the SWIG_init function. The user can
+ rename the function by a #define. */
+ Printf(f_runtime, "#define SWIG_GUILE_INIT_STATIC extern\n");
+ break;
+ default:
+ /* Other linkage; we make the SWIG_init function static */
+ Printf(f_runtime, "#define SWIG_GUILE_INIT_STATIC static\n");
+ break;
+ }
+
+ if (CPlusPlus) {
+ Printf(f_runtime, "extern \"C\" {\n\n");
+ }
+ Printf(f_runtime, "SWIG_GUILE_INIT_STATIC void\nSWIG_init (void);\n");
+ if (CPlusPlus) {
+ Printf(f_runtime, "\n}\n");
+ }
+
+ Printf(f_runtime, "\n");
+
+ Language::top(n);
+
+ /* Close module */
+
+ Printf(f_wrappers, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
+
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+
+ Printf(f_init, "}\n\n");
+ Printf(f_init, "#ifdef __cplusplus\n}\n#endif\n");
+
+ String *module_name = NewString("");
+
+ if (!module)
+ Printv(module_name, "swig", NIL);
+ else {
+ if (package)
+ Printf(module_name, "%s/%s", package, module);
+ else
+ Printv(module_name, module, NIL);
+ }
+ emit_linkage(module_name);
+
+ Delete(module_name);
+
+ if (procdoc) {
+ Delete(procdoc);
+ procdoc = NULL;
+ }
+ Delete(goopscode);
+ Delete(goopsexport);
+ Delete(goopstext);
+
+ /* Close all of the files */
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+ Dump(f_wrappers, f_begin);
+ Wrapper_pretty_print(f_init, f_begin);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_runtime);
+ Delete(f_begin);
+ return SWIG_OK;
+ }
+
+ void emit_linkage(String *module_name) {
+ String *module_func = NewString("");
+
+ if (CPlusPlus) {
+ Printf(f_init, "extern \"C\" {\n\n");
+ }
+
+ Printv(module_func, module_name, NIL);
+ Replaceall(module_func, "-", "_");
+
+ switch (linkage) {
+ case GUILE_LSTYLE_SIMPLE:
+ Printf(f_init, "\n/* Linkage: simple */\n");
+ break;
+ case GUILE_LSTYLE_PASSIVE:
+ Printf(f_init, "\n/* Linkage: passive */\n");
+ Replaceall(module_func, "/", "_");
+ Insert(module_func, 0, "scm_init_");
+ Append(module_func, "_module");
+
+ Printf(f_init, "SCM\n%s (void)\n{\n", module_func);
+ Printf(f_init, " SWIG_init();\n");
+ Printf(f_init, " return SCM_UNSPECIFIED;\n");
+ Printf(f_init, "}\n");
+ break;
+ case GUILE_LSTYLE_LTDLMOD_1_4:
+ Printf(f_init, "\n/* Linkage: ltdlmod */\n");
+ Replaceall(module_func, "/", "_");
+ Insert(module_func, 0, "scm_init_");
+ Append(module_func, "_module");
+ Printf(f_init, "SCM\n%s (void)\n{\n", module_func);
+ {
+ String *mod = NewString(module_name);
+ Replaceall(mod, "/", " ");
+ Printf(f_init, " scm_register_module_xxx (\"%s\", (void *) SWIG_init);\n", mod);
+ Printf(f_init, " return SCM_UNSPECIFIED;\n");
+ Delete(mod);
+ }
+ Printf(f_init, "}\n");
+ break;
+ case GUILE_LSTYLE_MODULE:
+ Printf(f_init, "\n/* Linkage: module */\n");
+ Replaceall(module_func, "/", "_");
+ Insert(module_func, 0, "scm_init_");
+ Append(module_func, "_module");
+
+ Printf(f_init, "static void SWIG_init_helper(void *data)\n");
+ Printf(f_init, "{\n SWIG_init();\n");
+ if (Len(exported_symbols) > 0)
+ Printf(f_init, " scm_c_export(%sNULL);", exported_symbols);
+ Printf(f_init, "\n}\n\n");
+
+ Printf(f_init, "SCM\n%s (void)\n{\n", module_func);
+ {
+ String *mod = NewString(module_name);
+ if (goops)
+ Printv(mod, "-", primsuffix, NIL);
+ Replaceall(mod, "/", " ");
+ Printf(f_init, " scm_c_define_module(\"%s\",\n", mod);
+ Printf(f_init, " SWIG_init_helper, NULL);\n");
+ Printf(f_init, " return SCM_UNSPECIFIED;\n");
+ Delete(mod);
+ }
+ Printf(f_init, "}\n");
+ break;
+ case GUILE_LSTYLE_HOBBIT:
+ Printf(f_init, "\n/* Linkage: hobbit */\n");
+ Replaceall(module_func, "/", "_slash_");
+ Insert(module_func, 0, "scm_init_");
+ Printf(f_init, "SCM\n%s (void)\n{\n", module_func);
+ {
+ String *mod = NewString(module_name);
+ Replaceall(mod, "/", " ");
+ Printf(f_init, " scm_register_module_xxx (\"%s\", (void *) SWIG_init);\n", mod);
+ Printf(f_init, " return SCM_UNSPECIFIED;\n");
+ Delete(mod);
+ }
+ Printf(f_init, "}\n");
+ break;
+ default:
+ fputs("Fatal internal error: Invalid Guile linkage setting.\n", stderr);
+ Exit(EXIT_FAILURE);
+ }
+
+ if (scmstub) {
+ /* Emit Scheme stub if requested */
+ String *primitive_name = NewString(module_name);
+ if (goops)
+ Printv(primitive_name, "-", primsuffix, NIL);
+
+ String *mod = NewString(primitive_name);
+ Replaceall(mod, "/", " ");
+
+ String *fname = NewStringf("%s%s.scm",
+ SWIG_output_directory(),
+ primitive_name);
+ Delete(primitive_name);
+ File *scmstubfile = NewFile(fname, "w", SWIG_output_files());
+ if (!scmstubfile) {
+ FileErrorDisplay(fname);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(fname);
+
+ Swig_banner_target_lang(scmstubfile, ";;;");
+ Printf(scmstubfile, "\n");
+ if (linkage == GUILE_LSTYLE_SIMPLE || linkage == GUILE_LSTYLE_PASSIVE)
+ Printf(scmstubfile, "(define-module (%s))\n\n", mod);
+ Delete(mod);
+ Printf(scmstubfile, "%s", scmtext);
+ if ((linkage == GUILE_LSTYLE_SIMPLE || linkage == GUILE_LSTYLE_PASSIVE)
+ && Len(exported_symbols) > 0) {
+ String *ex = NewString(exported_symbols);
+ Replaceall(ex, ", ", "\n ");
+ Replaceall(ex, "\"", "");
+ Chop(ex);
+ Printf(scmstubfile, "\n(export %s)\n", ex);
+ Delete(ex);
+ }
+ Delete(scmstubfile);
+ }
+
+ if (goops) {
+ String *mod = NewString(module_name);
+ Replaceall(mod, "/", " ");
+
+ String *fname = NewStringf("%s%s.scm", SWIG_output_directory(),
+ module_name);
+ File *goopsfile = NewFile(fname, "w", SWIG_output_files());
+ if (!goopsfile) {
+ FileErrorDisplay(fname);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(fname);
+ Swig_banner_target_lang(goopsfile, ";;;");
+ Printf(goopsfile, "\n");
+ Printf(goopsfile, "(define-module (%s))\n", mod);
+ Printf(goopsfile, "%s\n", goopstext);
+ Printf(goopsfile, "(use-modules (oop goops) (Swig common))\n");
+ if (primRenamer) {
+ Printf(goopsfile, "(use-modules ((%s-%s) :renamer (symbol-prefix-proc 'primitive:)))\n", mod, primsuffix);
+ }
+ Printf(goopsfile, "%s\n(export %s)", goopscode, goopsexport);
+ if (exportprimitive) {
+ String *ex = NewString(exported_symbols);
+ Replaceall(ex, ", ", "\n ");
+ Replaceall(ex, "\"", "");
+ Chop(ex);
+ Printf(goopsfile, "\n(export %s)", ex);
+ Delete(ex);
+ }
+ Delete(mod);
+ Delete(goopsfile);
+ }
+
+ Delete(module_func);
+ if (CPlusPlus) {
+ Printf(f_init, "\n}\n");
+ }
+ }
+
+ /* Return true iff T is a pointer type */
+
+ int is_a_pointer(SwigType *t) {
+ return SwigType_ispointer(SwigType_typedef_resolve_all(t));
+ }
+
+ /* Report an error handling the given type. */
+
+ void throw_unhandled_guile_type_error(SwigType *d) {
+ Swig_warning(WARN_TYPEMAP_UNDEF, input_file, line_number, "Unable to handle type %s.\n", SwigType_str(d, 0));
+ }
+
+ /* Write out procedure documentation */
+
+ void write_doc(const String *proc_name, const String *signature, const String *doc, const String *signature2 = NULL) {
+ switch (docformat) {
+ case GUILE_1_4:
+ Printv(procdoc, "\f\n", NIL);
+ Printv(procdoc, "(", signature, ")\n", NIL);
+ if (signature2)
+ Printv(procdoc, "(", signature2, ")\n", NIL);
+ Printv(procdoc, doc, "\n", NIL);
+ break;
+ case PLAIN:
+ Printv(procdoc, "\f", proc_name, "\n\n", NIL);
+ Printv(procdoc, "(", signature, ")\n", NIL);
+ if (signature2)
+ Printv(procdoc, "(", signature2, ")\n", NIL);
+ Printv(procdoc, doc, "\n\n", NIL);
+ break;
+ case TEXINFO:
+ Printv(procdoc, "\f", proc_name, "\n", NIL);
+ Printv(procdoc, "@deffn primitive ", signature, "\n", NIL);
+ if (signature2)
+ Printv(procdoc, "@deffnx primitive ", signature2, "\n", NIL);
+ Printv(procdoc, doc, "\n", NIL);
+ Printv(procdoc, "@end deffn\n\n", NIL);
+ break;
+ }
+ }
+
+ /* returns false if the typemap is an empty string */
+ bool handle_documentation_typemap(String *output,
+ const String *maybe_delimiter, Parm *p, const String *typemap, const String *default_doc, const String *name = NULL) {
+ String *tmp = NewString("");
+ String *tm;
+ if (!(tm = Getattr(p, typemap))) {
+ Printf(tmp, "%s", default_doc);
+ tm = tmp;
+ }
+ bool result = (Len(tm) > 0);
+ if (maybe_delimiter && Len(output) > 0 && Len(tm) > 0) {
+ Printv(output, maybe_delimiter, NIL);
+ }
+ const String *pn = !name ? (const String *) Getattr(p, "name") : name;
+ String *pt = Getattr(p, "type");
+ Replaceall(tm, "$name", pn); // legacy for $parmname
+ Replaceall(tm, "$type", SwigType_str(pt, 0));
+ /* $NAME is like $name, but marked-up as a variable. */
+ String *ARGNAME = NewString("");
+ if (docformat == TEXINFO)
+ Printf(ARGNAME, "@var{%s}", pn);
+ else
+ Printf(ARGNAME, "%(upper)s", pn);
+ Replaceall(tm, "$NAME", ARGNAME);
+ Replaceall(tm, "$PARMNAME", ARGNAME);
+ Printv(output, tm, NIL);
+ Delete(tmp);
+ return result;
+ }
+
+ /* ------------------------------------------------------------
+ * functionWrapper()
+ * Create a function declaration and register it with the interpreter.
+ * ------------------------------------------------------------ */
+
+ virtual int functionWrapper(Node *n) {
+ String *iname = Getattr(n, "sym:name");
+ SwigType *d = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ Parm *p;
+ String *proc_name = 0;
+ char source[256];
+ Wrapper *f = NewWrapper();
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+ String *signature = NewString("");
+ String *doc_body = NewString("");
+ String *returns = NewString("");
+ String *method_signature = NewString("");
+ String *primitive_args = NewString("");
+ Hash *scheme_arg_names = NewHash();
+ int num_results = 1;
+ String *tmp = NewString("");
+ String *tm;
+ int i;
+ int numargs = 0;
+ int numreq = 0;
+ String *overname = 0;
+ int args_passed_as_array = 0;
+ int scheme_argnum = 0;
+ bool any_specialized_arg = false;
+
+ // Make a wrapper name for this
+ String *wname = Swig_name_wrapper(iname);
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ args_passed_as_array = 1;
+ } else {
+ if (!addSymbol(iname, n)) {
+ DelWrapper(f);
+ return SWIG_ERROR;
+ }
+ }
+ if (overname) {
+ Append(wname, overname);
+ }
+ Setattr(n, "wrap:name", wname);
+
+ // Build the name for scheme.
+ proc_name = NewString(iname);
+ Replaceall(proc_name, "_", "-");
+
+ /* Emit locals etc. into f->code; figure out which args to ignore */
+ emit_parameter_variables(l, f);
+
+ /* Attach the standard typemaps */
+ emit_attach_parmmaps(l, f);
+ Setattr(n, "wrap:parms", l);
+
+ /* Get number of required and total arguments */
+ numargs = emit_num_arguments(l);
+ numreq = emit_num_required(l);
+
+ /* Declare return variable */
+
+ Wrapper_add_local(f, "gswig_result", "SCM gswig_result");
+ Wrapper_add_local(f, "gswig_list_p", "SWIGUNUSED int gswig_list_p = 0");
+
+ /* Open prototype and signature */
+
+ Printv(f->def, "static SCM\n", wname, " (", NIL);
+ if (args_passed_as_array) {
+ Printv(f->def, "int argc, SCM *argv", NIL);
+ }
+ Printv(signature, proc_name, NIL);
+
+ /* Now write code to extract the parameters */
+
+ for (i = 0, p = l; i < numargs; i++) {
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ int opt_p = (i >= numreq);
+
+ // Produce names of source and target
+ if (args_passed_as_array)
+ sprintf(source, "argv[%d]", i);
+ else
+ sprintf(source, "s_%d", i);
+
+ if (!args_passed_as_array) {
+ if (i != 0)
+ Printf(f->def, ", ");
+ Printf(f->def, "SCM s_%d", i);
+ }
+ if (opt_p) {
+ Printf(f->code, " if (%s != SCM_UNDEFINED) {\n", source);
+ }
+ if ((tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source);
+ Printv(f->code, tm, "\n", NIL);
+
+ SwigType *pb = SwigType_typedef_resolve_all(SwigType_base(pt));
+ SwigType *pn = Getattr(p, "name");
+ String *argname;
+ scheme_argnum++;
+ if (pn && !Getattr(scheme_arg_names, pn))
+ argname = pn;
+ else {
+ /* Anonymous arg or re-used argument name -- choose a name that cannot clash */
+ argname = NewStringf("%%arg%d", scheme_argnum);
+ }
+
+ if (procdoc) {
+ if (i == numreq) {
+ /* First optional argument */
+ Printf(signature, " #:optional");
+ }
+ /* Add to signature (arglist) */
+ handle_documentation_typemap(signature, " ", p, "tmap:in:arglist", "$name", argname);
+ /* Document the type of the arg in the documentation body */
+ handle_documentation_typemap(doc_body, ", ", p, "tmap:in:doc", "$NAME is of type <$type>", argname);
+ }
+
+ if (goops) {
+ if (i < numreq) {
+ if (strcmp("void", Char(pt)) != 0) {
+ Node *class_node = Swig_symbol_clookup_check(pb, Getattr(n, "sym:symtab"),
+ has_classname);
+ String *goopsclassname = !class_node ? NULL : Getattr(class_node, "guile:goopsclassname");
+ /* do input conversion */
+ if (goopsclassname) {
+ Printv(method_signature, " (", argname, " ", goopsclassname, ")", NIL);
+ any_specialized_arg = true;
+ } else {
+ Printv(method_signature, " ", argname, NIL);
+ }
+ Printv(primitive_args, " ", argname, NIL);
+ Setattr(scheme_arg_names, argname, p);
+ }
+ }
+ }
+
+ if (!pn) {
+ Delete(argname);
+ }
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ throw_unhandled_guile_type_error(pt);
+ p = nextSibling(p);
+ }
+ if (opt_p)
+ Printf(f->code, " }\n");
+ }
+ if (Len(doc_body) > 0)
+ Printf(doc_body, ".\n");
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ /* Pass output arguments back to the caller. */
+
+ /* Insert argument output code */
+ String *returns_argout = NewString("");
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ if (procdoc) {
+ if (handle_documentation_typemap(returns_argout, ", ", p, "tmap:argout:doc", "$NAME (of type $type)")) {
+ /* A documentation typemap that is not the empty string
+ indicates that a value is returned to Scheme. */
+ num_results++;
+ }
+ }
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ if (exporting_destructor) {
+ /* Mark the destructor's argument as destroyed. */
+ String *tm = NewString("SWIG_Guile_MarkPointerDestroyed($input);");
+ Replaceall(tm, "$input", Getattr(l, "emit:input"));
+ Printv(cleanup, tm, "\n", NIL);
+ Delete(tm);
+ }
+
+ /* Close prototype */
+
+ Printf(f->def, ")\n{\n");
+
+ /* Define the scheme name in C. This define is used by several Guile
+ macros. */
+ Printv(f->def, "#define FUNC_NAME \"", proc_name, "\"", NIL);
+
+ // Now write code to make the function call
+ String *actioncode = emit_action(n);
+
+ // Now have return value, figure out what to do with it.
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ Replaceall(tm, "$result", "gswig_result");
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "1");
+ else
+ Replaceall(tm, "$owner", "0");
+ Printv(f->code, tm, "\n", NIL);
+ } else {
+ throw_unhandled_guile_type_error(d);
+ }
+ emit_return_variable(n, d, f);
+
+ // Documentation
+ if ((tm = Getattr(n, "tmap:out:doc"))) {
+ Printv(returns, tm, NIL);
+ if (Len(tm) > 0)
+ num_results = 1;
+ else
+ num_results = 0;
+ } else {
+ String *s = SwigType_str(d, 0);
+ Chop(s);
+ Printf(returns, "<%s>", s);
+ Delete(s);
+ num_results = 1;
+ }
+ Append(returns, returns_argout);
+
+
+ // Dump the argument output code
+ Printv(f->code, outarg, NIL);
+
+ // Dump the argument cleanup code
+ Printv(f->code, cleanup, NIL);
+
+ // Look for any remaining cleanup
+
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printv(f->code, tm, "\n", NIL);
+ }
+ }
+ // Free any memory allocated by the function being wrapped..
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printv(f->code, tm, "\n", NIL);
+ }
+ // Wrap things up (in a manner of speaking)
+
+ if (beforereturn)
+ Printv(f->code, beforereturn, "\n", NIL);
+ Printv(f->code, "return gswig_result;\n", NIL);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", iname);
+ // Undefine the scheme name
+
+ Printf(f->code, "#undef FUNC_NAME\n");
+ Printf(f->code, "}\n");
+
+ Wrapper_print(f, f_wrappers);
+
+ if (!Getattr(n, "sym:overloaded")) {
+ if (numargs > 10) {
+ int i;
+ /* gh_new_procedure would complain: too many args */
+ /* Build a wrapper wrapper */
+ Printv(f_wrappers, "static SCM\n", wname, "_rest (SCM rest)\n", NIL);
+ Printv(f_wrappers, "{\n", NIL);
+ Printf(f_wrappers, "SCM arg[%d];\n", numargs);
+ Printf(f_wrappers, "SWIG_Guile_GetArgs (arg, rest, %d, %d, \"%s\");\n", numreq, numargs - numreq, proc_name);
+ Printv(f_wrappers, "return ", wname, "(", NIL);
+ Printv(f_wrappers, "arg[0]", NIL);
+ for (i = 1; i < numargs; i++)
+ Printf(f_wrappers, ", arg[%d]", i);
+ Printv(f_wrappers, ");\n", NIL);
+ Printv(f_wrappers, "}\n", NIL);
+ /* Register it */
+ Printf(f_init, "scm_c_define_gsubr(\"%s\", 0, 0, 1, (swig_guile_proc) %s_rest);\n", proc_name, wname);
+ } else if (emit_setters && struct_member && strlen(Char(proc_name)) > 3) {
+ int len = Len(proc_name);
+ const char *pc = Char(proc_name);
+ /* MEMBER-set and MEMBER-get functions. */
+ int is_setter = (pc[len - 3] == 's');
+ if (is_setter) {
+ Printf(f_init, "SCM setter = ");
+ struct_member = 2; /* have a setter */
+ } else
+ Printf(f_init, "SCM getter = ");
+ /* GOOPS support uses the MEMBER-set and MEMBER-get functions,
+ so ignore only_setters in this case. */
+ if (only_setters && !goops)
+ Printf(f_init, "scm_c_make_gsubr(\"%s\", %d, %d, 0, (swig_guile_proc) %s);\n", proc_name, numreq, numargs - numreq, wname);
+ else
+ Printf(f_init, "scm_c_define_gsubr(\"%s\", %d, %d, 0, (swig_guile_proc) %s);\n", proc_name, numreq, numargs - numreq, wname);
+
+ if (!is_setter) {
+ /* Strip off "-get" */
+ if (struct_member == 2) {
+ /* There was a setter, so create a procedure with setter */
+ Printf(f_init, "scm_c_define");
+ Printf(f_init, "(\"%.*s\", " "scm_make_procedure_with_setter(getter, setter));\n", pc, len - 4);
+ } else {
+ /* There was no setter, so make an alias to the getter */
+ Printf(f_init, "scm_c_define");
+ Printf(f_init, "(\"%.*s\", getter);\n", pc, len - 4);
+ }
+ Printf(exported_symbols, "\"%.*s\", ", pc, len - 4);
+ }
+ } else {
+ /* Register the function */
+ if (exporting_destructor) {
+ Printf(f_init, "((swig_guile_clientdata *)(SWIGTYPE%s->clientdata))->destroy = (guile_destructor) %s;\n", swigtype_ptr, wname);
+ //Printf(f_init, "SWIG_TypeClientData(SWIGTYPE%s, (void *) %s);\n", swigtype_ptr, wname);
+ }
+ Printf(f_init, "scm_c_define_gsubr(\"%s\", %d, %d, 0, (swig_guile_proc) %s);\n", proc_name, numreq, numargs - numreq, wname);
+ }
+ } else { /* overloaded function; don't export the single methods */
+ if (!Getattr(n, "sym:nextSibling")) {
+ /* Emit overloading dispatch function */
+
+ int maxargs;
+ String *dispatch = Swig_overload_dispatch(n, "return %s(argc,argv);", &maxargs);
+
+ /* Generate a dispatch wrapper for all overloaded functions */
+
+ Wrapper *df = NewWrapper();
+ String *dname = Swig_name_wrapper(iname);
+
+ Printv(df->def, "static SCM\n", dname, "(SCM rest)\n{\n", NIL);
+ Printf(df->code, "#define FUNC_NAME \"%s\"\n", proc_name);
+ Printf(df->code, "SCM argv[%d];\n", maxargs);
+ Printf(df->code, "int argc = SWIG_Guile_GetArgs (argv, rest, %d, %d, \"%s\");\n", 0, maxargs, proc_name);
+ Printv(df->code, dispatch, "\n", NIL);
+ Printf(df->code, "scm_misc_error(\"%s\", \"No matching method for generic function `%s'\", SCM_EOL);\n", proc_name, iname);
+ Printf(df->code, "#undef FUNC_NAME\n");
+ Printv(df->code, "}\n", NIL);
+ Wrapper_print(df, f_wrappers);
+ Printf(f_init, "scm_c_define_gsubr(\"%s\", 0, 0, 1, (swig_guile_proc) %s);\n", proc_name, dname);
+ DelWrapper(df);
+ Delete(dispatch);
+ Delete(dname);
+ }
+ }
+ Printf(exported_symbols, "\"%s\", ", proc_name);
+
+ if (!in_class || memberfunction_name) {
+ // export wrapper into goops file
+ String *method_def = NewString("");
+ String *goops_name;
+ if (in_class)
+ goops_name = NewString(memberfunction_name);
+ else
+ goops_name = goopsNameMapping(proc_name, "");
+ String *primitive_name = NewString("");
+ if (primRenamer)
+ Printv(primitive_name, "primitive:", proc_name, NIL);
+ else
+ Printv(primitive_name, proc_name, NIL);
+ Replaceall(method_signature, "_", "-");
+ Replaceall(primitive_args, "_", "-");
+ if (!any_specialized_arg) {
+ /* If there would not be any specialized argument in
+ the method declaration, we simply re-export the
+ function. This is a performance optimization. */
+ Printv(method_def, "(define ", goops_name, " ", primitive_name, ")\n", NIL);
+ } else if (numreq == numargs) {
+ Printv(method_def, "(define-method (", goops_name, method_signature, ")\n", NIL);
+ Printv(method_def, " (", primitive_name, primitive_args, "))\n", NIL);
+ } else {
+ /* Handle optional args. For the rest argument, use a name
+ that cannot clash. */
+ Printv(method_def, "(define-method (", goops_name, method_signature, " . %args)\n", NIL);
+ Printv(method_def, " (apply ", primitive_name, primitive_args, " %args))\n", NIL);
+ }
+ if (in_class) {
+ /* Defer method definition till end of class definition. */
+ Printv(goops_class_methods, method_def, NIL);
+ } else {
+ Printv(goopscode, method_def, NIL);
+ }
+ Printf(goopsexport, "%s ", goops_name);
+ Delete(primitive_name);
+ Delete(goops_name);
+ Delete(method_def);
+ }
+
+ if (procdoc) {
+ String *returns_text = NewString("");
+ if (num_results == 0)
+ Printv(returns_text, return_nothing_doc, NIL);
+ else if (num_results == 1)
+ Printv(returns_text, return_one_doc, NIL);
+ else
+ Printv(returns_text, return_multi_doc, NIL);
+ /* Substitute documentation variables */
+ static const char *numbers[] = { "zero", "one", "two", "three",
+ "four", "five", "six", "seven",
+ "eight", "nine", "ten", "eleven",
+ "twelve"
+ };
+ if (num_results <= 12)
+ Replaceall(returns_text, "$num_values", numbers[num_results]);
+ else {
+ String *num_results_str = NewStringf("%d", num_results);
+ Replaceall(returns_text, "$num_values", num_results_str);
+ Delete(num_results_str);
+ }
+ Replaceall(returns_text, "$values", returns);
+ Printf(doc_body, "\n%s", returns_text);
+ write_doc(proc_name, signature, doc_body);
+ Delete(returns_text);
+ }
+
+ Delete(proc_name);
+ Delete(outarg);
+ Delete(cleanup);
+ Delete(signature);
+ Delete(method_signature);
+ Delete(primitive_args);
+ Delete(doc_body);
+ Delete(returns_argout);
+ Delete(returns);
+ Delete(tmp);
+ Delete(scheme_arg_names);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * variableWrapper()
+ *
+ * Create a link to a C variable.
+ * This creates a single function PREFIX_var_VARNAME().
+ * This function takes a single optional argument. If supplied, it means
+ * we are setting this variable to some value. If omitted, it means we are
+ * simply evaluating this variable. Either way, we return the variables
+ * value.
+ * ------------------------------------------------------------ */
+
+ virtual int variableWrapper(Node *n) {
+
+ char *name = GetChar(n, "name");
+ char *iname = GetChar(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+
+ String *proc_name;
+ Wrapper *f;
+ String *tm;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ f = NewWrapper();
+ // evaluation function names
+
+ String *var_name = Swig_name_wrapper(iname);
+
+ // Build the name for scheme.
+ proc_name = NewString(iname);
+ Replaceall(proc_name, "_", "-");
+ Setattr(n, "wrap:name", proc_name);
+
+ if (1 || (SwigType_type(t) != T_USER) || (is_a_pointer(t))) {
+
+ Printf(f->def, "static SCM\n%s(SCM s_0)\n{\n", var_name);
+
+ /* Define the scheme name in C. This define is used by several Guile
+ macros. */
+ Printv(f->def, "#define FUNC_NAME \"", proc_name, "\"", NIL);
+
+ Wrapper_add_local(f, "gswig_result", "SCM gswig_result");
+
+ if (!GetFlag(n, "feature:immutable")) {
+ /* Check for a setting of the variable value */
+ Printf(f->code, "if (s_0 != SCM_UNDEFINED) {\n");
+ if ((tm = Swig_typemap_lookup("varin", n, name, 0))) {
+ Replaceall(tm, "$input", "s_0");
+ /* Printv(f->code,tm,"\n",NIL); */
+ emit_action_code(n, f->code, tm);
+ } else {
+ throw_unhandled_guile_type_error(t);
+ }
+ Printf(f->code, "}\n");
+ }
+ // Now return the value of the variable (regardless
+ // of evaluating or setting)
+
+ if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
+ Replaceall(tm, "$result", "gswig_result");
+ /* Printv(f->code,tm,"\n",NIL); */
+ emit_action_code(n, f->code, tm);
+ } else {
+ throw_unhandled_guile_type_error(t);
+ }
+ Printf(f->code, "\nreturn gswig_result;\n");
+ Printf(f->code, "#undef FUNC_NAME\n");
+ Printf(f->code, "}\n");
+
+ Wrapper_print(f, f_wrappers);
+
+ // Now add symbol to the Guile interpreter
+
+ if (!emit_setters || GetFlag(n, "feature:immutable")) {
+ /* Read-only variables become a simple procedure returning the
+ value; read-write variables become a simple procedure with
+ an optional argument. */
+
+ if (!goops && GetFlag(n, "feature:constasvar")) {
+ /* need to export this function as a variable instead of a procedure */
+ if (scmstub) {
+ /* export the function in the wrapper, and (set!) it in scmstub */
+ Printf(f_init, "scm_c_define_gsubr(\"%s\", 0, %d, 0, (swig_guile_proc) %s);\n", proc_name, !GetFlag(n, "feature:immutable"), var_name);
+ Printf(scmtext, "(set! %s (%s))\n", proc_name, proc_name);
+ } else {
+ /* export the variable directly */
+ Printf(f_init, "scm_c_define(\"%s\", %s(SCM_UNDEFINED));\n", proc_name, var_name);
+ }
+
+ } else {
+ /* Export the function as normal */
+ Printf(f_init, "scm_c_define_gsubr(\"%s\", 0, %d, 0, (swig_guile_proc) %s);\n", proc_name, !GetFlag(n, "feature:immutable"), var_name);
+ }
+
+ } else {
+ /* Read/write variables become a procedure with setter. */
+ Printf(f_init, "{ SCM p = scm_c_define_gsubr(\"%s\", 0, 1, 0, (swig_guile_proc) %s);\n", proc_name, var_name);
+ Printf(f_init, "scm_c_define");
+ Printf(f_init, "(\"%s\", " "scm_make_procedure_with_setter(p, p)); }\n", proc_name);
+ }
+ Printf(exported_symbols, "\"%s\", ", proc_name);
+
+ // export wrapper into goops file
+ if (!in_class) { // only if the variable is not part of a class
+ String *class_name = SwigType_typedef_resolve_all(SwigType_base(t));
+ String *goops_name = goopsNameMapping(proc_name, "");
+ String *primitive_name = NewString("");
+ if (primRenamer)
+ Printv(primitive_name, "primitive:", NIL);
+ Printv(primitive_name, proc_name, NIL);
+ /* Simply re-export the procedure */
+ if ((!emit_setters || GetFlag(n, "feature:immutable"))
+ && GetFlag(n, "feature:constasvar")) {
+ Printv(goopscode, "(define ", goops_name, " (", primitive_name, "))\n", NIL);
+ } else {
+ Printv(goopscode, "(define ", goops_name, " ", primitive_name, ")\n", NIL);
+ }
+ Printf(goopsexport, "%s ", goops_name);
+ Delete(primitive_name);
+ Delete(class_name);
+ Delete(goops_name);
+ }
+
+ if (procdoc) {
+ /* Compute documentation */
+ String *signature = NewString("");
+ String *signature2 = NULL;
+ String *doc = NewString("");
+
+ if (GetFlag(n, "feature:immutable")) {
+ Printv(signature, proc_name, NIL);
+ if (GetFlag(n, "feature:constasvar")) {
+ Printv(doc, "Is constant ", NIL);
+ } else {
+ Printv(doc, "Returns constant ", NIL);
+ }
+ if ((tm = Getattr(n, "tmap:varout:doc"))) {
+ Printv(doc, tm, NIL);
+ } else {
+ String *s = SwigType_str(t, 0);
+ Chop(s);
+ Printf(doc, "<%s>", s);
+ Delete(s);
+ }
+ } else if (emit_setters) {
+ Printv(signature, proc_name, NIL);
+ signature2 = NewString("");
+ Printv(signature2, "set! (", proc_name, ") ", NIL);
+ handle_documentation_typemap(signature2, NIL, n, "tmap:varin:arglist", "new-value");
+ Printv(doc, "Get or set the value of the C variable, \n", NIL);
+ Printv(doc, "which is of type ", NIL);
+ handle_documentation_typemap(doc, NIL, n, "tmap:varout:doc", "$1_type");
+ Printv(doc, ".");
+ } else {
+ Printv(signature, proc_name, " #:optional ", NIL);
+ if ((tm = Getattr(n, "tmap:varin:doc"))) {
+ Printv(signature, tm, NIL);
+ } else {
+ String *s = SwigType_str(t, 0);
+ Chop(s);
+ Printf(signature, "new-value <%s>", s);
+ Delete(s);
+ }
+
+ Printv(doc, "If NEW-VALUE is provided, " "set C variable to this value.\n", NIL);
+ Printv(doc, "Returns variable value ", NIL);
+ if ((tm = Getattr(n, "tmap:varout:doc"))) {
+ Printv(doc, tm, NIL);
+ } else {
+ String *s = SwigType_str(t, 0);
+ Chop(s);
+ Printf(doc, "<%s>", s);
+ Delete(s);
+ }
+ }
+ write_doc(proc_name, signature, doc, signature2);
+ Delete(signature);
+ if (signature2)
+ Delete(signature2);
+ Delete(doc);
+ }
+
+ } else {
+ Swig_warning(WARN_TYPEMAP_VAR_UNDEF, input_file, line_number, "Unsupported variable type %s (ignored).\n", SwigType_str(t, 0));
+ }
+ Delete(var_name);
+ Delete(proc_name);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constantWrapper()
+ *
+ * We create a read-only variable.
+ * ------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ char *name = GetChar(n, "name");
+ char *iname = GetChar(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ int constasvar = GetFlag(n, "feature:constasvar");
+
+
+ String *proc_name;
+ String *var_name;
+ Wrapper *f;
+ SwigType *nctype;
+ String *tm;
+
+ f = NewWrapper();
+
+ // Make a static variable;
+ var_name = NewStringf("%sconst_%s", prefix, iname);
+
+ // Strip const qualifier from type if present
+
+ nctype = NewString(type);
+ if (SwigType_isconst(nctype)) {
+ Delete(SwigType_pop(nctype));
+ }
+ // Build the name for scheme.
+ proc_name = NewString(iname);
+ Replaceall(proc_name, "_", "-");
+
+ if ((SwigType_type(nctype) == T_USER) && (!is_a_pointer(nctype))) {
+ Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value.\n");
+ Delete(var_name);
+ DelWrapper(f);
+ return SWIG_NOWRAP;
+ }
+ // See if there's a typemap
+
+ if ((tm = Swig_typemap_lookup("constant", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ Printv(f_header, tm, "\n", NIL);
+ } else {
+ // Create variable and assign it a value
+ Printf(f_header, "static %s = (%s)(%s);\n", SwigType_str(type, var_name), SwigType_str(type, 0), value);
+ }
+ {
+ /* Hack alert: will cleanup later -- Dave */
+ Node *nn = NewHash();
+ Setfile(nn, Getfile(n));
+ Setline(nn, Getline(n));
+ Setattr(nn, "name", var_name);
+ Setattr(nn, "sym:name", iname);
+ Setattr(nn, "type", nctype);
+ SetFlag(nn, "feature:immutable");
+ if (constasvar) {
+ SetFlag(nn, "feature:constasvar");
+ }
+ variableWrapper(nn);
+ Delete(nn);
+ }
+ Delete(var_name);
+ Delete(nctype);
+ Delete(proc_name);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classDeclaration()
+ * ------------------------------------------------------------ */
+ virtual int classDeclaration(Node *n) {
+ String *class_name = NewStringf("<%s>", Getattr(n, "sym:name"));
+ Setattr(n, "guile:goopsclassname", class_name);
+ return Language::classDeclaration(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classHandler()
+ * ------------------------------------------------------------ */
+ virtual int classHandler(Node *n) {
+ /* Create new strings for building up a wrapper function */
+ have_constructor = 0;
+
+ class_name = NewString("");
+ short_class_name = NewString("");
+ Printv(class_name, "<", Getattr(n, "sym:name"), ">", NIL);
+ Printv(short_class_name, Getattr(n, "sym:name"), NIL);
+ Replaceall(class_name, "_", "-");
+ Replaceall(short_class_name, "_", "-");
+
+ if (!addSymbol(class_name, n))
+ return SWIG_ERROR;
+
+ /* Handle inheritance */
+ String *base_class = NewString("<");
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator i = First(baselist);
+ while (i.item) {
+ Printv(base_class, Getattr(i.item, "sym:name"), NIL);
+ i = Next(i);
+ if (i.item) {
+ Printf(base_class, "> <");
+ }
+ }
+ }
+ Printf(base_class, ">");
+ Replaceall(base_class, "_", "-");
+
+ Printv(goopscode, "(define-class ", class_name, " ", NIL);
+ Printf(goopsexport, "%s ", class_name);
+
+ if (Len(base_class) > 2) {
+ Printv(goopscode, "(", base_class, ")\n", NIL);
+ } else {
+ Printv(goopscode, "(<swig>)\n", NIL);
+ }
+ SwigType *ct = NewStringf("p.%s", Getattr(n, "name"));
+ swigtype_ptr = SwigType_manglestr(ct);
+
+ String *mangled_classname = Swig_name_mangle(Getattr(n, "sym:name"));
+ /* Export clientdata structure */
+ Printf(f_runtime, "static swig_guile_clientdata _swig_guile_clientdata%s = { NULL, SCM_EOL };\n", mangled_classname);
+
+ Printv(f_init, "SWIG_TypeClientData(SWIGTYPE", swigtype_ptr, ", (void *) &_swig_guile_clientdata", mangled_classname, ");\n", NIL);
+ SwigType_remember(ct);
+ Delete(ct);
+
+ /* Emit all of the members */
+ goops_class_methods = NewString("");
+
+ in_class = 1;
+ Language::classHandler(n);
+ in_class = 0;
+
+ Printv(goopscode, " #:metaclass <swig-metaclass>\n", NIL);
+
+ if (have_constructor)
+ Printv(goopscode, " #:new-function ", primRenamer ? "primitive:" : "", "new-", short_class_name, "\n", NIL);
+
+ Printf(goopscode, ")\n%s\n", goops_class_methods);
+ Delete(goops_class_methods);
+ goops_class_methods = 0;
+
+
+ /* export class initialization function */
+ if (goops) {
+ /* export the wrapper function */
+ String *funcName = NewString(mangled_classname);
+ Printf(funcName, "_swig_guile_setgoopsclass");
+ String *guileFuncName = NewString(funcName);
+ Replaceall(guileFuncName, "_", "-");
+
+ Printv(f_wrappers, "static SCM ", funcName, "(SCM cl) \n", NIL);
+ Printf(f_wrappers, "#define FUNC_NAME %s\n{\n", guileFuncName);
+ Printv(f_wrappers, " ((swig_guile_clientdata *)(SWIGTYPE", swigtype_ptr, "->clientdata))->goops_class = cl;\n", NIL);
+ Printf(f_wrappers, " return SCM_UNSPECIFIED;\n");
+ Printf(f_wrappers, "}\n#undef FUNC_NAME\n\n");
+
+ Printf(f_init, "scm_c_define_gsubr(\"%s\", 1, 0, 0, (swig_guile_proc) %s);\n", guileFuncName, funcName);
+ Printf(exported_symbols, "\"%s\", ", guileFuncName);
+
+ /* export the call to the wrapper function */
+ Printf(goopscode, "(%s%s %s)\n\n", primRenamer ? "primitive:" : "", guileFuncName, class_name);
+
+ Delete(guileFuncName);
+ Delete(funcName);
+ }
+
+ Delete(mangled_classname);
+
+ Delete(swigtype_ptr);
+ swigtype_ptr = 0;
+
+ Delete(class_name);
+ Delete(short_class_name);
+ class_name = 0;
+ short_class_name = 0;
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * memberfunctionHandler()
+ * ------------------------------------------------------------ */
+ int memberfunctionHandler(Node *n) {
+ String *iname = Getattr(n, "sym:name");
+ String *proc = NewString(iname);
+ Replaceall(proc, "_", "-");
+
+ memberfunction_name = goopsNameMapping(proc, short_class_name);
+ Language::memberfunctionHandler(n);
+ Delete(memberfunction_name);
+ memberfunction_name = NULL;
+ Delete(proc);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * membervariableHandler()
+ * ------------------------------------------------------------ */
+ int membervariableHandler(Node *n) {
+ String *iname = Getattr(n, "sym:name");
+
+ if (emit_setters) {
+ struct_member = 1;
+ Printf(f_init, "{\n");
+ }
+
+ Language::membervariableHandler(n);
+
+ if (emit_setters) {
+ Printf(f_init, "}\n");
+ struct_member = 0;
+ }
+
+ String *proc = NewString(iname);
+ Replaceall(proc, "_", "-");
+ String *goops_name = goopsNameMapping(proc, short_class_name);
+
+ /* The slot name is never qualified with the class,
+ even if useclassprefix is true. */
+ Printv(goopscode, " (", proc, " #:allocation #:virtual", NIL);
+ /* GOOPS (at least in Guile 1.6.3) only accepts closures, not
+ primitive procedures for slot-ref and slot-set. */
+ Printv(goopscode, "\n #:slot-ref (lambda (obj) (", primRenamer ? "primitive:" : "", short_class_name, "-", proc, "-get", " obj))", NIL);
+ if (!GetFlag(n, "feature:immutable")) {
+ Printv(goopscode, "\n #:slot-set! (lambda (obj value) (", primRenamer ? "primitive:" : "", short_class_name, "-", proc, "-set", " obj value))", NIL);
+ } else {
+ Printf(goopscode, "\n #:slot-set! (lambda (obj value) (error \"Immutable slot\"))");
+ }
+ if (emit_slot_accessors) {
+ if (GetFlag(n, "feature:immutable")) {
+ Printv(goopscode, "\n #:getter ", goops_name, NIL);
+ } else {
+ Printv(goopscode, "\n #:accessor ", goops_name, NIL);
+ }
+ Printf(goopsexport, "%s ", goops_name);
+ }
+ Printv(goopscode, ")\n", NIL);
+ Delete(proc);
+ Delete(goops_name);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constructorHandler()
+ * ------------------------------------------------------------ */
+ int constructorHandler(Node *n) {
+ Language::constructorHandler(n);
+ have_constructor = 1;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * destructorHandler()
+ * ------------------------------------------------------------ */
+ virtual int destructorHandler(Node *n) {
+ exporting_destructor = true;
+ Language::destructorHandler(n);
+ exporting_destructor = false;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * pragmaDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int pragmaDirective(Node *n) {
+ if (!ImportMode) {
+ String *lang = Getattr(n, "lang");
+ String *cmd = Getattr(n, "name");
+ String *value = Getattr(n, "value");
+
+# define store_pragma(PRAGMANAME) \
+ if (Strcmp(cmd, #PRAGMANAME) == 0) { \
+ if (PRAGMANAME) Delete(PRAGMANAME); \
+ PRAGMANAME = value ? NewString(value) : NULL; \
+ }
+
+ if (Strcmp(lang, "guile") == 0) {
+ store_pragma(beforereturn)
+ store_pragma(return_nothing_doc)
+ store_pragma(return_one_doc)
+ store_pragma(return_multi_doc);
+# undef store_pragma
+ }
+ }
+ return Language::pragmaDirective(n);
+ }
+
+
+ /* ------------------------------------------------------------
+ * goopsNameMapping()
+ * Maps the identifier from C++ to the GOOPS based * on command
+ * line parameters and such.
+ * If class_name = "" that means the mapping is for a function or
+ * variable not attached to any class.
+ * ------------------------------------------------------------ */
+ String *goopsNameMapping(String *name, const_String_or_char_ptr class_name) {
+ String *n = NewString("");
+
+ if (Strcmp(class_name, "") == 0) {
+ // not part of a class, so no class name to prefix
+ if (goopsprefix) {
+ Printf(n, "%s%s", goopsprefix, name);
+ } else {
+ Printf(n, "%s", name);
+ }
+ } else {
+ if (useclassprefix) {
+ Printf(n, "%s-%s", class_name, name);
+ } else {
+ if (goopsprefix) {
+ Printf(n, "%s%s", goopsprefix, name);
+ } else {
+ Printf(n, "%s", name);
+ }
+ }
+ }
+ return n;
+ }
+
+
+ /* ------------------------------------------------------------
+ * validIdentifier()
+ * ------------------------------------------------------------ */
+
+ virtual int validIdentifier(String *s) {
+ char *c = Char(s);
+ /* Check whether we have an R5RS identifier. Guile supports a
+ superset of R5RS identifiers, but it's probably a bad idea to use
+ those. */
+ /* <identifier> --> <initial> <subsequent>* | <peculiar identifier> */
+ /* <initial> --> <letter> | <special initial> */
+ if (!(isalpha(*c) || (*c == '!') || (*c == '$') || (*c == '%')
+ || (*c == '&') || (*c == '*') || (*c == '/') || (*c == ':')
+ || (*c == '<') || (*c == '=') || (*c == '>') || (*c == '?')
+ || (*c == '^') || (*c == '_') || (*c == '~'))) {
+ /* <peculiar identifier> --> + | - | ... */
+ if ((strcmp(c, "+") == 0)
+ || strcmp(c, "-") == 0 || strcmp(c, "...") == 0)
+ return 1;
+ else
+ return 0;
+ }
+ /* <subsequent> --> <initial> | <digit> | <special subsequent> */
+ while (*c) {
+ if (!(isalnum(*c) || (*c == '!') || (*c == '$') || (*c == '%')
+ || (*c == '&') || (*c == '*') || (*c == '/') || (*c == ':')
+ || (*c == '<') || (*c == '=') || (*c == '>') || (*c == '?')
+ || (*c == '^') || (*c == '_') || (*c == '~') || (*c == '+')
+ || (*c == '-') || (*c == '.') || (*c == '@')))
+ return 0;
+ c++;
+ }
+ return 1;
+ }
+
+ String *runtimeCode() {
+ String *s;
+ s = Swig_include_sys("guile_scm_run.swg");
+ if (!s) {
+ Printf(stderr, "*** Unable to open 'guile_scm_run.swg");
+ s = NewString("");
+ }
+ return s;
+ }
+
+ String *defaultExternalRuntimeFilename() {
+ return NewString("swigguilerun.h");
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * swig_guile() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_guile() {
+ return new GUILE();
+}
+extern "C" Language *swig_guile(void) {
+ return new_swig_guile();
+}
diff --git a/contrib/tools/swig/Source/Modules/interface.cxx b/contrib/tools/swig/Source/Modules/interface.cxx
new file mode 100644
index 0000000000..83a5e5f8a1
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/interface.cxx
@@ -0,0 +1,210 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * interface.cxx
+ *
+ * This module contains support for the interface feature.
+ * This feature is used in language modules where the target language does not
+ * naturally support C++ style multiple inheritance, but does support inheritance
+ * from multiple interfaces.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+static bool interface_feature_enabled = false;
+
+/* -----------------------------------------------------------------------------
+ * collect_interface_methods()
+ *
+ * Create a list of all the methods from the base classes of class n that are
+ * marked as an interface. The resulting list is thus the list of methods that
+ * need to be implemented in order for n to be non-abstract.
+ * ----------------------------------------------------------------------------- */
+
+static List *collect_interface_methods(Node *n) {
+ List *methods = NewList();
+ if (List *bases = Getattr(n, "interface:bases")) {
+ for (Iterator base = First(bases); base.item; base = Next(base)) {
+ Node *cls = base.item;
+ if (cls == n)
+ continue;
+ for (Node *child = firstChild(cls); child; child = nextSibling(child)) {
+ if (Cmp(nodeType(child), "cdecl") == 0) {
+ if (GetFlag(child, "feature:ignore") || Getattr(child, "interface:owner"))
+ continue; // skip methods propagated to bases
+ if (!checkAttribute(child, "kind", "function"))
+ continue;
+ if (checkAttribute(child, "storage", "static"))
+ continue; // accept virtual methods, non-virtual methods too... mmm??. Warn that the interface class has something that is not a virtual method?
+ Node *nn = copyNode(child);
+ Setattr(nn, "interface:owner", cls);
+ ParmList *parms = CopyParmList(Getattr(child, "parms"));
+ Setattr(nn, "parms", parms);
+ Delete(parms);
+ ParmList *throw_parm_list = Getattr(child, "throws");
+ if (throw_parm_list)
+ Setattr(nn, "throws", CopyParmList(throw_parm_list));
+ Append(methods, nn);
+ }
+ }
+ }
+ }
+ return methods;
+}
+
+/* -----------------------------------------------------------------------------
+ * collect_interface_bases
+ * ----------------------------------------------------------------------------- */
+
+static void collect_interface_bases(List *bases, Node *n) {
+ if (GetFlag(n, "feature:interface")) {
+ String *name = Getattr(n, "interface:name");
+ if (!Getattr(bases, name))
+ Append(bases, n);
+ }
+
+ if (List *baselist = Getattr(n, "bases")) {
+ for (Iterator base = First(baselist); base.item; base = Next(base)) {
+ if (!GetFlag(base.item, "feature:ignore")) {
+ if (GetFlag(base.item, "feature:interface"))
+ collect_interface_bases(bases, base.item);
+ }
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * collect_interface_base_classes()
+ *
+ * Create a hash containing all the classes up the inheritance hierarchy
+ * marked with feature:interface (including this class n).
+ * Stops going up the inheritance chain as soon as a class is found without
+ * feature:interface.
+ * The idea is to find all the base interfaces that a class must implement.
+ * ----------------------------------------------------------------------------- */
+
+static void collect_interface_base_classes(Node *n) {
+ if (GetFlag(n, "feature:interface")) {
+ // check all bases are also interfaces
+ if (List *baselist = Getattr(n, "bases")) {
+ for (Iterator base = First(baselist); base.item; base = Next(base)) {
+ if (!GetFlag(base.item, "feature:ignore")) {
+ if (!GetFlag(base.item, "feature:interface")) {
+ Swig_error(Getfile(n), Getline(n), "Base class '%s' of '%s' is not similarly marked as an interface.\n", SwigType_namestr(Getattr(base.item, "name")), SwigType_namestr(Getattr(n, "name")));
+ Exit(EXIT_FAILURE);
+ }
+ }
+ }
+ }
+ }
+
+ List *interface_bases = NewList();
+ collect_interface_bases(interface_bases, n);
+ if (Len(interface_bases) == 0)
+ Delete(interface_bases);
+ else
+ Setattr(n, "interface:bases", interface_bases);
+}
+
+/* -----------------------------------------------------------------------------
+ * process_interface_name()
+ * ----------------------------------------------------------------------------- */
+
+static void process_interface_name(Node *n) {
+ if (GetFlag(n, "feature:interface")) {
+ String *interface_name = Getattr(n, "feature:interface:name");
+ if (!Len(interface_name)) {
+ Swig_error(Getfile(n), Getline(n), "The interface feature for '%s' is missing the name attribute.\n", SwigType_namestr(Getattr(n, "name")));
+ Exit(EXIT_FAILURE);
+ }
+ if (Strchr(interface_name, '%')) {
+ String *name = NewStringf(interface_name, Getattr(n, "sym:name"));
+ Setattr(n, "interface:name", name);
+ } else {
+ Setattr(n, "interface:name", interface_name);
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_interface_propagate_methods()
+ *
+ * Find all the base classes marked as an interface (with feature:interface) for
+ * class node n. For each of these, add all of its methods as methods of n so that
+ * n is not abstract. If class n is also marked as an interface, it will remain
+ * abstract and not have any methods added.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_interface_propagate_methods(Node *n) {
+ if (interface_feature_enabled) {
+ process_interface_name(n);
+ collect_interface_base_classes(n);
+ List *methods = collect_interface_methods(n);
+ bool is_interface = GetFlag(n, "feature:interface") ? true : false;
+ for (Iterator mi = First(methods); mi.item; mi = Next(mi)) {
+ if (!is_interface && GetFlag(mi.item, "abstract"))
+ continue;
+ String *this_decl = Getattr(mi.item, "decl");
+ String *this_decl_resolved = SwigType_typedef_resolve_all(this_decl);
+ bool identically_overloaded_method = false; // true when a base class' method is implemented in n
+ if (SwigType_isfunction(this_decl_resolved)) {
+ String *name = Getattr(mi.item, "name");
+ for (Node *child = firstChild(n); child; child = nextSibling(child)) {
+ if (Getattr(child, "interface:owner"))
+ break; // at the end of the list are newly appended methods
+ if (Cmp(nodeType(child), "cdecl") == 0) {
+ if (checkAttribute(child, "name", name)) {
+ String *decl = SwigType_typedef_resolve_all(Getattr(child, "decl"));
+ identically_overloaded_method = Strcmp(decl, this_decl_resolved) == 0;
+ Delete(decl);
+ if (identically_overloaded_method)
+ break;
+ }
+ }
+ }
+ }
+ Delete(this_decl_resolved);
+ if (!identically_overloaded_method) {
+ // Add method copied from base class to this derived class
+ Node *cn = mi.item;
+ Delattr(cn, "sym:overname");
+ String *prefix = Getattr(n, "name");
+ String *name = Getattr(cn, "name");
+ String *decl = Getattr(cn, "decl");
+ String *oldname = Getattr(cn, "sym:name");
+
+ String *symname = Swig_name_make(cn, prefix, name, decl, oldname);
+ if (Strcmp(symname, "$ignore") != 0) {
+ Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab"));
+ Node *on = Swig_symbol_add(symname, cn);
+ (void)on;
+ assert(on == cn);
+
+ // Features from the copied base class method are already present, now add in features specific to the added method in the derived class
+ Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn);
+ Swig_symbol_setscope(oldscope);
+ appendChild(n, cn);
+ }
+ } else {
+ Delete(mi.item);
+ }
+ }
+ Delete(methods);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_interface_feature_enable()
+ *
+ * Turn on interface feature support
+ * ----------------------------------------------------------------------------- */
+
+void Swig_interface_feature_enable() {
+ interface_feature_enabled = true;
+}
diff --git a/contrib/tools/swig/Source/Modules/java.cxx b/contrib/tools/swig/Source/Modules/java.cxx
new file mode 100644
index 0000000000..773945af20
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/java.cxx
@@ -0,0 +1,4979 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * java.cxx
+ *
+ * Java language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+#include <limits.h> // for INT_MAX
+#include <ctype.h>
+#include "javadoc.h"
+
+/* Hash type used for upcalls from C/C++ */
+typedef DOH UpcallData;
+
+class JAVA:public Language {
+ static const char *usage;
+ const String *empty_string;
+ const String *public_string;
+ const String *protected_string;
+
+ Hash *swig_types_hash;
+ File *f_begin;
+ File *f_runtime;
+ File *f_runtime_h;
+ File *f_header;
+ File *f_wrappers;
+ File *f_init;
+ File *f_directors;
+ File *f_directors_h;
+ List *filenames_list;
+
+ bool proxy_flag; // Flag for generating proxy classes
+ bool nopgcpp_flag; // Flag for suppressing the premature garbage collection prevention parameter
+ bool native_function_flag; // Flag for when wrapping a native function
+ bool enum_constant_flag; // Flag for when wrapping an enum or constant
+ bool static_flag; // Flag for when wrapping a static functions or member variables
+ bool variable_wrapper_flag; // Flag for when wrapping a nonstatic member variable
+ bool wrapping_member_flag; // Flag for when wrapping a member variable/enum/const
+ bool global_variable_flag; // Flag for when wrapping a global variable
+ bool old_variable_names; // Flag for old style variable names in the intermediary class
+ bool member_func_flag; // flag set when wrapping a member function
+ bool doxygen; //flag for converting found doxygen to javadoc
+ bool comment_creation_chatter; //flag for getting information about where comments were created in java.cxx
+
+ String *imclass_name; // intermediary class name
+ String *module_class_name; // module class name
+ String *constants_interface_name; // constants interface name
+ String *imclass_class_code; // intermediary class code
+ String *proxy_class_def;
+ String *proxy_class_code;
+ String *interface_class_code; // if %feature("interface") was declared for a class, here goes the interface declaration
+ String *module_class_code;
+ String *proxy_class_name; // proxy class name
+ String *full_proxy_class_name;// fully qualified proxy class name when using nspace feature, otherwise same as proxy_class_name
+ String *full_imclass_name; // fully qualified intermediary class name when using nspace feature, otherwise same as imclass_name
+ String *variable_name; //Name of a variable being wrapped
+ String *proxy_class_constants_code;
+ String *module_class_constants_code;
+ String *enum_code;
+ String *package; // Optional package name
+ String *jnipackage; // Package name used in the JNI code
+ String *package_path; // Package name used internally by JNI (slashes)
+ String *imclass_imports; //intermediary class imports from %pragma
+ String *module_imports; //module imports from %pragma
+ String *imclass_baseclass; //inheritance for intermediary class class from %pragma
+ String *imclass_package; //package in which to generate the intermediary class
+ String *module_baseclass; //inheritance for module class from %pragma
+ String *imclass_interfaces; //interfaces for intermediary class class from %pragma
+ String *module_interfaces; //interfaces for module class from %pragma
+ String *imclass_class_modifiers; //class modifiers for intermediary class overridden by %pragma
+ String *module_class_modifiers; //class modifiers for module class overridden by %pragma
+ String *upcasts_code; //C++ casts for inheritance hierarchies C++ code
+ String *imclass_cppcasts_code; //C++ casts up inheritance hierarchies intermediary class code
+ String *imclass_directors; // Intermediate class director code
+ String *destructor_call; //C++ destructor call if any
+ String *destructor_throws_clause; //C++ destructor throws clause if any
+
+ // Director method stuff:
+ List *dmethods_seq;
+ Hash *dmethods_table;
+ int n_dmethods;
+ int n_directors;
+ int first_class_dmethod;
+ int curr_class_dmethod;
+ int nesting_depth;
+
+ enum EnumFeature { SimpleEnum, TypeunsafeEnum, TypesafeEnum, ProperEnum };
+
+public:
+
+ /* -----------------------------------------------------------------------------
+ * JAVA()
+ * ----------------------------------------------------------------------------- */
+
+ JAVA():empty_string(NewString("")),
+ public_string(NewString("public")),
+ protected_string(NewString("protected")),
+ swig_types_hash(NULL),
+ f_begin(NULL),
+ f_runtime(NULL),
+ f_runtime_h(NULL),
+ f_header(NULL),
+ f_wrappers(NULL),
+ f_init(NULL),
+ f_directors(NULL),
+ f_directors_h(NULL),
+ filenames_list(NULL),
+ proxy_flag(true),
+ nopgcpp_flag(false),
+ native_function_flag(false),
+ enum_constant_flag(false),
+ static_flag(false),
+ variable_wrapper_flag(false),
+ wrapping_member_flag(false),
+ global_variable_flag(false),
+ old_variable_names(false),
+ member_func_flag(false),
+ doxygen(false),
+ comment_creation_chatter(false),
+ imclass_name(NULL),
+ module_class_name(NULL),
+ constants_interface_name(NULL),
+ imclass_class_code(NULL),
+ proxy_class_def(NULL),
+ proxy_class_code(NULL),
+ interface_class_code(NULL),
+ module_class_code(NULL),
+ proxy_class_name(NULL),
+ full_proxy_class_name(NULL),
+ full_imclass_name(NULL),
+ variable_name(NULL),
+ proxy_class_constants_code(NULL),
+ module_class_constants_code(NULL),
+ enum_code(NULL),
+ package(NULL),
+ jnipackage(NULL),
+ package_path(NULL),
+ imclass_imports(NULL),
+ module_imports(NULL),
+ imclass_baseclass(NULL),
+ imclass_package(NULL),
+ module_baseclass(NULL),
+ imclass_interfaces(NULL),
+ module_interfaces(NULL),
+ imclass_class_modifiers(NULL),
+ module_class_modifiers(NULL),
+ upcasts_code(NULL),
+ imclass_cppcasts_code(NULL),
+ imclass_directors(NULL),
+ destructor_call(NULL),
+ destructor_throws_clause(NULL),
+ dmethods_seq(NULL),
+ dmethods_table(NULL),
+ n_dmethods(0),
+ n_directors(0),
+ first_class_dmethod(0),
+ curr_class_dmethod(0),
+ nesting_depth(0){
+ /* for now, multiple inheritance in directors is disabled, this
+ should be easy to implement though */
+ director_multiple_inheritance = 0;
+ director_language = 1;
+ }
+
+ ~JAVA() {
+ delete doxygenTranslator;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * constructIntermediateClassName()
+ *
+ * Construct the fully qualified name of the intermediate class and set
+ * the full_imclass_name attribute accordingly.
+ * ----------------------------------------------------------------------------- */
+ void constructIntermediateClassName(Node *n) {
+ String *nspace = Getattr(n, "sym:nspace");
+
+ if (imclass_package && package)
+ full_imclass_name = NewStringf("%s.%s.%s", package, imclass_package, imclass_name);
+ else if (package && nspace)
+ full_imclass_name = NewStringf("%s.%s", package, imclass_name);
+ else if (imclass_package)
+ full_imclass_name = NewStringf("%s.%s", imclass_package, imclass_name);
+ else
+ full_imclass_name = NewStringf("%s", imclass_name);
+
+ if (nspace && !package) {
+ String *name = Getattr(n, "name") ? Getattr(n, "name") : NewString("<unnamed>");
+ Swig_warning(WARN_JAVA_NSPACE_WITHOUT_PACKAGE, Getfile(n), Getline(n),
+ "The nspace feature is used on '%s' without -package. "
+ "The generated code may not compile as Java does not support types declared in a named package accessing types declared in an unnamed package.\n", SwigType_namestr(name));
+ }
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getProxyName()
+ *
+ * Test to see if a type corresponds to something wrapped with a proxy class.
+ * Return NULL if not otherwise the proxy class name, fully qualified with
+ * package name if the nspace feature is used, unless jnidescriptor is true as
+ * the package name is handled differently (unfortunately for legacy reasons).
+ * ----------------------------------------------------------------------------- */
+
+ String *getProxyName(SwigType *t, bool jnidescriptor = false) {
+ String *proxyname = NULL;
+ if (proxy_flag) {
+ Node *n = classLookup(t);
+ if (n) {
+ proxyname = Getattr(n, "proxyname");
+ if (!proxyname || jnidescriptor) {
+ String *nspace = Getattr(n, "sym:nspace");
+ String *symname = Copy(Getattr(n, "sym:name"));
+ if (symname && !GetFlag(n, "feature:flatnested")) {
+ for (Node *outer_class = Getattr(n, "nested:outer"); outer_class; outer_class = Getattr(outer_class, "nested:outer")) {
+ if (String* name = Getattr(outer_class, "sym:name")) {
+ Push(symname, jnidescriptor ? "$" : ".");
+ Push(symname, name);
+ }
+ else
+ return NULL;
+ }
+ }
+ if (nspace) {
+ if (package && !jnidescriptor)
+ proxyname = NewStringf("%s.%s.%s", package, nspace, symname);
+ else
+ proxyname = NewStringf("%s.%s", nspace, symname);
+ } else {
+ proxyname = Copy(symname);
+ }
+ if (!jnidescriptor) {
+ Setattr(n, "proxyname", proxyname); // Cache it
+ Delete(proxyname);
+ }
+ Delete(symname);
+ }
+ }
+ }
+ return proxyname;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * makeValidJniName()
+ * ----------------------------------------------------------------------------- */
+
+ String *makeValidJniName(const String *name) {
+ String *valid_jni_name = NewString(name);
+ Replaceall(valid_jni_name, "_", "_1");
+ return valid_jni_name;
+ }
+
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+
+ SWIG_library_directory("java");
+
+ int doxygen_translator_flags = 0;
+
+ // Look for certain command line options
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-package") == 0) {
+ if (argv[i + 1]) {
+ package = NewString("");
+ Printf(package, argv[i + 1]);
+ if (Len(package) == 0) {
+ Delete(package);
+ package = 0;
+ }
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if ((strcmp(argv[i], "-shadow") == 0) || ((strcmp(argv[i], "-proxy") == 0))) {
+ Printf(stderr, "Deprecated command line option: %s. Proxy classes are now generated by default.\n", argv[i]);
+ Swig_mark_arg(i);
+ proxy_flag = true;
+ } else if ((strcmp(argv[i], "-doxygen") == 0)) {
+ Swig_mark_arg(i);
+ doxygen = true;
+ scan_doxygen_comments = true;
+ } else if ((strcmp(argv[i], "-debug-doxygen-translator") == 0)) {
+ Swig_mark_arg(i);
+ doxygen_translator_flags |= DoxygenTranslator::debug_translator;
+ } else if ((strcmp(argv[i], "-debug-doxygen-parser") == 0)) {
+ Swig_mark_arg(i);
+ doxygen_translator_flags |= DoxygenTranslator::debug_parser;
+ } else if ((strcmp(argv[i], "-noproxy") == 0)) {
+ Swig_mark_arg(i);
+ proxy_flag = false;
+ } else if (strcmp(argv[i], "-nopgcpp") == 0) {
+ Swig_mark_arg(i);
+ nopgcpp_flag = true;
+ } else if (strcmp(argv[i], "-oldvarnames") == 0) {
+ Swig_mark_arg(i);
+ old_variable_names = true;
+ } else if (strcmp(argv[i], "-jnic") == 0) {
+ Swig_mark_arg(i);
+ Printf(stderr, "Deprecated command line option: -jnic. C JNI calling convention now used when -c++ not specified.\n");
+ } else if (strcmp(argv[i], "-nofinalize") == 0) {
+ Swig_mark_arg(i);
+ Printf(stderr, "Deprecated command line option: -nofinalize. Use the new javafinalize typemap instead.\n");
+ } else if (strcmp(argv[i], "-jnicpp") == 0) {
+ Swig_mark_arg(i);
+ Printf(stderr, "Deprecated command line option: -jnicpp. C++ JNI calling convention now used when -c++ specified.\n");
+ } else if (strcmp(argv[i], "-help") == 0) {
+ Printf(stdout, "%s\n", usage);
+ }
+ }
+ }
+
+ if (doxygen)
+ doxygenTranslator = new JavaDocConverter(doxygen_translator_flags);
+
+ // Add a symbol to the parser for conditional compilation
+ Preprocessor_define("SWIGJAVA 1", 0);
+
+ // Add typemap definitions
+ SWIG_typemap_lang("java");
+ SWIG_config_file("java.swg");
+
+ allow_overloading();
+ Swig_interface_feature_enable();
+ }
+
+ /* ---------------------------------------------------------------------
+ * top()
+ * --------------------------------------------------------------------- */
+
+ virtual int top(Node *n) {
+
+ // Get any options set in the module directive
+ Node *optionsnode = Getattr(Getattr(n, "module"), "options");
+
+ if (optionsnode) {
+ if (Getattr(optionsnode, "jniclassname"))
+ imclass_name = Copy(Getattr(optionsnode, "jniclassname"));
+ /* check if directors are enabled for this module. note: this
+ * is a "master" switch, without which no director code will be
+ * emitted. %feature("director") statements are also required
+ * to enable directors for individual classes or methods.
+ *
+ * use %module(directors="1") modulename at the start of the
+ * interface file to enable director generation.
+ */
+ if (Getattr(optionsnode, "directors")) {
+ allow_directors();
+ }
+ if (Getattr(optionsnode, "dirprot")) {
+ allow_dirprot();
+ }
+ allow_allprotected(GetFlag(optionsnode, "allprotected"));
+ }
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+ String *outfile_h = Getattr(n, "outfile_h");
+
+ if (!outfile) {
+ Printf(stderr, "Unable to determine outfile\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+
+ if (directorsEnabled()) {
+ if (!outfile_h) {
+ Printf(stderr, "Unable to determine outfile_h\n");
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
+ if (!f_runtime_h) {
+ FileErrorDisplay(outfile_h);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+ f_directors_h = NewString("");
+ f_directors = NewString("");
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+
+ swig_types_hash = NewHash();
+ filenames_list = NewList();
+
+ // Make the intermediary class and module class names. The intermediary class name can be set in the module directive.
+ if (!imclass_name) {
+ imclass_name = NewStringf("%sJNI", Getattr(n, "name"));
+ module_class_name = Copy(Getattr(n, "name"));
+ } else {
+ // Rename the module name if it is the same as intermediary class name - a backwards compatibility solution
+ if (Cmp(imclass_name, Getattr(n, "name")) == 0)
+ module_class_name = NewStringf("%sModule", Getattr(n, "name"));
+ else
+ module_class_name = Copy(Getattr(n, "name"));
+ }
+ constants_interface_name = NewStringf("%sConstants", module_class_name);
+
+ // module class and intermediary classes are always created
+ if (!addSymbol(imclass_name, n))
+ return SWIG_ERROR;
+ if (!addSymbol(module_class_name, n))
+ return SWIG_ERROR;
+
+ imclass_class_code = NewString("");
+ proxy_class_def = NewString("");
+ proxy_class_code = NewString("");
+ module_class_constants_code = NewString("");
+ imclass_baseclass = NewString("");
+ imclass_package = NULL;
+ imclass_interfaces = NewString("");
+ imclass_class_modifiers = NewString("");
+ module_class_code = NewString("");
+ module_baseclass = NewString("");
+ module_interfaces = NewString("");
+ module_imports = NewString("");
+ module_class_modifiers = NewString("");
+ imclass_imports = NewString("");
+ imclass_cppcasts_code = NewString("");
+ imclass_directors = NewString("");
+ upcasts_code = NewString("");
+ dmethods_seq = NewList();
+ dmethods_table = NewHash();
+ n_dmethods = 0;
+ n_directors = 0;
+ jnipackage = NewString("");
+ package_path = NewString("");
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "JAVA");
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+
+ /* Emit initial director header and director code: */
+ Swig_banner(f_directors_h);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module_class_name);
+ Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module_class_name);
+
+ Printf(f_directors, "\n\n");
+ Printf(f_directors, "/* ---------------------------------------------------\n");
+ Printf(f_directors, " * C++ director class methods\n");
+ Printf(f_directors, " * --------------------------------------------------- */\n\n");
+ if (outfile_h) {
+ String *filename = Swig_file_filename(outfile_h);
+ Printf(f_directors, "#include \"%s\"\n\n", filename);
+ Delete(filename);
+ }
+ }
+
+ Printf(f_runtime, "\n");
+
+ String *wrapper_name = NewString("");
+
+ if (package) {
+ String *jniname = makeValidJniName(package);
+ Printv(jnipackage, jniname, NIL);
+ Delete(jniname);
+ Replaceall(jnipackage, ".", "_");
+ Append(jnipackage, "_");
+ Printv(package_path, package, NIL);
+ Replaceall(package_path, ".", "/");
+ }
+ String *jniname = makeValidJniName(imclass_name);
+ Printf(wrapper_name, "Java_%s%s_%%f", jnipackage, jniname);
+ Delete(jniname);
+
+ Swig_name_register("wrapper", Char(wrapper_name));
+ if (old_variable_names) {
+ Swig_name_register("set", "set_%n%v");
+ Swig_name_register("get", "get_%n%v");
+ }
+
+ Delete(wrapper_name);
+
+ Printf(f_wrappers, "\n#ifdef __cplusplus\n");
+ Printf(f_wrappers, "extern \"C\" {\n");
+ Printf(f_wrappers, "#endif\n\n");
+
+ /* Emit code */
+ Language::top(n);
+
+ if (directorsEnabled()) {
+ // Insert director runtime into the f_runtime file (make it occur before %header section)
+ Swig_insert_file("director_common.swg", f_runtime);
+ Swig_insert_file("director.swg", f_runtime);
+ }
+ // Generate the intermediary class
+ {
+ String *filen = NewStringf("%s%s.java", outputDirectory(imclass_package), imclass_name);
+ File *f_im = NewFile(filen, "w", SWIG_output_files());
+ if (!f_im) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ // Start writing out the intermediary class file
+ emitBanner(f_im);
+
+ if (imclass_package && package)
+ Printf(f_im, "package %s.%s;", package, imclass_package);
+ else if (imclass_package)
+ Printf(f_im, "package %s;", imclass_package);
+ else if (package)
+ Printf(f_im, "package %s;\n", package);
+
+ if (imclass_imports)
+ Printf(f_im, "%s\n", imclass_imports);
+
+ if (Len(imclass_class_modifiers) > 0)
+ Printf(f_im, "%s ", imclass_class_modifiers);
+ Printf(f_im, "%s ", imclass_name);
+
+ if (imclass_baseclass && *Char(imclass_baseclass))
+ Printf(f_im, "extends %s ", imclass_baseclass);
+ if (Len(imclass_interfaces) > 0)
+ Printv(f_im, "implements ", imclass_interfaces, " ", NIL);
+ Printf(f_im, "{\n");
+
+ // Add the intermediary class methods
+ Replaceall(imclass_class_code, "$module", module_class_name);
+ Replaceall(imclass_class_code, "$imclassname", imclass_name);
+ Printv(f_im, imclass_class_code, NIL);
+ Printv(f_im, imclass_cppcasts_code, NIL);
+ if (Len(imclass_directors) > 0)
+ Printv(f_im, "\n", imclass_directors, NIL);
+
+ if (n_dmethods > 0) {
+ Putc('\n', f_im);
+ Printf(f_im, " private final static native void swig_module_init();\n");
+ Printf(f_im, " static {\n");
+ Printf(f_im, " swig_module_init();\n");
+ Printf(f_im, " }\n");
+ }
+ // Finish off the class
+ Printf(f_im, "}\n");
+ Delete(f_im);
+ }
+
+ // Generate the Java module class
+ {
+ String *filen = NewStringf("%s%s.java", SWIG_output_directory(), module_class_name);
+ File *f_module = NewFile(filen, "w", SWIG_output_files());
+ if (!f_module) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ // Start writing out the module class file
+ emitBanner(f_module);
+
+ if (package)
+ Printf(f_module, "package %s;\n", package);
+
+ if (module_imports)
+ Printf(f_module, "%s\n", module_imports);
+
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, 0);
+ if (comment_creation_chatter)
+ Printf(f_module, "/* This was generated from top() */\n");
+ Printv(f_module, Char(doxygen_comments), NIL);
+ Delete(doxygen_comments);
+ }
+ if (Len(module_class_modifiers) > 0)
+ Printf(f_module, "%s ", module_class_modifiers);
+ Printf(f_module, "%s ", module_class_name);
+
+ if (module_baseclass && *Char(module_baseclass))
+ Printf(f_module, "extends %s ", module_baseclass);
+ if (Len(module_interfaces) > 0) {
+ if (Len(module_class_constants_code) != 0)
+ Printv(f_module, "implements ", constants_interface_name, ", ", module_interfaces, " ", NIL);
+ else
+ Printv(f_module, "implements ", module_interfaces, " ", NIL);
+ } else {
+ if (Len(module_class_constants_code) != 0)
+ Printv(f_module, "implements ", constants_interface_name, " ", NIL);
+ }
+ Printf(f_module, "{\n");
+
+ Replaceall(module_class_code, "$module", module_class_name);
+ Replaceall(module_class_constants_code, "$module", module_class_name);
+
+ Replaceall(module_class_code, "$imclassname", imclass_name);
+ Replaceall(module_class_constants_code, "$imclassname", imclass_name);
+
+ // Add the wrapper methods
+ Printv(f_module, module_class_code, NIL);
+
+ // Finish off the class
+ Printf(f_module, "}\n");
+ Delete(f_module);
+ }
+
+ // Generate the Java constants interface
+ if (Len(module_class_constants_code) != 0) {
+ String *filen = NewStringf("%s%s.java", SWIG_output_directory(), constants_interface_name);
+ File *f_module = NewFile(filen, "w", SWIG_output_files());
+ if (!f_module) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ // Start writing out the Java constants interface file
+ emitBanner(f_module);
+
+ if (package)
+ Printf(f_module, "package %s;\n", package);
+
+ if (module_imports)
+ Printf(f_module, "%s\n", module_imports);
+
+ Printf(f_module, "public interface %s {\n", constants_interface_name);
+
+ // Write out all the global constants
+ Printv(f_module, module_class_constants_code, NIL);
+
+ // Finish off the Java interface
+ Printf(f_module, "}\n");
+ Delete(f_module);
+ }
+
+ if (upcasts_code)
+ Printv(f_wrappers, upcasts_code, NIL);
+
+ emitDirectorUpcalls();
+
+ Printf(f_wrappers, "#ifdef __cplusplus\n");
+ Printf(f_wrappers, "}\n");
+ Printf(f_wrappers, "#endif\n");
+
+ // Output a Java type wrapper class for each SWIG type
+ for (Iterator swig_type = First(swig_types_hash); swig_type.key; swig_type = Next(swig_type)) {
+ emitTypeWrapperClass(swig_type.key, swig_type.item);
+ }
+
+ // Check for overwriting file problems on filesystems that are case insensitive
+ Iterator it1;
+ Iterator it2;
+ for (it1 = First(filenames_list); it1.item; it1 = Next(it1)) {
+ String *item1_lower = Swig_string_lower(it1.item);
+ for (it2 = Next(it1); it2.item; it2 = Next(it2)) {
+ String *item2_lower = Swig_string_lower(it2.item);
+ if (it1.item && it2.item) {
+ if (Strcmp(item1_lower, item2_lower) == 0) {
+ Swig_warning(WARN_LANG_PORTABILITY_FILENAME, input_file, line_number,
+ "Portability warning: File %s will be overwritten by %s on case insensitive filesystems such as "
+ "Windows' FAT32 and NTFS unless the class/module name is renamed\n", it1.item, it2.item);
+ }
+ }
+ Delete(item2_lower);
+ }
+ Delete(item1_lower);
+ }
+
+ Delete(swig_types_hash);
+ swig_types_hash = NULL;
+ Delete(filenames_list);
+ filenames_list = NULL;
+ Delete(imclass_name);
+ imclass_name = NULL;
+ Delete(imclass_class_code);
+ imclass_class_code = NULL;
+ Delete(proxy_class_def);
+ proxy_class_def = NULL;
+ Delete(proxy_class_code);
+ proxy_class_code = NULL;
+ Delete(module_class_constants_code);
+ module_class_constants_code = NULL;
+ Delete(imclass_baseclass);
+ imclass_baseclass = NULL;
+ Delete(imclass_package);
+ imclass_package = NULL;
+ Delete(imclass_interfaces);
+ imclass_interfaces = NULL;
+ Delete(imclass_class_modifiers);
+ imclass_class_modifiers = NULL;
+ Delete(module_class_name);
+ module_class_name = NULL;
+ Delete(constants_interface_name);
+ constants_interface_name = NULL;
+ Delete(module_class_code);
+ module_class_code = NULL;
+ Delete(module_baseclass);
+ module_baseclass = NULL;
+ Delete(module_interfaces);
+ module_interfaces = NULL;
+ Delete(module_imports);
+ module_imports = NULL;
+ Delete(module_class_modifiers);
+ module_class_modifiers = NULL;
+ Delete(imclass_imports);
+ imclass_imports = NULL;
+ Delete(imclass_cppcasts_code);
+ imclass_cppcasts_code = NULL;
+ Delete(imclass_directors);
+ imclass_directors = NULL;
+ Delete(upcasts_code);
+ upcasts_code = NULL;
+ Delete(package);
+ package = NULL;
+ Delete(jnipackage);
+ jnipackage = NULL;
+ Delete(package_path);
+ package_path = NULL;
+ Delete(dmethods_seq);
+ dmethods_seq = NULL;
+ Delete(dmethods_table);
+ dmethods_table = NULL;
+ n_dmethods = 0;
+
+ /* Close all of the files */
+ Dump(f_header, f_runtime);
+
+ if (directorsEnabled()) {
+ Dump(f_directors, f_runtime);
+ Dump(f_directors_h, f_runtime_h);
+
+ Printf(f_runtime_h, "\n");
+ Printf(f_runtime_h, "#endif\n");
+
+ Delete(f_runtime_h);
+ f_runtime_h = NULL;
+ Delete(f_directors);
+ f_directors = NULL;
+ Delete(f_directors_h);
+ f_directors_h = NULL;
+ }
+
+ Dump(f_wrappers, f_runtime);
+ Wrapper_pretty_print(f_init, f_runtime);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Dump(f_runtime, f_begin);
+ Delete(f_runtime);
+ Delete(f_begin);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * emitBanner()
+ * ----------------------------------------------------------------------------- */
+
+ void emitBanner(File *f) {
+ Printf(f, "/* ----------------------------------------------------------------------------\n");
+ Swig_banner_target_lang(f, " *");
+ Printf(f, " * ----------------------------------------------------------------------------- */\n\n");
+ }
+
+ /*-----------------------------------------------------------------------
+ * Add new director upcall signature
+ *----------------------------------------------------------------------*/
+
+ UpcallData *addUpcallMethod(String *imclass_method, String *class_method, String *imclass_desc, String *class_desc, String *decl) {
+ String *key = NewStringf("%s|%s", imclass_method, decl);
+
+ ++curr_class_dmethod;
+
+ String *imclass_methodidx = NewStringf("%d", n_dmethods);
+ String *class_methodidx = NewStringf("%d", n_dmethods - first_class_dmethod);
+ n_dmethods++;
+
+ Hash *new_udata = NewHash();
+ Append(dmethods_seq, new_udata);
+ Setattr(dmethods_table, key, new_udata);
+
+ Setattr(new_udata, "method", Copy(class_method));
+ Setattr(new_udata, "fdesc", Copy(class_desc));
+ Setattr(new_udata, "imclass_method", Copy(imclass_method));
+ Setattr(new_udata, "imclass_fdesc", Copy(imclass_desc));
+ Setattr(new_udata, "imclass_methodidx", imclass_methodidx);
+ Setattr(new_udata, "class_methodidx", class_methodidx);
+ Setattr(new_udata, "decl", Copy(decl));
+
+ Delete(key);
+ return new_udata;
+ }
+
+ /*-----------------------------------------------------------------------
+ * Get director upcall signature
+ *----------------------------------------------------------------------*/
+
+ UpcallData *getUpcallMethodData(String *director_class, String *decl) {
+ String *key = NewStringf("%s|%s", director_class, decl);
+ UpcallData *udata = Getattr(dmethods_table, key);
+
+ Delete(key);
+ return udata;
+ }
+
+ /* ----------------------------------------------------------------------
+ * nativeWrapper()
+ * ---------------------------------------------------------------------- */
+
+ virtual int nativeWrapper(Node *n) {
+ String *wrapname = Getattr(n, "wrap:name");
+
+ if (!addSymbol(wrapname, n, imclass_name))
+ return SWIG_ERROR;
+
+ if (Getattr(n, "type")) {
+ Swig_save("nativeWrapper", n, "name", NIL);
+ Setattr(n, "name", wrapname);
+ native_function_flag = true;
+ functionWrapper(n);
+ Swig_restore(n);
+ native_function_flag = false;
+ } else {
+ Swig_error(input_file, line_number, "No return type for %%native method %s.\n", Getattr(n, "wrap:name"));
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * functionWrapper()
+ * ---------------------------------------------------------------------- */
+
+ virtual int functionWrapper(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ Parm *p;
+ int i;
+ String *c_return_type = NewString("");
+ String *im_return_type = NewString("");
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+ String *body = NewString("");
+ int num_arguments = 0;
+ int gencomma = 0;
+ bool is_void_return;
+ String *overloaded_name = getOverloadedName(n);
+ String *nondir_args = NewString("");
+ bool is_destructor = (Cmp(Getattr(n, "nodeType"), "destructor") == 0);
+
+ if (!Getattr(n, "sym:overloaded")) {
+ if (!addSymbol(symname, n, imclass_name))
+ return SWIG_ERROR;
+ }
+
+ /*
+ The rest of this function deals with generating the intermediary class wrapper function (that wraps
+ a c/c++ function) and generating the JNI c code. Each Java wrapper function has a
+ matching JNI c function call.
+ */
+
+ // A new wrapper function object
+ Wrapper *f = NewWrapper();
+
+ // Make a wrapper name for this function
+ String *jniname = makeValidJniName(overloaded_name);
+ String *wname = Swig_name_wrapper(jniname);
+
+ Delete(jniname);
+
+ /* Attach the non-standard typemaps to the parameter list. */
+ Swig_typemap_attach_parms("jni", l, f);
+ Swig_typemap_attach_parms("jtype", l, f);
+ Swig_typemap_attach_parms("jstype", l, f);
+
+ /* Get return types */
+ if ((tm = Swig_typemap_lookup("jni", n, "", 0))) {
+ Printf(c_return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JNI_UNDEF, input_file, line_number, "No jni typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if ((tm = Swig_typemap_lookup("jtype", n, "", 0))) {
+ Printf(im_return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JTYPE_UNDEF, input_file, line_number, "No jtype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ is_void_return = (Cmp(c_return_type, "void") == 0);
+ if (!is_void_return)
+ Wrapper_add_localv(f, "jresult", c_return_type, "jresult = 0", NIL);
+
+ Printv(f->def, "SWIGEXPORT ", c_return_type, " JNICALL ", wname, "(JNIEnv *jenv, jclass jcls", NIL);
+
+ // Usually these function parameters are unused - The code below ensures
+ // that compilers do not issue such a warning if configured to do so.
+
+ Printv(f->code, " (void)jenv;\n", NIL);
+ Printv(f->code, " (void)jcls;\n", NIL);
+
+ // Emit all of the local variables for holding arguments.
+ emit_parameter_variables(l, f);
+
+ /* Attach the standard typemaps */
+ emit_attach_parmmaps(l, f);
+
+ // Parameter overloading
+ Setattr(n, "wrap:parms", l);
+ Setattr(n, "wrap:name", wname);
+
+ // Wrappers not wanted for some methods where the parameters cannot be overloaded in Java
+ if (Getattr(n, "sym:overloaded")) {
+ // Emit warnings for the few cases that can't be overloaded in Java and give up on generating wrapper
+ Swig_overload_check(n);
+ if (Getattr(n, "overload:ignore")) {
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+ }
+
+ Printf(imclass_class_code, " public final static native %s %s(", im_return_type, overloaded_name);
+
+ num_arguments = emit_num_arguments(l);
+
+ // Now walk the function parameter list and generate code to get arguments
+ for (i = 0, p = l; i < num_arguments; i++) {
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+ String *im_param_type = NewString("");
+ String *c_param_type = NewString("");
+ String *arg = NewString("");
+
+ Printf(arg, "j%s", ln);
+
+ /* Get the JNI C types of the parameter */
+ if ((tm = Getattr(p, "tmap:jni"))) {
+ Printv(c_param_type, tm, NIL);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JNI_UNDEF, input_file, line_number, "No jni typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Get the intermediary class parameter types of the parameter */
+ if ((tm = Getattr(p, "tmap:jtype"))) {
+ Printv(im_param_type, tm, NIL);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JTYPE_UNDEF, input_file, line_number, "No jtype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to intermediary class method */
+ if (gencomma)
+ Printf(imclass_class_code, ", ");
+ Printf(imclass_class_code, "%s %s", im_param_type, arg);
+
+ // Add parameter to C function
+ Printv(f->def, ", ", c_param_type, " ", arg, NIL);
+
+ ++gencomma;
+
+ // Premature garbage collection prevention parameter
+ if (!is_destructor) {
+ String *pgc_parameter = prematureGarbageCollectionPreventionParameter(pt, p);
+ if (pgc_parameter) {
+ Printf(imclass_class_code, ", %s %s_", pgc_parameter, arg);
+ Printf(f->def, ", jobject %s_", arg);
+ Printf(f->code, " (void)%s_;\n", arg);
+ }
+ }
+ // Get typemap for this argument
+ if ((tm = Getattr(p, "tmap:in"))) {
+ addThrows(n, "tmap:in", p);
+ Replaceall(tm, "$arg", arg); /* deprecated? */
+ Replaceall(tm, "$input", arg);
+ Setattr(p, "emit:input", arg);
+
+ Printf(nondir_args, "%s\n", tm);
+
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ p = nextSibling(p);
+ }
+
+ Delete(im_param_type);
+ Delete(c_param_type);
+ Delete(arg);
+ }
+
+ Printv(f->code, nondir_args, NIL);
+ Delete(nondir_args);
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ addThrows(n, "tmap:check", p);
+ Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ addThrows(n, "tmap:freearg", p);
+ Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert argument output code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ addThrows(n, "tmap:argout", p);
+ Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
+ Replaceall(tm, "$result", "jresult");
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Get any Java exception classes in the throws typemap
+ ParmList *throw_parm_list = NULL;
+ if ((throw_parm_list = Getattr(n, "catchlist"))) {
+ Swig_typemap_attach_parms("throws", throw_parm_list, f);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ addThrows(n, "tmap:throws", p);
+ }
+ }
+ }
+
+ // Now write code to make the function call
+ if (!native_function_flag) {
+
+ Swig_director_emit_dynamic_cast(n, f);
+ String *actioncode = emit_action(n);
+
+ // Handle exception classes specified in the "except" feature's "throws" attribute
+ addThrows(n, "feature:except", n);
+
+ /* Return value if necessary */
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ addThrows(n, "tmap:out", n);
+ Replaceall(tm, "$result", "jresult");
+
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "1");
+ else
+ Replaceall(tm, "$owner", "0");
+
+ Printf(f->code, "%s", tm);
+ if (Len(tm))
+ Printf(f->code, "\n");
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(t, 0), Getattr(n, "name"));
+ }
+ emit_return_variable(n, t, f);
+ }
+
+ /* Output argument output code */
+ Printv(f->code, outarg, NIL);
+
+ /* Output cleanup code */
+ Printv(f->code, cleanup, NIL);
+
+ /* Look to see if there is any newfree cleanup code */
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ addThrows(n, "tmap:newfree", n);
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if (!native_function_flag) {
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ addThrows(n, "tmap:ret", n);
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ /* Finish C function and intermediary class function definitions */
+ Printf(imclass_class_code, ")");
+ generateThrowsClause(n, imclass_class_code);
+ Printf(imclass_class_code, ";\n");
+
+ Printf(f->def, ") {");
+
+ if (!is_void_return)
+ Printv(f->code, " return jresult;\n", NIL);
+ Printf(f->code, "}\n");
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", symname);
+
+ /* Contract macro modification */
+ Replaceall(f->code, "SWIG_contract_assert(", "SWIG_contract_assert($null, ");
+
+ if (!is_void_return)
+ Replaceall(f->code, "$null", "0");
+ else
+ Replaceall(f->code, "$null", "");
+
+ /* Dump the function out */
+ if (!native_function_flag)
+ Wrapper_print(f, f_wrappers);
+
+ if (!(proxy_flag && is_wrapping_class()) && !enum_constant_flag) {
+ moduleClassFunctionHandler(n);
+ }
+
+ /*
+ * Generate the proxy class getters/setters for public member variables.
+ * Not for enums and constants.
+ */
+ if (proxy_flag && wrapping_member_flag && !enum_constant_flag) {
+ // Capitalize the first letter in the variable to create a JavaBean type getter/setter function name
+ bool getter_flag = Cmp(symname, Swig_name_set(getNSpace(), Swig_name_member(0, getClassPrefix(), variable_name))) != 0;
+
+ String *getter_setter_name = NewString("");
+ if (!getter_flag)
+ Printf(getter_setter_name, "set");
+ else
+ Printf(getter_setter_name, "get");
+ Putc(toupper((int) *Char(variable_name)), getter_setter_name);
+ Printf(getter_setter_name, "%s", Char(variable_name) + 1);
+
+ Setattr(n, "proxyfuncname", getter_setter_name);
+ Setattr(n, "imfuncname", symname);
+
+ proxyClassFunctionHandler(n);
+ Delete(getter_setter_name);
+ }
+
+ Delete(c_return_type);
+ Delete(im_return_type);
+ Delete(cleanup);
+ Delete(outarg);
+ Delete(body);
+ Delete(overloaded_name);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * variableWrapper()
+ * ----------------------------------------------------------------------- */
+
+ virtual int variableWrapper(Node *n) {
+ variable_wrapper_flag = true;
+ Language::variableWrapper(n); /* Default to functions */
+ variable_wrapper_flag = false;
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * globalvariableHandler()
+ * ------------------------------------------------------------------------ */
+
+ virtual int globalvariableHandler(Node *n) {
+
+ variable_name = Getattr(n, "sym:name");
+ global_variable_flag = true;
+ int ret = Language::globalvariableHandler(n);
+ global_variable_flag = false;
+ return ret;
+ }
+
+ String *getCurrentScopeName(String *nspace) {
+ String *scope = 0;
+ if (nspace || getCurrentClass()) {
+ scope = NewString("");
+ if (nspace)
+ Printf(scope, "%s", nspace);
+ if (Node* cls = getCurrentClass()) {
+ if (Node *outer = Getattr(cls, "nested:outer")) {
+ String *outerClassesPrefix = Copy(Getattr(outer, "sym:name"));
+ for (outer = Getattr(outer, "nested:outer"); outer != 0; outer = Getattr(outer, "nested:outer")) {
+ Push(outerClassesPrefix, ".");
+ Push(outerClassesPrefix, Getattr(outer, "sym:name"));
+ }
+ Printv(scope, nspace ? "." : "", outerClassesPrefix, ".", proxy_class_name, NIL);
+ Delete(outerClassesPrefix);
+ } else
+ Printv(scope, nspace ? "." : "", proxy_class_name, NIL);
+ }
+ }
+ return scope;
+ }
+
+ /* ----------------------------------------------------------------------
+ * enumDeclaration()
+ *
+ * C/C++ enums can be mapped in one of 4 ways, depending on the java:enum feature specified:
+ * 1) Simple enums - simple constant within the proxy class or module class
+ * 2) Typeunsafe enums - simple constant in a Java class (class named after the c++ enum name)
+ * 3) Typesafe enum - typesafe enum pattern (class named after the c++ enum name)
+ * 4) Proper enums - proper Java enum
+ * Anonymous enums always default to 1)
+ * ---------------------------------------------------------------------- */
+
+ virtual int enumDeclaration(Node *n) {
+
+ if (!ImportMode) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ String *nspace = Getattr(n, "sym:nspace"); // NSpace/getNSpace() only works during Language::enumDeclaration call
+ if (proxy_flag && !is_wrapping_class()) {
+ // Global enums / enums in a namespace
+ assert(!full_imclass_name);
+ constructIntermediateClassName(n);
+ }
+
+ enum_code = NewString("");
+ String *symname = Getattr(n, "sym:name");
+ String *constants_code = (proxy_flag && is_wrapping_class())? proxy_class_constants_code : module_class_constants_code;
+ EnumFeature enum_feature = decodeEnumFeature(n);
+ String *typemap_lookup_type = Getattr(n, "name");
+
+ if ((enum_feature != SimpleEnum) && symname && typemap_lookup_type) {
+ // Wrap (non-anonymous) C/C++ enum within a typesafe, typeunsafe or proper Java enum
+
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, 0);
+ if (comment_creation_chatter)
+ Printf(enum_code, "/* This was generated from enumDeclaration() */\n");
+ Printv(enum_code, Char(doxygen_comments), NIL);
+ Delete(doxygen_comments);
+ }
+
+ String *scope = getCurrentScopeName(nspace);
+ if (!addSymbol(symname, n, scope))
+ return SWIG_ERROR;
+
+ // Pure Java baseclass and interfaces
+ const String *pure_baseclass = typemapLookup(n, "javabase", typemap_lookup_type, WARN_NONE);
+ const String *pure_interfaces = typemapLookup(n, "javainterfaces", typemap_lookup_type, WARN_NONE);
+
+ // Emit the enum
+ Printv(enum_code, typemapLookup(n, "javaclassmodifiers", typemap_lookup_type, WARN_JAVA_TYPEMAP_CLASSMOD_UNDEF), // Class modifiers (enum modifiers really)
+ " ", symname, *Char(pure_baseclass) ? // Bases
+ " extends " : "", pure_baseclass, *Char(pure_interfaces) ? // Interfaces
+ " implements " : "", pure_interfaces, " {\n", NIL);
+ if (proxy_flag && is_wrapping_class())
+ Replaceall(enum_code, "$static ", "static ");
+ else
+ Replaceall(enum_code, "$static ", "");
+ Delete(scope);
+ } else {
+ if (symname && !Getattr(n, "unnamedinstance"))
+ Printf(constants_code, " // %s \n", symname);
+ // Translate and write javadoc comment for the enum itself if flagged
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, " ");
+ if (comment_creation_chatter)
+ Printf(constants_code, "/* This was generated from enumDeclaration() */\n");
+ Printf(constants_code, Char(doxygen_comments));
+ Printf(constants_code, "\n");
+ Delete(doxygen_comments);
+ }
+ }
+
+ // Emit each enum item
+ Language::enumDeclaration(n);
+
+ if ((enum_feature != SimpleEnum) && symname && typemap_lookup_type) {
+ // Wrap (non-anonymous) C/C++ enum within a typesafe, typeunsafe or proper Java enum
+ // Finish the enum declaration
+ // Typemaps are used to generate the enum definition in a similar manner to proxy classes.
+ Printv(enum_code, (enum_feature == ProperEnum) ? ";\n" : "", typemapLookup(n, "javabody", typemap_lookup_type, WARN_JAVA_TYPEMAP_JAVABODY_UNDEF), // main body of class
+ typemapLookup(n, "javacode", typemap_lookup_type, WARN_NONE), // extra Java code
+ "}", NIL);
+
+ Replaceall(enum_code, "$javaclassname", symname);
+
+ // Substitute $enumvalues - intended usage is for typesafe enums
+ if (Getattr(n, "enumvalues"))
+ Replaceall(enum_code, "$enumvalues", Getattr(n, "enumvalues"));
+ else
+ Replaceall(enum_code, "$enumvalues", "");
+
+ if (proxy_flag && is_wrapping_class()) {
+ // Enums defined within the C++ class are defined within the proxy class
+
+ // Add extra indentation
+ Replaceall(enum_code, "\n", "\n ");
+ Replaceall(enum_code, " \n", "\n");
+ Printv(proxy_class_constants_code, " ", enum_code, "\n\n", NIL);
+ } else {
+ // Global enums are defined in their own file
+ String *output_directory = outputDirectory(nspace);
+ String *filen = NewStringf("%s%s.java", output_directory, symname);
+ File *f_enum = NewFile(filen, "w", SWIG_output_files());
+ if (!f_enum) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ // Start writing out the enum file
+ emitBanner(f_enum);
+
+ if (package || nspace) {
+ Printf(f_enum, "package ");
+ if (package)
+ Printv(f_enum, package, nspace ? "." : "", NIL);
+ if (nspace)
+ Printv(f_enum, nspace, NIL);
+ Printf(f_enum, ";\n");
+ }
+
+ Printv(f_enum, typemapLookup(n, "javaimports", typemap_lookup_type, WARN_NONE), // Import statements
+ "\n", enum_code, "\n", NIL);
+
+ Printf(f_enum, "\n");
+ Delete(f_enum);
+ Delete(output_directory);
+ }
+ } else {
+ // Wrap C++ enum with simple constant
+ Printf(enum_code, "\n");
+ if (proxy_flag && is_wrapping_class())
+ Printv(proxy_class_constants_code, enum_code, NIL);
+ else
+ Printv(module_class_constants_code, enum_code, NIL);
+ }
+
+ Delete(enum_code);
+ enum_code = NULL;
+
+ if (proxy_flag && !is_wrapping_class()) {
+ Delete(full_imclass_name);
+ full_imclass_name = 0;
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * enumvalueDeclaration()
+ * ---------------------------------------------------------------------- */
+
+ virtual int enumvalueDeclaration(Node *n) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ Swig_require("enumvalueDeclaration", n, "*name", "?value", NIL);
+ String *symname = Getattr(n, "sym:name");
+ String *value = Getattr(n, "value");
+ String *name = Getattr(n, "name");
+ Node *parent = parentNode(n);
+ int unnamedinstance = GetFlag(parent, "unnamedinstance");
+ String *parent_name = Getattr(parent, "name");
+ String *nspace = getNSpace();
+ String *newsymname = 0;
+ String *tmpValue;
+
+ // Strange hack from parent method
+ if (value)
+ tmpValue = NewString(value);
+ else
+ tmpValue = NewString(name);
+ // Note that this is used in enumValue() amongst other places
+ Setattr(n, "value", tmpValue);
+
+ // Deal with enum values that are not int
+ int swigtype = SwigType_type(Getattr(n, "type"));
+ if (swigtype == T_BOOL) {
+ const char *val = Equal(Getattr(n, "enumvalue"), "true") ? "1" : "0";
+ Setattr(n, "enumvalue", val);
+ } else if (swigtype == T_CHAR) {
+ String *val = NewStringf("'%(escape)s'", Getattr(n, "enumvalue"));
+ Setattr(n, "enumvalue", val);
+ Delete(val);
+ }
+
+ {
+ EnumFeature enum_feature = decodeEnumFeature(parent);
+
+ if ((enum_feature == SimpleEnum) && GetFlag(parent, "scopedenum")) {
+ newsymname = Swig_name_member(0, Getattr(parent, "sym:name"), symname);
+ symname = newsymname;
+ }
+
+ // Add to language symbol table
+ String *scope = 0;
+ if (unnamedinstance || !parent_name || enum_feature == SimpleEnum) {
+ String *enumClassPrefix = getEnumClassPrefix();
+ if (enumClassPrefix) {
+ scope = NewString("");
+ if (nspace)
+ Printf(scope, "%s.", nspace);
+ Printf(scope, "%s", enumClassPrefix);
+ } else {
+ scope = Copy(constants_interface_name);
+ }
+ } else {
+ scope = getCurrentScopeName(nspace);
+ if (!scope)
+ scope = Copy(Getattr(parent, "sym:name"));
+ else
+ Printf(scope, ".%s", Getattr(parent, "sym:name"));
+ }
+ if (!addSymbol(symname, n, scope))
+ return SWIG_ERROR;
+
+ if ((enum_feature == ProperEnum) && parent_name && !unnamedinstance) {
+ if (!GetFlag(n, "firstenumitem"))
+ Printf(enum_code, ",\n");
+ }
+
+ // Translate and write javadoc comment if flagged
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, " ");
+ if (comment_creation_chatter)
+ Printf(enum_code, "/* This was generated from enumvalueDeclaration() */\n");
+ Printv(enum_code, Char(doxygen_comments), NIL);
+ Delete(doxygen_comments);
+ }
+
+ if ((enum_feature == ProperEnum) && parent_name && !unnamedinstance) {
+ // Wrap (non-anonymous) C/C++ enum with a proper Java enum
+ // Emit the enum item.
+ Printf(enum_code, " %s", symname);
+ if (Getattr(n, "enumvalue")) {
+ String *value = enumValue(n);
+ Printf(enum_code, "(%s)", value);
+ Delete(value);
+ }
+ } else {
+ // Wrap C/C++ enums with constant integers or use the typesafe enum pattern
+ SwigType *typemap_lookup_type = parent_name ? parent_name : NewString("enum ");
+ Setattr(n, "type", typemap_lookup_type);
+ const String *tm = typemapLookup(n, "jstype", typemap_lookup_type, WARN_JAVA_TYPEMAP_JSTYPE_UNDEF);
+
+ String *return_type = Copy(tm);
+ substituteClassname(typemap_lookup_type, return_type);
+ const String *methodmods = Getattr(n, "feature:java:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+
+ if ((enum_feature == TypesafeEnum) && parent_name && !unnamedinstance) {
+ // Wrap (non-anonymous) enum using the typesafe enum pattern
+ if (Getattr(n, "enumvalue")) {
+ String *value = enumValue(n);
+ Printf(enum_code, " %s final static %s %s = new %s(\"%s\", %s);\n", methodmods, return_type, symname, return_type, symname, value);
+ Delete(value);
+ } else {
+ Printf(enum_code, " %s final static %s %s = new %s(\"%s\");\n", methodmods, return_type, symname, return_type, symname);
+ }
+ } else {
+ // Simple integer constants
+ // Note these are always generated for anonymous enums, no matter what enum_feature is specified
+ // Code generated is the same for SimpleEnum and TypeunsafeEnum -> the class it is generated into is determined later
+ String *value = enumValue(n);
+ Printf(enum_code, " %s final static %s %s = %s;\n", methodmods, return_type, symname, value);
+ Delete(value);
+ }
+ Delete(return_type);
+ }
+
+ // Add the enum value to the comma separated list being constructed in the enum declaration.
+ String *enumvalues = Getattr(parent, "enumvalues");
+ if (!enumvalues)
+ Setattr(parent, "enumvalues", Copy(symname));
+ else
+ Printv(enumvalues, ", ", symname, NIL);
+ Delete(scope);
+ }
+
+ Delete(newsymname);
+ Delete(tmpValue);
+ Swig_restore(n);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * constantWrapper()
+ * Used for wrapping constants - #define or %constant.
+ * Also for inline initialised const static primitive type member variables (short, int, double, enums etc).
+ * Java static final variables are generated for these.
+ * If the %javaconst(1) feature is used then the C constant value is used to initialise the Java final variable.
+ * If not, a JNI method is generated to get the C constant value for initialisation of the Java final variable.
+ * However, if the %javaconstvalue feature is used, it overrides all other ways to generate the initialisation.
+ * Also note that this method might be called for wrapping enum items (when the enum is using %javaconst(0)).
+ * ------------------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+ SwigType *valuetype = Getattr(n, "valuetype");
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ String *return_type = NewString("");
+ String *constants_code = NewString("");
+ Swig_save("constantWrapper", n, "value", NIL);
+
+ // Translate and write javadoc comment if flagged
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, " ");
+ if (comment_creation_chatter)
+ Printf(constants_code, "/* This was generated from constantWrapper() */\n");
+ Printv(constants_code, Char(doxygen_comments), NIL);
+ Delete(doxygen_comments);
+ }
+
+ bool is_enum_item = (Cmp(nodeType(n), "enumitem") == 0);
+
+ const String *itemname = (proxy_flag && wrapping_member_flag) ? variable_name : symname;
+ if (!is_enum_item) {
+ String *scope = 0;
+ if (proxy_class_name) {
+ String *nspace = getNSpace();
+ scope = NewString("");
+ if (nspace)
+ Printf(scope, "%s.", nspace);
+ Printf(scope, "%s", proxy_class_name);
+ } else {
+ scope = Copy(constants_interface_name);
+ }
+ if (!addSymbol(itemname, n, scope))
+ return SWIG_ERROR;
+ Delete(scope);
+ }
+
+ // The %javaconst feature determines how the constant value is obtained
+ int const_feature_flag = GetFlag(n, "feature:java:const");
+
+ /* Adjust the enum type for the Swig_typemap_lookup.
+ * We want the same jstype typemap for all the enum items so we use the enum type (parent node). */
+ if (is_enum_item) {
+ t = Getattr(parentNode(n), "enumtype");
+ Setattr(n, "type", t);
+ }
+
+ /* Attach the non-standard typemaps to the parameter list. */
+ Swig_typemap_attach_parms("jstype", l, NULL);
+
+ /* Get Java return types */
+ bool classname_substituted_flag = false;
+
+ if ((tm = Swig_typemap_lookup("jstype", n, "", 0))) {
+ classname_substituted_flag = substituteClassname(t, tm);
+ Printf(return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JSTYPE_UNDEF, input_file, line_number, "No jstype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ // Add the stripped quotes back in
+ String *new_value = NewString("");
+ if (SwigType_type(t) == T_STRING) {
+ Printf(new_value, "\"%s\"", Copy(Getattr(n, "value")));
+ Setattr(n, "value", new_value);
+ } else if (SwigType_type(t) == T_CHAR) {
+ Printf(new_value, "\'%s\'", Copy(Getattr(n, "value")));
+ Setattr(n, "value", new_value);
+ }
+
+ const String *methodmods = Getattr(n, "feature:java:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+
+ Printf(constants_code, " %s final static %s %s = ", methodmods, return_type, itemname);
+
+ // Check for the %javaconstvalue feature
+ String *value = Getattr(n, "feature:java:constvalue");
+
+ if (value) {
+ Printf(constants_code, "%s;\n", value);
+ } else if (!const_feature_flag) {
+ // Default enum and constant handling will work with any type of C constant and initialises the Java variable from C through a JNI call.
+
+ if (classname_substituted_flag) {
+ if (SwigType_isenum(t)) {
+ // This handles wrapping of inline initialised const enum static member variables (not when wrapping enum items - ignored later on)
+ Printf(constants_code, "%s.swigToEnum(%s.%s());\n", return_type, full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
+ } else {
+ // This handles function pointers using the %constant directive
+ Printf(constants_code, "new %s(%s.%s(), false);\n", return_type, full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
+ }
+ } else {
+ Printf(constants_code, "%s.%s();\n", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
+ }
+
+ // Each constant and enum value is wrapped with a separate JNI function call
+ SetFlag(n, "feature:immutable");
+ enum_constant_flag = true;
+ variableWrapper(n);
+ enum_constant_flag = false;
+ } else {
+ // Alternative constant handling will use the C syntax to make a true Java constant and hope that it compiles as Java code
+ if (Getattr(n, "wrappedasconstant")) {
+ if (SwigType_type(valuetype) == T_CHAR)
+ Printf(constants_code, "\'%(escape)s\';\n", Getattr(n, "staticmembervariableHandler:value"));
+ else
+ Printf(constants_code, "%s;\n", Getattr(n, "staticmembervariableHandler:value"));
+ } else {
+ Printf(constants_code, "%s;\n", Getattr(n, "value"));
+ }
+ }
+
+ // Emit the generated code to appropriate place
+ // Enums only emit the intermediate and JNI methods, so no proxy or module class wrapper methods needed
+ if (!is_enum_item) {
+ if (proxy_flag && wrapping_member_flag)
+ Printv(proxy_class_constants_code, constants_code, NIL);
+ else
+ Printv(module_class_constants_code, constants_code, NIL);
+ }
+ // Cleanup
+ Swig_restore(n);
+ Delete(new_value);
+ Delete(return_type);
+ Delete(constants_code);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * insertDirective()
+ * ----------------------------------------------------------------------------- */
+
+ virtual int insertDirective(Node *n) {
+ int ret = SWIG_OK;
+ String *code = Getattr(n, "code");
+ String *section = Getattr(n, "section");
+ Replaceall(code, "$module", module_class_name);
+ Replaceall(code, "$imclassname", imclass_name);
+
+ if (!ImportMode && (Cmp(section, "proxycode") == 0)) {
+ if (proxy_class_code) {
+ Swig_typemap_replace_embedded_typemap(code, n);
+ int offset = Len(code) > 0 && *Char(code) == '\n' ? 1 : 0;
+ Printv(proxy_class_code, Char(code) + offset, "\n", NIL);
+ }
+ } else {
+ ret = Language::insertDirective(n);
+ }
+ return ret;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * pragmaDirective()
+ *
+ * Valid Pragmas:
+ * jniclassbase - base (extends) for the intermediary class
+ * jniclasspackage - package in which to generate the intermediary class
+ * jniclassclassmodifiers - class modifiers for the intermediary class
+ * jniclasscode - text (java code) is copied verbatim to the intermediary class
+ * jniclassimports - import statements for the intermediary class
+ * jniclassinterfaces - interface (implements) for the intermediary class
+ *
+ * modulebase - base (extends) for the module class
+ * moduleclassmodifiers - class modifiers for the module class
+ * modulecode - text (java code) is copied verbatim to the module class
+ * moduleimports - import statements for the module class
+ * moduleinterfaces - interface (implements) for the module class
+ *
+ * ----------------------------------------------------------------------------- */
+
+ virtual int pragmaDirective(Node *n) {
+ if (!ImportMode) {
+ String *lang = Getattr(n, "lang");
+ String *code = Getattr(n, "name");
+ String *value = Getattr(n, "value");
+
+ if (Strcmp(lang, "java") == 0) {
+
+ String *strvalue = NewString(value);
+ Replaceall(strvalue, "\\\"", "\"");
+
+ if (Strcmp(code, "jniclassbase") == 0) {
+ Delete(imclass_baseclass);
+ imclass_baseclass = Copy(strvalue);
+ } else if (Strcmp(code, "jniclasspackage") == 0) {
+ Delete(imclass_package);
+ imclass_package = Copy(strvalue);
+ String *imclass_class_package_jniname = makeValidJniName(imclass_package);
+ Printv(jnipackage, imclass_class_package_jniname, NIL);
+ Delete(imclass_class_package_jniname);
+ Replaceall(jnipackage, NSPACE_SEPARATOR, "_");
+ Append(jnipackage, "_");
+
+ String *wrapper_name = NewString("");
+ String *imclass_class_jniname = makeValidJniName(imclass_name);
+ Printf(wrapper_name, "Java_%s%s_%%f", jnipackage, imclass_class_jniname);
+ Delete(imclass_class_jniname);
+
+ Swig_name_unregister("wrapper");
+ Swig_name_register("wrapper", Char(wrapper_name));
+
+ Delete(wrapper_name);
+ } else if (Strcmp(code, "jniclassclassmodifiers") == 0) {
+ Delete(imclass_class_modifiers);
+ imclass_class_modifiers = Copy(strvalue);
+ } else if (Strcmp(code, "jniclasscode") == 0) {
+ Printf(imclass_class_code, "%s\n", strvalue);
+ } else if (Strcmp(code, "jniclassimports") == 0) {
+ Delete(imclass_imports);
+ imclass_imports = Copy(strvalue);
+ } else if (Strcmp(code, "jniclassinterfaces") == 0) {
+ Delete(imclass_interfaces);
+ imclass_interfaces = Copy(strvalue);
+ } else if (Strcmp(code, "modulebase") == 0) {
+ Delete(module_baseclass);
+ module_baseclass = Copy(strvalue);
+ } else if (Strcmp(code, "moduleclassmodifiers") == 0) {
+ Delete(module_class_modifiers);
+ module_class_modifiers = Copy(strvalue);
+ } else if (Strcmp(code, "modulecode") == 0) {
+ Printf(module_class_code, "%s\n", strvalue);
+ } else if (Strcmp(code, "moduleimports") == 0) {
+ Delete(module_imports);
+ module_imports = Copy(strvalue);
+ } else if (Strcmp(code, "moduleinterfaces") == 0) {
+ Delete(module_interfaces);
+ module_interfaces = Copy(strvalue);
+ } else if (Strcmp(code, "moduleimport") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use the moduleimports pragma.\n");
+ } else if (Strcmp(code, "moduleinterface") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use the moduleinterfaces pragma.\n");
+ } else if (Strcmp(code, "modulemethodmodifiers") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%javamethodmodifiers.\n");
+ } else if (Strcmp(code, "allshadowimport") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javaimports).\n");
+ } else if (Strcmp(code, "allshadowcode") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javacode).\n");
+ } else if (Strcmp(code, "allshadowbase") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javabase).\n");
+ } else if (Strcmp(code, "allshadowinterface") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javainterfaces).\n");
+ } else if (Strcmp(code, "allshadowclassmodifiers") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javaclassmodifiers).\n");
+ } else if (proxy_flag) {
+ if (Strcmp(code, "shadowcode") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javacode).\n");
+ } else if (Strcmp(code, "shadowimport") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javaimports).\n");
+ } else if (Strcmp(code, "shadowbase") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javabase).\n");
+ } else if (Strcmp(code, "shadowinterface") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javainterfaces).\n");
+ } else if (Strcmp(code, "shadowclassmodifiers") == 0) {
+ Swig_error(input_file, line_number, "Deprecated pragma. Please use %%typemap(javaclassmodifiers).\n");
+ } else {
+ Swig_error(input_file, line_number, "Unrecognized pragma.\n");
+ }
+ } else {
+ Swig_error(input_file, line_number, "Unrecognized pragma.\n");
+ }
+ Delete(strvalue);
+ }
+ }
+ return Language::pragmaDirective(n);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getQualifiedInterfaceName()
+ * ----------------------------------------------------------------------------- */
+
+ String *getQualifiedInterfaceName(Node *n) {
+ String *ret = Getattr(n, "interface:qname");
+ if (!ret) {
+ String *nspace = Getattr(n, "sym:nspace");
+ String *symname = Getattr(n, "interface:name");
+ if (nspace) {
+ if (package)
+ ret = NewStringf("%s.%s.%s", package, nspace, symname);
+ else
+ ret = NewStringf("%s.%s", nspace, symname);
+ } else {
+ ret = Copy(symname);
+ }
+ Setattr(n, "interface:qname", ret);
+ }
+ return ret;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getInterfaceName()
+ * ----------------------------------------------------------------------------- */
+
+ String *getInterfaceName(SwigType *t, bool qualified) {
+ String *interface_name = NULL;
+ if (proxy_flag) {
+ Node *n = classLookup(t);
+ if (n && Getattr(n, "interface:name"))
+ interface_name = qualified ? getQualifiedInterfaceName(n) : Getattr(n, "interface:name");
+ }
+ return interface_name;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * addInterfaceNameAndUpcasts()
+ * ----------------------------------------------------------------------------- */
+
+ void addInterfaceNameAndUpcasts(SwigType *smart, String *interface_list, String *interface_upcasts, List *base_list, SwigType *c_classname) {
+ for (Iterator it = First(base_list); it.item; it = Next(it)) {
+ Node *base = it.item;
+ SwigType *c_baseclassname = Getattr(base, "name");
+ String *interface_name = Getattr(base, "interface:name");
+ if (Len(interface_list))
+ Append(interface_list, ", ");
+ Append(interface_list, interface_name);
+
+ Node *attributes = NewHash();
+ String *interface_code = Copy(typemapLookup(base, "javainterfacecode", Getattr(base, "classtypeobj"), WARN_JAVA_TYPEMAP_INTERFACECODE_UNDEF, attributes));
+ String *cptr_method_name = 0;
+ if (interface_code) {
+ Replaceall(interface_code, "$interfacename", interface_name);
+ Printv(interface_upcasts, interface_code, NIL);
+ cptr_method_name = Copy(Getattr(attributes, "tmap:javainterfacecode:cptrmethod"));
+ }
+ if (!cptr_method_name)
+ cptr_method_name = NewStringf("%s_GetInterfaceCPtr", interface_name);
+ Replaceall(cptr_method_name, ".", "_");
+ Replaceall(cptr_method_name, "$interfacename", interface_name);
+
+ String *upcast_method_name = Swig_name_member(getNSpace(), getClassPrefix(), cptr_method_name);
+ upcastsCode(smart, upcast_method_name, c_classname, c_baseclassname);
+
+ Delete(upcast_method_name);
+ Delete(cptr_method_name);
+ Delete(interface_code);
+ }
+ }
+
+ /* -----------------------------------------------------------------------------
+ * upcastsCode()
+ *
+ * Add code for C++ casting to base class
+ * ----------------------------------------------------------------------------- */
+
+ void upcastsCode(SwigType *smart, String *upcast_method_name, SwigType *c_classname, SwigType *c_baseclassname) {
+ String *jniname = makeValidJniName(upcast_method_name);
+ String *wname = Swig_name_wrapper(jniname);
+
+ Printf(imclass_cppcasts_code, " public final static native long %s(long jarg1);\n", upcast_method_name);
+
+ String *classname = SwigType_namestr(c_classname);
+ String *baseclassname = SwigType_namestr(c_baseclassname);
+ if (smart) {
+ String *smartnamestr = SwigType_namestr(smart);
+ String *bsmartnamestr = SwigType_namestr(smart);
+
+ // TODO: SwigType_typedef_resolve_all on a String instead of SwigType is incorrect for templates
+ SwigType *rclassname = SwigType_typedef_resolve_all(classname);
+ SwigType *rbaseclassname = SwigType_typedef_resolve_all(baseclassname);
+ Replaceall(bsmartnamestr, rclassname, rbaseclassname);
+
+ Printv(upcasts_code,
+ "SWIGEXPORT jlong JNICALL ", wname, "(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n",
+ " jlong baseptr = 0;\n"
+ " ", smartnamestr, " *argp1;\n"
+ " (void)jenv;\n"
+ " (void)jcls;\n"
+ " argp1 = *(", smartnamestr, " **)&jarg1;\n"
+ " *(", bsmartnamestr, " **)&baseptr = argp1 ? new ", bsmartnamestr, "(*argp1) : 0;\n"
+ " return baseptr;\n"
+ "}\n", "\n", NIL);
+
+ Delete(rbaseclassname);
+ Delete(rclassname);
+ Delete(bsmartnamestr);
+ Delete(smartnamestr);
+ } else {
+ Printv(upcasts_code,
+ "SWIGEXPORT jlong JNICALL ", wname, "(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n",
+ " jlong baseptr = 0;\n"
+ " (void)jenv;\n"
+ " (void)jcls;\n"
+ " *(", baseclassname, " **)&baseptr = *(", classname, " **)&jarg1;\n"
+ " return baseptr;\n"
+ "}\n", "\n", NIL);
+ }
+
+ Delete(baseclassname);
+ Delete(classname);
+ Delete(wname);
+ Delete(jniname);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * emitProxyClassDefAndCPPCasts()
+ * ----------------------------------------------------------------------------- */
+
+ void emitProxyClassDefAndCPPCasts(Node *n) {
+ SwigType *c_classname = Getattr(n, "name");
+ SwigType *c_baseclassname = NULL;
+ String *baseclass = NULL;
+ String *interface_list = NewStringEmpty();
+ String *interface_upcasts = NewStringEmpty();
+ SwigType *typemap_lookup_type = Getattr(n, "classtypeobj");
+ bool feature_director = Swig_directorclass(n) ? true : false;
+ bool has_outerclass = Getattr(n, "nested:outer") != 0 && !GetFlag(n, "feature:flatnested");
+ SwigType *smart = Swig_cparse_smartptr(n);
+
+ // Inheritance from pure Java classes
+ Node *attributes = NewHash();
+ const String *pure_baseclass = typemapLookup(n, "javabase", typemap_lookup_type, WARN_NONE, attributes);
+ bool purebase_replace = GetFlag(attributes, "tmap:javabase:replace") ? true : false;
+ bool purebase_notderived = GetFlag(attributes, "tmap:javabase:notderived") ? true : false;
+ Delete(attributes);
+
+ // C++ inheritance
+ if (!purebase_replace) {
+ List *baselist = Getattr(n, "bases");
+ if (baselist) {
+ Iterator base = First(baselist);
+ while (base.item) {
+ if (!(GetFlag(base.item, "feature:ignore") || GetFlag(base.item, "feature:interface"))) {
+ SwigType *baseclassname = Getattr(base.item, "name");
+ if (!c_baseclassname) {
+ String *name = getProxyName(baseclassname);
+ if (name) {
+ c_baseclassname = baseclassname;
+ baseclass = name;
+ }
+ } else {
+ /* Warn about multiple inheritance for additional base class(es) */
+ String *proxyclassname = Getattr(n, "classtypeobj");
+ Swig_warning(WARN_JAVA_MULTIPLE_INHERITANCE, Getfile(n), Getline(n),
+ "Warning for %s, base %s ignored. Multiple inheritance is not supported in Java.\n", SwigType_namestr(proxyclassname), SwigType_namestr(baseclassname));
+ }
+ }
+ base = Next(base);
+ }
+ }
+ }
+
+ List *interface_bases = Getattr(n, "interface:bases");
+ if (interface_bases)
+ addInterfaceNameAndUpcasts(smart, interface_list, interface_upcasts, interface_bases, c_classname);
+
+ bool derived = baseclass != 0;
+ if (derived && purebase_notderived)
+ pure_baseclass = empty_string;
+ const String *wanted_base = baseclass ? baseclass : pure_baseclass;
+
+ if (purebase_replace) {
+ wanted_base = pure_baseclass;
+ derived = false;
+ baseclass = NULL;
+ if (purebase_notderived)
+ Swig_error(Getfile(n), Getline(n), "The javabase typemap for proxy %s must contain just one of the 'replace' or 'notderived' attributes.\n", typemap_lookup_type);
+ } else if (Len(pure_baseclass) > 0 && Len(baseclass) > 0) {
+ Swig_warning(WARN_JAVA_MULTIPLE_INHERITANCE, Getfile(n), Getline(n),
+ "Warning for %s, base %s ignored. Multiple inheritance is not supported in Java. "
+ "Perhaps you need one of the 'replace' or 'notderived' attributes in the javabase typemap?\n", typemap_lookup_type, pure_baseclass);
+ }
+
+ // Pure Java interfaces
+ const String *pure_interfaces = typemapLookup(n, "javainterfaces", typemap_lookup_type, WARN_NONE);
+
+ if (*Char(interface_list) && *Char(pure_interfaces))
+ Append(interface_list, ", ");
+ Append(interface_list, pure_interfaces);
+ // Start writing the proxy class
+ if (!has_outerclass) // Import statements
+ Printv(proxy_class_def, typemapLookup(n, "javaimports", typemap_lookup_type, WARN_NONE),"\n", NIL);
+
+ // Translate and write javadoc comment if flagged
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, 0);
+ if (comment_creation_chatter)
+ Printf(proxy_class_def, "/* This was generated from emitProxyClassDefAndCPPCasts() */\n");
+ Printv(proxy_class_def, Char(doxygen_comments), NIL);
+ Delete(doxygen_comments);
+ }
+
+ if (has_outerclass)
+ Printv(proxy_class_def, "static ", NIL); // C++ nested classes correspond to static java classes
+ Printv(proxy_class_def, typemapLookup(n, "javaclassmodifiers", typemap_lookup_type, WARN_JAVA_TYPEMAP_CLASSMOD_UNDEF), // Class modifiers
+ " $javaclassname", // Class name and bases
+ (*Char(wanted_base)) ? " extends " : "", wanted_base, *Char(interface_list) ? // Pure Java interfaces
+ " implements " : "", interface_list, " {", derived ? typemapLookup(n, "javabody_derived", typemap_lookup_type, WARN_JAVA_TYPEMAP_JAVABODY_UNDEF) : // main body of class
+ typemapLookup(n, "javabody", typemap_lookup_type, WARN_JAVA_TYPEMAP_JAVABODY_UNDEF), // main body of class
+ NIL);
+
+ // C++ destructor is wrapped by the delete method
+ // Note that the method name is specified in a typemap attribute called methodname
+ String *destruct = NewString("");
+ const String *tm = NULL;
+ attributes = NewHash();
+ const String *destruct_methodname = NULL;
+ const String *destruct_methodmodifiers = NULL;
+ const String *destruct_parameters = NULL;
+ if (derived) {
+ tm = typemapLookup(n, "javadestruct_derived", typemap_lookup_type, WARN_NONE, attributes);
+ destruct_methodname = Getattr(attributes, "tmap:javadestruct_derived:methodname");
+ destruct_methodmodifiers = Getattr(attributes, "tmap:javadestruct_derived:methodmodifiers");
+ destruct_parameters = Getattr(attributes, "tmap:javadestruct_derived:parameters");
+ } else {
+ tm = typemapLookup(n, "javadestruct", typemap_lookup_type, WARN_NONE, attributes);
+ destruct_methodname = Getattr(attributes, "tmap:javadestruct:methodname");
+ destruct_methodmodifiers = Getattr(attributes, "tmap:javadestruct:methodmodifiers");
+ destruct_parameters = Getattr(attributes, "tmap:javadestruct:parameters");
+ }
+ if (tm && *Char(tm)) {
+ if (!destruct_methodname) {
+ Swig_error(Getfile(n), Getline(n), "No methodname attribute defined in javadestruct%s typemap for %s\n", (derived ? "_derived" : ""), proxy_class_name);
+ }
+ if (!destruct_methodmodifiers) {
+ Swig_error(Getfile(n), Getline(n), "No methodmodifiers attribute defined in javadestruct%s typemap for %s.\n", (derived ? "_derived" : ""), proxy_class_name);
+ }
+ if (!destruct_parameters)
+ destruct_parameters = empty_string;
+ }
+ // Emit the finalize and delete methods
+ if (tm) {
+ // Finalize method
+ if (*Char(destructor_call)) {
+ Printv(proxy_class_def, typemapLookup(n, "javafinalize", typemap_lookup_type, WARN_NONE), NIL);
+ }
+ // delete method
+ Printv(destruct, tm, NIL);
+ if (*Char(destructor_call))
+ Replaceall(destruct, "$jnicall", destructor_call);
+ else
+ Replaceall(destruct, "$jnicall", "throw new UnsupportedOperationException(\"C++ destructor does not have public access\")");
+ if (*Char(destruct)) {
+ Printv(proxy_class_def, "\n ", NIL);
+ const String *methodmods = Getattr(n, "destructmethodmodifiers");
+ if (methodmods)
+ Printv(proxy_class_def, methodmods, NIL);
+ else
+ Printv(proxy_class_def, destruct_methodmodifiers, NIL);
+ Printv(proxy_class_def, " void ", destruct_methodname, "(", destruct_parameters, ")", destructor_throws_clause, " ", destruct, "\n", NIL);
+ }
+ }
+ if (*Char(interface_upcasts))
+ Printv(proxy_class_def, interface_upcasts, NIL);
+
+ /* Insert directordisconnect typemap, if this class has directors enabled */
+ /* Also insert the swigTakeOwnership and swigReleaseOwnership methods */
+ if (feature_director) {
+ String *destruct_jnicall, *release_jnicall, *take_jnicall;
+ String *changeown_method_name = Swig_name_member(getNSpace(), getClassPrefix(), "change_ownership");
+
+ destruct_jnicall = NewStringf("%s()", destruct_methodname);
+ release_jnicall = NewStringf("%s.%s(this, swigCPtr, false)", full_imclass_name, changeown_method_name);
+ take_jnicall = NewStringf("%s.%s(this, swigCPtr, true)", full_imclass_name, changeown_method_name);
+
+ emitCodeTypemap(n, false, typemap_lookup_type, "directordisconnect", "methodname", destruct_jnicall);
+ emitCodeTypemap(n, false, typemap_lookup_type, "directorowner_release", "methodname", release_jnicall);
+ emitCodeTypemap(n, false, typemap_lookup_type, "directorowner_take", "methodname", take_jnicall);
+
+ Delete(destruct_jnicall);
+ Delete(changeown_method_name);
+ Delete(release_jnicall);
+ Delete(take_jnicall);
+ }
+
+ Delete(interface_upcasts);
+ Delete(interface_list);
+ Delete(attributes);
+ Delete(destruct);
+
+ // Emit extra user code
+ Printv(proxy_class_def, typemapLookup(n, "javacode", typemap_lookup_type, WARN_NONE), // extra Java code
+ "\n", NIL);
+
+ if (derived) {
+ String *upcast_method_name = Swig_name_member(getNSpace(), getClassPrefix(), smart != 0 ? "SWIGSmartPtrUpcast" : "SWIGUpcast");
+ upcastsCode(smart, upcast_method_name, c_classname, c_baseclassname);
+ Delete(upcast_method_name);
+ }
+
+ Delete(smart);
+ }
+
+ /* ----------------------------------------------------------------------
+ * emitInterfaceDeclaration()
+ * ---------------------------------------------------------------------- */
+
+ void emitInterfaceDeclaration(Node *n, String *interface_name, File *f_interface, String *nspace) {
+ if (package || nspace) {
+ Printf(f_interface, "package ");
+ if (package)
+ Printv(f_interface, package, nspace ? "." : "", NIL);
+ if (nspace)
+ Printv(f_interface, nspace, NIL);
+ Printf(f_interface, ";\n");
+ }
+
+ Printv(f_interface, typemapLookup(n, "javaimports", Getattr(n, "classtypeobj"), WARN_NONE), "\n", NIL);
+ Printv(f_interface, typemapLookup(n, "javainterfacemodifiers", Getattr(n, "classtypeobj"), WARN_JAVA_TYPEMAP_INTERFACEMODIFIERS_UNDEF), NIL);
+ Printf(f_interface, " %s", interface_name);
+ if (List *baselist = Getattr(n, "bases")) {
+ String *bases = 0;
+ for (Iterator base = First(baselist); base.item; base = Next(base)) {
+ if (GetFlag(base.item, "feature:ignore") || !GetFlag(base.item, "feature:interface"))
+ continue; // TODO: warn about skipped non-interface bases
+ String *base_iname = Getattr(base.item, "interface:name");
+ if (!bases)
+ bases = Copy(base_iname);
+ else {
+ Append(bases, ", ");
+ Append(bases, base_iname);
+ }
+ }
+ if (bases) {
+ Printv(f_interface, " extends ", bases, NIL);
+ Delete(bases);
+ }
+ }
+ Printf(f_interface, " {\n");
+
+ Node *attributes = NewHash();
+ String *interface_code = Copy(typemapLookup(n, "javainterfacecode", Getattr(n, "classtypeobj"), WARN_JAVA_TYPEMAP_INTERFACECODE_UNDEF, attributes));
+ if (interface_code) {
+ String *interface_declaration = Copy(Getattr(attributes, "tmap:javainterfacecode:declaration"));
+ if (interface_declaration) {
+ Replaceall(interface_declaration, "$interfacename", interface_name);
+ Printv(f_interface, interface_declaration, NIL);
+ Delete(interface_declaration);
+ }
+ Delete(interface_code);
+ }
+ }
+
+ /* ----------------------------------------------------------------------
+ * classDeclaration()
+ * ---------------------------------------------------------------------- */
+
+ int classDeclaration(Node *n) {
+ return Language::classDeclaration(n);
+ }
+
+ /* ----------------------------------------------------------------------
+ * classHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int classHandler(Node *n) {
+ File *f_proxy = NULL;
+ File *f_interface = NULL;
+ String *old_proxy_class_name = proxy_class_name;
+ String *old_full_proxy_class_name = full_proxy_class_name;
+ String *old_full_imclass_name = full_imclass_name;
+ String *old_destructor_call = destructor_call;
+ String *old_destructor_throws_clause = destructor_throws_clause;
+ String *old_proxy_class_constants_code = proxy_class_constants_code;
+ String *old_proxy_class_def = proxy_class_def;
+ String *old_proxy_class_code = proxy_class_code;
+ bool has_outerclass = Getattr(n, "nested:outer") && !GetFlag(n, "feature:flatnested");
+ String *old_interface_class_code = interface_class_code;
+ interface_class_code = 0;
+
+ if (proxy_flag) {
+ proxy_class_name = NewString(Getattr(n, "sym:name"));
+ String *nspace = getNSpace();
+ constructIntermediateClassName(n);
+
+ String *outerClassesPrefix = 0;
+ if (Node *outer = Getattr(n, "nested:outer")) {
+ outerClassesPrefix = Copy(Getattr(outer, "sym:name"));
+ for (outer = Getattr(outer, "nested:outer"); outer != 0; outer = Getattr(outer, "nested:outer")) {
+ Push(outerClassesPrefix, ".");
+ Push(outerClassesPrefix, Getattr(outer, "sym:name"));
+ }
+ }
+ if (!nspace) {
+ full_proxy_class_name = outerClassesPrefix ? NewStringf("%s.%s", outerClassesPrefix, proxy_class_name) : NewStringf("%s", proxy_class_name);
+
+ if (Cmp(proxy_class_name, imclass_name) == 0) {
+ Printf(stderr, "Class name cannot be equal to intermediary class name: %s\n", proxy_class_name);
+ Exit(EXIT_FAILURE);
+ }
+
+ if (Cmp(proxy_class_name, module_class_name) == 0) {
+ Printf(stderr, "Class name cannot be equal to module class name: %s\n", proxy_class_name);
+ Exit(EXIT_FAILURE);
+ }
+ } else {
+ if (outerClassesPrefix) {
+ if (package)
+ full_proxy_class_name = NewStringf("%s.%s.%s.%s", package, nspace, outerClassesPrefix, proxy_class_name);
+ else
+ full_proxy_class_name = NewStringf("%s.%s.%s", nspace, outerClassesPrefix, proxy_class_name);
+ } else {
+ if (package)
+ full_proxy_class_name = NewStringf("%s.%s.%s", package, nspace, proxy_class_name);
+ else
+ full_proxy_class_name = NewStringf("%s.%s", nspace, proxy_class_name);
+ }
+ }
+
+ String *interface_name = GetFlag(n, "feature:interface") ? Getattr(n, "interface:name") : 0;
+ if (outerClassesPrefix) {
+ String *fnspace = nspace ? NewStringf("%s.%s", nspace, outerClassesPrefix) : outerClassesPrefix;
+ if (!addSymbol(proxy_class_name, n, fnspace))
+ return SWIG_ERROR;
+ if (interface_name && !addInterfaceSymbol(interface_name, n, fnspace))
+ return SWIG_ERROR;
+ if (nspace)
+ Delete(fnspace);
+ Delete(outerClassesPrefix);
+ } else {
+ if (!addSymbol(proxy_class_name, n, nspace))
+ return SWIG_ERROR;
+ if (interface_name && !addInterfaceSymbol(interface_name, n, nspace))
+ return SWIG_ERROR;
+ }
+
+ // Each outer proxy class goes into a separate file
+ if (!has_outerclass) {
+ String *output_directory = outputDirectory(nspace);
+ String *filen = NewStringf("%s%s.java", output_directory, proxy_class_name);
+ f_proxy = NewFile(filen, "w", SWIG_output_files());
+ if (!f_proxy) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ Delete(output_directory);
+
+ // Start writing out the proxy class file
+ emitBanner(f_proxy);
+
+ if (package || nspace) {
+ Printf(f_proxy, "package ");
+ if (package)
+ Printv(f_proxy, package, nspace ? "." : "", NIL);
+ if (nspace)
+ Printv(f_proxy, nspace, NIL);
+ Printf(f_proxy, ";\n");
+ }
+ }
+ else
+ ++nesting_depth;
+
+ proxy_class_def = NewString("");
+ proxy_class_code = NewString("");
+ destructor_call = NewString("");
+ destructor_throws_clause = NewString("");
+ proxy_class_constants_code = NewString("");
+
+ if (GetFlag(n, "feature:interface")) {
+ interface_class_code = NewString("");
+ String *output_directory = outputDirectory(nspace);
+ String *filen = NewStringf("%s%s.java", output_directory, interface_name);
+ f_interface = NewFile(filen, "w", SWIG_output_files());
+ if (!f_interface) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, filen); // file name ownership goes to the list
+ emitBanner(f_interface);
+ emitInterfaceDeclaration(n, interface_name, interface_class_code, nspace);
+ Delete(filen);
+ Delete(output_directory);
+ }
+ }
+
+ Language::classHandler(n);
+
+ if (proxy_flag) {
+ emitProxyClassDefAndCPPCasts(n);
+
+ String *javaclazzname = Swig_name_member(getNSpace(), getClassPrefix(), ""); // mangled full proxy class name
+
+ Replaceall(proxy_class_def, "$javaclassname", proxy_class_name);
+ Replaceall(proxy_class_code, "$javaclassname", proxy_class_name);
+ Replaceall(proxy_class_constants_code, "$javaclassname", proxy_class_name);
+ Replaceall(interface_class_code, "$javaclassname", proxy_class_name);
+
+ Replaceall(proxy_class_def, "$javaclazzname", javaclazzname);
+ Replaceall(proxy_class_code, "$javaclazzname", javaclazzname);
+ Replaceall(proxy_class_constants_code, "$javaclazzname", javaclazzname);
+ Replaceall(interface_class_code, "$javaclazzname", javaclazzname);
+
+ Replaceall(proxy_class_def, "$module", module_class_name);
+ Replaceall(proxy_class_code, "$module", module_class_name);
+ Replaceall(proxy_class_constants_code, "$module", module_class_name);
+ Replaceall(interface_class_code, "$module", module_class_name);
+
+ Replaceall(proxy_class_def, "$imclassname", full_imclass_name);
+ Replaceall(proxy_class_code, "$imclassname", full_imclass_name);
+ Replaceall(proxy_class_constants_code, "$imclassname", full_imclass_name);
+ Replaceall(interface_class_code, "$imclassname", full_imclass_name);
+
+ if (!has_outerclass)
+ Printv(f_proxy, proxy_class_def, proxy_class_code, NIL);
+ else {
+ Swig_offset_string(proxy_class_def, nesting_depth);
+ Append(old_proxy_class_code, proxy_class_def);
+ Swig_offset_string(proxy_class_code, nesting_depth);
+ Append(old_proxy_class_code, proxy_class_code);
+ }
+
+ // Write out all the constants
+ if (Len(proxy_class_constants_code) != 0) {
+ if (!has_outerclass)
+ Printv(f_proxy, proxy_class_constants_code, NIL);
+ else {
+ Swig_offset_string(proxy_class_constants_code, nesting_depth);
+ Append(old_proxy_class_code, proxy_class_constants_code);
+ }
+ }
+
+ if (!has_outerclass) {
+ Printf(f_proxy, "}\n");
+ Delete(f_proxy);
+ f_proxy = NULL;
+ } else {
+ for (int i = 0; i < nesting_depth; ++i)
+ Append(old_proxy_class_code, " ");
+ Append(old_proxy_class_code, "}\n\n");
+ --nesting_depth;
+ }
+
+ if (f_interface) {
+ Printv(f_interface, interface_class_code, "}\n", NIL);
+ Delete(f_interface);
+ f_interface = 0;
+ }
+
+ emitDirectorExtraMethods(n);
+
+ Delete(interface_class_code);
+ interface_class_code = old_interface_class_code;
+ Delete(javaclazzname);
+ Delete(proxy_class_name);
+ proxy_class_name = old_proxy_class_name;
+ Delete(full_proxy_class_name);
+ full_proxy_class_name = old_full_proxy_class_name;
+ Delete(full_imclass_name);
+ full_imclass_name = old_full_imclass_name;
+ Delete(destructor_call);
+ destructor_call = old_destructor_call;
+ Delete(destructor_throws_clause);
+ destructor_throws_clause = old_destructor_throws_clause;
+ Delete(proxy_class_constants_code);
+ proxy_class_constants_code = old_proxy_class_constants_code;
+ Delete(proxy_class_def);
+ proxy_class_def = old_proxy_class_def;
+ Delete(proxy_class_code);
+ proxy_class_code = old_proxy_class_code;
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * memberfunctionHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int memberfunctionHandler(Node *n) {
+ member_func_flag = true;
+ Language::memberfunctionHandler(n);
+
+ if (proxy_flag) {
+ String *overloaded_name = getOverloadedName(n);
+ String *intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), overloaded_name);
+ Setattr(n, "proxyfuncname", Getattr(n, "sym:name"));
+ Setattr(n, "imfuncname", intermediary_function_name);
+ proxyClassFunctionHandler(n);
+ Delete(overloaded_name);
+ }
+ member_func_flag = false;
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+
+ static_flag = true;
+ member_func_flag = true;
+ Language::staticmemberfunctionHandler(n);
+
+ if (proxy_flag) {
+ String *overloaded_name = getOverloadedName(n);
+ String *intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), overloaded_name);
+ Setattr(n, "proxyfuncname", Getattr(n, "sym:name"));
+ Setattr(n, "imfuncname", intermediary_function_name);
+ proxyClassFunctionHandler(n);
+ Delete(overloaded_name);
+ }
+ static_flag = false;
+ member_func_flag = false;
+
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * proxyClassFunctionHandler()
+ *
+ * Function called for creating a Java wrapper function around a c++ function in the
+ * proxy class. Used for both static and non-static C++ class functions.
+ * C++ class static functions map to Java static functions.
+ * Two extra attributes in the Node must be available. These are "proxyfuncname" -
+ * the name of the Java class proxy function, which in turn will call "imfuncname" -
+ * the intermediary (JNI) function name in the intermediary class.
+ * ----------------------------------------------------------------------------- */
+
+ void proxyClassFunctionHandler(Node *n) {
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *intermediary_function_name = Getattr(n, "imfuncname");
+ String *proxy_function_name = Getattr(n, "proxyfuncname");
+ String *tm;
+ Parm *p;
+ int i;
+ String *imcall = NewString("");
+ String *return_type = NewString("");
+ String *function_code = NewString("");
+ bool setter_flag = false;
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ bool is_interface = GetFlag(parentNode(n), "feature:interface") && !checkAttribute(n, "kind", "variable")
+ && !static_flag && Getattr(n, "interface:owner") == 0;
+
+ if (!proxy_flag)
+ return;
+
+ // Wrappers not wanted for some methods where the parameters cannot be overloaded in Java
+ if (Getattr(n, "overload:ignore"))
+ return;
+
+ // Don't generate proxy method for additional explicitcall method used in directors
+ if (GetFlag(n, "explicitcall"))
+ return;
+
+ if (l) {
+ if (SwigType_type(Getattr(l, "type")) == T_VOID) {
+ l = nextSibling(l);
+ }
+ }
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("in", l, NULL);
+ Swig_typemap_attach_parms("jtype", l, NULL);
+ Swig_typemap_attach_parms("jstype", l, NULL);
+ Swig_typemap_attach_parms("javain", l, NULL);
+
+ /* Get return types */
+ if ((tm = Swig_typemap_lookup("jstype", n, "", 0))) {
+ // Note that in the case of polymorphic (covariant) return types, the method's return type is changed to be the base of the C++ return type
+ SwigType *covariant = Getattr(n, "covariant");
+ substituteClassname(covariant ? covariant : t, tm);
+ Printf(return_type, "%s", tm);
+ if (covariant)
+ Swig_warning(WARN_JAVA_COVARIANT_RET, input_file, line_number,
+ "Covariant return types not supported in Java. Proxy method will return %s.\n", SwigType_str(covariant, 0));
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JSTYPE_UNDEF, input_file, line_number, "No jstype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if (wrapping_member_flag && !enum_constant_flag) {
+ // For wrapping member variables (Javabean setter)
+ setter_flag = (Cmp(Getattr(n, "sym:name"), Swig_name_set(getNSpace(), Swig_name_member(0, getClassPrefix(), variable_name))) == 0);
+ }
+
+ // Translate and write javadoc comment if flagged
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, " ");
+ if (comment_creation_chatter)
+ Printf(function_code, "/* This was generated from proxyclassfunctionhandler() */\n");
+ Printv(function_code, Char(doxygen_comments), NIL);
+ Delete(doxygen_comments);
+ }
+
+ /* Start generating the proxy function */
+ const String *methodmods = Getattr(n, "feature:java:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+ Printf(function_code, " %s ", methodmods);
+ if (static_flag)
+ Printf(function_code, "static ");
+ Printf(function_code, "%s %s(", return_type, proxy_function_name);
+
+ if (is_interface)
+ Printf(interface_class_code, " %s %s(", return_type, proxy_function_name);
+
+ Printv(imcall, full_imclass_name, ".$imfuncname(", NIL);
+ if (!static_flag) {
+ Printf(imcall, "swigCPtr");
+
+ String *this_type = Copy(getClassType());
+ String *name = NewString("jself");
+ String *qualifier = Getattr(n, "qualifier");
+ if (qualifier)
+ SwigType_push(this_type, qualifier);
+ SwigType_add_pointer(this_type);
+ Parm *this_parm = NewParm(this_type, name, n);
+ Swig_typemap_attach_parms("jtype", this_parm, NULL);
+ Swig_typemap_attach_parms("jstype", this_parm, NULL);
+
+ if (prematureGarbageCollectionPreventionParameter(this_type, this_parm))
+ Printf(imcall, ", this");
+
+ Delete(this_parm);
+ Delete(name);
+ Delete(this_type);
+ }
+
+ emit_mark_varargs(l);
+
+ int gencomma = !static_flag;
+
+ /* Output each parameter */
+ for (i = 0, p = l; p; i++) {
+
+ /* Ignored varargs */
+ if (checkAttribute(p, "varargs:ignore", "1")) {
+ p = nextSibling(p);
+ continue;
+ }
+
+ /* Ignored parameters */
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ /* Ignore the 'this' argument for variable wrappers */
+ if (!(variable_wrapper_flag && i == 0) || static_flag) {
+ SwigType *pt = Getattr(p, "type");
+ String *param_type = NewString("");
+
+ /* Get the Java parameter type */
+ if ((tm = Getattr(p, "tmap:jstype"))) {
+ substituteClassname(pt, tm);
+ Printf(param_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JSTYPE_UNDEF, input_file, line_number, "No jstype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma)
+ Printf(imcall, ", ");
+
+ String *arg = makeParameterName(n, p, i, setter_flag);
+
+ // Use typemaps to transform type used in Java proxy wrapper (in proxy class) to type used in JNI function (in intermediary class)
+ if ((tm = Getattr(p, "tmap:javain"))) {
+ addThrows(n, "tmap:javain", p);
+ substituteClassname(pt, tm);
+ Replaceall(tm, "$javainput", arg);
+ String *pre = Getattr(p, "tmap:javain:pre");
+ if (pre) {
+ substituteClassname(pt, pre);
+ Replaceall(pre, "$javainput", arg);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:javain:post");
+ if (post) {
+ substituteClassname(pt, post);
+ Replaceall(post, "$javainput", arg);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JAVAIN_UNDEF, input_file, line_number, "No javain typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to proxy function */
+ if (gencomma >= 2) {
+ Printf(function_code, ", ");
+ if (is_interface)
+ Printf(interface_class_code, ", ");
+ }
+ gencomma = 2;
+ Printf(function_code, "%s %s", param_type, arg);
+ if (is_interface)
+ Printf(interface_class_code, "%s %s", param_type, arg);
+
+ if (prematureGarbageCollectionPreventionParameter(pt, p)) {
+ String *pgcppname = Getattr(p, "tmap:javain:pgcppname");
+ if (pgcppname) {
+ String *argname = Copy(pgcppname);
+ Replaceall(argname, "$javainput", arg);
+ Printf(imcall, ", %s", argname);
+ Delete(argname);
+ } else {
+ Printf(imcall, ", %s", arg);
+ }
+ }
+
+ Delete(arg);
+ Delete(param_type);
+ }
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ Printf(imcall, ")");
+ Printf(function_code, ")");
+
+ // Transform return type used in JNI function (in intermediary class) to type used in Java wrapper function (in proxy class)
+ if ((tm = Swig_typemap_lookup("javaout", n, "", 0))) {
+ addThrows(n, "tmap:javaout", n);
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ if (is_pre_code || is_post_code) {
+ Replaceall(tm, "\n ", "\n "); // add extra indentation to code in typemap
+ if (is_post_code) {
+ Insert(tm, 0, "\n try ");
+ Printv(tm, " finally {\n", post_code, "\n }", NIL);
+ } else {
+ Insert(tm, 0, "\n ");
+ }
+ if (is_pre_code) {
+ Insert(tm, 0, pre_code);
+ Insert(tm, 0, "\n");
+ }
+ Insert(tm, 0, "{");
+ Printf(tm, "\n }");
+ }
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "true");
+ else
+ Replaceall(tm, "$owner", "false");
+ substituteClassname(t, tm);
+
+ // For director methods: generate code to selectively make a normal polymorphic call or
+ // an explicit method call - needed to prevent infinite recursion calls in director methods.
+ Node *explicit_n = Getattr(n, "explicitcallnode");
+ if (explicit_n) {
+ String *ex_overloaded_name = getOverloadedName(explicit_n);
+ String *ex_intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), ex_overloaded_name);
+
+ String *ex_imcall = Copy(imcall);
+ Replaceall(ex_imcall, "$imfuncname", ex_intermediary_function_name);
+ Replaceall(imcall, "$imfuncname", intermediary_function_name);
+
+ String *excode = NewString("");
+ if (!Cmp(return_type, "void"))
+ Printf(excode, "if (getClass() == %s.class) %s; else %s", proxy_class_name, imcall, ex_imcall);
+ else
+ Printf(excode, "(getClass() == %s.class) ? %s : %s", proxy_class_name, imcall, ex_imcall);
+
+ Clear(imcall);
+ Printv(imcall, excode, NIL);
+ Delete(ex_overloaded_name);
+ Delete(excode);
+ } else {
+ Replaceall(imcall, "$imfuncname", intermediary_function_name);
+ }
+
+ Replaceall(tm, "$imfuncname", intermediary_function_name);
+ Replaceall(tm, "$jnicall", imcall);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JAVAOUT_UNDEF, input_file, line_number, "No javaout typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ if (is_interface) {
+ Printf(interface_class_code, ")");
+ generateThrowsClause(n, interface_class_code);
+ Printf(interface_class_code, ";\n");
+ }
+ generateThrowsClause(n, function_code);
+ Printf(function_code, " %s\n\n", tm ? tm : empty_string);
+ Printv(proxy_class_code, function_code, NIL);
+
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(function_code);
+ Delete(return_type);
+ Delete(imcall);
+ }
+
+ /* ----------------------------------------------------------------------
+ * constructorHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int constructorHandler(Node *n) {
+
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ Parm *p;
+ int i;
+ String *function_code = NewString("");
+ String *helper_code = NewString(""); // Holds code for the constructor helper method generated only when the javain typemap has code in the pre or post attributes
+ String *helper_args = NewString("");
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *im_return_type = NewString("");
+ bool feature_director = (parentNode(n) && Swig_directorclass(n));
+
+ Language::constructorHandler(n);
+
+ // Wrappers not wanted for some methods where the parameters cannot be overloaded in Java
+ if (Getattr(n, "overload:ignore"))
+ return SWIG_OK;
+
+ if (proxy_flag) {
+ String *overloaded_name = getOverloadedName(n);
+ String *mangled_overname = Swig_name_construct(getNSpace(), overloaded_name);
+ String *imcall = NewString("");
+
+ const String *methodmods = Getattr(n, "feature:java:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+
+ tm = Getattr(n, "tmap:jtype"); // typemaps were attached earlier to the node
+ Printf(im_return_type, "%s", tm);
+
+ // Translate and write javadoc comment if flagged
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, " ");
+ if (comment_creation_chatter)
+ Printf(function_code, "/* This was generated from constructionhandler() */\n");
+ Printv(function_code, Char(doxygen_comments), NIL);
+ Delete(doxygen_comments);
+ }
+
+ Printf(function_code, " %s %s(", methodmods, proxy_class_name);
+ Printf(helper_code, " static private %s SwigConstruct%s(", im_return_type, proxy_class_name);
+
+ Printv(imcall, full_imclass_name, ".", mangled_overname, "(", NIL);
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("in", l, NULL);
+ Swig_typemap_attach_parms("jtype", l, NULL);
+ Swig_typemap_attach_parms("jstype", l, NULL);
+ Swig_typemap_attach_parms("javain", l, NULL);
+
+ emit_mark_varargs(l);
+
+ int gencomma = 0;
+
+ /* Output each parameter */
+ for (i = 0, p = l; p; i++) {
+
+ /* Ignored varargs */
+ if (checkAttribute(p, "varargs:ignore", "1")) {
+ p = nextSibling(p);
+ continue;
+ }
+
+ /* Ignored parameters */
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *param_type = NewString("");
+
+ /* Get the Java parameter type */
+ if ((tm = Getattr(p, "tmap:jstype"))) {
+ substituteClassname(pt, tm);
+ Printf(param_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JSTYPE_UNDEF, input_file, line_number, "No jstype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma)
+ Printf(imcall, ", ");
+
+ String *arg = makeParameterName(n, p, i, false);
+
+ // Use typemaps to transform type used in Java wrapper function (in proxy class) to type used in JNI function (in intermediary class)
+ if ((tm = Getattr(p, "tmap:javain"))) {
+ addThrows(n, "tmap:javain", p);
+ substituteClassname(pt, tm);
+ Replaceall(tm, "$javainput", arg);
+ String *pre = Getattr(p, "tmap:javain:pre");
+ if (pre) {
+ substituteClassname(pt, pre);
+ Replaceall(pre, "$javainput", arg);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:javain:post");
+ if (post) {
+ substituteClassname(pt, post);
+ Replaceall(post, "$javainput", arg);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JAVAIN_UNDEF, input_file, line_number, "No javain typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to proxy function */
+ if (gencomma) {
+ Printf(function_code, ", ");
+ Printf(helper_code, ", ");
+ Printf(helper_args, ", ");
+ }
+ Printf(function_code, "%s %s", param_type, arg);
+ Printf(helper_code, "%s %s", param_type, arg);
+ Printf(helper_args, "%s", arg);
+ ++gencomma;
+
+ if (prematureGarbageCollectionPreventionParameter(pt, p)) {
+ String *pgcppname = Getattr(p, "tmap:javain:pgcppname");
+ if (pgcppname) {
+ String *argname = Copy(pgcppname);
+ Replaceall(argname, "$javainput", arg);
+ Printf(imcall, ", %s", argname);
+ Delete(argname);
+ } else {
+ Printf(imcall, ", %s", arg);
+ }
+ }
+
+ Delete(arg);
+ Delete(param_type);
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ Printf(imcall, ")");
+
+ Printf(function_code, ")");
+ Printf(helper_code, ")");
+ generateThrowsClause(n, function_code);
+
+ /* Insert the javaconstruct typemap, doing the replacement for $directorconnect, as needed */
+ Hash *attributes = NewHash();
+ String *typemap_lookup_type = Getattr(getCurrentClass(), "classtypeobj");
+ String *construct_tm = Copy(typemapLookup(n, "javaconstruct", typemap_lookup_type,
+ WARN_JAVA_TYPEMAP_JAVACONSTRUCT_UNDEF, attributes));
+ if (construct_tm) {
+ if (!feature_director) {
+ Replaceall(construct_tm, "$directorconnect", "");
+ } else {
+ String *connect_attr = Getattr(attributes, "tmap:javaconstruct:directorconnect");
+
+ if (connect_attr) {
+ Replaceall(construct_tm, "$directorconnect", connect_attr);
+ } else {
+ Swig_warning(WARN_JAVA_NO_DIRECTORCONNECT_ATTR, input_file, line_number, "\"directorconnect\" attribute missing in %s \"javaconstruct\" typemap.\n",
+ Getattr(n, "name"));
+ Replaceall(construct_tm, "$directorconnect", "");
+ }
+ }
+
+ Printv(function_code, " ", construct_tm, "\n", NIL);
+ }
+
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ if (is_pre_code || is_post_code) {
+ generateThrowsClause(n, helper_code);
+ Printf(helper_code, " {\n");
+ if (is_pre_code) {
+ Printv(helper_code, pre_code, "\n", NIL);
+ }
+ if (is_post_code) {
+ Printf(helper_code, " try {\n");
+ Printv(helper_code, " return ", imcall, ";\n", NIL);
+ Printv(helper_code, " } finally {\n", post_code, "\n }", NIL);
+ } else {
+ Printv(helper_code, " return ", imcall, ";", NIL);
+ }
+ Printf(helper_code, "\n }\n");
+ String *helper_name = NewStringf("%s.SwigConstruct%s(%s)", proxy_class_name, proxy_class_name, helper_args);
+ Printv(proxy_class_code, helper_code, "\n", NIL);
+ Replaceall(function_code, "$imcall", helper_name);
+ Delete(helper_name);
+ } else {
+ Replaceall(function_code, "$imcall", imcall);
+ }
+
+ Printv(proxy_class_code, function_code, "\n", NIL);
+
+ Delete(helper_args);
+ Delete(im_return_type);
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(construct_tm);
+ Delete(attributes);
+ Delete(overloaded_name);
+ Delete(imcall);
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * destructorHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int destructorHandler(Node *n) {
+ Language::destructorHandler(n);
+ String *symname = Getattr(n, "sym:name");
+
+ if (proxy_flag) {
+ Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL);
+ generateThrowsClause(n, destructor_throws_clause);
+ const String *methodmods = Getattr(n, "feature:java:methodmodifiers");
+ if (methodmods)
+ Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods);
+ }
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * membervariableHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int membervariableHandler(Node *n) {
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ variable_wrapper_flag = true;
+ Language::membervariableHandler(n);
+ wrapping_member_flag = false;
+ variable_wrapper_flag = false;
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * staticmembervariableHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int staticmembervariableHandler(Node *n) {
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ static_flag = true;
+ Language::staticmembervariableHandler(n);
+ wrapping_member_flag = false;
+ static_flag = false;
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * memberconstantHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int memberconstantHandler(Node *n) {
+ variable_name = Getattr(n, "sym:name");
+ wrapping_member_flag = true;
+ Language::memberconstantHandler(n);
+ wrapping_member_flag = false;
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getOverloadedName()
+ * ----------------------------------------------------------------------------- */
+
+ String *getOverloadedName(Node *n) {
+
+ /* Although JNI functions are designed to handle overloaded Java functions,
+ * a Java long is used for all classes in the SWIG intermediary class.
+ * The intermediary class methods are thus mangled when overloaded to give
+ * a unique name. */
+ String *overloaded_name = NewStringf("%s", Getattr(n, "sym:name"));
+
+ if (Getattr(n, "sym:overloaded")) {
+ Printv(overloaded_name, Getattr(n, "sym:overname"), NIL);
+ }
+
+ return overloaded_name;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * moduleClassFunctionHandler()
+ * ----------------------------------------------------------------------------- */
+
+ void moduleClassFunctionHandler(Node *n) {
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *tm;
+ Parm *p;
+ int i;
+ String *imcall = NewString("");
+ String *return_type = NewString("");
+ String *function_code = NewString("");
+ int num_arguments = 0;
+ String *overloaded_name = getOverloadedName(n);
+ String *func_name = NULL;
+ bool setter_flag = false;
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+
+ // Translate and write javadoc comment if flagged
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ String *doxygen_comments = doxygenTranslator->getDocumentation(n, " ");
+ if (comment_creation_chatter)
+ Printf(function_code, "/* This was generated from moduleClassFunctionHandler() */\n");
+ Printv(function_code, doxygen_comments, NIL);
+ Delete(doxygen_comments);
+ }
+
+ if (l) {
+ if (SwigType_type(Getattr(l, "type")) == T_VOID) {
+ l = nextSibling(l);
+ }
+ }
+
+ /* Attach the non-standard typemaps to the parameter list */
+ Swig_typemap_attach_parms("jstype", l, NULL);
+ Swig_typemap_attach_parms("javain", l, NULL);
+
+ /* Get return types */
+ if ((tm = Swig_typemap_lookup("jstype", n, "", 0))) {
+ substituteClassname(t, tm);
+ Printf(return_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JSTYPE_UNDEF, input_file, line_number, "No jstype typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ /* Change function name for global variables */
+ if (proxy_flag && global_variable_flag) {
+ // Capitalize the first letter in the variable to create a JavaBean type getter/setter function name
+ func_name = NewString("");
+ setter_flag = (Cmp(Getattr(n, "sym:name"), Swig_name_set(getNSpace(), variable_name)) == 0);
+ if (setter_flag)
+ Printf(func_name, "set");
+ else
+ Printf(func_name, "get");
+ Putc(toupper((int) *Char(variable_name)), func_name);
+ Printf(func_name, "%s", Char(variable_name) + 1);
+ } else {
+ func_name = Copy(Getattr(n, "sym:name"));
+ }
+
+ /* Start generating the function */
+ const String *methodmods = Getattr(n, "feature:java:methodmodifiers");
+ methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
+ Printf(function_code, " %s static %s %s(", methodmods, return_type, func_name);
+ Printv(imcall, imclass_name, ".", overloaded_name, "(", NIL);
+
+ /* Get number of required and total arguments */
+ num_arguments = emit_num_arguments(l);
+
+ bool global_or_member_variable = global_variable_flag || (wrapping_member_flag && !enum_constant_flag);
+ int gencomma = 0;
+
+ /* Output each parameter */
+ for (i = 0, p = l; i < num_arguments; i++) {
+
+ /* Ignored parameters */
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *param_type = NewString("");
+
+ /* Get the Java parameter type */
+ if ((tm = Getattr(p, "tmap:jstype"))) {
+ substituteClassname(pt, tm);
+ Printf(param_type, "%s", tm);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JSTYPE_UNDEF, input_file, line_number, "No jstype typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ if (gencomma)
+ Printf(imcall, ", ");
+
+ String *arg = makeParameterName(n, p, i, global_or_member_variable);
+
+ // Use typemaps to transform type used in Java wrapper function (in proxy class) to type used in JNI function (in intermediary class)
+ if ((tm = Getattr(p, "tmap:javain"))) {
+ addThrows(n, "tmap:javain", p);
+ substituteClassname(pt, tm);
+ Replaceall(tm, "$javainput", arg);
+ String *pre = Getattr(p, "tmap:javain:pre");
+ if (pre) {
+ substituteClassname(pt, pre);
+ Replaceall(pre, "$javainput", arg);
+ if (Len(pre_code) > 0)
+ Printf(pre_code, "\n");
+ Printv(pre_code, pre, NIL);
+ }
+ String *post = Getattr(p, "tmap:javain:post");
+ if (post) {
+ substituteClassname(pt, post);
+ Replaceall(post, "$javainput", arg);
+ if (Len(post_code) > 0)
+ Printf(post_code, "\n");
+ Printv(post_code, post, NIL);
+ }
+ Printv(imcall, tm, NIL);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JAVAIN_UNDEF, input_file, line_number, "No javain typemap defined for %s\n", SwigType_str(pt, 0));
+ }
+
+ /* Add parameter to module class function */
+ if (gencomma >= 2)
+ Printf(function_code, ", ");
+ gencomma = 2;
+ Printf(function_code, "%s %s", param_type, arg);
+
+ if (prematureGarbageCollectionPreventionParameter(pt, p)) {
+ String *pgcppname = Getattr(p, "tmap:javain:pgcppname");
+ if (pgcppname) {
+ String *argname = Copy(pgcppname);
+ Replaceall(argname, "$javainput", arg);
+ Printf(imcall, ", %s", argname);
+ Delete(argname);
+ } else {
+ Printf(imcall, ", %s", arg);
+ }
+ }
+
+ p = Getattr(p, "tmap:in:next");
+ Delete(arg);
+ Delete(param_type);
+ }
+
+ Printf(imcall, ")");
+ Printf(function_code, ")");
+
+ // Transform return type used in JNI function (in intermediary class) to type used in Java wrapper function (in module class)
+ if ((tm = Swig_typemap_lookup("javaout", n, "", 0))) {
+ addThrows(n, "tmap:javaout", n);
+ bool is_pre_code = Len(pre_code) > 0;
+ bool is_post_code = Len(post_code) > 0;
+ if (is_pre_code || is_post_code) {
+ Replaceall(tm, "\n ", "\n "); // add extra indentation to code in typemap
+ if (is_post_code) {
+ Insert(tm, 0, "\n try ");
+ Printv(tm, " finally {\n", post_code, "\n }", NIL);
+ } else {
+ Insert(tm, 0, "\n ");
+ }
+ if (is_pre_code) {
+ Insert(tm, 0, pre_code);
+ Insert(tm, 0, "\n");
+ }
+ Insert(tm, 0, "{");
+ Printf(tm, "\n }");
+ }
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "true");
+ else
+ Replaceall(tm, "$owner", "false");
+ substituteClassname(t, tm);
+ Replaceall(tm, "$imfuncname", overloaded_name);
+ Replaceall(tm, "$jnicall", imcall);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JAVAOUT_UNDEF, input_file, line_number, "No javaout typemap defined for %s\n", SwigType_str(t, 0));
+ }
+
+ generateThrowsClause(n, function_code);
+ Printf(function_code, " %s\n\n", tm ? tm : empty_string);
+ Printv(module_class_code, function_code, NIL);
+
+ Delete(pre_code);
+ Delete(post_code);
+ Delete(function_code);
+ Delete(return_type);
+ Delete(imcall);
+ Delete(func_name);
+ }
+
+ /*----------------------------------------------------------------------
+ * replaceSpecialVariables()
+ *--------------------------------------------------------------------*/
+
+ virtual void replaceSpecialVariables(String *method, String *tm, Parm *parm) {
+ (void)method;
+ SwigType *type = Getattr(parm, "type");
+ substituteClassname(type, tm);
+ }
+
+ /*----------------------------------------------------------------------
+ * decodeEnumFeature()
+ * Decode the possible enum features, which are one of:
+ * %javaenum(simple)
+ * %javaenum(typeunsafe) - default
+ * %javaenum(typesafe)
+ * %javaenum(proper)
+ *--------------------------------------------------------------------*/
+
+ EnumFeature decodeEnumFeature(Node *n) {
+ EnumFeature enum_feature = TypeunsafeEnum;
+ String *feature = Getattr(n, "feature:java:enum");
+ if (feature) {
+ if (Cmp(feature, "simple") == 0)
+ enum_feature = SimpleEnum;
+ else if (Cmp(feature, "typesafe") == 0)
+ enum_feature = TypesafeEnum;
+ else if (Cmp(feature, "proper") == 0)
+ enum_feature = ProperEnum;
+ }
+ return enum_feature;
+ }
+
+ /* -----------------------------------------------------------------------
+ * enumValue()
+ * This method will return a string with an enum value to use in Java generated
+ * code. If the %javaconst feature is not used, the string will contain the intermediary
+ * class call to obtain the enum value. The intermediary class and JNI methods to obtain
+ * the enum value will be generated. Otherwise the C/C++ enum value will be used if there
+ * is one and hopefully it will compile as Java code - e.g. 20 as in: enum E{e=20};
+ * The %javaconstvalue feature overrides all other ways to generate the constant value.
+ * The caller must delete memory allocated for the returned string.
+ * ------------------------------------------------------------------------ */
+
+ String *enumValue(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+
+ // Check for the %javaconstvalue feature
+ String *value = Getattr(n, "feature:java:constvalue");
+
+ if (!value) {
+ // The %javaconst feature determines how the constant value is obtained
+ int const_feature_flag = GetFlag(n, "feature:java:const");
+
+ if (const_feature_flag) {
+ // Use the C syntax to make a true Java constant and hope that it compiles as Java code
+ value = Getattr(n, "enumvalue") ? Copy(Getattr(n, "enumvalue")) : Copy(Getattr(n, "enumvalueex"));
+ } else {
+ String *newsymname = 0;
+ if (!getCurrentClass() || !proxy_flag) {
+ String *enumClassPrefix = getEnumClassPrefix();
+ if (enumClassPrefix) {
+ // A global scoped enum
+ newsymname = Swig_name_member(0, enumClassPrefix, symname);
+ symname = newsymname;
+ }
+ }
+
+ // Get the enumvalue from a JNI call
+ if (!getCurrentClass() || !cparse_cplusplus || !proxy_flag) {
+ // Strange hack to change the name
+ Setattr(n, "name", Getattr(n, "value")); /* for wrapping of enums in a namespace when emit_action is used */
+ constantWrapper(n);
+ value = NewStringf("%s.%s()", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
+ } else {
+ memberconstantHandler(n);
+ value = NewStringf("%s.%s()", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), Swig_name_member(0, getEnumClassPrefix(), symname)));
+ }
+ Delete(newsymname);
+ }
+ }
+ return value;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getEnumName()
+ *
+ * If jnidescriptor is set, inner class names are separated with '$' otherwise a '.'
+ * and the package is also not added to the name.
+ * ----------------------------------------------------------------------------- */
+
+ String *getEnumName(SwigType *t, bool jnidescriptor) {
+ Node *enumname = NULL;
+ Node *n = enumLookup(t);
+ if (n) {
+ enumname = Getattr(n, "enumname");
+ if (!enumname || jnidescriptor) {
+ String *symname = Getattr(n, "sym:name");
+ if (symname) {
+ // Add in class scope when referencing enum if not a global enum
+ String *scopename_prefix = Swig_scopename_prefix(Getattr(n, "name"));
+ String *proxyname = 0;
+ if (scopename_prefix) {
+ proxyname = getProxyName(scopename_prefix, jnidescriptor);
+ }
+ if (proxyname) {
+ const char *class_separator = jnidescriptor ? "$" : ".";
+ enumname = NewStringf("%s%s%s", proxyname, class_separator, symname);
+ } else {
+ // global enum or enum in a namespace
+ String *nspace = Getattr(n, "sym:nspace");
+ if (nspace) {
+ if (package && !jnidescriptor)
+ enumname = NewStringf("%s.%s.%s", package, nspace, symname);
+ else
+ enumname = NewStringf("%s.%s", nspace, symname);
+ } else {
+ enumname = Copy(symname);
+ }
+ }
+ if (!jnidescriptor) {
+ Setattr(n, "enumname", enumname); // Cache it
+ Delete(enumname);
+ }
+ Delete(scopename_prefix);
+ }
+ }
+ }
+
+ return enumname;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * substituteClassname()
+ *
+ * Substitute the special variable $javaclassname with the proxy class name for classes/structs/unions
+ * that SWIG knows about. Also substitutes enums with enum name.
+ * Otherwise use the $descriptor name for the Java class name. Note that the $&javaclassname substitution
+ * is the same as a $&descriptor substitution, ie one pointer added to descriptor name.
+ * Note that the path separator is a '.' unless jnidescriptor is set.
+ * Inputs:
+ * pt - parameter type
+ * tm - typemap contents that might contain the special variable to be replaced
+ * jnidescriptor - if set, inner class names are separated with '$' otherwise a '/' is used for the path separator
+ * Outputs:
+ * tm - typemap contents complete with the special variable substitution
+ * Return:
+ * substitution_performed - flag indicating if a substitution was performed
+ * ----------------------------------------------------------------------------- */
+
+ bool substituteClassname(SwigType *pt, String *tm, bool jnidescriptor = false) {
+ bool substitution_performed = false;
+ SwigType *type = Copy(SwigType_typedef_resolve_all(pt));
+ SwigType *strippedtype = SwigType_strip_qualifiers(type);
+
+ if (Strstr(tm, "$javaclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ substituteClassnameSpecialVariable(classnametype, tm, "$javaclassname", jnidescriptor);
+ substitution_performed = true;
+ Delete(classnametype);
+ }
+ if (Strstr(tm, "$*javaclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ Delete(SwigType_pop(classnametype));
+ if (Len(classnametype) > 0) {
+ substituteClassnameSpecialVariable(classnametype, tm, "$*javaclassname", jnidescriptor);
+ substitution_performed = true;
+ }
+ Delete(classnametype);
+ }
+ if (Strstr(tm, "$&javaclassname")) {
+ SwigType *classnametype = Copy(strippedtype);
+ SwigType_add_pointer(classnametype);
+ substituteClassnameSpecialVariable(classnametype, tm, "$&javaclassname", jnidescriptor);
+ substitution_performed = true;
+ Delete(classnametype);
+ }
+ if (Strstr(tm, "$javainterfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$javainterfacename", jnidescriptor, true);
+ substitution_performed = true;
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$*javainterfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ Delete(SwigType_pop(interfacenametype));
+ if (Len(interfacenametype) > 0) {
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$*javainterfacename", jnidescriptor, true);
+ substitution_performed = true;
+ }
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$&javainterfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ SwigType_add_pointer(interfacenametype);
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$&javainterfacename", jnidescriptor, true);
+ substitution_performed = true;
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$interfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$interfacename", jnidescriptor, false);
+ substitution_performed = true;
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$*interfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ Delete(SwigType_pop(interfacenametype));
+ if (Len(interfacenametype) > 0) {
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$*interfacename", jnidescriptor, false);
+ substitution_performed = true;
+ }
+ Delete(interfacenametype);
+ }
+ if (Strstr(tm, "$&interfacename")) {
+ SwigType *interfacenametype = Copy(strippedtype);
+ SwigType_add_pointer(interfacenametype);
+ substituteInterfacenameSpecialVariable(interfacenametype, tm, "$&interfacename", jnidescriptor, false);
+ substitution_performed = true;
+ Delete(interfacenametype);
+ }
+
+ Delete(strippedtype);
+ Delete(type);
+
+ return substitution_performed;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * substituteClassnameSpecialVariable()
+ * ----------------------------------------------------------------------------- */
+
+ void substituteClassnameSpecialVariable(SwigType *classnametype, String *tm, const char *classnamespecialvariable, bool jnidescriptor) {
+ String *replacementname;
+
+ if (SwigType_isenum(classnametype)) {
+ String *enumname = getEnumName(classnametype, jnidescriptor);
+ if (enumname) {
+ replacementname = Copy(enumname);
+ } else {
+ bool anonymous_enum = (Cmp(classnametype, "enum ") == 0);
+ if (anonymous_enum) {
+ replacementname = NewString("int");
+ } else {
+ // An unknown enum - one that has not been parsed (neither a C enum forward reference nor a definition) or an ignored enum
+ replacementname = NewStringf("SWIGTYPE%s", SwigType_manglestr(classnametype));
+ Replace(replacementname, "enum ", "", DOH_REPLACE_ANY);
+ Setattr(swig_types_hash, replacementname, classnametype);
+ }
+ }
+ } else {
+ String *classname = getProxyName(classnametype, jnidescriptor); // getProxyName() works for pointers to classes too
+ if (classname) {
+ replacementname = Copy(classname);
+ } else {
+ // use $descriptor if SWIG does not know anything about this type. Note that any typedefs are resolved.
+ replacementname = NewStringf("SWIGTYPE%s", SwigType_manglestr(classnametype));
+
+ // Add to hash table so that the type wrapper classes can be created later
+ Setattr(swig_types_hash, replacementname, classnametype);
+ }
+ }
+ if (jnidescriptor)
+ Replaceall(replacementname,".","/");
+ Replaceall(tm, classnamespecialvariable, replacementname);
+
+ Delete(replacementname);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * substituteInterfacenameSpecialVariable()
+ * ----------------------------------------------------------------------------- */
+
+ void substituteInterfacenameSpecialVariable(SwigType *interfacenametype, String *tm, const char *interfacenamespecialvariable, bool jnidescriptor, bool qualified) {
+
+ String *interfacename = getInterfaceName(interfacenametype/*, jnidescriptor*/, qualified);
+ if (interfacename) {
+ String *replacementname = Copy(interfacename);
+
+ if (jnidescriptor)
+ Replaceall(replacementname,".","/");
+ Replaceall(tm, interfacenamespecialvariable, replacementname);
+
+ Delete(replacementname);
+ }
+ }
+
+ /* -----------------------------------------------------------------------------
+ * emitTypeWrapperClass()
+ * ----------------------------------------------------------------------------- */
+
+ void emitTypeWrapperClass(String *classname, SwigType *type) {
+ Node *n = NewHash();
+ Setfile(n, input_file);
+ Setline(n, line_number);
+
+ String *swigtype = NewString("");
+ String *filen = NewStringf("%s%s.java", SWIG_output_directory(), classname);
+ File *f_swigtype = NewFile(filen, "w", SWIG_output_files());
+ if (!f_swigtype) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Append(filenames_list, Copy(filen));
+ Delete(filen);
+ filen = NULL;
+
+ // Start writing out the type wrapper class file
+ emitBanner(f_swigtype);
+
+ if (package)
+ Printf(f_swigtype, "package %s;\n", package);
+
+ // Pure Java baseclass and interfaces
+ const String *pure_baseclass = typemapLookup(n, "javabase", type, WARN_NONE);
+ const String *pure_interfaces = typemapLookup(n, "javainterfaces", type, WARN_NONE);
+
+ // Emit the class
+ Printv(swigtype, typemapLookup(n, "javaimports", type, WARN_NONE), // Import statements
+ "\n", typemapLookup(n, "javaclassmodifiers", type, WARN_JAVA_TYPEMAP_CLASSMOD_UNDEF), // Class modifiers
+ " $javaclassname", // Class name and bases
+ *Char(pure_baseclass) ? " extends " : "", pure_baseclass, *Char(pure_interfaces) ? // Interfaces
+ " implements " : "", pure_interfaces, " {", typemapLookup(n, "javabody", type, WARN_JAVA_TYPEMAP_JAVABODY_UNDEF), // main body of class
+ typemapLookup(n, "javacode", type, WARN_NONE), // extra Java code
+ "}\n", "\n", NIL);
+
+ Replaceall(swigtype, "$javaclassname", classname);
+ Replaceall(swigtype, "$module", module_class_name);
+ Replaceall(swigtype, "$imclassname", imclass_name);
+
+ // For unknown enums
+ Replaceall(swigtype, "$static ", "");
+ Replaceall(swigtype, "$enumvalues", "");
+
+ Printv(f_swigtype, swigtype, NIL);
+
+ Delete(f_swigtype);
+ Delete(swigtype);
+ Delete(n);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * typemapLookup()
+ * n - for input only and must contain info for Getfile(n) and Getline(n) to work
+ * tmap_method - typemap method name
+ * type - typemap type to lookup
+ * warning - warning number to issue if no typemaps found
+ * typemap_attributes - the typemap attributes are attached to this node and will
+ * also be used for temporary storage if non null
+ * return is never NULL, unlike Swig_typemap_lookup()
+ * ----------------------------------------------------------------------------- */
+
+ const String *typemapLookup(Node *n, const_String_or_char_ptr tmap_method, SwigType *type, int warning, Node *typemap_attributes = 0) {
+ Node *node = !typemap_attributes ? NewHash() : typemap_attributes;
+ Setattr(node, "type", type);
+ Setfile(node, Getfile(n));
+ Setline(node, Getline(n));
+ const String *tm = Swig_typemap_lookup(tmap_method, node, "", 0);
+ if (!tm) {
+ tm = empty_string;
+ if (warning != WARN_NONE)
+ Swig_warning(warning, Getfile(n), Getline(n), "No %s typemap defined for %s\n", tmap_method, SwigType_str(type, 0));
+ }
+ if (!typemap_attributes)
+ Delete(node);
+ return tm;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * addThrows()
+ *
+ * Adds exception classes to a throws list. The throws list is the list of classes
+ * that will form the Java throws clause. Mainly for checked exceptions.
+ * ----------------------------------------------------------------------------- */
+
+ void addThrows(Node *n, const String *attribute, Node *parameter) {
+ // Get the comma separated exception classes for the throws clause - held in typemap/feature's "throws" attribute
+ String *throws_attribute = NewStringf("%s:throws", attribute);
+ String *throws = Getattr(parameter, throws_attribute);
+
+ if (throws && Len(throws) > 0) {
+ String *throws_list = Getattr(n, "java:throwslist");
+ if (!throws_list) {
+ throws_list = NewList();
+ Setattr(n, "java:throwslist", throws_list);
+ }
+ // Put the exception classes in the throws clause into a temporary List
+ List *temp_classes_list = Split(throws, ',', INT_MAX);
+
+ // Add the exception classes to the node throws list, but don't duplicate if already in list
+ if (temp_classes_list && Len(temp_classes_list) > 0) {
+ for (Iterator cls = First(temp_classes_list); cls.item; cls = Next(cls)) {
+ String *exception_class = NewString(cls.item);
+ Replaceall(exception_class, " ", ""); // remove spaces
+ Replaceall(exception_class, "\t", ""); // remove tabs
+ if (Len(exception_class) > 0) {
+ // $javaclassname substitution
+ SwigType *pt = Getattr(parameter, "type");
+ substituteClassname(pt, exception_class);
+
+ // Don't duplicate the Java exception class in the throws clause
+ bool found_flag = false;
+ for (Iterator item = First(throws_list); item.item; item = Next(item)) {
+ if (Strcmp(item.item, exception_class) == 0)
+ found_flag = true;
+ }
+ if (!found_flag)
+ Append(throws_list, exception_class);
+ }
+ Delete(exception_class);
+ }
+ }
+ Delete(temp_classes_list);
+ }
+ Delete(throws_attribute);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * generateThrowsClause()
+ *
+ * Generates throws clause for checked exception
+ * ----------------------------------------------------------------------------- */
+
+ void generateThrowsClause(Node *n, String *code) {
+ // Add the throws clause into code
+ List *throws_list = Getattr(n, "java:throwslist");
+ if (throws_list) {
+ Iterator cls = First(throws_list);
+ Printf(code, " throws %s", cls.item);
+ while ((cls = Next(cls)).item)
+ Printf(code, ", %s", cls.item);
+ }
+ }
+
+ /* -----------------------------------------------------------------------------
+ * prematureGarbageCollectionPreventionParameter()
+ *
+ * Get the proxy class name for use in an additional generated parameter. The
+ * additional parameter is added to a native method call purely to prevent
+ * premature garbage collection of proxy classes which pass their C++ class pointer
+ * in a Java long to the JNI layer.
+ * ----------------------------------------------------------------------------- */
+
+ String *prematureGarbageCollectionPreventionParameter(SwigType *t, Parm *p) {
+ String *pgcpp_java_type = 0;
+ String *jtype = NewString(Getattr(p, "tmap:jtype"));
+
+ // Strip C comments
+ String *stripped_jtype = Swig_strip_c_comments(jtype);
+ if (stripped_jtype) {
+ Delete(jtype);
+ jtype = stripped_jtype;
+ }
+
+ // Remove whitespace
+ Replaceall(jtype, " ", "");
+ Replaceall(jtype, "\t", "");
+
+ if (Cmp(jtype, "long") == 0) {
+ if (proxy_flag) {
+ if (!GetFlag(p, "tmap:jtype:nopgcpp") && !nopgcpp_flag) {
+ String *interface_name = getInterfaceName(t, true);
+ pgcpp_java_type = interface_name ? interface_name : getProxyName(t);
+ if (!pgcpp_java_type) {
+ // Look for proxy class parameters passed to C++ layer using non-default typemaps, ie not one of above types
+ String *jstype = NewString(Getattr(p, "tmap:jstype"));
+ if (jstype) {
+ Hash *classes = getClassHash();
+ if (classes) {
+ // Strip C comments
+ String *stripped_jstype = Swig_strip_c_comments(jstype);
+ if (stripped_jstype) {
+ Delete(jstype);
+ jstype = stripped_jstype;
+ }
+ // Remove whitespace
+ Replaceall(jstype, " ", "");
+ Replaceall(jstype, "\t", "");
+
+ Iterator ki;
+ for (ki = First(classes); ki.key; ki = Next(ki)) {
+ Node *cls = ki.item;
+ if (cls && !Getattr(cls, "feature:ignore")) {
+ String *symname = Getattr(cls, "sym:name");
+ if (symname && Strcmp(symname, jstype) == 0) {
+ pgcpp_java_type = symname;
+ }
+ }
+ }
+ }
+ }
+ Delete(jstype);
+ }
+ }
+ }
+ }
+ Delete(jtype);
+ return pgcpp_java_type;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * outputDirectory()
+ *
+ * Return the directory to use for generating Java classes/enums and create the
+ * subdirectory (does not create if language specific outdir does not exist).
+ * ----------------------------------------------------------------------------- */
+
+ String *outputDirectory(String *nspace) {
+ String *output_directory = Copy(SWIG_output_directory());
+ if (nspace) {
+ String *nspace_subdirectory = Copy(nspace);
+ Replaceall(nspace_subdirectory, ".", SWIG_FILE_DELIMITER);
+ String *newdir_error = Swig_new_subdirectory(output_directory, nspace_subdirectory);
+ if (newdir_error) {
+ Printf(stderr, "%s\n", newdir_error);
+ Delete(newdir_error);
+ Exit(EXIT_FAILURE);
+ }
+ Printv(output_directory, nspace_subdirectory, SWIG_FILE_DELIMITER, 0);
+ Delete(nspace_subdirectory);
+ }
+ return output_directory;
+ }
+
+ /*----------------------------------------------------------------------
+ * Start of director methods
+ *--------------------------------------------------------------------*/
+
+ /*----------------------------------------------------------------------
+ * getUpcallJNIMethod()
+ *--------------------------------------------------------------------*/
+
+ String *getUpcallJNIMethod(String *descrip) {
+ static struct {
+ char code;
+ const char *method;
+ } upcall_methods[] = {
+ {
+ 'B', "CallStaticByteMethod"}, {
+ 'C', "CallStaticCharMethod"}, {
+ 'D', "CallStaticDoubleMethod"}, {
+ 'F', "CallStaticFloatMethod"}, {
+ 'I', "CallStaticIntMethod"}, {
+ 'J', "CallStaticLongMethod"}, {
+ 'L', "CallStaticObjectMethod"}, {
+ 'S', "CallStaticShortMethod"}, {
+ 'V', "CallStaticVoidMethod"}, {
+ 'Z', "CallStaticBooleanMethod"}, {
+ '[', "CallStaticObjectMethod"}
+ };
+
+ char code;
+ int i;
+
+ code = *Char(descrip);
+ for (i = 0; i < (int) (sizeof(upcall_methods) / sizeof(upcall_methods[0])); ++i)
+ if (code == upcall_methods[i].code)
+ return NewString(upcall_methods[i].method);
+ return NULL;
+ }
+
+ /*----------------------------------------------------------------------
+ * emitDirectorUpcalls()
+ *--------------------------------------------------------------------*/
+
+ void emitDirectorUpcalls() {
+ if (n_dmethods) {
+ Wrapper *w = NewWrapper();
+ String *jni_imclass_name = makeValidJniName(imclass_name);
+ String *swig_module_init = NewString("swig_module_init");
+ String *swig_module_init_jni = makeValidJniName(swig_module_init);
+ String *dmethod_data = NewString("");
+ int n_methods = 0;
+ Iterator udata_iter;
+
+ udata_iter = First(dmethods_seq);
+ while (udata_iter.item) {
+ UpcallData *udata = udata_iter.item;
+ Printf(dmethod_data, " { \"%s\", \"%s\" }", Getattr(udata, "imclass_method"), Getattr(udata, "imclass_fdesc"));
+ ++n_methods;
+
+ udata_iter = Next(udata_iter);
+
+ if (udata_iter.item)
+ Putc(',', dmethod_data);
+ Putc('\n', dmethod_data);
+ }
+
+ Printf(f_runtime, "namespace Swig {\n");
+ Printf(f_runtime, " namespace {\n");
+ Printf(f_runtime, " jclass jclass_%s = NULL;\n", imclass_name);
+ Printf(f_runtime, " jmethodID director_method_ids[%d];\n", n_methods);
+ Printf(f_runtime, " }\n");
+ Printf(f_runtime, "}\n");
+
+ Printf(w->def, "SWIGEXPORT void JNICALL Java_%s%s_%s(JNIEnv *jenv, jclass jcls) {", jnipackage, jni_imclass_name, swig_module_init_jni);
+ Printf(w->code, "static struct {\n");
+ Printf(w->code, " const char *method;\n");
+ Printf(w->code, " const char *signature;\n");
+ Printf(w->code, "} methods[%d] = {\n", n_methods);
+ Printv(w->code, dmethod_data, NIL);
+ Printf(w->code, "};\n");
+
+ Wrapper_add_local(w, "i", "int i");
+
+ Printf(w->code, "Swig::jclass_%s = (jclass) jenv->NewGlobalRef(jcls);\n", imclass_name);
+ Printf(w->code, "if (!Swig::jclass_%s) return;\n", imclass_name);
+ Printf(w->code, "for (i = 0; i < (int) (sizeof(methods)/sizeof(methods[0])); ++i) {\n");
+ Printf(w->code, " Swig::director_method_ids[i] = jenv->GetStaticMethodID(jcls, methods[i].method, methods[i].signature);\n");
+ Printf(w->code, " if (!Swig::director_method_ids[i]) return;\n");
+ Printf(w->code, "}\n");
+
+ Printf(w->code, "}\n");
+
+ Wrapper_print(w, f_wrappers);
+ Delete(dmethod_data);
+ Delete(swig_module_init_jni);
+ Delete(swig_module_init);
+ Delete(jni_imclass_name);
+ DelWrapper(w);
+ }
+ }
+
+ /*----------------------------------------------------------------------
+ * emitDirectorExtraMethods()
+ *
+ * This is where the director connect method is generated.
+ *--------------------------------------------------------------------*/
+ void emitDirectorExtraMethods(Node *n) {
+ if (!Swig_directorclass(n))
+ return;
+
+ // Output the director connect method:
+ String *jni_imclass_name = makeValidJniName(imclass_name);
+ String *norm_name = SwigType_namestr(Getattr(n, "name"));
+ String *swig_director_connect = Swig_name_member(getNSpace(), getClassPrefix(), "director_connect");
+ String *swig_director_connect_jni = makeValidJniName(swig_director_connect);
+ String *smartptr = Getattr(n, "feature:smartptr");
+ String *dirClassName = directorClassName(n);
+ Wrapper *code_wrap;
+
+ Printf(imclass_class_code, " public final static native void %s(%s obj, long cptr, boolean mem_own, boolean weak_global);\n",
+ swig_director_connect, full_proxy_class_name);
+
+ code_wrap = NewWrapper();
+ Printf(code_wrap->def,
+ "SWIGEXPORT void JNICALL Java_%s%s_%s(JNIEnv *jenv, jclass jcls, jobject jself, jlong objarg, jboolean jswig_mem_own, "
+ "jboolean jweak_global) {\n", jnipackage, jni_imclass_name, swig_director_connect_jni);
+
+ if (smartptr) {
+ Printf(code_wrap->code, " %s *obj = *((%s **)&objarg);\n", smartptr, smartptr);
+ Printf(code_wrap->code, " (void)jcls;\n");
+ Printf(code_wrap->code, " // Keep a local instance of the smart pointer around while we are using the raw pointer\n");
+ Printf(code_wrap->code, " // Avoids using smart pointer specific API.\n");
+ Printf(code_wrap->code, " %s *director = static_cast<%s *>(obj->operator->());\n", dirClassName, dirClassName);
+ }
+ else {
+ Printf(code_wrap->code, " %s *obj = *((%s **)&objarg);\n", norm_name, norm_name);
+ Printf(code_wrap->code, " (void)jcls;\n");
+ Printf(code_wrap->code, " %s *director = static_cast<%s *>(obj);\n", dirClassName, dirClassName);
+ }
+
+ Printf(code_wrap->code, " director->swig_connect_director(jenv, jself, jenv->GetObjectClass(jself), "
+ "(jswig_mem_own == JNI_TRUE), (jweak_global == JNI_TRUE));\n");
+ Printf(code_wrap->code, "}\n");
+
+ Wrapper_print(code_wrap, f_wrappers);
+ DelWrapper(code_wrap);
+
+ Delete(swig_director_connect_jni);
+ Delete(swig_director_connect);
+
+ // Output the swigReleaseOwnership, swigTakeOwnership methods:
+ String *changeown_method_name = Swig_name_member(getNSpace(), getClassPrefix(), "change_ownership");
+ String *changeown_jnimethod_name = makeValidJniName(changeown_method_name);
+
+ Printf(imclass_class_code, " public final static native void %s(%s obj, long cptr, boolean take_or_release);\n", changeown_method_name, full_proxy_class_name);
+
+ code_wrap = NewWrapper();
+ Printf(code_wrap->def,
+ "SWIGEXPORT void JNICALL Java_%s%s_%s(JNIEnv *jenv, jclass jcls, jobject jself, jlong objarg, jboolean jtake_or_release) {\n",
+ jnipackage, jni_imclass_name, changeown_jnimethod_name);
+
+ if (Len(smartptr)) {
+ Printf(code_wrap->code, " %s *obj = *((%s **)&objarg);\n", smartptr, smartptr);
+ Printf(code_wrap->code, " // Keep a local instance of the smart pointer around while we are using the raw pointer\n");
+ Printf(code_wrap->code, " // Avoids using smart pointer specific API.\n");
+ Printf(code_wrap->code, " %s *director = dynamic_cast<%s *>(obj->operator->());\n", dirClassName, dirClassName);
+ } else {
+ Printf(code_wrap->code, " %s *obj = *((%s **)&objarg);\n", norm_name, norm_name);
+ Printf(code_wrap->code, " %s *director = dynamic_cast<%s *>(obj);\n", dirClassName, dirClassName);
+ }
+
+ Printf(code_wrap->code, " (void)jcls;\n");
+ Printf(code_wrap->code, " if (director) {\n");
+ Printf(code_wrap->code, " director->swig_java_change_ownership(jenv, jself, jtake_or_release ? true : false);\n");
+ Printf(code_wrap->code, " }\n");
+ Printf(code_wrap->code, "}\n");
+
+ Wrapper_print(code_wrap, f_wrappers);
+ DelWrapper(code_wrap);
+
+ Delete(changeown_method_name);
+ Delete(changeown_jnimethod_name);
+ Delete(norm_name);
+ Delete(dirClassName);
+ Delete(jni_imclass_name);
+ }
+
+ /*----------------------------------------------------------------------
+ * emitCodeTypemap()
+ *
+ * Output a code typemap that uses $methodname and $jnicall, as used
+ * in the directordisconnect, director_release and director_take
+ * typemaps.
+ *--------------------------------------------------------------------*/
+
+ void emitCodeTypemap(Node *n, bool derived, SwigType *lookup_type, const String *typemap, const String *methodname, const String *jnicall) {
+ const String *tm = NULL;
+ Node *tmattrs = NewHash();
+ String *lookup_tmname = NewString(typemap);
+ String *method_attr_name;
+ String *method_attr;
+
+ if (derived) {
+ Append(lookup_tmname, "_derived");
+ }
+
+ tm = typemapLookup(n, lookup_tmname, lookup_type, WARN_NONE, tmattrs);
+ method_attr_name = NewStringf("tmap:%s:%s", lookup_tmname, methodname);
+ method_attr = Getattr(tmattrs, method_attr_name);
+
+ if (*Char(tm)) {
+ if (method_attr) {
+ String *codebody = Copy(tm);
+ Replaceall(codebody, "$methodname", method_attr);
+ Replaceall(codebody, "$jnicall", jnicall);
+ Append(proxy_class_def, codebody);
+ Delete(codebody);
+ } else {
+ Swig_error(input_file, line_number, "No %s method name attribute for %s\n", lookup_tmname, proxy_class_name);
+ }
+ } else {
+ Swig_error(input_file, line_number, "No %s typemap for %s\n", lookup_tmname, proxy_class_name);
+ }
+
+ Delete(tmattrs);
+ Delete(lookup_tmname);
+ // Delete(method_attr);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * substitutePackagePath()
+ *
+ * Replace $packagepath using the javapackage typemap associated with passed
+ * parm or global package if p is 0. "$packagepath/" is replaced with "" if
+ * no package is set. Note that the path separator is a '/'.
+ * ----------------------------------------------------------------------------- */
+
+ void substitutePackagePath(String *text, Parm *p) {
+ String *pkg_path= 0;
+
+ if (p)
+ pkg_path = Swig_typemap_lookup("javapackage", p, "", 0);
+ if (!pkg_path || Len(pkg_path) == 0)
+ pkg_path = Copy(package_path);
+
+ if (Len(pkg_path) > 0) {
+ Replaceall(pkg_path, ".", "/");
+ Replaceall(text, "$packagepath", pkg_path);
+ } else {
+ Replaceall(text, "$packagepath/", empty_string);
+ Replaceall(text, "$packagepath", empty_string);
+ }
+ Delete(pkg_path);
+ }
+
+ /* ---------------------------------------------------------------
+ * Canonicalize the JNI field descriptor
+ *
+ * Replace the $packagepath and $javaclassname family of special
+ * variables with the desired package and Java proxy name as
+ * required in the JNI field descriptors.
+ *
+ * !!SFM!! If $packagepath occurs in the field descriptor, but
+ * package_path isn't set (length == 0), then strip it and the
+ * optional trailing '/' from the resulting name.
+ *
+ * --------------------------------------------------------------- */
+
+ String *canonicalizeJNIDescriptor(String *descriptor_in, Parm *p) {
+ SwigType *type = Getattr(p, "type");
+ String *descriptor_out = Copy(descriptor_in);
+
+ substituteClassname(type, descriptor_out, true);
+ substitutePackagePath(descriptor_out, p);
+
+ return descriptor_out;
+ }
+
+ /* ---------------------------------------------------------------
+ * classDirectorMethod()
+ *
+ * Emit a virtual director method to pass a method call on to the
+ * underlying Java object.
+ *
+ * --------------------------------------------------------------- */
+
+ int classDirectorMethod(Node *n, Node *parent, String *super) {
+ String *c_classname = Getattr(parent, "name");
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *returntype = Getattr(n, "type");
+ String *overloaded_name = getOverloadedName(n);
+ String *storage = Getattr(n, "storage");
+ String *value = Getattr(n, "value");
+ String *decl = Getattr(n, "decl");
+ String *declaration = NewString("");
+ String *tm;
+ Parm *p;
+ int i;
+ Wrapper *w = NewWrapper();
+ ParmList *l = Getattr(n, "parms");
+ bool is_void = !(Cmp(returntype, "void"));
+ String *qualified_return = 0;
+ bool pure_virtual = (!(Cmp(storage, "virtual")) && !(Cmp(value, "0")));
+ int status = SWIG_OK;
+ bool output_director = true;
+ String *dirclassname = directorClassName(parent);
+ String *qualified_name = NewStringf("%s::%s", dirclassname, name);
+ String *jnidesc = NewString("");
+ String *classdesc = NewString("");
+ String *jniret_desc = NewString("");
+ String *classret_desc = NewString("");
+ SwigType *c_ret_type = NULL;
+ String *jupcall_args = NewString("swigjobj");
+ String *imclass_dmethod;
+ String *callback_def = NewString("");
+ String *callback_code = NewString("");
+ String *imcall_args = NewString("");
+ int classmeth_off = curr_class_dmethod - first_class_dmethod;
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+ String *qualified_classname = getProxyName(getClassName());
+
+ // Kludge Alert: functionWrapper sets sym:overload properly, but it
+ // isn't at this point, so we have to manufacture it ourselves. At least
+ // we're consistent with the sym:overload name in functionWrapper. (?? when
+ // does the overloaded method name get set?)
+
+ imclass_dmethod = NewStringf("%s", Swig_name_member(getNSpace(), dirclassname, overloaded_name));
+
+ qualified_return = SwigType_rcaststr(returntype, "c_result");
+
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ String *base_typename = SwigType_base(returntype);
+ String *resolved_typename = SwigType_typedef_resolve_all(base_typename);
+ Symtab *symtab = Getattr(n, "sym:symtab");
+ Node *typenode = Swig_symbol_clookup(resolved_typename, symtab);
+
+ if (SwigType_ispointer(returntype) || (typenode && Getattr(typenode, "abstracts"))) {
+ /* initialize pointers to something sane. Same for abstract
+ classes when a reference is returned. */
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ } else {
+ /* If returning a reference, initialize the pointer to a sane
+ default - if a Java exception occurs, then the pointer returns
+ something other than a NULL-initialized reference. */
+ SwigType *noref_type = SwigType_del_reference(Copy(returntype));
+ String *noref_ltype = SwigType_lstr(noref_type, 0);
+ String *return_ltype = SwigType_lstr(returntype, 0);
+
+ Wrapper_add_localv(w, "result_default", "static", noref_ltype, "result_default", NIL);
+ Wrapper_add_localv(w, "c_result", return_ltype, "c_result", NIL);
+ Printf(w->code, "result_default = SwigValueInit< %s >();\n", noref_ltype);
+ Printf(w->code, "c_result = &result_default;\n");
+ Delete(return_ltype);
+ Delete(noref_ltype);
+ Delete(noref_type);
+ }
+
+ Delete(base_typename);
+ Delete(resolved_typename);
+ }
+ } else {
+ SwigType *vt;
+
+ vt = cplus_value_type(returntype);
+ if (!vt) {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), NIL);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(vt, "c_result"), NIL);
+ Delete(vt);
+ }
+ }
+ }
+
+ /* Create the intermediate class wrapper */
+ tm = Swig_typemap_lookup("jtype", n, "", 0);
+ if (tm) {
+ Printf(callback_def, " public static %s %s(%s jself", tm, imclass_dmethod, qualified_classname);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JTYPE_UNDEF, input_file, line_number, "No jtype typemap defined for %s\n", SwigType_str(returntype, 0));
+ }
+
+ String *cdesc = NULL;
+ SwigType *covariant = Getattr(n, "covariant");
+ SwigType *adjustedreturntype = covariant ? covariant : returntype;
+ Parm *adjustedreturntypeparm = NewParmNode(adjustedreturntype, n);
+
+ if (Swig_typemap_lookup("directorin", adjustedreturntypeparm, "", 0)
+ && (cdesc = Getattr(adjustedreturntypeparm, "tmap:directorin:descriptor"))) {
+
+ // Note that in the case of polymorphic (covariant) return types, the
+ // method's return type is changed to be the base of the C++ return
+ // type
+ String *jnidesc_canon = canonicalizeJNIDescriptor(cdesc, adjustedreturntypeparm);
+ Append(classret_desc, jnidesc_canon);
+ Delete(jnidesc_canon);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number, "No or improper directorin typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ /* Get the JNI field descriptor for this return type, add the JNI field descriptor
+ to jniret_desc */
+ if ((c_ret_type = Swig_typemap_lookup("jni", n, "", 0))) {
+ Parm *tp = NewParmNode(c_ret_type, n);
+
+ if (!is_void && !ignored_method) {
+ String *jretval_decl = NewStringf("%s jresult", c_ret_type);
+ Wrapper_add_localv(w, "jresult", jretval_decl, "= 0", NIL);
+ Delete(jretval_decl);
+ }
+
+ String *jdesc = NULL;
+ if (Swig_typemap_lookup("directorin", tp, "", 0)
+ && (jdesc = Getattr(tp, "tmap:directorin:descriptor"))) {
+
+ // Objects marshalled passing a Java class across JNI boundary use jobject - the nouse flag indicates this
+ // We need the specific Java class name instead of the generic 'Ljava/lang/Object;'
+ if (GetFlag(tp, "tmap:directorin:nouse"))
+ jdesc = cdesc;
+ String *jnidesc_canon = canonicalizeJNIDescriptor(jdesc, tp);
+ Append(jniret_desc, jnidesc_canon);
+ Delete(jnidesc_canon);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
+ "No or improper directorin typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(c_ret_type, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ Delete(tp);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JNI_UNDEF, input_file, line_number, "No jni typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ Delete(adjustedreturntypeparm);
+
+ Swig_director_parms_fixup(l);
+
+ /* Attach the standard typemaps */
+ Swig_typemap_attach_parms("out", l, 0);
+ Swig_typemap_attach_parms("jni", l, 0);
+ Swig_typemap_attach_parms("jtype", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("javadirectorin", l, 0);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ if (!ignored_method) {
+ /* Add Java environment pointer to wrapper */
+ String *jenvstr = NewString("jenv");
+ String *jobjstr = NewString("swigjobj");
+
+ Wrapper_add_localv(w, "swigjnienv", "JNIEnvWrapper", "swigjnienv(this)", NIL, NIL);
+ Wrapper_add_localv(w, jenvstr, "JNIEnv *", jenvstr, "= swigjnienv.getJNIEnv()", NIL);
+ Wrapper_add_localv(w, jobjstr, "jobject", jobjstr, "= (jobject) NULL", NIL);
+ Delete(jenvstr);
+ Delete(jobjstr);
+
+ /* Preamble code */
+ Printf(w->code, "if (!swig_override[%d]) {\n", classmeth_off);
+ }
+
+ if (!pure_virtual) {
+ String *super_call = Swig_method_call(super, l);
+ if (is_void) {
+ Printf(w->code, "%s;\n", super_call);
+ if (!ignored_method)
+ Printf(w->code, "return;\n");
+ } else {
+ Printf(w->code, "return %s;\n", super_call);
+ }
+ Delete(super_call);
+ } else {
+ Printf(w->code, "SWIG_JavaThrowException(JNIEnvWrapper(this).getJNIEnv(), SWIG_JavaDirectorPureVirtual, ");
+ Printf(w->code, "\"Attempted to invoke pure virtual method %s::%s.\");\n", SwigType_namestr(c_classname), SwigType_namestr(name));
+
+ /* Make sure that we return something in the case of a pure
+ * virtual method call for syntactical reasons. */
+ if (!is_void)
+ Printf(w->code, "return %s;", qualified_return);
+ else if (!ignored_method)
+ Printf(w->code, "return;\n");
+ }
+
+ if (!ignored_method) {
+ Printf(w->code, "}\n");
+ Printf(w->code, "swigjobj = swig_get_self(jenv);\n");
+ Printf(w->code, "if (swigjobj && jenv->IsSameObject(swigjobj, NULL) == JNI_FALSE) {\n");
+ }
+
+ /* Start the Java field descriptor for the intermediate class's upcall (insert jself object) */
+ Parm *tp = NewParmNode(c_classname, n);
+ String *jdesc;
+
+ if ((tm = Swig_typemap_lookup("directorin", tp, "", 0))
+ && (jdesc = Getattr(tp, "tmap:directorin:descriptor"))) {
+ String *jni_canon = canonicalizeJNIDescriptor(jdesc, tp);
+ Append(jnidesc, jni_canon);
+ Delete(jni_canon);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
+ "No or improper directorin typemap for type %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ Delete(tp);
+
+ /* Go through argument list, convert from native to Java */
+ for (i = 0, p = l; p; ++i) {
+ /* Is this superfluous? */
+ while (checkAttribute(p, "tmap:directorin:numinputs", "0")) {
+ p = Getattr(p, "tmap:directorin:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = makeParameterName(n, p, i, false);
+ String *c_param_type = NULL;
+ String *c_decl = NewString("");
+ String *arg = NewString("");
+
+ Printf(arg, "j%s", ln);
+
+ /* Add various typemap's 'throws' clauses */
+ addThrows(n, "tmap:directorin", p);
+ addThrows(n, "tmap:out", p);
+
+ /* And add to the upcall args */
+ Printf(jupcall_args, ", %s", arg);
+
+ /* Get parameter's intermediary C type */
+ if ((c_param_type = Getattr(p, "tmap:jni"))) {
+ Parm *tp = NewParm(c_param_type, Getattr(p, "name"), n);
+ String *desc_tm = NULL, *jdesc = NULL, *cdesc = NULL;
+
+ /* Add to local variables */
+ Printf(c_decl, "%s %s", c_param_type, arg);
+ if (!ignored_method)
+ Wrapper_add_localv(w, arg, c_decl, (!(SwigType_ispointer(pt) || SwigType_isreference(pt)) ? "" : "= 0"), NIL);
+
+ /* Add input marshalling code and update JNI field descriptor */
+ if ((desc_tm = Swig_typemap_lookup("directorin", tp, "", 0))
+ && (jdesc = Getattr(tp, "tmap:directorin:descriptor"))
+ && (tm = Getattr(p, "tmap:directorin"))
+ && (cdesc = Getattr(p, "tmap:directorin:descriptor"))) {
+
+ // Objects marshalled by passing a Java class across the JNI boundary use jobject as the JNI type -
+ // the nouse flag indicates this. We need the specific Java class name instead of the generic 'Ljava/lang/Object;'
+ if (GetFlag(tp, "tmap:directorin:nouse"))
+ jdesc = cdesc;
+ String *jni_canon = canonicalizeJNIDescriptor(jdesc, tp);
+ Append(jnidesc, jni_canon);
+ Delete(jni_canon);
+
+ Setattr(p, "emit:directorinput", arg);
+ Replaceall(tm, "$input", arg);
+ Replaceall(tm, "$owner", "0");
+
+ if (Len(tm))
+ if (!ignored_method)
+ Printf(w->code, "%s\n", tm);
+
+ /* Add parameter to the intermediate class code if generating the
+ * intermediate's upcall code */
+ if ((tm = Getattr(p, "tmap:jtype"))) {
+ String *din = Copy(Getattr(p, "tmap:javadirectorin"));
+ addThrows(n, "tmap:javadirectorin", p);
+
+ if (din) {
+ Replaceall(din, "$module", module_class_name);
+ Replaceall(din, "$imclassname", imclass_name);
+ substituteClassname(pt, din);
+ Replaceall(din, "$jniinput", ln);
+
+ if (i > 0)
+ Printf(imcall_args, ", ");
+ Printf(callback_def, ", %s %s", tm, ln);
+
+ if (Cmp(din, ln)) {
+ Printv(imcall_args, din, NIL);
+ } else
+ Printv(imcall_args, ln, NIL);
+
+ jni_canon = canonicalizeJNIDescriptor(cdesc, p);
+ Append(classdesc, jni_canon);
+ Delete(jni_canon);
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JAVADIRECTORIN_UNDEF, input_file, line_number, "No javadirectorin typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JTYPE_UNDEF, input_file, line_number, "No jtype typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ p = Getattr(p, "tmap:directorin:next");
+
+ Delete(desc_tm);
+ } else {
+ if (!desc_tm) {
+ Swig_warning(WARN_JAVA_TYPEMAP_JAVADIRECTORIN_UNDEF, input_file, line_number,
+ "No or improper directorin typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(c_param_type, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ p = nextSibling(p);
+ } else if (!jdesc) {
+ Swig_warning(WARN_JAVA_TYPEMAP_DIRECTORIN_NODESC, input_file, line_number,
+ "Missing JNI descriptor in directorin typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(c_param_type, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ p = Getattr(p, "tmap:directorin:next");
+ } else if (!tm) {
+ Swig_warning(WARN_JAVA_TYPEMAP_JAVADIRECTORIN_UNDEF, input_file, line_number,
+ "No or improper directorin typemap defined for argument %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ p = nextSibling(p);
+ } else if (!cdesc) {
+ Swig_warning(WARN_JAVA_TYPEMAP_DIRECTORIN_NODESC, input_file, line_number,
+ "Missing JNI descriptor in directorin typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ p = Getattr(p, "tmap:directorin:next");
+ }
+
+ output_director = false;
+ }
+
+ } else {
+ Swig_warning(WARN_JAVA_TYPEMAP_JNI_UNDEF, input_file, line_number, "No jni typemap defined for %s for use in %s::%s (skipping director method)\n",
+ SwigType_str(pt, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ p = nextSibling(p);
+ }
+
+ Delete(arg);
+ Delete(c_decl);
+ Delete(ln);
+ }
+
+ /* header declaration, start wrapper definition */
+ String *target;
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Add any exception specifications to the methods in the director class
+ // Get any Java exception classes in the throws typemap
+ ParmList *throw_parm_list = NULL;
+
+ // May need to add Java throws clause to director methods if %catches defined
+ // Get any Java exception classes in the throws typemap
+ ParmList *catches_list = Getattr(n, "catchlist");
+ if (catches_list) {
+ Swig_typemap_attach_parms("throws", catches_list, 0);
+ Swig_typemap_attach_parms("directorthrows", catches_list, 0);
+ for (p = catches_list; p; p = nextSibling(p)) {
+ addThrows(n, "tmap:throws", p);
+ }
+ }
+
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list) {
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ Swig_typemap_attach_parms("directorthrows", throw_parm_list, 0);
+ }
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ // %catches replaces the specified exception specification
+ if (!catches_list) {
+ addThrows(n, "tmap:throws", p);
+ }
+
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+
+ Printf(w->def, "%s", SwigType_str(Getattr(p, "type"), 0));
+ Printf(declaration, "%s", SwigType_str(Getattr(p, "type"), 0));
+ }
+ }
+
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+
+ /* Emit the intermediate class's upcall to the actual class */
+
+ String *upcall = NewStringf("jself.%s(%s)", symname, imcall_args);
+
+ // Handle exception classes specified in the "except" feature's "throws" attribute
+ addThrows(n, "feature:except", n);
+
+ if (!is_void) {
+ if ((tm = Swig_typemap_lookup("javadirectorout", n, "", 0))) {
+ addThrows(n, "tmap:javadirectorout", n);
+ substituteClassname(returntype, tm);
+ Replaceall(tm, "$javacall", upcall);
+
+ Printf(callback_code, " return %s;\n", tm);
+ }
+
+ if ((tm = Swig_typemap_lookup("out", n, "", 0)))
+ addThrows(n, "tmap:out", n);
+
+ Delete(tm);
+ } else
+ Printf(callback_code, " %s;\n", upcall);
+
+ Printf(callback_code, " }\n");
+ Delete(upcall);
+
+ /* Finish off the inherited upcall's definition */
+ Putc(')', callback_def);
+ generateThrowsClause(n, callback_def);
+ Printf(callback_def, " {\n");
+
+ if (!ignored_method) {
+ /* Emit the actual upcall through */
+ String *imclass_desc = NewStringf("(%s)%s", jnidesc, jniret_desc);
+ String *class_desc = NewStringf("(%s)%s", classdesc, classret_desc);
+ UpcallData *udata = addUpcallMethod(imclass_dmethod, symname, imclass_desc, class_desc, decl);
+ String *methid = Getattr(udata, "imclass_methodidx");
+ String *methop = getUpcallJNIMethod(jniret_desc);
+
+ if (!is_void)
+ Printf(w->code, "jresult = (%s) ", c_ret_type);
+
+ Printf(w->code, "jenv->%s(Swig::jclass_%s, Swig::director_method_ids[%s], %s);\n", methop, imclass_name, methid, jupcall_args);
+
+ // Generate code to handle any Java exception thrown by director delegation
+ directorExceptHandler(n, catches_list ? catches_list : throw_parm_list, w);
+
+ if (!is_void) {
+ String *jresult_str = NewString("jresult");
+ String *result_str = NewString("c_result");
+
+ /* Copy jresult into c_result... */
+ if ((tm = Swig_typemap_lookup("directorout", n, result_str, w))) {
+ addThrows(n, "tmap:directorout", n);
+ Replaceall(tm, "$input", jresult_str);
+ Replaceall(tm, "$result", result_str);
+ Printf(w->code, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s used in %s::%s (skipping director method)\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ output_director = false;
+ }
+
+ Delete(jresult_str);
+ Delete(result_str);
+ }
+
+ /* Marshal outputs */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout"))) {
+ addThrows(n, "tmap:directorargout", p);
+ Replaceall(tm, "$result", makeParameterName(n, p, i, false));
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ Delete(imclass_desc);
+ Delete(class_desc);
+
+ /* Terminate wrapper code */
+ Printf(w->code, "} else {\n");
+ Printf(w->code, "SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"null upcall object in %s::%s \");\n",
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ Printf(w->code, "}\n");
+
+ Printf(w->code, "if (swigjobj) jenv->DeleteLocalRef(swigjobj);\n");
+
+ if (!is_void)
+ Printf(w->code, "return %s;", qualified_return);
+ }
+
+ Printf(w->code, "}");
+
+ // We expose virtual protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewString("");
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+
+ /* emit the director method */
+ if (status == SWIG_OK && output_director) {
+ if (!is_void) {
+ Replaceall(w->code, "$null", qualified_return);
+ } else {
+ Replaceall(w->code, "$null", "");
+ }
+ if (!GetFlag(n, "feature:ignore"))
+ Printv(imclass_directors, callback_def, callback_code, NIL);
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+
+ Delete(inline_extra_method);
+ Delete(qualified_return);
+ Delete(jnidesc);
+ Delete(c_ret_type);
+ Delete(jniret_desc);
+ Delete(declaration);
+ Delete(callback_def);
+ Delete(callback_code);
+ DelWrapper(w);
+
+ return status;
+ }
+
+ /* ------------------------------------------------------------
+ * directorExceptHandler()
+ *
+ * Emit code to map Java exceptions back to C++ exceptions when
+ * feature("director:except") is applied to a method node.
+ * This is generated after the Java method upcall.
+ * ------------------------------------------------------------ */
+
+ void directorExceptHandler(Node *n, ParmList *throw_parm_list, Wrapper *w) {
+
+ String *directorexcept = Getattr(n, "feature:director:except");
+ if (!directorexcept) {
+ directorexcept = NewString("");
+ Printf(directorexcept, "jthrowable $error = jenv->ExceptionOccurred();\n");
+ Printf(directorexcept, "if ($error) {");
+ Printf(directorexcept, "$directorthrowshandlers\n");
+ Printf(directorexcept, " Swig::DirectorException::raise(jenv, $error);\n");
+ Printf(directorexcept, "}\n");
+ } else {
+ directorexcept = Copy(directorexcept);
+ }
+
+ // Can explicitly disable director:except by setting to "" or "0"
+ if (Len(directorexcept) > 0 && Cmp(directorexcept, "0") != 0) {
+
+ // Replace $packagepath
+ substitutePackagePath(directorexcept, 0);
+
+ // Replace $directorthrowshandlers with any defined typemap handlers (or nothing)
+ if (Strstr(directorexcept, "$directorthrowshandlers")) {
+ String *directorthrowshandlers_code = NewString("");
+
+ for (Parm *p = throw_parm_list; p; p = nextSibling(p)) {
+ String *tm = Getattr(p, "tmap:directorthrows");
+
+ if (tm) {
+ // replace $packagepath/$javaclassname
+ String *directorthrows = canonicalizeJNIDescriptor(tm, p);
+ Printv(directorthrowshandlers_code, directorthrows, NIL);
+ Delete(directorthrows);
+ } else {
+ String *t = Getattr(p,"type");
+ Swig_warning(WARN_TYPEMAP_DIRECTORTHROWS_UNDEF, Getfile(n), Getline(n), "No directorthrows typemap defined for %s\n", SwigType_str(t, 0));
+ }
+ }
+ Replaceall(directorexcept, "$directorthrowshandlers", directorthrowshandlers_code);
+ Delete(directorthrowshandlers_code);
+ }
+
+ Replaceall(directorexcept, "$error", "swigerror");
+ Printf(w->code, " %s\n", directorexcept);
+ }
+ Delete(directorexcept);
+ }
+
+ /* ------------------------------------------------------------
+ * directorPrefixArgs()
+ * ------------------------------------------------------------ */
+
+ void directorPrefixArgs(Node *n) {
+ Parm *p;
+
+ /* Need to prepend 'jenv' to the director constructor's argument list */
+
+ String *jenv_type = NewString("JNIEnv");
+
+ SwigType_add_pointer(jenv_type);
+
+ p = NewParm(jenv_type, NewString("jenv"), n);
+ Setattr(p, "arg:byname", "1");
+ set_nextSibling(p, NULL);
+
+ Setattr(n, "director:prefix_args", p);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorConstructor()
+ * ------------------------------------------------------------ */
+
+ int classDirectorConstructor(Node *n) {
+ Node *parent = parentNode(n);
+ String *decl = Getattr(n, "decl");
+ String *supername = Swig_class_name(parent);
+ String *dirclassname = directorClassName(parent);
+ String *sub = NewString("");
+ Parm *p;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms;
+ int argidx = 0;
+
+ /* Assign arguments to superclass's parameters, if not already done */
+ for (p = superparms; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+
+ if (!pname) {
+ pname = NewStringf("arg%d", argidx++);
+ Setattr(p, "name", pname);
+ }
+ }
+
+ /* insert jenv prefix argument */
+ parms = CopyParmList(superparms);
+
+ String *jenv_type = NewString("JNIEnv");
+ SwigType_add_pointer(jenv_type);
+ p = NewParm(jenv_type, NewString("jenv"), n);
+ set_nextSibling(p, parms);
+ parms = p;
+
+ directorPrefixArgs(n);
+
+ if (!Getattr(n, "defaultargs")) {
+ /* constructor */
+ {
+ String *basetype = Getattr(parent, "classtype");
+ String *target = Swig_method_decl(0, decl, dirclassname, parms, 0);
+ String *call = Swig_csuperclass_call(0, basetype, superparms);
+ String *classtype = SwigType_namestr(Getattr(n, "name"));
+
+ Printf(f_directors, "%s::%s : %s, %s {\n", dirclassname, target, call, Getattr(parent, "director:ctor"));
+ Printf(f_directors, "}\n\n");
+
+ Delete(classtype);
+ Delete(target);
+ Delete(call);
+ }
+
+ /* constructor header */
+ {
+ String *target = Swig_method_decl(0, decl, dirclassname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+
+ Delete(sub);
+ Delete(supername);
+ Delete(jenv_type);
+ Delete(parms);
+ Delete(dirclassname);
+ return Language::classDirectorConstructor(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorDefaultConstructor()
+ * ------------------------------------------------------------ */
+
+ int classDirectorDefaultConstructor(Node *n) {
+ String *classname = Swig_class_name(n);
+ String *classtype = SwigType_namestr(Getattr(n, "name"));
+ String *dirClassName = directorClassName(n);
+ Wrapper *w = NewWrapper();
+
+ Printf(w->def, "%s::%s(JNIEnv *jenv) : %s {", dirClassName, dirClassName, Getattr(n, "director:ctor"));
+ Printf(w->code, "}\n");
+ Wrapper_print(w, f_directors);
+
+ Printf(f_directors_h, " %s(JNIEnv *jenv);\n", dirClassName);
+ DelWrapper(w);
+ Delete(classtype);
+ Delete(classname);
+ Delete(dirClassName);
+ directorPrefixArgs(n);
+ return Language::classDirectorDefaultConstructor(n);
+ }
+
+
+ /* ------------------------------------------------------------
+ * classDirectorInit()
+ * ------------------------------------------------------------ */
+
+ int classDirectorInit(Node *n) {
+ Delete(none_comparison);
+ none_comparison = NewString(""); // not used
+
+ Delete(director_ctor_code);
+ director_ctor_code = NewString("$director_new");
+
+ directorDeclaration(n);
+
+ Printf(f_directors_h, "%s {\n", Getattr(n, "director:decl"));
+ Printf(f_directors_h, "\npublic:\n");
+ Printf(f_directors_h, " void swig_connect_director(JNIEnv *jenv, jobject jself, jclass jcls, bool swig_mem_own, bool weak_global);\n");
+
+ /* Keep track of the director methods for this class */
+ first_class_dmethod = curr_class_dmethod = n_dmethods;
+
+ return Language::classDirectorInit(n);
+ }
+
+ /* ----------------------------------------------------------------------
+ * classDirectorDestructor()
+ * ---------------------------------------------------------------------- */
+
+ int classDirectorDestructor(Node *n) {
+ Node *current_class = getCurrentClass();
+ String *full_classname = Getattr(current_class, "name");
+ String *classname = Swig_class_name(current_class);
+ String *dirClassName = directorClassName(current_class);
+ Wrapper *w = NewWrapper();
+
+ if (Getattr(n, "noexcept")) {
+ Printf(f_directors_h, " virtual ~%s() noexcept;\n", dirClassName);
+ Printf(w->def, "%s::~%s() noexcept {\n", dirClassName, dirClassName);
+ } else if (Getattr(n, "throw")) {
+ Printf(f_directors_h, " virtual ~%s() throw();\n", dirClassName);
+ Printf(w->def, "%s::~%s() throw() {\n", dirClassName, dirClassName);
+ } else {
+ Printf(f_directors_h, " virtual ~%s();\n", dirClassName);
+ Printf(w->def, "%s::~%s() {\n", dirClassName, dirClassName);
+ }
+
+ /* Ensure that correct directordisconnect typemap's method name is called
+ * here: */
+
+ Node *disconn_attr = NewHash();
+ String *disconn_methodname = NULL;
+
+ typemapLookup(n, "directordisconnect", full_classname, WARN_NONE, disconn_attr);
+ disconn_methodname = Getattr(disconn_attr, "tmap:directordisconnect:methodname");
+
+ Printv(w->code, " swig_disconnect_director_self(\"", disconn_methodname, "\");\n", "}\n", NIL);
+
+ Wrapper_print(w, f_directors);
+
+ DelWrapper(w);
+ Delete(disconn_attr);
+ Delete(classname);
+ Delete(dirClassName);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorEnd()
+ * ------------------------------------------------------------ */
+
+ int classDirectorEnd(Node *n) {
+ String *full_classname = Getattr(n, "name");
+ String *classname = getProxyName(full_classname, true);
+ String *director_classname = directorClassName(n);
+ String *internal_classname;
+
+ Wrapper *w = NewWrapper();
+
+ if (Len(package_path) > 0)
+ internal_classname = NewStringf("%s/%s", package_path, classname);
+ else
+ internal_classname = NewStringf("%s", classname);
+
+ // If the namespace is multiple levels, the result of getNSpace() will have inserted
+ // .'s to delimit namespaces, so we need to replace those with /'s
+ Replace(internal_classname, NSPACE_SEPARATOR, "/", DOH_REPLACE_ANY);
+
+ Printf(w->def, "void %s::swig_connect_director(JNIEnv *jenv, jobject jself, jclass jcls, bool swig_mem_own, bool weak_global) {", director_classname);
+
+ Printf(w->def, "static jclass baseclass = swig_new_global_ref(jenv, \"%s\");\n", internal_classname);
+ Printf(w->def, "if (!baseclass) return;\n");
+
+ if (first_class_dmethod != curr_class_dmethod) {
+ Printf(w->def, "static SwigDirectorMethod methods[] = {\n");
+
+ for (int i = first_class_dmethod; i < curr_class_dmethod; ++i) {
+ UpcallData *udata = Getitem(dmethods_seq, i);
+
+ Printf(w->def, "SwigDirectorMethod(jenv, baseclass, \"%s\", \"%s\")", Getattr(udata, "method"), Getattr(udata, "fdesc"));
+ if (i != curr_class_dmethod - 1)
+ Putc(',', w->def);
+ Putc('\n', w->def);
+ }
+
+ Printf(w->def, "};");
+ }
+
+ Printf(w->code, "if (swig_set_self(jenv, jself, swig_mem_own, weak_global)) {\n");
+
+ int n_methods = curr_class_dmethod - first_class_dmethod;
+
+ if (n_methods) {
+ /* Emit the swig_overrides() method and the swig_override array */
+ Printf(f_directors_h, "public:\n");
+ Printf(f_directors_h, " bool swig_overrides(int n) {\n");
+ Printf(f_directors_h, " return (n < %d ? swig_override[n] : false);\n", n_methods);
+ Printf(f_directors_h, " }\n");
+ Printf(f_directors_h, "protected:\n");
+ Printf(f_directors_h, " Swig::BoolArray<%d> swig_override;\n", n_methods);
+
+ /* Emit the code to look up the class's methods, initialize the override array */
+
+ Printf(w->code, " bool derived = (jenv->IsSameObject(baseclass, jcls) ? false : true);\n");
+ Printf(w->code, " for (int i = 0; i < %d; ++i) {\n", n_methods);
+ // Generally, derived classes have a mix of overridden and
+ // non-overridden methods and it is worth making a GetMethodID
+ // check during initialization to determine if each method is
+ // overridden, thus avoiding unnecessary calls into Java.
+ //
+ // On the other hand, when derived classes are
+ // expected to override all director methods then the
+ // GetMethodID calls are inefficient, and it is better to let
+ // the director unconditionally call up into Java. The resulting code
+ // will still behave correctly (though less efficiently) when Java
+ // code doesn't override a given method.
+ //
+ // The assumeoverride feature on a director controls whether or not
+ // overrides are assumed.
+ if (GetFlag(n, "feature:director:assumeoverride")) {
+ Printf(w->code, " swig_override[i] = derived;\n");
+ } else {
+ Printf(w->code, " swig_override[i] = false;\n");
+ Printf(w->code, " if (derived) {\n");
+ Printf(w->code, " jmethodID methid = jenv->GetMethodID(jcls, methods[i].name, methods[i].desc);\n");
+ Printf(w->code, " swig_override[i] = methods[i].methid && (methid != methods[i].methid);\n");
+ Printf(w->code, " jenv->ExceptionClear();\n");
+ Printf(w->code, " }\n");
+ }
+ Printf(w->code, "}\n");
+ } else {
+ Printf(f_directors_h, "public:\n");
+ Printf(f_directors_h, " bool swig_overrides(int n) {\n");
+ Printf(f_directors_h, " return false;\n");
+ Printf(f_directors_h, " }\n");
+ }
+
+ Printf(f_directors_h, "};\n\n");
+ Printf(w->code, "}\n");
+ Printf(w->code, "}\n");
+
+ Wrapper_print(w, f_directors);
+
+ DelWrapper(w);
+ Delete(internal_classname);
+
+ return Language::classDirectorEnd(n);
+ }
+
+ /* --------------------------------------------------------------------
+ * classDirectorDisown()
+ * ------------------------------------------------------------------*/
+
+ virtual int classDirectorDisown(Node *n) {
+ (void) n;
+ return SWIG_OK;
+ }
+
+ /*----------------------------------------------------------------------
+ * extraDirectorProtectedCPPMethodsRequired()
+ *--------------------------------------------------------------------*/
+
+ bool extraDirectorProtectedCPPMethodsRequired() const {
+ return false;
+ }
+
+ /*----------------------------------------------------------------------
+ * directorDeclaration()
+ *
+ * Generate the director class's declaration
+ * e.g. "class SwigDirector_myclass : public myclass, public Swig::Director {"
+ *--------------------------------------------------------------------*/
+
+ void directorDeclaration(Node *n) {
+ String *base = Getattr(n, "classtype");
+ String *class_ctor = NewString("Swig::Director(jenv)");
+
+ String *directorname = directorClassName(n);
+ String *declaration = Swig_class_declaration(n, directorname);
+
+ Printf(declaration, " : public %s, public Swig::Director", base);
+
+ // Stash stuff for later.
+ Setattr(n, "director:decl", declaration);
+ Setattr(n, "director:ctor", class_ctor);
+ }
+
+ /*----------------------------------------------------------------------
+ * nestedClassesSupport()
+ *--------------------------------------------------------------------*/
+
+ NestedClassSupport nestedClassesSupport() const {
+ return NCS_Full;
+ }
+}; /* class JAVA */
+
+/* -----------------------------------------------------------------------------
+ * swig_java() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_java() {
+ return new JAVA();
+}
+extern "C" Language *swig_java(void) {
+ return new_swig_java();
+}
+
+/* -----------------------------------------------------------------------------
+ * Static member variables
+ * ----------------------------------------------------------------------------- */
+
+const char *JAVA::usage = "\
+Java Options (available with -java)\n\
+ -doxygen - Convert C++ doxygen comments to JavaDoc comments in proxy classes\n\
+ -debug-doxygen-parser - Display doxygen parser module debugging information\n\
+ -debug-doxygen-translator - Display doxygen translator module debugging information\n\
+ -nopgcpp - Suppress premature garbage collection prevention parameter\n\
+ -noproxy - Generate the low-level functional interface instead\n\
+ of proxy classes\n\
+ -oldvarnames - Old intermediary method names for variable wrappers\n\
+ -package <name> - Set name of the Java package to <name>\n\
+\n";
diff --git a/contrib/tools/swig/Source/Modules/javascript.cxx b/contrib/tools/swig/Source/Modules/javascript.cxx
new file mode 100644
index 0000000000..17effc220f
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/javascript.cxx
@@ -0,0 +1,2490 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * javascript.cxx
+ *
+ * Javascript language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+/**
+ * Enables extra debugging information in typemaps.
+ */
+static bool js_template_enable_debug = false;
+
+#define ERR_MSG_ONLY_ONE_ENGINE_PLEASE "Only one engine can be specified at a time."
+
+// keywords used for state variables
+#define NAME "name"
+#define NAME_MANGLED "name_mangled"
+#define TYPE "type"
+#define TYPE_MANGLED "type_mangled"
+#define WRAPPER_NAME "wrapper"
+#define IS_IMMUTABLE "is_immutable"
+#define IS_STATIC "is_static"
+#define IS_ABSTRACT "is_abstract"
+#define GETTER "getter"
+#define SETTER "setter"
+#define PARENT "parent"
+#define PARENT_MANGLED "parent_mangled"
+#define CTOR "ctor"
+#define CTOR_WRAPPERS "ctor_wrappers"
+#define CTOR_DISPATCHERS "ctor_dispatchers"
+#define DTOR "dtor"
+#define ARGCOUNT "wrap:argc"
+#define HAS_TEMPLATES "has_templates"
+#define FORCE_CPP "force_cpp"
+#define RESET true
+
+// keys for global state variables
+#define CREATE_NAMESPACES "create_namespaces"
+#define REGISTER_NAMESPACES "register_namespaces"
+#define INITIALIZER "initializer"
+
+// keys for class scoped state variables
+#define MEMBER_VARIABLES "member_variables"
+#define MEMBER_FUNCTIONS "member_functions"
+#define STATIC_FUNCTIONS "static_functions"
+#define STATIC_VARIABLES "static_variables"
+
+
+/**
+ * A convenience class to manage state variables for emitters.
+ * The implementation delegates to SWIG Hash DOHs and provides
+ * named sub-hashes for class, variable, and function states.
+ */
+class JSEmitterState {
+
+public:
+ JSEmitterState();
+ ~JSEmitterState();
+ DOH *globals();
+ DOH *globals(const char *key, DOH *initial = 0);
+ DOH *clazz(bool reset = false);
+ DOH *clazz(const char *key, DOH *initial = 0);
+ DOH *function(bool reset = false);
+ DOH *function(const char *key, DOH *initial = 0);
+ DOH *variable(bool reset = false);
+ DOH *variable(const char *key, DOH *initial = 0);
+ static int IsSet(DOH *val);
+
+private:
+ DOH *getState(const char *key, bool reset = false);
+ Hash *globalHash;
+};
+
+/**
+ * A convenience class that wraps a code snippet used as template
+ * for code generation.
+ */
+class Template {
+
+public:
+ Template(const String *code);
+ Template(const String *code, const String *templateName);
+ Template(const Template & other);
+ ~Template();
+ String *str();
+ Template & replace(const String *pattern, const String *repl);
+ Template & print(DOH *doh);
+ Template & pretty_print(DOH *doh);
+ void operator=(const Template & t);
+ Template & trim();
+
+private:
+ String *code;
+ String *templateName;
+};
+
+/**
+ * JSEmitter represents an abstraction of javascript code generators
+ * for different javascript engines.
+ **/
+class JSEmitter {
+
+protected:
+
+ typedef JSEmitterState State;
+
+ enum MarshallingMode {
+ Setter,
+ Getter,
+ Ctor,
+ Function
+ };
+
+public:
+
+ enum JSEngine {
+ JavascriptCore,
+ V8,
+ NodeJS
+ };
+
+ JSEmitter(JSEngine engine);
+
+ virtual ~ JSEmitter();
+
+ /**
+ * Opens output files and temporary output DOHs.
+ */
+ virtual int initialize(Node *n);
+
+ /**
+ * Writes all collected code into the output file(s).
+ */
+ virtual int dump(Node *n) = 0;
+
+ /**
+ * Cleans up all open output DOHs.
+ */
+ virtual int close() = 0;
+
+ /**
+ * Switches the context for code generation.
+ *
+ * Classes, global variables and global functions may need to
+ * be registered in certain static tables.
+ * This method should be used to switch output DOHs correspondingly.
+ */
+ virtual int switchNamespace(Node *);
+
+ /**
+ * Invoked at the beginning of the classHandler.
+ */
+ virtual int enterClass(Node *);
+
+ /**
+ * Invoked at the end of the classHandler.
+ */
+ virtual int exitClass(Node *) {
+ return SWIG_OK;
+ }
+
+ /**
+ * Invoked at the beginning of the variableHandler.
+ */
+ virtual int enterVariable(Node *);
+
+ /**
+ * Invoked at the end of the variableHandler.
+ */
+ virtual int exitVariable(Node *) {
+ return SWIG_OK;
+ }
+
+ /**
+ * Invoked at the beginning of the functionHandler.
+ */
+ virtual int enterFunction(Node *);
+
+ /**
+ * Invoked at the end of the functionHandler.
+ */
+ virtual int exitFunction(Node *) {
+ return SWIG_OK;
+ }
+
+ /**
+ * Invoked by functionWrapper callback after call to Language::functionWrapper.
+ */
+ virtual int emitWrapperFunction(Node *n);
+
+ /**
+ * Invoked by nativeWrapper callback
+ */
+ virtual int emitNativeFunction(Node *n);
+
+ /**
+ * Invoked from constantWrapper after call to Language::constantWrapper.
+ **/
+ virtual int emitConstant(Node *n);
+
+ /**
+ * Registers a given code snippet for a given key name.
+ *
+ * This method is called by the fragmentDirective handler
+ * of the JAVASCRIPT language module.
+ **/
+ int registerTemplate(const String *name, const String *code);
+
+ /**
+ * Retrieve the code template registered for a given name.
+ */
+ Template getTemplate(const String *name);
+
+ State & getState();
+
+protected:
+
+ /**
+ * Generates code for a constructor function.
+ */
+ virtual int emitCtor(Node *n);
+
+ /**
+ * Generates code for a destructor function.
+ */
+ virtual int emitDtor(Node *n);
+
+ /**
+ * Generates code for a function.
+ */
+ virtual int emitFunction(Node *n, bool is_member, bool is_static);
+
+ virtual int emitFunctionDispatcher(Node *n, bool /*is_member */ );
+
+ /**
+ * Generates code for a getter function.
+ */
+ virtual int emitGetter(Node *n, bool is_member, bool is_static);
+
+ /**
+ * Generates code for a setter function.
+ */
+ virtual int emitSetter(Node *n, bool is_member, bool is_static);
+
+ virtual void marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static) = 0;
+
+ virtual String *emitInputTypemap(Node *n, Parm *params, Wrapper *wrapper, String *arg);
+
+ virtual void marshalOutput(Node *n, ParmList *params, Wrapper *wrapper, String *actioncode, const String *cresult = 0, bool emitReturnVariable = true);
+
+ virtual void emitCleanupCode(Node *n, Wrapper *wrapper, ParmList *params);
+
+ /**
+ * Helper function to retrieve the first parent class node.
+ */
+ Node *getBaseClass(Node *n);
+
+ Parm *skipIgnoredArgs(Parm *p);
+
+ virtual int createNamespace(String *scope);
+
+ virtual Hash *createNamespaceEntry(const char *name, const char *parent, const char *parent_mangled);
+
+ virtual int emitNamespaces() = 0;
+
+
+protected:
+
+ JSEngine engine;
+ Hash *templates;
+ State state;
+
+ // contains context specific data (DOHs)
+ // to allow generation of namespace related code
+ // which are switched on namespace change
+ Hash *namespaces;
+ Hash *current_namespace;
+ String *defaultResultName;
+ File *f_wrappers;
+};
+
+/* factory methods for concrete JSEmitters: */
+
+JSEmitter *swig_javascript_create_JSCEmitter();
+JSEmitter *swig_javascript_create_V8Emitter();
+JSEmitter *swig_javascript_create_NodeJSEmitter();
+
+/**********************************************************************
+ * JAVASCRIPT: SWIG module implementation
+ **********************************************************************/
+
+class JAVASCRIPT:public Language {
+
+public:
+
+ JAVASCRIPT():emitter(NULL) {
+ }
+ ~JAVASCRIPT() {
+ delete emitter;
+ }
+
+ virtual int functionHandler(Node *n);
+ virtual int globalfunctionHandler(Node *n);
+ virtual int variableHandler(Node *n);
+ virtual int globalvariableHandler(Node *n);
+ virtual int staticmemberfunctionHandler(Node *n);
+ virtual int classHandler(Node *n);
+ virtual int functionWrapper(Node *n);
+ virtual int constantWrapper(Node *n);
+ virtual int nativeWrapper(Node *n);
+ virtual void main(int argc, char *argv[]);
+ virtual int top(Node *n);
+
+ /**
+ * Registers all %fragments assigned to section "templates".
+ **/
+ virtual int fragmentDirective(Node *n);
+
+public:
+
+ virtual String *getNSpace() const;
+
+private:
+
+ JSEmitter *emitter;
+};
+
+/* ---------------------------------------------------------------------
+ * functionWrapper()
+ *
+ * Low level code generator for functions
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::functionWrapper(Node *n) {
+
+ // note: the default implementation only prints a message
+ // Language::functionWrapper(n);
+ emitter->emitWrapperFunction(n);
+
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * functionHandler()
+ *
+ * Function handler for generating wrappers for functions
+ * --------------------------------------------------------------------- */
+int JAVASCRIPT::functionHandler(Node *n) {
+
+ if (GetFlag(n, "isextension") == 1) {
+ SetFlag(n, "ismember");
+ }
+
+ emitter->enterFunction(n);
+ Language::functionHandler(n);
+ emitter->exitFunction(n);
+
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * globalfunctionHandler()
+ *
+ * Function handler for generating wrappers for functions
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::globalfunctionHandler(Node *n) {
+ emitter->switchNamespace(n);
+ Language::globalfunctionHandler(n);
+
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ *
+ * Function handler for generating wrappers for static member functions
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::staticmemberfunctionHandler(Node *n) {
+ /*
+ * Note: storage=static is removed by Language::staticmemberfunctionHandler.
+ * So, don't rely on that after here. Instead use the state variable which is
+ * set by JSEmitter::enterFunction().
+ */
+ Language::staticmemberfunctionHandler(n);
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * variableHandler()
+ *
+ * Function handler for generating wrappers for variables
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::variableHandler(Node *n) {
+
+ emitter->enterVariable(n);
+ Language::variableHandler(n);
+ emitter->exitVariable(n);
+
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * globalvariableHandler()
+ *
+ * Function handler for generating wrappers for global variables
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::globalvariableHandler(Node *n) {
+ emitter->switchNamespace(n);
+ Language::globalvariableHandler(n);
+
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * constantHandler()
+ *
+ * Function handler for generating wrappers for constants
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::constantWrapper(Node *n) {
+ emitter->switchNamespace(n);
+
+ // Note: callbacks trigger this wrapper handler
+ // TODO: handle callback declarations
+ if (Equal(Getattr(n, "kind"), "function")) {
+ return SWIG_OK;
+ }
+ // TODO: the emitter for constants must be implemented in a cleaner way
+ // currently we treat it like a read-only variable
+ // however, there is a remaining bug with function pointer constants
+ // which could be fixed with a cleaner approach
+ emitter->emitConstant(n);
+
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * nativeWrapper()
+ *
+ * Function wrapper for generating placeholders for native functions
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::nativeWrapper(Node *n) {
+ emitter->emitNativeFunction(n);
+
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * classHandler()
+ *
+ * Function handler for generating wrappers for class
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::classHandler(Node *n) {
+ emitter->switchNamespace(n);
+
+ emitter->enterClass(n);
+ Language::classHandler(n);
+ emitter->exitClass(n);
+
+ return SWIG_OK;
+}
+
+int JAVASCRIPT::fragmentDirective(Node *n) {
+
+ // catch all fragment directives that have "templates" as location
+ // and register them at the emitter.
+ String *section = Getattr(n, "section");
+
+ if (Equal(section, "templates") && !ImportMode) {
+ emitter->registerTemplate(Getattr(n, "value"), Getattr(n, "code"));
+ } else {
+ return Language::fragmentDirective(n);
+ }
+
+ return SWIG_OK;
+}
+
+String *JAVASCRIPT::getNSpace() const {
+ return Language::getNSpace();
+}
+
+/* ---------------------------------------------------------------------
+ * top()
+ *
+ * Function handler for processing top node of the parse tree
+ * Wrapper code generation essentially starts from here
+ * --------------------------------------------------------------------- */
+
+int JAVASCRIPT::top(Node *n) {
+ emitter->initialize(n);
+
+ Language::top(n);
+
+ emitter->dump(n);
+ emitter->close();
+
+ return SWIG_OK;
+}
+
+static const char *usage = (char *) "\
+Javascript Options (available with -javascript)\n\
+ -jsc - creates a JavascriptCore extension \n\
+ -v8 - creates a v8 extension \n\
+ -node - creates a node.js extension \n\
+ -debug-codetemplates - generates information about the origin of code templates\n";
+
+
+/* ---------------------------------------------------------------------
+ * main()
+ *
+ * Entry point for the JAVASCRIPT module
+ * --------------------------------------------------------------------- */
+
+void JAVASCRIPT::main(int argc, char *argv[]) {
+ // Set javascript subdirectory in SWIG library
+ SWIG_library_directory("javascript");
+
+ int engine = -1;
+
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-v8") == 0) {
+ if (engine != -1) {
+ Printf(stderr, ERR_MSG_ONLY_ONE_ENGINE_PLEASE);
+ Exit(EXIT_FAILURE);
+ }
+ Swig_mark_arg(i);
+ engine = JSEmitter::V8;
+ } else if (strcmp(argv[i], "-jsc") == 0) {
+ if (engine != -1) {
+ Printf(stderr, ERR_MSG_ONLY_ONE_ENGINE_PLEASE);
+ Exit(EXIT_FAILURE);
+ }
+ Swig_mark_arg(i);
+ engine = JSEmitter::JavascriptCore;
+ } else if (strcmp(argv[i], "-node") == 0) {
+ if (engine != -1) {
+ Printf(stderr, ERR_MSG_ONLY_ONE_ENGINE_PLEASE);
+ Exit(EXIT_FAILURE);
+ }
+ Swig_mark_arg(i);
+ engine = JSEmitter::NodeJS;
+ } else if (strcmp(argv[i], "-debug-codetemplates") == 0) {
+ Swig_mark_arg(i);
+ js_template_enable_debug = true;
+ } else if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage, stdout);
+ return;
+ }
+ }
+ }
+
+ switch (engine) {
+ case JSEmitter::V8:
+ {
+ emitter = swig_javascript_create_V8Emitter();
+ Preprocessor_define("SWIG_JAVASCRIPT_V8 1", 0);
+ SWIG_library_directory("javascript/v8");
+ // V8 API is C++, so output must be C++ compatible even when wrapping C code
+ if (!cparse_cplusplus) {
+ Swig_cparse_cplusplusout(1);
+ }
+ break;
+ }
+ case JSEmitter::JavascriptCore:
+ {
+ emitter = swig_javascript_create_JSCEmitter();
+ Preprocessor_define("SWIG_JAVASCRIPT_JSC 1", 0);
+ SWIG_library_directory("javascript/jsc");
+ break;
+ }
+ case JSEmitter::NodeJS:
+ {
+ emitter = swig_javascript_create_V8Emitter();
+ Preprocessor_define("SWIG_JAVASCRIPT_V8 1", 0);
+ Preprocessor_define("BUILDING_NODE_EXTENSION 1", 0);
+ SWIG_library_directory("javascript/v8");
+ break;
+ }
+ default:
+ {
+ Printf(stderr, "SWIG Javascript: Unknown engine. Please specify one of '-jsc', '-v8' or '-node'.\n");
+ Exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ // Add a symbol to the parser for conditional compilation
+ Preprocessor_define("SWIGJAVASCRIPT 1", 0);
+
+ // Add typemap definitions
+ SWIG_typemap_lang("javascript");
+
+ // Set configuration file
+ SWIG_config_file("javascript.swg");
+
+ allow_overloading();
+}
+
+/* -----------------------------------------------------------------------------
+ * swig_javascript() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_javascript() {
+ return new JAVASCRIPT();
+}
+
+extern "C" Language *swig_javascript(void) {
+ return new_swig_javascript();
+}
+
+/**********************************************************************
+ * Emitter implementations
+ **********************************************************************/
+
+/* -----------------------------------------------------------------------------
+ * JSEmitter()
+ * ----------------------------------------------------------------------------- */
+
+JSEmitter::JSEmitter(JSEmitter::JSEngine engine)
+: engine(engine), templates(NewHash()), namespaces(NULL), current_namespace(NULL), defaultResultName(NewString("result")), f_wrappers(NULL) {
+}
+
+/* -----------------------------------------------------------------------------
+ * ~JSEmitter()
+ * ----------------------------------------------------------------------------- */
+
+JSEmitter::~JSEmitter() {
+ Delete(templates);
+}
+
+/* -----------------------------------------------------------------------------
+ * JSEmitter::RegisterTemplate() : Registers a code template
+ *
+ * Note: this is used only by JAVASCRIPT::fragmentDirective().
+ * ----------------------------------------------------------------------------- */
+
+int JSEmitter::registerTemplate(const String *name, const String *code) {
+ if (!State::IsSet(state.globals(HAS_TEMPLATES))) {
+ SetFlag(state.globals(), HAS_TEMPLATES);
+ }
+ return Setattr(templates, name, code);
+}
+
+/* -----------------------------------------------------------------------------
+ * JSEmitter::getTemplate() : Provides a registered code template
+ * ----------------------------------------------------------------------------- */
+
+Template JSEmitter::getTemplate(const String *name) {
+ String *templ = Getattr(templates, name);
+
+ if (!templ) {
+ Printf(stderr, "Could not find template %s\n.", name);
+ Exit(EXIT_FAILURE);
+ }
+
+ Template t(templ, name);
+ return t;
+}
+
+JSEmitterState & JSEmitter::getState() {
+ return state;
+}
+
+int JSEmitter::initialize(Node * /*n */ ) {
+
+ if (namespaces != NULL) {
+ Delete(namespaces);
+ }
+ namespaces = NewHash();
+ Hash *global_namespace = createNamespaceEntry("exports", 0, 0);
+
+ Setattr(namespaces, "::", global_namespace);
+ current_namespace = global_namespace;
+
+ f_wrappers = NewString("");
+
+ return SWIG_OK;
+}
+
+/* ---------------------------------------------------------------------
+ * skipIgnoredArgs()
+ * --------------------------------------------------------------------- */
+
+Parm *JSEmitter::skipIgnoredArgs(Parm *p) {
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+ return p;
+}
+
+/* -----------------------------------------------------------------------------
+ * JSEmitter::getBaseClass() : the node of the base class or NULL
+ *
+ * Note: the first base class is provided. Multiple inheritance is not
+ * supported.
+ * ----------------------------------------------------------------------------- */
+
+Node *JSEmitter::getBaseClass(Node *n) {
+ // retrieve the first base class that is not %ignored
+ List *baselist = Getattr(n, "bases");
+ if (baselist) {
+ Iterator base = First(baselist);
+ while (base.item && GetFlag(base.item, "feature:ignore")) {
+ base = Next(base);
+ }
+ return base.item;
+ }
+ return NULL;
+}
+
+ /* -----------------------------------------------------------------------------
+ * JSEmitter::emitWrapperFunction() : dispatches emitter functions.
+ *
+ * This allows having small sized, dedicated emitting functions.
+ * All state dependent branching is done here.
+ * ----------------------------------------------------------------------------- */
+
+int JSEmitter::emitWrapperFunction(Node *n) {
+ int ret = SWIG_OK;
+
+ String *kind = Getattr(n, "kind");
+
+ if (kind) {
+
+ if (Equal(kind, "function")
+ // HACK: sneaky.ctest revealed that typedef'd (global) functions must be
+ // detected via the 'view' attribute.
+ || (Equal(kind, "variable") && Equal(Getattr(n, "view"), "globalfunctionHandler"))
+ ) {
+ bool is_member = GetFlag(n, "ismember") != 0 || GetFlag(n, "feature:extend") != 0;
+ bool is_static = GetFlag(state.function(), IS_STATIC) != 0;
+ ret = emitFunction(n, is_member, is_static);
+ } else if (Cmp(kind, "variable") == 0) {
+ bool is_static = GetFlag(state.variable(), IS_STATIC) != 0;
+ // HACK: smartpointeraccessed static variables are not treated as statics
+ if (GetFlag(n, "allocate:smartpointeraccess")) {
+ is_static = false;
+ }
+
+ bool is_member = GetFlag(n, "ismember") != 0;
+ bool is_setter = GetFlag(n, "memberset") != 0 || GetFlag(n, "varset") != 0;
+ bool is_getter = GetFlag(n, "memberget") != 0 || GetFlag(n, "varget") != 0;
+ if (is_setter) {
+ ret = emitSetter(n, is_member, is_static);
+ } else if (is_getter) {
+ ret = emitGetter(n, is_member, is_static);
+ }
+
+ } else {
+ Printf(stderr, "Warning: unsupported wrapper function type\n");
+ ret = SWIG_ERROR;
+ }
+ } else {
+ String *view = Getattr(n, "view");
+
+ if (Cmp(view, "constructorHandler") == 0) {
+ ret = emitCtor(n);
+ } else if (Cmp(view, "destructorHandler") == 0) {
+ ret = emitDtor(n);
+ } else {
+ Printf(stderr, "Warning: unsupported wrapper function type");
+ ret = SWIG_ERROR;
+ }
+ }
+
+ return ret;
+}
+
+int JSEmitter::emitNativeFunction(Node *n) {
+ String *wrapname = Getattr(n, "wrap:name");
+ enterFunction(n);
+ state.function(WRAPPER_NAME, wrapname);
+ exitFunction(n);
+ return SWIG_OK;
+}
+
+int JSEmitter::enterClass(Node *n) {
+ state.clazz(RESET);
+ state.clazz(NAME, Getattr(n, "sym:name"));
+ state.clazz("nspace", current_namespace);
+
+ // Creating a mangled name using the current namespace and the symbol name
+ String *mangled_name = NewString("");
+ Printf(mangled_name, "%s_%s", Getattr(current_namespace, NAME_MANGLED), Getattr(n, "sym:name"));
+ state.clazz(NAME_MANGLED, SwigType_manglestr(mangled_name));
+ Delete(mangled_name);
+
+ state.clazz(TYPE, NewString(Getattr(n, "classtype")));
+
+ String *type = SwigType_manglestr(Getattr(n, "classtypeobj"));
+ String *classtype_mangled = NewString("");
+ Printf(classtype_mangled, "p%s", type);
+ state.clazz(TYPE_MANGLED, classtype_mangled);
+ Delete(type);
+
+ String *ctor_wrapper = NewString("_wrap_new_veto_");
+ Append(ctor_wrapper, state.clazz(NAME));
+ state.clazz(CTOR, ctor_wrapper);
+ state.clazz(CTOR_DISPATCHERS, NewString(""));
+ state.clazz(DTOR, NewString("0"));
+
+ // HACK: assume that a class is abstract
+ // this is resolved by emitCtor (which is only called for non abstract classes)
+ SetFlag(state.clazz(), IS_ABSTRACT);
+
+ return SWIG_OK;
+}
+
+int JSEmitter::enterFunction(Node *n) {
+ state.function(RESET);
+ state.function(NAME, Getattr(n, "sym:name"));
+ if (Equal(Getattr(n, "storage"), "static")) {
+ SetFlag(state.function(), IS_STATIC);
+ }
+ return SWIG_OK;
+}
+
+int JSEmitter::enterVariable(Node *n) {
+ // reset the state information for variables.
+ state.variable(RESET);
+
+ // Retrieve a pure symbol name. Using 'sym:name' as a basis, as it considers %renamings.
+ if (Equal(Getattr(n, "view"), "memberconstantHandler")) {
+ // Note: this is kind of hacky/experimental
+ // For constants/enums 'sym:name' contains e.g., 'Foo_Hello' instead of 'Hello'
+ state.variable(NAME, Getattr(n, "memberconstantHandler:sym:name"));
+ } else {
+ state.variable(NAME, Swig_scopename_last(Getattr(n, "sym:name")));
+ }
+
+ if (Equal(Getattr(n, "storage"), "static")) {
+ SetFlag(state.variable(), IS_STATIC);
+ }
+
+ if (!Language::instance()->is_assignable(n)) {
+ SetFlag(state.variable(), IS_IMMUTABLE);
+ }
+ // FIXME: test "arrays_global" does not compile with that as it is not allowed to assign to char[]
+ if (Equal(Getattr(n, "type"), "a().char")) {
+ SetFlag(state.variable(), IS_IMMUTABLE);
+ }
+
+ return SWIG_OK;
+}
+
+int JSEmitter::emitCtor(Node *n) {
+
+ Wrapper *wrapper = NewWrapper();
+
+ bool is_overloaded = GetFlag(n, "sym:overloaded") != 0;
+
+ Template t_ctor(getTemplate("js_ctor"));
+
+ String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
+ if (is_overloaded) {
+ t_ctor = getTemplate("js_overloaded_ctor");
+ Append(wrap_name, Getattr(n, "sym:overname"));
+ }
+ Setattr(n, "wrap:name", wrap_name);
+ // note: we can remove the is_abstract flag now, as this
+ // is called for non-abstract classes only.
+ Setattr(state.clazz(), IS_ABSTRACT, 0);
+
+ ParmList *params = Getattr(n, "parms");
+ emit_parameter_variables(params, wrapper);
+ emit_attach_parmmaps(params, wrapper);
+ // HACK: in test-case `ignore_parameter` emit_attach_parmmaps generated an extra line of applied typemaps.
+ // Deleting wrapper->code here, to reset, and as it seemed to have no side effect elsewhere
+ Delete(wrapper->code);
+ wrapper->code = NewString("");
+
+ Printf(wrapper->locals, "%sresult;", SwigType_str(Getattr(n, "type"), 0));
+
+ marshalInputArgs(n, params, wrapper, Ctor, true, false);
+ String *action = emit_action(n);
+ Printv(wrapper->code, action, "\n", 0);
+
+ emitCleanupCode(n, wrapper, params);
+
+ t_ctor.replace("$jswrapper", wrap_name)
+ .replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
+ .replace("$jslocals", wrapper->locals)
+ .replace("$jscode", wrapper->code)
+ .replace("$jsargcount", Getattr(n, ARGCOUNT))
+ .pretty_print(f_wrappers);
+
+ Template t_ctor_case(getTemplate("js_ctor_dispatch_case"));
+ t_ctor_case.replace("$jswrapper", wrap_name)
+ .replace("$jsargcount", Getattr(n, ARGCOUNT));
+ Append(state.clazz(CTOR_DISPATCHERS), t_ctor_case.str());
+
+ DelWrapper(wrapper);
+
+ // create a dispatching ctor
+ if (is_overloaded) {
+ if (!Getattr(n, "sym:nextSibling")) {
+ String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
+ Template t_mainctor(getTemplate("js_ctor_dispatcher"));
+ t_mainctor.replace("$jswrapper", wrap_name)
+ .replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsdispatchcases", state.clazz(CTOR_DISPATCHERS))
+ .pretty_print(f_wrappers);
+ state.clazz(CTOR, wrap_name);
+ }
+ } else {
+ state.clazz(CTOR, wrap_name);
+ }
+
+ return SWIG_OK;
+}
+
+int JSEmitter::emitDtor(Node *n) {
+
+ String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
+
+ SwigType *type = state.clazz(TYPE);
+ String *p_classtype = SwigType_add_pointer(state.clazz(TYPE));
+ String *ctype = SwigType_lstr(p_classtype, "");
+ String *jsfree = NewString("");
+
+ // (Taken from JSCore implementation.)
+ /* The if (Extend) block was taken from the Ruby implementation.
+ * The problem is that in the case of an %extend to create a destructor for a struct to coordinate automatic memory cleanup with the Javascript collector,
+ * the SWIG function was not being generated. More specifically:
+ struct MyData {
+ %extend {
+ ~MyData() {
+ FreeData($self);
+ }
+ }
+ };
+ %newobject CreateData;
+ struct MyData* CreateData(void);
+ %delobject FreeData;
+ void FreeData(struct MyData* the_data);
+
+ where the use case is something like:
+ var my_data = example.CreateData();
+ my_data = null;
+
+ This function was not being generated:
+ SWIGINTERN void delete_MyData(struct MyData *self){
+ FreeData(self);
+ }
+
+ I don't understand fully why it wasn't being generated. It just seems to happen in the Lua generator.
+ There is a comment about staticmemberfunctionHandler having an inconsistency and I tracked down dome of the SWIGINTERN void delete_*
+ code to that function in the Language base class.
+ The Ruby implementation seems to have an explicit check for if(Extend) and explicitly generates the code, so that's what I'm doing here.
+ The Ruby implementation does other stuff which I omit.
+ */
+ if (Extend) {
+ String *wrap = Getattr(n, "wrap:code");
+ if (wrap) {
+ Printv(f_wrappers, wrap, NIL);
+ }
+ }
+ // HACK: this is only for the v8 emitter. maybe set an attribute wrap:action of node
+ // TODO: generate dtors more similar to other wrappers
+ // EW: I think this is wrong. delete should only be used when new was used to create. If malloc was used, free needs to be used.
+ if (SwigType_isarray(type)) {
+ Printf(jsfree, "delete [] (%s)", ctype);
+ } else {
+ Printf(jsfree, "delete (%s)", ctype);
+ }
+
+ String *destructor_action = Getattr(n, "wrap:action");
+ // Adapted from the JSCore implementation.
+ /* The next challenge is to generate the correct finalize function for JavaScriptCore to call.
+ Originally, it would use this fragment from javascriptcode.swg
+ %fragment ("JS_destructordefn", "templates")
+ %{
+ void _wrap_${classname_mangled}_finalize(JSObjectRef thisObject)
+ {
+ SWIG_PRV_DATA* t = (SWIG_PRV_DATA*)JSObjectGetPrivate(thisObject);
+ if(t && t->swigCMemOwn) free ((${type}*)t->swigCObject);
+ free(t);
+ }
+ %}
+
+ But for the above example case of %extend to define a destructor on a struct, we need to override the system to not call
+ free ((${type}*)t->swigCObject);
+ and substitute it with what the user has provided.
+ To solve this, I created a variation fragment called JS_destructoroverridedefn:
+ SWIG_PRV_DATA* t = (SWIG_PRV_DATA*)JSObjectGetPrivate(thisObject);
+ if(t && t->swigCMemOwn) {
+ ${type}* arg1 = (${type}*)t->swigCObject;
+ ${destructor_action}
+ }
+ free(t);
+
+ Based on what I saw in the Lua and Ruby modules, I use Getattr(n, "wrap:action")
+ to decide if the user has a preferred destructor action.
+ Based on that, I decide which fragment to use.
+ And in the case of the custom action, I substitute that action in.
+ I noticed that destructor_action has the form
+ delete_MyData(arg1);
+ The explicit arg1 is a little funny, so I structured the fragment to create a temporary variable called arg1 to make the generation easier.
+ This might suggest this solution misunderstands a more complex case.
+
+ Also, there is a problem where destructor_action is always true for me, even when not requesting %extend as above.
+ So this code doesn't actually quite work as I expect. The end result is that the code still works because
+ destructor_action calls free like the original template. The one caveat is the string in destructor_action casts to char* which is weird.
+ I think there is a deeper underlying SWIG issue because I don't think it should be char*. However, it doesn't really matter for free.
+
+ Maybe the fix for the destructor_action always true problem is that this is supposed to be embedded in the if(Extend) block above.
+ But I don't fully understand the conditions of any of these things, and since it works for the moment, I don't want to break more stuff.
+ */
+ if (destructor_action) {
+ Template t_dtor = getTemplate("js_dtoroverride");
+ state.clazz(DTOR, wrap_name);
+ t_dtor.replace("${classname_mangled}", state.clazz(NAME_MANGLED))
+ .replace("$jswrapper", wrap_name)
+ .replace("$jsfree", jsfree)
+ .replace("$jstype", ctype);
+
+ t_dtor.replace("${destructor_action}", destructor_action);
+ Wrapper_pretty_print(t_dtor.str(), f_wrappers);
+ } else {
+ Template t_dtor = getTemplate("js_dtor");
+ state.clazz(DTOR, wrap_name);
+ t_dtor.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jswrapper", wrap_name)
+ .replace("$jsfree", jsfree)
+ .replace("$jstype", ctype)
+ .pretty_print(f_wrappers);
+ }
+
+ Delete(p_classtype);
+ Delete(ctype);
+ Delete(jsfree);
+
+ return SWIG_OK;
+}
+
+int JSEmitter::emitGetter(Node *n, bool is_member, bool is_static) {
+ Wrapper *wrapper = NewWrapper();
+ Template t_getter(getTemplate("js_getter"));
+
+ // prepare wrapper name
+ String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
+ Setattr(n, "wrap:name", wrap_name);
+ state.variable(GETTER, wrap_name);
+
+ // prepare local variables
+ ParmList *params = Getattr(n, "parms");
+ emit_parameter_variables(params, wrapper);
+ emit_attach_parmmaps(params, wrapper);
+
+ // prepare code part
+ String *action = emit_action(n);
+ marshalInputArgs(n, params, wrapper, Getter, is_member, is_static);
+ marshalOutput(n, params, wrapper, action);
+
+ emitCleanupCode(n, wrapper, params);
+
+ t_getter.replace("$jswrapper", wrap_name)
+ .replace("$jslocals", wrapper->locals)
+ .replace("$jscode", wrapper->code)
+ .pretty_print(f_wrappers);
+
+ DelWrapper(wrapper);
+
+ return SWIG_OK;
+}
+
+int JSEmitter::emitSetter(Node *n, bool is_member, bool is_static) {
+
+ // skip variables that are immutable
+ if (State::IsSet(state.variable(IS_IMMUTABLE))) {
+ return SWIG_OK;
+ }
+
+ Wrapper *wrapper = NewWrapper();
+
+ Template t_setter(getTemplate("js_setter"));
+
+ // prepare wrapper name
+ String *wrap_name = Swig_name_wrapper(Getattr(n, "sym:name"));
+ Setattr(n, "wrap:name", wrap_name);
+ state.variable(SETTER, wrap_name);
+
+ // prepare local variables
+ ParmList *params = Getattr(n, "parms");
+ emit_parameter_variables(params, wrapper);
+ emit_attach_parmmaps(params, wrapper);
+
+ // prepare code part
+ String *action = emit_action(n);
+ marshalInputArgs(n, params, wrapper, Setter, is_member, is_static);
+ Append(wrapper->code, action);
+
+ emitCleanupCode(n, wrapper, params);
+
+ t_setter.replace("$jswrapper", wrap_name)
+ .replace("$jslocals", wrapper->locals)
+ .replace("$jscode", wrapper->code)
+ .pretty_print(f_wrappers);
+
+ DelWrapper(wrapper);
+
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * JSEmitter::emitConstant() : triggers code generation for constants
+ * ----------------------------------------------------------------------------- */
+
+int JSEmitter::emitConstant(Node *n) {
+ // HACK: somehow it happened under Mac OS X that before everything started
+ // a lot of SWIG internal constants were emitted
+ // This didn't happen on other platforms yet...
+ // we ignore those premature definitions
+ if (!State::IsSet(state.globals(HAS_TEMPLATES))) {
+ return SWIG_ERROR;
+ }
+
+ Wrapper *wrapper = NewWrapper();
+ SwigType *type = Getattr(n, "type");
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ String *wname = Swig_name_wrapper(name);
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+
+ // HACK: forcing usage of cppvalue for v8 (which turned out to fix typedef_struct.i, et. al)
+ if (State::IsSet(state.globals(FORCE_CPP)) && Getattr(n, "cppvalue") != NULL) {
+ value = Getattr(n, "cppvalue");
+ }
+
+ Template t_getter(getTemplate("js_getter"));
+
+ // call the variable methods as a constants are
+ // registered in same way
+ enterVariable(n);
+ state.variable(GETTER, wname);
+ // TODO: why do we need this?
+ Setattr(n, "wrap:name", wname);
+
+ // special treatment of member pointers
+ if (SwigType_type(type) == T_MPOINTER) {
+ // TODO: this could go into a code-template
+ String *mpointer_wname = NewString("");
+ Printf(mpointer_wname, "_wrapConstant_%s", iname);
+ Setattr(n, "memberpointer:constant:wrap:name", mpointer_wname);
+ String *str = SwigType_str(type, mpointer_wname);
+ Printf(f_wrappers, "static %s = %s;\n", str, value);
+ Delete(str);
+ value = mpointer_wname;
+ }
+
+ marshalOutput(n, 0, wrapper, NewString(""), value, false);
+
+ t_getter.replace("$jswrapper", wname)
+ .replace("$jslocals", wrapper->locals)
+ .replace("$jscode", wrapper->code)
+ .pretty_print(f_wrappers);
+
+ exitVariable(n);
+
+ DelWrapper(wrapper);
+
+ return SWIG_OK;
+}
+
+int JSEmitter::emitFunction(Node *n, bool is_member, bool is_static) {
+ Wrapper *wrapper = NewWrapper();
+ Template t_function(getTemplate("js_function"));
+
+ bool is_overloaded = GetFlag(n, "sym:overloaded") != 0;
+
+ // prepare the function wrapper name
+ String *iname = Getattr(n, "sym:name");
+ String *wrap_name = Swig_name_wrapper(iname);
+ if (is_overloaded) {
+ t_function = getTemplate("js_overloaded_function");
+ Append(wrap_name, Getattr(n, "sym:overname"));
+ }
+ Setattr(n, "wrap:name", wrap_name);
+ state.function(WRAPPER_NAME, wrap_name);
+
+ // prepare local variables
+ ParmList *params = Getattr(n, "parms");
+ emit_parameter_variables(params, wrapper);
+ emit_attach_parmmaps(params, wrapper);
+
+ // HACK: in test-case `ignore_parameter` emit_attach_parmmaps generates an extra line of applied typemap.
+ // Deleting wrapper->code here fixes the problem, and seems to have no side effect elsewhere
+ Delete(wrapper->code);
+ wrapper->code = NewString("");
+
+ marshalInputArgs(n, params, wrapper, Function, is_member, is_static);
+ String *action = emit_action(n);
+ marshalOutput(n, params, wrapper, action);
+ emitCleanupCode(n, wrapper, params);
+ Replaceall(wrapper->code, "$symname", iname);
+
+ t_function.replace("$jswrapper", wrap_name)
+ .replace("$jslocals", wrapper->locals)
+ .replace("$jscode", wrapper->code)
+ .replace("$jsargcount", Getattr(n, ARGCOUNT))
+ .pretty_print(f_wrappers);
+
+
+ DelWrapper(wrapper);
+
+ return SWIG_OK;
+}
+
+int JSEmitter::emitFunctionDispatcher(Node *n, bool /*is_member */ ) {
+ Wrapper *wrapper = NewWrapper();
+
+ // Generate call list, go to first node
+ Node *sibl = n;
+
+ while (Getattr(sibl, "sym:previousSibling"))
+ sibl = Getattr(sibl, "sym:previousSibling"); // go all the way up
+
+ do {
+ String *siblname = Getattr(sibl, "wrap:name");
+
+ if (siblname) {
+ // handle function overloading
+ Template t_dispatch_case = getTemplate("js_function_dispatch_case");
+ t_dispatch_case.replace("$jswrapper", siblname)
+ .replace("$jsargcount", Getattr(sibl, ARGCOUNT));
+
+ Append(wrapper->code, t_dispatch_case.str());
+ }
+
+ } while ((sibl = Getattr(sibl, "sym:nextSibling")));
+
+ Template t_function(getTemplate("js_function_dispatcher"));
+
+ // Note: this dispatcher function gets called after the last overloaded function has been created.
+ // At this time, n.wrap:name contains the name of the last wrapper function.
+ // To get a valid function name for the dispatcher function we take the last wrapper name and
+ // subtract the extension "sym:overname",
+ String *wrap_name = NewString(Getattr(n, "wrap:name"));
+ String *overname = Getattr(n, "sym:overname");
+
+ Node *methodclass = Swig_methodclass(n);
+ String *class_name = Getattr(methodclass, "sym:name");
+
+ int l1 = Len(wrap_name);
+ int l2 = Len(overname);
+ Delslice(wrap_name, l1 - l2, l1);
+
+ String *new_string = NewStringf("%s_%s", class_name, wrap_name);
+ String *final_wrap_name = Swig_name_wrapper(new_string);
+
+ Setattr(n, "wrap:name", final_wrap_name);
+ state.function(WRAPPER_NAME, final_wrap_name);
+
+
+
+ t_function.replace("$jslocals", wrapper->locals)
+ .replace("$jscode", wrapper->code);
+
+ // call this here, to replace all variables
+ t_function.replace("$jswrapper", final_wrap_name)
+ .replace("$jsname", state.function(NAME))
+ .pretty_print(f_wrappers);
+
+ // Delete the state variable
+ DelWrapper(wrapper);
+
+ return SWIG_OK;
+}
+
+String *JSEmitter::emitInputTypemap(Node *n, Parm *p, Wrapper *wrapper, String *arg) {
+ // Get input typemap for current param
+ String *tm = Getattr(p, "tmap:in");
+ SwigType *type = Getattr(p, "type");
+
+ if (tm != NULL) {
+ Replaceall(tm, "$input", arg);
+ Setattr(p, "emit:input", arg);
+ // do replacements for built-in variables
+ if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+ Replaceall(tm, "$symname", Getattr(n, "sym:name"));
+ Printf(wrapper->code, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(type, 0));
+ }
+
+ return tm;
+}
+
+void JSEmitter::marshalOutput(Node *n, ParmList *params, Wrapper *wrapper, String *actioncode, const String *cresult, bool emitReturnVariable) {
+ SwigType *type = Getattr(n, "type");
+ String *tm;
+ Parm *p;
+
+ // adds a declaration for the result variable
+ if (emitReturnVariable)
+ emit_return_variable(n, type, wrapper);
+ // if not given, use default result identifier ('result') for output typemap
+ if (cresult == 0)
+ cresult = defaultResultName;
+
+ tm = Swig_typemap_lookup_out("out", n, cresult, wrapper, actioncode);
+ bool should_own = GetFlag(n, "feature:new") != 0;
+
+ if (tm) {
+ Replaceall(tm, "$objecttype", Swig_scopename_last(SwigType_str(SwigType_strip_qualifiers(type), 0)));
+
+ if (should_own) {
+ Replaceall(tm, "$owner", "SWIG_POINTER_OWN");
+ } else {
+ Replaceall(tm, "$owner", "0");
+ }
+ Append(wrapper->code, tm);
+
+ if (Len(tm) > 0) {
+ Printf(wrapper->code, "\n");
+ }
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(type, 0), Getattr(n, "name"));
+ }
+
+ if (params) {
+ for (p = params; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(wrapper->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ }
+
+ Replaceall(wrapper->code, "$result", "jsresult");
+}
+
+void JSEmitter::emitCleanupCode(Node *n, Wrapper *wrapper, ParmList *params) {
+ Parm *p;
+ String *tm;
+
+ for (p = params; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ //addThrows(n, "tmap:freearg", p);
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(wrapper->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ if (GetFlag(n, "feature:new")) {
+ tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0);
+ if (tm != NIL) {
+ //addThrows(throws_hash, "newfree", n);
+ Printv(wrapper->code, tm, "\n", NIL);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(wrapper->code, "%s\n", tm);
+ Delete(tm);
+ }
+}
+
+int JSEmitter::switchNamespace(Node *n) {
+ // HACK: somehow this gets called for member functions.
+ // We can safely ignore them, as members are not associated to a namespace (only their class)
+ if (GetFlag(n, "ismember")) {
+ return SWIG_OK;
+ }
+
+ // if nspace is deactivated, everything goes into the global scope
+ if (!GetFlag(n, "feature:nspace")) {
+ current_namespace = Getattr(namespaces, "::");
+ return SWIG_OK;
+ }
+
+// EXPERIMENTAL: we want to use Language::getNSpace() here
+// However, it is not working yet.
+// For namespace functions Language::getNSpace() does not give a valid result
+#if 0
+ JAVASCRIPT *lang = static_cast<JAVASCRIPT*>(Language::instance());
+ String *_nspace = lang->getNSpace();
+ if (!Equal(nspace, _nspace)) {
+ Printf(stdout, "##### Custom vs Language::getNSpace(): %s | %s\n", nspace, _nspace);
+ }
+#endif
+
+ String *nspace = Getattr(n, "sym:nspace");
+
+ if (nspace == NULL) {
+ // It seems that only classes have 'sym:nspace' set.
+ // We try to get the namespace from the qualified name (i.e., everything before the last '::')
+ nspace = Swig_scopename_prefix(Getattr(n, "name"));
+ }
+
+ // If there is not even a scopename prefix then it must be global scope
+ if (nspace == NULL) {
+ current_namespace = Getattr(namespaces, "::");
+ return SWIG_OK;
+ }
+
+ String *scope = NewString(nspace);
+ // replace "." with "::" that we can use Swig_scopename_last
+ Replaceall(scope, ".", "::");
+
+ // if the scope is not yet registered
+ // create (parent) namespaces recursively
+ if (!Getattr(namespaces, scope)) {
+ createNamespace(scope);
+ }
+ current_namespace = Getattr(namespaces, scope);
+
+ return SWIG_OK;
+}
+
+int JSEmitter::createNamespace(String *scope) {
+
+ String *parent_scope = Swig_scopename_prefix(scope);
+ Hash *parent_namespace;
+ if (parent_scope == 0) {
+ parent_namespace = Getattr(namespaces, "::");
+ } else if (!Getattr(namespaces, parent_scope)) {
+ createNamespace(parent_scope);
+ parent_namespace = Getattr(namespaces, parent_scope);
+ } else {
+ parent_namespace = Getattr(namespaces, parent_scope);
+ }
+ assert(parent_namespace != 0);
+
+ Hash *new_namespace = createNamespaceEntry(Char(scope), Char(Getattr(parent_namespace, "name")), Char(Getattr(parent_namespace, "name_mangled")));
+ Setattr(namespaces, scope, new_namespace);
+
+ Delete(parent_scope);
+ return SWIG_OK;
+}
+
+Hash *JSEmitter::createNamespaceEntry(const char *_name, const char *parent, const char *parent_mangled) {
+ Hash *entry = NewHash();
+ String *name = NewString(_name);
+ Setattr(entry, NAME, Swig_scopename_last(name));
+ Setattr(entry, NAME_MANGLED, Swig_name_mangle(name));
+ Setattr(entry, PARENT, NewString(parent));
+ Setattr(entry, PARENT_MANGLED, NewString(parent_mangled));
+
+ Delete(name);
+ return entry;
+}
+
+/**********************************************************************
+ * JavascriptCore: JSEmitter implementation for JavascriptCore engine
+ **********************************************************************/
+
+class JSCEmitter:public JSEmitter {
+
+public:
+ JSCEmitter();
+ virtual ~ JSCEmitter();
+ virtual int initialize(Node *n);
+ virtual int dump(Node *n);
+ virtual int close();
+
+protected:
+ virtual int enterVariable(Node *n);
+ virtual int exitVariable(Node *n);
+ virtual int enterFunction(Node *n);
+ virtual int exitFunction(Node *n);
+ virtual int enterClass(Node *n);
+ virtual int exitClass(Node *n);
+ virtual void marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static);
+ virtual Hash *createNamespaceEntry(const char *name, const char *parent, const char *parent_mangled);
+ virtual int emitNamespaces();
+
+private:
+
+ String *NULL_STR;
+ String *VETO_SET;
+
+ // output file and major code parts
+ File *f_wrap_cpp;
+ File *f_runtime;
+ File *f_header;
+ File *f_init;
+
+};
+
+JSCEmitter::JSCEmitter()
+: JSEmitter(JSEmitter::JavascriptCore), NULL_STR(NewString("NULL")), VETO_SET(NewString("JS_veto_set_variable")), f_wrap_cpp(NULL), f_runtime(NULL), f_header(NULL), f_init(NULL) {
+}
+
+JSCEmitter::~JSCEmitter() {
+ Delete(NULL_STR);
+ Delete(VETO_SET);
+}
+
+
+/* ---------------------------------------------------------------------
+ * marshalInputArgs()
+ *
+ * Process all of the arguments passed into the argv array
+ * and convert them into C/C++ function arguments using the
+ * supplied typemaps.
+ * --------------------------------------------------------------------- */
+
+void JSCEmitter::marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static) {
+ Parm *p;
+ String *tm;
+
+ // determine an offset index, as members have an extra 'this' argument
+ // except: static members and ctors.
+ int startIdx = 0;
+ if (is_member && !is_static && mode != Ctor) {
+ startIdx = 1;
+ }
+ // store number of arguments for argument checks
+ int num_args = emit_num_arguments(parms) - startIdx;
+ String *argcount = NewString("");
+ Printf(argcount, "%d", num_args);
+ Setattr(n, ARGCOUNT, argcount);
+
+ // process arguments
+ int i = 0;
+ for (p = parms; p; i++) {
+ String *arg = NewString("");
+ String *type = Getattr(p, "type");
+
+ // ignore varargs
+ if (SwigType_isvarargs(type))
+ break;
+
+ switch (mode) {
+ case Getter:
+ case Function:
+ if (is_member && !is_static && i == 0) {
+ Printv(arg, "thisObject", 0);
+ } else {
+ Printf(arg, "argv[%d]", i - startIdx);
+ }
+ break;
+ case Setter:
+ if (is_member && !is_static && i == 0) {
+ Printv(arg, "thisObject", 0);
+ } else {
+ Printv(arg, "value", 0);
+ }
+ break;
+ case Ctor:
+ Printf(arg, "argv[%d]", i);
+ break;
+ default:
+ Printf(stderr, "Illegal MarshallingMode.");
+ Exit(EXIT_FAILURE);
+ }
+ tm = emitInputTypemap(n, p, wrapper, arg);
+ Delete(arg);
+ if (tm) {
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+}
+
+int JSCEmitter::initialize(Node *n) {
+
+ JSEmitter::initialize(n);
+
+ /* Get the output file name */
+ String *outfile = Getattr(n, "outfile");
+
+ /* Initialize I/O */
+ f_wrap_cpp = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_wrap_cpp) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+
+ /* Initialization of members */
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+
+ state.globals(CREATE_NAMESPACES, NewString(""));
+ state.globals(REGISTER_NAMESPACES, NewString(""));
+ state.globals(INITIALIZER, NewString(""));
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("begin", f_wrap_cpp);
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+
+ Swig_banner(f_wrap_cpp);
+
+ Swig_obligatory_macros(f_runtime, "JAVASCRIPT");
+
+ return SWIG_OK;
+}
+
+int JSCEmitter::dump(Node *n) {
+ /* Get the module name */
+ String *module = Getattr(n, "name");
+
+ Template initializer_define(getTemplate("js_initializer_define"));
+ initializer_define.replace("$jsname", module).pretty_print(f_header);
+
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+
+ Printv(f_wrap_cpp, f_runtime, "\n", 0);
+ Printv(f_wrap_cpp, f_header, "\n", 0);
+ Printv(f_wrap_cpp, f_wrappers, "\n", 0);
+
+ emitNamespaces();
+
+ // compose the initializer function using a template
+ Template initializer(getTemplate("js_initializer"));
+ initializer.replace("$jsname", module)
+ .replace("$jsregisterclasses", state.globals(INITIALIZER))
+ .replace("$jscreatenamespaces", state.globals(CREATE_NAMESPACES))
+ .replace("$jsregisternamespaces", state.globals(REGISTER_NAMESPACES))
+ .pretty_print(f_init);
+
+ Printv(f_wrap_cpp, f_init, 0);
+
+ return SWIG_OK;
+}
+
+int JSCEmitter::close() {
+ Delete(f_runtime);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(namespaces);
+ Delete(f_wrap_cpp);
+ return SWIG_OK;
+}
+
+int JSCEmitter::enterFunction(Node *n) {
+
+ JSEmitter::enterFunction(n);
+
+ return SWIG_OK;
+}
+
+int JSCEmitter::exitFunction(Node *n) {
+ Template t_function = getTemplate("jsc_function_declaration");
+
+ bool is_member = GetFlag(n, "ismember") != 0 || GetFlag(n, "feature:extend") != 0;
+ bool is_overloaded = GetFlag(n, "sym:overloaded") != 0;
+
+ // handle overloaded functions
+ if (is_overloaded) {
+ if (!Getattr(n, "sym:nextSibling")) {
+ //state.function(WRAPPER_NAME, Swig_name_wrapper(Getattr(n, "name")));
+ // create dispatcher
+ emitFunctionDispatcher(n, is_member);
+ } else {
+ //don't register wrappers of overloaded functions in function tables
+ return SWIG_OK;
+ }
+ }
+
+ t_function.replace("$jsname", state.function(NAME))
+ .replace("$jswrapper", state.function(WRAPPER_NAME));
+
+ if (is_member) {
+ if (GetFlag(state.function(), IS_STATIC)) {
+ t_function.pretty_print(state.clazz(STATIC_FUNCTIONS));
+ } else {
+ t_function.pretty_print(state.clazz(MEMBER_FUNCTIONS));
+ }
+ } else {
+ t_function.pretty_print(Getattr(current_namespace, "functions"));
+ }
+
+ return SWIG_OK;
+}
+
+int JSCEmitter::enterVariable(Node *n) {
+ JSEmitter::enterVariable(n);
+ state.variable(GETTER, NULL_STR);
+ state.variable(SETTER, VETO_SET);
+ return SWIG_OK;
+}
+
+int JSCEmitter::exitVariable(Node *n) {
+ Template t_variable(getTemplate("jsc_variable_declaration"));
+ t_variable.replace("$jsname", state.variable(NAME))
+ .replace("$jsgetter", state.variable(GETTER))
+ .replace("$jssetter", state.variable(SETTER));
+
+ if (GetFlag(n, "ismember")) {
+ if (GetFlag(state.variable(), IS_STATIC)
+ || Equal(Getattr(n, "nodeType"), "enumitem")) {
+ t_variable.pretty_print(state.clazz(STATIC_VARIABLES));
+ } else {
+ t_variable.pretty_print(state.clazz(MEMBER_VARIABLES));
+ }
+ } else {
+ t_variable.pretty_print(Getattr(current_namespace, "values"));
+ }
+
+ return SWIG_OK;
+}
+
+int JSCEmitter::enterClass(Node *n) {
+ JSEmitter::enterClass(n);
+ state.clazz(MEMBER_VARIABLES, NewString(""));
+ state.clazz(MEMBER_FUNCTIONS, NewString(""));
+ state.clazz(STATIC_VARIABLES, NewString(""));
+ state.clazz(STATIC_FUNCTIONS, NewString(""));
+
+ Template t_class_decl = getTemplate("jsc_class_declaration");
+ t_class_decl.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .pretty_print(f_wrappers);
+
+ return SWIG_OK;
+}
+
+int JSCEmitter::exitClass(Node *n) {
+ Template t_class_tables(getTemplate("jsc_class_tables"));
+ t_class_tables.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsclassvariables", state.clazz(MEMBER_VARIABLES))
+ .replace("$jsclassfunctions", state.clazz(MEMBER_FUNCTIONS))
+ .replace("$jsstaticclassfunctions", state.clazz(STATIC_FUNCTIONS))
+ .replace("$jsstaticclassvariables", state.clazz(STATIC_VARIABLES))
+ .pretty_print(f_wrappers);
+
+ /* adds the ctor wrappers at this position */
+ // Note: this is necessary to avoid extra forward declarations.
+ //Append(f_wrappers, state.clazz(CTOR_WRAPPERS));
+
+ // for abstract classes add a vetoing ctor
+ if (GetFlag(state.clazz(), IS_ABSTRACT)) {
+ Template t_veto_ctor(getTemplate("js_veto_ctor"));
+ t_veto_ctor.replace("$jswrapper", state.clazz(CTOR))
+ .replace("$jsname", state.clazz(NAME))
+ .pretty_print(f_wrappers);
+ }
+
+ /* adds a class template statement to initializer function */
+ Template t_classtemplate(getTemplate("jsc_class_definition"));
+
+ /* prepare registration of base class */
+ String *jsclass_inheritance = NewString("");
+ Node *base_class = getBaseClass(n);
+ if (base_class != NULL) {
+ Template t_inherit(getTemplate("jsc_class_inherit"));
+ t_inherit.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsbaseclassmangled", SwigType_manglestr(Getattr(base_class, "name")))
+ .pretty_print(jsclass_inheritance);
+ } else {
+ Template t_inherit(getTemplate("jsc_class_noinherit"));
+ t_inherit.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .pretty_print(jsclass_inheritance);
+ }
+
+ t_classtemplate.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
+ .replace("$jsclass_inheritance", jsclass_inheritance)
+ .replace("$jsctor", state.clazz(CTOR))
+ .replace("$jsdtor", state.clazz(DTOR))
+ .pretty_print(state.globals(INITIALIZER));
+ Delete(jsclass_inheritance);
+
+ /* Note: this makes sure that there is a swig_type added for this class */
+ SwigType_remember_clientdata(state.clazz(TYPE_MANGLED), NewString("0"));
+
+ /* adds a class registration statement to initializer function */
+ Template t_registerclass(getTemplate("jsc_class_registration"));
+ t_registerclass.replace("$jsname", state.clazz(NAME))
+ .replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsnspace", Getattr(state.clazz("nspace"), NAME_MANGLED))
+ .pretty_print(state.globals(INITIALIZER));
+
+ return SWIG_OK;
+}
+
+Hash *JSCEmitter::createNamespaceEntry(const char *name, const char *parent, const char *parent_mangled) {
+ Hash *entry = JSEmitter::createNamespaceEntry(name, parent, parent_mangled);
+ Setattr(entry, "functions", NewString(""));
+ Setattr(entry, "values", NewString(""));
+ return entry;
+}
+
+int JSCEmitter::emitNamespaces() {
+ Iterator it;
+ for (it = First(namespaces); it.item; it = Next(it)) {
+ Hash *entry = it.item;
+ String *name = Getattr(entry, NAME);
+ String *name_mangled = Getattr(entry, NAME_MANGLED);
+ String *parent_mangled = Getattr(entry, PARENT_MANGLED);
+ String *functions = Getattr(entry, "functions");
+ String *variables = Getattr(entry, "values");
+
+ // skip the global namespace which is given by the application
+
+ Template namespace_definition(getTemplate("jsc_nspace_declaration"));
+ namespace_definition.replace("$jsglobalvariables", variables)
+ .replace("$jsglobalfunctions", functions)
+ .replace("$jsnspace", name_mangled)
+ .replace("$jsmangledname", name_mangled)
+ .pretty_print(f_wrap_cpp);
+
+ Template t_createNamespace(getTemplate("jsc_nspace_definition"));
+ t_createNamespace.replace("$jsmangledname", name_mangled);
+ Append(state.globals(CREATE_NAMESPACES), t_createNamespace.str());
+
+ // Don't register 'exports' as namespace. It is return to the application.
+ if (!Equal("exports", name)) {
+ Template t_registerNamespace(getTemplate("jsc_nspace_registration"));
+ t_registerNamespace.replace("$jsmangledname", name_mangled)
+ .replace("$jsname", name)
+ .replace("$jsparent", parent_mangled);
+ Append(state.globals(REGISTER_NAMESPACES), t_registerNamespace.str());
+ }
+ }
+
+ return SWIG_OK;
+}
+
+JSEmitter *swig_javascript_create_JSCEmitter() {
+ return new JSCEmitter();
+}
+
+/**********************************************************************
+ * V8: JSEmitter implementation for V8 engine
+ **********************************************************************/
+
+class V8Emitter:public JSEmitter {
+
+public:
+ V8Emitter();
+
+ virtual ~ V8Emitter();
+ virtual int initialize(Node *n);
+ virtual int dump(Node *n);
+ virtual int close();
+ virtual int enterClass(Node *n);
+ virtual int exitClass(Node *n);
+ virtual int enterVariable(Node *n);
+ virtual int exitVariable(Node *n);
+ virtual int exitFunction(Node *n);
+
+protected:
+ virtual void marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static);
+ virtual int emitNamespaces();
+
+protected:
+ /* built-in parts */
+ String *f_runtime;
+ String *f_header;
+ String *f_init;
+ String *f_post_init;
+
+ /* part for class templates */
+ String *f_class_templates;
+
+ /* parts for initilizer */
+ String *f_init_namespaces;
+ String *f_init_class_templates;
+ String *f_init_wrappers;
+ String *f_init_inheritance;
+ String *f_init_class_instances;
+ String *f_init_static_wrappers;
+ String *f_init_register_classes;
+ String *f_init_register_namespaces;
+
+ // the output cpp file
+ File *f_wrap_cpp;
+
+ String *NULL_STR;
+ String *VETO_SET;
+ String *moduleName;
+
+};
+
+V8Emitter::V8Emitter()
+: JSEmitter(JSEmitter::V8), NULL_STR(NewString("0")), VETO_SET(NewString("JS_veto_set_variable")) {
+}
+
+V8Emitter::~V8Emitter() {
+ Delete(NULL_STR);
+ Delete(VETO_SET);
+}
+
+int V8Emitter::initialize(Node *n) {
+ JSEmitter::initialize(n);
+
+ moduleName = Getattr(n, "name");
+
+ // Get the output file name
+ String *outfile = Getattr(n, "outfile");
+ f_wrap_cpp = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_wrap_cpp) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+
+ f_runtime = NewString("");
+ f_header = NewString("");
+ f_class_templates = NewString("");
+ f_init = NewString("");
+ f_post_init = NewString("");
+
+ f_init_namespaces = NewString("");
+ f_init_class_templates = NewString("");
+ f_init_wrappers = NewString("");
+ f_init_inheritance = NewString("");
+ f_init_class_instances = NewString("");
+ f_init_static_wrappers = NewString("");
+ f_init_register_classes = NewString("");
+ f_init_register_namespaces = NewString("");
+
+ // note: this is necessary for built-in generation of SWIG runtime code
+ Swig_register_filebyname("begin", f_wrap_cpp);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("post-init", f_post_init);
+
+ state.globals(FORCE_CPP, NewString("1"));
+
+ Swig_banner(f_wrap_cpp);
+
+ Swig_obligatory_macros(f_runtime, "JAVASCRIPT");
+
+ return SWIG_OK;
+}
+
+int V8Emitter::dump(Node *n) {
+ /* Get the module name */
+ String *module = Getattr(n, "name");
+
+ Template initializer_define(getTemplate("js_initializer_define"));
+ initializer_define.replace("$jsname", module).pretty_print(f_header);
+
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+
+ Printv(f_wrap_cpp, f_runtime, "\n", 0);
+ Printv(f_wrap_cpp, f_header, "\n", 0);
+ Printv(f_wrap_cpp, f_class_templates, "\n", 0);
+ Printv(f_wrap_cpp, f_wrappers, "\n", 0);
+
+ emitNamespaces();
+
+ // compose the initializer function using a template
+ // filled with sub-parts
+ Template initializer(getTemplate("js_initializer"));
+ initializer.replace("$jsname", moduleName)
+ .replace("$jsv8nspaces", f_init_namespaces)
+ .replace("$jsv8classtemplates", f_init_class_templates)
+ .replace("$jsv8wrappers", f_init_wrappers)
+ .replace("$jsv8inheritance", f_init_inheritance)
+ .replace("$jsv8classinstances", f_init_class_instances)
+ .replace("$jsv8staticwrappers", f_init_static_wrappers)
+ .replace("$jsv8registerclasses", f_init_register_classes)
+ .replace("$jsv8registernspaces", f_init_register_namespaces);
+ Printv(f_init, initializer.str(), 0);
+
+ Printv(f_wrap_cpp, f_init, 0);
+
+ Printv(f_wrap_cpp, f_post_init, 0);
+
+ return SWIG_OK;
+}
+
+int V8Emitter::close() {
+ Delete(f_runtime);
+ Delete(f_header);
+ Delete(f_class_templates);
+ Delete(f_init_namespaces);
+ Delete(f_init_class_templates);
+ Delete(f_init_wrappers);
+ Delete(f_init_inheritance);
+ Delete(f_init_class_instances);
+ Delete(f_init_static_wrappers);
+ Delete(f_init_register_classes);
+ Delete(f_init_register_namespaces);
+ Delete(f_init);
+ Delete(f_post_init);
+ Delete(f_wrap_cpp);
+ return SWIG_OK;
+}
+
+int V8Emitter::enterClass(Node *n) {
+ JSEmitter::enterClass(n);
+
+ // emit declaration of a v8 class template
+ Template t_decl_class(getTemplate("jsv8_declare_class_template"));
+ t_decl_class.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .trim()
+ .pretty_print(f_class_templates);
+
+ return SWIG_OK;
+}
+
+int V8Emitter::exitClass(Node *n) {
+ if (GetFlag(state.clazz(), IS_ABSTRACT)) {
+ Template t_veto_ctor(getTemplate("js_veto_ctor"));
+ t_veto_ctor.replace("$jswrapper", state.clazz(CTOR))
+ .replace("$jsname", state.clazz(NAME))
+ .pretty_print(f_wrappers);
+ }
+
+ /* Note: this makes sure that there is a swig_type added for this class */
+ String *clientData = NewString("");
+ Printf(clientData, "&%s_clientData", state.clazz(NAME_MANGLED));
+
+ /* Note: this makes sure that there is a swig_type added for this class */
+ SwigType_remember_clientdata(state.clazz(TYPE_MANGLED), NewString("0"));
+
+ // emit definition of v8 class template
+ Template t_def_class = getTemplate("jsv8_define_class_template");
+ t_def_class.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsname", state.clazz(NAME))
+ .replace("$jsmangledtype", state.clazz(TYPE_MANGLED))
+ .replace("$jsdtor", state.clazz(DTOR))
+ .trim()
+ .pretty_print(f_init_class_templates);
+
+ Template t_class_instance = getTemplate("jsv8_create_class_instance");
+ t_class_instance.replace("$jsname", state.clazz(NAME))
+ .replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsctor", state.clazz(CTOR))
+ .trim()
+ .pretty_print(f_init_class_instances);
+
+ // emit inheritance setup
+ Node *baseClass = getBaseClass(n);
+ if (baseClass) {
+ String *base_name = Getattr(baseClass, "name");
+
+ Template t_inherit = getTemplate("jsv8_inherit");
+
+ String *base_name_mangled = SwigType_manglestr(base_name);
+ t_inherit.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsbaseclass", base_name_mangled)
+ .trim()
+ .pretty_print(f_init_inheritance);
+ Delete(base_name_mangled);
+ }
+ // emit registration of class template
+ Template t_register = getTemplate("jsv8_register_class");
+ t_register.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsname", state.clazz(NAME))
+ .replace("$jsparent", Getattr(state.clazz("nspace"), NAME_MANGLED))
+ .trim()
+ .pretty_print(f_init_register_classes);
+
+ return SWIG_OK;
+}
+
+int V8Emitter::enterVariable(Node *n) {
+ JSEmitter::enterVariable(n);
+
+ state.variable(GETTER, NULL_STR);
+ state.variable(SETTER, VETO_SET);
+
+ return SWIG_OK;
+}
+
+int V8Emitter::exitVariable(Node *n) {
+ if (GetFlag(n, "ismember")) {
+ if (GetFlag(state.variable(), IS_STATIC) || Equal(Getattr(n, "nodeType"), "enumitem")) {
+ Template t_register = getTemplate("jsv8_register_static_variable");
+ t_register.replace("$jsparent", state.clazz(NAME_MANGLED))
+ .replace("$jsname", state.variable(NAME))
+ .replace("$jsgetter", state.variable(GETTER))
+ .replace("$jssetter", state.variable(SETTER))
+ .trim()
+ .pretty_print(f_init_static_wrappers);
+ } else {
+ Template t_register = getTemplate("jsv8_register_member_variable");
+ t_register.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsname", state.variable(NAME))
+ .replace("$jsgetter", state.variable(GETTER))
+ .replace("$jssetter", state.variable(SETTER))
+ .trim()
+ .pretty_print(f_init_wrappers);
+ }
+ } else {
+ // Note: a global variable is treated like a static variable
+ // with the parent being a nspace object (instead of class object)
+ Template t_register = getTemplate("jsv8_register_static_variable");
+ t_register.replace("$jsparent", Getattr(current_namespace, NAME_MANGLED))
+ .replace("$jsname", state.variable(NAME))
+ .replace("$jsgetter", state.variable(GETTER))
+ .replace("$jssetter", state.variable(SETTER))
+ .trim()
+ .pretty_print(f_init_wrappers);
+ }
+
+ return SWIG_OK;
+}
+
+int V8Emitter::exitFunction(Node *n) {
+ bool is_member = GetFlag(n, "ismember") != 0 || GetFlag(n, "feature:extend") != 0;
+
+ // create a dispatcher for overloaded functions
+ bool is_overloaded = GetFlag(n, "sym:overloaded") != 0;
+ if (is_overloaded) {
+ if (!Getattr(n, "sym:nextSibling")) {
+ //state.function(WRAPPER_NAME, Swig_name_wrapper(Getattr(n, "name")));
+ emitFunctionDispatcher(n, is_member);
+ } else {
+ //don't register wrappers of overloaded functions in function tables
+ return SWIG_OK;
+ }
+ }
+ // register the function at the specific context
+ if (is_member) {
+ if (GetFlag(state.function(), IS_STATIC)) {
+ Template t_register = getTemplate("jsv8_register_static_function");
+ t_register.replace("$jsparent", state.clazz(NAME_MANGLED))
+ .replace("$jsname", state.function(NAME))
+ .replace("$jswrapper", state.function(WRAPPER_NAME))
+ .trim()
+ .pretty_print(f_init_static_wrappers);
+ } else {
+ Template t_register = getTemplate("jsv8_register_member_function");
+ t_register.replace("$jsmangledname", state.clazz(NAME_MANGLED))
+ .replace("$jsname", state.function(NAME))
+ .replace("$jswrapper", state.function(WRAPPER_NAME))
+ .trim()
+ .pretty_print(f_init_wrappers);
+ }
+ } else {
+ // Note: a global function is treated like a static function
+ // with the parent being a nspace object instead of class object
+ Template t_register = getTemplate("jsv8_register_static_function");
+ t_register.replace("$jsparent", Getattr(current_namespace, NAME_MANGLED))
+ .replace("$jsname", state.function(NAME))
+ .replace("$jswrapper", state.function(WRAPPER_NAME))
+ .trim()
+ .pretty_print(f_init_static_wrappers);
+ }
+
+ return SWIG_OK;
+}
+
+void V8Emitter::marshalInputArgs(Node *n, ParmList *parms, Wrapper *wrapper, MarshallingMode mode, bool is_member, bool is_static) {
+ Parm *p;
+ String *tm;
+
+ int startIdx = 0;
+ if (is_member && !is_static && mode != Ctor) {
+ startIdx = 1;
+ }
+ // store number of arguments for argument checks
+ int num_args = emit_num_arguments(parms) - startIdx;
+ String *argcount = NewString("");
+ Printf(argcount, "%d", num_args);
+ Setattr(n, ARGCOUNT, argcount);
+
+ int i = 0;
+ for (p = parms; p; i++) {
+ String *arg = NewString("");
+ String *type = Getattr(p, "type");
+
+ // ignore varargs
+ if (SwigType_isvarargs(type))
+ break;
+
+ switch (mode) {
+ case Getter:
+ if (is_member && !is_static && i == 0) {
+ Printv(arg, "info.Holder()", 0);
+ } else {
+ Printf(arg, "args[%d]", i - startIdx);
+ }
+ break;
+ case Function:
+ if (is_member && !is_static && i == 0) {
+ Printv(arg, "args.Holder()", 0);
+ } else {
+ Printf(arg, "args[%d]", i - startIdx);
+ }
+ break;
+ case Setter:
+ if (is_member && !is_static && i == 0) {
+ Printv(arg, "info.Holder()", 0);
+ } else {
+ Printv(arg, "value", 0);
+ }
+ break;
+ case Ctor:
+ Printf(arg, "args[%d]", i);
+ break;
+ default:
+ Printf(stderr, "Illegal MarshallingMode.");
+ Exit(EXIT_FAILURE);
+ }
+
+ tm = emitInputTypemap(n, p, wrapper, arg);
+ Delete(arg);
+
+ if (tm) {
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+}
+
+int V8Emitter::emitNamespaces() {
+ Iterator it;
+ for (it = First(namespaces); it.item; it = Next(it)) {
+ Hash *entry = it.item;
+ String *name = Getattr(entry, NAME);
+ String *name_mangled = Getattr(entry, NAME_MANGLED);
+ String *parent = Getattr(entry, PARENT);
+ String *parent_mangled = Getattr(entry, PARENT_MANGLED);
+
+ bool do_create = true;
+ bool do_register = true;
+
+ if (Equal(parent, "")) {
+ do_register = false;
+ }
+ // Note: 'exports' is by convention the name of the object where
+ // globals are stored into
+ if (Equal(name, "exports")) {
+ do_create = false;
+ }
+
+ if (do_create) {
+ // create namespace object and register it to the parent scope
+ Template t_create_ns = getTemplate("jsv8_create_namespace");
+ t_create_ns.replace("$jsmangledname", name_mangled)
+ .trim()
+ .pretty_print(f_init_namespaces);
+ }
+
+ if (do_register) {
+ Template t_register_ns = getTemplate("jsv8_register_namespace");
+ t_register_ns.replace("$jsmangledname", name_mangled)
+ .replace("$jsname", name)
+ .replace("$jsparent", parent_mangled)
+ .trim();
+
+ // prepend in order to achieve reversed order of registration statements
+ String *tmp_register_stmt = NewString("");
+ t_register_ns.pretty_print(tmp_register_stmt);
+ Insert(f_init_register_namespaces, 0, tmp_register_stmt);
+ Delete(tmp_register_stmt);
+ }
+ }
+
+ return SWIG_OK;
+}
+
+JSEmitter *swig_javascript_create_V8Emitter() {
+ return new V8Emitter();
+}
+
+/**********************************************************************
+ * Helper implementations
+ **********************************************************************/
+
+JSEmitterState::JSEmitterState()
+: globalHash(NewHash()) {
+ // initialize sub-hashes
+ Setattr(globalHash, "class", NewHash());
+ Setattr(globalHash, "function", NewHash());
+ Setattr(globalHash, "variable", NewHash());
+}
+
+JSEmitterState::~JSEmitterState() {
+ Delete(globalHash);
+}
+
+DOH *JSEmitterState::getState(const char *key, bool new_key) {
+ if (new_key) {
+ Hash *hash = NewHash();
+ Setattr(globalHash, key, hash);
+ }
+ return Getattr(globalHash, key);
+}
+
+DOH *JSEmitterState::globals() {
+ return globalHash;
+}
+
+DOH *JSEmitterState::globals(const char *key, DOH *initial) {
+ if (initial != 0) {
+ Setattr(globalHash, key, initial);
+ }
+ return Getattr(globalHash, key);
+}
+
+DOH *JSEmitterState::clazz(bool new_key) {
+ return getState("class", new_key);
+}
+
+DOH *JSEmitterState::clazz(const char *key, DOH *initial) {
+ DOH *c = clazz();
+ if (initial != 0) {
+ Setattr(c, key, initial);
+ }
+ return Getattr(c, key);
+}
+
+DOH *JSEmitterState::function(bool new_key) {
+ return getState("function", new_key);
+}
+
+DOH *JSEmitterState::function(const char *key, DOH *initial) {
+ DOH *f = function();
+ if (initial != 0) {
+ Setattr(f, key, initial);
+ }
+ return Getattr(f, key);
+}
+
+DOH *JSEmitterState::variable(bool new_key) {
+ return getState("variable", new_key);
+}
+
+DOH *JSEmitterState::variable(const char *key, DOH *initial) {
+ DOH *v = variable();
+ if (initial != 0) {
+ Setattr(v, key, initial);
+ }
+ return Getattr(v, key);
+}
+
+/*static*/
+int JSEmitterState::IsSet(DOH *val) {
+ if (!val) {
+ return 0;
+ } else {
+ const char *cval = Char(val);
+ if (!cval)
+ return 0;
+ return (strcmp(cval, "0") != 0) ? 1 : 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Template::Template() : creates a Template class for given template code
+ * ----------------------------------------------------------------------------- */
+
+Template::Template(const String *code_) {
+
+ if (!code_) {
+ Printf(stdout, "Template code was null. Illegal input for template.");
+ Exit(EXIT_FAILURE);
+ }
+ code = NewString(code_);
+ templateName = NewString("");
+}
+
+Template::Template(const String *code_, const String *templateName_) {
+
+ if (!code_) {
+ Printf(stdout, "Template code was null. Illegal input for template.");
+ Exit(EXIT_FAILURE);
+ }
+
+ code = NewString(code_);
+ templateName = NewString(templateName_);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Template::~Template() : cleans up of Template.
+ * ----------------------------------------------------------------------------- */
+
+Template::~Template() {
+ Delete(code);
+ Delete(templateName);
+}
+
+/* -----------------------------------------------------------------------------
+ * String* Template::str() : retrieves the current content of the template.
+ * ----------------------------------------------------------------------------- */
+
+String *Template::str() {
+ if (js_template_enable_debug) {
+ String *pre_code = NewString("");
+ String *post_code = NewString("");
+ String *debug_code = NewString("");
+ Printf(pre_code, "/* begin fragment(\"%s\") */", templateName);
+ Printf(post_code, "/* end fragment(\"%s\") */", templateName);
+ Printf(debug_code, "%s\n%s\n%s\n", pre_code, code, post_code);
+
+ Delete(code);
+ Delete(pre_code);
+ Delete(post_code);
+
+ code = debug_code;
+ }
+ return code;
+}
+
+Template & Template::trim() {
+ const char *str = Char(code);
+ if (str == 0)
+ return *this;
+
+ int length = Len(code);
+ if (length == 0)
+ return *this;
+
+ int idx;
+ for (idx = 0; idx < length; ++idx) {
+ if (str[idx] != ' ' && str[idx] != '\t' && str[idx] != '\r' && str[idx] != '\n')
+ break;
+ }
+ int start_pos = idx;
+
+ for (idx = length - 1; idx >= start_pos; --idx) {
+ if (str[idx] != ' ' && str[idx] != '\t' && str[idx] != '\r' && str[idx] != '\n')
+ break;
+ }
+ int end_pos = idx;
+
+ int new_length = end_pos - start_pos + 1;
+ char *newstr = new char[new_length + 1];
+ memcpy(newstr, str + start_pos, new_length);
+ newstr[new_length] = 0;
+
+ Delete(code);
+ code = NewString(newstr);
+ delete[]newstr;
+
+ return *this;
+}
+
+/* -----------------------------------------------------------------------------
+ * Template& Template::replace(const String* pattern, const String* repl) :
+ *
+ * replaces all occurrences of a given pattern with a given replacement.
+ *
+ * - pattern: the pattern to be replaced
+ * - repl: the replacement string
+ * - returns a reference to the Template to allow chaining of methods.
+ * ----------------------------------------------------------------------------- */
+
+Template & Template::replace(const String *pattern, const String *repl) {
+ Replaceall(code, pattern, repl);
+ return *this;
+}
+
+Template & Template::print(DOH *doh) {
+ Printv(doh, str(), 0);
+ return *this;
+}
+
+Template & Template::pretty_print(DOH *doh) {
+ Wrapper_pretty_print(str(), doh);
+ return *this;
+}
+
+Template::Template(const Template & t) {
+ code = NewString(t.code);
+ templateName = NewString(t.templateName);
+}
+
+void Template::operator=(const Template & t) {
+ Delete(code);
+ Delete(templateName);
+ code = NewString(t.code);
+ templateName = NewString(t.templateName);
+}
diff --git a/contrib/tools/swig/Source/Modules/lang.cxx b/contrib/tools/swig/Source/Modules/lang.cxx
new file mode 100644
index 0000000000..1e10e51d67
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/lang.cxx
@@ -0,0 +1,3902 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * lang.cxx
+ *
+ * Language base class functions. Default C++ handling is also implemented here.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+#include <ctype.h>
+
+/* default mode settings */
+static int director_mode = 0;
+static int director_protected_mode = 1;
+static int all_protected_mode = 0;
+static int naturalvar_mode = 0;
+Language *Language::this_ = 0;
+
+/* Set director_protected_mode */
+void Wrapper_director_mode_set(int flag) {
+ director_mode = flag;
+}
+
+void Wrapper_director_protected_mode_set(int flag) {
+ director_protected_mode = flag;
+}
+
+void Wrapper_all_protected_mode_set(int flag) {
+ all_protected_mode = flag;
+}
+
+void Wrapper_naturalvar_mode_set(int flag) {
+ naturalvar_mode = flag;
+}
+
+extern "C" {
+ int Swig_director_mode() {
+ return director_mode;
+ }
+ int Swig_director_protected_mode() {
+ return director_protected_mode;
+ }
+ int Swig_all_protected_mode() {
+ return all_protected_mode;
+ }
+ void Language_replace_special_variables(String *method, String *tm, Parm *parm) {
+ Language::instance()->replaceSpecialVariables(method, tm, parm);
+ }
+}
+
+/* Some status variables used during parsing */
+static int InClass = 0; /* Parsing C++ or not */
+static String *ClassName = 0; /* This is the real name of the current class */
+static String *EnumClassName = 0; /* Enum class name */
+static String *ClassPrefix = 0; /* Class prefix */
+static String *EnumClassPrefix = 0; /* Prefix for strongly typed enums (including ClassPrefix) */
+static String *NSpace = 0; /* Namespace for the nspace feature */
+static String *ClassType = 0; /* Fully qualified type name to use */
+static String *DirectorClassName = 0; /* Director name of the current class */
+int Abstract = 0;
+int ImportMode = 0;
+int IsVirtual = 0;
+static String *AttributeFunctionGet = 0;
+static String *AttributeFunctionSet = 0;
+static Node *CurrentClass = 0;
+int line_number = 0;
+String *input_file = 0;
+int SmartPointer = 0;
+static Hash *classhash;
+
+extern int GenerateDefault;
+extern int ForceExtern;
+extern int AddExtern;
+extern "C" {
+ extern int UseWrapperSuffix;
+}
+
+/* import modes */
+
+#define IMPORT_MODE 1
+
+/* ----------------------------------------------------------------------
+ * Dispatcher::emit_one()
+ *
+ * Dispatch a single node
+ * ---------------------------------------------------------------------- */
+
+int Dispatcher::emit_one(Node *n) {
+ int ret = SWIG_OK;
+
+ char *tag = Char(nodeType(n));
+ if (!tag) {
+ /* Printf(stderr,"SWIG: Fatal internal error. Malformed parse tree
+ node!\n"); */
+ return SWIG_OK;
+ }
+
+ /* Do not proceed if marked with an error */
+
+ if (Getattr(n, "error"))
+ return SWIG_OK;
+
+ /* Look for warnings */
+ String *wrn = Getattr(n, "feature:warnfilter");
+ if (wrn)
+ Swig_warnfilter(wrn, 1);
+
+ /* ============================================================
+ * C/C++ parsing
+ * ============================================================ */
+
+ if (strcmp(tag, "extern") == 0) {
+ ret = externDeclaration(n);
+ } else if (strcmp(tag, "cdecl") == 0) {
+ ret = cDeclaration(n);
+ } else if (strcmp(tag, "enum") == 0) {
+ ret = enumDeclaration(n);
+ } else if (strcmp(tag, "enumitem") == 0) {
+ ret = enumvalueDeclaration(n);
+ } else if (strcmp(tag, "enumforward") == 0) {
+ ret = enumforwardDeclaration(n);
+ } else if (strcmp(tag, "class") == 0) {
+ ret = classDeclaration(n);
+ } else if (strcmp(tag, "classforward") == 0) {
+ ret = classforwardDeclaration(n);
+ } else if (strcmp(tag, "constructor") == 0) {
+ ret = constructorDeclaration(n);
+ } else if (strcmp(tag, "destructor") == 0) {
+ ret = destructorDeclaration(n);
+ } else if (strcmp(tag, "access") == 0) {
+ ret = accessDeclaration(n);
+ } else if (strcmp(tag, "using") == 0) {
+ ret = usingDeclaration(n);
+ } else if (strcmp(tag, "namespace") == 0) {
+ ret = namespaceDeclaration(n);
+ } else if (strcmp(tag, "template") == 0) {
+ ret = templateDeclaration(n);
+ } else if (strcmp(tag, "lambda") == 0) {
+ ret = lambdaDeclaration(n);
+ }
+
+ /* ===============================================================
+ * SWIG directives
+ * =============================================================== */
+
+ else if (strcmp(tag, "top") == 0) {
+ ret = top(n);
+ } else if (strcmp(tag, "extend") == 0) {
+ ret = extendDirective(n);
+ } else if (strcmp(tag, "apply") == 0) {
+ ret = applyDirective(n);
+ } else if (strcmp(tag, "clear") == 0) {
+ ret = clearDirective(n);
+ } else if (strcmp(tag, "constant") == 0) {
+ ret = constantDirective(n);
+ } else if (strcmp(tag, "fragment") == 0) {
+ ret = fragmentDirective(n);
+ } else if (strcmp(tag, "import") == 0) {
+ ret = importDirective(n);
+ } else if (strcmp(tag, "include") == 0) {
+ ret = includeDirective(n);
+ } else if (strcmp(tag, "insert") == 0) {
+ ret = insertDirective(n);
+ } else if (strcmp(tag, "module") == 0) {
+ ret = moduleDirective(n);
+ } else if (strcmp(tag, "native") == 0) {
+ ret = nativeDirective(n);
+ } else if (strcmp(tag, "pragma") == 0) {
+ ret = pragmaDirective(n);
+ } else if (strcmp(tag, "typemap") == 0) {
+ ret = typemapDirective(n);
+ } else if (strcmp(tag, "typemapcopy") == 0) {
+ ret = typemapcopyDirective(n);
+ } else if (strcmp(tag, "typemapitem") == 0) {
+ ret = typemapitemDirective(n);
+ } else if (strcmp(tag, "types") == 0) {
+ ret = typesDirective(n);
+ } else {
+ Swig_error(input_file, line_number, "Unrecognized parse tree node type '%s'\n", tag);
+ ret = SWIG_ERROR;
+ }
+ if (wrn)
+ Swig_warnfilter(wrn, 0);
+ return ret;
+}
+
+/* ----------------------------------------------------------------------
+ * Dispatcher::emit_children()
+ *
+ * Emit all children that match the given type. type = 0 means all types.
+ * ---------------------------------------------------------------------- */
+
+int Dispatcher::emit_children(Node *n) {
+ Node *c;
+ char *eo = Char(Getattr(n, "feature:emitonlychildren"));
+ for (c = firstChild(n); c; c = nextSibling(c)) {
+ if (eo) {
+ const char *tag = Char(nodeType(c));
+ if (strcmp(tag, "cdecl") == 0) {
+ if (checkAttribute(c, "storage", "typedef"))
+ tag = "typedef";
+ }
+ if (strstr(eo, tag) == 0) {
+ continue;
+ }
+ }
+ emit_one(c);
+ }
+ return SWIG_OK;
+}
+
+
+/* Stubs for dispatcher class. We don't do anything by default---up to derived class
+ to fill in traversal code */
+
+int Dispatcher::defaultHandler(Node *) {
+ return SWIG_OK;
+}
+int Dispatcher::extendDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::applyDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::clearDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::constantDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::fragmentDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::importDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::includeDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::insertDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::moduleDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::nativeDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::pragmaDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::typemapDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::typemapitemDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::typemapcopyDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::typesDirective(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::cDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::externDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::enumDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::enumvalueDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::enumforwardDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::classDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::templateDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::lambdaDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::classforwardDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::constructorDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::destructorDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::accessDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::usingDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+int Dispatcher::namespaceDeclaration(Node *n) {
+ return defaultHandler(n);
+}
+
+/* Allocators */
+Language::Language():
+none_comparison(NewString("$arg != 0")),
+director_ctor_code(NewString("")),
+director_prot_ctor_code(0),
+symtabs(NewHash()),
+overloading(0),
+multiinput(0),
+cplus_runtime(0),
+directors(0) {
+ symbolAddScope(""); // create top level/global symbol table scope
+ argc_template_string = NewString("argc");
+ argv_template_string = NewString("argv[%d]");
+
+ /* Default director constructor code, passed to Swig_ConstructorToFunction */
+ Printv(director_ctor_code, "if ( $comparison ) { /* subclassed */\n", " $director_new \n", "} else {\n", " $nondirector_new \n", "}\n", NIL);
+
+ /*
+ Default director 'protected' constructor code, disabled by
+ default. Each language that needs it, has to define it.
+ */
+ director_prot_ctor_code = 0;
+ director_multiple_inheritance = 1;
+ director_language = 0;
+ assert(!this_);
+ this_ = this;
+
+ doxygenTranslator = NULL;
+}
+
+Language::~Language() {
+ Delete(symtabs);
+ Delete(director_ctor_code);
+ Delete(none_comparison);
+ this_ = 0;
+}
+
+ /* -----------------------------------------------------------------------------
+ * directorClassName()
+ * ----------------------------------------------------------------------------- */
+
+ String *Language::directorClassName(Node *n) {
+ String *dirclassname;
+ String *nspace = NewString(Getattr(n, "sym:nspace"));
+ const char *attrib = "director:classname";
+ String *classname = getClassPrefix();
+
+ Replace(nspace, NSPACE_SEPARATOR, "_", DOH_REPLACE_ANY);
+ if (Len(nspace) > 0)
+ dirclassname = NewStringf("SwigDirector_%s_%s", nspace, classname);
+ else
+ dirclassname = NewStringf("SwigDirector_%s", classname);
+ Setattr(n, attrib, dirclassname);
+
+ Delete(nspace);
+ return dirclassname;
+ }
+
+/* ----------------------------------------------------------------------
+ emit_one()
+ ---------------------------------------------------------------------- */
+
+int Language::emit_one(Node *n) {
+ int ret;
+ int oldext;
+ if (!n)
+ return SWIG_OK;
+
+ if (GetFlag(n, "feature:ignore")
+ && !Getattr(n, "feature:onlychildren"))
+ return SWIG_OK;
+
+ oldext = Extend;
+ if (Getattr(n, "feature:extend"))
+ Extend = 1;
+
+ line_number = Getline(n);
+ input_file = Getfile(n);
+
+ /*
+ symtab = Getattr(n,"symtab");
+ if (symtab) {
+ symtab = Swig_symbol_setscope(symtab);
+ }
+ */
+ ret = Dispatcher::emit_one(n);
+ /*
+ if (symtab) {
+ Swig_symbol_setscope(symtab);
+ }
+ */
+ Extend = oldext;
+ return ret;
+}
+
+
+static Parm *nonvoid_parms(Parm *p) {
+ if (p) {
+ SwigType *t = Getattr(p, "type");
+ if (SwigType_type(t) == T_VOID)
+ return 0;
+ }
+ return p;
+}
+
+/* -----------------------------------------------------------------------------
+ * cplus_value_type()
+ *
+ * Returns the alternative value type needed in C++ for class value
+ * types. When swig is not sure about using a plain $ltype value,
+ * since the class doesn't have a default constructor, or it can't be
+ * assigned, you will get back 'SwigValueWrapper<type >'.
+ *
+ * ----------------------------------------------------------------------------- */
+
+SwigType *cplus_value_type(SwigType *t) {
+ return SwigType_alttype(t, 0);
+}
+
+static Node *first_nontemplate(Node *n) {
+ while (n) {
+ if (Strcmp(nodeType(n), "template") != 0)
+ return n;
+ n = Getattr(n, "sym:nextSibling");
+ }
+ return n;
+}
+
+
+
+/* --------------------------------------------------------------------------
+ * swig_pragma()
+ *
+ * Handle swig pragma directives.
+ * -------------------------------------------------------------------------- */
+
+void swig_pragma(char *lang, char *name, char *value) {
+ if (strcmp(lang, "swig") == 0) {
+ if ((strcmp(name, "make_default") == 0) || ((strcmp(name, "makedefault") == 0))) {
+ GenerateDefault = 1;
+ } else if ((strcmp(name, "no_default") == 0) || ((strcmp(name, "nodefault") == 0))) {
+ Swig_warning(WARN_DEPRECATED_NODEFAULT, "SWIG", 1, "dangerous, use %%nodefaultctor, %%nodefaultdtor instead.\n");
+ GenerateDefault = 0;
+ } else if (strcmp(name, "attributefunction") == 0) {
+ String *nvalue = NewString(value);
+ char *s = strchr(Char(nvalue), ':');
+ if (!s) {
+ Swig_error(input_file, line_number, "Bad value for attributefunction. Expected \"fmtget:fmtset\".\n");
+ } else {
+ *s = 0;
+ AttributeFunctionGet = NewString(Char(nvalue));
+ AttributeFunctionSet = NewString(s + 1);
+ }
+ Delete(nvalue);
+ } else if (strcmp(name, "noattributefunction") == 0) {
+ AttributeFunctionGet = 0;
+ AttributeFunctionSet = 0;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------------
+ * Language::use_naturalvar_mode()
+ *
+ * Determine whether to use const ref typemaps instead of pointer typemaps
+ * for variable access.
+ * -------------------------------------------------------------------------- */
+int Language::use_naturalvar_mode(Node *n) const {
+ if (Getattr(n, "unnamed"))
+ return 0;
+
+ // The naturalvar feature can be attached to either the variable name or the variable's type
+ // naturalvar on the variable name is more specific and overrides naturalvar on the variable's type
+ String *naturalvar = Getattr(n, "feature:naturalvar");
+ bool explicitly_off = naturalvar && Strcmp(naturalvar, "0") == 0;
+ int nvar = GetFlag(n, "feature:naturalvar");
+
+ if (!explicitly_off && !nvar) {
+ /* look for feature in the class */
+ SwigType *ty = Getattr(n, "type");
+ SwigType *fullty = SwigType_typedef_resolve_all(ty);
+ if (SwigType_isclass(fullty)) {
+ SwigType *tys = SwigType_strip_qualifiers(fullty);
+ if (!CPlusPlus) {
+ Replaceall(tys, "struct ", "");
+ Replaceall(tys, "union ", "");
+ Replaceall(tys, "class ", "");
+ }
+ Node *typenode = Swig_symbol_clookup(tys, 0);
+ if (typenode) {
+ naturalvar = Getattr(typenode, "feature:naturalvar");
+ explicitly_off = naturalvar && Strcmp(naturalvar, "0") == 0;
+ nvar = nvar || GetFlag(typenode, "feature:naturalvar");
+ }
+ Delete(tys);
+ }
+ Delete(fullty);
+ }
+ nvar = nvar || naturalvar_mode;
+ return explicitly_off ? 0 : nvar ? CWRAP_NATURAL_VAR : 0;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::top() - Top of parsing tree
+ * ---------------------------------------------------------------------- */
+
+int Language::top(Node *n) {
+ Node *mod = Getattr(n, "module");
+ if (mod) {
+ Node *options = Getattr(mod, "options");
+ if (options) {
+ if (Getattr(options, "naturalvar")) {
+ naturalvar_mode = 1;
+ }
+ }
+ }
+ classhash = Getattr(n, "classes");
+ return emit_children(n);
+}
+
+/* ----------------------------------------------------------------------
+ * Language::extendDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::extendDirective(Node *n) {
+ save_value<int> oldam(Extend, CWRAP_EXTEND);
+ save_value<AccessMode> oldmode(cplus_mode, PUBLIC);
+ emit_children(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::applyDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::applyDirective(Node *n) {
+
+ Parm *pattern = Getattr(n, "pattern");
+ Node *c = firstChild(n);
+ while (c) {
+ Parm *apattern = Getattr(c, "pattern");
+ if (ParmList_len(pattern) != ParmList_len(apattern)) {
+ Swig_error(input_file, line_number, "Can't apply (%s) to (%s). Number of arguments don't match.\n", ParmList_str(pattern), ParmList_str(apattern));
+ } else {
+ if (!Swig_typemap_apply(pattern, apattern)) {
+ Swig_warning(WARN_TYPEMAP_APPLY_UNDEF, input_file, line_number, "Can't apply (%s). No typemaps are defined.\n", ParmList_str(pattern));
+ }
+ }
+ c = nextSibling(c);
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::clearDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::clearDirective(Node *n) {
+ Node *p;
+ for (p = firstChild(n); p; p = nextSibling(p)) {
+ ParmList *pattern = Getattr(p, "pattern");
+ Swig_typemap_clear_apply(pattern);
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::constantDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::constantDirective(Node *n) {
+
+ if (CurrentClass && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ if (!GetFlag(n, "feature:allowexcept")) {
+ UnsetFlag(n, "feature:except");
+ }
+ if (Getattr(n, "feature:exceptvar")) {
+ Setattr(n, "feature:except", Getattr(n, "feature:exceptvar"));
+ }
+
+ if (!ImportMode) {
+ Swig_require("constantDirective", n, "name", "?value", NIL);
+ String *name = Getattr(n, "name");
+ String *value = Getattr(n, "value");
+ if (!value) {
+ value = Copy(name);
+ } else {
+ /* if (checkAttribute(n,"type","char")) {
+ value = NewString(value);
+ } else {
+ value = NewStringf("%(escape)s", value);
+ }
+ */
+ Setattr(n, "rawvalue", value);
+ value = NewStringf("%(escape)s", value);
+ if (!Len(value))
+ Append(value, "\\0");
+ /* Printf(stdout,"'%s' = '%s'\n", name, value); */
+ }
+ Setattr(n, "value", value);
+ this->constantWrapper(n);
+ Swig_restore(n);
+ return SWIG_OK;
+ }
+ return SWIG_NOWRAP;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::fragmentDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::fragmentDirective(Node *n) {
+ if (!(Getattr(n, "emitonly") && ImportMode))
+ Swig_fragment_register(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::importDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::importDirective(Node *n) {
+ int oldim = ImportMode;
+ ImportMode = IMPORT_MODE;
+ emit_children(n);
+ ImportMode = oldim;
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::includeDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::includeDirective(Node *n) {
+ emit_children(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::insertDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::insertDirective(Node *n) {
+ /* %insert directive */
+ if ((!ImportMode) || Getattr(n, "generated")) {
+ String *code = Getattr(n, "code");
+ String *section = Getattr(n, "section");
+ File *f = 0;
+ if (!section) { /* %{ ... %} */
+ f = Swig_filebyname("header");
+ } else {
+ f = Swig_filebyname(section);
+ }
+ if (f) {
+ Printf(f, "%s\n", code);
+ } else {
+ Swig_error(input_file, line_number, "Unknown target '%s' for %%insert directive.\n", section);
+ }
+ return SWIG_OK;
+ } else {
+ return SWIG_NOWRAP;
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * Language::moduleDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::moduleDirective(Node *n) {
+ (void) n;
+ /* %module directive */
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::nativeDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::nativeDirective(Node *n) {
+ if (!ImportMode) {
+ return nativeWrapper(n);
+ } else {
+ return SWIG_NOWRAP;
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * Language::pragmaDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::pragmaDirective(Node *n) {
+ /* %pragma directive */
+ if (!ImportMode) {
+ String *lan = Getattr(n, "lang");
+ String *name = Getattr(n, "name");
+ String *value = Getattr(n, "value");
+ swig_pragma(Char(lan), Char(name), Char(value));
+ /* pragma(Char(lan),Char(name),Char(value)); */
+ return SWIG_OK;
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::typemapDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::typemapDirective(Node *n) {
+ /* %typemap directive */
+ String *method = Getattr(n, "method");
+ String *code = Getattr(n, "code");
+ Parm *kwargs = Getattr(n, "kwargs");
+ Node *items = firstChild(n);
+ static int nameerror = 0;
+
+
+ if (code && (Strstr(code, "$source") || (Strstr(code, "$target")))) {
+ Swig_error(Getfile(n), Getline(n), "Obsolete typemap feature ($source/$target).\n");
+ if (!nameerror) {
+ Swig_error(Getfile(n), Getline(n), "The use of $source and $target in a typemap declaration is no longer supported.\n\
+For typemaps related to argument input (in,ignore,default,arginit,check), replace\n\
+$source by $input and $target by $1. For typemaps related to return values (out,\n\
+argout,ret,except), replace $source by $1 and $target by $result. See the file\n\
+Doc/Manual/Typemaps.html for complete details.\n");
+ nameerror = 1;
+ }
+ }
+
+ if (Strcmp(method, "except") == 0) {
+ Swig_warning(WARN_DEPRECATED_EXCEPT_TM, Getfile(n), Getline(n), "%%typemap(except) is deprecated. Use the %%exception directive.\n");
+ }
+
+ if (Strcmp(method, "in") == 0) {
+ Hash *k;
+ k = kwargs;
+ while (k) {
+ if (checkAttribute(k, "name", "numinputs")) {
+ if (!multiinput && (GetInt(k, "value") > 1)) {
+ Swig_error(Getfile(n), Getline(n), "Multiple-input typemaps (numinputs > 1) not supported by this target language module.\n");
+ return SWIG_ERROR;
+ }
+ break;
+ }
+ k = nextSibling(k);
+ }
+ if (!k) {
+ k = NewHash();
+ Setattr(k, "name", "numinputs");
+ Setattr(k, "value", "1");
+ set_nextSibling(k, kwargs);
+ Setattr(n, "kwargs", k);
+ kwargs = k;
+ }
+ }
+
+ if (Strcmp(method, "ignore") == 0) {
+ Swig_warning(WARN_DEPRECATED_IGNORE_TM, Getfile(n), Getline(n), "%%typemap(ignore) has been replaced by %%typemap(in,numinputs=0).\n");
+
+ Clear(method);
+ Append(method, "in");
+ Hash *k = NewHash();
+ Setattr(k, "name", "numinputs");
+ Setattr(k, "value", "0");
+ set_nextSibling(k, kwargs);
+ Setattr(n, "kwargs", k);
+ kwargs = k;
+ }
+
+ /* Replace $descriptor() macros */
+
+ if (code) {
+ Setfile(code, Getfile(n));
+ Setline(code, Getline(n));
+ Swig_cparse_replace_descriptor(code);
+ }
+
+ while (items) {
+ Parm *pattern = Getattr(items, "pattern");
+ Parm *parms = Getattr(items, "parms");
+
+ if (code) {
+ Swig_typemap_register(method, pattern, code, parms, kwargs);
+ } else {
+ Swig_typemap_clear(method, pattern);
+ }
+ items = nextSibling(items);
+ }
+ return SWIG_OK;
+
+}
+
+/* ----------------------------------------------------------------------
+ * Language::typemapcopyDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::typemapcopyDirective(Node *n) {
+ String *method = Getattr(n, "method");
+ Parm *pattern = Getattr(n, "pattern");
+ Node *items = firstChild(n);
+ int nsrc = 0;
+ nsrc = ParmList_len(pattern);
+ while (items) {
+ ParmList *npattern = Getattr(items, "pattern");
+ if (nsrc != ParmList_len(npattern)) {
+ Swig_error(input_file, line_number, "Can't copy typemap. Number of types differ.\n");
+ } else {
+ if (Swig_typemap_copy(method, pattern, npattern) < 0) {
+ Swig_error(input_file, line_number, "Can't copy typemap (%s) %s = %s\n", method, ParmList_str(pattern), ParmList_str(npattern));
+ }
+ }
+ items = nextSibling(items);
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::typesDirective()
+ * ---------------------------------------------------------------------- */
+
+int Language::typesDirective(Node *n) {
+ Parm *parms = Getattr(n, "parms");
+ String *convcode = Getattr(n, "convcode"); /* optional user supplied conversion code for custom casting */
+ while (parms) {
+ SwigType *t = Getattr(parms, "type");
+ String *v = Getattr(parms, "value");
+ if (!v) {
+ SwigType_remember(t);
+ } else {
+ if (SwigType_issimple(t)) {
+ SwigType_inherit(t, v, 0, convcode);
+ }
+ }
+ parms = nextSibling(parms);
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::cDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::cDeclaration(Node *n) {
+
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ SwigType *decl = Getattr(n, "decl");
+ String *storage = Getattr(n, "storage");
+ Node *over;
+ File *f_header = 0;
+ SwigType *ty, *fullty;
+
+ if (Getattr(n, "feature:onlychildren")) {
+ if (GetFlag(n, "feature:ignore")) {
+ return SWIG_NOWRAP;
+ } else {
+ // Found an unignored templated method that has an empty template instantiation (%template())
+ // Ignore it unless it has been %rename'd
+ if (Strncmp(symname, "__dummy_", 8) == 0 && Cmp(storage, "typedef") != 0) {
+ SetFlag(n, "feature:ignore");
+ Swig_warning(WARN_LANG_TEMPLATE_METHOD_IGNORE, input_file, line_number,
+ "%%template() contains no name. Template method ignored: %s\n", Swig_name_decl(n));
+ return SWIG_NOWRAP;
+ }
+ }
+ }
+
+ /* discards nodes following the access control rules */
+ if (cplus_mode != PUBLIC || !is_public(n)) {
+ /* except for friends, they are not affected by access control */
+ int isfriend = Cmp(storage, "friend") == 0;
+ if (!isfriend) {
+ /* Check what the director needs. If the method is pure virtual, it is always needed.
+ * Also wrap non-virtual protected members if asked for (allprotected mode). */
+ if (!(directorsEnabled() && ((is_member_director(CurrentClass, n) && need_nonpublic_member(n)) || isNonVirtualProtectedAccess(n)))) {
+ return SWIG_NOWRAP;
+ }
+ // Prevent wrapping protected overloaded director methods more than once -
+ // This bit of code is only needed due to the cDeclaration call in classHandler()
+ String *wrapname = NewStringf("nonpublic_%s%s", symname, Getattr(n, "sym:overname"));
+ if (Getattr(CurrentClass, wrapname)) {
+ Delete(wrapname);
+ return SWIG_NOWRAP;
+ }
+ SetFlag(CurrentClass, wrapname);
+ Delete(wrapname);
+ }
+ }
+
+ if (Cmp(storage, "typedef") == 0) {
+ Swig_save("cDeclaration", n, "type", NIL);
+ SwigType *t = Copy(type);
+ if (t) {
+ SwigType_push(t, decl);
+ Setattr(n, "type", t);
+ typedefHandler(n);
+ }
+ Swig_restore(n);
+ return SWIG_OK;
+ }
+
+ /* If in import mode, we proceed no further */
+ if (ImportMode)
+ return SWIG_NOWRAP;
+
+ /* If we're in extend mode and there is code, replace the $descriptor macros */
+ if (Extend) {
+ String *code = Getattr(n, "code");
+ if (code) {
+ Setfile(code, Getfile(n));
+ Setline(code, Getline(n));
+ Swig_cparse_replace_descriptor(code);
+ }
+ }
+
+ /* Overloaded symbol check */
+ over = Swig_symbol_isoverloaded(n);
+ if (!overloading) {
+ if (over)
+ over = first_nontemplate(over);
+ if (over && (over != n)) {
+ Swig_warning(WARN_LANG_OVERLOAD_DECL, input_file, line_number, "Overloaded declaration ignored. %s\n", Swig_name_decl(n));
+ Swig_warning(WARN_LANG_OVERLOAD_DECL, Getfile(over), Getline(over), "Previous declaration is %s\n", Swig_name_decl(over));
+ return SWIG_NOWRAP;
+ }
+ }
+
+ if (!validIdentifier(symname)) {
+ Swig_warning(WARN_LANG_IDENTIFIER, input_file, line_number, "Can't wrap '%s' unless renamed to a valid identifier.\n", SwigType_namestr(symname));
+ return SWIG_NOWRAP;
+ }
+
+ ty = NewString(type);
+ SwigType_push(ty, decl);
+ fullty = SwigType_typedef_resolve_all(ty);
+ if (SwigType_isfunction(fullty)) {
+ if (!SwigType_isfunction(ty)) {
+ Delete(ty);
+ ty = fullty;
+ fullty = 0;
+ ParmList *parms = SwigType_function_parms(ty, n);
+ Setattr(n, "parms", parms);
+ }
+ /* Transform the node into a 'function' node and emit */
+ if (!CurrentClass) {
+ f_header = Swig_filebyname("header");
+
+ if (AddExtern) {
+ if (f_header) {
+ if (Swig_storage_isextern(n) || (ForceExtern && !storage)) {
+ /* we don't need the 'extern' part in the C/C++ declaration,
+ and it produces some problems when namespace and SUN
+ Studio is used.
+
+ Printf(f_header,"extern %s", SwigType_str(ty,name));
+
+ In fact generating extern declarations is quite error prone and is
+ no longer the default. Getting it right seems impossible with namespaces
+ and default arguments and when a method is declared with the various Windows
+ calling conventions - SWIG doesn't understand Windows (non standard) calling
+ conventions in the first place, so can't regenerate them.
+ */
+ String *str = SwigType_str(ty, name);
+ Printf(f_header, "%s", str);
+ Delete(str);
+ {
+ DOH *t = Getattr(n, "throws");
+ if (t) {
+ Printf(f_header, " throw(");
+ while (t) {
+ Printf(f_header, "%s", Getattr(t, "type"));
+ t = nextSibling(t);
+ if (t)
+ Printf(f_header, ",");
+ }
+ Printf(f_header, ")");
+ }
+ }
+ Printf(f_header, ";\n");
+ } else if (Swig_storage_isexternc(n)) {
+ /* here 'extern "C"' is needed */
+ String *str = SwigType_str(ty, name);
+ Printf(f_header, "extern \"C\" %s;\n", str);
+ Delete(str);
+ }
+ }
+ }
+ }
+ /* This needs to check qualifiers */
+ if (SwigType_isqualifier(ty)) {
+ SwigType *qual = SwigType_pop(ty);
+ Setattr(n, "qualifier", qual);
+ Delete(qual);
+ }
+ Delete(SwigType_pop_function(ty));
+ DohIncref(type);
+ Setattr(n, "type", ty);
+
+ functionHandler(n);
+
+ Setattr(n, "type", type);
+ Delete(ty);
+ Delete(type);
+ return SWIG_OK;
+ } else {
+ /* Some kind of variable declaration */
+ String *declaration = Copy(decl);
+ Delattr(n, "decl");
+ if (!CurrentClass) {
+ if (Swig_storage_isextern(n) || ForceExtern) {
+ if (AddExtern) {
+ f_header = Swig_filebyname("header");
+ if (f_header) {
+ String *str = SwigType_str(ty, name);
+ Printf(f_header, "%s %s;\n", Getattr(n, "storage"), str);
+ Delete(str);
+ }
+ }
+ }
+ }
+ if (!SwigType_ismutable(ty)) {
+ SetFlag(n, "feature:immutable");
+ }
+ /* If an array and elements are const, then read-only */
+ if (SwigType_isarray(ty)) {
+ SwigType *tya = SwigType_array_type(ty);
+ if (SwigType_isconst(tya)) {
+ SetFlag(n, "feature:immutable");
+ }
+ Delete(tya);
+ }
+ DohIncref(type);
+ Setattr(n, "type", ty);
+ variableHandler(n);
+ Setattr(n, "type", type);
+ Setattr(n, "decl", declaration);
+ Delete(ty);
+ Delete(type);
+ Delete(fullty);
+ return SWIG_OK;
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * Language::functionHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::functionHandler(Node *n) {
+ String *storage = Getattr(n, "storage");
+ int isfriend = CurrentClass && Cmp(storage, "friend") == 0;
+ int isstatic = CurrentClass && Swig_storage_isstatic(n) && !(SmartPointer && Getattr(n, "allocate:smartpointeraccess"));
+ Parm *p = Getattr(n, "parms");
+ if (GetFlag(n, "feature:del")) {
+ /* the method acts like a delete operator, ie, we need to disown the parameter */
+ if (CurrentClass && !isstatic && !isfriend) {
+ SetFlag(n, "feature:self:disown");
+ } else {
+ if (p)
+ SetFlag(p, "wrap:disown");
+ }
+ }
+ if (!CurrentClass) {
+ globalfunctionHandler(n);
+ } else {
+ if (isstatic) {
+ staticmemberfunctionHandler(n);
+ } else if (isfriend) {
+ int oldInClass = InClass;
+ InClass = 0;
+ globalfunctionHandler(n);
+ InClass = oldInClass;
+ } else {
+ // This is a member function, set a flag so the documentation type is correct
+ SetFlag(n, "memberfunction");
+ Node *explicit_n = 0;
+ if (directorsEnabled() && is_member_director(CurrentClass, n) && !extraDirectorProtectedCPPMethodsRequired()) {
+ bool virtual_but_not_pure_virtual = (!(Cmp(storage, "virtual")) && (Cmp(Getattr(n, "value"), "0") != 0));
+ if (virtual_but_not_pure_virtual) {
+ // Add additional wrapper which makes an explicit call to the virtual method (ie not a virtual call)
+ explicit_n = Copy(n);
+ String *new_symname = Copy(Getattr(n, "sym:name"));
+ String *suffix = Getattr(parentNode(n), "sym:name");
+ Printv(new_symname, "SwigExplicit", suffix, NIL);
+ Setattr(explicit_n, "sym:name", new_symname);
+ Delattr(explicit_n, "storage");
+ Delattr(explicit_n, "override");
+ Delattr(explicit_n, "hides");
+ SetFlag(explicit_n, "explicitcall");
+ Setattr(n, "explicitcallnode", explicit_n);
+ }
+ }
+
+ memberfunctionHandler(n);
+
+ if (explicit_n) {
+ memberfunctionHandler(explicit_n);
+ Delattr(explicit_n, "explicitcall");
+ Delete(explicit_n);
+ }
+ }
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::globalfunctionHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::globalfunctionHandler(Node *n) {
+
+ Swig_require("globalfunctionHandler", n, "name", "sym:name", "type", "?parms", NIL);
+
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ ParmList *parms = Getattr(n, "parms");
+
+ /* Check for callback mode */
+ String *cb = GetFlagAttr(n, "feature:callback");
+ if (cb) {
+ String *cbname = Getattr(n, "feature:callback:name");
+ if (!cbname) {
+ cbname = NewStringf(cb, symname);
+ Setattr(n, "feature:callback:name", cbname);
+ }
+
+ callbackfunctionHandler(n);
+ if (Cmp(cbname, symname) == 0) {
+ Delete(cbname);
+ Swig_restore(n);
+ return SWIG_NOWRAP;
+ }
+ Delete(cbname);
+ }
+ Setattr(n, "parms", nonvoid_parms(parms));
+
+ String *extendname = Getattr(n, "extendname");
+ String *call = Swig_cfunction_call(extendname ? extendname : name, parms);
+ String *cres = Swig_cresult(type, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(cres);
+ Delete(call);
+ functionWrapper(n);
+
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::callbackfunctionHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::callbackfunctionHandler(Node *n) {
+ Swig_require("callbackfunctionHandler", n, "name", "*sym:name", "*type", "?value", NIL);
+ String *type = Getattr(n, "type");
+ String *name = Getattr(n, "name");
+ String *parms = Getattr(n, "parms");
+ String *cbname = Getattr(n, "feature:callback:name");
+ String *calltype = NewStringf("(%s (*)(%s))(%s)", SwigType_str(type, 0), ParmList_str(parms), SwigType_namestr(name));
+ SwigType *cbty = Copy(type);
+ SwigType_add_function(cbty, parms);
+ SwigType_add_pointer(cbty);
+
+ Setattr(n, "sym:name", cbname);
+ Setattr(n, "type", cbty);
+ Setattr(n, "value", calltype);
+
+ Node *ns = symbolLookup(cbname);
+ if (!ns)
+ constantWrapper(n);
+
+ Delete(cbty);
+
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::memberfunctionHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::memberfunctionHandler(Node *n) {
+
+ Swig_require("memberfunctionHandler", n, "*name", "*sym:name", "*type", "?parms", "?value", NIL);
+
+ String *storage = Getattr(n, "storage");
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *value = Getattr(n, "value");
+ ParmList *parms = Getattr(n, "parms");
+ String *cb = GetFlagAttr(n, "feature:callback");
+
+ if (Cmp(storage, "virtual") == 0) {
+ if (Cmp(value, "0") == 0) {
+ IsVirtual = PURE_VIRTUAL;
+ } else {
+ IsVirtual = PLAIN_VIRTUAL;
+ }
+ } else {
+ IsVirtual = 0;
+ }
+ if (cb) {
+ Node *cbn = NewHash();
+ String *cbname = Getattr(n, "feature:callback:name");
+ if (!cbname) {
+ cbname = NewStringf(cb, symname);
+ }
+
+ SwigType *cbty = Copy(type);
+ SwigType_add_function(cbty, parms);
+ SwigType_add_memberpointer(cbty, ClassName);
+ String *cbvalue = NewStringf("&%s::%s", ClassName, name);
+ Setattr(cbn, "sym:name", cbname);
+ Setattr(cbn, "type", cbty);
+ Setattr(cbn, "value", cbvalue);
+ Setattr(cbn, "name", name);
+ Setfile(cbn, Getfile(n));
+ Setline(cbn, Getline(n));
+
+ memberconstantHandler(cbn);
+ Setattr(n, "feature:callback:name", Swig_name_member(NSpace, ClassPrefix, cbname));
+
+ Delete(cb);
+ Delete(cbn);
+ Delete(cbvalue);
+ Delete(cbty);
+ Delete(cbname);
+ if (Cmp(cbname, symname) == 0) {
+ Swig_restore(n);
+ return SWIG_NOWRAP;
+ }
+ }
+
+ String *fname = Swig_name_member(NSpace, ClassPrefix, symname);
+ if (Extend && SmartPointer) {
+ if (!Getattr(n, "extendsmartclassname")) {
+ Setattr(n, "extendsmartclassname", Getattr(CurrentClass, "allocate:smartpointerpointeeclassname"));
+ }
+ }
+ // Set up the type for the cast to this class for use when wrapping const director (virtual) methods.
+ // Note: protected director methods or when allprotected mode turned on.
+ String *director_type = 0;
+ if (!is_public(n) && (is_member_director(CurrentClass, n) || GetFlag(n, "explicitcall") || isNonVirtualProtectedAccess(n))) {
+ director_type = Copy(DirectorClassName);
+ String *qualifier = Getattr(n, "qualifier");
+ if (qualifier)
+ SwigType_push(director_type, qualifier);
+ SwigType_add_pointer(director_type);
+ }
+
+ int DirectorExtraCall = 0;
+ if (directorsEnabled() && is_member_director(CurrentClass, n) && !SmartPointer)
+ if (extraDirectorProtectedCPPMethodsRequired())
+ DirectorExtraCall = CWRAP_DIRECTOR_TWO_CALLS;
+
+ if (GetFlag(n, "explicitcall"))
+ DirectorExtraCall = CWRAP_DIRECTOR_ONE_CALL;
+
+ int extendmember = GetFlag(n, "isextendmember") ? Extend : 0;
+ int flags = Getattr(n, "template") ? extendmember | SmartPointer : Extend | SmartPointer | DirectorExtraCall;
+ Swig_MethodToFunction(n, NSpace, ClassType, flags, director_type, is_member_director(CurrentClass, n));
+ Setattr(n, "sym:name", fname);
+ /* Explicitly save low-level and high-level documentation names */
+ Setattr(n, "doc:low:name", fname);
+ Setattr(n, "doc:high:name", symname);
+
+ functionWrapper(n);
+
+ Delete(director_type);
+ Delete(fname);
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::staticmemberfunctionHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::staticmemberfunctionHandler(Node *n) {
+
+ Swig_require("staticmemberfunctionHandler", n, "*name", "*sym:name", "*type", NIL);
+ Swig_save("staticmemberfunctionHandler", n, "storage", NIL);
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ ParmList *parms = Getattr(n, "parms");
+ String *cb = GetFlagAttr(n, "feature:callback");
+ String *cname, *mrename;
+
+ if (!Extend) {
+ Node *sb = Getattr(n, "cplus:staticbase");
+ String *sname = Getattr(sb, "name");
+ if (isNonVirtualProtectedAccess(n))
+ cname = NewStringf("%s::%s", DirectorClassName, name);
+ else
+ cname = NewStringf("%s::%s", sname, name);
+ } else {
+ String *mname = Swig_name_mangle(ClassName);
+ cname = Swig_name_member(NSpace, mname, name);
+ Delete(mname);
+ }
+ mrename = Swig_name_member(NSpace, ClassPrefix, symname);
+
+ if (Extend) {
+ String *code = Getattr(n, "code");
+ String *defaultargs = Getattr(n, "defaultargs");
+ String *mangled = Swig_name_mangle(mrename);
+ Delete(mrename);
+ mrename = mangled;
+
+ if (code) {
+ // See Swig_MethodToFunction() for the explanation of this code.
+ if (Getattr(n, "sym:overloaded")) {
+ Append(cname, Getattr(defaultargs ? defaultargs : n, "sym:overname"));
+ } else if (UseWrapperSuffix) {
+ Append(cname, "__SWIG");
+ }
+
+ if (!defaultargs) {
+ /* Hmmm. An added static member. We have to create a little wrapper for this */
+ String *mangled_cname = Swig_name_mangle(cname);
+ Swig_add_extension_code(n, mangled_cname, parms, type, code, CPlusPlus, 0);
+ Setattr(n, "extendname", mangled_cname);
+ Delete(mangled_cname);
+ }
+ }
+ }
+
+ Setattr(n, "name", cname);
+ Setattr(n, "sym:name", mrename);
+ /* Explicitly save low-level and high-level documentation names */
+ Setattr(n, "doc:low:name", mrename);
+ Setattr(n, "doc:high:name", symname);
+
+ if (cb) {
+ String *cbname = NewStringf(cb, symname);
+ Setattr(n, "feature:callback:name", Swig_name_member(NSpace, ClassPrefix, cbname));
+ Setattr(n, "feature:callback:staticname", name);
+ }
+ Delattr(n, "storage");
+
+ globalfunctionHandler(n);
+
+ Delete(cname);
+ Delete(mrename);
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::variableHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::variableHandler(Node *n) {
+
+ /* If not a smart-pointer access or added method. We clear
+ feature:except. There is no way C++ or C would throw
+ an exception merely for accessing a member data.
+
+ Caveat: Some compilers seem to route attribute access through
+ methods which can generate exceptions. The feature:allowexcept
+ allows this. Also, the feature:exceptvar can be used to match
+ only variables.
+ */
+ if (!(Extend | SmartPointer)) {
+ if (!GetFlag(n, "feature:allowexcept")) {
+ UnsetFlag(n, "feature:except");
+ }
+ if (Getattr(n, "feature:exceptvar")) {
+ Setattr(n, "feature:except", Getattr(n, "feature:exceptvar"));
+ }
+ }
+
+ if (!CurrentClass) {
+ globalvariableHandler(n);
+ } else {
+ Swig_save("variableHandler", n, "feature:immutable", NIL);
+ if (SmartPointer) {
+ /* If a smart-pointer and it's a constant access, we have to set immutable */
+ if (!Getattr(CurrentClass, "allocate:smartpointermutable")) {
+ SetFlag(n, "feature:immutable");
+ }
+ }
+ if (Swig_storage_isstatic(n) && !(SmartPointer && Getattr(n, "allocate:smartpointeraccess"))) {
+ staticmembervariableHandler(n);
+ } else {
+ membervariableHandler(n);
+ }
+ Swig_restore(n);
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::globalvariableHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::globalvariableHandler(Node *n) {
+ variableWrapper(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::membervariableHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::membervariableHandler(Node *n) {
+
+ Swig_require("membervariableHandler", n, "*name", "*sym:name", "*type", NIL);
+ Swig_save("membervariableHandler", n, "parms", NIL);
+
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+
+ if (!AttributeFunctionGet) {
+ String *mname = Swig_name_member(0, ClassPrefix, symname);
+ String *mrename_get = Swig_name_get(NSpace, mname);
+ String *mrename_set = Swig_name_set(NSpace, mname);
+ Delete(mname);
+
+ /* Create a function to set the value of the variable */
+
+ int assignable = is_assignable(n);
+
+ if (SmartPointer) {
+ if (!Getattr(CurrentClass, "allocate:smartpointermutable")) {
+ assignable = 0;
+ }
+ }
+
+ if (assignable) {
+ int make_set_wrapper = 1;
+ String *tm = 0;
+ String *target = 0;
+ if (!Extend) {
+ if (SmartPointer) {
+ if (Swig_storage_isstatic(n)) {
+ Node *sn = Getattr(n, "cplus:staticbase");
+ String *base = Getattr(sn, "name");
+ target = NewStringf("%s::%s", base, name);
+ } else {
+ String *pname = Swig_cparm_name(0, 0);
+ target = NewStringf("(*%s)->%s", pname, name);
+ Delete(pname);
+ }
+ } else {
+ String *pname = isNonVirtualProtectedAccess(n) ? NewString("darg") : Swig_cparm_name(0, 0);
+ target = NewStringf("%s->%s", pname, name);
+ Delete(pname);
+ }
+
+ // This is an input type typemap lookup and so it should not use Node n
+ // otherwise qualification is done on the parameter name for the setter function
+ Parm *nin = NewParm(type, name, n);
+ tm = Swig_typemap_lookup("memberin", nin, target, 0);
+ Delete(nin);
+ }
+
+ int flags = Extend | SmartPointer | use_naturalvar_mode(n);
+ if (isNonVirtualProtectedAccess(n))
+ flags = flags | CWRAP_ALL_PROTECTED_ACCESS;
+
+ Swig_MembersetToFunction(n, ClassType, flags);
+ Setattr(n, "memberset", "1");
+ if (!Extend) {
+ /* Check for a member in typemap here */
+
+ if (!tm) {
+ if (SwigType_isarray(type)) {
+ Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s.\n", SwigType_str(type, 0));
+ make_set_wrapper = 0;
+ }
+ } else {
+ String *pname0 = Swig_cparm_name(0, 0);
+ String *pname1 = Swig_cparm_name(0, 1);
+ Replace(tm, "$input", pname1, DOH_REPLACE_ANY);
+ Replace(tm, "$self", pname0, DOH_REPLACE_ANY);
+ Setattr(n, "wrap:action", tm);
+ Delete(tm);
+ Delete(pname0);
+ Delete(pname1);
+ }
+ Delete(target);
+ }
+ if (make_set_wrapper) {
+ Setattr(n, "sym:name", mrename_set);
+ functionWrapper(n);
+ } else {
+ SetFlag(n, "feature:immutable");
+ }
+ /* Restore parameters */
+ Setattr(n, "type", type);
+ Setattr(n, "name", name);
+ Setattr(n, "sym:name", symname);
+ Delattr(n, "memberset");
+
+ /* Delete all attached typemaps and typemap attributes */
+ Iterator ki;
+ for (ki = First(n); ki.key; ki = Next(ki)) {
+ if (Strncmp(ki.key, "tmap:", 5) == 0)
+ Delattr(n, ki.key);
+ }
+ }
+ /* Emit get function */
+ {
+ int flags = Extend | SmartPointer | use_naturalvar_mode(n);
+ if (isNonVirtualProtectedAccess(n))
+ flags = flags | CWRAP_ALL_PROTECTED_ACCESS;
+ Swig_MembergetToFunction(n, ClassType, flags);
+ Setattr(n, "sym:name", mrename_get);
+ Setattr(n, "memberget", "1");
+ functionWrapper(n);
+ Delattr(n, "memberget");
+ }
+ Delete(mrename_get);
+ Delete(mrename_set);
+
+ } else {
+
+ /* This code is used to support the attributefunction directive
+ where member variables are converted automagically to
+ accessor functions */
+
+#if 0
+ Parm *p;
+ String *gname;
+ SwigType *vty;
+ p = NewParm(type, 0, n);
+ gname = NewStringf(AttributeFunctionGet, symname);
+ if (!Extend) {
+ ActionFunc = Copy(Swig_cmemberget_call(name, type));
+ cpp_member_func(Char(gname), Char(gname), type, 0);
+ Delete(ActionFunc);
+ } else {
+ String *cname = Swig_name_get(NSpace, name);
+ cpp_member_func(Char(cname), Char(gname), type, 0);
+ Delete(cname);
+ }
+ Delete(gname);
+ if (!GetFlag(n, "feature:immutable")) {
+ gname = NewStringf(AttributeFunctionSet, symname);
+ vty = NewString("void");
+ if (!Extend) {
+ ActionFunc = Copy(Swig_cmemberset_call(name, type));
+ cpp_member_func(Char(gname), Char(gname), vty, p);
+ Delete(ActionFunc);
+ } else {
+ String *cname = Swig_name_set(NSpace, name);
+ cpp_member_func(Char(cname), Char(gname), vty, p);
+ Delete(cname);
+ }
+ Delete(gname);
+ }
+ ActionFunc = 0;
+#endif
+ }
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::staticmembervariableHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::staticmembervariableHandler(Node *n) {
+ Swig_require("staticmembervariableHandler", n, "*name", "*sym:name", "*type", "?value", NIL);
+ String *value = Getattr(n, "value");
+ String *classname = !SmartPointer ? (isNonVirtualProtectedAccess(n) ? DirectorClassName : ClassName) : Getattr(CurrentClass, "allocate:smartpointerpointeeclassname");
+
+ if (!value || !Getattr(n, "hasconsttype")) {
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *cname, *mrename;
+
+ /* Create the variable name */
+ mrename = Swig_name_member(0, ClassPrefix, symname);
+ cname = NewStringf("%s::%s", classname, name);
+
+ Setattr(n, "sym:name", mrename);
+ Setattr(n, "name", cname);
+
+ /* Wrap as an ordinary global variable */
+ variableWrapper(n);
+
+ Delete(mrename);
+ Delete(cname);
+ } else {
+
+ /* This is a C++ static member declaration with an initializer and it's const.
+ Certain C++ compilers optimize this out so that there is no linkage to a
+ memory address. Example:
+
+ class Foo {
+ public:
+ static const int x = 3;
+ };
+
+ Some discussion of this in section 9.4 of the C++ draft standard.
+
+ Also, we have to manage the case:
+
+ class Foo {
+ public:
+ %extend {
+ static const int x = 3;
+ }
+ };
+
+ in which there's no actual Foo::x variable to refer to. In this case,
+ the best we can do is to wrap the given value verbatim.
+ */
+
+
+ String *name = Getattr(n, "name");
+ String *cname = NewStringf("%s::%s", classname, name);
+ if (Extend) {
+ /* the variable is a synthesized one.
+ There's nothing we can do; we just keep the given value */
+ } else {
+ /* we refer to the value as Foo::x */
+ String *value = SwigType_namestr(cname);
+ Setattr(n, "value", value);
+ }
+
+ SwigType *t1 = SwigType_typedef_resolve_all(Getattr(n, "type"));
+ SwigType *t2 = SwigType_strip_qualifiers(t1);
+ Setattr(n, "type", t2);
+ Delete(t1);
+ Delete(t2);
+ SetFlag(n, "wrappedasconstant");
+ memberconstantHandler(n);
+ Delete(cname);
+ }
+
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+
+/* ----------------------------------------------------------------------
+ * Language::externDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::externDeclaration(Node *n) {
+ return emit_children(n);
+}
+
+/* ----------------------------------------------------------------------
+ * Language::enumDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::enumDeclaration(Node *n) {
+ if (CurrentClass && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ String *oldNSpace = NSpace;
+ NSpace = Getattr(n, "sym:nspace");
+
+ String *oldEnumClassPrefix = EnumClassPrefix;
+ if (GetFlag(n, "scopedenum")) {
+ assert(Getattr(n, "sym:name"));
+ assert(Getattr(n, "name"));
+ EnumClassPrefix = ClassPrefix ? NewStringf("%s_", ClassPrefix) : NewString("");
+ Printv(EnumClassPrefix, Getattr(n, "sym:name"), NIL);
+ EnumClassName = Copy(Getattr(n, "name"));
+ }
+ if (!ImportMode) {
+ emit_children(n);
+ }
+
+ if (GetFlag(n, "scopedenum")) {
+ Delete(EnumClassName);
+ EnumClassName = 0;
+ Delete(EnumClassPrefix);
+ EnumClassPrefix = oldEnumClassPrefix;
+ }
+ NSpace = oldNSpace;
+
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::enumvalueDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::enumvalueDeclaration(Node *n) {
+ if (CurrentClass && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ Swig_require("enumvalueDeclaration", n, "*name", "*sym:name", "?value", NIL);
+ String *value = Getattr(n, "value");
+ String *name = Getattr(n, "name");
+ String *tmpValue;
+
+ if (value)
+ tmpValue = NewString(value);
+ else
+ tmpValue = NewString(name);
+ Setattr(n, "value", tmpValue);
+
+ Node *parent = parentNode(n);
+ if (GetFlag(parent, "scopedenum")) {
+ String *symname = Swig_name_member(0, Getattr(parent, "sym:name"), Getattr(n, "sym:name"));
+ Setattr(n, "sym:name", symname);
+ Delete(symname);
+ }
+
+ if (!CurrentClass || !cparse_cplusplus) {
+ Setattr(n, "name", tmpValue); /* for wrapping of enums in a namespace when emit_action is used */
+ constantWrapper(n);
+ } else {
+ memberconstantHandler(n);
+ }
+
+ Delete(tmpValue);
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::enumforwardDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::enumforwardDeclaration(Node *n) {
+ (void) n;
+ if (GetFlag(n, "enumMissing"))
+ enumDeclaration(n); // Generate an empty enum in target language
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::memberconstantHandler()
+ * ----------------------------------------------------------------------------- */
+
+int Language::memberconstantHandler(Node *n) {
+
+ Swig_require("memberconstantHandler", n, "*name", "*sym:name", "value", NIL);
+
+ if (!GetFlag(n, "feature:allowexcept")) {
+ UnsetFlag(n, "feature:except");
+ }
+ if (Getattr(n, "feature:exceptvar")) {
+ Setattr(n, "feature:except", Getattr(n, "feature:exceptvar"));
+ }
+
+ String *enumvalue_symname = Getattr(n, "enumvalueDeclaration:sym:name"); // Only set if a strongly typed enum
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *value = Getattr(n, "value");
+
+ String *mrename = Swig_name_member(0, EnumClassPrefix, enumvalue_symname ? enumvalue_symname : symname);
+ Setattr(n, "sym:name", mrename);
+
+ String *new_name = 0;
+ if (Extend)
+ new_name = Copy(value);
+ else if (EnumClassName)
+ new_name = NewStringf("%s::%s", isNonVirtualProtectedAccess(n) ? DirectorClassName : EnumClassName, name);
+ else
+ new_name = NewStringf("%s::%s", isNonVirtualProtectedAccess(n) ? DirectorClassName : ClassName, name);
+ Setattr(n, "name", new_name);
+
+ constantWrapper(n);
+ Delete(mrename);
+ Delete(new_name);
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::typedefHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::typedefHandler(Node *n) {
+ /* since this is a recurring issue, we are going to remember the
+ typedef pointer, if already it is not a pointer or reference, as
+ in
+
+ typedef void NT;
+ int func(NT *p);
+
+ see director_basic.i for example.
+ */
+ SwigType *name = Getattr(n, "name");
+ SwigType *decl = Getattr(n, "decl");
+ if (!SwigType_ispointer(decl) && !SwigType_isreference(decl)) {
+ SwigType *pname = Copy(name);
+ SwigType_add_pointer(pname);
+ SwigType_remember(pname);
+ Delete(pname);
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorMethod()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorMethod(Node *n, Node *parent, String *super) {
+ (void) n;
+ (void) parent;
+ (void) super;
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorConstructor()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorConstructor(Node *n) {
+ (void) n;
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorDefaultConstructor()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorDefaultConstructor(Node *n) {
+ (void) n;
+ return SWIG_OK;
+}
+
+static String *vtable_method_id(Node *n) {
+ String *nodeType = Getattr(n, "nodeType");
+ int is_destructor = (Cmp(nodeType, "destructor") == 0);
+ if (is_destructor)
+ return 0;
+ String *name = Getattr(n, "name");
+ String *decl = Getattr(n, "decl");
+ String *local_decl = SwigType_typedef_resolve_all(decl);
+ String *tmp = SwigType_pop_function(local_decl);
+ Delete(local_decl);
+ local_decl = tmp;
+ String *method_id = NewStringf("%s|%s", name, local_decl);
+ Delete(local_decl);
+ return method_id;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::unrollOneVirtualMethod()
+ * ---------------------------------------------------------------------- */
+
+void Language::unrollOneVirtualMethod(String *classname, Node *n, Node *parent, List *vm, int &virtual_destructor, int protectedbase) {
+ if (!checkAttribute(n, "storage", "virtual"))
+ return;
+ if (GetFlag(n, "final"))
+ return;
+
+ String *nodeType = Getattr(n, "nodeType");
+
+ /* we need to add methods(cdecl) and destructor (to check for throw decl) */
+ int is_destructor = (Cmp(nodeType, "destructor") == 0);
+ if ((Cmp(nodeType, "cdecl") == 0) || is_destructor) {
+ String *decl = Getattr(n, "decl");
+ /* extra check for function type and proper access */
+ if (SwigType_isfunction(decl) && (((!protectedbase || dirprot_mode()) && is_public(n)) || need_nonpublic_member(n))) {
+ String *name = Getattr(n, "name");
+ String *method_id = is_destructor ? NewStringf("~destructor") : vtable_method_id(n);
+ /* Make sure that the new method overwrites the existing: */
+ int len = Len(vm);
+ const int DO_NOT_REPLACE = -1;
+ int replace = DO_NOT_REPLACE;
+ for (int i = 0; i < len; i++) {
+ Node *item = Getitem(vm, i);
+ String *check_vmid = Getattr(item, "vmid");
+
+ if (Strcmp(method_id, check_vmid) == 0) {
+ replace = i;
+ break;
+ }
+ }
+ /* filling a new method item */
+ String *fqdname = NewStringf("%s::%s", classname, name);
+ Hash *item = NewHash();
+ Setattr(item, "fqdname", fqdname);
+ Node *m = Copy(n);
+
+ /* Store the complete return type - needed for non-simple return types (pointers, references etc.) */
+ SwigType *ty = NewString(Getattr(m, "type"));
+ SwigType_push(ty, decl);
+ if (SwigType_isqualifier(ty)) {
+ Delete(SwigType_pop(ty));
+ }
+ Delete(SwigType_pop_function(ty));
+ Setattr(m, "returntype", ty);
+
+ String *mname = NewStringf("%s::%s", Getattr(parent, "name"), name);
+ /* apply the features of the original method found in the base class */
+ Swig_features_get(Swig_cparse_features(), 0, mname, Getattr(m, "decl"), m);
+ Setattr(item, "methodNode", m);
+ Setattr(item, "vmid", method_id);
+ if (replace == DO_NOT_REPLACE)
+ Append(vm, item);
+ else
+ Setitem(vm, replace, item);
+ Setattr(n, "directorNode", m);
+
+ Delete(mname);
+ }
+ if (is_destructor) {
+ virtual_destructor = 1;
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * Language::unrollVirtualMethods()
+ * ---------------------------------------------------------------------- */
+int Language::unrollVirtualMethods(Node *n, Node *parent, List *vm, int &virtual_destructor, int protectedbase) {
+ bool first_base = false;
+ // recurse through all base classes to build the vtable
+ List *bl = Getattr(n, "bases");
+ if (bl) {
+ Iterator bi;
+ for (bi = First(bl); bi.item; bi = Next(bi)) {
+ if (first_base && !director_multiple_inheritance)
+ break;
+ unrollVirtualMethods(bi.item, parent, vm, virtual_destructor);
+ first_base = true;
+ }
+ }
+
+ // recurse through all protected base classes to build the vtable, as needed
+ bl = Getattr(n, "protectedbases");
+ if (bl) {
+ Iterator bi;
+ for (bi = First(bl); bi.item; bi = Next(bi)) {
+ if (first_base && !director_multiple_inheritance)
+ break;
+ unrollVirtualMethods(bi.item, parent, vm, virtual_destructor, 1);
+ first_base = true;
+ }
+ }
+
+ // find the methods that need directors
+ String *classname = Getattr(n, "name");
+ for (Node *ni = firstChild(n); ni; ni = nextSibling(ni)) {
+ /* we only need to check the virtual members */
+ if (Equal(nodeType(ni), "using")) {
+ for (Node *nn = firstChild(ni); nn; nn = Getattr(nn, "sym:nextSibling")) {
+ unrollOneVirtualMethod(classname, nn, parent, vm, virtual_destructor, protectedbase);
+ }
+ }
+ unrollOneVirtualMethod(classname, ni, parent, vm, virtual_destructor, protectedbase);
+ }
+
+ /*
+ We delete all the nodirector methods. This prevents the
+ generation of 'empty' director classes.
+
+ Done once we've collated all the virtual methods into vm.
+ */
+ if (n == parent) {
+ int len = Len(vm);
+ for (int i = 0; i < len; i++) {
+ Node *item = Getitem(vm, i);
+ Node *m = Getattr(item, "methodNode");
+ /* retrieve the director features */
+ int mdir = GetFlag(m, "feature:director");
+ int mndir = GetFlag(m, "feature:nodirector");
+ /* 'nodirector' has precedence over 'director' */
+ int dir = (mdir || mndir) ? (mdir && !mndir) : 1;
+ /* check if the method was found only in a base class */
+ Node *p = Getattr(m, "parentNode");
+ if (p != n) {
+ Node *c = Copy(m);
+ Setattr(c, "parentNode", n);
+ int cdir = GetFlag(c, "feature:director");
+ int cndir = GetFlag(c, "feature:nodirector");
+ dir = (cdir || cndir) ? (cdir && !cndir) : dir;
+ Delete(c);
+ }
+ if (dir) {
+ /* be sure the 'nodirector' feature is disabled */
+ if (mndir)
+ Delattr(m, "feature:nodirector");
+ } else {
+ /* or just delete from the vm, since is not a director method */
+ Delitem(vm, i);
+ len--;
+ i--;
+ }
+ }
+ }
+
+ return SWIG_OK;
+}
+
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorDisown()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorDisown(Node *n) {
+ Node *disown = NewHash();
+ String *mrename;
+ String *symname = Getattr(n, "sym:name");
+ mrename = Swig_name_disown(NSpace, symname);
+ String *type = NewString(ClassType);
+ String *name = NewString("self");
+ SwigType_add_pointer(type);
+ Parm *p = NewParm(type, name, n);
+ Delete(name);
+ Delete(type);
+ type = NewString("void");
+ String *action = NewString("");
+ Printv(action, "{\n", "Swig::Director *director = SWIG_DIRECTOR_CAST(arg1);\n", "if (director) director->swig_disown();\n", "}\n", NULL);
+ Setfile(disown, Getfile(n));
+ Setline(disown, Getline(n));
+ Setattr(disown, "wrap:action", action);
+ Setattr(disown, "name", mrename);
+ Setattr(disown, "sym:name", mrename);
+ Setattr(disown, "type", type);
+ Setattr(disown, "parms", p);
+ Delete(action);
+ Delete(mrename);
+ Delete(type);
+ Delete(p);
+
+ functionWrapper(disown);
+ Delete(disown);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorConstructors()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorConstructors(Node *n) {
+ Node *ni;
+ String *nodeType;
+ Node *parent = Swig_methodclass(n);
+ int default_ctor = Getattr(parent, "allocate:default_constructor") ? 1 : 0;
+ int protected_ctor = 0;
+ int constructor = 0;
+
+ /* emit constructors */
+ for (ni = Getattr(n, "firstChild"); ni; ni = nextSibling(ni)) {
+ nodeType = Getattr(ni, "nodeType");
+ if (Cmp(nodeType, "constructor") == 0) {
+ if (GetFlag(ni, "feature:ignore"))
+ continue;
+
+ Parm *parms = Getattr(ni, "parms");
+ if (is_public(ni)) {
+ /* emit public constructor */
+ classDirectorConstructor(ni);
+ constructor = 1;
+ if (default_ctor)
+ default_ctor = !ParmList_numrequired(parms);
+ } else {
+ /* emit protected constructor if needed */
+ if (need_nonpublic_ctor(ni)) {
+ classDirectorConstructor(ni);
+ constructor = 1;
+ protected_ctor = 1;
+ if (default_ctor)
+ default_ctor = !ParmList_numrequired(parms);
+ }
+ }
+ }
+ }
+ /* emit default constructor if needed */
+ if (!constructor) {
+ if (!default_ctor) {
+ /* we get here because the class has no public, protected or
+ default constructor, therefore, the director class can't be
+ created, ie, is kind of abstract. */
+ Swig_warning(WARN_LANG_DIRECTOR_ABSTRACT, Getfile(n), Getline(n), "Director class '%s' can't be constructed\n", SwigType_namestr(Getattr(n, "name")));
+ return SWIG_OK;
+ }
+ classDirectorDefaultConstructor(n);
+ default_ctor = 1;
+ }
+ /* this is just to support old java behavior, ie, the default
+ constructor is always emitted, even when protected, and not
+ needed, since there is a public constructor already defined.
+
+ (scottm) This code is needed here to make the director_abstract +
+ test generate compilable code (Example2 in director_abstract.i).
+
+ (mmatus) This is very strange, since swig compiled with gcc3.2.3
+ doesn't need it here....
+ */
+ if (!default_ctor && !protected_ctor) {
+ if (Getattr(parent, "allocate:default_base_constructor")) {
+ classDirectorDefaultConstructor(n);
+ }
+ }
+
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorMethods()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorMethods(Node *n) {
+ Node *vtable = Getattr(n, "vtable");
+
+ int len = Len(vtable);
+ for (int i = 0; i < len; i++) {
+ Node *item = Getitem(vtable, i);
+ String *method = Getattr(item, "methodNode");
+ String *fqdname = Getattr(item, "fqdname");
+ if (GetFlag(method, "feature:nodirector") || GetFlag(method, "final"))
+ continue;
+
+ String *wrn = Getattr(method, "feature:warnfilter");
+ if (wrn)
+ Swig_warnfilter(wrn, 1);
+
+ String *type = Getattr(method, "nodeType");
+ if (!Cmp(type, "destructor")) {
+ classDirectorDestructor(method);
+ } else {
+ Swig_require("classDirectorMethods", method, "*type", NIL);
+ assert(Getattr(method, "returntype"));
+ Setattr(method, "type", Getattr(method, "returntype"));
+ if (classDirectorMethod(method, n, fqdname) == SWIG_OK)
+ SetFlag(item, "director");
+ Swig_restore(method);
+ }
+ if (wrn)
+ Swig_warnfilter(wrn, 0);
+ }
+
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorInit()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorInit(Node *n) {
+ (void) n;
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorDestructor()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorDestructor(Node *n) {
+ /*
+ Always emit the virtual destructor in the declaration and in the
+ compilation unit. Been explicit here can't make any damage, and
+ can solve some nasty C++ compiler problems.
+ */
+ File *f_directors = Swig_filebyname("director");
+ File *f_directors_h = Swig_filebyname("director_h");
+ if (Getattr(n, "throw")) {
+ Printf(f_directors_h, " virtual ~%s() throw();\n", DirectorClassName);
+ Printf(f_directors, "%s::~%s() throw() {\n}\n\n", DirectorClassName, DirectorClassName);
+ } else {
+ Printf(f_directors_h, " virtual ~%s();\n", DirectorClassName);
+ Printf(f_directors, "%s::~%s() {\n}\n\n", DirectorClassName, DirectorClassName);
+ }
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirectorEnd()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirectorEnd(Node *n) {
+ (void) n;
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDirector()
+ * ---------------------------------------------------------------------- */
+
+int Language::classDirector(Node *n) {
+ Node *module = Getattr(n, "module");
+ String *classtype = Getattr(n, "classtype");
+ Hash *directormap = 0;
+ if (module) {
+ directormap = Getattr(module, "wrap:directormap");
+ if (directormap == 0) {
+ directormap = NewHash();
+ Setattr(module, "wrap:directormap", directormap);
+ }
+ }
+ List *vtable = NewList();
+ int virtual_destructor = 0;
+ unrollVirtualMethods(n, n, vtable, virtual_destructor);
+
+ // Emit all the using base::member statements for non virtual members (allprotected mode)
+ Node *ni;
+ String *using_protected_members_code = NewString("");
+ for (ni = Getattr(n, "firstChild"); ni; ni = nextSibling(ni)) {
+ Node *nodeType = Getattr(ni, "nodeType");
+ if (Cmp(nodeType, "destructor") == 0 && GetFlag(ni, "final")) {
+ String *classtype = Getattr(n, "classtype");
+ SWIG_WARN_NODE_BEGIN(ni);
+ Swig_warning(WARN_LANG_DIRECTOR_FINAL, input_file, line_number, "Destructor %s is final, %s cannot be a director class.\n", Swig_name_decl(ni), classtype);
+ SWIG_WARN_NODE_END(ni);
+ SetFlag(n, "feature:nodirector");
+ Delete(vtable);
+ Delete(using_protected_members_code);
+ return SWIG_OK;
+ }
+ bool cdeclaration = (Cmp(nodeType, "cdecl") == 0);
+ if (cdeclaration && !GetFlag(ni, "feature:ignore")) {
+ if (isNonVirtualProtectedAccess(ni)) {
+ Node *overloaded = Getattr(ni, "sym:overloaded");
+ // emit the using base::member statement (but only once if the method is overloaded)
+ if (!overloaded || (overloaded && (overloaded == ni)))
+ Printf(using_protected_members_code, " using %s::%s;\n", SwigType_namestr(ClassName), Getattr(ni, "name"));
+ }
+ }
+ }
+
+ if (virtual_destructor || Len(vtable) > 0) {
+ if (!virtual_destructor) {
+ String *classtype = Getattr(n, "classtype");
+ Swig_warning(WARN_LANG_DIRECTOR_VDESTRUCT, input_file, line_number, "Director base class %s has no virtual destructor.\n", classtype);
+ }
+
+ Setattr(n, "vtable", vtable);
+ if (directormap != 0) {
+ Setattr(directormap, classtype, n);
+ }
+ classDirectorInit(n);
+ classDirectorConstructors(n);
+ classDirectorMethods(n);
+
+ File *f_directors_h = Swig_filebyname("director_h");
+ Printv(f_directors_h, using_protected_members_code, NIL);
+
+ classDirectorEnd(n);
+ }
+ Delete(vtable);
+ Delete(using_protected_members_code);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classDeclaration()
+ * ---------------------------------------------------------------------- */
+
+static void addCopyConstructor(Node *n) {
+ Node *cn = NewHash();
+ set_nodeType(cn, "constructor");
+ Setattr(cn, "access", "public");
+ Setfile(cn, Getfile(n));
+ Setline(cn, Getline(n));
+
+ String *cname = Getattr(n, "name");
+ SwigType *type = Copy(cname);
+ String *name = Swig_scopename_last(cname);
+ String *cc = NewStringf("r.q(const).%s", type);
+ String *decl = NewStringf("f(%s).", cc);
+ String *oldname = Getattr(n, "sym:name");
+
+ if (Getattr(n, "allocate:has_constructor")) {
+ // to work properly with '%rename Class', we must look
+ // for any other constructor in the class, which has not been
+ // renamed, and use its name as oldname.
+ Node *c;
+ for (c = firstChild(n); c; c = nextSibling(c)) {
+ const char *tag = Char(nodeType(c));
+ if (strcmp(tag, "constructor") == 0) {
+ String *cname = Getattr(c, "name");
+ String *csname = Getattr(c, "sym:name");
+ String *clast = Swig_scopename_last(cname);
+ if (Equal(csname, clast)) {
+ oldname = csname;
+ break;
+ }
+ }
+ }
+ }
+
+ String *symname = Swig_name_make(cn, cname, name, decl, oldname);
+ if (Strcmp(symname, "$ignore") != 0) {
+ Parm *p = NewParm(cc, "other", n);
+
+ Setattr(cn, "name", name);
+ Setattr(cn, "sym:name", symname);
+ SetFlag(cn, "feature:new");
+ Setattr(cn, "decl", decl);
+ Setattr(cn, "parentNode", n);
+ Setattr(cn, "parms", p);
+ Setattr(cn, "copy_constructor", "1");
+
+ Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab"));
+ Node *on = Swig_symbol_add(symname, cn);
+ Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn);
+ Swig_symbol_setscope(oldscope);
+
+ if (on == cn) {
+ Node *access = NewHash();
+ set_nodeType(access, "access");
+ Setattr(access, "kind", "public");
+ appendChild(n, access);
+ appendChild(n, cn);
+ Setattr(n, "has_copy_constructor", "1");
+ Setattr(n, "copy_constructor_decl", decl);
+ Setattr(n, "allocate:copy_constructor", "1");
+ Delete(access);
+ }
+ }
+ Delete(cn);
+ Delete(name);
+ Delete(decl);
+ Delete(symname);
+}
+
+static void addDefaultConstructor(Node *n) {
+ Node *cn = NewHash();
+ set_nodeType(cn, "constructor");
+ Setattr(cn, "access", "public");
+ Setfile(cn, Getfile(n));
+ Setline(cn, Getline(n));
+
+ String *cname = Getattr(n, "name");
+ String *name = Swig_scopename_last(cname);
+ String *decl = NewString("f().");
+ String *oldname = Getattr(n, "sym:name");
+ String *symname = Swig_name_make(cn, cname, name, decl, oldname);
+ if (Strcmp(symname, "$ignore") != 0) {
+ Setattr(cn, "name", name);
+ Setattr(cn, "sym:name", symname);
+ SetFlag(cn, "feature:new");
+ Setattr(cn, "decl", decl);
+ Setattr(cn, "parentNode", n);
+ Setattr(cn, "default_constructor", "1");
+ Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab"));
+ Node *on = Swig_symbol_add(symname, cn);
+ Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn);
+ Swig_symbol_setscope(oldscope);
+
+ if (on == cn) {
+ Node *access = NewHash();
+ set_nodeType(access, "access");
+ Setattr(access, "kind", "public");
+ appendChild(n, access);
+ appendChild(n, cn);
+ Setattr(n, "has_default_constructor", "1");
+ Setattr(n, "allocate:default_constructor", "1");
+ Delete(access);
+ }
+ }
+ Delete(cn);
+ Delete(name);
+ Delete(decl);
+ Delete(symname);
+}
+
+static void addDestructor(Node *n) {
+ Node *cn = NewHash();
+ set_nodeType(cn, "destructor");
+ Setattr(cn, "access", "public");
+ Setfile(cn, Getfile(n));
+ Setline(cn, Getline(n));
+
+ String *cname = Getattr(n, "name");
+ String *name = Swig_scopename_last(cname);
+ Insert(name, 0, "~");
+ String *decl = NewString("f().");
+ String *symname = Swig_name_make(cn, cname, name, decl, 0);
+ if (Strcmp(symname, "$ignore") != 0) {
+ String *possible_nonstandard_symname = NewStringf("~%s", Getattr(n, "sym:name"));
+
+ Setattr(cn, "name", name);
+ Setattr(cn, "sym:name", symname);
+ Setattr(cn, "decl", "f().");
+ Setattr(cn, "parentNode", n);
+
+ Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab"));
+ Node *nonstandard_destructor = Equal(possible_nonstandard_symname, symname) ? 0 : Swig_symbol_clookup(possible_nonstandard_symname, 0);
+ Node *on = Swig_symbol_add(symname, cn);
+ Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn);
+ Swig_symbol_setscope(oldscope);
+
+ if (on == cn) {
+ // SWIG accepts a non-standard named destructor in %extend that uses a typedef for the destructor name
+ // For example: typedef struct X {} XX; %extend X { ~XX() {...} }
+ // Don't add another destructor if a nonstandard one has been declared
+ if (!nonstandard_destructor) {
+ Node *access = NewHash();
+ set_nodeType(access, "access");
+ Setattr(access, "kind", "public");
+ appendChild(n, access);
+ appendChild(n, cn);
+ Setattr(n, "has_destructor", "1");
+ Setattr(n, "allocate:destructor", "1");
+ Delete(access);
+ }
+ }
+ Delete(possible_nonstandard_symname);
+ }
+ Delete(cn);
+ Delete(name);
+ Delete(decl);
+ Delete(symname);
+}
+
+int Language::classDeclaration(Node *n) {
+ String *ochildren = Getattr(n, "feature:onlychildren");
+ if (ochildren) {
+ Setattr(n, "feature:emitonlychildren", ochildren);
+ emit_children(n);
+ Delattr(n, "feature:emitonlychildren");
+ SetFlag(n, "feature:ignore");
+ return SWIG_NOWRAP;
+ }
+
+ // save class local variables for nested classes support
+ int oldInClass = InClass;
+ String *oldClassType = ClassType;
+ String *oldClassPrefix = ClassPrefix;
+ String *oldEnumClassPrefix = EnumClassPrefix;
+ String *oldClassName = ClassName;
+ String *oldDirectorClassName = DirectorClassName;
+ String *oldNSpace = NSpace;
+ Node *oldCurrentClass = CurrentClass;
+ int dir = 0;
+
+ String *kind = Getattr(n, "kind");
+ String *name = Getattr(n, "name");
+ String *tdname = Getattr(n, "tdname");
+ String *unnamed = Getattr(n, "unnamed");
+ String *symname = Getattr(n, "sym:name");
+
+ int strip = CPlusPlus ? 1 : unnamed && tdname;
+
+ if (cplus_mode != PUBLIC)
+ return SWIG_NOWRAP;
+ if (!name) {
+ Swig_warning(WARN_LANG_CLASS_UNNAMED, input_file, line_number, "Can't generate wrappers for unnamed struct/class.\n");
+ return SWIG_NOWRAP;
+ }
+
+ /* Check symbol name for template. If not renamed. Issue a warning */
+ if (!validIdentifier(symname)) {
+ Swig_warning(WARN_LANG_IDENTIFIER, input_file, line_number, "Can't wrap class %s unless renamed to a valid identifier.\n", SwigType_namestr(symname));
+ return SWIG_NOWRAP;
+ }
+ AccessMode oldAccessMode = cplus_mode;
+ Node *outerClass = Getattr(n, "nested:outer");
+ if (outerClass && oldAccessMode != PUBLIC)
+ return SWIG_NOWRAP;
+ ClassName = Copy(name);
+ ClassPrefix = Copy(symname);
+ if (Cmp(kind, "class") == 0) {
+ cplus_mode = PRIVATE;
+ } else {
+ cplus_mode = PUBLIC;
+ }
+ for (; outerClass; outerClass = Getattr(outerClass, "nested:outer")) {
+ Push(ClassPrefix, "_");
+ Push(ClassPrefix, Getattr(outerClass, "sym:name"));
+ }
+ EnumClassPrefix = Copy(ClassPrefix);
+ if (strip) {
+ ClassType = Copy(name);
+ } else {
+ ClassType = NewStringf("%s %s", kind, name);
+ }
+ Setattr(n, "classtypeobj", Copy(ClassType));
+ Setattr(n, "classtype", SwigType_namestr(ClassType));
+
+ InClass = 1;
+ CurrentClass = n;
+ NSpace = Getattr(n, "sym:nspace");
+ int oldAbstract = Abstract;
+
+ /* Call classHandler() here */
+ if (!ImportMode) {
+ if (directorsEnabled()) {
+ int ndir = GetFlag(n, "feature:director");
+ int nndir = GetFlag(n, "feature:nodirector");
+ /* 'nodirector' has precedence over 'director' */
+ dir = (ndir || nndir) ? (ndir && !nndir) : 0;
+ }
+ int abstract = !dir && abstractClassTest(n);
+ int odefault = (GenerateDefault && !GetFlag(n, "feature:nodefault"));
+
+ /* default constructor */
+ if (!abstract && !GetFlag(n, "feature:nodefaultctor") && odefault) {
+ if (!Getattr(n, "has_constructor") && !Getattr(n, "allocate:has_constructor") && (Getattr(n, "allocate:default_constructor"))) {
+ addDefaultConstructor(n);
+ }
+ }
+ /* copy constructor */
+ if (CPlusPlus && !abstract && GetFlag(n, "feature:copyctor")) {
+ if (!Getattr(n, "has_copy_constructor") && !Getattr(n, "allocate:has_copy_constructor")
+ && (Getattr(n, "allocate:copy_constructor"))
+ && (!GetFlag(n, "feature:ignore"))) {
+ addCopyConstructor(n);
+ }
+ }
+ /* default destructor */
+ if (!GetFlag(n, "feature:nodefaultdtor") && odefault) {
+ if (!Getattr(n, "has_destructor") && (!Getattr(n, "allocate:has_destructor"))
+ && (Getattr(n, "allocate:default_destructor"))
+ && (!GetFlag(n, "feature:ignore"))) {
+ addDestructor(n);
+ }
+ }
+
+ if (dir) {
+ DirectorClassName = directorClassName(n);
+ classDirector(n);
+ }
+ /* check for abstract after resolving directors */
+
+ Abstract = abstractClassTest(n);
+ classHandler(n);
+ } else {
+ Abstract = abstractClassTest(n);
+ Language::classHandler(n);
+ }
+
+ Abstract = oldAbstract;
+ cplus_mode = oldAccessMode;
+ NSpace = oldNSpace;
+ InClass = oldInClass;
+ CurrentClass = oldCurrentClass;
+ Delete(ClassType);
+ ClassType = oldClassType;
+ Delete(EnumClassPrefix);
+ EnumClassPrefix = oldEnumClassPrefix;
+ Delete(ClassPrefix);
+ ClassPrefix = oldClassPrefix;
+ Delete(ClassName);
+ ClassName = oldClassName;
+ if (dir) {
+ Delete(DirectorClassName);
+ }
+ DirectorClassName = oldDirectorClassName;
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::classHandler(Node *n) {
+ save_value<int> oldExtend(Extend);
+ if (Getattr(n, "template"))
+ Extend = 0;
+ bool hasDirector = Swig_directorclass(n) ? true : false;
+
+ /* Emit all of the class members */
+ emit_children(n);
+
+ /* Look for smart pointer handling */
+ if (Getattr(n, "allocate:smartpointer")) {
+ List *methods = Getattr(n, "allocate:smartpointer");
+ cplus_mode = PUBLIC;
+ SmartPointer = CWRAP_SMART_POINTER;
+ if (Getattr(n, "allocate:smartpointerconst") && Getattr(n, "allocate:smartpointermutable")) {
+ SmartPointer |= CWRAP_SMART_POINTER_OVERLOAD;
+ }
+ Iterator c;
+ for (c = First(methods); c.item; c = Next(c)) {
+ emit_one(c.item);
+ }
+ SmartPointer = 0;
+ }
+
+ cplus_mode = PUBLIC;
+
+ /* emit director disown method */
+ if (hasDirector) {
+ classDirectorDisown(n);
+
+ /* Emit additional protected virtual methods - only needed if the language module
+ * codes logic in the C++ layer instead of the director proxy class method - primarily
+ * to catch public use of protected methods by the scripting languages. */
+ if (dirprot_mode() && extraDirectorProtectedCPPMethodsRequired()) {
+ Node *vtable = Getattr(n, "vtable");
+ String *symname = Getattr(n, "sym:name");
+ save_value<AccessMode> old_mode(cplus_mode);
+ cplus_mode = PROTECTED;
+ int len = Len(vtable);
+ for (int i = 0; i < len; i++) {
+ Node *item = Getitem(vtable, i);
+ Node *method = Getattr(item, "methodNode");
+ SwigType *type = Getattr(method, "nodeType");
+ if (Strcmp(type, "cdecl") != 0)
+ continue;
+ if (GetFlag(method, "feature:ignore"))
+ continue;
+ String *methodname = Getattr(method, "sym:name");
+ String *wrapname = NewStringf("%s_%s", symname, methodname);
+ if (!symbolLookup(wrapname, "") && (!is_public(method))) {
+ Node *m = Copy(method);
+ Setattr(m, "director", "1");
+ Setattr(m, "parentNode", n);
+ /*
+ * There is a bug that needs fixing still...
+ * This area of code is creating methods which have not been overridden in a derived class (director methods that are protected in the base)
+ * If the method is overloaded, then Swig_overload_dispatch() incorrectly generates a call to the base wrapper, _wrap_xxx method
+ * See director_protected_overloaded.i - Possibly sym:overname needs correcting here.
+ Printf(stdout, "new method: %s::%s(%s)\n", Getattr(parentNode(m), "name"), Getattr(m, "name"), ParmList_str_defaultargs(Getattr(m, "parms")));
+ */
+ cDeclaration(m);
+ Delete(m);
+ }
+ Delete(wrapname);
+ }
+ }
+ }
+
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::classforwardDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::classforwardDeclaration(Node *n) {
+ (void) n;
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::constructorDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::constructorDeclaration(Node *n) {
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+
+ if (!symname)
+ return SWIG_NOWRAP;
+ if (!CurrentClass)
+ return SWIG_NOWRAP;
+ if (ImportMode)
+ return SWIG_NOWRAP;
+
+ if (Extend) {
+ /* extend default constructor can be safely ignored if there is already one */
+ int num_required = ParmList_numrequired(Getattr(n, "parms"));
+ if ((num_required == 0) && Getattr(CurrentClass, "has_default_constructor")) {
+ return SWIG_NOWRAP;
+ }
+ if ((num_required == 1) && Getattr(CurrentClass, "has_copy_constructor")) {
+ String *ccdecl = Getattr(CurrentClass, "copy_constructor_decl");
+ if (ccdecl && (Strcmp(ccdecl, Getattr(n, "decl")) == 0)) {
+ return SWIG_NOWRAP;
+ }
+ }
+ }
+
+ /* clean protected overloaded constructors, in case they are not needed anymore */
+ Node *over = Swig_symbol_isoverloaded(n);
+ if (over && !Getattr(CurrentClass, "sym:cleanconstructor")) {
+ int dirclass = Swig_directorclass(CurrentClass);
+ Node *nn = over;
+ while (nn) {
+ if (!is_public(nn)) {
+ if (!dirclass || !need_nonpublic_ctor(nn)) {
+ SetFlag(nn, "feature:ignore");
+ }
+ }
+ nn = Getattr(nn, "sym:nextSibling");
+ }
+ clean_overloaded(over);
+ Setattr(CurrentClass, "sym:cleanconstructor", "1");
+ }
+
+ if ((cplus_mode != PUBLIC)) {
+ /* check only for director classes */
+ if (!Swig_directorclass(CurrentClass) || !need_nonpublic_ctor(n))
+ return SWIG_NOWRAP;
+ }
+
+ /* Name adjustment for %name */
+ Swig_save("constructorDeclaration", n, "sym:name", NIL);
+
+ {
+ String *base = Swig_scopename_last(name);
+ if ((Strcmp(base, symname) == 0) && (Strcmp(symname, ClassPrefix) != 0)) {
+ Setattr(n, "sym:name", ClassPrefix);
+ }
+ Delete(base);
+ }
+
+ /* Only create a constructor if the class is not abstract */
+ if (!Abstract) {
+ Node *over;
+ over = Swig_symbol_isoverloaded(n);
+ if (over)
+ over = first_nontemplate(over);
+ if ((over) && (!overloading)) {
+ /* If the symbol is overloaded. We check to see if it is a copy constructor. If so,
+ we invoke copyconstructorHandler() as a special case. */
+ if (Getattr(n, "copy_constructor") && (!Getattr(CurrentClass, "has_copy_constructor"))) {
+ copyconstructorHandler(n);
+ Setattr(CurrentClass, "has_copy_constructor", "1");
+ } else {
+ if (Getattr(over, "copy_constructor"))
+ over = Getattr(over, "sym:nextSibling");
+ if (over != n) {
+ Swig_warning(WARN_LANG_OVERLOAD_CONSTRUCT, input_file, line_number,
+ "Overloaded constructor ignored. %s\n", Swig_name_decl(n));
+ Swig_warning(WARN_LANG_OVERLOAD_CONSTRUCT, Getfile(over), Getline(over),
+ "Previous declaration is %s\n", Swig_name_decl(over));
+ } else {
+ constructorHandler(n);
+ }
+ }
+ } else {
+ String *expected_name = ClassName;
+ String *scope = Swig_scopename_check(ClassName) ? Swig_scopename_prefix(ClassName) : 0;
+ String *actual_name = scope ? NewStringf("%s::%s", scope, name) : NewString(name);
+ Delete(scope);
+ if (!Equal(actual_name, expected_name) && !SwigType_istemplate(expected_name) && !SwigType_istemplate(actual_name)) {
+ // Checking templates is skipped but they ought to be checked... they are just somewhat more tricky to check correctly
+ bool illegal_name = true;
+ if (Extend) {
+ // Check for typedef names used as a constructor name in %extend. This is deprecated except for anonymous
+ // typedef structs which have had their symbol names adjusted to the typedef name in the parser.
+ SwigType *name_resolved = SwigType_typedef_resolve_all(actual_name);
+ SwigType *expected_name_resolved = SwigType_typedef_resolve_all(expected_name);
+
+ if (!CPlusPlus) {
+ if (Strncmp(name_resolved, "struct ", 7) == 0)
+ Replace(name_resolved, "struct ", "", DOH_REPLACE_FIRST);
+ else if (Strncmp(name_resolved, "union ", 6) == 0)
+ Replace(name_resolved, "union ", "", DOH_REPLACE_FIRST);
+ }
+
+ illegal_name = !Equal(name_resolved, expected_name_resolved);
+ if (!illegal_name)
+ Swig_warning(WARN_LANG_EXTEND_CONSTRUCTOR, input_file, line_number, "Use of an illegal constructor name '%s' in %%extend is deprecated, the constructor name should be '%s'.\n",
+ SwigType_str(Swig_scopename_last(actual_name), 0), SwigType_str(Swig_scopename_last(expected_name), 0));
+ Delete(name_resolved);
+ Delete(expected_name_resolved);
+ }
+ if (illegal_name) {
+ Swig_warning(WARN_LANG_RETURN_TYPE, input_file, line_number, "Function %s must have a return type. Ignored.\n", Swig_name_decl(n));
+ Swig_restore(n);
+ return SWIG_NOWRAP;
+ }
+ }
+ constructorHandler(n);
+ }
+ }
+ Setattr(CurrentClass, "has_constructor", "1");
+
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * get_director_ctor_code()
+ * ---------------------------------------------------------------------- */
+
+static String *get_director_ctor_code(Node *n, String *director_ctor_code, String *director_prot_ctor_code, List *&abstracts) {
+ String *director_ctor = director_ctor_code;
+ int use_director = Swig_directorclass(n);
+ if (use_director) {
+ Node *pn = Swig_methodclass(n);
+ abstracts = Getattr(pn, "abstracts");
+ if (director_prot_ctor_code) {
+ int is_notabstract = GetFlag(pn, "feature:notabstract");
+ int is_abstract = abstracts && !is_notabstract;
+ if (is_protected(n) || is_abstract) {
+ director_ctor = director_prot_ctor_code;
+ abstracts = Copy(abstracts);
+ Delattr(pn, "abstracts");
+ } else {
+ if (is_notabstract) {
+ abstracts = Copy(abstracts);
+ Delattr(pn, "abstracts");
+ } else {
+ abstracts = 0;
+ }
+ }
+ }
+ }
+ return director_ctor;
+}
+
+
+/* ----------------------------------------------------------------------
+ * Language::constructorHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::constructorHandler(Node *n) {
+ Swig_require("constructorHandler", n, "?name", "*sym:name", "?type", "?parms", NIL);
+ String *symname = Getattr(n, "sym:name");
+ String *mrename = Swig_name_construct(NSpace, symname);
+ String *nodeType = Getattr(n, "nodeType");
+ int constructor = (!Cmp(nodeType, "constructor"));
+ List *abstracts = 0;
+ String *director_ctor = get_director_ctor_code(n, director_ctor_code,
+ director_prot_ctor_code,
+ abstracts);
+ if (!constructor) {
+ /* if not originally a constructor, still handle it as one */
+ Setattr(n, "handled_as_constructor", "1");
+ }
+
+ int extendmember = GetFlag(n, "isextendmember") ? Extend : 0;
+ int flags = Getattr(n, "template") ? extendmember : Extend;
+ Swig_ConstructorToFunction(n, NSpace, ClassType, none_comparison, director_ctor, CPlusPlus, flags, DirectorClassName);
+ Setattr(n, "sym:name", mrename);
+ functionWrapper(n);
+ Delete(mrename);
+ Swig_restore(n);
+ if (abstracts)
+ Setattr(Swig_methodclass(n), "abstracts", abstracts);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::copyconstructorHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::copyconstructorHandler(Node *n) {
+ Swig_require("copyconstructorHandler", n, "?name", "*sym:name", "?type", "?parms", NIL);
+ String *symname = Getattr(n, "sym:name");
+ String *mrename = Swig_name_copyconstructor(NSpace, symname);
+ List *abstracts = 0;
+ String *director_ctor = get_director_ctor_code(n, director_ctor_code,
+ director_prot_ctor_code,
+ abstracts);
+ Swig_ConstructorToFunction(n, NSpace, ClassType, none_comparison, director_ctor, CPlusPlus, Getattr(n, "template") ? 0 : Extend, DirectorClassName);
+ Setattr(n, "sym:name", mrename);
+ functionWrapper(n);
+ Delete(mrename);
+ Swig_restore(n);
+ if (abstracts)
+ Setattr(Swig_methodclass(n), "abstracts", abstracts);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::destructorDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::destructorDeclaration(Node *n) {
+
+ if (!CurrentClass)
+ return SWIG_NOWRAP;
+ if (cplus_mode != PUBLIC && !Getattr(CurrentClass, "feature:unref"))
+ return SWIG_NOWRAP;
+ if (ImportMode)
+ return SWIG_NOWRAP;
+
+ Swig_save("destructorDeclaration", n, "name", "sym:name", NIL);
+
+ char *c = GetChar(n, "sym:name");
+ if (c && (*c == '~')) {
+ Setattr(n, "sym:name", c + 1);
+ }
+
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+
+ if ((Strcmp(name, symname) == 0) || (Strcmp(symname, ClassPrefix) != 0)) {
+ Setattr(n, "sym:name", ClassPrefix);
+ }
+
+ String *expected_name = ClassName;
+ String *scope = Swig_scopename_check(ClassName) ? Swig_scopename_prefix(ClassName) : 0;
+ String *actual_name = scope ? NewStringf("%s::%s", scope, name) : NewString(name);
+ Delete(scope);
+ Replace(actual_name, "~", "", DOH_REPLACE_FIRST);
+ if (!Equal(actual_name, expected_name) && !(Getattr(n, "template"))) {
+ bool illegal_name = true;
+ if (Extend) {
+ // Check for typedef names used as a destructor name in %extend. This is deprecated except for anonymous
+ // typedef structs which have had their symbol names adjusted to the typedef name in the parser.
+ SwigType *name_resolved = SwigType_typedef_resolve_all(actual_name);
+ SwigType *expected_name_resolved = SwigType_typedef_resolve_all(expected_name);
+
+ if (!CPlusPlus) {
+ if (Strncmp(name_resolved, "struct ", 7) == 0)
+ Replace(name_resolved, "struct ", "", DOH_REPLACE_FIRST);
+ else if (Strncmp(name_resolved, "union ", 6) == 0)
+ Replace(name_resolved, "union ", "", DOH_REPLACE_FIRST);
+ }
+
+ illegal_name = !Equal(name_resolved, expected_name_resolved);
+ if (!illegal_name)
+ Swig_warning(WARN_LANG_EXTEND_DESTRUCTOR, input_file, line_number, "Use of an illegal destructor name '%s' in %%extend is deprecated, the destructor name should be '%s'.\n",
+ SwigType_str(Swig_scopename_last(actual_name), 0), SwigType_str(Swig_scopename_last(expected_name), 0));
+ Delete(name_resolved);
+ Delete(expected_name_resolved);
+ }
+
+ if (illegal_name) {
+ Swig_warning(WARN_LANG_ILLEGAL_DESTRUCTOR, input_file, line_number, "Illegal destructor name %s. Ignored.\n", Swig_name_decl(n));
+ Swig_restore(n);
+ return SWIG_NOWRAP;
+ }
+ }
+ destructorHandler(n);
+
+ Setattr(CurrentClass, "has_destructor", "1");
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::destructorHandler()
+ * ---------------------------------------------------------------------- */
+
+int Language::destructorHandler(Node *n) {
+ Swig_require("destructorHandler", n, "?name", "*sym:name", NIL);
+ Swig_save("destructorHandler", n, "type", "parms", NIL);
+
+ String *symname = Getattr(n, "sym:name");
+ String *mrename;
+ char *csymname = Char(symname);
+ if (*csymname == '~')
+ csymname += 1;
+
+ mrename = Swig_name_destroy(NSpace, csymname);
+
+ Swig_DestructorToFunction(n, NSpace, ClassType, CPlusPlus, Extend);
+ Setattr(n, "sym:name", mrename);
+ functionWrapper(n);
+ Delete(mrename);
+ Swig_restore(n);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::accessDeclaration()
+ * ---------------------------------------------------------------------- */
+
+int Language::accessDeclaration(Node *n) {
+ String *kind = Getattr(n, "kind");
+ if (Cmp(kind, "public") == 0) {
+ cplus_mode = PUBLIC;
+ } else if (Cmp(kind, "private") == 0) {
+ cplus_mode = PRIVATE;
+ } else if (Cmp(kind, "protected") == 0) {
+ cplus_mode = PROTECTED;
+ }
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::namespaceDeclaration()
+ * ----------------------------------------------------------------------------- */
+
+int Language::namespaceDeclaration(Node *n) {
+ if (Getattr(n, "alias"))
+ return SWIG_OK;
+ if (Getattr(n, "unnamed"))
+ return SWIG_OK;
+ emit_children(n);
+ return SWIG_OK;
+}
+
+int Language::validIdentifier(String *s) {
+ char *c = Char(s);
+ while (*c) {
+ if (!(isalnum(*c) || (*c == '_')))
+ return 0;
+ c++;
+ }
+ return 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::usingDeclaration()
+ * ----------------------------------------------------------------------------- */
+
+int Language::usingDeclaration(Node *n) {
+ if ((cplus_mode == PUBLIC) || (!is_public(n) && dirprot_mode())) {
+ Node *np = Copy(n);
+ Node *c;
+ for (c = firstChild(np); c; c = nextSibling(c)) {
+ /* it seems for some cases this is needed, like A* A::boo() */
+ if (CurrentClass)
+ Setattr(c, "parentNode", CurrentClass);
+ emit_one(c);
+ }
+ Delete(np);
+ }
+ return SWIG_OK;
+}
+
+/* Stubs. Language modules need to implement these */
+
+/* ----------------------------------------------------------------------
+ * Language::constantWrapper()
+ * ---------------------------------------------------------------------- */
+
+int Language::constantWrapper(Node *n) {
+ String *name = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *value = Getattr(n, "value");
+ String *str = SwigType_str(type, name);
+ Printf(stdout, "constantWrapper : %s = %s\n", str, value);
+ Delete(str);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::variableWrapper()
+ * ---------------------------------------------------------------------- */
+
+int Language::variableWrapper(Node *n) {
+ Swig_require("variableWrapper", n, "*name", "*sym:name", "*type", "?parms", "?varset", "?varget", NIL);
+ String *symname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *name = Getattr(n, "name");
+
+ Delattr(n,"varset");
+ Delattr(n,"varget");
+
+ String *newsymname = 0;
+ if (!CurrentClass && EnumClassPrefix) {
+ newsymname = Swig_name_member(0, EnumClassPrefix, symname);
+ symname = newsymname;
+ }
+
+ /* If no way to set variables. We simply create functions */
+ int assignable = is_assignable(n);
+ int flags = use_naturalvar_mode(n);
+ if (!GetFlag(n, "wrappedasconstant"))
+ flags = flags | Extend;
+
+ if (assignable) {
+ int make_set_wrapper = 1;
+ String *tm = Swig_typemap_lookup("globalin", n, name, 0);
+
+ Swig_VarsetToFunction(n, flags);
+ String *sname = Swig_name_set(NSpace, symname);
+ Setattr(n, "sym:name", sname);
+ Delete(sname);
+
+ if (!tm) {
+ if (SwigType_isarray(type)) {
+ Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s.\n", SwigType_str(type, 0));
+ make_set_wrapper = 0;
+ }
+ } else {
+ String *pname0 = Swig_cparm_name(0, 0);
+ Replace(tm, "$input", pname0, DOH_REPLACE_ANY);
+ Setattr(n, "wrap:action", tm);
+ Delete(tm);
+ Delete(pname0);
+ }
+ if (make_set_wrapper) {
+ Setattr(n, "varset", "1");
+ functionWrapper(n);
+ } else {
+ SetFlag(n, "feature:immutable");
+ }
+ /* Restore parameters */
+ Setattr(n, "sym:name", symname);
+ Setattr(n, "type", type);
+ Setattr(n, "name", name);
+ Delattr(n, "varset");
+
+ /* Delete all attached typemaps and typemap attributes */
+ Iterator ki;
+ for (ki = First(n); ki.key; ki = Next(ki)) {
+ if (Strncmp(ki.key, "tmap:", 5) == 0)
+ Delattr(n, ki.key);
+ }
+ }
+
+ Swig_VargetToFunction(n, flags);
+ String *gname = Swig_name_get(NSpace, symname);
+ Setattr(n, "sym:name", gname);
+ Delete(gname);
+ Setattr(n, "varget", "1");
+ functionWrapper(n);
+ Delattr(n, "varget");
+ Swig_restore(n);
+ Delete(newsymname);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * Language::functionWrapper()
+ * ---------------------------------------------------------------------- */
+
+int Language::functionWrapper(Node *n) {
+ String *name = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ ParmList *parms = Getattr(n, "parms");
+
+ Printf(stdout, "functionWrapper : %s\n", SwigType_str(type, NewStringf("%s(%s)", name, ParmList_str_defaultargs(parms))));
+ Printf(stdout, " action : %s\n", Getattr(n, "wrap:action"));
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::nativeWrapper()
+ * ----------------------------------------------------------------------------- */
+
+int Language::nativeWrapper(Node *n) {
+ (void) n;
+ return SWIG_OK;
+}
+
+void Language::main(int argc, char *argv[]) {
+ (void) argc;
+ (void) argv;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::addSymbol()
+ *
+ * Adds a symbol entry into the target language symbol tables.
+ * Returns 1 if the symbol is added successfully.
+ * Prints an error message and returns 0 if a conflict occurs.
+ * The scope is optional for target languages and if supplied must be a fully
+ * qualified scope and the symbol s must not contain any scope qualifiers.
+ * ----------------------------------------------------------------------------- */
+
+int Language::addSymbol(const String *s, const Node *n, const_String_or_char_ptr scope) {
+ //Printf( stdout, "addSymbol: %s %s\n", s, scope );
+ Hash *symbols = Getattr(symtabs, scope ? scope : "");
+ if (!symbols) {
+ symbols = symbolAddScope(scope);
+ } else {
+ Node *c = Getattr(symbols, s);
+ if (c && (c != n)) {
+ if (scope && Len(scope) > 0)
+ Swig_error(input_file, line_number, "'%s' is multiply defined in the generated target language module in scope '%s'.\n", s, scope);
+ else
+ Swig_error(input_file, line_number, "'%s' is multiply defined in the generated target language module.\n", s);
+ Swig_error(Getfile(c), Getline(c), "Previous declaration of '%s'\n", s);
+ return 0;
+ }
+ }
+ Setattr(symbols, s, n);
+ return 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::addInterfaceSymbol()
+ *
+ * Adds a symbol entry into the target language symbol tables - for the interface
+ * feature only.
+ * Returns 1 if the symbol is added successfully.
+ * The scope is as per addSymbol.
+ * ----------------------------------------------------------------------------- */
+
+int Language::addInterfaceSymbol(const String *interface_name, Node *n, const_String_or_char_ptr scope) {
+ if (interface_name) {
+ Node *existing_symbol = symbolLookup(interface_name, scope);
+ if (existing_symbol) {
+ String *proxy_class_name = Getattr(n, "sym:name");
+ Swig_error(input_file, line_number, "The interface feature name '%s' for proxy class '%s' is already defined in the generated target language module in scope '%s'.\n",
+ interface_name, proxy_class_name, scope);
+ Swig_error(Getfile(existing_symbol), Getline(existing_symbol), "Previous declaration of '%s'\n", interface_name);
+ return 0;
+ }
+ if (!addSymbol(interface_name, n, scope))
+ return 0;
+ }
+ return 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::symbolAddScope()
+ *
+ * Creates a scope (symbols Hash) for given name. This method is auxiliary,
+ * you don't have to call it - addSymbols will lazily create scopes automatically.
+ * If scope with given name already exists, then do nothing.
+ * Returns newly created (or already existing) scope.
+ * ----------------------------------------------------------------------------- */
+Hash* Language::symbolAddScope(const_String_or_char_ptr scope) {
+ Hash *symbols = symbolScopeLookup(scope);
+ if(!symbols) {
+ // The order in which the following code is executed is important. In the Language
+ // constructor addScope("") is called to create a top level scope.
+ // Thus we must first add a symbols hash to symtab and only then add pseudo
+ // symbols to the top-level scope.
+
+ // New scope which has not been added by the target language - lazily created.
+ symbols = NewHash();
+ Setattr(symtabs, scope, symbols);
+
+ // Add the new scope as a symbol in the top level scope.
+ // Alternatively the target language must add it in before attempting to add symbols into the scope.
+ const_String_or_char_ptr top_scope = "";
+ Hash *topscope_symbols = Getattr(symtabs, top_scope);
+ Hash *pseudo_symbol = NewHash();
+ Setattr(pseudo_symbol, "sym:scope", "1");
+ Setattr(topscope_symbols, scope, pseudo_symbol);
+ }
+ return symbols;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::symbolScopeLookup()
+ *
+ * Lookup and returns a symtable (hash) representing given scope. Hash contains
+ * all symbols in this scope.
+ * ----------------------------------------------------------------------------- */
+Hash* Language::symbolScopeLookup( const_String_or_char_ptr scope ) {
+ Hash *symbols = Getattr(symtabs, scope ? scope : "");
+ return symbols;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::symbolScopePseudoSymbolLookup()
+ *
+ * For every scope there is a special pseudo-symbol in the top scope (""). It
+ * exists solely to detect name clashes. This pseudo symbol may contain a few properties,
+ * but more could be added. This is also true for the top level scope ("").
+ * It contains a pseudo symbol with name "" (empty). Pseudo symbol contains the
+ * following properties:
+ * sym:scope = "1" - a flag that this is a scope pseudo symbol
+ *
+ * Pseudo symbols are a Hash*, not a Node*.
+ * There is no difference from symbolLookup() method except for signature
+ * and return type.
+ * ----------------------------------------------------------------------------- */
+Hash* Language::symbolScopePseudoSymbolLookup( const_String_or_char_ptr scope )
+{
+ /* Getting top scope */
+ const_String_or_char_ptr top_scope = "";
+ Hash *symbols = Getattr(symtabs, top_scope);
+ return Getattr(symbols, scope);
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::dumpSymbols()
+ * ----------------------------------------------------------------------------- */
+
+void Language::dumpSymbols() {
+ Printf(stdout, "LANGUAGE SYMBOLS start =======================================\n");
+
+ Node *table = symtabs;
+ Iterator ki = First(table);
+ while (ki.key) {
+ String *k = ki.key;
+ Printf(stdout, "===================================================\n");
+ Printf(stdout, "%s -\n", k);
+ {
+ Symtab *symtab = Getattr(table, k);
+ Iterator it = First(symtab);
+ while (it.key) {
+ String *symname = it.key;
+ Printf(stdout, " %s\n", symname);
+ it = Next(it);
+ }
+ }
+ ki = Next(ki);
+ }
+
+ Printf(stdout, "LANGUAGE SYMBOLS finish =======================================\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::symbolLookup()
+ * ----------------------------------------------------------------------------- */
+
+Node *Language::symbolLookup(const String *s, const_String_or_char_ptr scope) {
+ Hash *symbols = Getattr(symtabs, scope ? scope : "");
+ if (!symbols) {
+ return NULL;
+ }
+ return Getattr(symbols, s);
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::classLookup()
+ *
+ * Tries to locate a class from a type definition
+ * ----------------------------------------------------------------------------- */
+
+Node *Language::classLookup(const SwigType *s) {
+ static Hash *classtypes = 0;
+
+ Node *n = 0;
+
+ /* Look in hash of cached values */
+ n = classtypes ? Getattr(classtypes, s) : 0;
+ if (!n) {
+ Symtab *stab = 0;
+ SwigType *ty1 = SwigType_typedef_resolve_all(s);
+ SwigType *ty2 = SwigType_strip_qualifiers(ty1);
+
+ String *base = SwigType_base(ty2);
+
+ Replaceall(base, "class ", "");
+ Replaceall(base, "struct ", "");
+ Replaceall(base, "union ", "");
+
+ if (strncmp(Char(base), "::", 2) == 0) {
+ String *oldbase = base;
+ base = NewString(Char(base) + 2);
+ Delete(oldbase);
+ }
+
+ String *prefix = SwigType_prefix(ty2);
+
+ /* Do a symbol table search on the base type */
+ while (!n) {
+ Hash *nstab;
+ n = Swig_symbol_clookup(base, stab);
+ if (!n)
+ break;
+ if (Strcmp(nodeType(n), "class") == 0)
+ break;
+ Node *sibling = n;
+ while (sibling) {
+ sibling = Getattr(sibling, "csym:nextSibling");
+ if (sibling && Strcmp(nodeType(sibling), "class") == 0)
+ break;
+ }
+ if (sibling)
+ break;
+ n = parentNode(n);
+ if (!n)
+ break;
+ nstab = Getattr(n, "sym:symtab");
+ n = 0;
+ if ((!nstab) || (nstab == stab)) {
+ break;
+ }
+ stab = nstab;
+ }
+ if (n) {
+ /* Found a match. Look at the prefix. We only allow
+ the cases where we want a proxy class for the particular type */
+ bool acceptable_prefix =
+ (Len(prefix) == 0) || // simple type (pass by value)
+ (Strcmp(prefix, "p.") == 0) || // pointer
+ (Strcmp(prefix, "r.") == 0) || // reference
+ (Strcmp(prefix, "z.") == 0) || // rvalue reference
+ SwigType_prefix_is_simple_1D_array(prefix); // Simple 1D array (not arrays of pointers/references)
+ // Also accept pointer by const reference, not non-const pointer reference
+ if (!acceptable_prefix && (Strcmp(prefix, "r.p.") == 0)) {
+ Delete(prefix);
+ prefix = SwigType_prefix(ty1);
+ acceptable_prefix = (Strncmp(prefix, "r.q(const", 9) == 0);
+ }
+ if (acceptable_prefix) {
+ SwigType *cs = Copy(s);
+ if (!classtypes)
+ classtypes = NewHash();
+ Setattr(classtypes, cs, n);
+ Delete(cs);
+ } else {
+ n = 0;
+ }
+ }
+ Delete(prefix);
+ Delete(base);
+ Delete(ty2);
+ Delete(ty1);
+ }
+ if (n && (GetFlag(n, "feature:ignore") || Getattr(n, "feature:onlychildren"))) {
+ n = 0;
+ }
+
+ return n;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::enumLookup()
+ *
+ * Finds and returns the Node containing the enum declaration for the (enum)
+ * type passed in.
+ * ----------------------------------------------------------------------------- */
+
+Node *Language::enumLookup(SwigType *s) {
+ static Hash *enumtypes = 0;
+
+ Node *n = 0;
+
+ /* Look in hash of cached values */
+ n = enumtypes ? Getattr(enumtypes, s) : 0;
+ if (!n) {
+ Symtab *stab = 0;
+ SwigType *lt = SwigType_ltype(s);
+ SwigType *ty1 = SwigType_typedef_resolve_all(lt);
+ SwigType *ty2 = SwigType_strip_qualifiers(ty1);
+
+ String *base = SwigType_base(ty2);
+
+ Replaceall(base, "enum ", "");
+ String *prefix = SwigType_prefix(ty2);
+
+ if (strncmp(Char(base), "::", 2) == 0) {
+ String *oldbase = base;
+ base = NewString(Char(base) + 2);
+ Delete(oldbase);
+ }
+
+ /* Look for type in symbol table */
+ while (!n) {
+ Hash *nstab;
+ n = Swig_symbol_clookup(base, stab);
+ if (!n)
+ break;
+ if (Equal(nodeType(n), "enum"))
+ break;
+ if (Equal(nodeType(n), "enumforward") && GetFlag(n, "enumMissing"))
+ break;
+ n = parentNode(n);
+ if (!n)
+ break;
+ nstab = Getattr(n, "sym:symtab");
+ n = 0;
+ if ((!nstab) || (nstab == stab)) {
+ break;
+ }
+ stab = nstab;
+ }
+ if (n) {
+ /* Found a match. Look at the prefix. We only allow simple types. */
+ if (Len(prefix) == 0) { /* Simple type */
+ if (!enumtypes)
+ enumtypes = NewHash();
+ Setattr(enumtypes, Copy(s), n);
+ } else {
+ n = 0;
+ }
+ }
+ Delete(prefix);
+ Delete(base);
+ Delete(ty2);
+ Delete(ty1);
+ Delete(lt);
+ }
+ if (n && (GetFlag(n, "feature:ignore"))) {
+ n = 0;
+ }
+
+ return n;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::allow_overloading()
+ * ----------------------------------------------------------------------------- */
+
+void Language::allow_overloading(int val) {
+ overloading = val;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::allow_multiple_input()
+ * ----------------------------------------------------------------------------- */
+
+void Language::allow_multiple_input(int val) {
+ multiinput = val;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::enable_cplus_runtime_mode()
+ * ----------------------------------------------------------------------------- */
+
+void Language::enable_cplus_runtime_mode() {
+ cplus_runtime = 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::cplus_runtime_mode()
+ * ----------------------------------------------------------------------------- */
+
+int Language::cplus_runtime_mode() {
+ return cplus_runtime;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::allow_directors()
+ * ----------------------------------------------------------------------------- */
+
+void Language::allow_directors(int val) {
+ directors = val;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::directorsEnabled()
+ * ----------------------------------------------------------------------------- */
+
+int Language::directorsEnabled() const {
+ return director_language && CPlusPlus && (directors || director_mode);
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::allow_dirprot()
+ * ----------------------------------------------------------------------------- */
+
+void Language::allow_dirprot(int val) {
+ director_protected_mode = val;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::allow_allprotected()
+ * ----------------------------------------------------------------------------- */
+
+void Language::allow_allprotected(int val) {
+ all_protected_mode = val;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::dirprot_mode()
+ * ----------------------------------------------------------------------------- */
+
+int Language::dirprot_mode() const {
+ return directorsEnabled() ? director_protected_mode : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::need_nonpublic_ctor()
+ * ----------------------------------------------------------------------------- */
+
+int Language::need_nonpublic_ctor(Node *n) {
+ /*
+ detects when a protected constructor is needed, which is always
+ the case if 'dirprot' mode is used. However, if that is not the
+ case, we will try to strictly emit what is minimal to don't break
+ the generated, while preserving compatibility with java, which
+ always try to emit the default constructor.
+
+ rules:
+
+ - when dirprot mode is used, the protected constructors are
+ always needed.
+
+ - the protected default constructor is always needed.
+
+ - if dirprot mode is not used, the protected constructors will be
+ needed only if:
+
+ - there is no any public constructor in the class, and
+ - there is no protected default constructor
+
+ In that case, all the declared protected constructors are
+ needed since we don't know which one to pick up.
+
+ Note: given all the complications here, I am always in favor to
+ always enable 'dirprot', since is the C++ idea of protected
+ members, and use %ignore for the method you don't want to add in
+ the director class.
+ */
+ if (directorsEnabled()) {
+ if (is_protected(n)) {
+ if (dirprot_mode()) {
+ /* when using dirprot mode, the protected constructors are
+ always needed */
+ return 1;
+ } else {
+ int is_default_ctor = !ParmList_numrequired(Getattr(n, "parms"));
+ if (is_default_ctor) {
+ /* the default protected constructor is always needed, for java compatibility */
+ return 1;
+ } else {
+ /* check if there is a public constructor */
+ Node *parent = Swig_methodclass(n);
+ int public_ctor = Getattr(parent, "allocate:default_constructor")
+ || Getattr(parent, "allocate:public_constructor");
+ if (!public_ctor) {
+ /* if not, the protected constructor will be needed only
+ if there is no protected default constructor declared */
+ int no_prot_default_ctor = !Getattr(parent, "allocate:default_base_constructor");
+ return no_prot_default_ctor;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::need_nonpublic_member()
+ * ----------------------------------------------------------------------------- */
+int Language::need_nonpublic_member(Node *n) {
+ if (directorsEnabled() && DirectorClassName) {
+ if (is_protected(n)) {
+ if (dirprot_mode()) {
+ /* when using dirprot mode, the protected members are always needed. */
+ return 1;
+ } else {
+ /* if the method is pure virtual, we need it. */
+ int pure_virtual = (Cmp(Getattr(n, "value"), "0") == 0);
+ return pure_virtual;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Language::is_smart_pointer()
+ * ----------------------------------------------------------------------------- */
+
+int Language::is_smart_pointer() const {
+ return SmartPointer;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::makeParameterName()
+ *
+ * Inputs:
+ * n - Node
+ * p - parameter node
+ * arg_num - parameter argument number
+ * setter - set this flag when wrapping variables
+ * Return:
+ * arg - a unique parameter name
+ * ----------------------------------------------------------------------------- */
+String *Language::makeParameterName(Node *n, Parm *p, int arg_num, bool setter) const {
+
+ String *arg = 0;
+ String *pn = Getattr(p, "name");
+
+ // Check if parameter name is a duplicate.
+ int count = 0;
+ ParmList *plist = Getattr(n, "parms");
+ while (plist) {
+ if ((Cmp(pn, Getattr(plist, "name")) == 0))
+ count++;
+ plist = nextSibling(plist);
+ }
+
+ // If the parameter has no name at all or has a non-unique name, replace it with "argN".
+ if (!pn || count > 1) {
+ arg = NewStringf("arg%d", arg_num);
+ } else {
+ // Otherwise, try to use the original C name, but modify it if necessary to avoid conflicting with the language keywords.
+ arg = Swig_name_make(p, 0, pn, 0, 0);
+ }
+
+ if (setter && Cmp(arg, "self") != 0) {
+ // Some languages (C#) insist on calling the input variable "value" while
+ // others (D, Java) could, in principle, use something different but this
+ // would require more work, and so we just use "value" for them too.
+ // For setters the parameter name sometimes includes C++ scope resolution which needs removing.
+ Delete(arg);
+ arg = NewString("value");
+ }
+
+ return arg;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::()
+ * ----------------------------------------------------------------------------- */
+
+bool Language::isNonVirtualProtectedAccess(Node *n) const {
+ // Ideally is_non_virtual_protected_access() would contain all this logic, see
+ // comments therein about vtable.
+ return DirectorClassName && is_non_virtual_protected_access(n);
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::extraDirectorProtectedCPPMethodsRequired()
+ * ----------------------------------------------------------------------------- */
+
+bool Language::extraDirectorProtectedCPPMethodsRequired() const {
+ return true;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::nestedClassesSupport()
+ * ----------------------------------------------------------------------------- */
+
+Language::NestedClassSupport Language::nestedClassesSupport() const {
+ return NCS_Unknown;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::kwargsSupport()
+ * ----------------------------------------------------------------------------- */
+
+bool Language::kwargsSupport() const {
+ return false;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::is_wrapping_class()
+ * ----------------------------------------------------------------------------- */
+
+int Language::is_wrapping_class() const {
+ return InClass;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::getCurrentClass()
+ * ----------------------------------------------------------------------------- */
+
+Node *Language::getCurrentClass() const {
+ return CurrentClass;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::getNSpace()
+ * ----------------------------------------------------------------------------- */
+
+String *Language::getNSpace() const {
+ return NSpace;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::getClassName()
+ * ----------------------------------------------------------------------------- */
+
+String *Language::getClassName() const {
+ return ClassName;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::getClassPrefix()
+ * ----------------------------------------------------------------------------- */
+
+String *Language::getClassPrefix() const {
+ return ClassPrefix;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::getEnumClassPrefix()
+ * ----------------------------------------------------------------------------- */
+
+String *Language::getEnumClassPrefix() const {
+ return EnumClassPrefix;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::getClassType()
+ * ----------------------------------------------------------------------------- */
+
+String *Language::getClassType() const {
+ return ClassType;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::abstractClassTest()
+ * ----------------------------------------------------------------------------- */
+//#define SWIG_DEBUG
+int Language::abstractClassTest(Node *n) {
+ /* check for non public operator new */
+ if (GetFlag(n, "feature:notabstract"))
+ return 0;
+ if (Getattr(n, "allocate:nonew"))
+ return 1;
+
+ // A class cannot be instantiated if one of its bases has a private destructor
+ // Note that if the above does not hold the class can be instantiated if its own destructor is private
+ List *bases = Getattr(n, "bases");
+ if (bases) {
+ for (int i = 0; i < Len(bases); i++) {
+ Node *b = Getitem(bases, i);
+ if (GetFlag(b, "allocate:private_destructor"))
+ return 1;
+ }
+ }
+
+ /* now check for the rest */
+ List *abstracts = Getattr(n, "abstracts");
+ if (!abstracts)
+ return 0;
+ int labs = Len(abstracts);
+#ifdef SWIG_DEBUG
+ List *allbases = Getattr(n, "allbases");
+ Printf(stderr, "testing %s %d %d\n", Getattr(n, "name"), labs, Len(allbases));
+#endif
+ if (!labs)
+ return 0; /*strange, but need to be fixed */
+ if (abstracts && !directorsEnabled())
+ return 1;
+ if (!GetFlag(n, "feature:director"))
+ return 1;
+
+ Node *dirabstract = 0;
+ Node *vtable = Getattr(n, "vtable");
+ if (vtable) {
+#ifdef SWIG_DEBUG
+ Printf(stderr, "vtable %s %d %d\n", Getattr(n, "name"), Len(vtable), labs);
+#endif
+ for (int i = 0; i < labs; i++) {
+ Node *ni = Getitem(abstracts, i);
+ String *method_id = vtable_method_id(ni);
+ if (!method_id)
+ continue;
+ bool exists_item = false;
+ int len = Len(vtable);
+ for (int i = 0; i < len; i++) {
+ Node *item = Getitem(vtable, i);
+ String *check_item = Getattr(item, "vmid");
+ if (Strcmp(method_id, check_item) == 0) {
+ exists_item = true;
+ break;
+ }
+ }
+#ifdef SWIG_DEBUG
+ Printf(stderr, "method %s %d\n", method_id, exists_item ? 1 : 0);
+#endif
+ Delete(method_id);
+ if (!exists_item) {
+ dirabstract = ni;
+ break;
+ }
+ }
+ if (dirabstract) {
+ if (is_public(dirabstract)) {
+ Swig_warning(WARN_LANG_DIRECTOR_ABSTRACT, Getfile(n), Getline(n),
+ "Director class '%s' is abstract, abstract method '%s' is not accessible, maybe due to multiple inheritance or 'nodirector' feature\n",
+ SwigType_namestr(Getattr(n, "name")), Getattr(dirabstract, "name"));
+ } else {
+ Swig_warning(WARN_LANG_DIRECTOR_ABSTRACT, Getfile(n), Getline(n),
+ "Director class '%s' is abstract, abstract method '%s' is private\n", SwigType_namestr(Getattr(n, "name")), Getattr(dirabstract, "name"));
+ }
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+ return 0;
+}
+
+void Language::setSubclassInstanceCheck(String *nc) {
+ none_comparison = nc;
+}
+
+void Language::setOverloadResolutionTemplates(String *argc, String *argv) {
+ Delete(argc_template_string);
+ argc_template_string = Copy(argc);
+ Delete(argv_template_string);
+ argv_template_string = Copy(argv);
+}
+
+int Language::is_assignable(Node *n) {
+ if (GetFlag(n, "feature:immutable"))
+ return 0;
+ SwigType *type = Getattr(n, "type");
+ Node *cn = 0;
+ SwigType *ftd = SwigType_typedef_resolve_all(type);
+ SwigType *td = SwigType_strip_qualifiers(ftd);
+ if (SwigType_type(td) == T_USER) {
+ cn = Swig_symbol_clookup(td, 0);
+ if (cn) {
+ if ((Strcmp(nodeType(cn), "class") == 0)) {
+ if (Getattr(cn, "allocate:noassign")) {
+ SetFlag(n, "feature:immutable");
+ Delete(ftd);
+ Delete(td);
+ return 0;
+ }
+ }
+ }
+ }
+ Delete(ftd);
+ Delete(td);
+ return 1;
+}
+
+String *Language::runtimeCode() {
+ return NewString("");
+}
+
+String *Language::defaultExternalRuntimeFilename() {
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Language::replaceSpecialVariables()
+ *
+ * Language modules should implement this if special variables are to be handled
+ * correctly in the $typemap(...) special variable macro.
+ * method - typemap method name
+ * tm - string containing typemap contents
+ * parm - a parameter describing the typemap type to be handled
+ * ----------------------------------------------------------------------------- */
+void Language::replaceSpecialVariables(String *method, String *tm, Parm *parm) {
+ (void)method;
+ (void)tm;
+ (void)parm;
+}
+
+Language *Language::instance() {
+ return this_;
+}
+
+Hash *Language::getClassHash() const {
+ return classhash;
+}
+
diff --git a/contrib/tools/swig/Source/Modules/lua.cxx b/contrib/tools/swig/Source/Modules/lua.cxx
new file mode 100644
index 0000000000..d3cf96b526
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/lua.cxx
@@ -0,0 +1,2240 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * lua.cxx
+ *
+ * Lua language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+/* NEW LANGUAGE NOTE:
+ * ver001
+ this is simply a copy of tcl8.cxx, which has been renamed
+ * ver002
+ all non essential code commented out, program now does virtually nothing
+ it prints to stderr the list of functions to wrap, but does not create
+ the XXX_wrap.c file
+ * ver003
+ added back top(), still prints the list of fns to stderr
+ but now creates a rather empty XXX_wrap.c with some basic boilerplate code
+ * ver004
+ very basic version of functionWrapper()
+ also uncommented usage_string() to keep compiler happy
+ this will start producing proper looking code soon (I hope)
+ produced the wrapper code, but without any type conversion (in or out)
+ generates a few warning because of no wrappering
+ does not generate SWIG_init()
+ reason for this is that lua.swg is empty
+ we will need to add code into this to make it work
+ * ver005/6
+ massive rework, basing work on the pike module instead of tcl
+ (pike module it only 1/3 of the size)(though not as complete)
+ * ver007
+ added simple type checking
+ * ver008
+ INPUT, OUTPUT, INOUT typemaps handled (though not all types yet)
+ * ver009
+ class support: ok for basic types, but methods still TDB
+ (code is VERY messed up & needs to be cleaned)
+ * ver010
+ Added support for embedded Lua. Try swig -lua -help for more information
+*/
+
+#include "swigmod.h"
+#include "cparse.h"
+
+/**** Diagnostics:
+ With the #define REPORT(), you can change the amount of diagnostics given
+ This helps me search the parse tree & figure out what is going on inside SWIG
+ (because it's not clear or documented)
+*/
+#define REPORT(T,D) // no info:
+//#define REPORT(T,D) {Printf(stdout,T"\n");} // only title
+//#define REPORT(T,D) {Printf(stdout,T" %p\n",n);} // title & pointer
+//#define REPORT(T,D) {Printf(stdout,T"\n");display_mapping(D);} // the works
+//#define REPORT(T,D) {Printf(stdout,T"\n");if(D)Swig_print_node(D);} // the works
+
+void display_mapping(DOH *d) {
+ if (d == 0 || !DohIsMapping(d))
+ return;
+ for (Iterator it = First(d); it.item; it = Next(it)) {
+ if (DohIsString(it.item))
+ Printf(stdout, " %s = %s\n", it.key, it.item);
+ else if (DohIsMapping(it.item))
+ Printf(stdout, " %s = <mapping>\n", it.key);
+ else if (DohIsSequence(it.item))
+ Printf(stdout, " %s = <sequence>\n", it.key);
+ else
+ Printf(stdout, " %s = <unknown>\n", it.key);
+ }
+}
+
+extern "C"
+{
+ static int compareByLen(const DOH *f, const DOH *s) {
+ return Len(s) - Len(f);
+ }
+}
+
+
+/* NEW LANGUAGE NOTE:***********************************************
+ most of the default options are handled by SWIG
+ you can add new ones here
+ (though for now I have not bothered)
+NEW LANGUAGE NOTE:END ************************************************/
+static const char *usage = "\
+Lua Options (available with -lua)\n\
+ -elua - Generates LTR compatible wrappers for smaller devices running elua\n\
+ -eluac - LTR compatible wrappers in \"crass compress\" mode for elua\n\
+ -elua-emulate - Emulates behaviour of eLua. Useful only for testing.\n\
+ Incompatible with -elua/-eluac options.\n\
+ -nomoduleglobal - Do not register the module name as a global variable \n\
+ but return the module table from calls to require.\n\
+ -no-old-metatable-bindings\n\
+ - Disable support for old-style bindings name generation, some\n\
+ old-style members scheme etc.\n\
+ -squash-bases - Squashes symbols from all inheritance tree of a given class\n\
+ into itself. Emulates pre-SWIG3.0 inheritance. Insignificantly\n\
+ speeds things up, but increases memory consumption.\n\
+\n";
+
+static int nomoduleglobal = 0;
+static int elua_ltr = 0;
+static int eluac_ltr = 0;
+static int elua_emulate = 0;
+static int squash_bases = 0;
+/* The new metatable bindings were introduced in SWIG 3.0.0.
+ * old_metatable_bindings in v2:
+ * 1. static methods will be put into the scope their respective class
+ * belongs to as well as into the class scope itself. (only for classes without %nspace given)
+ * 2. The layout in elua mode is somewhat different
+ */
+static int old_metatable_bindings = 1;
+static int old_compatible_names = 1; // This flag can temporarily disable backward compatible names generation if old_metatable_bindings is enabled
+
+/* NEW LANGUAGE NOTE:***********************************************
+ To add a new language, you need to derive your class from
+ Language and the overload various virtual functions
+ (more on this as I figure it out)
+NEW LANGUAGE NOTE:END ************************************************/
+
+class LUA:public Language {
+private:
+
+ File *f_begin;
+ File *f_runtime;
+ File *f_header;
+ File *f_wrappers;
+ File *f_init;
+ File *f_initbeforefunc;
+ String *s_luacode; // luacode to be called during init
+ String *module; //name of the module
+
+ // Parameters for current class. NIL if not parsing class
+ int have_constructor;
+ int have_destructor;
+ String *destructor_action;
+ // This variable holds the name of the current class in Lua. Usually it is
+ // the same as C++ class name, but rename directives can change it.
+ String *proxy_class_name;
+ // This is a so called fully qualified symname - the above proxy class name
+ // prepended with class namespace. If class Lua name is the same as class C++ name,
+ // then it is basically C++ fully qualified name with colons replaced with dots.
+ String *full_proxy_class_name;
+ // All static methods and/or variables are treated as if they were in the
+ // special C++ namespace $(classname).SwigStatic. This is internal mechanism only
+ // and is not visible to user in any manner. This variable holds the name
+ // of such pseudo-namespace a.k.a the result of above expression evaluation
+ String *class_static_nspace;
+ // This variable holds the name of generated C function that acts as a constructor
+ // for the currently parsed class.
+ String *constructor_name;
+
+ // Many wrappers forward calls to each other, for example staticmembervariableHandler
+ // forwards calls to variableHandler, which, in turn, makes to call to functionWrapper.
+ // In order to access information about whether it is a static member of class or just
+ // a plain old variable, the current array is kept and used as a 'log' of the call stack.
+ enum TState {
+ NO_CPP,
+ VARIABLE,
+ GLOBAL_FUNC,
+ GLOBAL_VAR,
+ MEMBER_FUNC,
+ CONSTRUCTOR,
+ DESTRUCTOR,
+ MEMBER_VAR,
+ STATIC_FUNC,
+ STATIC_VAR,
+ STATIC_CONST, // enums and things like static const int x = 5;
+ ENUM_CONST, // This is only needed for backward compatibility in C mode
+
+ STATES_COUNT
+ };
+ bool current[STATES_COUNT];
+
+public:
+
+ /* ---------------------------------------------------------------------
+ * LUA()
+ *
+ * Initialize member data
+ * --------------------------------------------------------------------- */
+
+ LUA():
+ f_begin(0),
+ f_runtime(0),
+ f_header(0),
+ f_wrappers(0),
+ f_init(0),
+ f_initbeforefunc(0),
+ s_luacode(0),
+ module(0),
+ have_constructor(0),
+ have_destructor(0),
+ destructor_action(0),
+ proxy_class_name(0),
+ full_proxy_class_name(0),
+ class_static_nspace(0),
+ constructor_name(0) {
+ for (int i = 0; i < STATES_COUNT; i++)
+ current[i] = false;
+ }
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ This is called to initialise the system & read any command line args
+ most of this is boilerplate code, except the command line args
+ which depends upon what args your code supports
+ NEW LANGUAGE NOTE:END *********************************************** */
+
+ /* ---------------------------------------------------------------------
+ * main()
+ *
+ * Parse command line options and initializes variables.
+ * --------------------------------------------------------------------- */
+
+ virtual void main(int argc, char *argv[]) {
+
+ /* Set location of SWIG library */
+ SWIG_library_directory("lua");
+
+ /* Look for certain command line options */
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-help") == 0) { // usage flags
+ fputs(usage, stdout);
+ } else if (strcmp(argv[i], "-nomoduleglobal") == 0) {
+ nomoduleglobal = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-elua") == 0) {
+ elua_ltr = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-eluac") == 0) {
+ eluac_ltr = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-no-old-metatable-bindings") == 0) {
+ Swig_mark_arg(i);
+ old_metatable_bindings = 0;
+ } else if (strcmp(argv[i], "-squash-bases") == 0) {
+ Swig_mark_arg(i);
+ squash_bases = 1;
+ } else if (strcmp(argv[i], "-elua-emulate") == 0) {
+ Swig_mark_arg(i);
+ elua_emulate = 1;
+ }
+ }
+ }
+
+ if (elua_emulate && (eluac_ltr || elua_ltr )) {
+ Printf(stderr, "Cannot have -elua-emulate with either -eluac or -elua\n");
+ Swig_arg_error();
+ }
+
+ // Set elua_ltr if elua_emulate is requested
+ if(elua_emulate)
+ elua_ltr = 1;
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ This is the boilerplate code, setting a few #defines
+ and which lib directory to use
+ the SWIG_library_directory() is also boilerplate code
+ but it always seems to be the first line of code
+ NEW LANGUAGE NOTE:END *********************************************** */
+ /* Add a symbol to the parser for conditional compilation */
+ Preprocessor_define("SWIGLUA 1", 0);
+
+ /* Set language-specific configuration file */
+ SWIG_config_file("lua.swg");
+
+ /* Set typemap language */
+ SWIG_typemap_lang("lua");
+
+ /* Enable overloaded methods support */
+ allow_overloading();
+ }
+
+
+
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ After calling main, SWIG parses the code to wrap (I believe)
+ then calls top()
+ in this is more boilerplate code to set everything up
+ and a call to Language::top()
+ which begins the code generations by calling the member fns
+ after all that is more boilerplate code to close all down
+ (overall there is virtually nothing here that needs to be edited
+ just use as is)
+ NEW LANGUAGE NOTE:END *********************************************** */
+ /* ---------------------------------------------------------------------
+ * top()
+ * --------------------------------------------------------------------- */
+
+ virtual int top(Node *n) {
+ /* Get the module name */
+ module = Getattr(n, "name");
+
+ /* Get the output file name */
+ String *outfile = Getattr(n, "outfile");
+
+ /* Open the output file */
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+ f_initbeforefunc = NewString("");
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("initbeforefunc", f_initbeforefunc);
+
+
+ s_luacode = NewString("");
+ Swig_register_filebyname("luacode", s_luacode);
+
+ current[NO_CPP] = true;
+
+ /* Standard stuff for the SWIG runtime section */
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "LUA");
+
+ emitLuaFlavor(f_runtime);
+
+ if (nomoduleglobal) {
+ Printf(f_runtime, "#define SWIG_LUA_NO_MODULE_GLOBAL\n");
+ } else {
+ Printf(f_runtime, "#define SWIG_LUA_MODULE_GLOBAL\n");
+ }
+ if (squash_bases)
+ Printf(f_runtime, "#define SWIG_LUA_SQUASH_BASES\n");
+
+ // if (NoInclude) {
+ // Printf(f_runtime, "#define SWIG_NOINCLUDE\n");
+ // }
+
+ Printf(f_runtime, "\n");
+
+ //String *init_name = NewStringf("%(title)s_Init", module);
+ //Printf(f_header, "#define SWIG_init %s\n", init_name);
+ //Printf(f_header, "#define SWIG_name \"%s\"\n", module);
+ /* SWIG_import is a special function name for importing within Lua5.1 */
+ //Printf(f_header, "#define SWIG_import luaopen_%s\n\n", module);
+ Printf(f_header, "#define SWIG_name \"%s\"\n", module);
+ Printf(f_header, "#define SWIG_init luaopen_%s\n", module);
+ Printf(f_header, "#define SWIG_init_user luaopen_%s_user\n\n", module);
+ Printf(f_header, "#define SWIG_LUACODE luaopen_%s_luacode\n", module);
+
+ Printf(f_wrappers, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
+
+ /* %init code inclusion, effectively in the SWIG_init function */
+ Printf(f_init, "void SWIG_init_user(lua_State* L)\n{\n");
+ Language::top(n);
+ Printf(f_init, "/* exec Lua code if applicable */\nSWIG_Lua_dostring(L,SWIG_LUACODE);\n");
+ Printf(f_init, "}\n");
+
+ // Done. Close up the module & write to the wrappers
+ closeNamespaces(f_wrappers);
+ Printf(f_wrappers, "#ifdef __cplusplus\n}\n#endif\n");
+
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ this basically combines several of the strings together
+ and then writes it all to a file
+ NEW LANGUAGE NOTE:END *********************************************** */
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+ Dump(f_wrappers, f_begin);
+ Dump(f_initbeforefunc, f_begin);
+ /* for the Lua code it needs to be properly escaped to be added into the C/C++ code */
+ escapeCode(s_luacode);
+ Printf(f_begin, "const char* SWIG_LUACODE=\n \"%s\";\n\n", s_luacode);
+ Wrapper_pretty_print(f_init, f_begin);
+ /* Close all of the files */
+ Delete(s_luacode);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_initbeforefunc);
+ Delete(f_runtime);
+ Delete(f_begin);
+
+ /* Done */
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * importDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int importDirective(Node *n) {
+ return Language::importDirective(n);
+ }
+
+ /* ------------------------------------------------------------
+ * cDeclaration()
+ * It copies sym:name to lua:name to preserve its original value
+ * ------------------------------------------------------------ */
+
+ virtual int cDeclaration(Node *n) {
+ // class 'Language' is messing with symname in a really heavy way.
+ // Although documentation states that sym:name is a name in
+ // the target language space, it is not true. sym:name and
+ // its derivatives are used in various places, including
+ // behind-the-scene C code generation. The best way is not to
+ // touch it at all.
+ // But we need to know what was the name of function/variable
+ // etc that user desired, that's why we store correct symname
+ // as lua:name
+ String *symname = Getattr(n, "sym:name");
+ if (symname)
+ Setattr(n, "lua:name", symname);
+ return Language::cDeclaration(n);
+ }
+ virtual int constructorDeclaration(Node *n) {
+ Setattr(n, "lua:name", Getattr(n, "sym:name"));
+ return Language::constructorDeclaration(n);
+ }
+ virtual int destructorDeclaration(Node *n) {
+ Setattr(n, "lua:name", Getattr(n, "sym:name"));
+ return Language::destructorDeclaration(n);
+ }
+ /* NEW LANGUAGE NOTE:***********************************************
+ This is it!
+ you get this one right, and most of your work is done
+ but it's going to take some file to get it working right
+ quite a bit of this is generally boilerplate code
+ (or stuff I don't understand)
+ that which matters will have extra added comments
+ NEW LANGUAGE NOTE:END *********************************************** */
+ /* ---------------------------------------------------------------------
+ * functionWrapper()
+ *
+ * Create a function declaration and register it with the interpreter.
+ * --------------------------------------------------------------------- */
+
+ /* -----------------------------------------------------------------------
+ * registerMethod()
+ *
+ * Determines wrap name of a method, its scope etc and calls
+ * registerMethod overload with correct arguments
+ * Overloaded variant adds method to the "methods" array of specified lua scope/class
+ * ---------------------------------------------------------------------- */
+
+ void registerMethod(Node *n, bool overwrite = false, String *overwriteLuaScope = 0) {
+ String *symname = Getattr(n, "sym:name");
+ assert(symname);
+
+ if (Getattr(n, "sym:nextSibling"))
+ return;
+
+ // Lua scope. It is not symbol NSpace, it is the actual key to retrieve getCArraysHash.
+ String *luaScope = luaCurrentSymbolNSpace();
+ if (overwrite)
+ luaScope = overwriteLuaScope;
+
+ String *wrapname = 0;
+ String *mrename;
+ if (current[NO_CPP] || !getCurrentClass()) {
+ mrename = symname;
+ } else {
+ assert(!current[NO_CPP]);
+ if (current[STATIC_FUNC] || current[MEMBER_FUNC]) {
+ mrename = Swig_name_member(getNSpace(), getClassPrefix(), symname);
+ } else {
+ mrename = symname;
+ }
+ }
+ wrapname = Swig_name_wrapper(mrename);
+ registerMethod(n, wrapname, luaScope);
+ }
+
+ /* -----------------------------------------------------------------------
+ * registerMethod()
+ *
+ * Add method to the "methods" C array of given namespace/class
+ * ---------------------------------------------------------------------- */
+
+ void registerMethod(Node *n, String* wname, String *luaScope) {
+ assert(n);
+ Hash *nspaceHash = getCArraysHash(luaScope);
+ String *s_ns_methods_tab = Getattr(nspaceHash, "methods");
+ String *lua_name = Getattr(n, "lua:name");
+ if (elua_ltr || eluac_ltr)
+ Printv(s_ns_methods_tab, tab4, "{LSTRKEY(\"", lua_name, "\")", ", LFUNCVAL(", wname, ")", "},\n", NIL);
+ else
+ Printv(s_ns_methods_tab, tab4, "{ \"", lua_name, "\", ", wname, "},\n", NIL);
+ // Add to the metatable if method starts with '__'
+ const char * tn = Char(lua_name);
+ if (tn[0]=='_' && tn[1] == '_' && !eluac_ltr) {
+ String *metatable_tab = Getattr(nspaceHash, "metatable");
+ assert(metatable_tab);
+ if (elua_ltr)
+ Printv(metatable_tab, tab4, "{LSTRKEY(\"", lua_name, "\")", ", LFUNCVAL(", wname, ")", "},\n", NIL);
+ else
+ Printv(metatable_tab, tab4, "{ \"", lua_name, "\", ", wname, "},\n", NIL);
+ }
+ }
+
+ virtual int functionWrapper(Node *n) {
+ REPORT("functionWrapper", n);
+
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ String *lua_name = Getattr(n, "lua:name");
+ assert(lua_name);
+ SwigType *d = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ Parm *p;
+ String *tm;
+ int i;
+ //Printf(stdout,"functionWrapper %s %s %d\n",name,iname,current);
+
+ String *overname = 0;
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ } else {
+ if (!luaAddSymbol(lua_name, n)) {
+ return SWIG_ERROR;
+ }
+ }
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ the wrapper object holds all the wrapper code
+ we need to add a couple of local variables
+ NEW LANGUAGE NOTE:END *********************************************** */
+ Wrapper *f = NewWrapper();
+ Wrapper_add_local(f, "SWIG_arg", "int SWIG_arg = 0");
+
+
+ String *wname = Swig_name_wrapper(iname);
+ if (overname) {
+ Append(wname, overname);
+ }
+ if (current[CONSTRUCTOR]) {
+ if (constructor_name != 0)
+ Delete(constructor_name);
+ constructor_name = Copy(wname);
+ }
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ the format of a lua fn is:
+ static int wrap_XXX(lua_State* L){...}
+ this line adds this into the wrapper code
+ NEW LANGUAGE NOTE:END *********************************************** */
+ Printv(f->def, "static int ", wname, "(lua_State* L) {", NIL);
+ // SWIG_fail in lua leads to a call to lua_error() which calls longjmp()
+ // which means the destructors of any live function-local C++ objects won't
+ // get run. To avoid this happening, we wrap almost everything in the
+ // function in a block, and end that right before lua_error() at which
+ // point those destructors will get called.
+ if (CPlusPlus) Append(f->def, "\n{");
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ this prints the list of args, eg for a C fn
+ int gcd(int x,int y);
+ it will print
+ int arg1;
+ int arg2;
+ NEW LANGUAGE NOTE:END *********************************************** */
+ /* Write code to extract function parameters. */
+ emit_parameter_variables(l, f);
+
+ /* Attach the standard typemaps */
+ emit_attach_parmmaps(l, f);
+ Setattr(n, "wrap:parms", l);
+
+ /* Get number of required and total arguments */
+ int num_arguments = emit_num_arguments(l);
+ int num_required = emit_num_required(l);
+ int varargs = emit_isvarargs(l);
+
+ // Check if we have to ignore arguments that are passed by LUA.
+ // Needed for unary minus, where lua passes two arguments and
+ // we have to ignore the second.
+
+ int args_to_ignore = 0;
+ if (Getattr(n, "lua:ignore_args")) {
+ args_to_ignore = GetInt(n, "lua:ignore_args");
+ }
+
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ from here on in, it gets rather hairy
+ this is the code to convert from the scripting language to C/C++
+ some of the stuff will refer to the typemaps code written in your swig file
+ (lua.swg), and some is done in the code here
+ I suppose you could do all the conversion in C, but it would be a nightmare to do
+ NEW LANGUAGE NOTE:END *********************************************** */
+ /* Generate code for argument marshalling */
+ // String *description = NewString("");
+ /* NEW LANGUAGE NOTE:***********************************************
+ argument_check is a new feature I added to check types of arguments:
+ eg for int gcd(int,int)
+ I want to check that arg1 & arg2 really are integers
+ NEW LANGUAGE NOTE:END *********************************************** */
+ String *argument_check = NewString("");
+ String *argument_parse = NewString("");
+ String *checkfn = NULL;
+ char source[64];
+ Printf(argument_check, "SWIG_check_num_args(\"%s\",%d,%d)\n", Swig_name_str(n), num_required + args_to_ignore, num_arguments + args_to_ignore);
+
+ for (i = 0, p = l; i < num_arguments; i++) {
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ /* Look for an input typemap */
+ sprintf(source, "%d", i + 1);
+ if ((tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source);
+ if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+ /* NEW LANGUAGE NOTE:***********************************************
+ look for a 'checkfn' typemap
+ this an additional parameter added to the in typemap
+ if found the type will be tested for
+ this will result in code either in the
+ argument_check or argument_parse string
+ NEW LANGUAGE NOTE:END *********************************************** */
+ if ((checkfn = Getattr(p, "tmap:in:checkfn"))) {
+ if (i < num_required) {
+ Printf(argument_check, "if(!%s(L,%s))", checkfn, source);
+ } else {
+ Printf(argument_check, "if(lua_gettop(L)>=%s && !%s(L,%s))", source, checkfn, source);
+ }
+ Printf(argument_check, " SWIG_fail_arg(\"%s\",%s,\"%s\");\n", Swig_name_str(n), source, SwigType_str(pt, 0));
+ }
+ /* NEW LANGUAGE NOTE:***********************************************
+ lua states the number of arguments passed to a function using the fn
+ lua_gettop()
+ we can use this to deal with default arguments
+ NEW LANGUAGE NOTE:END *********************************************** */
+ if (i < num_required) {
+ Printf(argument_parse, "%s\n", tm);
+ } else {
+ Printf(argument_parse, "if(lua_gettop(L)>=%s){%s}\n", source, tm);
+ }
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ } else {
+ /* NEW LANGUAGE NOTE:***********************************************
+ // why is this code not called when I don't have a typemap?
+ // instead of giving a warning, no code is generated
+ NEW LANGUAGE NOTE:END *********************************************** */
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ break;
+ }
+ }
+
+ // add all argcheck code
+ Printv(f->code, argument_check, argument_parse, NIL);
+
+ /* Check for trailing varargs */
+ if (varargs) {
+ if (p && (tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", "varargs");
+ Printv(f->code, tm, "\n", NIL);
+ }
+ }
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ String *cleanup = NewString("");
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert argument output code */
+ String *outarg = NewString("");
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ // // managing the number of returning variables
+ // if (numoutputs=Getattr(p,"tmap:argout:numoutputs")){
+ // int i=GetInt(p,"tmap:argout:numoutputs");
+ // printf("got argout:numoutputs of %d\n",i);
+ // returnval+=GetInt(p,"tmap:argout:numoutputs");
+ // }
+ // else returnval++;
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Remember C name of the wrapping function
+ Setattr(n, "wrap:name", wname);
+
+ /* Emit the function call */
+ String *actioncode = emit_action(n);
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ FIXME:
+ returns 1 if there is a void return type
+ this is because there is a typemap for void
+ NEW LANGUAGE NOTE:END *********************************************** */
+ // Return value if necessary
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ // managing the number of returning variables
+ // if (numoutputs=Getattr(tm,"numoutputs")){
+ // int i=GetInt(tm,"numoutputs");
+ // printf("return numoutputs %d\n",i);
+ // returnval+=GetInt(tm,"numoutputs");
+ // }
+ // else returnval++;
+ if (GetFlag(n, "feature:new")) {
+ Replaceall(tm, "$owner", "1");
+ } else {
+ Replaceall(tm, "$owner", "0");
+ }
+ Printf(f->code, "%s\n", tm);
+ // returnval++;
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(d, 0), name);
+ }
+ emit_return_variable(n, d, f);
+
+ /* Output argument output code */
+ Printv(f->code, outarg, NIL);
+
+ /* Output cleanup code */
+ Printv(f->code, cleanup, NIL);
+
+ /* Look to see if there is any newfree cleanup code */
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ }
+
+
+ /* Close the function */
+ Printv(f->code, "return SWIG_arg;\n", NIL);
+ // add the failure cleanup code:
+ Printv(f->code, "\nfail: SWIGUNUSED;\n", "$cleanup", NIL);
+ if (CPlusPlus) Append(f->code, "}\n");
+ Printv(f->code, "lua_error(L);\n", NIL);
+ // lua_error() calls longjmp() but we need a dummy return to avoid compiler
+ // warnings.
+ Printv(f->code, "return 0;\n", NIL);
+ Printf(f->code, "}\n");
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", iname);
+ Replaceall(f->code, "$result", Swig_cresult_name());
+
+ /* Dump the function out */
+ /* in Lua we will not emit the destructor as a wrapper function,
+ Lua will automatically call the destructor when the object is free'd
+ However: you cannot just skip this function as it will not emit
+ any custom destructor (using %extend), as you need to call emit_action()
+ Therefore we go though the whole function,
+ but do not write the code into the wrapper
+ */
+ if (!current[DESTRUCTOR]) {
+ Wrapper_print(f, f_wrappers);
+ }
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ register the function in SWIG
+ different language mappings seem to use different ideas
+ NEW LANGUAGE NOTE:END *********************************************** */
+ /* Now register the function with the interpreter. */
+ int result = SWIG_OK;
+ if (Getattr(n, "sym:overloaded")) {
+ if (!Getattr(n, "sym:nextSibling")) {
+ result = dispatchFunction(n);
+ }
+ }
+
+ Delete(argument_check);
+ Delete(argument_parse);
+
+ Delete(cleanup);
+ Delete(outarg);
+ // Delete(description);
+ DelWrapper(f);
+
+ return result;
+ }
+
+ /* ------------------------------------------------------------
+ * dispatchFunction()
+ *
+ * Emit overloading dispatch function
+ * ------------------------------------------------------------ */
+
+ /* NEW LANGUAGE NOTE:***********************************************
+ This is an extra function used for overloading of functions
+ it checks the args & then calls the relevant fn
+ most of the real work in again typemaps:
+ look for %typecheck(SWIG_TYPECHECK_*) in the .swg file
+ NEW LANGUAGE NOTE:END *********************************************** */
+ int dispatchFunction(Node *n) {
+ //REPORT("dispatchFunction", n);
+ /* Last node in overloaded chain */
+
+ int maxargs;
+ String *tmp = NewString("");
+ String *dispatch = Swig_overload_dispatch(n, "return %s(L);", &maxargs);
+
+ /* Generate a dispatch wrapper for all overloaded functions */
+
+ Wrapper *f = NewWrapper();
+ String *symname = Getattr(n, "sym:name");
+ String *lua_name = Getattr(n, "lua:name");
+ assert(lua_name);
+ String *wname = Swig_name_wrapper(symname);
+
+ //Printf(stdout,"Swig_overload_dispatch %s %s '%s' %d\n",symname,wname,dispatch,maxargs);
+
+ if (!luaAddSymbol(lua_name, n)) {
+ DelWrapper(f);
+ Delete(dispatch);
+ Delete(tmp);
+ return SWIG_ERROR;
+ }
+
+ Printv(f->def, "static int ", wname, "(lua_State* L) {", NIL);
+ Wrapper_add_local(f, "argc", "int argc");
+ Printf(tmp, "int argv[%d]={1", maxargs + 1);
+ for (int i = 1; i <= maxargs; i++) {
+ Printf(tmp, ",%d", i + 1);
+ }
+ Printf(tmp, "}");
+ Wrapper_add_local(f, "argv", tmp);
+ Printf(f->code, "argc = lua_gettop(L);\n");
+
+ Replaceall(dispatch, "$args", "self,args");
+ Printv(f->code, dispatch, "\n", NIL);
+
+ Node *sibl = n;
+ while (Getattr(sibl, "sym:previousSibling"))
+ sibl = Getattr(sibl, "sym:previousSibling"); // go all the way up
+ String *protoTypes = NewString("");
+ do {
+ String *fulldecl = Swig_name_decl(sibl);
+ Printf(protoTypes, "\n\" %s\\n\"", fulldecl);
+ Delete(fulldecl);
+ } while ((sibl = Getattr(sibl, "sym:nextSibling")));
+ Printf(f->code, "SWIG_Lua_pusherrstring(L,\"Wrong arguments for overloaded function '%s'\\n\"\n"
+ "\" Possible C/C++ prototypes are:\\n\"%s);\n", symname, protoTypes);
+ Delete(protoTypes);
+
+ Printf(f->code, "lua_error(L);return 0;\n");
+ Printv(f->code, "}\n", NIL);
+ Wrapper_print(f, f_wrappers);
+
+ // Remember C name of the wrapping function
+ Setattr(n, "wrap:name", wname);
+
+ if (current[CONSTRUCTOR]) {
+ if (constructor_name != 0)
+ Delete(constructor_name);
+ constructor_name = Copy(wname);
+ }
+
+ DelWrapper(f);
+ Delete(dispatch);
+ Delete(tmp);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * Add variable to "attributes" C arrays of given namespace or class.
+ * Input is node. Based on the state of "current" array it determines
+ * the name of the getter function, setter function etc and calls
+ * registerVariable overload with necessary params.
+ * Lua scope could be overwritten. (Used only for backward compatibility)
+ * ------------------------------------------------------------ */
+
+ void registerVariable(Node *n, bool overwrite = false, String *overwriteLuaScope = 0) {
+ int assignable = is_assignable(n);
+ String *symname = Getattr(n, "sym:name");
+ assert(symname);
+
+ // Lua scope. It is not symbol NSpace, it is the actual key to retrieve getCArraysHash.
+ String *luaScope = luaCurrentSymbolNSpace();
+ if (overwrite)
+ luaScope = overwriteLuaScope;
+
+ // Getter and setter
+ String *getName = 0;
+ String *setName = 0;
+ String *mrename = 0;
+ if (current[NO_CPP] || !getCurrentClass()) {
+ // Global variable
+ getName = Swig_name_get(getNSpace(), symname);
+ if (assignable)
+ setName = Swig_name_set(getNSpace(), symname);
+ } else {
+ assert(!current[NO_CPP]);
+ if (current[STATIC_VAR] ) {
+ mrename = Swig_name_member(getNSpace(), getClassPrefix(), symname);
+ getName = Swig_name_get(0, mrename);
+ if (assignable)
+ setName = Swig_name_set(0, mrename);
+ } else if (current[MEMBER_VAR]) {
+ mrename = Swig_name_member(0, getClassPrefix(), symname);
+ getName = Swig_name_get(getNSpace(), mrename);
+ if (assignable)
+ setName = Swig_name_set(getNSpace(), mrename);
+ } else {
+ assert(false);
+ }
+ }
+
+ getName = Swig_name_wrapper(getName);
+ if (setName)
+ setName = Swig_name_wrapper(setName);
+ registerVariable(luaScope, n, getName, setName);
+ }
+
+ /* ------------------------------------------------------------
+ * registerVariable()
+ *
+ * Add variable to the "attributes" (or "get"/"set" in
+ * case of elua_ltr) C arrays of given namespace or class
+ * ------------------------------------------------------------ */
+
+ void registerVariable(String *lua_nspace_or_class_name, Node *n, String *getName, String *setName) {
+ String *unassignable = NewString("SWIG_Lua_set_immutable");
+ if (setName == 0 || GetFlag(n, "feature:immutable")) {
+ setName = unassignable;
+ }
+ Hash *nspaceHash = getCArraysHash(lua_nspace_or_class_name);
+ String *s_ns_methods_tab = Getattr(nspaceHash, "methods");
+ String *s_ns_var_tab = Getattr(nspaceHash, "attributes");
+ String *lua_name = Getattr(n, "lua:name");
+ if (elua_ltr) {
+ String *s_ns_dot_get = Getattr(nspaceHash, "get");
+ String *s_ns_dot_set = Getattr(nspaceHash, "set");
+ Printf(s_ns_dot_get, "%s{LSTRKEY(\"%s\"), LFUNCVAL(%s)},\n", tab4, lua_name, getName);
+ Printf(s_ns_dot_set, "%s{LSTRKEY(\"%s\"), LFUNCVAL(%s)},\n", tab4, lua_name, setName);
+ } else if (eluac_ltr) {
+ Printv(s_ns_methods_tab, tab4, "{LSTRKEY(\"", lua_name, "_get", "\")", ", LFUNCVAL(", getName, ")", "},\n", NIL);
+ Printv(s_ns_methods_tab, tab4, "{LSTRKEY(\"", lua_name, "_set", "\")", ", LFUNCVAL(", setName, ")", "},\n", NIL);
+ } else {
+ Printf(s_ns_var_tab, "%s{ \"%s\", %s, %s },\n", tab4, lua_name, getName, setName);
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * variableWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int variableWrapper(Node *n) {
+ /* NEW LANGUAGE NOTE:***********************************************
+ Language::variableWrapper(n) will generate two wrapper fns
+ Foo_get & Foo_set by calling functionWrapper()
+ so we will just add these into the variable lists
+ ideally we should not have registered these as functions,
+ only WRT this variable will look into this later.
+ NEW LANGUAGE NOTE:END *********************************************** */
+ // REPORT("variableWrapper", n);
+ String *lua_name = Getattr(n, "lua:name");
+ assert(lua_name);
+ (void)lua_name;
+ current[VARIABLE] = true;
+ // let SWIG generate the wrappers
+ int result = Language::variableWrapper(n);
+
+ // It is impossible to use registerVariable, because sym:name of the Node is currently
+ // in an undefined state - the callees of this function may have modified it.
+ // registerVariable should be used from respective callees.*
+ current[VARIABLE] = false;
+ return result;
+ }
+
+
+ /* ------------------------------------------------------------
+ * Add constant to appropriate C array. constantRecord is an array record.
+ * Actually, in current implementation it is resolved consttab typemap
+ * ------------------------------------------------------------ */
+
+ void registerConstant(String *nspace, String *constantRecord) {
+ Hash *nspaceHash = getCArraysHash(nspace);
+ String *s_const_tab = 0;
+ if (eluac_ltr || elua_ltr)
+ // In elua everything goes to "methods" tab
+ s_const_tab = Getattr(nspaceHash, "methods");
+ else
+ s_const_tab = Getattr(nspaceHash, "constants");
+
+ assert(s_const_tab);
+ Printf(s_const_tab, " %s,\n", constantRecord);
+
+ if ((eluac_ltr || elua_ltr) && old_metatable_bindings) {
+ s_const_tab = Getattr(nspaceHash, "constants");
+ assert(s_const_tab);
+ Printf(s_const_tab, " %s,\n", constantRecord);
+ }
+
+ }
+
+ /* ------------------------------------------------------------
+ * constantWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ REPORT("constantWrapper", n);
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ String *lua_name = Getattr(n, "lua:name");
+ if (lua_name == 0)
+ lua_name = iname;
+ String *nsname = Copy(iname);
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ String *tm;
+ String *lua_name_v2 = 0;
+ String *tm_v2 = 0;
+ String *iname_v2 = 0;
+ Node *n_v2 = 0;
+
+ if (!luaAddSymbol(lua_name, n))
+ return SWIG_ERROR;
+
+ Swig_save("lua_constantMember", n, "sym:name", NIL);
+ Setattr(n, "sym:name", lua_name);
+ /* Special hook for member pointer */
+ if (SwigType_type(type) == T_MPOINTER) {
+ String *wname = Swig_name_wrapper(iname);
+ Printf(f_wrappers, "static %s = %s;\n", SwigType_str(type, wname), value);
+ value = Char(wname);
+ }
+
+ if ((tm = Swig_typemap_lookup("consttab", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ Replaceall(tm, "$nsname", nsname);
+ registerConstant(luaCurrentSymbolNSpace(), tm);
+ } else if ((tm = Swig_typemap_lookup("constcode", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ Replaceall(tm, "$nsname", nsname);
+ Printf(f_init, "%s\n", tm);
+ } else {
+ Delete(nsname);
+ nsname = 0;
+ Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value.\n");
+ Swig_restore(n);
+ return SWIG_NOWRAP;
+ }
+
+ bool make_v2_compatible = old_metatable_bindings && getCurrentClass() && old_compatible_names;
+
+ if (make_v2_compatible) {
+ // Don't do anything for enums in C mode - they are already
+ // wrapped correctly
+ if (CPlusPlus || !current[ENUM_CONST]) {
+ lua_name_v2 = Swig_name_member(0, proxy_class_name, lua_name);
+ iname_v2 = Swig_name_member(0, proxy_class_name, iname);
+ n_v2 = Copy(n);
+ if (!luaAddSymbol(iname_v2, n, getNSpace())) {
+ Swig_restore(n);
+ return SWIG_ERROR;
+ }
+
+ Setattr(n_v2, "sym:name", lua_name_v2);
+ tm_v2 = Swig_typemap_lookup("consttab", n_v2, name, 0);
+ if (tm_v2) {
+ Replaceall(tm_v2, "$value", value);
+ Replaceall(tm_v2, "$nsname", nsname);
+ registerConstant(getNSpace(), tm_v2);
+ } else {
+ tm_v2 = Swig_typemap_lookup("constcode", n_v2, name, 0);
+ if (!tm_v2) {
+ // This can't be.
+ assert(false);
+ Swig_restore(n);
+ return SWIG_ERROR;
+ }
+ Replaceall(tm_v2, "$value", value);
+ Replaceall(tm_v2, "$nsname", nsname);
+ Printf(f_init, "%s\n", tm_v2);
+ }
+ Delete(n_v2);
+ }
+ }
+
+ Swig_restore(n);
+ Delete(nsname);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * nativeWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int nativeWrapper(Node *n) {
+ // REPORT("nativeWrapper", n);
+ String *symname = Getattr(n, "sym:name");
+ String *wrapname = Getattr(n, "wrap:name");
+ if (!luaAddSymbol(wrapname, n))
+ return SWIG_ERROR;
+
+ Hash *nspaceHash = getCArraysHash(getNSpace());
+ String *s_ns_methods_tab = Getattr(nspaceHash, "methods");
+ Printv(s_ns_methods_tab, tab4, "{ \"", symname, "\",", wrapname, "},\n", NIL);
+ // return Language::nativeWrapper(n); // this does nothing...
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * enumDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int enumDeclaration(Node *n) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ current[STATIC_CONST] = true;
+ current[ENUM_CONST] = true;
+ // There is some slightly specific behaviour with enums. Basically,
+ // their NSpace may be tracked separately. The code below tries to work around
+ // this issue to some degree.
+ // The idea is the same as in classHandler - to drop old names generation if
+ // enum is in class in namespace.
+ const int old_compatible_names_saved = old_compatible_names;
+ if (getNSpace() || ( Getattr(n, "sym:nspace") != 0 && Len(Getattr(n, "sym:nspace")) > 0 ) ) {
+ old_compatible_names = 0;
+ }
+ int result = Language::enumDeclaration(n);
+ current[STATIC_CONST] = false;
+ current[ENUM_CONST] = false;
+ old_compatible_names = old_compatible_names_saved;
+ return result;
+ }
+
+ /* ------------------------------------------------------------
+ * enumvalueDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int enumvalueDeclaration(Node *n) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ Swig_require("enumvalueDeclaration", n, "*name", "?value", "*sym:name", NIL);
+ String *symname = Getattr(n, "sym:name");
+ String *value = Getattr(n, "value");
+ String *name = Getattr(n, "name");
+ String *tmpValue;
+ Node *parent = parentNode(n);
+
+ if (value)
+ tmpValue = NewString(value);
+ else
+ tmpValue = NewString(name);
+ Setattr(n, "value", tmpValue);
+
+ Setattr(n, "name", tmpValue); /* for wrapping of enums in a namespace when emit_action is used */
+
+ if (GetFlag(parent, "scopedenum")) {
+ symname = Swig_name_member(0, Getattr(parent, "sym:name"), symname);
+ Setattr(n, "sym:name", symname);
+ Delete(symname);
+ }
+
+ int result = constantWrapper(n);
+
+ Delete(tmpValue);
+ Swig_restore(n);
+ return result;
+ }
+
+ /* ------------------------------------------------------------
+ * classDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int classDeclaration(Node *n) {
+ return Language::classDeclaration(n);
+ }
+
+
+ /* ------------------------------------------------------------
+ * Helper function that adds record to appropriate C arrays
+ * ------------------------------------------------------------ */
+
+ void registerClass(String *scope, String *wrap_class) {
+ assert(wrap_class);
+ Hash *nspaceHash = getCArraysHash(scope);
+ String *ns_classes = Getattr(nspaceHash, "classes");
+ Printv(ns_classes, "&", wrap_class, ",\n", NIL);
+ if (elua_ltr || eluac_ltr) {
+ String *ns_methods = Getattr(nspaceHash, "methods");
+ Hash *class_hash = getCArraysHash(class_static_nspace);
+ assert(class_hash);
+ String *cls_methods = Getattr(class_hash, "methods:name");
+ assert(cls_methods);
+ Printv(ns_methods, tab4, "{LSTRKEY(\"", proxy_class_name, "\")", ", LROVAL(", cls_methods, ")", "},\n", NIL);
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * classHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int classHandler(Node *n) {
+ //REPORT("classHandler", n);
+
+ String *mangled_full_proxy_class_name = 0;
+ String *destructor_name = 0;
+ String *nspace = getNSpace();
+
+ constructor_name = 0;
+ have_constructor = 0;
+ have_destructor = 0;
+ destructor_action = 0;
+ assert(class_static_nspace == 0);
+ assert(full_proxy_class_name == 0);
+ assert(proxy_class_name == 0);
+
+ current[NO_CPP] = false;
+
+ proxy_class_name = Getattr(n, "sym:name");
+ // We have to enforce nspace here, because technically we are already
+ // inside class parsing (getCurrentClass != 0), but we should register
+ // class in its parent namespace
+ if (!luaAddSymbol(proxy_class_name, n, nspace))
+ return SWIG_ERROR;
+
+ if (nspace == 0)
+ full_proxy_class_name = NewStringf("%s", proxy_class_name);
+ else
+ full_proxy_class_name = NewStringf("%s.%s", nspace, proxy_class_name);
+
+ assert(full_proxy_class_name);
+ mangled_full_proxy_class_name = Swig_name_mangle(full_proxy_class_name);
+
+ SwigType *t = Copy(Getattr(n, "name"));
+ SwigType *fr_t = SwigType_typedef_resolve_all(t); /* Create fully resolved type */
+ SwigType *t_tmp = 0;
+ t_tmp = SwigType_typedef_qualified(fr_t); // Temporal variable
+ Delete(fr_t);
+ fr_t = SwigType_strip_qualifiers(t_tmp);
+ String *mangled_fr_t = 0;
+ mangled_fr_t = SwigType_manglestr(fr_t);
+ // not sure exactly how this works,
+ // but tcl has a static hashtable of all classes emitted and then only emits code for them once.
+ // this fixes issues in test suites: template_default2 & template_specialization
+
+ // * if i understand correctly, this is a bug.
+ // * consider effect on template_specialization_defarg
+
+ static Hash *emitted = NewHash();
+ if (GetFlag(emitted, mangled_fr_t)) {
+ full_proxy_class_name = 0;
+ proxy_class_name = 0;
+ return SWIG_NOWRAP;
+ }
+ SetFlag(emitted, mangled_fr_t);
+
+ // We treat class T as both 'class' and 'namespace'. All static members, attributes
+ // and constants are considered part of namespace T, all members - part of the 'class'
+ // Now, here is a trick. Static methods, attributes and non-static methods and attributes
+ // are described with same structures - swig_lua_attribute/swig_lua_method. Instead of calling
+ // getCArraysHash(class name) to initialize things for static methods/attributes and then
+ // manually doing same initialization for non-static methods, we call getCArraysHash 2 times:
+ // 1) With name "class name" + "." + "SwigStatic" to initialize static things
+ // 2) With "class name" to initialize non-static things
+ Hash *instance_cls = getCArraysHash(full_proxy_class_name, false);
+ assert(instance_cls);
+ String *s_attr_tab_name = Getattr(instance_cls, "attributes:name");
+ String *s_methods_tab_name = Getattr(instance_cls, "methods:name");
+ SetFlag(instance_cls, "lua:no_namespaces");
+ SetFlag(instance_cls, "lua:no_classes");
+ SetFlag(instance_cls, "lua:class_instance");
+
+ /* There is no use for "constants", "classes" and "namespaces" arrays.
+ * All constants are considered part of static part of class.
+ */
+
+ class_static_nspace = NewStringf("%s%sSwigStatic", full_proxy_class_name, NSPACE_SEPARATOR);
+ Hash *static_cls = getCArraysHash(class_static_nspace, false);
+ assert(static_cls);
+ SetFlag(static_cls, "lua:no_namespaces");
+ SetFlag(static_cls, "lua:class_static");
+
+ // Notifying instance_cls and static_cls hashes about each other
+ Setattr(instance_cls, "lua:class_instance:static_hash", static_cls);
+ Setattr(static_cls, "lua:class_static:instance_hash", instance_cls);
+
+ const int old_compatible_names_saved = old_compatible_names;
+ // If class has %nspace enabled, then generation of backward compatible names
+ // should be disabled
+ if (getNSpace()) {
+ old_compatible_names = 0;
+ }
+
+ /* There is no use for "classes" and "namespaces" arrays. Subclasses are not supported
+ * by SWIG and namespaces couldn't be nested inside classes (C++ Standard)
+ */
+ // Generate normal wrappers
+ Language::classHandler(n);
+
+ old_compatible_names = old_compatible_names_saved;
+
+ SwigType_add_pointer(t);
+
+ // Catch all: eg. a class with only static functions and/or variables will not have 'remembered'
+ String *wrap_class_name = Swig_name_wrapper(NewStringf("class_%s", mangled_full_proxy_class_name));
+ String *wrap_class = NewStringf("&%s", wrap_class_name);
+ SwigType_remember_clientdata(t, wrap_class);
+
+ String *rt = Copy(getClassType());
+ SwigType_add_pointer(rt);
+
+ // Adding class to appropriate namespace
+ registerClass(nspace, wrap_class_name);
+ Hash *nspaceHash = getCArraysHash(nspace);
+
+ // Register the class structure with the type checker
+ // Printf(f_init,"SWIG_TypeClientData(SWIGTYPE%s, (void *) &_wrap_class_%s);\n", SwigType_manglestr(t), mangled_full_proxy_class_name);
+
+ // emit a function to be called to delete the object
+ if (have_destructor) {
+ destructor_name = NewStringf("swig_delete_%s", mangled_full_proxy_class_name);
+ Printv(f_wrappers, "static void ", destructor_name, "(void *obj) {\n", NIL);
+ if (destructor_action) {
+ Printv(f_wrappers, SwigType_str(rt, "arg1"), " = (", SwigType_str(rt, 0), ") obj;\n", NIL);
+ Printv(f_wrappers, destructor_action, "\n", NIL);
+ } else {
+ if (CPlusPlus) {
+ Printv(f_wrappers, " delete (", SwigType_str(rt, 0), ") obj;\n", NIL);
+ } else {
+ Printv(f_wrappers, " free((char *) obj);\n", NIL);
+ }
+ }
+ Printf(f_wrappers, "}\n");
+ }
+ // Wrap constructor wrapper into one more proxy function. It will be used as class namespace __call method, thus
+ // allowing both
+ // Module.ClassName.StaticMethod to access static method/variable/constant
+ // Module.ClassName() to create new object
+ if (have_constructor) {
+ assert(constructor_name);
+ String *constructor_proxy_name = NewStringf("_proxy_%s", constructor_name);
+ Printv(f_wrappers, "static int ", constructor_proxy_name, "(lua_State *L) {\n", NIL);
+ Printv(f_wrappers,
+ tab4, "assert(lua_istable(L,1));\n",
+ tab4, "lua_pushcfunction(L,", constructor_name, ");\n",
+ tab4, "assert(!lua_isnil(L,-1));\n",
+ tab4, "lua_replace(L,1); /* replace our table with real constructor */\n",
+ tab4, "lua_call(L,lua_gettop(L)-1,1);\n",
+ tab4, "return 1;\n}\n", NIL);
+ Delete(constructor_name);
+ constructor_name = constructor_proxy_name;
+ if (elua_ltr) {
+ String *static_cls_metatable_tab = Getattr(static_cls, "metatable");
+ assert(static_cls_metatable_tab);
+ Printf(static_cls_metatable_tab, " {LSTRKEY(\"__call\"), LFUNCVAL(%s)},\n", constructor_name);
+ } else if (eluac_ltr) {
+ String *ns_methods_tab = Getattr(nspaceHash, "methods");
+ assert(ns_methods_tab);
+ Printv(ns_methods_tab, tab4, "{LSTRKEY(\"", "new_", proxy_class_name, "\")", ", LFUNCVAL(", constructor_name, ")", "},\n", NIL);
+ }
+ }
+ if (have_destructor) {
+ if (eluac_ltr) {
+ String *ns_methods_tab = Getattr(nspaceHash, "methods");
+ assert(ns_methods_tab);
+ Printv(ns_methods_tab, tab4, "{LSTRKEY(\"", "free_", mangled_full_proxy_class_name, "\")", ", LFUNCVAL(", destructor_name, ")", "},\n", NIL);
+ }
+ }
+
+ closeCArraysHash(full_proxy_class_name, f_wrappers);
+ closeCArraysHash(class_static_nspace, f_wrappers);
+
+
+ // Handle inheritance
+ // note: with the idea of class hierarchies spread over multiple modules
+ // cf test-suite: imports.i
+ // it is not possible to just add the pointers to the base classes to the code
+ // (as sometimes these classes are not present)
+ // therefore we instead hold the name of the base class and a null pointer
+ // at runtime: we can query the swig type manager & see if the class exists
+ // if so, we can get the pointer to the base class & replace the null pointer
+ // if the type does not exist, then we cannot...
+ String *base_class = NewString("");
+ String *base_class_names = NewString("");
+
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator b;
+ b = First(baselist);
+ while (b.item) {
+ String *bname = Getattr(b.item, "name");
+ if ((!bname) || GetFlag(b.item, "feature:ignore") || (!Getattr(b.item, "module"))) {
+ b = Next(b);
+ continue;
+ }
+ // stores a null pointer & the name
+ Printf(base_class, "0,");
+ Printf(base_class_names, "\"%s *\",", SwigType_namestr(bname));
+
+ b = Next(b);
+ }
+ }
+ // First, print class static part
+ printCArraysDefinition(class_static_nspace, proxy_class_name, f_wrappers);
+
+ assert(mangled_full_proxy_class_name);
+ assert(base_class);
+ assert(base_class_names);
+ assert(proxy_class_name);
+ assert(full_proxy_class_name);
+
+ // Then print class instance part
+ Printv(f_wrappers, "static swig_lua_class *swig_", mangled_full_proxy_class_name, "_bases[] = {", base_class, "0};\n", NIL);
+ Delete(base_class);
+ Printv(f_wrappers, "static const char *swig_", mangled_full_proxy_class_name, "_base_names[] = {", base_class_names, "0};\n", NIL);
+ Delete(base_class_names);
+
+ Printv(f_wrappers, "static swig_lua_class _wrap_class_", mangled_full_proxy_class_name, " = { \"", proxy_class_name, "\", \"", full_proxy_class_name, "\", &SWIGTYPE",
+ SwigType_manglestr(t), ",", NIL);
+
+ if (have_constructor) {
+ Printv(f_wrappers, constructor_name, NIL);
+ Delete(constructor_name);
+ constructor_name = 0;
+ } else {
+ Printf(f_wrappers, "0");
+ }
+
+ if (have_destructor) {
+ Printv(f_wrappers, ", ", destructor_name, NIL);
+ } else {
+ Printf(f_wrappers, ",0");
+ }
+ Printf(f_wrappers, ", %s, %s, &%s", s_methods_tab_name, s_attr_tab_name, Getattr(static_cls, "cname"));
+
+ if (!eluac_ltr) {
+ Printf(f_wrappers, ", %s", Getattr(instance_cls,"metatable:name"));
+ }
+ else
+ Printf(f_wrappers, ", 0");
+
+ Printf(f_wrappers, ", swig_%s_bases, swig_%s_base_names };\n\n", mangled_full_proxy_class_name, mangled_full_proxy_class_name);
+
+ current[NO_CPP] = true;
+ Delete(class_static_nspace);
+ class_static_nspace = 0;
+ Delete(mangled_full_proxy_class_name);
+ mangled_full_proxy_class_name = 0;
+ Delete(destructor_name);
+ destructor_name = 0;
+ Delete(full_proxy_class_name);
+ full_proxy_class_name = 0;
+ proxy_class_name = 0;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * memberfunctionHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int memberfunctionHandler(Node *n) {
+ String *symname = GetChar(n, "sym:name");
+ //Printf(stdout,"memberfunctionHandler %s %s\n",name,iname);
+
+ // Special case unary minus: LUA passes two parameters for the
+ // wrapper function while we want only one. Tell our
+ // functionWrapper to ignore a parameter.
+
+ if (Cmp(symname, "__unm") == 0) {
+ //Printf(stdout, "unary minus: ignore one argument\n");
+ SetInt(n, "lua:ignore_args", 1);
+ }
+
+ current[MEMBER_FUNC] = true;
+ Language::memberfunctionHandler(n);
+
+ registerMethod(n);
+ current[MEMBER_FUNC] = false;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * membervariableHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int membervariableHandler(Node *n) {
+ // REPORT("membervariableHandler",n);
+ current[MEMBER_VAR] = true;
+ Language::membervariableHandler(n);
+ registerVariable(n);
+ current[MEMBER_VAR] = false;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constructorHandler()
+ *
+ * Method for adding C++ member constructor
+ * ------------------------------------------------------------ */
+
+ virtual int constructorHandler(Node *n) {
+ // REPORT("constructorHandler", n);
+ current[CONSTRUCTOR] = true;
+ Language::constructorHandler(n);
+ current[CONSTRUCTOR] = false;
+ //constructor_name = NewString(Getattr(n, "sym:name"));
+ have_constructor = 1;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * destructorHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int destructorHandler(Node *n) {
+ REPORT("destructorHandler", n);
+ current[DESTRUCTOR] = true;
+ Language::destructorHandler(n);
+ current[DESTRUCTOR] = false;
+ have_destructor = 1;
+ destructor_action = Getattr(n, "wrap:action");
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * globalfunctionHandler()
+ *
+ * It can be called:
+ * 1. Usual C/C++ global function.
+ * 2. During class parsing for functions declared/defined as friend
+ * 3. During class parsing from staticmemberfunctionHandler
+ * ---------------------------------------------------------------------- */
+
+ virtual int globalfunctionHandler(Node *n) {
+ bool oldVal = current[NO_CPP];
+ if (!current[STATIC_FUNC]) // If static function, don't switch to NO_CPP
+ current[NO_CPP] = true;
+ const int result = Language::globalfunctionHandler(n);
+
+ if (!current[STATIC_FUNC]) // Register only if not called from static function handler
+ registerMethod(n);
+ current[NO_CPP] = oldVal;
+ return result;
+ }
+
+ /* ----------------------------------------------------------------------
+ * globalvariableHandler()
+ *
+ * Sets "current" array correctly
+ * ---------------------------------------------------------------------- */
+
+ virtual int globalvariableHandler(Node *n) {
+ bool oldVal = current[NO_CPP];
+ current[GLOBAL_VAR] = true;
+ current[NO_CPP] = true;
+
+ const int result = Language::globalvariableHandler(n);
+ registerVariable(n);
+
+ current[GLOBAL_VAR] = false;
+ current[NO_CPP] = oldVal;
+ return result;
+ }
+
+
+ /* -----------------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ *
+ * Wrap a static C++ function
+ * ---------------------------------------------------------------------- */
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+ REPORT("staticmemberfunctionHandler", n);
+ current[STATIC_FUNC] = true;
+
+ const int result = Language::staticmemberfunctionHandler(n);
+ registerMethod(n);
+
+ if (old_metatable_bindings && result == SWIG_OK && old_compatible_names) {
+ Swig_require("lua_staticmemberfunctionHandler", n, "*lua:name", NIL);
+ String *lua_name = Getattr(n, "lua:name");
+ // Although this function uses Swig_name_member, it actually generates the Lua name,
+ // not the C++ name. This is because an earlier version used such a scheme for static function
+ // name generation and we have to maintain backward compatibility.
+ String *compat_name = Swig_name_member(0, proxy_class_name, lua_name);
+ Setattr(n, "lua:name", compat_name);
+ registerMethod(n, true, getNSpace());
+ Delete(compat_name);
+ Swig_restore(n);
+ }
+
+ current[STATIC_FUNC] = false;;
+ return result;
+ }
+
+ /* ------------------------------------------------------------
+ * memberconstantHandler()
+ *
+ * Create a C++ constant
+ * ------------------------------------------------------------ */
+
+ virtual int memberconstantHandler(Node *n) {
+ REPORT("memberconstantHandler", n);
+ int result = Language::memberconstantHandler(n);
+
+ return result;
+ }
+
+ /* ---------------------------------------------------------------------
+ * staticmembervariableHandler()
+ * --------------------------------------------------------------------- */
+
+ virtual int staticmembervariableHandler(Node *n) {
+ REPORT("staticmembervariableHandler", n);
+ current[STATIC_VAR] = true;
+ //String *symname = Getattr(n, "sym:name");
+ int result = Language::staticmembervariableHandler(n);
+ if (!GetFlag(n, "wrappedasconstant")) {
+ registerVariable(n);
+ }
+
+ if (result == SWIG_OK) {
+ // This will add static member variable to the class namespace with name ClassName_VarName
+ if (old_metatable_bindings && old_compatible_names) {
+ Swig_save("lua_staticmembervariableHandler", n, "lua:name", NIL);
+ String *lua_name = Getattr(n, "lua:name");
+ // Although this function uses Swig_name_member, it actually generates the Lua name,
+ // not the C++ name. This is because an earlier version used such a scheme for static function
+ // name generation and we have to maintain backward compatibility.
+ String *v2_name = Swig_name_member(NIL, proxy_class_name, lua_name);
+ if (!GetFlag(n, "wrappedasconstant")) {
+ Setattr(n, "lua:name", v2_name);
+ // Registering static var in the class parent nspace
+ registerVariable(n, true, getNSpace());
+ }
+ // If static member variable was wrapped as a constant, then
+ // constant wrapper has already performed all actions necessary for old_metatable_bindings
+ Delete(v2_name);
+ Swig_restore(n);
+ }
+ }
+ current[STATIC_VAR] = false;
+
+ return result;
+ }
+
+
+ /* ---------------------------------------------------------------------
+ * external runtime generation
+ * --------------------------------------------------------------------- */
+
+ /* This is to support the usage
+ SWIG -external-runtime <filename>
+ The code consists of two functions:
+ String *runtimeCode() // returns a large string with all the runtimes in
+ String *defaultExternalRuntimeFilename() // returns the default filename
+ I am writing a generic solution, even though SWIG-Lua only has one file right now...
+ */
+ String *runtimeCode() {
+ String *s = NewString("");
+ const char *filenames[] = { "luarun.swg", 0 }; // must be 0 terminated
+
+ emitLuaFlavor(s);
+
+ String *sfile = 0;
+ for (int i = 0; filenames[i] != 0; i++) {
+ sfile = Swig_include_sys(filenames[i]);
+ if (!sfile) {
+ Printf(stderr, "*** Unable to open '%s'\n", filenames[i]);
+ } else {
+ Append(s, sfile);
+ Delete(sfile);
+ }
+ }
+
+ return s;
+ }
+
+ String *defaultExternalRuntimeFilename() {
+ return NewString("swigluarun.h");
+ }
+
+ /* ---------------------------------------------------------------------
+ * helpers
+ * --------------------------------------------------------------------- */
+
+ void emitLuaFlavor(String *s) {
+ if (elua_emulate) {
+ Printf(s, "/*This is only emulation!*/\n");
+ Printf(s, "#define SWIG_LUA_TARGET SWIG_LUA_FLAVOR_ELUA\n");
+ Printf(s, "#define SWIG_LUA_ELUA_EMULATE\n");
+ } else if (elua_ltr)
+ Printf(s, "#define SWIG_LUA_TARGET SWIG_LUA_FLAVOR_ELUA\n");
+ else if (eluac_ltr)
+ Printf(s, "#define SWIG_LUA_TARGET SWIG_LUA_FLAVOR_ELUAC\n");
+ else
+ Printf(s, "#define SWIG_LUA_TARGET SWIG_LUA_FLAVOR_LUA\n");
+ }
+
+
+ /* -----------------------------------------------------------------------------
+ * escapeCode()
+ *
+ * This is to convert the string of Lua code into a proper string, which can then be
+ * emitted into the C/C++ code.
+ * Basically it is a lot of search & replacing of odd sequences
+ * ---------------------------------------------------------------------------- */
+
+ void escapeCode(String *str) {
+ //Printf(f_runtime,"/* original luacode:[[[\n%s\n]]]\n*/\n",str);
+ Chop(str); // trim
+ Replace(str, "\\", "\\\\", DOH_REPLACE_ANY); // \ to \\ (this must be done first)
+ Replace(str, "\"", "\\\"", DOH_REPLACE_ANY); // " to \"
+ Replace(str, "\n", "\\n\"\n \"", DOH_REPLACE_ANY); // \n to \n"\n" (ie quoting every line)
+ //Printf(f_runtime,"/* hacked luacode:[[[\n%s\n]]]\n*/\n",str);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * rawGetCArraysHash(String *name)
+ *
+ * A small helper to hide implementation of how CArrays hashes are stored
+ * ---------------------------------------------------------------------------- */
+
+ Hash *rawGetCArraysHash(const_String_or_char_ptr name) {
+ Hash *scope = symbolScopeLookup( name ? name : "" );
+ if(!scope)
+ return 0;
+
+ Hash *carrays_hash = Getattr(scope, "lua:cdata");
+ return carrays_hash;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * getCArraysHash()
+ *
+ * Each namespace can be described with a hash that stores C arrays
+ * where members of the namespace should be added. All these hashes are stored
+ * inside the symbols table, in pseudo-symbol for every namespace.
+ * nspace could be NULL (NSPACE_TODO), that means functions and variables and classes
+ * that are not in any namespace (this is default for SWIG unless %nspace feature is used).
+ * You can later set some attributes that will affect behaviour of functions that use this hash:
+ * "lua:no_namespaces" will disable "namespaces" array.
+ * "lua:no_classes" will disable "classes" array.
+ * For every component ("attributes", "methods", etc) there are subcomponents:
+ * XXX:name - name of the C array that stores data for component
+ * XXX:decl - statement with forward declaration of this array;
+ * Namespace could be automatically registered to its parent if 'reg' == true. This can only be
+ * done during the first call (a.k.a when nspace is created).
+ * ---------------------------------------------------------------------------- */
+
+ Hash *getCArraysHash(String *nspace, bool reg = true) {
+ Hash *scope = symbolScopeLookup(nspace ? nspace : "");
+ if(!scope) {
+ symbolAddScope( nspace ? nspace : "" );
+ scope = symbolScopeLookup(nspace ? nspace : "");
+ assert(scope);
+ }
+ Hash *carrays_hash = Getattr(scope, "lua:cdata");
+ if (carrays_hash != 0)
+ return carrays_hash;
+ carrays_hash = NewHash();
+ String *mangled_name = 0;
+ if (nspace == 0 || Len(nspace) == 0)
+ mangled_name = NewString("SwigModule");
+ else
+ mangled_name = Swig_name_mangle(nspace);
+ String *cname = NewStringf("swig_%s", mangled_name);
+
+ Setattr(carrays_hash, "cname", cname);
+
+ String *attr_tab = NewString("");
+ String *attr_tab_name = NewStringf("swig_%s_attributes", mangled_name);
+ String *attr_tab_decl = NewString("");
+ Printv(attr_tab, "static swig_lua_attribute ", NIL);
+ Printv(attr_tab, attr_tab_name, "[]", NIL);
+ Printv(attr_tab_decl, attr_tab, ";\n", NIL);
+ Printv(attr_tab, " = {\n", NIL);
+ Setattr(carrays_hash, "attributes", attr_tab);
+ Setattr(carrays_hash, "attributes:name", attr_tab_name);
+ Setattr(carrays_hash, "attributes:decl", attr_tab_decl);
+
+ String *methods_tab = NewString("");
+ String *methods_tab_name = NewStringf("swig_%s_methods", mangled_name);
+ String *methods_tab_decl = NewString("");
+ if (elua_ltr || eluac_ltr) // In this case methods array also acts as namespace rotable
+ Printf(methods_tab, "const LUA_REG_TYPE ");
+ else
+ Printf(methods_tab, "static swig_lua_method ");
+ Printv(methods_tab, methods_tab_name, "[]", NIL);
+ Printv(methods_tab_decl, methods_tab, ";\n", NIL);
+ Printv(methods_tab, "= {\n", NIL);
+ Setattr(carrays_hash, "methods", methods_tab);
+ Setattr(carrays_hash, "methods:name", methods_tab_name);
+ Setattr(carrays_hash, "methods:decl", methods_tab_decl);
+
+ String *const_tab = NewString("");
+ String *const_tab_name = NewStringf("swig_%s_constants", mangled_name);
+ String *const_tab_decl = NewString("");
+ if (elua_ltr || eluac_ltr) // In this case const array holds rotable with namespace constants
+ Printf(const_tab, "const LUA_REG_TYPE ");
+ else
+ Printf(const_tab, "static swig_lua_const_info ");
+ Printv(const_tab, const_tab_name, "[]", NIL);
+ Printv(const_tab_decl, const_tab, ";", NIL);
+ Printv(const_tab, "= {\n", NIL);
+ Setattr(carrays_hash, "constants", const_tab);
+ Setattr(carrays_hash, "constants:name", const_tab_name);
+ Setattr(carrays_hash, "constants:decl", const_tab_decl);
+
+ String *classes_tab = NewString("");
+ String *classes_tab_name = NewStringf("swig_%s_classes", mangled_name);
+ String *classes_tab_decl = NewString("");
+ Printf(classes_tab, "static swig_lua_class* ");
+ Printv(classes_tab, classes_tab_name, "[]", NIL);
+ Printv(classes_tab_decl, classes_tab, ";", NIL);
+ Printv(classes_tab, "= {\n", NIL);
+ Setattr(carrays_hash, "classes", classes_tab);
+ Setattr(carrays_hash, "classes:name", classes_tab_name);
+ Setattr(carrays_hash, "classes:decl", classes_tab_decl);
+
+ String *namespaces_tab = NewString("");
+ String *namespaces_tab_name = NewStringf("swig_%s_namespaces", mangled_name);
+ String *namespaces_tab_decl = NewString("");
+ Printf(namespaces_tab, "static swig_lua_namespace* ");
+ Printv(namespaces_tab, namespaces_tab_name, "[]", NIL);
+ Printv(namespaces_tab_decl, namespaces_tab, ";", NIL);
+ Printv(namespaces_tab, " = {\n", NIL);
+ Setattr(carrays_hash, "namespaces", namespaces_tab);
+ Setattr(carrays_hash, "namespaces:name", namespaces_tab_name);
+ Setattr(carrays_hash, "namespaces:decl", namespaces_tab_decl);
+
+ if (elua_ltr) {
+ String *get_tab = NewString("");
+ String *get_tab_name = NewStringf("swig_%s_get", mangled_name);
+ String *get_tab_decl = NewString("");
+ Printv(get_tab, "const LUA_REG_TYPE ", get_tab_name, "[]", NIL);
+ Printv(get_tab_decl, get_tab, ";", NIL);
+ Printv(get_tab, " = {\n", NIL);
+ Setattr(carrays_hash, "get", get_tab);
+ Setattr(carrays_hash, "get:name", get_tab_name);
+ Setattr(carrays_hash, "get:decl", get_tab_decl);
+
+ String *set_tab = NewString("");
+ String *set_tab_name = NewStringf("swig_%s_set", mangled_name);
+ String *set_tab_decl = NewString("");
+ Printv(set_tab, "const LUA_REG_TYPE ", set_tab_name, "[]", NIL);
+ Printv(set_tab_decl, set_tab, ";", NIL);
+ Printv(set_tab, " = {\n", NIL);
+ Setattr(carrays_hash, "set", set_tab);
+ Setattr(carrays_hash, "set:name", set_tab_name);
+ Setattr(carrays_hash, "set:decl", set_tab_decl);
+
+ }
+ if (!eluac_ltr) {
+ String *metatable_tab = NewString("");
+ String *metatable_tab_name = NewStringf("swig_%s_meta", mangled_name);
+ String *metatable_tab_decl = NewString("");
+ if (elua_ltr) // In this case const array holds rotable with namespace constants
+ Printf(metatable_tab, "const LUA_REG_TYPE ");
+ else
+ Printf(metatable_tab, "static swig_lua_method ");
+ Printv(metatable_tab, metatable_tab_name, "[]", NIL);
+ Printv(metatable_tab_decl, metatable_tab, ";", NIL);
+ Printv(metatable_tab, " = {\n", NIL);
+ Setattr(carrays_hash, "metatable", metatable_tab);
+ Setattr(carrays_hash, "metatable:name", metatable_tab_name);
+ Setattr(carrays_hash, "metatable:decl", metatable_tab_decl);
+ }
+
+ Setattr(scope, "lua:cdata", carrays_hash);
+ assert(rawGetCArraysHash(nspace));
+
+ if (reg && nspace != 0 && Len(nspace) != 0 && GetFlag(carrays_hash, "lua:no_reg") == 0) {
+ // Split names into components
+ List *components = Split(nspace, '.', -1);
+ String *parent_path = NewString("");
+ int len = Len(components);
+ String *name = Copy(Getitem(components, len - 1));
+ for (int i = 0; i < len - 1; i++) {
+ if (i > 0)
+ Printv(parent_path, NSPACE_SEPARATOR, NIL);
+ String *item = Getitem(components, i);
+ Printv(parent_path, item, NIL);
+ }
+ Hash *parent = getCArraysHash(parent_path, true);
+ String *namespaces_tab = Getattr(parent, "namespaces");
+ Printv(namespaces_tab, "&", cname, ",\n", NIL);
+ if (elua_ltr || eluac_ltr) {
+ String *methods_tab = Getattr(parent, "methods");
+ Printv(methods_tab, tab4, "{LSTRKEY(\"", name, "\")", ", LROVAL(", methods_tab_name, ")", "},\n", NIL);
+ }
+ Setattr(carrays_hash, "name", name);
+
+ Delete(components);
+ Delete(parent_path);
+ } else if (!reg) // This namespace shouldn't be registered. Lets remember it.
+ SetFlag(carrays_hash, "lua:no_reg");
+
+ Delete(mangled_name);
+ mangled_name = 0;
+ return carrays_hash;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * closeCArraysHash()
+ *
+ * Functions add end markers {0,0,...,0} to all arrays, prints them to
+ * output and marks hash as closed (lua:closed). Consequent attempts to
+ * close the same hash will result in an error.
+ * closeCArraysHash DOES NOT print structure that describes namespace, it only
+ * prints array. You can use printCArraysDefinition to print structure.
+ * if "lua:no_namespaces" is set, then array for "namespaces" won't be printed
+ * if "lua:no_classes" is set, then array for "classes" won't be printed
+ * ----------------------------------------------------------------------------- */
+
+ void closeCArraysHash(String *nspace, File *output) {
+ Hash *carrays_hash = rawGetCArraysHash(nspace);
+ assert(carrays_hash);
+ assert(GetFlag(carrays_hash, "lua:closed") == 0);
+
+ SetFlag(carrays_hash, "lua:closed");
+
+ // Do arrays describe class instance part or class static part
+ const int is_instance = GetFlag(carrays_hash, "lua:class_instance");
+
+
+ String *attr_tab = Getattr(carrays_hash, "attributes");
+ Printf(attr_tab, " {0,0,0}\n};\n");
+ Printv(output, attr_tab, NIL);
+
+ String *const_tab = Getattr(carrays_hash, "constants");
+ String *const_tab_name = Getattr(carrays_hash, "constants:name");
+ if (elua_ltr || eluac_ltr)
+ Printv(const_tab, tab4, "{LNILKEY, LNILVAL}\n", "};\n", NIL);
+ else
+ Printf(const_tab, " {0,0,0,0,0,0}\n};\n");
+
+ // For the sake of compiling with -Wall -Werror we print constants
+ // only when necessary
+ int need_constants = 0;
+ if ( (elua_ltr || eluac_ltr) && (old_metatable_bindings) )
+ need_constants = 1;
+ else if (!is_instance) // static part need constants tab
+ need_constants = 1;
+
+ if (need_constants)
+ Printv(output, const_tab, NIL);
+
+ if (elua_ltr) {
+ // Put forward declaration of metatable array
+ Printv(output, "extern ", Getattr(carrays_hash, "metatable:decl"), "\n", NIL);
+ }
+ String *methods_tab = Getattr(carrays_hash, "methods");
+ String *metatable_tab_name = Getattr(carrays_hash, "metatable:name");
+ if (elua_ltr || eluac_ltr) {
+ if (old_metatable_bindings)
+ Printv(methods_tab, tab4, "{LSTRKEY(\"const\"), LROVAL(", const_tab_name, ")},\n", NIL);
+ if (elua_ltr) {
+ Printv(methods_tab, tab4, "{LSTRKEY(\"__metatable\"), LROVAL(", metatable_tab_name, ")},\n", NIL);
+ }
+
+ Printv(methods_tab, tab4, "{LSTRKEY(\"__disown\"), LFUNCVAL(SWIG_Lua_class_disown)},\n", NIL);
+ Printv(methods_tab, tab4, "{LNILKEY, LNILVAL}\n};\n", NIL);
+ } else
+ Printf(methods_tab, " {0,0}\n};\n");
+ Printv(output, methods_tab, NIL);
+
+ if (!GetFlag(carrays_hash, "lua:no_classes")) {
+ String *classes_tab = Getattr(carrays_hash, "classes");
+ Printf(classes_tab, " 0\n};\n");
+ Printv(output, classes_tab, NIL);
+ }
+
+ if (!GetFlag(carrays_hash, "lua:no_namespaces")) {
+ String *namespaces_tab = Getattr(carrays_hash, "namespaces");
+ Printf(namespaces_tab, " 0\n};\n");
+ Printv(output, namespaces_tab, NIL);
+ }
+ if (elua_ltr) {
+ String *get_tab = Getattr(carrays_hash, "get");
+ String *set_tab = Getattr(carrays_hash, "set");
+ Printv(get_tab, tab4, "{LNILKEY, LNILVAL}\n};\n", NIL);
+ Printv(set_tab, tab4, "{LNILKEY, LNILVAL}\n};\n", NIL);
+ Printv(output, get_tab, NIL);
+ Printv(output, set_tab, NIL);
+ }
+
+ // Heuristic whether we need to print metatable or not.
+ // For the sake of compiling with -Wall -Werror we don't print
+ // metatable for static part.
+ int need_metatable = 0;
+ if (eluac_ltr)
+ need_metatable = 0;
+ else if(!is_instance)
+ need_metatable = 0;
+ else
+ need_metatable = 1;
+
+ if (need_metatable) {
+ String *metatable_tab = Getattr(carrays_hash, "metatable");
+ assert(metatable_tab);
+ if (elua_ltr) {
+ String *get_tab_name = Getattr(carrays_hash, "get:name");
+ String *set_tab_name = Getattr(carrays_hash, "set:name");
+
+ if (GetFlag(carrays_hash, "lua:class_instance")) {
+ Printv(metatable_tab, tab4, "{LSTRKEY(\"__index\"), LFUNCVAL(SWIG_Lua_class_get)},\n", NIL);
+ Printv(metatable_tab, tab4, "{LSTRKEY(\"__newindex\"), LFUNCVAL(SWIG_Lua_class_set)},\n", NIL);
+ } else {
+ Printv(metatable_tab, tab4, "{LSTRKEY(\"__index\"), LFUNCVAL(SWIG_Lua_namespace_get)},\n", NIL);
+ Printv(metatable_tab, tab4, "{LSTRKEY(\"__newindex\"), LFUNCVAL(SWIG_Lua_namespace_set)},\n", NIL);
+ }
+
+ Printv(metatable_tab, tab4, "{LSTRKEY(\"__gc\"), LFUNCVAL(SWIG_Lua_class_destruct)},\n", NIL);
+ Printv(metatable_tab, tab4, "{LSTRKEY(\".get\"), LROVAL(", get_tab_name, ")},\n", NIL);
+ Printv(metatable_tab, tab4, "{LSTRKEY(\".set\"), LROVAL(", set_tab_name, ")},\n", NIL);
+ Printv(metatable_tab, tab4, "{LSTRKEY(\".fn\"), LROVAL(", Getattr(carrays_hash, "methods:name"), ")},\n", NIL);
+
+ if (GetFlag(carrays_hash, "lua:class_instance")) {
+ String *static_cls = Getattr(carrays_hash, "lua:class_instance:static_hash");
+ assert(static_cls);
+ // static_cls is swig_lua_namespace. This structure can't be used with eLua(LTR)
+ // Instead a structure describing its methods is used
+ String *static_cls_cname = Getattr(static_cls, "methods:name");
+ assert(static_cls_cname);
+ Printv(metatable_tab, tab4, "{LSTRKEY(\".static\"), LROVAL(", static_cls_cname, ")},\n", NIL);
+ // Put forward declaration of this array
+ Printv(output, "extern ", Getattr(static_cls, "methods:decl"), "\n", NIL);
+ } else if (GetFlag(carrays_hash, "lua:class_static")) {
+ Hash *instance_cls = Getattr(carrays_hash, "lua:class_static:instance_hash");
+ assert(instance_cls);
+ String *instance_cls_metatable_name = Getattr(instance_cls, "metatable:name");
+ assert(instance_cls_metatable_name);
+ Printv(metatable_tab, tab4, "{LSTRKEY(\".instance\"), LROVAL(", instance_cls_metatable_name, ")},\n", NIL);
+ }
+
+ Printv(metatable_tab, tab4, "{LNILKEY, LNILVAL}\n};\n", NIL);
+ } else {
+ Printf(metatable_tab, " {0,0}\n};\n");
+ }
+
+ Printv(output, metatable_tab, NIL);
+ }
+
+ Printv(output, "\n", NIL);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * closeNamespaces()
+ *
+ * Recursively close all non-closed namespaces. Prints data to dataOutput.
+ * ----------------------------------------------------------------------------- */
+
+ void closeNamespaces(File *dataOutput) {
+ // Special handling for empty module.
+ if (symbolScopeLookup("") == 0 || rawGetCArraysHash("") == 0) {
+ // Module is empty. Create hash for global scope in order to have swig_SwigModule
+ // variable in resulting file
+ getCArraysHash(0);
+ }
+ // Because we can't directly access 'symtabs', instead we access
+ // top-level scope and look on all scope pseudo-symbols in it.
+ Hash *top_scope = symbolScopeLookup("");
+ assert(top_scope);
+ Iterator ki = First(top_scope);
+ List *to_close = NewList();
+ while (ki.key) {
+ assert(ki.item);
+ if (Getattr(ki.item, "sym:scope")) {
+ // We have a pseudo symbol. Lets get actual scope for this pseudo symbol
+ Hash *carrays_hash = rawGetCArraysHash(ki.key);
+ assert(carrays_hash);
+ if (GetFlag(carrays_hash, "lua:closed") == 0)
+ Append(to_close, ki.key);
+ }
+ ki = Next(ki);
+ }
+ SortList(to_close, &compareByLen);
+ int len = Len(to_close);
+ for (int i = 0; i < len; i++) {
+ String *key = Getitem(to_close, i);
+ closeCArraysHash(key, dataOutput);
+ Hash *carrays_hash = rawGetCArraysHash(key);
+ String *name = 0; // name - name of the namespace as it should be visible in Lua
+ if (DohLen(key) == 0) // This is global module
+ name = module;
+ else
+ name = Getattr(carrays_hash, "name");
+ assert(name);
+ printCArraysDefinition(key, name, dataOutput);
+ }
+ Delete(to_close);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * printCArraysDefinition()
+ *
+ * This function prints to output a definition of namespace in form
+ * swig_lua_namespace $cname = { attr_array, methods_array, ... , namespaces_array };
+ * You can call this function as many times as is necessary.
+ * 'name' is a user-visible name that this namespace will have in Lua. It shouldn't
+ * be a fully qualified name, just its own name.
+ * ----------------------------------------------------------------------------- */
+
+ void printCArraysDefinition(String *nspace, String *name, File *output) {
+ Hash *carrays_hash = getCArraysHash(nspace, false);
+
+ String *cname = Getattr(carrays_hash, "cname"); // cname - name of the C structure that describes namespace
+ assert(cname);
+ Printv(output, "static swig_lua_namespace ", cname, " = ", NIL);
+
+ String *null_string = NewString("0");
+ String *attr_tab_name = Getattr(carrays_hash, "attributes:name");
+ String *methods_tab_name = Getattr(carrays_hash, "methods:name");
+ String *const_tab_name = Getattr(carrays_hash, "constants:name");
+ String *classes_tab_name = Getattr(carrays_hash, "classes:name");
+ String *namespaces_tab_name = Getattr(carrays_hash, "namespaces:name");
+ bool has_classes = GetFlag(carrays_hash, "lua:no_classes") == 0;
+ bool has_namespaces = GetFlag(carrays_hash, "lua:no_namespaces") == 0;
+
+ Printv(output, "{\n",
+ tab4, "\"", name, "\",\n",
+ tab4, methods_tab_name, ",\n",
+ tab4, attr_tab_name, ",\n",
+ tab4, const_tab_name, ",\n",
+ tab4, (has_classes) ? classes_tab_name : null_string, ",\n",
+ tab4, (has_namespaces) ? namespaces_tab_name : null_string, "\n};\n", NIL);
+ Delete(null_string);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * luaCurrentSymbolNSpace()
+ *
+ * This function determines actual namespace/scope where any symbol at the
+ * current moment should be placed. It looks at the 'current' array
+ * and depending on where are we - static class member/function,
+ * instance class member/function or just global functions decides
+ * where symbol should be put.
+ * The namespace/scope doesn't depend from symbol, only from 'current'
+ * ----------------------------------------------------------------------------- */
+
+ String *luaCurrentSymbolNSpace() {
+ String *scope = 0;
+ // If outside class, than NSpace is used.
+ // If inside class, but current[NO_CPP], then this is friend function. It belongs to NSpace
+ if (!getCurrentClass() || current[NO_CPP]) {
+ scope = getNSpace();
+ } else if (current[ENUM_CONST] && !CPlusPlus ) {
+ // Enums in C mode go to NSpace
+ scope = getNSpace();
+ } else {
+ // If inside class, then either class static namespace or class fully qualified name is used
+ assert(!current[NO_CPP]);
+ if (current[STATIC_FUNC] || current[STATIC_VAR] || current[STATIC_CONST]) {
+ scope = class_static_nspace;
+ } else if (current[MEMBER_VAR] || current[CONSTRUCTOR] || current[DESTRUCTOR]
+ || current[MEMBER_FUNC]) {
+ scope = full_proxy_class_name;
+ } else { // Friend functions are handled this way
+ scope = class_static_nspace;
+ }
+ assert(scope);
+ }
+ return scope;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * luaAddSymbol()
+ *
+ * Our implementation of addSymbol. Determines scope correctly, then
+ * forwards to Language::addSymbol
+ * ----------------------------------------------------------------------------- */
+
+ int luaAddSymbol(const String *s, const Node *n) {
+ String *scope = luaCurrentSymbolNSpace();
+ return luaAddSymbol(s, n, scope);
+ }
+
+ /* -----------------------------------------------------------------------------
+ * luaAddSymbol()
+ *
+ * Overload. Enforces given scope. Actually, it simply forwards call to Language::addSymbol
+ * ----------------------------------------------------------------------------- */
+
+ int luaAddSymbol(const String *s, const Node *n, const_String_or_char_ptr scope) {
+ int result = Language::addSymbol(s, n, scope);
+ if (!result)
+ Printf(stderr, "addSymbol(%s to scope %s) failed\n", s, scope);
+ return result;
+ }
+
+};
+
+/* -----------------------------------------------------------------------------
+ * swig_lua() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+extern "C" Language *swig_lua(void) {
+ return new LUA();
+}
diff --git a/contrib/tools/swig/Source/Modules/main.cxx b/contrib/tools/swig/Source/Modules/main.cxx
new file mode 100644
index 0000000000..f5bdec644c
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/main.cxx
@@ -0,0 +1,1407 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * main.cxx
+ *
+ * Main entry point to the SWIG core.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigconfig.h"
+
+#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include "swigmod.h"
+
+#include "swigwarn.h"
+#include "cparse.h"
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h> // for INT_MAX
+
+// Global variables
+
+static Language *lang = 0; // Language method
+int CPlusPlus = 0;
+int Extend = 0; // Extend flag
+int ForceExtern = 0; // Force extern mode
+int GenerateDefault = 1; // Generate default constructors
+int Verbose = 0;
+int AddExtern = 0;
+int NoExcept = 0;
+int SwigRuntime = 0; // 0 = no option, 1 = -runtime, 2 = -noruntime
+extern "C" {
+ int UseWrapperSuffix = 0; // If 1, append suffix to non-overloaded functions too.
+}
+
+/* Suppress warning messages for private inheritance, etc by default.
+ These are enabled by command line option -Wextra.
+
+ WARN_PARSE_PRIVATE_INHERIT 309
+ WARN_PARSE_BUILTIN_NAME 321
+ WARN_PARSE_REDUNDANT 322
+ WARN_TYPE_ABSTRACT 403
+ WARN_TYPE_RVALUE_REF_QUALIFIER_IGNORED 405
+ WARN_LANG_OVERLOAD_CONST 512
+ */
+#define EXTRA_WARNINGS "309,403,405,512,321,322"
+
+extern "C" {
+ extern String *ModuleName;
+ extern int ignore_nested_classes;
+ extern int kwargs_supported;
+}
+
+/* usage string split into multiple parts otherwise string is too big for some compilers */
+/* naming conventions for commandline options - no underscores, no capital letters, join words together
+ * except when using a common prefix, then use '-' to separate, eg the debug-xxx options */
+static const char *usage1 = (const char *) "\
+\nGeneral Options\n\
+ -addextern - Add extra extern declarations\n\
+ -c++ - Enable C++ processing\n\
+ -co <file> - Check <file> out of the SWIG library\n\
+ -copyctor - Automatically generate copy constructors wherever possible\n\
+ -cpperraswarn - Treat the preprocessor #error statement as #warning (default)\n\
+ -cppext <ext> - Change file extension of generated C++ files to <ext>\n\
+ (default is cxx)\n\
+ -copyright - Display copyright notices\n\
+ -debug-classes - Display information about the classes found in the interface\n\
+ -debug-module <n>- Display module parse tree at stages 1-4, <n> is a csv list of stages\n\
+ -debug-symtabs - Display symbol tables information\n\
+ -debug-symbols - Display target language symbols in the symbol tables\n\
+ -debug-csymbols - Display C symbols in the symbol tables\n\
+ -debug-lsymbols - Display target language layer symbols\n\
+ -debug-quiet - Display less parse tree node debug info when using other -debug options\n\
+ -debug-tags - Display information about the tags found in the interface\n\
+ -debug-template - Display information for debugging templates\n\
+ -debug-top <n> - Display entire parse tree at stages 1-4, <n> is a csv list of stages\n\
+ -debug-typedef - Display information about the types and typedefs in the interface\n\
+ -debug-typemap - Display typemap debugging information\n\
+ -debug-tmsearch - Display typemap search debugging information\n\
+ -debug-tmused - Display typemaps used debugging information\n\
+ -directors - Turn on director mode for all the classes, mainly for testing\n\
+ -dirprot - Turn on wrapping of protected members for director classes (default)\n\
+ -D<symbol> - Define a symbol <symbol> (for conditional compilation)\n\
+";
+
+static const char *usage2 = (const char *) "\
+ -E - Preprocess only, does not generate wrapper code\n\
+ -external-runtime [file] - Export the SWIG runtime stack\n\
+ -fakeversion <v>- Make SWIG fake the program version number to <v>\n\
+ -fcompact - Compile in compact mode\n\
+ -features <list>- Set global features, where <list> is a comma separated list of\n\
+ features, eg -features directors,autodoc=1\n\
+ If no explicit value is given to the feature, a default of 1 is used\n\
+ -fastdispatch - Enable fast dispatch mode to produce faster overload dispatcher code\n\
+ -Fmicrosoft - Display error/warning messages in Microsoft format\n\
+ -Fstandard - Display error/warning messages in commonly used format\n\
+ -fvirtual - Compile in virtual elimination mode\n\
+ -help - Display help\n\
+ -I- - Don't search the current directory\n\
+ -I<dir> - Look for SWIG files in directory <dir>\n\
+ -ignoremissing - Ignore missing include files\n\
+ -importall - Follow all #include statements as imports\n\
+ -includeall - Follow all #include statements\n\
+ -l<ifile> - Include SWIG library file <ifile>\n\
+";
+
+static const char *usage3 = (const char *) "\
+ -macroerrors - Report errors inside macros\n\
+ -makedefault - Create default constructors/destructors (the default)\n\
+ -M - List all dependencies\n\
+ -MD - Is equivalent to `-M -MF <file>', except `-E' is not implied\n\
+ -MF <file> - Generate dependencies into <file> and continue generating wrappers\n\
+ -MM - List dependencies, but omit files in SWIG library\n\
+ -MMD - Like `-MD', but omit files in SWIG library\n\
+ -module <name> - Set module name to <name>\n\
+ -MP - Generate phony targets for all dependencies\n\
+ -MT <target> - Set the target of the rule emitted by dependency generation\n\
+ -nocontract - Turn off contract checking\n\
+ -nocpperraswarn - Do not treat the preprocessor #error statement as #warning\n\
+ -nodefault - Do not generate default constructors nor default destructors\n\
+ -nodefaultctor - Do not generate implicit default constructors\n\
+ -nodefaultdtor - Do not generate implicit default destructors\n\
+ -nodirprot - Do not wrap director protected members\n\
+ -noexcept - Do not wrap exception specifiers\n\
+ -nofastdispatch - Disable fast dispatch mode (default)\n\
+ -nopreprocess - Skip the preprocessor step\n\
+ -notemplatereduce - Disable reduction of the typedefs in templates\n\
+";
+
+static const char *usage4 = (const char *) "\
+ -O - Enable the optimization options:\n\
+ -fastdispatch -fvirtual\n\
+ -o <outfile> - Set name of C/C++ output file to <outfile>\n\
+ -oh <headfile> - Set name of C++ output header file for directors to <headfile>\n\
+ -outcurrentdir - Set default output dir to current dir instead of input file's path\n\
+ -outdir <dir> - Set language specific files output directory to <dir>\n\
+ -pcreversion - Display PCRE2 version information\n\
+ -small - Compile in virtual elimination and compact mode\n\
+ -swiglib - Report location of SWIG library and exit\n\
+ -templatereduce - Reduce all the typedefs in templates\n\
+ -v - Run in verbose mode\n\
+ -version - Display SWIG version number\n\
+ -Wall - Remove all warning suppression, also implies -Wextra\n\
+ -Wallkw - Enable keyword warnings for all the supported languages\n\
+ -Werror - Treat warnings as errors\n\
+ -Wextra - Adds the following additional warnings: " EXTRA_WARNINGS "\n\
+ -w<list> - Suppress/add warning messages, eg -w401,+321 - see Warnings.html\n\
+ -xmlout <file> - Write XML version of the parse tree to <file> after normal processing\n\
+\n\
+Options can also be defined using the SWIG_FEATURES environment variable, for example:\n\
+\n\
+ $ SWIG_FEATURES=\"-Wall\"\n\
+ $ export SWIG_FEATURES\n\
+ $ swig -python interface.i\n\
+\n\
+is equivalent to:\n\
+\n\
+ $ swig -Wall -python interface.i\n\
+\n\
+Arguments may also be passed in a file, separated by whitespace. For example:\n\
+\n\
+ $ echo \"-Wall -python interface.i\" > args.txt\n\
+ $ swig @args.txt\n\
+\n";
+
+// Local variables
+static String *LangSubDir = 0; // Target language library subdirectory
+static String *SwigLib = 0; // Library directory
+static String *SwigLibWinUnix = 0; // Extra library directory on Windows
+static int freeze = 0;
+static String *lang_config = 0;
+static const char *hpp_extension = "h";
+static const char *cpp_extension = "cxx";
+static const char *depends_extension = "d";
+static String *outdir = 0;
+static String *xmlout = 0;
+static int outcurrentdir = 0;
+static int help = 0;
+static int checkout = 0;
+static int cpp_only = 0;
+static int no_cpp = 0;
+static String *outfile_name = 0;
+static String *outfile_name_h = 0;
+static int tm_debug = 0;
+static int dump_symtabs = 0;
+static int dump_symbols = 0;
+static int dump_csymbols = 0;
+static int dump_lang_symbols = 0;
+static int dump_tags = 0;
+static int dump_module = 0;
+static int dump_top = 0;
+static int dump_xml = 0;
+static int dump_typedef = 0;
+static int dump_classes = 0;
+static int werror = 0;
+static int depend = 0;
+static int depend_only = 0;
+static int depend_phony = 0;
+static int memory_debug = 0;
+static int allkw = 0;
+static DOH *cpps = 0;
+static String *dependencies_file = 0;
+static String *dependencies_target = 0;
+static int external_runtime = 0;
+static String *external_runtime_name = 0;
+enum { STAGE1=1, STAGE2=2, STAGE3=4, STAGE4=8, STAGEOVERFLOW=16 };
+static List *libfiles = 0;
+static List *all_output_files = 0;
+
+/* -----------------------------------------------------------------------------
+ * check_extension()
+ *
+ * Checks the extension of a file to see if we should emit extern declarations.
+ * ----------------------------------------------------------------------------- */
+
+static bool check_extension(String *filename) {
+ bool wanted = false;
+ const char *name = Char(filename);
+ if (!name)
+ return 0;
+ String *extension = Swig_file_extension(name);
+ const char *c = Char(extension);
+ if ((strcmp(c, ".c") == 0) ||
+ (strcmp(c, ".C") == 0) || (strcmp(c, ".cc") == 0) || (strcmp(c, ".cxx") == 0) || (strcmp(c, ".c++") == 0) || (strcmp(c, ".cpp") == 0)) {
+ wanted = true;
+ }
+ Delete(extension);
+ return wanted;
+}
+
+/* -----------------------------------------------------------------------------
+ * install_opts()
+ *
+ * Install all command line options as preprocessor symbols
+ * ----------------------------------------------------------------------------- */
+
+static void install_opts(int argc, char *argv[]) {
+ int i;
+ int noopt = 0;
+ char *c;
+ for (i = 1; i < (argc - 1); i++) {
+ if (argv[i]) {
+ if ((*argv[i] == '-') && (!isupper(*(argv[i] + 1)))) {
+ String *opt = NewStringf("SWIGOPT%(upper)s", argv[i]);
+ Replaceall(opt, "-", "_");
+ c = Char(opt);
+ noopt = 0;
+ while (*c) {
+ if (!(isalnum(*c) || (*c == '_'))) {
+ noopt = 1;
+ break;
+ }
+ c++;
+ }
+ if (((i + 1) < (argc - 1)) && (argv[i + 1]) && (*argv[i + 1] != '-')) {
+ Printf(opt, " %s", argv[i + 1]);
+ i++;
+ } else {
+ Printf(opt, " 1");
+ }
+ if (!noopt) {
+ /* Printf(stdout,"%s\n", opt); */
+ Preprocessor_define(opt, 0);
+ }
+ Delete(opt);
+ }
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * decode_numbers_list()
+ *
+ * Decode comma separated list into a binary number of the inputs or'd together
+ * eg list="1,4" will return (2^0 || 2^3) = 0x1001
+ * ----------------------------------------------------------------------------- */
+
+static unsigned int decode_numbers_list(String *numlist) {
+ unsigned int decoded_number = 0;
+ if (numlist) {
+ List *numbers = Split(numlist, ',', INT_MAX);
+ if (numbers && Len(numbers) > 0) {
+ for (Iterator it = First(numbers); it.item; it = Next(it)) {
+ String *numstring = it.item;
+ // TODO: check that it is a number
+ int number = atoi(Char(numstring));
+ if (number > 0 && number <= 16) {
+ decoded_number |= (1 << (number-1));
+ }
+ }
+ }
+ }
+ return decoded_number;
+}
+
+/* -----------------------------------------------------------------------------
+ * Sets the output directory for language specific (proxy) files from the
+ * C wrapper file if not set and corrects the directory name and adds a trailing
+ * file separator if necessary.
+ * ----------------------------------------------------------------------------- */
+
+static void configure_outdir(const String *c_wrapper_outfile) {
+
+ // Use the C wrapper file's directory if the output directory has not been set by user
+ if (!outdir || Len(outdir) == 0)
+ outdir = Swig_file_dirname(c_wrapper_outfile);
+
+ Swig_filename_correct(outdir);
+
+ // Add trailing file delimiter if not present in output directory name
+ if (Len(outdir) > 0) {
+ const char *outd = Char(outdir);
+ if (strcmp(outd + strlen(outd) - strlen(SWIG_FILE_DELIMITER), SWIG_FILE_DELIMITER) != 0)
+ Printv(outdir, SWIG_FILE_DELIMITER, NIL);
+ }
+}
+
+/* This function sets the name of the configuration file */
+void SWIG_config_file(const_String_or_char_ptr filename) {
+ lang_config = NewString(filename);
+}
+
+/* Sets the target language subdirectory name */
+void SWIG_library_directory(const char *subdirectory) {
+ LangSubDir = NewString(subdirectory);
+}
+
+// Returns the directory for generating language specific files (non C/C++ files)
+const String *SWIG_output_directory() {
+ assert(outdir);
+ return outdir;
+}
+
+void SWIG_config_cppext(const char *ext) {
+ cpp_extension = ext;
+}
+
+List *SWIG_output_files() {
+ assert(all_output_files);
+ return all_output_files;
+}
+
+void SWIG_setfeature(const char *cfeature, const char *cvalue) {
+ Hash *features_hash = Swig_cparse_features();
+ String *name = NewString("");
+ String *fname = NewString(cfeature);
+ String *fvalue = NewString(cvalue);
+ Swig_feature_set(features_hash, name, 0, fname, fvalue, 0);
+ Delete(name);
+ Delete(fname);
+ Delete(fvalue);
+}
+
+
+void SWIG_setfeatures(const char *c) {
+ char feature[64];
+ char *fb = feature;
+ char *fe = fb + 63;
+ Hash *features_hash = Swig_cparse_features();
+ String *name = NewString("");
+ /* Printf(stderr,"all features %s\n", c); */
+ while (*c) {
+ char *f = fb;
+ String *fname = NewString("feature:");
+ String *fvalue = NewString("");
+ while ((f != fe) && *c != '=' && *c != ',' && *c) {
+ *(f++) = *(c++);
+ }
+ *f = 0;
+ Printf(fname, "%s", feature);
+ if (*c && *(c++) == '=') {
+ char value[64];
+ char *v = value;
+ char *ve = v + 63;
+ while ((v != ve) && *c != ',' && *c && !isspace(*c)) {
+ *(v++) = *(c++);
+ }
+ *v = 0;
+ Printf(fvalue, "%s", value);
+ } else {
+ Printf(fvalue, "1");
+ }
+ /* Printf(stderr,"%s %s\n", fname, fvalue); */
+ Swig_feature_set(features_hash, name, 0, fname, fvalue, 0);
+ Delete(fname);
+ Delete(fvalue);
+ }
+ Delete(name);
+}
+
+/* This function handles the -external-runtime command option */
+static void SWIG_dump_runtime() {
+ String *outfile;
+ File *runtime;
+ String *s;
+
+ outfile = external_runtime_name;
+ if (!outfile) {
+ outfile = lang->defaultExternalRuntimeFilename();
+ if (!outfile) {
+ Printf(stderr, "*** Please provide a filename for the external runtime\n");
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ runtime = NewFile(outfile, "w", SWIG_output_files());
+ if (!runtime) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+
+ Swig_banner(runtime);
+ Printf(runtime, "\n");
+
+ s = Swig_include_sys("swiglabels.swg");
+ if (!s) {
+ Printf(stderr, "*** Unable to open 'swiglabels.swg'\n");
+ Delete(runtime);
+ Exit(EXIT_FAILURE);
+ }
+ Printf(runtime, "%s", s);
+ Delete(s);
+
+ s = Swig_include_sys("swigerrors.swg");
+ if (!s) {
+ Printf(stderr, "*** Unable to open 'swigerrors.swg'\n");
+ Delete(runtime);
+ Exit(EXIT_FAILURE);
+ }
+ Printf(runtime, "%s", s);
+ Delete(s);
+
+ s = Swig_include_sys("swigrun.swg");
+ if (!s) {
+ Printf(stderr, "*** Unable to open 'swigrun.swg'\n");
+ Delete(runtime);
+ Exit(EXIT_FAILURE);
+ }
+ Printf(runtime, "%s", s);
+ Delete(s);
+
+ s = lang->runtimeCode();
+ Printf(runtime, "%s", s);
+ Delete(s);
+
+ s = Swig_include_sys("runtime.swg");
+ if (!s) {
+ Printf(stderr, "*** Unable to open 'runtime.swg'\n");
+ Delete(runtime);
+ Exit(EXIT_FAILURE);
+ }
+ Printf(runtime, "%s", s);
+ Delete(s);
+
+ Delete(runtime);
+ Exit(EXIT_SUCCESS);
+}
+
+static void getoptions(int argc, char *argv[]) {
+ int i;
+ // Get options
+ for (i = 1; i < argc; i++) {
+ if (argv[i] && !Swig_check_marked(i)) {
+ if (strncmp(argv[i], "-I-", 3) == 0) {
+ // Don't push/pop directories
+ Swig_set_push_dir(0);
+ Swig_mark_arg(i);
+ } else if (strncmp(argv[i], "-I", 2) == 0) {
+ // Add a new directory search path
+ Swig_add_directory((String_or_char*)(argv[i] + 2));
+ Swig_mark_arg(i);
+ } else if (strncmp(argv[i], "-D", 2) == 0) {
+ String *d = NewString(argv[i] + 2);
+ if (Replace(d, "=", " ", DOH_REPLACE_FIRST) == 0) {
+ // Match C preprocessor behaviour whereby -DFOO sets FOO=1.
+ Append(d, " 1");
+ }
+ Preprocessor_define((DOH *) d, 0);
+ Delete(d);
+ // Create a symbol
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-E") == 0) {
+ cpp_only = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nopreprocess") == 0) {
+ no_cpp = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-verbose") == 0) || (strcmp(argv[i], "-v") == 0)) {
+ Verbose = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-c++") == 0) {
+ CPlusPlus = 1;
+ Preprocessor_define((DOH *) "__cplusplus __cplusplus", 0);
+ Swig_cparse_cplusplus(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-c++out") == 0) {
+ // Undocumented
+ Swig_cparse_cplusplusout(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-fcompact") == 0) {
+ Wrapper_compact_print_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-fvirtual") == 0) {
+ Wrapper_virtual_elimination_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-fastdispatch") == 0) {
+ Wrapper_fast_dispatch_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nofastdispatch") == 0) {
+ Wrapper_fast_dispatch_mode_set(0);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-naturalvar") == 0) {
+ Wrapper_naturalvar_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-directors") == 0) {
+ SWIG_setfeature("feature:director", "1");
+ Wrapper_director_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-dirprot") == 0) {
+ Wrapper_director_protected_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nodirprot") == 0) {
+ Wrapper_director_protected_mode_set(0);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-pcreversion") == 0) {
+ String *version = Swig_pcre_version();
+ Printf(stdout, "%s\n", version);
+ Delete(version);
+ Swig_mark_arg(i);
+ Exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "-small") == 0) {
+ Wrapper_compact_print_mode_set(1);
+ Wrapper_virtual_elimination_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-runtime") == 0) { // Used to also accept -c. removed in swig-1.3.36
+ Swig_mark_arg(i);
+ Swig_warning(WARN_DEPRECATED_OPTC, "SWIG", 1, "-runtime, -noruntime command line options are deprecated.\n");
+ SwigRuntime = 1;
+ } else if (strcmp(argv[i], "-noruntime") == 0) {
+ Swig_mark_arg(i);
+ Swig_warning(WARN_DEPRECATED_OPTC, "SWIG", 1, "-runtime, -noruntime command line options are deprecated.\n");
+ SwigRuntime = 2;
+ } else if (strcmp(argv[i], "-external-runtime") == 0) {
+ external_runtime = 1;
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ external_runtime_name = NewString(argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ i++;
+ }
+ } else if ((strcmp(argv[i], "-make_default") == 0) || (strcmp(argv[i], "-makedefault") == 0)) {
+ GenerateDefault = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-no_default") == 0) || (strcmp(argv[i], "-nodefault") == 0)) {
+ GenerateDefault = 0;
+ Swig_warning(WARN_DEPRECATED_NODEFAULT, "SWIG", 1, "dangerous, use -nodefaultctor, -nodefaultdtor instead.\n");
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-nodefaultctor") == 0)) {
+ SWIG_setfeature("feature:nodefaultctor", "1");
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-nodefaultdtor") == 0)) {
+ SWIG_setfeature("feature:nodefaultdtor", "1");
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-copyctor") == 0)) {
+ SWIG_setfeature("feature:copyctor", "1");
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-noexcept") == 0) {
+ NoExcept = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-noextern") == 0) {
+ Swig_warning(WARN_DEPRECATED_NOEXTERN, "SWIG", 1, "-noextern command line option is deprecated; extern is no longer generated by default.\n");
+ AddExtern = 0;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-addextern") == 0) {
+ AddExtern = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-debug-template") == 0) || (strcmp(argv[i], "-debug_template") == 0) || (strcmp(argv[i], "-show_templates") == 0)) {
+ Swig_cparse_debug_templates(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-templatereduce") == 0) {
+ SWIG_cparse_template_reduce(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-notemplatereduce") == 0) {
+ SWIG_cparse_template_reduce(0);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-macroerrors") == 0) {
+ Swig_cparse_follow_locators(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-swiglib") == 0) {
+ Printf(stdout, "%s\n", SwigLib);
+ if (SwigLibWinUnix)
+ Printf(stdout, "%s\n", SwigLibWinUnix);
+ Exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "-o") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ outfile_name = NewString(argv[i + 1]);
+ Swig_filename_correct(outfile_name);
+ if (!outfile_name_h || !dependencies_file) {
+ char *ext = strrchr(Char(outfile_name), '.');
+ String *basename = ext ? NewStringWithSize(Char(outfile_name), (int)(Char(ext) - Char(outfile_name))) : NewString(outfile_name);
+ if (!dependencies_file) {
+ dependencies_file = NewStringf("%s.%s", basename, depends_extension);
+ }
+ if (!outfile_name_h) {
+ Printf(basename, ".%s", hpp_extension);
+ outfile_name_h = NewString(basename);
+ }
+ Delete(basename);
+ }
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-oh") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ outfile_name_h = NewString(argv[i + 1]);
+ Swig_filename_correct(outfile_name_h);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-fakeversion") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ Swig_set_fakeversion(argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-version") == 0) {
+ fprintf(stdout, "\nSWIG Version %s\n", Swig_package_version());
+ fprintf(stdout, "\nCompiled with %s [%s]\n", SWIG_CXX, SWIG_PLATFORM);
+ fprintf(stdout, "\nConfigured options: %cpcre\n",
+#ifdef HAVE_PCRE
+ '+'
+#else
+ '-'
+#endif
+ );
+ fprintf(stdout, "\nPlease see %s for reporting bugs and further information\n", PACKAGE_BUGREPORT);
+ Exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "-copyright") == 0) {
+ fprintf(stdout, "\nSWIG Version %s\n", Swig_package_version());
+ fprintf(stdout, "Copyright (c) 1995-1998\n");
+ fprintf(stdout, "University of Utah and the Regents of the University of California\n");
+ fprintf(stdout, "Copyright (c) 1998-2005\n");
+ fprintf(stdout, "University of Chicago\n");
+ fprintf(stdout, "Copyright (c) 2005-2006\n");
+ fprintf(stdout, "Arizona Board of Regents (University of Arizona)\n");
+ Exit(EXIT_SUCCESS);
+ } else if (strncmp(argv[i], "-l", 2) == 0) {
+ // Add a new directory search path
+ Append(libfiles, argv[i] + 2);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-co") == 0) {
+ checkout = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-features") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ SWIG_setfeatures(argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-freeze") == 0) {
+ freeze = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-includeall") == 0) {
+ Preprocessor_include_all(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-importall") == 0) {
+ Preprocessor_import_all(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-ignoremissing") == 0) {
+ Preprocessor_ignore_missing(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-cpperraswarn") == 0) {
+ Preprocessor_error_as_warning(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nocpperraswarn") == 0) {
+ Preprocessor_error_as_warning(0);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-cppext") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ SWIG_config_cppext(argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if ((strcmp(argv[i], "-debug-typemap") == 0) || (strcmp(argv[i], "-debug_typemap") == 0) || (strcmp(argv[i], "-tm_debug") == 0)) {
+ tm_debug = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-tmsearch") == 0) {
+ Swig_typemap_search_debug_set();
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-tmused") == 0) {
+ Swig_typemap_used_debug_set();
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-module") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ ModuleName = NewString(argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-M") == 0) {
+ depend = 1;
+ depend_only = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-MM") == 0) {
+ depend = 2;
+ depend_only = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-MF") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ dependencies_file = NewString(argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-MD") == 0) {
+ depend = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-MMD") == 0) {
+ depend = 2;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-MP") == 0) {
+ depend_phony = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-MT") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ if (!dependencies_target)
+ dependencies_target = NewString(argv[i + 1]);
+ else
+ Printf(dependencies_target, " %s", argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-outdir") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ outdir = NewString(argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-outcurrentdir") == 0) {
+ Swig_mark_arg(i);
+ outcurrentdir = 1;
+ } else if (strcmp(argv[i], "-Wall") == 0) {
+ Swig_mark_arg(i);
+ Swig_warnall();
+ } else if (strcmp(argv[i], "-Wallkw") == 0) {
+ allkw = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-Werror") == 0) {
+ werror = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-Wextra") == 0) {
+ Swig_mark_arg(i);
+ Swig_warnfilter(EXTRA_WARNINGS, 0);
+ } else if (strncmp(argv[i], "-w", 2) == 0) {
+ Swig_mark_arg(i);
+ Swig_warnfilter(argv[i] + 2, 1);
+ } else if (strcmp(argv[i], "-debug-quiet") == 0) {
+ Swig_print_quiet(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-symtabs") == 0) {
+ dump_symtabs = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-symbols") == 0) {
+ dump_symbols = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-csymbols") == 0) {
+ dump_csymbols = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-lsymbols") == 0) {
+ dump_lang_symbols = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-debug-tags") == 0) || (strcmp(argv[i], "-dump_tags") == 0)) {
+ dump_tags = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-top") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ String *dump_list = NewString(argv[i + 1]);
+ dump_top = decode_numbers_list(dump_list);
+ if (dump_top < STAGE1 || dump_top >= STAGEOVERFLOW)
+ Swig_arg_error();
+ else
+ Swig_mark_arg(i + 1);
+ Delete(dump_list);
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-debug-module") == 0) {
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ String *dump_list = NewString(argv[i + 1]);
+ dump_module = decode_numbers_list(dump_list);
+ if (dump_module < STAGE1 || dump_module >= STAGEOVERFLOW)
+ Swig_arg_error();
+ else
+ Swig_mark_arg(i + 1);
+ Delete(dump_list);
+ } else {
+ Swig_arg_error();
+ }
+ } else if ((strcmp(argv[i], "-dump_tree") == 0) || (strcmp(argv[i], "-dump_top") == 0)) {
+ dump_top |= STAGE4;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-dump_module") == 0) {
+ dump_module |= STAGE4;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-dump_parse_module") == 0) {
+ dump_module |= STAGE1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-dump_parse_top") == 0) {
+ dump_top |= STAGE1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-dump_xml") == 0) {
+ dump_xml = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-xmlout") == 0) {
+ dump_xml = 1;
+ Swig_mark_arg(i);
+ if (argv[i + 1]) {
+ xmlout = NewString(argv[i + 1]);
+ Swig_mark_arg(i + 1);
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-nocontract") == 0) {
+ Swig_mark_arg(i);
+ Swig_contract_mode_set(0);
+ } else if ((strcmp(argv[i], "-debug-typedef") == 0) || (strcmp(argv[i], "-dump_typedef") == 0)) {
+ dump_typedef = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-debug-classes") == 0) || (strcmp(argv[i], "-dump_classes") == 0)) {
+ dump_classes = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-debug-memory") == 0) || (strcmp(argv[i], "-dump_memory") == 0)) {
+ memory_debug = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-Fstandard") == 0) {
+ Swig_error_msg_format(EMF_STANDARD);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-Fmicrosoft") == 0) {
+ Swig_error_msg_format(EMF_MICROSOFT);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-O") == 0) {
+ Wrapper_virtual_elimination_mode_set(1);
+ Wrapper_fast_dispatch_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage1, stdout);
+ fputs(usage2, stdout);
+ fputs(usage3, stdout);
+ fputs(usage4, stdout);
+ Swig_mark_arg(i);
+ help = 1;
+ }
+ }
+ }
+}
+
+static void SWIG_exit_handler(int status);
+
+int SWIG_main(int argc, char *argv[], const TargetLanguageModule *tlm) {
+ char *c;
+
+ /* Set function for Exit() to call. */
+ SetExitHandler(SWIG_exit_handler);
+
+ /* Initialize the SWIG core */
+ Swig_init();
+
+ // Default warning suppression
+ Swig_warnfilter(EXTRA_WARNINGS, 1);
+
+ // Initialize the preprocessor
+ Preprocessor_init();
+
+ // Set lang to a dummy value if no target language was specified so we
+ // can process options enough to handle -version, etc.
+ lang = tlm ? tlm->fac() : new Language;
+
+ // Set up some default symbols (available in both SWIG interface files
+ // and C files)
+
+ Preprocessor_define((DOH *) "SWIG 1", 0);
+ Preprocessor_define((DOH *) "__STDC__", 0);
+
+ String *vers = Swig_package_version_hex();
+ Preprocessor_define(vers, 0);
+ Delete(vers);
+
+ Swig_contract_mode_set(1);
+
+ /* Turn off directors mode */
+ Wrapper_director_mode_set(0);
+ Wrapper_director_protected_mode_set(1);
+
+ // Inform the parser if the nested classes should be ignored unless explicitly told otherwise via feature:flatnested
+ ignore_nested_classes = lang->nestedClassesSupport() == Language::NCS_Unknown ? 1 : 0;
+
+ kwargs_supported = lang->kwargsSupport() ? 1 : 0;
+
+ // Create Library search directories
+
+ // Check for SWIG_LIB environment variable
+ if ((c = getenv("SWIG_LIB")) == (char *) 0) {
+#if defined(_WIN32)
+ char buf[MAX_PATH];
+ char *p;
+ if (!(GetModuleFileName(0, buf, MAX_PATH) == 0 || (p = strrchr(buf, '\\')) == 0)) {
+ *(p + 1) = '\0';
+ SwigLib = NewStringf("%sLib", buf); // Native windows installation path
+ } else {
+ SwigLib = NewStringf(""); // Unexpected error
+ }
+ if (Len(SWIG_LIB_WIN_UNIX) > 0)
+ SwigLibWinUnix = NewString(SWIG_LIB_WIN_UNIX); // Unix installation path using a drive letter (for msys/mingw)
+#else
+ SwigLib = NewString(SWIG_LIB);
+#endif
+ } else {
+ SwigLib = NewString(c);
+ }
+
+ libfiles = NewList();
+ all_output_files = NewList();
+
+ /* Check for SWIG_FEATURES environment variable */
+
+ getoptions(argc, argv);
+
+ // Define the __cplusplus symbol
+ if (CPlusPlus)
+ Preprocessor_define((DOH *) "__cplusplus __cplusplus", 0);
+
+ // Parse language dependent options
+ lang->main(argc, argv);
+
+ if (help) {
+ Printf(stdout, "\nNote: 'swig -<lang> -help' displays options for a specific target language.\n\n");
+ Exit(EXIT_SUCCESS); // Exit if we're in help mode
+ }
+
+ // Check all of the options to make sure we're cool.
+ // Don't check for an input file if -external-runtime is passed
+ Swig_check_options(external_runtime ? 0 : 1);
+
+ if (CPlusPlus && cparse_cplusplusout) {
+ Printf(stderr, "The -c++out option is for C input but C++ input has been requested via -c++\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ install_opts(argc, argv);
+
+ // Add language dependent directory to the search path
+ {
+ String *rl = NewString("");
+ Printf(rl, ".%sswig_lib%s%s", SWIG_FILE_DELIMITER, SWIG_FILE_DELIMITER, LangSubDir);
+ Swig_add_directory(rl);
+ if (SwigLibWinUnix) {
+ rl = NewString("");
+ Printf(rl, "%s%s%s", SwigLibWinUnix, SWIG_FILE_DELIMITER, LangSubDir);
+ Swig_add_directory(rl);
+ }
+ rl = NewString("");
+ Printf(rl, "%s%s%s", SwigLib, SWIG_FILE_DELIMITER, LangSubDir);
+ Swig_add_directory(rl);
+ }
+
+ Swig_add_directory((String *) "." SWIG_FILE_DELIMITER "swig_lib");
+ if (SwigLibWinUnix)
+ Swig_add_directory((String *) SwigLibWinUnix);
+ Swig_add_directory(SwigLib);
+
+ if (Verbose) {
+ Printf(stdout, "Language subdirectory: %s\n", LangSubDir);
+ Printf(stdout, "Search paths:\n");
+ List *sp = Swig_search_path();
+ Iterator s;
+ for (s = First(sp); s.item; s = Next(s)) {
+ Printf(stdout, " %s\n", s.item);
+ }
+ }
+ // handle the -external-runtime argument
+ if (external_runtime)
+ SWIG_dump_runtime();
+
+ // If we made it this far, looks good. go for it....
+
+ input_file = NewString(argv[argc - 1]);
+ Swig_filename_correct(input_file);
+
+ // If the user has requested to check out a file, handle that
+ if (checkout) {
+ DOH *s;
+ String *outfile = input_file;
+ if (outfile_name)
+ outfile = outfile_name;
+
+ if (Verbose)
+ Printf(stdout, "Handling checkout...\n");
+
+ s = Swig_include(input_file);
+ if (!s) {
+ Printf(stderr, "Unable to locate '%s' in the SWIG library.\n", input_file);
+ } else {
+ FILE *f = Swig_open(outfile);
+ if (f) {
+ fclose(f);
+ Printf(stderr, "File '%s' already exists. Checkout aborted.\n", outfile);
+ } else {
+ File *f_outfile = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_outfile) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ } else {
+ if (Verbose)
+ Printf(stdout, "'%s' checked out from the SWIG library.\n", outfile);
+ Printv(f_outfile, s, NIL);
+ Delete(f_outfile);
+ }
+ }
+ }
+ } else {
+ // Run the preprocessor
+ if (Verbose)
+ Printf(stdout, "Preprocessing...\n");
+
+ {
+ int i;
+ String *fs = NewString("");
+ FILE *df = Swig_open(input_file);
+ if (!df) {
+ df = Swig_include_open(input_file);
+ if (!df) {
+ char *cfile = Char(input_file);
+ if (cfile && cfile[0] == '-') {
+ Printf(stderr, "Unable to find option or file '%s', ", input_file);
+ Printf(stderr, "Use 'swig -help' for more information.\n");
+ } else {
+ Printf(stderr, "Unable to find file '%s'.\n", input_file);
+ }
+ Exit(EXIT_FAILURE);
+ } else {
+ Swig_warning(WARN_DEPRECATED_INPUT_FILE, "SWIG", 1, "Use of the include path to find the input file is deprecated and will not work with ccache. Please include the path when specifying the input file.\n"); // so that behaviour is like c/c++ compilers
+ }
+ }
+
+ if (!tlm) {
+ Printf(stderr, "No target language specified.\n");
+ Printf(stderr, "Use 'swig -help' for more information.\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ if (!no_cpp) {
+ fclose(df);
+ Printf(fs, "%%include <swig.swg>\n");
+ if (allkw) {
+ Printf(fs, "%%include <allkw.swg>\n");
+ }
+ if (lang_config) {
+ Printf(fs, "\n%%include <%s>\n", lang_config);
+ }
+ Printf(fs, "%%include(maininput=\"%s\") \"%s\"\n", Swig_filename_escape(input_file), Swig_filename_escape(Swig_last_file()));
+ for (i = 0; i < Len(libfiles); i++) {
+ Printf(fs, "\n%%include \"%s\"\n", Swig_filename_escape(Getitem(libfiles, i)));
+ }
+ Seek(fs, 0, SEEK_SET);
+ cpps = Preprocessor_parse(fs);
+ Delete(fs);
+ } else {
+ cpps = Swig_read_file(df);
+ fclose(df);
+ }
+ if (Swig_error_count()) {
+ Exit(EXIT_FAILURE);
+ }
+ if (cpp_only) {
+ Printf(stdout, "%s", cpps);
+ Exit(EXIT_SUCCESS);
+ }
+ if (depend) {
+ if (!no_cpp) {
+ String *outfile;
+ File *f_dependencies_file = 0;
+
+ String *inputfile_filename = outcurrentdir ? Swig_file_filename(input_file): Copy(input_file);
+ String *basename = Swig_file_basename(inputfile_filename);
+ if (!outfile_name) {
+ if (CPlusPlus || lang->cplus_runtime_mode()) {
+ outfile = NewStringf("%s_wrap.%s", basename, cpp_extension);
+ } else {
+ outfile = NewStringf("%s_wrap.c", basename);
+ }
+ } else {
+ outfile = NewString(outfile_name);
+ }
+ if (dependencies_file && Len(dependencies_file) != 0) {
+ f_dependencies_file = NewFile(dependencies_file, "w", SWIG_output_files());
+ if (!f_dependencies_file) {
+ FileErrorDisplay(dependencies_file);
+ Exit(EXIT_FAILURE);
+ }
+ } else if (!depend_only) {
+ String *filename = NewStringf("%s_wrap.%s", basename, depends_extension);
+ f_dependencies_file = NewFile(filename, "w", SWIG_output_files());
+ if (!f_dependencies_file) {
+ FileErrorDisplay(filename);
+ Exit(EXIT_FAILURE);
+ }
+ } else
+ f_dependencies_file = stdout;
+ if (dependencies_target) {
+ Printf(f_dependencies_file, "%s: ", Swig_filename_escape_space(dependencies_target));
+ } else {
+ Printf(f_dependencies_file, "%s: ", Swig_filename_escape_space(outfile));
+ }
+ List *files = Preprocessor_depend();
+ List *phony_targets = NewList();
+ for (int i = 0; i < Len(files); i++) {
+ int use_file = 1;
+ if (depend == 2) {
+ if ((Strncmp(Getitem(files, i), SwigLib, Len(SwigLib)) == 0) || (SwigLibWinUnix && (Strncmp(Getitem(files, i), SwigLibWinUnix, Len(SwigLibWinUnix)) == 0)))
+ use_file = 0;
+ }
+ if (use_file) {
+ Printf(f_dependencies_file, "\\\n %s ", Swig_filename_escape_space(Getitem(files, i)));
+ if (depend_phony)
+ Append(phony_targets, Getitem(files, i));
+ }
+ }
+ Printf(f_dependencies_file, "\n");
+ if (depend_phony) {
+ for (int i = 0; i < Len(phony_targets); i++) {
+ Printf(f_dependencies_file, "\n%s:\n", Swig_filename_escape_space(Getitem(phony_targets, i)));
+ }
+ }
+
+ if (f_dependencies_file != stdout)
+ Delete(f_dependencies_file);
+ if (depend_only)
+ Exit(EXIT_SUCCESS);
+ Delete(inputfile_filename);
+ Delete(basename);
+ Delete(phony_targets);
+ } else {
+ Printf(stderr, "Cannot generate dependencies with -nopreprocess\n");
+ // Actually we could but it would be inefficient when just generating dependencies, as it would be done after Swig_cparse
+ Exit(EXIT_FAILURE);
+ }
+ }
+ Seek(cpps, 0, SEEK_SET);
+ }
+
+ /* Register a null file with the file handler */
+ Swig_register_filebyname("null", NewString(""));
+
+ // Pass control over to the specific language interpreter
+ if (Verbose) {
+ fprintf(stdout, "Starting language-specific parse...\n");
+ fflush(stdout);
+ }
+
+ Node *top = Swig_cparse(cpps);
+
+ if (dump_top & STAGE1) {
+ Printf(stdout, "debug-top stage 1\n");
+ Swig_print_tree(top);
+ }
+ if (dump_module & STAGE1) {
+ Printf(stdout, "debug-module stage 1\n");
+ Swig_print_tree(Getattr(top, "module"));
+ }
+ if (!CPlusPlus) {
+ if (Verbose)
+ Printf(stdout, "Processing unnamed structs...\n");
+ Swig_nested_name_unnamed_c_structs(top);
+ }
+ Swig_extend_unused_check();
+
+ if (Verbose) {
+ Printf(stdout, "Processing types...\n");
+ }
+ Swig_process_types(top);
+
+ if (dump_top & STAGE2) {
+ Printf(stdout, "debug-top stage 2\n");
+ Swig_print_tree(top);
+ }
+ if (dump_module & STAGE2) {
+ Printf(stdout, "debug-module stage 2\n");
+ Swig_print_tree(Getattr(top, "module"));
+ }
+
+ if (Verbose) {
+ Printf(stdout, "C++ analysis...\n");
+ }
+ Swig_default_allocators(top);
+
+ if (CPlusPlus) {
+ if (Verbose)
+ Printf(stdout, "Processing nested classes...\n");
+ Swig_nested_process_classes(top);
+ }
+
+ if (dump_top & STAGE3) {
+ Printf(stdout, "debug-top stage 3\n");
+ Swig_print_tree(top);
+ }
+ if (top && (dump_module & STAGE3)) {
+ Printf(stdout, "debug-module stage 3\n");
+ Swig_print_tree(Getattr(top, "module"));
+ }
+
+ if (Verbose) {
+ Printf(stdout, "Generating wrappers...\n");
+ }
+
+ if (top && dump_classes) {
+ Hash *classes = Getattr(top, "classes");
+ if (classes) {
+ Printf(stdout, "Classes\n");
+ Printf(stdout, "------------\n");
+ Iterator ki;
+ for (ki = First(classes); ki.key; ki = Next(ki)) {
+ Printf(stdout, "%s\n", ki.key);
+ }
+ }
+ }
+
+ if (dump_typedef) {
+ SwigType_print_scope();
+ }
+
+ if (dump_symtabs) {
+ Swig_symbol_print_tables(Swig_symbol_global_scope());
+ Swig_symbol_print_tables_summary();
+ }
+
+ if (dump_symbols) {
+ Swig_symbol_print_symbols();
+ }
+
+ if (dump_csymbols) {
+ Swig_symbol_print_csymbols();
+ }
+
+ if (dump_tags) {
+ Swig_print_tags(top, 0);
+ }
+ if (top) {
+ if (!Getattr(top, "name")) {
+ Printf(stderr, "No module name specified using %%module or -module.\n");
+ Exit(EXIT_FAILURE);
+ } else {
+ /* Set some filename information on the object */
+ String *infile = scanner_get_main_input_file();
+ if (!infile) {
+ Printf(stderr, "Missing input file in preprocessed output.\n");
+ Exit(EXIT_FAILURE);
+ }
+ Setattr(top, "infile", infile); // Note: if nopreprocess then infile is the original input file, otherwise input_file
+ Setattr(top, "inputfile", input_file);
+
+ String *infile_filename = outcurrentdir ? Swig_file_filename(infile): Copy(infile);
+ String *basename = Swig_file_basename(infile_filename);
+ if (!outfile_name) {
+ if (CPlusPlus || lang->cplus_runtime_mode()) {
+ Setattr(top, "outfile", NewStringf("%s_wrap.%s", basename, cpp_extension));
+ } else {
+ Setattr(top, "outfile", NewStringf("%s_wrap.c", basename));
+ }
+ } else {
+ Setattr(top, "outfile", outfile_name);
+ }
+ if (!outfile_name_h) {
+ Setattr(top, "outfile_h", NewStringf("%s_wrap.%s", basename, hpp_extension));
+ } else {
+ Setattr(top, "outfile_h", outfile_name_h);
+ }
+ configure_outdir(Getattr(top, "outfile"));
+ if (Swig_contract_mode_get()) {
+ Swig_contracts(top);
+ }
+
+ // Check the extension for a c/c++ file. If so, we're going to declare everything we see as "extern"
+ ForceExtern = check_extension(input_file);
+
+ if (tlm->status == Experimental) {
+ Swig_warning(WARN_LANG_EXPERIMENTAL, "SWIG", 1, "Experimental target language. "
+ "Target language %s specified by %s is an experimental language. "
+ "Please read about SWIG experimental languages, https://swig.org/Doc4.0/Introduction.html#Introduction_experimental_status.\n",
+ tlm->help ? tlm->help : "", tlm->name);
+ }
+
+ lang->top(top);
+
+ Delete(infile_filename);
+ Delete(basename);
+ }
+ }
+ if (dump_lang_symbols) {
+ lang->dumpSymbols();
+ }
+ if (dump_top & STAGE4) {
+ Printf(stdout, "debug-top stage 4\n");
+ Swig_print_tree(top);
+ }
+ if (dump_module & STAGE4) {
+ Printf(stdout, "debug-module stage 4\n");
+ Swig_print_tree(Getattr(top, "module"));
+ }
+ if (dump_xml && top) {
+ delete lang;
+ lang = 0;
+ Swig_print_xml(top, xmlout);
+ }
+ Delete(top);
+ }
+ if (tm_debug)
+ Swig_typemap_debug();
+ if (memory_debug)
+ DohMemoryDebug();
+
+ char *outfiles = getenv("CCACHE_OUTFILES");
+ if (outfiles) {
+ File *f_outfiles = NewFile(outfiles, "w", 0);
+ if (!f_outfiles) {
+ Printf(stderr, "Failed to write list of output files to the filename '%s' specified in CCACHE_OUTFILES environment variable - ", outfiles);
+ FileErrorDisplay(outfiles);
+ Exit(EXIT_FAILURE);
+ } else {
+ int i;
+ for (i = 0; i < Len(all_output_files); i++)
+ Printf(f_outfiles, "%s\n", Getitem(all_output_files, i));
+ Delete(f_outfiles);
+ }
+ }
+
+ // Deletes
+ Delete(libfiles);
+ Preprocessor_delete();
+
+ while (freeze) {
+ }
+
+ delete lang;
+
+ int error_count = werror ? Swig_warn_count() : 0;
+ error_count += Swig_error_count();
+
+ if (error_count != 0)
+ Exit(EXIT_FAILURE);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * SWIG_exit_handler()
+ *
+ * Cleanup and either freeze or exit
+ * ----------------------------------------------------------------------------- */
+
+static void SWIG_exit_handler(int status) {
+ while (freeze) {
+ }
+
+ if (status > 0) {
+ CloseAllOpenFiles();
+
+ /* Remove all generated files */
+ if (all_output_files) {
+ for (int i = 0; i < Len(all_output_files); i++) {
+ String *filename = Getitem(all_output_files, i);
+ int removed = remove(Char(filename));
+ if (removed == -1)
+ fprintf(stderr, "On exit, could not delete file %s: %s\n", Char(filename), strerror(errno));
+ }
+ }
+ }
+}
diff --git a/contrib/tools/swig/Source/Modules/mzscheme.cxx b/contrib/tools/swig/Source/Modules/mzscheme.cxx
new file mode 100644
index 0000000000..e22f8bb7ad
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/mzscheme.cxx
@@ -0,0 +1,802 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * mzscheme.cxx
+ *
+ * Mzscheme language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include <ctype.h>
+
+static const char *usage = "\
+Mzscheme Options (available with -mzscheme)\n\
+ -declaremodule - Create extension that declares a module\n\
+ -dynamic-load <lib>,[lib,...] - Do not link with these libraries, dynamic load them\n\
+ -noinit - Do not emit module initialization code\n\
+ -prefix <name> - Set a prefix <name> to be prepended to all names\n\
+";
+
+static String *fieldnames_tab = 0;
+static String *convert_tab = 0;
+static String *convert_proto_tab = 0;
+static String *struct_name = 0;
+static String *mangled_struct_name = 0;
+
+static String *prefix = 0;
+static bool declaremodule = false;
+static bool noinit = false;
+static String *load_libraries = NULL;
+static String *module = 0;
+static const char *mzscheme_path = "mzscheme";
+static String *init_func_def = 0;
+
+static File *f_begin = 0;
+static File *f_runtime = 0;
+static File *f_header = 0;
+static File *f_wrappers = 0;
+static File *f_init = 0;
+
+// Used for garbage collection
+static int exporting_destructor = 0;
+static String *swigtype_ptr = 0;
+static String *cls_swigtype = 0;
+
+class MZSCHEME:public Language {
+public:
+
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+
+ int i;
+
+ SWIG_library_directory(mzscheme_path);
+
+ // Look for certain command line options
+ for (i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage, stdout);
+ Exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "-prefix") == 0) {
+ if (argv[i + 1]) {
+ prefix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-declaremodule") == 0) {
+ declaremodule = true;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-noinit") == 0) {
+ noinit = true;
+ Swig_mark_arg(i);
+ }
+ else if (strcmp(argv[i], "-dynamic-load") == 0) {
+ if (argv[i + 1]) {
+ Delete(load_libraries);
+ load_libraries = NewString(argv[i + 1]);
+ Swig_mark_arg(i++);
+ Swig_mark_arg(i);
+ } else {
+ Swig_arg_error();
+ }
+ }
+ }
+ }
+
+ // If a prefix has been specified make sure it ends in a '_' (not actually used!)
+ if (prefix) {
+ const char *px = Char(prefix);
+ if (px[Len(prefix) - 1] != '_')
+ Printf(prefix, "_");
+ } else
+ prefix = NewString("swig_");
+
+ // Add a symbol for this module
+
+ Preprocessor_define("SWIGMZSCHEME 1", 0);
+
+ // Set name of typemaps
+
+ SWIG_typemap_lang("mzscheme");
+
+ // Read in default typemaps */
+ SWIG_config_file("mzscheme.swg");
+ allow_overloading();
+
+ }
+
+ /* ------------------------------------------------------------
+ * top()
+ * ------------------------------------------------------------ */
+
+ virtual int top(Node *n) {
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+
+ init_func_def = NewString("");
+ Swig_register_filebyname("init", init_func_def);
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "MZSCHEME");
+
+ module = Getattr(n, "name");
+
+ Language::top(n);
+
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+ if (!noinit) {
+ if (declaremodule) {
+ Printf(f_init, "#define SWIG_MZSCHEME_CREATE_MENV(env) scheme_primitive_module(scheme_intern_symbol(\"%s\"), env)\n", module);
+ } else {
+ Printf(f_init, "#define SWIG_MZSCHEME_CREATE_MENV(env) (env)\n");
+ }
+ Printf(f_init, "%s\n", Char(init_func_def));
+ if (declaremodule) {
+ Printf(f_init, "\tscheme_finish_primitive_module(menv);\n");
+ }
+ Printf(f_init, "\treturn scheme_void;\n}\n");
+ Printf(f_init, "Scheme_Object *scheme_initialize(Scheme_Env *env) {\n");
+
+ if (load_libraries) {
+ Printf(f_init, "mz_set_dlopen_libraries(\"%s\");\n", load_libraries);
+ }
+
+ Printf(f_init, "\treturn scheme_reload(env);\n");
+ Printf(f_init, "}\n");
+
+ Printf(f_init, "Scheme_Object *scheme_module_name(void) {\n");
+ if (declaremodule) {
+ Printf(f_init, " return scheme_intern_symbol((char*)\"%s\");\n", module);
+ } else {
+ Printf(f_init, " return scheme_make_symbol((char*)\"%s\");\n", module);
+ }
+ Printf(f_init, "}\n");
+ }
+
+ /* Close all of the files */
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+ Dump(f_wrappers, f_begin);
+ Wrapper_pretty_print(f_init, f_begin);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_runtime);
+ Delete(f_begin);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * functionWrapper()
+ * Create a function declaration and register it with the interpreter.
+ * ------------------------------------------------------------ */
+
+ void throw_unhandled_mzscheme_type_error(SwigType *d) {
+ Swig_warning(WARN_TYPEMAP_UNDEF, input_file, line_number, "Unable to handle type %s.\n", SwigType_str(d, 0));
+ }
+
+ /* Return true iff T is a pointer type */
+
+ int
+ is_a_pointer(SwigType *t) {
+ return SwigType_ispointer(SwigType_typedef_resolve_all(t));
+ }
+
+ virtual int functionWrapper(Node *n) {
+ char *iname = GetChar(n, "sym:name");
+ SwigType *d = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ Parm *p;
+
+ Wrapper *f = NewWrapper();
+ String *proc_name = NewString("");
+ String *target = NewString("");
+ String *arg = NewString("");
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+ String *build = NewString("");
+ String *tm;
+ int i = 0;
+ int numargs;
+ int numreq;
+ String *overname = 0;
+
+ if (load_libraries) {
+ ParmList *parms = Getattr(n, "parms");
+ SwigType *type = Getattr(n, "type");
+ String *name = NewString("caller");
+ Setattr(n, "wrap:action", Swig_cresult(type, Swig_cresult_name(), Swig_cfunction_call(name, parms)));
+ }
+
+ // Make a wrapper name for this
+ String *wname = Swig_name_wrapper(iname);
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ } else {
+ if (!addSymbol(iname, n)) {
+ DelWrapper(f);
+ return SWIG_ERROR;
+ }
+ }
+ if (overname) {
+ Append(wname, overname);
+ }
+ Setattr(n, "wrap:name", wname);
+
+ // Build the name for Scheme.
+ Printv(proc_name, iname, NIL);
+ Replaceall(proc_name, "_", "-");
+
+ // writing the function wrapper function
+ Printv(f->def, "static Scheme_Object *", wname, " (", NIL);
+ Printv(f->def, "int argc, Scheme_Object **argv", NIL);
+ Printv(f->def, ")\n{", NIL);
+
+ /* Define the scheme name in C. This define is used by several
+ macros. */
+ Printv(f->def, "#define FUNC_NAME \"", proc_name, "\"", NIL);
+
+ // Emit all of the local variables for holding arguments.
+ emit_parameter_variables(l, f);
+
+ /* Attach the standard typemaps */
+ emit_attach_parmmaps(l, f);
+ Setattr(n, "wrap:parms", l);
+
+ numargs = emit_num_arguments(l);
+ numreq = emit_num_required(l);
+
+ /* Add the holder for the pointer to the function to be opened */
+ if (load_libraries) {
+ Wrapper_add_local(f, "_function_loaded", "static int _function_loaded=(1==0)");
+ Wrapper_add_local(f, "_the_function", "static void *_the_function=NULL");
+ {
+ String *parms = ParmList_protostr(l);
+ String *func = NewStringf("(*caller)(%s)", parms);
+ Wrapper_add_local(f, "caller", SwigType_lstr(d, func)); /*"(*caller)()")); */
+ }
+ }
+
+ // adds local variables
+ Wrapper_add_local(f, "lenv", "int lenv = 1");
+ Wrapper_add_local(f, "values", "Scheme_Object *values[MAXVALUES]");
+
+ if (load_libraries) {
+ Printf(f->code, "if (!_function_loaded) { _the_function=mz_load_function(\"%s\");_function_loaded=(1==1); }\n", iname);
+ Printf(f->code, "if (!_the_function) { scheme_signal_error(\"Cannot load C function '%s'\"); }\n", iname);
+ Printf(f->code, "caller=_the_function;\n");
+ }
+
+ // Now write code to extract the parameters (this is super ugly)
+
+ for (i = 0, p = l; i < numargs; i++) {
+ /* Skip ignored arguments */
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+
+ // Produce names of source and target
+ Clear(target);
+ Clear(arg);
+ String *source = NewStringf("argv[%d]", i);
+ Printf(target, "%s", ln);
+ Printv(arg, Getattr(p, "name"), NIL);
+
+ if (i >= numreq) {
+ Printf(f->code, "if (argc > %d) {\n", i);
+ }
+ // Handle parameter types.
+ if ((tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source);
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ // no typemap found
+ // check if typedef and resolve
+ throw_unhandled_mzscheme_type_error(pt);
+ p = nextSibling(p);
+ }
+ if (i >= numreq) {
+ Printf(f->code, "}\n");
+ }
+ Delete(source);
+ }
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Pass output arguments back to the caller.
+
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Free up any memory allocated for the arguments.
+
+ /* Insert cleanup code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Now write code to make the function call
+
+ String *actioncode = emit_action(n);
+
+ // Now have return value, figure out what to do with it.
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ Replaceall(tm, "$result", "values[0]");
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "1");
+ else
+ Replaceall(tm, "$owner", "0");
+ Printv(f->code, tm, "\n", NIL);
+ } else {
+ throw_unhandled_mzscheme_type_error(d);
+ }
+ emit_return_variable(n, d, f);
+
+ // Dump the argument output code
+ Printv(f->code, Char(outarg), NIL);
+
+ // Dump the argument cleanup code
+ Printv(f->code, Char(cleanup), NIL);
+
+ // Look for any remaining cleanup
+
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printv(f->code, tm, "\n", NIL);
+ }
+ }
+ // Free any memory allocated by the function being wrapped..
+
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printv(f->code, tm, "\n", NIL);
+ }
+ // Wrap things up (in a manner of speaking)
+
+ Printv(f->code, tab4, "return SWIG_MzScheme_PackageValues(lenv, values);\n", NIL);
+ Printf(f->code, "#undef FUNC_NAME\n");
+ Printv(f->code, "}\n", NIL);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", iname);
+
+ Wrapper_print(f, f_wrappers);
+
+ if (!Getattr(n, "sym:overloaded")) {
+
+ // Now register the function
+ char temp[256];
+ sprintf(temp, "%d", numargs);
+ if (exporting_destructor) {
+ Printf(init_func_def, "SWIG_TypeClientData(SWIGTYPE%s, (void *) %s);\n", swigtype_ptr, wname);
+ }
+ Printf(init_func_def, "scheme_add_global(\"%s\", scheme_make_prim_w_arity(%s,\"%s\",%d,%d),menv);\n", proc_name, wname, proc_name, numreq, numargs);
+ } else {
+ if (!Getattr(n, "sym:nextSibling")) {
+ /* Emit overloading dispatch function */
+
+ int maxargs;
+ String *dispatch = Swig_overload_dispatch(n, "return %s(argc,argv);", &maxargs);
+
+ /* Generate a dispatch wrapper for all overloaded functions */
+
+ Wrapper *df = NewWrapper();
+ String *dname = Swig_name_wrapper(iname);
+
+ Printv(df->def, "static Scheme_Object *\n", dname, "(int argc, Scheme_Object **argv) {", NIL);
+ Printv(df->code, dispatch, "\n", NIL);
+ Printf(df->code, "scheme_signal_error(\"No matching function for overloaded '%s'\");\n", iname);
+ Printf(df->code, "return NULL;\n");
+ Printv(df->code, "}\n", NIL);
+ Wrapper_print(df, f_wrappers);
+ Printf(init_func_def, "scheme_add_global(\"%s\", scheme_make_prim_w_arity(%s,\"%s\",%d,%d),menv);\n", proc_name, dname, proc_name, 0, maxargs);
+ DelWrapper(df);
+ Delete(dispatch);
+ Delete(dname);
+ }
+ }
+
+ Delete(proc_name);
+ Delete(target);
+ Delete(arg);
+ Delete(outarg);
+ Delete(cleanup);
+ Delete(build);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * variableWrapper()
+ *
+ * Create a link to a C variable.
+ * This creates a single function _wrap_swig_var_varname().
+ * This function takes a single optional argument. If supplied, it means
+ * we are setting this variable to some value. If omitted, it means we are
+ * simply evaluating this variable. Either way, we return the variables
+ * value.
+ * ------------------------------------------------------------ */
+
+ virtual int variableWrapper(Node *n) {
+
+ char *name = GetChar(n, "name");
+ char *iname = GetChar(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+
+ String *proc_name = NewString("");
+ String *tm;
+ String *tm2 = NewString("");
+ String *argnum = NewString("0");
+ String *arg = NewString("argv[0]");
+ Wrapper *f;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ f = NewWrapper();
+
+ // evaluation function names
+ String *var_name = Swig_name_wrapper(iname);
+
+ // Build the name for scheme.
+ Printv(proc_name, iname, NIL);
+ Replaceall(proc_name, "_", "-");
+ Setattr(n, "wrap:name", proc_name);
+
+ if ((SwigType_type(t) != T_USER) || (is_a_pointer(t))) {
+
+ Printf(f->def, "static Scheme_Object *%s(int argc, Scheme_Object** argv) {\n", var_name);
+ Printv(f->def, "#define FUNC_NAME \"", proc_name, "\"", NIL);
+
+ Wrapper_add_local(f, "swig_result", "Scheme_Object *swig_result");
+
+ if (!GetFlag(n, "feature:immutable")) {
+ /* Check for a setting of the variable value */
+ Printf(f->code, "if (argc) {\n");
+ if ((tm = Swig_typemap_lookup("varin", n, name, 0))) {
+ Replaceall(tm, "$input", "argv[0]");
+ Replaceall(tm, "$argnum", "1");
+ emit_action_code(n, f->code, tm);
+ } else {
+ throw_unhandled_mzscheme_type_error(t);
+ }
+ Printf(f->code, "}\n");
+ }
+ // Now return the value of the variable (regardless
+ // of evaluating or setting)
+
+ if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
+ Replaceall(tm, "$result", "swig_result");
+ /* Printf (f->code, "%s\n", tm); */
+ emit_action_code(n, f->code, tm);
+ } else {
+ throw_unhandled_mzscheme_type_error(t);
+ }
+ Printf(f->code, "\nreturn swig_result;\n");
+ Printf(f->code, "#undef FUNC_NAME\n");
+ Printf(f->code, "}\n");
+
+ Wrapper_print(f, f_wrappers);
+
+ // Now add symbol to the MzScheme interpreter
+
+ Printv(init_func_def,
+ "scheme_add_global(\"", proc_name, "\", scheme_make_prim_w_arity(", var_name, ", \"", proc_name, "\", ", "0", ", ", "1", "), menv);\n", NIL);
+
+ } else {
+ Swig_warning(WARN_TYPEMAP_VAR_UNDEF, input_file, line_number, "Unsupported variable type %s (ignored).\n", SwigType_str(t, 0));
+ }
+ Delete(var_name);
+ Delete(proc_name);
+ Delete(argnum);
+ Delete(arg);
+ Delete(tm2);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constantWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ char *name = GetChar(n, "name");
+ char *iname = GetChar(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *value = Getattr(n, "value");
+
+ String *var_name = NewString("");
+ String *proc_name = NewString("");
+ String *rvalue = NewString("");
+ String *temp = NewString("");
+ String *tm;
+
+ // Make a static variable;
+
+ Printf(var_name, "_wrap_const_%s", Swig_name_mangle(Getattr(n, "sym:name")));
+
+ // Build the name for scheme.
+ Printv(proc_name, iname, NIL);
+ Replaceall(proc_name, "_", "-");
+
+ if ((SwigType_type(type) == T_USER) && (!is_a_pointer(type))) {
+ Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value.\n");
+ return SWIG_NOWRAP;
+ }
+ // See if there's a typemap
+
+ Printv(rvalue, value, NIL);
+ if ((SwigType_type(type) == T_CHAR) && (is_a_pointer(type) == 1)) {
+ temp = Copy(rvalue);
+ Clear(rvalue);
+ Printv(rvalue, "\"", temp, "\"", NIL);
+ }
+ if ((SwigType_type(type) == T_CHAR) && (is_a_pointer(type) == 0)) {
+ Delete(temp);
+ temp = Copy(rvalue);
+ Clear(rvalue);
+ Printv(rvalue, "'", temp, "'", NIL);
+ }
+ if ((tm = Swig_typemap_lookup("constant", n, name, 0))) {
+ Replaceall(tm, "$value", rvalue);
+ Printf(f_init, "%s\n", tm);
+ } else {
+ // Create variable and assign it a value
+
+ Printf(f_header, "static %s = ", SwigType_lstr(type, var_name));
+ bool is_enum_item = (Cmp(nodeType(n), "enumitem") == 0);
+ if ((SwigType_type(type) == T_STRING)) {
+ Printf(f_header, "\"%s\";\n", value);
+ } else if (SwigType_type(type) == T_CHAR && !is_enum_item) {
+ Printf(f_header, "\'%s\';\n", value);
+ } else {
+ Printf(f_header, "%s;\n", value);
+ }
+
+ // Now create a variable declaration
+
+ {
+ /* Hack alert: will cleanup later -- Dave */
+ Node *nn = NewHash();
+ Setfile(nn, Getfile(n));
+ Setline(nn, Getline(n));
+ Setattr(nn, "name", var_name);
+ Setattr(nn, "sym:name", iname);
+ Setattr(nn, "type", type);
+ SetFlag(nn, "feature:immutable");
+ variableWrapper(nn);
+ Delete(nn);
+ }
+ }
+ Delete(proc_name);
+ Delete(rvalue);
+ Delete(temp);
+ return SWIG_OK;
+ }
+
+ virtual int destructorHandler(Node *n) {
+ exporting_destructor = true;
+ Language::destructorHandler(n);
+ exporting_destructor = false;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classHandler()
+ * ------------------------------------------------------------ */
+ virtual int classHandler(Node *n) {
+ String *mangled_classname = 0;
+ String *real_classname = 0;
+ String *scm_structname = NewString("");
+ SwigType *ctype_ptr = NewStringf("p.%s", getClassType());
+
+ SwigType *t = NewStringf("p.%s", Getattr(n, "name"));
+ swigtype_ptr = SwigType_manglestr(t);
+ Delete(t);
+
+ cls_swigtype = SwigType_manglestr(Getattr(n, "name"));
+
+
+ fieldnames_tab = NewString("");
+ convert_tab = NewString("");
+ convert_proto_tab = NewString("");
+
+ struct_name = Getattr(n, "sym:name");
+ mangled_struct_name = Swig_name_mangle(Getattr(n, "sym:name"));
+
+ Printv(scm_structname, struct_name, NIL);
+ Replaceall(scm_structname, "_", "-");
+
+ real_classname = Getattr(n, "name");
+ mangled_classname = Swig_name_mangle(real_classname);
+
+ Printv(fieldnames_tab, "static const char *_swig_struct_", cls_swigtype, "_field_names[] = { \n", NIL);
+
+ Printv(convert_proto_tab, "static Scheme_Object *_swig_convert_struct_", cls_swigtype, "(", SwigType_str(ctype_ptr, "ptr"), ");\n", NIL);
+
+ Printv(convert_tab, "static Scheme_Object *_swig_convert_struct_", cls_swigtype, "(", SwigType_str(ctype_ptr, "ptr"), ")\n {\n", NIL);
+
+ Printv(convert_tab,
+ tab4, "Scheme_Object *obj;\n", tab4, "Scheme_Object *fields[_swig_struct_", cls_swigtype, "_field_names_cnt];\n", tab4, "int i = 0;\n\n", NIL);
+
+ /* Generate normal wrappers */
+ Language::classHandler(n);
+
+ Printv(convert_tab, tab4, "obj = scheme_make_struct_instance(", "_swig_struct_type_", cls_swigtype, ", i, fields);\n", NIL);
+ Printv(convert_tab, tab4, "return obj;\n}\n\n", NIL);
+
+ Printv(fieldnames_tab, "};\n", NIL);
+
+ Printv(f_header, "static Scheme_Object *_swig_struct_type_", cls_swigtype, ";\n", NIL);
+
+ Printv(f_header, fieldnames_tab, NIL);
+ Printv(f_header, "#define _swig_struct_", cls_swigtype, "_field_names_cnt (sizeof(_swig_struct_", cls_swigtype, "_field_names)/sizeof(char*))\n", NIL);
+
+ Printv(f_header, convert_proto_tab, NIL);
+ Printv(f_wrappers, convert_tab, NIL);
+
+ Printv(init_func_def, "_swig_struct_type_", cls_swigtype,
+ " = SWIG_MzScheme_new_scheme_struct(menv, \"", scm_structname, "\", ",
+ "_swig_struct_", cls_swigtype, "_field_names_cnt,", "(char**) _swig_struct_", cls_swigtype, "_field_names);\n", NIL);
+
+ Delete(mangled_classname);
+ Delete(swigtype_ptr);
+ swigtype_ptr = 0;
+ Delete(fieldnames_tab);
+ Delete(convert_tab);
+ Delete(ctype_ptr);
+ Delete(convert_proto_tab);
+ struct_name = 0;
+ mangled_struct_name = 0;
+ Delete(cls_swigtype);
+ cls_swigtype = 0;
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * membervariableHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int membervariableHandler(Node *n) {
+ Language::membervariableHandler(n);
+
+ if (!is_smart_pointer()) {
+ String *symname = Getattr(n, "sym:name");
+ String *name = Getattr(n, "name");
+ SwigType *type = Getattr(n, "type");
+ String *swigtype = SwigType_manglestr(Getattr(n, "type"));
+ String *tm = 0;
+ String *access_mem = NewString("");
+ SwigType *ctype_ptr = NewStringf("p.%s", Getattr(n, "type"));
+
+ Printv(fieldnames_tab, tab4, "\"", symname, "\",\n", NIL);
+ Printv(access_mem, "(ptr)->", name, NIL);
+ if ((SwigType_type(type) == T_USER) && (!is_a_pointer(type))) {
+ Printv(convert_tab, tab4, "fields[i++] = ", NIL);
+ Printv(convert_tab, "_swig_convert_struct_", swigtype, "((", SwigType_str(ctype_ptr, 0), ")&((ptr)->", name, "));\n", NIL);
+ } else if ((tm = Swig_typemap_lookup("varout", n, access_mem, 0))) {
+ Replaceall(tm, "$result", "fields[i++]");
+ Printv(convert_tab, tm, "\n", NIL);
+ } else
+ Swig_warning(WARN_TYPEMAP_VAR_UNDEF, input_file, line_number, "Unsupported member variable type %s (ignored).\n", SwigType_str(type, 0));
+
+ Delete(access_mem);
+ }
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * validIdentifier()
+ * ------------------------------------------------------------ */
+
+ virtual int validIdentifier(String *s) {
+ char *c = Char(s);
+ /* Check whether we have an R5RS identifier. */
+ /* <identifier> --> <initial> <subsequent>* | <peculiar identifier> */
+ /* <initial> --> <letter> | <special initial> */
+ if (!(isalpha(*c) || (*c == '!') || (*c == '$') || (*c == '%')
+ || (*c == '&') || (*c == '*') || (*c == '/') || (*c == ':')
+ || (*c == '<') || (*c == '=') || (*c == '>') || (*c == '?')
+ || (*c == '^') || (*c == '_') || (*c == '~'))) {
+ /* <peculiar identifier> --> + | - | ... */
+ if ((strcmp(c, "+") == 0)
+ || strcmp(c, "-") == 0 || strcmp(c, "...") == 0)
+ return 1;
+ else
+ return 0;
+ }
+ /* <subsequent> --> <initial> | <digit> | <special subsequent> */
+ while (*c) {
+ if (!(isalnum(*c) || (*c == '!') || (*c == '$') || (*c == '%')
+ || (*c == '&') || (*c == '*') || (*c == '/') || (*c == ':')
+ || (*c == '<') || (*c == '=') || (*c == '>') || (*c == '?')
+ || (*c == '^') || (*c == '_') || (*c == '~') || (*c == '+')
+ || (*c == '-') || (*c == '.') || (*c == '@')))
+ return 0;
+ c++;
+ }
+ return 1;
+ }
+
+ String *runtimeCode() {
+ String *s = Swig_include_sys("mzrun.swg");
+ if (!s) {
+ Printf(stderr, "*** Unable to open 'mzrun.swg'\n");
+ s = NewString("");
+ }
+ return s;
+ }
+
+ String *defaultExternalRuntimeFilename() {
+ return NewString("swigmzrun.h");
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * swig_mzscheme() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_mzscheme() {
+ return new MZSCHEME();
+}
+extern "C" Language *swig_mzscheme(void) {
+ return new_swig_mzscheme();
+}
diff --git a/contrib/tools/swig/Source/Modules/nested.cxx b/contrib/tools/swig/Source/Modules/nested.cxx
new file mode 100644
index 0000000000..d027eebe5b
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/nested.cxx
@@ -0,0 +1,453 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * nested.cxx
+ *
+ * Nested structs support
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+// Nested classes processing section
+static Hash *classhash = 0;
+
+static String *make_name(Node *n, String *name, SwigType *decl) {
+ int destructor = name && (*(Char(name)) == '~');
+ if (String *yyrename = Getattr(n, "class_rename")) {
+ String *s = NewString(yyrename);
+ Delattr(n, "class_rename");
+ if (destructor && (*(Char(s)) != '~')) {
+ Insert(s, 0, "~");
+ }
+ return s;
+ }
+
+ if (!name)
+ return 0;
+ return Swig_name_make(n, 0, name, decl, 0);
+}
+
+// C version of add_symbols()
+static void add_symbols_c(Node *n) {
+ String *decl;
+ String *wrn = 0;
+ String *symname = 0;
+ int iscdecl = Cmp(nodeType(n), "cdecl") == 0;
+ Setattr(n, "ismember", "1");
+ Setattr(n, "access", "public");
+ if (Getattr(n, "sym:name"))
+ return;
+ decl = Getattr(n, "decl");
+ if (!SwigType_isfunction(decl)) {
+ String *name = Getattr(n, "name");
+ String *makename = Getattr(n, "parser:makename");
+ if (iscdecl) {
+ String *storage = Getattr(n, "storage");
+ if (Cmp(storage, "typedef") == 0) {
+ Setattr(n, "kind", "typedef");
+ } else {
+ SwigType *type = Getattr(n, "type");
+ String *value = Getattr(n, "value");
+ Setattr(n, "kind", "variable");
+ if (value && Len(value)) {
+ Setattr(n, "hasvalue", "1");
+ }
+ if (type) {
+ SwigType *ty;
+ SwigType *tmp = 0;
+ if (decl) {
+ ty = tmp = Copy(type);
+ SwigType_push(ty, decl);
+ } else {
+ ty = type;
+ }
+ if (!SwigType_ismutable(ty)) {
+ SetFlag(n, "hasconsttype");
+ SetFlag(n, "feature:immutable");
+ }
+ if (tmp)
+ Delete(tmp);
+ }
+ if (!type) {
+ Printf(stderr, "notype name %s\n", name);
+ }
+ }
+ }
+ Swig_features_get(Swig_cparse_features(), 0, name, 0, n);
+ if (makename) {
+ symname = make_name(n, makename, 0);
+ Delattr(n, "parser:makename"); /* temporary information, don't leave it hanging around */
+ } else {
+ makename = name;
+ symname = make_name(n, makename, 0);
+ }
+
+ if (!symname) {
+ symname = Copy(Getattr(n, "unnamed"));
+ }
+ if (symname) {
+ wrn = Swig_name_warning(n, 0, symname, 0);
+ }
+ } else {
+ String *name = Getattr(n, "name");
+ SwigType *fdecl = Copy(decl);
+ SwigType *fun = SwigType_pop_function(fdecl);
+ if (iscdecl) {
+ Setattr(n, "kind", "function");
+ }
+
+ Swig_features_get(Swig_cparse_features(), 0, name, fun, n);
+
+ symname = make_name(n, name, fun);
+ wrn = Swig_name_warning(n, 0, symname, fun);
+
+ Delete(fdecl);
+ Delete(fun);
+
+ }
+ if (!symname)
+ return;
+ if (GetFlag(n, "feature:ignore")) {
+ /* Only add to C symbol table and continue */
+ Swig_symbol_add(0, n);
+ } else if (strncmp(Char(symname), "$ignore", 7) == 0) {
+ char *c = Char(symname) + 7;
+ SetFlag(n, "feature:ignore");
+ if (strlen(c)) {
+ SWIG_WARN_NODE_BEGIN(n);
+ Swig_warning(0, Getfile(n), Getline(n), "%s\n", c + 1);
+ SWIG_WARN_NODE_END(n);
+ }
+ Swig_symbol_add(0, n);
+ } else {
+ Node *c;
+ if ((wrn) && (Len(wrn))) {
+ String *metaname = symname;
+ if (!Getmeta(metaname, "already_warned")) {
+ SWIG_WARN_NODE_BEGIN(n);
+ Swig_warning(0, Getfile(n), Getline(n), "%s\n", wrn);
+ SWIG_WARN_NODE_END(n);
+ Setmeta(metaname, "already_warned", "1");
+ }
+ }
+ c = Swig_symbol_add(symname, n);
+
+ if (c != n) {
+ /* symbol conflict attempting to add in the new symbol */
+ if (Getattr(n, "sym:weak")) {
+ Setattr(n, "sym:name", symname);
+ } else {
+ String *e = NewStringEmpty();
+ String *en = NewStringEmpty();
+ String *ec = NewStringEmpty();
+ int redefined = Swig_need_redefined_warn(n, c, true);
+ if (redefined) {
+ Printf(en, "Identifier '%s' redefined (ignored)", symname);
+ Printf(ec, "previous definition of '%s'", symname);
+ } else {
+ Printf(en, "Redundant redeclaration of '%s'", symname);
+ Printf(ec, "previous declaration of '%s'", symname);
+ }
+ if (Cmp(symname, Getattr(n, "name"))) {
+ Printf(en, " (Renamed from '%s')", SwigType_namestr(Getattr(n, "name")));
+ }
+ Printf(en, ",");
+ if (Cmp(symname, Getattr(c, "name"))) {
+ Printf(ec, " (Renamed from '%s')", SwigType_namestr(Getattr(c, "name")));
+ }
+ Printf(ec, ".");
+ SWIG_WARN_NODE_BEGIN(n);
+ if (redefined) {
+ Swig_warning(WARN_PARSE_REDEFINED, Getfile(n), Getline(n), "%s\n", en);
+ Swig_warning(WARN_PARSE_REDEFINED, Getfile(c), Getline(c), "%s\n", ec);
+ } else {
+ Swig_warning(WARN_PARSE_REDUNDANT, Getfile(n), Getline(n), "%s\n", en);
+ Swig_warning(WARN_PARSE_REDUNDANT, Getfile(c), Getline(c), "%s\n", ec);
+ }
+ SWIG_WARN_NODE_END(n);
+ Printf(e, "%s:%d:%s\n%s:%d:%s\n", Getfile(n), Getline(n), en, Getfile(c), Getline(c), ec);
+ Setattr(n, "error", e);
+ Delete(e);
+ Delete(en);
+ Delete(ec);
+ }
+ }
+ }
+ Delete(symname);
+}
+
+/* Strips C-style and C++-style comments from string in-place. */
+static void strip_comments(char *string) {
+ int state = 0;
+ /*
+ * 0 - not in comment
+ * 1 - in c-style comment
+ * 2 - in c++-style comment
+ * 3 - in string
+ * 4 - after reading / not in comments
+ * 5 - after reading * in c-style comments
+ * 6 - after reading \ in strings
+ */
+ char *c = string;
+ while (*c) {
+ switch (state) {
+ case 0:
+ if (*c == '\"')
+ state = 3;
+ else if (*c == '/')
+ state = 4;
+ break;
+ case 1:
+ if (*c == '*')
+ state = 5;
+ *c = ' ';
+ break;
+ case 2:
+ if (*c == '\n')
+ state = 0;
+ else
+ *c = ' ';
+ break;
+ case 3:
+ if (*c == '\"')
+ state = 0;
+ else if (*c == '\\')
+ state = 6;
+ break;
+ case 4:
+ if (*c == '/') {
+ *(c - 1) = ' ';
+ *c = ' ';
+ state = 2;
+ } else if (*c == '*') {
+ *(c - 1) = ' ';
+ *c = ' ';
+ state = 1;
+ } else
+ state = 0;
+ break;
+ case 5:
+ if (*c == '/')
+ state = 0;
+ else
+ state = 1;
+ *c = ' ';
+ break;
+ case 6:
+ state = 3;
+ break;
+ }
+ ++c;
+ }
+}
+
+// Create a %insert with a typedef to make a new name visible to C
+static Node *create_insert(Node *n, bool noTypedef = false) {
+ // format a typedef
+ String *ccode = Getattr(n, "code");
+ Push(ccode, " ");
+ if (noTypedef) {
+ Push(ccode, Getattr(n, "name"));
+ Push(ccode, " ");
+ Push(ccode, Getattr(n, "kind"));
+ } else {
+ Push(ccode, Getattr(n, "kind"));
+ Push(ccode, "typedef ");
+ Append(ccode, " ");
+ Append(ccode, Getattr(n, "tdname"));
+ }
+ Append(ccode, ";");
+
+ /* Strip comments - further code may break in presence of comments. */
+ strip_comments(Char(ccode));
+
+ /* Make all SWIG created typedef structs/unions/classes unnamed else
+ redefinition errors occur - nasty hack alert. */
+ if (!noTypedef) {
+ const char *types_array[3] = { "struct", "union", "class" };
+ for (int i = 0; i < 3; i++) {
+ char *code_ptr = Char(ccode);
+ while (code_ptr) {
+ /* Replace struct name (as in 'struct name {...}' ) with whitespace
+ name will be between struct and opening brace */
+
+ code_ptr = strstr(code_ptr, types_array[i]);
+ if (code_ptr) {
+ char *open_bracket_pos;
+ code_ptr += strlen(types_array[i]);
+ open_bracket_pos = strchr(code_ptr, '{');
+ if (open_bracket_pos) {
+ /* Make sure we don't have something like struct A a; */
+ char *semi_colon_pos = strchr(code_ptr, ';');
+ if (!(semi_colon_pos && (semi_colon_pos < open_bracket_pos)))
+ while (code_ptr < open_bracket_pos)
+ *code_ptr++ = ' ';
+ }
+ }
+ }
+ }
+ }
+ {
+ /* Remove SWIG directive %constant which may be left in the SWIG created typedefs */
+ char *code_ptr = Char(ccode);
+ while (code_ptr) {
+ code_ptr = strstr(code_ptr, "%constant");
+ if (code_ptr) {
+ char *directive_end_pos = strchr(code_ptr, ';');
+ if (directive_end_pos) {
+ while (code_ptr <= directive_end_pos)
+ *code_ptr++ = ' ';
+ }
+ }
+ }
+ }
+ Node *newnode = NewHash();
+ set_nodeType(newnode, "insert");
+ Setfile(newnode, Getfile(n));
+ Setline(newnode, Getline(n));
+ String *code = NewStringEmpty();
+ Wrapper_pretty_print(ccode, code);
+ Setattr(newnode, "code", code);
+ Delete(code);
+ Delattr(n, "code");
+ return newnode;
+}
+
+static void insertNodeAfter(Node *n, Node *c) {
+ Node *g = parentNode(n);
+ set_parentNode(c, g);
+ Node *ns = nextSibling(n);
+ if (Node *outer = Getattr(c, "nested:outer")) {
+ while (ns && outer == Getattr(ns, "nested:outer")) {
+ n = ns;
+ ns = nextSibling(n);
+ }
+ }
+ if (!ns) {
+ set_lastChild(g, c);
+ } else {
+ set_nextSibling(c, ns);
+ set_previousSibling(ns, c);
+ }
+ set_nextSibling(n, c);
+ set_previousSibling(c, n);
+}
+
+void Swig_nested_name_unnamed_c_structs(Node *n) {
+ if (!n)
+ return;
+ if (!classhash)
+ classhash = Getattr(n, "classes");
+ Node *c = firstChild(n);
+ while (c) {
+ Node *next = nextSibling(c);
+ if (String *declName = Getattr(c, "nested:unnamed")) {
+ if (Node *outer = Getattr(c, "nested:outer")) {
+ // generate a name
+ String *name = NewStringf("%s_%s", Getattr(outer, "name"), declName);
+ Delattr(c, "nested:unnamed");
+ // set the name to the class and symbol table
+ Setattr(c, "tdname", name);
+ Setattr(c, "name", name);
+ Swig_symbol_setscope(Getattr(c, "symtab"));
+ Swig_symbol_setscopename(name);
+ // now that we have a name - gather base symbols
+ if (List *publicBases = Getattr(c, "baselist")) {
+ List *bases = Swig_make_inherit_list(name, publicBases, 0);
+ Swig_inherit_base_symbols(bases);
+ Delete(bases);
+ }
+ Setattr(classhash, name, c);
+
+ // Merge the extension into the symbol table
+ if (Node *am = Getattr(Swig_extend_hash(), name)) {
+ Swig_extend_merge(c, am);
+ Swig_extend_append_previous(c, am);
+ Delattr(Swig_extend_hash(), name);
+ }
+ Swig_symbol_popscope();
+
+ // process declarations following this type (assign correct new type)
+ SwigType *ty = Copy(name);
+ Node *decl = nextSibling(c);
+ List *declList = NewList();
+ while (decl && Getattr(decl, "nested:unnamedtype") == c) {
+ Setattr(decl, "type", ty);
+ Append(declList, decl);
+ Delattr(decl, "nested:unnamedtype");
+ SetFlag(decl, "feature:immutable");
+ add_symbols_c(decl);
+ decl = nextSibling(decl);
+ }
+ Delete(ty);
+ Swig_symbol_setscope(Swig_symbol_global_scope());
+ add_symbols_c(c);
+
+ Node *ins = create_insert(c);
+ insertNodeAfter(c, ins);
+ removeNode(c);
+ insertNodeAfter(n, c);
+ Delete(ins);
+ Delattr(c, "nested:outer");
+ } else {
+ // global unnamed struct - ignore it and its instances
+ SetFlag(c, "feature:ignore");
+ while (next && Getattr(next, "nested:unnamedtype") == c) {
+ SetFlag(next, "feature:ignore");
+ next = nextSibling(next);
+ }
+ c = next;
+ continue;
+ }
+ } else if (cparse_cplusplusout) {
+ if (Getattr(c, "nested:outer")) {
+ Node *ins = create_insert(c, true);
+ insertNodeAfter(c, ins);
+ Delete(ins);
+ Delattr(c, "nested:outer");
+ }
+ }
+ // process children
+ Swig_nested_name_unnamed_c_structs(c);
+ c = next;
+ }
+}
+
+static void remove_outer_class_reference(Node *n) {
+ for (Node *c = firstChild(n); c; c = nextSibling(c)) {
+ if (GetFlag(c, "feature:flatnested") || Language::instance()->nestedClassesSupport() == Language::NCS_None) {
+ Delattr(c, "nested:outer");
+ remove_outer_class_reference(c);
+ }
+ }
+}
+
+void Swig_nested_process_classes(Node *n) {
+ if (!n)
+ return;
+ Node *c = firstChild(n);
+ while (c) {
+ Node *next = nextSibling(c);
+ if (!Getattr(c, "templatetype")) {
+ if (GetFlag(c, "nested") && (GetFlag(c, "feature:flatnested") || Language::instance()->nestedClassesSupport() == Language::NCS_None)) {
+ removeNode(c);
+ if (!checkAttribute(c, "access", "public"))
+ SetFlag(c, "feature:ignore");
+ else if (Strcmp(nodeType(n),"extend") == 0 && Strcmp(nodeType(parentNode(n)),"class") == 0)
+ insertNodeAfter(parentNode(n), c);
+ else
+ insertNodeAfter(n, c);
+ }
+ Swig_nested_process_classes(c);
+ }
+ c = next;
+ }
+ remove_outer_class_reference(n);
+}
+
diff --git a/contrib/tools/swig/Source/Modules/ocaml.cxx b/contrib/tools/swig/Source/Modules/ocaml.cxx
new file mode 100644
index 0000000000..963a0c2d10
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/ocaml.cxx
@@ -0,0 +1,1848 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * ocaml.cxx
+ *
+ * Ocaml language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include <ctype.h>
+
+static const char *usage = "\
+Ocaml Options (available with -ocaml)\n\
+ -oldvarnames - Old intermediary method names for variable wrappers\n\
+ -prefix <name> - Set a prefix <name> to be prepended to all names\n\
+ -suffix <name> - Deprecated alias for general option -cppext\n\
+ -where - Emit library location\n\
+\n";
+
+static int classmode = 0;
+static int in_constructor = 0, in_destructor = 0, in_copyconst = 0;
+static int const_enum = 0;
+static int static_member_function = 0;
+static int generate_sizeof = 0;
+static String *prefix = 0;
+static const char *ocaml_path = "ocaml";
+static bool old_variable_names = false;
+static String *classname = 0;
+static String *module = 0;
+static String *init_func_def = 0;
+static String *f_classtemplate = 0;
+static SwigType *name_qualifier_type = 0;
+
+static Hash *seen_enums = 0;
+static Hash *seen_enumvalues = 0;
+static Hash *seen_constructors = 0;
+
+static File *f_header = 0;
+static File *f_begin = 0;
+static File *f_runtime = 0;
+static File *f_wrappers = 0;
+static File *f_directors = 0;
+static File *f_directors_h = 0;
+static File *f_init = 0;
+static File *f_mlout = 0;
+static File *f_mliout = 0;
+static File *f_mlbody = 0;
+static File *f_mlibody = 0;
+static File *f_mltail = 0;
+static File *f_mlitail = 0;
+static File *f_enumtypes_type = 0;
+static File *f_enumtypes_value = 0;
+static File *f_class_ctors = 0;
+static File *f_class_ctors_end = 0;
+static File *f_enum_to_int = 0;
+static File *f_int_to_enum = 0;
+
+class OCAML:public Language {
+public:
+
+ OCAML() {
+ director_prot_ctor_code = NewString("");
+ Printv(director_prot_ctor_code,
+ "if ( $comparison ) { /* subclassed */\n",
+ " $director_new \n", "} else {\n", " caml_failwith(\"accessing abstract class or protected constructor\"); \n", "}\n", NIL);
+ director_multiple_inheritance = 1;
+ director_language = 1;
+ }
+
+ String *Swig_class_name(Node *n) {
+ String *name;
+ name = Copy(Getattr(n, "sym:name"));
+ return name;
+ }
+
+ void PrintIncludeArg() {
+ Printv(stdout, SWIG_LIB, SWIG_FILE_DELIMITER, ocaml_path, "\n", NIL);
+ }
+
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+ int i;
+
+ prefix = 0;
+
+ SWIG_library_directory(ocaml_path);
+
+ // Look for certain command line options
+ for (i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage, stdout);
+ Exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "-where") == 0) {
+ PrintIncludeArg();
+ Exit(EXIT_SUCCESS);
+ } else if (strcmp(argv[i], "-prefix") == 0) {
+ if (argv[i + 1]) {
+ prefix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-suffix") == 0) {
+ if (argv[i + 1]) {
+ Printf(stderr, "swig: warning: -suffix option deprecated. SWIG 3.0.4 and later provide a -cppext option which should be used instead.\n");
+ SWIG_config_cppext(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else
+ Swig_arg_error();
+ } else if (strcmp(argv[i], "-oldvarnames") == 0) {
+ Swig_mark_arg(i);
+ old_variable_names = true;
+ }
+ }
+ }
+
+ // If a prefix has been specified make sure it ends in a '_' (not actually used!)
+ if (prefix) {
+ const char *px = Char(prefix);
+ if (px[Len(prefix) - 1] != '_')
+ Printf(prefix, "_");
+ } else
+ prefix = NewString("swig_");
+
+ // Add a symbol for this module
+
+ Preprocessor_define("SWIGOCAML 1", 0);
+ // Set name of typemaps
+
+ SWIG_typemap_lang("ocaml");
+
+ // Read in default typemaps */
+ SWIG_config_file("ocaml.i");
+ allow_overloading();
+
+ }
+
+ /* Swig_director_declaration()
+ *
+ * Generate the full director class declaration, complete with base classes.
+ * e.g. "class SwigDirector_myclass : public myclass, public Swig::Director {"
+ *
+ */
+
+ String *Swig_director_declaration(Node *n) {
+ String *classname = Swig_class_name(n);
+ String *directorname = NewStringf("SwigDirector_%s", classname);
+ String *base = Getattr(n, "classtype");
+ String *declaration = Swig_class_declaration(n, directorname);
+ Printf(declaration, " : public %s, public Swig::Director {\n", base);
+ Delete(classname);
+ Delete(directorname);
+ return declaration;
+ }
+
+ void emitBanner(File *f) {
+ Printf(f, "(* ----------------------------------------------------------------------------\n");
+ Swig_banner_target_lang(f, " *");
+ Printf(f, " * ---------------------------------------------------------------------------- *)\n\n");
+ }
+
+ /* ------------------------------------------------------------
+ * top()
+ *
+ * Recognize the %module, and capture the module name.
+ * Create the default enum cases.
+ * Set up the named outputs:
+ *
+ * init
+ * ml
+ * mli
+ * wrapper
+ * header
+ * runtime
+ * directors
+ * directors_h
+ * ------------------------------------------------------------ */
+
+ virtual int top(Node *n) {
+ /* Set comparison with none for ConstructorToFunction */
+ setSubclassInstanceCheck(NewString("caml_list_nth(args,0) != Val_unit"));
+
+ /* check if directors are enabled for this module. note: this
+ * is a "master" switch, without which no director code will be
+ * emitted. %feature("director") statements are also required
+ * to enable directors for individual classes or methods.
+ *
+ * use %module(directors="1") modulename at the start of the
+ * interface file to enable director generation.
+ */
+ String *mod_docstring = NULL;
+ {
+ Node *module = Getattr(n, "module");
+ if (module) {
+ Node *options = Getattr(module, "options");
+ if (options) {
+ if (Getattr(options, "directors")) {
+ allow_directors();
+ }
+ if (Getattr(options, "dirprot")) {
+ allow_dirprot();
+ }
+ if (Getattr(options, "sizeof")) {
+ generate_sizeof = 1;
+ }
+ mod_docstring = Getattr(options, "docstring");
+ }
+ }
+ }
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+ f_directors = NewString("");
+ f_directors_h = NewString("");
+ f_enumtypes_type = NewString("");
+ f_enumtypes_value = NewString("");
+ init_func_def = NewString("");
+ f_mlbody = NewString("");
+ f_mlibody = NewString("");
+ f_mltail = NewString("");
+ f_mlitail = NewString("");
+ f_class_ctors = NewString("");
+ f_class_ctors_end = NewString("");
+ f_enum_to_int = NewString("");
+ f_int_to_enum = NewString("");
+ f_classtemplate = NewString("");
+
+ module = Getattr(n, "name");
+
+ seen_constructors = NewHash();
+ seen_enums = NewHash();
+ seen_enumvalues = NewHash();
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("init", init_func_def);
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("mli", f_mlibody);
+ Swig_register_filebyname("ml", f_mlbody);
+ Swig_register_filebyname("mlitail", f_mlitail);
+ Swig_register_filebyname("mltail", f_mltail);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+ Swig_register_filebyname("classtemplate", f_classtemplate);
+ Swig_register_filebyname("class_ctors", f_class_ctors);
+
+ if (old_variable_names) {
+ Swig_name_register("set", "%n%v__set__");
+ Swig_name_register("get", "%n%v__get__");
+ }
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "OCAML");
+
+ Printf(f_runtime, "#define SWIG_MODULE \"%s\"\n", module);
+ /* Module name */
+ Printf(f_mlbody, "let module_name = \"%s\"\n", module);
+ Printf(f_mlibody, "val module_name : string\n");
+ Printf(f_enum_to_int,
+ "let enum_to_int x (v : c_obj) =\n"
+ " match v with\n"
+ " C_enum _y ->\n"
+ " (let y = _y in match (x : c_enum_type) with\n"
+ " `unknown -> " " (match y with\n" " `Int x -> (Swig.C_int x)\n" " | _ -> raise (LabelNotFromThisEnum v))\n");
+
+ Printf(f_int_to_enum, "let int_to_enum x y =\n" " match (x : c_enum_type) with\n" " `unknown -> C_enum (`Int y)\n");
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+ }
+
+ Printf(f_runtime, "\n");
+
+ /* Produce the enum_to_int and int_to_enum functions */
+
+ Printf(f_enumtypes_type, "open Swig\n" "type c_enum_type = [ \n `unknown\n");
+ Printf(f_enumtypes_value, "type c_enum_value = [ \n `Int of int\n");
+ String *mlfile = NewString("");
+ String *mlifile = NewString("");
+
+ Printv(mlfile, module, ".ml", NIL);
+ Printv(mlifile, module, ".mli", NIL);
+
+ String *mlfilen = NewStringf("%s%s", SWIG_output_directory(), mlfile);
+ if ((f_mlout = NewFile(mlfilen, "w", SWIG_output_files())) == 0) {
+ FileErrorDisplay(mlfilen);
+ Exit(EXIT_FAILURE);
+ }
+ String *mlifilen = NewStringf("%s%s", SWIG_output_directory(), mlifile);
+ if ((f_mliout = NewFile(mlifilen, "w", SWIG_output_files())) == 0) {
+ FileErrorDisplay(mlifilen);
+ Exit(EXIT_FAILURE);
+ }
+ emitBanner(f_mlout);
+ emitBanner(f_mliout);
+
+ Language::top(n);
+
+ if (mod_docstring) {
+ if (Len(mod_docstring)) {
+ Printv(f_mliout, "(** ", mod_docstring, " *)\n", NIL);
+ }
+ Delete(mod_docstring);
+ mod_docstring = NULL;
+ }
+
+ Printf(f_enum_to_int, ") | _ -> (C_int (get_int v))\n" "let _ = Callback.register \"%s_enum_to_int\" enum_to_int\n", module);
+ Printf(f_mlibody, "val enum_to_int : c_enum_type -> c_obj -> Swig.c_obj\n");
+
+ Printf(f_int_to_enum, "let _ = Callback.register \"%s_int_to_enum\" int_to_enum\n", module);
+ Printf(f_mlibody, "val int_to_enum : c_enum_type -> int -> c_obj\n");
+ Printf(f_init, "#define SWIG_init f_%s_init\n" "%s" "}\n", module, init_func_def);
+ Printf(f_mlbody, "external f_init : unit -> unit = \"f_%s_init\" ;;\n" "let _ = f_init ()\n", module);
+ Printf(f_enumtypes_type, "]\n");
+ Printf(f_enumtypes_value, "]\n\n" "type c_obj = c_enum_value c_obj_t\n");
+
+ if (directorsEnabled()) {
+ // Insert director runtime into the f_runtime file (make it occur before %header section)
+ Swig_insert_file("director_common.swg", f_runtime);
+ Swig_insert_file("director.swg", f_runtime);
+ }
+
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+ /* Close all of the files */
+ Dump(f_runtime, f_begin);
+ Dump(f_directors_h, f_header);
+ Dump(f_header, f_begin);
+ Dump(f_directors, f_wrappers);
+ Dump(f_wrappers, f_begin);
+ Wrapper_pretty_print(f_init, f_begin);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_runtime);
+ Delete(f_begin);
+
+ Dump(f_enumtypes_type, f_mlout);
+ Dump(f_enumtypes_value, f_mlout);
+ Dump(f_mlbody, f_mlout);
+ Dump(f_enum_to_int, f_mlout);
+ Dump(f_int_to_enum, f_mlout);
+ Delete(f_int_to_enum);
+ Delete(f_enum_to_int);
+ Dump(f_class_ctors, f_mlout);
+ Dump(f_class_ctors_end, f_mlout);
+ Dump(f_mltail, f_mlout);
+ Delete(f_mlout);
+
+ Dump(f_enumtypes_type, f_mliout);
+ Dump(f_enumtypes_value, f_mliout);
+ Dump(f_mlibody, f_mliout);
+ Dump(f_mlitail, f_mliout);
+ Delete(f_mliout);
+
+ return SWIG_OK;
+ }
+
+ /* Produce an error for the given type */
+ void throw_unhandled_ocaml_type_error(SwigType *d, const char *types) {
+ Swig_warning(WARN_TYPEMAP_UNDEF, input_file, line_number, "Unable to handle type %s (%s).\n", SwigType_str(d, 0), types);
+ }
+
+ /* Return true iff T is a pointer type */
+ int
+ is_a_pointer(SwigType *t) {
+ return SwigType_ispointer(SwigType_typedef_resolve_all(t));
+ }
+
+ /*
+ * Delete one reference from a given type.
+ */
+
+ void oc_SwigType_del_reference(SwigType *t) {
+ if (SwigType_isqualifier(t)) {
+ SwigType_del_qualifier(t);
+ }
+ SwigType_del_reference(t);
+ }
+
+ void oc_SwigType_del_array(SwigType *t) {
+ if (SwigType_isqualifier(t)) {
+ SwigType_del_qualifier(t);
+ }
+ if (SwigType_isarray(t)) {
+ SwigType_del_array(t);
+ }
+ }
+
+ /*
+ * Return true iff T is a reference type
+ */
+
+ int
+ is_a_reference(SwigType *t) {
+ return SwigType_isreference(SwigType_typedef_resolve_all(t));
+ }
+
+ int
+ is_an_array(SwigType *t) {
+ return SwigType_isarray(SwigType_typedef_resolve_all(t));
+ }
+
+ virtual int membervariableHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ Language::membervariableHandler(n);
+
+ String *mname = Swig_name_member(NSPACE_TODO, classname, symname);
+ String *getname = Swig_name_get(NSPACE_TODO, mname);
+ String *mangled_getname = mangleNameForCaml(getname);
+ Delete(getname);
+
+ if (!GetFlag(n, "feature:immutable")) {
+ String *setname = Swig_name_set(NSPACE_TODO, mname);
+ String *mangled_setname = mangleNameForCaml(setname);
+ Delete(setname);
+ Printf(f_class_ctors, " \"[%s]\", (fun args -> " "if args = (C_list [ raw_ptr ]) then _%s args else _%s args) ;\n", symname, mangled_getname, mangled_setname);
+ Delete(mangled_setname);
+ } else {
+ Printf(f_class_ctors, " \"[%s]\", (fun args -> " "if args = (C_list [ raw_ptr ]) then _%s args else C_void) ;\n", symname, mangled_getname);
+ }
+ Delete(mangled_getname);
+ Delete(mname);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * functionWrapper()
+ * Create a function declaration and register it with the interpreter.
+ * ------------------------------------------------------------ */
+
+ virtual int functionWrapper(Node *n) {
+ char *iname = GetChar(n, "sym:name");
+ SwigType *d = Getattr(n, "type");
+ String *return_type_normalized = normalizeTemplatedClassName(d);
+ ParmList *l = Getattr(n, "parms");
+ int director_method = 0;
+ Parm *p;
+
+ Wrapper *f = NewWrapper();
+ String *proc_name = NewString("");
+ String *target = NewString("");
+ String *arg = NewString("");
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+ String *build = NewString("");
+ String *tm;
+ int i = 0;
+ int numargs;
+ int numreq;
+ int newobj = GetFlag(n, "feature:new");
+ String *nodeType = Getattr(n, "nodeType");
+ int destructor = (!Cmp(nodeType, "destructor"));
+ String *overname = 0;
+ bool isOverloaded = Getattr(n, "sym:overloaded") ? true : false;
+ // For overloaded functions, only the dispatch function needs to be exposed in the ml and mli files.
+ bool expose_func = !isOverloaded || !Getattr(n, "sym:nextSibling");
+
+ // Make a wrapper name for this
+ String *wname = Swig_name_wrapper(iname);
+ if (isOverloaded) {
+ overname = Getattr(n, "sym:overname");
+ } else {
+ if (!addSymbol(iname, n)) {
+ DelWrapper(f);
+ return SWIG_ERROR;
+ }
+ }
+ if (overname) {
+ Append(wname, overname);
+ }
+ /* Do this to disambiguate functions emitted from different modules */
+ Append(wname, module);
+
+ Setattr(n, "wrap:name", wname);
+
+ // Build the name for Scheme.
+ Printv(proc_name, "_", iname, NIL);
+ String *mangled_name = mangleNameForCaml(proc_name);
+
+ if (classmode && in_constructor && expose_func) { // Emit constructor for object
+ String *mangled_name_nounder = NewString((char *) (Char(mangled_name)) + 1);
+ Printf(f_class_ctors_end, "let %s clst = _%s clst\n", mangled_name_nounder, mangled_name_nounder);
+ Printf(f_mlibody, "val %s : c_obj -> c_obj\n", mangled_name_nounder);
+ Delete(mangled_name_nounder);
+ } else if (classmode && in_destructor) {
+ Printf(f_class_ctors, " \"~\", %s ;\n", mangled_name);
+ } else if (classmode && !in_constructor && !in_destructor && !static_member_function &&
+ !Getattr(n, "membervariableHandler:sym:name") && expose_func) {
+ String *opname = Copy(Getattr(n, "memberfunctionHandler:sym:name"));
+
+ Replaceall(opname, "operator ", "");
+ Printf(f_class_ctors, " \"%s\", %s ;\n", opname, mangled_name);
+ Delete(opname);
+ }
+
+ if (classmode && in_constructor) {
+ Setattr(seen_constructors, mangled_name, "true");
+ }
+ // writing the function wrapper function
+ Printv(f->def, "SWIGEXT CAML_VALUE ", wname, " (", NIL);
+ Printv(f->def, "CAML_VALUE args", NIL);
+ Printv(f->def, ")\n{", NIL);
+
+ /* Define the scheme name in C. This define is used by several
+ macros. */
+ //Printv(f->def, "#define FUNC_NAME \"", mangled_name, "\"", NIL);
+
+ // adds local variables
+ Wrapper_add_local(f, "args", "CAMLparam1(args)");
+ Wrapper_add_local(f, "ret", "SWIG_CAMLlocal2(swig_result,rv)");
+ d = SwigType_typedef_qualified(d);
+ emit_parameter_variables(l, f);
+
+ /* Attach the standard typemaps */
+ emit_attach_parmmaps(l, f);
+ Setattr(n, "wrap:parms", l);
+
+ numargs = emit_num_arguments(l);
+ numreq = emit_num_required(l);
+ if (!isOverloaded) {
+ if (numargs > 0) {
+ if (numreq > 0) {
+ Printf(f->code, "if (caml_list_length(args) < %d || caml_list_length(args) > %d) {\n", numreq, numargs);
+ } else {
+ Printf(f->code, "if (caml_list_length(args) > %d) {\n", numargs);
+ }
+ Printf(f->code, "caml_invalid_argument(\"Incorrect number of arguments passed to '%s'\");\n}\n", iname);
+ } else {
+ Printf(f->code, "if (caml_list_length(args) > 0) caml_invalid_argument(\"'%s' takes no arguments\");\n", iname);
+ }
+ }
+ Printf(f->code, "swig_result = Val_unit;\n");
+
+ // Now write code to extract the parameters (this is super ugly)
+
+ for (i = 0, p = l; i < numargs; i++) {
+ /* Skip ignored arguments */
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+ pt = SwigType_typedef_qualified(pt);
+
+ // Produce names of source and target
+ Clear(target);
+ Clear(arg);
+ String *source = NewStringf("caml_list_nth(args,%d)", i);
+ Printf(target, "%s", ln);
+ Printv(arg, Getattr(p, "name"), NIL);
+
+ if (i >= numreq) {
+ Printf(f->code, "if (caml_list_length(args) > %d) {\n", i);
+ }
+ // Handle parameter types.
+ if ((tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source);
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ // no typemap found
+ // check if typedef and resolve
+ throw_unhandled_ocaml_type_error(pt, "in");
+ p = nextSibling(p);
+ }
+ if (i >= numreq) {
+ Printf(f->code, "}\n");
+ }
+ Delete(source);
+ }
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Pass output arguments back to the caller.
+
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Replaceall(tm, "$ntype", normalizeTemplatedClassName(Getattr(p, "type")));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Free up any memory allocated for the arguments.
+
+ /* Insert cleanup code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* if the object is a director, and the method call originated from its
+ * underlying ocaml object, resolve the call by going up the c++
+ * inheritance chain. otherwise try to resolve the method in ocaml.
+ * without this check an infinite loop is set up between the director and
+ * shadow class method calls.
+ */
+
+ // NOTE: this code should only be inserted if this class is the
+ // base class of a director class. however, in general we haven't
+ // yet analyzed all classes derived from this one to see if they are
+ // directors. furthermore, this class may be used as the base of
+ // a director class defined in a completely different module at a
+ // later time, so this test must be included whether or not directorbase
+ // is true. we do skip this code if directors have not been enabled
+ // at the command line to preserve source-level compatibility with
+ // non-polymorphic swig. also, if this wrapper is for a smart-pointer
+ // method, there is no need to perform the test since the calling object
+ // (the smart-pointer) and the director object (the "pointee") are
+ // distinct.
+
+ director_method = is_member_director(n) && !is_smart_pointer() && !destructor;
+ if (director_method) {
+ Wrapper_add_local(f, "director", "Swig::Director *director = 0");
+ Printf(f->code, "director = dynamic_cast<Swig::Director *>(arg1);\n");
+ Wrapper_add_local(f, "upcall", "bool upcall = false");
+ Append(f->code, "upcall = (director);\n");
+ }
+
+ // Now write code to make the function call
+ Swig_director_emit_dynamic_cast(n, f);
+ String *actioncode = emit_action(n);
+
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ Replaceall(tm, "$result", "rv");
+ Replaceall(tm, "$ntype", return_type_normalized);
+ Printv(f->code, tm, "\n", NIL);
+ } else {
+ throw_unhandled_ocaml_type_error(d, "out");
+ }
+ emit_return_variable(n, d, f);
+
+ // Dump the argument output code
+ Printv(f->code, Char(outarg), NIL);
+
+ // Dump the argument cleanup code
+ Printv(f->code, Char(cleanup), NIL);
+
+ // Look for any remaining cleanup
+
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printv(f->code, tm, "\n", NIL);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+
+ // Free any memory allocated by the function being wrapped..
+
+ if ((tm = Swig_typemap_lookup("swig_result", n, Swig_cresult_name(), 0))) {
+ Printv(f->code, tm, "\n", NIL);
+ }
+ // Wrap things up (in a manner of speaking)
+
+ Printv(f->code, tab4, "swig_result = caml_list_append(swig_result,rv);\n", NIL);
+ Printv(f->code, tab4, "CAMLreturn(swig_result);\n", NIL);
+ Printv(f->code, "}\n", NIL);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", iname);
+
+ Wrapper_print(f, f_wrappers);
+
+ if (isOverloaded) {
+ if (!Getattr(n, "sym:nextSibling")) {
+ int maxargs;
+ Wrapper *df = NewWrapper();
+ String *dispatch = Swig_overload_dispatch(n,
+ "free(argv);\n" "CAMLreturn(%s(args));\n",
+ &maxargs);
+
+ Wrapper_add_local(df, "argv", "CAML_VALUE *argv");
+
+ /* Undifferentiate name .. this is the dispatch function */
+ wname = Swig_name_wrapper(iname);
+ /* Do this to disambiguate functions emitted from different
+ * modules */
+ Append(wname, module);
+
+ Printv(df->def,
+ "SWIGEXT CAML_VALUE ", wname, "(CAML_VALUE args) {\n" " CAMLparam1(args);\n" " int i;\n" " int argc = caml_list_length(args);\n", NIL);
+ Printv(df->code,
+ "argv = (CAML_VALUE *)malloc( argc * sizeof( CAML_VALUE ) );\n"
+ "for( i = 0; i < argc; i++ ) {\n" " argv[i] = caml_list_nth(args,i);\n" "}\n", NIL);
+ Printv(df->code, dispatch, "\nfree(argv);\n", NIL);
+ Node *sibl = n;
+ while (Getattr(sibl, "sym:previousSibling"))
+ sibl = Getattr(sibl, "sym:previousSibling");
+ String *protoTypes = NewString("");
+ do {
+ String *fulldecl = Swig_name_decl(sibl);
+ Printf(protoTypes, "\n\" %s\\n\"", fulldecl);
+ Delete(fulldecl);
+ } while ((sibl = Getattr(sibl, "sym:nextSibling")));
+ Printf(df->code, "caml_failwith(\"Wrong number or type of arguments for overloaded function '%s'.\\n\""
+ "\n\" Possible C/C++ prototypes are:\\n\"%s);\n", iname, protoTypes);
+ Delete(protoTypes);
+ Printv(df->code, "}\n", NIL);
+ Wrapper_print(df, f_wrappers);
+
+ DelWrapper(df);
+ Delete(dispatch);
+ }
+ }
+
+ if (expose_func) {
+ Printf(f_mlbody, "external %s_f : c_obj list -> c_obj list = \"%s\" ;;\n", mangled_name, wname);
+ Printf(f_mlbody, "let %s arg = match %s_f (%s(fnhelper arg)) with\n", mangled_name, mangled_name,
+ in_constructor && Swig_directorclass(getCurrentClass()) ? "director_core_helper " : "");
+ Printf(f_mlbody, " [] -> C_void\n"
+ "| [x] -> (if %s then Gc.finalise \n"
+ " (fun x -> ignore ((invoke x) \"~\" C_void)) x) ; x\n"
+ "| lst -> C_list lst ;;\n", newobj ? "true" : "false");
+ }
+
+ if ((!classmode || in_constructor || in_destructor || static_member_function) && expose_func)
+ Printf(f_mlibody, "val %s : c_obj -> c_obj\n", mangled_name);
+
+ Delete(proc_name);
+ Delete(target);
+ Delete(arg);
+ Delete(outarg);
+ Delete(cleanup);
+ Delete(build);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * variableWrapper()
+ *
+ * Create a link to a C variable.
+ * This creates a single function _wrap_varname().
+ * This function takes a single optional argument. If supplied, it means
+ * we are setting this variable to some value. If omitted, it means we are
+ * simply evaluating this variable. We return the value of the variable
+ * in both cases.
+ *
+ * symname is the name of the variable with respect to C. This
+ * may need to differ from the original name in the case of enums.
+ * enumvname is the name of the variable with respect to ocaml. This
+ * will vary if the variable has been renamed.
+ * ------------------------------------------------------------ */
+
+ virtual int variableWrapper(Node *n) {
+ char *name = GetChar(n, "feature:symname");
+ String *iname = Getattr(n, "feature:enumvname");
+ String *mname = mangleNameForCaml(iname);
+ SwigType *t = Getattr(n, "type");
+
+ String *proc_name = NewString("");
+ String *tm;
+ Wrapper *f;
+
+ if (!name) {
+ name = GetChar(n, "name");
+ }
+
+ if (!iname) {
+ iname = Getattr(n, "sym:name");
+ mname = mangleNameForCaml(NewString(iname));
+ }
+
+ if (!iname || !addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ f = NewWrapper();
+
+ // evaluation function names
+ String *var_name = Swig_name_wrapper(iname);
+
+ // Build the name for OCaml.
+ Printv(proc_name, iname, NIL);
+ Setattr(n, "wrap:name", proc_name);
+
+ Printf(f->def, "SWIGEXT CAML_VALUE %s(CAML_VALUE args) {\n", var_name);
+ // Printv(f->def, "#define FUNC_NAME \"", proc_name, "\"", NIL);
+
+ Wrapper_add_local(f, "args", "CAMLparam1(args)");
+ Wrapper_add_local(f, "swig_result", "SWIG_CAMLlocal1(swig_result)");
+ Printf(f->code, "swig_result = Val_unit;\n");
+
+ if (!GetFlag(n, "feature:immutable")) {
+ /* Check for a setting of the variable value */
+ Printf(f->code, "if (args != Val_int(0)) {\n");
+ if ((tm = Swig_typemap_lookup("varin", n, name, 0))) {
+ Replaceall(tm, "$input", "args");
+ emit_action_code(n, f->code, tm);
+ } else if ((tm = Swig_typemap_lookup("in", n, name, 0))) {
+ Replaceall(tm, "$input", "args");
+ emit_action_code(n, f->code, tm);
+ } else {
+ throw_unhandled_ocaml_type_error(t, "varin/in");
+ }
+ Printf(f->code, "}\n");
+ }
+ // Now return the value of the variable (regardless
+ // of evaluating or setting)
+
+ if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
+ Replaceall(tm, "$result", "swig_result");
+ emit_action_code(n, f->code, tm);
+ } else if ((tm = Swig_typemap_lookup("out", n, name, 0))) {
+ Replaceall(tm, "$result", "swig_result");
+ emit_action_code(n, f->code, tm);
+ } else {
+ throw_unhandled_ocaml_type_error(t, "varout/out");
+ }
+
+ Printf(f->code, "\nCAMLreturn(swig_result);\n");
+ Printf(f->code, "}\n");
+
+ Wrapper_print(f, f_wrappers);
+
+ // Now add symbol to the Ocaml interpreter
+
+ if (GetFlag(n, "feature:immutable")) {
+ Printf(f_mlbody, "external _%s : c_obj -> Swig.c_obj = \"%s\" \n", mname, var_name);
+ Printf(f_mlibody, "val _%s : c_obj -> Swig.c_obj\n", iname);
+ if (const_enum) {
+ Printf(f_enum_to_int, " | `%s -> _%s C_void\n", mname, mname);
+ Printf(f_int_to_enum, " if y = (get_int (_%s C_void)) then `%s else\n", mname, mname);
+ }
+ } else {
+ Printf(f_mlbody, "external _%s : c_obj -> c_obj = \"%s\"\n", mname, var_name);
+ Printf(f_mlibody, "external _%s : c_obj -> c_obj = \"%s\"\n", mname, var_name);
+ }
+
+ Delete(var_name);
+ Delete(proc_name);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * staticmemberfunctionHandler --
+ * Overridden to set static_member_function
+ * ------------------------------------------------------------ */
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+ static_member_function = 1;
+ Language::staticmemberfunctionHandler(n);
+ static_member_function = 0;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constantWrapper()
+ *
+ * The one trick here is that we have to make sure we rename the
+ * constant to something useful that doesn't collide with the
+ * original if any exists.
+ * ------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ String *name = Getattr(n, "feature:symname");
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ SwigType *qname = Getattr(n, "qualified:name");
+
+ if (qname)
+ value = qname;
+
+ if (!name) {
+ name = mangleNameForCaml(Getattr(n, "name"));
+ Insert(name, 0, "_swig_wrap_");
+ Setattr(n, "feature:symname", name);
+ }
+ // See if there's a typemap
+
+ // Create variable and assign it a value
+ Printf(f_header, "static %s = %s;\n", SwigType_str(type, name), value);
+ SetFlag(n, "feature:immutable");
+ variableWrapper(n);
+ return SWIG_OK;
+ }
+
+ int constructorHandler(Node *n) {
+ int ret;
+
+ in_constructor = 1;
+ ret = Language::constructorHandler(n);
+ in_constructor = 0;
+
+ return ret;
+ }
+
+ /* destructorHandler:
+ * Turn on destructor flag to inform decisions in functionWrapper
+ */
+
+ int destructorHandler(Node *n) {
+ int ret;
+
+ in_destructor = 1;
+ ret = Language::destructorHandler(n);
+ in_destructor = 0;
+
+ return ret;
+ }
+
+ /* copyconstructorHandler:
+ * Turn on constructor and copyconstructor flags for functionWrapper
+ */
+
+ int copyconstructorHandler(Node *n) {
+ int ret;
+
+ in_copyconst = 1;
+ in_constructor = 1;
+ ret = Language::copyconstructorHandler(n);
+ in_constructor = 0;
+ in_copyconst = 0;
+
+ return ret;
+ }
+
+ /**
+ * A simple, somewhat general purpose function for writing to multiple
+ * streams from a source template. This allows the user to define the
+ * class definition in ways different from the one I have here if they
+ * want to. It will also make the class definition system easier to
+ * fiddle with when I want to change methods, etc.
+ */
+
+ void Multiwrite(String *s) {
+ char *find_marker = strstr(Char(s), "(*Stream:");
+ while (find_marker) {
+ char *next = strstr(find_marker, "*)");
+ find_marker += strlen("(*Stream:");
+
+ if (next) {
+ int num_chars = (int)(next - find_marker);
+ String *stream_name = NewString(find_marker);
+ Delslice(stream_name, num_chars, Len(stream_name));
+ File *fout = Swig_filebyname(stream_name);
+ if (fout) {
+ next += strlen("*)");
+ char *following = strstr(next, "(*Stream:");
+ find_marker = following;
+ if (!following)
+ following = next + strlen(next);
+ String *chunk = NewString(next);
+ Delslice(chunk, (int)(following - next), Len(chunk));
+ Printv(fout, chunk, NIL);
+ }
+ }
+ }
+ }
+
+ bool isSimpleType(String *name) {
+ char *ch = Char(name);
+
+ return !(strchr(ch, '(') || strchr(ch, '<') || strchr(ch, ')') || strchr(ch, '>'));
+ }
+
+ /* We accept all chars in identifiers because we use strings to index
+ * them. */
+ int validIdentifier(String *name) {
+ return Len(name) > 0 ? 1 : 0;
+ }
+
+ /* classHandler
+ *
+ * Create a "class" definition for ocaml. I thought quite a bit about
+ * how I should do this part of it, and arrived here, using a function
+ * invocation to select a method, and dispatch. This can obviously be
+ * done better, but I can't see how, given that I want to support
+ * overloaded methods, out parameters, and operators.
+ *
+ * I needed a system that would do this:
+ *
+ * a Be able to call these methods:
+ * int foo( int x );
+ * float foo( int x, int &out );
+ *
+ * b Be typeable, even in the presence of mutually dependent classes.
+ *
+ * c Support some form of operator invocation.
+ *
+ * (c) I chose strings for the method names so that "+=" would be a
+ * valid method name, and the somewhat natural << (invoke x) "+=" y >>
+ * would work.
+ *
+ * (a) (b) Since the c_obj type exists, it's easy to return C_int in one
+ * case and C_list [ C_float ; C_int ] in the other. This makes tricky
+ * problems with out parameters disappear; they're simply appended to the
+ * return list.
+ *
+ * (b) Since every item that comes from C++ is the same type, there is no
+ * problem with the following:
+ *
+ * class Foo;
+ * class Bar { Foo *toFoo(); }
+ * class Foo { Bar *toBar(); }
+ *
+ * Since the Objective caml types of Foo and Bar are the same. Now that
+ * I correctly incorporate SWIG's typechecking, this isn't a big deal.
+ *
+ * The class is in the form of a function returning a c_obj. The c_obj
+ * is a C_obj containing a function which invokes a method on the
+ * underlying object given its type.
+ *
+ * The name emitted here is normalized before being sent to
+ * Callback.register, because we need this string to look up properly
+ * when the typemap passes the descriptor string. I've been considering
+ * some, possibly more forgiving method that would do some transformations
+ * on the $descriptor in order to find a potential match. This is for
+ * later.
+ *
+ * Important things to note:
+ *
+ * We rely on exception handling (BadMethodName) in order to call an
+ * ancestor. This can be improved.
+ *
+ * The method used to get :classof could be improved to look at the type
+ * info that the base pointer contains. It's really an error to have a
+ * SWIG-generated object that does not contain type info, since the
+ * existence of the object means that SWIG knows the type.
+ *
+ * :parents could use :classof to tell what class it is and make a better
+ * decision. This could be nice, (i.e. provide a run-time graph of C++
+ * classes represented);.
+ *
+ * I can't think of a more elegant way of converting a C_obj fun to a
+ * pointer than "operator &"...
+ *
+ * Added a 'sizeof' that will allow you to do the expected thing.
+ * This should help users to fill buffer structs and the like (as is
+ * typical in windows-styled code). It's only enabled if you give
+ * %feature(sizeof) and then, only for simple types.
+ *
+ * Overall, carrying the list of methods and base classes has worked well.
+ * It allows me to give the Ocaml user introspection over their objects.
+ */
+
+ int classHandler(Node *n) {
+ String *name = Getattr(n, "name");
+ classname = Getattr(n, "sym:name");
+
+ if (!name)
+ return SWIG_OK;
+
+ String *mangled_name = mangleNameForCaml(name);
+ String *this_class_def = NewString(f_classtemplate);
+ String *name_normalized = normalizeTemplatedClassName(name);
+ String *old_class_ctors = f_class_ctors;
+ String *base_classes = NewString("");
+ f_class_ctors = NewString("");
+ bool sizeof_feature = generate_sizeof && isSimpleType(name);
+
+
+ classmode = true;
+ int rv = Language::classHandler(n);
+ classmode = false;
+
+ if (sizeof_feature) {
+ Printf(f_wrappers,
+ "SWIGEXT CAML_VALUE _wrap_%s_sizeof( CAML_VALUE args ) {\n"
+ " CAMLparam1(args);\n" " CAMLreturn(Val_int(sizeof(%s)));\n" "}\n", mangled_name, name_normalized);
+
+ Printf(f_mlbody, "external __%s_sizeof : unit -> int = " "\"_wrap_%s_sizeof\"\n", mangled_name, mangled_name);
+ }
+
+
+ /* Insert sizeof operator for concrete classes */
+ if (sizeof_feature) {
+ Printv(f_class_ctors, "\"sizeof\" , (fun args -> C_int (__", mangled_name, "_sizeof ())) ;\n", NIL);
+ }
+ /* Handle up-casts in a nice way */
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator b;
+ b = First(baselist);
+ while (b.item) {
+ String *bname = Getattr(b.item, "name");
+ if (bname) {
+ String *base_create = NewString("");
+ Printv(base_create, "(create_class \"", bname, "\")", NIL);
+ Printv(f_class_ctors, " \"::", bname, "\", (fun args -> ", base_create, " args) ;\n", NIL);
+ Printv(base_classes, base_create, " ;\n", NIL);
+ }
+ b = Next(b);
+ }
+ }
+
+ Replaceall(this_class_def, "$classname", mangled_name);
+ Replaceall(this_class_def, "$normalized", name_normalized);
+ Replaceall(this_class_def, "$realname", name);
+ Replaceall(this_class_def, "$baselist", base_classes);
+ Replaceall(this_class_def, "$classbody", f_class_ctors);
+
+ Delete(f_class_ctors);
+ f_class_ctors = old_class_ctors;
+
+ // Actually write out the class definition
+
+ Multiwrite(this_class_def);
+
+ Setattr(n, "ocaml:ctor", mangled_name);
+
+ return rv;
+ }
+
+ String *normalizeTemplatedClassName(String *name) {
+ String *name_normalized = SwigType_typedef_resolve_all(name);
+ bool took_action;
+
+ do {
+ took_action = false;
+
+ if (is_a_pointer(name_normalized)) {
+ SwigType_del_pointer(name_normalized);
+ took_action = true;
+ }
+
+ if (is_a_reference(name_normalized)) {
+ oc_SwigType_del_reference(name_normalized);
+ took_action = true;
+ }
+
+ if (is_an_array(name_normalized)) {
+ oc_SwigType_del_array(name_normalized);
+ took_action = true;
+ }
+ } while (took_action);
+
+ return SwigType_str(name_normalized, 0);
+ }
+
+ /*
+ * Produce the symbol name that ocaml will use when referring to the
+ * target item. I wonder if there's a better way to do this:
+ *
+ * I shudder to think about doing it with a hash lookup, but that would
+ * make a couple of things easier:
+ */
+
+ String *mangleNameForCaml(String *s) {
+ String *out = Copy(s);
+ Replaceall(out, " ", "_xx");
+ Replaceall(out, "::", "_xx");
+ Replaceall(out, ",", "_x");
+ Replaceall(out, "+", "_xx_plus");
+ Replaceall(out, "-", "_xx_minus");
+ Replaceall(out, "<", "_xx_ldbrace");
+ Replaceall(out, ">", "_xx_rdbrace");
+ Replaceall(out, "!", "_xx_not");
+ Replaceall(out, "%", "_xx_mod");
+ Replaceall(out, "^", "_xx_xor");
+ Replaceall(out, "*", "_xx_star");
+ Replaceall(out, "&", "_xx_amp");
+ Replaceall(out, "|", "_xx_or");
+ Replaceall(out, "(", "_xx_lparen");
+ Replaceall(out, ")", "_xx_rparen");
+ Replaceall(out, "[", "_xx_lbrace");
+ Replaceall(out, "]", "_xx_rbrace");
+ Replaceall(out, "~", "_xx_bnot");
+ Replaceall(out, "=", "_xx_equals");
+ Replaceall(out, "/", "_xx_slash");
+ Replaceall(out, ".", "_xx_dot");
+ return out;
+ }
+
+ SwigType *fully_qualified_enum_type(Node *n) {
+ Node *parent = 0;
+ String *fully_qualified_name = NewString("");
+ String *parent_type = 0;
+
+ parent = parentNode(n);
+ while (parent) {
+ parent_type = nodeType(parent);
+ if (Getattr(parent, "name")) {
+ String *parent_copy = NewStringf("%s::", Getattr(parent, "name"));
+ if (Cmp(parent_type, "class") == 0 || Cmp(parent_type, "namespace") == 0)
+ Insert(fully_qualified_name, 0, parent_copy);
+ Delete(parent_copy);
+ }
+ if (!Cmp(parent_type, "class"))
+ break;
+ parent = parentNode(parent);
+ }
+
+ return fully_qualified_name;
+ }
+
+ /* Benedikt Grundmann inspired --> Enum wrap styles */
+
+ int enumvalueDeclaration(Node *n) {
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ SwigType *qtype = 0;
+
+ if (name_qualifier_type) {
+ qtype = Copy(name_qualifier_type);
+ Printv(qtype, name, NIL);
+ }
+
+ if (const_enum && qtype && symname && !Getattr(seen_enumvalues, symname)) {
+ Setattr(seen_enumvalues, symname, "true");
+ SetFlag(n, "feature:immutable");
+ Setattr(n, "feature:enumvalue", "1"); // this does not appear to be used
+
+ Setattr(n, "qualified:name", SwigType_namestr(qtype));
+
+ String *evname = SwigType_manglestr(qtype);
+ Insert(evname, 0, "SWIG_ENUM_");
+
+ Setattr(n, "feature:enumvname", symname);
+ Setattr(n, "feature:symname", evname);
+ Delete(evname);
+ Printf(f_enumtypes_value, "| `%s\n", symname);
+
+ return Language::enumvalueDeclaration(n);
+ } else
+ return SWIG_OK;
+ }
+
+ /* -------------------------------------------------------------------
+ * This function is a bit uglier than it deserves.
+ *
+ * I used to direct lookup the name of the enum. Now that certain fixes
+ * have been made in other places, the names of enums are now fully
+ * qualified, which is a good thing, overall, but requires me to do
+ * some legwork.
+ *
+ * The other thing that uglifies this function is the varying way that
+ * typedef enum and enum are handled. I need to produce consistent names,
+ * which means looking up and registering by typedef and enum name. */
+ int enumDeclaration(Node *n) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ String *name = Getattr(n, "name");
+ if (name) {
+ String *oname = NewString(name);
+ /* name is now fully qualified */
+ String *fully_qualified_name = NewString(name);
+ bool seen_enum = false;
+ if (name_qualifier_type)
+ Delete(name_qualifier_type);
+ char *strip_position;
+ name_qualifier_type = fully_qualified_enum_type(n);
+
+ strip_position = strstr(Char(oname), "::");
+
+ while (strip_position) {
+ strip_position += 2;
+ oname = NewString(strip_position);
+ strip_position = strstr(Char(oname), "::");
+ }
+
+ seen_enum = (Getattr(seen_enums, fully_qualified_name) ? true : false);
+
+ if (!seen_enum) {
+ const_enum = true;
+ Printf(f_enum_to_int, "| `%s -> (match y with\n", oname);
+ Printf(f_int_to_enum, "| `%s -> C_enum (\n", oname);
+ /* * * * A note about enum name resolution * * * *
+ * This code should now work, but I think we can do a bit better.
+ * The problem I'm having is that swig isn't very precise about
+ * typedef name resolution. My opinion is that SwigType_typedef
+ * resolve_all should *always* return the enum tag if one exists,
+ * rather than the admittedly friendlier enclosing typedef.
+ *
+ * This would make one of the cases below unnecessary.
+ * * * */
+ Printf(f_mlbody, "let _ = Callback.register \"%s_marker\" (`%s)\n", fully_qualified_name, oname);
+ if (!strncmp(Char(fully_qualified_name), "enum ", 5)) {
+ String *fq_noenum = NewString(Char(fully_qualified_name) + 5);
+ Printf(f_mlbody,
+ "let _ = Callback.register \"%s_marker\" (`%s)\n" "let _ = Callback.register \"%s_marker\" (`%s)\n", fq_noenum, oname, fq_noenum, name);
+ }
+
+ Printf(f_enumtypes_type, "| `%s\n", oname);
+ Insert(fully_qualified_name, 0, "enum ");
+ Setattr(seen_enums, fully_qualified_name, n);
+ }
+ }
+
+ int ret = Language::enumDeclaration(n);
+
+ if (const_enum) {
+ Printf(f_int_to_enum, "`Int y)\n");
+ Printf(f_enum_to_int, "| `Int x -> Swig.C_int x\n" "| _ -> raise (LabelNotFromThisEnum v))\n");
+ }
+
+ const_enum = false;
+
+ return ret;
+ }
+
+ /* ----------------------------------------------------------------------------
+ * BEGIN C++ Director Class modifications
+ * ------------------------------------------------------------------------- */
+
+ /*
+ * Modified polymorphism code for Ocaml language module.
+ *
+ * TODO
+ *
+ * Move some boilerplate code generation to Swig_...() functions.
+ *
+ */
+
+ /* ---------------------------------------------------------------
+ * classDirectorMethod()
+ *
+ * Emit a virtual director method to pass a method call on to the
+ * underlying Python object.
+ *
+ * --------------------------------------------------------------- */
+
+ int classDirectorMethod(Node *n, Node *parent, String *super) {
+ int is_void = 0;
+ int is_pointer = 0;
+ String *storage = Getattr(n, "storage");
+ String *value = Getattr(n, "value");
+ String *decl = Getattr(n, "decl");
+ String *returntype = Getattr(n, "type");
+ String *name = Getattr(n, "name");
+ String *classname = Getattr(parent, "sym:name");
+ String *c_classname = Getattr(parent, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *declaration = NewString("");
+ ParmList *l = Getattr(n, "parms");
+ Wrapper *w = NewWrapper();
+ String *tm;
+ String *wrap_args = NewString("");
+ int status = SWIG_OK;
+ int idx;
+ bool pure_virtual = false;
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+
+ if (Cmp(storage, "virtual") == 0) {
+ if (Cmp(value, "0") == 0) {
+ pure_virtual = true;
+ }
+ }
+ Printf(w->locals, "CAMLparam0();\n");
+
+ /* determine if the method returns a pointer */
+ is_pointer = SwigType_ispointer_return(decl);
+ is_void = (!Cmp(returntype, "void") && !is_pointer);
+
+ /* virtual method definition */
+ String *target;
+ String *pclassname = NewStringf("SwigDirector_%s", classname);
+ String *qualified_name = NewStringf("%s::%s", pclassname, name);
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+ /* header declaration */
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Get any exception classes in the throws typemap
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ ParmList *throw_parm_list = 0;
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ Parm *p;
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list)
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+ String *str = SwigType_str(Getattr(p, "type"), 0);
+ Append(w->def, str);
+ Append(declaration, str);
+ Delete(str);
+ }
+ }
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+ /* declare method return value
+ * if the return value is a reference or const reference, a specialized typemap must
+ * handle it, including declaration of c_result ($result).
+ */
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ }
+ } else {
+ String *cres = SwigType_lstr(returntype, "c_result");
+ Printf(w->code, "%s;\n", cres);
+ Delete(cres);
+ }
+ }
+
+ if (ignored_method) {
+ if (!pure_virtual) {
+ String *super_call = Swig_method_call(super, l);
+ if (is_void)
+ Printf(w->code, "%s;\n", super_call);
+ else
+ Printf(w->code, "CAMLreturn_type(%s);\n", super_call);
+ Delete(super_call);
+ } else {
+ Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname),
+ SwigType_namestr(name));
+ }
+ } else {
+ Wrapper_add_local(w, "swig_result", "SWIG_CAMLlocal2(swig_result, args)");
+ /* attach typemaps to arguments (C/C++ -> Ocaml) */
+ String *arglist = NewString("");
+
+ Swig_director_parms_fixup(l);
+
+ Swig_typemap_attach_parms("in", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ Parm *p;
+ int num_arguments = emit_num_arguments(l);
+ int i;
+ char source[256];
+
+ /* build argument list and type conversion string */
+ for (i = 0, idx = 0, p = l; i < num_arguments; i++) {
+
+ while (Getattr(p, "tmap:ignore")) {
+ p = Getattr(p, "tmap:ignore:next");
+ }
+
+ String *pname = Getattr(p, "name");
+ String *ptype = Getattr(p, "type");
+
+ Putc(',', arglist);
+ if ((tm = Getattr(p, "tmap:directorin")) != 0) {
+ Setattr(p, "emit:directorinput", pname);
+ Replaceall(tm, "$input", pname);
+ Replaceall(tm, "$owner", "0");
+ if (Len(tm) == 0)
+ Append(tm, pname);
+ Printv(wrap_args, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorin:next");
+ continue;
+ } else if (Cmp(ptype, "void")) {
+ /* special handling for pointers to other C++ director classes.
+ * ideally this would be left to a typemap, but there is currently no
+ * way to selectively apply the dynamic_cast<> to classes that have
+ * directors. in other words, the type "SwigDirector_$1_lname" only exists
+ * for classes with directors. we avoid the problem here by checking
+ * module.wrap::directormap, but it's not clear how to get a typemap to
+ * do something similar. perhaps a new default typemap (in addition
+ * to SWIGTYPE) called DIRECTORTYPE?
+ */
+ if (SwigType_ispointer(ptype) || SwigType_isreference(ptype)) {
+ Node *module = Getattr(parent, "module");
+ Node *target = Swig_directormap(module, ptype);
+ sprintf(source, "obj%d", idx++);
+ String *nonconst = 0;
+ /* strip pointer/reference --- should move to Swig/stype.c */
+ String *nptype = NewString(Char(ptype) + 2);
+ /* name as pointer */
+ String *ppname = Copy(pname);
+ if (SwigType_isreference(ptype)) {
+ Insert(ppname, 0, "&");
+ }
+ /* if necessary, cast away const since Python doesn't support it! */
+ if (SwigType_isconst(nptype)) {
+ nonconst = NewStringf("nc_tmp_%s", pname);
+ String *nonconst_i = NewStringf("= const_cast< %s >(%s)", SwigType_lstr(ptype, 0), ppname);
+ Wrapper_add_localv(w, nonconst, SwigType_lstr(ptype, 0), nonconst, nonconst_i, NIL);
+ Delete(nonconst_i);
+ Swig_warning(WARN_LANG_DISCARD_CONST, input_file, line_number,
+ "Target language argument '%s' discards const in director method %s::%s.\n", SwigType_str(ptype, pname),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ } else {
+ nonconst = Copy(ppname);
+ }
+ Delete(nptype);
+ Delete(ppname);
+ String *mangle = SwigType_manglestr(ptype);
+ if (target) {
+ String *director = NewStringf("director_%s", mangle);
+ Wrapper_add_localv(w, director, "Swig::Director *", director, "= 0", NIL);
+ Wrapper_add_localv(w, source, "CAML_VALUE", source, "= Val_unit", NIL);
+ Printf(wrap_args, "%s = dynamic_cast<Swig::Director *>(%s);\n", director, nonconst);
+ Printf(wrap_args, "if (!%s) {\n", director);
+ Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
+ Printf(wrap_args, "} else {\n");
+ Printf(wrap_args, "%s = %s->swig_get_self();\n", source, director);
+ Printf(wrap_args, "}\n");
+ Delete(director);
+ Printv(arglist, source, NIL);
+ } else {
+ Wrapper_add_localv(w, source, "CAML_VALUE", source, "= Val_unit", NIL);
+ Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
+ //Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE_p_%s, 0);\n",
+ // source, nonconst, base);
+ Printv(arglist, source, NIL);
+ }
+ Delete(mangle);
+ Delete(nonconst);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
+ "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(ptype, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_NOWRAP;
+ break;
+ }
+ }
+ p = nextSibling(p);
+ }
+
+ Printv(w->code, "swig_result = Val_unit;\n", 0);
+ Printf(w->code, "args = Val_unit;\n");
+
+ /* wrap complex arguments to values */
+ Printv(w->code, wrap_args, NIL);
+
+ /* pass the method call on to the OCaml object */
+ Printv(w->code,
+ "swig_result = caml_swig_alloc(1,C_list);\n" "SWIG_Store_field(swig_result,0,args);\n" "args = swig_result;\n" "swig_result = Val_unit;\n", 0);
+ Printf(w->code, "static const CAML_VALUE *swig_ocaml_func_val = NULL;\n" "if (!swig_ocaml_func_val) {\n");
+ Printf(w->code, " swig_ocaml_func_val = caml_named_value(\"swig_runmethod\");\n }\n");
+ Printf(w->code, "swig_result = caml_callback3(*swig_ocaml_func_val,swig_get_self(),caml_copy_string(\"%s\"),args);\n", Getattr(n, "name"));
+ /* exception handling */
+ tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
+ if (!tm) {
+ tm = Getattr(n, "feature:director:except");
+ }
+ if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
+ Printf(w->code, "if (!%s) {\n", Swig_cresult_name());
+ Printf(w->code, " CAML_VALUE error = *caml_named_value(\"director_except\");\n");
+ Replaceall(tm, "$error", "error");
+ Printv(w->code, Str(tm), "\n", NIL);
+ Printf(w->code, "}\n");
+ }
+
+ /*
+ * Python method may return a simple object, or a tuple.
+ * for in/out arguments, we have to extract the appropriate values from the
+ * argument list, then marshal everything back to C/C++ (return value and
+ * output arguments).
+ */
+
+ /* marshal return value and other outputs (if any) from value to C/C++
+ * type */
+
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+
+ tm = Swig_typemap_lookup("directorout", n, "c_result", w);
+ if (tm != 0) {
+ Replaceall(tm, "$input", "swig_result");
+ /* TODO check this */
+ if (Getattr(n, "wrap:disown")) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+ Replaceall(tm, "$result", "c_result");
+ Printv(w->code, tm, "\n", NIL);
+ }
+
+ /* marshal outputs */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
+ Replaceall(tm, "$result", "swig_result");
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ Delete(arglist);
+ Delete(cleanup);
+ Delete(outarg);
+ }
+
+ /* any existing helper functions to handle this? */
+ if (!is_void) {
+ if (!(ignored_method && !pure_virtual)) {
+ String *rettype = SwigType_str(returntype, 0);
+ if (!SwigType_isreference(returntype)) {
+ Printf(w->code, "CAMLreturn_type((%s)c_result);\n", rettype);
+ } else {
+ Printf(w->code, "CAMLreturn_type((%s)*c_result);\n", rettype);
+ }
+ Delete(rettype);
+ }
+ } else {
+ Printf(w->code, "CAMLreturn0;\n");
+ }
+
+ Printf(w->code, "}\n");
+
+ // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewString("");
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+
+ /* emit the director method */
+ if (status == SWIG_OK) {
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+
+ /* clean up */
+ Delete(wrap_args);
+ Delete(pclassname);
+ DelWrapper(w);
+ return status;
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorConstructor()
+ * ------------------------------------------------------------ */
+
+ int classDirectorConstructor(Node *n) {
+ Node *parent = Getattr(n, "parentNode");
+ String *sub = NewString("");
+ String *decl = Getattr(n, "decl");
+ String *supername = Swig_class_name(parent);
+ String *classname = NewString("");
+ Printf(classname, "SwigDirector_%s", supername);
+
+ /* insert self parameter */
+ Parm *p, *q;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms = CopyParmList(superparms);
+ String *type = NewString("CAML_VALUE");
+ p = NewParm(type, NewString("self"), n);
+ q = Copy(p);
+ set_nextSibling(q, superparms);
+ set_nextSibling(p, parms);
+ parms = p;
+
+ if (!Getattr(n, "defaultargs")) {
+ /* constructor */
+ {
+ Wrapper *w = NewWrapper();
+ String *call;
+ String *basetype = Getattr(parent, "classtype");
+ String *target = Swig_method_decl(0, decl, classname, parms, 0);
+ call = Swig_csuperclass_call(0, basetype, superparms);
+ Printf(w->def, "%s::%s: %s, Swig::Director(self) { }", classname, target, call);
+ Delete(target);
+ Wrapper_print(w, f_directors);
+ Delete(call);
+ DelWrapper(w);
+ }
+
+ /* constructor header */
+ {
+ String *target = Swig_method_decl(0, decl, classname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+
+ Setattr(n, "parms", q);
+ Language::classDirectorConstructor(n);
+
+ Delete(sub);
+ Delete(classname);
+ Delete(supername);
+ //Delete(parms);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorDefaultConstructor()
+ * ------------------------------------------------------------ */
+
+ int classDirectorDefaultConstructor(Node *n) {
+ String *classname;
+ classname = Swig_class_name(n);
+
+ /* insert self parameter */
+ Parm *p, *q;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms = CopyParmList(superparms);
+ String *type = NewString("CAML_VALUE");
+ p = NewParm(type, NewString("self"), n);
+ q = Copy(p);
+ set_nextSibling(p, parms);
+
+ {
+ Wrapper *w = NewWrapper();
+ Printf(w->def, "SwigDirector_%s::SwigDirector_%s(CAML_VALUE self) : Swig::Director(self) { }", classname, classname);
+ Wrapper_print(w, f_directors);
+ DelWrapper(w);
+ }
+ Printf(f_directors_h, " SwigDirector_%s(CAML_VALUE self);\n", classname);
+ Delete(classname);
+ Setattr(n, "parms", q);
+ return Language::classDirectorDefaultConstructor(n);
+ }
+
+ int classDirectorInit(Node *n) {
+ String *declaration = Swig_director_declaration(n);
+ Printf(f_directors_h, "\n" "%s\n" "public:\n", declaration);
+ Delete(declaration);
+ return Language::classDirectorInit(n);
+ }
+
+ int classDirectorEnd(Node *n) {
+ Printf(f_directors_h, "};\n\n");
+ return Language::classDirectorEnd(n);
+ }
+
+ /* ---------------------------------------------------------------------
+ * typedefHandler
+ *
+ * This is here in order to maintain the correct association between
+ * typedef names and enum names.
+ *
+ * Since I implement enums as polymorphic variant tags, I need to call
+ * back into ocaml to evaluate them. This requires a string that can
+ * be generated in the typemaps, and also at SWIG time to be the same
+ * string. The problem that arises is that SWIG variously generates
+ * enum e_name_tag
+ * e_name_tag
+ * e_typedef_name
+ * for
+ * typedef enum e_name_tag { ... } e_typedef_name;
+ *
+ * Since I need these strings to be consistent, I must maintain a correct
+ * association list between typedef and enum names.
+ * --------------------------------------------------------------------- */
+ int typedefHandler(Node *n) {
+ String *type = Getattr(n, "type");
+ Node *enum_node = type ? Getattr(seen_enums, type) : 0;
+ if (enum_node) {
+ String *name = Getattr(enum_node, "name");
+
+ Printf(f_mlbody, "let _ = Callback.register \"%s_marker\" (`%s)\n", Getattr(n, "name"), name);
+
+ }
+ return SWIG_OK;
+ }
+
+ String *runtimeCode() {
+ String *s = Swig_include_sys("ocamlrun.swg");
+ if (!s) {
+ Printf(stderr, "*** Unable to open 'ocamlrun.swg'\n");
+ s = NewString("");
+ }
+ return s;
+ }
+
+ String *defaultExternalRuntimeFilename() {
+ return NewString("swigocamlrun.h");
+ }
+};
+
+/* -------------------------------------------------------------------------
+ * swig_ocaml() - Instantiate module
+ * ------------------------------------------------------------------------- */
+
+static Language *new_swig_ocaml() {
+ return new OCAML();
+}
+extern "C" Language *swig_ocaml(void) {
+ return new_swig_ocaml();
+}
diff --git a/contrib/tools/swig/Source/Modules/octave.cxx b/contrib/tools/swig/Source/Modules/octave.cxx
new file mode 100644
index 0000000000..352105b9bd
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/octave.cxx
@@ -0,0 +1,1572 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * octave.cxx
+ *
+ * Octave language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+static String *global_name = 0;
+static String *op_prefix = 0;
+
+static const char *usage = "\
+Octave Options (available with -octave)\n\
+ -globals <name> - Set <name> used to access C global variables [default: 'cvar']\n\
+ Use '.' to load C global variables into module namespace\n\
+ -opprefix <str> - Prefix <str> for global operator functions [default: 'op_']\n\
+\n";
+
+
+class OCTAVE:public Language {
+private:
+ File *f_begin;
+ File *f_runtime;
+ File *f_header;
+ File *f_doc;
+ File *f_wrappers;
+ File *f_init;
+ File *f_initbeforefunc;
+ File *f_directors;
+ File *f_directors_h;
+ String *s_global_tab;
+ String *s_members_tab;
+ String *class_name;
+
+ int have_constructor;
+ int have_destructor;
+ String *constructor_name;
+
+ Hash *docs;
+
+ void Octave_begin_function(Node *n, File *f, const_String_or_char_ptr cname, const_String_or_char_ptr wname, bool dld) {
+ if (dld) {
+ String *tname = texinfo_name(n, "std::string()");
+ Printf(f, "SWIG_DEFUN( %s, %s, %s ) {", cname, wname, tname);
+ }
+ else {
+ Printf(f, "static octave_value_list %s (const octave_value_list& args, int nargout) {", wname);
+ }
+ }
+
+public:
+ OCTAVE():
+ f_begin(0),
+ f_runtime(0),
+ f_header(0),
+ f_doc(0),
+ f_wrappers(0),
+ f_init(0),
+ f_initbeforefunc(0),
+ f_directors(0),
+ f_directors_h(0),
+ s_global_tab(0),
+ s_members_tab(0),
+ class_name(0),
+ have_constructor(0),
+ have_destructor(0),
+ constructor_name(0),
+ docs(0)
+ {
+ /* Add code to manage protected constructors and directors */
+ director_prot_ctor_code = NewString("");
+ Printv(director_prot_ctor_code,
+ "if ( $comparison ) { /* subclassed */\n",
+ " $director_new \n",
+ "} else {\n", " error(\"accessing abstract class or protected constructor\"); \n", " SWIG_fail;\n", "}\n", NIL);
+
+ enable_cplus_runtime_mode();
+ allow_overloading();
+ director_multiple_inheritance = 1;
+ director_language = 1;
+ docs = NewHash();
+ }
+
+ virtual void main(int argc, char *argv[]) {
+
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage, stdout);
+ } else if (strcmp(argv[i], "-globals") == 0) {
+ if (argv[i + 1]) {
+ global_name = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-opprefix") == 0) {
+ if (argv[i + 1]) {
+ op_prefix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-cppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is now always on.\n", argv[i]);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nocppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is no longer supported.\n", argv[i]);
+ Swig_mark_arg(i);
+ Exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (!global_name)
+ global_name = NewString("cvar");
+ if (!op_prefix)
+ op_prefix = NewString("op_");
+
+ SWIG_library_directory("octave");
+ Preprocessor_define("SWIGOCTAVE 1", 0);
+ SWIG_config_file("octave.swg");
+ SWIG_typemap_lang("octave");
+ allow_overloading();
+
+ // Octave API is C++, so output must be C++ compatible even when wrapping C code
+ if (!cparse_cplusplus)
+ Swig_cparse_cplusplusout(1);
+ }
+
+ virtual int top(Node *n) {
+ {
+ Node *mod = Getattr(n, "module");
+ if (mod) {
+ Node *options = Getattr(mod, "options");
+ if (options) {
+ int dirprot = 0;
+ if (Getattr(options, "dirprot")) {
+ dirprot = 1;
+ }
+ if (Getattr(options, "nodirprot")) {
+ dirprot = 0;
+ }
+ if (Getattr(options, "directors")) {
+ allow_directors();
+ if (dirprot)
+ allow_dirprot();
+ }
+ }
+ }
+ }
+
+ String *module = Getattr(n, "name");
+ String *outfile = Getattr(n, "outfile");
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewString("");
+ f_header = NewString("");
+ f_doc = NewString("");
+ f_wrappers = NewString("");
+ f_init = NewString("");
+ f_initbeforefunc = NewString("");
+ f_directors_h = NewString("");
+ f_directors = NewString("");
+ s_global_tab = NewString("");
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("doc", f_doc);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("initbeforefunc", f_initbeforefunc);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "OCTAVE");
+
+ Printf(f_runtime, "#define SWIG_name_d \"%s\"\n", module);
+ Printf(f_runtime, "#define SWIG_name %s\n", module);
+
+ Printf(f_runtime, "\n");
+ Printf(f_runtime, "#define SWIG_global_name \"%s\"\n", global_name);
+ Printf(f_runtime, "#define SWIG_op_prefix \"%s\"\n", op_prefix);
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+ Swig_banner(f_directors_h);
+ if (dirprot_mode()) {
+ // Printf(f_directors_h, "#include <map>\n");
+ // Printf(f_directors_h, "#include <string>\n\n");
+ }
+ }
+
+ Printf(f_runtime, "\n");
+
+ Printf(s_global_tab, "\nstatic const struct swig_octave_member swig_globals[] = {\n");
+ Printf(f_init, "static bool SWIG_init_user(octave_swig_type* module_ns)\n{\n");
+
+ if (!CPlusPlus)
+ Printf(f_header,"extern \"C\" {\n");
+
+ Language::top(n);
+
+ if (!CPlusPlus)
+ Printf(f_header,"}\n");
+
+ if (Len(docs))
+ emit_doc_texinfo();
+
+ if (directorsEnabled()) {
+ Swig_insert_file("director_common.swg", f_runtime);
+ Swig_insert_file("director.swg", f_runtime);
+ }
+
+ Printf(f_init, "return true;\n}\n");
+ Printf(s_global_tab, "{0,0,0,0,0,0}\n};\n");
+
+ Printv(f_wrappers, s_global_tab, NIL);
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+ Dump(f_doc, f_begin);
+ if (directorsEnabled()) {
+ Dump(f_directors_h, f_begin);
+ Dump(f_directors, f_begin);
+ }
+ Dump(f_wrappers, f_begin);
+ Dump(f_initbeforefunc, f_begin);
+ Wrapper_pretty_print(f_init, f_begin);
+
+ Delete(s_global_tab);
+ Delete(f_initbeforefunc);
+ Delete(f_init);
+ Delete(f_wrappers);
+ Delete(f_doc);
+ Delete(f_header);
+ Delete(f_directors);
+ Delete(f_directors_h);
+ Delete(f_runtime);
+ Delete(f_begin);
+
+ return SWIG_OK;
+ }
+
+ String *texinfo_escape(String *_s) {
+ const char* s=(const char*)Data(_s);
+ while (*s&&(*s=='\t'||*s=='\r'||*s=='\n'||*s==' '))
+ ++s;
+ String *r = NewString("");
+ for (int j=0;s[j];++j) {
+ if (s[j] == '\n') {
+ Append(r, "\\n\\\n");
+ } else if (s[j] == '\r') {
+ Append(r, "\\r");
+ } else if (s[j] == '\t') {
+ Append(r, "\\t");
+ } else if (s[j] == '\\') {
+ Append(r, "\\\\");
+ } else if (s[j] == '\'') {
+ Append(r, "\\\'");
+ } else if (s[j] == '\"') {
+ Append(r, "\\\"");
+ } else
+ Putc(s[j], r);
+ }
+ return r;
+ }
+ void emit_doc_texinfo() {
+ for (Iterator it = First(docs); it.key; it = Next(it)) {
+ String *wrap_name = it.key;
+
+ String *synopsis = Getattr(it.item, "synopsis");
+ String *decl_info = Getattr(it.item, "decl_info");
+ String *cdecl_info = Getattr(it.item, "cdecl_info");
+ String *args_info = Getattr(it.item, "args_info");
+
+ String *doc_str = NewString("");
+ Printv(doc_str, synopsis, decl_info, cdecl_info, args_info, NIL);
+ String *escaped_doc_str = texinfo_escape(doc_str);
+
+ if (Len(doc_str)>0) {
+ Printf(f_doc,"static const char* %s_texinfo = ",wrap_name);
+ Printf(f_doc,"\"-*- texinfo -*-\\n\\\n%s", escaped_doc_str);
+ if (Len(decl_info))
+ Printf(f_doc,"\\n\\\n@end deftypefn");
+ Printf(f_doc,"\";\n");
+ }
+
+ Delete(escaped_doc_str);
+ Delete(doc_str);
+ Delete(wrap_name);
+ }
+ Printf(f_doc,"\n");
+ }
+ bool is_empty_doc_node(Node* n) {
+ if (!n)
+ return true;
+ String *synopsis = Getattr(n, "synopsis");
+ String *decl_info = Getattr(n, "decl_info");
+ String *cdecl_info = Getattr(n, "cdecl_info");
+ String *args_info = Getattr(n, "args_info");
+ return !Len(synopsis) && !Len(decl_info) &&
+ !Len(cdecl_info) && !Len(args_info);
+ }
+ String *texinfo_name(Node* n, const char* defval = "0") {
+ String *tname = NewString("");
+ String *iname = Getattr(n, "sym:name");
+ String *wname = Swig_name_wrapper(iname);
+ Node* d = Getattr(docs, wname);
+
+ if (is_empty_doc_node(d))
+ Printf(tname, defval);
+ else
+ Printf(tname, "%s_texinfo", wname);
+
+ return tname;
+ }
+ void process_autodoc(Node *n) {
+ String *iname = Getattr(n, "sym:name");
+ String *name = Getattr(n, "name");
+ String *wname = Swig_name_wrapper(iname);
+ String *str = Getattr(n, "feature:docstring");
+ bool autodoc_enabled = !Cmp(Getattr(n, "feature:autodoc"), "1");
+ Node* d = Getattr(docs, wname);
+ if (!d) {
+ d = NewHash();
+ Setattr(d, "synopsis", NewString(""));
+ Setattr(d, "decl_info", NewString(""));
+ Setattr(d, "cdecl_info", NewString(""));
+ Setattr(d, "args_info", NewString(""));
+ Setattr(docs, wname, d);
+ }
+
+ String *synopsis = Getattr(d, "synopsis");
+ String *decl_info = Getattr(d, "decl_info");
+ // String *cdecl_info = Getattr(d, "cdecl_info");
+ String *args_info = Getattr(d, "args_info");
+
+ // * couldn't we just emit the docs here?
+
+ if (autodoc_enabled) {
+ String *decl_str = NewString("");
+ String *args_str = NewString("");
+ make_autodocParmList(n, decl_str, args_str);
+ Append(decl_info, "@deftypefn {Loadable Function} ");
+
+ SwigType *type = Getattr(n, "type");
+ if (type && Strcmp(type, "void")) {
+ Node *nn = classLookup(Getattr(n, "type"));
+ String *type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
+ Append(decl_info, "@var{retval} = ");
+ Printf(args_str, "%s@var{retval} is of type %s. ", args_str, type_str);
+ Delete(type_str);
+ }
+
+ Append(decl_info, name);
+ Append(decl_info, " (");
+ Append(decl_info, decl_str);
+ Append(decl_info, ")\n");
+ Append(args_info, args_str);
+ Delete(decl_str);
+ Delete(args_str);
+ }
+
+ if (str && Len(str) > 0) {
+ // strip off {} if necessary
+ char *t = Char(str);
+ if (*t == '{') {
+ Delitem(str, 0);
+ Delitem(str, DOH_END);
+ }
+
+ // emit into synopsis section
+ Append(synopsis, str);
+ }
+ }
+
+ virtual int importDirective(Node *n) {
+ String *modname = Getattr(n, "module");
+ if (modname)
+ Printf(f_init, "if (!SWIG_Octave_LoadModule(\"%s\")) return false;\n", modname);
+ return Language::importDirective(n);
+ }
+
+ const char *get_implicitconv_flag(Node *n) {
+ int conv = 0;
+ if (n && GetFlag(n, "feature:implicitconv")) {
+ conv = 1;
+ }
+ return conv ? "SWIG_POINTER_IMPLICIT_CONV" : "0";
+ }
+
+ /* -----------------------------------------------------------------------------
+ * addMissingParameterNames()
+ * For functions that have not had nameless parameters set in the Language class.
+ *
+ * Inputs:
+ * plist - entire parameter list
+ * arg_offset - argument number for first parameter
+ * Side effects:
+ * The "lname" attribute in each parameter in plist will be contain a parameter name
+ * ----------------------------------------------------------------------------- */
+
+ void addMissingParameterNames(Node* n, ParmList *plist, int arg_offset) {
+ Parm *p = plist;
+ int i = arg_offset;
+ while (p) {
+ if (!Getattr(p, "lname")) {
+ String *name = makeParameterName(n, p, i);
+ Setattr(p, "lname", name);
+ Delete(name);
+ }
+ i++;
+ p = nextSibling(p);
+ }
+ }
+
+ void make_autodocParmList(Node *n, String *decl_str, String *args_str) {
+ String *pdocs = 0;
+ ParmList *plist = CopyParmList(Getattr(n, "parms"));
+ Parm *p;
+ Parm *pnext;
+ int arg_num = is_wrapping_class() ? 1 : 0;
+
+ addMissingParameterNames(n, plist, arg_num); // for $1_name substitutions done in Swig_typemap_attach_parms
+
+ Swig_typemap_attach_parms("in", plist, 0);
+ Swig_typemap_attach_parms("doc", plist, 0);
+
+ for (p = plist; p; p = pnext, arg_num++) {
+
+ String *tm = Getattr(p, "tmap:in");
+ if (tm) {
+ pnext = Getattr(p, "tmap:in:next");
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ continue;
+ }
+ } else {
+ pnext = nextSibling(p);
+ }
+
+ String *name = 0;
+ String *type = 0;
+ String *value = 0;
+ String *pdoc = Getattr(p, "tmap:doc");
+ if (pdoc) {
+ name = Getattr(p, "tmap:doc:name");
+ type = Getattr(p, "tmap:doc:type");
+ value = Getattr(p, "tmap:doc:value");
+ }
+
+ String *made_name = 0;
+ if (!name) {
+ name = made_name = makeParameterName(n, p, arg_num);
+ }
+
+ type = type ? type : Getattr(p, "type");
+ value = value ? value : Getattr(p, "value");
+
+ if (SwigType_isvarargs(type))
+ break;
+
+ String *tex_name = NewString("");
+ if (name)
+ Printf(tex_name, "@var{%s}", name);
+ else
+ Printf(tex_name, "@var{?}");
+
+ if (Len(decl_str))
+ Append(decl_str, ", ");
+ Append(decl_str, tex_name);
+
+ if (value) {
+ String *new_value = convertValue(value, Getattr(p, "type"));
+ if (new_value) {
+ value = new_value;
+ } else {
+ Node *lookup = Swig_symbol_clookup(value, 0);
+ if (lookup)
+ value = Getattr(lookup, "sym:name");
+ }
+ Printf(decl_str, " = %s", value);
+ }
+
+ Node *nn = classLookup(Getattr(p, "type"));
+ String *type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
+ Printf(args_str, "%s is of type %s. ", tex_name, type_str);
+
+ Delete(type_str);
+ Delete(tex_name);
+ Delete(made_name);
+ }
+ if (pdocs)
+ Setattr(n, "feature:pdocs", pdocs);
+ Delete(plist);
+ }
+
+ /* ------------------------------------------------------------
+ * convertValue()
+ * Check if string v can be an Octave value literal,
+ * (eg. number or string), or translate it to an Octave literal.
+ * ------------------------------------------------------------ */
+ String *convertValue(String *v, SwigType *t) {
+ if (v && Len(v) > 0) {
+ char fc = (Char(v))[0];
+ if (('0' <= fc && fc <= '9') || '\'' == fc || '"' == fc) {
+ /* number or string (or maybe NULL pointer) */
+ if (SwigType_ispointer(t) && Strcmp(v, "0") == 0)
+ return NewString("None");
+ else
+ return v;
+ }
+ if (Strcmp(v, "NULL") == 0 || Strcmp(v, "nullptr") == 0)
+ return SwigType_ispointer(t) ? NewString("nil") : NewString("0");
+ if (Strcmp(v, "true") == 0 || Strcmp(v, "TRUE") == 0)
+ return NewString("true");
+ if (Strcmp(v, "false") == 0 || Strcmp(v, "FALSE") == 0)
+ return NewString("false");
+ }
+ return 0;
+ }
+
+ virtual int functionWrapper(Node *n) {
+ Parm *p;
+ String *tm;
+ int j;
+
+ String *nodeType = Getattr(n, "nodeType");
+ int constructor = (!Cmp(nodeType, "constructor"));
+ int destructor = (!Cmp(nodeType, "destructor"));
+ String *storage = Getattr(n, "storage");
+
+ bool overloaded = !!Getattr(n, "sym:overloaded");
+ bool last_overload = overloaded && !Getattr(n, "sym:nextSibling");
+ String *iname = Getattr(n, "sym:name");
+ String *wname = Swig_name_wrapper(iname);
+ String *overname = Copy(wname);
+ SwigType *d = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+
+ if (!overloaded && !addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ if (overloaded)
+ Append(overname, Getattr(n, "sym:overname"));
+
+ if (!overloaded || last_overload)
+ process_autodoc(n);
+
+ Wrapper *f = NewWrapper();
+ Octave_begin_function(n, f->def, iname, overname, !overloaded);
+
+ // Start default try block to execute
+ // cleanup code if exception is thrown
+ Printf(f->code, "try {\n");
+
+ emit_parameter_variables(l, f);
+ emit_attach_parmmaps(l, f);
+ Setattr(n, "wrap:parms", l);
+
+ int num_arguments = emit_num_arguments(l);
+ int num_required = emit_num_required(l);
+ int varargs = emit_isvarargs(l);
+ char source[64];
+
+ Printf(f->code, "if (!SWIG_check_num_args(\"%s\",args.length(),%i,%i,%i)) "
+ "{\n SWIG_fail;\n }\n", iname, num_arguments, num_required, varargs);
+
+ if (constructor && num_arguments == 1 && num_required == 1) {
+ if (Cmp(storage, "explicit") == 0) {
+ Node *parent = Swig_methodclass(n);
+ if (GetFlag(parent, "feature:implicitconv")) {
+ String *desc = NewStringf("SWIGTYPE%s", SwigType_manglestr(Getattr(n, "type")));
+ Printf(f->code, "if (SWIG_CheckImplicit(%s)) SWIG_fail;\n", desc);
+ Delete(desc);
+ }
+ }
+ }
+
+ for (j = 0, p = l; j < num_arguments; ++j) {
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+
+ String *tm = Getattr(p, "tmap:in");
+ if (tm) {
+ if (!tm || checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = nextSibling(p);
+ continue;
+ }
+
+ sprintf(source, "args(%d)", j);
+ Setattr(p, "emit:input", source);
+
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+
+ if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+
+ if (Getattr(p, "tmap:in:implicitconv")) {
+ const char *convflag = "0";
+ if (!Getattr(p, "hidden")) {
+ SwigType *ptype = Getattr(p, "type");
+ convflag = get_implicitconv_flag(classLookup(ptype));
+ }
+ Replaceall(tm, "$implicitconv", convflag);
+ Setattr(p, "implicitconv", convflag);
+ }
+
+ String *getargs = NewString("");
+ if (j >= num_required)
+ Printf(getargs, "if (%d<args.length()) {\n%s\n}", j, tm);
+ else
+ Printv(getargs, tm, NIL);
+ Printv(f->code, getargs, "\n", NIL);
+ Delete(getargs);
+
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ break;
+ }
+ }
+
+ // Check for trailing varargs
+ if (varargs) {
+ if (p && (tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", "varargs");
+ Printv(f->code, tm, "\n", NIL);
+ }
+ }
+
+ // Insert constraint checking code
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Insert cleanup code
+ String *cleanup = NewString("");
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ if (Getattr(p, "tmap:freearg:implicitconv")) {
+ const char *convflag = "0";
+ if (!Getattr(p, "hidden")) {
+ SwigType *ptype = Getattr(p, "type");
+ convflag = get_implicitconv_flag(classLookup(ptype));
+ }
+ if (strcmp(convflag, "0") == 0) {
+ tm = 0;
+ }
+ }
+ if (tm && (Len(tm) != 0)) {
+ Printv(cleanup, tm, "\n", NIL);
+ }
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ // Insert argument output code
+ String *outarg = NewString("");
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ Replaceall(tm, "$result", "_outp");
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ int director_method = is_member_director(n) && !is_smart_pointer() && !destructor;
+ if (director_method) {
+ Wrapper_add_local(f, "upcall", "bool upcall = false");
+ Append(f->code, "upcall = !!dynamic_cast<Swig::Director*>(arg1);\n");
+ }
+
+ Setattr(n, "wrap:name", overname);
+
+ Swig_director_emit_dynamic_cast(n, f);
+ String *actioncode = emit_action(n);
+
+ Wrapper_add_local(f, "_out", "octave_value_list _out");
+ Wrapper_add_local(f, "_outp", "octave_value_list *_outp=&_out");
+ Wrapper_add_local(f, "_outv", "octave_value _outv");
+
+ // Return the function value
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ Replaceall(tm, "$result", "_outv");
+
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "1");
+ else
+ Replaceall(tm, "$owner", "0");
+
+ Printf(f->code, "%s\n", tm);
+ Printf(f->code, "if (_outv.is_defined()) _outp = " "SWIG_Octave_AppendOutput(_outp, _outv);\n");
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(d, 0), iname);
+ }
+ emit_return_variable(n, d, f);
+
+ Printv(f->code, outarg, NIL);
+ Printv(f->code, cleanup, NIL);
+
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Replaceall(tm, "$result", "_outv");
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+
+ Printf(f->code, "return _out;\n");
+
+ // Execute cleanup code if branched to fail: label
+ Printf(f->code, "fail:\n");
+ Printv(f->code, cleanup, NIL);
+ Printf(f->code, "return octave_value_list();\n");
+
+ // Execute cleanup code if exception was thrown
+ Printf(f->code, "}\n");
+ Printf(f->code, "catch(...) {\n");
+ Printv(f->code, cleanup, NIL);
+ Printf(f->code, "throw;\n");
+ Printf(f->code, "}\n");
+
+ // End wrapper function
+ Printf(f->code, "}\n");
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+
+ Replaceall(f->code, "$symname", iname);
+ Wrapper_print(f, f_wrappers);
+ DelWrapper(f);
+
+ if (last_overload)
+ dispatchFunction(n);
+
+ if (!overloaded || last_overload) {
+ String *tname = texinfo_name(n);
+ Printf(s_global_tab, "{\"%s\",%s,0,0,2,%s},\n", iname, wname, tname);
+ Delete(tname);
+ }
+
+ Delete(overname);
+ Delete(wname);
+ Delete(cleanup);
+ Delete(outarg);
+
+ return SWIG_OK;
+ }
+
+ void dispatchFunction(Node *n) {
+ Wrapper *f = NewWrapper();
+
+ String *iname = Getattr(n, "sym:name");
+ String *wname = Swig_name_wrapper(iname);
+ int maxargs;
+ String *dispatch = Swig_overload_dispatch(n, "return %s(args, nargout);", &maxargs);
+ String *tmp = NewString("");
+
+ Octave_begin_function(n, f->def, iname, wname, true);
+ Wrapper_add_local(f, "argc", "int argc = args.length()");
+ Printf(tmp, "octave_value_ref argv[%d]={", maxargs);
+ for (int j = 0; j < maxargs; ++j)
+ Printf(tmp, "%soctave_value_ref(args,%d)", j ? "," : " ", j);
+ Printf(tmp, "}");
+ Wrapper_add_local(f, "argv", tmp);
+ Printv(f->code, dispatch, "\n", NIL);
+ Printf(f->code, "error(\"No matching function for overload\");\n");
+ Printf(f->code, "return octave_value_list();\n");
+ Printv(f->code, "}\n", NIL);
+
+ Wrapper_print(f, f_wrappers);
+ Delete(tmp);
+ DelWrapper(f);
+ Delete(dispatch);
+ Delete(wname);
+ }
+
+ virtual int variableWrapper(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ String *tm;
+ Wrapper *getf = NewWrapper();
+ Wrapper *setf = NewWrapper();
+
+ String *getname = Swig_name_get(NSPACE_TODO, iname);
+ String *setname = Swig_name_set(NSPACE_TODO, iname);
+
+ String *getwname = Swig_name_wrapper(getname);
+ String *setwname = Swig_name_wrapper(setname);
+
+ Octave_begin_function(n, setf->def, setname, setwname, true);
+ Printf(setf->code, "if (!SWIG_check_num_args(\"%s_set\",args.length(),1,1,0)) return octave_value_list();\n", iname);
+ if (is_assignable(n)) {
+ Setattr(n, "wrap:name", setname);
+ if ((tm = Swig_typemap_lookup("varin", n, name, 0))) {
+ Replaceall(tm, "$input", "args(0)");
+ if (Getattr(n, "tmap:varin:implicitconv")) {
+ Replaceall(tm, "$implicitconv", get_implicitconv_flag(n));
+ }
+ emit_action_code(n, setf->code, tm);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s.\n", SwigType_str(t, 0));
+ }
+ Append(setf->code, "return octave_value_list();\n");
+ Append(setf->code, "fail:\n");
+ Append(setf->code, "return octave_value_list();\n");
+ } else {
+ Printf(setf->code, "return octave_set_immutable(args,nargout);");
+ }
+ Append(setf->code, "}\n");
+ Wrapper_print(setf, f_wrappers);
+
+ Setattr(n, "wrap:name", getname);
+ int addfail = 0;
+ Octave_begin_function(n, getf->def, getname, getwname, true);
+ Wrapper_add_local(getf, "obj", "octave_value obj");
+ if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
+ Replaceall(tm, "$result", "obj");
+ addfail = emit_action_code(n, getf->code, tm);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VAROUT_UNDEF, input_file, line_number, "Unable to read variable of type %s\n", SwigType_str(t, 0));
+ }
+ Append(getf->code, "return obj;\n");
+ if (addfail) {
+ Append(getf->code, "fail:\n");
+ Append(getf->code, "return octave_value_list();\n");
+ }
+ Append(getf->code, "}\n");
+ Wrapper_print(getf, f_wrappers);
+
+ Printf(s_global_tab, "{\"%s\",0,%s,%s,2,0},\n", iname, getwname, setwname);
+
+ Delete(getwname);
+ Delete(setwname);
+ DelWrapper(setf);
+ DelWrapper(getf);
+
+ return SWIG_OK;
+ }
+
+ virtual int constantWrapper(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ String *cppvalue = Getattr(n, "cppvalue");
+ String *tm;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ if (SwigType_type(type) == T_MPOINTER) {
+ String *wname = Swig_name_wrapper(iname);
+ String *str = SwigType_str(type, wname);
+ Printf(f_header, "static %s = %s;\n", str, value);
+ Delete(str);
+ value = wname;
+ }
+ if ((tm = Swig_typemap_lookup("constcode", n, name, 0))) {
+ Replaceall(tm, "$value", cppvalue ? cppvalue : value);
+ Replaceall(tm, "$nsname", iname);
+ Printf(f_init, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value.\n");
+ return SWIG_NOWRAP;
+ }
+
+ return SWIG_OK;
+ }
+
+ virtual int nativeWrapper(Node *n) {
+ return Language::nativeWrapper(n);
+ }
+
+ virtual int enumDeclaration(Node *n) {
+ return Language::enumDeclaration(n);
+ }
+
+ virtual int enumvalueDeclaration(Node *n) {
+ return Language::enumvalueDeclaration(n);
+ }
+
+ virtual int classDeclaration(Node *n) {
+ return Language::classDeclaration(n);
+ }
+
+ virtual int classHandler(Node *n) {
+ have_constructor = 0;
+ have_destructor = 0;
+ constructor_name = 0;
+
+ class_name = Getattr(n, "sym:name");
+
+ if (!addSymbol(class_name, n))
+ return SWIG_ERROR;
+
+ // This is a bug, due to the fact that swig_type -> octave_class mapping
+ // is 1-to-n.
+ static Hash *emitted = NewHash();
+ String *mangled_classname = Swig_name_mangle(Getattr(n, "name"));
+ if (Getattr(emitted, mangled_classname)) {
+ Delete(mangled_classname);
+ return SWIG_NOWRAP;
+ }
+ Setattr(emitted, mangled_classname, "1");
+ Delete(mangled_classname);
+
+ assert(!s_members_tab);
+ s_members_tab = NewString("");
+ Printv(s_members_tab, "static swig_octave_member swig_", class_name, "_members[] = {\n", NIL);
+
+ Language::classHandler(n);
+
+ SwigType *t = Copy(Getattr(n, "name"));
+ SwigType_add_pointer(t);
+
+ // Replace storing a pointer to underlying class with a smart pointer (intended for use with non-intrusive smart pointers)
+ SwigType *smart = Swig_cparse_smartptr(n);
+ String *wrap_class = NewStringf("&_wrap_class_%s", class_name);
+ if (smart) {
+ SwigType_add_pointer(smart);
+ SwigType_remember_clientdata(smart, wrap_class);
+ }
+ //String *wrap_class = NewStringf("&_wrap_class_%s", class_name);
+ SwigType_remember_clientdata(t, wrap_class);
+
+ int use_director = Swig_directorclass(n);
+ if (use_director) {
+ String *nspace = Getattr(n, "sym:nspace");
+ String *cname = Swig_name_disown(nspace, class_name);
+ String *wcname = Swig_name_wrapper(cname);
+ String *cnameshdw = NewStringf("%s_shadow", cname);
+ String *wcnameshdw = Swig_name_wrapper(cnameshdw);
+ Octave_begin_function(n, f_wrappers, cnameshdw, wcnameshdw, true);
+ Printf(f_wrappers, " if (args.length()!=1) {\n");
+ Printf(f_wrappers, " error(\"disown takes no arguments\");\n");
+ Printf(f_wrappers, " return octave_value_list();\n");
+ Printf(f_wrappers, " }\n");
+ Printf(f_wrappers, " %s (args, nargout);\n", wcname);
+ Printf(f_wrappers, " return args;\n");
+ Printf(f_wrappers, "}\n");
+ Printf(s_members_tab, "{\"__disown\",%s,0,0,0,0},\n", wcnameshdw);
+ Delete(wcname);
+ Delete(cname);
+ Delete(wcnameshdw);
+ Delete(cnameshdw);
+ }
+
+ Printf(s_members_tab, "{0,0,0,0,0,0}\n};\n");
+ Printv(f_wrappers, s_members_tab, NIL);
+
+ String *base_class_names = NewString("");
+ String *base_class = NewString("");
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator b;
+ b = First(baselist);
+ while (b.item) {
+ String *bname = Getattr(b.item, "name");
+ if ((!bname) || GetFlag(b.item, "feature:ignore") || (!Getattr(b.item, "module"))) {
+ b = Next(b);
+ continue;
+ }
+
+ String *bname_mangled = SwigType_manglestr(SwigType_add_pointer(Copy(bname)));
+ Printf(base_class_names, "\"%s\",", bname_mangled);
+ Printf(base_class, "0,");
+ b = Next(b);
+ Delete(bname_mangled);
+ }
+ }
+
+ Printv(f_wrappers, "static const char *swig_", class_name, "_base_names[] = {", base_class_names, "0};\n", NIL);
+ Printv(f_wrappers, "static const swig_type_info *swig_", class_name, "_base[] = {", base_class, "0};\n", NIL);
+ Printv(f_wrappers, "static swig_octave_class _wrap_class_", class_name, " = {\"", class_name, "\", &SWIGTYPE", SwigType_manglestr(t), ",", NIL);
+ Printv(f_wrappers, Swig_directorclass(n) ? "1," : "0,", NIL);
+ if (have_constructor) {
+ String *nspace = Getattr(n, "sym:nspace");
+ String *cname = Swig_name_construct(nspace, constructor_name);
+ String *wcname = Swig_name_wrapper(cname);
+ String *tname = texinfo_name(n);
+ Printf(f_wrappers, "%s,%s,", wcname, tname);
+ Delete(tname);
+ Delete(wcname);
+ Delete(cname);
+ } else
+ Printv(f_wrappers, "0,0,", NIL);
+ if (have_destructor) {
+ String *nspace = Getattr(n, "sym:nspace");
+ String *cname = Swig_name_destroy(nspace, class_name);
+ String *wcname = Swig_name_wrapper(cname);
+ Printf(f_wrappers, "%s,", wcname);
+ Delete(wcname);
+ Delete(cname);
+ } else
+ Printv(f_wrappers, "0", ",", NIL);
+ Printf(f_wrappers, "swig_%s_members,swig_%s_base_names,swig_%s_base };\n\n", class_name, class_name, class_name);
+
+ Delete(base_class);
+ Delete(base_class_names);
+ Delete(smart);
+ Delete(t);
+ Delete(s_members_tab);
+ s_members_tab = 0;
+ class_name = 0;
+
+ return SWIG_OK;
+ }
+
+ virtual int memberfunctionHandler(Node *n) {
+ Language::memberfunctionHandler(n);
+
+ assert(s_members_tab);
+ assert(class_name);
+ String *name = Getattr(n, "name");
+ String *iname = GetChar(n, "sym:name");
+ String *realname = iname ? iname : name;
+ String *wname = Getattr(n, "wrap:name");
+ assert(wname);
+
+ if (!Getattr(n, "sym:nextSibling")) {
+ String *tname = texinfo_name(n);
+ String *rname = Copy(wname);
+ bool overloaded = !!Getattr(n, "sym:overloaded");
+ if (overloaded)
+ Delslice(rname, Len(rname) - Len(Getattr(n, "sym:overname")), DOH_END);
+ Printf(s_members_tab, "{\"%s\",%s,0,0,0,%s},\n",
+ realname, rname, tname);
+ Delete(rname);
+ Delete(tname);
+ }
+
+ return SWIG_OK;
+ }
+
+ virtual int membervariableHandler(Node *n) {
+ Setattr(n, "feature:autodoc", "0");
+
+ Language::membervariableHandler(n);
+
+ assert(s_members_tab);
+ assert(class_name);
+ String *symname = Getattr(n, "sym:name");
+ String *getname = Swig_name_get(NSPACE_TODO, Swig_name_member(NSPACE_TODO, class_name, symname));
+ String *setname = Swig_name_set(NSPACE_TODO, Swig_name_member(NSPACE_TODO, class_name, symname));
+ String *getwname = Swig_name_wrapper(getname);
+ String *setwname = GetFlag(n, "feature:immutable") ? NewString("octave_set_immutable") : Swig_name_wrapper(setname);
+ assert(s_members_tab);
+
+ Printf(s_members_tab, "{\"%s\",0,%s,%s,0,0},\n", symname, getwname, setwname);
+
+ Delete(getname);
+ Delete(setname);
+ Delete(getwname);
+ Delete(setwname);
+ return SWIG_OK;
+ }
+
+ virtual int constructorHandler(Node *n) {
+ have_constructor = 1;
+ if (!constructor_name)
+ constructor_name = NewString(Getattr(n, "sym:name"));
+
+ int use_director = Swig_directorclass(n);
+ if (use_director) {
+ Parm *parms = Getattr(n, "parms");
+ Parm *self;
+ String *name = NewString("self");
+ String *type = NewString("void");
+ SwigType_add_pointer(type);
+ self = NewParm(type, name, n);
+ Delete(type);
+ Delete(name);
+ Setattr(self, "lname", "self_obj");
+ if (parms)
+ set_nextSibling(self, parms);
+ Setattr(n, "parms", self);
+ Setattr(n, "wrap:self", "1");
+ Setattr(n, "hidden", "1");
+ Delete(self);
+ }
+
+ return Language::constructorHandler(n);
+ }
+
+ virtual int destructorHandler(Node *n) {
+ have_destructor = 1;
+ return Language::destructorHandler(n);
+ }
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+ Language::staticmemberfunctionHandler(n);
+
+ assert(s_members_tab);
+ assert(class_name);
+ String *name = Getattr(n, "name");
+ String *iname = GetChar(n, "sym:name");
+ String *realname = iname ? iname : name;
+ String *wname = Getattr(n, "wrap:name");
+ assert(wname);
+
+ if (!Getattr(n, "sym:nextSibling")) {
+ String *tname = texinfo_name(n);
+ String *rname = Copy(wname);
+ bool overloaded = !!Getattr(n, "sym:overloaded");
+ if (overloaded)
+ Delslice(rname, Len(rname) - Len(Getattr(n, "sym:overname")), DOH_END);
+ Printf(s_members_tab, "{\"%s\",%s,0,0,1,%s},\n",
+ realname, rname, tname);
+ Delete(rname);
+ Delete(tname);
+ }
+
+ return SWIG_OK;
+ }
+
+ virtual int memberconstantHandler(Node *n) {
+ return Language::memberconstantHandler(n);
+ }
+
+ virtual int staticmembervariableHandler(Node *n) {
+ Setattr(n, "feature:autodoc", "0");
+
+ Language::staticmembervariableHandler(n);
+
+ if (!GetFlag(n, "wrappedasconstant")) {
+ assert(s_members_tab);
+ assert(class_name);
+ String *symname = Getattr(n, "sym:name");
+ String *getname = Swig_name_get(NSPACE_TODO, Swig_name_member(NSPACE_TODO, class_name, symname));
+ String *setname = Swig_name_set(NSPACE_TODO, Swig_name_member(NSPACE_TODO, class_name, symname));
+ String *getwname = Swig_name_wrapper(getname);
+ String *setwname = GetFlag(n, "feature:immutable") ? NewString("octave_set_immutable") : Swig_name_wrapper(setname);
+ assert(s_members_tab);
+
+ Printf(s_members_tab, "{\"%s\",0,%s,%s,1,0},\n", symname, getwname, setwname);
+
+ Delete(getname);
+ Delete(setname);
+ Delete(getwname);
+ Delete(setwname);
+ }
+ return SWIG_OK;
+ }
+
+ int classDirectorInit(Node *n) {
+ String *declaration = Swig_director_declaration(n);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "%s\n", declaration);
+ Printf(f_directors_h, "public:\n");
+ Delete(declaration);
+ return Language::classDirectorInit(n);
+ }
+
+ int classDirectorEnd(Node *n) {
+ Printf(f_directors_h, "};\n\n");
+ return Language::classDirectorEnd(n);
+ }
+
+ int classDirectorConstructor(Node *n) {
+ Node *parent = Getattr(n, "parentNode");
+ String *sub = NewString("");
+ String *decl = Getattr(n, "decl");
+ String *supername = Swig_class_name(parent);
+ String *classname = NewString("");
+ Printf(classname, "SwigDirector_%s", supername);
+
+ // insert self parameter
+ Parm *p;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms = CopyParmList(superparms);
+ String *type = NewString("void");
+ SwigType_add_pointer(type);
+ p = NewParm(type, NewString("self"), n);
+ set_nextSibling(p, parms);
+ parms = p;
+
+ if (!Getattr(n, "defaultargs")) {
+ // constructor
+ {
+ Wrapper *w = NewWrapper();
+ String *call;
+ String *basetype = Getattr(parent, "classtype");
+ String *target = Swig_method_decl(0, decl, classname, parms, 0);
+ call = Swig_csuperclass_call(0, basetype, superparms);
+ Printf(w->def, "%s::%s: %s," "\nSwig::Director(static_cast<%s*>(this)) { \n", classname, target, call, basetype);
+ Append(w->def, "}\n");
+ Delete(target);
+ Wrapper_print(w, f_directors);
+ Delete(call);
+ DelWrapper(w);
+ }
+
+ // constructor header
+ {
+ String *target = Swig_method_decl(0, decl, classname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+
+ Delete(sub);
+ Delete(classname);
+ Delete(supername);
+ Delete(parms);
+ return Language::classDirectorConstructor(n);
+ }
+
+ int classDirectorDefaultConstructor(Node *n) {
+ String *classname = Swig_class_name(n);
+ {
+ Wrapper *w = NewWrapper();
+ Printf(w->def, "SwigDirector_%s::SwigDirector_%s(void* self) :"
+ "\nSwig::Director((octave_swig_type*)self,static_cast<%s*>(this)) { \n", classname, classname, classname);
+ Append(w->def, "}\n");
+ Wrapper_print(w, f_directors);
+ DelWrapper(w);
+ }
+ Printf(f_directors_h, " SwigDirector_%s(octave_swig_type* self);\n", classname);
+ Delete(classname);
+ return Language::classDirectorDefaultConstructor(n);
+ }
+
+ int classDirectorMethod(Node *n, Node *parent, String *super) {
+ int is_void = 0;
+ int is_pointer = 0;
+ String *decl = Getattr(n, "decl");
+ String *returntype = Getattr(n, "type");
+ String *name = Getattr(n, "name");
+ String *classname = Getattr(parent, "sym:name");
+ String *c_classname = Getattr(parent, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *declaration = NewString("");
+ ParmList *l = Getattr(n, "parms");
+ Wrapper *w = NewWrapper();
+ String *tm;
+ String *wrap_args = NewString("");
+ String *value = Getattr(n, "value");
+ String *storage = Getattr(n, "storage");
+ bool pure_virtual = false;
+ int status = SWIG_OK;
+ int idx;
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+
+ if (Cmp(storage, "virtual") == 0) {
+ if (Cmp(value, "0") == 0) {
+ pure_virtual = true;
+ }
+ }
+
+ // determine if the method returns a pointer
+ is_pointer = SwigType_ispointer_return(decl);
+ is_void = (!Cmp(returntype, "void") && !is_pointer);
+
+ // virtual method definition
+ String *target;
+ String *pclassname = NewStringf("SwigDirector_%s", classname);
+ String *qualified_name = NewStringf("%s::%s", pclassname, name);
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+
+ // header declaration
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Get any exception classes in the throws typemap
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ ParmList *throw_parm_list = 0;
+
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ Parm *p;
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list)
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+ String *str = SwigType_str(Getattr(p, "type"), 0);
+ Append(w->def, str);
+ Append(declaration, str);
+ Delete(str);
+ }
+ }
+
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+
+ // declare method return value
+ // if the return value is a reference or const reference, a specialized typemap must
+ // handle it, including declaration of c_result ($result).
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ }
+ } else {
+ String *cres = SwigType_lstr(returntype, "c_result");
+ Printf(w->code, "%s;\n", cres);
+ Delete(cres);
+ }
+ }
+
+ if (ignored_method) {
+ if (!pure_virtual) {
+ if (!is_void)
+ Printf(w->code, "return ");
+ String *super_call = Swig_method_call(super, l);
+ Printf(w->code, "%s;\n", super_call);
+ Delete(super_call);
+ } else {
+ Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname),
+ SwigType_namestr(name));
+ }
+ } else {
+ // attach typemaps to arguments (C/C++ -> Octave)
+ String *parse_args = NewString("");
+
+ Swig_director_parms_fixup(l);
+
+ Swig_typemap_attach_parms("in", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ Parm *p;
+
+ int outputs = 0;
+ if (!is_void)
+ outputs++;
+
+ // build argument list and type conversion string
+ p = l;
+ while (p) {
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ if (Getattr(p, "tmap:directorargout") != 0)
+ outputs++;
+
+ String *pname = Getattr(p, "name");
+ String *ptype = Getattr(p, "type");
+ Wrapper_add_local(w, "tmpv", "octave_value tmpv");
+
+ if ((tm = Getattr(p, "tmap:directorin")) != 0) {
+ String *parse = Getattr(p, "tmap:directorin:parse");
+ if (!parse) {
+ Setattr(p, "emit:directorinput", "tmpv");
+ Replaceall(tm, "$input", "tmpv");
+ Replaceall(tm, "$owner", "0");
+ Printv(wrap_args, tm, "\n", NIL);
+ Printf(wrap_args, "args.append(tmpv);\n");
+ Putc('O', parse_args);
+ } else {
+ Append(parse_args, parse);
+ Setattr(p, "emit:directorinput", pname);
+ Replaceall(tm, "$input", pname);
+ Replaceall(tm, "$owner", "0");
+ if (Len(tm) == 0)
+ Append(tm, pname);
+ }
+ p = Getattr(p, "tmap:directorin:next");
+ continue;
+ } else if (Cmp(ptype, "void")) {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
+ "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(ptype, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_NOWRAP;
+ break;
+ }
+ p = nextSibling(p);
+ }
+
+ String *method_name = Getattr(n, "sym:name");
+
+ Printv(w->code, wrap_args, NIL);
+
+ // emit method invocation
+ Wrapper_add_local(w, "args", "octave_value_list args");
+ Wrapper_add_local(w, "out", "octave_value_list out");
+ Wrapper_add_local(w, "idx", "std::list<octave_value_list> idx");
+ Printf(w->code, "idx.push_back(octave_value_list(\"%s\"));\n", method_name);
+ Printf(w->code, "idx.push_back(args);\n");
+ Printf(w->code, "out=swig_get_self()->subsref(\".(\",idx,%d);\n", outputs);
+
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+ idx = 0;
+
+ // marshal return value
+ if (!is_void) {
+ Printf(w->code, "if (out.length()<%d) {\n", outputs);
+ Printf(w->code, "Swig::DirectorTypeMismatchException::raise(\"Octave "
+ "method %s.%s failed to return the required number " "of arguments.\");\n", classname, method_name);
+ Printf(w->code, "}\n");
+
+ tm = Swig_typemap_lookup("directorout", n, Swig_cresult_name(), w);
+ if (tm != 0) {
+ char temp[24];
+ sprintf(temp, "out(%d)", idx);
+ Replaceall(tm, "$input", temp);
+ // Replaceall(tm, "$argnum", temp);
+ Replaceall(tm, "$disown", Getattr(n, "wrap:disown") ? "SWIG_POINTER_DISOWN" : "0");
+ if (Getattr(n, "tmap:directorout:implicitconv")) {
+ Replaceall(tm, "$implicitconv", get_implicitconv_flag(n));
+ }
+ Replaceall(tm, "$result", "c_result");
+ Printv(w->code, tm, "\n", NIL);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s in director method %s::%s (skipping method).\n",
+ SwigType_str(returntype, 0), SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_ERROR;
+ }
+ }
+ idx++;
+
+ // marshal outputs
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
+ char temp[24];
+ sprintf(temp, "out(%d)", idx);
+ Replaceall(tm, "$result", temp);
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ Delete(parse_args);
+ Delete(cleanup);
+ Delete(outarg);
+ }
+
+ if (!is_void) {
+ if (!(ignored_method && !pure_virtual)) {
+ String *rettype = SwigType_str(returntype, 0);
+ if (!SwigType_isreference(returntype)) {
+ Printf(w->code, "return (%s) c_result;\n", rettype);
+ } else {
+ Printf(w->code, "return (%s) *c_result;\n", rettype);
+ }
+ Delete(rettype);
+ }
+ }
+
+ Append(w->code, "}\n");
+
+ // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewString("");
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+ // emit the director method
+ if (status == SWIG_OK) {
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+ // clean up
+ Delete(wrap_args);
+ Delete(pclassname);
+ DelWrapper(w);
+ return status;
+ }
+
+ String *runtimeCode() {
+ String *s = NewString("");
+ String *srun = Swig_include_sys("octrun.swg");
+ if (!srun) {
+ Printf(stderr, "*** Unable to open 'octrun.swg'\n");
+ } else {
+ Append(s, srun);
+ Delete(srun);
+ }
+ return s;
+ }
+
+ String *defaultExternalRuntimeFilename() {
+ return NewString("swigoctaverun.h");
+ }
+};
+
+extern "C" Language *swig_octave(void) {
+ return new OCTAVE();
+}
diff --git a/contrib/tools/swig/Source/Modules/overload.cxx b/contrib/tools/swig/Source/Modules/overload.cxx
new file mode 100644
index 0000000000..b94e87ebb7
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/overload.cxx
@@ -0,0 +1,866 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * overload.cxx
+ *
+ * This file is used to analyze overloaded functions and methods.
+ * It looks at signatures and tries to gather information for
+ * building a dispatch function.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+
+#define MAX_OVERLOAD 4096
+
+/* Overload "argc" and "argv" */
+String *argv_template_string;
+String *argc_template_string;
+
+namespace {
+struct Overloaded {
+ Node *n; /* Node */
+ int argc; /* Argument count */
+ ParmList *parms; /* Parameters used for overload check */
+ int error; /* Ambiguity error */
+ bool implicitconv_function; /* For ordering implicitconv functions*/
+};
+}
+
+static int fast_dispatch_mode = 0;
+static int cast_dispatch_mode = 0;
+
+/* Set fast_dispatch_mode */
+void Wrapper_fast_dispatch_mode_set(int flag) {
+ fast_dispatch_mode = flag;
+}
+
+void Wrapper_cast_dispatch_mode_set(int flag) {
+ cast_dispatch_mode = flag;
+}
+
+/* -----------------------------------------------------------------------------
+ * mark_implicitconv_function()
+ *
+ * Mark function if it contains an implicitconv type in the parameter list
+ * ----------------------------------------------------------------------------- */
+static void mark_implicitconv_function(Overloaded& onode) {
+ Parm *parms = onode.parms;
+ if (parms) {
+ bool is_implicitconv_function = false;
+ Parm *p = parms;
+ while (p) {
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+ if (GetFlag(p, "implicitconv")) {
+ is_implicitconv_function = true;
+ break;
+ }
+ p = nextSibling(p);
+ }
+ if (is_implicitconv_function)
+ onode.implicitconv_function = true;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_overload_rank()
+ *
+ * This function takes an overloaded declaration and creates a list that ranks
+ * all overloaded methods in an order that can be used to generate a dispatch
+ * function.
+ * Slight difference in the way this function is used by scripting languages and
+ * statically typed languages. The script languages call this method via
+ * Swig_overload_dispatch() - where wrappers for all overloaded methods are generated,
+ * however sometimes the code can never be executed. The non-scripting languages
+ * call this method via Swig_overload_check() for each overloaded method in order
+ * to determine whether or not the method should be wrapped. Note the slight
+ * difference when overloading methods that differ by const only. The
+ * scripting languages will ignore the const method, whereas the non-scripting
+ * languages ignore the first method parsed.
+ * ----------------------------------------------------------------------------- */
+
+List *Swig_overload_rank(Node *n, bool script_lang_wrapping) {
+ Overloaded nodes[MAX_OVERLOAD];
+ int nnodes = 0;
+ Node *o = Getattr(n, "sym:overloaded");
+ Node *c;
+
+ if (!o)
+ return 0;
+
+ c = o;
+ while (c) {
+ if (Getattr(c, "error")) {
+ c = Getattr(c, "sym:nextSibling");
+ continue;
+ }
+ /* if (SmartPointer && Getattr(c,"cplus:staticbase")) {
+ c = Getattr(c,"sym:nextSibling");
+ continue;
+ } */
+
+ /* Make a list of all the declarations (methods) that are overloaded with
+ * this one particular method name */
+ if (Getattr(c, "wrap:name")) {
+ assert(nnodes < MAX_OVERLOAD);
+ nodes[nnodes].n = c;
+ nodes[nnodes].parms = Getattr(c, "wrap:parms");
+ nodes[nnodes].argc = emit_num_required(nodes[nnodes].parms);
+ nodes[nnodes].error = 0;
+ nodes[nnodes].implicitconv_function = false;
+
+ mark_implicitconv_function(nodes[nnodes]);
+ nnodes++;
+ }
+ c = Getattr(c, "sym:nextSibling");
+ }
+
+ /* Sort the declarations by required argument count */
+ {
+ int i, j;
+ for (i = 0; i < nnodes; i++) {
+ for (j = i + 1; j < nnodes; j++) {
+ if (nodes[i].argc > nodes[j].argc) {
+ Overloaded t = nodes[i];
+ nodes[i] = nodes[j];
+ nodes[j] = t;
+ }
+ }
+ }
+ }
+
+ /* Sort the declarations by argument types */
+ {
+ int i, j;
+ for (i = 0; i < nnodes - 1; i++) {
+ if (nodes[i].argc == nodes[i + 1].argc) {
+ for (j = i + 1; (j < nnodes) && (nodes[j].argc == nodes[i].argc); j++) {
+ Parm *p1 = nodes[i].parms;
+ Parm *p2 = nodes[j].parms;
+ int differ = 0;
+ int num_checked = 0;
+ while (p1 && p2 && (num_checked < nodes[i].argc)) {
+ // Printf(stdout,"p1 = '%s', p2 = '%s'\n", Getattr(p1,"type"), Getattr(p2,"type"));
+ if (checkAttribute(p1, "tmap:in:numinputs", "0")) {
+ p1 = Getattr(p1, "tmap:in:next");
+ continue;
+ }
+ if (checkAttribute(p2, "tmap:in:numinputs", "0")) {
+ p2 = Getattr(p2, "tmap:in:next");
+ continue;
+ }
+ String *t1 = Getattr(p1, "tmap:typecheck:precedence");
+ String *t2 = Getattr(p2, "tmap:typecheck:precedence");
+ if ((!t1) && (!nodes[i].error)) {
+ Swig_warning(WARN_TYPEMAP_TYPECHECK, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "Overloaded method %s not supported (incomplete type checking rule - no precedence level in typecheck typemap for '%s').\n",
+ Swig_name_decl(nodes[i].n), SwigType_str(Getattr(p1, "type"), 0));
+ nodes[i].error = 1;
+ } else if ((!t2) && (!nodes[j].error)) {
+ Swig_warning(WARN_TYPEMAP_TYPECHECK, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s not supported (incomplete type checking rule - no precedence level in typecheck typemap for '%s').\n",
+ Swig_name_decl(nodes[j].n), SwigType_str(Getattr(p2, "type"), 0));
+ nodes[j].error = 1;
+ }
+ if (t1 && t2) {
+ int t1v, t2v;
+ t1v = atoi(Char(t1));
+ t2v = atoi(Char(t2));
+ differ = t1v - t2v;
+ } else if (!t1 && t2)
+ differ = 1;
+ else if (t1 && !t2)
+ differ = -1;
+ else if (!t1 && !t2)
+ differ = -1;
+ num_checked++;
+ if (differ > 0) {
+ Overloaded t = nodes[i];
+ nodes[i] = nodes[j];
+ nodes[j] = t;
+ break;
+ } else if ((differ == 0) && (Strcmp(t1, "0") == 0)) {
+ t1 = Getattr(p1, "equivtype");
+ t1 = t1 ? t1 : Getattr(p1, "ltype");
+ if (!t1) {
+ t1 = SwigType_ltype(Getattr(p1, "type"));
+ if (Getattr(p1, "tmap:typecheck:SWIGTYPE")) {
+ SwigType_add_pointer(t1);
+ }
+ Setattr(p1, "ltype", t1);
+ }
+ t2 = Getattr(p2, "equivtype");
+ t2 = t2 ? t2 : Getattr(p2, "ltype");
+ if (!t2) {
+ t2 = SwigType_ltype(Getattr(p2, "type"));
+ if (Getattr(p2, "tmap:typecheck:SWIGTYPE")) {
+ SwigType_add_pointer(t2);
+ }
+ Setattr(p2, "ltype", t2);
+ }
+
+ /* Need subtype check here. If t2 is a subtype of t1, then we need to change the
+ order */
+
+ if (SwigType_issubtype(t2, t1)) {
+ Overloaded t = nodes[i];
+ nodes[i] = nodes[j];
+ nodes[j] = t;
+ }
+
+ if (Strcmp(t1, t2) != 0) {
+ differ = 1;
+ break;
+ }
+ } else if (differ) {
+ break;
+ }
+ if (Getattr(p1, "tmap:in:next")) {
+ p1 = Getattr(p1, "tmap:in:next");
+ } else {
+ p1 = nextSibling(p1);
+ }
+ if (Getattr(p2, "tmap:in:next")) {
+ p2 = Getattr(p2, "tmap:in:next");
+ } else {
+ p2 = nextSibling(p2);
+ }
+ }
+ if (!differ) {
+ /* See if declarations differ by const only */
+ String *decl1 = Getattr(nodes[i].n, "decl");
+ String *decl2 = Getattr(nodes[j].n, "decl");
+ if (decl1 && decl2) {
+ /* Remove ref-qualifiers. Note that rvalue ref-qualifiers are already ignored and
+ * it is illegal to overload a function with and without ref-qualifiers. So with
+ * all the combinations of ref-qualifiers and cv-qualifiers, we just detect
+ * the cv-qualifier (const) overloading. */
+ String *d1 = Copy(decl1);
+ String *d2 = Copy(decl2);
+ if (SwigType_isreference(d1) || SwigType_isrvalue_reference(d1)) {
+ Delete(SwigType_pop(d1));
+ }
+ if (SwigType_isreference(d2) || SwigType_isrvalue_reference(d2)) {
+ Delete(SwigType_pop(d2));
+ }
+ String *dq1 = Copy(d1);
+ String *dq2 = Copy(d2);
+ if (SwigType_isconst(d1)) {
+ Delete(SwigType_pop(dq1));
+ }
+ if (SwigType_isconst(d2)) {
+ Delete(SwigType_pop(dq2));
+ }
+ if (Strcmp(dq1, dq2) == 0) {
+
+ if (SwigType_isconst(d1) && !SwigType_isconst(d2)) {
+ if (script_lang_wrapping) {
+ // Swap nodes so that the const method gets ignored (shadowed by the non-const method)
+ Overloaded t = nodes[i];
+ nodes[i] = nodes[j];
+ nodes[j] = t;
+ }
+ differ = 1;
+ if (!nodes[j].error) {
+ if (script_lang_wrapping) {
+ Swig_warning(WARN_LANG_OVERLOAD_CONST, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_CONST, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using non-const method %s instead.\n", Swig_name_decl(nodes[i].n));
+ } else {
+ if (!Getattr(nodes[j].n, "overload:ignore")) {
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using %s instead.\n", Swig_name_decl(nodes[i].n));
+ }
+ }
+ }
+ nodes[j].error = 1;
+ } else if (!SwigType_isconst(d1) && SwigType_isconst(d2)) {
+ differ = 1;
+ if (!nodes[j].error) {
+ if (script_lang_wrapping) {
+ Swig_warning(WARN_LANG_OVERLOAD_CONST, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_CONST, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using non-const method %s instead.\n", Swig_name_decl(nodes[i].n));
+ } else {
+ if (!Getattr(nodes[j].n, "overload:ignore")) {
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using %s instead.\n", Swig_name_decl(nodes[i].n));
+ }
+ }
+ }
+ nodes[j].error = 1;
+ }
+ }
+ Delete(dq1);
+ Delete(dq2);
+ }
+ }
+ if (!differ) {
+ if (!nodes[j].error) {
+ if (script_lang_wrapping) {
+ Swig_warning(WARN_LANG_OVERLOAD_SHADOW, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s effectively ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_SHADOW, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "as it is shadowed by %s.\n", Swig_name_decl(nodes[i].n));
+ } else {
+ if (!Getattr(nodes[j].n, "overload:ignore")) {
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using %s instead.\n", Swig_name_decl(nodes[i].n));
+ }
+ }
+ nodes[j].error = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ List *result = NewList();
+ {
+ int i;
+ int argc_changed_index = -1;
+ for (i = 0; i < nnodes; i++) {
+ if (nodes[i].error)
+ Setattr(nodes[i].n, "overload:ignore", "1");
+ Append(result, nodes[i].n);
+ // Printf(stdout,"[ %d ] %d %s\n", i, nodes[i].implicitconv_function, ParmList_errorstr(nodes[i].parms));
+ if (i == nnodes-1 || nodes[i].argc != nodes[i+1].argc) {
+ if (argc_changed_index+2 < nnodes && (nodes[argc_changed_index+1].argc == nodes[argc_changed_index+2].argc)) {
+ // Add additional implicitconv functions in same order as already ranked.
+ // Consider overloaded functions by argument count... only add additional implicitconv functions if
+ // the number of functions with the same arg count > 1, ie, only if overloaded by same argument count.
+ int j;
+ for (j = argc_changed_index + 1; j <= i; j++) {
+ if (nodes[j].implicitconv_function) {
+ SetFlag(nodes[j].n, "implicitconvtypecheckoff");
+ Append(result, nodes[j].n);
+ // Printf(stdout,"[ %d ] %d + %s\n", j, nodes[j].implicitconv_function, ParmList_errorstr(nodes[j].parms));
+ }
+ }
+ }
+ argc_changed_index = i;
+ }
+ }
+ }
+ return result;
+}
+
+// /* -----------------------------------------------------------------------------
+// * print_typecheck()
+// * ----------------------------------------------------------------------------- */
+
+static bool print_typecheck(String *f, int j, Parm *pj, bool implicitconvtypecheckoff) {
+ char tmp[256];
+ sprintf(tmp, Char(argv_template_string), j);
+ String *tm = Getattr(pj, "tmap:typecheck");
+ if (tm) {
+ tm = Copy(tm);
+ Replaceid(tm, Getattr(pj, "lname"), "_v");
+ String *conv = Getattr(pj, "implicitconv");
+ if (conv && !implicitconvtypecheckoff) {
+ Replaceall(tm, "$implicitconv", conv);
+ } else {
+ Replaceall(tm, "$implicitconv", "0");
+ }
+ Replaceall(tm, "$input", tmp);
+ Printv(f, tm, "\n", NIL);
+ Delete(tm);
+ return true;
+ } else
+ return false;
+}
+
+/* -----------------------------------------------------------------------------
+ * ReplaceFormat()
+ * ----------------------------------------------------------------------------- */
+
+static String *ReplaceFormat(const_String_or_char_ptr fmt, int j) {
+ String *lfmt = NewString(fmt);
+ char buf[50];
+ sprintf(buf, "%d", j);
+ Replaceall(lfmt, "$numargs", buf);
+ int i;
+ String *commaargs = NewString("");
+ for (i = 0; i < j; i++) {
+ Printv(commaargs, ", ", NIL);
+ Printf(commaargs, Char(argv_template_string), i);
+ }
+ Replaceall(lfmt, "$commaargs", commaargs);
+ return lfmt;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_overload_dispatch()
+ *
+ * Generate a dispatch function. argc is assumed to hold the argument count.
+ * argv is the argument vector.
+ *
+ * Note that for C++ class member functions, Swig_overload_dispatch() assumes
+ * that argc includes the "self" argument and that the first element of argv[]
+ * is the "self" argument. So for a member function:
+ *
+ * Foo::bar(int x, int y, int z);
+ *
+ * the argc should be 4 (not 3!) and the first element of argv[] would be
+ * the appropriate scripting language reference to "self". For regular
+ * functions (and static class functions) the argc and argv only include
+ * the regular function arguments.
+ * ----------------------------------------------------------------------------- */
+
+/*
+ Cast dispatch mechanism.
+*/
+String *Swig_overload_dispatch_cast(Node *n, const_String_or_char_ptr fmt, int *maxargs) {
+ int i, j;
+
+ *maxargs = 1;
+
+ String *f = NewString("");
+ String *sw = NewString("");
+ Printf(f, "{\n");
+ Printf(f, "unsigned long _index = 0;\n");
+ Printf(f, "SWIG_TypeRank _rank = 0; \n");
+
+ /* Get a list of methods ranked by precedence values and argument count */
+ List *dispatch = Swig_overload_rank(n, true);
+ int nfunc = Len(dispatch);
+
+ /* Loop over the functions */
+
+ bool emitcheck = true;
+ for (i = 0; i < nfunc; i++) {
+ int fn = 0;
+ Node *ni = Getitem(dispatch, i);
+ Parm *pi = Getattr(ni, "wrap:parms");
+ bool implicitconvtypecheckoff = GetFlag(ni, "implicitconvtypecheckoff") != 0;
+ int num_required = emit_num_required(pi);
+ int num_arguments = emit_num_arguments(pi);
+ if (num_arguments > *maxargs)
+ *maxargs = num_arguments;
+
+ if (num_required == num_arguments) {
+ Printf(f, "if (%s == %d) {\n", argc_template_string, num_required);
+ } else {
+ Printf(f, "if ((%s >= %d) && (%s <= %d)) {\n", argc_template_string, num_required, argc_template_string, num_arguments);
+ }
+ Printf(f, "SWIG_TypeRank _ranki = 0;\n");
+ Printf(f, "SWIG_TypeRank _rankm = 0;\n");
+ if (num_arguments)
+ Printf(f, "SWIG_TypeRank _pi = 1;\n");
+
+ /* create a list with the wrappers that collide with the
+ current one based on argument number */
+ List *coll = NewList();
+ for (int k = i + 1; k < nfunc; k++) {
+ Node *nk = Getitem(dispatch, k);
+ Parm *pk = Getattr(nk, "wrap:parms");
+ int nrk = emit_num_required(pk);
+ int nak = emit_num_arguments(pk);
+ if ((nrk >= num_required && nrk <= num_arguments) || (nak >= num_required && nak <= num_arguments) || (nrk <= num_required && nak >= num_arguments))
+ Append(coll, nk);
+ }
+
+ // printf("overload: %s coll=%d\n", Char(Getattr(n, "sym:name")), Len(coll));
+
+ int num_braces = 0;
+ bool test = (num_arguments > 0);
+ if (test) {
+ int need_v = 1;
+ j = 0;
+ Parm *pj = pi;
+ while (pj) {
+ if (checkAttribute(pj, "tmap:in:numinputs", "0")) {
+ pj = Getattr(pj, "tmap:in:next");
+ continue;
+ }
+
+ String *tm = Getattr(pj, "tmap:typecheck");
+ if (tm) {
+ tm = Copy(tm);
+ /* normalise for comparison later */
+ Replaceid(tm, Getattr(pj, "lname"), "_v");
+
+ /* if all the wrappers have the same type check on this
+ argument we can optimize it out */
+ for (int k = 0; k < Len(coll) && !emitcheck; k++) {
+ Node *nk = Getitem(coll, k);
+ Parm *pk = Getattr(nk, "wrap:parms");
+ int nak = emit_num_arguments(pk);
+ if (nak <= j)
+ continue;
+ int l = 0;
+ Parm *pl = pk;
+ /* finds arg j on the collider wrapper */
+ while (pl && l <= j) {
+ if (checkAttribute(pl, "tmap:in:numinputs", "0")) {
+ pl = Getattr(pl, "tmap:in:next");
+ continue;
+ }
+ if (l == j) {
+ /* we are at arg j, so we compare the tmaps now */
+ String *tml = Getattr(pl, "tmap:typecheck");
+ /* normalise it before comparing */
+ if (tml)
+ Replaceid(tml, Getattr(pl, "lname"), "_v");
+ if (!tml || Cmp(tm, tml))
+ emitcheck = true;
+ //printf("tmap: %s[%d] (%d) => %s\n\n",
+ // Char(Getattr(nk, "sym:name")),
+ // l, emitcheck, tml?Char(tml):0);
+ }
+ Parm *pl1 = Getattr(pl, "tmap:in:next");
+ if (pl1)
+ pl = pl1;
+ else
+ pl = nextSibling(pl);
+ l++;
+ }
+ }
+
+ if (emitcheck) {
+ if (need_v) {
+ Printf(f, "int _v = 0;\n");
+ need_v = 0;
+ }
+ if (j >= num_required) {
+ Printf(f, "if (%s > %d) {\n", argc_template_string, j);
+ num_braces++;
+ }
+ String *tmp = NewStringf(argv_template_string, j);
+
+ String *conv = Getattr(pj, "implicitconv");
+ if (conv && !implicitconvtypecheckoff) {
+ Replaceall(tm, "$implicitconv", conv);
+ } else {
+ Replaceall(tm, "$implicitconv", "0");
+ }
+ Replaceall(tm, "$input", tmp);
+ Printv(f, "{\n", tm, "}\n", NIL);
+ Delete(tm);
+ fn = i + 1;
+ Printf(f, "if (!_v) goto check_%d;\n", fn);
+ Printf(f, "_ranki += _v*_pi;\n");
+ Printf(f, "_rankm += _pi;\n");
+ Printf(f, "_pi *= SWIG_MAXCASTRANK;\n");
+ }
+ }
+ if (!Getattr(pj, "tmap:in:SWIGTYPE") && Getattr(pj, "tmap:typecheck:SWIGTYPE")) {
+ /* we emit a warning if the argument defines the 'in' typemap, but not the 'typecheck' one */
+ Swig_warning(WARN_TYPEMAP_TYPECHECK_UNDEF, Getfile(ni), Getline(ni),
+ "Overloaded method %s with no explicit typecheck typemap for arg %d of type '%s'\n",
+ Swig_name_decl(n), j, SwigType_str(Getattr(pj, "type"), 0));
+ }
+ Parm *pj1 = Getattr(pj, "tmap:in:next");
+ if (pj1)
+ pj = pj1;
+ else
+ pj = nextSibling(pj);
+ j++;
+ }
+ }
+
+ /* close braces */
+ for ( /* empty */ ; num_braces > 0; num_braces--)
+ Printf(f, "}\n");
+
+ Printf(f, "if (!_index || (_ranki < _rank)) {\n");
+ Printf(f, " _rank = _ranki; _index = %d;\n", i + 1);
+ Printf(f, " if (_rank == _rankm) goto dispatch;\n");
+ Printf(f, "}\n");
+ String *lfmt = ReplaceFormat(fmt, num_arguments);
+ Printf(sw, "case %d:\n", i + 1);
+ Printf(sw, Char(lfmt), Getattr(ni, "wrap:name"));
+ Printf(sw, "\n");
+
+ Printf(f, "}\n"); /* braces closes "if" for this method */
+ if (fn)
+ Printf(f, "check_%d:\n\n", fn);
+
+ if (implicitconvtypecheckoff)
+ Delattr(ni, "implicitconvtypecheckoff");
+
+ Delete(lfmt);
+ Delete(coll);
+ }
+ Delete(dispatch);
+ Printf(f, "dispatch:\n");
+ Printf(f, "switch(_index) {\n");
+ Printf(f, "%s", sw);
+ Printf(f, "}\n");
+
+ Printf(f, "}\n");
+ return f;
+}
+
+/*
+ Fast dispatch mechanism, provided by Salvador Fandi~no Garc'ia (#930586).
+*/
+static String *overload_dispatch_fast(Node *n, const_String_or_char_ptr fmt, int *maxargs, const_String_or_char_ptr fmt_fastdispatch) {
+ int i, j;
+
+ *maxargs = 1;
+
+ String *f = NewString("");
+
+ /* Get a list of methods ranked by precedence values and argument count */
+ List *dispatch = Swig_overload_rank(n, true);
+ int nfunc = Len(dispatch);
+
+ /* Loop over the functions */
+
+ for (i = 0; i < nfunc; i++) {
+ int fn = 0;
+ Node *ni = Getitem(dispatch, i);
+ Parm *pi = Getattr(ni, "wrap:parms");
+ bool implicitconvtypecheckoff = GetFlag(ni, "implicitconvtypecheckoff") != 0;
+ int num_required = emit_num_required(pi);
+ int num_arguments = emit_num_arguments(pi);
+ if (num_arguments > *maxargs)
+ *maxargs = num_arguments;
+
+ if (num_required == num_arguments) {
+ Printf(f, "if (%s == %d) {\n", argc_template_string, num_required);
+ } else {
+ Printf(f, "if ((%s >= %d) && (%s <= %d)) {\n", argc_template_string, num_required, argc_template_string, num_arguments);
+ }
+
+ /* create a list with the wrappers that collide with the
+ current one based on argument number */
+ List *coll = NewList();
+ for (int k = i + 1; k < nfunc; k++) {
+ Node *nk = Getitem(dispatch, k);
+ Parm *pk = Getattr(nk, "wrap:parms");
+ int nrk = emit_num_required(pk);
+ int nak = emit_num_arguments(pk);
+ if ((nrk >= num_required && nrk <= num_arguments) || (nak >= num_required && nak <= num_arguments) || (nrk <= num_required && nak >= num_arguments))
+ Append(coll, nk);
+ }
+
+ // printf("overload: %s coll=%d\n", Char(Getattr(n, "sym:name")), Len(coll));
+
+ bool emitcheck = false;
+ int num_braces = 0;
+ bool test = (Len(coll) > 0 && num_arguments);
+ if (test) {
+ int need_v = 1;
+ j = 0;
+ Parm *pj = pi;
+ while (pj) {
+ if (checkAttribute(pj, "tmap:in:numinputs", "0")) {
+ pj = Getattr(pj, "tmap:in:next");
+ continue;
+ }
+
+ String *tm = Getattr(pj, "tmap:typecheck");
+ if (tm) {
+ tm = Copy(tm);
+ /* normalise for comparison later */
+ Replaceid(tm, Getattr(pj, "lname"), "_v");
+
+ /* if all the wrappers have the same type check on this
+ argument we can optimize it out */
+ emitcheck = false;
+ for (int k = 0; k < Len(coll) && !emitcheck; k++) {
+ Node *nk = Getitem(coll, k);
+ Parm *pk = Getattr(nk, "wrap:parms");
+ int nak = emit_num_arguments(pk);
+ if (nak <= j)
+ continue;
+ int l = 0;
+ Parm *pl = pk;
+ /* finds arg j on the collider wrapper */
+ while (pl && l <= j) {
+ if (checkAttribute(pl, "tmap:in:numinputs", "0")) {
+ pl = Getattr(pl, "tmap:in:next");
+ continue;
+ }
+ if (l == j) {
+ /* we are at arg j, so we compare the tmaps now */
+ String *tml = Getattr(pl, "tmap:typecheck");
+ /* normalise it before comparing */
+ if (tml)
+ Replaceid(tml, Getattr(pl, "lname"), "_v");
+ if (!tml || Cmp(tm, tml))
+ emitcheck = true;
+ //printf("tmap: %s[%d] (%d) => %s\n\n",
+ // Char(Getattr(nk, "sym:name")),
+ // l, emitcheck, tml?Char(tml):0);
+ }
+ Parm *pl1 = Getattr(pl, "tmap:in:next");
+ if (pl1)
+ pl = pl1;
+ else
+ pl = nextSibling(pl);
+ l++;
+ }
+ }
+
+ if (emitcheck) {
+ if (need_v) {
+ Printf(f, "int _v = 0;\n");
+ need_v = 0;
+ }
+ if (j >= num_required) {
+ Printf(f, "if (%s > %d) {\n", argc_template_string, j);
+ num_braces++;
+ }
+ String *tmp = NewStringf(argv_template_string, j);
+
+ String *conv = Getattr(pj, "implicitconv");
+ if (conv && !implicitconvtypecheckoff) {
+ Replaceall(tm, "$implicitconv", conv);
+ } else {
+ Replaceall(tm, "$implicitconv", "0");
+ }
+ Replaceall(tm, "$input", tmp);
+ Printv(f, "{\n", tm, "}\n", NIL);
+ Delete(tm);
+ fn = i + 1;
+ Printf(f, "if (!_v) goto check_%d;\n", fn);
+ }
+ }
+ if (!Getattr(pj, "tmap:in:SWIGTYPE") && Getattr(pj, "tmap:typecheck:SWIGTYPE")) {
+ /* we emit a warning if the argument defines the 'in' typemap, but not the 'typecheck' one */
+ Swig_warning(WARN_TYPEMAP_TYPECHECK_UNDEF, Getfile(ni), Getline(ni),
+ "Overloaded method %s with no explicit typecheck typemap for arg %d of type '%s'\n",
+ Swig_name_decl(n), j, SwigType_str(Getattr(pj, "type"), 0));
+ }
+ Parm *pj1 = Getattr(pj, "tmap:in:next");
+ if (pj1)
+ pj = pj1;
+ else
+ pj = nextSibling(pj);
+ j++;
+ }
+ }
+
+ /* close braces */
+ for ( /* empty */ ; num_braces > 0; num_braces--)
+ Printf(f, "}\n");
+
+ // The language module may want to generate different code for last overloaded function called (with same number of arguments)
+ String *lfmt = ReplaceFormat(!emitcheck && fmt_fastdispatch ? fmt_fastdispatch : fmt, num_arguments);
+ Printf(f, Char(lfmt), Getattr(ni, "wrap:name"));
+
+ Printf(f, "}\n"); /* braces closes "if" for this method */
+ if (fn)
+ Printf(f, "check_%d:\n\n", fn);
+
+ if (implicitconvtypecheckoff)
+ Delattr(ni, "implicitconvtypecheckoff");
+
+ Delete(lfmt);
+ Delete(coll);
+ }
+ Delete(dispatch);
+ return f;
+}
+
+String *Swig_overload_dispatch(Node *n, const_String_or_char_ptr fmt, int *maxargs, const_String_or_char_ptr fmt_fastdispatch) {
+
+ if (fast_dispatch_mode || GetFlag(n, "feature:fastdispatch")) {
+ return overload_dispatch_fast(n, fmt, maxargs, fmt_fastdispatch);
+ }
+
+ int i, j;
+
+ *maxargs = 1;
+
+ String *f = NewString("");
+
+ /* Get a list of methods ranked by precedence values and argument count */
+ List *dispatch = Swig_overload_rank(n, true);
+ int nfunc = Len(dispatch);
+
+ /* Loop over the functions */
+
+ for (i = 0; i < nfunc; i++) {
+ Node *ni = Getitem(dispatch, i);
+ Parm *pi = Getattr(ni, "wrap:parms");
+ bool implicitconvtypecheckoff = GetFlag(ni, "implicitconvtypecheckoff") != 0;
+ int num_required = emit_num_required(pi);
+ int num_arguments = emit_num_arguments(pi);
+ if (GetFlag(n, "wrap:this")) {
+ num_required++;
+ num_arguments++;
+ }
+ if (num_arguments > *maxargs)
+ *maxargs = num_arguments;
+
+ if (num_required == num_arguments) {
+ Printf(f, "if (%s == %d) {\n", argc_template_string, num_required);
+ } else {
+ Printf(f, "if ((%s >= %d) && (%s <= %d)) {\n", argc_template_string, num_required, argc_template_string, num_arguments);
+ }
+
+ if (num_arguments) {
+ Printf(f, "int _v = 0;\n");
+ }
+
+ int num_braces = 0;
+ j = 0;
+ Parm *pj = pi;
+ while (pj) {
+ if (checkAttribute(pj, "tmap:in:numinputs", "0")) {
+ pj = Getattr(pj, "tmap:in:next");
+ continue;
+ }
+ if (j >= num_required) {
+ String *lfmt = ReplaceFormat(fmt, num_arguments);
+ Printf(f, "if (%s <= %d) {\n", argc_template_string, j);
+ Printf(f, Char(lfmt), Getattr(ni, "wrap:name"));
+ Printf(f, "}\n");
+ Delete(lfmt);
+ }
+ if (print_typecheck(f, (GetFlag(n, "wrap:this") ? j + 1 : j), pj, implicitconvtypecheckoff)) {
+ Printf(f, "if (_v) {\n");
+ num_braces++;
+ }
+ if (!Getattr(pj, "tmap:in:SWIGTYPE") && Getattr(pj, "tmap:typecheck:SWIGTYPE")) {
+ /* we emit a warning if the argument defines the 'in' typemap, but not the 'typecheck' one */
+ Swig_warning(WARN_TYPEMAP_TYPECHECK_UNDEF, Getfile(ni), Getline(ni),
+ "Overloaded method %s with no explicit typecheck typemap for arg %d of type '%s'\n",
+ Swig_name_decl(n), j, SwigType_str(Getattr(pj, "type"), 0));
+ }
+ Parm *pk = Getattr(pj, "tmap:in:next");
+ if (pk)
+ pj = pk;
+ else
+ pj = nextSibling(pj);
+ j++;
+ }
+ String *lfmt = ReplaceFormat(fmt, num_arguments);
+ Printf(f, Char(lfmt), Getattr(ni, "wrap:name"));
+ Delete(lfmt);
+ /* close braces */
+ for ( /* empty */ ; num_braces > 0; num_braces--)
+ Printf(f, "}\n");
+ Printf(f, "}\n"); /* braces closes "if" for this method */
+ if (implicitconvtypecheckoff)
+ Delattr(ni, "implicitconvtypecheckoff");
+ }
+ Delete(dispatch);
+ return f;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_overload_check()
+ * ----------------------------------------------------------------------------- */
+void Swig_overload_check(Node *n) {
+ Swig_overload_rank(n, false);
+}
diff --git a/contrib/tools/swig/Source/Modules/perl5.cxx b/contrib/tools/swig/Source/Modules/perl5.cxx
new file mode 100644
index 0000000000..0cbf6b17a7
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/perl5.cxx
@@ -0,0 +1,2502 @@
+/* ----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * perl5.cxx
+ *
+ * Perl5 language module for SWIG.
+ * ------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+#include <ctype.h>
+
+static const char *usage = "\
+Perl 5 Options (available with -perl5)\n\
+ -compat - Compatibility mode\n\
+ -const - Wrap constants as constants and not variables (implies -proxy)\n\
+ -nopm - Do not generate the .pm file\n\
+ -noproxy - Don't create proxy classes\n\
+ -proxy - Create proxy classes (enabled by default)\n\
+ -static - Omit code related to dynamic loading\n\
+\n";
+
+static int compat = 0;
+
+static int no_pmfile = 0;
+
+static int export_all = 0;
+
+/*
+ * pmfile
+ * set by the -pm flag, overrides the name of the .pm file
+ */
+static String *pmfile = 0;
+
+/*
+ * module
+ * set by the %module directive, e.g. "Xerces". It will determine
+ * the name of the .pm file, and the dynamic library, and the name
+ * used by any module wanting to %import the module.
+ */
+static String *module = 0;
+
+/*
+ * namespace_module
+ * the fully namespace qualified name of the module. It will be used
+ * to set the package namespace in the .pm file, as well as the name
+ * of the initialization methods in the glue library. This will be
+ * the same as module, above, unless the %module directive is given
+ * the 'package' option, e.g. %module(package="Foo::Bar") "baz"
+ */
+static String *namespace_module = 0;
+
+/*
+ * cmodule
+ * the namespace of the internal glue code, set to the value of
+ * module with a 'c' appended
+ */
+static String *cmodule = 0;
+
+/*
+ * dest_package
+ * an optional namespace to put all classes into. Specified by using
+ * the %module(package="Foo::Bar") "baz" syntax
+ */
+static String *dest_package = 0;
+
+static String *command_tab = 0;
+static String *constant_tab = 0;
+static String *variable_tab = 0;
+
+static File *f_begin = 0;
+static File *f_runtime = 0;
+static File *f_runtime_h = 0;
+static File *f_header = 0;
+static File *f_wrappers = 0;
+static File *f_directors = 0;
+static File *f_directors_h = 0;
+static File *f_init = 0;
+static File *f_pm = 0;
+static String *pm; /* Package initialization code */
+static String *magic; /* Magic variable wrappers */
+
+static int staticoption = 0;
+
+// controlling verbose output
+static int verbose = 0;
+
+/* The following variables are used to manage Perl5 classes */
+
+static int blessed = 1; /* Enable object oriented features */
+static int do_constants = 0; /* Constant wrapping */
+static List *classlist = 0; /* List of classes */
+static int have_constructor = 0;
+static int have_destructor = 0;
+static int have_data_members = 0;
+static String *class_name = 0; /* Name of the class (what Perl thinks it is) */
+static String *real_classname = 0; /* Real name of C/C++ class */
+static String *fullclassname = 0;
+
+static String *pcode = 0; /* Perl code associated with each class */
+ /* static String *blessedmembers = 0; *//* Member data associated with each class */
+static int member_func = 0; /* Set to 1 when wrapping a member function */
+static String *func_stubs = 0; /* Function stubs */
+static String *const_stubs = 0; /* Constant stubs */
+static int num_consts = 0; /* Number of constants */
+static String *var_stubs = 0; /* Variable stubs */
+static String *exported = 0; /* Exported symbols */
+static String *pragma_include = 0;
+static String *additional_perl_code = 0; /* Additional Perl code from %perlcode %{ ... %} */
+static Hash *operators = 0;
+static int have_operators = 0;
+
+class PERL5:public Language {
+public:
+
+ PERL5():Language () {
+ Clear(argc_template_string);
+ Printv(argc_template_string, "items", NIL);
+ Clear(argv_template_string);
+ Printv(argv_template_string, "ST(%d)", NIL);
+ director_language = 1;
+ }
+
+ /* Test to see if a type corresponds to something wrapped with a shadow class */
+ Node *is_shadow(SwigType *t) {
+ Node *n;
+ n = classLookup(t);
+ /* Printf(stdout,"'%s' --> '%p'\n", t, n); */
+ if (n) {
+ if (!Getattr(n, "perl5:proxy")) {
+ setclassname(n);
+ }
+ return Getattr(n, "perl5:proxy");
+ }
+ return 0;
+ }
+
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+ int i = 1;
+
+ SWIG_library_directory("perl5");
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-package") == 0) {
+ Printv(stderr,
+ "*** -package is no longer supported\n*** use the directive '%module A::B::C' in your interface file instead\n*** see the Perl section in the manual for details.\n", NIL);
+ Exit(EXIT_FAILURE);
+ } else if (strcmp(argv[i], "-interface") == 0) {
+ Printv(stderr,
+ "*** -interface is no longer supported\n*** use the directive '%module A::B::C' in your interface file instead\n*** see the Perl section in the manual for details.\n", NIL);
+ Exit(EXIT_FAILURE);
+ } else if (strcmp(argv[i], "-exportall") == 0) {
+ export_all = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-static") == 0) {
+ staticoption = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-shadow") == 0) || ((strcmp(argv[i], "-proxy") == 0))) {
+ blessed = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-noproxy") == 0)) {
+ blessed = 0;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-const") == 0) {
+ do_constants = 1;
+ blessed = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nopm") == 0) {
+ no_pmfile = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-pm") == 0) {
+ Swig_mark_arg(i);
+ i++;
+ pmfile = NewString(argv[i]);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i],"-v") == 0) {
+ Swig_mark_arg(i);
+ verbose++;
+ } else if (strcmp(argv[i], "-compat") == 0) {
+ compat = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage, stdout);
+ } else if (strcmp(argv[i], "-cppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is now always on.\n", argv[i]);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nocppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is no longer supported.\n", argv[i]);
+ Swig_mark_arg(i);
+ Exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ Preprocessor_define("SWIGPERL 1", 0);
+ // SWIGPERL5 is deprecated, and no longer documented.
+ Preprocessor_define("SWIGPERL5 1", 0);
+ SWIG_typemap_lang("perl5");
+ SWIG_config_file("perl5.swg");
+ allow_overloading();
+ }
+
+ /* ------------------------------------------------------------
+ * top()
+ * ------------------------------------------------------------ */
+
+ virtual int top(Node *n) {
+ /* check if directors are enabled for this module. note: this
+ * is a "master" switch, without which no director code will be
+ * emitted. %feature("director") statements are also required
+ * to enable directors for individual classes or methods.
+ *
+ * use %module(directors="1") modulename at the start of the
+ * interface file to enable director generation.
+ *
+ * TODO: directors are disallowed in conjunction with many command
+ * line options. Some of them are probably safe, but it will take
+ * some effort to validate each one.
+ */
+ {
+ Node *mod = Getattr(n, "module");
+ if (mod) {
+ Node *options = Getattr(mod, "options");
+ if (options) {
+ int dirprot = 0;
+ if (Getattr(options, "dirprot"))
+ dirprot = 1;
+ if (Getattr(options, "nodirprot"))
+ dirprot = 0;
+ if (Getattr(options, "directors")) {
+ int allow = 1;
+ if (export_all) {
+ Printv(stderr, "*** directors are not supported with -exportall\n", NIL);
+ allow = 0;
+ }
+ if (staticoption) {
+ Printv(stderr, "*** directors are not supported with -static\n", NIL);
+ allow = 0;
+ }
+ if (!blessed) {
+ Printv(stderr, "*** directors are not supported with -noproxy\n", NIL);
+ allow = 0;
+ }
+ if (no_pmfile) {
+ Printv(stderr, "*** directors are not supported with -nopm\n", NIL);
+ allow = 0;
+ }
+ if (compat) {
+ Printv(stderr, "*** directors are not supported with -compat\n", NIL);
+ allow = 0;
+ }
+ if (allow) {
+ allow_directors();
+ if (dirprot)
+ allow_dirprot();
+ }
+ }
+ }
+ }
+ }
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+ String *outfile_h = Getattr(n, "outfile_h");
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+ f_directors_h = NewString("");
+ f_directors = NewString("");
+
+ if (directorsEnabled()) {
+ f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
+ if (!f_runtime_h) {
+ FileErrorDisplay(outfile_h);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+
+ classlist = NewList();
+
+ pm = NewString("");
+ func_stubs = NewString("");
+ var_stubs = NewString("");
+ const_stubs = NewString("");
+ exported = NewString("");
+ magic = NewString("");
+ pragma_include = NewString("");
+ additional_perl_code = NewString("");
+
+ command_tab = NewString("static swig_command_info swig_commands[] = {\n");
+ constant_tab = NewString("static swig_constant_info swig_constants[] = {\n");
+ variable_tab = NewString("static swig_variable_info swig_variables[] = {\n");
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "PERL");
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+ }
+ Printf(f_runtime, "#define SWIG_CASTRANK_MODE\n");
+ Printf(f_runtime, "\n");
+
+ // Is the imported module in another package? (IOW, does it use the
+ // %module(package="name") option and it's different than the package
+ // of this module.)
+ Node *mod = Getattr(n, "module");
+ Node *options = Getattr(mod, "options");
+ module = Copy(Getattr(n,"name"));
+
+ String *underscore_module = Copy(module);
+ Replaceall(underscore_module,":","_");
+
+ if (verbose > 0) {
+ fprintf(stdout, "top: using namespace_module: %s\n", Char(namespace_module));
+ }
+
+ if (directorsEnabled()) {
+ Swig_banner(f_directors_h);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", underscore_module);
+ Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", underscore_module);
+ if (dirprot_mode()) {
+ Printf(f_directors_h, "#include <map>\n");
+ Printf(f_directors_h, "#include <string>\n\n");
+ }
+
+ Printf(f_directors, "\n\n");
+ Printf(f_directors, "/* ---------------------------------------------------\n");
+ Printf(f_directors, " * C++ director class methods\n");
+ Printf(f_directors, " * --------------------------------------------------- */\n\n");
+ if (outfile_h) {
+ String *filename = Swig_file_filename(outfile_h);
+ Printf(magic, "#include \"%s\"\n\n", filename);
+ Delete(filename);
+ }
+ }
+
+ if (verbose > 0) {
+ fprintf(stdout, "top: using module: %s\n", Char(module));
+ }
+
+ dest_package = options ? Getattr(options, "package") : 0;
+ if (dest_package) {
+ namespace_module = Copy(dest_package);
+ if (verbose > 0) {
+ fprintf(stdout, "top: Found package: %s\n",Char(dest_package));
+ }
+ } else {
+ namespace_module = Copy(module);
+ if (verbose > 0) {
+ fprintf(stdout, "top: No package found\n");
+ }
+ }
+ /* If we're in blessed mode, change the package name to "packagec" */
+
+ if (blessed) {
+ cmodule = NewStringf("%sc",namespace_module);
+ } else {
+ cmodule = NewString(namespace_module);
+ }
+
+ /* Create a .pm file
+ * Need to strip off any prefixes that might be found in
+ * the module name */
+
+ if (no_pmfile) {
+ f_pm = NewString(0);
+ } else {
+ if (!pmfile) {
+ char *m = Char(module) + Len(module);
+ while (m != Char(module)) {
+ if (*m == ':') {
+ m++;
+ break;
+ }
+ m--;
+ }
+ pmfile = NewStringf("%s.pm", m);
+ }
+ String *filen = NewStringf("%s%s", SWIG_output_directory(), pmfile);
+ if ((f_pm = NewFile(filen, "w", SWIG_output_files())) == 0) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(filen);
+ filen = NULL;
+ Swig_register_filebyname("pm", f_pm);
+ Swig_register_filebyname("perl", f_pm);
+ }
+ {
+ String *boot_name = NewStringf("boot_%s", underscore_module);
+ Printf(f_header,"#define SWIG_init %s\n\n", boot_name);
+ Printf(f_header,"#define SWIG_name \"%s::%s\"\n", cmodule, boot_name);
+ Printf(f_header,"#define SWIG_prefix \"%s::\"\n", cmodule);
+ Delete(boot_name);
+ }
+
+ Swig_banner_target_lang(f_pm, "#");
+ Printf(f_pm, "\n");
+
+ Printf(f_pm, "package %s;\n", module);
+
+ /*
+ * If the package option has been given we are placing our
+ * symbols into some other packages namespace, so we do not
+ * mess with @ISA or require for that package
+ */
+ if (dest_package) {
+ Printf(f_pm,"use base qw(DynaLoader);\n");
+ } else {
+ Printf(f_pm,"use base qw(Exporter);\n");
+ if (!staticoption) {
+ Printf(f_pm,"use base qw(DynaLoader);\n");
+ }
+ }
+
+ /* Start creating magic code */
+
+ Printv(magic,
+ "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n",
+ "#define MAGIC_CLASS\n",
+ "SWIGCLASS_STATIC int swig_magic_readonly(pTHX_ SV *SWIGUNUSEDPARM(sv), MAGIC *SWIGUNUSEDPARM(mg)) {\n",
+ tab4, "MAGIC_PPERL\n", tab4, "croak(\"Value is read-only.\");\n", tab4, "return 0;\n", "}\n", NIL);
+
+ Printf(f_wrappers, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
+
+ /* emit wrappers */
+ Language::top(n);
+
+ if (directorsEnabled()) {
+ // Insert director runtime into the f_runtime file (make it occur before %header section)
+ Swig_insert_file("director_common.swg", f_runtime);
+ Swig_insert_file("director.swg", f_runtime);
+ }
+
+ String *base = NewString("");
+
+ /* Dump out variable wrappers */
+
+ Printv(magic, "\n#ifdef __cplusplus\n}\n#endif\n", NIL);
+
+ Printf(f_header, "%s\n", magic);
+
+ String *type_table = NewString("");
+
+ /* Patch the type table to reflect the names used by shadow classes */
+ if (blessed) {
+ Iterator cls;
+ for (cls = First(classlist); cls.item; cls = Next(cls)) {
+ String *pname = Getattr(cls.item, "perl5:proxy");
+ if (pname) {
+ SwigType *type = Getattr(cls.item, "classtypeobj");
+ if (!type)
+ continue; /* If unnamed class, no type will be found */
+ type = Copy(type);
+
+ SwigType_add_pointer(type);
+ String *mangled = SwigType_manglestr(type);
+ SwigType_remember_mangleddata(mangled, NewStringf("\"%s\"", pname));
+ Delete(type);
+ Delete(mangled);
+ }
+ }
+ }
+ SwigType_emit_type_table(f_runtime, type_table);
+
+ Printf(f_wrappers, "%s", type_table);
+ Delete(type_table);
+
+ Printf(constant_tab, "{0,0,0,0,0,0}\n};\n");
+ Printv(f_wrappers, constant_tab, NIL);
+
+ Printf(f_wrappers, "#ifdef __cplusplus\n}\n#endif\n");
+
+ Printf(f_init, "\t ST(0) = &PL_sv_yes;\n");
+ Printf(f_init, "\t XSRETURN(1);\n");
+ Printf(f_init, "}\n");
+
+ /* Finish off tables */
+ Printf(variable_tab, "{0,0,0,0}\n};\n");
+ Printv(f_wrappers, variable_tab, NIL);
+
+ Printf(command_tab, "{0,0}\n};\n");
+ Printv(f_wrappers, command_tab, NIL);
+
+
+ Printf(f_pm, "package %s;\n", cmodule);
+
+ if (!staticoption) {
+ Printf(f_pm,"bootstrap %s;\n", module);
+ } else {
+ Printf(f_pm,"package %s;\n", cmodule);
+ Printf(f_pm,"boot_%s();\n", underscore_module);
+ }
+
+ Printf(f_pm, "package %s;\n", module);
+ /*
+ * If the package option has been given we are placing our
+ * symbols into some other packages namespace, so we do not
+ * mess with @EXPORT
+ */
+ if (!dest_package) {
+ Printf(f_pm,"@EXPORT = qw(%s);\n", exported);
+ }
+
+ Printf(f_pm, "%s", pragma_include);
+
+ if (blessed) {
+
+ /*
+ * These methods will be duplicated if package
+ * has been specified, so we do not output them
+ */
+ if (!dest_package) {
+ Printv(base, "\n# ---------- BASE METHODS -------------\n\n", "package ", namespace_module, ";\n\n", NIL);
+
+ /* Write out the TIE method */
+
+ Printv(base, "sub TIEHASH {\n", tab4, "my ($classname,$obj) = @_;\n", tab4, "return bless $obj, $classname;\n", "}\n\n", NIL);
+
+ /* Output a CLEAR method. This is just a place-holder, but by providing it we
+ * can make declarations such as
+ * %$u = ( x => 2, y=>3, z =>4 );
+ *
+ * Where x,y,z are the members of some C/C++ object. */
+
+ Printf(base, "sub CLEAR { }\n\n");
+
+ /* Output default firstkey/nextkey methods */
+
+ Printf(base, "sub FIRSTKEY { }\n\n");
+ Printf(base, "sub NEXTKEY { }\n\n");
+
+ /* Output a FETCH method. This is actually common to all classes */
+ Printv(base,
+ "sub FETCH {\n",
+ tab4, "my ($self,$field) = @_;\n", tab4, "my $member_func = \"swig_${field}_get\";\n", tab4, "$self->$member_func();\n", "}\n\n", NIL);
+
+ /* Output a STORE method. This is also common to all classes (might move to base class) */
+
+ Printv(base,
+ "sub STORE {\n",
+ tab4, "my ($self,$field,$newval) = @_;\n",
+ tab4, "my $member_func = \"swig_${field}_set\";\n", tab4, "$self->$member_func($newval);\n", "}\n\n", NIL);
+
+ /* Output a 'this' method */
+
+ Printv(base, "sub this {\n", tab4, "my $ptr = shift;\n", tab4, "return tied(%$ptr);\n", "}\n\n", NIL);
+
+ Printf(f_pm, "%s", base);
+ }
+
+ /* Emit function stubs for stand-alone functions */
+ Printf(f_pm, "\n# ------- FUNCTION WRAPPERS --------\n\n");
+ Printf(f_pm, "package %s;\n\n", namespace_module);
+ Printf(f_pm, "%s", func_stubs);
+
+ /* Emit package code for different classes */
+ Printf(f_pm, "%s", pm);
+
+ if (num_consts > 0) {
+ /* Emit constant stubs */
+ Printf(f_pm, "\n# ------- CONSTANT STUBS -------\n\n");
+ Printf(f_pm, "package %s;\n\n", namespace_module);
+ Printf(f_pm, "%s", const_stubs);
+ }
+
+ /* Emit variable stubs */
+
+ Printf(f_pm, "\n# ------- VARIABLE STUBS --------\n\n");
+ Printf(f_pm, "package %s;\n\n", namespace_module);
+ Printf(f_pm, "%s", var_stubs);
+ }
+
+ /* Add additional Perl code at the end */
+ Printf(f_pm, "%s", additional_perl_code);
+
+ Printf(f_pm, "1;\n");
+ Delete(f_pm);
+ Delete(base);
+ Delete(dest_package);
+ Delete(underscore_module);
+
+ /* Close all of the files */
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+
+ if (directorsEnabled()) {
+ Dump(f_directors_h, f_runtime_h);
+ Printf(f_runtime_h, "\n");
+ Printf(f_runtime_h, "#endif\n");
+ Dump(f_directors, f_begin);
+ }
+
+ Dump(f_wrappers, f_begin);
+ Wrapper_pretty_print(f_init, f_begin);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_directors);
+ Delete(f_directors_h);
+ Delete(f_runtime);
+ Delete(f_begin);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * importDirective(Node *n)
+ * ------------------------------------------------------------ */
+
+ virtual int importDirective(Node *n) {
+ if (blessed) {
+ String *modname = Getattr(n, "module");
+ if (modname) {
+ Printf(f_pm, "require %s;\n", modname);
+ }
+ }
+ return Language::importDirective(n);
+ }
+
+ /* ------------------------------------------------------------
+ * functionWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int functionWrapper(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *d = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *overname = 0;
+ int director_method = 0;
+
+ Parm *p;
+ int i;
+ Wrapper *f;
+ char source[256], temp[256];
+ String *tm;
+ String *cleanup, *outarg;
+ int num_saved = 0;
+ int num_arguments, num_required;
+ int varargs = 0;
+
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ } else {
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+ }
+
+ f = NewWrapper();
+ cleanup = NewString("");
+ outarg = NewString("");
+
+ String *wname = Swig_name_wrapper(iname);
+ if (overname) {
+ Append(wname, overname);
+ }
+ Setattr(n, "wrap:name", wname);
+ Printv(f->def, "XS(", wname, ") {\n", "{\n", /* scope to destroy C++ objects before croaking */
+ NIL);
+
+ emit_parameter_variables(l, f);
+ emit_attach_parmmaps(l, f);
+ Setattr(n, "wrap:parms", l);
+
+ num_arguments = emit_num_arguments(l);
+ num_required = emit_num_required(l);
+ varargs = emit_isvarargs(l);
+
+ Wrapper_add_local(f, "argvi", "int argvi = 0");
+
+ /* Check the number of arguments */
+ if (!varargs) {
+ Printf(f->code, " if ((items < %d) || (items > %d)) {\n", num_required, num_arguments);
+ } else {
+ Printf(f->code, " if (items < %d) {\n", num_required);
+ }
+ Printf(f->code, " SWIG_croak(\"Usage: %s\");\n", usage_func(Char(iname), d, l));
+ Printf(f->code, "}\n");
+
+ /* Write code to extract parameters. */
+ for (i = 0, p = l; i < num_arguments; i++) {
+
+ /* Skip ignored arguments */
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+
+ /* Produce string representation of source and target arguments */
+ sprintf(source, "ST(%d)", i);
+
+ if (i >= num_required) {
+ Printf(f->code, " if (items > %d) {\n", i);
+ }
+ if ((tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source); /* Save input location */
+
+ if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+
+ Printf(f->code, "%s\n", tm);
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ p = nextSibling(p);
+ }
+ if (i >= num_required) {
+ Printf(f->code, " }\n");
+ }
+ }
+
+ if (varargs) {
+ if (p && (tm = Getattr(p, "tmap:in"))) {
+ sprintf(source, "ST(%d)", i);
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source);
+ Printf(f->code, "if (items >= %d) {\n", i);
+ Printv(f->code, tm, "\n", NIL);
+ Printf(f->code, "}\n");
+ }
+ }
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ for (i = 0, p = l; p; i++) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert argument output code */
+ num_saved = 0;
+ for (i = 0, p = l; p; i++) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ SwigType *t = Getattr(p, "type");
+ Replaceall(tm, "$result", "ST(argvi)");
+ if (is_shadow(t)) {
+ Replaceall(tm, "$shadow", "SWIG_SHADOW");
+ } else {
+ Replaceall(tm, "$shadow", "0");
+ }
+
+ String *in = Getattr(p, "emit:input");
+ if (in) {
+ sprintf(temp, "_saved[%d]", num_saved);
+ Replaceall(tm, "$arg", temp);
+ Replaceall(tm, "$input", temp);
+ Printf(f->code, "_saved[%d] = %s;\n", num_saved, in);
+ num_saved++;
+ }
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* If there were any saved arguments, emit a local variable for them */
+ if (num_saved) {
+ sprintf(temp, "_saved[%d]", num_saved);
+ Wrapper_add_localv(f, "_saved", "SV *", temp, NIL);
+ }
+
+ director_method = is_member_director(n) && !is_smart_pointer() && 0 != Cmp(nodeType(n), "destructor");
+ if (director_method) {
+ Wrapper_add_local(f, "director", "Swig::Director *director = 0");
+ Append(f->code, "director = SWIG_DIRECTOR_CAST(arg1);\n");
+ if (dirprot_mode() && !is_public(n)) {
+ Printf(f->code, "if (!director || !(director->swig_get_inner(\"%s\"))) {\n", name);
+ Printf(f->code, "SWIG_exception_fail(SWIG_RuntimeError, \"accessing protected member %s\");\n", name);
+ Append(f->code, "}\n");
+ }
+ Wrapper_add_local(f, "upcall", "bool upcall = false");
+ Printf(f->code, "upcall = director && SvSTASH(SvRV(ST(0))) == gv_stashpv(director->swig_get_class(), 0);\n");
+ }
+
+ /* Emit the function call */
+ if (director_method) {
+ Append(f->code, "try {\n");
+ }
+
+ /* Now write code to make the function call */
+
+ Swig_director_emit_dynamic_cast(n, f);
+ String *actioncode = emit_action(n);
+
+ if (director_method) {
+ Append(actioncode, "} catch (Swig::DirectorException& swig_err) {\n");
+ Append(actioncode, " sv_setsv(ERRSV, swig_err.getNative());\n");
+ Append(actioncode, " SWIG_fail;\n");
+ Append(actioncode, "}\n");
+ }
+
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ SwigType *t = Getattr(n, "type");
+ Replaceall(tm, "$result", "ST(argvi)");
+ if (is_shadow(t)) {
+ Replaceall(tm, "$shadow", "SWIG_SHADOW");
+ } else {
+ Replaceall(tm, "$shadow", "0");
+ }
+ if (GetFlag(n, "feature:new")) {
+ Replaceall(tm, "$owner", "SWIG_OWNER");
+ } else {
+ Replaceall(tm, "$owner", "0");
+ }
+ Printf(f->code, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(d, 0), name);
+ }
+ emit_return_variable(n, d, f);
+
+ /* If there were any output args, take care of them. */
+
+ Printv(f->code, outarg, NIL);
+
+ /* If there was any cleanup, do that. */
+
+ Printv(f->code, cleanup, NIL);
+
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ }
+
+ if (director_method) {
+ if ((tm = Swig_typemap_lookup("directorfree", n, Swig_cresult_name(), 0))) {
+ Replaceall(tm, "$input", Swig_cresult_name());
+ Replaceall(tm, "$result", "ST(argvi)");
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+ }
+
+ Printv(f->code, "XSRETURN(argvi);\n", "fail:\n", cleanup, "SWIG_croak_null();\n" "}\n" "}\n", NIL);
+
+ /* Add the dXSARGS last */
+
+ Wrapper_add_local(f, "dXSARGS", "dXSARGS");
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+ Replaceall(f->code, "$symname", iname);
+
+ /* Dump the wrapper function */
+
+ Wrapper_print(f, f_wrappers);
+
+ /* Now register the function */
+
+ if (!Getattr(n, "sym:overloaded")) {
+ Printf(command_tab, "{\"%s::%s\", %s},\n", cmodule, iname, wname);
+ } else if (!Getattr(n, "sym:nextSibling")) {
+ /* Generate overloaded dispatch function */
+ int maxargs;
+ String *dispatch = Swig_overload_dispatch_cast(n, "PUSHMARK(MARK); SWIG_CALLXS(%s); return;", &maxargs);
+
+ /* Generate a dispatch wrapper for all overloaded functions */
+
+ Wrapper *df = NewWrapper();
+ String *dname = Swig_name_wrapper(iname);
+
+ Printv(df->def, "XS(", dname, ") {\n", NIL);
+
+ Wrapper_add_local(df, "dXSARGS", "dXSARGS");
+ Printv(df->code, dispatch, "\n", NIL);
+ Printf(df->code, "croak(\"No matching function for overloaded '%s'\");\n", iname);
+ Printf(df->code, "XSRETURN(0);\n");
+ Printv(df->code, "}\n", NIL);
+ Wrapper_print(df, f_wrappers);
+ Printf(command_tab, "{\"%s::%s\", %s},\n", cmodule, iname, dname);
+ DelWrapper(df);
+ Delete(dispatch);
+ Delete(dname);
+ }
+ if (!Getattr(n, "sym:nextSibling")) {
+ if (export_all) {
+ Printf(exported, "%s ", iname);
+ }
+
+ /* --------------------------------------------------------------------
+ * Create a stub for this function, provided it's not a member function
+ * -------------------------------------------------------------------- */
+
+ if ((blessed) && (!member_func)) {
+ Printv(func_stubs, "*", iname, " = *", cmodule, "::", iname, ";\n", NIL);
+ }
+
+ }
+ Delete(cleanup);
+ Delete(outarg);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * variableWrapper()
+ * ------------------------------------------------------------ */
+ virtual int variableWrapper(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+ Wrapper *getf, *setf;
+ String *tm;
+ String *getname = Swig_name_get(NSPACE_TODO, iname);
+ String *setname = Swig_name_set(NSPACE_TODO, iname);
+
+ String *get_name = Swig_name_wrapper(getname);
+ String *set_name = Swig_name_wrapper(setname);
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ getf = NewWrapper();
+ setf = NewWrapper();
+
+ /* Create a Perl function for setting the variable value */
+
+ if (!GetFlag(n, "feature:immutable")) {
+ Setattr(n, "wrap:name", set_name);
+ Printf(setf->def, "SWIGCLASS_STATIC int %s(pTHX_ SV* sv, MAGIC * SWIGUNUSEDPARM(mg)) {\n", set_name);
+ Printv(setf->code, tab4, "MAGIC_PPERL\n", NIL);
+
+ /* Check for a few typemaps */
+ tm = Swig_typemap_lookup("varin", n, name, 0);
+ if (tm) {
+ Replaceall(tm, "$input", "sv");
+ /* Printf(setf->code,"%s\n", tm); */
+ emit_action_code(n, setf->code, tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s.\n", SwigType_str(t, 0));
+ DelWrapper(setf);
+ DelWrapper(getf);
+ return SWIG_NOWRAP;
+ }
+ Printf(setf->code, "fail:\n");
+ Printf(setf->code, " return 1;\n}\n");
+ Replaceall(setf->code, "$symname", iname);
+ Wrapper_print(setf, magic);
+ }
+
+ /* Now write a function to evaluate the variable */
+ Setattr(n, "wrap:name", get_name);
+ int addfail = 0;
+ Printf(getf->def, "SWIGCLASS_STATIC int %s(pTHX_ SV *sv, MAGIC *SWIGUNUSEDPARM(mg)) {\n", get_name);
+ Printv(getf->code, tab4, "MAGIC_PPERL\n", NIL);
+
+ if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
+ Replaceall(tm, "$result", "sv");
+ if (is_shadow(t)) {
+ Replaceall(tm, "$shadow", "SWIG_SHADOW");
+ } else {
+ Replaceall(tm, "$shadow", "0");
+ }
+ /* Printf(getf->code,"%s\n", tm); */
+ addfail = emit_action_code(n, getf->code, tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VAROUT_UNDEF, input_file, line_number, "Unable to read variable of type %s\n", SwigType_str(t, 0));
+ DelWrapper(setf);
+ DelWrapper(getf);
+ return SWIG_NOWRAP;
+ }
+ Printf(getf->code, " return 1;\n");
+ if (addfail) {
+ Append(getf->code, "fail:\n");
+ Append(getf->code, " return 0;\n");
+ }
+ Append(getf->code, "}\n");
+
+
+ Replaceall(getf->code, "$symname", iname);
+ Wrapper_print(getf, magic);
+
+ String *tt = Getattr(n, "tmap:varout:type");
+ if (tt) {
+ tt = NewStringf("&%s", tt);
+ } else {
+ tt = NewString("0");
+ }
+ /* Now add symbol to the PERL interpreter */
+ if (GetFlag(n, "feature:immutable")) {
+ Printv(variable_tab, tab4, "{ \"", cmodule, "::", iname, "\", MAGIC_CLASS swig_magic_readonly, MAGIC_CLASS ", get_name, ",", tt, " },\n", NIL);
+
+ } else {
+ Printv(variable_tab, tab4, "{ \"", cmodule, "::", iname, "\", MAGIC_CLASS ", set_name, ", MAGIC_CLASS ", get_name, ",", tt, " },\n", NIL);
+ }
+
+ /* If we're blessed, try to figure out what to do with the variable
+ 1. If it's a Perl object of some sort, create a tied-hash
+ around it.
+ 2. Otherwise, just hack Perl's symbol table */
+
+ if (blessed) {
+ if (is_shadow(t)) {
+ Printv(var_stubs,
+ "\nmy %__", iname, "_hash;\n",
+ "tie %__", iname, "_hash,\"", is_shadow(t), "\", $",
+ cmodule, "::", iname, ";\n", "$", iname, "= \\%__", iname, "_hash;\n", "bless $", iname, ", ", is_shadow(t), ";\n", NIL);
+ } else {
+ Printv(var_stubs, "*", iname, " = *", cmodule, "::", iname, ";\n", NIL);
+ }
+ }
+ if (export_all)
+ Printf(exported, "$%s ", iname);
+
+ Delete(tt);
+ DelWrapper(setf);
+ DelWrapper(getf);
+ Delete(getname);
+ Delete(setname);
+ Delete(set_name);
+ Delete(get_name);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constantWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ String *tm;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ /* Special hook for member pointer */
+ if (SwigType_type(type) == T_MPOINTER) {
+ String *wname = Swig_name_wrapper(iname);
+ Printf(f_wrappers, "static %s = %s;\n", SwigType_str(type, wname), value);
+ value = Char(wname);
+ }
+
+ if ((tm = Swig_typemap_lookup("consttab", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ if (is_shadow(type)) {
+ Replaceall(tm, "$shadow", "SWIG_SHADOW");
+ } else {
+ Replaceall(tm, "$shadow", "0");
+ }
+ Printf(constant_tab, "%s,\n", tm);
+ } else if ((tm = Swig_typemap_lookup("constcode", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ if (is_shadow(type)) {
+ Replaceall(tm, "$shadow", "SWIG_SHADOW");
+ } else {
+ Replaceall(tm, "$shadow", "0");
+ }
+ Printf(f_init, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value.\n");
+ return SWIG_NOWRAP;
+ }
+
+ if (blessed) {
+ if (is_shadow(type)) {
+ Printv(var_stubs,
+ "\nmy %__", iname, "_hash;\n",
+ "tie %__", iname, "_hash,\"", is_shadow(type), "\", $",
+ cmodule, "::", iname, ";\n", "$", iname, "= \\%__", iname, "_hash;\n", "bless $", iname, ", ", is_shadow(type), ";\n", NIL);
+ } else if (do_constants) {
+ Printv(const_stubs, "sub ", name, " () { $", cmodule, "::", name, " }\n", NIL);
+ num_consts++;
+ } else {
+ Printv(var_stubs, "*", iname, " = *", cmodule, "::", iname, ";\n", NIL);
+ }
+ }
+ if (export_all) {
+ if (do_constants && !is_shadow(type)) {
+ Printf(exported, "%s ", name);
+ } else {
+ Printf(exported, "$%s ", iname);
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * usage_func()
+ * ------------------------------------------------------------ */
+ char *usage_func(char *iname, SwigType *, ParmList *l) {
+ static String *temp = 0;
+ Parm *p;
+ int i;
+
+ if (!temp)
+ temp = NewString("");
+ Clear(temp);
+ Printf(temp, "%s(", iname);
+
+ /* Now go through and print parameters */
+ p = l;
+ i = 0;
+ while (p != 0) {
+ SwigType *pt = Getattr(p, "type");
+ String *pn = Getattr(p, "name");
+ if (!checkAttribute(p,"tmap:in:numinputs","0")) {
+ /* If parameter has been named, use that. Otherwise, just print a type */
+ if (SwigType_type(pt) != T_VOID) {
+ if (Len(pn) > 0) {
+ Printf(temp, "%s", pn);
+ } else {
+ Printf(temp, "%s", SwigType_str(pt, 0));
+ }
+ }
+ i++;
+ p = nextSibling(p);
+ if (p)
+ if (!checkAttribute(p,"tmap:in:numinputs","0"))
+ Putc(',', temp);
+ } else {
+ p = nextSibling(p);
+ if (p)
+ if ((i > 0) && (!checkAttribute(p,"tmap:in:numinputs","0")))
+ Putc(',', temp);
+ }
+ }
+ Printf(temp, ");");
+ return Char(temp);
+ }
+
+ /* ------------------------------------------------------------
+ * nativeWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int nativeWrapper(Node *n) {
+ String *name = Getattr(n, "sym:name");
+ String *funcname = Getattr(n, "wrap:name");
+
+ if (!addSymbol(funcname, n))
+ return SWIG_ERROR;
+
+ Printf(command_tab, "{\"%s::%s\", %s},\n", cmodule, name, funcname);
+ if (export_all)
+ Printf(exported, "%s ", name);
+ if (blessed) {
+ Printv(func_stubs, "*", name, " = *", cmodule, "::", name, ";\n", NIL);
+ }
+ return SWIG_OK;
+ }
+
+/* ----------------------------------------------------------------------------
+ * OBJECT-ORIENTED FEATURES
+ *
+ * These extensions provide a more object-oriented interface to C++
+ * classes and structures. The code here is based on extensions
+ * provided by David Fletcher and Gary Holt.
+ *
+ * I have generalized these extensions to make them more general purpose
+ * and to resolve object-ownership problems.
+ *
+ * The approach here is very similar to the Python module :
+ * 1. All of the original methods are placed into a single
+ * package like before except that a 'c' is appended to the
+ * package name.
+ *
+ * 2. All methods and function calls are wrapped with a new
+ * perl function. While possibly inefficient this allows
+ * us to catch complex function arguments (which are hard to
+ * track otherwise).
+ *
+ * 3. Classes are represented as tied-hashes in a manner similar
+ * to Gary Holt's extension. This allows us to access
+ * member data.
+ *
+ * 4. Stand-alone (global) C functions are modified to take
+ * tied hashes as arguments for complex datatypes (if
+ * appropriate).
+ *
+ * 5. Global variables involving a class/struct is encapsulated
+ * in a tied hash.
+ *
+ * ------------------------------------------------------------------------- */
+
+
+ void setclassname(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ String *fullname;
+ String *actualpackage;
+ Node *clsmodule = Getattr(n, "module");
+
+ if (!clsmodule) {
+ /* imported module does not define a module name. Oh well */
+ return;
+ }
+
+ /* Do some work on the class name */
+ if (verbose > 0) {
+ String *modulename = Getattr(clsmodule, "name");
+ fprintf(stdout, "setclassname: Found sym:name: %s\n", Char(symname));
+ fprintf(stdout, "setclassname: Found module: %s\n", Char(modulename));
+ fprintf(stdout, "setclassname: No package found\n");
+ }
+
+ if (dest_package) {
+ fullname = NewStringf("%s::%s", namespace_module, symname);
+ } else {
+ actualpackage = Getattr(clsmodule,"name");
+
+ if (verbose > 0) {
+ fprintf(stdout, "setclassname: Found actualpackage: %s\n", Char(actualpackage));
+ }
+ if ((!compat) && (!Strchr(symname,':'))) {
+ fullname = NewStringf("%s::%s",actualpackage,symname);
+ } else {
+ fullname = NewString(symname);
+ }
+ }
+ if (verbose > 0) {
+ fprintf(stdout, "setclassname: setting proxy: %s\n", Char(fullname));
+ }
+ Setattr(n, "perl5:proxy", fullname);
+ }
+
+ /* ------------------------------------------------------------
+ * classDeclaration()
+ * ------------------------------------------------------------ */
+ virtual int classDeclaration(Node *n) {
+ /* Do some work on the class name */
+ if (!Getattr(n, "feature:onlychildren")) {
+ if (blessed) {
+ setclassname(n);
+ Append(classlist, n);
+ }
+ }
+
+ return Language::classDeclaration(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int classHandler(Node *n) {
+
+ if (blessed) {
+ have_constructor = 0;
+ have_operators = 0;
+ have_destructor = 0;
+ have_data_members = 0;
+ operators = NewHash();
+
+ class_name = Getattr(n, "sym:name");
+
+ if (!addSymbol(class_name, n))
+ return SWIG_ERROR;
+
+ /* Use the fully qualified name of the Perl class */
+ if (!compat) {
+ fullclassname = NewStringf("%s::%s", namespace_module, class_name);
+ } else {
+ fullclassname = NewString(class_name);
+ }
+ real_classname = Getattr(n, "name");
+ pcode = NewString("");
+ // blessedmembers = NewString("");
+ }
+
+ /* Emit all of the members */
+ Language::classHandler(n);
+
+
+ /* Finish the rest of the class */
+ if (blessed) {
+ /* Generate a client-data entry */
+ SwigType *ct = NewStringf("p.%s", real_classname);
+ Printv(f_init, "SWIG_TypeClientData(SWIGTYPE", SwigType_manglestr(ct), ", (void*) \"", fullclassname, "\");\n", NIL);
+ SwigType_remember(ct);
+ Delete(ct);
+
+ Printv(pm, "\n############# Class : ", fullclassname, " ##############\n", "\npackage ", fullclassname, ";\n", NIL);
+
+ if (have_operators) {
+ Printf(pm, "use overload\n");
+ Iterator ki;
+ for (ki = First(operators); ki.key; ki = Next(ki)) {
+ char *name = Char(ki.key);
+ // fprintf(stderr,"found name: <%s>\n", name);
+ if (strstr(name, "__eq__")) {
+ Printv(pm, tab4, "\"==\" => sub { $_[0]->__eq__($_[1])},\n",NIL);
+ } else if (strstr(name, "__ne__")) {
+ Printv(pm, tab4, "\"!=\" => sub { $_[0]->__ne__($_[1])},\n",NIL);
+ // there are no tests for this in operator_overload_runme.pl
+ // it is likely to be broken
+ // } else if (strstr(name, "__assign__")) {
+ // Printv(pm, tab4, "\"=\" => sub { $_[0]->__assign__($_[1])},\n",NIL);
+ } else if (strstr(name, "__str__")) {
+ Printv(pm, tab4, "'\"\"' => sub { $_[0]->__str__()},\n",NIL);
+ } else if (strstr(name, "__plusplus__")) {
+ Printv(pm, tab4, "\"++\" => sub { $_[0]->__plusplus__()},\n",NIL);
+ } else if (strstr(name, "__minmin__")) {
+ Printv(pm, tab4, "\"--\" => sub { $_[0]->__minmin__()},\n",NIL);
+ } else if (strstr(name, "__add__")) {
+ Printv(pm, tab4, "\"+\" => sub { $_[0]->__add__($_[1])},\n",NIL);
+ } else if (strstr(name, "__sub__")) {
+ Printv(pm, tab4, "\"-\" => sub { if( not $_[2] ) { $_[0]->__sub__($_[1]) }\n",NIL);
+ Printv(pm, tab8, "elsif( $_[0]->can('__rsub__') ) { $_[0]->__rsub__($_[1]) }\n",NIL);
+ Printv(pm, tab8, "else { die(\"reverse subtraction not supported\") }\n",NIL);
+ Printv(pm, tab8, "},\n",NIL);
+ } else if (strstr(name, "__mul__")) {
+ Printv(pm, tab4, "\"*\" => sub { $_[0]->__mul__($_[1])},\n",NIL);
+ } else if (strstr(name, "__div__")) {
+ Printv(pm, tab4, "\"/\" => sub { $_[0]->__div__($_[1])},\n",NIL);
+ } else if (strstr(name, "__mod__")) {
+ Printv(pm, tab4, "\"%\" => sub { $_[0]->__mod__($_[1])},\n",NIL);
+ // there are no tests for this in operator_overload_runme.pl
+ // it is likely to be broken
+ // } else if (strstr(name, "__and__")) {
+ // Printv(pm, tab4, "\"&\" => sub { $_[0]->__and__($_[1])},\n",NIL);
+
+ // there are no tests for this in operator_overload_runme.pl
+ // it is likely to be broken
+ // } else if (strstr(name, "__or__")) {
+ // Printv(pm, tab4, "\"|\" => sub { $_[0]->__or__($_[1])},\n",NIL);
+ } else if (strstr(name, "__gt__")) {
+ Printv(pm, tab4, "\">\" => sub { $_[0]->__gt__($_[1])},\n",NIL);
+ } else if (strstr(name, "__ge__")) {
+ Printv(pm, tab4, "\">=\" => sub { $_[0]->__ge__($_[1])},\n",NIL);
+ } else if (strstr(name, "__not__")) {
+ Printv(pm, tab4, "\"!\" => sub { $_[0]->__not__()},\n",NIL);
+ } else if (strstr(name, "__lt__")) {
+ Printv(pm, tab4, "\"<\" => sub { $_[0]->__lt__($_[1])},\n",NIL);
+ } else if (strstr(name, "__le__")) {
+ Printv(pm, tab4, "\"<=\" => sub { $_[0]->__le__($_[1])},\n",NIL);
+ } else if (strstr(name, "__pluseq__")) {
+ Printv(pm, tab4, "\"+=\" => sub { $_[0]->__pluseq__($_[1])},\n",NIL);
+ } else if (strstr(name, "__mineq__")) {
+ Printv(pm, tab4, "\"-=\" => sub { $_[0]->__mineq__($_[1])},\n",NIL);
+ } else if (strstr(name, "__neg__")) {
+ Printv(pm, tab4, "\"neg\" => sub { $_[0]->__neg__()},\n",NIL);
+ } else {
+ fprintf(stderr,"Unknown operator: %s\n", name);
+ }
+ }
+ Printv(pm, tab4,
+ "\"=\" => sub { my $class = ref($_[0]); $class->new($_[0]) },\n", NIL);
+ Printv(pm, tab4, "\"fallback\" => 1;\n", NIL);
+ }
+ // make use strict happy
+ Printv(pm, "use vars qw(@ISA %OWNER %ITERATORS %BLESSEDMEMBERS);\n", NIL);
+
+ /* If we are inheriting from a base class, set that up */
+
+ Printv(pm, "@ISA = qw(", NIL);
+
+ /* Handle inheritance */
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator b;
+ b = First(baselist);
+ while (b.item) {
+ String *bname = Getattr(b.item, "perl5:proxy");
+ if (!bname) {
+ b = Next(b);
+ continue;
+ }
+ Printv(pm, " ", bname, NIL);
+ b = Next(b);
+ }
+ }
+
+ /* Module comes last */
+ if (!compat || Cmp(namespace_module, fullclassname)) {
+ Printv(pm, " ", namespace_module, NIL);
+ }
+
+ Printf(pm, " );\n");
+
+ /* Dump out a hash table containing the pointers that we own */
+ Printf(pm, "%%OWNER = ();\n");
+ if (have_data_members || have_destructor)
+ Printf(pm, "%%ITERATORS = ();\n");
+
+ /* Dump out the package methods */
+
+ Printv(pm, pcode, NIL);
+ Delete(pcode);
+
+ /* Output methods for managing ownership */
+
+ String *director_disown;
+ if (Getattr(n, "perl5:directordisown")) {
+ director_disown = NewStringf("%s%s($self);\n", tab4, Getattr(n, "perl5:directordisown"));
+ } else {
+ director_disown = NewString("");
+ }
+ Printv(pm,
+ "sub DISOWN {\n",
+ tab4, "my $self = shift;\n",
+ director_disown,
+ tab4, "my $ptr = tied(%$self);\n",
+ tab4, "delete $OWNER{$ptr};\n",
+ "}\n\n", "sub ACQUIRE {\n", tab4, "my $self = shift;\n", tab4, "my $ptr = tied(%$self);\n", tab4, "$OWNER{$ptr} = 1;\n", "}\n\n", NIL);
+ Delete(director_disown);
+
+ /* Only output the following methods if a class has member data */
+
+ Delete(operators);
+ operators = 0;
+ if (Swig_directorclass(n)) {
+ /* director classes need a way to recover subclass instance attributes */
+ Node *get_attr = NewHash();
+ String *mrename;
+ String *symname = Getattr(n, "sym:name");
+ mrename = Swig_name_disown(NSPACE_TODO, symname);
+ Replaceall(mrename, "disown", "swig_get_attr");
+ String *type = NewString(getClassType());
+ String *name = NewString("self");
+ SwigType_add_pointer(type);
+ Parm *p = NewParm(type, name, n);
+ Delete(name);
+ Delete(type);
+ type = NewString("SV");
+ SwigType_add_pointer(type);
+ String *action = NewString("");
+ Printv(action, "{\n", " Swig::Director *director = SWIG_DIRECTOR_CAST(arg1);\n",
+ " result = sv_newmortal();\n" " if (director) sv_setsv(result, director->swig_get_self());\n", "}\n", NIL);
+ Setfile(get_attr, Getfile(n));
+ Setline(get_attr, Getline(n));
+ Setattr(get_attr, "wrap:action", action);
+ Setattr(get_attr, "name", mrename);
+ Setattr(get_attr, "sym:name", mrename);
+ Setattr(get_attr, "type", type);
+ Setattr(get_attr, "parms", p);
+ Delete(action);
+ Delete(type);
+ Delete(p);
+
+ member_func = 1;
+ functionWrapper(get_attr);
+ member_func = 0;
+ Delete(get_attr);
+
+ Printv(pm, "sub FETCH {\n", tab4, "my ($self,$field) = @_;\n", tab4, "my $member_func = \"swig_${field}_get\";\n", tab4,
+ "if (not $self->can($member_func)) {\n", tab8, "my $h = ", cmodule, "::", mrename, "($self);\n", tab8, "return $h->{$field} if $h;\n",
+ tab4, "}\n", tab4, "return $self->$member_func;\n", "}\n", "\n", "sub STORE {\n", tab4, "my ($self,$field,$newval) = @_;\n", tab4,
+ "my $member_func = \"swig_${field}_set\";\n", tab4, "if (not $self->can($member_func)) {\n", tab8, "my $h = ", cmodule, "::", mrename,
+ "($self);\n", tab8, "return $h->{$field} = $newval if $h;\n", tab4, "}\n", tab4, "return $self->$member_func($newval);\n", "}\n", NIL);
+
+ Delete(mrename);
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * memberfunctionHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int memberfunctionHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+
+ member_func = 1;
+ Language::memberfunctionHandler(n);
+ member_func = 0;
+
+ if ((blessed) && (!Getattr(n, "sym:nextSibling"))) {
+
+ if (Strstr(symname, "__eq__")) {
+ DohSetInt(operators, "__eq__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__ne__")) {
+ DohSetInt(operators, "__ne__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__assign__")) {
+ DohSetInt(operators, "__assign__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__str__")) {
+ DohSetInt(operators, "__str__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__add__")) {
+ DohSetInt(operators, "__add__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__sub__")) {
+ DohSetInt(operators, "__sub__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__mul__")) {
+ DohSetInt(operators, "__mul__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__div__")) {
+ DohSetInt(operators, "__div__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__mod__")) {
+ DohSetInt(operators, "__mod__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__and__")) {
+ DohSetInt(operators, "__and__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__or__")) {
+ DohSetInt(operators, "__or__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__not__")) {
+ DohSetInt(operators, "__not__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__gt__")) {
+ DohSetInt(operators, "__gt__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__ge__")) {
+ DohSetInt(operators, "__ge__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__lt__")) {
+ DohSetInt(operators, "__lt__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__le__")) {
+ DohSetInt(operators, "__le__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__neg__")) {
+ DohSetInt(operators, "__neg__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__plusplus__")) {
+ DohSetInt(operators, "__plusplus__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__minmin__")) {
+ DohSetInt(operators, "__minmin__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__mineq__")) {
+ DohSetInt(operators, "__mineq__", 1);
+ have_operators = 1;
+ } else if (Strstr(symname, "__pluseq__")) {
+ DohSetInt(operators, "__pluseq__", 1);
+ have_operators = 1;
+ }
+
+ if (Getattr(n, "feature:shadow")) {
+ String *plcode = perlcode(Getattr(n, "feature:shadow"), 0);
+ String *plaction = NewStringf("%s::%s", cmodule, Swig_name_member(NSPACE_TODO, class_name, symname));
+ Replaceall(plcode, "$action", plaction);
+ Delete(plaction);
+ Printv(pcode, plcode, NIL);
+ } else {
+ Printv(pcode, "*", symname, " = *", cmodule, "::", Swig_name_member(NSPACE_TODO, class_name, symname), ";\n", NIL);
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * membervariableHandler()
+ *
+ * Adds an instance member.
+ * ----------------------------------------------------------------------------- */
+
+ virtual int membervariableHandler(Node *n) {
+
+ String *symname = Getattr(n, "sym:name");
+ /* SwigType *t = Getattr(n,"type"); */
+
+ /* Emit a pair of get/set functions for the variable */
+
+ member_func = 1;
+ Language::membervariableHandler(n);
+ member_func = 0;
+
+ if (blessed) {
+
+ Printv(pcode, "*swig_", symname, "_get = *", cmodule, "::", Swig_name_get(NSPACE_TODO, Swig_name_member(NSPACE_TODO, class_name, symname)), ";\n", NIL);
+ Printv(pcode, "*swig_", symname, "_set = *", cmodule, "::", Swig_name_set(NSPACE_TODO, Swig_name_member(NSPACE_TODO, class_name, symname)), ";\n", NIL);
+
+ /* Now we need to generate a little Perl code for this */
+
+ /* if (is_shadow(t)) {
+
+ *//* This is a Perl object that we have already seen. Add an
+ entry to the members list *//*
+ Printv(blessedmembers,
+ tab4, symname, " => '", is_shadow(t), "',\n",
+ NIL);
+
+ }
+ */
+ }
+ have_data_members++;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constructorDeclaration()
+ *
+ * Emits a blessed constructor for our class. In addition to our construct
+ * we manage a Perl hash table containing all of the pointers created by
+ * the constructor. This prevents us from accidentally trying to free
+ * something that wasn't necessarily allocated by malloc or new
+ * ------------------------------------------------------------ */
+
+ virtual int constructorHandler(Node *n) {
+
+ String *symname = Getattr(n, "sym:name");
+
+ member_func = 1;
+
+ Swig_save("perl5:constructorHandler", n, "parms", NIL);
+ if (Swig_directorclass(n)) {
+ Parm *parms = Getattr(n, "parms");
+ Parm *self;
+ String *name = NewString("self");
+ String *type = NewString("SV");
+ SwigType_add_pointer(type);
+ self = NewParm(type, name, n);
+ Delete(type);
+ Delete(name);
+ Setattr(self, "lname", "O");
+ if (parms)
+ set_nextSibling(self, parms);
+ Setattr(n, "parms", self);
+ Setattr(n, "wrap:self", "1");
+ Setattr(n, "hidden", "1");
+ Delete(self);
+ }
+
+ String *saved_nc = none_comparison;
+ none_comparison = NewStringf("strcmp(SvPV_nolen(ST(0)), \"%s::%s\") != 0", module, class_name);
+ String *saved_director_prot_ctor_code = director_prot_ctor_code;
+ director_prot_ctor_code = NewStringf("if ($comparison) { /* subclassed */\n" " $director_new\n" "} else {\n"
+ "SWIG_exception_fail(SWIG_RuntimeError, \"accessing abstract class or protected constructor\");\n" "}\n");
+ Language::constructorHandler(n);
+ Delete(none_comparison);
+ none_comparison = saved_nc;
+ Delete(director_prot_ctor_code);
+ director_prot_ctor_code = saved_director_prot_ctor_code;
+ Swig_restore(n);
+
+ if ((blessed) && (!Getattr(n, "sym:nextSibling"))) {
+ if (Getattr(n, "feature:shadow")) {
+ String *plcode = perlcode(Getattr(n, "feature:shadow"), 0);
+ String *plaction = NewStringf("%s::%s", module, Swig_name_member(NSPACE_TODO, class_name, symname));
+ Replaceall(plcode, "$action", plaction);
+ Delete(plaction);
+ Printv(pcode, plcode, NIL);
+ } else {
+ if ((Cmp(symname, class_name) == 0)) {
+ /* Emit a blessed constructor */
+ Printf(pcode, "sub new {\n");
+ } else {
+ /* Constructor doesn't match classname so we'll just use the normal name */
+ Printv(pcode, "sub ", Swig_name_construct(NSPACE_TODO, symname), " {\n", NIL);
+ }
+
+ const char *pkg = getCurrentClass() && Swig_directorclass(getCurrentClass())? "$_[0]" : "shift";
+ Printv(pcode,
+ tab4, "my $pkg = ", pkg, ";\n",
+ tab4, "my $self = ", cmodule, "::", Swig_name_construct(NSPACE_TODO, symname), "(@_);\n", tab4, "bless $self, $pkg if defined($self);\n", "}\n\n", NIL);
+
+ have_constructor = 1;
+ }
+ }
+ member_func = 0;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * destructorHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int destructorHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ member_func = 1;
+ Language::destructorHandler(n);
+ if (blessed) {
+ if (Getattr(n, "feature:shadow")) {
+ String *plcode = perlcode(Getattr(n, "feature:shadow"), 0);
+ String *plaction = NewStringf("%s::%s", module, Swig_name_member(NSPACE_TODO, class_name, symname));
+ Replaceall(plcode, "$action", plaction);
+ Delete(plaction);
+ Printv(pcode, plcode, NIL);
+ } else {
+ Printv(pcode,
+ "sub DESTROY {\n",
+ tab4, "return unless $_[0]->isa('HASH');\n",
+ tab4, "my $self = tied(%{$_[0]});\n",
+ tab4, "return unless defined $self;\n",
+ tab4, "delete $ITERATORS{$self};\n",
+ tab4, "if (exists $OWNER{$self}) {\n",
+ tab8, cmodule, "::", Swig_name_destroy(NSPACE_TODO, symname), "($self);\n", tab8, "delete $OWNER{$self};\n", tab4, "}\n}\n\n", NIL);
+ have_destructor = 1;
+ }
+ }
+ member_func = 0;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+ member_func = 1;
+ Language::staticmemberfunctionHandler(n);
+ member_func = 0;
+ if ((blessed) && (!Getattr(n, "sym:nextSibling"))) {
+ String *symname = Getattr(n, "sym:name");
+ Printv(pcode, "*", symname, " = *", cmodule, "::", Swig_name_member(NSPACE_TODO, class_name, symname), ";\n", NIL);
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * staticmembervariableHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int staticmembervariableHandler(Node *n) {
+ Language::staticmembervariableHandler(n);
+ if (blessed) {
+ String *symname = Getattr(n, "sym:name");
+ Printv(pcode, "*", symname, " = *", cmodule, "::", Swig_name_member(NSPACE_TODO, class_name, symname), ";\n", NIL);
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * memberconstantHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int memberconstantHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ int oldblessed = blessed;
+
+ /* Create a normal constant */
+ blessed = 0;
+ Language::memberconstantHandler(n);
+ blessed = oldblessed;
+
+ if (blessed) {
+ Printv(pcode, "*", symname, " = *", cmodule, "::", Swig_name_member(NSPACE_TODO, class_name, symname), ";\n", NIL);
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * pragma()
+ *
+ * Pragma directive.
+ *
+ * %pragma(perl5) code="String" # Includes a string in the .pm file
+ * %pragma(perl5) include="file.pl" # Includes a file in the .pm file
+ * ------------------------------------------------------------ */
+
+ virtual int pragmaDirective(Node *n) {
+ String *lang;
+ String *code;
+ String *value;
+ if (!ImportMode) {
+ lang = Getattr(n, "lang");
+ code = Getattr(n, "name");
+ value = Getattr(n, "value");
+ if (Strcmp(lang, "perl5") == 0) {
+ if (Strcmp(code, "code") == 0) {
+ /* Dump the value string into the .pm file */
+ if (value) {
+ Printf(pragma_include, "%s\n", value);
+ }
+ } else if (Strcmp(code, "include") == 0) {
+ /* Include a file into the .pm file */
+ if (value) {
+ FILE *f = Swig_include_open(value);
+ if (!f) {
+ Swig_error(input_file, line_number, "Unable to locate file %s\n", value);
+ } else {
+ char buffer[4096];
+ while (fgets(buffer, 4095, f)) {
+ Printf(pragma_include, "%s", buffer);
+ }
+ fclose(f);
+ }
+ }
+ } else {
+ Swig_error(input_file, line_number, "Unrecognized pragma.\n");
+ }
+ }
+ }
+ return Language::pragmaDirective(n);
+ }
+
+ /* ------------------------------------------------------------
+ * perlcode() - Output perlcode code into the shadow file
+ * ------------------------------------------------------------ */
+
+ String *perlcode(String *code, const String *indent) {
+ String *out = NewString("");
+ String *temp;
+ char *t;
+ if (!indent)
+ indent = "";
+
+ temp = NewString(code);
+
+ t = Char(temp);
+ if (*t == '{') {
+ Delitem(temp, 0);
+ Delitem(temp, DOH_END);
+ }
+
+ /* Split the input text into lines */
+ List *clist = SplitLines(temp);
+ Delete(temp);
+ int initial = 0;
+ String *s = 0;
+ Iterator si;
+ /* Get the initial indentation */
+
+ for (si = First(clist); si.item; si = Next(si)) {
+ s = si.item;
+ if (Len(s)) {
+ char *c = Char(s);
+ while (*c) {
+ if (!isspace(*c))
+ break;
+ initial++;
+ c++;
+ }
+ if (*c && !isspace(*c))
+ break;
+ else {
+ initial = 0;
+ }
+ }
+ }
+ while (si.item) {
+ s = si.item;
+ if (Len(s) > initial) {
+ char *c = Char(s);
+ c += initial;
+ Printv(out, indent, c, "\n", NIL);
+ } else {
+ Printv(out, "\n", NIL);
+ }
+ si = Next(si);
+ }
+ Delete(clist);
+ return out;
+ }
+
+ /* ------------------------------------------------------------
+ * insertDirective()
+ *
+ * Hook for %insert directive.
+ * ------------------------------------------------------------ */
+
+ virtual int insertDirective(Node *n) {
+ String *code = Getattr(n, "code");
+ String *section = Getattr(n, "section");
+
+ if ((!ImportMode) && (Cmp(section, "perl") == 0)) {
+ Printv(additional_perl_code, code, NIL);
+ } else {
+ Language::insertDirective(n);
+ }
+ return SWIG_OK;
+ }
+
+ String *runtimeCode() {
+ String *s = NewString("");
+ String *shead = Swig_include_sys("perlhead.swg");
+ if (!shead) {
+ Printf(stderr, "*** Unable to open 'perlhead.swg'\n");
+ } else {
+ Append(s, shead);
+ Delete(shead);
+ }
+ String *serrors = Swig_include_sys("perlerrors.swg");
+ if (!serrors) {
+ Printf(stderr, "*** Unable to open 'perlerrors.swg'\n");
+ } else {
+ Append(s, serrors);
+ Delete(serrors);
+ }
+ String *srun = Swig_include_sys("perlrun.swg");
+ if (!srun) {
+ Printf(stderr, "*** Unable to open 'perlrun.swg'\n");
+ } else {
+ Append(s, srun);
+ Delete(srun);
+ }
+ return s;
+ }
+
+ String *defaultExternalRuntimeFilename() {
+ return NewString("swigperlrun.h");
+ }
+
+ virtual int classDirectorInit(Node *n) {
+ String *declaration = Swig_director_declaration(n);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "%s\n", declaration);
+ Printf(f_directors_h, "public:\n");
+ Delete(declaration);
+ return Language::classDirectorInit(n);
+ }
+
+ virtual int classDirectorEnd(Node *n) {
+ if (dirprot_mode()) {
+ /*
+ This implementation uses a std::map<std::string,int>.
+
+ It should be possible to rewrite it using a more elegant way,
+ like copying the Java approach for the 'override' array.
+
+ But for now, this seems to be the least intrusive way.
+ */
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "/* Internal director utilities */\n");
+ Printf(f_directors_h, "public:\n");
+ Printf(f_directors_h, " bool swig_get_inner(const char *swig_protected_method_name) const {\n");
+ Printf(f_directors_h, " std::map<std::string, bool>::const_iterator iv = swig_inner.find(swig_protected_method_name);\n");
+ Printf(f_directors_h, " return (iv != swig_inner.end() ? iv->second : false);\n");
+ Printf(f_directors_h, " }\n");
+
+ Printf(f_directors_h, " void swig_set_inner(const char *swig_protected_method_name, bool swig_val) const {\n");
+ Printf(f_directors_h, " swig_inner[swig_protected_method_name] = swig_val;\n");
+ Printf(f_directors_h, " }\n");
+ Printf(f_directors_h, "private:\n");
+ Printf(f_directors_h, " mutable std::map<std::string, bool> swig_inner;\n");
+ }
+ Printf(f_directors_h, "};\n");
+ return Language::classDirectorEnd(n);
+ }
+
+ virtual int classDirectorConstructor(Node *n) {
+ Node *parent = Getattr(n, "parentNode");
+ String *sub = NewString("");
+ String *decl = Getattr(n, "decl");
+ String *supername = Swig_class_name(parent);
+ String *classname = NewString("");
+ Printf(classname, "SwigDirector_%s", supername);
+
+ /* insert self parameter */
+ Parm *p;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms = CopyParmList(superparms);
+ String *type = NewString("SV");
+ SwigType_add_pointer(type);
+ p = NewParm(type, NewString("self"), n);
+ set_nextSibling(p, parms);
+ parms = p;
+
+ if (!Getattr(n, "defaultargs")) {
+ /* constructor */
+ {
+ Wrapper *w = NewWrapper();
+ String *call;
+ String *basetype = Getattr(parent, "classtype");
+ String *target = Swig_method_decl(0, decl, classname, parms, 0);
+ call = Swig_csuperclass_call(0, basetype, superparms);
+ Printf(w->def, "%s::%s: %s, Swig::Director(self) { \n", classname, target, call);
+ Printf(w->def, " SWIG_DIRECTOR_RGTR((%s *)this, this); \n", basetype);
+ Append(w->def, "}\n");
+ Delete(target);
+ Wrapper_print(w, f_directors);
+ Delete(call);
+ DelWrapper(w);
+ }
+
+ /* constructor header */
+ {
+ String *target = Swig_method_decl(0, decl, classname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+
+ Delete(sub);
+ Delete(classname);
+ Delete(supername);
+ Delete(parms);
+ return Language::classDirectorConstructor(n);
+ }
+
+ virtual int classDirectorMethod(Node *n, Node *parent, String *super) {
+ int is_void = 0;
+ int is_pointer = 0;
+ String *decl = Getattr(n, "decl");
+ String *name = Getattr(n, "name");
+ String *classname = Getattr(parent, "sym:name");
+ String *c_classname = Getattr(parent, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *declaration = NewString("");
+ ParmList *l = Getattr(n, "parms");
+ Wrapper *w = NewWrapper();
+ String *tm;
+ String *wrap_args = NewString("");
+ String *returntype = Getattr(n, "type");
+ String *value = Getattr(n, "value");
+ String *storage = Getattr(n, "storage");
+ bool pure_virtual = false;
+ int status = SWIG_OK;
+ int idx;
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+
+ if (Cmp(storage, "virtual") == 0) {
+ if (Cmp(value, "0") == 0) {
+ pure_virtual = true;
+ }
+ }
+
+ /* determine if the method returns a pointer */
+ is_pointer = SwigType_ispointer_return(decl);
+ is_void = (!Cmp(returntype, "void") && !is_pointer);
+
+ /* virtual method definition */
+ String *target;
+ String *pclassname = NewStringf("SwigDirector_%s", classname);
+ String *qualified_name = NewStringf("%s::%s", pclassname, name);
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+ /* header declaration */
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Get any exception classes in the throws typemap
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ ParmList *throw_parm_list = 0;
+
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ Parm *p;
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list)
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+ String *str = SwigType_str(Getattr(p, "type"), 0);
+ Append(w->def, str);
+ Append(declaration, str);
+ Delete(str);
+ }
+ }
+
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+
+ /* declare method return value
+ * if the return value is a reference or const reference, a specialized typemap must
+ * handle it, including declaration of c_result ($result).
+ */
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ }
+ } else {
+ String *cres = SwigType_lstr(returntype, "c_result");
+ Printf(w->code, "%s;\n", cres);
+ Delete(cres);
+ }
+ }
+
+ if (!is_void && !ignored_method) {
+ String *pres = NewStringf("SV *%s", Swig_cresult_name());
+ Wrapper_add_local(w, Swig_cresult_name(), pres);
+ Delete(pres);
+ }
+
+ if (ignored_method) {
+ if (!pure_virtual) {
+ if (!is_void)
+ Printf(w->code, "return ");
+ String *super_call = Swig_method_call(super, l);
+ Printf(w->code, "%s;\n", super_call);
+ Delete(super_call);
+ } else {
+ Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname),
+ SwigType_namestr(name));
+ }
+ } else {
+ /* attach typemaps to arguments (C/C++ -> Perl) */
+ String *parse_args = NewString("");
+ String *pstack = NewString("");
+
+ Swig_director_parms_fixup(l);
+
+ /* remove the wrapper 'w' since it was producing spurious temps */
+ Swig_typemap_attach_parms("in", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ Wrapper_add_local(w, "SP", "dSP");
+
+ {
+ String *ptype = Copy(getClassType());
+ SwigType_add_pointer(ptype);
+ String *mangle = SwigType_manglestr(ptype);
+
+ Wrapper_add_local(w, "swigself", "SV *swigself");
+ Printf(w->code, "swigself = SWIG_NewPointerObj(SWIG_as_voidptr(this), SWIGTYPE%s, SWIG_SHADOW);\n", mangle);
+ Printf(w->code, "sv_bless(swigself, gv_stashpv(swig_get_class(), 0));\n");
+ Delete(mangle);
+ Delete(ptype);
+ Append(pstack, "XPUSHs(swigself);\n");
+ }
+
+ Parm *p;
+ char source[256];
+
+ int outputs = 0;
+ if (!is_void)
+ outputs++;
+
+ /* build argument list and type conversion string */
+ idx = 0;
+ p = l;
+ while (p) {
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ /* old style? caused segfaults without the p!=0 check
+ in the for() condition, and seems dangerous in the
+ while loop as well.
+ while (Getattr(p, "tmap:ignore")) {
+ p = Getattr(p, "tmap:ignore:next");
+ }
+ */
+
+ if (Getattr(p, "tmap:directorargout") != 0)
+ outputs++;
+
+ String *pname = Getattr(p, "name");
+ String *ptype = Getattr(p, "type");
+
+ if ((tm = Getattr(p, "tmap:directorin")) != 0) {
+ sprintf(source, "obj%d", idx++);
+ String *input = NewString(source);
+ Setattr(p, "emit:directorinput", input);
+ Replaceall(tm, "$input", input);
+ Delete(input);
+ Replaceall(tm, "$owner", "0");
+ Replaceall(tm, "$shadow", "0");
+ /* Wrapper_add_localv(w, source, "SV *", source, "= 0", NIL); */
+ Printv(wrap_args, "SV *", source, ";\n", NIL);
+
+ Printv(wrap_args, tm, "\n", NIL);
+ Putc('O', parse_args);
+ Printv(pstack, "XPUSHs(", source, ");\n", NIL);
+ p = Getattr(p, "tmap:directorin:next");
+ continue;
+ } else if (Cmp(ptype, "void")) {
+ /* special handling for pointers to other C++ director classes.
+ * ideally this would be left to a typemap, but there is currently no
+ * way to selectively apply the dynamic_cast<> to classes that have
+ * directors. in other words, the type "SwigDirector_$1_lname" only exists
+ * for classes with directors. we avoid the problem here by checking
+ * module.wrap::directormap, but it's not clear how to get a typemap to
+ * do something similar. perhaps a new default typemap (in addition
+ * to SWIGTYPE) called DIRECTORTYPE?
+ */
+ if (SwigType_ispointer(ptype) || SwigType_isreference(ptype)) {
+ Node *module = Getattr(parent, "module");
+ Node *target = Swig_directormap(module, ptype);
+ sprintf(source, "obj%d", idx++);
+ String *nonconst = 0;
+ /* strip pointer/reference --- should move to Swig/stype.c */
+ String *nptype = NewString(Char(ptype) + 2);
+ /* name as pointer */
+ String *ppname = Copy(pname);
+ if (SwigType_isreference(ptype)) {
+ Insert(ppname, 0, "&");
+ }
+ /* if necessary, cast away const since Perl doesn't support it! */
+ if (SwigType_isconst(nptype)) {
+ nonconst = NewStringf("nc_tmp_%s", pname);
+ String *nonconst_i = NewStringf("= const_cast< %s >(%s)", SwigType_lstr(ptype, 0), ppname);
+ Wrapper_add_localv(w, nonconst, SwigType_lstr(ptype, 0), nonconst, nonconst_i, NIL);
+ Delete(nonconst_i);
+ Swig_warning(WARN_LANG_DISCARD_CONST, input_file, line_number,
+ "Target language argument '%s' discards const in director method %s::%s.\n",
+ SwigType_str(ptype, pname), SwigType_namestr(c_classname), SwigType_namestr(name));
+ } else {
+ nonconst = Copy(ppname);
+ }
+ Delete(nptype);
+ Delete(ppname);
+ String *mangle = SwigType_manglestr(ptype);
+ if (target) {
+ String *director = NewStringf("director_%s", mangle);
+ Wrapper_add_localv(w, director, "Swig::Director *", director, "= 0", NIL);
+ Wrapper_add_localv(w, source, "SV *", source, "= 0", NIL);
+ Printf(wrap_args, "%s = SWIG_DIRECTOR_CAST(%s);\n", director, nonconst);
+ Printf(wrap_args, "if (!%s) {\n", director);
+ Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
+ Append(wrap_args, "} else {\n");
+ Printf(wrap_args, "%s = %s->swig_get_self();\n", source, director);
+ Printf(wrap_args, "SvREFCNT_inc((SV *)%s);\n", source);
+ Append(wrap_args, "}\n");
+ Delete(director);
+ } else {
+ Wrapper_add_localv(w, source, "SV *", source, "= 0", NIL);
+ Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
+ Printf(pstack, "XPUSHs(sv_2mortal(%s));\n", source);
+ }
+ Putc('O', parse_args);
+ Delete(mangle);
+ Delete(nonconst);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
+ "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(ptype, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_NOWRAP;
+ break;
+ }
+ }
+ p = nextSibling(p);
+ }
+
+ /* add the method name as a PyString */
+ String *pyname = Getattr(n, "sym:name");
+
+ /* wrap complex arguments to PyObjects */
+ Printv(w->code, wrap_args, NIL);
+
+ /* pass the method call on to the Python object */
+ if (dirprot_mode() && !is_public(n)) {
+ Printf(w->code, "swig_set_inner(\"%s\", true);\n", name);
+ }
+
+ Append(w->code, "ENTER;\n");
+ Append(w->code, "SAVETMPS;\n");
+ Append(w->code, "PUSHMARK(SP);\n");
+ Append(w->code, pstack);
+ Delete(pstack);
+ Append(w->code, "PUTBACK;\n");
+ Printf(w->code, "call_method(\"%s\", G_EVAL | G_SCALAR);\n", pyname);
+
+ if (dirprot_mode() && !is_public(n))
+ Printf(w->code, "swig_set_inner(\"%s\", false);\n", name);
+
+ /* exception handling */
+ tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
+ if (!tm) {
+ tm = Getattr(n, "feature:director:except");
+ if (tm)
+ tm = Copy(tm);
+ }
+ Append(w->code, "if (SvTRUE(ERRSV)) {\n");
+ Append(w->code, " PUTBACK;\n FREETMPS;\n LEAVE;\n");
+ if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
+ Replaceall(tm, "$error", "ERRSV");
+ Printv(w->code, Str(tm), "\n", NIL);
+ } else {
+ Printf(w->code, " Swig::DirectorMethodException::raise(ERRSV);\n");
+ }
+ Append(w->code, "}\n");
+ Delete(tm);
+
+ /*
+ * Python method may return a simple object, or a tuple.
+ * for in/out arguments, we have to extract the appropriate PyObjects from the tuple,
+ * then marshal everything back to C/C++ (return value and output arguments).
+ *
+ */
+
+ /* marshal return value and other outputs (if any) from PyObject to C/C++ type */
+
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+
+ if (outputs > 1) {
+ Wrapper_add_local(w, "output", "SV *output");
+ Printf(w->code, "if (count != %d) {\n", outputs);
+ Printf(w->code, " Swig::DirectorTypeMismatchException::raise(\"Perl method %s.%sfailed to return a list.\");\n", classname, pyname);
+ Append(w->code, "}\n");
+ }
+
+ idx = 0;
+
+ /* marshal return value */
+ if (!is_void) {
+ Append(w->code, "SPAGAIN;\n");
+ Printf(w->code, "%s = POPs;\n", Swig_cresult_name());
+ tm = Swig_typemap_lookup("directorout", n, Swig_cresult_name(), w);
+ if (tm != 0) {
+ if (outputs > 1) {
+ Printf(w->code, "output = POPs;\n");
+ Replaceall(tm, "$input", "output");
+ } else {
+ Replaceall(tm, "$input", Swig_cresult_name());
+ }
+ char temp[24];
+ sprintf(temp, "%d", idx);
+ Replaceall(tm, "$argnum", temp);
+
+ /* TODO check this */
+ if (Getattr(n, "wrap:disown")) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+ Replaceall(tm, "$result", "c_result");
+ Printv(w->code, tm, "\n", NIL);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(returntype, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_ERROR;
+ }
+ }
+
+ /* marshal outputs */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
+ if (outputs > 1) {
+ Printf(w->code, "output = POPs;\n");
+ Replaceall(tm, "$result", "output");
+ } else {
+ Replaceall(tm, "$result", Swig_cresult_name());
+ }
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ Delete(parse_args);
+ Delete(cleanup);
+ Delete(outarg);
+ }
+
+ if (!ignored_method) {
+ Append(w->code, "PUTBACK;\n");
+ Append(w->code, "FREETMPS;\n");
+ Append(w->code, "LEAVE;\n");
+ }
+
+ if (!is_void) {
+ if (!(ignored_method && !pure_virtual)) {
+ String *rettype = SwigType_str(returntype, 0);
+ if (!SwigType_isreference(returntype)) {
+ Printf(w->code, "return (%s) c_result;\n", rettype);
+ } else {
+ Printf(w->code, "return (%s) *c_result;\n", rettype);
+ }
+ Delete(rettype);
+ }
+ }
+
+ Append(w->code, "}\n");
+
+ // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewString("");
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+
+ /* emit the director method */
+ if (status == SWIG_OK) {
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+
+ /* clean up */
+ Delete(wrap_args);
+ Delete(pclassname);
+ DelWrapper(w);
+ return status;
+ }
+ int classDirectorDisown(Node *n) {
+ int rv;
+ member_func = 1;
+ rv = Language::classDirectorDisown(n);
+ member_func = 0;
+ if (rv == SWIG_OK && Swig_directorclass(n)) {
+ String *symname = Getattr(n, "sym:name");
+ String *disown = Swig_name_disown(NSPACE_TODO, symname);
+ Setattr(n, "perl5:directordisown", NewStringf("%s::%s", cmodule, disown));
+ }
+ return rv;
+ }
+ int classDirectorDestructor(Node *n) {
+ /* TODO: it would be nice if this didn't have to copy the body of Language::classDirectorDestructor() */
+ String *DirectorClassName = directorClassName(getCurrentClass());
+ String *body = NewString("\n");
+
+ String *ptype = Copy(getClassType());
+ SwigType_add_pointer(ptype);
+ String *mangle = SwigType_manglestr(ptype);
+
+ Printv(body, tab4, "dSP;\n", tab4, "SV *self = SWIG_NewPointerObj(SWIG_as_voidptr(this), SWIGTYPE", mangle, ", SWIG_SHADOW);\n", tab4, "\n", tab4,
+ "sv_bless(self, gv_stashpv(swig_get_class(), 0));\n", tab4, "ENTER;\n", tab4, "SAVETMPS;\n", tab4, "PUSHMARK(SP);\n", tab4,
+ "XPUSHs(self);\n", tab4, "XPUSHs(&PL_sv_yes);\n", tab4, "PUTBACK;\n", tab4, "call_method(\"DESTROY\", G_EVAL | G_VOID);\n", tab4,
+ "FREETMPS;\n", tab4, "LEAVE;\n", NIL);
+
+ Delete(mangle);
+ Delete(ptype);
+
+ if (Getattr(n, "noexcept")) {
+ Printf(f_directors_h, " virtual ~%s() noexcept;\n", DirectorClassName);
+ Printf(f_directors, "%s::~%s() noexcept {%s}\n\n", DirectorClassName, DirectorClassName, body);
+ } else if (Getattr(n, "throw")) {
+ Printf(f_directors_h, " virtual ~%s() throw();\n", DirectorClassName);
+ Printf(f_directors, "%s::~%s() throw() {%s}\n\n", DirectorClassName, DirectorClassName, body);
+ } else {
+ Printf(f_directors_h, " virtual ~%s();\n", DirectorClassName);
+ Printf(f_directors, "%s::~%s() {%s}\n\n", DirectorClassName, DirectorClassName, body);
+ }
+ return SWIG_OK;
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * swig_perl5() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_perl5() {
+ return new PERL5();
+}
+extern "C" Language *swig_perl5(void) {
+ return new_swig_perl5();
+}
diff --git a/contrib/tools/swig/Source/Modules/php.cxx b/contrib/tools/swig/Source/Modules/php.cxx
new file mode 100644
index 0000000000..c8cc9212b1
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/php.cxx
@@ -0,0 +1,2702 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * php.cxx
+ *
+ * PHP language module for SWIG.
+ * -----------------------------------------------------------------------------
+ */
+
+#include "swigmod.h"
+#include <algorithm>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+
+static const char *usage = "\
+PHP Options (available with -php7)\n\
+ -prefix <prefix> - Prepend <prefix> to all class names in PHP wrappers\n\
+\n";
+
+// How to wrap non-class functions, variables and constants:
+// FIXME: Make this specifiable and also allow a real namespace.
+
+// Wrap as global PHP names.
+static bool wrap_nonclass_global = true;
+
+// Wrap in a class to fake a namespace (for compatibility with SWIG's behaviour
+// before PHP added namespaces.
+static bool wrap_nonclass_fake_class = true;
+
+static String *module = 0;
+static String *cap_module = 0;
+static String *prefix = 0;
+
+static File *f_begin = 0;
+static File *f_runtime = 0;
+static File *f_runtime_h = 0;
+static File *f_h = 0;
+static File *f_directors = 0;
+static File *f_directors_h = 0;
+
+static String *s_header;
+static String *s_wrappers;
+static String *s_init;
+static String *r_init; // RINIT user code
+static String *s_shutdown; // MSHUTDOWN user code
+static String *r_shutdown; // RSHUTDOWN user code
+static String *s_vdecl;
+static String *s_cinit; // consttab initialization code.
+static String *s_oinit;
+static String *s_arginfo;
+static String *s_entry;
+static String *cs_entry;
+static String *all_cs_entry;
+static String *fake_cs_entry;
+static String *s_creation;
+static String *pragma_incl;
+static String *pragma_code;
+static String *pragma_phpinfo;
+static String *pragma_version;
+
+static String *class_name = NULL;
+static String *base_class = NULL;
+static String *destructor_action = NULL;
+static String *magic_set = NULL;
+static String *magic_get = NULL;
+static String *magic_isset = NULL;
+
+// Class used as pseudo-namespace for compatibility.
+static String *fake_class_name() {
+ static String *result = NULL;
+ if (!result) {
+ result = Len(prefix) ? prefix : module;
+ if (!fake_cs_entry) {
+ fake_cs_entry = NewStringf("static const zend_function_entry class_%s_functions[] = {\n", result);
+ }
+
+ Printf(s_creation, "static zend_class_entry *SWIG_Php_ce_%s;\n\n",result);
+
+ Printf(s_oinit, " INIT_CLASS_ENTRY(internal_ce, \"%s\", class_%s_functions);\n", result, result);
+ Printf(s_oinit, " SWIG_Php_ce_%s = zend_register_internal_class(&internal_ce);\n", result);
+ Printf(s_oinit, "\n");
+ }
+ return result;
+}
+
+static String *swig_wrapped_interface_ce() {
+ static String *result = NULL;
+ if (!result) {
+ result = NewStringf("SWIG_Php_swig_wrapped_interface_ce");
+ Printf(s_oinit, " INIT_CLASS_ENTRY(%s, \"SWIG\\\\wrapped\", NULL);\n", result);
+ }
+ return result;
+}
+
+/* To reduce code size (generated and compiled) we only want to emit each
+ * different arginfo once, so we need to track which have been used.
+ */
+static Hash *arginfo_used;
+
+/* Track non-class pointer types we need to to wrap */
+static Hash *raw_pointer_types = 0;
+
+static int shadow = 1;
+
+// These static variables are used to pass some state from Handlers into functionWrapper
+static enum {
+ standard = 0,
+ memberfn,
+ staticmemberfn,
+ membervar,
+ staticmembervar,
+ constructor,
+ destructor,
+ directorconstructor,
+ directordisown
+} wrapperType = standard;
+
+extern "C" {
+ static void (*r_prevtracefunc) (const SwigType *t, String *mangled, String *clientdata) = 0;
+}
+
+static void SwigPHP_emit_pointer_type_registrations() {
+ if (!raw_pointer_types)
+ return;
+
+ Iterator ki = First(raw_pointer_types);
+ if (!ki.key)
+ return;
+
+ Printf(s_wrappers, "/* class object handlers for pointer wrappers */\n");
+ Printf(s_wrappers, "static zend_object_handlers swig_ptr_object_handlers;\n\n");
+
+ Printf(s_wrappers, "/* Object Creation Method for pointer wrapping class */\n");
+ Printf(s_wrappers, "static zend_object *swig_ptr_object_new(zend_class_entry *ce) {\n");
+ Printf(s_wrappers, " swig_object_wrapper *obj = (swig_object_wrapper*)zend_object_alloc(sizeof(swig_object_wrapper), ce);\n");
+ Printf(s_wrappers, " zend_object_std_init(&obj->std, ce);\n");
+ Printf(s_wrappers, " object_properties_init(&obj->std, ce);\n");
+ Printf(s_wrappers, " obj->std.handlers = &swig_ptr_object_handlers;\n");
+ Printf(s_wrappers, " obj->newobject = 0;\n");
+ Printf(s_wrappers, " return &obj->std;\n");
+ Printf(s_wrappers, "}\n\n");
+
+ Printf(s_wrappers, "/* Implement __toString equivalent, since that worked for the old-style resource wrapped pointers. */\n");
+ Append(s_wrappers, "#if PHP_MAJOR_VERSION < 8\n");
+ Printf(s_wrappers, "static int swig_ptr_cast_object(zval *z, zval *retval, int type) {\n");
+ Append(s_wrappers, "#elif PHP_MAJOR_VERSION > 8 || PHP_MINOR_VERSION >= 2\n");
+ Printf(s_wrappers, "static ZEND_RESULT_CODE swig_ptr_cast_object(zend_object *zobj, zval *retval, int type) {\n");
+ Append(s_wrappers, "#else\n");
+ Printf(s_wrappers, "static int swig_ptr_cast_object(zend_object *zobj, zval *retval, int type) {\n");
+ Append(s_wrappers, "#endif\n");
+ Printf(s_wrappers, " if (type == IS_STRING) {\n");
+ Append(s_wrappers, "#if PHP_MAJOR_VERSION < 8\n");
+ Printf(s_wrappers, " swig_object_wrapper *obj = SWIG_Z_FETCH_OBJ_P(z);\n");
+ Append(s_wrappers, "#else\n");
+ Printf(s_wrappers, " swig_object_wrapper *obj = swig_php_fetch_object(zobj);\n");
+ Append(s_wrappers, "#endif\n");
+ Printv(s_wrappers, " ZVAL_NEW_STR(retval, zend_strpprintf(0, \"SWIGPointer(%p,owned=%d)\", obj->ptr, obj->newobject));\n", NIL);
+ Printf(s_wrappers, " return SUCCESS;\n");
+ Printf(s_wrappers, " }\n");
+ Printf(s_wrappers, " return FAILURE;\n");
+ Printf(s_wrappers, "}\n\n");
+
+ Printf(s_oinit, "\n /* Register classes to represent non-class pointer types */\n");
+ Printf(s_oinit, " swig_ptr_object_handlers = *zend_get_std_object_handlers();\n");
+ Printf(s_oinit, " swig_ptr_object_handlers.offset = XtOffsetOf(swig_object_wrapper, std);\n");
+ Printf(s_oinit, " swig_ptr_object_handlers.cast_object = swig_ptr_cast_object;\n");
+
+ while (ki.key) {
+ String *type = ki.key;
+
+ String *swig_wrapped = swig_wrapped_interface_ce();
+ Printf(s_creation, "/* class entry for pointer to %s */\n", type);
+ Printf(s_creation, "static zend_class_entry *SWIG_Php_ce_%s;\n\n", type);
+
+ Printf(s_oinit, " INIT_CLASS_ENTRY(internal_ce, \"%s\\\\%s\", NULL);\n", "SWIG", type);
+ Printf(s_oinit, " SWIG_Php_ce_%s = zend_register_internal_class(&internal_ce);\n", type);
+ Printf(s_oinit, " SWIG_Php_ce_%s->create_object = swig_ptr_object_new;\n", type);
+ Printv(s_oinit, " zend_do_implement_interface(SWIG_Php_ce_", type, ", &", swig_wrapped, ");\n", NIL);
+ Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE%s,SWIG_Php_ce_%s);\n", type, type);
+ Printf(s_oinit, "\n");
+
+ ki = Next(ki);
+ }
+}
+
+static Hash *create_php_type_flags() {
+ Hash *h = NewHash();
+ Setattr(h, "array", "MAY_BE_ARRAY");
+ Setattr(h, "bool", "MAY_BE_BOOL");
+ Setattr(h, "callable", "MAY_BE_CALLABLE");
+ Setattr(h, "float", "MAY_BE_DOUBLE");
+ Setattr(h, "int", "MAY_BE_LONG");
+ Setattr(h, "iterable", "MAY_BE_ITERABLE");
+ Setattr(h, "mixed", "MAY_BE_MIXED");
+ Setattr(h, "null", "MAY_BE_NULL");
+ Setattr(h, "object", "MAY_BE_OBJECT");
+ Setattr(h, "resource", "MAY_BE_RESOURCE");
+ Setattr(h, "string", "MAY_BE_STRING");
+ Setattr(h, "void", "MAY_BE_VOID");
+ return h;
+}
+
+static Hash *php_type_flags = create_php_type_flags();
+
+// php_class + ":" + php_method -> PHPTypes*
+// ":" + php_function -> PHPTypes*
+static Hash *all_phptypes = NewHash();
+
+// php_class_name -> php_parent_class_name
+static Hash *php_parent_class = NewHash();
+
+// Track if a method is directed in a descendent class.
+// php_class + ":" + php_method -> boolean (using SetFlag()/GetFlag()).
+static Hash *has_directed_descendent = NewHash();
+
+// Track required return type for parent class methods.
+// php_class + ":" + php_method -> List of php types.
+static Hash *parent_class_method_return_type = NewHash();
+
+// Class encapsulating the machinery to add PHP type declarations.
+class PHPTypes {
+ // List with an entry for each parameter and one for the return type.
+ //
+ // We assemble the types in here before emitting them so for an overloaded
+ // function we combine the type declarations from each overloaded form.
+ List *merged_types;
+
+ // List with an entry for each parameter which is passed "byref" in any
+ // overloaded form. We use this to pass such parameters by reference in
+ // the dispatch function. If NULL, no parameters are passed by reference.
+ List *byref;
+
+ // The id string used in the name of the arginfo for this object.
+ String *arginfo_id;
+
+ // The feature:php:type value: 0, 1 or -1 for "compatibility".
+ int php_type_flag;
+
+ // Does the node for this have directorNode set?
+ bool has_director_node;
+
+ // Used to clamp the required number of parameters in the arginfo to be
+ // compatible with any parent class version of the method.
+ int num_required;
+
+ int get_byref(int key) const {
+ return byref && key < Len(byref) && Getitem(byref, key) != None;
+ }
+
+ int size() const {
+ return std::max(Len(merged_types), Len(byref));
+ }
+
+ String *get_phptype(int key, String *classtypes, List *more_return_types = NULL) {
+ Clear(classtypes);
+ // We want to minimise the list of class types by not redundantly listing
+ // a class for which a super-class is also listed. This canonicalisation
+ // allows for more sharing of arginfo (which reduces module size), makes
+ // for a cleaner list if it's shown to the user, and also will speed up
+ // module load a bit.
+ Hash *classes = NewHash();
+ DOH *types = Getitem(merged_types, key);
+ String *result = NewStringEmpty();
+ if (more_return_types) {
+ if (types != None) {
+ merge_type_lists(types, more_return_types);
+ }
+ }
+ if (types != None) {
+ SortList(types, NULL);
+ String *prev = NULL;
+ for (Iterator i = First(types); i.item; i = Next(i)) {
+ if (prev && Equal(prev, i.item)) {
+ // Skip duplicates when merging.
+ continue;
+ }
+ String *c = Getattr(php_type_flags, i.item);
+ if (c) {
+ if (Len(result) > 0) Append(result, "|");
+ Append(result, c);
+ } else {
+ SetFlag(classes, i.item);
+ }
+ prev = i.item;
+ }
+ }
+
+ // Remove entries for which a super-class is also listed.
+ Iterator i = First(classes);
+ while (i.key) {
+ String *this_class = i.key;
+ // We must advance the iterator early so we don't delete the element it
+ // points to.
+ i = Next(i);
+ String *parent = this_class;
+ while ((parent = Getattr(php_parent_class, parent)) != NULL) {
+ if (GetFlag(classes, parent)) {
+ Delattr(classes, this_class);
+ break;
+ }
+ }
+ }
+
+ List *sorted_classes = SortedKeys(classes, Strcmp);
+ for (i = First(sorted_classes); i.item; i = Next(i)) {
+ if (Len(classtypes) > 0) Append(classtypes, "|");
+ Append(classtypes, prefix);
+ Append(classtypes, i.item);
+ }
+ Delete(sorted_classes);
+
+ // Make the mask 0 if there are only class names specified.
+ if (Len(result) == 0) {
+ Append(result, "0");
+ }
+ return result;
+ }
+
+public:
+ PHPTypes(Node *n)
+ : merged_types(NewList()),
+ byref(NULL),
+ num_required(INT_MAX) {
+ String *php_type_feature = Getattr(n, "feature:php:type");
+ php_type_flag = 0;
+ if (php_type_feature != NULL) {
+ if (Equal(php_type_feature, "1")) {
+ php_type_flag = 1;
+ } else if (!Equal(php_type_feature, "0")) {
+ php_type_flag = -1;
+ }
+ }
+ arginfo_id = Copy(Getattr(n, "sym:name"));
+ has_director_node = (Getattr(n, "directorNode") != NULL);
+ }
+
+ ~PHPTypes() {
+ Delete(merged_types);
+ Delete(byref);
+ }
+
+ void adjust(int num_required_, bool php_constructor) {
+ num_required = std::min(num_required, num_required_);
+ if (php_constructor) {
+ // Don't add a return type declaration for a PHP __construct method
+ // (because there it has no return type as far as PHP is concerned).
+ php_type_flag = 0;
+ }
+ }
+
+ String *get_arginfo_id() const {
+ return arginfo_id;
+ }
+
+ // key is 0 for return type, or >= 1 for parameters numbered from 1
+ List *process_phptype(Node *n, int key, const String_or_char *attribute_name);
+
+ // Merge entries from o_merge_list into merge_list, skipping any entries
+ // already present.
+ //
+ // Both merge_list and o_merge_list should be in sorted order.
+ static void merge_type_lists(List *merge_list, List *o_merge_list);
+
+ void merge_from(const PHPTypes* o);
+
+ void set_byref(int key) {
+ if (!byref) {
+ byref = NewList();
+ }
+ while (Len(byref) <= key) {
+ Append(byref, None);
+ }
+ // If any overload takes a particular parameter by reference then the
+ // dispatch function also needs to take that parameter by reference so
+ // we can just set unconditionally here.
+ Setitem(byref, key, ""); // Just needs to be something != None.
+ }
+
+ void emit_arginfo(DOH *item, String *key) {
+ Setmark(item, 1);
+ char *colon_ptr = Strchr(key, ':');
+ assert(colon_ptr);
+ int colon = (int)(colon_ptr - Char(key));
+ if (colon > 0 && Strcmp(colon_ptr + 1, "__construct") != 0) {
+ // See if there's a parent class which implements this method, and if so
+ // emit its arginfo and then merge its PHPTypes into ours as we need to
+ // be compatible with it (whether it is virtual or not).
+ String *this_class = NewStringWithSize(Char(key), colon);
+ String *parent = this_class;
+ while ((parent = Getattr(php_parent_class, parent)) != NULL) {
+ String *k = NewStringf("%s%s", parent, colon_ptr);
+ DOH *item = Getattr(all_phptypes, k);
+ if (item) {
+ PHPTypes *p = (PHPTypes*)Data(item);
+ if (!Getmark(item)) {
+ p->emit_arginfo(item, k);
+ }
+ merge_from(p);
+ Delete(k);
+ break;
+ }
+ Delete(k);
+ }
+ Delete(this_class);
+ }
+
+ // We want to only emit each different arginfo once, as that reduces the
+ // size of both the generated source code and the compiled extension
+ // module. The parameters at this level are just named arg1, arg2, etc
+ // so the arginfo will be the same for any function with the same number
+ // of parameters and (if present) PHP type declarations for parameters and
+ // return type.
+ //
+ // We generate the arginfo we want (taking care to normalise, e.g. the
+ // lists of types are unique and in sorted order), then use the
+ // arginfo_used Hash to see if we've already generated it.
+ String *out_phptype = NULL;
+ String *out_phpclasses = NewStringEmpty();
+
+ // We provide a simple way to generate PHP return type declarations
+ // except for directed methods. The point of directors is to allow
+ // subclassing in the target language, and if the wrapped method has
+ // a return type declaration then an overriding method in user code
+ // needs to have a compatible declaration.
+ //
+ // The upshot of this is that enabling return type declarations for
+ // existing bindings would break compatibility with user code written
+ // for an older version. For parameters however the situation is
+ // different because if the parent class declares types for parameters
+ // a subclass overriding the function will be compatible whether it
+ // declares them or not.
+ //
+ // directorNode being present seems to indicate if this method or one
+ // it inherits from is directed, which is what we care about here.
+ // Using (!is_member_director(n)) would get it wrong for testcase
+ // director_frob.
+ if (php_type_flag && (php_type_flag > 0 || !has_director_node)) {
+ if (!GetFlag(has_directed_descendent, key)) {
+ out_phptype = get_phptype(0, out_phpclasses, Getattr(parent_class_method_return_type, key));
+ }
+ }
+
+ // ### in arginfo_code will be replaced with the id once that is known.
+ String *arginfo_code = NewStringEmpty();
+ if (out_phptype) {
+ if (Len(out_phpclasses)) {
+ Replace(out_phpclasses, "\\", "\\\\", DOH_REPLACE_ANY);
+ Printf(arginfo_code, "ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(swig_arginfo_###, 0, %d, %s, %s)\n", num_required, out_phpclasses, out_phptype);
+ } else {
+ Printf(arginfo_code, "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(swig_arginfo_###, 0, %d, %s)\n", num_required, out_phptype);
+ }
+ } else {
+ Printf(arginfo_code, "ZEND_BEGIN_ARG_INFO_EX(swig_arginfo_###, 0, 0, %d)\n", num_required);
+ }
+
+ int phptypes_size = size();
+ for (int param_count = 1; param_count < phptypes_size; ++param_count) {
+ String *phpclasses = NewStringEmpty();
+ String *phptype = get_phptype(param_count, phpclasses);
+
+ int byref = get_byref(param_count);
+
+ // FIXME: Should we be doing byref for return value as well?
+
+ if (phptype) {
+ if (Len(phpclasses)) {
+ // We need to double any backslashes (which are PHP namespace
+ // separators) in the PHP class names as they get turned into
+ // C strings by the ZEND_ARG_OBJ_TYPE_MASK macro.
+ Replace(phpclasses, "\\", "\\\\", DOH_REPLACE_ANY);
+ Printf(arginfo_code, " ZEND_ARG_OBJ_TYPE_MASK(%d,arg%d,%s,%s,NULL)\n", byref, param_count, phpclasses, phptype);
+ } else {
+ Printf(arginfo_code, " ZEND_ARG_TYPE_MASK(%d,arg%d,%s,NULL)\n", byref, param_count, phptype);
+ }
+ } else {
+ Printf(arginfo_code, " ZEND_ARG_INFO(%d,arg%d)\n", byref, param_count);
+ }
+ }
+ Printf(arginfo_code, "ZEND_END_ARG_INFO()\n");
+
+ String *arginfo_id_same = Getattr(arginfo_used, arginfo_code);
+ if (arginfo_id_same) {
+ Printf(s_arginfo, "#define swig_arginfo_%s swig_arginfo_%s\n", arginfo_id, arginfo_id_same);
+ } else {
+ // Not had this arginfo before.
+ Setattr(arginfo_used, arginfo_code, arginfo_id);
+ arginfo_code = Copy(arginfo_code);
+ Replace(arginfo_code, "###", arginfo_id, DOH_REPLACE_FIRST);
+ Append(s_arginfo, arginfo_code);
+ }
+ Delete(arginfo_code);
+ arginfo_code = NULL;
+ }
+};
+
+static PHPTypes *phptypes = NULL;
+
+class PHP : public Language {
+public:
+ PHP() {
+ director_language = 1;
+ }
+
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+ SWIG_library_directory("php");
+
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-prefix") == 0) {
+ if (argv[i + 1]) {
+ prefix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if ((strcmp(argv[i], "-noshadow") == 0)) {
+ shadow = 0;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage, stdout);
+ }
+ }
+
+ Preprocessor_define("SWIGPHP 1", 0);
+ Preprocessor_define("SWIGPHP7 1", 0);
+ SWIG_typemap_lang("php");
+ SWIG_config_file("php.swg");
+ allow_overloading();
+ }
+
+ /* ------------------------------------------------------------
+ * top()
+ * ------------------------------------------------------------ */
+
+ virtual int top(Node *n) {
+
+ String *filen;
+
+ /* Check if directors are enabled for this module. */
+ Node *mod = Getattr(n, "module");
+ if (mod) {
+ Node *options = Getattr(mod, "options");
+ if (options && Getattr(options, "directors")) {
+ allow_directors();
+ }
+ }
+
+ /* Set comparison with null for ConstructorToFunction */
+ setSubclassInstanceCheck(NewString("Z_TYPE_P($arg) != IS_NULL"));
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+ String *outfile_h = Getattr(n, "outfile_h");
+
+ /* main output file */
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewStringEmpty();
+
+ /* sections of the output file */
+ s_init = NewStringEmpty();
+ r_init = NewStringEmpty();
+ s_shutdown = NewStringEmpty();
+ r_shutdown = NewStringEmpty();
+ s_header = NewString("/* header section */\n");
+ s_wrappers = NewString("/* wrapper section */\n");
+ s_creation = NewStringEmpty();
+ /* subsections of the init section */
+ s_vdecl = NewString("/* vdecl subsection */\n");
+ s_cinit = NewString(" /* cinit subsection */\n");
+ s_oinit = NewString(" /* oinit subsection */\n");
+ pragma_phpinfo = NewStringEmpty();
+ f_directors_h = NewStringEmpty();
+ f_directors = NewStringEmpty();
+
+ if (directorsEnabled()) {
+ f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
+ if (!f_runtime_h) {
+ FileErrorDisplay(outfile_h);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", s_init);
+ Swig_register_filebyname("rinit", r_init);
+ Swig_register_filebyname("shutdown", s_shutdown);
+ Swig_register_filebyname("rshutdown", r_shutdown);
+ Swig_register_filebyname("header", s_header);
+ Swig_register_filebyname("wrapper", s_wrappers);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "PHP");
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+ }
+
+ // We need to include php.h before string.h gets included, at least with
+ // PHP 8.2. Otherwise string.h is included without _GNU_SOURCE being
+ // included and memrchr() doesn't get declared, and then inline code in
+ // the PHP headers defines _GNU_SOURCE, includes string.h (which is a
+ // no op thanks to the include gaurds), then tries to use memrchr() and
+ // fails.
+ //
+ // We also need to suppress -Wdeclaration-after-statement if enabled
+ // since with PHP 8.2 zend_operators.h contains inline code which triggers
+ // this warning and our testsuite uses with option and -Werror. I don't
+ // see a good way to only do this within our testsuite, but disabling
+ // it globally like this shouldn't be problematic.
+ Append(f_runtime,
+ "\n"
+ "#if defined __GNUC__ && !defined __cplusplus\n"
+ "# if __GNUC__ >= 4\n"
+ "# pragma GCC diagnostic push\n"
+ "# pragma GCC diagnostic ignored \"-Wdeclaration-after-statement\"\n"
+ "# endif\n"
+ "#endif\n"
+ "#include \"php.h\"\n"
+ "#if defined __GNUC__ && !defined __cplusplus\n"
+ "# if __GNUC__ >= 4\n"
+ "# pragma GCC diagnostic pop\n"
+ "# endif\n"
+ "#endif\n\n");
+
+ /* Set the module name */
+ module = Copy(Getattr(n, "name"));
+ cap_module = NewStringf("%(upper)s", module);
+ if (!prefix)
+ prefix = NewStringEmpty();
+
+ if (directorsEnabled()) {
+ Swig_banner(f_directors_h);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", cap_module);
+ Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", cap_module);
+
+ String *filename = Swig_file_filename(outfile_h);
+ Printf(f_directors, "\n#include \"%s\"\n\n", filename);
+ Delete(filename);
+ }
+
+ /* sub-sections of the php file */
+ pragma_code = NewStringEmpty();
+ pragma_incl = NewStringEmpty();
+ pragma_version = NULL;
+
+ /* Initialize the rest of the module */
+
+ /* start the header section */
+ Printf(s_header, "#define SWIG_name \"%s\"\n", module);
+ Printf(s_header, "#ifdef __cplusplus\n");
+ Printf(s_header, "extern \"C\" {\n");
+ Printf(s_header, "#endif\n");
+ Printf(s_header, "#include \"php_ini.h\"\n");
+ Printf(s_header, "#include \"ext/standard/info.h\"\n");
+ Printf(s_header, "#include \"php_%s.h\"\n", module);
+ Printf(s_header, "#ifdef __cplusplus\n");
+ Printf(s_header, "}\n");
+ Printf(s_header, "#endif\n\n");
+
+ if (directorsEnabled()) {
+ // Insert director runtime
+ Swig_insert_file("director_common.swg", s_header);
+ Swig_insert_file("director.swg", s_header);
+ }
+
+ /* Create the .h file too */
+ filen = NewStringEmpty();
+ Printv(filen, SWIG_output_directory(), "php_", module, ".h", NIL);
+ f_h = NewFile(filen, "w", SWIG_output_files());
+ if (!f_h) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+
+ Swig_banner(f_h);
+
+ Printf(f_h, "\n");
+ Printf(f_h, "#ifndef PHP_%s_H\n", cap_module);
+ Printf(f_h, "#define PHP_%s_H\n\n", cap_module);
+ Printf(f_h, "extern zend_module_entry %s_module_entry;\n", module);
+ Printf(f_h, "#define phpext_%s_ptr &%s_module_entry\n\n", module, module);
+ Printf(f_h, "#ifdef PHP_WIN32\n");
+ Printf(f_h, "# define PHP_%s_API __declspec(dllexport)\n", cap_module);
+ Printf(f_h, "#else\n");
+ Printf(f_h, "# define PHP_%s_API\n", cap_module);
+ Printf(f_h, "#endif\n\n");
+
+ /* start the arginfo section */
+ s_arginfo = NewString("/* arginfo subsection */\n");
+ arginfo_used = NewHash();
+
+ /* start the function entry section */
+ s_entry = NewString("/* entry subsection */\n");
+
+ /* holds all the per-class function entry sections */
+ all_cs_entry = NewString("/* class entry subsection */\n");
+ cs_entry = NULL;
+ fake_cs_entry = NULL;
+
+ Printf(s_entry, "/* Every non-class user visible function must have an entry here */\n");
+ Printf(s_entry, "static const zend_function_entry module_%s_functions[] = {\n", module);
+
+ /* Emit all of the code */
+ Language::top(n);
+
+ /* Emit all the arginfo. We sort the keys so the output order doesn't depend on
+ * hashkey order.
+ */
+ {
+ List *sorted_keys = SortedKeys(all_phptypes, Strcmp);
+ for (Iterator k = First(sorted_keys); k.item; k = Next(k)) {
+ DOH *val = Getattr(all_phptypes, k.item);
+ if (!Getmark(val)) {
+ PHPTypes *p = (PHPTypes*)Data(val);
+ p->emit_arginfo(val, k.item);
+ }
+ }
+ Delete(sorted_keys);
+ }
+
+ SwigPHP_emit_pointer_type_registrations();
+ Dump(s_creation, s_header);
+ Delete(s_creation);
+ s_creation = NULL;
+
+ /* start the init section */
+ {
+ String *s_init_old = s_init;
+ s_init = NewString("/* init section */\n");
+ Printv(s_init, "zend_module_entry ", module, "_module_entry = {\n", NIL);
+ Printf(s_init, " STANDARD_MODULE_HEADER,\n");
+ Printf(s_init, " \"%s\",\n", module);
+ Printf(s_init, " module_%s_functions,\n", module);
+ Printf(s_init, " PHP_MINIT(%s),\n", module);
+ if (Len(s_shutdown) > 0) {
+ Printf(s_init, " PHP_MSHUTDOWN(%s),\n", module);
+ } else {
+ Printf(s_init, " NULL, /* No MSHUTDOWN code */\n");
+ }
+ if (Len(r_init) > 0) {
+ Printf(s_init, " PHP_RINIT(%s),\n", module);
+ } else {
+ Printf(s_init, " NULL, /* No RINIT code */\n");
+ }
+ if (Len(r_shutdown) > 0) {
+ Printf(s_init, " PHP_RSHUTDOWN(%s),\n", module);
+ } else {
+ Printf(s_init, " NULL, /* No RSHUTDOWN code */\n");
+ }
+ if (Len(pragma_phpinfo) > 0) {
+ Printf(s_init, " PHP_MINFO(%s),\n", module);
+ } else {
+ Printf(s_init, " NULL, /* No MINFO code */\n");
+ }
+ if (Len(pragma_version) > 0) {
+ Printf(s_init, " \"%s\",\n", pragma_version);
+ } else {
+ Printf(s_init, " NO_VERSION_YET,\n");
+ }
+ Printf(s_init, " STANDARD_MODULE_PROPERTIES\n");
+ Printf(s_init, "};\n\n");
+
+ Printf(s_init, "#ifdef __cplusplus\n");
+ Printf(s_init, "extern \"C\" {\n");
+ Printf(s_init, "#endif\n");
+ // We want to write "SWIGEXPORT ZEND_GET_MODULE(%s)" but ZEND_GET_MODULE
+ // in PHP7 has "extern "C" { ... }" around it so we can't do that.
+ Printf(s_init, "SWIGEXPORT zend_module_entry *get_module(void) { return &%s_module_entry; }\n", module);
+ Printf(s_init, "#ifdef __cplusplus\n");
+ Printf(s_init, "}\n");
+ Printf(s_init, "#endif\n\n");
+
+ Printf(s_init, "#define SWIG_php_minit PHP_MINIT_FUNCTION(%s)\n\n", module);
+
+ Printv(s_init, s_init_old, NIL);
+ Delete(s_init_old);
+ }
+
+ /* We have to register the constants before they are (possibly) used
+ * by the pointer typemaps. This all needs re-arranging really as
+ * things are being called in the wrong order
+ */
+
+ Printf(s_oinit, " /* end oinit subsection */\n");
+ Printf(s_init, "%s\n", s_oinit);
+
+ /* Constants generated during top call */
+ Printf(s_cinit, " /* end cinit subsection */\n");
+ Printf(s_init, "%s\n", s_cinit);
+ Clear(s_cinit);
+ Delete(s_cinit);
+
+ Printf(s_init, " return SUCCESS;\n");
+ Printf(s_init, "}\n\n");
+
+ // Now do REQUEST init which holds any user specified %rinit, and also vinit
+ if (Len(r_init) > 0) {
+ Printf(f_h, "PHP_RINIT_FUNCTION(%s);\n", module);
+
+ Printf(s_init, "PHP_RINIT_FUNCTION(%s)\n{\n", module);
+ Printv(s_init,
+ "/* rinit section */\n",
+ r_init, "\n",
+ NIL);
+
+ Printf(s_init, " return SUCCESS;\n");
+ Printf(s_init, "}\n\n");
+ }
+
+ Printf(f_h, "PHP_MINIT_FUNCTION(%s);\n", module);
+
+ if (Len(s_shutdown) > 0) {
+ Printf(f_h, "PHP_MSHUTDOWN_FUNCTION(%s);\n", module);
+
+ Printv(s_init, "PHP_MSHUTDOWN_FUNCTION(", module, ")\n"
+ "/* shutdown section */\n"
+ "{\n",
+ s_shutdown,
+ " return SUCCESS;\n"
+ "}\n\n", NIL);
+ }
+
+ if (Len(r_shutdown) > 0) {
+ Printf(f_h, "PHP_RSHUTDOWN_FUNCTION(%s);\n", module);
+
+ Printf(s_init, "PHP_RSHUTDOWN_FUNCTION(%s)\n{\n", module);
+ Printf(s_init, "/* rshutdown section */\n");
+ Printf(s_init, "%s\n", r_shutdown);
+ Printf(s_init, " return SUCCESS;\n");
+ Printf(s_init, "}\n\n");
+ }
+
+ if (Len(pragma_phpinfo) > 0) {
+ Printf(f_h, "PHP_MINFO_FUNCTION(%s);\n", module);
+
+ Printf(s_init, "PHP_MINFO_FUNCTION(%s)\n{\n", module);
+ Printf(s_init, "%s", pragma_phpinfo);
+ Printf(s_init, "}\n");
+ }
+
+ Printf(s_init, "/* end init section */\n");
+
+ Printf(f_h, "\n#endif /* PHP_%s_H */\n", cap_module);
+
+ Delete(f_h);
+
+ String *type_table = NewStringEmpty();
+ SwigType_emit_type_table(f_runtime, type_table);
+ Printf(s_header, "%s", type_table);
+ Delete(type_table);
+
+ /* Oh dear, more things being called in the wrong order. This whole
+ * function really needs totally redoing.
+ */
+
+ if (directorsEnabled()) {
+ Dump(f_directors_h, f_runtime_h);
+ Printf(f_runtime_h, "\n");
+ Printf(f_runtime_h, "#endif\n");
+ Delete(f_runtime_h);
+ }
+
+ Printf(s_header, "/* end header section */\n");
+ Printf(s_wrappers, "/* end wrapper section */\n");
+ Printf(s_vdecl, "/* end vdecl subsection */\n");
+
+ Dump(f_runtime, f_begin);
+ Printv(f_begin, s_header, NIL);
+ if (directorsEnabled()) {
+ Dump(f_directors, f_begin);
+ }
+ Printv(f_begin, s_vdecl, s_wrappers, NIL);
+ Printv(f_begin, s_arginfo, "\n\n", all_cs_entry, "\n\n", s_entry,
+ " ZEND_FE_END\n};\n\n", NIL);
+ if (fake_cs_entry) {
+ Printv(f_begin, fake_cs_entry, " ZEND_FE_END\n};\n\n", NIL);
+ Delete(fake_cs_entry);
+ fake_cs_entry = NULL;
+ }
+ Printv(f_begin, s_init, NIL);
+ Delete(s_header);
+ Delete(s_wrappers);
+ Delete(s_init);
+ Delete(s_vdecl);
+ Delete(all_cs_entry);
+ Delete(s_entry);
+ Delete(s_arginfo);
+ Delete(f_runtime);
+ Delete(f_begin);
+ Delete(arginfo_used);
+
+ if (Len(pragma_incl) > 0 || Len(pragma_code) > 0) {
+ /* PHP module file */
+ String *php_filename = NewStringEmpty();
+ Printv(php_filename, SWIG_output_directory(), module, ".php", NIL);
+
+ File *f_phpcode = NewFile(php_filename, "w", SWIG_output_files());
+ if (!f_phpcode) {
+ FileErrorDisplay(php_filename);
+ Exit(EXIT_FAILURE);
+ }
+
+ Printf(f_phpcode, "<?php\n\n");
+
+ if (Len(pragma_incl) > 0) {
+ Printv(f_phpcode, pragma_incl, "\n", NIL);
+ }
+
+ if (Len(pragma_code) > 0) {
+ Printv(f_phpcode, pragma_code, "\n", NIL);
+ }
+
+ Delete(f_phpcode);
+ Delete(php_filename);
+ }
+
+ return SWIG_OK;
+ }
+
+ /* Just need to append function names to function table to register with PHP. */
+ void create_command(String *cname, String *fname, Node *n, bool dispatch, String *modes = NULL) {
+ // This is for the single main zend_function_entry record
+ ParmList *l = Getattr(n, "parms");
+ if (cname && !Equal(Getattr(n, "storage"), "friend")) {
+ Printf(f_h, "static PHP_METHOD(%s%s,%s);\n", prefix, cname, fname);
+ if (wrapperType != staticmemberfn &&
+ wrapperType != staticmembervar &&
+ !Equal(fname, "__construct")) {
+ // Skip the first entry in the parameter list which is the this pointer.
+ if (l) l = Getattr(l, "tmap:in:next");
+ // FIXME: does this throw the phptype key value off?
+ }
+ } else {
+ if (dispatch) {
+ Printf(f_h, "static ZEND_NAMED_FUNCTION(%s);\n", fname);
+ } else {
+ Printf(f_h, "static PHP_FUNCTION(%s);\n", fname);
+ }
+ }
+
+ phptypes->adjust(emit_num_required(l), Equal(fname, "__construct") ? true : false);
+
+ String *arginfo_id = phptypes->get_arginfo_id();
+ String *s = cs_entry;
+ if (!s) s = s_entry;
+ if (cname && !Equal(Getattr(n, "storage"), "friend")) {
+ Printf(all_cs_entry, " PHP_ME(%s%s,%s,swig_arginfo_%s,%s)\n", prefix, cname, fname, arginfo_id, modes);
+ } else {
+ if (dispatch) {
+ if (wrap_nonclass_global) {
+ Printf(s, " ZEND_NAMED_FE(%(lower)s,%s,swig_arginfo_%s)\n", Getattr(n, "sym:name"), fname, arginfo_id);
+ }
+
+ if (wrap_nonclass_fake_class) {
+ (void)fake_class_name();
+ Printf(fake_cs_entry, " ZEND_NAMED_ME(%(lower)s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", Getattr(n, "sym:name"), fname, arginfo_id);
+ }
+ } else {
+ if (wrap_nonclass_global) {
+ Printf(s, " PHP_FE(%s,swig_arginfo_%s)\n", fname, arginfo_id);
+ }
+
+ if (wrap_nonclass_fake_class) {
+ String *fake_class = fake_class_name();
+ Printf(fake_cs_entry, " PHP_ME(%s,%s,swig_arginfo_%s,ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)\n", fake_class, fname, arginfo_id);
+ }
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * dispatchFunction()
+ * ------------------------------------------------------------ */
+ void dispatchFunction(Node *n, int constructor) {
+ /* Last node in overloaded chain */
+
+ int maxargs;
+ String *tmp = NewStringEmpty();
+ String *dispatch = Swig_overload_dispatch(n, "%s(INTERNAL_FUNCTION_PARAM_PASSTHRU); return;", &maxargs);
+
+ /* Generate a dispatch wrapper for all overloaded functions */
+
+ Wrapper *f = NewWrapper();
+ String *symname = Getattr(n, "sym:name");
+ String *wname = NULL;
+ String *modes = NULL;
+ bool constructorRenameOverload = false;
+
+ if (constructor) {
+ if (!Equal(class_name, Getattr(n, "constructorHandler:sym:name"))) {
+ // Renamed constructor - turn into static factory method
+ constructorRenameOverload = true;
+ wname = Copy(Getattr(n, "constructorHandler:sym:name"));
+ } else {
+ wname = NewString("__construct");
+ }
+ } else if (class_name) {
+ wname = Getattr(n, "wrapper:method:name");
+ } else {
+ wname = Swig_name_wrapper(symname);
+ }
+
+ if (constructor) {
+ modes = NewString("ZEND_ACC_PUBLIC | ZEND_ACC_CTOR");
+ if (constructorRenameOverload) {
+ Append(modes, " | ZEND_ACC_STATIC");
+ }
+ } else if (wrapperType == staticmemberfn || Cmp(Getattr(n, "storage"), "static") == 0) {
+ modes = NewString("ZEND_ACC_PUBLIC | ZEND_ACC_STATIC");
+ } else {
+ modes = NewString("ZEND_ACC_PUBLIC");
+ }
+
+ create_command(class_name, wname, n, true, modes);
+
+ if (class_name && !Equal(Getattr(n, "storage"), "friend")) {
+ Printv(f->def, "static PHP_METHOD(", prefix, class_name, ",", wname, ") {\n", NIL);
+ } else {
+ Printv(f->def, "static ZEND_NAMED_FUNCTION(", wname, ") {\n", NIL);
+ }
+
+ Wrapper_add_local(f, "argc", "int argc");
+
+ Printf(tmp, "zval argv[%d]", maxargs);
+ Wrapper_add_local(f, "argv", tmp);
+
+ Printf(f->code, "argc = ZEND_NUM_ARGS();\n");
+
+ Printf(f->code, "zend_get_parameters_array_ex(argc, argv);\n");
+
+ Replaceall(dispatch, "$args", "self,args");
+
+ Printv(f->code, dispatch, "\n", NIL);
+
+ Printf(f->code, "zend_throw_exception(zend_ce_type_error, \"No matching function for overloaded '%s'\", 0);\n", symname);
+ Printv(f->code, "fail:\n", NIL);
+ Printv(f->code, "return;\n", NIL);
+ Printv(f->code, "}\n", NIL);
+ Wrapper_print(f, s_wrappers);
+
+ DelWrapper(f);
+ Delete(dispatch);
+ Delete(tmp);
+ }
+
+ /* ------------------------------------------------------------
+ * functionWrapper()
+ * ------------------------------------------------------------ */
+
+ /* Helper function to check if class is wrapped */
+ bool is_class_wrapped(String *className) {
+ if (!className)
+ return false;
+ Node *n = symbolLookup(className);
+ return n && Getattr(n, "classtype") != NULL;
+ }
+
+ void generate_magic_property_methods(Node *class_node) {
+ String *swig_base = base_class;
+ if (Equal(swig_base, "Exception") || !is_class_wrapped(swig_base)) {
+ swig_base = NULL;
+ }
+
+ static bool generated_magic_arginfo = false;
+ if (!generated_magic_arginfo) {
+ // Create arginfo entries for __get, __set and __isset.
+ Append(s_arginfo,
+ "ZEND_BEGIN_ARG_INFO_EX(swig_magic_arginfo_get, 0, 0, 1)\n"
+ " ZEND_ARG_TYPE_MASK(0,arg1,MAY_BE_STRING,NULL)\n"
+ "ZEND_END_ARG_INFO()\n");
+ Append(s_arginfo,
+ "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(swig_magic_arginfo_set, 0, 1, MAY_BE_VOID)\n"
+ " ZEND_ARG_TYPE_MASK(0,arg1,MAY_BE_STRING,NULL)\n"
+ " ZEND_ARG_INFO(0,arg2)\n"
+ "ZEND_END_ARG_INFO()\n");
+ Append(s_arginfo,
+ "ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(swig_magic_arginfo_isset, 0, 1, MAY_BE_BOOL)\n"
+ " ZEND_ARG_TYPE_MASK(0,arg1,MAY_BE_STRING,NULL)\n"
+ "ZEND_END_ARG_INFO()\n");
+ generated_magic_arginfo = true;
+ }
+
+ Wrapper *f = NewWrapper();
+
+ Printf(f_h, "PHP_METHOD(%s%s,__set);\n", prefix, class_name);
+ Printf(all_cs_entry, " PHP_ME(%s%s,__set,swig_magic_arginfo_set,ZEND_ACC_PUBLIC)\n", prefix, class_name);
+ Printf(f->code, "PHP_METHOD(%s%s,__set) {\n", prefix, class_name);
+
+ Printf(f->code, " swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n");
+ Printf(f->code, " zval args[2];\n zval tempZval;\n zend_string *arg2 = 0;\n\n");
+ Printf(f->code, " if(ZEND_NUM_ARGS() != 2 || zend_get_parameters_array_ex(2, args) != SUCCESS) {\n");
+ Printf(f->code, "\tWRONG_PARAM_COUNT;\n}\n\n");
+ Printf(f->code, " if (!arg) {\n");
+ Printf(f->code, " zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
+ Printf(f->code, " return;\n");
+ Printf(f->code, " }\n");
+ Printf(f->code, " arg2 = Z_STR(args[0]);\n\n");
+
+ Printf(f->code, "if (!arg2) {\n RETVAL_NULL();\n}\n");
+ if (magic_set) {
+ Append(f->code, magic_set);
+ }
+ Printf(f->code, "\nelse if (strcmp(ZSTR_VAL(arg2),\"thisown\") == 0) {\n");
+ Printf(f->code, "arg->newobject = zval_get_long(&args[1]);\n");
+ if (Swig_directorclass(class_node)) {
+ Printv(f->code, "if (arg->newobject == 0) {\n",
+ " Swig::Director *director = SWIG_DIRECTOR_CAST((", Getattr(class_node, "classtype"), "*)(arg->ptr));\n",
+ " if (director) director->swig_disown();\n",
+ "}\n", NIL);
+ }
+ if (swig_base) {
+ Printf(f->code, "} else {\nPHP_MN(%s%s___set)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n", prefix, swig_base);
+ } else if (Getattr(class_node, "feature:php:allowdynamicproperties")) {
+ Printf(f->code, "} else {\nadd_property_zval_ex(ZEND_THIS, ZSTR_VAL(arg2), ZSTR_LEN(arg2), &args[1]);\n");
+ }
+ Printf(f->code, "}\n");
+
+ Printf(f->code, "fail:\n");
+ Printf(f->code, "return;\n");
+ Printf(f->code, "}\n\n\n");
+
+
+ Printf(f_h, "PHP_METHOD(%s%s,__get);\n", prefix, class_name);
+ Printf(all_cs_entry, " PHP_ME(%s%s,__get,swig_magic_arginfo_get,ZEND_ACC_PUBLIC)\n", prefix, class_name);
+ Printf(f->code, "PHP_METHOD(%s%s,__get) {\n",prefix, class_name);
+
+ Printf(f->code, " swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n");
+ Printf(f->code, " zval args[1];\n zval tempZval;\n zend_string *arg2 = 0;\n\n");
+ Printf(f->code, " if(ZEND_NUM_ARGS() != 1 || zend_get_parameters_array_ex(1, args) != SUCCESS) {\n");
+ Printf(f->code, "\tWRONG_PARAM_COUNT;\n}\n\n");
+ Printf(f->code, " if (!arg) {\n");
+ Printf(f->code, " zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
+ Printf(f->code, " return;\n");
+ Printf(f->code, " }\n");
+ Printf(f->code, " arg2 = Z_STR(args[0]);\n\n");
+
+ Printf(f->code, "if (!arg2) {\n RETVAL_NULL();\n}\n");
+ if (magic_get) {
+ Append(f->code, magic_get);
+ }
+ Printf(f->code, "\nelse if (strcmp(ZSTR_VAL(arg2),\"thisown\") == 0) {\n");
+ Printf(f->code, "if(arg->newobject) {\nRETVAL_LONG(1);\n}\nelse {\nRETVAL_LONG(0);\n}\n}\n\n");
+ Printf(f->code, "else {\n");
+ if (swig_base) {
+ Printf(f->code, "PHP_MN(%s%s___get)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n", prefix, swig_base);
+ } else {
+ // __get is only called if the property isn't set on the zend_object.
+ Printf(f->code, "RETVAL_NULL();\n}\n");
+ }
+
+ Printf(f->code, "fail:\n");
+ Printf(f->code, "return;\n");
+ Printf(f->code, "}\n\n\n");
+
+
+ Printf(f_h, "PHP_METHOD(%s%s,__isset);\n", prefix, class_name);
+ Printf(all_cs_entry, " PHP_ME(%s%s,__isset,swig_magic_arginfo_isset,ZEND_ACC_PUBLIC)\n", prefix, class_name);
+ Printf(f->code, "PHP_METHOD(%s%s,__isset) {\n",prefix, class_name);
+
+ Printf(f->code, " swig_object_wrapper *arg = SWIG_Z_FETCH_OBJ_P(ZEND_THIS);\n");
+ Printf(f->code, " zval args[1];\n zend_string *arg2 = 0;\n\n");
+ Printf(f->code, " if(ZEND_NUM_ARGS() != 1 || zend_get_parameters_array_ex(1, args) != SUCCESS) {\n");
+ Printf(f->code, "\tWRONG_PARAM_COUNT;\n}\n\n");
+ Printf(f->code, " if(!arg) {\n");
+ Printf(f->code, " zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
+ Printf(f->code, " return;\n");
+ Printf(f->code, " }\n");
+ Printf(f->code, " arg2 = Z_STR(args[0]);\n\n");
+
+ Printf(f->code, "if (!arg2) {\n RETVAL_FALSE;\n}\n");
+ Printf(f->code, "\nelse if (strcmp(ZSTR_VAL(arg2),\"thisown\") == 0) {\n");
+ Printf(f->code, "RETVAL_TRUE;\n}\n\n");
+ if (magic_isset) {
+ Append(f->code, magic_isset);
+ }
+ Printf(f->code, "else {\n");
+ if (swig_base) {
+ Printf(f->code, "PHP_MN(%s%s___isset)(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n}\n", prefix, swig_base);
+ } else {
+ // __isset is only called if the property isn't set on the zend_object.
+ Printf(f->code, "RETVAL_FALSE;\n}\n");
+ }
+
+ Printf(f->code, "fail:\n");
+ Printf(f->code, "return;\n");
+ Printf(f->code, "}\n\n\n");
+
+ Wrapper_print(f, s_wrappers);
+ DelWrapper(f);
+ f = NULL;
+
+ Delete(magic_set);
+ Delete(magic_get);
+ Delete(magic_isset);
+ magic_set = NULL;
+ magic_get = NULL;
+ magic_isset = NULL;
+ }
+
+ String *getAccessMode(String *access) {
+ if (Cmp(access, "protected") == 0) {
+ return NewString("ZEND_ACC_PROTECTED");
+ } else if (Cmp(access, "private") == 0) {
+ return NewString("ZEND_ACC_PRIVATE");
+ }
+ return NewString("ZEND_ACC_PUBLIC");
+ }
+
+ bool is_setter_method(Node *n) {
+ const char *p = GetChar(n, "sym:name");
+ if (strlen(p) > 4) {
+ p += strlen(p) - 4;
+ if (strcmp(p, "_set") == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool is_getter_method(Node *n) {
+ const char *p = GetChar(n, "sym:name");
+ if (strlen(p) > 4) {
+ p += strlen(p) - 4;
+ if (strcmp(p, "_get") == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual int functionWrapper(Node *n) {
+ if (wrapperType == directordisown) {
+ // Handled via __set magic method - no explicit wrapper method wanted.
+ return SWIG_OK;
+ }
+ String *name = GetChar(n, "name");
+ String *iname = GetChar(n, "sym:name");
+ SwigType *d = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ String *nodeType = Getattr(n, "nodeType");
+ int newobject = GetFlag(n, "feature:new");
+ int constructor = (Cmp(nodeType, "constructor") == 0);
+
+ Parm *p;
+ int i;
+ int numopt;
+ String *tm;
+ Wrapper *f;
+
+ String *wname = NewStringEmpty();
+ String *overloadwname = NULL;
+ int overloaded = 0;
+ String *modes = NULL;
+ bool static_setter = false;
+ bool static_getter = false;
+
+ modes = getAccessMode(Getattr(n, "access"));
+
+ if (constructor) {
+ Append(modes, " | ZEND_ACC_CTOR");
+ }
+ if (wrapperType == staticmemberfn || Cmp(Getattr(n, "storage"), "static") == 0) {
+ Append(modes, " | ZEND_ACC_STATIC");
+ }
+ if (GetFlag(n, "abstract") && Swig_directorclass(Swig_methodclass(n)) && !is_member_director(n))
+ Append(modes, " | ZEND_ACC_ABSTRACT");
+
+ if (Getattr(n, "sym:overloaded")) {
+ overloaded = 1;
+ overloadwname = NewString(Swig_name_wrapper(iname));
+ Printf(overloadwname, "%s", Getattr(n, "sym:overname"));
+ } else {
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+ }
+
+ if (constructor) {
+ if (!Equal(class_name, Getattr(n, "constructorHandler:sym:name"))) {
+ // Renamed constructor - turn into static factory method
+ wname = Copy(Getattr(n, "constructorHandler:sym:name"));
+ } else {
+ wname = NewString("__construct");
+ }
+ } else if (wrapperType == membervar) {
+ wname = Copy(Getattr(n, "membervariableHandler:sym:name"));
+ if (is_setter_method(n)) {
+ Append(wname, "_set");
+ } else if (is_getter_method(n)) {
+ Append(wname, "_get");
+ }
+ } else if (wrapperType == memberfn) {
+ wname = Getattr(n, "memberfunctionHandler:sym:name");
+ } else if (wrapperType == staticmembervar) {
+ // Shape::nshapes -> nshapes
+ wname = Getattr(n, "staticmembervariableHandler:sym:name");
+
+ /* We get called twice for getter and setter methods. But to maintain
+ compatibility, Shape::nshapes() is being used for both setter and
+ getter methods. So using static_setter and static_getter variables
+ to generate half of the code each time.
+ */
+ static_setter = is_setter_method(n);
+
+ if (is_getter_method(n)) {
+ // This is to overcome types that can't be set and hence no setter.
+ if (!Equal(Getattr(n, "feature:immutable"), "1"))
+ static_getter = true;
+ }
+ } else if (wrapperType == staticmemberfn) {
+ wname = Getattr(n, "staticmemberfunctionHandler:sym:name");
+ } else {
+ if (class_name) {
+ if (Cmp(Getattr(n, "storage"), "friend") == 0 && Cmp(Getattr(n, "view"), "globalfunctionHandler") == 0) {
+ wname = iname;
+ } else {
+ wname = Getattr(n, "destructorHandler:sym:name");
+ }
+ } else {
+ wname = iname;
+ }
+ }
+
+ if (wrapperType == destructor) {
+ // We don't explicitly wrap the destructor for PHP - Zend manages the
+ // reference counting, and the user can just do `$obj = null;' or similar
+ // to remove a reference to an object.
+ Setattr(n, "wrap:name", wname);
+ (void)emit_action(n);
+ return SWIG_OK;
+ }
+
+ if (!static_getter) {
+ // Create or find existing PHPTypes.
+ phptypes = NULL;
+
+ String *key;
+ if (class_name && !Equal(Getattr(n, "storage"), "friend")) {
+ key = NewStringf("%s:%s", class_name, wname);
+ } else {
+ key = NewStringf(":%s", wname);
+ }
+
+ PHPTypes *p = (PHPTypes*)GetVoid(all_phptypes, key);
+ if (p) {
+ // We already have an entry so use it.
+ phptypes = p;
+ Delete(key);
+ } else {
+ phptypes = new PHPTypes(n);
+ SetVoid(all_phptypes, key, phptypes);
+ }
+ }
+
+ f = NewWrapper();
+
+ if (static_getter) {
+ Printf(f->def, "{\n");
+ }
+
+ String *outarg = NewStringEmpty();
+ String *cleanup = NewStringEmpty();
+
+ if (!overloaded) {
+ if (!static_getter) {
+ if (class_name && !Equal(Getattr(n, "storage"), "friend")) {
+ Printv(f->def, "static PHP_METHOD(", prefix, class_name, ",", wname, ") {\n", NIL);
+ } else {
+ if (wrap_nonclass_global) {
+ Printv(f->def, "static PHP_METHOD(", fake_class_name(), ",", wname, ") {\n",
+ " PHP_FN(", wname, ")(INTERNAL_FUNCTION_PARAM_PASSTHRU);\n",
+ "}\n\n", NIL);
+ }
+
+ if (wrap_nonclass_fake_class) {
+ Printv(f->def, "static PHP_FUNCTION(", wname, ") {\n", NIL);
+ }
+ }
+ }
+ } else {
+ Printv(f->def, "static ZEND_NAMED_FUNCTION(", overloadwname, ") {\n", NIL);
+ }
+
+ emit_parameter_variables(l, f);
+ /* Attach standard typemaps */
+
+ emit_attach_parmmaps(l, f);
+
+ if (wrapperType == memberfn || wrapperType == membervar) {
+ // Assign "this" to arg1 and remove first entry from ParmList l.
+ Printf(f->code, "arg1 = (%s)SWIG_Z_FETCH_OBJ_P(ZEND_THIS)->ptr;\n", SwigType_lstr(Getattr(l, "type"), ""));
+ l = nextSibling(l);
+ }
+
+ // wrap:parms is used by overload resolution.
+ Setattr(n, "wrap:parms", l);
+
+ int num_arguments = emit_num_arguments(l);
+ int num_required = emit_num_required(l);
+ numopt = num_arguments - num_required;
+
+ if (num_arguments > 0) {
+ String *args = NewStringEmpty();
+ Printf(args, "zval args[%d]", num_arguments);
+ Wrapper_add_local(f, "args", args);
+ Delete(args);
+ args = NULL;
+ }
+ if (wrapperType == directorconstructor) {
+ Wrapper_add_local(f, "arg0", "zval *arg0 = ZEND_THIS");
+ }
+
+ // This generated code may be called:
+ // 1) as an object method, or
+ // 2) as a class-method/function (without a "this_ptr")
+ // Option (1) has "this_ptr" for "this", option (2) needs it as
+ // first parameter
+
+ // NOTE: possible we ignore this_ptr as a param for native constructor
+
+ if (numopt > 0) { // membervariable wrappers do not have optional args
+ Wrapper_add_local(f, "arg_count", "int arg_count");
+ Printf(f->code, "arg_count = ZEND_NUM_ARGS();\n");
+ Printf(f->code, "if(arg_count<%d || arg_count>%d ||\n", num_required, num_arguments);
+ Printf(f->code, " zend_get_parameters_array_ex(arg_count,args)!=SUCCESS)\n");
+ Printf(f->code, "\tWRONG_PARAM_COUNT;\n\n");
+ } else if (static_setter || static_getter) {
+ if (num_arguments == 0) {
+ Printf(f->code, "if(ZEND_NUM_ARGS() == 0) {\n");
+ } else {
+ Printf(f->code, "if(ZEND_NUM_ARGS() == %d && zend_get_parameters_array_ex(%d, args) == SUCCESS) {\n", num_arguments, num_arguments);
+ }
+ } else {
+ if (num_arguments == 0) {
+ Printf(f->code, "if(ZEND_NUM_ARGS() != 0) {\n");
+ } else {
+ Printf(f->code, "if(ZEND_NUM_ARGS() != %d || zend_get_parameters_array_ex(%d, args) != SUCCESS) {\n", num_arguments, num_arguments);
+ }
+ Printf(f->code, "WRONG_PARAM_COUNT;\n}\n\n");
+ }
+
+ /* Now convert from PHP to C variables */
+ // At this point, argcount if used is the number of deliberately passed args
+ // not including this_ptr even if it is used.
+ // It means error messages may be out by argbase with error
+ // reports. We can either take argbase into account when raising
+ // errors, or find a better way of dealing with _thisptr.
+ // I would like, if objects are wrapped, to assume _thisptr is always
+ // _this and not the first argument.
+ // This may mean looking at Language::memberfunctionHandler
+
+ for (i = 0, p = l; i < num_arguments; i++) {
+ /* Skip ignored arguments */
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ /* Check if optional */
+ if (i >= num_required) {
+ Printf(f->code, "\tif(arg_count > %d) {\n", i);
+ }
+
+ tm = Getattr(p, "tmap:in");
+ if (!tm) {
+ SwigType *pt = Getattr(p, "type");
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ p = nextSibling(p);
+ continue;
+ }
+
+ phptypes->process_phptype(p, i + 1, "tmap:in:phptype");
+ if (GetFlag(p, "tmap:in:byref")) phptypes->set_byref(i + 1);
+
+ String *source = NewStringf("args[%d]", i);
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source);
+ Printf(f->code, "%s\n", tm);
+ if (i == 0 && Getattr(p, "self")) {
+ Printf(f->code, "\tif(!arg1) {\n");
+ Printf(f->code, "\t zend_throw_exception(zend_ce_type_error, \"this pointer is NULL\", 0);\n");
+ Printf(f->code, "\t return;\n");
+ Printf(f->code, "\t}\n");
+ }
+
+ if (i >= num_required) {
+ Printf(f->code, "}\n");
+ }
+
+ p = Getattr(p, "tmap:in:next");
+
+ Delete(source);
+ }
+
+ if (is_member_director(n)) {
+ Wrapper_add_local(f, "director", "Swig::Director *director = 0");
+ Append(f->code, "director = SWIG_DIRECTOR_CAST(arg1);\n");
+ Wrapper_add_local(f, "upcall", "bool upcall = false");
+ Printf(f->code, "upcall = (director && (director->swig_get_self()==Z_OBJ_P(ZEND_THIS)));\n");
+ }
+
+ Swig_director_emit_dynamic_cast(n, f);
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ for (i = 0, p = l; p; i++) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ Printv(cleanup, tm, "\n", NIL);
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert argument output code */
+ for (i = 0, p = l; p; i++) {
+ if ((tm = Getattr(p, "tmap:argout")) && Len(tm)) {
+ Replaceall(tm, "$result", "return_value");
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ if (!overloaded) {
+ Setattr(n, "wrap:name", wname);
+ } else {
+ Setattr(n, "wrap:name", overloadwname);
+ }
+ Setattr(n, "wrapper:method:name", wname);
+
+ bool php_constructor = (constructor && Cmp(class_name, Getattr(n, "constructorHandler:sym:name")) == 0);
+
+ /* emit function call */
+ String *actioncode = emit_action(n);
+
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ Replaceall(tm, "$input", Swig_cresult_name());
+ Replaceall(tm, "$result", php_constructor ? "ZEND_THIS" : "return_value");
+ Replaceall(tm, "$owner", newobject ? "1" : "0");
+ Printf(f->code, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(d, 0), name);
+ }
+ emit_return_variable(n, d, f);
+
+ List *return_types = phptypes->process_phptype(n, 0, "tmap:out:phptype");
+
+ if (class_name && !Equal(Getattr(n, "storage"), "friend")) {
+ if (is_member_director(n)) {
+ String *parent = class_name;
+ while ((parent = Getattr(php_parent_class, parent)) != NULL) {
+ // Mark this method name as having no return type declaration for all
+ // classes we're derived from.
+ SetFlag(has_directed_descendent, NewStringf("%s:%s", parent, wname));
+ }
+ } else if (return_types) {
+ String *parent = class_name;
+ while ((parent = Getattr(php_parent_class, parent)) != NULL) {
+ String *key = NewStringf("%s:%s", parent, wname);
+ // The parent class method needs to have a superset of the possible
+ // return types of methods with the same name in subclasses.
+ List *v = Getattr(parent_class_method_return_type, key);
+ if (!v) {
+ // New entry.
+ Setattr(parent_class_method_return_type, key, Copy(return_types));
+ } else {
+ // Update existing entry.
+ PHPTypes::merge_type_lists(v, return_types);
+ }
+ }
+ }
+ }
+
+ if (outarg) {
+ Printv(f->code, outarg, NIL);
+ }
+
+ if (static_setter && cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+
+ /* Look to see if there is any newfree cleanup code */
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+
+ if (static_getter) {
+ Printf(f->code, "}\n");
+ }
+
+ if (static_setter || static_getter) {
+ Printf(f->code, "}\n");
+ }
+
+ if (!static_setter) {
+ Printf(f->code, "fail:\n");
+ Printv(f->code, cleanup, NIL);
+ Printf(f->code, "return;\n");
+ Printf(f->code, "}\n");
+ }
+
+ Replaceall(f->code, "$cleanup", cleanup);
+ Replaceall(f->code, "$symname", iname);
+
+ Wrapper_print(f, s_wrappers);
+ DelWrapper(f);
+ f = NULL;
+
+ if (overloaded) {
+ if (!Getattr(n, "sym:nextSibling")) {
+ dispatchFunction(n, constructor);
+ }
+ } else {
+ if (!static_setter) {
+ create_command(class_name, wname, n, false, modes);
+ }
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * globalvariableHandler()
+ * ------------------------------------------------------------ */
+
+ /* PHP doesn't support intercepting reads and writes to global variables
+ * (nor static property reads and writes so we can't wrap them as static
+ * properties on a dummy class) so just let SWIG do its default thing and
+ * wrap them as name_get() and name_set().
+ */
+ //virtual int globalvariableHandler(Node *n) {
+ //}
+
+ /* ------------------------------------------------------------
+ * constantWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ String *name = GetChar(n, "name");
+ String *iname = GetChar(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ String *tm;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ SwigType_remember(type);
+
+ String *wrapping_member_constant = Getattr(n, "memberconstantHandler:sym:name");
+ if (!wrapping_member_constant) {
+ {
+ tm = Swig_typemap_lookup("consttab", n, name, 0);
+ Replaceall(tm, "$value", value);
+ if (Getattr(n, "tmap:consttab:rinit")) {
+ Printf(r_init, "%s\n", tm);
+ } else {
+ Printf(s_cinit, "%s\n", tm);
+ }
+ }
+
+ {
+ tm = Swig_typemap_lookup("classconsttab", n, name, 0);
+
+ Replaceall(tm, "$class", fake_class_name());
+ Replaceall(tm, "$const_name", iname);
+ Replaceall(tm, "$value", value);
+ if (Getattr(n, "tmap:classconsttab:rinit")) {
+ Printf(r_init, "%s\n", tm);
+ } else {
+ Printf(s_cinit, "%s\n", tm);
+ }
+ }
+ } else {
+ tm = Swig_typemap_lookup("classconsttab", n, name, 0);
+ Replaceall(tm, "$class", class_name);
+ Replaceall(tm, "$const_name", wrapping_member_constant);
+ Replaceall(tm, "$value", value);
+ if (Getattr(n, "tmap:classconsttab:rinit")) {
+ Printf(r_init, "%s\n", tm);
+ } else {
+ Printf(s_cinit, "%s\n", tm);
+ }
+ }
+
+ wrapperType = standard;
+ return SWIG_OK;
+ }
+
+ /*
+ * PHP::pragma()
+ *
+ * Pragma directive.
+ *
+ * %pragma(php) code="String" # Includes a string in the .php file
+ * %pragma(php) include="file.php" # Includes a file in the .php file
+ */
+
+ virtual int pragmaDirective(Node *n) {
+ if (!ImportMode) {
+ String *lang = Getattr(n, "lang");
+ String *type = Getattr(n, "name");
+ String *value = Getattr(n, "value");
+
+ if (Strcmp(lang, "php") == 0) {
+ if (Strcmp(type, "code") == 0) {
+ if (value) {
+ Printf(pragma_code, "%s\n", value);
+ }
+ } else if (Strcmp(type, "include") == 0) {
+ if (value) {
+ Printf(pragma_incl, "include '%s';\n", value);
+ }
+ } else if (Strcmp(type, "phpinfo") == 0) {
+ if (value) {
+ Printf(pragma_phpinfo, "%s\n", value);
+ }
+ } else if (Strcmp(type, "version") == 0) {
+ if (value) {
+ pragma_version = value;
+ }
+ } else {
+ Swig_warning(WARN_PHP_UNKNOWN_PRAGMA, input_file, line_number, "Unrecognized pragma <%s>.\n", type);
+ }
+ }
+ }
+ return Language::pragmaDirective(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int classDeclaration(Node *n) {
+ return Language::classDeclaration(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int classHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+
+ class_name = symname;
+ base_class = NULL;
+ destructor_action = NULL;
+
+ Printf(all_cs_entry, "static const zend_function_entry class_%s_functions[] = {\n", class_name);
+
+ // namespace code to introduce namespaces into wrapper classes.
+ //if (nameSpace != NULL)
+ //Printf(s_oinit, "INIT_CLASS_ENTRY(internal_ce, \"%s\\\\%s\", class_%s_functions);\n", nameSpace, class_name, class_name);
+ //else
+ Printf(s_oinit, " INIT_CLASS_ENTRY(internal_ce, \"%s%s\", class_%s_functions);\n", prefix, class_name, class_name);
+
+ if (shadow) {
+ char *rename = GetChar(n, "sym:name");
+
+ if (!addSymbol(rename, n))
+ return SWIG_ERROR;
+
+ /* Deal with inheritance */
+ List *baselist = Getattr(n, "bases");
+ if (baselist) {
+ Iterator base = First(baselist);
+ while (base.item) {
+ if (!GetFlag(base.item, "feature:ignore")) {
+ if (!base_class) {
+ base_class = Getattr(base.item, "sym:name");
+ } else {
+ /* Warn about multiple inheritance for additional base class(es) */
+ String *proxyclassname = SwigType_str(Getattr(n, "classtypeobj"), 0);
+ String *baseclassname = SwigType_str(Getattr(base.item, "name"), 0);
+ Swig_warning(WARN_PHP_MULTIPLE_INHERITANCE, input_file, line_number,
+ "Warning for %s, base %s ignored. Multiple inheritance is not supported in PHP.\n", proxyclassname, baseclassname);
+ }
+ }
+ base = Next(base);
+ }
+ }
+ }
+
+ if (GetFlag(n, "feature:exceptionclass") && Getattr(n, "feature:except")) {
+ /* PHP requires thrown objects to be instances of or derived from
+ * Exception, so that really needs to take priority over any
+ * explicit base class.
+ */
+ if (base_class) {
+ String *proxyclassname = SwigType_str(Getattr(n, "classtypeobj"), 0);
+ Swig_warning(WARN_PHP_MULTIPLE_INHERITANCE, input_file, line_number,
+ "Warning for %s, base %s ignored. Multiple inheritance is not supported in PHP.\n", proxyclassname, base_class);
+ }
+ base_class = NewString("Exception");
+ }
+
+ if (!base_class) {
+ Printf(s_oinit, " SWIG_Php_ce_%s = zend_register_internal_class(&internal_ce);\n", class_name);
+ } else if (Equal(base_class, "Exception")) {
+ Printf(s_oinit, " SWIG_Php_ce_%s = zend_register_internal_class_ex(&internal_ce, zend_ce_exception);\n", class_name);
+ } else if (is_class_wrapped(base_class)) {
+ Printf(s_oinit, " SWIG_Php_ce_%s = zend_register_internal_class_ex(&internal_ce, SWIG_Php_ce_%s);\n", class_name, base_class);
+ Setattr(php_parent_class, class_name, base_class);
+ } else {
+ Printf(s_oinit, " {\n");
+ Printf(s_oinit, " swig_type_info *type_info = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, \"_p_%s\");\n", base_class);
+ Printf(s_oinit, " SWIG_Php_ce_%s = zend_register_internal_class_ex(&internal_ce, (zend_class_entry*)(type_info ? type_info->clientdata : NULL));\n", class_name);
+ Printf(s_oinit, " }\n");
+ }
+
+ if (Getattr(n, "abstracts") && !GetFlag(n, "feature:notabstract")) {
+ Printf(s_oinit, " SWIG_Php_ce_%s->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;\n", class_name);
+ }
+ if (Getattr(n, "feature:php:allowdynamicproperties")) {
+ Append(s_oinit, "#ifdef ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES\n");
+ Printf(s_oinit, " SWIG_Php_ce_%s->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;\n", class_name);
+ Append(s_oinit, "#endif\n");
+ } else {
+ Append(s_oinit, "#ifdef ZEND_ACC_NO_DYNAMIC_PROPERTIES\n");
+ Printf(s_oinit, " SWIG_Php_ce_%s->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES;\n", class_name);
+ Append(s_oinit, "#endif\n");
+ }
+ String *swig_wrapped = swig_wrapped_interface_ce();
+ Printv(s_oinit, " zend_do_implement_interface(SWIG_Php_ce_", class_name, ", &", swig_wrapped, ");\n", NIL);
+
+ {
+ Node *node = NewHash();
+ Setattr(node, "type", Getattr(n, "name"));
+ Setfile(node, Getfile(n));
+ Setline(node, Getline(n));
+ String *interfaces = Swig_typemap_lookup("phpinterfaces", node, "", 0);
+ Replaceall(interfaces, " ", "");
+ if (interfaces && Len(interfaces) > 0) {
+ // It seems we need to wait until RINIT time to look up class entries
+ // for interfaces by name. The downside is that this then happens for
+ // every request.
+ //
+ // Most pre-defined interfaces are accessible via zend_class_entry*
+ // variables declared in the PHP C API - these we can use at MINIT
+ // time, so we special case them. This will also be a little faster
+ // than looking up by name.
+ Printv(s_header,
+ "#ifdef __cplusplus\n",
+ "extern \"C\" {\n",
+ "#endif\n",
+ NIL);
+
+ String *r_init_prefix = NewStringEmpty();
+
+ List *interface_list = Split(interfaces, ',', -1);
+ int num_interfaces = Len(interface_list);
+ for (int i = 0; i < num_interfaces; ++i) {
+ String *interface = Getitem(interface_list, i);
+ // We generate conditional code in both minit and rinit - then we or the user
+ // just need to define SWIG_PHP_INTERFACE_xxx_CE (and optionally
+ // SWIG_PHP_INTERFACE_xxx_HEADER) to handle interface `xxx` at minit-time.
+ Printv(s_header,
+ "#ifdef SWIG_PHP_INTERFACE_", interface, "_HEADER\n",
+ "# include SWIG_PHP_INTERFACE_", interface, "_HEADER\n",
+ "#endif\n",
+ NIL);
+ Printv(s_oinit,
+ "#ifdef SWIG_PHP_INTERFACE_", interface, "_CE\n",
+ " zend_do_implement_interface(SWIG_Php_ce_", class_name, ", SWIG_PHP_INTERFACE_", interface, "_CE);\n",
+ "#endif\n",
+ NIL);
+ Printv(r_init_prefix,
+ "#ifndef SWIG_PHP_INTERFACE_", interface, "_CE\n",
+ " {\n",
+ " zend_class_entry *swig_interface_ce = zend_lookup_class(zend_string_init(\"", interface, "\", sizeof(\"", interface, "\") - 1, 0));\n",
+ " if (swig_interface_ce)\n",
+ " zend_do_implement_interface(SWIG_Php_ce_", class_name, ", swig_interface_ce);\n",
+ " else\n",
+ " zend_throw_exception(zend_ce_error, \"Interface \\\"", interface, "\\\" not found\", 0);\n",
+ " }\n",
+ "#endif\n",
+ NIL);
+ }
+
+ // Handle interfaces at the start of rinit so that they're added
+ // before any potential constant objects, etc which might be created
+ // later in rinit.
+ Insert(r_init, 0, r_init_prefix);
+ Delete(r_init_prefix);
+
+ Printv(s_header,
+ "#ifdef __cplusplus\n",
+ "}\n",
+ "#endif\n",
+ NIL);
+ }
+ Delete(interfaces);
+ }
+
+ Language::classHandler(n);
+
+ static bool emitted_base_object_handlers = false;
+ if (!emitted_base_object_handlers) {
+ Printf(s_creation, "static zend_object_handlers Swig_Php_base_object_handlers;\n\n");
+
+ // Set up a base zend_object_handlers structure which we can use as-is
+ // for classes without a destructor, and copy as the basis for other
+ // classes.
+ Printf(s_oinit, " Swig_Php_base_object_handlers = *zend_get_std_object_handlers();\n");
+ Printf(s_oinit, " Swig_Php_base_object_handlers.offset = XtOffsetOf(swig_object_wrapper, std);\n");
+ Printf(s_oinit, " Swig_Php_base_object_handlers.clone_obj = NULL;\n");
+ emitted_base_object_handlers = true;
+ }
+
+ Printf(s_creation, "static zend_class_entry *SWIG_Php_ce_%s;\n\n", class_name);
+
+ if (Getattr(n, "has_destructor")) {
+ if (destructor_action ? Equal(destructor_action, "free((char *) arg1);") : !CPlusPlus) {
+ // We can use a single function if the destructor action calls free()
+ // (either explicitly or as the default in C-mode) since free() doesn't
+ // care about the object's type. We currently only check for the exact
+ // code that Swig_cdestructor_call() emits.
+ static bool emitted_common_cdestructor = false;
+ if (!emitted_common_cdestructor) {
+ Printf(s_creation, "static zend_object_handlers Swig_Php_common_c_object_handlers;\n\n");
+ Printf(s_creation, "static void SWIG_Php_common_c_free_obj(zend_object *object) {free(SWIG_Php_free_obj(object));}\n\n");
+ Printf(s_creation, "static zend_object *SWIG_Php_common_c_create_object(zend_class_entry *ce) {return SWIG_Php_do_create_object(ce, &Swig_Php_common_c_object_handlers);}\n");
+
+ Printf(s_oinit, " Swig_Php_common_c_object_handlers = Swig_Php_base_object_handlers;\n");
+ Printf(s_oinit, " Swig_Php_common_c_object_handlers.free_obj = SWIG_Php_common_c_free_obj;\n");
+
+ emitted_common_cdestructor = true;
+ }
+
+ Printf(s_oinit, " SWIG_Php_ce_%s->create_object = SWIG_Php_common_c_create_object;\n", class_name);
+ } else {
+ Printf(s_creation, "static zend_object_handlers %s_object_handlers;\n", class_name);
+ Printf(s_creation, "static zend_object *SWIG_Php_create_object_%s(zend_class_entry *ce) {return SWIG_Php_do_create_object(ce, &%s_object_handlers);}\n", class_name, class_name);
+
+ Printf(s_creation, "static void SWIG_Php_free_obj_%s(zend_object *object) {",class_name);
+ String *type = Getattr(n, "classtype");
+ // Special case handling the delete call generated by
+ // Swig_cppdestructor_call() and generate simpler code.
+ if (destructor_action && !Equal(destructor_action, "delete arg1;")) {
+ Printv(s_creation, "\n"
+ " ", type, " *arg1 = (" , type, " *)SWIG_Php_free_obj(object);\n"
+ " if (arg1) {\n"
+ " ", destructor_action, "\n"
+ " }\n", NIL);
+ } else {
+ Printf(s_creation, "delete (%s *)SWIG_Php_free_obj(object);", type);
+ }
+ Printf(s_creation, "}\n\n");
+
+ Printf(s_oinit, " SWIG_Php_ce_%s->create_object = SWIG_Php_create_object_%s;\n", class_name, class_name);
+ Printf(s_oinit, " %s_object_handlers = Swig_Php_base_object_handlers;\n", class_name);
+ Printf(s_oinit, " %s_object_handlers.free_obj = SWIG_Php_free_obj_%s;\n", class_name, class_name);
+ }
+ } else {
+ static bool emitted_destructorless_create_object = false;
+ if (!emitted_destructorless_create_object) {
+ emitted_destructorless_create_object = true;
+ Printf(s_creation, "static zend_object *SWIG_Php_create_object(zend_class_entry *ce) {return SWIG_Php_do_create_object(ce, &Swig_Php_base_object_handlers);}\n", class_name);
+ }
+
+ Printf(s_oinit, " SWIG_Php_ce_%s->create_object = SWIG_Php_create_object;\n", class_name);
+ }
+
+ // If not defined we aren't wrapping any functions which use this type as a
+ // parameter or return value, in which case we don't need the clientdata
+ // set.
+ Printf(s_oinit, "#ifdef SWIGTYPE_p%s\n", SwigType_manglestr(Getattr(n, "classtypeobj")));
+ Printf(s_oinit, " SWIG_TypeClientData(SWIGTYPE_p%s,SWIG_Php_ce_%s);\n", SwigType_manglestr(Getattr(n, "classtypeobj")), class_name);
+ Printf(s_oinit, "#endif\n");
+ Printf(s_oinit, "\n");
+
+ generate_magic_property_methods(n);
+ Printf(all_cs_entry, " ZEND_FE_END\n};\n\n");
+
+ class_name = NULL;
+ base_class = NULL;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * memberfunctionHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int memberfunctionHandler(Node *n) {
+ wrapperType = memberfn;
+ Language::memberfunctionHandler(n);
+ wrapperType = standard;
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * membervariableHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int membervariableHandler(Node *n) {
+ if (magic_set == NULL) {
+ magic_set = NewStringEmpty();
+ magic_get = NewStringEmpty();
+ magic_isset = NewStringEmpty();
+ }
+
+ String *v_name = GetChar(n, "name");
+
+ Printf(magic_set, "\nelse if (strcmp(ZSTR_VAL(arg2),\"%s\") == 0) {\n", v_name);
+ Printf(magic_set, "ZVAL_STRING(&tempZval, \"%s_set\");\n", v_name);
+ Printf(magic_set, "call_user_function(EG(function_table),ZEND_THIS,&tempZval,return_value,1,&args[1]);\n}\n");
+
+ Printf(magic_get, "\nelse if (strcmp(ZSTR_VAL(arg2),\"%s\") == 0) {\n", v_name);
+ Printf(magic_get, "ZVAL_STRING(&tempZval, \"%s_get\");\n", v_name);
+ Printf(magic_get, "call_user_function(EG(function_table),ZEND_THIS,&tempZval,return_value,0,NULL);\n}\n");
+
+ Printf(magic_isset, "\nelse if (strcmp(ZSTR_VAL(arg2),\"%s\") == 0) {\n", v_name);
+ Printf(magic_isset, "RETVAL_TRUE;\n}\n");
+
+ wrapperType = membervar;
+ Language::membervariableHandler(n);
+ wrapperType = standard;
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * staticmembervariableHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int staticmembervariableHandler(Node *n) {
+ wrapperType = staticmembervar;
+ Language::staticmembervariableHandler(n);
+ wrapperType = standard;
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+ wrapperType = staticmemberfn;
+ Language::staticmemberfunctionHandler(n);
+ wrapperType = standard;
+
+ return SWIG_OK;
+ }
+
+ int abstractConstructorHandler(Node *) {
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constructorHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int constructorHandler(Node *n) {
+ if (Swig_directorclass(n)) {
+ String *ctype = GetChar(Swig_methodclass(n), "classtype");
+ String *sname = GetChar(Swig_methodclass(n), "sym:name");
+ String *args = NewStringEmpty();
+ ParmList *p = Getattr(n, "parms");
+ int i;
+
+ for (i = 0; p; p = nextSibling(p), i++) {
+ if (i) {
+ Printf(args, ", ");
+ }
+ if (Strcmp(GetChar(p, "type"), SwigType_str(GetChar(p, "type"), 0))) {
+ SwigType *t = Getattr(p, "type");
+ Printf(args, "%s", SwigType_rcaststr(t, 0));
+ if (SwigType_isreference(t)) {
+ Append(args, "*");
+ }
+ }
+ Printf(args, "arg%d", i+1);
+ }
+
+ /* director ctor code is specific for each class */
+ Delete(director_ctor_code);
+ director_ctor_code = NewStringEmpty();
+ director_prot_ctor_code = NewStringEmpty();
+ Printf(director_ctor_code, "if (Z_OBJCE_P(arg0) == SWIG_Php_ce_%s) { /* not subclassed */\n", class_name);
+ Printf(director_prot_ctor_code, "if (Z_OBJCE_P(arg0) == SWIG_Php_ce_%s) { /* not subclassed */\n", class_name);
+ Printf(director_ctor_code, " %s = new %s(%s);\n", Swig_cresult_name(), ctype, args);
+ Printf(director_prot_ctor_code,
+ " zend_throw_exception(zend_ce_type_error, \"accessing abstract class or protected constructor\", 0);\n"
+ " return;\n");
+ if (i) {
+ Insert(args, 0, ", ");
+ }
+ Printf(director_ctor_code, "} else {\n %s = (%s *)new SwigDirector_%s(arg0%s);\n}\n", Swig_cresult_name(), ctype, sname, args);
+ Printf(director_prot_ctor_code, "} else {\n %s = (%s *)new SwigDirector_%s(arg0%s);\n}\n", Swig_cresult_name(), ctype, sname, args);
+ Delete(args);
+
+ wrapperType = directorconstructor;
+ } else {
+ wrapperType = constructor;
+ }
+ Language::constructorHandler(n);
+ wrapperType = standard;
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * destructorHandler()
+ * ------------------------------------------------------------ */
+ virtual int destructorHandler(Node *n) {
+ wrapperType = destructor;
+ Language::destructorHandler(n);
+ destructor_action = Getattr(n, "wrap:action");
+ wrapperType = standard;
+ return SWIG_OK;
+ }
+
+ int classDirectorInit(Node *n) {
+ String *declaration = Swig_director_declaration(n);
+ Printf(f_directors_h, "%s\n", declaration);
+ Printf(f_directors_h, "public:\n");
+ Delete(declaration);
+ return Language::classDirectorInit(n);
+ }
+
+ int classDirectorEnd(Node *n) {
+ Printf(f_directors_h, "};\n");
+ return Language::classDirectorEnd(n);
+ }
+
+ int classDirectorConstructor(Node *n) {
+ Node *parent = Getattr(n, "parentNode");
+ String *decl = Getattr(n, "decl");
+ String *supername = Swig_class_name(parent);
+ String *classname = NewStringEmpty();
+ Printf(classname, "SwigDirector_%s", supername);
+
+ /* insert self parameter */
+ Parm *p;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms = CopyParmList(superparms);
+ String *type = NewString("zval");
+ SwigType_add_pointer(type);
+ p = NewParm(type, NewString("self"), n);
+ set_nextSibling(p, parms);
+ parms = p;
+
+ if (!Getattr(n, "defaultargs")) {
+ // There should always be a "self" parameter first.
+ assert(ParmList_len(parms) > 0);
+
+ /* constructor */
+ {
+ Wrapper *w = NewWrapper();
+ String *call;
+ String *basetype = Getattr(parent, "classtype");
+
+ String *target = Swig_method_decl(0, decl, classname, parms, 0);
+ call = Swig_csuperclass_call(0, basetype, superparms);
+ Printf(w->def, "%s::%s: %s, Swig::Director(self) {", classname, target, call);
+ Append(w->def, "}");
+ Delete(target);
+ Wrapper_print(w, f_directors);
+ Delete(call);
+ DelWrapper(w);
+ }
+
+ /* constructor header */
+ {
+ String *target = Swig_method_decl(0, decl, classname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+ return Language::classDirectorConstructor(n);
+ }
+
+ int classDirectorMethod(Node *n, Node *parent, String *super) {
+ int is_void = 0;
+ int is_pointer = 0;
+ String *decl = Getattr(n, "decl");
+ String *returntype = Getattr(n, "type");
+ String *name = Getattr(n, "name");
+ String *classname = Getattr(parent, "sym:name");
+ String *c_classname = Getattr(parent, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *declaration = NewStringEmpty();
+ ParmList *l = Getattr(n, "parms");
+ Wrapper *w = NewWrapper();
+ String *tm;
+ String *wrap_args = NewStringEmpty();
+ String *value = Getattr(n, "value");
+ String *storage = Getattr(n, "storage");
+ bool pure_virtual = false;
+ int status = SWIG_OK;
+ int idx;
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+
+ if (Cmp(storage, "virtual") == 0) {
+ if (Cmp(value, "0") == 0) {
+ pure_virtual = true;
+ }
+ }
+
+ /* determine if the method returns a pointer */
+ is_pointer = SwigType_ispointer_return(decl);
+ is_void = (Cmp(returntype, "void") == 0 && !is_pointer);
+
+ /* virtual method definition */
+ String *target;
+ String *pclassname = NewStringf("SwigDirector_%s", classname);
+ String *qualified_name = NewStringf("%s::%s", pclassname, name);
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+ /* header declaration */
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Get any exception classes in the throws typemap
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ ParmList *throw_parm_list = 0;
+
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ Parm *p;
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list)
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+ String *str = SwigType_str(Getattr(p, "type"), 0);
+ Append(w->def, str);
+ Append(declaration, str);
+ Delete(str);
+ }
+ }
+
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+
+ /* declare method return value
+ * if the return value is a reference or const reference, a specialized typemap must
+ * handle it, including declaration of c_result ($result).
+ */
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ }
+ } else {
+ String *cres = SwigType_lstr(returntype, "c_result");
+ Printf(w->code, "%s;\n", cres);
+ Delete(cres);
+ }
+ }
+
+ if (ignored_method) {
+ if (!pure_virtual) {
+ if (!is_void)
+ Printf(w->code, "return ");
+ String *super_call = Swig_method_call(super, l);
+ Printf(w->code, "%s;\n", super_call);
+ Delete(super_call);
+ } else {
+ Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname),
+ SwigType_namestr(name));
+ }
+ } else {
+ /* attach typemaps to arguments (C/C++ -> PHP) */
+ Swig_director_parms_fixup(l);
+
+ /* remove the wrapper 'w' since it was producing spurious temps */
+ Swig_typemap_attach_parms("in", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ Parm *p;
+
+ /* build argument list and type conversion string */
+ idx = 0;
+ p = l;
+ while (p) {
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ String *pname = Getattr(p, "name");
+ String *ptype = Getattr(p, "type");
+
+ if ((tm = Getattr(p, "tmap:directorin")) != 0) {
+ String *parse = Getattr(p, "tmap:directorin:parse");
+ if (!parse) {
+ String *input = NewStringf("&args[%d]", idx++);
+ Setattr(p, "emit:directorinput", input);
+ Replaceall(tm, "$input", input);
+ Delete(input);
+ Replaceall(tm, "$owner", "0");
+ Printv(wrap_args, tm, "\n", NIL);
+ } else {
+ Setattr(p, "emit:directorinput", pname);
+ Replaceall(tm, "$input", pname);
+ Replaceall(tm, "$owner", "0");
+ if (Len(tm) == 0)
+ Append(tm, pname);
+ }
+ p = Getattr(p, "tmap:directorin:next");
+ continue;
+ } else if (Cmp(ptype, "void")) {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
+ "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(ptype, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_NOWRAP;
+ break;
+ }
+ p = nextSibling(p);
+ }
+
+ if (!idx) {
+ Printf(w->code, "zval *args = NULL;\n");
+ } else {
+ Printf(w->code, "zval args[%d];\n", idx);
+ }
+ // typemap_directorout testcase requires that 0 can be assigned to the
+ // variable named after the result of Swig_cresult_name(), so that can't
+ // be a zval - make it a pointer to one instead.
+ Printf(w->code, "zval swig_zval_result;\n");
+ Printf(w->code, "zval * SWIGUNUSED %s = &swig_zval_result;\n", Swig_cresult_name());
+
+ /* wrap complex arguments to zvals */
+ Append(w->code, wrap_args);
+
+ const char *funcname = GetChar(n, "sym:name");
+ Append(w->code, "{\n");
+ Append(w->code, "#if PHP_MAJOR_VERSION < 8\n");
+ Printf(w->code, "zval swig_funcname;\n");
+ Printf(w->code, "ZVAL_STRINGL(&swig_funcname, \"%s\", %d);\n", funcname, strlen(funcname));
+ Printf(w->code, "call_user_function(EG(function_table), &swig_self, &swig_funcname, &swig_zval_result, %d, args);\n", idx);
+ Append(w->code, "#else\n");
+ Printf(w->code, "zend_string *swig_funcname = zend_string_init(\"%s\", %d, 0);\n", funcname, strlen(funcname));
+ Append(w->code, "zend_function *swig_zend_func = zend_std_get_method(&Z_OBJ(swig_self), swig_funcname, NULL);\n");
+ Append(w->code, "zend_string_release(swig_funcname);\n");
+ Printf(w->code, "if (swig_zend_func) zend_call_known_instance_method(swig_zend_func, Z_OBJ(swig_self), &swig_zval_result, %d, args);\n", idx);
+ Append(w->code, "#endif\n");
+
+ /* exception handling */
+ tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
+ if (!tm) {
+ tm = Getattr(n, "feature:director:except");
+ if (tm)
+ tm = Copy(tm);
+ }
+ if (!tm || Len(tm) == 0 || Equal(tm, "1")) {
+ // Skip marshalling the return value as there isn't one.
+ tm = NewString("if ($error) SWIG_fail;");
+ }
+
+ Replaceall(tm, "$error", "EG(exception)");
+ Printv(w->code, Str(tm), "\n}\n{\n", NIL);
+ Delete(tm);
+
+ /* marshal return value from PHP to C/C++ type */
+
+ String *cleanup = NewStringEmpty();
+ String *outarg = NewStringEmpty();
+
+ idx = 0;
+
+ /* marshal return value */
+ if (!is_void) {
+ tm = Swig_typemap_lookup("directorout", n, Swig_cresult_name(), w);
+ if (tm != 0) {
+ Replaceall(tm, "$input", Swig_cresult_name());
+ char temp[24];
+ sprintf(temp, "%d", idx);
+ Replaceall(tm, "$argnum", temp);
+
+ /* TODO check this */
+ if (Getattr(n, "wrap:disown")) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+ Replaceall(tm, "$result", "c_result");
+ Printv(w->code, tm, "\n", NIL);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(returntype, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_ERROR;
+ }
+ }
+
+ /* marshal outputs */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
+ Replaceall(tm, "$result", Swig_cresult_name());
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ Append(w->code, "}\n");
+
+ Delete(cleanup);
+ Delete(outarg);
+ }
+
+ Append(w->code, "fail: ;\n");
+ if (!is_void) {
+ if (!(ignored_method && !pure_virtual)) {
+ String *rettype = SwigType_str(returntype, 0);
+ if (!SwigType_isreference(returntype)) {
+ Printf(w->code, "return (%s) c_result;\n", rettype);
+ } else {
+ Printf(w->code, "return (%s) *c_result;\n", rettype);
+ }
+ Delete(rettype);
+ }
+ }
+ Append(w->code, "}\n");
+
+ // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewStringEmpty();
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+
+ /* emit the director method */
+ if (status == SWIG_OK) {
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+
+ /* clean up */
+ Delete(wrap_args);
+ Delete(pclassname);
+ DelWrapper(w);
+ return status;
+ }
+
+ int classDirectorDisown(Node *n) {
+ wrapperType = directordisown;
+ int result = Language::classDirectorDisown(n);
+ wrapperType = standard;
+ return result;
+ }
+}; /* class PHP */
+
+static PHP *maininstance = 0;
+
+List *PHPTypes::process_phptype(Node *n, int key, const String_or_char *attribute_name) {
+
+ while (Len(merged_types) <= key) {
+ Append(merged_types, NewList());
+ }
+
+ String *phptype = Getattr(n, attribute_name);
+ if (!phptype || Len(phptype) == 0) {
+ // There's no type declaration, so any merged version has no type declaration.
+ //
+ // Use a DOH None object as a marker to indicate there's no type
+ // declaration for this parameter/return value (you can't store NULL as a
+ // value in a DOH List).
+ Setitem(merged_types, key, None);
+ return NULL;
+ }
+
+ DOH *merge_list = Getitem(merged_types, key);
+ if (merge_list == None) return NULL;
+
+ List *types = Split(phptype, '|', -1);
+ String *first_type = Getitem(types, 0);
+ if (Char(first_type)[0] == '?') {
+ if (Len(types) > 1) {
+ Printf(stderr, "warning: Invalid phptype: '%s' (can't use ? and | together)\n", phptype);
+ }
+ // Treat `?foo` just like `foo|null`.
+ Append(types, "null");
+ Setitem(types, 0, NewString(Char(first_type) + 1));
+ }
+
+ SortList(types, NULL);
+ String *prev = NULL;
+ for (Iterator i = First(types); i.item; i = Next(i)) {
+ if (prev && Equal(prev, i.item)) {
+ Printf(stderr, "warning: Invalid phptype: '%s' (duplicate entry for '%s')\n", phptype, i.item);
+ continue;
+ }
+
+ if (key > 0 && Equal(i.item, "void")) {
+ // Reject void for parameter type.
+ Printf(stderr, "warning: Invalid phptype: '%s' ('%s' can't be used as a parameter phptype)\n", phptype, i.item);
+ continue;
+ }
+
+ if (Equal(i.item, "SWIGTYPE")) {
+ String *type = Getattr(n, "type");
+ Node *class_node = maininstance->classLookup(type);
+ if (class_node) {
+ // FIXME: Prefix classname with a backslash to prevent collisions
+ // with built-in types? Or are non of those valid anyway and so will
+ // have been renamed at this point?
+ Append(merge_list, Getattr(class_node, "sym:name"));
+ } else {
+ // SWIG wraps a pointer to a non-object type as an object in a PHP
+ // class named based on the SWIG-mangled C/C++ type.
+ //
+ // FIXME: We should check this is actually a known pointer to
+ // non-object type so we complain about `phptype="SWIGTYPE"` being
+ // used for PHP types like `int` or `string` (currently this only
+ // fails at runtime and the error isn't very helpful). We could
+ // check the condition
+ //
+ // raw_pointer_types && Getattr(raw_pointer_types, SwigType_manglestr(type))
+ //
+ // except that raw_pointer_types may not have been fully filled in when
+ // we are called.
+ Append(merge_list, NewStringf("SWIG\\%s", SwigType_manglestr(type)));
+ }
+ } else {
+ Append(merge_list, i.item);
+ }
+ prev = i.item;
+ }
+ SortList(merge_list, NULL);
+ return merge_list;
+}
+
+void PHPTypes::merge_type_lists(List *merge_list, List *o_merge_list) {
+ int i = 0, j = 0;
+ while (j < Len(o_merge_list)) {
+ String *candidate = Getitem(o_merge_list, j);
+ while (i < Len(merge_list)) {
+ int cmp = Cmp(Getitem(merge_list, i), candidate);
+ if (cmp == 0)
+ goto handled;
+ if (cmp > 0)
+ break;
+ ++i;
+ }
+ Insert(merge_list, i, candidate);
+ ++i;
+handled:
+ ++j;
+ }
+}
+
+void PHPTypes::merge_from(const PHPTypes* o) {
+ num_required = std::min(num_required, o->num_required);
+
+ if (o->byref) {
+ if (byref == NULL) {
+ byref = Copy(o->byref);
+ } else {
+ int len = std::min(Len(byref), Len(o->byref));
+ // Start at 1 because we only want to merge parameter types, and key 0 is
+ // the return type.
+ for (int key = 1; key < len; ++key) {
+ if (Getitem(byref, key) == None &&
+ Getitem(o->byref, key) != None) {
+ Setitem(byref, key, "");
+ }
+ }
+ for (int key = len; key < Len(o->byref); ++key) {
+ Append(byref, Getitem(o->byref, key));
+ }
+ }
+ }
+
+ int len = std::min(Len(merged_types), Len(o->merged_types));
+ for (int key = 0; key < len; ++key) {
+ DOH *merge_list = Getitem(merged_types, key);
+ // None trumps anything else in the merge.
+ if (merge_list == None) continue;
+ DOH *o_merge_list = Getitem(o->merged_types, key);
+ if (o_merge_list == None) {
+ Setitem(merged_types, key, None);
+ continue;
+ }
+ merge_type_lists(merge_list, o_merge_list);
+ }
+ // Copy over any additional entries.
+ for (int key = len; key < Len(o->merged_types); ++key) {
+ Append(merged_types, Copy(Getitem(o->merged_types, key)));
+ }
+}
+
+// Collect non-class pointer types from the type table so we can set up PHP
+// classes for them later.
+//
+// NOTE: it's a function NOT A PHP::METHOD
+extern "C" {
+static void typetrace(const SwigType *ty, String *mangled, String *clientdata) {
+ if (maininstance->classLookup(ty) == NULL) {
+ // a non-class pointer
+ if (!raw_pointer_types) {
+ raw_pointer_types = NewHash();
+ }
+ Setattr(raw_pointer_types, mangled, mangled);
+ }
+ if (r_prevtracefunc)
+ (*r_prevtracefunc) (ty, mangled, clientdata);
+}
+}
+
+/* -----------------------------------------------------------------------------
+ * new_swig_php() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_php() {
+ maininstance = new PHP;
+ if (!r_prevtracefunc) {
+ r_prevtracefunc = SwigType_remember_trace(typetrace);
+ } else {
+ Printf(stderr, "php Typetrace vector already saved!\n");
+ assert(0);
+ }
+ return maininstance;
+}
+
+extern "C" Language *swig_php(void) {
+ return new_swig_php();
+}
diff --git a/contrib/tools/swig/Source/Modules/python.cxx b/contrib/tools/swig/Source/Modules/python.cxx
new file mode 100644
index 0000000000..63f0c1650d
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/python.cxx
@@ -0,0 +1,5768 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * python.cxx
+ *
+ * Python language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include "pydoc.h"
+
+#define PYSHADOW_MEMBER 0x2
+#define WARN_PYTHON_MULTIPLE_INH 405
+
+#define PYTHON_INT_MAX (2147483647)
+#define PYTHON_INT_MIN (-2147483647-1)
+
+static String *const_code = 0;
+static String *module = 0;
+static String *package = 0;
+static String *mainmodule = 0;
+static String *interface = 0;
+static String *global_name = 0;
+static int shadow = 1;
+static int use_kw = 0;
+static int director_method_index = 0;
+static int builtin = 0;
+
+static File *f_begin = 0;
+static File *f_runtime = 0;
+static File *f_runtime_h = 0;
+static File *f_header = 0;
+static File *f_wrappers = 0;
+static File *f_directors = 0;
+static File *f_directors_h = 0;
+static File *f_init = 0;
+static File *f_shadow_py = 0;
+static String *f_shadow = 0;
+static String *f_shadow_begin = 0;
+static Hash *f_shadow_imports = 0;
+static String *f_shadow_after_begin = 0;
+static String *f_shadow_stubs = 0;
+static Hash *builtin_getset = 0;
+static Hash *builtin_closures = 0;
+static Hash *class_members = 0;
+static File *f_builtins = 0;
+static String *builtin_tp_init = 0;
+static String *builtin_methods = 0;
+static String *builtin_default_unref = 0;
+static String *builtin_closures_code = 0;
+
+static String *methods;
+static String *methods_proxydocs;
+static String *class_name;
+static String *shadow_indent = 0;
+static int in_class = 0;
+static int no_header_file = 0;
+static int max_bases = 0;
+static int builtin_bases_needed = 0;
+
+/* C++ Support + Shadow Classes */
+
+static int have_constructor = 0;
+static int have_repr = 0;
+static bool have_builtin_static_member_method_callback = false;
+static bool have_fast_proxy_static_member_method_callback = false;
+static String *real_classname;
+
+/* Thread Support */
+static int threads = 0;
+static int nothreads = 0;
+
+/* Other options */
+static int dirvtable = 0;
+static int doxygen = 0;
+static int fastunpack = 1;
+static int fastproxy = 0;
+static int olddefs = 0;
+static int castmode = 0;
+static int extranative = 0;
+static int nortti = 0;
+static int relativeimport = 0;
+static int flat_static_method = 0;
+
+/* flags for the make_autodoc function */
+namespace {
+enum autodoc_t {
+ AUTODOC_CLASS,
+ AUTODOC_CTOR,
+ AUTODOC_DTOR,
+ AUTODOC_STATICFUNC,
+ AUTODOC_FUNC,
+ AUTODOC_METHOD,
+ AUTODOC_CONST,
+ AUTODOC_VAR
+};
+}
+
+static const char *usage1 = "\
+Python Options (available with -python)\n\
+ -builtin - Create Python built-in types rather than proxy classes, for better performance\n\
+ -castmode - Enable the casting mode, which allows implicit cast between types in Python\n\
+ -debug-doxygen-parser - Display doxygen parser module debugging information\n\
+ -debug-doxygen-translator - Display doxygen translator module debugging information\n\
+ -dirvtable - Generate a pseudo virtual table for directors for faster dispatch\n\
+ -doxygen - Convert C++ doxygen comments to pydoc comments in proxy classes\n\
+ -extranative - Return extra native wrappers for C++ std containers wherever possible\n\
+ -fastproxy - Use fast proxy mechanism for member methods\n\
+ -flatstaticmethod - Generate additional flattened Python methods for C++ static methods\n\
+ -globals <name> - Set <name> used to access C global variable (default: 'cvar')\n\
+ -interface <mod>- Set low-level C/C++ module name to <mod> (default: module name prefixed by '_')\n\
+ -keyword - Use keyword arguments\n";
+static const char *usage2 = "\
+ -nofastunpack - Use traditional UnpackTuple method to parse the argument functions\n\
+ -noh - Don't generate the output header file\n";
+static const char *usage3 = "\
+ -noproxy - Don't generate proxy classes\n\
+ -nortti - Disable the use of the native C++ RTTI with directors\n\
+ -nothreads - Disable thread support for the entire interface\n\
+ -olddefs - Keep the old method definitions when using -fastproxy\n\
+ -relativeimport - Use relative Python imports\n\
+ -threads - Add thread support for all the interface\n\
+ -O - Enable the following optimization options:\n\
+ -fastdispatch -fastproxy -fvirtual\n\
+\n";
+
+static String *getSlot(Node *n = NULL, const char *key = NULL, String *default_slot = NULL) {
+ static String *zero = NewString("0");
+ String *val = n && key && *key ? Getattr(n, key) : NULL;
+ return val ? val : default_slot ? default_slot : zero;
+}
+
+static void printSlot(File *f, String *slotval, const char *slotname, const char *functype = NULL) {
+ String *slotval_override = 0;
+ if (functype && Strcmp(slotval, "0") == 0)
+ slotval = slotval_override = NewStringf("(%s) %s", functype, slotval);
+ int len = Len(slotval);
+ int fieldwidth = len > 41 ? (len > 61 ? 0 : 61 - len) : 41 - len;
+ Printf(f, " %s,%*s/* %s */\n", slotval, fieldwidth, "", slotname);
+ Delete(slotval_override);
+}
+
+static String *getClosure(String *functype, String *wrapper, int funpack = 0) {
+ static const char *functypes[] = {
+ "unaryfunc", "SWIGPY_UNARYFUNC_CLOSURE",
+ "destructor", "SWIGPY_DESTRUCTOR_CLOSURE",
+ "inquiry", "SWIGPY_INQUIRY_CLOSURE",
+ "getiterfunc", "SWIGPY_GETITERFUNC_CLOSURE",
+ "binaryfunc", "SWIGPY_BINARYFUNC_CLOSURE",
+ "ternaryfunc", "SWIGPY_TERNARYFUNC_CLOSURE",
+ "ternarycallfunc", "SWIGPY_TERNARYCALLFUNC_CLOSURE",
+ "lenfunc", "SWIGPY_LENFUNC_CLOSURE",
+ "ssizeargfunc", "SWIGPY_SSIZEARGFUNC_CLOSURE",
+ "ssizessizeargfunc", "SWIGPY_SSIZESSIZEARGFUNC_CLOSURE",
+ "ssizeobjargproc", "SWIGPY_SSIZEOBJARGPROC_CLOSURE",
+ "ssizessizeobjargproc", "SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE",
+ "objobjproc", "SWIGPY_OBJOBJPROC_CLOSURE",
+ "objobjargproc", "SWIGPY_OBJOBJARGPROC_CLOSURE",
+ "reprfunc", "SWIGPY_REPRFUNC_CLOSURE",
+ "hashfunc", "SWIGPY_HASHFUNC_CLOSURE",
+ "iternextfunc", "SWIGPY_ITERNEXTFUNC_CLOSURE",
+ NULL
+ };
+
+ static const char *funpack_functypes[] = {
+ "unaryfunc", "SWIGPY_UNARYFUNC_CLOSURE",
+ "destructor", "SWIGPY_DESTRUCTOR_CLOSURE",
+ "inquiry", "SWIGPY_INQUIRY_CLOSURE",
+ "getiterfunc", "SWIGPY_GETITERFUNC_CLOSURE",
+ "ternaryfunc", "SWIGPY_TERNARYFUNC_CLOSURE",
+ "ternarycallfunc", "SWIGPY_TERNARYCALLFUNC_CLOSURE",
+ "lenfunc", "SWIGPY_LENFUNC_CLOSURE",
+ "ssizeargfunc", "SWIGPY_FUNPACK_SSIZEARGFUNC_CLOSURE",
+ "ssizessizeargfunc", "SWIGPY_SSIZESSIZEARGFUNC_CLOSURE",
+ "ssizeobjargproc", "SWIGPY_SSIZEOBJARGPROC_CLOSURE",
+ "ssizessizeobjargproc", "SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE",
+ "objobjproc", "SWIGPY_FUNPACK_OBJOBJPROC_CLOSURE",
+ "objobjargproc", "SWIGPY_OBJOBJARGPROC_CLOSURE",
+ "reprfunc", "SWIGPY_REPRFUNC_CLOSURE",
+ "hashfunc", "SWIGPY_HASHFUNC_CLOSURE",
+ "iternextfunc", "SWIGPY_ITERNEXTFUNC_CLOSURE",
+ NULL
+ };
+
+ if (!functype)
+ return NULL;
+ char *c = Char(functype);
+ int i;
+ if (funpack) {
+ for (i = 0; funpack_functypes[i] != NULL; i += 2) {
+ if (!strcmp(c, funpack_functypes[i]))
+ return NewStringf("%s(%s)", funpack_functypes[i + 1], wrapper);
+ }
+ } else {
+ for (i = 0; functypes[i] != NULL; i += 2) {
+ if (!strcmp(c, functypes[i]))
+ return NewStringf("%s(%s)", functypes[i + 1], wrapper);
+ }
+ }
+ return NULL;
+}
+
+class PYTHON:public Language {
+public:
+ PYTHON() {
+ /* Add code to manage protected constructors and directors */
+ director_prot_ctor_code = NewString("");
+ Printv(director_prot_ctor_code,
+ "if ( $comparison ) { /* subclassed */\n",
+ " $director_new \n",
+ "} else {\n", " SWIG_SetErrorMsg(PyExc_RuntimeError,\"accessing abstract class or protected constructor\"); \n", " SWIG_fail;\n", "}\n", NIL);
+ director_multiple_inheritance = 1;
+ director_language = 1;
+ }
+
+ ~PYTHON() {
+ delete doxygenTranslator;
+ }
+
+ /* ------------------------------------------------------------
+ * Thread Implementation
+ * ------------------------------------------------------------ */
+ int threads_enable(Node *n) const {
+ return threads && !GetFlagAttr(n, "feature:nothread");
+ }
+
+ int initialize_threads(String *f_init) {
+ if (!threads) {
+ return SWIG_OK;
+ }
+ Printf(f_init, "\n");
+ Printf(f_init, "/* Initialize threading */\n");
+ Printf(f_init, "SWIG_PYTHON_INITIALIZE_THREADS;\n");
+
+ return SWIG_OK;
+ }
+
+ virtual void thread_begin_block(Node *n, String *f) {
+ if (!GetFlag(n, "feature:nothreadblock")) {
+ String *bb = Getattr(n, "feature:threadbeginblock");
+ if (bb) {
+ Append(f, bb);
+ } else {
+ Append(f, "SWIG_PYTHON_THREAD_BEGIN_BLOCK;\n");
+ }
+ }
+ }
+
+ virtual void thread_end_block(Node *n, String *f) {
+ if (!GetFlag(n, "feature:nothreadblock")) {
+ String *eb = Getattr(n, "feature:threadendblock");
+ if (eb) {
+ Append(f, eb);
+ } else {
+ Append(f, "SWIG_PYTHON_THREAD_END_BLOCK;\n");
+ }
+ }
+ }
+
+ virtual void thread_begin_allow(Node *n, String *f) {
+ if (!GetFlag(n, "feature:nothreadallow")) {
+ String *bb = Getattr(n, "feature:threadbeginallow");
+ Append(f, "{\n");
+ if (bb) {
+ Append(f, bb);
+ } else {
+ Append(f, "SWIG_PYTHON_THREAD_BEGIN_ALLOW;\n");
+ }
+ }
+ }
+
+ virtual void thread_end_allow(Node *n, String *f) {
+ if (!GetFlag(n, "feature:nothreadallow")) {
+ String *eb = Getattr(n, "feature:threadendallow");
+ Append(f, "\n");
+ if (eb) {
+ Append(f, eb);
+ } else {
+ Append(f, "SWIG_PYTHON_THREAD_END_ALLOW;");
+ }
+ Append(f, "\n}");
+ }
+ }
+
+
+ /* ------------------------------------------------------------
+ * main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+
+ SWIG_library_directory("python");
+
+ int doxygen_translator_flags = 0;
+
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-interface") == 0) {
+ if (argv[i + 1]) {
+ interface = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-globals") == 0) {
+ if (argv[i + 1]) {
+ global_name = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if ((strcmp(argv[i], "-shadow") == 0) || ((strcmp(argv[i], "-proxy") == 0))) {
+ shadow = 1;
+ Swig_mark_arg(i);
+ } else if ((strcmp(argv[i], "-noproxy") == 0)) {
+ shadow = 0;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-keyword") == 0) {
+ use_kw = 1;
+ SWIG_cparse_set_compact_default_args(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nortti") == 0) {
+ nortti = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-threads") == 0) {
+ threads = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nothreads") == 0) {
+ /* Turn off thread support mode */
+ nothreads = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-dirvtable") == 0) {
+ dirvtable = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-doxygen") == 0) {
+ doxygen = 1;
+ scan_doxygen_comments = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-doxygen-translator") == 0) {
+ doxygen_translator_flags |= DoxygenTranslator::debug_translator;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-debug-doxygen-parser") == 0) {
+ doxygen_translator_flags |= DoxygenTranslator::debug_parser;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nofastunpack") == 0) {
+ fastunpack = 0;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-fastproxy") == 0) {
+ fastproxy = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-olddefs") == 0) {
+ olddefs = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-castmode") == 0) {
+ castmode = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-extranative") == 0) {
+ extranative = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-flatstaticmethod") == 0) {
+ flat_static_method = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-noh") == 0) {
+ no_header_file = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-newvwm") == 0) {
+ /* Turn on new value wrapper mode */
+ /* Undocumented option, did have -help text: New value wrapper mode, use only when everything else fails */
+ Swig_value_wrapper_mode(1);
+ no_header_file = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-O") == 0) {
+ fastproxy = 1;
+ Wrapper_fast_dispatch_mode_set(1);
+ Wrapper_virtual_elimination_mode_set(1);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage1, stdout);
+ fputs(usage2, stdout);
+ fputs(usage3, stdout);
+ } else if (strcmp(argv[i], "-builtin") == 0) {
+ builtin = 1;
+ Preprocessor_define("SWIGPYTHON_BUILTIN", 0);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-relativeimport") == 0) {
+ relativeimport = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-cppcast") == 0 ||
+ strcmp(argv[i], "-fastinit") == 0 ||
+ strcmp(argv[i], "-fastquery") == 0 ||
+ strcmp(argv[i], "-fastunpack") == 0 ||
+ strcmp(argv[i], "-modern") == 0 ||
+ strcmp(argv[i], "-modernargs") == 0 ||
+ strcmp(argv[i], "-noproxydel") == 0 ||
+ strcmp(argv[i], "-safecstrings") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. Ignored, this option is now always on.\n", argv[i]);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-py3") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. Ignored, this option is no longer supported.\n", argv[i]);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-aliasobj0") == 0 ||
+ strcmp(argv[i], "-buildnone") == 0 ||
+ strcmp(argv[i], "-classic") == 0 ||
+ strcmp(argv[i], "-classptr") == 0 ||
+ strcmp(argv[i], "-new_repr") == 0 ||
+ strcmp(argv[i], "-new_vwm") == 0 ||
+ strcmp(argv[i], "-newrepr") == 0 ||
+ strcmp(argv[i], "-noaliasobj0") == 0 ||
+ strcmp(argv[i], "-nobuildnone") == 0 ||
+ strcmp(argv[i], "-nocastmode") == 0 ||
+ strcmp(argv[i], "-nocppcast") == 0 ||
+ strcmp(argv[i], "-nodirvtable") == 0 ||
+ strcmp(argv[i], "-noextranative") == 0 ||
+ strcmp(argv[i], "-nofastinit") == 0 ||
+ strcmp(argv[i], "-nofastproxy") == 0 ||
+ strcmp(argv[i], "-nofastquery") == 0 ||
+ strcmp(argv[i], "-nomodern") == 0 ||
+ strcmp(argv[i], "-nomodernargs") == 0 ||
+ strcmp(argv[i], "-noolddefs") == 0 ||
+ strcmp(argv[i], "-nooutputtuple") == 0 ||
+ strcmp(argv[i], "-noproxyimport") == 0 ||
+ strcmp(argv[i], "-nosafecstrings") == 0 ||
+ strcmp(argv[i], "-old_repr") == 0 ||
+ strcmp(argv[i], "-oldrepr") == 0 ||
+ strcmp(argv[i], "-outputtuple") == 0 ||
+ strcmp(argv[i], "-proxydel") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is no longer available.\n", argv[i]);
+ Swig_mark_arg(i);
+ Exit(EXIT_FAILURE);
+ }
+
+ }
+ }
+
+ if (builtin && !shadow) {
+ Printf(stderr, "Incompatible options -builtin and -noproxy specified.\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ if (fastproxy) {
+ Preprocessor_define("SWIGPYTHON_FASTPROXY", 0);
+ }
+
+ if (doxygen)
+ doxygenTranslator = new PyDocConverter(doxygen_translator_flags);
+
+ if (!global_name)
+ global_name = NewString("cvar");
+ Preprocessor_define("SWIGPYTHON 1", 0);
+ SWIG_typemap_lang("python");
+ SWIG_config_file("python.swg");
+ allow_overloading();
+ }
+
+
+ /* ------------------------------------------------------------
+ * top()
+ * ------------------------------------------------------------ */
+
+ virtual int top(Node *n) {
+ /* check if directors are enabled for this module. note: this
+ * is a "master" switch, without which no director code will be
+ * emitted. %feature("director") statements are also required
+ * to enable directors for individual classes or methods.
+ *
+ * use %module(directors="1") modulename at the start of the
+ * interface file to enable director generation.
+ */
+ String *mod_docstring = NULL;
+ String *moduleimport = NULL;
+ {
+ Node *mod = Getattr(n, "module");
+ if (mod) {
+ Node *options = Getattr(mod, "options");
+ if (options) {
+ int dirprot = 0;
+ if (Getattr(options, "dirprot")) {
+ dirprot = 1;
+ }
+ if (Getattr(options, "nodirprot")) {
+ dirprot = 0;
+ }
+ if (Getattr(options, "directors")) {
+ allow_directors();
+ if (dirprot)
+ allow_dirprot();
+ }
+ if (Getattr(options, "threads")) {
+ threads = 1;
+ }
+ if (Getattr(options, "castmode")) {
+ castmode = 1;
+ }
+ if (Getattr(options, "nocastmode")) {
+ Printf(stderr, "Deprecated module option: %s. This option is no longer supported.\n", "nocastmode");
+ Exit(EXIT_FAILURE);
+ }
+ if (Getattr(options, "extranative")) {
+ extranative = 1;
+ }
+ if (Getattr(options, "noextranative")) {
+ Printf(stderr, "Deprecated module option: %s. This option is no longer supported.\n", "noextranative");
+ Exit(EXIT_FAILURE);
+ }
+ if (Getattr(options, "outputtuple")) {
+ Printf(stderr, "Deprecated module option: %s. This option is no longer supported.\n", "outputtuple");
+ Exit(EXIT_FAILURE);
+ }
+ if (Getattr(options, "nooutputtuple")) {
+ Printf(stderr, "Deprecated module option: %s. This option is no longer supported.\n", "nooutputtuple");
+ Exit(EXIT_FAILURE);
+ }
+ mod_docstring = Getattr(options, "docstring");
+ package = Getattr(options, "package");
+ moduleimport = Getattr(options, "moduleimport");
+ }
+ }
+ }
+
+ /* Set comparison with none for ConstructorToFunction */
+ setSubclassInstanceCheck(NewString("$arg != Py_None"));
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+ String *outfile_h = !no_header_file ? Getattr(n, "outfile_h") : 0;
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+ f_directors_h = NewString("");
+ f_directors = NewString("");
+ builtin_getset = NewHash();
+ builtin_closures = NewHash();
+ builtin_closures_code = NewString("");
+ class_members = NewHash();
+ builtin_methods = NewString("");
+ builtin_default_unref = NewString("delete $self;");
+
+ if (builtin) {
+ f_builtins = NewString("");
+ }
+
+ if (directorsEnabled()) {
+ if (!no_header_file) {
+ f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
+ if (!f_runtime_h) {
+ FileErrorDisplay(outfile_h);
+ Exit(EXIT_FAILURE);
+ }
+ } else {
+ f_runtime_h = f_runtime;
+ }
+ }
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+
+ const_code = NewString("");
+ methods = NewString("");
+ methods_proxydocs = NewString("");
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "PYTHON");
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+ }
+
+ if (nothreads) {
+ Printf(f_runtime, "#define SWIG_PYTHON_NO_THREADS\n");
+ } else if (threads) {
+ Printf(f_runtime, "#define SWIG_PYTHON_THREADS\n");
+ }
+
+ if (!dirvtable) {
+ Printf(f_runtime, "#define SWIG_PYTHON_DIRECTOR_NO_VTABLE\n");
+ }
+
+ if (nortti) {
+ Printf(f_runtime, "#ifndef SWIG_DIRECTOR_NORTTI\n");
+ Printf(f_runtime, "#define SWIG_DIRECTOR_NORTTI\n");
+ Printf(f_runtime, "#endif\n");
+ }
+
+ if (castmode) {
+ Printf(f_runtime, "#define SWIG_CASTRANK_MODE\n");
+ Printf(f_runtime, "#define SWIG_PYTHON_CAST_MODE\n");
+ }
+
+ if (extranative) {
+ Printf(f_runtime, "#define SWIG_PYTHON_EXTRA_NATIVE_CONTAINERS\n");
+ }
+
+ if (builtin) {
+ Printf(f_runtime, "#define SWIGPYTHON_BUILTIN\n");
+ }
+
+ if (fastproxy) {
+ Printf(f_runtime, "#define SWIGPYTHON_FASTPROXY\n");
+ }
+
+ Printf(f_runtime, "\n");
+
+ Printf(f_header, "#ifdef SWIG_TypeQuery\n");
+ Printf(f_header, "# undef SWIG_TypeQuery\n");
+ Printf(f_header, "#endif\n");
+ Printf(f_header, "#define SWIG_TypeQuery SWIG_Python_TypeQuery\n");
+
+
+ /* Set module name */
+ module = Copy(Getattr(n, "name"));
+ mainmodule = Getattr(n, "name");
+
+ if (directorsEnabled()) {
+ Swig_banner(f_directors_h);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module);
+ Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module);
+ if (dirprot_mode()) {
+ Printf(f_directors_h, "#include <map>\n");
+ Printf(f_directors_h, "#include <string>\n\n");
+ }
+
+ Printf(f_directors, "\n\n");
+ Printf(f_directors, "/* ---------------------------------------------------\n");
+ Printf(f_directors, " * C++ director class methods\n");
+ Printf(f_directors, " * --------------------------------------------------- */\n\n");
+ if (outfile_h) {
+ String *filename = Swig_file_filename(outfile_h);
+ Printf(f_directors, "#include \"%s\"\n\n", filename);
+ Delete(filename);
+ }
+ }
+
+ /* If shadow classing is enabled, we're going to change the module name to "_module" */
+ String *default_import_code = NewString("");
+ if (shadow) {
+ String *filen = NewStringf("%s%s.py", SWIG_output_directory(), Char(module));
+ // If we don't have an interface then change the module name X to _X
+ if (interface)
+ module = interface;
+ else
+ Insert(module, 0, "_");
+ if ((f_shadow_py = NewFile(filen, "w", SWIG_output_files())) == 0) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(filen);
+ filen = NULL;
+
+ f_shadow = NewString("");
+ f_shadow_begin = NewString("");
+ f_shadow_imports = NewHash();
+ f_shadow_after_begin = NewString("");
+ f_shadow_stubs = NewString("");
+
+ Swig_register_filebyname("shadow", f_shadow);
+ Swig_register_filebyname("python", f_shadow);
+
+ if (!builtin) {
+ /* Import the low-level C/C++ module. This should be a relative import,
+ * since the shadow module may also have been imported by a relative
+ * import, and there is thus no guarantee that the low-level C/C++ module is on
+ * sys.path. Relative imports must be explicitly specified from 2.6.0
+ * onwards (implicit relative imports raised a DeprecationWarning in 2.6,
+ * and fail in 2.7 onwards).
+ *
+ * First check for __package__ which is available from 2.6 onwards, see PEP366.
+ * Next try determine the shadow wrapper's package based on the __name__ it
+ * was given by the importer that loaded it.
+ * If the module is in a package, load the low-level C/C++ module from the
+ * same package, otherwise load it as a global module.
+ */
+ Printv(default_import_code, "# Import the low-level C/C++ module\n", NULL);
+ Printv(default_import_code, "if __package__ or \".\" in __name__:\n", NULL);
+ Printv(default_import_code, tab4, "from . import ", module, "\n", NULL);
+ Printv(default_import_code, "else:\n", NULL);
+ Printv(default_import_code, tab4, "import ", module, "\n", NULL);
+ } else {
+ Printv(default_import_code, "# Pull in all the attributes from the low-level C/C++ module\n", NULL);
+ Printv(default_import_code, "if __package__ or \".\" in __name__:\n", NULL);
+ Printv(default_import_code, tab4, "from .", module, " import *\n", NULL);
+ Printv(default_import_code, "else:\n", NULL);
+ Printv(default_import_code, tab4, "from ", module, " import *\n", NULL);
+ }
+
+ if (!builtin) {
+ /* Need builtins to qualify names like Exception that might also be
+ defined in this module (try both Python 3 and Python 2 names) */
+ Printv(f_shadow, "try:\n", tab4, "import builtins as __builtin__\n", "except ImportError:\n", tab4, "import __builtin__\n", NULL);
+ }
+
+ if (!builtin && fastproxy) {
+ Printf(f_shadow, "\n");
+ Printf(f_shadow, "_swig_new_instance_method = %s.SWIG_PyInstanceMethod_New\n", module);
+ Printf(f_shadow, "_swig_new_static_method = %s.SWIG_PyStaticMethod_New\n", module);
+ }
+
+ if (!builtin) {
+ Printv(f_shadow, "\n",
+ "def _swig_repr(self):\n",
+ tab4, "try:\n",
+ tab4, tab4, "strthis = \"proxy of \" + self.this.__repr__()\n",
+ tab4, "except __builtin__.Exception:\n",
+ tab4, tab4, "strthis = \"\"\n",
+ tab4, "return \"<%s.%s; %s >\" % (self.__class__.__module__, self.__class__.__name__, strthis,)\n\n", NIL);
+
+ Printv(f_shadow, "\n",
+ "def _swig_setattr_nondynamic_instance_variable(set):\n",
+ tab4, "def set_instance_attr(self, name, value):\n",
+ tab4, tab4, "if name == \"this\":\n",
+ tab4, tab4, tab4, "set(self, name, value)\n",
+ tab4, tab4, "elif name == \"thisown\":\n",
+ tab4, tab4, tab4, "self.this.own(value)\n",
+ tab4, tab4, "elif hasattr(self, name) and isinstance(getattr(type(self), name), property):\n",
+ tab4, tab4, tab4, "set(self, name, value)\n",
+ tab4, tab4, "else:\n",
+ tab4, tab4, tab4, "raise AttributeError(\"You cannot add instance attributes to %s\" % self)\n",
+ tab4, "return set_instance_attr\n\n", NIL);
+
+ Printv(f_shadow, "\n",
+ "def _swig_setattr_nondynamic_class_variable(set):\n",
+ tab4, "def set_class_attr(cls, name, value):\n",
+ tab4, tab4, "if hasattr(cls, name) and not isinstance(getattr(cls, name), property):\n",
+ tab4, tab4, tab4, "set(cls, name, value)\n",
+ tab4, tab4, "else:\n",
+ tab4, tab4, tab4, "raise AttributeError(\"You cannot add class attributes to %s\" % cls)\n",
+ tab4, "return set_class_attr\n\n", NIL);
+
+ Printv(f_shadow, "\n",
+ "def _swig_add_metaclass(metaclass):\n",
+ tab4, "\"\"\"Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass\"\"\"\n",
+ tab4, "def wrapper(cls):\n",
+ tab4, tab4, "return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())\n",
+ tab4, "return wrapper\n\n", NIL);
+
+ Printv(f_shadow, "\n",
+ "class _SwigNonDynamicMeta(type):\n",
+ tab4, "\"\"\"Meta class to enforce nondynamic attributes (no new attributes) for a class\"\"\"\n",
+ tab4, "__setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)\n",
+ "\n", NIL);
+
+ Printv(f_shadow, "\n", NIL);
+
+ if (directorsEnabled())
+ Printv(f_shadow, "import weakref\n\n", NIL);
+ }
+ }
+ // Include some information in the code
+ Printf(f_header, "\n/*-----------------------------------------------\n @(target):= %s.so\n\
+ ------------------------------------------------*/\n", module);
+
+ Printf(f_header, "#if PY_VERSION_HEX >= 0x03000000\n");
+ Printf(f_header, "# define SWIG_init PyInit_%s\n\n", module);
+ Printf(f_header, "#else\n");
+ Printf(f_header, "# define SWIG_init init%s\n\n", module);
+ Printf(f_header, "#endif\n");
+ Printf(f_header, "#define SWIG_name \"%s\"\n", module);
+
+ Printf(f_wrappers, "#ifdef __cplusplus\n");
+ Printf(f_wrappers, "extern \"C\" {\n");
+ Printf(f_wrappers, "#endif\n");
+ Append(const_code, "static swig_const_info swig_const_table[] = {\n");
+ Append(methods, "static PyMethodDef SwigMethods[] = {\n");
+ Append(methods_proxydocs, "static PyMethodDef SwigMethods_proxydocs[] = {\n");
+
+ /* the method exported for replacement of new.instancemethod in Python 3 */
+ add_pyinstancemethod_new();
+ add_pystaticmethod_new();
+
+ if (builtin) {
+ SwigType *s = NewString("SwigPyObject");
+ SwigType_add_pointer(s);
+ SwigType_remember(s);
+ Delete(s);
+ }
+
+ /* emit code */
+ Language::top(n);
+
+ if (directorsEnabled()) {
+ // Insert director runtime into the f_runtime file (make it occur before %header section)
+ Swig_insert_file("director_common.swg", f_runtime);
+ Swig_insert_file("director.swg", f_runtime);
+ }
+
+ /* Close language module */
+ Append(methods, "\t { NULL, NULL, 0, NULL }\n");
+ Append(methods, "};\n");
+ Printf(f_wrappers, "%s\n", methods);
+ Append(methods_proxydocs, "\t { NULL, NULL, 0, NULL }\n");
+ Append(methods_proxydocs, "};\n");
+ if ((fastproxy && !builtin) || have_fast_proxy_static_member_method_callback)
+ Printf(f_wrappers, "%s\n", methods_proxydocs);
+
+ if (builtin) {
+ Dump(f_builtins, f_wrappers);
+ }
+
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+
+ Append(const_code, "{0, 0, 0, 0.0, 0, 0}};\n");
+ Printf(f_wrappers, "%s\n", const_code);
+
+ if (have_fast_proxy_static_member_method_callback)
+ Printf(f_init, " SWIG_Python_FixMethods(SwigMethods_proxydocs, swig_const_table, swig_types, swig_type_initial);\n\n");
+
+ initialize_threads(f_init);
+
+ Printf(f_init, "#if PY_VERSION_HEX >= 0x03000000\n");
+ Printf(f_init, " return m;\n");
+ Printf(f_init, "#else\n");
+ Printf(f_init, " return;\n");
+ Printf(f_init, "#endif\n");
+ Printf(f_init, "}\n");
+
+ Printf(f_wrappers, "#ifdef __cplusplus\n");
+ Printf(f_wrappers, "}\n");
+ Printf(f_wrappers, "#endif\n");
+
+ if (shadow) {
+ Swig_banner_target_lang(f_shadow_py, "#");
+
+ if (mod_docstring) {
+ if (Len(mod_docstring)) {
+ const char *triple_double = "\"\"\"";
+ // follow PEP257 rules: https://www.python.org/dev/peps/pep-0257/
+ // reported by pep257: https://github.com/GreenSteam/pep257
+ bool multi_line_ds = Strchr(mod_docstring, '\n') != 0;
+ Printv(f_shadow_py, "\n", triple_double, multi_line_ds ? "\n":"", mod_docstring, multi_line_ds ? "\n":"", triple_double, "\n", NIL);
+ }
+ Delete(mod_docstring);
+ mod_docstring = NULL;
+ }
+
+ if (Len(f_shadow_begin) > 0)
+ Printv(f_shadow_py, "\n", f_shadow_begin, "\n", NIL);
+
+ Printv(f_shadow_py, "\nfrom sys import version_info as _swig_python_version_info\n", NULL);
+
+ if (Len(f_shadow_after_begin) > 0)
+ Printv(f_shadow_py, f_shadow_after_begin, "\n", NIL);
+
+ if (moduleimport) {
+ Replaceall(moduleimport, "$module", module);
+ Printv(f_shadow_py, moduleimport, "\n", NIL);
+ } else {
+ Printv(f_shadow_py, default_import_code, NIL);
+ }
+
+ if (Len(f_shadow) > 0)
+ Printv(f_shadow_py, "\n", f_shadow, "\n", NIL);
+ if (Len(f_shadow_stubs) > 0)
+ Printv(f_shadow_py, f_shadow_stubs, "\n", NIL);
+ Delete(f_shadow_py);
+ }
+
+ /* Close all of the files */
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+
+ if (directorsEnabled()) {
+ Dump(f_directors_h, f_runtime_h);
+ Printf(f_runtime_h, "\n");
+ Printf(f_runtime_h, "#endif\n");
+ if (f_runtime_h != f_begin)
+ Delete(f_runtime_h);
+ Dump(f_directors, f_begin);
+ }
+
+ Dump(f_wrappers, f_begin);
+ if (builtin && builtin_bases_needed)
+ Printf(f_begin, "static PyTypeObject *builtin_bases[%d];\n\n", max_bases + 2);
+ Wrapper_pretty_print(f_init, f_begin);
+
+ Delete(default_import_code);
+ Delete(f_shadow_after_begin);
+ Delete(f_shadow_imports);
+ Delete(f_shadow_begin);
+ Delete(f_shadow);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_builtins);
+ Delete(f_init);
+ Delete(f_directors);
+ Delete(f_directors_h);
+ Delete(f_runtime);
+ Delete(f_begin);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * Emit the wrapper for PyInstanceMethod_New to MethodDef array.
+ * This wrapper is used to implement -fastproxy,
+ * as a replacement of new.instancemethod in Python 3.
+ * ------------------------------------------------------------ */
+ int add_pyinstancemethod_new() {
+ if (!builtin && fastproxy) {
+ String *name = NewString("SWIG_PyInstanceMethod_New");
+ String *line = NewString("");
+ Printf(line, "\t { \"%s\", %s, METH_O, NULL},\n", name, name);
+ Append(methods, line);
+ Append(methods_proxydocs, line);
+ Delete(line);
+ Delete(name);
+ }
+ return 0;
+ }
+
+ /* ------------------------------------------------------------
+ * Emit the wrapper for PyStaticMethod_New to MethodDef array.
+ * This wrapper is used to ensure the correct documentation is
+ * generated for static methods when using -fastproxy
+ * ------------------------------------------------------------ */
+ int add_pystaticmethod_new() {
+ if (!builtin && fastproxy) {
+ String *name = NewString("SWIG_PyStaticMethod_New");
+ String *line = NewString("");
+ Printf(line, "\t { \"%s\", %s, METH_O, NULL},\n", name, name);
+ Append(methods, line);
+ Append(methods_proxydocs, line);
+ Delete(line);
+ Delete(name);
+ }
+ return 0;
+ }
+
+ /* ------------------------------------------------------------
+ * subpkg_tail()
+ *
+ * Return the name of 'other' package relative to 'base'.
+ *
+ * 1. If 'other' is a sub-package of 'base', returns the 'other' relative to
+ * 'base'.
+ * 2. If 'other' and 'base' are equal, returns empty string "".
+ * 3. In any other case, NULL pointer is returned.
+ *
+ * The 'base' and 'other' are expected to be fully qualified names.
+ *
+ * NOTE: none of 'base' nor 'other' can be null.
+ *
+ * Examples:
+ *
+ * # base other tail
+ * -- ---- ----- ----
+ * 1 "Foo" "Foo.Bar" -> "Bar"
+ * 2 "Foo" "Foo." -> ""
+ * 3 "Foo" "FooB.ar" -> NULL
+ * 4 "Foo.Bar" "Foo.Bar" -> ""
+ * 5 "Foo.Bar" "Foo" -> NULL
+ * 6 "Foo.Bar" "Foo.Gez" -> NULL
+ *
+ * NOTE: the example #2 is actually a syntax error (at input). I believe
+ * swig parser prevents us from this case happening here.
+ * ------------------------------------------------------------ */
+
+ static String *subpkg_tail(const String *base, const String *other) {
+ int baselen = Len(base);
+ int otherlen = Len(other);
+
+ if (Strncmp(other, base, baselen) == 0) {
+ if ((baselen < otherlen) && (Char(other))[baselen] == '.') {
+ return NewString((Char(other)) + baselen + 1);
+ } else if (baselen == otherlen) {
+ return NewString("");
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * abs_import_directive_string()
+ *
+ * Return a string containing python code to import module.
+ *
+ * pkg package name or the module being imported
+ * mod module name of the module being imported
+ * pfx optional prefix to module name
+ *
+ * NOTE: keep this function consistent with abs_import_name_string().
+ * ------------------------------------------------------------ */
+
+ static String *abs_import_directive_string(const String *pkg, const String *mod, const char *pfx = "") {
+ String *out = NewString("");
+
+ if (pkg && *Char(pkg)) {
+ Printf(out, "import %s.%s%s\n", pkg, pfx, mod);
+ } else {
+ Printf(out, "import %s%s\n", pfx, mod);
+ }
+ return out;
+ }
+
+ /* ------------------------------------------------------------
+ * rel_import_directive_string()
+ *
+ * Return a string containing python code to import module that
+ * is potentially within a package.
+ *
+ * mainpkg package name of the module which imports the other module
+ * pkg package name or the module being imported
+ * mod module name of the module being imported
+ * pfx optional prefix to module name
+ *
+ * NOTE: keep this function consistent with rel_import_name_string().
+ * ------------------------------------------------------------ */
+
+ static String *rel_import_directive_string(const String *mainpkg, const String *pkg, const String *mod, const char *pfx = "") {
+
+ /* NOTE: things are not so trivial. This is what we do here (by examples):
+ *
+ * 0. To import module 'foo', which is not in any package, we do absolute
+ * import:
+ *
+ * import foo
+ *
+ * 1. To import 'pkg1.pkg2.foo', when mainpkg != "pkg1" and
+ * mainpkg != "pkg1.pkg2" or when mainpkg is not given we do absolute
+ * import:
+ *
+ * import pkg1.pkg2.foo
+ *
+ * 2. To import module pkg1.foo, when mainpkg == "pkg1", we do:
+ *
+ * - for py3 = 0:
+ *
+ * import foo
+ *
+ * - for py3 = 1:
+ *
+ * from . import foo
+ *
+ * 3. To import "pkg1.pkg2.pkg3.foo", when mainpkg = "pkg1", we do:
+ *
+ * - for py3 == 0:
+ *
+ * import pkg2.pkg3.foo
+ *
+ * - for py3 == 1:
+ *
+ * from . import pkg2 # [1]
+ * import pkg1.pkg2.pkg3.foo
+ *
+ * NOTE: [1] is necessary for pkg2.foo to be present in the importing module
+ */
+
+ String *apkg = 0; // absolute (FQDN) package name of pkg
+ String *rpkg = 0; // relative package name
+ int py3_rlen1 = 0; // length of 1st level sub-package name, used by py3
+ String *out = NewString("");
+
+ if (pkg && *Char(pkg)) {
+ if (mainpkg) {
+ String *tail = subpkg_tail(mainpkg, pkg);
+ if (tail) {
+ if (*Char(tail)) {
+ rpkg = NewString(tail);
+ const char *py3_end1 = Strchr(rpkg, '.');
+ if (!py3_end1)
+ py3_end1 = (Char(rpkg)) + Len(rpkg);
+ py3_rlen1 = (int)(py3_end1 - Char(rpkg));
+ } else {
+ rpkg = NewString("");
+ }
+ Delete(tail);
+ } else {
+ apkg = NewString(pkg);
+ }
+ } else {
+ apkg = NewString(pkg);
+ }
+ } else {
+ apkg = NewString("");
+ }
+
+ if (apkg) {
+ Printf(out, "import %s%s%s%s\n", apkg, *Char(apkg) ? "." : "", pfx, mod);
+ Delete(apkg);
+ } else {
+ if (py3_rlen1)
+ Printf(out, "from . import %.*s\n", py3_rlen1, rpkg);
+ Printf(out, "from .%s import %s%s\n", rpkg, pfx, mod);
+ Delete(rpkg);
+ }
+ return out;
+ }
+
+ /* ------------------------------------------------------------
+ * import_directive_string()
+ * ------------------------------------------------------------ */
+
+ static String *import_directive_string(const String *mainpkg, const String *pkg, const String *mod, const char *pfx = "") {
+ if (!relativeimport) {
+ return abs_import_directive_string(pkg, mod, pfx);
+ } else {
+ return rel_import_directive_string(mainpkg, pkg, mod, pfx);
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * abs_import_name_string()
+ *
+ * Return a string with the name of a symbol (perhaps imported
+ * from external module by absolute import directive).
+ *
+ * mainpkg package name of current module
+ * mainmod module name of current module
+ * pkg package name of (perhaps other) module
+ * mod module name of (perhaps other) module
+ * sym symbol name
+ *
+ * NOTE: mainmod, mod, and sym can't be NULL.
+ * NOTE: keep this function consistent with abs_import_directive_string()
+ * ------------------------------------------------------------ */
+
+ static String *abs_import_name_string(const String *mainpkg, const String *mainmod, const String *pkg, const String *mod, const String *sym) {
+ String *out = NewString("");
+ if (pkg && *Char(pkg)) {
+ if (mainpkg && *Char(mainpkg)) {
+ if (Strcmp(mainpkg,pkg) != 0 || Strcmp(mainmod, mod) != 0) {
+ Printf(out, "%s.%s.", pkg, mod);
+ }
+ } else {
+ Printf(out, "%s.%s.", pkg, mod);
+ }
+ } else if ((mainpkg && *Char(mainpkg)) || Strcmp(mainmod, mod) != 0) {
+ Printf(out, "%s.", mod);
+ }
+ Append(out, sym);
+ return out;
+ }
+
+ /* ------------------------------------------------------------
+ * rel_import_name_string()
+ *
+ * Return a string with the name of a symbol (perhaps imported
+ * from external module by relative import directive).
+ *
+ * mainpkg package name of current module
+ * mainmod module name of current module
+ * pkg package name of (perhaps other) module
+ * mod module name of (perhaps other) module
+ * sym symbol name
+ *
+ * NOTE: mainmod, mod, and sym can't be NULL.
+ * NOTE: keep this function consistent with rel_import_directive_string()
+ * ------------------------------------------------------------ */
+
+ static String *rel_import_name_string(const String *mainpkg, const String *mainmod, const String *pkg, const String *mod, const String *sym) {
+ String *out = NewString("");
+ if (pkg && *Char(pkg)) {
+ String *tail = 0;
+ if (mainpkg)
+ tail = subpkg_tail(mainpkg, pkg);
+ if (!tail)
+ tail = NewString(pkg);
+ if (*Char(tail)) {
+ Printf(out, "%s.%s.", tail, mod);
+ } else if (Strcmp(mainmod, mod) != 0) {
+ Printf(out, "%s.", mod);
+ }
+ Delete(tail);
+ } else if ((mainpkg && *Char(mainpkg)) || Strcmp(mainmod, mod) != 0) {
+ Printf(out, "%s.", mod);
+ }
+ Append(out, sym);
+ return out;
+ }
+
+ /* ------------------------------------------------------------
+ * import_name_string()
+ * ------------------------------------------------------------ */
+
+ static String *import_name_string(const String *mainpkg, const String *mainmod, const String *pkg, const String *mod, const String *sym) {
+ if (!relativeimport) {
+ return abs_import_name_string(mainpkg,mainmod,pkg,mod,sym);
+ } else {
+ return rel_import_name_string(mainpkg,mainmod,pkg,mod,sym);
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * importDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int importDirective(Node *n) {
+ if (shadow) {
+ String *modname = Getattr(n, "module");
+
+ if (modname) {
+ // Find the module node for this imported module. It should be the
+ // first child but search just in case.
+ Node *mod = firstChild(n);
+ while (mod && Strcmp(nodeType(mod), "module") != 0)
+ mod = nextSibling(mod);
+
+ Node *options = Getattr(mod, "options");
+ String *pkg = options ? Getattr(options, "package") : 0;
+
+ if (!options || (!Getattr(options, "noshadow") && !Getattr(options, "noproxy"))) {
+ String *_import = import_directive_string(package, pkg, modname, "_");
+ if (!GetFlagAttr(f_shadow_imports, _import)) {
+ String *import = import_directive_string(package, pkg, modname);
+ Printf(builtin ? f_shadow_after_begin : f_shadow, "%s", import);
+ Delete(import);
+ SetFlag(f_shadow_imports, _import);
+ }
+ Delete(_import);
+ }
+
+ }
+ }
+ return Language::importDirective(n);
+ }
+
+ /* ------------------------------------------------------------
+ * funcCall()
+ *
+ * Emit shadow code to call a function in the extension
+ * module. Using proper argument and calling style for
+ * given node n.
+ * ------------------------------------------------------------ */
+ String *funcCall(String *name, String *parms) {
+ String *str = NewString("");
+
+ Printv(str, module, ".", name, "(", parms, ")", NIL);
+ return str;
+ }
+
+ /* ------------------------------------------------------------
+ * indent_pythoncode()
+ *
+ * Format (indent) Python code.
+ * Remove leading whitespace from 'code' and re-indent using
+ * the indentation string in 'indent'.
+ * ------------------------------------------------------------ */
+
+ String *indent_pythoncode(const String *code, const_String_or_char_ptr indent, String *file, int line, const char *directive_name) {
+ String *out = NewString("");
+ String *temp;
+ char *t;
+ if (!indent)
+ indent = "";
+
+ temp = NewString(code);
+
+ t = Char(temp);
+ if (*t == '{') {
+ Delitem(temp, 0);
+ Delitem(temp, DOH_END);
+ }
+
+ /* Split the input text into lines */
+ List *clist = SplitLines(temp);
+ Delete(temp);
+
+ // Line number within the pythoncode.
+ int py_line = 0;
+
+ String *initial = 0;
+ Iterator si;
+
+ /* Get the initial indentation. Skip lines which only contain whitespace
+ * and/or a comment, as the indentation of those doesn't matter:
+ *
+ * A logical line that contains only spaces, tabs, formfeeds and
+ * possibly a comment, is ignored (i.e., no NEWLINE token is
+ * generated).
+ *
+ * see:
+ * https://docs.python.org/2/reference/lexical_analysis.html#blank-lines
+ * https://docs.python.org/3/reference/lexical_analysis.html#blank-lines
+ */
+ for (si = First(clist); si.item; si = Next(si), ++py_line) {
+ const char *c = Char(si.item);
+ int i;
+ for (i = 0; isspace((unsigned char)c[i]); i++) {
+ // Scan forward until we find a non-space (which may be a null byte).
+ }
+ char ch = c[i];
+ if (ch && ch != '#') {
+ // Found a line with actual content.
+ initial = NewStringWithSize(c, i);
+ break;
+ }
+ if (ch) {
+ Printv(out, indent, c, NIL);
+ }
+ Putc('\n', out);
+ }
+
+ // Process remaining lines.
+ for ( ; si.item; si = Next(si), ++py_line) {
+ const char *c = Char(si.item);
+ // If no prefixed line was found, the above loop should have completed.
+ assert(initial);
+
+ int i;
+ for (i = 0; isspace((unsigned char)c[i]); i++) {
+ // Scan forward until we find a non-space (which may be a null byte).
+ }
+ char ch = c[i];
+ if (!ch) {
+ // Line is just whitespace - emit an empty line.
+ Putc('\n', out);
+ continue;
+ }
+
+ if (ch == '#') {
+ // Comment - the indentation doesn't matter to python, but try to
+ // adjust the whitespace for the benefit of human readers (though SWIG
+ // currently seems to always remove any whitespace before a '#' before
+ // we get here, in which case we'll just leave the comment at the start
+ // of the line).
+ if (i >= Len(initial)) {
+ Printv(out, indent, NIL);
+ }
+
+ Printv(out, c + i, "\n", NIL);
+ continue;
+ }
+
+ if (i < Len(initial)) {
+ // There's non-whitespace in the initial prefix of this line.
+ Swig_error(file, line, "Line indented less than expected (line %d of %s) as no line should be indented less than the indentation in line 1\n", py_line, directive_name);
+ Printv(out, indent, c, "\n", NIL);
+ } else {
+ if (memcmp(c, Char(initial), Len(initial)) == 0) {
+ // Prefix matches initial, so just remove it.
+ Printv(out, indent, c + Len(initial), "\n", NIL);
+ continue;
+ }
+ Swig_warning(WARN_PYTHON_INDENT_MISMATCH,
+ file, line, "Whitespace indentation is inconsistent compared to earlier lines (line %d of %s)\n", py_line, directive_name);
+ // To avoid gratuitously breaking interface files which worked with
+ // SWIG <= 3.0.5, we remove a prefix of the same number of bytes for
+ // lines which start with different whitespace to the line we got
+ // 'initial' from.
+ Printv(out, indent, c + Len(initial), "\n", NIL);
+ }
+ }
+ Delete(clist);
+ return out;
+ }
+
+ /* ------------------------------------------------------------
+ * indent_docstring()
+ *
+ * Format (indent) a Python docstring.
+ * Remove leading whitespace from 'code' and re-indent using
+ * the indentation string in 'indent'.
+ * ------------------------------------------------------------ */
+
+ String *indent_docstring(const String *code, const_String_or_char_ptr indent) {
+ String *out = NewString("");
+ String *temp;
+ char *t;
+ if (!indent)
+ indent = "";
+
+ temp = NewString(code);
+
+ t = Char(temp);
+ if (*t == '{') {
+ Delitem(temp, 0);
+ Delitem(temp, DOH_END);
+ }
+
+ /* Split the input text into lines */
+ List *clist = SplitLines(temp);
+ Delete(temp);
+
+ Iterator si;
+
+ int truncate_characters_count = INT_MAX;
+ for (si = First(clist); si.item; si = Next(si)) {
+ const char *c = Char(si.item);
+ int i;
+ for (i = 0; isspace((unsigned char)c[i]); i++) {
+ // Scan forward until we find a non-space (which may be a null byte).
+ }
+ char ch = c[i];
+ if (ch) {
+ // Found a line which isn't just whitespace
+ if (i < truncate_characters_count)
+ truncate_characters_count = i;
+ }
+ }
+
+ if (truncate_characters_count == INT_MAX)
+ truncate_characters_count = 0;
+
+ for (si = First(clist); si.item; si = Next(si)) {
+ const char *c = Char(si.item);
+
+ int i;
+ for (i = 0; isspace((unsigned char)c[i]); i++) {
+ // Scan forward until we find a non-space (which may be a null byte).
+ }
+ char ch = c[i];
+ if (!ch) {
+ // Line is just whitespace - emit an empty line.
+ Putc('\n', out);
+ continue;
+ }
+
+ Printv(out, indent, c + truncate_characters_count, "\n", NIL);
+ }
+ Delete(clist);
+ return out;
+ }
+
+ /* ------------------------------------------------------------
+ * autodoc level declarations
+ * ------------------------------------------------------------ */
+
+ enum autodoc_l {
+ NO_AUTODOC = -2, // no autodoc
+ STRING_AUTODOC = -1, // use provided string
+ NAMES_AUTODOC = 0, // only parameter names
+ TYPES_AUTODOC = 1, // parameter names and types
+ EXTEND_AUTODOC = 2, // extended documentation and parameter names
+ EXTEND_TYPES_AUTODOC = 3 // extended documentation and parameter types + names
+ };
+
+
+ autodoc_l autodoc_level(String *autodoc) {
+ autodoc_l dlevel = NO_AUTODOC;
+ char *c = Char(autodoc);
+ if (c) {
+ if (isdigit(c[0])) {
+ dlevel = (autodoc_l) atoi(c);
+ } else {
+ if (strcmp(c, "extended") == 0) {
+ dlevel = EXTEND_AUTODOC;
+ } else {
+ dlevel = STRING_AUTODOC;
+ }
+ }
+ }
+ return dlevel;
+ }
+
+
+ /* ------------------------------------------------------------
+ * have_docstring()
+ *
+ * Check if there is a docstring directive and it has text,
+ * or there is an autodoc flag set
+ * ------------------------------------------------------------ */
+
+ bool have_docstring(Node *n) {
+ String *str = Getattr(n, "feature:docstring");
+ return ((str && Len(str) > 0)
+ || (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"))
+ || (doxygen && doxygenTranslator->hasDocumentation(n))
+ );
+ }
+
+ /* ------------------------------------------------------------
+ * build_combined_docstring()
+ *
+ * Build the full docstring:
+ * Use the docstring if there is one present otherwise
+ * use the Doxygen comment if there is one present.
+ * Ignore autodoc if there is a Doxygen comment, otherwise
+ * create the autodoc string and append to any docstring.
+ *
+ * Return new string to be deleted by caller (never NIL but
+ * may be empty if there is no docstring).
+ * ------------------------------------------------------------ */
+
+ String *build_combined_docstring(Node *n, autodoc_t ad_type, const String *indent = "", bool low_level = false) {
+ bool add_autodoc = true;
+ String *docstr = Getattr(n, "feature:docstring");
+ if (docstr) {
+ // Simplify the code below by just ignoring empty docstrings.
+ if (!Len(docstr))
+ docstr = NULL;
+ else
+ docstr = Copy(docstr);
+ }
+
+ if (docstr) {
+ char *t = Char(docstr);
+ if (*t == '{') {
+ Delitem(docstr, 0);
+ Delitem(docstr, DOH_END);
+ }
+ }
+
+ if (!docstr) {
+ if (doxygen && doxygenTranslator->hasDocumentation(n)) {
+ docstr = Getattr(n, "python:docstring");
+ if (!docstr) {
+ docstr = doxygenTranslator->getDocumentation(n, 0);
+
+ // Avoid rebuilding it again the next time: notice that we can't do
+ // this for the combined doc string as autodoc part of it depends on
+ // the sym:name of the node and it is changed while handling it, so
+ // the cached results become incorrect. But Doxygen docstring only
+ // depends on the comment which is not going to change, so we can
+ // safely cache it.
+ Setattr(n, "python:docstring", Copy(docstr));
+ } else {
+ // Must copy here since if the docstring is multi-line, the String*
+ // here will get Deleted below, which is bad if it is a pointer to
+ // the cached object!
+ docstr = Copy(docstr);
+ }
+ add_autodoc = false;
+ }
+ }
+
+ if (add_autodoc && Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc")) {
+ String *autodoc = make_autodoc(n, ad_type, low_level);
+ if (autodoc && Len(autodoc) > 0) {
+ if (docstr) {
+ Append(autodoc, "\n");
+ Append(autodoc, docstr);
+ }
+
+ String *tmp = autodoc;
+ autodoc = docstr;
+ docstr = tmp;
+ }
+
+ Delete(autodoc);
+ }
+
+ if (!docstr)
+ docstr = NewString("");
+
+ // If there is more than one line then make docstrings like this:
+ //
+ // """
+ // This is line1
+ // And here is line2 followed by the rest of them
+ // """
+ //
+ // otherwise, put it all on a single line
+ if (Strchr(docstr, '\n')) {
+ String *tmp = NewString("");
+ Append(tmp, "\n");
+ Append(tmp, indent_docstring(docstr, indent));
+ Append(tmp, indent);
+ Delete(docstr);
+ docstr = tmp;
+ }
+
+ return docstr;
+ }
+
+ /* ------------------------------------------------------------
+ * docstring()
+ *
+ * Get the docstring text, stripping off {} if necessary,
+ * and enclose in triple double quotes. If autodoc is also
+ * set then it will build a combined docstring.
+ * ------------------------------------------------------------ */
+
+ String *docstring(Node *n, autodoc_t ad_type, const String *indent, bool low_level = false) {
+ String *docstr = build_combined_docstring(n, ad_type, indent, low_level);
+ const int len = Len(docstr);
+ if (!len)
+ return docstr;
+
+ // Notice that all comments are created as raw strings (prefix "r"),
+ // because '\' is used often in comments, but may break Python module from
+ // loading. For example, in doxy comment one may write path in quotes:
+ //
+ // This is path to file "C:\x\file.txt"
+ //
+ // Python will not load the module with such comment because of illegal
+ // escape '\x'. '\' may additionally appear in verbatim or htmlonly sections
+ // of doxygen doc, Latex expressions, ...
+ String *doc = NewString("");
+
+ // Determine which kind of quotes to use as delimiters: for single line
+ // strings we can avoid problems with having a quote as the last character
+ // of the docstring by using different kind of quotes as delimiters. For
+ // multi-line strings this problem doesn't arise, as we always have a new
+ // line or spaces at the end of it, but it still does no harm to do it for
+ // them too.
+ //
+ // Note: we use double quotes by default, i.e. if there is no reason to
+ // prefer using single ones, for consistency with the older SWIG versions.
+ const bool useSingleQuotes = (Char(docstr))[len - 1] == '"';
+
+ Append(doc, useSingleQuotes ? "r'''" : "r\"\"\"");
+
+ // We also need to avoid having triple quotes of whichever type we use, as
+ // this would break Python doc string syntax too. Unfortunately there is no
+ // way to have triple quotes inside of raw-triple-quoted string, so we have
+ // to break the string in parts and rely on concatenation of the adjacent
+ // string literals.
+ if (useSingleQuotes)
+ Replaceall(docstr, "'''", "''' \"'''\" '''");
+ else
+ Replaceall(docstr, "\"\"\"", "\"\"\" '\"\"\"' \"\"\"");
+
+ Append(doc, docstr);
+ Append(doc, useSingleQuotes ? "'''" : "\"\"\"");
+ Delete(docstr);
+
+ return doc;
+ }
+
+ /* ------------------------------------------------------------
+ * cdocstring()
+ *
+ * Get the docstring text as it would appear in C-language
+ * source code (but without quotes around it).
+ * ------------------------------------------------------------ */
+
+ String *cdocstring(Node *n, autodoc_t ad_type, bool low_level = false) {
+ String *ds = build_combined_docstring(n, ad_type, "", low_level);
+ Replaceall(ds, "\\", "\\\\");
+ Replaceall(ds, "\"", "\\\"");
+ Replaceall(ds, "\n", "\\n\"\n\t\t\"");
+ return ds;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * addMissingParameterNames()
+ *
+ * For functions that have not had nameless parameters set in the Language class.
+ *
+ * Inputs:
+ * plist - entire parameter list
+ * arg_num - the number to start from when naming arguments
+ * Side effects:
+ * The "lname" attribute in each parameter in plist will be contain a parameter name
+ * ----------------------------------------------------------------------------- */
+
+ void addMissingParameterNames(Node *n, ParmList *plist, int arg_num) {
+ Parm *p = plist;
+ int i = arg_num;
+ while (p) {
+ if (!Getattr(p, "lname")) {
+ String *name = makeParameterName(n, p, i);
+ Setattr(p, "lname", name);
+ Delete(name);
+ }
+ i++;
+ p = nextSibling(p);
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * make_autodocParmList()
+ *
+ * Generate the documentation for the function parameters
+ * Parameters:
+ * arg_num: The number to start assigning unnamed arguments from
+ * func_annotation: Function annotation support
+ * ------------------------------------------------------------ */
+
+ String *make_autodocParmList(Node *n, bool showTypes, int arg_num = 1, bool calling = false, bool func_annotation = false) {
+
+ String *doc = NewString("");
+ String *pdocs = 0;
+ ParmList *plist = CopyParmList(Getattr(n, "parms"));
+ Parm *p;
+ Parm *pnext;
+
+ if (calling)
+ func_annotation = false;
+
+ addMissingParameterNames(n, plist, arg_num); // for $1_name substitutions done in Swig_typemap_attach_parms
+ Swig_typemap_attach_parms("in", plist, 0);
+ Swig_typemap_attach_parms("doc", plist, 0);
+
+ if (Strcmp(ParmList_protostr(plist), "void") == 0) {
+ //No parameters actually
+ return doc;
+ }
+
+ for (p = plist; p; p = pnext) {
+ String *tm = Getattr(p, "tmap:in");
+ if (tm) {
+ pnext = Getattr(p, "tmap:in:next");
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ continue;
+ }
+ } else {
+ pnext = nextSibling(p);
+ }
+
+ String *name = 0;
+ String *type = 0;
+ String *value = 0;
+ String *pdoc = Getattr(p, "tmap:doc");
+ if (pdoc) {
+ name = Getattr(p, "tmap:doc:name");
+ type = Getattr(p, "tmap:doc:type");
+ value = Getattr(p, "tmap:doc:value");
+ }
+
+ // Skip the "self" argument - it is added to the parameter list automatically
+ // and shouldn't be included in the Parameters block
+ if (Getattr(p, "self")) {
+ continue;
+ }
+
+ // Note: the generated name should be consistent with that in kwnames[]
+ String *made_name = 0;
+ if (!name) {
+ name = made_name = makeParameterName(n, p, arg_num);
+ }
+
+ // Increment the argument number once we are sure this is a real argument to count
+ arg_num++;
+
+ type = type ? type : Getattr(p, "type");
+ value = value ? value : Getattr(p, "value");
+
+ if (SwigType_isvarargs(type)) {
+ Delete(made_name);
+ break;
+ }
+
+ if (Len(doc)) {
+ // add a comma to the previous one if any
+ Append(doc, ", ");
+ }
+
+ // Do the param type too?
+ Node *nn = classLookup(Getattr(p, "type"));
+ String *type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
+ if (showTypes)
+ Printf(doc, "%s ", type_str);
+
+ Append(doc, name);
+ if (pdoc) {
+ if (!pdocs)
+ // numpydoc style: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
+ pdocs = NewString("\nParameters\n----------\n");
+ Printf(pdocs, "%s\n", pdoc);
+ }
+ // Write the function annotation
+ if (func_annotation)
+ Printf(doc, ": \"%s\"", type_str);
+
+ // Write default value
+ if (value && !calling) {
+ String *new_value = convertValue(value, Getattr(p, "type"));
+ if (new_value) {
+ value = new_value;
+ } else {
+ // Even if the value is not representable in the target language, still use it in the documentation, for compatibility with the previous SWIG versions
+ // and because it can still be useful to see the C++ expression there.
+ Node *lookup = Swig_symbol_clookup(value, 0);
+ if (lookup)
+ value = Getattr(lookup, "sym:name");
+ }
+ Printf(doc, "=%s", value);
+
+ if (new_value)
+ Delete(new_value);
+ }
+ Delete(type_str);
+ Delete(made_name);
+ }
+ if (pdocs)
+ Setattr(n, "feature:pdocs", pdocs);
+ Delete(plist);
+ return doc;
+ }
+
+ /* ------------------------------------------------------------
+ * make_autodoc()
+ *
+ * Build a docstring for the node, using parameter and other
+ * info in the parse tree. If the value of the autodoc
+ * attribute is "0" then do not include parameter types, if
+ * it is "1" (the default) then do. If it has some other
+ * value then assume it is supplied by the extension writer
+ * and use it directly.
+ * ------------------------------------------------------------ */
+
+ String *make_autodoc(Node *n, autodoc_t ad_type, bool low_level = false) {
+ int extended = 0;
+ bool first_func = true;
+ // If the function is overloaded then this function is called
+ // for the last one. Rewind to the first so the docstrings are
+ // in order.
+ while (Getattr(n, "sym:previousSibling"))
+ n = Getattr(n, "sym:previousSibling");
+
+ String *doc = NewString("");
+ while (n) {
+ bool showTypes = false;
+ bool skipAuto = false;
+ String *autodoc = Getattr(n, "feature:autodoc");
+ autodoc_l dlevel = autodoc_level(autodoc);
+ switch (dlevel) {
+ case NO_AUTODOC:
+ break;
+ case NAMES_AUTODOC:
+ showTypes = false;
+ break;
+ case TYPES_AUTODOC:
+ showTypes = true;
+ break;
+ case EXTEND_AUTODOC:
+ extended = 1;
+ showTypes = false;
+ break;
+ case EXTEND_TYPES_AUTODOC:
+ extended = 1;
+ showTypes = true;
+ break;
+ case STRING_AUTODOC:
+ Append(doc, autodoc);
+ skipAuto = true;
+ break;
+ }
+
+ if (!skipAuto) {
+ /* Check if a documentation name was given for either the low-level C API or high-level Python shadow API */
+ String *symname = Getattr(n, low_level ? "doc:low:name" : "doc:high:name");
+ if (!symname) {
+ symname = Getattr(n, "sym:name");
+ }
+
+ SwigType *type = Getattr(n, "type");
+ String *type_str = NULL;
+
+ // If the function has default arguments, then that documentation covers this version too
+ if (Getattr(n, "defaultargs") != NULL) {
+ n = Getattr(n, "sym:nextSibling");
+ continue;
+ }
+
+ if (!first_func)
+ Append(doc, "\n");
+
+ if (type) {
+ if (Strcmp(type, "void") == 0) {
+ type_str = NULL;
+ } else {
+ Node *nn = classLookup(type);
+ type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
+ }
+ }
+
+ /* Treat the low-level C API functions for getting/setting variables as methods for documentation purposes */
+ String *kind = Getattr(n, "kind");
+ if (kind && Strcmp(kind, "variable") == 0) {
+ if (ad_type == AUTODOC_FUNC) {
+ ad_type = AUTODOC_METHOD;
+ }
+ }
+ /* Treat destructors as methods for documentation purposes */
+ String *nodeType = Getattr(n, "nodeType");
+ if (nodeType && Strcmp(nodeType, "destructor") == 0) {
+ if (ad_type == AUTODOC_FUNC) {
+ ad_type = AUTODOC_METHOD;
+ }
+ }
+
+ switch (ad_type) {
+ case AUTODOC_CLASS:
+ {
+ // Only do the autodoc if there isn't a docstring for the class
+ String *str = Getattr(n, "feature:docstring");
+ if (!str || Len(str) == 0) {
+ if (builtin) {
+ String *name = Getattr(n, "name");
+ String *rname = add_explicit_scope(SwigType_namestr(name));
+ Printf(doc, "%s", rname);
+ Delete(rname);
+ } else {
+ if (CPlusPlus) {
+ Printf(doc, "Proxy of C++ %s class.", SwigType_namestr(real_classname));
+ } else {
+ Printf(doc, "Proxy of C %s struct.", SwigType_namestr(real_classname));
+ }
+ }
+ }
+ }
+ break;
+ case AUTODOC_CTOR:
+ if (Strcmp(class_name, symname) == 0) {
+ String *paramList = make_autodocParmList(n, showTypes, 2);
+ Printf(doc, "__init__(");
+ if (showTypes)
+ Printf(doc, "%s ", class_name);
+ if (Len(paramList))
+ Printf(doc, "self, %s) -> %s", paramList, class_name);
+ else
+ Printf(doc, "self) -> %s", class_name);
+ } else
+ Printf(doc, "%s(%s) -> %s", symname, make_autodocParmList(n, showTypes), class_name);
+ break;
+
+ case AUTODOC_DTOR:
+ if (showTypes)
+ Printf(doc, "__del__(%s self)", class_name);
+ else
+ Printf(doc, "__del__(self)");
+ break;
+
+ case AUTODOC_STATICFUNC:
+ Printf(doc, "%s(%s)", symname, make_autodocParmList(n, showTypes));
+ if (type_str)
+ Printf(doc, " -> %s", type_str);
+ break;
+
+ case AUTODOC_FUNC:
+ Printf(doc, "%s(%s)", symname, make_autodocParmList(n, showTypes));
+ if (type_str)
+ Printf(doc, " -> %s", type_str);
+ break;
+
+ case AUTODOC_METHOD:
+ {
+ String *paramList = make_autodocParmList(n, showTypes, 2);
+ Printf(doc, "%s(", symname);
+ if (showTypes)
+ Printf(doc, "%s ", class_name);
+ if (Len(paramList))
+ Printf(doc, "self, %s)", paramList);
+ else
+ Printf(doc, "self)");
+ if (type_str)
+ Printf(doc, " -> %s", type_str);
+ }
+ break;
+
+ case AUTODOC_CONST:
+ // There is no autodoc support for constants currently, this enum
+ // element only exists to allow calling docstring() with it.
+ return NULL;
+ case AUTODOC_VAR:
+ // Variables can also be documented (e.g. through the property() function in python)
+ Printf(doc, "%s", symname);
+ if (showTypes) {
+ String *type = Getattr(n, "tmap:doc:type");
+ if (!type)
+ type = Getattr(n, "membervariableHandler:type");
+ if (!type)
+ type = Getattr(n, "type");
+ Printf(doc, " : %s", type);
+ }
+ break;
+ }
+ Delete(type_str);
+
+ // Special case: wrapper functions to get a variable should have no parameters.
+ // Because the node is re-used for the setter and getter, the feature:pdocs field will
+ // exist for the getter function, so explicitly avoid printing parameters in this case.
+ bool variable_getter = kind && Strcmp(kind, "variable") == 0 && Getattr(n, "memberget");
+ if (extended && ad_type != AUTODOC_VAR && !variable_getter) {
+ String *pdocs = Getattr(n, "feature:pdocs");
+ if (pdocs) {
+ Printv(doc, "\n", pdocs, NULL);
+ }
+ }
+ }
+ // if it's overloaded then get the next decl and loop around again
+ n = Getattr(n, "sym:nextSibling");
+ if (n)
+ first_func = false;
+ }
+
+ return doc;
+ }
+
+ /* ------------------------------------------------------------
+ * convertIntegerValue()
+ *
+ * Check if string v is an integer and can be represented in
+ * Python. If so, return an appropriate Python representation,
+ * otherwise (or if we are unsure), return NIL.
+ * ------------------------------------------------------------ */
+ String *convertIntegerValue(String *v, SwigType *resolved_type) {
+ const char *const s = Char(v);
+ char *end;
+ String *result = NIL;
+
+ // Check if this is an integer number in any base.
+ errno = 0;
+ long value = strtol(s, &end, 0);
+ if (errno == ERANGE || end == s)
+ return NIL;
+
+ if (*end != '\0') {
+ // If there is a suffix after the number, we can safely ignore "l"
+ // and (provided the number is unsigned) "u", and also combinations of
+ // these, but not anything else.
+ for (char *p = end; *p != '\0'; ++p) {
+ switch (*p) {
+ case 'l':
+ case 'L':
+ break;
+ case 'u':
+ case 'U':
+ if (value < 0)
+ return NIL;
+ break;
+ default:
+ return NIL;
+ }
+ }
+ }
+ // So now we are certain that we are indeed dealing with an integer
+ // that has a representation as long given by value.
+
+ // Restrict to guaranteed supported range in Python, see maxint docs: https://docs.python.org/2/library/sys.html#sys.maxint
+ // Don't do this pointless check when long is 32 bits or smaller as strtol will have already failed with ERANGE
+#if LONG_MAX > PYTHON_INT_MAX || LONG_MIN < PYTHON_INT_MIN
+ if (value > PYTHON_INT_MAX || value < PYTHON_INT_MIN) {
+ return NIL;
+ }
+#endif
+
+ if (Cmp(resolved_type, "bool") == 0)
+ // Allow integers as the default value for a bool parameter.
+ return NewString(value ? "True" : "False");
+
+ if (value == 0)
+ return NewString(SwigType_ispointer(resolved_type) ? "None" : "0");
+
+ // v may still be octal or hexadecimal:
+ const char *p = s;
+ if (*p == '+' || *p == '-')
+ ++p;
+ if (*p == '0' && *(p+1) != 'x' && *(p+1) != 'X') {
+ // This must have been an octal number. This is the only case we
+ // cannot use in Python directly, since Python 2 and 3 use non-
+ // compatible representations.
+ result = NewString(*s == '-' ? "int(\"-" : "int(\"");
+ String *octal_string = NewStringWithSize(p, (int) (end - p));
+ Append(result, octal_string);
+ Append(result, "\", 8)");
+ Delete(octal_string);
+ return result;
+ }
+ result = *end == '\0' ? Copy(v) : NewStringWithSize(s, (int) (end - s));
+ return result;
+ }
+
+ /* ------------------------------------------------------------
+ * convertDoubleValue()
+ *
+ * Check if the given string looks like a decimal floating point constant
+ * and return it if it does, otherwise return NIL.
+ * ------------------------------------------------------------ */
+ String *convertDoubleValue(String *v) {
+ const char *const s = Char(v);
+ char *end;
+
+ errno = 0;
+ double value = strtod(s, &end);
+ (void) value;
+ if (errno != ERANGE && end != s) {
+ // An added complication: at least some versions of strtod() recognize
+ // hexadecimal floating point numbers which don't exist in Python, so
+ // detect them ourselves and refuse to convert them (this can't be done
+ // without loss of precision in general).
+ //
+ // Also don't accept neither "NAN" nor "INFINITY" (both of which
+ // conveniently contain "n").
+ if (strpbrk(s, "xXnN"))
+ return NIL;
+
+ // Disregard optional "f" suffix, it can be just dropped in Python as it
+ // uses doubles for everything anyhow.
+ for (char * p = end; *p != '\0'; ++p) {
+ switch (*p) {
+ case 'f':
+ case 'F':
+ break;
+
+ default:
+ return NIL;
+ }
+ }
+
+ // Avoid unnecessary string allocation in the common case when we don't
+ // need to remove any suffix.
+ return *end == '\0' ? Copy(v) : NewStringWithSize(s, (int)(end - s));
+ }
+
+ return NIL;
+ }
+
+ /* ------------------------------------------------------------
+ * convertValue()
+ *
+ * Check if string v can be a Python value literal or a
+ * constant. Return an equivalent Python representation,
+ * or NIL if it isn't, or we are unsure.
+ * ------------------------------------------------------------ */
+ String *convertValue(String *v, SwigType *type) {
+ const char *const s = Char(v);
+ String *result = NIL;
+ SwigType *resolved_type = SwigType_typedef_resolve_all(type);
+
+ result = convertIntegerValue(v, resolved_type);
+ if (!result) {
+ result = convertDoubleValue(v);
+ if (!result) {
+ if (Strcmp(v, "true") == 0)
+ result = NewString("True");
+ else if (Strcmp(v, "false") == 0)
+ result = NewString("False");
+ else if (Strcmp(v, "NULL") == 0 || Strcmp(v, "nullptr") == 0)
+ result = SwigType_ispointer(resolved_type) ? NewString("None") : NewString("0");
+ // This could also be an enum type, default value of which could be
+ // representable in Python if it doesn't include any scope (which could,
+ // but currently is not, translated).
+ else if (!Strchr(s, ':')) {
+ Node *lookup = Swig_symbol_clookup(v, 0);
+ if (lookup) {
+ if (Cmp(Getattr(lookup, "nodeType"), "enumitem") == 0)
+ result = Copy(Getattr(lookup, "sym:name"));
+ }
+ }
+ }
+ }
+
+ Delete(resolved_type);
+ return result;
+ }
+
+ /* ------------------------------------------------------------
+ * is_representable_as_pyargs()
+ *
+ * Check if the function parameters default argument values
+ * can be represented in Python.
+ *
+ * If this method returns false, the parameters will be translated
+ * to a generic "*args" which allows us to deal with default values
+ * at C++ code level where they can always be handled.
+ * ------------------------------------------------------------ */
+ bool is_representable_as_pyargs(Node *n) {
+ ParmList *plist = CopyParmList(Getattr(n, "parms"));
+ Swig_typemap_attach_parms("default", plist, NULL);
+
+ Parm *p;
+ Parm *pnext;
+
+ for (p = plist; p; p = pnext) {
+ pnext = nextSibling(p);
+ String *tm = Getattr(p, "tmap:in");
+ if (tm) {
+ Parm *in_next = Getattr(p, "tmap:in:next");
+ if (in_next)
+ pnext = in_next;
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ continue;
+ }
+ }
+
+ // "default" typemap can contain arbitrary C++ code, so while it could, in
+ // principle, be possible to examine it and check if it's just something
+ // simple of the form "$1 = expression" and then use convertValue() to
+ // check if expression can be used in Python, but for now we just
+ // pessimistically give up and prefer to handle this at C++ level only.
+ if (Getattr(p, "tmap:default"))
+ return false;
+
+ String *value = Getattr(p, "value");
+ if (value) {
+ String *convertedValue = convertValue(value, Getattr(p, "type"));
+ if (!convertedValue)
+ return false;
+ Delete(convertedValue);
+ }
+ }
+
+ return true;
+ }
+
+
+ /* ------------------------------------------------------------
+ * is_real_overloaded()
+ *
+ * Check if the function is overloaded, but not just have some
+ * siblings generated due to the original function having
+ * default arguments.
+ * ------------------------------------------------------------ */
+ bool is_real_overloaded(Node *n) {
+ Node *h = Getattr(n, "sym:overloaded");
+ Node *i;
+ if (!h)
+ return false;
+
+ i = Getattr(h, "sym:nextSibling");
+ while (i) {
+ Node *nn = Getattr(i, "defaultargs");
+ if (nn != h) {
+ /* Check if overloaded function has defaultargs and
+ * pointed to the first overloaded. */
+ return true;
+ }
+ i = Getattr(i, "sym:nextSibling");
+ }
+
+ return false;
+ }
+
+ /* ------------------------------------------------------------
+ * make_pyParmList()
+ *
+ * Generate parameter list for Python functions or methods,
+ * reuse make_autodocParmList() to do so.
+ * ------------------------------------------------------------ */
+ String *make_pyParmList(Node *n, bool in_class, bool is_calling, int kw, bool has_self_for_count = false) {
+ /* Get the original function for a defaultargs copy,
+ * see default_arguments() in parser.y. */
+ Node *nn = Getattr(n, "defaultargs");
+ if (nn)
+ n = nn;
+
+ Parm *parms = Getattr(n, "parms");
+ int varargs = parms ? emit_isvarargs(parms) : 0;
+
+ /* We prefer to explicitly list all parameters of the C function in the
+ generated Python code as this makes the function more convenient to use,
+ however in some cases we must replace the real parameters list with just
+ the catch all "*args". This happens when:
+
+ 1. The function is overloaded as Python doesn't support this.
+ 2. We were explicitly asked to use the "compact" arguments form.
+ 3. We were explicitly asked to use default args from C via the "python:cdefaultargs" feature.
+ 4. One of the default argument values can't be represented in Python.
+ 5. Varargs that haven't been forced to use a fixed number of arguments with %varargs.
+ */
+ if (is_real_overloaded(n) || GetFlag(n, "feature:compactdefaultargs") || GetFlag(n, "feature:python:cdefaultargs") || !is_representable_as_pyargs(n) || varargs) {
+ String *parms = NewString("");
+ if (in_class)
+ Printf(parms, "self, ");
+ Printf(parms, "*args");
+ if (kw)
+ Printf(parms, ", **kwargs");
+ return parms;
+ }
+
+ bool funcanno = Equal(Getattr(n, "feature:python:annotations"), "c") ? true : false;
+ String *params = NewString("");
+ String *_params = make_autodocParmList(n, false, ((in_class || has_self_for_count)? 2 : 1), is_calling, funcanno);
+
+ if (in_class) {
+ Printf(params, "self");
+ if (Len(_params) > 0)
+ Printf(params, ", ");
+ }
+
+ Printv(params, _params, NULL);
+
+ return params;
+ }
+
+ /* ------------------------------------------------------------
+ * have_pythonprepend()
+ *
+ * Check if there is a %pythonprepend directive and it has text
+ * ------------------------------------------------------------ */
+
+ bool have_pythonprepend(Node *n) {
+ String *str = Getattr(n, "feature:pythonprepend");
+ return (str && Len(str) > 0);
+ }
+
+ /* ------------------------------------------------------------
+ * pythonprepend()
+ *
+ * Get the %pythonprepend code, stripping off {} if necessary
+ * ------------------------------------------------------------ */
+
+ String *pythonprepend(Node *n) {
+ String *str = Getattr(n, "feature:pythonprepend");
+ char *t = Char(str);
+ if (*t == '{') {
+ Delitem(str, 0);
+ Delitem(str, DOH_END);
+ }
+ return str;
+ }
+
+ /* ------------------------------------------------------------
+ * have_pythonappend()
+ *
+ * Check if there is a %pythonappend directive and it has text
+ * ------------------------------------------------------------ */
+
+ bool have_pythonappend(Node *n) {
+ String *str = Getattr(n, "feature:pythonappend");
+ if (!str)
+ str = Getattr(n, "feature:addtofunc");
+ return (str && Len(str) > 0);
+ }
+
+ /* ------------------------------------------------------------
+ * pythonappend()
+ *
+ * Get the %pythonappend code, stripping off {} if necessary
+ * ------------------------------------------------------------ */
+
+ String *pythonappend(Node *n) {
+ String *str = Getattr(n, "feature:pythonappend");
+ if (!str)
+ str = Getattr(n, "feature:addtofunc");
+
+ char *t = Char(str);
+ if (*t == '{') {
+ Delitem(str, 0);
+ Delitem(str, DOH_END);
+ }
+ return str;
+ }
+
+ /* ------------------------------------------------------------
+ * have_addtofunc()
+ *
+ * Check if there is a %addtofunc directive and it has text
+ * ------------------------------------------------------------ */
+
+ bool have_addtofunc(Node *n) {
+ return have_pythonappend(n) || have_pythonprepend(n);
+ }
+
+
+ /* ------------------------------------------------------------
+ * returnTypeAnnotation()
+ *
+ * Helper function for constructing the function annotation
+ * of the returning type, return a empty string for Python 2.x
+ * ------------------------------------------------------------ */
+ String *returnTypeAnnotation(Node *n) {
+ String *ret = 0;
+ Parm *p = Getattr(n, "parms");
+ String *tm;
+ /* Try to guess the returning type by argout typemap,
+ * however the result may not accurate. */
+ while (p) {
+ if ((tm = Getattr(p, "tmap:argout:match_type"))) {
+ tm = SwigType_str(tm, 0);
+ if (ret)
+ Printv(ret, ", ", tm, NULL);
+ else
+ ret = tm;
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ /* If no argout typemap, then get the returning type from
+ * the function prototype. */
+ if (!ret) {
+ ret = Getattr(n, "type");
+ if (ret)
+ ret = SwigType_str(ret, 0);
+ }
+ bool funcanno = Equal(Getattr(n, "feature:python:annotations"), "c") ? true : false;
+ return (ret && funcanno) ? NewStringf(" -> \"%s\"", ret) : NewString("");
+ }
+
+ /* ------------------------------------------------------------
+ * variableAnnotation()
+ *
+ * Helper function for constructing a variable annotation
+ * ------------------------------------------------------------ */
+
+ String *variableAnnotation(Node *n) {
+ String *type = Getattr(n, "type");
+ if (type)
+ type = SwigType_str(type, 0);
+ bool anno = Equal(Getattr(n, "feature:python:annotations"), "c") ? true : false;
+ anno = GetFlag(n, "feature:python:annotations:novar") ? false : anno;
+ String *annotation = (type && anno) ? NewStringf(": \"%s\"", type) : NewString("");
+ Delete(type);
+ return annotation;
+ }
+
+ /* ------------------------------------------------------------
+ * emitFunctionShadowHelper()
+ *
+ * Refactoring some common code out of functionWrapper and
+ * dispatchFunction that writes the proxy code for non-member
+ * functions.
+ * ------------------------------------------------------------ */
+
+ void emitFunctionShadowHelper(Node *n, File *f_dest, String *name, int kw) {
+ String *parms = make_pyParmList(n, false, false, kw);
+ String *callParms = make_pyParmList(n, false, true, kw);
+
+ // Callbacks need the C function in order to extract the pointer from the swig_ptr: string
+ bool fast = (fastproxy && !have_addtofunc(n)) || Getattr(n, "feature:callback");
+
+ if (!fast || olddefs) {
+ /* Make a wrapper function to insert the code into */
+ Printv(f_dest, "\n", "def ", name, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
+ if (have_docstring(n))
+ Printv(f_dest, tab4, docstring(n, AUTODOC_FUNC, tab4, true), "\n", NIL);
+ if (have_pythonprepend(n))
+ Printv(f_dest, indent_pythoncode(pythonprepend(n), tab4, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
+ if (have_pythonappend(n)) {
+ Printv(f_dest, tab4 "val = ", funcCall(name, callParms), "\n", NIL);
+ Printv(f_dest, indent_pythoncode(pythonappend(n), tab4, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
+ Printv(f_dest, tab4 "return val\n", NIL);
+ } else {
+ Printv(f_dest, tab4 "return ", funcCall(name, callParms), "\n", NIL);
+ }
+ }
+
+ // Below may result in a 2nd definition of the method when -olddefs is used. The Python interpreter will use the second definition as it overwrites the first.
+ if (fast) {
+ /* If there is no addtofunc directive then just assign from the extension module (for speed up) */
+ Printv(f_dest, name, " = ", module, ".", name, "\n", NIL);
+ }
+ }
+
+
+ /* ------------------------------------------------------------
+ * check_kwargs()
+ *
+ * check if using kwargs is allowed for this Node
+ * ------------------------------------------------------------ */
+
+ int check_kwargs(Node *n) const {
+ return (use_kw || GetFlag(n, "feature:kwargs"))
+ && !GetFlag(n, "memberset") && !GetFlag(n, "memberget");
+ }
+
+
+
+ /* ------------------------------------------------------------
+ * add_method()
+ * ------------------------------------------------------------ */
+
+ void add_method(String *name, String *function, int kw, Node *n = 0, int funpack = 0, int num_required = -1, int num_arguments = -1) {
+ String * meth_str = NewString("");
+ if (!kw) {
+ if (funpack) {
+ if (num_required == 0 && num_arguments == 0) {
+ Printf(meth_str, "\t { \"%s\", %s, METH_NOARGS, ", name, function);
+ } else if (num_required == 1 && num_arguments == 1) {
+ Printf(meth_str, "\t { \"%s\", %s, METH_O, ", name, function);
+ } else {
+ Printf(meth_str, "\t { \"%s\", %s, METH_VARARGS, ", name, function);
+ }
+ } else {
+ Printf(meth_str, "\t { \"%s\", %s, METH_VARARGS, ", name, function);
+ }
+ } else {
+ // Cast via void(*)(void) to suppress GCC -Wcast-function-type warning.
+ // Python should always call the function correctly, but the Python C API
+ // requires us to store it in function pointer of a different type.
+ Printf(meth_str, "\t { \"%s\", (PyCFunction)(void(*)(void))%s, METH_VARARGS|METH_KEYWORDS, ", name, function);
+ }
+ Append(methods, meth_str);
+ if (fastproxy) {
+ Append(methods_proxydocs, meth_str);
+ }
+ Delete(meth_str);
+
+ if (!n) {
+ Append(methods, "NULL");
+ if (fastproxy) {
+ Append(methods_proxydocs, "NULL");
+ }
+ } else if (have_docstring(n)) {
+ /* Use the low-level docstring here since this is the docstring that will be used for the C API */
+ String *ds = cdocstring(n, Getattr(n, "memberfunction") ? AUTODOC_METHOD : AUTODOC_FUNC, true);
+ Printf(methods, "\"%s\"", ds);
+ if (fastproxy) {
+ /* In the fastproxy case, we must also record the high-level docstring for use in the Python shadow API */
+ Delete(ds);
+ ds = cdocstring(n, Getattr(n, "memberfunction") ? AUTODOC_METHOD : AUTODOC_FUNC);
+ Printf(methods_proxydocs, "\"%s\"", ds);
+ }
+ Delete(ds);
+ } else if (Getattr(n, "feature:callback")) {
+ Printf(methods, "\"swig_ptr: %s\"", Getattr(n, "feature:callback:name"));
+ if (fastproxy) {
+ Printf(methods_proxydocs, "\"swig_ptr: %s\"", Getattr(n, "feature:callback:name"));
+ have_fast_proxy_static_member_method_callback = true;
+ }
+ } else {
+ Append(methods, "NULL");
+ if (fastproxy) {
+ Append(methods_proxydocs, "NULL");
+ }
+ }
+
+ Append(methods, "},\n");
+ if (fastproxy) {
+ Append(methods_proxydocs, "},\n");
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * dispatchFunction()
+ * ------------------------------------------------------------ */
+ void dispatchFunction(Node *n, String *linkage, int funpack = 0, bool builtin_self = false, bool builtin_ctor = false, bool director_class = false, bool use_static_method = false) {
+ /* Last node in overloaded chain */
+
+ bool add_self = builtin_self && (!builtin_ctor || director_class);
+
+ int maxargs;
+
+ String *tmp = NewString("");
+ String *dispatch;
+
+ const char *dispatch_call = funpack ? "%s(self, argc, argv);" : (builtin_ctor ? "%s(self, args, NULL);" : "%s(self, args);");
+ String *dispatch_code = NewStringf("return %s", dispatch_call);
+
+ if (castmode) {
+ dispatch = Swig_overload_dispatch_cast(n, dispatch_code, &maxargs);
+ } else {
+ String *fastdispatch_code;
+ if (builtin_ctor)
+ fastdispatch_code = NewStringf("int retval = %s\nif (retval == 0 || !SWIG_Python_TypeErrorOccurred(NULL)) return retval;\nSWIG_fail;", dispatch_call);
+ else
+ fastdispatch_code = NewStringf("PyObject *retobj = %s\nif (!SWIG_Python_TypeErrorOccurred(retobj)) return retobj;\nSWIG_fail;", dispatch_call);
+ if (!CPlusPlus) {
+ Insert(fastdispatch_code, 0, "{\n");
+ Append(fastdispatch_code, "\n}");
+ }
+ dispatch = Swig_overload_dispatch(n, dispatch_code, &maxargs, fastdispatch_code);
+ Delete(fastdispatch_code);
+ }
+
+ /* Generate a dispatch wrapper for all overloaded functions */
+
+ Wrapper *f = NewWrapper();
+ String *symname = Getattr(n, "sym:name");
+ String *wname = Swig_name_wrapper(symname);
+
+ const char *builtin_kwargs = builtin_ctor ? ", PyObject *kwargs" : "";
+ Printv(f->def, linkage, builtin_ctor ? "int " : "PyObject *", wname, "(PyObject *self, PyObject *args", builtin_kwargs, ") {", NIL);
+
+ if (builtin) {
+ /* Avoid warning if the self parameter is not used. */
+ Append(f->code, "(void)self;\n");
+ }
+
+ Wrapper_add_local(f, "argc", "Py_ssize_t argc");
+ Printf(tmp, "PyObject *argv[%d] = {0}", maxargs + 1);
+ Wrapper_add_local(f, "argv", tmp);
+
+ if (!fastunpack) {
+ Wrapper_add_local(f, "ii", "Py_ssize_t ii");
+
+ if (builtin_ctor)
+ Printf(f->code, "if (!SWIG_Python_CheckNoKeywords(kwargs, \"%s\")) SWIG_fail;\n", symname);
+
+ if (maxargs - (add_self ? 1 : 0) > 0) {
+ Append(f->code, "if (!PyTuple_Check(args)) SWIG_fail;\n");
+ Append(f->code, "argc = PyObject_Length(args);\n");
+ } else {
+ Append(f->code, "argc = args ? PyObject_Length(args) : 0;\n");
+ }
+
+ if (add_self)
+ Append(f->code, "argv[0] = self;\n");
+ Printf(f->code, "for (ii = 0; (ii < %d) && (ii < argc); ii++) {\n", add_self ? maxargs - 1 : maxargs);
+ Printf(f->code, "argv[ii%s] = PyTuple_GET_ITEM(args,ii);\n", add_self ? " + 1" : "");
+ Append(f->code, "}\n");
+ if (add_self)
+ Append(f->code, "argc++;\n");
+ } else {
+ if (builtin_ctor)
+ Printf(f->code, "if (!SWIG_Python_CheckNoKeywords(kwargs, \"%s\")) SWIG_fail;\n", symname);
+ Printf(f->code, "if (!(argc = SWIG_Python_UnpackTuple(args, \"%s\", 0, %d, argv%s))) SWIG_fail;\n", symname, maxargs, add_self ? "+1" : "");
+ if (add_self)
+ Append(f->code, "argv[0] = self;\n");
+ else
+ Append(f->code, "--argc;\n");
+ }
+
+ Replaceall(dispatch, "$args", "self, args");
+
+ Printv(f->code, dispatch, "\n", NIL);
+
+ if (GetFlag(n, "feature:python:maybecall")) {
+ Append(f->code, "fail:\n");
+ Append(f->code, " Py_INCREF(Py_NotImplemented);\n");
+ Append(f->code, " return Py_NotImplemented;\n");
+ } else {
+ Node *sibl = n;
+ while (Getattr(sibl, "sym:previousSibling"))
+ sibl = Getattr(sibl, "sym:previousSibling"); // go all the way up
+ String *protoTypes = NewString("");
+ do {
+ String *fulldecl = Swig_name_decl(sibl);
+ Printf(protoTypes, "\n\" %s\\n\"", fulldecl);
+ Delete(fulldecl);
+ } while ((sibl = Getattr(sibl, "sym:nextSibling")));
+ Append(f->code, "fail:\n");
+ Printf(f->code, " SWIG_Python_RaiseOrModifyTypeError("
+ "\"Wrong number or type of arguments for overloaded function '%s'.\\n\"" "\n\" Possible C/C++ prototypes are:\\n\"%s);\n", symname, protoTypes);
+ Printf(f->code, "return %s;\n", builtin_ctor ? "-1" : "0");
+ Delete(protoTypes);
+ }
+ Printv(f->code, "}\n", NIL);
+ Wrapper_print(f, f_wrappers);
+ Node *p = Getattr(n, "sym:previousSibling");
+ if (!builtin_self && (use_static_method || !builtin))
+ add_method(symname, wname, 0, p);
+
+ /* Create a shadow for this function (if enabled and not in a member function) */
+ if (!builtin && shadow && !(shadow & PYSHADOW_MEMBER) && use_static_method) {
+ emitFunctionShadowHelper(n, in_class ? f_shadow_stubs : f_shadow, symname, 0);
+ }
+ DelWrapper(f);
+ Delete(dispatch);
+ Delete(dispatch_code);
+ Delete(tmp);
+ Delete(wname);
+ }
+
+ /* ------------------------------------------------------------
+ * functionWrapper()
+ * ------------------------------------------------------------ */
+
+ /*
+ A note about argument marshalling with built-in types.
+ There are three distinct cases for member (non-static) methods:
+
+ 1) An ordinary member function. In this case, the first param in
+ the param list is 'this'. For builtin types, 'this' is taken from
+ the first argument to the wrapper (usually called 'self); it's not
+ extracted from the second argument (which is usually a tuple).
+
+ 2) A constructor for a non-director class. In this case, the
+ param list doesn't contain an entry for 'this', but the first ('self')
+ argument to the wrapper *does* contain the newly-allocated,
+ uninitialized object.
+
+ 3) A constructor for a director class. In this case, the param
+ list contains a 'self' param, which comes from the first argument
+ to the wrapper function.
+ */
+
+ const char *get_implicitconv_flag(Node *klass) {
+ int conv = 0;
+ if (klass && GetFlag(klass, "feature:implicitconv")) {
+ conv = 1;
+ }
+ return conv ? "SWIG_POINTER_IMPLICIT_CONV" : "0";
+ }
+
+
+ virtual int functionWrapper(Node *n) {
+
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *d = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ Node *parent = Swig_methodclass(n);
+
+ int director_method = 0;
+
+ Parm *p;
+ int i;
+ char source[64];
+ Wrapper *f;
+ String *self_parse;
+ String *parse_args;
+ String *arglist;
+ String *get_pointers;
+ String *cleanup;
+ String *outarg;
+ String *kwargs;
+ String *tm;
+ String *overname = 0;
+
+ int num_required;
+ int num_arguments;
+ int num_fixed_arguments;
+ int tuple_required;
+ int tuple_arguments;
+ int varargs = 0;
+ int allow_kwargs = check_kwargs(n);
+
+ String *nodeType = Getattr(n, "nodeType");
+ int constructor = (!Cmp(nodeType, "constructor"));
+ int destructor = (!Cmp(nodeType, "destructor"));
+ String *storage = Getattr(n, "storage");
+ /* Only the first constructor is handled as init method. Others
+ constructor can be emitted via %rename */
+ int handled_as_init = 0;
+ if (!have_constructor && (constructor || Getattr(n, "handled_as_constructor"))
+ && ((shadow & PYSHADOW_MEMBER))) {
+ String *nname = Getattr(n, "sym:name");
+ String *sname = Getattr(getCurrentClass(), "sym:name");
+ String *cname = Swig_name_construct(NSPACE_TODO, sname);
+ handled_as_init = (Strcmp(nname, sname) == 0) || (Strcmp(nname, cname) == 0);
+ Delete(cname);
+ }
+ bool builtin_self = builtin && in_class && (constructor || (l && Getattr(l, "self")));
+ bool builtin_ctor = false;
+ if (builtin_self && constructor) {
+ String *class_mname = Getattr(getCurrentClass(), "sym:name");
+ String *mrename = Swig_name_construct(getNSpace(), class_mname);
+ if (Cmp(iname, mrename))
+ builtin_self = false;
+ else
+ builtin_ctor = true;
+ Delete(mrename);
+ }
+ bool director_class = (getCurrentClass() && Swig_directorclass(getCurrentClass()));
+ bool add_self = builtin_self && (!builtin_ctor || director_class);
+ bool builtin_getter = (builtin && GetFlag(n, "memberget"));
+ bool builtin_setter = (builtin && GetFlag(n, "memberset") && !builtin_getter);
+ char const *wrap_return = builtin_ctor ? "int " : "PyObject *";
+ String *linkage = NewString("SWIGINTERN ");
+ String *wrapper_name = Swig_name_wrapper(iname);
+
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ } else {
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+ }
+
+ f = NewWrapper();
+ self_parse = NewString("");
+ parse_args = NewString("");
+ arglist = NewString("");
+ get_pointers = NewString("");
+ cleanup = NewString("");
+ outarg = NewString("");
+ kwargs = NewString("");
+
+ int allow_thread = threads_enable(n);
+
+ Wrapper_add_local(f, "resultobj", "PyObject *resultobj = 0");
+
+ // Emit all of the local variables for holding arguments.
+ emit_parameter_variables(l, f);
+
+ /* Attach the standard typemaps */
+ emit_attach_parmmaps(l, f);
+ Setattr(n, "wrap:parms", l);
+ /* Get number of required and total arguments */
+ tuple_arguments = num_arguments = emit_num_arguments(l);
+ tuple_required = num_required = emit_num_required(l);
+ if (add_self) {
+ --tuple_arguments;
+ --tuple_required;
+ }
+ num_fixed_arguments = tuple_required;
+
+ // builtin handles/checks kwargs by default except in constructor wrappers so we need to explicitly handle them in the C constructor wrapper
+ // The check below is for zero arguments. Sometimes (eg directors) self is the first argument for a method with zero arguments.
+ if (((num_arguments == 0) && (num_required == 0)) || ((num_arguments == 1) && (num_required == 1) && Getattr(l, "self")))
+ if (!builtin_ctor)
+ allow_kwargs = 0;
+ varargs = emit_isvarargs(l);
+
+ String *wname = Copy(wrapper_name);
+ if (overname) {
+ Append(wname, overname);
+ }
+
+ const char *builtin_kwargs = builtin_ctor ? ", PyObject *kwargs" : "";
+ if (!allow_kwargs || overname) {
+ if (!varargs) {
+ Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, PyObject *args", builtin_kwargs, ") {", NIL);
+ } else {
+ Printv(f->def, linkage, wrap_return, wname, "__varargs__", "(PyObject *self, PyObject *args, PyObject *varargs", builtin_kwargs, ") {", NIL);
+ }
+ if (allow_kwargs) {
+ Swig_warning(WARN_LANG_OVERLOAD_KEYWORD, input_file, line_number, "Can't use keyword arguments with overloaded functions (%s).\n", Swig_name_decl(n));
+ allow_kwargs = 0;
+ }
+ } else {
+ if (varargs) {
+ Swig_warning(WARN_LANG_VARARGS_KEYWORD, input_file, line_number, "Can't wrap varargs with keyword arguments enabled\n");
+ varargs = 0;
+ }
+ Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, PyObject *args, PyObject *kwargs) {", NIL);
+ /* Avoid warning if the self parameter is not used. */
+ Append(f->def, "(void)self;\n");
+ }
+
+ if (builtin) {
+ /* Avoid warning if the self parameter is not used. */
+ Append(f->code, "(void)self;\n");
+ }
+
+ if (!builtin || !in_class || tuple_arguments > 0 || builtin_ctor) {
+ if (!allow_kwargs) {
+ Append(parse_args, " if (!PyArg_ParseTuple(args, \"");
+ } else {
+ Append(parse_args, " if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"");
+ Append(arglist, ", kwnames");
+ }
+ }
+
+ bool over_varargs = emit_isvarargs_function(n);
+
+ int funpack = fastunpack && !varargs && !over_varargs && !allow_kwargs;
+ int noargs = funpack && (tuple_required == 0 && tuple_arguments == 0);
+ int onearg = funpack && (tuple_required == 1 && tuple_arguments == 1);
+
+ if (builtin && funpack && !overname && !builtin_ctor) {
+ int compactdefargs = ParmList_is_compactdefargs(l);
+ if (!(compactdefargs && (tuple_arguments > tuple_required || varargs))) {
+ String *argattr = NewStringf("%d", tuple_arguments);
+ Setattr(n, "python:argcount", argattr);
+ Delete(argattr);
+ }
+ }
+
+ /* Generate code for argument marshalling */
+ if (funpack) {
+ if (num_arguments > (builtin_self && !constructor ? 1 : 0) && !overname) {
+ sprintf(source, "PyObject *swig_obj[%d]", num_arguments);
+ Wrapper_add_localv(f, "swig_obj", source, NIL);
+ }
+ }
+
+
+ if (constructor && num_arguments == 1 && num_required == 1) {
+ if (Cmp(storage, "explicit") == 0) {
+ if (GetFlag(parent, "feature:implicitconv")) {
+ String *desc = NewStringf("SWIGTYPE%s", SwigType_manglestr(Getattr(n, "type")));
+ Printf(f->code, "if (SWIG_CheckImplicit(%s)) SWIG_fail;\n", desc);
+ Delete(desc);
+ }
+ }
+ }
+
+ if (builtin_ctor && checkAttribute(n, "access", "protected")) {
+ String *tmp_none_comparison = Copy(none_comparison);
+ Replaceall(tmp_none_comparison, "$arg", "self");
+ Printf(self_parse, "if (!(%s)) {\n", tmp_none_comparison);
+ Printv(self_parse, " SWIG_SetErrorMsg(PyExc_RuntimeError, \"accessing abstract class or protected constructor\");\n SWIG_fail;\n}\n", NIL);
+ Delete(tmp_none_comparison);
+ }
+
+ int use_parse = 0;
+ Append(kwargs, "{");
+ for (i = 0, p = l; i < num_arguments; i++) {
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+ bool parse_from_tuple = (i > 0 || !add_self);
+ if (SwigType_type(pt) == T_VARARGS) {
+ parse_from_tuple = false;
+ num_fixed_arguments -= atoi(Char(Getattr(p, "tmap:in:numinputs")));
+ }
+ if (!parse_from_tuple)
+ sprintf(source, "self");
+ else if (funpack)
+ sprintf(source, "swig_obj[%d]", add_self && !overname ? i - 1 : i);
+ else
+ sprintf(source, "obj%d", builtin_ctor ? i + 1 : i);
+
+ if (parse_from_tuple) {
+ Printf(arglist, ", ");
+ if (i == num_required)
+ Putc('|', parse_args); /* Optional argument separator */
+ }
+
+ /* Keyword argument handling */
+ if (allow_kwargs && parse_from_tuple) {
+ String *name = makeParameterName(n, p, i + 1);
+ Printf(kwargs, " (char *)\"%s\", ", name);
+ Delete(name);
+ }
+
+ /* Look for an input typemap */
+ if ((tm = Getattr(p, "tmap:in"))) {
+ String *parse = Getattr(p, "tmap:in:parse");
+ if (!parse) {
+ if (builtin_self) {
+ Replaceall(tm, "$self", "self");
+ } else if (funpack) {
+ Replaceall(tm, "$self", "swig_obj[0]");
+ } else {
+ Replaceall(tm, "$self", "obj0");
+ }
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source); /* Save the location of the object */
+
+ if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+
+ if (Getattr(p, "tmap:in:implicitconv")) {
+ const char *convflag = "0";
+ if (!Getattr(p, "hidden")) {
+ SwigType *ptype = Getattr(p, "type");
+ convflag = get_implicitconv_flag(classLookup(ptype));
+ }
+ Replaceall(tm, "$implicitconv", convflag);
+ Setattr(p, "implicitconv", convflag);
+ }
+
+ if (parse_from_tuple)
+ Putc('O', parse_args);
+ if (!funpack && parse_from_tuple) {
+ Wrapper_add_localv(f, source, "PyObject *", source, "= 0", NIL);
+ Printf(arglist, "&%s", source);
+ }
+ if (i >= num_required)
+ Printv(get_pointers, "if (", source, ") {\n", NIL);
+ Printv(get_pointers, tm, "\n", NIL);
+ if (i >= num_required)
+ Printv(get_pointers, "}\n", NIL);
+
+ } else {
+ use_parse = 1;
+ Append(parse_args, parse);
+ if (parse_from_tuple)
+ Printf(arglist, "&%s", ln);
+ }
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ break;
+ }
+ }
+
+ /* finish argument marshalling */
+ Append(kwargs, " NULL }");
+ if (allow_kwargs) {
+ Printv(f->locals, " char * kwnames[] = ", kwargs, ";\n", NIL);
+ }
+
+ if (use_parse || allow_kwargs) {
+ Printf(parse_args, ":%s\"", iname);
+ Printv(parse_args, arglist, ")) SWIG_fail;\n", NIL);
+ funpack = 0;
+ } else {
+ Clear(parse_args);
+
+ if (funpack) {
+ Clear(f->def);
+ if (overname) {
+ if (noargs) {
+ Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, Py_ssize_t nobjs, PyObject **SWIGUNUSEDPARM(swig_obj)) {", NIL);
+ } else {
+ Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {", NIL);
+ }
+ /* Avoid warning if the self parameter is not used. */
+ Append(f->def, "(void)self;\n");
+ Printf(parse_args, "if ((nobjs < %d) || (nobjs > %d)) SWIG_fail;\n", num_required, num_arguments);
+ } else {
+ int is_tp_call = Equal(Getattr(n, "feature:python:slot"), "tp_call");
+ Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, PyObject *args", builtin_kwargs, ") {", NIL);
+ /* Avoid warning if the self parameter is not used. */
+ Append(f->def, "(void)self;\n");
+ if (builtin_ctor)
+ Printf(parse_args, "if (!SWIG_Python_CheckNoKeywords(kwargs, \"%s\")) SWIG_fail;\n", iname);
+ if (onearg && !builtin_ctor && !is_tp_call) {
+ Printf(parse_args, "if (!args) SWIG_fail;\n");
+ Append(parse_args, "swig_obj[0] = args;\n");
+ } else if (!noargs) {
+ Printf(parse_args, "if (!SWIG_Python_UnpackTuple(args, \"%s\", %d, %d, swig_obj)) SWIG_fail;\n", iname, num_fixed_arguments, tuple_arguments);
+ } else if (noargs) {
+ Printf(parse_args, "if (!SWIG_Python_UnpackTuple(args, \"%s\", %d, %d, 0)) SWIG_fail;\n", iname, num_fixed_arguments, tuple_arguments);
+ }
+ }
+ } else {
+ if (builtin_ctor)
+ Printf(parse_args, "if (!SWIG_Python_CheckNoKeywords(kwargs, \"%s\")) SWIG_fail;\n", iname);
+ if (builtin && in_class && tuple_arguments == 0) {
+ Printf(parse_args, " if (args && PyTuple_Check(args) && PyTuple_GET_SIZE(args) > 0) SWIG_exception_fail(SWIG_TypeError, \"%s takes no arguments\");\n", iname);
+ } else {
+ Printf(parse_args, "if (!PyArg_UnpackTuple(args, \"%s\", %d, %d", iname, num_fixed_arguments, tuple_arguments);
+ Printv(parse_args, arglist, ")) SWIG_fail;\n", NIL);
+ }
+ }
+ }
+
+ /* Now piece together the first part of the wrapper function */
+ Printv(f->code, self_parse, parse_args, get_pointers, NIL);
+
+ /* Check for trailing varargs */
+ if (varargs) {
+ if (p && (tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", "varargs");
+ Printv(f->code, tm, "\n", NIL);
+ }
+ }
+
+ /* Insert constraint checking code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ for (p = l; p;) {
+ if (!Getattr(p, "tmap:in:parse") && (tm = Getattr(p, "tmap:freearg"))) {
+ if (Getattr(p, "tmap:freearg:implicitconv")) {
+ const char *convflag = "0";
+ if (!Getattr(p, "hidden")) {
+ SwigType *ptype = Getattr(p, "type");
+ convflag = get_implicitconv_flag(classLookup(ptype));
+ }
+ if (strcmp(convflag, "0") == 0) {
+ tm = 0;
+ }
+ }
+ if (tm && (Len(tm) != 0)) {
+ Printv(cleanup, tm, "\n", NIL);
+ }
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert argument output code */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* if the object is a director, and the method call originated from its
+ * underlying python object, resolve the call by going up the c++
+ * inheritance chain. otherwise try to resolve the method in python.
+ * without this check an infinite loop is set up between the director and
+ * shadow class method calls.
+ */
+
+ // NOTE: this code should only be inserted if this class is the
+ // base class of a director class. however, in general we haven't
+ // yet analyzed all classes derived from this one to see if they are
+ // directors. furthermore, this class may be used as the base of
+ // a director class defined in a completely different module at a
+ // later time, so this test must be included whether or not directorbase
+ // is true. we do skip this code if directors have not been enabled
+ // at the command line to preserve source-level compatibility with
+ // non-polymorphic swig. also, if this wrapper is for a smart-pointer
+ // method, there is no need to perform the test since the calling object
+ // (the smart-pointer) and the director object (the "pointee") are
+ // distinct.
+
+ director_method = is_member_director(n) && !is_smart_pointer() && !destructor;
+ if (director_method) {
+ Wrapper_add_local(f, "director", "Swig::Director *director = 0");
+ Append(f->code, "director = SWIG_DIRECTOR_CAST(arg1);\n");
+ if (dirprot_mode() && !is_public(n)) {
+ Printf(f->code, "if (!director || !(director->swig_get_inner(\"%s\"))) {\n", name);
+ Printf(f->code, "SWIG_SetErrorMsg(PyExc_RuntimeError,\"accessing protected member %s\");\n", name);
+ Append(f->code, "SWIG_fail;\n");
+ Append(f->code, "}\n");
+ }
+ Wrapper_add_local(f, "upcall", "bool upcall = false");
+ if (funpack) {
+ const char *self_parm = builtin_self ? "self" : "swig_obj[0]";
+ Printf(f->code, "upcall = (director && (director->swig_get_self()==%s));\n", self_parm);
+ } else {
+ const char *self_parm = builtin_self ? "self" : "obj0";
+ Printf(f->code, "upcall = (director && (director->swig_get_self()==%s));\n", self_parm);
+ }
+ }
+
+ /* Emit the function call */
+ if (director_method) {
+ Append(f->code, "try {\n");
+ } else {
+ if (allow_thread) {
+ String *preaction = NewString("");
+ thread_begin_allow(n, preaction);
+ Setattr(n, "wrap:preaction", preaction);
+
+ String *postaction = NewString("");
+ thread_end_allow(n, postaction);
+ Setattr(n, "wrap:postaction", postaction);
+ }
+ }
+
+ Setattr(n, "wrap:name", wname);
+
+ Swig_director_emit_dynamic_cast(n, f);
+ String *actioncode = emit_action(n);
+
+ if (director_method) {
+ Append(actioncode, "} catch (Swig::DirectorException&) {\n");
+ Append(actioncode, " SWIG_fail;\n");
+ Append(actioncode, "}\n");
+ }
+
+ /* This part below still needs cleanup */
+
+ /* Return the function value */
+ tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode);
+
+ if (tm) {
+ if (builtin_self) {
+ Replaceall(tm, "$self", "self");
+ } else if (funpack) {
+ Replaceall(tm, "$self", "swig_obj[0]");
+ } else {
+ Replaceall(tm, "$self", "obj0");
+ }
+ Replaceall(tm, "$result", "resultobj");
+ if (builtin_ctor) {
+ Replaceall(tm, "$owner", "SWIG_BUILTIN_INIT");
+ } else if (handled_as_init) {
+ Replaceall(tm, "$owner", "SWIG_POINTER_NEW");
+ } else {
+ if (GetFlag(n, "feature:new")) {
+ Replaceall(tm, "$owner", "SWIG_POINTER_OWN");
+ } else {
+ Replaceall(tm, "$owner", "0");
+ }
+ }
+
+ // Unwrap return values that are director classes so that the original Python object is returned instead.
+ if (!constructor && Swig_director_can_unwrap(n)) {
+ Wrapper_add_local(f, "director", "Swig::Director *director = 0");
+ Printf(f->code, "director = SWIG_DIRECTOR_CAST(%s);\n", Swig_cresult_name());
+ Append(f->code, "if (director) {\n");
+ Append(f->code, " resultobj = director->swig_get_self();\n");
+ Append(f->code, " Py_INCREF(resultobj);\n");
+ Append(f->code, "} else {\n");
+ Printf(f->code, "%s\n", tm);
+ Append(f->code, "}\n");
+ } else {
+ Printf(f->code, "%s\n", tm);
+ }
+
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(d, 0), name);
+ }
+ emit_return_variable(n, d, f);
+
+ /* Output argument output code */
+ Printv(f->code, outarg, NIL);
+
+ /* Output cleanup code */
+ int need_cleanup = Len(cleanup) != 0;
+ if (need_cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+
+ /* Look to see if there is any newfree cleanup code */
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+
+ if (director_method) {
+ if ((tm = Swig_typemap_lookup("directorfree", n, Swig_cresult_name(), 0))) {
+ Replaceall(tm, "$input", Swig_cresult_name());
+ Replaceall(tm, "$result", "resultobj");
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+ }
+
+ if (builtin_ctor)
+ Append(f->code, " return resultobj == Py_None ? -1 : 0;\n");
+ else
+ Append(f->code, " return resultobj;\n");
+
+ /* Error handling code */
+
+ Append(f->code, "fail:\n");
+ if (need_cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+ if (builtin_ctor) {
+ Printv(f->code, " return -1;\n", NIL);
+ } else {
+ if (GetFlag(n, "feature:python:maybecall")) {
+ Append(f->code, " PyErr_Clear();\n");
+ Append(f->code, " Py_INCREF(Py_NotImplemented);\n");
+ Append(f->code, " return Py_NotImplemented;\n");
+ } else {
+ Printv(f->code, " return NULL;\n", NIL);
+ }
+ }
+
+ Append(f->code, "}\n");
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", iname);
+ Replaceall(f->code, "$result", "resultobj");
+
+ if (builtin_self) {
+ Replaceall(f->code, "$self", "self");
+ } else if (funpack) {
+ Replaceall(f->code, "$self", "swig_obj[0]");
+ } else {
+ Replaceall(f->code, "$self", "obj0");
+ }
+
+ /* Dump the function out */
+ Wrapper_print(f, f_wrappers);
+
+ /* If varargs. Need to emit a varargs stub */
+ if (varargs) {
+ DelWrapper(f);
+ f = NewWrapper();
+ if (funpack) {
+ // Note: funpack is currently always false for varargs
+ Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {", NIL);
+ } else {
+ Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, PyObject *args", builtin_kwargs, ") {", NIL);
+ }
+ Wrapper_add_local(f, "resultobj", builtin_ctor ? "int resultobj" : "PyObject *resultobj");
+ Wrapper_add_local(f, "varargs", "PyObject *varargs");
+ Wrapper_add_local(f, "newargs", "PyObject *newargs");
+ if (funpack) {
+ Wrapper_add_local(f, "i", "int i");
+ Printf(f->code, "newargs = PyTuple_New(%d);\n", num_fixed_arguments);
+ Printf(f->code, "for (i = 0; i < %d; ++i) {\n", num_fixed_arguments);
+ Printf(f->code, " PyTuple_SET_ITEM(newargs, i, swig_obj[i]);\n");
+ Printf(f->code, " Py_XINCREF(swig_obj[i]);\n");
+ Printf(f->code, "}\n");
+ Printf(f->code, "varargs = PyTuple_New(nobjs > %d ? nobjs - %d : 0);\n", num_fixed_arguments, num_fixed_arguments);
+ Printf(f->code, "for (i = 0; i < nobjs - %d; ++i) {\n", num_fixed_arguments);
+ Printf(f->code, " PyTuple_SET_ITEM(newargs, i, swig_obj[i + %d]);\n", num_fixed_arguments);
+ Printf(f->code, " Py_XINCREF(swig_obj[i + %d]);\n", num_fixed_arguments);
+ Printf(f->code, "}\n");
+ } else {
+ Printf(f->code, "newargs = PyTuple_GetSlice(args, 0, %d);\n", num_fixed_arguments);
+ Printf(f->code, "varargs = PyTuple_GetSlice(args, %d, PyTuple_Size(args));\n", num_fixed_arguments);
+ }
+ Printf(f->code, "resultobj = %s__varargs__(%s, newargs, varargs%s);\n", wname, builtin ? "self" : "NULL", strlen(builtin_kwargs) == 0 ? "" : ", kwargs");
+ Append(f->code, "Py_XDECREF(newargs);\n");
+ Append(f->code, "Py_XDECREF(varargs);\n");
+ Append(f->code, "return resultobj;\n");
+ Append(f->code, "}\n");
+ Wrapper_print(f, f_wrappers);
+ }
+
+ bool use_static_method = flat_static_method || !Swig_storage_isstatic_custom(n, "staticmemberfunctionHandler:storage");
+ /* Now register the function with the interpreter. */
+ if (!Getattr(n, "sym:overloaded")) {
+ if (!builtin_self && (use_static_method || !builtin))
+ add_method(iname, wname, allow_kwargs, n, funpack, num_required, num_arguments);
+
+ /* Create a shadow for this function (if enabled and not in a member function) */
+ if (!builtin && shadow && !(shadow & PYSHADOW_MEMBER) && use_static_method) {
+ emitFunctionShadowHelper(n, in_class ? f_shadow_stubs : f_shadow, iname, allow_kwargs);
+ }
+
+ } else {
+ if (!Getattr(n, "sym:nextSibling")) {
+ dispatchFunction(n, linkage, funpack, builtin_self, builtin_ctor, director_class, use_static_method);
+ }
+ }
+
+ // Put this in tp_init of the PyTypeObject
+ if (builtin_ctor) {
+ if ((director_method || !is_private(n)) && !Getattr(class_members, iname)) {
+ Setattr(class_members, iname, n);
+ if (!builtin_tp_init)
+ builtin_tp_init = Swig_name_wrapper(iname);
+ }
+ }
+
+ /* If this is a builtin type, create a PyGetSetDef entry for this member variable. */
+ if (builtin) {
+ const char *memname = "__dict__";
+ Hash *h = Getattr(builtin_getset, memname);
+ if (!h) {
+ h = NewHash();
+ Setattr(builtin_getset, memname, h);
+ Delete(h);
+ }
+ Setattr(h, "getter", "SwigPyObject_get___dict__");
+ if (!Getattr(h, "doc")) {
+ Setattr(n, "doc:high:name", Getattr(n, "name"));
+ Setattr(h, "doc", cdocstring(n, AUTODOC_VAR));
+ }
+ }
+
+ if (builtin_getter) {
+ String *memname = Getattr(n, "membervariableHandler:sym:name");
+ if (!memname)
+ memname = iname;
+ Hash *h = Getattr(builtin_getset, memname);
+ if (!h) {
+ h = NewHash();
+ Setattr(builtin_getset, memname, h);
+ Delete(h);
+ }
+ Setattr(h, "getter", wrapper_name);
+ Delattr(n, "memberget");
+ if (!Getattr(h, "doc")) {
+ Setattr(n, "doc:high:name", Getattr(n, "name"));
+ String *ds = cdocstring(n, AUTODOC_VAR);
+ Setattr(h, "doc", ds);
+ Delete(ds);
+ }
+ }
+ if (builtin_setter) {
+ String *memname = Getattr(n, "membervariableHandler:sym:name");
+ if (!memname)
+ memname = iname;
+ Hash *h = Getattr(builtin_getset, memname);
+ if (!h) {
+ h = NewHash();
+ Setattr(builtin_getset, memname, h);
+ Delete(h);
+ }
+ Setattr(h, "setter", wrapper_name);
+ Delattr(n, "memberset");
+ if (!Getattr(h, "doc")) {
+ Setattr(n, "doc:high:name", Getattr(n, "name"));
+ String *ds = cdocstring(n, AUTODOC_VAR);
+ Setattr(h, "doc", ds);
+ Delete(ds);
+ }
+ }
+
+ if (in_class && builtin) {
+ /* Handle operator overloads for builtin types */
+ String *slot = Getattr(n, "feature:python:slot");
+ if (slot) {
+ String *func_type = Getattr(n, "feature:python:slot:functype");
+ String *closure_decl = getClosure(func_type, wrapper_name, overname ? 0 : funpack);
+ String *feature_name = NewStringf("feature:python:%s", slot);
+ String *closure_name = 0;
+ if (closure_decl) {
+ closure_name = NewStringf("%s_%s_closure", wrapper_name, func_type);
+ if (!GetFlag(builtin_closures, closure_name))
+ Printf(builtin_closures_code, "%s /* defines %s */\n\n", closure_decl, closure_name);
+ SetFlag(builtin_closures, closure_name);
+ Delete(closure_decl);
+ } else {
+ closure_name = Copy(wrapper_name);
+ }
+ if (func_type) {
+ String *s = NewStringf("%s", closure_name);
+ Delete(closure_name);
+ closure_name = s;
+ }
+ Setattr(parent, feature_name, closure_name);
+ Delete(feature_name);
+ Delete(closure_name);
+ }
+
+ /* Handle comparison operators for builtin types */
+ String *compare = Getattr(n, "feature:python:compare");
+ if (compare) {
+ Hash *richcompare = Getattr(parent, "python:richcompare");
+ assert(richcompare);
+ Setattr(richcompare, compare, wrapper_name);
+ }
+ }
+
+ Delete(self_parse);
+ Delete(parse_args);
+ Delete(linkage);
+ Delete(arglist);
+ Delete(get_pointers);
+ Delete(cleanup);
+ Delete(outarg);
+ Delete(kwargs);
+ Delete(wname);
+ DelWrapper(f);
+ Delete(wrapper_name);
+ return SWIG_OK;
+ }
+
+
+
+ /* ------------------------------------------------------------
+ * variableWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int variableWrapper(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+
+ static int have_globals = 0;
+ String *tm;
+ Wrapper *getf, *setf;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ getf = NewWrapper();
+ setf = NewWrapper();
+
+ /* If this is our first call, add the globals variable to the
+ Python dictionary. */
+
+ if (!have_globals) {
+ Printf(f_init, "\t globals = SWIG_globals();\n");
+ Printf(f_init, "\t if (!globals) {\n");
+ Printf(f_init, " PyErr_SetString(PyExc_TypeError, \"Failure to create SWIG globals.\");\n");
+ Printf(f_init, "#if PY_VERSION_HEX >= 0x03000000\n");
+ Printf(f_init, "\t return NULL;\n");
+ Printf(f_init, "#else\n");
+ Printf(f_init, "\t return;\n");
+ Printf(f_init, "#endif\n");
+ Printf(f_init, "\t }\n");
+ Printf(f_init, "\t PyDict_SetItemString(md, \"%s\", globals);\n", global_name);
+ if (builtin)
+ Printf(f_init, "\t SwigPyBuiltin_AddPublicSymbol(public_interface, \"%s\");\n", global_name);
+ have_globals = 1;
+ if (!builtin && shadow && !(shadow & PYSHADOW_MEMBER)) {
+ Printf(f_shadow_stubs, "%s = %s.%s\n", global_name, module, global_name);
+ }
+ }
+ int assignable = is_assignable(n);
+
+ if (!builtin && shadow && !assignable && !in_class)
+ Printf(f_shadow_stubs, "%s = %s.%s\n", iname, global_name, iname);
+
+ String *getname = Swig_name_get(NSPACE_TODO, iname);
+ String *setname = Swig_name_set(NSPACE_TODO, iname);
+ String *vargetname = NewStringf("Swig_var_%s", getname);
+ String *varsetname = NewStringf("Swig_var_%s", setname);
+
+ /* Create a function for setting the value of the variable */
+ if (assignable) {
+ Setattr(n, "wrap:name", varsetname);
+ if (builtin && in_class) {
+ String *set_wrapper = Swig_name_wrapper(setname);
+ Setattr(n, "pybuiltin:setter", set_wrapper);
+ Delete(set_wrapper);
+ }
+ Printf(setf->def, "SWIGINTERN int %s(PyObject *_val) {", varsetname);
+ if ((tm = Swig_typemap_lookup("varin", n, name, 0))) {
+ Replaceall(tm, "$input", "_val");
+ if (Getattr(n, "tmap:varin:implicitconv")) {
+ Replaceall(tm, "$implicitconv", get_implicitconv_flag(n));
+ }
+ emit_action_code(n, setf->code, tm);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s.\n", SwigType_str(t, 0));
+ }
+ Printv(setf->code, " return 0;\n", NULL);
+ Append(setf->code, "fail:\n");
+ Printv(setf->code, " return 1;\n", NULL);
+ } else {
+ /* Is a readonly variable. Issue an error */
+ if (CPlusPlus) {
+ Printf(setf->def, "SWIGINTERN int %s(PyObject *) {", varsetname);
+ } else {
+ Printf(setf->def, "SWIGINTERN int %s(PyObject *_val SWIGUNUSED) {", varsetname);
+ }
+ Printv(setf->code, " SWIG_Error(SWIG_AttributeError,\"Variable ", iname, " is read-only.\");\n", " return 1;\n", NIL);
+ }
+
+ Append(setf->code, "}\n");
+ Wrapper_print(setf, f_wrappers);
+
+ /* Create a function for getting the value of a variable */
+ Setattr(n, "wrap:name", vargetname);
+ if (builtin && in_class) {
+ String *get_wrapper = Swig_name_wrapper(getname);
+ Setattr(n, "pybuiltin:getter", get_wrapper);
+ Delete(get_wrapper);
+ }
+ int addfail = 0;
+ Printf(getf->def, "SWIGINTERN PyObject *%s(void) {", vargetname);
+ Wrapper_add_local(getf, "pyobj", "PyObject *pyobj = 0");
+ if (builtin) {
+ Wrapper_add_local(getf, "self", "PyObject *self = 0");
+ Append(getf->code, " (void)self;\n");
+ }
+ if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
+ Replaceall(tm, "$result", "pyobj");
+ addfail = emit_action_code(n, getf->code, tm);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VAROUT_UNDEF, input_file, line_number, "Unable to read variable of type %s\n", SwigType_str(t, 0));
+ }
+ Append(getf->code, " return pyobj;\n");
+ if (addfail) {
+ Append(getf->code, "fail:\n");
+ Append(getf->code, " return NULL;\n");
+ }
+ Append(getf->code, "}\n");
+
+ Wrapper_print(getf, f_wrappers);
+
+ /* Now add this to the variable linking mechanism */
+ Printf(f_init, "\t SWIG_addvarlink(globals, \"%s\", %s, %s);\n", iname, vargetname, varsetname);
+ if (builtin && shadow && !assignable && !in_class) {
+ Printf(f_init, "\t PyDict_SetItemString(md, \"%s\", PyObject_GetAttrString(globals, \"%s\"));\n", iname, iname);
+ Printf(f_init, "\t SwigPyBuiltin_AddPublicSymbol(public_interface, \"%s\");\n", iname);
+ }
+ Delete(vargetname);
+ Delete(varsetname);
+ Delete(getname);
+ Delete(setname);
+ DelWrapper(setf);
+ DelWrapper(getf);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constantWrapper()
+ * ------------------------------------------------------------ */
+
+ /* Determine if the node requires the _swigconstant code to be generated */
+ bool needs_swigconstant(Node *n) {
+ SwigType *type = Getattr(n, "type");
+ SwigType *qtype = SwigType_typedef_resolve_all(type);
+ SwigType *uqtype = SwigType_strip_qualifiers(qtype);
+ bool result = false;
+
+ /* Note, that we need special handling for function pointers, as
+ * SwigType_base(fptr) does not return the underlying pointer-to-function
+ * type but the return-type of function. */
+ if (!SwigType_isfunction(uqtype) && !SwigType_isfunctionpointer(uqtype)) {
+ SwigType *basetype = SwigType_base(uqtype);
+ result = SwigType_isclass(basetype) != 0;
+ Delete(basetype);
+ }
+
+ Delete(qtype);
+ Delete(uqtype);
+
+ return result;
+ }
+
+ virtual int constantWrapper(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ String *tm;
+ int have_tm = 0;
+ int have_builtin_symname = 0;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ /* Special hook for member pointer */
+ if (SwigType_type(type) == T_MPOINTER) {
+ String *wname = Swig_name_wrapper(iname);
+ String *str = SwigType_str(type, wname);
+ Printf(f_header, "static %s = %s;\n", str, value);
+ Delete(str);
+ value = wname;
+ }
+
+ if ((tm = Swig_typemap_lookup("consttab", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ Printf(const_code, "%s,\n", tm);
+ Delete(tm);
+ have_tm = 1;
+ }
+
+
+ if (builtin && in_class && Getattr(n, "pybuiltin:symname")) {
+ have_builtin_symname = 1;
+ Swig_require("builtin_constantWrapper", n, "*sym:name", "pybuiltin:symname", NIL);
+ Setattr(n, "sym:name", Getattr(n, "pybuiltin:symname"));
+ }
+
+ if ((tm = Swig_typemap_lookup("constcode", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ if (needs_swigconstant(n) && !builtin && shadow && !(shadow & PYSHADOW_MEMBER) && (!in_class || !Getattr(n, "feature:python:callback"))) {
+ // Generate `*_swigconstant()` method which registers the new constant.
+ //
+ // *_swigconstant methods are required for constants of class type.
+ // Class types are registered in shadow file (see *_swigregister). The
+ // instances of class must be created (registered) after the type is
+ // registered, so we can't let SWIG_init() to register constants of
+ // class type (the SWIG_init() is called before shadow classes are
+ // defined and registered).
+ Printf(f_wrappers, "SWIGINTERN PyObject *%s_swigconstant(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {\n", iname);
+ Printf(f_wrappers, tab2 "PyObject *module;\n");
+ Printf(f_wrappers, tab2 "PyObject *d;\n");
+ Printf(f_wrappers, tab2 "if (!SWIG_Python_UnpackTuple(args, \"swigconstant\", 1, 1, &module)) return NULL;\n");
+ Printf(f_wrappers, tab2 "d = PyModule_GetDict(module);\n");
+ Printf(f_wrappers, tab2 "if (!d) return NULL;\n");
+ Printf(f_wrappers, tab2 "%s\n", tm);
+ Printf(f_wrappers, tab2 "return SWIG_Py_Void();\n");
+ Printf(f_wrappers, "}\n\n\n");
+
+ // Register the method in SwigMethods array
+ String *cname = NewStringf("%s_swigconstant", iname);
+ add_method(cname, cname, 0, 0, 1, 1, 1);
+ Delete(cname);
+ } else {
+ Printf(f_init, "%s\n", tm);
+ }
+ Delete(tm);
+ have_tm = 1;
+ }
+
+ if (have_builtin_symname)
+ Swig_restore(n);
+
+ if (!have_tm) {
+ Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value.\n");
+ return SWIG_NOWRAP;
+ }
+
+ if (!builtin && shadow && !(shadow & PYSHADOW_MEMBER)) {
+ String *f_s;
+ if (!in_class) {
+ f_s = f_shadow;
+ } else {
+ f_s = Getattr(n, "feature:python:callback") ? NIL : f_shadow_stubs;
+ }
+
+ if (f_s) {
+ if (needs_swigconstant(n)) {
+ Printv(f_s, "\n",NIL);
+ Printv(f_s, module, ".", iname, "_swigconstant(",module,")\n", NIL);
+ }
+ Printv(f_s, iname, " = ", module, ".", iname, "\n", NIL);
+ if (have_docstring(n))
+ Printv(f_s, docstring(n, AUTODOC_CONST, tab4), "\n", NIL);
+ }
+ }
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * nativeWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int nativeWrapper(Node *n) {
+ String *name = Getattr(n, "sym:name");
+ String *wrapname = Getattr(n, "wrap:name");
+
+ if (!addSymbol(wrapname, n))
+ return SWIG_ERROR;
+
+ add_method(name, wrapname, 0);
+ if (!builtin && shadow) {
+ Printv(f_shadow_stubs, name, " = ", module, ".", name, "\n", NIL);
+ }
+ return SWIG_OK;
+ }
+
+
+
+ /* ----------------------------------------------------------------------------
+ * BEGIN C++ Director Class modifications
+ * ------------------------------------------------------------------------- */
+
+ /* C++/Python polymorphism demo code
+ *
+ * TODO
+ *
+ * Move some boilerplate code generation to Swig_...() functions.
+ *
+ */
+
+ /* ---------------------------------------------------------------
+ * classDirectorMethod()
+ *
+ * Emit a virtual director method to pass a method call on to the
+ * underlying Python object.
+ * ** Moved down due to gcc-2.96 internal error **
+ * --------------------------------------------------------------- */
+
+ int classDirectorMethods(Node *n);
+
+ int classDirectorMethod(Node *n, Node *parent, String *super);
+
+ /* ------------------------------------------------------------
+ * classDirectorConstructor()
+ * ------------------------------------------------------------ */
+
+ int classDirectorConstructor(Node *n) {
+ Node *parent = Getattr(n, "parentNode");
+ String *sub = NewString("");
+ String *decl = Getattr(n, "decl");
+ String *supername = Swig_class_name(parent);
+ String *classname = NewString("");
+ Printf(classname, "SwigDirector_%s", supername);
+
+ /* insert self parameter */
+ Parm *p;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms = CopyParmList(superparms);
+ String *type = NewString("PyObject");
+ SwigType_add_pointer(type);
+ p = NewParm(type, NewString("self"), n);
+ set_nextSibling(p, parms);
+ parms = p;
+
+ if (!Getattr(n, "defaultargs")) {
+ /* constructor */
+ {
+ Wrapper *w = NewWrapper();
+ String *call;
+ String *basetype = Getattr(parent, "classtype");
+ String *target = Swig_method_decl(0, decl, classname, parms, 0);
+ call = Swig_csuperclass_call(0, basetype, superparms);
+ Printf(w->def, "%s::%s: %s, Swig::Director(self) { \n", classname, target, call);
+ Printf(w->def, " SWIG_DIRECTOR_RGTR((%s *)this, this); \n", basetype);
+ Append(w->def, "}\n");
+ Delete(target);
+ Wrapper_print(w, f_directors);
+ Delete(call);
+ DelWrapper(w);
+ }
+
+ /* constructor header */
+ {
+ String *target = Swig_method_decl(0, decl, classname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+
+ Delete(sub);
+ Delete(classname);
+ Delete(supername);
+ Delete(parms);
+ return Language::classDirectorConstructor(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorDefaultConstructor()
+ * ------------------------------------------------------------ */
+
+ int classDirectorDefaultConstructor(Node *n) {
+ String *classname = Swig_class_name(n);
+ {
+ Node *parent = Swig_methodclass(n);
+ String *basetype = Getattr(parent, "classtype");
+ Wrapper *w = NewWrapper();
+ Printf(w->def, "SwigDirector_%s::SwigDirector_%s(PyObject *self) : Swig::Director(self) { \n", classname, classname);
+ Printf(w->def, " SWIG_DIRECTOR_RGTR((%s *)this, this); \n", basetype);
+ Append(w->def, "}\n");
+ Wrapper_print(w, f_directors);
+ DelWrapper(w);
+ }
+ Printf(f_directors_h, " SwigDirector_%s(PyObject *self);\n", classname);
+ Delete(classname);
+ return Language::classDirectorDefaultConstructor(n);
+ }
+
+
+ /* ------------------------------------------------------------
+ * classDirectorInit()
+ * ------------------------------------------------------------ */
+
+ int classDirectorInit(Node *n) {
+ String *declaration = Swig_director_declaration(n);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "%s\n", declaration);
+ Printf(f_directors_h, "public:\n");
+ Delete(declaration);
+ return Language::classDirectorInit(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorEnd()
+ * ------------------------------------------------------------ */
+
+ int classDirectorEnd(Node *n) {
+ String *classname = Swig_class_name(n);
+
+ if (dirprot_mode()) {
+ /*
+ This implementation uses a std::map<std::string,int>.
+
+ It should be possible to rewrite it using a more elegant way,
+ like copying the Java approach for the 'override' array.
+
+ But for now, this seems to be the least intrusive way.
+ */
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "/* Internal director utilities */\n");
+ Printf(f_directors_h, "public:\n");
+ Printf(f_directors_h, " bool swig_get_inner(const char *swig_protected_method_name) const {\n");
+ Printf(f_directors_h, " std::map<std::string, bool>::const_iterator iv = swig_inner.find(swig_protected_method_name);\n");
+ Printf(f_directors_h, " return (iv != swig_inner.end() ? iv->second : false);\n");
+ Printf(f_directors_h, " }\n");
+
+ Printf(f_directors_h, " void swig_set_inner(const char *swig_protected_method_name, bool swig_val) const {\n");
+ Printf(f_directors_h, " swig_inner[swig_protected_method_name] = swig_val;\n");
+ Printf(f_directors_h, " }\n");
+ Printf(f_directors_h, "private:\n");
+ Printf(f_directors_h, " mutable std::map<std::string, bool> swig_inner;\n");
+
+ }
+ if (director_method_index) {
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "#if defined(SWIG_PYTHON_DIRECTOR_VTABLE)\n");
+ Printf(f_directors_h, "/* VTable implementation */\n");
+ Printf(f_directors_h, " PyObject *swig_get_method(size_t method_index, const char *method_name) const {\n");
+ Printf(f_directors_h, " PyObject *method = vtable[method_index];\n");
+ Printf(f_directors_h, " if (!method) {\n");
+ Printf(f_directors_h, " swig::SwigVar_PyObject name = SWIG_Python_str_FromChar(method_name);\n");
+ Printf(f_directors_h, " method = PyObject_GetAttr(swig_get_self(), name);\n");
+ Printf(f_directors_h, " if (!method) {\n");
+ Printf(f_directors_h, " std::string msg = \"Method in class %s doesn't exist, undefined \";\n", classname);
+ Printf(f_directors_h, " msg += method_name;\n");
+ Printf(f_directors_h, " Swig::DirectorMethodException::raise(msg.c_str());\n");
+ Printf(f_directors_h, " }\n");
+ Printf(f_directors_h, " vtable[method_index] = method;\n");
+ Printf(f_directors_h, " }\n");
+ Printf(f_directors_h, " return method;\n");
+ Printf(f_directors_h, " }\n");
+ Printf(f_directors_h, "private:\n");
+ Printf(f_directors_h, " mutable swig::SwigVar_PyObject vtable[%d];\n", director_method_index);
+ Printf(f_directors_h, "#endif\n\n");
+ }
+
+ Printf(f_directors_h, "};\n\n");
+ return Language::classDirectorEnd(n);
+ }
+
+
+ /* ------------------------------------------------------------
+ * classDirectorDisown()
+ * ------------------------------------------------------------ */
+
+ int classDirectorDisown(Node *n) {
+ int result;
+ int oldshadow = shadow;
+ /* disable shadowing */
+ if (shadow)
+ shadow = shadow | PYSHADOW_MEMBER;
+ result = Language::classDirectorDisown(n);
+ shadow = oldshadow;
+ if (shadow) {
+ if (builtin) {
+ String *rname = SwigType_namestr(real_classname);
+ Printf(builtin_methods, " { \"__disown__\", Swig::Director::swig_pyobj_disown< %s >, METH_NOARGS, \"\" },\n", rname);
+ Delete(rname);
+ } else {
+ String *symname = Getattr(n, "sym:name");
+ String *mrename = Swig_name_disown(NSPACE_TODO, symname); //Getattr(n, "name"));
+ Printv(f_shadow, tab4, "def __disown__(self):\n", NIL);
+ Printv(f_shadow, tab8, "self.this.disown()\n", NIL);
+ Printv(f_shadow, tab8, module, ".", mrename, "(self)\n", NIL);
+ Printv(f_shadow, tab8, "return weakref.proxy(self)\n", NIL);
+ Delete(mrename);
+ }
+ }
+ return result;
+ }
+
+ /* ----------------------------------------------------------------------------
+ * END of C++ Director Class modifications
+ * ------------------------------------------------------------------------- */
+
+
+ /* ------------------------------------------------------------
+ * classDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int classDeclaration(Node *n) {
+ if (shadow && !Getattr(n, "feature:onlychildren")) {
+ Node *mod = Getattr(n, "module");
+ if (mod) {
+ String *modname = Getattr(mod, "name");
+ Node *options = Getattr(mod, "options");
+ String *pkg = options ? Getattr(options, "package") : 0;
+ String *sym = Getattr(n, "sym:name");
+ String *importname = import_name_string(package, mainmodule, pkg, modname, sym);
+ Setattr(n, "python:proxy", importname);
+ Delete(importname);
+ }
+ }
+ int result = Language::classDeclaration(n);
+ return result;
+ }
+
+ /* ------------------------------------------------------------
+ * classHandler()
+ * ------------------------------------------------------------ */
+
+ String *add_explicit_scope(String *s) {
+ if (!Strstr(s, "::")) {
+ String *ss = NewStringf("::%s", s);
+ Delete(s);
+ s = ss;
+ }
+ return s;
+ }
+
+ void builtin_pre_decl(Node *n) {
+ String *name = Getattr(n, "name");
+ String *rname = add_explicit_scope(SwigType_namestr(name));
+ String *mname = SwigType_manglestr(rname);
+
+ Printf(f_init, "\n/* type '%s' */\n", rname);
+ Printf(f_init, " builtin_pytype = (PyTypeObject *)&SwigPyBuiltin_%s_type;\n", mname);
+ Printf(f_init, " builtin_pytype->tp_dict = d = PyDict_New();\n");
+
+ Delete(rname);
+ Delete(mname);
+ }
+
+ void builtin_post_decl(File *f, Node *n) {
+ String *name = Getattr(n, "name");
+ String *pname = Copy(name);
+ SwigType_add_pointer(pname);
+ String *symname = Getattr(n, "sym:name");
+ String *rname = add_explicit_scope(SwigType_namestr(name));
+ String *mname = SwigType_manglestr(rname);
+ String *pmname = SwigType_manglestr(pname);
+ String *templ = NewStringf("SwigPyBuiltin_%s", mname);
+ int funpack = fastunpack;
+ static String *tp_new = NewString("PyType_GenericNew");
+
+ if (have_builtin_static_member_method_callback) {
+ Printf(f_init, " SWIG_Python_FixMethods(SwigPyBuiltin_%s_methods, swig_const_table, swig_types, swig_type_initial);\n", mname);
+ }
+
+ Printv(f_init, " SwigPyBuiltin_SetMetaType(builtin_pytype, metatype);\n", NIL);
+
+ // We can’t statically initialize a structure member with a function defined in another C module
+ // So this is done in the initialization function instead, see https://docs.python.org/2/extending/newtypes.html
+ Printf(f_init, " builtin_pytype->tp_new = %s;\n", getSlot(n, "feature:python:tp_new", tp_new));
+
+ Printv(f_init, " builtin_base_count = 0;\n", NIL);
+ List *baselist = Getattr(n, "bases");
+ if (baselist) {
+ int base_count = 0;
+ for (Iterator b = First(baselist); b.item; b = Next(b)) {
+ String *bname = Getattr(b.item, "name");
+ if (!bname || GetFlag(b.item, "feature:ignore"))
+ continue;
+ base_count++;
+ String *base_name = Copy(bname);
+ SwigType_add_pointer(base_name);
+ String *base_mname = SwigType_manglestr(base_name);
+ Printf(f_init, " builtin_basetype = SWIG_MangledTypeQuery(\"%s\");\n", base_mname);
+ Printv(f_init, " if (builtin_basetype && builtin_basetype->clientdata && ((SwigPyClientData *) builtin_basetype->clientdata)->pytype) {\n", NIL);
+ Printv(f_init, " builtin_bases[builtin_base_count++] = ((SwigPyClientData *) builtin_basetype->clientdata)->pytype;\n", NIL);
+ Printv(f_init, " } else {\n", NIL);
+ Printf(f_init, " PyErr_SetString(PyExc_TypeError, \"Could not create type '%s' as base '%s' has not been initialized.\\n\");\n", symname, bname);
+ Printv(f_init, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
+ Printv(f_init, " return NULL;\n", NIL);
+ Printv(f_init, "#else\n", NIL);
+ Printv(f_init, " return;\n", NIL);
+ Printv(f_init, "#endif\n", NIL);
+ Printv(f_init, " }\n", NIL);
+ Delete(base_name);
+ Delete(base_mname);
+ }
+ if (base_count > max_bases)
+ max_bases = base_count;
+ }
+ Printv(f_init, " builtin_bases[builtin_base_count] = NULL;\n", NIL);
+ Printv(f_init, " SwigPyBuiltin_InitBases(builtin_pytype, builtin_bases);\n", NIL);
+ builtin_bases_needed = 1;
+
+ // Check for non-public destructor, in which case tp_dealloc will issue
+ // a warning and allow the memory to leak. Any class that doesn't explicitly
+ // have a private/protected destructor has an implicit public destructor.
+ static String *tp_dealloc_bad = NewString("SwigPyBuiltin_BadDealloc");
+
+ String *getset_name = NewStringf("%s_getset", templ);
+ String *methods_name = NewStringf("%s_methods", templ);
+ String *getset_def = NewString("");
+ Printf(getset_def, "SWIGINTERN PyGetSetDef %s[] = {\n", getset_name);
+
+ // All objects have 'this' and 'thisown' attributes
+ Printv(f_init, "PyDict_SetItemString(d, \"this\", this_descr);\n", NIL);
+ Printv(f_init, "PyDict_SetItemString(d, \"thisown\", thisown_descr);\n", NIL);
+
+ // Now, the rest of the attributes
+ for (Iterator member_iter = First(builtin_getset); member_iter.item; member_iter = Next(member_iter)) {
+ String *memname = member_iter.key;
+ Hash *mgetset = member_iter.item;
+ String *getter = Getattr(mgetset, "getter");
+ String *setter = Getattr(mgetset, "setter");
+ const char *getter_closure = getter ? funpack ? "SwigPyBuiltin_FunpackGetterClosure" : "SwigPyBuiltin_GetterClosure" : "0";
+ const char *setter_closure = setter ? funpack ? "SwigPyBuiltin_FunpackSetterClosure" : "SwigPyBuiltin_SetterClosure" : "0";
+ String *gspair = NewStringf("%s_%s_getset", symname, memname);
+ Printf(f, "static SwigPyGetSet %s = { %s, %s };\n", gspair, getter ? getter : "0", setter ? setter : "0");
+ String *doc = Getattr(mgetset, "doc");
+ if (!doc)
+ doc = NewStringf("%s.%s", name, memname);
+ String *entry = NewStringf("{ (char *)\"%s\", %s, %s, (char *)\"%s\", &%s }", memname, getter_closure, setter_closure, doc, gspair);
+ if (GetFlag(mgetset, "static")) {
+ Printf(f, "static PyGetSetDef %s_def = %s;\n", gspair, entry);
+ Printf(f_init, "static_getset = SwigPyStaticVar_new_getset(metatype, &%s_def);\n", gspair);
+ Printf(f_init, "PyDict_SetItemString(d, static_getset->d_getset->name, (PyObject *) static_getset);\n");
+ Printf(f_init, "Py_DECREF(static_getset);\n");
+ } else {
+ Printf(getset_def, " %s,\n", entry);
+ }
+ Delete(gspair);
+ Delete(entry);
+ }
+ Printv(f, getset_def, " { NULL, NULL, NULL, NULL, NULL } /* Sentinel */\n", "};\n\n", NIL);
+
+ // Rich compare function
+ Hash *richcompare = Getattr(n, "python:richcompare");
+ String *richcompare_func = NewStringf("%s_richcompare", templ);
+ assert(richcompare);
+ Printf(f, "SWIGINTERN PyObject *\n");
+ Printf(f, "%s(PyObject *self, PyObject *other, int op) {\n", richcompare_func);
+ Printf(f, " PyObject *result = NULL;\n");
+ if (!funpack) {
+ Printf(f, " PyObject *tuple = PyTuple_New(1);\n");
+ Printf(f, " assert(tuple);\n");
+ Printf(f, " PyTuple_SET_ITEM(tuple, 0, other);\n");
+ Printf(f, " Py_XINCREF(other);\n");
+ }
+ List *richcompare_list = SortedKeys(richcompare, 0);
+ Iterator rich_iter = First(richcompare_list);
+ if (rich_iter.item) {
+ Printf(f, " switch (op) {\n");
+ for (; rich_iter.item; rich_iter = Next(rich_iter))
+ Printf(f, " case %s : result = %s(self, %s); break;\n", rich_iter.item, Getattr(richcompare, rich_iter.item), funpack ? "other" : "tuple");
+ Printv(f, " default : break;\n", NIL);
+ Printf(f, " }\n");
+ }
+ Delete(richcompare_list);
+ Printv(f, " if (!result) {\n", NIL);
+ Printv(f, " if (SwigPyObject_Check(self) && SwigPyObject_Check(other)) {\n", NIL);
+ Printv(f, " result = SwigPyObject_richcompare((SwigPyObject *)self, (SwigPyObject *)other, op);\n", NIL);
+ Printv(f, " } else {\n", NIL);
+ Printv(f, " result = Py_NotImplemented;\n", NIL);
+ Printv(f, " Py_INCREF(result);\n", NIL);
+ Printv(f, " }\n", NIL);
+ Printv(f, " }\n", NIL);
+ if (!funpack)
+ Printf(f, " Py_DECREF(tuple);\n");
+ Printf(f, " return result;\n");
+ Printf(f, "}\n\n");
+
+ // Methods
+ Printf(f, "SWIGINTERN PyMethodDef %s_methods[] = {\n", templ);
+ Dump(builtin_methods, f);
+ Printf(f, " { NULL, NULL, 0, NULL } /* Sentinel */\n};\n\n");
+
+ // No instance dict for nondynamic objects
+ if (GetFlag(n, "feature:python:nondynamic"))
+ Setattr(n, "feature:python:tp_setattro", "SWIG_Python_NonDynamicSetAttr");
+
+ Node *mod = Getattr(n, "module");
+ String *modname = mod ? Getattr(mod, "name") : 0;
+ String *quoted_symname;
+ if (package) {
+ if (modname)
+ quoted_symname = NewStringf("\"%s.%s.%s\"", package, modname, symname);
+ else
+ quoted_symname = NewStringf("\"%s.%s\"", package, symname);
+ } else {
+ if (modname)
+ quoted_symname = NewStringf("\"%s.%s\"", modname, symname);
+ else
+ quoted_symname = NewStringf("\"%s\"", symname);
+ }
+ String *quoted_tp_doc_str = NewStringf("\"%s\"", getSlot(n, "feature:python:tp_doc"));
+ String *tp_init = NewString(builtin_tp_init ? Char(builtin_tp_init) : Swig_directorclass(n) ? "0" : "SwigPyBuiltin_BadInit");
+ String *tp_flags = NewString("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES");
+ String *tp_flags_py3 = NewString("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE");
+
+ static String *tp_basicsize = NewStringf("sizeof(SwigPyObject)");
+ static String *tp_dictoffset_default = NewString("offsetof(SwigPyObject, dict)");
+ static String *tp_hash = NewString("SwigPyObject_hash");
+ String *tp_as_number = NewStringf("&%s_type.as_number", templ);
+ String *tp_as_sequence = NewStringf("&%s_type.as_sequence", templ);
+ String *tp_as_mapping = NewStringf("&%s_type.as_mapping", templ);
+ String *tp_as_buffer = NewStringf("&%s_type.as_buffer", templ);
+
+ Printf(f, "static PyHeapTypeObject %s_type = {\n", templ);
+
+ // PyTypeObject ht_type
+ Printf(f, " {\n");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
+ Printv(f, " PyVarObject_HEAD_INIT(NULL, 0)\n", NIL);
+ Printv(f, "#else\n", NIL);
+ Printf(f, " PyObject_HEAD_INIT(NULL)\n");
+ printSlot(f, getSlot(), "ob_size");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, quoted_symname, "tp_name");
+ printSlot(f, getSlot(n, "feature:python:tp_basicsize", tp_basicsize), "tp_basicsize");
+ printSlot(f, getSlot(n, "feature:python:tp_itemsize"), "tp_itemsize");
+ printSlot(f, getSlot(n, "feature:python:tp_dealloc", tp_dealloc_bad), "tp_dealloc", "destructor");
+ Printv(f, "#if PY_VERSION_HEX < 0x030800b4\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_print"), "tp_print", "printfunc");
+ Printv(f, "#else\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_vectorcall_offset"), "tp_vectorcall_offset", "Py_ssize_t");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_getattr"), "tp_getattr", "getattrfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_setattr"), "tp_setattr", "setattrfunc");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_compare"), "tp_compare");
+ Printv(f, "#else\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_compare"), "tp_compare", "cmpfunc");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_repr"), "tp_repr", "reprfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_as_number", tp_as_number), "tp_as_number");
+ printSlot(f, getSlot(n, "feature:python:tp_as_sequence", tp_as_sequence), "tp_as_sequence");
+ printSlot(f, getSlot(n, "feature:python:tp_as_mapping", tp_as_mapping), "tp_as_mapping");
+ printSlot(f, getSlot(n, "feature:python:tp_hash", tp_hash), "tp_hash", "hashfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_call"), "tp_call", "ternaryfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_str"), "tp_str", "reprfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_getattro"), "tp_getattro", "getattrofunc");
+ printSlot(f, getSlot(n, "feature:python:tp_setattro"), "tp_setattro", "setattrofunc");
+ printSlot(f, getSlot(n, "feature:python:tp_as_buffer", tp_as_buffer), "tp_as_buffer");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_flags", tp_flags_py3), "tp_flags");
+ Printv(f, "#else\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_flags", tp_flags), "tp_flags");
+ Printv(f, "#endif\n", NIL);
+ if (have_docstring(n)) {
+ String *ds = cdocstring(n, AUTODOC_CLASS);
+ String *tp_doc = NewString("");
+ Printf(tp_doc, "\"%s\"", ds);
+ Delete(ds);
+ printSlot(f, tp_doc, "tp_doc");
+ Delete(tp_doc);
+ } else {
+ printSlot(f, quoted_tp_doc_str, "tp_doc");
+ }
+ printSlot(f, getSlot(n, "feature:python:tp_traverse"), "tp_traverse", "traverseproc");
+ printSlot(f, getSlot(n, "feature:python:tp_clear"), "tp_clear", "inquiry");
+ printSlot(f, getSlot(n, "feature:python:tp_richcompare", richcompare_func), "tp_richcompare", "richcmpfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_weaklistoffset"), "tp_weaklistoffset");
+ printSlot(f, getSlot(n, "feature:python:tp_iter"), "tp_iter", "getiterfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_iternext"), "tp_iternext", "iternextfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_methods", methods_name), "tp_methods");
+ printSlot(f, getSlot(n, "feature:python:tp_members"), "tp_members");
+ printSlot(f, getSlot(n, "feature:python:tp_getset", getset_name), "tp_getset");
+ printSlot(f, getSlot(n, "feature:python:tp_base"), "tp_base");
+ printSlot(f, getSlot(n, "feature:python:tp_dict"), "tp_dict");
+ printSlot(f, getSlot(n, "feature:python:tp_descr_get"), "tp_descr_get", "descrgetfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_descr_set"), "tp_descr_set", "descrsetfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_dictoffset", tp_dictoffset_default), "tp_dictoffset", "Py_ssize_t");
+ printSlot(f, getSlot(n, "feature:python:tp_init", tp_init), "tp_init", "initproc");
+ printSlot(f, getSlot(n, "feature:python:tp_alloc"), "tp_alloc", "allocfunc");
+ printSlot(f, getSlot(), "tp_new", "newfunc");
+ printSlot(f, getSlot(n, "feature:python:tp_free"), "tp_free", "freefunc");
+ printSlot(f, getSlot(n, "feature:python:tp_is_gc"), "tp_is_gc", "inquiry");
+ printSlot(f, getSlot(n, "feature:python:tp_bases"), "tp_bases", "PyObject *");
+ printSlot(f, getSlot(n, "feature:python:tp_mro"), "tp_mro", "PyObject *");
+ printSlot(f, getSlot(n, "feature:python:tp_cache"), "tp_cache", "PyObject *");
+ printSlot(f, getSlot(n, "feature:python:tp_subclasses"), "tp_subclasses", "PyObject *");
+ printSlot(f, getSlot(n, "feature:python:tp_weaklist"), "tp_weaklist", "PyObject *");
+ printSlot(f, getSlot(n, "feature:python:tp_del"), "tp_del", "destructor");
+ printSlot(f, getSlot(n, "feature:python:tp_version_tag"), "tp_version_tag", "int");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03040000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_finalize"), "tp_finalize", "destructor");
+ Printv(f, "#endif\n", NIL);
+ Printv(f, "#if PY_VERSION_HEX >= 0x03080000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_vectorcall"), "tp_vectorcall", "vectorcallfunc");
+ Printv(f, "#endif\n", NIL);
+ Printv(f, "#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000)\n", NIL);
+ printSlot(f, getSlot(), "tp_print");
+ Printv(f, "#endif\n", NIL);
+
+ Printv(f, "#ifdef COUNT_ALLOCS\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:tp_allocs"), "tp_allocs", "Py_ssize_t");
+ printSlot(f, getSlot(n, "feature:python:tp_frees"), "tp_frees", "Py_ssize_t");
+ printSlot(f, getSlot(n, "feature:python:tp_maxalloc"), "tp_maxalloc", "Py_ssize_t");
+ printSlot(f, getSlot(n, "feature:python:tp_prev"), "tp_prev");
+ printSlot(f, getSlot(n, "feature:python:tp_next"), "tp_next");
+ Printv(f, "#endif\n", NIL);
+ Printf(f, " },\n");
+
+ // PyAsyncMethods as_async
+ Printv(f, "#if PY_VERSION_HEX >= 0x03050000\n", NIL);
+ Printf(f, " {\n");
+ printSlot(f, getSlot(n, "feature:python:am_await"), "am_await", "unaryfunc");
+ printSlot(f, getSlot(n, "feature:python:am_aiter"), "am_aiter", "unaryfunc");
+ printSlot(f, getSlot(n, "feature:python:am_anext"), "am_anext", "unaryfunc");
+ Printv(f, "# if PY_VERSION_HEX >= 0x030a0000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:am_send"), "am_send", "sendfunc");
+ Printv(f, "# endif\n", NIL);
+ Printf(f, " },\n");
+ Printv(f, "#endif\n", NIL);
+
+ // PyNumberMethods as_number
+ Printf(f, " {\n");
+ printSlot(f, getSlot(n, "feature:python:nb_add"), "nb_add", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_subtract"), "nb_subtract", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_multiply"), "nb_multiply", "binaryfunc");
+ Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_divide"), "nb_divide", "binaryfunc");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_remainder"), "nb_remainder", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_divmod"), "nb_divmod", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_power"), "nb_power", "ternaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_negative"), "nb_negative", "unaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_positive"), "nb_positive", "unaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_absolute"), "nb_absolute", "unaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_nonzero"), "nb_nonzero", "inquiry");
+ printSlot(f, getSlot(n, "feature:python:nb_invert"), "nb_invert", "unaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_lshift"), "nb_lshift", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_rshift"), "nb_rshift", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_and"), "nb_and", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_xor"), "nb_xor", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_or"), "nb_or", "binaryfunc");
+ Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_coerce"), "nb_coerce", "coercion");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_int"), "nb_int", "unaryfunc");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_reserved"), "nb_reserved", "void *");
+ Printv(f, "#else\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_long"), "nb_long", "unaryfunc");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_float"), "nb_float", "unaryfunc");
+ Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_oct"), "nb_oct", "unaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_hex"), "nb_hex", "unaryfunc");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_add"), "nb_inplace_add", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_subtract"), "nb_inplace_subtract", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_multiply"), "nb_inplace_multiply", "binaryfunc");
+ Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_divide"), "nb_inplace_divide", "binaryfunc");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_remainder"), "nb_inplace_remainder", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_power"), "nb_inplace_power", "ternaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_lshift"), "nb_inplace_lshift", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_rshift"), "nb_inplace_rshift", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_and"), "nb_inplace_and", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_xor"), "nb_inplace_xor", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_or"), "nb_inplace_or", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_floor_divide"), "nb_floor_divide", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_divide"), "nb_true_divide", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_floor_divide"), "nb_inplace_floor_divide", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_divide"), "nb_inplace_true_divide", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_index"), "nb_index", "unaryfunc");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03050000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:nb_matrix_multiply"), "nb_matrix_multiply", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:nb_inplace_matrix_multiply"), "nb_inplace_matrix_multiply", "binaryfunc");
+ Printv(f, "#endif\n", NIL);
+ Printf(f, " },\n");
+
+ // PyMappingMethods as_mapping;
+ Printf(f, " {\n");
+ printSlot(f, getSlot(n, "feature:python:mp_length"), "mp_length", "lenfunc");
+ printSlot(f, getSlot(n, "feature:python:mp_subscript"), "mp_subscript", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:mp_ass_subscript"), "mp_ass_subscript", "objobjargproc");
+ Printf(f, " },\n");
+
+ // PySequenceMethods as_sequence;
+ Printf(f, " {\n");
+ printSlot(f, getSlot(n, "feature:python:sq_length"), "sq_length", "lenfunc");
+ printSlot(f, getSlot(n, "feature:python:sq_concat"), "sq_concat", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:sq_repeat"), "sq_repeat", "ssizeargfunc");
+ printSlot(f, getSlot(n, "feature:python:sq_item"), "sq_item", "ssizeargfunc");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:was_sq_slice"), "was_sq_slice", "void *");
+ Printv(f, "#else\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:sq_slice"), "sq_slice", "ssizessizeargfunc");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:sq_ass_item"), "sq_ass_item", "ssizeobjargproc");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:was_sq_ass_slice"), "was_sq_ass_slice", "void *");
+ Printv(f, "#else\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:sq_ass_slice"), "sq_ass_slice", "ssizessizeobjargproc");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:sq_contains"), "sq_contains", "objobjproc");
+ printSlot(f, getSlot(n, "feature:python:sq_inplace_concat"), "sq_inplace_concat", "binaryfunc");
+ printSlot(f, getSlot(n, "feature:python:sq_inplace_repeat"), "sq_inplace_repeat", "ssizeargfunc");
+ Printf(f, " },\n");
+
+ // PyBufferProcs as_buffer;
+ Printf(f, " {\n");
+ Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:bf_getreadbuffer"), "bf_getreadbuffer", "readbufferproc");
+ printSlot(f, getSlot(n, "feature:python:bf_getwritebuffer"), "bf_getwritebuffer", "writebufferproc");
+ printSlot(f, getSlot(n, "feature:python:bf_getsegcount"), "bf_getsegcount", "segcountproc");
+ printSlot(f, getSlot(n, "feature:python:bf_getcharbuffer"), "bf_getcharbuffer", "charbufferproc");
+ Printv(f, "#endif\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:bf_getbuffer"), "bf_getbuffer", "getbufferproc");
+ printSlot(f, getSlot(n, "feature:python:bf_releasebuffer"), "bf_releasebuffer", "releasebufferproc");
+ Printf(f, " },\n");
+
+ // PyObject *ht_name, *ht_slots, *ht_qualname;
+ printSlot(f, getSlot(n, "feature:python:ht_name"), "ht_name", "PyObject *");
+ printSlot(f, getSlot(n, "feature:python:ht_slots"), "ht_slots", "PyObject *");
+ Printv(f, "#if PY_VERSION_HEX >= 0x03030000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:ht_qualname"), "ht_qualname", "PyObject *");
+
+ // struct _dictkeysobject *ht_cached_keys;
+ printSlot(f, getSlot(n, "feature:python:ht_cached_keys"), "ht_cached_keys");
+ Printv(f, "#endif\n", NIL);
+
+ // PyObject *ht_module;
+ Printv(f, "#if PY_VERSION_HEX >= 0x03090000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:ht_module"), "ht_module", "PyObject *");
+ Printv(f, "#endif\n", NIL);
+
+ // char *_ht_tpname;
+ Printv(f, "#if PY_VERSION_HEX >= 0x030b0000\n", NIL);
+ printSlot(f, getSlot(n, "feature:python:_ht_tpname"), "_ht_tpname", "char *");
+
+ // struct _specialization_cache _spec_cache;
+ Printf(f, " {\n");
+ printSlot(f, getSlot(n, "feature:python:getitem"), "getitem", "PyObject *");
+ Printf(f, " }\n");
+ Printv(f, "#endif\n", NIL);
+ Printf(f, "};\n\n");
+
+ String *clientdata = NewString("");
+ Printf(clientdata, "&%s_clientdata", templ);
+ SwigType_remember_mangleddata(pmname, clientdata);
+
+ SwigType *smart = Swig_cparse_smartptr(n);
+ if (smart) {
+ SwigType_add_pointer(smart);
+ String *smart_pmname = SwigType_manglestr(smart);
+ SwigType_remember_mangleddata(smart_pmname, clientdata);
+ Delete(smart_pmname);
+ }
+
+ String *clientdata_klass = NewString("0");
+ if (GetFlag(n, "feature:implicitconv")) {
+ Clear(clientdata_klass);
+ Printf(clientdata_klass, "(PyObject *) &%s_type", templ);
+ }
+
+ Printf(f, "SWIGINTERN SwigPyClientData %s_clientdata = {%s, 0, 0, 0, 0, 0, (PyTypeObject *)&%s_type};\n\n", templ, clientdata_klass, templ);
+
+ Printv(f_init, " if (PyType_Ready(builtin_pytype) < 0) {\n", NIL);
+ Printf(f_init, " PyErr_SetString(PyExc_TypeError, \"Could not create type '%s'.\");\n", symname);
+ Printv(f_init, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
+ Printv(f_init, " return NULL;\n", NIL);
+ Printv(f_init, "#else\n", NIL);
+ Printv(f_init, " return;\n", NIL);
+ Printv(f_init, "#endif\n", NIL);
+ Printv(f_init, " }\n", NIL);
+ Printv(f_init, " Py_INCREF(builtin_pytype);\n", NIL);
+ Printf(f_init, " PyModule_AddObject(m, \"%s\", (PyObject *)builtin_pytype);\n", symname);
+ Printf(f_init, " SwigPyBuiltin_AddPublicSymbol(public_interface, \"%s\");\n", symname);
+ Printv(f_init, " d = md;\n", NIL);
+
+ Delete(clientdata);
+ Delete(smart);
+ Delete(rname);
+ Delete(pname);
+ Delete(mname);
+ Delete(pmname);
+ Delete(templ);
+ Delete(tp_flags);
+ Delete(tp_flags_py3);
+ Delete(tp_as_buffer);
+ Delete(tp_as_mapping);
+ Delete(tp_as_sequence);
+ Delete(tp_as_number);
+ Delete(quoted_symname);
+ Delete(quoted_tp_doc_str);
+ Delete(tp_init);
+ Delete(clientdata_klass);
+ Delete(richcompare_func);
+ Delete(getset_name);
+ Delete(methods_name);
+ }
+
+ virtual int classHandler(Node *n) {
+ File *f_shadow_file = f_shadow;
+ Node *base_node = NULL;
+
+ if (shadow) {
+
+ /* Create new strings for building up a wrapper function */
+ have_constructor = 0;
+ have_repr = 0;
+ have_builtin_static_member_method_callback = false;
+
+ class_name = Getattr(n, "sym:name");
+ real_classname = Getattr(n, "name");
+
+ if (!addSymbol(class_name, n))
+ return SWIG_ERROR;
+
+ if (builtin) {
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist) > 0) {
+ Iterator b = First(baselist);
+ base_node = b.item;
+ }
+ }
+
+ shadow_indent = (String *) tab4;
+
+ /* Handle inheritance */
+ String *base_class = NewString("");
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator b;
+ b = First(baselist);
+ while (b.item) {
+ String *bname = Getattr(b.item, "python:proxy");
+ bool ignore = GetFlag(b.item, "feature:ignore") ? true : false;
+ if (!bname || ignore) {
+ if (!bname && !ignore) {
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(n), Getline(n),
+ "Base class '%s' ignored - unknown module name for base. Either import the appropriate module interface file or specify the name of the module in the %%import directive.\n",
+ SwigType_namestr(Getattr(b.item, "name")));
+ }
+ b = Next(b);
+ continue;
+ }
+ Printv(base_class, bname, NIL);
+ b = Next(b);
+ if (b.item) {
+ Printv(base_class, ", ", NIL);
+ }
+ }
+ }
+
+ if (builtin) {
+ Hash *base_richcompare = NULL;
+ Hash *richcompare = NULL;
+ if (base_node)
+ base_richcompare = Getattr(base_node, "python:richcompare");
+ if (base_richcompare)
+ richcompare = Copy(base_richcompare);
+ else
+ richcompare = NewHash();
+ Setattr(n, "python:richcompare", richcompare);
+ }
+
+ /* dealing with abstract base class */
+ String *abcs = Getattr(n, "feature:python:abc");
+ if (abcs) {
+ if (Len(base_class) > 0)
+ Printv(base_class, ", ", NIL);
+ Printv(base_class, abcs, NIL);
+ }
+
+ if (builtin) {
+ if (have_docstring(n)) {
+ String *ds = cdocstring(n, AUTODOC_CLASS);
+ Setattr(n, "feature:python:tp_doc", ds);
+ Delete(ds);
+ } else {
+ String *name = Getattr(n, "name");
+ String *rname = add_explicit_scope(SwigType_namestr(name));
+ Setattr(n, "feature:python:tp_doc", rname);
+ Delete(rname);
+ }
+ } else {
+ if (GetFlag(n, "feature:python:nondynamic"))
+ Printv(f_shadow, "@_swig_add_metaclass(_SwigNonDynamicMeta)\n", NIL);
+ Printv(f_shadow, "class ", class_name, NIL);
+
+ if (Len(base_class)) {
+ Printf(f_shadow, "(%s)", base_class);
+ } else {
+ if (GetFlag(n, "feature:exceptionclass")) {
+ Printf(f_shadow, "(Exception)");
+ } else {
+ Printf(f_shadow, "(object");
+ /* Replace @_swig_add_metaclass above with below when support for python 2.7 is dropped
+ if (GetFlag(n, "feature:python:nondynamic")) {
+ Printf(f_shadow, ", metaclass=_SwigNonDynamicMeta");
+ }
+ */
+ Printf(f_shadow, ")");
+ }
+ }
+
+ Printf(f_shadow, ":\n");
+
+ // write docstrings if requested
+ if (have_docstring(n)) {
+ String *str = docstring(n, AUTODOC_CLASS, tab4);
+ if (str && Len(str))
+ Printv(f_shadow, tab4, str, "\n\n", NIL);
+ }
+
+ Printv(f_shadow, tab4, "thisown = property(lambda x: x.this.own(), ", "lambda x, v: x.this.own(v), doc=\"The membership flag\")\n", NIL);
+ /* Add static attribute */
+ if (GetFlag(n, "feature:python:nondynamic")) {
+ Printv(f_shadow_file, tab4, "__setattr__ = _swig_setattr_nondynamic_instance_variable(object.__setattr__)\n", NIL);
+ }
+ }
+ }
+
+ /* Emit all of the members */
+
+ in_class = 1;
+ if (builtin)
+ builtin_pre_decl(n);
+
+ /* Override the shadow file so we can capture its methods */
+ f_shadow = NewString("");
+
+ // Set up type check for director class constructor
+ Clear(none_comparison);
+ if (builtin && Swig_directorclass(n)) {
+ String *p_real_classname = Copy(real_classname);
+ SwigType_add_pointer(p_real_classname);
+ String *mangle = SwigType_manglestr(p_real_classname);
+ String *descriptor = NewStringf("SWIGTYPE%s", mangle);
+ Printv(none_comparison, "self->ob_type != ((SwigPyClientData *)(", descriptor, ")->clientdata)->pytype", NIL);
+ Delete(descriptor);
+ Delete(mangle);
+ Delete(p_real_classname);
+ } else {
+ Printv(none_comparison, "$arg != Py_None", NIL);
+ }
+
+ Language::classHandler(n);
+
+ in_class = 0;
+
+ /* Complete the class */
+ if (shadow) {
+ /* Generate a class registration function */
+ // Replace storing a pointer to underlying class with a smart pointer (intended for use with non-intrusive smart pointers)
+ SwigType *smart = Swig_cparse_smartptr(n);
+ SwigType *ct = Copy(smart ? smart : real_classname);
+ SwigType_add_pointer(ct);
+ SwigType *realct = Copy(real_classname);
+ SwigType_add_pointer(realct);
+ SwigType_remember(realct);
+ if (builtin) {
+ Printv(f_wrappers, builtin_closures_code, NIL);
+ Delete(builtin_closures_code);
+ builtin_closures_code = NewString("");
+ Clear(builtin_closures);
+ } else {
+ Printv(f_wrappers, "SWIGINTERN PyObject *", class_name, "_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {\n", NIL);
+ Printv(f_wrappers, " PyObject *obj;\n", NIL);
+ Printv(f_wrappers, " if (!SWIG_Python_UnpackTuple(args, \"swigregister\", 1, 1, &obj)) return NULL;\n", NIL);
+
+ Printv(f_wrappers,
+ " SWIG_TypeNewClientData(SWIGTYPE", SwigType_manglestr(ct), ", SWIG_NewClientData(obj));\n", " return SWIG_Py_Void();\n", "}\n\n", NIL);
+ String *cname = NewStringf("%s_swigregister", class_name);
+ add_method(cname, cname, 0, 0, 1, 1, 1);
+ Delete(cname);
+ }
+ Delete(smart);
+ Delete(ct);
+ Delete(realct);
+ if (!have_constructor) {
+ if (!builtin)
+ Printv(f_shadow_file, "\n", tab4, "def __init__(self, *args, **kwargs):\n", tab8, "raise AttributeError(\"", "No constructor defined",
+ (Getattr(n, "abstracts") ? " - class is abstract" : ""), "\")\n", NIL);
+ } else if (!builtin) {
+
+ Printv(f_wrappers, "SWIGINTERN PyObject *", class_name, "_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {\n", NIL);
+ Printv(f_wrappers, " return SWIG_Python_InitShadowInstance(args);\n", "}\n\n", NIL);
+ String *cname = NewStringf("%s_swiginit", class_name);
+ add_method(cname, cname, 0);
+ Delete(cname);
+ }
+ if (!have_repr && !builtin) {
+ /* Supply a repr method for this class */
+ String *rname = SwigType_namestr(real_classname);
+ Printv(f_shadow_file, tab4, "__repr__ = _swig_repr\n", NIL);
+ Delete(rname);
+ }
+
+ if (builtin)
+ builtin_post_decl(f_builtins, n);
+
+ if (builtin_tp_init) {
+ Delete(builtin_tp_init);
+ builtin_tp_init = 0;
+ }
+
+ if (!builtin) {
+ /* Now emit methods */
+ Printv(f_shadow_file, f_shadow, NIL);
+ Printf(f_shadow_file, "\n");
+ Printf(f_shadow_file, "# Register %s in %s:\n", class_name, module);
+ Printf(f_shadow_file, "%s.%s_swigregister(%s)\n", module, class_name, class_name);
+ }
+
+ shadow_indent = 0;
+ if (Len(f_shadow_stubs) > 0)
+ Printf(f_shadow_file, "%s\n", f_shadow_stubs);
+ Clear(f_shadow_stubs);
+ }
+
+ if (builtin) {
+ Clear(class_members);
+ Clear(builtin_getset);
+ Clear(builtin_methods);
+ }
+
+ /* Restore shadow file back to original version */
+ Delete(f_shadow);
+ f_shadow = f_shadow_file;
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * functionHandler() - Mainly overloaded for callback handling
+ * ------------------------------------------------------------ */
+
+ virtual int functionHandler(Node *n) {
+ String *pcb = GetFlagAttr(n, "feature:python:callback");
+ if (pcb) {
+ if (Strcmp(pcb, "1") == 0) {
+ SetFlagAttr(n, "feature:callback", "%s_cb_ptr");
+ } else {
+ SetFlagAttr(n, "feature:callback", pcb);
+ }
+ autodoc_l dlevel = autodoc_level(Getattr(n, "feature:autodoc"));
+ if (dlevel != NO_AUTODOC && dlevel > TYPES_AUTODOC) {
+ Setattr(n, "feature:autodoc", "1");
+ }
+ }
+ return Language::functionHandler(n);
+ }
+
+ /* ------------------------------------------------------------
+ * memberfunctionHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int memberfunctionHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ int oldshadow;
+
+ if (builtin)
+ Swig_save("builtin_memberfunc", n, "python:argcount", NIL);
+
+ /* Create the default member function */
+ oldshadow = shadow; /* Disable shadowing when wrapping member functions */
+ if (shadow)
+ shadow = shadow | PYSHADOW_MEMBER;
+ Language::memberfunctionHandler(n);
+ shadow = oldshadow;
+
+ if (builtin && in_class) {
+ // Can't use checkAttribute(n, "access", "public") because
+ // "access" attr isn't set on %extend methods
+ if (!checkAttribute(n, "access", "private") && strncmp(Char(symname), "operator ", 9) && !Getattr(class_members, symname)) {
+ String *fullname = Swig_name_member(NSPACE_TODO, class_name, symname);
+ String *wname = Swig_name_wrapper(fullname);
+ Setattr(class_members, symname, n);
+ int argcount = Getattr(n, "python:argcount") ? atoi(Char(Getattr(n, "python:argcount"))) : 2;
+ String *ds = have_docstring(n) ? cdocstring(n, AUTODOC_METHOD) : NewString("");
+ if (check_kwargs(n)) {
+ // Cast via void(*)(void) to suppress GCC -Wcast-function-type
+ // warning. Python should always call the function correctly, but
+ // the Python C API requires us to store it in function pointer of a
+ // different type.
+ Printf(builtin_methods, " { \"%s\", (PyCFunction)(void(*)(void))%s, METH_VARARGS|METH_KEYWORDS, \"%s\" },\n", symname, wname, ds);
+ } else if (argcount == 0) {
+ Printf(builtin_methods, " { \"%s\", %s, METH_NOARGS, \"%s\" },\n", symname, wname, ds);
+ } else if (argcount == 1) {
+ Printf(builtin_methods, " { \"%s\", %s, METH_O, \"%s\" },\n", symname, wname, ds);
+ } else {
+ Printf(builtin_methods, " { \"%s\", %s, METH_VARARGS, \"%s\" },\n", symname, wname, ds);
+ }
+ Delete(fullname);
+ Delete(wname);
+ Delete(ds);
+ }
+ }
+
+ if (builtin)
+ Swig_restore(n);
+
+ if (!Getattr(n, "sym:nextSibling")) {
+ if (shadow && !builtin) {
+ int fproxy = fastproxy;
+ String *fullname = Swig_name_member(NSPACE_TODO, class_name, symname);
+ if (Strcmp(symname, "__repr__") == 0) {
+ have_repr = 1;
+ }
+ if (Getattr(n, "feature:shadow")) {
+ String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
+ String *pyaction = NewStringf("%s.%s", module, fullname);
+ Replaceall(pycode, "$action", pyaction);
+ Delete(pyaction);
+ Printv(f_shadow, pycode, "\n", NIL);
+ Delete(pycode);
+ fproxy = 0;
+ } else {
+ int allow_kwargs = (check_kwargs(n) && !Getattr(n, "sym:overloaded")) ? 1 : 0;
+ String *parms = make_pyParmList(n, true, false, allow_kwargs);
+ String *callParms = make_pyParmList(n, true, true, allow_kwargs);
+ if (!have_addtofunc(n)) {
+ if (!fastproxy || olddefs) {
+ Printv(f_shadow, "\n", tab4, "def ", symname, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
+ if (have_docstring(n))
+ Printv(f_shadow, tab8, docstring(n, AUTODOC_METHOD, tab8), "\n", NIL);
+ Printv(f_shadow, tab8, "return ", funcCall(fullname, callParms), "\n", NIL);
+ }
+ } else {
+ Printv(f_shadow, "\n", tab4, "def ", symname, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
+ if (have_docstring(n))
+ Printv(f_shadow, tab8, docstring(n, AUTODOC_METHOD, tab8), "\n", NIL);
+ if (have_pythonprepend(n)) {
+ fproxy = 0;
+ Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
+ }
+ if (have_pythonappend(n)) {
+ fproxy = 0;
+ Printv(f_shadow, tab8, "val = ", funcCall(fullname, callParms), "\n", NIL);
+ Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
+ Printv(f_shadow, tab8, "return val\n\n", NIL);
+ } else {
+ Printv(f_shadow, tab8, "return ", funcCall(fullname, callParms), "\n\n", NIL);
+ }
+ }
+ }
+ if (fproxy) {
+ Printf(f_shadow, tab4);
+ Printf(f_shadow, "%s = _swig_new_instance_method(%s.%s)\n", symname, module, Swig_name_member(NSPACE_TODO, class_name, symname));
+ }
+ Delete(fullname);
+ }
+ }
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ if (builtin && in_class) {
+ Swig_save("builtin_memberconstantHandler", n, "pybuiltin:symname", NIL);
+ Setattr(n, "pybuiltin:symname", symname);
+ }
+ Language::staticmemberfunctionHandler(n);
+ if (builtin && in_class) {
+ Swig_restore(n);
+ }
+
+ int kw = (check_kwargs(n) && !Getattr(n, "sym:overloaded")) ? 1 : 0;
+ if (builtin && in_class) {
+ if ((GetFlagAttr(n, "feature:extend") || checkAttribute(n, "access", "public"))
+ && !Getattr(class_members, symname)) {
+ String *fullname = Swig_name_member(NSPACE_TODO, class_name, symname);
+ String *wname = Swig_name_wrapper(fullname);
+ Setattr(class_members, symname, n);
+ int funpack = fastunpack && !Getattr(n, "sym:overloaded");
+ String *pyflags = NewString("METH_STATIC|");
+ int argcount = Getattr(n, "python:argcount") ? atoi(Char(Getattr(n, "python:argcount"))) : 2;
+ if (funpack && argcount == 0)
+ Append(pyflags, "METH_NOARGS");
+ else if (funpack && argcount == 1)
+ Append(pyflags, "METH_O");
+ else
+ Append(pyflags, kw ? "METH_VARARGS|METH_KEYWORDS" : "METH_VARARGS");
+ // Cast via void(*)(void) to suppress GCC -Wcast-function-type warning.
+ // Python should always call the function correctly, but the Python C
+ // API requires us to store it in function pointer of a different type.
+ if (have_docstring(n)) {
+ String *ds = cdocstring(n, AUTODOC_STATICFUNC);
+ Printf(builtin_methods, " { \"%s\", (PyCFunction)(void(*)(void))%s, %s, \"%s\" },\n", symname, wname, pyflags, ds);
+ Delete(ds);
+ } else if (Getattr(n, "feature:callback")) {
+ String *ds = NewStringf("swig_ptr: %s", Getattr(n, "feature:callback:name"));
+ Printf(builtin_methods, " { \"%s\", (PyCFunction)(void(*)(void))%s, %s, \"%s\" },\n", symname, wname, pyflags, ds);
+ Delete(ds);
+ have_builtin_static_member_method_callback = true;
+ } else {
+ Printf(builtin_methods, " { \"%s\", (PyCFunction)(void(*)(void))%s, %s, \"\" },\n", symname, wname, pyflags);
+ }
+ Delete(fullname);
+ Delete(wname);
+ Delete(pyflags);
+ }
+ return SWIG_OK;
+ }
+
+ if (Getattr(n, "sym:nextSibling")) {
+ return SWIG_OK;
+ }
+
+ if (shadow) {
+ String *staticfunc_name = NewString(fastproxy ? "_swig_new_static_method" : "staticmethod");
+ bool fast = (fastproxy && !have_addtofunc(n)) || Getattr(n, "feature:callback");
+ if (!fast || olddefs) {
+ String *parms = make_pyParmList(n, false, false, kw);
+ String *callParms = make_pyParmList(n, false, true, kw);
+ Printv(f_shadow, "\n", tab4, "@staticmethod", NIL);
+ Printv(f_shadow, "\n", tab4, "def ", symname, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
+ if (have_docstring(n))
+ Printv(f_shadow, tab8, docstring(n, AUTODOC_STATICFUNC, tab8), "\n", NIL);
+ if (have_pythonprepend(n))
+ Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
+ if (have_pythonappend(n)) {
+ Printv(f_shadow, tab8, "val = ", funcCall(Swig_name_member(NSPACE_TODO, class_name, symname), callParms), "\n", NIL);
+ Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
+ Printv(f_shadow, tab8, "return val\n", NIL);
+ } else {
+ Printv(f_shadow, tab8, "return ", funcCall(Swig_name_member(NSPACE_TODO, class_name, symname), callParms), "\n", NIL);
+ }
+ }
+
+ // Below may result in a 2nd definition of the method when -olddefs is used. The Python interpreter will use the second definition as it overwrites the first.
+ if (fast) {
+ Printv(f_shadow, tab4, symname, " = ", staticfunc_name, "(", module, ".", Swig_name_member(NSPACE_TODO, class_name, symname),
+ ")\n", NIL);
+ }
+ Delete(staticfunc_name);
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constructorDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int constructorHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ int oldshadow = shadow;
+ int use_director = Swig_directorclass(n);
+
+ /*
+ * If we're wrapping the constructor of a C++ director class, prepend a new parameter
+ * to receive the scripting language object (e.g. 'self')
+ *
+ */
+ Swig_save("python:constructorHandler", n, "parms", NIL);
+ if (use_director) {
+ Parm *parms = Getattr(n, "parms");
+ Parm *self;
+ String *name = NewString("self");
+ String *type = NewString("PyObject");
+ SwigType_add_pointer(type);
+ self = NewParm(type, name, n);
+ Delete(type);
+ Delete(name);
+ Setattr(self, "lname", "O");
+ if (parms)
+ set_nextSibling(self, parms);
+ Setattr(n, "parms", self);
+ Setattr(n, "wrap:self", "1");
+ Setattr(n, "hidden", "1");
+ Delete(self);
+ }
+
+ if (shadow)
+ shadow = shadow | PYSHADOW_MEMBER;
+ Language::constructorHandler(n);
+ shadow = oldshadow;
+
+ Delattr(n, "wrap:self");
+ Swig_restore(n);
+
+ if (!Getattr(n, "sym:nextSibling")) {
+ if (shadow) {
+ int allow_kwargs = (check_kwargs(n) && (!Getattr(n, "sym:overloaded"))) ? 1 : 0;
+ int handled_as_init = 0;
+ if (!have_constructor) {
+ String *nname = Getattr(n, "sym:name");
+ String *sname = Getattr(getCurrentClass(), "sym:name");
+ String *cname = Swig_name_construct(NSPACE_TODO, sname);
+ handled_as_init = (Strcmp(nname, sname) == 0) || (Strcmp(nname, cname) == 0);
+ Delete(cname);
+ }
+
+ String *subfunc = Swig_name_construct(NSPACE_TODO, symname);
+ if (!have_constructor && handled_as_init) {
+ if (!builtin) {
+ if (Getattr(n, "feature:shadow")) {
+ String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
+ String *pyaction = NewStringf("%s.%s", module, subfunc);
+ Replaceall(pycode, "$action", pyaction);
+ Delete(pyaction);
+ Printv(f_shadow, pycode, "\n", NIL);
+ Delete(pycode);
+ } else {
+ String *pass_self = NewString("");
+ Node *parent = Swig_methodclass(n);
+ String *classname = Swig_class_name(parent);
+ String *rclassname = Swig_class_name(getCurrentClass());
+ assert(rclassname);
+ (void)rclassname;
+
+ String *parms = make_pyParmList(n, true, false, allow_kwargs);
+ /* Pass 'self' only if using director */
+ String *callParms = make_pyParmList(n, false, true, allow_kwargs, true);
+
+ if (use_director) {
+ Insert(callParms, 0, "_self, ");
+ Printv(pass_self, tab8, NIL);
+ Printf(pass_self, "if self.__class__ == %s:\n", classname);
+ //Printv(pass_self, tab8, tab4, "args = (None,) + args\n", tab8, "else:\n", tab8, tab4, "args = (self,) + args\n", NIL);
+ Printv(pass_self, tab8, tab4, "_self = None\n", tab8, "else:\n", tab8, tab4, "_self = self\n", NIL);
+ }
+
+ Printv(f_shadow, "\n", tab4, "def __init__(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
+ if (have_docstring(n))
+ Printv(f_shadow, tab8, docstring(n, AUTODOC_CTOR, tab8), "\n", NIL);
+ if (have_pythonprepend(n))
+ Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
+ Printv(f_shadow, pass_self, NIL);
+ Printv(f_shadow, tab8, module, ".", class_name, "_swiginit(self, ", funcCall(subfunc, callParms), ")\n", NIL);
+ if (have_pythonappend(n))
+ Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n\n", NIL);
+ Delete(pass_self);
+ }
+ have_constructor = 1;
+ }
+ } else {
+ /* Hmmm. We seem to be creating a different constructor. We're just going to create a
+ function for it. */
+ if (!builtin) {
+ if (Getattr(n, "feature:shadow")) {
+ String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), "", Getfile(n), Getline(n), "%feature(\"shadow\")");
+ String *pyaction = NewStringf("%s.%s", module, subfunc);
+ Replaceall(pycode, "$action", pyaction);
+ Delete(pyaction);
+ Printv(f_shadow_stubs, pycode, "\n", NIL);
+ Delete(pycode);
+ } else {
+ String *parms = make_pyParmList(n, false, false, allow_kwargs);
+ String *callParms = make_pyParmList(n, false, true, allow_kwargs);
+
+ Printv(f_shadow_stubs, "\ndef ", symname, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
+ if (have_docstring(n))
+ Printv(f_shadow_stubs, tab4, docstring(n, AUTODOC_CTOR, tab4), "\n", NIL);
+ if (have_pythonprepend(n))
+ Printv(f_shadow_stubs, indent_pythoncode(pythonprepend(n), tab4, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
+ Printv(f_shadow_stubs, tab4, "val = ", funcCall(subfunc, callParms), "\n", NIL);
+ if (have_pythonappend(n))
+ Printv(f_shadow_stubs, indent_pythoncode(pythonappend(n), tab4, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
+ Printv(f_shadow_stubs, tab4, "return val\n", NIL);
+ }
+ } else {
+ Printf(f_shadow_stubs, "%s = %s\n", symname, subfunc);
+ }
+ }
+ Delete(subfunc);
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * destructorHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int destructorHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ int oldshadow = shadow;
+
+ if (builtin && in_class) {
+ Node *cls = Swig_methodclass(n);
+ // Use the destructor for the tp_dealloc slot unless a user overrides it with another method
+ if (!Getattr(cls, "feature:python:tp_dealloc")) {
+ Setattr(n, "feature:python:slot", "tp_dealloc");
+ Setattr(n, "feature:python:slot:functype", "destructor");
+ }
+ }
+
+ if (shadow)
+ shadow = shadow | PYSHADOW_MEMBER;
+ //Setattr(n,"emit:dealloc","1");
+ Language::destructorHandler(n);
+ shadow = oldshadow;
+
+ if (shadow) {
+ if (Getattr(n, "feature:shadow")) {
+ String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
+ String *pyaction = NewStringf("%s.%s", module, Swig_name_destroy(NSPACE_TODO, symname));
+ Replaceall(pycode, "$action", pyaction);
+ Delete(pyaction);
+ Printv(f_shadow, pycode, "\n", NIL);
+ Delete(pycode);
+ } else {
+ Printv(f_shadow, tab4, "__swig_destroy__ = ", module, ".", Swig_name_destroy(NSPACE_TODO, symname), "\n", NIL);
+ if (!have_pythonprepend(n) && !have_pythonappend(n)) {
+ return SWIG_OK;
+ }
+ Printv(f_shadow, tab4, "def __del__(self):\n", NIL);
+ if (have_docstring(n))
+ Printv(f_shadow, tab8, docstring(n, AUTODOC_DTOR, tab8), "\n", NIL);
+ if (have_pythonprepend(n))
+ Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
+ if (have_pythonappend(n))
+ Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
+ Printv(f_shadow, tab8, "pass\n", NIL);
+ Printv(f_shadow, "\n", NIL);
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * membervariableHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int membervariableHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+
+ int oldshadow = shadow;
+ if (shadow)
+ shadow = shadow | PYSHADOW_MEMBER;
+ Language::membervariableHandler(n);
+ shadow = oldshadow;
+
+ if (shadow && !builtin) {
+ String *mname = Swig_name_member(NSPACE_TODO, class_name, symname);
+ String *setname = Swig_name_set(NSPACE_TODO, mname);
+ String *getname = Swig_name_get(NSPACE_TODO, mname);
+ int assignable = is_assignable(n);
+ String *variable_annotation = variableAnnotation(n);
+ Printv(f_shadow, tab4, symname, variable_annotation, " = property(", module, ".", getname, NIL);
+ if (assignable)
+ Printv(f_shadow, ", ", module, ".", setname, NIL);
+ if (have_docstring(n)) {
+ String *s = docstring(n, AUTODOC_VAR, tab4);
+ if (Len(s))
+ Printv(f_shadow, ", doc=", s, NIL);
+ }
+ Printv(f_shadow, ")\n", NIL);
+ Delete(variable_annotation);
+ Delete(mname);
+ Delete(setname);
+ Delete(getname);
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * staticmembervariableHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int staticmembervariableHandler(Node *n) {
+ Swig_save("builtin_staticmembervariableHandler", n, "builtin_symname", NIL);
+ Language::staticmembervariableHandler(n);
+ Swig_restore(n);
+
+ if (GetFlag(n, "wrappedasconstant"))
+ return SWIG_OK;
+
+ String *symname = Getattr(n, "sym:name");
+
+ if (shadow) {
+ if (!builtin && GetFlag(n, "hasconsttype")) {
+ String *mname = Swig_name_member(NSPACE_TODO, class_name, symname);
+ Printf(f_shadow_stubs, "%s.%s = %s.%s.%s\n", class_name, symname, module, global_name, mname);
+ Delete(mname);
+ } else {
+ String *mname = Swig_name_member(NSPACE_TODO, class_name, symname);
+ String *getname = Swig_name_get(NSPACE_TODO, mname);
+ String *wrapgetname = Swig_name_wrapper(getname);
+ String *vargetname = NewStringf("Swig_var_%s", getname);
+ String *setname = Swig_name_set(NSPACE_TODO, mname);
+ String *wrapsetname = Swig_name_wrapper(setname);
+ String *varsetname = NewStringf("Swig_var_%s", setname);
+
+ Wrapper *f = NewWrapper();
+ Printv(f->def, "SWIGINTERN PyObject *", wrapgetname, "(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(args)) {", NIL);
+ Printv(f->code, " return ", vargetname, "();\n", NIL);
+ Append(f->code, "}\n");
+ add_method(getname, wrapgetname, 0);
+ Wrapper_print(f, f_wrappers);
+ DelWrapper(f);
+ int assignable = is_assignable(n);
+ if (assignable) {
+ int funpack = fastunpack;
+ Wrapper *f = NewWrapper();
+ Printv(f->def, "SWIGINTERN PyObject *", wrapsetname, "(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {", NIL);
+ Wrapper_add_local(f, "res", "int res");
+ if (!funpack) {
+ Wrapper_add_local(f, "value", "PyObject *value");
+ Append(f->code, "if (!PyArg_ParseTuple(args, \"O:set\", &value)) return NULL;\n");
+ }
+ Printf(f->code, "res = %s(%s);\n", varsetname, funpack ? "args" : "value");
+ Append(f->code, "return !res ? SWIG_Py_Void() : NULL;\n");
+ Append(f->code, "}\n");
+ Wrapper_print(f, f_wrappers);
+ add_method(setname, wrapsetname, 0, 0, funpack, 1, 1);
+ DelWrapper(f);
+ }
+ if (!builtin) {
+ Printv(f_shadow, tab4, symname, " = property(", module, ".", getname, NIL);
+ if (assignable)
+ Printv(f_shadow, ", ", module, ".", setname, NIL);
+ if (have_docstring(n)) {
+ String *s = docstring(n, AUTODOC_VAR, tab4);
+ if (Len(s))
+ Printv(f_shadow, ", doc=", s, NIL);
+ }
+ Printv(f_shadow, ")\n", NIL);
+ }
+ String *getter = Getattr(n, "pybuiltin:getter");
+ String *setter = Getattr(n, "pybuiltin:setter");
+ Hash *h = NULL;
+ if (getter || setter) {
+ h = Getattr(builtin_getset, symname);
+ if (!h) {
+ h = NewHash();
+ Setattr(h, "static", "1");
+ Setattr(builtin_getset, symname, h);
+ }
+ }
+ if (getter)
+ Setattr(h, "getter", getter);
+ if (setter)
+ Setattr(h, "setter", setter);
+ if (h)
+ Delete(h);
+ Delete(mname);
+ Delete(getname);
+ Delete(wrapgetname);
+ Delete(vargetname);
+ Delete(setname);
+ Delete(wrapsetname);
+ Delete(varsetname);
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * memberconstantHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int memberconstantHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ if (builtin && in_class) {
+ Swig_save("builtin_memberconstantHandler", n, "pybuiltin:symname", NIL);
+ Setattr(n, "pybuiltin:symname", symname);
+ }
+ int oldshadow = shadow;
+ if (shadow)
+ shadow = shadow | PYSHADOW_MEMBER;
+ Language::memberconstantHandler(n);
+ shadow = oldshadow;
+
+ if (builtin && in_class) {
+ Swig_restore(n);
+ } else if (shadow) {
+ Printv(f_shadow, tab4, symname, " = ", module, ".", Swig_name_member(NSPACE_TODO, class_name, symname), "\n", NIL);
+ if (have_docstring(n))
+ Printv(f_shadow, tab4, docstring(n, AUTODOC_CONST, tab4), "\n", NIL);
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * insertDirective()
+ *
+ * Hook for %insert directive. We're going to look for special %shadow inserts
+ * as a special case so we can do indenting correctly
+ * ------------------------------------------------------------ */
+
+ virtual int insertDirective(Node *n) {
+ String *code = Getattr(n, "code");
+ String *section = Getattr(n, "section");
+
+ if (!ImportMode && (Cmp(section, "python") == 0 || Cmp(section, "shadow") == 0)) {
+ if (shadow) {
+ String *pycode = indent_pythoncode(code, shadow_indent, Getfile(n), Getline(n), "%pythoncode or %insert(\"python\") block");
+ Printv(f_shadow, pycode, NIL);
+ Delete(pycode);
+ }
+ } else if (!ImportMode && (Cmp(section, "pythonbegin") == 0)) {
+ if (shadow) {
+ String *pycode = indent_pythoncode(code, "", Getfile(n), Getline(n), "%pythonbegin or %insert(\"pythonbegin\") block");
+ Printv(f_shadow_begin, pycode, NIL);
+ Delete(pycode);
+ }
+ } else {
+ Language::insertDirective(n);
+ }
+ return SWIG_OK;
+ }
+
+ virtual String *runtimeCode() {
+ String *s = NewString("");
+ String *shead = Swig_include_sys("pyhead.swg");
+ if (!shead) {
+ Printf(stderr, "*** Unable to open 'pyhead.swg'\n");
+ } else {
+ Append(s, shead);
+ Delete(shead);
+ }
+ String *serrors = Swig_include_sys("pyerrors.swg");
+ if (!serrors) {
+ Printf(stderr, "*** Unable to open 'pyerrors.swg'\n");
+ } else {
+ Append(s, serrors);
+ Delete(serrors);
+ }
+ String *sthread = Swig_include_sys("pythreads.swg");
+ if (!sthread) {
+ Printf(stderr, "*** Unable to open 'pythreads.swg'\n");
+ } else {
+ Append(s, sthread);
+ Delete(sthread);
+ }
+ String *sapi = Swig_include_sys("pyapi.swg");
+ if (!sapi) {
+ Printf(stderr, "*** Unable to open 'pyapi.swg'\n");
+ } else {
+ Append(s, sapi);
+ Delete(sapi);
+ }
+ String *srun = Swig_include_sys("pyrun.swg");
+ if (!srun) {
+ Printf(stderr, "*** Unable to open 'pyrun.swg'\n");
+ } else {
+ Append(s, srun);
+ Delete(srun);
+ }
+ return s;
+ }
+
+ virtual String *defaultExternalRuntimeFilename() {
+ return NewString("swigpyrun.h");
+ }
+
+ /*----------------------------------------------------------------------
+ * kwargsSupport()
+ *--------------------------------------------------------------------*/
+
+ bool kwargsSupport() const {
+ return true;
+ }
+};
+
+/* ---------------------------------------------------------------
+ * classDirectorMethod()
+ *
+ * Emit a virtual director method to pass a method call on to the
+ * underlying Python object.
+ *
+ * ** Moved it here due to internal error on gcc-2.96 **
+ * --------------------------------------------------------------- */
+int PYTHON::classDirectorMethods(Node *n) {
+ director_method_index = 0;
+ return Language::classDirectorMethods(n);
+}
+
+
+int PYTHON::classDirectorMethod(Node *n, Node *parent, String *super) {
+ int is_void = 0;
+ int is_pointer = 0;
+ String *decl = Getattr(n, "decl");
+ String *name = Getattr(n, "name");
+ String *classname = Getattr(parent, "sym:name");
+ String *c_classname = Getattr(parent, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *declaration = NewString("");
+ ParmList *l = Getattr(n, "parms");
+ Wrapper *w = NewWrapper();
+ String *tm;
+ String *wrap_args = NewString("");
+ String *returntype = Getattr(n, "type");
+ String *value = Getattr(n, "value");
+ String *storage = Getattr(n, "storage");
+ bool pure_virtual = false;
+ int status = SWIG_OK;
+ int idx;
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+
+ if (builtin) {
+ // Rename any wrapped parameters called 'self' as the generated code contains a variable with same name
+ Parm *p;
+ for (p = l; p; p = nextSibling(p)) {
+ String *arg = Getattr(p, "name");
+ if (arg && Cmp(arg, "self") == 0)
+ Delattr(p, "name");
+ }
+ }
+
+ if (Cmp(storage, "virtual") == 0) {
+ if (Cmp(value, "0") == 0) {
+ pure_virtual = true;
+ }
+ }
+
+ /* determine if the method returns a pointer */
+ is_pointer = SwigType_ispointer_return(decl);
+ is_void = (!Cmp(returntype, "void") && !is_pointer);
+
+ /* virtual method definition */
+ String *target;
+ String *pclassname = NewStringf("SwigDirector_%s", classname);
+ String *qualified_name = NewStringf("%s::%s", pclassname, name);
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+ /* header declaration */
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Get any exception classes in the throws typemap
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ ParmList *throw_parm_list = 0;
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ Parm *p;
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list)
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+ String *str = SwigType_str(Getattr(p, "type"), 0);
+ Append(w->def, str);
+ Append(declaration, str);
+ Delete(str);
+ }
+ }
+
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+
+ /* declare method return value
+ * if the return value is a reference or const reference, a specialized typemap must
+ * handle it, including declaration of c_result ($result).
+ */
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ }
+ } else {
+ String *cres = SwigType_lstr(returntype, "c_result");
+ Printf(w->code, "%s;\n", cres);
+ Delete(cres);
+ }
+ }
+
+ if (builtin) {
+ Printv(w->code, "PyObject *self = NULL;\n", NIL);
+ Printv(w->code, "(void)self;\n", NIL);
+ }
+
+ if (ignored_method) {
+ if (!pure_virtual) {
+ if (!is_void)
+ Printf(w->code, "return ");
+ String *super_call = Swig_method_call(super, l);
+ Printf(w->code, "%s;\n", super_call);
+ Delete(super_call);
+ } else {
+ Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname),
+ SwigType_namestr(name));
+ }
+ } else {
+ /* attach typemaps to arguments (C/C++ -> Python) */
+ String *arglist = NewString("");
+ String *parse_args = NewString("");
+
+ Swig_director_parms_fixup(l);
+
+ /* remove the wrapper 'w' since it was producing spurious temps */
+ Swig_typemap_attach_parms("in", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ Parm *p;
+ char source[256];
+
+ int outputs = 0;
+ if (!is_void)
+ outputs++;
+
+ /* build argument list and type conversion string */
+ idx = 0;
+ p = l;
+ int use_parse = 0;
+ while (p) {
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ /* old style? caused segfaults without the p!=0 check
+ in the for() condition, and seems dangerous in the
+ while loop as well.
+ while (Getattr(p, "tmap:ignore")) {
+ p = Getattr(p, "tmap:ignore:next");
+ }
+ */
+
+ if (Getattr(p, "tmap:directorargout") != 0)
+ outputs++;
+
+ String *pname = Getattr(p, "name");
+ String *ptype = Getattr(p, "type");
+
+ Putc(',', arglist);
+ if ((tm = Getattr(p, "tmap:directorin")) != 0) {
+ String *parse = Getattr(p, "tmap:directorin:parse");
+ if (!parse) {
+ sprintf(source, "obj%d", idx++);
+ String *input = NewString(source);
+ Setattr(p, "emit:directorinput", input);
+ Replaceall(tm, "$input", input);
+ Delete(input);
+ Replaceall(tm, "$owner", "0");
+ /* Wrapper_add_localv(w, source, "swig::SwigVar_PyObject", source, "= 0", NIL); */
+ Printv(wrap_args, "swig::SwigVar_PyObject ", source, ";\n", NIL);
+
+ Printv(wrap_args, tm, "\n", NIL);
+ Printv(arglist, "(PyObject *)", source, NIL);
+ Putc('O', parse_args);
+ } else {
+ use_parse = 1;
+ Append(parse_args, parse);
+ Setattr(p, "emit:directorinput", pname);
+ Replaceall(tm, "$input", pname);
+ Replaceall(tm, "$owner", "0");
+ if (Len(tm) == 0)
+ Append(tm, pname);
+ Append(arglist, tm);
+ }
+ p = Getattr(p, "tmap:directorin:next");
+ continue;
+ } else if (Cmp(ptype, "void")) {
+ /* special handling for pointers to other C++ director classes.
+ * ideally this would be left to a typemap, but there is currently no
+ * way to selectively apply the dynamic_cast<> to classes that have
+ * directors. in other words, the type "SwigDirector_$1_lname" only exists
+ * for classes with directors. we avoid the problem here by checking
+ * module.wrap::directormap, but it's not clear how to get a typemap to
+ * do something similar. perhaps a new default typemap (in addition
+ * to SWIGTYPE) called DIRECTORTYPE?
+ */
+ if (SwigType_ispointer(ptype) || SwigType_isreference(ptype)) {
+ Node *module = Getattr(parent, "module");
+ Node *target = Swig_directormap(module, ptype);
+ sprintf(source, "obj%d", idx++);
+ String *nonconst = 0;
+ /* strip pointer/reference --- should move to Swig/stype.c */
+ String *nptype = NewString(Char(ptype) + 2);
+ /* name as pointer */
+ String *ppname = Copy(pname);
+ if (SwigType_isreference(ptype)) {
+ Insert(ppname, 0, "&");
+ }
+ /* if necessary, cast away const since Python doesn't support it! */
+ if (SwigType_isconst(nptype)) {
+ nonconst = NewStringf("nc_tmp_%s", pname);
+ String *nonconst_i = NewStringf("= const_cast< %s >(%s)", SwigType_lstr(ptype, 0), ppname);
+ Wrapper_add_localv(w, nonconst, SwigType_lstr(ptype, 0), nonconst, nonconst_i, NIL);
+ Delete(nonconst_i);
+ Swig_warning(WARN_LANG_DISCARD_CONST, input_file, line_number,
+ "Target language argument '%s' discards const in director method %s::%s.\n",
+ SwigType_str(ptype, pname), SwigType_namestr(c_classname), SwigType_namestr(name));
+ } else {
+ nonconst = Copy(ppname);
+ }
+ Delete(nptype);
+ Delete(ppname);
+ String *mangle = SwigType_manglestr(ptype);
+ if (target) {
+ String *director = NewStringf("director_%s", mangle);
+ Wrapper_add_localv(w, director, "Swig::Director *", director, "= 0", NIL);
+ Wrapper_add_localv(w, source, "swig::SwigVar_PyObject", source, "= 0", NIL);
+ Printf(wrap_args, "%s = SWIG_DIRECTOR_CAST(%s);\n", director, nonconst);
+ Printf(wrap_args, "if (!%s) {\n", director);
+ Printf(wrap_args, "%s = SWIG_InternalNewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
+ Append(wrap_args, "} else {\n");
+ Printf(wrap_args, "%s = %s->swig_get_self();\n", source, director);
+ Printf(wrap_args, "Py_INCREF((PyObject *)%s);\n", source);
+ Append(wrap_args, "}\n");
+ Delete(director);
+ Printv(arglist, source, NIL);
+ } else {
+ Wrapper_add_localv(w, source, "swig::SwigVar_PyObject", source, "= 0", NIL);
+ Printf(wrap_args, "%s = SWIG_InternalNewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
+ //Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE_p_%s, 0);\n",
+ // source, nonconst, base);
+ Printv(arglist, source, NIL);
+ }
+ Putc('O', parse_args);
+ Delete(mangle);
+ Delete(nonconst);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
+ "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(ptype, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_NOWRAP;
+ break;
+ }
+ }
+ p = nextSibling(p);
+ }
+
+ /* add the method name as a PyString */
+ String *pyname = Getattr(n, "sym:name");
+
+ int allow_thread = threads_enable(n);
+
+ if (allow_thread) {
+ thread_begin_block(n, w->code);
+ Append(w->code, "{\n");
+ }
+
+ /* wrap complex arguments to PyObjects */
+ Printv(w->code, wrap_args, NIL);
+
+ /* pass the method call on to the Python object */
+ if (dirprot_mode() && !is_public(n)) {
+ Printf(w->code, "swig_set_inner(\"%s\", true);\n", name);
+ }
+
+
+ Append(w->code, "if (!swig_get_self()) {\n");
+ Printf(w->code, " Swig::DirectorException::raise(\"'self' uninitialized, maybe you forgot to call %s.__init__.\");\n", classname);
+ Append(w->code, "}\n");
+ Append(w->code, "#if defined(SWIG_PYTHON_DIRECTOR_VTABLE)\n");
+ Printf(w->code, "const size_t swig_method_index = %d;\n", director_method_index++);
+ Printf(w->code, "const char *const swig_method_name = \"%s\";\n", pyname);
+
+ Append(w->code, "PyObject *method = swig_get_method(swig_method_index, swig_method_name);\n");
+ if (Len(parse_args) > 0) {
+ if (use_parse) {
+ Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallFunction(method, (char *)\"(%s)\" %s);\n", Swig_cresult_name(), parse_args, arglist);
+ } else {
+ Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallFunctionObjArgs(method %s, NULL);\n", Swig_cresult_name(), arglist);
+ }
+ } else {
+ Append(w->code, "swig::SwigVar_PyObject args = PyTuple_New(0);\n");
+ Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_Call(method, (PyObject *) args, NULL);\n", Swig_cresult_name());
+ }
+ Append(w->code, "#else\n");
+ if (Len(parse_args) > 0) {
+ if (use_parse) {
+ Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallMethod(swig_get_self(), (char *)\"%s\", (char *)\"(%s)\" %s);\n", Swig_cresult_name(), pyname, parse_args, arglist);
+ } else {
+ Printf(w->code, "swig::SwigVar_PyObject swig_method_name = SWIG_Python_str_FromChar(\"%s\");\n", pyname);
+ Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallMethodObjArgs(swig_get_self(), (PyObject *) swig_method_name %s, NULL);\n", Swig_cresult_name(), arglist);
+ }
+ } else {
+ Printf(w->code, "swig::SwigVar_PyObject swig_method_name = SWIG_Python_str_FromChar(\"%s\");\n", pyname);
+ Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallMethodObjArgs(swig_get_self(), (PyObject *) swig_method_name, NULL);\n", Swig_cresult_name());
+ }
+ Append(w->code, "#endif\n");
+
+ if (dirprot_mode() && !is_public(n))
+ Printf(w->code, "swig_set_inner(\"%s\", false);\n", name);
+
+ /* exception handling */
+ tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
+ if (!tm) {
+ tm = Getattr(n, "feature:director:except");
+ if (tm)
+ tm = Copy(tm);
+ }
+ Printf(w->code, "if (!%s) {\n", Swig_cresult_name());
+ Append(w->code, " PyObject *error = PyErr_Occurred();\n");
+ if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
+ Replaceall(tm, "$error", "error");
+ Printv(w->code, Str(tm), "\n", NIL);
+ } else {
+ Append(w->code, " if (error) {\n");
+ Printf(w->code, " Swig::DirectorMethodException::raise(\"Error detected when calling '%s.%s'\");\n", classname, pyname);
+ Append(w->code, " }\n");
+ }
+ Append(w->code, "}\n");
+ Delete(tm);
+
+ /*
+ * Python method may return a simple object, or a tuple.
+ * for in/out arguments, we have to extract the appropriate PyObjects from the tuple,
+ * then marshal everything back to C/C++ (return value and output arguments).
+ *
+ */
+
+ /* marshal return value and other outputs (if any) from PyObject to C/C++ type */
+
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+
+ if (outputs > 1) {
+ Wrapper_add_local(w, "output", "PyObject *output");
+ Printf(w->code, "if (!PyTuple_Check(%s)) {\n", Swig_cresult_name());
+ Printf(w->code, " Swig::DirectorTypeMismatchException::raise(\"Python method %s.%sfailed to return a tuple.\");\n", classname, pyname);
+ Append(w->code, "}\n");
+ }
+
+ idx = 0;
+
+ /* marshal return value */
+ if (!is_void) {
+ tm = Swig_typemap_lookup("directorout", n, Swig_cresult_name(), w);
+ if (tm != 0) {
+ if (outputs > 1) {
+ Printf(w->code, "output = PyTuple_GetItem(%s, %d);\n", Swig_cresult_name(), idx++);
+ Replaceall(tm, "$input", "output");
+ } else {
+ Replaceall(tm, "$input", Swig_cresult_name());
+ }
+ char temp[24];
+ sprintf(temp, "%d", idx);
+ Replaceall(tm, "$argnum", temp);
+
+ /* TODO check this */
+ if (Getattr(n, "wrap:disown")) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+ if (Getattr(n, "tmap:directorout:implicitconv")) {
+ Replaceall(tm, "$implicitconv", get_implicitconv_flag(n));
+ }
+ Replaceall(tm, "$result", "c_result");
+ Printv(w->code, tm, "\n", NIL);
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(returntype, 0), SwigType_namestr(c_classname),
+ SwigType_namestr(name));
+ status = SWIG_ERROR;
+ }
+ }
+
+ /* marshal outputs */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
+ if (outputs > 1) {
+ Printf(w->code, "output = PyTuple_GetItem(%s, %d);\n", Swig_cresult_name(), idx++);
+ Replaceall(tm, "$result", "output");
+ } else {
+ Replaceall(tm, "$result", Swig_cresult_name());
+ }
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* any existing helper functions to handle this? */
+ if (allow_thread) {
+ Append(w->code, "}\n");
+ thread_end_block(n, w->code);
+ }
+
+ Delete(parse_args);
+ Delete(arglist);
+ Delete(cleanup);
+ Delete(outarg);
+ }
+
+ if (!is_void) {
+ if (!(ignored_method && !pure_virtual)) {
+ String *rettype = SwigType_str(returntype, 0);
+ if (!SwigType_isreference(returntype)) {
+ Printf(w->code, "return (%s) c_result;\n", rettype);
+ } else {
+ Printf(w->code, "return (%s) *c_result;\n", rettype);
+ }
+ Delete(rettype);
+ }
+ }
+
+ Append(w->code, "}\n");
+
+ // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewString("");
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+
+ /* emit the director method */
+ if (status == SWIG_OK) {
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+
+ /* clean up */
+ Delete(wrap_args);
+ Delete(pclassname);
+ DelWrapper(w);
+ return status;
+}
+
+/* -----------------------------------------------------------------------------
+ * swig_python() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_python() {
+ return new PYTHON();
+}
+extern "C" Language *swig_python(void) {
+ return new_swig_python();
+}
diff --git a/contrib/tools/swig/Source/Modules/r.cxx b/contrib/tools/swig/Source/Modules/r.cxx
new file mode 100644
index 0000000000..9b465a5716
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/r.cxx
@@ -0,0 +1,2889 @@
+
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * r.cxx
+ *
+ * R language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+static String* replaceInitialDash(const String *name)
+{
+ String *retval;
+ if (!Strncmp(name, "_", 1)) {
+ retval = Copy(name);
+ Insert(retval, 0, "s");
+ } else {
+ retval = Copy(name);
+ }
+ return retval;
+}
+
+static String * getRTypeName(SwigType *t, int *outCount = NULL) {
+ String *b = SwigType_base(t);
+ List *els = SwigType_split(t);
+ int count = 0;
+ int i;
+
+ if(Strncmp(b, "struct ", 7) == 0)
+ Replace(b, "struct ", "", DOH_REPLACE_FIRST);
+
+ for(i = 0; i < Len(els); i++) {
+ String *el = Getitem(els, i);
+ if(Strcmp(el, "p.") == 0 || Strncmp(el, "a(", 2) == 0) {
+ count++;
+ Append(b, "Ref");
+ }
+ }
+ if(outCount)
+ *outCount = count;
+
+ String *tmp = NewString("");
+ char *retName = Char(SwigType_manglestr(t));
+ Insert(tmp, 0, retName);
+ return tmp;
+
+}
+
+/* --------------------------------------------------------------
+ * Tries to get the resolved name, with options of adding
+ * or removing a layer of references. Take care not
+ * to request both
+ * --------------------------------------------------------------*/
+
+static String *getRClassName(String *retType, int deRef=0, int upRef=0) {
+ SwigType *resolved = SwigType_typedef_resolve_all(retType);
+ int ispointer = SwigType_ispointer(resolved);
+ int isreference = SwigType_isreference(resolved);
+ if (upRef) {
+ SwigType_add_pointer(resolved);
+ }
+ if (deRef) {
+ if (ispointer) {
+ SwigType_del_pointer(resolved);
+ }
+ if (isreference) {
+ SwigType_del_reference(resolved);
+ }
+ }
+ String *tmp = NewString("");
+ Insert(tmp, 0, Char(SwigType_manglestr(resolved)));
+ return(tmp);
+}
+
+/* --------------------------------------------------------------
+ * Tries to get the name of the R class corresponding to the given type
+ * e.g. struct A * is ARef, struct A** is ARefRef.
+ * Now handles arrays, i.e. struct A[2]
+ * --------------------------------------------------------------*/
+
+
+static String * getRClassNameCopyStruct(String *retType, int addRef) {
+ String *tmp = NewString("");
+
+ List *l = SwigType_split(retType);
+ int n = Len(l);
+ if(!l || n == 0) {
+#ifdef R_SWIG_VERBOSE
+ Printf(stdout, "SwigType_split return an empty list for %s\n", retType);
+#endif
+ return(tmp);
+ }
+
+
+ String *el = Getitem(l, n-1);
+ char *ptr = Char(el);
+ if(strncmp(ptr, "struct ", 7) == 0)
+ ptr += 7;
+
+ Printf(tmp, "%s", ptr);
+
+ if(addRef) {
+ for(int i = 0; i < n; i++) {
+ if(Strcmp(Getitem(l, i), "p.") == 0 ||
+ Strncmp(Getitem(l, i), "a(", 2) == 0)
+ Printf(tmp, "Ref");
+ }
+ }
+
+ return tmp;
+}
+
+
+/* -------------------------------------------------------------
+ * Write the elements of a list to the File*, one element per line.
+ * If quote is true, surround the element with "element".
+ * This takes care of inserting a tab in front of each line and also
+ * a comma after each element, except the last one.
+ * --------------------------------------------------------------*/
+
+
+static void writeListByLine(List *l, File *out, bool quote = 0) {
+ int i, n = Len(l);
+ for(i = 0; i < n; i++)
+ Printf(out, "%s%s%s%s%s\n", tab8,
+ quote ? "\"" :"",
+ Getitem(l, i),
+ quote ? "\"" :"", i < n-1 ? "," : "");
+}
+
+
+static const char *usage = "\
+R Options (available with -r)\n\
+ -copystruct - Emit R code to copy C structs (on by default)\n\
+ -debug - Output debug\n\
+ -dll <name> - Name of the DLL (without the .dll or .so suffix).\n\
+ Default is the module name.\n\
+ -gc - Aggressive garbage collection\n\
+ -memoryprof - Add memory profile\n\
+ -namespace - Output NAMESPACE file\n\
+ -no-init-code - Turn off the generation of the R_init_<pkgname> code\n\
+ (registration information still generated)\n\
+ -package <name> - Package name for the PACKAGE argument of the R .Call()\n\
+ invocations. Default is the module name.\n\
+";
+
+
+
+/* -------------------------------------------------------------
+ * Display the help for this module on the screen/console.
+ * --------------------------------------------------------------*/
+
+static void showUsage() {
+ fputs(usage, stdout);
+}
+
+static bool expandTypedef(SwigType *t) {
+ if (SwigType_isenum(t)) return false;
+ String *prefix = SwigType_prefix(t);
+ if (Strncmp(prefix, "f", 1)) return false;
+ if (Strncmp(prefix, "p.f", 3)) return false;
+ return true;
+}
+
+
+/* -------------------------------------------------------------
+ * Determine whether we should add a .copy argument to the S function
+ * that wraps/interfaces to the routine that returns the given type.
+ * --------------------------------------------------------------*/
+
+static int addCopyParameter(SwigType *type) {
+ int ok = 0;
+ ok = Strncmp(type, "struct ", 7) == 0 || Strncmp(type, "p.struct ", 9) == 0;
+ if(!ok) {
+ ok = Strncmp(type, "p.", 2);
+ }
+
+ return(ok);
+}
+
+static void replaceRClass(String *tm, SwigType *type) {
+ String *tmp = getRClassName(type, 0, 0);
+ String *tmp_base = getRClassName(type, 1, 0);
+ String *tmp_ref = getRClassName(type, 0, 1);
+ Replaceall(tm, "$R_class", tmp);
+ Replaceall(tm, "$*R_class", tmp_base);
+ Replaceall(tm, "$&R_class", tmp_ref);
+ Delete(tmp); Delete(tmp_base); Delete(tmp_ref);
+}
+
+class R : public Language {
+public:
+ R();
+ void registerClass(Node *n);
+ void main(int argc, char *argv[]);
+ int top(Node *n);
+
+ void dispatchFunction(Node *n);
+ int functionWrapper(Node *n);
+ int constantWrapper(Node *n);
+ int variableWrapper(Node *n);
+
+ int classDeclaration(Node *n);
+ int enumDeclaration(Node *n);
+ String *enumValue(Node *n);
+ virtual int enumvalueDeclaration(Node *n);
+ int membervariableHandler(Node *n);
+
+ int typedefHandler(Node *n);
+
+ static List *Swig_overload_rank(Node *n, bool script_lang_wrapping);
+
+ int memberfunctionHandler(Node *n) {
+ if (debugMode)
+ Printf(stdout, "<memberfunctionHandler> %s %s\n",
+ Getattr(n, "name"),
+ Getattr(n, "type"));
+ member_name = Getattr(n, "sym:name");
+ processing_class_member_function = 1;
+ int status = Language::memberfunctionHandler(n);
+ processing_class_member_function = 0;
+ return status;
+ }
+
+ /* Grab the name of the current class being processed so that we can
+ deal with members of that class. */
+ int classHandler(Node *n){
+ if(!ClassMemberTable)
+ ClassMemberTable = NewHash();
+
+ class_name = Getattr(n, "name");
+ int status = Language::classHandler(n);
+
+ class_name = NULL;
+ return status;
+ }
+
+ String *runtimeCode();
+ void replaceSpecialVariables(String *method, String *tm, Parm *parm);
+
+protected:
+ int addRegistrationRoutine(String *rname, int nargs);
+ int outputRegistrationRoutines(File *out);
+
+ int outputCommandLineArguments(File *out);
+ int generateCopyRoutines(Node *n);
+ int DumpCode(Node *n);
+
+ int OutputMemberReferenceMethod(String *className, int isSet, List *memberList, List *nameList, List *typeList, File *out);
+ int defineArrayAccessors(SwigType *type);
+
+ void addNamespaceFunction(String *name) {
+ if(!namespaceFunctions)
+ namespaceFunctions = NewList();
+ Append(namespaceFunctions, name);
+ }
+
+ void addNamespaceMethod(String *name) {
+ if(!namespaceMethods)
+ namespaceMethods = NewList();
+ Append(namespaceMethods, name);
+ }
+
+ String* processType(SwigType *t, Node *n, int *nargs = NULL);
+ String *createFunctionPointerHandler(SwigType *t, Node *n, int *nargs);
+ int addFunctionPointerProxy(String *name, Node *n, SwigType *t, String *s_paramTypes) {
+ /*XXX Do we need to put the t in there to get the return type later. */
+ if(!functionPointerProxyTable)
+ functionPointerProxyTable = NewHash();
+
+ Setattr(functionPointerProxyTable, name, n);
+
+ Setattr(SClassDefs, name, name);
+ Printv(s_classes, "setClass('",
+ name,
+ "',\n", tab8,
+ "prototype = list(parameterTypes = c(", s_paramTypes, "),\n",
+ tab8, tab8, tab8,
+ "returnType = '", SwigType_manglestr(t), "'),\n", tab8,
+ "contains = 'CRoutinePointer')\n\n##\n", NIL);
+
+ return SWIG_OK;
+ }
+
+
+ void addSMethodInfo(String *name,
+ String *argType, int nargs);
+ // Simple initialization such as constant strings that can be reused.
+ void init();
+
+
+ void addAccessor(String *memberName, Wrapper *f,
+ String *name, String *methodSetGet);
+
+ static int getFunctionPointerNumArgs(Node *n, SwigType *tt);
+
+ // filtering of class member lists by function type. Used in constructing accessors
+ // are we allowed to use stl style functors to customise this?
+ List* filterMemberList(List *class_member_function_types, List *class_member_other, String *R_MEMBER, bool equal);
+
+protected:
+ bool copyStruct;
+ bool memoryProfile;
+ bool aggressiveGc;
+
+ // Strings into which we cumulate the generated code that is to be written
+ //vto the files.
+ String *enum_values;
+ String *enum_def_calls;
+ String *sfile;
+ String *f_init;
+ String *s_classes;
+ String *f_begin;
+ String *f_runtime;
+ String *f_wrapper;
+ String *s_header;
+ String *f_wrappers;
+ String *s_init;
+ String *s_init_routine;
+ String *s_namespace;
+
+ // State variables that carry information across calls to functionWrapper()
+ // from member accessors and class declarations.
+ String *opaqueClassDeclaration;
+ int processing_variable;
+ int processing_member_access_function;
+ String *member_name;
+ String *class_name;
+
+ String *R_MEMBER_NORMAL;
+ String *R_MEMBER_SET;
+ String *R_MEMBER_GET;
+
+ int processing_class_member_function;
+ // Spread out the lists so that they are simpler to process
+ // by storing the type of the method (i.e. set, get or nothing)
+ // and having separate lists for name, membername and wrapper
+ List *class_member_function_types;
+ List *class_member_function_names;
+ List *class_member_function_membernames;
+ List *class_member_function_wrappernames;
+ /* */
+ Hash *ClassMemberTable;
+ Hash *ClassMethodsTable;
+ Hash *SClassDefs;
+ Hash *SMethodInfo;
+
+ // Information about routines that are generated and to be registered with
+ // R for dynamic lookup.
+ Hash *registrationTable;
+ Hash *functionPointerProxyTable;
+
+ List *namespaceFunctions;
+ List *namespaceMethods;
+ List *namespaceClasses; // Probably can do this from ClassMemberTable.
+
+
+ // Store a copy of the command line.
+ // Need only keep a string that has it formatted.
+ char **Argv;
+ int Argc;
+ bool inCPlusMode;
+
+ // State variables that we remember from the command line settings
+ // potentially that govern the code we generate.
+ String *DllName;
+ String *Rpackage;
+ bool noInitializationCode;
+ bool outputNamespaceInfo;
+
+ String *UnProtectWrapupCode;
+
+ // Static members
+ static bool debugMode;
+};
+
+R::R() :
+ copyStruct(false),
+ memoryProfile(false),
+ aggressiveGc(false),
+ enum_values(0),
+ enum_def_calls(0),
+ sfile(0),
+ f_init(0),
+ s_classes(0),
+ f_begin(0),
+ f_runtime(0),
+ f_wrapper(0),
+ s_header(0),
+ f_wrappers(0),
+ s_init(0),
+ s_init_routine(0),
+ s_namespace(0),
+ opaqueClassDeclaration(0),
+ processing_variable(0),
+ processing_member_access_function(0),
+ member_name(0),
+ class_name(0),
+ R_MEMBER_NORMAL(NewString("normal")),
+ R_MEMBER_SET(NewString("set")),
+ R_MEMBER_GET(NewString("get")),
+ processing_class_member_function(0),
+ class_member_function_types(0),
+ class_member_function_names(0),
+ class_member_function_membernames(0),
+ class_member_function_wrappernames(0),
+ ClassMemberTable(0),
+ ClassMethodsTable(0),
+ SClassDefs(0),
+ SMethodInfo(0),
+ registrationTable(0),
+ functionPointerProxyTable(0),
+ namespaceFunctions(0),
+ namespaceMethods(0),
+ namespaceClasses(0),
+ Argv(0),
+ Argc(0),
+ inCPlusMode(false),
+ DllName(0),
+ Rpackage(0),
+ noInitializationCode(false),
+ outputNamespaceInfo(false),
+ UnProtectWrapupCode(0) {
+}
+
+bool R::debugMode = false;
+
+int R::getFunctionPointerNumArgs(Node *n, SwigType *tt) {
+ (void) tt;
+ n = Getattr(n, "type");
+ if (debugMode)
+ Printf(stdout, "type: %s\n", n);
+
+ ParmList *parms = Getattr(n, "parms");
+ if (debugMode)
+ Printf(stdout, "parms = %p\n", parms);
+ return ParmList_len(parms);
+}
+
+
+void R::addSMethodInfo(String *name, String *argType, int nargs) {
+ (void) argType;
+
+ if(!SMethodInfo)
+ SMethodInfo = NewHash();
+ if (debugMode)
+ Printf(stdout, "[addMethodInfo] %s\n", name);
+
+ Hash *tb = Getattr(SMethodInfo, name);
+
+ if(!tb) {
+ tb = NewHash();
+ Setattr(SMethodInfo, name, tb);
+ }
+
+ String *str = Getattr(tb, "max");
+ int max = -1;
+ if(str)
+ max = atoi(Char(str));
+ if(max < nargs) {
+ if(str) Delete(str);
+ str = NewStringf("%d", max);
+ Setattr(tb, "max", str);
+ }
+}
+
+/* ----------------------------------------
+ * Returns the name of the new routine.
+ * ------------------------------------------ */
+
+String * R::createFunctionPointerHandler(SwigType *t, Node *n, int *numArgs) {
+ String *funName = SwigType_manglestr(t);
+
+ /* See if we have already processed this one. */
+ if(functionPointerProxyTable && Getattr(functionPointerProxyTable, funName))
+ return funName;
+
+ if (debugMode)
+ Printf(stdout, "<createFunctionPointerHandler> Defining %s\n", t);
+
+ SwigType *rettype = Copy(Getattr(n, "type"));
+ SwigType *funcparams = SwigType_functionpointer_decompose(rettype);
+ String *rtype = SwigType_str(rettype, 0);
+
+ // ParmList *parms = Getattr(n, "parms");
+ // memory leak
+ ParmList *parms = SwigType_function_parms(SwigType_del_pointer(Copy(t)), n);
+
+
+ if (debugMode) {
+ Printf(stdout, "Type: %s\n", t);
+ Printf(stdout, "Return type: %s\n", SwigType_base(t));
+ }
+
+ bool isVoidType = Strcmp(rettype, "void") == 0;
+ if (debugMode)
+ Printf(stdout, "%s is void ? %s (%s)\n", funName, isVoidType ? "yes" : "no", rettype);
+
+ Wrapper *f = NewWrapper();
+
+ /* Go through argument list, attach lnames for arguments */
+ int i = 0;
+ Parm *p = parms;
+ for (i = 0; p; p = nextSibling(p), ++i) {
+ String *arg = Getattr(p, "name");
+ String *lname;
+ if (!arg && Cmp(Getattr(p, "type"), "void")) {
+ lname = NewStringf("arg%d", i+1);
+ Setattr(p, "name", lname);
+ } else
+ lname = arg;
+
+ Setattr(p, "lname", lname);
+ }
+
+ Swig_typemap_attach_parms("out", parms, f);
+ Swig_typemap_attach_parms("scoerceout", parms, f);
+ Swig_typemap_attach_parms("scheck", parms, f);
+
+ Printf(f->def, "%s %s(", rtype, funName);
+
+ emit_parameter_variables(parms, f);
+ emit_return_variable(n, rettype, f);
+ // emit_attach_parmmaps(parms,f);
+
+ /* Using weird name and struct to avoid potential conflicts. */
+ Wrapper_add_local(f, "r_swig_cb_data", "RCallbackFunctionData *r_swig_cb_data = R_SWIG_getCallbackFunctionData()");
+ String *lvar = NewString("r_swig_cb_data");
+
+ Wrapper_add_local(f, "r_tmp", "SEXP r_tmp"); // for use in converting arguments to R objects for call.
+ Wrapper_add_local(f, "r_nprotect", "int r_nprotect = 0"); // for use in converting arguments to R objects for call.
+ Wrapper_add_local(f, "r_vmax", "char * r_vmax= 0"); // for use in converting arguments to R objects for call.
+
+ // Add local for error code in return value. This is not in emit_return_variable because that assumes an out typemap
+ // whereas the type makes are reverse
+ Wrapper_add_local(f, "ecode", "int ecode = 0");
+
+ p = parms;
+ int nargs = ParmList_len(parms);
+ if(numArgs) {
+ *numArgs = nargs;
+ if (debugMode)
+ Printf(stdout, "Setting number of parameters to %d\n", *numArgs);
+ }
+ String *setExprElements = NewString("");
+
+ String *s_paramTypes = NewString("");
+ for(i = 0; p; i++) {
+ SwigType *tt = Getattr(p, "type");
+ SwigType *name = Getattr(p, "name");
+ SwigType *swig_parm_name = NewStringf("swigarg_%s", name);
+ String *tm = Getattr(p, "tmap:out");
+ bool isVoidParm = Strcmp(tt, "void") == 0;
+ if (isVoidParm)
+ Printf(f->def, "%s", SwigType_str(tt, 0));
+ else
+ Printf(f->def, "%s %s", SwigType_str(tt, 0), swig_parm_name);
+ if (tm) {
+ String *lstr = SwigType_lstr(tt, 0);
+ if (SwigType_isreference(tt) || SwigType_isrvalue_reference(tt)) {
+ Printf(f->code, "%s = (%s) &%s;\n", Getattr(p, "lname"), lstr, swig_parm_name);
+ } else if (!isVoidParm) {
+ Printf(f->code, "%s = (%s) %s;\n", Getattr(p, "lname"), lstr, swig_parm_name);
+ }
+ Replaceall(tm, "$1", name);
+ Replaceall(tm, "$result", "r_tmp");
+ if (debugMode) {
+ Printf(stdout, "Calling Replace A: %s\n", Getattr(p,"type"));
+ }
+ replaceRClass(tm, Getattr(p,"type"));
+ Replaceall(tm,"$owner", "0");
+ Delete(lstr);
+ }
+
+ Printf(setExprElements, "%s\n", tm);
+ Printf(setExprElements, "SETCAR(r_swig_cb_data->el, %s);\n", "r_tmp");
+ Printf(setExprElements, "r_swig_cb_data->el = CDR(r_swig_cb_data->el);\n\n");
+
+ Printf(s_paramTypes, "'%s'", SwigType_manglestr(tt));
+
+
+ p = nextSibling(p);
+ if(p) {
+ Printf(f->def, ", ");
+ Printf(s_paramTypes, ", ");
+ }
+ }
+
+ Printf(f->def, ") {\n");
+
+ Printf(f->code, "Rf_protect(%s->expr = Rf_allocVector(LANGSXP, %d));\n", lvar, nargs + 1);
+ Printf(f->code, "r_nprotect++;\n");
+ Printf(f->code, "r_swig_cb_data->el = r_swig_cb_data->expr;\n\n");
+
+ Printf(f->code, "SETCAR(r_swig_cb_data->el, r_swig_cb_data->fun);\n");
+ Printf(f->code, "r_swig_cb_data->el = CDR(r_swig_cb_data->el);\n\n");
+
+ Printf(f->code, "%s\n\n", setExprElements);
+
+ Printv(f->code, "r_swig_cb_data->retValue = R_tryEval(",
+ "r_swig_cb_data->expr,",
+ " R_GlobalEnv,",
+ " &r_swig_cb_data->errorOccurred",
+ ");\n",
+ NIL);
+
+ Printv(f->code, "\n",
+ "if(r_swig_cb_data->errorOccurred) {\n",
+ "R_SWIG_popCallbackFunctionData(1);\n",
+ "Rf_error(\"error in calling R function as a function pointer (",
+ funName,
+ ")\");\n",
+ "}\n",
+ NIL);
+
+
+
+ if(!isVoidType) {
+ /* Need to deal with the return type of the function pointer, not the function pointer itself.
+ So build a new node that has the relevant pieces.
+ XXX Have to be a little more clever so that we can deal with struct A * - the * is getting lost.
+ Is this still true? If so, will a SwigType_push() solve things?
+ */
+ Parm *bbase = NewParmNode(rettype, n);
+ String *returnTM = Swig_typemap_lookup("in", bbase, Swig_cresult_name(), f);
+ if(returnTM) {
+ String *tm = returnTM;
+ Replaceall(tm,"$input", "r_swig_cb_data->retValue");
+ replaceRClass(tm, rettype);
+ Replaceall(tm,"$owner", "0");
+ Replaceall(tm,"$disown","0");
+ Printf(f->code, "%s\n", tm);
+ }
+ Delete(bbase);
+ }
+
+ Printv(f->code, "R_SWIG_popCallbackFunctionData(1);\n", NIL);
+ Printv(f->code, "\n", UnProtectWrapupCode, NIL);
+
+ if (SwigType_isreference(rettype)) {
+ Printv(f->code, "return *", Swig_cresult_name(), ";\n", NIL);
+ } else if (SwigType_isrvalue_reference(rettype)) {
+ Printv(f->code, "return std::move(*", Swig_cresult_name(), ");\n", NIL);
+ } else if (!isVoidType) {
+ Printv(f->code, "return ", Swig_cresult_name(), ";\n", NIL);
+ }
+
+ Printv(f->code, "\n}\n", NIL);
+ Replaceall(f->code, "SWIG_exception_fail", "SWIG_exception_noreturn");
+
+ /* To coerce correctly in S, we really want to have an extra/intermediate
+ function that handles the scoerceout.
+ We need to check if any of the argument types have an entry in
+ that map. If none do, the ignore and call the function straight.
+ Otherwise, generate a marshalling function.
+ Need to be able to find it in S. Or use an entirely generic one
+ that evaluates the expressions.
+ Handle errors in the evaluation of the function by restoring
+ the stack, if there is one in use for this function (i.e. no
+ userData).
+ */
+
+ Wrapper_print(f, f_wrapper);
+
+ addFunctionPointerProxy(funName, n, t, s_paramTypes);
+ Delete(s_paramTypes);
+ Delete(rtype);
+ Delete(rettype);
+ Delete(funcparams);
+ DelWrapper(f);
+
+ return funName;
+}
+
+void R::init() {
+ UnProtectWrapupCode =
+ NewStringf("%s", "vmaxset(r_vmax);\nif(r_nprotect) Rf_unprotect(r_nprotect);\n\n");
+
+ SClassDefs = NewHash();
+
+ sfile = NewString("");
+ f_init = NewString("");
+ s_header = NewString("");
+ f_begin = NewString("");
+ f_runtime = NewString("");
+ f_wrapper = NewString("");
+ s_classes = NewString("");
+ s_init = NewString("");
+ s_init_routine = NewString("");
+ enum_def_calls = NewString("");
+}
+
+
+/* -------------------------------------------------------------
+ * Method from Language that is called to start the entire
+ * processing off, i.e. the generation of the code.
+ * It is called after the input has been read and parsed.
+ * Here we open the output streams and generate the code.
+ * ------------------------------------------------------------- */
+int R::top(Node *n) {
+ String *module = Getattr(n, "name");
+
+ if (debugMode) {
+ Printf(stdout, "<Top> %s\n", module);
+ }
+
+ if(!Rpackage)
+ Rpackage = Copy(module);
+ if(!DllName)
+ DllName = Copy(module);
+
+ if(outputNamespaceInfo) {
+ s_namespace = NewString("");
+ Swig_register_filebyname("snamespace", s_namespace);
+ Printf(s_namespace, "useDynLib(%s)\n", DllName);
+ }
+ // Register the naming functions
+ Swig_name_register("wrapper", "R_swig_%f");
+
+ /* Associate the different streams with names so that they can be used in %insert directives by the
+ typemap code. */
+ Swig_register_filebyname("sinit", s_init);
+ Swig_register_filebyname("sinitroutine", s_init_routine);
+
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("header", s_header);
+ Swig_register_filebyname("wrapper", f_wrapper);
+ Swig_register_filebyname("s", sfile);
+ Swig_register_filebyname("sclasses", s_classes);
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "R");
+
+ Swig_banner_target_lang(s_init, "#");
+ outputCommandLineArguments(s_init);
+
+ Printf(f_wrapper, "#ifdef __cplusplus\n");
+ Printf(f_wrapper, "extern \"C\" {\n");
+ Printf(f_wrapper, "#endif\n\n");
+
+ Language::top(n);
+
+ Printf(f_wrapper, "#ifdef __cplusplus\n");
+ Printf(f_wrapper, "}\n");
+ Printf(f_wrapper, "#endif\n");
+
+ String *type_table = NewString("");
+ SwigType_emit_type_table(f_runtime,f_wrapper);
+ Delete(type_table);
+
+ if(ClassMemberTable) {
+ //XXX OutputClassAccessInfo(ClassMemberTable, sfile);
+ Delete(ClassMemberTable);
+ ClassMemberTable = NULL;
+ }
+
+ Printf(f_init,"}\n");
+ if(registrationTable)
+ outputRegistrationRoutines(f_init);
+
+ /* Now arrange to write the 2 files - .S and .c. */
+
+ DumpCode(n);
+
+ Delete(sfile);
+ Delete(s_classes);
+ Delete(s_init);
+ Delete(f_wrapper);
+ Delete(f_init);
+
+ Delete(s_header);
+ Delete(f_runtime);
+ Delete(f_begin);
+
+ return SWIG_OK;
+}
+
+
+/* -------------------------------------------------------------
+ * Write the generated code to the .S and the .c files.
+ * ------------------------------------------------------------- */
+int R::DumpCode(Node *n) {
+ String *output_filename = NewString("");
+
+
+ /* The name of the file in which we will generate the S code. */
+ Printf(output_filename, "%s%s.R", SWIG_output_directory(), Rpackage);
+
+#ifdef R_SWIG_VERBOSE
+ Printf(stdout, "Writing S code to %s\n", output_filename);
+#endif
+
+ File *scode = NewFile(output_filename, "w", SWIG_output_files());
+ if (!scode) {
+ FileErrorDisplay(output_filename);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(output_filename);
+
+
+ Printf(scode, "%s\n\n", s_init);
+ Printf(scode, "%s\n\n", s_classes);
+ Printf(scode, "%s\n", sfile);
+ Printf(scode, "%s\n", enum_def_calls);
+
+ Delete(scode);
+ String *outfile = Getattr(n,"outfile");
+ File *runtime = NewFile(outfile,"w", SWIG_output_files());
+ if (!runtime) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+
+ Printf(runtime, "%s", f_begin);
+ Printf(runtime, "%s\n", f_runtime);
+ Printf(runtime, "%s\n", s_header);
+ Printf(runtime, "%s\n", f_wrapper);
+ Printf(runtime, "%s\n", f_init);
+
+ Delete(runtime);
+
+ if(outputNamespaceInfo) {
+ output_filename = NewString("");
+ Printf(output_filename, "%sNAMESPACE", SWIG_output_directory());
+ File *ns = NewFile(output_filename, "w", SWIG_output_files());
+ if (!ns) {
+ FileErrorDisplay(output_filename);
+ Exit(EXIT_FAILURE);
+ }
+ Delete(output_filename);
+
+ Printf(ns, "%s\n", s_namespace);
+
+ Printf(ns, "\nexport(\n");
+ writeListByLine(namespaceFunctions, ns);
+ Printf(ns, ")\n");
+ Printf(ns, "\nexportMethods(\n");
+ writeListByLine(namespaceMethods, ns, 1);
+ Printf(ns, ")\n");
+ Delete(ns);
+ Delete(s_namespace);
+ }
+
+ return SWIG_OK;
+}
+
+
+List *R::filterMemberList(List *class_member_types,
+ List *class_member_other,
+ String *R_MEMBER, bool equal) {
+ // filters class_member_other based on whether corresponding elements of
+ // class_member_function_types are equal or notequal to R_MEMBER
+ List *CM = NewList();
+ Iterator ftype, other;
+
+ for (ftype = First(class_member_types), other = First(class_member_other);
+ ftype.item;
+ ftype=Next(ftype), other=Next(other)) {
+ // verbose, clean up later if the overall structure works
+ if (equal) {
+ if (ftype.item == R_MEMBER) {
+ Append(CM, other.item);
+ }
+ } else {
+ if (ftype.item != R_MEMBER) {
+ Append(CM, other.item);
+ }
+ }
+ }
+ return(CM);
+}
+
+# if 0
+// not called
+/* -------------------------------------------------------------
+ * We may need to do more.... so this is left as a
+ * stub for the moment.
+ * -------------------------------------------------------------*/
+int R::OutputClassAccessInfo(Hash *tb, File *out) {
+ int n = OutputClassMemberTable(tb, out);
+ OutputClassMethodsTable(out);
+ return n;
+}
+
+/* -------------------------------------------------------------
+ * Currently this just writes the information collected about the
+ * different methods of the C++ classes that have been processed
+ * to the console.
+ * This will be used later to define S4 generics and methods.
+ * --------------------------------------------------------------*/
+
+int R::OutputClassMethodsTable(File *) {
+ Hash *tb = ClassMethodsTable;
+
+ if(!tb)
+ return SWIG_OK;
+
+ List *keys = Keys(tb);
+ String *key;
+ int i, n = Len(keys);
+ if (debugMode) {
+ for(i = 0; i < n ; i++ ) {
+ key = Getitem(keys, i);
+ Printf(stdout, "%d) %s\n", i, key);
+ List *els = Getattr(tb, key);
+ int nels = Len(els);
+ Printf(stdout, "\t");
+ for(int j = 0; j < nels; j+=2) {
+ Printf(stdout, "%s%s", Getitem(els, j), j < nels - 1 ? ", " : "");
+ Printf(stdout, "%s\n", Getitem(els, j+1));
+ }
+ Printf(stdout, "\n");
+ }
+ }
+
+ return SWIG_OK;
+}
+
+
+/* --------------------------------------------------------------
+ * Iterate over the <class name>_set and <>_get
+ * elements and generate the $ and $<- functions
+ * that provide constrained access to the member
+ * fields in these elements.
+
+ * tb - a hash table that is built up in functionWrapper
+ * as we process each membervalueHandler.
+ * The entries are indexed by <class name>_set and
+ * <class_name>_get. Each entry is a List *.
+
+ * out - the stream where the code is to be written. This is the S
+ * code stream as we generate only S code here.
+ * --------------------------------------------------------------*/
+
+int R::OutputClassMemberTable(Hash *tb, File *out) {
+ List *keys = Keys(tb), *el;
+
+ String *key;
+ int i, n = Len(keys);
+ /* Loop over all the <Class>_set and <Class>_get entries in the table. */
+ /* This function checks for names ending in _set - perhaps it should */
+ /* use attributes of some other form, as it potentially clashes with */
+ /* methods ending in _set */
+
+ if(n && outputNamespaceInfo) {
+ Printf(s_namespace, "exportClasses(");
+ }
+ for(i = 0; i < n; i++) {
+ key = Getitem(keys, i);
+ el = Getattr(tb, key);
+
+ String *className = Getitem(el, 0);
+ char *ptr = Char(key);
+ int klen = Len(key);
+ int isSet = 0;
+ if (klen > 4) {
+ ptr = &ptr[klen - 4];
+ isSet = strcmp(ptr, "_set") == 0;
+ }
+
+ if(outputNamespaceInfo)
+ Printf(s_namespace, "\"%s\"%s", className, i < n-1 ? "," : "");
+ }
+ if(n && outputNamespaceInfo) {
+ Printf(s_namespace, ")\n");
+ }
+
+ return n;
+}
+
+// end not used
+#endif
+/* --------------------------------------------------------------
+ * Write the methods for $ or $<- for accessing a member field in an
+ * struct or union (or class).
+ * className - the name of the struct or union (e.g. Bar for struct Bar)
+ * isSet - a logical value indicating whether the method is for
+ * modifying ($<-) or accessing ($) the member field.
+ * el - a list of length 2 * # accessible member elements + 1.
+ * The first element is the name of the class.
+ * The other pairs are member name and the name of the R function to access it.
+ * out - the stream where we write the code.
+ * --------------------------------------------------------------*/
+
+int R::OutputMemberReferenceMethod(String *className, int isSet,
+ List *memberList, List *nameList,
+ List *typeList, File *out) {
+ int numMems = Len(memberList), j;
+ int varaccessor = 0;
+ if (numMems == 0)
+ return SWIG_OK;
+
+ Wrapper *f = NewWrapper(), *attr = NewWrapper();
+
+ Printf(f->def, "function(x, name%s)", isSet ? ", value" : "");
+ Printf(attr->def, "function(x, i, j, ...%s)", isSet ? ", value" : "");
+
+ Printf(f->code, "{\n");
+ Printf(f->code, "%saccessorFuns = list(", tab8);
+
+ Node *itemList = NewHash();
+ bool has_prev = false;
+ for(j = 0; j < numMems; j++) {
+ String *item = Getitem(memberList, j);
+ String *dup = Getitem(nameList, j);
+ String *setgetmethod = Getitem(typeList, j);
+
+ if (setgetmethod == R_MEMBER_GET)
+ varaccessor++;
+
+ if (Getattr(itemList, item))
+ continue;
+ Setattr(itemList, item, "1");
+
+ String *pitem;
+ if (!Strcmp(item, "operator ()")) {
+ pitem = NewString("call");
+ } else if (!Strcmp(item, "operator ->")) {
+ pitem = NewString("deref");
+ } else if (!Strcmp(item, "operator +")) {
+ pitem = NewString("add");
+ } else if (!Strcmp(item, "operator -")) {
+ pitem = NewString("sub");
+ } else {
+ pitem = Copy(item);
+ }
+ if (has_prev)
+ Printf(f->code, ", ");
+ Printf(f->code, "'%s' = %s", pitem, dup);
+ has_prev = true;
+ Delete(pitem);
+ }
+ Delete(itemList);
+ Printf(f->code, ");\n");
+
+ if (!isSet && varaccessor > 0) {
+ Printf(f->code, "%svaccessors = c(", tab8);
+ bool first = true;
+ for(j = 0; j < numMems; j++) {
+ String *item = Getitem(memberList, j);
+ String *setgetmethod = Getitem(typeList, j);
+
+ // Check the type here instead of the name
+ if (setgetmethod == R_MEMBER_GET) {
+ Printf(f->code, "%s'%s'", first ? "" : ", ", item);
+ first = false;
+ }
+ }
+ Printf(f->code, ");\n");
+ }
+
+ Printv(f->code, ";", tab8,
+ "idx = pmatch(name, names(accessorFuns));\n",
+ tab8,
+ "if(is.na(idx)) \n",
+ tab8, tab4, NIL);
+ Printf(f->code, "return(callNextMethod(x, name%s));\n",
+ isSet ? ", value" : "");
+ Printv(f->code, tab8, "f = accessorFuns[[idx]];\n", NIL);
+ if(isSet) {
+ Printv(f->code, tab8, "f(x, value);\n", NIL);
+ Printv(f->code, tab8, "x;\n", NIL); // make certain to return the S value.
+ } else {
+ if (varaccessor) {
+ Printv(f->code, tab8,
+ "if (is.na(match(name, vaccessors))) function(...){f(x, ...)} else f(x);\n", NIL);
+ } else {
+ Printv(f->code, tab8, "function(...){f(x, ...)};\n", NIL);
+ }
+ }
+ Printf(f->code, "}\n");
+
+ String *classname_str = SwigType_namestr(className);
+ Printf(out, "# Start of accessor method for %s\n", classname_str);
+ Printf(out, "setMethod('$%s', '_p%s', ",
+ isSet ? "<-" : "",
+ getRClassName(className));
+ Wrapper_print(f, out);
+ Printf(out, ");\n");
+
+ if(isSet) {
+ Printf(out, "setMethod('[[<-', c('_p%s', 'character'),",
+ getRClassName(className));
+ Insert(f->code, 2, "name = i;\n");
+ Printf(attr->code, "%s", f->code);
+ Wrapper_print(attr, out);
+ Printf(out, ");\n");
+ }
+
+ Printf(out, "# end of accessor method for %s\n", classname_str);
+
+ Delete(classname_str);
+ DelWrapper(attr);
+ DelWrapper(f);
+
+ return SWIG_OK;
+}
+
+/* -------------------------------------------------------------
+ * Called when a enumeration is to be processed.
+ * We want to call the R function defineEnumeration().
+ * tdname is the typedef of the enumeration, i.e. giving its name.
+ * --------------------------------------------------------------*/
+
+int R::enumDeclaration(Node *n) {
+ if (!ImportMode) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC))
+ return SWIG_NOWRAP;
+
+ String *symname = Getattr(n, "sym:name");
+
+ // TODO - deal with anonymous enumerations
+ // Previous enum code for R didn't wrap them
+ if (!symname || Getattr(n, "unnamedinstance"))
+ return SWIG_NOWRAP;
+
+ // create mangled name for the enum
+ // This will have content if the %nspace feature is set on
+ // the input file
+ String *nspace = Getattr(n, "sym:nspace"); // NSpace/getNSpace() only works during Language::enumDeclaration call
+ String *ename;
+
+ String *name = Getattr(n, "name");
+ ename = getRClassName(name);
+ if (debugMode) {
+ Node *current_class = getCurrentClass();
+ String *cl = NewString("");
+ if (current_class) {
+ cl = getEnumClassPrefix();
+ }
+ Printf(stdout, "enumDeclaration: %s, %s, %s, %s, %s\n", name, symname, nspace, ename, cl);
+ }
+ Delete(name);
+ // set up a call to create the R enum structure. The list of
+ // individual elements will be built in enum_code
+ enum_values = 0;
+ // Emit each enum item
+ Language::enumDeclaration(n);
+
+ Printf(enum_def_calls, "defineEnumeration(\"%s\",\n .values=c(%s))\n\n", ename, enum_values);
+ Delete(enum_values);
+ Delete(ename);
+ }
+ return SWIG_OK;
+}
+
+/* -------------------------------------------------------------
+* --------------------------------------------------------------*/
+
+int R::enumvalueDeclaration(Node *n) {
+ if (getCurrentClass() && (cplus_mode != PUBLIC)) {
+ Printf(stdout, "evd: Not public\n");
+ return SWIG_NOWRAP;
+ }
+
+ Swig_require("enumvalueDeclaration", n, "*name", "?value", NIL);
+ String *symname = Getattr(n, "sym:name");
+ String *value = Getattr(n, "value");
+ String *name = Getattr(n, "name");
+ Node *parent = parentNode(n);
+ String *parent_name = Getattr(parent, "name");
+ String *newsymname = 0;
+ String *tmpValue;
+
+ // Strange hack from parent method
+ if (value)
+ tmpValue = NewString(value);
+ else
+ tmpValue = NewString(name);
+ // Note that this is used in enumValue() amongst other places
+ Setattr(n, "value", tmpValue);
+
+ // Deal with enum values that are not int
+ int swigtype = SwigType_type(Getattr(n, "type"));
+ if (swigtype == T_BOOL) {
+ const char *val = Equal(Getattr(n, "enumvalue"), "true") ? "1" : "0";
+ Setattr(n, "enumvalue", val);
+ } else if (swigtype == T_CHAR) {
+ String *val = NewStringf("'%s'", Getattr(n, "enumvalue"));
+ Setattr(n, "enumvalue", val);
+ Delete(val);
+ }
+
+ if (GetFlag(parent, "scopedenum")) {
+ newsymname = Swig_name_member(0, Getattr(parent, "sym:name"), symname);
+ symname = newsymname;
+ }
+
+ {
+ // Wrap C/C++ enums with constant integers or use the typesafe enum pattern
+ SwigType *typemap_lookup_type = parent_name ? parent_name : NewString("enum ");
+ if (debugMode) {
+ Printf(stdout, "Setting type: %s\n", Copy(typemap_lookup_type));
+ }
+ Setattr(n, "type", typemap_lookup_type);
+
+ // Simple integer constants
+ // Note these are always generated for anonymous enums, no matter what enum_feature is specified
+ // Code generated is the same for SimpleEnum and TypeunsafeEnum -> the class it is generated into is determined later
+
+ String *value = enumValue(n);
+ if (enum_values) {
+ Printf(enum_values, ",\n\"%s\" = %s", name, value);
+ } else {
+ enum_values = NewString("");
+ Printf(enum_values, "\"%s\" = %s", name, value);
+ }
+
+ Delete(value);
+ }
+
+ return SWIG_OK;
+}
+
+
+/* -------------------------------------------------------------
+ * Create accessor functions for variables.
+ * Does not create equivalent wrappers for enumerations,
+ * which are handled differently
+ * --------------------------------------------------------------*/
+
+int R::variableWrapper(Node *n) {
+ String *name = Getattr(n, "sym:name");
+ if (debugMode) {
+ Printf(stdout, "variableWrapper %s\n", n);
+ }
+ processing_variable = 1;
+ Language::variableWrapper(n); // Force the emission of the _set and _get function wrappers.
+ processing_variable = 0;
+
+
+ SwigType *ty = Getattr(n, "type");
+ String *nodeType = nodeType(n);
+ int addCopyParam = addCopyParameter(ty);
+
+ //XXX
+ processType(ty, n);
+
+ if (nodeType && !Strcmp(nodeType, "enumitem")) {
+ /* special wrapper for enums - don't want the R _set, _get functions*/
+ if (debugMode) {
+ Printf(stdout, "variableWrapper enum branch\n");
+ }
+ } else if(!SwigType_isconst(ty)) {
+ Wrapper *f = NewWrapper();
+ Printf(f->def, "%s = \nfunction(value%s)\n{\n",
+ name, addCopyParam ? ", .copy = FALSE" : "");
+ Printv(f->code, "if(missing(value)) {\n",
+ name, "_get(", addCopyParam ? ".copy" : "", ")\n}", NIL);
+ Printv(f->code, " else {\n",
+ name, "_set(value)\n}\n}", NIL);
+
+ Wrapper_print(f, sfile);
+ DelWrapper(f);
+ } else {
+ Printf(sfile, "%s = %s_get\n", name, name);
+ }
+
+ return SWIG_OK;
+}
+
+/* -------------------------------------------------------------
+ * Creates accessor functions for class members.
+
+ * ToDo - this version depends on naming conventions and needs
+ * to be replaced.
+ * --------------------------------------------------------------*/
+
+void R::addAccessor(String *memberName, Wrapper *wrapper, String *name,
+ String *methodSetGet) {
+
+ if (!class_member_function_names) {
+ class_member_function_names = NewList();
+ class_member_function_membernames = NewList();
+ class_member_function_wrappernames = NewList();
+ class_member_function_types = NewList();
+ }
+ Append(class_member_function_types, methodSetGet);
+ Append(class_member_function_names, name);
+ Append(class_member_function_membernames, memberName);
+
+ String *tmp = NewString("");
+ Wrapper_print(wrapper, tmp);
+ Append(class_member_function_wrappernames, tmp);
+ // if we could put the wrapper in directly: Append(l, Copy(sfun));
+ if (debugMode)
+ Printf(stdout, "Adding accessor: %s (%s) => %s\n", memberName, name, tmp);
+}
+
+#define MAX_OVERLOAD 256
+
+namespace {
+struct Overloaded {
+ Node *n; /* Node */
+ int argc; /* Argument count */
+ ParmList *parms; /* Parameters used for overload check */
+ int error; /* Ambiguity error */
+};
+}
+
+List * R::Swig_overload_rank(Node *n,
+ bool script_lang_wrapping) {
+ Overloaded nodes[MAX_OVERLOAD];
+ int nnodes = 0;
+ Node *o = Getattr(n,"sym:overloaded");
+
+
+ if (!o) return 0;
+
+ Node *c = o;
+ while (c) {
+ if (Getattr(c,"error")) {
+ c = Getattr(c,"sym:nextSibling");
+ continue;
+ }
+ /* Make a list of all the declarations (methods) that are overloaded with
+ * this one particular method name */
+
+ if (Getattr(c,"wrap:name")) {
+ nodes[nnodes].n = c;
+ nodes[nnodes].parms = Getattr(c,"wrap:parms");
+ nodes[nnodes].argc = emit_num_required(nodes[nnodes].parms);
+ nodes[nnodes].error = 0;
+ nnodes++;
+ }
+ c = Getattr(c,"sym:nextSibling");
+ }
+
+ /* Sort the declarations by required argument count */
+ {
+ int i,j;
+ for (i = 0; i < nnodes; i++) {
+ for (j = i+1; j < nnodes; j++) {
+ if (nodes[i].argc > nodes[j].argc) {
+ Overloaded t = nodes[i];
+ nodes[i] = nodes[j];
+ nodes[j] = t;
+ }
+ }
+ }
+ }
+
+ /* Sort the declarations by argument types */
+ {
+ int i,j;
+ for (i = 0; i < nnodes-1; i++) {
+ if (nodes[i].argc == nodes[i+1].argc) {
+ for (j = i+1; (j < nnodes) && (nodes[j].argc == nodes[i].argc); j++) {
+ Parm *p1 = nodes[i].parms;
+ Parm *p2 = nodes[j].parms;
+ int differ = 0;
+ int num_checked = 0;
+ while (p1 && p2 && (num_checked < nodes[i].argc)) {
+ if (debugMode) {
+ Printf(stdout,"p1 = '%s', p2 = '%s'\n", Getattr(p1,"type"), Getattr(p2,"type"));
+ }
+ if (checkAttribute(p1,"tmap:in:numinputs","0")) {
+ p1 = Getattr(p1,"tmap:in:next");
+ continue;
+ }
+ if (checkAttribute(p2,"tmap:in:numinputs","0")) {
+ p2 = Getattr(p2,"tmap:in:next");
+ continue;
+ }
+ String *t1 = Getattr(p1,"tmap:typecheck:precedence");
+ String *t2 = Getattr(p2,"tmap:typecheck:precedence");
+ if (debugMode) {
+ Printf(stdout,"t1 = '%s', t2 = '%s'\n", t1, t2);
+ }
+ if ((!t1) && (!nodes[i].error)) {
+ Swig_warning(WARN_TYPEMAP_TYPECHECK, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "Overloaded method %s not supported (incomplete type checking rule - no precedence level in typecheck typemap for '%s').\n",
+ Swig_name_decl(nodes[i].n), SwigType_str(Getattr(p1, "type"), 0));
+ nodes[i].error = 1;
+ } else if ((!t2) && (!nodes[j].error)) {
+ Swig_warning(WARN_TYPEMAP_TYPECHECK, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s not supported (incomplete type checking rule - no precedence level in typecheck typemap for '%s').\n",
+ Swig_name_decl(nodes[j].n), SwigType_str(Getattr(p2, "type"), 0));
+ nodes[j].error = 1;
+ }
+ if (t1 && t2) {
+ int t1v, t2v;
+ t1v = atoi(Char(t1));
+ t2v = atoi(Char(t2));
+ differ = t1v-t2v;
+ }
+ else if (!t1 && t2) differ = 1;
+ else if (t1 && !t2) differ = -1;
+ else if (!t1 && !t2) differ = -1;
+ num_checked++;
+ if (differ > 0) {
+ Overloaded t = nodes[i];
+ nodes[i] = nodes[j];
+ nodes[j] = t;
+ break;
+ } else if ((differ == 0) && (Strcmp(t1,"0") == 0)) {
+ t1 = Getattr(p1,"ltype");
+ if (!t1) {
+ t1 = SwigType_ltype(Getattr(p1,"type"));
+ if (Getattr(p1,"tmap:typecheck:SWIGTYPE")) {
+ SwigType_add_pointer(t1);
+ }
+ Setattr(p1,"ltype",t1);
+ }
+ t2 = Getattr(p2,"ltype");
+ if (!t2) {
+ t2 = SwigType_ltype(Getattr(p2,"type"));
+ if (Getattr(p2,"tmap:typecheck:SWIGTYPE")) {
+ SwigType_add_pointer(t2);
+ }
+ Setattr(p2,"ltype",t2);
+ }
+
+ /* Need subtype check here. If t2 is a subtype of t1, then we need to change the
+ order */
+
+ if (SwigType_issubtype(t2,t1)) {
+ Overloaded t = nodes[i];
+ nodes[i] = nodes[j];
+ nodes[j] = t;
+ }
+
+ if (Strcmp(t1,t2) != 0) {
+ differ = 1;
+ break;
+ }
+ } else if (differ) {
+ break;
+ }
+ if (Getattr(p1,"tmap:in:next")) {
+ p1 = Getattr(p1,"tmap:in:next");
+ } else {
+ p1 = nextSibling(p1);
+ }
+ if (Getattr(p2,"tmap:in:next")) {
+ p2 = Getattr(p2,"tmap:in:next");
+ } else {
+ p2 = nextSibling(p2);
+ }
+ }
+ if (!differ) {
+ /* See if declarations differ by const only */
+ String *d1 = Getattr(nodes[i].n, "decl");
+ String *d2 = Getattr(nodes[j].n, "decl");
+ if (d1 && d2) {
+ String *dq1 = Copy(d1);
+ String *dq2 = Copy(d2);
+ if (SwigType_isconst(d1)) {
+ Delete(SwigType_pop(dq1));
+ }
+ if (SwigType_isconst(d2)) {
+ Delete(SwigType_pop(dq2));
+ }
+ if (Strcmp(dq1, dq2) == 0) {
+
+ if (SwigType_isconst(d1) && !SwigType_isconst(d2)) {
+ if (script_lang_wrapping) {
+ // Swap nodes so that the const method gets ignored (shadowed by the non-const method)
+ Overloaded t = nodes[i];
+ nodes[i] = nodes[j];
+ nodes[j] = t;
+ }
+ differ = 1;
+ if (!nodes[j].error) {
+ if (script_lang_wrapping) {
+ Swig_warning(WARN_LANG_OVERLOAD_CONST, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_CONST, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using non-const method %s instead.\n", Swig_name_decl(nodes[i].n));
+ } else {
+ if (!Getattr(nodes[j].n, "overload:ignore")) {
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using %s instead.\n", Swig_name_decl(nodes[i].n));
+ }
+ }
+ }
+ nodes[j].error = 1;
+ } else if (!SwigType_isconst(d1) && SwigType_isconst(d2)) {
+ differ = 1;
+ if (!nodes[j].error) {
+ if (script_lang_wrapping) {
+ Swig_warning(WARN_LANG_OVERLOAD_CONST, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_CONST, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using non-const method %s instead.\n", Swig_name_decl(nodes[i].n));
+ } else {
+ if (!Getattr(nodes[j].n, "overload:ignore")) {
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using %s instead.\n", Swig_name_decl(nodes[i].n));
+ }
+ }
+ }
+ nodes[j].error = 1;
+ }
+ }
+ Delete(dq1);
+ Delete(dq2);
+ }
+ }
+ if (!differ) {
+ if (!nodes[j].error) {
+ if (script_lang_wrapping) {
+ Swig_warning(WARN_LANG_OVERLOAD_SHADOW, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s effectively ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_SHADOW, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "as it is shadowed by %s.\n", Swig_name_decl(nodes[i].n));
+ } else {
+ if (!Getattr(nodes[j].n, "overload:ignore")) {
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[j].n), Getline(nodes[j].n),
+ "Overloaded method %s ignored,\n", Swig_name_decl(nodes[j].n));
+ Swig_warning(WARN_LANG_OVERLOAD_IGNORED, Getfile(nodes[i].n), Getline(nodes[i].n),
+ "using %s instead.\n", Swig_name_decl(nodes[i].n));
+ }
+ }
+ nodes[j].error = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ List *result = NewList();
+ {
+ int i;
+ for (i = 0; i < nnodes; i++) {
+ if (nodes[i].error)
+ Setattr(nodes[i].n, "overload:ignore", "1");
+ Append(result,nodes[i].n);
+ }
+ }
+ return result;
+}
+
+void R::dispatchFunction(Node *n) {
+ Wrapper *f = NewWrapper();
+ String *symname = Getattr(n, "sym:name");
+ String *nodeType = Getattr(n, "nodeType");
+ bool constructor = (!Cmp(nodeType, "constructor"));
+
+ String *sfname = NewString(symname);
+
+ if (constructor)
+ Replace(sfname, "new_", "", DOH_REPLACE_FIRST);
+
+ Printf(f->def,
+ "`%s` <- function(...) {", sfname);
+ if (debugMode) {
+ Swig_print_node(n);
+ }
+ List *dispatch = Swig_overload_rank(n, true);
+ int nfunc = Len(dispatch);
+ Printv(f->code,
+ "argtypes <- mapply(class, list(...));\n",
+ "argv <- list(...);\n",
+ "argc <- length(argtypes);\n",
+ "f <- NULL;\n", NIL);
+
+ Printf(f->code, "# dispatch functions %d\n", nfunc);
+ int cur_args = -1;
+ bool first_compare = true;
+ for (int i=0; i < nfunc; i++) {
+ Node *ni = Getitem(dispatch,i);
+ Parm *pi = Getattr(ni,"wrap:parms");
+ int num_arguments = emit_num_arguments(pi);
+
+ String *overname = Getattr(ni,"sym:overname");
+ if (cur_args != num_arguments) {
+ if (cur_args != -1) {
+ Printv(f->code, "} else ", NIL);
+ }
+ Printf(f->code, "if (argc == %d) {", num_arguments);
+ cur_args = num_arguments;
+ first_compare = true;
+ }
+ Parm *p;
+ int j;
+ if (num_arguments > 0) {
+ if (!first_compare) {
+ Printv(f->code, " else ", NIL);
+ } else {
+ first_compare = false;
+ }
+ Printv(f->code, "if (", NIL);
+ for (p = pi, j = 0 ; j < num_arguments ; j++) {
+ if (debugMode) {
+ Swig_print_node(p);
+ }
+ String *tm = Swig_typemap_lookup("rtype", p, "", 0);
+ if (tm) {
+ replaceRClass(tm, Getattr(p, "type"));
+ }
+
+ String *tmcheck = Swig_typemap_lookup("rtypecheck", p, "", 0);
+ if (tmcheck) {
+ String *tmp_argtype = NewStringf("argtypes[%d]", j+1);
+ Replaceall(tmcheck, "$argtype", tmp_argtype);
+ String *tmp_arg = NewStringf("argv[[%d]]", j+1);
+ Replaceall(tmcheck, "$arg", tmp_arg);
+ replaceRClass(tmcheck, Getattr(p, "type"));
+ if (debugMode) {
+ Printf(stdout, "<rtypecheck>%s\n", tmcheck);
+ }
+ if (num_arguments == 1) {
+ Printf(f->code, "%s", tmcheck);
+ } else {
+ Printf(f->code, "%s(%s)", j == 0 ? "" : " && ", tmcheck);
+ }
+ p = Getattr(p, "tmap:in:next");
+ Delete(tmp_arg);
+ Delete(tmp_argtype);
+ continue;
+ }
+ // Below should be migrated into rtypecheck typemaps
+ // Preparation for this has started by warning in swig-4.1.1 for "numeric", "integer", "character" typemaps
+ // For swig-4.2: remove the code block below and uncomment typemaps marked 'Replacement rtypecheck typemaps' in rtype.swg.
+ // There is a slight difference in output as the typemap approach fixes some bugs due to a missing type resolution below
+ if (tm) {
+ String *tmcode = NULL;
+ Printf(f->code, "%s", j == 0 ? "" : " && ");
+ if (num_arguments != 1)
+ Printf(f->code, "(");
+ Printf(f->code, " ");
+ if (Strcmp(tm, "numeric") == 0) {
+ tmcode = NewString("is.numeric($arg)");
+ } else if (Strcmp(tm, "integer") == 0) {
+ tmcode = NewString("(is.integer($arg) || is.numeric($arg))");
+ } else if (Strcmp(tm, "character") == 0) {
+ tmcode = NewString("is.character($arg)");
+ } else {
+ if (SwigType_ispointer(Getattr(p, "type")))
+ Printf(f->code, "extends(argtypes[%d], '%s') || is.null(argv[[%d]])", j+1, tm, j+1);
+ else
+ Printf(f->code, "extends(argtypes[%d], '%s') && length(argv[[%d]]) == 1", j+1, tm, j+1);
+ }
+ if (tmcode) {
+ if (!SwigType_ispointer(Getattr(p, "type")))
+ Printf(tmcode, " && length($arg) == 1");
+ Swig_warning(WARN_R_MISSING_RTYPECHECK_TYPEMAP, input_file, line_number,
+ "Optional rtypecheck code is deprecated. Add the following typemap to fix as the next version of SWIG will not work without it: %%typemap(\"rtypecheck\") %s %%{ %s %%}\n",
+ SwigType_str(Getattr(p, "type"), 0), tmcode);
+ String *tmp_arg = NewStringf("argv[[%d]]", j+1);
+ Replaceall(tmcode, "$arg", tmp_arg);
+ Printv(f->code, tmcode, NIL);
+ Delete(tmp_arg);
+ }
+ Printf(f->code, " ");
+ if (num_arguments != 1)
+ Printf(f->code, ")");
+ Delete(tmcode);
+ }
+ p = Getattr(p, "tmap:in:next");
+ }
+ Printf(f->code, ") { f <- %s%s; }\n", sfname, overname);
+ } else {
+ Printf(f->code, "f <- %s%s; ", sfname, overname);
+ }
+ }
+ if (cur_args != -1) {
+ Printf(f->code, "};\n");
+ }
+ Printf(f->code, "if (is.null(f)) {\n"
+ "stop(\"cannot find overloaded function for %s with argtypes (\","
+ "toString(argtypes),\")\");\n"
+ "}", sfname);
+ Printv(f->code, ";\nf(...)", NIL);
+ Printv(f->code, ";\n}", NIL);
+ Wrapper_print(f, sfile);
+ Printv(sfile, "# Dispatch function\n", NIL);
+ DelWrapper(f);
+}
+
+/*--------------------------------------------------------------
+
+* --------------------------------------------------------------*/
+
+int R::functionWrapper(Node *n) {
+ String *fname = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ String *type = Getattr(n, "type");
+
+ if (debugMode) {
+ Printf(stdout,
+ "<functionWrapper> %s %s %s\n", fname, iname, type);
+ }
+ String *overname = 0;
+ String *nodeType = Getattr(n, "nodeType");
+ bool constructor = (!Cmp(nodeType, "constructor"));
+ bool destructor = (!Cmp(nodeType, "destructor"));
+
+ String *sfname = NewString(iname);
+
+ if (constructor)
+ Replace(sfname, "new_", "", DOH_REPLACE_FIRST);
+
+ if (Getattr(n,"sym:overloaded")) {
+ overname = Getattr(n,"sym:overname");
+ Append(sfname, overname);
+ }
+
+ if (debugMode)
+ Printf(stdout,
+ "<functionWrapper> processing parameters\n");
+
+
+ ParmList *l = Getattr(n, "parms");
+ Parm *p;
+ String *tm;
+
+ p = l;
+ while(p) {
+ SwigType *resultType = Getattr(p, "type");
+ if (expandTypedef(resultType) &&
+ SwigType_istypedef(resultType)) {
+ SwigType *resolved =
+ SwigType_typedef_resolve_all(resultType);
+ if (expandTypedef(resolved)) {
+ if (debugMode) {
+ Printf(stdout, "Setting type: %s\n", resolved);
+ }
+ Setattr(p, "type", Copy(resolved));
+ }
+ }
+ p = nextSibling(p);
+ }
+
+ String *unresolved_return_type =
+ Copy(type);
+ if (expandTypedef(type) &&
+ SwigType_istypedef(type)) {
+ SwigType *resolved =
+ SwigType_typedef_resolve_all(type);
+ if (debugMode)
+ Printf(stdout, "<functionWrapper> resolved %s\n", Copy(unresolved_return_type));
+ if (expandTypedef(resolved)) {
+ type = Copy(resolved);
+ Setattr(n, "type", type);
+ }
+ }
+ if (debugMode)
+ Printf(stdout, "<functionWrapper> unresolved_return_type %s\n", unresolved_return_type);
+ if(processing_member_access_function) {
+ if (debugMode)
+ Printf(stdout, "<functionWrapper memberAccess> '%s' '%s' '%s' '%s'\n", fname, iname, member_name, class_name);
+
+ if(opaqueClassDeclaration)
+ return SWIG_OK;
+
+
+ /* Add the name of this member to a list for this class_name.
+ We will dump all these at the end. */
+
+ bool isSet = GetFlag(n, "memberset") ? true : false;
+
+ String *tmp = NewString(isSet ? Swig_name_set(NSPACE_TODO, class_name) : Swig_name_get(NSPACE_TODO, class_name));
+
+ List *memList = Getattr(ClassMemberTable, tmp);
+ if(!memList) {
+ memList = NewList();
+ Append(memList, class_name);
+ Setattr(ClassMemberTable, tmp, memList);
+ }
+ Delete(tmp);
+ Append(memList, member_name);
+ Append(memList, iname);
+ }
+
+ int i;
+ int nargs;
+
+ String *wname = Swig_name_wrapper(iname);
+
+ if(overname)
+ Append(wname, overname);
+ Setattr(n,"wrap:name", wname);
+
+ Wrapper *f = NewWrapper();
+ Wrapper *sfun = NewWrapper();
+
+ int isVoidReturnType = (Strcmp(type, "void") == 0);
+ // Need to use the unresolved return type since
+ // typedef resolution removes the const which causes a
+ // mismatch with the function action
+ emit_return_variable(n, unresolved_return_type, f);
+
+ SwigType *rtype = Getattr(n, "type");
+ int addCopyParam = 0;
+
+ if(!isVoidReturnType)
+ addCopyParam = addCopyParameter(rtype);
+
+ if (debugMode)
+ Printf(stdout, "Adding a .copy argument to %s for %s = %s\n",
+ iname, type, addCopyParam ? "yes" : "no");
+
+ Printv(f->def, "SWIGEXPORT SEXP\n", wname, " ( ", NIL);
+
+ Printf(sfun->def, "# Start of %s\n", iname);
+ Printv(sfun->def, "\n`", sfname, "` = function(", NIL);
+
+ if(outputNamespaceInfo) {//XXX Need to be a little more discriminating
+ if (constructor) {
+ String *niname = Copy(iname);
+ Replace(niname, "new_", "", DOH_REPLACE_FIRST);
+ addNamespaceFunction(niname);
+ Delete(niname);
+ } else {
+ addNamespaceFunction(iname);
+ }
+ }
+
+ Swig_typemap_attach_parms("scoercein", l, f);
+ Swig_typemap_attach_parms("scoerceout", l, f);
+ Swig_typemap_attach_parms("scheck", l, f);
+
+ emit_parameter_variables(l, f);
+ emit_attach_parmmaps(l,f);
+ Setattr(n,"wrap:parms",l);
+
+ nargs = emit_num_arguments(l);
+
+ Wrapper_add_local(f, "r_nprotect", "unsigned int r_nprotect = 0");
+ Wrapper_add_localv(f, "r_ans", "SEXP", "r_ans = R_NilValue", NIL);
+ Wrapper_add_localv(f, "r_vmax", "VMAXTYPE", "r_vmax = vmaxget()", NIL);
+
+ String *sargs = NewString("");
+
+
+ String *s_inputTypes = NewString("");
+ String *s_inputMap = NewString("");
+ bool inFirstArg = true;
+ bool inFirstType = true;
+ Parm *curP;
+ for (p =l, i = 0 ; i < nargs ; i++) {
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *tt = Getattr(p, "type");
+ int nargs = -1;
+ String *funcptr_name = processType(tt, p, &nargs);
+
+ // SwigType *tp = Getattr(p, "type");
+ String *name = Getattr(p,"name");
+ String *lname = Getattr(p,"lname");
+
+ // R keyword renaming
+ if (name) {
+ if (Swig_name_warning(p, 0, name, 0)) {
+ name = 0;
+ } else {
+ /* If we have a :: in the parameter name because we are accessing a static member of a class, say, then
+ we need to remove that prefix. */
+ while (Strstr(name, "::")) {
+ //XXX need to free.
+ name = NewStringf("%s", Strchr(name, ':') + 2);
+ if (debugMode)
+ Printf(stdout, "+++ parameter name with :: in it %s\n", name);
+ }
+ }
+ }
+ if (!name || Len(name) == 0)
+ name = NewStringf("s_arg%d", i+1);
+
+ name = replaceInitialDash(name);
+
+ if (!Strncmp(name, "arg", 3)) {
+ name = Copy(name);
+ Insert(name, 0, "s_");
+ }
+
+ if(processing_variable) {
+ name = Copy(name);
+ Insert(name, 0, "s_");
+ }
+
+ if(!Strcmp(name, fname)) {
+ name = Copy(name);
+ Insert(name, 0, "s_");
+ }
+
+ Printf(sargs, "%s, ", name);
+
+ String *tm;
+ if((tm = Getattr(p, "tmap:scoercein"))) {
+ Replaceall(tm, "$input", name);
+ replaceRClass(tm, Getattr(p, "type"));
+
+ if(funcptr_name) {
+ //XXX need to get this to return non-zero
+ if(nargs == -1)
+ nargs = getFunctionPointerNumArgs(p, tt);
+
+ String *snargs = NewStringf("%d", nargs);
+ Printv(sfun->code, "if(is.function(", name, ")) {", "\n",
+ "assert('...' %in% names(formals(", name,
+ ")) || length(formals(", name, ")) >= ", snargs, ");\n} ", NIL);
+ Delete(snargs);
+
+ Printv(sfun->code, "else {\n",
+ "if(is.character(", name, ")) {\n",
+ name, " = getNativeSymbolInfo(", name, ");",
+ "\n};\n",
+ "if(is(", name, ", \"NativeSymbolInfo\")) {\n",
+ name, " = ", name, "$address", ";\n};\n",
+ "if(is(", name, ", \"ExternalReference\")) {\n",
+ name, " = ", name, "@ref;\n}\n",
+ "}; \n",
+ NIL);
+ } else {
+ Printf(sfun->code, "%s\n", tm);
+ }
+ }
+
+ Printv(sfun->def, inFirstArg ? "" : ", ", name, NIL);
+
+ if ((tm = Getattr(p,"tmap:scheck"))) {
+
+ Replaceall(tm,"$input", name);
+ replaceRClass(tm, Getattr(p, "type"));
+ Printf(sfun->code,"%s\n",tm);
+ }
+
+
+
+ curP = p;
+ if ((tm = Getattr(p,"tmap:in"))) {
+
+ Replaceall(tm,"$input", name);
+
+ if (Getattr(p,"wrap:disown") || (Getattr(p,"tmap:in:disown"))) {
+ Replaceall(tm,"$disown","SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm,"$disown","0");
+ }
+
+ if(funcptr_name) {
+ /* have us a function pointer */
+ Printf(f->code, "if(TYPEOF(%s) != CLOSXP) {\n", name);
+ Replaceall(tm,"$R_class", "");
+ } else {
+ replaceRClass(tm, Getattr(p, "type"));
+ }
+
+
+ Printf(f->code,"%s\n",tm);
+ if(funcptr_name)
+ Printf(f->code, "} else {\n%s = %s;\nR_SWIG_pushCallbackFunctionData(%s, NULL);\n}\n",
+ lname, funcptr_name, name);
+ Printv(f->def, inFirstArg ? "" : ", ", "SEXP ", name, NIL);
+ if (Len(name) != 0)
+ inFirstArg = false;
+ p = Getattr(p,"tmap:in:next");
+
+ } else {
+ p = nextSibling(p);
+ }
+
+
+ tm = Swig_typemap_lookup("rtype", curP, "", 0);
+ if(tm) {
+ replaceRClass(tm, Getattr(curP, "type"));
+ }
+ Printf(s_inputTypes, "%s'%s'", inFirstType ? "" : ", ", tm);
+ Printf(s_inputMap, "%s%s='%s'", inFirstType ? "" : ", ", name, tm);
+ inFirstType = false;
+
+ if(funcptr_name)
+ Delete(funcptr_name);
+ } /* end of looping over parameters. */
+
+ if(addCopyParam) {
+ Printf(sfun->def, "%s.copy = FALSE", nargs > 0 ? ", " : "");
+ Printf(f->def, "%sSEXP s_swig_copy", nargs > 0 ? ", " : "");
+
+ Printf(sargs, "as.logical(.copy), ");
+ }
+
+ Printv(f->def, ")\n{\n", NIL);
+ // SWIG_fail in R leads to a call to Rf_error() which calls longjmp()
+ // which means the destructors of any live function-local C++ objects won't
+ // get run. To avoid this happening, we wrap almost everything in the
+ // function in a block, and end that right before Rf_error() at which
+ // point those destructors will get called.
+ if (CPlusPlus) Append(f->def, "{\n");
+
+ Printv(sfun->def, ")\n{\n", NIL);
+
+
+ /* Insert cleanup code */
+ String *cleanup = NewString("");
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ if (tm && (Len(tm) != 0)) {
+ Printv(cleanup, tm, "\n", NIL);
+ }
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ String *outargs = NewString("");
+ int numOutArgs = isVoidReturnType ? -1 : 0;
+ for(p = l, i = 0; p; i++) {
+ if((tm = Getattr(p, "tmap:argout"))) {
+ // String *lname = Getattr(p, "lname");
+ numOutArgs++;
+ String *pos = NewStringf("%d", numOutArgs);
+ Replaceall(tm,"$result", "r_ans");
+ Replaceall(tm,"$n", pos); // The position into which to store the answer.
+ Replaceall(tm,"$arg", Getattr(p, "emit:input"));
+ Replaceall(tm,"$input", Getattr(p, "emit:input"));
+ Replaceall(tm,"$owner", "0");
+
+
+ Printf(outargs, "%s\n", tm);
+ p = Getattr(p,"tmap:argout:next");
+ } else
+ p = nextSibling(p);
+ }
+
+ String *actioncode = emit_action(n);
+
+ /* Deal with the explicit return value. */
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+ SwigType *retType = Getattr(n, "type");
+
+ Replaceall(tm,"$1", Swig_cresult_name());
+ Replaceall(tm,"$result", "r_ans");
+ if (debugMode){
+ Printf(stdout, "Calling replace D: %s, %s, %s\n", retType, n, tm);
+ }
+ replaceRClass(tm, retType);
+
+ if (GetFlag(n,"feature:new")) {
+ Replaceall(tm, "$owner", "SWIG_POINTER_OWN");
+ } else {
+ Replaceall(tm,"$owner", "0");
+ }
+
+ Printf(f->code, "%s\n", tm);
+
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s in function %s.\n", SwigType_str(type, 0), fname);
+ }
+
+
+ if(Len(outargs)) {
+ Wrapper_add_local(f, "R_OutputValues", "SEXP R_OutputValues");
+
+ String *tmp = NewString("");
+ if(!isVoidReturnType)
+ Printf(tmp, "Rf_protect(r_ans);\n");
+
+ Printf(tmp, "Rf_protect(R_OutputValues = Rf_allocVector(VECSXP,%d));\nr_nprotect += %d;\n",
+ numOutArgs + !isVoidReturnType,
+ isVoidReturnType ? 1 : 2);
+
+ if(!isVoidReturnType)
+ Printf(tmp, "SET_VECTOR_ELT(R_OutputValues, 0, r_ans);\n");
+ Printf(tmp, "r_ans = R_OutputValues;\n");
+
+ Insert(outargs, 0, tmp);
+ Delete(tmp);
+
+
+
+ Printv(f->code, outargs, NIL);
+ Delete(outargs);
+
+ }
+
+ /* Output cleanup code */
+ int need_cleanup = Len(cleanup) != 0;
+ if (need_cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+
+ /* Look to see if there is any newfree cleanup code */
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ Delete(tm);
+ }
+
+ Printv(f->code, UnProtectWrapupCode, NIL);
+
+ /*If the user gave us something to convert the result in */
+ if ((tm = Swig_typemap_lookup("scoerceout", n, Swig_cresult_name(), sfun))) {
+ Replaceall(tm,"$result","ans");
+ if (debugMode) {
+ Printf(stdout, "Calling replace B: %s, %s, %s\n", Getattr(n, "type"), Getattr(n, "sym:name"), getNSpace());
+ }
+ replaceRClass(tm, Getattr(n, "type"));
+ Chop(tm);
+ }
+
+
+ Printv(sfun->code, ";", (Len(tm) ? "ans = " : ""), ".Call('", wname,
+ "', ", sargs, "PACKAGE='", Rpackage, "');\n", NIL);
+ if(Len(tm))
+ {
+ Printf(sfun->code, "%s\n\n", tm);
+ if (constructor)
+ {
+ String *finalizer = NewString(iname);
+ Replace(finalizer, "new_", "", DOH_REPLACE_FIRST);
+ Printf(sfun->code, "reg.finalizer(ans@ref, delete_%s);\n", finalizer);
+ }
+ Printf(sfun->code, "ans\n");
+ }
+
+ if (destructor)
+ Printv(f->code, "R_ClearExternalPtr(self);\n", NIL);
+
+ Printv(f->code, "return r_ans;\n", NIL);
+
+ /* Error handling code */
+ Printv(f->code, "fail: SWIGUNUSED;\n", NIL);
+ if (need_cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+ if (CPlusPlus) Append(f->code, "}\n");
+ Printv(f->code, " Rf_error(\"%s %s\", SWIG_ErrorType(SWIG_lasterror_code), SWIG_lasterror_msg);\n", NIL);
+ Printv(f->code, " return R_NilValue;\n", NIL);
+ Delete(cleanup);
+
+ Printv(f->code, "}\n", NIL);
+ Printv(sfun->code, "\n}", NIL);
+
+ /* Substitute the function name */
+ Replaceall(f->code,"$symname",iname);
+
+ Wrapper_print(f, f_wrapper);
+ Wrapper_print(sfun, sfile);
+
+ Printf(sfun->code, "\n# End of %s\n", iname);
+ tm = Swig_typemap_lookup("rtype", n, "", 0);
+ if(tm) {
+ SwigType *retType = Getattr(n, "type");
+ if (debugMode) {
+ Printf(stdout, "Calling replace C: %s\n", Copy(retType));
+ }
+ replaceRClass(tm, retType);
+ }
+
+ Printv(sfile, "attr(`", sfname, "`, 'returnType') = '",
+ isVoidReturnType ? "void" : (tm ? tm : ""),
+ "'\n", NIL);
+
+ if(nargs > 0)
+ Printv(sfile, "attr(`", sfname, "`, \"inputTypes\") = c(",
+ s_inputTypes, ")\n", NIL);
+ Printv(sfile, "class(`", sfname, "`) = c(\"SWIGFunction\", class('",
+ sfname, "'))\n\n", NIL);
+
+ if (memoryProfile) {
+ Printv(sfile, "memory.profile()\n", NIL);
+ }
+ if (aggressiveGc) {
+ Printv(sfile, "gc()\n", NIL);
+ }
+
+ // Printv(sfile, "setMethod('", name, "', '", name, "', ", iname, ")\n\n\n");
+
+
+
+ /* If we are dealing with a method in an C++ class, then
+ add the name of the R function and its definition.
+ XXX need to figure out how to store the Wrapper if possible in the hash/list.
+ Would like to be able to do this so that we can potentially insert
+ */
+ if(processing_member_access_function || processing_class_member_function) {
+ String *method_type = R_MEMBER_NORMAL;
+ if (GetFlag(n, "memberset")) {
+ method_type = R_MEMBER_SET;
+ } else if (GetFlag(n, "memberget")) {
+ method_type = R_MEMBER_GET;
+ }
+ addAccessor(member_name, sfun, iname, method_type);
+ }
+
+ if (Getattr(n, "sym:overloaded") &&
+ !Getattr(n, "sym:nextSibling")) {
+ dispatchFunction(n);
+ }
+
+ addRegistrationRoutine(wname, addCopyParam ? nargs +1 : nargs);
+
+ DelWrapper(f);
+ DelWrapper(sfun);
+
+ Delete(sargs);
+ Delete(sfname);
+ return SWIG_OK;
+}
+
+/* ----------------------------------------------------------------------
+ * R::constantWrapper()
+ * ---------------------------------------------------------------------- */
+
+int R::constantWrapper(Node *n) {
+ (void) n;
+ // TODO
+ return SWIG_OK;
+}
+
+/*--------------------------------------------------------------
+ * Add the specified routine name to the collection of
+ * generated routines that are called from R functions.
+ * This is used to register the routines with R for
+ * resolving symbols.
+
+ * rname - the name of the routine
+ * nargs - the number of arguments it expects.
+ * --------------------------------------------------------------*/
+
+int R::addRegistrationRoutine(String *rname, int nargs) {
+ if(!registrationTable)
+ registrationTable = NewHash();
+
+ String *el =
+ NewStringf("{\"%s\", (DL_FUNC) &%s, %d}", rname, rname, nargs);
+
+ Setattr(registrationTable, rname, el);
+
+ return SWIG_OK;
+}
+
+/* -------------------------------------------------------------
+ * Write the registration information to an array and
+ * create the initialization routine for registering
+ * these.
+ * --------------------------------------------------------------*/
+
+int R::outputRegistrationRoutines(File *out) {
+ int i, n;
+ if(!registrationTable)
+ return(0);
+ if(inCPlusMode)
+ Printf(out, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");
+
+ Printf(out, "#include <R_ext/Rdynload.h>\n\n");
+ if(inCPlusMode)
+ Printf(out, "#ifdef __cplusplus\n}\n#endif\n\n");
+
+ Printf(out, "SWIGINTERN R_CallMethodDef CallEntries[] = {\n");
+
+ List *keys = Keys(registrationTable);
+ n = Len(keys);
+ for(i = 0; i < n; i++)
+ Printf(out, " %s,\n", Getattr(registrationTable, Getitem(keys, i)));
+
+ Printf(out, " {NULL, NULL, 0}\n};\n\n");
+
+ if(!noInitializationCode) {
+ if (inCPlusMode)
+ Printv(out, "extern \"C\" ", NIL);
+ { /* R allows pckage names to have '.' in the name, which is not allowed in C++ var names
+ we simply replace all occurrences of '.' with '_' to construct the var name */
+ String * Rpackage_sane = Copy(Rpackage);
+ Replace(Rpackage_sane, ".", "_", DOH_REPLACE_ANY);
+ Printf(out, "SWIGEXPORT void R_init_%s(DllInfo *dll) {\n", Rpackage_sane);
+ Delete(Rpackage_sane);
+ }
+ Printf(out, "%sR_registerRoutines(dll, NULL, CallEntries, NULL, NULL);\n", tab4);
+ if(Len(s_init_routine)) {
+ Printf(out, "\n%s\n", s_init_routine);
+ }
+ Printf(out, "}\n");
+ }
+
+ return n;
+}
+
+
+
+/* -------------------------------------------------------------
+ * Process a struct, union or class declaration in the source code,
+ * or an anonymous typedef struct
+ * --------------------------------------------------------------*/
+
+//XXX What do we need to do here -
+// Define an S4 class to refer to this.
+
+void R::registerClass(Node *n) {
+ String *name = Getattr(n, "name");
+
+ if (debugMode)
+ Swig_print_node(n);
+ String *sname = NewStringf("_p%s", SwigType_manglestr(name));
+ if(!Getattr(SClassDefs, sname)) {
+ Setattr(SClassDefs, sname, sname);
+ String *base;
+
+ if (CPlusPlus && (Strcmp(nodeType(n), "class") == 0)) {
+ base = NewString("");
+ List *l = Getattr(n, "bases");
+ if(Len(l)) {
+ Printf(base, "c(");
+ for(int i = 0; i < Len(l); i++) {
+ registerClass(Getitem(l, i));
+ Printf(base, "'_p%s'%s",
+ SwigType_manglestr(Getattr(Getitem(l, i), "name")),
+ i < Len(l)-1 ? ", " : "");
+ }
+ Printf(base, ")");
+ } else {
+ base = NewString("'C++Reference'");
+ }
+ } else
+ base = NewString("'ExternalReference'");
+
+ Printf(s_classes, "setClass('%s', contains = %s)\n", sname, base);
+ Delete(base);
+ }
+}
+
+int R::classDeclaration(Node *n) {
+
+ String *name = Getattr(n, "name");
+ String *kind = Getattr(n, "kind");
+
+ if (debugMode)
+ Swig_print_node(n);
+ registerClass(n);
+
+
+ /* If we have a typedef union { ... } U, then we never get to see the typedef
+ via a regular call to typedefHandler. Instead, */
+ if(Getattr(n, "unnamed") && Getattr(n, "storage") && Strcmp(Getattr(n, "storage"), "typedef") == 0
+ && Getattr(n, "tdname") && Strcmp(Getattr(n, "tdname"), name) == 0) {
+ if (debugMode)
+ Printf(stdout, "Typedef in the class declaration for %s\n", name);
+ // typedefHandler(n);
+ }
+
+ bool opaque = GetFlag(n, "feature:opaque") ? true : false;
+
+ if(opaque)
+ opaqueClassDeclaration = name;
+
+ int status = Language::classDeclaration(n);
+
+ opaqueClassDeclaration = NULL;
+
+
+ if (class_member_function_types) {
+
+ // collect the "set" methods
+ List *class_set_membernames = filterMemberList(class_member_function_types,
+ class_member_function_membernames, R_MEMBER_SET, true);
+ List *class_set_functionnames = filterMemberList(class_member_function_types,
+ class_member_function_names, R_MEMBER_SET, true);
+ // this one isn't used - collecting to keep code simpler
+ List *class_set_functiontypes = filterMemberList(class_member_function_types,
+ class_member_function_types, R_MEMBER_SET, true);
+
+ // collect the others
+ List *class_other_membernames = filterMemberList(class_member_function_types,
+ class_member_function_membernames, R_MEMBER_SET, false);
+ List *class_other_functionnames = filterMemberList(class_member_function_types,
+ class_member_function_names, R_MEMBER_SET, false);
+ List *class_other_functiontypes = filterMemberList(class_member_function_types,
+ class_member_function_types, R_MEMBER_SET, false);
+
+ if (Len(class_other_membernames) > 0) {
+ OutputMemberReferenceMethod(name, 0, class_other_membernames, class_other_functionnames, class_other_functiontypes, sfile);
+ }
+ if (Len(class_set_membernames) > 0) {
+ OutputMemberReferenceMethod(name, 1, class_set_membernames, class_set_functionnames, class_set_functiontypes, sfile);
+ }
+ Delete(class_set_membernames);
+ Delete(class_set_functionnames);
+ Delete(class_set_functiontypes);
+ Delete(class_other_membernames);
+ Delete(class_other_functionnames);
+ Delete(class_other_functiontypes);
+ }
+
+ if (class_member_function_types) {
+ Delete(class_member_function_types);
+ class_member_function_types = NULL;
+ Delete(class_member_function_names);
+ class_member_function_names = NULL;
+ Delete(class_member_function_membernames);
+ class_member_function_membernames = NULL;
+ Delete(class_member_function_wrappernames);
+ class_member_function_wrappernames = NULL;
+ }
+ if (Getattr(n, "has_destructor")) {
+ Printf(sfile, "setMethod('delete', '_p%s', function(obj) {delete%s(obj)})\n", getRClassName(name), getRClassName(name));
+
+ }
+ if(!opaque && !Strcmp(kind, "struct") && copyStruct) {
+
+ String *def =
+ NewStringf("setClass(\"%s\",\n%srepresentation(\n", name, tab4);
+ bool firstItem = true;
+
+ for(Node *c = firstChild(n); c; ) {
+ String *elName;
+ String *tp;
+
+ elName = Getattr(c, "name");
+
+ String *elKind = Getattr(c, "kind");
+ if (!Equal(elKind, "variable")) {
+ c = nextSibling(c);
+ continue;
+ }
+ if (!Len(elName)) {
+ c = nextSibling(c);
+ continue;
+ }
+ tp = Swig_typemap_lookup("rtype", c, "", 0);
+ if(!tp) {
+ c = nextSibling(c);
+ continue;
+ }
+ if (Strstr(tp, "R_class")) {
+ c = nextSibling(c);
+ continue;
+ }
+ if (Strcmp(tp, "character") &&
+ Strstr(Getattr(c, "decl"), "p.")) {
+ c = nextSibling(c);
+ continue;
+ }
+
+ if (!firstItem) {
+ Printf(def, ",\n");
+ }
+ // else
+ //XXX How can we tell if this is already done.
+ // SwigType_push(elType, elDecl);
+
+
+ // returns "" tp = processType(elType, c, NULL);
+ // Printf(stdout, "<classDeclaration> elType %p\n", elType);
+ // tp = getRClassNameCopyStruct(Getattr(c, "type"), 1);
+
+ String *elNameT = replaceInitialDash(elName);
+ Printf(def, "%s%s = \"%s\"", tab8, elNameT, tp);
+ firstItem = false;
+ Delete(tp);
+ Delete(elNameT);
+ c = nextSibling(c);
+ }
+ Printf(def, "),\n%scontains = \"RSWIGStruct\")\n", tab8);
+ Printf(s_classes, "%s\n\n# End class %s\n\n", def, name);
+
+ generateCopyRoutines(n);
+
+ Delete(def);
+ }
+
+ return status;
+}
+
+
+
+/* -------------------------------------------------------------
+ * Create the C routines that copy an S object of the class given
+ * by the given struct definition in Node *n to the C value
+ * and also the routine that goes from the C routine to an object
+ * of this S class.
+ * --------------------------------------------------------------*/
+
+/*XXX
+ Clean up the toCRef - make certain the names are correct for the types, etc.
+ in all cases.
+*/
+
+int R::generateCopyRoutines(Node *n) {
+ Wrapper *copyToR = NewWrapper();
+ Wrapper *copyToC = NewWrapper();
+
+ String *name = Getattr(n, "name");
+ String *tdname = Getattr(n, "tdname");
+ String *kind = Getattr(n, "kind");
+ String *type;
+
+ if(Len(tdname)) {
+ type = Copy(tdname);
+ } else {
+ type = NewStringf("%s %s", kind, name);
+ }
+
+ String *mangledName = SwigType_manglestr(name);
+
+ if (debugMode)
+ Printf(stdout, "generateCopyRoutines: name = %s, %s\n", name, type);
+
+ Printf(copyToR->def, "CopyToR%s = function(value, obj = new(\"%s\"))\n{\n",
+ mangledName, name);
+ Printf(copyToC->def, "CopyToC%s = function(value, obj)\n{\n",
+ mangledName);
+
+ Node *c = firstChild(n);
+
+ for(; c; c = nextSibling(c)) {
+ String *elName = Getattr(c, "name");
+ if (!Len(elName)) {
+ continue;
+ }
+ String *elKind = Getattr(c, "kind");
+ if (!Equal(elKind, "variable")) {
+ continue;
+ }
+
+ String *tp = Swig_typemap_lookup("rtype", c, "", 0);
+ if(!tp) {
+ continue;
+ }
+ if (Strstr(tp, "R_class")) {
+ continue;
+ }
+ if (Strcmp(tp, "character") &&
+ Strstr(Getattr(c, "decl"), "p.")) {
+ continue;
+ }
+
+
+ /* The S functions to get and set the member value. */
+ String *elNameT = replaceInitialDash(elName);
+ Printf(copyToR->code, "obj@%s = value$%s;\n", elNameT, elNameT);
+ Printf(copyToC->code, "obj$%s = value@%s;\n", elNameT, elNameT);
+ Delete(elNameT);
+ }
+ Printf(copyToR->code, "obj;\n}\n\n");
+ String *rclassName = getRClassNameCopyStruct(type, 0); // without the Ref.
+ Printf(sfile, "# Start definition of copy functions & methods for %s\n", rclassName);
+
+ Wrapper_print(copyToR, sfile);
+ Printf(copyToC->code, "obj\n}\n\n");
+ Wrapper_print(copyToC, sfile);
+
+
+ Printf(sfile, "# Start definition of copy methods for %s\n", rclassName);
+ Printf(sfile, "setMethod('copyToR', '_p%s', CopyToR%s);\n", mangledName,
+ mangledName);
+ Printf(sfile, "setMethod('copyToC', '%s', CopyToC%s);\n\n", rclassName,
+ mangledName);
+
+ Printf(sfile, "# End definition of copy methods for %s\n", rclassName);
+ Printf(sfile, "# End definition of copy functions & methods for %s\n", rclassName);
+
+ String *m = NewStringf("%sCopyToR", name);
+ addNamespaceMethod(m);
+ char *tt = Char(m); tt[Len(m)-1] = 'C';
+ addNamespaceMethod(m);
+ Delete(m);
+ Delete(rclassName);
+ Delete(mangledName);
+ DelWrapper(copyToR);
+ DelWrapper(copyToC);
+
+ return SWIG_OK;
+}
+
+
+
+/* -------------------------------------------------------------
+ * Called when there is a typedef to be invoked.
+ *
+ * XXX Needs to be enhanced or split to handle the case where we have a
+ * typedef within a classDeclaration emission because the struct/union/etc.
+ * is anonymous.
+ * --------------------------------------------------------------*/
+
+int R::typedefHandler(Node *n) {
+ SwigType *tp = Getattr(n, "type");
+ String *type = Getattr(n, "type");
+ if (debugMode)
+ Printf(stdout, "<typedefHandler> %s\n", Getattr(n, "name"));
+
+ processType(tp, n);
+
+ if(Strncmp(type, "struct ", 7) == 0) {
+ String *name = Getattr(n, "name");
+ char *trueName = Char(type);
+ trueName += 7;
+ if (debugMode)
+ Printf(stdout, "<typedefHandler> Defining S class %s\n", trueName);
+ Printf(s_classes, "setClass('_p%s', contains = 'ExternalReference')\n",
+ SwigType_manglestr(name));
+ }
+
+ return Language::typedefHandler(n);
+}
+
+
+
+/* --------------------------------------------------------------
+ * Called when processing a field in a "class", i.e. struct, union or
+ * actual class. We set a state variable so that we can correctly
+ * interpret the resulting functionWrapper() call and understand that
+ * it is for a field element.
+ * --------------------------------------------------------------*/
+
+int R::membervariableHandler(Node *n) {
+ SwigType *t = Getattr(n, "type");
+ processType(t, n, NULL);
+ processing_member_access_function = 1;
+ member_name = Getattr(n,"sym:name");
+ if (debugMode)
+ Printf(stdout, "<membervariableHandler> name = %s, sym:name = %s\n",
+ Getattr(n, "name"), member_name);
+
+ int status(Language::membervariableHandler(n));
+
+ if(!opaqueClassDeclaration && debugMode)
+ Printf(stdout, "<membervariableHandler> %s %s\n", Getattr(n, "name"), Getattr(n, "type"));
+
+ processing_member_access_function = 0;
+ member_name = NULL;
+
+ return status;
+}
+
+
+/*
+ This doesn't seem to get used so leave it out for the moment.
+*/
+String * R::runtimeCode() {
+ String *s = Swig_include_sys("rrun.swg");
+ if (!s) {
+ Printf(stdout, "*** Unable to open 'rrun.swg'\n");
+ s = NewString("");
+ }
+ return s;
+}
+
+/*----------------------------------------------------------------------
+ * replaceSpecialVariables()
+ *--------------------------------------------------------------------*/
+
+void R::replaceSpecialVariables(String *method, String *tm, Parm *parm) {
+ (void)method;
+ SwigType *type = Getattr(parm, "type");
+ replaceRClass(tm, type);
+}
+
+
+/* -----------------------------------------------------------------------
+ * Called when SWIG wants to initialize this
+ * We initialize anythin we want here.
+ * Most importantly, tell SWIG where to find the files (e.g. r.swg) for this module.
+ * Use Swig_mark_arg() to tell SWIG that it is understood and not to
+ * throw an error.
+ * --------------------------------------------------------------*/
+
+void R::main(int argc, char *argv[]) {
+ init();
+ Preprocessor_define("SWIGR 1", 0);
+ SWIG_library_directory("r");
+ SWIG_config_file("r.swg");
+ debugMode = false;
+ copyStruct = true;
+ memoryProfile = false;
+ aggressiveGc = false;
+ inCPlusMode = false;
+ outputNamespaceInfo = false;
+ noInitializationCode = false;
+
+ this->Argc = argc;
+ this->Argv = argv;
+
+ allow_overloading();// can we support this?
+
+ for(int i = 0; i < argc; i++) {
+ if(strcmp(argv[i], "-package") == 0) {
+ Swig_mark_arg(i);
+ i++;
+ Swig_mark_arg(i);
+ Rpackage = argv[i];
+ } else if(strcmp(argv[i], "-dll") == 0) {
+ Swig_mark_arg(i);
+ i++;
+ Swig_mark_arg(i);
+ DllName = argv[i];
+ } else if(strcmp(argv[i], "-help") == 0) {
+ showUsage();
+ } else if(strcmp(argv[i], "-namespace") == 0) {
+ outputNamespaceInfo = true;
+ Swig_mark_arg(i);
+ } else if(!strcmp(argv[i], "-no-init-code")) {
+ noInitializationCode = true;
+ Swig_mark_arg(i);
+ } else if(!strcmp(argv[i], "-c++")) {
+ inCPlusMode = true;
+ Swig_mark_arg(i);
+ Printf(s_classes, "setClass('C++Reference', contains = 'ExternalReference')\n");
+ } else if(!strcmp(argv[i], "-debug")) {
+ debugMode = true;
+ Swig_mark_arg(i);
+ } else if (!strcmp(argv[i],"-copystruct")) {
+ copyStruct = true;
+ Swig_mark_arg(i);
+ } else if (!strcmp(argv[i], "-nocopystruct")) {
+ copyStruct = false;
+ Swig_mark_arg(i);
+ } else if (!strcmp(argv[i], "-memoryprof")) {
+ memoryProfile = true;
+ Swig_mark_arg(i);
+ } else if (!strcmp(argv[i], "-nomemoryprof")) {
+ memoryProfile = false;
+ Swig_mark_arg(i);
+ } else if (!strcmp(argv[i], "-aggressivegc")) {
+ aggressiveGc = true;
+ Swig_mark_arg(i);
+ } else if (!strcmp(argv[i], "-noaggressivegc")) {
+ aggressiveGc = false;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-cppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is now always on.\n", argv[i]);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nocppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is no longer supported.\n", argv[i]);
+ Swig_mark_arg(i);
+ Exit(EXIT_FAILURE);
+ }
+
+ if (debugMode) {
+ Swig_typemap_search_debug_set();
+ Swig_typemap_used_debug_set();
+ Swig_typemap_register_debug_set();
+ Swig_file_debug_set();
+ }
+ /// copyToR copyToC functions.
+
+ }
+}
+
+/* -----------------------------------------------------------------------
+ * Could make this work for String or File and then just store the resulting string
+ * rather than the collection of arguments and argc.
+ * ----------------------------------------------------------------------- */
+int R::outputCommandLineArguments(File *out)
+{
+ if(Argc < 1 || !Argv || !Argv[0])
+ return(-1);
+
+ Printf(out, "\n## Generated via the command line invocation:\n##\t");
+ for(int i = 0; i < Argc ; i++) {
+ Printf(out, " %s", Argv[i]);
+ }
+ Printf(out, "\n\n\n");
+
+ return Argc;
+}
+
+
+
+/* How SWIG instantiates an object from this module.
+ See swigmain.cxx */
+extern "C"
+Language *swig_r(void) {
+ return new R();
+}
+
+
+
+
+/* -----------------------------------------------------------------------
+ * Needs to be reworked.
+ *----------------------------------------------------------------------- */
+String * R::processType(SwigType *t, Node *n, int *nargs) {
+ //XXX Need to handle typedefs, e.g.
+ // a type which is a typedef to a function pointer.
+
+ SwigType *tmp = Getattr(n, "tdname");
+ if (debugMode)
+ Printf(stdout, "processType %s (tdname = %s)(SwigType = %s)\n", Getattr(n, "name"), tmp, Copy(t));
+
+ SwigType *td = t;
+ if (expandTypedef(t) &&
+ SwigType_istypedef(t)) {
+ SwigType *resolved =
+ SwigType_typedef_resolve_all(t);
+ if (expandTypedef(resolved)) {
+ td = Copy(resolved);
+ }
+ }
+
+ if(!td) {
+ int count = 0;
+ String *b = getRTypeName(t, &count);
+ if(count && b && !Getattr(SClassDefs, b)) {
+ if (debugMode)
+ Printf(stdout, "<processType> Defining class %s\n", b);
+
+ Printf(s_classes, "setClass('%s', contains = 'ExternalReference')\n", b);
+ Setattr(SClassDefs, b, b);
+ }
+
+ }
+
+
+ if(td)
+ t = td;
+
+ if(SwigType_isfunctionpointer(t)) {
+ if (debugMode)
+ Printf(stdout,
+ "<processType> Defining pointer handler %s\n", t);
+
+ String *tmp = createFunctionPointerHandler(t, n, nargs);
+ return tmp;
+ }
+
+ return NULL;
+}
+
+
+/* -----------------------------------------------------------------------
+ * enumValue()
+ * This method will return a string with an enum value to use in from R when
+ * setting up an enum variable
+ * ------------------------------------------------------------------------ */
+
+String *R::enumValue(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ String *value = Getattr(n, "value");
+ String *newsymname = 0;
+
+ Node *parent = parentNode(n);
+ symname = Getattr(n, "sym:name");
+
+ // parent enumtype has namespace mangled in
+ String *etype = Getattr(parent, "enumtype");
+ // we have to directly call the c wrapper function, as the
+ // R wrapper to the enum is designed to be used after the enum
+ // structures have been created on the R side. This means
+ // that we'll need to construct a .Call expression
+
+ // change the type for variableWrapper
+ if (debugMode) {
+ Printf(stdout, "<enumValue> type set: %s\n", etype);
+ }
+
+ Setattr(n, "type", etype);
+
+ if (!getCurrentClass()) {
+ newsymname = Swig_name_member(0, Getattr(parent, "sym:name"), symname);
+ // Strange hack to change the name
+ Setattr(n, "name", Getattr(n, "value"));
+ Setattr(n, "sym:name", newsymname);
+ variableWrapper(n);
+ value = Swig_name_get(NSPACE_TODO, newsymname);
+ } else {
+ String *enumClassPrefix = getEnumClassPrefix();
+ newsymname = Swig_name_member(0, enumClassPrefix, symname);
+ Setattr(n, "name", Getattr(n, "value"));
+ Setattr(n, "sym:name", newsymname);
+ variableWrapper(n);
+ value = Swig_name_get(NSPACE_TODO, newsymname);
+ }
+ value = Swig_name_wrapper(value);
+ Replace(value, "_wrap", "R_swig", DOH_REPLACE_FIRST);
+
+ String *valuecall=NewString("");
+ Printv(valuecall, ".Call('", value, "',FALSE, PACKAGE='", Rpackage, "')", NIL);
+ Delete(value);
+ return valuecall;
+}
diff --git a/contrib/tools/swig/Source/Modules/ruby.cxx b/contrib/tools/swig/Source/Modules/ruby.cxx
new file mode 100644
index 0000000000..0208435f03
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/ruby.cxx
@@ -0,0 +1,3470 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * ruby.cxx
+ *
+ * Ruby language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+#include <ctype.h>
+#include <string.h>
+#include <limits.h> /* for INT_MAX */
+
+#define SWIG_PROTECTED_TARGET_METHODS 1
+
+class RClass {
+private:
+ String *temp;
+
+public:
+ String *name; /* class name (renamed) */
+ String *cname; /* original C class/struct name */
+ String *mname; /* Mangled name */
+
+ /**
+ * The C variable name used in the SWIG-generated wrapper code to refer to
+ * this class; usually it is of the form "SwigClassXXX.klass", where SwigClassXXX
+ * is a swig_class struct instance and klass is a member of that struct.
+ */
+ String *vname;
+
+ /**
+ * The C variable name used in the SWIG-generated wrapper code to refer to
+ * the module that implements this class's methods (when we're trying to
+ * support C++ multiple inheritance). Usually it is of the form
+ * "SwigClassClassName.mImpl", where SwigClassXXX is a swig_class struct instance
+ * and mImpl is a member of that struct.
+ */
+ String *mImpl;
+
+ String *type;
+ String *prefix;
+ String *init;
+
+
+ int constructor_defined;
+ int destructor_defined;
+
+ RClass() {
+ temp = NewString("");
+ name = NewString("");
+ cname = NewString("");
+ mname = NewString("");
+ vname = NewString("");
+ mImpl = NewString("");
+ type = NewString("");
+ prefix = NewString("");
+ init = NewString("");
+ constructor_defined = 0;
+ destructor_defined = 0;
+ }
+
+ ~RClass() {
+ Delete(name);
+ Delete(cname);
+ Delete(vname);
+ Delete(mImpl);
+ Delete(mname);
+ Delete(type);
+ Delete(prefix);
+ Delete(init);
+ Delete(temp);
+ }
+
+ void set_name(const_String_or_char_ptr cn, const_String_or_char_ptr rn, const_String_or_char_ptr valn) {
+ /* Original C/C++ class (or struct) name */
+ Clear(cname);
+ Append(cname, cn);
+
+ /* Mangled name */
+ Delete(mname);
+ mname = Swig_name_mangle(cname);
+
+ /* Renamed class name */
+ Clear(name);
+ Append(name, valn);
+
+ /* Variable name for the VALUE that refers to the Ruby Class object */
+ Clear(vname);
+ Printf(vname, "SwigClass%s.klass", name);
+
+ /* Variable name for the VALUE that refers to the Ruby Class object */
+ Clear(mImpl);
+ Printf(mImpl, "SwigClass%s.mImpl", name);
+
+ /* Prefix */
+ Clear(prefix);
+ Printv(prefix, (rn ? rn : cn), "_", NIL);
+ }
+
+ char *strip(const_String_or_char_ptr s) {
+ Clear(temp);
+ if (Strncmp(s, prefix, Len(prefix)) == 0) {
+ Append(temp, Char(s) + Len(prefix));
+ } else {
+ Append(temp, s);
+ }
+ return Char(temp);
+ }
+};
+
+
+/* flags for the make_autodoc function */
+namespace {
+enum autodoc_t {
+ AUTODOC_CLASS,
+ AUTODOC_CTOR,
+ AUTODOC_DTOR,
+ AUTODOC_STATICFUNC,
+ AUTODOC_FUNC,
+ AUTODOC_METHOD,
+ AUTODOC_GETTER,
+ AUTODOC_SETTER,
+ AUTODOC_NONE
+};
+}
+
+static const char *usage = "\
+Ruby Options (available with -ruby)\n\
+ -autorename - Enable renaming of classes and methods to follow Ruby coding standards\n\
+ -globalmodule - Wrap everything into the global module\n\
+ -initname <name>- Set entry function to Init_<name> (used by `require')\n\
+ -minherit - Attempt to support multiple inheritance\n\
+ -noautorename - Disable renaming of classes and methods (default)\n\
+ -prefix <name> - Set a prefix <name> to be prepended to all names\n\
+";
+
+
+#define RCLASS(hash, name) (RClass*)(Getattr(hash, name) ? Data(Getattr(hash, name)) : 0)
+#define SET_RCLASS(hash, name, klass) Setattr(hash, name, NewVoid(klass, 0))
+
+
+class RUBY:public Language {
+private:
+
+ String *module;
+ String *modvar;
+ String *feature;
+ String *prefix;
+ int current;
+ Hash *classes; /* key=cname val=RClass */
+ RClass *klass; /* Currently processing class */
+ Hash *special_methods; /* Python style special method name table */
+
+ File *f_directors;
+ File *f_directors_h;
+ File *f_directors_helpers;
+ File *f_begin;
+ File *f_runtime;
+ File *f_runtime_h;
+ File *f_header;
+ File *f_wrappers;
+ File *f_init;
+ File *f_initbeforefunc;
+
+ bool useGlobalModule;
+ bool multipleInheritance;
+
+ // Wrap modes
+ enum WrapperMode {
+ NO_CPP,
+ MEMBER_FUNC,
+ CONSTRUCTOR_ALLOCATE,
+ CONSTRUCTOR_INITIALIZE,
+ DESTRUCTOR,
+ MEMBER_VAR,
+ CLASS_CONST,
+ STATIC_FUNC,
+ STATIC_VAR
+ };
+
+ /* ------------------------------------------------------------
+ * autodoc level declarations
+ * ------------------------------------------------------------ */
+
+ enum autodoc_l {
+ NO_AUTODOC = -2, // no autodoc
+ STRING_AUTODOC = -1, // use provided string
+ NAMES_AUTODOC = 0, // only parameter names
+ TYPES_AUTODOC = 1, // parameter names and types
+ EXTEND_AUTODOC = 2, // extended documentation and parameter names
+ EXTEND_TYPES_AUTODOC = 3 // extended documentation and parameter types + names
+ };
+
+ autodoc_t last_mode;
+ String* last_autodoc;
+
+ autodoc_l autodoc_level(String *autodoc) {
+ autodoc_l dlevel = NO_AUTODOC;
+ char *c = Char(autodoc);
+ if (c) {
+ if (isdigit(c[0])) {
+ dlevel = (autodoc_l) atoi(c);
+ } else {
+ if (strcmp(c, "extended") == 0) {
+ dlevel = EXTEND_AUTODOC;
+ } else {
+ dlevel = STRING_AUTODOC;
+ }
+ }
+ }
+ return dlevel;
+ }
+
+
+
+ /* ------------------------------------------------------------
+ * have_docstring()
+ * Check if there is a docstring directive and it has text,
+ * or there is an autodoc flag set
+ * ------------------------------------------------------------ */
+
+ bool have_docstring(Node *n) {
+ String *str = Getattr(n, "feature:docstring");
+ return (str && Len(str) > 0) || (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"));
+ }
+
+ /* ------------------------------------------------------------
+ * docstring()
+ * Get the docstring text, stripping off {} if necessary,
+ * and enclose in triple double quotes. If autodoc is also
+ * set then it will build a combined docstring.
+ * ------------------------------------------------------------ */
+
+ String *docstring(Node *n, autodoc_t ad_type) {
+
+ String *str = Getattr(n, "feature:docstring");
+ bool have_ds = (str && Len(str) > 0);
+ bool have_auto = (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"));
+ String *autodoc = NULL;
+ String *doc = NULL;
+
+ if (have_ds) {
+ char *t = Char(str);
+ if (*t == '{') {
+ Delitem(str, 0);
+ Delitem(str, DOH_END);
+ }
+ }
+
+ if (have_auto) {
+ autodoc = make_autodoc(n, ad_type);
+ have_auto = (autodoc && Len(autodoc) > 0);
+ }
+
+ if (have_auto || have_ds)
+ doc = NewString("/*");
+
+ if (have_auto && have_ds) { // Both autodoc and docstring are present
+ Printv(doc, "\n", autodoc, "\n", str, "\n", NIL);
+ } else if (!have_auto && have_ds) { // only docstring
+ Printv(doc, str, NIL);
+ } else if (have_auto && !have_ds) { // only autodoc
+ Printv(doc, "\n", autodoc, "\n", NIL);
+ } else {
+ doc = NewString("");
+ }
+
+ if (have_auto || have_ds)
+ Append(doc, "*/\n");
+
+ // Save the generated strings in the parse tree in case they are used later
+ // by post processing tools
+ Setattr(n, "ruby:docstring", doc);
+ Setattr(n, "ruby:autodoc", autodoc);
+ return doc;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * addMissingParameterNames()
+ * For functions that have not had nameless parameters set in the Language class.
+ *
+ * Inputs:
+ * plist - entire parameter list
+ * arg_offset - argument number for first parameter
+ * Side effects:
+ * The "lname" attribute in each parameter in plist will be contain a parameter name
+ * ----------------------------------------------------------------------------- */
+
+ void addMissingParameterNames(Node* n, ParmList *plist, int arg_offset) {
+ Parm *p = plist;
+ int i = arg_offset;
+ while (p) {
+ if (!Getattr(p, "lname")) {
+ String *name = makeParameterName(n, p, i);
+ Setattr(p, "lname", name);
+ Delete(name);
+ }
+ i++;
+ p = nextSibling(p);
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * make_autodocParmList()
+ * Generate the documentation for the function parameters
+ * ------------------------------------------------------------ */
+
+ String *make_autodocParmList(Node *n, bool showTypes) {
+ String *doc = NewString("");
+ String *pdocs = 0;
+ ParmList *plist = CopyParmList(Getattr(n, "parms"));
+ Parm *p;
+ Parm *pnext;
+ int lines = 0;
+ int arg_num = is_wrapping_class() ? 1 : 0;
+ const int maxwidth = 80;
+
+ addMissingParameterNames(n, plist, arg_num); // for $1_name substitutions done in Swig_typemap_attach_parms
+
+ Swig_typemap_attach_parms("in", plist, 0);
+ Swig_typemap_attach_parms("doc", plist, 0);
+
+ if (Strcmp(ParmList_protostr(plist), "void") == 0) {
+ //No parameters actually
+ return doc;
+ }
+
+ for (p = plist; p; p = pnext, arg_num++) {
+
+ String *tm = Getattr(p, "tmap:in");
+ if (tm) {
+ pnext = Getattr(p, "tmap:in:next");
+ if (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ continue;
+ }
+ } else {
+ pnext = nextSibling(p);
+ }
+
+ String *name = 0;
+ String *type = 0;
+ String *value = 0;
+ String *pdoc = Getattr(p, "tmap:doc");
+ if (pdoc) {
+ name = Getattr(p, "tmap:doc:name");
+ type = Getattr(p, "tmap:doc:type");
+ value = Getattr(p, "tmap:doc:value");
+ }
+
+ // Note: the generated name should be consistent with that in kwnames[]
+ String *made_name = 0;
+ if (!name) {
+ name = made_name = makeParameterName(n, p, arg_num);
+ }
+
+ type = type ? type : Getattr(p, "type");
+ value = value ? value : Getattr(p, "value");
+
+ if (SwigType_isvarargs(type))
+ break;
+
+ // Skip the 'self' parameter which in ruby is implicit
+ if ( Cmp(name, "self") == 0 )
+ continue;
+
+ // Make __p parameters just p (as used in STL)
+ Replace( name, "__", "", DOH_REPLACE_FIRST );
+
+ if (Len(doc)) {
+ // add a comma to the previous one if any
+ Append(doc, ", ");
+
+ // Do we need to wrap a long line?
+ if ((Len(doc) - lines * maxwidth) > maxwidth) {
+ Printf(doc, "\n%s", tab4);
+ lines += 1;
+ }
+ }
+
+ // Do the param type too?
+ Node *nn = classLookup(Getattr(p, "type"));
+ String *type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
+ if (showTypes)
+ Printf(doc, "%s ", type_str);
+
+ Append(doc, name);
+ if (pdoc) {
+ if (!pdocs)
+ pdocs = NewString("Parameters:\n");
+ Printf(pdocs, " %s.\n", pdoc);
+ }
+
+ if (value) {
+ String *new_value = convertValue(value, Getattr(p, "type"));
+ if (new_value) {
+ value = new_value;
+ } else {
+ Node *lookup = Swig_symbol_clookup(value, 0);
+ if (lookup)
+ value = Getattr(lookup, "sym:name");
+ }
+ Printf(doc, "=%s", value);
+ }
+ Delete(type_str);
+ Delete(made_name);
+ }
+ if (pdocs)
+ Setattr(n, "feature:pdocs", pdocs);
+ Delete(plist);
+ return doc;
+ }
+
+ /* ------------------------------------------------------------
+ * make_autodoc()
+ * Build a docstring for the node, using parameter and other
+ * info in the parse tree. If the value of the autodoc
+ * attribute is "0" then do not include parameter types, if
+ * it is "1" (the default) then do. If it has some other
+ * value then assume it is supplied by the extension writer
+ * and use it directly.
+ * ------------------------------------------------------------ */
+
+ String *make_autodoc(Node *n, autodoc_t ad_type) {
+ int extended = 0;
+ // If the function is overloaded then this function is called
+ // for the last one. Rewind to the first so the docstrings are
+ // in order.
+ while (Getattr(n, "sym:previousSibling"))
+ n = Getattr(n, "sym:previousSibling");
+
+ Node *pn = Swig_methodclass(n);
+ String* super_names = NewString("");
+ String* class_name = Getattr(pn, "sym:name") ;
+
+ if ( !class_name ) {
+ class_name = NewString("");
+ } else {
+ class_name = Copy(class_name);
+ List *baselist = Getattr(pn, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator base = First(baselist);
+ while (base.item && GetFlag(base.item, "feature:ignore")) {
+ base = Next(base);
+ }
+
+ int count = 0;
+ for ( ;base.item; ++count) {
+ if ( count ) Append(super_names, ", ");
+ String *basename = Getattr(base.item, "sym:name");
+
+ String* basenamestr = NewString(basename);
+ Node* parent = parentNode(base.item);
+ while (parent)
+ {
+ String *parent_name = Copy( Getattr(parent, "sym:name") );
+ if ( !parent_name ) {
+ Node* mod = Getattr(parent, "module");
+ if ( mod )
+ parent_name = Copy( Getattr(mod, "name") );
+ if ( parent_name )
+ (Char(parent_name))[0] = (char)toupper((Char(parent_name))[0]);
+ }
+ if ( parent_name ) {
+ Insert(basenamestr, 0, "::");
+ Insert(basenamestr, 0, parent_name);
+ Delete(parent_name);
+ }
+ parent = parentNode(parent);
+ }
+
+ Append(super_names, basenamestr );
+ Delete(basenamestr);
+ base = Next(base);
+ }
+ }
+ }
+ String* full_name;
+ if ( module ) {
+ full_name = NewString(module);
+ if (Len(class_name) > 0)
+ Append(full_name, "::");
+ }
+ else
+ full_name = NewString("");
+ Append(full_name, class_name);
+
+ String* symname = Getattr(n, "sym:name");
+ if ( Getattr( special_methods, symname ) )
+ symname = Getattr( special_methods, symname );
+
+ String* methodName = NewString(full_name);
+ Append(methodName, symname);
+
+
+ // Each overloaded function will try to get documented,
+ // so we keep the name of the last overloaded function and its type.
+ // Documenting just from functionWrapper() is not possible as
+ // sym:name has already been changed to include the class name
+ if ( last_mode == ad_type && Cmp(methodName, last_autodoc) == 0 ) {
+ Delete(full_name);
+ Delete(class_name);
+ Delete(super_names);
+ Delete(methodName);
+ return NewString("");
+ }
+
+
+ last_mode = ad_type;
+ last_autodoc = Copy(methodName);
+
+ String *doc = NewString("");
+ int counter = 0;
+ bool skipAuto = false;
+ Node* on = n;
+ for ( ; n; ++counter ) {
+ String *type_str = NULL;
+ skipAuto = false;
+ bool showTypes = false;
+ String *autodoc = Getattr(n, "feature:autodoc");
+ autodoc_l dlevel = autodoc_level(autodoc);
+ switch (dlevel) {
+ case NO_AUTODOC:
+ break;
+ case NAMES_AUTODOC:
+ showTypes = false;
+ break;
+ case TYPES_AUTODOC:
+ showTypes = true;
+ break;
+ case EXTEND_AUTODOC:
+ extended = 1;
+ showTypes = false;
+ break;
+ case EXTEND_TYPES_AUTODOC:
+ extended = 1;
+ showTypes = true;
+ break;
+ case STRING_AUTODOC:
+ skipAuto = true;
+ break;
+ }
+
+ SwigType *type = Getattr(n, "type");
+
+ if (type) {
+ if (Strcmp(type, "void") == 0) {
+ type_str = NULL;
+ } else {
+ SwigType *qt = SwigType_typedef_resolve_all(type);
+ if (SwigType_isenum(qt)) {
+ type_str = NewString("int");
+ } else {
+ Node *nn = classLookup(type);
+ type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
+ }
+ }
+ }
+
+ if (counter == 0) {
+ switch (ad_type) {
+ case AUTODOC_CLASS:
+ Printf(doc, " Document-class: %s", full_name);
+ if ( Len(super_names) > 0 )
+ Printf( doc, " < %s", super_names);
+ Append(doc, "\n\n");
+ break;
+ case AUTODOC_CTOR:
+ Printf(doc, " Document-method: %s.new\n\n", full_name);
+ break;
+
+ case AUTODOC_DTOR:
+ break;
+
+ case AUTODOC_STATICFUNC:
+ Printf(doc, " Document-method: %s.%s\n\n", full_name, symname);
+ break;
+
+ case AUTODOC_FUNC:
+ case AUTODOC_METHOD:
+ case AUTODOC_GETTER:
+ Printf(doc, " Document-method: %s.%s\n\n", full_name, symname);
+ break;
+ case AUTODOC_SETTER:
+ Printf(doc, " Document-method: %s.%s=\n\n", full_name, symname);
+ break;
+ case AUTODOC_NONE:
+ break;
+ }
+ }
+
+ if (skipAuto) {
+ if ( counter == 0 ) Printf(doc, " call-seq:\n");
+ switch( ad_type )
+ {
+ case AUTODOC_STATICFUNC:
+ case AUTODOC_FUNC:
+ case AUTODOC_METHOD:
+ case AUTODOC_GETTER:
+ {
+ String *paramList = make_autodocParmList(n, showTypes);
+ if (Len(paramList))
+ Printf(doc, " %s(%s)", symname, paramList);
+ else
+ Printf(doc, " %s", symname);
+ if (type_str)
+ Printf(doc, " -> %s", type_str);
+ break;
+ }
+ case AUTODOC_SETTER:
+ {
+ Printf(doc, " %s=(x)", symname);
+ if (type_str)
+ Printf(doc, " -> %s", type_str);
+ break;
+ }
+ default:
+ break;
+ }
+ } else {
+ switch (ad_type) {
+ case AUTODOC_CLASS:
+ {
+ // Only do the autodoc if there isn't a docstring for the class
+ String *str = Getattr(n, "feature:docstring");
+ if (counter == 0 && (str == 0 || Len(str) == 0)) {
+ if (CPlusPlus) {
+ Printf(doc, " Proxy of C++ %s class", full_name);
+ } else {
+ Printf(doc, " Proxy of C %s struct", full_name);
+ }
+ }
+ }
+ break;
+ case AUTODOC_CTOR:
+ if (counter == 0)
+ Printf(doc, " call-seq:\n");
+ if (Strcmp(class_name, symname) == 0) {
+ String *paramList = make_autodocParmList(n, showTypes);
+ if (Len(paramList))
+ Printf(doc, " %s.new(%s)", class_name, paramList);
+ else
+ Printf(doc, " %s.new", class_name);
+ } else {
+ Printf(doc, " %s.new(%s)", class_name, make_autodocParmList(n, showTypes));
+ }
+ break;
+
+ case AUTODOC_DTOR:
+ break;
+
+ case AUTODOC_STATICFUNC:
+ case AUTODOC_FUNC:
+ case AUTODOC_METHOD:
+ case AUTODOC_GETTER:
+ {
+ if (counter == 0)
+ Printf(doc, " call-seq:\n");
+ String *paramList = make_autodocParmList(n, showTypes);
+ if (Len(paramList))
+ Printf(doc, " %s(%s)", symname, paramList);
+ else
+ Printf(doc, " %s", symname);
+ if (type_str)
+ Printf(doc, " -> %s", type_str);
+ break;
+ }
+ case AUTODOC_SETTER:
+ {
+ Printf(doc, " call-seq:\n");
+ Printf(doc, " %s=(x)", symname);
+ if (type_str)
+ Printf(doc, " -> %s", type_str);
+ break;
+ }
+ case AUTODOC_NONE:
+ break;
+ }
+ }
+
+ // if it's overloaded then get the next decl and loop around again
+ n = Getattr(n, "sym:nextSibling");
+ if (n)
+ Append(doc, "\n");
+ Delete(type_str);
+ }
+
+ Printf(doc, "\n\n");
+ if (!skipAuto) {
+ switch (ad_type) {
+ case AUTODOC_CLASS:
+ case AUTODOC_DTOR:
+ break;
+ case AUTODOC_CTOR:
+ Printf(doc, "Class constructor.\n");
+ break;
+ case AUTODOC_STATICFUNC:
+ Printf(doc, "A class method.\n");
+ break;
+ case AUTODOC_FUNC:
+ Printf(doc, "A module function.\n");
+ break;
+ case AUTODOC_METHOD:
+ Printf(doc, "An instance method.\n");
+ break;
+ case AUTODOC_GETTER:
+ Printf(doc, "Get value of attribute.\n");
+ break;
+ case AUTODOC_SETTER:
+ Printf(doc, "Set new value for attribute.\n");
+ break;
+ case AUTODOC_NONE:
+ break;
+ }
+ }
+
+
+ n = on;
+ while ( n ) {
+ String *autodoc = Getattr(n, "feature:autodoc");
+ autodoc_l dlevel = autodoc_level(autodoc);
+
+ switch (dlevel) {
+ case NO_AUTODOC:
+ case NAMES_AUTODOC:
+ case TYPES_AUTODOC:
+ extended = 0;
+ break;
+ case STRING_AUTODOC:
+ extended = 2;
+ Replaceall( autodoc, "$class", class_name );
+ Printv(doc, autodoc, ".", NIL);
+ break;
+ case EXTEND_AUTODOC:
+ case EXTEND_TYPES_AUTODOC:
+ extended = 1;
+ break;
+ }
+
+
+ if (extended) {
+ String *pdocs = Getattr(n, "feature:pdocs");
+ if (pdocs) {
+ Printv(doc, "\n\n", pdocs, NULL);
+ break;
+ }
+ if ( extended == 2 ) break;
+ }
+ n = Getattr(n, "sym:nextSibling");
+ }
+
+ Delete(full_name);
+ Delete(class_name);
+ Delete(super_names);
+ Delete(methodName);
+
+ return doc;
+ }
+
+ /* ------------------------------------------------------------
+ * convertValue()
+ * Check if string v can be a Ruby value literal,
+ * (eg. number or string), or translate it to a Ruby literal.
+ * ------------------------------------------------------------ */
+ String *convertValue(String *v, SwigType *t) {
+ if (v && Len(v) > 0) {
+ char fc = (Char(v))[0];
+ if (('0' <= fc && fc <= '9') || '\'' == fc || '"' == fc) {
+ /* number or string (or maybe NULL pointer) */
+ if (SwigType_ispointer(t) && Strcmp(v, "0") == 0)
+ return NewString("None");
+ else
+ return v;
+ }
+ if (Strcmp(v, "NULL") == 0 || Strcmp(v, "nullptr") == 0)
+ return SwigType_ispointer(t) ? NewString("nil") : NewString("0");
+ if (Strcmp(v, "true") == 0 || Strcmp(v, "TRUE") == 0)
+ return NewString("True");
+ if (Strcmp(v, "false") == 0 || Strcmp(v, "FALSE") == 0)
+ return NewString("False");
+ }
+ return 0;
+ }
+
+public:
+
+ /* ---------------------------------------------------------------------
+ * RUBY()
+ *
+ * Initialize member data
+ * --------------------------------------------------------------------- */
+ RUBY() :
+ module(0),
+ modvar(0),
+ feature(0),
+ prefix(0),
+ current(0),
+ classes(0),
+ klass(0),
+ special_methods(0),
+ f_directors(0),
+ f_directors_h(0),
+ f_directors_helpers(0),
+ f_begin(0),
+ f_runtime(0),
+ f_runtime_h(0),
+ f_header(0),
+ f_wrappers(0),
+ f_init(0),
+ f_initbeforefunc(0),
+ useGlobalModule(false),
+ multipleInheritance(false),
+ last_mode(AUTODOC_NONE),
+ last_autodoc(NewString("")) {
+ current = NO_CPP;
+ director_prot_ctor_code = NewString("");
+ Printv(director_prot_ctor_code,
+ "if ( $comparison ) { /* subclassed */\n",
+ " $director_new \n",
+ "} else {\n", " rb_raise(rb_eRuntimeError,\"accessing abstract class or protected constructor\"); \n", " return Qnil;\n", "}\n", NIL);
+ director_multiple_inheritance = 0;
+ director_language = 1;
+ }
+
+ /* ---------------------------------------------------------------------
+ * main()
+ *
+ * Parse command line options and initializes variables.
+ * --------------------------------------------------------------------- */
+
+ virtual void main(int argc, char *argv[]) {
+
+ int autorename = 0;
+
+ /* Set location of SWIG library */
+ SWIG_library_directory("ruby");
+
+ /* Look for certain command line options */
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-initname") == 0) {
+ if (argv[i + 1]) {
+ char *name = argv[i + 1];
+ feature = NewString(name);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ }
+ else if (strcmp(argv[i], "-feature") == 0) {
+ fprintf( stderr, "Warning: Ruby -feature option is deprecated, "
+ "please use -initname instead.\n");
+ if (argv[i + 1]) {
+ char *name = argv[i + 1];
+ feature = NewString(name);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-globalmodule") == 0) {
+ useGlobalModule = true;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-minherit") == 0) {
+ multipleInheritance = true;
+ director_multiple_inheritance = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-autorename") == 0) {
+ autorename = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-noautorename") == 0) {
+ autorename = 0;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-prefix") == 0) {
+ if (argv[i + 1]) {
+ char *name = argv[i + 1];
+ prefix = NewString(name);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else {
+ Swig_arg_error();
+ }
+ } else if (strcmp(argv[i], "-help") == 0) {
+ Printf(stdout, "%s\n", usage);
+ } else if (strcmp(argv[i], "-cppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is now always on.\n", argv[i]);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nocppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is no longer supported.\n", argv[i]);
+ Swig_mark_arg(i);
+ Exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (autorename) {
+ /* Turn on the autorename mode */
+ Preprocessor_define((DOH *) "SWIG_RUBY_AUTORENAME", 0);
+ }
+
+ /* Add a symbol to the parser for conditional compilation */
+ Preprocessor_define("SWIGRUBY 1", 0);
+
+ /* Add typemap definitions */
+ SWIG_typemap_lang("ruby");
+ SWIG_config_file("ruby.swg");
+ allow_overloading();
+ }
+
+ /**
+ * Generate initialization code to define the Ruby module(s),
+ * accounting for nested modules as necessary.
+ */
+ void defineRubyModule() {
+ List *modules = Split(module, ':', INT_MAX);
+ if (modules != 0 && Len(modules) > 0) {
+ String *mv = 0;
+ Iterator m;
+ m = First(modules);
+ while (m.item) {
+ if (Len(m.item) > 0) {
+ if (mv != 0) {
+ Printv(f_init, tab4, modvar, " = rb_define_module_under(", modvar, ", \"", m.item, "\");\n", NIL);
+ } else {
+ Printv(f_init, tab4, modvar, " = rb_define_module(\"", m.item, "\");\n", NIL);
+ mv = NewString(modvar);
+ }
+ }
+ m = Next(m);
+ }
+ Delete(mv);
+ Delete(modules);
+ }
+ }
+
+ void registerMagicMethods() {
+
+ special_methods = NewHash();
+
+ /* Python->Ruby style special method name. */
+ /* Basic */
+ Setattr(special_methods, "__repr__", "inspect");
+ Setattr(special_methods, "__str__", "to_s");
+ Setattr(special_methods, "__cmp__", "<=>");
+ Setattr(special_methods, "__hash__", "hash");
+ Setattr(special_methods, "__nonzero__", "nonzero?");
+
+ /* Callable */
+ Setattr(special_methods, "__call__", "call");
+
+ /* Collection */
+ Setattr(special_methods, "__len__", "length");
+ Setattr(special_methods, "__getitem__", "[]");
+ Setattr(special_methods, "__setitem__", "[]=");
+
+ /* Operators */
+ Setattr(special_methods, "__add__", "+");
+ Setattr(special_methods, "__pos__", "+@");
+ Setattr(special_methods, "__sub__", "-");
+ Setattr(special_methods, "__neg__", "-@");
+ Setattr(special_methods, "__mul__", "*");
+ Setattr(special_methods, "__div__", "/");
+ Setattr(special_methods, "__mod__", "%");
+ Setattr(special_methods, "__lshift__", "<<");
+ Setattr(special_methods, "__rshift__", ">>");
+ Setattr(special_methods, "__and__", "&");
+ Setattr(special_methods, "__or__", "|");
+ Setattr(special_methods, "__xor__", "^");
+ Setattr(special_methods, "__invert__", "~");
+ Setattr(special_methods, "__lt__", "<");
+ Setattr(special_methods, "__le__", "<=");
+ Setattr(special_methods, "__gt__", ">");
+ Setattr(special_methods, "__ge__", ">=");
+ Setattr(special_methods, "__eq__", "==");
+
+ /* Other numeric */
+ Setattr(special_methods, "__divmod__", "divmod");
+ Setattr(special_methods, "__pow__", "**");
+ Setattr(special_methods, "__abs__", "abs");
+ Setattr(special_methods, "__int__", "to_i");
+ Setattr(special_methods, "__float__", "to_f");
+ Setattr(special_methods, "__coerce__", "coerce");
+ }
+
+ /* ---------------------------------------------------------------------
+ * top()
+ * --------------------------------------------------------------------- */
+
+ virtual int top(Node *n) {
+
+ String *mod_docstring = NULL;
+
+ /**
+ * See if any Ruby module options have been specified as options
+ * to the %module directive.
+ */
+ Node *swigModule = Getattr(n, "module");
+ if (swigModule) {
+ Node *options = Getattr(swigModule, "options");
+ if (options) {
+ if (Getattr(options, "directors")) {
+ allow_directors();
+ }
+ if (Getattr(options, "dirprot")) {
+ allow_dirprot();
+ }
+ if (Getattr(options, "ruby_globalmodule")) {
+ useGlobalModule = true;
+ }
+ if (Getattr(options, "ruby_minherit")) {
+ multipleInheritance = true;
+ director_multiple_inheritance = 1;
+ }
+ mod_docstring = Getattr(options, "docstring");
+ }
+ }
+
+ /* Set comparison with none for ConstructorToFunction */
+
+
+ setSubclassInstanceCheck(NewStringf("strcmp(rb_obj_classname(self), classname) != 0"));
+ // setSubclassInstanceCheck(NewString("CLASS_OF(self) != cFoo.klass"));
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+ String *outfile_h = Getattr(n, "outfile_h");
+
+ if (!outfile) {
+ Printf(stderr, "Unable to determine outfile\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+ f_directors_h = NewString("");
+ f_directors = NewString("");
+ f_directors_helpers = NewString("");
+ f_initbeforefunc = NewString("");
+
+ if (directorsEnabled()) {
+ if (!outfile_h) {
+ Printf(stderr, "Unable to determine outfile_h\n");
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
+ if (!f_runtime_h) {
+ FileErrorDisplay(outfile_h);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+ Swig_register_filebyname("director", f_directors);
+ Swig_register_filebyname("director_h", f_directors_h);
+ Swig_register_filebyname("director_helpers", f_directors_helpers);
+ Swig_register_filebyname("initbeforefunc", f_initbeforefunc);
+
+ modvar = 0;
+ current = NO_CPP;
+ klass = 0;
+ classes = NewHash();
+
+ registerMagicMethods();
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "RUBY");
+
+ if (directorsEnabled()) {
+ Printf(f_runtime, "#define SWIG_DIRECTORS\n");
+ }
+
+ Printf(f_runtime, "\n");
+
+ /* typedef void *VALUE */
+ SwigType *value = NewSwigType(T_VOID);
+ SwigType_add_pointer(value);
+ SwigType_typedef(value, "VALUE");
+ Delete(value);
+
+ /* Set module name */
+ set_module(Char(Getattr(n, "name")));
+
+ if (directorsEnabled()) {
+ /* Build a version of the module name for use in a C macro name. */
+ String *module_macro = Copy(module);
+ Replaceall(module_macro, "::", "__");
+
+ Swig_banner(f_directors_h);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module_macro);
+ Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module_macro);
+ Printf(f_directors_h, "namespace Swig {\n");
+ Printf(f_directors_h, " class Director;\n");
+ Printf(f_directors_h, "}\n\n");
+
+ Printf(f_directors_helpers, "/* ---------------------------------------------------\n");
+ Printf(f_directors_helpers, " * C++ director class helpers\n");
+ Printf(f_directors_helpers, " * --------------------------------------------------- */\n\n");
+
+ Printf(f_directors, "\n\n");
+ Printf(f_directors, "/* ---------------------------------------------------\n");
+ Printf(f_directors, " * C++ director class methods\n");
+ Printf(f_directors, " * --------------------------------------------------- */\n\n");
+ if (outfile_h) {
+ String *filename = Swig_file_filename(outfile_h);
+ Printf(f_directors, "#include \"%s\"\n\n", filename);
+ Delete(filename);
+ }
+
+ Delete(module_macro);
+ }
+
+ Printf(f_header, "#define SWIG_init Init_%s\n", feature);
+ Printf(f_header, "#define SWIG_name \"%s\"\n\n", module);
+
+ if (mod_docstring) {
+ if (Len(mod_docstring)) {
+ Printf(f_header, "/*\n Document-module: %s\n\n%s\n*/\n", module, mod_docstring);
+ }
+ Delete(mod_docstring);
+ mod_docstring = NULL;
+ }
+
+ Printf(f_header, "static VALUE %s;\n", modvar);
+
+ /* Start generating the initialization function */
+ String* docs = docstring(n, AUTODOC_CLASS);
+ Printf(f_init, "/*\n%s\n*/", docs );
+ Printv(f_init, "\n", "#ifdef __cplusplus\n", "extern \"C\"\n", "#endif\n", "SWIGEXPORT void Init_", feature, "(void) {\n", "size_t i;\n", "\n", NIL);
+
+ Printv(f_init, tab4, "SWIG_InitRuntime();\n", NIL);
+
+ if (!useGlobalModule)
+ defineRubyModule();
+
+ Printv(f_init, "\n", "SWIG_InitializeModule(0);\n", "for (i = 0; i < swig_module.size; i++) {\n", "SWIG_define_class(swig_module.types[i]);\n", "}\n", NIL);
+ Printf(f_init, "\n");
+
+ /* Initialize code to keep track of objects */
+ Printf(f_init, "SWIG_RubyInitializeTrackings();\n");
+
+ Language::top(n);
+
+ if (directorsEnabled()) {
+ // Insert director runtime into the f_runtime file (make it occur before %header section)
+ Swig_insert_file("director_common.swg", f_runtime);
+ Swig_insert_file("director.swg", f_runtime);
+ }
+
+ /* Finish off our init function */
+ Printf(f_init, "}\n");
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+
+ /* Close all of the files */
+ Dump(f_runtime, f_begin);
+ Dump(f_header, f_begin);
+
+ if (directorsEnabled()) {
+ Dump(f_directors_helpers, f_begin);
+ Dump(f_directors, f_begin);
+ Dump(f_directors_h, f_runtime_h);
+ Printf(f_runtime_h, "\n");
+ Printf(f_runtime_h, "#endif\n");
+ Delete(f_runtime_h);
+ }
+
+ Dump(f_wrappers, f_begin);
+ Dump(f_initbeforefunc, f_begin);
+ Wrapper_pretty_print(f_init, f_begin);
+
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_initbeforefunc);
+ Delete(f_runtime);
+ Delete(f_begin);
+
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * importDirective()
+ * ----------------------------------------------------------------------------- */
+
+ virtual int importDirective(Node *n) {
+ String *modname = Getattr(n, "module");
+ if (modname) {
+ if (prefix) {
+ Insert(modname, 0, prefix);
+ }
+
+ List *modules = Split(modname, ':', INT_MAX);
+ if (modules && Len(modules) > 0) {
+ modname = NewString("");
+ String *last = NULL;
+ Iterator m = First(modules);
+ while (m.item) {
+ if (Len(m.item) > 0) {
+ if (last) {
+ Append(modname, "/");
+ }
+ Append(modname, m.item);
+ last = m.item;
+ }
+ m = Next(m);
+ }
+ Printf(f_init, "rb_require(\"%s\");\n", modname);
+ Delete(modname);
+ }
+ Delete(modules);
+ }
+ return Language::importDirective(n);
+ }
+
+ /* ---------------------------------------------------------------------
+ * set_module(const char *mod_name)
+ *
+ * Sets the module name. Does nothing if it's already set (so it can
+ * be overridden as a command line option).
+ *---------------------------------------------------------------------- */
+
+ void set_module(const char *s) {
+ String *mod_name = NewString(s);
+ if (module == 0) {
+ /* Start with the empty string */
+ module = NewString("");
+
+ if (prefix) {
+ Insert(mod_name, 0, prefix);
+ }
+
+ /* Account for nested modules */
+ List *modules = Split(mod_name, ':', INT_MAX);
+ if (modules != 0 && Len(modules) > 0) {
+ String *last = 0;
+ Iterator m = First(modules);
+ while (m.item) {
+ if (Len(m.item) > 0) {
+ String *cap = NewString(m.item);
+ (Char(cap))[0] = (char)toupper((Char(cap))[0]);
+ if (last != 0) {
+ Append(module, "::");
+ }
+ Append(module, cap);
+ last = m.item;
+ }
+ m = Next(m);
+ }
+ if (last) {
+ if (feature == 0) {
+ feature = Copy(last);
+ }
+ (Char(last))[0] = (char)toupper((Char(last))[0]);
+ modvar = NewStringf("m%s", last);
+ }
+ }
+ Delete(modules);
+ }
+ Delete(mod_name);
+ }
+
+ /* --------------------------------------------------------------------------
+ * nativeWrapper()
+ * -------------------------------------------------------------------------- */
+ virtual int nativeWrapper(Node *n) {
+ String *funcname = Getattr(n, "wrap:name");
+ Swig_warning(WARN_LANG_NATIVE_UNIMPL, input_file, line_number, "Adding native function %s not supported (ignored).\n", funcname);
+ return SWIG_NOWRAP;
+ }
+
+ /**
+ * Process the comma-separated list of aliases (if any).
+ */
+ void defineAliases(Node *n, const_String_or_char_ptr iname) {
+ String *aliasv = Getattr(n, "feature:alias");
+ if (aliasv) {
+ List *aliases = Split(aliasv, ',', INT_MAX);
+ if (aliases && Len(aliases) > 0) {
+ Iterator alias = First(aliases);
+ while (alias.item) {
+ if (Len(alias.item) > 0) {
+ if (current == NO_CPP) {
+ if (useGlobalModule) {
+ Printv(f_init, tab4, "rb_define_alias(rb_cObject, \"", alias.item, "\", \"", iname, "\");\n", NIL);
+ } else {
+ Printv(f_init, tab4, "rb_define_alias(rb_singleton_class(", modvar, "), \"", alias.item, "\", \"", iname, "\");\n", NIL);
+ }
+ } else if (multipleInheritance) {
+ Printv(klass->init, tab4, "rb_define_alias(", klass->mImpl, ", \"", alias.item, "\", \"", iname, "\");\n", NIL);
+ } else {
+ Printv(klass->init, tab4, "rb_define_alias(", klass->vname, ", \"", alias.item, "\", \"", iname, "\");\n", NIL);
+ }
+ }
+ alias = Next(alias);
+ }
+ }
+ Delete(aliases);
+ }
+ }
+
+ /* ---------------------------------------------------------------------
+ * create_command(Node *n, char *iname)
+ *
+ * Creates a new command from a C function.
+ * iname = Name of function in scripting language
+ *
+ * A note about what "protected" and "private" mean in Ruby:
+ *
+ * A private method is accessible only within the class or its subclasses,
+ * and it is callable only in "function form", with 'self' (implicit or
+ * explicit) as a receiver.
+ *
+ * A protected method is callable only from within its class, but unlike
+ * a private method, it can be called with a receiver other than self, such
+ * as another instance of the same class.
+ * --------------------------------------------------------------------- */
+
+ void create_command(Node *n, const_String_or_char_ptr iname) {
+
+ String *alloc_func = Swig_name_wrapper(iname);
+ String *wname = Swig_name_wrapper(iname);
+ if (CPlusPlus) {
+ Insert(wname, 0, "VALUEFUNC(");
+ Append(wname, ")");
+ }
+ if (current != NO_CPP)
+ iname = klass->strip(iname);
+ if (Getattr(special_methods, iname)) {
+ iname = GetChar(special_methods, iname);
+ }
+
+ String *s = NewString("");
+ String *temp = NewString("");
+
+#ifdef SWIG_PROTECTED_TARGET_METHODS
+ const char *rb_define_method = is_public(n) ? "rb_define_method" : "rb_define_protected_method";
+#else
+ const char *rb_define_method = "rb_define_method";
+#endif
+ switch (current) {
+ case MEMBER_FUNC:
+ {
+ if (multipleInheritance) {
+ Printv(klass->init, tab4, rb_define_method, "(", klass->mImpl, ", \"", iname, "\", ", wname, ", -1);\n", NIL);
+ } else {
+ Printv(klass->init, tab4, rb_define_method, "(", klass->vname, ", \"", iname, "\", ", wname, ", -1);\n", NIL);
+ }
+ }
+ break;
+ case CONSTRUCTOR_ALLOCATE:
+ Printv(s, tab4, "rb_define_alloc_func(", klass->vname, ", ", alloc_func, ");\n", NIL);
+ Replaceall(klass->init, "$allocator", s);
+ break;
+ case CONSTRUCTOR_INITIALIZE:
+ Printv(s, tab4, rb_define_method, "(", klass->vname, ", \"initialize\", ", wname, ", -1);\n", NIL);
+ Replaceall(klass->init, "$initializer", s);
+ break;
+ case MEMBER_VAR:
+ Append(temp, iname);
+ /* Check for _set or _get at the end of the name. */
+ if (Len(temp) > 4) {
+ const char *p = Char(temp) + (Len(temp) - 4);
+ if (strcmp(p, "_set") == 0) {
+ Delslice(temp, Len(temp) - 4, DOH_END);
+ Append(temp, "=");
+ } else if (strcmp(p, "_get") == 0) {
+ Delslice(temp, Len(temp) - 4, DOH_END);
+ }
+ }
+ if (multipleInheritance) {
+ Printv(klass->init, tab4, "rb_define_method(", klass->mImpl, ", \"", temp, "\", ", wname, ", -1);\n", NIL);
+ } else {
+ Printv(klass->init, tab4, "rb_define_method(", klass->vname, ", \"", temp, "\", ", wname, ", -1);\n", NIL);
+ }
+ break;
+ case STATIC_FUNC:
+ Printv(klass->init, tab4, "rb_define_singleton_method(", klass->vname, ", \"", iname, "\", ", wname, ", -1);\n", NIL);
+ break;
+ case NO_CPP:
+ if (!useGlobalModule) {
+ Printv(s, tab4, "rb_define_module_function(", modvar, ", \"", iname, "\", ", wname, ", -1);\n", NIL);
+ Printv(f_init, s, NIL);
+ } else {
+ Printv(s, tab4, "rb_define_global_function(\"", iname, "\", ", wname, ", -1);\n", NIL);
+ Printv(f_init, s, NIL);
+ }
+ break;
+ case DESTRUCTOR:
+ case CLASS_CONST:
+ case STATIC_VAR:
+ default:
+ assert(false); // Should not have gotten here for these types
+ }
+
+ defineAliases(n, iname);
+
+ Delete(temp);
+ Delete(s);
+ Delete(wname);
+ Delete(alloc_func);
+ }
+
+ /* ---------------------------------------------------------------------
+ * applyInputTypemap()
+ *
+ * Look up the appropriate "in" typemap for this parameter (p),
+ * substitute the correct strings for the typemap parameters, and dump the
+ * resulting code to the wrapper file.
+ * --------------------------------------------------------------------- */
+
+ Parm *applyInputTypemap(Parm *p, String *source, Wrapper *f, String *symname) {
+ String *tm;
+ SwigType *pt = Getattr(p, "type");
+ if ((tm = Getattr(p, "tmap:in"))) {
+ Replaceall(tm, "$input", source);
+ Replaceall(tm, "$symname", symname);
+
+ if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+
+ Setattr(p, "emit:input", Copy(source));
+ Printf(f->code, "%s\n", tm);
+ p = Getattr(p, "tmap:in:next");
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ p = nextSibling(p);
+ }
+ return p;
+ }
+
+ Parm *skipIgnoredArgs(Parm *p) {
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+ return p;
+ }
+
+ /* ---------------------------------------------------------------------
+ * marshalInputArgs()
+ *
+ * Process all of the arguments passed into the scripting language
+ * method and convert them into C/C++ function arguments using the
+ * supplied typemaps.
+ * --------------------------------------------------------------------- */
+
+ void marshalInputArgs(Node *n, ParmList *l, int numarg, int numreq, String *kwargs, bool allow_kwargs, Wrapper *f) {
+ int i;
+ Parm *p;
+ String *tm;
+ String *source;
+
+ source = NewString("");
+
+ bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n));
+
+ /**
+ * The 'start' value indicates which of the C/C++ function arguments
+ * produced here corresponds to the first value in Ruby's argv[] array.
+ * The value of start is either zero or one. If start is zero, then
+ * the first argument (with name arg1) is based on the value of argv[0].
+ * If start is one, then arg1 is based on the value of argv[1].
+ */
+ int start = (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) ? 1 : 0;
+
+ int varargs = emit_isvarargs(l);
+
+ Printf(kwargs, "{ ");
+ for (i = 0, p = l; i < numarg; i++) {
+
+ p = skipIgnoredArgs(p);
+
+ String *pn = Getattr(p, "name");
+
+ /* Produce string representation of source argument */
+ Clear(source);
+
+ /* First argument is a special case */
+ if (i == 0) {
+ Printv(source, (start == 0) ? "argv[0]" : "self", NIL);
+ } else {
+ Printf(source, "argv[%d]", i - start);
+ }
+
+ if (i >= (numreq)) { /* Check if parsing an optional argument */
+ Printf(f->code, " if (argc > %d) {\n", i - start);
+ }
+
+ /* Record argument name for keyword argument handling */
+ if (Len(pn)) {
+ Printf(kwargs, "\"%s\",", pn);
+ } else {
+ Printf(kwargs, "\"arg%d\",", i + 1);
+ }
+
+ /* Look for an input typemap */
+ p = applyInputTypemap(p, source, f, Getattr(n, "name"));
+ if (i >= numreq) {
+ Printf(f->code, "}\n");
+ }
+ }
+
+ /* Finish argument marshalling */
+ Printf(kwargs, " NULL }");
+ if (allow_kwargs) {
+// kwarg support not implemented
+// Printv(f->locals, tab4, "const char *kwnames[] = ", kwargs, ";\n", NIL);
+ }
+
+ /* Trailing varargs */
+ if (varargs) {
+ if (p && (tm = Getattr(p, "tmap:in"))) {
+ Clear(source);
+ Printf(source, "argv[%d]", i - start);
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", Copy(source));
+ Printf(f->code, "if (argc > %d) {\n", i - start);
+ Printv(f->code, tm, "\n", NIL);
+ Printf(f->code, "}\n");
+ }
+ }
+
+ Delete(source);
+ }
+
+ /* ---------------------------------------------------------------------
+ * insertConstraintCheckingCode(ParmList *l, Wrapper *f)
+ *
+ * Checks each of the parameters in the parameter list for a "check"
+ * typemap and (if it finds one) inserts the typemapping code into
+ * the function wrapper.
+ * --------------------------------------------------------------------- */
+
+ void insertConstraintCheckingCode(ParmList *l, Wrapper *f) {
+ Parm *p;
+ String *tm;
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ }
+
+ /* ---------------------------------------------------------------------
+ * insertCleanupCode(ParmList *l, String *cleanup)
+ *
+ * Checks each of the parameters in the parameter list for a "freearg"
+ * typemap and (if it finds one) inserts the typemapping code into
+ * the function wrapper.
+ * --------------------------------------------------------------------- */
+
+ void insertCleanupCode(ParmList *l, String *cleanup) {
+ String *tm;
+ for (Parm *p = l; p;) {
+ if ((tm = Getattr(p, "tmap:freearg"))) {
+ if (Len(tm) != 0) {
+ Printv(cleanup, tm, "\n", NIL);
+ }
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ }
+
+ /* ---------------------------------------------------------------------
+ * insertArgOutputCode(ParmList *l, String *outarg, int& need_result)
+ *
+ * Checks each of the parameters in the parameter list for a "argout"
+ * typemap and (if it finds one) inserts the typemapping code into
+ * the function wrapper.
+ * --------------------------------------------------------------------- */
+
+ void insertArgOutputCode(ParmList *l, String *outarg, int &need_result) {
+ String *tm;
+ for (Parm *p = l; p;) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+ Replaceall(tm, "$result", "vresult");
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+
+ Printv(outarg, tm, "\n", NIL);
+ need_result += 1;
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+ }
+
+ /* ---------------------------------------------------------------------
+ * validIdentifier()
+ *
+ * Is this a valid identifier in the scripting language?
+ * Ruby method names can include any combination of letters, numbers
+ * and underscores. A Ruby method name may optionally end with
+ * a question mark ("?"), exclamation point ("!") or equals sign ("=").
+ *
+ * Methods whose names end with question marks are, by convention,
+ * predicate methods that return true or false (e.g. Array#empty?).
+ *
+ * Methods whose names end with exclamation points are, by convention,
+ * called bang methods that modify the instance in place (e.g. Array#sort!).
+ *
+ * Methods whose names end with an equals sign are attribute setters
+ * (e.g. Thread#critical=).
+ * --------------------------------------------------------------------- */
+
+ virtual int validIdentifier(String *s) {
+ char *c = Char(s);
+ while (*c) {
+ if (!(isalnum(*c) || (*c == '_') || (*c == '?') || (*c == '!') || (*c == '=')))
+ return 0;
+ c++;
+ }
+ return 1;
+ }
+
+ /* ---------------------------------------------------------------------
+ * functionWrapper()
+ *
+ * Create a function declaration and register it with the interpreter.
+ * --------------------------------------------------------------------- */
+
+ virtual int functionWrapper(Node *n) {
+
+ String *nodeType;
+ bool destructor;
+
+ String *symname = Copy(Getattr(n, "sym:name"));
+ SwigType *t = Getattr(n, "type");
+ ParmList *l = Getattr(n, "parms");
+ int director_method = 0;
+ String *tm;
+
+ int need_result = 0;
+
+ /* Ruby needs no destructor wrapper */
+ if (current == DESTRUCTOR)
+ return SWIG_NOWRAP;
+
+ nodeType = Getattr(n, "nodeType");
+ destructor = (!Cmp(nodeType, "destructor"));
+
+ /* If the C++ class constructor is overloaded, we only want to
+ * write out the "new" singleton method once since it is always
+ * the same. (It's the "initialize" method that will handle the
+ * overloading). */
+
+ if (current == CONSTRUCTOR_ALLOCATE && Swig_symbol_isoverloaded(n) && Getattr(n, "sym:nextSibling") != 0)
+ return SWIG_OK;
+
+ String *overname = 0;
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ } else {
+ if (!addSymbol(symname, n))
+ return SWIG_ERROR;
+ }
+
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+ String *kwargs = NewString("");
+ Wrapper *f = NewWrapper();
+
+ /* Rename predicate methods */
+ if (GetFlag(n, "feature:predicate")) {
+ Append(symname, "?");
+ }
+
+ /* Rename bang methods */
+ if (GetFlag(n, "feature:bang")) {
+ Append(symname, "!");
+ }
+
+ /* Determine the name of the SWIG wrapper function */
+ String *wname = Swig_name_wrapper(symname);
+ if (overname && current != CONSTRUCTOR_ALLOCATE) {
+ Append(wname, overname);
+ }
+
+ /* Emit arguments */
+ if (current != CONSTRUCTOR_ALLOCATE) {
+ emit_parameter_variables(l, f);
+ }
+
+ /* Attach standard typemaps */
+ if (current != CONSTRUCTOR_ALLOCATE) {
+ emit_attach_parmmaps(l, f);
+ }
+ Setattr(n, "wrap:parms", l);
+
+ /* Get number of arguments */
+ int numarg = emit_num_arguments(l);
+ int numreq = emit_num_required(l);
+ int varargs = emit_isvarargs(l);
+ bool allow_kwargs = GetFlag(n, "feature:kwargs") ? true : false;
+
+ bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n));
+ int start = (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) ? 1 : 0;
+
+ /* Now write the wrapper function itself */
+ if (current == CONSTRUCTOR_ALLOCATE) {
+ Printv(f->def, "SWIGINTERN VALUE\n", NIL);
+ Printf(f->def, "#ifdef HAVE_RB_DEFINE_ALLOC_FUNC\n");
+ Printv(f->def, wname, "(VALUE self)\n", NIL);
+ Printf(f->def, "#else\n");
+ Printv(f->def, wname, "(int argc, VALUE *argv, VALUE self)\n", NIL);
+ Printf(f->def, "#endif\n");
+ Printv(f->def, "{\n", NIL);
+ } else if (current == CONSTRUCTOR_INITIALIZE) {
+ Printv(f->def, "SWIGINTERN VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL);
+ if (!varargs) {
+ Printf(f->code, "if ((argc < %d) || (argc > %d)) ", numreq - start, numarg - start);
+ } else {
+ Printf(f->code, "if (argc < %d) ", numreq - start);
+ }
+ Printf(f->code, "{rb_raise(rb_eArgError, \"wrong # of arguments(%%d for %d)\",argc); SWIG_fail;}\n", numreq - start);
+ } else {
+
+ if ( current == NO_CPP )
+ {
+ String* docs = docstring(n, AUTODOC_FUNC);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+ }
+
+ Printv(f->def, "SWIGINTERN VALUE\n", wname, "(int argc, VALUE *argv, VALUE self) {", NIL);
+ if (!varargs) {
+ Printf(f->code, "if ((argc < %d) || (argc > %d)) ", numreq - start, numarg - start);
+ } else {
+ Printf(f->code, "if (argc < %d) ", numreq - start);
+ }
+ Printf(f->code, "{rb_raise(rb_eArgError, \"wrong # of arguments(%%d for %d)\",argc); SWIG_fail;}\n", numreq - start);
+ }
+
+ /* Now walk the function parameter list and generate code */
+ /* to get arguments */
+ if (current != CONSTRUCTOR_ALLOCATE) {
+ marshalInputArgs(n, l, numarg, numreq, kwargs, allow_kwargs, f);
+ }
+ // FIXME?
+ if (ctor_director) {
+ numarg--;
+ numreq--;
+ }
+
+ /* Insert constraint checking code */
+ insertConstraintCheckingCode(l, f);
+
+ /* Insert cleanup code */
+ insertCleanupCode(l, cleanup);
+
+ /* Insert argument output code */
+ insertArgOutputCode(l, outarg, need_result);
+
+ /* if the object is a director, and the method call originated from its
+ * underlying Ruby object, resolve the call by going up the c++
+ * inheritance chain. otherwise try to resolve the method in Ruby.
+ * without this check an infinite loop is set up between the director and
+ * shadow class method calls.
+ */
+
+ // NOTE: this code should only be inserted if this class is the
+ // base class of a director class. however, in general we haven't
+ // yet analyzed all classes derived from this one to see if they are
+ // directors. furthermore, this class may be used as the base of
+ // a director class defined in a completely different module at a
+ // later time, so this test must be included whether or not directorbase
+ // is true. we do skip this code if directors have not been enabled
+ // at the command line to preserve source-level compatibility with
+ // non-polymorphic swig. also, if this wrapper is for a smart-pointer
+ // method, there is no need to perform the test since the calling object
+ // (the smart-pointer) and the director object (the "pointee") are
+ // distinct.
+
+ director_method = is_member_director(n) && !is_smart_pointer() && !destructor;
+ if (director_method) {
+ Wrapper_add_local(f, "director", "Swig::Director *director = 0");
+ Printf(f->code, "director = dynamic_cast<Swig::Director *>(arg1);\n");
+ Wrapper_add_local(f, "upcall", "bool upcall = false");
+ Append(f->code, "upcall = (director && (director->swig_get_self() == self));\n");
+ }
+
+ /* Now write code to make the function call */
+ if (current != CONSTRUCTOR_ALLOCATE) {
+ if (current == CONSTRUCTOR_INITIALIZE) {
+ Node *pn = Swig_methodclass(n);
+ String *symname = Getattr(pn, "sym:name");
+ String *action = Getattr(n, "wrap:action");
+ if (directorsEnabled()) {
+ String *classname = NewStringf("const char *classname SWIGUNUSED = \"%s::%s\"", module, symname);
+ Wrapper_add_local(f, "classname", classname);
+ }
+ if (action) {
+ SwigType *smart = Swig_cparse_smartptr(pn);
+ String *result_name = NewStringf("%s%s", smart ? "smart" : "", Swig_cresult_name());
+ if (smart) {
+ String *result_var = NewStringf("%s *%s = 0", SwigType_namestr(smart), result_name);
+ Wrapper_add_local(f, result_name, result_var);
+ Printf(action, "\n%s = new %s(%s);", result_name, SwigType_namestr(smart), Swig_cresult_name());
+ }
+ Printf(action, "\nDATA_PTR(self) = %s;", result_name);
+ if (GetFlag(pn, "feature:trackobjects")) {
+ Printf(action, "\nSWIG_RubyAddTracking(%s, self);", result_name);
+ }
+ Delete(result_name);
+ Delete(smart);
+ }
+ }
+
+ /* Emit the function call */
+ if (director_method) {
+ Printf(f->code, "try {\n");
+ }
+
+ Setattr(n, "wrap:name", wname);
+
+ Swig_director_emit_dynamic_cast(n, f);
+ String *actioncode = emit_action(n);
+
+ if (director_method) {
+ Printf(actioncode, "} catch (Swig::DirectorException& e) {\n");
+ Printf(actioncode, " rb_exc_raise(e.getError());\n");
+ Printf(actioncode, " SWIG_fail;\n");
+ Printf(actioncode, "}\n");
+ }
+
+ /* Return value if necessary */
+ if (SwigType_type(t) != T_VOID && current != CONSTRUCTOR_INITIALIZE) {
+ need_result = 1;
+ if (GetFlag(n, "feature:predicate")) {
+ Printv(actioncode, tab4, "vresult = (", Swig_cresult_name(), " ? Qtrue : Qfalse);\n", NIL);
+ } else {
+ tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode);
+ actioncode = 0;
+ if (tm) {
+ Replaceall(tm, "$result", "vresult");
+
+ if (GetFlag(n, "feature:new"))
+ Replaceall(tm, "$owner", "SWIG_POINTER_OWN");
+ else
+ Replaceall(tm, "$owner", "0");
+
+ // Unwrap return values that are director classes so that the original Ruby object is returned instead.
+ if (Swig_director_can_unwrap(n)) {
+ Wrapper_add_local(f, "director", "Swig::Director *director = 0");
+ Printf(f->code, "director = dynamic_cast<Swig::Director *>(%s);\n", Swig_cresult_name());
+ Printf(f->code, "if (director) {\n");
+ Printf(f->code, " vresult = director->swig_get_self();\n");
+ Printf(f->code, "} else {\n");
+ Printf(f->code, "%s\n", tm);
+ Printf(f->code, "}\n");
+ director_method = 0;
+ } else {
+ Printf(f->code, "%s\n", tm);
+ }
+
+ Delete(tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s.\n", SwigType_str(t, 0));
+ }
+ }
+ }
+ if (actioncode) {
+ Append(f->code, actioncode);
+ Delete(actioncode);
+ }
+ emit_return_variable(n, t, f);
+ }
+
+ /* Extra code needed for new and initialize methods */
+ if (current == CONSTRUCTOR_ALLOCATE) {
+ Node *pn = Swig_methodclass(n);
+ SwigType *smart = Swig_cparse_smartptr(pn);
+ if (smart)
+ SwigType_add_pointer(smart);
+ String *classtype = smart ? smart : t;
+ need_result = 1;
+ Printf(f->code, "VALUE vresult = SWIG_NewClassInstance(self, SWIGTYPE%s);\n", Char(SwigType_manglestr(classtype)));
+ Printf(f->code, "#ifndef HAVE_RB_DEFINE_ALLOC_FUNC\n");
+ Printf(f->code, "rb_obj_call_init(vresult, argc, argv);\n");
+ Printf(f->code, "#endif\n");
+ Delete(smart);
+ } else if (current == CONSTRUCTOR_INITIALIZE) {
+ need_result = 1;
+ }
+ else
+ {
+ if ( need_result > 1 ) {
+ if ( SwigType_type(t) == T_VOID )
+ Printf(f->code, "vresult = rb_ary_new();\n");
+ else
+ {
+ Printf(f->code, "if (vresult == Qnil) vresult = rb_ary_new();\n");
+ Printf(f->code, "else vresult = SWIG_Ruby_AppendOutput( "
+ "rb_ary_new(), vresult);\n");
+ }
+ }
+ }
+
+ /* Dump argument output code; */
+ Printv(f->code, outarg, NIL);
+
+ /* Dump the argument cleanup code */
+ int need_cleanup = (current != CONSTRUCTOR_ALLOCATE) && (Len(cleanup) != 0);
+ if (need_cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+
+
+ /* Look for any remaining cleanup. This processes the %new directive */
+ if (current != CONSTRUCTOR_ALLOCATE && GetFlag(n, "feature:new")) {
+ tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0);
+ if (tm) {
+ Printv(f->code, tm, "\n", NIL);
+ Delete(tm);
+ }
+ }
+
+ /* Special processing on return value. */
+ tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0);
+ if (tm) {
+ Printv(f->code, tm, NIL);
+ Delete(tm);
+ }
+
+ if (director_method) {
+ if ((tm = Swig_typemap_lookup("directorfree", n, Swig_cresult_name(), 0))) {
+ Replaceall(tm, "$input", Swig_cresult_name());
+ Replaceall(tm, "$result", "vresult");
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+
+ /* Wrap things up (in a manner of speaking) */
+ if (need_result) {
+ if (current == CONSTRUCTOR_ALLOCATE) {
+ Printv(f->code, tab4, "return vresult;\n", NIL);
+ } else if (current == CONSTRUCTOR_INITIALIZE) {
+ Printv(f->code, tab4, "return self;\n", NIL);
+ Printv(f->code, "fail:\n", NIL);
+ if (need_cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+ Printv(f->code, tab4, "return Qnil;\n", NIL);
+ } else {
+ Wrapper_add_local(f, "vresult", "VALUE vresult = Qnil");
+ Printv(f->code, tab4, "return vresult;\n", NIL);
+ Printv(f->code, "fail:\n", NIL);
+ if (need_cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+ Printv(f->code, tab4, "return Qnil;\n", NIL);
+ }
+ } else {
+ Printv(f->code, tab4, "return Qnil;\n", NIL);
+ Printv(f->code, "fail:\n", NIL);
+ if (need_cleanup) {
+ Printv(f->code, cleanup, NIL);
+ }
+ Printv(f->code, tab4, "return Qnil;\n", NIL);
+ }
+
+ Printf(f->code, "}\n");
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+
+ /* Substitute the function name */
+ Replaceall(f->code, "$symname", symname);
+
+ /* Emit the function */
+ Wrapper_print(f, f_wrappers);
+
+ /* Now register the function with the interpreter */
+ if (!Swig_symbol_isoverloaded(n)) {
+ create_command(n, symname);
+ } else {
+ if (current == CONSTRUCTOR_ALLOCATE) {
+ create_command(n, symname);
+ } else {
+ if (!Getattr(n, "sym:nextSibling"))
+ dispatchFunction(n);
+ }
+ }
+
+ Delete(kwargs);
+ Delete(cleanup);
+ Delete(outarg);
+ DelWrapper(f);
+ Delete(symname);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * dispatchFunction()
+ * ------------------------------------------------------------ */
+
+ void dispatchFunction(Node *n) {
+ /* Last node in overloaded chain */
+
+ int maxargs;
+ String *tmp = NewString("");
+ String *dispatch = Swig_overload_dispatch(n, "return %s(nargs, args, self);", &maxargs);
+
+ /* Generate a dispatch wrapper for all overloaded functions */
+
+ Wrapper *f = NewWrapper();
+ String *symname = Getattr(n, "sym:name");
+ String *wname = Swig_name_wrapper(symname);
+
+ Printv(f->def, "SWIGINTERN VALUE ", wname, "(int nargs, VALUE *args, VALUE self) {", NIL);
+
+ Wrapper_add_local(f, "argc", "int argc");
+ bool ctor_director = (current == CONSTRUCTOR_INITIALIZE && Swig_directorclass(n));
+ if (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) {
+ Printf(tmp, "VALUE argv[%d]", maxargs + 1);
+ } else {
+ Printf(tmp, "VALUE argv[%d]", maxargs);
+ }
+ Wrapper_add_local(f, "argv", tmp);
+ Wrapper_add_local(f, "ii", "int ii");
+
+ if (current == MEMBER_FUNC || current == MEMBER_VAR || ctor_director) {
+ maxargs += 1;
+ Printf(f->code, "argc = nargs + 1;\n");
+ Printf(f->code, "argv[0] = self;\n");
+ Printf(f->code, "if (argc > %d) SWIG_fail;\n", maxargs);
+ Printf(f->code, "for (ii = 1; (ii < argc); ++ii) {\n");
+ Printf(f->code, "argv[ii] = args[ii-1];\n");
+ Printf(f->code, "}\n");
+ } else {
+ Printf(f->code, "argc = nargs;\n");
+ Printf(f->code, "if (argc > %d) SWIG_fail;\n", maxargs);
+ Printf(f->code, "for (ii = 0; (ii < argc); ++ii) {\n");
+ Printf(f->code, "argv[ii] = args[ii];\n");
+ Printf(f->code, "}\n");
+ }
+
+ Replaceall(dispatch, "$args", "nargs, args, self");
+ Printv(f->code, dispatch, "\n", NIL);
+
+
+
+ // Generate prototype list, go to first node
+ Node *sibl = n;
+
+ while (Getattr(sibl, "sym:previousSibling"))
+ sibl = Getattr(sibl, "sym:previousSibling"); // go all the way up
+
+ // Constructors will be treated specially
+ const bool isCtor = (!Cmp(Getattr(sibl, "nodeType"), "constructor"));
+ const bool isMethod = ( Cmp(Getattr(sibl, "ismember"), "1") == 0 &&
+ (!isCtor) );
+
+ // Construct real method name
+ String* methodName = NewString("");
+ if ( isMethod ) {
+ // Sometimes a method node has no parent (SF#3034054).
+ // This value is used in an exception message, so just skip the class
+ // name in this case so at least we don't segfault. This is probably
+ // just working around a problem elsewhere though.
+ Node *parent_node = parentNode(sibl);
+ if (parent_node)
+ Printv( methodName, Getattr(parent_node,"sym:name"), ".", NIL );
+ }
+ Append( methodName, Getattr(sibl,"sym:name" ) );
+ if ( isCtor ) Append( methodName, ".new" );
+
+ // Generate prototype list
+ String *protoTypes = NewString("");
+ do {
+ Append( protoTypes, "\n\" ");
+ if (!isCtor) {
+ SwigType *type = SwigType_str(Getattr(sibl, "type"), NULL);
+ Printv(protoTypes, type, " ", NIL);
+ Delete(type);
+ }
+ Printv(protoTypes, methodName, NIL );
+ Parm* p = Getattr(sibl, "wrap:parms");
+ if (p && (current == MEMBER_FUNC || current == MEMBER_VAR ||
+ ctor_director) )
+ p = nextSibling(p); // skip self
+ Append( protoTypes, "(" );
+ while(p)
+ {
+ Append( protoTypes, SwigType_str(Getattr(p,"type"), Getattr(p,"name")) );
+ if ( ( p = nextSibling(p)) ) Append(protoTypes, ", ");
+ }
+ Append( protoTypes, ")\\n\"" );
+ } while ((sibl = Getattr(sibl, "sym:nextSibling")));
+
+ Append(f->code, "fail:\n");
+ Printf(f->code, "Ruby_Format_OverloadedError( argc, %d, \"%s\", %s);\n",
+ maxargs, methodName, protoTypes);
+ Append(f->code, "\nreturn Qnil;\n");
+
+ Delete(methodName);
+ Delete(protoTypes);
+
+ Printv(f->code, "}\n", NIL);
+ Wrapper_print(f, f_wrappers);
+ create_command(n, Char(symname));
+
+ DelWrapper(f);
+ Delete(dispatch);
+ Delete(tmp);
+ Delete(wname);
+ }
+
+ /* ---------------------------------------------------------------------
+ * variableWrapper()
+ * --------------------------------------------------------------------- */
+
+ virtual int variableWrapper(Node *n) {
+ String* docs = docstring(n, AUTODOC_GETTER);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+
+ char *name = GetChar(n, "name");
+ char *iname = GetChar(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+ String *tm;
+ String *getfname, *setfname;
+ Wrapper *getf, *setf;
+ const int assignable = is_assignable(n);
+
+ // Determine whether virtual global variables shall be used
+ // which have different getter and setter signatures,
+ // see https://docs.ruby-lang.org/en/2.6.0/extension_rdoc.html#label-Global+Variables+Shared+Between+C+and+Ruby
+ const bool use_virtual_var = (current == NO_CPP && useGlobalModule);
+
+ getf = NewWrapper();
+ setf = NewWrapper();
+
+ /* create getter */
+ int addfail = 0;
+ String *getname = Swig_name_get(NSPACE_TODO, iname);
+ getfname = Swig_name_wrapper(getname);
+ Setattr(n, "wrap:name", getfname);
+ Printv(getf->def, "SWIGINTERN VALUE\n", getfname, "(", NIL);
+ Printf(getf->def, (use_virtual_var) ? "ID id, VALUE *data" : "VALUE self");
+ Printf(getf->def, ") {");
+ Wrapper_add_local(getf, "_val", "VALUE _val");
+
+ tm = Swig_typemap_lookup("varout", n, name, 0);
+ if (tm) {
+ Replaceall(tm, "$result", "_val");
+ /* Printv(getf->code,tm, NIL); */
+ addfail = emit_action_code(n, getf->code, tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VAROUT_UNDEF, input_file, line_number, "Unable to read variable of type %s\n", SwigType_str(t, 0));
+ }
+ Printv(getf->code, tab4, "return _val;\n", NIL);
+ if (addfail) {
+ Append(getf->code, "fail:\n");
+ Append(getf->code, " return Qnil;\n");
+ }
+ Append(getf->code, "}\n");
+
+ Wrapper_print(getf, f_wrappers);
+
+ if (!assignable) {
+ setfname = NewString("(rb_gvar_setter_t *)NULL");
+ } else {
+ /* create setter */
+ String* docs = docstring(n, AUTODOC_SETTER);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+ String *setname = Swig_name_set(NSPACE_TODO, iname);
+ setfname = Swig_name_wrapper(setname);
+ Setattr(n, "wrap:name", setfname);
+ Printf(setf->def, "SWIGINTERN ");
+ if (use_virtual_var) {
+ Printv(setf->def, "void\n", setfname, "(VALUE _val, ID id, VALUE *data) {", NIL);
+ } else {
+ Printv(setf->def, "VALUE\n", setfname, "(VALUE self, VALUE _val) {", NIL);
+ }
+ tm = Swig_typemap_lookup("varin", n, name, 0);
+ if (tm) {
+ Replaceall(tm, "$input", "_val");
+ /* Printv(setf->code,tm,"\n",NIL); */
+ emit_action_code(n, setf->code, tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s\n", SwigType_str(t, 0));
+ }
+ if (use_virtual_var) {
+ Printf(setf->code, "fail:\n");
+ Printv(setf->code, tab4, "return;\n", NIL);
+ } else {
+ Printv(setf->code, tab4, "return _val;\n", NIL);
+ Printf(setf->code, "fail:\n");
+ Printv(setf->code, tab4, "return Qnil;\n", NIL);
+ }
+ Printf(setf->code, "}\n");
+ Wrapper_print(setf, f_wrappers);
+ Delete(setname);
+ }
+
+ /* define accessor methods */
+ Insert(getfname, 0, "VALUEFUNC(");
+ Append(getfname, ")");
+ Insert(setfname, 0, (use_virtual_var) ? "SWIG_RUBY_VOID_ANYARGS_FUNC(" : "VALUEFUNC(");
+ Append(setfname, ")");
+
+ String *s = NewString("");
+ switch (current) {
+ case STATIC_VAR:
+ /* C++ class variable */
+ Printv(s, tab4, "rb_define_singleton_method(", klass->vname, ", \"", klass->strip(iname), "\", ", getfname, ", 0);\n", NIL);
+ if (assignable) {
+ Printv(s, tab4, "rb_define_singleton_method(", klass->vname, ", \"", klass->strip(iname), "=\", ", setfname, ", 1);\n", NIL);
+ }
+ Printv(klass->init, s, NIL);
+ break;
+ default:
+ /* C global variable */
+ /* wrapped in Ruby module attribute */
+ assert(current == NO_CPP);
+ if (!useGlobalModule) {
+ Printv(s, tab4, "rb_define_singleton_method(", modvar, ", \"", iname, "\", ", getfname, ", 0);\n", NIL);
+ if (assignable) {
+ Printv(s, tab4, "rb_define_singleton_method(", modvar, ", \"", iname, "=\", ", setfname, ", 1);\n", NIL);
+ }
+ } else {
+ Printv(s, tab4, "rb_define_virtual_variable(\"$", iname, "\", ", getfname, ", ", setfname, ");\n", NIL);
+ }
+ Printv(f_init, s, NIL);
+ Delete(s);
+ break;
+ }
+ Delete(getname);
+ Delete(getfname);
+ Delete(setfname);
+ DelWrapper(setf);
+ DelWrapper(getf);
+ return SWIG_OK;
+ }
+
+
+ /* ---------------------------------------------------------------------
+ * validate_const_name(char *name)
+ *
+ * Validate constant name.
+ * --------------------------------------------------------------------- */
+
+ char *validate_const_name(char *name, const char *reason) {
+ if (!name || name[0] == '\0')
+ return name;
+
+ if (isupper(name[0]))
+ return name;
+
+ if (islower(name[0])) {
+ name[0] = (char)toupper(name[0]);
+ Swig_warning(WARN_RUBY_WRONG_NAME, input_file, line_number, "Wrong %s name (corrected to `%s')\n", reason, name);
+ return name;
+ }
+
+ Swig_warning(WARN_RUBY_WRONG_NAME, input_file, line_number, "Wrong %s name %s\n", reason, name);
+
+ return name;
+ }
+
+ /* ---------------------------------------------------------------------
+ * constantWrapper()
+ * --------------------------------------------------------------------- */
+
+ virtual int constantWrapper(Node *n) {
+ Swig_require("constantWrapper", n, "*sym:name", "type", "value", NIL);
+
+ char *iname = GetChar(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+
+ if (current == CLASS_CONST) {
+ iname = klass->strip(iname);
+ }
+ validate_const_name(iname, "constant");
+ SetChar(n, "sym:name", iname);
+
+ /* Special hook for member pointer */
+ if (SwigType_type(type) == T_MPOINTER) {
+ String *wname = Swig_name_wrapper(iname);
+ Printf(f_header, "static %s = %s;\n", SwigType_str(type, wname), value);
+ value = Char(wname);
+ }
+ String *tm = Swig_typemap_lookup("constant", n, value, 0);
+ if (!tm)
+ tm = Swig_typemap_lookup("constcode", n, value, 0);
+ if (tm) {
+ Replaceall(tm, "$symname", iname);
+ Replaceall(tm, "$value", value);
+ if (current == CLASS_CONST) {
+ if (multipleInheritance) {
+ Replaceall(tm, "$module", klass->mImpl);
+ Printv(klass->init, tm, "\n", NIL);
+ } else {
+ Replaceall(tm, "$module", klass->vname);
+ Printv(klass->init, tm, "\n", NIL);
+ }
+ } else {
+ if (!useGlobalModule) {
+ Replaceall(tm, "$module", modvar);
+ } else {
+ Replaceall(tm, "$module", "rb_cObject");
+ }
+ Printf(f_init, "%s\n", tm);
+ }
+ } else {
+ Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value %s = %s\n", SwigType_str(type, 0), value);
+ }
+ Swig_restore(n);
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------------
+ * classDeclaration()
+ *
+ * Records information about classes---even classes that might be defined in
+ * other modules referenced by %import.
+ * ----------------------------------------------------------------------------- */
+
+ virtual int classDeclaration(Node *n) {
+ if (!Getattr(n, "feature:onlychildren")) {
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+
+ String *namestr = SwigType_namestr(name);
+ klass = RCLASS(classes, Char(namestr));
+ if (!klass) {
+ klass = new RClass();
+ String *valid_name = NewString(symname ? symname : namestr);
+ validate_const_name(Char(valid_name), "class");
+ klass->set_name(namestr, symname, valid_name);
+ SET_RCLASS(classes, Char(namestr), klass);
+ Delete(valid_name);
+ }
+ Delete(namestr);
+ }
+ return Language::classDeclaration(n);
+ }
+
+ /**
+ * Process the comma-separated list of mixed-in module names (if any).
+ */
+ void includeRubyModules(Node *n) {
+ String *mixin = Getattr(n, "feature:mixin");
+ if (mixin) {
+ List *modules = Split(mixin, ',', INT_MAX);
+ if (modules && Len(modules) > 0) {
+ Iterator mod = First(modules);
+ while (mod.item) {
+ if (Len(mod.item) > 0) {
+ Printf(klass->init, "rb_include_module(%s, rb_eval_string(\"%s\"));\n", klass->vname, mod.item);
+ }
+ mod = Next(mod);
+ }
+ }
+ Delete(modules);
+ }
+ }
+
+ void handleBaseClasses(Node *n) {
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator base = First(baselist);
+ while (base.item && GetFlag(base.item, "feature:ignore")) {
+ base = Next(base);
+ }
+ while (base.item) {
+ String *basename = Getattr(base.item, "name");
+ String *basenamestr = SwigType_namestr(basename);
+ RClass *super = RCLASS(classes, Char(basenamestr));
+ Delete(basenamestr);
+ if (super) {
+ SwigType *btype = NewString(basename);
+ SwigType_add_pointer(btype);
+ SwigType_remember(btype);
+ SwigType *smart = Swig_cparse_smartptr(base.item);
+ if (smart) {
+ SwigType_add_pointer(smart);
+ SwigType_remember(smart);
+ }
+ String *bmangle = SwigType_manglestr(smart ? smart : btype);
+ if (multipleInheritance) {
+ Insert(bmangle, 0, "((swig_class *) SWIGTYPE");
+ Append(bmangle, "->clientdata)->mImpl");
+ Printv(klass->init, "rb_include_module(", klass->mImpl, ", ", bmangle, ");\n", NIL);
+ } else {
+ Insert(bmangle, 0, "((swig_class *) SWIGTYPE");
+ Append(bmangle, "->clientdata)->klass");
+ Replaceall(klass->init, "$super", bmangle);
+ }
+ Delete(bmangle);
+ Delete(smart);
+ Delete(btype);
+ }
+ base = Next(base);
+ while (base.item && GetFlag(base.item, "feature:ignore")) {
+ base = Next(base);
+ }
+ if (!multipleInheritance) {
+ /* Warn about multiple inheritance for additional base class(es) */
+ while (base.item) {
+ if (GetFlag(base.item, "feature:ignore")) {
+ base = Next(base);
+ continue;
+ }
+ String *proxyclassname = SwigType_str(Getattr(n, "classtypeobj"), 0);
+ String *baseclassname = SwigType_str(Getattr(base.item, "name"), 0);
+ Swig_warning(WARN_RUBY_MULTIPLE_INHERITANCE, Getfile(n), Getline(n),
+ "Warning for %s, base %s ignored. Multiple inheritance is not supported in Ruby.\n", proxyclassname, baseclassname);
+ base = Next(base);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Check to see if a %markfunc was specified.
+ */
+ void handleMarkFuncDirective(Node *n) {
+ String *markfunc = Getattr(n, "feature:markfunc");
+ if (markfunc) {
+ Printf(klass->init, "SwigClass%s.mark = (void (*)(void *)) %s;\n", klass->name, markfunc);
+ } else {
+ Printf(klass->init, "SwigClass%s.mark = 0;\n", klass->name);
+ }
+ }
+
+ /**
+ * Check to see if a %freefunc was specified.
+ */
+ void handleFreeFuncDirective(Node *n) {
+ String *freefunc = Getattr(n, "feature:freefunc");
+ if (freefunc) {
+ Printf(klass->init, "SwigClass%s.destroy = (void (*)(void *)) %s;\n", klass->name, freefunc);
+ } else {
+ if (klass->destructor_defined) {
+ Printf(klass->init, "SwigClass%s.destroy = (void (*)(void *)) free_%s;\n", klass->name, klass->mname);
+ }
+ }
+ }
+
+ /**
+ * Check to see if tracking is enabled for this class.
+ */
+ void handleTrackDirective(Node *n) {
+ int trackObjects = GetFlag(n, "feature:trackobjects");
+ if (trackObjects) {
+ Printf(klass->init, "SwigClass%s.trackObjects = 1;\n", klass->name);
+ } else {
+ Printf(klass->init, "SwigClass%s.trackObjects = 0;\n", klass->name);
+ }
+ }
+
+ /* ----------------------------------------------------------------------
+ * classHandler()
+ * ---------------------------------------------------------------------- */
+
+ virtual int classHandler(Node *n) {
+ String* docs = docstring(n, AUTODOC_CLASS);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+ String *name = Getattr(n, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *namestr = SwigType_namestr(name); // does template expansion
+
+ klass = RCLASS(classes, Char(namestr));
+ assert(klass != 0);
+ Delete(namestr);
+ String *valid_name = NewString(symname);
+ validate_const_name(Char(valid_name), "class");
+
+ Clear(klass->type);
+ Printv(klass->type, Getattr(n, "classtype"), NIL);
+ Printv(f_wrappers, "static swig_class SwigClass", valid_name, ";\n\n", NIL);
+ Printv(klass->init, "\n", tab4, NIL);
+
+ if (!useGlobalModule) {
+ Printv(klass->init, klass->vname, " = rb_define_class_under(", modvar, ", \"", klass->name, "\", $super);\n", NIL);
+ } else {
+ Printv(klass->init, klass->vname, " = rb_define_class(\"", klass->name,
+ "\", $super);\n", NIL);
+ }
+
+ if (multipleInheritance) {
+ Printv(klass->init, klass->mImpl, " = rb_define_module_under(", klass->vname, ", \"Impl\");\n", NIL);
+ }
+
+ SwigType *tt = NewString(name);
+ SwigType_add_pointer(tt);
+ SwigType_remember(tt);
+ SwigType *smart = Swig_cparse_smartptr(n);
+ if (smart) {
+ SwigType_add_pointer(smart);
+ SwigType_remember(smart);
+ }
+ String *tm = SwigType_manglestr(smart ? smart : tt);
+ Printf(klass->init, "SWIG_TypeClientData(SWIGTYPE%s, (void *) &SwigClass%s);\n", tm, valid_name);
+ Delete(tm);
+ Delete(smart);
+ Delete(tt);
+ Delete(valid_name);
+
+ includeRubyModules(n);
+
+ Printv(klass->init, "$allocator", NIL);
+ Printv(klass->init, "$initializer", NIL);
+
+ Language::classHandler(n);
+
+ handleBaseClasses(n);
+ handleMarkFuncDirective(n);
+ handleFreeFuncDirective(n);
+ handleTrackDirective(n);
+
+ if (multipleInheritance) {
+ Printv(klass->init, "rb_include_module(", klass->vname, ", ", klass->mImpl, ");\n", NIL);
+ }
+
+ String *s = NewString("");
+ Printv(s, tab4, "rb_undef_alloc_func(", klass->vname, ");\n", NIL);
+ Replaceall(klass->init, "$allocator", s);
+ Replaceall(klass->init, "$initializer", "");
+
+ if (GetFlag(n, "feature:exceptionclass")) {
+ Replaceall(klass->init, "$super", "rb_eRuntimeError");
+ } else {
+ Replaceall(klass->init, "$super", "rb_cObject");
+ }
+ Delete(s);
+
+ Printv(f_init, klass->init, NIL);
+ klass = 0;
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * memberfunctionHandler()
+ *
+ * Method for adding C++ member function
+ *
+ * By default, we're going to create a function of the form :
+ *
+ * Foo_bar(this,args)
+ *
+ * Where Foo is the classname, bar is the member name and the this pointer
+ * is explicitly attached to the beginning.
+ *
+ * The renaming only applies to the member function part, not the full
+ * classname.
+ *
+ * --------------------------------------------------------------------- */
+
+ virtual int memberfunctionHandler(Node *n) {
+ current = MEMBER_FUNC;
+
+ String* docs = docstring(n, AUTODOC_METHOD);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+ Language::memberfunctionHandler(n);
+ current = NO_CPP;
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------
+ * constructorHandler()
+ *
+ * Method for adding C++ member constructor
+ * -------------------------------------------------------------------- */
+
+ void set_director_ctor_code(Node *n) {
+ /* director ctor code is specific for each class */
+ Delete(director_prot_ctor_code);
+ director_prot_ctor_code = NewString("");
+ Node *pn = Swig_methodclass(n);
+ String *symname = Getattr(pn, "sym:name");
+ String *name = Copy(symname);
+ char *cname = Char(name);
+ if (cname)
+ cname[0] = (char)toupper(cname[0]);
+ Printv(director_prot_ctor_code,
+ "if ( $comparison ) { /* subclassed */\n",
+ " $director_new \n",
+ "} else {\n", " rb_raise(rb_eNameError,\"accessing abstract class or protected constructor\"); \n", " return Qnil;\n", "}\n", NIL);
+ Delete(director_ctor_code);
+ director_ctor_code = NewString("");
+ Printv(director_ctor_code, "if ( $comparison ) { /* subclassed */\n", " $director_new \n", "} else {\n", " $nondirector_new \n", "}\n", NIL);
+ Delete(name);
+ }
+
+ virtual int constructorHandler(Node *n) {
+ int use_director = Swig_directorclass(n);
+ if (use_director) {
+ set_director_ctor_code(n);
+ }
+
+ /* First wrap the allocate method */
+ current = CONSTRUCTOR_ALLOCATE;
+ Swig_name_register("construct", "%n%c_allocate");
+
+ Language::constructorHandler(n);
+
+ String* docs = docstring(n, AUTODOC_CTOR);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+ /*
+ * If we're wrapping the constructor of a C++ director class, prepend a new parameter
+ * to receive the scripting language object (e.g. 'self')
+ *
+ */
+ Swig_save("ruby:constructorHandler", n, "parms", NIL);
+ if (use_director) {
+ Parm *parms = Getattr(n, "parms");
+ Parm *self;
+ String *name = NewString("self");
+ String *type = NewString("VALUE");
+ self = NewParm(type, name, n);
+ Delete(type);
+ Delete(name);
+ Setattr(self, "lname", "Qnil");
+ if (parms)
+ set_nextSibling(self, parms);
+ Setattr(n, "parms", self);
+ Setattr(n, "wrap:self", "1");
+ Delete(self);
+ }
+
+ /* Now do the instance initialize method */
+ current = CONSTRUCTOR_INITIALIZE;
+ Swig_name_register("construct", "new_%n%c");
+ Language::constructorHandler(n);
+
+ /* Restore original parameter list */
+ Delattr(n, "wrap:self");
+ Swig_restore(n);
+
+ /* Done */
+ Swig_name_unregister("construct");
+ current = NO_CPP;
+ klass->constructor_defined = 1;
+ return SWIG_OK;
+ }
+
+ virtual int copyconstructorHandler(Node *n) {
+ int use_director = Swig_directorclass(n);
+ if (use_director) {
+ set_director_ctor_code(n);
+ }
+
+ /* First wrap the allocate method */
+ current = CONSTRUCTOR_ALLOCATE;
+ Swig_name_register("construct", "%n%c_allocate");
+
+ return Language::copyconstructorHandler(n);
+ }
+
+
+ /* ---------------------------------------------------------------------
+ * destructorHandler()
+ * -------------------------------------------------------------------- */
+
+ virtual int destructorHandler(Node *n) {
+
+ /* Do no spit free function if user defined his own for this class */
+ Node *pn = Swig_methodclass(n);
+ String *freefunc = Getattr(pn, "feature:freefunc");
+ if (freefunc) return SWIG_OK;
+
+ current = DESTRUCTOR;
+ Language::destructorHandler(n);
+
+ freefunc = NewString("");
+ String *freebody = NewString("");
+ String *pname0 = Swig_cparm_name(0, 0);
+
+ Printv(freefunc, "free_", klass->mname, NIL);
+ Printv(freebody, "SWIGINTERN void\n", freefunc, "(void *self) {\n", NIL);
+ Printv(freebody, tab4, klass->type, " *", pname0, " = (", klass->type, " *)self;\n", NIL);
+ Printv(freebody, tab4, NIL);
+
+ /* Check to see if object tracking is activated for the class
+ that owns this destructor. */
+ if (GetFlag(pn, "feature:trackobjects")) {
+ Printf(freebody, "SWIG_RubyRemoveTracking(%s);\n", pname0);
+ Printv(freebody, tab4, NIL);
+ }
+
+ if (Extend) {
+ String *wrap = Getattr(n, "wrap:code");
+ if (wrap) {
+ Printv(f_wrappers, wrap, NIL);
+ }
+ /* Printv(freebody, Swig_name_destroy(name), "(", pname0, ")", NIL); */
+ Printv(freebody, Getattr(n, "wrap:action"), "\n", NIL);
+ } else {
+ String *action = Getattr(n, "wrap:action");
+ if (action) {
+ Printv(freebody, action, "\n", NIL);
+ } else {
+ /* In the case swig emits no destroy function. */
+ if (CPlusPlus)
+ Printf(freebody, "delete %s;\n", pname0);
+ else
+ Printf(freebody, "free((char*) %s);\n", pname0);
+ }
+ }
+
+ Printv(freebody, "}\n\n", NIL);
+
+ Printv(f_wrappers, freebody, NIL);
+
+ klass->destructor_defined = 1;
+ current = NO_CPP;
+ Delete(freefunc);
+ Delete(freebody);
+ Delete(pname0);
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------
+ * membervariableHandler()
+ *
+ * This creates a pair of functions to set/get the variable of a member.
+ * -------------------------------------------------------------------- */
+
+ virtual int membervariableHandler(Node *n) {
+ String* docs = docstring(n, AUTODOC_GETTER);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+ if (is_assignable(n)) {
+ String* docs = docstring(n, AUTODOC_SETTER);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+ }
+
+ current = MEMBER_VAR;
+ Language::membervariableHandler(n);
+ current = NO_CPP;
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * staticmemberfunctionHandler()
+ *
+ * Wrap a static C++ function
+ * ---------------------------------------------------------------------- */
+
+ virtual int staticmemberfunctionHandler(Node *n) {
+ String* docs = docstring(n, AUTODOC_STATICFUNC);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+ current = STATIC_FUNC;
+ Language::staticmemberfunctionHandler(n);
+ current = NO_CPP;
+ return SWIG_OK;
+ }
+
+ /* ----------------------------------------------------------------------
+ * memberconstantHandler()
+ *
+ * Create a C++ constant
+ * --------------------------------------------------------------------- */
+
+ virtual int memberconstantHandler(Node *n) {
+ String* docs = docstring(n, AUTODOC_STATICFUNC);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+ current = CLASS_CONST;
+ Language::memberconstantHandler(n);
+ current = NO_CPP;
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------
+ * staticmembervariableHandler()
+ * --------------------------------------------------------------------- */
+
+ virtual int staticmembervariableHandler(Node *n) {
+ String* docs = docstring(n, AUTODOC_GETTER);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+
+ if (is_assignable(n)) {
+ String* docs = docstring(n, AUTODOC_SETTER);
+ Printf(f_wrappers, "%s", docs);
+ Delete(docs);
+ }
+
+ current = STATIC_VAR;
+ Language::staticmembervariableHandler(n);
+ current = NO_CPP;
+ return SWIG_OK;
+ }
+
+ /* C++ director class generation */
+ virtual int classDirector(Node *n) {
+ return Language::classDirector(n);
+ }
+
+ virtual int classDirectorInit(Node *n) {
+ String *declaration;
+ declaration = Swig_director_declaration(n);
+ Printf(f_directors_h, "\n");
+ Printf(f_directors_h, "%s\n", declaration);
+ Printf(f_directors_h, "public:\n");
+ Delete(declaration);
+ return Language::classDirectorInit(n);
+ }
+
+ virtual int classDirectorEnd(Node *n) {
+ Printf(f_directors_h, "};\n\n");
+ return Language::classDirectorEnd(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorConstructor()
+ * ------------------------------------------------------------ */
+
+ virtual int classDirectorConstructor(Node *n) {
+ Node *parent = Getattr(n, "parentNode");
+ String *sub = NewString("");
+ String *decl = Getattr(n, "decl");
+ String *supername = Swig_class_name(parent);
+ String *classname = NewString("");
+ Printf(classname, "SwigDirector_%s", supername);
+
+ /* insert self parameter */
+ Parm *p;
+ ParmList *superparms = Getattr(n, "parms");
+ ParmList *parms = CopyParmList(superparms);
+ String *type = NewString("VALUE");
+ p = NewParm(type, NewString("self"), n);
+ set_nextSibling(p, parms);
+ parms = p;
+
+ if (!Getattr(n, "defaultargs")) {
+ /* constructor */
+ {
+ Wrapper *w = NewWrapper();
+ String *call;
+ String *basetype = Getattr(parent, "classtype");
+ String *target = Swig_method_decl(0, decl, classname, parms, 0);
+ call = Swig_csuperclass_call(0, basetype, superparms);
+ Printf(w->def, "%s::%s: %s, Swig::Director(self) { }", classname, target, call);
+ Delete(target);
+ Wrapper_print(w, f_directors);
+ Delete(call);
+ DelWrapper(w);
+ }
+
+ /* constructor header */
+ {
+ String *target = Swig_method_decl(0, decl, classname, parms, 1);
+ Printf(f_directors_h, " %s;\n", target);
+ Delete(target);
+ }
+ }
+
+ Delete(sub);
+ Delete(classname);
+ Delete(supername);
+ Delete(parms);
+ return Language::classDirectorConstructor(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classDirectorDefaultConstructor()
+ * ------------------------------------------------------------ */
+
+ virtual int classDirectorDefaultConstructor(Node *n) {
+ String *classname;
+ Wrapper *w;
+ classname = Swig_class_name(n);
+ w = NewWrapper();
+ Printf(w->def, "SwigDirector_%s::SwigDirector_%s(VALUE self) : Swig::Director(self) { }", classname, classname);
+ Wrapper_print(w, f_directors);
+ DelWrapper(w);
+ Printf(f_directors_h, " SwigDirector_%s(VALUE self);\n", classname);
+ Delete(classname);
+ return Language::classDirectorDefaultConstructor(n);
+ }
+
+ /* ---------------------------------------------------------------
+ * exceptionSafeMethodCall()
+ *
+ * Emit a virtual director method to pass a method call on to the
+ * underlying Ruby instance.
+ *
+ * --------------------------------------------------------------- */
+
+ void exceptionSafeMethodCall(String *className, Node *n, Wrapper *w, int argc, String *args, bool initstack) {
+ Wrapper *body = NewWrapper();
+ Wrapper *rescue = NewWrapper();
+
+ String *methodName = Getattr(n, "sym:name");
+
+ String *bodyName = NewStringf("%s_%s_body", className, methodName);
+ String *rescueName = NewStringf("%s_%s_rescue", className, methodName);
+ String *depthCountName = NewStringf("%s_%s_call_depth", className, methodName);
+
+ // Check for an exception typemap of some kind
+ String *tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
+ if (!tm) {
+ tm = Getattr(n, "feature:director:except");
+ }
+
+ if ((tm != 0) && (Len(tm) > 0) && (Strcmp(tm, "1") != 0)) {
+ // Declare a global to hold the depth count
+ if (!Getattr(n, "sym:nextSibling")) {
+ Printf(body->def, "static int %s = 0;\n", depthCountName);
+
+ // Function body
+ Printf(body->def, "VALUE %s(VALUE data) {\n", bodyName);
+ Wrapper_add_localv(body, "args", "Swig::body_args *", "args", "= reinterpret_cast<Swig::body_args *>(data)", NIL);
+ Wrapper_add_localv(body, Swig_cresult_name(), "VALUE", Swig_cresult_name(), "= Qnil", NIL);
+ Printf(body->code, "%s++;\n", depthCountName);
+ Printv(body->code, Swig_cresult_name(), " = rb_funcall2(args->recv, args->id, args->argc, args->argv);\n", NIL);
+ Printf(body->code, "%s--;\n", depthCountName);
+ Printv(body->code, "return ", Swig_cresult_name(), ";\n", NIL);
+ Printv(body->code, "}", NIL);
+
+ // Exception handler
+ Printf(rescue->def, "VALUE %s(VALUE args, VALUE error) {\n", rescueName);
+ Replaceall(tm, "$error", "error");
+ Printf(rescue->code, "%s--;\n", depthCountName);
+ Printf(rescue->code, "if (%s == 0) ", depthCountName);
+ Printv(rescue->code, Str(tm), "\n", NIL);
+ Printv(rescue->code, "rb_exc_raise(error);\n", NIL);
+ Printv(rescue->code, "return Qnil;\n", NIL);
+ Printv(rescue->code, "}", NIL);
+ }
+
+ // Main code
+ Wrapper_add_localv(w, "args", "Swig::body_args", "args", NIL);
+ Wrapper_add_localv(w, "status", "int", "status", NIL);
+ Printv(w->code, "args.recv = swig_get_self();\n", NIL);
+ Printf(w->code, "args.id = rb_intern(\"%s\");\n", methodName);
+ Printf(w->code, "args.argc = %d;\n", argc);
+ if (argc > 0) {
+ Printf(w->code, "args.argv = new VALUE[%d];\n", argc);
+ for (int i = 0; i < argc; i++) {
+ Printf(w->code, "args.argv[%d] = obj%d;\n", i, i);
+ }
+ } else {
+ Printv(w->code, "args.argv = 0;\n", NIL);
+ }
+ Printf(w->code, "%s = rb_protect(PROTECTFUNC(%s), reinterpret_cast<VALUE>(&args), &status);\n", Swig_cresult_name(), bodyName);
+ if ( initstack ) Printf(w->code, "SWIG_RELEASE_STACK;\n");
+ Printf(w->code, "if (status) {\n");
+ Printf(w->code, "VALUE lastErr = rb_gv_get(\"$!\");\n");
+ Printf(w->code, "%s(reinterpret_cast<VALUE>(&args), lastErr);\n", rescueName);
+ Printf(w->code, "}\n");
+ if (argc > 0) {
+ Printv(w->code, "delete [] args.argv;\n", NIL);
+ }
+ // Dump wrapper code
+ Wrapper_print(body, f_directors_helpers);
+ Wrapper_print(rescue, f_directors_helpers);
+ } else {
+ if (argc > 0) {
+ Printf(w->code, "%s = rb_funcall(swig_get_self(), rb_intern(\"%s\"), %d%s);\n", Swig_cresult_name(), methodName, argc, args);
+ } else {
+ Printf(w->code, "%s = rb_funcall(swig_get_self(), rb_intern(\"%s\"), 0, Qnil);\n", Swig_cresult_name(), methodName);
+ }
+ if ( initstack ) Printf(w->code, "SWIG_RELEASE_STACK;\n");
+ }
+
+ // Clean up
+ Delete(bodyName);
+ Delete(rescueName);
+ Delete(depthCountName);
+ DelWrapper(body);
+ DelWrapper(rescue);
+ }
+
+ virtual int classDirectorMethod(Node *n, Node *parent, String *super) {
+ int is_void = 0;
+ int is_pointer = 0;
+ String *decl = Getattr(n, "decl");
+ String *name = Getattr(n, "name");
+ String *classname = Getattr(parent, "sym:name");
+ String *c_classname = Getattr(parent, "name");
+ String *symname = Getattr(n, "sym:name");
+ String *declaration = NewString("");
+ ParmList *l = Getattr(n, "parms");
+ Wrapper *w = NewWrapper();
+ String *tm;
+ String *wrap_args = NewString("");
+ String *returntype = Getattr(n, "type");
+ Parm *p;
+ String *value = Getattr(n, "value");
+ String *storage = Getattr(n, "storage");
+ bool pure_virtual = false;
+ int status = SWIG_OK;
+ int idx;
+ bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;
+ bool asvoid = checkAttribute( n, "feature:numoutputs", "0") ? true : false;
+ bool initstack = checkAttribute( n, "feature:initstack", "1") ? true : false;
+
+ if (Cmp(storage, "virtual") == 0) {
+ if (Cmp(value, "0") == 0) {
+ pure_virtual = true;
+ }
+ }
+ String *overnametmp = NewString(Getattr(n, "sym:name"));
+ if (Getattr(n, "sym:overloaded")) {
+ Printf(overnametmp, "::%s", Getattr(n, "sym:overname"));
+ }
+
+ /* determine if the method returns a pointer */
+ is_pointer = SwigType_ispointer_return(decl);
+ is_void = (!Cmp(returntype, "void") && !is_pointer);
+
+ /* virtual method definition */
+ String *target;
+ String *pclassname = NewStringf("SwigDirector_%s", classname);
+ String *qualified_name = NewStringf("%s::%s", pclassname, name);
+ SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
+ target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
+ Printf(w->def, "%s", target);
+ Delete(qualified_name);
+ Delete(target);
+ /* header declaration */
+ target = Swig_method_decl(rtype, decl, name, l, 1);
+ Printf(declaration, " virtual %s", target);
+ Delete(target);
+
+ // Get any exception classes in the throws typemap
+ if (Getattr(n, "noexcept")) {
+ Append(w->def, " noexcept");
+ Append(declaration, " noexcept");
+ }
+ ParmList *throw_parm_list = 0;
+
+ if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
+ Parm *p;
+ int gencomma = 0;
+
+ Append(w->def, " throw(");
+ Append(declaration, " throw(");
+
+ if (throw_parm_list)
+ Swig_typemap_attach_parms("throws", throw_parm_list, 0);
+ for (p = throw_parm_list; p; p = nextSibling(p)) {
+ if (Getattr(p, "tmap:throws")) {
+ if (gencomma++) {
+ Append(w->def, ", ");
+ Append(declaration, ", ");
+ }
+
+ Printf(w->def, "%s", SwigType_str(Getattr(p, "type"), 0));
+ Printf(declaration, "%s", SwigType_str(Getattr(p, "type"), 0));
+ }
+ }
+
+ Append(w->def, ")");
+ Append(declaration, ")");
+ }
+
+ Append(w->def, " {");
+ Append(declaration, ";\n");
+
+ if (initstack && !(ignored_method && !pure_virtual)) {
+ Append(w->def, "\nSWIG_INIT_STACK;\n");
+ }
+
+ /* declare method return value
+ * if the return value is a reference or const reference, a specialized typemap must
+ * handle it, including declaration of c_result ($result).
+ */
+ if (!is_void && (!ignored_method || pure_virtual)) {
+ if (!SwigType_isclass(returntype)) {
+ if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
+ String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
+ Delete(construct_result);
+ } else {
+ Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
+ }
+ } else {
+ String *cres = SwigType_lstr(returntype, "c_result");
+ Printf(w->code, "%s;\n", cres);
+ Delete(cres);
+ }
+ }
+
+ if (ignored_method) {
+ if (!pure_virtual) {
+ if (!is_void)
+ Printf(w->code, "return ");
+ String *super_call = Swig_method_call(super, l);
+ Printf(w->code, "%s;\n", super_call);
+ Delete(super_call);
+ } else {
+ Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname),
+ SwigType_namestr(name));
+ }
+ } else {
+ /* attach typemaps to arguments (C/C++ -> Ruby) */
+ String *arglist = NewString("");
+
+ Swig_director_parms_fixup(l);
+
+ Swig_typemap_attach_parms("in", l, 0);
+ Swig_typemap_attach_parms("directorin", l, w);
+ Swig_typemap_attach_parms("directorargout", l, w);
+
+ char source[256];
+
+ int outputs = 0;
+ if (!is_void && !asvoid)
+ outputs++;
+
+ /* build argument list and type conversion string */
+ idx = 0; p = l;
+ while ( p ) {
+
+ if (Getattr(p, "tmap:ignore")) {
+ p = Getattr(p, "tmap:ignore:next");
+ continue;
+ }
+
+ if (Getattr(p, "tmap:directorargout") != 0)
+ outputs++;
+
+ if ( checkAttribute( p, "tmap:in:numinputs", "0") ) {
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ }
+
+ String *parameterName = Getattr(p, "name");
+ String *parameterType = Getattr(p, "type");
+
+ Putc(',', arglist);
+ if ((tm = Getattr(p, "tmap:directorin")) != 0) {
+ sprintf(source, "obj%d", idx++);
+ String *input = NewString(source);
+ Setattr(p, "emit:directorinput", input);
+ Replaceall(tm, "$input", input);
+ Replaceall(tm, "$owner", "0");
+ Delete(input);
+ Printv(wrap_args, tm, "\n", NIL);
+ Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL);
+ Printv(arglist, source, NIL);
+ p = Getattr(p, "tmap:directorin:next");
+ continue;
+ } else if (Cmp(parameterType, "void")) {
+ /**
+ * Special handling for pointers to other C++ director classes.
+ * Ideally this would be left to a typemap, but there is currently no
+ * way to selectively apply the dynamic_cast<> to classes that have
+ * directors. In other words, the type "SwigDirector_$1_lname" only exists
+ * for classes with directors. We avoid the problem here by checking
+ * module.wrap::directormap, but it's not clear how to get a typemap to
+ * do something similar. Perhaps a new default typemap (in addition
+ * to SWIGTYPE) called DIRECTORTYPE?
+ */
+ if (SwigType_ispointer(parameterType) || SwigType_isreference(parameterType)) {
+ Node *modname = Getattr(parent, "module");
+ Node *target = Swig_directormap(modname, parameterType);
+ sprintf(source, "obj%d", idx++);
+ String *nonconst = 0;
+ /* strip pointer/reference --- should move to Swig/stype.c */
+ String *nptype = NewString(Char(parameterType) + 2);
+ /* name as pointer */
+ String *ppname = Copy(parameterName);
+ if (SwigType_isreference(parameterType)) {
+ Insert(ppname, 0, "&");
+ }
+ /* if necessary, cast away const since Ruby doesn't support it! */
+ if (SwigType_isconst(nptype)) {
+ nonconst = NewStringf("nc_tmp_%s", parameterName);
+ String *nonconst_i = NewStringf("= const_cast< %s >(%s)", SwigType_lstr(parameterType, 0), ppname);
+ Wrapper_add_localv(w, nonconst, SwigType_lstr(parameterType, 0), nonconst, nonconst_i, NIL);
+ Delete(nonconst_i);
+ Swig_warning(WARN_LANG_DISCARD_CONST, input_file, line_number,
+ "Target language argument '%s' discards const in director method %s::%s.\n", SwigType_str(parameterType, parameterName),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ } else {
+ nonconst = Copy(ppname);
+ }
+ Delete(nptype);
+ Delete(ppname);
+ String *mangle = SwigType_manglestr(parameterType);
+ if (target) {
+ String *director = NewStringf("director_%s", mangle);
+ Wrapper_add_localv(w, director, "Swig::Director *", director, "= 0", NIL);
+ Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL);
+ Printf(wrap_args, "%s = dynamic_cast<Swig::Director *>(%s);\n", director, nonconst);
+ Printf(wrap_args, "if (!%s) {\n", director);
+ Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
+ Printf(wrap_args, "} else {\n");
+ Printf(wrap_args, "%s = %s->swig_get_self();\n", source, director);
+ Printf(wrap_args, "}\n");
+ Delete(director);
+ Printv(arglist, source, NIL);
+ } else {
+ Wrapper_add_localv(w, source, "VALUE", source, "= Qnil", NIL);
+ Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
+ //Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE_p_%s, 0);\n",
+ // source, nonconst, base);
+ Printv(arglist, source, NIL);
+ }
+ Delete(mangle);
+ Delete(nonconst);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
+ "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(parameterType, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_NOWRAP;
+ break;
+ }
+ }
+ p = nextSibling(p);
+ }
+
+ /* declare Ruby return value */
+ String *value_result = NewStringf("VALUE SWIGUNUSED %s", Swig_cresult_name());
+ Wrapper_add_local(w, Swig_cresult_name(), value_result);
+ Delete(value_result);
+
+ /* wrap complex arguments to VALUEs */
+ Printv(w->code, wrap_args, NIL);
+
+ /* pass the method call on to the Ruby object */
+ exceptionSafeMethodCall(classname, n, w, idx, arglist, initstack);
+
+ /*
+ * Ruby method may return a simple object, or an Array of objects.
+ * For in/out arguments, we have to extract the appropriate VALUEs from the Array,
+ * then marshal everything back to C/C++ (return value and output arguments).
+ */
+
+ /* Marshal return value and other outputs (if any) from VALUE to C/C++ type */
+
+ String *cleanup = NewString("");
+ String *outarg = NewString("");
+
+ if (outputs > 1) {
+ Wrapper_add_local(w, "output", "VALUE output");
+ Printf(w->code, "if (TYPE(%s) != T_ARRAY) {\n", Swig_cresult_name());
+ Printf(w->code, "Ruby_DirectorTypeMismatchException(\"Ruby method failed to return an array.\");\n");
+ Printf(w->code, "}\n");
+ }
+
+ idx = 0;
+
+ /* Marshal return value */
+ if (!is_void) {
+ tm = Swig_typemap_lookup("directorout", n, Swig_cresult_name(), w);
+ if (tm != 0) {
+ if (outputs > 1 && !asvoid ) {
+ Printf(w->code, "output = rb_ary_entry(%s, %d);\n", Swig_cresult_name(), idx++);
+ Replaceall(tm, "$input", "output");
+ } else {
+ Replaceall(tm, "$input", Swig_cresult_name());
+ }
+ /* TODO check this */
+ if (Getattr(n, "wrap:disown")) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+ Replaceall(tm, "$result", "c_result");
+ Printv(w->code, tm, "\n", NIL);
+ } else {
+ Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
+ "Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(returntype, 0),
+ SwigType_namestr(c_classname), SwigType_namestr(name));
+ status = SWIG_ERROR;
+ }
+ }
+
+ /* Marshal outputs */
+ for (p = l; p;) {
+ if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
+ if (outputs > 1) {
+ Printf(w->code, "output = rb_ary_entry(%s, %d);\n", Swig_cresult_name(), idx++);
+ Replaceall(tm, "$result", "output");
+ } else {
+ Replaceall(tm, "$result", Swig_cresult_name());
+ }
+ Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
+ Printv(w->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:directorargout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ Delete(arglist);
+ Delete(cleanup);
+ Delete(outarg);
+ }
+
+ /* any existing helper functions to handle this? */
+ if (!is_void) {
+ if (!(ignored_method && !pure_virtual)) {
+ String *rettype = SwigType_str(returntype, 0);
+ if (!SwigType_isreference(returntype)) {
+ Printf(w->code, "return (%s) c_result;\n", rettype);
+ } else {
+ Printf(w->code, "return (%s) *c_result;\n", rettype);
+ }
+ Delete(rettype);
+ }
+ }
+
+ Printf(w->code, "}\n");
+
+ // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
+ String *inline_extra_method = NewString("");
+ if (dirprot_mode() && !is_public(n) && !pure_virtual) {
+ Printv(inline_extra_method, declaration, NIL);
+ String *extra_method_name = NewStringf("%sSwigPublic", name);
+ Replaceall(inline_extra_method, name, extra_method_name);
+ Replaceall(inline_extra_method, ";\n", " {\n ");
+ if (!is_void)
+ Printf(inline_extra_method, "return ");
+ String *methodcall = Swig_method_call(super, l);
+ Printv(inline_extra_method, methodcall, ";\n }\n", NIL);
+ Delete(methodcall);
+ Delete(extra_method_name);
+ }
+
+ /* emit the director method */
+ if (status == SWIG_OK) {
+ if (!Getattr(n, "defaultargs")) {
+ Replaceall(w->code, "$symname", symname);
+ Wrapper_print(w, f_directors);
+ Printv(f_directors_h, declaration, NIL);
+ Printv(f_directors_h, inline_extra_method, NIL);
+ }
+ }
+
+ /* clean up */
+ Delete(wrap_args);
+ Delete(pclassname);
+ DelWrapper(w);
+ return status;
+ }
+
+ virtual int classDirectorConstructors(Node *n) {
+ return Language::classDirectorConstructors(n);
+ }
+
+ virtual int classDirectorMethods(Node *n) {
+ return Language::classDirectorMethods(n);
+ }
+
+ virtual int classDirectorDisown(Node *n) {
+ return Language::classDirectorDisown(n);
+ }
+
+ String *runtimeCode() {
+ String *s = NewString("");
+ String *shead = Swig_include_sys("rubyhead.swg");
+ if (!shead) {
+ Printf(stderr, "*** Unable to open 'rubyhead.swg'\n");
+ } else {
+ Append(s, shead);
+ Delete(shead);
+ }
+ String *serrors = Swig_include_sys("rubyerrors.swg");
+ if (!serrors) {
+ Printf(stderr, "*** Unable to open 'rubyerrors.swg'\n");
+ } else {
+ Append(s, serrors);
+ Delete(serrors);
+ }
+ String *strack = Swig_include_sys("rubytracking.swg");
+ if (!strack) {
+ Printf(stderr, "*** Unable to open 'rubytracking.swg'\n");
+ } else {
+ Append(s, strack);
+ Delete(strack);
+ }
+ String *sapi = Swig_include_sys("rubyapi.swg");
+ if (!sapi) {
+ Printf(stderr, "*** Unable to open 'rubyapi.swg'\n");
+ } else {
+ Append(s, sapi);
+ Delete(sapi);
+ }
+ String *srun = Swig_include_sys("rubyrun.swg");
+ if (!srun) {
+ Printf(stderr, "*** Unable to open 'rubyrun.swg'\n");
+ } else {
+ Append(s, srun);
+ Delete(srun);
+ }
+ return s;
+ }
+
+ String *defaultExternalRuntimeFilename() {
+ return NewString("swigrubyrun.h");
+ }
+
+ /*----------------------------------------------------------------------
+ * kwargsSupport()
+ *--------------------------------------------------------------------*/
+
+ bool kwargsSupport() const {
+ // kwargs support isn't actually implemented, but changing to return false may break something now as it turns on compactdefaultargs
+ return true;
+ }
+}; /* class RUBY */
+
+/* -----------------------------------------------------------------------------
+ * swig_ruby() - Instantiate module
+ * ----------------------------------------------------------------------------- */
+
+static Language *new_swig_ruby() {
+ return new RUBY();
+}
+extern "C" Language *swig_ruby(void) {
+ return new_swig_ruby();
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 2
+ * End:
+ */
diff --git a/contrib/tools/swig/Source/Modules/scilab.cxx b/contrib/tools/swig/Source/Modules/scilab.cxx
new file mode 100644
index 0000000000..aabd2d8426
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/scilab.cxx
@@ -0,0 +1,1167 @@
+/* ----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * scilab.cxx
+ *
+ * Scilab language module for SWIG.
+ * --------------------------------------------------------------------------*/
+
+#include "swigmod.h"
+#include <cstddef>
+#include <cstdlib>
+
+static const int SCILAB_IDENTIFIER_NAME_CHAR_MAX = 24;
+
+static const char *usage = (char *) " \
+Scilab options (available with -scilab)\n \
+ -builder - Generate a Scilab builder script\n \
+ -buildercflags <cflags> - Add <cflags> to the builder compiler flags\n \
+ -builderflagscript <file> - Set the Scilab script <file> to use by builder to configure the build flags\n \
+ -builderldflags <ldflags> - Add <ldflags> to the builder linker flags\n \
+ -buildersources <files> - Add the (comma separated) files <files> to the builder sources\n \
+ -builderverbositylevel <level> - Set the builder verbosity level to <level> (default 0: off, 2: high)\n \
+ -gatewayxml <gateway_id> - Generate gateway xml with the given <gateway_id>\n \
+\n";
+
+
+class SCILAB:public Language {
+protected:
+ /* General objects used for holding the strings */
+ File *beginSection;
+ File *runtimeSection;
+ File *headerSection;
+ File *wrappersSection;
+ File *initSection;
+
+ String *variablesCode;
+
+ bool generateBuilder;
+ File *builderFile;
+ String *builderCode;
+ String *builderCode5;
+ String *builderCode6;
+ int builderFunctionCount;
+
+ List *sourceFileList;
+ List *cflags;
+ List *ldflags;
+
+ String *verboseBuildLevel;
+ String *buildFlagsScript;
+
+ String *gatewayHeader;
+ String *gatewayHeaderV5;
+ String *gatewayHeaderV6;
+
+ bool createGatewayXML;
+ File *gatewayXMLFile;
+ String *gatewayXML;
+ String *gatewayID;
+ int primitiveID;
+
+ bool createLoader;
+ File *loaderFile;
+ String *loaderScript;
+ String *loaderScript5;
+ String *loaderScript6;
+ int loaderFunctionCount;
+public:
+
+ /* ------------------------------------------------------------------------
+ * main()
+ * ----------------------------------------------------------------------*/
+
+ virtual void main(int argc, char *argv[]) {
+ generateBuilder = false;
+ sourceFileList = NewList();
+ cflags = NewList();
+ ldflags = NewList();
+ verboseBuildLevel = NULL;
+ buildFlagsScript = NULL;
+
+ gatewayHeader = NULL;
+ gatewayHeaderV5 = NULL;
+ gatewayHeaderV6 = NULL;
+
+ createGatewayXML = false;
+ gatewayXML = NULL;
+ gatewayXMLFile = NULL;
+ gatewayID = NULL;
+
+ createLoader = true;
+ loaderFile = NULL;
+ loaderScript = NULL;
+
+ /* Manage command line arguments */
+ for (int argIndex = 1; argIndex < argc; argIndex++) {
+ if (argv[argIndex] != NULL) {
+ if (strcmp(argv[argIndex], "-help") == 0) {
+ Printf(stdout, "%s\n", usage);
+ } else if (strcmp(argv[argIndex], "-builder") == 0) {
+ Swig_mark_arg(argIndex);
+ generateBuilder = true;
+ createLoader = false;
+ } else if (strcmp(argv[argIndex], "-buildersources") == 0) {
+ if (argv[argIndex + 1] != NULL) {
+ Swig_mark_arg(argIndex);
+ char *sourceFile = strtok(argv[argIndex + 1], ",");
+ while (sourceFile != NULL) {
+ Insert(sourceFileList, Len(sourceFileList), sourceFile);
+ sourceFile = strtok(NULL, ",");
+ }
+ Swig_mark_arg(argIndex + 1);
+ }
+ } else if (strcmp(argv[argIndex], "-buildercflags") == 0) {
+ Swig_mark_arg(argIndex);
+ if (argv[argIndex + 1] != NULL) {
+ Insert(cflags, Len(cflags), argv[argIndex + 1]);
+ Swig_mark_arg(argIndex + 1);
+ }
+ } else if (strcmp(argv[argIndex], "-builderldflags") == 0) {
+ Swig_mark_arg(argIndex);
+ if (argv[argIndex + 1] != NULL) {
+ Insert(ldflags, Len(ldflags), argv[argIndex + 1]);
+ Swig_mark_arg(argIndex + 1);
+ }
+ } else if (strcmp(argv[argIndex], "-builderverbositylevel") == 0) {
+ Swig_mark_arg(argIndex);
+ verboseBuildLevel = NewString(argv[argIndex + 1]);
+ Swig_mark_arg(argIndex + 1);
+ } else if (strcmp(argv[argIndex], "-builderflagscript") == 0) {
+ Swig_mark_arg(argIndex);
+ buildFlagsScript = NewString(argv[argIndex + 1]);
+ Swig_mark_arg(argIndex + 1);
+ } else if (strcmp(argv[argIndex], "-gatewayxml") == 0) {
+ Swig_mark_arg(argIndex);
+ createGatewayXML = true;
+ gatewayID = NewString(argv[argIndex + 1]);
+ Swig_mark_arg(argIndex + 1);
+ }
+ }
+ }
+
+ if (verboseBuildLevel == NULL) {
+ verboseBuildLevel = NewString("0");
+ }
+
+ /* Set language-specific subdirectory in SWIG library */
+ SWIG_library_directory("scilab");
+
+ /* Add a symbol to the parser for conditional compilation */
+ Preprocessor_define("SWIGSCILAB 1", 0);
+
+ /* Set scilab configuration file */
+ SWIG_config_file("scilab.swg");
+
+ /* Set typemap for scilab */
+ SWIG_typemap_lang("scilab");
+
+ allow_overloading();
+ }
+
+ /* ------------------------------------------------------------------------
+ * top()
+ * ----------------------------------------------------------------------*/
+
+ virtual int top(Node *node) {
+
+ /* Get the module name */
+ String *gatewayName = Getattr(node, "name");
+
+ // Set library name
+ String *gatewayLibraryName = NewStringf("lib%s", gatewayName);
+
+ /* Get the output file name */
+ String *outputFilename = Getattr(node, "outfile");
+
+ /* Initialize I/O */
+ beginSection = NewFile(outputFilename, "w", SWIG_output_files());
+ if (!beginSection) {
+ FileErrorDisplay(outputFilename);
+ Exit(EXIT_FAILURE);
+ }
+ runtimeSection = NewString("");
+ initSection = NewString("");
+ headerSection = NewString("");
+ wrappersSection = NewString("");
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("begin", beginSection);
+ Swig_register_filebyname("header", headerSection);
+ Swig_register_filebyname("wrapper", wrappersSection);
+ Swig_register_filebyname("runtime", runtimeSection);
+ Swig_register_filebyname("init", initSection);
+
+ /* Output module initialization code */
+ Swig_banner(beginSection);
+
+ Swig_obligatory_macros(runtimeSection, "SCILAB");
+
+ // Gateway header source merged with wrapper source in nobuilder mode
+ if (!generateBuilder)
+ startGatewayHeader(gatewayLibraryName);
+
+ // Create builder file if required
+ if (generateBuilder) {
+ createBuilderFile(outputFilename);
+ }
+
+ // Create gateway XML if required
+ if (createGatewayXML) {
+ createGatewayXMLFile(gatewayName);
+ }
+
+ // Create loader script if required
+ if (createLoader) {
+ createLoaderFile(gatewayLibraryName);
+ }
+
+ // Module initialization function
+ String *smallFunctionName = createSmallIdentifierName(gatewayName, SCILAB_IDENTIFIER_NAME_CHAR_MAX - 5);
+ String *gatewayInitFunctionName = NewStringf("%s_Init", gatewayName);
+ String *gatewayInitSmallFunctionName = NewStringf("%s_Init", smallFunctionName);
+ String *wrapperFunctionName = NewStringf("SWIG_%s_Init", gatewayName);
+
+ /* Add initialization function to builder table */
+ addFunctionToScilab(gatewayInitFunctionName, gatewayInitSmallFunctionName, wrapperFunctionName);
+
+ // Add helper functions to builder table
+ addHelperFunctions();
+
+ // Open Scilab wrapper variables creation function
+ variablesCode = NewString("");
+ Printf(variablesCode, "int SWIG_CreateScilabVariables(void *_pvApiCtx) {");
+
+ /* Emit code for children */
+ if (CPlusPlus) {
+ Printf(wrappersSection, "extern \"C\" {\n");
+ }
+
+ Language::top(node);
+
+ if (CPlusPlus) {
+ Printf(wrappersSection, "}\n");
+ }
+ // Close Scilab wrapper variables creation function
+ Printf(variablesCode, " return SWIG_OK;\n}\n");
+
+ // Add Builder footer code and save
+ if (generateBuilder) {
+ saveBuilderFile(gatewayLibraryName);
+ }
+
+ /* Close the init function and rename with module name */
+ Printf(initSection, "return 0;\n}\n");
+ Replaceall(initSection, "<module>", gatewayName);
+
+ /* Write all to the wrapper file */
+ SwigType_emit_type_table(runtimeSection, wrappersSection); // Declare pointer types, ... (Ex: SWIGTYPE_p_p_double)
+
+ // Gateway header source merged with wrapper source in nobuilder mode
+ if (!generateBuilder) {
+ terminateGatewayHeader(gatewayLibraryName);
+ Printv(initSection, gatewayHeader, NIL);
+ }
+
+ Dump(runtimeSection, beginSection);
+ Dump(headerSection, beginSection);
+ Dump(wrappersSection, beginSection);
+ Dump(variablesCode, beginSection);
+ Wrapper_pretty_print(initSection, beginSection);
+
+ if (createGatewayXML) {
+ saveGatewayXMLFile();
+ }
+
+ if (createLoader) {
+ saveLoaderFile(gatewayLibraryName);
+ }
+
+ /* Cleanup files */
+ Delete(runtimeSection);
+ Delete(headerSection);
+ Delete(wrappersSection);
+ Delete(initSection);
+ Delete(beginSection);
+
+ Delete(sourceFileList);
+ Delete(cflags);
+ Delete(ldflags);
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------------------
+ * emitBanner()
+ * ----------------------------------------------------------------------*/
+
+ void emitBanner(File *f) {
+ Printf(f, "// ----------------------------------------------------------------------------\n");
+ Swig_banner_target_lang(f, "// ");
+ Printf(f, "// ----------------------------------------------------------------------------- */\n\n");
+ }
+
+ /* ------------------------------------------------------------------------
+ * functionWrapper()
+ * ----------------------------------------------------------------------*/
+
+ virtual int functionWrapper(Node *node) {
+
+ /* Get some useful attributes of this function */
+ String *functionName = Getattr(node, "sym:name");
+ String *smallFunctionName = createSmallIdentifierName(functionName);
+
+ SwigType *functionReturnType = Getattr(node, "type");
+ ParmList *functionParamsList = Getattr(node, "parms");
+
+ int paramIndex = 0; // Used for loops over ParmsList
+ Parm *param = NULL; // Used for loops over ParamsList
+
+ /* Create the wrapper object */
+ Wrapper *wrapper = NewWrapper();
+
+ /* Create the function wrapper name */
+ String *wrapperName = Swig_name_wrapper(functionName);
+
+ /* Deal with overloading */
+ String *overloadedName = Copy(wrapperName);
+ /* Determine whether the function is overloaded or not */
+ bool isOverloaded = ! !Getattr(node, "sym:overloaded");
+ /* Determine whether the function is the last overloaded */
+ bool isLastOverloaded = isOverloaded && !Getattr(node, "sym:nextSibling");
+
+ if (!isOverloaded && !addSymbol(functionName, node)) {
+ DelWrapper(wrapper);
+ return SWIG_ERROR;
+ }
+
+ if (isOverloaded) {
+ Append(overloadedName, Getattr(node, "sym:overname"));
+ }
+
+ /* Write the wrapper function definition (standard Scilab gateway function prototype) */
+ Printv(wrapper->def, "SWIGEXPORT int ", overloadedName, "(SWIG_GatewayParameters) {", NIL);
+
+ /* Emit all of the local variables for holding arguments */
+ // E.g.: double arg1;
+ emit_parameter_variables(functionParamsList, wrapper);
+
+ /* Attach typemaps to the parameter list */
+ // Add local variables used in typemaps (iRows, iCols, ...)
+ emit_attach_parmmaps(functionParamsList, wrapper);
+ Setattr(node, "wrap:parms", functionParamsList);
+
+ /* Check input/output arguments count */
+ int maxInputArguments = emit_num_arguments(functionParamsList);
+ int minInputArguments = emit_num_required(functionParamsList);
+ int minOutputArguments = 0;
+ int maxOutputArguments = 1;
+
+ if (!emit_isvarargs(functionParamsList)) {
+ Printf(wrapper->code, "SWIG_CheckInputArgument(pvApiCtx, $mininputarguments, $maxinputarguments);\n");
+ }
+ else {
+ Printf(wrapper->code, "SWIG_CheckInputArgumentAtLeast(pvApiCtx, $mininputarguments-1);\n");
+ }
+ Printf(wrapper->code, "SWIG_CheckOutputArgument(pvApiCtx, $minoutputarguments, $maxoutputarguments);\n");
+
+ /* Set context */
+ Printf(wrapper->code, "SWIG_Scilab_SetFuncName(fname);\n");
+ Printf(wrapper->code, "SWIG_Scilab_SetApiContext(pvApiCtx);\n");
+
+ /* Write typemaps(in) */
+
+ for (paramIndex = 0, param = functionParamsList; paramIndex < maxInputArguments; ++paramIndex) {
+ // Ignore parameter if the typemap specifies numinputs=0
+ while (checkAttribute(param, "tmap:in:numinputs", "0")) {
+ param = Getattr(param, "tmap:in:next");
+ }
+
+ SwigType *paramType = Getattr(param, "type");
+ String *paramTypemap = Getattr(param, "tmap:in");
+
+ if (paramTypemap) {
+ // Replace $input by the position on Scilab stack
+ String *source = NewString("");
+ Printf(source, "%d", paramIndex + 1);
+ Setattr(param, "emit:input", source);
+ Replaceall(paramTypemap, "$input", Getattr(param, "emit:input"));
+
+ if (Getattr(param, "wrap:disown") || (Getattr(param, "tmap:in:disown"))) {
+ Replaceall(paramTypemap, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(paramTypemap, "$disown", "0");
+ }
+
+ if (paramIndex >= minInputArguments) { /* Optional input argument management */
+ Printf(wrapper->code, "if (SWIG_NbInputArgument(pvApiCtx) > %d) {\n%s\n}\n", paramIndex, paramTypemap);
+ } else {
+ Printf(wrapper->code, "%s\n", paramTypemap);
+ }
+ param = Getattr(param, "tmap:in:next");
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(paramType, 0));
+ break;
+ }
+ }
+
+ /* TODO write constraints */
+
+ Setattr(node, "wrap:name", overloadedName);
+
+ /* Emit the function call */
+ Swig_director_emit_dynamic_cast(node, wrapper);
+ String *functionActionCode = emit_action(node);
+
+ /* Insert the return variable */
+ emit_return_variable(node, functionReturnType, wrapper);
+
+ /* Return the function value if necessary */
+ String *functionReturnTypemap = Swig_typemap_lookup_out("out", node, Swig_cresult_name(), wrapper, functionActionCode);
+ if (functionReturnTypemap) {
+ // Result is actually the position of output value on stack
+ if (Len(functionReturnTypemap) > 0) {
+ Printf(wrapper->code, "SWIG_Scilab_SetOutputPosition(%d);\n", 1);
+ }
+ Replaceall(functionReturnTypemap, "$result", "1");
+
+ if (GetFlag(node, "feature:new")) {
+ Replaceall(functionReturnTypemap, "$owner", "1");
+ } else {
+ Replaceall(functionReturnTypemap, "$owner", "0");
+ }
+
+ Printf(wrapper->code, "%s\n", functionReturnTypemap);
+
+ /* If the typemap is not empty, the function return one more argument than the typemaps gives */
+ if (Len(functionReturnTypemap) > 0) {
+ minOutputArguments++;
+ maxOutputArguments++;
+ }
+ Delete(functionReturnTypemap);
+
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(functionReturnType, 0),
+ functionName);
+ }
+
+ /* Write typemaps(out) */
+ for (param = functionParamsList; param;) {
+ String *paramTypemap = Getattr(param, "tmap:argout");
+ if (paramTypemap) {
+ minOutputArguments++;
+ maxOutputArguments++;
+ Printf(wrapper->code, "SWIG_Scilab_SetOutputPosition(%d);\n", minOutputArguments);
+ String *result = NewString("");
+ Printf(result, "%d", minOutputArguments);
+ Replaceall(paramTypemap, "$result", result);
+ Printf(wrapper->code, "%s\n", paramTypemap);
+ Delete(paramTypemap);
+ param = Getattr(param, "tmap:argout:next");
+ } else {
+ param = nextSibling(param);
+ }
+ }
+ /* Add cleanup code */
+ for (param = functionParamsList; param;) {
+ String *tm;
+ if ((tm = Getattr(param, "tmap:freearg"))) {
+ if (tm && (Len(tm) != 0)) {
+ Printf(wrapper->code, "%s\n", tm);
+ }
+ param = Getattr(param, "tmap:freearg:next");
+ } else {
+ param = nextSibling(param);
+ }
+ }
+
+ /* See if there is any return cleanup code */
+ String *tm;
+ if ((tm = Swig_typemap_lookup("ret", node, Swig_cresult_name(), 0))) {
+ Printf(wrapper->code, "%s\n", tm);
+ Delete(tm);
+ }
+
+ /* Close the function(ok) */
+ Printv(wrapper->code, "return SWIG_OK;\n", NIL);
+ Printv(wrapper->code, "}\n", NIL);
+
+ /* Add the failure cleanup code */
+ /* TODO */
+
+ /* Final substitutions if applicable */
+ Replaceall(wrapper->code, "$symname", functionName);
+
+ /* Set CheckInputArgument and CheckOutputArgument input arguments */
+ if (maxOutputArguments < 1) {
+ maxOutputArguments = 1;
+ }
+ if (minOutputArguments == 1) {
+ minOutputArguments = 0;
+ }
+ String *argnumber = NewString("");
+ Printf(argnumber, "%d", minInputArguments);
+ Replaceall(wrapper->code, "$mininputarguments", argnumber);
+
+ argnumber = NewString("");
+ Printf(argnumber, "%d", maxInputArguments);
+ Replaceall(wrapper->code, "$maxinputarguments", argnumber);
+
+ argnumber = NewString("");
+ Printf(argnumber, "%d", minOutputArguments);
+ Replaceall(wrapper->code, "$minoutputarguments", argnumber);
+
+ argnumber = NewString("");
+ Printf(argnumber, "%d", maxOutputArguments);
+ Replaceall(wrapper->code, "$maxoutputarguments", argnumber);
+
+ /* Dump the function out */
+ Wrapper_print(wrapper, wrappersSection);
+
+ /* Update builder.sce contents */
+ if (isLastOverloaded) {
+ addFunctionToScilab(functionName, smallFunctionName, wrapperName);
+ dispatchFunction(node);
+ }
+
+ if (!isOverloaded) {
+ addFunctionToScilab(functionName, smallFunctionName, wrapperName);
+ }
+
+ /* tidy up */
+ Delete(overloadedName);
+ Delete(wrapperName);
+ DelWrapper(wrapper);
+
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * dispatchFunction()
+ * ----------------------------------------------------------------------- */
+
+ void dispatchFunction(Node *node) {
+ Wrapper *wrapper = NewWrapper();
+
+ String *functionName = Getattr(node, "sym:name");
+ String *wrapperName = Swig_name_wrapper(functionName);
+ int maxargs = 0;
+
+ /* Generate the dispatch function */
+ String *dispatch = Swig_overload_dispatch(node, "return %s(SWIG_GatewayArguments);", &maxargs);
+ String *tmp = NewString("");
+
+ Printv(wrapper->def, "SWIGEXPORT int ", wrapperName, "(SWIG_GatewayParameters) {\n", NIL);
+
+ /* Get the number of the parameters */
+ Wrapper_add_local(wrapper, "argc", "int argc = SWIG_NbInputArgument(pvApiCtx)");
+ Printf(tmp, "int argv[%d] = {", maxargs);
+ for (int j = 0; j < maxargs; ++j) {
+ Printf(tmp, "%s%d", j ? "," : " ", j + 1);
+ }
+ Printf(tmp, "}");
+ Wrapper_add_local(wrapper, "argv", tmp);
+
+ Printf(wrapper->code, "SWIG_Scilab_SetApiContext(pvApiCtx);\n");
+
+ /* Dump the dispatch function */
+ Printv(wrapper->code, dispatch, "\n", NIL);
+ Printf(wrapper->code, "Scierror(999, _(\"No matching function for overload\"));\n");
+ Printf(wrapper->code, "return SWIG_ERROR;\n");
+ Printv(wrapper->code, "}\n", NIL);
+ Wrapper_print(wrapper, wrappersSection);
+
+ Delete(tmp);
+ DelWrapper(wrapper);
+ Delete(dispatch);
+ Delete(wrapperName);
+ }
+
+ /* -----------------------------------------------------------------------
+ * variableWrapper()
+ * ----------------------------------------------------------------------- */
+
+ virtual int variableWrapper(Node *node) {
+
+ /* Get information about variable */
+ String *origVariableName = Getattr(node, "name"); // Ex: Shape::nshapes
+ String *variableName = Getattr(node, "sym:name"); // Ex; Shape_nshapes (can be used for function names, ...)
+ String *smallVariableName = createSmallIdentifierName(variableName, SCILAB_IDENTIFIER_NAME_CHAR_MAX - 4);
+
+ /* Manage GET function */
+ Wrapper *getFunctionWrapper = NewWrapper();
+ String *getFunctionName = Swig_name_get(NSPACE_TODO, variableName);
+ String *scilabGetFunctionName = Swig_name_get(NSPACE_TODO, variableName);
+ String *scilabGetSmallFunctionName = Swig_name_get(NSPACE_TODO, smallVariableName);
+
+ Setattr(node, "wrap:name", getFunctionName);
+ Printv(getFunctionWrapper->def, "SWIGEXPORT int ", getFunctionName, "(SWIG_GatewayParameters) {\n", NIL);
+
+ /* Check the number of input and output */
+ Printf(getFunctionWrapper->def, "SWIG_CheckInputArgument(pvApiCtx, 0, 0);\n");
+ Printf(getFunctionWrapper->def, "SWIG_CheckOutputArgument(pvApiCtx, 0, 1);\n");
+ Printf(getFunctionWrapper->def, "SWIG_Scilab_SetApiContext(pvApiCtx);\n");
+
+ String *varoutTypemap = Swig_typemap_lookup("varout", node, origVariableName, 0);
+ if (varoutTypemap != NULL) {
+ Printf(getFunctionWrapper->code, "SWIG_Scilab_SetOutputPosition(%d);\n", 1);
+ Replaceall(varoutTypemap, "$value", origVariableName);
+ Replaceall(varoutTypemap, "$result", "1");
+ emit_action_code(node, getFunctionWrapper->code, varoutTypemap);
+ Delete(varoutTypemap);
+ }
+ Append(getFunctionWrapper->code, "return SWIG_OK;\n");
+ Append(getFunctionWrapper->code, "}\n");
+ Wrapper_print(getFunctionWrapper, wrappersSection);
+
+ /* Add function to builder table */
+ addFunctionToScilab(scilabGetFunctionName, scilabGetSmallFunctionName, getFunctionName);
+
+ /* Manage SET function */
+ if (is_assignable(node)) {
+ Wrapper *setFunctionWrapper = NewWrapper();
+ String *setFunctionName = Swig_name_set(NSPACE_TODO, variableName);
+ String *scilabSetFunctionName = Swig_name_set(NSPACE_TODO, variableName);
+ String *scilabSetSmallFunctionName = Swig_name_set(NSPACE_TODO, smallVariableName);
+
+ Setattr(node, "wrap:name", setFunctionName);
+ Printv(setFunctionWrapper->def, "SWIGEXPORT int ", setFunctionName, "(SWIG_GatewayParameters) {\n", NIL);
+
+ /* Check the number of input and output */
+ Printf(setFunctionWrapper->def, "SWIG_CheckInputArgument(pvApiCtx, 1, 1);\n");
+ Printf(setFunctionWrapper->def, "SWIG_CheckOutputArgument(pvApiCtx, 0, 1);\n");
+ Printf(setFunctionWrapper->def, "SWIG_Scilab_SetApiContext(pvApiCtx);\n");
+
+ String *varinTypemap = Swig_typemap_lookup("varin", node, origVariableName, 0);
+ if (varinTypemap != NULL) {
+ Replaceall(varinTypemap, "$input", "1");
+ emit_action_code(node, setFunctionWrapper->code, varinTypemap);
+ Delete(varinTypemap);
+ }
+ Append(setFunctionWrapper->code, "return SWIG_OK;\n");
+ Append(setFunctionWrapper->code, "}\n");
+ Wrapper_print(setFunctionWrapper, wrappersSection);
+
+ /* Add function to builder table */
+ addFunctionToScilab(scilabSetFunctionName, scilabSetSmallFunctionName, setFunctionName);
+
+ DelWrapper(setFunctionWrapper);
+ }
+ DelWrapper(getFunctionWrapper);
+
+ return SWIG_OK;
+ }
+
+ /* -----------------------------------------------------------------------
+ * constantWrapper()
+ * ----------------------------------------------------------------------- */
+
+ virtual int constantWrapper(Node *node) {
+
+ /* Get the useful information from the node */
+ String *nodeName = Getattr(node, "name");
+ SwigType *type = Getattr(node, "type");
+ String *constantName = Getattr(node, "sym:name");
+ String *rawValue = Getattr(node, "rawval");
+ String *constantValue = rawValue ? rawValue : Getattr(node, "value");
+ String *constantTypemap = NULL;
+
+ // If feature scilab:const enabled, constants & enums are wrapped to Scilab variables
+ if (GetFlag(node, "feature:scilab:const")) {
+ bool isConstant = ((SwigType_issimple(type)) || (SwigType_type(type) == T_STRING));
+ bool isEnum = (Cmp(nodeType(node), "enumitem") == 0);
+
+ if (isConstant || isEnum) {
+ if (isEnum) {
+ Setattr(node, "type", "double");
+ constantValue = Getattr(node, "value");
+ }
+
+ constantTypemap = Swig_typemap_lookup("scilabconstcode", node, nodeName, 0);
+ if (constantTypemap != NULL) {
+
+ Setattr(node, "wrap:name", constantName);
+ Replaceall(constantTypemap, "$result", constantName);
+ Replaceall(constantTypemap, "$value", constantValue);
+
+ emit_action_code(node, variablesCode, constantTypemap);
+ Delete(constantTypemap);
+ return SWIG_OK;
+ }
+ }
+ }
+
+ /* Create variables for member pointer constants, not supported by typemaps (like Python wrapper does) */
+ if (SwigType_type(type) == T_MPOINTER) {
+ String *wname = Swig_name_wrapper(constantName);
+ String *str = SwigType_str(type, wname);
+ Printf(headerSection, "static %s = %s;\n", str, constantValue);
+ Delete(str);
+ constantValue = wname;
+ }
+
+ // Constant names can have SCILAB_VARIABLE_NAME_CHAR_MAX because of suffixes "_get" added to function
+ String *smallConstantName = createSmallIdentifierName(constantName, SCILAB_IDENTIFIER_NAME_CHAR_MAX - 4);
+
+ /* Create GET function to get the constant value */
+ Wrapper *getFunctionWrapper = NewWrapper();
+ String *getFunctionName = Swig_name_get(NSPACE_TODO, constantName);
+ String *scilabGetSmallFunctionName = Swig_name_get(NSPACE_TODO, smallConstantName);
+ Setattr(node, "wrap:name", getFunctionName);
+ Setattr(node, "wrap:name", getFunctionName);
+ Printv(getFunctionWrapper->def, "SWIGEXPORT int ", getFunctionName, "(SWIG_GatewayParameters) {\n", NIL);
+
+ /* Check the number of input and output */
+ Printf(getFunctionWrapper->def, "SWIG_CheckInputArgument(pvApiCtx, 0, 0);\n");
+ Printf(getFunctionWrapper->def, "SWIG_CheckOutputArgument(pvApiCtx, 0, 1);\n");
+ Printf(getFunctionWrapper->def, "SWIG_Scilab_SetApiContext(pvApiCtx);\n");
+
+ constantTypemap = Swig_typemap_lookup("constcode", node, nodeName, 0);
+ if (constantTypemap != NULL) {
+ Printf(getFunctionWrapper->code, "SWIG_Scilab_SetOutputPosition(%d);\n", 1);
+ Replaceall(constantTypemap, "$value", constantValue);
+ Replaceall(constantTypemap, "$result", "1");
+ emit_action_code(node, getFunctionWrapper->code, constantTypemap);
+ Delete(constantTypemap);
+ }
+
+ /* Dump the wrapper function */
+ Append(getFunctionWrapper->code, "return SWIG_OK;\n");
+ Append(getFunctionWrapper->code, "}\n");
+ Wrapper_print(getFunctionWrapper, wrappersSection);
+
+ /* Add the function to Scilab */
+ addFunctionToScilab(getFunctionName, scilabGetSmallFunctionName, getFunctionName);
+
+ DelWrapper(getFunctionWrapper);
+
+ return SWIG_OK;
+ }
+
+ /* ---------------------------------------------------------------------
+ * enumvalueDeclaration()
+ * --------------------------------------------------------------------- */
+
+ virtual int enumvalueDeclaration(Node *node) {
+ static int iPreviousEnumValue = 0;
+
+ if (GetFlag(node, "feature:scilab:const")) {
+ // Compute the "absolute" value of enum if needed
+ // (most of time enum values are a linked list of relative values)
+ String *enumValue = Getattr(node, "enumvalue");
+ String *enumValueEx = Getattr(node, "enumvalueex");
+
+ // First enum value ?
+ String *firstenumitem = Getattr(node, "firstenumitem");
+ if (firstenumitem) {
+ if (enumValue) {
+ // Value is in 'enumvalue'
+ iPreviousEnumValue = atoi(Char(enumValue));
+ } else if (enumValueEx) {
+ // Or value is in 'enumValueEx'
+ iPreviousEnumValue = atoi(Char(enumValueEx));
+
+ enumValue = NewString("");
+ Printf(enumValue, "%d", iPreviousEnumValue);
+ Setattr(node, "enumvalue", enumValue);
+ }
+ } else if (!enumValue && enumValueEx) {
+ // Value is not specified, set it by incrementing last value
+ enumValue = NewString("");
+ Printf(enumValue, "%d", ++iPreviousEnumValue);
+ Setattr(node, "enumvalue", enumValue);
+ }
+ // Enums in Scilab are mapped to double
+ Setattr(node, "type", "double");
+ }
+
+ return Language::enumvalueDeclaration(node);
+ }
+
+ /* -----------------------------------------------------------------------
+ * addHelperFunctions()
+ * ----------------------------------------------------------------------- */
+
+ void addHelperFunctions() {
+ addFunctionToScilab("SWIG_this", "SWIG_this", "SWIG_this");
+ addFunctionToScilab("SWIG_ptr", "SWIG_ptr", "SWIG_ptr");
+ }
+
+ /* -----------------------------------------------------------------------
+ * addFunctionToScilab()
+ * Declare a wrapped function in Scilab (builder, gateway, XML, ...)
+ * ----------------------------------------------------------------------- */
+
+ void addFunctionToScilab(const_String_or_char_ptr scilabFunctionName, const_String_or_char_ptr scilabSmallFunctionName, const_String_or_char_ptr wrapperFunctionName) {
+ if (!generateBuilder)
+ addFunctionInGatewayHeader(scilabFunctionName, scilabSmallFunctionName, wrapperFunctionName);
+
+ if (generateBuilder) {
+ addFunctionInScriptTable(scilabFunctionName, scilabSmallFunctionName, wrapperFunctionName, builderCode5, builderCode6);
+ }
+
+ if (createLoader) {
+ addFunctionInLoader(scilabFunctionName, scilabSmallFunctionName);
+ }
+
+ if (gatewayXMLFile) {
+ Printf(gatewayXML, "<PRIMITIVE gatewayId=\"%s\" primitiveId=\"%d\" primitiveName=\"%s\"/>\n", gatewayID, primitiveID++, scilabSmallFunctionName);
+ }
+ }
+
+
+ /* -----------------------------------------------------------------------
+ * createBuilderCode()
+ * ----------------------------------------------------------------------- */
+
+ void createBuilderFile(String *outputFilename) {
+ String *builderFilename = NewStringf("builder.sce");
+ builderFile = NewFile(builderFilename, "w", SWIG_output_files());
+ if (!builderFile) {
+ FileErrorDisplay(builderFilename);
+ Exit(EXIT_FAILURE);
+ }
+ emitBanner(builderFile);
+
+ builderFunctionCount = 0;
+ builderCode = NewString("");
+ builderCode5 = NewString("");
+ builderCode6 = NewString("");
+ Printf(builderCode, "mode(-1);\n");
+ Printf(builderCode, "lines(0);\n"); /* Useful for automatic tests */
+
+ // Scilab needs to be in the build directory
+ Printf(builderCode, "originaldir = pwd();\n");
+ Printf(builderCode, "builddir = get_absolute_file_path('builder.sce');\n");
+ Printf(builderCode, "cd(builddir);\n");
+
+ Printf(builderCode, "ilib_verbose(%s);\n", verboseBuildLevel);
+
+ Printf(builderCode, "libs = [];\n");
+
+ // Flags from command line arguments
+ Printf(builderCode, "cflags = \"\";\n");
+ for (int i = 0; i < Len(cflags); i++) {
+ String *cflag = Getitem(cflags, i);
+ Printf(builderCode, "cflags = cflags + \" %s\";\n", cflag);
+ }
+
+ if (Len(ldflags) > 0) {
+ for (int i = 0; i < Len(ldflags); i++) {
+ String *ldflag = Getitem(ldflags, i);
+ if (i == 0) {
+ Printf(builderCode, "ldflags = \"%s\";\n", ldflag);
+ } else {
+ Printf(builderCode, "ldflags = ldflags + \" %s\";\n", ldflag);
+ }
+ }
+ } else {
+ Printf(builderCode, "ldflags = \"\";\n");
+ }
+
+ // External script to set flags
+ if (buildFlagsScript) {
+ Printf(builderCode, "exec(\"%s\");\n", buildFlagsScript);
+ Printf(builderCode, "cflags = cflags + getCompilationFlags();\n");
+ Printf(builderCode, "ldflags = ldflags + getLinkFlags();\n");
+ }
+ // Additional sources
+ Insert(sourceFileList, 0, outputFilename);
+ for (int i = 0; i < Len(sourceFileList); i++) {
+ String *sourceFile = Getitem(sourceFileList, i);
+ if (i == 0) {
+ Printf(builderCode, "files = \"%s\";\n", sourceFile);
+ } else {
+ Printf(builderCode, "files($ + 1) = \"%s\";\n", sourceFile);
+ }
+ }
+
+ Printf(builderCode5, "table = [ ..\n");
+ Printf(builderCode6, "table = [ ..\n");
+ }
+
+ /* -----------------------------------------------------------------------
+ * addFunctionInBuilderCode()
+ * Add a function wrapper in the function table of generated builder script
+ * ----------------------------------------------------------------------- */
+
+ void addFunctionInScriptTable(const_String_or_char_ptr scilabFunctionName, const_String_or_char_ptr scilabSmallFunctionName, const_String_or_char_ptr wrapperFunctionName, String *scriptCode5, String *scriptCode6) {
+ if (++builderFunctionCount % 10 == 0) {
+ Printf(scriptCode5, "];\ntable = [table; ..\n");
+ Printf(scriptCode6, "];\ntable = [table; ..\n");
+ }
+ Printf(scriptCode5, "\"%s\",\"%s\"; ..\n", scilabSmallFunctionName, wrapperFunctionName);
+ Printf(scriptCode6, "\"%s\",\"%s\"; ..\n", scilabFunctionName, wrapperFunctionName);
+ }
+
+ /* -----------------------------------------------------------------------
+ * saveBuilderFile()
+ * ----------------------------------------------------------------------- */
+
+ void saveBuilderFile(String *gatewayName) {
+ Printf(builderCode5, "];\n");
+ Printf(builderCode6, "];\n");
+
+ if (Equal(builderCode5, builderCode6)) {
+ Append(builderCode, builderCode6);
+ } else {
+ Printf(builderCode, "ver = getversion('scilab');\n");
+ Printf(builderCode, "if ver(1) < 6 then\n");
+ Printf(builderCode, " // version is less or equal to 5.5.2\n");
+ Printf(builderCode, " \n");
+ Append(builderCode, builderCode5);
+ Printf(builderCode, " \n");
+ Printf(builderCode, "else\n");
+ Printf(builderCode, " // version is 6.0.0 or more\n");
+ Printf(builderCode, " \n");
+ Append(builderCode, builderCode6);
+ Printf(builderCode, " \n");
+ Printf(builderCode, "end\n");
+ }
+
+ Printf(builderCode, "ierr = 0;\n");
+ Printf(builderCode, "if ~isempty(table) then\n");
+ Printf(builderCode, " ierr = execstr(\"ilib_build(''%s'', table, files, libs, [], ldflags, cflags);\", 'errcatch');\n", gatewayName);
+ Printf(builderCode, " if ierr <> 0 then\n");
+ Printf(builderCode, " err_msg = lasterror();\n");
+ Printf(builderCode, " end\n");
+ Printf(builderCode, "end\n");
+ Printf(builderCode, "cd(originaldir);\n");
+ Printf(builderCode, "if ierr <> 0 then\n");
+ Printf(builderCode, " error(ierr, err_msg);\n");
+ Printf(builderCode, "end\n");
+ Printv(builderFile, builderCode, NIL);
+
+ Delete(builderCode);
+ Delete(builderFile);
+ }
+
+ /* -----------------------------------------------------------------------
+ * createGatewayXMLFile()
+ * This XML file is used by Scilab in the context of internal modules
+ * ----------------------------------------------------------------------- */
+
+ void createGatewayXMLFile(String *gatewayName) {
+ String *gatewayXMLFilename = NewStringf("%s_gateway.xml", gatewayName);
+ gatewayXMLFile = NewFile(gatewayXMLFilename, "w", SWIG_output_files());
+ if (!gatewayXMLFile) {
+ FileErrorDisplay(gatewayXMLFilename);
+ Exit(EXIT_FAILURE);
+ }
+ // Add a slightly modified SWIG banner to the gateway XML ("--modify" is illegal in XML)
+ gatewayXML = NewString("");
+ Printf(gatewayXML, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
+ Printf(gatewayXML, "<!--\n");
+ Swig_banner_target_lang(gatewayXML, "");
+ Printf(gatewayXML, "-->\n");
+ Printf(gatewayXML, "<GATEWAY name=\"%s\">\n", gatewayName);
+
+ primitiveID = 1;
+ }
+
+ /* -----------------------------------------------------------------------
+ * saveGatewayXMLFile()
+ * ----------------------------------------------------------------------- */
+
+ void saveGatewayXMLFile() {
+ Printf(gatewayXML, "</GATEWAY>\n");
+ Printv(gatewayXMLFile, gatewayXML, NIL);
+ Delete(gatewayXMLFile);
+ }
+
+ /* -----------------------------------------------------------------------
+ * startGatewayHeader()
+ * Start the gateway header
+ * ----------------------------------------------------------------------- */
+ void startGatewayHeader(String *gatewayLibraryName) {
+ gatewayHeader = NewString("");
+ Printf(gatewayHeader, "\n");
+
+ gatewayHeaderV6 = NewString("");
+ Printf(gatewayHeaderV6, "#ifdef __cplusplus\n");
+ Printf(gatewayHeaderV6, "extern \"C\" {\n");
+ Printf(gatewayHeaderV6, "#endif\n");
+ Printf(gatewayHeaderV6, "#include \"c_gateway_prototype.h\"\n");
+ Printf(gatewayHeaderV6, "#include \"addfunction.h\"\n");
+ Printf(gatewayHeaderV6, "#ifdef __cplusplus\n");
+ Printf(gatewayHeaderV6, "}\n");
+ Printf(gatewayHeaderV6, "#endif\n");
+ Printf(gatewayHeaderV6, "\n");
+ Printf(gatewayHeaderV6, "#define MODULE_NAME L\"%s\"\n", gatewayLibraryName);
+ Printf(gatewayHeaderV6, "#ifdef __cplusplus\n");
+ Printf(gatewayHeaderV6, "extern \"C\"\n");
+ Printf(gatewayHeaderV6, "#endif\n");
+ Printf(gatewayHeaderV6, "SWIGEXPORT int %s(wchar_t *pwstFuncName) {\n", gatewayLibraryName);
+ Printf(gatewayHeaderV6, "\n");
+ }
+
+ /* -----------------------------------------------------------------------
+ * addFunctionInGatewayHeader()
+ * Add a function in the gateway header
+ * ----------------------------------------------------------------------- */
+
+ void addFunctionInGatewayHeader(const_String_or_char_ptr scilabFunctionName, const_String_or_char_ptr scilabSmallFunctionName, const_String_or_char_ptr wrapperFunctionName) {
+ if (gatewayHeaderV5 == NULL) {
+ gatewayHeaderV5 = NewString("");
+ Printf(gatewayHeaderV5, "static GenericTable Tab[] = {\n");
+ } else
+ Printf(gatewayHeaderV5, ",\n");
+ Printf(gatewayHeaderV5, " {(Myinterfun)sci_gateway, (GT)%s, (char *)\"%s\"}", wrapperFunctionName, scilabSmallFunctionName);
+
+ Printf(gatewayHeaderV6, "if (wcscmp(pwstFuncName, L\"%s\") == 0) { addCStackFunction((wchar_t *)L\"%s\", &%s, (wchar_t *)MODULE_NAME); }\n", scilabFunctionName, scilabFunctionName, wrapperFunctionName);
+ }
+
+ /* -----------------------------------------------------------------------
+ * terminateGatewayHeader()
+ * Terminates the gateway header
+ * ----------------------------------------------------------------------- */
+
+ void terminateGatewayHeader(String *gatewayLibraryName) {
+ Printf(gatewayHeaderV5, "};\n");
+ Printf(gatewayHeaderV5, "\n");
+ Printf(gatewayHeaderV5, "#ifdef __cplusplus\n");
+ Printf(gatewayHeaderV5, "extern \"C\" {\n");
+ Printf(gatewayHeaderV5, "#endif\n");
+ Printf(gatewayHeaderV5, "SWIGEXPORT int C2F(%s)() {\n", gatewayLibraryName);
+ Printf(gatewayHeaderV5, " Rhs = Max(0, Rhs);\n");
+ Printf(gatewayHeaderV5, " if (*(Tab[Fin-1].f) != NULL) {\n");
+ Printf(gatewayHeaderV5, " if(pvApiCtx == NULL) {\n");
+ Printf(gatewayHeaderV5, " pvApiCtx = (StrCtx *)MALLOC(sizeof(StrCtx));\n");
+ Printf(gatewayHeaderV5, " }\n");
+ Printf(gatewayHeaderV5, " pvApiCtx->pstName = (char *)Tab[Fin-1].name;\n");
+ Printf(gatewayHeaderV5, " (*(Tab[Fin-1].f))(Tab[Fin-1].name,(GatefuncH)Tab[Fin-1].F);\n");
+ Printf(gatewayHeaderV5, " }\n");
+ Printf(gatewayHeaderV5, " return 0;\n");
+ Printf(gatewayHeaderV5, "}\n");
+ Printf(gatewayHeaderV5, "\n");
+ Printf(gatewayHeaderV5, "#ifdef __cplusplus\n");
+ Printf(gatewayHeaderV5, "}\n");
+ Printf(gatewayHeaderV5, "#endif\n");
+
+ Printf(gatewayHeaderV6, "return 1;\n");
+ Printf(gatewayHeaderV6, "};\n");
+
+ Printf(gatewayHeader, "#if SWIG_SCILAB_VERSION >= 600\n");
+ Printv(gatewayHeader, gatewayHeaderV6, NIL);
+ Printf(gatewayHeader, "#else\n");
+ Printv(gatewayHeader, gatewayHeaderV5, NIL);
+ Printf(gatewayHeader, "#endif\n");
+ }
+
+
+ /* -----------------------------------------------------------------------
+ * createLoaderScriptFile()
+ * Creates the loader script file (loader.sce)
+ * ----------------------------------------------------------------------- */
+
+ void createLoaderFile(String *gatewayLibraryName) {
+ String *loaderFilename = NewString("loader.sce");
+ loaderFile = NewFile(loaderFilename, "w", SWIG_output_files());
+ if (!loaderFile) {
+ FileErrorDisplay(loaderFilename);
+ Exit(EXIT_FAILURE);
+ }
+
+ emitBanner(loaderFile);
+
+ loaderFunctionCount = 0;
+ loaderScript = NewString("function loader_function()\n");
+ Printf(loaderScript, " p = get_absolute_file_path('loader.sce');\n", gatewayLibraryName);
+ Printf(loaderScript, " [bOK, ilib] = c_link('%s');\n", gatewayLibraryName);
+ Printf(loaderScript, " if bOK then\n");
+ Printf(loaderScript, " ulink(ilib);\n");
+ Printf(loaderScript, " end\n");
+ loaderScript5 = NewString(" list_functions = [ ..\n");
+ loaderScript6 = NewString(" list_functions = [ ..\n");
+ }
+
+ /* -----------------------------------------------------------------------
+ * addFunctionInLoaderScript()
+ * Add a function in the loader script table
+ * ----------------------------------------------------------------------- */
+
+ void addFunctionInLoader(const_String_or_char_ptr scilabFunctionName, const_String_or_char_ptr scilabSmallFunctionName) {
+ if (++loaderFunctionCount % 10 == 0) {
+ Printf(loaderScript5, " ];\n list_functions = [list_functions; ..\n");
+ Printf(loaderScript6, " ];\n list_functions = [list_functions; ..\n");
+ }
+ Printf(loaderScript5, " '%s'; ..\n", scilabSmallFunctionName);
+ Printf(loaderScript6, " '%s'; ..\n", scilabFunctionName);
+ }
+
+ /* -----------------------------------------------------------------------
+ * saveLoaderScriptFile()
+ * Terminates and saves the loader script
+ * ----------------------------------------------------------------------- */
+
+ void saveLoaderFile(String *gatewayLibraryName) {
+ Printf(loaderScript5, " ];\n");
+ Printf(loaderScript6, " ];\n");
+
+ if (Equal(loaderScript5, loaderScript6)) {
+ Append(loaderScript, loaderScript6);
+ } else {
+ Printf(loaderScript, " ver = getversion('scilab');\n");
+ Printf(loaderScript, " if ver(1) < 6 then\n");
+ Printf(loaderScript, " // version is less or equal to 5.5.2\n");
+ Printf(loaderScript, " \n");
+ Append(loaderScript, loaderScript5);
+ Delete(loaderScript5);
+ Printf(loaderScript, " \n");
+ Printf(loaderScript, " else\n");
+ Printf(loaderScript, " // version is 6.0.0 or more\n");
+ Printf(loaderScript, " \n");
+ Append(loaderScript, loaderScript6);
+ Delete(loaderScript6);
+ Printf(loaderScript, " \n");
+ Printf(loaderScript, " end\n");
+ }
+
+ Printf(loaderScript, " addinter(p + '%s' + getdynlibext(), '%s', list_functions);\n", gatewayLibraryName, gatewayLibraryName);
+ Printf(loaderScript, "endfunction\n");
+ Printf(loaderScript, "loader_function();\n");
+ Printf(loaderScript, "clear loader_function;\n");
+ Printv(loaderFile, loaderScript, NIL);
+
+ Delete(loaderScript);
+ Delete(loaderFile);
+ }
+
+ /* -----------------------------------------------------------------------
+ * createSmallIdentifierName()
+ * Create a Scilab small identifier to be used by Scilab 5
+ * ----------------------------------------------------------------------- */
+
+ String* createSmallIdentifierName(String* name, int outputLen = SCILAB_IDENTIFIER_NAME_CHAR_MAX) {
+ char* s = Char(name);
+ int nameLen = Len(s);
+
+ // truncate and preserve common suffix
+ if (outputLen > 4 && nameLen > outputLen) {
+ String* smallName = NewStringWithSize(name, outputLen);
+ char* smallNameStr = (char*) Data(smallName);
+
+ if (s[nameLen-4] == '_' && s[nameLen - 3] == 'g' && s[nameLen - 2] == 'e' && s[nameLen - 1] == 't') {
+ // get
+ memcpy(&smallNameStr[outputLen - 4], &s[nameLen - 4], 4);
+ } else if (s[nameLen-4] == '_' && s[nameLen - 3] == 's' && s[nameLen - 2] == 'e' && s[nameLen - 1] == 't') {
+ // set
+ memcpy(&smallNameStr[outputLen - 4], &s[nameLen - 4], 4);
+ }
+
+ return smallName;
+ }
+
+ return name;
+ }
+
+};
+
+extern "C" Language *swig_scilab(void) {
+ return new SCILAB();
+}
diff --git a/contrib/tools/swig/Source/Modules/swigmain.cxx b/contrib/tools/swig/Source/Modules/swigmain.cxx
new file mode 100644
index 0000000000..d553fe8930
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/swigmain.cxx
@@ -0,0 +1,265 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigmain.cxx
+ *
+ * Simplified Wrapper and Interface Generator (SWIG)
+ *
+ * This file is the main entry point to SWIG. It collects the command
+ * line options, registers built-in language modules, and instantiates
+ * a module for code generation. If adding new language modules
+ * to SWIG, you would modify this file.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include <ctype.h>
+
+/* Module factories. These functions are used to instantiate
+ the built-in language modules. If adding a new language
+ module to SWIG, place a similar function here. Make sure
+ the function has "C" linkage. This is required so that modules
+ can be dynamically loaded in future versions. */
+
+extern "C" {
+ Language *swig_csharp(void);
+ Language *swig_d(void);
+ Language *swig_go(void);
+ Language *swig_guile(void);
+ Language *swig_java(void);
+ Language *swig_javascript(void);
+ Language *swig_lua(void);
+ Language *swig_mzscheme(void);
+ Language *swig_ocaml(void);
+ Language *swig_octave(void);
+ Language *swig_perl5(void);
+ Language *swig_php(void);
+ Language *swig_python(void);
+ Language *swig_r(void);
+ Language *swig_ruby(void);
+ Language *swig_scilab(void);
+ Language *swig_tcl(void);
+ Language *swig_xml(void);
+}
+
+/* Association of command line options to language modules.
+ Place an entry for new language modules here, keeping the
+ list sorted alphabetically. */
+
+static TargetLanguageModule modules[] = {
+ {"-allegrocl", NULL, "ALLEGROCL", Disabled},
+ {"-chicken", NULL, "CHICKEN", Disabled},
+ {"-clisp", NULL, "CLISP", Disabled},
+ {"-cffi", NULL, "CFFI", Disabled},
+ {"-csharp", swig_csharp, "C#", Supported},
+ {"-d", swig_d, "D", Supported},
+ {"-go", swig_go, "Go", Supported},
+ {"-guile", swig_guile, "Guile", Supported},
+ {"-java", swig_java, "Java", Supported},
+ {"-javascript", swig_javascript, "Javascript", Supported},
+ {"-lua", swig_lua, "Lua", Supported},
+ {"-modula3", NULL, "Modula 3", Disabled},
+ {"-mzscheme", swig_mzscheme, "MzScheme/Racket", Experimental},
+ {"-ocaml", swig_ocaml, "OCaml", Experimental},
+ {"-octave", swig_octave, "Octave", Supported},
+ {"-perl", swig_perl5, NULL, Supported},
+ {"-perl5", swig_perl5, "Perl 5", Supported},
+ {"-php", swig_php, NULL, Supported},
+ {"-php5", NULL, "PHP 5", Disabled},
+ {"-php7", swig_php, "PHP 7 or later", Supported},
+ {"-pike", NULL, "Pike", Disabled},
+ {"-python", swig_python, "Python", Supported},
+ {"-r", swig_r, "R (aka GNU S)", Supported},
+ {"-ruby", swig_ruby, "Ruby", Supported},
+ {"-scilab", swig_scilab, "Scilab", Supported},
+ {"-sexp", NULL, "Lisp S-Expressions", Disabled},
+ {"-tcl", swig_tcl, NULL, Supported},
+ {"-tcl8", swig_tcl, "Tcl 8", Supported},
+ {"-uffi", NULL, "Common Lisp / UFFI", Disabled},
+ {"-xml", swig_xml, "XML", Supported},
+ {NULL, NULL, NULL, Disabled}
+};
+
+//-----------------------------------------------------------------
+// main()
+//
+// Main program. Initializes the files and starts the parser.
+//-----------------------------------------------------------------
+
+void SWIG_merge_envopt(const char *env, int oargc, char *oargv[], int *nargc, char ***nargv) {
+ if (!env) {
+ *nargc = oargc;
+ *nargv = (char **)Malloc(sizeof(char *) * (oargc + 1));
+ memcpy(*nargv, oargv, sizeof(char *) * (oargc + 1));
+ return;
+ }
+
+ int argc = 1;
+ int arge = oargc + 1024;
+ char **argv = (char **) Malloc(sizeof(char *) * (arge + 1));
+ char *buffer = (char *) Malloc(2048);
+ char *b = buffer;
+ char *be = b + 1023;
+ const char *c = env;
+ while ((b != be) && *c && (argc < arge)) {
+ while (isspace(*c) && *c)
+ ++c;
+ if (*c) {
+ argv[argc] = b;
+ ++argc;
+ }
+ while ((b != be) && *c && !isspace(*c)) {
+ *(b++) = *(c++);
+ }
+ *b++ = 0;
+ }
+
+ argv[0] = oargv[0];
+ for (int i = 1; (i < oargc) && (argc < arge); ++i, ++argc) {
+ argv[argc] = oargv[i];
+ }
+ argv[argc] = NULL;
+
+ *nargc = argc;
+ *nargv = argv;
+}
+
+static void insert_option(int *argc, char ***argv, int index, char const *start, char const *end) {
+ int new_argc = *argc;
+ char **new_argv = *argv;
+ size_t option_len = end - start;
+
+ // Preserve the NULL pointer at argv[argc]
+ new_argv = (char **)Realloc(new_argv, (new_argc + 2) * sizeof(char *));
+ memmove(&new_argv[index + 1], &new_argv[index], sizeof(char *) * (new_argc + 1 - index));
+ new_argc++;
+
+ new_argv[index] = (char *)Malloc(option_len + 1);
+ memcpy(new_argv[index], start, option_len);
+ new_argv[index][option_len] = '\0';
+
+ *argc = new_argc;
+ *argv = new_argv;
+}
+
+static void merge_options_files(int *argc, char ***argv) {
+ static const int BUFFER_SIZE = 4096;
+ char buffer[BUFFER_SIZE];
+ int i;
+ int insert;
+ char **new_argv = *argv;
+ int new_argc = *argc;
+ FILE *f;
+
+ i = 1;
+ while (i < new_argc) {
+ if (new_argv[i] && new_argv[i][0] == '@' && (f = fopen(&new_argv[i][1], "r"))) {
+ int ci;
+ char *b;
+ char *be = &buffer[BUFFER_SIZE];
+ int quote = 0;
+ bool escape = false;
+
+ new_argc--;
+ memmove(&new_argv[i], &new_argv[i + 1], sizeof(char *) * (new_argc - i));
+ insert = i;
+ b = buffer;
+
+ while ((ci = fgetc(f)) != EOF) {
+ const char c = static_cast<char>(ci);
+ if (escape) {
+ if (b != be) {
+ *b = c;
+ ++b;
+ }
+ escape = false;
+ } else if (c == '\\') {
+ escape = true;
+ } else if (!quote && (c == '\'' || c == '"')) {
+ quote = c;
+ } else if (quote && c == quote) {
+ quote = 0;
+ } else if (isspace(c) && !quote) {
+ if (b != buffer) {
+ insert_option(&new_argc, &new_argv, insert, buffer, b);
+ insert++;
+
+ b = buffer;
+ }
+ } else if (b != be) {
+ *b = c;
+ ++b;
+ }
+ }
+ if (b != buffer)
+ insert_option(&new_argc, &new_argv, insert, buffer, b);
+ fclose(f);
+ } else {
+ ++i;
+ }
+ }
+
+ *argv = new_argv;
+ *argc = new_argc;
+}
+
+int main(int margc, char **margv) {
+ int i;
+ const TargetLanguageModule *language_module = 0;
+
+ int argc;
+ char **argv;
+
+ SWIG_merge_envopt(getenv("SWIG_FEATURES"), margc, margv, &argc, &argv);
+ merge_options_files(&argc, &argv);
+
+ Swig_init_args(argc, argv);
+
+ /* Get options */
+ for (i = 1; i < argc; i++) {
+ if (argv[i]) {
+ bool is_target_language_module = false;
+ for (int j = 0; modules[j].name; j++) {
+ if (strcmp(modules[j].name, argv[i]) == 0) {
+ language_module = &modules[j];
+ is_target_language_module = true;
+ break;
+ }
+ }
+ if (is_target_language_module) {
+ Swig_mark_arg(i);
+ if (language_module->status == Disabled) {
+ if (language_module->help)
+ Printf(stderr, "Target language option %s (%s) is no longer supported.\n", language_module->name, language_module->help);
+ else
+ Printf(stderr, "Target language option %s is no longer supported.\n", language_module->name);
+ Exit(EXIT_FAILURE);
+ }
+ } else if ((strcmp(argv[i], "-help") == 0) || (strcmp(argv[i], "--help") == 0)) {
+ if (strcmp(argv[i], "--help") == 0)
+ strcpy(argv[i], "-help");
+ Printf(stdout, "Supported Target Language Options\n");
+ for (int j = 0; modules[j].name; j++) {
+ if (modules[j].help && modules[j].status == Supported) {
+ Printf(stdout, " %-15s - Generate %s wrappers\n", modules[j].name, modules[j].help);
+ }
+ }
+ Printf(stdout, "\nExperimental Target Language Options\n");
+ for (int j = 0; modules[j].name; j++) {
+ if (modules[j].help && modules[j].status == Experimental) {
+ Printf(stdout, " %-15s - Generate %s wrappers\n", modules[j].name, modules[j].help);
+ }
+ }
+ // Swig_mark_arg not called as the general -help options also need to be displayed later on
+ }
+ }
+ }
+
+ int res = SWIG_main(argc, argv, language_module);
+
+ return res;
+}
diff --git a/contrib/tools/swig/Source/Modules/swigmod.h b/contrib/tools/swig/Source/Modules/swigmod.h
new file mode 100644
index 0000000000..c605edf9d0
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/swigmod.h
@@ -0,0 +1,463 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigmod.h
+ *
+ * Main header file for SWIG modules.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_SWIGMOD_H
+#define SWIG_SWIGMOD_H
+
+#include "swig.h"
+#include "preprocessor.h"
+#include "swigwarn.h"
+
+#define NOT_VIRTUAL 0
+#define PLAIN_VIRTUAL 1
+#define PURE_VIRTUAL 2
+
+extern String *input_file;
+extern int line_number;
+extern int start_line;
+extern int CPlusPlus; // C++ mode
+extern int Extend; // Extend mode
+extern int Verbose;
+extern int IsVirtual;
+extern int ImportMode;
+extern int NoExcept; // -no_except option
+extern int Abstract; // abstract base class
+extern int SmartPointer; // smart pointer methods being emitted
+extern int SwigRuntime;
+
+/* Overload "argc" and "argv" */
+extern String *argv_template_string;
+extern String *argc_template_string;
+
+/* Miscellaneous stuff */
+
+#define tab2 " "
+#define tab4 " "
+#define tab8 " "
+
+class Dispatcher {
+public:
+
+ Dispatcher ():cplus_mode(PUBLIC) {
+ }
+ virtual ~ Dispatcher () {
+ }
+
+ virtual int emit_one(Node *n);
+ virtual int emit_children(Node *n);
+ virtual int defaultHandler(Node *n);
+
+ /* Top of the parse tree */
+ virtual int top(Node *n) = 0;
+
+ /* SWIG directives */
+
+ virtual int applyDirective(Node *n);
+ virtual int clearDirective(Node *n);
+ virtual int constantDirective(Node *n);
+ virtual int extendDirective(Node *n);
+ virtual int fragmentDirective(Node *n);
+ virtual int importDirective(Node *n);
+ virtual int includeDirective(Node *n);
+ virtual int insertDirective(Node *n);
+ virtual int moduleDirective(Node *n);
+ virtual int nativeDirective(Node *n);
+ virtual int pragmaDirective(Node *n);
+ virtual int typemapDirective(Node *n);
+ virtual int typemapitemDirective(Node *n);
+ virtual int typemapcopyDirective(Node *n);
+ virtual int typesDirective(Node *n);
+
+ /* C/C++ parsing */
+
+ virtual int cDeclaration(Node *n);
+ virtual int externDeclaration(Node *n);
+ virtual int enumDeclaration(Node *n);
+ virtual int enumvalueDeclaration(Node *n);
+ virtual int enumforwardDeclaration(Node *n);
+ virtual int classDeclaration(Node *n);
+ virtual int classforwardDeclaration(Node *n);
+ virtual int constructorDeclaration(Node *n);
+ virtual int destructorDeclaration(Node *n);
+ virtual int accessDeclaration(Node *n);
+ virtual int usingDeclaration(Node *n);
+ virtual int namespaceDeclaration(Node *n);
+ virtual int templateDeclaration(Node *n);
+ virtual int lambdaDeclaration(Node *n);
+
+ enum AccessMode { PUBLIC, PRIVATE, PROTECTED };
+
+protected:
+ AccessMode cplus_mode;
+};
+
+/* ----------------------------------------------------------------------------
+ * class language:
+ *
+ * This class defines the functions that need to be supported by the
+ * scripting language being used. The translator calls these virtual
+ * functions to output different types of code for different languages.
+ * ------------------------------------------------------------------------- */
+
+class Language:public Dispatcher {
+public:
+ Language();
+ virtual ~Language();
+ virtual int emit_one(Node *n);
+
+ String *directorClassName(Node *n);
+
+ /* Parse command line options */
+
+ virtual void main(int argc, char *argv[]);
+
+ /* Top of the parse tree */
+
+ virtual int top(Node *n);
+
+ /* SWIG directives */
+
+
+ virtual int applyDirective(Node *n);
+ virtual int clearDirective(Node *n);
+ virtual int constantDirective(Node *n);
+ virtual int extendDirective(Node *n);
+ virtual int fragmentDirective(Node *n);
+ virtual int importDirective(Node *n);
+ virtual int includeDirective(Node *n);
+ virtual int insertDirective(Node *n);
+ virtual int moduleDirective(Node *n);
+ virtual int nativeDirective(Node *n);
+ virtual int pragmaDirective(Node *n);
+ virtual int typemapDirective(Node *n);
+ virtual int typemapcopyDirective(Node *n);
+ virtual int typesDirective(Node *n);
+
+ /* C/C++ parsing */
+
+ virtual int cDeclaration(Node *n);
+ virtual int externDeclaration(Node *n);
+ virtual int enumDeclaration(Node *n);
+ virtual int enumvalueDeclaration(Node *n);
+ virtual int enumforwardDeclaration(Node *n);
+ virtual int classDeclaration(Node *n);
+ virtual int classforwardDeclaration(Node *n);
+ virtual int constructorDeclaration(Node *n);
+ virtual int destructorDeclaration(Node *n);
+ virtual int accessDeclaration(Node *n);
+ virtual int namespaceDeclaration(Node *n);
+ virtual int usingDeclaration(Node *n);
+
+ /* Function handlers */
+
+ virtual int functionHandler(Node *n);
+ virtual int globalfunctionHandler(Node *n);
+ virtual int memberfunctionHandler(Node *n);
+ virtual int staticmemberfunctionHandler(Node *n);
+ virtual int callbackfunctionHandler(Node *n);
+
+ /* Variable handlers */
+
+ virtual int variableHandler(Node *n);
+ virtual int globalvariableHandler(Node *n);
+ virtual int membervariableHandler(Node *n);
+ virtual int staticmembervariableHandler(Node *n);
+
+ /* C++ handlers */
+
+ virtual int memberconstantHandler(Node *n);
+ virtual int constructorHandler(Node *n);
+ virtual int copyconstructorHandler(Node *n);
+ virtual int destructorHandler(Node *n);
+ virtual int classHandler(Node *n);
+
+ /* Miscellaneous */
+
+ virtual int typedefHandler(Node *n);
+
+ /* Low-level code generation */
+
+ virtual int constantWrapper(Node *n);
+ virtual int variableWrapper(Node *n);
+ virtual int functionWrapper(Node *n);
+ virtual int nativeWrapper(Node *n);
+
+ /* C++ director class generation */
+ virtual int classDirector(Node *n);
+ virtual int classDirectorInit(Node *n);
+ virtual int classDirectorEnd(Node *n);
+ virtual int unrollVirtualMethods(Node *n, Node *parent, List *vm, int &virtual_destructor, int protectedbase = 0);
+ virtual int classDirectorConstructor(Node *n);
+ virtual int classDirectorDefaultConstructor(Node *n);
+ virtual int classDirectorMethod(Node *n, Node *parent, String *super);
+ virtual int classDirectorConstructors(Node *n);
+ virtual int classDirectorDestructor(Node *n);
+ virtual int classDirectorMethods(Node *n);
+ virtual int classDirectorDisown(Node *n);
+
+ /* Miscellaneous */
+ virtual int validIdentifier(String *s); /* valid identifier? */
+ virtual int addSymbol(const String *s, const Node *n, const_String_or_char_ptr scope = ""); /* Add symbol */
+ virtual int addInterfaceSymbol(const String *interface_name, Node *n, const_String_or_char_ptr scope = "");
+ virtual void dumpSymbols();
+ virtual Node *symbolLookup(const String *s, const_String_or_char_ptr scope = ""); /* Symbol lookup */
+ virtual Hash* symbolAddScope(const_String_or_char_ptr scope);
+ virtual Hash* symbolScopeLookup(const_String_or_char_ptr scope);
+ virtual Hash* symbolScopePseudoSymbolLookup(const_String_or_char_ptr scope);
+ static Node *classLookup(const SwigType *s); /* Class lookup */
+ static Node *enumLookup(SwigType *s); /* Enum lookup */
+ virtual int abstractClassTest(Node *n); /* Is class really abstract? */
+ virtual int is_assignable(Node *n); /* Is variable assignable? */
+ virtual String *runtimeCode(); /* returns the language specific runtime code */
+ virtual String *defaultExternalRuntimeFilename(); /* the default filename for the external runtime */
+ virtual void replaceSpecialVariables(String *method, String *tm, Parm *parm); /* Language specific special variable substitutions for $typemap() */
+
+ /* Runtime is C++ based, so extern "C" header section */
+ void enable_cplus_runtime_mode();
+
+ /* Returns the cplus_runtime mode */
+ int cplus_runtime_mode();
+
+ /* Allow director related code generation */
+ void allow_directors(int val = 1);
+
+ /* Return true if directors are enabled */
+ int directorsEnabled() const;
+
+ /* Allow director protected members related code generation */
+ void allow_dirprot(int val = 1);
+
+ /* Allow all protected members code generation (for directors) */
+ void allow_allprotected(int val = 0);
+
+ /* Returns the dirprot mode */
+ int dirprot_mode() const;
+
+ /* Check if the non public constructor is needed (for directors) */
+ int need_nonpublic_ctor(Node *n);
+
+ /* Check if the non public member is needed (for directors) */
+ int need_nonpublic_member(Node *n);
+
+ /* Set none comparison string */
+ void setSubclassInstanceCheck(String *s);
+
+ /* Set overload variable templates argc and argv */
+ void setOverloadResolutionTemplates(String *argc, String *argv);
+
+ /* Language instance is a singleton - get instance */
+ static Language* instance();
+
+protected:
+ /* Allow multiple-input typemaps */
+ void allow_multiple_input(int val = 1);
+
+ /* Allow overloaded functions */
+ void allow_overloading(int val = 1);
+
+ /* Wrapping class query */
+ int is_wrapping_class() const;
+
+ /* Return the node for the current class */
+ Node *getCurrentClass() const;
+
+ /* Return C++ mode */
+ int getCPlusMode() const;
+
+ /* Return the namespace for the class/enum - the nspace feature */
+ String *getNSpace() const;
+
+ /* Return the real name of the current class */
+ String *getClassName() const;
+
+ /* Return the classes hash */
+ Hash *getClassHash() const;
+
+ /* Return the current class prefix */
+ String *getClassPrefix() const;
+
+ /* Return the current enum class prefix */
+ String *getEnumClassPrefix() const;
+
+ /* Fully qualified type name to use */
+ String *getClassType() const;
+
+ /* Return true if the current method is part of a smart-pointer */
+ int is_smart_pointer() const;
+
+ /* Return the name to use for the given parameter. */
+ virtual String *makeParameterName(Node *n, Parm *p, int arg_num, bool setter = false) const;
+
+ /* Some language modules require additional wrappers for virtual methods not declared in sub-classes */
+ virtual bool extraDirectorProtectedCPPMethodsRequired() const;
+
+public:
+ enum NestedClassSupport {
+ NCS_None, // Target language does not have an equivalent to nested classes
+ NCS_Full, // Target language does have an equivalent to nested classes and is fully implemented
+ NCS_Unknown // Target language may or may not have an equivalent to nested classes. If it does, it has not been implemented yet.
+ };
+ /* Does target language support nested classes? Default is NCS_Unknown.
+ If NCS_Unknown is returned, then the nested classes will be ignored unless
+ %feature "flatnested" is applied to them, in which case they will appear in global space.
+ If the target language does not support the notion of class
+ nesting, the language module should return NCS_None from this function, and
+ the nested classes will be moved to the global scope (like implicit global %feature "flatnested").
+ */
+ virtual NestedClassSupport nestedClassesSupport() const;
+
+ /* Returns true if the target language supports key word arguments (kwargs) */
+ virtual bool kwargsSupport() const;
+
+protected:
+ /* Identifies if a protected members that are generated when the allprotected option is used.
+ This does not include protected virtual methods as they are turned on with the dirprot option. */
+ bool isNonVirtualProtectedAccess(Node *n) const;
+
+ /* Identify if a wrapped global or member variable n should use the naturalvar feature */
+ int use_naturalvar_mode(Node *n) const;
+
+ /* Director subclass comparison test */
+ String *none_comparison;
+
+ /* Director constructor "template" code */
+ String *director_ctor_code;
+
+ /* Director 'protected' constructor "template" code */
+ String *director_prot_ctor_code;
+
+ /* Director allows multiple inheritance */
+ int director_multiple_inheritance;
+
+ /* Director language module */
+ int director_language;
+
+ /* Used to translate Doxygen comments to target documentation format */
+ class DoxygenTranslator *doxygenTranslator;
+
+private:
+ void unrollOneVirtualMethod(String *classname, Node *n, Node *parent, List *vm, int &virtual_destructor, int protectedbase);
+
+ Hash *symtabs; /* symbol tables */
+ int overloading;
+ int multiinput;
+ int cplus_runtime;
+ int directors;
+ static Language *this_;
+};
+
+extern "C" {
+ void SWIG_typemap_lang(const char *);
+ typedef Language *(*ModuleFactory) (void);
+}
+
+enum Status {Disabled, Experimental, Supported};
+
+struct TargetLanguageModule {
+ const char *name;
+ ModuleFactory fac;
+ const char *help;
+ Status status;
+};
+
+int SWIG_main(int argc, char *argv[], const TargetLanguageModule *tlm);
+void emit_parameter_variables(ParmList *l, Wrapper *f);
+void emit_return_variable(Node *n, SwigType *rt, Wrapper *f);
+void SWIG_config_file(const_String_or_char_ptr );
+const String *SWIG_output_directory();
+void SWIG_config_cppext(const char *ext);
+void Swig_print_xml(Node *obj, String *filename);
+
+/* get the list of generated files */
+List *SWIG_output_files();
+
+void SWIG_library_directory(const char *);
+int emit_num_arguments(ParmList *);
+int emit_num_required(ParmList *);
+int emit_isvarargs(ParmList *p);
+bool emit_isvarargs_function(Node *n);
+void emit_attach_parmmaps(ParmList *, Wrapper *f);
+void emit_mark_varargs(ParmList *l);
+String *emit_action(Node *n);
+int emit_action_code(Node *n, String *wrappercode, String *action);
+void Swig_overload_check(Node *n);
+String *Swig_overload_dispatch(Node *n, const_String_or_char_ptr fmt, int *, const_String_or_char_ptr fmt_fastdispatch = 0);
+String *Swig_overload_dispatch_cast(Node *n, const_String_or_char_ptr fmt, int *);
+List *Swig_overload_rank(Node *n, bool script_lang_wrapping);
+SwigType *cplus_value_type(SwigType *t);
+
+/* directors.cxx start */
+String *Swig_csuperclass_call(String *base, String *method, ParmList *l);
+String *Swig_class_declaration(Node *n, String *name);
+String *Swig_class_name(Node *n);
+String *Swig_method_call(const_String_or_char_ptr name, ParmList *parms);
+String *Swig_method_decl(SwigType *return_base_type, SwigType *decl, const_String_or_char_ptr id, List *args, int default_args);
+String *Swig_director_declaration(Node *n);
+void Swig_director_emit_dynamic_cast(Node *n, Wrapper *f);
+void Swig_director_parms_fixup(ParmList *parms);
+bool Swig_director_can_unwrap(Node *n);
+/* directors.cxx end */
+
+/* Utilities */
+
+int is_public(Node *n);
+int is_private(Node *n);
+int is_protected(Node *n);
+int is_member_director(Node *parentnode, Node *member);
+int is_member_director(Node *member);
+int is_non_virtual_protected_access(Node *n); /* Check if the non-virtual protected members are required (for directors) */
+
+void Wrapper_virtual_elimination_mode_set(int);
+void Wrapper_fast_dispatch_mode_set(int);
+void Wrapper_cast_dispatch_mode_set(int);
+void Wrapper_naturalvar_mode_set(int);
+
+void clean_overloaded(Node *n);
+
+extern "C" {
+ const char *Swig_to_string(DOH *object, int count = -1);
+ const char *Swig_to_string_with_location(DOH *object, int count = -1);
+ void Swig_print(DOH *object, int count = -1);
+ void Swig_print_with_location(DOH *object, int count = -1);
+}
+
+void Swig_default_allocators(Node *n);
+void Swig_process_types(Node *n);
+
+/* Contracts */
+void Swig_contracts(Node *n);
+void Swig_contract_mode_set(int flag);
+int Swig_contract_mode_get();
+
+/* Nested classes */
+void Swig_nested_process_classes(Node *n);
+void Swig_nested_name_unnamed_c_structs(Node *n);
+
+/* Interface feature */
+void Swig_interface_feature_enable();
+void Swig_interface_propagate_methods(Node *n);
+
+/* Miscellaneous */
+template <class T> class save_value {
+ T _value;
+ T& _value_ptr;
+ save_value(const save_value&);
+ save_value& operator=(const save_value&);
+
+public:
+ save_value(T& value) : _value(value), _value_ptr(value) {}
+ save_value(T& value, T new_val) : _value(value), _value_ptr(value) { value = new_val; }
+ ~save_value() { _value_ptr = _value; }
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Modules/tcl8.cxx b/contrib/tools/swig/Source/Modules/tcl8.cxx
new file mode 100644
index 0000000000..975230e849
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/tcl8.cxx
@@ -0,0 +1,1291 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * tcl8.cxx
+ *
+ * Tcl8 language module for SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+static const char *usage = "\
+Tcl 8 Options (available with -tcl8)\n\
+ -itcl - Enable ITcl support\n\
+ -nosafe - Leave out SafeInit module function.\n\
+ -prefix <name> - Set a prefix <name> to be prepended to all names\n\
+ -namespace - Build module into a Tcl 8 namespace\n\
+ -pkgversion - Set package version\n\n";
+
+static String *cmd_tab = 0; /* Table of command names */
+static String *var_tab = 0; /* Table of global variables */
+static String *const_tab = 0; /* Constant table */
+static String *methods_tab = 0; /* Methods table */
+static String *attr_tab = 0; /* Attribute table */
+static String *prefix = 0;
+static String *module = 0;
+static int namespace_option = 0;
+static String *init_name = 0;
+static String *ns_name = 0;
+static int have_constructor;
+static String *constructor_name;
+static int have_destructor;
+static int have_base_classes;
+static String *destructor_action = 0;
+static String *version = (String *) "0.0";
+static String *class_name = 0;
+
+static int have_attributes;
+static int have_methods;
+static int nosafe = 0;
+
+static File *f_header = 0;
+static File *f_wrappers = 0;
+static File *f_init = 0;
+static File *f_begin = 0;
+static File *f_runtime = 0;
+
+
+// Itcl support
+static int itcl = 0;
+static File *f_shadow = 0;
+static File *f_shadow_stubs = 0;
+
+static String *constructor = 0;
+static String *destructor = 0;
+static String *base_classes = 0;
+static String *base_class_init = 0;
+static String *methods = 0;
+static String *imethods = 0;
+static String *attributes = 0;
+static String *attribute_traces = 0;
+static String *iattribute_traces = 0;
+
+
+
+class TCL8:public Language {
+public:
+
+ /* ------------------------------------------------------------
+ * TCL8::main()
+ * ------------------------------------------------------------ */
+
+ virtual void main(int argc, char *argv[]) {
+
+ SWIG_library_directory("tcl");
+
+ for (int i = 1; i < argc; i++) {
+ if (argv[i]) {
+ if (strcmp(argv[i], "-prefix") == 0) {
+ if (argv[i + 1]) {
+ prefix = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ } else
+ Swig_arg_error();
+ } else if (strcmp(argv[i], "-pkgversion") == 0) {
+ if (argv[i + 1]) {
+ version = NewString(argv[i + 1]);
+ Swig_mark_arg(i);
+ Swig_mark_arg(i + 1);
+ i++;
+ }
+ } else if (strcmp(argv[i], "-namespace") == 0) {
+ namespace_option = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-itcl") == 0) {
+ itcl = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nosafe") == 0) {
+ nosafe = 1;
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-help") == 0) {
+ fputs(usage, stdout);
+ } else if (strcmp(argv[i], "-cppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is now always on.\n", argv[i]);
+ Swig_mark_arg(i);
+ } else if (strcmp(argv[i], "-nocppcast") == 0) {
+ Printf(stderr, "Deprecated command line option: %s. This option is no longer supported.\n", argv[i]);
+ Swig_mark_arg(i);
+ Exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ Preprocessor_define("SWIGTCL 1", 0);
+ // SWIGTCL8 is deprecated, and no longer documented.
+ Preprocessor_define("SWIGTCL8 1", 0);
+ SWIG_typemap_lang("tcl8");
+ SWIG_config_file("tcl8.swg");
+ allow_overloading();
+ }
+
+ /* ------------------------------------------------------------
+ * top()
+ * ------------------------------------------------------------ */
+
+ virtual int top(Node *n) {
+
+ /* Initialize all of the output files */
+ String *outfile = Getattr(n, "outfile");
+
+ f_begin = NewFile(outfile, "w", SWIG_output_files());
+ if (!f_begin) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ f_runtime = NewString("");
+ f_init = NewString("");
+ f_header = NewString("");
+ f_wrappers = NewString("");
+
+ /* Register file targets with the SWIG file handler */
+ Swig_register_filebyname("header", f_header);
+ Swig_register_filebyname("wrapper", f_wrappers);
+ Swig_register_filebyname("begin", f_begin);
+ Swig_register_filebyname("runtime", f_runtime);
+ Swig_register_filebyname("init", f_init);
+
+ /* Initialize some variables for the object interface */
+
+ cmd_tab = NewString("");
+ var_tab = NewString("");
+ methods_tab = NewString("");
+ const_tab = NewString("");
+
+ Swig_banner(f_begin);
+
+ Swig_obligatory_macros(f_runtime, "TCL");
+
+ /* Set the module name, namespace, and prefix */
+
+ module = NewStringf("%(lower)s", Getattr(n, "name"));
+ init_name = NewStringf("%(title)s_Init", module);
+
+ ns_name = prefix ? Copy(prefix) : Copy(module);
+ if (prefix)
+ Append(prefix, "_");
+
+
+ /* If shadow classing is enabled, we're going to change the module name to "_module" */
+ if (itcl) {
+ String *filen;
+ filen = NewStringf("%s%s.itcl", SWIG_output_directory(), module);
+
+ Insert(module, 0, "_");
+
+ if ((f_shadow = NewFile(filen, "w", SWIG_output_files())) == 0) {
+ FileErrorDisplay(filen);
+ Exit(EXIT_FAILURE);
+ }
+ f_shadow_stubs = NewString("");
+
+ Swig_register_filebyname("shadow", f_shadow);
+ Swig_register_filebyname("itcl", f_shadow);
+
+ Swig_banner_target_lang(f_shadow, "#");
+
+ Printv(f_shadow, "\npackage require Itcl\n\n", NIL);
+ Delete(filen);
+ }
+
+ /* Generate some macros used throughout code generation */
+
+ Printf(f_header, "#define SWIG_init %s\n", init_name);
+ Printf(f_header, "#define SWIG_name \"%s\"\n", module);
+ if (namespace_option) {
+ Printf(f_header, "#define SWIG_prefix \"%s::\"\n", ns_name);
+ Printf(f_header, "#define SWIG_namespace \"%s\"\n\n", ns_name);
+ } else {
+ Printf(f_header, "#define SWIG_prefix \"%s\"\n", prefix);
+ }
+ Printf(f_header, "#define SWIG_version \"%s\"\n", version);
+
+ Printf(cmd_tab, "\nstatic swig_command_info swig_commands[] = {\n");
+ Printf(var_tab, "\nstatic swig_var_info swig_variables[] = {\n");
+ Printf(const_tab, "\nstatic swig_const_info swig_constants[] = {\n");
+
+ Printf(f_wrappers, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
+
+ /* Start emitting code */
+ Language::top(n);
+
+ /* Done. Close up the module */
+ Printv(cmd_tab, tab4, "{0, 0, 0}\n", "};\n", NIL);
+ Printv(var_tab, tab4, "{0,0,0,0}\n", "};\n", NIL);
+ Printv(const_tab, tab4, "{0,0,0,0,0,0}\n", "};\n", NIL);
+
+ Printv(f_wrappers, cmd_tab, var_tab, const_tab, NIL);
+
+ /* Dump the pointer equivalency table */
+ SwigType_emit_type_table(f_runtime, f_wrappers);
+
+ Printf(f_wrappers, "#ifdef __cplusplus\n}\n#endif\n");
+
+ /* Close the init function and quit */
+ Printf(f_init, "return TCL_OK;\n}\n");
+
+ if (!nosafe) {
+ Printf(f_init, "SWIGEXPORT int %(title)s_SafeInit(Tcl_Interp *interp) {\n", module);
+ Printf(f_init, " return SWIG_init(interp);\n");
+ Printf(f_init, "}\n");
+ }
+
+ if (itcl) {
+ Printv(f_shadow, f_shadow_stubs, "\n", NIL);
+ Delete(f_shadow);
+ }
+
+ /* Close all of the files */
+ Dump(f_runtime, f_begin);
+ Printv(f_begin, f_header, f_wrappers, NIL);
+ Wrapper_pretty_print(f_init, f_begin);
+ Delete(f_header);
+ Delete(f_wrappers);
+ Delete(f_init);
+ Delete(f_runtime);
+ Delete(f_begin);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * functionWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int functionWrapper(Node *n) {
+ String *name = Getattr(n, "name"); /* Like to get rid of this */
+ String *iname = Getattr(n, "sym:name");
+ SwigType *type = Getattr(n, "type");
+ ParmList *parms = Getattr(n, "parms");
+ String *overname = 0;
+
+ Parm *p;
+ int i;
+ String *tm;
+ Wrapper *f;
+ String *incode, *cleanup, *outarg, *argstr, *args;
+ int num_arguments = 0;
+ int num_required = 0;
+ int varargs = 0;
+
+ char source[64];
+
+ if (Getattr(n, "sym:overloaded")) {
+ overname = Getattr(n, "sym:overname");
+ } else {
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+ }
+
+ incode = NewString("");
+ cleanup = NewString("");
+ outarg = NewString("");
+ argstr = NewString("\"");
+ args = NewString("");
+
+ f = NewWrapper();
+
+#ifdef SWIG_USE_RESULTOBJ
+ Wrapper_add_local(f, "resultobj", "Tcl_Obj *resultobj = NULL");
+#endif
+
+
+ String *wname = Swig_name_wrapper(iname);
+ if (overname) {
+ Append(wname, overname);
+ }
+ Setattr(n, "wrap:name", wname);
+
+ Printv(f->def, "SWIGINTERN int\n ", wname, "(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {", NIL);
+
+ // Emit all of the local variables for holding arguments.
+ emit_parameter_variables(parms, f);
+
+ /* Attach standard typemaps */
+ emit_attach_parmmaps(parms, f);
+ Setattr(n, "wrap:parms", parms);
+
+ /* Get number of require and total arguments */
+ num_arguments = emit_num_arguments(parms);
+ num_required = emit_num_required(parms);
+ varargs = emit_isvarargs(parms);
+
+ /* Unmarshal parameters */
+
+ for (i = 0, p = parms; i < num_arguments; i++) {
+ /* Skip ignored arguments */
+
+ while (checkAttribute(p, "tmap:in:numinputs", "0")) {
+ p = Getattr(p, "tmap:in:next");
+ }
+
+ SwigType *pt = Getattr(p, "type");
+ String *ln = Getattr(p, "lname");
+
+ /* Produce string representations of the source and target arguments */
+ sprintf(source, "objv[%d]", i + 1);
+
+ if (i == num_required)
+ Putc('|', argstr);
+ if ((tm = Getattr(p, "tmap:in"))) {
+ String *parse = Getattr(p, "tmap:in:parse");
+ if (!parse) {
+ Replaceall(tm, "$input", source);
+ Setattr(p, "emit:input", source);
+
+ if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
+ Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
+ } else {
+ Replaceall(tm, "$disown", "0");
+ }
+
+ Putc('o', argstr);
+ Printf(args, ",(void *)0");
+ if (i >= num_required) {
+ Printf(incode, "if (objc > %d) {\n", i + 1);
+ }
+ Printf(incode, "%s\n", tm);
+ if (i >= num_required) {
+ Printf(incode, "}\n");
+ }
+ } else {
+ Printf(argstr, "%s", parse);
+ Printf(args, ",&%s", ln);
+ if (Strcmp(parse, "p") == 0) {
+ SwigType *lt = SwigType_ltype(pt);
+ SwigType_remember(pt);
+ if (Cmp(lt, "p.void") == 0) {
+ Printf(args, ",(void *)0");
+ } else {
+ Printf(args, ",SWIGTYPE%s", SwigType_manglestr(pt));
+ }
+ Delete(lt);
+ }
+ }
+ p = Getattr(p, "tmap:in:next");
+ continue;
+ } else {
+ Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
+ }
+ p = nextSibling(p);
+ }
+
+ if (!varargs) {
+ Putc(':', argstr);
+ } else {
+ Putc(';', argstr);
+ /* If variable length arguments we need to emit the in typemap here */
+ if (p && (tm = Getattr(p, "tmap:in"))) {
+ sprintf(source, "objv[%d]", i + 1);
+ Printf(incode, "if (objc > %d) {\n", i);
+ Replaceall(tm, "$input", source);
+ Printv(incode, tm, "\n", NIL);
+ Printf(incode, "}\n");
+ }
+ }
+
+ Printf(argstr, "%s\"", usage_string(Char(iname), type, parms));
+
+ Printv(f->code, "if (SWIG_GetArgs(interp, objc, objv,", argstr, args, ") == TCL_ERROR) SWIG_fail;\n", NIL);
+
+ Printv(f->code, incode, NIL);
+
+ /* Insert constraint checking code */
+ for (p = parms; p;) {
+ if ((tm = Getattr(p, "tmap:check"))) {
+ Printv(f->code, tm, "\n", NIL);
+ p = Getattr(p, "tmap:check:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert cleanup code */
+ for (i = 0, p = parms; p; i++) {
+ if (!checkAttribute(p, "tmap:in:numinputs", "0")
+ && !Getattr(p, "tmap:in:parse") && (tm = Getattr(p, "tmap:freearg"))) {
+ if (Len(tm) != 0) {
+ Printv(cleanup, tm, "\n", NIL);
+ }
+ p = Getattr(p, "tmap:freearg:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Insert argument output code */
+ for (i = 0, p = parms; p; i++) {
+ if ((tm = Getattr(p, "tmap:argout"))) {
+#ifdef SWIG_USE_RESULTOBJ
+ Replaceall(tm, "$result", "resultobj");
+#else
+ Replaceall(tm, "$result", "(Tcl_GetObjResult(interp))");
+#endif
+ Replaceall(tm, "$arg", Getattr(p, "emit:input"));
+ Replaceall(tm, "$input", Getattr(p, "emit:input"));
+ Printv(outarg, tm, "\n", NIL);
+ p = Getattr(p, "tmap:argout:next");
+ } else {
+ p = nextSibling(p);
+ }
+ }
+
+ /* Now write code to make the function call */
+ String *actioncode = emit_action(n);
+
+ /* Need to redo all of this code (eventually) */
+
+ /* Return value if necessary */
+ if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
+#ifdef SWIG_USE_RESULTOBJ
+ Replaceall(tm, "$result", "resultobj");
+#else
+ Replaceall(tm, "$result", "(Tcl_GetObjResult(interp))");
+#endif
+ if (GetFlag(n, "feature:new")) {
+ Replaceall(tm, "$owner", "SWIG_POINTER_OWN");
+ } else {
+ Replaceall(tm, "$owner", "0");
+ }
+ Printf(f->code, "%s\n", tm);
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(type, 0), name);
+ }
+ emit_return_variable(n, type, f);
+
+ /* Dump output argument code */
+ Printv(f->code, outarg, NIL);
+
+ /* Dump the argument cleanup code */
+ Printv(f->code, cleanup, NIL);
+
+ /* Look for any remaining cleanup */
+ if (GetFlag(n, "feature:new")) {
+ if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ }
+ }
+
+ if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
+ Printf(f->code, "%s\n", tm);
+ }
+#ifdef SWIG_USE_RESULTOBJ
+ Printv(f->code, "if (resultobj) Tcl_SetObjResult(interp, resultobj);\n", NIL);
+#endif
+ Printv(f->code, "return TCL_OK;\n", NIL);
+ Printv(f->code, "fail:\n", cleanup, "return TCL_ERROR;\n", NIL);
+ Printv(f->code, "}\n", NIL);
+
+ /* Substitute the cleanup code */
+ Replaceall(f->code, "$cleanup", cleanup);
+ Replaceall(f->code, "$symname", iname);
+
+ /* Dump out the function */
+ Wrapper_print(f, f_wrappers);
+
+ if (!Getattr(n, "sym:overloaded")) {
+ /* Register the function with Tcl */
+ Printv(cmd_tab, tab4, "{ SWIG_prefix \"", iname, "\", (swig_wrapper_func) ", Swig_name_wrapper(iname), ", NULL},\n", NIL);
+ } else {
+ if (!Getattr(n, "sym:nextSibling")) {
+ /* Emit overloading dispatch function */
+
+ int maxargs;
+ String *dispatch = Swig_overload_dispatch(n, "return %s(clientData, interp, objc, argv - 1);", &maxargs);
+
+ /* Generate a dispatch wrapper for all overloaded functions */
+
+ Wrapper *df = NewWrapper();
+ String *dname = Swig_name_wrapper(iname);
+
+ Printv(df->def, "SWIGINTERN int\n", dname, "(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {", NIL);
+ Printf(df->code, "Tcl_Obj *CONST *argv = objv+1;\n");
+ Printf(df->code, "int argc = objc-1;\n");
+ Printv(df->code, dispatch, "\n", NIL);
+ Node *sibl = n;
+ while (Getattr(sibl, "sym:previousSibling"))
+ sibl = Getattr(sibl, "sym:previousSibling"); // go all the way up
+ String *protoTypes = NewString("");
+ do {
+ String *fulldecl = Swig_name_decl(sibl);
+ Printf(protoTypes, "\n\" %s\\n\"", fulldecl);
+ Delete(fulldecl);
+ } while ((sibl = Getattr(sibl, "sym:nextSibling")));
+ Printf(df->code, "Tcl_SetResult(interp,(char *) "
+ "\"Wrong number or type of arguments for overloaded function '%s'.\\n\""
+ "\n\" Possible C/C++ prototypes are:\\n\"%s, TCL_STATIC);\n", iname, protoTypes);
+ Delete(protoTypes);
+ Printf(df->code, "return TCL_ERROR;\n");
+ Printv(df->code, "}\n", NIL);
+ Wrapper_print(df, f_wrappers);
+ Printv(cmd_tab, tab4, "{ SWIG_prefix \"", iname, "\", (swig_wrapper_func) ", dname, ", NULL},\n", NIL);
+ DelWrapper(df);
+ Delete(dispatch);
+ Delete(dname);
+ }
+ }
+
+ Delete(incode);
+ Delete(cleanup);
+ Delete(outarg);
+ Delete(argstr);
+ Delete(args);
+ DelWrapper(f);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * variableWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int variableWrapper(Node *n) {
+
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ SwigType *t = Getattr(n, "type");
+
+ String *setname = 0;
+ String *setfname = 0;
+ Wrapper *setf = 0, *getf = 0;
+ int readonly = 0;
+ String *tm;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+
+ /* Create a function for getting a variable */
+ int addfail = 0;
+ getf = NewWrapper();
+ String *getname = Swig_name_get(NSPACE_TODO, iname);
+ String *getfname = Swig_name_wrapper(getname);
+ Setattr(n, "wrap:name", getfname);
+ Printv(getf->def, "SWIGINTERN const char *", getfname, "(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, char *name1, char *name2, int flags) {", NIL);
+ Wrapper_add_local(getf, "value", "Tcl_Obj *value = 0");
+ if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
+ Replaceall(tm, "$result", "value");
+ /* Printf(getf->code, "%s\n",tm); */
+ addfail = emit_action_code(n, getf->code, tm);
+ Printf(getf->code, "if (value) {\n");
+ Printf(getf->code, "Tcl_SetVar2(interp,name1,name2,Tcl_GetStringFromObj(value,NULL), flags);\n");
+ Printf(getf->code, "Tcl_DecrRefCount(value);\n");
+ Printf(getf->code, "}\n");
+ Printf(getf->code, "return NULL;\n");
+ if (addfail) {
+ Append(getf->code, "fail:\n");
+ Printf(getf->code, "return \"%s\";\n", iname);
+ }
+ Printf(getf->code, "}\n");
+ Wrapper_print(getf, f_wrappers);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VAROUT_UNDEF, input_file, line_number, "Unable to read variable of type %s\n", SwigType_str(t, 0));
+ DelWrapper(getf);
+ return SWIG_NOWRAP;
+ }
+ DelWrapper(getf);
+
+ /* Try to create a function setting a variable */
+ if (is_assignable(n)) {
+ setf = NewWrapper();
+ setname = Swig_name_set(NSPACE_TODO, iname);
+ setfname = Swig_name_wrapper(setname);
+ Setattr(n, "wrap:name", setfname);
+ if (setf) {
+ Printv(setf->def, "SWIGINTERN const char *", setfname,
+ "(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, char *name1, char *name2 SWIGUNUSED, int flags) {", NIL);
+ Wrapper_add_local(setf, "value", "Tcl_Obj *value = 0");
+ Wrapper_add_local(setf, "name1o", "Tcl_Obj *name1o = 0");
+
+ if ((tm = Swig_typemap_lookup("varin", n, name, 0))) {
+ Replaceall(tm, "$input", "value");
+ Printf(setf->code, "name1o = Tcl_NewStringObj(name1,-1);\n");
+ Printf(setf->code, "value = Tcl_ObjGetVar2(interp, name1o, 0, flags);\n");
+ Printf(setf->code, "Tcl_DecrRefCount(name1o);\n");
+ Printf(setf->code, "if (!value) SWIG_fail;\n");
+ /* Printf(setf->code,"%s\n", tm); */
+ emit_action_code(n, setf->code, tm);
+ Printf(setf->code, "return NULL;\n");
+ Printf(setf->code, "fail:\n");
+ Printf(setf->code, "return \"%s\";\n", iname);
+ Printf(setf->code, "}\n");
+ Wrapper_print(setf, f_wrappers);
+ } else {
+ Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s.\n", SwigType_str(t, 0));
+ readonly = 1;
+ }
+ }
+ DelWrapper(setf);
+ } else {
+ readonly = 1;
+ }
+
+
+ Printv(var_tab, tab4, "{ SWIG_prefix \"", iname, "\", 0, (swig_variable_func) ", getfname, ",", NIL);
+ if (readonly) {
+ static int readonlywrap = 0;
+ if (!readonlywrap) {
+ Wrapper *ro = NewWrapper();
+ Printf(ro->def,
+ "SWIGINTERN const char *swig_readonly(ClientData clientData SWIGUNUSED, Tcl_Interp *interp SWIGUNUSED, char *name1 SWIGUNUSED, char *name2 SWIGUNUSED, int flags SWIGUNUSED) {");
+ Printv(ro->code, "return \"Variable is read-only\";\n", "}\n", NIL);
+ Wrapper_print(ro, f_wrappers);
+ readonlywrap = 1;
+ DelWrapper(ro);
+ }
+ Printf(var_tab, "(swig_variable_func) swig_readonly},\n");
+ } else {
+ Printv(var_tab, "(swig_variable_func) ", setfname, "},\n", NIL);
+ }
+ Delete(getfname);
+ Delete(setfname);
+ Delete(setname);
+ Delete(getname);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constantWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int constantWrapper(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = Getattr(n, "sym:name");
+ String *nsname = !namespace_option ? Copy(iname) : NewStringf("%s::%s", ns_name, iname);
+ SwigType *type = Getattr(n, "type");
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ String *tm;
+
+ if (!addSymbol(iname, n))
+ return SWIG_ERROR;
+ if (namespace_option)
+ Setattr(n, "sym:name", nsname);
+
+ /* Special hook for member pointer */
+ if (SwigType_type(type) == T_MPOINTER) {
+ String *wname = Swig_name_wrapper(iname);
+ Printf(f_wrappers, "static %s = %s;\n", SwigType_str(type, wname), value);
+ value = Char(wname);
+ }
+
+ if ((tm = Swig_typemap_lookup("consttab", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ Replaceall(tm, "$nsname", nsname);
+ Printf(const_tab, "%s,\n", tm);
+ } else if ((tm = Swig_typemap_lookup("constcode", n, name, 0))) {
+ Replaceall(tm, "$value", value);
+ Replaceall(tm, "$nsname", nsname);
+ Printf(f_init, "%s\n", tm);
+ } else {
+ Delete(nsname);
+ Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value.\n");
+ return SWIG_NOWRAP;
+ }
+ Delete(nsname);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * nativeWrapper()
+ * ------------------------------------------------------------ */
+
+ virtual int nativeWrapper(Node *n) {
+ String *name = Getattr(n, "sym:name");
+ String *funcname = Getattr(n, "wrap:name");
+ if (!addSymbol(funcname, n))
+ return SWIG_ERROR;
+
+ Printf(f_init, "\t Tcl_CreateObjCommand(interp, SWIG_prefix \"%s\", (swig_wrapper_func) %s, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);\n", name,
+ funcname);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int classHandler(Node *n) {
+ static Hash *emitted = NewHash();
+ String *mangled_classname = 0;
+ String *real_classname = 0;
+
+ have_constructor = 0;
+ have_destructor = 0;
+ destructor_action = 0;
+ constructor_name = 0;
+
+ if (itcl) {
+ constructor = NewString("");
+ destructor = NewString("");
+ base_classes = NewString("");
+ base_class_init = NewString("");
+ methods = NewString("");
+ imethods = NewString("");
+ attributes = NewString("");
+ attribute_traces = NewString("");
+ iattribute_traces = NewString("");
+
+ have_base_classes = 0;
+ have_methods = 0;
+ have_attributes = 0;
+ }
+
+ class_name = Getattr(n, "sym:name");
+ if (!addSymbol(class_name, n))
+ return SWIG_ERROR;
+
+ real_classname = Getattr(n, "name");
+ mangled_classname = Swig_name_mangle(real_classname);
+
+ if (Getattr(emitted, mangled_classname))
+ return SWIG_NOWRAP;
+ Setattr(emitted, mangled_classname, "1");
+
+ attr_tab = NewString("");
+ Printf(attr_tab, "static swig_attribute swig_");
+ Printv(attr_tab, mangled_classname, "_attributes[] = {\n", NIL);
+
+ methods_tab = NewStringf("");
+ Printf(methods_tab, "static swig_method swig_");
+ Printv(methods_tab, mangled_classname, "_methods[] = {\n", NIL);
+
+ /* Generate normal wrappers */
+ Language::classHandler(n);
+
+ SwigType *t = Copy(Getattr(n, "name"));
+ SwigType_add_pointer(t);
+
+ // Catch all: eg. a class with only static functions and/or variables will not have 'remembered'
+ // SwigType_remember(t);
+ String *wrap_class = NewStringf("&_wrap_class_%s", mangled_classname);
+ SwigType_remember_clientdata(t, wrap_class);
+
+ String *rt = Copy(getClassType());
+ SwigType_add_pointer(rt);
+
+ // Register the class structure with the type checker
+ /* Printf(f_init,"SWIG_TypeClientData(SWIGTYPE%s, (void *) &_wrap_class_%s);\n", SwigType_manglestr(t), mangled_classname); */
+ if (have_destructor) {
+ Printv(f_wrappers, "SWIGINTERN void swig_delete_", class_name, "(void *obj) {\n", NIL);
+ if (destructor_action) {
+ Printv(f_wrappers, SwigType_str(rt, "arg1"), " = (", SwigType_str(rt, 0), ") obj;\n", NIL);
+ Printv(f_wrappers, destructor_action, "\n", NIL);
+ } else {
+ if (CPlusPlus) {
+ Printv(f_wrappers, " delete (", SwigType_str(rt, 0), ") obj;\n", NIL);
+ } else {
+ Printv(f_wrappers, " free((char *) obj);\n", NIL);
+ }
+ }
+ Printf(f_wrappers, "}\n");
+ }
+
+ Printf(methods_tab, " {0,0}\n};\n");
+ Printv(f_wrappers, methods_tab, NIL);
+
+ Printf(attr_tab, " {0,0,0}\n};\n");
+ Printv(f_wrappers, attr_tab, NIL);
+
+ /* Handle inheritance */
+
+ String *base_class = NewString("");
+ String *base_class_names = NewString("");
+
+ if (itcl) {
+ base_classes = NewString("");
+ }
+
+ List *baselist = Getattr(n, "bases");
+ if (baselist && Len(baselist)) {
+ Iterator b;
+ int index = 0;
+ b = First(baselist);
+ while (b.item) {
+ String *bname = Getattr(b.item, "name");
+ if ((!bname) || GetFlag(b.item, "feature:ignore") || (!Getattr(b.item, "module"))) {
+ b = Next(b);
+ continue;
+ }
+ if (itcl) {
+ have_base_classes = 1;
+ Printv(base_classes, bname, " ", NIL);
+ Printv(base_class_init, " ", bname, "Ptr::constructor $ptr\n", NIL);
+ }
+ String *bmangle = Swig_name_mangle(bname);
+ // Printv(f_wrappers,"extern swig_class _wrap_class_", bmangle, ";\n", NIL);
+ // Printf(base_class,"&_wrap_class_%s",bmangle);
+ Printf(base_class, "0");
+ Printf(base_class_names, "\"%s *\",", SwigType_namestr(bname));
+ /* Put code to register base classes in init function */
+
+ //Printf(f_init,"/* Register base : %s */\n", bmangle);
+ //Printf(f_init,"swig_%s_bases[%d] = (swig_class *) SWIG_TypeQuery(\"%s *\")->clientdata;\n", mangled_classname, index, SwigType_namestr(bname));
+ (void)index;
+ b = Next(b);
+ index++;
+ Putc(',', base_class);
+ Delete(bmangle);
+ }
+ }
+
+ if (itcl) {
+ String *ptrclass = NewString("");
+
+ // First, build the pointer base class
+ Printv(ptrclass, "itcl::class ", class_name, "Ptr {\n", NIL);
+ if (have_base_classes)
+ Printv(ptrclass, " inherit ", base_classes, "\n", NIL);
+
+ // Define protected variables for SWIG object pointer
+ Printv(ptrclass, " protected variable swigobj\n", " protected variable thisown\n", NIL);
+
+ // Define public variables
+ if (have_attributes) {
+ Printv(ptrclass, attributes, NIL);
+
+ // base class swig_getset was being called for complex inheritance trees
+ if (namespace_option) {
+
+ Printv(ptrclass, " protected method ", class_name, "_swig_getset {var name1 name2 op} {\n", NIL);
+
+ Printv(ptrclass,
+ " switch -exact -- $op {\n",
+ " r {set $var [", ns_name, "::", class_name, "_[set var]_get $swigobj]}\n",
+ " w {", ns_name, "::", class_name, "_${var}_set $swigobj [set $var]}\n", " }\n", " }\n", NIL);
+ } else {
+ Printv(ptrclass,
+ " protected method ", class_name, "_swig_getset {var name1 name2 op} {\n",
+ " switch -exact -- $op {\n",
+ " r {set $var [", class_name, "_[set var]_get $swigobj]}\n",
+ " w {", class_name, "_${var}_set $swigobj [set $var]}\n", " }\n", " }\n", NIL);
+ }
+ }
+ // Add the constructor, which may include
+ // calls to base class class constructors
+
+ Printv(ptrclass, " constructor { ptr } {\n", NIL);
+ if (have_base_classes) {
+ Printv(ptrclass, base_class_init, NIL);
+ Printv(ptrclass, " } {\n", NIL);
+ }
+
+ Printv(ptrclass, " set swigobj $ptr\n", " set thisown 0\n", NIL);
+
+ if (have_attributes) {
+ Printv(ptrclass, attribute_traces, NIL);
+ }
+ Printv(ptrclass, " }\n", NIL);
+
+
+ // Add destructor
+ Printv(ptrclass, " destructor {\n",
+ " set d_func delete_", class_name, "\n",
+ " if { $thisown && ([info command $d_func] != \"\") } {\n" " $d_func $swigobj\n", " }\n", " }\n", NIL);
+
+ // Add methods
+ if (have_methods) {
+ Printv(ptrclass, imethods, NIL);
+ }
+
+ // Close out the pointer class
+ Printv(ptrclass, "}\n\n", NIL);
+ Printv(f_shadow, ptrclass, NIL);
+ // pointer class end
+
+
+ // Create the "real" class.
+ Printv(f_shadow, "itcl::class ", class_name, " {\n", NIL);
+ Printv(f_shadow, " inherit ", class_name, "Ptr\n", NIL);
+
+ // If we have a constructor, then use it.
+ // If not, then we must have an abstract class without
+ // any constructor. So we create a class constructor
+ // which will fail for this class (but not for inherited
+ // classes). Note that the constructor must fail before
+ // calling the ptrclass constructor.
+
+ if (have_constructor) {
+ Printv(f_shadow, constructor, NIL);
+ } else {
+ Printv(f_shadow, " constructor { } {\n", NIL);
+ Printv(f_shadow, " # This constructor will fail if called directly\n", NIL);
+ Printv(f_shadow, " if { [info class] == \"::", class_name, "\" } {\n", NIL);
+ Printv(f_shadow, " error \"No constructor for class ", class_name, (Getattr(n, "abstracts") ? " - class is abstract" : ""), "\"\n", NIL);
+ Printv(f_shadow, " }\n", NIL);
+ Printv(f_shadow, " }\n", NIL);
+ }
+
+ Printv(f_shadow, "}\n\n", NIL);
+ }
+
+ Printv(f_wrappers, "static swig_class *swig_", mangled_classname, "_bases[] = {", base_class, "0};\n", NIL);
+ Printv(f_wrappers, "static const char * swig_", mangled_classname, "_base_names[] = {", base_class_names, "0};\n", NIL);
+ Delete(base_class);
+ Delete(base_class_names);
+
+ Printv(f_wrappers, "static swig_class _wrap_class_", mangled_classname, " = { \"", class_name, "\", &SWIGTYPE", SwigType_manglestr(t), ",", NIL);
+
+ if (have_constructor) {
+ Printf(f_wrappers, "%s", Swig_name_wrapper(Swig_name_construct(NSPACE_TODO, constructor_name)));
+ Delete(constructor_name);
+ constructor_name = 0;
+ } else {
+ Printf(f_wrappers, "0");
+ }
+ if (have_destructor) {
+ Printv(f_wrappers, ", swig_delete_", class_name, NIL);
+ } else {
+ Printf(f_wrappers, ",0");
+ }
+ Printv(f_wrappers, ", swig_", mangled_classname, "_methods, swig_", mangled_classname, "_attributes, swig_", mangled_classname, "_bases,",
+ "swig_", mangled_classname, "_base_names, &swig_module, SWIG_TCL_HASHTABLE_INIT };\n", NIL);
+
+ if (!itcl) {
+ Printv(cmd_tab, tab4, "{ SWIG_prefix \"", class_name, "\", (swig_wrapper_func) SWIG_ObjectConstructor, (ClientData)&_wrap_class_", mangled_classname,
+ "},\n", NIL);
+ }
+
+ Delete(t);
+ Delete(mangled_classname);
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * memberfunctionHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int memberfunctionHandler(Node *n) {
+ String *name = Getattr(n, "name");
+ String *iname = GetChar(n, "sym:name");
+
+ String *realname, *rname;
+
+ Language::memberfunctionHandler(n);
+
+ realname = iname ? iname : name;
+ rname = Swig_name_wrapper(Swig_name_member(NSPACE_TODO, class_name, realname));
+ if (!Getattr(n, "sym:nextSibling")) {
+ Printv(methods_tab, tab4, "{\"", realname, "\", ", rname, "}, \n", NIL);
+ }
+
+ if (itcl) {
+ ParmList *l = Getattr(n, "parms");
+ Parm *p = 0;
+ String *pname = NewString("");
+
+ // Add this member to our class handler function
+ Printv(imethods, tab2, "method ", realname, " [list ", NIL);
+
+ int pnum = 0;
+ for (p = l; p; p = nextSibling(p)) {
+
+ String *pn = Getattr(p, "name");
+ String *dv = Getattr(p, "value");
+ SwigType *pt = Getattr(p, "type");
+
+ Printv(pname, ",(", pt, ")", NIL);
+ Clear(pname);
+
+ /* Only print an argument if not void */
+ if (Cmp(pt, "void") != 0) {
+ if (Len(pn) > 0) {
+ Printv(pname, pn, NIL);
+ } else {
+ Printf(pname, "p%d", pnum);
+ }
+
+ if (Len(dv) > 0) {
+ String *defval = NewString(dv);
+ if (namespace_option) {
+ Insert(defval, 0, "::");
+ Insert(defval, 0, ns_name);
+ }
+ if (Strncmp(dv, "(", 1) == 0) {
+ Insert(defval, 0, "$");
+ Replaceall(defval, "(", "");
+ Replaceall(defval, ")", "");
+ }
+ Printv(imethods, "[list ", pname, " ", defval, "] ", NIL);
+ } else {
+ Printv(imethods, pname, " ", NIL);
+ }
+ }
+ ++pnum;
+ }
+ Printv(imethods, "] ", NIL);
+
+ if (namespace_option) {
+ Printv(imethods, "{ ", ns_name, "::", class_name, "_", realname, " $swigobj", NIL);
+ } else {
+ Printv(imethods, "{ ", class_name, "_", realname, " $swigobj", NIL);
+ }
+
+ pnum = 0;
+ for (p = l; p; p = nextSibling(p)) {
+
+ String *pn = Getattr(p, "name");
+ SwigType *pt = Getattr(p, "type");
+ Clear(pname);
+
+ /* Only print an argument if not void */
+ if (Cmp(pt, "void") != 0) {
+ if (Len(pn) > 0) {
+ Printv(pname, pn, NIL);
+ } else {
+ Printf(pname, "p%d", pnum);
+ }
+ Printv(imethods, " $", pname, NIL);
+ }
+ ++pnum;
+ }
+ Printv(imethods, " }\n", NIL);
+ have_methods = 1;
+ }
+
+ Delete(rname);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * membervariableHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int membervariableHandler(Node *n) {
+ String *symname = Getattr(n, "sym:name");
+ String *rname;
+
+ Language::membervariableHandler(n);
+ Printv(attr_tab, tab4, "{ \"-", symname, "\",", NIL);
+ rname = Swig_name_wrapper(Swig_name_get(NSPACE_TODO, Swig_name_member(NSPACE_TODO, class_name, symname)));
+ Printv(attr_tab, rname, ", ", NIL);
+ Delete(rname);
+ if (!GetFlag(n, "feature:immutable")) {
+ rname = Swig_name_wrapper(Swig_name_set(NSPACE_TODO, Swig_name_member(NSPACE_TODO, class_name, symname)));
+ Printv(attr_tab, rname, "},\n", NIL);
+ Delete(rname);
+ } else {
+ Printf(attr_tab, "0 },\n");
+ }
+
+ if (itcl) {
+ Printv(attributes, " public variable ", symname, "\n", NIL);
+
+ Printv(attribute_traces, " trace variable ", symname, " rw [list ", class_name, "_swig_getset ", symname, "]\n", NIL);
+ Printv(attribute_traces, " set ", symname, "\n", NIL);
+
+ have_attributes = 1;
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constructorHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int constructorHandler(Node *n) {
+ Language::constructorHandler(n);
+
+ if (itcl) {
+ String *name = Getattr(n, "name");
+ String *iname = GetChar(n, "sym:name");
+
+ String *realname;
+
+ ParmList *l = Getattr(n, "parms");
+ Parm *p = 0;
+
+ String *pname = NewString("");
+
+ realname = iname ? iname : name;
+
+ if (!have_constructor) {
+ // Add this member to our class handler function
+ Printf(constructor, " constructor { ");
+
+ // Add parameter list
+ int pnum = 0;
+ for (p = l; p; p = nextSibling(p)) {
+
+ SwigType *pt = Getattr(p, "type");
+ String *pn = Getattr(p, "name");
+ String *dv = Getattr(p, "value");
+ Clear(pname);
+
+ /* Only print an argument if not void */
+ if (Cmp(pt, "void") != 0) {
+ if (Len(pn) > 0) {
+ Printv(pname, pn, NIL);
+ } else {
+ Printf(pname, "p%d", pnum);
+ }
+
+ if (Len(dv) > 0) {
+ Printv(constructor, "{", pname, " {", dv, "} } ", NIL);
+ } else {
+ Printv(constructor, pname, " ", NIL);
+ }
+ }
+ ++pnum;
+ }
+ Printf(constructor, "} { \n");
+
+ // [BRE] 08/17/00 Added test to see if we are instantiating this object
+ // type, or, if this constructor is being called as part of the itcl
+ // inheritance hierarchy.
+ // In the former case, we need to call the C++ constructor, in the
+ // latter we don't, or we end up with two C++ objects.
+ // Check to see if we are instantiating a 'realname' or something
+ // derived from it.
+ //
+ Printv(constructor, " if { [string equal -nocase \"", realname, "\" \"[namespace tail [info class]]\" ] } {\n", NIL);
+
+ // Call to constructor wrapper and parent Ptr class
+ // [BRE] add -namespace/-prefix support
+
+ if (namespace_option) {
+ Printv(constructor, " ", realname, "Ptr::constructor [", ns_name, "::new_", realname, NIL);
+ } else {
+ Printv(constructor, " ", realname, "Ptr::constructor [new_", realname, NIL);
+ }
+
+ pnum = 0;
+ for (p = l; p; p = nextSibling(p)) {
+
+ SwigType *pt = Getattr(p, "type");
+ String *pn = Getattr(p, "name");
+ Clear(pname);
+
+ /* Only print an argument if not void */
+ if (Cmp(pt, "void") != 0) {
+ if (Len(pn) > 0) {
+ Printv(pname, pn, NIL);
+ } else {
+ Printf(pname, "p%d", pnum);
+ }
+ Printv(constructor, " $", pname, NIL);
+ }
+ ++pnum;
+ }
+
+ Printv(constructor, "]\n", " }\n", " } {\n", " set thisown 1\n", " }\n", NIL);
+ }
+ }
+
+ if (!have_constructor)
+ constructor_name = NewString(Getattr(n, "sym:name"));
+ have_constructor = 1;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * destructorHandler()
+ * ------------------------------------------------------------ */
+
+ virtual int destructorHandler(Node *n) {
+ Language::destructorHandler(n);
+ have_destructor = 1;
+ destructor_action = Getattr(n, "wrap:action");
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * validIdentifier()
+ * ------------------------------------------------------------ */
+
+ virtual int validIdentifier(String *s) {
+ if (Strchr(s, ' '))
+ return 0;
+ return 1;
+ }
+
+ /* ------------------------------------------------------------
+ * usage_string()
+ * ------------------------------------------------------------ */
+
+ char *usage_string(char *iname, SwigType *, ParmList *l) {
+ static String *temp = 0;
+ Parm *p;
+ int i, numopt, pcount;
+
+ if (!temp)
+ temp = NewString("");
+ Clear(temp);
+ if (namespace_option) {
+ Printf(temp, "%s::%s ", ns_name, iname);
+ } else {
+ Printf(temp, "%s ", iname);
+ }
+ /* Now go through and print parameters */
+ i = 0;
+ pcount = emit_num_arguments(l);
+ numopt = pcount - emit_num_required(l);
+ for (p = l; p; p = nextSibling(p)) {
+
+ SwigType *pt = Getattr(p, "type");
+ String *pn = Getattr(p, "name");
+ /* Only print an argument if not ignored */
+ if (!checkAttribute(p, "tmap:in:numinputs", "0")) {
+ if (i >= (pcount - numopt))
+ Putc('?', temp);
+ if (Len(pn) > 0) {
+ Printf(temp, "%s", pn);
+ } else {
+ Printf(temp, "%s", SwigType_str(pt, 0));
+ }
+ if (i >= (pcount - numopt))
+ Putc('?', temp);
+ Putc(' ', temp);
+ i++;
+ }
+ }
+ return Char(temp);
+ }
+
+ String *runtimeCode() {
+ String *s = NewString("");
+ String *serrors = Swig_include_sys("tclerrors.swg");
+ if (!serrors) {
+ Printf(stderr, "*** Unable to open 'tclerrors.swg'\n");
+ } else {
+ Append(s, serrors);
+ Delete(serrors);
+ }
+ String *sapi = Swig_include_sys("tclapi.swg");
+ if (!sapi) {
+ Printf(stderr, "*** Unable to open 'tclapi.swg'\n");
+ } else {
+ Append(s, sapi);
+ Delete(sapi);
+ }
+ String *srun = Swig_include_sys("tclrun.swg");
+ if (!srun) {
+ Printf(stderr, "*** Unable to open 'tclrun.swg'\n");
+ } else {
+ Append(s, srun);
+ Delete(srun);
+ }
+
+ return s;
+ }
+
+ String *defaultExternalRuntimeFilename() {
+ return NewString("swigtclrun.h");
+ }
+};
+
+/* ----------------------------------------------------------------------
+ * swig_tcl() - Instantiate module
+ * ---------------------------------------------------------------------- */
+
+static Language *new_swig_tcl() {
+ return new TCL8();
+}
+extern "C" Language *swig_tcl(void) {
+ return new_swig_tcl();
+}
diff --git a/contrib/tools/swig/Source/Modules/typepass.cxx b/contrib/tools/swig/Source/Modules/typepass.cxx
new file mode 100644
index 0000000000..2a1dadf735
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/typepass.cxx
@@ -0,0 +1,1322 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * typepass.cxx
+ *
+ * This module builds all of the internal type information by collecting
+ * typedef declarations as well as registering classes, structures, and unions.
+ * This information is needed to correctly handle shadow classes and other
+ * advanced features. This phase of compilation is also used to perform
+ * type-expansion. All types are fully qualified with namespace prefixes
+ * and other information needed for compilation.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+#include "cparse.h"
+
+struct normal_node {
+ Symtab *symtab;
+ Hash *typescope;
+ List *normallist;
+ normal_node *next;
+};
+
+static normal_node *patch_list = 0;
+
+/* Singleton class - all non-static methods in this class are private */
+class TypePass:private Dispatcher {
+ Node *inclass;
+ Node *module;
+ int importmode;
+ String *nsname;
+ String *nssymname;
+ Hash *classhash;
+ List *normalize;
+
+ TypePass() :
+ inclass(0),
+ module(0),
+ importmode(0),
+ nsname(0),
+ nssymname(0),
+ classhash(0),
+ normalize(0) {
+ }
+
+ /* Normalize a type. Replaces type with fully qualified version */
+ void normalize_type(SwigType *ty) {
+ SwigType *qty;
+ if (CPlusPlus) {
+ Replaceall(ty, "struct ", "");
+ Replaceall(ty, "union ", "");
+ Replaceall(ty, "class ", "");
+ }
+
+ qty = SwigType_typedef_qualified(ty);
+ /* Printf(stdout,"%s --> %s\n", ty, qty); */
+ Clear(ty);
+ Append(ty, qty);
+ Delete(qty);
+ }
+
+ /* Normalize a parameter list */
+
+ void normalize_parms(ParmList *p) {
+ while (p) {
+ SwigType *ty = Getattr(p, "type");
+ normalize_type(ty);
+ /* This is a check for a function type */
+ {
+ SwigType *qty = SwigType_typedef_resolve_all(ty);
+ if (SwigType_isfunction(qty)) {
+ SwigType_add_pointer(ty);
+ }
+ Delete(qty);
+ }
+
+ String *value = Getattr(p, "value");
+ if (value) {
+ Node *n = Swig_symbol_clookup(value, 0);
+ if (n) {
+ String *q = Swig_symbol_qualified(n);
+ if (q && Len(q)) {
+ String *vb = Swig_scopename_last(value);
+ Clear(value);
+ Printf(value, "%s::%s", SwigType_namestr(q), vb);
+ Delete(q);
+ }
+ }
+ }
+ if (value && SwigType_istemplate(value)) {
+ String *nv = SwigType_namestr(value);
+ Setattr(p, "value", nv);
+ }
+ p = nextSibling(p);
+ }
+ }
+
+ void normalize_later(ParmList *p) {
+ while (p) {
+ SwigType *ty = Getattr(p, "type");
+ Append(normalize, ty);
+ p = nextSibling(p);
+ }
+ }
+
+ /* Walk through entries in normalize list and patch them up */
+ void normalize_list() {
+ Hash *currentsym = Swig_symbol_current();
+
+ normal_node *nn = patch_list;
+ normal_node *np;
+ while (nn) {
+ Swig_symbol_setscope(nn->symtab);
+ SwigType_set_scope(nn->typescope);
+ Iterator t;
+ for (t = First(nn->normallist); t.item; t = Next(t)) {
+ normalize_type(t.item);
+ }
+ Delete(nn->normallist);
+ np = nn->next;
+ delete(nn);
+ nn = np;
+ }
+ Swig_symbol_setscope(currentsym);
+ }
+
+ /* generate C++ inheritance type-relationships */
+ void cplus_inherit_types_impl(Node *first, Node *cls, String *clsname, const char *bases, const char *baselist, int ispublic, String *cast = 0) {
+
+ if (first == cls)
+ return; /* The Marcelo check */
+ if (!cls)
+ cls = first;
+ List *alist = 0;
+ List *ilist = Getattr(cls, bases);
+ if (!ilist) {
+ List *nlist = Getattr(cls, baselist);
+ if (nlist) {
+ int len = Len(nlist);
+ int i;
+ for (i = 0; i < len; i++) {
+ Node *bcls = 0;
+ int clsforward = 0;
+ String *bname = Getitem(nlist, i);
+ String *sname = bname;
+ String *tname = 0;
+
+ /* Try to locate the base class. We look in the symbol table and we chase
+ typedef declarations to get to the base class if necessary */
+ Symtab *st = Getattr(cls, "sym:symtab");
+
+ if (SwigType_istemplate(bname)) {
+ tname = SwigType_typedef_resolve_all(bname);
+ sname = tname;
+ }
+ while (1) {
+ String *qsname = SwigType_typedef_qualified(sname);
+ bcls = Swig_symbol_clookup(qsname, st);
+ Delete(qsname);
+ if (bcls) {
+ if (Strcmp(nodeType(bcls), "class") != 0) {
+ /* Not a class. The symbol could be a typedef. */
+ if (checkAttribute(bcls, "storage", "typedef")) {
+ SwigType *decl = Getattr(bcls, "decl");
+ if (!decl || !(Len(decl))) {
+ sname = Getattr(bcls, "type");
+ st = Getattr(bcls, "sym:symtab");
+ if (SwigType_istemplate(sname)) {
+ if (tname)
+ Delete(tname);
+ tname = SwigType_typedef_resolve_all(sname);
+ sname = tname;
+ }
+ continue;
+ }
+ // A case when both outer and nested classes inherit from the same parent. Constructor may be found instead of the class itself.
+ } else if (GetFlag(cls, "nested") && checkAttribute(bcls, "nodeType", "constructor")) {
+ bcls = Getattr(bcls, "parentNode");
+ if (Getattr(bcls, "typepass:visit")) {
+ if (!Getattr(bcls, "feature:onlychildren")) {
+ if (!ilist)
+ ilist = alist = NewList();
+ Append(ilist, bcls);
+ } else {
+ if (!GetFlag(bcls, "feature:ignore")) {
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Base class '%s' has no name as it is an empty template instantiated with '%%template()'. Ignored.\n", SwigType_namestr(bname));
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bcls), Getline(bcls), "The %%template directive must be written before '%s' is used as a base class and be declared with a name.\n", SwigType_namestr(bname));
+ }
+ }
+ }
+ break;
+ }
+ if (Strcmp(nodeType(bcls), "classforward") != 0) {
+ Swig_error(Getfile(bname), Getline(bname), "'%s' is not a valid base class.\n", SwigType_namestr(bname));
+ Swig_error(Getfile(bcls), Getline(bcls), "See definition of '%s'.\n", SwigType_namestr(bname));
+ } else {
+ Swig_warning(WARN_TYPE_INCOMPLETE, Getfile(bname), Getline(bname), "Base class '%s' is incomplete.\n", SwigType_namestr(bname));
+ Swig_warning(WARN_TYPE_INCOMPLETE, Getfile(bcls), Getline(bcls), "Only forward declaration '%s' was found.\n", SwigType_namestr(bname));
+ clsforward = 1;
+ }
+ bcls = 0;
+ } else {
+ if (Getattr(bcls, "typepass:visit")) {
+ if (!Getattr(bcls, "feature:onlychildren")) {
+ if (!ilist)
+ ilist = alist = NewList();
+ Append(ilist, bcls);
+ } else {
+ if (!GetFlag(bcls, "feature:ignore")) {
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Base class '%s' has no name as it is an empty template instantiated with '%%template()'. Ignored.\n", SwigType_namestr(bname));
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bcls), Getline(bcls), "The %%template directive must be written before '%s' is used as a base class and be declared with a name.\n", SwigType_namestr(bname));
+ }
+ }
+ } else {
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Base class '%s' undefined.\n", SwigType_namestr(bname));
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bcls), Getline(bcls), "'%s' must be defined before it is used as a base class.\n", SwigType_namestr(bname));
+ }
+ }
+ }
+ break;
+ }
+
+ if (tname)
+ Delete(tname);
+ if (!bcls) {
+ if (!clsforward && !GetFlag(cls, "feature:ignore")) {
+ if (ispublic && !Getmeta(bname, "already_warned")) {
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Nothing known about base class '%s'. Ignored.\n", SwigType_namestr(bname));
+ if (Strchr(bname, '<')) {
+ Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Maybe you forgot to instantiate '%s' using %%template.\n", SwigType_namestr(bname));
+ }
+ Setmeta(bname, "already_warned", "1");
+ }
+ }
+ SwigType_inherit(clsname, bname, cast, 0);
+ }
+ }
+ }
+ if (ilist) {
+ Setattr(cls, bases, ilist);
+ }
+ }
+ if (alist)
+ Delete(alist);
+
+ if (!ilist)
+ return;
+ int len = Len(ilist);
+ int i;
+ for (i = 0; i < len; i++) {
+ Node *n = Getitem(ilist, i);
+ String *bname = Getattr(n, "name");
+ Node *bclass = n; /* Getattr(n,"class"); */
+ Hash *scopes = Getattr(bclass, "typescope");
+ SwigType_inherit(clsname, bname, cast, 0);
+ if (ispublic && !GetFlag(bclass, "feature:ignore")) {
+ String *smartptr = Getattr(first, "feature:smartptr");
+ if (smartptr) {
+ SwigType *smart = Swig_cparse_smartptr(first);
+ if (smart) {
+ /* Record a (fake) inheritance relationship between smart pointer
+ and smart pointer to base class, so that smart pointer upcasts
+ are automatically generated. */
+ SwigType *bsmart = Copy(smart);
+
+ // TODO: SwigType_typedef_resolve_all on a String instead of SwigType is incorrect for templates
+ SwigType *rclsname = SwigType_typedef_resolve_all(clsname);
+ SwigType *rbname = SwigType_typedef_resolve_all(bname);
+ int replace_count = Replaceall(bsmart, rclsname, rbname);
+ if (replace_count == 0) {
+ // If no replacement made, it will be because rclsname is fully resolved, but the
+ // type in the smartptr feature used a typedef or not fully resolved name.
+ String *firstname = Getattr(first, "name");
+ Replaceall(bsmart, firstname, rbname);
+ }
+ // The code above currently creates a smartptr of the base class by substitution, replacing Derived
+ // with Base resulting in something like: 'smartptr< Derived >' from 'smartptr< Base >'. Instead
+ // the feature:smartptr should be used as it also contains 'smartptr< Base >' as specified by the user.
+ // A similar fix should also be done in upcastsCode in java.cxx, csharp.cxx and writeClassUpcast in d.cxx.
+ // Printf(stdout, "smartcomparison %s <=> %s\n", SwigType_namestr(bsmart), Getattr(bclass, "feature:smartptr"));
+
+ Delete(rclsname);
+ Delete(rbname);
+ String *smartnamestr = SwigType_namestr(smart);
+ String *bsmartnamestr = SwigType_namestr(bsmart);
+ /* construct casting code */
+ String *convcode = NewStringf("\n *newmemory = SWIG_CAST_NEW_MEMORY;\n return (void *) new %s(*(%s *)$from);\n", bsmartnamestr, smartnamestr);
+ Delete(bsmartnamestr);
+ Delete(smartnamestr);
+ /* setup inheritance relationship between smart pointer templates */
+ SwigType_inherit(smart, bsmart, 0, convcode);
+ if (!GetFlag(bclass, "feature:smartptr"))
+ Swig_warning(WARN_LANG_SMARTPTR_MISSING, Getfile(first), Getline(first), "Base class '%s' of '%s' is not similarly marked as a smart pointer.\n", SwigType_namestr(Getattr(bclass, "name")), SwigType_namestr(Getattr(first, "name")));
+ Delete(convcode);
+ Delete(bsmart);
+ }
+ Delete(smart);
+ } else {
+ if (GetFlag(bclass, "feature:smartptr"))
+ Swig_warning(WARN_LANG_SMARTPTR_MISSING, Getfile(first), Getline(first), "Derived class '%s' of '%s' is not similarly marked as a smart pointer.\n", SwigType_namestr(Getattr(first, "name")), SwigType_namestr(Getattr(bclass, "name")));
+ }
+ }
+ if (!importmode) {
+ String *btype = Copy(bname);
+ SwigType_add_pointer(btype);
+ SwigType_remember(btype);
+ Delete(btype);
+ }
+ if (scopes) {
+ SwigType_inherit_scope(scopes);
+ }
+ /* Set up inheritance in the symbol table */
+ Symtab *st = Getattr(cls, "symtab");
+ Symtab *bst = Getattr(bclass, "symtab");
+ if (st == bst) {
+ Swig_warning(WARN_PARSE_REC_INHERITANCE, Getfile(cls), Getline(cls), "Recursive scope inheritance of '%s'.\n", SwigType_namestr(Getattr(cls, "name")));
+ continue;
+ }
+ Symtab *s = Swig_symbol_current();
+ Swig_symbol_setscope(st);
+ Swig_symbol_inherit(bst);
+ Swig_symbol_setscope(s);
+
+ /* Recursively hit base classes */
+ String *namestr = SwigType_namestr(Getattr(bclass, "name"));
+ String *newcast = NewStringf("(%s *)%s", namestr, cast);
+ Delete(namestr);
+ cplus_inherit_types_impl(first, bclass, clsname, bases, baselist, ispublic, newcast);
+ Delete(newcast);
+ }
+ }
+
+ void append_list(List *lb, List *la) {
+ if (la && lb) {
+ for (Iterator bi = First(la); bi.item; bi = Next(bi)) {
+ Append(lb, bi.item);
+ }
+ }
+ }
+
+ void cplus_inherit_types(Node *first, Node *cls, String *clsname, String *cast = 0) {
+ cplus_inherit_types_impl(first, cls, clsname, "bases", "baselist", 1, cast);
+ cplus_inherit_types_impl(first, cls, clsname, "protectedbases", "protectedbaselist", 0, cast);
+ cplus_inherit_types_impl(first, cls, clsname, "privatebases", "privatebaselist", 0, cast);
+
+ if (!cls)
+ cls = first;
+
+ List *allbases = NewList();
+ append_list(allbases, Getattr(cls, "bases"));
+ append_list(allbases, Getattr(cls, "protectedbases"));
+ append_list(allbases, Getattr(cls, "privatebases"));
+ if (Len(allbases)) {
+ Setattr(cls, "allbases", allbases);
+ }
+ Delete(allbases);
+ }
+
+ /* ------------------------------------------------------------
+ * top()
+ * ------------------------------------------------------------ */
+
+ virtual int top(Node *n) {
+ importmode = 0;
+ module = Getattr(n, "module");
+ inclass = 0;
+ normalize = 0;
+ nsname = 0;
+ nssymname = 0;
+ classhash = Getattr(n, "classes");
+ emit_children(n);
+ normalize_list();
+ SwigType_set_scope(0);
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * moduleDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int moduleDirective(Node *n) {
+ if (!module) {
+ module = n;
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * importDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int importDirective(Node *n) {
+ String *oldmodule = module;
+ int oldimport = importmode;
+ importmode = 1;
+ module = 0;
+ emit_children(n);
+ importmode = oldimport;
+ module = oldmodule;
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * includeDirective()
+ * externDirective()
+ * extendDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int includeDirective(Node *n) {
+ return emit_children(n);
+ }
+ virtual int externDeclaration(Node *n) {
+ return emit_children(n);
+ }
+ virtual int extendDirective(Node *n) {
+ return emit_children(n);
+ }
+
+ /* ------------------------------------------------------------
+ * classDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int classDeclaration(Node *n) {
+ String *name = Getattr(n, "name");
+ String *tdname = Getattr(n, "tdname");
+ String *unnamed = Getattr(n, "unnamed");
+ String *storage = Getattr(n, "storage");
+ String *kind = Getattr(n, "kind");
+ save_value<Node*> oldinclass(inclass);
+ List *olist = normalize;
+ Symtab *symtab;
+ String *nname = 0;
+ String *fname = 0;
+ String *scopename = 0;
+ String *template_default_expanded = 0;
+
+ normalize = NewList();
+
+ if (name) {
+ if (SwigType_istemplate(name)) {
+ // We need to fully resolve the name and expand default template parameters to make templates work correctly */
+ Node *cn;
+ SwigType *resolved_name = SwigType_typedef_resolve_all(name);
+ SwigType *deftype_name = Swig_symbol_template_deftype(resolved_name, 0);
+ fname = Copy(resolved_name);
+ if (!Equal(resolved_name, deftype_name))
+ template_default_expanded = Copy(deftype_name);
+ if (!Equal(fname, name) && (cn = Swig_symbol_clookup_local(fname, 0))) {
+ if ((n == cn)
+ || (Strcmp(nodeType(cn), "template") == 0)
+ || (Getattr(cn, "feature:onlychildren") != 0)
+ || (Getattr(n, "feature:onlychildren") != 0)) {
+ Swig_symbol_cadd(fname, n);
+ if (template_default_expanded)
+ Swig_symbol_cadd(template_default_expanded, n);
+ SwigType_typedef_class(fname);
+ scopename = Copy(fname);
+ } else {
+ Swig_warning(WARN_TYPE_REDEFINED, Getfile(n), Getline(n), "Template '%s' was already wrapped,\n", SwigType_namestr(name));
+ Swig_warning(WARN_TYPE_REDEFINED, Getfile(cn), Getline(cn), "previous wrap of '%s'.\n", SwigType_namestr(Getattr(cn, "name")));
+ scopename = 0;
+ }
+ } else {
+ Swig_symbol_cadd(fname, n);
+ SwigType_typedef_class(fname);
+ scopename = Copy(fname);
+ }
+ Delete(deftype_name);
+ Delete(resolved_name);
+ } else {
+ if ((CPlusPlus) || (unnamed)) {
+ SwigType_typedef_class(name);
+ } else {
+ SwigType_typedef_class(NewStringf("%s %s", kind, name));
+ }
+ scopename = Copy(name);
+ }
+ } else {
+ scopename = 0;
+ }
+
+ Setattr(n, "typepass:visit", "1");
+
+ /* Need to set up a typedef if unnamed */
+ if (unnamed && tdname && (Cmp(storage, "typedef") == 0)) {
+ SwigType_typedef(unnamed, tdname);
+ }
+ // name of the outer class should already be patched to contain its outer classes names, but not to contain namespaces
+ // namespace name (if present) is added after processing child nodes
+ if (Getattr(n, "nested:outer") && name) {
+ String *outerName = Getattr(Getattr(n, "nested:outer"), "name");
+ name = NewStringf("%s::%s", outerName, name);
+ Setattr(n, "name", name);
+ if (tdname) {
+ tdname = NewStringf("%s::%s", outerName, tdname);
+ Setattr(n, "tdname", tdname);
+ }
+ }
+
+ if (nsname && name) {
+ nname = NewStringf("%s::%s", nsname, name);
+ String *tdname = Getattr(n, "tdname");
+ if (tdname) {
+ tdname = NewStringf("%s::%s", nsname, tdname);
+ Setattr(n, "tdname", tdname);
+ }
+ }
+ if (nssymname) {
+ if (GetFlag(n, "feature:nspace"))
+ Setattr(n, "sym:nspace", nssymname);
+ }
+ SwigType_new_scope(scopename);
+ SwigType_attach_symtab(Getattr(n, "symtab"));
+
+ /* Inherit type definitions into the class */
+ if (name && !(GetFlag(n, "nested") && !checkAttribute(n, "access", "public") &&
+ (GetFlag(n, "feature:flatnested") || Language::instance()->nestedClassesSupport() == Language::NCS_None))) {
+ cplus_inherit_types(n, 0, nname ? nname : (fname ? fname : name));
+ }
+
+ inclass = n;
+ symtab = Swig_symbol_setscope(Getattr(n, "symtab"));
+ emit_children(n);
+ Swig_symbol_setscope(symtab);
+
+ Hash *ts = SwigType_pop_scope();
+ Setattr(n, "typescope", ts);
+ Delete(ts);
+ Setattr(n, "module", module);
+
+ // When a fully qualified templated type with default parameters is used in the parsed code,
+ // the following additional symbols and scopes are needed for successful lookups
+ if (template_default_expanded) {
+ Swig_symbol_alias(template_default_expanded, Getattr(n, "symtab"));
+ SwigType_scope_alias(template_default_expanded, Getattr(n, "typescope"));
+ }
+
+ /* Normalize deferred types */
+ {
+ normal_node *nn = new normal_node();
+ nn->normallist = normalize;
+ nn->symtab = Getattr(n, "symtab");
+ nn->next = patch_list;
+ nn->typescope = Getattr(n, "typescope");
+ patch_list = nn;
+ }
+
+ normalize = olist;
+
+ /* If in a namespace, patch the class name */
+ if (nname) {
+ Setattr(n, "name", nname);
+ Delete(nname);
+ }
+ Delete(fname);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * templateDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int templateDeclaration(Node *n) {
+ String *name = Getattr(n, "name");
+ String *ttype = Getattr(n, "templatetype");
+ if (Strcmp(ttype, "class") == 0) {
+ String *rname = SwigType_typedef_resolve_all(name);
+ SwigType_typedef_class(rname);
+ Delete(rname);
+ } else if (Strcmp(ttype, "classforward") == 0) {
+ String *rname = SwigType_typedef_resolve_all(name);
+ SwigType_typedef_class(rname);
+ Delete(rname);
+ /* SwigType_typedef_class(name); */
+ } else if (Strcmp(ttype, "cdecl") == 0) {
+ String *rname = SwigType_typedef_resolve_all(name);
+ SwigType_typedef_class(rname);
+ Delete(rname);
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * lambdaDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int lambdaDeclaration(Node *) {
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * classforwardDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int classforwardDeclaration(Node *n) {
+
+ /* Can't do inside a C struct because it breaks C nested structure wrapping */
+ if ((!inclass) || (CPlusPlus)) {
+ String *name = Getattr(n, "name");
+ SwigType_typedef_class(name);
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * namespaceDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int namespaceDeclaration(Node *n) {
+ Symtab *symtab;
+ String *name = Getattr(n, "name");
+ String *alias = Getattr(n, "alias");
+ List *olist = normalize;
+ normalize = NewList();
+ if (alias) {
+ Typetab *ts = Getattr(n, "typescope");
+ if (!ts) {
+ /* Create an empty scope for the alias */
+ Node *ns = Getattr(n, "namespace");
+ SwigType_scope_alias(name, Getattr(ns, "typescope"));
+ ts = Getattr(ns, "typescope");
+ Setattr(n, "typescope", ts);
+ }
+ /* Namespace alias */
+ return SWIG_OK;
+ } else {
+ if (name) {
+ Node *nn = Swig_symbol_clookup(name, n);
+ Hash *ts = 0;
+ if (nn)
+ ts = Getattr(nn, "typescope");
+ if (!ts) {
+ SwigType_new_scope(name);
+ SwigType_attach_symtab(Getattr(n, "symtab"));
+ } else {
+ SwigType_set_scope(ts);
+ }
+ }
+ String *oldnsname = nsname;
+ String *oldnssymname = nssymname;
+ nsname = Swig_symbol_qualified(Getattr(n, "symtab"));
+ nssymname = Swig_symbol_qualified_language_scopename(Getattr(n, "symtab"));
+ symtab = Swig_symbol_setscope(Getattr(n, "symtab"));
+ emit_children(n);
+ Swig_symbol_setscope(symtab);
+
+ if (name) {
+ Hash *ts = SwigType_pop_scope();
+ Setattr(n, "typescope", ts);
+ Delete(ts);
+ }
+
+ /* Normalize deferred types */
+ {
+ normal_node *nn = new normal_node();
+ nn->normallist = normalize;
+ nn->symtab = Getattr(n, "symtab");
+ nn->next = patch_list;
+ nn->typescope = Getattr(n, "typescope");
+ patch_list = nn;
+ }
+ normalize = olist;
+
+ Delete(nssymname);
+ nssymname = oldnssymname;
+ Delete(nsname);
+ nsname = oldnsname;
+ return SWIG_OK;
+ }
+ }
+
+ /* ------------------------------------------------------------
+ * cDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int cDeclaration(Node *n) {
+ if (NoExcept) {
+ Delattr(n, "throws");
+ }
+
+ /* Normalize types. */
+ SwigType *ty = Getattr(n, "type");
+ if (!ty) {
+ return SWIG_OK;
+ }
+ normalize_type(ty);
+ SwigType *decl = Getattr(n, "decl");
+ if (decl) {
+ normalize_type(decl);
+ }
+ normalize_parms(Getattr(n, "parms"));
+ normalize_parms(Getattr(n, "throws"));
+ if (GetFlag(n, "conversion_operator")) {
+ /* The call to the operator in the generated wrapper must be fully qualified in order to compile */
+ SwigType *name = Getattr(n, "name");
+ SwigType *qualifiedname = Swig_symbol_string_qualify(name, 0);
+ Clear(name);
+ Append(name, qualifiedname);
+ Delete(qualifiedname);
+ }
+
+ if (checkAttribute(n, "storage", "typedef")) {
+ String *name = Getattr(n, "name");
+ ty = Getattr(n, "type");
+ decl = Getattr(n, "decl");
+ SwigType *t = Copy(ty);
+ {
+ /* If the typename is qualified, make sure the scopename is fully qualified when making a typedef */
+ if (Swig_scopename_check(t) && strncmp(Char(t), "::", 2)) {
+ String *base, *prefix, *qprefix;
+ base = Swig_scopename_last(t);
+ prefix = Swig_scopename_prefix(t);
+ qprefix = SwigType_typedef_qualified(prefix);
+ Delete(t);
+ t = NewStringf("%s::%s", qprefix, base);
+ Delete(base);
+ Delete(prefix);
+ Delete(qprefix);
+ }
+ }
+ SwigType_push(t, decl);
+ if (CPlusPlus) {
+ Replaceall(t, "struct ", "");
+ Replaceall(t, "union ", "");
+ Replaceall(t, "class ", "");
+ }
+ SwigType_typedef(t, name);
+ }
+ /* If namespaces are active. We need to patch the name with a namespace prefix */
+ if (nsname && !inclass) {
+ String *name = Getattr(n, "name");
+ if (name) {
+ String *nname = NewStringf("%s::%s", nsname, name);
+ Setattr(n, "name", nname);
+ Delete(nname);
+ }
+ }
+ clean_overloaded(n);
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * constructorDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int constructorDeclaration(Node *n) {
+ if (NoExcept) {
+ Delattr(n, "throws");
+ }
+
+ normalize_parms(Getattr(n, "parms"));
+ normalize_parms(Getattr(n, "throws"));
+
+ clean_overloaded(n);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * destructorDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int destructorDeclaration(Node *) {
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * constantDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int constantDirective(Node *n) {
+ SwigType *ty = Getattr(n, "type");
+ if (ty) {
+ Setattr(n, "type", SwigType_typedef_qualified(ty));
+ }
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * enumDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int enumDeclaration(Node *n) {
+ String *name = Getattr(n, "name");
+
+ if (name) {
+ String *scope = 0;
+
+ // Add a typedef to the type table so that we can use 'enum Name' as well as just 'Name'
+ if (nsname || inclass) {
+
+ // But first correct the name and tdname to contain the fully qualified scopename
+ if (nsname && inclass) {
+ scope = NewStringf("%s::%s", nsname, Getattr(inclass, "name"));
+ } else if (nsname) {
+ scope = NewStringf("%s", nsname);
+ } else if (inclass) {
+ scope = NewStringf("%s", Getattr(inclass, "name"));
+ }
+
+ String *nname = NewStringf("%s::%s", scope, name);
+ Setattr(n, "name", nname);
+
+ String *tdname = Getattr(n, "tdname");
+ if (tdname) {
+ tdname = NewStringf("%s::%s", scope, tdname);
+ Setattr(n, "tdname", tdname);
+ }
+
+ SwigType *t = NewStringf("enum %s", nname);
+ SwigType_typedef(t, name);
+ } else {
+ SwigType *t = NewStringf("enum %s", name);
+ SwigType_typedef(t, name);
+ }
+ Delete(scope);
+ }
+
+ String *tdname = Getattr(n, "tdname");
+ String *unnamed = Getattr(n, "unnamed");
+ String *storage = Getattr(n, "storage");
+
+ // Construct enumtype - for declaring an enum of this type with SwigType_ltype() etc
+ String *enumtype = 0;
+ if (unnamed && tdname && (Cmp(storage, "typedef") == 0)) {
+ enumtype = Copy(Getattr(n, "tdname"));
+ } else if (name) {
+ enumtype = NewStringf("%s%s", CPlusPlus ? "" : "enum ", Getattr(n, "name"));
+ } else {
+ // anonymous enums
+ enumtype = Copy(Getattr(n, "type"));
+ }
+ Setattr(n, "enumtype", enumtype);
+
+ if (nssymname) {
+ if (GetFlag(n, "feature:nspace"))
+ Setattr(n, "sym:nspace", nssymname);
+ }
+
+ // This block of code is for dealing with %ignore on an enum item where the target language
+ // attempts to use the C enum value in the target language itself and expects the previous enum value
+ // to be one more than the previous value... the previous enum item might not exist if it is ignored!
+ // - It sets the first non-ignored enum item with the "firstenumitem" attribute.
+ // - It adds an enumvalue attribute if the previous enum item is ignored
+ {
+ Node *c;
+ int count = 0;
+ String *previous = 0;
+ bool previous_ignored = false;
+ bool firstenumitem = false;
+ for (c = firstChild(n); c; c = nextSibling(c)) {
+ assert(strcmp(Char(nodeType(c)), "enumitem") == 0);
+
+ bool reset;
+ String *enumvalue = Getattr(c, "enumvalue");
+ if (GetFlag(c, "feature:ignore") || !Getattr(c, "sym:name")) {
+ reset = enumvalue ? true : false;
+ previous_ignored = true;
+ } else {
+ if (!enumvalue && previous_ignored) {
+ if (previous)
+ Setattr(c, "enumvalue", NewStringf("(%s) + %d", previous, count+1));
+ else
+ Setattr(c, "enumvalue", NewStringf("%d", count));
+ SetFlag(c, "virtenumvalue"); // identify enumvalue as virtual, ie not from the parsed source
+ }
+ if (!firstenumitem) {
+ SetFlag(c, "firstenumitem");
+ firstenumitem = true;
+ }
+ reset = true;
+ previous_ignored = false;
+ }
+ if (reset) {
+ previous = enumvalue ? enumvalue : Getattr(c, "name");
+ count = 0;
+ } else {
+ count++;
+ }
+ }
+ }
+
+ emit_children(n);
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * enumvalueDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int enumvalueDeclaration(Node *n) {
+ String *name = Getattr(n, "name");
+ String *value = Getattr(n, "value");
+ String *scopedenum = Getattr(parentNode(n), "scopedenum");
+ if (!value)
+ value = name;
+ if (Strcmp(value, name) == 0) {
+ String *new_value;
+ if ((nsname || inclass || scopedenum) && cparse_cplusplus) {
+ new_value = NewStringf("%s::%s", SwigType_namestr(Swig_symbol_qualified(n)), value);
+ } else {
+ new_value = NewString(value);
+ }
+ if ((nsname || inclass || scopedenum) && !cparse_cplusplus) {
+ String *cppvalue = NewStringf("%s::%s", SwigType_namestr(Swig_symbol_qualified(n)), value);
+ Setattr(n, "cppvalue", cppvalue); /* for target languages that always generate C++ code even when wrapping C code */
+ }
+ Setattr(n, "value", new_value);
+ Delete(new_value);
+ }
+ Node *next = nextSibling(n);
+
+ // Make up an enumvalue if one was not specified in the parsed code (not designed to be used on enum items and %ignore - enumvalue will be set instead)
+ if (!GetFlag(n, "feature:ignore")) {
+ if (Getattr(n, "_last") && !Getattr(n, "enumvalue")) { // Only the first enum item has _last set (Note: first non-ignored enum item has firstenumitem set)
+ Setattr(n, "enumvalueex", "0");
+ }
+ if (next && !Getattr(next, "enumvalue")) {
+ Setattr(next, "enumvalueex", NewStringf("%s + 1", Getattr(n, "sym:name")));
+ }
+ }
+
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * enumforwardDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int enumforwardDeclaration(Node *n) {
+
+ // Use enumDeclaration() to do all the hard work.
+ // Note that no children can be emitted in a forward declaration as there aren't any.
+ int result = enumDeclaration(n);
+ if (result == SWIG_OK) {
+ // Detect when the real enum matching the forward enum declaration has not been parsed/declared
+ SwigType *ty = SwigType_typedef_resolve_all(Getattr(n, "type"));
+ Replaceall(ty, "enum ", "");
+ Node *nn = Swig_symbol_clookup(ty, 0);
+
+ String *nodetype = nn ? nodeType(nn) : 0;
+ if (nodetype) {
+ if (Equal(nodetype, "enumforward")) {
+ SetFlag(nn, "enumMissing");
+ } // if a real enum was declared this would be an "enum" node type
+ }
+ Delete(ty);
+ }
+ return result;
+ }
+
+#ifdef DEBUG_OVERLOADED
+ static void show_overloaded(Node *n) {
+ Node *c = Getattr(n, "sym:overloaded");
+ Node *checkoverloaded = c;
+ Printf(stdout, "-------------------- overloaded start %s sym:overloaded():%p -------------------------------\n", Getattr(n, "name"), c);
+ while (c) {
+ if (Getattr(c, "error")) {
+ c = Getattr(c, "sym:nextSibling");
+ continue;
+ }
+ if (Getattr(c, "sym:overloaded") != checkoverloaded) {
+ Printf(stdout, "sym:overloaded error c:%p checkoverloaded:%p\n", c, checkoverloaded);
+ Swig_print_node(c);
+ Exit(EXIT_FAILURE);
+ }
+
+ String *decl = Strcmp(nodeType(c), "using") == 0 ? NewString("------") : Getattr(c, "decl");
+ Printf(stdout, " show_overloaded %s::%s(%s) [%s] nodeType:%s\n", parentNode(c) ? Getattr(parentNode(c), "name") : "NOPARENT", Getattr(c, "name"), decl, Getattr(c, "sym:overname"), nodeType(c));
+ if (!Getattr(c, "sym:overloaded")) {
+ Printf(stdout, "sym:overloaded error.....%p\n", c);
+ Swig_print_node(c);
+ Exit(EXIT_FAILURE);
+ }
+ c = Getattr(c, "sym:nextSibling");
+ }
+ Printf(stdout, "-------------------- overloaded end %s -------------------------------\n", Getattr(n, "name"));
+ }
+#endif
+
+ /* ------------------------------------------------------------
+ * usingDeclaration()
+ * ------------------------------------------------------------ */
+
+ virtual int usingDeclaration(Node *n) {
+ if (Getattr(n, "namespace")) {
+ /* using namespace id */
+
+ /* For a namespace import. We set up inheritance in the type system */
+ Node *ns = Getattr(n, "node");
+ if (ns) {
+ Typetab *ts = Getattr(ns, "typescope");
+ if (ts) {
+ SwigType_using_scope(ts);
+ }
+ }
+ return SWIG_OK;
+ } else {
+ Node *ns;
+ /* using id */
+ Symtab *stab = Getattr(n, "sym:symtab");
+ if (stab) {
+ String *uname = Getattr(n, "uname");
+ ns = Swig_symbol_clookup(uname, stab);
+ if (!ns && SwigType_istemplate(uname)) {
+ String *tmp = Swig_symbol_template_deftype(uname, 0);
+ if (!Equal(tmp, uname)) {
+ ns = Swig_symbol_clookup(tmp, stab);
+ }
+ Delete(tmp);
+ }
+ } else {
+ ns = 0;
+ }
+ if (!ns) {
+ if (is_public(n)) {
+ Swig_warning(WARN_PARSE_USING_UNDEF, Getfile(n), Getline(n), "Nothing known about '%s'.\n", SwigType_namestr(Getattr(n, "uname")));
+ }
+ } else {
+ /* Only a single symbol is being used. There are only a few symbols that
+ we actually care about. These are typedef, class declarations, and enum */
+ String *ntype = nodeType(ns);
+ if (Strcmp(ntype, "cdecl") == 0) {
+ if (checkAttribute(ns, "storage", "typedef")) {
+ /* A typedef declaration */
+ String *uname = Getattr(n, "uname");
+ SwigType_typedef_using(uname);
+ } else {
+ /* A normal C declaration. */
+ if ((inclass) && (!GetFlag(n, "feature:ignore")) && (Getattr(n, "sym:name"))) {
+ Node *c = ns;
+ Node *unodes = 0, *last_unodes = 0;
+ int ccount = 0;
+ String *symname = Getattr(n, "sym:name");
+
+ // The overloaded functions in scope may not yet have had their parameters normalized yet (in cDeclaration).
+ // Happens if the functions were declared after the using declaration. So use a normalized copy.
+ List *n_decl_list = NewList();
+ Node *over = Getattr(n, "sym:overloaded");
+ while (over) {
+ String *odecl = Copy(Getattr(over, "decl"));
+ if (odecl) {
+ normalize_type(odecl);
+ Append(n_decl_list, odecl);
+ Delete(odecl);
+ }
+ over = Getattr(over, "sym:nextSibling");
+ }
+
+ while (c) {
+ if (Strcmp(nodeType(c), "cdecl") == 0) {
+ if (!(Swig_storage_isstatic(c)
+ || checkAttribute(c, "storage", "typedef")
+ || checkAttribute(c, "storage", "friend")
+ || (Getattr(c, "feature:extend") && !Getattr(c, "code"))
+ || GetFlag(c, "feature:ignore"))) {
+
+ String *csymname = Getattr(c, "sym:name");
+ if (!csymname || (Strcmp(csymname, symname) == 0)) {
+ String *decl = Getattr(c, "decl");
+ int match = 0;
+
+ for (Iterator it = First(n_decl_list); it.item; it = Next(it)) {
+ String *odecl = it.item;
+ if (Cmp(decl, odecl) == 0) {
+ match = 1;
+ break;
+ }
+ }
+ if (match) {
+ /* Don't generate a method if the method is overridden in this class,
+ * for example don't generate another m(bool) should there be a Base::m(bool) :
+ * struct Derived : Base {
+ * void m(bool);
+ * using Base::m;
+ * };
+ */
+ c = Getattr(c, "csym:nextSibling");
+ continue;
+ }
+
+ Node *nn = copyNode(c);
+ Setfile(nn, Getfile(n));
+ Setline(nn, Getline(n));
+ Delattr(nn, "access"); // access might be different from the method in the base class
+ Setattr(nn, "access", Getattr(n, "access"));
+ if (!Getattr(nn, "sym:name"))
+ Setattr(nn, "sym:name", symname);
+ Symtab *st = Getattr(n, "sym:symtab");
+ assert(st);
+ Setattr(nn, "sym:symtab", st);
+
+ if (!GetFlag(nn, "feature:ignore")) {
+ ParmList *parms = CopyParmList(Getattr(c, "parms"));
+ int is_pointer = SwigType_ispointer_return(Getattr(nn, "decl"));
+ int is_void = checkAttribute(nn, "type", "void") && !is_pointer;
+ Setattr(nn, "parms", parms);
+ Delete(parms);
+ if (Getattr(n, "feature:extend")) {
+ String *ucode = is_void ? NewStringf("{ self->%s(", Getattr(n, "uname")) : NewStringf("{ return self->%s(", Getattr(n, "uname"));
+
+ for (ParmList *p = parms; p;) {
+ Append(ucode, Getattr(p, "name"));
+ p = nextSibling(p);
+ if (p)
+ Append(ucode, ",");
+ }
+ Append(ucode, "); }");
+ Setattr(nn, "code", ucode);
+ Delete(ucode);
+ }
+ ParmList *throw_parm_list = Getattr(c, "throws");
+ if (throw_parm_list)
+ Setattr(nn, "throws", CopyParmList(throw_parm_list));
+ ccount++;
+ if (!last_unodes) {
+ last_unodes = nn;
+ unodes = nn;
+ } else {
+ Setattr(nn, "previousSibling", last_unodes);
+ Setattr(last_unodes, "nextSibling", nn);
+ Setattr(nn, "sym:previousSibling", last_unodes);
+ Setattr(last_unodes, "sym:nextSibling", nn);
+ Setattr(nn, "sym:overloaded", unodes);
+ Setattr(unodes, "sym:overloaded", unodes);
+ last_unodes = nn;
+ }
+ } else {
+ Delete(nn);
+ }
+ } else {
+ Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(n), Getline(n), "Using declaration %s, with name '%s', is not actually using\n", SwigType_namestr(Getattr(n, "uname")), symname);
+ Swig_warning(WARN_LANG_USING_NAME_DIFFERENT, Getfile(c), Getline(c), "the method from %s, with name '%s', as the names are different.\n", Swig_name_decl(c), csymname);
+ }
+ }
+ }
+ c = Getattr(c, "csym:nextSibling");
+ }
+ if (unodes) {
+ set_firstChild(n, unodes);
+ if (ccount > 1) {
+ if (!Getattr(n, "sym:overloaded")) {
+ Setattr(n, "sym:overloaded", n);
+ Setattr(n, "sym:overname", "_SWIG_0");
+ }
+ }
+ }
+
+ /* Hack the parse tree symbol table for overloaded methods. Replace the "using" node with the
+ * list of overloaded methods we have just added in as child nodes to the "using" node.
+ * The node will still exist, it is just the symbol table linked list of overloaded methods
+ * which is hacked. */
+ if (Getattr(n, "sym:overloaded")) {
+ int cnt = 0;
+ Node *ps = Getattr(n, "sym:previousSibling");
+ Node *ns = Getattr(n, "sym:nextSibling");
+ Node *fc = firstChild(n);
+ Node *firstoverloaded = Getattr(n, "sym:overloaded");
+#ifdef DEBUG_OVERLOADED
+ show_overloaded(firstoverloaded);
+#endif
+
+ if (firstoverloaded == n) {
+ // This 'using' node we are cutting out was the first node in the overloaded list.
+ // Change the first node in the list
+ Delattr(firstoverloaded, "sym:overloaded");
+ firstoverloaded = fc ? fc : ns;
+
+ // Correct all the sibling overloaded methods (before adding in new methods)
+ Node *nnn = ns;
+ while (nnn) {
+ Setattr(nnn, "sym:overloaded", firstoverloaded);
+ nnn = Getattr(nnn, "sym:nextSibling");
+ }
+ }
+
+ if (!fc) {
+ // Remove from overloaded list ('using' node does not actually end up adding in any methods)
+ if (ps) {
+ Setattr(ps, "sym:nextSibling", ns);
+ }
+ if (ns) {
+ Setattr(ns, "sym:previousSibling", ps);
+ }
+ } else {
+ // The 'using' node results in methods being added in - slot in these methods here
+ Node *pp = fc;
+ while (pp) {
+ Node *ppn = Getattr(pp, "sym:nextSibling");
+ Setattr(pp, "sym:overloaded", firstoverloaded);
+ Setattr(pp, "sym:overname", NewStringf("%s_%d", Getattr(n, "sym:overname"), cnt++));
+ if (ppn)
+ pp = ppn;
+ else
+ break;
+ }
+ if (ps) {
+ Setattr(ps, "sym:nextSibling", fc);
+ Setattr(fc, "sym:previousSibling", ps);
+ }
+ if (ns) {
+ Setattr(ns, "sym:previousSibling", pp);
+ Setattr(pp, "sym:nextSibling", ns);
+ }
+ }
+ Delattr(n, "sym:previousSibling");
+ Delattr(n, "sym:nextSibling");
+ Delattr(n, "sym:overloaded");
+ Delattr(n, "sym:overname");
+ clean_overloaded(firstoverloaded);
+#ifdef DEBUG_OVERLOADED
+ show_overloaded(firstoverloaded);
+#endif
+ }
+ Delete(n_decl_list);
+ }
+ }
+ } else if ((Strcmp(ntype, "class") == 0) || ((Strcmp(ntype, "classforward") == 0))) {
+ /* We install the using class name as kind of a typedef back to the original class */
+ String *uname = Getattr(n, "uname");
+ /* Import into current type scope */
+ SwigType_typedef_using(uname);
+ } else if (Strcmp(ntype, "enum") == 0) {
+ SwigType_typedef_using(Getattr(n, "uname"));
+ } else if (Strcmp(ntype, "template") == 0) {
+ SwigType_typedef_using(Getattr(n, "uname"));
+ }
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * typemapDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int typemapDirective(Node *n) {
+ if (inclass || nsname) {
+ Node *items = firstChild(n);
+ while (items) {
+ Parm *pattern = Getattr(items, "pattern");
+ Parm *parms = Getattr(items, "parms");
+ normalize_later(pattern);
+ normalize_later(parms);
+ items = nextSibling(items);
+ }
+ }
+ return SWIG_OK;
+ }
+
+
+ /* ------------------------------------------------------------
+ * typemapcopyDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int typemapcopyDirective(Node *n) {
+ if (inclass || nsname) {
+ Node *items = firstChild(n);
+ ParmList *pattern = Getattr(n, "pattern");
+ normalize_later(pattern);
+ while (items) {
+ ParmList *npattern = Getattr(items, "pattern");
+ normalize_later(npattern);
+ items = nextSibling(items);
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * applyDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int applyDirective(Node *n) {
+ if (inclass || nsname) {
+ ParmList *pattern = Getattr(n, "pattern");
+ normalize_later(pattern);
+ Node *items = firstChild(n);
+ while (items) {
+ Parm *apattern = Getattr(items, "pattern");
+ normalize_later(apattern);
+ items = nextSibling(items);
+ }
+ }
+ return SWIG_OK;
+ }
+
+ /* ------------------------------------------------------------
+ * clearDirective()
+ * ------------------------------------------------------------ */
+
+ virtual int clearDirective(Node *n) {
+ if (inclass || nsname) {
+ Node *p;
+ for (p = firstChild(n); p; p = nextSibling(p)) {
+ ParmList *pattern = Getattr(p, "pattern");
+ normalize_later(pattern);
+ }
+ }
+ return SWIG_OK;
+ }
+
+public:
+ static void pass(Node *n) {
+ TypePass t;
+ t.top(n);
+ }
+};
+
+void Swig_process_types(Node *n) {
+ if (!n)
+ return;
+ TypePass::pass(n);
+}
+
diff --git a/contrib/tools/swig/Source/Modules/utils.cxx b/contrib/tools/swig/Source/Modules/utils.cxx
new file mode 100644
index 0000000000..de6f87d8c3
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/utils.cxx
@@ -0,0 +1,218 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * utils.cxx
+ *
+ * Various utility functions.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+
+int is_public(Node *n) {
+ String *access = Getattr(n, "access");
+ return !access || !Cmp(access, "public");
+}
+
+int is_private(Node *n) {
+ String *access = Getattr(n, "access");
+ return access && !Cmp(access, "private");
+}
+
+int is_protected(Node *n) {
+ String *access = Getattr(n, "access");
+ return access && !Cmp(access, "protected");
+}
+
+static int is_member_director_helper(Node *parentnode, Node *member) {
+ int parent_nodirector = GetFlag(parentnode, "feature:nodirector");
+ if (parent_nodirector)
+ return 0;
+ int parent_director = Swig_director_mode() && GetFlag(parentnode, "feature:director");
+ int cdecl_director = parent_director || GetFlag(member, "feature:director");
+ int cdecl_nodirector = GetFlag(member, "feature:nodirector");
+ return cdecl_director && !cdecl_nodirector && !GetFlag(member, "feature:extend");
+}
+
+int is_member_director(Node *parentnode, Node *member) {
+ if (parentnode && checkAttribute(member, "storage", "virtual")) {
+ return is_member_director_helper(parentnode, member);
+ } else {
+ return 0;
+ }
+}
+
+int is_member_director(Node *member) {
+ return is_member_director(Getattr(member, "parentNode"), member);
+}
+
+// Identifies the additional protected members that are generated when the allprotected option is used.
+// This does not include protected virtual methods as they are turned on with the dirprot option.
+int is_non_virtual_protected_access(Node *n) {
+ int result = 0;
+ if (Swig_director_mode() && Swig_director_protected_mode() && Swig_all_protected_mode() && is_protected(n) && !checkAttribute(n, "storage", "virtual")) {
+ Node *parentNode = Getattr(n, "parentNode");
+ // When vtable is empty, the director class does not get emitted, so a check for an empty vtable should be done.
+ // However, vtable is set in Language and so is not yet set when methods in Typepass call clean_overloaded()
+ // which calls is_non_virtual_protected_access. So commented out below.
+ // Moving the director vtable creation into Typepass should solve this problem.
+ if (is_member_director_helper(parentNode, n) /* && Getattr(parentNode, "vtable")*/)
+ result = 1;
+ }
+ return result;
+}
+
+/* Clean overloaded list. Removes templates, ignored, and errors */
+
+void clean_overloaded(Node *n) {
+ Node *nn = Getattr(n, "sym:overloaded");
+ Node *first = 0;
+ while (nn) {
+ String *ntype = nodeType(nn);
+ if ((GetFlag(nn, "feature:ignore")) ||
+ (Getattr(nn, "error")) ||
+ (Strcmp(ntype, "template") == 0) ||
+ ((Strcmp(ntype, "cdecl") == 0) && is_protected(nn) && !is_member_director(nn) && !is_non_virtual_protected_access(n))) {
+ /* Remove from overloaded list */
+ Node *ps = Getattr(nn, "sym:previousSibling");
+ Node *ns = Getattr(nn, "sym:nextSibling");
+ if (ps) {
+ Setattr(ps, "sym:nextSibling", ns);
+ }
+ if (ns) {
+ Setattr(ns, "sym:previousSibling", ps);
+ }
+ Delattr(nn, "sym:previousSibling");
+ Delattr(nn, "sym:nextSibling");
+ Delattr(nn, "sym:overloaded");
+ nn = ns;
+ continue;
+ } else {
+ if (!first)
+ first = nn;
+ Setattr(nn, "sym:overloaded", first);
+ }
+ nn = Getattr(nn, "sym:nextSibling");
+ }
+ if (!first || (first && !Getattr(first, "sym:nextSibling"))) {
+ if (Getattr(n, "sym:overloaded"))
+ Delattr(n, "sym:overloaded");
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_set_max_hash_expand()
+ *
+ * Controls how many Hash objects are displayed when displaying nested Hash objects.
+ * Makes DohSetMaxHashExpand an externally callable function (for debugger).
+ * ----------------------------------------------------------------------------- */
+
+void Swig_set_max_hash_expand(int count) {
+ SetMaxHashExpand(count);
+}
+
+extern "C" {
+
+/* -----------------------------------------------------------------------------
+ * Swig_get_max_hash_expand()
+ *
+ * Returns how many Hash objects are displayed when displaying nested Hash objects.
+ * Makes DohGetMaxHashExpand an externally callable function (for debugger).
+ * ----------------------------------------------------------------------------- */
+
+int Swig_get_max_hash_expand() {
+ return GetMaxHashExpand();
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_to_doh_string()
+ *
+ * DOH version of Swig_to_string()
+ * ----------------------------------------------------------------------------- */
+
+static String *Swig_to_doh_string(DOH *object, int count) {
+ int old_count = Swig_get_max_hash_expand();
+ if (count >= 0)
+ Swig_set_max_hash_expand(count);
+
+ String *debug_string = object ? NewStringf("%s", object) : NewString("NULL");
+
+ Swig_set_max_hash_expand(old_count);
+ return debug_string;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_to_doh_string_with_location()
+ *
+ * DOH version of Swig_to_string_with_location()
+ * ----------------------------------------------------------------------------- */
+
+static String *Swig_to_doh_string_with_location(DOH *object, int count) {
+ int old_count = Swig_get_max_hash_expand();
+ if (count >= 0)
+ Swig_set_max_hash_expand(count);
+
+ String *debug_string = Swig_stringify_with_location(object);
+
+ Swig_set_max_hash_expand(old_count);
+ return debug_string;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_to_string()
+ *
+ * Swig debug - return C string representation of any DOH type.
+ * Nested Hash types expand count is value of Swig_get_max_hash_expand when count<0
+ * Note: leaks memory.
+ * ----------------------------------------------------------------------------- */
+
+const char *Swig_to_string(DOH *object, int count) {
+ return Char(Swig_to_doh_string(object, count));
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_to_string_with_location()
+ *
+ * Swig debug - return C string representation of any DOH type, within [] brackets
+ * for Hash and List types, prefixed by line and file information.
+ * Nested Hash types expand count is value of Swig_get_max_hash_expand when count<0
+ * Note: leaks memory.
+ * ----------------------------------------------------------------------------- */
+
+const char *Swig_to_string_with_location(DOH *object, int count) {
+ return Char(Swig_to_doh_string_with_location(object, count));
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_print()
+ *
+ * Swig debug - display string representation of any DOH type.
+ * Nested Hash types expand count is value of Swig_get_max_hash_expand when count<0
+ * ----------------------------------------------------------------------------- */
+
+void Swig_print(DOH *object, int count) {
+ String *output = Swig_to_doh_string(object, count);
+ Printf(stdout, "%s\n", output);
+ Delete(output);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_to_string_with_location()
+ *
+ * Swig debug - display string representation of any DOH type, within [] brackets
+ * for Hash and List types, prefixed by line and file information.
+ * Nested Hash types expand count is value of Swig_get_max_hash_expand when count<0
+ * ----------------------------------------------------------------------------- */
+
+void Swig_print_with_location(DOH *object, int count) {
+ String *output = Swig_to_doh_string_with_location(object, count);
+ Printf(stdout, "%s\n", output);
+ Delete(output);
+}
+
+} // extern "C"
+
diff --git a/contrib/tools/swig/Source/Modules/xml.cxx b/contrib/tools/swig/Source/Modules/xml.cxx
new file mode 100644
index 0000000000..ed4213cc5c
--- /dev/null
+++ b/contrib/tools/swig/Source/Modules/xml.cxx
@@ -0,0 +1,326 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * xml.cxx
+ *
+ * An Xml parse tree generator.
+ * ----------------------------------------------------------------------------- */
+
+#include "swigmod.h"
+
+static const char *usage = "\
+XML Options (available with -xml)\n\
+ -xmllang <lang> - Typedef language\n\
+ -xmllite - More lightweight version of XML\n\
+ ------\n\
+ deprecated (use -o): -xml <output.xml> - Use <output.xml> as output file (extension .xml mandatory)\n";
+
+static File *out = 0;
+static int xmllite = 0;
+
+
+class XML:public Language {
+public:
+
+ int indent_level;
+ long id;
+
+ XML() :indent_level(0) , id(0) {
+ }
+
+ virtual ~ XML() {
+ }
+
+ virtual void main(int argc, char *argv[]) {
+ SWIG_typemap_lang("xml");
+ for (int iX = 0; iX < argc; iX++) {
+ if (strcmp(argv[iX], "-xml") == 0) {
+ char *extension = 0;
+ if (iX + 1 >= argc)
+ continue;
+ extension = argv[iX + 1] + strlen(argv[iX + 1]) - 4;
+ if (strcmp(extension, ".xml"))
+ continue;
+ iX++;
+ Swig_mark_arg(iX);
+ String *outfile = NewString(argv[iX]);
+ out = NewFile(outfile, "w", SWIG_output_files());
+ if (!out) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ continue;
+ }
+ if (strcmp(argv[iX], "-xmllang") == 0) {
+ Swig_mark_arg(iX);
+ iX++;
+ SWIG_typemap_lang(argv[iX]);
+ Swig_mark_arg(iX);
+ continue;
+ }
+ if (strcmp(argv[iX], "-help") == 0) {
+ fputs(usage, stdout);
+ }
+ if (strcmp(argv[iX], "-xmllite") == 0) {
+ Swig_mark_arg(iX);
+ xmllite = 1;
+ }
+ }
+
+ // Add a symbol to the parser for conditional compilation
+ Preprocessor_define("SWIGXML 1", 0);
+ }
+
+ /* Top of the parse tree */
+
+ virtual int top(Node *n) {
+ if (out == 0) {
+ String *outfile = Getattr(n, "outfile");
+ String *ext = Swig_file_extension(outfile);
+ // If there's an extension, ext will include the ".".
+ Delslice(outfile, Len(outfile) - Len(ext), DOH_END);
+ Delete(ext);
+ Append(outfile, ".xml");
+ out = NewFile(outfile, "w", SWIG_output_files());
+ if (!out) {
+ FileErrorDisplay(outfile);
+ Exit(EXIT_FAILURE);
+ }
+ }
+ Printf(out, "<?xml version=\"1.0\" ?> \n");
+ Xml_print_tree(n);
+ return SWIG_OK;
+ }
+
+ void print_indent(int l) {
+ int i;
+ for (i = 0; i < indent_level; i++) {
+ Printf(out, " ");
+ }
+ if (l) {
+ Printf(out, " ");
+ }
+ }
+
+ void Xml_print_tree(DOH *obj) {
+ while (obj) {
+ Xml_print_node(obj);
+ obj = nextSibling(obj);
+ }
+ }
+
+ void Xml_print_attributes(Node *obj) {
+ String *k;
+ indent_level += 4;
+ print_indent(0);
+ Printf(out, "<attributelist id=\"%ld\" addr=\"%p\">\n", ++id, obj);
+ indent_level += 4;
+ Iterator ki;
+ ki = First(obj);
+ while (ki.key) {
+ k = ki.key;
+ if ((Cmp(k, "nodeType") == 0)
+ || (Cmp(k, "firstChild") == 0)
+ || (Cmp(k, "lastChild") == 0)
+ || (Cmp(k, "parentNode") == 0)
+ || (Cmp(k, "nextSibling") == 0)
+ || (Cmp(k, "previousSibling") == 0)
+ || (*(Char(k)) == '$')) {
+ /* Do nothing */
+ } else if (Cmp(k, "module") == 0) {
+ Xml_print_module(Getattr(obj, k));
+ } else if (Cmp(k, "baselist") == 0) {
+ Xml_print_baselist(Getattr(obj, k));
+ } else if (!xmllite && Cmp(k, "typescope") == 0) {
+ Xml_print_typescope(Getattr(obj, k));
+ } else if (!xmllite && Cmp(k, "typetab") == 0) {
+ Xml_print_typetab(Getattr(obj, k));
+ } else if (Cmp(k, "kwargs") == 0) {
+ Xml_print_kwargs(Getattr(obj, k));
+ } else if (Cmp(k, "parms") == 0 || Cmp(k, "pattern") == 0) {
+ Xml_print_parmlist(Getattr(obj, k));
+ } else if (Cmp(k, "catchlist") == 0 || Cmp(k, "templateparms") == 0) {
+ Xml_print_parmlist(Getattr(obj, k), Char(k));
+ } else {
+ DOH *o;
+ print_indent(0);
+ if (DohIsString(Getattr(obj, k))) {
+ String *ck = NewString(k);
+ o = Str(Getattr(obj, k));
+ Replaceall(ck, ":", "_");
+ Replaceall(ck, "<", "&lt;");
+ /* Do first to avoid aliasing errors. */
+ Replaceall(o, "&", "&amp;");
+ Replaceall(o, "<", "&lt;");
+ Replaceall(o, "\"", "&quot;");
+ Replaceall(o, "\\", "\\\\");
+ Replaceall(o, "\n", "&#10;");
+ Printf(out, "<attribute name=\"%s\" value=\"%s\" id=\"%ld\" addr=\"%p\" />\n", ck, o, ++id, o);
+ Delete(o);
+ Delete(ck);
+ } else {
+ o = Getattr(obj, k);
+ String *ck = NewString(k);
+ Replaceall(ck, ":", "_");
+ Printf(out, "<attribute name=\"%s\" value=\"%p\" id=\"%ld\" addr=\"%p\" />\n", ck, o, ++id, o);
+ Delete(ck);
+ }
+ }
+ ki = Next(ki);
+ }
+ indent_level -= 4;
+ print_indent(0);
+ Printf(out, "</attributelist>\n");
+ indent_level -= 4;
+ }
+
+ void Xml_print_node(Node *obj) {
+ Node *cobj;
+
+ print_indent(0);
+ Printf(out, "<%s id=\"%ld\" addr=\"%p\">\n", nodeType(obj), ++id, obj);
+ Xml_print_attributes(obj);
+ cobj = firstChild(obj);
+ if (cobj) {
+ indent_level += 4;
+ Printf(out, "\n");
+ Xml_print_tree(cobj);
+ indent_level -= 4;
+ } else {
+ print_indent(1);
+ Printf(out, "\n");
+ }
+ print_indent(0);
+ Printf(out, "</%s>\n", nodeType(obj));
+ }
+
+
+ void Xml_print_parmlist(ParmList *p, const char* markup = "parmlist") {
+
+ print_indent(0);
+ Printf(out, "<%s id=\"%ld\" addr=\"%p\">\n", markup, ++id, p);
+ indent_level += 4;
+ while (p) {
+ print_indent(0);
+ Printf(out, "<parm id=\"%ld\">\n", ++id);
+ Xml_print_attributes(p);
+ print_indent(0);
+ Printf(out, "</parm>\n");
+ p = nextSibling(p);
+ }
+ indent_level -= 4;
+ print_indent(0);
+ Printf(out, "</%s>\n", markup);
+ }
+
+ void Xml_print_baselist(List *p) {
+
+ print_indent(0);
+ Printf(out, "<baselist id=\"%ld\" addr=\"%p\">\n", ++id, p);
+ indent_level += 4;
+ Iterator s;
+ for (s = First(p); s.item; s = Next(s)) {
+ print_indent(0);
+ String *item_name = Xml_escape_string(s.item);
+ Printf(out, "<base name=\"%s\" id=\"%ld\" addr=\"%p\" />\n", item_name, ++id, s.item);
+ Delete(item_name);
+ }
+ indent_level -= 4;
+ print_indent(0);
+ Printf(out, "</baselist>\n");
+ }
+
+ String *Xml_escape_string(String *str) {
+ String *escaped_str = 0;
+ if (str) {
+ escaped_str = NewString(str);
+ Replaceall(escaped_str, "&", "&amp;");
+ Replaceall(escaped_str, "<", "&lt;");
+ Replaceall(escaped_str, "\"", "&quot;");
+ Replaceall(escaped_str, "\\", "\\\\");
+ Replaceall(escaped_str, "\n", "&#10;");
+ }
+ return escaped_str;
+ }
+
+ void Xml_print_module(Node *p) {
+
+ print_indent(0);
+ Printf(out, "<attribute name=\"module\" value=\"%s\" id=\"%ld\" addr=\"%p\" />\n", Getattr(p, "name"), ++id, p);
+ }
+
+ void Xml_print_kwargs(Hash *p) {
+ Xml_print_hash(p, "kwargs");
+ }
+
+ void Xml_print_typescope(Hash *p) {
+
+ Xml_print_hash(p, "typescope");
+ }
+
+ void Xml_print_typetab(Hash *p) {
+
+ Xml_print_hash(p, "typetab");
+ }
+
+
+ void Xml_print_hash(Hash *p, const char *markup) {
+
+ print_indent(0);
+ Printf(out, "<%s id=\"%ld\" addr=\"%p\">\n", markup, ++id, p);
+ Xml_print_attributes(p);
+ indent_level += 4;
+ Iterator n = First(p);
+ while (n.key) {
+ print_indent(0);
+ Printf(out, "<%ssitem id=\"%ld\" addr=\"%p\">\n", markup, ++id, n.item);
+ Xml_print_attributes(n.item);
+ print_indent(0);
+ Printf(out, "</%ssitem>\n", markup);
+ n = Next(n);
+ }
+ indent_level -= 4;
+ print_indent(0);
+ Printf(out, "</%s>\n", markup);
+ }
+
+};
+
+/* -----------------------------------------------------------------------------
+ * Swig_print_xml
+ *
+ * Dump an XML version of the parse tree. This is different from using the -xml
+ * language module normally as it allows the real language module to process the
+ * tree first, possibly stuffing in new attributes, so the XML that is output ends
+ * up being a post-processing version of the tree.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_print_xml(DOH *obj, String *filename) {
+ XML xml;
+ xmllite = 1;
+
+ if (!filename) {
+ out = stdout;
+ } else {
+ out = NewFile(filename, "w", SWIG_output_files());
+ if (!out) {
+ FileErrorDisplay(filename);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ Printf(out, "<?xml version=\"1.0\" ?> \n");
+ xml.Xml_print_tree(obj);
+}
+
+static Language *new_swig_xml() {
+ return new XML();
+}
+extern "C" Language *swig_xml(void) {
+ return new_swig_xml();
+}
diff --git a/contrib/tools/swig/Source/Preprocessor/cpp.c b/contrib/tools/swig/Source/Preprocessor/cpp.c
new file mode 100644
index 0000000000..a80434323c
--- /dev/null
+++ b/contrib/tools/swig/Source/Preprocessor/cpp.c
@@ -0,0 +1,2111 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * cpp.c
+ *
+ * An implementation of a C preprocessor plus some support for additional
+ * SWIG directives.
+ *
+ * - SWIG directives such as %include, %extern, and %import are handled
+ * - A new macro %define ... %enddef can be used for multiline macros
+ * - No preprocessing is performed in %{ ... %} blocks
+ * - Lines beginning with %# are stripped down to #... and passed through.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "preprocessor.h"
+#include <ctype.h>
+
+static Hash *cpp = 0; /* C preprocessor data */
+static int include_all = 0; /* Follow all includes */
+static int ignore_missing = 0;
+static int import_all = 0; /* Follow all includes, but as %import statements */
+static int imported_depth = 0; /* Depth of %imported files */
+static int single_include = 1; /* Only include each file once */
+static Hash *included_files = 0;
+static List *dependencies = 0;
+static Scanner *id_scan = 0;
+static int error_as_warning = 0; /* Understand the cpp #error directive as a special #warning */
+static int expand_defined_operator = 0;
+static int macro_level = 0;
+static int macro_start_line = 0;
+static const String * macro_start_file = 0;
+
+/* Test a character to see if it starts an identifier */
+#define isidentifier(c) ((isalpha(c)) || (c == '_') || (c == '$'))
+
+/* Test a character to see if it valid in an identifier (after the first letter) */
+#define isidchar(c) ((isalnum(c)) || (c == '_') || (c == '$'))
+
+static DOH *Preprocessor_replace(DOH *);
+
+/* Skip whitespace */
+static void skip_whitespace(String *s, String *out) {
+ int c;
+ while ((c = Getc(s)) != EOF) {
+ if (!isspace(c)) {
+ Ungetc(c, s);
+ break;
+ } else if (out)
+ Putc(c, out);
+ }
+}
+
+/* Skip to a specified character taking line breaks into account */
+static int skip_tochar(String *s, int ch, String *out) {
+ int c;
+ while ((c = Getc(s)) != EOF) {
+ if (out)
+ Putc(c, out);
+ if (c == ch)
+ break;
+ if (c == '\\') {
+ c = Getc(s);
+ if ((c != EOF) && (out))
+ Putc(c, out);
+ }
+ }
+ if (c == EOF)
+ return -1;
+ return 0;
+}
+
+static void copy_location(const DOH *s1, DOH *s2) {
+ Setfile(s2, Getfile((DOH *) s1));
+ Setline(s2, Getline((DOH *) s1));
+}
+
+static String *cpp_include(const_String_or_char_ptr fn, int sysfile) {
+ String *s = sysfile ? Swig_include_sys(fn) : Swig_include(fn);
+ if (s && single_include) {
+ String *file = Getfile(s);
+ if (Getattr(included_files, file)) {
+ Delete(s);
+ return 0;
+ }
+ Setattr(included_files, file, file);
+ }
+ if (!s) {
+ if (ignore_missing) {
+ Swig_warning(WARN_PP_MISSING_FILE, Getfile(fn), Getline(fn), "Unable to find '%s'\n", fn);
+ } else {
+ Swig_error(Getfile(fn), Getline(fn), "Unable to find '%s'\n", fn);
+ }
+ } else {
+ String *lf;
+ Seek(s, 0, SEEK_SET);
+ if (!dependencies) {
+ dependencies = NewList();
+ }
+ lf = Copy(Swig_last_file());
+ Append(dependencies, lf);
+ Delete(lf);
+ }
+ return s;
+}
+
+static int is_digits(const String *str) {
+ const char *s = Char(str);
+ int isdigits = (*s != 0);
+ while (*s) {
+ if (!isdigit((int)*s)) {
+ isdigits = 0;
+ break;
+ }
+ s++;
+ }
+ return isdigits;
+}
+
+List *Preprocessor_depend(void) {
+ return dependencies;
+}
+
+/* -----------------------------------------------------------------------------
+ * void Preprocessor_cpp_init() - Initialize the preprocessor
+ * ----------------------------------------------------------------------------- */
+static String *kpp_args = 0;
+static String *kpp_define = 0;
+static String *kpp_defined = 0;
+static String *kpp_elif = 0;
+static String *kpp_else = 0;
+static String *kpp_endif = 0;
+static String *kpp_expanded = 0;
+static String *kpp_if = 0;
+static String *kpp_ifdef = 0;
+static String *kpp_ifndef = 0;
+static String *kpp_name = 0;
+static String *kpp_swigmacro = 0;
+static String *kpp_symbols = 0;
+static String *kpp_undef = 0;
+static String *kpp_value = 0;
+static String *kpp_varargs = 0;
+static String *kpp_error = 0;
+static String *kpp_warning = 0;
+static String *kpp_line = 0;
+static String *kpp_include = 0;
+static String *kpp_pragma = 0;
+static String *kpp_level = 0;
+
+static String *kpp_dline = 0;
+static String *kpp_ddefine = 0;
+static String *kpp_dinclude = 0;
+static String *kpp_dimport = 0;
+static String *kpp_dbeginfile = 0;
+static String *kpp_dextern = 0;
+
+static String *kpp_LINE = 0;
+static String *kpp_FILE = 0;
+
+static String *kpp_hash_if = 0;
+static String *kpp_hash_elif = 0;
+
+void Preprocessor_init(void) {
+ Hash *s;
+
+ kpp_args = NewString("args");
+ kpp_define = NewString("define");
+ kpp_defined = NewString("defined");
+ kpp_else = NewString("else");
+ kpp_elif = NewString("elif");
+ kpp_endif = NewString("endif");
+ kpp_expanded = NewString("*expanded*");
+ kpp_if = NewString("if");
+ kpp_ifdef = NewString("ifdef");
+ kpp_ifndef = NewString("ifndef");
+ kpp_name = NewString("name");
+ kpp_swigmacro = NewString("swigmacro");
+ kpp_symbols = NewString("symbols");
+ kpp_undef = NewString("undef");
+ kpp_value = NewString("value");
+ kpp_error = NewString("error");
+ kpp_warning = NewString("warning");
+ kpp_pragma = NewString("pragma");
+ kpp_level = NewString("level");
+ kpp_line = NewString("line");
+ kpp_include = NewString("include");
+ kpp_varargs = NewString("varargs");
+
+ kpp_dinclude = NewString("%include");
+ kpp_dimport = NewString("%import");
+ kpp_dbeginfile = NewString("%beginfile");
+ kpp_dextern = NewString("%extern");
+ kpp_ddefine = NewString("%define");
+ kpp_dline = NewString("%line");
+
+
+ kpp_LINE = NewString("__LINE__");
+ kpp_FILE = NewString("__FILE__");
+
+ kpp_hash_if = NewString("#if");
+ kpp_hash_elif = NewString("#elif");
+
+ cpp = NewHash();
+ s = NewHash();
+ Setattr(cpp, kpp_symbols, s);
+ Delete(s);
+ Preprocessor_expr_init(); /* Initialize the expression evaluator */
+ included_files = NewHash();
+
+ id_scan = NewScanner();
+
+}
+
+void Preprocessor_delete(void) {
+ Delete(kpp_args);
+ Delete(kpp_define);
+ Delete(kpp_defined);
+ Delete(kpp_else);
+ Delete(kpp_elif);
+ Delete(kpp_endif);
+ Delete(kpp_expanded);
+ Delete(kpp_if);
+ Delete(kpp_ifdef);
+ Delete(kpp_ifndef);
+ Delete(kpp_name);
+ Delete(kpp_swigmacro);
+ Delete(kpp_symbols);
+ Delete(kpp_undef);
+ Delete(kpp_value);
+ Delete(kpp_error);
+ Delete(kpp_warning);
+ Delete(kpp_pragma);
+ Delete(kpp_level);
+ Delete(kpp_line);
+ Delete(kpp_include);
+ Delete(kpp_varargs);
+
+ Delete(kpp_dinclude);
+ Delete(kpp_dimport);
+ Delete(kpp_dbeginfile);
+ Delete(kpp_dextern);
+ Delete(kpp_ddefine);
+ Delete(kpp_dline);
+
+ Delete(kpp_LINE);
+ Delete(kpp_FILE);
+
+ Delete(kpp_hash_if);
+ Delete(kpp_hash_elif);
+
+ Delete(cpp);
+ Delete(included_files);
+ Preprocessor_expr_delete();
+ DelScanner(id_scan);
+
+ Delete(dependencies);
+
+ Delete(Swig_add_directory(0));
+}
+
+/* -----------------------------------------------------------------------------
+ * void Preprocessor_include_all() - Instruct preprocessor to include all files
+ * ----------------------------------------------------------------------------- */
+void Preprocessor_include_all(int a) {
+ include_all = a;
+}
+
+void Preprocessor_import_all(int a) {
+ import_all = a;
+}
+
+void Preprocessor_ignore_missing(int a) {
+ ignore_missing = a;
+}
+
+void Preprocessor_error_as_warning(int a) {
+ error_as_warning = a;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Preprocessor_define()
+ *
+ * Defines a new C preprocessor symbol. swigmacro specifies whether or not the macro has
+ * SWIG macro semantics.
+ * ----------------------------------------------------------------------------- */
+
+
+String *Macro_vararg_name(const_String_or_char_ptr str, const_String_or_char_ptr line) {
+ String *argname;
+ String *varargname;
+ char *s, *dots;
+
+ argname = Copy(str);
+ s = Char(argname);
+ dots = strchr(s, '.');
+ if (!dots) {
+ Delete(argname);
+ return NULL;
+ }
+
+ if (strcmp(dots, "...") != 0) {
+ Swig_error(Getfile(line), Getline(line), "Illegal macro argument name '%s'\n", str);
+ Delete(argname);
+ return NULL;
+ }
+ if (dots == s) {
+ varargname = NewString("__VA_ARGS__");
+ } else {
+ *dots = '\0';
+ varargname = NewString(s);
+ }
+ Delete(argname);
+ return varargname;
+}
+
+Hash *Preprocessor_define(const_String_or_char_ptr _str, int swigmacro) {
+ String *macroname = 0, *argstr = 0, *macrovalue = 0, *file = 0, *s = 0;
+ Hash *macro = 0, *symbols = 0, *m1;
+ List *arglist = 0;
+ int c, line;
+ int varargs = 0;
+ String *str;
+
+ assert(cpp);
+ assert(_str);
+
+ /* First make sure that string is actually a string */
+ if (DohCheck(_str)) {
+ s = Copy(_str);
+ copy_location(_str, s);
+ str = s;
+ } else {
+ str = NewString((char *) _str);
+ }
+ Seek(str, 0, SEEK_SET);
+ line = Getline(str);
+ file = Getfile(str);
+
+ /* Skip over any leading whitespace */
+ skip_whitespace(str, 0);
+
+ /* Now look for a macro name */
+ macroname = NewStringEmpty();
+ copy_location(str, macroname);
+ while ((c = Getc(str)) != EOF) {
+ if (c == '(') {
+ argstr = NewStringEmpty();
+ copy_location(str, argstr);
+ /* It is a macro. Go extract its argument string */
+ while ((c = Getc(str)) != EOF) {
+ if (c == ')')
+ break;
+ else
+ Putc(c, argstr);
+ }
+ if (c != ')') {
+ Swig_error(Getfile(argstr), Getline(argstr), "Missing \')\' in macro parameters\n");
+ goto macro_error;
+ }
+ break;
+ } else if (isidchar(c) || (c == '%')) {
+ Putc(c, macroname);
+ } else if (isspace(c)) {
+ break;
+ } else if (c == '\\') {
+ c = Getc(str);
+ if (c != '\n') {
+ Ungetc(c, str);
+ Ungetc('\\', str);
+ break;
+ }
+ } else {
+ Ungetc(c, str);
+ break;
+ }
+ }
+ if (!swigmacro)
+ skip_whitespace(str, 0);
+ macrovalue = NewStringEmpty();
+ copy_location(str, macrovalue);
+ while ((c = Getc(str)) != EOF) {
+ Putc(c, macrovalue);
+ }
+
+ /* If there are any macro arguments, convert into a list */
+ if (argstr) {
+ String *argname, *varargname;
+ arglist = NewList();
+ Seek(argstr, 0, SEEK_SET);
+ argname = NewStringEmpty();
+ while ((c = Getc(argstr)) != EOF) {
+ if (c == ',') {
+ varargname = Macro_vararg_name(argname, argstr);
+ if (varargname) {
+ Delete(varargname);
+ Swig_error(Getfile(argstr), Getline(argstr), "Variable length macro argument must be last parameter\n");
+ } else {
+ Append(arglist, argname);
+ }
+ Delete(argname);
+ argname = NewStringEmpty();
+ } else if (isidchar(c) || (c == '.')) {
+ Putc(c, argname);
+ } else if (!(isspace(c) || (c == '\\'))) {
+ Delete(argname);
+ Swig_error(Getfile(argstr), Getline(argstr), "Illegal character in macro argument name\n");
+ goto macro_error;
+ }
+ }
+ if (Len(argname)) {
+ /* Check for varargs */
+ varargname = Macro_vararg_name(argname, argstr);
+ if (varargname) {
+ Append(arglist, varargname);
+ Delete(varargname);
+ varargs = 1;
+ } else {
+ Append(arglist, argname);
+ }
+ }
+ Delete(argname);
+ }
+
+ if (!swigmacro) {
+ Replace(macrovalue, "\\\n", " ", DOH_REPLACE_NOQUOTE);
+ }
+
+ /* Look for special # substitutions. We only consider # that appears
+ outside of quotes and comments */
+
+ {
+ int state = 0;
+ char *cc = Char(macrovalue);
+ while (*cc) {
+ switch (state) {
+ case 0:
+ if (*cc == '#')
+ *cc = '\001';
+ else if (*cc == '/')
+ state = 10;
+ else if (*cc == '\'')
+ state = 20;
+ else if (*cc == '\"')
+ state = 30;
+ break;
+ case 10:
+ if (*cc == '*')
+ state = 11;
+ else if (*cc == '/')
+ state = 15;
+ else {
+ state = 0;
+ cc--;
+ }
+ break;
+ case 11:
+ if (*cc == '*')
+ state = 12;
+ break;
+ case 12:
+ if (*cc == '/')
+ state = 0;
+ else if (*cc != '*')
+ state = 11;
+ break;
+ case 15:
+ if (*cc == '\n')
+ state = 0;
+ break;
+ case 20:
+ if (*cc == '\'')
+ state = 0;
+ if (*cc == '\\')
+ state = 21;
+ break;
+ case 21:
+ state = 20;
+ break;
+ case 30:
+ if (*cc == '\"')
+ state = 0;
+ if (*cc == '\\')
+ state = 31;
+ break;
+ case 31:
+ state = 30;
+ break;
+ default:
+ break;
+ }
+ cc++;
+ }
+ }
+
+ /* Get rid of whitespace surrounding # */
+ /* Replace(macrovalue,"#","\001",DOH_REPLACE_NOQUOTE); */
+ while (strstr(Char(macrovalue), "\001 ")) {
+ Replace(macrovalue, "\001 ", "\001", DOH_REPLACE_ANY);
+ }
+ while (strstr(Char(macrovalue), " \001")) {
+ Replace(macrovalue, " \001", "\001", DOH_REPLACE_ANY);
+ }
+ /* Replace '##' with a special token */
+ Replace(macrovalue, "\001\001", "\002", DOH_REPLACE_ANY);
+ /* Replace '#@' with a special token */
+ Replace(macrovalue, "\001@", "\004", DOH_REPLACE_ANY);
+ /* Replace '##@' with a special token */
+ Replace(macrovalue, "\002@", "\005", DOH_REPLACE_ANY);
+
+ /* Go create the macro */
+ macro = NewHash();
+ Setattr(macro, kpp_name, macroname);
+
+ if (arglist) {
+ Setattr(macro, kpp_args, arglist);
+ Delete(arglist);
+ if (varargs) {
+ Setattr(macro, kpp_varargs, "1");
+ }
+ }
+ Setattr(macro, kpp_value, macrovalue);
+ Setline(macro, line);
+ Setfile(macro, file);
+ if (swigmacro) {
+ Setattr(macro, kpp_swigmacro, "1");
+ }
+ symbols = Getattr(cpp, kpp_symbols);
+ if ((m1 = Getattr(symbols, macroname))) {
+ if (!Checkattr(m1, kpp_value, macrovalue)) {
+ Swig_error(Getfile(macroname), Getline(macroname), "Macro '%s' redefined,\n", macroname);
+ Swig_error(Getfile(m1), Getline(m1), "previous definition of '%s'.\n", macroname);
+ goto macro_error;
+ }
+ } else {
+ Setattr(symbols, macroname, macro);
+ Delete(macro);
+ }
+
+ Delete(macroname);
+ Delete(macrovalue);
+
+ Delete(str);
+ Delete(argstr);
+ return macro;
+
+macro_error:
+ Delete(str);
+ Delete(argstr);
+ Delete(arglist);
+ Delete(macroname);
+ Delete(macrovalue);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Preprocessor_undef()
+ *
+ * Undefines a macro.
+ * ----------------------------------------------------------------------------- */
+void Preprocessor_undef(const_String_or_char_ptr str) {
+ Hash *symbols;
+ assert(cpp);
+ symbols = Getattr(cpp, kpp_symbols);
+ Delattr(symbols, str);
+}
+
+/* -----------------------------------------------------------------------------
+ * find_args()
+ *
+ * Isolates macro arguments and returns them in a list. For each argument,
+ * leading and trailing whitespace is stripped (ala K&R, pg. 230).
+ * ----------------------------------------------------------------------------- */
+static List *find_args(String *s, int ismacro, String *macro_name) {
+ List *args;
+ String *str;
+ int c, level;
+ long pos;
+
+ /* Create a new list */
+ args = NewList();
+ copy_location(s, args);
+
+ /* First look for a '(' */
+ pos = Tell(s);
+ skip_whitespace(s, 0);
+
+ /* Now see if the next character is a '(' */
+ c = Getc(s);
+ if (c != '(') {
+ /* Not a macro, bail out now! */
+ assert(pos != -1);
+ (void)Seek(s, pos, SEEK_SET);
+ Delete(args);
+ return 0;
+ }
+ c = Getc(s);
+ /* Okay. This appears to be a macro so we will start isolating arguments */
+ while (c != EOF) {
+ if (isspace(c)) {
+ skip_whitespace(s, 0); /* Skip leading whitespace */
+ c = Getc(s);
+ }
+ str = NewStringEmpty();
+ copy_location(s, str);
+ level = 0;
+ while (c != EOF) {
+ if (c == '\"') {
+ Putc(c, str);
+ skip_tochar(s, '\"', str);
+ c = Getc(s);
+ continue;
+ } else if (c == '\'') {
+ Putc(c, str);
+ skip_tochar(s, '\'', str);
+ c = Getc(s);
+ continue;
+ } else if (c == '/') {
+ /* Ensure comments are ignored by eating up the characters */
+ c = Getc(s);
+ /* Handle / * ... * / type comments (multi-line) */
+ if (c == '*') {
+ while ((c = Getc(s)) != EOF) {
+ if (c == '*') {
+ c = Getc(s);
+ if (c == '/' || c == EOF)
+ break;
+ }
+ }
+ c = Getc(s);
+ continue;
+ }
+ /* Handle // ... type comments (single-line) */
+ if (c == '/') {
+ while ((c = Getc(s)) != EOF) {
+ if (c == '\n') {
+ break;
+ }
+ }
+ c = Getc(s);
+ continue;
+ }
+ /* ensure char is available in the stream as this was not a comment*/
+ Ungetc(c, s);
+ c = '/';
+ }
+ if ((c == ',') && (level == 0))
+ break;
+ if ((c == ')') && (level == 0))
+ break;
+ Putc(c, str);
+ if (c == '(')
+ level++;
+ if (c == ')')
+ level--;
+ c = Getc(s);
+ }
+ if (level > 0) {
+ goto unterm;
+ }
+ Chop(str);
+ Append(args, str);
+ Delete(str);
+ if (c == ')')
+ return args;
+ c = Getc(s);
+ }
+unterm:
+ if (ismacro)
+ Swig_error(Getfile(args), Getline(args), "Unterminated call invoking macro '%s'\n", macro_name);
+ else
+ Swig_error(Getfile(args), Getline(args), "Unterminated call to '%s'\n", macro_name);
+ return args;
+}
+
+/* -----------------------------------------------------------------------------
+ * DOH *get_filename()
+ *
+ * Read a filename from str. A filename can be enclosed in quotes, angle brackets,
+ * or bare.
+ * ----------------------------------------------------------------------------- */
+
+static String *get_filename(String *str, int *sysfile) {
+ String *fn;
+ int c;
+
+ fn = NewStringEmpty();
+ copy_location(str, fn);
+ c = Getc(str);
+ *sysfile = 0;
+ if (c == '\"') {
+ while (((c = Getc(str)) != EOF) && (c != '\"'))
+ Putc(c, fn);
+ } else if (c == '<') {
+ *sysfile = 1;
+ while (((c = Getc(str)) != EOF) && (c != '>'))
+ Putc(c, fn);
+ } else {
+ String *preprocessed_str;
+ Putc(c, fn);
+ while (((c = Getc(str)) != EOF) && (!isspace(c)))
+ Putc(c, fn);
+ if (isspace(c))
+ Ungetc(c, str);
+ preprocessed_str = Preprocessor_replace(fn);
+ Seek(preprocessed_str, 0, SEEK_SET);
+ Delete(fn);
+
+ fn = NewStringEmpty();
+ copy_location(preprocessed_str, fn);
+ c = Getc(preprocessed_str);
+ if (c == '\"') {
+ while (((c = Getc(preprocessed_str)) != EOF) && (c != '\"'))
+ Putc(c, fn);
+ } else if (c == '<') {
+ *sysfile = 1;
+ while (((c = Getc(preprocessed_str)) != EOF) && (c != '>'))
+ Putc(c, fn);
+ } else {
+ fn = Copy(preprocessed_str);
+ }
+ Delete(preprocessed_str);
+ }
+ Swig_filename_unescape(fn);
+ Swig_filename_correct(fn);
+ Seek(fn, 0, SEEK_SET);
+ return fn;
+}
+
+static String *get_options(String *str) {
+ int c;
+ c = Getc(str);
+ if (c == '(') {
+ String *opt;
+ int level = 1;
+ opt = NewString("(");
+ while (((c = Getc(str)) != EOF)) {
+ Putc(c, opt);
+ switch (c) {
+ case ')':
+ level--;
+ if (!level)
+ return opt;
+ break;
+ case '(':
+ level++;
+ break;
+ case '"':
+ /* Skip over quoted strings */
+ while (1) {
+ c = Getc(str);
+ if (c == EOF)
+ goto bad;
+ Putc(c, opt);
+ if (c == '"')
+ break;
+ if (c == '\\') {
+ c = Getc(str);
+ if (c == EOF)
+ goto bad;
+ Putc(c, opt);
+ }
+ }
+ break;
+ }
+ }
+bad:
+ Delete(opt);
+ return 0;
+ } else {
+ Ungetc(c, str);
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * expand_macro()
+ *
+ * Perform macro expansion and return a new string. Returns NULL if some sort
+ * of error occurred.
+ * name - name of the macro
+ * args - arguments passed to the macro
+ * line_file - only used for line/file name when reporting errors
+ * ----------------------------------------------------------------------------- */
+
+static String *expand_macro(String *name, List *args, String *line_file) {
+ String *ns;
+ DOH *symbols, *macro, *margs, *mvalue, *temp, *tempa, *e;
+ int i, l;
+ int isvarargs = 0;
+
+ symbols = Getattr(cpp, kpp_symbols);
+ if (!symbols)
+ return 0;
+
+ /* See if the name is actually defined */
+ macro = Getattr(symbols, name);
+ if (!macro)
+ return 0;
+
+ if (macro_level == 0) {
+ /* Store the start of the macro should the macro contain __LINE__ and __FILE__ for expansion */
+ macro_start_line = Getline(args ? args : line_file);
+ macro_start_file = Getfile(args ? args : line_file);
+ }
+ macro_level++;
+
+ if (Getattr(macro, kpp_expanded)) {
+ ns = NewStringEmpty();
+ Append(ns, name);
+ if (args) {
+ int lenargs = Len(args);
+ if (lenargs)
+ Putc('(', ns);
+ for (i = 0; i < lenargs; i++) {
+ Append(ns, Getitem(args, i));
+ if (i < (lenargs - 1))
+ Putc(',', ns);
+ }
+ if (i)
+ Putc(')', ns);
+ }
+ macro_level--;
+ return ns;
+ }
+
+ /* Get macro arguments and value */
+ mvalue = Getattr(macro, kpp_value);
+ assert(mvalue);
+ margs = Getattr(macro, kpp_args);
+
+ if (args && Getattr(macro, kpp_varargs)) {
+ isvarargs = 1;
+ /* Variable length argument macro. We need to collect all of the extra arguments into a single argument */
+ if (Len(args) >= (Len(margs) - 1)) {
+ int i;
+ int vi, na;
+ String *vararg = NewStringEmpty();
+ vi = Len(margs) - 1;
+ na = Len(args);
+ for (i = vi; i < na; i++) {
+ Append(vararg, Getitem(args, i));
+ if ((i + 1) < na) {
+ Append(vararg, ",");
+ }
+ }
+ /* Remove arguments */
+ for (i = vi; i < na; i++) {
+ Delitem(args, vi);
+ }
+ Append(args, vararg);
+ Delete(vararg);
+ }
+ }
+
+ if (args && margs && Len(margs) == 0 && Len(args) == 1 && Len(Getitem(args, 0)) == 0) {
+ /* FOO() can invoke a macro defined as FOO(X) as well as one defined FOO().
+ *
+ * Handle this by removing the only argument if it's empty and the macro
+ * expects no arguments.
+ *
+ * We don't need to worry about varargs here - a varargs macro will always have
+ * Len(margs) >= 1, since the varargs are put in the final macro argument.
+ */
+ Delitem(args, 0);
+ }
+
+ /* If there are arguments, see if they match what we were given */
+ if (args && (!margs || Len(margs) != Len(args))) {
+ if (margs && Len(margs) > (1 + isvarargs))
+ Swig_error(macro_start_file, macro_start_line, "Macro '%s' expects %d arguments\n", name, Len(margs) - isvarargs);
+ else if (margs && Len(margs) == (1 + isvarargs))
+ Swig_error(macro_start_file, macro_start_line, "Macro '%s' expects 1 argument\n", name);
+ else
+ Swig_error(macro_start_file, macro_start_line, "Macro '%s' expects no arguments\n", name);
+ macro_level--;
+ return 0;
+ }
+
+ /* If the macro expects arguments, but none were supplied, we leave it in place */
+ if (!args && margs) {
+ macro_level--;
+ return NewString(name);
+ }
+
+ /* Copy the macro value */
+ ns = Copy(mvalue);
+ copy_location(mvalue, ns);
+
+ /* Tag the macro as being expanded. This is to avoid recursion in
+ macro expansion */
+
+ temp = NewStringEmpty();
+ tempa = NewStringEmpty();
+ if (args && margs) {
+ l = Len(margs);
+ for (i = 0; i < l; i++) {
+ DOH *arg, *aname;
+ String *reparg;
+ arg = Getitem(args, i); /* Get an argument value */
+ reparg = Preprocessor_replace(arg);
+ aname = Getitem(margs, i); /* Get macro argument name */
+ if (strstr(Char(ns), "\001")) {
+ /* Try to replace a quoted version of the argument */
+ Clear(temp);
+ Clear(tempa);
+ Printf(temp, "\001%s", aname);
+ Printf(tempa, "\"%s\"", arg);
+ Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
+ }
+ if (strstr(Char(ns), "\002")) {
+ /* Look for concatenation tokens */
+ Clear(temp);
+ Clear(tempa);
+ Printf(temp, "\002%s", aname);
+ Append(tempa, "\002\003");
+ Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
+ Clear(temp);
+ Clear(tempa);
+ Printf(temp, "%s\002", aname);
+ Append(tempa, "\003\002");
+ Replace(ns, temp, tempa, DOH_REPLACE_ID_BEGIN);
+ }
+
+ /* Non-standard macro expansion. The value `x` is replaced by a quoted
+ version of the argument except that if the argument is already quoted
+ nothing happens */
+
+ if (strchr(Char(ns), '`')) {
+ String *rep;
+ char *c;
+ Clear(temp);
+ Printf(temp, "`%s`", aname);
+ c = Char(arg);
+ if (*c == '\"') {
+ rep = arg;
+ } else {
+ Clear(tempa);
+ Printf(tempa, "\"%s\"", arg);
+ rep = tempa;
+ }
+ Replace(ns, temp, rep, DOH_REPLACE_ANY);
+ }
+
+ /* Non-standard mangle expansions.
+ The #@Name is replaced by mangle_arg(Name). */
+ if (strstr(Char(ns), "\004")) {
+ String *marg = Swig_string_mangle(arg);
+ Clear(temp);
+ Printf(temp, "\004%s", aname);
+ Replace(ns, temp, marg, DOH_REPLACE_ID_END);
+ Delete(marg);
+ }
+ if (strstr(Char(ns), "\005")) {
+ String *marg = Swig_string_mangle(arg);
+ Clear(temp);
+ Clear(tempa);
+ Printf(temp, "\005%s", aname);
+ Printf(tempa, "\"%s\"", marg);
+ Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
+ Delete(marg);
+ }
+
+ if (isvarargs && i == l - 1 && Len(arg) == 0) {
+ /* Zero length varargs macro argument. We search for commas that might appear before and nuke them */
+ char *a, *s, *t, *name;
+ int namelen;
+ s = Char(ns);
+ name = Char(aname);
+ namelen = Len(aname);
+ a = strstr(s, name);
+ while (a) {
+ char ca = a[namelen];
+ if (!isidchar((int) ca)) {
+ /* Matched the entire vararg name, not just a prefix */
+ if (a > s) {
+ t = a - 1;
+ if (*t == '\002') {
+ t--;
+ while (t >= s) {
+ if (isspace((int) *t))
+ t--;
+ else if (*t == ',') {
+ *t = ' ';
+ } else
+ break;
+ }
+ }
+ }
+ }
+ a = strstr(a + namelen, name);
+ }
+ }
+ /* Replace(ns, aname, arg, DOH_REPLACE_ID); */
+ Replace(ns, aname, reparg, DOH_REPLACE_ID); /* Replace expanded args */
+ Replace(ns, "\003", arg, DOH_REPLACE_ANY); /* Replace unexpanded arg */
+ Delete(reparg);
+ }
+ }
+ Replace(ns, "\002", "", DOH_REPLACE_ANY); /* Get rid of concatenation tokens */
+ Replace(ns, "\001", "#", DOH_REPLACE_ANY); /* Put # back (non-standard C) */
+ Replace(ns, "\004", "#@", DOH_REPLACE_ANY); /* Put # back (non-standard C) */
+
+ /* Expand this macro even further */
+ Setattr(macro, kpp_expanded, "1");
+
+ e = Preprocessor_replace(ns);
+
+ Delattr(macro, kpp_expanded);
+ Delete(ns);
+
+ if (Getattr(macro, kpp_swigmacro)) {
+ String *g;
+ String *f = NewStringEmpty();
+ Seek(e, 0, SEEK_SET);
+ copy_location(macro, e);
+ g = Preprocessor_parse(e);
+
+#if 0
+ /* Drop the macro in place, but with a marker around it */
+ Printf(f, "/*@%s,%d,%s@*/%s/*@@*/", Getfile(macro), Getline(macro), name, g);
+#else
+ /* Use simplified around markers to properly count lines in cscanner.c */
+ if (strchr(Char(g), '\n')) {
+ Printf(f, "/*@SWIG:%s,%d,%s@*/%s/*@SWIG@*/", Getfile(macro), Getline(macro), name, g);
+#if 0
+ Printf(f, "/*@SWIG:%s@*/%s/*@SWIG@*/", name, g);
+#endif
+ } else {
+ Append(f, g);
+ }
+#endif
+
+ Delete(g);
+ Delete(e);
+ e = f;
+ }
+ macro_level--;
+ Delete(temp);
+ Delete(tempa);
+ return e;
+}
+
+/* -----------------------------------------------------------------------------
+ * DOH *Preprocessor_replace(DOH *s)
+ *
+ * Performs a macro substitution on a string s. Returns a new string with
+ * substitutions applied. This function works by walking down s and looking
+ * for identifiers. When found, a check is made to see if they are macros
+ * which are then expanded.
+ * ----------------------------------------------------------------------------- */
+
+/* #define SWIG_PUT_BUFF */
+
+static DOH *Preprocessor_replace(DOH *s) {
+ DOH *ns, *symbols, *m;
+ int c, i, state = 0;
+ String *id = NewStringEmpty();
+
+ assert(cpp);
+ symbols = Getattr(cpp, kpp_symbols);
+
+ ns = NewStringEmpty();
+ copy_location(s, ns);
+ Seek(s, 0, SEEK_SET);
+
+ /* Try to locate identifiers in s and replace them with macro replacements */
+ while ((c = Getc(s)) != EOF) {
+ switch (state) {
+ case 0:
+ if (isidentifier(c)) {
+ Clear(id);
+ Putc(c, id);
+ state = 4;
+ } else if (c == '%') {
+ Clear(id);
+ Putc(c, id);
+ state = 2;
+ } else if (c == '#') {
+ Clear(id);
+ Putc(c, id);
+ state = 4;
+ } else if (c == '\"') {
+ Putc(c, ns);
+ skip_tochar(s, '\"', ns);
+ } else if (c == '\'') {
+ Putc(c, ns);
+ skip_tochar(s, '\'', ns);
+ } else if (c == '/') {
+ Putc(c, ns);
+ state = 10;
+ } else if (c == '\\') {
+ Putc(c, ns);
+ c = Getc(s);
+ if (c == '\n') {
+ Putc(c, ns);
+ } else {
+ Ungetc(c, s);
+ }
+ } else if (c == '\n') {
+ Putc(c, ns);
+ expand_defined_operator = 0;
+ } else {
+ Putc(c, ns);
+ }
+ break;
+ case 2:
+ /* Found '%#' */
+ if (c == '#') {
+ Putc(c, id);
+ state = 4;
+ } else {
+ Ungetc(c, s);
+ state = 4;
+ }
+ break;
+ case 4: /* An identifier */
+ if (isidchar(c)) {
+ Putc(c, id);
+ state = 4;
+ } else {
+ /* We found the end of a valid identifier */
+ Ungetc(c, s);
+ /* See if this is the special "defined" operator */
+ if (Equal(kpp_defined, id)) {
+ if (expand_defined_operator) {
+ int lenargs = 0;
+ DOH *args = 0;
+ /* See whether or not a parenthesis has been used */
+ skip_whitespace(s, 0);
+ c = Getc(s);
+ if (c == '(') {
+ Ungetc(c, s);
+ args = find_args(s, 0, kpp_defined);
+ } else if (isidchar(c)) {
+ DOH *arg = NewStringEmpty();
+ args = NewList();
+ Putc(c, arg);
+ while (((c = Getc(s)) != EOF)) {
+ if (!isidchar(c)) {
+ Ungetc(c, s);
+ break;
+ }
+ Putc(c, arg);
+ }
+ if (Len(arg))
+ Append(args, arg);
+ Delete(arg);
+ } else {
+ Seek(s, -1, SEEK_CUR);
+ }
+ lenargs = Len(args);
+ if ((!args) || (!lenargs)) {
+ /* This is not a defined() operator. */
+ Append(ns, id);
+ state = 0;
+ break;
+ }
+ for (i = 0; i < lenargs; i++) {
+ DOH *o = Getitem(args, i);
+ if (!Getattr(symbols, o)) {
+ break;
+ }
+ }
+ if (i < lenargs)
+ Putc('0', ns);
+ else
+ Putc('1', ns);
+ Delete(args);
+ } else {
+ Append(ns, id);
+ }
+ state = 0;
+ break;
+ } else if (Equal(kpp_LINE, id)) {
+ Printf(ns, "%d", macro_level > 0 ? macro_start_line : Getline(s));
+ state = 0;
+ break;
+ } else if (Equal(kpp_FILE, id)) {
+ String *fn = Copy(macro_level > 0 ? macro_start_file : Getfile(s));
+ Replaceall(fn, "\\", "\\\\");
+ Printf(ns, "\"%s\"", fn);
+ Delete(fn);
+ state = 0;
+ break;
+ } else if (Equal(kpp_hash_if, id) || Equal(kpp_hash_elif, id)) {
+ expand_defined_operator = 1;
+ Append(ns, id);
+ /*
+ } else if (Equal("%#if", id) || Equal("%#ifdef", id)) {
+ Swig_warning(998, Getfile(s), Getline(s), "Found: %s preprocessor directive.\n", id);
+ Append(ns, id);
+ } else if (Equal("#ifdef", id) || Equal("#ifndef", id)) {
+ Swig_warning(998, Getfile(s), Getline(s), "The %s preprocessor directive does not work in macros, try #if instead.\n", id);
+ Append(ns, id);
+ */
+ } else if ((m = Getattr(symbols, id))) {
+ /* See if the macro is defined in the preprocessor symbol table */
+ DOH *args = 0;
+ DOH *e;
+ int macro_additional_lines = 0;
+ /* See if the macro expects arguments */
+ if (Getattr(m, kpp_args)) {
+ /* Yep. We need to go find the arguments and do a substitution */
+ int line = Getline(s);
+ args = find_args(s, 1, id);
+ macro_additional_lines = Getline(s) - line;
+ assert(macro_additional_lines >= 0);
+ } else {
+ args = 0;
+ }
+ e = expand_macro(id, args, s);
+ if (e) {
+ Append(ns, e);
+ }
+ while (macro_additional_lines--) {
+ Putc('\n', ns);
+ }
+ Delete(e);
+ Delete(args);
+ } else {
+ Append(ns, id);
+ }
+ state = 0;
+ }
+ break;
+ case 10:
+ if (c == '/')
+ state = 11;
+ else if (c == '*')
+ state = 12;
+ else {
+ Ungetc(c, s);
+ state = 0;
+ break;
+ }
+ Putc(c, ns);
+ break;
+ case 11:
+ /* in C++ comment */
+ Putc(c, ns);
+ if (c == '\n') {
+ expand_defined_operator = 0;
+ state = 0;
+ }
+ break;
+ case 12:
+ /* in C comment */
+ Putc(c, ns);
+ if (c == '*')
+ state = 13;
+ break;
+ case 13:
+ Putc(c, ns);
+ if (c == '/')
+ state = 0;
+ else if (c != '*')
+ state = 12;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ }
+
+ /* Identifier at the end */
+ if (state == 2 || state == 4) {
+ /* See if this is the special "defined" operator */
+ if (Equal(kpp_defined, id)) {
+ Swig_error(Getfile(s), Getline(s), "No arguments given to defined()\n");
+ } else if (Equal(kpp_LINE, id)) {
+ Printf(ns, "%d", macro_level > 0 ? macro_start_line : Getline(s));
+ } else if (Equal(kpp_FILE, id)) {
+ String *fn = Copy(macro_level > 0 ? macro_start_file : Getfile(s));
+ Replaceall(fn, "\\", "\\\\");
+ Printf(ns, "\"%s\"", fn);
+ Delete(fn);
+ } else if (Getattr(symbols, id)) {
+ DOH *e;
+ /* Yes. There is a macro here */
+ /* See if the macro expects arguments */
+ e = expand_macro(id, 0, s);
+ if (e)
+ Append(ns, e);
+ Delete(e);
+ } else {
+ Append(ns, id);
+ }
+ }
+ Delete(id);
+ return ns;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * int checkpp_id(DOH *s)
+ *
+ * Checks the string s to see if it contains any unresolved identifiers. This
+ * function contains the heuristic that determines whether or not a macro
+ * definition passes through the preprocessor as a constant declaration.
+ * ----------------------------------------------------------------------------- */
+static int checkpp_id(DOH *s) {
+ int c;
+ int hastok = 0;
+ Scanner *scan = id_scan;
+
+ Seek(s, 0, SEEK_SET);
+
+ Scanner_clear(scan);
+ s = Copy(s);
+ Seek(s, SEEK_SET, 0);
+ Scanner_push(scan, s);
+ while ((c = Scanner_token(scan))) {
+ hastok = 1;
+ if ((c == SWIG_TOKEN_ID) || (c == SWIG_TOKEN_LBRACE) || (c == SWIG_TOKEN_RBRACE))
+ return 1;
+ }
+ if (!hastok)
+ return 1;
+ return 0;
+}
+
+/* addline(). Utility function for adding lines to a chunk */
+static void addline(DOH *s1, DOH *s2, int allow) {
+ if (allow) {
+ Append(s1, s2);
+ } else {
+ char *c = Char(s2);
+ while (*c) {
+ if (*c == '\n')
+ Putc('\n', s1);
+ c++;
+ }
+ }
+}
+
+static void add_chunk(DOH *ns, DOH *chunk, int allow) {
+ DOH *echunk;
+ Seek(chunk, 0, SEEK_SET);
+ if (allow) {
+ echunk = Preprocessor_replace(chunk);
+ addline(ns, echunk, allow);
+ Delete(echunk);
+ } else {
+ addline(ns, chunk, 0);
+ }
+ Clear(chunk);
+}
+
+/*
+ push/pop_imported(): helper functions for defining and undefining
+ SWIGIMPORTED (when %importing a file).
+ */
+static void push_imported(void) {
+ if (imported_depth == 0) {
+ Preprocessor_define("SWIGIMPORTED 1", 0);
+ }
+ ++imported_depth;
+}
+
+static void pop_imported(void) {
+ --imported_depth;
+ if (imported_depth == 0) {
+ Preprocessor_undef("SWIGIMPORTED");
+ }
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Preprocessor_parse()
+ *
+ * Parses the string s. Returns a new string containing the preprocessed version.
+ *
+ * Parsing rules :
+ * 1. Lines starting with # are C preprocessor directives
+ * 2. Macro expansion inside strings is not allowed
+ * 3. All code inside false conditionals is changed to blank lines
+ * 4. Code in %{, %} is not parsed because it may need to be
+ * included inline (with all preprocessor directives included).
+ * ----------------------------------------------------------------------------- */
+
+String *Preprocessor_parse(String *s) {
+ String *ns; /* New string containing the preprocessed text */
+ String *chunk, *decl;
+ Hash *symbols;
+ String *id = 0, *value = 0, *comment = 0;
+ int i, state, e, c;
+ int start_line = 0;
+ int allow = 1;
+ int level = 0;
+ int dlevel = 0;
+ int filelevel = 0;
+ int mask = 0;
+ int start_level = 0;
+ int cpp_lines = 0;
+ int cond_lines[256];
+
+ /* Blow away all carriage returns */
+ Replace(s, "\015", "", DOH_REPLACE_ANY);
+
+ ns = NewStringEmpty(); /* Return result */
+
+ decl = NewStringEmpty();
+ id = NewStringEmpty();
+ value = NewStringEmpty();
+ comment = NewStringEmpty();
+ chunk = NewStringEmpty();
+ copy_location(s, chunk);
+ copy_location(s, ns);
+ symbols = Getattr(cpp, kpp_symbols);
+
+ state = 0;
+ while ((c = Getc(s)) != EOF) {
+ switch (state) {
+ case 0: /* Initial state - in first column */
+ /* Look for C preprocessor directives. Otherwise, go directly to state 1 */
+ if (c == '#') {
+ copy_location(s, chunk);
+ add_chunk(ns, chunk, allow);
+ cpp_lines = 1;
+ state = 40;
+ } else if (isspace(c)) {
+ Putc(c, chunk);
+ skip_whitespace(s, chunk);
+ } else {
+ state = 1;
+ Ungetc(c, s);
+ }
+ break;
+ case 1: /* Non-preprocessor directive */
+ /* Look for SWIG directives */
+ if (c == '%') {
+ state = 100;
+ break;
+ }
+ Putc(c, chunk);
+ if (c == '\n')
+ state = 0;
+ else if (c == '\"') {
+ start_line = Getline(s);
+ if (skip_tochar(s, '\"', chunk) < 0) {
+ Swig_error(Getfile(s), start_line, "Unterminated string constant\n");
+ }
+ } else if (c == '\'') {
+ start_line = Getline(s);
+ if (skip_tochar(s, '\'', chunk) < 0) {
+ Swig_error(Getfile(s), start_line, "Unterminated character constant\n");
+ }
+ } else if (c == '/')
+ state = 30; /* Comment */
+ break;
+
+ case 30: /* Possibly a comment string of some sort */
+ start_line = Getline(s);
+ Putc(c, chunk);
+ if (c == '/')
+ state = 31;
+ else if (c == '*')
+ state = 32;
+ else
+ state = 1;
+ break;
+ case 31:
+ Putc(c, chunk);
+ if (c == '\n')
+ state = 0;
+ break;
+ case 32:
+ Putc(c, chunk);
+ if (c == '*')
+ state = 33;
+ break;
+ case 33:
+ Putc(c, chunk);
+ if (c == '/')
+ state = 1;
+ else if (c != '*')
+ state = 32;
+ break;
+
+ case 40: /* Start of a C preprocessor directive */
+ if (c == '\n') {
+ Putc('\n', chunk);
+ state = 0;
+ } else if (isspace(c)) {
+ state = 40;
+ } else {
+ /* Got the start of a preprocessor directive */
+ Ungetc(c, s);
+ Clear(id);
+ copy_location(s, id);
+ state = 41;
+ }
+ break;
+
+ case 41: /* Build up the name of the preprocessor directive */
+ if ((isspace(c) || (!isidchar(c)))) {
+ Clear(value);
+ Clear(comment);
+ if (c == '\n') {
+ Ungetc(c, s);
+ state = 50;
+ } else {
+ state = 42;
+ if (!isspace(c)) {
+ Ungetc(c, s);
+ }
+ }
+
+ copy_location(s, value);
+ break;
+ }
+ Putc(c, id);
+ break;
+
+ case 42: /* Strip any leading space after the preprocessor directive (before preprocessor value) */
+ if (isspace(c)) {
+ if (c == '\n') {
+ Ungetc(c, s);
+ state = 50;
+ }
+ break;
+ }
+ state = 43;
+ /* FALL THRU */
+
+ case 43:
+ /* Get preprocessor value */
+ if (c == '\n') {
+ Ungetc(c, s);
+ state = 50;
+ } else if (c == '/') {
+ state = 45;
+ } else if (c == '\"') {
+ Putc(c, value);
+ skip_tochar(s, '\"', value);
+ } else if (c == '\'') {
+ Putc(c, value);
+ skip_tochar(s, '\'', value);
+ } else {
+ Putc(c, value);
+ if (c == '\\')
+ state = 44;
+ }
+ break;
+
+ case 44:
+ if (c == '\n') {
+ Putc(c, value);
+ cpp_lines++;
+ } else {
+ Ungetc(c, s);
+ }
+ state = 43;
+ break;
+
+ /* States 45-48 are used to remove, but retain comments from macro values. The comments
+ will be placed in the output in an alternative form */
+
+ case 45:
+ if (c == '/')
+ state = 46;
+ else if (c == '*')
+ state = 47;
+ else if (c == '\n') {
+ Putc('/', value);
+ Ungetc(c, s);
+ state = 50;
+ } else {
+ Putc('/', value);
+ Putc(c, value);
+ state = 43;
+ }
+ break;
+ case 46: /* in C++ comment */
+ if (c == '\n') {
+ Ungetc(c, s);
+ state = 50;
+ } else
+ Putc(c, comment);
+ break;
+ case 47: /* in C comment */
+ if (c == '*')
+ state = 48;
+ else
+ Putc(c, comment);
+ break;
+ case 48:
+ if (c == '/')
+ state = 43;
+ else if (c == '*')
+ Putc(c, comment);
+ else {
+ Putc('*', comment);
+ Putc(c, comment);
+ state = 47;
+ }
+ break;
+ case 50:
+ /* Check for various preprocessor directives */
+ Chop(value);
+ if (Equal(id, kpp_define)) {
+ if (allow) {
+ DOH *m, *v, *v1;
+ Seek(value, 0, SEEK_SET);
+ m = Preprocessor_define(value, 0);
+ if ((m) && !(Getattr(m, kpp_args))) {
+ v = Copy(Getattr(m, kpp_value));
+ copy_location(m, v);
+ if (Len(v)) {
+ Swig_error_silent(1);
+ v1 = Preprocessor_replace(v);
+ Swig_error_silent(0);
+ /* Printf(stdout,"checking '%s'\n", v1); */
+ if (!checkpp_id(v1)) {
+ if (Len(comment) == 0)
+ Printf(ns, "%%constant %s = %s;\n", Getattr(m, kpp_name), v1);
+ else
+ Printf(ns, "%%constant %s = %s; /*%s*/\n", Getattr(m, kpp_name), v1, comment);
+ cpp_lines--;
+ }
+ Delete(v1);
+ }
+ Delete(v);
+ }
+ }
+ } else if (Equal(id, kpp_undef)) {
+ if (allow)
+ Preprocessor_undef(value);
+ } else if (Equal(id, kpp_ifdef)) {
+ cond_lines[level] = Getline(id);
+ level++;
+ if (allow) {
+ start_level = level;
+ if (Len(value) > 0) {
+ /* See if the identifier is in the hash table */
+ if (!Getattr(symbols, value))
+ allow = 0;
+ } else {
+ Swig_error(Getfile(s), Getline(id), "Missing identifier for #ifdef.\n");
+ allow = 0;
+ }
+ mask = 1;
+ }
+ } else if (Equal(id, kpp_ifndef)) {
+ cond_lines[level] = Getline(id);
+ level++;
+ if (allow) {
+ start_level = level;
+ if (Len(value) > 0) {
+ /* See if the identifier is in the hash table */
+ if (Getattr(symbols, value))
+ allow = 0;
+ } else {
+ Swig_error(Getfile(s), Getline(id), "Missing identifier for #ifndef.\n");
+ allow = 0;
+ }
+ mask = 1;
+ }
+ } else if (Equal(id, kpp_else)) {
+ if (level <= 0) {
+ Swig_error(Getfile(s), Getline(id), "Misplaced #else.\n");
+ } else {
+ cond_lines[level - 1] = Getline(id);
+ if (Len(value) != 0)
+ Swig_warning(WARN_PP_UNEXPECTED_TOKENS, Getfile(s), Getline(id), "Unexpected tokens after #else directive.\n");
+ if (allow) {
+ allow = 0;
+ mask = 0;
+ } else if (level == start_level) {
+ allow = 1 * mask;
+ }
+ }
+ } else if (Equal(id, kpp_endif)) {
+ level--;
+ if (level < 0) {
+ Swig_error(Getfile(id), Getline(id), "Extraneous #endif.\n");
+ level = 0;
+ } else {
+ if (level < start_level) {
+ if (Len(value) != 0)
+ Swig_warning(WARN_PP_UNEXPECTED_TOKENS, Getfile(s), Getline(id), "Unexpected tokens after #endif directive.\n");
+ allow = 1;
+ start_level--;
+ }
+ }
+ } else if (Equal(id, kpp_if)) {
+ cond_lines[level] = Getline(id);
+ level++;
+ if (allow) {
+ int val;
+ String *sval;
+ expand_defined_operator = 1;
+ sval = Preprocessor_replace(value);
+ start_level = level;
+ Seek(sval, 0, SEEK_SET);
+ /* Printf(stdout,"Evaluating '%s'\n", sval); */
+ if (Len(sval) > 0) {
+ val = Preprocessor_expr(sval, &e);
+ if (e) {
+ const char *msg = Preprocessor_expr_error();
+ Seek(value, 0, SEEK_SET);
+ Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "Could not evaluate expression '%s'\n", value);
+ if (msg)
+ Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "%s\n", msg);
+ allow = 0;
+ } else {
+ if (val == 0)
+ allow = 0;
+ }
+ } else {
+ Swig_error(Getfile(s), Getline(id), "Missing expression for #if.\n");
+ allow = 0;
+ }
+ expand_defined_operator = 0;
+ mask = 1;
+ }
+ } else if (Equal(id, kpp_elif)) {
+ if (level == 0) {
+ Swig_error(Getfile(s), Getline(id), "Misplaced #elif.\n");
+ } else {
+ cond_lines[level - 1] = Getline(id);
+ if (allow) {
+ allow = 0;
+ mask = 0;
+ } else if (level == start_level) {
+ int val;
+ String *sval;
+ expand_defined_operator = 1;
+ sval = Preprocessor_replace(value);
+ Seek(sval, 0, SEEK_SET);
+ if (Len(sval) > 0) {
+ val = Preprocessor_expr(sval, &e);
+ if (e) {
+ const char *msg = Preprocessor_expr_error();
+ Seek(value, 0, SEEK_SET);
+ Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "Could not evaluate expression '%s'\n", value);
+ if (msg)
+ Swig_warning(WARN_PP_EVALUATION, Getfile(value), Getline(value), "%s\n", msg);
+ allow = 0;
+ } else {
+ if (val)
+ allow = 1 * mask;
+ else
+ allow = 0;
+ }
+ } else {
+ Swig_error(Getfile(s), Getline(id), "Missing expression for #elif.\n");
+ allow = 0;
+ }
+ expand_defined_operator = 0;
+ }
+ }
+ } else if (Equal(id, kpp_warning)) {
+ if (allow) {
+ Swig_warning(WARN_PP_CPP_WARNING, Getfile(s), Getline(id), "CPP #warning, \"%s\".\n", value);
+ }
+ } else if (Equal(id, kpp_error)) {
+ if (allow) {
+ if (error_as_warning) {
+ Swig_warning(WARN_PP_CPP_ERROR, Getfile(s), Getline(id), "CPP #error \"%s\".\n", value);
+ } else {
+ Swig_error(Getfile(s), Getline(id), "CPP #error \"%s\". Use the -cpperraswarn option to continue swig processing.\n", value);
+ }
+ }
+ } else if (Equal(id, kpp_line)) {
+ } else if (Equal(id, kpp_include)) {
+ if (((include_all) || (import_all)) && (allow)) {
+ String *s1, *s2, *fn;
+ String *dirname;
+ int sysfile = 0;
+ if (include_all && import_all) {
+ Swig_warning(WARN_PP_INCLUDEALL_IMPORTALL, Getfile(s), Getline(id), "Both includeall and importall are defined: using includeall.\n");
+ import_all = 0;
+ }
+ Seek(value, 0, SEEK_SET);
+ fn = get_filename(value, &sysfile);
+ s1 = cpp_include(fn, sysfile);
+ if (s1) {
+ if (include_all)
+ Printf(ns, "%%includefile \"%s\" %%beginfile\n", Swig_filename_escape(Swig_last_file()));
+ else if (import_all) {
+ Printf(ns, "%%importfile \"%s\" %%beginfile\n", Swig_filename_escape(Swig_last_file()));
+ push_imported();
+ }
+
+ /* See if the filename has a directory component */
+ dirname = Swig_file_dirname(Swig_last_file());
+ if (sysfile || !Len(dirname)) {
+ Delete(dirname);
+ dirname = 0;
+ }
+ if (dirname) {
+ int len = Len(dirname);
+ Delslice(dirname, len - 1, len); /* Kill trailing directory delimiter */
+ Swig_push_directory(dirname);
+ }
+ s2 = Preprocessor_parse(s1);
+ addline(ns, s2, allow);
+ Append(ns, "%endoffile");
+ if (dirname) {
+ Swig_pop_directory();
+ }
+ if (import_all) {
+ pop_imported();
+ }
+ Delete(s2);
+ Delete(dirname);
+ Delete(s1);
+ }
+ Delete(fn);
+ }
+ } else if (Equal(id, kpp_pragma)) {
+ if (Strncmp(value, "SWIG ", 5) == 0) {
+ char *c = Char(value) + 5;
+ while (*c && (isspace((int) *c)))
+ c++;
+ if (*c) {
+ if (strncmp(c, "nowarn=", 7) == 0) {
+ String *val = NewString(c + 7);
+ String *nowarn = Preprocessor_replace(val);
+ Swig_warnfilter(nowarn, 1);
+ Delete(nowarn);
+ Delete(val);
+ } else if (strncmp(c, "cpperraswarn=", 13) == 0) {
+ error_as_warning = atoi(c + 13);
+ } else {
+ Swig_error(Getfile(s), Getline(id), "Unknown SWIG pragma: %s\n", c);
+ }
+ }
+ }
+ } else if (Equal(id, kpp_level)) {
+ Swig_error(Getfile(s), Getline(id), "cpp debug: level = %d, startlevel = %d\n", level, start_level);
+ } else if (Equal(id, "")) {
+ /* Null directive */
+ } else if (is_digits(id)) {
+ /* A gcc linemarker of the form '# linenum filename flags' (resulting from running gcc -E) */
+ } else {
+ /* Ignore unknown preprocessor directives which are inside an inactive
+ * conditional (github issue #394). */
+ if (allow)
+ Swig_error(Getfile(s), Getline(id), "Unknown SWIG preprocessor directive: %s (if this is a block of target language code, delimit it with %%{ and %%})\n", id);
+ }
+ for (i = 0; i < cpp_lines; i++)
+ Putc('\n', ns);
+ state = 0;
+ break;
+
+ /* SWIG directives */
+ case 100:
+ /* %{,%} block */
+ if (c == '{') {
+ start_line = Getline(s);
+ copy_location(s, chunk);
+ add_chunk(ns, chunk, allow);
+ Putc('%', chunk);
+ Putc(c, chunk);
+ state = 105;
+ }
+ /* %#cpp - an embedded C preprocessor directive (we strip off the %) */
+ else if (c == '#') {
+ add_chunk(ns, chunk, allow);
+ Putc(c, chunk);
+ state = 107;
+ } else if (isidentifier(c)) {
+ Clear(decl);
+ Putc('%', decl);
+ Putc(c, decl);
+ state = 110;
+ } else {
+ Putc('%', chunk);
+ Putc(c, chunk);
+ state = 1;
+ }
+ break;
+
+ case 105:
+ Putc(c, chunk);
+ if (c == '%')
+ state = 106;
+ break;
+
+ case 106:
+ Putc(c, chunk);
+ if (c == '}') {
+ state = 1;
+ addline(ns, chunk, allow);
+ Clear(chunk);
+ copy_location(s, chunk);
+ } else {
+ state = 105;
+ }
+ break;
+
+ case 107:
+ Putc(c, chunk);
+ if (c == '\n') {
+ addline(ns, chunk, allow);
+ Clear(chunk);
+ state = 0;
+ } else if (c == '\\') {
+ state = 108;
+ }
+ break;
+
+ case 108:
+ Putc(c, chunk);
+ state = 107;
+ break;
+
+ case 110:
+ if (!isidchar(c)) {
+ Ungetc(c, s);
+ /* Look for common SWIG directives */
+ if (Equal(decl, kpp_dinclude) || Equal(decl, kpp_dimport) || Equal(decl, kpp_dextern)) {
+ /* Got some kind of file inclusion directive, eg: %import(option1="value1") "filename" */
+ if (allow) {
+ DOH *s1, *s2, *fn, *opt;
+ String *options_whitespace = NewStringEmpty();
+ String *filename_whitespace = NewStringEmpty();
+ int sysfile = 0;
+
+ if (Equal(decl, kpp_dextern)) {
+ Swig_warning(WARN_DEPRECATED_EXTERN, Getfile(s), Getline(s), "%%extern is deprecated. Use %%import instead.\n");
+ Clear(decl);
+ Append(decl, "%%import");
+ }
+ skip_whitespace(s, options_whitespace);
+ opt = get_options(s);
+
+ skip_whitespace(s, filename_whitespace);
+ fn = get_filename(s, &sysfile);
+ s1 = cpp_include(fn, sysfile);
+ if (s1) {
+ String *dirname;
+ copy_location(s, chunk);
+ add_chunk(ns, chunk, allow);
+ Printf(ns, "%sfile%s%s%s\"%s\" %%beginfile\n", decl, options_whitespace, opt, filename_whitespace, Swig_filename_escape(Swig_last_file()));
+ if (Equal(decl, kpp_dimport)) {
+ push_imported();
+ }
+ dirname = Swig_file_dirname(Swig_last_file());
+ if (sysfile || !Len(dirname)) {
+ Delete(dirname);
+ dirname = 0;
+ }
+ if (dirname) {
+ int len = Len(dirname);
+ Delslice(dirname, len - 1, len); /* Kill trailing directory delimiter */
+ Swig_push_directory(dirname);
+ }
+ s2 = Preprocessor_parse(s1);
+ if (dirname) {
+ Swig_pop_directory();
+ }
+ if (Equal(decl, kpp_dimport)) {
+ pop_imported();
+ }
+ addline(ns, s2, allow);
+ Append(ns, "%endoffile");
+ Delete(s2);
+ Delete(dirname);
+ Delete(s1);
+ }
+ Delete(fn);
+ Delete(filename_whitespace);
+ Delete(options_whitespace);
+ }
+ state = 1;
+ } else if (Equal(decl, kpp_dbeginfile)) {
+ /* Got an internal directive marking the beginning of an included file: %beginfile ... %endoffile */
+ filelevel++;
+ start_line = Getline(s);
+ copy_location(s, chunk);
+ add_chunk(ns, chunk, allow);
+ Append(chunk, decl);
+ state = 120;
+ } else if (Equal(decl, kpp_dline)) {
+ /* Got a line directive */
+ state = 1;
+ } else if (Equal(decl, kpp_ddefine)) {
+ /* Got a define directive */
+ dlevel++;
+ copy_location(s, chunk);
+ add_chunk(ns, chunk, allow);
+ Clear(value);
+ copy_location(s, value);
+ state = 150;
+ } else {
+ Append(chunk, decl);
+ state = 1;
+ }
+ } else {
+ Putc(c, decl);
+ }
+ break;
+
+ /* Searching for the end of a %beginfile block */
+ case 120:
+ Putc(c, chunk);
+ if (c == '%') {
+ const char *bf = "beginfile";
+ const char *ef = "endoffile";
+ char statement[10];
+ int i = 0;
+ for (i = 0; i < 9;) {
+ c = Getc(s);
+ Putc(c, chunk);
+ statement[i++] = (char)c;
+ if (strncmp(statement, bf, i) && strncmp(statement, ef, i))
+ break;
+ }
+ c = Getc(s);
+ Ungetc(c, s);
+ if ((i == 9) && (isspace(c))) {
+ if (strncmp(statement, bf, i) == 0) {
+ ++filelevel;
+ } else if (strncmp(statement, ef, i) == 0) {
+ --filelevel;
+ if (!filelevel) {
+ /* Reached end of included file */
+ addline(ns, chunk, allow);
+ Clear(chunk);
+ copy_location(s, chunk);
+ state = 1;
+ }
+ }
+ }
+ }
+ break;
+
+ /* Searching for the end of a %define statement */
+ case 150:
+ Putc(c, value);
+ if (c == '%') {
+ const char *ed = "enddef";
+ const char *df = "define";
+ char statement[7];
+ int i = 0;
+ for (i = 0; i < 6;) {
+ c = Getc(s);
+ Putc(c, value);
+ statement[i++] = (char)c;
+ if (strncmp(statement, ed, i) && strncmp(statement, df, i))
+ break;
+ }
+ c = Getc(s);
+ Ungetc(c, s);
+ if ((i == 6) && (isspace(c))) {
+ if (strncmp(statement, df, i) == 0) {
+ ++dlevel;
+ } else {
+ if (strncmp(statement, ed, i) == 0) {
+ --dlevel;
+ if (!dlevel) {
+ /* Got the macro */
+ for (i = 0; i < 7; i++) {
+ Delitem(value, DOH_END);
+ }
+ if (allow) {
+ Seek(value, 0, SEEK_SET);
+ Preprocessor_define(value, 1);
+ }
+ addline(ns, value, 0);
+ state = 0;
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ Printf(stderr, "cpp: Invalid parser state %d\n", state);
+ Exit(EXIT_FAILURE);
+ }
+ }
+ while (level > 0) {
+ Swig_error(Getfile(s), cond_lines[level - 1], "Missing #endif for conditional starting here\n");
+ level--;
+ }
+ if (state == 120) {
+ Swig_error(Getfile(s), start_line, "Missing %%endoffile for file inclusion block starting here\n");
+ }
+ if (state == 150) {
+ Seek(value, 0, SEEK_SET);
+ Swig_error(Getfile(s), Getline(value), "Missing %%enddef for macro starting here\n", Getline(value));
+ }
+ if ((state >= 105) && (state < 107)) {
+ Swig_error(Getfile(s), start_line, "Unterminated %%{ ... %%} block\n");
+ }
+ if ((state >= 30) && (state < 40)) {
+ Swig_error(Getfile(s), start_line, "Unterminated comment\n");
+ }
+
+ copy_location(s, chunk);
+ add_chunk(ns, chunk, allow);
+
+ /* DelScope(scp); */
+ Delete(decl);
+ Delete(id);
+ Delete(value);
+ Delete(comment);
+ Delete(chunk);
+
+ return ns;
+}
diff --git a/contrib/tools/swig/Source/Preprocessor/expr.c b/contrib/tools/swig/Source/Preprocessor/expr.c
new file mode 100644
index 0000000000..95e05cf562
--- /dev/null
+++ b/contrib/tools/swig/Source/Preprocessor/expr.c
@@ -0,0 +1,496 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * expr.c
+ *
+ * Integer arithmetic expression evaluator used to handle expressions
+ * encountered during preprocessing.
+ *
+ * Note that this is used for expressions in `#if` and the like, but not
+ * for expressions in `#define` which SWIG wraps as constants - for those
+ * we inject a `%constant` directive which is handled by the parser in
+ * `Source/CParse/parser.y`.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "preprocessor.h"
+
+static Scanner *scan = 0;
+
+typedef struct {
+ /* One of the EXPR_xxx values defined below. */
+ int op;
+ /* op == EXPR_OP: value is the token specifying which operator.
+ *
+ * op == EXPR_VALUE && svalue == NULL: Numeric expression value.
+ *
+ * Otherwise unused.
+ */
+ long value;
+ /* op == EXPR_VALUE: If non-NULL, string expression value; if NULL see value.
+ *
+ * Otherwise unused.
+ */
+ String *svalue;
+} exprval;
+
+#define EXPR_TOP 1
+#define EXPR_VALUE 2
+#define EXPR_OP 3
+#define EXPR_GROUP 4
+
+/* Special token values used here to distinguish from SWIG_TOKEN_MINUS
+ * and SWIG_TOKEN_PLUS (which we use here for a two argument versions).
+ */
+#define OP_UMINUS 100
+#define OP_UPLUS 101
+
+static exprval stack[256]; /* Parsing stack */
+static int sp = 0; /* Stack pointer */
+static int prec[256]; /* Precedence rules */
+static int expr_init = 0; /* Initialization flag */
+static const char *errmsg = 0; /* Parsing error */
+
+/* Initialize the precedence table for various operators. Low values have higher precedence */
+static void init_precedence(void) {
+ prec[SWIG_TOKEN_NOT] = 10;
+ prec[SWIG_TOKEN_LNOT] = 10;
+ prec[OP_UMINUS] = 10;
+ prec[OP_UPLUS] = 10;
+ prec[SWIG_TOKEN_STAR] = 20;
+ prec[SWIG_TOKEN_SLASH] = 20;
+ prec[SWIG_TOKEN_PERCENT] = 20;
+ prec[SWIG_TOKEN_PLUS] = 30;
+ prec[SWIG_TOKEN_MINUS] = 30;
+ prec[SWIG_TOKEN_LSHIFT] = 40;
+ prec[SWIG_TOKEN_RSHIFT] = 40;
+ prec[SWIG_TOKEN_LESSTHAN] = 50;
+ prec[SWIG_TOKEN_GREATERTHAN] = 50;
+ prec[SWIG_TOKEN_LTEQUAL] = 50;
+ prec[SWIG_TOKEN_GTEQUAL] = 50;
+ prec[SWIG_TOKEN_EQUALTO] = 60;
+ prec[SWIG_TOKEN_NOTEQUAL] = 60;
+ prec[SWIG_TOKEN_AND] = 70;
+ prec[SWIG_TOKEN_XOR] = 80;
+ prec[SWIG_TOKEN_OR] = 90;
+ prec[SWIG_TOKEN_LAND] = 100;
+ prec[SWIG_TOKEN_LOR] = 110;
+ expr_init = 1;
+}
+
+#define UNARY_OP(token) (((token) == SWIG_TOKEN_NOT) || \
+ ((token) == SWIG_TOKEN_LNOT) || \
+ ((token) == OP_UMINUS) || \
+ ((token) == OP_UPLUS))
+
+/* Reduce a single operator on the stack */
+/* return 0 on failure, 1 on success */
+static int reduce_op(void) {
+ long op_token = stack[sp - 1].value;
+ assert(sp > 0);
+ assert(stack[sp - 1].op == EXPR_OP);
+ /* do some basic checking first: */
+ if (stack[sp].op != EXPR_VALUE) {
+ errmsg = "Right-hand side is not value";
+ return 0;
+ }
+ if (UNARY_OP(op_token)) {
+ if (stack[sp].svalue) {
+ errmsg = "Syntax error: attempt to apply unary operator to string";
+ return 0;
+ }
+ } else {
+ /* binary operator: */
+ if (sp == 1) {
+ /* top of stack: don't attempt to use sp-2! */
+ errmsg = "Missing left-hand side for binary operator";
+ return 0;
+ }
+ if (stack[sp].op != EXPR_VALUE) {
+ errmsg = "Left-hand side of binary operator is not a value";
+ return 0;
+ }
+ if ((!stack[sp - 2].svalue) != (!stack[sp].svalue)) {
+ errmsg = "Can't mix strings and integers in expression";
+ return 0;
+ }
+ }
+ if (stack[sp].svalue) {
+ /* A binary string expression */
+ switch (stack[sp - 1].value) {
+ case SWIG_TOKEN_EQUALTO:
+ stack[sp - 2].value = (Strcmp(stack[sp - 2].svalue, stack[sp].svalue) == 0);
+ Delete(stack[sp - 2].svalue);
+ Delete(stack[sp].svalue);
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_NOTEQUAL:
+ stack[sp - 2].value = (Strcmp(stack[sp - 2].svalue, stack[sp].svalue) != 0);
+ Delete(stack[sp - 2].svalue);
+ Delete(stack[sp].svalue);
+ sp -= 2;
+ break;
+ default:
+ errmsg = "Syntax error: bad binary operator for strings";
+ return 0;
+ break;
+ }
+ } else {
+ switch (op_token) {
+ case SWIG_TOKEN_STAR:
+ stack[sp - 2].value = stack[sp - 2].value * stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_EQUALTO:
+ stack[sp - 2].value = stack[sp - 2].value == stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_NOTEQUAL:
+ stack[sp - 2].value = stack[sp - 2].value != stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_PLUS:
+ stack[sp - 2].value = stack[sp - 2].value + stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_MINUS:
+ stack[sp - 2].value = stack[sp - 2].value - stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_AND:
+ stack[sp - 2].value = stack[sp - 2].value & stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_LAND:
+ stack[sp - 2].value = stack[sp - 2].value && stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_OR:
+ stack[sp - 2].value = stack[sp - 2].value | stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_LOR:
+ stack[sp - 2].value = stack[sp - 2].value || stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_XOR:
+ stack[sp - 2].value = stack[sp - 2].value ^ stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_LESSTHAN:
+ stack[sp - 2].value = stack[sp - 2].value < stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_GREATERTHAN:
+ stack[sp - 2].value = stack[sp - 2].value > stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_LTEQUAL:
+ stack[sp - 2].value = stack[sp - 2].value <= stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_GTEQUAL:
+ stack[sp - 2].value = stack[sp - 2].value >= stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_NOT:
+ stack[sp - 1].value = ~stack[sp].value;
+ sp--;
+ break;
+ case SWIG_TOKEN_LNOT:
+ stack[sp - 1].value = !stack[sp].value;
+ sp--;
+ break;
+ case OP_UMINUS:
+ stack[sp - 1].value = -stack[sp].value;
+ sp--;
+ break;
+ case OP_UPLUS:
+ stack[sp - 1].value = stack[sp].value;
+ sp--;
+ break;
+ case SWIG_TOKEN_SLASH:
+ if (stack[sp].value != 0) {
+ stack[sp - 2].value = stack[sp - 2].value / stack[sp].value;
+ sp -= 2;
+ } else {
+ errmsg = "Division by zero in expression";
+ return 0;
+ }
+ break;
+ case SWIG_TOKEN_PERCENT:
+ if (stack[sp].value != 0) {
+ stack[sp - 2].value = stack[sp - 2].value % stack[sp].value;
+ sp -= 2;
+ } else {
+ errmsg = "Modulo by zero in expression";
+ return 0;
+ }
+ break;
+ case SWIG_TOKEN_LSHIFT:
+ stack[sp - 2].value = stack[sp - 2].value << stack[sp].value;
+ sp -= 2;
+ break;
+ case SWIG_TOKEN_RSHIFT:
+ stack[sp - 2].value = stack[sp - 2].value >> stack[sp].value;
+ sp -= 2;
+ break;
+ default:
+ errmsg = "Syntax error: bad operator";
+ return 0;
+ break;
+ }
+ }
+ stack[sp].op = EXPR_VALUE;
+ stack[sp].svalue = 0; /* ensure it's not a string! */
+ return 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Preprocessor_expr_init()
+ *
+ * Initialize the expression evaluator
+ * ----------------------------------------------------------------------------- */
+
+void Preprocessor_expr_init(void) {
+ if (!expr_init)
+ init_precedence();
+ if (!scan)
+ scan = NewScanner();
+}
+
+void Preprocessor_expr_delete(void) {
+ DelScanner(scan);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Tokenizer
+ * ----------------------------------------------------------------------------- */
+
+static int expr_token(Scanner * s) {
+ int t;
+ while (1) {
+ t = Scanner_token(s);
+ if (!((t == SWIG_TOKEN_BACKSLASH) || (t == SWIG_TOKEN_ENDLINE) || (t == SWIG_TOKEN_COMMENT)))
+ break;
+ }
+ return t;
+}
+
+/* -----------------------------------------------------------------------------
+ * Preprocessor_expr()
+ *
+ * Evaluates an arithmetic expression. Returns the result and sets an error code.
+ * ----------------------------------------------------------------------------- */
+
+int Preprocessor_expr(DOH *s, int *error) {
+ int token = 0;
+ int op = 0;
+
+ sp = 0;
+ assert(s);
+ assert(scan);
+
+ Seek(s, 0, SEEK_SET);
+ /* Printf(stdout,"evaluating : '%s'\n", s); */
+ *error = 0;
+ Scanner_clear(scan);
+ Scanner_push(scan, s);
+
+ /* Put initial state onto the stack */
+ stack[sp].op = EXPR_TOP;
+
+ while (1) {
+ /* Look at the top of the stack */
+ switch (stack[sp].op) {
+ case EXPR_TOP:
+ /* EXPR_TOP is a place-holder which can only appear on the top of the
+ * stack. We can reduce it to any expression - a number, a string, an
+ * unary operator, or another expression enclosed in parentheses.
+ */
+ token = expr_token(scan);
+ if (!token) {
+ errmsg = "Expected an expression";
+ *error = 1;
+ return 0;
+ }
+ if ((token == SWIG_TOKEN_INT) || (token == SWIG_TOKEN_UINT) || (token == SWIG_TOKEN_LONG) || (token == SWIG_TOKEN_ULONG)) {
+ /* A number. Reduce EXPR_TOP to an EXPR_VALUE */
+ char *c = Char(Scanner_text(scan));
+ if (c[0] == '0' && (c[1] == 'b' || c[1] == 'B')) {
+ /* strtol() doesn't handle binary constants */
+ stack[sp].value = (long) strtol(c + 2, 0, 2);
+ } else {
+ stack[sp].value = (long) strtol(c, 0, 0);
+ }
+ stack[sp].svalue = 0;
+ stack[sp].op = EXPR_VALUE;
+ } else if ((token == SWIG_TOKEN_MINUS) || (token == SWIG_TOKEN_PLUS) || (token == SWIG_TOKEN_LNOT) || (token == SWIG_TOKEN_NOT)) {
+ if (token == SWIG_TOKEN_MINUS)
+ token = OP_UMINUS;
+ else if (token == SWIG_TOKEN_PLUS)
+ token = OP_UPLUS;
+ stack[sp].value = token;
+ stack[sp].op = EXPR_OP;
+ sp++;
+ stack[sp].op = EXPR_TOP;
+ } else if (token == SWIG_TOKEN_LPAREN) {
+ stack[sp].op = EXPR_GROUP;
+ sp++;
+ stack[sp].op = EXPR_TOP;
+ } else if (token == SWIG_TOKEN_ENDLINE) {
+ } else if (token == SWIG_TOKEN_STRING) {
+ stack[sp].svalue = NewString(Scanner_text(scan));
+ stack[sp].op = EXPR_VALUE;
+ } else if (token == SWIG_TOKEN_ID) {
+ /* Defined macros have been expanded already so this is an unknown
+ * macro, which gets treated as zero.
+ */
+ stack[sp].value = 0;
+ stack[sp].svalue = 0;
+ stack[sp].op = EXPR_VALUE;
+ } else if ((token == SWIG_TOKEN_FLOAT) || (token == SWIG_TOKEN_DOUBLE)) {
+ errmsg = "Floating point constant in preprocessor expression";
+ *error = 1;
+ return 0;
+ } else
+ goto syntax_error;
+ break;
+ case EXPR_VALUE:
+ /* A value is on top of the stack. We may reduce or evaluate depending
+ * on what the next token is.
+ */
+ token = expr_token(scan);
+ if (!token) {
+ /* End of input. Might have to reduce if an operator is on stack */
+ while (sp > 0) {
+ if (stack[sp - 1].op == EXPR_OP) {
+ if (!reduce_op())
+ goto reduce_error;
+ } else if (stack[sp - 1].op == EXPR_GROUP) {
+ errmsg = "Missing \')\'";
+ *error = 1;
+ return 0;
+ } else
+ goto syntax_error;
+ }
+ return stack[sp].value;
+ }
+ /* Token must be an operator */
+ switch (token) {
+ case SWIG_TOKEN_STAR:
+ case SWIG_TOKEN_EQUALTO:
+ case SWIG_TOKEN_NOTEQUAL:
+ case SWIG_TOKEN_PLUS:
+ case SWIG_TOKEN_MINUS:
+ case SWIG_TOKEN_AND:
+ case SWIG_TOKEN_LAND:
+ case SWIG_TOKEN_OR:
+ case SWIG_TOKEN_LOR:
+ case SWIG_TOKEN_XOR:
+ case SWIG_TOKEN_LESSTHAN:
+ case SWIG_TOKEN_GREATERTHAN:
+ case SWIG_TOKEN_LTEQUAL:
+ case SWIG_TOKEN_GTEQUAL:
+ case SWIG_TOKEN_SLASH:
+ case SWIG_TOKEN_PERCENT:
+ case SWIG_TOKEN_LSHIFT:
+ case SWIG_TOKEN_RSHIFT:
+ if ((sp == 0) || (stack[sp - 1].op == EXPR_GROUP)) {
+ /* No possibility of reduce. Push operator and expression */
+ sp++;
+ stack[sp].op = EXPR_OP;
+ stack[sp].value = token;
+ sp++;
+ stack[sp].op = EXPR_TOP;
+ } else {
+ if (stack[sp - 1].op != EXPR_OP)
+ goto syntax_error_expected_operator;
+ op = stack[sp - 1].value; /* Previous operator */
+
+ /* Now, depending on the precedence relationship between the last operator and the current
+ we will reduce or push */
+
+ if (prec[op] <= prec[token]) {
+ /* Reduce the previous operator */
+ if (!reduce_op())
+ goto reduce_error;
+ }
+ sp++;
+ stack[sp].op = EXPR_OP;
+ stack[sp].value = token;
+ sp++;
+ stack[sp].op = EXPR_TOP;
+ }
+ break;
+ case SWIG_TOKEN_RPAREN:
+ if (sp == 0)
+ goto extra_rparen;
+
+ /* Might have to reduce operators first */
+ while ((sp > 0) && (stack[sp - 1].op == EXPR_OP)) {
+ if (!reduce_op())
+ goto reduce_error;
+ }
+ if ((sp == 0) || (stack[sp - 1].op != EXPR_GROUP))
+ goto extra_rparen;
+ stack[sp - 1].op = EXPR_VALUE;
+ stack[sp - 1].value = stack[sp].value;
+ stack[sp - 1].svalue = stack[sp].svalue;
+ sp--;
+ break;
+ case SWIG_TOKEN_LTEQUALGT:
+ goto spaceship_not_allowed;
+ default:
+ goto syntax_error_expected_operator;
+ break;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "Internal error in expression evaluator.\n");
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+syntax_error:
+ errmsg = "Syntax error";
+ *error = 1;
+ return 0;
+
+syntax_error_expected_operator:
+ errmsg = "Syntax error: expected operator";
+ *error = 1;
+ return 0;
+
+reduce_error:
+ /* errmsg has been set by reduce_op */
+ *error = 1;
+ return 0;
+
+extra_rparen:
+ errmsg = "Extra \')\'";
+ *error = 1;
+ return 0;
+
+spaceship_not_allowed:
+ errmsg = "Spaceship operator (<=>) not allowed in preprocessor expression";
+ *error = 1;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Preprocessor_expr_error()
+ *
+ * Return error message set by the evaluator (if any)
+ * ----------------------------------------------------------------------------- */
+
+const char *Preprocessor_expr_error(void) {
+ return errmsg;
+}
diff --git a/contrib/tools/swig/Source/Preprocessor/preprocessor.h b/contrib/tools/swig/Source/Preprocessor/preprocessor.h
new file mode 100644
index 0000000000..c1a30da793
--- /dev/null
+++ b/contrib/tools/swig/Source/Preprocessor/preprocessor.h
@@ -0,0 +1,40 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * preprocessor.h
+ *
+ * SWIG preprocessor module.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_PREPROCESSOR_H
+#define SWIG_PREPROCESSOR_H
+
+#include "swigwarn.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern int Preprocessor_expr(String *s, int *error);
+ extern const char *Preprocessor_expr_error(void);
+ extern Hash *Preprocessor_define(const_String_or_char_ptr str, int swigmacro);
+ extern void Preprocessor_undef(const_String_or_char_ptr name);
+ extern void Preprocessor_init(void);
+ extern void Preprocessor_delete(void);
+ extern String *Preprocessor_parse(String *s);
+ extern void Preprocessor_include_all(int);
+ extern void Preprocessor_import_all(int);
+ extern void Preprocessor_ignore_missing(int);
+ extern void Preprocessor_error_as_warning(int);
+ extern List *Preprocessor_depend(void);
+ extern void Preprocessor_expr_init(void);
+ extern void Preprocessor_expr_delete(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/contrib/tools/swig/Source/README b/contrib/tools/swig/Source/README
new file mode 100644
index 0000000000..0889333088
--- /dev/null
+++ b/contrib/tools/swig/Source/README
@@ -0,0 +1,15 @@
+SWIG Source directory
+
+ Source/DOH - A core set of basic datatypes including
+ strings, lists, hashes, and files. Used
+ extensively by the rest of SWIG.
+
+ Source/Swig - Swig core. Type-system, utility functions.
+
+ Source/Preprocessor - SWIG C Preprocessor
+
+ Source/CParse - SWIG C Parser (still messy)
+
+ Source/Modules - Language modules.
+
+ Source/Include - Include files.
diff --git a/contrib/tools/swig/Source/Swig/cwrap.c b/contrib/tools/swig/Source/Swig/cwrap.c
new file mode 100644
index 0000000000..b7d01bc117
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/cwrap.c
@@ -0,0 +1,1661 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * cwrap.c
+ *
+ * This file defines a variety of wrapping rules for C/C++ handling including
+ * the naming of local variables, calling conventions, and so forth.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+
+extern int UseWrapperSuffix;
+
+static const char *cresult_variable_name = "result";
+
+static Parm *nonvoid_parms(Parm *p) {
+ if (p) {
+ SwigType *t = Getattr(p, "type");
+ if (SwigType_type(t) == T_VOID)
+ return 0;
+ }
+ return p;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cresult_name_set()
+ *
+ * Change the name of the variable used to hold the return value from C/C++ wrapper functions
+ * from the default "result".
+ * ----------------------------------------------------------------------------- */
+
+void Swig_cresult_name_set(const char *new_name) {
+ cresult_variable_name = new_name;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cresult_name()
+ *
+ * Get the name of the variable used to hold the return value from C/C++ wrapper functions
+ * ----------------------------------------------------------------------------- */
+
+const char *Swig_cresult_name(void) {
+ return cresult_variable_name;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cparm_name()
+ *
+ * Generates a name for the ith argument in an argument list
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cparm_name(Parm *p, int i) {
+ String *name = NewStringf("arg%d", i + 1);
+ if (p) {
+ Setattr(p, "lname", name);
+ }
+
+ return name;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_clocal()
+ *
+ * Creates a string that declares a C local variable type. Converts references
+ * and user defined types to pointers.
+ * ----------------------------------------------------------------------------- */
+
+static String *Swig_clocal(SwigType *t, const_String_or_char_ptr name, const_String_or_char_ptr value) {
+ String *decl;
+
+ decl = NewStringEmpty();
+
+ switch (SwigType_type(t)) {
+ case T_REFERENCE:
+ if (value) {
+ String *lstrname = SwigType_lstr(t, name);
+ String *lstr = SwigType_lstr(t, 0);
+ Printf(decl, "%s = (%s) &%s_defvalue", lstrname, lstr, name);
+ Delete(lstrname);
+ Delete(lstr);
+ } else {
+ String *lstrname = SwigType_lstr(t, name);
+ Printf(decl, "%s = 0", lstrname);
+ Delete(lstrname);
+ }
+ break;
+ case T_RVALUE_REFERENCE:
+ if (value) {
+ String *lstrname = SwigType_lstr(t, name);
+ String *lstr = SwigType_lstr(t, 0);
+ Printf(decl, "%s = (%s) &%s_defrvalue", lstrname, lstr, name);
+ Delete(lstrname);
+ Delete(lstr);
+ } else {
+ String *lstrname = SwigType_lstr(t, name);
+ Printf(decl, "%s = 0", lstrname);
+ Delete(lstrname);
+ }
+ break;
+ case T_VOID:
+ break;
+ case T_VARARGS:
+ Printf(decl, "void *%s = 0", name);
+ break;
+
+ default:
+ if (value) {
+ String *lcaststr = SwigType_lcaststr(t, value);
+ String *lstr = SwigType_lstr(t, 0);
+ String *lstrn = SwigType_lstr(t, name);
+ Printf(decl, "%s = (%s) %s", lstrn, lstr, lcaststr);
+ Delete(lcaststr);
+ Delete(lstr);
+ Delete(lstrn);
+ } else {
+ String *lstrname = SwigType_lstr(t, name);
+ Append(decl, lstrname);
+ Delete(lstrname);
+ }
+ }
+ return decl;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_wrapped_var_convert()
+ *
+ * Converts a member variable for use in the get and set wrapper methods.
+ * This function only converts user defined types to pointers.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_wrapped_var_type(SwigType *t, int varcref) {
+ SwigType *ty;
+
+ if (!Strstr(t, "enum $unnamed")) {
+ ty = Copy(t);
+ } else {
+ /* Change the type for unnamed enum instance variables */
+ ty = NewString("int");
+ }
+
+ if (SwigType_isclass(t)) {
+ if (varcref) {
+ if (cparse_cplusplus) {
+ if (!SwigType_isconst(ty))
+ SwigType_add_qualifier(ty, "const");
+ SwigType_add_reference(ty);
+ } else {
+ return Copy(ty);
+ }
+ } else {
+ SwigType_add_pointer(ty);
+ }
+ }
+ return ty;
+}
+
+String *Swig_wrapped_member_var_type(SwigType *t, int varcref) {
+ SwigType *ty;
+
+ if (!Strstr(t, "enum $unnamed")) {
+ ty = Copy(t);
+ } else {
+ /* Change the type for unnamed enum instance variables */
+ ty = NewString("int");
+ }
+ if (SwigType_isclass(t)) {
+ if (varcref) {
+ if (cparse_cplusplus) {
+ if (!SwigType_isconst(ty))
+ SwigType_add_qualifier(ty, "const");
+ SwigType_add_reference(ty);
+ } else {
+ return Copy(ty);
+ }
+ } else {
+ SwigType_add_pointer(ty);
+ }
+ }
+ return ty;
+}
+
+
+static String *Swig_wrapped_var_deref(SwigType *t, const_String_or_char_ptr name, int varcref) {
+ if (SwigType_isclass(t)) {
+ if (varcref) {
+ if (cparse_cplusplus) {
+ return NewStringf("*%s", name);
+ } else {
+ return NewStringf("%s", name);
+ }
+ } else {
+ return NewStringf("*%s", name);
+ }
+ } else {
+ return SwigType_rcaststr(t, name);
+ }
+}
+
+static String *Swig_wrapped_var_assign(SwigType *t, const_String_or_char_ptr name, int varcref) {
+ if (SwigType_isclass(t)) {
+ if (varcref) {
+ return NewStringf("%s", name);
+ } else {
+ return NewStringf("&%s", name);
+ }
+ } else {
+ return SwigType_lcaststr(t, name);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cargs()
+ *
+ * Emit all of the local variables for a list of parameters. Returns the
+ * number of parameters.
+ * Default values for the local variables are only emitted if the compact default
+ * argument behaviour is required.
+ * ----------------------------------------------------------------------------- */
+int Swig_cargs(Wrapper *w, ParmList *p) {
+ int i = 0;
+ int compactdefargs = ParmList_is_compactdefargs(p);
+
+ while (p != 0) {
+ String *lname = Swig_cparm_name(p, i);
+ SwigType *pt = Getattr(p, "type");
+ if ((SwigType_type(pt) != T_VOID)) {
+ String *local = 0;
+ String *type = Getattr(p, "type");
+ /* default values only emitted if in compact default args mode */
+ String *pvalue = (compactdefargs) ? Getattr(p, "value") : 0;
+
+ /* When using compactdefaultargs, the code generated initialises a variable via a constructor call that accepts the
+ * default value as a parameter. The default constructor is not called and therefore SwigValueWrapper is not needed. */
+ SwigType *altty = pvalue ? 0 : SwigType_alttype(type, 0);
+
+ int tycode = SwigType_type(type);
+ if (tycode == T_REFERENCE) {
+ if (pvalue) {
+ SwigType *tvalue;
+ String *defname, *defvalue, *rvalue, *qvalue;
+ rvalue = SwigType_typedef_resolve_all(pvalue);
+ qvalue = SwigType_typedef_qualified(rvalue);
+ defname = NewStringf("%s_defvalue", lname);
+ tvalue = Copy(type);
+ SwigType_del_reference(tvalue);
+ tycode = SwigType_type(tvalue);
+ if (tycode != T_USER) {
+ /* plain primitive type, we copy the def value */
+ String *lstr = SwigType_lstr(tvalue, defname);
+ defvalue = NewStringf("%s = %s", lstr, qvalue);
+ Delete(lstr);
+ } else {
+ /* user type, we copy the reference value */
+ String *str = SwigType_str(type, defname);
+ defvalue = NewStringf("%s = %s", str, qvalue);
+ Delete(str);
+ }
+ Wrapper_add_localv(w, defname, defvalue, NIL);
+ Delete(tvalue);
+ Delete(rvalue);
+ Delete(qvalue);
+ Delete(defname);
+ Delete(defvalue);
+ }
+ } else if (tycode == T_RVALUE_REFERENCE) {
+ if (pvalue) {
+ SwigType *tvalue;
+ String *defname, *defvalue, *rvalue, *qvalue;
+ rvalue = SwigType_typedef_resolve_all(pvalue);
+ qvalue = SwigType_typedef_qualified(rvalue);
+ defname = NewStringf("%s_defrvalue", lname);
+ tvalue = Copy(type);
+ SwigType_del_rvalue_reference(tvalue);
+ tycode = SwigType_type(tvalue);
+ if (tycode != T_USER) {
+ /* plain primitive type, we copy the def value */
+ String *lstr = SwigType_lstr(tvalue, defname);
+ defvalue = NewStringf("%s = %s", lstr, qvalue);
+ Delete(lstr);
+ } else {
+ /* user type, we copy the reference value */
+ String *str = SwigType_str(type, defname);
+ defvalue = NewStringf("%s = %s", str, qvalue);
+ Delete(str);
+ }
+ Wrapper_add_localv(w, defname, defvalue, NIL);
+ Delete(tvalue);
+ Delete(rvalue);
+ Delete(qvalue);
+ Delete(defname);
+ Delete(defvalue);
+ }
+ } else if (!pvalue && ((tycode == T_POINTER) || (tycode == T_STRING) || (tycode == T_WSTRING))) {
+ pvalue = (String *) "0";
+ }
+ if (!altty) {
+ local = Swig_clocal(pt, lname, pvalue);
+ } else {
+ local = Swig_clocal(altty, lname, pvalue);
+ Delete(altty);
+ }
+ Wrapper_add_localv(w, lname, local, NIL);
+ Delete(local);
+ i++;
+ }
+ Delete(lname);
+ p = nextSibling(p);
+ }
+ return (i);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cresult()
+ *
+ * This function generates the C code needed to set the result of a C
+ * function call.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cresult(SwigType *t, const_String_or_char_ptr name, const_String_or_char_ptr decl) {
+ String *fcall;
+
+ fcall = NewStringEmpty();
+ switch (SwigType_type(t)) {
+ case T_VOID:
+ break;
+ case T_REFERENCE:
+ {
+ String *lstr = SwigType_lstr(t, 0);
+ Printf(fcall, "%s = (%s) &", name, lstr);
+ Delete(lstr);
+ }
+ break;
+ case T_RVALUE_REFERENCE:
+ {
+ String *const_lvalue_str;
+ String *lstr = SwigType_lstr(t, 0);
+ SwigType *tt = Copy(t);
+ SwigType_del_rvalue_reference(tt);
+ SwigType_add_qualifier(tt, "const");
+ SwigType_add_reference(tt);
+ const_lvalue_str = SwigType_rcaststr(tt, 0);
+
+ Printf(fcall, "%s = (%s) &%s", name, lstr, const_lvalue_str);
+
+ Delete(const_lvalue_str);
+ Delete(tt);
+ Delete(lstr);
+ }
+ break;
+ case T_USER:
+ Printf(fcall, "%s = ", name);
+ break;
+
+ default:
+ /* Normal return value */
+ {
+ String *lstr = SwigType_lstr(t, 0);
+ Printf(fcall, "%s = (%s)", name, lstr);
+ Delete(lstr);
+ }
+ break;
+ }
+
+ /* Now print out function call */
+ Append(fcall, decl);
+
+ /* A sick hack */
+ {
+ char *c = Char(decl) + Len(decl) - 1;
+ if (!((*c == ';') || (*c == '}')))
+ Append(fcall, ";");
+ }
+
+ return fcall;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cfunction_call()
+ *
+ * Creates a string that calls a C function using the local variable rules
+ * defined above.
+ *
+ * name(arg0, arg1, arg2, ... argn)
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cfunction_call(const_String_or_char_ptr name, ParmList *parms) {
+ String *func;
+ int i = 0;
+ int comma = 0;
+ Parm *p = parms;
+ String *nname;
+
+ func = NewStringEmpty();
+ nname = SwigType_namestr(name);
+
+ /*
+ SWIGTEMPLATEDISAMBIGUATOR is compiler dependent (swiglabels.swg),
+ - SUN Studio 9 requires 'template',
+ - gcc-3.4 forbids the use of 'template'.
+ the rest seems not caring very much,
+ */
+ if (SwigType_istemplate(name)) {
+ String *prefix = Swig_scopename_prefix(nname);
+ if (!prefix || Len(prefix) == 0) {
+ Printf(func, "%s(", nname);
+ } else {
+ String *last = Swig_scopename_last(nname);
+ Printf(func, "%s::SWIGTEMPLATEDISAMBIGUATOR %s(", prefix, last);
+ Delete(last);
+ }
+ Delete(prefix);
+ } else {
+ Printf(func, "%s(", nname);
+ }
+ Delete(nname);
+
+ while (p) {
+ SwigType *pt = Getattr(p, "type");
+ if ((SwigType_type(pt) != T_VOID)) {
+ SwigType *rpt = SwigType_typedef_resolve_all(pt);
+ String *pname = Swig_cparm_name(p, i);
+ String *rcaststr = SwigType_rcaststr(rpt, pname);
+
+ if (comma) {
+ Append(func, ",");
+ }
+
+ if (cparse_cplusplus && SwigType_type(rpt) == T_USER)
+ Printv(func, "SWIG_STD_MOVE(", rcaststr, ")", NIL);
+ else
+ Printv(func, rcaststr, NIL);
+
+ Delete(rpt);
+ Delete(pname);
+ Delete(rcaststr);
+ comma = 1;
+ i++;
+ }
+ p = nextSibling(p);
+ }
+ Append(func, ")");
+ return func;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cmethod_call()
+ *
+ * Generates a string that calls a C++ method from a list of parameters.
+ *
+ * arg0->name(arg1, arg2, arg3, ..., argn)
+ *
+ * self is an argument that defines how to handle the first argument. Normally,
+ * it should be set to "this->". With C++ proxy classes enabled, it could be
+ * set to "(*this)->" or some similar sequence.
+ * ----------------------------------------------------------------------------- */
+
+static String *Swig_cmethod_call(const_String_or_char_ptr name, ParmList *parms, const_String_or_char_ptr self, String *explicit_qualifier, SwigType *director_type) {
+ String *func, *nname;
+ int i = 0;
+ Parm *p = parms;
+ SwigType *pt;
+ int comma = 0;
+
+ func = NewStringEmpty();
+ if (!p)
+ return func;
+
+ if (!self)
+ self = "(this)->";
+ Append(func, self);
+
+ if (SwigType_istemplate(name) && (strncmp(Char(name), "operator ", 9) == 0)) {
+ /* fix for template + operators and compilers like gcc 3.3.5 */
+ String *tprefix = SwigType_templateprefix(name);
+ nname = tprefix;
+ } else {
+ nname = SwigType_namestr(name);
+ }
+
+ if (director_type) {
+ const char *pname = "darg";
+ String *rcaststr = SwigType_rcaststr(director_type, pname);
+ Replaceall(func, "this", rcaststr);
+ Delete(rcaststr);
+ } else {
+ pt = Getattr(p, "type");
+
+ /* If the method is invoked through a dereferenced pointer, we don't add any casts
+ (needed for smart pointers). Otherwise, we cast to the appropriate type */
+
+ if (Strstr(func, "*this")) {
+ String *pname = Swig_cparm_name(p, 0);
+ Replaceall(func, "this", pname);
+ Delete(pname);
+ } else {
+ String *pname = Swig_cparm_name(p, 0);
+ String *rcaststr = SwigType_rcaststr(pt, pname);
+ Replaceall(func, "this", rcaststr);
+ Delete(rcaststr);
+ Delete(pname);
+ }
+
+ /*
+ SWIGTEMPLATEDESIMBUAGATOR is compiler dependent (swiglabels.swg),
+ - SUN Studio 9 requires 'template',
+ - gcc-3.4 forbids the use of 'template' (correctly implementing the ISO C++ standard)
+ the others don't seem to care,
+ */
+ if (SwigType_istemplate(name))
+ Printf(func, "SWIGTEMPLATEDISAMBIGUATOR ");
+
+ if (explicit_qualifier) {
+ Printv(func, explicit_qualifier, "::", NIL);
+ }
+ }
+
+ Printf(func, "%s(", nname);
+
+ i++;
+ p = nextSibling(p);
+ while (p) {
+ pt = Getattr(p, "type");
+ if ((SwigType_type(pt) != T_VOID)) {
+ String *pname = Swig_cparm_name(p, i);
+ String *rcaststr = SwigType_rcaststr(pt, pname);
+ if (comma)
+ Append(func, ",");
+ Append(func, rcaststr);
+ Delete(rcaststr);
+ Delete(pname);
+ comma = 1;
+ i++;
+ }
+ p = nextSibling(p);
+ }
+ Append(func, ")");
+ Delete(nname);
+ return func;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cconstructor_call()
+ *
+ * Creates a string that calls a C constructor function.
+ *
+ * calloc(1,sizeof(name));
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cconstructor_call(const_String_or_char_ptr name) {
+ DOH *func;
+
+ func = NewStringEmpty();
+ Printf(func, "calloc(1, sizeof(%s))", name);
+ return func;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_cppconstructor_call()
+ *
+ * Creates a string that calls a C function using the local variable rules
+ * defined above.
+ *
+ * name(arg0, arg1, arg2, ... argn)
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cppconstructor_base_call(const_String_or_char_ptr name, ParmList *parms, int skip_self) {
+ String *func;
+ String *nname;
+ int i = 0;
+ int comma = 0;
+ Parm *p = parms;
+ SwigType *pt;
+ if (skip_self) {
+ if (p)
+ p = nextSibling(p);
+ i++;
+ }
+ nname = SwigType_namestr(name);
+ func = NewStringEmpty();
+ Printf(func, "new %s(", nname);
+ while (p) {
+ pt = Getattr(p, "type");
+ if ((SwigType_type(pt) != T_VOID)) {
+ String *rcaststr = 0;
+ String *pname = 0;
+ if (comma)
+ Append(func, ",");
+ if (!Getattr(p, "arg:byname")) {
+ pname = Swig_cparm_name(p, i);
+ i++;
+ } else {
+ pname = Getattr(p, "value");
+ if (pname)
+ pname = Copy(pname);
+ else
+ pname = Copy(Getattr(p, "name"));
+ }
+ rcaststr = SwigType_rcaststr(pt, pname);
+ Append(func, rcaststr);
+ Delete(rcaststr);
+ comma = 1;
+ Delete(pname);
+ }
+ p = nextSibling(p);
+ }
+ Append(func, ")");
+ Delete(nname);
+ return func;
+}
+
+String *Swig_cppconstructor_call(const_String_or_char_ptr name, ParmList *parms) {
+ return Swig_cppconstructor_base_call(name, parms, 0);
+}
+
+String *Swig_cppconstructor_nodirector_call(const_String_or_char_ptr name, ParmList *parms) {
+ return Swig_cppconstructor_base_call(name, parms, 1);
+}
+
+String *Swig_cppconstructor_director_call(const_String_or_char_ptr name, ParmList *parms) {
+ return Swig_cppconstructor_base_call(name, parms, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * recursive_flag_search()
+ *
+ * This function searches for the class attribute 'attr' in the class
+ * 'n' or recursively in its bases.
+ *
+ * If you define SWIG_FAST_REC_SEARCH, the method will set the found
+ * 'attr' in the target class 'n'. If not, the method will set the
+ * 'noattr' one. This prevents of having to navigate the entire
+ * hierarchy tree every time, so, it is an O(1) method... or something
+ * like that. However, it populates all the parsed classes with the
+ * 'attr' and/or 'noattr' attributes.
+ *
+ * If you undefine the SWIG_FAST_REC_SEARCH no attribute will be set
+ * while searching. This could be slower for large projects with very
+ * large hierarchy trees... or maybe not. But it will be cleaner.
+ *
+ * Maybe later a swig option can be added to switch at runtime.
+ *
+ * ----------------------------------------------------------------------------- */
+
+/* #define SWIG_FAST_REC_SEARCH 1 */
+static String *recursive_flag_search(Node *n, const String *attr, const String *noattr) {
+ String *f = 0;
+ n = Swig_methodclass(n);
+ if (GetFlag(n, noattr)) {
+ return 0;
+ }
+ f = GetFlagAttr(n, attr);
+ if (f) {
+ return f;
+ } else {
+ List *bl = Getattr(n, "bases");
+ if (bl) {
+ Iterator bi;
+ for (bi = First(bl); bi.item; bi = Next(bi)) {
+ f = recursive_flag_search(bi.item, attr, noattr);
+ if (f) {
+#ifdef SWIG_FAST_REC_SEARCH
+ SetFlagAttr(n, attr, f);
+#endif
+ return f;
+ }
+ }
+ }
+ }
+#ifdef SWIG_FAST_REC_SEARCH
+ SetFlag(n, noattr);
+#endif
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_unref_call()
+ *
+ * Find the "feature:unref" call, if any.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_unref_call(Node *n) {
+ String *unref = recursive_flag_search(n, "feature:unref", "feature:nounref");
+ if (unref) {
+ String *pname = Swig_cparm_name(0, 0);
+ unref = NewString(unref);
+ Replaceall(unref, "$this", pname);
+ Replaceall(unref, "$self", pname);
+ Delete(pname);
+ }
+ return unref;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_ref_call()
+ *
+ * Find the "feature:ref" call, if any.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_ref_call(Node *n, const String *lname) {
+ String *ref = recursive_flag_search(n, "feature:ref", "feature:noref");
+ if (ref) {
+ ref = NewString(ref);
+ Replaceall(ref, "$this", lname);
+ Replaceall(ref, "$self", lname);
+ }
+ return ref;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cdestructor_call()
+ *
+ * Creates a string that calls a C destructor function.
+ *
+ * free((char *) arg0);
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cdestructor_call(Node *n) {
+ Node *cn = Swig_methodclass(n);
+ String *unref = Swig_unref_call(cn);
+
+ if (unref) {
+ return unref;
+ } else {
+ String *pname = Swig_cparm_name(0, 0);
+ String *call = NewStringf("free((char *) %s);", pname);
+ Delete(pname);
+ return call;
+ }
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_cppdestructor_call()
+ *
+ * Creates a string that calls a C destructor function.
+ *
+ * delete arg1;
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cppdestructor_call(Node *n) {
+ Node *cn = Swig_methodclass(n);
+ String *unref = Swig_unref_call(cn);
+ if (unref) {
+ return unref;
+ } else {
+ String *pname = Swig_cparm_name(0, 0);
+ String *call = NewStringf("delete %s;", pname);
+ Delete(pname);
+ return call;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_cmemberset_call()
+ *
+ * Generates a string that sets the name of a member in a C++ class or C struct.
+ *
+ * arg0->name = arg1
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cmemberset_call(const_String_or_char_ptr name, SwigType *type, String *self, int varcref) {
+ String *func;
+ String *pname0 = Swig_cparm_name(0, 0);
+ String *pname1 = Swig_cparm_name(0, 1);
+ func = NewStringEmpty();
+ if (!self)
+ self = NewString("(this)->");
+ else
+ self = NewString(self);
+ Replaceall(self, "this", pname0);
+ if (SwigType_type(type) != T_ARRAY) {
+ if (!Strstr(type, "enum $unnamed")) {
+ String *dref = Swig_wrapped_var_deref(type, pname1, varcref);
+ int extra_cast = 0;
+ if (cparse_cplusplusout) {
+ /* Required for C nested structs compiled as C++ as a duplicate of the nested struct is put into the global namespace.
+ * We could improve this by adding the extra casts just for nested structs rather than all structs. */
+ String *base = SwigType_base(type);
+ extra_cast = SwigType_isclass(base);
+ Delete(base);
+ }
+ if (extra_cast) {
+ String *lstr;
+ SwigType *ptype = Copy(type);
+ SwigType_add_pointer(ptype);
+ lstr = SwigType_lstr(ptype, 0);
+ Printf(func, "if (%s) *(%s)&%s%s = %s", pname0, lstr, self, name, dref);
+ Delete(lstr);
+ Delete(ptype);
+ } else {
+ Printf(func, "if (%s) %s%s = %s", pname0, self, name, dref);
+ }
+ Delete(dref);
+ } else {
+ Printf(func, "if (%s && sizeof(int) == sizeof(%s%s)) *(int*)(void*)&(%s%s) = %s", pname0, self, name, self, name, pname1);
+ }
+ }
+ Delete(self);
+ Delete(pname0);
+ Delete(pname1);
+ return (func);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_cmemberget_call()
+ *
+ * Generates a string that sets the name of a member in a C++ class or C struct.
+ *
+ * arg0->name
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_cmemberget_call(const_String_or_char_ptr name, SwigType *t, String *self, int varcref) {
+ String *func;
+ String *call;
+ String *pname0 = Swig_cparm_name(0, 0);
+ if (!self)
+ self = NewString("(this)->");
+ else
+ self = NewString(self);
+ Replaceall(self, "this", pname0);
+ func = NewStringEmpty();
+ call = Swig_wrapped_var_assign(t, "", varcref);
+ Printf(func, "%s (%s%s)", call, self, name);
+ Delete(self);
+ Delete(call);
+ Delete(pname0);
+ return func;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_replace_special_variables()
+ *
+ * Replaces special variables with a value from the supplied node
+ * ----------------------------------------------------------------------------- */
+void Swig_replace_special_variables(Node *n, Node *parentnode, String *code) {
+ Node *parentclass = parentnode;
+ String *overloaded = Getattr(n, "sym:overloaded");
+ Replaceall(code, "$name", Getattr(n, "name"));
+ Replaceall(code, "$symname", Getattr(n, "sym:name"));
+ Replaceall(code, "$wrapname", Getattr(n, "wrap:name"));
+ Replaceall(code, "$overname", overloaded ? Char(Getattr(n, "sym:overname")) : "");
+
+ if (Strstr(code, "$decl")) {
+ String *decl = Swig_name_decl(n);
+ Replaceall(code, "$decl", decl);
+ Delete(decl);
+ }
+ if (Strstr(code, "$fulldecl")) {
+ String *fulldecl = Swig_name_fulldecl(n);
+ Replaceall(code, "$fulldecl", fulldecl);
+ Delete(fulldecl);
+ }
+
+ if (parentclass && !Equal(nodeType(parentclass), "class"))
+ parentclass = 0;
+ if (Strstr(code, "$parentclasssymname")) {
+ String *parentclasssymname = 0;
+ if (parentclass)
+ parentclasssymname = Getattr(parentclass, "sym:name");
+ Replaceall(code, "$parentclasssymname", parentclasssymname ? parentclasssymname : "");
+ }
+ if (Strstr(code, "$parentclassname")) {
+ String *parentclassname = 0;
+ if (parentclass)
+ parentclassname = Getattr(parentclass, "name");
+ Replaceall(code, "$parentclassname", parentclassname ? SwigType_str(parentclassname, "") : "");
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * extension_code()
+ *
+ * Generates an extension function (a function defined in %extend)
+ *
+ * return_type function_name(parms) code
+ *
+ * ----------------------------------------------------------------------------- */
+static String *extension_code(Node *n, const String *function_name, ParmList *parms, SwigType *return_type, const String *code, int cplusplus, const String *self) {
+ String *parms_str = cplusplus ? ParmList_str_defaultargs(parms) : ParmList_str(parms);
+ String *sig = NewStringf("%s(%s)", function_name, (cplusplus || Len(parms_str)) ? parms_str : "void");
+ String *rt_sig = SwigType_str(return_type, sig);
+ String *body = NewStringf("SWIGINTERN %s", rt_sig);
+ Printv(body, code, "\n", NIL);
+ if (Strstr(body, "$")) {
+ Swig_replace_special_variables(n, parentNode(parentNode(n)), body);
+ if (self)
+ Replaceall(body, "$self", self);
+ }
+ Delete(parms_str);
+ Delete(sig);
+ Delete(rt_sig);
+ return body;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_add_extension_code()
+ *
+ * Generates an extension function (a function defined in %extend) and
+ * adds it to the "wrap:code" attribute of a node
+ *
+ * See also extension_code()
+ *
+ * ----------------------------------------------------------------------------- */
+int Swig_add_extension_code(Node *n, const String *function_name, ParmList *parms, SwigType *return_type, const String *code, int cplusplus, const String *self) {
+ String *body = extension_code(n, function_name, parms, return_type, code, cplusplus, self);
+ Setattr(n, "wrap:code", body);
+ Delete(body);
+ return SWIG_OK;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_MethodToFunction(Node *n)
+ *
+ * Converts a C++ method node to a function accessor function.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_MethodToFunction(Node *n, const_String_or_char_ptr nspace, String *classname, int flags, SwigType *director_type, int is_director) {
+ String *name;
+ ParmList *parms;
+ SwigType *type;
+ Parm *p;
+ String *self = 0;
+ int is_smart_pointer_overload = 0;
+ String *qualifier = Getattr(n, "qualifier");
+ String *directorScope = NewString(nspace);
+
+ Replace(directorScope, NSPACE_SEPARATOR, "_", DOH_REPLACE_ANY);
+
+ /* If smart pointer without const overload or mutable method, change self dereferencing */
+ if (flags & CWRAP_SMART_POINTER) {
+ if (flags & CWRAP_SMART_POINTER_OVERLOAD) {
+ if (qualifier && strncmp(Char(qualifier), "q(const)", 8) == 0) {
+ self = NewString("(*(this))->");
+ is_smart_pointer_overload = 1;
+ }
+ else if (Swig_storage_isstatic(n)) {
+ String *cname = Getattr(n, "extendsmartclassname") ? Getattr(n, "extendsmartclassname") : classname;
+ String *ctname = SwigType_namestr(cname);
+ self = NewStringf("(*(%s const *)this)->", ctname);
+ is_smart_pointer_overload = 1;
+ Delete(ctname);
+ }
+ else {
+ self = NewString("(*this)->");
+ }
+ } else {
+ self = NewString("(*this)->");
+ }
+ }
+
+ /* If node is a member template expansion, we don't allow added code */
+ if (Getattr(n, "templatetype"))
+ flags &= ~(CWRAP_EXTEND);
+
+ name = Getattr(n, "name");
+ parms = CopyParmList(nonvoid_parms(Getattr(n, "parms")));
+
+ type = NewString(classname);
+ if (qualifier) {
+ SwigType_push(type, qualifier);
+ }
+ SwigType_add_pointer(type);
+ p = NewParm(type, "self", n);
+ Setattr(p, "self", "1");
+ Setattr(p, "hidden","1");
+ /*
+ Disable the 'this' ownership in 'self' to manage inplace
+ operations like:
+
+ A& A::operator+=(int i) { ...; return *this;}
+
+ Here the 'self' parameter ownership needs to be disabled since
+ there could be two objects sharing the same 'this' pointer: the
+ input and the result one. And worse, the pointer could be deleted
+ in one of the objects (input), leaving the other (output) with
+ just a seg. fault to happen.
+
+ To avoid the previous problem, use
+
+ %feature("self:disown") *::operator+=;
+ %feature("new") *::operator+=;
+
+ These two lines just transfer the ownership of the 'this' pointer
+ from the input to the output wrapping object.
+
+ This happens in python, but may also happen in other target
+ languages.
+ */
+ if (GetFlag(n, "feature:self:disown")) {
+ Setattr(p, "wrap:disown", "1");
+ }
+ set_nextSibling(p, parms);
+ Delete(type);
+
+ /* Generate action code for the access */
+ if (!(flags & CWRAP_EXTEND)) {
+ String *explicit_qualifier = 0;
+ String *call = 0;
+ String *cres = 0;
+ String *explicitcall_name = 0;
+ int pure_virtual = !(Cmp(Getattr(n, "storage"), "virtual")) && !(Cmp(Getattr(n, "value"), "0"));
+
+ /* Call the explicit method rather than allow for a polymorphic call */
+ if ((flags & CWRAP_DIRECTOR_TWO_CALLS) || (flags & CWRAP_DIRECTOR_ONE_CALL)) {
+ String *access = Getattr(n, "access");
+ if (access && (Cmp(access, "protected") == 0)) {
+ /* If protected access (can only be if a director method) then call the extra public accessor method (language module must provide this) */
+ String *explicit_qualifier_tmp = SwigType_namestr(Getattr(Getattr(parentNode(n), "typescope"), "qname"));
+ explicitcall_name = NewStringf("%sSwigPublic", name);
+ if (Len(directorScope) > 0)
+ explicit_qualifier = NewStringf("SwigDirector_%s_%s", directorScope, explicit_qualifier_tmp);
+ else
+ explicit_qualifier = NewStringf("SwigDirector_%s", explicit_qualifier_tmp);
+ Delete(explicit_qualifier_tmp);
+ } else {
+ explicit_qualifier = SwigType_namestr(Getattr(Getattr(parentNode(n), "typescope"), "qname"));
+ }
+ }
+
+ if (!self && SwigType_isrvalue_reference(Getattr(n, "refqualifier"))) {
+ String *memory_header = NewString("<memory>");
+ Setfile(memory_header, Getfile(n));
+ Setline(memory_header, Getline(n));
+ Swig_fragment_emit(memory_header);
+ self = NewString("std::move(*this).");
+ Delete(memory_header);
+ }
+
+ call = Swig_cmethod_call(explicitcall_name ? explicitcall_name : name, p, self, explicit_qualifier, director_type);
+ cres = Swig_cresult(Getattr(n, "type"), Swig_cresult_name(), call);
+
+ if (pure_virtual && is_director && (flags & CWRAP_DIRECTOR_TWO_CALLS)) {
+ String *qualifier = SwigType_namestr(Getattr(Getattr(parentNode(n), "typescope"), "qname"));
+ Delete(cres);
+ cres = NewStringf("Swig::DirectorPureVirtualException::raise(\"%s::%s\");", qualifier, name);
+ Delete(qualifier);
+ }
+
+ if (flags & CWRAP_DIRECTOR_TWO_CALLS) {
+ /* Create two method calls, one to call the explicit method, the other a normal polymorphic function call */
+ String *cres_both_calls = NewStringf("");
+ String *call_extra = Swig_cmethod_call(name, p, self, 0, director_type);
+ String *cres_extra = Swig_cresult(Getattr(n, "type"), Swig_cresult_name(), call_extra);
+ Printv(cres_both_calls, "if (upcall) {\n", cres, "\n", "} else {", cres_extra, "\n}", NIL);
+ Setattr(n, "wrap:action", cres_both_calls);
+ Delete(cres_extra);
+ Delete(call_extra);
+ Delete(cres_both_calls);
+ } else {
+ Setattr(n, "wrap:action", cres);
+ }
+
+ Delete(explicitcall_name);
+ Delete(call);
+ Delete(cres);
+ Delete(explicit_qualifier);
+ } else {
+ /* Methods with default arguments are wrapped with additional methods for each default argument,
+ * however, only one extra %extend method is generated. */
+
+ String *defaultargs = Getattr(n, "defaultargs");
+ String *code = Getattr(n, "code");
+ String *cname = Getattr(n, "extendsmartclassname") ? Getattr(n, "extendsmartclassname") : classname;
+ String *membername = Swig_name_member(nspace, cname, name);
+ String *mangled = Swig_name_mangle(membername);
+ int is_smart_pointer = flags & CWRAP_SMART_POINTER;
+
+ type = Getattr(n, "type");
+
+ /* Check if the method is overloaded. If so, and it has code attached, we append an extra suffix
+ to avoid a name-clash in the generated wrappers. This allows overloaded methods to be defined
+ in C.
+
+ But when not using the suffix used for overloaded functions, we still need to ensure that the
+ wrapper name doesn't conflict with any wrapper functions for some languages, so optionally make
+ it sufficiently unique by appending a suffix similar to the one used for overloaded functions to it.
+ */
+ if (code) {
+ if (Getattr(n, "sym:overloaded")) {
+ Append(mangled, Getattr(defaultargs ? defaultargs : n, "sym:overname"));
+ } else if (UseWrapperSuffix) {
+ Append(mangled, "__SWIG");
+ }
+ }
+
+ /* See if there is any code that we need to emit */
+ if (!defaultargs && code && !is_smart_pointer) {
+ Swig_add_extension_code(n, mangled, p, type, code, cparse_cplusplus, "self");
+ }
+ if (is_smart_pointer) {
+ int i = 0;
+ Parm *pp = p;
+ String *func = NewStringf("%s(", mangled);
+ String *cres;
+
+ if (!Swig_storage_isstatic(n)) {
+ String *pname = Swig_cparm_name(pp, i);
+ String *ctname = SwigType_namestr(cname);
+ String *fadd = 0;
+ if (is_smart_pointer_overload) {
+ String *nclassname = SwigType_namestr(classname);
+ fadd = NewStringf("(%s const *)((%s const *)%s)->operator ->()", ctname, nclassname, pname);
+ Delete(nclassname);
+ }
+ else {
+ fadd = NewStringf("(%s*)(%s)->operator ->()", ctname, pname);
+ }
+ Append(func, fadd);
+ Delete(ctname);
+ Delete(fadd);
+ Delete(pname);
+ pp = nextSibling(pp);
+ if (pp)
+ Append(func, ",");
+ } else {
+ pp = nextSibling(pp);
+ }
+ ++i;
+ while (pp) {
+ SwigType *pt = Getattr(pp, "type");
+ if ((SwigType_type(pt) != T_VOID)) {
+ String *pname = Swig_cparm_name(pp, i++);
+ String *rcaststr = SwigType_rcaststr(pt, pname);
+ Append(func, rcaststr);
+ Delete(rcaststr);
+ Delete(pname);
+ pp = nextSibling(pp);
+ if (pp)
+ Append(func, ",");
+ }
+ }
+ Append(func, ")");
+ cres = Swig_cresult(Getattr(n, "type"), Swig_cresult_name(), func);
+ Setattr(n, "wrap:action", cres);
+ Delete(cres);
+ } else {
+ String *call = Swig_cfunction_call(mangled, p);
+ String *cres = Swig_cresult(Getattr(n, "type"), Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(call);
+ Delete(cres);
+ }
+
+ Delete(membername);
+ Delete(mangled);
+ }
+ Setattr(n, "parms", p);
+ Delete(p);
+ Delete(self);
+ Delete(parms);
+ Delete(directorScope);
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_methodclass()
+ *
+ * This function returns the class node for a given method or class.
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_methodclass(Node *n) {
+ Node *nodetype = nodeType(n);
+ if (Cmp(nodetype, "class") == 0)
+ return n;
+ return GetFlag(n, "feature:extend") ? parentNode(parentNode(n)) : parentNode(n);
+}
+
+int Swig_directorclass(Node *n) {
+ Node *classNode = Swig_methodclass(n);
+ assert(classNode != 0);
+ return (Getattr(classNode, "vtable") != 0);
+}
+
+Node *Swig_directormap(Node *module, String *type) {
+ int is_void = !Cmp(type, "void");
+ if (!is_void && module) {
+ /* ?? follow the inheritance hierarchy? */
+
+ String *base = SwigType_base(type);
+
+ Node *directormap = Getattr(module, "wrap:directormap");
+ if (directormap)
+ return Getattr(directormap, base);
+ }
+ return 0;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_ConstructorToFunction()
+ *
+ * This function creates a C wrapper for a C constructor function.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_ConstructorToFunction(Node *n, const_String_or_char_ptr nspace, String *classname, String *none_comparison, String *director_ctor, int cplus, int flags, String *directorname) {
+ Parm *p;
+ ParmList *directorparms;
+ SwigType *type;
+ int use_director = Swig_directorclass(n);
+ ParmList *parms = CopyParmList(nonvoid_parms(Getattr(n, "parms")));
+ /* Prepend the list of prefix_args (if any) */
+ Parm *prefix_args = Getattr(n, "director:prefix_args");
+ if (prefix_args != NIL) {
+ Parm *p2, *p3;
+
+ directorparms = CopyParmList(prefix_args);
+ for (p = directorparms; nextSibling(p); p = nextSibling(p));
+ for (p2 = parms; p2; p2 = nextSibling(p2)) {
+ p3 = CopyParm(p2);
+ set_nextSibling(p, p3);
+ Delete(p3);
+ p = p3;
+ }
+ } else
+ directorparms = parms;
+
+ type = NewString(classname);
+ SwigType_add_pointer(type);
+
+ if (flags & CWRAP_EXTEND) {
+ /* Constructors with default arguments are wrapped with additional constructor methods for each default argument,
+ * however, only one extra %extend method is generated. */
+ String *call;
+ String *cres;
+ String *defaultargs = Getattr(n, "defaultargs");
+ String *code = Getattr(n, "code");
+ String *membername = Swig_name_construct(nspace, classname);
+ String *mangled = Swig_name_mangle(membername);
+
+ /* Check if the constructor is overloaded. If so, and it has code attached, we append an extra suffix
+ to avoid a name-clash in the generated wrappers. This allows overloaded constructors to be defined
+ in C. */
+ if (Getattr(n, "sym:overloaded") && code) {
+ Append(mangled, Getattr(defaultargs ? defaultargs : n, "sym:overname"));
+ }
+
+ /* See if there is any code that we need to emit */
+ if (!defaultargs && code) {
+ Swig_add_extension_code(n, mangled, parms, type, code, cparse_cplusplus, "self");
+ }
+
+ call = Swig_cfunction_call(mangled, parms);
+ cres = Swig_cresult(type, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(cres);
+ Delete(call);
+ Delete(membername);
+ Delete(mangled);
+ } else {
+ if (cplus) {
+ /* if a C++ director class exists, create it rather than the original class */
+ if (use_director) {
+ Node *parent = Swig_methodclass(n);
+ int abstract = Getattr(parent, "abstracts") != 0;
+ String *action = NewStringEmpty();
+ String *tmp_none_comparison = Copy(none_comparison);
+ String *director_call;
+ String *nodirector_call;
+
+ Replaceall(tmp_none_comparison, "$arg", "arg1");
+
+ director_call = Swig_cppconstructor_director_call(directorname, directorparms);
+ nodirector_call = Swig_cppconstructor_nodirector_call(classname, parms);
+
+ if (abstract) {
+ /* whether or not the abstract class has been subclassed in python,
+ * create a director instance (there's no way to create a normal
+ * instance). if any of the pure virtual methods haven't been
+ * implemented in the target language, calls to those methods will
+ * generate Swig::DirectorPureVirtualException exceptions.
+ */
+ String *cres = Swig_cresult(type, Swig_cresult_name(), director_call);
+ Append(action, cres);
+ Delete(cres);
+ } else {
+ /* (scottm): The code for creating a new director is now a string
+ template that gets passed in via the director_ctor argument.
+
+ $comparison : an 'if' comparison from none_comparison
+ $director_new: Call new for director class
+ $nondirector_new: Call new for non-director class
+ */
+ String *cres;
+ Append(action, director_ctor);
+ Replaceall(action, "$comparison", tmp_none_comparison);
+
+ cres = Swig_cresult(type, Swig_cresult_name(), director_call);
+ Replaceall(action, "$director_new", cres);
+ Delete(cres);
+
+ cres = Swig_cresult(type, Swig_cresult_name(), nodirector_call);
+ Replaceall(action, "$nondirector_new", cres);
+ Delete(cres);
+ }
+ Setattr(n, "wrap:action", action);
+ Delete(tmp_none_comparison);
+ Delete(action);
+ } else {
+ String *call = Swig_cppconstructor_call(classname, parms);
+ String *cres = Swig_cresult(type, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(cres);
+ Delete(call);
+ }
+ } else {
+ String *call = Swig_cconstructor_call(classname);
+ String *cres = Swig_cresult(type, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(cres);
+ Delete(call);
+ }
+ }
+ Setattr(n, "type", type);
+ Setattr(n, "parms", parms);
+ Delete(type);
+ if (directorparms != parms)
+ Delete(directorparms);
+ Delete(parms);
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_DestructorToFunction()
+ *
+ * This function creates a C wrapper for a destructor function.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_DestructorToFunction(Node *n, const_String_or_char_ptr nspace, String *classname, int cplus, int flags) {
+ SwigType *type;
+ Parm *p;
+
+ type = NewString(classname);
+ SwigType_add_pointer(type);
+ p = NewParm(type, "self", n);
+ Setattr(p, "self", "1");
+ Setattr(p, "hidden", "1");
+ Setattr(p, "wrap:disown", "1");
+ Delete(type);
+ type = NewString("void");
+
+ if (flags & CWRAP_EXTEND) {
+ String *cres;
+ String *call;
+ String *membername, *mangled, *code;
+ membername = Swig_name_destroy(nspace, classname);
+ mangled = Swig_name_mangle(membername);
+ code = Getattr(n, "code");
+ if (code) {
+ Swig_add_extension_code(n, mangled, p, type, code, cparse_cplusplus, "self");
+ }
+ call = Swig_cfunction_call(mangled, p);
+ cres = NewStringf("%s;", call);
+ Setattr(n, "wrap:action", cres);
+ Delete(membername);
+ Delete(mangled);
+ Delete(call);
+ Delete(cres);
+ } else {
+ if (cplus) {
+ String *call = Swig_cppdestructor_call(n);
+ String *cres = NewStringf("%s", call);
+ Setattr(n, "wrap:action", cres);
+ Delete(call);
+ Delete(cres);
+ } else {
+ String *call = Swig_cdestructor_call(n);
+ String *cres = NewStringf("%s", call);
+ Setattr(n, "wrap:action", cres);
+ Delete(call);
+ Delete(cres);
+ }
+ }
+ Setattr(n, "type", type);
+ Setattr(n, "parms", p);
+ Delete(type);
+ Delete(p);
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_MembersetToFunction()
+ *
+ * This function creates a C wrapper for setting a structure member.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_MembersetToFunction(Node *n, String *classname, int flags) {
+ String *name;
+ ParmList *parms;
+ Parm *p;
+ SwigType *t;
+ SwigType *ty;
+ SwigType *type;
+ SwigType *void_type = NewString("void");
+ String *self = 0;
+
+ int varcref = flags & CWRAP_NATURAL_VAR;
+
+ if (flags & CWRAP_SMART_POINTER) {
+ self = NewString("(*this)->");
+ }
+ if (flags & CWRAP_ALL_PROTECTED_ACCESS) {
+ self = NewStringf("darg->");
+ }
+
+ name = Getattr(n, "name");
+ type = Getattr(n, "type");
+
+ t = NewString(classname);
+ SwigType_add_pointer(t);
+ parms = NewParm(t, "self", n);
+ Setattr(parms, "self", "1");
+ Setattr(parms, "hidden","1");
+ Delete(t);
+
+ ty = Swig_wrapped_member_var_type(type, varcref);
+ p = NewParm(ty, name, n);
+ Setattr(parms, "hidden", "1");
+ set_nextSibling(parms, p);
+
+ /* If the type is a pointer or reference. We mark it with a special wrap:disown attribute */
+ if (SwigType_check_decl(type, "p.")) {
+ Setattr(p, "wrap:disown", "1");
+ }
+ Delete(p);
+
+ if (flags & CWRAP_EXTEND) {
+ String *call;
+ String *cres;
+ String *code = Getattr(n, "code");
+
+ String *sname = Swig_name_set(0, name);
+ String *membername = Swig_name_member(0, classname, sname);
+ String *mangled = Swig_name_mangle(membername);
+
+ if (code) {
+ /* I don't think this ever gets run - WSF */
+ Swig_add_extension_code(n, mangled, parms, void_type, code, cparse_cplusplus, "self");
+ }
+ call = Swig_cfunction_call(mangled, parms);
+ cres = NewStringf("%s;", call);
+ Setattr(n, "wrap:action", cres);
+
+ Delete(cres);
+ Delete(call);
+ Delete(mangled);
+ Delete(membername);
+ Delete(sname);
+ } else {
+ String *call = Swig_cmemberset_call(name, type, self, varcref);
+ String *cres = NewStringf("%s;", call);
+ Setattr(n, "wrap:action", cres);
+ Delete(call);
+ Delete(cres);
+ }
+ Setattr(n, "type", void_type);
+ Setattr(n, "parms", parms);
+ Delete(parms);
+ Delete(ty);
+ Delete(void_type);
+ Delete(self);
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_MembergetToFunction()
+ *
+ * This function creates a C wrapper for getting a structure member.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_MembergetToFunction(Node *n, String *classname, int flags) {
+ String *name;
+ ParmList *parms;
+ SwigType *t;
+ SwigType *ty;
+ SwigType *type;
+ String *self = 0;
+
+ int varcref = flags & CWRAP_NATURAL_VAR;
+
+ if (flags & CWRAP_SMART_POINTER) {
+ if (Swig_storage_isstatic(n)) {
+ Node *sn = Getattr(n, "cplus:staticbase");
+ String *base = Getattr(sn, "name");
+ self = NewStringf("%s::", base);
+ } else if (flags & CWRAP_SMART_POINTER_OVERLOAD) {
+ String *nclassname = SwigType_namestr(classname);
+ self = NewStringf("(*(%s const *)this)->", nclassname);
+ Delete(nclassname);
+ } else {
+ self = NewString("(*this)->");
+ }
+ }
+ if (flags & CWRAP_ALL_PROTECTED_ACCESS) {
+ self = NewStringf("darg->");
+ }
+
+ name = Getattr(n, "name");
+ type = Getattr(n, "type");
+
+ t = NewString(classname);
+ SwigType_add_pointer(t);
+ parms = NewParm(t, "self", n);
+ Setattr(parms, "self", "1");
+ Setattr(parms, "hidden","1");
+ Delete(t);
+
+ ty = Swig_wrapped_member_var_type(type, varcref);
+ if (flags & CWRAP_EXTEND) {
+ String *call;
+ String *cres;
+ String *code = Getattr(n, "code");
+
+ String *gname = Swig_name_get(0, name);
+ String *membername = Swig_name_member(0, classname, gname);
+ String *mangled = Swig_name_mangle(membername);
+
+ if (code) {
+ /* I don't think this ever gets run - WSF */
+ Swig_add_extension_code(n, mangled, parms, ty, code, cparse_cplusplus, "self");
+ }
+ call = Swig_cfunction_call(mangled, parms);
+ cres = Swig_cresult(ty, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+
+ Delete(cres);
+ Delete(call);
+ Delete(mangled);
+ Delete(membername);
+ Delete(gname);
+ } else {
+ String *call = Swig_cmemberget_call(name, type, self, varcref);
+ String *cres = Swig_cresult(ty, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(call);
+ Delete(cres);
+ }
+ Setattr(n, "type", ty);
+ Setattr(n, "parms", parms);
+ Delete(parms);
+ Delete(ty);
+
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_VarsetToFunction()
+ *
+ * This function creates a C wrapper for setting a global variable or static member
+ * variable.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_VarsetToFunction(Node *n, int flags) {
+ String *name, *nname;
+ ParmList *parms;
+ SwigType *type, *ty;
+
+ int varcref = flags & CWRAP_NATURAL_VAR;
+
+ name = Getattr(n, "name");
+ type = Getattr(n, "type");
+ nname = SwigType_namestr(name);
+ ty = Swig_wrapped_var_type(type, varcref);
+ parms = NewParm(ty, name, n);
+
+ if (flags & CWRAP_EXTEND) {
+ String *sname = Swig_name_set(0, name);
+ String *mangled = Swig_name_mangle(sname);
+ String *call = Swig_cfunction_call(mangled, parms);
+ String *cres = NewStringf("%s;", call);
+ Setattr(n, "wrap:action", cres);
+ Delete(cres);
+ Delete(call);
+ Delete(mangled);
+ Delete(sname);
+ } else {
+ if (!Strstr(type, "enum $unnamed")) {
+ String *pname = Swig_cparm_name(0, 0);
+ String *dref = Swig_wrapped_var_deref(type, pname, varcref);
+ String *call = NewStringf("%s = %s;", nname, dref);
+ Setattr(n, "wrap:action", call);
+ Delete(call);
+ Delete(dref);
+ Delete(pname);
+ } else {
+ String *pname = Swig_cparm_name(0, 0);
+ String *call = NewStringf("if (sizeof(int) == sizeof(%s)) *(int*)(void*)&(%s) = %s;", nname, nname, pname);
+ Setattr(n, "wrap:action", call);
+ Delete(pname);
+ Delete(call);
+ }
+ }
+ Setattr(n, "type", "void");
+ Setattr(n, "parms", parms);
+ Delete(parms);
+ Delete(ty);
+ Delete(nname);
+ return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_VargetToFunction()
+ *
+ * This function creates a C wrapper for getting a global variable or static member
+ * variable.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_VargetToFunction(Node *n, int flags) {
+ String *cres, *call;
+ String *name;
+ SwigType *type;
+ SwigType *ty = 0;
+
+ int varcref = flags & CWRAP_NATURAL_VAR;
+
+ name = Getattr(n, "name");
+ type = Getattr(n, "type");
+ ty = Swig_wrapped_var_type(type, varcref);
+
+ if (flags & CWRAP_EXTEND) {
+ String *sname = Swig_name_get(0, name);
+ String *mangled = Swig_name_mangle(sname);
+ call = Swig_cfunction_call(mangled, 0);
+ cres = Swig_cresult(ty, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(mangled);
+ Delete(sname);
+ } else {
+ String *nname = 0;
+ if (Equal(nodeType(n), "constant")) {
+ String *rawval = Getattr(n, "rawval");
+ String *value = rawval ? rawval : Getattr(n, "value");
+ nname = NewStringf("(%s)", value);
+ } else {
+ nname = SwigType_namestr(name);
+ }
+ call = Swig_wrapped_var_assign(type, nname, varcref);
+ cres = Swig_cresult(ty, Swig_cresult_name(), call);
+ Setattr(n, "wrap:action", cres);
+ Delete(nname);
+ }
+
+ Setattr(n, "type", ty);
+ Delattr(n, "parms");
+ Delete(cres);
+ Delete(call);
+ Delete(ty);
+ return SWIG_OK;
+}
diff --git a/contrib/tools/swig/Source/Swig/deprecate.c b/contrib/tools/swig/Source/Swig/deprecate.c
new file mode 100644
index 0000000000..5783455e59
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/deprecate.c
@@ -0,0 +1,107 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * deprecate.c
+ *
+ * The functions in this file are SWIG core functions that are deprecated
+ * or which do not fit in nicely with everything else. Generally this means
+ * that the function and/or API needs to be changed in some future release.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+
+/* ---------------------------------------------------------------------
+ * ParmList_is_compactdefargs()
+ *
+ * Returns 1 if the parameter list passed in is marked for compact argument
+ * handling (by the "compactdefargs" attribute). Otherwise returns 0.
+ * ---------------------------------------------------------------------- */
+
+/* Discussion:
+
+ "compactdefargs" is a property set by the Parser to indicate special
+ handling of default arguments. This property seems to be something that
+ is associated with functions and methods rather than low-level ParmList
+ objects. Therefore, I don't like the fact that this special purpose
+ feature is bolted onto the side of ParmList objects.
+
+ Proposed solution:
+
+ 1. "compactdefargs" should be a feature set on function/method nodes
+ instead of ParmList objects. For example, if you have a function,
+ you would check the function node to see if the parameters are
+ to be handled in this way.
+
+
+ Difficulties:
+
+ 1. This is used by functions in cwrap.c and emit.cxx, none of which
+ are passed information about the function/method node. We might
+ have to change the API of those functions to make this work correctly.
+ For example:
+
+ int emit_num_required(ParmList *parms)
+
+ might become
+
+ int emit_num_required(ParmList *parms, int compactargs)
+
+*/
+
+int ParmList_is_compactdefargs(ParmList *p) {
+ int compactdefargs = 0;
+
+ if (p) {
+ compactdefargs = Getattr(p, "compactdefargs") ? 1 : 0;
+
+ /* The "compactdefargs" attribute should only be set on the first parameter in the list.
+ * However, sometimes an extra parameter is inserted at the beginning of the parameter list,
+ * so we check the 2nd parameter too. */
+ if (!compactdefargs) {
+ Parm *nextparm = nextSibling(p);
+ compactdefargs = (nextparm && Getattr(nextparm, "compactdefargs")) ? 1 : 0;
+ }
+ }
+
+ return compactdefargs;
+}
+
+/* ---------------------------------------------------------------------
+ * ParmList_errorstr()
+ *
+ * Generate a prototype string suitable for use in error/warning messages.
+ * Similar to ParmList_protostr() but is also aware of hidden parameters.
+ * ---------------------------------------------------------------------- */
+
+/* Discussion. This function is used to generate error messages, but take
+ into account that there might be a hidden parameter. Although this involves
+ parameter lists, it really isn't a core feature of swigparm.h or parms.c.
+ This is because the "hidden" attribute of parameters is added elsewhere (cwrap.c).
+
+ For now, this function is placed here because it doesn't really seem to fit in
+ with the parms.c interface.
+
+*/
+
+String *ParmList_errorstr(ParmList *p) {
+ String *out = NewStringEmpty();
+ while (p) {
+ if (Getattr(p,"hidden")) {
+ p = nextSibling(p);
+ } else {
+ String *pstr = SwigType_str(Getattr(p, "type"), 0);
+ Append(out, pstr);
+ p = nextSibling(p);
+ if (p) {
+ Append(out, ",");
+ }
+ Delete(pstr);
+ }
+ }
+ return out;
+}
diff --git a/contrib/tools/swig/Source/Swig/error.c b/contrib/tools/swig/Source/Swig/error.c
new file mode 100644
index 0000000000..efd644f9a8
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/error.c
@@ -0,0 +1,351 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * error.c
+ *
+ * Error handling functions. These are used to issue warnings and
+ * error messages.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include <stdarg.h>
+#include <ctype.h>
+
+/* -----------------------------------------------------------------------------
+ * Commentary on the warning filter.
+ *
+ * The warning filter is a string of numbers prefaced by (-) or (+) to
+ * indicate whether or not a warning message is displayed. For example:
+ *
+ * "-304-201-140+210+201"
+ *
+ * The filter string is scanned left to right and the first occurrence
+ * of a warning number is used to determine printing behavior.
+ *
+ * The same number may appear more than once in the string. For example, in the
+ * above string, "201" appears twice. This simply means that warning 201
+ * was disabled after it was previously enabled. This may only be temporary
+ * setting--the first number may be removed later in which case the warning
+ * is reenabled.
+ * ----------------------------------------------------------------------------- */
+
+#if defined(_WIN32)
+# define DEFAULT_ERROR_MSG_FORMAT EMF_MICROSOFT
+#else
+# define DEFAULT_ERROR_MSG_FORMAT EMF_STANDARD
+#endif
+static ErrorMessageFormat msg_format = DEFAULT_ERROR_MSG_FORMAT;
+static int silence = 0; /* Silent operation */
+static String *filter = 0; /* Warning filter */
+static int warnall = 0;
+static int nwarning = 0;
+static int nerrors = 0;
+
+static int init_fmt = 0;
+static char wrn_wnum_fmt[64];
+static char wrn_nnum_fmt[64];
+static char err_line_fmt[64];
+static char err_eof_fmt[64];
+static char diag_line_fmt[64];
+static char diag_eof_fmt[64];
+
+static String *format_filename(const_String_or_char_ptr filename);
+
+/* -----------------------------------------------------------------------------
+ * Swig_warning()
+ *
+ * Issue a warning message on stderr.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_warning(int wnum, const_String_or_char_ptr filename, int line, const char *fmt, ...) {
+ String *out;
+ char *msg;
+ int wrn = 1;
+ va_list ap;
+ if (silence)
+ return;
+ if (!init_fmt)
+ Swig_error_msg_format(DEFAULT_ERROR_MSG_FORMAT);
+
+ va_start(ap, fmt);
+
+ out = NewStringEmpty();
+ vPrintf(out, fmt, ap);
+
+ msg = Char(out);
+ if (isdigit((unsigned char) *msg)) {
+ unsigned long result = strtoul(msg, &msg, 10);
+ if (msg != Char(out)) {
+ msg++;
+ wnum = result;
+ }
+ }
+
+ /* Check in the warning filter */
+ if (filter) {
+ char temp[32];
+ char *c;
+ char *f = Char(filter);
+ sprintf(temp, "%d", wnum);
+ while (*f != '\0' && (c = strstr(f, temp))) {
+ if (*(c - 1) == '-') {
+ wrn = 0; /* Warning disabled */
+ break;
+ }
+ if (*(c - 1) == '+') {
+ wrn = 1; /* Warning enabled */
+ break;
+ }
+ f += strlen(temp);
+ }
+ }
+ if (warnall || wrn) {
+ String *formatted_filename = format_filename(filename);
+ String *full_message = NewString("");
+ if (wnum) {
+ Printf(full_message, wrn_wnum_fmt, formatted_filename, line, wnum);
+ } else {
+ Printf(full_message, wrn_nnum_fmt, formatted_filename, line);
+ }
+ Printf(full_message, "%s", msg);
+ Printv(stderr, full_message, NIL);
+ nwarning++;
+ Delete(full_message);
+ Delete(formatted_filename);
+ }
+ Delete(out);
+ va_end(ap);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_error()
+ *
+ * Issue an error message on stderr.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_error(const_String_or_char_ptr filename, int line, const char *fmt, ...) {
+ va_list ap;
+ String *formatted_filename = NULL;
+ String *full_message = NULL;
+
+ if (silence)
+ return;
+ if (!init_fmt)
+ Swig_error_msg_format(DEFAULT_ERROR_MSG_FORMAT);
+
+ va_start(ap, fmt);
+ formatted_filename = format_filename(filename);
+ full_message = NewString("");
+ if (line > 0) {
+ Printf(full_message, err_line_fmt, formatted_filename, line);
+ } else {
+ Printf(full_message, err_eof_fmt, formatted_filename);
+ }
+ vPrintf(full_message, fmt, ap);
+ Printv(stderr, full_message, NIL);
+ va_end(ap);
+ nerrors++;
+ Delete(full_message);
+ Delete(formatted_filename);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_error_count()
+ *
+ * Returns number of errors received.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_error_count(void) {
+ return nerrors;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_error_silent()
+ *
+ * Set silent flag
+ * ----------------------------------------------------------------------------- */
+
+void Swig_error_silent(int s) {
+ silence = s;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_warnfilter()
+ *
+ * Takes a comma separate list of warning numbers and puts in the filter.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_warnfilter(const_String_or_char_ptr wlist, int add) {
+ char *c;
+ char *cw;
+ String *s;
+ if (!filter)
+ filter = NewStringEmpty();
+
+ s = NewString("");
+ Clear(s);
+ cw = Char(wlist);
+ while (*cw != '\0') {
+ if (*cw != ' ') {
+ Putc(*cw, s);
+ }
+ ++cw;
+ }
+ c = Char(s);
+ c = strtok(c, ", ");
+ while (c) {
+ if (isdigit((int) *c) || (*c == '+') || (*c == '-')) {
+ /* Even if c is a digit, the rest of the string might not be, eg in the case of typemap
+ * warnings (a bit odd really), eg: %warnfilter(SWIGWARN_TYPEMAP_CHARLEAK_MSG) */
+ if (add) {
+ Insert(filter, 0, c);
+ if (isdigit((int) *c)) {
+ Insert(filter, 0, "-");
+ }
+ } else {
+ char *temp = (char *)Malloc(sizeof(char)*strlen(c) + 2);
+ if (isdigit((int) *c)) {
+ sprintf(temp, "-%s", c);
+ } else {
+ strcpy(temp, c);
+ }
+ Replace(filter, temp, "", DOH_REPLACE_FIRST);
+ Free(temp);
+ }
+ }
+ c = strtok(NULL, ", ");
+ }
+ Delete(s);
+}
+
+void Swig_warnall(void) {
+ warnall = 1;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_warn_count()
+ *
+ * Return the number of warnings
+ * ----------------------------------------------------------------------------- */
+
+int Swig_warn_count(void) {
+ return nwarning;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_error_msg_format()
+ *
+ * Set the type of error/warning message display
+ * ----------------------------------------------------------------------------- */
+
+void Swig_error_msg_format(ErrorMessageFormat format) {
+ const char *error = "Error";
+ const char *warning = "Warning";
+
+ const char *fmt_eof = 0;
+ const char *fmt_line = 0;
+
+ /* here 'format' could be directly a string instead of an enum, but
+ by now a switch is used to translated into one. */
+ switch (format) {
+ case EMF_MICROSOFT:
+ fmt_line = "%s(%d) ";
+ fmt_eof = "%s(999999) "; /* Is there a special character for EOF? Just use a large number. */
+ break;
+ case EMF_STANDARD:
+ default:
+ fmt_line = "%s:%d";
+ fmt_eof = "%s:EOF";
+ }
+
+ sprintf(wrn_wnum_fmt, "%s: %s %%d: ", fmt_line, warning);
+ sprintf(wrn_nnum_fmt, "%s: %s: ", fmt_line, warning);
+ sprintf(err_line_fmt, "%s: %s: ", fmt_line, error);
+ sprintf(err_eof_fmt, "%s: %s: ", fmt_eof, error);
+ sprintf(diag_line_fmt, "%s: ", fmt_line);
+ sprintf(diag_eof_fmt, "%s: ", fmt_eof);
+
+ msg_format = format;
+ init_fmt = 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * format_filename()
+ *
+ * Remove double backslashes in Windows filename paths for display
+ * ----------------------------------------------------------------------------- */
+static String *format_filename(const_String_or_char_ptr filename) {
+ String *formatted_filename = NewString(filename);
+#if defined(_WIN32)
+ Replaceall(formatted_filename, "\\\\", "\\");
+#endif
+ return formatted_filename;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_stringify_with_location()
+ *
+ * Return a string representation of any DOH object with line and file location
+ * information in the appropriate error message format. The string representation
+ * is enclosed within [] brackets after the line and file information.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_stringify_with_location(DOH *object) {
+ String *str = NewStringEmpty();
+
+ if (!init_fmt)
+ Swig_error_msg_format(DEFAULT_ERROR_MSG_FORMAT);
+
+ if (object) {
+ int line = Getline(object);
+ String *formatted_filename = format_filename(Getfile(object));
+ if (line > 0) {
+ Printf(str, diag_line_fmt, formatted_filename, line);
+ } else {
+ Printf(str, diag_eof_fmt, formatted_filename);
+ }
+ if (Len(object) == 0) {
+ Printf(str, "[EMPTY]");
+ } else {
+ Printf(str, "[%s]", object);
+ }
+ Delete(formatted_filename);
+ } else {
+ Printf(str, "[NULL]");
+ }
+
+ return str;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_diagnostic()
+ *
+ * Issue a diagnostic message on stdout.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_diagnostic(const_String_or_char_ptr filename, int line, const char *fmt, ...) {
+ va_list ap;
+ String *formatted_filename = NULL;
+
+ if (!init_fmt)
+ Swig_error_msg_format(DEFAULT_ERROR_MSG_FORMAT);
+
+ va_start(ap, fmt);
+ formatted_filename = format_filename(filename);
+ if (line > 0) {
+ Printf(stdout, diag_line_fmt, formatted_filename, line);
+ } else {
+ Printf(stdout, diag_eof_fmt, formatted_filename);
+ }
+ vPrintf(stdout, fmt, ap);
+ va_end(ap);
+ Delete(formatted_filename);
+}
+
diff --git a/contrib/tools/swig/Source/Swig/extend.c b/contrib/tools/swig/Source/Swig/extend.c
new file mode 100644
index 0000000000..23660c0ad8
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/extend.c
@@ -0,0 +1,141 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * extend.c
+ *
+ * Extensions support (%extend)
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+
+static Hash *extendhash = 0; /* Hash table of added methods */
+
+/* -----------------------------------------------------------------------------
+ * Swig_extend_hash()
+ *
+ * Access the extend hash
+ * ----------------------------------------------------------------------------- */
+Hash *Swig_extend_hash(void) {
+ if (!extendhash)
+ extendhash = NewHash();
+ return extendhash;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_extend_merge()
+ *
+ * Extension merge. This function is used to handle the %extend directive
+ * when it appears before a class definition. To handle this, the %extend
+ * actually needs to take precedence. Therefore, we will selectively nuke symbols
+ * from the current symbol table, replacing them with the added methods.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_extend_merge(Node *cls, Node *am) {
+ Node *n;
+ Node *csym;
+
+ n = firstChild(am);
+ while (n) {
+ String *symname;
+ if (Strcmp(nodeType(n),"constructor") == 0) {
+ symname = Getattr(n,"sym:name");
+ if (symname) {
+ if (Strcmp(symname,Getattr(n,"name")) == 0) {
+ /* If the name and the sym:name of a constructor are the same,
+ then it hasn't been renamed. However---the name of the class
+ itself might have been renamed so we need to do a consistency
+ check here */
+ if (Getattr(cls,"sym:name")) {
+ Setattr(n,"sym:name", Getattr(cls,"sym:name"));
+ }
+ }
+ }
+ }
+
+ symname = Getattr(n,"sym:name");
+ DohIncref(symname);
+ if ((symname) && (!Getattr(n,"error"))) {
+ /* Remove node from its symbol table */
+ Swig_symbol_remove(n);
+ csym = Swig_symbol_add(symname,n);
+ if (csym != n) {
+ /* Conflict with previous definition. Nuke previous definition */
+ String *e = NewStringEmpty();
+ String *en = NewStringEmpty();
+ String *ec = NewStringEmpty();
+ Printf(ec,"Identifier '%s' redefined by %%extend (ignored),",symname);
+ Printf(en,"%%extend definition of '%s'.",symname);
+ SWIG_WARN_NODE_BEGIN(n);
+ Swig_warning(WARN_PARSE_REDEFINED,Getfile(csym),Getline(csym),"%s\n",ec);
+ Swig_warning(WARN_PARSE_REDEFINED,Getfile(n),Getline(n),"%s\n",en);
+ SWIG_WARN_NODE_END(n);
+ Printf(e,"%s:%d:%s\n%s:%d:%s\n",Getfile(csym),Getline(csym),ec,
+ Getfile(n),Getline(n),en);
+ Setattr(csym,"error",e);
+ Delete(e);
+ Delete(en);
+ Delete(ec);
+ Swig_symbol_remove(csym); /* Remove class definition */
+ Swig_symbol_add(symname,n); /* Insert extend definition */
+ }
+ }
+ n = nextSibling(n);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_extend_append_previous()
+ * ----------------------------------------------------------------------------- */
+
+void Swig_extend_append_previous(Node *cls, Node *am) {
+ Node *n, *ne;
+ Node *pe = 0;
+ Node *ae = 0;
+
+ if (!am) return;
+
+ n = firstChild(am);
+ while (n) {
+ ne = nextSibling(n);
+ set_nextSibling(n,0);
+ /* typemaps and fragments need to be prepended */
+ if (((Cmp(nodeType(n),"typemap") == 0) || (Cmp(nodeType(n),"fragment") == 0))) {
+ if (!pe) pe = Swig_cparse_new_node("extend");
+ appendChild(pe, n);
+ } else {
+ if (!ae) ae = Swig_cparse_new_node("extend");
+ appendChild(ae, n);
+ }
+ n = ne;
+ }
+ if (pe) prependChild(cls,pe);
+ if (ae) appendChild(cls,ae);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_extend_unused_check()
+ *
+ * Check for unused %extend. Special case, don't report unused
+ * extensions for templates
+ * ----------------------------------------------------------------------------- */
+
+void Swig_extend_unused_check(void) {
+ Iterator ki;
+
+ if (!extendhash) return;
+ for (ki = First(extendhash); ki.key; ki = Next(ki)) {
+ if (!Strchr(ki.key,'<')) {
+ SWIG_WARN_NODE_BEGIN(ki.item);
+ Swig_warning(WARN_PARSE_EXTEND_UNDEF,Getfile(ki.item), Getline(ki.item), "%%extend defined for an undeclared class %s.\n", SwigType_namestr(ki.key));
+ SWIG_WARN_NODE_END(ki.item);
+ }
+ }
+}
+
diff --git a/contrib/tools/swig/Source/Swig/fragment.c b/contrib/tools/swig/Source/Swig/fragment.c
new file mode 100644
index 0000000000..03b231fa13
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/fragment.c
@@ -0,0 +1,188 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * fragment.c
+ *
+ * This file manages named code fragments. Code fragments are typically
+ * used to hold helper-code that may or may not be included in the wrapper
+ * file (depending on what features are actually used in the interface).
+ *
+ * By using fragments, it's possible to greatly reduce the amount of
+ * wrapper code and to generate cleaner wrapper files.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "swigwarn.h"
+#include "cparse.h"
+
+static Hash *fragments = 0;
+static Hash *looking_fragments = 0;
+static int debug = 0;
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_fragment_register()
+ *
+ * Add a fragment. Use the original Node*, so, if something needs to be
+ * changed, lang.cxx doesn't need to be touched again.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_fragment_register(Node *fragment) {
+ if (Getattr(fragment, "emitonly")) {
+ Swig_fragment_emit(fragment);
+ return;
+ } else {
+ String *name = Copy(Getattr(fragment, "value"));
+ String *type = Getattr(fragment, "type");
+ if (type) {
+ SwigType *rtype = SwigType_typedef_resolve_all(type);
+ String *mangle = Swig_string_mangle(type);
+ Append(name, mangle);
+ Delete(mangle);
+ Delete(rtype);
+ if (debug)
+ Printf(stdout, "register fragment %s %s\n", name, type);
+ }
+ if (!fragments) {
+ fragments = NewHash();
+ }
+ if (!Getattr(fragments, name)) {
+ String *section = Copy(Getattr(fragment, "section"));
+ String *ccode = Copy(Getattr(fragment, "code"));
+ Hash *kwargs = Getattr(fragment, "kwargs");
+ Setmeta(ccode, "section", section);
+ if (kwargs) {
+ Setmeta(ccode, "kwargs", kwargs);
+ }
+ Setfile(ccode, Getfile(fragment));
+ Setline(ccode, Getline(fragment));
+ /* Replace $descriptor() macros */
+ Swig_cparse_replace_descriptor(ccode);
+ Setattr(fragments, name, ccode);
+ if (debug)
+ Printf(stdout, "registering fragment %s %s\n", name, section);
+ Delete(section);
+ Delete(ccode);
+ }
+ Delete(name);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_fragment_emit()
+ *
+ * Emit a fragment
+ * ----------------------------------------------------------------------------- */
+
+static
+char *char_index(char *str, char c) {
+ while (*str && (c != *str))
+ ++str;
+ return (c == *str) ? str : 0;
+}
+
+void Swig_fragment_emit(Node *n) {
+ String *code;
+ char *pc, *tok;
+ String *t;
+ String *mangle = 0;
+ String *name = 0;
+ String *type = 0;
+
+ name = Getattr(n, "value");
+ if (!name) {
+ name = n;
+ }
+
+ if (!fragments) {
+ Swig_warning(WARN_FRAGMENT_NOT_FOUND, Getfile(n), Getline(n), "Fragment '%s' not found.\n", name);
+ return;
+ }
+
+ type = Getattr(n, "type");
+ if (type) {
+ mangle = Swig_string_mangle(type);
+ }
+
+ if (debug)
+ Printf(stdout, "looking fragment %s %s\n", name, type);
+ t = Copy(name);
+ tok = Char(t);
+ pc = char_index(tok, ',');
+ if (pc)
+ *pc = 0;
+ while (tok) {
+ String *name = NewString(tok);
+ if (mangle)
+ Append(name, mangle);
+ if (looking_fragments && Getattr(looking_fragments, name)) {
+ return;
+ }
+ code = Getattr(fragments, name);
+ if (debug)
+ Printf(stdout, "looking subfragment %s\n", name);
+ if (code && (Strcmp(code, "ignore") != 0)) {
+ String *section = Getmeta(code, "section");
+ Hash *nn = Getmeta(code, "kwargs");
+ if (!looking_fragments)
+ looking_fragments = NewHash();
+ Setattr(looking_fragments, name, "1");
+ while (nn) {
+ if (Equal(Getattr(nn, "name"), "fragment")) {
+ if (debug)
+ Printf(stdout, "emitting fragment %s %s\n", nn, type);
+ Setfile(nn, Getfile(n));
+ Setline(nn, Getline(n));
+ Swig_fragment_emit(nn);
+ }
+ nn = nextSibling(nn);
+ }
+ if (section) {
+ File *f = Swig_filebyname(section);
+ if (!f) {
+ Swig_error(Getfile(code), Getline(code), "Bad section '%s' in %%fragment declaration for code fragment '%s'\n", section, name);
+ } else {
+ if (debug)
+ Printf(stdout, "emitting subfragment %s %s\n", name, section);
+ if (debug)
+ Printf(f, "/* begin fragment %s */\n", name);
+ Printf(f, "%s\n", code);
+ if (debug)
+ Printf(f, "/* end fragment %s */\n\n", name);
+ Setattr(fragments, name, "ignore");
+ Delattr(looking_fragments, name);
+ }
+ }
+ } else if (!code && type) {
+ SwigType *rtype = SwigType_typedef_resolve_all(type);
+ if (!Equal(type, rtype)) {
+ String *name = Copy(Getattr(n, "value"));
+ String *mangle = Swig_string_mangle(type);
+ Append(name, mangle);
+ Setfile(name, Getfile(n));
+ Setline(name, Getline(n));
+ Swig_fragment_emit(name);
+ Delete(mangle);
+ Delete(name);
+ }
+ Delete(rtype);
+ }
+
+ if (!code) {
+ Swig_warning(WARN_FRAGMENT_NOT_FOUND, Getfile(n), Getline(n), "Fragment '%s' not found.\n", name);
+ }
+ tok = pc ? pc + 1 : 0;
+ if (tok) {
+ pc = char_index(tok, ',');
+ if (pc)
+ *pc = 0;
+ }
+ Delete(name);
+ }
+ Delete(t);
+}
diff --git a/contrib/tools/swig/Source/Swig/getopt.c b/contrib/tools/swig/Source/Swig/getopt.c
new file mode 100644
index 0000000000..7791d13f72
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/getopt.c
@@ -0,0 +1,104 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * getopt.c
+ *
+ * Handles the parsing of command line options. This is particularly nasty
+ * compared to other utilities given that command line options can potentially
+ * be read by many different modules within SWIG. Thus, in order to make sure
+ * there are no unrecognized options, each module is required to "mark"
+ * the options that it uses. Afterwards, we can make a quick scan to make
+ * sure there are no unmarked options.
+ *
+ * TODO:
+ * Should have cleaner error handling in general.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+
+static char **args;
+static int numargs;
+static int *marked;
+
+/* -----------------------------------------------------------------------------
+ * Swig_init_args()
+ *
+ * Initialize the argument list handler.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_init_args(int argc, char **argv) {
+ assert(argc > 0);
+ assert(argv);
+
+ numargs = argc;
+ args = argv;
+ marked = (int *) Calloc(numargs, sizeof(int));
+ marked[0] = 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_mark_arg()
+ *
+ * Marks an argument as being parsed.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_mark_arg(int n) {
+ assert(marked);
+ assert((n >= 0) && (n < numargs));
+ marked[n] = 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_check_marked()
+ *
+ * Checks to see if argument has been picked up.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_check_marked(int n) {
+ assert((n >= 0) && (n < numargs));
+ return marked[n];
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_check_options()
+ *
+ * Checkers for unprocessed command line options and errors.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_check_options(int check_input) {
+ int error = 0;
+ int i;
+ int max = check_input ? numargs - 1 : numargs;
+ assert(marked);
+ for (i = 1; i < max; i++) {
+ if (!marked[i]) {
+ Printf(stderr, "swig error : Unrecognized option %s\n", args[i]);
+ error = 1;
+ }
+ }
+ if (error) {
+ Printf(stderr, "Use 'swig -help' for available options.\n");
+ Exit(EXIT_FAILURE);
+ }
+ if (check_input && marked[numargs - 1]) {
+ Printf(stderr, "Must specify an input file. Use -help for available options.\n");
+ Exit(EXIT_FAILURE);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_arg_error()
+ *
+ * Generates a generic error message and exits.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_arg_error(void) {
+ Printf(stderr, "SWIG : Unable to parse command line options.\n");
+ Printf(stderr, "Use 'swig -help' for available options.\n");
+ Exit(EXIT_FAILURE);
+}
diff --git a/contrib/tools/swig/Source/Swig/include.c b/contrib/tools/swig/Source/Swig/include.c
new file mode 100644
index 0000000000..c153ac9b7a
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/include.c
@@ -0,0 +1,377 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * include.c
+ *
+ * The functions in this file are used to manage files in the SWIG library.
+ * General purpose functions for opening, including, and retrieving pathnames
+ * are provided.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+
+static List *directories = 0; /* List of include directories */
+static String *lastpath = 0; /* Last file that was included */
+static List *pdirectories = 0; /* List of pushed directories */
+static int dopush = 1; /* Whether to push directories */
+static int file_debug = 0;
+
+/* This functions determine whether to push/pop dirs in the preprocessor */
+void Swig_set_push_dir(int push) {
+ dopush = push;
+}
+
+int Swig_get_push_dir(void) {
+ return dopush;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_add_directory()
+ *
+ * Adds a directory to the SWIG search path.
+ * ----------------------------------------------------------------------------- */
+
+List *Swig_add_directory(const_String_or_char_ptr dirname) {
+ String *adirname;
+ if (!directories)
+ directories = NewList();
+ assert(directories);
+ if (dirname) {
+ adirname = NewString(dirname);
+ Append(directories,adirname);
+ Delete(adirname);
+ }
+ return directories;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_push_directory()
+ *
+ * Inserts a directory at the front of the SWIG search path. This is used by
+ * the preprocessor to grab files in the same directory as other included files.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_push_directory(const_String_or_char_ptr dirname) {
+ String *pdirname;
+ if (!Swig_get_push_dir())
+ return;
+ if (!pdirectories)
+ pdirectories = NewList();
+ assert(pdirectories);
+ pdirname = NewString(dirname);
+ assert(pdirname);
+ Insert(pdirectories,0,pdirname);
+ Delete(pdirname);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_pop_directory()
+ *
+ * Pops a directory off the front of the SWIG search path. This is used by
+ * the preprocessor.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_pop_directory(void) {
+ if (!Swig_get_push_dir())
+ return;
+ if (!pdirectories)
+ return;
+ Delitem(pdirectories, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_last_file()
+ *
+ * Returns the full pathname of the last file opened.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_last_file(void) {
+ assert(lastpath);
+ return lastpath;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_search_path_any()
+ *
+ * Returns a list of the current search paths.
+ * ----------------------------------------------------------------------------- */
+
+static List *Swig_search_path_any(int syspath) {
+ String *filename;
+ List *slist;
+ int i, ilen;
+
+ slist = NewList();
+ assert(slist);
+ filename = NewStringEmpty();
+ assert(filename);
+ Printf(filename, ".%s", SWIG_FILE_DELIMITER);
+ Append(slist, filename);
+ Delete(filename);
+
+ /* If there are any pushed directories. Add them first */
+ if (pdirectories) {
+ ilen = Len(pdirectories);
+ for (i = 0; i < ilen; i++) {
+ filename = NewString(Getitem(pdirectories,i));
+ Append(filename,SWIG_FILE_DELIMITER);
+ Append(slist,filename);
+ Delete(filename);
+ }
+ }
+ /* Add system directories next */
+ ilen = Len(directories);
+ for (i = 0; i < ilen; i++) {
+ filename = NewString(Getitem(directories,i));
+ Append(filename,SWIG_FILE_DELIMITER);
+ if (syspath) {
+ /* If doing a system include, put the system directories first */
+ Insert(slist,i,filename);
+ } else {
+ /* Otherwise, just put the system directories after the pushed directories (if any) */
+ Append(slist,filename);
+ }
+ Delete(filename);
+ }
+ return slist;
+}
+
+List *Swig_search_path(void) {
+ return Swig_search_path_any(0);
+}
+
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_open()
+ *
+ * open a file, optionally looking for it in the include path. Returns an open
+ * FILE * on success.
+ * ----------------------------------------------------------------------------- */
+
+static FILE *Swig_open_file(const_String_or_char_ptr name, int sysfile, int use_include_path) {
+ FILE *f;
+ String *filename;
+ List *spath = 0;
+ char *cname;
+ int i, ilen, nbytes;
+ char bom[3];
+
+ if (!directories)
+ directories = NewList();
+ assert(directories);
+
+ cname = Char(name);
+ filename = NewString(cname);
+ assert(filename);
+ if (file_debug) {
+ Printf(stdout, " Open: %s\n", filename);
+ }
+ f = fopen(Char(filename), "r");
+ if (!f && use_include_path) {
+ spath = Swig_search_path_any(sysfile);
+ ilen = Len(spath);
+ for (i = 0; i < ilen; i++) {
+ Clear(filename);
+ Printf(filename, "%s%s", Getitem(spath, i), cname);
+ f = fopen(Char(filename), "r");
+ if (f)
+ break;
+ }
+ Delete(spath);
+ }
+ if (f) {
+ Delete(lastpath);
+ lastpath = filename;
+
+ /* Skip the UTF-8 BOM if it's present */
+ nbytes = (int)fread(bom, 1, 3, f);
+ if (nbytes == 3 && bom[0] == (char)0xEF && bom[1] == (char)0xBB && bom[2] == (char)0xBF) {
+ /* skip */
+ } else {
+ fseek(f, 0, SEEK_SET);
+ }
+ }
+ return f;
+}
+
+/* Open a file - searching the include paths to find it */
+FILE *Swig_include_open(const_String_or_char_ptr name) {
+ return Swig_open_file(name, 0, 1);
+}
+
+/* Open a file - does not use include paths to find it */
+FILE *Swig_open(const_String_or_char_ptr name) {
+ return Swig_open_file(name, 0, 0);
+}
+
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_read_file()
+ *
+ * Reads data from an open FILE * and returns it as a string.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_read_file(FILE *f) {
+ int len;
+ char buffer[4096];
+ String *str = NewStringEmpty();
+
+ assert(str);
+ while (fgets(buffer, 4095, f)) {
+ Append(str, buffer);
+ }
+ len = Len(str);
+ /* Add a newline if not present on last line -- the preprocessor seems to
+ * rely on \n and not EOF terminating lines */
+ if (len) {
+ char *cstr = Char(str);
+ if (cstr[len - 1] != '\n') {
+ Append(str, "\n");
+ }
+ }
+ return str;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_include()
+ *
+ * Opens a file and returns it as a string.
+ * ----------------------------------------------------------------------------- */
+
+static String *Swig_include_any(const_String_or_char_ptr name, int sysfile) {
+ FILE *f;
+ String *str;
+ String *file;
+
+ f = Swig_open_file(name, sysfile, 1);
+ if (!f)
+ return 0;
+ str = Swig_read_file(f);
+ fclose(f);
+ Seek(str, 0, SEEK_SET);
+ file = Copy(Swig_last_file());
+ Setfile(str, file);
+ Delete(file);
+ Setline(str, 1);
+ return str;
+}
+
+String *Swig_include(const_String_or_char_ptr name) {
+ return Swig_include_any(name, 0);
+}
+
+String *Swig_include_sys(const_String_or_char_ptr name) {
+ return Swig_include_any(name, 1);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_insert_file()
+ *
+ * Copies the contents of a file into another file
+ * ----------------------------------------------------------------------------- */
+
+int Swig_insert_file(const_String_or_char_ptr filename, File *outfile) {
+ char buffer[4096];
+ int nbytes;
+ FILE *f = Swig_include_open(filename);
+
+ if (!f)
+ return -1;
+ while ((nbytes = Read(f, buffer, 4096)) > 0) {
+ Write(outfile, buffer, nbytes);
+ }
+ fclose(f);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_register_filebyname()
+ *
+ * Register a "named" file with the core. Named files can become targets
+ * for %insert directives and other SWIG operations. This function takes
+ * the place of the f_header, f_wrapper, f_init, and other global variables
+ * in SWIG1.1
+ * ----------------------------------------------------------------------------- */
+
+static Hash *named_files = 0;
+
+void Swig_register_filebyname(const_String_or_char_ptr filename, File *outfile) {
+ if (!named_files)
+ named_files = NewHash();
+ Setattr(named_files, filename, outfile);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_filebyname()
+ *
+ * Get a named file
+ * ----------------------------------------------------------------------------- */
+
+File *Swig_filebyname(const_String_or_char_ptr filename) {
+ if (!named_files)
+ return 0;
+ return Getattr(named_files, filename);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_file_extension()
+ *
+ * Returns the extension of a file
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_file_extension(const_String_or_char_ptr filename) {
+ String *name = Swig_file_filename(filename);
+ const char *c = strrchr(Char(name), '.');
+ String *extension = c ? NewString(c) : NewString("");
+ Delete(name);
+ return extension;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_file_basename()
+ *
+ * Returns the filename with the extension removed.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_file_basename(const_String_or_char_ptr filename) {
+ String *extension = Swig_file_extension(filename);
+ String *basename = NewStringWithSize(filename, Len(filename) - Len(extension));
+ Delete(extension);
+ return basename;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_file_filename()
+ *
+ * Return the file name with any leading path stripped off
+ * ----------------------------------------------------------------------------- */
+String *Swig_file_filename(const_String_or_char_ptr filename) {
+ const char *delim = SWIG_FILE_DELIMITER;
+ const char *c = strrchr(Char(filename), *delim);
+ return c ? NewString(c + 1) : NewString(filename);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_file_dirname()
+ *
+ * Return the name of the directory associated with a file
+ * ----------------------------------------------------------------------------- */
+String *Swig_file_dirname(const_String_or_char_ptr filename) {
+ const char *delim = SWIG_FILE_DELIMITER;
+ const char *c = strrchr(Char(filename), *delim);
+ return c ? NewStringWithSize(filename, (int)(c - Char(filename) + 1)) : NewString("");
+}
+
+/*
+ * Swig_file_debug()
+ */
+void Swig_file_debug_set(void) {
+ file_debug = 1;
+}
diff --git a/contrib/tools/swig/Source/Swig/misc.c b/contrib/tools/swig/Source/Swig/misc.c
new file mode 100644
index 0000000000..7d1119c5e2
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/misc.c
@@ -0,0 +1,1569 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * misc.c
+ *
+ * Miscellaneous functions that don't really fit anywhere else.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include <direct.h>
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (((mode) & S_IFDIR) == S_IFDIR)
+#endif
+#endif
+
+static char *fake_version = 0;
+
+/* -----------------------------------------------------------------------------
+ * Swig_copy_string()
+ *
+ * Duplicate a NULL-terminate string given as a char *.
+ * ----------------------------------------------------------------------------- */
+
+char *Swig_copy_string(const char *s) {
+ char *c = 0;
+ if (s) {
+ c = (char *) Malloc(strlen(s) + 1);
+ strcpy(c, s);
+ }
+ return c;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_set_fakeversion()
+ *
+ * Version string override
+ * ----------------------------------------------------------------------------- */
+
+void Swig_set_fakeversion(const char *version) {
+ fake_version = Swig_copy_string(version);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_package_version()
+ *
+ * Return the package string containing the version number
+ * ----------------------------------------------------------------------------- */
+
+const char *Swig_package_version(void) {
+ return fake_version ? fake_version : PACKAGE_VERSION;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_package_version_hex()
+ *
+ * Return the package version in hex format "0xAABBCC" such as "0x040200" for 4.2.0
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_package_version_hex(void) {
+ String *package_version = NewString(Swig_package_version());
+ char *token = strtok(Char(package_version), ".");
+ String *vers = NewString("SWIG_VERSION 0x");
+ int count = 0;
+ while (token) {
+ int len = (int)strlen(token);
+ assert(len == 1 || len == 2);
+ Printf(vers, "%s%s", (len == 1) ? "0" : "", token);
+ token = strtok(NULL, ".");
+ count++;
+ }
+ Delete(package_version);
+ assert(count == 3); /* Check version format is correct */
+ return vers;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_obligatory_macros()
+ *
+ * Generates the SWIG_VERSION and SWIGXXX macros where XXX is the target language
+ * name (must be provided uppercase).
+ * ----------------------------------------------------------------------------- */
+
+void Swig_obligatory_macros(String *f_runtime, const char *language) {
+ String *version_hex = Swig_package_version_hex();
+ Printf(f_runtime, "\n\n");
+ Printf(f_runtime, "#define %s\n", version_hex);
+ Printf(f_runtime, "#define SWIG%s\n", language);
+ Delete(version_hex);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_banner()
+ *
+ * Emits the SWIG identifying banner for the C/C++ wrapper file.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_banner(File *f) {
+ Printf(f, "/* ----------------------------------------------------------------------------\n");
+ Swig_banner_target_lang(f, " *");
+ Printf(f, " * ----------------------------------------------------------------------------- */\n");
+
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_banner_target_lang()
+ *
+ * Emits a SWIG identifying banner in the target language
+ * ----------------------------------------------------------------------------- */
+
+void Swig_banner_target_lang(File *f, const_String_or_char_ptr commentchar) {
+ Printf(f, "%s This file was automatically generated by SWIG (https://www.swig.org).\n", commentchar);
+ Printf(f, "%s Version %s\n", commentchar, Swig_package_version());
+ Printf(f, "%s\n", commentchar);
+ Printf(f, "%s Do not make changes to this file unless you know what you are doing - modify\n", commentchar);
+ Printf(f, "%s the SWIG interface file instead.\n", commentchar);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_strip_c_comments()
+ *
+ * Return a new string with C comments stripped from the input string. NULL is
+ * returned if there aren't any comments.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_strip_c_comments(const String *s) {
+ const char *c = Char(s);
+ const char *comment_begin = 0;
+ const char *comment_end = 0;
+ String *stripped = 0;
+
+ while (*c) {
+ if (!comment_begin && *c == '/') {
+ ++c;
+ if (!*c)
+ break;
+ if (*c == '*')
+ comment_begin = c-1;
+ } else if (comment_begin && !comment_end && *c == '*') {
+ ++c;
+ if (*c == '/') {
+ comment_end = c;
+ break;
+ }
+ }
+ ++c;
+ }
+
+ if (comment_begin && comment_end) {
+ int size = (int)(comment_begin - Char(s));
+ String *stripmore = 0;
+ stripped = NewStringWithSize(s, size);
+ Printv(stripped, comment_end + 1, NIL);
+ do {
+ stripmore = Swig_strip_c_comments(stripped);
+ if (stripmore) {
+ Delete(stripped);
+ stripped = stripmore;
+ }
+ } while (stripmore);
+ }
+ return stripped;
+}
+
+/* -----------------------------------------------------------------------------
+ * is_directory()
+ * ----------------------------------------------------------------------------- */
+static int is_directory(String *directory) {
+ int last = Len(directory) - 1;
+ int statres;
+ struct stat st;
+ char *dir = Char(directory);
+ if (dir[last] == SWIG_FILE_DELIMITER[0]) {
+ /* remove trailing slash - can cause S_ISDIR to fail on Windows, at least */
+ dir[last] = 0;
+ statres = stat(dir, &st);
+ dir[last] = SWIG_FILE_DELIMITER[0];
+ } else {
+ statres = stat(dir, &st);
+ }
+ return (statres == 0 && S_ISDIR(st.st_mode));
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_new_subdirectory()
+ *
+ * Create the subdirectory only if the basedirectory already exists as a directory.
+ * basedirectory can be empty to indicate current directory but not NULL.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_new_subdirectory(String *basedirectory, String *subdirectory) {
+ String *error = 0;
+ int current_directory = Len(basedirectory) == 0;
+
+ if (current_directory || is_directory(basedirectory)) {
+ Iterator it;
+ String *dir = NewString(basedirectory);
+ List *subdirs = Split(subdirectory, SWIG_FILE_DELIMITER[0], INT_MAX);
+
+ for (it = First(subdirs); it.item; it = Next(it)) {
+ int result;
+ String *subdirectory = it.item;
+ Printf(dir, "%s", subdirectory);
+#ifdef _WIN32
+ result = _mkdir(Char(dir));
+#else
+ result = mkdir(Char(dir), 0777);
+#endif
+ if (result != 0 && errno != EEXIST) {
+ error = NewStringf("Cannot create directory %s: %s", dir, strerror(errno));
+ break;
+ }
+ if (!is_directory(dir)) {
+ error = NewStringf("Cannot create directory %s: it may already exist but not be a directory", dir);
+ break;
+ }
+ Printf(dir, SWIG_FILE_DELIMITER);
+ }
+ } else {
+ error = NewStringf("Cannot create subdirectory %s under the base directory %s. Either the base does not exist as a directory or it is not readable.", subdirectory, basedirectory);
+ }
+ return error;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_filename_correct()
+ *
+ * Corrects filename paths by removing duplicate delimiters and on non-unix
+ * systems use the correct delimiter across the whole name.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_filename_correct(String *filename) {
+ int network_path = 0;
+ if (Len(filename) >= 2) {
+ const char *fname = Char(filename);
+ if (fname[0] == '\\' && fname[1] == '\\')
+ network_path = 1;
+ if (fname[0] == '/' && fname[1] == '/')
+ network_path = 1;
+ }
+#if defined(_WIN32)
+ /* accept Unix path separator on non-Unix systems */
+ Replaceall(filename, "/", SWIG_FILE_DELIMITER);
+#endif
+#if defined(__CYGWIN__)
+ /* accept Windows path separator in addition to Unix path separator */
+ Replaceall(filename, "\\", SWIG_FILE_DELIMITER);
+#endif
+ /* remove all duplicate file name delimiters */
+ while (Replaceall(filename, SWIG_FILE_DELIMITER SWIG_FILE_DELIMITER, SWIG_FILE_DELIMITER)) {
+ }
+ /* Network paths can start with a double slash on Windows - unremove the duplicate slash we just removed */
+ if (network_path)
+ Insert(filename, 0, SWIG_FILE_DELIMITER);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_filename_escape()
+ *
+ * Escapes backslashes in filename - for Windows
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_filename_escape(String *filename) {
+ String *adjusted_filename = Copy(filename);
+ Swig_filename_correct(adjusted_filename);
+#if defined(_WIN32) /* Note not on Cygwin else filename is displayed with double '/' */
+ Replaceall(adjusted_filename, "\\", "\\\\");
+#endif
+ return adjusted_filename;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_filename_escape()
+ *
+ * Escapes spaces in filename - for Makefiles
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_filename_escape_space(String *filename) {
+ String *adjusted_filename = Copy(filename);
+ Swig_filename_correct(adjusted_filename);
+ Replaceall(adjusted_filename, " ", "\\ ");
+ return adjusted_filename;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_filename_unescape()
+ *
+ * Remove double backslash escaping in filename - for Windows
+ * ----------------------------------------------------------------------------- */
+
+void Swig_filename_unescape(String *filename) {
+ (void)filename;
+#if defined(_WIN32)
+ Replaceall(filename, "\\\\", "\\");
+#endif
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_storage_isextern()
+ *
+ * Determine if the storage class specifier is extern (but not externc)
+ * ----------------------------------------------------------------------------- */
+
+int Swig_storage_isextern(Node *n) {
+ const String *storage = Getattr(n, "storage");
+ return storage ? Strcmp(storage, "extern") == 0 || Strncmp(storage, "extern ", 7) == 0 : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_storage_isexternc()
+ *
+ * Determine if the storage class specifier is externc (but not plain extern)
+ * ----------------------------------------------------------------------------- */
+
+int Swig_storage_isexternc(Node *n) {
+ const String *storage = Getattr(n, "storage");
+ return storage ? Strcmp(storage, "externc") == 0 || Strncmp(storage, "externc ", 8) == 0 : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_storage_isstatic_custom()
+ *
+ * Determine if the storage class specifier is static
+ * ----------------------------------------------------------------------------- */
+
+int Swig_storage_isstatic_custom(Node *n, const_String_or_char_ptr storage_name) {
+ const String *storage = Getattr(n, storage_name);
+ return storage ? Strncmp(storage, "static", 6) == 0 : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_storage_isstatic()
+ *
+ * Determine if the storage class specifier is static
+ * ----------------------------------------------------------------------------- */
+
+int Swig_storage_isstatic(Node *n) {
+ return Swig_storage_isstatic_custom(n, "storage");
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_escape()
+ *
+ * Takes a string object and produces a string with escape codes added to it.
+ * Octal escaping is used.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_escape(String *s) {
+ String *ns;
+ int c;
+ ns = NewStringEmpty();
+
+ while ((c = Getc(s)) != EOF) {
+ if (c == '\n') {
+ Printf(ns, "\\n");
+ } else if (c == '\r') {
+ Printf(ns, "\\r");
+ } else if (c == '\t') {
+ Printf(ns, "\\t");
+ } else if (c == '\\') {
+ Printf(ns, "\\\\");
+ } else if (c == '\'') {
+ Printf(ns, "\\'");
+ } else if (c == '\"') {
+ Printf(ns, "\\\"");
+ } else if (c == ' ') {
+ Putc(c, ns);
+ } else if (!isgraph(c)) {
+ if (c < 0)
+ c += UCHAR_MAX + 1;
+ Printf(ns, "\\%o", c);
+ } else {
+ Putc(c, ns);
+ }
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_hexescape()
+ *
+ * Takes a string object and produces a string with escape codes added to it.
+ * Hex escaping is used.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_hexescape(String *s) {
+ String *ns;
+ int c;
+ ns = NewStringEmpty();
+
+ while ((c = Getc(s)) != EOF) {
+ if (c == '\n') {
+ Printf(ns, "\\n");
+ } else if (c == '\r') {
+ Printf(ns, "\\r");
+ } else if (c == '\t') {
+ Printf(ns, "\\t");
+ } else if (c == '\\') {
+ Printf(ns, "\\\\");
+ } else if (c == '\'') {
+ Printf(ns, "\\'");
+ } else if (c == '\"') {
+ Printf(ns, "\\\"");
+ } else if (c == ' ') {
+ Putc(c, ns);
+ } else if (!isgraph(c)) {
+ if (c < 0)
+ c += UCHAR_MAX + 1;
+ Printf(ns, "\\x%X", c);
+ } else {
+ Putc(c, ns);
+ }
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_upper()
+ *
+ * Takes a string object and returns a copy that is uppercase
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_upper(String *s) {
+ String *ns;
+ int c;
+ ns = NewStringEmpty();
+
+ Seek(s, 0, SEEK_SET);
+ while ((c = Getc(s)) != EOF) {
+ Putc(toupper(c), ns);
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_lower()
+ *
+ * Takes a string object and returns a copy that is lowercase
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_lower(String *s) {
+ String *ns;
+ int c;
+ ns = NewStringEmpty();
+
+ Seek(s, 0, SEEK_SET);
+ while ((c = Getc(s)) != EOF) {
+ Putc(tolower(c), ns);
+ }
+ return ns;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_title()
+ *
+ * Takes a string object and returns a copy that is lowercase with first letter
+ * capitalized
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_title(String *s) {
+ String *ns;
+ int first = 1;
+ int c;
+ ns = NewStringEmpty();
+
+ Seek(s, 0, SEEK_SET);
+ while ((c = Getc(s)) != EOF) {
+ Putc(first ? toupper(c) : tolower(c), ns);
+ first = 0;
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_ccase()
+ *
+ * Takes a string object and returns a copy that is lowercase with the first
+ * letter capitalized and the one following '_', which are removed.
+ *
+ * camel_case -> CamelCase
+ * camelCase -> CamelCase
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_ccase(String *s) {
+ String *ns;
+ int first = 1;
+ int c;
+ ns = NewStringEmpty();
+
+ Seek(s, 0, SEEK_SET);
+ while ((c = Getc(s)) != EOF) {
+ if (c == '_') {
+ first = 1;
+ continue;
+ }
+ Putc(first ? toupper(c) : c, ns);
+ first = 0;
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_lccase()
+ *
+ * Takes a string object and returns a copy with the character after
+ * each '_' capitalised, and the '_' removed. The first character is
+ * also forced to lowercase.
+ *
+ * camel_case -> camelCase
+ * CamelCase -> camelCase
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_lccase(String *s) {
+ String *ns;
+ int first = 1;
+ int after_underscore = 0;
+ int c;
+ ns = NewStringEmpty();
+
+ Seek(s, 0, SEEK_SET);
+ while ((c = Getc(s)) != EOF) {
+ if (c == '_') {
+ after_underscore = 1;
+ continue;
+ }
+ if (first) {
+ Putc(tolower(c), ns);
+ first = 0;
+ } else {
+ Putc(after_underscore ? toupper(c) : c, ns);
+ }
+ after_underscore = 0;
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_ucase()
+ *
+ * This is the reverse case of ccase, ie
+ *
+ * CamelCase -> camel_case
+ * get2D -> get_2d
+ * asFloat2 -> as_float2
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_ucase(String *s) {
+ String *ns;
+ int c;
+ int lastC = 0;
+ int nextC = 0;
+ int underscore = 0;
+ ns = NewStringEmpty();
+
+ /* We insert a underscore when:
+ 1. Lower case char followed by upper case char
+ getFoo > get_foo; getFOo > get_foo; GETFOO > getfoo
+ 2. Number preceded by char and not end of string
+ get2D > get_2d; get22D > get_22d; GET2D > get_2d
+ but:
+ asFloat2 > as_float2
+ */
+
+ Seek(s, 0, SEEK_SET);
+
+ while ((c = Getc(s)) != EOF) {
+ nextC = Getc(s); Ungetc(nextC, s);
+ if (isdigit(c) && isalpha(lastC) && nextC != EOF)
+ underscore = 1;
+ else if (isupper(c) && isalpha(lastC) && !isupper(lastC))
+ underscore = 1;
+
+ lastC = c;
+
+ if (underscore) {
+ Putc('_', ns);
+ underscore = 0;
+ }
+
+ Putc(tolower(c), ns);
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_first_upper()
+ *
+ * Make the first character in the string uppercase, leave all the
+ * rest the same. This is used by the Ruby module to provide backwards
+ * compatibility with the old way of naming classes and constants. For
+ * more info see the Ruby documentation.
+ *
+ * firstUpper -> FirstUpper
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_first_upper(String *s) {
+ String *ns = NewStringEmpty();
+ char *cs = Char(s);
+ if (cs && cs[0] != 0) {
+ Putc(toupper((int)cs[0]), ns);
+ Append(ns, cs + 1);
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_first_lower()
+ *
+ * Make the first character in the string lowercase, leave all the
+ * rest the same. This is used by the Ruby module to provide backwards
+ * compatibility with the old way of naming classes and constants. For
+ * more info see the Ruby documentation.
+ *
+ * firstLower -> FirstLower
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_first_lower(String *s) {
+ String *ns = NewStringEmpty();
+ char *cs = Char(s);
+ if (cs && cs[0] != 0) {
+ Putc(tolower((int)cs[0]), ns);
+ Append(ns, cs + 1);
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_schemify()
+ *
+ * Replace underscores with dashes, to make identifiers look nice to Schemers.
+ *
+ * under_scores -> under-scores
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_schemify(String *s) {
+ String *ns = NewString(s);
+ Replaceall(ns, "_", "-");
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_typecode()
+ *
+ * Takes a string with possible type-escapes in it and replaces them with
+ * real C datatypes.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_typecode(String *s) {
+ String *ns;
+ int c;
+ String *tc;
+ ns = NewStringEmpty();
+ while ((c = Getc(s)) != EOF) {
+ if (c == '`') {
+ String *str = 0;
+ tc = NewStringEmpty();
+ while ((c = Getc(s)) != EOF) {
+ if (c == '`')
+ break;
+ Putc(c, tc);
+ }
+ str = SwigType_str(tc, 0);
+ Append(ns, str);
+ Delete(str);
+ } else {
+ Putc(c, ns);
+ if (c == '\'') {
+ while ((c = Getc(s)) != EOF) {
+ Putc(c, ns);
+ if (c == '\'')
+ break;
+ if (c == '\\') {
+ c = Getc(s);
+ Putc(c, ns);
+ }
+ }
+ } else if (c == '\"') {
+ while ((c = Getc(s)) != EOF) {
+ Putc(c, ns);
+ if (c == '\"')
+ break;
+ if (c == '\\') {
+ c = Getc(s);
+ Putc(c, ns);
+ }
+ }
+ }
+ }
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_mangle()
+ *
+ * Take a string and mangle it by stripping all non-valid C identifier
+ * characters.
+ *
+ * This routine skips unnecessary blank spaces, therefore mangling
+ * 'char *' and 'char*', 'std::pair<int, int >' and
+ * 'std::pair<int,int>', produce the same result.
+ *
+ * However, note that 'long long' and 'long_long' produce different
+ * mangled strings.
+ *
+ * The mangling method still is not 'perfect', for example std::pair and
+ * std_pair return the same mangling. This is just a little better
+ * than before, but it seems to be enough for most of the purposes.
+ *
+ * Having a perfect mangling will break some examples and code which
+ * assume, for example, that A::get_value will be mangled as
+ * A_get_value.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_mangle(const String *s) {
+#if 0
+ /* old mangling, not suitable for using in macros */
+ String *t = Copy(s);
+ char *c = Char(t);
+ while (*c) {
+ if (!isalnum(*c))
+ *c = '_';
+ c++;
+ }
+ return t;
+#else
+ String *result = NewStringEmpty();
+ int space = 0;
+ int state = 0;
+ char *pc, *cb;
+ String *b = Copy(s);
+ if (SwigType_istemplate(b)) {
+ String *st = Swig_symbol_template_deftype(b, 0);
+ String *sq = Swig_symbol_type_qualify(st, 0);
+ String *t = SwigType_namestr(sq);
+ Delete(st);
+ Delete(sq);
+ Delete(b);
+ b = t;
+ }
+ pc = cb = Char(b);
+ while (*pc) {
+ char c = *pc;
+ if (isalnum((int) c) || (c == '_')) {
+ state = 1;
+ if (space && (space == state)) {
+ Append(result, "_SS_");
+ }
+ space = 0;
+ Printf(result, "%c", (int) c);
+
+ } else {
+ if (isspace((int) c)) {
+ space = state;
+ ++pc;
+ continue;
+ } else {
+ state = 3;
+ space = 0;
+ }
+ switch (c) {
+ case '.':
+ if ((cb != pc) && (*(pc - 1) == 'p')) {
+ Append(result, "_");
+ ++pc;
+ continue;
+ } else {
+ c = 'f';
+ }
+ break;
+ case ':':
+ if (*(pc + 1) == ':') {
+ Append(result, "_");
+ ++pc;
+ ++pc;
+ continue;
+ }
+ break;
+ case '*':
+ c = 'm';
+ break;
+ case '&':
+ c = 'A';
+ break;
+ case '<':
+ c = 'l';
+ break;
+ case '>':
+ c = 'g';
+ break;
+ case '=':
+ c = 'e';
+ break;
+ case ',':
+ c = 'c';
+ break;
+ case '(':
+ c = 'p';
+ break;
+ case ')':
+ c = 'P';
+ break;
+ case '[':
+ c = 'b';
+ break;
+ case ']':
+ c = 'B';
+ break;
+ case '^':
+ c = 'x';
+ break;
+ case '|':
+ c = 'o';
+ break;
+ case '~':
+ c = 'n';
+ break;
+ case '!':
+ c = 'N';
+ break;
+ case '%':
+ c = 'M';
+ break;
+ case '?':
+ c = 'q';
+ break;
+ case '+':
+ c = 'a';
+ break;
+ case '-':
+ c = 's';
+ break;
+ case '/':
+ c = 'd';
+ break;
+ default:
+ break;
+ }
+ if (isalpha((int) c)) {
+ Printf(result, "_S%c_", (int) c);
+ } else {
+ Printf(result, "_S%02X_", (int) c);
+ }
+ }
+ ++pc;
+ }
+ Delete(b);
+ return result;
+#endif
+}
+
+String *Swig_string_emangle(String *s) {
+ return Swig_string_mangle(s);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_scopename_split()
+ *
+ * Take a qualified name like "A::B::C" and splits off the last name.
+ * In this case, returns "C" as last and "A::B" as prefix.
+ * Always returns non NULL for last, but prefix may be NULL if there is no prefix.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_scopename_split(const String *s, String **rprefix, String **rlast) {
+ char *tmp = Char(s);
+ char *c = tmp;
+ char *cc = c;
+ char *co = 0;
+ if (!strstr(c, "::")) {
+ *rprefix = 0;
+ *rlast = Copy(s);
+ }
+
+ co = strstr(cc, "operator ");
+ if (co) {
+ if (co == cc) {
+ *rprefix = 0;
+ *rlast = Copy(s);
+ return;
+ } else {
+ *rprefix = NewStringWithSize(cc, (int)(co - cc - 2));
+ *rlast = NewString(co);
+ return;
+ }
+ }
+ while (*c) {
+ if ((*c == ':') && (*(c + 1) == ':')) {
+ cc = c;
+ c += 2;
+ } else {
+ if (*c == '<') {
+ int level = 1;
+ c++;
+ while (*c && level) {
+ if (*c == '<')
+ level++;
+ if (*c == '>')
+ level--;
+ c++;
+ }
+ } else {
+ c++;
+ }
+ }
+ }
+
+ if (cc != tmp) {
+ *rprefix = NewStringWithSize(tmp, (int)(cc - tmp));
+ *rlast = NewString(cc + 2);
+ return;
+ } else {
+ *rprefix = 0;
+ *rlast = Copy(s);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_scopename_prefix()
+ *
+ * Take a qualified name like "A::B::C" and return the scope name.
+ * In this case, "A::B". Returns NULL if there is no base.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_scopename_prefix(const String *s) {
+ char *tmp = Char(s);
+ char *c = tmp;
+ char *cc = c;
+ char *co = 0;
+ if (!strstr(c, "::"))
+ return 0;
+ co = strstr(cc, "operator ");
+
+ if (co) {
+ if (co == cc) {
+ return 0;
+ } else {
+ String *prefix = NewStringWithSize(cc, (int)(co - cc - 2));
+ return prefix;
+ }
+ }
+ while (*c) {
+ if ((*c == ':') && (*(c + 1) == ':')) {
+ cc = c;
+ c += 2;
+ } else {
+ if (*c == '<') {
+ int level = 1;
+ c++;
+ while (*c && level) {
+ if (*c == '<')
+ level++;
+ if (*c == '>')
+ level--;
+ c++;
+ }
+ } else {
+ c++;
+ }
+ }
+ }
+
+ if (cc != tmp) {
+ return NewStringWithSize(tmp, (int)(cc - tmp));
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_scopename_last()
+ *
+ * Take a qualified name like "A::B::C" and returns the last. In this
+ * case, "C".
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_scopename_last(const String *s) {
+ char *tmp = Char(s);
+ char *c = tmp;
+ char *cc = c;
+ char *co = 0;
+ if (!strstr(c, "::"))
+ return NewString(s);
+
+ co = strstr(cc, "operator ");
+ if (co) {
+ return NewString(co);
+ }
+
+
+ while (*c) {
+ if ((*c == ':') && (*(c + 1) == ':')) {
+ c += 2;
+ cc = c;
+ } else {
+ if (*c == '<') {
+ int level = 1;
+ c++;
+ while (*c && level) {
+ if (*c == '<')
+ level++;
+ if (*c == '>')
+ level--;
+ c++;
+ }
+ } else {
+ c++;
+ }
+ }
+ }
+ return NewString(cc);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_scopename_first()
+ *
+ * Take a qualified name like "A::B::C" and returns the first scope name.
+ * In this case, "A". Returns NULL if there is no base.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_scopename_first(const String *s) {
+ char *tmp = Char(s);
+ char *c = tmp;
+ char *co = 0;
+ if (!strstr(c, "::"))
+ return 0;
+
+ co = strstr(c, "operator ");
+ if (co) {
+ if (co == c) {
+ return 0;
+ }
+ } else {
+ co = c + Len(s);
+ }
+
+ while (*c && (c != co)) {
+ if ((*c == ':') && (*(c + 1) == ':')) {
+ break;
+ } else {
+ if (*c == '<') {
+ int level = 1;
+ c++;
+ while (*c && level) {
+ if (*c == '<')
+ level++;
+ if (*c == '>')
+ level--;
+ c++;
+ }
+ } else {
+ c++;
+ }
+ }
+ }
+ if (*c && (c != tmp)) {
+ return NewStringWithSize(tmp, (int)(c - tmp));
+ } else {
+ return 0;
+ }
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_scopename_suffix()
+ *
+ * Take a qualified name like "A::B::C" and returns the suffix.
+ * In this case, "B::C". Returns NULL if there is no suffix.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_scopename_suffix(const String *s) {
+ char *tmp = Char(s);
+ char *c = tmp;
+ char *co = 0;
+ if (!strstr(c, "::"))
+ return 0;
+
+ co = strstr(c, "operator ");
+ if (co) {
+ if (co == c)
+ return 0;
+ }
+ while (*c) {
+ if ((*c == ':') && (*(c + 1) == ':')) {
+ break;
+ } else {
+ if (*c == '<') {
+ int level = 1;
+ c++;
+ while (*c && level) {
+ if (*c == '<')
+ level++;
+ if (*c == '>')
+ level--;
+ c++;
+ }
+ } else {
+ c++;
+ }
+ }
+ }
+ if (*c && (c != tmp)) {
+ return NewString(c + 2);
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_scopename_tolist()
+ *
+ * Take a qualified scope name like "A::B::C" and convert it to a list.
+ * In this case, return a list of 3 elements "A", "B", "C".
+ * Returns an empty list if the input is empty.
+ * ----------------------------------------------------------------------------- */
+
+List *Swig_scopename_tolist(const String *s) {
+ List *scopes = NewList();
+ String *name = Len(s) == 0 ? 0 : NewString(s);
+
+ while (name) {
+ String *last = 0;
+ String *prefix = 0;
+ Swig_scopename_split(name, &prefix, &last);
+ Insert(scopes, 0, last);
+ Delete(last);
+ Delete(name);
+ name = prefix;
+ }
+ Delete(name);
+ return scopes;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_scopename_check()
+ *
+ * Checks to see if a name is qualified with a scope name, examples:
+ * foo -> 0
+ * ::foo -> 1
+ * foo::bar -> 1
+ * foo< ::bar > -> 0
+ * ----------------------------------------------------------------------------- */
+
+int Swig_scopename_check(const String *s) {
+ char *c = Char(s);
+ char *co = strstr(c, "operator ");
+
+ if (co) {
+ if (co == c)
+ return 0;
+ }
+ if (!strstr(c, "::"))
+ return 0;
+ while (*c) {
+ if ((*c == ':') && (*(c + 1) == ':')) {
+ return 1;
+ } else {
+ if (*c == '<') {
+ int level = 1;
+ c++;
+ while (*c && level) {
+ if (*c == '<')
+ level++;
+ if (*c == '>')
+ level--;
+ c++;
+ }
+ } else {
+ c++;
+ }
+ }
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_command()
+ *
+ * Feature removed in SWIG 4.1.0.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_command(String *s) {
+ Swig_error("SWIG", Getline(s), "Command encoder no longer supported - use regex encoder instead.\n");
+ Exit(EXIT_FAILURE);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_strip()
+ *
+ * Strip given prefix from identifiers
+ *
+ * Printf(stderr,"%(strip:[wx])s","wxHello") -> Hello
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_strip(String *s) {
+ String *ns;
+ if (!Len(s)) {
+ ns = NewString(s);
+ } else {
+ const char *cs = Char(s);
+ const char *ce = Strchr(cs, ']');
+ if (*cs != '[' || !ce) {
+ ns = NewString(s);
+ } else {
+ String *fmt = NewStringf("%%.%ds", ce-cs-1);
+ String *prefix = NewStringf(fmt, cs+1);
+ if (0 == Strncmp(ce+1, prefix, Len(prefix))) {
+ ns = NewString(ce+1+Len(prefix));
+ } else {
+ ns = NewString(ce+1);
+ }
+ }
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_rstrip()
+ *
+ * Strip given suffix from identifiers
+ *
+ * Printf(stderr,"%(rstrip:[Cls])s","HelloCls") -> Hello
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_string_rstrip(String *s) {
+ String *ns;
+ int len = Len(s);
+ if (!len) {
+ ns = NewString(s);
+ } else {
+ const char *cs = Char(s);
+ const char *ce = Strchr(cs, ']');
+ if (*cs != '[' || !ce) {
+ ns = NewString(s);
+ } else {
+ String *fmt = NewStringf("%%.%ds", ce-cs-1);
+ String *suffix = NewStringf(fmt, cs+1);
+ int suffix_len = Len(suffix);
+ if (0 == Strncmp(cs+len-suffix_len, suffix, suffix_len)) {
+ int copy_len = len-suffix_len-(int)(ce+1-cs);
+ ns = NewStringWithSize(ce+1, copy_len);
+ } else {
+ ns = NewString(ce+1);
+ }
+ }
+ }
+ return ns;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_offset_string()
+ *
+ * Insert number tabs before each new line in s
+ * ----------------------------------------------------------------------------- */
+
+void Swig_offset_string(String *s, int number) {
+ char *res, *p, *end, *start;
+ /* count a number of lines in s */
+ int lines = 1;
+ int len = Len(s);
+ if (len == 0)
+ return;
+ start = strchr(Char(s), '\n');
+ while (start) {
+ ++lines;
+ start = strchr(start + 1, '\n');
+ }
+ /* do not count pending new line */
+ if ((Char(s))[len-1] == '\n')
+ --lines;
+ /* allocate a temporary storage for a padded string */
+ res = (char*)Malloc(len + lines * number * 2 + 1);
+ res[len + lines * number * 2] = 0;
+
+ /* copy lines to res, prepending tabs to each line */
+ p = res; /* output pointer */
+ start = Char(s); /* start of a current line */
+ end = strchr(start, '\n'); /* end of a current line */
+ while (end) {
+ memset(p, ' ', number*2);
+ p += number*2;
+ memcpy(p, start, end - start + 1);
+ p += end - start + 1;
+ start = end + 1;
+ end = strchr(start, '\n');
+ }
+ /* process the last line */
+ if (*start) {
+ memset(p, ' ', number*2);
+ p += number*2;
+ strcpy(p, start);
+ }
+ /* replace 's' contents with 'res' */
+ Clear(s);
+ Append(s, res);
+ Free(res);
+}
+
+
+#ifdef HAVE_PCRE
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
+
+static int split_regex_pattern_subst(String *s, String **pattern, String **subst, const char **input)
+{
+ const char *pats, *pate;
+ const char *subs, *sube;
+
+ /* Locate the search pattern */
+ const char *p = Char(s);
+ if (*p++ != '/') goto err_out;
+ pats = p;
+ p = strchr(p, '/');
+ if (!p) goto err_out;
+ pate = p;
+
+ /* Locate the substitution string */
+ subs = ++p;
+ p = strchr(p, '/');
+ if (!p) goto err_out;
+ sube = p;
+
+ *pattern = NewStringWithSize(pats, (int)(pate - pats));
+ *subst = NewStringWithSize(subs, (int)(sube - subs));
+ *input = p + 1;
+ return 1;
+
+err_out:
+ Swig_error("SWIG", Getline(s), "Invalid regex substitution: '%s'.\n", s);
+ Exit(EXIT_FAILURE);
+ return 0;
+}
+
+/* This function copies len characters from src to dst, possibly applying case conversions to them: if convertCase is 1, to upper case and if it is -1, to lower
+ * case. If convertNextOnly is 1, only a single character is converted (and convertCase is reset), otherwise all of them are. */
+static void copy_with_maybe_case_conversion(String *dst, const char *src, int len, int *convertCase, int convertNextOnly)
+{
+ /* Deal with the trivial cases first. */
+ if (!len)
+ return;
+
+ if (!*convertCase) {
+ Write(dst, src, len);
+ return;
+ }
+
+ /* If we must convert only the first character, do it and write the rest at once. */
+ if (convertNextOnly) {
+ int src_char = *src;
+ Putc(*convertCase == 1 ? toupper(src_char) : tolower(src_char), dst);
+ *convertCase = 0;
+ if (len > 1) {
+ Write(dst, src + 1, len - 1);
+ }
+ } else {
+ /* We need to convert all characters. */
+ int i;
+ for (i = 0; i < len; i++, src++) {
+ int src_char = *src;
+ Putc(*convertCase == 1 ? toupper(src_char) : tolower(src_char), dst);
+ }
+ }
+}
+
+String *replace_captures(int num_captures, const char *input, String *subst, size_t captures[], String *pattern, String *s)
+{
+ int convertCase = 0, convertNextOnly = 0;
+ String *result = NewStringEmpty();
+ const char *p = Char(subst);
+
+ while (*p) {
+ /* Copy part without substitutions */
+ const char *q = strchr(p, '\\');
+ if (!q) {
+ copy_with_maybe_case_conversion(result, p, (int)strlen(p), &convertCase, convertNextOnly);
+ break;
+ }
+ copy_with_maybe_case_conversion(result, p, (int)(q - p), &convertCase, convertNextOnly);
+ p = q + 1;
+
+ /* Handle substitution */
+ if (*p == '\0') {
+ Putc('\\', result);
+ } else if (isdigit((unsigned char)*p)) {
+ int group = *p++ - '0';
+ if (group < num_captures) {
+ int l = (int)captures[group*2], r = (int)captures[group*2 + 1];
+ if (l != -1) {
+ copy_with_maybe_case_conversion(result, input + l, r - l, &convertCase, convertNextOnly);
+ }
+ } else {
+ Swig_error("SWIG", Getline(s), "PCRE capture replacement failed while matching \"%s\" using \"%s\" - request for group %d is greater than the number of captures %d.\n",
+ Char(pattern), input, group, num_captures-1);
+ }
+ } else {
+ /* Handle Perl-like case conversion escapes. */
+ switch (*p) {
+ case 'u':
+ convertCase = 1;
+ convertNextOnly = 1;
+ break;
+ case 'U':
+ convertCase = 1;
+ convertNextOnly = 0;
+ break;
+ case 'l':
+ convertCase = -1;
+ convertNextOnly = 1;
+ break;
+ case 'L':
+ convertCase = -1;
+ convertNextOnly = 0;
+ break;
+ case 'E':
+ convertCase = 0;
+ break;
+ default:
+ Swig_error("SWIG", Getline(s), "Unrecognized escape character '%c' in the replacement string \"%s\".\n",
+ *p, Char(subst));
+ }
+ p++;
+ }
+ }
+
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_string_regex()
+ *
+ * Executes a regular expression substitution. For example:
+ *
+ * Printf(stderr,"gsl%(regex:/GSL_(.*)_/\\1/)s", "GSL_Hello_") -> gslHello
+ * ----------------------------------------------------------------------------- */
+String *Swig_string_regex(String *s) {
+ const int pcre_options = 0;
+
+ String *res = 0;
+ pcre2_code *compiled_pat = 0;
+ const char *input;
+ PCRE2_UCHAR pcre_error[256];
+ int pcre_errornum;
+ size_t pcre_errorpos;
+ String *pattern = 0, *subst = 0;
+ size_t *captures = 0;
+ pcre2_match_data *match_data = 0;
+ if (split_regex_pattern_subst(s, &pattern, &subst, &input)) {
+ int rc;
+
+ compiled_pat = pcre2_compile(
+ (PCRE2_SPTR8)Char(pattern), PCRE2_ZERO_TERMINATED, pcre_options, &pcre_errornum, &pcre_errorpos, NULL);
+ if (!compiled_pat) {
+ pcre2_get_error_message (pcre_errornum, pcre_error, sizeof pcre_error);
+ Swig_error("SWIG", Getline(s), "PCRE compilation failed: '%s' in '%s':%i.\n",
+ pcre_error, Char(pattern), pcre_errorpos);
+ Exit(EXIT_FAILURE);
+ }
+ match_data = pcre2_match_data_create_from_pattern (compiled_pat, NULL);
+ rc = pcre2_match(compiled_pat, (PCRE2_SPTR8)input, PCRE2_ZERO_TERMINATED, 0, 0, match_data, NULL);
+ captures = pcre2_get_ovector_pointer (match_data);
+ if (rc >= 0) {
+ res = replace_captures(rc, input, subst, captures, pattern, s);
+ } else if (rc != PCRE2_ERROR_NOMATCH) {
+ Swig_error("SWIG", Getline(s), "PCRE execution failed: error %d while matching \"%s\" using \"%s\".\n",
+ rc, Char(pattern), input);
+ Exit(EXIT_FAILURE);
+ }
+ }
+
+ DohDelete(pattern);
+ DohDelete(subst);
+ pcre2_code_free(compiled_pat);
+ pcre2_match_data_free(match_data);
+ return res ? res : NewStringEmpty();
+}
+
+String *Swig_pcre_version(void) {
+ int len = pcre2_config(PCRE2_CONFIG_VERSION, NULL);
+ char *buf = Malloc(len);
+ String *result;
+ pcre2_config(PCRE2_CONFIG_VERSION, buf);
+ result = NewStringf("PCRE2 Version: %s", buf);
+ Free(buf);
+ return result;
+}
+
+#else
+
+String *Swig_string_regex(String *s) {
+ Swig_error("SWIG", Getline(s), "PCRE regex support not enabled in this SWIG build.\n");
+ Exit(EXIT_FAILURE);
+ return 0;
+}
+
+String *Swig_pcre_version(void) {
+ return NewStringf("PCRE not used");
+}
+
+#endif
+
+/* ------------------------------------------------------------
+ * Swig_is_generated_overload()
+ * Check if the function is an automatically generated
+ * overload created because a method has default parameters.
+ * ------------------------------------------------------------ */
+int Swig_is_generated_overload(Node *n) {
+ Node *base_method = Getattr(n, "sym:overloaded");
+ Node *default_args = Getattr(n, "defaultargs");
+ return ((base_method != NULL) && (default_args != NULL) && (base_method == default_args));
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_init()
+ *
+ * Initialize the SWIG core
+ * ----------------------------------------------------------------------------- */
+
+void Swig_init(void) {
+ /* Set some useful string encoding methods */
+ DohEncoding("escape", Swig_string_escape);
+ DohEncoding("hexescape", Swig_string_hexescape);
+ DohEncoding("upper", Swig_string_upper);
+ DohEncoding("lower", Swig_string_lower);
+ DohEncoding("title", Swig_string_title);
+ DohEncoding("ctitle", Swig_string_ccase);
+ DohEncoding("lctitle", Swig_string_lccase);
+ DohEncoding("utitle", Swig_string_ucase);
+ DohEncoding("typecode", Swig_string_typecode);
+ DohEncoding("mangle", Swig_string_emangle);
+ DohEncoding("command", Swig_string_command);
+ DohEncoding("schemify", Swig_string_schemify);
+ DohEncoding("strip", Swig_string_strip);
+ DohEncoding("rstrip", Swig_string_rstrip);
+ DohEncoding("regex", Swig_string_regex);
+
+ /* aliases for the case encoders */
+ DohEncoding("uppercase", Swig_string_upper);
+ DohEncoding("lowercase", Swig_string_lower);
+ DohEncoding("camelcase", Swig_string_ccase);
+ DohEncoding("lowercamelcase", Swig_string_lccase);
+ DohEncoding("undercase", Swig_string_ucase);
+ DohEncoding("firstuppercase", Swig_string_first_upper);
+ DohEncoding("firstlowercase", Swig_string_first_lower);
+
+ /* Initialize typemaps */
+ Swig_typemap_init();
+
+ /* Initialize symbol table */
+ Swig_symbol_init();
+
+ /* Initialize type system */
+ SwigType_typesystem_init();
+
+ /* Initialize template system */
+ SwigType_template_init();
+}
diff --git a/contrib/tools/swig/Source/Swig/naming.c b/contrib/tools/swig/Source/Swig/naming.c
new file mode 100644
index 0000000000..c4613f6c62
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/naming.c
@@ -0,0 +1,1771 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * naming.c
+ *
+ * Functions for generating various kinds of names during code generation.
+ *
+ * Swig_name_register is used to register a format string for generating names.
+ * The format string makes use of the following format specifiers:
+ *
+ * %c - class name is substituted
+ * %f - function name is substituted
+ * %m - member name is substituted
+ * %n - namespace is substituted
+ * %v - variable name is substituted
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+#include <ctype.h>
+
+/* Hash table containing naming data */
+
+static Hash *naming_hash = 0;
+
+#if 0
+#define SWIG_DEBUG
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_register()
+ *
+ * Register a new naming format.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_name_register(const_String_or_char_ptr method, const_String_or_char_ptr format) {
+ if (!naming_hash)
+ naming_hash = NewHash();
+ Setattr(naming_hash, method, format);
+}
+
+void Swig_name_unregister(const_String_or_char_ptr method) {
+ if (naming_hash) {
+ Delattr(naming_hash, method);
+ }
+}
+
+/* Return naming format for the specified method or the default format if none was explicitly registered */
+static String* get_naming_format_for(const char *method, const char *def_format) {
+ String* f = naming_hash ? Getattr(naming_hash, method) : NULL;
+
+ return f ? Copy(f) : NewString(def_format);
+}
+
+static int name_mangle(String *r) {
+ char *c;
+ int special;
+ special = 0;
+ Replaceall(r, "::", "_");
+ c = Char(r);
+ while (*c) {
+ if (!isalnum((int) *c) && (*c != '_')) {
+ special = 1;
+ switch (*c) {
+ case '+':
+ *c = 'a';
+ break;
+ case '-':
+ *c = 's';
+ break;
+ case '*':
+ *c = 'm';
+ break;
+ case '/':
+ *c = 'd';
+ break;
+ case '<':
+ *c = 'l';
+ break;
+ case '>':
+ *c = 'g';
+ break;
+ case '=':
+ *c = 'e';
+ break;
+ case ',':
+ *c = 'c';
+ break;
+ case '(':
+ *c = 'p';
+ break;
+ case ')':
+ *c = 'P';
+ break;
+ case '[':
+ *c = 'b';
+ break;
+ case ']':
+ *c = 'B';
+ break;
+ case '^':
+ *c = 'x';
+ break;
+ case '&':
+ *c = 'A';
+ break;
+ case '|':
+ *c = 'o';
+ break;
+ case '~':
+ *c = 'n';
+ break;
+ case '!':
+ *c = 'N';
+ break;
+ case '%':
+ *c = 'M';
+ break;
+ case '.':
+ *c = 'f';
+ break;
+ case '?':
+ *c = 'q';
+ break;
+ default:
+ *c = '_';
+ break;
+ }
+ }
+ c++;
+ }
+ if (special)
+ Append(r, "___");
+ return special;
+}
+
+/* -----------------------------------------------------------------------------
+ * replace_nspace()
+ *
+ * Mangles in the namespace from nspace by replacing %n in name if nspace feature required.
+ * ----------------------------------------------------------------------------- */
+
+static void replace_nspace(String *name, const_String_or_char_ptr nspace) {
+ if (nspace) {
+ String *namspace = NewStringf("%s_", nspace);
+ Replaceall(namspace, NSPACE_SEPARATOR, "_");
+ Replace(name, "%n", namspace, DOH_REPLACE_ANY);
+ Delete(namspace);
+ } else {
+ Replace(name, "%n", "", DOH_REPLACE_ANY);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_mangle()
+ *
+ * Converts all of the non-identifier characters of a string to underscores.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_mangle(const_String_or_char_ptr s) {
+#if 0
+ String *r = NewString(s);
+ name_mangle(r);
+ return r;
+#else
+ return Swig_string_mangle(s);
+#endif
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_wrapper()
+ *
+ * Returns the name of a wrapper function.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_wrapper(const_String_or_char_ptr fname) {
+ String *r = get_naming_format_for("wrapper", "_wrap_%f");
+
+ Replace(r, "%f", fname, DOH_REPLACE_ANY);
+ name_mangle(r);
+ return r;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_member()
+ *
+ * Returns the name of a class method.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_member(const_String_or_char_ptr nspace, const_String_or_char_ptr classname, const_String_or_char_ptr membername) {
+ String *r;
+ String *rclassname;
+ char *cname;
+
+ rclassname = SwigType_namestr(classname);
+ r = get_naming_format_for("member", "%n%c_%m");
+ cname = Char(rclassname);
+ if ((strncmp(cname, "struct ", 7) == 0) || ((strncmp(cname, "class ", 6) == 0)) || ((strncmp(cname, "union ", 6) == 0))) {
+ cname = strchr(cname, ' ') + 1;
+ }
+ replace_nspace(r, nspace);
+ Replace(r, "%c", cname, DOH_REPLACE_ANY);
+ Replace(r, "%m", membername, DOH_REPLACE_ANY);
+ /* name_mangle(r); */
+ Delete(rclassname);
+ return r;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_get()
+ *
+ * Returns the name of the accessor function used to get a variable.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_get(const_String_or_char_ptr nspace, const_String_or_char_ptr vname) {
+ String *r = get_naming_format_for("get", "%n%v_get");
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_name_get: '%s'\n", vname);
+#endif
+
+ replace_nspace(r, nspace);
+ Replace(r, "%v", vname, DOH_REPLACE_ANY);
+ /* name_mangle(r); */
+ return r;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_set()
+ *
+ * Returns the name of the accessor function used to set a variable.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_set(const_String_or_char_ptr nspace, const_String_or_char_ptr vname) {
+ String *r = get_naming_format_for("set", "%n%v_set");
+
+ replace_nspace(r, nspace);
+ Replace(r, "%v", vname, DOH_REPLACE_ANY);
+ /* name_mangle(r); */
+ return r;
+}
+
+/* Common implementation of all Swig_name_<special-method>() functions below. */
+static String *make_full_name_for(const char *method, const char *def_format, const_String_or_char_ptr nspace, const_String_or_char_ptr classname) {
+ String *r;
+ String *rclassname;
+ char *cname;
+
+ rclassname = SwigType_namestr(classname);
+ r = get_naming_format_for(method, def_format);
+
+ cname = Char(rclassname);
+ if ((strncmp(cname, "struct ", 7) == 0) || ((strncmp(cname, "class ", 6) == 0)) || ((strncmp(cname, "union ", 6) == 0))) {
+ cname = strchr(cname, ' ') + 1;
+ }
+
+ replace_nspace(r, nspace);
+ Replace(r, "%c", cname, DOH_REPLACE_ANY);
+ Delete(rclassname);
+ return r;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_construct()
+ *
+ * Returns the name of the accessor function used to create an object.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_construct(const_String_or_char_ptr nspace, const_String_or_char_ptr classname) {
+ return make_full_name_for("construct", "new_%n%c", nspace, classname);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_copyconstructor()
+ *
+ * Returns the name of the accessor function used to copy an object.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_copyconstructor(const_String_or_char_ptr nspace, const_String_or_char_ptr classname) {
+ return make_full_name_for("copy", "copy_%n%c", nspace, classname);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_destroy()
+ *
+ * Returns the name of the accessor function used to destroy an object.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_destroy(const_String_or_char_ptr nspace, const_String_or_char_ptr classname) {
+ return make_full_name_for("destroy", "delete_%n%c", nspace, classname);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_disown()
+ *
+ * Returns the name of the accessor function used to disown an object.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_disown(const_String_or_char_ptr nspace, const_String_or_char_ptr classname) {
+ return make_full_name_for("disown", "disown_%n%c", nspace, classname);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_object_set()
+ *
+ * Sets an object associated with a name and optional declarators.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_name_object_set(Hash *namehash, String *name, SwigType *decl, DOH *object) {
+ DOH *n;
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_name_object_set: '%s', '%s'\n", name, decl);
+#endif
+ n = Getattr(namehash, name);
+ if (!n) {
+ n = NewHash();
+ Setattr(namehash, name, n);
+ Delete(n);
+ }
+ /* Add an object based on the declarator value */
+ if (!decl) {
+ Setattr(n, "start", object);
+ } else {
+ SwigType *cd = Copy(decl);
+ Setattr(n, cd, object);
+ Delete(cd);
+ }
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_object_get()
+ *
+ * Return an object associated with an optional class prefix, name, and
+ * declarator. This function operates according to name matching rules
+ * described for the %rename directive in the SWIG manual.
+ * ----------------------------------------------------------------------------- */
+
+static DOH *get_object(Hash *n, String *decl) {
+ DOH *rn = 0;
+ if (!n)
+ return 0;
+ if (decl) {
+ rn = Getattr(n, decl);
+ } else {
+ rn = Getattr(n, "start");
+ }
+ return rn;
+}
+
+static DOH *name_object_get(Hash *namehash, String *tname, SwigType *decl, SwigType *ncdecl) {
+ DOH *rn = 0;
+ Hash *n = Getattr(namehash, tname);
+ if (n) {
+ rn = get_object(n, decl);
+ if ((!rn) && ncdecl)
+ rn = get_object(n, ncdecl);
+ if (!rn)
+ rn = get_object(n, 0);
+ }
+ return rn;
+}
+
+DOH *Swig_name_object_get(Hash *namehash, String *prefix, String *name, SwigType *decl) {
+ String *tname = NewStringEmpty();
+ DOH *rn = 0;
+ char *ncdecl = 0;
+
+ if (!namehash)
+ return 0;
+
+ /* DB: This removed to more tightly control feature/name matching */
+ /* if ((decl) && (SwigType_isqualifier(decl))) {
+ ncdecl = strchr(Char(decl),'.');
+ ncdecl++;
+ }
+ */
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_name_object_get: '%s' '%s', '%s'\n", prefix, name, decl);
+#endif
+
+
+ /* Perform a class-based lookup (if class prefix supplied) */
+ if (prefix) {
+ if (Len(prefix)) {
+ Printf(tname, "%s::%s", prefix, name);
+ rn = name_object_get(namehash, tname, decl, ncdecl);
+ if (!rn) {
+ String *cls = Swig_scopename_last(prefix);
+ if (!Equal(cls, prefix)) {
+ Clear(tname);
+ Printf(tname, "*::%s::%s", cls, name);
+ rn = name_object_get(namehash, tname, decl, ncdecl);
+ }
+ Delete(cls);
+ }
+ /* Lookup a name within a templated-based class */
+ if (!rn) {
+ String *t_name = SwigType_istemplate_templateprefix(prefix);
+ if (t_name) {
+ Clear(tname);
+ Printf(tname, "%s::%s", t_name, name);
+ rn = name_object_get(namehash, tname, decl, ncdecl);
+ Delete(t_name);
+ }
+ }
+ /* Lookup a template-based name within a class */
+ if (!rn) {
+ String *t_name = SwigType_istemplate_templateprefix(name);
+ if (t_name)
+ rn = Swig_name_object_get(namehash, prefix, t_name, decl);
+ Delete(t_name);
+ }
+ }
+ /* A wildcard-based class lookup */
+ if (!rn) {
+ Clear(tname);
+ Printf(tname, "*::%s", name);
+ rn = name_object_get(namehash, tname, decl, ncdecl);
+ }
+ } else {
+ /* Lookup in the global namespace only */
+ Clear(tname);
+ Printf(tname, "::%s", name);
+ rn = name_object_get(namehash, tname, decl, ncdecl);
+ }
+ /* Catch-all */
+ if (!rn) {
+ rn = name_object_get(namehash, name, decl, ncdecl);
+ }
+ if (!rn && Swig_scopename_check(name)) {
+ String *nprefix = 0;
+ String *nlast = 0;
+ Swig_scopename_split(name, &nprefix, &nlast);
+ rn = name_object_get(namehash, nlast, decl, ncdecl);
+ Delete(nlast);
+ Delete(nprefix);
+ }
+
+ Delete(tname);
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_name_object_get: found %d\n", rn ? 1 : 0);
+#endif
+
+ return rn;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_object_inherit()
+ *
+ * Implements name-based inheritance scheme.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_name_object_inherit(Hash *namehash, String *base, String *derived) {
+ Iterator ki;
+ Hash *derh;
+ String *bprefix;
+ String *dprefix;
+ char *cbprefix;
+ int plen;
+
+ if (!namehash)
+ return;
+
+ /* Temporary hash holding all the entries we add while we iterate over
+ namehash itself as we can't modify the latter while iterating over it. */
+ derh = NULL;
+ bprefix = NewStringf("%s::", base);
+ dprefix = NewStringf("%s::", derived);
+ cbprefix = Char(bprefix);
+ plen = (int)strlen(cbprefix);
+ for (ki = First(namehash); ki.key; ki = Next(ki)) {
+ char *k = Char(ki.key);
+ if (strncmp(k, cbprefix, plen) == 0) {
+ /* Copy, adjusting name, this element to the derived hash. */
+ Iterator oi;
+ String *nkey = NewStringf("%s%s", dprefix, k + plen);
+ Hash *n = ki.item;
+ Hash *newh;
+
+ /* Don't overwrite an existing value for the derived class, if any. */
+ newh = Getattr(namehash, nkey);
+ if (!newh) {
+ if (!derh)
+ derh = NewHash();
+
+ newh = NewHash();
+ Setattr(derh, nkey, newh);
+ Delete(newh);
+ }
+ for (oi = First(n); oi.key; oi = Next(oi)) {
+ if (!Getattr(newh, oi.key)) {
+ String *ci = Copy(oi.item);
+ Setattr(newh, oi.key, ci);
+ Delete(ci);
+ }
+ }
+ Delete(nkey);
+ }
+ }
+
+ /* Merge the contents of derived hash into the main hash. */
+ if (derh) {
+ for (ki = First(derh); ki.key; ki = Next(ki)) {
+ Setattr(namehash, ki.key, ki.item);
+ }
+ }
+
+ Delete(bprefix);
+ Delete(dprefix);
+ Delete(derh);
+}
+
+/* -----------------------------------------------------------------------------
+ * merge_features()
+ *
+ * Given a hash, this function merges the features in the hash into the node.
+ * ----------------------------------------------------------------------------- */
+
+static void merge_features(Hash *features, Node *n) {
+ Iterator ki;
+
+ if (!features)
+ return;
+ for (ki = First(features); ki.key; ki = Next(ki)) {
+ String *ci = Copy(ki.item);
+ Setattr(n, ki.key, ci);
+ Delete(ci);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_features_get()
+ *
+ * Attaches any features in the features hash to the node that matches
+ * the declaration, decl.
+ * ----------------------------------------------------------------------------- */
+
+static void features_get(Hash *features, const String *tname, SwigType *decl, SwigType *ncdecl, Node *node) {
+ Node *n = Getattr(features, tname);
+#ifdef SWIG_DEBUG
+ Printf(stdout, " features_get: %s\n", tname);
+#endif
+ if (n) {
+ merge_features(get_object(n, 0), node);
+ if (ncdecl)
+ merge_features(get_object(n, ncdecl), node);
+ merge_features(get_object(n, decl), node);
+ }
+}
+
+void Swig_features_get(Hash *features, String *prefix, String *name, SwigType *decl, Node *node) {
+ char *ncdecl = 0;
+ String *rdecl = 0;
+ String *rname = 0;
+ if (!features)
+ return;
+
+ /* MM: This removed to more tightly control feature/name matching */
+ /*
+ if ((decl) && (SwigType_isqualifier(decl))) {
+ ncdecl = strchr(Char(decl),'.');
+ ncdecl++;
+ }
+ */
+
+ /* very specific hack for template constructors/destructors */
+ if (name && SwigType_istemplate(name)) {
+ String *nodetype = nodeType(node);
+ if (nodetype && (Equal(nodetype, "constructor") || Equal(nodetype, "destructor"))) {
+ String *nprefix = 0;
+ String *nlast = 0;
+ String *tprefix;
+ Swig_scopename_split(name, &nprefix, &nlast);
+ tprefix = SwigType_templateprefix(nlast);
+ Delete(nlast);
+ if (Len(nprefix)) {
+ Append(nprefix, "::");
+ Append(nprefix, tprefix);
+ Delete(tprefix);
+ rname = nprefix;
+ } else {
+ rname = tprefix;
+ Delete(nprefix);
+ }
+ rdecl = Copy(decl);
+ Replaceall(rdecl, name, rname);
+ decl = rdecl;
+ name = rname;
+ }
+ }
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_features_get: '%s' '%s' '%s'\n", prefix, name, decl);
+#endif
+
+ /* Global features */
+ features_get(features, "", 0, 0, node);
+ if (name) {
+ String *tname = NewStringEmpty();
+ /* add features for 'root' template */
+ String *dname = SwigType_istemplate_templateprefix(name);
+ if (dname) {
+ features_get(features, dname, decl, ncdecl, node);
+ }
+ /* Catch-all */
+ features_get(features, name, decl, ncdecl, node);
+ /* Perform a class-based lookup (if class prefix supplied) */
+ if (prefix) {
+ /* A class-generic feature */
+ if (Len(prefix)) {
+ Printf(tname, "%s::", prefix);
+ features_get(features, tname, decl, ncdecl, node);
+ }
+ /* A wildcard-based class lookup */
+ Clear(tname);
+ Printf(tname, "*::%s", name);
+ features_get(features, tname, decl, ncdecl, node);
+ /* A specific class lookup */
+ if (Len(prefix)) {
+ /* A template-based class lookup */
+ String *tprefix = SwigType_istemplate_templateprefix(prefix);
+ if (tprefix) {
+ Clear(tname);
+ Printf(tname, "%s::%s", tprefix, name);
+ features_get(features, tname, decl, ncdecl, node);
+ }
+ Clear(tname);
+ Printf(tname, "%s::%s", prefix, name);
+ features_get(features, tname, decl, ncdecl, node);
+ Delete(tprefix);
+ }
+ } else {
+ /* Lookup in the global namespace only */
+ Clear(tname);
+ Printf(tname, "::%s", name);
+ features_get(features, tname, decl, ncdecl, node);
+ }
+ Delete(tname);
+ Delete(dname);
+ }
+ if (name && SwigType_istemplate(name)) {
+ /* add features for complete template type */
+ String *dname = Swig_symbol_template_deftype(name, 0);
+ if (!Equal(dname, name)) {
+ Swig_features_get(features, prefix, dname, decl, node);
+ }
+ Delete(dname);
+ }
+
+ if (rname)
+ Delete(rname);
+ if (rdecl)
+ Delete(rdecl);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_feature_set()
+ *
+ * Sets a feature name and value. Also sets optional feature attributes as
+ * passed in by featureattribs. Optional feature attributes are given a full name
+ * concatenating the feature name plus ':' plus the attribute name.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_feature_set(Hash *features, const_String_or_char_ptr name, SwigType *decl, const_String_or_char_ptr featurename, const_String_or_char_ptr value, Hash *featureattribs) {
+ Hash *n;
+ Hash *fhash;
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_feature_set: '%s' '%s' '%s' '%s'\n", name, decl, featurename, value);
+#endif
+
+ n = Getattr(features, name);
+ if (!n) {
+ n = NewHash();
+ Setattr(features, name, n);
+ Delete(n);
+ }
+ if (!decl) {
+ fhash = Getattr(n, "start");
+ if (!fhash) {
+ fhash = NewHash();
+ Setattr(n, "start", fhash);
+ Delete(fhash);
+ }
+ } else {
+ fhash = Getattr(n, decl);
+ if (!fhash) {
+ String *cdecl_ = Copy(decl);
+ fhash = NewHash();
+ Setattr(n, cdecl_, fhash);
+ Delete(cdecl_);
+ Delete(fhash);
+ }
+ }
+ if (value) {
+ Setattr(fhash, featurename, value);
+ } else {
+ Delattr(fhash, featurename);
+ }
+
+ {
+ /* Add in the optional feature attributes */
+ Hash *attribs = featureattribs;
+ while (attribs) {
+ String *attribname = Getattr(attribs, "name");
+ String *featureattribname = NewStringf("%s:%s", featurename, attribname);
+ if (value) {
+ String *attribvalue = Getattr(attribs, "value");
+ Setattr(fhash, featureattribname, attribvalue);
+ } else {
+ Delattr(fhash, featureattribname);
+ }
+ attribs = nextSibling(attribs);
+ Delete(featureattribname);
+ }
+ }
+
+ if (name && SwigType_istemplate(name)) {
+ String *dname = Swig_symbol_template_deftype(name, 0);
+ if (Strcmp(dname, name)) {
+ Swig_feature_set(features, dname, decl, featurename, value, featureattribs);
+ }
+ Delete(dname);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * The rename/namewarn engine
+ *
+ * Code below was in parser.y for a while
+ * ----------------------------------------------------------------------------- */
+
+static Hash *namewarn_hash = 0;
+static Hash *name_namewarn_hash(void) {
+ if (!namewarn_hash)
+ namewarn_hash = NewHash();
+ return namewarn_hash;
+}
+
+static Hash *rename_hash = 0;
+static Hash *name_rename_hash(void) {
+ if (!rename_hash)
+ rename_hash = NewHash();
+ return rename_hash;
+}
+
+static List *namewarn_list = 0;
+static List *name_namewarn_list(void) {
+ if (!namewarn_list)
+ namewarn_list = NewList();
+ return namewarn_list;
+}
+
+static List *rename_list = 0;
+static List *name_rename_list(void) {
+ if (!rename_list)
+ rename_list = NewList();
+ return rename_list;
+}
+
+/* -----------------------------------------------------------------------------
+ * int need_name_warning(Node *n)
+ *
+ * Detects if a node needs name warnings
+ *
+ * ----------------------------------------------------------------------------- */
+
+static int need_name_warning(Node *n) {
+ int need = 1;
+ /*
+ We don't use name warnings for:
+ - class forwards, no symbol is generated at the target language.
+ - template declarations, only for real instances using %template(name).
+ - typedefs, have no effect at the target language.
+ - using declarations and using directives, have no effect at the target language.
+ */
+ if (checkAttribute(n, "nodeType", "classforward")) {
+ need = 0;
+ } else if (checkAttribute(n, "nodeType", "using")) {
+ need = 0;
+ } else if (checkAttribute(n, "storage", "typedef")) {
+ need = 0;
+ } else if (Getattr(n, "hidden")) {
+ need = 0;
+ } else if (Getattr(n, "ignore")) {
+ need = 0;
+ } else if (Getattr(n, "templatetype")) {
+ need = 0;
+ } else if (GetFlag(n, "parsing_template_declaration")) {
+ need = 0;
+ }
+ return need;
+}
+
+/* -----------------------------------------------------------------------------
+ * int Swig_need_redefined_warn()
+ *
+ * Detects when a redefined object needs a warning
+ *
+ * ----------------------------------------------------------------------------- */
+
+static int nodes_are_equivalent(Node *a, Node *b, int a_inclass) {
+ /* they must have the same type */
+ String *ta = nodeType(a);
+ String *tb = nodeType(b);
+ if (!Equal(ta, tb)) {
+ if (!(Equal(ta, "using") && Equal(tb, "cdecl"))) {
+ return 0;
+ }
+ }
+
+ if (Cmp(ta, "cdecl") == 0) {
+ /* both cdecl case */
+ /* typedef */
+ String *a_storage = Getattr(a, "storage");
+ String *b_storage = Getattr(b, "storage");
+
+ if ((Cmp(a_storage, "typedef") == 0)
+ || (Cmp(b_storage, "typedef") == 0)) {
+ if (Cmp(a_storage, b_storage) == 0) {
+ String *a_type = (Getattr(a, "type"));
+ String *b_type = (Getattr(b, "type"));
+ if (Cmp(a_type, b_type) == 0)
+ return 1;
+ }
+ return 0;
+ }
+
+ /* static functions */
+ if (Swig_storage_isstatic(a) || Swig_storage_isstatic(b)) {
+ if (Cmp(a_storage, b_storage) != 0)
+ return 0;
+ }
+
+ /* friend methods */
+
+ if (!a_inclass || (Cmp(a_storage, "friend") == 0)) {
+ /* check declaration */
+
+ String *a_decl = (Getattr(a, "decl"));
+ String *b_decl = (Getattr(b, "decl"));
+ if (Cmp(a_decl, b_decl) == 0) {
+ /* check return type */
+ String *a_type = (Getattr(a, "type"));
+ String *b_type = (Getattr(b, "type"));
+ if (Cmp(a_type, b_type) == 0) {
+ /* check parameters */
+ Parm *ap = (Getattr(a, "parms"));
+ Parm *bp = (Getattr(b, "parms"));
+ while (ap && bp) {
+ SwigType *at = Getattr(ap, "type");
+ SwigType *bt = Getattr(bp, "type");
+ if (Cmp(at, bt) != 0)
+ return 0;
+ ap = nextSibling(ap);
+ bp = nextSibling(bp);
+ }
+ if (ap || bp) {
+ return 0;
+ } else {
+ Node *a_template = Getattr(a, "template");
+ Node *b_template = Getattr(b, "template");
+ /* Not equivalent if one is a template instantiation (via %template) and the other is a non-templated function */
+ if ((a_template && !b_template) || (!a_template && b_template))
+ return 0;
+ }
+ return 1;
+ }
+ }
+ }
+ } else if (Equal(ta, "using")) {
+ /* using and cdecl case */
+ String *b_storage = Getattr(b, "storage");
+ if (Equal(b_storage, "typedef")) {
+ String *a_name = Getattr(a, "name");
+ String *b_name = Getattr(b, "name");
+ if (Equal(a_name, b_name))
+ return 1;
+ }
+ } else {
+ /* both %constant case */
+ String *a_storage = Getattr(a, "storage");
+ String *b_storage = Getattr(b, "storage");
+ if ((Cmp(a_storage, "%constant") == 0)
+ || (Cmp(b_storage, "%constant") == 0)) {
+ if (Cmp(a_storage, b_storage) == 0) {
+ String *a_type = (Getattr(a, "type"));
+ String *b_type = (Getattr(b, "type"));
+ if ((Cmp(a_type, b_type) == 0)
+ && (Cmp(Getattr(a, "value"), Getattr(b, "value")) == 0))
+ return 1;
+ }
+ return 0;
+ }
+ if (Equal(ta, "template") && Equal(tb, "template")) {
+ if (Cmp(a_storage, "friend") == 0 || Cmp(b_storage, "friend") == 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int Swig_need_redefined_warn(Node *a, Node *b, int InClass) {
+ String *a_name = Getattr(a, "name");
+ String *b_name = Getattr(b, "name");
+ String *a_symname = Getattr(a, "sym:name");
+ String *b_symname = Getattr(b, "sym:name");
+ /* always send a warning if a 'rename' is involved */
+ if ((a_symname && !Equal(a_symname, a_name))
+ || (b_symname && !Equal(b_symname, b_name))) {
+ if (!Equal(a_name, b_name)) {
+ return 1;
+ }
+ }
+
+
+ return !nodes_are_equivalent(a, b, InClass);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * int Swig_need_protected(Node* n)
+ *
+ * Detects when we need to fully register the protected member.
+ * This is basically any protected members when the allprotected mode is set.
+ * Otherwise we take just the protected virtual methods and non-static methods
+ * (potentially virtual methods) as well as constructors/destructors.
+ * Also any "using" statements in a class may potentially be virtual.
+ * ----------------------------------------------------------------------------- */
+
+int Swig_need_protected(Node *n) {
+ String *nodetype = nodeType(n);
+ if (checkAttribute(n, "access", "protected")) {
+ if ((Equal(nodetype, "cdecl"))) {
+ if (Swig_director_mode() && Swig_director_protected_mode() && Swig_all_protected_mode()) {
+ return 1;
+ }
+ if (SwigType_isfunction(Getattr(n, "decl"))) {
+ String *storage = Getattr(n, "storage");
+ /* The function is declared virtual, or it has no storage. This eliminates typedef, static etc. */
+ return !storage || Equal(storage, "virtual");
+ }
+ } else if (Equal(nodetype, "constructor") || Equal(nodetype, "destructor")) {
+ return 1;
+ } else if (Equal(nodetype, "using") && !Getattr(n, "namespace")) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * void name_nameobj_add()
+ *
+ * Add nameobj (rename/namewarn)
+ *
+ * ----------------------------------------------------------------------------- */
+
+static List *make_attrlist(const char *ckey) {
+ List *list = NewList();
+ const char *cattr = strchr(ckey, '$');
+ if (cattr) {
+ String *nattr;
+ const char *rattr = strchr(++cattr, '$');
+ while (rattr) {
+ nattr = NewStringWithSize(cattr, (int)(rattr - cattr));
+ Append(list, nattr);
+ Delete(nattr);
+ cattr = rattr + 1;
+ rattr = strchr(cattr, '$');
+ }
+ nattr = NewString(cattr);
+ Append(list, nattr);
+ Delete(nattr);
+ } else {
+ Append(list, "nodeType");
+ }
+ return list;
+}
+
+static void name_object_attach_keys(const char *keys[], Hash *nameobj) {
+ Node *kw = nextSibling(nameobj);
+ List *matchlist = 0;
+ while (kw) {
+ Node *next = nextSibling(kw);
+ String *kname = Getattr(kw, "name");
+ char *ckey = kname ? Char(kname) : 0;
+ if (ckey) {
+ const char **rkey;
+ int isnotmatch = 0;
+ int isregexmatch = 0;
+ if ((strncmp(ckey, "match", 5) == 0)
+ || (isnotmatch = (strncmp(ckey, "notmatch", 8) == 0))
+ || (isregexmatch = (strncmp(ckey, "regexmatch", 10) == 0))
+ || (isnotmatch = isregexmatch = (strncmp(ckey, "notregexmatch", 13) == 0))) {
+ Hash *mi = NewHash();
+ List *attrlist = make_attrlist(ckey);
+ if (!matchlist)
+ matchlist = NewList();
+ Setattr(mi, "value", Getattr(kw, "value"));
+ Setattr(mi, "attrlist", attrlist);
+ if (isnotmatch)
+ SetFlag(mi, "notmatch");
+ if (isregexmatch)
+ SetFlag(mi, "regexmatch");
+ Delete(attrlist);
+ Append(matchlist, mi);
+ Delete(mi);
+ removeNode(kw);
+ } else {
+ for (rkey = keys; *rkey != 0; ++rkey) {
+ if (strcmp(ckey, *rkey) == 0) {
+ Setattr(nameobj, *rkey, Getattr(kw, "value"));
+ removeNode(kw);
+ }
+ }
+ }
+ }
+ kw = next;
+ }
+ if (matchlist) {
+ Setattr(nameobj, "matchlist", matchlist);
+ Delete(matchlist);
+ }
+}
+
+static void name_nameobj_add(Hash *name_hash, List *name_list, String *prefix, String *name, SwigType *decl, Hash *nameobj) {
+ String *nname = 0;
+ if (name && Len(name)) {
+ String *target_fmt = Getattr(nameobj, "targetfmt");
+ nname = prefix ? NewStringf("%s::%s", prefix, name) : NewString(name);
+ if (target_fmt) {
+ String *tmp = NewStringf(target_fmt, nname);
+ Delete(nname);
+ nname = tmp;
+ }
+ }
+
+ if (!nname || !Len(nname) || Getattr(nameobj, "fullname") || /* any of these options trigger a 'list' nameobj */
+ Getattr(nameobj, "sourcefmt") || Getattr(nameobj, "matchlist") || Getattr(nameobj, "regextarget")) {
+ if (decl)
+ Setattr(nameobj, "decl", decl);
+ if (nname && Len(nname))
+ Setattr(nameobj, "targetname", nname);
+ /* put the new nameobj at the beginning of the list, such that the
+ last inserted rule take precedence */
+ Insert(name_list, 0, nameobj);
+ } else {
+ /* here we add an old 'hash' nameobj, simple and fast */
+ Swig_name_object_set(name_hash, nname, decl, nameobj);
+ }
+ Delete(nname);
+}
+
+/* -----------------------------------------------------------------------------
+ * int name_match_nameobj()
+ *
+ * Apply and check the nameobj's math list to the node
+ *
+ * ----------------------------------------------------------------------------- */
+
+static DOH *get_lattr(Node *n, List *lattr) {
+ DOH *res = 0;
+ int ilen = Len(lattr);
+ int i;
+ for (i = 0; n && (i < ilen); ++i) {
+ String *nattr = Getitem(lattr, i);
+ res = Getattr(n, nattr);
+#ifdef SWIG_DEBUG
+ if (!res) {
+ Printf(stdout, "missing %s %s %s\n", nattr, Getattr(n, "name"), Getattr(n, "member"));
+ } else {
+ Printf(stdout, "lattr %d %s %s\n", i, nattr, DohIsString(res) ? res : Getattr(res, "name"));
+ }
+#endif
+ n = res;
+ }
+ return res;
+}
+
+#ifdef HAVE_PCRE
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
+
+static int name_regexmatch_value(Node *n, String *pattern, String *s) {
+ pcre2_code *compiled_pat;
+ PCRE2_UCHAR err[256];
+ int errornum;
+ size_t errpos;
+ int rc;
+ pcre2_match_data *match_data = 0;
+
+ compiled_pat = pcre2_compile((PCRE2_SPTR8)Char(pattern), PCRE2_ZERO_TERMINATED, 0, &errornum, &errpos, NULL);
+ if (!compiled_pat) {
+ pcre2_get_error_message (errornum, err, sizeof err);
+ Swig_error("SWIG", Getline(n),
+ "Invalid regex \"%s\": compilation failed at %d: %s\n",
+ Char(pattern), errpos, err);
+ Exit(EXIT_FAILURE);
+ }
+
+ match_data = pcre2_match_data_create_from_pattern (compiled_pat, NULL);
+ rc = pcre2_match(compiled_pat, (PCRE2_SPTR8)Char(s), PCRE2_ZERO_TERMINATED, 0, 0, match_data, 0);
+ pcre2_code_free(compiled_pat);
+ pcre2_match_data_free(match_data);
+
+ if (rc == PCRE2_ERROR_NOMATCH)
+ return 0;
+
+ if (rc < 0 ) {
+ Swig_error("SWIG", Getline(n),
+ "Matching \"%s\" against regex \"%s\" failed: %d\n",
+ Char(s), Char(pattern), rc);
+ Exit(EXIT_FAILURE);
+ }
+
+ return 1;
+}
+
+#else /* !HAVE_PCRE */
+
+static int name_regexmatch_value(Node *n, String *pattern, String *s) {
+ (void)pattern;
+ (void)s;
+ Swig_error("SWIG", Getline(n),
+ "PCRE regex matching is not available in this SWIG build.\n");
+ Exit(EXIT_FAILURE);
+ return 0;
+}
+
+#endif /* HAVE_PCRE/!HAVE_PCRE */
+
+static int name_match_value(String *mvalue, String *value) {
+#if defined(SWIG_USE_SIMPLE_MATCHOR)
+ int match = 0;
+ char *cvalue = Char(value);
+ char *cmvalue = Char(mvalue);
+ char *sep = strchr(cmvalue, '|');
+ while (sep && !match) {
+ match = strncmp(cvalue, cmvalue, sep - cmvalue) == 0;
+#ifdef SWIG_DEBUG
+ Printf(stdout, "match_value: %s %s %d\n", cvalue, cmvalue, match);
+#endif
+ cmvalue = sep + 1;
+ sep = strchr(cmvalue, '|');
+ }
+ if (!match) {
+ match = strcmp(cvalue, cmvalue) == 0;
+#ifdef SWIG_DEBUG
+ Printf(stdout, "match_value: %s %s %d\n", cvalue, cmvalue, match);
+#endif
+ }
+ return match;
+#else
+ return Equal(mvalue, value);
+#endif
+}
+
+static int name_match_nameobj(Hash *rn, Node *n) {
+ int match = 1;
+ List *matchlist = Getattr(rn, "matchlist");
+#ifdef SWIG_DEBUG
+ Printf(stdout, "name_match_nameobj: %s\n", Getattr(n, "name"));
+#endif
+ if (matchlist) {
+ int ilen = Len(matchlist);
+ int i;
+ for (i = 0; match && (i < ilen); ++i) {
+ Node *mi = Getitem(matchlist, i);
+ List *lattr = Getattr(mi, "attrlist");
+ String *nval = get_lattr(n, lattr);
+ int notmatch = GetFlag(mi, "notmatch");
+ int regexmatch = GetFlag(mi, "regexmatch");
+ match = 0;
+ if (nval) {
+ String *kwval = Getattr(mi, "value");
+ match = regexmatch ? name_regexmatch_value(n, kwval, nval)
+ : name_match_value(kwval, nval);
+#ifdef SWIG_DEBUG
+ Printf(stdout, "val %s %s %d %d \n", nval, kwval, match, ilen);
+#endif
+ }
+ if (notmatch)
+ match = !match;
+ }
+ }
+#ifdef SWIG_DEBUG
+ Printf(stdout, "name_match_nameobj: %d\n", match);
+#endif
+ return match;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash *name_nameobj_lget()
+ *
+ * Get a nameobj (rename/namewarn) from the list of filters
+ *
+ * ----------------------------------------------------------------------------- */
+
+static Hash *name_nameobj_lget(List *namelist, Node *n, String *prefix, String *name, String *decl) {
+ Hash *res = 0;
+ if (namelist) {
+ int len = Len(namelist);
+ int i;
+ int match = 0;
+ for (i = 0; !match && (i < len); i++) {
+ Hash *rn = Getitem(namelist, i);
+ String *rdecl = Getattr(rn, "decl");
+ if (rdecl && (!decl || !Equal(rdecl, decl))) {
+ continue;
+ } else if (name_match_nameobj(rn, n)) {
+ String *tname = Getattr(rn, "targetname");
+ if (tname) {
+ String *sfmt = Getattr(rn, "sourcefmt");
+ String *sname = 0;
+ int fullname = GetFlag(rn, "fullname");
+ int regextarget = GetFlag(rn, "regextarget");
+ if (sfmt) {
+ if (fullname && prefix) {
+ String *pname = NewStringf("%s::%s", prefix, name);
+ sname = NewStringf(sfmt, pname);
+ Delete(pname);
+ } else {
+ sname = NewStringf(sfmt, name);
+ }
+ } else {
+ if (fullname && prefix) {
+ sname = NewStringf("%s::%s", prefix, name);
+ } else {
+ sname = name;
+ DohIncref(name);
+ }
+ }
+ match = regextarget ? name_regexmatch_value(n, tname, sname)
+ : name_match_value(tname, sname);
+ Delete(sname);
+ } else {
+ /* Applying the renaming rule may fail if it contains a %(regex)s expression that doesn't match the given name. */
+ String *sname = NewStringf(Getattr(rn, "name"), name);
+ if (sname) {
+ if (Len(sname))
+ match = 1;
+ Delete(sname);
+ }
+ }
+ }
+ if (match) {
+ res = rn;
+ break;
+ }
+ }
+ }
+ return res;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_namewarn_add
+ *
+ * Add a namewarn objects
+ *
+ * ----------------------------------------------------------------------------- */
+
+void Swig_name_namewarn_add(String *prefix, String *name, SwigType *decl, Hash *namewrn) {
+ const char *namewrn_keys[] = { "rename", "error", "fullname", "sourcefmt", "targetfmt", 0 };
+ name_object_attach_keys(namewrn_keys, namewrn);
+ name_nameobj_add(name_namewarn_hash(), name_namewarn_list(), prefix, name, decl, namewrn);
+}
+
+/* -----------------------------------------------------------------------------
+ * Hash *name_namewarn_get()
+ *
+ * Return the namewarn object, if there is one.
+ *
+ * ----------------------------------------------------------------------------- */
+
+static Hash *name_namewarn_get(Node *n, String *prefix, String *name, SwigType *decl) {
+ if (!namewarn_hash && !namewarn_list)
+ return 0;
+ if (n) {
+ /* Return in the obvious cases */
+ if (!name || !need_name_warning(n)) {
+ return 0;
+ } else {
+ String *access = Getattr(n, "access");
+ int is_public = !access || Equal(access, "public");
+ if (!is_public && !Swig_need_protected(n)) {
+ return 0;
+ }
+ }
+ }
+ if (name) {
+ /* Check to see if the name is in the hash */
+ Hash *wrn = Swig_name_object_get(name_namewarn_hash(), prefix, name, decl);
+ if (wrn && !name_match_nameobj(wrn, n))
+ wrn = 0;
+ if (!wrn) {
+ wrn = name_nameobj_lget(name_namewarn_list(), n, prefix, name, decl);
+ }
+ if (wrn && Getattr(wrn, "error")) {
+ if (n) {
+ Swig_error(Getfile(n), Getline(n), "%s\n", Getattr(wrn, "name"));
+ } else {
+ Swig_error(cparse_file, cparse_line, "%s\n", Getattr(wrn, "name"));
+ }
+ }
+ return wrn;
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * String *Swig_name_warning()
+ *
+ * Return the name warning, if there is one.
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_warning(Node *n, String *prefix, String *name, SwigType *decl) {
+ Hash *wrn = name_namewarn_get(n, prefix, name, decl);
+ return (name && wrn) ? Getattr(wrn, "name") : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_name_rename_add()
+ *
+ * Manage the rename objects
+ *
+ * ----------------------------------------------------------------------------- */
+
+static void single_rename_add(String *prefix, String *name, SwigType *decl, Hash *newname) {
+ name_nameobj_add(name_rename_hash(), name_rename_list(), prefix, name, decl, newname);
+}
+
+/* Add a new rename. Works much like new_feature including default argument handling. */
+void Swig_name_rename_add(String *prefix, String *name, SwigType *decl, Hash *newname, ParmList *declaratorparms) {
+
+ ParmList *declparms = declaratorparms;
+
+ const char *rename_keys[] = { "fullname", "sourcefmt", "targetfmt", "continue", "regextarget", 0 };
+ name_object_attach_keys(rename_keys, newname);
+
+ /* Add the name */
+ single_rename_add(prefix, name, decl, newname);
+
+ /* Add extra names if there are default parameters in the parameter list */
+ if (decl) {
+ int constqualifier = SwigType_isconst(decl);
+ while (declparms) {
+ if (ParmList_has_defaultargs(declparms)) {
+
+ /* Create a parameter list for the new rename by copying all
+ but the last (defaulted) parameter */
+ ParmList *newparms = CopyParmListMax(declparms,ParmList_len(declparms)-1);
+
+ /* Create new declaration - with the last parameter removed */
+ SwigType *newdecl = Copy(decl);
+ Delete(SwigType_pop_function(newdecl)); /* remove the old parameter list from newdecl */
+ SwigType_add_function(newdecl, newparms);
+ if (constqualifier)
+ SwigType_add_qualifier(newdecl, "const");
+
+ single_rename_add(prefix, name, newdecl, newname);
+ declparms = newparms;
+ Delete(newdecl);
+ } else {
+ declparms = 0;
+ }
+ }
+ }
+}
+
+
+/* Create a name for the given node applying rename/namewarn if needed */
+static String *apply_rename(Node* n, String *newname, int fullname, String *prefix, String *name) {
+ String *result = 0;
+ if (newname && Len(newname)) {
+ if (Strcmp(newname, "$ignore") == 0) {
+ /* $ignore doesn't apply to parameters and while it's rare to explicitly write %ignore directives for them they could be caught by a wildcard ignore using
+ regex match, just ignore the attempt to ignore them in this case */
+ if (!Equal(nodeType(n), "parm"))
+ result = Copy(newname);
+ } else {
+ char *cnewname = Char(newname);
+ if (cnewname) {
+ int destructor = name && (*(Char(name)) == '~');
+ String *fmt = newname;
+ /* use name as a fmt, but avoid C++ "%" and "%=" operators */
+ if (Len(newname) > 1 && strchr(cnewname, '%') && !(strcmp(cnewname, "%=") == 0)) {
+ if (fullname && prefix) {
+ result = NewStringf(fmt, prefix, name);
+ } else {
+ result = NewStringf(fmt, name);
+ }
+ } else {
+ result = Copy(newname);
+ }
+ if (destructor && result && (*(Char(result)) != '~')) {
+ Insert(result, 0, "~");
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * String *Swig_name_make()
+ *
+ * Make a name after applying all the rename/namewarn objects
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_make(Node *n, String *prefix, const_String_or_char_ptr cname, SwigType *decl, String *oldname) {
+ String *nname = 0;
+ String *result = 0;
+ String *name = NewString(cname);
+ Hash *wrn = 0;
+ String *rdecl = 0;
+ String *rname = 0;
+
+ /* very specific hack for template constructors/destructors */
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_name_make: looking for %s %s %s %s\n", prefix, name, decl, oldname);
+#endif
+
+ if (name && n && SwigType_istemplate(name)) {
+ String *nodetype = nodeType(n);
+ if (nodetype && (Equal(nodetype, "constructor") || Equal(nodetype, "destructor"))) {
+ String *nprefix = 0;
+ String *nlast = 0;
+ String *tprefix;
+ Swig_scopename_split(name, &nprefix, &nlast);
+ tprefix = SwigType_templateprefix(nlast);
+ Delete(nlast);
+ if (Len(nprefix)) {
+ Append(nprefix, "::");
+ Append(nprefix, tprefix);
+ Delete(tprefix);
+ rname = nprefix;
+ } else {
+ rname = tprefix;
+ Delete(nprefix);
+ }
+ rdecl = Copy(decl);
+ Replaceall(rdecl, name, rname);
+#ifdef SWIG_DEBUG
+ Printf(stdout, "SWIG_name_make: use new name %s %s : %s %s\n", name, decl, rname, rdecl);
+#endif
+ decl = rdecl;
+ Delete(name);
+ name = rname;
+ }
+ }
+
+ if (rename_hash || rename_list || namewarn_hash || namewarn_list) {
+ Hash *rn = Swig_name_object_get(name_rename_hash(), prefix, name, decl);
+ if (!rn || !name_match_nameobj(rn, n)) {
+ rn = name_nameobj_lget(name_rename_list(), n, prefix, name, decl);
+ if (rn) {
+ String *sfmt = Getattr(rn, "sourcefmt");
+ int fullname = GetFlag(rn, "fullname");
+ if (fullname && prefix) {
+ String *sname = NewStringf("%s::%s", prefix, name);
+ Delete(name);
+ name = sname;
+ prefix = 0;
+ }
+ if (sfmt) {
+ String *sname = NewStringf(sfmt, name);
+ Delete(name);
+ name = sname;
+ }
+ }
+ }
+ if (rn) {
+ String *newname = Getattr(rn, "name");
+ int fullname = GetFlag(rn, "fullname");
+ result = apply_rename(n, newname, fullname, prefix, name);
+ }
+ if (result && !Equal(result, name)) {
+ /* operators in C++ allow aliases, we look for them */
+ char *cresult = Char(result);
+ if (cresult && (strncmp(cresult, "operator ", 9) == 0)) {
+ String *nresult = Swig_name_make(n, prefix, result, decl, oldname);
+ if (!Equal(nresult, result)) {
+ Delete(result);
+ result = nresult;
+ } else {
+ Delete(nresult);
+ }
+ }
+ }
+ nname = result ? result : name;
+ wrn = name_namewarn_get(n, prefix, nname, decl);
+ if (wrn) {
+ String *rename = Getattr(wrn, "rename");
+ if (rename) {
+ String *msg = Getattr(wrn, "name");
+ int fullname = GetFlag(wrn, "fullname");
+ if (result)
+ Delete(result);
+ result = apply_rename(n, rename, fullname, prefix, name);
+ if ((msg) && (Len(msg))) {
+ if (!Getmeta(nname, "already_warned")) {
+ String* suffix = 0;
+ if (Strcmp(result, "$ignore") == 0) {
+ suffix = NewStringf(": ignoring '%s'\n", name);
+ } else if (Strcmp(result, name) != 0) {
+ suffix = NewStringf(", renaming to '%s'\n", result);
+ } else {
+ /* No rename was performed */
+ suffix = NewString("\n");
+ }
+ if (n) {
+ /* Parameter renaming is not fully implemented. Mainly because there is no C/C++ syntax to
+ * for %rename to fully qualify a function's parameter name from outside the function. Hence it
+ * is not possible to implemented targeted warning suppression on one parameter in one function. */
+ int suppress_parameter_rename_warning = Equal(nodeType(n), "parm");
+ if (!suppress_parameter_rename_warning) {
+ SWIG_WARN_NODE_BEGIN(n);
+ Swig_warning(0, Getfile(n), Getline(n), "%s%s", msg, suffix);
+ SWIG_WARN_NODE_END(n);
+ }
+ } else {
+ Swig_warning(0, Getfile(name), Getline(name), "%s%s", msg, suffix);
+ }
+ Setmeta(nname, "already_warned", "1");
+ Delete(suffix);
+ }
+ }
+ }
+ }
+ }
+ if (!result || !Len(result)) {
+ if (result)
+ Delete(result);
+ if (oldname) {
+ result = NewString(oldname);
+ } else {
+ result = NewString(cname);
+ }
+ }
+ Delete(name);
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_name_make: result '%s' '%s'\n", cname, result);
+#endif
+
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * void Swig_name_inherit()
+ *
+ * Inherit namewarn, rename, and feature objects
+ *
+ * ----------------------------------------------------------------------------- */
+
+void Swig_name_inherit(String *base, String *derived) {
+ /* Printf(stdout,"base = '%s', derived = '%s'\n", base, derived); */
+ Swig_name_object_inherit(name_rename_hash(), base, derived);
+ Swig_name_object_inherit(name_namewarn_hash(), base, derived);
+ Swig_name_object_inherit(Swig_cparse_features(), base, derived);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_inherit_base_symbols()
+ * ----------------------------------------------------------------------------- */
+
+void Swig_inherit_base_symbols(List *bases) {
+ if (bases) {
+ Iterator s;
+ for (s = First(bases); s.item; s = Next(s)) {
+ Symtab *st = Getattr(s.item, "symtab");
+ if (st) {
+ Setfile(st, Getfile(s.item));
+ Setline(st, Getline(s.item));
+ Swig_symbol_inherit(st);
+ }
+ }
+ Delete(bases);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_make_inherit_list()
+ * ----------------------------------------------------------------------------- */
+
+List *Swig_make_inherit_list(String *clsname, List *names, String *Namespaceprefix) {
+ int i, ilen;
+ String *derived;
+ List *bases = NewList();
+
+ if (Namespaceprefix)
+ derived = NewStringf("%s::%s", Namespaceprefix, clsname);
+ else
+ derived = NewString(clsname);
+
+ ilen = Len(names);
+ for (i = 0; i < ilen; i++) {
+ String *base;
+ String *n = Getitem(names, i);
+ /* Try to figure out where this symbol is */
+ Node *s = Swig_symbol_clookup(n, 0);
+ if (s) {
+ while (s && (Strcmp(nodeType(s), "class") != 0)) {
+ /* Not a class. Could be a typedef though. */
+ String *storage = Getattr(s, "storage");
+ if (storage && (Strcmp(storage, "typedef") == 0)) {
+ String *nn = Getattr(s, "type");
+ s = Swig_symbol_clookup(nn, Getattr(s, "sym:symtab"));
+ } else {
+ break;
+ }
+ }
+ if (s && ((Strcmp(nodeType(s), "class") == 0) || (Strcmp(nodeType(s), "template") == 0))) {
+ String *q = Swig_symbol_qualified(s);
+ Append(bases, s);
+ if (q) {
+ base = NewStringf("%s::%s", q, Getattr(s, "name"));
+ Delete(q);
+ } else {
+ base = NewString(Getattr(s, "name"));
+ }
+ } else {
+ base = NewString(n);
+ }
+ } else {
+ base = NewString(n);
+ }
+ if (base) {
+ Swig_name_inherit(base, derived);
+ Delete(base);
+ }
+ }
+ return bases;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * void Swig_name_str()
+ *
+ * Return a stringified version of a C/C++ symbol from a node.
+ * The node passed in is expected to be a function, constructor, destructor or
+ * variable. Some example return values:
+ * "MyNameSpace::MyTemplate<MyNameSpace::ABC >::~MyTemplate"
+ * "MyNameSpace::ABC::ABC"
+ * "MyNameSpace::ABC::constmethod"
+ * "MyNameSpace::ABC::variablename"
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_str(Node *n) {
+ String *qname;
+ String *qualifier = Swig_symbol_qualified(n);
+ String *name = Swig_scopename_last(Getattr(n, "name"));
+ if (qualifier)
+ qualifier = SwigType_namestr(qualifier);
+
+ /* Very specific hack for template constructors/destructors */
+ if (SwigType_istemplate(name)) {
+ String *nodetype = nodeType(n);
+ if (nodetype && (Equal(nodetype, "constructor") || Equal(nodetype, "destructor"))) {
+ String *nprefix = 0;
+ String *nlast = 0;
+ String *tprefix;
+ Swig_scopename_split(name, &nprefix, &nlast);
+ tprefix = SwigType_templateprefix(nlast);
+ Delete(nlast);
+ Delete(nprefix);
+ Delete(name);
+ name = tprefix;
+ }
+ }
+
+ qname = NewString("");
+ if (qualifier && Len(qualifier) > 0)
+ Printf(qname, "%s::", qualifier);
+ Printf(qname, "%s", SwigType_str(name, 0));
+
+ Delete(name);
+ Delete(qualifier);
+
+ return qname;
+}
+
+/* -----------------------------------------------------------------------------
+ * void Swig_name_decl()
+ *
+ * Return a stringified version of a C/C++ declaration without the return type.
+ * The node passed in is expected to be a function, constructor, destructor or
+ * variable. Some example return values:
+ * "MyNameSpace::MyTemplate<MyNameSpace::ABC >::~MyTemplate()"
+ * "MyNameSpace::ABC::ABC(int,double)"
+ * "MyNameSpace::ABC::constmethod(int) const"
+ * "MyNameSpace::ABC::refqualifiermethod(int) const &"
+ * "MyNameSpace::ABC::variablename"
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_decl(Node *n) {
+ String *qname;
+ String *decl;
+
+ qname = Swig_name_str(n);
+ decl = NewStringf("%s", qname);
+
+ if (!checkAttribute(n, "kind", "variable")) {
+ String *d = Getattr(n, "decl");
+ Printv(decl, "(", ParmList_errorstr(Getattr(n, "parms")), ")", NIL);
+ if (SwigType_isfunction(d)) {
+ SwigType *decl_temp = Copy(d);
+ SwigType *qualifiers = SwigType_pop_function_qualifiers(decl_temp);
+ if (qualifiers) {
+ String *qualifiers_string = SwigType_str(qualifiers, 0);
+ Printv(decl, " ", qualifiers_string, NIL);
+ Delete(qualifiers_string);
+ }
+ Delete(decl_temp);
+ }
+ }
+
+ Delete(qname);
+
+ return decl;
+}
+
+/* -----------------------------------------------------------------------------
+ * void Swig_name_fulldecl()
+ *
+ * Return a stringified version of a C/C++ declaration including the return type.
+ * The node passed in is expected to be a function, constructor or destructor.
+ * Some example return values:
+ * "MyNameSpace::MyTemplate<MyNameSpace::ABC >::~MyTemplate()"
+ * "MyNameSpace::ABC::ABC(int,double)"
+ * "int * MyNameSpace::ABC::constmethod(int) const"
+ *
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_name_fulldecl(Node *n) {
+ String *decl = Swig_name_decl(n);
+ String *type = Getattr(n, "type");
+ String *nodetype = nodeType(n);
+ String *fulldecl;
+ /* add on the return type */
+ if (nodetype && (Equal(nodetype, "constructor") || Equal(nodetype, "destructor"))) {
+ fulldecl = decl;
+ } else {
+ String *t = SwigType_str(type, 0);
+ fulldecl = NewStringf("%s %s", t, decl);
+ Delete(decl);
+ Delete(t);
+ }
+ return fulldecl;
+}
+
diff --git a/contrib/tools/swig/Source/Swig/parms.c b/contrib/tools/swig/Source/Swig/parms.c
new file mode 100644
index 0000000000..11071ce0cd
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/parms.c
@@ -0,0 +1,272 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * parms.c
+ *
+ * Parameter list class.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+
+/* ------------------------------------------------------------------------
+ * NewParm()
+ *
+ * Create a new parameter from datatype 'type' and name 'name' copying
+ * the file and line number from the Node from_node.
+ * ------------------------------------------------------------------------ */
+
+Parm *NewParm(SwigType *type, const_String_or_char_ptr name, Node *from_node) {
+ Parm *p = NewParmWithoutFileLineInfo(type, name);
+ Setfile(p, Getfile(from_node));
+ Setline(p, Getline(from_node));
+ return p;
+}
+
+/* ------------------------------------------------------------------------
+ * NewParmWithoutFileLineInfo()
+ *
+ * Create a new parameter from datatype 'type' and name 'name' without any
+ * file / line numbering information.
+ * ------------------------------------------------------------------------ */
+
+Parm *NewParmWithoutFileLineInfo(SwigType *type, const_String_or_char_ptr name) {
+ Parm *p = NewHash();
+ set_nodeType(p, "parm");
+ if (type) {
+ SwigType *ntype = Copy(type);
+ Setattr(p, "type", ntype);
+ Delete(ntype);
+ }
+ Setattr(p, "name", name);
+ return p;
+}
+
+/* ------------------------------------------------------------------------
+ * NewParmNode()
+ *
+ * Create a new parameter from datatype 'type' and name and symbol table as
+ * well as file and line number from the 'from_node'.
+ * The resulting Parm will be similar to a Node used for typemap lookups.
+ * ------------------------------------------------------------------------ */
+
+Parm *NewParmNode(SwigType *type, Node *from_node) {
+ Parm *p = NewParm(type, Getattr(from_node, "name"), from_node);
+ Setattr(p, "sym:symtab", Getattr(from_node, "sym:symtab"));
+ return p;
+}
+
+/* ------------------------------------------------------------------------
+ * CopyParm()
+ * ------------------------------------------------------------------------ */
+
+Parm *CopyParm(Parm *p) {
+ Parm *np = NewHash();
+ Iterator ki;
+ for (ki = First(p); ki.key; ki = Next(ki)) {
+ if (DohIsString(ki.item)) {
+ DOH *c = Copy(ki.item);
+ Setattr(np,ki.key,c);
+ Delete(c);
+ }
+ }
+ Setfile(np, Getfile(p));
+ Setline(np, Getline(p));
+ return np;
+}
+
+/* ------------------------------------------------------------------
+ * CopyParmListMax()
+ * CopyParmList()
+ * ------------------------------------------------------------------ */
+
+ParmList *CopyParmListMax(ParmList *p, int count) {
+ Parm *np;
+ Parm *pp = 0;
+ Parm *fp = 0;
+
+ if (!p)
+ return 0;
+
+ while (p) {
+ if (count == 0) break;
+ np = CopyParm(p);
+ if (pp) {
+ set_nextSibling(pp, np);
+ Delete(np);
+ } else {
+ fp = np;
+ }
+ pp = np;
+ p = nextSibling(p);
+ count--;
+ }
+ return fp;
+}
+
+ParmList *CopyParmList(ParmList *p) {
+ return CopyParmListMax(p,-1);
+}
+
+/* -----------------------------------------------------------------------------
+ * int ParmList_numrequired(). Return number of required arguments
+ * ----------------------------------------------------------------------------- */
+
+int ParmList_numrequired(ParmList *p) {
+ int i = 0;
+ while (p) {
+ SwigType *t = Getattr(p, "type");
+ String *value = Getattr(p, "value");
+ if (value)
+ return i;
+ if (!(SwigType_type(t) == T_VOID))
+ i++;
+ else
+ break;
+ p = nextSibling(p);
+ }
+ return i;
+}
+
+/* -----------------------------------------------------------------------------
+ * int ParmList_len()
+ * ----------------------------------------------------------------------------- */
+
+int ParmList_len(ParmList *p) {
+ int i = 0;
+ while (p) {
+ i++;
+ p = nextSibling(p);
+ }
+ return i;
+}
+
+/* ---------------------------------------------------------------------
+ * get_empty_type()
+ * ---------------------------------------------------------------------- */
+
+static SwigType *get_empty_type(void) {
+ return NewStringEmpty();
+}
+
+/* ---------------------------------------------------------------------
+ * ParmList_str()
+ *
+ * Generates a string of parameters
+ * ---------------------------------------------------------------------- */
+
+String *ParmList_str(ParmList *p) {
+ String *out = NewStringEmpty();
+ while (p) {
+ String *type = Getattr(p, "type");
+ String *pstr = SwigType_str(type ? type : get_empty_type(), Getattr(p, "name"));
+ Append(out, pstr);
+ p = nextSibling(p);
+ if (p) {
+ Append(out, ",");
+ }
+ Delete(pstr);
+ }
+ return out;
+}
+
+/* ---------------------------------------------------------------------
+ * ParmList_str_defaultargs()
+ *
+ * Generates a string of parameters including default arguments
+ * ---------------------------------------------------------------------- */
+
+String *ParmList_str_defaultargs(ParmList *p) {
+ String *out = NewStringEmpty();
+ while (p) {
+ String *value = Getattr(p, "value");
+ String *type = Getattr(p, "type");
+ String *pstr = SwigType_str(type ? type : get_empty_type(), Getattr(p, "name"));
+ Append(out, pstr);
+ if (value) {
+ Printf(out, "=%s", value);
+ }
+ p = nextSibling(p);
+ if (p) {
+ Append(out, ",");
+ }
+ Delete(pstr);
+ }
+ return out;
+}
+
+/* -----------------------------------------------------------------------------
+ * ParmList_str_multibrackets()
+ *
+ * Generates a string of parameters including default arguments adding brackets
+ * if more than one parameter
+ * ----------------------------------------------------------------------------- */
+
+String *ParmList_str_multibrackets(ParmList *p) {
+ String *out;
+ String *parm_str = ParmList_str_defaultargs(p);
+ if (ParmList_len(p) > 1)
+ out = NewStringf("(%s)", parm_str);
+ else
+ out = NewStringf("%s", parm_str);
+ Delete(parm_str);
+ return out;
+}
+
+/* ---------------------------------------------------------------------
+ * ParmList_protostr()
+ *
+ * Generate a prototype string.
+ * ---------------------------------------------------------------------- */
+
+String *ParmList_protostr(ParmList *p) {
+ String *out = NewStringEmpty();
+ while (p) {
+ String *type = Getattr(p, "type");
+ String *pstr = SwigType_str(type ? type : get_empty_type(), 0);
+ Append(out, pstr);
+ p = nextSibling(p);
+ if (p) {
+ Append(out, ",");
+ }
+ Delete(pstr);
+ }
+ return out;
+}
+
+/* ---------------------------------------------------------------------
+ * ParmList_has_defaultargs()
+ *
+ * Returns 1 if the parameter list passed in is has one or more default
+ * arguments. Otherwise returns 0.
+ * ---------------------------------------------------------------------- */
+
+int ParmList_has_defaultargs(ParmList *p) {
+ while (p) {
+ if (Getattr(p, "value")) {
+ return 1;
+ }
+ p = nextSibling(p);
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * ParmList_has_varargs()
+ *
+ * Returns 1 if the parameter list passed in has varargs.
+ * Otherwise returns 0.
+ * ---------------------------------------------------------------------- */
+
+int ParmList_has_varargs(ParmList *p) {
+ Parm *lp = 0;
+ while (p) {
+ lp = p;
+ p = nextSibling(p);
+ }
+ return lp ? SwigType_isvarargs(Getattr(lp, "type")) : 0;
+}
diff --git a/contrib/tools/swig/Source/Swig/scanner.c b/contrib/tools/swig/Source/Swig/scanner.c
new file mode 100644
index 0000000000..9dbb353648
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/scanner.c
@@ -0,0 +1,1898 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * scanner.c
+ *
+ * This file implements a general purpose C/C++ compatible lexical scanner.
+ * This scanner isn't intended to be plugged directly into a parser built
+ * with yacc. Rather, it contains a lot of generic code that could be used
+ * to easily construct yacc-compatible scanners.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include <ctype.h>
+
+extern String *cparse_file;
+extern int cparse_line;
+extern int cparse_cplusplus;
+extern int cparse_start_line;
+
+struct Scanner {
+ String *text; /* Current token value */
+ List *scanobjs; /* Objects being scanned */
+ String *str; /* Current object being scanned */
+ char *idstart; /* Optional identifier start characters */
+ int nexttoken; /* Next token to be returned */
+ int start_line; /* Starting line of certain declarations */
+ int line;
+ int yylen; /* Length of text pushed into text */
+ String *file;
+ String *error; /* Last error message (if any) */
+ int error_line; /* Error line number */
+ int freeze_line; /* Suspend line number updates */
+ List *brackets; /* Current level of < > brackets on each level */
+};
+
+typedef struct Locator {
+ String *filename;
+ int line_number;
+ struct Locator *next;
+} Locator;
+static int follow_locators = 0;
+
+static void brackets_push(Scanner *);
+static void brackets_clear(Scanner *);
+
+/* -----------------------------------------------------------------------------
+ * NewScanner()
+ *
+ * Create a new scanner object
+ * ----------------------------------------------------------------------------- */
+
+Scanner *NewScanner(void) {
+ Scanner *s;
+ s = (Scanner *) Malloc(sizeof(Scanner));
+ s->line = 1;
+ s->file = 0;
+ s->nexttoken = -1;
+ s->start_line = 1;
+ s->yylen = 0;
+ s->idstart = NULL;
+ s->scanobjs = NewList();
+ s->text = NewStringEmpty();
+ s->str = 0;
+ s->error = 0;
+ s->error_line = 0;
+ s->freeze_line = 0;
+ s->brackets = NewList();
+ brackets_push(s);
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * DelScanner()
+ *
+ * Delete a scanner object.
+ * ----------------------------------------------------------------------------- */
+
+void DelScanner(Scanner *s) {
+ assert(s);
+ Delete(s->scanobjs);
+ Delete(s->brackets);
+ Delete(s->text);
+ Delete(s->file);
+ Delete(s->error);
+ Delete(s->str);
+ Free(s->idstart);
+ Free(s);
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_clear()
+ *
+ * Clear the contents of a scanner object.
+ * ----------------------------------------------------------------------------- */
+
+void Scanner_clear(Scanner *s) {
+ assert(s);
+ Delete(s->str);
+ Clear(s->text);
+ Clear(s->scanobjs);
+ brackets_clear(s);
+ Delete(s->error);
+ s->str = 0;
+ s->error = 0;
+ s->line = 1;
+ s->nexttoken = -1;
+ s->start_line = 0;
+ s->yylen = 0;
+ /* Should these be cleared too?
+ s->idstart;
+ s->file;
+ s->error_line;
+ s->freeze_line;
+ */
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_push()
+ *
+ * Push some new text into the scanner. The scanner will start parsing this text
+ * immediately before returning to the old text.
+ * ----------------------------------------------------------------------------- */
+
+void Scanner_push(Scanner *s, String *txt) {
+ assert(s && txt);
+ Push(s->scanobjs, txt);
+ if (s->str) {
+ Setline(s->str,s->line);
+ Delete(s->str);
+ }
+ s->str = txt;
+ DohIncref(s->str);
+ s->line = Getline(txt);
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_pushtoken()
+ *
+ * Push a token into the scanner. This token will be returned on the next
+ * call to Scanner_token().
+ * ----------------------------------------------------------------------------- */
+
+void Scanner_pushtoken(Scanner *s, int nt, const_String_or_char_ptr val) {
+ assert(s);
+ assert((nt >= 0) && (nt < SWIG_MAXTOKENS));
+ s->nexttoken = nt;
+ if ( Char(val) != Char(s->text) ) {
+ Clear(s->text);
+ Append(s->text,val);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_set_location()
+ *
+ * Set the file and line number location of the scanner.
+ * ----------------------------------------------------------------------------- */
+
+void Scanner_set_location(Scanner *s, String *file, int line) {
+ Setline(s->str, line);
+ Setfile(s->str, file);
+ s->line = line;
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_file()
+ *
+ * Get the current file.
+ * ----------------------------------------------------------------------------- */
+
+String *Scanner_file(Scanner *s) {
+ return Getfile(s->str);
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_line()
+ *
+ * Get the current line number
+ * ----------------------------------------------------------------------------- */
+int Scanner_line(Scanner *s) {
+ return s->line;
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_start_line()
+ *
+ * Get the line number on which the current token starts
+ * ----------------------------------------------------------------------------- */
+int Scanner_start_line(Scanner *s) {
+ return s->start_line;
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_idstart()
+ *
+ * Change the set of additional characters that can be used to start an identifier.
+ * ----------------------------------------------------------------------------- */
+
+void Scanner_idstart(Scanner *s, const char *id) {
+ Free(s->idstart);
+ s->idstart = Swig_copy_string(id);
+}
+
+/* -----------------------------------------------------------------------------
+ * nextchar()
+ *
+ * Returns the next character from the scanner or 0 if end of the string.
+ * ----------------------------------------------------------------------------- */
+static char nextchar(Scanner *s) {
+ int nc;
+ if (!s->str)
+ return 0;
+ while ((nc = Getc(s->str)) == EOF) {
+ Delete(s->str);
+ s->str = 0;
+ Delitem(s->scanobjs, 0);
+ if (Len(s->scanobjs) == 0)
+ return 0;
+ s->str = Getitem(s->scanobjs, 0);
+ s->line = Getline(s->str);
+ DohIncref(s->str);
+ }
+ if ((nc == '\n') && (!s->freeze_line))
+ s->line++;
+ Putc(nc,s->text);
+ return (char)nc;
+}
+
+/* -----------------------------------------------------------------------------
+ * set_error()
+ *
+ * Sets error information on the scanner.
+ * ----------------------------------------------------------------------------- */
+
+static void set_error(Scanner *s, int line, const_String_or_char_ptr msg) {
+ s->error_line = line;
+ s->error = NewString(msg);
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_errmsg()
+ * Scanner_errline()
+ *
+ * Returns error information (if any)
+ * ----------------------------------------------------------------------------- */
+
+String *Scanner_errmsg(Scanner *s) {
+ return s->error;
+}
+
+int Scanner_errline(Scanner *s) {
+ return s->error_line;
+}
+
+/* -----------------------------------------------------------------------------
+ * freeze_line()
+ *
+ * Freezes the current line number.
+ * ----------------------------------------------------------------------------- */
+
+static void freeze_line(Scanner *s, int val) {
+ s->freeze_line = val;
+}
+
+/* -----------------------------------------------------------------------------
+ * brackets_count()
+ *
+ * Returns the number of brackets at the current depth.
+ * A syntax error with unbalanced ) brackets will result in a NULL pointer return.
+ * ----------------------------------------------------------------------------- */
+static int *brackets_count(Scanner *s) {
+ int *count;
+ if (Len(s->brackets) > 0)
+ count = (int *)Data(Getitem(s->brackets, 0));
+ else
+ count = 0;
+ return count;
+}
+
+/* -----------------------------------------------------------------------------
+ * brackets_clear()
+ *
+ * Resets the current depth and clears all brackets.
+ * Usually called at the end of statements;
+ * ----------------------------------------------------------------------------- */
+static void brackets_clear(Scanner *s) {
+ Clear(s->brackets);
+ brackets_push(s); /* base bracket count should always be created */
+}
+
+/* -----------------------------------------------------------------------------
+ * brackets_increment()
+ *
+ * Increases the number of brackets at the current depth.
+ * Usually called when a single '<' is found.
+ * ----------------------------------------------------------------------------- */
+static void brackets_increment(Scanner *s) {
+ int *count = brackets_count(s);
+ if (count)
+ (*count)++;
+}
+
+/* -----------------------------------------------------------------------------
+ * brackets_decrement()
+ *
+ * Decreases the number of brackets at the current depth.
+ * Usually called when a single '>' is found.
+ * ----------------------------------------------------------------------------- */
+static void brackets_decrement(Scanner *s) {
+ int *count = brackets_count(s);
+ if (count)
+ (*count)--;
+}
+
+/* -----------------------------------------------------------------------------
+ * brackets_reset()
+ *
+ * Sets the number of '<' brackets back to zero. Called at the point where
+ * it is no longer possible to have a matching closing >> pair for a template.
+ * ----------------------------------------------------------------------------- */
+static void brackets_reset(Scanner *s) {
+ int *count = brackets_count(s);
+ if (count)
+ *count = 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * brackets_push()
+ *
+ * Increases the depth of brackets.
+ * Usually called when '(' is found.
+ * ----------------------------------------------------------------------------- */
+static void brackets_push(Scanner *s) {
+ int *newInt = (int *)Malloc(sizeof(int));
+ *newInt = 0;
+ Push(s->brackets, NewVoid(newInt, Free));
+}
+
+/* -----------------------------------------------------------------------------
+ * brackets_pop()
+ *
+ * Decreases the depth of brackets.
+ * Usually called when ')' is found.
+ * ----------------------------------------------------------------------------- */
+static void brackets_pop(Scanner *s) {
+ if (Len(s->brackets) > 0) /* protect against unbalanced ')' brackets */
+ Delitem(s->brackets, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * brackets_allow_shift()
+ *
+ * Return 1 to allow shift (>>), or 0 if (>>) should be split into (> >).
+ * This is for C++11 template syntax for closing templates.
+ * ----------------------------------------------------------------------------- */
+static int brackets_allow_shift(Scanner *s) {
+ int *count = brackets_count(s);
+ return !count || (*count <= 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * retract()
+ *
+ * Retract n characters
+ * ----------------------------------------------------------------------------- */
+static void retract(Scanner *s, int n) {
+ int i, l;
+ char *str;
+
+ str = Char(s->text);
+ l = Len(s->text);
+ assert(n <= l);
+ for (i = 0; i < n; i++) {
+ if (str[l - 1] == '\n') {
+ if (!s->freeze_line) s->line--;
+ }
+ (void)Seek(s->str, -1, SEEK_CUR);
+ Delitem(s->text, DOH_END);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * get_escape()
+ *
+ * Get escape sequence. Called when a backslash is found in a string
+ * ----------------------------------------------------------------------------- */
+
+static void get_escape(Scanner *s) {
+ int result = 0;
+ int state = 0;
+ int c;
+
+ while (1) {
+ c = nextchar(s);
+ if (c == 0)
+ break;
+ switch (state) {
+ case 0:
+ if (c == 'n') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\n");
+ return;
+ }
+ if (c == 'r') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\r");
+ return;
+ }
+ if (c == 't') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\t");
+ return;
+ }
+ if (c == 'a') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\a");
+ return;
+ }
+ if (c == 'b') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\b");
+ return;
+ }
+ if (c == 'f') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\f");
+ return;
+ }
+ if (c == '\\') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\\");
+ return;
+ }
+ if (c == 'v') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\v");
+ return;
+ }
+ if (c == 'e') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\033");
+ return;
+ }
+ if (c == '\'') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\'");
+ return;
+ }
+ if (c == '\"') {
+ Delitem(s->text, DOH_END);
+ Append(s->text,"\"");
+ return;
+ }
+ if (c == '\n') {
+ Delitem(s->text, DOH_END);
+ return;
+ }
+ if (isdigit(c)) {
+ state = 10;
+ result = (c - '0');
+ Delitem(s->text, DOH_END);
+ } else if (c == 'x') {
+ state = 20;
+ Delitem(s->text, DOH_END);
+ } else {
+ char tmp[3];
+ tmp[0] = '\\';
+ tmp[1] = (char)c;
+ tmp[2] = 0;
+ Delitem(s->text, DOH_END);
+ Append(s->text, tmp);
+ return;
+ }
+ break;
+ case 10:
+ if (!isdigit(c)) {
+ retract(s,1);
+ Putc((char)result,s->text);
+ return;
+ }
+ result = (result << 3) + (c - '0');
+ Delitem(s->text, DOH_END);
+ break;
+ case 20:
+ if (!isxdigit(c)) {
+ retract(s,1);
+ Putc((char)result, s->text);
+ return;
+ }
+ if (isdigit(c))
+ result = (result << 4) + (c - '0');
+ else
+ result = (result << 4) + (10 + tolower(c) - 'a');
+ Delitem(s->text, DOH_END);
+ break;
+ }
+ }
+ return;
+}
+
+/* -----------------------------------------------------------------------------
+ * look()
+ *
+ * Return the raw value of the next token.
+ * ----------------------------------------------------------------------------- */
+
+static int look(Scanner *s) {
+ int state = 0;
+ int c = 0;
+ String *str_delimiter = 0;
+
+ Clear(s->text);
+ s->start_line = s->line;
+ Setfile(s->text, Getfile(s->str));
+
+
+ while (1) {
+ switch (state) {
+ case 0:
+ if ((c = nextchar(s)) == 0)
+ return (0);
+
+ /* Process delimiters */
+
+ if (c == '\n') {
+ return SWIG_TOKEN_ENDLINE;
+ } else if (!isspace(c)) {
+ retract(s, 1);
+ state = 1000;
+ Clear(s->text);
+ Setline(s->text, s->line);
+ Setfile(s->text, Getfile(s->str));
+ }
+ break;
+
+ case 1000:
+ if ((c = nextchar(s)) == 0)
+ return (0);
+ if (c == '%')
+ state = 4; /* Possibly a SWIG directive */
+
+ /* Look for possible identifiers or unicode/delimiter strings */
+ else if ((isalpha(c)) || (c == '_') ||
+ (s->idstart && strchr(s->idstart, c))) {
+ state = 7;
+ }
+
+ /* Look for single character symbols */
+
+ else if (c == '(') {
+ brackets_push(s);
+ return SWIG_TOKEN_LPAREN;
+ }
+ else if (c == ')') {
+ brackets_pop(s);
+ return SWIG_TOKEN_RPAREN;
+ }
+ else if (c == ';') {
+ brackets_clear(s);
+ return SWIG_TOKEN_SEMI;
+ }
+ else if (c == ',')
+ return SWIG_TOKEN_COMMA;
+ else if (c == '*')
+ state = 220;
+ else if (c == '}')
+ return SWIG_TOKEN_RBRACE;
+ else if (c == '{') {
+ brackets_reset(s);
+ return SWIG_TOKEN_LBRACE;
+ }
+ else if (c == '=')
+ state = 33;
+ else if (c == '+')
+ state = 200;
+ else if (c == '-')
+ state = 210;
+ else if (c == '&')
+ state = 31;
+ else if (c == '|')
+ state = 32;
+ else if (c == '^')
+ state = 230;
+ else if (c == '<')
+ state = 60;
+ else if (c == '>')
+ state = 61;
+ else if (c == '~')
+ return SWIG_TOKEN_NOT;
+ else if (c == '!')
+ state = 3;
+ else if (c == '\\')
+ return SWIG_TOKEN_BACKSLASH;
+ else if (c == '@')
+ return SWIG_TOKEN_AT;
+ else if (c == '$')
+ state = 75;
+ else if (c == '#')
+ return SWIG_TOKEN_POUND;
+ else if (c == '?')
+ return SWIG_TOKEN_QUESTION;
+
+ /* Look for multi-character sequences */
+
+ else if (c == '/') {
+ state = 1; /* Comment (maybe) */
+ s->start_line = s->line;
+ }
+
+ else if (c == ':')
+ state = 5; /* maybe double colon */
+ else if (c == '0')
+ state = 83; /* An octal or hex value */
+ else if (c == '\"') {
+ state = 2; /* A string constant */
+ s->start_line = s->line;
+ Clear(s->text);
+ }
+ else if (c == '\'') {
+ s->start_line = s->line;
+ Clear(s->text);
+ state = 9; /* A character constant */
+ } else if (c == '`') {
+ s->start_line = s->line;
+ Clear(s->text);
+ state = 900;
+ }
+
+ else if (c == '.')
+ state = 100; /* Maybe a number, maybe ellipsis, just a period */
+ else if (c == '[')
+ state = 102; /* Maybe a bracket or a double bracket */
+ else if (c == ']')
+ state = 103; /* Maybe a bracket or a double bracket */
+ else if (isdigit(c))
+ state = 8; /* A numerical value */
+ else
+ state = 99; /* An error */
+ break;
+
+ case 1: /* Comment block */
+ if ((c = nextchar(s)) == 0)
+ return (0);
+ if (c == '/') {
+ state = 10; /* C++ style comment */
+ Clear(s->text);
+ Setline(s->text, Getline(s->str));
+ Setfile(s->text, Getfile(s->str));
+ Append(s->text, "//");
+ } else if (c == '*') {
+ state = 11; /* C style comment */
+ Clear(s->text);
+ Setline(s->text, Getline(s->str));
+ Setfile(s->text, Getfile(s->str));
+ Append(s->text, "/*");
+ } else if (c == '=') {
+ return SWIG_TOKEN_DIVEQUAL;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_SLASH;
+ }
+ break;
+ case 10: /* C++ style comment */
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated comment\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (c == '\n') {
+ retract(s,1);
+ return SWIG_TOKEN_COMMENT;
+ } else {
+ state = 10;
+ }
+ break;
+ case 11: /* C style comment block */
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated comment\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (c == '*') {
+ state = 12;
+ } else {
+ state = 11;
+ }
+ break;
+ case 12: /* Still in C style comment */
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated comment\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (c == '*') {
+ state = 12;
+ } else if (c == '/') {
+ return SWIG_TOKEN_COMMENT;
+ } else {
+ state = 11;
+ }
+ break;
+
+ case 2: /* Processing a string */
+ if (!str_delimiter) {
+ state=20;
+ break;
+ }
+
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated string\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ else if (c == '(') {
+ state = 20;
+ }
+ else {
+ char temp[2] = { 0, 0 };
+ temp[0] = c;
+ Append( str_delimiter, temp );
+ }
+
+ break;
+
+ case 20: /* Inside the string */
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated string\n");
+ return SWIG_TOKEN_ERROR;
+ }
+
+ if (!str_delimiter) { /* Ordinary string: "value" */
+ if (c == '\"') {
+ Delitem(s->text, DOH_END);
+ return SWIG_TOKEN_STRING;
+ } else if (c == '\\') {
+ Delitem(s->text, DOH_END);
+ get_escape(s);
+ }
+ } else { /* Custom delimiter string: R"XXXX(value)XXXX" */
+ if (c==')') {
+ int i=0;
+ String *end_delimiter = NewStringEmpty();
+ while ((c = nextchar(s)) != 0 && c!='\"') {
+ char temp[2] = { 0, 0 };
+ temp[0] = c;
+ Append( end_delimiter, temp );
+ i++;
+ }
+
+ if (Strcmp( str_delimiter, end_delimiter )==0) {
+ int len = Len(s->text);
+ Delslice(s->text, len - 2 - Len(str_delimiter), len); /* Delete ending )XXXX" */
+ Delslice(s->text, 0, Len(str_delimiter) + 1); /* Delete starting XXXX( */
+ Delete( end_delimiter ); /* Correct end delimiter )XXXX" occurred */
+ Delete( str_delimiter );
+ str_delimiter = 0;
+ return SWIG_TOKEN_STRING;
+ } else { /* Incorrect end delimiter occurred */
+ if (c == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated raw string, started with R\"%s( is not terminated by )%s\"\n", str_delimiter, str_delimiter);
+ return SWIG_TOKEN_ERROR;
+ }
+ retract( s, i );
+ Delete( end_delimiter );
+ }
+ }
+ }
+
+ break;
+
+ case 3: /* Maybe a not equals */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_LNOT;
+ else if (c == '=')
+ return SWIG_TOKEN_NOTEQUAL;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_LNOT;
+ }
+ break;
+
+ case 31: /* AND or Logical AND or ANDEQUAL */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_AND;
+ else if (c == '&')
+ return SWIG_TOKEN_LAND;
+ else if (c == '=')
+ return SWIG_TOKEN_ANDEQUAL;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_AND;
+ }
+ break;
+
+ case 32: /* OR or Logical OR */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_OR;
+ else if (c == '|')
+ return SWIG_TOKEN_LOR;
+ else if (c == '=')
+ return SWIG_TOKEN_OREQUAL;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_OR;
+ }
+ break;
+
+ case 33: /* EQUAL or EQUALTO */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_EQUAL;
+ else if (c == '=')
+ return SWIG_TOKEN_EQUALTO;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_EQUAL;
+ }
+ break;
+
+ case 4: /* A wrapper generator directive (maybe) */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_PERCENT;
+ if (c == '{') {
+ state = 40; /* Include block */
+ Clear(s->text);
+ Setline(s->text, Getline(s->str));
+ Setfile(s->text, Getfile(s->str));
+ s->start_line = s->line;
+ } else if (s->idstart && strchr(s->idstart, '%') &&
+ ((isalpha(c)) || (c == '_'))) {
+ state = 7;
+ } else if (c == '=') {
+ return SWIG_TOKEN_MODEQUAL;
+ } else if (c == '}') {
+ Swig_error(cparse_file, cparse_line, "Syntax error. Extraneous '%%}'\n");
+ Exit(EXIT_FAILURE);
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_PERCENT;
+ }
+ break;
+
+ case 40: /* Process an include block */
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated block\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (c == '%')
+ state = 41;
+ break;
+ case 41: /* Still processing include block */
+ if ((c = nextchar(s)) == 0) {
+ set_error(s,s->start_line,"Unterminated code block");
+ return 0;
+ }
+ if (c == '}') {
+ Delitem(s->text, DOH_END);
+ Delitem(s->text, DOH_END);
+ Seek(s->text,0,SEEK_SET);
+ return SWIG_TOKEN_CODEBLOCK;
+ } else {
+ state = 40;
+ }
+ break;
+
+ case 5: /* Maybe a double colon */
+
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_COLON;
+ if (c == ':')
+ state = 50;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_COLON;
+ }
+ break;
+
+ case 50: /* DCOLON, DCOLONSTAR */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_DCOLON;
+ else if (c == '*')
+ return SWIG_TOKEN_DCOLONSTAR;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_DCOLON;
+ }
+ break;
+
+ case 60: /* shift operators */
+ if ((c = nextchar(s)) == 0) {
+ brackets_increment(s);
+ return SWIG_TOKEN_LESSTHAN;
+ }
+ if (c == '<')
+ state = 240;
+ else if (c == '=') {
+ if ((c = nextchar(s)) == 0) {
+ return SWIG_TOKEN_LTEQUAL;
+ } else if (c == '>' && cparse_cplusplus) { /* Spaceship operator */
+ return SWIG_TOKEN_LTEQUALGT;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_LTEQUAL;
+ }
+ } else {
+ retract(s, 1);
+ brackets_increment(s);
+ return SWIG_TOKEN_LESSTHAN;
+ }
+ break;
+ case 61:
+ if ((c = nextchar(s)) == 0) {
+ brackets_decrement(s);
+ return SWIG_TOKEN_GREATERTHAN;
+ }
+ if (c == '>' && brackets_allow_shift(s))
+ state = 250;
+ else if (c == '=')
+ return SWIG_TOKEN_GTEQUAL;
+ else {
+ retract(s, 1);
+ brackets_decrement(s);
+ return SWIG_TOKEN_GREATERTHAN;
+ }
+ break;
+
+ case 7: /* Identifier or true/false or unicode/custom delimiter string */
+ if (c == 'R') { /* Possibly CUSTOM DELIMITER string */
+ state = 72;
+ break;
+ }
+ else if (c == 'L') { /* Probably identifier but may be a wide string literal */
+ state = 77;
+ break;
+ }
+ else if (c != 'u' && c != 'U') { /* Definitely an identifier */
+ state = 70;
+ break;
+ }
+
+ if ((c = nextchar(s)) == 0) {
+ state = 76;
+ }
+ else if (c == '\"') { /* Definitely u, U or L string */
+ retract(s, 1);
+ state = 1000;
+ }
+ else if (c == '\'') { /* Definitely u, U or L char */
+ retract(s, 1);
+ state = 77;
+ }
+ else if (c == 'R') { /* Possibly CUSTOM DELIMITER u, U, L string */
+ state = 73;
+ }
+ else if (c == '8') { /* Possibly u8 string/char */
+ state = 71;
+ }
+ else {
+ retract(s, 1); /* Definitely an identifier */
+ state = 70;
+ }
+ break;
+
+ case 70: /* Identifier */
+ if ((c = nextchar(s)) == 0)
+ state = 76;
+ else if (isalnum(c) || (c == '_') || (c == '$')) {
+ state = 70;
+ } else {
+ retract(s, 1);
+ state = 76;
+ }
+ break;
+
+ case 71: /* Possibly u8 string/char */
+ if ((c = nextchar(s)) == 0) {
+ state = 76;
+ }
+ else if (c=='\"') {
+ retract(s, 1); /* Definitely u8 string */
+ state = 1000;
+ }
+ else if (c=='\'') {
+ retract(s, 1); /* Definitely u8 char */
+ state = 77;
+ }
+ else if (c=='R') {
+ state = 74; /* Possibly CUSTOM DELIMITER u8 string */
+ }
+ else {
+ retract(s, 2); /* Definitely an identifier. Retract 8" */
+ state = 70;
+ }
+
+ break;
+
+ case 72: /* Possibly CUSTOM DELIMITER string */
+ case 73:
+ case 74:
+ if ((c = nextchar(s)) == 0) {
+ state = 76;
+ }
+ else if (c=='\"') {
+ retract(s, 1); /* Definitely custom delimiter u, U or L string */
+ str_delimiter = NewStringEmpty();
+ state = 1000;
+ }
+ else {
+ if (state==72) {
+ retract(s, 1); /* Definitely an identifier. Retract ? */
+ }
+ else if (state==73) {
+ retract(s, 2); /* Definitely an identifier. Retract R? */
+ }
+ else if (state==74) {
+ retract(s, 3); /* Definitely an identifier. Retract 8R? */
+ }
+ state = 70;
+ }
+
+ break;
+
+ case 75: /* Special identifier $ */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_DOLLAR;
+ if (isalnum(c) || (c == '_') || (c == '*') || (c == '&')) {
+ state = 70;
+ } else {
+ retract(s,1);
+ if (Len(s->text) == 1) return SWIG_TOKEN_DOLLAR;
+ state = 76;
+ }
+ break;
+
+ case 76: /* Identifier or true/false */
+ if (cparse_cplusplus) {
+ if (Strcmp(s->text, "true") == 0)
+ return SWIG_TOKEN_BOOL;
+ else if (Strcmp(s->text, "false") == 0)
+ return SWIG_TOKEN_BOOL;
+ }
+ return SWIG_TOKEN_ID;
+ break;
+
+ case 77: /*identifier or wide string literal*/
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_ID;
+ else if (c == '\"') {
+ s->start_line = s->line;
+ Clear(s->text);
+ state = 78;
+ }
+ else if (c == '\'') {
+ s->start_line = s->line;
+ Clear(s->text);
+ state = 79;
+ }
+ else if (isalnum(c) || (c == '_') || (c == '$'))
+ state = 7;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_ID;
+ }
+ break;
+
+ case 78: /* Processing a wide string literal*/
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated wide string\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (c == '\"') {
+ Delitem(s->text, DOH_END);
+ return SWIG_TOKEN_WSTRING;
+ } else if (c == '\\') {
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated wide string\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ }
+ break;
+
+ case 79: /* Processing a wide char literal */
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated wide character constant\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (c == '\'') {
+ Delitem(s->text, DOH_END);
+ return (SWIG_TOKEN_WCHAR);
+ } else if (c == '\\') {
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated wide character literal\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ }
+ break;
+
+ case 8: /* A numerical digit */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_INT;
+ if (c == '.') {
+ state = 81;
+ } else if ((c == 'e') || (c == 'E')) {
+ state = 82;
+ } else if ((c == 'f') || (c == 'F')) {
+ Delitem(s->text, DOH_END);
+ return SWIG_TOKEN_FLOAT;
+ } else if (isdigit(c)) {
+ state = 8;
+ } else if ((c == 'l') || (c == 'L')) {
+ state = 87;
+ } else if ((c == 'u') || (c == 'U')) {
+ state = 88;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_INT;
+ }
+ break;
+ case 81: /* A floating pointer number of some sort */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_DOUBLE;
+ if (isdigit(c))
+ state = 81;
+ else if ((c == 'e') || (c == 'E'))
+ state = 820;
+ else if ((c == 'f') || (c == 'F')) {
+ Delitem(s->text, DOH_END);
+ return SWIG_TOKEN_FLOAT;
+ } else if ((c == 'l') || (c == 'L')) {
+ Delitem(s->text, DOH_END);
+ return SWIG_TOKEN_DOUBLE;
+ } else {
+ retract(s, 1);
+ return (SWIG_TOKEN_DOUBLE);
+ }
+ break;
+ case 82:
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Exponent does not have any digits\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if ((isdigit(c)) || (c == '-') || (c == '+'))
+ state = 86;
+ else {
+ retract(s, 2);
+ Swig_error(cparse_file, cparse_start_line, "Exponent does not have any digits\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ break;
+ case 820:
+ /* Like case 82, but we've seen a decimal point. */
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Exponent does not have any digits\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if ((isdigit(c)) || (c == '-') || (c == '+'))
+ state = 86;
+ else {
+ retract(s, 2);
+ Swig_error(cparse_file, cparse_start_line, "Exponent does not have any digits\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ break;
+ case 83:
+ /* Might be a hexadecimal or octal number */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_INT;
+ if (isdigit(c))
+ state = 84;
+ else if ((c == 'e') || (c == 'E'))
+ state = 82;
+ else if ((c == 'x') || (c == 'X'))
+ state = 85;
+ else if ((c == 'b') || (c == 'B'))
+ state = 850;
+ else if (c == '.')
+ state = 81;
+ else if ((c == 'l') || (c == 'L')) {
+ state = 87;
+ } else if ((c == 'u') || (c == 'U')) {
+ state = 88;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_INT;
+ }
+ break;
+ case 84:
+ /* This is an octal number */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_INT;
+ if (isdigit(c))
+ state = 84;
+ else if (c == '.')
+ state = 81;
+ else if ((c == 'e') || (c == 'E'))
+ state = 82;
+ else if ((c == 'l') || (c == 'L')) {
+ state = 87;
+ } else if ((c == 'u') || (c == 'U')) {
+ state = 88;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_INT;
+ }
+ break;
+ case 85:
+ /* This is an hex number */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_INT;
+ if (isxdigit(c))
+ state = 85;
+ else if (c == '.') /* hexadecimal float */
+ state = 860;
+ else if ((c == 'p') || (c == 'P')) /* hexadecimal float */
+ state = 820;
+ else if ((c == 'l') || (c == 'L')) {
+ state = 87;
+ } else if ((c == 'u') || (c == 'U')) {
+ state = 88;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_INT;
+ }
+ break;
+ case 850:
+ /* This is a binary number */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_INT;
+ if ((c == '0') || (c == '1'))
+ state = 850;
+ else if ((c == 'l') || (c == 'L')) {
+ state = 87;
+ } else if ((c == 'u') || (c == 'U')) {
+ state = 88;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_INT;
+ }
+ break;
+ case 860:
+ /* hexadecimal float */
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Hexadecimal floating literals require an exponent\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (isxdigit(c))
+ state = 860;
+ else if ((c == 'p') || (c == 'P'))
+ state = 820;
+ else {
+ retract(s, 2);
+ Swig_error(cparse_file, cparse_start_line, "Hexadecimal floating literals require an exponent\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ break;
+ case 86:
+ /* Rest of floating point number */
+
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_DOUBLE;
+ if (isdigit(c))
+ state = 86;
+ else if ((c == 'f') || (c == 'F')) {
+ Delitem(s->text, DOH_END);
+ return SWIG_TOKEN_FLOAT;
+ } else if ((c == 'l') || (c == 'L')) {
+ Delitem(s->text, DOH_END);
+ return SWIG_TOKEN_DOUBLE;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_DOUBLE;
+ }
+ break;
+
+ case 87:
+ /* A long integer of some sort */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_LONG;
+ if ((c == 'u') || (c == 'U')) {
+ return SWIG_TOKEN_ULONG;
+ } else if ((c == 'l') || (c == 'L')) {
+ state = 870;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_LONG;
+ }
+ break;
+
+ /* A long long integer */
+
+ case 870:
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_LONGLONG;
+ if ((c == 'u') || (c == 'U')) {
+ return SWIG_TOKEN_ULONGLONG;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_LONGLONG;
+ }
+
+ /* An unsigned number */
+ case 88:
+
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_UINT;
+ if ((c == 'l') || (c == 'L')) {
+ state = 880;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_UINT;
+ }
+ break;
+
+ /* Possibly an unsigned long long or unsigned long */
+ case 880:
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_ULONG;
+ if ((c == 'l') || (c == 'L'))
+ return SWIG_TOKEN_ULONGLONG;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_ULONG;
+ }
+
+ /* A character constant */
+ case 9:
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated character constant\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (c == '\'') {
+ Delitem(s->text, DOH_END);
+ return (SWIG_TOKEN_CHAR);
+ } else if (c == '\\') {
+ Delitem(s->text, DOH_END);
+ get_escape(s);
+ }
+ break;
+
+ /* A period or an ellipsis or maybe a floating point number */
+
+ case 100:
+ if ((c = nextchar(s)) == 0)
+ return (0);
+ if (isdigit(c))
+ state = 81;
+ else if (c == '.')
+ state = 101;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_PERIOD;
+ }
+ break;
+
+ /* An ellipsis */
+
+ case 101:
+ if ((c = nextchar(s)) == 0)
+ return (0);
+ if (c == '.') {
+ return SWIG_TOKEN_ELLIPSIS;
+ } else {
+ retract(s, 2);
+ return SWIG_TOKEN_PERIOD;
+ }
+ break;
+
+ /* A left bracket or a double left bracket */
+ case 102:
+
+ if ((c = nextchar(s)) == 0) {
+ return SWIG_TOKEN_LBRACKET;
+ } else if (c == '[') {
+ return SWIG_TOKEN_LLBRACKET;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_LBRACKET;
+ }
+ break;
+
+ /* a right bracket or a double right bracket */
+ case 103:
+ if ((c = nextchar(s)) == 0) {
+ return SWIG_TOKEN_RBRACKET;
+ } else if (c == ']') {
+ return SWIG_TOKEN_RRBRACKET;
+ } else {
+ retract(s, 1);
+ return SWIG_TOKEN_RBRACKET;
+ }
+ break;
+
+ case 200: /* PLUS, PLUSPLUS, PLUSEQUAL */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_PLUS;
+ else if (c == '+')
+ return SWIG_TOKEN_PLUSPLUS;
+ else if (c == '=')
+ return SWIG_TOKEN_PLUSEQUAL;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_PLUS;
+ }
+ break;
+
+ case 210: /* MINUS, MINUSMINUS, MINUSEQUAL, ARROW */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_MINUS;
+ else if (c == '-')
+ return SWIG_TOKEN_MINUSMINUS;
+ else if (c == '=')
+ return SWIG_TOKEN_MINUSEQUAL;
+ else if (c == '>')
+ state = 211;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_MINUS;
+ }
+ break;
+
+ case 211: /* ARROW, ARROWSTAR */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_ARROW;
+ else if (c == '*')
+ return SWIG_TOKEN_ARROWSTAR;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_ARROW;
+ }
+ break;
+
+
+ case 220: /* STAR, TIMESEQUAL */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_STAR;
+ else if (c == '=')
+ return SWIG_TOKEN_TIMESEQUAL;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_STAR;
+ }
+ break;
+
+ case 230: /* XOR, XOREQUAL */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_XOR;
+ else if (c == '=')
+ return SWIG_TOKEN_XOREQUAL;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_XOR;
+ }
+ break;
+
+ case 240: /* LSHIFT, LSEQUAL */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_LSHIFT;
+ else if (c == '=')
+ return SWIG_TOKEN_LSEQUAL;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_LSHIFT;
+ }
+ break;
+
+ case 250: /* RSHIFT, RSEQUAL */
+ if ((c = nextchar(s)) == 0)
+ return SWIG_TOKEN_RSHIFT;
+ else if (c == '=')
+ return SWIG_TOKEN_RSEQUAL;
+ else {
+ retract(s, 1);
+ return SWIG_TOKEN_RSHIFT;
+ }
+ break;
+
+
+ /* An illegal character */
+
+ /* Reverse string */
+ case 900:
+ if ((c = nextchar(s)) == 0) {
+ Swig_error(cparse_file, cparse_start_line, "Unterminated character constant\n");
+ return SWIG_TOKEN_ERROR;
+ }
+ if (c == '`') {
+ Delitem(s->text, DOH_END);
+ return (SWIG_TOKEN_RSTRING);
+ }
+ break;
+
+ default:
+ return SWIG_TOKEN_ILLEGAL;
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_token()
+ *
+ * Real entry point to return the next token. Returns 0 if at end of input.
+ * ----------------------------------------------------------------------------- */
+
+int Scanner_token(Scanner *s) {
+ int t;
+ Delete(s->error);
+ if (s->nexttoken >= 0) {
+ t = s->nexttoken;
+ s->nexttoken = -1;
+ return t;
+ }
+ s->start_line = 0;
+ t = look(s);
+ if (!s->start_line) {
+ Setline(s->text,s->line);
+ } else {
+ Setline(s->text,s->start_line);
+ }
+ return t;
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_text()
+ *
+ * Return the lexene associated with the last returned token.
+ * ----------------------------------------------------------------------------- */
+
+String *Scanner_text(Scanner *s) {
+ return s->text;
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_skip_line()
+ *
+ * Skips to the end of a line
+ * ----------------------------------------------------------------------------- */
+
+void Scanner_skip_line(Scanner *s) {
+ char c;
+ int done = 0;
+ Clear(s->text);
+ Setfile(s->text, Getfile(s->str));
+ Setline(s->text, s->line);
+ while (!done) {
+ if ((c = nextchar(s)) == 0)
+ return;
+ if (c == '\\') {
+ nextchar(s);
+ } else if (c == '\n') {
+ done = 1;
+ }
+ }
+ return;
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_skip_balanced()
+ *
+ * Skips a piece of code enclosed in begin/end symbols such as '{...}' or
+ * (...). Ignores symbols inside comments or strings.
+ * ----------------------------------------------------------------------------- */
+
+int Scanner_skip_balanced(Scanner *s, int startchar, int endchar) {
+ char c;
+ int num_levels = 1;
+ int state = 0;
+ char temp[2] = { 0, 0 };
+ String *locator = 0;
+ temp[0] = (char) startchar;
+ Clear(s->text);
+ Setfile(s->text, Getfile(s->str));
+ Setline(s->text, s->line);
+
+ Append(s->text, temp);
+ while (num_levels > 0) {
+ if ((c = nextchar(s)) == 0) {
+ Delete(locator);
+ return -1;
+ }
+ switch (state) {
+ case 0:
+ if (c == startchar)
+ num_levels++;
+ else if (c == endchar)
+ num_levels--;
+ else if (c == '/')
+ state = 10;
+ else if (c == '\"')
+ state = 20;
+ else if (c == '\'')
+ state = 30;
+ break;
+ case 10:
+ if (c == '/')
+ state = 11;
+ else if (c == '*')
+ state = 12;
+ else if (c == startchar) {
+ state = 0;
+ num_levels++;
+ }
+ else
+ state = 0;
+ break;
+ case 11:
+ if (c == '\n')
+ state = 0;
+ else
+ state = 11;
+ break;
+ case 12: /* first character inside C comment */
+ if (c == '*')
+ state = 14;
+ else if (c == '@')
+ state = 40;
+ else
+ state = 13;
+ break;
+ case 13:
+ if (c == '*')
+ state = 14;
+ break;
+ case 14: /* possible end of C comment */
+ if (c == '*')
+ state = 14;
+ else if (c == '/')
+ state = 0;
+ else
+ state = 13;
+ break;
+ case 20:
+ if (c == '\"')
+ state = 0;
+ else if (c == '\\')
+ state = 21;
+ break;
+ case 21:
+ state = 20;
+ break;
+ case 30:
+ if (c == '\'')
+ state = 0;
+ else if (c == '\\')
+ state = 31;
+ break;
+ case 31:
+ state = 30;
+ break;
+ /* 40-45 SWIG locator checks - a C comment with contents starting: @SWIG */
+ case 40:
+ state = (c == 'S') ? 41 : (c == '*') ? 14 : 13;
+ break;
+ case 41:
+ state = (c == 'W') ? 42 : (c == '*') ? 14 : 13;
+ break;
+ case 42:
+ state = (c == 'I') ? 43 : (c == '*') ? 14 : 13;
+ break;
+ case 43:
+ state = (c == 'G') ? 44 : (c == '*') ? 14 : 13;
+ if (c == 'G') {
+ Delete(locator);
+ locator = NewString("/*@SWIG");
+ }
+ break;
+ case 44:
+ if (c == '*')
+ state = 45;
+ Putc(c, locator);
+ break;
+ case 45: /* end of SWIG locator in C comment */
+ if (c == '/') {
+ state = 0;
+ Putc(c, locator);
+ Scanner_locator(s, locator);
+ } else {
+ /* malformed locator */
+ state = (c == '*') ? 14 : 13;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ Delete(locator);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Scanner_get_raw_text_balanced()
+ *
+ * Returns raw text between 2 braces, does not change scanner state in any way
+ * ----------------------------------------------------------------------------- */
+
+String *Scanner_get_raw_text_balanced(Scanner *s, int startchar, int endchar) {
+ String *result = 0;
+ char c;
+ int old_line = s->line;
+ String *old_text = Copy(s->text);
+ long position = Tell(s->str);
+
+ int num_levels = 1;
+ int state = 0;
+ char temp[2] = { 0, 0 };
+ temp[0] = (char) startchar;
+ Clear(s->text);
+ Setfile(s->text, Getfile(s->str));
+ Setline(s->text, s->line);
+ Append(s->text, temp);
+ while (num_levels > 0) {
+ if ((c = nextchar(s)) == 0) {
+ Clear(s->text);
+ Append(s->text, old_text);
+ Delete(old_text);
+ s->line = old_line;
+ return 0;
+ }
+ switch (state) {
+ case 0:
+ if (c == startchar)
+ num_levels++;
+ else if (c == endchar)
+ num_levels--;
+ else if (c == '/')
+ state = 10;
+ else if (c == '\"')
+ state = 20;
+ else if (c == '\'')
+ state = 30;
+ break;
+ case 10:
+ if (c == '/')
+ state = 11;
+ else if (c == '*')
+ state = 12;
+ else if (c == startchar) {
+ state = 0;
+ num_levels++;
+ }
+ else
+ state = 0;
+ break;
+ case 11:
+ if (c == '\n')
+ state = 0;
+ else
+ state = 11;
+ break;
+ case 12: /* first character inside C comment */
+ if (c == '*')
+ state = 14;
+ else
+ state = 13;
+ break;
+ case 13:
+ if (c == '*')
+ state = 14;
+ break;
+ case 14: /* possible end of C comment */
+ if (c == '*')
+ state = 14;
+ else if (c == '/')
+ state = 0;
+ else
+ state = 13;
+ break;
+ case 20:
+ if (c == '\"')
+ state = 0;
+ else if (c == '\\')
+ state = 21;
+ break;
+ case 21:
+ state = 20;
+ break;
+ case 30:
+ if (c == '\'')
+ state = 0;
+ else if (c == '\\')
+ state = 31;
+ break;
+ case 31:
+ state = 30;
+ break;
+ default:
+ break;
+ }
+ }
+ Seek(s->str, position, SEEK_SET);
+ result = Copy(s->text);
+ Clear(s->text);
+ Append(s->text, old_text);
+ Delete(old_text);
+ s->line = old_line;
+ return result;
+}
+/* -----------------------------------------------------------------------------
+ * Scanner_isoperator()
+ *
+ * Returns 0 or 1 depending on whether or not a token corresponds to a C/C++
+ * operator.
+ * ----------------------------------------------------------------------------- */
+
+int Scanner_isoperator(int tokval) {
+ if (tokval >= 100) return 1;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------
+ * locator()
+ *
+ * Support for locator strings. These are strings of the form
+ * @SWIG:filename,line,id@ emitted by the SWIG preprocessor. They
+ * are primarily used for macro line number reporting.
+ * We just use the locator to mark when to activate/deactivate linecounting.
+ * ---------------------------------------------------------------------- */
+
+
+void Scanner_locator(Scanner *s, String *loc) {
+ static Locator *locs = 0;
+ static int expanding_macro = 0;
+
+ if (!follow_locators) {
+ if (Equal(loc, "/*@SWIG@*/")) {
+ /* End locator. */
+ if (expanding_macro)
+ --expanding_macro;
+ } else {
+ /* Begin locator. */
+ ++expanding_macro;
+ }
+ /* Freeze line number processing in Scanner */
+ freeze_line(s,expanding_macro);
+ } else {
+ int c;
+ Locator *l;
+ (void)Seek(loc, 7, SEEK_SET);
+ c = Getc(loc);
+ if (c == '@') {
+ /* Empty locator. We pop the last location off */
+ if (locs) {
+ Scanner_set_location(s, locs->filename, locs->line_number);
+ cparse_file = locs->filename;
+ cparse_line = locs->line_number;
+ l = locs->next;
+ Free(locs);
+ locs = l;
+ }
+ return;
+ }
+
+ /* We're going to push a new location */
+ l = (Locator *) Malloc(sizeof(Locator));
+ l->filename = cparse_file;
+ l->line_number = cparse_line;
+ l->next = locs;
+ locs = l;
+
+ /* Now, parse the new location out of the locator string */
+ {
+ String *fn = NewStringEmpty();
+ /* Putc(c, fn); */
+
+ while ((c = Getc(loc)) != EOF) {
+ if ((c == '@') || (c == ','))
+ break;
+ Putc(c, fn);
+ }
+ cparse_file = Swig_copy_string(Char(fn));
+ Clear(fn);
+ cparse_line = 1;
+ /* Get the line number */
+ while ((c = Getc(loc)) != EOF) {
+ if ((c == '@') || (c == ','))
+ break;
+ Putc(c, fn);
+ }
+ cparse_line = atoi(Char(fn));
+ Clear(fn);
+
+ /* Get the rest of it */
+ while ((c = Getc(loc)) != EOF) {
+ if (c == '@')
+ break;
+ Putc(c, fn);
+ }
+ /* Swig_diagnostic(cparse_file, cparse_line, "Scanner_set_location\n"); */
+ Scanner_set_location(s, cparse_file, cparse_line);
+ Delete(fn);
+ }
+ }
+}
+
+void Swig_cparse_follow_locators(int v) {
+ follow_locators = v;
+}
+
+
diff --git a/contrib/tools/swig/Source/Swig/stype.c b/contrib/tools/swig/Source/Swig/stype.c
new file mode 100644
index 0000000000..f227778f6c
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/stype.c
@@ -0,0 +1,1422 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * stype.c
+ *
+ * This file provides general support for datatypes that are encoded in
+ * the form of simple strings.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+#include <ctype.h>
+
+/* -----------------------------------------------------------------------------
+ * Synopsis
+ *
+ * The purpose of this module is to provide a general purpose type representation
+ * based on simple text strings.
+ *
+ * General idea:
+ *
+ * Types are represented by a base type (e.g., "int") and a collection of
+ * type operators applied to the base (e.g., pointers, arrays, etc...).
+ *
+ * Encoding:
+ *
+ * Types are encoded as strings of type constructors such as follows:
+ *
+ * String Encoding C Example
+ * --------------- ---------
+ * p.p.int int **
+ * a(300).a(400).int int [300][400]
+ * p.q(const).char char const *
+ *
+ * All type constructors are denoted by a trailing '.':
+ *
+ * 'p.' = Pointer (*)
+ * 'r.' = Reference (&)
+ * 'z.' = Rvalue reference (&&)
+ * 'a(n).' = Array of size n [n]
+ * 'f(..,..).' = Function with arguments (args)
+ * 'q(str).' = Qualifier, such as const or volatile (cv-qualifier)
+ * 'm(cls).' = Pointer to member (cls::*)
+ *
+ * The encoding follows the order that you might describe a type in words.
+ * For example "p.a(200).int" is "A pointer to array of int's" and
+ * "p.q(const).char" is "a pointer to a const char".
+ *
+ * This representation of types is fairly convenient because ordinary string
+ * operations can be used for type manipulation. For example, a type could be
+ * formed by combining two strings such as the following:
+ *
+ * "p.p." + "a(400).int" = "p.p.a(400).int"
+ *
+ * Similarly, one could strip a 'const' declaration from a type doing something
+ * like this:
+ *
+ * Replace(t,"q(const).","",DOH_REPLACE_ANY)
+ *
+ * More examples:
+ *
+ * String Encoding C++ Example
+ * --------------- -----------
+ * p.f(bool).r.q(const).long const long & (*)(bool)
+ * m(Funcs).q(const).f(bool).long long (Funcs::*)(bool) const
+ * r.q(const).m(Funcs).f(int).long long (Funcs::*const &)(int)
+ * m(Funcs).z.q(const).f(bool).long long (Funcs::*)(bool) const &&
+ *
+ * Function decl examples:
+ *
+ * f(bool). long a(bool);
+ * r.f(bool). long b(bool) &;
+ * z.f(bool). long c(bool) &&;
+ * z.q(const).f(bool). long d(bool) const &&;
+ *
+ * For the most part, this module tries to minimize the use of special
+ * characters (*, [, <, etc...) in its type encoding. One reason for this
+ * is that SWIG might be extended to encode data in formats such as XML
+ * where you might want to do this:
+ *
+ * <function>
+ * <type>p.p.int</type>
+ * ...
+ * </function>
+ *
+ * Or alternatively,
+ *
+ * <function type="p.p.int" ...>blah</function>
+ *
+ * In either case, it's probably best to avoid characters such as '&', '*', or '<'.
+ *
+ * Why not use C syntax? Well, C syntax is fairly complicated to parse
+ * and not particularly easy to manipulate---especially for adding, deleting and
+ * composing type constructors. The string representation presented here makes
+ * this pretty easy.
+ *
+ * Why not use a bunch of nested data structures? Are you kidding? How
+ * would that be easier to use than a few simple string operations?
+ * ----------------------------------------------------------------------------- */
+
+
+SwigType *NewSwigType(int t) {
+ switch (t) {
+ case T_BOOL:
+ return NewString("bool");
+ break;
+ case T_INT:
+ return NewString("int");
+ break;
+ case T_UINT:
+ return NewString("unsigned int");
+ break;
+ case T_SHORT:
+ return NewString("short");
+ break;
+ case T_USHORT:
+ return NewString("unsigned short");
+ break;
+ case T_LONG:
+ return NewString("long");
+ break;
+ case T_ULONG:
+ return NewString("unsigned long");
+ break;
+ case T_FLOAT:
+ return NewString("float");
+ break;
+ case T_DOUBLE:
+ return NewString("double");
+ break;
+ case T_COMPLEX:
+ return NewString("_Complex");
+ break;
+ case T_CHAR:
+ return NewString("char");
+ break;
+ case T_SCHAR:
+ return NewString("signed char");
+ break;
+ case T_UCHAR:
+ return NewString("unsigned char");
+ break;
+ case T_STRING: {
+ SwigType *t = NewString("char");
+ SwigType_add_qualifier(t, "const");
+ SwigType_add_pointer(t);
+ return t;
+ break;
+ }
+ case T_WCHAR:
+ return NewString("wchar_t");
+ break;
+ case T_WSTRING: {
+ SwigType *t = NewString("wchar_t");
+ SwigType_add_pointer(t);
+ return t;
+ break;
+ }
+ case T_LONGLONG:
+ return NewString("long long");
+ break;
+ case T_ULONGLONG:
+ return NewString("unsigned long long");
+ break;
+ case T_VOID:
+ return NewString("void");
+ break;
+ case T_AUTO:
+ return NewString("auto");
+ break;
+ default:
+ break;
+ }
+ return NewStringEmpty();
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_push()
+ *
+ * Push a type constructor onto the type
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_push(SwigType *t, String *cons) {
+ if (!cons)
+ return;
+ if (!Len(cons))
+ return;
+
+ if (Len(t)) {
+ char *c = Char(cons);
+ if (c[strlen(c) - 1] != '.')
+ Insert(t, 0, ".");
+ }
+ Insert(t, 0, cons);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_ispointer_return()
+ *
+ * Testing functions for querying a raw datatype
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_ispointer_return(const SwigType *t) {
+ char *c;
+ int idx;
+ if (!t)
+ return 0;
+ c = Char(t);
+ idx = (int)strlen(c) - 4;
+ if (idx >= 0) {
+ return (strcmp(c + idx, ").p.") == 0);
+ }
+ return 0;
+}
+
+int SwigType_isreference_return(const SwigType *t) {
+ char *c;
+ int idx;
+ if (!t)
+ return 0;
+ c = Char(t);
+ idx = (int)strlen(c) - 4;
+ if (idx >= 0) {
+ return (strcmp(c + idx, ").r.") == 0);
+ }
+ return 0;
+}
+
+int SwigType_isconst(const SwigType *t) {
+ char *c;
+ if (!t)
+ return 0;
+ c = Char(t);
+ if (strncmp(c, "q(", 2) == 0) {
+ String *q = SwigType_parm(t);
+ if (strstr(Char(q), "const")) {
+ Delete(q);
+ return 1;
+ }
+ Delete(q);
+ }
+ /* Hmmm. Might be const through a typedef */
+ if (SwigType_issimple(t)) {
+ int ret;
+ SwigType *td = SwigType_typedef_resolve(t);
+ if (td) {
+ ret = SwigType_isconst(td);
+ Delete(td);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int SwigType_ismutable(const SwigType *t) {
+ int r;
+ SwigType *qt = SwigType_typedef_resolve_all(t);
+ if (SwigType_isreference(qt) || SwigType_isrvalue_reference(qt) || SwigType_isarray(qt)) {
+ Delete(SwigType_pop(qt));
+ }
+ r = SwigType_isconst(qt);
+ Delete(qt);
+ return r ? 0 : 1;
+}
+
+int SwigType_isenum(const SwigType *t) {
+ char *c = Char(t);
+ if (!t)
+ return 0;
+ if (strncmp(c, "enum ", 5) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+int SwigType_issimple(const SwigType *t) {
+ char *c = Char(t);
+ if (!t)
+ return 0;
+ while (*c) {
+ if (*c == '<') {
+ int nest = 1;
+ c++;
+ while (*c && nest) {
+ if (*c == '<')
+ nest++;
+ if (*c == '>')
+ nest--;
+ c++;
+ }
+ c--;
+ }
+ if (*c == '.')
+ return 0;
+ c++;
+ }
+ return 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_default_create()
+ *
+ * Create the default type for this datatype. This takes a type and strips it
+ * down to a generic form first by resolving all typedefs.
+ *
+ * Rules:
+ * Pointers: p.SWIGTYPE
+ * References: r.SWIGTYPE
+ * Arrays no dimension: a().SWIGTYPE
+ * Arrays with dimension: a(ANY).SWIGTYPE
+ * Member pointer: m(CLASS).SWIGTYPE
+ * Function pointer: f(ANY).SWIGTYPE
+ * Enums: enum SWIGTYPE
+ * Types: SWIGTYPE
+ *
+ * Examples (also see SwigType_default_deduce):
+ *
+ * int [2][4]
+ * a(2).a(4).int
+ * a(ANY).a(ANY).SWIGTYPE
+ *
+ * struct A {};
+ * typedef A *Aptr;
+ * Aptr const &
+ * r.q(const).Aptr
+ * r.q(const).p.SWIGTYPE
+ *
+ * enum E {e1, e2};
+ * enum E const &
+ * r.q(const).enum E
+ * r.q(const).enum SWIGTYPE
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_default_create(const SwigType *ty) {
+ SwigType *r = 0;
+ List *l;
+ Iterator it;
+ int numitems;
+
+ if (!SwigType_isvarargs(ty)) {
+ SwigType *t = SwigType_typedef_resolve_all(ty);
+ r = NewStringEmpty();
+ l = SwigType_split(t);
+ numitems = Len(l);
+
+ if (numitems >= 1) {
+ String *last_subtype = Getitem(l, numitems-1);
+ if (SwigType_isenum(last_subtype))
+ Setitem(l, numitems-1, NewString("enum SWIGTYPE"));
+ else
+ Setitem(l, numitems-1, NewString("SWIGTYPE"));
+ }
+
+ for (it = First(l); it.item; it = Next(it)) {
+ String *subtype = it.item;
+ if (SwigType_isarray(subtype)) {
+ if (Equal(subtype, "a()."))
+ Append(r, NewString("a()."));
+ else
+ Append(r, NewString("a(ANY)."));
+ } else if (SwigType_isfunction(subtype)) {
+ Append(r, NewString("f(ANY).SWIGTYPE"));
+ break;
+ } else if (SwigType_ismemberpointer(subtype)) {
+ Append(r, NewString("m(CLASS).SWIGTYPE"));
+ break;
+ } else {
+ Append(r, subtype);
+ }
+ }
+
+ Delete(l);
+ Delete(t);
+ }
+
+ return r;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_default_deduce()
+ *
+ * This function implements type deduction used in the typemap matching rules
+ * and is very close to the type deduction used in partial template class
+ * specialization matching in that the most specialized type is always chosen.
+ * SWIGTYPE is used as the generic type. The basic idea is to repeatedly call
+ * this function to find a deduced type until nothing matches.
+ *
+ * The type t must have already been converted to the default type via a call to
+ * SwigType_default_create() before calling this function.
+ *
+ * Example deductions (matching the examples described in SwigType_default_create),
+ * where the most specialized matches are highest in the list:
+ *
+ * a(ANY).a(ANY).SWIGTYPE
+ * a(ANY).a().SWIGTYPE
+ * a(ANY).p.SWIGTYPE
+ * a(ANY).SWIGTYPE
+ * a().SWIGTYPE
+ * p.SWIGTYPE
+ * SWIGTYPE
+ *
+ * r.q(const).p.SWIGTYPE
+ * r.q(const).SWIGTYPE
+ * r.SWIGTYPE
+ * SWIGTYPE
+ *
+ * r.q(const).enum SWIGTYPE
+ * r.enum SWIGTYPE
+ * r.SWIGTYPE
+ * SWIGTYPE
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_default_deduce(const SwigType *t) {
+ SwigType *r = NewStringEmpty();
+ List *l;
+ Iterator it;
+ int numitems;
+
+ l = SwigType_split(t);
+
+ numitems = Len(l);
+ if (numitems >= 1) {
+ String *last_subtype = Getitem(l, numitems-1);
+ int is_enum = SwigType_isenum(last_subtype);
+
+ if (numitems >=2 ) {
+ String *subtype = Getitem(l, numitems-2); /* last but one */
+ if (SwigType_isarray(subtype)) {
+ if (is_enum) {
+ /* enum deduction, enum SWIGTYPE => SWIGTYPE */
+ Setitem(l, numitems-1, NewString("SWIGTYPE"));
+ } else {
+ /* array deduction, a(ANY). => a(). => p. */
+ String *deduced_subtype = 0;
+ if (Strcmp(subtype, "a().") == 0) {
+ deduced_subtype = NewString("p.");
+ } else if (Strcmp(subtype, "a(ANY).") == 0) {
+ deduced_subtype = NewString("a().");
+ } else {
+ assert(0);
+ }
+ Setitem(l, numitems-2, deduced_subtype);
+ }
+ } else if (SwigType_ismemberpointer(subtype)) {
+ /* member pointer deduction, m(CLASS). => p. */
+ Setitem(l, numitems-2, NewString("p."));
+ } else if (is_enum && !SwigType_isqualifier(subtype)) {
+ /* enum deduction, enum SWIGTYPE => SWIGTYPE */
+ Setitem(l, numitems-1, NewString("SWIGTYPE"));
+ } else {
+ /* simple type deduction, eg, r.p.p. => r.p. */
+ /* also function pointers eg, p.f(ANY). => p. */
+ Delitem(l, numitems-2);
+ }
+ } else {
+ if (is_enum) {
+ /* enum deduction, enum SWIGTYPE => SWIGTYPE */
+ Setitem(l, numitems-1, NewString("SWIGTYPE"));
+ } else {
+ /* delete the only item, we are done with deduction */
+ Delitem(l, 0);
+ }
+ }
+ } else {
+ assert(0);
+ }
+
+ for (it = First(l); it.item; it = Next(it)) {
+ Append(r, it.item);
+ }
+
+ if (Len(r) == 0) {
+ Delete(r);
+ r = 0;
+ }
+
+ Delete(l);
+ return r;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_namestr()
+ *
+ * Returns a string of the base type. Takes care of template expansions
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_namestr(const SwigType *t) {
+ String *r;
+ String *suffix;
+ List *p;
+ int i, sz;
+ char *d = Char(t);
+ char *c = strstr(d, "<(");
+
+ if (!c || !strstr(c + 2, ")>"))
+ return NewString(t);
+
+ r = NewStringWithSize(d, (int)(c - d));
+ if (*(c - 1) == '<')
+ Putc(' ', r);
+ Putc('<', r);
+
+ p = SwigType_parmlist(c + 1);
+ sz = Len(p);
+ for (i = 0; i < sz; i++) {
+ String *str = SwigType_str(Getitem(p, i), 0);
+ /* Avoid creating a <: token, which is the same as [ in C++ - put a space after '<'. */
+ if (i == 0 && Len(str))
+ Putc(' ', r);
+ Append(r, str);
+ if ((i + 1) < sz)
+ Putc(',', r);
+ Delete(str);
+ }
+ Putc(' ', r);
+ Putc('>', r);
+ suffix = SwigType_templatesuffix(t);
+ if (Len(suffix) > 0) {
+ String *suffix_namestr = SwigType_namestr(suffix);
+ Append(r, suffix_namestr);
+ Delete(suffix_namestr);
+ } else {
+ Append(r, suffix);
+ }
+ Delete(suffix);
+ Delete(p);
+ return r;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_str()
+ *
+ * Create a C string representation of a datatype.
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_str(const SwigType *s, const_String_or_char_ptr id) {
+ String *result;
+ String *element = 0;
+ String *nextelement;
+ String *forwardelement;
+ SwigType *member_function_qualifiers = 0;
+ List *elements;
+ int nelements, i;
+
+ if (id) {
+ /* stringify the id expanding templates, for example when the id is a fully qualified templated class name */
+ String *id_str = NewString(id); /* unfortunate copy due to current const limitations */
+ result = SwigType_str(id_str, 0);
+ Delete(id_str);
+ } else {
+ result = NewStringEmpty();
+ }
+
+ elements = SwigType_split(s);
+ nelements = Len(elements);
+
+ if (nelements > 0) {
+ element = Getitem(elements, 0);
+ }
+ /* Now, walk the type list and start emitting */
+ for (i = 0; i < nelements; i++) {
+ if (i < (nelements - 1)) {
+ nextelement = Getitem(elements, i + 1);
+ forwardelement = nextelement;
+ if (SwigType_isqualifier(nextelement)) {
+ if (i < (nelements - 2))
+ forwardelement = Getitem(elements, i + 2);
+ }
+ } else {
+ nextelement = 0;
+ forwardelement = 0;
+ }
+ if (SwigType_isqualifier(element)) {
+ if (!member_function_qualifiers) {
+ DOH *q = 0;
+ q = SwigType_parm(element);
+ Insert(result, 0, " ");
+ Insert(result, 0, q);
+ Delete(q);
+ }
+ } else if (SwigType_ispointer(element)) {
+ Insert(result, 0, "*");
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ Insert(result, 0, "(");
+ Append(result, ")");
+ }
+ } else if (SwigType_ismemberpointer(element)) {
+ String *q;
+ q = SwigType_parm(element);
+ Insert(result, 0, "::*");
+ Insert(result, 0, q);
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ Insert(result, 0, "(");
+ Append(result, ")");
+ }
+ {
+ String *next3elements = NewStringEmpty();
+ int j;
+ for (j = i + 1; j < i + 4 && j < nelements; j++) {
+ Append(next3elements, Getitem(elements, j));
+ }
+ if (SwigType_isfunction(next3elements))
+ member_function_qualifiers = SwigType_pop_function_qualifiers(next3elements);
+ Delete(next3elements);
+ }
+ Delete(q);
+ } else if (SwigType_isreference(element)) {
+ if (!member_function_qualifiers)
+ Insert(result, 0, "&");
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ Insert(result, 0, "(");
+ Append(result, ")");
+ }
+ } else if (SwigType_isrvalue_reference(element)) {
+ if (!member_function_qualifiers)
+ Insert(result, 0, "&&");
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ Insert(result, 0, "(");
+ Append(result, ")");
+ }
+ } else if (SwigType_isarray(element)) {
+ DOH *size;
+ Append(result, "[");
+ size = SwigType_parm(element);
+ Append(result, size);
+ Append(result, "]");
+ Delete(size);
+ } else if (SwigType_isfunction(element)) {
+ DOH *parms, *p;
+ int j, plen;
+ Append(result, "(");
+ parms = SwigType_parmlist(element);
+ plen = Len(parms);
+ for (j = 0; j < plen; j++) {
+ p = SwigType_str(Getitem(parms, j), 0);
+ Append(result, p);
+ if (j < (plen - 1))
+ Append(result, ",");
+ }
+ Append(result, ")");
+ if (member_function_qualifiers) {
+ String *p = SwigType_str(member_function_qualifiers, 0);
+ Append(result, " ");
+ Append(result, p);
+ Delete(p);
+ Delete(member_function_qualifiers);
+ member_function_qualifiers = 0;
+ }
+ Delete(parms);
+ } else {
+ if (strcmp(Char(element), "v(...)") == 0) {
+ Insert(result, 0, "...");
+ } else {
+ String *bs = SwigType_namestr(element);
+ Insert(result, 0, " ");
+ Insert(result, 0, bs);
+ Delete(bs);
+ }
+ }
+ element = nextelement;
+ }
+ Delete(elements);
+ Chop(result);
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_ltype(const SwigType *ty)
+ *
+ * Create a locally assignable type
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_ltype(const SwigType *s) {
+ String *result;
+ String *element;
+ SwigType *td, *tc = 0;
+ List *elements;
+ int nelements, i;
+ int firstarray = 1;
+ int notypeconv = 0;
+ int ignore_member_function_qualifiers = 0;
+
+ result = NewStringEmpty();
+ tc = Copy(s);
+ /* Nuke all leading qualifiers */
+ while (SwigType_isqualifier(tc)) {
+ Delete(SwigType_pop(tc));
+ }
+ if (SwigType_issimple(tc)) {
+ /* Resolve any typedef definitions */
+ SwigType *tt = Copy(tc);
+ td = 0;
+ while ((td = SwigType_typedef_resolve(tt))) {
+ if (td && (SwigType_isconst(td) || SwigType_isarray(td) || SwigType_isreference(td) || SwigType_isrvalue_reference(td))) {
+ /* We need to use the typedef type */
+ Delete(tt);
+ break;
+ } else if (td) {
+ Delete(tt);
+ tt = td;
+ }
+ }
+ if (td) {
+ Delete(tc);
+ tc = td;
+ }
+ }
+
+ elements = SwigType_split(tc);
+ nelements = Len(elements);
+
+ /* Now, walk the type list and start emitting */
+ for (i = 0; i < nelements; i++) {
+ element = Getitem(elements, i);
+ /* when we see a function, we need to preserve the following types */
+ if (SwigType_isfunction(element)) {
+ notypeconv = 1;
+ ignore_member_function_qualifiers = 0;
+ }
+ if (ignore_member_function_qualifiers) {
+ /* cv-qualifiers and ref-qualifiers up until the f() element have already been added */
+ } else if (SwigType_isqualifier(element)) {
+ /* swallow cv-qualifiers */
+ } else if (SwigType_ispointer(element)) {
+ Append(result, element);
+ firstarray = 0;
+ } else if (SwigType_ismemberpointer(element)) {
+ Append(result, element);
+ {
+ String *next3elements = NewStringEmpty();
+ int j;
+ for (j = i + 1; j < i + 4 && j < nelements; j++) {
+ Append(next3elements, Getitem(elements, j));
+ }
+ if (SwigType_isfunction(next3elements)) {
+ SwigType *member_function_qualifiers = SwigType_pop_function_qualifiers(next3elements);
+ /* compilers won't let us cast from a member function without qualifiers to one with qualifiers, so the qualifiers are kept in the ltype */
+ if (member_function_qualifiers)
+ Append(result, member_function_qualifiers);
+ Delete(member_function_qualifiers);
+ ignore_member_function_qualifiers = 1;
+ }
+ Delete(next3elements);
+ }
+ firstarray = 0;
+ } else if (SwigType_isreference(element)) {
+ if (notypeconv) {
+ Append(result, element);
+ } else {
+ Append(result, "p.");
+ }
+ firstarray = 0;
+ } else if (SwigType_isrvalue_reference(element)) {
+ if (notypeconv) {
+ Append(result, element);
+ } else {
+ Append(result, "p.");
+ }
+ firstarray = 0;
+ } else if (SwigType_isarray(element) && firstarray) {
+ if (notypeconv) {
+ Append(result, element);
+ } else {
+ Append(result, "p.");
+ }
+ firstarray = 0;
+ } else if (SwigType_isenum(element)) {
+ int anonymous_enum = (Cmp(element, "enum ") == 0);
+ if (notypeconv || !anonymous_enum) {
+ Append(result, element);
+ } else {
+ Append(result, "int");
+ }
+ } else {
+ Append(result, element);
+ }
+ }
+ Delete(elements);
+ Delete(tc);
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_lstr()
+ *
+ * Produces a type-string that is suitable as a lvalue in an expression.
+ * That is, a type that can be freely assigned a value without violating
+ * any C assignment rules.
+ *
+ * - Qualifiers such as 'const' and 'volatile' are stripped.
+ * Except for member function cv-qualifiers and ref-qualifiers.
+ * - Arrays are converted into a *single* pointer (i.e.,
+ * double [][] becomes double *).
+ * - References are converted into a pointer.
+ * - Typedef names that refer to read-only types will be replaced
+ * with an equivalent assignable version.
+ * -------------------------------------------------------------------- */
+
+String *SwigType_lstr(const SwigType *s, const_String_or_char_ptr id) {
+ String *result;
+ SwigType *tc;
+
+ tc = SwigType_ltype(s);
+ result = SwigType_str(tc, id);
+ Delete(tc);
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_rcaststr()
+ *
+ * Produces a casting string that maps the type returned by lstr() to the real
+ * datatype printed by str().
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_rcaststr(const SwigType *s, const_String_or_char_ptr name) {
+ String *result, *cast;
+ String *element = 0;
+ String *nextelement;
+ String *forwardelement;
+ String *member_function_qualifiers = 0;
+ SwigType *td, *tc = 0;
+ const SwigType *rs;
+ List *elements;
+ int nelements, i;
+ int clear = 1;
+ int firstarray = 1;
+ int isreference = 0;
+ int isfunction = 0;
+
+ result = NewStringEmpty();
+
+ if (SwigType_isconst(s)) {
+ tc = Copy(s);
+ Delete(SwigType_pop(tc));
+ if (SwigType_ismemberpointer(tc))
+ rs = s;
+ else
+ rs = tc;
+ } else {
+ rs = s;
+ }
+
+ if ((SwigType_isconst(rs) || SwigType_isarray(rs) || SwigType_isreference(rs) || SwigType_isrvalue_reference(rs))) {
+ td = 0;
+ } else {
+ td = SwigType_typedef_resolve(rs);
+ }
+
+ if (td) {
+ if ((SwigType_isconst(td) || SwigType_isarray(td) || SwigType_isreference(td) || SwigType_isrvalue_reference(td))) {
+ elements = SwigType_split(td);
+ } else {
+ elements = SwigType_split(rs);
+ }
+ Delete(td);
+ } else {
+ elements = SwigType_split(rs);
+ }
+ nelements = Len(elements);
+ if (nelements > 0) {
+ element = Getitem(elements, 0);
+ }
+ /* Now, walk the type list and start emitting */
+ for (i = 0; i < nelements; i++) {
+ if (i < (nelements - 1)) {
+ nextelement = Getitem(elements, i + 1);
+ forwardelement = nextelement;
+ if (SwigType_isqualifier(nextelement)) {
+ if (i < (nelements - 2))
+ forwardelement = Getitem(elements, i + 2);
+ }
+ } else {
+ nextelement = 0;
+ forwardelement = 0;
+ }
+ if (SwigType_isqualifier(element)) {
+ if (!member_function_qualifiers) {
+ DOH *q = 0;
+ q = SwigType_parm(element);
+ Insert(result, 0, " ");
+ Insert(result, 0, q);
+ Delete(q);
+ clear = 0;
+ }
+ } else if (SwigType_ispointer(element)) {
+ Insert(result, 0, "*");
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ Insert(result, 0, "(");
+ Append(result, ")");
+ }
+ firstarray = 0;
+ } else if (SwigType_ismemberpointer(element)) {
+ String *q;
+ Insert(result, 0, "::*");
+ q = SwigType_parm(element);
+ Insert(result, 0, q);
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ Insert(result, 0, "(");
+ Append(result, ")");
+ }
+ {
+ String *next3elements = NewStringEmpty();
+ int j;
+ for (j = i + 1; j < i + 4 && j < nelements; j++) {
+ Append(next3elements, Getitem(elements, j));
+ }
+ if (SwigType_isfunction(next3elements))
+ member_function_qualifiers = SwigType_pop_function_qualifiers(next3elements);
+ Delete(next3elements);
+ }
+ firstarray = 0;
+ Delete(q);
+ } else if (SwigType_isreference(element)) {
+ if (!member_function_qualifiers) {
+ Insert(result, 0, "&");
+ if (!isfunction)
+ isreference = 1;
+ }
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ Insert(result, 0, "(");
+ Append(result, ")");
+ }
+ } else if (SwigType_isrvalue_reference(element)) {
+ if (!member_function_qualifiers) {
+ Insert(result, 0, "&&");
+ if (!isfunction)
+ isreference = 1;
+ }
+ if ((forwardelement) && ((SwigType_isfunction(forwardelement) || (SwigType_isarray(forwardelement))))) {
+ Insert(result, 0, "(");
+ Append(result, ")");
+ }
+ clear = 0;
+ } else if (SwigType_isarray(element)) {
+ DOH *size;
+ if (firstarray && !isreference) {
+ Append(result, "(*)");
+ firstarray = 0;
+ } else {
+ Append(result, "[");
+ size = SwigType_parm(element);
+ Append(result, size);
+ Append(result, "]");
+ Delete(size);
+ clear = 0;
+ }
+ } else if (SwigType_isfunction(element)) {
+ DOH *parms, *p;
+ int j, plen;
+ Append(result, "(");
+ parms = SwigType_parmlist(element);
+ plen = Len(parms);
+ for (j = 0; j < plen; j++) {
+ p = SwigType_str(Getitem(parms, j), 0);
+ Append(result, p);
+ Delete(p);
+ if (j < (plen - 1))
+ Append(result, ",");
+ }
+ Append(result, ")");
+ Delete(parms);
+ if (member_function_qualifiers) {
+ String *p = SwigType_str(member_function_qualifiers, 0);
+ Append(result, " ");
+ Append(result, p);
+ Delete(p);
+ Delete(member_function_qualifiers);
+ member_function_qualifiers = 0;
+ clear = 0;
+ }
+ isfunction = 1;
+ } else {
+ String *bs = SwigType_namestr(element);
+ Insert(result, 0, " ");
+ Insert(result, 0, bs);
+ Delete(bs);
+ }
+ element = nextelement;
+ }
+ Delete(elements);
+ if (clear) {
+ cast = NewStringEmpty();
+ } else {
+ cast = NewStringf("(%s)", result);
+ }
+ if (name) {
+ if (isreference) {
+ Append(cast, "*");
+ }
+ Append(cast, name);
+ }
+ Delete(result);
+ Delete(tc);
+ return cast;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_lcaststr()
+ *
+ * Casts a variable from the real type to the local datatype.
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_lcaststr(const SwigType *s, const_String_or_char_ptr name) {
+ String *result;
+
+ result = NewStringEmpty();
+
+ if (SwigType_isarray(s)) {
+ String *lstr = SwigType_lstr(s, 0);
+ Printf(result, "(%s)%s", lstr, name);
+ Delete(lstr);
+ } else if (SwigType_isreference(s)) {
+ String *str = SwigType_str(s, 0);
+ Printf(result, "(%s)", str);
+ Delete(str);
+ if (name)
+ Append(result, name);
+ } else if (SwigType_isrvalue_reference(s)) {
+ String *str = SwigType_str(s, 0);
+ Printf(result, "(%s)", str);
+ Delete(str);
+ if (name)
+ Append(result, name);
+ } else if (SwigType_isqualifier(s)) {
+ String *lstr = SwigType_lstr(s, 0);
+ Printf(result, "(%s)%s", lstr, name);
+ Delete(lstr);
+ } else {
+ if (name)
+ Append(result, name);
+ }
+ return result;
+}
+
+#if 0
+/* Alternative implementation for manglestr_default. Mangling is similar to the original
+ except for a few subtle differences for example in templates:
+ namespace foo {
+ template<class T> class bar {};
+ typedef int Integer;
+ void test2(bar<Integer *> *x);
+ }
+ Mangling is more consistent and changes from
+ _p_foo__barT_int_p_t to
+ _p_foo__barT_p_int_t.
+*/
+static void mangle_stringcopy(String *destination, const char *source, int count) {
+ while (count-- > 0) {
+ char newc = '_';
+ if (!(*source == '.' || *source == ':' || *source == ' '))
+ newc = *source;
+ /* TODO: occasionally '*' or numerics need converting to '_', eg in array dimensions and template expressions */
+ Putc(newc, destination);
+ source++;
+ }
+}
+
+static void mangle_subtype(String *mangled, SwigType *s);
+
+/* -----------------------------------------------------------------------------
+ * mangle_namestr()
+ *
+ * Mangles a type taking care of template expansions. Similar to SwigType_namestr().
+ * The type may include a trailing '.', for example "p."
+ * ----------------------------------------------------------------------------- */
+
+static void mangle_namestr(String *mangled, SwigType *t) {
+ int length = Len(t);
+ if (SwigType_isqualifier(t)) {
+ Append(mangled, "q_");
+ mangle_stringcopy(mangled, Char(t)+2, length-4);
+ Append(mangled, "__");
+ } else if (SwigType_ismemberpointer(t)) {
+ Append(mangled, "m_");
+ mangle_stringcopy(mangled, Char(t)+2, length-4);
+ Append(mangled, "__");
+ } else if (SwigType_isarray(t)) {
+ Append(mangled, "a_");
+ mangle_stringcopy(mangled, Char(t)+2, length-4);
+ Append(mangled, "__");
+ } else if (SwigType_isfunction(t)) {
+ List *p = SwigType_parmlist(t);
+ int sz = Len(p);
+ int i;
+ Append(mangled, "f_");
+ for (i = 0; i < sz; i++) {
+ mangle_subtype(mangled, Getitem(p, i));
+ Putc('_', mangled);
+ }
+ Append(mangled, (sz > 0) ? "_" : "__");
+ } else if (SwigType_isvarargs(t)) {
+ Append(mangled, "___");
+ } else {
+ char *d = Char(t);
+ char *c = strstr(d, "<(");
+ if (!c || !strstr(c + 2, ")>")) {
+ /* not a template type */
+ mangle_stringcopy(mangled, Char(t), Len(t));
+ } else {
+ /* a template type */
+ String *suffix;
+ List *p;
+ int i, sz;
+ mangle_stringcopy(mangled, d, c-d);
+ Putc('T', mangled);
+ Putc('_', mangled);
+
+ p = SwigType_parmlist(c + 1);
+ sz = Len(p);
+ for (i = 0; i < sz; i++) {
+ mangle_subtype(mangled, Getitem(p, i));
+ Putc('_', mangled);
+ }
+ Putc('t', mangled);
+ suffix = SwigType_templatesuffix(t);
+ if (Len(suffix) > 0) {
+ mangle_namestr(mangled, suffix);
+ } else {
+ Append(mangled, suffix);
+ }
+ Delete(suffix);
+ Delete(p);
+ }
+ }
+}
+
+static void mangle_subtype(String *mangled, SwigType *s) {
+ List *elements;
+ int nelements, i;
+
+ assert(s);
+ elements = SwigType_split(s);
+ nelements = Len(elements);
+ for (i = 0; i < nelements; i++) {
+ SwigType *element = Getitem(elements, i);
+ mangle_namestr(mangled, element);
+ }
+ Delete(elements);
+}
+
+static String *manglestr_default(const SwigType *s) {
+ String *mangled = NewString("_");
+ SwigType *sr = SwigType_typedef_resolve_all(s);
+ SwigType *sq = SwigType_typedef_qualified(sr);
+ SwigType *ss = SwigType_remove_global_scope_prefix(sq);
+ SwigType *type = ss;
+ SwigType *lt;
+
+ if (SwigType_istemplate(ss)) {
+ SwigType *ty = Swig_symbol_template_deftype(ss, 0);
+ Delete(ss);
+ ss = ty;
+ type = ss;
+ }
+
+ lt = SwigType_ltype(type);
+
+ Replace(lt, "struct ", "", DOH_REPLACE_ANY);
+ Replace(lt, "class ", "", DOH_REPLACE_ANY);
+ Replace(lt, "union ", "", DOH_REPLACE_ANY);
+ Replace(lt, "enum ", "", DOH_REPLACE_ANY);
+
+ mangle_subtype(mangled, lt);
+
+ Delete(ss);
+ Delete(sq);
+ Delete(sr);
+
+ return mangled;
+}
+
+#else
+
+static String *manglestr_default(const SwigType *s) {
+ char *c;
+ String *result = 0;
+ String *base = 0;
+ SwigType *lt;
+ SwigType *sr = SwigType_typedef_resolve_all(s);
+ SwigType *sq = SwigType_typedef_qualified(sr);
+ SwigType *ss = SwigType_remove_global_scope_prefix(sq);
+ SwigType *type = ss;
+
+ if (SwigType_istemplate(ss)) {
+ SwigType *ty = Swig_symbol_template_deftype(ss, 0);
+ Delete(ss);
+ ss = ty;
+ type = ss;
+ }
+
+ lt = SwigType_ltype(type);
+ result = SwigType_prefix(lt);
+ base = SwigType_base(lt);
+
+ c = Char(result);
+ while (*c) {
+ if (!isalnum((int) *c))
+ *c = '_';
+ c++;
+ }
+ if (SwigType_istemplate(base)) {
+ String *b = SwigType_namestr(base);
+ Delete(base);
+ base = b;
+ }
+
+ Replace(base, "struct ", "", DOH_REPLACE_ANY); /* This might be problematic */
+ Replace(base, "class ", "", DOH_REPLACE_ANY);
+ Replace(base, "union ", "", DOH_REPLACE_ANY);
+ Replace(base, "enum ", "", DOH_REPLACE_ANY);
+
+ c = Char(base);
+ while (*c) {
+ if (*c == '<')
+ *c = 'T';
+ else if (*c == '>')
+ *c = 't';
+ else if (*c == '*')
+ *c = 'p';
+ else if (*c == '[')
+ *c = 'a';
+ else if (*c == ']')
+ *c = 'A';
+ else if (*c == '&')
+ *c = 'R';
+ else if (*c == '(')
+ *c = 'f';
+ else if (*c == ')')
+ *c = 'F';
+ else if (!isalnum((int) *c))
+ *c = '_';
+ c++;
+ }
+ Append(result, base);
+ Insert(result, 0, "_");
+ Delete(lt);
+ Delete(base);
+ Delete(ss);
+ Delete(sq);
+ Delete(sr);
+ return result;
+}
+#endif
+
+String *SwigType_manglestr(const SwigType *s) {
+#if 0
+ /* Debugging checks to ensure a proper SwigType is passed in and not a stringified type */
+ String *angle = Strstr(s, "<");
+ if (angle && Strncmp(angle, "<(", 2) != 0)
+ Printf(stderr, "SwigType_manglestr error: %s\n", s);
+ else if (Strstr(s, "*") || Strstr(s, "&") || Strstr(s, "["))
+ Printf(stderr, "SwigType_manglestr error: %s\n", s);
+#endif
+ return manglestr_default(s);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_typename_replace()
+ *
+ * Replaces a typename in a type with something else. Needed for templates.
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_typename_replace(SwigType *t, String *pat, String *rep) {
+ String *nt;
+ int i, ilen;
+ List *elem;
+
+ if (!Strstr(t, pat))
+ return;
+
+ if (Equal(t, pat)) {
+ Replace(t, pat, rep, DOH_REPLACE_ANY);
+ return;
+ }
+ nt = NewStringEmpty();
+ elem = SwigType_split(t);
+ ilen = Len(elem);
+ for (i = 0; i < ilen; i++) {
+ String *e = Getitem(elem, i);
+ if (SwigType_issimple(e)) {
+ if (Equal(e, pat)) {
+ /* Replaces a type of the form 'pat' with 'rep<args>' */
+ Replace(e, pat, rep, DOH_REPLACE_ANY);
+ } else if (SwigType_istemplate(e)) {
+ /* Replaces a type of the form 'pat<args>' with 'rep' */
+ {
+ /* To match "e=TemplateTemplateT<(float)>"
+ * with "pat=TemplateTemplateT"
+ * we need to compare only the first part of the string e.
+ */
+ int len = Len(pat);
+
+ /* Len(e) > len, not >= (because we expect at least a
+ * character '<' following the template typename)
+ */
+ if (Len(e) > len) {
+ String *firstPartOfType = NewStringWithSize(e, len);
+ const char* e_as_char = Char(e);
+
+ if (Equal(firstPartOfType, pat) && e_as_char[len] == '<') {
+ String *repbase = SwigType_templateprefix(rep);
+ Replace(e, pat, repbase, DOH_REPLACE_ID | DOH_REPLACE_FIRST);
+ Delete(repbase);
+ }
+ Delete(firstPartOfType);
+ }
+ }
+
+ {
+ String *tsuffix;
+ List *tparms = SwigType_parmlist(e);
+ int j, jlen;
+ String *nt = SwigType_templateprefix(e);
+ Append(nt, "<(");
+ jlen = Len(tparms);
+ for (j = 0; j < jlen; j++) {
+ SwigType_typename_replace(Getitem(tparms, j), pat, rep);
+ Append(nt, Getitem(tparms, j));
+ if (j < (jlen - 1))
+ Putc(',', nt);
+ }
+ tsuffix = SwigType_templatesuffix(e);
+ SwigType_typename_replace(tsuffix, pat, rep);
+ Printf(nt, ")>%s", tsuffix);
+ Delete(tsuffix);
+ Clear(e);
+ Append(e, nt);
+ Delete(nt);
+ Delete(tparms);
+ }
+ } else if (Swig_scopename_check(e)) {
+ String *first = 0;
+ String *rest = 0;
+ Swig_scopename_split(e, &first, &rest);
+
+ /* Swig_scopename_split doesn't handle :: prefix very well ... could do with a rework */
+ if (Strncmp(rest, "::", 2) == 0) {
+ String *tmp = NewString(Char(rest) + 2);
+ Clear(rest);
+ Printv(rest, tmp, NIL);
+ Delete(tmp);
+ assert(!first);
+ }
+
+ Clear(e);
+ if (first)
+ SwigType_typename_replace(first, pat, rep);
+ SwigType_typename_replace(rest, pat, rep);
+ Printv(e, first ? first : "", "::", rest, NIL);
+ Delete(first);
+ Delete(rest);
+ }
+ } else if (SwigType_isfunction(e)) {
+ int j, jlen;
+ List *fparms = SwigType_parmlist(e);
+ Clear(e);
+ Append(e, "f(");
+ jlen = Len(fparms);
+ for (j = 0; j < jlen; j++) {
+ SwigType_typename_replace(Getitem(fparms, j), pat, rep);
+ Append(e, Getitem(fparms, j));
+ if (j < (jlen - 1))
+ Putc(',', e);
+ }
+ Append(e, ").");
+ Delete(fparms);
+ } else if (SwigType_isarray(e)) {
+ Replace(e, pat, rep, DOH_REPLACE_ID);
+ }
+ Append(nt, e);
+ }
+ Clear(t);
+ Append(t, nt);
+ Delete(nt);
+ Delete(elem);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_remove_global_scope_prefix()
+ *
+ * Removes the unary scope operator (::) prefix indicating global scope in all
+ * components of the type
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_remove_global_scope_prefix(const SwigType *t) {
+ SwigType *result;
+ const char *type = Char(t);
+ if (strncmp(type, "::", 2) == 0)
+ type += 2;
+ result = NewString(type);
+ Replaceall(result, ".::", ".");
+ Replaceall(result, "(::", "(");
+ Replaceall(result, "enum ::", "enum ");
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_check_decl()
+ *
+ * Checks type declarators for a match
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_check_decl(const SwigType *ty, const SwigType *decl) {
+ SwigType *t, *t1, *t2;
+ int r;
+ t = SwigType_typedef_resolve_all(ty);
+ t1 = SwigType_strip_qualifiers(t);
+ t2 = SwigType_prefix(t1);
+ r = Equal(t2, decl);
+ Delete(t);
+ Delete(t1);
+ Delete(t2);
+ return r == 1;
+}
diff --git a/contrib/tools/swig/Source/Swig/swig.h b/contrib/tools/swig/Source/Swig/swig.h
new file mode 100644
index 0000000000..19e61b4558
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/swig.h
@@ -0,0 +1,451 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swig.h
+ *
+ * Header file for the SWIG core.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_SWIG_H
+#define SWIG_SWIG_H
+
+#include "swigconfig.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "doh.h"
+
+/* Status codes */
+
+#define SWIG_OK 1
+#define SWIG_ERROR 0
+#define SWIG_NOWRAP 0
+
+/* Global macros */
+#define NSPACE_SEPARATOR "." /* Namespace separator for the nspace feature - this should be changed to a target language configurable variable */
+#define NSPACE_TODO 0 /* Languages that still need to implement and test the nspace feature use this */
+
+/* Short names for common data types */
+
+ typedef DOH String;
+ typedef DOH Hash;
+ typedef DOH List;
+ typedef DOH String_or_char;
+ typedef DOH File;
+ typedef DOH Parm;
+ typedef DOH ParmList;
+ typedef DOH Node;
+ typedef DOH Symtab;
+ typedef DOH Typetab;
+ typedef DOH SwigType;
+
+/* --- Legacy DataType interface. These type codes are provided solely
+ for backwards compatibility with older modules --- */
+
+/* --- The ordering of type values is used to determine type-promotion
+ in the parser. Do not change */
+
+/* Numeric types */
+
+#define T_BOOL 1
+#define T_SCHAR 2
+#define T_UCHAR 3
+#define T_SHORT 4
+#define T_USHORT 5
+#define T_ENUM 6
+#define T_INT 7
+#define T_UINT 8
+#define T_LONG 9
+#define T_ULONG 10
+#define T_LONGLONG 11
+#define T_ULONGLONG 12
+#define T_FLOAT 20
+#define T_DOUBLE 21
+#define T_LONGDOUBLE 22
+#define T_FLTCPLX 23
+#define T_DBLCPLX 24
+#define T_NUMERIC 25
+#define T_AUTO 26
+
+#define T_COMPLEX T_DBLCPLX
+
+/* non-numeric */
+
+#define T_CHAR 29
+#define T_WCHAR 30
+#define T_USER 31
+#define T_VOID 32
+#define T_STRING 33
+#define T_POINTER 34
+#define T_REFERENCE 35
+#define T_ARRAY 36
+#define T_FUNCTION 37
+#define T_MPOINTER 38
+#define T_VARARGS 39
+#define T_RVALUE_REFERENCE 40
+#define T_WSTRING 41
+
+#define T_SYMBOL 98
+#define T_ERROR 99
+
+
+
+/* --- File interface --- */
+
+#include "swigfile.h"
+
+/* --- Command line parsing --- */
+
+#include "swigopt.h"
+
+/* --- Scanner Interface --- */
+
+#include "swigscan.h"
+
+/* --- Functions for manipulating the string-based type encoding --- */
+
+ extern SwigType *NewSwigType(int typecode);
+ extern SwigType *SwigType_del_element(SwigType *t);
+ extern SwigType *SwigType_add_pointer(SwigType *t);
+ extern SwigType *SwigType_add_memberpointer(SwigType *t, const_String_or_char_ptr qual);
+ extern SwigType *SwigType_del_memberpointer(SwigType *t);
+ extern SwigType *SwigType_del_pointer(SwigType *t);
+ extern SwigType *SwigType_add_array(SwigType *t, const_String_or_char_ptr size);
+ extern SwigType *SwigType_del_array(SwigType *t);
+ extern SwigType *SwigType_pop_arrays(SwigType *t);
+ extern SwigType *SwigType_add_reference(SwigType *t);
+ extern SwigType *SwigType_del_reference(SwigType *t);
+ extern SwigType *SwigType_add_rvalue_reference(SwigType *t);
+ extern SwigType *SwigType_del_rvalue_reference(SwigType *t);
+ extern SwigType *SwigType_add_qualifier(SwigType *t, const_String_or_char_ptr qual);
+ extern SwigType *SwigType_del_qualifier(SwigType *t);
+ extern SwigType *SwigType_add_function(SwigType *t, ParmList *parms);
+ extern SwigType *SwigType_add_template(SwigType *t, ParmList *parms);
+ extern SwigType *SwigType_pop_function(SwigType *t);
+ extern SwigType *SwigType_pop_function_qualifiers(SwigType *t);
+ extern ParmList *SwigType_function_parms(const SwigType *t, Node *file_line_node);
+ extern List *SwigType_split(const SwigType *t);
+ extern String *SwigType_pop(SwigType *t);
+ extern void SwigType_push(SwigType *t, String *s);
+ extern SwigType *SwigType_last(SwigType *t);
+ extern List *SwigType_parmlist(const SwigType *p);
+ extern String *SwigType_parm(const SwigType *p);
+ extern String *SwigType_str(const SwigType *s, const_String_or_char_ptr id);
+ extern String *SwigType_lstr(const SwigType *s, const_String_or_char_ptr id);
+ extern String *SwigType_rcaststr(const SwigType *s, const_String_or_char_ptr id);
+ extern String *SwigType_lcaststr(const SwigType *s, const_String_or_char_ptr id);
+ extern String *SwigType_manglestr(const SwigType *t);
+ extern SwigType *SwigType_ltype(const SwigType *t);
+ extern int SwigType_ispointer(const SwigType *t);
+ extern int SwigType_ispointer_return(const SwigType *t);
+ extern int SwigType_isfunctionpointer(const SwigType *t);
+ extern int SwigType_ismemberpointer(const SwigType *t);
+ extern int SwigType_isreference(const SwigType *t);
+ extern int SwigType_isreference_return(const SwigType *t);
+ extern int SwigType_isrvalue_reference(const SwigType *t);
+ extern int SwigType_isarray(const SwigType *t);
+ extern int SwigType_prefix_is_simple_1D_array(const SwigType *t);
+ extern int SwigType_isfunction(const SwigType *t);
+ extern int SwigType_isqualifier(const SwigType *t);
+ extern int SwigType_isconst(const SwigType *t);
+ extern int SwigType_issimple(const SwigType *t);
+ extern int SwigType_ismutable(const SwigType *t);
+ extern int SwigType_isvarargs(const SwigType *t);
+ extern int SwigType_istemplate(const SwigType *t);
+ extern int SwigType_isenum(const SwigType *t);
+ extern int SwigType_check_decl(const SwigType *t, const_String_or_char_ptr decl);
+ extern SwigType *SwigType_strip_qualifiers(const SwigType *t);
+ extern SwigType *SwigType_strip_single_qualifier(const SwigType *t);
+ extern SwigType *SwigType_functionpointer_decompose(SwigType *t);
+ extern String *SwigType_base(const SwigType *t);
+ extern String *SwigType_namestr(const SwigType *t);
+ extern String *SwigType_templateprefix(const SwigType *t);
+ extern String *SwigType_templatesuffix(const SwigType *t);
+ extern String *SwigType_istemplate_templateprefix(const SwigType *t);
+ extern String *SwigType_istemplate_only_templateprefix(const SwigType *t);
+ extern String *SwigType_templateargs(const SwigType *t);
+ extern String *SwigType_prefix(const SwigType *t);
+ extern int SwigType_array_ndim(const SwigType *t);
+ extern String *SwigType_array_getdim(const SwigType *t, int n);
+ extern void SwigType_array_setdim(SwigType *t, int n, const_String_or_char_ptr rep);
+ extern SwigType *SwigType_array_type(const SwigType *t);
+ extern SwigType *SwigType_default_create(const SwigType *ty);
+ extern SwigType *SwigType_default_deduce(const SwigType *t);
+ extern void SwigType_typename_replace(SwigType *t, String *pat, String *rep);
+ extern SwigType *SwigType_remove_global_scope_prefix(const SwigType *t);
+ extern SwigType *SwigType_alttype(const SwigType *t, int ltmap);
+
+/* --- Type-system management --- */
+ extern void SwigType_typesystem_init(void);
+ extern int SwigType_typedef(const SwigType *type, const_String_or_char_ptr name);
+ extern int SwigType_typedef_class(const_String_or_char_ptr name);
+ extern int SwigType_typedef_using(const_String_or_char_ptr qname);
+ extern void SwigType_inherit(String *subclass, String *baseclass, String *cast, String *conversioncode);
+ extern int SwigType_issubtype(const SwigType *subtype, const SwigType *basetype);
+ extern void SwigType_scope_alias(String *aliasname, Typetab *t);
+ extern void SwigType_using_scope(Typetab *t);
+ extern void SwigType_new_scope(const_String_or_char_ptr name);
+ extern void SwigType_inherit_scope(Typetab *scope);
+ extern Typetab *SwigType_pop_scope(void);
+ extern Typetab *SwigType_set_scope(Typetab *h);
+ extern void SwigType_print_scope(void);
+ extern SwigType *SwigType_typedef_resolve(const SwigType *t);
+ extern SwigType *SwigType_typedef_resolve_all(const SwigType *t);
+ extern SwigType *SwigType_typedef_qualified(const SwigType *t);
+ extern int SwigType_istypedef(const SwigType *t);
+ extern int SwigType_isclass(const SwigType *t);
+ extern void SwigType_attach_symtab(Symtab *syms);
+ extern void SwigType_remember(const SwigType *t);
+ extern void SwigType_remember_clientdata(const SwigType *t, const_String_or_char_ptr clientdata);
+ extern void SwigType_remember_mangleddata(String *mangled, const_String_or_char_ptr clientdata);
+ extern void (*SwigType_remember_trace(void (*tf) (const SwigType *, String *, String *))) (const SwigType *, String *, String *);
+ extern void SwigType_emit_type_table(File *f_headers, File *f_table);
+ extern int SwigType_type(const SwigType *t);
+
+/* --- Symbol table module --- */
+
+ extern void Swig_symbol_print_tables(Symtab *symtab);
+ extern void Swig_symbol_print_tables_summary(void);
+ extern void Swig_symbol_print_symbols(void);
+ extern void Swig_symbol_print_csymbols(void);
+ extern void Swig_symbol_init(void);
+ extern void Swig_symbol_setscopename(const_String_or_char_ptr name);
+ extern String *Swig_symbol_getscopename(void);
+ extern String *Swig_symbol_qualifiedscopename(Symtab *symtab);
+ extern String *Swig_symbol_qualified_language_scopename(Symtab *symtab);
+ extern Symtab *Swig_symbol_newscope(void);
+ extern Symtab *Swig_symbol_setscope(Symtab *);
+ extern Symtab *Swig_symbol_getscope(const_String_or_char_ptr symname);
+ extern Symtab *Swig_symbol_global_scope(void);
+ extern Symtab *Swig_symbol_current(void);
+ extern Symtab *Swig_symbol_popscope(void);
+ extern Node *Swig_symbol_add(const_String_or_char_ptr symname, Node *node);
+ extern void Swig_symbol_cadd(const_String_or_char_ptr symname, Node *node);
+ extern Node *Swig_symbol_clookup(const_String_or_char_ptr symname, Symtab *tab);
+ extern Node *Swig_symbol_clookup_check(const_String_or_char_ptr symname, Symtab *tab, int (*check) (Node *));
+ extern Node *Swig_symbol_clookup_no_inherit(const_String_or_char_ptr name, Symtab *n);
+ extern Symtab *Swig_symbol_cscope(const_String_or_char_ptr symname, Symtab *tab);
+ extern Node *Swig_symbol_clookup_local(const_String_or_char_ptr symname, Symtab *tab);
+ extern Node *Swig_symbol_clookup_local_check(const_String_or_char_ptr symname, Symtab *tab, int (*check) (Node *));
+ extern String *Swig_symbol_qualified(Node *node);
+ extern Node *Swig_symbol_isoverloaded(Node *node);
+ extern void Swig_symbol_remove(Node *node);
+ extern void Swig_symbol_alias(const_String_or_char_ptr aliasname, Symtab *tab);
+ extern void Swig_symbol_inherit(Symtab *tab);
+ extern SwigType *Swig_symbol_type_qualify(const SwigType *ty, Symtab *tab);
+ extern String *Swig_symbol_string_qualify(String *s, Symtab *tab);
+ extern SwigType *Swig_symbol_typedef_reduce(const SwigType *ty, Symtab *tab);
+
+ extern ParmList *Swig_symbol_template_defargs(Parm *parms, Parm *targs, Symtab *tscope, Symtab *tsdecl);
+ extern SwigType *Swig_symbol_template_deftype(const SwigType *type, Symtab *tscope);
+ extern SwigType *Swig_symbol_template_param_eval(const SwigType *p, Symtab *symtab);
+
+/* --- Parameters and Parameter Lists --- */
+
+#include "swigparm.h"
+
+extern String *ParmList_errorstr(ParmList *);
+extern int ParmList_is_compactdefargs(ParmList *p);
+
+/* --- Parse tree support --- */
+
+#include "swigtree.h"
+
+/* -- Wrapper function Object */
+
+#include "swigwrap.h"
+
+/* --- Naming functions --- */
+
+ extern void Swig_name_register(const_String_or_char_ptr method, const_String_or_char_ptr format);
+ extern void Swig_name_unregister(const_String_or_char_ptr method);
+ extern String *Swig_name_mangle(const_String_or_char_ptr s);
+ extern String *Swig_name_wrapper(const_String_or_char_ptr fname);
+ extern String *Swig_name_member(const_String_or_char_ptr nspace, const_String_or_char_ptr classname, const_String_or_char_ptr membername);
+ extern String *Swig_name_get(const_String_or_char_ptr nspace, const_String_or_char_ptr vname);
+ extern String *Swig_name_set(const_String_or_char_ptr nspace, const_String_or_char_ptr vname);
+ extern String *Swig_name_construct(const_String_or_char_ptr nspace, const_String_or_char_ptr classname);
+ extern String *Swig_name_copyconstructor(const_String_or_char_ptr nspace, const_String_or_char_ptr classname);
+ extern String *Swig_name_destroy(const_String_or_char_ptr nspace, const_String_or_char_ptr classname);
+ extern String *Swig_name_disown(const_String_or_char_ptr nspace, const_String_or_char_ptr classname);
+
+ extern void Swig_naming_init(void);
+ extern void Swig_name_namewarn_add(String *prefix, String *name, SwigType *decl, Hash *namewrn);
+ extern void Swig_name_rename_add(String *prefix, String *name, SwigType *decl, Hash *namewrn, ParmList *declaratorparms);
+ extern void Swig_name_inherit(String *base, String *derived);
+ extern List *Swig_make_inherit_list(String *clsname, List *names, String *Namespaceprefix);
+ extern void Swig_inherit_base_symbols(List *bases);
+ extern int Swig_need_protected(Node *n);
+ extern int Swig_need_redefined_warn(Node *a, Node *b, int InClass);
+
+ extern String *Swig_name_make(Node *n, String *prefix, const_String_or_char_ptr cname, SwigType *decl, String *oldname);
+ extern String *Swig_name_warning(Node *n, String *prefix, String *name, SwigType *decl);
+ extern String *Swig_name_str(Node *n);
+ extern String *Swig_name_decl(Node *n);
+ extern String *Swig_name_fulldecl(Node *n);
+
+/* --- parameterized rename functions --- */
+
+ extern void Swig_name_object_set(Hash *namehash, String *name, SwigType *decl, DOH *object);
+ extern DOH *Swig_name_object_get(Hash *namehash, String *prefix, String *name, SwigType *decl);
+ extern void Swig_name_object_inherit(Hash *namehash, String *base, String *derived);
+ extern void Swig_features_get(Hash *features, String *prefix, String *name, SwigType *decl, Node *n);
+ extern void Swig_feature_set(Hash *features, const_String_or_char_ptr name, SwigType *decl, const_String_or_char_ptr featurename, const_String_or_char_ptr value, Hash *featureattribs);
+
+/* --- Misc --- */
+ extern char *Swig_copy_string(const char *c);
+ extern void Swig_set_fakeversion(const char *version);
+ extern const char *Swig_package_version(void);
+ extern String *Swig_package_version_hex(void);
+ extern void Swig_obligatory_macros(String *f_runtime, const char *language);
+ extern void Swig_banner(File *f);
+ extern void Swig_banner_target_lang(File *f, const_String_or_char_ptr commentchar);
+ extern String *Swig_strip_c_comments(const String *s);
+ extern String *Swig_new_subdirectory(String *basedirectory, String *subdirectory);
+ extern void Swig_filename_correct(String *filename);
+ extern String *Swig_filename_escape(String *filename);
+ extern String *Swig_filename_escape_space(String *filename);
+ extern void Swig_filename_unescape(String *filename);
+ extern int Swig_storage_isextern(Node *n);
+ extern int Swig_storage_isexternc(Node *n);
+ extern int Swig_storage_isstatic_custom(Node *n, const_String_or_char_ptr storage);
+ extern int Swig_storage_isstatic(Node *n);
+ extern String *Swig_string_escape(String *s);
+ extern String *Swig_string_mangle(const String *s);
+ extern void Swig_scopename_split(const String *s, String **prefix, String **last);
+ extern String *Swig_scopename_prefix(const String *s);
+ extern String *Swig_scopename_last(const String *s);
+ extern String *Swig_scopename_first(const String *s);
+ extern String *Swig_scopename_suffix(const String *s);
+ extern List *Swig_scopename_tolist(const String *s);
+ extern int Swig_scopename_check(const String *s);
+ extern String *Swig_string_lower(String *s);
+ extern String *Swig_string_upper(String *s);
+ extern String *Swig_string_title(String *s);
+ extern void Swig_offset_string(String *s, int number);
+ extern String *Swig_pcre_version(void);
+ extern void Swig_init(void);
+
+ extern int Swig_value_wrapper_mode(int mode);
+ extern int Swig_is_generated_overload(Node *n);
+
+ typedef enum { EMF_STANDARD, EMF_MICROSOFT } ErrorMessageFormat;
+
+ extern void Swig_warning(int num, const_String_or_char_ptr filename, int line, const char *fmt, ...);
+ extern void Swig_error(const_String_or_char_ptr filename, int line, const char *fmt, ...);
+ extern int Swig_error_count(void);
+ extern void Swig_error_silent(int s);
+ extern void Swig_warnfilter(const_String_or_char_ptr wlist, int val);
+ extern void Swig_warnall(void);
+ extern int Swig_warn_count(void);
+ extern void Swig_error_msg_format(ErrorMessageFormat format);
+ extern void Swig_diagnostic(const_String_or_char_ptr filename, int line, const char *fmt, ...);
+ extern String *Swig_stringify_with_location(DOH *object);
+
+/* --- C Wrappers --- */
+ extern void Swig_cresult_name_set(const char *new_name);
+ extern const char *Swig_cresult_name(void);
+ extern String *Swig_cparm_name(Parm *p, int i);
+ extern String *Swig_wrapped_var_type(SwigType *t, int varcref);
+ extern int Swig_cargs(Wrapper *w, ParmList *l);
+ extern String *Swig_cresult(SwigType *t, const_String_or_char_ptr name, const_String_or_char_ptr decl);
+
+ extern String *Swig_cfunction_call(const_String_or_char_ptr name, ParmList *parms);
+ extern String *Swig_cconstructor_call(const_String_or_char_ptr name);
+ extern String *Swig_cppconstructor_call(const_String_or_char_ptr name, ParmList *parms);
+ extern String *Swig_unref_call(Node *n);
+ extern String *Swig_ref_call(Node *n, const String *lname);
+ extern String *Swig_cdestructor_call(Node *n);
+ extern String *Swig_cppdestructor_call(Node *n);
+ extern String *Swig_cmemberset_call(const_String_or_char_ptr name, SwigType *type, String *self, int varcref);
+ extern String *Swig_cmemberget_call(const_String_or_char_ptr name, SwigType *t, String *self, int varcref);
+
+ extern int Swig_add_extension_code(Node *n, const String *function_name, ParmList *parms, SwigType *return_type, const String *code, int cplusplus, const String *self);
+ extern void Swig_replace_special_variables(Node *n, Node *parentnode, String *code);
+
+/* --- Transformations --- */
+
+ extern int Swig_MethodToFunction(Node *n, const_String_or_char_ptr nspace, String *classname, int flags, SwigType *director_type, int is_director);
+ extern int Swig_ConstructorToFunction(Node *n, const_String_or_char_ptr nspace, String *classname, String *none_comparison, String *director_ctor, int cplus, int flags, String *directorname);
+ extern int Swig_DestructorToFunction(Node *n, const_String_or_char_ptr nspace, String *classname, int cplus, int flags);
+ extern int Swig_MembersetToFunction(Node *n, String *classname, int flags);
+ extern int Swig_MembergetToFunction(Node *n, String *classname, int flags);
+ extern int Swig_VargetToFunction(Node *n, int flags);
+ extern int Swig_VarsetToFunction(Node *n, int flags);
+
+#define CWRAP_EXTEND 0x01
+#define CWRAP_SMART_POINTER 0x02
+#define CWRAP_NATURAL_VAR 0x04
+#define CWRAP_DIRECTOR_ONE_CALL 0x08
+#define CWRAP_DIRECTOR_TWO_CALLS 0x10
+#define CWRAP_ALL_PROTECTED_ACCESS 0x20
+#define CWRAP_SMART_POINTER_OVERLOAD 0x40
+
+/* --- Director Helpers --- */
+ extern Node *Swig_methodclass(Node *n);
+ extern int Swig_directorclass(Node *n);
+ extern Node *Swig_directormap(Node *n, String *type);
+
+/* --- Legacy Typemap API (somewhat simplified, ha!) --- */
+
+ extern void Swig_typemap_init(void);
+ extern void Swig_typemap_register(const_String_or_char_ptr tmap_method, ParmList *pattern, const_String_or_char_ptr code, ParmList *locals, ParmList *kwargs);
+ extern int Swig_typemap_copy(const_String_or_char_ptr tmap_method, ParmList *srcpattern, ParmList *pattern);
+ extern void Swig_typemap_clear(const_String_or_char_ptr tmap_method, ParmList *pattern);
+ extern int Swig_typemap_apply(ParmList *srcpat, ParmList *destpat);
+ extern void Swig_typemap_clear_apply(ParmList *pattern);
+ extern void Swig_typemap_replace_embedded_typemap(String *s, Node *file_line_node);
+ extern void Swig_typemap_debug(void);
+ extern void Swig_typemap_search_debug_set(void);
+ extern void Swig_typemap_used_debug_set(void);
+ extern void Swig_typemap_register_debug_set(void);
+
+ extern String *Swig_typemap_lookup(const_String_or_char_ptr tmap_method, Node *n, const_String_or_char_ptr lname, Wrapper *f);
+ extern String *Swig_typemap_lookup_out(const_String_or_char_ptr tmap_method, Node *n, const_String_or_char_ptr lname, Wrapper *f, String *actioncode);
+
+ extern void Swig_typemap_attach_parms(const_String_or_char_ptr tmap_method, ParmList *parms, Wrapper *f);
+
+/* --- Code fragment support --- */
+
+ extern void Swig_fragment_register(Node *fragment);
+ extern void Swig_fragment_emit(String *name);
+ extern void Swig_fragment_clear(String *section);
+
+/* --- Extension support --- */
+
+ extern Hash *Swig_extend_hash(void);
+ extern void Swig_extend_merge(Node *cls, Node *am);
+ extern void Swig_extend_append_previous(Node *cls, Node *am);
+ extern void Swig_extend_unused_check(void);
+
+/* hacks defined in C++ ! */
+ extern int Swig_director_mode(void);
+ extern int Swig_director_protected_mode(void);
+ extern int Swig_all_protected_mode(void);
+ extern void Wrapper_director_mode_set(int);
+ extern void Wrapper_director_protected_mode_set(int);
+ extern void Wrapper_all_protected_mode_set(int);
+ extern void Language_replace_special_variables(String *method, String *tm, Parm *parm);
+ extern void Swig_print(DOH *object, int count);
+ extern void Swig_print_with_location(DOH *object, int count);
+
+/* -- template init -- */
+ extern void SwigType_template_init(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/contrib/tools/swig/Source/Swig/swigfile.h b/contrib/tools/swig/Source/Swig/swigfile.h
new file mode 100644
index 0000000000..009599a112
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/swigfile.h
@@ -0,0 +1,41 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigfile.h
+ *
+ * File handling functions in the SWIG core
+ * ----------------------------------------------------------------------------- */
+
+extern List *Swig_add_directory(const_String_or_char_ptr dirname);
+extern void Swig_push_directory(const_String_or_char_ptr dirname);
+extern void Swig_pop_directory(void);
+extern String *Swig_last_file(void);
+extern List *Swig_search_path(void);
+extern FILE *Swig_include_open(const_String_or_char_ptr name);
+extern FILE *Swig_open(const_String_or_char_ptr name);
+extern String *Swig_read_file(FILE *f);
+extern String *Swig_include(const_String_or_char_ptr name);
+extern String *Swig_include_sys(const_String_or_char_ptr name);
+extern int Swig_insert_file(const_String_or_char_ptr name, File *outfile);
+extern void Swig_set_push_dir(int dopush);
+extern int Swig_get_push_dir(void);
+extern void Swig_register_filebyname(const_String_or_char_ptr filename, File *outfile);
+extern File *Swig_filebyname(const_String_or_char_ptr filename);
+extern String *Swig_file_extension(const_String_or_char_ptr filename);
+extern String *Swig_file_basename(const_String_or_char_ptr filename);
+extern String *Swig_file_filename(const_String_or_char_ptr filename);
+extern String *Swig_file_dirname(const_String_or_char_ptr filename);
+extern void Swig_file_debug_set(void);
+
+/* Delimiter used in accessing files and directories */
+
+#if defined(_WIN32)
+# define SWIG_FILE_DELIMITER "\\"
+#else
+# define SWIG_FILE_DELIMITER "/"
+#endif
diff --git a/contrib/tools/swig/Source/Swig/swigopt.h b/contrib/tools/swig/Source/Swig/swigopt.h
new file mode 100644
index 0000000000..86a477b8f7
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/swigopt.h
@@ -0,0 +1,18 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigopt.h
+ *
+ * Header file for the SWIG command line processing functions
+ * ----------------------------------------------------------------------------- */
+
+ extern void Swig_init_args(int argc, char **argv);
+ extern void Swig_mark_arg(int n);
+ extern int Swig_check_marked(int n);
+ extern void Swig_check_options(int check_input);
+ extern void Swig_arg_error(void);
diff --git a/contrib/tools/swig/Source/Swig/swigparm.h b/contrib/tools/swig/Source/Swig/swigparm.h
new file mode 100644
index 0000000000..7b63546ec0
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/swigparm.h
@@ -0,0 +1,35 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigparm.h
+ *
+ * Functions related to the handling of function/method parameters and
+ * parameter lists.
+ * ----------------------------------------------------------------------------- */
+
+/* Individual parameters */
+extern Parm *NewParm(SwigType *type, const_String_or_char_ptr name, Node *from_node);
+extern Parm *NewParmWithoutFileLineInfo(SwigType *type, const_String_or_char_ptr name);
+extern Parm *NewParmNode(SwigType *type, Node *from_node);
+extern Parm *CopyParm(Parm *p);
+
+/* Parameter lists */
+extern ParmList *CopyParmList(ParmList *);
+extern ParmList *CopyParmListMax(ParmList *, int count);
+extern int ParmList_len(ParmList *);
+extern int ParmList_numrequired(ParmList *);
+extern int ParmList_has_defaultargs(ParmList *p);
+extern int ParmList_has_varargs(ParmList *p);
+
+/* Output functions */
+extern String *ParmList_str(ParmList *);
+extern String *ParmList_str_defaultargs(ParmList *);
+extern String *ParmList_str_multibrackets(ParmList *);
+extern String *ParmList_protostr(ParmList *);
+
+
diff --git a/contrib/tools/swig/Source/Swig/swigscan.h b/contrib/tools/swig/Source/Swig/swigscan.h
new file mode 100644
index 0000000000..6c9d1ff7fc
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/swigscan.h
@@ -0,0 +1,119 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigscan.h
+ *
+ * C/C++ scanner.
+ * ----------------------------------------------------------------------------- */
+
+typedef struct Scanner Scanner;
+
+extern Scanner *NewScanner(void);
+extern void DelScanner(Scanner *);
+extern void Scanner_clear(Scanner *);
+extern void Scanner_push(Scanner *, String *);
+extern void Scanner_pushtoken(Scanner *, int, const_String_or_char_ptr value);
+extern int Scanner_token(Scanner *);
+extern String *Scanner_text(Scanner *);
+extern void Scanner_skip_line(Scanner *);
+extern int Scanner_skip_balanced(Scanner *, int startchar, int endchar);
+extern String *Scanner_get_raw_text_balanced(Scanner *, int startchar, int endchar);
+extern void Scanner_set_location(Scanner *, String *file, int line);
+extern String *Scanner_file(Scanner *);
+extern int Scanner_line(Scanner *);
+extern int Scanner_start_line(Scanner *);
+extern void Scanner_idstart(Scanner *, const char *idchar);
+extern String *Scanner_errmsg(Scanner *);
+extern int Scanner_errline(Scanner *);
+extern int Scanner_isoperator(int tokval);
+extern void Scanner_locator(Scanner *, String *loc);
+
+/* Note: Tokens in range 100+ are for C/C++ operators */
+
+#define SWIG_MAXTOKENS 200
+#define SWIG_TOKEN_LPAREN 1 /* ( */
+#define SWIG_TOKEN_RPAREN 2 /* ) */
+#define SWIG_TOKEN_SEMI 3 /* ; */
+#define SWIG_TOKEN_LBRACE 4 /* { */
+#define SWIG_TOKEN_RBRACE 5 /* } */
+#define SWIG_TOKEN_LBRACKET 6 /* [ */
+#define SWIG_TOKEN_RBRACKET 7 /* ] */
+#define SWIG_TOKEN_BACKSLASH 8 /* \ */
+#define SWIG_TOKEN_ENDLINE 9 /* \n */
+#define SWIG_TOKEN_STRING 10 /* "str" */
+#define SWIG_TOKEN_POUND 11 /* # */
+#define SWIG_TOKEN_COLON 12 /* : */
+#define SWIG_TOKEN_DCOLON 13 /* :: */
+#define SWIG_TOKEN_DCOLONSTAR 14 /* ::* */
+#define SWIG_TOKEN_ID 15 /* identifier */
+#define SWIG_TOKEN_FLOAT 16 /* 3.1415F */
+#define SWIG_TOKEN_DOUBLE 17 /* 3.1415 */
+#define SWIG_TOKEN_INT 18 /* 314 */
+#define SWIG_TOKEN_UINT 19 /* 314U */
+#define SWIG_TOKEN_LONG 20 /* 314L */
+#define SWIG_TOKEN_ULONG 21 /* 314UL */
+#define SWIG_TOKEN_CHAR 22 /* 'charconst' */
+#define SWIG_TOKEN_PERIOD 23 /* . */
+#define SWIG_TOKEN_AT 24 /* @ */
+#define SWIG_TOKEN_DOLLAR 25 /* $ */
+#define SWIG_TOKEN_CODEBLOCK 26 /* %{ ... %} ... */
+#define SWIG_TOKEN_RSTRING 27 /* `charconst` */
+#define SWIG_TOKEN_LONGLONG 28 /* 314LL */
+#define SWIG_TOKEN_ULONGLONG 29 /* 314ULL */
+#define SWIG_TOKEN_QUESTION 30 /* ? */
+#define SWIG_TOKEN_COMMENT 31 /* C or C++ comment */
+#define SWIG_TOKEN_BOOL 32 /* true or false */
+#define SWIG_TOKEN_WSTRING 33 /* L"str" */
+#define SWIG_TOKEN_WCHAR 34 /* L'c' */
+#define SWIG_TOKEN_ELLIPSIS 35 /* ... */
+#define SWIG_TOKEN_LLBRACKET 36 /* [[ */
+#define SWIG_TOKEN_RRBRACKET 37 /* ]] */
+
+#define SWIG_TOKEN_ILLEGAL 99
+#define SWIG_TOKEN_ERROR -1
+
+#define SWIG_TOKEN_COMMA 101 /* , */
+#define SWIG_TOKEN_STAR 102 /* * */
+#define SWIG_TOKEN_TIMES 102 /* * */
+#define SWIG_TOKEN_EQUAL 103 /* = */
+#define SWIG_TOKEN_EQUALTO 104 /* == */
+#define SWIG_TOKEN_NOTEQUAL 105 /* != */
+#define SWIG_TOKEN_PLUS 106 /* + */
+#define SWIG_TOKEN_MINUS 107 /* - */
+#define SWIG_TOKEN_AND 108 /* & */
+#define SWIG_TOKEN_LAND 109 /* && */
+#define SWIG_TOKEN_OR 110 /* | */
+#define SWIG_TOKEN_LOR 111 /* || */
+#define SWIG_TOKEN_XOR 112 /* ^ */
+#define SWIG_TOKEN_LESSTHAN 113 /* < */
+#define SWIG_TOKEN_GREATERTHAN 114 /* > */
+#define SWIG_TOKEN_LTEQUAL 115 /* <= */
+#define SWIG_TOKEN_GTEQUAL 116 /* >= */
+#define SWIG_TOKEN_NOT 117 /* ~ */
+#define SWIG_TOKEN_LNOT 118 /* ! */
+#define SWIG_TOKEN_SLASH 119 /* / */
+#define SWIG_TOKEN_DIVIDE 119 /* / */
+#define SWIG_TOKEN_PERCENT 120 /* % */
+#define SWIG_TOKEN_MODULO 120 /* % */
+#define SWIG_TOKEN_LSHIFT 121 /* << */
+#define SWIG_TOKEN_RSHIFT 122 /* >> */
+#define SWIG_TOKEN_PLUSPLUS 123 /* ++ */
+#define SWIG_TOKEN_MINUSMINUS 124 /* -- */
+#define SWIG_TOKEN_PLUSEQUAL 125 /* += */
+#define SWIG_TOKEN_MINUSEQUAL 126 /* -= */
+#define SWIG_TOKEN_TIMESEQUAL 127 /* *= */
+#define SWIG_TOKEN_DIVEQUAL 128 /* /= */
+#define SWIG_TOKEN_ANDEQUAL 129 /* &= */
+#define SWIG_TOKEN_OREQUAL 130 /* |= */
+#define SWIG_TOKEN_XOREQUAL 131 /* ^= */
+#define SWIG_TOKEN_LSEQUAL 132 /* <<= */
+#define SWIG_TOKEN_RSEQUAL 133 /* >>= */
+#define SWIG_TOKEN_MODEQUAL 134 /* %= */
+#define SWIG_TOKEN_ARROW 135 /* -> */
+#define SWIG_TOKEN_ARROWSTAR 136 /* ->* */
+#define SWIG_TOKEN_LTEQUALGT 137 /* <=> */
diff --git a/contrib/tools/swig/Source/Swig/swigtree.h b/contrib/tools/swig/Source/Swig/swigtree.h
new file mode 100644
index 0000000000..8d63d8fd33
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/swigtree.h
@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigtree.h
+ *
+ * These functions are used to access and manipulate the SWIG parse tree.
+ * The structure of this tree is modeled directly after XML-DOM. The attribute
+ * and function names are meant to be similar.
+ * ----------------------------------------------------------------------------- */
+
+/* Macros to traverse the DOM tree */
+
+#define nodeType(x) Getattr(x,"nodeType")
+#define parentNode(x) Getattr(x,"parentNode")
+#define previousSibling(x) Getattr(x,"previousSibling")
+#define nextSibling(x) Getattr(x,"nextSibling")
+#define firstChild(x) Getattr(x,"firstChild")
+#define lastChild(x) Getattr(x,"lastChild")
+
+/* Macros to set up the DOM tree (mostly used by the parser) */
+
+#define set_nodeType(x,v) Setattr(x,"nodeType",v)
+#define set_parentNode(x,v) Setattr(x,"parentNode",v)
+#define set_previousSibling(x,v) Setattr(x,"previousSibling",v)
+#define set_nextSibling(x,v) Setattr(x,"nextSibling",v)
+#define set_firstChild(x,v) Setattr(x,"firstChild",v)
+#define set_lastChild(x,v) Setattr(x,"lastChild",v)
+
+/* Utility functions */
+
+extern int checkAttribute(Node *obj, const_String_or_char_ptr name, const_String_or_char_ptr value);
+extern void appendChild(Node *node, Node *child);
+extern void prependChild(Node *node, Node *child);
+extern void removeNode(Node *node);
+extern Node *copyNode(Node *node);
+extern void appendSibling(Node *node, Node *child);
+
+/* Node restoration/restore functions */
+
+extern void Swig_require(const char *ns, Node *node, ...);
+extern void Swig_save(const char *ns, Node *node, ...);
+extern void Swig_restore(Node *node);
+
+/* Debugging of parse trees */
+
+extern void Swig_print_tags(File *obj, Node *root);
+extern void Swig_print_tree(Node *obj);
+extern void Swig_print_node(Node *obj);
+extern int Swig_print_quiet(int quiet);
diff --git a/contrib/tools/swig/Source/Swig/swigwrap.h b/contrib/tools/swig/Source/Swig/swigwrap.h
new file mode 100644
index 0000000000..b584ca495b
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/swigwrap.h
@@ -0,0 +1,31 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * swigwrap.h
+ *
+ * Functions related to wrapper objects.
+ * ----------------------------------------------------------------------------- */
+
+typedef struct Wrapper {
+ Hash *localh;
+ String *def;
+ String *locals;
+ String *code;
+} Wrapper;
+
+extern Wrapper *NewWrapper(void);
+extern void DelWrapper(Wrapper *w);
+extern void Wrapper_compact_print_mode_set(int flag);
+extern void Wrapper_pretty_print(String *str, File *f);
+extern void Wrapper_compact_print(String *str, File *f);
+extern void Wrapper_print(Wrapper *w, File *f);
+extern int Wrapper_add_local(Wrapper *w, const_String_or_char_ptr name, const_String_or_char_ptr decl);
+extern int Wrapper_add_localv(Wrapper *w, const_String_or_char_ptr name, ...);
+extern int Wrapper_check_local(Wrapper *w, const_String_or_char_ptr name);
+extern char *Wrapper_new_local(Wrapper *w, const_String_or_char_ptr name, const_String_or_char_ptr decl);
+extern char *Wrapper_new_localv(Wrapper *w, const_String_or_char_ptr name, ...);
diff --git a/contrib/tools/swig/Source/Swig/symbol.c b/contrib/tools/swig/Source/Swig/symbol.c
new file mode 100644
index 0000000000..107b1caef9
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/symbol.c
@@ -0,0 +1,2128 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * symbol.c
+ *
+ * This file implements the SWIG symbol table. See details below.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+#include <ctype.h>
+
+/* #define SWIG_DEBUG*/
+/* -----------------------------------------------------------------------------
+ * Synopsis
+ *
+ * This module provides symbol table management for all of SWIG. In previous
+ * releases, the management of symbols was rather haphazard. This module tries
+ * to correct that.
+ *
+ * All symbols are associated with simple identifiers. For example, here are some
+ * declarations that generate symbol table entries:
+ *
+ * decl symbol
+ * -------------- ------------
+ * void foo(int); foo
+ * int x; x
+ * typedef int *blah; blah
+ *
+ * Associated with each symbol is a Hash table that can contain any set of
+ * attributes that make sense for that object. For example:
+ *
+ * typedef int *blah; ----> "name" : 'blah'
+ * "type" : 'int'
+ * "decl" : 'p.'
+ * "storage" : 'typedef'
+ *
+ * In some cases, the symbol table needs to manage overloaded entries. For instance,
+ * overloaded functions. In this case, a linked list is built. The "sym:nextSibling"
+ * attribute is reserved to hold a link to the next entry. For example:
+ *
+ * int foo(int); --> "name" : "foo" "name" : "foo"
+ * int foo(int,double); "type" : "int" "type" : "int"
+ * "decl" : "f(int)." "decl" : "f(int,double)."
+ * ... ...
+ * "sym:nextSibling" : --------> "sym:nextSibling": --------> ...
+ *
+ * When more than one symbol has the same name, the symbol declarator is
+ * used to detect duplicates. For example, in the above case, foo(int) and
+ * foo(int,double) are different because their "decl" attribute is different.
+ * However, if a third declaration "foo(int)" was made, it would generate a
+ * conflict (due to having a declarator that matches a previous entry).
+ *
+ * Structures and classes:
+ *
+ * C/C++ symbol tables are normally managed in a few different spaces. The
+ * most visible namespace is reserved for functions, variables, typedef, enum values
+ * and such. In C, a separate tag-space is reserved for 'struct name', 'class name',
+ * and 'union name' declarations. In SWIG, a single namespace is used for everything
+ * this means that certain incompatibilities will arise with some C programs. For instance:
+ *
+ * struct Foo {
+ * ...
+ * }
+ *
+ * int Foo(); // Error. Name clash. Works in C though
+ *
+ * Due to the unified namespace for structures, special handling is performed for
+ * the following:
+ *
+ * typedef struct Foo {
+ *
+ * } Foo;
+ *
+ * In this case, the symbol table contains an entry for the structure itself. The
+ * typedef is left out of the symbol table.
+ *
+ * Target language vs C:
+ *
+ * The symbol tables are normally managed *in the namespace of the target language*.
+ * This means that name-collisions can be resolved using %rename and related
+ * directives. A quirk of this is that sometimes the symbol tables need to
+ * be used for C type resolution as well. To handle this, each symbol table
+ * also has a C-symbol table lurking behind the scenes. This is used to locate
+ * symbols in the C namespace. However, this symbol table is not used for error
+ * reporting nor is it used for anything else during code generation.
+ *
+ * Symbol table structure:
+ *
+ * Symbol tables themselves are a special kind of node that is organized just like
+ * a normal parse tree node. Symbol tables are organized in a tree that can be
+ * traversed using the SWIG-DOM API. The following attributes names are reserved.
+ *
+ * name -- Name of the scope defined by the symbol table (if any)
+ * This name is the C-scope name and is not affected by
+ * %renaming operations
+ * symtab -- Hash table mapping identifiers to nodes.
+ * csymtab -- Hash table mapping C identifiers to nodes.
+ *
+ * Reserved attributes on symbol objects:
+ *
+ * When a symbol is placed in the symbol table, the following attributes
+ * are set:
+ *
+ * sym:name -- Symbol name
+ * sym:nextSibling -- Next symbol (if overloaded)
+ * sym:previousSibling -- Previous symbol (if overloaded)
+ * sym:symtab -- Symbol table object holding the symbol
+ * sym:overloaded -- Set to the first symbol if overloaded
+ *
+ * These names are modeled after XML namespaces. In particular, every attribute
+ * pertaining to symbol table management is prefaced by the "sym:" prefix.
+ *
+ * An example dump of the parse tree showing symbol table entries for the
+ * following code should clarify this:
+ *
+ * namespace OuterNamespace {
+ * namespace InnerNamespace {
+ * class Class {
+ * };
+ * struct Struct {
+ * int Var;
+ * };
+ * }
+ * }
+ *
+ * +++ namespace ----------------------------------------
+ * | sym:name - "OuterNamespace"
+ * | symtab - 0xa064bf0
+ * | sym:symtab - 0xa041690
+ * | sym:overname - "__SWIG_0"
+ *
+ * +++ namespace ----------------------------------------
+ * | sym:name - "InnerNamespace"
+ * | symtab - 0xa064cc0
+ * | sym:symtab - 0xa064bf0
+ * | sym:overname - "__SWIG_0"
+ *
+ * +++ class ----------------------------------------
+ * | sym:name - "Class"
+ * | symtab - 0xa064d80
+ * | sym:symtab - 0xa064cc0
+ * | sym:overname - "__SWIG_0"
+ * |
+ * +++ class ----------------------------------------
+ * | sym:name - "Struct"
+ * | symtab - 0xa064f00
+ * | sym:symtab - 0xa064cc0
+ * | sym:overname - "__SWIG_0"
+ *
+ * +++ cdecl ----------------------------------------
+ * | sym:name - "Var"
+ * | sym:symtab - 0xa064f00
+ * | sym:overname - "__SWIG_0"
+ * |
+ *
+ *
+ * Each class and namespace has its own scope and thus a new symbol table (sym)
+ * is created. The sym attribute is only set for the first entry in the symbol
+ * table. The sym:symtab entry points to the symbol table in which the symbol
+ * exists, so for example, Struct is in the scope OuterNamespace::InnerNamespace
+ * so sym:symtab points to this symbol table (0xa064cc0).
+ *
+ * ----------------------------------------------------------------------------- */
+
+static Hash *current = 0; /* The current symbol table hash */
+static Hash *ccurrent = 0; /* The current c symbol table hash */
+static Hash *current_symtab = 0; /* Current symbol table node */
+static Hash *symtabs = 0; /* Hash of all symbol tables by fully-qualified name */
+static Hash *global_scope = 0; /* Global scope */
+
+static int use_inherit = 1;
+
+/* common attribute keys, to avoid calling find_key all the times */
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_print_tables()
+ *
+ * Debug display of symbol tables
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_print_tables(Symtab *symtab) {
+ if (!symtab)
+ symtab = current_symtab;
+
+ Printf(stdout, "SYMBOL TABLES start =======================================\n");
+ Swig_print_tree(symtab);
+ Printf(stdout, "SYMBOL TABLES finish =======================================\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_print_tables_summary()
+ *
+ * Debug summary display of all symbol tables by fully-qualified name
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_print_tables_summary(void) {
+ Printf(stdout, "SYMBOL TABLES SUMMARY start =======================================\n");
+ Swig_print_node(symtabs);
+ Printf(stdout, "SYMBOL TABLES SUMMARY finish =======================================\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * symbol_print_symbols()
+ * ----------------------------------------------------------------------------- */
+
+static void symbol_print_symbols(const char *symboltabletype, const char *nextSibling) {
+ Node *table = symtabs;
+ Iterator ki = First(table);
+ int show_pointers = 0;
+ while (ki.key) {
+ String *k = ki.key;
+ Printf(stdout, "===================================================\n");
+ Printf(stdout, "%s -\n", k);
+ {
+ Symtab *symtab = Getattr(Getattr(table, k), symboltabletype);
+ Iterator it = First(symtab);
+ while (it.key) {
+ String *symname = it.key;
+ Printf(stdout, " %s (%s)", symname, nodeType(it.item));
+ if (show_pointers)
+ Printf(stdout, " %p", it.item);
+ Printf(stdout, "\n");
+ {
+ Node *sibling = Getattr(it.item, nextSibling);
+ while (sibling) {
+ Printf(stdout, " %s (%s)", symname, nodeType(sibling));
+ if (show_pointers)
+ Printf(stdout, " %p", sibling);
+ Printf(stdout, "\n");
+ sibling = Getattr(sibling, nextSibling);
+ }
+ }
+ it = Next(it);
+ }
+ }
+ ki = Next(ki);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_print_symbols()
+ *
+ * Debug display of all the target language symbols
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_print_symbols(void) {
+ Printf(stdout, "SYMBOLS start =======================================\n");
+ symbol_print_symbols("symtab", "sym:nextSibling");
+ Printf(stdout, "SYMBOLS finish =======================================\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_print_csymbols()
+ *
+ * Debug display of all the C symbols
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_print_csymbols(void) {
+ Printf(stdout, "CSYMBOLS start =======================================\n");
+ symbol_print_symbols("csymtab", "csym:nextSibling");
+ Printf(stdout, "CSYMBOLS finish =======================================\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_init()
+ *
+ * Create a new symbol table object
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_init(void) {
+
+ current = NewHash();
+ current_symtab = NewHash();
+ ccurrent = NewHash();
+ set_nodeType(current_symtab, "symboltable");
+ Setattr(current_symtab, "symtab", current);
+ Delete(current);
+ Setattr(current_symtab, "csymtab", ccurrent);
+ Delete(ccurrent);
+
+ /* Set the global scope */
+ symtabs = NewHash();
+ Setattr(symtabs, "", current_symtab);
+ Delete(current_symtab);
+ global_scope = current_symtab;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_setscopename()
+ *
+ * Set the C scopename of the current symbol table.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_setscopename(const_String_or_char_ptr name) {
+ String *qname;
+ /* assert(!Getattr(current_symtab,"name")); */
+ Setattr(current_symtab, "name", name);
+
+ /* Set nested scope in parent */
+
+ qname = Swig_symbol_qualifiedscopename(current_symtab);
+
+ /* Save a reference to this scope */
+ Setattr(symtabs, qname, current_symtab);
+ Delete(qname);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_getscopename()
+ *
+ * Get the C scopename of the current symbol table
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_symbol_getscopename(void) {
+ return Getattr(current_symtab, "name");
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_getscope()
+ *
+ * Given a fully qualified C scopename, this function returns a symbol table
+ * ----------------------------------------------------------------------------- */
+
+Symtab *Swig_symbol_getscope(const_String_or_char_ptr name) {
+ if (!symtabs)
+ return 0;
+ if (Equal("::", (const_String_or_char_ptr ) name))
+ name = "";
+ return Getattr(symtabs, name);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_qualifiedscopename()
+ *
+ * Get the fully qualified C scopename of a symbol table. Note, this only pertains
+ * to the C/C++ scope name. It is not affected by renaming.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_symbol_qualifiedscopename(Symtab *symtab) {
+ String *result = 0;
+ Hash *parent;
+ String *name;
+ if (!symtab)
+ symtab = current_symtab;
+ parent = Getattr(symtab, "parentNode");
+ if (parent) {
+ result = Swig_symbol_qualifiedscopename(parent);
+ }
+ name = Getattr(symtab, "name");
+ if (name) {
+ if (!result) {
+ result = NewStringEmpty();
+ }
+ if (Len(result)) {
+ Printv(result, "::", name, NIL);
+ } else {
+ Append(result, name);
+ }
+ }
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_qualified_language_scopename()
+ *
+ * Get the fully qualified C scopename of a symbol table but using a language
+ * specific separator for the scopenames. Basically the same as
+ * Swig_symbol_qualifiedscopename() but using the different separator.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_symbol_qualified_language_scopename(Symtab *n) {
+ /* TODO: fix for %rename to work */
+ String *result = Swig_symbol_qualifiedscopename(n);
+ Replaceall(result, "::", NSPACE_SEPARATOR);
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_newscope()
+ *
+ * Create a new scope. Returns the newly created scope.
+ * ----------------------------------------------------------------------------- */
+
+Symtab *Swig_symbol_newscope(void) {
+ Hash *n;
+ Hash *hsyms, *h;
+
+ hsyms = NewHash();
+ h = NewHash();
+
+ set_nodeType(h, "symboltable");
+ Setattr(h, "symtab", hsyms);
+ Delete(hsyms);
+ set_parentNode(h, current_symtab);
+
+ n = lastChild(current_symtab);
+ if (!n) {
+ set_firstChild(current_symtab, h);
+ } else {
+ set_nextSibling(n, h);
+ Delete(h);
+ }
+ set_lastChild(current_symtab, h);
+ current = hsyms;
+ ccurrent = NewHash();
+ Setattr(h, "csymtab", ccurrent);
+ Delete(ccurrent);
+ current_symtab = h;
+ return h;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_setscope()
+ *
+ * Set the current scope. Returns the previous current scope.
+ * ----------------------------------------------------------------------------- */
+
+Symtab *Swig_symbol_setscope(Symtab *sym) {
+ Symtab *ret = current_symtab;
+ current_symtab = sym;
+ current = Getattr(sym, "symtab");
+ assert(current);
+ ccurrent = Getattr(sym, "csymtab");
+ assert(ccurrent);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_popscope()
+ *
+ * Pop out of the current scope. Returns the popped scope and sets the
+ * scope to the parent scope.
+ * ----------------------------------------------------------------------------- */
+
+Symtab *Swig_symbol_popscope(void) {
+ Hash *h = current_symtab;
+ current_symtab = Getattr(current_symtab, "parentNode");
+ assert(current_symtab);
+ current = Getattr(current_symtab, "symtab");
+ assert(current);
+ ccurrent = Getattr(current_symtab, "csymtab");
+ assert(ccurrent);
+ return h;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_global_scope()
+ *
+ * Return the symbol table for the global scope.
+ * ----------------------------------------------------------------------------- */
+
+Symtab *Swig_symbol_global_scope(void) {
+ return global_scope;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_current()
+ *
+ * Return the current symbol table.
+ * ----------------------------------------------------------------------------- */
+
+Symtab *Swig_symbol_current(void) {
+ return current_symtab;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_alias()
+ *
+ * Makes an alias for a symbol in the global symbol table.
+ * Primarily for namespace aliases such as 'namespace X = Y;'.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_alias(const_String_or_char_ptr aliasname, Symtab *s) {
+ String *qname = Swig_symbol_qualifiedscopename(current_symtab);
+ if (qname) {
+ Printf(qname, "::%s", aliasname);
+ } else {
+ qname = NewString(aliasname);
+ }
+ if (!Getattr(symtabs, qname)) {
+ Setattr(symtabs, qname, s);
+ }
+ Delete(qname);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_inherit()
+ *
+ * Inherit symbols from another scope. Primarily for C++ inheritance and
+ * for using directives, such as 'using namespace X;'
+ * but not for using declarations, such as 'using A;'.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_inherit(Symtab *s) {
+ int i, ilen;
+ List *inherit = Getattr(current_symtab, "inherit");
+ if (!inherit) {
+ inherit = NewList();
+ Setattr(current_symtab, "inherit", inherit);
+ Delete(inherit);
+ }
+
+ if (s == current_symtab) {
+ Swig_warning(WARN_PARSE_REC_INHERITANCE, Getfile(s), Getline(s), "Recursive scope inheritance of '%s'.\n", Getattr(s, "name"));
+ return;
+ }
+ assert(s != current_symtab);
+ ilen = Len(inherit);
+ for (i = 0; i < ilen; i++) {
+ Node *n = Getitem(inherit, i);
+ if (n == s)
+ return; /* Already inherited */
+ }
+ Append(inherit, s);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_cadd()
+ *
+ * Adds a node to the C symbol table only.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_cadd(const_String_or_char_ptr name, Node *n) {
+ Node *append = 0;
+ Node *cn;
+ /* There are a few options for weak symbols. A "weak" symbol
+ is any symbol that can be replaced by another symbol in the C symbol
+ table. An example would be a forward class declaration. A forward
+ class sits in the symbol table until a real class declaration comes along.
+
+ Certain symbols are marked as "sym:typename". These are important
+ symbols related to the C++ type-system and take precedence in the C
+ symbol table. An example might be code like this:
+
+ template<class T> T foo(T x);
+ int foo(int);
+
+ In this case, the template is marked with "sym:typename" so that it
+ stays in the C symbol table (so that it can be expanded using %template).
+ */
+
+ if (!name)
+ return;
+
+ if (SwigType_istemplate(name)) {
+ String *cname = NewString(name);
+ String *dname = Swig_symbol_template_deftype(cname, 0);
+ if (!Equal(dname, name)) {
+ Swig_symbol_cadd(dname, n);
+ }
+ Delete(dname);
+ Delete(cname);
+ }
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_cadd %s %p\n", name, n);
+#endif
+ cn = Getattr(ccurrent, name);
+
+ if (cn && (Getattr(cn, "sym:typename"))) {
+ /* The node in the C symbol table is a typename. Do nothing */
+ /* We might append the symbol at the end */
+ append = n;
+ } else if (cn && (Getattr(cn, "sym:weak"))) {
+ /* The node in the symbol table is weak. Replace it */
+ if (checkAttribute(cn, "nodeType", "template")
+ && checkAttribute(cn, "templatetype", "classforward")) {
+ /* The node is a template classforward declaration, and the
+ default template parameters here take precedence. */
+ ParmList *pc = Getattr(cn, "templateparms");
+ ParmList *pn = Getattr(n, "templateparms");
+#ifdef SWIG_DEBUG
+ Printf(stderr, "found template classforward %s\n", Getattr(cn, "name"));
+#endif
+ while (pc && pn) {
+ String *value = Getattr(pc, "value");
+ if (value) {
+#ifdef SWIG_DEBUG
+ Printf(stderr, "add default template value %s %s\n", Getattr(pc, "name"), value);
+#endif
+ Setattr(pn, "value", value);
+ }
+ pc = nextSibling(pc);
+ pn = nextSibling(pn);
+ }
+ Setattr(n, "templateparms", Getattr(cn, "templateparms"));
+ }
+ Setattr(ccurrent, name, n);
+
+ } else if (cn && (Getattr(n, "sym:weak"))) {
+ /* The node being added is weak. Don't worry about it */
+ } else if (cn && (Getattr(n, "sym:typename"))) {
+ /* The node being added is a typename. We definitely add it */
+ Setattr(ccurrent, name, n);
+ append = cn;
+ } else if (cn && (Checkattr(cn, "nodeType", "templateparm"))) {
+ Swig_error(Getfile(n), Getline(n), "Declaration of '%s' shadows template parameter,\n", name);
+ Swig_error(Getfile(cn), Getline(cn), "previous template parameter declaration '%s'.\n", name);
+ return;
+ } else if (cn) {
+ append = n;
+ } else if (!cn) {
+ /* No conflict. Add the symbol */
+ Setattr(ccurrent, name, n);
+ }
+
+ /* Multiple entries in the C symbol table. We append to the symbol table */
+ if (append) {
+ Node *fn, *pn = 0;
+ cn = Getattr(ccurrent, name);
+ fn = cn;
+ while (fn) {
+ pn = fn;
+ if (fn == append) {
+ /* already added. Bail */
+ return;
+ }
+ fn = Getattr(fn, "csym:nextSibling");
+ }
+ if (pn) {
+ Setattr(pn, "csym:nextSibling", append);
+ }
+ }
+
+ /* Special typedef handling. When a typedef node is added to the symbol table, we
+ might have to add a type alias. This would occur if the typedef mapped to another
+ scope in the system. For example:
+
+ class Foo {
+ };
+
+ typedef Foo OtherFoo;
+
+ In this case, OtherFoo becomes an alias for Foo. */
+
+ {
+ Node *td = n;
+ while (td && ((Equal(nodeType(td), "cdecl") && Checkattr(td, "storage", "typedef")) || (Equal(nodeType(td), "using") && !Getattr(n, "namespace")))) {
+ SwigType *type;
+ Node *td1;
+ int using_not_typedef = Equal(nodeType(td), "using");
+ type = Copy(Getattr(td, using_not_typedef ? "uname" : "type"));
+ SwigType_push(type, Getattr(td, "decl"));
+ td1 = Swig_symbol_clookup(type, 0);
+
+ /* Fix pathetic case #1214313:
+
+ class Foo
+ {
+ };
+
+ typedef Foo FooBar;
+
+ class CBaz
+ {
+ public:
+ typedef FooBar Foo;
+ };
+
+ ie, when Foo -> FooBar -> Foo, jump one scope up when possible.
+
+ */
+ if (td1) {
+ String *st = 0;
+ String *sn = Getattr(td, "name");
+ if (Equal(nodeType(td1), "cdecl") && Checkattr(td1, "storage", "typedef"))
+ st = Getattr(td1, "type");
+ else if (Equal(nodeType(td1), "using") && !Getattr(td1, "namespace"))
+ st = Getattr(td1, "uname");
+ if (st && sn && Equal(st, sn)) {
+ Symtab *sc = Getattr(current_symtab, "parentNode");
+ if (sc)
+ td1 = Swig_symbol_clookup(type, sc);
+ }
+ }
+
+ Delete(type);
+ if (td1 == td)
+ break;
+ td = td1;
+ if (td) {
+ Symtab *st = Getattr(td, "symtab");
+ if (st) {
+ Swig_symbol_alias(Getattr(n, "name"), st);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_add()
+ *
+ * Adds a node to the symbol table. Returns the node itself if successfully
+ * added. Otherwise, it returns the symbol table entry of the conflicting node.
+ *
+ * Also places the symbol in a behind-the-scenes C symbol table. This is needed
+ * for namespace support, type resolution, and other issues.
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_symbol_add(const_String_or_char_ptr symname, Node *n) {
+ Hash *c, *cl = 0;
+ SwigType *decl, *ndecl;
+ String *cstorage, *nstorage;
+ int nt = 0, ct = 0;
+ int pn = 0;
+ int u1 = 0, u2 = 0;
+ String *name, *overname;
+
+ /* See if the node has a name. If so, we place in the C symbol table for this
+ scope. We don't worry about overloading here---the primary purpose of this
+ is to record information for type/name resolution for later. Conflicts
+ in C namespaces are errors, but these will be caught by the C++ compiler
+ when compiling the wrapper code */
+
+
+ /* There are a few options for weak symbols. A "weak" symbol
+ is any symbol that can be replaced by another symbol in the C symbol
+ table. An example would be a forward class declaration. A forward
+ class sits in the symbol table until a real class declaration comes along.
+
+ Certain symbols are marked as "sym:typename". These are important
+ symbols related to the C++ type-system and take precedence in the C
+ symbol table. An example might be code like this:
+
+ template<class T> T foo(T x);
+ int foo(int);
+
+ In this case, the template is marked with "sym:typename" so that it
+ stays in the C symbol table (so that it can be expanded using %template).
+ */
+
+ name = Getattr(n, "name");
+ if (name && Len(name)) {
+ Swig_symbol_cadd(name, n);
+ }
+
+ /* No symbol name defined. We return. */
+ if (!symname) {
+ Setattr(n, "sym:symtab", current_symtab);
+ return n;
+ }
+
+ /* If node is ignored. We don't proceed any further */
+ if (GetFlag(n, "feature:ignore"))
+ return n;
+
+ /* See if the symbol already exists in the table */
+ c = Getattr(current, symname);
+
+ /* Check for a weak symbol. A weak symbol is allowed to be in the
+ symbol table, but is silently overwritten by other symbols. An example
+ would be a forward class declaration. For instance:
+
+ class Foo;
+
+ In this case, "Foo" sits in the symbol table. However, the
+ definition of Foo would replace the entry if it appeared later. */
+
+ if (c && Getattr(c, "sym:weak")) {
+ c = 0;
+ }
+ if (c) {
+ /* There is a symbol table conflict. There are a few cases to consider here:
+ (1) A conflict between a class/enum and a typedef declaration is okay.
+ In this case, the symbol table entry is set to the class/enum declaration
+ itself, not the typedef.
+ (2) A conflict between namespaces is okay--namespaces are open
+ (3) Otherwise, overloading is only allowed for functions
+ (4) This special case is okay: a class template instantiated with same name as the template's name
+ */
+
+ /* Check for namespaces */
+ String *ntype = Getattr(n, "nodeType");
+ if ((Equal(ntype, Getattr(c, "nodeType"))) && ((Equal(ntype, "namespace")))) {
+ Node *cl, *pcl = 0;
+ cl = c;
+ while (cl) {
+ pcl = cl;
+ cl = Getattr(cl, "sym:nextSibling");
+ }
+ Setattr(pcl, "sym:nextSibling", n);
+ Setattr(n, "sym:symtab", current_symtab);
+ Setattr(n, "sym:name", symname);
+ Setattr(n, "sym:previousSibling", pcl);
+ return n;
+ }
+
+ /* Special case: class template instantiated with same name as the template's name eg: %template(X) X<int>; */
+ if (Equal(nodeType(c), "template")) {
+ String *nt1 = Getattr(c, "templatetype");
+ String *nt2 = nodeType(n);
+ if (Equal(nt1, "class") && Equal(nt1, nt2)) {
+ if (Getattr(n, "template")) {
+ /* Finally check that another %template with same name doesn't already exist */
+ if (!Getattr(c, "sym:nextSibling")) {
+ Setattr(c, "sym:nextSibling", n);
+ Setattr(n, "sym:symtab", current_symtab);
+ Setattr(n, "sym:name", symname);
+ Setattr(n, "sym:previousSibling", c);
+ return n;
+ }
+ }
+ }
+ }
+
+ if (Getattr(n, "allows_typedef"))
+ nt = 1;
+ if (Getattr(c, "allows_typedef"))
+ ct = 1;
+ if (nt || ct) {
+ Node *td, *other;
+ String *s;
+ /* At least one of the nodes allows typedef overloading. Make sure that
+ both don't--this would be a conflict */
+
+ if (nt && ct)
+ return c;
+
+ /* Figure out which node allows the typedef */
+ if (nt) {
+ td = n;
+ other = c;
+ } else {
+ td = c;
+ other = n;
+ }
+ /* Make sure the other node is a typedef */
+ s = Getattr(other, "storage");
+ if (!s || (!Equal(s, "typedef")))
+ return c; /* No. This is a conflict */
+
+ /* Hmmm. This appears to be okay. Make sure the symbol table refers to the allow_type node */
+
+ if (td != c) {
+ Setattr(current, symname, td);
+ Setattr(td, "sym:symtab", current_symtab);
+ Setattr(td, "sym:name", symname);
+ }
+ return n;
+ }
+
+ decl = Getattr(c, "decl");
+ ndecl = Getattr(n, "decl");
+
+ {
+ String *nt1, *nt2;
+ nt1 = Getattr(n, "nodeType");
+ if (Equal(nt1, "template"))
+ nt1 = Getattr(n, "templatetype");
+ nt2 = Getattr(c, "nodeType");
+ if (Equal(nt2, "template"))
+ nt2 = Getattr(c, "templatetype");
+ if (Equal(nt1, "using"))
+ u1 = 1;
+ if (Equal(nt2, "using"))
+ u2 = 1;
+
+ if ((!Equal(nt1, nt2)) && !(u1 || u2))
+ return c;
+ }
+ if (!(u1 || u2)) {
+ if ((!SwigType_isfunction(decl)) || (!SwigType_isfunction(ndecl))) {
+ /* Symbol table conflict */
+ return c;
+ }
+ }
+
+ /* Hmmm. Declarator seems to indicate that this is a function */
+ /* Look at storage class to see if compatible */
+ cstorage = Getattr(c, "storage");
+ nstorage = Getattr(n, "storage");
+
+ /* If either one is declared as typedef, forget it. We're hosed */
+ if (Cmp(cstorage, "typedef") == 0) {
+ return c;
+ }
+ if (Cmp(nstorage, "typedef") == 0) {
+ return c;
+ }
+
+ /* Okay. Walk down the list of symbols and see if we get a declarator match */
+ {
+ String *nt = Getattr(n, "nodeType");
+ int n_template = Equal(nt, "template") && Checkattr(n, "templatetype", "cdecl");
+ int n_plain_cdecl = Equal(nt, "cdecl");
+ Node *cn = c;
+ pn = 0;
+ while (cn) {
+ decl = Getattr(cn, "decl");
+ if (!(u1 || u2)) {
+ if (Cmp(ndecl, decl) == 0) {
+ /* Declarator conflict */
+ /* Now check we don't have a non-templated function overloaded by a templated function with same params,
+ * eg void foo(); template<typename> void foo(); */
+ String *cnt = Getattr(cn, "nodeType");
+ int cn_template = Equal(cnt, "template") && Checkattr(cn, "templatetype", "cdecl");
+ int cn_plain_cdecl = Equal(cnt, "cdecl");
+ if (!((n_template && cn_plain_cdecl) || (cn_template && n_plain_cdecl))) {
+ /* found a conflict */
+ return cn;
+ }
+ }
+ }
+ cl = cn;
+ cn = Getattr(cn, "sym:nextSibling");
+ pn++;
+ }
+ }
+ /* Well, we made it this far. Guess we can drop the symbol in place */
+ Setattr(n, "sym:symtab", current_symtab);
+ Setattr(n, "sym:name", symname);
+ /* Printf(stdout,"%s %p\n", Getattr(n,"sym:overname"), current_symtab); */
+ assert(!Getattr(n, "sym:overname"));
+ overname = NewStringf("__SWIG_%d", pn);
+ Setattr(n, "sym:overname", overname);
+ /*Printf(stdout,"%s %s %s\n", symname, Getattr(n,"decl"), Getattr(n,"sym:overname")); */
+ Setattr(cl, "sym:nextSibling", n);
+ Setattr(n, "sym:previousSibling", cl);
+ Setattr(cl, "sym:overloaded", c);
+ Setattr(n, "sym:overloaded", c);
+ Delete(overname);
+ return n;
+ }
+
+ /* No conflict. Just add it */
+ Setattr(n, "sym:symtab", current_symtab);
+ Setattr(n, "sym:name", symname);
+ /* Printf(stdout,"%s\n", Getattr(n,"sym:overname")); */
+ overname = NewStringf("__SWIG_%d", pn);
+ Setattr(n, "sym:overname", overname);
+ Delete(overname);
+ /* Printf(stdout,"%s %s %s\n", symname, Getattr(n,"decl"), Getattr(n,"sym:overname")); */
+ Setattr(current, symname, n);
+ return n;
+}
+
+/* -----------------------------------------------------------------------------
+ * symbol_lookup()
+ *
+ * Internal function to handle fully qualified symbol table lookups. This
+ * works from the symbol table supplied in symtab and unwinds its way out
+ * towards the global scope.
+ *
+ * This function operates in the C namespace, not the target namespace.
+ *
+ * The check function is an optional callback that can be used to verify a particular
+ * symbol match. This is only used in some of the more exotic parts of SWIG. For instance,
+ * verifying that a class hierarchy implements all pure virtual methods.
+ * ----------------------------------------------------------------------------- */
+
+static Node *_symbol_lookup(const String *name, Symtab *symtab, int (*check) (Node *n)) {
+ Node *n;
+ List *inherit;
+ Hash *sym = Getattr(symtab, "csymtab");
+ if (Getmark(symtab))
+ return 0;
+ Setmark(symtab, 1);
+
+ n = Getattr(sym, name);
+
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_look %s %p %p %s\n", name, n, symtab, Getattr(symtab, "name"));
+#endif
+
+ if (n) {
+ /* if a check-function is defined. Call it to determine a match */
+ if (check) {
+ int c = check(n);
+ if (c == 1) {
+ Setmark(symtab, 0);
+ return n;
+ }
+ if (c < 0) {
+ /* Terminate the search right away */
+ Setmark(symtab, 0);
+ return 0;
+ }
+ } else {
+ Setmark(symtab, 0);
+ return n;
+ }
+ }
+
+ if (!n && SwigType_istemplate(name)) {
+ String *dname = 0;
+ Setmark(symtab, 0);
+ dname = Swig_symbol_template_deftype(name, symtab);
+ if (!Equal(dname, name)) {
+ n = _symbol_lookup(dname, symtab, check);
+ }
+ Delete(dname);
+ if (n)
+ return n;
+ Setmark(symtab, 1);
+ }
+
+ inherit = Getattr(symtab, "inherit");
+ if (inherit && use_inherit) {
+ int i, len;
+ len = Len(inherit);
+ for (i = 0; i < len; i++) {
+ n = _symbol_lookup(name, Getitem(inherit, i), check);
+ if (n) {
+ Setmark(symtab, 0);
+ return n;
+ }
+ }
+ }
+
+ Setmark(symtab, 0);
+ return 0;
+}
+
+static Node *symbol_lookup(const_String_or_char_ptr name, Symtab *symtab, int (*check) (Node *n)) {
+ Node *n = 0;
+ if (DohCheck(name)) {
+ n = _symbol_lookup(name, symtab, check);
+ } else {
+ String *sname = NewString(name);
+ n = _symbol_lookup(sname, symtab, check);
+ Delete(sname);
+ }
+ return n;
+}
+
+
+
+/* -----------------------------------------------------------------------------
+ * symbol_lookup_qualified()
+ * ----------------------------------------------------------------------------- */
+
+static Node *symbol_lookup_qualified(const_String_or_char_ptr name, Symtab *symtab, const String *prefix, int local, int (*checkfunc) (Node *n)) {
+ /* This is a little funky, we search by fully qualified names */
+
+ if (!symtab)
+ return 0;
+ if (!prefix) {
+ Node *n;
+ String *bname = 0;
+ String *prefix = 0;
+ Swig_scopename_split(name, &prefix, &bname);
+ n = symbol_lookup_qualified(bname, symtab, prefix, local, checkfunc);
+ Delete(bname);
+ Delete(prefix);
+ return n;
+ } else {
+ Symtab *st;
+ Node *n = 0;
+ /* Make qualified name of current scope */
+ String *qalloc = 0;
+ String *qname = Swig_symbol_qualifiedscopename(symtab);
+ const String *cqname;
+ if (qname) {
+ if (Len(qname)) {
+ if (prefix && Len(prefix)) {
+ Printv(qname, "::", prefix, NIL);
+ }
+ } else {
+ Append(qname, prefix);
+ }
+ qalloc = qname;
+ cqname = qname;
+ } else {
+ cqname = prefix;
+ }
+ st = Getattr(symtabs, cqname);
+ /* Found a scope match */
+ if (st) {
+ if (!name) {
+ if (qalloc)
+ Delete(qalloc);
+ return st;
+ }
+ n = symbol_lookup(name, st, checkfunc);
+ }
+ if (qalloc)
+ Delete(qalloc);
+
+ if (!n) {
+ if (!local) {
+ Node *pn = Getattr(symtab, "parentNode");
+ if (pn)
+ n = symbol_lookup_qualified(name, pn, prefix, local, checkfunc);
+
+ /* Check inherited scopes */
+ if (!n) {
+ List *inherit = Getattr(symtab, "inherit");
+ if (inherit && use_inherit) {
+ int i, len;
+ len = Len(inherit);
+ for (i = 0; i < len; i++) {
+ Node *prefix_node = symbol_lookup(prefix, Getitem(inherit, i), checkfunc);
+ if (prefix_node) {
+ Node *prefix_symtab = Getattr(prefix_node, "symtab");
+ if (prefix_symtab) {
+ n = symbol_lookup(name, prefix_symtab, checkfunc);
+ break;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ n = 0;
+ }
+ }
+ return n;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_clookup()
+ *
+ * Look up a symbol in the symbol table. This uses the C name, not scripting
+ * names. Note: If we come across a using declaration, we follow it to
+ * to get the real node. Any using directives are also followed (but this is
+ * implemented in symbol_lookup()).
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_symbol_clookup(const_String_or_char_ptr name, Symtab *n) {
+ Hash *hsym = 0;
+ Node *s = 0;
+
+ if (!n) {
+ hsym = current_symtab;
+ } else {
+ if (!Checkattr(n, "nodeType", "symboltable")) {
+ n = Getattr(n, "sym:symtab");
+ }
+ assert(n);
+ if (n) {
+ hsym = n;
+ }
+ }
+
+ if (Swig_scopename_check(name)) {
+ char *cname = Char(name);
+ if (strncmp(cname, "::", 2) == 0) {
+ String *nname = NewString(cname + 2);
+ if (Swig_scopename_check(nname)) {
+ s = symbol_lookup_qualified(nname, global_scope, 0, 0, 0);
+ } else {
+ s = symbol_lookup(nname, global_scope, 0);
+ }
+ Delete(nname);
+ } else {
+ String *prefix = Swig_scopename_prefix(name);
+ if (prefix) {
+ s = symbol_lookup_qualified(name, hsym, 0, 0, 0);
+ Delete(prefix);
+ if (!s) {
+ return 0;
+ }
+ }
+ }
+ }
+ if (!s) {
+ while (hsym) {
+ s = symbol_lookup(name, hsym, 0);
+ if (s)
+ break;
+ hsym = Getattr(hsym, "parentNode");
+ if (!hsym)
+ break;
+ }
+ }
+
+ if (!s) {
+ return 0;
+ }
+ /* Check if s is a 'using' node */
+ while (s && Checkattr(s, "nodeType", "using")) {
+ String *uname = Getattr(s, "uname");
+ Symtab *un = Getattr(s, "sym:symtab");
+ Node *ss = (!Equal(name, uname) || (un != n)) ? Swig_symbol_clookup(uname, un) : 0; /* avoid infinity loop */
+ if (!ss) {
+ SWIG_WARN_NODE_BEGIN(s);
+ Swig_warning(WARN_PARSE_USING_UNDEF, Getfile(s), Getline(s), "Nothing known about '%s'.\n", SwigType_namestr(Getattr(s, "uname")));
+ SWIG_WARN_NODE_END(s);
+ }
+ s = ss;
+ }
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_clookup_check()
+ *
+ * This function is identical to Swig_symbol_clookup() except that it
+ * accepts a callback function that is invoked to determine a symbol match.
+ * The purpose of this function is to support complicated algorithms that need
+ * to examine multiple definitions of the same symbol that might appear in an
+ * inheritance hierarchy.
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_symbol_clookup_check(const_String_or_char_ptr name, Symtab *n, int (*checkfunc) (Node *n)) {
+ Hash *hsym = 0;
+ Node *s = 0;
+
+ if (!n) {
+ hsym = current_symtab;
+ } else {
+ if (!Checkattr(n, "nodeType", "symboltable")) {
+ n = Getattr(n, "sym:symtab");
+ }
+ assert(n);
+ if (n) {
+ hsym = n;
+ }
+ }
+
+ if (Swig_scopename_check(name)) {
+ char *cname = Char(name);
+ if (strncmp(cname, "::", 2) == 0) {
+ String *nname = NewString(cname + 2);
+ if (Swig_scopename_check(nname)) {
+ s = symbol_lookup_qualified(nname, global_scope, 0, 0, checkfunc);
+ } else {
+ s = symbol_lookup(nname, global_scope, checkfunc);
+ }
+ Delete(nname);
+ } else {
+ String *prefix = Swig_scopename_prefix(name);
+ if (prefix) {
+ s = symbol_lookup_qualified(name, hsym, 0, 0, checkfunc);
+ Delete(prefix);
+ if (!s) {
+ return 0;
+ }
+ }
+ }
+ }
+ if (!s) {
+ while (hsym) {
+ s = symbol_lookup(name, hsym, checkfunc);
+ if (s)
+ break;
+ hsym = Getattr(hsym, "parentNode");
+ if (!hsym)
+ break;
+ }
+ }
+ if (!s) {
+ return 0;
+ }
+ /* Check if s is a 'using' node */
+ while (s && Checkattr(s, "nodeType", "using")) {
+ Node *ss;
+ ss = Swig_symbol_clookup(Getattr(s, "uname"), Getattr(s, "sym:symtab"));
+ if (!ss && !checkfunc) {
+ SWIG_WARN_NODE_BEGIN(s);
+ Swig_warning(WARN_PARSE_USING_UNDEF, Getfile(s), Getline(s), "Nothing known about '%s'.\n", SwigType_namestr(Getattr(s, "uname")));
+ SWIG_WARN_NODE_END(s);
+ }
+ s = ss;
+ }
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_clookup_local()
+ *
+ * Same as Swig_symbol_clookup but parent nodes are not searched, that is, just
+ * this symbol table is searched.
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_symbol_clookup_local(const_String_or_char_ptr name, Symtab *n) {
+ Hash *hsym;
+ Node *s = 0;
+
+ if (!n) {
+ hsym = current_symtab;
+ } else {
+ if (!Checkattr(n, "nodeType", "symboltable")) {
+ n = Getattr(n, "sym:symtab");
+ }
+ assert(n);
+ hsym = n;
+ }
+
+ if (Swig_scopename_check(name)) {
+ char *cname = Char(name);
+ if (strncmp(cname, "::", 2) == 0) {
+ String *nname = NewString(cname + 2);
+ if (Swig_scopename_check(nname)) {
+ s = symbol_lookup_qualified(nname, global_scope, 0, 0, 0);
+ } else {
+ s = symbol_lookup(nname, global_scope, 0);
+ }
+ Delete(nname);
+ } else {
+ s = symbol_lookup_qualified(name, hsym, 0, 0, 0);
+ }
+ }
+ if (!s) {
+ s = symbol_lookup(name, hsym, 0);
+ }
+ if (!s)
+ return 0;
+ /* Check if s is a 'using' node */
+ while (s && Checkattr(s, "nodeType", "using")) {
+ Node *ss = Swig_symbol_clookup_local(Getattr(s, "uname"), Getattr(s, "sym:symtab"));
+ if (!ss) {
+ SWIG_WARN_NODE_BEGIN(s);
+ Swig_warning(WARN_PARSE_USING_UNDEF, Getfile(s), Getline(s), "Nothing known about '%s'.\n", SwigType_namestr(Getattr(s, "uname")));
+ SWIG_WARN_NODE_END(s);
+ }
+ s = ss;
+ }
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_clookup_local_check()
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_symbol_clookup_local_check(const_String_or_char_ptr name, Symtab *n, int (*checkfunc) (Node *)) {
+ Hash *hsym;
+ Node *s = 0;
+
+ if (!n) {
+ hsym = current_symtab;
+ } else {
+ if (!Checkattr(n, "nodeType", "symboltable")) {
+ n = Getattr(n, "sym:symtab");
+ }
+ assert(n);
+ hsym = n;
+ }
+
+ if (Swig_scopename_check(name)) {
+ char *cname = Char(name);
+ if (strncmp(cname, "::", 2) == 0) {
+ String *nname = NewString(cname + 2);
+ if (Swig_scopename_check(nname)) {
+ s = symbol_lookup_qualified(nname, global_scope, 0, 0, checkfunc);
+ } else {
+ s = symbol_lookup(nname, global_scope, checkfunc);
+ }
+ Delete(nname);
+ } else {
+ s = symbol_lookup_qualified(name, hsym, 0, 0, checkfunc);
+ }
+ }
+ if (!s) {
+ s = symbol_lookup(name, hsym, checkfunc);
+ }
+ if (!s)
+ return 0;
+ /* Check if s is a 'using' node */
+ while (s && Checkattr(s, "nodeType", "using")) {
+ Node *ss = Swig_symbol_clookup_local_check(Getattr(s, "uname"), Getattr(s, "sym:symtab"), checkfunc);
+ if (!ss && !checkfunc) {
+ SWIG_WARN_NODE_BEGIN(s);
+ Swig_warning(WARN_PARSE_USING_UNDEF, Getfile(s), Getline(s), "Nothing known about '%s'.\n", SwigType_namestr(Getattr(s, "uname")));
+ SWIG_WARN_NODE_END(s);
+ }
+ s = ss;
+ }
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_clookup_no_inherit()
+ *
+ * Symbol lookup like Swig_symbol_clookup but does not follow using declarations.
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_symbol_clookup_no_inherit(const_String_or_char_ptr name, Symtab *n) {
+ Node *s = 0;
+ assert(use_inherit==1);
+ use_inherit = 0;
+ s = Swig_symbol_clookup(name, n);
+ use_inherit = 1;
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_cscope()
+ *
+ * Look up a scope name.
+ * ----------------------------------------------------------------------------- */
+
+Symtab *Swig_symbol_cscope(const_String_or_char_ptr name, Symtab *symtab) {
+ char *cname = Char(name);
+ if (strncmp(cname, "::", 2) == 0)
+ return symbol_lookup_qualified(0, global_scope, name, 0, 0);
+ return symbol_lookup_qualified(0, symtab, name, 0, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_remove()
+ *
+ * Remove a symbol. If the symbol is an overloaded function and the symbol removed
+ * is not the last in the list of overloaded functions, then the overloaded
+ * names (sym:overname attribute) are changed to start from zero, eg __SWIG_0.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_symbol_remove(Node *n) {
+ Symtab *symtab;
+ String *symname;
+ String *overname;
+ Node *symprev;
+ Node *symnext;
+ Node *fixovername = 0;
+ symtab = Getattr(n, "sym:symtab"); /* Get symbol table object */
+ symtab = Getattr(symtab, "symtab"); /* Get actual hash table of symbols */
+ symname = Getattr(n, "sym:name");
+ symprev = Getattr(n, "sym:previousSibling");
+ symnext = Getattr(n, "sym:nextSibling");
+
+ /* If previous symbol, just fix the links */
+ if (symprev) {
+ if (symnext) {
+ Setattr(symprev, "sym:nextSibling", symnext);
+ fixovername = symprev; /* fix as symbol to remove is somewhere in the middle of the linked list */
+ } else {
+ Delattr(symprev, "sym:nextSibling");
+ }
+ } else {
+ /* If no previous symbol, see if there is a next symbol */
+ if (symnext) {
+ Setattr(symtab, symname, symnext);
+ fixovername = symnext; /* fix as symbol to remove is at head of linked list */
+ } else {
+ if (symname)
+ Delattr(symtab, symname);
+ }
+ }
+ if (symnext) {
+ if (symprev) {
+ Setattr(symnext, "sym:previousSibling", symprev);
+ } else {
+ Delattr(symnext, "sym:previousSibling");
+ }
+ }
+ Delattr(n, "sym:symtab");
+ Delattr(n, "sym:previousSibling");
+ Delattr(n, "sym:nextSibling");
+ Delattr(n, "csym:nextSibling");
+ Delattr(n, "sym:overname");
+ Delattr(n, "csym:previousSibling");
+ Delattr(n, "sym:overloaded");
+ n = 0;
+
+ if (fixovername) {
+ Node *nn = fixovername;
+ Node *head = fixovername;
+ int pn = 0;
+
+ /* find head of linked list */
+ while (nn) {
+ head = nn;
+ nn = Getattr(nn, "sym:previousSibling");
+ }
+
+ /* adjust all the sym:overname strings to start from 0 and increment by one */
+ nn = head;
+ while (nn) {
+ assert(Getattr(nn, "sym:overname"));
+ Delattr(nn, "sym:overname");
+ overname = NewStringf("__SWIG_%d", pn);
+ Setattr(nn, "sym:overname", overname);
+ Delete(overname);
+ pn++;
+ nn = Getattr(nn, "sym:nextSibling");
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_qualified()
+ *
+ * Return the qualified name of a symbol
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_symbol_qualified(Node *n) {
+ Hash *symtab;
+ if (Checkattr(n, "nodeType", "symboltable")) {
+ symtab = n;
+ } else {
+ symtab = Getattr(n, "sym:symtab");
+ }
+ if (!symtab)
+ return NewStringEmpty();
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_qscope %s %p %s\n", Getattr(n, "name"), symtab, Getattr(symtab, "name"));
+#endif
+ return Swig_symbol_qualifiedscopename(symtab);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_isoverloaded()
+ *
+ * Check if a symbol is overloaded. Returns the first symbol if so.
+ * ----------------------------------------------------------------------------- */
+
+Node *Swig_symbol_isoverloaded(Node *n) {
+ return Getattr(n, "sym:overloaded");
+}
+
+/* -----------------------------------------------------------------------------
+ * symbol_template_qualify()
+ *
+ * Internal function to create a fully qualified type name for templates
+ * ----------------------------------------------------------------------------- */
+
+/* This cache produces problems with OSS, don't active it */
+/* #define SWIG_TEMPLATE_QUALIFY_CACHE */
+static SwigType *symbol_template_qualify(const SwigType *e, Symtab *st) {
+ String *tprefix, *tsuffix;
+ SwigType *qprefix;
+ String *targs;
+ List *targslist;
+ Node *tempn;
+ Symtab *tscope;
+ Iterator ti;
+#ifdef SWIG_TEMPLATE_QUALIFY_CACHE
+ static Hash *qualify_cache = 0;
+ String *scopetype = st ? NewStringf("%s::%s", Getattr(st, "name"), e)
+ : NewStringf("%s::%s", Swig_symbol_getscopename(), e);
+ if (!qualify_cache) {
+ qualify_cache = NewHash();
+ }
+ if (scopetype) {
+ String *cres = Getattr(qualify_cache, scopetype);
+ if (cres) {
+ Delete(scopetype);
+ return Copy(cres);
+ }
+ }
+#endif
+
+ tprefix = SwigType_templateprefix(e);
+ tsuffix = SwigType_templatesuffix(e);
+ qprefix = Swig_symbol_type_qualify(tprefix, st);
+ targs = SwigType_templateargs(e);
+ targslist = SwigType_parmlist(targs);
+ tempn = Swig_symbol_clookup_local(tprefix, st);
+ tscope = tempn ? Getattr(tempn, "sym:symtab") : 0;
+ Append(qprefix, "<(");
+ for (ti = First(targslist); ti.item;) {
+ String *vparm;
+ /* TODO: the logic here should be synchronised with that in SwigType_typedef_qualified() */
+ /* TODO: ti.item might be a non-type parameter possibly within (), eg: (std::is_integral_v<(A)>||std::is_same_v<(A,node_t)>) */
+ String *qparm = Swig_symbol_type_qualify(ti.item, st);
+ if (tscope && (tscope != st)) {
+ String *ty = Swig_symbol_type_qualify(qparm, tscope);
+ Delete(qparm);
+ qparm = ty;
+ }
+
+ vparm = Swig_symbol_template_param_eval(qparm, st);
+ Append(qprefix, vparm);
+ ti = Next(ti);
+ if (ti.item) {
+ Putc(',', qprefix);
+ }
+ Delete(qparm);
+ Delete(vparm);
+ }
+ Append(qprefix, ")>");
+ Append(qprefix, tsuffix);
+ Delete(tprefix);
+ Delete(tsuffix);
+ Delete(targs);
+ Delete(targslist);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_temp_qual %s %s\n", e, qprefix);
+#endif
+#ifdef SWIG_TEMPLATE_QUALIFY_CACHE
+ Setattr(qualify_cache, scopetype, qprefix);
+ Delete(scopetype);
+#endif
+
+ return qprefix;
+}
+
+
+static int symbol_no_constructor(Node *n) {
+ return !Checkattr(n, "nodeType", "constructor");
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_type_qualify()
+ *
+ * Create a fully qualified type name
+ * Note: Does not resolve a constructor if passed in as the 'type'.
+ * ----------------------------------------------------------------------------- */
+
+SwigType *Swig_symbol_type_qualify(const SwigType *t, Symtab *st) {
+ List *elements;
+ String *result = NewStringEmpty();
+ int i, len;
+ char *c = Char(t);
+ if (strncmp(c, "::", 2) == 0) {
+ Append(result, t);
+ return result;
+ }
+
+ elements = SwigType_split(t);
+
+ len = Len(elements);
+ for (i = 0; i < len; i++) {
+ String *e = Getitem(elements, i);
+ if (SwigType_issimple(e)) {
+ /* Note: the unary scope operator (::) is being removed from the template parameters here. */
+ Node *n = Swig_symbol_clookup_check(e, st, symbol_no_constructor);
+ if (n) {
+ String *name = Getattr(n, "name");
+ Clear(e);
+ Append(e, name);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_qual_ei %d %s %s %p\n", i, name, e, st);
+#endif
+ if (!Swig_scopename_check(name)) {
+ String *qname = Swig_symbol_qualified(n);
+ if (qname && Len(qname)) {
+ Insert(e, 0, "::");
+ Insert(e, 0, qname);
+ }
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_qual_sc %d %s %s %p\n", i, qname, e, st);
+#endif
+ Delete(qname);
+ }
+ } else if (SwigType_istemplate(e)) {
+ SwigType *ty = symbol_template_qualify(e, st);
+ Clear(e);
+ Append(e, ty);
+ Delete(ty);
+ }
+ if (strncmp(Char(e), "::", 2) == 0) {
+ Delitem(e, 0);
+ Delitem(e, 0);
+ }
+ Append(result, e);
+ } else if (SwigType_isfunction(e)) {
+ List *parms = SwigType_parmlist(e);
+ String *s = NewString("f(");
+ Iterator pi = First(parms);
+ while (pi.item) {
+ String *pf = Swig_symbol_type_qualify(pi.item, st);
+ Append(s, pf);
+ pi = Next(pi);
+ if (pi.item) {
+ Append(s, ",");
+ }
+ Delete(pf);
+ }
+ Append(s, ").");
+ Append(result, s);
+ Delete(parms);
+ Delete(s);
+ } else {
+ Append(result, e);
+ }
+ }
+ Delete(elements);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_qualify %s %s %p %s\n", t, result, st, st ? Getattr(st, "name") : 0);
+#endif
+
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_template_reduce()
+ * Resolves template parameter types
+ * For example:
+ * typedef int Int;
+ * typedef Int Integer;
+ * with input:
+ * Foo<(Int,Integer)>
+ * returns:
+ * Foo<(int,int)>
+ * ----------------------------------------------------------------------------- */
+
+static
+SwigType *Swig_symbol_template_reduce(SwigType *qt, Symtab *ntab) {
+ Parm *p;
+ String *templateargs = SwigType_templateargs(qt);
+ List *parms = SwigType_parmlist(templateargs);
+ Iterator pi = First(parms);
+ String *tprefix = SwigType_templateprefix(qt);
+ String *tsuffix = SwigType_templatesuffix(qt);
+ String *qprefix = SwigType_typedef_qualified(tprefix);
+ Append(qprefix, "<(");
+ while ((p = pi.item)) {
+ String *np;
+ String *tp = Swig_symbol_typedef_reduce(p, ntab);
+ String *qp = Swig_symbol_type_qualify(tp, ntab);
+ Node *n = Swig_symbol_clookup(qp, ntab);
+ if (n) {
+ String *qual = Swig_symbol_qualified(n);
+ np = Copy(Getattr(n, "name"));
+ Delete(tp);
+ tp = np;
+ if (qual && Len(qual)) {
+ Insert(np, 0, "::");
+ Insert(np, 0, qual);
+ }
+ Delete(qual);
+ } else {
+ np = qp;
+ }
+ Append(qprefix, np);
+ pi = Next(pi);
+ if (pi.item) {
+ Append(qprefix, ",");
+ }
+ Delete(qp);
+ Delete(tp);
+ }
+ Append(qprefix, ")>");
+ Append(qprefix, tsuffix);
+ Delete(parms);
+ Delete(tprefix);
+ Delete(tsuffix);
+ Delete(templateargs);
+ return qprefix;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_typedef_reduce()
+ *
+ * Chase a typedef through symbol tables looking for a match.
+ * ----------------------------------------------------------------------------- */
+
+SwigType *Swig_symbol_typedef_reduce(const SwigType *ty, Symtab *tab) {
+ SwigType *prefix, *base;
+ Node *n;
+ String *nt;
+
+ base = SwigType_base(ty);
+ prefix = SwigType_prefix(ty);
+
+ n = Swig_symbol_clookup(base, tab);
+ if (!n) {
+ if (SwigType_istemplate(ty)) {
+ SwigType *qt = Swig_symbol_template_reduce(base, tab);
+ Append(prefix, qt);
+ Delete(qt);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_reduce (a) %s %s\n", ty, prefix);
+#endif
+ Delete(base);
+ return prefix;
+ } else {
+ Delete(prefix);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_reduce (b) %s %s\n", ty, ty);
+#endif
+ return Copy(ty);
+ }
+ }
+ nt = Getattr(n, "nodeType");
+ if (Equal(nt, "using")) {
+ String *uname = Getattr(n, "uname");
+ if (uname) {
+ n = Swig_symbol_clookup(base, Getattr(n, "sym:symtab"));
+ if (!n) {
+ Delete(base);
+ Delete(prefix);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_reduce (c) %s %s\n", ty, ty);
+#endif
+ return Copy(ty);
+ }
+ }
+ }
+ if (Equal(nt, "cdecl")) {
+ String *storage = Getattr(n, "storage");
+ if (storage && (Equal(storage, "typedef"))) {
+ SwigType *decl;
+ SwigType *rt;
+ SwigType *qt;
+ Symtab *ntab;
+ SwigType *nt = Copy(Getattr(n, "type"));
+
+ /* Fix for case 'typedef struct Hello hello;' */
+ {
+ const char *dclass[3] = { "struct ", "union ", "class " };
+ int i;
+ char *c = Char(nt);
+ for (i = 0; i < 3; i++) {
+ if (strstr(c, dclass[i]) == c) {
+ Replace(nt, dclass[i], "", DOH_REPLACE_FIRST);
+ }
+ }
+ }
+ decl = Getattr(n, "decl");
+ if (decl) {
+ SwigType_push(nt, decl);
+ }
+ SwigType_push(nt, prefix);
+ Delete(base);
+ Delete(prefix);
+ ntab = Getattr(n, "sym:symtab");
+ rt = Swig_symbol_typedef_reduce(nt, ntab);
+ qt = Swig_symbol_type_qualify(rt, ntab);
+ if (SwigType_istemplate(qt)) {
+ SwigType *qtr = Swig_symbol_template_reduce(qt, ntab);
+ Delete(qt);
+ qt = qtr;
+ }
+ Delete(nt);
+ Delete(rt);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_reduce (d) %s %s\n", qt, ty);
+#endif
+ return qt;
+ }
+ }
+ Delete(base);
+ Delete(prefix);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "symbol_reduce (e) %s %s\n", ty, ty);
+#endif
+ return Copy(ty);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_string_qualify()
+ *
+ * This function takes a string and looks for identifiers. Identifiers are
+ * then qualified according to scope rules. This function is used in a number
+ * of settings including expression evaluation, scoping of conversion operators,
+ * and so forth.
+ * ----------------------------------------------------------------------------- */
+
+String *Swig_symbol_string_qualify(String *s, Symtab *st) {
+ int have_id = 0;
+ String *id = NewStringEmpty();
+ String *r = NewStringEmpty();
+ char *c = Char(s);
+ int first_char = 1;
+ while (*c) {
+ if (isalpha((int) *c) || (*c == '_') || (*c == ':') || (*c == '~' && first_char) || (isdigit((int) *c) && !first_char)) {
+ Putc(*c, id);
+ have_id = 1;
+ } else {
+ if (have_id) {
+ String *qid = Swig_symbol_type_qualify(id, st);
+ Append(r, qid);
+ Clear(id);
+ Delete(qid);
+ have_id = 0;
+ }
+ Putc(*c, r);
+ }
+ first_char = (*c == ':');
+ c++;
+ }
+ if (have_id) {
+ String *qid = Swig_symbol_type_qualify(id, st);
+ Append(r, qid);
+ Delete(qid);
+ }
+ Delete(id);
+ return r;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_template_defargs()
+ *
+ * Apply default arg from generic template default args
+ * Returns a parameter list which contains missing default arguments (if any)
+ * Note side effects: parms will also contain the extra parameters in its list
+ * (but only if non-zero).
+ * ----------------------------------------------------------------------------- */
+
+
+ParmList *Swig_symbol_template_defargs(Parm *parms, Parm *targs, Symtab *tscope, Symtab *tsdecl) {
+ ParmList *expandedparms = parms;
+ if (Len(parms) < Len(targs)) {
+ Parm *lp = parms;
+ Parm *p = lp;
+ Parm *tp = targs;
+ while (p && tp) {
+ p = nextSibling(p);
+ tp = nextSibling(tp);
+ if (p)
+ lp = p;
+ }
+ while (tp) {
+ String *value = Getattr(tp, "value");
+ if (value) {
+ Parm *cp;
+ Parm *ta = targs;
+ Parm *p = parms;
+ SwigType *nt = Swig_symbol_string_qualify(value, tsdecl);
+ SwigType *ntq = 0;
+#ifdef SWIG_DEBUG
+ Printf(stderr, "value %s %s %s\n", value, nt, tsdecl ? Getattr(tsdecl, "name") : tsdecl);
+#endif
+ while (p && ta) {
+ String *name = Getattr(ta, "name");
+ String *pvalue = Getattr(p, "value");
+ String *value = pvalue ? pvalue : Getattr(p, "type");
+ String *ttq = Swig_symbol_type_qualify(value, tscope);
+ /* value = SwigType_typedef_resolve_all(value); */
+ Replaceid(nt, name, ttq);
+ p = nextSibling(p);
+ ta = nextSibling(ta);
+ Delete(ttq);
+ }
+ ntq = Swig_symbol_type_qualify(nt, tsdecl);
+ if (SwigType_istemplate(ntq)) {
+ String *ty = Swig_symbol_template_deftype(ntq, tscope);
+ Delete(ntq);
+ ntq = ty;
+ }
+ cp = NewParmWithoutFileLineInfo(ntq, 0);
+ if (lp) {
+ set_nextSibling(lp, cp);
+ Delete(cp);
+ } else {
+ expandedparms = cp;
+ }
+ lp = cp;
+ tp = nextSibling(tp);
+ Delete(nt);
+ Delete(ntq);
+ } else {
+ tp = 0;
+ }
+ }
+ }
+ return expandedparms;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_symbol_template_deftype()
+ *
+ * Apply default args to generic template type
+ * ----------------------------------------------------------------------------- */
+
+#define SWIG_TEMPLATE_DEFTYPE_CACHE
+SwigType *Swig_symbol_template_deftype(const SwigType *type, Symtab *tscope) {
+ String *result = NewStringEmpty();
+ List *elements = SwigType_split(type);
+ int len = Len(elements);
+ int i;
+#ifdef SWIG_TEMPLATE_DEFTYPE_CACHE
+ static Hash *s_cache = 0;
+ Hash *scope_cache;
+ /* The lookup depends on the current scope and potential namespace qualification.
+ Looking up x in namespace y is not the same as looking up x::y in outer scope.
+ -> we use a 2-level hash: first scope and then symbol. */
+ String *scope_name = tscope
+ ? Swig_symbol_qualifiedscopename(tscope)
+ : Swig_symbol_qualifiedscopename(current_symtab);
+ String *type_name = tscope
+ ? NewStringf("%s::%s", Getattr(tscope, "name"), type)
+ : NewStringf("%s::%s", Swig_symbol_getscopename(), type);
+ if (!scope_name) scope_name = NewString("::");
+ if (!s_cache) {
+ s_cache = NewHash();
+ }
+ scope_cache = Getattr(s_cache, scope_name);
+ if (scope_cache) {
+ String *cres = Getattr(scope_cache, type_name);
+ if (cres) {
+ Append(result, cres);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "cached deftype %s(%s) -> %s\n", type, scope_name, result);
+#endif
+ Delete(type_name);
+ Delete(scope_name);
+ return result;
+ }
+ } else {
+ scope_cache = NewHash();
+ Setattr(s_cache, scope_name, scope_cache);
+ Delete(scope_name);
+ }
+#endif
+
+#ifdef SWIG_DEBUG
+ Printf(stderr, "finding deftype %s\n", type);
+#endif
+
+ for (i = 0; i < len; i++) {
+ String *e = Getitem(elements, i);
+ if (SwigType_isfunction(e)) {
+ String *s = NewString("f(");
+ List *parms = SwigType_parmlist(e);
+ Iterator pi = First(parms);
+ while (pi.item) {
+ String *pf = SwigType_istemplate(e) ? Swig_symbol_template_deftype(pi.item, tscope)
+ : Swig_symbol_type_qualify(pi.item, tscope);
+ Append(s, pf);
+ pi = Next(pi);
+ if (pi.item) {
+ Append(s, ",");
+ }
+ Delete(pf);
+ }
+ Append(s, ").");
+ Append(result, s);
+ Delete(s);
+ Delete(parms);
+ } else if (SwigType_istemplate(e)) {
+ String *prefix = SwigType_prefix(e);
+ String *base = SwigType_base(e);
+ String *tprefix = SwigType_templateprefix(base);
+ String *targs = SwigType_templateargs(base);
+ String *tsuffix = SwigType_templatesuffix(base);
+ ParmList *tparms = SwigType_function_parms(targs, 0);
+ Node *tempn = Swig_symbol_clookup_local(tprefix, tscope);
+ if (!tempn && tsuffix && Len(tsuffix)) {
+ tempn = Swig_symbol_clookup(tprefix, 0);
+ }
+#ifdef SWIG_DEBUG
+ Printf(stderr, "deftype type %s %s %d\n", e, tprefix, (long) tempn);
+#endif
+ if (tempn) {
+ ParmList *tnargs = Getattr(tempn, "templateparms");
+ ParmList *expandedparms;
+ Parm *p;
+ Symtab *tsdecl = Getattr(tempn, "sym:symtab");
+
+#ifdef SWIG_DEBUG
+ Printf(stderr, "deftype type %s %s %s\n", tprefix, targs, tsuffix);
+#endif
+ Append(tprefix, "<(");
+ expandedparms = Swig_symbol_template_defargs(tparms, tnargs, tscope, tsdecl);
+ p = expandedparms;
+ tscope = tsdecl;
+ while (p) {
+ SwigType *ptype = Getattr(p, "type");
+ SwigType *ttr = ptype ? ptype : Getattr(p, "value");
+ SwigType *ttf = Swig_symbol_type_qualify(ttr, tscope);
+ SwigType *ttq = Swig_symbol_template_param_eval(ttf, tscope);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "arg type %s\n", ttq);
+#endif
+ if (SwigType_istemplate(ttq)) {
+ SwigType *ttd = Swig_symbol_template_deftype(ttq, tscope);
+ Delete(ttq);
+ ttq = ttd;
+#ifdef SWIG_DEBUG
+ Printf(stderr, "arg deftype %s\n", ttq);
+#endif
+ }
+ Append(tprefix, ttq);
+ p = nextSibling(p);
+ if (p)
+ Putc(',', tprefix);
+ Delete(ttf);
+ Delete(ttq);
+ }
+ Append(tprefix, ")>");
+ Append(tprefix, tsuffix);
+ Append(prefix, tprefix);
+#ifdef SWIG_DEBUG
+ Printf(stderr, "deftype %s %s \n", type, tprefix);
+#endif
+ Append(result, prefix);
+ } else {
+ Append(result, e);
+ }
+ Delete(prefix);
+ Delete(base);
+ Delete(tprefix);
+ Delete(tsuffix);
+ Delete(targs);
+ Delete(tparms);
+ } else {
+ Append(result, e);
+ }
+ }
+ Delete(elements);
+#ifdef SWIG_TEMPLATE_DEFTYPE_CACHE
+ Setattr(scope_cache, type_name, result);
+ Delete(type_name);
+#endif
+
+ return result;
+}
+
+SwigType *Swig_symbol_template_param_eval(const SwigType *p, Symtab *symtab) {
+ String *value = Copy(p);
+ Node *lastnode = 0;
+ while (1) {
+ Node *n = Swig_symbol_clookup(value, symtab);
+ if (n == lastnode)
+ break;
+ lastnode = n;
+ if (n) {
+ String *nt = Getattr(n, "nodeType");
+ if (Equal(nt, "enumitem")) {
+ /* An enum item. Generate a fully qualified name */
+ String *qn = Swig_symbol_qualified(n);
+ if (qn && Len(qn)) {
+ Append(qn, "::");
+ Append(qn, Getattr(n, "name"));
+ Delete(value);
+ value = qn;
+ continue;
+ } else {
+ Delete(qn);
+ break;
+ }
+ } else if ((Equal(nt, "cdecl"))) {
+ String *nv = Getattr(n, "value");
+ if (nv) {
+ Delete(value);
+ value = Copy(nv);
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ return value;
+}
diff --git a/contrib/tools/swig/Source/Swig/tree.c b/contrib/tools/swig/Source/Swig/tree.c
new file mode 100644
index 0000000000..438e8b73dc
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/tree.c
@@ -0,0 +1,426 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * tree.c
+ *
+ * This file provides some general purpose functions for manipulating
+ * parse trees.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include <stdarg.h>
+#include <assert.h>
+
+static int debug_quiet = 0;
+
+/* -----------------------------------------------------------------------------
+ * Swig_print_quiet()
+ *
+ * Set quiet mode when printing a parse tree node
+ * ----------------------------------------------------------------------------- */
+
+int Swig_print_quiet(int quiet) {
+ int previous_quiet = debug_quiet;
+ debug_quiet = quiet;
+ return previous_quiet;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_print_tags()
+ *
+ * Dump the tag structure of a parse tree to standard output
+ * ----------------------------------------------------------------------------- */
+
+void Swig_print_tags(DOH *obj, DOH *root) {
+ DOH *croot, *newroot;
+ DOH *cobj;
+
+ if (!root)
+ croot = NewStringEmpty();
+ else
+ croot = root;
+
+ while (obj) {
+ Swig_diagnostic(Getfile(obj), Getline(obj), "%s . %s\n", croot, nodeType(obj));
+ cobj = firstChild(obj);
+ if (cobj) {
+ newroot = NewStringf("%s . %s", croot, nodeType(obj));
+ Swig_print_tags(cobj, newroot);
+ Delete(newroot);
+ }
+ obj = nextSibling(obj);
+ }
+ if (!root)
+ Delete(croot);
+}
+
+static int indent_level = 0;
+
+static void print_indent(int l) {
+ int i;
+ for (i = 0; i < indent_level; i++) {
+ fputc(' ', stdout);
+ }
+ if (l) {
+ fputc('|', stdout);
+ fputc(' ', stdout);
+ }
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_print_node(Node *n)
+ * ----------------------------------------------------------------------------- */
+
+void Swig_print_node(Node *obj) {
+ Iterator ki;
+ Node *cobj;
+ List *keys = Keys(obj);
+
+ print_indent(0);
+ if (debug_quiet)
+ Printf(stdout, "+++ %s ----------------------------------------\n", nodeType(obj));
+ else
+ Printf(stdout, "+++ %s - %p ----------------------------------------\n", nodeType(obj), obj);
+
+ SortList(keys, 0);
+ ki = First(keys);
+ while (ki.item) {
+ String *k = ki.item;
+ DOH *value = Getattr(obj, k);
+ if (Equal(k, "nodeType") || (*(Char(k)) == '$')) {
+ /* Do nothing */
+ } else if (debug_quiet && (Equal(k, "firstChild") || Equal(k, "lastChild") || Equal(k, "parentNode") || Equal(k, "nextSibling") ||
+ Equal(k, "previousSibling") || Equal(k, "symtab") || Equal(k, "csymtab") || Equal(k, "sym:symtab") || Equal(k, "sym:nextSibling") ||
+ Equal(k, "sym:previousSibling") || Equal(k, "csym:nextSibling") || Equal(k, "csym:previousSibling"))) {
+ /* Do nothing */
+ } else if (Equal(k, "kwargs") || Equal(k, "parms") || Equal(k, "wrap:parms") || Equal(k, "pattern") || Equal(k, "templateparms") || Equal(k, "throws")) {
+ print_indent(2);
+ /* Differentiate parameter lists by displaying within single quotes */
+ Printf(stdout, "%-12s - \'%s\'\n", k, ParmList_str_defaultargs(value));
+ } else {
+ DOH *o;
+ const char *trunc = "";
+ print_indent(2);
+ if (DohIsString(value)) {
+ o = Str(value);
+ if (Len(o) > 80) {
+ trunc = "...";
+ }
+ Printf(stdout, "%-12s - \"%(escape)-0.80s%s\"\n", k, o, trunc);
+ Delete(o);
+ } else {
+ Printf(stdout, "%-12s - %p\n", k, value);
+ }
+ }
+ ki = Next(ki);
+ }
+ cobj = firstChild(obj);
+ if (cobj) {
+ indent_level += 6;
+ Printf(stdout, "\n");
+ Swig_print_tree(cobj);
+ indent_level -= 6;
+ } else {
+ print_indent(1);
+ Printf(stdout, "\n");
+ }
+ Delete(keys);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_print_tree()
+ *
+ * Dump the tree structure of a parse tree to standard output
+ * ----------------------------------------------------------------------------- */
+
+void Swig_print_tree(DOH *obj) {
+ while (obj) {
+ Swig_print_node(obj);
+ obj = nextSibling(obj);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * appendChild()
+ *
+ * Appends a new child to a node
+ * ----------------------------------------------------------------------------- */
+
+void appendChild(Node *node, Node *chd) {
+ Node *lc;
+
+ if (!chd)
+ return;
+
+ lc = lastChild(node);
+ if (!lc) {
+ set_firstChild(node, chd);
+ } else {
+ set_nextSibling(lc, chd);
+ set_previousSibling(chd, lc);
+ }
+ while (chd) {
+ lc = chd;
+ set_parentNode(chd, node);
+ chd = nextSibling(chd);
+ }
+ set_lastChild(node, lc);
+}
+
+/* -----------------------------------------------------------------------------
+ * prependChild()
+ *
+ * Prepends a new child to a node
+ * ----------------------------------------------------------------------------- */
+
+void prependChild(Node *node, Node *chd) {
+ Node *fc;
+
+ if (!chd)
+ return;
+
+ fc = firstChild(node);
+ if (fc) {
+ set_nextSibling(chd, fc);
+ set_previousSibling(fc, chd);
+ }
+ set_firstChild(node, chd);
+ while (chd) {
+ set_parentNode(chd, node);
+ chd = nextSibling(chd);
+ }
+}
+
+void appendSibling(Node *node, Node *chd) {
+ Node *parent;
+ Node *lc = node;
+ while (nextSibling(lc))
+ lc = nextSibling(lc);
+ set_nextSibling(lc, chd);
+ set_previousSibling(chd, lc);
+ parent = parentNode(node);
+ if (parent) {
+ while (chd) {
+ lc = chd;
+ set_parentNode(chd, parent);
+ chd = nextSibling(chd);
+ }
+ set_lastChild(parent, lc);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * removeNode()
+ *
+ * Removes a node from the parse tree. Detaches it from its parent's child list.
+ * ----------------------------------------------------------------------------- */
+
+void removeNode(Node *n) {
+ Node *parent;
+ Node *prev;
+ Node *next;
+
+ parent = parentNode(n);
+ if (!parent) return;
+
+ prev = previousSibling(n);
+ next = nextSibling(n);
+ if (prev) {
+ set_nextSibling(prev, next);
+ } else {
+ if (parent) {
+ set_firstChild(parent, next);
+ }
+ }
+ if (next) {
+ set_previousSibling(next, prev);
+ } else {
+ if (parent) {
+ set_lastChild(parent, prev);
+ }
+ }
+
+ /* Delete attributes */
+ Delattr(n,"parentNode");
+ Delattr(n,"nextSibling");
+ Delattr(n,"previousSibling");
+}
+
+/* -----------------------------------------------------------------------------
+ * copyNode()
+ *
+ * Copies a node, but only copies simple attributes (no lists, hashes).
+ * ----------------------------------------------------------------------------- */
+
+Node *copyNode(Node *n) {
+ Iterator ki;
+ Node *c = NewHash();
+ for (ki = First(n); ki.key; ki = Next(ki)) {
+ if (DohIsString(ki.item)) {
+ Setattr(c, ki.key, Copy(ki.item));
+ }
+ }
+ Setfile(c, Getfile(n));
+ Setline(c, Getline(n));
+ return c;
+}
+
+/* -----------------------------------------------------------------------------
+ * checkAttribute()
+ * ----------------------------------------------------------------------------- */
+
+int checkAttribute(Node *n, const_String_or_char_ptr name, const_String_or_char_ptr value) {
+ String *v = Getattr(n, name);
+ return v ? Equal(v, value) : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_require()
+ * ns - namespace for the view name for saving any attributes under
+ * n - node
+ * ... - list of attribute names of type char*
+ *
+ * An attribute is optional if it is prefixed by ?, eg "?value". All
+ * non-optional attributes are checked for on node n and if any do not exist
+ * SWIG exits with a fatal error.
+ *
+ * If the attribute name is prefixed by * or ?, eg "*value" then a copy of the
+ * attribute is saved. The saved attributes will be restored on a subsequent
+ * call to Swig_restore(). All the saved attributes are saved in the view
+ * namespace (prefixed by ns).
+ *
+ * This function can be called more than once with different namespaces.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_require(const char *ns, Node *n, ...) {
+ va_list ap;
+ char *name;
+ DOH *obj;
+
+ va_start(ap, n);
+ name = va_arg(ap, char *);
+ while (name) {
+ int newref = 0;
+ int opt = 0;
+ if (*name == '*') {
+ newref = 1;
+ name++;
+ } else if (*name == '?') {
+ newref = 1;
+ opt = 1;
+ name++;
+ }
+ obj = Getattr(n, name);
+ if (!opt && !obj) {
+ Swig_error(Getfile(n), Getline(n), "Fatal error (Swig_require). Missing attribute '%s' in node '%s'.\n", name, nodeType(n));
+ Exit(EXIT_FAILURE);
+ }
+ if (!obj)
+ obj = DohNone;
+ if (newref) {
+ /* Save a copy of the attribute */
+ Setattr(n, NewStringf("%s:%s", ns, name), obj);
+ }
+ name = va_arg(ap, char *);
+ }
+ va_end(ap);
+
+ /* Save the view */
+ {
+ String *view = Getattr(n, "view");
+ if (view) {
+ if (Strcmp(view, ns) != 0) {
+ Setattr(n, NewStringf("%s:view", ns), view);
+ Setattr(n, "view", NewString(ns));
+ }
+ } else {
+ Setattr(n, "view", NewString(ns));
+ }
+ }
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_save()
+ * Same as Swig_require(), but all attribute names are optional and all attributes
+ * are saved, ie behaves as if all the attribute names were prefixed by ?.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_save(const char *ns, Node *n, ...) {
+ va_list ap;
+ char *name;
+ DOH *obj;
+
+ va_start(ap, n);
+ name = va_arg(ap, char *);
+ while (name) {
+ if (*name == '*') {
+ name++;
+ } else if (*name == '?') {
+ name++;
+ }
+ obj = Getattr(n, name);
+ if (!obj)
+ obj = DohNone;
+
+ /* Save a copy of the attribute */
+ if (Setattr(n, NewStringf("%s:%s", ns, name), obj)) {
+ Printf(stderr, "Swig_save('%s','%s'): Warning, attribute '%s' was already saved.\n", ns, nodeType(n), name);
+ }
+ name = va_arg(ap, char *);
+ }
+ va_end(ap);
+
+ /* Save the view */
+ {
+ String *view = Getattr(n, "view");
+ if (view) {
+ if (Strcmp(view, ns) != 0) {
+ Setattr(n, NewStringf("%s:view", ns), view);
+ Setattr(n, "view", NewString(ns));
+ }
+ } else {
+ Setattr(n, "view", NewString(ns));
+ }
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_restore()
+ * Restores attributes saved by a previous call to Swig_require() or Swig_save().
+ * ----------------------------------------------------------------------------- */
+
+void Swig_restore(Node *n) {
+ String *temp;
+ int len;
+ List *l;
+ String *ns;
+ Iterator ki;
+
+ ns = Getattr(n, "view");
+ assert(ns);
+
+ l = NewList();
+
+ temp = NewStringf("%s:", ns);
+ len = Len(temp);
+
+ for (ki = First(n); ki.key; ki = Next(ki)) {
+ if (Strncmp(temp, ki.key, len) == 0) {
+ Append(l, ki.key);
+ }
+ }
+ for (ki = First(l); ki.item; ki = Next(ki)) {
+ DOH *obj = Getattr(n, ki.item);
+ Setattr(n, Char(ki.item) + len, obj);
+ Delattr(n, ki.item);
+ }
+ Delete(l);
+ Delete(temp);
+}
diff --git a/contrib/tools/swig/Source/Swig/typemap.c b/contrib/tools/swig/Source/Swig/typemap.c
new file mode 100644
index 0000000000..f0dee59d9e
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/typemap.c
@@ -0,0 +1,2210 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * typemap.c
+ *
+ * A somewhat generalized implementation of SWIG1.1 typemaps.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+#include <ctype.h>
+
+#if 0
+#define SWIG_DEBUG
+#endif
+
+static int typemap_search_debug = 0;
+static int typemaps_used_debug = 0;
+static int typemap_register_debug = 0;
+static int in_typemap_search_multi = 0;
+
+static void replace_embedded_typemap(String *s, ParmList *parm_sublist, Wrapper *f, Node *file_line_node);
+
+/* -----------------------------------------------------------------------------
+ * Typemaps are stored in a collection of nested hash tables. Something like
+ * this:
+ *
+ * [ type ]
+ * +-------- [ name ]
+ * +-------- [ name ]
+ *
+ * Each hash table [ type ] or [ name ] then contains references to the
+ * different typemap methods. These are referenced by names such as
+ * "tmap:in", "tmap:out", "tmap:argout", and so forth.
+ *
+ * The object corresponding to a specific typemap method has the following attributes:
+ *
+ * "type" - Typemap type
+ * "pname" - Parameter name
+ * "code" - Typemap code
+ * "source" - Source directive (%apply or %typemap) for the typemap
+ * "locals" - Local variables (if any)
+ * "kwargs" - Typemap attributes
+ *
+ * Example for a typemap method named "in":
+ * %typemap(in, warning="987:my warning", noblock=1) int &my_int (int tmp) "$1 = $input;"
+ *
+ * "type" - r.int
+ * "pname" - my_int
+ * "code" - $1 = $input;
+ * "source" - typemap(in) int &my_int
+ * "locals" - int tmp
+ * "kwargs" - warning="987:my typemap warning", foo=123
+ *
+ * ----------------------------------------------------------------------------- */
+
+static Hash *typemaps;
+
+/* -----------------------------------------------------------------------------
+ * typemap_identifier_fix()
+ *
+ * Create a type that can be used as a hash key lookup independent of the various
+ * ways a template parameter list can be defined. This is achieved by fully
+ * resolving the template parameters.
+ *
+ * This is a copy and modification of feature_identifier_fix in parser.y.
+ * ----------------------------------------------------------------------------- */
+
+static SwigType *typemap_identifier_fix(const SwigType *s) {
+ String *tp = SwigType_istemplate_templateprefix(s);
+ if (tp) {
+ String *ts, *ta, *tq, *tr;
+ ts = SwigType_templatesuffix(s);
+ ta = SwigType_templateargs(s);
+ tq = Swig_symbol_type_qualify(ta, 0);
+ tr = SwigType_typedef_resolve_all(ta);
+ Append(tp,tr);
+ Append(tp,ts);
+ Delete(ts);
+ Delete(ta);
+ Delete(tq);
+ Delete(tr);
+ return tp;
+ } else {
+ return NewString(s);
+ }
+}
+
+static Hash *get_typemap(const SwigType *type) {
+ Hash *tm = 0;
+ SwigType *dtype = 0;
+ SwigType *hashtype;
+
+ if (SwigType_istemplate(type)) {
+ SwigType *rty = typemap_identifier_fix(type);
+ String *ty = Swig_symbol_template_deftype(rty, 0);
+ dtype = Swig_symbol_type_qualify(ty, 0);
+ type = dtype;
+ Delete(ty);
+ }
+
+ /* remove unary scope operator (::) prefix indicating global scope for looking up in the hashmap */
+ hashtype = SwigType_remove_global_scope_prefix(type);
+ tm = Getattr(typemaps, hashtype);
+
+ Delete(dtype);
+ Delete(hashtype);
+
+ return tm;
+}
+
+static void set_typemap(const SwigType *type, Hash **tmhash) {
+ SwigType *hashtype = 0;
+ Hash *new_tm = 0;
+ assert(*tmhash == 0);
+ if (SwigType_istemplate(type)) {
+ SwigType *rty = typemap_identifier_fix(type);
+ String *ty = Swig_symbol_template_deftype(rty, 0);
+ String *tyq = Swig_symbol_type_qualify(ty, 0);
+ hashtype = SwigType_remove_global_scope_prefix(tyq);
+ *tmhash = Getattr(typemaps, hashtype);
+ Delete(rty);
+ Delete(tyq);
+ Delete(ty);
+ } else {
+ hashtype = SwigType_remove_global_scope_prefix(type);
+ }
+
+ if (!*tmhash) {
+ /* this type has not been seen before even after resolving template parameter types */
+ new_tm = NewHash();
+ *tmhash = new_tm;
+ }
+
+ /* note that the unary scope operator (::) prefix indicating global scope has been removed from the type */
+ Setattr(typemaps, hashtype, *tmhash);
+
+ Delete(hashtype);
+ Delete(new_tm);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_init()
+ *
+ * Initialize the typemap system
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_init(void) {
+ typemaps = NewHash();
+}
+
+static String *typemap_method_name(const_String_or_char_ptr tmap_method) {
+ static Hash *names = 0;
+ String *s;
+ /* Due to "interesting" object-identity semantics of DOH,
+ we have to make sure that we only intern strings without object
+ identity into the hash table.
+
+ (typemap_attach_kwargs calls typemap_method_name several times with
+ the "same" String *tmap_method (i.e., same object identity) but differing
+ string values.)
+
+ Most other callers work around this by using char* rather than
+ String *.
+ -- mkoeppe, Jun 17, 2003
+ */
+ const char *method_without_object_identity = Char(tmap_method);
+ if (!names)
+ names = NewHash();
+ s = Getattr(names, method_without_object_identity);
+ if (s)
+ return s;
+ s = NewStringf("tmap:%s", tmap_method);
+ Setattr(names, method_without_object_identity, s);
+ Delete(s);
+ return s;
+}
+
+/* -----------------------------------------------------------------------------
+ * typemap_register()
+ *
+ * Internal implementation for Swig_typemap_register()
+ * ----------------------------------------------------------------------------- */
+
+static void typemap_register(const_String_or_char_ptr tmap_method, ParmList *parms, const_String_or_char_ptr code, ParmList *locals, ParmList *kwargs, String *source_directive) {
+ Hash *tm;
+ Hash *tm1;
+ Hash *tm2;
+ Parm *np;
+ String *tm_method;
+ SwigType *type;
+ String *pname;
+ if (!parms)
+ return;
+
+ if (typemap_register_debug) {
+ Printf(stdout, "Registering - %s\n", tmap_method);
+ Swig_print_node(parms);
+ }
+
+ tm_method = typemap_method_name(tmap_method);
+
+ /* Register the first type in the parameter list */
+
+ type = Getattr(parms, "type");
+ pname = Getattr(parms, "name");
+
+ /* See if this type has been seen before */
+ tm = get_typemap(type);
+ if (!tm) {
+ set_typemap(type, &tm);
+ }
+ if (pname) {
+ /* See if parameter has been seen before */
+ tm1 = Getattr(tm, pname);
+ if (!tm1) {
+ tm1 = NewHash();
+ Setattr(tm, pname, tm1);
+ Delete(tm1);
+ }
+ tm = tm1;
+ }
+
+ /* Now see if this typemap method has been seen before */
+ tm2 = Getattr(tm, tm_method);
+ if (!tm2) {
+ tm2 = NewHash();
+ Setattr(tm, tm_method, tm2);
+ Delete(tm2);
+ }
+
+ /* For a multi-argument typemap, the typemap code and information
+ is really only stored in the last argument. However, to
+ make this work, we perform a really neat trick using
+ the typemap method name.
+
+ For example, consider this typemap
+
+ %typemap(in) (int foo, int *bar, char *blah[]) {
+ ...
+ }
+
+ To store it, we look at typemaps for the following:
+
+ typemap method type-name
+ ----------------------------------------------
+ "in" int foo
+ "in-int+foo:" int *bar
+ "in-int+foo:-p.int+bar: char *blah[]
+
+ Notice how the typemap method name expands to encode information about
+ previous arguments.
+
+ */
+
+ np = nextSibling(parms);
+ if (np) {
+ /* Make an entirely new typemap method key */
+ String *multi_tmap_method = NewStringf("%s-%s+%s:", tmap_method, type, pname);
+
+ /* Now reregister on the remaining arguments */
+ typemap_register(multi_tmap_method, np, code, locals, kwargs, source_directive);
+
+ Delete(multi_tmap_method);
+ } else {
+ ParmList *clocals = CopyParmList(locals);
+ ParmList *ckwargs = CopyParmList(kwargs);
+
+ Setfile(tm2, Getfile(code));
+ Setline(tm2, Getline(code));
+ Setattr(tm2, "code", code);
+ Setattr(tm2, "type", type);
+ Setattr(tm2, "source", source_directive);
+ if (pname) {
+ Setattr(tm2, "pname", pname);
+ }
+ Setattr(tm2, "locals", clocals);
+ Setattr(tm2, "kwargs", ckwargs);
+
+ Delete(clocals);
+ Delete(ckwargs);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_register()
+ *
+ * Add a new, possibly multi-argument, typemap
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_register(const_String_or_char_ptr tmap_method, ParmList *parms, const_String_or_char_ptr code, ParmList *locals, ParmList *kwargs) {
+ String *parms_str = ParmList_str_multibrackets(parms);
+ String *source_directive = NewStringf("typemap(%s) %s", tmap_method, parms_str);
+
+ typemap_register(tmap_method, parms, code, locals, kwargs, source_directive);
+
+ Delete(source_directive);
+ Delete(parms_str);
+}
+
+/* -----------------------------------------------------------------------------
+ * typemap_get()
+ *
+ * Retrieve typemap information.
+ * ----------------------------------------------------------------------------- */
+
+static Hash *typemap_get(SwigType *type, const_String_or_char_ptr name) {
+ Hash *tm, *tm1;
+ tm = get_typemap(type);
+ if (!tm) {
+ return 0;
+ }
+ if ((name) && Len(name)) {
+ tm1 = Getattr(tm, name);
+ return tm1;
+ }
+ return tm;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_copy()
+ *
+ * Copy a typemap
+ * ----------------------------------------------------------------------------- */
+
+int Swig_typemap_copy(const_String_or_char_ptr tmap_method, ParmList *srcparms, ParmList *parms) {
+ Hash *tm = 0;
+ String *tm_method;
+ Parm *p;
+ String *pname;
+ SwigType *ptype;
+ String *tm_methods, *multi_tmap_method;
+ if (ParmList_len(parms) != ParmList_len(srcparms))
+ return -1;
+
+ tm_method = typemap_method_name(tmap_method);
+ p = srcparms;
+ tm_methods = NewString(tm_method);
+ while (p) {
+ ptype = Getattr(p, "type");
+ pname = Getattr(p, "name");
+
+ /* Lookup the type */
+ tm = typemap_get(ptype, pname);
+ if (!tm)
+ break;
+
+ tm = Getattr(tm, tm_methods);
+ if (!tm)
+ break;
+
+ /* Got a match. Look for next typemap */
+ multi_tmap_method = NewStringf("%s-%s+%s:", tm_methods, ptype, pname);
+ Delete(tm_methods);
+ tm_methods = multi_tmap_method;
+ p = nextSibling(p);
+ }
+ Delete(tm_methods);
+
+ if (!p && tm) {
+ /* Got some kind of match */
+ String *parms_str = ParmList_str_multibrackets(parms);
+ String *srcparms_str = ParmList_str_multibrackets(srcparms);
+ String *source_directive = NewStringf("typemap(%s) %s = %s", tmap_method, parms_str, srcparms_str);
+
+ typemap_register(tmap_method, parms, Getattr(tm, "code"), Getattr(tm, "locals"), Getattr(tm, "kwargs"), source_directive);
+
+ Delete(source_directive);
+ Delete(srcparms_str);
+ Delete(parms_str);
+ return 0;
+ }
+
+ /* Not found */
+ return -1;
+
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_clear()
+ *
+ * Delete a multi-argument typemap
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_clear(const_String_or_char_ptr tmap_method, ParmList *parms) {
+ SwigType *type;
+ String *name;
+ Parm *p;
+ String *multi_tmap_method;
+ Hash *tm = 0;
+
+ /* This might not work */
+ multi_tmap_method = NewString(tmap_method);
+ p = parms;
+ while (p) {
+ type = Getattr(p, "type");
+ name = Getattr(p, "name");
+ tm = typemap_get(type, name);
+ if (!tm)
+ return;
+ p = nextSibling(p);
+ if (p)
+ Printf(multi_tmap_method, "-%s+%s:", type, name);
+ }
+ if (tm) {
+ tm = Getattr(tm, typemap_method_name(multi_tmap_method));
+ if (tm) {
+ Delattr(tm, "code");
+ Delattr(tm, "locals");
+ Delattr(tm, "kwargs");
+ }
+ }
+ Delete(multi_tmap_method);
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_apply()
+ *
+ * Multi-argument %apply directive. This is pretty horrible so I sure hope
+ * it works.
+ * ----------------------------------------------------------------------------- */
+
+static int count_args(String *s) {
+ /* Count up number of arguments */
+ int na = 0;
+ char *c = Char(s);
+ while (*c) {
+ if (*c == '+')
+ na++;
+ c++;
+ }
+ return na;
+}
+
+int Swig_typemap_apply(ParmList *src, ParmList *dest) {
+ String *ssig, *dsig;
+ Parm *p, *np, *lastp, *dp, *lastdp = 0;
+ int narg = 0;
+ SwigType *type = 0, *name;
+ Hash *tm, *sm;
+ int match = 0;
+
+ /* Printf(stdout,"apply : %s --> %s\n", ParmList_str(src), ParmList_str(dest)); */
+
+ /* Create type signature of source */
+ ssig = NewStringEmpty();
+ dsig = NewStringEmpty();
+ p = src;
+ dp = dest;
+ lastp = 0;
+ while (p) {
+ lastp = p;
+ lastdp = dp;
+ np = nextSibling(p);
+ if (np) {
+ Printf(ssig, "-%s+%s:", Getattr(p, "type"), Getattr(p, "name"));
+ Printf(dsig, "-%s+%s:", Getattr(dp, "type"), Getattr(dp, "name"));
+ narg++;
+ }
+ p = np;
+ dp = nextSibling(dp);
+ }
+
+ /* make sure a typemap node exists for the last destination node */
+ type = Getattr(lastdp, "type");
+ tm = get_typemap(type);
+ if (!tm) {
+ set_typemap(type, &tm);
+ }
+ name = Getattr(lastdp, "name");
+ if (name) {
+ Hash *tm1 = Getattr(tm, name);
+ if (!tm1) {
+ tm1 = NewHash();
+ Setattr(tm, NewString(name), tm1);
+ Delete(tm1);
+ }
+ tm = tm1;
+ }
+
+ /* This is a little nasty. We need to go searching for all possible typemaps in the
+ source and apply them to the target */
+
+ type = Getattr(lastp, "type");
+ name = Getattr(lastp, "name");
+
+ /* See if there is a matching typemap in this scope */
+ sm = typemap_get(type, name);
+
+ /* if there is not matching, look for a typemap in the
+ original typedef, if any, like in:
+
+ typedef unsigned long size_t;
+ ...
+ %apply(size_t) {my_size}; ==> %apply(unsigned long) {my_size};
+ */
+ if (!sm) {
+ SwigType *ntype = SwigType_typedef_resolve(type);
+ if (ntype && (Cmp(ntype, type) != 0)) {
+ sm = typemap_get(ntype, name);
+ }
+ Delete(ntype);
+ }
+
+ if (sm) {
+ /* Got a typemap. Need to only merge attributes for methods that match our signature */
+ Iterator ki;
+ Hash *deferred_add;
+ match = 1;
+
+ /* Since typemap_register can modify the `sm` hash, we *cannot* call typemap_register while iterating over sm.
+ * Create a temporary hash of typemaps to add immediately after. */
+ deferred_add = NewHash();
+ for (ki = First(sm); ki.key; ki = Next(ki)) {
+ /* Check for a signature match with the source signature */
+ if ((count_args(ki.key) == narg) && (Strstr(ki.key, ssig))) {
+ String *oldm;
+ /* A typemap we have to copy */
+ String *nkey = Copy(ki.key);
+ Replace(nkey, ssig, dsig, DOH_REPLACE_ANY);
+
+ /* Make sure the typemap doesn't already exist in the target map */
+ oldm = Getattr(tm, nkey);
+ if (!oldm || (!Getattr(tm, "code"))) {
+ String *code;
+ Hash *sm1 = ki.item;
+
+ code = Getattr(sm1, "code");
+ if (code) {
+ Replace(nkey, dsig, "", DOH_REPLACE_ANY);
+ Replace(nkey, "tmap:", "", DOH_REPLACE_ANY);
+ Setattr(deferred_add, nkey, sm1);
+ }
+ }
+ Delete(nkey);
+ }
+ }
+
+ /* After assembling the key/item pairs, add the resulting typemaps */
+ for (ki = First(deferred_add); ki.key; ki = Next(ki)) {
+ Hash *sm1 = ki.item;
+ String *src_str = ParmList_str_multibrackets(src);
+ String *dest_str = ParmList_str_multibrackets(dest);
+ String *source_directive = NewStringf("apply %s { %s }", src_str, dest_str);
+
+ typemap_register(ki.key, dest, Getattr(sm1, "code"), Getattr(sm1, "locals"), Getattr(sm1, "kwargs"), source_directive);
+
+ Delete(source_directive);
+ Delete(dest_str);
+ Delete(src_str);
+ }
+ Delete(deferred_add);
+ }
+ Delete(ssig);
+ Delete(dsig);
+ return match;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_clear_apply()
+ *
+ * %clear directive. Clears all typemaps for a type (in the current scope only).
+ * ----------------------------------------------------------------------------- */
+
+/* Multi-argument %clear directive */
+void Swig_typemap_clear_apply(Parm *parms) {
+ String *tsig;
+ Parm *p, *np, *lastp;
+ int narg = 0;
+ Hash *tm;
+ String *name;
+
+ /* Create a type signature of the parameters */
+ tsig = NewStringEmpty();
+ p = parms;
+ lastp = 0;
+ while (p) {
+ lastp = p;
+ np = nextSibling(p);
+ if (np) {
+ Printf(tsig, "-%s+%s:", Getattr(p, "type"), Getattr(p, "name"));
+ narg++;
+ }
+ p = np;
+ }
+ tm = get_typemap(Getattr(lastp, "type"));
+ if (!tm) {
+ Delete(tsig);
+ return;
+ }
+ name = Getattr(lastp, "name");
+ if (name) {
+ tm = Getattr(tm, name);
+ }
+ if (tm) {
+ /* Clear typemaps that match our signature */
+ Iterator ki, ki2;
+ char *ctsig = Char(tsig);
+ for (ki = First(tm); ki.key; ki = Next(ki)) {
+ char *ckey = Char(ki.key);
+ if (strncmp(ckey, "tmap:", 5) == 0) {
+ int na = count_args(ki.key);
+ if ((na == narg) && strstr(ckey, ctsig)) {
+ Hash *h = ki.item;
+ for (ki2 = First(h); ki2.key; ki2 = Next(ki2)) {
+ Delattr(h, ki2.key);
+ }
+ }
+ }
+ }
+ }
+ Delete(tsig);
+}
+
+/* Internal function to strip array dimensions. */
+static SwigType *strip_arrays(SwigType *type) {
+ SwigType *t;
+ int ndim;
+ int i;
+ t = Copy(type);
+ ndim = SwigType_array_ndim(t);
+ for (i = 0; i < ndim; i++) {
+ SwigType_array_setdim(t, i, "ANY");
+ }
+ return t;
+}
+
+static void debug_search_result_display(Node *tm) {
+ if (tm)
+ Printf(stdout, " Using: %%%s\n", Getattr(tm, "source"));
+ else
+ Printf(stdout, " None found\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * typemap_search_helper()
+ *
+ * Helper function for typemap_search to see if there is a type match in the typemap
+ * tm. A match is sought in this order:
+ * %typemap(tm_method) ctype cqualifiedname
+ * %typemap(tm_method) ctype cname
+ * %typemap(tm_method) ctype
+ * ----------------------------------------------------------------------------- */
+
+static Hash *typemap_search_helper(int debug_display, Hash *tm, const String *tm_method, SwigType *ctype, const String *cqualifiedname, const String *cname, Hash **backup) {
+ Hash *result = 0;
+ Hash *tm1;
+ if (debug_display && cqualifiedname)
+ Printf(stdout, " Looking for: %s\n", SwigType_str(ctype, cqualifiedname));
+ if (tm && cqualifiedname) {
+ tm1 = Getattr(tm, cqualifiedname);
+ if (tm1) {
+ result = Getattr(tm1, tm_method); /* See if there is a type - qualified name match */
+ if (result && Getattr(result, "code"))
+ goto ret_result;
+ if (result)
+ *backup = result;
+ }
+ }
+ if (debug_display && cname)
+ Printf(stdout, " Looking for: %s\n", SwigType_str(ctype, cname));
+ if (tm && cname) {
+ tm1 = Getattr(tm, cname);
+ if (tm1) {
+ result = Getattr(tm1, tm_method); /* See if there is a type - name match */
+ if (result && Getattr(result, "code"))
+ goto ret_result;
+ if (result)
+ *backup = result;
+ }
+ }
+ if (debug_display)
+ Printf(stdout, " Looking for: %s\n", SwigType_str(ctype, 0));
+ if (tm) {
+ result = Getattr(tm, tm_method); /* See if there is simply a type without name match */
+ if (result && Getattr(result, "code"))
+ goto ret_result;
+ if (result)
+ *backup = result;
+ }
+ret_result:
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * typemap_search()
+ *
+ * Search for a typemap match. This is where the typemap pattern matching rules
+ * are implemented... tries to find the most specific typemap that includes a
+ * 'code' attribute.
+ * ----------------------------------------------------------------------------- */
+
+static Hash *typemap_search(const_String_or_char_ptr tmap_method, SwigType *type, const_String_or_char_ptr name, const_String_or_char_ptr qualifiedname, SwigType **matchtype, Node *node) {
+ Hash *result = 0;
+ Hash *tm;
+ Hash *backup = 0;
+ SwigType *primitive = 0;
+ SwigType *ctype = 0;
+ SwigType *ctype_unstripped = 0;
+ int isarray;
+ const String *cname = 0;
+ const String *cqualifiedname = 0;
+ String *tm_method = typemap_method_name(tmap_method);
+ int debug_display = (in_typemap_search_multi == 0) && typemap_search_debug;
+
+ if ((name) && Len(name))
+ cname = name;
+ if ((qualifiedname) && Len(qualifiedname))
+ cqualifiedname = qualifiedname;
+
+ if (debug_display) {
+ String *typestr = SwigType_str(type, cqualifiedname ? cqualifiedname : cname);
+ Swig_diagnostic(Getfile(node), Getline(node), "Searching for a suitable '%s' typemap for: %s\n", tmap_method, typestr);
+ Delete(typestr);
+ }
+ ctype = Copy(type);
+ ctype_unstripped = Copy(ctype);
+ while (ctype) {
+ /* Try to get an exact type-match */
+ tm = get_typemap(ctype);
+ result = typemap_search_helper(debug_display, tm, tm_method, ctype, cqualifiedname, cname, &backup);
+ if (result && Getattr(result, "code"))
+ goto ret_result;
+
+ {
+ /* Look for the type reduced to just the template prefix - for templated types without the template parameter list being specified */
+ SwigType *template_prefix = SwigType_istemplate_only_templateprefix(ctype);
+ if (template_prefix) {
+ tm = get_typemap(template_prefix);
+ result = typemap_search_helper(debug_display, tm, tm_method, template_prefix, cqualifiedname, cname, &backup);
+ Delete(template_prefix);
+ if (result && Getattr(result, "code"))
+ goto ret_result;
+ }
+ }
+
+ /* look for [ANY] arrays */
+ isarray = SwigType_isarray(ctype);
+ if (isarray) {
+ /* If working with arrays, strip away all of the dimensions and replace with "ANY".
+ See if that generates a match */
+ SwigType *noarrays = strip_arrays(ctype);
+ tm = get_typemap(noarrays);
+ result = typemap_search_helper(debug_display, tm, tm_method, noarrays, cqualifiedname, cname, &backup);
+ Delete(noarrays);
+ if (result && Getattr(result, "code"))
+ goto ret_result;
+ }
+
+ /* No match so far - try with a qualifier stripped (strip one qualifier at a time until none remain)
+ * The order of stripping in SwigType_strip_single_qualifier is used to provide some sort of consistency
+ * with the default (SWIGTYPE) typemap matching rules for the first qualifier to be stripped. */
+ {
+ SwigType *oldctype = ctype;
+ ctype = SwigType_strip_single_qualifier(oldctype);
+ if (!Equal(ctype, oldctype)) {
+ Delete(oldctype);
+ continue;
+ }
+ Delete(oldctype);
+ }
+
+ /* Once all qualifiers are stripped try resolve a typedef */
+ {
+ SwigType *oldctype = ctype;
+ ctype = SwigType_typedef_resolve(ctype_unstripped);
+ Delete(oldctype);
+ Delete(ctype_unstripped);
+ ctype_unstripped = Copy(ctype);
+ }
+ }
+
+ /* Hmmm. Well, no match seems to be found at all. See if there is some kind of default (SWIGTYPE) mapping */
+
+ primitive = SwigType_default_create(type);
+ while (primitive) {
+ tm = get_typemap(primitive);
+ result = typemap_search_helper(debug_display, tm, tm_method, primitive, cqualifiedname, cname, &backup);
+ if (result && Getattr(result, "code"))
+ goto ret_result;
+
+ {
+ SwigType *nprim = SwigType_default_deduce(primitive);
+ Delete(primitive);
+ primitive = nprim;
+ }
+ }
+ if (ctype != type) {
+ Delete(ctype);
+ ctype = 0;
+ }
+ result = backup;
+
+ret_result:
+ Delete(primitive);
+ if (matchtype)
+ *matchtype = Copy(ctype);
+ Delete(ctype);
+ Delete(ctype_unstripped);
+ return result;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * typemap_search_multi()
+ *
+ * Search for a multi-argument typemap.
+ * ----------------------------------------------------------------------------- */
+
+static Hash *typemap_search_multi(const_String_or_char_ptr tmap_method, ParmList *parms, int *nmatch) {
+ SwigType *type;
+ SwigType *mtype = 0;
+ String *name;
+ String *multi_tmap_method;
+ Hash *tm;
+ Hash *tm1 = 0;
+
+ if (!parms) {
+ *nmatch = 0;
+ return 0;
+ }
+ type = Getattr(parms, "type");
+ name = Getattr(parms, "name");
+
+ /* Try to find a match on the first type */
+ tm = typemap_search(tmap_method, type, name, 0, &mtype, parms);
+ if (tm) {
+ if (mtype && SwigType_isarray(mtype)) {
+ Setattr(parms, "tmap:match", mtype);
+ }
+ Delete(mtype);
+ multi_tmap_method = NewStringf("%s-%s+%s:", tmap_method, type, name);
+ in_typemap_search_multi++;
+ tm1 = typemap_search_multi(multi_tmap_method, nextSibling(parms), nmatch);
+ in_typemap_search_multi--;
+ if (tm1)
+ tm = tm1;
+ if (Getattr(tm, "code")) {
+ *(nmatch) = *nmatch + 1;
+ if (typemap_search_debug && tm1 && (in_typemap_search_multi == 0)) {
+ Printf(stdout, " Multi-argument typemap found...\n");
+ }
+ } else {
+ tm = 0;
+ }
+ Delete(multi_tmap_method);
+ }
+
+ if (typemap_search_debug && (in_typemap_search_multi == 0))
+ debug_search_result_display(tm);
+ if (typemaps_used_debug && (in_typemap_search_multi == 0) && tm) {
+ String *typestr = SwigType_str(type, name);
+ Swig_diagnostic(Getfile(parms), Getline(parms), "Typemap for %s (%s) : %%%s\n", typestr, tmap_method, Getattr(tm, "source"));
+ assert(Getfile(parms) && Len(Getfile(parms)) > 0); /* Missing file and line numbering information */
+ Delete(typestr);
+ }
+
+ return tm;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * typemap_replace_vars()
+ *
+ * Replaces typemap variables on a string. index is the $n variable.
+ * type and pname are the type and parameter name.
+ * ----------------------------------------------------------------------------- */
+
+static void replace_local_types(ParmList *p, const String *name, const String *rep) {
+ SwigType *t;
+ while (p) {
+ t = Getattr(p, "type");
+ Replace(t, name, rep, DOH_REPLACE_ANY);
+ p = nextSibling(p);
+ }
+}
+
+static int check_locals(ParmList *p, const char *s) {
+ while (p) {
+ char *c = GetChar(p, "type");
+ if (strstr(c, s))
+ return 1;
+ p = nextSibling(p);
+ }
+ return 0;
+}
+
+static int typemap_replace_vars(String *s, ParmList *locals, SwigType *type, SwigType *rtype, String *pname, String *lname, int index) {
+ char var[512];
+ char *varname;
+ SwigType *ftype;
+ int bare_substitution_count = 0;
+
+ Replaceall(s, "$typemap", "$TYPEMAP"); /* workaround for $type substitution below */
+
+ ftype = SwigType_typedef_resolve_all(type);
+
+ if (!pname)
+ pname = lname;
+ {
+ Parm *p;
+ int rep = 0;
+ p = locals;
+ while (p) {
+ if (Strchr(Getattr(p, "type"), '$'))
+ rep = 1;
+ p = nextSibling(p);
+ }
+ if (!rep)
+ locals = 0;
+ }
+
+ sprintf(var, "$%d_", index);
+ varname = &var[strlen(var)];
+
+ /* If the original datatype was an array. We're going to go through and substitute
+ its array dimensions */
+
+ if (SwigType_isarray(type) || SwigType_isarray(ftype)) {
+ String *size;
+ int ndim;
+ int i;
+ if (SwigType_array_ndim(type) != SwigType_array_ndim(ftype))
+ type = ftype;
+ ndim = SwigType_array_ndim(type);
+ size = NewStringEmpty();
+ for (i = 0; i < ndim; i++) {
+ String *dim = SwigType_array_getdim(type, i);
+ if (index == 1) {
+ char t[32];
+ sprintf(t, "$dim%d", i);
+ Replace(s, t, dim, DOH_REPLACE_ANY);
+ replace_local_types(locals, t, dim);
+ }
+ sprintf(varname, "dim%d", i);
+ Replace(s, var, dim, DOH_REPLACE_ANY);
+ replace_local_types(locals, var, dim);
+ if (Len(size))
+ Putc('*', size);
+ Append(size, dim);
+ Delete(dim);
+ }
+ sprintf(varname, "size");
+ Replace(s, var, size, DOH_REPLACE_ANY);
+ replace_local_types(locals, var, size);
+ Delete(size);
+ }
+
+ /* Parameter name substitution */
+ if (index == 1) {
+ Replace(s, "$parmname", pname, DOH_REPLACE_ANY);
+ }
+ strcpy(varname, "name");
+ Replace(s, var, pname, DOH_REPLACE_ANY);
+
+ /* Type-related stuff */
+ {
+ SwigType *star_type, *amp_type, *base_type, *lex_type;
+ SwigType *ltype, *star_ltype, *amp_ltype;
+ String *mangle, *star_mangle, *amp_mangle, *base_mangle, *base_name, *base_type_str;
+ String *descriptor, *star_descriptor, *amp_descriptor;
+ String *ts;
+ char *sc;
+
+ sc = Char(s);
+
+ if (strstr(sc, "type") || check_locals(locals, "type")) {
+ /* Given type : $type */
+ ts = SwigType_str(type, 0);
+ if (index == 1) {
+ Replace(s, "$type", ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, "$type", type);
+ }
+ strcpy(varname, "type");
+ Replace(s, var, ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, var, type);
+ Delete(ts);
+ sc = Char(s);
+ }
+ if (strstr(sc, "ltype") || check_locals(locals, "ltype")) {
+ /* Local type: $ltype */
+ ltype = SwigType_ltype(type);
+ ts = SwigType_str(ltype, 0);
+ if (index == 1) {
+ Replace(s, "$ltype", ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, "$ltype", ltype);
+ }
+ strcpy(varname, "ltype");
+ Replace(s, var, ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, var, ltype);
+ Delete(ts);
+ Delete(ltype);
+ sc = Char(s);
+ }
+ if (strstr(sc, "mangle") || strstr(sc, "descriptor")) {
+ /* Mangled type */
+
+ mangle = SwigType_manglestr(type);
+ if (index == 1)
+ Replace(s, "$mangle", mangle, DOH_REPLACE_ANY);
+ strcpy(varname, "mangle");
+ Replace(s, var, mangle, DOH_REPLACE_ANY);
+
+ descriptor = NewStringf("SWIGTYPE%s", mangle);
+
+ if (index == 1)
+ if (Replace(s, "$descriptor", descriptor, DOH_REPLACE_ANY))
+ SwigType_remember(type);
+
+ strcpy(varname, "descriptor");
+ if (Replace(s, var, descriptor, DOH_REPLACE_ANY))
+ SwigType_remember(type);
+
+ Delete(descriptor);
+ Delete(mangle);
+ }
+
+ /* One pointer level removed */
+ /* This creates variables of the form
+ $*n_type
+ $*n_ltype
+ */
+
+ if (SwigType_ispointer(ftype) || (SwigType_isarray(ftype)) || (SwigType_isreference(ftype)) || (SwigType_isrvalue_reference(ftype))) {
+ if (!(SwigType_isarray(type) || SwigType_ispointer(type) || SwigType_isreference(type) || SwigType_isrvalue_reference(type))) {
+ star_type = Copy(ftype);
+ } else {
+ star_type = Copy(type);
+ }
+ if (!(SwigType_isreference(star_type) || SwigType_isrvalue_reference(star_type))) {
+ if (SwigType_isarray(star_type)) {
+ SwigType_del_element(star_type);
+ } else {
+ SwigType_del_pointer(star_type);
+ }
+ ts = SwigType_str(star_type, 0);
+ if (index == 1) {
+ Replace(s, "$*type", ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, "$*type", star_type);
+ }
+ sprintf(varname, "$*%d_type", index);
+ Replace(s, varname, ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, varname, star_type);
+ Delete(ts);
+ } else {
+ SwigType_del_element(star_type);
+ }
+ star_ltype = SwigType_ltype(star_type);
+ ts = SwigType_str(star_ltype, 0);
+ if (index == 1) {
+ Replace(s, "$*ltype", ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, "$*ltype", star_ltype);
+ }
+ sprintf(varname, "$*%d_ltype", index);
+ Replace(s, varname, ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, varname, star_ltype);
+ Delete(ts);
+ Delete(star_ltype);
+
+ star_mangle = SwigType_manglestr(star_type);
+ if (index == 1)
+ Replace(s, "$*mangle", star_mangle, DOH_REPLACE_ANY);
+
+ sprintf(varname, "$*%d_mangle", index);
+ Replace(s, varname, star_mangle, DOH_REPLACE_ANY);
+
+ star_descriptor = NewStringf("SWIGTYPE%s", star_mangle);
+ if (index == 1)
+ if (Replace(s, "$*descriptor", star_descriptor, DOH_REPLACE_ANY))
+ SwigType_remember(star_type);
+ sprintf(varname, "$*%d_descriptor", index);
+ if (Replace(s, varname, star_descriptor, DOH_REPLACE_ANY))
+ SwigType_remember(star_type);
+
+ Delete(star_descriptor);
+ Delete(star_mangle);
+ Delete(star_type);
+ } else {
+ /* TODO: Signal error if one of the $* substitutions is
+ requested */
+ }
+ /* One pointer level added */
+ amp_type = Copy(type);
+ SwigType_add_pointer(amp_type);
+ ts = SwigType_str(amp_type, 0);
+ if (index == 1) {
+ Replace(s, "$&type", ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, "$&type", amp_type);
+ }
+ sprintf(varname, "$&%d_type", index);
+ Replace(s, varname, ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, varname, amp_type);
+ Delete(ts);
+
+ amp_ltype = SwigType_ltype(type);
+ SwigType_add_pointer(amp_ltype);
+ ts = SwigType_str(amp_ltype, 0);
+
+ if (index == 1) {
+ Replace(s, "$&ltype", ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, "$&ltype", amp_ltype);
+ }
+ sprintf(varname, "$&%d_ltype", index);
+ Replace(s, varname, ts, DOH_REPLACE_ANY);
+ replace_local_types(locals, varname, amp_ltype);
+ Delete(ts);
+ Delete(amp_ltype);
+
+ amp_mangle = SwigType_manglestr(amp_type);
+ if (index == 1)
+ Replace(s, "$&mangle", amp_mangle, DOH_REPLACE_ANY);
+ sprintf(varname, "$&%d_mangle", index);
+ Replace(s, varname, amp_mangle, DOH_REPLACE_ANY);
+
+ amp_descriptor = NewStringf("SWIGTYPE%s", amp_mangle);
+ if (index == 1)
+ if (Replace(s, "$&descriptor", amp_descriptor, DOH_REPLACE_ANY))
+ SwigType_remember(amp_type);
+ sprintf(varname, "$&%d_descriptor", index);
+ if (Replace(s, varname, amp_descriptor, DOH_REPLACE_ANY))
+ SwigType_remember(amp_type);
+
+ Delete(amp_descriptor);
+ Delete(amp_mangle);
+ Delete(amp_type);
+
+ /* Base type */
+ if (SwigType_isarray(type)) {
+ base_type = Copy(type);
+ Delete(SwigType_pop_arrays(base_type));
+ } else {
+ base_type = SwigType_base(type);
+ }
+
+ base_type_str = SwigType_str(base_type, 0);
+ base_name = SwigType_namestr(base_type_str);
+ if (index == 1) {
+ Replace(s, "$basetype", base_name, DOH_REPLACE_ANY);
+ replace_local_types(locals, "$basetype", base_name);
+ }
+ strcpy(varname, "basetype");
+ Replace(s, var, base_type_str, DOH_REPLACE_ANY);
+ replace_local_types(locals, var, base_name);
+
+ base_mangle = SwigType_manglestr(base_type);
+ if (index == 1)
+ Replace(s, "$basemangle", base_mangle, DOH_REPLACE_ANY);
+ strcpy(varname, "basemangle");
+ Replace(s, var, base_mangle, DOH_REPLACE_ANY);
+ Delete(base_mangle);
+ Delete(base_name);
+ Delete(base_type_str);
+ Delete(base_type);
+
+ lex_type = SwigType_base(rtype);
+ if (index == 1)
+ Replace(s, "$lextype", lex_type, DOH_REPLACE_ANY);
+ strcpy(varname, "lextype");
+ Replace(s, var, lex_type, DOH_REPLACE_ANY);
+ Delete(lex_type);
+ }
+
+ /* Replace any $n. with (&n)-> */
+ {
+ char temp[64];
+ sprintf(var, "$%d.", index);
+ sprintf(temp, "(&$%d)->", index);
+ Replace(s, var, temp, DOH_REPLACE_ANY);
+ }
+
+ /* Replace the bare $n variable */
+ sprintf(var, "$%d", index);
+ bare_substitution_count = Replace(s, var, lname, DOH_REPLACE_NUMBER_END);
+ Delete(ftype);
+ return bare_substitution_count;
+}
+
+/* ------------------------------------------------------------------------
+ * static typemap_locals()
+ *
+ * Takes a string, a parameter list and a wrapper function argument and
+ * creates the local variables.
+ * ------------------------------------------------------------------------ */
+
+static void typemap_locals(String *s, ParmList *l, Wrapper *f, int argnum) {
+ Parm *p;
+ char *new_name;
+
+ p = l;
+ while (p) {
+ SwigType *pt = Getattr(p, "type");
+ SwigType *at = SwigType_alttype(pt, 1);
+ String *pn = Getattr(p, "name");
+ String *value = Getattr(p, "value");
+ if (at)
+ pt = at;
+ if (pn) {
+ if (Len(pn) > 0) {
+ String *str;
+ int isglobal = 0;
+
+ str = NewStringEmpty();
+
+ if (strncmp(Char(pn), "_global_", 8) == 0) {
+ isglobal = 1;
+ }
+
+ /* If the user gave us $type as the name of the local variable, we'll use
+ the passed datatype instead */
+
+ if ((argnum >= 0) && (!isglobal)) {
+ Printf(str, "%s%d", pn, argnum);
+ } else {
+ Append(str, pn);
+ }
+ if (isglobal && Wrapper_check_local(f, str)) {
+ p = nextSibling(p);
+ Delete(str);
+ if (at)
+ Delete(at);
+ continue;
+ }
+ if (value) {
+ String *pstr = SwigType_str(pt, str);
+ new_name = Wrapper_new_localv(f, str, pstr, "=", value, NIL);
+ Delete(pstr);
+ } else {
+ String *pstr = SwigType_str(pt, str);
+ new_name = Wrapper_new_localv(f, str, pstr, NIL);
+ Delete(pstr);
+ }
+ if (!isglobal) {
+ /* Substitute */
+ Replace(s, pn, new_name, DOH_REPLACE_ID | DOH_REPLACE_NOQUOTE);
+ }
+ Delete(str);
+ }
+ }
+ p = nextSibling(p);
+ if (at)
+ Delete(at);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * typemap_warn()
+ *
+ * If any warning message is attached to this parameter's "tmap:<method>:warning"
+ * attribute, return the warning message (special variables will need expanding
+ * before displaying the warning).
+ * ----------------------------------------------------------------------------- */
+
+static String *typemap_warn(const_String_or_char_ptr tmap_method, Parm *p) {
+ String *temp = NewStringf("%s:warning", tmap_method);
+ String *w = Getattr(p, typemap_method_name(temp));
+ Delete(temp);
+ return w ? Copy(w) : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * typemap_merge_fragment_kwargs()
+ *
+ * If multiple 'fragment' attributes are provided to a typemap, combine them by
+ * concatenating with commas.
+ * ----------------------------------------------------------------------------- */
+
+static void typemap_merge_fragment_kwargs(Parm *kw) {
+ Parm *reattach_kw = NULL;
+ Parm *prev_kw = NULL;
+ Parm *next_kw = NULL;
+ String *fragment = NULL;
+ while (kw) {
+ next_kw = nextSibling(kw);
+ if (Strcmp(Getattr(kw, "name"), "fragment") == 0) {
+ String *thisfragment = Getattr(kw, "value");
+ String *kwtype = Getattr(kw, "type");
+ if (!fragment) {
+ /* First fragment found; it should remain in the list */
+ fragment = thisfragment;
+ prev_kw = kw;
+ } else {
+ /* Concatenate to previously found fragment */
+ Printv(fragment, ",", thisfragment, NULL);
+ reattach_kw = prev_kw;
+ }
+ if (kwtype) {
+ String *mangle = Swig_string_mangle(kwtype);
+ Append(fragment, mangle);
+ Delete(mangle);
+ /* Remove 'type' from kwargs so it's not duplicated later */
+ Setattr(kw, "type", NULL);
+ }
+ } else {
+ /* Not a fragment */
+ if (reattach_kw) {
+ /* Update linked list to remove duplicate fragment */
+ DohIncref(kw);
+ set_nextSibling(reattach_kw, kw);
+ set_previousSibling(kw, reattach_kw);
+ Delete(reattach_kw);
+ reattach_kw = NULL;
+ }
+ prev_kw = kw;
+ }
+ kw = next_kw;
+ }
+ if (reattach_kw) {
+ /* Update linked list to remove duplicate fragment */
+ set_nextSibling(reattach_kw, kw);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_lookup()
+ *
+ * Attach one or more typemaps to a node and optionally generate the typemap contents
+ * into the wrapper.
+ *
+ * Looks for a typemap matching the given type and name and attaches the typemap code
+ * and any typemap attributes to the provided node.
+ *
+ * The node should contain the "type" and "name" attributes for the typemap match on.
+ * input. The typemap code and typemap attribute values are attached onto the node
+ * prefixed with "tmap:". For example with tmap_method="in", the typemap code can be retrieved
+ * with a call to Getattr(node, "tmap:in") (this is also the string returned) and the
+ * "noblock" attribute can be retrieved with a call to Getattr(node, "tmap:in:noblock").
+ *
+ * tmap_method - typemap method, eg "in", "out", "newfree"
+ * node - the node to attach the typemap and typemap attributes to
+ * lname - name of variable to substitute $1, $2 etc for
+ * f - wrapper code to generate into if non null
+ * actioncode - code to generate into f before the out typemap code, unless
+ * the optimal attribute is set in the out typemap in which case
+ * $1 in the out typemap will be replaced by the code in actioncode.
+ * ----------------------------------------------------------------------------- */
+
+static String *Swig_typemap_lookup_impl(const_String_or_char_ptr tmap_method, Node *node, const_String_or_char_ptr lname, Wrapper *f, String *actioncode) {
+ SwigType *type;
+ SwigType *mtype = 0;
+ String *pname;
+ String *qpname = 0;
+ String *noscope_pname = 0;
+ Hash *tm = 0;
+ String *s = 0;
+ String *sdef = 0;
+ String *warning = 0;
+ ParmList *locals;
+ ParmList *kw;
+ char temp[256];
+ String *symname;
+ String *cname = 0;
+ String *clname = 0;
+ char *cmethod = Char(tmap_method);
+ int optimal_attribute = 0;
+ int optimal_substitution = 0;
+ int delete_optimal_attribute = 0;
+ int num_substitutions = 0;
+ SwigType *matchtype = 0;
+
+ type = Getattr(node, "type");
+ if (!type)
+ return sdef;
+
+ /* Special hook (hack!). Check for the 'ref' feature and add code it contains to any 'newfree' typemap code.
+ * We could choose to put this hook into a number of different typemaps, not necessarily 'newfree'...
+ * Rather confusingly 'newfree' is used to release memory and the 'ref' feature is used to add in memory references - yuck! */
+ if (Cmp(tmap_method, "newfree") == 0) {
+ String *base = SwigType_base(type);
+ Node *typenode = Swig_symbol_clookup(base, 0);
+ if (typenode)
+ sdef = Swig_ref_call(typenode, lname);
+ Delete(base);
+ }
+
+ pname = Getattr(node, "name");
+ noscope_pname = Copy(pname);
+
+ if (pname && Getattr(node, "sym:symtab")) {
+ /* Add on a qualified name search for any symbol in the symbol table, for example:
+ * struct Foo {
+ * int *foo(int bar) -> Foo::foo
+ * };
+ * Note that if node is a parameter (Parm *) then there will be no symbol table attached to the Parm *.
+ */
+ String *qsn;
+ if (Swig_scopename_check(pname)) {
+ /* sometimes pname is qualified, so we remove all the scope for the lookup */
+ Delete(noscope_pname);
+ noscope_pname = Swig_scopename_last(pname);
+ /*
+ Printf(stdout, "Removed scope: %s => %s\n", pname, noscope_pname);
+ */
+ }
+ qsn = Swig_symbol_qualified(node);
+ if (qsn && Len(qsn)) {
+ qpname = NewStringf("%s::%s", qsn, noscope_pname);
+ Delete(qsn);
+ }
+ }
+
+ tm = typemap_search(tmap_method, type, noscope_pname, qpname, &mtype, node);
+ if (typemap_search_debug)
+ debug_search_result_display(tm);
+ if (typemaps_used_debug && tm) {
+ String *typestr = SwigType_str(type, qpname ? qpname : pname);
+ Swig_diagnostic(Getfile(node), Getline(node), "Typemap for %s (%s) : %%%s\n", typestr, tmap_method, Getattr(tm, "source"));
+ assert(Getfile(node) && Len(Getfile(node)) > 0); /* Missing file and line numbering information */
+ Delete(typestr);
+ }
+
+ Delete(qpname);
+ qpname = 0;
+ Delete(noscope_pname);
+ noscope_pname = 0;
+
+ if (!tm)
+ return sdef;
+
+ s = Getattr(tm, "code");
+ if (!s)
+ return sdef;
+
+ /* Empty typemap. No match */
+ if (Cmp(s, "pass") == 0)
+ return sdef;
+
+ s = Copy(s); /* Make a local copy of the typemap code */
+
+ /* Look in the "out" typemap for the "optimal" attribute */
+ if (Cmp(cmethod, "out") == 0) {
+ kw = Getattr(tm, "kwargs");
+ while (kw) {
+ if (Cmp(Getattr(kw, "name"), "optimal") == 0) {
+ optimal_attribute = GetFlag(kw, "value");
+ break;
+ }
+ kw = nextSibling(kw);
+ }
+ }
+
+ if (optimal_attribute) {
+ /* Note: "out" typemap is the only typemap that will have the "optimal" attribute set.
+ * If f and actioncode are NULL, then the caller is just looking to attach the "out" attributes
+ * ie, not use the typemap code, otherwise both f and actioncode must be non null. */
+ if (actioncode) {
+ const String *result_equals = NewStringf("%s = ", Swig_cresult_name());
+ /* check that the code in the typemap can be used in this optimal way.
+ * The code should be in the form "result = ...;\n". We need to extract
+ * the "..." part. This may not be possible for various reasons, eg
+ * code added by %exception. This optimal code generation is bit of a
+ * hack and circumvents the normal requirement for a temporary variable
+ * to hold the result returned from a wrapped function call.
+ */
+ if (Strncmp(actioncode, result_equals, Len(result_equals)) == 0 &&
+ Strchr(actioncode, ';') == Char(actioncode) + Len(actioncode) - 2 &&
+ Char(actioncode)[Len(actioncode) - 1] == '\n') {
+ clname = NewStringWithSize(Char(actioncode) + Len(result_equals),
+ Len(actioncode) - Len(result_equals) - 2);
+ lname = clname;
+ actioncode = 0;
+ optimal_substitution = 1;
+ } else {
+ Swig_warning(WARN_TYPEMAP_OUT_OPTIMAL_IGNORED, Getfile(node), Getline(node), "Method %s usage of the optimal attribute ignored\n", Swig_name_decl(node));
+ Swig_warning(WARN_TYPEMAP_OUT_OPTIMAL_IGNORED, Getfile(s), Getline(s), "in the out typemap as the following cannot be used to generate optimal code: %s\n", actioncode);
+ delete_optimal_attribute = 1;
+ }
+ } else {
+ assert(!f);
+ }
+ }
+
+ if (actioncode) {
+ assert(f);
+ Append(f->code, actioncode);
+ }
+
+ /* emit local variables declared in typemap, eg emit declarations for aa and bb in:
+ * %typemap(in) foo (int aa, int bb) "..." */
+ locals = Getattr(tm, "locals");
+ if (locals)
+ locals = CopyParmList(locals);
+
+ if (pname) {
+ if (SwigType_istemplate(pname)) {
+ cname = SwigType_namestr(pname);
+ pname = cname;
+ }
+ }
+ if (SwigType_istemplate((char *) lname)) {
+ clname = SwigType_namestr((char *) lname);
+ lname = clname;
+ }
+
+ matchtype = mtype && SwigType_isarray(mtype) ? mtype : type;
+ num_substitutions = typemap_replace_vars(s, locals, matchtype, type, pname, (char *) lname, 1);
+ if (optimal_substitution && num_substitutions > 1) {
+ Swig_warning(WARN_TYPEMAP_OUT_OPTIMAL_MULTIPLE, Getfile(node), Getline(node), "Multiple calls to %s might be generated due to\n", Swig_name_decl(node));
+ Swig_warning(WARN_TYPEMAP_OUT_OPTIMAL_MULTIPLE, Getfile(s), Getline(s), "optimal attribute usage in the out typemap.\n");
+ }
+
+ if (locals && f) {
+ typemap_locals(s, locals, f, -1);
+ }
+
+ {
+ ParmList *parm_sublist = NewParmWithoutFileLineInfo(type, pname);
+ Setattr(parm_sublist, "lname", lname);
+ replace_embedded_typemap(s, parm_sublist, f, tm);
+ Delete(parm_sublist);
+ }
+
+ /* Attach kwargs - ie the typemap attributes */
+ kw = Getattr(tm, "kwargs");
+ typemap_merge_fragment_kwargs(kw);
+ while (kw) {
+ String *value = Copy(Getattr(kw, "value"));
+ String *kwtype = Getattr(kw, "type");
+ char *ckwname = Char(Getattr(kw, "name"));
+ {
+ /* Expand special variables in typemap attributes. */
+ SwigType *ptype = Getattr(node, "type");
+ String *pname = Getattr(node, "name");
+ SwigType *mtype = Getattr(node, "tmap:match");
+ SwigType *matchtype = mtype ? mtype : ptype;
+ ParmList *parm_sublist;
+ typemap_replace_vars(value, NULL, matchtype, ptype, pname, (char *)lname, 1);
+
+ /* Expand special variable macros (embedded typemaps) in typemap attributes. */
+ parm_sublist = NewParmWithoutFileLineInfo(ptype, pname);
+ Setattr(parm_sublist, "lname", lname);
+ replace_embedded_typemap(value, parm_sublist, NULL, tm);
+ Delete(parm_sublist);
+ }
+ if (kwtype) {
+ String *mangle = Swig_string_mangle(kwtype);
+ Append(value, mangle);
+ Delete(mangle);
+ }
+ sprintf(temp, "%s:%s", cmethod, ckwname);
+ Setattr(node, typemap_method_name(temp), value);
+ Delete(value);
+ kw = nextSibling(kw);
+ }
+
+ if (delete_optimal_attribute)
+ Delattr(node, "tmap:out:optimal");
+
+ Replace(s, "$name", pname, DOH_REPLACE_ANY);
+
+ symname = Getattr(node, "sym:name");
+ if (symname)
+ Replace(s, "$symname", symname, DOH_REPLACE_ANY);
+
+ Setattr(node, typemap_method_name(tmap_method), s);
+ if (locals) {
+ sprintf(temp, "%s:locals", cmethod);
+ Setattr(node, typemap_method_name(temp), locals);
+ Delete(locals);
+ }
+
+ if (Checkattr(tm, "type", "SWIGTYPE")) {
+ sprintf(temp, "%s:SWIGTYPE", cmethod);
+ Setattr(node, typemap_method_name(temp), "1");
+ }
+
+ /* Print warnings, if any */
+ warning = typemap_warn(cmethod, node);
+ if (warning) {
+ typemap_replace_vars(warning, 0, matchtype, type, pname, (char *) lname, 1);
+ Replace(warning, "$name", pname, DOH_REPLACE_ANY);
+ if (symname)
+ Replace(warning, "$symname", symname, DOH_REPLACE_ANY);
+ Swig_warning(0, Getfile(node), Getline(node), "%s\n", warning);
+ Delete(warning);
+ }
+
+ /* Look for code fragments */
+ {
+ String *fragment;
+ sprintf(temp, "%s:fragment", cmethod);
+ fragment = Getattr(node, typemap_method_name(temp));
+ if (fragment) {
+ String *fname = Copy(fragment);
+ Setfile(fname, Getfile(node));
+ Setline(fname, Getline(node));
+ Swig_fragment_emit(fname);
+ Delete(fname);
+ }
+ }
+
+ Delete(cname);
+ Delete(clname);
+ Delete(mtype);
+ if (sdef) { /* put 'ref' and 'newfree' codes together */
+ String *p = NewStringf("%s\n%s", sdef, s);
+ Delete(s);
+ Delete(sdef);
+ s = p;
+ }
+ Delete(actioncode);
+ return s;
+}
+
+String *Swig_typemap_lookup_out(const_String_or_char_ptr tmap_method, Node *node, const_String_or_char_ptr lname, Wrapper *f, String *actioncode) {
+ assert(actioncode);
+ assert(Cmp(tmap_method, "out") == 0);
+ return Swig_typemap_lookup_impl(tmap_method, node, lname, f, actioncode);
+}
+
+String *Swig_typemap_lookup(const_String_or_char_ptr tmap_method, Node *node, const_String_or_char_ptr lname, Wrapper *f) {
+ return Swig_typemap_lookup_impl(tmap_method, node, lname, f, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * typemap_attach_kwargs()
+ *
+ * If this hash (tm) contains a linked list of parameters under its "kwargs"
+ * attribute, add keys for each of those named keyword arguments to this
+ * parameter for later use.
+ * For example, attach the typemap attributes to firstp (first parameter in parameter list):
+ * %typemap(in, foo="xyz") ...
+ * A new attribute called "tmap:in:foo" with value "xyz" is attached to firstp.
+ * Also expands special variables and special variable macros in the typemap attributes.
+ * ----------------------------------------------------------------------------- */
+
+static void typemap_attach_kwargs(Hash *tm, const_String_or_char_ptr tmap_method, Parm *firstp, int nmatch) {
+ String *temp = NewStringEmpty();
+ Parm *kw = Getattr(tm, "kwargs");
+ typemap_merge_fragment_kwargs(kw);
+ while (kw) {
+ String *value = Copy(Getattr(kw, "value"));
+ String *type = Getattr(kw, "type");
+ int i;
+ Parm *p = firstp;
+ /* Expand special variables */
+ for (i = 0; i < nmatch; i++) {
+ SwigType *type = Getattr(p, "type");
+ String *pname = Getattr(p, "name");
+ String *lname = Getattr(p, "lname");
+ SwigType *mtype = Getattr(p, "tmap:match");
+ SwigType *matchtype = mtype ? mtype : type;
+ typemap_replace_vars(value, NULL, matchtype, type, pname, lname, i + 1);
+ p = nextSibling(p);
+ }
+
+ /* Expand special variable macros (embedded typemaps).
+ * Special variable are expanded first above as they might be used in the special variable macros.
+ * For example: $typemap(imtype, $2_type). */
+ p = firstp;
+ for (i = 0; i < nmatch; i++) {
+ SwigType *type = Getattr(p, "type");
+ String *pname = Getattr(p, "name");
+ String *lname = Getattr(p, "lname");
+ ParmList *parm_sublist = NewParmWithoutFileLineInfo(type, pname);
+ Setattr(parm_sublist, "lname", lname);
+ replace_embedded_typemap(value, parm_sublist, NULL, tm);
+ p = nextSibling(p);
+ }
+ if (type) {
+ Hash *v = NewHash();
+ Setattr(v, "type", type);
+ Setattr(v, "value", value);
+ Delete(value);
+ value = v;
+ }
+ Clear(temp);
+ Printf(temp, "%s:%s", tmap_method, Getattr(kw, "name"));
+ Setattr(firstp, typemap_method_name(temp), value);
+ Delete(value);
+ kw = nextSibling(kw);
+ }
+ Clear(temp);
+ Printf(temp, "%s:match_type", tmap_method);
+ Setattr(firstp, typemap_method_name(temp), Getattr(tm, "type"));
+ Delete(temp);
+}
+
+static void typemap_emit_code_fragments(const_String_or_char_ptr tmap_method, Parm *p) {
+ String *temp = NewStringf("%s:fragment", tmap_method);
+ String *f = Getattr(p, typemap_method_name(temp));
+ if (f) {
+ String *fname = Copy(f);
+ Setfile(fname, Getfile(p));
+ Setline(fname, Getline(p));
+ Swig_fragment_emit(fname);
+ Delete(fname);
+ }
+ Delete(temp);
+}
+
+static String *typemap_get_option(Hash *tm, const_String_or_char_ptr name) {
+ Parm *kw = Getattr(tm, "kwargs");
+ while (kw) {
+ String *kname = Getattr(kw, "name");
+ if (Equal(kname, name)) {
+ return Getattr(kw, "value");
+ }
+ kw = nextSibling(kw);
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_attach_parms()
+ *
+ * Given a parameter list, this function attaches all of the typemaps and typemap
+ * attributes to the parameter for each type in the parameter list.
+ *
+ * This function basically provides the typemap code and typemap attribute values as
+ * attributes on each parameter prefixed with "tmap:". For example with tmap_method="in", the typemap
+ * code can be retrieved for the first parameter with a call to Getattr(parm, "tmap:in")
+ * and the "numinputs" attribute can be retrieved with a call to Getattr(parm, "tmap:in:numinputs").
+ *
+ * tmap_method - typemap method, eg "in", "out", "newfree"
+ * parms - parameter list to attach each typemap and all typemap attributes
+ * f - wrapper code to generate into if non null
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_attach_parms(const_String_or_char_ptr tmap_method, ParmList *parms, Wrapper *f) {
+ Parm *p, *firstp;
+ Hash *tm;
+ int nmatch = 0;
+ int i;
+ String *s;
+ String *warning = 0;
+ ParmList *locals;
+ int argnum = 0;
+ char temp[256];
+ char *cmethod = Char(tmap_method);
+ String *kwmatch = 0;
+ p = parms;
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_typemap_attach_parms: %s\n", tmap_method);
+#endif
+
+ while (p) {
+ argnum++;
+ nmatch = 0;
+#ifdef SWIG_DEBUG
+ Printf(stdout, "parms: %s %s %s\n", tmap_method, Getattr(p, "name"), Getattr(p, "type"));
+#endif
+ tm = typemap_search_multi(tmap_method, p, &nmatch);
+#ifdef SWIG_DEBUG
+ if (tm)
+ Printf(stdout, "found: %s\n", tm);
+#endif
+ if (!tm) {
+ p = nextSibling(p);
+ continue;
+ }
+ /*
+ Check if the typemap requires to match the type of another
+ typemap, for example:
+
+ %typemap(in) SWIGTYPE * (int var) {...}
+ %typemap(freearg,match="in") SWIGTYPE * {if (var$argnum) ...}
+
+ here, the freearg typemap requires the "in" typemap to match,
+ or the 'var$argnum' variable will not exist.
+ */
+ kwmatch = typemap_get_option(tm, "match");
+ if (kwmatch) {
+ String *tmname = NewStringf("tmap:%s", kwmatch);
+ String *tmin = Getattr(p, tmname);
+ Delete(tmname);
+#ifdef SWIG_DEBUG
+ if (tm)
+ Printf(stdout, "matching: %s\n", kwmatch);
+#endif
+ if (tmin) {
+ String *tmninp = NewStringf("tmap:%s:numinputs", kwmatch);
+ String *ninp = Getattr(p, tmninp);
+ Delete(tmninp);
+ if (ninp && Equal(ninp, "0")) {
+ p = nextSibling(p);
+ continue;
+ } else {
+ SwigType *typetm = Getattr(tm, "type");
+ String *temp = NewStringf("tmap:%s:match_type", kwmatch);
+ SwigType *typein = Getattr(p, temp);
+ Delete(temp);
+ if (!Equal(typein, typetm)) {
+ p = nextSibling(p);
+ continue;
+ } else {
+ int nnmatch;
+ Hash *tmapin = typemap_search_multi(kwmatch, p, &nnmatch);
+ String *tmname = Getattr(tm, "pname");
+ String *tnname = Getattr(tmapin, "pname");
+ if (!(tmname && tnname && Equal(tmname, tnname)) && !(!tmname && !tnname)) {
+ p = nextSibling(p);
+ continue;
+ }
+ }
+
+ }
+ } else {
+ p = nextSibling(p);
+ continue;
+ }
+ }
+
+ s = Getattr(tm, "code");
+ if (!s) {
+ p = nextSibling(p);
+ continue;
+ }
+#ifdef SWIG_DEBUG
+ if (s)
+ Printf(stdout, "code: %s\n", s);
+#endif
+
+ /* Empty typemap. No match */
+ if (Cmp(s, "pass") == 0) {
+ p = nextSibling(p);
+ continue;
+ }
+
+ s = Copy(s);
+ locals = Getattr(tm, "locals");
+ if (locals)
+ locals = CopyParmList(locals);
+ firstp = p;
+#ifdef SWIG_DEBUG
+ Printf(stdout, "nmatch: %d\n", nmatch);
+#endif
+ for (i = 0; i < nmatch; i++) {
+ SwigType *type = Getattr(p, "type");
+ String *pname = Getattr(p, "name");
+ String *lname = Getattr(p, "lname");
+ SwigType *mtype = Getattr(p, "tmap:match");
+ SwigType *matchtype = mtype ? mtype : type;
+
+ typemap_replace_vars(s, locals, matchtype, type, pname, lname, i + 1);
+ if (mtype)
+ Delattr(p, "tmap:match");
+
+ if (Checkattr(tm, "type", "SWIGTYPE")) {
+ sprintf(temp, "%s:SWIGTYPE", cmethod);
+ Setattr(p, typemap_method_name(temp), "1");
+ }
+ p = nextSibling(p);
+ }
+
+ if (locals && f) {
+ typemap_locals(s, locals, f, argnum);
+ }
+
+ replace_embedded_typemap(s, firstp, f, tm);
+
+ /* Attach attributes to object */
+#ifdef SWIG_DEBUG
+ Printf(stdout, "attach: %s %s %s\n", Getattr(firstp, "name"), typemap_method_name(tmap_method), s);
+#endif
+ Setattr(firstp, typemap_method_name(tmap_method), s); /* Code object */
+
+ if (locals) {
+ sprintf(temp, "%s:locals", cmethod);
+ Setattr(firstp, typemap_method_name(temp), locals);
+ Delete(locals);
+ }
+
+ /* Attach a link to the next parameter. Needed for multimaps */
+ sprintf(temp, "%s:next", cmethod);
+ Setattr(firstp, typemap_method_name(temp), p);
+
+ /* Attach kwargs */
+ typemap_attach_kwargs(tm, tmap_method, firstp, nmatch);
+
+ /* Replace the argument number */
+ sprintf(temp, "%d", argnum);
+ Replace(s, "$argnum", temp, DOH_REPLACE_ANY);
+
+ /* Print warnings, if any */
+ warning = typemap_warn(tmap_method, firstp);
+ if (warning) {
+ SwigType *type = Getattr(firstp, "type");
+ String *pname = Getattr(firstp, "name");
+ String *lname = Getattr(firstp, "lname");
+ SwigType *mtype = Getattr(firstp, "tmap:match");
+ SwigType *matchtype = mtype ? mtype : type;
+ typemap_replace_vars(warning, 0, matchtype, type, pname, lname, 1);
+ Replace(warning, "$argnum", temp, DOH_REPLACE_ANY);
+ Swig_warning(0, Getfile(firstp), Getline(firstp), "%s\n", warning);
+ Delete(warning);
+ }
+
+ /* Look for code fragments */
+ typemap_emit_code_fragments(tmap_method, firstp);
+
+ /* increase argnum to consider numinputs */
+ argnum += nmatch - 1;
+ Delete(s);
+#ifdef SWIG_DEBUG
+ Printf(stdout, "res: %s %s %s\n", Getattr(firstp, "name"), typemap_method_name(tmap_method), Getattr(firstp, typemap_method_name(tmap_method)));
+#endif
+
+ }
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_typemap_attach_parms: end\n");
+#endif
+
+}
+
+/* Splits the arguments of an embedded typemap */
+static List *split_embedded_typemap(String *s) {
+ List *args = 0;
+ char *c, *start;
+ int level = 0;
+ int angle_level = 0;
+ int leading = 1;
+
+ args = NewList();
+ c = strchr(Char(s), '(');
+ assert(c);
+ c++;
+
+ start = c;
+ while (*c) {
+ if (*c == '\"') {
+ c++;
+ while (*c) {
+ if (*c == '\\') {
+ c++;
+ } else {
+ if (*c == '\"')
+ break;
+ }
+ c++;
+ }
+ }
+ if ((level == 0) && angle_level == 0 && ((*c == ',') || (*c == ')'))) {
+ String *tmp = NewStringWithSize(start, (int)(c - start));
+ Append(args, tmp);
+ Delete(tmp);
+ start = c + 1;
+ leading = 1;
+ if (*c == ')')
+ break;
+ c++;
+ continue;
+ }
+ if (*c == '(')
+ level++;
+ if (*c == ')')
+ level--;
+ if (*c == '<')
+ angle_level++;
+ if (*c == '>')
+ angle_level--;
+ if (isspace((int) *c) && leading)
+ start++;
+ if (!isspace((int) *c))
+ leading = 0;
+ c++;
+ }
+ return args;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_replace_embedded_typemap()
+ *
+ * For special variable macro $typemap(...) expansion outside of typemaps.
+ * Only limited usage works as most typemap special variables ($1, $input etc)
+ * are not expanded correctly outside of typemaps.
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_replace_embedded_typemap(String *s, Node *file_line_node) {
+ Setfile(s, Getfile(file_line_node));
+ Setline(s, Getline(file_line_node));
+ Replaceall(s, "$typemap", "$TYPEMAP");
+ replace_embedded_typemap(s, 0, 0, file_line_node);
+}
+
+/* -----------------------------------------------------------------------------
+ * replace_embedded_typemap()
+ *
+ * This function replaces the special variable macro $typemap(...) with typemap
+ * code. The general form of $typemap is as follows:
+ *
+ * $typemap(method, typelist, var1=value, var2=value, ...)
+ *
+ * where varx parameters are optional and undocumented; they were used in an earlier version of $typemap.
+ * A search is made using the typemap matching rules of form:
+ *
+ * %typemap(method) typelist {...}
+ *
+ * and if found will substitute in the typemap contents, making appropriate variable replacements.
+ *
+ * For example:
+ * $typemap(in, int) # simple usage matching %typemap(in) int { ... }
+ * $typemap(in, int b) # simple usage matching %typemap(in) int b { ... } or above %typemap
+ * $typemap(in, (Foo<int, bool> a, int b)) # multi-argument typemap matching %typemap(in) (Foo<int, bool> a, int b) {...}
+ * ----------------------------------------------------------------------------- */
+
+static void replace_embedded_typemap(String *s, ParmList *parm_sublist, Wrapper *f, Node *file_line_node) {
+ char *start = 0;
+ while ((start = strstr(Char(s), "$TYPEMAP("))) { /* note $typemap capitalisation to $TYPEMAP hack */
+
+ /* Gather the parameters */
+ char *end = 0, *c;
+ int level = 0;
+ String *dollar_typemap;
+ int syntax_error = 1;
+ c = start;
+ while (*c) {
+ if (*c == '(')
+ level++;
+ if (*c == ')') {
+ level--;
+ if (level == 0) {
+ end = c + 1;
+ break;
+ }
+ }
+ c++;
+ }
+ if (end) {
+ dollar_typemap = NewStringWithSize(start, (int)((end - start)));
+ syntax_error = 0;
+ } else {
+ dollar_typemap = NewStringWithSize(start, (int)((c - start)));
+ }
+
+ if (!syntax_error) {
+ List *l;
+ String *tmap_method;
+ Hash *vars;
+ syntax_error = 1;
+
+ /* Split apart each parameter in $typemap(...) */
+ l = split_embedded_typemap(dollar_typemap);
+
+ if (Len(l) >= 2) {
+ ParmList *to_match_parms;
+ tmap_method = Getitem(l, 0);
+
+ /* the second parameter might contain multiple sub-parameters for multi-argument
+ * typemap matching, so split these parameters apart */
+ to_match_parms = Swig_cparse_parms(Getitem(l, 1), file_line_node);
+ if (to_match_parms) {
+ Parm *p = to_match_parms;
+ Parm *sub_p = parm_sublist;
+ String *empty_string = NewStringEmpty();
+ String *lname = empty_string;
+ while (p) {
+ if (sub_p) {
+ lname = Getattr(sub_p, "lname");
+ sub_p = nextSibling(sub_p);
+ }
+ Setattr(p, "lname", lname);
+ p = nextSibling(p);
+ }
+ Delete(empty_string);
+ }
+
+ /* process optional extra parameters - the variable replacements (undocumented) */
+ vars = NewHash();
+ {
+ int i, ilen;
+ ilen = Len(l);
+ for (i = 2; i < ilen; i++) {
+ String *parm = Getitem(l, i);
+ char *eq = strchr(Char(parm), '=');
+ char *c = Char(parm);
+ if (eq && (eq - c > 0)) {
+ String *name = NewStringWithSize(c, (int)(eq - c));
+ String *value = NewString(eq + 1);
+ Insert(name, 0, "$");
+ Setattr(vars, name, value);
+ } else {
+ to_match_parms = 0; /* error - variable replacement parameters must be of form varname=value */
+ }
+ }
+ }
+
+ /* Perform a typemap search */
+ if (to_match_parms) {
+ static int already_substituting = 0;
+ String *tm;
+ String *attr;
+ int match = 0;
+#ifdef SWIG_DEBUG
+ Printf(stdout, "Swig_typemap_attach_parms: embedded\n");
+#endif
+ if (already_substituting < 10) {
+ char* found_colon;
+ already_substituting++;
+ if ((in_typemap_search_multi == 0) && typemap_search_debug) {
+ String *dtypemap = NewString(dollar_typemap);
+ Replaceall(dtypemap, "$TYPEMAP", "$typemap");
+ Printf(stdout, " Containing: %s\n", dtypemap);
+ Delete(dtypemap);
+ }
+ found_colon = Strchr(tmap_method, ':');
+ if (found_colon) {
+ /* Substitute from a keyword argument to a typemap. Avoid emitting local variables from the attached typemap by passing NULL for the file. */
+ String *temp_tmap_method = NewStringWithSize(Char(tmap_method), (int)(found_colon - Char(tmap_method)));
+ Swig_typemap_attach_parms(temp_tmap_method, to_match_parms, NULL);
+ Delete(temp_tmap_method);
+ } else {
+ Swig_typemap_attach_parms(tmap_method, to_match_parms, f);
+ }
+ already_substituting--;
+
+ /* Look for the typemap code */
+ attr = NewStringf("tmap:%s", tmap_method);
+ tm = Getattr(to_match_parms, attr);
+ if (tm) {
+ Printf(attr, "%s", ":next");
+ /* fail if multi-argument lookup requested in $typemap(...) and the lookup failed */
+ if (!Getattr(to_match_parms, attr)) {
+ /* Replace parameter variables */
+ Iterator ki;
+ for (ki = First(vars); ki.key; ki = Next(ki)) {
+ Replace(tm, ki.key, ki.item, DOH_REPLACE_ANY);
+ }
+ /* offer the target language module the chance to make special variable substitutions */
+ Language_replace_special_variables(tmap_method, tm, to_match_parms);
+ /* finish up - do the substitution */
+ Replace(s, dollar_typemap, tm, DOH_REPLACE_ANY);
+ Delete(tm);
+ match = 1;
+ }
+ }
+
+ if (!match) {
+ String *dtypemap = NewString(dollar_typemap);
+ Replaceall(dtypemap, "$TYPEMAP", "$typemap");
+ Swig_error(Getfile(s), Getline(s), "No typemap found for %s\n", dtypemap);
+ Delete(dtypemap);
+ }
+ Delete(attr);
+ } else {
+ /* Simple recursive call check to prevent infinite recursion - this strategy only allows a limited
+ * number of calls by a embedded typemaps to other embedded typemaps though */
+ String *dtypemap = NewString(dollar_typemap);
+ Replaceall(dtypemap, "$TYPEMAP", "$typemap");
+ Swig_error(Getfile(s), Getline(s), "Likely recursive $typemap calls containing %s. Use -debug-tmsearch to debug.\n", dtypemap);
+ Delete(dtypemap);
+ }
+ syntax_error = 0;
+ }
+ Delete(vars);
+ }
+ Delete(l);
+ }
+
+ if (syntax_error) {
+ String *dtypemap = NewString(dollar_typemap);
+ Replaceall(dtypemap, "$TYPEMAP", "$typemap");
+ Swig_error(Getfile(s), Getline(s), "Syntax error in: %s\n", dtypemap);
+ Delete(dtypemap);
+ }
+ Replace(s, dollar_typemap, "<error in embedded typemap>", DOH_REPLACE_ANY);
+ Delete(dollar_typemap);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_debug()
+ *
+ * Display all typemaps
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_debug(void) {
+ int nesting_level = 2;
+ Printf(stdout, "---[ typemaps ]--------------------------------------------------------------\n");
+ Swig_print(typemaps, nesting_level);
+ Printf(stdout, "-----------------------------------------------------------------------------\n");
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_search_debug_set()
+ *
+ * Turn on typemap searching debug display
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_search_debug_set(void) {
+ typemap_search_debug = 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_used_debug_set()
+ *
+ * Turn on typemaps used debug display
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_used_debug_set(void) {
+ typemaps_used_debug = 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Swig_typemap_register_debug_set()
+ *
+ * Turn on typemaps used debug display
+ * ----------------------------------------------------------------------------- */
+
+void Swig_typemap_register_debug_set(void) {
+ typemap_register_debug = 1;
+}
+
diff --git a/contrib/tools/swig/Source/Swig/typeobj.c b/contrib/tools/swig/Source/Swig/typeobj.c
new file mode 100644
index 0000000000..8cd2e28e98
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/typeobj.c
@@ -0,0 +1,1367 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * typeobj.c
+ *
+ * This file provides functions for constructing, manipulating, and testing
+ * type objects. Type objects are merely the raw low-level representation
+ * of C++ types. They do not incorporate high-level type system features
+ * like typedef, namespaces, etc.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include <ctype.h>
+#include <limits.h>
+
+/* -----------------------------------------------------------------------------
+ * Synopsis
+ *
+ * This file provides a collection of low-level functions for constructing and
+ * manipulating C++ data types. In SWIG, C++ datatypes are encoded as simple
+ * text strings. This representation is compact, easy to debug, and easy to read.
+ *
+ * General idea:
+ *
+ * Types are represented by a base type (e.g., "int") and a collection of
+ * type operators applied to the base (e.g., pointers, arrays, etc...).
+ *
+ * Encoding:
+ *
+ * Types are encoded as strings of type constructors such as follows:
+ *
+ * String Encoding C Example
+ * --------------- ---------
+ * p.p.int int **
+ * a(300).a(400).int int [300][400]
+ * p.q(const).char char const *
+ *
+ * All type constructors are denoted by a trailing '.':
+ *
+ * 'p.' = Pointer (*)
+ * 'r.' = Reference or ref-qualifier (&)
+ * 'z.' = Rvalue reference or ref-qualifier (&&)
+ * 'a(n).' = Array of size n [n]
+ * 'f(..,..).' = Function with arguments (args)
+ * 'q(str).' = Qualifier, such as const or volatile (cv-qualifier)
+ * 'm(cls).' = Pointer to member (cls::*)
+ *
+ * The complete type representation for varargs is:
+ * 'v(...)'
+ *
+ * The encoding follows the order that you might describe a type in words.
+ * For example "p.a(200).int" is "A pointer to array of int's" and
+ * "p.q(const).char" is "a pointer to a const char".
+ *
+ * This representation of types is fairly convenient because ordinary string
+ * operations can be used for type manipulation. For example, a type could be
+ * formed by combining two strings such as the following:
+ *
+ * "p.p." + "a(400).int" = "p.p.a(400).int"
+ *
+ * For C++, typenames may be parameterized using <(...)>. Here are some
+ * examples:
+ *
+ * String Encoding C++ Example
+ * --------------- ------------
+ * p.vector<(int)> vector<int> *
+ * r.foo<(int,p.double)> foo<int,double *> &
+ *
+ * Contents of this file:
+ *
+ * Most of this functions in this file pertain to the low-level manipulation
+ * of type objects. There are constructor functions like this:
+ *
+ * SwigType_add_pointer()
+ * SwigType_add_reference()
+ * SwigType_add_rvalue_reference()
+ * SwigType_add_array()
+ *
+ * These are used to build new types. There are also functions to undo these
+ * operations. For example:
+ *
+ * SwigType_del_pointer()
+ * SwigType_del_reference()
+ * SwigType_del_rvalue_reference()
+ * SwigType_del_array()
+ *
+ * In addition, there are query functions
+ *
+ * SwigType_ispointer()
+ * SwigType_isreference()
+ * SwigType_isrvalue_reference()
+ * SwigType_isarray()
+ *
+ * Finally, there are some data extraction functions that can be used to
+ * extract array dimensions, template arguments, and so forth.
+ *
+ * It is very important for developers to realize that the functions in this
+ * module do *NOT* incorporate higher-level type system features like typedef.
+ * For example, you could have C code like this:
+ *
+ * typedef int *intptr;
+ *
+ * In this case, a SwigType of type 'intptr' will be treated as a simple type and
+ * functions like SwigType_ispointer() will evaluate as false. It is strongly
+ * advised that developers use the TypeSys_* interface to check types in a more
+ * reliable manner.
+ * ----------------------------------------------------------------------------- */
+
+
+/* -----------------------------------------------------------------------------
+ * NewSwigType()
+ *
+ * Constructs a new type object. Eventually, it would be nice for this function
+ * to accept an initial value in the form a C/C++ abstract type (currently unimplemented).
+ * ----------------------------------------------------------------------------- */
+
+#ifdef NEW
+SwigType *NewSwigType(const_String_or_char_ptr initial) {
+ return NewString(initial);
+}
+
+#endif
+
+/* The next few functions are utility functions used in the construction and
+ management of types */
+
+/* -----------------------------------------------------------------------------
+ * static element_size()
+ *
+ * This utility function finds the size of a single type element in a type string.
+ * Type elements are always delimited by periods, but may be nested with
+ * parentheses. A nested element is always handled as a single item.
+ *
+ * Returns the integer size of the element (which can be used to extract a
+ * substring, to chop the element off, or for other purposes).
+ * ----------------------------------------------------------------------------- */
+
+static int element_size(char *c) {
+ int nparen;
+ char *s = c;
+ while (*c) {
+ if (*c == '.') {
+ c++;
+ return (int) (c - s);
+ } else if (*c == '(') {
+ nparen = 1;
+ c++;
+ while (*c) {
+ if (*c == '(')
+ nparen++;
+ if (*c == ')') {
+ nparen--;
+ if (nparen == 0)
+ break;
+ }
+ c++;
+ }
+ }
+ if (*c)
+ c++;
+ }
+ return (int) (c - s);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_del_element()
+ *
+ * Deletes one type element from the type.
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_del_element(SwigType *t) {
+ int sz = element_size(Char(t));
+ Delslice(t, 0, sz);
+ return t;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_pop()
+ *
+ * Pop one type element off the type.
+ * For example:
+ * t in: q(const).p.Integer
+ * t out: p.Integer
+ * result: q(const).
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_pop(SwigType *t) {
+ SwigType *result;
+ char *c;
+ int sz;
+
+ c = Char(t);
+ if (!*c)
+ return 0;
+
+ sz = element_size(c);
+ result = NewStringWithSize(c, sz);
+ Delslice(t, 0, sz);
+ c = Char(t);
+ if (*c == '.') {
+ Delitem(t, 0);
+ }
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_last()
+ *
+ * Return the last element of the given (partial) type.
+ * For example:
+ * t: q(const).p.
+ * result: p.
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_last(SwigType *t) {
+ SwigType *result;
+ char *c;
+ char *last;
+ int sz = 0;
+
+ if (!t)
+ return 0;
+
+ /* Find the last element */
+ c = Char(t);
+ last = 0;
+ while (*c) {
+ last = c;
+ sz = element_size(c);
+ c = c + sz;
+ if (*c == '.') {
+ c++;
+ sz++;
+ }
+ }
+
+ /* Extract the last element */
+ if (last) {
+ result = NewStringWithSize(last, sz);
+ } else {
+ result = 0;
+ }
+
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_parm()
+ *
+ * Returns the parameter of an operator as a string
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_parm(const SwigType *t) {
+ char *start, *c;
+ int nparens = 0;
+
+ c = Char(t);
+ while (*c && (*c != '(') && (*c != '.'))
+ c++;
+ if (!*c || (*c == '.'))
+ return 0;
+ c++;
+ start = c;
+ while (*c) {
+ if (*c == ')') {
+ if (nparens == 0)
+ break;
+ nparens--;
+ } else if (*c == '(') {
+ nparens++;
+ }
+ c++;
+ }
+ return NewStringWithSize(start, (int) (c - start));
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_split()
+ *
+ * Splits a type into its component parts and returns a list of string.
+ * ----------------------------------------------------------------------------- */
+
+List *SwigType_split(const SwigType *t) {
+ String *item;
+ List *list;
+ char *c;
+ int len;
+
+ c = Char(t);
+ list = NewList();
+ while (*c) {
+ len = element_size(c);
+ item = NewStringWithSize(c, len);
+ Append(list, item);
+ Delete(item);
+ c = c + len;
+ if (*c == '.')
+ c++;
+ }
+ return list;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_parmlist()
+ *
+ * Splits a comma separated list of parameters into its component parts
+ * The input is expected to contain the parameter list within () brackets
+ * Returns 0 if no argument list in the input, ie there are no round brackets ()
+ * Returns an empty List if there are no parameters in the () brackets
+ * For example:
+ *
+ * Foo(std::string,p.f().Bar<(int,double)>)
+ *
+ * returns 2 elements in the list:
+ * std::string
+ * p.f().Bar<(int,double)>
+ * ----------------------------------------------------------------------------- */
+
+List *SwigType_parmlist(const String *p) {
+ String *item = 0;
+ List *list;
+ char *c;
+ char *itemstart;
+ int size;
+
+ assert(p);
+ c = Char(p);
+ while (*c && (*c != '(') && (*c != '.'))
+ c++;
+ if (!*c)
+ return 0;
+ assert(*c != '.'); /* p is expected to contain sub elements of a type */
+ c++;
+ list = NewList();
+ itemstart = c;
+ while (*c) {
+ if (*c == ',') {
+ size = (int) (c - itemstart);
+ item = NewStringWithSize(itemstart, size);
+ Append(list, item);
+ Delete(item);
+ itemstart = c + 1;
+ } else if (*c == '(') {
+ int nparens = 1;
+ c++;
+ while (*c) {
+ if (*c == '(')
+ nparens++;
+ if (*c == ')') {
+ nparens--;
+ if (nparens == 0)
+ break;
+ }
+ c++;
+ }
+ } else if (*c == ')') {
+ break;
+ }
+ if (*c)
+ c++;
+ }
+ size = (int) (c - itemstart);
+ if (size > 0) {
+ item = NewStringWithSize(itemstart, size);
+ Append(list, item);
+ }
+ Delete(item);
+ return list;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pointers
+ *
+ * SwigType_add_pointer()
+ * SwigType_del_pointer()
+ * SwigType_ispointer()
+ *
+ * Add, remove, and test if a type is a pointer. The deletion and query
+ * functions take into account qualifiers (if any).
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_add_pointer(SwigType *t) {
+ Insert(t, 0, "p.");
+ return t;
+}
+
+SwigType *SwigType_del_pointer(SwigType *t) {
+ char *c, *s;
+ c = Char(t);
+ s = c;
+ /* Skip qualifiers, if any */
+ if (strncmp(c, "q(", 2) == 0) {
+ c = strchr(c, '.');
+ assert(c);
+ c++;
+ }
+ if (strncmp(c, "p.", 2)) {
+ printf("Fatal error: SwigType_del_pointer applied to non-pointer.\n");
+ Exit(EXIT_FAILURE);
+ }
+ Delslice(t, 0, (int)((c - s) + 2));
+ return t;
+}
+
+int SwigType_ispointer(const SwigType *t) {
+ char *c;
+ if (!t)
+ return 0;
+ c = Char(t);
+ /* Skip qualifiers, if any */
+ if (strncmp(c, "q(", 2) == 0) {
+ c = strchr(c, '.');
+ if (!c)
+ return 0;
+ c++;
+ }
+ if (strncmp(c, "p.", 2) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * References
+ *
+ * SwigType_add_reference()
+ * SwigType_del_reference()
+ * SwigType_isreference()
+ *
+ * Add, remove, and test if a type is a reference. The deletion and query
+ * functions take into account qualifiers (if any).
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_add_reference(SwigType *t) {
+ Insert(t, 0, "r.");
+ return t;
+}
+
+SwigType *SwigType_del_reference(SwigType *t) {
+ char *c = Char(t);
+ if (strncmp(c, "r.", 2)) {
+ printf("Fatal error: SwigType_del_reference applied to non-reference.\n");
+ Exit(EXIT_FAILURE);
+ }
+ Delslice(t, 0, 2);
+ return t;
+}
+
+int SwigType_isreference(const SwigType *t) {
+ char *c;
+ if (!t)
+ return 0;
+ c = Char(t);
+ if (strncmp(c, "r.", 2) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Rvalue References
+ *
+ * SwigType_add_rvalue_reference()
+ * SwigType_del_rvalue_reference()
+ * SwigType_isrvalue_reference()
+ *
+ * Add, remove, and test if a type is a rvalue reference. The deletion and query
+ * functions take into account qualifiers (if any).
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_add_rvalue_reference(SwigType *t) {
+ Insert(t, 0, "z.");
+ return t;
+}
+
+SwigType *SwigType_del_rvalue_reference(SwigType *t) {
+ char *c = Char(t);
+ if (strncmp(c, "z.", 2)) {
+ fprintf(stderr, "Fatal error: SwigType_del_rvalue_reference() applied to non-rvalue-reference.\n");
+ Exit(EXIT_FAILURE);
+ }
+ Delslice(t, 0, 2);
+ return t;
+}
+
+int SwigType_isrvalue_reference(const SwigType *t) {
+ char *c;
+ if (!t)
+ return 0;
+ c = Char(t);
+ if (strncmp(c, "z.", 2) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Qualifiers
+ *
+ * SwigType_add_qualifier()
+ * SwigType_del_qualifier()
+ * SwigType_is_qualifier()
+ *
+ * Adds type qualifiers like "const" and "volatile". When multiple qualifiers
+ * are added to a type, they are combined together into a single qualifier.
+ * Repeated qualifications have no effect. Moreover, the order of qualifications
+ * is alphabetical---meaning that "const volatile" and "volatile const" are
+ * stored in exactly the same way as "q(const volatile)".
+ * 'qual' can be a list of multiple qualifiers in any order, separated by spaces.
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_add_qualifier(SwigType *t, const_String_or_char_ptr qual) {
+ List *qlist;
+ String *allq, *newq;
+ int i, sz;
+ const char *cqprev = 0;
+ const char *c = Char(t);
+ const char *cqual = Char(qual);
+
+ /* if 't' has no qualifiers and 'qual' is a single qualifier, simply add it */
+ if ((strncmp(c, "q(", 2) != 0) && (strstr(cqual, " ") == 0)) {
+ String *temp = NewStringf("q(%s).", cqual);
+ Insert(t, 0, temp);
+ Delete(temp);
+ return t;
+ }
+
+ /* create string of all qualifiers */
+ if (strncmp(c, "q(", 2) == 0) {
+ allq = SwigType_parm(t);
+ Append(allq, " ");
+ SwigType_del_element(t); /* delete old qualifier list from 't' */
+ } else {
+ allq = NewStringEmpty();
+ }
+ Append(allq, qual);
+
+ /* create list of all qualifiers from string */
+ qlist = Split(allq, ' ', INT_MAX);
+ Delete(allq);
+
+ /* sort in alphabetical order */
+ SortList(qlist, Strcmp);
+
+ /* create new qualifier string from unique elements of list */
+ sz = Len(qlist);
+ newq = NewString("q(");
+ for (i = 0; i < sz; ++i) {
+ String *q = Getitem(qlist, i);
+ const char *cq = Char(q);
+ if (cqprev == 0 || strcmp(cqprev, cq) != 0) {
+ if (i > 0) {
+ Append(newq, " ");
+ }
+ Append(newq, q);
+ cqprev = cq;
+ }
+ }
+ Append(newq, ").");
+ Delete(qlist);
+
+ /* replace qualifier string with new one */
+ Insert(t, 0, newq);
+ Delete(newq);
+ return t;
+}
+
+SwigType *SwigType_del_qualifier(SwigType *t) {
+ char *c = Char(t);
+ int check = strncmp(c, "q(", 2);
+ assert(check == 0);
+ (void)check;
+ Delslice(t, 0, element_size(c));
+ return t;
+}
+
+int SwigType_isqualifier(const SwigType *t) {
+ char *c;
+ if (!t)
+ return 0;
+ c = Char(t);
+ if (strncmp(c, "q(", 2) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Function Pointers
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_isfunctionpointer(const SwigType *t) {
+ char *c;
+ if (!t)
+ return 0;
+ c = Char(t);
+ if (strncmp(c, "p.f(", 4) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_functionpointer_decompose
+ *
+ * Decompose the function pointer into the parameter list and the return type
+ * t - input and on completion contains the return type
+ * returns the function's parameters
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_functionpointer_decompose(SwigType *t) {
+ String *p;
+ assert(SwigType_isfunctionpointer(t));
+ p = SwigType_pop(t);
+ Delete(p);
+ p = SwigType_pop(t);
+ return p;
+}
+
+/* -----------------------------------------------------------------------------
+ * Member Pointers
+ *
+ * SwigType_add_memberpointer()
+ * SwigType_del_memberpointer()
+ * SwigType_ismemberpointer()
+ *
+ * Add, remove, and test for C++ pointer to members.
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_add_memberpointer(SwigType *t, const_String_or_char_ptr name) {
+ String *temp = NewStringf("m(%s).", name);
+ Insert(t, 0, temp);
+ Delete(temp);
+ return t;
+}
+
+SwigType *SwigType_del_memberpointer(SwigType *t) {
+ char *c = Char(t);
+ int check = strncmp(c, "m(", 2);
+ assert(check == 0);
+ (void)check;
+ Delslice(t, 0, element_size(c));
+ return t;
+}
+
+int SwigType_ismemberpointer(const SwigType *t) {
+ char *c;
+ if (!t)
+ return 0;
+ c = Char(t);
+ if (strncmp(c, "m(", 2) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Arrays
+ *
+ * SwigType_add_array()
+ * SwigType_del_array()
+ * SwigType_isarray()
+ *
+ * Utility functions:
+ *
+ * SwigType_array_ndim() - Calculate number of array dimensions.
+ * SwigType_array_getdim() - Get array dimension
+ * SwigType_array_setdim() - Set array dimension
+ * SwigType_array_type() - Return array type
+ * SwigType_pop_arrays() - Remove all arrays
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_add_array(SwigType *t, const_String_or_char_ptr size) {
+ String *temp = NewString("a(");
+ Append(temp, size);
+ Append(temp, ").");
+ Insert(t, 0, temp);
+ Delete(temp);
+ return t;
+}
+
+SwigType *SwigType_del_array(SwigType *t) {
+ char *c = Char(t);
+ if (strncmp(c, "a(", 2)) {
+ fprintf(stderr, "Fatal error: SwigType_del_array() applied to non-array.\n");
+ Exit(EXIT_FAILURE);
+ }
+ Delslice(t, 0, element_size(c));
+ return t;
+}
+
+int SwigType_isarray(const SwigType *t) {
+ char *c;
+ if (!t)
+ return 0;
+ c = Char(t);
+ if (strncmp(c, "a(", 2) == 0) {
+ return 1;
+ }
+ return 0;
+}
+/*
+ * SwigType_prefix_is_simple_1D_array
+ *
+ * Determine if the type is a 1D array type that is treated as a pointer within SWIG
+ * eg Foo[], Foo[3] return true, but Foo[3][3], Foo*[], Foo*[3], Foo**[] return false
+ */
+int SwigType_prefix_is_simple_1D_array(const SwigType *t) {
+ char *c = Char(t);
+
+ if (c && (strncmp(c, "a(", 2) == 0)) {
+ c = strchr(c, '.');
+ if (c)
+ return (*(++c) == 0);
+ }
+ return 0;
+}
+
+
+/* Remove all arrays */
+SwigType *SwigType_pop_arrays(SwigType *t) {
+ String *ta;
+ assert(SwigType_isarray(t));
+ ta = NewStringEmpty();
+ while (SwigType_isarray(t)) {
+ SwigType *td = SwigType_pop(t);
+ Append(ta, td);
+ Delete(td);
+ }
+ return ta;
+}
+
+/* Return number of array dimensions */
+int SwigType_array_ndim(const SwigType *t) {
+ int ndim = 0;
+ char *c = Char(t);
+
+ while (c && (strncmp(c, "a(", 2) == 0)) {
+ c = strchr(c, '.');
+ if (c) {
+ c++;
+ ndim++;
+ }
+ }
+ return ndim;
+}
+
+/* Get nth array dimension */
+String *SwigType_array_getdim(const SwigType *t, int n) {
+ char *c = Char(t);
+ while (c && (strncmp(c, "a(", 2) == 0) && (n > 0)) {
+ c = strchr(c, '.');
+ if (c) {
+ c++;
+ n--;
+ }
+ }
+ if (n == 0) {
+ String *dim = SwigType_parm(c);
+ if (SwigType_istemplate(dim)) {
+ String *ndim = SwigType_namestr(dim);
+ Delete(dim);
+ dim = ndim;
+ }
+
+ return dim;
+ }
+
+ return 0;
+}
+
+/* Replace nth array dimension */
+void SwigType_array_setdim(SwigType *t, int n, const_String_or_char_ptr rep) {
+ String *result = 0;
+ char temp;
+ char *start;
+ char *c = Char(t);
+
+ start = c;
+ if (strncmp(c, "a(", 2)) {
+ fprintf(stderr, "Fatal error: SwigType_array_type applied to non-array.\n");
+ Exit(EXIT_FAILURE);
+ }
+
+ while (c && (strncmp(c, "a(", 2) == 0) && (n > 0)) {
+ c = strchr(c, '.');
+ if (c) {
+ c++;
+ n--;
+ }
+ }
+ if (n == 0) {
+ temp = *c;
+ *c = 0;
+ result = NewString(start);
+ Printf(result, "a(%s)", rep);
+ *c = temp;
+ c = strchr(c, '.');
+ Append(result, c);
+ }
+ Clear(t);
+ Append(t, result);
+ Delete(result);
+}
+
+/* Return base type of an array */
+SwigType *SwigType_array_type(const SwigType *ty) {
+ SwigType *t;
+ t = Copy(ty);
+ while (SwigType_isarray(t)) {
+ Delete(SwigType_pop(t));
+ }
+ return t;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Functions
+ *
+ * SwigType_add_function()
+ * SwigType_isfunction()
+ * SwigType_pop_function()
+ *
+ * Add, remove, and test for function types.
+ * ----------------------------------------------------------------------------- */
+
+/* Returns the function type, t, constructed from the parameters, parms */
+SwigType *SwigType_add_function(SwigType *t, ParmList *parms) {
+ String *pstr;
+ Parm *p;
+
+ Insert(t, 0, ").");
+ pstr = NewString("f(");
+ for (p = parms; p; p = nextSibling(p)) {
+ if (p != parms)
+ Putc(',', pstr);
+ Append(pstr, Getattr(p, "type"));
+ }
+ Insert(t, 0, pstr);
+ Delete(pstr);
+ return t;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_pop_function()
+ *
+ * Pop and return the function from the input type leaving the function's return
+ * type, if any.
+ * For example:
+ * t in: q(const).f().p.
+ * t out: p.
+ * result: q(const).f().
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_pop_function(SwigType *t) {
+ SwigType *f = 0;
+ SwigType *g = 0;
+ char *c = Char(t);
+ if (strncmp(c, "r.", 2) == 0 || strncmp(c, "z.", 2) == 0) {
+ /* Remove ref-qualifier */
+ f = SwigType_pop(t);
+ c = Char(t);
+ }
+ if (strncmp(c, "q(", 2) == 0) {
+ /* Remove cv-qualifier */
+ String *qual = SwigType_pop(t);
+ if (f) {
+ SwigType_push(qual, f);
+ Delete(f);
+ }
+ f = qual;
+ c = Char(t);
+ }
+ if (strncmp(c, "f(", 2)) {
+ fprintf(stderr, "Fatal error. SwigType_pop_function applied to non-function.\n");
+ Exit(EXIT_FAILURE);
+ }
+ g = SwigType_pop(t);
+ if (f)
+ SwigType_push(g, f);
+ Delete(f);
+ return g;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_pop_function_qualifiers()
+ *
+ * Pop and return the function qualifiers from the input type leaving the rest of
+ * function declaration. Returns NULL if no qualifiers.
+ * For example:
+ * t in: r.q(const).f().p.
+ * t out: f().p.
+ * result: r.q(const)
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_pop_function_qualifiers(SwigType *t) {
+ SwigType *qualifiers = 0;
+ char *c = Char(t);
+ if (strncmp(c, "r.", 2) == 0 || strncmp(c, "z.", 2) == 0) {
+ /* Remove ref-qualifier */
+ String *qual = SwigType_pop(t);
+ qualifiers = qual;
+ c = Char(t);
+ }
+ if (strncmp(c, "q(", 2) == 0) {
+ /* Remove cv-qualifier */
+ String *qual = SwigType_pop(t);
+ if (qualifiers) {
+ SwigType_push(qual, qualifiers);
+ Delete(qualifiers);
+ }
+ qualifiers = qual;
+ }
+ assert(Strncmp(t, "f(", 2) == 0);
+
+ return qualifiers;
+}
+
+int SwigType_isfunction(const SwigType *t) {
+ char *c;
+ if (!t) {
+ return 0;
+ }
+ c = Char(t);
+ if (strncmp(c, "r.", 2) == 0 || strncmp(c, "z.", 2) == 0) {
+ /* Might be a function with a ref-qualifier, skip over */
+ c += 2;
+ if (!*c)
+ return 0;
+ }
+ if (strncmp(c, "q(", 2) == 0) {
+ /* Might be a function with a cv-qualifier, skip over */
+ c = strchr(c, '.');
+ if (c)
+ c++;
+ else
+ return 0;
+ }
+ if (strncmp(c, "f(", 2) == 0) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Create a list of parameters from the type t, using the file_line_node Node for
+ * file and line numbering for the parameters */
+ParmList *SwigType_function_parms(const SwigType *t, Node *file_line_node) {
+ List *l = SwigType_parmlist(t);
+ Hash *p, *pp = 0, *firstp = 0;
+ Iterator o;
+
+ for (o = First(l); o.item; o = Next(o)) {
+ p = file_line_node ? NewParm(o.item, 0, file_line_node) : NewParmWithoutFileLineInfo(o.item, 0);
+ if (!firstp)
+ firstp = p;
+ if (pp) {
+ set_nextSibling(pp, p);
+ Delete(p);
+ }
+ pp = p;
+ }
+ Delete(l);
+ return firstp;
+}
+
+int SwigType_isvarargs(const SwigType *t) {
+ if (Strcmp(t, "v(...)") == 0)
+ return 1;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Templates
+ *
+ * SwigType_add_template()
+ *
+ * Template handling.
+ * ----------------------------------------------------------------------------- */
+
+/* -----------------------------------------------------------------------------
+ * SwigType_add_template()
+ *
+ * Adds a template to a type. This template is encoded in the SWIG type
+ * mechanism and produces a string like this:
+ *
+ * vector<int *> ----> "vector<(p.int)>"
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_add_template(SwigType *t, ParmList *parms) {
+ Parm *p;
+
+ Append(t, "<(");
+ for (p = parms; p; p = nextSibling(p)) {
+ String *v;
+ if (Getattr(p, "default"))
+ continue;
+ if (p != parms)
+ Append(t, ",");
+ v = Getattr(p, "value");
+ if (v) {
+ Append(t, v);
+ } else {
+ Append(t, Getattr(p, "type"));
+ }
+ }
+ Append(t, ")>");
+ return t;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_templateprefix()
+ *
+ * Returns the prefix before the first template definition.
+ * Returns the type unmodified if not a template.
+ * For example:
+ *
+ * Foo<(p.int)>::bar => Foo
+ * r.q(const).Foo<(p.int)>::bar => r.q(const).Foo
+ * Foo => Foo
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_templateprefix(const SwigType *t) {
+ const char *s = Char(t);
+ const char *c = strstr(s, "<(");
+ return c ? NewStringWithSize(s, (int)(c - s)) : NewString(s);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_templatesuffix()
+ *
+ * Returns text after a template substitution. Used to handle scope names
+ * for example:
+ *
+ * Foo<(p.int)>::bar
+ *
+ * returns "::bar"
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_templatesuffix(const SwigType *t) {
+ const char *c;
+ c = Char(t);
+ while (*c) {
+ if ((*c == '<') && (*(c + 1) == '(')) {
+ int nest = 1;
+ c++;
+ while (*c && nest) {
+ if (*c == '<')
+ nest++;
+ if (*c == '>')
+ nest--;
+ c++;
+ }
+ return NewString(c);
+ }
+ c++;
+ }
+ return NewStringEmpty();
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_istemplate_templateprefix()
+ *
+ * Combines SwigType_istemplate and SwigType_templateprefix efficiently into one function.
+ * Returns the prefix before the first template definition.
+ * Returns NULL if not a template.
+ * For example:
+ *
+ * Foo<(p.int)>::bar => Foo
+ * r.q(const).Foo<(p.int)>::bar => r.q(const).Foo
+ * Foo => NULL
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_istemplate_templateprefix(const SwigType *t) {
+ const char *s = Char(t);
+ const char *c = strstr(s, "<(");
+ return c ? NewStringWithSize(s, (int)(c - s)) : 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_istemplate_only_templateprefix()
+ *
+ * Similar to SwigType_istemplate_templateprefix() but only returns the template
+ * prefix if the type is just the template and not a subtype/symbol within the template.
+ * Returns NULL if not a template or is a template with a symbol within the template.
+ * For example:
+ *
+ * Foo<(p.int)> => Foo
+ * Foo<(p.int)>::bar => NULL
+ * r.q(const).Foo<(p.int)> => r.q(const).Foo
+ * r.q(const).Foo<(p.int)>::bar => NULL
+ * Foo => NULL
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_istemplate_only_templateprefix(const SwigType *t) {
+ int len = Len(t);
+ const char *s = Char(t);
+ if (len >= 4 && strcmp(s + len - 2, ")>") == 0) {
+ const char *c = strstr(s, "<(");
+ return c ? NewStringWithSize(s, (int)(c - s)) : 0;
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_templateargs()
+ *
+ * Returns the template arguments
+ * For example:
+ *
+ * Foo<(p.int)>::bar
+ *
+ * returns "<(p.int)>"
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_templateargs(const SwigType *t) {
+ const char *c;
+ const char *start;
+ c = Char(t);
+ while (*c) {
+ if ((*c == '<') && (*(c + 1) == '(')) {
+ int nest = 1;
+ start = c;
+ c++;
+ while (*c && nest) {
+ if (*c == '<')
+ nest++;
+ if (*c == '>')
+ nest--;
+ c++;
+ }
+ return NewStringWithSize(start, (int)(c - start));
+ }
+ c++;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_istemplate()
+ *
+ * Tests a type to see if it includes template parameters
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_istemplate(const SwigType *t) {
+ char *ct = Char(t);
+ ct = strstr(ct, "<(");
+ if (ct && (strstr(ct + 2, ")>")))
+ return 1;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_base()
+ *
+ * This function returns the base of a type. For example, if you have a
+ * type "p.p.int", the function would return "int".
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_base(const SwigType *t) {
+ char *c;
+ char *lastop = 0;
+ c = Char(t);
+
+ lastop = c;
+
+ /* Search for the last type constructor separator '.' */
+ while (*c) {
+ if (*c == '.') {
+ if (*(c + 1)) {
+ lastop = c + 1;
+ }
+ c++;
+ continue;
+ }
+ if (*c == '<') {
+ /* Skip over template---it's part of the base name */
+ int ntemp = 1;
+ c++;
+ while ((*c) && (ntemp > 0)) {
+ if (*c == '>')
+ ntemp--;
+ else if (*c == '<')
+ ntemp++;
+ c++;
+ }
+ if (ntemp)
+ break;
+ continue;
+ }
+ if (*c == '(') {
+ /* Skip over params */
+ int nparen = 1;
+ c++;
+ while ((*c) && (nparen > 0)) {
+ if (*c == '(')
+ nparen++;
+ else if (*c == ')')
+ nparen--;
+ c++;
+ }
+ if (nparen)
+ break;
+ continue;
+ }
+ c++;
+ }
+ return NewString(lastop);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_prefix()
+ *
+ * Returns the prefix of a datatype. For example, the prefix of the
+ * type "p.p.int" is "p.p.".
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_prefix(const SwigType *t) {
+ char *c, *d;
+ String *r = 0;
+
+ c = Char(t);
+ d = c + strlen(c);
+
+ /* Check for a type constructor */
+ if ((d > c) && (*(d - 1) == '.'))
+ d--;
+
+ while (d > c) {
+ d--;
+ if (*d == '>') {
+ int nest = 1;
+ d--;
+ while ((d > c) && (nest)) {
+ if (*d == '>')
+ nest++;
+ if (*d == '<')
+ nest--;
+ d--;
+ }
+ }
+ if (*d == ')') {
+ /* Skip over params */
+ int nparen = 1;
+ d--;
+ while ((d > c) && (nparen)) {
+ if (*d == ')')
+ nparen++;
+ if (*d == '(')
+ nparen--;
+ d--;
+ }
+ }
+
+ if (*d == '.') {
+ char t = *(d + 1);
+ *(d + 1) = 0;
+ r = NewString(c);
+ *(d + 1) = t;
+ return r;
+ }
+ }
+ return NewStringEmpty();
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_strip_qualifiers()
+ *
+ * Strip all qualifiers from a type and return a new type
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_strip_qualifiers(const SwigType *t) {
+ static Hash *memoize_stripped = 0;
+ SwigType *r;
+ List *l;
+ Iterator ei;
+
+ if (!memoize_stripped)
+ memoize_stripped = NewHash();
+ r = Getattr(memoize_stripped, t);
+ if (r)
+ return Copy(r);
+
+ l = SwigType_split(t);
+ r = NewStringEmpty();
+
+ for (ei = First(l); ei.item; ei = Next(ei)) {
+ if (SwigType_isqualifier(ei.item))
+ continue;
+ Append(r, ei.item);
+ }
+ Delete(l);
+ {
+ String *key, *value;
+ key = Copy(t);
+ value = Copy(r);
+ Setattr(memoize_stripped, key, value);
+ Delete(key);
+ Delete(value);
+ }
+ return r;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_strip_single_qualifier()
+ *
+ * If the type contains a qualifier, strip one qualifier and return a new type.
+ * The left most qualifier is stripped first (when viewed as C source code) but
+ * this is the equivalent to the right most qualifier using SwigType notation.
+ * Example:
+ * r.q(const).p.q(const).int => r.q(const).p.int
+ * r.q(const).p.int => r.p.int
+ * r.p.int => r.p.int
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_strip_single_qualifier(const SwigType *t) {
+ static Hash *memoize_stripped = 0;
+ SwigType *r = 0;
+ List *l;
+ int numitems;
+
+ if (!memoize_stripped)
+ memoize_stripped = NewHash();
+ r = Getattr(memoize_stripped, t);
+ if (r)
+ return Copy(r);
+
+ l = SwigType_split(t);
+
+ numitems = Len(l);
+ if (numitems >= 2) {
+ int item;
+ /* iterate backwards from last but one item */
+ for (item = numitems - 2; item >= 0; --item) {
+ String *subtype = Getitem(l, item);
+ if (SwigType_isqualifier(subtype)) {
+ Iterator it;
+ Delitem(l, item);
+ r = NewStringEmpty();
+ for (it = First(l); it.item; it = Next(it)) {
+ Append(r, it.item);
+ }
+ break;
+ }
+ }
+ }
+ if (!r)
+ r = Copy(t);
+
+ Delete(l);
+ {
+ String *key, *value;
+ key = Copy(t);
+ value = Copy(r);
+ Setattr(memoize_stripped, key, value);
+ Delete(key);
+ Delete(value);
+ }
+ return r;
+}
+
diff --git a/contrib/tools/swig/Source/Swig/typesys.c b/contrib/tools/swig/Source/Swig/typesys.c
new file mode 100644
index 0000000000..4a11790c72
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/typesys.c
@@ -0,0 +1,2309 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * typesys.c
+ *
+ * SWIG type system management. These functions are used to manage
+ * the C++ type system including typenames, typedef, type scopes,
+ * inheritance, and namespaces. Generation of support code for the
+ * run-time type checker is also handled here.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include "cparse.h"
+
+/* -----------------------------------------------------------------------------
+ * Synopsis
+ *
+ * The purpose of this module is to manage type names and scoping issues related
+ * to the C++ type system. The primary use is tracking typenames through typedef
+ * and inheritance.
+ *
+ * New typenames are introduced by typedef, class, and enum declarations.
+ * Each type is declared in a scope. This is either the global scope, a
+ * class, or a namespace. For example:
+ *
+ * typedef int A; // Typename A, in global scope
+ * namespace Foo {
+ * typedef int A; // Typename A, in scope Foo::
+ * }
+ * class Bar { // Typename Bar, in global scope
+ * typedef int A; // Typename A, in scope Bar::
+ * }
+ *
+ * To manage scopes, the type system is constructed as a tree of hash tables. Each
+ * hash table contains the following attributes:
+ *
+ * "name" - Scope name
+ * "qname" - Fully qualified typename
+ * "typetab" - Type table containing typenames and typedef information
+ * For a given key in the typetab table, the value is a fully
+ * qualified name if not pointing to itself.
+ * "symtab" - Hash table of symbols defined in a scope
+ * "inherit" - List of inherited scopes
+ * "parent" - Parent scope
+ *
+ * The contents of these tables can be viewed for debugging using the -debug-typedef
+ * option which calls SwigType_print_scope().
+ *
+ * Typedef information is stored in the "typetab" hash table. For example,
+ * if you have these declarations:
+ *
+ * typedef int A;
+ * typedef A B;
+ * typedef B *C;
+ *
+ * typetab in scope '' contains:
+ * "A" : "int"
+ * "B" : "A"
+ * "C" : "p.B"
+ *
+ * To resolve a type back to its root type, one repeatedly expands on the type base.
+ * For example:
+ *
+ * C *[40] ---> a(40).p.C (string type representation, see stype.c)
+ * ---> a(40).p.p.B (C --> p.B)
+ * ---> a(40).p.p.A (B --> A)
+ * ---> a(40).p.p.int (A --> int)
+ *
+ *
+ * Using declarations are stored in the "typetab" hash table. For example,
+ *
+ * namespace NN {
+ * struct SS {};
+ * }
+ * namespace N {
+ * struct S {};
+ * using NN::SS;
+ * }
+ * using N::S;
+ *
+ * typetab in scope '' contains:
+ * "S" : "N::S"
+ *
+ * and typetab in scope 'N' contains:
+ * "SS" : "NN::SS"
+ * "S" : "S"
+ *
+ *
+ * For inheritance, SWIG tries to resolve types back to the base class. For instance, if
+ * you have this:
+ *
+ * class Foo {
+ * public:
+ * typedef int Integer;
+ * };
+ * struct Bar : public Foo {
+ * void blah(Integer x);
+ * };
+ *
+ * In this case typetab in scope '' contains:
+ * "Foo" : "Foo"
+ * "Bar" : "Bar"
+ * and scope 'Foo' contains:
+ * "Integer" : "int"
+ * and scope 'Bar' inherits from 'Foo' but is empty (observe that blah is not a scope or typedef)
+ *
+ * The argument type of Bar::blah will be set to Foo::Integer.
+ *
+ *
+ * The scope-inheritance mechanism is used to manage C++ using directives.
+ *
+ * namespace XX {
+ * class CC {};
+ * }
+ * namespace X {
+ * class C {};
+ * using namespace XX;
+ * }
+ * using namespace X;
+ *
+ * typetab in scope '' inherits from 'X'
+ * typetab in scope 'X' inherits from 'XX' and contains:
+ * "C" : "C"
+ * typetab in scope 'XX' contains:
+ * "CC" : "CC"
+ *
+ *
+ * The scope-inheritance mechanism is used to manage C++ namespace aliases.
+ * For example, if you have this:
+ *
+ * namespace Foo {
+ * typedef int Integer;
+ * }
+ *
+ * namespace F = Foo;
+ *
+ * In this case, F is defined as a scope that "inherits" from Foo. Internally,
+ * F will merely be an empty scope that points to Foo. SWIG will never
+ * place new type information into a namespace alias---attempts to do so
+ * will generate a warning message (in the parser) and will place information into
+ * Foo instead.
+ *
+ *----------------------------------------------------------------------------- */
+
+static Typetab *current_scope = 0; /* Current type scope */
+static Hash *current_typetab = 0; /* Current type table */
+static Hash *current_symtab = 0; /* Current symbol table */
+static Typetab *global_scope = 0; /* The global scope */
+static Hash *scopes = 0; /* Hash table containing fully qualified scopes */
+
+/* Performance optimization */
+#define SWIG_TYPEDEF_RESOLVE_CACHE
+static Hash *typedef_resolve_cache = 0;
+static Hash *typedef_all_cache = 0;
+static Hash *typedef_qualified_cache = 0;
+
+static Typetab *SwigType_find_scope(Typetab *s, const SwigType *nameprefix);
+
+/* common attribute keys, to avoid calling find_key all the times */
+
+/*
+ Enable this one if your language fully support SwigValueWrapper<T>.
+
+ Leaving at '0' keeps the old swig behavior, which is not
+ always safe, but is well known.
+
+ Setting at '1' activates the new scheme, which is always safe but
+ it requires all the typemaps to be ready for that.
+
+*/
+static int value_wrapper_mode = 0;
+int Swig_value_wrapper_mode(int mode) {
+ value_wrapper_mode = mode;
+ return mode;
+}
+
+
+static void flush_cache(void) {
+ typedef_resolve_cache = 0;
+ typedef_all_cache = 0;
+ typedef_qualified_cache = 0;
+}
+
+/* Initialize the scoping system */
+
+void SwigType_typesystem_init(void) {
+ if (global_scope)
+ Delete(global_scope);
+ if (scopes)
+ Delete(scopes);
+
+ current_scope = NewHash();
+ global_scope = current_scope;
+
+ Setattr(current_scope, "name", ""); /* No name for global scope */
+ current_typetab = NewHash();
+ Setattr(current_scope, "typetab", current_typetab);
+
+ current_symtab = 0;
+ scopes = NewHash();
+ Setattr(scopes, "", current_scope);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_typedef()
+ *
+ * Defines a new typedef in the current scope. Returns -1 if the type name is
+ * already defined.
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_typedef(const SwigType *type, const_String_or_char_ptr name) {
+ /* Printf(stdout, "typedef %s %s\n", type, name); */
+ if (Getattr(current_typetab, name))
+ return -1; /* Already defined */
+ if (Strcmp(type, name) == 0) { /* Can't typedef a name to itself */
+ return 0;
+ }
+
+ /* Check if 'type' is already a scope. If so, we create an alias in the type
+ system for it. This is needed to make strange nested scoping problems work
+ correctly. */
+ {
+ Typetab *t = SwigType_find_scope(current_scope, type);
+ if (t) {
+ SwigType_new_scope(name);
+ SwigType_inherit_scope(t);
+ SwigType_pop_scope();
+ }
+ }
+ Setattr(current_typetab, name, type);
+ flush_cache();
+ return 0;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_typedef_class()
+ *
+ * Defines a class in the current scope.
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_typedef_class(const_String_or_char_ptr name) {
+ String *cname;
+ /* Printf(stdout,"class : '%s'\n", name); */
+ if (Getattr(current_typetab, name))
+ return -1; /* Already defined */
+ cname = NewString(name);
+ Setmeta(cname, "class", "1");
+ Setattr(current_typetab, cname, cname);
+ Delete(cname);
+ flush_cache();
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_scope_name()
+ *
+ * Returns the qualified scope name of a type table
+ * ----------------------------------------------------------------------------- */
+
+String *SwigType_scope_name(Typetab *ttab) {
+ String *qname = NewString(Getattr(ttab, "name"));
+ ttab = Getattr(ttab, "parent");
+ while (ttab) {
+ String *pname = Getattr(ttab, "name");
+ if (Len(pname)) {
+ Insert(qname, 0, "::");
+ Insert(qname, 0, pname);
+ }
+ ttab = Getattr(ttab, "parent");
+ }
+ return qname;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_new_scope()
+ *
+ * Creates a new scope
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_new_scope(const_String_or_char_ptr name) {
+ Typetab *s;
+ Hash *ttab;
+ String *qname;
+
+ if (!name) {
+ name = "<unnamed>";
+ }
+ s = NewHash();
+ Setattr(s, "name", name);
+ Setattr(s, "parent", current_scope);
+ ttab = NewHash();
+ Setattr(s, "typetab", ttab);
+
+ /* Build fully qualified name */
+ qname = SwigType_scope_name(s);
+#if 1
+ {
+ /* TODO: only do with templates? What happens with non-templates with code below? */
+ String *stripped_qname;
+ stripped_qname = SwigType_remove_global_scope_prefix(qname);
+ /* Use fully qualified name for hash key without unary scope prefix, qname may contain unary scope */
+ Setattr(scopes, stripped_qname, s);
+ Setattr(s, "qname", qname);
+ /*
+ Printf(stdout, "SwigType_new_scope stripped %s %s\n", qname, stripped_qname);
+ */
+ Delete(stripped_qname);
+ }
+#else
+ Printf(stdout, "SwigType_new_scope %s\n", qname);
+ Setattr(scopes, qname, s);
+ Setattr(s, "qname", qname);
+#endif
+ Delete(qname);
+
+ current_scope = s;
+ current_typetab = ttab;
+ current_symtab = 0;
+ flush_cache();
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_inherit_scope()
+ *
+ * Makes the current scope inherit from another scope. This is used for both
+ * C++ class inheritance, namespaces, and namespace aliases.
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_inherit_scope(Typetab *scope) {
+ List *inherits;
+ int i, len;
+ inherits = Getattr(current_scope, "inherit");
+ if (!inherits) {
+ inherits = NewList();
+ Setattr(current_scope, "inherit", inherits);
+ Delete(inherits);
+ }
+ assert(scope != current_scope);
+
+ len = Len(inherits);
+ for (i = 0; i < len; i++) {
+ Node *n = Getitem(inherits, i);
+ if (n == scope)
+ return;
+ }
+ Append(inherits, scope);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_scope_alias()
+ *
+ * Creates a scope-alias.
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_scope_alias(String *aliasname, Typetab *ttab) {
+ String *q;
+ /* Printf(stdout,"alias: '%s' '%p'\n", aliasname, ttab); */
+ q = SwigType_scope_name(current_scope);
+ if (Len(q)) {
+ Append(q, "::");
+ }
+ Append(q, aliasname);
+ Setattr(scopes, q, ttab);
+ flush_cache();
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_using_scope()
+ *
+ * Import another scope into this scope.
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_using_scope(Typetab *scope) {
+ SwigType_inherit_scope(scope);
+ {
+ List *ulist;
+ int i, len;
+ ulist = Getattr(current_scope, "using");
+ if (!ulist) {
+ ulist = NewList();
+ Setattr(current_scope, "using", ulist);
+ Delete(ulist);
+ }
+ assert(scope != current_scope);
+ len = Len(ulist);
+ for (i = 0; i < len; i++) {
+ Typetab *n = Getitem(ulist, i);
+ if (n == scope)
+ return;
+ }
+ Append(ulist, scope);
+ }
+ flush_cache();
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_pop_scope()
+ *
+ * Pop off the last scope and perform a merge operation. Returns the hash
+ * table for the scope that was popped off.
+ * ----------------------------------------------------------------------------- */
+
+Typetab *SwigType_pop_scope(void) {
+ Typetab *t, *old = current_scope;
+ t = Getattr(current_scope, "parent");
+ if (!t)
+ t = global_scope;
+ current_scope = t;
+ current_typetab = Getattr(t, "typetab");
+ current_symtab = Getattr(t, "symtab");
+ flush_cache();
+ return old;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_set_scope()
+ *
+ * Set the scope. Returns the old scope.
+ * ----------------------------------------------------------------------------- */
+
+Typetab *SwigType_set_scope(Typetab *t) {
+ Typetab *old = current_scope;
+ if (!t)
+ t = global_scope;
+ current_scope = t;
+ current_typetab = Getattr(t, "typetab");
+ current_symtab = Getattr(t, "symtab");
+ flush_cache();
+ return old;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_attach_symtab()
+ *
+ * Attaches a symbol table to a type scope
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_attach_symtab(Symtab *sym) {
+ Setattr(current_scope, "symtab", sym);
+ current_symtab = sym;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_print_scope()
+ *
+ * Debugging function for printing out current scope
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_print_scope(void) {
+ Hash *ttab;
+ Iterator i, j;
+
+ Printf(stdout, "SCOPES start =======================================\n");
+ for (i = First(scopes); i.key; i = Next(i)) {
+ Printf(stdout, "-------------------------------------------------------------\n");
+ ttab = Getattr(i.item, "typetab");
+
+ Printf(stdout, "Type scope '%s' (%p)\n", i.key, i.item);
+ {
+ List *inherit = Getattr(i.item, "inherit");
+ if (inherit) {
+ Iterator j;
+ for (j = First(inherit); j.item; j = Next(j)) {
+ Printf(stdout, " Inherits from '%s' (%p)\n", Getattr(j.item, "qname"), j.item);
+ }
+ }
+ }
+ for (j = First(ttab); j.key; j = Next(j)) {
+ Printf(stdout, "%40s -> %s\n", j.key, j.item);
+ }
+ }
+ Printf(stdout, "SCOPES finish =======================================\n");
+}
+
+static Typetab *SwigType_find_scope(Typetab *s, const SwigType *nameprefix) {
+ Typetab *ss;
+ Typetab *s_orig = s;
+ String *nnameprefix = 0;
+ static int check_parent = 1;
+ int is_template = 0;
+
+ if (Getmark(s))
+ return 0;
+ Setmark(s, 1);
+
+ is_template = SwigType_istemplate(nameprefix);
+ if (is_template) {
+ nnameprefix = SwigType_typedef_resolve_all(nameprefix);
+ nameprefix = nnameprefix;
+ }
+
+ ss = s;
+ while (ss) {
+ String *full;
+ String *qname = Getattr(ss, "qname");
+ if (qname) {
+ full = NewStringf("%s::%s", qname, nameprefix);
+ } else {
+ full = NewString(nameprefix);
+ }
+ s = Getattr(scopes, full);
+ if (!s && is_template) {
+ /* try look up scope with all the unary scope operators within the template parameter list removed */
+ SwigType *full_stripped = SwigType_remove_global_scope_prefix(full);
+ s = Getattr(scopes, full_stripped);
+ Delete(full_stripped);
+ }
+ Delete(full);
+ if (s) {
+ if (nnameprefix)
+ Delete(nnameprefix);
+ Setmark(s_orig, 0);
+ return s;
+ }
+ if (!s) {
+ /* Check inheritance */
+ List *inherit;
+ inherit = Getattr(ss, "using");
+ if (inherit) {
+ Typetab *ttab;
+ int i, len;
+ len = Len(inherit);
+ for (i = 0; i < len; i++) {
+ int oldcp = check_parent;
+ ttab = Getitem(inherit, i);
+ check_parent = 0;
+ s = SwigType_find_scope(ttab, nameprefix);
+ check_parent = oldcp;
+ if (s) {
+ if (nnameprefix)
+ Delete(nnameprefix);
+ Setmark(s_orig, 0);
+ return s;
+ }
+ }
+ }
+ }
+ if (!check_parent)
+ break;
+ ss = Getattr(ss, "parent");
+ }
+ if (nnameprefix)
+ Delete(nnameprefix);
+ Setmark(s_orig, 0);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * typedef_resolve()
+ *
+ * Resolves a typedef and returns a new type string. Returns 0 if there is no
+ * typedef mapping. base is a name without qualification.
+ * Internal function.
+ * ----------------------------------------------------------------------------- */
+
+static Typetab *resolved_scope = 0;
+
+/* Internal function */
+
+static SwigType *_typedef_resolve(Typetab *s, String *base, int look_parent) {
+ Hash *ttab;
+ SwigType *type = 0;
+ List *inherit;
+ Typetab *parent;
+
+ /* if (!s) return 0; *//* now is checked below */
+ /* Printf(stdout,"Typetab %s : %s\n", Getattr(s,"name"), base); */
+
+ if (!Getmark(s)) {
+ Setmark(s, 1);
+
+ ttab = Getattr(s, "typetab");
+ type = Getattr(ttab, base);
+ if (type) {
+ resolved_scope = s;
+ Setmark(s, 0);
+ } else {
+ /* Hmmm. Not found in my scope. It could be in an inherited scope */
+ inherit = Getattr(s, "inherit");
+ if (inherit) {
+ int i, len;
+ len = Len(inherit);
+ for (i = 0; i < len; i++) {
+ type = _typedef_resolve(Getitem(inherit, i), base, 0);
+ if (type) {
+ Setmark(s, 0);
+ break;
+ }
+ }
+ }
+ if (!type) {
+ /* Hmmm. Not found in my scope. check parent */
+ if (look_parent) {
+ parent = Getattr(s, "parent");
+ type = parent ? _typedef_resolve(parent, base, 1) : 0;
+ }
+ }
+ Setmark(s, 0);
+ }
+ }
+ return type;
+}
+
+/* -----------------------------------------------------------------------------
+ * template_parameters_resolve()
+ *
+ * For use with templates only. Attempts to resolve one template parameter.
+ *
+ * If one of the template parameters can be resolved, the type is returned with
+ * just the one parameter resolved and the remaining parameters left as is.
+ * If none of the template parameters can be resolved, zero is returned.
+ * ----------------------------------------------------------------------------- */
+
+static String *template_parameters_resolve(const String *base) {
+ List *tparms;
+ String *suffix;
+ String *type;
+ int i, sz;
+ int rep = 0;
+ type = SwigType_templateprefix(base);
+ suffix = SwigType_templatesuffix(base);
+ Append(type, "<(");
+ tparms = SwigType_parmlist(base);
+ sz = Len(tparms);
+ for (i = 0; i < sz; i++) {
+ SwigType *tpr;
+ SwigType *tp = Getitem(tparms, i);
+ if (!rep) {
+ tpr = SwigType_typedef_resolve(tp);
+ } else {
+ tpr = 0;
+ }
+ if (tpr) {
+ Append(type, tpr);
+ Delete(tpr);
+ rep = 1;
+ } else {
+ Append(type, tp);
+ }
+ if ((i + 1) < sz)
+ Append(type, ",");
+ }
+ if (rep) {
+ Append(type, ")>");
+ Append(type, suffix);
+ } else {
+ Delete(type);
+ type = 0;
+ }
+ Delete(suffix);
+ Delete(tparms);
+ return type;
+}
+
+static SwigType *typedef_resolve(Typetab *s, String *base) {
+ return _typedef_resolve(s, base, 1);
+}
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_typedef_resolve()
+ *
+ * Given a type declaration, this function looks to reduce/resolve the type via a
+ * typedef (including via C++ using declarations).
+ *
+ * If it is able to find a typedef, the resolved type is returned. If no typedef
+ * is found NULL is returned. The type name is resolved in the current scope.
+ * The type returned is not always fully qualified for the global scope, it is
+ * valid for use in the current scope. If the current scope is global scope, a
+ * fully qualified type should be returned.
+ *
+ * Some additional notes are in Doc/Manual/Extending.html.
+ * ----------------------------------------------------------------------------- */
+
+/* #define SWIG_DEBUG */
+SwigType *SwigType_typedef_resolve(const SwigType *t) {
+ String *base;
+ String *type = 0;
+ String *r = 0;
+ Typetab *s;
+ Hash *ttab;
+ String *namebase = 0;
+ String *nameprefix = 0, *rnameprefix = 0;
+ int newtype = 0;
+
+ resolved_scope = 0;
+
+#ifdef SWIG_TYPEDEF_RESOLVE_CACHE
+ if (!typedef_resolve_cache) {
+ typedef_resolve_cache = NewHash();
+ }
+ r = Getattr(typedef_resolve_cache, t);
+ if (r) {
+ resolved_scope = Getmeta(r, "scope");
+ return Copy(r);
+ }
+#endif
+
+ base = SwigType_base(t);
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "base = '%s' t='%s'\n", base, t);
+#endif
+
+ if (SwigType_issimple(base)) {
+ s = current_scope;
+ ttab = current_typetab;
+ if (strncmp(Char(base), "::", 2) == 0) {
+ s = global_scope;
+ ttab = Getattr(s, "typetab");
+ Delitem(base, 0);
+ Delitem(base, 0);
+ }
+ /* Do a quick check in the local scope */
+ type = Getattr(ttab, base);
+ if (type) {
+ resolved_scope = s;
+ }
+ if (!type) {
+ /* Didn't find in this scope. We need to do a little more searching */
+ if (Swig_scopename_check(base)) {
+ /* A qualified name. */
+ Swig_scopename_split(base, &nameprefix, &namebase);
+#ifdef SWIG_DEBUG
+ Printf(stdout, "nameprefix = '%s'\n", nameprefix);
+#endif
+ if (nameprefix) {
+ rnameprefix = SwigType_typedef_resolve(nameprefix);
+ if(rnameprefix != NULL) {
+#ifdef SWIG_DEBUG
+ Printf(stdout, "nameprefix '%s' is a typedef to '%s'\n", nameprefix, rnameprefix);
+#endif
+ type = Copy(namebase);
+ Insert(type, 0, "::");
+ Insert(type, 0, rnameprefix);
+ if (strncmp(Char(type), "::", 2) == 0) {
+ Delitem(type, 0);
+ Delitem(type, 0);
+ }
+ newtype = 1;
+ } else {
+ /* Name had a prefix on it. See if we can locate the proper scope for it */
+ String *rnameprefix = template_parameters_resolve(nameprefix);
+ nameprefix = rnameprefix ? Copy(rnameprefix) : nameprefix;
+ Delete(rnameprefix);
+ s = SwigType_find_scope(s, nameprefix);
+
+ /* Couldn't locate a scope for the type. */
+ if (!s) {
+ Delete(base);
+ Delete(namebase);
+ Delete(nameprefix);
+ r = 0;
+ goto return_result;
+ }
+ /* Try to locate the name starting in the scope */
+#ifdef SWIG_DEBUG
+ Printf(stdout, "namebase = '%s'\n", namebase);
+#endif
+ type = typedef_resolve(s, namebase);
+ if (type && resolved_scope) {
+ /* we need to look for the resolved type, this will also
+ fix the resolved_scope if 'type' and 'namebase' are
+ declared in different scopes */
+ String *rtype = 0;
+ rtype = typedef_resolve(resolved_scope, type);
+ if (rtype)
+ type = rtype;
+ }
+#ifdef SWIG_DEBUG
+ Printf(stdout, "%s type = '%s'\n", Getattr(s, "name"), type);
+#endif
+ if ((type) && (!Swig_scopename_check(type)) && resolved_scope) {
+ Typetab *rtab = resolved_scope;
+ String *qname = Getattr(resolved_scope, "qname");
+ /* If qualified *and* the typename is defined from the resolved scope, we qualify */
+ if ((qname) && typedef_resolve(resolved_scope, type)) {
+ type = Copy(type);
+ Insert(type, 0, "::");
+ Insert(type, 0, qname);
+#ifdef SWIG_DEBUG
+ Printf(stdout, "qual %s \n", type);
+#endif
+ newtype = 1;
+ }
+ resolved_scope = rtab;
+ }
+ }
+ } else {
+ /* Name is unqualified. */
+ type = typedef_resolve(s, base);
+ }
+ } else {
+ /* Name is unqualified. */
+ type = typedef_resolve(s, base);
+ }
+ }
+
+ if (!type && SwigType_istemplate(base)) {
+ String *tprefix = SwigType_templateprefix(base);
+ String *rtprefix = SwigType_typedef_resolve(tprefix);
+ /* We're looking for a using declaration on the template prefix to resolve the template prefix
+ * in another scope. Using declaration do not have template parameters. */
+ if (rtprefix && !SwigType_istemplate(rtprefix)) {
+ String *tsuffix = SwigType_templatesuffix(base);
+ String *targs = SwigType_templateargs(base);
+ type = NewString(rtprefix);
+ newtype = 1;
+ Append(type, targs);
+ Append(type, tsuffix);
+ Delete(targs);
+ Delete(tsuffix);
+ Delete(rtprefix);
+ }
+ Delete(tprefix);
+ }
+
+ if (type && (Equal(base, type))) {
+ if (newtype)
+ Delete(type);
+ Delete(base);
+ Delete(namebase);
+ Delete(nameprefix);
+ r = 0;
+ goto return_result;
+ }
+
+ /* If the type is a template, and no typedef was found, we need to check the
+ template arguments one by one to see if they can be resolved. */
+
+ if (!type && SwigType_istemplate(base)) {
+ newtype = 1;
+ type = template_parameters_resolve(base);
+ }
+ Delete(namebase);
+ Delete(nameprefix);
+ } else {
+ if (SwigType_isfunction(base)) {
+ List *parms;
+ int i, sz;
+ int rep = 0;
+ type = NewString("f(");
+ newtype = 1;
+ parms = SwigType_parmlist(base);
+ sz = Len(parms);
+ for (i = 0; i < sz; i++) {
+ SwigType *tpr;
+ SwigType *tp = Getitem(parms, i);
+ if (!rep) {
+ tpr = SwigType_typedef_resolve(tp);
+ } else {
+ tpr = 0;
+ }
+ if (tpr) {
+ Append(type, tpr);
+ Delete(tpr);
+ rep = 1;
+ } else {
+ Append(type, tp);
+ }
+ if ((i + 1) < sz)
+ Append(type, ",");
+ }
+ Append(type, ").");
+ Delete(parms);
+ if (!rep) {
+ Delete(type);
+ type = 0;
+ }
+ } else if (SwigType_ismemberpointer(base)) {
+ String *rt;
+ String *mtype = SwigType_parm(base);
+ rt = SwigType_typedef_resolve(mtype);
+ if (rt) {
+ type = NewStringf("m(%s).", rt);
+ newtype = 1;
+ Delete(rt);
+ }
+ Delete(mtype);
+ } else {
+ type = 0;
+ }
+ }
+ r = SwigType_prefix(t);
+ if (!type) {
+ if (r && Len(r)) {
+ char *cr = Char(r);
+ if ((strstr(cr, "f(") || (strstr(cr, "m(")))) {
+ SwigType *rt = SwigType_typedef_resolve(r);
+ if (rt) {
+ Delete(r);
+ Append(rt, base);
+ Delete(base);
+ r = rt;
+ goto return_result;
+ }
+ }
+ }
+ Delete(r);
+ Delete(base);
+ r = 0;
+ goto return_result;
+ }
+ Delete(base);
+
+ /* If 'type' is an array, then the right-most qualifier in 'r' should
+ be added to 'type' after the array qualifier, so that given
+ a(7).q(volatile).double myarray // typedef volatile double[7] myarray;
+ the type
+ q(const).myarray // const myarray
+ becomes
+ a(7).q(const volatile).double // const volatile double[7]
+ and NOT
+ q(const).a(7).q(volatile).double // non-sensical type
+ */
+ if (r && Len(r) && SwigType_isarray(type)) {
+ List *r_elem;
+ String *r_qual;
+ int r_sz;
+ r_elem = SwigType_split(r);
+ r_sz = Len(r_elem);
+ r_qual = Getitem(r_elem, r_sz-1);
+ if (SwigType_isqualifier(r_qual)) {
+ String *new_r;
+ String *new_type;
+ List *type_elem;
+ String *type_qual;
+ String *r_qual_arg;
+ int i, type_sz;
+
+ type_elem = SwigType_split(type);
+ type_sz = Len(type_elem);
+
+ for (i = 0; i < type_sz; ++i) {
+ String *e = Getitem(type_elem, i);
+ if (!SwigType_isarray(e))
+ break;
+ }
+ type_qual = Copy(Getitem(type_elem, i));
+ r_qual_arg = SwigType_parm(r_qual);
+ SwigType_add_qualifier(type_qual, r_qual_arg);
+ Delete(r_qual_arg);
+ Setitem(type_elem, i, type_qual);
+
+ new_r = NewStringEmpty();
+ for (i = 0; i < r_sz-1; ++i) {
+ Append(new_r, Getitem(r_elem, i));
+ }
+ new_type = NewStringEmpty();
+ for (i = 0; i < type_sz; ++i) {
+ Append(new_type, Getitem(type_elem, i));
+ }
+#ifdef SWIG_DEBUG
+ Printf(stdout, "r+type='%s%s' new_r+new_type='%s%s'\n", r, type, new_r, new_type);
+#endif
+
+ Delete(r);
+ r = new_r;
+ newtype = 1;
+ type = new_type;
+ Delete(type_elem);
+ }
+ Delete(r_elem);
+ }
+
+ Append(r, type);
+ if (newtype) {
+ Delete(type);
+ }
+
+return_result:
+#ifdef SWIG_TYPEDEF_RESOLVE_CACHE
+ {
+ String *key = NewString(t);
+ if (r) {
+ SwigType *r1;
+ Setattr(typedef_resolve_cache, key, r);
+ Setmeta(r, "scope", resolved_scope);
+ r1 = Copy(r);
+ Delete(r);
+ r = r1;
+ }
+ Delete(key);
+ }
+#endif
+ return r;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_typedef_resolve_all()
+ *
+ * Fully resolve a type down to its most basic datatype
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_typedef_resolve_all(const SwigType *t) {
+ SwigType *n;
+ SwigType *r;
+ int count = 0;
+
+ /* Check to see if the typedef resolve has been done before by checking the cache */
+ if (!typedef_all_cache) {
+ typedef_all_cache = NewHash();
+ }
+ r = Getattr(typedef_all_cache, t);
+ if (r) {
+ return Copy(r);
+ }
+
+#ifdef SWIG_DEBUG
+ Printf(stdout, "SwigType_typedef_resolve_all start ... %s\n", t);
+#endif
+ /* Recursively resolve the typedef */
+ r = NewString(t);
+ while ((n = SwigType_typedef_resolve(r))) {
+ Delete(r);
+ r = n;
+ if (++count >= 512) {
+ Swig_error(Getfile(t), Getline(t), "Recursive typedef detected resolving '%s' to '%s' to '%s' and so on...\n", SwigType_str(t, 0), SwigType_str(SwigType_typedef_resolve(t), 0), SwigType_str(SwigType_typedef_resolve(SwigType_typedef_resolve(t)), 0));
+ break;
+ }
+ }
+
+ /* Add the typedef to the cache for next time it is looked up */
+ {
+ String *key;
+ SwigType *rr = Copy(r);
+ key = NewString(t);
+ Setattr(typedef_all_cache, key, rr);
+ Delete(key);
+ Delete(rr);
+ }
+#ifdef SWIG_DEBUG
+ Printf(stdout, "SwigType_typedef_resolve_all end === %s => %s\n", t, r);
+#endif
+ return r;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_typedef_qualified()
+ *
+ * Given a type declaration, this function tries to fully qualify it so that the
+ * resulting type can be used in the global scope. The type name is resolved in
+ * the current scope.
+ *
+ * It provides a fully qualified name, not necessarily a fully expanded name.
+ * When a using declaration or using directive is found the type may not be fully
+ * expanded, but it will be resolved and fully qualified for use in the global scope.
+ *
+ * This function is for looking up scopes to qualify a type. It does not resolve
+ * C typedefs, it just qualifies them. See SwigType_typedef_resolve for resolving.
+ *
+ * If the unary scope operator (::) is used as a prefix to the type to denote global
+ * scope, it is left in place.
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_typedef_qualified(const SwigType *t) {
+ List *elements;
+ String *result;
+ int i, len;
+
+ if (!typedef_qualified_cache)
+ typedef_qualified_cache = NewHash();
+ result = Getattr(typedef_qualified_cache, t);
+ if (result) {
+ String *rc = Copy(result);
+ return rc;
+ }
+
+ result = NewStringEmpty();
+ elements = SwigType_split(t);
+ len = Len(elements);
+ for (i = 0; i < len; i++) {
+ String *ty = 0;
+ String *e = Getitem(elements, i);
+ if (SwigType_issimple(e)) {
+ if (!SwigType_istemplate(e)) {
+ String *isenum = 0;
+ if (SwigType_isenum(e)) {
+ isenum = NewString("enum ");
+ ty = NewString(Char(e) + 5);
+ e = ty;
+ }
+ resolved_scope = 0;
+ if (typedef_resolve(current_scope, e) && resolved_scope) {
+ /* resolved_scope contains the scope that actually resolved the symbol */
+ String *qname = Getattr(resolved_scope, "qname");
+ if (qname) {
+ Insert(e, 0, "::");
+ Insert(e, 0, qname);
+ }
+ } else {
+ if (Swig_scopename_check(e)) {
+ String *qlast;
+ String *qname;
+ Swig_scopename_split(e, &qname, &qlast);
+ if (qname) {
+ String *tqname = SwigType_typedef_qualified(qname);
+ Clear(e);
+ Printf(e, "%s::%s", tqname, qlast);
+ Delete(qname);
+ Delete(tqname);
+ }
+ Delete(qlast);
+
+ /* Automatic template instantiation might go here??? */
+ } else {
+ /* It's a bare name. It's entirely possible, that the
+ name is part of a namespace. We'll check this by unrolling
+ out of the current scope */
+
+ Typetab *cs = current_scope;
+ if (cs) {
+ Typetab *found_scope = SwigType_find_scope(cs, e);
+ if (found_scope) {
+ String *qs = SwigType_scope_name(found_scope);
+ Clear(e);
+ Append(e, qs);
+ Delete(qs);
+ }
+ }
+ }
+ }
+ if (isenum) {
+ Insert(e, 0, isenum);
+ Delete(isenum);
+ }
+ } else {
+ /* Template. We need to qualify template parameters as well as the template itself */
+ String *tprefix, *qprefix;
+ String *tsuffix;
+ Iterator pi;
+ Parm *p;
+ List *parms;
+ ty = Swig_symbol_template_deftype(e, current_symtab);
+ e = ty;
+ parms = SwigType_parmlist(e);
+ tprefix = SwigType_templateprefix(e);
+ tsuffix = SwigType_templatesuffix(e);
+ qprefix = SwigType_typedef_qualified(tprefix);
+ Append(qprefix, "<(");
+ pi = First(parms);
+ while ((p = pi.item)) {
+ /* TODO: the logic here should be synchronised with that in symbol_template_qualify() in symbol.c */
+ String *qt = SwigType_typedef_qualified(p);
+ if (Equal(qt, p)) { /* && (!Swig_scopename_check(qt))) */
+ /* No change in value. It is entirely possible that the parameter is an integer value.
+ If there is a symbol table associated with this scope, we're going to check for this */
+
+ if (current_symtab) {
+ Node *lastnode = 0;
+ String *value = Copy(p);
+ while (1) {
+ Node *n = Swig_symbol_clookup(value, current_symtab);
+ if (n == lastnode)
+ break;
+ lastnode = n;
+ if (n) {
+ char *ntype = Char(nodeType(n));
+ if (strcmp(ntype, "enumitem") == 0) {
+ /* An enum item. Generate a fully qualified name */
+ String *qn = Swig_symbol_qualified(n);
+ if (Len(qn)) {
+ Append(qn, "::");
+ Append(qn, Getattr(n, "name"));
+ Delete(value);
+ value = qn;
+ continue;
+ } else {
+ Delete(qn);
+ break;
+ }
+ } else if ((strcmp(ntype, "cdecl") == 0) && (Getattr(n, "value"))) {
+ Delete(value);
+ value = Copy(Getattr(n, "value"));
+ continue;
+ }
+ }
+ break;
+ }
+ Append(qprefix, value);
+ Delete(value);
+ } else {
+ Append(qprefix, p);
+ }
+ } else {
+ Append(qprefix, qt);
+ }
+ Delete(qt);
+ pi = Next(pi);
+ if (pi.item) {
+ Append(qprefix, ",");
+ }
+ }
+ Append(qprefix, ")>");
+ Append(qprefix, tsuffix);
+ Delete(tsuffix);
+ Clear(e);
+ Append(e, qprefix);
+ Delete(tprefix);
+ Delete(qprefix);
+ Delete(parms);
+ }
+ Append(result, e);
+ Delete(ty);
+ } else if (SwigType_isfunction(e)) {
+ List *parms = SwigType_parmlist(e);
+ String *s = NewString("f(");
+ Iterator pi;
+ pi = First(parms);
+ while (pi.item) {
+ String *pq = SwigType_typedef_qualified(pi.item);
+ Append(s, pq);
+ Delete(pq);
+ pi = Next(pi);
+ if (pi.item) {
+ Append(s, ",");
+ }
+ }
+ Append(s, ").");
+ Append(result, s);
+ Delete(s);
+ Delete(parms);
+ } else if (SwigType_isarray(e)) {
+ String *ndim;
+ String *dim = SwigType_parm(e);
+ ndim = Swig_symbol_string_qualify(dim, 0);
+ Printf(result, "a(%s).", ndim);
+ Delete(dim);
+ Delete(ndim);
+ } else {
+ Append(result, e);
+ }
+ }
+ Delete(elements);
+ {
+ String *key, *cresult;
+ key = NewString(t);
+ cresult = NewString(result);
+ Setattr(typedef_qualified_cache, key, cresult);
+ Delete(key);
+ Delete(cresult);
+ }
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_istypedef()
+ *
+ * Checks a typename to see if it is a typedef.
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_istypedef(const SwigType *t) {
+ String *type;
+
+ type = SwigType_typedef_resolve(t);
+ if (type) {
+ Delete(type);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_typedef_using()
+ *
+ * Processes a 'using' declaration to import types from one scope into another.
+ * Name is a qualified name like A::B.
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_typedef_using(const_String_or_char_ptr name) {
+ String *base;
+ String *td;
+ String *prefix;
+ Typetab *s;
+ Typetab *tt = 0;
+
+ String *defined_name = 0;
+
+ /* Printf(stdout, "using %s\n", name); */
+
+ if (!Swig_scopename_check(name))
+ return -1; /* Not properly qualified */
+ base = Swig_scopename_last(name);
+
+ /* See if the base is already defined in this scope */
+ if (Getattr(current_typetab, base)) {
+ Delete(base);
+ return -1;
+ }
+
+ /* See if the using name is a scope */
+ /* tt = SwigType_find_scope(current_scope,name);
+ Printf(stdout,"tt = %p, name = '%s'\n", tt, name); */
+
+ /* We set up a typedef B --> A::B */
+ Setattr(current_typetab, base, name);
+
+ /* Find the scope name where the symbol is defined */
+ td = SwigType_typedef_resolve(name);
+ /* Printf(stdout,"td = '%s' %p\n", td, resolved_scope); */
+ if (resolved_scope) {
+ defined_name = Getattr(resolved_scope, "qname");
+ if (defined_name) {
+ defined_name = Copy(defined_name);
+ Append(defined_name, "::");
+ Append(defined_name, base);
+ /* Printf(stdout,"defined_name = '%s'\n", defined_name); */
+ tt = SwigType_find_scope(current_scope, defined_name);
+ }
+ }
+ if (td)
+ Delete(td);
+
+
+ /* Figure out the scope the using directive refers to */
+ {
+ prefix = Swig_scopename_prefix(name);
+ if (prefix) {
+ s = SwigType_find_scope(current_scope, prefix);
+ if (s) {
+ Hash *ttab = Getattr(s, "typetab");
+ if (!Getattr(ttab, base) && defined_name) {
+ Setattr(ttab, base, defined_name);
+ }
+ }
+ }
+ }
+
+ if (tt) {
+ /* Using directive had its own scope. We need to create a new scope for it */
+ SwigType_new_scope(base);
+ SwigType_inherit_scope(tt);
+ SwigType_pop_scope();
+ }
+
+ if (defined_name)
+ Delete(defined_name);
+ Delete(prefix);
+ Delete(base);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_isclass()
+ *
+ * Determines if a type defines a class or not. A class is defined by
+ * its type-table entry maps to itself. Note: a pointer to a class is not
+ * a class.
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_isclass(const SwigType *t) {
+ SwigType *qty, *qtys;
+ int isclass = 0;
+
+ qty = SwigType_typedef_resolve_all(t);
+ qtys = SwigType_strip_qualifiers(qty);
+ if (SwigType_issimple(qtys)) {
+ String *td = SwigType_typedef_resolve(qtys);
+ if (td) {
+ Delete(td);
+ }
+ if (resolved_scope) {
+ isclass = 1;
+ }
+ /* Hmmm. Not a class. If a template, it might be uninstantiated */
+ if (!isclass) {
+ String *tp = SwigType_istemplate_templateprefix(qtys);
+ if (tp && Strcmp(tp, t) != 0) {
+ isclass = SwigType_isclass(tp);
+ }
+ Delete(tp);
+ }
+ }
+ Delete(qty);
+ Delete(qtys);
+ return isclass;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_type()
+ *
+ * Returns an integer code describing the datatype. This is only used for
+ * compatibility with SWIG1.1 language modules and is likely to go away once
+ * everything is based on typemaps.
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_type(const SwigType *t) {
+ char *c;
+ /* Check for the obvious stuff */
+ c = Char(t);
+
+ if (strncmp(c, "p.", 2) == 0) {
+ if (SwigType_type(c + 2) == T_CHAR)
+ return T_STRING;
+ else if (SwigType_type(c + 2) == T_WCHAR)
+ return T_WSTRING;
+ else
+ return T_POINTER;
+ }
+ if (strncmp(c, "a(", 2) == 0)
+ return T_ARRAY;
+ if (strncmp(c, "r.", 2) == 0)
+ return T_REFERENCE;
+ if (strncmp(c, "z.", 2) == 0)
+ return T_RVALUE_REFERENCE;
+ if (strncmp(c, "m(", 2) == 0)
+ return T_MPOINTER;
+ if (strncmp(c, "q(", 2) == 0) {
+ while (*c && (*c != '.'))
+ c++;
+ if (*c)
+ return SwigType_type(c + 1);
+ return T_ERROR;
+ }
+ if (strncmp(c, "f(", 2) == 0)
+ return T_FUNCTION;
+
+ /* Look for basic types */
+ if (strcmp(c, "int") == 0)
+ return T_INT;
+ if (strcmp(c, "long") == 0)
+ return T_LONG;
+ if (strcmp(c, "short") == 0)
+ return T_SHORT;
+ if (strcmp(c, "unsigned") == 0)
+ return T_UINT;
+ if (strcmp(c, "unsigned short") == 0)
+ return T_USHORT;
+ if (strcmp(c, "unsigned long") == 0)
+ return T_ULONG;
+ if (strcmp(c, "unsigned int") == 0)
+ return T_UINT;
+ if (strcmp(c, "char") == 0)
+ return T_CHAR;
+ if (strcmp(c, "signed char") == 0)
+ return T_SCHAR;
+ if (strcmp(c, "unsigned char") == 0)
+ return T_UCHAR;
+ if (strcmp(c, "wchar_t") == 0)
+ return T_WCHAR;
+ if (strcmp(c, "float") == 0)
+ return T_FLOAT;
+ if (strcmp(c, "double") == 0)
+ return T_DOUBLE;
+ if (strcmp(c, "long double") == 0)
+ return T_LONGDOUBLE;
+ if (!cparse_cplusplus && (strcmp(c, "float _Complex") == 0))
+ return T_FLTCPLX;
+ if (!cparse_cplusplus && (strcmp(c, "double _Complex") == 0))
+ return T_DBLCPLX;
+ if (!cparse_cplusplus && (strcmp(c, "_Complex") == 0))
+ return T_COMPLEX;
+ if (strcmp(c, "void") == 0)
+ return T_VOID;
+ if (strcmp(c, "bool") == 0)
+ return T_BOOL;
+ if (strcmp(c, "long long") == 0)
+ return T_LONGLONG;
+ if (strcmp(c, "unsigned long long") == 0)
+ return T_ULONGLONG;
+ if (strncmp(c, "enum ", 5) == 0)
+ return T_INT;
+ if (strcmp(c, "auto") == 0)
+ return T_AUTO;
+
+ if (strcmp(c, "v(...)") == 0)
+ return T_VARARGS;
+ /* Hmmm. Unknown type */
+ if (SwigType_istypedef(t)) {
+ int r;
+ SwigType *nt = SwigType_typedef_resolve(t);
+ r = SwigType_type(nt);
+ Delete(nt);
+ return r;
+ }
+ return T_USER;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_alttype()
+ *
+ * Returns the alternative value type needed in C++ for class value
+ * types. When swig is not sure about using a plain $ltype value,
+ * since the class doesn't have a default constructor, or it can't be
+ * assigned, you will get back 'SwigValueWrapper<type >'.
+ *
+ * This is the default behavior unless:
+ *
+ * 1.- swig detects a default_constructor and 'setallocate:default_constructor'
+ * attribute.
+ *
+ * 2.- swig doesn't mark 'type' as non-assignable.
+ *
+ * 3.- the user specifies that the value wrapper is not needed by using
+ * %feature("novaluewrapper") like so:
+ *
+ * %feature("novaluewrapper") MyOpaqueClass;
+ * class MyOpaqueClass;
+ *
+ * The user can also force the use of the value wrapper with
+ * %feature("valuewrapper").
+ * ----------------------------------------------------------------------------- */
+
+SwigType *SwigType_alttype(const SwigType *t, int local_tmap) {
+ Node *n;
+ SwigType *w = 0;
+ int use_wrapper = 0;
+ SwigType *td = 0;
+
+ if (!cparse_cplusplus)
+ return 0;
+
+ if (value_wrapper_mode == 0) {
+ /* old partial use of SwigValueTypes, it can fail for opaque types */
+ if (local_tmap)
+ return 0;
+ if (SwigType_isclass(t)) {
+ SwigType *ftd = SwigType_typedef_resolve_all(t);
+ td = SwigType_strip_qualifiers(ftd);
+ Delete(ftd);
+ n = Swig_symbol_clookup(td, 0);
+ if (n) {
+ if (GetFlag(n, "feature:valuewrapper")) {
+ use_wrapper = 1;
+ } else {
+ if (Checkattr(n, "nodeType", "class")
+ && (!Getattr(n, "allocate:default_constructor")
+ || (Getattr(n, "allocate:noassign")))) {
+ use_wrapper = !GetFlag(n, "feature:novaluewrapper") || GetFlag(n, "feature:nodefault");
+ }
+ }
+ } else {
+ if (SwigType_issimple(td) && SwigType_istemplate(td)) {
+ use_wrapper = 1;
+ }
+ }
+ }
+ } else {
+ /* safe use of SwigValueTypes, it can fail with some typemaps */
+ SwigType *ftd = SwigType_typedef_resolve_all(t);
+ td = SwigType_strip_qualifiers(ftd);
+ Delete(ftd);
+ if (SwigType_type(td) == T_USER) {
+ use_wrapper = 1;
+ n = Swig_symbol_clookup(td, 0);
+ if (n) {
+ if ((Checkattr(n, "nodeType", "class")
+ && !Getattr(n, "allocate:noassign")
+ && (Getattr(n, "allocate:default_constructor")))
+ || (GetFlag(n, "feature:novaluewrapper"))) {
+ use_wrapper = GetFlag(n, "feature:valuewrapper");
+ }
+ }
+ }
+ }
+
+ if (use_wrapper) {
+ /* Need a space before the type in case it starts "::" (since the <:
+ * token is a digraph for [ in C++. Also need a space after the
+ * type in case it ends with ">" since then we form the token ">>".
+ */
+ w = NewStringf("SwigValueWrapper< %s >", td);
+ }
+ Delete(td);
+ return w;
+}
+
+/* ----------------------------------------------------------------------------
+ * * * * WARNING * * * ***
+ * ***
+ * Don't even think about modifying anything below this line unless you ***
+ * are completely on top of *EVERY* subtle aspect of the C++ type system ***
+ * and you are prepared to suffer endless hours of agony trying to ***
+ * debug the SWIG run-time type checker after you break it. ***
+ * ------------------------------------------------------------------------- */
+
+/* -----------------------------------------------------------------------------
+ * SwigType_remember()
+ *
+ * This function "remembers" a datatype that was used during wrapper code generation
+ * so that a type-checking table can be generated later on. It is up to the language
+ * modules to actually call this function--it is not done automatically.
+ *
+ * Type tracking is managed through two separate hash tables. The hash 'r_mangled'
+ * is mapping between mangled type names (used in the target language) and
+ * fully-resolved C datatypes used in the source input. The second hash 'r_resolved'
+ * is the inverse mapping that maps fully-resolved C datatypes to all of the mangled
+ * names in the scripting languages. For example, consider the following set of
+ * typedef declarations:
+ *
+ * typedef double Real;
+ * typedef double Float;
+ * typedef double Point[3];
+ *
+ * Now, suppose that the types 'double *', 'Real *', 'Float *', 'double[3]', and
+ * 'Point' were used in an interface file and "remembered" using this function.
+ * The hash tables would look like this:
+ *
+ * r_mangled {
+ * _p_double : [ p.double, a(3).double ]
+ * _p_Real : [ p.double ]
+ * _p_Float : [ p.double ]
+ * _Point : [ a(3).double ]
+ *
+ * r_resolved {
+ * p.double : [ _p_double, _p_Real, _p_Float ]
+ * a(3).double : [ _p_double, _Point ]
+ * }
+ *
+ * Together these two hash tables can be used to determine type-equivalency between
+ * mangled typenames. To do this, we view the two hash tables as a large graph and
+ * compute the transitive closure.
+ * ----------------------------------------------------------------------------- */
+
+static Hash *r_mangled = 0; /* Hash mapping mangled types to fully resolved types */
+static Hash *r_resolved = 0; /* Hash mapping resolved types to mangled types */
+static Hash *r_ltype = 0; /* Hash mapping mangled names to their local c type */
+static Hash *r_clientdata = 0; /* Hash mapping resolved types to client data */
+static Hash *r_mangleddata = 0; /* Hash mapping mangled types to client data */
+static Hash *r_remembered = 0; /* Hash of types we remembered already */
+
+static void (*r_tracefunc) (const SwigType *t, String *mangled, String *clientdata) = 0;
+
+void SwigType_remember_mangleddata(String *mangled, const_String_or_char_ptr clientdata) {
+ if (!r_mangleddata) {
+ r_mangleddata = NewHash();
+ }
+ Setattr(r_mangleddata, mangled, clientdata);
+}
+
+
+void SwigType_remember_clientdata(const SwigType *t, const_String_or_char_ptr clientdata) {
+ String *mt;
+ SwigType *lt;
+ Hash *h;
+ SwigType *fr;
+ SwigType *qr;
+ String *tkey;
+ String *cd;
+ Hash *lthash;
+
+ if (!r_mangled) {
+ r_mangled = NewHash();
+ r_resolved = NewHash();
+ r_ltype = NewHash();
+ r_clientdata = NewHash();
+ r_remembered = NewHash();
+ }
+
+ {
+ String *last;
+ last = Getattr(r_remembered, t);
+ if (last && (Cmp(last, clientdata) == 0))
+ return;
+ }
+
+ tkey = Copy(t);
+ cd = clientdata ? NewString(clientdata) : NewStringEmpty();
+ Setattr(r_remembered, tkey, cd);
+ Delete(tkey);
+ Delete(cd);
+
+ mt = SwigType_manglestr(t); /* Create mangled string */
+
+ if (r_tracefunc) {
+ (*r_tracefunc) (t, mt, (String *) clientdata);
+ }
+
+ if (SwigType_istypedef(t)) {
+ lt = Copy(t);
+ } else {
+ lt = SwigType_ltype(t);
+ }
+
+ lthash = Getattr(r_ltype, mt);
+ if (!lthash) {
+ lthash = NewHash();
+ Setattr(r_ltype, mt, lthash);
+ }
+ Setattr(lthash, lt, "1");
+ Delete(lt);
+
+ fr = SwigType_typedef_resolve_all(t); /* Create fully resolved type */
+ qr = SwigType_typedef_qualified(fr);
+ Delete(fr);
+
+ /* Added to deal with possible table bug */
+ fr = SwigType_strip_qualifiers(qr);
+ Delete(qr);
+
+ /*Printf(stdout,"t = '%s'\n", t);
+ Printf(stdout,"fr= '%s'\n\n", fr); */
+
+ if (t) {
+ char *ct = Char(t);
+ const char *lt = strchr(ct, '<');
+ /* Allow for `<<` operator in constant expression for array size. */
+ if (lt && lt[1] != '(' && lt[1] != '<') {
+ Printf(stdout, "Bad template type passed to SwigType_remember: %s\n", t);
+ assert(0);
+ }
+ }
+
+ h = Getattr(r_mangled, mt);
+ if (!h) {
+ h = NewHash();
+ Setattr(r_mangled, mt, h);
+ Delete(h);
+ }
+ Setattr(h, fr, mt);
+
+ h = Getattr(r_resolved, fr);
+ if (!h) {
+ h = NewHash();
+ Setattr(r_resolved, fr, h);
+ Delete(h);
+ }
+ Setattr(h, mt, fr);
+
+ if (clientdata) {
+ String *cd = Getattr(r_clientdata, fr);
+ if (cd) {
+ if (Strcmp(clientdata, cd) != 0) {
+ Printf(stderr, "*** Internal error. Inconsistent clientdata for type '%s'\n", SwigType_str(fr, 0));
+ Printf(stderr, "*** '%s' != '%s'\n", clientdata, cd);
+ assert(0);
+ }
+ } else {
+ String *cstr = NewString(clientdata);
+ Setattr(r_clientdata, fr, cstr);
+ Delete(cstr);
+ }
+ }
+
+ /* If the remembered type is a reference, we also remember the pointer version.
+ This is to prevent odd problems with mixing pointers and references--especially
+ when different functions are using different typenames (via typedef). */
+
+ if (SwigType_isreference(t)) {
+ SwigType *tt = Copy(t);
+ SwigType_del_reference(tt);
+ SwigType_add_pointer(tt);
+ SwigType_remember_clientdata(tt, clientdata);
+ } else if (SwigType_isrvalue_reference(t)) {
+ SwigType *tt = Copy(t);
+ SwigType_del_rvalue_reference(tt);
+ SwigType_add_pointer(tt);
+ SwigType_remember_clientdata(tt, clientdata);
+ }
+}
+
+void SwigType_remember(const SwigType *ty) {
+ SwigType_remember_clientdata(ty, 0);
+}
+
+void (*SwigType_remember_trace(void (*tf) (const SwigType *, String *, String *))) (const SwigType *, String *, String *) {
+ void (*o) (const SwigType *, String *, String *) = r_tracefunc;
+ r_tracefunc = tf;
+ return o;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_equivalent_mangle()
+ *
+ * Return a list of all of the mangled typenames that are equivalent to another
+ * mangled name. This works as follows: For each fully qualified C datatype
+ * in the r_mangled hash entry, we collect all of the mangled names from the
+ * r_resolved hash and combine them together in a list (removing duplicate entries).
+ * ----------------------------------------------------------------------------- */
+
+List *SwigType_equivalent_mangle(String *ms, Hash *checked, Hash *found) {
+ List *l;
+ Hash *h;
+ Hash *ch;
+ Hash *mh;
+
+ if (found) {
+ h = found;
+ } else {
+ h = NewHash();
+ }
+ if (checked) {
+ ch = checked;
+ } else {
+ ch = NewHash();
+ }
+ if (Getattr(ch, ms))
+ goto check_exit; /* Already checked this type */
+ Setattr(h, ms, "1");
+ Setattr(ch, ms, "1");
+ mh = Getattr(r_mangled, ms);
+ if (mh) {
+ Iterator ki;
+ ki = First(mh);
+ while (ki.key) {
+ Hash *rh;
+ if (Getattr(ch, ki.key)) {
+ ki = Next(ki);
+ continue;
+ }
+ Setattr(ch, ki.key, "1");
+ rh = Getattr(r_resolved, ki.key);
+ if (rh) {
+ Iterator rk;
+ rk = First(rh);
+ while (rk.key) {
+ Setattr(h, rk.key, "1");
+ SwigType_equivalent_mangle(rk.key, ch, h);
+ rk = Next(rk);
+ }
+ }
+ ki = Next(ki);
+ }
+ }
+check_exit:
+ if (!found) {
+ l = Keys(h);
+ Delete(h);
+ Delete(ch);
+ return l;
+ } else {
+ return 0;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_clientdata_collect()
+ *
+ * Returns the clientdata field for a mangled type-string.
+ * ----------------------------------------------------------------------------- */
+
+static
+String *SwigType_clientdata_collect(String *ms) {
+ Hash *mh;
+ String *clientdata = 0;
+
+ if (r_mangleddata) {
+ clientdata = Getattr(r_mangleddata, ms);
+ if (clientdata)
+ return clientdata;
+ }
+
+ mh = Getattr(r_mangled, ms);
+ if (mh) {
+ Iterator ki;
+ ki = First(mh);
+ while (ki.key) {
+ clientdata = Getattr(r_clientdata, ki.key);
+ if (clientdata)
+ break;
+ ki = Next(ki);
+ }
+ }
+ return clientdata;
+}
+
+
+
+
+/* -----------------------------------------------------------------------------
+ * SwigType_inherit()
+ *
+ * Record information about inheritance. We keep a hash table that keeps
+ * a mapping between base classes and all of the classes that are derived
+ * from them.
+ *
+ * subclass is a hash that maps base-classes to all of the classes derived from them.
+ *
+ * derived - name of derived class
+ * base - name of base class
+ * cast - additional casting code when casting from derived to base
+ * conversioncode - if set, overrides the default code in the function when casting
+ * from derived to base
+ * ----------------------------------------------------------------------------- */
+
+static Hash *subclass = 0;
+static Hash *conversions = 0;
+
+void SwigType_inherit(String *derived, String *base, String *cast, String *conversioncode) {
+ Hash *h;
+ String *dd = 0;
+ String *bb = 0;
+ if (!subclass)
+ subclass = NewHash();
+
+ /* Printf(stdout,"'%s' --> '%s' '%s'\n", derived, base, cast); */
+
+ if (SwigType_istemplate(derived)) {
+ String *ty = SwigType_typedef_resolve_all(derived);
+ dd = SwigType_typedef_qualified(ty);
+ derived = dd;
+ Delete(ty);
+ }
+ if (SwigType_istemplate(base)) {
+ String *ty = SwigType_typedef_resolve_all(base);
+ bb = SwigType_typedef_qualified(ty);
+ base = bb;
+ Delete(ty);
+ }
+
+ /* Printf(stdout,"'%s' --> '%s' '%s'\n", derived, base, cast); */
+
+ h = Getattr(subclass, base);
+ if (!h) {
+ h = NewHash();
+ Setattr(subclass, base, h);
+ Delete(h);
+ }
+ if (!Getattr(h, derived)) {
+ Hash *c = NewHash();
+ if (cast)
+ Setattr(c, "cast", cast);
+ if (conversioncode)
+ Setattr(c, "convcode", conversioncode);
+ Setattr(h, derived, c);
+ Delete(c);
+ }
+
+ Delete(dd);
+ Delete(bb);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_issubtype()
+ *
+ * Determines if a t1 is a subtype of t2, ie, is t1 derived from t2
+ * ----------------------------------------------------------------------------- */
+
+int SwigType_issubtype(const SwigType *t1, const SwigType *t2) {
+ SwigType *ft1, *ft2;
+ String *b1, *b2;
+ Hash *h;
+ int r = 0;
+
+ if (!subclass)
+ return 0;
+
+ ft1 = SwigType_typedef_resolve_all(t1);
+ ft2 = SwigType_typedef_resolve_all(t2);
+ b1 = SwigType_base(ft1);
+ b2 = SwigType_base(ft2);
+
+ h = Getattr(subclass, b2);
+ if (h) {
+ if (Getattr(h, b1)) {
+ r = 1;
+ }
+ }
+ Delete(ft1);
+ Delete(ft2);
+ Delete(b1);
+ Delete(b2);
+ /* Printf(stdout, "issubtype(%s,%s) --> %d\n", t1, t2, r); */
+ return r;
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_inherit_equiv()
+ *
+ * Modify the type table to handle C++ inheritance
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_inherit_equiv(File *out) {
+ String *ckey;
+ String *prefix, *base;
+ String *mprefix, *mkey;
+ Hash *sub;
+ Hash *rh;
+ List *rlist;
+ List *r_resolved_sorted_keys;
+ Iterator rk, bk, ck;
+
+ if (!conversions)
+ conversions = NewHash();
+ if (!subclass)
+ subclass = NewHash();
+
+ r_resolved_sorted_keys = SortedKeys(r_resolved, Strcmp);
+ rk = First(r_resolved_sorted_keys);
+ while (rk.item) {
+ List *sub_sorted_keys;
+ /* rkey is a fully qualified type. We strip all of the type constructors off of it just to get the base */
+ base = SwigType_base(rk.item);
+ /* Check to see whether the base is recorded in the subclass table */
+ sub = Getattr(subclass, base);
+ Delete(base);
+ if (!sub) {
+ rk = Next(rk);
+ continue;
+ }
+
+ /* This type has subclasses. We now need to walk through these subtypes and generate pointer conversion functions */
+
+ rh = Getattr(r_resolved, rk.item);
+ rlist = NewList();
+ for (ck = First(rh); ck.key; ck = Next(ck)) {
+ Append(rlist, ck.key);
+ }
+ /* Printf(stdout,"rk.item = '%s'\n", rk.item);
+ Printf(stdout,"rh = %p '%s'\n", rh,rh); */
+
+ sub_sorted_keys = SortedKeys(sub, Strcmp);
+ bk = First(sub_sorted_keys);
+ while (bk.item) {
+ prefix = SwigType_prefix(rk.item);
+ Append(prefix, bk.item);
+ /* Printf(stdout,"set %p = '%s' : '%s'\n", rh, SwigType_manglestr(prefix),prefix); */
+ mprefix = SwigType_manglestr(prefix);
+ Setattr(rh, mprefix, prefix);
+ mkey = SwigType_manglestr(rk.item);
+ ckey = NewStringf("%s+%s", mprefix, mkey);
+ if (!Getattr(conversions, ckey)) {
+ String *convname = NewStringf("%sTo%s", mprefix, mkey);
+ String *lkey = SwigType_lstr(rk.item, 0);
+ String *lprefix = SwigType_lstr(prefix, 0);
+ Hash *subhash = Getattr(sub, bk.item);
+ String *convcode = Getattr(subhash, "convcode");
+ if (convcode) {
+ char *newmemoryused = Strstr(convcode, "newmemory"); /* see if newmemory parameter is used in order to avoid unused parameter warnings */
+ String *fn = Copy(convcode);
+ Replaceall(fn, "$from", "x");
+ Printf(out, "static void *%s(void *x, int *%s) {", convname, newmemoryused ? "newmemory" : "SWIGUNUSEDPARM(newmemory)");
+ Printf(out, "%s", fn);
+ } else {
+ String *cast = Getattr(subhash, "cast");
+ Printf(out, "static void *%s(void *x, int *SWIGUNUSEDPARM(newmemory)) {", convname);
+ Printf(out, "\n return (void *)((%s) ", lkey);
+ if (cast)
+ Printf(out, "%s", cast);
+ Printf(out, " ((%s) x));\n", lprefix);
+ }
+ Printf(out, "}\n");
+ Setattr(conversions, ckey, convname);
+ Delete(ckey);
+ Delete(lkey);
+ Delete(lprefix);
+
+ /* This inserts conversions for typedefs */
+ {
+ Hash *r = Getattr(r_resolved, prefix);
+ if (r) {
+ Iterator rrk;
+ rrk = First(r);
+ while (rrk.key) {
+ Iterator rlk;
+ String *rkeymangle;
+
+ /* Make sure this name equivalence is not due to inheritance */
+ if (Cmp(prefix, Getattr(r, rrk.key)) == 0) {
+ rkeymangle = Copy(mkey);
+ ckey = NewStringf("%s+%s", rrk.key, rkeymangle);
+ if (!Getattr(conversions, ckey)) {
+ Setattr(conversions, ckey, convname);
+ }
+ Delete(ckey);
+ for (rlk = First(rlist); rlk.item; rlk = Next(rlk)) {
+ ckey = NewStringf("%s+%s", rrk.key, rlk.item);
+ Setattr(conversions, ckey, convname);
+ Delete(ckey);
+ }
+ Delete(rkeymangle);
+ /* This is needed to pick up other alternative names for the same type.
+ Needed to make templates work */
+ Setattr(rh, rrk.key, rrk.item);
+ }
+ rrk = Next(rrk);
+ }
+ }
+ }
+ Delete(convname);
+ }
+ Delete(prefix);
+ Delete(mprefix);
+ Delete(mkey);
+ bk = Next(bk);
+ }
+ Delete(sub_sorted_keys);
+ rk = Next(rk);
+ Delete(rlist);
+ }
+ Delete(r_resolved_sorted_keys);
+}
+
+/* -----------------------------------------------------------------------------
+ * SwigType_type_table()
+ *
+ * Generate the type-table for the type-checker.
+ * ----------------------------------------------------------------------------- */
+
+void SwigType_emit_type_table(File *f_forward, File *f_table) {
+ Iterator ki;
+ String *types, *table, *cast, *cast_init, *cast_temp;
+ Hash *imported_types;
+ List *mangled_list;
+ List *table_list = NewList();
+ int i = 0;
+
+ if (!r_mangled) {
+ r_mangled = NewHash();
+ r_resolved = NewHash();
+ }
+
+ Printf(f_table, "\n/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */\n\n");
+
+ SwigType_inherit_equiv(f_table);
+
+ /*#define DEBUG 1*/
+#ifdef DEBUG
+ Printf(stdout, "---r_mangled---\n");
+ Swig_print(r_mangled, 2);
+
+ Printf(stdout, "---r_resolved---\n");
+ Swig_print(r_resolved, 2);
+
+ Printf(stdout, "---r_ltype---\n");
+ Swig_print(r_ltype, 2);
+
+ Printf(stdout, "---subclass---\n");
+ Swig_print(subclass, 2);
+
+ Printf(stdout, "---conversions---\n");
+ Swig_print(conversions, 2);
+
+ Printf(stdout, "---r_clientdata---\n");
+ Swig_print(r_clientdata, 2);
+
+#endif
+ table = NewStringEmpty();
+ types = NewStringEmpty();
+ cast = NewStringEmpty();
+ cast_init = NewStringEmpty();
+ imported_types = NewHash();
+
+ Printf(table, "static swig_type_info *swig_type_initial[] = {\n");
+ Printf(cast_init, "static swig_cast_info *swig_cast_initial[] = {\n");
+
+ Printf(f_forward, "\n/* -------- TYPES TABLE (BEGIN) -------- */\n\n");
+
+ mangled_list = SortedKeys(r_mangled, Strcmp);
+ for (ki = First(mangled_list); ki.item; ki = Next(ki)) {
+ List *el;
+ Iterator ei;
+ String *nt;
+ String *ln;
+ String *rn;
+ const String *cd;
+ Hash *lthash;
+ Iterator ltiter;
+ Hash *nthash;
+ String *cast_temp_conv;
+ String *resolved_lstr = 0;
+ List *ntlist;
+
+ cast_temp = NewStringEmpty();
+ cast_temp_conv = NewStringEmpty();
+
+ Printv(types, "static swig_type_info _swigt_", ki.item, " = {", NIL);
+ Append(table_list, ki.item);
+ Printf(cast_temp, "static swig_cast_info _swigc_%s[] = {", ki.item);
+ i++;
+
+ cd = SwigType_clientdata_collect(ki.item);
+ if (!cd)
+ cd = "0";
+
+ lthash = Getattr(r_ltype, ki.item);
+ nt = 0;
+ nthash = NewHash();
+ ltiter = First(lthash);
+ while (ltiter.key) {
+ SwigType *lt = ltiter.key;
+ SwigType *rt = SwigType_typedef_resolve_all(lt);
+ /* we save the original type and the fully resolved version */
+ ln = SwigType_lstr(lt, 0);
+ rn = SwigType_lstr(rt, 0);
+ if (Equal(ln, rn)) {
+ Setattr(nthash, ln, "1");
+ } else {
+ Setattr(nthash, rn, "1");
+ Setattr(nthash, ln, "1");
+ }
+ if (!resolved_lstr) {
+ resolved_lstr = Copy(rn);
+ } else if (Len(rn) < Len(resolved_lstr)) {
+ Delete(resolved_lstr);
+ resolved_lstr = Copy(rn);
+ }
+ if (SwigType_istemplate(rt)) {
+ String *dt = Swig_symbol_template_deftype(rt, 0);
+ String *dn = SwigType_lstr(dt, 0);
+ if (!Equal(dn, rn) && !Equal(dn, ln)) {
+ Setattr(nthash, dn, "1");
+ }
+ Delete(dt);
+ Delete(dn);
+ }
+ Delete(rt);
+ Delete(rn);
+ Delete(ln);
+
+ ltiter = Next(ltiter);
+ }
+
+ /* now build nt */
+ ntlist = SortedKeys(nthash, Strcmp);
+ ltiter = First(ntlist);
+ nt = 0;
+ while (ltiter.item) {
+ if (!Equal(resolved_lstr, ltiter.item)) {
+ if (nt) {
+ Printf(nt, "|%s", ltiter.item);
+ } else {
+ nt = NewString(ltiter.item);
+ }
+ }
+ ltiter = Next(ltiter);
+ }
+ /* Last in list is a resolved type used by SWIG_TypePrettyName.
+ * There can be more than one resolved type and the chosen one is simply the
+ * shortest in length, arguably the most user friendly/readable. */
+ if (nt) {
+ Printf(nt, "|%s", resolved_lstr);
+ } else {
+ nt = NewString(resolved_lstr);
+ }
+ Delete(ntlist);
+ Delete(nthash);
+ Delete(resolved_lstr);
+
+ Printf(types, "\"%s\", \"%s\", 0, 0, (void*)%s, 0};\n", ki.item, nt, cd);
+
+ el = SwigType_equivalent_mangle(ki.item, 0, 0);
+ SortList(el, Strcmp);
+ for (ei = First(el); ei.item; ei = Next(ei)) {
+ String *ckey;
+ String *conv;
+ ckey = NewStringf("%s+%s", ei.item, ki.item);
+ conv = Getattr(conversions, ckey);
+ if (conv) {
+ Printf(cast_temp_conv, " {&_swigt_%s, %s, 0, 0},", ei.item, conv);
+ } else {
+ Printf(cast_temp, " {&_swigt_%s, 0, 0, 0},", ei.item);
+ }
+ Delete(ckey);
+
+ if (!Getattr(r_mangled, ei.item) && !Getattr(imported_types, ei.item)) {
+ Printf(types, "static swig_type_info _swigt_%s = {\"%s\", 0, 0, 0, 0, 0};\n", ei.item, ei.item);
+ Append(table_list, ei.item);
+
+ Printf(cast, "static swig_cast_info _swigc_%s[] = {{&_swigt_%s, 0, 0, 0},{0, 0, 0, 0}};\n", ei.item, ei.item);
+ i++;
+
+ Setattr(imported_types, ei.item, "1");
+ }
+ }
+ Delete(el);
+ Printf(cast, "%s%s{0, 0, 0, 0}};\n", cast_temp, cast_temp_conv);
+ Delete(cast_temp_conv);
+ Delete(cast_temp);
+ Delete(nt);
+ }
+ /* print the tables in the proper order */
+ SortList(table_list, Strcmp);
+ i = 0;
+ for (ki = First(table_list); ki.item; ki = Next(ki)) {
+ Printf(f_forward, "#define SWIGTYPE%s swig_types[%d]\n", ki.item, i++);
+ Printf(table, " &_swigt_%s,\n", ki.item);
+ Printf(cast_init, " _swigc_%s,\n", ki.item);
+ }
+ if (i == 0) {
+ /* empty arrays are not allowed by ISO C */
+ Printf(table, " NULL\n");
+ Printf(cast_init, " NULL\n");
+ }
+
+ Delete(table_list);
+
+ Delete(mangled_list);
+
+ Printf(table, "};\n");
+ Printf(cast_init, "};\n");
+ Printf(f_table, "%s\n", types);
+ Printf(f_table, "%s\n", table);
+ Printf(f_table, "%s\n", cast);
+ Printf(f_table, "%s\n", cast_init);
+ Printf(f_table, "\n/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */\n\n");
+
+ Printf(f_forward, "static swig_type_info *swig_types[%d];\n", i + 1);
+ Printf(f_forward, "static swig_module_info swig_module = {swig_types, %d, 0, 0, 0, 0};\n", i);
+ Printf(f_forward, "#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)\n");
+ Printf(f_forward, "#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)\n");
+ Printf(f_forward, "\n/* -------- TYPES TABLE (END) -------- */\n\n");
+
+ Delete(types);
+ Delete(table);
+ Delete(cast);
+ Delete(cast_init);
+ Delete(imported_types);
+}
diff --git a/contrib/tools/swig/Source/Swig/wrapfunc.c b/contrib/tools/swig/Source/Swig/wrapfunc.c
new file mode 100644
index 0000000000..6d82372453
--- /dev/null
+++ b/contrib/tools/swig/Source/Swig/wrapfunc.c
@@ -0,0 +1,520 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * wrapfunc.c
+ *
+ * This file defines a object for creating wrapper functions. Primarily
+ * this is used for convenience since it allows pieces of a wrapper function
+ * to be created in a piecemeal manner.
+ * ----------------------------------------------------------------------------- */
+
+#include "swig.h"
+#include <ctype.h>
+
+static int Compact_mode = 0; /* set to 0 on default */
+static int Max_line_size = 128;
+
+/* -----------------------------------------------------------------------------
+ * NewWrapper()
+ *
+ * Create a new wrapper function object.
+ * ----------------------------------------------------------------------------- */
+
+Wrapper *NewWrapper(void) {
+ Wrapper *w;
+ w = (Wrapper *) Malloc(sizeof(Wrapper));
+ w->localh = NewHash();
+ w->locals = NewStringEmpty();
+ w->code = NewStringEmpty();
+ w->def = NewStringEmpty();
+ return w;
+}
+
+/* -----------------------------------------------------------------------------
+ * DelWrapper()
+ *
+ * Delete a wrapper function object.
+ * ----------------------------------------------------------------------------- */
+
+void DelWrapper(Wrapper *w) {
+ Delete(w->localh);
+ Delete(w->locals);
+ Delete(w->code);
+ Delete(w->def);
+ Free(w);
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_compact_print_mode_set()
+ *
+ * Set compact_mode.
+ * ----------------------------------------------------------------------------- */
+
+void Wrapper_compact_print_mode_set(int flag) {
+ Compact_mode = flag;
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_pretty_print()
+ *
+ * Formats a wrapper function and fixes up the indentation.
+ * ----------------------------------------------------------------------------- */
+
+void Wrapper_pretty_print(String *str, File *f) {
+ String *ts;
+ int level = 0;
+ int c, i;
+ int empty = 1;
+ int indent = 2;
+ int plevel = 0;
+ int label = 0;
+
+ ts = NewStringEmpty();
+ Seek(str, 0, SEEK_SET);
+ while ((c = Getc(str)) != EOF) {
+ if (c == '\"') {
+ Putc(c, ts);
+ while ((c = Getc(str)) != EOF) {
+ if (c == '\\') {
+ Putc(c, ts);
+ c = Getc(str);
+ }
+ Putc(c, ts);
+ if (c == '\"')
+ break;
+ }
+ empty = 0;
+ } else if (c == '\'') {
+ Putc(c, ts);
+ while ((c = Getc(str)) != EOF) {
+ if (c == '\\') {
+ Putc(c, ts);
+ c = Getc(str);
+ }
+ Putc(c, ts);
+ if (c == '\'')
+ break;
+ }
+ empty = 0;
+ } else if (c == ':') {
+ Putc(c, ts);
+ if ((c = Getc(str)) == '\n') {
+ if (!empty && !strchr(Char(ts), '?'))
+ label = 1;
+ }
+ Ungetc(c, str);
+ } else if (c == '(') {
+ Putc(c, ts);
+ plevel += indent;
+ empty = 0;
+ } else if (c == ')') {
+ Putc(c, ts);
+ plevel -= indent;
+ empty = 0;
+ } else if (c == '{') {
+ Putc(c, ts);
+ Putc('\n', ts);
+ for (i = 0; i < level; i++)
+ Putc(' ', f);
+ Printf(f, "%s", ts);
+ Clear(ts);
+ level += indent;
+ while ((c = Getc(str)) != EOF) {
+ if (!isspace(c)) {
+ Ungetc(c, str);
+ break;
+ }
+ }
+ empty = 0;
+ } else if (c == '}') {
+ if (!empty) {
+ Putc('\n', ts);
+ for (i = 0; i < level; i++)
+ Putc(' ', f);
+ Printf(f, "%s", ts);
+ Clear(ts);
+ }
+ level -= indent;
+ Putc(c, ts);
+ empty = 0;
+ } else if (c == '\n') {
+ Putc(c, ts);
+ empty = 0;
+ if (!empty) {
+ int slevel = level;
+ if (label && (slevel >= indent))
+ slevel -= indent;
+ if ((Char(ts))[0] != '#') {
+ for (i = 0; i < slevel; i++)
+ Putc(' ', f);
+ }
+ Printf(f, "%s", ts);
+ for (i = 0; i < plevel; i++)
+ Putc(' ', f);
+ }
+ Clear(ts);
+ label = 0;
+ empty = 1;
+ } else if (c == '/') {
+ empty = 0;
+ Putc(c, ts);
+ c = Getc(str);
+ if (c != EOF) {
+ Putc(c, ts);
+ if (c == '/') { /* C++ comment */
+ while ((c = Getc(str)) != EOF) {
+ if (c == '\n') {
+ Ungetc(c, str);
+ break;
+ }
+ Putc(c, ts);
+ }
+ } else if (c == '*') { /* C comment */
+ int endstar = 0;
+ while ((c = Getc(str)) != EOF) {
+ if (endstar && c == '/') { /* end of C comment */
+ Putc(c, ts);
+ break;
+ }
+ endstar = (c == '*');
+ Putc(c, ts);
+ if (c == '\n') { /* multi-line C comment. Could be improved slightly. */
+ for (i = 0; i < level; i++)
+ Putc(' ', ts);
+ }
+ }
+ }
+ }
+ } else {
+ if (!empty || !isspace(c)) {
+ Putc(c, ts);
+ empty = 0;
+ }
+ }
+ }
+ if (!empty)
+ Printf(f, "%s", ts);
+ Delete(ts);
+ Printf(f, "\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_compact_print()
+ *
+ * Formats a wrapper function and fixes up the indentation.
+ * Print out in compact format, with Compact enabled.
+ * ----------------------------------------------------------------------------- */
+
+void Wrapper_compact_print(String *str, File *f) {
+ String *ts, *tf; /*temp string & temp file */
+ int level = 0;
+ int c, i;
+ int empty = 1;
+ int indent = 2;
+
+ ts = NewStringEmpty();
+ tf = NewStringEmpty();
+ Seek(str, 0, SEEK_SET);
+
+ while ((c = Getc(str)) != EOF) {
+ if (c == '\"') { /* string 1 */
+ empty = 0;
+ Putc(c, ts);
+ while ((c = Getc(str)) != EOF) {
+ if (c == '\\') {
+ Putc(c, ts);
+ c = Getc(str);
+ }
+ Putc(c, ts);
+ if (c == '\"')
+ break;
+ }
+ } else if (c == '\'') { /* string 2 */
+ empty = 0;
+ Putc(c, ts);
+ while ((c = Getc(str)) != EOF) {
+ if (c == '\\') {
+ Putc(c, ts);
+ c = Getc(str);
+ }
+ Putc(c, ts);
+ if (c == '\'')
+ break;
+ }
+ } else if (c == '{') { /* start of {...} */
+ empty = 0;
+ Putc(c, ts);
+ if (Len(tf) == 0) {
+ for (i = 0; i < level; i++)
+ Putc(' ', tf);
+ } else if ((Len(tf) + Len(ts)) < Max_line_size) {
+ Putc(' ', tf);
+ } else {
+ Putc('\n', tf);
+ Printf(f, "%s", tf);
+ Clear(tf);
+ for (i = 0; i < level; i++)
+ Putc(' ', tf);
+ }
+ Append(tf, ts);
+ Clear(ts);
+ level += indent;
+ while ((c = Getc(str)) != EOF) {
+ if (!isspace(c)) {
+ Ungetc(c, str);
+ break;
+ }
+ }
+ } else if (c == '}') { /* end of {...} */
+ empty = 0;
+ if (Len(tf) == 0) {
+ for (i = 0; i < level; i++)
+ Putc(' ', tf);
+ } else if ((Len(tf) + Len(ts)) < Max_line_size) {
+ Putc(' ', tf);
+ } else {
+ Putc('\n', tf);
+ Printf(f, "%s", tf);
+ Clear(tf);
+ for (i = 0; i < level; i++)
+ Putc(' ', tf);
+ }
+ Append(tf, ts);
+ Putc(c, tf);
+ Clear(ts);
+ level -= indent;
+ } else if (c == '\n') { /* line end */
+ while ((c = Getc(str)) != EOF) {
+ if (!isspace(c))
+ break;
+ }
+ if (c == '#') {
+ Putc('\n', ts);
+ } else if (c == '}') {
+ Putc(' ', ts);
+ } else if ((c != EOF) || (Len(ts) != 0)) {
+ if (Len(tf) == 0) {
+ for (i = 0; i < level; i++)
+ Putc(' ', tf);
+ } else if ((Len(tf) + Len(ts)) < Max_line_size) {
+ Putc(' ', tf);
+ } else {
+ Putc('\n', tf);
+ Printf(f, "%s", tf);
+ Clear(tf);
+ for (i = 0; i < level; i++)
+ Putc(' ', tf);
+ }
+ Append(tf, ts);
+ Clear(ts);
+ }
+ Ungetc(c, str);
+
+ empty = 1;
+ } else if (c == '/') { /* comment */
+ empty = 0;
+ c = Getc(str);
+ if (c != EOF) {
+ if (c == '/') { /* C++ comment */
+ while ((c = Getc(str)) != EOF) {
+ if (c == '\n') {
+ Ungetc(c, str);
+ break;
+ }
+ }
+ } else if (c == '*') { /* C comment */
+ int endstar = 0;
+ while ((c = Getc(str)) != EOF) {
+ if (endstar && c == '/') { /* end of C comment */
+ break;
+ }
+ endstar = (c == '*');
+ }
+ } else {
+ Putc('/', ts);
+ Putc(c, ts);
+ }
+ }
+ } else if (c == '#') { /* Preprocessor line */
+ Putc('#', ts);
+ while ((c = Getc(str)) != EOF) {
+ Putc(c, ts);
+ if (c == '\\') { /* Continued line of the same PP */
+ c = Getc(str);
+ if (c == '\n')
+ Putc(c, ts);
+ else
+ Ungetc(c, str);
+ } else if (c == '\n')
+ break;
+ }
+ if (!empty) {
+ Append(tf, "\n");
+ }
+ Append(tf, ts);
+ Printf(f, "%s", tf);
+ Clear(tf);
+ Clear(ts);
+ for (i = 0; i < level; i++)
+ Putc(' ', tf);
+ empty = 1;
+ } else {
+ if (!empty || !isspace(c)) {
+ Putc(c, ts);
+ empty = 0;
+ }
+ }
+ }
+ if (!empty) {
+ Append(tf, ts);
+ }
+ if (Len(tf) != 0)
+ Printf(f, "%s", tf);
+ Delete(ts);
+ Delete(tf);
+ Printf(f, "\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_print()
+ *
+ * Print out a wrapper function. Does pretty or compact printing as well.
+ * ----------------------------------------------------------------------------- */
+
+void Wrapper_print(Wrapper *w, File *f) {
+ String *str;
+
+ str = NewStringEmpty();
+ Printf(str, "%s\n", w->def);
+ Printf(str, "%s\n", w->locals);
+ Printf(str, "%s\n", w->code);
+ if (Compact_mode == 1)
+ Wrapper_compact_print(str, f);
+ else
+ Wrapper_pretty_print(str, f);
+
+ Delete(str);
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_add_local()
+ *
+ * Adds a new local variable declaration to a function. Returns -1 if already
+ * present (which may or may not be okay to the caller).
+ * ----------------------------------------------------------------------------- */
+
+int Wrapper_add_local(Wrapper *w, const_String_or_char_ptr name, const_String_or_char_ptr decl) {
+ /* See if the local has already been declared */
+ if (Getattr(w->localh, name)) {
+ return -1;
+ }
+ Setattr(w->localh, name, decl);
+ Printf(w->locals, "%s;\n", decl);
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_add_localv()
+ *
+ * Same as add_local(), but allows a NULL terminated list of strings to be
+ * used as a replacement for decl. This saves the caller the trouble of having
+ * to manually construct the 'decl' string before calling.
+ * ----------------------------------------------------------------------------- */
+
+int Wrapper_add_localv(Wrapper *w, const_String_or_char_ptr name, ...) {
+ va_list ap;
+ int ret;
+ String *decl;
+ DOH *obj;
+ decl = NewStringEmpty();
+ va_start(ap, name);
+
+ obj = va_arg(ap, void *);
+ while (obj) {
+ Append(decl, obj);
+ Putc(' ', decl);
+ obj = va_arg(ap, void *);
+ }
+ va_end(ap);
+
+ ret = Wrapper_add_local(w, name, decl);
+ Delete(decl);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_check_local()
+ *
+ * Check to see if a local name has already been declared
+ * ----------------------------------------------------------------------------- */
+
+int Wrapper_check_local(Wrapper *w, const_String_or_char_ptr name) {
+ if (Getattr(w->localh, name)) {
+ return 1;
+ }
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_new_local()
+ *
+ * Adds a new local variable with a guarantee that a unique local name will be
+ * used. Returns the name that was actually selected.
+ * ----------------------------------------------------------------------------- */
+
+char *Wrapper_new_local(Wrapper *w, const_String_or_char_ptr name, const_String_or_char_ptr decl) {
+ int i;
+ String *nname = NewString(name);
+ String *ndecl = NewString(decl);
+ char *ret;
+
+ i = 0;
+
+ while (Wrapper_check_local(w, nname)) {
+ Clear(nname);
+ Printf(nname, "%s%d", name, i);
+ i++;
+ }
+ Replace(ndecl, name, nname, DOH_REPLACE_ID);
+ Setattr(w->localh, nname, ndecl);
+ Printf(w->locals, "%s;\n", ndecl);
+ ret = Char(nname);
+ Delete(nname);
+ Delete(ndecl);
+ return ret; /* Note: nname should still exists in the w->localh hash */
+}
+
+
+/* -----------------------------------------------------------------------------
+ * Wrapper_new_localv()
+ *
+ * Same as add_local(), but allows a NULL terminated list of strings to be
+ * used as a replacement for decl. This saves the caller the trouble of having
+ * to manually construct the 'decl' string before calling.
+ * ----------------------------------------------------------------------------- */
+
+char *Wrapper_new_localv(Wrapper *w, const_String_or_char_ptr name, ...) {
+ va_list ap;
+ char *ret;
+ String *decl;
+ DOH *obj;
+ decl = NewStringEmpty();
+ va_start(ap, name);
+
+ obj = va_arg(ap, void *);
+ while (obj) {
+ Append(decl, obj);
+ Putc(' ', decl);
+ obj = va_arg(ap, void *);
+ }
+ va_end(ap);
+
+ ret = Wrapper_new_local(w, name, decl);
+ Delete(decl);
+ return ret;
+}
diff --git a/contrib/tools/swig/swig_lib.cpp b/contrib/tools/swig/swig_lib.cpp
new file mode 100644
index 0000000000..75b98501e8
--- /dev/null
+++ b/contrib/tools/swig/swig_lib.cpp
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define _STR(a) #a
+#define STR(a) _STR(a)
+
+static const char* ArcadiaRoot() {
+ const char* root = getenv("ARCADIA_ROOT_DISTBUILD");
+ return root ? root : STR(ARCADIA_ROOT);
+}
+
+#ifdef _MSC_VER
+static int setenv(const char* name, const char* value, int overwrite) {
+ return (overwrite || !getenv(name)) ? _putenv_s(name, value) : 0;
+}
+#endif
+
+static void InitSwigLib() {
+ const char* root = ArcadiaRoot();
+ const char* lib = STR(SWIG_LIB_ARCPATH);
+ char* s = new char[strlen(root) + 1 + strlen(lib) + 1];
+ sprintf(s, "%s/%s", root, lib);
+ setenv("SWIG_LIB", s, false);
+ delete[] s;
+}
+
+static int initSwigLib = (InitSwigLib(), 0);
diff --git a/contrib/tools/swig/ya.make b/contrib/tools/swig/ya.make
new file mode 100644
index 0000000000..d1d4b407c8
--- /dev/null
+++ b/contrib/tools/swig/ya.make
@@ -0,0 +1,120 @@
+# Generated by devtools/yamaker from nixpkgs 22.11.
+
+PROGRAM(swig)
+
+LICENSE(
+ Custom-swig AND
+ GPL-3.0-only
+)
+
+LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
+
+VERSION(4.1.1)
+
+ORIGINAL_SOURCE(https://github.com/swig/swig/archive/v4.1.1.tar.gz)
+
+PEERDIR(
+ contrib/libs/pcre2
+)
+
+ADDINCL(
+ contrib/libs/pcre2
+ contrib/tools/swig/Source/CParse
+ contrib/tools/swig/Source/DOH
+ contrib/tools/swig/Source/Doxygen
+ contrib/tools/swig/Source/Include
+ contrib/tools/swig/Source/Modules
+ contrib/tools/swig/Source/Preprocessor
+ contrib/tools/swig/Source/Swig
+)
+
+NO_COMPILER_WARNINGS()
+
+NO_UTIL()
+
+CFLAGS(
+ -DSWIG_LIB_ARCPATH=contrib/tools/swig/Lib
+ -DHAVE_CONFIG_H
+)
+
+BISON_GEN_C()
+
+SRCS(
+ Source/CParse/cscanner.c
+ Source/CParse/parser.y
+ Source/CParse/templ.c
+ Source/CParse/util.c
+ Source/DOH/base.c
+ Source/DOH/file.c
+ Source/DOH/fio.c
+ Source/DOH/hash.c
+ Source/DOH/list.c
+ Source/DOH/memory.c
+ Source/DOH/string.c
+ Source/DOH/void.c
+ Source/Doxygen/doxyentity.cxx
+ Source/Doxygen/doxyparser.cxx
+ Source/Doxygen/doxytranslator.cxx
+ Source/Doxygen/javadoc.cxx
+ Source/Doxygen/pydoc.cxx
+ Source/Modules/allocate.cxx
+ Source/Modules/contract.cxx
+ Source/Modules/csharp.cxx
+ Source/Modules/d.cxx
+ Source/Modules/directors.cxx
+ Source/Modules/emit.cxx
+ Source/Modules/go.cxx
+ Source/Modules/guile.cxx
+ Source/Modules/interface.cxx
+ Source/Modules/java.cxx
+ Source/Modules/javascript.cxx
+ Source/Modules/lang.cxx
+ Source/Modules/lua.cxx
+ Source/Modules/main.cxx
+ Source/Modules/mzscheme.cxx
+ Source/Modules/nested.cxx
+ Source/Modules/ocaml.cxx
+ Source/Modules/octave.cxx
+ Source/Modules/overload.cxx
+ Source/Modules/perl5.cxx
+ Source/Modules/php.cxx
+ Source/Modules/python.cxx
+ Source/Modules/r.cxx
+ Source/Modules/ruby.cxx
+ Source/Modules/scilab.cxx
+ Source/Modules/swigmain.cxx
+ Source/Modules/tcl8.cxx
+ Source/Modules/typepass.cxx
+ Source/Modules/utils.cxx
+ Source/Modules/xml.cxx
+ Source/Preprocessor/cpp.c
+ Source/Preprocessor/expr.c
+ Source/Swig/cwrap.c
+ Source/Swig/deprecate.c
+ Source/Swig/error.c
+ Source/Swig/extend.c
+ Source/Swig/fragment.c
+ Source/Swig/getopt.c
+ Source/Swig/include.c
+ Source/Swig/misc.c
+ Source/Swig/naming.c
+ Source/Swig/parms.c
+ Source/Swig/scanner.c
+ Source/Swig/stype.c
+ Source/Swig/symbol.c
+ Source/Swig/tree.c
+ Source/Swig/typemap.c
+ Source/Swig/typeobj.c
+ Source/Swig/typesys.c
+ Source/Swig/wrapfunc.c
+ swig_lib.cpp
+)
+
+END()
+
+RECURSE(
+ Lib/go
+ Lib/java
+ Lib/perl5
+ Lib/python
+)